summaryrefslogtreecommitdiffstats
path: root/usr.sbin
diff options
context:
space:
mode:
Diffstat (limited to 'usr.sbin')
-rw-r--r--usr.sbin/IPXrouted/IPXrouted.8189
-rw-r--r--usr.sbin/IPXrouted/Makefile11
-rw-r--r--usr.sbin/IPXrouted/af.c298
-rw-r--r--usr.sbin/IPXrouted/af.h77
-rw-r--r--usr.sbin/IPXrouted/defs.h106
-rw-r--r--usr.sbin/IPXrouted/if.c151
-rw-r--r--usr.sbin/IPXrouted/input.c304
-rw-r--r--usr.sbin/IPXrouted/interface.h95
-rw-r--r--usr.sbin/IPXrouted/main.c395
-rw-r--r--usr.sbin/IPXrouted/output.c231
-rw-r--r--usr.sbin/IPXrouted/protocol.h92
-rw-r--r--usr.sbin/IPXrouted/sap.h108
-rw-r--r--usr.sbin/IPXrouted/sap_input.c212
-rw-r--r--usr.sbin/IPXrouted/sap_output.c197
-rw-r--r--usr.sbin/IPXrouted/sap_tables.c335
-rw-r--r--usr.sbin/IPXrouted/startup.c281
-rw-r--r--usr.sbin/IPXrouted/table.h115
-rw-r--r--usr.sbin/IPXrouted/tables.c434
-rw-r--r--usr.sbin/IPXrouted/timer.c239
-rw-r--r--usr.sbin/IPXrouted/trace.c519
-rw-r--r--usr.sbin/IPXrouted/trace.h138
-rw-r--r--usr.sbin/Makefile37
-rw-r--r--usr.sbin/Makefile.inc3
-rw-r--r--usr.sbin/ac/Makefile18
-rw-r--r--usr.sbin/ac/ac.8155
-rw-r--r--usr.sbin/ac/ac.c561
-rw-r--r--usr.sbin/accton/Makefile6
-rw-r--r--usr.sbin/accton/accton.834
-rw-r--r--usr.sbin/accton/accton.c93
-rw-r--r--usr.sbin/adduser/Makefile12
-rw-r--r--usr.sbin/adduser/adduser.8284
-rw-r--r--usr.sbin/adduser/adduser.perl1423
-rw-r--r--usr.sbin/adduser/rmuser.8169
-rw-r--r--usr.sbin/adduser/rmuser.perl576
-rw-r--r--usr.sbin/amd/Makefile5
-rw-r--r--usr.sbin/amd/amd/ChangeLog1169
-rw-r--r--usr.sbin/amd/amd/Makefile59
-rw-r--r--usr.sbin/amd/amd/afs_ops.c1819
-rw-r--r--usr.sbin/amd/amd/am_ops.c181
-rw-r--r--usr.sbin/amd/amd/amd.8232
-rw-r--r--usr.sbin/amd/amd/amd.c331
-rw-r--r--usr.sbin/amd/amd/amq_subr.c464
-rw-r--r--usr.sbin/amd/amd/clock.c241
-rw-r--r--usr.sbin/amd/amd/efs_ops.c135
-rw-r--r--usr.sbin/amd/amd/get_args.c341
-rw-r--r--usr.sbin/amd/amd/host_ops.c743
-rw-r--r--usr.sbin/amd/amd/ifs_ops.c195
-rw-r--r--usr.sbin/amd/amd/info_file.c276
-rw-r--r--usr.sbin/amd/amd/info_hes.c697
-rw-r--r--usr.sbin/amd/amd/info_ndbm.c123
-rw-r--r--usr.sbin/amd/amd/info_nis.c247
-rw-r--r--usr.sbin/amd/amd/info_passwd.c162
-rw-r--r--usr.sbin/amd/amd/info_union.c155
-rw-r--r--usr.sbin/amd/amd/map.c1140
-rw-r--r--usr.sbin/amd/amd/mapc.c914
-rw-r--r--usr.sbin/amd/amd/misc_rpc.c333
-rw-r--r--usr.sbin/amd/amd/mntfs.c367
-rw-r--r--usr.sbin/amd/amd/mount_fs.c274
-rw-r--r--usr.sbin/amd/amd/mtab.c108
-rw-r--r--usr.sbin/amd/amd/nfs_ops.c883
-rw-r--r--usr.sbin/amd/amd/nfs_start.c438
-rw-r--r--usr.sbin/amd/amd/nfs_subr.c562
-rw-r--r--usr.sbin/amd/amd/nfsx_ops.c516
-rw-r--r--usr.sbin/amd/amd/opts.c835
-rw-r--r--usr.sbin/amd/amd/pfs_ops.c169
-rw-r--r--usr.sbin/amd/amd/restart.c181
-rw-r--r--usr.sbin/amd/amd/rpc_fwd.c430
-rw-r--r--usr.sbin/amd/amd/sched.c325
-rw-r--r--usr.sbin/amd/amd/sfs_ops.c208
-rw-r--r--usr.sbin/amd/amd/srvr_afs.c205
-rw-r--r--usr.sbin/amd/amd/srvr_nfs.c719
-rw-r--r--usr.sbin/amd/amd/ufs_ops.c178
-rw-r--r--usr.sbin/amd/amd/umount_fs.c225
-rw-r--r--usr.sbin/amd/amd/util.c648
-rw-r--r--usr.sbin/amd/amd/wire.c281
-rw-r--r--usr.sbin/amd/amd/xutil.c494
-rw-r--r--usr.sbin/amd/amq/Makefile25
-rw-r--r--usr.sbin/amd/amq/amq.8131
-rw-r--r--usr.sbin/amd/amq/amq.c644
-rw-r--r--usr.sbin/amd/config/Configure60
-rw-r--r--usr.sbin/amd/config/Makefile.aix35
-rw-r--r--usr.sbin/amd/config/Makefile.bsd448
-rw-r--r--usr.sbin/amd/config/Makefile.config96
-rw-r--r--usr.sbin/amd/config/Makefile.hpux13
-rw-r--r--usr.sbin/amd/config/Makefile.irix10
-rw-r--r--usr.sbin/amd/config/Makefile.irix313
-rw-r--r--usr.sbin/amd/config/Makefile.irix414
-rw-r--r--usr.sbin/amd/config/Makefile.stellix10
-rw-r--r--usr.sbin/amd/config/RELEASE1
-rw-r--r--usr.sbin/amd/config/arch127
-rw-r--r--usr.sbin/amd/config/misc-aix3.h96
-rw-r--r--usr.sbin/amd/config/misc-bsd44l.h57
-rw-r--r--usr.sbin/amd/config/misc-hpux.h79
-rw-r--r--usr.sbin/amd/config/misc-irix.h50
-rw-r--r--usr.sbin/amd/config/misc-next.h44
-rw-r--r--usr.sbin/amd/config/misc-stellix.h65
-rw-r--r--usr.sbin/amd/config/misc-ultrix.h55
-rw-r--r--usr.sbin/amd/config/mount_aix.c153
-rw-r--r--usr.sbin/amd/config/mount_irix.c83
-rw-r--r--usr.sbin/amd/config/mount_stellix.c76
-rw-r--r--usr.sbin/amd/config/mtab_aix.c137
-rw-r--r--usr.sbin/amd/config/mtab_bsd.c114
-rw-r--r--usr.sbin/amd/config/mtab_file.c472
-rw-r--r--usr.sbin/amd/config/mtab_ultrix.c115
-rw-r--r--usr.sbin/amd/config/newvers.sh88
-rw-r--r--usr.sbin/amd/config/os-acis43.h83
-rw-r--r--usr.sbin/amd/config/os-aix3.h181
-rw-r--r--usr.sbin/amd/config/os-aux.h117
-rw-r--r--usr.sbin/amd/config/os-bsd44.h239
-rw-r--r--usr.sbin/amd/config/os-concentrix.h79
-rw-r--r--usr.sbin/amd/config/os-convex.h80
-rw-r--r--usr.sbin/amd/config/os-defaults.h143
-rw-r--r--usr.sbin/amd/config/os-dgux.h114
-rw-r--r--usr.sbin/amd/config/os-fpx4.h92
-rw-r--r--usr.sbin/amd/config/os-hcx.h81
-rw-r--r--usr.sbin/amd/config/os-hlh42.h89
-rw-r--r--usr.sbin/amd/config/os-hpux.h150
-rw-r--r--usr.sbin/amd/config/os-irix.h133
-rw-r--r--usr.sbin/amd/config/os-irix3.h133
-rw-r--r--usr.sbin/amd/config/os-irix4.h150
-rw-r--r--usr.sbin/amd/config/os-next.h79
-rw-r--r--usr.sbin/amd/config/os-pyrOSx.h80
-rw-r--r--usr.sbin/amd/config/os-riscix.h88
-rw-r--r--usr.sbin/amd/config/os-sos3.h79
-rw-r--r--usr.sbin/amd/config/os-sos4.h116
-rw-r--r--usr.sbin/amd/config/os-stellix.h108
-rw-r--r--usr.sbin/amd/config/os-type128
-rw-r--r--usr.sbin/amd/config/os-u2_2.h161
-rw-r--r--usr.sbin/amd/config/os-u3_0.h154
-rw-r--r--usr.sbin/amd/config/os-u4_0.h158
-rw-r--r--usr.sbin/amd/config/os-u4_2.h153
-rw-r--r--usr.sbin/amd/config/os-umax43.h79
-rw-r--r--usr.sbin/amd/config/os-utek.h49
-rw-r--r--usr.sbin/amd/config/os-utx32.h85
-rw-r--r--usr.sbin/amd/config/os-xinu43.h112
-rw-r--r--usr.sbin/amd/doc/Makefile46
-rw-r--r--usr.sbin/amd/doc/amdref.cps381
-rw-r--r--usr.sbin/amd/doc/amdref.ps6429
-rw-r--r--usr.sbin/amd/doc/amdref.texinfo4557
-rw-r--r--usr.sbin/amd/doc/texinfo.tex2192
-rw-r--r--usr.sbin/amd/fsinfo/Makefile32
-rw-r--r--usr.sbin/amd/fsinfo/conf/automounts48
-rw-r--r--usr.sbin/amd/fsinfo/conf/csg_sun318
-rw-r--r--usr.sbin/amd/fsinfo/conf/csg_vax67
-rw-r--r--usr.sbin/amd/fsinfo/conf/diskless_sun3_sos49
-rw-r--r--usr.sbin/amd/fsinfo/conf/hosts/achilles.doc.ic.ac.uk116
-rw-r--r--usr.sbin/amd/fsinfo/conf/hosts/bigears.doc.ic.ac.uk3
-rw-r--r--usr.sbin/amd/fsinfo/conf/hosts/dylan.doc.ic.ac.uk68
-rw-r--r--usr.sbin/amd/fsinfo/conf/hosts/flamingo.doc.ic.ac.uk104
-rw-r--r--usr.sbin/amd/fsinfo/conf/hosts/ganymede.doc.ic.ac.uk33
-rw-r--r--usr.sbin/amd/fsinfo/conf/hosts/gould.doc.ic.ac.uk477
-rw-r--r--usr.sbin/amd/fsinfo/conf/hosts/gummo.doc.ic.ac.uk12
-rw-r--r--usr.sbin/amd/fsinfo/conf/hosts/ivax.doc.ic.ac.uk84
-rw-r--r--usr.sbin/amd/fsinfo/conf/hosts/obsidian.doc.ic.ac.uk28
-rw-r--r--usr.sbin/amd/fsinfo/conf/hosts/pelican.doc.ic.ac.uk208
-rw-r--r--usr.sbin/amd/fsinfo/conf/hosts/rvax.doc.ic.ac.uk66
-rw-r--r--usr.sbin/amd/fsinfo/conf/hosts/sky.doc.ic.ac.uk79
-rw-r--r--usr.sbin/amd/fsinfo/conf/hosts/svax.doc.ic.ac.uk66
-rw-r--r--usr.sbin/amd/fsinfo/conf/hosts/tcsun1.doc.ic.ac.uk3
-rw-r--r--usr.sbin/amd/fsinfo/conf/hosts/tcsun2.doc.ic.ac.uk3
-rw-r--r--usr.sbin/amd/fsinfo/conf/hosts/tcsun3.doc.ic.ac.uk3
-rw-r--r--usr.sbin/amd/fsinfo/conf/hosts/tcsun4.doc.ic.ac.uk3
-rw-r--r--usr.sbin/amd/fsinfo/conf/hosts/tcsun5.doc.ic.ac.uk3
-rw-r--r--usr.sbin/amd/fsinfo/conf/hosts/toytown.doc.ic.ac.uk116
-rw-r--r--usr.sbin/amd/fsinfo/conf/hosts/truth.doc.ic.ac.uk9
-rw-r--r--usr.sbin/amd/fsinfo/conf/hosts/tsun1.doc.ic.ac.uk3
-rw-r--r--usr.sbin/amd/fsinfo/conf/hosts/tsun10.doc.ic.ac.uk3
-rw-r--r--usr.sbin/amd/fsinfo/conf/hosts/tsun11.doc.ic.ac.uk3
-rw-r--r--usr.sbin/amd/fsinfo/conf/hosts/tsun12.doc.ic.ac.uk3
-rw-r--r--usr.sbin/amd/fsinfo/conf/hosts/tsun13.doc.ic.ac.uk3
-rw-r--r--usr.sbin/amd/fsinfo/conf/hosts/tsun14.doc.ic.ac.uk3
-rw-r--r--usr.sbin/amd/fsinfo/conf/hosts/tsun15.doc.ic.ac.uk3
-rw-r--r--usr.sbin/amd/fsinfo/conf/hosts/tsun16.doc.ic.ac.uk3
-rw-r--r--usr.sbin/amd/fsinfo/conf/hosts/tsun17.doc.ic.ac.uk3
-rw-r--r--usr.sbin/amd/fsinfo/conf/hosts/tsun18.doc.ic.ac.uk3
-rw-r--r--usr.sbin/amd/fsinfo/conf/hosts/tsun19.doc.ic.ac.uk3
-rw-r--r--usr.sbin/amd/fsinfo/conf/hosts/tsun2.doc.ic.ac.uk3
-rw-r--r--usr.sbin/amd/fsinfo/conf/hosts/tsun3.doc.ic.ac.uk3
-rw-r--r--usr.sbin/amd/fsinfo/conf/hosts/tsun4.doc.ic.ac.uk3
-rw-r--r--usr.sbin/amd/fsinfo/conf/hosts/tsun5.doc.ic.ac.uk3
-rw-r--r--usr.sbin/amd/fsinfo/conf/hosts/tsun6.doc.ic.ac.uk3
-rw-r--r--usr.sbin/amd/fsinfo/conf/hosts/tsun7.doc.ic.ac.uk3
-rw-r--r--usr.sbin/amd/fsinfo/conf/hosts/tsun8.doc.ic.ac.uk3
-rw-r--r--usr.sbin/amd/fsinfo/conf/hosts/tsun9.doc.ic.ac.uk3
-rw-r--r--usr.sbin/amd/fsinfo/conf/hosts/tsunfs.doc.ic.ac.uk211
-rw-r--r--usr.sbin/amd/fsinfo/conf/hosts/whoops.doc.ic.ac.uk21
-rw-r--r--usr.sbin/amd/fsinfo/conf/users106
-rw-r--r--usr.sbin/amd/fsinfo/fsi_analyze.c645
-rw-r--r--usr.sbin/amd/fsinfo/fsi_data.h236
-rw-r--r--usr.sbin/amd/fsinfo/fsi_dict.c125
-rw-r--r--usr.sbin/amd/fsinfo/fsi_gram.y394
-rw-r--r--usr.sbin/amd/fsinfo/fsi_lex.l403
-rw-r--r--usr.sbin/amd/fsinfo/fsi_util.c576
-rw-r--r--usr.sbin/amd/fsinfo/fsinfo.877
-rw-r--r--usr.sbin/amd/fsinfo/fsinfo.c267
-rw-r--r--usr.sbin/amd/fsinfo/fsinfo.h159
-rw-r--r--usr.sbin/amd/fsinfo/wr_atab.c284
-rw-r--r--usr.sbin/amd/fsinfo/wr_bparam.c101
-rw-r--r--usr.sbin/amd/fsinfo/wr_dumpset.c88
-rw-r--r--usr.sbin/amd/fsinfo/wr_exportfs.c100
-rw-r--r--usr.sbin/amd/fsinfo/wr_fstab.c303
-rw-r--r--usr.sbin/amd/include/am.h567
-rw-r--r--usr.sbin/amd/include/config.h141
-rw-r--r--usr.sbin/amd/include/fstype.h148
-rw-r--r--usr.sbin/amd/include/mountres.h40
-rw-r--r--usr.sbin/amd/include/re.h21
-rw-r--r--usr.sbin/amd/include/remagic.h5
-rw-r--r--usr.sbin/amd/include/uwait.h83
-rw-r--r--usr.sbin/amd/maps/a_master79
-rw-r--r--usr.sbin/amd/maps/a_net3
-rw-r--r--usr.sbin/amd/mk-amd-map/Makefile21
-rw-r--r--usr.sbin/amd/mk-amd-map/mk-amd-map.859
-rw-r--r--usr.sbin/amd/mk-amd-map/mk-amd-map.c392
-rw-r--r--usr.sbin/amd/rpcx/amq.h153
-rw-r--r--usr.sbin/amd/rpcx/amq.x185
-rw-r--r--usr.sbin/amd/rpcx/amq_clnt.c181
-rw-r--r--usr.sbin/amd/rpcx/amq_svc.c136
-rw-r--r--usr.sbin/amd/rpcx/amq_xdr.c251
-rw-r--r--usr.sbin/amd/text/COPYRIGHT3
-rw-r--r--usr.sbin/amd/text/INSTALL194
-rw-r--r--usr.sbin/amd/text/README37
-rw-r--r--usr.sbin/amd/text/amd.start.ex87
-rw-r--r--usr.sbin/apm/Makefile8
-rw-r--r--usr.sbin/apm/apm.897
-rw-r--r--usr.sbin/apm/apm.c187
-rw-r--r--usr.sbin/apmconf/Makefile5
-rw-r--r--usr.sbin/apmconf/apmconf.860
-rw-r--r--usr.sbin/apmconf/apmconf.c147
-rw-r--r--usr.sbin/arp/Makefile7
-rw-r--r--usr.sbin/arp/arp.4142
-rw-r--r--usr.sbin/arp/arp.8140
-rw-r--r--usr.sbin/arp/arp.c635
-rw-r--r--usr.sbin/bad144/Makefile10
-rw-r--r--usr.sbin/bad144/bad144.8191
-rw-r--r--usr.sbin/bad144/bad144.c756
-rw-r--r--usr.sbin/bootparamd/Makefile4
-rw-r--r--usr.sbin/bootparamd/Makefile.inc3
-rw-r--r--usr.sbin/bootparamd/bootparamd/Makefile22
-rw-r--r--usr.sbin/bootparamd/bootparamd/README75
-rw-r--r--usr.sbin/bootparamd/bootparamd/bootparamd.848
-rw-r--r--usr.sbin/bootparamd/bootparamd/bootparamd.c340
-rw-r--r--usr.sbin/bootparamd/bootparamd/bootparams.578
-rw-r--r--usr.sbin/bootparamd/bootparamd/main.c117
-rw-r--r--usr.sbin/bootparamd/callbootd/Makefile21
-rw-r--r--usr.sbin/bootparamd/callbootd/callbootd.c196
-rw-r--r--usr.sbin/cdcontrol/Makefile4
-rw-r--r--usr.sbin/cdcontrol/cdcontrol.1162
-rw-r--r--usr.sbin/cdcontrol/cdcontrol.c1009
-rw-r--r--usr.sbin/chown/Makefile9
-rw-r--r--usr.sbin/chown/chgrp.1133
-rw-r--r--usr.sbin/chown/chown.8152
-rw-r--r--usr.sbin/chown/chown.c273
-rw-r--r--usr.sbin/chroot/Makefile6
-rw-r--r--usr.sbin/chroot/chroot.880
-rw-r--r--usr.sbin/chroot/chroot.c99
-rw-r--r--usr.sbin/ckdist/Makefile12
-rw-r--r--usr.sbin/ckdist/ckdist.196
-rw-r--r--usr.sbin/ckdist/ckdist.c440
-rw-r--r--usr.sbin/config/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/Makefile10
-rw-r--r--usr.sbin/config/SMM.doc/a.t162
-rw-r--r--usr.sbin/config/SMM.doc/b.t137
-rw-r--r--usr.sbin/config/SMM.doc/c.t109
-rw-r--r--usr.sbin/config/SMM.doc/d.t272
-rw-r--r--usr.sbin/config/SMM.doc/e.t114
-rw-r--r--usr.sbin/config/SMM.doc/spell.ok306
-rw-r--r--usr.sbin/config/config.8186
-rw-r--r--usr.sbin/config/config.h232
-rw-r--r--usr.sbin/config/config.y1139
-rw-r--r--usr.sbin/config/configvers.h11
-rw-r--r--usr.sbin/config/lang.l236
-rw-r--r--usr.sbin/config/main.c436
-rw-r--r--usr.sbin/config/mkglue.c411
-rw-r--r--usr.sbin/config/mkheaders.c229
-rw-r--r--usr.sbin/config/mkioconf.c1327
-rw-r--r--usr.sbin/config/mkmakefile.c856
-rw-r--r--usr.sbin/config/mkoptions.c323
-rw-r--r--usr.sbin/config/mkswapconf.c269
-rw-r--r--usr.sbin/config/mkubglue.c198
-rw-r--r--usr.sbin/cron/Makefile3
-rw-r--r--usr.sbin/cron/cron/Makefile22
-rw-r--r--usr.sbin/cron/cron/compat.h140
-rw-r--r--usr.sbin/cron/cron/config.h87
-rw-r--r--usr.sbin/cron/cron/cron.875
-rw-r--r--usr.sbin/cron/cron/cron.c310
-rw-r--r--usr.sbin/cron/cron/cron.h289
-rw-r--r--usr.sbin/cron/cron/database.c263
-rw-r--r--usr.sbin/cron/cron/do_command.c541
-rw-r--r--usr.sbin/cron/cron/externs.h145
-rw-r--r--usr.sbin/cron/cron/job.c76
-rw-r--r--usr.sbin/cron/cron/pathnames.h81
-rw-r--r--usr.sbin/cron/cron/popen.c170
-rw-r--r--usr.sbin/cron/cron/user.c128
-rw-r--r--usr.sbin/cron/crontab/Makefile26
-rw-r--r--usr.sbin/cron/crontab/crontab.1115
-rw-r--r--usr.sbin/cron/crontab/crontab.5225
-rw-r--r--usr.sbin/cron/crontab/crontab.c593
-rw-r--r--usr.sbin/cron/doc/CHANGES155
-rw-r--r--usr.sbin/cron/doc/CONVERSION85
-rw-r--r--usr.sbin/cron/doc/FEATURES84
-rw-r--r--usr.sbin/cron/doc/INSTALL87
-rw-r--r--usr.sbin/cron/doc/MAIL475
-rw-r--r--usr.sbin/cron/doc/Makefile.vixie128
-rw-r--r--usr.sbin/cron/doc/README72
-rw-r--r--usr.sbin/cron/doc/README.1ST4
-rw-r--r--usr.sbin/cron/doc/THANKS29
-rw-r--r--usr.sbin/cron/lib/Makefile12
-rw-r--r--usr.sbin/cron/lib/compat.c236
-rw-r--r--usr.sbin/cron/lib/entry.c552
-rw-r--r--usr.sbin/cron/lib/env.c203
-rw-r--r--usr.sbin/cron/lib/misc.c645
-rw-r--r--usr.sbin/crunch/COPYRIGHT25
-rw-r--r--usr.sbin/crunch/Makefile4
-rw-r--r--usr.sbin/crunch/Makefile.inc2
-rw-r--r--usr.sbin/crunch/README88
-rw-r--r--usr.sbin/crunch/crunchgen/Makefile10
-rw-r--r--usr.sbin/crunch/crunchgen/crunched_main.c117
-rw-r--r--usr.sbin/crunch/crunchgen/crunchgen.1278
-rw-r--r--usr.sbin/crunch/crunchgen/crunchgen.c878
-rw-r--r--usr.sbin/crunch/crunchgen/mkskel.sh15
-rw-r--r--usr.sbin/crunch/crunchide/Makefile4
-rw-r--r--usr.sbin/crunch/crunchide/crunchide.168
-rw-r--r--usr.sbin/crunch/crunchide/crunchide.c314
-rw-r--r--usr.sbin/crunch/examples/Makefile32
-rw-r--r--usr.sbin/crunch/examples/filesystem.conf31
-rw-r--r--usr.sbin/crunch/examples/fixit.conf45
-rw-r--r--usr.sbin/crunch/examples/kcopy.conf21
-rw-r--r--usr.sbin/crunch/examples/really-big.conf155
-rw-r--r--usr.sbin/ctm/Makefile5
-rw-r--r--usr.sbin/ctm/Makefile.inc5
-rw-r--r--usr.sbin/ctm/README97
-rw-r--r--usr.sbin/ctm/ctm/Makefile25
-rw-r--r--usr.sbin/ctm/ctm/ctm.1301
-rw-r--r--usr.sbin/ctm/ctm/ctm.5214
-rw-r--r--usr.sbin/ctm/ctm/ctm.c318
-rw-r--r--usr.sbin/ctm/ctm/ctm.h164
-rw-r--r--usr.sbin/ctm/ctm/ctm_ed.c114
-rw-r--r--usr.sbin/ctm/ctm/ctm_input.c138
-rw-r--r--usr.sbin/ctm/ctm/ctm_pass1.c250
-rw-r--r--usr.sbin/ctm/ctm/ctm_pass2.c245
-rw-r--r--usr.sbin/ctm/ctm/ctm_pass3.c295
-rw-r--r--usr.sbin/ctm/ctm/ctm_passb.c142
-rw-r--r--usr.sbin/ctm/ctm/ctm_syntax.c67
-rw-r--r--usr.sbin/ctm/ctm_dequeue/Makefile8
-rw-r--r--usr.sbin/ctm/ctm_dequeue/ctm_dequeue.c209
-rw-r--r--usr.sbin/ctm/ctm_rmail/Makefile7
-rw-r--r--usr.sbin/ctm/ctm_rmail/ctm_rmail.1479
-rw-r--r--usr.sbin/ctm/ctm_rmail/ctm_rmail.c661
-rw-r--r--usr.sbin/ctm/ctm_rmail/error.c97
-rw-r--r--usr.sbin/ctm/ctm_rmail/error.h3
-rw-r--r--usr.sbin/ctm/ctm_rmail/options.h137
-rw-r--r--usr.sbin/ctm/ctm_smail/Makefile7
-rw-r--r--usr.sbin/ctm/ctm_smail/ctm_smail.c469
-rw-r--r--usr.sbin/ctm/mkCTM/Makefile25
-rw-r--r--usr.sbin/ctm/mkCTM/ctm_conf.cvs-cur9
-rw-r--r--usr.sbin/ctm/mkCTM/ctm_conf.gnats8
-rw-r--r--usr.sbin/ctm/mkCTM/ctm_conf.ports-cur7
-rw-r--r--usr.sbin/ctm/mkCTM/ctm_conf.smp-cur7
-rw-r--r--usr.sbin/ctm/mkCTM/ctm_conf.src-cur9
-rw-r--r--usr.sbin/ctm/mkCTM/ctm_conf.src-special9
-rwxr-xr-xusr.sbin/ctm/mkCTM/dequeue6
-rw-r--r--usr.sbin/ctm/mkCTM/mkCTM186
-rw-r--r--usr.sbin/ctm/mkCTM/mkctm.c593
-rw-r--r--usr.sbin/dev_mkdb/Makefile6
-rw-r--r--usr.sbin/dev_mkdb/dev_mkdb.881
-rw-r--r--usr.sbin/dev_mkdb/dev_mkdb.c153
-rw-r--r--usr.sbin/diskpart/Makefile6
-rw-r--r--usr.sbin/diskpart/diskpart.8143
-rw-r--r--usr.sbin/diskpart/diskpart.c496
-rw-r--r--usr.sbin/edquota/Makefile6
-rw-r--r--usr.sbin/edquota/edquota.8174
-rw-r--r--usr.sbin/edquota/edquota.c754
-rw-r--r--usr.sbin/edquota/pathnames.h39
-rw-r--r--usr.sbin/fdcontrol/Makefile4
-rw-r--r--usr.sbin/fdcontrol/fdcontrol.898
-rw-r--r--usr.sbin/fdcontrol/fdcontrol.c141
-rw-r--r--usr.sbin/fdformat/Makefile9
-rw-r--r--usr.sbin/fdformat/fdformat.1157
-rw-r--r--usr.sbin/fdformat/fdformat.c354
-rw-r--r--usr.sbin/fdwrite/Makefile16
-rw-r--r--usr.sbin/fdwrite/fdwrite.1121
-rw-r--r--usr.sbin/fdwrite/fdwrite.c194
-rw-r--r--usr.sbin/inetd/Makefile13
-rw-r--r--usr.sbin/inetd/inetd.8511
-rw-r--r--usr.sbin/inetd/inetd.c1883
-rw-r--r--usr.sbin/inetd/pathnames.h40
-rw-r--r--usr.sbin/iostat/Makefile11
-rw-r--r--usr.sbin/iostat/iostat.8144
-rw-r--r--usr.sbin/iostat/iostat.c399
-rw-r--r--usr.sbin/kbdcontrol/Makefile6
-rw-r--r--usr.sbin/kbdcontrol/kbdcontrol.1101
-rw-r--r--usr.sbin/kbdcontrol/kbdcontrol.c601
-rw-r--r--usr.sbin/kbdcontrol/lex.h57
-rw-r--r--usr.sbin/kbdcontrol/lex.l117
-rw-r--r--usr.sbin/kbdcontrol/path.h4
-rw-r--r--usr.sbin/kbdmap/Languages.phrases37
-rw-r--r--usr.sbin/kbdmap/Makefile16
-rw-r--r--usr.sbin/kbdmap/TODO5
-rw-r--r--usr.sbin/kbdmap/kbdmap.1131
-rw-r--r--usr.sbin/kbdmap/kbdmap.pl317
-rw-r--r--usr.sbin/kernbb/Makefile9
-rw-r--r--usr.sbin/kernbb/kernbb.870
-rw-r--r--usr.sbin/kernbb/kernbb.c139
-rw-r--r--usr.sbin/keyadmin/Makefile6
-rw-r--r--usr.sbin/keyadmin/keyadmin.8239
-rw-r--r--usr.sbin/keyadmin/keyadmin.c1251
-rw-r--r--usr.sbin/keyadmin/keys18
-rw-r--r--usr.sbin/keyserv/Makefile24
-rw-r--r--usr.sbin/keyserv/crypt_server.c297
-rw-r--r--usr.sbin/keyserv/keyserv.879
-rw-r--r--usr.sbin/keyserv/keyserv.c803
-rw-r--r--usr.sbin/keyserv/keyserv.h19
-rw-r--r--usr.sbin/keyserv/keyserv_uid.c76
-rw-r--r--usr.sbin/keyserv/setkey.c550
-rw-r--r--usr.sbin/kgmon/Makefile14
-rw-r--r--usr.sbin/kgmon/kgmon.8129
-rw-r--r--usr.sbin/kgmon/kgmon.c505
-rw-r--r--usr.sbin/kvm_mkdb/Makefile11
-rw-r--r--usr.sbin/kvm_mkdb/extern.h38
-rw-r--r--usr.sbin/kvm_mkdb/kvm_mkdb.870
-rw-r--r--usr.sbin/kvm_mkdb/kvm_mkdb.c126
-rw-r--r--usr.sbin/kvm_mkdb/nlist.c338
-rw-r--r--usr.sbin/kvm_mkdb/testdb.c117
-rw-r--r--usr.sbin/lpr/Makefile6
-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/Makefile10
-rw-r--r--usr.sbin/lpr/SMM.doc/spell.ok70
-rw-r--r--usr.sbin/lpr/common_source/common.c386
-rw-r--r--usr.sbin/lpr/common_source/displayq.c490
-rw-r--r--usr.sbin/lpr/common_source/lp.h133
-rw-r--r--usr.sbin/lpr/common_source/lp.local.h81
-rw-r--r--usr.sbin/lpr/common_source/pathnames.h50
-rw-r--r--usr.sbin/lpr/common_source/recvjob.c372
-rw-r--r--usr.sbin/lpr/common_source/rmjob.c378
-rw-r--r--usr.sbin/lpr/common_source/startdaemon.c109
-rw-r--r--usr.sbin/lpr/filters/Makefile7
-rw-r--r--usr.sbin/lpr/filters/lpf.c219
-rw-r--r--usr.sbin/lpr/lp/Makefile10
-rw-r--r--usr.sbin/lpr/lp/lp.1107
-rw-r--r--usr.sbin/lpr/lp/lp.sh71
-rw-r--r--usr.sbin/lpr/lpc/Makefile12
-rw-r--r--usr.sbin/lpr/lpc/cmds.c1160
-rw-r--r--usr.sbin/lpr/lpc/cmdtab.c79
-rw-r--r--usr.sbin/lpr/lpc/extern.h58
-rw-r--r--usr.sbin/lpr/lpc/lpc.8175
-rw-r--r--usr.sbin/lpr/lpc/lpc.c322
-rw-r--r--usr.sbin/lpr/lpc/lpc.h45
-rw-r--r--usr.sbin/lpr/lpd/Makefile11
-rw-r--r--usr.sbin/lpr/lpd/extern.h42
-rw-r--r--usr.sbin/lpr/lpd/lpd.8256
-rw-r--r--usr.sbin/lpr/lpd/lpd.c615
-rw-r--r--usr.sbin/lpr/lpd/lpdchar.c1067
-rw-r--r--usr.sbin/lpr/lpd/modes.c232
-rw-r--r--usr.sbin/lpr/lpd/printjob.c1676
-rw-r--r--usr.sbin/lpr/lpd/recvjob.c372
-rw-r--r--usr.sbin/lpr/lpq/Makefile13
-rw-r--r--usr.sbin/lpr/lpq/lpq.1136
-rw-r--r--usr.sbin/lpr/lpq/lpq.c177
-rw-r--r--usr.sbin/lpr/lpr/Makefile13
-rw-r--r--usr.sbin/lpr/lpr/lpr.1253
-rw-r--r--usr.sbin/lpr/lpr/lpr.c792
-rw-r--r--usr.sbin/lpr/lpr/printcap.5315
-rw-r--r--usr.sbin/lpr/lprm/Makefile13
-rw-r--r--usr.sbin/lpr/lprm/lprm.1145
-rw-r--r--usr.sbin/lpr/lprm/lprm.c152
-rw-r--r--usr.sbin/lpr/lptest/Makefile7
-rw-r--r--usr.sbin/lpr/lptest/lptest.174
-rw-r--r--usr.sbin/lpr/lptest/lptest.c83
-rw-r--r--usr.sbin/lpr/pac/Makefile10
-rw-r--r--usr.sbin/lpr/pac/pac.8106
-rw-r--r--usr.sbin/lpr/pac/pac.c457
-rw-r--r--usr.sbin/lpr/runqueue/extern.h42
-rw-r--r--usr.sbin/lpr/runqueue/lpdchar.c1067
-rw-r--r--usr.sbin/lpr/runqueue/modes.c232
-rw-r--r--usr.sbin/lpr/runqueue/printjob.c1676
-rw-r--r--usr.sbin/lptcontrol/Makefile5
-rw-r--r--usr.sbin/lptcontrol/lptcontrol.879
-rw-r--r--usr.sbin/lptcontrol/lptcontrol.c108
-rw-r--r--usr.sbin/manctl/Makefile10
-rw-r--r--usr.sbin/manctl/manctl.855
-rw-r--r--usr.sbin/manctl/manctl.sh376
-rw-r--r--usr.sbin/mixer/Makefile6
-rw-r--r--usr.sbin/mixer/mixer.8137
-rw-r--r--usr.sbin/mixer/mixer.c233
-rw-r--r--usr.sbin/mkdosfs/Makefile59
-rw-r--r--usr.sbin/mkdosfs/bootcode.asm101
-rw-r--r--usr.sbin/mkdosfs/bootcode.h54
-rw-r--r--usr.sbin/mkdosfs/dosfs.h135
-rw-r--r--usr.sbin/mkdosfs/mkdosfs.1135
-rw-r--r--usr.sbin/mkdosfs/mkdosfs.c312
-rw-r--r--usr.sbin/mount_portalfs/Makefile14
-rw-r--r--usr.sbin/mount_portalfs/activate.c215
-rw-r--r--usr.sbin/mount_portalfs/conf.c337
-rw-r--r--usr.sbin/mount_portalfs/mount_portalfs.8137
-rw-r--r--usr.sbin/mount_portalfs/mount_portalfs.c292
-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.h82
-rw-r--r--usr.sbin/mount_portalfs/pt_conf.c51
-rw-r--r--usr.sbin/mount_portalfs/pt_exec.c61
-rw-r--r--usr.sbin/mount_portalfs/pt_file.c108
-rw-r--r--usr.sbin/mount_portalfs/pt_tcp.c165
-rw-r--r--usr.sbin/mountd/Makefile10
-rw-r--r--usr.sbin/mountd/exports.5289
-rw-r--r--usr.sbin/mountd/mountd.8143
-rw-r--r--usr.sbin/mountd/mountd.c2159
-rw-r--r--usr.sbin/mountd/netgroup.5160
-rw-r--r--usr.sbin/mountd/pathnames.h39
-rw-r--r--usr.sbin/moused/Makefile9
-rw-r--r--usr.sbin/moused/moused.8123
-rw-r--r--usr.sbin/moused/moused.c717
-rw-r--r--usr.sbin/mptable/Makefile8
-rw-r--r--usr.sbin/mptable/mptable.165
-rw-r--r--usr.sbin/mptable/mptable.c1133
-rw-r--r--usr.sbin/mrouted/LICENSE48
-rw-r--r--usr.sbin/mrouted/Makefile5
-rw-r--r--usr.sbin/mrouted/Makefile.inc2
-rw-r--r--usr.sbin/mrouted/RELEASE326
-rw-r--r--usr.sbin/mrouted/callout.c226
-rw-r--r--usr.sbin/mrouted/cfparse.y655
-rw-r--r--usr.sbin/mrouted/common/Makefile17
-rw-r--r--usr.sbin/mrouted/config.c148
-rw-r--r--usr.sbin/mrouted/defs.h330
-rw-r--r--usr.sbin/mrouted/dvmrp.h174
-rw-r--r--usr.sbin/mrouted/igmp.c467
-rw-r--r--usr.sbin/mrouted/inet.c232
-rw-r--r--usr.sbin/mrouted/kern.c241
-rw-r--r--usr.sbin/mrouted/main.c759
-rw-r--r--usr.sbin/mrouted/map-mbone.880
-rw-r--r--usr.sbin/mrouted/map-mbone/Makefile20
-rw-r--r--usr.sbin/mrouted/mapper.c1032
-rw-r--r--usr.sbin/mrouted/mrinfo.876
-rw-r--r--usr.sbin/mrouted/mrinfo.c628
-rw-r--r--usr.sbin/mrouted/mrinfo/Makefile22
-rw-r--r--usr.sbin/mrouted/mrouted.8420
-rw-r--r--usr.sbin/mrouted/mrouted.conf43
-rw-r--r--usr.sbin/mrouted/mrouted/Makefile21
-rw-r--r--usr.sbin/mrouted/mtrace.8550
-rw-r--r--usr.sbin/mrouted/mtrace.c2761
-rw-r--r--usr.sbin/mrouted/mtrace.h87
-rw-r--r--usr.sbin/mrouted/mtrace/Makefile14
-rw-r--r--usr.sbin/mrouted/pathnames.h25
-rw-r--r--usr.sbin/mrouted/prune.c2302
-rw-r--r--usr.sbin/mrouted/prune.h143
-rw-r--r--usr.sbin/mrouted/route.c1172
-rw-r--r--usr.sbin/mrouted/route.h51
-rw-r--r--usr.sbin/mrouted/rsrr.c514
-rw-r--r--usr.sbin/mrouted/rsrr.h138
-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.c1441
-rw-r--r--usr.sbin/mrouted/vif.h79
-rw-r--r--usr.sbin/mtest/Makefile4
-rw-r--r--usr.sbin/mtest/mtest.852
-rw-r--r--usr.sbin/mtest/mtest.c205
-rw-r--r--usr.sbin/mtree/Makefile12
-rw-r--r--usr.sbin/mtree/compare.c295
-rw-r--r--usr.sbin/mtree/create.c354
-rw-r--r--usr.sbin/mtree/extern.h43
-rw-r--r--usr.sbin/mtree/misc.c101
-rw-r--r--usr.sbin/mtree/mtree.8290
-rw-r--r--usr.sbin/mtree/mtree.c163
-rw-r--r--usr.sbin/mtree/mtree.h90
-rw-r--r--usr.sbin/mtree/spec.c299
-rw-r--r--usr.sbin/mtree/verify.c209
-rw-r--r--usr.sbin/named.reload/Makefile22
-rw-r--r--usr.sbin/named.restart/Makefile22
-rw-r--r--usr.sbin/named/Makefile23
-rw-r--r--usr.sbin/named/Makefile.inc24
-rw-r--r--usr.sbin/named/Makefile.maninc56
-rw-r--r--usr.sbin/natd/HISTORY119
-rw-r--r--usr.sbin/natd/Makefile10
-rw-r--r--usr.sbin/natd/README53
-rw-r--r--usr.sbin/natd/icmp.c113
-rw-r--r--usr.sbin/natd/natd.8383
-rw-r--r--usr.sbin/natd/natd.c1369
-rw-r--r--usr.sbin/natd/natd.h5
-rw-r--r--usr.sbin/natd/samples/natd.cf.sample52
-rw-r--r--usr.sbin/natd/samples/natd.test14
-rw-r--r--usr.sbin/ncrcontrol/Makefile11
-rw-r--r--usr.sbin/ncrcontrol/ncrcontrol.8290
-rw-r--r--usr.sbin/ncrcontrol/ncrcontrol.c1552
-rw-r--r--usr.sbin/ndc/Makefile25
-rw-r--r--usr.sbin/ndc/ndcedit.awk35
-rw-r--r--usr.sbin/newsyslog/Makefile16
-rw-r--r--usr.sbin/newsyslog/newsyslog.8172
-rw-r--r--usr.sbin/newsyslog/newsyslog.c636
-rw-r--r--usr.sbin/nfsd/Makefile18
-rw-r--r--usr.sbin/nfsd/nfsd.8134
-rw-r--r--usr.sbin/nfsd/nfsd.c673
-rw-r--r--usr.sbin/nologin/Makefile12
-rw-r--r--usr.sbin/nologin/nologin.564
-rw-r--r--usr.sbin/nologin/nologin.854
-rw-r--r--usr.sbin/nologin/nologin.sh38
-rw-r--r--usr.sbin/nslookup/Makefile22
-rw-r--r--usr.sbin/pccard/Makefile7
-rw-r--r--usr.sbin/pccard/Makefile.inc2
-rw-r--r--usr.sbin/pccard/pccardc/Makefile14
-rw-r--r--usr.sbin/pccard/pccardc/dumpcis.c113
-rw-r--r--usr.sbin/pccard/pccardc/enabler.c153
-rw-r--r--usr.sbin/pccard/pccardc/pccardc.c91
-rw-r--r--usr.sbin/pccard/pccardc/pccardmem.c71
-rw-r--r--usr.sbin/pccard/pccardc/printcis.c703
-rw-r--r--usr.sbin/pccard/pccardc/rdmap.c105
-rw-r--r--usr.sbin/pccard/pccardc/rdreg.c81
-rw-r--r--usr.sbin/pccard/pccardc/wrattr.c77
-rw-r--r--usr.sbin/pccard/pccardc/wrreg.c73
-rw-r--r--usr.sbin/pccard/pccardd/Makefile12
-rw-r--r--usr.sbin/pccard/pccardd/cardd.c666
-rw-r--r--usr.sbin/pccard/pccardd/cardd.h147
-rw-r--r--usr.sbin/pccard/pccardd/file.c626
-rw-r--r--usr.sbin/pccard/pccardd/pccard.conf.5202
-rw-r--r--usr.sbin/pccard/pccardd/pccardd.8158
-rw-r--r--usr.sbin/pccard/pccardd/readcis.c615
-rw-r--r--usr.sbin/pccard/pccardd/readcis.h133
-rw-r--r--usr.sbin/pccard/pccardd/util.c272
-rw-r--r--usr.sbin/pciconf/Makefile7
-rw-r--r--usr.sbin/pciconf/pathnames.h1
-rw-r--r--usr.sbin/pciconf/pciconf.8185
-rw-r--r--usr.sbin/pciconf/pciconf.c243
-rw-r--r--usr.sbin/pcvt/Makefile7
-rw-r--r--usr.sbin/pcvt/Makefile.inc8
-rw-r--r--usr.sbin/pcvt/Misc/Doc/Acknowledgements111
-rw-r--r--usr.sbin/pcvt/Misc/Doc/Bibliography189
-rw-r--r--usr.sbin/pcvt/Misc/Doc/BugList62
-rw-r--r--usr.sbin/pcvt/Misc/Doc/ChangeLog899
-rw-r--r--usr.sbin/pcvt/Misc/Doc/CharGen149
-rw-r--r--usr.sbin/pcvt/Misc/Doc/Charsets99
-rw-r--r--usr.sbin/pcvt/Misc/Doc/EscapeSequences268
-rw-r--r--usr.sbin/pcvt/Misc/Doc/Keyboard.HP286
-rw-r--r--usr.sbin/pcvt/Misc/Doc/Keyboard.VT231
-rw-r--r--usr.sbin/pcvt/Misc/Doc/Makefile15
-rw-r--r--usr.sbin/pcvt/Misc/Doc/Manifest162
-rw-r--r--usr.sbin/pcvt/Misc/Doc/NotesAndHints321
-rw-r--r--usr.sbin/pcvt/Misc/Doc/TestedHardware79
-rw-r--r--usr.sbin/pcvt/Misc/Doc/ToDo13
-rw-r--r--usr.sbin/pcvt/Misc/Etc/Makefile14
-rw-r--r--usr.sbin/pcvt/Misc/Etc/Termcap284
-rw-r--r--usr.sbin/pcvt/Misc/Etc/Terminfo41
-rw-r--r--usr.sbin/pcvt/Misc/Etc/pcvt.el19
-rw-r--r--usr.sbin/pcvt/Misc/Etc/rc.local264
-rw-r--r--usr.sbin/pcvt/Misc/Etc/uemacs.tar.Z.uu594
-rw-r--r--usr.sbin/pcvt/Misc/Etc/xmodmap-german117
-rw-r--r--usr.sbin/pcvt/Misc/Makefile15
-rw-r--r--usr.sbin/pcvt/Misc/Makefile.inc3
-rw-r--r--usr.sbin/pcvt/Misc/README.FIRST291
-rw-r--r--usr.sbin/pcvt/cursor/Makefile3
-rw-r--r--usr.sbin/pcvt/cursor/cursor.176
-rw-r--r--usr.sbin/pcvt/cursor/cursor.c157
-rw-r--r--usr.sbin/pcvt/demo/Makefile54
-rw-r--r--usr.sbin/pcvt/demo/README20
-rw-r--r--usr.sbin/pcvt/demo/chardemo.vt.gz.uu53
-rw-r--r--usr.sbin/pcvt/demo/colors.vt.gz.uu15
-rw-r--r--usr.sbin/pcvt/demo/cowscene.vt.gz.uu90
-rw-r--r--usr.sbin/pcvt/demo/outerlimit.vt.gz.uu193
-rw-r--r--usr.sbin/pcvt/demo/playvt.c111
-rw-r--r--usr.sbin/pcvt/demo/sgr.vt.gz.uu11
-rw-r--r--usr.sbin/pcvt/demo/twzone.vt.gz.uu350
-rw-r--r--usr.sbin/pcvt/demo/xmas.vt.gz.uu110
-rw-r--r--usr.sbin/pcvt/fed/Makefile29
-rw-r--r--usr.sbin/pcvt/fed/edit.c340
-rw-r--r--usr.sbin/pcvt/fed/fed.c168
-rw-r--r--usr.sbin/pcvt/fed/fed.h127
-rw-r--r--usr.sbin/pcvt/fed/misc.c353
-rw-r--r--usr.sbin/pcvt/fed/select.c334
-rw-r--r--usr.sbin/pcvt/fontedit/Makefile20
-rw-r--r--usr.sbin/pcvt/fontedit/README36
-rw-r--r--usr.sbin/pcvt/fontedit/fontedit.158
-rw-r--r--usr.sbin/pcvt/fontedit/fontedit.c925
-rw-r--r--usr.sbin/pcvt/fonts/COPYRIGHT38
-rw-r--r--usr.sbin/pcvt/fonts/Makefile70
-rw-r--r--usr.sbin/pcvt/fonts/vt100pc.814.uu83
-rw-r--r--usr.sbin/pcvt/fonts/vt100sg.814.uu83
-rw-r--r--usr.sbin/pcvt/fonts/vt220h.808.uu49
-rw-r--r--usr.sbin/pcvt/fonts/vt220h.810.uu60
-rw-r--r--usr.sbin/pcvt/fonts/vt220h.814.uu83
-rw-r--r--usr.sbin/pcvt/fonts/vt220h.816.uu95
-rw-r--r--usr.sbin/pcvt/fonts/vt220l.808.uu49
-rw-r--r--usr.sbin/pcvt/fonts/vt220l.810.uu60
-rw-r--r--usr.sbin/pcvt/fonts/vt220l.814.uu83
-rw-r--r--usr.sbin/pcvt/fonts/vt220l.816.uu95
-rw-r--r--usr.sbin/pcvt/ispcvt/Makefile4
-rw-r--r--usr.sbin/pcvt/ispcvt/ispcvt.891
-rw-r--r--usr.sbin/pcvt/ispcvt/ispcvt.c308
-rw-r--r--usr.sbin/pcvt/kbdio/Makefile37
-rw-r--r--usr.sbin/pcvt/kbdio/kbdio.y333
-rw-r--r--usr.sbin/pcvt/kbdio/lex.l102
-rw-r--r--usr.sbin/pcvt/kcon/Makefile17
-rw-r--r--usr.sbin/pcvt/kcon/kcon.1122
-rw-r--r--usr.sbin/pcvt/kcon/kcon.c753
-rw-r--r--usr.sbin/pcvt/keycap/Makefile33
-rw-r--r--usr.sbin/pcvt/keycap/keycap.3124
-rw-r--r--usr.sbin/pcvt/keycap/keycap.c377
-rw-r--r--usr.sbin/pcvt/keycap/keycap.h49
-rw-r--r--usr.sbin/pcvt/keycap/keycap.src613
-rw-r--r--usr.sbin/pcvt/keycap/man5/keycap.5130
-rw-r--r--usr.sbin/pcvt/loadfont/Makefile3
-rw-r--r--usr.sbin/pcvt/loadfont/loadfont.190
-rw-r--r--usr.sbin/pcvt/loadfont/loadfont.c345
-rw-r--r--usr.sbin/pcvt/mcon/Makefile3
-rw-r--r--usr.sbin/pcvt/mcon/mcon.1166
-rw-r--r--usr.sbin/pcvt/mcon/mcon.c193
-rw-r--r--usr.sbin/pcvt/scon/Makefile3
-rw-r--r--usr.sbin/pcvt/scon/scon.1214
-rw-r--r--usr.sbin/pcvt/scon/scon.c856
-rw-r--r--usr.sbin/pcvt/set2061/CAUTION28
-rw-r--r--usr.sbin/pcvt/set2061/ICD2061Aalt.c297
-rw-r--r--usr.sbin/pcvt/set2061/Makefile13
-rw-r--r--usr.sbin/pcvt/set2061/README22
-rw-r--r--usr.sbin/pcvt/set2061/compiler.h341
-rw-r--r--usr.sbin/pcvt/set2061/main.c112
-rw-r--r--usr.sbin/pcvt/userkeys/Makefile18
-rw-r--r--usr.sbin/pcvt/userkeys/vt220keys.1131
-rw-r--r--usr.sbin/pcvt/userkeys/vt220keys.c297
-rw-r--r--usr.sbin/pcvt/vgaio/CAUTION28
-rw-r--r--usr.sbin/pcvt/vgaio/Makefile37
-rw-r--r--usr.sbin/pcvt/vgaio/lex.l82
-rw-r--r--usr.sbin/pcvt/vgaio/vgaio.8144
-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/Makefile6
-rw-r--r--usr.sbin/pcvt/vttest/README57
-rw-r--r--usr.sbin/pcvt/vttest/esc.c398
-rw-r--r--usr.sbin/pcvt/vttest/header.h43
-rw-r--r--usr.sbin/pcvt/vttest/main.c2016
-rw-r--r--usr.sbin/pcvt/vttest/vttest.113
-rw-r--r--usr.sbin/periodic/Makefile11
-rw-r--r--usr.sbin/periodic/periodic.8137
-rw-r--r--usr.sbin/periodic/periodic.sh64
-rw-r--r--usr.sbin/pkg_install/Makefile3
-rw-r--r--usr.sbin/pkg_install/Makefile.inc2
-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.c232
-rw-r--r--usr.sbin/pkg_install/add/futil.c93
-rw-r--r--usr.sbin/pkg_install/add/main.c157
-rw-r--r--usr.sbin/pkg_install/add/perform.c479
-rw-r--r--usr.sbin/pkg_install/add/pkg_add.1365
-rw-r--r--usr.sbin/pkg_install/create/Makefile18
-rw-r--r--usr.sbin/pkg_install/create/create.h46
-rw-r--r--usr.sbin/pkg_install/create/main.c160
-rw-r--r--usr.sbin/pkg_install/create/perform.c297
-rw-r--r--usr.sbin/pkg_install/create/pkg_create.1382
-rw-r--r--usr.sbin/pkg_install/create/pl.c220
-rw-r--r--usr.sbin/pkg_install/delete/Makefile17
-rw-r--r--usr.sbin/pkg_install/delete/delete.h33
-rw-r--r--usr.sbin/pkg_install/delete/main.c108
-rw-r--r--usr.sbin/pkg_install/delete/perform.c222
-rw-r--r--usr.sbin/pkg_install/delete/pkg_delete.1178
-rw-r--r--usr.sbin/pkg_install/info/Makefile17
-rw-r--r--usr.sbin/pkg_install/info/info.h59
-rw-r--r--usr.sbin/pkg_install/info/main.c159
-rw-r--r--usr.sbin/pkg_install/info/perform.c206
-rw-r--r--usr.sbin/pkg_install/info/pkg_info.1135
-rw-r--r--usr.sbin/pkg_install/info/show.c199
-rw-r--r--usr.sbin/pkg_install/lib/Makefile10
-rw-r--r--usr.sbin/pkg_install/lib/exec.c62
-rw-r--r--usr.sbin/pkg_install/lib/file.c539
-rw-r--r--usr.sbin/pkg_install/lib/global.c35
-rw-r--r--usr.sbin/pkg_install/lib/lib.h176
-rw-r--r--usr.sbin/pkg_install/lib/msg.c77
-rw-r--r--usr.sbin/pkg_install/lib/pen.c145
-rw-r--r--usr.sbin/pkg_install/lib/plist.c505
-rw-r--r--usr.sbin/pkg_install/lib/str.c111
-rwxr-xr-xusr.sbin/pkg_install/tkpkg186
-rw-r--r--usr.sbin/pnpinfo/Makefile11
-rw-r--r--usr.sbin/portmap/Makefile11
-rw-r--r--usr.sbin/portmap/from_local.c157
-rw-r--r--usr.sbin/portmap/pmap_check.c264
-rw-r--r--usr.sbin/portmap/pmap_check.h11
-rw-r--r--usr.sbin/portmap/pmap_dump/Makefile7
-rw-r--r--usr.sbin/portmap/pmap_dump/pmap_dump.c68
-rw-r--r--usr.sbin/portmap/pmap_set/Makefile7
-rw-r--r--usr.sbin/portmap/pmap_set/pmap_set.c79
-rw-r--r--usr.sbin/portmap/portmap.8114
-rw-r--r--usr.sbin/portmap/portmap.c608
-rw-r--r--usr.sbin/ppp/Makefile23
-rw-r--r--usr.sbin/ppp/README.alias352
-rw-r--r--usr.sbin/ppp/README.nat352
-rw-r--r--usr.sbin/ppp/alias_cmd.c190
-rw-r--r--usr.sbin/ppp/alias_cmd.h6
-rw-r--r--usr.sbin/ppp/arp.c311
-rw-r--r--usr.sbin/ppp/arp.h25
-rw-r--r--usr.sbin/ppp/async.c198
-rw-r--r--usr.sbin/ppp/async.h8
-rw-r--r--usr.sbin/ppp/auth.c235
-rw-r--r--usr.sbin/ppp/auth.h41
-rw-r--r--usr.sbin/ppp/ccp.c275
-rw-r--r--usr.sbin/ppp/ccp.h56
-rw-r--r--usr.sbin/ppp/chap.c311
-rw-r--r--usr.sbin/ppp/chap.h30
-rw-r--r--usr.sbin/ppp/chap_ms.c114
-rw-r--r--usr.sbin/ppp/chap_ms.h31
-rw-r--r--usr.sbin/ppp/chat.c640
-rw-r--r--usr.sbin/ppp/chat.h29
-rw-r--r--usr.sbin/ppp/command.c1550
-rw-r--r--usr.sbin/ppp/command.h51
-rw-r--r--usr.sbin/ppp/defs.c26
-rw-r--r--usr.sbin/ppp/defs.h88
-rw-r--r--usr.sbin/ppp/filter.c484
-rw-r--r--usr.sbin/ppp/filter.h87
-rw-r--r--usr.sbin/ppp/fsm.c785
-rw-r--r--usr.sbin/ppp/fsm.h132
-rw-r--r--usr.sbin/ppp/hdlc.c444
-rw-r--r--usr.sbin/ppp/hdlc.h68
-rw-r--r--usr.sbin/ppp/id.c145
-rw-r--r--usr.sbin/ppp/id.h13
-rw-r--r--usr.sbin/ppp/ip.c499
-rw-r--r--usr.sbin/ppp/ip.h30
-rw-r--r--usr.sbin/ppp/ipcp.c618
-rw-r--r--usr.sbin/ppp/ipcp.h78
-rw-r--r--usr.sbin/ppp/lcp.c683
-rw-r--r--usr.sbin/ppp/lcp.h83
-rw-r--r--usr.sbin/ppp/lcpproto.h39
-rw-r--r--usr.sbin/ppp/loadalias.c92
-rw-r--r--usr.sbin/ppp/loadalias.h21
-rw-r--r--usr.sbin/ppp/log.c221
-rw-r--r--usr.sbin/ppp/log.h44
-rw-r--r--usr.sbin/ppp/lqr.c270
-rw-r--r--usr.sbin/ppp/lqr.h64
-rw-r--r--usr.sbin/ppp/main.c1040
-rw-r--r--usr.sbin/ppp/main.h32
-rw-r--r--usr.sbin/ppp/mbuf.c175
-rw-r--r--usr.sbin/ppp/mbuf.h63
-rw-r--r--usr.sbin/ppp/modem.c962
-rw-r--r--usr.sbin/ppp/modem.h42
-rw-r--r--usr.sbin/ppp/nat_cmd.c190
-rw-r--r--usr.sbin/ppp/nat_cmd.h6
-rw-r--r--usr.sbin/ppp/os.c395
-rw-r--r--usr.sbin/ppp/os.h33
-rw-r--r--usr.sbin/ppp/pap.c218
-rw-r--r--usr.sbin/ppp/pap.h29
-rw-r--r--usr.sbin/ppp/pathnames.h43
-rw-r--r--usr.sbin/ppp/phase.c66
-rw-r--r--usr.sbin/ppp/phase.h32
-rw-r--r--usr.sbin/ppp/ppp.82351
-rw-r--r--usr.sbin/ppp/ppp.8.m42351
-rw-r--r--usr.sbin/ppp/pred.c221
-rw-r--r--usr.sbin/ppp/pred.h25
-rw-r--r--usr.sbin/ppp/route.c433
-rw-r--r--usr.sbin/ppp/route.h27
-rw-r--r--usr.sbin/ppp/server.c146
-rw-r--r--usr.sbin/ppp/server.h9
-rw-r--r--usr.sbin/ppp/sig.c72
-rw-r--r--usr.sbin/ppp/sig.h11
-rw-r--r--usr.sbin/ppp/slcompress.c587
-rw-r--r--usr.sbin/ppp/slcompress.h134
-rw-r--r--usr.sbin/ppp/systems.c279
-rw-r--r--usr.sbin/ppp/systems.h28
-rw-r--r--usr.sbin/ppp/timer.c295
-rw-r--r--usr.sbin/ppp/timer.h51
-rw-r--r--usr.sbin/ppp/vars.c195
-rw-r--r--usr.sbin/ppp/vars.h203
-rw-r--r--usr.sbin/ppp/vjcomp.c154
-rw-r--r--usr.sbin/ppp/vjcomp.h7
-rw-r--r--usr.sbin/pppctl/Makefile10
-rw-r--r--usr.sbin/pppctl/pppctl.8203
-rw-r--r--usr.sbin/pppctl/pppctl.c358
-rw-r--r--usr.sbin/pppd/Makefile31
-rw-r--r--usr.sbin/pppd/RELNOTES641
-rw-r--r--usr.sbin/pppd/args.h12
-rw-r--r--usr.sbin/pppd/auth.c1531
-rw-r--r--usr.sbin/pppd/callout.h18
-rw-r--r--usr.sbin/pppd/cbcp.c430
-rw-r--r--usr.sbin/pppd/cbcp.h26
-rw-r--r--usr.sbin/pppd/ccp.c1047
-rw-r--r--usr.sbin/pppd/ccp.h46
-rw-r--r--usr.sbin/pppd/chap.c867
-rw-r--r--usr.sbin/pppd/chap.h124
-rw-r--r--usr.sbin/pppd/chap_ms.c327
-rw-r--r--usr.sbin/pppd/chap_ms.h32
-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.c1528
-rw-r--r--usr.sbin/pppd/ipcp.h70
-rw-r--r--usr.sbin/pppd/ipxcp.c1414
-rw-r--r--usr.sbin/pppd/ipxcp.h71
-rw-r--r--usr.sbin/pppd/lcp.c1861
-rw-r--r--usr.sbin/pppd/lcp.h88
-rw-r--r--usr.sbin/pppd/lock.c122
-rw-r--r--usr.sbin/pppd/magic.c87
-rw-r--r--usr.sbin/pppd/magic.h23
-rw-r--r--usr.sbin/pppd/main.c1598
-rw-r--r--usr.sbin/pppd/options.c2535
-rw-r--r--usr.sbin/pppd/patchlevel.h6
-rw-r--r--usr.sbin/pppd/pathnames.h32
-rw-r--r--usr.sbin/pppd/ppp.h41
-rw-r--r--usr.sbin/pppd/pppd.81158
-rw-r--r--usr.sbin/pppd/pppd.h487
-rw-r--r--usr.sbin/pppd/sys-bsd.c1556
-rw-r--r--usr.sbin/pppd/upap.c618
-rw-r--r--usr.sbin/pppd/upap.h87
-rw-r--r--usr.sbin/pppstats/Makefile10
-rw-r--r--usr.sbin/pppstats/pppstats.8217
-rw-r--r--usr.sbin/pppstats/pppstats.c523
-rw-r--r--usr.sbin/pstat/Makefile13
-rw-r--r--usr.sbin/pstat/pstat.8386
-rw-r--r--usr.sbin/pstat/pstat.c1148
-rw-r--r--usr.sbin/pw/Makefile19
-rw-r--r--usr.sbin/pw/README22
-rw-r--r--usr.sbin/pw/bitmap.c132
-rw-r--r--usr.sbin/pw/bitmap.h50
-rw-r--r--usr.sbin/pw/cpdir.c116
-rw-r--r--usr.sbin/pw/edgroup.c226
-rw-r--r--usr.sbin/pw/fileupd.c191
-rw-r--r--usr.sbin/pw/grupd.c131
-rw-r--r--usr.sbin/pw/psdate.c300
-rw-r--r--usr.sbin/pw/psdate.h40
-rw-r--r--usr.sbin/pw/pw.8847
-rw-r--r--usr.sbin/pw/pw.c343
-rw-r--r--usr.sbin/pw/pw.conf.5301
-rw-r--r--usr.sbin/pw/pw.h127
-rw-r--r--usr.sbin/pw/pw_conf.c496
-rw-r--r--usr.sbin/pw/pw_group.c335
-rw-r--r--usr.sbin/pw/pw_log.c67
-rw-r--r--usr.sbin/pw/pw_nis.c73
-rw-r--r--usr.sbin/pw/pw_user.c1111
-rw-r--r--usr.sbin/pw/pwupd.c163
-rw-r--r--usr.sbin/pw/pwupd.h81
-rw-r--r--usr.sbin/pw/rm_r.c74
-rw-r--r--usr.sbin/pwd_mkdb/Makefile8
-rw-r--r--usr.sbin/pwd_mkdb/pw_scan.c144
-rw-r--r--usr.sbin/pwd_mkdb/pw_scan.h36
-rw-r--r--usr.sbin/pwd_mkdb/pwd_mkdb.8141
-rw-r--r--usr.sbin/pwd_mkdb/pwd_mkdb.c577
-rw-r--r--usr.sbin/qcamcontrol/Makefile3
-rw-r--r--usr.sbin/qcamcontrol/qcamcontrol.168
-rw-r--r--usr.sbin/qcamcontrol/qcamcontrol.c224
-rw-r--r--usr.sbin/qcamcontrol/qcamtime/Makefile8
-rw-r--r--usr.sbin/qcamcontrol/qcamtime/qcamtime.c101
-rw-r--r--usr.sbin/quot/Makefile6
-rw-r--r--usr.sbin/quot/quot.8103
-rw-r--r--usr.sbin/quot/quot.c584
-rw-r--r--usr.sbin/quotaon/Makefile8
-rw-r--r--usr.sbin/quotaon/quotaon.8137
-rw-r--r--usr.sbin/quotaon/quotaon.c265
-rw-r--r--usr.sbin/rarpd/Makefile10
-rw-r--r--usr.sbin/rarpd/rarpd.8117
-rw-r--r--usr.sbin/rarpd/rarpd.c1010
-rw-r--r--usr.sbin/repquota/Makefile6
-rw-r--r--usr.sbin/repquota/repquota.8103
-rw-r--r--usr.sbin/repquota/repquota.c391
-rw-r--r--usr.sbin/rmt/Makefile11
-rw-r--r--usr.sbin/rmt/rmt.8218
-rw-r--r--usr.sbin/rmt/rmt.c253
-rw-r--r--usr.sbin/rndcontrol/Makefile9
-rw-r--r--usr.sbin/rndcontrol/random.4187
-rw-r--r--usr.sbin/rndcontrol/rndcontrol.896
-rw-r--r--usr.sbin/rndcontrol/rndcontrol.c121
-rw-r--r--usr.sbin/rpc.lockd/Makefile26
-rw-r--r--usr.sbin/rpc.lockd/handles.c61
-rw-r--r--usr.sbin/rpc.lockd/lockd.c107
-rw-r--r--usr.sbin/rpc.lockd/lockd.h44
-rw-r--r--usr.sbin/rpc.lockd/procs.c592
-rw-r--r--usr.sbin/rpc.lockd/rpc.lockd.895
-rw-r--r--usr.sbin/rpc.lockd/test.c366
-rw-r--r--usr.sbin/rpc.statd/Makefile27
-rw-r--r--usr.sbin/rpc.statd/file.c350
-rw-r--r--usr.sbin/rpc.statd/procs.c355
-rw-r--r--usr.sbin/rpc.statd/rpc.statd.8104
-rw-r--r--usr.sbin/rpc.statd/statd.c140
-rw-r--r--usr.sbin/rpc.statd/statd.h123
-rw-r--r--usr.sbin/rpc.statd/test.c144
-rw-r--r--usr.sbin/rpc.yppasswdd/Makefile65
-rw-r--r--usr.sbin/rpc.yppasswdd/pw_copy.c143
-rw-r--r--usr.sbin/rpc.yppasswdd/pw_util.c184
-rw-r--r--usr.sbin/rpc.yppasswdd/rpc.yppasswdd.8327
-rw-r--r--usr.sbin/rpc.yppasswdd/yppasswd_private.x72
-rw-r--r--usr.sbin/rpc.yppasswdd/yppasswdd_extern.h82
-rw-r--r--usr.sbin/rpc.yppasswdd/yppasswdd_main.c337
-rw-r--r--usr.sbin/rpc.yppasswdd/yppasswdd_server.c868
-rw-r--r--usr.sbin/rpc.yppasswdd/yppwupdate34
-rw-r--r--usr.sbin/rpc.ypupdated/Makefile35
-rw-r--r--usr.sbin/rpc.ypupdated/update.c363
-rw-r--r--usr.sbin/rpc.ypupdated/yp_dbdelete.c71
-rw-r--r--usr.sbin/rpc.ypupdated/yp_dbupdate.c154
-rwxr-xr-xusr.sbin/rpc.ypupdated/ypupdate33
-rw-r--r--usr.sbin/rpc.ypupdated/ypupdated_extern.h29
-rw-r--r--usr.sbin/rpc.ypupdated/ypupdated_main.c289
-rw-r--r--usr.sbin/rpc.ypupdated/ypupdated_server.c235
-rw-r--r--usr.sbin/rpc.ypxfrd/Makefile37
-rw-r--r--usr.sbin/rpc.ypxfrd/rpc.ypxfrd.8140
-rw-r--r--usr.sbin/rpc.ypxfrd/ypxfrd_extern.h50
-rw-r--r--usr.sbin/rpc.ypxfrd/ypxfrd_main.c297
-rw-r--r--usr.sbin/rpc.ypxfrd/ypxfrd_server.c147
-rw-r--r--usr.sbin/rtprio/Makefile10
-rw-r--r--usr.sbin/rtprio/rtprio.1206
-rw-r--r--usr.sbin/rtprio/rtprio.c140
-rw-r--r--usr.sbin/rwhod/Makefile6
-rw-r--r--usr.sbin/rwhod/rwhod.8201
-rw-r--r--usr.sbin/rwhod/rwhod.c728
-rw-r--r--usr.sbin/sa/Makefile7
-rw-r--r--usr.sbin/sa/extern.h100
-rw-r--r--usr.sbin/sa/main.c553
-rw-r--r--usr.sbin/sa/pathnames.h35
-rw-r--r--usr.sbin/sa/pdb.c420
-rw-r--r--usr.sbin/sa/sa.8238
-rw-r--r--usr.sbin/sa/usrdb.c284
-rw-r--r--usr.sbin/sade/Makefile79
-rw-r--r--usr.sbin/sade/command.c179
-rw-r--r--usr.sbin/sade/config.c869
-rw-r--r--usr.sbin/sade/devices.c424
-rw-r--r--usr.sbin/sade/disks.c731
-rw-r--r--usr.sbin/sade/dispatch.c405
-rw-r--r--usr.sbin/sade/dmenu.c304
-rw-r--r--usr.sbin/sade/globals.c71
-rw-r--r--usr.sbin/sade/help/partition.hlp153
-rw-r--r--usr.sbin/sade/help/slice.hlp59
-rw-r--r--usr.sbin/sade/install.c1138
-rw-r--r--usr.sbin/sade/keymap.c95
-rw-r--r--usr.sbin/sade/label.c1249
-rw-r--r--usr.sbin/sade/list.h60
-rw-r--r--usr.sbin/sade/main.c134
-rw-r--r--usr.sbin/sade/menus.c1465
-rw-r--r--usr.sbin/sade/misc.c485
-rw-r--r--usr.sbin/sade/msg.c320
-rw-r--r--usr.sbin/sade/rtermcap.c15
-rw-r--r--usr.sbin/sade/sade.8804
-rw-r--r--usr.sbin/sade/sade.h726
-rw-r--r--usr.sbin/sade/system.c376
-rw-r--r--usr.sbin/sade/termcap.c135
-rw-r--r--usr.sbin/sade/variable.c193
-rw-r--r--usr.sbin/sade/wizard.c232
-rw-r--r--usr.sbin/sendmail/Makefile2
-rw-r--r--usr.sbin/sendmail/cf/cf/Makefile47
-rw-r--r--usr.sbin/sendmail/cf/cf/Makefile.dist108
-rw-r--r--usr.sbin/sendmail/cf/cf/freebsd.mc54
-rw-r--r--usr.sbin/sendmail/cf/cf/freefall.mc47
-rw-r--r--usr.sbin/sendmail/cf/cf/hub.mc124
-rw-r--r--usr.sbin/sendmail/cf/cf/knecht.mc5
-rw-r--r--usr.sbin/sendmail/cf/feature/smrsh.m42
-rw-r--r--usr.sbin/sendmail/cf/m4/cfhead.m43
-rw-r--r--usr.sbin/sendmail/cf/ostype/bsd4.4.m41
-rw-r--r--usr.sbin/sendmail/cf/sh/makeinfo.sh2
-rw-r--r--usr.sbin/sendmail/contrib/bitdomain.c8
-rw-r--r--usr.sbin/sendmail/contrib/re-mqueue.pl2
-rw-r--r--usr.sbin/sendmail/contrib/xla/README207
-rw-r--r--usr.sbin/sendmail/contrib/xla/xla.c532
-rw-r--r--usr.sbin/sendmail/doc/changes/changes.ps1092
-rw-r--r--usr.sbin/sendmail/doc/intro/intro.ps1295
-rw-r--r--usr.sbin/sendmail/doc/op/op.ps5944
-rw-r--r--usr.sbin/sendmail/doc/usenix/usenix.ps1004
-rw-r--r--usr.sbin/sendmail/mail.local/Makefile3
-rw-r--r--usr.sbin/sendmail/mail.local/mail.local.814
-rw-r--r--usr.sbin/sendmail/mail.local/mail.local.c42
-rw-r--r--usr.sbin/sendmail/mailstats/Makefile2
-rw-r--r--usr.sbin/sendmail/mailstats/mailstats.8130
-rw-r--r--usr.sbin/sendmail/mailstats/mailstats.c4
-rw-r--r--usr.sbin/sendmail/makemap/Makefile2
-rw-r--r--usr.sbin/sendmail/makemap/makemap.c21
-rw-r--r--usr.sbin/sendmail/praliases/Makefile5
-rw-r--r--usr.sbin/sendmail/praliases/praliases.c12
-rw-r--r--usr.sbin/sendmail/rmail/Makefile9
-rw-r--r--usr.sbin/sendmail/rmail/rmail.c10
-rw-r--r--usr.sbin/sendmail/smrsh/Makefile6
-rw-r--r--usr.sbin/sendmail/smrsh/smrsh.815
-rw-r--r--usr.sbin/sendmail/smrsh/smrsh.c11
-rw-r--r--usr.sbin/sendmail/src/Makefile36
-rw-r--r--usr.sbin/sendmail/src/Makefiles/Makefile.386BSD43
-rw-r--r--usr.sbin/sendmail/src/Makefiles/Makefile.A-UX113
-rw-r--r--usr.sbin/sendmail/src/Makefiles/Makefile.AIX116
-rw-r--r--usr.sbin/sendmail/src/Makefiles/Makefile.Altos109
-rw-r--r--usr.sbin/sendmail/src/Makefiles/Makefile.BSD-OS37
-rw-r--r--usr.sbin/sendmail/src/Makefiles/Makefile.BSD43128
-rw-r--r--usr.sbin/sendmail/src/Makefiles/Makefile.CLIX119
-rw-r--r--usr.sbin/sendmail/src/Makefiles/Makefile.CSOS114
-rw-r--r--usr.sbin/sendmail/src/Makefiles/Makefile.ConvexOS110
-rw-r--r--usr.sbin/sendmail/src/Makefiles/Makefile.Dell117
-rw-r--r--usr.sbin/sendmail/src/Makefiles/Makefile.DomainOS128
-rw-r--r--usr.sbin/sendmail/src/Makefiles/Makefile.Dynix118
-rw-r--r--usr.sbin/sendmail/src/Makefiles/Makefile.EWS-UX_V132
-rw-r--r--usr.sbin/sendmail/src/Makefiles/Makefile.FreeBSD50
-rw-r--r--usr.sbin/sendmail/src/Makefiles/Makefile.HP-UX114
-rw-r--r--usr.sbin/sendmail/src/Makefiles/Makefile.HP-UX.10.x114
-rw-r--r--usr.sbin/sendmail/src/Makefiles/Makefile.IRIX113
-rw-r--r--usr.sbin/sendmail/src/Makefiles/Makefile.IRIX.5.x115
-rw-r--r--usr.sbin/sendmail/src/Makefiles/Makefile.IRIX64114
-rw-r--r--usr.sbin/sendmail/src/Makefiles/Makefile.ISC108
-rw-r--r--usr.sbin/sendmail/src/Makefiles/Makefile.KSR112
-rw-r--r--usr.sbin/sendmail/src/Makefiles/Makefile.LUNA147
-rw-r--r--usr.sbin/sendmail/src/Makefiles/Makefile.Linux134
-rw-r--r--usr.sbin/sendmail/src/Makefiles/Makefile.Mach386112
-rw-r--r--usr.sbin/sendmail/src/Makefiles/Makefile.NCR3000113
-rw-r--r--usr.sbin/sendmail/src/Makefiles/Makefile.NEWS-OS.4.x110
-rw-r--r--usr.sbin/sendmail/src/Makefiles/Makefile.NEWS-OS.6.x133
-rw-r--r--usr.sbin/sendmail/src/Makefiles/Makefile.NEXTSTEP128
-rw-r--r--usr.sbin/sendmail/src/Makefiles/Makefile.NeXT122
-rw-r--r--usr.sbin/sendmail/src/Makefiles/Makefile.NetBSD47
-rw-r--r--usr.sbin/sendmail/src/Makefiles/Makefile.NonStop-UX116
-rw-r--r--usr.sbin/sendmail/src/Makefiles/Makefile.OSF1118
-rw-r--r--usr.sbin/sendmail/src/Makefiles/Makefile.PTX117
-rw-r--r--usr.sbin/sendmail/src/Makefiles/Makefile.Paragon114
-rw-r--r--usr.sbin/sendmail/src/Makefiles/Makefile.RISCos122
-rw-r--r--usr.sbin/sendmail/src/Makefiles/Makefile.SCO109
-rw-r--r--usr.sbin/sendmail/src/Makefiles/Makefile.SCO.3.2v4.2109
-rw-r--r--usr.sbin/sendmail/src/Makefiles/Makefile.SVR4117
-rw-r--r--usr.sbin/sendmail/src/Makefiles/Makefile.Solaris124
-rw-r--r--usr.sbin/sendmail/src/Makefiles/Makefile.SunOS116
-rw-r--r--usr.sbin/sendmail/src/Makefiles/Makefile.SunOS.4.0118
-rw-r--r--usr.sbin/sendmail/src/Makefiles/Makefile.SunOS.5.1124
-rw-r--r--usr.sbin/sendmail/src/Makefiles/Makefile.SunOS.5.2124
-rw-r--r--usr.sbin/sendmail/src/Makefiles/Makefile.SunOS.5.3122
-rw-r--r--usr.sbin/sendmail/src/Makefiles/Makefile.SunOS.5.4122
-rw-r--r--usr.sbin/sendmail/src/Makefiles/Makefile.SunOS.5.5122
-rw-r--r--usr.sbin/sendmail/src/Makefiles/Makefile.Titan119
-rw-r--r--usr.sbin/sendmail/src/Makefiles/Makefile.ULTRIX117
-rw-r--r--usr.sbin/sendmail/src/Makefiles/Makefile.UMAX119
-rw-r--r--usr.sbin/sendmail/src/Makefiles/Makefile.UNICOS117
-rw-r--r--usr.sbin/sendmail/src/Makefiles/Makefile.UNIX_SV.4.x.i386118
-rw-r--r--usr.sbin/sendmail/src/Makefiles/Makefile.UX4800129
-rw-r--r--usr.sbin/sendmail/src/Makefiles/Makefile.UXPDS130
-rw-r--r--usr.sbin/sendmail/src/Makefiles/Makefile.Utah41
-rw-r--r--usr.sbin/sendmail/src/Makefiles/Makefile.dgux108
-rw-r--r--usr.sbin/sendmail/src/Makefiles/Makefile.uts.systemV189
-rw-r--r--usr.sbin/sendmail/src/aliases.54
-rw-r--r--usr.sbin/sendmail/src/collect.c4
-rw-r--r--usr.sbin/sendmail/src/conf.c26
-rw-r--r--usr.sbin/sendmail/src/conf.h27
-rw-r--r--usr.sbin/sendmail/src/daemon.c39
-rw-r--r--usr.sbin/sendmail/src/deliver.c87
-rw-r--r--usr.sbin/sendmail/src/domain.c4
-rw-r--r--usr.sbin/sendmail/src/headers.c24
-rw-r--r--usr.sbin/sendmail/src/main.c54
-rw-r--r--usr.sbin/sendmail/src/makesendmail331
-rw-r--r--usr.sbin/sendmail/src/parseaddr.c15
-rw-r--r--usr.sbin/sendmail/src/readcf.c14
-rw-r--r--usr.sbin/sendmail/src/recipient.c9
-rw-r--r--usr.sbin/sendmail/src/savemail.c70
-rw-r--r--usr.sbin/sendmail/src/sendmail.88
-rw-r--r--usr.sbin/sendmail/src/sendmail.h7
-rw-r--r--usr.sbin/sendmail/src/srvrsmtp.c30
-rw-r--r--usr.sbin/sendmail/src/sysexits.h118
-rw-r--r--usr.sbin/sendmail/src/udb.c4
-rw-r--r--usr.sbin/sendmail/src/usersmtp.c7
-rw-r--r--usr.sbin/sendmail/src/util.c12
-rw-r--r--usr.sbin/sgsc/Makefile5
-rw-r--r--usr.sbin/sgsc/sgsc.197
-rw-r--r--usr.sbin/sgsc/sgsc.c163
-rw-r--r--usr.sbin/sicontrol/Makefile5
-rw-r--r--usr.sbin/sicontrol/sicontrol.8114
-rw-r--r--usr.sbin/sicontrol/sicontrol.c585
-rw-r--r--usr.sbin/sliplogin/Makefile10
-rw-r--r--usr.sbin/sliplogin/pathnames.h47
-rw-r--r--usr.sbin/sliplogin/sliplogin.8313
-rw-r--r--usr.sbin/sliplogin/sliplogin.c543
-rw-r--r--usr.sbin/slstat/Makefile7
-rw-r--r--usr.sbin/slstat/slstat.8128
-rw-r--r--usr.sbin/slstat/slstat.c246
-rw-r--r--usr.sbin/spkrtest/Makefile7
-rw-r--r--usr.sbin/spkrtest/spkrtest.854
-rw-r--r--usr.sbin/spkrtest/spkrtest.pl137
-rw-r--r--usr.sbin/spray/Makefile8
-rw-r--r--usr.sbin/spray/spray.872
-rw-r--r--usr.sbin/spray/spray.c226
-rw-r--r--usr.sbin/stallion/Makefile5
-rw-r--r--usr.sbin/stallion/Makefile.inc7
-rw-r--r--usr.sbin/stallion/bootcode/2681.sys.uu690
-rw-r--r--usr.sbin/stallion/bootcode/Makefile34
-rw-r--r--usr.sbin/stallion/bootcode/cdk.sys.uu733
-rw-r--r--usr.sbin/stallion/bootcode/stl.4324
-rw-r--r--usr.sbin/stallion/stlload/Makefile8
-rw-r--r--usr.sbin/stallion/stlload/stlload.8124
-rw-r--r--usr.sbin/stallion/stlload/stlload.c504
-rw-r--r--usr.sbin/stallion/stlstats/Makefile9
-rw-r--r--usr.sbin/stallion/stlstats/stlstats.8135
-rw-r--r--usr.sbin/stallion/stlstats/stlstats.c581
-rw-r--r--usr.sbin/sysctl/Makefile6
-rw-r--r--usr.sbin/sysctl/pathconf.c240
-rw-r--r--usr.sbin/sysctl/sysctl.8238
-rw-r--r--usr.sbin/sysctl/sysctl.c461
-rw-r--r--usr.sbin/sysinstall/Makefile79
-rw-r--r--usr.sbin/sysinstall/anonFTP.c316
-rw-r--r--usr.sbin/sysinstall/cdrom.c163
-rw-r--r--usr.sbin/sysinstall/command.c179
-rw-r--r--usr.sbin/sysinstall/config.c869
-rw-r--r--usr.sbin/sysinstall/dev2c.sh80
-rw-r--r--usr.sbin/sysinstall/devices.c424
-rw-r--r--usr.sbin/sysinstall/disks.c731
-rw-r--r--usr.sbin/sysinstall/dispatch.c405
-rw-r--r--usr.sbin/sysinstall/dist.c827
-rw-r--r--usr.sbin/sysinstall/dist.h117
-rw-r--r--usr.sbin/sysinstall/dmenu.c304
-rw-r--r--usr.sbin/sysinstall/doc.c126
-rw-r--r--usr.sbin/sysinstall/dos.c106
-rw-r--r--usr.sbin/sysinstall/floppy.c204
-rw-r--r--usr.sbin/sysinstall/ftp.c257
-rw-r--r--usr.sbin/sysinstall/globals.c71
-rw-r--r--usr.sbin/sysinstall/help/anonftp.hlp19
-rw-r--r--usr.sbin/sysinstall/help/configure.hlp10
-rw-r--r--usr.sbin/sysinstall/help/distributions.hlp62
-rw-r--r--usr.sbin/sysinstall/help/drives.hlp92
-rw-r--r--usr.sbin/sysinstall/help/fixit.hlp9
-rw-r--r--usr.sbin/sysinstall/help/html.hlp19
-rw-r--r--usr.sbin/sysinstall/help/media.hlp50
-rw-r--r--usr.sbin/sysinstall/help/network_device.hlp56
-rw-r--r--usr.sbin/sysinstall/help/options.hlp124
-rw-r--r--usr.sbin/sysinstall/help/partition.hlp153
-rw-r--r--usr.sbin/sysinstall/help/register.hlp76
-rw-r--r--usr.sbin/sysinstall/help/shortcuts.hlp116
-rw-r--r--usr.sbin/sysinstall/help/slice.hlp59
-rw-r--r--usr.sbin/sysinstall/help/tcp.hlp30
-rw-r--r--usr.sbin/sysinstall/help/usage.hlp65
-rw-r--r--usr.sbin/sysinstall/help/usermgmt.hlp89
-rw-r--r--usr.sbin/sysinstall/index.c599
-rw-r--r--usr.sbin/sysinstall/install.c1138
-rw-r--r--usr.sbin/sysinstall/install.cfg96
-rw-r--r--usr.sbin/sysinstall/installUpgrade.c495
-rw-r--r--usr.sbin/sysinstall/keymap.c95
-rw-r--r--usr.sbin/sysinstall/label.c1249
-rw-r--r--usr.sbin/sysinstall/list.h60
-rw-r--r--usr.sbin/sysinstall/main.c134
-rw-r--r--usr.sbin/sysinstall/media.c754
-rw-r--r--usr.sbin/sysinstall/menus.c1465
-rw-r--r--usr.sbin/sysinstall/misc.c485
-rw-r--r--usr.sbin/sysinstall/msg.c320
-rw-r--r--usr.sbin/sysinstall/network.c300
-rw-r--r--usr.sbin/sysinstall/nfs.c110
-rw-r--r--usr.sbin/sysinstall/options.c304
-rw-r--r--usr.sbin/sysinstall/package.c219
-rw-r--r--usr.sbin/sysinstall/rtermcap.c15
-rw-r--r--usr.sbin/sysinstall/sysinstall.8804
-rw-r--r--usr.sbin/sysinstall/sysinstall.h726
-rw-r--r--usr.sbin/sysinstall/system.c376
-rw-r--r--usr.sbin/sysinstall/tape.c120
-rw-r--r--usr.sbin/sysinstall/tcpip.c397
-rw-r--r--usr.sbin/sysinstall/termcap.c135
-rw-r--r--usr.sbin/sysinstall/ufs.c63
-rw-r--r--usr.sbin/sysinstall/user.c727
-rw-r--r--usr.sbin/sysinstall/variable.c193
-rw-r--r--usr.sbin/sysinstall/wizard.c232
-rw-r--r--usr.sbin/syslogd/Makefile9
-rw-r--r--usr.sbin/syslogd/pathnames.h40
-rw-r--r--usr.sbin/syslogd/syslog.conf.5324
-rw-r--r--usr.sbin/syslogd/syslogd.8218
-rw-r--r--usr.sbin/syslogd/syslogd.c1749
-rw-r--r--usr.sbin/tcpdump/Makefile5
-rw-r--r--usr.sbin/tcpdump/Makefile.inc3
-rw-r--r--usr.sbin/tcpdump/tcpdump/Makefile30
-rw-r--r--usr.sbin/tcpdump/tcpslice/Makefile20
-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.1260
-rw-r--r--usr.sbin/tcpdump/tcpslice/tcpslice.c618
-rw-r--r--usr.sbin/tcpdump/tcpslice/tcpslice.h59
-rw-r--r--usr.sbin/tcpdump/tcpslice/util.c56
-rw-r--r--usr.sbin/timed/Makefile5
-rw-r--r--usr.sbin/timed/SMM.doc/timed/Makefile11
-rw-r--r--usr.sbin/timed/SMM.doc/timed/date53
-rw-r--r--usr.sbin/timed/SMM.doc/timed/loop54
-rw-r--r--usr.sbin/timed/SMM.doc/timed/spell.ok34
-rw-r--r--usr.sbin/timed/SMM.doc/timed/time53
-rw-r--r--usr.sbin/timed/SMM.doc/timed/timed.ms504
-rw-r--r--usr.sbin/timed/SMM.doc/timed/unused53
-rw-r--r--usr.sbin/timed/SMM.doc/timedop/Makefile7
-rw-r--r--usr.sbin/timed/SMM.doc/timedop/timed.ms279
-rw-r--r--usr.sbin/timed/timed/CHANGES144
-rw-r--r--usr.sbin/timed/timed/Makefile13
-rw-r--r--usr.sbin/timed/timed/acksend.c132
-rw-r--r--usr.sbin/timed/timed/byteorder.c86
-rw-r--r--usr.sbin/timed/timed/candidate.c167
-rw-r--r--usr.sbin/timed/timed/cksum.c87
-rw-r--r--usr.sbin/timed/timed/correct.c294
-rw-r--r--usr.sbin/timed/timed/extern.h89
-rw-r--r--usr.sbin/timed/timed/globals.h182
-rw-r--r--usr.sbin/timed/timed/master.c907
-rw-r--r--usr.sbin/timed/timed/measure.c352
-rw-r--r--usr.sbin/timed/timed/networkdelta.c264
-rw-r--r--usr.sbin/timed/timed/pathnames.h44
-rw-r--r--usr.sbin/timed/timed/readmsg.c488
-rw-r--r--usr.sbin/timed/timed/slave.c716
-rw-r--r--usr.sbin/timed/timed/timed.8226
-rw-r--r--usr.sbin/timed/timed/timed.c964
-rw-r--r--usr.sbin/timed/timedc/Makefile11
-rw-r--r--usr.sbin/timed/timedc/cmds.c526
-rw-r--r--usr.sbin/timed/timedc/cmdtab.c61
-rw-r--r--usr.sbin/timed/timedc/extern.h52
-rw-r--r--usr.sbin/timed/timedc/timedc.8145
-rw-r--r--usr.sbin/timed/timedc/timedc.c260
-rw-r--r--usr.sbin/timed/timedc/timedc.h64
-rw-r--r--usr.sbin/traceroute/Makefile25
-rw-r--r--usr.sbin/trpt/Makefile8
-rw-r--r--usr.sbin/trpt/trpt.8152
-rw-r--r--usr.sbin/trpt/trpt.c380
-rw-r--r--usr.sbin/tzsetup/Makefile9
-rw-r--r--usr.sbin/tzsetup/paths.h4
-rw-r--r--usr.sbin/tzsetup/tzsetup.874
-rw-r--r--usr.sbin/tzsetup/tzsetup.c671
-rw-r--r--usr.sbin/vidcontrol/Makefile6
-rw-r--r--usr.sbin/vidcontrol/decode.c80
-rw-r--r--usr.sbin/vidcontrol/decode.h1
-rw-r--r--usr.sbin/vidcontrol/path.h4
-rw-r--r--usr.sbin/vidcontrol/vidcontrol.1123
-rw-r--r--usr.sbin/vidcontrol/vidcontrol.c453
-rw-r--r--usr.sbin/vipw/Makefile7
-rw-r--r--usr.sbin/vipw/pw_util.c237
-rw-r--r--usr.sbin/vipw/pw_util.h42
-rw-r--r--usr.sbin/vipw/vipw.893
-rw-r--r--usr.sbin/vipw/vipw.c131
-rw-r--r--usr.sbin/vnconfig/Makefile7
-rw-r--r--usr.sbin/vnconfig/vnconfig.8180
-rw-r--r--usr.sbin/vnconfig/vnconfig.c449
-rw-r--r--usr.sbin/watch/Makefile9
-rw-r--r--usr.sbin/watch/watch.886
-rw-r--r--usr.sbin/watch/watch.c430
-rw-r--r--usr.sbin/wlconfig/Makefile7
-rw-r--r--usr.sbin/wlconfig/wlconfig.8133
-rw-r--r--usr.sbin/wlconfig/wlconfig.c419
-rw-r--r--usr.sbin/wormcontrol/Makefile4
-rw-r--r--usr.sbin/wormcontrol/wormcontrol.8175
-rw-r--r--usr.sbin/wormcontrol/wormcontrol.c158
-rw-r--r--usr.sbin/xntpd/COPYRIGHT62
-rw-r--r--usr.sbin/xntpd/Makefile7
-rw-r--r--usr.sbin/xntpd/Makefile.inc17
-rw-r--r--usr.sbin/xntpd/PORTING37
-rw-r--r--usr.sbin/xntpd/README156
-rw-r--r--usr.sbin/xntpd/README.FreeBSD15
-rw-r--r--usr.sbin/xntpd/RELNOTES216
-rw-r--r--usr.sbin/xntpd/TODO26
-rw-r--r--usr.sbin/xntpd/VERSION1
-rw-r--r--usr.sbin/xntpd/authstuff/Makefile27
-rw-r--r--usr.sbin/xntpd/authstuff/README13
-rw-r--r--usr.sbin/xntpd/authstuff/auth.samplekeys44
-rw-r--r--usr.sbin/xntpd/authstuff/auth.speed33
-rw-r--r--usr.sbin/xntpd/authstuff/authcert.c95
-rw-r--r--usr.sbin/xntpd/authstuff/authspeed.c315
-rw-r--r--usr.sbin/xntpd/authstuff/certdata34
-rw-r--r--usr.sbin/xntpd/authstuff/keyparity.c279
-rw-r--r--usr.sbin/xntpd/authstuff/makeIPFP.c345
-rw-r--r--usr.sbin/xntpd/authstuff/makePC1.c286
-rw-r--r--usr.sbin/xntpd/authstuff/makePC2.c238
-rw-r--r--usr.sbin/xntpd/authstuff/makeSP.c183
-rw-r--r--usr.sbin/xntpd/authstuff/md5_sample_output8
-rw-r--r--usr.sbin/xntpd/authstuff/md5driver.c211
-rw-r--r--usr.sbin/xntpd/authstuff/mkrandkeys.c167
-rw-r--r--usr.sbin/xntpd/authstuff/omakeIPFP.c361
-rw-r--r--usr.sbin/xntpd/authstuff/results2
-rw-r--r--usr.sbin/xntpd/authstuff/unixcert.c156
-rw-r--r--usr.sbin/xntpd/clockstuff/Makefile16
-rw-r--r--usr.sbin/xntpd/clockstuff/README31
-rw-r--r--usr.sbin/xntpd/clockstuff/chutest.c798
-rw-r--r--usr.sbin/xntpd/clockstuff/clktest.c511
-rw-r--r--usr.sbin/xntpd/clockstuff/propdelay.c536
-rw-r--r--usr.sbin/xntpd/conf/Config.CHATHAM211
-rw-r--r--usr.sbin/xntpd/conf/Config.HP-UX7
-rw-r--r--usr.sbin/xntpd/conf/Config.MONOMOY186
-rw-r--r--usr.sbin/xntpd/conf/Config.OSF17
-rw-r--r--usr.sbin/xntpd/conf/Config.SunOS7
-rw-r--r--usr.sbin/xntpd/conf/Config.TIGER182
-rw-r--r--usr.sbin/xntpd/conf/Config.TRURO202
-rw-r--r--usr.sbin/xntpd/conf/Config.ULTRIX7
-rw-r--r--usr.sbin/xntpd/conf/Config.VAX7
-rw-r--r--usr.sbin/xntpd/conf/Config.dartnet187
-rw-r--r--usr.sbin/xntpd/conf/Config.local190
-rw-r--r--usr.sbin/xntpd/conf/Config.plain190
-rw-r--r--usr.sbin/xntpd/conf/Config.solaris7
-rw-r--r--usr.sbin/xntpd/conf/Config.svr4167
-rw-r--r--usr.sbin/xntpd/conf/README11
-rw-r--r--usr.sbin/xntpd/conf/baldwin.conf40
-rw-r--r--usr.sbin/xntpd/conf/dewey.conf46
-rw-r--r--usr.sbin/xntpd/conf/grundoon.conf157
-rw-r--r--usr.sbin/xntpd/conf/maccarony.conf33
-rw-r--r--usr.sbin/xntpd/conf/malarky.conf27
-rw-r--r--usr.sbin/xntpd/conf/ntp.conf.dcf7719
-rw-r--r--usr.sbin/xntpd/conf/ntp.conf.gw34
-rw-r--r--usr.sbin/xntpd/conf/ntp.conf.ipl32
-rw-r--r--usr.sbin/xntpd/conf/ntp.conf.nsf156
-rw-r--r--usr.sbin/xntpd/conf/ntp.conf.shiningtree32
-rw-r--r--usr.sbin/xntpd/conf/ntp.conf.suzuki43
-rw-r--r--usr.sbin/xntpd/conf/pogo.conf34
-rw-r--r--usr.sbin/xntpd/conf/rackety.conf69
-rw-r--r--usr.sbin/xntpd/conf/snow-white.conf33
-rw-r--r--usr.sbin/xntpd/doc/README.irig306
-rw-r--r--usr.sbin/xntpd/doc/README.kern1374
-rw-r--r--usr.sbin/xntpd/doc/README.magic346
-rw-r--r--usr.sbin/xntpd/doc/README.refclock1421
-rw-r--r--usr.sbin/xntpd/doc/UofT146
-rw-r--r--usr.sbin/xntpd/doc/acts.c878
-rw-r--r--usr.sbin/xntpd/doc/notes.txt1258
-rw-r--r--usr.sbin/xntpd/doc/ntpdate.8189
-rw-r--r--usr.sbin/xntpd/doc/ntpq.8563
-rw-r--r--usr.sbin/xntpd/doc/ntptrace.8104
-rw-r--r--usr.sbin/xntpd/doc/tickadj.8143
-rw-r--r--usr.sbin/xntpd/doc/xntpd.81105
-rw-r--r--usr.sbin/xntpd/doc/xntpdc.8733
-rw-r--r--usr.sbin/xntpd/include/README21
-rw-r--r--usr.sbin/xntpd/include/in.h256
-rw-r--r--usr.sbin/xntpd/include/l_stdlib.h284
-rw-r--r--usr.sbin/xntpd/include/md5.h56
-rw-r--r--usr.sbin/xntpd/include/mx4200.h40
-rw-r--r--usr.sbin/xntpd/include/ntp.h706
-rw-r--r--usr.sbin/xntpd/include/ntp_calendar.h80
-rw-r--r--usr.sbin/xntpd/include/ntp_control.h253
-rw-r--r--usr.sbin/xntpd/include/ntp_datum.h30
-rw-r--r--usr.sbin/xntpd/include/ntp_filegen.h51
-rw-r--r--usr.sbin/xntpd/include/ntp_fp.h316
-rw-r--r--usr.sbin/xntpd/include/ntp_if.h51
-rwxr-xr-xusr.sbin/xntpd/include/ntp_in.h259
-rw-r--r--usr.sbin/xntpd/include/ntp_io.h25
-rw-r--r--usr.sbin/xntpd/include/ntp_machine.h741
-rw-r--r--usr.sbin/xntpd/include/ntp_malloc.h15
-rw-r--r--usr.sbin/xntpd/include/ntp_refclock.h224
-rw-r--r--usr.sbin/xntpd/include/ntp_request.h808
-rw-r--r--usr.sbin/xntpd/include/ntp_select.h20
-rw-r--r--usr.sbin/xntpd/include/ntp_stdlib.h93
-rw-r--r--usr.sbin/xntpd/include/ntp_string.h35
-rw-r--r--usr.sbin/xntpd/include/ntp_syslog.h15
-rw-r--r--usr.sbin/xntpd/include/ntp_timex.h273
-rw-r--r--usr.sbin/xntpd/include/ntp_types.h60
-rw-r--r--usr.sbin/xntpd/include/ntp_unixtime.h119
-rw-r--r--usr.sbin/xntpd/include/ntpd.h175
-rw-r--r--usr.sbin/xntpd/include/parse.h459
-rw-r--r--usr.sbin/xntpd/include/parse_conf.h54
-rw-r--r--usr.sbin/xntpd/include/sys/bsd_audioirig.h101
-rw-r--r--usr.sbin/xntpd/include/sys/chudefs.h22
-rw-r--r--usr.sbin/xntpd/include/sys/clkdefs.h38
-rw-r--r--usr.sbin/xntpd/include/sys/parsestreams.h66
-rw-r--r--usr.sbin/xntpd/include/sys/ppsclock.h58
-rw-r--r--usr.sbin/xntpd/include/sys/timex.h290
-rw-r--r--usr.sbin/xntpd/include/sys/tpro.h34
-rw-r--r--usr.sbin/xntpd/kernel/README.kern596
-rw-r--r--usr.sbin/xntpd/kernel/chuinit.c76
-rw-r--r--usr.sbin/xntpd/kernel/clkinit.c76
-rw-r--r--usr.sbin/xntpd/lib/Makefile35
-rw-r--r--usr.sbin/xntpd/lib/README5
-rw-r--r--usr.sbin/xntpd/lib/a_md512crypt.c86
-rw-r--r--usr.sbin/xntpd/lib/a_md5decrypt.c59
-rw-r--r--usr.sbin/xntpd/lib/a_md5encrypt.c69
-rw-r--r--usr.sbin/xntpd/lib/adjtimex.c15
-rw-r--r--usr.sbin/xntpd/lib/atoint.c48
-rw-r--r--usr.sbin/xntpd/lib/atolfp.c117
-rw-r--r--usr.sbin/xntpd/lib/atouint.c33
-rw-r--r--usr.sbin/xntpd/lib/auth12crypt.c125
-rw-r--r--usr.sbin/xntpd/lib/authdecrypt.c82
-rw-r--r--usr.sbin/xntpd/lib/authdes.c.export41
-rw-r--r--usr.sbin/xntpd/lib/authencrypt.c88
-rw-r--r--usr.sbin/xntpd/lib/authkeys.c601
-rw-r--r--usr.sbin/xntpd/lib/authparity.c58
-rw-r--r--usr.sbin/xntpd/lib/authreadkeys.c191
-rw-r--r--usr.sbin/xntpd/lib/authusekey.c132
-rw-r--r--usr.sbin/xntpd/lib/buftvtots.c61
-rw-r--r--usr.sbin/xntpd/lib/caljulian.c105
-rw-r--r--usr.sbin/xntpd/lib/calleapwhen.c61
-rw-r--r--usr.sbin/xntpd/lib/caltontp.c90
-rw-r--r--usr.sbin/xntpd/lib/calyearstart.c62
-rw-r--r--usr.sbin/xntpd/lib/clocktime.c131
-rw-r--r--usr.sbin/xntpd/lib/clocktypes.c72
-rw-r--r--usr.sbin/xntpd/lib/decodenetnum.c58
-rw-r--r--usr.sbin/xntpd/lib/dofptoa.c117
-rw-r--r--usr.sbin/xntpd/lib/dolfptoa.c161
-rw-r--r--usr.sbin/xntpd/lib/emalloc.c20
-rwxr-xr-xusr.sbin/xntpd/lib/findconfig.c62
-rw-r--r--usr.sbin/xntpd/lib/fptoa.c24
-rw-r--r--usr.sbin/xntpd/lib/fptoms.c23
-rw-r--r--usr.sbin/xntpd/lib/getopt.c105
-rw-r--r--usr.sbin/xntpd/lib/gettstamp.c29
-rw-r--r--usr.sbin/xntpd/lib/hextoint.c38
-rw-r--r--usr.sbin/xntpd/lib/hextolfp.c66
-rw-r--r--usr.sbin/xntpd/lib/humandate.c61
-rw-r--r--usr.sbin/xntpd/lib/inttoa.c19
-rw-r--r--usr.sbin/xntpd/lib/lib_strbuf.c21
-rw-r--r--usr.sbin/xntpd/lib/lib_strbuf.h22
-rw-r--r--usr.sbin/xntpd/lib/machines.c61
-rw-r--r--usr.sbin/xntpd/lib/md5.c322
-rw-r--r--usr.sbin/xntpd/lib/mfptoa.c22
-rw-r--r--usr.sbin/xntpd/lib/mfptoms.c22
-rw-r--r--usr.sbin/xntpd/lib/modetoa.c33
-rw-r--r--usr.sbin/xntpd/lib/mstolfp.c99
-rw-r--r--usr.sbin/xntpd/lib/msutotsf.c35
-rw-r--r--usr.sbin/xntpd/lib/msyslog.c111
-rw-r--r--usr.sbin/xntpd/lib/netof.c24
-rw-r--r--usr.sbin/xntpd/lib/numtoa.c22
-rw-r--r--usr.sbin/xntpd/lib/numtohost.c38
-rw-r--r--usr.sbin/xntpd/lib/octtoint.c34
-rw-r--r--usr.sbin/xntpd/lib/prettydate.c44
-rw-r--r--usr.sbin/xntpd/lib/ranny.c81
-rw-r--r--usr.sbin/xntpd/lib/refnumtoa.c30
-rw-r--r--usr.sbin/xntpd/lib/syssignal.c45
-rw-r--r--usr.sbin/xntpd/lib/systime.c376
-rw-r--r--usr.sbin/xntpd/lib/tsftomsu.c37
-rw-r--r--usr.sbin/xntpd/lib/tstotod.c21
-rw-r--r--usr.sbin/xntpd/lib/tstotv.c135
-rw-r--r--usr.sbin/xntpd/lib/tvtoa.c33
-rw-r--r--usr.sbin/xntpd/lib/tvtots.c159
-rw-r--r--usr.sbin/xntpd/lib/uglydate.c49
-rw-r--r--usr.sbin/xntpd/lib/uinttoa.c19
-rw-r--r--usr.sbin/xntpd/lib/utvtoa.c21
-rw-r--r--usr.sbin/xntpd/ntpdate/Makefile28
-rw-r--r--usr.sbin/xntpd/ntpdate/README7
-rw-r--r--usr.sbin/xntpd/ntpdate/ntpdate.c1586
-rw-r--r--usr.sbin/xntpd/ntpdate/ntpdate.h89
-rw-r--r--usr.sbin/xntpd/ntpq/Makefile29
-rw-r--r--usr.sbin/xntpd/ntpq/README6
-rw-r--r--usr.sbin/xntpd/ntpq/ntpq.c3091
-rw-r--r--usr.sbin/xntpd/ntpq/ntpq.h97
-rw-r--r--usr.sbin/xntpd/ntpq/ntpq_ops.c1610
-rw-r--r--usr.sbin/xntpd/ntptrace/Makefile28
-rw-r--r--usr.sbin/xntpd/ntptrace/README7
-rw-r--r--usr.sbin/xntpd/ntptrace/ntptrace.c772
-rw-r--r--usr.sbin/xntpd/ntptrace/ntptrace.h36
-rw-r--r--usr.sbin/xntpd/parse/Makefile19
-rw-r--r--usr.sbin/xntpd/parse/README100
-rw-r--r--usr.sbin/xntpd/parse/README.new_clocks212
-rw-r--r--usr.sbin/xntpd/parse/README.parse142
-rw-r--r--usr.sbin/xntpd/parse/README.parse_clocks264
-rw-r--r--usr.sbin/xntpd/parse/clk_dcf7000.c150
-rw-r--r--usr.sbin/xntpd/parse/clk_meinberg.c473
-rw-r--r--usr.sbin/xntpd/parse/clk_rawdcf.c580
-rw-r--r--usr.sbin/xntpd/parse/clk_schmid.c217
-rw-r--r--usr.sbin/xntpd/parse/clk_trimble.c140
-rw-r--r--usr.sbin/xntpd/parse/clk_trimtaip.c140
-rw-r--r--usr.sbin/xntpd/parse/clk_trimtsip.c474
-rw-r--r--usr.sbin/xntpd/parse/empty.c7
-rw-r--r--usr.sbin/xntpd/parse/parse.c1309
-rw-r--r--usr.sbin/xntpd/parse/parse_conf.c133
-rw-r--r--usr.sbin/xntpd/parse/parsesolaris.c1249
-rw-r--r--usr.sbin/xntpd/parse/parsestreams.c1363
-rw-r--r--usr.sbin/xntpd/parse/util/Makefile24
-rw-r--r--usr.sbin/xntpd/parse/util/Makefile.tmpl49
-rw-r--r--usr.sbin/xntpd/parse/util/README19
-rw-r--r--usr.sbin/xntpd/parse/util/dcfd.c1641
-rw-r--r--usr.sbin/xntpd/parse/util/parsetest.c275
-rw-r--r--usr.sbin/xntpd/parse/util/testdcf.c485
-rw-r--r--usr.sbin/xntpd/refclocks/Dependencies30
-rw-r--r--usr.sbin/xntpd/refclocks/README4
-rwxr-xr-xusr.sbin/xntpd/refclocks/check2
-rwxr-xr-xusr.sbin/xntpd/refclocks/echon2
-rwxr-xr-xusr.sbin/xntpd/refclocks/query11
-rw-r--r--usr.sbin/xntpd/refclocks/rclk.AS220129
-rw-r--r--usr.sbin/xntpd/refclocks/rclk.CHU24
-rw-r--r--usr.sbin/xntpd/refclocks/rclk.DATUM22
-rw-r--r--usr.sbin/xntpd/refclocks/rclk.GOES32
-rw-r--r--usr.sbin/xntpd/refclocks/rclk.GPSTM33
-rw-r--r--usr.sbin/xntpd/refclocks/rclk.IRIG24
-rw-r--r--usr.sbin/xntpd/refclocks/rclk.LEITCH29
-rw-r--r--usr.sbin/xntpd/refclocks/rclk.LOCAL_CLOCK22
-rw-r--r--usr.sbin/xntpd/refclocks/rclk.MOTO29
-rw-r--r--usr.sbin/xntpd/refclocks/rclk.MSFEES26
-rw-r--r--usr.sbin/xntpd/refclocks/rclk.MX420027
-rw-r--r--usr.sbin/xntpd/refclocks/rclk.NMEA23
-rw-r--r--usr.sbin/xntpd/refclocks/rclk.OMEGA29
-rw-r--r--usr.sbin/xntpd/refclocks/rclk.PARSE55
-rw-r--r--usr.sbin/xntpd/refclocks/rclk.PST37
-rw-r--r--usr.sbin/xntpd/refclocks/rclk.TPRO24
-rw-r--r--usr.sbin/xntpd/refclocks/rclk.TRAK29
-rw-r--r--usr.sbin/xntpd/refclocks/rclk.WWVB38
-rw-r--r--usr.sbin/xntpd/refclocks/rconfig130
-rw-r--r--usr.sbin/xntpd/refclocks/setup16
-rw-r--r--usr.sbin/xntpd/refclocks/setupfn27
-rwxr-xr-xusr.sbin/xntpd/scripts/Guess.sh130
-rw-r--r--usr.sbin/xntpd/scripts/README41
-rwxr-xr-xusr.sbin/xntpd/scripts/autoconf885
-rwxr-xr-xusr.sbin/xntpd/scripts/install.sh100
-rwxr-xr-xusr.sbin/xntpd/scripts/makeconfig.sh85
-rwxr-xr-xusr.sbin/xntpd/scripts/mklinks9
-rwxr-xr-xusr.sbin/xntpd/scripts/mkversion36
-rw-r--r--usr.sbin/xntpd/scripts/monitoring/README154
-rw-r--r--usr.sbin/xntpd/scripts/monitoring/loopwatch.config.SAMPLE89
-rwxr-xr-xusr.sbin/xntpd/scripts/monitoring/lr.pl145
-rwxr-xr-xusr.sbin/xntpd/scripts/monitoring/ntp.pl477
-rwxr-xr-xusr.sbin/xntpd/scripts/monitoring/ntploopstat457
-rwxr-xr-xusr.sbin/xntpd/scripts/monitoring/ntploopwatch1631
-rwxr-xr-xusr.sbin/xntpd/scripts/monitoring/ntptrap453
-rwxr-xr-xusr.sbin/xntpd/scripts/monitoring/timelocal.pl78
-rwxr-xr-xusr.sbin/xntpd/scripts/ntp-groper95
-rwxr-xr-xusr.sbin/xntpd/scripts/ntp-restart9
-rw-r--r--usr.sbin/xntpd/scripts/stats/README39
-rw-r--r--usr.sbin/xntpd/scripts/stats/README.stats246
-rw-r--r--usr.sbin/xntpd/scripts/stats/README.timecodes149
-rw-r--r--usr.sbin/xntpd/scripts/stats/clock.awk341
-rwxr-xr-xusr.sbin/xntpd/scripts/stats/clock.sh17
-rw-r--r--usr.sbin/xntpd/scripts/stats/dupe.awk8
-rw-r--r--usr.sbin/xntpd/scripts/stats/ensemble.S5
-rw-r--r--usr.sbin/xntpd/scripts/stats/ensemble.awk17
-rw-r--r--usr.sbin/xntpd/scripts/stats/etf.S15
-rw-r--r--usr.sbin/xntpd/scripts/stats/etf.awk19
-rw-r--r--usr.sbin/xntpd/scripts/stats/itf.S5
-rw-r--r--usr.sbin/xntpd/scripts/stats/itf.awk19
-rw-r--r--usr.sbin/xntpd/scripts/stats/loop.S7
-rw-r--r--usr.sbin/xntpd/scripts/stats/loop.awk41
-rwxr-xr-xusr.sbin/xntpd/scripts/stats/loop.sh13
-rw-r--r--usr.sbin/xntpd/scripts/stats/peer.awk57
-rwxr-xr-xusr.sbin/xntpd/scripts/stats/peer.sh13
-rw-r--r--usr.sbin/xntpd/scripts/stats/psummary.awk43
-rw-r--r--usr.sbin/xntpd/scripts/stats/rms.awk41
-rwxr-xr-xusr.sbin/xntpd/scripts/stats/summary.sh88
-rw-r--r--usr.sbin/xntpd/scripts/stats/tdata.S5
-rw-r--r--usr.sbin/xntpd/scripts/stats/tdata.awk45
-rw-r--r--usr.sbin/xntpd/scripts/support/README73
-rwxr-xr-xusr.sbin/xntpd/scripts/support/bin/monl213
-rwxr-xr-xusr.sbin/xntpd/scripts/support/bin/mvstats23
-rw-r--r--usr.sbin/xntpd/scripts/support/conf/hp300.hp30070
-rw-r--r--usr.sbin/xntpd/scripts/support/conf/hp700.hp70067
-rw-r--r--usr.sbin/xntpd/scripts/support/conf/hp700.hp700.faui4771
-rw-r--r--usr.sbin/xntpd/scripts/support/conf/hp800.hp80070
-rw-r--r--usr.sbin/xntpd/scripts/support/conf/ntp.conf36
-rw-r--r--usr.sbin/xntpd/scripts/support/conf/ntp.keys0
-rw-r--r--usr.sbin/xntpd/scripts/support/conf/ntp.keys.dumb0
-rw-r--r--usr.sbin/xntpd/scripts/support/conf/sun3.sun336
-rw-r--r--usr.sbin/xntpd/scripts/support/conf/sun4.sun4.faui0183
-rw-r--r--usr.sbin/xntpd/scripts/support/conf/sun4.sun4.faui10176
-rw-r--r--usr.sbin/xntpd/scripts/support/conf/sun4.sun4.faui45228
-rw-r--r--usr.sbin/xntpd/scripts/support/conf/sun4.sun4c63
-rw-r--r--usr.sbin/xntpd/scripts/support/conf/sun4.sun4c.Lucifer174
-rw-r--r--usr.sbin/xntpd/scripts/support/conf/sun4.sun4m69
-rw-r--r--usr.sbin/xntpd/scripts/support/conf/sun4.sun4m.faui42152
-rw-r--r--usr.sbin/xntpd/scripts/support/conf/sun4.sun4m.faui45m165
-rw-r--r--usr.sbin/xntpd/scripts/support/conf/tickconf19
-rwxr-xr-xusr.sbin/xntpd/scripts/support/etc/cron18
-rw-r--r--usr.sbin/xntpd/scripts/support/etc/crontab8
-rwxr-xr-xusr.sbin/xntpd/scripts/support/etc/install67
-rwxr-xr-xusr.sbin/xntpd/scripts/support/etc/rc198
-rwxr-xr-xusr.sbin/xntpd/scripts/support/etc/setup72
-rw-r--r--usr.sbin/xntpd/util/Makefile28
-rw-r--r--usr.sbin/xntpd/util/README67
-rw-r--r--usr.sbin/xntpd/util/byteorder.c52
-rw-r--r--usr.sbin/xntpd/util/jitter.c73
-rw-r--r--usr.sbin/xntpd/util/kern.c210
-rw-r--r--usr.sbin/xntpd/util/longsize.c11
-rw-r--r--usr.sbin/xntpd/util/ntptime.c236
-rw-r--r--usr.sbin/xntpd/util/precision.c150
-rw-r--r--usr.sbin/xntpd/util/testrs6000.c44
-rw-r--r--usr.sbin/xntpd/util/tickadj.c584
-rw-r--r--usr.sbin/xntpd/util/timetrim.c85
-rw-r--r--usr.sbin/xntpd/xntpd/Makefile48
-rw-r--r--usr.sbin/xntpd/xntpd/README6
-rw-r--r--usr.sbin/xntpd/xntpd/minpoll0
-rw-r--r--usr.sbin/xntpd/xntpd/ntp_config.c1717
-rw-r--r--usr.sbin/xntpd/xntpd/ntp_control.c2690
-rw-r--r--usr.sbin/xntpd/xntpd/ntp_filegen.c538
-rw-r--r--usr.sbin/xntpd/xntpd/ntp_intres.c799
-rw-r--r--usr.sbin/xntpd/xntpd/ntp_io.c1809
-rw-r--r--usr.sbin/xntpd/xntpd/ntp_leap.c315
-rw-r--r--usr.sbin/xntpd/xntpd/ntp_loopfilter.c721
-rw-r--r--usr.sbin/xntpd/xntpd/ntp_monitor.c349
-rw-r--r--usr.sbin/xntpd/xntpd/ntp_peer.c667
-rw-r--r--usr.sbin/xntpd/xntpd/ntp_proto.c2268
-rw-r--r--usr.sbin/xntpd/xntpd/ntp_refclock.c1286
-rw-r--r--usr.sbin/xntpd/xntpd/ntp_request.c2453
-rw-r--r--usr.sbin/xntpd/xntpd/ntp_restrict.c459
-rw-r--r--usr.sbin/xntpd/xntpd/ntp_timer.c187
-rw-r--r--usr.sbin/xntpd/xntpd/ntp_unixclock.c606
-rw-r--r--usr.sbin/xntpd/xntpd/ntp_util.c441
-rw-r--r--usr.sbin/xntpd/xntpd/ntpd.c461
-rw-r--r--usr.sbin/xntpd/xntpd/refclock_acts.c895
-rw-r--r--usr.sbin/xntpd/xntpd/refclock_as2201.c453
-rw-r--r--usr.sbin/xntpd/xntpd/refclock_atom.c499
-rw-r--r--usr.sbin/xntpd/xntpd/refclock_chu.c800
-rw-r--r--usr.sbin/xntpd/xntpd/refclock_conf.c185
-rw-r--r--usr.sbin/xntpd/xntpd/refclock_datum.c871
-rw-r--r--usr.sbin/xntpd/xntpd/refclock_goes.c533
-rw-r--r--usr.sbin/xntpd/xntpd/refclock_gpstm.c999
-rw-r--r--usr.sbin/xntpd/xntpd/refclock_heath.c393
-rw-r--r--usr.sbin/xntpd/xntpd/refclock_irig.c259
-rw-r--r--usr.sbin/xntpd/xntpd/refclock_leitch.c710
-rw-r--r--usr.sbin/xntpd/xntpd/refclock_local.c170
-rw-r--r--usr.sbin/xntpd/xntpd/refclock_moto.c2
-rw-r--r--usr.sbin/xntpd/xntpd/refclock_msfees.c1557
-rw-r--r--usr.sbin/xntpd/xntpd/refclock_mx4200.c1343
-rw-r--r--usr.sbin/xntpd/xntpd/refclock_nmea.c391
-rw-r--r--usr.sbin/xntpd/xntpd/refclock_omega.c999
-rw-r--r--usr.sbin/xntpd/xntpd/refclock_parse.c3925
-rw-r--r--usr.sbin/xntpd/xntpd/refclock_pst.c329
-rw-r--r--usr.sbin/xntpd/xntpd/refclock_tpro.c227
-rw-r--r--usr.sbin/xntpd/xntpd/refclock_trak.c339
-rw-r--r--usr.sbin/xntpd/xntpd/refclock_wwvb.c420
-rw-r--r--usr.sbin/xntpd/xntpdc/Makefile28
-rw-r--r--usr.sbin/xntpd/xntpdc/README6
-rw-r--r--usr.sbin/xntpd/xntpdc/ntpdc.c1532
-rw-r--r--usr.sbin/xntpd/xntpdc/ntpdc.h59
-rw-r--r--usr.sbin/xntpd/xntpdc/ntpdc_ops.c2441
-rw-r--r--usr.sbin/xten/Makefile7
-rw-r--r--usr.sbin/xten/README367
-rw-r--r--usr.sbin/xten/xten.1113
-rw-r--r--usr.sbin/xten/xten.c178
-rw-r--r--usr.sbin/yp_mkdb/Makefile13
-rw-r--r--usr.sbin/yp_mkdb/yp_mkdb.8171
-rw-r--r--usr.sbin/yp_mkdb/yp_mkdb.c338
-rw-r--r--usr.sbin/ypbind/Makefile9
-rw-r--r--usr.sbin/ypbind/yp_ping.c545
-rw-r--r--usr.sbin/ypbind/yp_ping.h1
-rw-r--r--usr.sbin/ypbind/ypbind.8181
-rw-r--r--usr.sbin/ypbind/ypbind.c1008
-rw-r--r--usr.sbin/yppoll/Makefile7
-rw-r--r--usr.sbin/yppoll/yppoll.882
-rw-r--r--usr.sbin/yppoll/yppoll.c97
-rw-r--r--usr.sbin/yppush/Makefile28
-rw-r--r--usr.sbin/yppush/yppush.8169
-rw-r--r--usr.sbin/yppush/yppush_extern.h44
-rw-r--r--usr.sbin/yppush/yppush_main.c688
-rw-r--r--usr.sbin/ypserv/Makefile39
-rw-r--r--usr.sbin/ypserv/Makefile.yp524
-rw-r--r--usr.sbin/ypserv/yp_access.c324
-rw-r--r--usr.sbin/ypserv/yp_dblookup.c750
-rw-r--r--usr.sbin/ypserv/yp_dnslookup.c541
-rw-r--r--usr.sbin/ypserv/yp_error.c90
-rw-r--r--usr.sbin/ypserv/yp_extern.h113
-rw-r--r--usr.sbin/ypserv/yp_main.c331
-rw-r--r--usr.sbin/ypserv/yp_server.c951
-rw-r--r--usr.sbin/ypserv/yp_svc_udp.c89
-rw-r--r--usr.sbin/ypserv/ypserv.8420
-rw-r--r--usr.sbin/ypset/Makefile7
-rw-r--r--usr.sbin/ypset/ypset.885
-rw-r--r--usr.sbin/ypset/ypset.c156
-rw-r--r--usr.sbin/zic/Makefile5
-rw-r--r--usr.sbin/zic/Makefile.inc3
-rw-r--r--usr.sbin/zic/Music81
-rw-r--r--usr.sbin/zic/README66
-rw-r--r--usr.sbin/zic/Theory120
-rw-r--r--usr.sbin/zic/WWW71
-rw-r--r--usr.sbin/zic/ialloc.c93
-rw-r--r--usr.sbin/zic/scheck.c67
-rw-r--r--usr.sbin/zic/zdump.839
-rw-r--r--usr.sbin/zic/zdump.c367
-rw-r--r--usr.sbin/zic/zdump/Makefile13
-rw-r--r--usr.sbin/zic/zic.8383
-rw-r--r--usr.sbin/zic/zic.c2105
-rw-r--r--usr.sbin/zic/zic/Makefile14
1734 files changed, 392633 insertions, 17409 deletions
diff --git a/usr.sbin/IPXrouted/IPXrouted.8 b/usr.sbin/IPXrouted/IPXrouted.8
new file mode 100644
index 0000000..79779d5
--- /dev/null
+++ b/usr.sbin/IPXrouted/IPXrouted.8
@@ -0,0 +1,189 @@
+.\" Copyright (c) 1986, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Copyright (c) 1995 John Hay. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.Dd Oct 11, 1995
+.Dt IPXROUTED 8
+.Os FreeBSD
+.Sh NAME
+.Nm IPXrouted
+.Nd IPX Routing Information Protocol daemon
+.Sh SYNOPSIS
+.Nm IPXrouted
+.Op Fl q
+.Op Fl s
+.Op Fl S
+.Op Fl t
+.Op Ar logfile
+.Sh DESCRIPTION
+.Nm IPXrouted
+is invoked at boot time to manage the IPX routing tables.
+The IPX routing daemon uses the Novell IPX Routing
+Information Protocol in maintaining up to date kernel routing
+table entries.
+.Pp
+Available options:
+.Bl -tag -width logfile
+.It Fl q
+Do not supply routing information (opposite of
+.Fl s
+option below).
+.It Fl s
+Forces
+.Nm IPXrouted
+to supply routing information whether it is acting as an internetwork
+router or not.
+.It Fl S
+Do not supply Service Advertizing Protocol
+.Nm (SAP)
+information. The default is to supply
+.Nm SAP
+information.
+.It Fl t
+All packets sent or received are
+printed on the standard output. In addition,
+.Nm IPXrouted
+will not divorce itself from the controlling terminal
+so that interrupts from the keyboard will kill the process.
+.It Ar logfile
+Name of file in which
+.Nm IPXrouted Ns 's
+actions should be logged. This log contains information
+about any changes to the routing tables and a history of
+recent messages sent and received which are related to
+the changed route.
+.El
+.Pp
+In normal operation
+.Nm IPXrouted
+listens
+for routing information packets. If the host is connected to
+multiple IPX networks, it periodically supplies copies
+of its routing tables to any directly connected hosts
+and networks.
+.Pp
+When
+.Nm IPXrouted
+is started, it uses the
+.Dv SIOCGIFCONF
+.Xr ioctl 2
+to find those
+directly connected interfaces configured into the
+system and marked
+.Dq up
+(the software loopback interface
+is ignored). If multiple interfaces
+are present, it is assumed the host will forward packets
+between networks.
+.Nm IPXrouted
+then transmits a
+.Em request
+packet on each interface (using a broadcast packet if
+the interface supports it) and enters a loop, listening
+for
+.Em request
+and
+.Em response
+packets from other hosts.
+.Pp
+When a
+.Em request
+packet is received,
+.Nm IPXrouted
+formulates a reply based on the information maintained in its
+internal tables. The
+.Em response
+packet generated contains a list of known routes, each marked
+with a
+.Dq hop count
+metric (a count of 16, or greater, is
+considered
+.Dq infinite ) .
+The metric associated with each
+route returned provides a metric
+.Em relative to the sender .
+.Pp
+.Em Response
+packets received by
+.Nm IPXrouted
+are used to update the routing tables if one of the following
+conditions is satisfied:
+.Bl -bullet
+.It
+No routing table entry exists for the destination network
+or host, and the metric indicates the destination is ``reachable''
+(i.e. the hop count is not infinite).
+.It
+The source host of the packet is the same as the router in the
+existing routing table entry. That is, updated information is
+being received from the very internetwork router through which
+packets for the destination are being routed.
+.It
+The existing entry in the routing table has not been updated for
+some time (defined to be 90 seconds) and the route is at least
+as cost effective as the current route.
+.It
+The new route describes a shorter route to the destination than
+the one currently stored in the routing tables; the metric of
+the new route is compared against the one stored in the table
+to decide this.
+.El
+.Pp
+When an update is applied,
+.Nm IPXrouted
+records the change in its internal tables and generates a
+.Em response
+packet to all directly connected hosts and networks.
+.Xr Routed 8
+waits a short period
+of time (no more than 30 seconds) before modifying the kernel's
+routing tables to allow possible unstable situations to settle.
+.Pp
+In addition to processing incoming packets,
+.Nm IPXrouted
+also periodically checks the routing table entries.
+If an entry has not been updated for 3 minutes, the entry's metric
+is set to infinity and marked for deletion. Deletions are delayed
+an additional 60 seconds to insure the invalidation is propagated
+to other routers.
+.Pp
+Hosts acting as internetwork routers gratuitously supply their
+routing tables every 30 seconds to all directly connected hosts
+and networks.
+.Pp
+If
+.Nm IPXrouted
+receives a SIGINFO signal the current contents of the RIP and SAP
+tables are appended to the file /tmp/ipxrouted.dmp.
+.Sh SEE ALSO
+.Xr ipx 3
+.Sh HISTORY
diff --git a/usr.sbin/IPXrouted/Makefile b/usr.sbin/IPXrouted/Makefile
new file mode 100644
index 0000000..5c8be5e
--- /dev/null
+++ b/usr.sbin/IPXrouted/Makefile
@@ -0,0 +1,11 @@
+# @(#)Makefile 8.1 (Berkeley) 6/5/93
+# $Id$
+
+PROG= IPXrouted
+MAN8= IPXrouted.8
+SRCS= af.c if.c input.c main.c output.c startup.c tables.c timer.c trace.c
+SRCS+= sap_input.c sap_tables.c sap_output.c
+DPADD= ${LIBCOMPAT} ${LIBIPX}
+LDADD= -lcompat -lipx
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/IPXrouted/af.c b/usr.sbin/IPXrouted/af.c
new file mode 100644
index 0000000..05034ab
--- /dev/null
+++ b/usr.sbin/IPXrouted/af.c
@@ -0,0 +1,298 @@
+/*
+ * Copyright (c) 1985, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Copyright (c) 1995 John Hay. All rights reserved.
+ *
+ * This file includes significant work done at Cornell University by
+ * Bill Nesheim. That work included by permission.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: af.c,v 1.4 1997/02/22 16:00:54 peter Exp $
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)af.c 8.1 (Berkeley) 6/5/93";
+#endif /* not lint */
+
+#include "defs.h"
+
+/*
+ * Address family support routines
+ */
+af_hash_t null_hash;
+af_netmatch_t null_netmatch;
+af_output_t null_output;
+af_portmatch_t null_portmatch;
+af_portcheck_t null_portcheck;
+af_checkhost_t null_checkhost;
+af_ishost_t null_ishost;
+af_canon_t null_canon;
+
+void ipxnet_hash(struct sockaddr_ipx *, struct afhash *);
+int ipxnet_netmatch(struct sockaddr_ipx *, struct sockaddr_ipx *);
+void ipxnet_output(int, int, struct sockaddr_ipx *, int);
+int ipxnet_portmatch(struct sockaddr_ipx *);
+int ipxnet_checkhost(struct sockaddr_ipx *);
+int ipxnet_ishost(struct sockaddr_ipx *);
+void ipxnet_canon(struct sockaddr_ipx *);
+
+#define NIL \
+ { null_hash, null_netmatch, null_output, \
+ null_portmatch, null_portcheck, null_checkhost, \
+ null_ishost, null_canon }
+#define IPXNET \
+ { (af_hash_t *)ipxnet_hash, \
+ (af_netmatch_t *)ipxnet_netmatch, \
+ (af_output_t *)ipxnet_output, \
+ (af_portmatch_t *)ipxnet_portmatch, \
+ (af_portcheck_t *)ipxnet_portmatch, \
+ (af_checkhost_t *)ipxnet_checkhost, \
+ (af_ishost_t *)ipxnet_ishost, \
+ (af_canon_t *)ipxnet_canon }
+
+struct afswitch afswitch[AF_MAX] =
+ { NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL,
+ NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL, NIL,
+ NIL, NIL, NIL, IPXNET, NIL, NIL };
+
+struct sockaddr_ipx ipxnet_default = { sizeof(struct sockaddr_ipx), AF_IPX };
+
+union ipx_net ipx_anynet;
+union ipx_net ipx_zeronet;
+
+void
+ipxnet_hash(sipx, hp)
+ register struct sockaddr_ipx *sipx;
+ struct afhash *hp;
+{
+ long hash;
+#if 0
+ u_short *s = sipx->sipx_addr.x_host.s_host;
+#endif
+ u_char *c;
+
+ c = sipx->sipx_addr.x_net.c_net;
+
+#define IMVAL 33
+ hash = 0;
+ hash = hash * IMVAL + *c++;
+ hash = hash * IMVAL + *c++;
+ hash = hash * IMVAL + *c++;
+ hash = hash * IMVAL + *c++;
+#undef IMVAL
+
+ hp->afh_nethash = hash;
+ hp->afh_nethash ^= (hash >> 8);
+ hp->afh_nethash ^= (hash >> 16);
+ hp->afh_nethash ^= (hash >> 24);
+
+#if 0
+ hash = 0;
+ hash = *s++; hash <<= 8; hash += *s++; hash <<= 8; hash += *s;
+ hp->afh_hosthash = hash;
+#endif
+}
+
+int
+ipxnet_netmatch(sxn1, sxn2)
+ struct sockaddr_ipx *sxn1, *sxn2;
+{
+ return (ipx_neteq(sxn1->sipx_addr, sxn2->sipx_addr));
+}
+
+/*
+ * Verify the message is from the right port.
+ */
+int
+ipxnet_portmatch(sipx)
+ register struct sockaddr_ipx *sipx;
+{
+
+ return (ntohs(sipx->sipx_addr.x_port) == IPXPORT_RIP );
+}
+
+
+/*
+ * ipx output routine.
+ */
+#ifdef DEBUG
+int do_output = 0;
+#endif
+void
+ipxnet_output(s, flags, sipx, size)
+ int s;
+ int flags;
+ struct sockaddr_ipx *sipx;
+ int size;
+{
+ struct sockaddr_ipx dst;
+
+ dst = *sipx;
+ sipx = &dst;
+ if (sipx->sipx_addr.x_port == 0)
+ sipx->sipx_addr.x_port = htons(IPXPORT_RIP);
+#ifdef DEBUG
+ if(do_output || ntohs(msg->rip_cmd) == RIPCMD_REQUEST)
+#endif
+ /*
+ * Kludge to allow us to get routes out to machines that
+ * don't know their addresses yet; send to that address on
+ * ALL connected nets
+ */
+ if (ipx_neteqnn(sipx->sipx_addr.x_net, ipx_zeronet)) {
+ extern struct interface *ifnet;
+ register struct interface *ifp;
+
+ for (ifp = ifnet; ifp; ifp = ifp->int_next) {
+ sipx->sipx_addr.x_net =
+ satoipx_addr(ifp->int_addr).x_net;
+ (void) sendto(s, msg, size, flags,
+ (struct sockaddr *)sipx, sizeof (*sipx));
+ }
+ return;
+ }
+
+ (void) sendto(s, msg, size, flags,
+ (struct sockaddr *)sipx, sizeof (*sipx));
+}
+
+/*
+ * Return 1 if we want this route.
+ * We use this to disallow route net G entries for one for multiple
+ * point to point links.
+ */
+int
+ipxnet_checkhost(sipx)
+ struct sockaddr_ipx *sipx;
+{
+ register struct interface *ifp = if_ifwithnet((struct sockaddr *)sipx);
+ /*
+ * We want this route if there is no more than one
+ * point to point interface with this network.
+ */
+ if (ifp == 0 || (ifp->int_flags & IFF_POINTOPOINT)==0) return (1);
+ return (ifp->int_sq.n == ifp->int_sq.p);
+}
+
+/*
+ * Return 1 if the address is
+ * for a host, 0 for a network.
+ */
+int
+ipxnet_ishost(sipx)
+struct sockaddr_ipx *sipx;
+{
+ register u_short *s = sipx->sipx_addr.x_host.s_host;
+
+ if ((s[0]==0x0000) && (s[1]==0x0000) && (s[2]==0x0000))
+ return (0);
+ if ((s[0]==0xffff) && (s[1]==0xffff) && (s[2]==0xffff))
+ return (0);
+
+ return (1);
+}
+
+void
+ipxnet_canon(sipx)
+ struct sockaddr_ipx *sipx;
+{
+
+ sipx->sipx_addr.x_port = 0;
+}
+
+void
+null_hash(addr, hp)
+ struct sockaddr *addr;
+ struct afhash *hp;
+{
+
+ hp->afh_nethash = hp->afh_hosthash = 0;
+}
+
+int
+null_netmatch(a1, a2)
+ struct sockaddr *a1, *a2;
+{
+
+ return (0);
+}
+
+void
+null_output(s, f, a1, n)
+ int s;
+ int f;
+ struct sockaddr *a1;
+ int n;
+{
+
+ ;
+}
+
+int
+null_portmatch(a1)
+ struct sockaddr *a1;
+{
+
+ return (0);
+}
+
+int
+null_portcheck(a1)
+ struct sockaddr *a1;
+{
+
+ return (0);
+}
+
+int
+null_ishost(a1)
+ struct sockaddr *a1;
+{
+
+ return (0);
+}
+
+int
+null_checkhost(a1)
+ struct sockaddr *a1;
+{
+
+ return (0);
+}
+
+void
+null_canon(a1)
+ struct sockaddr *a1;
+{
+
+ ;
+}
+
diff --git a/usr.sbin/IPXrouted/af.h b/usr.sbin/IPXrouted/af.h
new file mode 100644
index 0000000..cad2861
--- /dev/null
+++ b/usr.sbin/IPXrouted/af.h
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Copyright (c) 1995 John Hay. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)af.h 5.1 (Berkeley) 6/4/85 (routed/af.h)
+ *
+ * @(#)af.h 8.1 (Berkeley) 6/5/93
+ *
+ * $Id$
+ */
+
+/*
+ * Routing table management daemon.
+ */
+
+/*
+ * Structure returned by af_hash routines.
+ */
+struct afhash {
+ u_int afh_hosthash; /* host based hash */
+ u_int afh_nethash; /* network based hash */
+};
+
+/*
+ * Per address family routines.
+ */
+typedef void af_hash_t(struct sockaddr *, struct afhash *);
+typedef int af_netmatch_t(struct sockaddr *, struct sockaddr *);
+typedef void af_output_t(int, int, struct sockaddr *, int);
+typedef int af_portmatch_t(struct sockaddr *);
+typedef int af_portcheck_t(struct sockaddr *);
+typedef int af_checkhost_t(struct sockaddr *);
+typedef int af_ishost_t(struct sockaddr *);
+typedef void af_canon_t(struct sockaddr *);
+
+struct afswitch {
+ af_hash_t *af_hash; /* returns keys based on address */
+ af_netmatch_t *af_netmatch; /* verifies net # matching */
+ af_output_t *af_output; /* interprets address for sending */
+ af_portmatch_t *af_portmatch; /* packet from some other router? */
+ af_portcheck_t *af_portcheck; /* packet from privileged peer? */
+ af_checkhost_t *af_checkhost; /* tells if address for host or net */
+ af_ishost_t *af_ishost; /* tells if address is valid */
+ af_canon_t *af_canon; /* canonicalize address for compares */
+};
+
+struct afswitch afswitch[AF_MAX]; /* table proper */
diff --git a/usr.sbin/IPXrouted/defs.h b/usr.sbin/IPXrouted/defs.h
new file mode 100644
index 0000000..55e08b3
--- /dev/null
+++ b/usr.sbin/IPXrouted/defs.h
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Copyright (c) 1995 John Hay. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)defs.h 8.1 (Berkeley) 6/5/93
+ *
+ * $Id: defs.h,v 1.5 1997/02/22 16:00:55 peter Exp $
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <net/route.h>
+#include <netipx/ipx.h>
+#if defined(vax) || defined(pdp11)
+#define xnnet(x) ((u_long) (x)->rip_dst[1] << 16 | (u_long) (x)->rip_dst[0] )
+#else
+#define xnnet(x) ((u_long) (x)->rip_dst[0] << 16 | (u_long) (x)->rip_dst[1] )
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <syslog.h>
+
+#include "protocol.h"
+#include "sap.h"
+#include "table.h"
+#include "trace.h"
+#include "interface.h"
+#include "af.h"
+
+
+/*
+ * When we find any interfaces marked down we rescan the
+ * kernel every CHECK_INTERVAL seconds to see if they've
+ * come up.
+ */
+#define CHECK_INTERVAL (5*60)
+
+#define equal(a1, a2) \
+ (bcmp((caddr_t)(a1), (caddr_t)(a2), sizeof (struct sockaddr)) == 0)
+#define min(a,b) ((a)>(b)?(b):(a))
+#define max(a,b) ((a)<(b)?(b):(a))
+
+extern int ripsock; /* Socket to listen on */
+extern int sapsock; /* Socket to listen on */
+extern int kmem;
+extern int supplier; /* process should supply updates */
+extern int dosap; /* SAP is enabled */
+extern int install; /* if 1 call kernel */
+extern int lookforinterfaces; /* if 1 probe kernel for new up ifs */
+extern int performnlist; /* if 1 check if /kernel has changed */
+extern int externalinterfaces; /* # of remote and local interfaces */
+extern int timeval; /* local idea of time */
+extern int noteremoterequests; /* squawk on requests from non-local nets */
+extern int r; /* Routing socket to install updates with */
+extern int gateway;
+extern struct sockaddr_ipx ipx_netmask; /* Used in installing routes */
+
+extern char packet[MAXRXPACKETSIZE+1];
+extern struct rip *msg;
+
+extern char **argv0;
+
+#define ADD 1
+#define DELETE 2
+#define CHANGE 3
+
+void sndmsg(struct sockaddr *, int, struct interface *, int);
+void supply(struct sockaddr *, int, struct interface *, int);
+void addrouteforif(struct interface *);
+void ifinit(void);
+void toall(void (*f)(struct sockaddr *, int, struct interface *, int),
+ struct rt_entry *, int);
+void rip_input(struct sockaddr *, int);
+
diff --git a/usr.sbin/IPXrouted/if.c b/usr.sbin/IPXrouted/if.c
new file mode 100644
index 0000000..c2522a9
--- /dev/null
+++ b/usr.sbin/IPXrouted/if.c
@@ -0,0 +1,151 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Copyright (c) 1995 John Hay. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * static char sccsid[] = "@(#)if.c 5.1 (Berkeley) 6/4/85"; (routed/if.c)
+ *
+ * $Id: if.c,v 1.3 1997/02/22 16:00:55 peter Exp $
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)if.c 8.1 (Berkeley) 6/5/93";
+#endif /* not lint */
+
+/*
+ * Routing Table Management Daemon
+ */
+#include "defs.h"
+
+extern struct interface *ifnet;
+
+/*
+ * Find the interface with address addr.
+ */
+struct interface *
+if_ifwithaddr(addr)
+ struct sockaddr *addr;
+{
+ register struct interface *ifp;
+
+#define same(a1, a2) \
+ (bcmp((caddr_t)((a1)->sa_data), (caddr_t)((a2)->sa_data), 10) == 0)
+ for (ifp = ifnet; ifp; ifp = ifp->int_next) {
+ if (ifp->int_flags & IFF_REMOTE)
+ continue;
+ if (ifp->int_addr.sa_family != addr->sa_family)
+ continue;
+ if (same(&ifp->int_addr, addr))
+ break;
+ if ((ifp->int_flags & IFF_BROADCAST) &&
+ same(&ifp->int_broadaddr, addr))
+ break;
+ }
+ return (ifp);
+}
+
+/*
+ * Find the point-to-point interface with destination address addr.
+ */
+struct interface *
+if_ifwithdstaddr(addr)
+ struct sockaddr *addr;
+{
+ register struct interface *ifp;
+
+ for (ifp = ifnet; ifp; ifp = ifp->int_next) {
+ if ((ifp->int_flags & IFF_POINTOPOINT) == 0)
+ continue;
+ if (same(&ifp->int_dstaddr, addr))
+ break;
+ }
+ return (ifp);
+}
+
+/*
+ * Find the interface on the network
+ * of the specified address.
+ */
+struct interface *
+if_ifwithnet(addr)
+ register struct sockaddr *addr;
+{
+ register struct interface *ifp;
+ register int af = addr->sa_family;
+ register int (*netmatch)();
+
+ if (af >= AF_MAX)
+ return (0);
+ netmatch = afswitch[af].af_netmatch;
+ for (ifp = ifnet; ifp; ifp = ifp->int_next) {
+ if (ifp->int_flags & IFF_REMOTE)
+ continue;
+ if (af != ifp->int_addr.sa_family)
+ continue;
+ if ((*netmatch)(addr, &ifp->int_addr))
+ break;
+ }
+ return (ifp);
+}
+
+/*
+ * Find an interface from which the specified address
+ * should have come from. Used for figuring out which
+ * interface a packet came in on -- for tracing.
+ */
+struct interface *
+if_iflookup(addr)
+ struct sockaddr *addr;
+{
+ register struct interface *ifp, *maybe;
+ register int af = addr->sa_family;
+ register int (*netmatch)();
+
+ if (af >= AF_MAX)
+ return (0);
+ maybe = 0;
+ netmatch = afswitch[af].af_netmatch;
+ for (ifp = ifnet; ifp; ifp = ifp->int_next) {
+ if (ifp->int_addr.sa_family != af)
+ continue;
+ if (same(&ifp->int_addr, addr))
+ break;
+ if ((ifp->int_flags & IFF_BROADCAST) &&
+ same(&ifp->int_broadaddr, addr))
+ break;
+ if (maybe == 0 && (*netmatch)(addr, &ifp->int_addr))
+ maybe = ifp;
+ }
+ if (ifp == 0)
+ ifp = maybe;
+ return (ifp);
+}
diff --git a/usr.sbin/IPXrouted/input.c b/usr.sbin/IPXrouted/input.c
new file mode 100644
index 0000000..a9e2f39
--- /dev/null
+++ b/usr.sbin/IPXrouted/input.c
@@ -0,0 +1,304 @@
+/*
+ * Copyright (c) 1985, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Copyright (c) 1995 John Hay. All rights reserved.
+ *
+ * This file includes significant work done at Cornell University by
+ * Bill Nesheim. That work included by permission.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: input.c,v 1.5 1997/02/22 16:00:56 peter Exp $
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)input.c 8.1 (Berkeley) 6/5/93";
+#endif /* not lint */
+
+/*
+ * IPX Routing Table Management Daemon
+ */
+#include "defs.h"
+
+struct sockaddr *
+ipx_nettosa(net)
+union ipx_net net;
+{
+ static struct sockaddr_ipx sxn;
+
+ bzero(&sxn, sizeof (struct sockaddr_ipx));
+ sxn.sipx_family = AF_IPX;
+ sxn.sipx_len = sizeof (sxn);
+ sxn.sipx_addr.x_net = net;
+ return( (struct sockaddr *)&sxn);
+
+}
+
+/*
+ * Process a newly received packet.
+ */
+void
+rip_input(from, size)
+ struct sockaddr *from;
+ int size;
+{
+ int newsize;
+ int rtchanged = 0;
+ struct rt_entry *rt;
+ struct netinfo *n;
+ struct interface *ifp = 0;
+ struct afswitch *afp;
+ struct sockaddr_ipx *ipxp;
+
+ ifp = if_ifwithnet(from);
+ ipxp = (struct sockaddr_ipx *)from;
+ if (ifp == 0) {
+ if(ftrace) {
+ fprintf(ftrace, "Received bogus packet from %s\n",
+ ipxdp_ntoa(&ipxp->sipx_addr));
+ }
+ return;
+ }
+
+ TRACE_INPUT(ifp, from, size);
+ if (from->sa_family >= AF_MAX)
+ return;
+ afp = &afswitch[from->sa_family];
+
+ size -= sizeof (u_short) /* command */;
+ n = msg->rip_nets;
+
+ switch (ntohs(msg->rip_cmd)) {
+
+ case RIPCMD_REQUEST:
+ if (ipx_hosteq(satoipx_addr(ifp->int_addr), ipxp->sipx_addr))
+ return;
+ newsize = 0;
+ while (size > 0) {
+ if (size < sizeof (struct netinfo))
+ break;
+ size -= sizeof (struct netinfo);
+
+ /*
+ * A single entry with rip_dst == DSTNETS_ALL and
+ * metric ``infinity'' means ``all routes''.
+ *
+ * XXX According to the IPX RIP spec the metric
+ * and tick fields can be anything. So maybe we
+ * should not check the metric???
+ */
+ if (ipx_neteqnn(n->rip_dst, ipx_anynet) &&
+ ntohs(n->rip_metric) == HOPCNT_INFINITY &&
+ size == 0) {
+ supply(from, 0, ifp, 0);
+ return;
+ }
+ /*
+ * request for specific nets
+ */
+ rt = rtlookup(ipx_nettosa(n->rip_dst));
+ if (ftrace) {
+ fprintf(ftrace,
+ "specific request for %s",
+ ipxdp_nettoa(n->rip_dst));
+ fprintf(ftrace,
+ " yields route %x\n",
+ (u_int)rt);
+ }
+ /*
+ * XXX We break out on the first net that isn't
+ * found. The specs is a bit vague here. I'm not
+ * sure what we should do.
+ */
+ if (rt == 0)
+ return;
+ /* XXX
+ * According to the spec we should not include
+ * information about networks for which the number
+ * of hops is 16.
+ */
+ if (rt->rt_metric == (HOPCNT_INFINITY-1))
+ return;
+ n->rip_metric = htons( rt == 0 ? HOPCNT_INFINITY :
+ min(rt->rt_metric+1, HOPCNT_INFINITY));
+ n->rip_ticks = htons(rt->rt_ticks+1);
+
+ /*
+ * We use split horizon with a twist. If the requested
+ * net is the directly connected net we supply an
+ * answer. This is so that the host can learn about
+ * the routers on its net.
+ */
+ {
+ register struct rt_entry *trt = rt;
+
+ while (trt) {
+ if ((trt->rt_ifp == ifp) &&
+ !ipx_neteqnn(n->rip_dst,
+ satoipx_addr(ifp->int_addr).x_net))
+ return;
+ trt = trt->rt_clone;
+ }
+ n++;
+ newsize += sizeof (struct netinfo);
+ }
+ }
+ if (newsize > 0) {
+ msg->rip_cmd = htons(RIPCMD_RESPONSE);
+ newsize += sizeof (u_short);
+ /* should check for if with dstaddr(from) first */
+ (*afp->af_output)(ripsock, 0, from, newsize);
+ TRACE_OUTPUT(ifp, from, newsize);
+ if (ftrace) {
+ /* XXX This should not happen anymore. */
+ if(ifp == 0)
+ fprintf(ftrace, "--- ifp = 0\n");
+ else
+ fprintf(ftrace,
+ "request arrived on interface %s\n",
+ ifp->int_name);
+ }
+ }
+ return;
+
+ case RIPCMD_RESPONSE:
+ /* verify message came from a router */
+ if ((*afp->af_portmatch)(from) == 0)
+ return;
+ (*afp->af_canon)(from);
+ /* are we talking to ourselves? */
+ if ((ifp = if_ifwithaddr(from)) != 0) {
+ rt = rtfind(from);
+ if (rt == 0 || (rt->rt_state & RTS_INTERFACE) == 0) {
+ addrouteforif(ifp);
+ rtchanged = 1;
+ } else
+ rt->rt_timer = 0;
+ return;
+ }
+ /* Update timer for interface on which the packet arrived.
+ * If from other end of a point-to-point link that isn't
+ * in the routing tables, (re-)add the route.
+ */
+ if ((rt = rtfind(from)) && (rt->rt_state & RTS_INTERFACE)) {
+ if(ftrace) fprintf(ftrace, "Got route\n");
+ rt->rt_timer = 0;
+ } else if ((ifp = if_ifwithdstaddr(from)) != 0) {
+ if(ftrace) fprintf(ftrace, "Got partner\n");
+ addrouteforif(ifp);
+ rtchanged = 1;
+ }
+ for (; size > 0; size -= sizeof (struct netinfo), n++) {
+ struct sockaddr *sa;
+ if (size < sizeof (struct netinfo))
+ break;
+ if ((unsigned) ntohs(n->rip_metric) > HOPCNT_INFINITY)
+ continue;
+ rt = rtfind(sa = ipx_nettosa(n->rip_dst));
+ if (rt == 0) {
+ if (ntohs(n->rip_metric) == HOPCNT_INFINITY)
+ continue;
+ rtadd(sa, from, ntohs(n->rip_metric),
+ ntohs(n->rip_ticks), 0);
+ rtchanged = 1;
+ continue;
+ }
+
+ /*
+ * A clone is a different route to the same net
+ * with exactly the same cost (ticks and metric).
+ * They must all be recorded because those interfaces
+ * must be handled in the same way as the first route
+ * to that net. ie When using the split horizon
+ * algorithm we must look at these interfaces also.
+ *
+ * Update if from gateway and different,
+ * from anywhere and less ticks or
+ * if same ticks and shorter,
+ * or getting stale and equivalent.
+ */
+ if (!equal(from, &rt->rt_router) &&
+ ntohs(n->rip_ticks) == rt->rt_ticks &&
+ ntohs(n->rip_metric) == rt->rt_metric &&
+ ntohs(n->rip_metric) != HOPCNT_INFINITY) {
+ register struct rt_entry *trt = rt->rt_clone;
+
+ while (trt) {
+ if (equal(from, &trt->rt_router)) {
+ trt->rt_timer = 0;
+ break;
+ }
+ trt = trt->rt_clone;
+ }
+ if (trt == NULL) {
+ rtadd_clone(rt, sa, from,
+ ntohs(n->rip_metric),
+ ntohs(n->rip_ticks), 0);
+ }
+ continue;
+ }
+ if ((equal(from, &rt->rt_router) &&
+ ((ntohs(n->rip_ticks) != rt->rt_ticks) ||
+ (ntohs(n->rip_metric) != rt->rt_metric))) ||
+ (ntohs(n->rip_ticks) < rt->rt_ticks) ||
+ ((ntohs(n->rip_ticks) == rt->rt_ticks) &&
+ (ntohs(n->rip_metric) < rt->rt_metric)) ||
+ (rt->rt_timer > (EXPIRE_TIME*2/3) &&
+ rt->rt_metric == ntohs(n->rip_metric) &&
+ ntohs(n->rip_metric) != HOPCNT_INFINITY)) {
+ rtchange(rt, from, ntohs(n->rip_metric),
+ ntohs(n->rip_ticks));
+ if (ntohs(n->rip_metric) == HOPCNT_INFINITY)
+ rt->rt_timer = EXPIRE_TIME;
+ else
+ rt->rt_timer = 0;
+ rtchanged = 1;
+ } else if (equal(from, &rt->rt_router) &&
+ (ntohs(n->rip_ticks) == rt->rt_ticks) &&
+ (ntohs(n->rip_metric) == rt->rt_metric) &&
+ (ntohs(n->rip_metric) != HOPCNT_INFINITY)) {
+ rt->rt_timer = 0;
+ }
+ }
+ if (rtchanged) {
+ register struct rthash *rh;
+ register struct rt_entry *rt;
+
+ toall(supply, NULL, 1);
+ for (rh = nethash; rh < &nethash[ROUTEHASHSIZ]; rh++)
+ for (rt = rh->rt_forw;
+ rt != (struct rt_entry *)rh;
+ rt = rt->rt_forw)
+ rt->rt_state &= ~RTS_CHANGED;
+ }
+
+ return;
+ }
+}
diff --git a/usr.sbin/IPXrouted/interface.h b/usr.sbin/IPXrouted/interface.h
new file mode 100644
index 0000000..ed12087
--- /dev/null
+++ b/usr.sbin/IPXrouted/interface.h
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Copyright (c) 1995 John Hay. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)interface.h 8.1 (Berkeley) 6/5/93
+ *
+ * $Id$
+ */
+
+/*
+ * Routing table management daemon.
+ */
+
+/*
+ * An ``interface'' is similar to an ifnet structure,
+ * except it doesn't contain q'ing info, and it also
+ * handles ``logical'' interfaces (remote gateways
+ * that we want to keep polling even if they go down).
+ * The list of interfaces which we maintain is used
+ * in supplying the gratuitous routing table updates.
+ * We list only one address for each interface, the AF_IPX one.
+ */
+struct interface {
+ struct interface *int_next;
+ struct sockaddr int_addr; /* address on this host */
+ union {
+ struct sockaddr intu_broadaddr;
+ struct sockaddr intu_dstaddr;
+ } int_intu;
+#define int_broadaddr int_intu.intu_broadaddr /* broadcast address */
+#define int_dstaddr int_intu.intu_dstaddr /* other end of p-to-p link */
+ int int_metric; /* init's routing entry */
+ int int_flags; /* see below */
+ struct ifdebug int_input, int_output; /* packet tracing stuff */
+ int int_ipackets; /* input packets received */
+ int int_opackets; /* output packets sent */
+ char *int_name; /* from kernel if structure */
+ u_short int_transitions; /* times gone up-down */
+
+ /* XXX IPX Specific entry */
+ struct sameq {
+ struct sameq *n; /* q of other pt-to-pt links */
+ struct sameq *p; /* with same net # */
+ } int_sq;
+};
+
+/*
+ * 0x1 to 0x10 are reused from the kernel's ifnet definitions,
+ * the others agree with the RTS_ flags defined elsewhere.
+ */
+#define IFF_UP 0x1 /* interface is up */
+#define IFF_BROADCAST 0x2 /* broadcast address valid */
+#define IFF_DEBUG 0x4 /* turn on debugging */
+#define IFF_ROUTE 0x8 /* routing entry installed */
+#define IFF_POINTOPOINT 0x10 /* interface is point-to-point link */
+
+#define IFF_PASSIVE 0x200000 /* can't tell if up/down */
+#define IFF_INTERFACE 0x400000 /* hardware interface */
+#define IFF_REMOTE 0x800000 /* interface isn't on this machine */
+
+struct interface *if_ifwithaddr(struct sockaddr *);
+struct interface *if_ifwithdstaddr(struct sockaddr *);
+struct interface *if_ifwithnet(struct sockaddr *);
+struct interface *if_iflookup(struct sockaddr *);
+
diff --git a/usr.sbin/IPXrouted/main.c b/usr.sbin/IPXrouted/main.c
new file mode 100644
index 0000000..9af5b2e
--- /dev/null
+++ b/usr.sbin/IPXrouted/main.c
@@ -0,0 +1,395 @@
+/*
+ * Copyright (c) 1985, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Copyright (c) 1995 John Hay. All rights reserved.
+ *
+ * This file includes significant work done at Cornell University by
+ * Bill Nesheim. That work included by permission.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: main.c,v 1.6 1997/02/22 16:00:57 peter Exp $
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1985, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)main.c 8.1 (Berkeley) 6/5/93";
+#endif /* not lint */
+
+/*
+ * IPX Routing Information Protocol Daemon
+ */
+#include "defs.h"
+#include <sys/time.h>
+
+#include <net/if.h>
+
+#include <errno.h>
+#include <nlist.h>
+#include <signal.h>
+#include <paths.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#define SAP_PKT 0
+#define RIP_PKT 1
+
+struct sockaddr_ipx addr; /* Daemon's Address */
+int ripsock; /* RIP Socket to listen on */
+int sapsock; /* SAP Socket to listen on */
+int kmem;
+int install; /* if 1 call kernel */
+int lookforinterfaces; /* if 1 probe kernel for new up interfaces */
+int performnlist; /* if 1 check if /kernel has changed */
+int externalinterfaces; /* # of remote and local interfaces */
+int timeval; /* local idea of time */
+int noteremoterequests; /* squawk on requests from non-local nets */
+int r; /* Routing socket to install updates with */
+struct sockaddr_ipx ipx_netmask; /* Used in installing routes */
+
+char packet[MAXRXPACKETSIZE+1];
+
+char **argv0;
+
+int supplier = -1; /* process should supply updates */
+int dosap = 1; /* By default do SAP services. */
+int dobcast = 1; /* A RIP/SAP broadcast is needed. */
+time_t lastbcast; /* Time of last RIP/SAP broadcast */
+
+struct rip *msg = (struct rip *) &packet[sizeof (struct ipx)];
+struct sap_packet *sap_msg =
+ (struct sap_packet *) &packet[sizeof (struct ipx)];
+void hup(), fkexit(), timer();
+void process(int fd, int pkt_type);
+int getsocket(int type, int proto, struct sockaddr_ipx *sipx);
+void getinfo();
+void catchtimer();
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int nfds;
+ fd_set fdvar;
+ time_t ttime;
+ struct itimerval tval;
+
+ argv0 = argv;
+ argv++, argc--;
+ while (argc > 0 && **argv == '-') {
+ if (strcmp(*argv, "-s") == 0) {
+ supplier = 1;
+ argv++, argc--;
+ continue;
+ }
+ if (strcmp(*argv, "-q") == 0) {
+ supplier = 0;
+ argv++, argc--;
+ continue;
+ }
+ if (strcmp(*argv, "-R") == 0) {
+ noteremoterequests++;
+ argv++, argc--;
+ continue;
+ }
+ if (strcmp(*argv, "-S") == 0) {
+ dosap = 0;
+ argv++, argc--;
+ continue;
+ }
+ if (strcmp(*argv, "-t") == 0) {
+ tracepackets++;
+ argv++, argc--;
+ ftrace = stderr;
+ tracing = 1;
+ continue;
+ }
+ if (strcmp(*argv, "-g") == 0) {
+ gateway = 1;
+ argv++, argc--;
+ continue;
+ }
+ if (strcmp(*argv, "-l") == 0) {
+ gateway = -1;
+ argv++, argc--;
+ continue;
+ }
+ fprintf(stderr,
+ "usage: ipxrouted [ -s ] [ -q ] [ -t ] [ -g ] [ -l ]\n");
+ exit(1);
+ }
+
+
+#ifndef DEBUG
+ if (!tracepackets)
+ daemon(0, 0);
+#endif
+ openlog("IPXrouted", LOG_PID, LOG_DAEMON);
+
+ addr.sipx_family = AF_IPX;
+ addr.sipx_len = sizeof(addr);
+ addr.sipx_port = htons(IPXPORT_RIP);
+ ipx_anynet.s_net[0] = ipx_anynet.s_net[1] = -1;
+ ipx_netmask.sipx_addr.x_net = ipx_anynet;
+ ipx_netmask.sipx_len = 6;
+ ipx_netmask.sipx_family = AF_IPX;
+ r = socket(AF_ROUTE, SOCK_RAW, 0);
+ /* later, get smart about lookingforinterfaces */
+ if (r)
+ shutdown(r, 0); /* for now, don't want reponses */
+ else {
+ fprintf(stderr, "IPXrouted: no routing socket\n");
+ exit(1);
+ }
+ ripsock = getsocket(SOCK_DGRAM, 0, &addr);
+ if (ripsock < 0)
+ exit(1);
+
+ if (dosap) {
+ addr.sipx_port = htons(IPXPORT_SAP);
+ sapsock = getsocket(SOCK_DGRAM, 0, &addr);
+ if (sapsock < 0)
+ exit(1);
+ } else
+ sapsock = -1;
+
+ /*
+ * Any extra argument is considered
+ * a tracing log file.
+ */
+ if (argc > 0)
+ traceon(*argv);
+ /*
+ * Collect an initial view of the world by
+ * snooping in the kernel. Then, send a request packet on all
+ * directly connected networks to find out what
+ * everyone else thinks.
+ */
+ rtinit();
+ sapinit();
+ ifinit();
+ if (supplier < 0)
+ supplier = 0;
+ /* request the state of the world */
+ msg->rip_cmd = htons(RIPCMD_REQUEST);
+ msg->rip_nets[0].rip_dst = ipx_anynet;
+ msg->rip_nets[0].rip_metric = htons(HOPCNT_INFINITY);
+ msg->rip_nets[0].rip_ticks = htons(-1);
+ toall(sndmsg, NULL, 0);
+
+ if (dosap) {
+ sap_msg->sap_cmd = htons(SAP_REQ);
+ sap_msg->sap[0].ServType = htons(SAP_WILDCARD);
+ toall(sapsndmsg, NULL, 0);
+ }
+
+ signal(SIGALRM, catchtimer);
+ signal(SIGHUP, hup);
+ signal(SIGINT, hup);
+ signal(SIGEMT, fkexit);
+ signal(SIGINFO, getinfo);
+
+ tval.it_interval.tv_sec = TIMER_RATE;
+ tval.it_interval.tv_usec = 0;
+ tval.it_value.tv_sec = TIMER_RATE;
+ tval.it_value.tv_usec = 0;
+ setitimer(ITIMER_REAL, &tval, NULL);
+
+ nfds = 1 + max(sapsock, ripsock);
+
+ for (;;) {
+ if (dobcast) {
+ dobcast = 0;
+ lastbcast = time(NULL);
+ timer();
+ }
+
+ FD_ZERO(&fdvar);
+ if (dosap) {
+ FD_SET(sapsock, &fdvar);
+ }
+ FD_SET(ripsock, &fdvar);
+
+ if(select(nfds, &fdvar, (fd_set *)NULL, (fd_set *)NULL,
+ (struct timeval *)NULL) < 0) {
+ if(errno == EINTR)
+ continue;
+ perror("during select");
+ exit(1);
+ }
+
+ if(FD_ISSET(ripsock, &fdvar))
+ process(ripsock, RIP_PKT);
+
+ if(dosap && FD_ISSET(sapsock, &fdvar))
+ process(sapsock, SAP_PKT);
+
+ ttime = time(NULL);
+ if (ttime > (lastbcast + TIMER_RATE + (TIMER_RATE * 2 / 3))) {
+ dobcast = 1;
+ syslog(LOG_ERR, "Missed alarm");
+ }
+ }
+}
+
+void
+process(fd, pkt_type)
+ int fd;
+ int pkt_type;
+{
+ struct sockaddr from;
+ int fromlen = sizeof (from), cc, omask;
+ struct ipx *ipxdp = (struct ipx *)packet;
+
+ cc = recvfrom(fd, packet, sizeof (packet), 0, &from, &fromlen);
+ if (cc <= 0) {
+ if (cc < 0 && errno != EINTR)
+ syslog(LOG_ERR, "recvfrom: %m");
+ return;
+ }
+ if (tracepackets > 1 && ftrace) {
+ fprintf(ftrace,"rcv %d bytes on %s ",
+ cc, ipxdp_ntoa(&ipxdp->ipx_dna));
+ fprintf(ftrace," from %s\n", ipxdp_ntoa(&ipxdp->ipx_sna));
+ }
+
+ if (noteremoterequests &&
+ !ipx_neteqnn(ipxdp->ipx_sna.x_net, ipx_zeronet) &&
+ !ipx_neteq(ipxdp->ipx_sna, ipxdp->ipx_dna))
+ {
+ syslog(LOG_ERR,
+ "net of interface (%s) != net on ether (%s)!\n",
+ ipxdp_nettoa(ipxdp->ipx_dna.x_net),
+ ipxdp_nettoa(ipxdp->ipx_sna.x_net));
+ }
+
+ /* We get the IPX header in front of the RIF packet*/
+ cc -= sizeof (struct ipx);
+#define mask(s) (1<<((s)-1))
+ omask = sigblock(mask(SIGALRM));
+ switch(pkt_type) {
+ case SAP_PKT: sap_input(&from, cc);
+ break;
+ case RIP_PKT: rip_input(&from, cc);
+ break;
+ }
+ sigsetmask(omask);
+}
+
+int
+getsocket(type, proto, sipx)
+ int type, proto;
+ struct sockaddr_ipx *sipx;
+{
+ int domain = sipx->sipx_family;
+ int retry, s, on = 1;
+
+ retry = 1;
+ while ((s = socket(domain, type, proto)) < 0 && retry) {
+ syslog(LOG_ERR, "socket: %m");
+ sleep(5 * retry);
+ retry <<= 1;
+ }
+ if (retry == 0)
+ return (-1);
+ while (bind(s, (struct sockaddr *)sipx, sizeof (*sipx)) < 0 && retry) {
+ syslog(LOG_ERR, "bind: %m");
+ sleep(5 * retry);
+ retry <<= 1;
+ }
+ if (retry == 0)
+ return (-1);
+ if (domain==AF_IPX) {
+ struct ipx ipxdp;
+ if (setsockopt(s, 0, SO_HEADERS_ON_INPUT, &on, sizeof(on))) {
+ syslog(LOG_ERR, "setsockopt SEE HEADERS: %m");
+ exit(1);
+ }
+ if (ntohs(sipx->sipx_addr.x_port) == IPXPORT_RIP)
+ ipxdp.ipx_pt = IPXPROTO_RI;
+ else if (ntohs(sipx->sipx_addr.x_port) == IPXPORT_SAP)
+#ifdef IPXPROTO_SAP
+ ipxdp.ipx_pt = IPXPROTO_SAP;
+#else
+ ipxdp.ipx_pt = IPXPROTO_PXP;
+#endif
+ else {
+ syslog(LOG_ERR, "port should be either RIP or SAP");
+ exit(1);
+ }
+ if (setsockopt(s, 0, SO_DEFAULT_HEADERS, &ipxdp, sizeof(ipxdp))) {
+ syslog(LOG_ERR, "setsockopt SET HEADER: %m");
+ exit(1);
+ }
+ }
+ if (setsockopt(s, SOL_SOCKET, SO_BROADCAST, &on, sizeof (on)) < 0) {
+ syslog(LOG_ERR, "setsockopt SO_BROADCAST: %m");
+ exit(1);
+ }
+ return (s);
+}
+
+/*
+ * Fork and exit on EMT-- for profiling.
+ */
+void
+fkexit()
+{
+ if (fork() == 0)
+ exit(0);
+}
+
+void
+catchtimer()
+{
+ dobcast = 1;
+}
+
+void
+getinfo()
+{
+ FILE *fh;
+
+ fh = fopen("/tmp/ipxrouted.dmp", "a");
+ if(fh == NULL)
+ return;
+
+ dumpriptable(fh);
+ dumpsaptable(fh, sap_head);
+
+ fclose(fh);
+}
+
diff --git a/usr.sbin/IPXrouted/output.c b/usr.sbin/IPXrouted/output.c
new file mode 100644
index 0000000..f824ecd
--- /dev/null
+++ b/usr.sbin/IPXrouted/output.c
@@ -0,0 +1,231 @@
+/*
+ * Copyright (c) 1985, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Copyright (c) 1995 John Hay. All rights reserved.
+ *
+ * This file includes significant work done at Cornell University by
+ * Bill Nesheim. That work included by permission.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: output.c,v 1.6 1997/02/22 16:00:58 peter Exp $
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)output.c 8.1 (Berkeley) 6/5/93";
+#endif /* not lint */
+
+/*
+ * Routing Table Management Daemon
+ */
+#include <unistd.h>
+#include "defs.h"
+
+/*
+ * Apply the function "f" to all non-passive
+ * interfaces. If the interface supports the
+ * use of broadcasting use it, otherwise address
+ * the output to the known router.
+ */
+void
+toall(f, except, changesonly)
+ void (*f)(struct sockaddr *, int, struct interface *, int);
+ struct rt_entry *except;
+ int changesonly;
+{
+ register struct interface *ifp;
+ register struct sockaddr *dst;
+ register int flags;
+ register struct rt_entry *trt;
+ int onlist;
+ extern struct interface *ifnet;
+
+ for (ifp = ifnet; ifp; ifp = ifp->int_next) {
+ if (ifp->int_flags & IFF_PASSIVE)
+ continue;
+
+ /*
+ * Don't send it on interfaces in the except list.
+ */
+ onlist = 0;
+ trt = except;
+ while(trt) {
+ if (ifp == trt->rt_ifp) {
+ onlist = 1;
+ break;
+ }
+ trt = trt->rt_clone;
+ }
+ if (onlist)
+ continue;
+
+ dst = ifp->int_flags & IFF_BROADCAST ? &ifp->int_broadaddr :
+ ifp->int_flags & IFF_POINTOPOINT ? &ifp->int_dstaddr :
+ &ifp->int_addr;
+ flags = ifp->int_flags & IFF_INTERFACE ? MSG_DONTROUTE : 0;
+ (*f)(dst, flags, ifp, changesonly);
+ }
+}
+
+/*
+ * Output a preformed packet.
+ */
+void
+sndmsg(dst, flags, ifp, changesonly)
+ struct sockaddr *dst;
+ int flags;
+ struct interface *ifp;
+ int changesonly;
+{
+
+ (*afswitch[dst->sa_family].af_output)
+ (ripsock, flags, dst, sizeof (struct rip));
+ TRACE_OUTPUT(ifp, dst, sizeof (struct rip));
+}
+
+/*
+ * Supply dst with the contents of the routing tables.
+ * If this won't fit in one packet, chop it up into several.
+ *
+ * This must be done using the split horizon algorithm.
+ * 1. Don't send routing info to the interface from where it was received.
+ * 2. Don't publish an interface to itself.
+ * 3. If a route is received from more than one interface and the cost is
+ * the same, don't publish it on either interface. I am calling this
+ * clones.
+ */
+void
+supply(dst, flags, ifp, changesonly)
+ struct sockaddr *dst;
+ int flags;
+ struct interface *ifp;
+ int changesonly;
+{
+ register struct rt_entry *rt;
+ register struct rt_entry *crt; /* Clone route */
+ register struct rthash *rh;
+ register struct netinfo *nn;
+ register struct netinfo *n = msg->rip_nets;
+ struct sockaddr_ipx *sipx = (struct sockaddr_ipx *) dst;
+ af_output_t *output = afswitch[dst->sa_family].af_output;
+ int size, metric, ticks;
+ union ipx_net net;
+ int delay = 0;
+
+ if (sipx->sipx_port == 0)
+ sipx->sipx_port = htons(IPXPORT_RIP);
+
+ msg->rip_cmd = ntohs(RIPCMD_RESPONSE);
+ for (rh = nethash; rh < &nethash[ROUTEHASHSIZ]; rh++)
+ for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
+ size = (char *)n - (char *)msg;
+ if (size >= ((MAXRIPNETS * sizeof (struct netinfo)) +
+ sizeof (msg->rip_cmd))) {
+ (*output)(ripsock, flags, dst, size);
+ TRACE_OUTPUT(ifp, dst, size);
+ n = msg->rip_nets;
+ delay++;
+ if(delay == 2) {
+ usleep(50000);
+ delay = 0;
+ }
+ }
+
+ if (changesonly && !(rt->rt_state & RTS_CHANGED))
+ continue;
+
+ /*
+ * This should do rule one and two of the split horizon
+ * algorithm.
+ */
+ if (rt->rt_ifp == ifp)
+ continue;
+
+ /*
+ * Rule 3.
+ * Look if we have clones (different routes to the same
+ * place with exactly the same cost).
+ *
+ * We should not publish on any of the clone interfaces.
+ */
+ crt = rt->rt_clone;
+ while (crt) {
+ if (crt->rt_ifp == ifp)
+ goto next;
+ crt = crt->rt_clone;
+ }
+
+ sipx = (struct sockaddr_ipx *)&rt->rt_dst;
+ if ((rt->rt_flags & (RTF_HOST|RTF_GATEWAY)) == RTF_HOST)
+ sipx = (struct sockaddr_ipx *)&rt->rt_router;
+ if (rt->rt_metric == HOPCNT_INFINITY)
+ metric = HOPCNT_INFINITY;
+ else {
+ metric = rt->rt_metric + 1;
+ /*
+ * We don't advertize routes with more than 15 hops.
+ */
+ if (metric >= HOPCNT_INFINITY)
+ continue;
+ }
+ /* XXX One day we should cater for slow interfaces also. */
+ ticks = rt->rt_ticks + 1;
+ net = sipx->sipx_addr.x_net;
+
+ /*
+ * Make sure that we don't put out a two net entries
+ * for a pt to pt link (one for the G route, one for the if)
+ * This is a kludge, and won't work if there are lots of nets.
+ */
+ for (nn = msg->rip_nets; nn < n; nn++) {
+ if (ipx_neteqnn(net, nn->rip_dst)) {
+ if (ticks < ntohs(nn->rip_ticks)) {
+ nn->rip_metric = htons(metric);
+ nn->rip_ticks = htons(ticks);
+ } else if ((ticks == ntohs(nn->rip_ticks)) &&
+ (metric < ntohs(nn->rip_metric))) {
+ nn->rip_metric = htons(metric);
+ nn->rip_ticks = htons(ticks);
+ }
+ goto next;
+ }
+ }
+ n->rip_dst = net;
+ n->rip_metric = htons(metric);
+ n->rip_ticks = htons(ticks);
+ n++;
+ next:;
+ }
+ if (n != msg->rip_nets) {
+ size = (char *)n - (char *)msg;
+ (*output)(ripsock, flags, dst, size);
+ TRACE_OUTPUT(ifp, dst, size);
+ }
+}
diff --git a/usr.sbin/IPXrouted/protocol.h b/usr.sbin/IPXrouted/protocol.h
new file mode 100644
index 0000000..90a013a
--- /dev/null
+++ b/usr.sbin/IPXrouted/protocol.h
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 1985, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Copyright (c) 1995 John Hay. All rights reserved.
+ *
+ * This file includes significant work done at Cornell University by
+ * Bill Nesheim. That work included by permission.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)protocol.h 8.1 (Berkeley) 6/5/93
+ *
+ * $Id$
+ */
+
+/*
+ * IPX Routing Information Protocol
+ *
+ */
+
+struct netinfo {
+ union ipx_net rip_dst; /* destination net */
+ u_short rip_metric; /* cost of route */
+ u_short rip_ticks; /* cost of route */
+};
+
+struct rip {
+ u_short rip_cmd; /* request/response */
+ struct netinfo rip_nets[1]; /* variable length */
+};
+
+/*
+ * Packet types.
+ */
+#define RIPCMD_REQUEST 1 /* want info */
+#define RIPCMD_RESPONSE 2 /* responding to request */
+
+#define RIPCMD_MAX 3
+#ifdef RIPCMDS
+char *ripcmds[RIPCMD_MAX] =
+ { "#0", "REQUEST", "RESPONSE" };
+#endif
+
+#define HOPCNT_INFINITY 16 /* per IPX */
+#define DSTNETS_ALL 0xffffffff /* per IPX */
+#define MAXRXPACKETSIZE 1500 /* max rx broadcast size */
+#define MAXRIPNETS 50 /* max nets in tx packet */
+
+extern union ipx_net ipx_anynet;
+extern union ipx_net ipx_zeronet;
+
+/*
+ * Timer values used in managing the routing table.
+ * Every update forces an entry's timer to be reset. After
+ * EXPIRE_TIME without updates, the entry is marked invalid,
+ * but held onto until GARBAGE_TIME so that others may
+ * see it "be deleted".
+ */
+#define TIMER_RATE 30 /* alarm clocks every 30 seconds */
+
+#define SUPPLY_INTERVAL 30 /* time to supply tables */
+#define RIP_INTERVAL 60 /* time to supply rip tables */
+
+#define EXPIRE_TIME 180 /* time to mark entry invalid */
+#define GARBAGE_TIME 240 /* time to garbage collect */
diff --git a/usr.sbin/IPXrouted/sap.h b/usr.sbin/IPXrouted/sap.h
new file mode 100644
index 0000000..5473851
--- /dev/null
+++ b/usr.sbin/IPXrouted/sap.h
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 1995 John Hay. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by John Hay.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY John Hay AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL John Hay OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: sap.h,v 1.5 1997/02/22 16:00:59 peter Exp $
+ */
+#ifndef _SAP_H_
+#define _SAP_H_
+
+#define SAP_REQ 1
+#define SAP_RESP 2
+#define SAP_REQ_NEAR 3
+#define SAP_RESP_NEAR 4
+
+#define SAPCMD_MAX 5
+#ifdef SAPCMDS
+char *sapcmds[SAPCMD_MAX] =
+ { "#0", "REQUEST", "RESPONSE", "REQ NEAREST", "RESP NEAREST"};
+#endif
+
+#define MAXSAPENTRIES 7
+#define SAP_WILDCARD 0xFFFF
+#define SERVNAMELEN 48
+typedef struct sap_info {
+ u_short ServType;
+ char ServName[SERVNAMELEN];
+ struct ipx_addr ipx;
+ u_short hops;
+ }sap_info;
+
+typedef struct sap_packet {
+ u_short sap_cmd;
+ sap_info sap[0]; /* Variable length. */
+ }sap_packet;
+
+typedef struct sap_entry {
+ struct sap_entry *forw;
+ struct sap_entry *back;
+ struct sap_entry *clone;
+ struct interface *ifp;
+ struct sap_info sap;
+ struct sockaddr source;
+ int hash;
+ int state;
+ int timer;
+ }sap_entry;
+
+#define SAPHASHSIZ 256 /* Should be a power of 2 */
+#define SAPHASHMASK (SAPHASHSIZ-1)
+typedef struct sap_hash {
+ struct sap_entry *forw;
+ struct sap_entry *back;
+ }sap_hash;
+
+extern sap_hash sap_head[SAPHASHSIZ];
+
+extern struct sap_packet *sap_msg;
+
+void sapinit(void);
+void sap_input(struct sockaddr *from, int size);
+void sapsndmsg(struct sockaddr *dst, int flags, struct interface *ifp,
+ int changesonly);
+void sap_supply_toall(int changesonly);
+void sap_supply(struct sockaddr *dst,
+ int flags,
+ struct interface *ifp,
+ int ServType,
+ int changesonly);
+
+struct sap_entry *sap_lookup(u_short ServType, char *ServName);
+struct sap_entry *sap_nearestserver(ushort ServType, struct interface *ifp);
+void sap_add(struct sap_info *si, struct sockaddr *from);
+void sap_change(struct sap_entry *sap,
+ struct sap_info *si,
+ struct sockaddr *from);
+void sap_add_clone(struct sap_entry *sap,
+ struct sap_info *clone,
+ struct sockaddr *from);
+void sap_delete(struct sap_entry *sap);
+
+#endif /*_SAP_H_*/
+
diff --git a/usr.sbin/IPXrouted/sap_input.c b/usr.sbin/IPXrouted/sap_input.c
new file mode 100644
index 0000000..26130fe
--- /dev/null
+++ b/usr.sbin/IPXrouted/sap_input.c
@@ -0,0 +1,212 @@
+/*
+ * Copyright (c) 1995 John Hay. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by John Hay.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY John Hay AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL John Hay OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: sap_input.c,v 1.4 1997/02/22 16:00:59 peter Exp $
+ */
+
+/*
+ * IPX Routing Table Management Daemon
+ */
+#include "defs.h"
+
+
+/*
+ * Process a newly received packet.
+ */
+void
+sap_input(from, size)
+ struct sockaddr *from;
+ int size;
+{
+ int newsize;
+ int sapchanged = 0;
+ struct sap_entry *sap;
+ struct sap_info *n;
+ struct interface *ifp = 0;
+ struct afswitch *afp;
+ struct sockaddr_ipx *ipxp;
+
+ ifp = if_ifwithnet(from);
+ ipxp = (struct sockaddr_ipx *)from;
+ if (ifp == 0) {
+ if(ftrace) {
+ fprintf(ftrace, "Received bogus packet from %s\n",
+ ipxdp_ntoa(&ipxp->sipx_addr));
+ }
+ return;
+ }
+
+ if (ftrace)
+ dumpsappacket(ftrace, "received", from, (char *)sap_msg , size);
+
+ if (from->sa_family >= AF_MAX)
+ return;
+ afp = &afswitch[from->sa_family];
+
+ size -= sizeof (u_short) /* command */;
+ n = sap_msg->sap;
+
+ switch (ntohs(sap_msg->sap_cmd)) {
+
+ case SAP_REQ_NEAR:
+ if (ftrace)
+ fprintf(ftrace, "Received a sap REQ_NEAR packet.\n");
+ sap = sap_nearestserver(n->ServType, ifp);
+ if (sap == NULL)
+ return;
+ sap_msg->sap_cmd = htons(SAP_RESP_NEAR);
+ *n = sap->sap;
+ n->hops = htons(ntohs(n->hops) + 1);
+ if (ntohs(n->hops) >= HOPCNT_INFINITY)
+ return;
+
+ newsize = sizeof(struct sap_info) + sizeof(struct sap_packet);
+ (*afp->af_output)(sapsock, 0, from, newsize);
+ if (ftrace) {
+ fprintf(ftrace, "sap_nearestserver %X %s returned:\n",
+ ntohs(n->ServType),
+ ifp->int_name);
+ fprintf(ftrace, " service %04X %-20.20s "
+ "addr %s.%04X metric %d\n",
+ ntohs(sap->sap.ServType),
+ sap->sap.ServName,
+ ipxdp_ntoa(&sap->sap.ipx),
+ ntohs(sap->sap.ipx.x_port),
+ ntohs(sap->sap.hops));
+ }
+ return;
+
+ case SAP_REQ:
+ if (ftrace)
+ fprintf(ftrace, "Received a sap REQ packet.\n");
+
+ sap_supply(from, 0, ifp, n->ServType, 0);
+ return;
+
+ case SAP_RESP_NEAR:
+ /* XXX We do nothing here, for the moment.
+ * Maybe we should check if the service is in our table?
+ *
+ */
+ if (ftrace)
+ fprintf(ftrace, "Received a sap RESP_NEAR packet.\n");
+
+ return;
+
+ case SAP_RESP:
+ if (ftrace)
+ fprintf(ftrace, "Received a sap RESP packet.\n");
+
+ (*afp->af_canon)(from);
+
+ for (; size > 0; size -= sizeof (struct sap_info), n++) {
+ if (size < sizeof (struct netinfo))
+ break;
+ /*
+ * The idea here is that if the hop count is more
+ * than INFINITY it is bogus and should be discarded.
+ * If it is equal to INFINITY it is a message to say
+ * that a service went down. If we don't allready
+ * have it in our tables discard it. Otherwise
+ * update our table and set the timer to EXPIRE_TIME
+ * so that it is removed next time we go through the
+ * tables.
+ */
+ if (ntohs(n->hops) > HOPCNT_INFINITY)
+ continue;
+ sap = sap_lookup(n->ServType, n->ServName);
+ if (sap == 0) {
+ if (ntohs(n->hops) == HOPCNT_INFINITY)
+ continue;
+ sap_add(n, from);
+ sapchanged = 1;
+ continue;
+ }
+
+ /*
+ * A clone is a different route to the same service
+ * with exactly the same cost (metric).
+ * They must all be recorded because those interfaces
+ * must be handled in the same way as the first route
+ * to that service. ie When using the split horizon
+ * algorithm we must look at these interfaces also.
+ *
+ * Update if from gateway and different,
+ * from anywhere and less hops or
+ * getting stale and equivalent.
+ */
+ if (((ifp != sap->ifp) ||
+ !equal(&sap->source, from)) &&
+ (n->hops == sap->sap.hops) &&
+ (ntohs(n->hops) != HOPCNT_INFINITY)) {
+ register struct sap_entry *tsap = sap->clone;
+
+ while (tsap) {
+ if ((ifp == tsap->ifp) &&
+ equal(&tsap->source, from)) {
+ tsap->timer = 0;
+ break;
+ }
+ tsap = tsap->clone;
+ }
+ if (tsap == NULL) {
+ sap_add_clone(sap, n, from);
+ }
+ continue;
+ }
+ if ((ifp == sap->ifp) &&
+ equal(&sap->source, from) &&
+ (ntohs(n->hops) == ntohs(sap->sap.hops)))
+ sap->timer = 0;
+ else if (((ifp == sap->ifp) &&
+ equal(&sap->source, from) &&
+ (n->hops != sap->sap.hops)) ||
+ (ntohs(n->hops) < ntohs(sap->sap.hops)) ||
+ (sap->timer > (EXPIRE_TIME*2/3) &&
+ ntohs(sap->sap.hops) == ntohs(n->hops) &&
+ ntohs(n->hops) != HOPCNT_INFINITY)) {
+ sap_change(sap, n, from);
+ sapchanged = 1;
+ }
+ }
+ if (sapchanged) {
+ register struct sap_entry *sap;
+ register struct sap_hash *sh;
+ sap_supply_toall(1);
+
+ for (sh = sap_head; sh < &sap_head[SAPHASHSIZ]; sh++)
+ for (sap = sh->forw;
+ sap != (struct sap_entry *)sh;
+ sap = sap->forw)
+ sap->state &= ~RTS_CHANGED;
+ }
+ return;
+ }
+}
diff --git a/usr.sbin/IPXrouted/sap_output.c b/usr.sbin/IPXrouted/sap_output.c
new file mode 100644
index 0000000..7f7dbcd
--- /dev/null
+++ b/usr.sbin/IPXrouted/sap_output.c
@@ -0,0 +1,197 @@
+/*
+ * Copyright (c) 1995 John Hay. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by John Hay.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY John Hay AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL John Hay OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: sap_output.c,v 1.7 1997/02/22 16:01:00 peter Exp $
+ */
+
+/*
+ * Routing Table Management Daemon
+ */
+#include <unistd.h>
+#include "defs.h"
+
+/*
+ * Apply the function "f" to all non-passive
+ * interfaces. If the interface supports the
+ * use of broadcasting use it, otherwise address
+ * the output to the known router.
+ */
+void
+sap_supply_toall(changesonly)
+ int changesonly;
+{
+ register struct interface *ifp;
+ struct sockaddr dst;
+ register struct sockaddr_ipx *ipx_dst;
+ register int flags;
+ extern struct interface *ifnet;
+
+ ipx_dst = (struct sockaddr_ipx *)&dst;
+
+ for (ifp = ifnet; ifp; ifp = ifp->int_next) {
+ if (ifp->int_flags & IFF_PASSIVE)
+ continue;
+
+ dst = ifp->int_flags & IFF_BROADCAST ? ifp->int_broadaddr :
+ ifp->int_flags & IFF_POINTOPOINT ? ifp->int_dstaddr :
+ ifp->int_addr;
+
+ ipx_dst->sipx_addr.x_port = htons(IPXPORT_SAP);
+
+ flags = ifp->int_flags & IFF_INTERFACE ? MSG_DONTROUTE : 0;
+ sap_supply(&dst, flags, ifp, SAP_WILDCARD, changesonly);
+ }
+}
+
+void
+sapsndmsg(dst, flags, ifp, changesonly)
+ struct sockaddr *dst;
+ int flags;
+ struct interface *ifp;
+ int changesonly;
+{
+ struct sockaddr t_dst;
+ struct sockaddr_ipx *ipx_dst;
+
+ t_dst = *dst;
+ ipx_dst = (struct sockaddr_ipx *)&t_dst;
+
+ if (ipx_dst->sipx_addr.x_port == 0)
+ ipx_dst->sipx_addr.x_port = htons(IPXPORT_SAP);
+
+ (*afswitch[dst->sa_family].af_output)
+ (sapsock, flags, &t_dst,
+ sizeof (struct sap_packet) + sizeof(u_short));
+ TRACE_SAP_OUTPUT(ifp, &t_dst,
+ sizeof (struct sap_packet) + sizeof(u_short));
+}
+
+/*
+ * Supply dst with the contents of the SAP tables. If the ServType ==
+ * SAP_WILDCARD (0xFFFF) supply the whole table, otherwise only the
+ * services that are of ServType. If this won't fit in one packet, chop
+ * it up into several.
+ *
+ * This must be done using the split horizon algorithm.
+ * 1. Don't send SAP info to the interface from where it was received.
+ * 2. If a service is received from more than one interface and the cost is
+ * the same, don't publish it on either interface. I am calling this
+ * clones.
+ */
+void
+sap_supply(dst, flags, ifp, ServType, changesonly)
+ struct sockaddr *dst;
+ int flags;
+ struct interface *ifp;
+ int ServType;
+ int changesonly;
+{
+ register struct sap_entry *sap;
+ register struct sap_entry *csap; /* Clone route */
+ register struct sap_hash *sh;
+ register struct sap_info *n = sap_msg->sap;
+ struct sap_hash *base = sap_head;
+ struct sockaddr_ipx *sipx = (struct sockaddr_ipx *) dst;
+ af_output_t *output = afswitch[dst->sa_family].af_output;
+ int size, metric;
+ int delay = 0;
+
+ if (sipx->sipx_port == 0)
+ sipx->sipx_port = htons(IPXPORT_SAP);
+
+ sap_msg->sap_cmd = ntohs(SAP_RESP);
+
+ for (sh = base; sh < &base[SAPHASHSIZ]; sh++)
+ for (sap = sh->forw; sap != (struct sap_entry *)sh; sap = sap->forw) {
+ size = (char *)n - (char *)sap_msg;
+ if (size >= ((MAXSAPENTRIES * sizeof (struct sap_info)) +
+ sizeof (sap_msg->sap_cmd))) {
+ (*output)(sapsock, flags, dst, size);
+ TRACE_SAP_OUTPUT(ifp, dst, size);
+ n = sap_msg->sap;
+ delay++;
+ if(delay == 2) {
+ usleep(50000);
+ delay = 0;
+ }
+ }
+
+ if (changesonly && !(sap->state & RTS_CHANGED))
+ continue;
+
+ /*
+ * Check for the servicetype except if the ServType is
+ * a wildcard (0xFFFF).
+ */
+ if ((ServType != SAP_WILDCARD) &&
+ (ServType != sap->sap.ServType))
+ continue;
+
+ /*
+ * This should do rule one and two of the split horizon
+ * algorithm.
+ */
+ if (sap->ifp == ifp)
+ continue;
+
+ /*
+ * Rule 2.
+ * Look if we have clones (different routes to the same
+ * place with exactly the same cost).
+ *
+ * We should not publish on any of the clone interfaces.
+ */
+ csap = sap->clone;
+ while (csap) {
+ if (csap->ifp == ifp)
+ goto next;
+ csap = csap->clone;
+ }
+
+ /*
+ * Don't advertise services with more than 15 hops. It
+ * will be confused with a service that has gone down.
+ */
+ if (ntohs(sap->sap.hops) == (HOPCNT_INFINITY - 1))
+ continue;
+ metric = min(ntohs(sap->sap.hops) + 1, HOPCNT_INFINITY);
+
+ *n = sap->sap;
+ n->hops = htons(metric);
+ n++;
+next:
+ }
+ if (n != sap_msg->sap) {
+ size = (char *)n - (char *)sap_msg;
+ (*output)(sapsock, flags, dst, size);
+ TRACE_SAP_OUTPUT(ifp, dst, size);
+ }
+}
+
diff --git a/usr.sbin/IPXrouted/sap_tables.c b/usr.sbin/IPXrouted/sap_tables.c
new file mode 100644
index 0000000..863943b
--- /dev/null
+++ b/usr.sbin/IPXrouted/sap_tables.c
@@ -0,0 +1,335 @@
+/*
+ * Copyright (c) 1995 John Hay. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by John Hay.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY John Hay AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL John Hay OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: sap_tables.c,v 1.4 1997/07/01 00:33:41 bde Exp $
+ */
+
+#include "defs.h"
+#include <string.h>
+#include <stdlib.h>
+
+#define FIXLEN(s) { if ((s)->sa_len == 0) (s)->sa_len = sizeof (*(s));}
+
+sap_hash sap_head[SAPHASHSIZ];
+
+void
+sapinit(void)
+{
+ int i;
+
+ for (i=0; i<SAPHASHSIZ; i++)
+ sap_head[i].forw = sap_head[i].back =
+ (struct sap_entry *)&sap_head[i];
+}
+
+/*
+ * This hash use the first 14 letters of the ServName and the ServType
+ * to create a 32 bit hash value.
+ */
+int
+saphash(u_short ServType, char *ServName)
+{
+ int hsh, i;
+ char name[SERVNAMELEN];
+
+ bzero(name, SERVNAMELEN);
+ strncpy(name, ServName, SERVNAMELEN);
+ ServName = name;
+
+ hsh = 0;
+
+#define SMVAL 33
+
+ hsh = hsh * SMVAL + (ServType & 0xff);
+ hsh = hsh * SMVAL + (ServType >> 8);
+
+ for (i=0;i<14;i++) {
+ hsh = hsh * SMVAL + *ServName++;
+ ServName++;
+ }
+
+#undef SMVAL
+
+ return hsh;
+}
+
+/*
+ * Look for an exact match on ServType and ServName. It is
+ * mostly used by the function that process SAP RESPONSE packets.
+ *
+ * A hash is created and used to index into the hash table. Then
+ * that list is walk through searching for a match.
+ *
+ * If no match is found NULL is returned.
+ */
+struct sap_entry *
+sap_lookup(u_short ServType, char *ServName)
+{
+ register struct sap_entry *sap;
+ register struct sap_hash *sh;
+ int hsh;
+
+ hsh = saphash(ServType, ServName);
+ sh = &sap_head[hsh & SAPHASHMASK];
+
+ for(sap = sh->forw; sap != (sap_entry *)sh; sap = sap->forw) {
+ if ((hsh == sap->hash) &&
+ (ServType == sap->sap.ServType) &&
+ (strncmp(ServName, sap->sap.ServName, SERVNAMELEN) == 0)) {
+ return sap;
+ }
+ }
+ return NULL;
+}
+
+/*
+ * This returns the nearest service of the specified type. If no
+ * suitable service is found or if that service is on the interface
+ * where the request came from, NULL is returned.
+ *
+ * When checking interfaces clones must be considered also.
+ *
+ * XXX TODO:
+ * Maybe we can use RIP tables to get the fastest service (ticks).
+ */
+struct sap_entry *
+sap_nearestserver(ushort ServType, struct interface *ifp)
+{
+ register struct sap_entry *sap;
+ register struct sap_entry *csap;
+ struct sap_hash *sh;
+ register struct sap_entry *best = NULL;
+ register int besthops = HOPCNT_INFINITY;
+
+ sh = sap_head;
+
+ for (; sh < &sap_head[SAPHASHSIZ]; sh++)
+ for(sap = sh->forw; sap != (sap_entry *)sh; sap = sap->forw) {
+ if (ServType != sap->sap.ServType)
+ continue;
+ if (ifp == sap->ifp)
+ continue;
+
+ csap = sap->clone;
+ while (csap) {
+ if (ifp == csap->ifp)
+ /*
+ * I would have loved to use
+ * something else here.
+ */
+ goto next;
+ csap = csap->clone;
+ }
+
+ 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..6261cb7
--- /dev/null
+++ b/usr.sbin/IPXrouted/startup.c
@@ -0,0 +1,281 @@
+/*
+ * Copyright (c) 1985, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Copyright (c) 1995 John Hay. All rights reserved.
+ *
+ * This file includes significant work done at Cornell University by
+ * Bill Nesheim. That work included by permission.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id$
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)startup.c 8.1 (Berkeley) 6/5/93";
+#endif /* not lint */
+
+/*
+ * Routing Table Management Daemon
+ */
+#include "defs.h"
+
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/sysctl.h>
+#include <sys/time.h>
+
+#include <net/if.h>
+#include <net/if_dl.h>
+
+#include <nlist.h>
+#include <stdlib.h>
+
+struct interface *ifnet;
+int lookforinterfaces = 1;
+int performnlist = 1;
+int gateway = 0;
+int externalinterfaces = 0; /* # of remote and local interfaces */
+
+void
+quit(s)
+ char *s;
+{
+ extern int errno;
+ int sverrno = errno;
+
+ (void) fprintf(stderr, "IPXroute: ");
+ if (s)
+ (void) fprintf(stderr, "%s: ", s);
+ (void) fprintf(stderr, "%s\n", strerror(sverrno));
+ exit(1);
+ /* NOTREACHED */
+}
+
+struct rt_addrinfo info;
+/* Sleazy use of local variables throughout file, warning!!!! */
+#define netmask info.rti_info[RTAX_NETMASK]
+#define ifaaddr info.rti_info[RTAX_IFA]
+#define brdaddr info.rti_info[RTAX_BRD]
+
+#define ROUNDUP(a) \
+ ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
+#define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len))
+
+void
+rt_xaddrs(cp, cplim, rtinfo)
+ register caddr_t cp, cplim;
+ register struct rt_addrinfo *rtinfo;
+{
+ register struct sockaddr *sa;
+ register int i;
+
+ bzero(rtinfo->rti_info, sizeof(rtinfo->rti_info));
+ for (i = 0; (i < RTAX_MAX) && (cp < cplim); i++) {
+ if ((rtinfo->rti_addrs & (1 << i)) == 0)
+ continue;
+ rtinfo->rti_info[i] = sa = (struct sockaddr *)cp;
+ ADVANCE(cp, sa);
+ }
+}
+
+/*
+ * Find the network interfaces which have configured themselves.
+ * If the interface is present but not yet up (for example an
+ * ARPANET IMP), set the lookforinterfaces flag so we'll
+ * come back later and look again.
+ */
+void
+ifinit(void)
+{
+ struct interface ifs, *ifp;
+ size_t needed;
+ int mib[6], no_ipxaddr = 0, flags = 0;
+ char *buf, *cplim, *cp;
+ register struct if_msghdr *ifm;
+ register struct ifa_msghdr *ifam;
+ struct sockaddr_dl *sdl = 0;
+
+ mib[0] = CTL_NET;
+ mib[1] = PF_ROUTE;
+ mib[2] = 0;
+ mib[3] = AF_IPX;
+ mib[4] = NET_RT_IFLIST;
+ mib[5] = 0;
+ if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0)
+ quit("route-sysctl-estimate");
+ if ((buf = malloc(needed)) == NULL)
+ quit("malloc");
+ if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0)
+ lookforinterfaces = 0;
+ cplim = buf + needed;
+ for (cp = buf; cp < cplim; cp += ifm->ifm_msglen) {
+ ifm = (struct if_msghdr *)cp;
+ if (ifm->ifm_type == RTM_IFINFO) {
+ bzero(&ifs, sizeof(ifs));
+ ifs.int_flags = flags = ifm->ifm_flags | IFF_INTERFACE;
+ if ((flags & IFF_UP) == 0 || no_ipxaddr)
+ lookforinterfaces = 1;
+ sdl = (struct sockaddr_dl *) (ifm + 1);
+ sdl->sdl_data[sdl->sdl_nlen] = 0;
+ no_ipxaddr = 1;
+ continue;
+ }
+ if (ifm->ifm_type != RTM_NEWADDR)
+ quit("ifinit: out of sync");
+ if ((flags & IFF_UP) == 0)
+ continue;
+ ifam = (struct ifa_msghdr *)ifm;
+ info.rti_addrs = ifam->ifam_addrs;
+ rt_xaddrs((char *)(ifam + 1), cp + ifam->ifam_msglen, &info);
+ if (ifaaddr == 0) {
+ syslog(LOG_ERR, "%s: (get addr)", sdl->sdl_data);
+ continue;
+ }
+ ifs.int_addr = *ifaaddr;
+ if (ifs.int_addr.sa_family != AF_IPX)
+ continue;
+ no_ipxaddr = 0;
+ if (ifs.int_flags & IFF_POINTOPOINT) {
+ if (brdaddr == 0) {
+ syslog(LOG_ERR, "%s: (get dstaddr)",
+ sdl->sdl_data);
+ continue;
+ }
+ if (brdaddr->sa_family == AF_UNSPEC) {
+ lookforinterfaces = 1;
+ continue;
+ }
+ ifs.int_dstaddr = *brdaddr;
+ }
+ if (ifs.int_flags & IFF_BROADCAST) {
+ if (brdaddr == 0) {
+ syslog(LOG_ERR, "%s: (get broadaddr)",
+ sdl->sdl_data);
+ continue;
+ }
+ ifs.int_dstaddr = *brdaddr;
+ }
+ /*
+ * 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;
+ /* no one cares about software loopback interfaces */
+ if (ifs.int_flags & IFF_LOOPBACK)
+ continue;
+ ifp = (struct interface *)
+ malloc(sdl->sdl_nlen + 1 + sizeof(ifs));
+ if (ifp == 0) {
+ syslog(LOG_ERR, "IPXrouted: out of memory\n");
+ lookforinterfaces = 1;
+ break;
+ }
+ *ifp = ifs;
+ /*
+ * Count the # of directly connected networks
+ * and point to point links which aren't looped
+ * back to ourself. This is used below to
+ * decide if we should be a routing ``supplier''.
+ */
+ if ((ifs.int_flags & IFF_POINTOPOINT) == 0 ||
+ if_ifwithaddr(&ifs.int_dstaddr) == 0)
+ externalinterfaces++;
+ /*
+ * If we have a point-to-point link, we want to act
+ * as a supplier even if it's our only interface,
+ * as that's the only way our peer on the other end
+ * can tell that the link is up.
+ */
+ if ((ifs.int_flags & IFF_POINTOPOINT) && supplier < 0)
+ supplier = 1;
+ ifp->int_name = (char *)(ifp + 1);
+ strcpy(ifp->int_name, sdl->sdl_data);
+
+ ifp->int_metric = ifam->ifam_metric;
+ ifp->int_next = ifnet;
+ ifnet = ifp;
+ traceinit(ifp);
+ addrouteforif(ifp);
+ }
+ if (externalinterfaces > 1 && supplier < 0)
+ supplier = 1;
+ free(buf);
+}
+
+void
+addrouteforif(ifp)
+ struct interface *ifp;
+{
+ struct sockaddr_ipx net;
+ struct sockaddr *dst;
+ struct rt_entry *rt;
+
+ if (ifp->int_flags & IFF_POINTOPOINT) {
+ int (*match)();
+ register struct interface *ifp2 = ifnet;
+
+ dst = &ifp->int_dstaddr;
+
+ /* Search for interfaces with the same net */
+ ifp->int_sq.n = ifp->int_sq.p = &(ifp->int_sq);
+ match = afswitch[dst->sa_family].af_netmatch;
+ if (match)
+ for (ifp2 = ifnet; ifp2; ifp2 =ifp2->int_next) {
+ if (ifp->int_flags & IFF_POINTOPOINT == 0)
+ continue;
+ if ((*match)(&ifp2->int_dstaddr,&ifp->int_dstaddr)) {
+ insque(&ifp2->int_sq,&ifp->int_sq);
+ break;
+ }
+ }
+ } else {
+ bzero(&net, sizeof(net));
+ net.sipx_family = AF_IPX;
+ net.sipx_len = sizeof (net);
+ net.sipx_addr.x_net = satoipx_addr(ifp->int_broadaddr).x_net;
+ dst = (struct sockaddr *)&net;
+ }
+ rt = rtlookup(dst);
+ if (rt)
+ rtdelete(rt);
+ if (tracing)
+ fprintf(stderr, "Adding route to interface %s\n", ifp->int_name);
+ if (ifp->int_transitions++ > 0)
+ syslog(LOG_ERR, "re-installing interface %s", ifp->int_name);
+ rtadd(dst, &ifp->int_addr, ifp->int_metric, 0,
+ ifp->int_flags & (IFF_INTERFACE|IFF_PASSIVE|IFF_REMOTE));
+}
+
diff --git a/usr.sbin/IPXrouted/table.h b/usr.sbin/IPXrouted/table.h
new file mode 100644
index 0000000..dbfdfe5
--- /dev/null
+++ b/usr.sbin/IPXrouted/table.h
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Copyright (c) 1995 John Hay. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)table.h 5.1 (Berkeley) 6/4/85 (routed/table.h)
+ *
+ * @(#)table.h 8.1 (Berkeley) 6/5/93
+ *
+ * $Id: table.h,v 1.4 1997/02/22 16:01:02 peter Exp $
+ */
+
+/*
+ * Routing table management daemon.
+ */
+
+/*
+ * Routing table structure; differs a bit from kernel tables.
+ *
+ * Note: the union below must agree in the first 4 members
+ * so the ioctl's will work.
+ */
+struct rthash {
+ struct rt_entry *rt_forw;
+ struct rt_entry *rt_back;
+};
+
+#ifdef RTM_ADD
+#define rtentry ortentry
+#endif
+
+struct rt_entry {
+ struct rt_entry *rt_forw;
+ struct rt_entry *rt_back;
+ union {
+ struct rtentry rtu_rt;
+ struct rtuentry {
+ u_long rtu_hash;
+ struct sockaddr rtu_dst;
+ struct sockaddr rtu_router;
+ short rtu_rtflags; /* used by old rtioctl */
+ short rtu_wasted; /* XXX routed does it this way. */
+ int rtu_flags;
+ int rtu_state;
+ int rtu_timer;
+ int rtu_metric;
+ int rtu_ticks;
+ struct interface *rtu_ifp;
+ } rtu_entry;
+ } rt_rtu;
+ struct rt_entry *rt_clone;
+};
+
+#define rt_rt rt_rtu.rtu_entry /* pass to ioctl */
+#define rt_hash rt_rtu.rtu_entry.rtu_hash /* for net or host */
+#define rt_dst rt_rtu.rtu_entry.rtu_dst /* match value */
+#define rt_router rt_rtu.rtu_entry.rtu_router /* who to forward to */
+#define rt_flags rt_rtu.rtu_entry.rtu_flags /* kernel flags */
+#define rt_timer rt_rtu.rtu_entry.rtu_timer /* for invalidation */
+#define rt_state rt_rtu.rtu_entry.rtu_state /* see below */
+#define rt_metric rt_rtu.rtu_entry.rtu_metric /* cost of route */
+#define rt_ticks rt_rtu.rtu_entry.rtu_ticks /* time of route */
+#define rt_ifp rt_rtu.rtu_entry.rtu_ifp /* interface to take */
+
+#define ROUTEHASHSIZ 128 /* must be a power of 2 */
+#define ROUTEHASHMASK (ROUTEHASHSIZ - 1)
+
+/*
+ * "State" of routing table entry.
+ */
+#define RTS_CHANGED 0x1 /* route has been altered recently */
+#define RTS_PASSIVE IFF_PASSIVE /* don't time out route */
+#define RTS_INTERFACE IFF_INTERFACE /* route is for network interface */
+#define RTS_REMOTE IFF_REMOTE /* route is for ``remote'' entity */
+
+extern struct rthash nethash[ROUTEHASHSIZ];
+struct rt_entry *rtlookup(struct sockaddr *);
+struct rt_entry *rtfind(struct sockaddr *);
+void rtadd(struct sockaddr *, struct sockaddr *, short, short, int);
+void rtadd_clone(struct rt_entry *, struct sockaddr *, struct sockaddr *,
+ short, short, int);
+void rtchange(struct rt_entry *, struct sockaddr *, short, short);
+void rtdelete(struct rt_entry *);
+int rtioctl(int, struct rtuentry *);
+void rtinit(void);
+
diff --git a/usr.sbin/IPXrouted/tables.c b/usr.sbin/IPXrouted/tables.c
new file mode 100644
index 0000000..9fe7f45
--- /dev/null
+++ b/usr.sbin/IPXrouted/tables.c
@@ -0,0 +1,434 @@
+/*
+ * Copyright (c) 1985, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Copyright (c) 1995 John Hay. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: tables.c,v 1.5 1997/07/01 00:33:42 bde Exp $
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)tables.c 8.1 (Berkeley) 6/5/93";
+#endif /* not lint */
+
+/*
+ * Routing Table Management Daemon
+ */
+#include "defs.h"
+#include <sys/ioctl.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#ifndef DEBUG
+#define DEBUG 0
+#endif
+
+#define FIXLEN(s) { if ((s)->sa_len == 0) (s)->sa_len = sizeof (*(s));}
+
+int install = !DEBUG; /* if 1 call kernel */
+int delete = 1;
+
+struct rthash nethash[ROUTEHASHSIZ];
+
+/*
+ * Lookup dst in the tables for an exact match.
+ */
+struct rt_entry *
+rtlookup(dst)
+ struct sockaddr *dst;
+{
+ register struct rt_entry *rt;
+ register struct rthash *rh;
+ register u_int hash;
+ struct afhash h;
+
+ if (dst->sa_family >= AF_MAX)
+ return (0);
+ (*afswitch[dst->sa_family].af_hash)(dst, &h);
+ hash = h.afh_nethash;
+ rh = &nethash[hash & ROUTEHASHMASK];
+ for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
+ if (rt->rt_hash != hash)
+ continue;
+ if (equal(&rt->rt_dst, dst))
+ return (rt);
+ }
+ return (0);
+}
+
+/*
+ * Find a route to dst as the kernel would.
+ */
+struct rt_entry *
+rtfind(dst)
+ struct sockaddr *dst;
+{
+ register struct rt_entry *rt;
+ register struct rthash *rh;
+ register u_int hash;
+ struct afhash h;
+ int af = dst->sa_family;
+ int (*match)() = 0;
+
+ if (af >= AF_MAX)
+ return (0);
+ (*afswitch[af].af_hash)(dst, &h);
+
+ hash = h.afh_nethash;
+ rh = &nethash[hash & ROUTEHASHMASK];
+ match = afswitch[af].af_netmatch;
+ for (rt = rh->rt_forw; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
+ if (rt->rt_hash != hash)
+ continue;
+ if (rt->rt_dst.sa_family == af &&
+ (*match)(&rt->rt_dst, dst))
+ return (rt);
+ }
+ return (0);
+}
+
+void
+rtadd(dst, gate, metric, ticks, state)
+ struct sockaddr *dst, *gate;
+ short metric, ticks;
+ int state;
+{
+ struct afhash h;
+ register struct rt_entry *rt;
+ struct rthash *rh;
+ int af = dst->sa_family, flags;
+ u_int hash;
+
+ FIXLEN(dst);
+ FIXLEN(gate);
+ if (af >= AF_MAX)
+ return;
+ (*afswitch[af].af_hash)(dst, &h);
+ flags = (*afswitch[af].af_ishost)(dst) ? RTF_HOST : 0;
+ hash = h.afh_nethash;
+ rh = &nethash[hash & ROUTEHASHMASK];
+ rt = (struct rt_entry *)malloc(sizeof (*rt));
+ if (rt == 0)
+ return;
+ rt->rt_hash = hash;
+ rt->rt_dst = *dst;
+ rt->rt_router = *gate;
+ rt->rt_metric = metric;
+ rt->rt_ticks = ticks;
+ rt->rt_timer = 0;
+ rt->rt_flags = RTF_UP | flags;
+ rt->rt_state = state | RTS_CHANGED;
+ rt->rt_ifp = if_ifwithnet(&rt->rt_router);
+ rt->rt_clone = NULL;
+ if (metric)
+ rt->rt_flags |= RTF_GATEWAY;
+ insque(rt, rh);
+ TRACE_ACTION("ADD", rt);
+ /*
+ * If the ioctl fails because the gateway is unreachable
+ * from this host, discard the entry. This should only
+ * occur because of an incorrect entry in /etc/gateways.
+ */
+ if (install && rtioctl(ADD, &rt->rt_rt) < 0) {
+ if (errno != EEXIST)
+ perror("SIOCADDRT");
+ if (errno == ENETUNREACH) {
+ TRACE_ACTION("DELETE", rt);
+ remque(rt);
+ free((char *)rt);
+ }
+ }
+}
+
+void
+rtadd_clone(ort, dst, gate, metric, ticks, state)
+ struct rt_entry *ort;
+ struct sockaddr *dst, *gate;
+ short metric, ticks;
+ int state;
+{
+ struct afhash h;
+ register struct rt_entry *rt;
+ struct rthash *rh;
+ int af = dst->sa_family, flags;
+ u_int hash;
+
+ FIXLEN(dst);
+ FIXLEN(gate);
+ if (af >= AF_MAX)
+ return;
+ (*afswitch[af].af_hash)(dst, &h);
+ flags = (*afswitch[af].af_ishost)(dst) ? RTF_HOST : 0;
+ hash = h.afh_nethash;
+ rh = &nethash[hash & ROUTEHASHMASK];
+ rt = (struct rt_entry *)malloc(sizeof (*rt));
+ if (rt == 0)
+ return;
+ rt->rt_hash = hash;
+ rt->rt_dst = *dst;
+ rt->rt_router = *gate;
+ rt->rt_metric = metric;
+ rt->rt_ticks = ticks;
+ rt->rt_timer = 0;
+ rt->rt_flags = RTF_UP | flags;
+ rt->rt_state = state | RTS_CHANGED;
+ rt->rt_ifp = if_ifwithnet(&rt->rt_router);
+ rt->rt_clone = NULL;
+ rt->rt_forw = NULL;
+ rt->rt_back = NULL;
+ if (metric)
+ rt->rt_flags |= RTF_GATEWAY;
+
+ while(ort->rt_clone != NULL)
+ ort = ort->rt_clone;
+ ort->rt_clone = rt;
+ TRACE_ACTION("ADD_CLONE", rt);
+}
+
+void
+rtchange(rt, gate, metric, ticks)
+ struct rt_entry *rt;
+ struct sockaddr *gate;
+ short metric, ticks;
+{
+ int doioctl = 0, metricchanged = 0;
+ struct rtuentry oldroute;
+
+ FIXLEN(gate);
+ /*
+ * Handling of clones.
+ * When the route changed and it had clones, handle it special.
+ * 1. If the new route is cheaper than the clone(s), free the clones.
+ * 2. If the new route is the same cost, it may be one of the clones,
+ * search for it and free it.
+ * 3. If the new route is more expensive than the clone(s), use the
+ * values of the clone(s).
+ */
+ if (rt->rt_clone) {
+ if ((ticks < rt->rt_clone->rt_ticks) ||
+ ((ticks == rt->rt_clone->rt_ticks) &&
+ (metric < rt->rt_clone->rt_metric))) {
+ /*
+ * Free all clones.
+ */
+ struct rt_entry *trt, *nrt;
+
+ trt = rt->rt_clone;
+ rt->rt_clone = NULL;
+ while(trt) {
+ nrt = trt->rt_clone;
+ free((char *)trt);
+ trt = nrt;
+ }
+ } else if ((ticks == rt->rt_clone->rt_ticks) &&
+ (metric == rt->rt_clone->rt_metric)) {
+ struct rt_entry *prt, *trt;
+
+ prt = rt;
+ trt = rt->rt_clone;
+
+ while(trt) {
+ if (equal(&trt->rt_router, gate)) {
+ prt->rt_clone = trt->rt_clone;
+ free(trt);
+ trt = prt->rt_clone;
+ } else {
+ prt = trt;
+ trt = trt->rt_clone;
+ }
+ }
+ } else {
+ /*
+ * Use the values of the first clone.
+ * Delete the corresponding clone.
+ */
+ struct rt_entry *trt;
+
+ trt = rt->rt_clone;
+ rt->rt_clone = rt->rt_clone->rt_clone;
+ metric = trt->rt_metric;
+ ticks = trt->rt_ticks;
+ *gate = trt->rt_router;
+ free((char *)trt);
+ }
+ }
+
+ if (!equal(&rt->rt_router, gate))
+ doioctl++;
+ if ((metric != rt->rt_metric) || (ticks != rt->rt_ticks))
+ metricchanged++;
+ if (doioctl || metricchanged) {
+ TRACE_ACTION("CHANGE FROM", rt);
+ if (doioctl) {
+ oldroute = rt->rt_rt;
+ rt->rt_router = *gate;
+ }
+ rt->rt_metric = metric;
+ rt->rt_ticks = ticks;
+ if ((rt->rt_state & RTS_INTERFACE) && metric) {
+ rt->rt_state &= ~RTS_INTERFACE;
+ if(rt->rt_ifp)
+ syslog(LOG_ERR,
+ "changing route from interface %s (timed out)",
+ rt->rt_ifp->int_name);
+ else
+ syslog(LOG_ERR,
+ "changing route from interface ??? (timed out)");
+ }
+ if (metric)
+ rt->rt_flags |= RTF_GATEWAY;
+ else
+ rt->rt_flags &= ~RTF_GATEWAY;
+ rt->rt_ifp = if_ifwithnet(&rt->rt_router);
+ rt->rt_state |= RTS_CHANGED;
+ TRACE_ACTION("CHANGE TO", rt);
+ }
+ if (doioctl && install) {
+#ifndef RTM_ADD
+ if (rtioctl(ADD, &rt->rt_rt) < 0)
+ syslog(LOG_ERR, "rtioctl ADD dst %s, gw %s: %m",
+ ipx_ntoa(&((struct sockaddr_ipx *)&rt->rt_dst)->sipx_addr),
+ ipx_ntoa(&((struct sockaddr_ipx *)&rt->rt_router)->sipx_addr));
+ if (delete && rtioctl(DELETE, &oldroute) < 0)
+ perror("rtioctl DELETE");
+#else
+ if (delete == 0) {
+ if (rtioctl(ADD, &rt->rt_rt) >= 0)
+ return;
+ } else {
+ if (rtioctl(CHANGE, &rt->rt_rt) >= 0)
+ return;
+ }
+ syslog(LOG_ERR, "rtioctl ADD dst %s, gw %s: %m",
+ ipxdp_ntoa(&((struct sockaddr_ipx *)&rt->rt_dst)->sipx_addr),
+ ipxdp_ntoa(&((struct sockaddr_ipx *)&rt->rt_router)->sipx_addr));
+#endif
+ }
+}
+
+void
+rtdelete(rt)
+ struct rt_entry *rt;
+{
+
+ struct sockaddr *sa = &(rt->rt_router);
+ FIXLEN(sa);
+ sa = &(rt->rt_dst);
+ FIXLEN(sa);
+ if (rt->rt_clone) {
+ /*
+ * If there is a clone we just do a rt_change to it.
+ */
+ struct rt_entry *trt = rt->rt_clone;
+ rtchange(rt, &trt->rt_router, trt->rt_metric, trt->rt_ticks);
+ return;
+ }
+ if (rt->rt_state & RTS_INTERFACE) {
+ if (rt->rt_ifp)
+ syslog(LOG_ERR,
+ "deleting route to interface %s (timed out)",
+ rt->rt_ifp->int_name);
+ else
+ syslog(LOG_ERR,
+ "deleting route to interface ??? (timed out)");
+ }
+ TRACE_ACTION("DELETE", rt);
+ if (install && rtioctl(DELETE, &rt->rt_rt) < 0)
+ perror("rtioctl DELETE");
+ remque(rt);
+ free((char *)rt);
+}
+
+void
+rtinit(void)
+{
+ register struct rthash *rh;
+
+ for (rh = nethash; rh < &nethash[ROUTEHASHSIZ]; rh++)
+ rh->rt_forw = rh->rt_back = (struct rt_entry *)rh;
+}
+int seqno;
+
+int
+rtioctl(action, ort)
+ int action;
+ struct rtuentry *ort;
+{
+#ifndef RTM_ADD
+ if (install == 0)
+ return (errno = 0);
+
+ ort->rtu_rtflags = ort->rtu_flags;
+
+ switch (action) {
+
+ case ADD:
+ return (ioctl(s, SIOCADDRT, (char *)ort));
+
+ case DELETE:
+ return (ioctl(s, SIOCDELRT, (char *)ort));
+
+ default:
+ return (-1);
+ }
+#else /* RTM_ADD */
+ struct {
+ struct rt_msghdr w_rtm;
+ struct sockaddr w_dst;
+ struct sockaddr w_gate;
+ struct sockaddr_ipx w_netmask;
+ } w;
+#define rtm w.w_rtm
+
+ bzero((char *)&w, sizeof(w));
+ rtm.rtm_msglen = sizeof(w);
+ rtm.rtm_version = RTM_VERSION;
+ rtm.rtm_type = (action == ADD ? RTM_ADD :
+ (action == DELETE ? RTM_DELETE : RTM_CHANGE));
+ rtm.rtm_flags = ort->rtu_flags;
+ rtm.rtm_seq = ++seqno;
+ rtm.rtm_addrs = RTA_DST|RTA_GATEWAY;
+ bcopy((char *)&ort->rtu_dst, (char *)&w.w_dst, sizeof(w.w_dst));
+ bcopy((char *)&ort->rtu_router, (char *)&w.w_gate, sizeof(w.w_gate));
+ w.w_gate.sa_family = w.w_dst.sa_family = AF_IPX;
+ w.w_gate.sa_len = w.w_dst.sa_len = sizeof(w.w_dst);
+ if (rtm.rtm_flags & RTF_HOST) {
+ rtm.rtm_msglen -= sizeof(w.w_netmask);
+ } else {
+ rtm.rtm_addrs |= RTA_NETMASK;
+ w.w_netmask = ipx_netmask;
+ rtm.rtm_msglen -= sizeof(w.w_netmask) - ipx_netmask.sipx_len;
+ }
+ errno = 0;
+ return write(r, (char *)&w, rtm.rtm_msglen);
+#endif /* RTM_ADD */
+}
diff --git a/usr.sbin/IPXrouted/timer.c b/usr.sbin/IPXrouted/timer.c
new file mode 100644
index 0000000..ea75a98
--- /dev/null
+++ b/usr.sbin/IPXrouted/timer.c
@@ -0,0 +1,239 @@
+/*
+ * Copyright (c) 1985, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Copyright (c) 1995 John Hay. All rights reserved.
+ *
+ * This file includes significant work done at Cornell University by
+ * Bill Nesheim. That work included by permission.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: timer.c,v 1.3 1997/02/22 16:01:03 peter Exp $
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)timer.c 8.1 (Berkeley) 6/5/93";
+#endif /* not lint */
+
+/*
+ * Routing Table Management Daemon
+ */
+#include "defs.h"
+#include <unistd.h>
+#include <stdlib.h>
+
+int timeval = -TIMER_RATE;
+
+/*
+ * Timer routine. Performs routing information supply
+ * duties and manages timers on routing and SAP table entries.
+ */
+void
+timer()
+{
+ register struct rthash *rh;
+ register struct rt_entry *rt;
+ register struct sap_hash *sh;
+ register struct sap_entry *sap;
+ struct sap_hash *sap_base = sap_head;
+ int timetobroadcast, ripbroadcast, sapbroadcast;
+
+ timeval += TIMER_RATE;
+ if (lookforinterfaces && (timeval % CHECK_INTERVAL) == 0)
+ ifinit();
+ timetobroadcast = supplier && (timeval % SUPPLY_INTERVAL) == 0;
+ ripbroadcast = supplier && timetobroadcast &&
+ (timeval % RIP_INTERVAL) == 0;
+ sapbroadcast = timetobroadcast && dosap && !ripbroadcast;
+
+ for (rh = nethash; rh < &nethash[ROUTEHASHSIZ]; rh++) {
+ rt = rh->rt_forw;
+ for (; rt != (struct rt_entry *)rh; rt = rt->rt_forw) {
+ if (rt->rt_clone) {
+ struct rt_entry *trt, *prt;
+ /*
+ * If a clone expire free it and mark the
+ * main route RTS_CHANGED.
+ */
+ prt = rt;
+ trt = rt->rt_clone;
+ while (trt) {
+ trt->rt_timer += TIMER_RATE;
+ if (trt->rt_timer >= EXPIRE_TIME) {
+ prt->rt_clone = trt->rt_clone;
+ free((char *)trt);
+ trt = prt->rt_clone;
+ rt->rt_state |= RTS_CHANGED;
+ } else {
+ prt = trt;
+ trt = prt->rt_clone;
+ }
+ }
+ }
+ /*
+ * We don't advance time on a routing entry for
+ * a passive gateway or that for our only interface.
+ * The latter is excused because we don't act as
+ * a routing information supplier and hence would
+ * time it out. This is fair as if it's down
+ * we're cut off from the world anyway and it's
+ * not likely we'll grow any new hardware in
+ * the mean time.
+ */
+ if (!(rt->rt_state & RTS_PASSIVE) &&
+ !(rt->rt_state & RTS_INTERFACE))
+ rt->rt_timer += TIMER_RATE;
+ if (rt->rt_timer >= EXPIRE_TIME) {
+ rt->rt_metric = HOPCNT_INFINITY;
+ rt->rt_state |= RTS_CHANGED;
+ }
+ if (rt->rt_timer >= GARBAGE_TIME) {
+ rt = rt->rt_back;
+ /* Perhaps we should send a REQUEST for this route? */
+ rtdelete(rt->rt_forw);
+ continue;
+ }
+ if (rt->rt_state & RTS_CHANGED) {
+ rt->rt_state &= ~RTS_CHANGED;
+ /* don't send extraneous packets */
+ if (!supplier || ripbroadcast)
+ continue;
+ if ((rt->rt_metric + 1) == HOPCNT_INFINITY)
+ continue;
+ msg->rip_cmd = htons(RIPCMD_RESPONSE);
+ msg->rip_nets[0].rip_dst =
+ (satoipx_addr(rt->rt_dst)).x_net;
+ msg->rip_nets[0].rip_metric =
+ htons(min(rt->rt_metric+1, HOPCNT_INFINITY));
+ msg->rip_nets[0].rip_ticks =
+ htons(rt->rt_ticks + 1);
+ toall(sndmsg, rt, 0);
+ }
+ }
+ }
+ if (ripbroadcast)
+ toall(supply, NULL, 0);
+
+ /*
+ * Now do the SAP stuff.
+ */
+ for (sh = sap_base; sh < &sap_base[SAPHASHSIZ]; sh++) {
+ sap = sh->forw;
+ for (; sap != (struct sap_entry *)sh; sap = sap->forw) {
+ if (sap->clone) {
+ struct sap_entry *tsap, *psap;
+ /*
+ * If a clone expire free it and mark the
+ * main sap entry RTS_CHANGED.
+ */
+ psap = sap;
+ tsap = sap->clone;
+ while (tsap) {
+ tsap->timer += TIMER_RATE;
+ if (tsap->timer >= EXPIRE_TIME) {
+ psap->clone = tsap->clone;
+ free((char *)tsap);
+ tsap = psap->clone;
+ sap->state |= RTS_CHANGED;
+ } else {
+ psap = tsap;
+ tsap = psap->clone;
+ }
+ }
+ }
+ sap->timer += TIMER_RATE;
+ if (sap->timer >= EXPIRE_TIME) {
+ sap->sap.hops = htons(HOPCNT_INFINITY);
+ sap->state |= RTS_CHANGED;
+ }
+ if (sap->timer >= GARBAGE_TIME) {
+ sap = sap->back;
+ /* Perhaps we should send a REQUEST for this route? */
+ sap_delete(sap->forw);
+ continue;
+ }
+ /*
+ * XXX sap_sndmsg on RTS_CHANGED
+ */
+ if (sap->state & RTS_CHANGED) {
+ sap->state &= ~RTS_CHANGED;
+#ifdef notyet
+ /* don't send extraneous packets */
+ if (!supplier || sapbroadcast)
+ continue;
+ if ((ntohs(sap->sap.hops) + 1) == HOPCNT_INFINITY)
+ continue;
+ sap_msg->sap_cmd = htons(SAP_RESP);
+ sap_msg->sap[0] = sap->sap;
+ sap_msg->sap[0].hops =
+ htons(min(sap->sap.hops+1, HOPCNT_INFINITY));
+ toall(sapsndmsg, rt, 0);
+#endif
+ }
+ }
+ }
+ if (sapbroadcast)
+ sap_supply_toall(0);
+ if (ftrace && sapbroadcast)
+ dumpsaptable(ftrace, sap_head);
+}
+
+/*
+ * On hangup, let everyone know we're going away.
+ */
+void
+hup()
+{
+ register struct rthash *rh;
+ register struct rt_entry *rt;
+ register struct sap_hash *sh;
+ register struct sap_entry *sap;
+
+ if (supplier) {
+ for (rh = nethash; rh < &nethash[ROUTEHASHSIZ]; rh++) {
+ rt = rh->rt_forw;
+ for (; rt != (struct rt_entry *)rh; rt = rt->rt_forw)
+ rt->rt_metric = HOPCNT_INFINITY;
+ }
+ toall(supply, NULL, 0);
+
+ /*
+ * Now for SAP.
+ */
+ for (sh = sap_head; sh < &sap_head[SAPHASHSIZ]; sh++) {
+ sap = sh->forw;
+ for (; sap != (struct sap_entry *)sh; sap = sap->forw)
+ sap->sap.hops = htons(HOPCNT_INFINITY);
+ }
+ if (dosap)
+ sap_supply_toall(0);
+ }
+ exit(1);
+}
diff --git a/usr.sbin/IPXrouted/trace.c b/usr.sbin/IPXrouted/trace.c
new file mode 100644
index 0000000..a4c5df9
--- /dev/null
+++ b/usr.sbin/IPXrouted/trace.c
@@ -0,0 +1,519 @@
+/*
+ * Copyright (c) 1985, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Copyright (c) 1995 John Hay. All rights reserved.
+ *
+ * This file includes significant work done at Cornell University by
+ * Bill Nesheim. That work included by permission.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: trace.c,v 1.4 1997/02/22 16:01:04 peter Exp $
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)trace.c 8.1 (Berkeley) 6/5/93";
+#endif /* not lint */
+
+/*
+ * Routing Table Management Daemon
+ */
+#define RIPCMDS
+#define SAPCMDS
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <time.h>
+#include "defs.h"
+
+#define NRECORDS 50 /* size of circular trace buffer */
+#ifdef DEBUG
+FILE *ftrace = stdout;
+int tracing = 1;
+#else DEBUG
+FILE *ftrace = NULL;
+int tracing = 0;
+#endif
+
+void dumpif(FILE *fd, struct interface *ifp);
+void dumptrace(FILE *fd, char *dir, struct ifdebug *ifd);
+
+void
+traceinit(ifp)
+ register struct interface *ifp;
+{
+ static int iftraceinit();
+
+ if (iftraceinit(ifp, &ifp->int_input) &&
+ iftraceinit(ifp, &ifp->int_output))
+ return;
+ tracing = 0;
+ syslog(LOG_ERR, "traceinit: can't init %s\n", ifp->int_name);
+}
+
+static int
+iftraceinit(ifp, ifd)
+ struct interface *ifp;
+ register struct ifdebug *ifd;
+{
+ register struct iftrace *t;
+
+ ifd->ifd_records =
+ (struct iftrace *)malloc(NRECORDS * sizeof (struct iftrace));
+ if (ifd->ifd_records == 0)
+ return (0);
+ ifd->ifd_front = ifd->ifd_records;
+ ifd->ifd_count = 0;
+ for (t = ifd->ifd_records; t < ifd->ifd_records + NRECORDS; t++) {
+ t->ift_size = 0;
+ t->ift_packet = 0;
+ }
+ ifd->ifd_if = ifp;
+ return (1);
+}
+
+void
+traceon(file)
+ char *file;
+{
+
+ if (ftrace != NULL)
+ return;
+ ftrace = fopen(file, "a");
+ if (ftrace == NULL)
+ return;
+ dup2(fileno(ftrace), 1);
+ dup2(fileno(ftrace), 2);
+ tracing = 1;
+}
+
+void
+traceoff(void)
+{
+ if (!tracing)
+ return;
+ if (ftrace != NULL)
+ fclose(ftrace);
+ ftrace = NULL;
+ tracing = 0;
+}
+
+void
+trace(ifd, who, p, len, m)
+ register struct ifdebug *ifd;
+ struct sockaddr *who;
+ char *p;
+ int len, m;
+{
+ register struct iftrace *t;
+
+ if (ifd->ifd_records == 0)
+ return;
+ t = ifd->ifd_front++;
+ if (ifd->ifd_front >= ifd->ifd_records + NRECORDS)
+ ifd->ifd_front = ifd->ifd_records;
+ if (ifd->ifd_count < NRECORDS)
+ ifd->ifd_count++;
+ if (t->ift_size > 0 && t->ift_packet)
+ free(t->ift_packet);
+ t->ift_packet = 0;
+ t->ift_stamp = time(0);
+ t->ift_who = *who;
+ if (len > 0) {
+ t->ift_packet = malloc(len);
+ if (t->ift_packet)
+ bcopy(p, t->ift_packet, len);
+ else
+ len = 0;
+ }
+ t->ift_size = len;
+ t->ift_metric = m;
+}
+
+void
+traceaction(fd, action, rt)
+ FILE *fd;
+ char *action;
+ struct rt_entry *rt;
+{
+ struct sockaddr_ipx *dst, *gate;
+ static struct bits {
+ int t_bits;
+ char *t_name;
+ } flagbits[] = {
+ { RTF_UP, "UP" },
+ { RTF_GATEWAY, "GATEWAY" },
+ { RTF_HOST, "HOST" },
+ { 0 }
+ }, statebits[] = {
+ { RTS_PASSIVE, "PASSIVE" },
+ { RTS_REMOTE, "REMOTE" },
+ { RTS_INTERFACE,"INTERFACE" },
+ { RTS_CHANGED, "CHANGED" },
+ { 0 }
+ };
+ register struct bits *p;
+ register int first;
+ char *cp;
+
+ if (fd == NULL)
+ return;
+ fprintf(fd, "%s ", action);
+ dst = (struct sockaddr_ipx *)&rt->rt_dst;
+ gate = (struct sockaddr_ipx *)&rt->rt_router;
+ fprintf(fd, "dst %s, ", ipxdp_ntoa(&dst->sipx_addr));
+ fprintf(fd, "router %s, metric %d, ticks %d, flags",
+ ipxdp_ntoa(&gate->sipx_addr), rt->rt_metric, rt->rt_ticks);
+ cp = " %s";
+ for (first = 1, p = flagbits; p->t_bits > 0; p++) {
+ if ((rt->rt_flags & p->t_bits) == 0)
+ continue;
+ fprintf(fd, cp, p->t_name);
+ if (first) {
+ cp = "|%s";
+ first = 0;
+ }
+ }
+ fprintf(fd, " state");
+ cp = " %s";
+ for (first = 1, p = statebits; p->t_bits > 0; p++) {
+ if ((rt->rt_state & p->t_bits) == 0)
+ continue;
+ fprintf(fd, cp, p->t_name);
+ if (first) {
+ cp = "|%s";
+ first = 0;
+ }
+ }
+ putc('\n', fd);
+ if (!tracepackets && (rt->rt_state & RTS_PASSIVE) == 0 && rt->rt_ifp)
+ dumpif(fd, rt->rt_ifp);
+ fflush(fd);
+}
+
+void
+traceactionlog(action, rt)
+ char *action;
+ struct rt_entry *rt;
+{
+ struct sockaddr_ipx *dst, *gate;
+ static struct bits {
+ int t_bits;
+ char *t_name;
+ } flagbits[] = {
+ { RTF_UP, "UP" },
+ { RTF_GATEWAY, "GATEWAY" },
+ { RTF_HOST, "HOST" },
+ { 0 }
+ }, statebits[] = {
+ { RTS_PASSIVE, "PASSIVE" },
+ { RTS_REMOTE, "REMOTE" },
+ { RTS_INTERFACE,"INTERFACE" },
+ { RTS_CHANGED, "CHANGED" },
+ { 0 }
+ };
+ register struct bits *p;
+ register int first;
+ char *cp;
+ char *lstr, *olstr;
+
+ dst = (struct sockaddr_ipx *)&rt->rt_dst;
+ gate = (struct sockaddr_ipx *)&rt->rt_router;
+ asprintf(&lstr, "%s dst %s,", action, ipxdp_ntoa(&dst->sipx_addr));
+ olstr = lstr;
+ asprintf(&lstr, "%s router %s, metric %d, ticks %d, flags",
+ olstr, ipxdp_ntoa(&gate->sipx_addr), rt->rt_metric, rt->rt_ticks);
+ free(olstr);
+ olstr = lstr;
+ cp = "%s %s";
+ for (first = 1, p = flagbits; p->t_bits > 0; p++) {
+ if ((rt->rt_flags & p->t_bits) == 0)
+ continue;
+ asprintf(&lstr, cp, olstr, p->t_name);
+ free(olstr);
+ olstr = lstr;
+ if (first) {
+ cp = "%s|%s";
+ first = 0;
+ }
+ }
+ asprintf(&lstr, "%s state", olstr);
+ free(olstr);
+ olstr = lstr;
+ cp = "%s %s";
+ for (first = 1, p = statebits; p->t_bits > 0; p++) {
+ if ((rt->rt_state & p->t_bits) == 0)
+ continue;
+ asprintf(&lstr, cp, olstr, p->t_name);
+ free(olstr);
+ olstr = lstr;
+ if (first) {
+ cp = "%s|%s";
+ first = 0;
+ }
+ }
+ syslog(LOG_DEBUG, lstr);
+ free(lstr);
+}
+
+void
+tracesapactionlog(action, sap)
+ char *action;
+ struct sap_entry *sap;
+{
+ syslog(LOG_DEBUG, "%-12.12s service %04X %-20.20s "
+ "addr %s.%04X %c metric %d\n",
+ action,
+ ntohs(sap->sap.ServType),
+ sap->sap.ServName,
+ ipxdp_ntoa(&sap->sap.ipx),
+ ntohs(sap->sap.ipx.x_port),
+ (sap->clone ? 'C' : ' '),
+ ntohs(sap->sap.hops));
+}
+
+void
+dumpif(fd, ifp)
+ register struct interface *ifp;
+ FILE *fd;
+{
+ if (ifp->int_input.ifd_count || ifp->int_output.ifd_count) {
+ fprintf(fd, "*** Packet history for interface %s ***\n",
+ ifp->int_name);
+ dumptrace(fd, "to", &ifp->int_output);
+ dumptrace(fd, "from", &ifp->int_input);
+ fprintf(fd, "*** end packet history ***\n");
+ }
+}
+
+void
+dumptrace(fd, dir, ifd)
+ FILE *fd;
+ char *dir;
+ register struct ifdebug *ifd;
+{
+ register struct iftrace *t;
+ char *cp = !strcmp(dir, "to") ? "Output" : "Input";
+
+ if (ifd->ifd_front == ifd->ifd_records &&
+ ifd->ifd_front->ift_size == 0) {
+ fprintf(fd, "%s: no packets.\n", cp);
+ return;
+ }
+ fprintf(fd, "%s trace:\n", cp);
+ t = ifd->ifd_front - ifd->ifd_count;
+ if (t < ifd->ifd_records)
+ t += NRECORDS;
+ for ( ; ifd->ifd_count; ifd->ifd_count--, t++) {
+ if (t >= ifd->ifd_records + NRECORDS)
+ t = ifd->ifd_records;
+ if (t->ift_size == 0)
+ continue;
+ fprintf(fd, "%.24s: metric=%d\n", ctime(&t->ift_stamp),
+ t->ift_metric);
+ dumppacket(fd, dir, &t->ift_who, t->ift_packet, t->ift_size);
+ }
+}
+
+void
+dumppacket(fd, dir, source, cp, size)
+ FILE *fd;
+ char *dir;
+ struct sockaddr *source;
+ char *cp;
+ register int size;
+{
+ register struct rip *msg = (struct rip *)cp;
+ register struct netinfo *n;
+ struct sockaddr_ipx *who = (struct sockaddr_ipx *)source;
+
+ if (msg->rip_cmd && ntohs(msg->rip_cmd) < RIPCMD_MAX)
+ fprintf(fd, "%s %s %s#%x", ripcmds[ntohs(msg->rip_cmd)],
+ dir, ipxdp_ntoa(&who->sipx_addr),
+ ntohs(who->sipx_addr.x_port));
+ else {
+ fprintf(fd, "Bad cmd 0x%x %s %s#%x\n", ntohs(msg->rip_cmd),
+ dir, ipxdp_ntoa(&who->sipx_addr),
+ ntohs(who->sipx_addr.x_port));
+ fprintf(fd, "size=%d cp=%x packet=%x\n", size,
+ (u_int)cp, (u_int)packet);
+ return;
+ }
+ switch (ntohs(msg->rip_cmd)) {
+
+ case RIPCMD_REQUEST:
+ case RIPCMD_RESPONSE:
+ fprintf(fd, ":\n");
+ size -= sizeof (u_short);
+ n = msg->rip_nets;
+ for (; size > 0; n++, size -= sizeof (struct netinfo)) {
+ if (size < sizeof (struct netinfo))
+ break;
+ fprintf(fd, "\tnet %s metric %d ticks %d\n",
+ ipxdp_nettoa(n->rip_dst),
+ ntohs(n->rip_metric),
+ ntohs(n->rip_ticks));
+ }
+ break;
+
+ }
+}
+
+void
+dumpsappacket(fd, dir, source, cp, size)
+ FILE *fd;
+ char *dir;
+ struct sockaddr *source;
+ char *cp;
+ register int size;
+{
+ register struct sap_packet *msg = (struct sap_packet *)cp;
+ register struct sap_info *n;
+ struct sockaddr_ipx *who = (struct sockaddr_ipx *)source;
+
+ if (msg->sap_cmd && ntohs(msg->sap_cmd) < SAPCMD_MAX)
+ fprintf(fd, "%s %s %s#%x", sapcmds[ntohs(msg->sap_cmd)],
+ dir, ipxdp_ntoa(&who->sipx_addr),
+ ntohs(who->sipx_addr.x_port));
+ else {
+ fprintf(fd, "Bad cmd 0x%x %s %s#%x\n", ntohs(msg->sap_cmd),
+ dir, ipxdp_ntoa(&who->sipx_addr),
+ ntohs(who->sipx_addr.x_port));
+ fprintf(fd, "size=%d cp=%x packet=%x\n", size,
+ (u_int)cp, (u_int)packet);
+ return;
+ }
+ switch (ntohs(msg->sap_cmd)) {
+
+ case SAP_REQ:
+ case SAP_RESP:
+ case SAP_REQ_NEAR:
+ case SAP_RESP_NEAR:
+ fprintf(fd, ":\n");
+ size -= sizeof (u_short);
+ n = msg->sap;
+ for (; size > 0; n++, size -= sizeof (struct sap_info)) {
+ if (size < sizeof (struct sap_info))
+ break;
+ fprintf(fd, " service %04X %-20.20s "
+ "addr %s.%04X metric %d\n",
+ ntohs(n->ServType),
+ n->ServName,
+ ipxdp_ntoa(&n->ipx),
+ ntohs(n->ipx.x_port),
+ ntohs(n->hops));
+ }
+ break;
+
+ }
+}
+
+void
+dumpsaptable(fd, sh)
+ FILE *fd;
+ struct sap_hash *sh;
+{
+ register struct sap_entry *sap;
+ struct sap_hash *hash;
+ int x = 0;
+
+ fprintf(fd, "------- SAP table dump. -------\n");
+ for (hash = sh; hash < &sh[SAPHASHSIZ]; hash++, x++) {
+ fprintf(fd, "HASH %d\n", x);
+ sap = hash->forw;
+ for (; sap != (struct sap_entry *)hash; sap = sap->forw) {
+ fprintf(fd, " service %04X %-20.20s "
+ "addr %s.%04X %c metric %d\n",
+ ntohs(sap->sap.ServType),
+ sap->sap.ServName,
+ ipxdp_ntoa(&sap->sap.ipx),
+ ntohs(sap->sap.ipx.x_port),
+ (sap->clone ? 'C' : ' '),
+ ntohs(sap->sap.hops));
+ }
+ }
+ fprintf(fd, "\n");
+}
+
+void
+dumpriptable(fd)
+ FILE *fd;
+{
+ register struct rt_entry *rip;
+ struct rthash *hash;
+ int x;
+ struct rthash *rh = nethash;
+
+ fprintf(fd, "------- RIP table dump. -------\n");
+ x = 0;
+ fprintf(fd, "Network table.\n");
+
+ for (hash = rh; hash < &rh[ROUTEHASHSIZ]; hash++, x++) {
+ fprintf(fd, "HASH %d\n", x);
+ rip = hash->rt_forw;
+ for (; rip != (struct rt_entry *)hash; rip = rip->rt_forw) {
+ fprintf(fd, " dest %s\t",
+ ipxdp_ntoa(&satoipx_addr(rip->rt_dst)));
+ fprintf(fd, "%s metric %d, ticks %d\n",
+ ipxdp_ntoa(&satoipx_addr(rip->rt_router)),
+ rip->rt_metric,
+ rip->rt_ticks);
+ }
+ }
+ fprintf(fd, "\n");
+}
+
+union ipx_net_u net;
+
+char *
+ipxdp_nettoa(val)
+union ipx_net val;
+{
+ static char buf[100];
+ net.net_e = val;
+ (void)sprintf(buf, "%lx", ntohl(net.long_e));
+ return (buf);
+}
+
+
+char *
+ipxdp_ntoa(addr)
+struct ipx_addr *addr;
+{
+ static char buf[100];
+
+ (void)sprintf(buf, "%s#%x:%x:%x:%x:%x:%x",
+ ipxdp_nettoa(addr->x_net),
+ addr->x_host.c_host[0], addr->x_host.c_host[1],
+ addr->x_host.c_host[2], addr->x_host.c_host[3],
+ addr->x_host.c_host[4], addr->x_host.c_host[5]);
+
+ return(buf);
+}
diff --git a/usr.sbin/IPXrouted/trace.h b/usr.sbin/IPXrouted/trace.h
new file mode 100644
index 0000000..17fef0f
--- /dev/null
+++ b/usr.sbin/IPXrouted/trace.h
@@ -0,0 +1,138 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Copyright (c) 1995 John Hay. All rights reserved.
+ *
+ * This file includes significant work done at Cornell University by
+ * Bill Nesheim. That work included by permission.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)trace.h 8.1 (Berkeley) 6/5/93
+ *
+ * $Id: trace.h,v 1.5 1997/02/22 16:01:04 peter Exp $
+ */
+
+/*
+ * IPX Routing Information Protocol.
+ */
+
+/*
+ * Trace record format.
+ */
+struct iftrace {
+ time_t ift_stamp; /* time stamp */
+ struct sockaddr ift_who; /* from/to */
+ char *ift_packet; /* pointer to packet */
+ short ift_size; /* size of packet */
+ short ift_metric; /* metric */
+};
+
+/*
+ * Per interface packet tracing buffers. An incoming and
+ * outgoing circular buffer of packets is maintained, per
+ * interface, for debugging. Buffers are dumped whenever
+ * an interface is marked down.
+ */
+struct ifdebug {
+ struct iftrace *ifd_records; /* array of trace records */
+ struct iftrace *ifd_front; /* next empty trace record */
+ int ifd_count; /* number of unprinted records */
+ struct interface *ifd_if; /* for locating stuff */
+};
+
+/*
+ * Packet tracing stuff.
+ */
+int tracepackets; /* watch packets as they go by */
+int tracing; /* on/off */
+FILE *ftrace; /* output trace file */
+
+#define TRACE_ACTION(action, route) { \
+ if (tracing) \
+ traceaction(ftrace, "action", route); \
+ traceactionlog(action, route); \
+ }
+#define TRACE_SAP_ACTION(action, service) { \
+ tracesapactionlog(action, service); \
+ }
+#define TRACE_INPUT(ifp, src, size) { \
+ if (tracing) { \
+ ifp = if_iflookup(src); \
+ if (ifp) \
+ trace(&ifp->int_input, src, \
+ &packet[sizeof(struct ipx)], size, \
+ ntohl(ifp->int_metric)); \
+ } \
+ if (tracepackets && ftrace) \
+ dumppacket(ftrace, "from", src, \
+ &packet[sizeof(struct ipx)], size); \
+ }
+#define TRACE_OUTPUT(ifp, dst, size) { \
+ if (tracing) { \
+ ifp = if_iflookup(dst); \
+ if (ifp) \
+ trace(&ifp->int_output, dst, \
+ &packet[sizeof(struct ipx)], \
+ size, ifp->int_metric); \
+ } \
+ if (tracepackets && ftrace) \
+ dumppacket(ftrace, "to", dst, \
+ &packet[sizeof(struct ipx)], size); \
+ }
+
+#define TRACE_SAP_OUTPUT(ifp, dst, size) { \
+ if (tracing) { \
+ ifp = if_iflookup(dst); \
+ if (ifp) \
+ trace(&ifp->int_output, dst, \
+ &packet[sizeof(struct ipx)], \
+ size, ifp->int_metric); \
+ } \
+ if (tracepackets && ftrace) \
+ dumpsappacket(ftrace, "to", dst, \
+ &packet[sizeof(struct ipx)], size); \
+ }
+
+void traceinit(struct interface *);
+void traceon(char *file);
+void traceoff(void);
+void traceaction(FILE *, char *, struct rt_entry *);
+void traceactionlog(char *, struct rt_entry *);
+void tracesapactionlog(char *action, struct sap_entry *sap);
+void trace(struct ifdebug *, struct sockaddr *, char *, int, int);
+void dumppacket(FILE *, char *, struct sockaddr *, char *, int);
+void dumpsappacket(FILE *, char *, struct sockaddr *, char *, int);
+void dumpsaptable(FILE *fd, struct sap_hash *sh);
+void dumpriptable(FILE *fd);
+
+char *ipxdp_nettoa(union ipx_net);
+char *ipxdp_ntoa(struct ipx_addr *);
+
diff --git a/usr.sbin/Makefile b/usr.sbin/Makefile
new file mode 100644
index 0000000..6a5205b
--- /dev/null
+++ b/usr.sbin/Makefile
@@ -0,0 +1,37 @@
+# From: @(#)Makefile 5.20 (Berkeley) 6/12/93
+# $Id: Makefile,v 1.112 1997/09/19 15:41:43 jmg Exp $
+
+# XXX MISSING: mkproto
+SUBDIR= ac accton adduser amd arp bootparamd cdcontrol chown chroot ckdist \
+ cron crunch ctm dev_mkdb diskpart edquota inetd kernbb keyadmin \
+ keyserv kgmon kvm_mkdb lpr manctl mkdosfs mrouted mtest mtree \
+ named named.reload named.restart natd ndc newsyslog nslookup \
+ pccard pciconf pcvt periodic pkg_install pnpinfo portmap \
+ ppp pppctl pppd pppstats pstat pw pwd_mkdb quot quotaon rarpd \
+ repquota rmt rndcontrol rpc.lockd rpc.statd rpc.yppasswdd rpc.ypxfrd \
+ rpc.ypupdated rwhod sa sendmail sliplogin slstat \
+ spray sysctl syslogd tcpdump timed traceroute trpt tzsetup vipw \
+ vnconfig watch wlconfig wormcontrol xntpd xten ypbind yp_mkdb \
+ yppoll yppush ypset ypserv zic
+
+SUBDIR+=IPXrouted
+
+.if make(clean) || make(cleandir)
+SUBDIR+=apm apmconf bad144 config fdformat fdwrite fdcontrol iostat \
+ kbdcontrol kbdmap lptcontrol mixer moused mptable ncrcontrol \
+ qcamcontrol rtprio sgsc sicontrol spkrtest stallion vidcontrol
+.elif ${MACHINE} == "hp300"
+SUBDIR+=config iostat
+.elif ${MACHINE} == "i386"
+SUBDIR+=apm apmconf bad144 config fdformat fdwrite fdcontrol iostat \
+ kbdcontrol kbdmap lptcontrol mixer moused mptable ncrcontrol \
+ qcamcontrol rtprio sgsc sicontrol spkrtest stallion vidcontrol
+.elif ${MACHINE} == "luna68k"
+SUBDIR+=config iostat
+.elif ${MACHINE} == "mips"
+SUBDIR+=config iostat
+.elif ${MACHINE} == "tahoe"
+SUBDIR+=config iostat
+.endif
+
+.include <bsd.subdir.mk>
diff --git a/usr.sbin/Makefile.inc b/usr.sbin/Makefile.inc
new file mode 100644
index 0000000..fd92864
--- /dev/null
+++ b/usr.sbin/Makefile.inc
@@ -0,0 +1,3 @@
+# @(#)Makefile.inc 8.1 (Berkeley) 6/6/93
+
+BINDIR?= /usr/sbin
diff --git a/usr.sbin/ac/Makefile b/usr.sbin/ac/Makefile
new file mode 100644
index 0000000..e3b47f5
--- /dev/null
+++ b/usr.sbin/ac/Makefile
@@ -0,0 +1,18 @@
+# $Id$
+
+PROG= ac
+MAN8= ac.8
+
+# If "CONSOLE_TTY" is not defined, this program is compatible with the
+# traditional implementation (using SunOS 4.x as the sample traditional
+# implementation). This is the default.
+#
+# If "CONSOLE_TTY" is defined, it must be defined to the appropriate
+# console name, e.g. "vga". Additionally, the various commented-out
+# sections of the man page should be uncommented. This is not the
+# default because of the inability to detect the proper console name
+# easily, especially on m68k systems, which can share binaries.
+#
+#CFLAGS+=-DCONSOLE_TTY=\"vga\"
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/ac/ac.8 b/usr.sbin/ac/ac.8
new file mode 100644
index 0000000..341226a
--- /dev/null
+++ b/usr.sbin/ac/ac.8
@@ -0,0 +1,155 @@
+.\"
+.\" Copyright (c) 1994 Simon J. Gerraty
+.\" Copyright (c) 1994 Christopher G. Demetriou
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by Christopher G. Demetriou.
+.\" 3. The name of the author may not be used to endorse or promote products
+.\" derived from this software without specific prior written permission
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+.\"
+.\" $Id: ac.8,v 1.9 1997/02/22 16:01:10 peter Exp $
+.\"
+.Dd March 15, 1994
+.Dt AC 8
+.Os
+.Sh NAME
+.Nm ac
+.Nd connect time accounting
+.Sh SYNOPSIS
+.Nm ac
+.Op Fl dp
+.\".Op Fl c Ar console
+.Op Fl t Ar tty
+.Op Fl w Ar wtmp
+.Op Ar users ...
+.Sh DESCRIPTION
+If the file
+.Pa /var/log/wtmp
+exists, a record of individual login and logout
+times are written to it by
+.Xr login 1
+and
+.Xr init 8 ,
+respectively.
+.Nm \&Ac
+examines these records and writes the accumulated connect time (in hours)
+for all logins to the standard output.
+.Pp
+The options are as follows:
+.Bl -tag -width indentXXX
+.It Fl d
+Display the connect times in 24 hour chunks.
+.\" .It Fl c Ar console
+.\" Use
+.\" .Ar console
+.\" as the name of the device that local X sessions (ut_host of ":0.0")
+.\" originate from. If any login has been recorded on
+.\" .Ar console
+.\" then these X sessions are ignored unless COMPAT_SUNOS was defined at
+.\" compile time.
+.It Fl p
+Print individual users' totals.
+.It Fl t Ar tty
+Only do accounting logins on certain ttys. The
+.Ar tty
+specification can start with '!' to indicate not this
+.Ar tty
+and end with '*' to indicate all similarly named ttys.
+Multiple
+.Fl t
+flags may be specified.
+.It Fl w Ar wtmp
+Read connect time data from
+.Ar wtmp
+instead of the default file,
+.Pa /var/log/wtmp .
+.It Ar users ...
+Display totals for the given individuals only.
+.El
+.Pp
+If no arguments are given,
+.Nm
+displays the total connect time for all
+accounts with login sessions recorded in
+.Pa wtmp .
+.Pp
+The default
+.Pa wtmp
+file will increase without bound unless it is truncated.
+It is normally truncated by the daily scripts run
+by
+.Xr cron 8 ,
+which rename and rotate the
+.Pa wtmp
+files, keeping a week's worth of data on
+hand. No login or connect time accounting is performed if
+.Pa /var/log/wtmp
+does not exist.
+.Pp
+For example,
+.Bd -literal -offset
+ac -p -t "ttyd*" > modems
+ac -p -t "!ttyd*" > other
+.Ed
+.Pp
+allows times recorded in
+.Pa modems
+to be charged out at a different rate than
+.Pa other .
+.Pp
+The
+.Nm
+utility exits 0 on success, and >0 if a fatal error occurs.
+.Sh FILES
+.Bl -tag -width /var/log/wtmp.[0-7] -compact
+.It Pa /var/log/wtmp
+connect time accounting file
+.It Pa /var/log/wtmp.[0-7]
+rotated files
+.El
+.Sh SEE ALSO
+.Xr login 1 ,
+.Xr utmp 5 ,
+.Xr init 8 ,
+.Xr sa 8
+.\" .Sh NOTES
+.\" If COMPAT_SUNOS is defined
+.\" .Nm ac
+.\" ignores the fact that entries with ut_host of ":0.0" are not real
+.\" login sessions. Normally such entries are ignored except in the case
+.\" of a user being logged in when the
+.\" .Pa wtmp
+.\" file was rotated, in which case a login with ut_host of ":0.0" may
+.\" appear without any preceding console logins.
+.\" If no one is logged in on the console, the user is deemed to have
+.\" logged in on at the earliest time stamp found in
+.\" .Pa wtmp .
+.\" Use of
+.\" .Pa console
+.\" allows
+.\" .Nm ac
+.\" to identify and correctly process a logout for the user. The default
+.\" value for
+.\" .Pa console
+.\" is usually correct at compile time.
diff --git a/usr.sbin/ac/ac.c b/usr.sbin/ac/ac.c
new file mode 100644
index 0000000..f59cf37
--- /dev/null
+++ b/usr.sbin/ac/ac.c
@@ -0,0 +1,561 @@
+/*
+ * Copyright (c) 1994 Christopher G. Demetriou.
+ * @(#)Copyright (c) 1994, Simon J. Gerraty.
+ *
+ * This is free software. It comes with NO WARRANTY.
+ * Permission to use, modify and distribute this source code
+ * is granted subject to the following conditions.
+ * 1/ that the above copyright notice and this notice
+ * are preserved in all copies and that due credit be given
+ * to the author.
+ * 2/ that any changes to this code are clearly commented
+ * as such so that the author does not get blamed for bugs
+ * other than his own.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/file.h>
+#include <sys/time.h>
+#include <err.h>
+#include <errno.h>
+#include <locale.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <utmp.h>
+
+/*
+ * this is for our list of currently logged in sessions
+ */
+struct utmp_list {
+ struct utmp_list *next;
+ struct utmp usr;
+};
+
+/*
+ * this is for our list of users that are accumulating time.
+ */
+struct user_list {
+ struct user_list *next;
+ char name[UT_NAMESIZE+1];
+ time_t secs;
+};
+
+/*
+ * this is for chosing whether to ignore a login
+ */
+struct tty_list {
+ struct tty_list *next;
+ char name[UT_LINESIZE+3];
+ int len;
+ int ret;
+};
+
+/*
+ * globals - yes yuk
+ */
+#ifdef CONSOLE_TTY
+static char *Console = CONSOLE_TTY;
+#endif
+static time_t Total = 0;
+static time_t FirstTime = 0;
+static int Flags = 0;
+static struct user_list *Users = NULL;
+static struct tty_list *Ttys = NULL;
+
+#define NEW(type) (type *)malloc(sizeof (type))
+
+#define AC_W 1 /* not _PATH_WTMP */
+#define AC_D 2 /* daily totals (ignore -p) */
+#define AC_P 4 /* per-user totals */
+#define AC_U 8 /* specified users only */
+#define AC_T 16 /* specified ttys only */
+
+#ifdef DEBUG
+static int Debug = 0;
+#endif
+
+int main __P((int, char **));
+int ac __P((FILE *));
+struct tty_list *add_tty __P((char *));
+int do_tty __P((char *));
+FILE *file __P((char *));
+struct utmp_list *log_in __P((struct utmp_list *, struct utmp *));
+struct utmp_list *log_out __P((struct utmp_list *, struct utmp *));
+int on_console __P((struct utmp_list *));
+void show __P((char *, time_t));
+void show_today __P((struct user_list *, struct utmp_list *,
+ time_t));
+void show_users __P((struct user_list *));
+struct user_list *update_user __P((struct user_list *, char *, time_t));
+void usage __P((void));
+
+/*
+ * open wtmp or die
+ */
+FILE *
+file(name)
+ char *name;
+{
+ FILE *fp;
+
+ if ((fp = fopen(name, "r")) == NULL)
+ err(1, "%s", name);
+ /* in case we want to discriminate */
+ if (strcmp(_PATH_WTMP, name))
+ Flags |= AC_W;
+ return fp;
+}
+
+struct tty_list *
+add_tty(name)
+ char *name;
+{
+ struct tty_list *tp;
+ register char *rcp;
+
+ Flags |= AC_T;
+
+ if ((tp = NEW(struct tty_list)) == NULL)
+ err(1, "malloc");
+ tp->len = 0; /* full match */
+ tp->ret = 1; /* do if match */
+ if (*name == '!') { /* don't do if match */
+ tp->ret = 0;
+ name++;
+ }
+ (void)strncpy(tp->name, name, sizeof (tp->name) - 1);
+ tp->name[sizeof (tp->name) - 1] = '\0';
+ if ((rcp = strchr(tp->name, '*')) != NULL) { /* wild card */
+ *rcp = '\0';
+ tp->len = strlen(tp->name); /* match len bytes only */
+ }
+ tp->next = Ttys;
+ Ttys = tp;
+ return Ttys;
+}
+
+/*
+ * should we process the named tty?
+ */
+int
+do_tty(name)
+ char *name;
+{
+ struct tty_list *tp;
+ int def_ret = 0;
+
+ for (tp = Ttys; tp != NULL; tp = tp->next) {
+ if (tp->ret == 0) /* specific don't */
+ def_ret = 1; /* default do */
+ if (tp->len != 0) {
+ if (strncmp(name, tp->name, tp->len) == 0)
+ return tp->ret;
+ } else {
+ if (strncmp(name, tp->name, sizeof (tp->name)) == 0)
+ return tp->ret;
+ }
+ }
+ return def_ret;
+}
+
+#ifdef CONSOLE_TTY
+/*
+ * is someone logged in on Console?
+ */
+int
+on_console(head)
+ struct utmp_list *head;
+{
+ struct utmp_list *up;
+
+ for (up = head; up; up = up->next) {
+ if (strncmp(up->usr.ut_line, Console,
+ sizeof (up->usr.ut_line)) == 0)
+ return 1;
+ }
+ return 0;
+}
+#endif
+
+/*
+ * update user's login time
+ */
+struct user_list *
+update_user(head, name, secs)
+ struct user_list *head;
+ char *name;
+ time_t secs;
+{
+ struct user_list *up;
+
+ for (up = head; up != NULL; up = up->next) {
+ if (strncmp(up->name, name, UT_NAMESIZE) == 0) {
+ up->secs += secs;
+ Total += secs;
+ return head;
+ }
+ }
+ /*
+ * not found so add new user unless specified users only
+ */
+ if (Flags & AC_U)
+ return head;
+
+ if ((up = NEW(struct user_list)) == NULL)
+ err(1, "malloc");
+ up->next = head;
+ (void)strncpy(up->name, name, sizeof (up->name) - 1);
+ up->name[sizeof (up->name) - 1] = '\0'; /* paranoid! */
+ up->secs = secs;
+ Total += secs;
+ return up;
+}
+
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ FILE *fp;
+ int c;
+
+ (void) setlocale(LC_TIME, "");
+
+ fp = NULL;
+ while ((c = getopt(argc, argv, "Dc:dpt:w:")) != -1) {
+ switch (c) {
+#ifdef DEBUG
+ case 'D':
+ Debug++;
+ break;
+#endif
+ case 'c':
+#ifdef CONSOLE_TTY
+ Console = optarg;
+#else
+ usage(); /* XXX */
+#endif
+ break;
+ case 'd':
+ Flags |= AC_D;
+ break;
+ case 'p':
+ Flags |= AC_P;
+ break;
+ case 't': /* only do specified ttys */
+ add_tty(optarg);
+ break;
+ case 'w':
+ fp = file(optarg);
+ break;
+ case '?':
+ default:
+ usage();
+ break;
+ }
+ }
+ if (optind < argc) {
+ /*
+ * initialize user list
+ */
+ for (; optind < argc; optind++) {
+ Users = update_user(Users, argv[optind], 0L);
+ }
+ Flags |= AC_U; /* freeze user list */
+ }
+ if (Flags & AC_D)
+ Flags &= ~AC_P;
+ if (fp == NULL) {
+ /*
+ * if _PATH_WTMP does not exist, exit quietly
+ */
+ if (access(_PATH_WTMP, 0) != 0 && errno == ENOENT)
+ return 0;
+
+ fp = file(_PATH_WTMP);
+ }
+ ac(fp);
+
+ return 0;
+}
+
+/*
+ * print login time in decimal hours
+ */
+void
+show(name, secs)
+ char *name;
+ time_t secs;
+{
+ (void)printf("\t%-*s %8.2f\n", UT_NAMESIZE, name,
+ ((double)secs / 3600));
+}
+
+void
+show_users(list)
+ struct user_list *list;
+{
+ struct user_list *lp;
+
+ for (lp = list; lp; lp = lp->next)
+ show(lp->name, lp->secs);
+}
+
+/*
+ * print total login time for 24hr period in decimal hours
+ */
+void
+show_today(users, logins, secs)
+ struct user_list *users;
+ struct utmp_list *logins;
+ time_t secs;
+{
+ struct user_list *up;
+ struct utmp_list *lp;
+ char date[64];
+ time_t yesterday = secs - 1;
+
+ (void)strftime(date, sizeof (date), "%b %e total",
+ localtime(&yesterday));
+
+ /* restore the missing second */
+ yesterday++;
+
+ for (lp = logins; lp != NULL; lp = lp->next) {
+ secs = yesterday - lp->usr.ut_time;
+ Users = update_user(Users, lp->usr.ut_name, secs);
+ lp->usr.ut_time = yesterday; /* as if they just logged in */
+ }
+ secs = 0;
+ for (up = users; up != NULL; up = up->next) {
+ secs += up->secs;
+ up->secs = 0; /* for next day */
+ }
+ if (secs)
+ (void)printf("%s %11.2f\n", date, ((double)secs / 3600));
+}
+
+/*
+ * log a user out and update their times.
+ * if ut_line is "~", we log all users out as the system has
+ * been shut down.
+ */
+struct utmp_list *
+log_out(head, up)
+ struct utmp_list *head;
+ struct utmp *up;
+{
+ struct utmp_list *lp, *lp2, *tlp;
+ time_t secs;
+
+ for (lp = head, lp2 = NULL; lp != NULL; )
+ if (*up->ut_line == '~' || strncmp(lp->usr.ut_line, up->ut_line,
+ sizeof (up->ut_line)) == 0) {
+ secs = up->ut_time - lp->usr.ut_time;
+ Users = update_user(Users, lp->usr.ut_name, secs);
+#ifdef DEBUG
+ if (Debug)
+ printf("%-.*s %-.*s: %-.*s logged out (%2d:%02d:%02d)\n",
+ 19, ctime(&up->ut_time),
+ sizeof (lp->usr.ut_line), lp->usr.ut_line,
+ sizeof (lp->usr.ut_name), lp->usr.ut_name,
+ secs / 3600, (secs % 3600) / 60, secs % 60);
+#endif
+ /*
+ * now lose it
+ */
+ tlp = lp;
+ lp = lp->next;
+ if (tlp == head)
+ head = lp;
+ else if (lp2 != NULL)
+ lp2->next = lp;
+ free(tlp);
+ } else {
+ lp2 = lp;
+ lp = lp->next;
+ }
+ return head;
+}
+
+
+/*
+ * if do_tty says ok, login a user
+ */
+struct utmp_list *
+log_in(head, up)
+ struct utmp_list *head;
+ struct utmp *up;
+{
+ struct utmp_list *lp;
+
+ /*
+ * this could be a login. if we're not dealing with
+ * the console name, say it is.
+ *
+ * If we are, and if ut_host==":0.0" we know that it
+ * isn't a real login. _But_ if we have not yet recorded
+ * someone being logged in on Console - due to the wtmp
+ * file starting after they logged in, we'll pretend they
+ * logged in, at the start of the wtmp file.
+ */
+
+#ifdef CONSOLE_TTY
+ if (up->ut_host[0] == ':') {
+ /*
+ * SunOS 4.0.2 does not treat ":0.0" as special but we
+ * do.
+ */
+ if (on_console(head))
+ return head;
+ /*
+ * ok, no recorded login, so they were here when wtmp
+ * started! Adjust ut_time!
+ */
+ up->ut_time = FirstTime;
+ /*
+ * this allows us to pick the right logout
+ */
+ (void)strncpy(up->ut_line, Console, sizeof (up->ut_line) - 1);
+ up->ut_line[sizeof (up->ut_line) - 1] = '\0'; /* paranoid! */
+ }
+#endif
+ /*
+ * If we are doing specified ttys only, we ignore
+ * anything else.
+ */
+ if (Flags & AC_T)
+ if (!do_tty(up->ut_line))
+ return head;
+
+ /*
+ * go ahead and log them in
+ */
+ if ((lp = NEW(struct utmp_list)) == NULL)
+ err(1, "malloc");
+ lp->next = head;
+ head = lp;
+ memmove((char *)&lp->usr, (char *)up, sizeof (struct utmp));
+#ifdef DEBUG
+ if (Debug) {
+ printf("%-.*s %-.*s: %-.*s logged in", 19,
+ ctime(&lp->usr.ut_time), sizeof (up->ut_line),
+ up->ut_line, sizeof (up->ut_name), up->ut_name);
+ if (*up->ut_host)
+ printf(" (%-.*s)", sizeof (up->ut_host), up->ut_host);
+ putchar('\n');
+ }
+#endif
+ return head;
+}
+
+int
+ac(fp)
+ FILE *fp;
+{
+ struct utmp_list *lp, *head = NULL;
+ struct utmp usr;
+ struct tm *ltm;
+ time_t secs;
+ int day = -1;
+
+ while (fread((char *)&usr, sizeof(usr), 1, fp) == 1) {
+ if (!FirstTime)
+ FirstTime = usr.ut_time;
+ if (Flags & AC_D) {
+ ltm = localtime(&usr.ut_time);
+ if (day >= 0 && day != ltm->tm_yday) {
+ day = ltm->tm_yday;
+ /*
+ * print yesterday's total
+ */
+ secs = usr.ut_time;
+ secs -= ltm->tm_sec;
+ secs -= 60 * ltm->tm_min;
+ secs -= 3600 * ltm->tm_hour;
+ show_today(Users, head, secs);
+ } else
+ day = ltm->tm_yday;
+ }
+ switch(*usr.ut_line) {
+ case '|':
+ secs = usr.ut_time;
+ break;
+ case '{':
+ secs -= usr.ut_time;
+ /*
+ * adjust time for those logged in
+ */
+ for (lp = head; lp != NULL; lp = lp->next)
+ lp->usr.ut_time -= secs;
+ break;
+ case '~': /* reboot or shutdown */
+ head = log_out(head, &usr);
+ FirstTime = usr.ut_time; /* shouldn't be needed */
+ break;
+ default:
+ /*
+ * if they came in on tty[p-y]*, 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("pqrstuvwxy", usr.ut_line[3]) == 0 ||
+ *usr.ut_host != '\0')
+ head = log_in(head, &usr);
+ } else
+ head = log_out(head, &usr);
+ break;
+ }
+ }
+ (void)fclose(fp);
+ usr.ut_time = time((time_t *)0);
+ (void)strcpy(usr.ut_line, "~");
+
+ if (Flags & AC_D) {
+ ltm = localtime(&usr.ut_time);
+ if (day >= 0 && day != ltm->tm_yday) {
+ /*
+ * print yesterday's total
+ */
+ secs = usr.ut_time;
+ secs -= ltm->tm_sec;
+ secs -= 60 * ltm->tm_min;
+ secs -= 3600 * ltm->tm_hour;
+ show_today(Users, head, secs);
+ }
+ }
+ /*
+ * anyone still logged in gets time up to now
+ */
+ head = log_out(head, &usr);
+
+ if (Flags & AC_D)
+ show_today(Users, head, time((time_t *)0));
+ else {
+ if (Flags & AC_P)
+ show_users(Users);
+ show("total", Total);
+ }
+ return 0;
+}
+
+void
+usage()
+{
+ (void)fprintf(stderr,
+#ifdef CONSOLE_TTY
+ "ac [-dp] [-c console] [-t tty] [-w wtmp] [users ...]\n");
+#else
+ "ac [-dp] [-t tty] [-w wtmp] [users ...]\n");
+#endif
+ exit(1);
+}
diff --git a/usr.sbin/accton/Makefile b/usr.sbin/accton/Makefile
new file mode 100644
index 0000000..8e3f336
--- /dev/null
+++ b/usr.sbin/accton/Makefile
@@ -0,0 +1,6 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= accton
+MAN8= accton.8
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/accton/accton.8 b/usr.sbin/accton/accton.8
new file mode 100644
index 0000000..1b5ad28
--- /dev/null
+++ b/usr.sbin/accton/accton.8
@@ -0,0 +1,34 @@
+.Dd May 21, 1993
+.Dt ACCTON 8
+.Os
+.Sh NAME
+.Nm accton
+.Nd enable/disable system accounting
+.Sh SYNOPSIS
+.Nm accton
+.Op Ar acctfile
+.Sh DESCRIPTION
+The
+.Nm
+command is used
+for switching system accounting on or off.
+If called with the argument
+.Ar acctfile ,
+system accounting is enabled and a record of
+every process that is started by the
+.Xr execve 2
+system call and then later exits the system is stored in
+.Ar acctfile .
+The
+.Xr sa 8
+command may be used to examining the accounting records.
+If no arguments are given, system account is disabled.
+.Sh FILES
+.Bl -tag -width /var/account/acct
+.It Pa /var/account/acct
+Default accounting file.
+.El
+.Sh SEE ALSO
+.Xr lastcomm 1 ,
+.Xr acct 2 ,
+.Xr sa 8
diff --git a/usr.sbin/accton/accton.c b/usr.sbin/accton/accton.c
new file mode 100644
index 0000000..af2fa89
--- /dev/null
+++ b/usr.sbin/accton/accton.c
@@ -0,0 +1,93 @@
+/*
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 1988, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)accton.c 8.1 (Berkeley) 6/6/93";
+#endif
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+static void usage __P((void));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int ch;
+
+ while ((ch = getopt(argc, argv, "")) != -1)
+ switch(ch) {
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ switch(argc) {
+ case 0:
+ if (acct(NULL))
+ err(1, NULL);
+ break;
+ case 1:
+ if (acct(*argv))
+ err(1, "%s", *argv);
+ break;
+ default:
+ usage();
+ }
+ exit(0);
+}
+
+static void
+usage()
+{
+ (void)fprintf(stderr, "usage: accton [file]\n");
+ exit(1);
+}
diff --git a/usr.sbin/adduser/Makefile b/usr.sbin/adduser/Makefile
new file mode 100644
index 0000000..c7908c9
--- /dev/null
+++ b/usr.sbin/adduser/Makefile
@@ -0,0 +1,12 @@
+# $Id: Makefile,v 1.11 1997/02/22 16:01:14 peter Exp $
+
+SCRIPTS= adduser.perl rmuser.perl
+MAN8= adduser.8 rmuser.8
+
+beforeinstall:
+.for script in ${SCRIPTS}
+ ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \
+ ${.CURDIR}/${script} ${DESTDIR}${BINDIR}/${script:R}
+.endfor
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/adduser/adduser.8 b/usr.sbin/adduser/adduser.8
new file mode 100644
index 0000000..dc4a839
--- /dev/null
+++ b/usr.sbin/adduser/adduser.8
@@ -0,0 +1,284 @@
+.\" Copyright (c) 1995-1996 Wolfram Schneider <wosch@FreeBSD.org>. Berlin.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $Id: adduser.8,v 1.21 1997/06/23 04:52:07 steve Exp $
+.\"
+.Dd January 9, 1995
+.Dt ADDUSER 8
+.Os FreeBSD 2.1
+.Sh NAME
+.Nm adduser
+.Nd command for adding new users
+.Sh SYNOPSIS
+.Nm adduser
+.Oo
+.Fl batch Ar username
+.Op Ar group Ns , Ns Op Ar group,...
+.Op Ar class
+.Op Ar fullname
+.Op Ar password
+.Oc
+.br
+.Op Fl check_only
+.br
+.Op Fl class Ar login_class
+.br
+.Op Fl config_create
+.br
+.Op Fl dotdir Ar dotdir
+.br
+.Op Fl group Ar login_group
+.br
+.Op Fl h | help
+.br
+.Op Fl home Ar home
+.br
+.Op Fl message Ar message_file
+.br
+.Op Fl noconfig
+.br
+.Op Fl shell Ar shell
+.br
+.Op Fl s | silent | q | quiet
+.br
+.Op Fl uid Ar uid_start
+.br
+.Op Fl v | verbose
+.Sh DESCRIPTION
+.Nm Adduser
+is a simple program for adding new users. Adduser checks
+the passwd, group and shell databases. It creates passwd/group entries,
+.Ev HOME
+directory, dotfiles and sends the new user a welcome message.
+.Sh RESTRICTIONS
+.Bl -tag -width Ds -compact
+.It Sy username
+Login name. May contain only lowercase characters or digits. Maximum length
+is 16 characters (see
+.Xr setlogin 2
+BUGS section).
+The reasons for this limit are "Historical".
+Given that people have traditionally wanted to break this
+limit for aesthetic reasons, it's never been of great importance to break
+such a basic fundamental parameter in UNIX.
+You can change
+.Dv UT_NAMESIZE
+in
+.Pa /usr/include/utmp.h
+and recompile the
+world; people have done this and it works, but you will have problems
+with any precompiled programs, or source that assumes the 8-character
+name limit and NIS. The NIS protocol mandates an 8-character username.
+If you need a longer login name for e-mail addresses,
+you can define an alias in
+.Pa /etc/aliases .
+.It Sy fullname
+Firstname and surname.
+The
+.Ql Pa \:
+character is not allowed.
+.It Sy shell
+Only valid shells from the shell database or sliplogin and pppd
+.It Sy uid
+Automatically generated or your choice, must be less than 32000.
+.It Sy gid/login group
+Your choice or automatically generated.
+.It Sy password
+If not empty, password is encoded with
+.Xr crypt 3 .
+.El
+.Sh UNIQUE GROUPS
+Perhaps you're missing what
+.Em can
+be done with this scheme that falls apart
+with most other schemes. With each user in his/her own group the user can
+safely run with a umask of 002 and have files created in their home directory
+and not worry about others being able to read them.
+.Pp
+For a shared area you create a separate uid/gid (like cvs or ncvs on freefall),
+you place each person that should be able to access this area into that new
+group.
+.Pp
+This model of uid/gid administration allows far greater flexibility than lumping
+users into groups and having to muck with the umask when working in a shared
+area.
+.Pp
+I have been using this model for almost 10 years and found that it works
+for most situations, and has never gotten in the way. (Rod Grimes)
+.Sh CONFIGURATION
+.Bl -enum
+.It
+Read internal variables.
+.It
+Read configuration file (/etc/adduser.conf).
+.It
+Parse command line options.
+.El
+.Sh OPTIONS
+.Bl -tag -width Ds
+.It Sy -batch username [group[,group]...] [class] [fullname] [password]
+Batch mode.
+.It Sy -check_only
+Check /etc/passwd, /etc/group, /etc/shells and exit.
+.It Sy -class Ar login_class
+Set default login class.
+.It Sy -create_config
+Create new configuration and message file and exit.
+.It Sy -dotdir Ar directory
+Copy files from
+.Ar directory
+into the
+.Ev HOME
+directory of new users,
+.Ql Pa dot.foo
+will be renamed to
+.Ql Pa .foo .
+Don't copy files if
+.Ar directory
+specified is equal to
+.Ar no .
+For security make all files writable and readable for owner,
+don't allow group or world to write files and allow only owner
+to read/execute/write
+.Pa .rhost ,
+.Pa .Xauthority ,
+.Pa .kermrc ,
+.Pa .netrc ,
+.Pa Mail ,
+.Pa prv ,
+.Pa iscreen ,
+.Pa term .
+.It Sy -group Ar login_group
+Login group.
+.Ar USER
+means that the username is to be used as login group.
+.It Sy -help,-h,-?
+Print a summary of options and exit.
+.It Sy -home Ar partition
+Default home partition where all users located.
+.It Sy -message Ar file
+Send new users a welcome message from
+.Ar file .
+Specifying a value of
+.Ar no
+for
+.Ar file
+causes no message to be sent to new users.
+.It Sy -noconfig
+Do not read the default configuration file.
+.It Sy -shell Ar shell
+Default shell for new users.
+.It Sy -silent,-s,-quiet,-q
+Few warnings, questions, bug reports.
+.It Sy -uid Ar uid
+Use uid's from
+.Ar uid
+on up.
+.It Sy -verbose,-v
+Many warnings, questions. Recommended for novice users.
+.Sh FORMATS
+.Bl -tag -width Ds -compact
+.Ql Pa #
+is a comment.
+.It Sy configuration file
+.Nm Adduser
+reads and writes this file.
+See
+.Pa /etc/adduser.conf
+for more details.
+.It Sy message file
+Eval variables in this file. See
+.Pa /etc/adduser.message
+for more
+details.
+.El
+.Sh EXAMPLES
+.Pp
+$ adduser
+.Pp
+Start adduser in interactive mode.
+.Pp
+$ adduser -batch baerenklau guest,staff,baer '' 'Teddy II' qwerty7
+.Pp
+Create user 'baerenklau' and login group 'baerenklau'. Invite user
+baerenklau into groups guest, staff and baer. Use default login class.
+Realname (fullname)
+is 'Teddy II'. Password is 'qwerty7' (don't use such passwords!). Create
+.Ev HOME
+directory
+.Pa /home/baerenklau
+and copy all files and directories
+from
+.Pa /usr/share/skel
+to
+.Pa /home/baerenklau .
+Send user baerenklau
+a welcome message.
+.Pp
+$ adduser -uid 5000 -group guest -message no -batch vehlefanz
+.Pp
+Create user 'vehlefanz'. Login group is guest. Uid next available uid
+after 5000, for instance 5007. No other groups, no realname, no password.
+Do not send a welcome message.
+.Sh FILES
+.Bl -tag -width /etc/master.passwdxx -compact
+.It Pa /etc/master.passwd
+user database
+.It Pa /etc/group
+group database
+.It Pa /etc/shells
+shell database
+.It Pa /etc/login.conf
+login classes database
+.It Pa /etc/adduser.conf
+configuration file for adduser
+.It Pa /etc/adduser.message
+message file for adduser
+.It Pa /usr/share/skel
+skeletal login directory
+.It Pa /var/log/adduser
+logfile for adduser
+.El
+.Sh SEE ALSO
+.Xr chpass 1 ,
+.Xr finger 1 ,
+.Xr passwd 1 ,
+.Xr setlogin 2 ,
+.Xr yp 4 ,
+.Xr aliases 5 ,
+.Xr group 5 ,
+.Xr login.conf 5 ,
+.Xr passwd 5 ,
+.Xr shells 5 ,
+.Xr addgroup 8 ,
+.Xr pwd_mkdb 8 ,
+.Xr rmgroup 8 ,
+.Xr rmuser 8 ,
+.Xr vipw 8
+.\" .Sh BUGS
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Fx 2.1 .
diff --git a/usr.sbin/adduser/adduser.perl b/usr.sbin/adduser/adduser.perl
new file mode 100644
index 0000000..56482b9
--- /dev/null
+++ b/usr.sbin/adduser/adduser.perl
@@ -0,0 +1,1423 @@
+#!/usr/bin/perl
+#
+# Copyright (c) 1995-1996 Wolfram Schneider <wosch@FreeBSD.org>. Berlin.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# $Id: adduser.perl,v 1.36 1997/09/20 18:26:22 wosch Exp $
+
+
+# read variables
+sub variables {
+ $verbose = 1; # verbose = [0-2]
+ $defaultpasswd = "yes"; # use password for new users
+ $dotdir = "/usr/share/skel"; # copy dotfiles from this dir
+ $dotdir_bak = $dotdir;
+ $send_message = "/etc/adduser.message"; # send message to new user
+ $send_message_bak = '/etc/adduser.message';
+ $config = "/etc/adduser.conf"; # config file for adduser
+ $config_read = 1; # read config file
+ $logfile = "/var/log/adduser"; # logfile
+ $home = "/home"; # default HOME
+ $etc_shells = "/etc/shells";
+ $etc_passwd = "/etc/master.passwd";
+ $group = "/etc/group";
+ $pwd_mkdb = "pwd_mkdb -p"; # program for building passwd database
+
+
+ # List of directories where shells located
+ @path = ('/bin', '/usr/bin', '/usr/local/bin');
+ # common shells, first element has higher priority
+ @shellpref = ('csh', 'sh', 'bash', 'tcsh', 'ksh');
+
+ $defaultshell = 'sh'; # defaultshell if not empty
+ $group_uniq = 'USER';
+ $defaultgroup = $group_uniq;# login groupname, $group_uniq means username
+ $defaultclass = '';
+
+ $uid_start = 1000; # new users get this uid
+ $uid_end = 32000; # max. uid
+
+ # global variables
+ # passwd
+ $username = ''; # $username{username} = uid
+ $uid = ''; # $uid{uid} = username
+ $pwgid = ''; # $pwgid{pwgid} = username; gid from passwd db
+
+ $password = ''; # password for new users
+
+ # group
+ $groupname =''; # $groupname{groupname} = gid
+ $groupmembers = ''; # $groupmembers{gid} = members of group/kommalist
+ $gid = ''; # $gid{gid} = groupname; gid form group db
+
+ # shell
+ $shell = ''; # $shell{`basename sh`} = sh
+
+ umask 022; # don't give login group write access
+
+ $ENV{'PATH'} = "/sbin:/bin:/usr/sbin:/usr/bin";
+ @passwd_backup = '';
+ @group_backup = '';
+ @message_buffer = '';
+ @user_variable_list = ''; # user variables in /etc/adduser.conf
+ $do_not_delete = '## DO NOT DELETE THIS LINE!';
+}
+
+# read shell database, see also: shells(5)
+sub shells_read {
+ local($sh);
+ local($err) = 0;
+
+ print "Check $etc_shells\n" if $verbose;
+ open(S, $etc_shells) || die "$etc_shells:$!\n";
+
+ while(<S>) {
+ if (/^\s*\//) {
+ s/^\s*//; s/\s+.*//; # chop
+ $sh = $_;
+ if (-x $sh) {
+ $shell{&basename($sh)} = $sh;
+ } else {
+ warn "Shell: $sh not executable!\n";
+ $err++;
+ }
+ }
+ }
+
+ # Allow /nonexistent and /bin/date as a valid shell for system utils
+ push(@list, "/nonexistent");
+ push(@shellpref, "no") if !grep(/^no$/, @shellpref);
+ $shell{"no"} = "/nonexistent";
+
+ push(@list, "/bin/date");
+ push(@shellpref, "date") if !grep(/^date$/, @shellpref);
+ $shell{"date"} = "/bin/date";
+
+ return $err;
+}
+
+# add new shells if possible
+sub shells_add {
+ local($sh,$dir,@list);
+
+ return 1 unless $verbose;
+
+ foreach $sh (@shellpref) {
+ # all known shells
+ if (!$shell{$sh}) {
+ # shell $sh is not defined as login shell
+ foreach $dir (@path) {
+ if (-x "$dir/$sh") {
+ # found shell
+ if (&confirm_yn("Found shell: $dir/$sh. Add to $etc_shells?", "yes")) {
+ push(@list, "$dir/$sh");
+ push(@shellpref, "$sh");
+ $shell{&basename("$dir/$sh")} = "$dir/$sh";
+ $changes++;
+ }
+ }
+ }
+ }
+ }
+ &append_file($etc_shells, @list) if $#list >= 0;
+}
+
+# choose your favourite shell and return the shell
+sub shell_default {
+ local($e,$i,$new_shell);
+ local($sh);
+
+ $sh = &shell_default_valid($defaultshell);
+ return $sh unless $verbose;
+
+ $new_shell = &confirm_list("Enter your default shell:", 0,
+ $sh, sort(keys %shell));
+ print "Your default shell is: $new_shell -> $shell{$new_shell}\n";
+ $changes++ if $new_shell ne $sh;
+ return $new_shell;
+}
+
+sub shell_default_valid {
+ local($sh) = @_;
+ local($s,$e);
+
+ return $sh if $shell{$sh};
+
+ foreach $e (@shellpref) {
+ $s = $e;
+ last if defined($shell{$s});
+ }
+ $s = "sh" unless $s;
+ warn "Shell ``$sh'' is undefined, use ``$s''\n";
+ return $s;
+}
+
+# return default home partition (f.e. "/home")
+# create base directory if nesseccary
+sub home_partition {
+ local($home) = @_;
+ $home = &stripdir($home);
+ local($h) = $home;
+
+ return $h if !$verbose && $h eq &home_partition_valid($h);
+
+ while(1) {
+ $h = &confirm_list("Enter your default HOME partition:", 1, $home, "");
+ $h = &stripdir($h);
+ last if $h eq &home_partition_valid($h);
+ }
+
+ $changes++ if $h ne $home;
+ return $h;
+}
+
+sub home_partition_valid {
+ local($h) = @_;
+
+ $h = &stripdir($h);
+ # all right (I hope)
+ return $h if $h =~ "^/" && -e $h && -w $h && (-d $h || -l $h);
+
+ # Errors or todo
+ if ($h !~ "^/") {
+ warn "Please use absolute path for home: ``$h''.\a\n";
+ return 0;
+ }
+
+ if (-e $h) {
+ warn "$h exists, but is not a directory or symlink!\n"
+ unless -d $h || -l $h;
+ warn "$h is not writable!\n"
+ unless -w $h;
+ return 0;
+ } else {
+ # create home partition
+ return $h if &mkdir_home($h);
+ }
+ return 0;
+}
+
+# check for valid passwddb
+sub passwd_check {
+ system("$pwd_mkdb -c $etc_passwd");
+ die "\nInvalid $etc_passwd - cannot add any users!\n" if $?;
+}
+
+# read /etc/passwd
+sub passwd_read {
+ local($p_username, $pw, $p_uid, $p_gid, $sh, %shlist);
+
+ print "Check $etc_passwd\n" if $verbose;
+ open(P, "$etc_passwd") || die "$passwd: $!\n";
+
+ while(<P>) {
+ chop;
+ push(@passwd_backup, $_);
+ # ignore comments
+ next if /^\s*$/;
+ next if /^\s*#/;
+
+ ($p_username, $pw, $p_uid, $p_gid, $sh) = (split(/:/, $_))[0..3,9];
+
+ print "$p_username already exists with uid: $username{$p_username}!\n"
+ if $username{$p_username} && $verbose;
+ $username{$p_username} = $p_uid;
+ print "User $p_username: uid $p_uid exists twice: $uid{$p_uid}\n"
+ if $uid{$p_uid} && $verbose && $p_uid; # don't warn for uid 0
+ print "User $p_username: illegal shell: ``$sh''\n"
+ if ($verbose && $sh &&
+ !$shell{&basename($sh)} &&
+ $p_username !~ /^(news|xten|bin|nobody|uucp)$/ &&
+ $sh !~ /\/(pppd|sliplogin|nologin|nonexistent)$/);
+ $uid{$p_uid} = $p_username;
+ $pwgid{$p_gid} = $p_username;
+ }
+ close P;
+}
+
+# read /etc/group
+sub group_read {
+ local($g_groupname,$pw,$g_gid, $memb);
+
+ print "Check $group\n" if $verbose;
+ open(G, "$group") || die "$group: $!\n";
+ while(<G>) {
+ chop;
+ push(@group_backup, $_);
+ # ignore comments
+ next if /^\s*$/;
+ next if /^\s*#/;
+
+ ($g_groupname, $pw, $g_gid, $memb) = (split(/:/, $_))[0..3];
+
+ $groupmembers{$g_gid} = $memb;
+ warn "Groupname exists twice: $g_groupname:$g_gid -> $g_groupname:$groupname{$g_groupname}\n"
+ if $groupname{$g_groupname} && $verbose;
+ $groupname{$g_groupname} = $g_gid;
+ warn "Groupid exists twice: $g_groupname:$g_gid -> $gid{$g_gid}:$g_gid\n"
+ if $gid{$g_gid} && $verbose;
+ $gid{$g_gid} = $g_groupname;
+ }
+ close G;
+}
+
+# check gids /etc/passwd <-> /etc/group
+sub group_check {
+ local($c_gid, $c_username, @list);
+
+ foreach $c_gid (keys %pwgid) {
+ if (!$gid{$c_gid}) {
+ $c_username = $pwgid{$c_gid};
+ warn "User ``$c_username'' has gid $c_gid but a group with this " .
+ "gid does not exist.\n" if $verbose;
+ }
+ }
+}
+
+#
+# main loop for creating new users
+#
+
+# return username
+sub new_users_name {
+ local($name);
+
+ while(1) {
+ $name = &confirm_list("Enter username", 1, "a-z0-9_-", "");
+ if (length($name) > 16) {
+ warn "Username is longer than 16 chars\a\n";
+ next;
+ }
+ last if (&new_users_name_valid($name) eq $name);
+ }
+ return $name;
+}
+
+sub new_users_name_valid {
+ local($name) = @_;
+
+ if ($name !~ /^[a-z0-9_][a-z0-9_\-]*$/ || $name eq "a-z0-9_-") {
+ warn "Wrong username. " .
+ "Please use only lowercase characters or digits\a\n";
+ return 0;
+ } elsif ($username{$name}) {
+ warn "Username ``$name'' already exists!\a\n"; return 0;
+ }
+ return $name;
+}
+
+# return full name
+sub new_users_fullname {
+ local($name) = @_;
+ local($fullname);
+
+ while(1) {
+ $fullname = &confirm_list("Enter full name", 1, "", "");
+ last if $fullname eq &new_users_fullname_valid($fullname);
+ }
+ $fullname = $name unless $fullname;
+ return $fullname;
+}
+
+sub new_users_fullname_valid {
+ local($fullname) = @_;
+
+ return $fullname if $fullname !~ /:/;
+
+ warn "``:'' is not allowed!\a\n";
+ return 0;
+}
+
+# return shell (full path) for user
+sub new_users_shell {
+ local($sh);
+
+ $sh = &confirm_list("Enter shell", 0, $defaultshell, keys %shell);
+ return $shell{$sh};
+}
+
+# return 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 login group
+sub new_users_grplogin_batch {
+ local($name, $defaultgroup) = @_;
+ local($group_login, $group);
+
+ $group_login = $name;
+ $group_login = $defaultgroup if $defaultgroup ne $group_uniq;
+
+ if (defined $gid{$group_login}) {
+ # convert numeric groupname (gid) to groupname
+ $group_login = $gid{$group_login};
+ }
+
+ # if (defined($groupname{$group_login})) {
+ # &add_group($groupname{$group_login}, $name);
+ # }
+
+ return $group_login
+ if defined($groupname{$group_login}) || $group_login eq $name;
+ warn "Group ``$group_login'' does not exist\a\n";
+ return 0;
+}
+
+# return other groups (string)
+sub new_users_groups {
+ local($name, $other_groups) = @_;
+ local($string) =
+ "Login group is ``$group_login''. Invite $name into other groups:";
+ local($e, $flag);
+ local($new_groups,$groups);
+
+ $other_groups = "no" unless $other_groups;
+
+ while(1) {
+ $groups = &confirm_list($string, 1, $other_groups,
+ ("no", $other_groups, "guest"));
+ # no other groups
+ return "" if $groups eq "no";
+
+ ($flag, $new_groups) = &new_users_groups_valid($groups);
+ last unless $flag;
+ }
+ $new_groups =~ s/\s*$//;
+ return $new_groups;
+}
+
+sub new_users_groups_valid {
+ local($groups) = @_;
+ local($e, $new_groups);
+ local($flag) = 0;
+
+ foreach $e (split(/[,\s]+/, $groups)) {
+ # convert numbers to groupname
+ if ($e =~ /^[0-9]+$/ && $gid{$e}) {
+ $e = $gid{$e};
+ }
+ if (defined($groupname{$e})) {
+ if ($e eq $group_login) {
+ # do not add user to a group if this group
+ # is also the login group.
+ } elsif (&add_group($groupname{$e}, $name)) {
+ $new_groups .= "$e ";
+ } else {
+ warn "$name is already member of group ``$e''\n";
+ }
+ } else {
+ warn "Group ``$e'' does not exist\a\n"; $flag++;
+ }
+ }
+ return ($flag, $new_groups);
+}
+
+# your last change
+sub new_users_ok {
+
+ print <<EOF;
+
+Name: $name
+Password: ****
+Fullname: $fullname
+Uid: $u_id
+Gid: $g_id ($group_login)
+Class: $class
+Groups: $group_login $new_groups
+HOME: $home/$name
+Shell: $sh
+EOF
+
+ return &confirm_yn("OK?", "yes");
+}
+
+# make password database
+sub new_users_pwdmkdb {
+ local($last) = @_;
+
+ system("$pwd_mkdb $etc_passwd");
+ if ($?) {
+ warn "$last\n";
+ warn "``$pwd_mkdb'' failed\n";
+ exit($? >> 8);
+ }
+}
+
+# update group database
+sub new_users_group_update {
+ local($e, @a);
+
+ # Add *new* group
+ if (!defined($groupname{$group_login}) &&
+ !defined($gid{$groupname{$group_login}})) {
+ push(@group_backup, "$group_login:*:$g_id:");
+ $groupname{$group_login} = $g_id;
+ $gid{$g_id} = $group_login;
+ # $groupmembers{$g_id} = $group_login;
+ }
+
+ if ($new_groups || defined($groupname{$group_login}) ||
+ defined($gid{$groupname{$group_login}}) &&
+ $gid{$groupname{$group_login}} ne "+") {
+ # new user is member of some groups
+ # new login group is already in name space
+ rename($group, "$group.bak");
+ #warn "$group_login $groupname{$group_login} $groupmembers{$groupname{$group_login}}\n";
+ foreach $e (sort {$a <=> $b} (keys %gid)) {
+ push(@a, "$gid{$e}:*:$e:$groupmembers{$e}");
+ }
+ &append_file($group, @a);
+ } else {
+ &append_file($group, "$group_login:*:$g_id:");
+ }
+
+}
+
+sub new_users_passwd_update {
+ # update passwd/group variables
+ push(@passwd_backup, $new_entry);
+ $username{$name} = $u_id;
+ $uid{$u_id} = $name;
+ $pwgid{$g_id} = $name;
+}
+
+# send message to new user
+sub new_users_sendmessage {
+ return 1 if $send_message eq "no";
+
+ local($cc) =
+ &confirm_list("Send message to ``$name'' and:",
+ 1, "no", ("root", "second_mail_address", "no"));
+ local($e);
+ $cc = "" if $cc eq "no";
+
+ foreach $e (@message_buffer) {
+ print eval "\"$e\"";
+ }
+ print "\n";
+
+ local(@message_buffer_append) = ();
+ if (!&confirm_yn("Add anything to default message", "no")) {
+ print "Use ``.'' or ^D alone on a line to finish your message.\n";
+ push(@message_buffer_append, "\n");
+ while($read = <STDIN>) {
+ last if $read eq "\.\n";
+ push(@message_buffer_append, $read);
+ }
+ }
+
+ &sendmessage("$name $cc", (@message_buffer, @message_buffer_append))
+ if (&confirm_yn("Send message", "yes"));
+}
+
+sub sendmessage {
+ local($to, @message) = @_;
+ local($e);
+
+ if (!open(M, "| mail -s Welcome $to")) {
+ warn "Cannot send mail to: $to!\n";
+ return 0;
+ } else {
+ foreach $e (@message) {
+ print M eval "\"$e\"";
+ }
+ close M;
+ }
+}
+
+
+sub new_users_password {
+
+ # empty password
+ return "" if $defaultpasswd ne "yes";
+
+ local($password);
+
+ while(1) {
+ system("stty -echo");
+ $password = &confirm_list("Enter password", 1, "", "");
+ system("stty echo");
+ print "\n";
+ if ($password ne "") {
+ system("stty -echo");
+ $newpass = &confirm_list("Enter password again", 1, "", "");
+ system("stty echo");
+ print "\n";
+ last if $password eq $newpass;
+ print "They didn't match, please try again\n";
+ }
+ elsif (&confirm_yn("Use an empty password?", "yes")) {
+ last;
+ }
+ }
+
+ return $password;
+}
+
+
+sub new_users {
+
+ print "\n" if $verbose;
+ print "Ok, let's go.\n" .
+ "Don't worry about mistakes. I will give you the chance later to " .
+ "correct any input.\n" if $verbose;
+
+ # name: Username
+ # fullname: Full name
+ # sh: shell
+ # 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($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;
+ ($u_id, $g_id) = &new_users_id($name);
+ $class = &new_users_class($defaultclass);
+ ($group_login, $defaultgroup) =
+ &new_users_grplogin($name, $defaultgroup, $new_users_ok);
+ # do not use uniq username and login group
+ $g_id = $groupname{$group_login} if (defined($groupname{$group_login}));
+
+ $new_groups = &new_users_groups($name, $new_groups);
+ $password = &new_users_password;
+
+
+ if (&new_users_ok) {
+ $new_users_ok = 1;
+
+ $cryptpwd = "";
+ $cryptpwd = crypt($password, &salt) if $password ne "";
+ # obscure perl bug
+ $new_entry = "$name\:" . "$cryptpwd" .
+ "\:$u_id\:$g_id\:$class\:0:0:$fullname:$home/$name:$sh";
+ &append_file($etc_passwd, "$new_entry");
+ &new_users_pwdmkdb("$new_entry");
+ &new_users_group_update;
+ &new_users_passwd_update; print "Added user ``$name''\n";
+ &new_users_sendmessage;
+ &adduser_log("$name:*:$u_id:$g_id($group_login):$fullname");
+ &home_create($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;
+ }
+}
+
+sub batch {
+ local($name, $groups, $class, $fullname, $password) = @_;
+ local($sh);
+
+ $defaultshell = &shell_default_valid($defaultshell);
+ return 0 unless $home = &home_partition_valid($home);
+ return 0 if $dotdir ne &dotdir_default_valid($dotdir);
+ $send_message = &message_default;
+
+ return 0 if $name ne &new_users_name_valid($name);
+ $sh = $shell{$defaultshell};
+ ($u_id, $g_id) = &next_id($name);
+ $group_login = &new_users_grplogin_batch($name, $defaultgroup);
+ return 0 unless $group_login;
+ $g_id = $groupname{$group_login} if (defined($groupname{$group_login}));
+ ($flag, $new_groups) = &new_users_groups_valid($groups);
+ return 0 if $flag;
+
+ $class = $defaultclass if $class eq "";
+ $cryptpwd = "";
+ $cryptpwd = crypt($password, &salt) if $password ne "";
+ # obscure perl bug
+ $new_entry = "$name\:" . "$cryptpwd" .
+ "\:$u_id\:$g_id\:$class\:0:0:$fullname:$home/$name:$sh";
+ &append_file($etc_passwd, "$new_entry");
+ &new_users_pwdmkdb("$new_entry");
+ &new_users_group_update;
+ &new_users_passwd_update; print "Added user ``$name''\n";
+ &sendmessage($name, @message_buffer) if $send_message ne "no";
+ &adduser_log("$name:*:$u_id:$g_id($group_login):$fullname");
+ &home_create($name, $group_login);
+}
+
+# ask for password usage
+sub password_default {
+ local($p) = $defaultpasswd;
+ if ($verbose) {
+ $p = &confirm_yn("Use passwords", $defaultpasswd);
+ $changes++ unless $p;
+ }
+ return "yes" if (($defaultpasswd eq "yes" && $p) ||
+ ($defaultpasswd eq "no" && !$p));
+ return "no"; # otherwise
+}
+
+# misc
+sub check_root {
+ die "You are not root!\n" if $< && !$test;
+}
+
+sub usage {
+ warn <<USAGE;
+usage: adduser
+ [-batch username [group[,group]...] [class] [fullname] [password]]
+ [-check_only]
+ [-class login_class]
+ [-config_create]
+ [-dotdir dotdir]
+ [-group login_group]
+ [-h|-help]
+ [-home home]
+ [-message message_file]
+ [-noconfig]
+ [-shell shell]
+ [-s|-silent|-q|-quiet]
+ [-uid uid_start]
+ [-v|-verbose]
+
+home=$home shell=$defaultshell dotdir=$dotdir login_group=$defaultgroup
+login_class=$defaultclass message_file=$send_message uid_start=$uid_start
+USAGE
+ exit 1;
+}
+
+# uniq(1)
+sub uniq {
+ local(@list) = @_;
+ local($e, $last, @array);
+
+ foreach $e (sort @list) {
+ push(@array, $e) unless $e eq $last;
+ $last = $e;
+ }
+ return @array;
+}
+
+# see /usr/src/usr.bin/passwd/local_passwd.c or librcypt, crypt(3)
+sub salt {
+ local($salt); # initialization
+ local($i, $rand);
+ local(@itoa64) = ( 0 .. 9, a .. z, A .. Z ); # 0 .. 63
+
+ warn "calculate salt\n" if $verbose > 1;
+ # to64
+ for ($i = 0; $i < 8; $i++) {
+ srand(time + $rand + $$);
+ $rand = rand(25*29*17 + $rand);
+ $salt .= $itoa64[$rand & $#itoa64];
+ }
+ warn "Salt is: $salt\n" if $verbose > 1;
+
+ return $salt;
+}
+
+
+# print banner
+sub copyright {
+ return;
+}
+
+# hints
+sub hints {
+ if ($verbose) {
+ print "Use option ``-silent'' if you don't want see " .
+ "all warnings & questions.\n\n";
+ } else {
+ print "Use option ``-verbose'' if you want see more warnings & " .
+ "questions \nor try to repair bugs.\n\n";
+ }
+}
+
+#
+sub parse_arguments {
+ local(@argv) = @_;
+
+ while ($_ = $argv[0], /^-/) {
+ shift @argv;
+ last if /^--$/;
+ if (/^--?(v|verbose)$/) { $verbose = 1 }
+ elsif (/^--?(s|silent|q|quiet)$/) { $verbose = 0 }
+ elsif (/^--?(debug)$/) { $verbose = 2 }
+ elsif (/^--?(h|help|\?)$/) { &usage }
+ elsif (/^--?(home)$/) { $home = $argv[0]; shift @argv }
+ elsif (/^--?(shell)$/) { $defaultshell = $argv[0]; shift @argv }
+ elsif (/^--?(dotdir)$/) { $dotdir = $argv[0]; shift @argv }
+ elsif (/^--?(uid)$/) { $uid_start = $argv[0]; shift @argv }
+ elsif (/^--?(class)$/) { $defaultclass = $argv[0]; shift @argv }
+ elsif (/^--?(group)$/) { $defaultgroup = $argv[0]; shift @argv }
+ elsif (/^--?(check_only)$/) { $check_only = 1 }
+ elsif (/^--?(message)$/) { $send_message = $argv[0]; shift @argv;
+ $sendmessage = 1; }
+ elsif (/^--?(batch)$/) {
+ @batch = splice(@argv, 0, 5); $verbose = 0;
+ die "batch: too few arguments\n" if $#batch < 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;
+ $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($name, $group) = @_;
+ local($homedir) = "$home/$name";
+
+ if (-e "$homedir") {
+ warn "HOME Directory ``$homedir'' already exist\a\n";
+ return 0;
+ }
+
+ if ($dotdir eq 'no') {
+ if (!mkdir("$homedir",0755)) {
+ warn "mkdir $homedir: $!\n"; return 0;
+ }
+ system 'chown', "$name:$group", $homedir;
+ return !$?;
+ }
+
+ # copy files from $dotdir to $homedir
+ # rename 'dot.foo' files to '.foo'
+ print "Copy files from $dotdir to $homedir\n" if $verbose;
+ system("cp -R $dotdir $homedir");
+ system("chmod -R u+wrX,go-w $homedir");
+ system("chown -R $name:$group $homedir");
+
+ # security
+ opendir(D, $homedir);
+ foreach $file (readdir(D)) {
+ if ($file =~ /^dot\./ && -f "$homedir/$file") {
+ $file =~ s/^dot\././;
+ rename("$homedir/dot$file", "$homedir/$file");
+ }
+ chmod(0600, "$homedir/$file")
+ if ($file =~ /^\.(rhosts|Xauthority|kermrc|netrc)$/);
+ chmod(0700, "$homedir/$file")
+ if ($file =~ /^(Mail|prv|\.(iscreen|term))$/);
+ }
+ closedir D;
+ return 1;
+}
+
+# makes a directory hierarchy
+sub mkdir_home {
+ local($dir) = @_;
+ $dir = &stripdir($dir);
+ local($user_partition) = "/usr";
+ local($dirname) = &dirname($dir);
+
+
+ -e $dirname || &mkdirhier($dirname);
+
+ if (((stat($dirname))[0]) == ((stat("/"))[0])){
+ # home partition is on root partition
+ # create home partition on $user_partition and make
+ # a symlink from $dir to $user_partition/`basename $dir`
+ # For instance: /home -> /usr/home
+
+ local($basename) = &basename($dir);
+ local($d) = "$user_partition/$basename";
+
+
+ if (-d $d) {
+ warn "Oops, $d already exist\n" if $verbose;
+ } else {
+ print "Create $d\n" if $verbose;
+ if (!mkdir("$d", 0755)) {
+ warn "$d: $!\a\n"; return 0;
+ }
+ }
+
+ unlink($dir); # symlink to nonexist file
+ print "Create symlink: $dir -> $d\n" if $verbose;
+ if (!symlink("$d", $dir)) {
+ warn "Symlink $d: $!\a\n"; return 0;
+ }
+ } else {
+ print "Create $dir\n" if $verbose;
+ if (!mkdir("$dir", 0755)) {
+ warn "Directory ``$dir'': $!\a\n"; return 0;
+ }
+ }
+ return 1;
+}
+
+sub mkdirhier {
+ local($dir) = @_;
+ local($d,$p);
+
+ $dir = &stripdir($dir);
+
+ foreach $d (split('/', $dir)) {
+ $dir = "$p/$d";
+ $dir =~ s|^//|/|;
+ if (! -e "$dir") {
+ print "Create $dir\n" if $verbose;
+ if (!mkdir("$dir", 0755)) {
+ warn "$dir: $!\n"; return 0;
+ }
+ }
+ $p .= "/$d";
+ }
+ return 1;
+}
+
+# stript unused '/'
+# F.i.: //usr///home// -> /usr/home
+sub stripdir {
+ local($dir) = @_;
+
+ $dir =~ s|/+|/|g; # delete double '/'
+ $dir =~ s|/$||; # delete '/' at end
+ return $dir if $dir ne "";
+ return '/';
+}
+
+# Read one of the elements from @list. $confirm is default.
+# If !$allow accept only elements from @list.
+sub confirm_list {
+ local($message, $allow, $confirm, @list) = @_;
+ local($read, $c, $print);
+
+ $print = "$message" if $message;
+ $print .= " " unless $message =~ /\n$/ || $#list == 0;
+
+ $print .= join($", &uniq(@list)); #"
+ $print .= " " unless $message =~ /\n$/ && $#list == 0;
+ print "$print";
+ print "\n" if (length($print) + length($confirm)) > 60;
+ print "[$confirm]: ";
+
+ chop($read = <STDIN>);
+ $read =~ s/^\s*//;
+ $read =~ s/\s*$//;
+ return $confirm if $read eq "";
+ return "$read" if $allow;
+
+ foreach $c (@list) {
+ return $read if $c eq $read;
+ }
+ warn "$read: is not allowed!\a\n";
+ return &confirm_list($message, $allow, $confirm, @list);
+}
+
+# YES or NO question
+# return 1 if &confirm("message", "yes") and answer is yes
+# or if &confirm("message", "no") an answer is no
+# otherwise 0
+sub confirm_yn {
+ local($message, $confirm) = @_;
+ local($yes) = '^(yes|YES|y|Y)$';
+ local($no) = '^(no|NO|n|N)$';
+ local($read, $c);
+
+ if ($confirm && ($confirm =~ "$yes" || $confirm == 1)) {
+ $confirm = "y";
+ } else {
+ $confirm = "n";
+ }
+ print "$message (y/n) [$confirm]: ";
+ chop($read = <STDIN>);
+ $read =~ s/^\s*//;
+ $read =~ s/\s*$//;
+ return 1 unless $read;
+
+ if (($confirm eq "y" && $read =~ "$yes") ||
+ ($confirm eq "n" && $read =~ "$no")) {
+ return 1;
+ }
+
+ if ($read !~ "$yes" && $read !~ "$no") {
+ warn "Wrong value. Enter again!\a\n";
+ return &confirm_yn($message, $confirm);
+ }
+ return 0;
+}
+
+# test if $dotdir exist
+# return "no" if $dotdir not exist or dotfiles should not copied
+sub dotdir_default {
+ local($dir) = $dotdir;
+
+ return &dotdir_default_valid($dir) unless $verbose;
+ while($verbose) {
+ $dir = &confirm_list("Copy dotfiles from:", 1,
+ $dir, ("no", $dotdir_bak, $dir));
+ last if $dir eq &dotdir_default_valid($dir);
+ }
+ warn "Do not copy dotfiles.\n" if $verbose && $dir eq "no";
+
+ $changes++ if $dir ne $dotdir;
+ return $dir;
+}
+
+sub dotdir_default_valid {
+ local($dir) = @_;
+
+ return $dir if (-e $dir && -r _ && (-d _ || -l $dir) && $dir =~ "^/");
+ return $dir if $dir eq "no";
+ warn "Dotdir ``$dir'' is not a directory\a\n";
+ return "no";
+}
+
+# ask for messages to new users
+sub message_default {
+ local($file) = $send_message;
+ local(@d) = ($file, $send_message_bak, "no");
+
+ while($verbose) {
+ $file = &confirm_list("Send message from file:", 1, $file, @d);
+ last if $file eq "no";
+ last if &filetest($file, 1);
+
+ # maybe create message file
+ &message_create($file) if &confirm_yn("Create ``$file''?", "yes");
+ last if &filetest($file, 0);
+ last if !&confirm_yn("File ``$file'' does not exist, try again?",
+ "yes");
+ }
+
+ if ($file eq "no" || !&filetest($file, 0)) {
+ warn "Do not send message\n" if $verbose;
+ $file = "no";
+ } else {
+ &message_read($file);
+ }
+
+ $changes++ if $file ne $send_message && $verbose;
+ return $file;
+}
+
+# create message file
+sub message_create {
+ local($file) = @_;
+
+ rename($file, "$file.bak");
+ if (!open(M, "> $file")) {
+ warn "Messagefile ``$file'': $!\n"; return 0;
+ }
+ print M <<EOF;
+#
+# Message file for adduser(8)
+# comment: ``#''
+# default variables: \$name, \$fullname, \$password
+# other variables: see /etc/adduser.conf after
+# line ``$do_not_delete''
+#
+
+\$fullname,
+
+your account ``\$name'' was created.
+Have fun!
+
+See also chpass(1), finger(1), passwd(1)
+EOF
+ close M;
+ return 1;
+}
+
+# read message file into buffer
+sub message_read {
+ local($file) = @_;
+ @message_buffer = '';
+
+ if (!open(R, "$file")) {
+ warn "File ``$file'':$!\n"; return 0;
+ }
+ while(<R>) {
+ push(@message_buffer, $_) unless /^\s*#/;
+ }
+ close R;
+}
+
+# write @list to $file with file-locking
+sub append_file {
+ local($file,@list) = @_;
+ local($e);
+ local($LOCK_EX) = 2;
+ local($LOCK_NB) = 4;
+ local($LOCK_UN) = 8;
+
+ open(F, ">> $file") || die "$file: $!\n";
+ print "Lock $file.\n" if $verbose > 1;
+ while(!flock(F, $LOCK_EX | $LOCK_NB)) {
+ warn "Cannot lock file: $file\a\n";
+ die "Sorry, give up\n"
+ unless &confirm_yn("Try again?", "yes");
+ }
+ print F join("\n", @list) . "\n";
+ close F;
+ print "Unlock $file.\n" if $verbose > 1;
+ flock(F, $LOCK_UN);
+}
+
+# return free uid+gid
+# uid == gid if possible
+sub next_id {
+ local($group) = @_;
+
+ $uid_start = 1000 if ($uid_start <= 0 || $uid_start >= $uid_end);
+ # looking for next free uid
+ while($uid{$uid_start}) {
+ $uid_start++;
+ $uid_start = 1000 if $uid_start >= $uid_end;
+ print "$uid_start\n" if $verbose > 1;
+ }
+
+ local($gid_start) = $uid_start;
+ # group for user (username==groupname) already exist
+ if ($groupname{$group}) {
+ $gid_start = $groupname{$group};
+ }
+ # gid is in use, looking for another gid.
+ # Note: uid an gid are not equal
+ elsif ($gid{$uid_start}) {
+ while($gid{$gid_start} || $uid{$gid_start}) {
+ $gid_start--;
+ $gid_start = $uid_end if $gid_start < 100;
+ }
+ }
+ return ($uid_start, $gid_start);
+}
+
+# read config file
+sub config_read {
+ local($opt) = @_;
+ local($user_flag) = 0;
+
+ # don't read config file
+ return 1 if $opt =~ /-(noconfig|config_create)/ || !$config_read;
+
+ if(!open(C, "$config")) {
+ warn "$config: $!\n"; return 0;
+ }
+
+ while(<C>) {
+ # user defined variables
+ /^$do_not_delete/ && $user_flag++;
+ # found @array or $variable
+ if (s/^(\w+\s*=\s*\()/\@$1/ || s/^(\w+\s*=)/\$$1/) {
+ eval $_;
+ #warn "$_";
+ }
+ # lines with '^##' are not saved
+ push(@user_variable_list, $_)
+ if $user_flag && !/^##/ && (s/^[\$\@]// || /^[#\s]/);
+ }
+ #warn "X @user_variable_list X\n";
+ close C;
+}
+
+
+# write config file
+sub config_write {
+ local($silent) = @_;
+
+ # nothing to do
+ return 1 unless ($changes || ! -e $config || !$config_read || $silent);
+
+ if (!$silent) {
+ if (-e $config) {
+ return 1 if &confirm_yn("\nWrite your changes to $config?", "no");
+ } else {
+ return 1 unless
+ &confirm_yn("\nWrite your configuration to $config?", "yes");
+ }
+ }
+
+ rename($config, "$config.bak");
+ open(C, "> $config") || die "$config: $!\n";
+
+ # prepare some variables
+ $send_message = "no" unless $send_message;
+ $defaultpasswd = "no" unless $defaultpasswd;
+ local($shpref) = "'" . join("', '", @shellpref) . "'";
+ local($shpath) = "'" . join("', '", @path) . "'";
+ local($user_var) = join('', @user_variable_list);
+
+ print C <<EOF;
+#
+# $config - automatic generated by adduser(8)
+#
+# Note: adduser read *and* write this file.
+# You may change values, but don't add new things befor the
+# line ``$do_not_delete''
+#
+
+# verbose = [0-2]
+verbose = $verbose
+
+# use password for new users
+# defaultpasswd = yes | no
+defaultpasswd = $defaultpasswd
+
+# copy dotfiles from this dir ("/usr/share/skel" or "no")
+dotdir = "$dotdir"
+
+# send this file to new user ("/etc/adduser.message" or "no")
+send_message = "$send_message"
+
+# config file for adduser ("/etc/adduser.conf")
+config = "$config"
+
+# logfile ("/var/log/adduser" or "no")
+logfile = "$logfile"
+
+# default HOME directory ("/home")
+home = "$home"
+
+# List of directories where shells located
+# path = ('/bin', '/usr/bin', '/usr/local/bin')
+path = ($shpath)
+
+# common shell list, first element has higher priority
+# shellpref = ('bash', 'tcsh', 'ksh', 'csh', 'sh')
+shellpref = ($shpref)
+
+# defaultshell if not empty ("bash")
+defaultshell = "$defaultshell"
+
+# defaultgroup ('USER' for same as username or any other valid group)
+defaultgroup = $defaultgroup
+
+# defaultclass if not empty
+defaultclass = "$defaultclass"
+
+# new users get this uid (1000)
+uid_start = 1000
+
+$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 && $#batch < 0) {
+ &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
+
+exit(!&batch(@batch)) if $#batch >= 0; # batch mode
+
+# interactive
+# some questions
+&shells_add; # maybe add some new shells
+$defaultshell = &shell_default; # enter default shell
+$home = &home_partition($home); # find HOME partition
+$dotdir = &dotdir_default; # check $dotdir
+$send_message = &message_default; # send message to new user
+$defaultpasswd = &password_default; # maybe use password
+&config_write(!$verbose); # write variables in file
+
+# main loop for creating new users
+&new_users; # add new users
+
+#end
diff --git a/usr.sbin/adduser/rmuser.8 b/usr.sbin/adduser/rmuser.8
new file mode 100644
index 0000000..5edaeef
--- /dev/null
+++ b/usr.sbin/adduser/rmuser.8
@@ -0,0 +1,169 @@
+.\" Copyright 1995, 1996, 1997
+.\" Guy Helmer, Ames, Iowa 50014. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer as
+.\" the first lines of this file unmodified.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. The name of the author may not be used to endorse or promote products
+.\" derived from this software without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY GUY HELMER ``AS IS'' AND ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+.\" IN NO EVENT SHALL GUY HELMER BE LIABLE FOR ANY DIRECT, INDIRECT,
+.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+.\"
+.\" $Id: rmuser.8,v 1.7 1997/09/01 06:14:17 charnier Exp $
+.\"
+.Dd February 23, 1997
+.Dt RMUSER 8
+.Os
+.Sh NAME
+.Nm rmuser
+.Nd removes users from the system
+.Sh SYNOPSIS
+.Nm rmuser
+.Op Fl y
+.Op Ar username
+.Sh DESCRIPTION
+The utility
+.Nm
+.Pp
+.Bl -enum
+.It
+Removes the user's
+.Xr crontab 1
+entry (if any).
+.It
+Removes any
+.Xr at 1
+jobs belonging to the user.
+.It
+Sends a SIGKILL signal to all processes owned by the user.
+.It
+Removes the user from the system's local password file.
+.It
+Removes the user's home directory (if it is owned by the user),
+including handling of symbolic links in the path to the actual home
+directory.
+.It
+Removes the incoming mail and pop daemon mail files belonging to the
+user from
+.Pa /var/mail .
+.It
+Removes all files owned by the user from
+.Pa /tmp ,
+.Pa /var/tmp ,
+and
+.Pa /var/tmp/vi.recover .
+.It
+Removes the username from all groups to which it belongs in
+.Pa /etc/group .
+(If a group becomes empty and the group name is the same as the username,
+the group is removed; this complements
+.Xr adduser 8 's
+per-user unique groups).
+.El
+.Pp
+.Nm Rmuser
+politely refuses to remove users whose uid is 0 (typically root), since
+certain actions (namely, killing all the user's processes, and perhaps
+removing the user's home directory) would cause damage to a running system.
+If it is necessary to remove a user whose uid is 0, see
+.Xr vipw 8
+for information on directly editing the password file, by which the desired
+user's
+.Xr passwd 5
+entry may be removed manually.
+.Pp
+If not running "affirmatively" (i.e., option
+.Fl y
+is not specified),
+.Nm
+shows the selected user's password file entry and asks for confirmation
+that you wish to remove the user. If the user's home directory is owned
+by the user,
+.Nm
+asks whether you wish to remove the user's home directory and everything
+below.
+.Pp
+As
+.Nm
+operates, it informs the user regarding the current activity. If any
+errors occur, they are posted to standard error and, if it is possible for
+.Nm
+to continue, it will.
+.Pp
+Available options:
+.Pp
+.Bl -tag -width username
+.It Fl y
+Affirm - any question that would be asked is answered implicitly in
+the affirmative (i.e., yes). A username must also be specified on the
+command line if this option is used.
+.It Ar \&username
+Identifies the user to be removed; if not present,
+.Nm
+interactively asks for the user to be removed.
+.Sh FILES
+.Bl -tag -width /etc/master.passwd -compact
+.It Pa /etc/master.passwd
+.It Pa /etc/passwd
+.It Pa /etc/group
+.It Pa /etc/spwd.db
+.It Pa /etc/pwd.db
+.El
+.Sh SEE ALSO
+.Xr at 1 ,
+.Xr chpass 1 ,
+.Xr crontab 1 ,
+.Xr finger 1 ,
+.Xr passwd 1 ,
+.Xr group 5 ,
+.Xr passwd 5 ,
+.Xr addgroup 8 ,
+.Xr adduser 8 ,
+.Xr pwd_mkdb 8 ,
+.Xr rmgroup 8 ,
+.Xr vipw 8
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Fx 2.2 .
+.\" .Sh AUTHOR
+.\" Guy Helmer, Ames, Iowa
+.Sh BUGS
+.Nm Rmuser
+does not comprehensively search the filesystem for all files
+owned by the removed user and remove them; to do so on a system
+of any size is prohibitively slow and I/O intensive.
+.Nm Rmuser
+also is unable to remove symbolic links that were created by the
+user in
+.Pa /tmp
+or
+.Pa /var/tmp
+as symbolic links on 4.4BSD filesystems do not contain information
+as to who created them. Also, there may be other files created in
+.Pa /var/mail
+other than
+.Pa /var/mail/username
+and
+.Pa /var/mail/.pop.username
+that are not owned by the removed user but should be removed.
+.Pp
+.Nm Rmuser
+has no knowledge of NIS (Yellow Pages), and it operates only on the
+local password file.
diff --git a/usr.sbin/adduser/rmuser.perl b/usr.sbin/adduser/rmuser.perl
new file mode 100644
index 0000000..57bd74f
--- /dev/null
+++ b/usr.sbin/adduser/rmuser.perl
@@ -0,0 +1,576 @@
+#!/usr/bin/perl
+# -*- perl -*-
+# Copyright 1995, 1996, 1997 Guy Helmer, Ames, Iowa 50014.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer as
+# the first lines of this file unmodified.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. The name of the author may not be used to endorse or promote products
+# derived from this software without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY GUY HELMER ``AS IS'' AND ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+# IN NO EVENT SHALL GUY HELMER BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+# rmuser - Perl script to remove users
+#
+# Guy Helmer <ghelmer@cs.iastate.edu>, 02/23/97
+#
+# $Id: rmuser.perl,v 1.6 1997/03/08 18:04:45 wosch Exp $
+
+sub LOCK_SH {0x01;}
+sub LOCK_EX {0x02;}
+sub LOCK_NB {0x04;}
+sub LOCK_UN {0x08;}
+sub F_SETFD {2;}
+
+$ENV{"PATH"} = "/bin:/sbin:/usr/bin:/usr/sbin";
+umask(022);
+$whoami = $0;
+$passwd_file = "/etc/master.passwd";
+$new_passwd_file = "${passwd_file}.new.$$";
+$group_file = "/etc/group";
+$new_group_file = "${group_file}.new.$$";
+$mail_dir = "/var/mail";
+$crontab_dir = "/var/cron/tabs";
+$atjob_dir = "/var/at/jobs";
+$affirm = 0;
+
+#$debug = 1;
+
+sub cleanup {
+ local($sig) = @_;
+
+ print STDERR "Caught signal SIG$sig -- cleaning up.\n";
+ &unlockpw;
+ if (-e $new_passwd_file) {
+ unlink $new_passwd_file;
+ }
+ exit(0);
+}
+
+sub lockpw {
+ # Open the password file for reading
+ if (!open(MASTER_PW, "$passwd_file")) {
+ print STDERR "${whoami}: Error: Couldn't open ${passwd_file}: $!\n";
+ exit(1);
+ }
+ # Set the close-on-exec flag just in case
+ fcntl(MASTER_PW, &F_SETFD, 1);
+ # Apply an advisory lock the password file
+ if (!flock(MASTER_PW, &LOCK_EX|&LOCK_NB)) {
+ print STDERR "${whoami}: Error: Couldn't lock ${passwd_file}: $!\n";
+ exit(1);
+ }
+}
+
+sub unlockpw {
+ flock(MASTER_PW, &LOCK_UN);
+}
+
+$SIG{'INT'} = 'cleanup';
+$SIG{'QUIT'} = 'cleanup';
+$SIG{'HUP'} = 'cleanup';
+$SIG{'TERM'} = 'cleanup';
+
+if ($#ARGV == 1 && $ARGV[0] eq '-y') {
+ shift @ARGV;
+ $affirm = 1;
+}
+
+if ($#ARGV > 0) {
+ print STDERR "usage: ${whoami} [-y] [username]\n";
+ exit(1);
+}
+
+if ($< != 0) {
+ print STDERR "${whoami}: Error: you must be root to use ${whoami}\n";
+ exit(1);
+}
+
+&lockpw;
+
+if ($#ARGV == 0) {
+ # Username was given as a parameter
+ $login_name = pop(@ARGV);
+ die "Sorry, login name must contain alphanumeric characters only.\n"
+ if ($login_name !~ /^[a-z0-9_][a-z0-9_\-]*$/);
+} else {
+ if ($affirm) {
+ print STDERR "${whoami}: Error: -y option given without username!\n";
+ &unlockpw;
+ exit 1;
+ }
+ # Get the user name from the user
+ $login_name = &get_login_name;
+}
+
+if (($pw_ent = &check_login_name($login_name)) eq '0') {
+ print STDERR "${whoami}: Error: User ${login_name} not in password database\n";
+ &unlockpw;
+ exit 1;
+}
+
+($name, $password, $uid, $gid, $class, $change, $expire, $gecos, $home_dir,
+ $shell) = split(/:/, $pw_ent);
+
+if ($uid == 0) {
+ print "${whoami}: Error: I'd rather not remove a user with a uid of 0.\n";
+ &unlockpw;
+ exit 1;
+}
+
+if (! $affirm) {
+ print "Matching password entry:\n\n$pw_ent\n\n";
+
+ $ans = &get_yn("Is this the entry you wish to remove? ");
+
+ if ($ans eq 'N') {
+ print "${whoami}: Informational: User ${login_name} not removed.\n";
+ &unlockpw;
+ exit 0;
+ }
+}
+
+#
+# Get owner of user's home directory; don't remove home dir if not
+# owned by $login_name
+
+$remove_directory = 1;
+
+if (-l $home_dir) {
+ $real_home_dir = &resolvelink($home_dir);
+} else {
+ $real_home_dir = $home_dir;
+}
+
+#
+# If home_dir is a symlink and points to something that isn't a directory,
+# or if home_dir is not a symlink and is not a directory, don't remove
+# home_dir -- seems like a good thing to do, but probably isn't necessary...
+
+if (((-l $home_dir) && ((-e $real_home_dir) && !(-d $real_home_dir))) ||
+ (!(-l $home_dir) && !(-d $home_dir))) {
+ print STDERR "${whoami}: Informational: Home ${home_dir} is not a directory, so it won't be removed\n";
+ $remove_directory = 0;
+}
+
+if (length($real_home_dir) && -d $real_home_dir) {
+ $dir_owner = (stat($real_home_dir))[4]; # UID
+ if ($dir_owner != $uid) {
+ print STDERR "${whoami}: Informational: Home dir ${real_home_dir} is" .
+ " not owned by ${login_name} (uid ${dir_owner})\n," .
+ "\tso it won't be removed\n";
+ $remove_directory = 0;
+ }
+}
+
+if ($remove_directory && ! $affirm) {
+ $ans = &get_yn("Remove user's home directory ($home_dir)? ");
+ if ($ans eq 'N') {
+ $remove_directory = 0;
+ }
+}
+
+#exit 0 if $debug;
+
+#
+# Remove the user's crontab, if there is one
+# (probably needs to be done before password databases are updated)
+
+if (-e "$crontab_dir/$login_name") {
+ print STDERR "Removing user's crontab:";
+ system('/usr/bin/crontab', '-u', $login_name, '-r');
+ print STDERR " done.\n";
+}
+
+#
+# Remove the user's at jobs, if any
+# (probably also needs to be done before password databases are updated)
+
+&remove_at_jobs($login_name, $uid);
+
+#
+# Kill all the user's processes
+
+&kill_users_processes($login_name, $uid);
+
+#
+# Copy master password file to new file less removed user's entry
+
+&update_passwd_file;
+
+#
+# Remove the user from all groups in /etc/group
+
+&update_group_file($login_name);
+
+#
+# Remove the user's home directory
+
+if ($remove_directory) {
+ print STDERR "Removing user's home directory ($home_dir):";
+ &remove_dir($home_dir);
+ print STDERR " done.\n";
+}
+
+#
+# Remove files related to the user from the mail directory
+
+#&remove_files_from_dir($mail_dir, $login_name, $uid);
+$file = "$mail_dir/$login_name";
+if (-e $file || -l $file) {
+ print STDERR "Removing user's incoming mail file ${file}:";
+ unlink $file ||
+ print STDERR "\n${whoami}: Warning: unlink on $file failed ($!) - continuing\n";
+ print STDERR " done.\n";
+}
+
+#
+# Remove some pop daemon's leftover file
+
+$file = "$mail_dir/.${login_name}.pop";
+if (-e $file || -l $file) {
+ print STDERR "Removing pop daemon's temporary mail file ${file}:";
+ unlink $file ||
+ print STDERR "\n${whoami}: Warning: unlink on $file failed ($!) - continuing\n";
+ print STDERR " done.\n";
+}
+
+#
+# Remove files belonging to the user from the directories /tmp, /var/tmp,
+# and /var/tmp/vi.recover. Note that this doesn't take care of the
+# problem where a user may have directories or symbolic links in those
+# directories -- only regular files are removed.
+
+&remove_files_from_dir('/tmp', $login_name, $uid);
+&remove_files_from_dir('/var/tmp', $login_name, $uid);
+&remove_files_from_dir('/var/tmp/vi.recover', $login_name, $uid)
+ if (-e '/var/tmp/vi.recover');
+
+#
+# All done!
+
+exit 0;
+
+sub get_login_name {
+ #
+ # Get new user's name
+ local($done, $login_name);
+
+ for ($done = 0; ! $done; ) {
+ print "Enter login name for user to remove: ";
+ $login_name = <>;
+ chop $login_name;
+ if (!($login_name =~ /^[a-z0-9_][a-z0-9_\-]*$/)) {
+ print STDERR "Sorry, login name must contain alphanumeric characters only.\n";
+ } elsif (length($login_name) > 16 || length($login_name) == 0) {
+ print STDERR "Sorry, login name must be 16 characters or less.\n";
+ } else {
+ $done = 1;
+ }
+ }
+
+ print "User name is ${login_name}\n" if $debug;
+ return($login_name);
+}
+
+sub check_login_name {
+ #
+ # Check to see whether login name is in password file
+ local($login_name) = @_;
+ local($Mname, $Mpassword, $Muid, $Mgid, $Mclass, $Mchange, $Mexpire,
+ $Mgecos, $Mhome_dir, $Mshell);
+ local($i);
+
+ seek(MASTER_PW, 0, 0);
+ while ($i = <MASTER_PW>) {
+ chop $i;
+ ($Mname, $Mpassword, $Muid, $Mgid, $Mclass, $Mchange, $Mexpire,
+ $Mgecos, $Mhome_dir, $Mshell) = split(/:/, $i);
+ if ($Mname eq $login_name) {
+ seek(MASTER_PW, 0, 0);
+ return($i); # User is in password database
+ }
+ }
+ seek(MASTER_PW, 0, 0);
+
+ return '0'; # User wasn't found
+}
+
+sub get_yn {
+ #
+ # Get a yes or no answer; return 'Y' or 'N'
+ local($prompt) = @_;
+ local($done, $ans);
+
+ for ($done = 0; ! $done; ) {
+ print $prompt;
+ $ans = <>;
+ chop $ans;
+ $ans =~ tr/a-z/A-Z/;
+ if (!($ans =~ /^[YN]/)) {
+ print STDERR "Please answer (y)es or (n)o.\n";
+ } else {
+ $done = 1;
+ }
+ }
+
+ return(substr($ans, 0, 1));
+}
+
+sub update_passwd_file {
+ local($skipped, $i);
+
+ print STDERR "Updating password file,";
+ seek(MASTER_PW, 0, 0);
+ open(NEW_PW, ">$new_passwd_file") ||
+ die "\n${whoami}: Error: Couldn't open file ${new_passwd_file}:\n $!\n";
+ chmod(0600, $new_passwd_file) ||
+ print STDERR "\n${whoami}: Warning: couldn't set mode of $new_passwd_file to 0600 ($!)\n\tcontinuing, but please check mode of /etc/master.passwd!\n";
+ $skipped = 0;
+ while ($i = <MASTER_PW>) {
+ if ($i =~ /\n$/) {
+ chop $i;
+ }
+ if ($i ne $pw_ent) {
+ print NEW_PW "$i\n";
+ } else {
+ print STDERR "Dropped entry for $login_name\n" if $debug;
+ $skipped = 1;
+ }
+ }
+ close(NEW_PW);
+ seek(MASTER_PW, 0, 0);
+
+ if ($skipped == 0) {
+ print STDERR "\n${whoami}: Whoops! Didn't find ${login_name}'s entry second time around!\n";
+ unlink($new_passwd_file) ||
+ print STDERR "\n${whoami}: Warning: couldn't unlink $new_passwd_file ($!)\n\tPlease investigate, as this file should not be left in the filesystem\n";
+ &unlockpw;
+ exit 1;
+ }
+
+ #
+ # Run pwd_mkdb to install the updated password files and databases
+
+ print STDERR " updating databases,";
+ system('/usr/sbin/pwd_mkdb', '-p', ${new_passwd_file});
+ print STDERR " done.\n";
+
+ close(MASTER_PW); # Not useful anymore
+}
+
+sub update_group_file {
+ local($login_name) = @_;
+
+ local($i, $j, $grmember_list, $new_grent, $changes);
+ local($grname, $grpass, $grgid, $grmember_list, @grmembers);
+
+ $changes = 0;
+ print STDERR "Updating group file:";
+ open(GROUP, $group_file) ||
+ die "\n${whoami}: Error: couldn't open ${group_file}: $!\n";
+ if (!flock(GROUP, &LOCK_EX|&LOCK_NB)) {
+ print STDERR "\n${whoami}: Error: couldn't lock ${group_file}: $!\n";
+ exit 1;
+ }
+ local($group_perms, $group_uid, $group_gid) =
+ (stat(GROUP))[2, 4, 5]; # File Mode, uid, gid
+ open(NEW_GROUP, ">$new_group_file") ||
+ die "\n${whoami}: Error: couldn't open ${new_group_file}: $!\n";
+ chmod($group_perms, $new_group_file) ||
+ printf STDERR "\n${whoami}: Warning: could not set permissions of new group file to %o ($!)\n\tContinuing, but please check permissions of $group_file!\n", $group_perms;
+ chown($group_uid, $group_gid, $new_group_file) ||
+ print STDERR "\n${whoami}: Warning: could not set owner/group of new group file to ${group_uid}/${group_gid} ($!)\n\rContinuing, but please check ownership of $group_file!\n";
+ while ($i = <GROUP>) {
+ if (!($i =~ /$login_name/)) {
+ # Line doesn't contain any references to the user, so just add it
+ # to the new file
+ print NEW_GROUP $i;
+ } else {
+ #
+ # Remove the user from the group
+ if ($i =~ /\n$/) {
+ chop $i;
+ }
+ ($grname, $grpass, $grgid, $grmember_list) = split(/:/, $i);
+ @grmembers = split(/,/, $grmember_list);
+ undef @new_grmembers;
+ local(@new_grmembers);
+ foreach $j (@grmembers) {
+ if ($j ne $login_name) {
+ push(@new_grmembers, $j);
+ } else {
+ print STDERR " $grname";
+ $changes = 1;
+ }
+ }
+ if ($grname eq $login_name && $#new_grmembers == -1) {
+ # Remove a user's personal group if empty
+ print STDERR " (removing group $grname -- personal group is empty)";
+ $changes = 1;
+ } else {
+ $grmember_list = join(',', @new_grmembers);
+ $new_grent = join(':', $grname, $grpass, $grgid, $grmember_list);
+ print NEW_GROUP "$new_grent\n";
+ }
+ }
+ }
+ close(NEW_GROUP);
+ rename($new_group_file, $group_file) || # Replace old group file with new
+ die "\n${whoami}: Error: couldn't rename $new_group_file to $group_file ($!)\n";
+ close(GROUP); # File handle is worthless now
+ print STDERR " (no changes)" if (! $changes);
+ print STDERR " done.\n";
+}
+
+sub remove_dir {
+ # Remove the user's home directory
+ local($dir) = @_;
+ local($linkdir);
+
+ if (-l $dir) {
+ $linkdir = &resolvelink($dir);
+ # Remove the symbolic link
+ unlink($dir) ||
+ warn "${whoami}: Warning: could not unlink symlink $dir: $!\n";
+ if (!(-e $linkdir)) {
+ #
+ # Dangling symlink - just return now
+ return;
+ }
+ # Set dir to be the resolved pathname
+ $dir = $linkdir;
+ }
+ if (!(-d $dir)) {
+ print STDERR "${whoami}: Warning: $dir is not a directory\n";
+ unlink($dir) || warn "${whoami}: Warning: could not unlink $dir: $!\n";
+ return;
+ }
+ system('/bin/rm', '-rf', $dir);
+}
+
+sub remove_files_from_dir {
+ local($dir, $login_name, $uid) = @_;
+ local($path, $i, $owner);
+
+ print STDERR "Removing files belonging to ${login_name} from ${dir}:";
+
+ if (!opendir(DELDIR, $dir)) {
+ print STDERR "\n${whoami}: Warning: couldn't open directory ${dir} ($!)\n";
+ return;
+ }
+ while ($i = readdir(DELDIR)) {
+ next if $i eq '.';
+ next if $i eq '..';
+
+ $owner = (stat("$dir/$i"))[4]; # UID
+ if ($uid == $owner) {
+ if (-f "$dir/$i") {
+ print STDERR " $i";
+ unlink "$dir/$i" ||
+ print STDERR "\n${whoami}: Warning: unlink on ${dir}/${i} failed ($!) - continuing\n";
+ } else {
+ print STDERR " ($i not a regular file - skipped)";
+ }
+ }
+ }
+ closedir(DELDIR);
+
+ printf STDERR " done.\n";
+}
+
+sub remove_at_jobs {
+ local($login_name, $uid) = @_;
+ local($i, $owner, $found);
+
+ $found = 0;
+ opendir(ATDIR, $atjob_dir) || return;
+ while ($i = readdir(ATDIR)) {
+ next if $i eq '.';
+ next if $i eq '..';
+ next if $i eq '.lockfile';
+
+ $owner = (stat("$atjob_dir/$i"))[4]; # UID
+ if ($uid == $owner) {
+ if (!$found) {
+ print STDERR "Removing user's at jobs:";
+ $found = 1;
+ }
+ # Use atrm to remove the job
+ print STDERR " $i";
+ system('/usr/bin/atrm', $i);
+ }
+ }
+ closedir(ATDIR);
+ if ($found) {
+ print STDERR " done.\n";
+ }
+}
+
+sub resolvelink {
+ local($path) = @_;
+ local($l);
+
+ while (-l $path && -e $path) {
+ if (!defined($l = readlink($path))) {
+ die "${whoami}: readlink on $path failed (but it should have worked!): $!\n";
+ }
+ if ($l =~ /^\//) {
+ # Absolute link
+ $path = $l;
+ } else {
+ # Relative link
+ $path =~ s/\/[^\/]+\/?$/\/$l/; # Replace last component of path
+ }
+ }
+ return $path;
+}
+
+sub kill_users_processes {
+ local($login_name, $uid) = @_;
+ local($pid, $result);
+
+ #
+ # Do something a little complex: fork a child that changes its
+ # real and effective UID to that of the removed user, then issues
+ # a "kill(9, -1)" to kill all processes of the same uid as the sender
+ # (see kill(2) for details).
+ # The parent waits for the exit of the child and then returns.
+
+ if ($pid = fork) {
+ # Parent process
+ waitpid($pid, 0);
+ } elsif (defined $pid) {
+ # Child process
+ $< = $uid;
+ $> = $uid;
+ if ($< != $uid || $> != $uid) {
+ print STDERR "${whoami}: Error (kill_users_processes):\n" .
+ "\tCouldn't reset uid/euid to ${uid}: current uid/euid's are $< and $>\n";
+ exit 1;
+ }
+ $result = kill(9, -1);
+ print STDERR "Killed process(es) belonging to $login_name.\n"
+ if $result;
+ exit 0;
+ } else {
+ # Couldn't fork!
+ print STDERR "${whoami}: Error: couldn't fork to kill ${login_name}'s processes - continuing\n";
+ }
+}
diff --git a/usr.sbin/amd/Makefile b/usr.sbin/amd/Makefile
new file mode 100644
index 0000000..5b1fb9d
--- /dev/null
+++ b/usr.sbin/amd/Makefile
@@ -0,0 +1,5 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+SUBDIR= amd amq doc fsinfo mk-amd-map
+
+.include <bsd.subdir.mk>
diff --git a/usr.sbin/amd/amd/ChangeLog b/usr.sbin/amd/amd/ChangeLog
new file mode 100644
index 0000000..2cd1807
--- /dev/null
+++ b/usr.sbin/amd/amd/ChangeLog
@@ -0,0 +1,1169 @@
+Sun Jun 7 19:01:37 1992 Jan-Simon Pendry (jsp at achilles)
+
+ * Code cut for BSD 4.4 alpha.
+
+Sun May 31 17:28:52 1992 Jan-Simon Pendry (jsp at achilles)
+
+ * (afs_ops.c) make sure error code is returned to user if there
+ is an immediate error in afs_lookuppn.
+
+ * (nfs_ops.c) clear out mountd port number after use.
+
+ * (nfs_ops.c) optionally (KICK_KERNEL) run lstat over the old
+ mount point. This should go somewhere else more appropriate.
+
+Sun Mar 29 16:22:01 1992 Jan-Simon Pendry (jsp at achilles)
+
+ * (mount_fs.c) FASCIST_DF_COMMAND is now defined as the fstype
+ you want df to see.
+
+ * (opts.c) fix buffer overrun in slash munging.
+
+ * (host_ops.c) add support for INFORM_MOUNTD.
+
+Sat Mar 7 10:23:04 1992 Jan-Simon Pendry (jsp at achilles)
+
+ * (nfsx_ops.c) fix buffer overrun problem by allocating sufficient
+ memory.
+
+ * (afs_ops.c) fix dereference of free'ed memory when calling
+ assign_error_mntfs.
+
+ * (xutil.c) fix xmalloc and xrealloc to allow zero sized
+ allocation.
+
+ * (os-type) make it recognise aix3.2
+
+Sun Feb 9 13:14:54 1992 Jan-Simon Pendry (jsp at achilles)
+
+ * Release 5.3 Beta
+
+ * hpux 8 support is still missing.
+
+ * (os-bsd44.h) merged in changes for new bsd nfs code. still need
+ to add in lease and kerberos support.
+
+Sun Dec 1 16:20:21 1991 Jan-Simon Pendry (jsp at achilles)
+
+ * (info_nis.c) changed so that it remembers if the NIS domainname
+ is set and doesn't repeatedly complain. Message changed from an
+ Error to a Warning.
+
+ * (sfs_ops.c) added linkx fstype. linkx is the same as link but
+ it also checks that the target exists. This makes it useful for
+ wildcard map entries.
+
+ * (wire.c) applied changes from Dirk Grunwald. Now stands a
+ better chance of correctly determining the network name.
+
+ * (map.c) changes in timeout_mp to cause second and subsequent
+ backgrounded unmount attempts to backoff and so reduce the chance
+ of running more than one backgrounded unmount at a time.
+
+ * (misc_rpc.c) bug fix from Martyn Johnson to avoid xdr_free'ing
+ the wrong piece of memory. This was causing random core dumps,
+ often with a stack trace through pickup_rpc_reply.
+
+Sun Sep 15 21:16:38 1991 Jan-Simon Pendry (jsp at achilles)
+
+ * Release 5.3 alpha 14.
+
+ * (os-osf1.h) merged in OSF/1 support.
+
+Sun Aug 4 18:25:04 1991 Jan-Simon Pendry (jsp at achilles)
+
+ * (os-stellix.h) merged in Stellix support.
+
+ * (os-u4_2.h) merged in Ultrix 4.2 support.
+
+ * (host_ops.c) made TCP the default transport protocol. Define
+ HOST_RPC_UDP somewhere to get UDP transport.
+
+ * (many) added support for ${remopts}. Suggested by Steve
+ Heimlich. remopts is the same as opts, but is used when the
+ remote server is not on the local network. If it is not set it
+ defaults to whatever value ${opts} has.
+
+Tue May 7 22:30:00 1991 Jan-Simon Pendry (jsp at achilles)
+
+ * Checkpoint for Berkeley network tape II.
+
+Sat May 4 22:06:24 1991 Jan-Simon Pendry (jsp at achilles)
+
+ * New Berkeley Copyright.
+
+ * (mntfs.c) more short-circuiting in realloc_mntfs().
+
+ * (host_ops.c) increase RPC timeout to 20 seconds.
+
+ * (info_hes.c) now supports lookup in other domains.
+
+ * (srvr_nfs.c) call map_flush_srvr whenever a server comes up.
+
+ * (map.c) added hook to flush hung file servers.
+
+ * (wire.c) make loop step more portable.
+
+ * (util.c) fix for compiling on rios.
+
+Sun Apr 21 21:54:52 1991 Jan-Simon Pendry (jsp at forest)
+
+ * (util.c) ignore EINVAL returned by rmdir().
+
+ * (am_ops.c, afs_ops.c) remove SunOS4 map compat code.
+
+ * (nfsx_ops.c) don't clear MFF_MOUNTING until finished mount attempts.
+
+ * (nfsx_ops.c) don't call sched_task more than once.
+
+ * (afs_ops.c) don't call afs_bgmount if a mount is in progress.
+
+ * (info_file.c) handle case where map ends with \
+
+Fri Apr 5 19:23:50 1991 Jan-Simon Pendry (jsp at forest)
+
+ * Release 5.3 Alpha 12.
+
+ * (util.c) don't clear MFF_MOUNTING flag if mount is still in progress.
+
+ * (srvr_nfs.c) calls make_nfs_auth() as required.
+
+ * (host_ops.c) calls make_nfs_auth() as required.
+
+ * (afs_ops.c) allow foreground mounts to return pending. This is
+ used by nfsx_ops.c.
+
+ * (mapc.c) uses new RE_HDR abstraction coping with systems which
+ already have the re package installed.
+
+ * (nfsx_ops.c) automatically generates a suitable sublink to make
+ things work. Remounts now work correctly, but are done in the
+ foreground so there is a possibility that things may hang. This is
+ too hard to do differently.
+
+Wed Apr 3 17:49:05 1991 Jan-Simon Pendry (jsp at achilles)
+
+ * (nfs_ops.c) HAS_NFS_QUALIFIED_NAME is a new compile time switch
+ which puts a qualified domain name into the RPC authentication
+ instead of using the default value. Abstracted this out into new
+ routine called make_nfs_auth().
+
+ * (afs_ops.c) fixed bug which caused spurious ENOENTs to appear.
+ this was caused by some code motion which also got slightly
+ altered int the process. moral: don't make two changes at once.
+
+Sun Mar 17 12:05:27 1991 Jan-Simon Pendry (jsp at forest)
+
+ * Release 5.3 Alpha 11.
+
+ * (amq.8) Updated.
+
+ * (amq.c) Added new -v option which displays the version number of
+ the target Amd. Also added support to Amd and reworked newvers
+ script. Got rid of rcs_info.c.
+
+ * (mk-amd-map.c) Changed name of remove function to avoid clash
+ with ANSI C.
+
+ * (wire.c) Fixed to work with new 4.4BSD sockaddr's.
+
+ * Changed const to Const everywhere and added new define in config.h.
+
+ * (mk-amd-map.c) New -p option which just writes the output to
+ stdout. Useful for making NIS or Hesiod maps.
+
+ * (amdref) Small updates and clarifications.
+
+Sat Mar 16 20:35:17 1991 Jan-Simon Pendry (jsp at forest)
+
+ * (amq_subr.c) Flush request now flushes all internal caches.
+
+ * (amq_subr.c) Changed xdr_amq_mount_tree to return only the
+ required sub-tree.
+
+ * (ifs_ops.c) Added missing return 0; to ifs_mount.
+
+Sat Mar 9 19:31:25 1991 Jan-Simon Pendry (jsp at forest)
+
+ * (get_args.c) Output the primary network interface information
+ with the -v option.
+
+ * (mapc.c) Fixed spurious warning about "root" map not supporting
+ cache mode "all". Added new (unnamed) cache mode MAPC_ROOT.
+
+ * (info_nis.c) Fixed order number testing which was the cause of a
+ loop.
+
+Sun Mar 3 17:57:03 1991 Jan-Simon Pendry (jsp at forest)
+
+ * Release 5.3 Alpha 10.
+
+ * Introduced new inet_dquad routine which prints IP addresses in
+ dotted quad format. The C library routine is not used because it
+ uses a static buffer and it takes a structure argument on some
+ machines and unsigned longs on others. This confuses the hell out
+ of some compilers and causes SEGVs.
+
+ * task_notify becomes do_task_notify to avoid clash with Mach.
+
+ * (mntfs.c) In realloc_mntfs, the private data field wasn't being
+ cleared out when the mntfs object was being re-used. This meant
+ that the data might be used for the wrong mount, so causing
+ various obscure errors.
+
+ * (info_file.c) Reworked to provide support for map cache "sync"
+ option.
+
+ * (mapc.c) Added new "sync" option to map cache. This ensures
+ that the cached data doesn't become stale wrt the source.
+ Currently this isn't implemented for passwd and hesiod maps.
+
+Wed Feb 27 11:38:07 1991 Jan-Simon Pendry (jsp at forest)
+
+ * (afs_ops.c) Fixed pid put in fs_hostname for toplvl mount.
+
+Sun Feb 24 19:37:55 1991 Jan-Simon Pendry (jsp at forest)
+
+ * (wire.c) New module which determines the name of the primary
+ attached network. This could be used to determine which
+ server to use.
+
+ * (srvr_nfs.c) Changed mount daemon port mapping caching. This is
+ now much more eager to recompute the mapping than before. Will
+ also now work even if pinging is switched off (for example "tcp").
+
+ * (info_nis.c) Added back old NIS reload code to allow NIS maps to
+ support "regexp" caching.
+
+ * (mapc.c) Added support for "regexp" caching. This is a type of
+ map cache where the entries are all REs to be matched against the
+ requested key. This implies "all" since all the keys must be
+ loaded for the search to work.
+
+Sat Feb 23 15:32:43 1991 Jan-Simon Pendry (jsp at forest)
+
+ * (afs_ops.c) Avoid spurious error messages about discarding a
+ retry mntfs.
+
+ * (amq_subr.c) Removed inet_ntoa call due to disagreement between
+ gcc and libc about 4 byte structure passing.
+
+ * (xutil.c) Changed way initial logging is done to make command
+ line more usable. Default logging flags are set statically and
+ can then be modified by -x options. At the end an additional call
+ to switch_option is made to initialise xlog_level_init.
+
+ * (umount_fs.c) ENOENT now treated the same as EINVAL. If the
+ filesystem gets removed and the mountpoint deleted then just
+ assume the filesystem isn't there - which it isn't.
+
+ * (host_ops.c) Now copes with unmount failures by attempting to
+ remount the filesystems which had been unmounted.
+
+ * (host_ops.c) Added check during fhandle collection to detect
+ duplicate entries in the export list (from Stefan Petri).
+
+ * (nfsx_ops.c) Reworked to correctly keep track of what is and
+ isn't mounted.
+
+Sun Jan 27 16:58:02 1991 Jan-Simon Pendry (jsp at achilles)
+
+ * (misc-next.h) Added missing NeXT config file.
+
+ * Merged Harris HCX/UX support from Chris Metcalf.
+
+ * (ifs_ops.c) added ifs_fmount entry point to keep nfsx_ops happy.
+
+Sun Jan 13 18:19:19 1991 Jan-Simon Pendry (jsp at beauty)
+
+ * (nfsx_ops.c) play with opt_fs field to make sure it is unique.
+
+Fri Dec 21 15:35:45 1990 Jan-Simon Pendry (jsp at forest)
+
+ * Release 5.3 Alpha 9. This is still not Beta!
+
+ * (host_ops.c) use normalized hostname in mtab entries, from
+ Chris Metcalf.
+
+ * (map.c) enum ftype -> ftype, from Andrew Findlay.
+
+Mon Dec 17 01:11:25 1990 Jan-Simon Pendry (jsp at beauty)
+
+ * (amdref.texinfo) merged in fsinfo documentation from Nick.
+
+Sat Dec 15 15:39:07 1990 Jan-Simon Pendry (jsp at beauty)
+
+ * (clock.c) minor tweaks to messages.
+
+ * (sfs_ops.c) make the opt_fs field unique, rather than always
+ ".", but make sure it still begins with a "." to avoid a clash
+ with any other existing mounts.
+
+ * (amd.c) changed way local IP address is obtained to avoid using
+ a call to the name server. It was observed that if the power to
+ the building goes and everything reboots simultaneously then there
+ would be a good chance that the nameserver would not recover
+ before Amd on another machine required it. This happened :-) Now
+ use the RPC get_myaddress call.
+
+ * (info_hes.c) added hesiod_reload code from Bruce Cole.
+ Optionally compiled when HAS_HESIOD_RELOAD is defined.
+
+ * (amq_subr.c) fix "security" check.
+
+ * (amq.c) make sure a privileged port is allocated. In fact
+ RPC3.9 and newer make this guarentee but older versions don't so
+ we do it here.
+
+Sun Dec 2 21:30:07 1990 Jan-Simon Pendry (jsp at achilles)
+
+ * (afs_ops.c) fixed problem with pointer pre-increment.
+
+Mon Nov 19 00:31:28 1990 Jan-Simon Pendry (jsp at achilles)
+
+ * (nfs_ops.c) saved filehandle with mntfs structure. this allows
+ nfsx unmounts to be undone even if the filehandle cache has lost
+ the entry. all of this is bogus and deserves a rewrite...
+
+Sun Nov 18 22:55:43 1990 Jan-Simon Pendry (jsp at achilles)
+
+ * (nfsx_ops.c) now handles mounts of root filesystem correctly.
+
+ * (afs_ops.c) fixed dfs_ops definition to call toplvl_mounted when
+ done, so making sure that a map cache reference is set up.
+
+Sun Nov 11 21:09:34 1990 Jan-Simon Pendry (jsp at achilles)
+
+ * (fsinfo/wr_fstab.c) has per-ostype fstab rules.
+
+ * (fsinfo/fsi_lex.l) now works correctly with flex.
+
+Mon Nov 5 00:08:30 1990 Jan-Simon Pendry (jsp at achilles)
+
+ * Release 5.3 Alpha 8. No more new features from now to 5.3Rel.
+ Call next one Beta.
+
+ * (amdref.texinfo) more updates.
+
+ * (info_union.c) reload routine now adds a wildcard pointing to
+ the last named directory so that new files get created correctly.
+
+Sun Nov 4 22:02:50 1990 Jan-Simon Pendry (jsp at achilles)
+
+ * (os-u4_0.h) Ultrix 4.0 support merged.
+
+ * (os-utek.h) Utek 4.0 support merged.
+
+ * (amdref.texinfo) fixed & updated.
+
+ * (nfsx_ops.c) reworked string munging to prevent ..//.. strings
+ from occuring.
+
+ * (util.c) am_mounted now correctly updates the am_node for
+ duplicate mounts.
+
+ * (afs_ops.c) added union fstype. derived from "toplvl" except it
+ causes all the filesystems to be mounted. cannot be used for
+ filesystem types whose mounts may be defered (eg nfs) since it
+ doesn't retry the mounts.
+
+ * (info_union.c) map type for union fstype support. currently
+ can't handle being given automounted filesystems - causes a
+ deadlock.
+
+Sun Oct 21 22:56:16 1990 Jan-Simon Pendry (jsp at achilles)
+
+ * (fsinfo/*) finally integrated the fsinfo package. currently no
+ documentation or man page. this is a pre-req to getting mmd up.
+
+Sun Oct 14 20:02:11 1990 Jan-Simon Pendry (jsp at achilles)
+
+ * (amdref.texinfo) reworking of documentation continues.
+
+ * (clock.c) reschedule_timeouts() now called in the event that the
+ system clock goes backwards. this is possible during the average
+ bootstrap juggling act with timed/xntpd etc. especially useful if
+ your machines TOY clock gets way ahead of time.
+
+Sat Oct 6 19:57:55 1990 Jan-Simon Pendry (jsp at achilles)
+
+ * (map.c) when expanding the filehandle for a server which is
+ down, don't update the ttl on the original node unless a new node
+ gets allocated. this should give the original mount a chance to
+ go away as soon as the server comes back.
+
+ * (arch, os-type) Updated.
+
+ * (nfs_ops.c) Added "noconn", "spongy" and "compress" mount
+ options for 4.4 BSD.
+
+Sun Sep 30 18:37:54 1990 Jan-Simon Pendry (jsp at beauty)
+
+ * Release 5.3 Alpha 6.
+
+ * (util.c) domain_strip now doesn't leave partial domains behind.
+ if it can't strip the complete domain it leaves the string untouched.
+
+ * (restart.c) remember to initialise opt_opts field from mtab.
+
+ * (nfs_ops.c) supports "nocto" option for SunOS4.1.
+
+ * (os-irix.h) new SGI Iris port.
+
+ * (os-next.h) new NeXT port.
+
+ * (os-dgux.h) new DG/UX port.
+
+ * (many) fixed problem where mf_opts was being used for two
+ different purposes. Now have two fields.
+
+ * (mapc.c) error map prints error message whenever used.
+
+ * (mapc.c) wildcard code rewritten.
+
+ * (util.c) slpit into two parts - some code now in xutil.c. This
+ is used by mmd (not yet shipped).
+
+Sun Aug 19 19:58:16 1990 Jan-Simon Pendry (jsp at achilles)
+
+ * (srvr_nfs.c) reduce verbosity of "nfs server yoyo is ok" messages.
+
+ * (opts.c) fix deslashification in expand.
+
+ * (nfs_start.c) amq registering done just before the server kicks
+ in. running amd with nothing to do leaves the portmapper in peace.
+
+ * (mapc.c) bootstrap code abstracted to allow AMQ_MOUNT entry point.
+
+ * (nfsx_ops.c) new filesystem type, supporting groups of nfs
+ mounts. needs abstracting to allow groups of (*) mounts.
+
+ * (am.h, *_ops.c) am_ops structure changed; corresponding changes
+ in the filesystem implemention source. Change was to allow nfsx
+ filesystem implementation.
+
+ * (amd.c) hostname defaults to "localhost" startup code re-ordered
+ so that logging still works in case things go wrong early.
+
+ * (am_ops.c) new routine to print list of available fs types; used
+ by the -v option.
+
+ * (info_file.c) bug fix to make reloads work correctly.
+
+ * (mtab_file.c) does locking on single write, to avoid trashing
+ mount table when a mount and unmount are done at the same time.
+
+ * (mount_fs.c) automount hack removed since afs_ops no longer
+ needs it.
+
+ * (afs_ops.c) split "auto" into several other filesystem types.
+ Now much cleaner.
+
+ * (amq.c) new -M option.
+
+ * (amq_subr.c) support for AMQ_MOUNT added.
+
+ * (amq.x) new AMQ_MOUNT RPC call allows mount map entries to be
+ passed in at run-time. Automount points can now be added
+ dynamically, but not yet deleted.
+
+Sat Jun 23 22:12:48 1990 Jan-Simon Pendry (jsp at beauty)
+
+ * Release 5.2 for Berkeley.
+
+ * (*) Re-organised source code layout. Now much more complicated.
+
+ * (map.c) code which flushed the kernel name cache is not really
+ needed now that the modify times on the automount directories are
+ correctly updated so ifdef the whole lot. Remove later...
+
+ * (map.c) make sure that the automount directories modify times
+ are updated when a change occurs so that the nfs client code can
+ decide when to update its name cache.
+
+ * (srvr_nfs.c) fixed bug which caused mounted filesystems to
+ appear down when they were up.
+
+ * YP becomes NIS.
+
+Mon May 28 19:50:34 1990 Jan-Simon Pendry (jsp at achilles)
+
+ * (amd.tex) substantially updated with more explanation of the
+ theory and more examples.
+
+ * (nfs_stubs.c) statfs now claims to have a single used block.
+ Avoids ambiguity between 100% and 0% full.
+
+ * (nfs_stubs.c) changed to allow the size of symlinks optionally
+ to be accurate wrt the length of the string returned. Optional
+ because it is cheaper to ignore the length when doing a getattr
+ and just send any length for the readlink. However, this breaks
+ on some systems (e.g. Ultrix).
+
+ * (mount_fs.c) automount points get marked type "nfs" instead of
+ "ignore". This is to fix an interaction with getwd(). Can go
+ away when getwd() gets re-written. Really only applies to SunOS4
+ but change applies to everything to keep consistent across platforms.
+
+ * (mount_fs.c) abstracted out mount options into a table and
+ corresponding loop.
+
+ * (srvr_nfs.c) make sure portmap information is available when
+ needed, not just after a ping has succeeded. This needed changing
+ after the ping algorithm got changed.
+
+ * (mntfs.c) fixed incorrect reference to mf_error instead of mf_flags.
+
+ * (nfs_start.c) keep track of number of fds in use, so don't run
+ select on system maximum (which is bad news if you have a large
+ system maximum).
+
+ * (host_ops.c) new NFS tree mount filesystem type (ala Sun -hosts).
+
+ * (nfs_ops.c) abstracted out NFS mount code to support host_ops.
+
+ * (afs_ops.c) dfs_readlink now returns the am_node, instead of the
+ link. This allows getattr to return the correct set of attributes
+ so keeping Ultrix happy.
+
+ * (afs_ops.c) Make certain the hostname field given to mount()
+ does not get too long - otherwise random EINVAL errors occur.
+
+ * Putting closing comments on all #endif's
+
+Sun May 13 16:07:21 1990 Jan-Simon Pendry (jsp at achilles)
+
+ * (srvr_nfs.c) second rewrite of NFS ping algorithm.
+
+Sun Apr 29 21:12:33 1990 Jan-Simon Pendry (jsp at achilles)
+
+ * (nfs_stubs.c) re-arranged readlink code to avoid need to
+ pre-mount a direct filesystem whenever possible.
+
+ * (host_ops.c) finally incorporated new module to support /net
+ mount point.
+
+Sat Mar 24 13:18:47 1990 Jan-Simon Pendry (jsp at achilles)
+
+ * (nfs_stubs.c) workaround added to rename entry point to avoid
+ arguments with NFS client code.
+
+ * (srvr_nfs.c) changed the way NFS pings are done and cleaned up
+ the process for deciding when a server is up or down. Now there
+ is just a simple time limit on a reply from the server. The limit
+ is adjusted depending on whether the state of the server is known.
+
+ * (opts.c) fixed bug with ${var/} expansion et al. Added ${varN}
+ N = 0..7 for use as scratch variables.
+
+ * (mntfs.c) fixed bug in dup_mntfs().
+
+Thu Jan 11 16:56:41 1990 Jan-Simon Pendry (jsp at achilles)
+
+ * Release 5.1c.
+
+ * (amq*) has new options. -f flushes the map cache and -m prints
+ information about mounted filesystem and fileservers.
+
+Tue Jan 2 14:44:21 1990 Jan-Simon Pendry (jsp at achilles)
+
+ * (util.c) am_mounted() patches the path for "direct" mounted
+ filesystems - cosmetic.
+
+ * (afs_ops.c) when possible sets a small kernel attribute cache
+ timeout on the automount points.
+
+ * (nfs_stubs.c) delete() and rmdir() operations implemented. Used
+ when a mount point is timed out so the kernel name cache gets to
+ know about the changes. Fixes most ESTALE errors.
+
+ * (nfs_stubs.c) New do_readlink() function added. This is used to
+ make sure that a filesystem is mounted at the time a link is read
+ in the case of "direct" mounts. Done so that the length of the
+ link is available when the initial getattr is done on the mountpoint.
+
+ * (sfs_ops.c) Changed implementation to avoid race conditions.
+ The link target is re-arranged so that sublink points to the
+ target and fs always points at ".".
+
+ * Fixed mount flag bug on Ultrix.
+
+ * Added support from Sjoerd Mullender for Alliant FX/4 and Encore
+ Multimax.
+
+Thu Dec 7 17:55:29 1989 Jan-Simon Pendry (jsp at achilles)
+
+ * (afs_ops.c) dfs_readlink now does a new_ttl on the node it
+ returns.
+
+ * (afs_ops.c) next_nonerror_node now implements the task after
+ which it is named.
+
+Tue Nov 28 17:20:52 1989 Jan-Simon Pendry (jsp at achilles)
+
+ * Release 5.1b.
+
+ * (restart.c) Generates link nodes for any unrecognised filesystem
+ types and then marks them so that they are never deleted (since
+ they could never be automounted later).
+
+ * (os-*.h) Irrelevant #undef's deleted.
+
+ * (arch) Now knows about AIX on RTs.
+
+ * (amq.c) Rationalised the output. Now only gives you what you
+ asked for.
+
+ * (am.h) New macro: FSRV_ISDOWN(fs), which checks whether a
+ fileserver is down.
+
+ * (afs_ops.c) When a mount fails due to a timeout the underlying
+ filesystem is ripped away and replaced with an error fs. This
+ avoids the possibility of being left with a single error reference
+ to a valid mounted filesystem.
+
+Thu Nov 23 18:04:29 1989 Jan-Simon Pendry (jsp at achilles)
+
+ * (nfs_start.c) Re-order bootstrap sequence to avoid potential
+ deadlock if restart() ends up accessing one of the automount points.
+
+ * (amq.c) Don't produce default mount output if one of the -l, -x
+ or -D options was used.
+
+ * (umount_fs.c) Add alternative unmount routine for 4.4 BSD.
+
+Mon Nov 20 16:22:50 1989 Jan-Simon Pendry (jsp at achilles)
+
+ * (os-bsd44.h) Fixed redefinition of UMOUNT_FS.
+
+ * (info_ndbm.c) Added missing #include <sys/stat.h>.
+
+ * (mapc.c) Fixed typo in ifdef around gdbm config entry.
+
+Sat Nov 18 16:39:13 1989 Jan-Simon Pendry (jsp at achilles)
+
+ * (util.c) If "/" is automounted, make sure it is never timed out.
+
+ * (mtab.c) Missing clock invalidation added in read_mtab (from a file).
+
+ * (mntfs.c) realloc_mntfs simplified.
+
+ * (map.c) Closed a race condition during shutdown when second and
+ subsequent duplicate mounts were deleted prematurely.
+
+ * (afs_ops.c) Duplicate mounts are now given the correct return
+ code.
+
+Fri Nov 17 18:58:18 1989 Jan-Simon Pendry (jsp at achilles)
+
+ * 5.1 Release.
+
+Thu Nov 16 17:57:02 1989 Jan-Simon Pendry (jsp at achilles)
+
+ * (mntfs.c) Make sure inherit mntfs structures are not cached
+ after last reference; otherwise a second reference to the
+ inherited filesystem will get stuck on the inherit rather than the
+ (now) fully mounted filesystem.
+
+ * (am.c, nfs_start.c) After forking the server, make sure the
+ parent does not exit until the automount points are mounted. This
+ allows a clean sequence during system startup.
+
+ * Initial port to 4.4 BSD. Several new configuration abstractions
+ were added to make this port possible.
+
+Thu Nov 9 21:42:14 1989 Jan-Simon Pendry (jsp at achilles)
+
+ * (afs_ops.c, opts.c) Added map logging to facilitate mount map
+ debugging without needing a -DDEBUG version of Amd.
+
+ * (afs_ops.c) Make sure the length of the fs_hostname mount
+ parameter does not exceed MAXHOSTNAMESZ.
+
+Wed Nov 8 13:44:02 1989 Jan-Simon Pendry (jsp at achilles)
+
+ * Change the message log format to indicate the severity of the
+ message to allow simpler analysis of the log file.
+
+Tue Nov 7 14:11:36 1989 Jan-Simon Pendry (jsp at achilles)
+
+ * 5.0 Patchlevel 11.
+
+ * (os-bsd44.h) Initial guess at 4.4 BSD definitions.
+
+ * (os-aux.h) Port for Macintosh II from Julian Onions.
+
+ * (amq.c) Output formats cleaned up. AMQ_MNTTREE is still broken
+ in amq_subr.c though.
+
+ * (afs_ops.c) If a mount timed out, for example an NFS server was
+ down at the time, it was possible for the error code to remain
+ unset thus jamming that mount node in a state from which it could
+ not recover. Just make sure that the mf_error field gets filled
+ in when an error occurs.
+
+ * (afs_ops.c) strsplit is run over /defaults to avoid problems
+ with whitespace creeping in.
+
+Sun Nov 5 11:50:51 1989 Jan-Simon Pendry (jsp at achilles)
+
+ * (util.c) am_mounted: Added missing initialisation of stats.s_mtime.
+
+Fri Nov 3 17:33:02 1989 Jan-Simon Pendry (jsp at achilles)
+
+ * 5.0 Patchlevel 10.
+
+ * Changed the copyright.
+
+Thu Nov 2 17:07:53 1989 Jan-Simon Pendry (jsp at achilles)
+
+ * 5.0 Patchlevel 9.
+
+ * (opts.c) new option syntax: == != :=
+
+ * (nfs_ops.c) Less caching of filehandles. Now cached errors are
+ discarded after use.
+
+ * (mtab.c) now attempts to deal with a lack of open file slots (ENFILE).
+
+ * (mount_fs.c) automount entries in the mount table now have a
+ dev= entry in the same way as NFS and UFS.
+
+ * (mntfs.c) mntfs nodes are now cached after the last reference
+ and discarded <ALLOWED_MOUNT_TIME> seconds later. This avoids
+ thrashing during a mount.
+
+ * (mapc.c) map default cache mode is now selected with
+ "mapdefault", not "default"
+
+ * (amd.tex) numerous clarifications. Still more work required...
+
+ * (amq_subr.c) now allows the -x option of amq to operate.
+
+ * (afs_ops.c) afs_bgmount now keeps track of which filesystem
+ needed retrying and ensures that the mount node in the
+ continuation correctly points at an unmounted filesystem. This
+ fixes a problem whereby a valid mounted filesystem could appear to
+ have failed to mount.
+
+ * Configure now gives more of a running commentary and checks
+ whether os-type and arch actually worked.
+
+ * Allow spurious ';'s in a mount location.
+
+Fri Oct 27 14:03:31 1989 Jan-Simon Pendry (jsp at achilles)
+
+ * foo=blah changed to foo:=blah, foo==blah and foo!=blah.
+
+ * -l stderr changed to -l /dev/stderr.
+
+Thu Oct 19 15:34:28 1989 Jan-Simon Pendry (jsp at achilles)
+
+ * 5.0 Patchlevel 6.
+
+ * LOG_INFO messages have been rationalised so that some
+ statistics, graphs and so on can be generated.
+
+ * Transaction ID's for RPC calls are now allocated by the
+ individual callers, rather than from a central pool. This
+ decreases the load on mount daemons and NFS servers since the
+ same XID is used for retries when needed.
+
+ * Many fine details of the new data structures have been changed.
+ Some areas have been optimized.
+
+Fri Oct 13 12:31:26 1989 Jan-Simon Pendry (jsp at achilles)
+
+ * Restart code re-implemented to work with the new data structures.
+
+ * Fine tuning applied to new NFS server modeling code.
+
+Thu Oct 12 15:57:24 1989 Jan-Simon Pendry (jsp at achilles)
+
+ * Added ${/var} and ${var/} variable expansions. The first gives
+ the "basename" component of the variable, the latter gives the
+ "dirname" component. Additionally, spurious /'s are deleted after
+ the variable expansions is complete.
+
+ * Added new -C option to allow the machine's cluster name to be
+ given to amd. ${cluster} fetches the value and can be used as
+ another selector.
+
+ * Broken the major data struct (am_node) into three layers:
+ am_node (one for each automount node), mntfs (one for each mounted
+ filesystem) and fserver (one for each file server). Machine
+ up/down state is maintained in the fserver layer. Filesystem
+ mount/unmount state is maintained in the mntfs layer. This change
+ fixes the last known major problem caused by the lack of a central
+ focus for filesystem and fileserver status. There is a dummy file
+ server layer for local filesystems (ufs, link, program, error).
+
+Tue Oct 10 11:15:42 1989 Jan-Simon Pendry (jsp at achilles)
+
+ * 5.0 Patchlevel 5.
+
+ * (nfs_ops.c) the filehandle cache is now flushed when a
+ filesystem is unmounted. This avoids ending up with stale
+ information if a server bounces.
+
+ * (clock.c) new module to implement callouts. Many other
+ routines changed to use callouts instead of messing with ttl
+ fields.
+
+Sun Oct 1 17:08:20 1989 Jan-Simon Pendry (jsp at achilles)
+
+ * 5.0 Patchlevel 3 & 4.
+
+ * Numerous cleanups.
+
+Wed Sep 13 14:30:05 1989 Jan-Simon Pendry (jsp at achilles)
+
+ * 5.0 Patchlevel 2.
+
+ * (nfs_ops.c) portmap information is not remembered beyond the
+ basic filehandle cache interval. That avoids problems when a new
+ portmap and/or rpc.mountd is started and the bound port changes.
+
+ * (mapc.c) cache reloads are automatically done every hour.
+
+ * Removed xlog macro in favour of plog() so that the log level
+ can be reflected through to syslog(). log() routine renamed to
+ plog() which takes an extra parameter indicating the log level.
+
+Tue Sep 5 20:00:19 1989 Jan-Simon Pendry (jsp at achilles)
+
+ * (nfs_ops.c) when a server is known to be down, any cached file
+ handles and port mapping informaton is flushed since that may have
+ changed when it comes back up.
+
+ * (map.c) timeout no longer attempts to unmount a hung mount point.
+
+Mon Sep 4 14:49:18 1989 Jan-Simon Pendry (jsp at achilles)
+
+ * (afs_ops.c) a mount node which timed out during mount is now
+ retained for the normal timeout interval rather than for a short
+ period. This avoids wasting time retrying mounts from a server
+ which is down.
+
+ * (afs_ops.c) hung mounts are now detected and not used as a
+ duplicate mount - something which defeated the replacement fs
+ scheme.
+
+ * (nfs_ops.c) keepalive's now back-off when a server has gone
+ down.
+
+Thu Aug 31 21:18:35 1989 Jan-Simon Pendry (jsp at achilles)
+
+ * 5.0 Patchlevel 1.
+
+ * Fixed several bugs which showed up in the keepalive
+ implementation when a gateway went down causing
+ a different sequence of errors than usual.
+
+Wed Aug 30 11:29:21 1989 Jan-Simon Pendry (jsp at achilles)
+
+ * (amq.x) now uses a Sun assigned program number.
+
+ * Revision 5.0 - can now start using metaconfig.
+
+Tue Aug 29 14:36:48 1989 Jan-Simon Pendry (jsp at achilles)
+
+ * (os-u3_0.h, os-type) now knows about DECstations (mips).
+
+ * (nfs_stubs.c) Added hooks to readlink entry point to call
+ per-fs readlink routine if it exists, otherwise old behaviour.
+
+ * (afs_ops.c) Added implementation of "type=direct". This is
+ the same as "type=auto" but is itself the link to the
+ mount point, rather than being a directory containing a list
+ of links to mount points.
+
+Mon Aug 28 17:48:15 1989 Jan-Simon Pendry (jsp at achilles)
+
+ * (afs_ops.c) Changed readdir to workaround a problem on
+ ultrix 3 where it seems to forget that eof has been reached.
+
+Thu Aug 24 15:17:55 1989 Jan-Simon Pendry (jsp at achilles)
+
+ * Created "beta16".
+
+ * (afs_ops.c) /defaults is located along with every key.
+ this makes it possible to update the /defaults in
+ a map and get to use it.
+
+ * (mapc.c) added map cache synchronization support. if
+ a file or ndbm map is updated the cache is junked so avoiding
+ things getting out of sync.
+
+Wed Aug 23 19:17:52 1989 Jan-Simon Pendry (jsp at achilles)
+
+ * (os-u3_0.h) new file to support Ultrix 3.0
+
+ * (opts.c) allow environment variables to be accessed via
+ the same ${env} syntax used for options & selectors.
+
+Tue Aug 22 13:19:49 1989 Jan-Simon Pendry (jsp at achilles)
+
+ * (opts.c, get_args.c) added support for kernel architecture
+ type to allow /usr/kvm to be automounted under SunOS 4.x.
+
+ * (os-xinu43.h) updated for june '89 release of MORE/bsd.
+
+ * (opts.c) fixed memory allocation problems where some strings
+ may not have been strdup'ed before they were free'ed so causing
+ the malloc arena to get into a twist. This caused core dumps on
+ some machines and infinite loops on others.
+
+ * (*.c) clock handling is now done by a macro. Global variable
+ clock_valid is > 0 (ie the time) when valid, 0 if invalid.
+
+ * (map.c) timeout code survived a complete rewrite and is now
+ O(n) rather than O(n^2).
+
+ * (info_hes.c) new database hooks for Hesiod nameserver.
+
+ * (get_args.c) the local sub-domain is picked up from the
+ hostname if it is not specifed with -d. The subdomain is
+ then stripped from the hostname.
+
+ * (am.c) when a SIGTERM is received, an immediate abort
+ occurs - only the top-level automounts are unmounted; all
+ other mounts stay -- use amd -r to restart.
+
+ * (afs_ops.c) cleaned up key prefix handling. Again updated
+ the "hostname" string passed to the kernel so that includes
+ the hostname, pid and mount point.
+
+Tue Aug 8 16:05:23 1989 Jan-Simon Pendry (jsp at achilles)
+
+ * (nfs_ops.c) changed the way the file handle cache is managed.
+ No longer gets a race condition between something entering the
+ cache and being used and discard.
+
+Tue Jul 25 20:40:51 1989 Jan-Simon Pendry (jsp at achilles)
+
+ * (map.c) changed fh_to_mp2 so that it does not return
+ ESTALE during shutdown. it returns ENOENT instead which
+ avoids thrashing with the kernel.
+
+Sun Jul 23 15:06:10 1989 Jan-Simon Pendry (jsp at achilles)
+
+ * (afs_ops.c) make sure the incoming key from the kernel
+ does not contain any characters which could cause trouble
+ during macro expansion (such as `"! etc).
+
+ * (afs_ops.c) fixed contruction of "mtab" entry.
+
+Fri Jul 21 11:01:05 1989 Jan-Simon Pendry (jsp at achilles)
+
+ * (afs_ops.c) some changes to support the new startup
+ shutdown scheme.
+
+ * (map.c) startup and shutdown are now done using the
+ standard interfaces. Startup is done by creating a
+ private cache map ";root;" and then doing lookups
+ on all the names in it. Shutdown is done by forcibly
+ timing out all the mount points including the automount
+ points.
+
+ * (info_*.c) modified to provide interface required by
+ mapc.c module.
+
+ * (mapc.c) new module to implement map caching. Caching
+ can be controlled by an fs option. "all" means cache
+ the entire map (if possible). "inc" means cache things
+ incrementally. "none" means never cache things. Each
+ map type has a default caching mode which is used if
+ cache option "default" is used.
+
+Wed Jul 19 16:14:52 1989 Jan-Simon Pendry (jsp at achilles)
+
+ * (sched.c) implements a general sleep/wakeup scheme and uses
+ it for sub-process handling.
+
+ * (nfs_start.c) task_notify() called from where it used to
+ be called.
+
+ * (nfs_ops.c) now implements a non-blocking rpc library.
+ Everything in nfs_ops was changed to use it. This should
+ not be in this file and will be moved later.
+
+ * (map.c) if a mount point times out and it is deferred then
+ issue a wakeup so that it can be retried.
+
+ * (map.c) when creating a new mount point fetches the entry
+ "/defaults" from the associated map if no other options are
+ specified.
+
+ * (am.c) implements the -p (print process id) option.
+
+ * (afs_ops.c) a mount attempt now has a time-to-live of twenty
+ seconds. if only deferred attempts are waiting after that
+ interval the kernel gets sent ETIMEDOUT.
+
+ * (afs_ops.c) the name by which the kernel knows the filesystem
+ has changed from pid%d@host to /mountpoint@host. That looks
+ better to users who get hit by it.
+
+Fri Jul 14 18:46:16 1989 Jan-Simon Pendry (jsp at achilles)
+
+ * (afs_ops.c) now knows about defered mounts - mounts which
+ are not in progress, not completed, and not failed.
+
+ * (sched.c) added new entry point sched_ast(). This simulates
+ a completed job. The basic idea is to let something else return
+ to the main scheduling loop with a guarentee that it will be
+ called back when some other action has taken place.
+
+ * (nfs_ops.c) implemented a file handle cache. The nfs_init
+ routine starts up a request for the filehandle and the mount
+ routine uses it when it arrives.
+
+Thu Jul 13 18:07:58 1989 Jan-Simon Pendry (jsp at achilles)
+
+ * (afs_ops.c) found a race condition between an error occuring
+ and the am_node being timed out. Fixed by updating the
+ time-to-live and keepalive counters in the node whenever
+ AMF_MOUNTING is cleared. Also changed afs_lookuppn() so that
+ it doesn't destroy the node when it returns the error code.
+ This stops thrashing and the node is eventually timed out.
+ Now the only way a node gets deleted is by the timeout code
+ which seems more elegant.
+
+Tue Jul 11 15:36:44 1989 Jan-Simon Pendry (jsp at achilles)
+
+ * Created "beta15".
+
+ * Fixed *all* references to "u2.2". Some where missed in
+ the original change. They are now u2_2.
+
+ * (mk-amd-map.c) new command. Converts plain files into
+ ndbm databases for use by the info_ndbm module. Hooks
+ included for future support for gdbm just as soon as I
+ can get a copy.
+
+Sun Jul 9 19:00:22 1989 Jan-Simon Pendry (jsp at achilles)
+
+ * Created "beta14".
+
+ * (get_info.c) code to handle yp and files now split into
+ new files info_yp.c and info_file.c New support for ndbm
+ databases is in info_ndbm.c. A table in get_info.c controls
+ what and in which order things are searched.
+
+ * (map.c, nfs_stubs.c) better handling for hung mount points.
+ if a filehandle is passed in by the kernel which references
+ a hung node, then try to find a replacement, possibly by calling
+ lookup explicitly.
+
+ * (*.c) use new xlog(level)(message) interface
+
+Thu Jun 8 20:28:55 1989 Jan-Simon Pendry (jsp at achilles)
+
+ * (nfs_ops.c, ufs_ops.c) when compiled with DEBUG, display
+ the fs options being used.
+
+ * (am.c) make test for root a little more polite.
+
+ * (get_args.c) update Usage message to include -r option.
+
+Wed Jun 7 16:28:51 1989 Jan-Simon Pendry (jsp at achilles)
+
+ * (rpc_fwd.c) fwd_reply: if the recvfrom call fails because it
+ is interrupted then try again.
+
+Tue Jun 6 16:39:15 1989 Jan-Simon Pendry (jsp at achilles)
+
+ * Created "beta12".
+
+ * (afs_ops.c) inheriting mount option list from command line
+ is now cumulative. A -foo on the command line is prepended
+ to the default option list taken from the map. This can be
+ used to override the ``default default'' options in opts.c.
+
+ * (get_args.c, am.c) added new -r (restart) option. Restart of
+ mounted filesystems is only done if this option is specified.
+ [Should *not* be specified in /etc/rc.local of course. - wrong]
+
+ * (yp_master.c) make the enumeration error message more verbose
+ when debugging is enabled.
+
+ * (rpc_fwd.c) rearranged some declarations at the top. Removed
+ a spurious call to free which was causing grief on some systems,
+ but not on Sun's. [This problem was the reason for implementing
+ the -D mem option.]
+
+ * (opts.c) make sure opt_key and opt_path are set to a zero
+ length string unless otherwise specified. Previously they
+ were a source of dangling pointers.
+
+ * (nfs_ops.c) make sure that the allocated nfs_private identifiers
+ are unique even when some filesystem are being restarted. This mean
+ starting the basic allocation from 1, not zero.
+
+ * (am.h, get_args.c, util.c) added definition and implmentation of
+ a simple memory allocation trace (D_MEM).
+
+ * (afs_ops.c) afs_lookuppn: tightened up memory allocation and
+ delay string copying until last possible moment.
+
+Mon Jun 5 18:01:18 1989 Jan-Simon Pendry (jsp at achilles)
+
+ * (Makefile.com) diffs: added new rule to generate diffs
+ between versions.
+
+ * (get_info.c) search_file: added a new dlog() to note when
+ wildcards are returned.
+
+ * (afs_ops.c) afs_lookuppn: call to init_map specifies efs as
+ the default ops structure. If the location list only contained
+ defaults and no real mounts then this previously caused a null
+ pointer dereference.
+
+ * (map.c) last_used_map: Added new variable. Keeps track of the
+ last used map, which may be wildly different from first_free_map.
+ This fixes bugs in several routines in this file.
+
+ * (util.c) mkdirs, rmdirs: Changed directory make/unmake. It is
+ not possible to quickly determine how many directories need to
+ be created or deleted, so we try to make as many as possible.
+
+ * (opts.c) Added default values for rfs, rhost and fs.
+ The new defaults guarentee unique names to allow the NFS
+ keepalive stuff to work.
+
+Sun Jun 4 16:12:15 1989 Jan-Simon Pendry (jsp at achilles)
+
+ * First draft of documentation included in the next release.
+
+ * Hooks for TFS added, though this still requires a lot of work.
+
+ * Re-implemented option handling. Options are now allocated
+ dynamically on a per-mount basis in the continuation structure.
+
+ * Changed os type u2.2 to u2_2 to allow for regular expression
+ matching in selectors.
+
+ * Format of mount maps is now entirely different. Instead of
+ guessing which filesystem type is being used, it is now explicitly
+ stated along with the required options. Variable expansion is
+ done on the options and selectors are also implemented. The
+ requested name can also contain any of the selectors.
+
+Wed May 24 15:21:39 1989 Jan-Simon Pendry (jsp at achilles)
+
+ * Re-implemented NFS ping algorithm to use the new RPC forwarding
+ system. This allowed a large amount of nfs_ops specific code
+ to be removed from nfs_start.c and moved to nfs_ops.c.
+ There is still no strategy for hung file systems. At the moment
+ it will merely try to mount an alternative (or the same again)
+ to the same place in the file system.
+
+ * Added RPC forwarding package. This supports general RPC gatewaying
+ over a UDP transport. The idea is to put a packet identifier into
+ each outgoing RPC packet and then match that up in a database when
+ a reply comes in. The database records the original packet identifier
+ (so that it can be replaced), the source address for the packet and
+ a function to call to do the forwarding.
+
+ * ChangeLog added between beta8 and beta9. Should have done this sooner.
diff --git a/usr.sbin/amd/amd/Makefile b/usr.sbin/amd/amd/Makefile
new file mode 100644
index 0000000..89762eb
--- /dev/null
+++ b/usr.sbin/amd/amd/Makefile
@@ -0,0 +1,59 @@
+# @(#)Makefile 8.2 (Berkeley) 4/22/94
+
+PROG= amd
+MAN8= amd.8
+OS= bsd44
+SRCS= afs_ops.c am_ops.c clock.c util.c xutil.c \
+ efs_ops.c mapc.c info_file.c info_hes.c \
+ info_ndbm.c info_passwd.c info_nis.c \
+ info_union.c map.c srvr_afs.c srvr_nfs.c \
+ mntfs.c misc_rpc.c mount_fs.c mount_xdr.c \
+ mtab.c mtab_bsd.c nfs_ops.c nfs_prot_svc.c \
+ nfs_start.c nfs_subr.c nfs_prot_xdr.c opts.c \
+ pfs_ops.c rpc_fwd.c sched.c sfs_ops.c amq_svc.c \
+ amq_subr.c umount_fs.c host_ops.c nfsx_ops.c \
+ ufs_ops.c ifs_ops.c amd.c get_args.c restart.c wire.c
+OBJS+= vers.${PROG}.o
+CFLAGS+=-I${.OBJDIR}
+CFLAGS+=-I${.CURDIR}/../rpcx
+CFLAGS+=-I${.CURDIR}/../config
+CFLAGS+=-I${.CURDIR}/../include
+CFLAGS+=-DARCH_REP=\"${MACHINE}\"
+CFLAGS+=-DOS_REP=\"${OS}\"
+CFLAGS+=-DOS_HDR=\"os-${OS}.h\"
+CFLAGS+=${CONFIG}
+CFLAGS+=-D_NEW_VFSCONF
+LDADD= ${XLIBDIR} ${RESOLV}
+CLEANFILES+=vers.${PROG}.c vers.${PROG}.o version.amd
+CLEANFILES+=mount.h mount_xdr.c nfs_prot.h nfs_prot_xdr.c nfs_prot_svc.c
+RPCCOM = rpcgen
+MOUNT_X= ${DESTDIR}/usr/include/rpcsvc/mount.x
+NFS_PROT_X= ${DESTDIR}/usr/include/rpcsvc/nfs_prot.x
+DPSRCS= nfs_prot.h
+
+vers.${PROG}.c: ${SRCS:.c=.o}
+ @d=${.CURDIR}/ sh ${.CURDIR}/../config/newvers.sh ${PROG} ${MACHINE} ${OS}
+
+afs_ops.o host_ops.o nfs_ops.o srvr_nfs.o: mount.h
+
+mount.h: ${MOUNT_X}
+ ${RPCCOM} -h -DWANT_NFS3 ${MOUNT_X} -o ${.TARGET}
+
+mount_xdr.c: mount.h ${MOUNT_X}
+ ${RPCCOM} -c -DWANT_NFS3 ${MOUNT_X} -o ${.TARGET}
+
+beforedepend: nfs_prot.h
+
+nfs_prot.h: ${NFS_PROT_X}
+ ${RPCCOM} -h ${NFS_PROT_X} -o ${.TARGET}
+
+nfs_prot_xdr.c: ${NFS_PROT_X}
+ ${RPCCOM} -c ${NFS_PROT_X} -o ${.TARGET}
+
+nfs_prot_svc.c: ${NFS_PROT_X}
+ ${RPCCOM} -m ${NFS_PROT_X} -o ${.TARGET}
+
+.PATH: ${.CURDIR}/../rpcx ${.CURDIR}/../config
+.include "Makefile.config"
+.include "../../Makefile.inc"
+.include <bsd.prog.mk>
diff --git a/usr.sbin/amd/amd/afs_ops.c b/usr.sbin/amd/amd/afs_ops.c
new file mode 100644
index 0000000..f84edb2
--- /dev/null
+++ b/usr.sbin/amd/amd/afs_ops.c
@@ -0,0 +1,1819 @@
+/*
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)afs_ops.c 8.1 (Berkeley) 6/6/93
+ *
+ * $Id$
+ *
+ */
+
+#include "am.h"
+
+#define NFS
+#define NFSCLIENT
+
+#include <sys/stat.h>
+#ifdef NFS_3
+typedef nfs_fh fhandle_t;
+#endif /* NFS_3 */
+#ifdef NFS_HDR
+#include NFS_HDR
+#endif /* NFS_HDR */
+#include <sys/mount.h>
+#include "mount.h"
+
+/*
+ * Automount file system
+ * Direct file system
+ * Root file system
+ * Top-level file system
+ */
+
+/*
+ * Interval between forced retries of a mount.
+ */
+#define RETRY_INTERVAL 2
+
+/*
+ * AFS needs nothing in particular.
+ */
+static char *afs_match P((am_opts *fo));
+static char *afs_match(fo)
+am_opts *fo;
+{
+ char *p = fo->opt_rfs;
+ if (!fo->opt_rfs) {
+ plog(XLOG_USER, "auto: no mount point named (rfs:=)");
+ return 0;
+ }
+ if (!fo->opt_fs) {
+ plog(XLOG_USER, "auto: no map named (fs:=)");
+ return 0;
+ }
+ /*
+ * Swap round fs:= and rfs:= options
+ * ... historical (jsp)
+ */
+ fo->opt_rfs = fo->opt_fs;
+ fo->opt_fs = p;
+ /*
+ * mtab entry turns out to be the name of the mount map
+ */
+ return strdup(fo->opt_rfs ? fo->opt_rfs : ".");
+}
+
+/*
+ * Mount an automounter directory.
+ * The automounter is connected into the system
+ * as a user-level NFS server. mount_toplvl constructs
+ * the necessary NFS parameters to be given to the
+ * kernel so that it will talk back to us.
+ */
+static int mount_toplvl P((char *dir, char *opts));
+static int mount_toplvl(dir, opts)
+char *dir;
+char *opts;
+{
+ struct nfs_args nfs_args;
+ struct mntent mnt;
+ int retry;
+ struct sockaddr_in sin;
+ unsigned short port;
+ int flags;
+ extern nfs_fh *root_fh();
+ nfs_fh *fhp;
+ char fs_hostname[MAXHOSTNAMELEN+MAXPATHLEN+1];
+
+ MTYPE_TYPE type = MOUNT_TYPE_NFS;
+
+ bzero((voidp) &nfs_args, sizeof(nfs_args)); /* Paranoid */
+
+ mnt.mnt_dir = dir;
+ mnt.mnt_fsname = pid_fsname;
+ mnt.mnt_type = MNTTYPE_AUTO;
+ mnt.mnt_opts = opts;
+ mnt.mnt_freq = 0;
+ mnt.mnt_passno = 0;
+
+ retry = hasmntval(&mnt, "retry");
+ if (retry <= 0)
+ retry = 2; /* XXX */
+
+ /*
+ * get fhandle of remote path for automount point
+ */
+
+ fhp = root_fh(dir);
+ if (!fhp) {
+ plog(XLOG_FATAL, "Can't find root file handle for %s", dir);
+ return EINVAL;
+ }
+
+ NFS_FH_DREF(nfs_args.fh, (NFS_FH_TYPE) fhp);
+#ifdef NFSv3
+ nfs_args.fhsize = FHSIZE;
+#endif
+
+ /*
+ * Create sockaddr to point to the local machine. 127.0.0.1
+ * is not used since that will not work in HP-UX clusters and
+ * this is no more expensive.
+ */
+ bzero((voidp) &sin, sizeof(sin));
+ sin.sin_family = AF_INET;
+ sin.sin_addr = myipaddr;
+ if (port = hasmntval(&mnt, "port")) {
+ sin.sin_port = htons(port);
+ } else {
+ plog(XLOG_ERROR, "no port number specified for %s", dir);
+ return EINVAL;
+ }
+
+ /*
+ * set mount args
+ */
+ NFS_SA_DREF(nfs_args, &sin);
+
+ /*
+ * Make a ``hostname'' string for the kernel
+ */
+#ifndef HOSTNAMESZ
+#define SHORT_MOUNT_NAME
+#endif /* HOSTNAMESZ */
+#ifdef SHORT_MOUNT_NAME
+ sprintf(fs_hostname, "amd:%d", foreground ? mypid : getppid());
+#else
+ sprintf(fs_hostname, "pid%d@%s:%s", foreground ? mypid : getppid(), hostname, dir);
+#endif /* SHORT_MOUNT_NAME */
+ nfs_args.hostname = fs_hostname;
+ nfs_args.flags |= NFSMNT_HOSTNAME;
+#ifdef HOSTNAMESZ
+ /*
+ * Most kernels have a name length restriction.
+ */
+ if (strlen(fs_hostname) >= HOSTNAMESZ)
+ strcpy(fs_hostname + HOSTNAMESZ - 3, "..");
+#endif /* HOSTNAMESZ */
+
+#ifdef NFSMNT_DUMBTIMR
+ nfs_args.flags |= NFSMNT_DUMBTIMR;
+ plog(XLOG_INFO, "defeating nfs window computation");
+#endif
+
+ /*
+ * Parse a subset of the standard nfs options. The
+ * others are probably irrelevant for this application
+ */
+ if (nfs_args.timeo = hasmntval(&mnt, "timeo"))
+ nfs_args.flags |= NFSMNT_TIMEO;
+
+ if (nfs_args.retrans = hasmntval(&mnt, "retrans"))
+ nfs_args.flags |= NFSMNT_RETRANS;
+
+#ifdef NFSMNT_BIODS
+ if (nfs_args.biods = hasmntval(&mnt, "biods"))
+ nfs_args.flags |= NFSMNT_BIODS;
+
+#endif /* NFSMNT_BIODS */
+
+#if defined(NFSMNT_ACREGMIN) && defined(NFSMNT_ACREGMAX)
+ /*
+ * Don't cache attributes - they are changing under
+ * the kernel's feet...
+ */
+ nfs_args.acregmin = nfs_args.acregmax = 1;
+ nfs_args.flags |= NFSMNT_ACREGMIN|NFSMNT_ACREGMAX;
+#endif /* defined(NFSMNT_ACREGMIN) && defined(NFSMNT_ACREGMAX) */
+ /*
+ * These two are constructed internally by the calling routine
+ */
+ if (hasmntopt(&mnt, MNTOPT_SOFT) != NULL)
+ nfs_args.flags |= NFSMNT_SOFT;
+
+#ifdef MNTOPT_INTR
+ if (hasmntopt(&mnt, MNTOPT_INTR) != NULL)
+ nfs_args.flags |= NFSMNT_INT;
+#endif /* MNTOPT_INTR */
+
+ flags = compute_mount_flags(&mnt);
+#ifdef ULTRIX_HACK
+ nfs_args.gfs_flags = flags;
+ flags &= M_RDONLY;
+ if (flags & M_RDONLY)
+ nfs_args.flags |= NFSMNT_RONLY;
+#endif /* ULTRIX_HACK */
+ return mount_fs(&mnt, flags, (caddr_t) &nfs_args, retry, type);
+}
+
+static void afs_mkcacheref P((mntfs *mf));
+static void afs_mkcacheref(mf)
+mntfs *mf;
+{
+ /*
+ * Build a new map cache for this node, or re-use
+ * an existing cache for the same map.
+ */
+ char *cache;
+ if (mf->mf_fo && mf->mf_fo->opt_cache)
+ cache = mf->mf_fo->opt_cache;
+ else
+ cache = "none";
+ mf->mf_private = (voidp) mapc_find(mf->mf_info, cache);
+ mf->mf_prfree = mapc_free;
+}
+
+/*
+ * Mount the root...
+ */
+static int root_mount P((am_node *mp));
+static int root_mount(mp)
+am_node *mp;
+{
+ mntfs *mf = mp->am_mnt;
+
+ mf->mf_mount = strealloc(mf->mf_mount, pid_fsname);
+ mf->mf_private = (voidp) mapc_find(mf->mf_info, "");
+ mf->mf_prfree = mapc_free;
+
+ return 0;
+}
+
+/*
+ * Mount a sub-mount
+ */
+static int afs_mount P((am_node *mp));
+static int afs_mount(mp)
+am_node *mp;
+{
+ mntfs *mf = mp->am_mnt;
+
+ /*
+ * Pseudo-directories are used to provide some structure
+ * to the automounted directories instead
+ * of putting them all in the top-level automount directory.
+ *
+ * Here, just increment the parent's link count.
+ */
+ mp->am_parent->am_fattr.nlink++;
+ /*
+ * Info field of . means use parent's info field.
+ * Historical - not documented.
+ */
+ if (mf->mf_info[0] == '.' && mf->mf_info[1] == '\0')
+ mf->mf_info = strealloc(mf->mf_info, mp->am_parent->am_mnt->mf_info);
+ /*
+ * Compute prefix:
+ *
+ * If there is an option prefix then use that else
+ * If the parent had a prefix then use that with name
+ * of this node appended else
+ * Use the name of this node.
+ *
+ * That means if you want no prefix you must say so
+ * in the map.
+ */
+ if (mf->mf_fo->opt_pref) {
+ /*
+ * the prefix specified as an option
+ */
+ mp->am_pref = strdup(mf->mf_fo->opt_pref);
+ } else {
+ /*
+ * else the parent's prefix
+ * followed by the name
+ * followed by /
+ */
+ char *ppref = mp->am_parent->am_pref;
+ if (ppref == 0)
+ ppref = "";
+ mp->am_pref = str3cat((char *) 0, ppref, mp->am_name, "/");
+ }
+
+ /*
+ * Attach a map cache
+ */
+ afs_mkcacheref(mf);
+
+ return 0;
+}
+
+/*
+ * Mount the top-level
+ */
+static int toplvl_mount P((am_node *mp));
+static int toplvl_mount(mp)
+am_node *mp;
+{
+ mntfs *mf = mp->am_mnt;
+ struct stat stb;
+ char opts[256];
+ int error;
+ char *mnttype;
+
+ /*
+ * Mounting the automounter.
+ * Make sure the mount directory exists, construct
+ * the mount options and call the mount_toplvl routine.
+ */
+
+ if (stat(mp->am_path, &stb) < 0) {
+ return errno;
+ } else if ((stb.st_mode & S_IFMT) != S_IFDIR) {
+ plog(XLOG_WARNING, "%s is not a directory", mp->am_path);
+ return ENOTDIR;
+ }
+
+ if (mf->mf_ops == &toplvl_ops) mnttype = "indirect";
+ else if (mf->mf_ops == &dfs_ops) mnttype = "direct";
+#ifdef HAS_UNION_FS
+ else if (mf->mf_ops == &union_ops) mnttype = "union";
+#endif
+ else mnttype = "auto";
+
+ /*
+ * Construct some mount options
+ */
+ sprintf(opts,
+#ifdef MNTOPT_INTR
+ "%s,%s,%s=%d,%s=%d,%s=%d,%s",
+ MNTOPT_INTR,
+#else
+ "%s,%s=%d,%s=%d,%s=%d,%s",
+#endif /* MNTOPT_INTR */
+ "rw",
+ "port", nfs_port,
+ "timeo", afs_timeo,
+ "retrans", afs_retrans,
+ mnttype);
+
+ error = mount_toplvl(mf->mf_mount, opts);
+ if (error) {
+ errno = error;
+ plog(XLOG_FATAL, "mount_toplvl: %m");
+ return error;
+ }
+
+ return 0;
+}
+
+static void toplvl_mounted P((mntfs *mf));
+static void toplvl_mounted(mf)
+mntfs *mf;
+{
+ afs_mkcacheref(mf);
+}
+
+#ifdef HAS_UNION_FS
+/*
+ * Create a reference to a union'ed entry
+ */
+static int create_union_node P((char *dir, voidp arg));
+static int create_union_node(dir, arg)
+char *dir;
+voidp arg;
+{
+ if (strcmp(dir, "/defaults") != 0) {
+ int error = 0;
+ (void) toplvl_ops.lookuppn(arg, dir, &error, VLOOK_CREATE);
+ if (error > 0) {
+ errno = error; /* XXX */
+ plog(XLOG_ERROR, "Could not mount %s: %m", dir);
+ }
+ return error;
+ }
+ return 0;
+}
+
+static void union_mounted P((mntfs *mf));
+static void union_mounted(mf)
+mntfs *mf;
+{
+ int i;
+
+ afs_mkcacheref(mf);
+
+ /*
+ * Having made the union mount point,
+ * populate all the entries...
+ */
+ for (i = 0; i <= last_used_map; i++) {
+ am_node *mp = exported_ap[i];
+ if (mp && mp->am_mnt == mf) {
+ /* return value from create_union_node is ignored by mapc_keyiter */
+ (void) mapc_keyiter((mnt_map *) mp->am_mnt->mf_private,
+ (void (*)P((char*,void*))) create_union_node, mp);
+ break;
+ }
+ }
+
+#ifdef notdef
+ /*
+ * would be nice to flush most of the cache, but we need to
+ * keep the wildcard and /defaults entries...
+ */
+ mapc_free(mf->mf_private);
+ mf->mf_private = (voidp) mapc_find(mf->mf_info, "inc");
+/* mapc_add_kv(mf->mf_private, strdup("/defaults"),
+ strdup("type:=link;opts:=nounmount;sublink:=${key}")); */
+#endif
+}
+#endif /* HAS_UNION_FS */
+
+/*
+ * Unmount an automount sub-node
+ */
+static int afs_umount P((am_node *mp));
+static int afs_umount(mp)
+am_node *mp;
+{
+ return 0;
+}
+
+/*
+ * Unmount a top-level automount node
+ */
+static int toplvl_umount P((am_node *mp));
+static int toplvl_umount(mp)
+am_node *mp;
+{
+ int error;
+
+ struct stat stb;
+again:
+ /*
+ * The lstat is needed if this mount is type=direct.
+ * When that happens, the kernel cache gets confused
+ * between the underlying type (dir) and the mounted
+ * type (link) and so needs to be re-synced before
+ * the unmount. This is all because the unmount system
+ * call follows links and so can't actually unmount
+ * a link (stupid!). It was noted that doing an ls -ld
+ * of the mount point to see why things were not working
+ * actually fixed the problem - so simulate an ls -ld here.
+ */
+ if (lstat(mp->am_path, &stb) < 0) {
+#ifdef DEBUG
+ dlog("lstat(%s): %m", mp->am_path);
+#endif /* DEBUG */
+ }
+ error = UMOUNT_FS(mp->am_path);
+ if (error == EBUSY) {
+ plog(XLOG_WARNING, "afs_unmount retrying %s in 1s", mp->am_path);
+ sleep(1); /* XXX */
+ goto again;
+ }
+
+ return error;
+}
+
+/*
+ * Unmount an automount node
+ */
+static void afs_umounted P((am_node *mp));
+static void afs_umounted(mp)
+am_node *mp;
+{
+ /*
+ * If this is a pseudo-directory then just adjust the link count
+ * in the parent, otherwise call the generic unmount routine
+ */
+ if (mp->am_parent && mp->am_parent->am_parent)
+ --mp->am_parent->am_fattr.nlink;
+}
+
+/*
+ * Mounting a file system may take a significant period of time. The
+ * problem is that if this is done in the main process thread then
+ * the entire automounter could be blocked, possibly hanging lots of
+ * processes on the system. Instead we use a continuation scheme to
+ * allow mounts to be attempted in a sub-process. When the sub-process
+ * exits we pick up the exit status (by convention a UN*X error number)
+ * and continue in a notifier. The notifier gets handed a data structure
+ * and can then determine whether the mount was successful or not. If
+ * not, it updates the data structure and tries again until there are no
+ * more ways to try the mount, or some other permanent error occurs.
+ * In the mean time no RPC reply is sent, even after the mount is succesful.
+ * We rely on the RPC retry mechanism to resend the lookup request which
+ * can then be handled.
+ */
+
+
+struct continuation {
+ char **ivec; /* Current mount info */
+ am_node *mp; /* Node we are trying to mount */
+ char *key; /* Map key */
+ char *info; /* Info string */
+ char **xivec; /* Saved strsplit vector */
+ char *auto_opts; /* Automount options */
+ am_opts fs_opts; /* Filesystem options */
+ char *def_opts; /* Default automount options */
+ int retry; /* Try again? */
+ int tried; /* Have we tried any yet? */
+ time_t start; /* Time we started this mount */
+ int callout; /* Callout identifier */
+};
+
+#define IN_PROGRESS(cp) ((cp)->mp->am_mnt->mf_flags & MFF_MOUNTING)
+
+/*
+ * Discard an old continuation
+ */
+static void free_continuation P((struct continuation *cp));
+static void free_continuation(cp)
+struct continuation *cp;
+{
+ if (cp->callout)
+ untimeout(cp->callout);
+ free((voidp) cp->key);
+ free((voidp) cp->xivec);
+ free((voidp) cp->info);
+ free((voidp) cp->auto_opts);
+ free((voidp) cp->def_opts);
+ free_opts(&cp->fs_opts);
+ free((voidp) cp);
+}
+
+static int afs_bgmount P((struct continuation*, int));
+
+/*
+ * Discard the underlying mount point and replace
+ * with a reference to an error filesystem.
+ */
+static void assign_error_mntfs P((am_node *mp));
+static void assign_error_mntfs(mp)
+am_node *mp;
+{
+ if (mp->am_error > 0) {
+ /*
+ * Save the old error code
+ */
+ int error = mp->am_error;
+ if (error <= 0)
+ error = mp->am_mnt->mf_error;
+ /*
+ * Discard the old filesystem
+ */
+ free_mntfs(mp->am_mnt);
+ /*
+ * Allocate a new error reference
+ */
+ mp->am_mnt = new_mntfs();
+ /*
+ * Put back the error code
+ */
+ mp->am_mnt->mf_error = error;
+ mp->am_mnt->mf_flags |= MFF_ERROR;
+ /*
+ * Zero the error in the mount point
+ */
+ mp->am_error = 0;
+ }
+}
+
+/*
+ * The continuation function. This is called by
+ * the task notifier when a background mount attempt
+ * completes.
+ */
+static void afs_cont P((int rc, int term, voidp closure));
+static void afs_cont(rc, term, closure)
+int rc;
+int term;
+voidp closure;
+{
+ struct continuation *cp = (struct continuation *) closure;
+ mntfs *mf = cp->mp->am_mnt;
+
+ /*
+ * Definitely not trying to mount at the moment
+ */
+ mf->mf_flags &= ~MFF_MOUNTING;
+ /*
+ * While we are mounting - try to avoid race conditions
+ */
+ new_ttl(cp->mp);
+
+ /*
+ * Wakeup anything waiting for this mount
+ */
+ wakeup((voidp) mf);
+
+ /*
+ * Check for termination signal or exit status...
+ */
+ if (rc || term) {
+ am_node *xmp;
+
+ if (term) {
+ /*
+ * Not sure what to do for an error code.
+ */
+ mf->mf_error = EIO; /* XXX ? */
+ mf->mf_flags |= MFF_ERROR;
+ plog(XLOG_ERROR, "mount for %s got signal %d", cp->mp->am_path, term);
+ } else {
+ /*
+ * Check for exit status...
+ */
+ mf->mf_error = rc;
+ mf->mf_flags |= MFF_ERROR;
+ errno = rc; /* XXX */
+ plog(XLOG_ERROR, "%s: mount (afs_cont): %m", cp->mp->am_path);
+ }
+
+ /*
+ * If we get here then that attempt didn't work, so
+ * move the info vector pointer along by one and
+ * call the background mount routine again
+ */
+ amd_stats.d_merr++;
+ cp->ivec++;
+ xmp = cp->mp;
+ (void) afs_bgmount(cp, 0);
+ assign_error_mntfs(xmp);
+ } else {
+ /*
+ * The mount worked.
+ */
+ am_mounted(cp->mp);
+ free_continuation(cp);
+ }
+
+ reschedule_timeout_mp();
+}
+
+/*
+ * Retry a mount
+ */
+/*ARGSUSED*/
+static void afs_retry P((int rc, int term, voidp closure));
+static void afs_retry(rc, term, closure)
+int rc;
+int term;
+voidp closure;
+{
+ struct continuation *cp = (struct continuation *) closure;
+ int error = 0;
+
+#ifdef DEBUG
+ dlog("Commencing retry for mount of %s", cp->mp->am_path);
+#endif /* DEBUG */
+
+ new_ttl(cp->mp);
+
+ if ((cp->start + ALLOWED_MOUNT_TIME) < clocktime()) {
+ /*
+ * The entire mount has timed out.
+ * Set the error code and skip past
+ * all the info vectors so that
+ * afs_bgmount will not have any more
+ * ways to try the mount, so causing
+ * an error.
+ */
+ plog(XLOG_INFO, "mount of \"%s\" has timed out", cp->mp->am_path);
+ error = ETIMEDOUT;
+ while (*cp->ivec)
+ cp->ivec++;
+ }
+
+ if (error || !IN_PROGRESS(cp)) {
+ (void) afs_bgmount(cp, error);
+ }
+ reschedule_timeout_mp();
+}
+
+/*
+ * Try to mount a file system. Can be called
+ * directly or in a sub-process by run_task
+ */
+static int try_mount P((voidp mvp));
+static int try_mount(mvp)
+voidp mvp;
+{
+ /*
+ * Mount it!
+ */
+ int error;
+ am_node *mp = (am_node *) mvp;
+ mntfs *mf = mp->am_mnt;
+
+ /*
+ * If the directory is not yet made and
+ * it needs to be made, then make it!
+ * This may be run in a backgroun process
+ * in which case the flag setting won't be
+ * noticed later - but it is set anyway
+ * just after run_task is called. It
+ * should probably go away totally...
+ */
+ if (!(mf->mf_flags & MFF_MKMNT) && mf->mf_ops->fs_flags & FS_MKMNT) {
+ error = mkdirs(mf->mf_mount, 0555);
+ if (!error)
+ mf->mf_flags |= MFF_MKMNT;
+ }
+
+ error = mount_node(mp);
+#ifdef DEBUG
+ if (error > 0) {
+ errno = error;
+ dlog("afs call to mount_node failed: %m");
+ }
+#endif /* DEBUG */
+ return error;
+}
+
+/*
+ * Pick a file system to try mounting and
+ * do that in the background if necessary
+ *
+For each location:
+ if it is new -defaults then
+ extract and process
+ continue;
+ fi
+ if it is a cut then
+ if a location has been tried then
+ break;
+ fi
+ continue;
+ fi
+ parse mount location
+ discard previous mount location if required
+ find matching mounted filesystem
+ if not applicable then
+ this_error = No such file or directory
+ continue
+ fi
+ if the filesystem failed to be mounted then
+ this_error = error from filesystem
+ elif the filesystem is mounting or unmounting then
+ this_error = -1
+ elif the fileserver is down then
+ this_error = -1
+ elif the filesystem is already mounted
+ this_error = 0
+ break
+ fi
+ if no error on this mount then
+ this_error = initialise mount point
+ fi
+ if no error on this mount and mount is delayed then
+ this_error = -1
+ fi
+ if this_error < 0 then
+ retry = true
+ fi
+ if no error on this mount then
+ make mount point if required
+ fi
+ if no error on this mount then
+ if mount in background then
+ run mount in background
+ return -1
+ else
+ this_error = mount in foreground
+ fi
+ fi
+ if an error occured on this mount then
+ update stats
+ save error in mount point
+ fi
+endfor
+ */
+
+static int afs_bgmount P((struct continuation *cp, int mpe));
+static int afs_bgmount(cp, mpe)
+struct continuation *cp;
+int mpe;
+{
+ mntfs *mf = cp->mp->am_mnt; /* Current mntfs */
+ mntfs *mf_retry = 0; /* First mntfs which needed retrying */
+ int this_error = -1; /* Per-mount error */
+ int hard_error = -1;
+ int mp_error = mpe;
+
+ /*
+ * Try to mount each location.
+ * At the end:
+ * hard_error == 0 indicates something was mounted.
+ * hard_error > 0 indicates everything failed with a hard error
+ * hard_error < 0 indicates nothing could be mounted now
+ */
+ for (; this_error && *cp->ivec; cp->ivec++) {
+ am_ops *p;
+ am_node *mp = cp->mp;
+ char *link_dir;
+ int dont_retry;
+
+ if (hard_error < 0)
+ hard_error = this_error;
+
+ this_error = -1;
+
+ if (**cp->ivec == '-') {
+ /*
+ * Pick up new defaults
+ */
+ if (cp->auto_opts && *cp->auto_opts)
+ cp->def_opts = str3cat(cp->def_opts, cp->auto_opts, ";", *cp->ivec+1);
+ else
+ cp->def_opts = strealloc(cp->def_opts, *cp->ivec+1);
+#ifdef DEBUG
+ dlog("Setting def_opts to \"%s\"", cp->def_opts);
+#endif /* DEBUG */
+ continue;
+ }
+
+ /*
+ * If a mount has been attempted, and we find
+ * a cut then don't try any more locations.
+ */
+ if (strcmp(*cp->ivec, "/") == 0 || strcmp(*cp->ivec, "||") == 0) {
+ if (cp->tried) {
+#ifdef DEBUG
+ dlog("Cut: not trying any more locations for %s",
+ mp->am_path);
+#endif /* DEBUG */
+ break;
+ }
+ continue;
+ }
+
+#ifdef SUNOS4_COMPAT
+#ifdef nomore
+ /*
+ * By default, you only get this bit on SunOS4.
+ * If you want this anyway, then define SUNOS4_COMPAT
+ * in the relevant "os-blah.h" file.
+ *
+ * We make the observation that if the local key line contains
+ * no '=' signs then either it is sick, or it is a SunOS4-style
+ * "host:fs[:link]" line. In the latter case the am_opts field
+ * is also assumed to be in old-style, so you can't mix & match.
+ * You can use ${} expansions for the fs and link bits though...
+ *
+ * Actually, this doesn't really cover all the possibilities for
+ * the latest SunOS automounter and it is debatable whether there
+ * is any point bothering.
+ */
+ if (strchr(*cp->ivec, '=') == 0)
+ p = sunos4_match(&cp->fs_opts, *cp->ivec, cp->def_opts, mp->am_path, cp->key, mp->am_parent->am_mnt->mf_info);
+ else
+#endif
+#endif /* SUNOS4_COMPAT */
+ p = ops_match(&cp->fs_opts, *cp->ivec, cp->def_opts, mp->am_path, cp->key, mp->am_parent->am_mnt->mf_info);
+
+ /*
+ * Find a mounted filesystem for this node.
+ */
+ mp->am_mnt = mf = realloc_mntfs(mf, p, &cp->fs_opts, cp->fs_opts.opt_fs,
+ cp->fs_opts.fs_mtab, cp->auto_opts, cp->fs_opts.opt_opts, cp->fs_opts.opt_remopts);
+
+ p = mf->mf_ops;
+#ifdef DEBUG
+ dlog("Got a hit with %s", p->fs_type);
+#endif /* DEBUG */
+ /*
+ * Note whether this is a real mount attempt
+ */
+ if (p == &efs_ops) {
+ plog(XLOG_MAP, "Map entry %s for %s failed to match", *cp->ivec, mp->am_path);
+ if (this_error <= 0)
+ this_error = ENOENT;
+ continue;
+ } else {
+ if (cp->fs_opts.fs_mtab) {
+ plog(XLOG_MAP, "Trying mount of %s on %s fstype %s",
+ cp->fs_opts.fs_mtab, mp->am_path, p->fs_type);
+ }
+ cp->tried = TRUE;
+ }
+
+ this_error = 0;
+ dont_retry = FALSE;
+
+ if (mp->am_link) {
+ free(mp->am_link);
+ mp->am_link = 0;
+ }
+
+ link_dir = mf->mf_fo->opt_sublink;
+
+ if (link_dir && *link_dir) {
+ if (*link_dir == '/') {
+ mp->am_link = strdup(link_dir);
+ } else {
+ mp->am_link = str3cat((char *) 0,
+ mf->mf_fo->opt_fs, "/", link_dir);
+ normalize_slash(mp->am_link);
+ }
+ }
+
+ if (mf->mf_error > 0) {
+ this_error = mf->mf_error;
+ } else if (mf->mf_flags & (MFF_MOUNTING|MFF_UNMOUNTING)) {
+ /*
+ * Still mounting - retry later
+ */
+#ifdef DEBUG
+ dlog("Duplicate pending mount fstype %s", p->fs_type);
+#endif /* DEBUG */
+ this_error = -1;
+ } else if (FSRV_ISDOWN(mf->mf_server)) {
+ /*
+ * Would just mount from the same place
+ * as a hung mount - so give up
+ */
+#ifdef DEBUG
+ dlog("%s is already hung - giving up", mf->mf_mount);
+#endif /* DEBUG */
+ mp_error = EWOULDBLOCK;
+ dont_retry = TRUE;
+ this_error = -1;
+ } else if (mf->mf_flags & MFF_MOUNTED) {
+#ifdef DEBUG
+ dlog("duplicate mount of \"%s\" ...", mf->mf_info);
+#endif /* DEBUG */
+ /*
+ * Just call mounted()
+ */
+ am_mounted(mp);
+
+ this_error = 0;
+ break;
+ }
+
+ /*
+ * Will usually need to play around with the mount nodes
+ * file attribute structure. This must be done here.
+ * Try and get things initialised, even if the fileserver
+ * is not known to be up. In the common case this will
+ * progress things faster.
+ */
+ if (!this_error) {
+ /*
+ * Fill in attribute fields.
+ */
+ if (mf->mf_ops->fs_flags & FS_DIRECTORY)
+ mk_fattr(mp, NFDIR);
+ else
+ mk_fattr(mp, NFLNK);
+
+ mp->am_fattr.fileid = mp->am_gen;
+
+ if (p->fs_init)
+ this_error = (*p->fs_init)(mf);
+ }
+
+ /*
+ * Make sure the fileserver is UP before doing any more work
+ */
+ if (!FSRV_ISUP(mf->mf_server)) {
+#ifdef DEBUG
+ dlog("waiting for server %s to become available", mf->mf_server->fs_host);
+#endif
+ this_error = -1;
+ }
+
+ if (!this_error && mf->mf_fo->opt_delay) {
+ /*
+ * If there is a delay timer on the mount
+ * then don't try to mount if the timer
+ * has not expired.
+ */
+ int i = atoi(mf->mf_fo->opt_delay);
+ if (i > 0 && clocktime() < (cp->start + i)) {
+#ifdef DEBUG
+ dlog("Mount of %s delayed by %ds", mf->mf_mount, i - clocktime() + cp->start);
+#endif /* DEBUG */
+ this_error = -1;
+ }
+ }
+
+ if (this_error < 0 && !dont_retry) {
+ if (!mf_retry)
+ mf_retry = dup_mntfs(mf);
+ cp->retry = TRUE;
+ }
+
+ if (!this_error)
+ if (p->fs_flags & FS_MBACKGROUND) {
+ mf->mf_flags |= MFF_MOUNTING; /*XXX*/
+#ifdef DEBUG
+ dlog("backgrounding mount of \"%s\"", mf->mf_mount);
+#endif /* DEBUG */
+ if (cp->callout) {
+ untimeout(cp->callout);
+ cp->callout = 0;
+ }
+ run_task(try_mount, (voidp) mp, afs_cont, (voidp) cp);
+ mf->mf_flags |= MFF_MKMNT; /* XXX */
+ if (mf_retry) free_mntfs(mf_retry);
+ return -1;
+ } else {
+#ifdef DEBUG
+ dlog("foreground mount of \"%s\" ...", mf->mf_info);
+#endif /* DEBUG */
+ this_error = try_mount((voidp) mp);
+ if (this_error < 0) {
+ if (!mf_retry)
+ mf_retry = dup_mntfs(mf);
+ cp->retry = TRUE;
+ }
+ }
+
+ if (this_error >= 0) {
+ if (this_error > 0) {
+ amd_stats.d_merr++;
+ if (mf != mf_retry) {
+ mf->mf_error = this_error;
+ mf->mf_flags |= MFF_ERROR;
+ }
+ }
+ /*
+ * Wakeup anything waiting for this mount
+ */
+ wakeup((voidp) mf);
+ }
+ }
+
+ if (this_error && cp->retry) {
+ free_mntfs(mf);
+ mf = cp->mp->am_mnt = mf_retry;
+ /*
+ * Not retrying again (so far)
+ */
+ cp->retry = FALSE;
+ cp->tried = FALSE;
+ /*
+ * Start at the beginning.
+ * Rewind the location vector and
+ * reset the default options.
+ */
+ cp->ivec = cp->xivec;
+ cp->def_opts = strealloc(cp->def_opts, cp->auto_opts);
+ /*
+ * Arrange that afs_bgmount is called
+ * after anything else happens.
+ */
+#ifdef DEBUG
+ dlog("Arranging to retry mount of %s", cp->mp->am_path);
+#endif /* DEBUG */
+ sched_task(afs_retry, (voidp) cp, (voidp) mf);
+ if (cp->callout)
+ untimeout(cp->callout);
+ cp->callout = timeout(RETRY_INTERVAL, wakeup, (voidp) mf);
+
+ cp->mp->am_ttl = clocktime() + RETRY_INTERVAL;
+
+ /*
+ * Not done yet - so don't return anything
+ */
+ return -1;
+ }
+
+ if (hard_error < 0 || this_error == 0)
+ hard_error = this_error;
+
+ /*
+ * Discard handle on duff filesystem.
+ * This should never happen since it
+ * should be caught by the case above.
+ */
+ if (mf_retry) {
+ if (hard_error)
+ plog(XLOG_ERROR, "discarding a retry mntfs for %s", mf_retry->mf_mount);
+ free_mntfs(mf_retry);
+ }
+
+ /*
+ * If we get here, then either the mount succeeded or
+ * there is no more mount information available.
+ */
+ if (hard_error < 0 && mp_error)
+ hard_error = cp->mp->am_error = mp_error;
+ if (hard_error > 0) {
+ /*
+ * Set a small(ish) timeout on an error node if
+ * the error was not a time out.
+ */
+ switch (hard_error) {
+ case ETIMEDOUT:
+ case EWOULDBLOCK:
+ cp->mp->am_timeo = 5;
+ break;
+ default:
+ cp->mp->am_timeo = 17;
+ break;
+ }
+ new_ttl(cp->mp);
+ }
+
+ /*
+ * Make sure that the error value in the mntfs has a
+ * reasonable value.
+ */
+ if (mf->mf_error < 0) {
+ mf->mf_error = hard_error;
+ if (hard_error)
+ mf->mf_flags |= MFF_ERROR;
+ }
+
+ /*
+ * In any case we don't need the continuation any more
+ */
+ free_continuation(cp);
+
+ return hard_error;
+}
+
+/*
+ * Automount interface to RPC lookup routine
+ */
+static am_node *afs_lookuppn P((am_node *mp, char *fname, int *error_return, int op));
+static am_node *afs_lookuppn(mp, fname, error_return, op)
+am_node *mp;
+char *fname;
+int *error_return;
+int op;
+{
+#define ereturn(x) { *error_return = x; return 0; }
+
+ /*
+ * Find the corresponding entry and return
+ * the file handle for it.
+ */
+ am_node *ap, *new_mp, *ap_hung;
+ char *info; /* Mount info - where to get the file system */
+ char **ivec, **xivec; /* Split version of info */
+ char *auto_opts; /* Automount options */
+ int error = 0; /* Error so far */
+ char path_name[MAXPATHLEN]; /* General path name buffer */
+ char *pfname; /* Path for database lookup */
+ struct continuation *cp; /* Continuation structure if we need to mount */
+ int in_progress = 0; /* # of (un)mount in progress */
+ char *dflts;
+ mntfs *mf;
+
+#ifdef DEBUG
+ dlog("in afs_lookuppn");
+#endif /* DEBUG */
+
+ /*
+ * If the server is shutting down
+ * then don't return information
+ * about the mount point.
+ */
+ if (amd_state == Finishing) {
+#ifdef DEBUG
+ if ((mf = mp->am_mnt) == 0 || mf->mf_ops == &dfs_ops)
+ dlog("%s mount ignored - going down", fname);
+ else
+ dlog("%s/%s mount ignored - going down", mp->am_path, fname);
+#endif /* DEBUG */
+ ereturn(ENOENT);
+ }
+
+ /*
+ * Handle special case of "." and ".."
+ */
+ if (fname[0] == '.') {
+ if (fname[1] == '\0')
+ return mp; /* "." is the current node */
+ if (fname[1] == '.' && fname[2] == '\0') {
+ if (mp->am_parent) {
+#ifdef DEBUG
+ dlog(".. in %s gives %s", mp->am_path, mp->am_parent->am_path);
+#endif /* DEBUG */
+ return mp->am_parent; /* ".." is the parent node */
+ }
+ ereturn(ESTALE);
+ }
+ }
+
+ /*
+ * Check for valid key name.
+ * If it is invalid then pretend it doesn't exist.
+ */
+ if (!valid_key(fname)) {
+ plog(XLOG_WARNING, "Key \"%s\" contains a disallowed character", fname);
+ ereturn(ENOENT);
+ }
+
+ /*
+ * Expand key name.
+ * fname is now a private copy.
+ */
+ fname = expand_key(fname);
+
+ for (ap_hung = 0, ap = mp->am_child; ap; ap = ap->am_osib) {
+ /*
+ * Otherwise search children of this node
+ */
+ if (FSTREQ(ap->am_name, fname)) {
+ mf = ap->am_mnt;
+ if (ap->am_error) {
+ error = ap->am_error;
+ continue;
+ }
+
+ /*
+ * If the error code is undefined then it must be
+ * in progress.
+ */
+ if (mf->mf_error < 0)
+ goto in_progrss;
+
+ /*
+ * Check for a hung node
+ */
+ if (FSRV_ISDOWN(mf->mf_server)) {
+#ifdef DEBUG
+ dlog("server hung");
+#endif /* DEBUG */
+ error = ap->am_error;
+ ap_hung = ap;
+ continue;
+ }
+
+ /*
+ * If there was a previous error with this node
+ * then return that error code.
+ */
+ if (mf->mf_flags & MFF_ERROR) {
+ error = mf->mf_error;
+ continue;
+ }
+
+ if (!(mf->mf_flags & MFF_MOUNTED) /*|| (mf->mf_flags & MFF_UNMOUNTING)*/) {
+in_progrss:
+ /*
+ * If the fs is not mounted or it is unmounting then there
+ * is a background (un)mount in progress. In this case
+ * we just drop the RPC request (return nil) and
+ * wait for a retry, by which time the (un)mount may
+ * have completed.
+ */
+#ifdef DEBUG
+ dlog("ignoring mount of %s in %s -- in progress",
+ fname, mf->mf_mount);
+#endif /* DEBUG */
+ in_progress++;
+ continue;
+ }
+
+ /*
+ * Otherwise we have a hit: return the current mount point.
+ */
+#ifdef DEBUG
+ dlog("matched %s in %s", fname, ap->am_path);
+#endif /* DEBUG */
+ free(fname);
+ return ap;
+ }
+ }
+
+ if (in_progress) {
+#ifdef DEBUG
+ dlog("Waiting while %d mount(s) in progress", in_progress);
+#endif /* DEBUG */
+ free(fname);
+ ereturn(-1);
+ }
+
+ /*
+ * If an error occured then return it.
+ */
+ if (error) {
+#ifdef DEBUG
+ errno = error; /* XXX */
+ dlog("Returning error: %m", error);
+#endif /* DEBUG */
+ free(fname);
+ ereturn(error);
+ }
+
+ /*
+ * If doing a delete then don't create again!
+ */
+ switch (op) {
+ case VLOOK_DELETE:
+ ereturn(ENOENT);
+ break;
+
+ case VLOOK_CREATE:
+ break;
+
+ default:
+ plog(XLOG_FATAL, "Unknown op to afs_lookuppn: 0x%x", op);
+ ereturn(EINVAL);
+ break;
+ }
+
+ /*
+ * If the server is going down then just return,
+ * don't try to mount any more file systems
+ */
+ if ((int)amd_state >= (int)Finishing) {
+#ifdef DEBUG
+ dlog("not found - server going down anyway");
+#endif /* DEBUG */
+ free(fname);
+ ereturn(ENOENT);
+ }
+
+ /*
+ * If we get there then this is a reference to an,
+ * as yet, unknown name so we need to search the mount
+ * map for it.
+ */
+ if (mp->am_pref) {
+ sprintf(path_name, "%s%s", mp->am_pref, fname);
+ pfname = path_name;
+ } else {
+ pfname = fname;
+ }
+
+ mf = mp->am_mnt;
+
+#ifdef DEBUG
+ dlog("will search map info in %s to find %s", mf->mf_info, pfname);
+#endif /* DEBUG */
+ /*
+ * Consult the oracle for some mount information.
+ * info is malloc'ed and belongs to this routine.
+ * It ends up being free'd in free_continuation().
+ *
+ * Note that this may return -1 indicating that information
+ * is not yet available.
+ */
+ error = mapc_search((mnt_map*) mf->mf_private, pfname, &info);
+ if (error) {
+ if (error > 0)
+ plog(XLOG_MAP, "No map entry for %s", pfname);
+ else
+ plog(XLOG_MAP, "Waiting on map entry for %s", pfname);
+ free(fname);
+ ereturn(error);
+ }
+
+#ifdef DEBUG
+ dlog("mount info is %s", info);
+#endif /* DEBUG */
+
+ /*
+ * Split info into an argument vector.
+ * The vector is malloc'ed and belongs to
+ * this routine. It is free'd in free_continuation()
+ */
+ xivec = ivec = strsplit(info, ' ', '\"');
+
+ /*
+ * Default error code...
+ */
+ if (ap_hung)
+ error = EWOULDBLOCK;
+ else
+ error = ENOENT;
+
+ /*
+ * Allocate a new map
+ */
+ new_mp = exported_ap_alloc();
+ if (new_mp == 0) {
+ free((voidp) xivec);
+ free((voidp) info);
+ free((voidp) fname);
+ ereturn(ENOSPC);
+ }
+
+ if (mf->mf_auto)
+ auto_opts = mf->mf_auto;
+ else
+ auto_opts = "";
+
+ auto_opts = strdup(auto_opts);
+
+#ifdef DEBUG
+ dlog("searching for /defaults entry");
+#endif /* DEBUG */
+ if (mapc_search((mnt_map*) mf->mf_private, "/defaults", &dflts) == 0) {
+ char *dfl;
+ char **rvec;
+#ifdef DEBUG
+ dlog("/defaults gave %s", dflts);
+#endif /* DEBUG */
+ if (*dflts == '-')
+ dfl = dflts+1;
+ else
+ dfl = dflts;
+
+ /*
+ * Chop the defaults up
+ */
+ rvec = strsplit(dfl, ' ', '\"');
+ /*
+ * Extract first value
+ */
+ dfl = rvec[0];
+
+ /*
+ * If there were any values at all...
+ */
+ if (dfl) {
+ /*
+ * Log error if there were other values
+ */
+ if (rvec[1]) {
+#ifdef DEBUG
+ dlog("/defaults chopped into %s", dfl);
+#endif /* DEBUG */
+ plog(XLOG_USER, "More than a single value for /defaults in %s", mf->mf_info);
+ }
+
+ /*
+ * Prepend to existing defaults if they exist,
+ * otherwise just use these defaults.
+ */
+ if (*auto_opts && *dfl) {
+ char *nopts = (char *) xmalloc(strlen(auto_opts)+strlen(dfl)+2);
+ sprintf(nopts, "%s;%s", dfl, auto_opts);
+ free(auto_opts);
+ auto_opts = nopts;
+ } else if (*dfl) {
+ auto_opts = strealloc(auto_opts, dfl);
+ }
+ }
+ free(dflts);
+ /*
+ * Don't need info vector any more
+ */
+ free((voidp) rvec);
+ }
+
+ /*
+ * Fill it in
+ */
+ init_map(new_mp, fname);
+
+ /*
+ * Put it in the table
+ */
+ insert_am(new_mp, mp);
+
+ /*
+ * Fill in some other fields,
+ * path and mount point.
+ *
+ * bugfix: do not prepend old am_path if direct map
+ * <wls@astro.umd.edu> William Sebok
+ */
+ new_mp->am_path = str3cat(new_mp->am_path,
+ mf->mf_ops == &dfs_ops ? "" : mp->am_path,
+ *fname == '/' ? "" : "/", fname);
+
+#ifdef DEBUG
+ dlog("setting path to %s", new_mp->am_path);
+#endif /* DEBUG */
+
+ /*
+ * Take private copy of pfname
+ */
+ pfname = strdup(pfname);
+
+ /*
+ * Construct a continuation
+ */
+ cp = ALLOC(continuation);
+ cp->mp = new_mp;
+ cp->xivec = xivec;
+ cp->ivec = ivec;
+ cp->info = info;
+ cp->key = pfname;
+ cp->auto_opts = auto_opts;
+ cp->retry = FALSE;
+ cp->tried = FALSE;
+ cp->start = clocktime();
+ cp->def_opts = strdup(auto_opts);
+ bzero((voidp) &cp->fs_opts, sizeof(cp->fs_opts));
+
+ /*
+ * Try and mount the file system
+ * If this succeeds immediately (possible
+ * for a ufs file system) then return
+ * the attributes, otherwise just
+ * return an error.
+ */
+ error = afs_bgmount(cp, error);
+ reschedule_timeout_mp();
+ if (!error) {
+ free(fname);
+ return new_mp;
+ }
+
+ if (error && (new_mp->am_mnt->mf_ops == &efs_ops))
+ new_mp->am_error = error;
+
+ assign_error_mntfs(new_mp);
+
+ free(fname);
+
+ ereturn(error);
+#undef ereturn
+}
+
+/*
+ * Locate next node in sibling list which is mounted
+ * and is not an error node.
+ */
+static am_node *next_nonerror_node P((am_node *xp));
+static am_node *next_nonerror_node(xp)
+am_node *xp;
+{
+ mntfs *mf;
+
+ /*
+ * Bug report (7/12/89) from Rein Tollevik <rein@ifi.uio.no>
+ * Fixes a race condition when mounting direct automounts.
+ * Also fixes a problem when doing a readdir on a directory
+ * containing hung automounts.
+ */
+ while (xp &&
+ (!(mf = xp->am_mnt) || /* No mounted filesystem */
+ mf->mf_error != 0 || /* There was a mntfs error */
+ xp->am_error != 0 || /* There was a mount error */
+ !(mf->mf_flags & MFF_MOUNTED) || /* The fs is not mounted */
+ (mf->mf_server->fs_flags & FSF_DOWN)) /* The fs may be down */
+ )
+ xp = xp->am_osib;
+
+ return xp;
+}
+
+static int afs_readdir P((am_node *mp, nfscookie cookie, struct dirlist *dp, struct entry *ep, int count));
+static int afs_readdir(mp, cookie, dp, ep, count)
+am_node *mp;
+nfscookie cookie;
+struct dirlist *dp;
+struct entry *ep;
+int count;
+{
+ unsigned int gen = *(unsigned int*) cookie;
+ am_node *xp;
+
+ dp->eof = FALSE;
+
+ if (gen == 0) {
+ /*
+ * In the default instance (which is used to
+ * start a search) we return "." and "..".
+ *
+ * This assumes that the count is big enough
+ * to allow both "." and ".." to be returned in
+ * a single packet. If it isn't (which would
+ * be fairly unbelievable) then tough.
+ */
+#ifdef DEBUG
+ dlog("default search");
+#endif /* DEBUG */
+ /*
+ * Check for enough room. This is extremely
+ * approximate but is more than enough space.
+ * Really need 2 times:
+ * 4byte fileid
+ * 4byte cookie
+ * 4byte name length
+ * 4byte name
+ * plus the dirlist structure
+ */
+ if (count <
+ (2 * (2 * (sizeof(*ep) + sizeof("..") + 4)
+ + sizeof(*dp))))
+ return EINVAL;
+
+ xp = next_nonerror_node(mp->am_child);
+ dp->entries = ep;
+
+ /* construct "." */
+ ep[0].fileid = mp->am_gen;
+ ep[0].name = ".";
+ ep[0].nextentry = &ep[1];
+ *(unsigned int *) ep[0].cookie = 0;
+
+ /* construct ".." */
+ if (mp->am_parent)
+ ep[1].fileid = mp->am_parent->am_gen;
+ else
+ ep[1].fileid = mp->am_gen;
+ ep[1].name = "..";
+ ep[1].nextentry = 0;
+ *(unsigned int *) ep[1].cookie =
+ xp ? xp->am_gen : ~(unsigned int)0;
+
+ if (!xp) dp->eof = TRUE;
+ return 0;
+ }
+
+#ifdef DEBUG
+ dlog("real child");
+#endif /* DEBUG */
+
+ if (gen == ~(unsigned int)0) {
+#ifdef DEBUG
+ dlog("End of readdir in %s", mp->am_path);
+#endif /* DEBUG */
+ dp->eof = TRUE;
+ dp->entries = 0;
+ return 0;
+ }
+
+ xp = mp->am_child;
+ while (xp && xp->am_gen != gen)
+ xp = xp->am_osib;
+
+ if (xp) {
+ int nbytes = count / 2; /* conservative */
+ int todo = MAX_READDIR_ENTRIES;
+ dp->entries = ep;
+ do {
+ am_node *xp_next = next_nonerror_node(xp->am_osib);
+
+ if (xp_next) {
+ *(unsigned int *) ep->cookie = xp_next->am_gen;
+ } else {
+ *(unsigned int *) ep->cookie = ~(unsigned int)0;
+ dp->eof = TRUE;
+ }
+
+ ep->fileid = xp->am_gen;
+ ep->name = xp->am_name;
+ nbytes -= sizeof(*ep) + strlen(xp->am_name) + 1;
+
+ xp = xp_next;
+
+ if (nbytes > 0 && !dp->eof && todo > 1) {
+ ep->nextentry = ep + 1;
+ ep++;
+ --todo;
+ } else {
+ todo = 0;
+ }
+ } while (todo > 0);
+
+ ep->nextentry = 0;
+
+ return 0;
+ }
+
+ return ESTALE;
+
+}
+
+static am_node *dfs_readlink P((am_node *mp, int *error_return));
+static am_node *dfs_readlink(mp, error_return)
+am_node *mp;
+int *error_return;
+{
+ am_node *xp;
+ int rc = 0;
+
+ xp = next_nonerror_node(mp->am_child);
+ if (!xp) {
+ if (!mp->am_mnt->mf_private)
+ afs_mkcacheref(mp->am_mnt); /* XXX */
+ xp = afs_lookuppn(mp, mp->am_path+1, &rc, VLOOK_CREATE);
+ }
+
+ if (xp) {
+ new_ttl(xp); /* (7/12/89) from Rein Tollevik */
+ return xp;
+ }
+ if (amd_state == Finishing)
+ rc = ENOENT;
+ *error_return = rc;
+ return 0;
+}
+
+/*
+ * Ops structure
+ */
+am_ops root_ops = {
+ "root",
+ 0, /* root_match */
+ 0, /* root_init */
+ root_mount,
+ 0,
+ afs_umount,
+ 0,
+ afs_lookuppn,
+ afs_readdir,
+ 0, /* root_readlink */
+ 0, /* root_mounted */
+ 0, /* root_umounted */
+ find_afs_srvr,
+ FS_NOTIMEOUT|FS_AMQINFO|FS_DIRECTORY
+};
+
+am_ops afs_ops = {
+ "auto",
+ afs_match,
+ 0, /* afs_init */
+ afs_mount,
+ 0,
+ afs_umount,
+ 0,
+ afs_lookuppn,
+ afs_readdir,
+ 0, /* afs_readlink */
+ 0, /* afs_mounted */
+ afs_umounted,
+ find_afs_srvr,
+ FS_AMQINFO|FS_DIRECTORY
+};
+
+am_ops toplvl_ops = {
+ "toplvl",
+ afs_match,
+ 0, /* afs_init */
+ toplvl_mount,
+ 0,
+ toplvl_umount,
+ 0,
+ afs_lookuppn,
+ afs_readdir,
+ 0, /* toplvl_readlink */
+ toplvl_mounted,
+ 0, /* toplvl_umounted */
+ find_afs_srvr,
+ FS_MKMNT|FS_NOTIMEOUT|FS_BACKGROUND|FS_AMQINFO|FS_DIRECTORY
+};
+
+am_ops dfs_ops = {
+ "direct",
+ afs_match,
+ 0, /* dfs_init */
+ toplvl_mount,
+ 0,
+ toplvl_umount,
+ 0,
+ efs_lookuppn,
+ efs_readdir,
+ dfs_readlink,
+ toplvl_mounted,
+ 0, /* afs_umounted */
+ find_afs_srvr,
+ FS_MKMNT|FS_NOTIMEOUT|FS_BACKGROUND|FS_AMQINFO
+};
+
+#ifdef HAS_UNION_FS
+am_ops union_ops = {
+ "union",
+ afs_match,
+ 0, /* afs_init */
+ toplvl_mount,
+ 0,
+ toplvl_umount,
+ 0,
+ afs_lookuppn,
+ afs_readdir,
+ 0, /* toplvl_readlink */
+ union_mounted,
+ 0, /* toplvl_umounted */
+ find_afs_srvr,
+ FS_MKMNT|FS_NOTIMEOUT|FS_BACKGROUND|FS_AMQINFO|FS_DIRECTORY
+};
+#endif /* HAS_UNION_FS */
diff --git a/usr.sbin/amd/amd/am_ops.c b/usr.sbin/amd/amd/am_ops.c
new file mode 100644
index 0000000..f3360e0
--- /dev/null
+++ b/usr.sbin/amd/amd/am_ops.c
@@ -0,0 +1,181 @@
+/*
+ * Copyright (c) 1989 Jan-Simon Pendry
+ * Copyright (c) 1989 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)am_ops.c 8.1 (Berkeley) 6/6/93
+ *
+ * $Id$
+ *
+ */
+
+#include "am.h"
+
+static am_ops *vops[] = {
+#ifdef HAS_UFS
+ &ufs_ops,
+#endif
+#ifdef HAS_NFS
+ &nfs_ops,
+#endif
+#ifdef HAS_NFSX
+ &nfsx_ops,
+#endif
+#ifdef HAS_HOST
+ &host_ops,
+#endif
+#ifdef HAS_SFS
+ &sfs_ops,
+#endif
+#ifdef HAS_SFSX
+ &sfsx_ops,
+#endif
+#ifdef HAS_LOFS
+ &lofs_ops,
+#endif
+#ifdef HAS_PFS
+ &pfs_ops,
+#endif
+#ifdef HAS_UNION_FS
+ &union_ops,
+#endif
+ &afs_ops, /* These four should be last ... */
+ &dfs_ops, /* ... */
+ &toplvl_ops, /* ... */
+ &efs_ops, /* ... in the order afs; dfs; toplvl; efs */
+ 0
+};
+
+void ops_showfstypes P((FILE *fp));
+void ops_showfstypes(fp)
+FILE *fp;
+{
+ struct am_ops **ap;
+ int l = 0;
+
+ for (ap = vops; *ap; ap++) {
+ fputs((*ap)->fs_type, fp);
+ if (ap[1]) fputs(", ", fp);
+ l += strlen((*ap)->fs_type) + 2;
+ if (l > 60) { l = 0; fputs("\n ", fp); }
+ }
+}
+
+#ifdef SUNOS4_COMPAT
+#ifdef nomore
+/*
+ * Crack a SunOS4-style host:fs:sub-link line
+ * Construct an amd-style line and call the
+ * normal amd matcher.
+ */
+am_ops *sunos4_match(fo, key, g_key, path, keym, map)
+am_opts *fo;
+char *key;
+char *g_key;
+char *path;
+char *keym;
+char *map;
+{
+ char *host = key;
+ char *fs = strchr(host, ':');
+ char *sublink = fs ? strchr(fs+1, ':') : 0;
+ char keybuf[MAXPATHLEN];
+
+ sprintf(keybuf, "type:=nfs;rhost:=%s;rfs:=%s;sublink:=%s;opts:=%s", host,
+ fs ? fs+1 : "",
+ sublink ? sublink+1 : "",
+ g_key);
+ return ops_match(fo, keybuf, "", path, keym, map);
+}
+#endif
+#endif /* SUNOS4_COMPAT */
+
+am_ops *ops_match(fo, key, g_key, path, keym, map)
+am_opts *fo;
+char *key;
+char *g_key;
+char *path;
+char *keym;
+char *map;
+{
+ am_ops **vp;
+ am_ops *rop = 0;
+
+ /*
+ * First crack the global opts and the local opts
+ */
+ if (!eval_fs_opts(fo, key, g_key, path, keym, map)) {
+ rop = &efs_ops;
+ } else if (fo->opt_type == 0) {
+ plog(XLOG_USER, "No fs type specified (key = \"%s\", map = \"%s\")", keym, map);
+ rop = &efs_ops;
+ } else {
+ /*
+ * Next find the correct filesystem type
+ */
+ for (vp = vops; rop = *vp; vp++)
+ if (strcmp(rop->fs_type, fo->opt_type) == 0)
+ break;
+
+ if (!rop) {
+ plog(XLOG_USER, "fs type \"%s\" not recognised", fo->opt_type);
+ rop = &efs_ops;
+ }
+ }
+
+ /*
+ * Make sure we have a default mount option.
+ * Otherwise skip past any leading '-'.
+ */
+ if (fo->opt_opts == 0)
+ fo->opt_opts = "rw,defaults";
+ else if (*fo->opt_opts == '-')
+ fo->opt_opts++;
+
+ /*
+ * Check the filesystem is happy
+ */
+ if (fo->fs_mtab)
+ free((voidp) fo->fs_mtab);
+
+ if (fo->fs_mtab = (*rop->fs_match)(fo))
+ return rop;
+
+ /*
+ * Return error file system
+ */
+ fo->fs_mtab = (*efs_ops.fs_match)(fo);
+ return &efs_ops;
+}
diff --git a/usr.sbin/amd/amd/amd.8 b/usr.sbin/amd/amd/amd.8
new file mode 100644
index 0000000..71e0f78
--- /dev/null
+++ b/usr.sbin/amd/amd/amd.8
@@ -0,0 +1,232 @@
+.\"
+.\" Copyright (c) 1989 Jan-Simon Pendry
+.\" Copyright (c) 1989 Imperial College of Science, Technology & Medicine
+.\" Copyright (c) 1989, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" Jan-Simon Pendry at Imperial College, London.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)amd.8 5.10 (Berkeley) 4/19/94
+.\"
+.\" $Id: amd.8,v 1.5 1997/02/22 16:01:26 peter Exp $
+.\"
+.Dd April 19, 1994
+.Dt AMD 8
+.Os
+.Sh NAME
+.Nm amd
+.Nd automatically mount file systems
+.Sh SYNOPSIS
+.Nm amd
+.Op Fl nprv
+.Op Fl a Ar mount_point
+.Op Fl c Ar duration
+.Op Fl d Ar domain
+.Bk -words
+.Op Fl k Ar kernel-arch
+.Ek
+.Op Fl l Ar logfile
+.Op Fl t Ar interval.interval
+.Bk -words
+.Op Fl w Ar interval
+.Ek
+.Op Fl x Ar log-option
+.Op Fl y Ar YP-domain
+.Bk -words
+.Op Fl C Ar cluster-name
+.Ek
+.Op Fl D Ar option
+.Oo
+.Ar directory mapname
+.Op Fl map-options
+.Oc
+.Ar ...
+.Sh DESCRIPTION
+.Nm Amd
+is a daemon that automatically mounts filesystems
+whenever a file or directory
+within that filesystem is accessed.
+Filesystems are automatically unmounted when they
+appear to be quiescent.
+.Pp
+.Nm Amd
+operates by attaching itself as an
+.Tn NFS
+server to each of the specified
+.Ar directories .
+Lookups within the specified directories
+are handled by
+.Nm amd ,
+which uses the map defined by
+.Ar mapname
+to determine how to resolve the lookup.
+Generally, this will be a host name, some filesystem information
+and some mount options for the given filesystem.
+.Sh OPTIONS
+.Bl -tag -width Ds
+.It Fl a Ar temporary-directory
+Specify an alternative location for the real mount points.
+The default is
+.Pa /a .
+.It Fl c Ar duration
+Specify a
+.Ar duration ,
+in seconds, that a looked up name remains
+cached when not in use. The default is 5 minutes.
+.It Fl d Ar domain
+Specify the local domain name. If this option is not
+given the domain name is determined from the hostname.
+.It Fl k Ar kernel-arch
+Specifies the kernel architecture. This is used solely
+to set the ${karch} selector.
+.It Fl l Ar logfile
+Specify a logfile in which to record mount and unmount events.
+If
+.Ar logfile
+is the string
+.Em syslog ,
+the log messages will be sent to the system log daemon by
+.Xr syslog 3 .
+.It Fl n
+Normalize hostnames.
+The name referred to by ${rhost} is normalized relative to the
+host database before being used. The effect is to translate
+aliases into ``official'' names.
+.It Fl p
+Print
+.Em PID .
+Outputs the process-id of
+.Nm amd
+to standard output where it can be saved into a file.
+.It Fl r
+Restart existing mounts.
+.Nm Amd
+will scan the mount file table to determine which filesystems
+are currently mounted. Whenever one of these would have
+been auto-mounted,
+.Nm amd
+.Em inherits
+it.
+.It Fl t Ar interval.interval
+Specify the
+.Ar interval ,
+in tenths of a second, between
+.Tn NFS/RPC/UDP
+retries.
+The default is 0.8 seconds.
+The second values alters the retransmit counter.
+Useful defaults are supplied if either or both
+values are missing.
+.It Fl v
+Version. Displays version and configuration information on standard error.
+.It Fl w Ar interval
+Specify an
+.Ar interval ,
+in seconds, between attempts to dismount
+filesystems that have exceeded their cached times.
+The default is 2 minutes.
+.It Fl y Ar domain
+Specify an alternative
+.Tn NIS
+domain from which to fetch the
+.Tn NIS
+maps.
+The default is the system domain name.
+This option is ignored if
+.Tn NIS
+support is not available.
+.It Fl x Ar options
+Specify run-time logging options. The options are a comma separated
+list chosen from: fatal, error, user, warn, info, map, stats, all.
+.It Fl D Ar option
+Select from a variety of debug options. Prefixing an
+option with the string
+.Em no
+reverses the effect of that option. Options are cumulative.
+The most useful option is
+.Ar all .
+.El
+.Pp
+Since
+.Fl D
+is only used for debugging other options are not documented here:
+the current supported set of options is listed by the
+.Fl v
+option
+and a fuller description is available in the program source.
+.Sh FILES
+.Bl -tag -width /axx
+.It Pa /a
+directory under which filesystems are dynamically mounted
+.El
+.Sh CAVEATS
+Some care may be required when creating a mount map.
+.Pp
+Symbolic links on an
+.Tn NFS
+filesystem can be incredibly inefficient.
+In most implementations of
+.Tn NFS ,
+their interpolations are not cached by
+the kernel and each time a symbolic link is
+encountered during a
+.Em lookuppn
+translation it costs an
+.Tn RPC
+call to the
+.Tn NFS
+server.
+A large improvement in real-time
+performance could be gained by adding a cache somewhere.
+Replacing
+.Xr symlink 2
+with a suitable incarnation of the auto-mounter
+results in a large real-time speedup, but also causes a large
+number of process context switches.
+.Pp
+A weird imagination is most useful to gain full advantage of all
+the features.
+.Sh SEE ALSO
+.Xr hostname 1 ,
+.Xr amq 8 ,
+.Xr mount 8 ,
+.Xr umount 8
+.Rs
+.%T Amd \- The 4.4 BSD Automounter
+.Re
+.Sh AUTHOR
+.An Jan-Simon Pendry
+<jsp@doc.ic.ac.uk>, Department of Computing, Imperial College, London, UK.
+.Sh HISTORY
+The
+.Nm amd
+utility first appeared in 4.4BSD.
diff --git a/usr.sbin/amd/amd/amd.c b/usr.sbin/amd/amd/amd.c
new file mode 100644
index 0000000..1d3a1e4
--- /dev/null
+++ b/usr.sbin/amd/amd/amd.c
@@ -0,0 +1,331 @@
+/*
+ * Copyright (c) 1989 Jan-Simon Pendry
+ * Copyright (c) 1989 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)amd.c 8.1 (Berkeley) 6/6/93
+ *
+ * $Id: amd.c,v 1.5 1997/02/22 16:01:26 peter Exp $
+ *
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1989, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+/*
+ * Automounter
+ */
+
+#include "am.h"
+#include <sys/signal.h>
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <setjmp.h>
+
+char pid_fsname[16 + MAXHOSTNAMELEN]; /* "kiska.southseas.nz:(pid%d)" */
+#ifdef HAS_HOST
+#ifdef HOST_EXEC
+char *host_helper;
+#endif /* HOST_EXEC */
+#endif /* HAS_HOST */
+char *auto_dir = "/a";
+char *hostdomain = "unknown.domain";
+char hostname[MAXHOSTNAMELEN] = "localhost"; /* Hostname */
+char hostd[2*MAXHOSTNAMELEN]; /* Host+domain */
+char *op_sys = OS_REP; /* Name of current op_sys */
+char *arch = ARCH_REP; /* Name of current architecture */
+char *endian = ARCH_ENDIAN; /* Big or Little endian */
+char *wire;
+int foreground = 1; /* This is the top-level server */
+int mypid; /* Current process id */
+int immediate_abort; /* Should close-down unmounts be retried */
+struct in_addr myipaddr; /* (An) IP address of this host */
+serv_state amd_state;
+struct amd_stats amd_stats; /* Server statistics */
+time_t do_mapc_reload = 0; /* mapc_reload() call required? */
+jmp_buf select_intr;
+int select_intr_valid;
+int orig_umask;
+
+/*
+ * Signal handler:
+ * SIGINT - tells amd to do a full shutdown, including unmounting all filesystem.
+ * SIGTERM - tells amd to shutdown now. Just unmounts the automount nodes.
+ */
+static void sigterm(sig)
+int sig;
+{
+#ifdef SYS5_SIGNALS
+ signal(sig, sigterm);
+#endif /* SYS5_SIGNALS */
+
+ switch (sig) {
+ case SIGINT:
+ immediate_abort = 15;
+ break;
+
+ case SIGTERM:
+ immediate_abort = -1;
+ /* fall through... */
+
+ default:
+ plog(XLOG_WARNING, "WARNING: automounter going down on signal %d", sig);
+ break;
+ }
+ if (select_intr_valid)
+ longjmp(select_intr, sig);
+}
+
+/*
+ * Hook for cache reload.
+ * When a SIGHUP arrives it schedules a call to mapc_reload
+ */
+/*ARGSUSED*/
+static void sighup(sig)
+int sig;
+{
+#ifdef SYS5_SIGNALS
+ signal(sig, sighup);
+#endif /* SYS5_SIGNALS */
+
+#ifdef DEBUG
+ if (sig != SIGHUP)
+ dlog("spurious call to sighup");
+#endif /* DEBUG */
+ /*
+ * Force a reload by zero'ing the timer
+ */
+ if (amd_state == Run)
+ do_mapc_reload = 0;
+}
+
+/*ARGSUSED*/
+static void parent_exit(sig)
+int sig;
+{
+ exit(0);
+}
+
+static int daemon_mode(P_void)
+{
+ int bgpid;
+
+ signal(SIGQUIT, parent_exit);
+ bgpid = background();
+
+ if (bgpid != 0) {
+ if (print_pid) {
+ printf("%d\n", bgpid);
+ fflush(stdout);
+ }
+ /*
+ * Now wait for the automount points to
+ * complete.
+ */
+ for (;;)
+ pause();
+ }
+
+ signal(SIGQUIT, SIG_DFL);
+
+ /*
+ * Pretend we are in the foreground again
+ */
+ foreground = 1;
+
+#ifdef TIOCNOTTY
+ {
+ int t = open("/dev/tty", O_RDWR);
+ if (t < 0) {
+ if (errno != ENXIO) /* not an error if already no controlling tty */
+ plog(XLOG_WARNING, "Could not open controlling tty: %m");
+ } else {
+ if (ioctl(t, TIOCNOTTY, 0) < 0 && errno != ENOTTY)
+ plog(XLOG_WARNING, "Could not disassociate tty (TIOCNOTTY): %m");
+ (void) close(t);
+ }
+ }
+#else
+ (void) setpgrp();
+#endif /* TIOCNOTTY */
+
+ return getppid();
+}
+
+main(argc, argv)
+int argc;
+char *argv[];
+{
+ char *domdot;
+ int ppid = 0;
+ int error;
+
+ /*
+ * Make sure some built-in assumptions are true before we start
+ */
+ assert(sizeof(nfscookie) >= sizeof (unsigned int));
+ assert(sizeof(int) >= 4);
+
+ /*
+ * Set processing status.
+ */
+ amd_state = Start;
+
+ /*
+ * Initialise process id. This is kept
+ * cached since it is used for generating
+ * and using file handles.
+ */
+ mypid = getpid();
+
+ /*
+ * Get local machine name
+ */
+ if (gethostname(hostname, sizeof(hostname)) < 0) {
+ plog(XLOG_FATAL, "gethostname: %m");
+ going_down(1);
+ }
+ /*
+ * Check it makes sense
+ */
+ if (!*hostname) {
+ plog(XLOG_FATAL, "host name is not set");
+ going_down(1);
+ }
+ /*
+ * Partially initialise hostd[]. This
+ * is completed in get_args().
+ */
+ if (domdot = strchr(hostname, '.')) {
+ /*
+ * Hostname already contains domainname.
+ * Split out hostname and domainname
+ * components
+ */
+ *domdot++ = '\0';
+ hostdomain = domdot;
+ }
+ strcpy(hostd, hostname);
+
+ /*
+ * Trap interrupts for shutdowns.
+ */
+ (void) signal(SIGINT, sigterm);
+
+ /*
+ * Hangups tell us to reload the cache
+ */
+ (void) signal(SIGHUP, sighup);
+
+ /*
+ * Trap Terminate so that we can shutdown gracefully (some chance)
+ */
+ (void) signal(SIGTERM, sigterm);
+ /*
+ * Trap Death-of-a-child. These allow us to
+ * pick up the exit status of backgrounded mounts.
+ * See "sched.c".
+ */
+ (void) signal(SIGCHLD, sigchld);
+
+ /*
+ * Fix-up any umask problems. Most systems default
+ * to 002 which is not too convenient for our purposes
+ */
+ orig_umask = umask(0);
+
+ /*
+ * Figure out primary network name
+ */
+ wire = getwire();
+
+ /*
+ * Determine command-line arguments
+ */
+ get_args(argc, argv);
+
+ /*
+ * Get our own IP address so that we
+ * can mount the automounter.
+ */
+ { struct sockaddr_in sin;
+ get_myaddress(&sin);
+ myipaddr.s_addr = sin.sin_addr.s_addr;
+ }
+
+ /*
+ * Now check we are root.
+ */
+ if (geteuid() != 0) {
+ plog(XLOG_FATAL, "Must be root to mount filesystems (euid = %d)", geteuid());
+ going_down(1);
+ }
+
+#ifdef HAS_NIS_MAPS
+ /*
+ * If the domain was specified then bind it here
+ * to circumvent any default bindings that may
+ * be done in the C library.
+ */
+ if (domain && yp_bind(domain)) {
+ plog(XLOG_FATAL, "Can't bind to domain \"%s\"", domain);
+ going_down(1);
+ }
+#endif /* HAS_NIS_MAPS */
+
+#ifdef DEBUG
+ Debug(D_DAEMON)
+#endif /* DEBUG */
+ ppid = daemon_mode();
+
+ sprintf(pid_fsname, "%s:(pid%d)", hostname, mypid);
+
+ do_mapc_reload = clocktime() + ONE_HOUR;
+
+ /*
+ * Register automounter with system
+ */
+
+ error = mount_automounter(ppid);
+ if (error && ppid)
+ kill(SIGALRM, ppid);
+ going_down(error);
+
+ abort();
+}
diff --git a/usr.sbin/amd/amd/amq_subr.c b/usr.sbin/amd/amd/amq_subr.c
new file mode 100644
index 0000000..ede6aa5
--- /dev/null
+++ b/usr.sbin/amd/amd/amq_subr.c
@@ -0,0 +1,464 @@
+/*
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)amq_subr.c 8.1 (Berkeley) 6/6/93
+ *
+ * $Id$
+ *
+ */
+/*
+ * Auxilliary routines for amq tool
+ */
+
+#include "am.h"
+#include "amq.h"
+#include <ctype.h>
+
+/*ARGSUSED*/
+voidp
+amqproc_null_1(argp, rqstp)
+voidp argp;
+struct svc_req *rqstp;
+{
+ static char res;
+
+ return (voidp) &res;
+}
+
+/*
+ * Return a sub-tree of mounts
+ */
+/*ARGSUSED*/
+amq_mount_tree_p *
+amqproc_mnttree_1(argp, rqstp)
+voidp argp;
+struct svc_req *rqstp;
+{
+ static am_node *mp;
+ mp = find_ap(*(char **) argp);
+ return (amq_mount_tree_p *) &mp;
+}
+
+/*
+ * Unmount a single node
+ */
+/*ARGSUSED*/
+voidp
+amqproc_umnt_1(argp, rqstp)
+voidp argp;
+struct svc_req *rqstp;
+{
+ static char res;
+ am_node *mp = find_ap(*(char **) argp);
+ if (mp)
+ forcibly_timeout_mp(mp);
+
+ return (voidp) &res;
+}
+
+/*
+ * Return global statistics
+ */
+/*ARGSUSED*/
+amq_mount_stats *
+amqproc_stats_1(argp, rqstp)
+voidp argp;
+struct svc_req *rqstp;
+{
+ return (amq_mount_stats *) &amd_stats;
+}
+
+/*
+ * Return the entire tree of mount nodes
+ */
+/*ARGSUSED*/
+amq_mount_tree_list *
+amqproc_export_1(argp, rqstp)
+voidp argp;
+struct svc_req *rqstp;
+{
+ static amq_mount_tree_list aml;
+
+ aml.amq_mount_tree_list_val = (amq_mount_tree_p *) &exported_ap[0];
+ aml.amq_mount_tree_list_len = 1; /* XXX */
+
+ return &aml;
+}
+
+int *
+amqproc_setopt_1(argp, rqstp)
+voidp argp;
+struct svc_req *rqstp;
+{
+ static int rc;
+
+ amq_setopt *opt = (amq_setopt *) argp;
+
+ rc = 0;
+ switch (opt->as_opt) {
+ case AMOPT_DEBUG:
+#ifdef DEBUG
+ if (debug_option(opt->as_str))
+ rc = EINVAL;
+#else
+ rc = EINVAL;
+#endif /* DEBUG */
+ break;
+
+ case AMOPT_LOGFILE:
+#ifdef not_yet
+ if (switch_to_logfile(opt->as_str))
+ rc = EINVAL;
+#else
+ rc = EACCES;
+#endif /* not_yet */
+ break;
+
+ case AMOPT_XLOG:
+ if (switch_option(opt->as_str))
+ rc = EINVAL;
+ break;
+
+ case AMOPT_FLUSHMAPC:
+ if (amd_state == Run) {
+ plog(XLOG_INFO, "amq says flush cache");
+ do_mapc_reload = 0;
+ flush_nfs_fhandle_cache((fserver *) 0);
+ flush_srvr_nfs_cache();
+ }
+ break;
+ }
+ return &rc;
+}
+
+amq_mount_info_list *
+amqproc_getmntfs_1(argp, rqstp)
+voidp argp;
+struct svc_req *rqstp;
+{
+extern qelem mfhead;
+ return (amq_mount_info_list *) &mfhead; /* XXX */
+}
+
+static int ok_security(rqstp)
+struct svc_req *rqstp;
+{
+ struct sockaddr_in *sin;
+
+ sin = svc_getcaller(rqstp->rq_xprt);
+ if (ntohs(sin->sin_port) >= 1024 ||
+ !(sin->sin_addr.s_addr == htonl(0x7f000001) ||
+ sin->sin_addr.s_addr == myipaddr.s_addr)) {
+ char dq[20];
+ plog(XLOG_INFO, "AMQ request from %s.%d DENIED",
+ inet_dquad(dq, sin->sin_addr.s_addr),
+ ntohs(sin->sin_port));
+ return(0);
+ }
+ return(1);
+}
+
+int *
+amqproc_mount_1(argp, rqstp)
+voidp argp;
+struct svc_req *rqstp;
+{
+ static int rc;
+ char *s = *(amq_string *) argp;
+ char *cp;
+
+ plog(XLOG_INFO, "amq requested mount of %s", s);
+ /*
+ * Minimalist security check.
+ */
+ if (!ok_security(rqstp)) {
+ rc = EACCES;
+ return &rc;
+ }
+
+ /*
+ * Find end of key
+ */
+ for (cp = (char *) s; *cp&&(!isascii(*cp)||!isspace(*cp)); cp++)
+ ;
+
+ if (!*cp) {
+ plog(XLOG_INFO, "amqproc_mount: Invalid arguments");
+ rc = EINVAL;
+ return &rc;
+ }
+ *cp++ = '\0';
+
+ /*
+ * Find start of value
+ */
+ while (*cp && isascii(*cp) && isspace(*cp))
+ cp++;
+
+ root_newmap(s, cp, (char *) 0);
+ rc = mount_auto_node(s, (voidp) root_node);
+ if (rc < 0)
+ return 0;
+ return &rc;
+}
+
+amq_string *
+amqproc_getvers_1(argp, rqstp)
+voidp argp;
+struct svc_req *rqstp;
+{
+static amq_string res;
+ res = version;
+ return &res;
+}
+
+/*
+ * XDR routines.
+ */
+bool_t
+xdr_amq_string(xdrs, objp)
+ XDR *xdrs;
+ amq_string *objp;
+{
+ if (!xdr_string(xdrs, objp, AMQ_STRLEN)) {
+ return (FALSE);
+ }
+ return (TRUE);
+}
+
+bool_t
+xdr_amq_setopt(xdrs, objp)
+ XDR *xdrs;
+ amq_setopt *objp;
+{
+ if (!xdr_enum(xdrs, (enum_t *)&objp->as_opt)) {
+ return (FALSE);
+ }
+ if (!xdr_string(xdrs, &objp->as_str, AMQ_STRLEN)) {
+ return (FALSE);
+ }
+ return (TRUE);
+}
+
+/*
+ * More XDR routines - Should be used for OUTPUT ONLY.
+ */
+bool_t
+xdr_amq_mount_tree_node(xdrs, objp)
+ XDR *xdrs;
+ amq_mount_tree *objp;
+{
+ am_node *mp = (am_node *) objp;
+
+ if (!xdr_amq_string(xdrs, &mp->am_mnt->mf_info)) {
+ return (FALSE);
+ }
+ if (!xdr_amq_string(xdrs, &mp->am_path)) {
+ return (FALSE);
+ }
+ if (!xdr_amq_string(xdrs, mp->am_link ? &mp->am_link : &mp->am_mnt->mf_mount)) {
+ return (FALSE);
+ }
+ if (!xdr_amq_string(xdrs, &mp->am_mnt->mf_ops->fs_type)) {
+ return (FALSE);
+ }
+ if (!xdr_long(xdrs, &mp->am_stats.s_mtime)) {
+ return (FALSE);
+ }
+ if (!xdr_u_short(xdrs, &mp->am_stats.s_uid)) {
+ return (FALSE);
+ }
+ if (!xdr_int(xdrs, &mp->am_stats.s_getattr)) {
+ return (FALSE);
+ }
+ if (!xdr_int(xdrs, &mp->am_stats.s_lookup)) {
+ return (FALSE);
+ }
+ if (!xdr_int(xdrs, &mp->am_stats.s_readdir)) {
+ return (FALSE);
+ }
+ if (!xdr_int(xdrs, &mp->am_stats.s_readlink)) {
+ return (FALSE);
+ }
+ if (!xdr_int(xdrs, &mp->am_stats.s_statfs)) {
+ return (FALSE);
+ }
+ return (TRUE);
+}
+
+bool_t
+xdr_amq_mount_subtree(xdrs, objp)
+ XDR *xdrs;
+ amq_mount_tree *objp;
+{
+ am_node *mp = (am_node *) objp;
+
+ if (!xdr_amq_mount_tree_node(xdrs, objp)) {
+ return (FALSE);
+ }
+ if (!xdr_pointer(xdrs, (char **)&mp->am_osib, sizeof(amq_mount_tree), xdr_amq_mount_subtree)) {
+ return (FALSE);
+ }
+ if (!xdr_pointer(xdrs, (char **)&mp->am_child, sizeof(amq_mount_tree), xdr_amq_mount_subtree)) {
+ return (FALSE);
+ }
+ return (TRUE);
+}
+
+bool_t
+xdr_amq_mount_tree(xdrs, objp)
+ XDR *xdrs;
+ amq_mount_tree *objp;
+{
+ am_node *mp = (am_node *) objp;
+ am_node *mnil = 0;
+
+ if (!xdr_amq_mount_tree_node(xdrs, objp)) {
+ return (FALSE);
+ }
+ if (!xdr_pointer(xdrs, (char **)&mnil, sizeof(amq_mount_tree), xdr_amq_mount_subtree)) {
+ return (FALSE);
+ }
+ if (!xdr_pointer(xdrs, (char **)&mp->am_child, sizeof(amq_mount_tree), xdr_amq_mount_subtree)) {
+ return (FALSE);
+ }
+ return (TRUE);
+}
+
+bool_t
+xdr_amq_mount_tree_p(xdrs, objp)
+ XDR *xdrs;
+ amq_mount_tree_p *objp;
+{
+ if (!xdr_pointer(xdrs, (char **)objp, sizeof(amq_mount_tree), xdr_amq_mount_tree)) {
+ return (FALSE);
+ }
+ return (TRUE);
+}
+
+
+bool_t
+xdr_amq_mount_stats(xdrs, objp)
+ XDR *xdrs;
+ amq_mount_stats *objp;
+{
+ if (!xdr_int(xdrs, &objp->as_drops)) {
+ return (FALSE);
+ }
+ if (!xdr_int(xdrs, &objp->as_stale)) {
+ return (FALSE);
+ }
+ if (!xdr_int(xdrs, &objp->as_mok)) {
+ return (FALSE);
+ }
+ if (!xdr_int(xdrs, &objp->as_merr)) {
+ return (FALSE);
+ }
+ if (!xdr_int(xdrs, &objp->as_uerr)) {
+ return (FALSE);
+ }
+ return (TRUE);
+}
+
+
+bool_t
+xdr_amq_mount_tree_list(xdrs, objp)
+ XDR *xdrs;
+ amq_mount_tree_list *objp;
+{
+ if (!xdr_array(xdrs, (char **)&objp->amq_mount_tree_list_val, (u_int *)&objp->amq_mount_tree_list_len, ~0, sizeof(amq_mount_tree_p), xdr_amq_mount_tree_p)) {
+ return (FALSE);
+ }
+ return (TRUE);
+}
+
+bool_t
+xdr_amq_mount_info_qelem(xdrs, qhead)
+ XDR *xdrs;
+ qelem *qhead;
+{
+ /*
+ * Compute length of list
+ */
+ mntfs *mf;
+ u_int len = 0;
+ for (mf = LAST(mntfs, qhead); mf != HEAD(mntfs, qhead); mf = PREV(mntfs, mf)) {
+ if (!(mf->mf_ops->fs_flags & FS_AMQINFO))
+ continue;
+ len++;
+ }
+ xdr_u_int(xdrs, &len);
+
+ /*
+ * Send individual data items
+ */
+ for (mf = LAST(mntfs, qhead); mf != HEAD(mntfs, qhead); mf = PREV(mntfs, mf)) {
+ int up;
+ if (!(mf->mf_ops->fs_flags & FS_AMQINFO))
+ continue;
+
+ if (!xdr_amq_string(xdrs, &mf->mf_ops->fs_type)) {
+ return (FALSE);
+ }
+ if (!xdr_amq_string(xdrs, &mf->mf_mount)) {
+ return (FALSE);
+ }
+ if (!xdr_amq_string(xdrs, &mf->mf_info)) {
+ return (FALSE);
+ }
+ if (!xdr_amq_string(xdrs, &mf->mf_server->fs_host)) {
+ return (FALSE);
+ }
+ if (!xdr_int(xdrs, &mf->mf_error)) {
+ return (FALSE);
+ }
+ if (!xdr_int(xdrs, &mf->mf_refc)) {
+ return (FALSE);
+ }
+ if (mf->mf_server->fs_flags & FSF_ERROR)
+ up = 0;
+ else switch (mf->mf_server->fs_flags & (FSF_DOWN|FSF_VALID)) {
+ case FSF_DOWN|FSF_VALID: up = 0; break;
+ case FSF_VALID: up = 1; break;
+ default: up = -1; break;
+ }
+ if (!xdr_int(xdrs, &up)) {
+ return (FALSE);
+ }
+ }
+ return (TRUE);
+}
diff --git a/usr.sbin/amd/amd/clock.c b/usr.sbin/amd/amd/clock.c
new file mode 100644
index 0000000..a97282f
--- /dev/null
+++ b/usr.sbin/amd/amd/clock.c
@@ -0,0 +1,241 @@
+/*
+ * Copyright (c) 1989 Jan-Simon Pendry
+ * Copyright (c) 1989 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)clock.c 8.1 (Berkeley) 6/6/93
+ *
+ * $Id$
+ *
+ */
+
+/*
+ * Callouts.
+ *
+ * Modelled on kernel object of the same name.
+ * See usual references.
+ *
+ * Use of a heap-based mechanism was rejected:
+ * 1. more complex implementation needed.
+ * 2. not obvious that a list is too slow for Amd.
+ */
+
+#include "am.h"
+
+typedef struct callout callout;
+struct callout {
+ callout *c_next; /* List of callouts */
+ void (*c_fn)(); /* Function to call */
+ voidp c_closure; /* Closure to pass to call */
+ time_t c_time; /* Time of call */
+ int c_id; /* Unique identifier */
+};
+
+static callout callouts; /* List of pending callouts */
+static callout *free_callouts; /* Cache of free callouts */
+static int nfree_callouts; /* Number on free list */
+static int callout_id; /* Next free callout identifier */
+time_t next_softclock; /* Time of next call to softclock() */
+
+/*
+ * Number of callout slots we keep on the free list
+ */
+#define CALLOUT_FREE_SLOP 10
+
+/*
+ * Global assumption: valid id's are non-zero.
+ */
+#define CID_ALLOC() (++callout_id)
+#define CID_UNDEF (0)
+
+static callout *alloc_callout(P_void);
+static callout *alloc_callout()
+{
+ callout *cp = free_callouts;
+ if (cp) {
+ --nfree_callouts;
+ free_callouts = free_callouts->c_next;
+ return cp;
+ }
+ return ALLOC(callout);
+}
+
+static void free_callout P((callout *cp));
+static void free_callout(cp)
+callout *cp;
+{
+ if (nfree_callouts > CALLOUT_FREE_SLOP) {
+ free((voidp) cp);
+ } else {
+ cp->c_next = free_callouts;
+ free_callouts = cp;
+ nfree_callouts++;
+ }
+}
+
+/*
+ * Schedule a callout.
+ *
+ * (*fn)(closure) will be called at clocktime() + secs
+ */
+int timeout P((unsigned int secs, void (*fn)(), voidp closure));
+int timeout(secs, fn, closure)
+unsigned int secs;
+void (*fn)();
+voidp closure;
+{
+ callout *cp, *cp2;
+ time_t t = clocktime() + secs;
+
+ /*
+ * Allocate and fill in a new callout structure
+ */
+ callout *cpnew = alloc_callout();
+ cpnew->c_closure = closure;
+ cpnew->c_fn = fn;
+ cpnew->c_time = t;
+ cpnew->c_id = CID_ALLOC();
+
+ if (t < next_softclock)
+ next_softclock = t;
+
+ /*
+ * Find the correct place in the list
+ */
+ for (cp = &callouts; cp2 = cp->c_next; cp = cp2)
+ if (cp2->c_time >= t)
+ break;
+
+ /*
+ * And link it in
+ */
+ cp->c_next = cpnew;
+ cpnew->c_next = cp2;
+
+ /*
+ * Return callout identifier
+ */
+ return cpnew->c_id;
+}
+
+/*
+ * De-schedule a callout
+ */
+void untimeout P((int id));
+void untimeout(id)
+int id;
+{
+ callout *cp, *cp2;
+ for (cp = &callouts; cp2 = cp->c_next; cp = cp2) {
+ if (cp2->c_id == id) {
+ cp->c_next = cp2->c_next;
+ free_callout(cp2);
+ break;
+ }
+ }
+}
+
+/*
+ * Reschedule after clock changed
+ */
+void reschedule_timeouts P((time_t now, time_t then));
+void reschedule_timeouts(now, then)
+time_t now;
+time_t then;
+{
+ callout *cp;
+
+ for (cp = callouts.c_next; cp; cp = cp->c_next) {
+ if (cp->c_time >= now && cp->c_time <= then) {
+ plog(XLOG_WARNING, "job %d rescheduled to run immediately", cp->c_id);
+#ifdef DEBUG
+ dlog("rescheduling job %d back %d seconds",
+ cp->c_id, cp->c_time - now);
+#endif
+ next_softclock = cp->c_time = now;
+ }
+ }
+}
+
+/*
+ * Clock handler
+ */
+int softclock(P_void);
+int softclock()
+{
+ time_t now;
+ callout *cp;
+
+ do {
+ if (task_notify_todo)
+ do_task_notify();
+
+ now = clocktime();
+
+ /*
+ * While there are more callouts waiting...
+ */
+ while ((cp = callouts.c_next) && cp->c_time <= now) {
+ /*
+ * Extract first from list, save fn & closure and
+ * unlink callout from list and free.
+ * Finally call function.
+ *
+ * The free is done first because
+ * it is quite common that the
+ * function will call timeout()
+ * and try to allocate a callout
+ */
+ void (*fn)() = cp->c_fn;
+ voidp closure = cp->c_closure;
+
+ callouts.c_next = cp->c_next;
+ free_callout(cp);
+#ifdef DEBUG
+ /*dlog("Calling %#x(%#x)", fn, closure);*/
+#endif /* DEBUG */
+ (*fn)(closure);
+ }
+
+ } while (task_notify_todo);
+
+ /*
+ * Return number of seconds to next event,
+ * or 0 if there is no event.
+ */
+ if (cp = callouts.c_next)
+ return cp->c_time - now;
+ return 0;
+}
diff --git a/usr.sbin/amd/amd/efs_ops.c b/usr.sbin/amd/amd/efs_ops.c
new file mode 100644
index 0000000..71e6f9b
--- /dev/null
+++ b/usr.sbin/amd/amd/efs_ops.c
@@ -0,0 +1,135 @@
+/*
+ * Copyright (c) 1989 Jan-Simon Pendry
+ * Copyright (c) 1989 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)efs_ops.c 8.1 (Berkeley) 6/6/93
+ *
+ * $Id$
+ *
+ */
+
+#include "am.h"
+
+#ifdef HAS_EFS
+
+/*
+ * Error file system.
+ * This is used as a last resort catchall if
+ * nothing else worked. EFS just returns lots
+ * of error codes, except for unmount which
+ * always works of course.
+ */
+
+/*
+ * EFS file system always matches
+ */
+static char *efs_match(fo)
+am_opts *fo;
+{
+ return strdup("(error-hook)");
+}
+
+/*ARGSUSED*/
+static int efs_fmount(mf)
+mntfs *mf;
+{
+ return ENOENT;
+}
+
+/*ARGSUSED*/
+static int efs_fumount(mf)
+mntfs *mf;
+{
+ /*
+ * Always succeed
+ */
+
+ return 0;
+}
+
+/*
+ * EFS interface to RPC lookup() routine.
+ * Should never get here in the automounter.
+ * If we do then just give an error.
+ */
+/*ARGSUSED*/
+am_node *efs_lookuppn(mp, fname, error_return, op)
+am_node *mp;
+char *fname;
+int *error_return;
+int op;
+{
+ *error_return = ESTALE;
+ return 0;
+}
+
+/*
+ * EFS interface to RPC readdir() routine.
+ * Should never get here in the automounter.
+ * If we do then just give an error.
+ */
+/*ARGSUSED*/
+int efs_readdir(mp, cookie, dp, ep, count)
+am_node *mp;
+nfscookie cookie;
+dirlist *dp;
+entry *ep;
+int count;
+{
+ return ESTALE;
+}
+
+/*
+ * Ops structure
+ */
+am_ops efs_ops = {
+ "error",
+ efs_match,
+ 0, /* efs_init */
+ auto_fmount,
+ efs_fmount,
+ auto_fumount,
+ efs_fumount,
+ efs_lookuppn,
+ efs_readdir,
+ 0, /* efs_readlink */
+ 0, /* efs_mounted */
+ 0, /* efs_umounted */
+ find_afs_srvr,
+ FS_DISCARD
+};
+
+#endif /* HAS_EFS */
diff --git a/usr.sbin/amd/amd/get_args.c b/usr.sbin/amd/amd/get_args.c
new file mode 100644
index 0000000..27dd8d3
--- /dev/null
+++ b/usr.sbin/amd/amd/get_args.c
@@ -0,0 +1,341 @@
+/*
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE 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[] = "@(#)get_args.c 8.1 (Berkeley) 6/6/93";
+#endif
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+/*
+ * Argument decode
+ */
+
+#include <unistd.h>
+#include "am.h"
+#ifdef HAS_SYSLOG
+#include <syslog.h>
+#endif /* HAS_SYSLOG */
+#include <sys/stat.h>
+
+#if defined(DEBUG) && defined(PARANOID)
+char **gargv;
+#endif /* defined(DEBUG) && defined(PARANOID) */
+int restart_existing_mounts;
+int print_pid;
+int normalize_hosts;
+char *karch; /* Kernel architecture */
+char *cluster; /* Cluster name */
+#ifdef HAS_NIS_MAPS
+char *domain; /* YP domain */
+#endif /* HAS_NIS_MAPS */
+#ifdef UPDATE_MTAB
+char *mtab;
+#endif /* UPDATE_MTAB */
+int afs_timeo = -1;
+int afs_retrans = -1;
+int am_timeo = AM_TTL;
+int am_timeo_w = AM_TTL_W;
+
+#ifdef DEBUG
+/*
+ * List of debug options.
+ */
+static struct opt_tab dbg_opt[] = {
+ { "all", D_ALL }, /* All */
+ { "amq", D_AMQ }, /* Register for AMQ program */
+ { "daemon", D_DAEMON }, /* Enter daemon mode */
+ { "full", D_FULL }, /* Program trace */
+ { "mem", D_MEM }, /* Trace memory allocations */
+ { "mtab", D_MTAB }, /* Use local mtab file */
+ { "str", D_STR }, /* Debug string munging */
+ { "test", D_TEST }, /* Full debug - but no daemon */
+ { "trace", D_TRACE }, /* Protocol trace */
+ { 0, 0 }
+};
+
+int debug_flags = D_AMQ /* Register AMQ */
+ |D_DAEMON /* Enter daemon mode */
+ ;
+
+/*
+ * Switch on/off debug options
+ */
+int debug_option(opt)
+char *opt;
+{
+ return cmdoption(opt, dbg_opt, &debug_flags);
+}
+#endif /* DEBUG */
+
+static void usage __P((void));
+
+void get_args(c, v)
+int c;
+char *v[];
+{
+ int opt_ch;
+ int usageflg = 0;
+ char *logfile = 0;
+ char *sub_domain = 0;
+
+ while ((opt_ch = getopt(c, v, "mnprva:c:d:h:k:l:t:w:x:y:C:D:")) != -1)
+ switch (opt_ch) {
+ case 'a':
+ if (*optarg != '/')
+ errx(1, "-a option must begin with a '/'");
+ auto_dir = optarg;
+ break;
+
+ case 'c':
+ am_timeo = atoi(optarg);
+ if (am_timeo <= 0)
+ am_timeo = AM_TTL;
+ break;
+
+ case 'd':
+ sub_domain = optarg;
+ break;
+
+ case 'h':
+#if defined(HAS_HOST) && defined(HOST_EXEC)
+ host_helper = optarg;
+#else
+ plog(XLOG_USER, "-h: option ignored. HOST_EXEC is not enabled.");
+ break;
+#endif /* defined(HAS_HOST) && defined(HOST_EXEC) */
+
+ case 'k':
+ karch = optarg;
+ break;
+
+ case 'l':
+ logfile = optarg;
+ break;
+
+ case 'm':
+ plog(XLOG_USER, "The -m option is no longer supported.");
+ plog(XLOG_USER, "... Use `ypcat -k am.master` on the command line instead");
+ break;
+
+ case 'n':
+ normalize_hosts = 1;
+ break;
+
+ case 'p':
+ print_pid = 1;
+ break;
+
+ case 'r':
+ restart_existing_mounts = 1;
+ break;
+
+ case 't':
+ /* timeo.retrans */
+ { char *dot = strchr(optarg, '.');
+ if (dot) *dot = '\0';
+ if (*optarg) {
+ afs_timeo = atoi(optarg);
+ }
+ if (dot) {
+ afs_retrans = atoi(dot+1);
+ *dot = '.';
+ }
+ }
+ break;
+
+ case 'v':
+ fprintf(stderr, "%s%s (%s-endian).\n", copyright, version, endian);
+ fputs("Map support for: ", stderr);
+ mapc_showtypes(stderr);
+ fputs(".\nFS: ", stderr);
+ ops_showfstypes(stderr);
+ fputs(".\n", stderr);
+ fprintf(stderr, "Primary network is %s.\n", wire);
+ exit(0);
+ break;
+
+ case 'w':
+ am_timeo_w = atoi(optarg);
+ if (am_timeo_w <= 0)
+ am_timeo_w = AM_TTL_W;
+ break;
+
+ case 'x':
+ usageflg += switch_option(optarg);
+ break;
+
+ case 'y':
+#ifdef HAS_NIS_MAPS
+ domain = optarg;
+#else
+ plog(XLOG_USER, "-y: option ignored. No NIS support available.");
+#endif /* HAS_NIS_MAPS */
+ break;
+
+ case 'C':
+ cluster = optarg;
+ break;
+
+ case 'D':
+#ifdef DEBUG
+ usageflg += debug_option(optarg);
+#else
+ warnx("not compiled with DEBUG option -- sorry");
+#endif /* DEBUG */
+ break;
+
+ default:
+ usageflg = 1;
+ break;
+ }
+
+ if (xlog_level_init == ~0) {
+ (void) switch_option("");
+#ifdef DEBUG
+ usageflg += switch_option("debug");
+#endif /* DEBUG */
+ } else {
+#ifdef DEBUG
+ usageflg += switch_option("debug");
+#endif /* DEBUG */
+ }
+
+ if (usageflg)
+ usage();
+
+ while (optind <= c-2) {
+ char *dir = v[optind++];
+ char *map = v[optind++];
+ char *opts = "";
+ if (v[optind] && *v[optind] == '-')
+ opts = &v[optind++][1];
+
+ root_newmap(dir, opts, map);
+ }
+
+ if (optind == c) {
+#ifdef hpux
+ /*
+ * HP-UX can't handle ./mtab
+ * That system is sick - really.
+ */
+#ifdef DEBUG
+ debug_option("nomtab");
+#endif /* DEBUG */
+#endif /* hpux */
+
+ /*
+ * Append domain name to hostname.
+ * sub_domain overrides hostdomain
+ * if given.
+ */
+ if (sub_domain)
+ hostdomain = sub_domain;
+ if (*hostdomain == '.')
+ hostdomain++;
+ strcat(hostd, ".");
+ strcat(hostd, hostdomain);
+
+#ifdef UPDATE_MTAB
+#ifdef DEBUG
+ if (debug_flags & D_MTAB)
+ mtab = DEBUG_MTAB;
+ else
+#endif /* DEBUG */
+ mtab = MOUNTED;
+#else
+#ifdef DEBUG
+ { if (debug_flags & D_MTAB) {
+ dlog("-D mtab option ignored");
+ } }
+#endif /* DEBUG */
+#endif /* UPDATE_MTAB */
+
+ if (switch_to_logfile(logfile) != 0)
+ plog(XLOG_USER, "Cannot switch logfile");
+
+ /*
+ * If the kernel architecture was not specified
+ * then use the machine architecture.
+ */
+ if (karch == 0)
+ karch = arch;
+
+ if (cluster == 0)
+ cluster = hostdomain;
+
+ if (afs_timeo <= 0)
+ afs_timeo = AFS_TIMEO;
+ if (afs_retrans <= 0)
+ afs_retrans = AFS_RETRANS;
+ if (afs_retrans <= 0)
+ afs_retrans = 3; /* XXX */
+ return;
+ }
+ usage();
+}
+
+
+static void
+usage()
+{
+ fprintf(stderr, "%s\n%s\n%s",
+ "usage: amd [-mnprv] [-a mnt_point] [-c cache_time] [-d domain]",
+ " [-k kernel_arch] [-l logfile|\"syslog\"] [-t afs_timeout]",
+ " [-w wait_timeout] [-C cluster_name]");
+
+#if defined(HAS_HOST) && defined(HOST_EXEC)
+ fputs(" [-h host_helper]", stderr);
+#endif /* defined(HAS_HOST) && defined(HOST_EXEC) */
+
+#ifdef HAS_NIS_MAPS
+ fputs(" [-y nis-domain]\n", stderr);
+#else
+ fputc('\n', stderr);
+#endif /* HAS_NIS_MAPS */
+
+ show_opts('x', xlog_opt);
+#ifdef DEBUG
+ show_opts('D', dbg_opt);
+#endif /* DEBUG */
+ fprintf(stderr, " {directory mapname [-map_options]} ...\n");
+ exit(1);
+}
diff --git a/usr.sbin/amd/amd/host_ops.c b/usr.sbin/amd/amd/host_ops.c
new file mode 100644
index 0000000..bf9eb92
--- /dev/null
+++ b/usr.sbin/amd/amd/host_ops.c
@@ -0,0 +1,743 @@
+/*
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)host_ops.c 8.1 (Berkeley) 6/6/93
+ *
+ * $Id: host_ops.c,v 1.4 1997/02/22 16:01:29 peter Exp $
+ *
+ */
+
+#include "am.h"
+
+#ifdef HAS_HOST
+
+#include "mount.h"
+#include "mountres.h"
+#include <sys/stat.h>
+
+/*
+ * NFS host file system.
+ * Mounts all exported filesystems from a given host.
+ * This has now degenerated into a mess but will not
+ * be rewritten. Amd 6 will support the abstractions
+ * needed to make this work correctly.
+ */
+
+/*
+ * Define HOST_RPC_UDP to use dgram instead of stream RPC.
+ * Datagrams are generally much faster.
+ */
+/*#define HOST_RPC_UDP*/
+
+/*
+ * Define HOST_MKDIRS to make Amd automatically try
+ * to create the mount points.
+ */
+#define HOST_MKDIRS
+
+/*
+ * Determine the mount point
+ */
+#define MAKE_MNTPT(mntpt, ex, mf) { \
+ if (strcmp((ex)->ex_dir, "/") == 0) \
+ strcpy((mntpt), (mf)->mf_mount); \
+ else \
+ sprintf((mntpt), "%s%s", (mf)->mf_mount, (ex)->ex_dir); \
+}
+
+/*
+ * Execute needs the same as NFS plus a helper command
+ */
+static char *host_match P((am_opts *fo));
+static char *host_match(fo)
+am_opts *fo;
+{
+#ifdef HOST_EXEC
+ if (!host_helper) {
+ plog(XLOG_USER, "No host helper command given");
+ return FALSE;
+ }
+#endif /* HOST_EXEC */
+
+ /*
+ * Make sure rfs is specified to keep nfs_match happy...
+ */
+ if (!fo->opt_rfs)
+ fo->opt_rfs = "/";
+
+
+ return (*nfs_ops.fs_match)(fo);
+}
+
+static int host_init(mf)
+mntfs *mf;
+{
+ if (strchr(mf->mf_info, ':') == 0)
+ return ENOENT;
+ return 0;
+}
+
+/*
+ * Two implementations:
+ * HOST_EXEC gets you the external version. The program specified with
+ * the -h option is called. The external program is not published...
+ * roll your own.
+ *
+ * Otherwise you get the native version. Faster but makes the program
+ * bigger.
+ */
+
+#ifndef HOST_EXEC
+
+static bool_t
+xdr_pri_free(xdr_args, args_ptr)
+xdrproc_t xdr_args;
+caddr_t args_ptr;
+{
+ XDR xdr;
+ xdr.x_op = XDR_FREE;
+ return ((*xdr_args)(&xdr, args_ptr));
+}
+
+static int do_mount P((mountres *mrp, char *dir, char *fs_name, char *opts, mntfs *mf));
+static int do_mount(mrp, dir, fs_name, opts, mf)
+mountres *mrp;
+char *dir;
+char *fs_name;
+char *opts;
+mntfs *mf;
+{
+ struct stat stb;
+#ifdef DEBUG
+ dlog("host: mounting fs %s on %s\n", fs_name, dir);
+#endif /* DEBUG */
+#ifdef HOST_MKDIRS
+ (void) mkdirs(dir, 0555);
+#endif /* HOST_MKDIRS */
+ if (stat(dir, &stb) < 0 || (stb.st_mode & S_IFMT) != S_IFDIR) {
+ plog(XLOG_ERROR, "No mount point for %s - skipping", dir);
+ return ENOENT;
+ }
+
+ return mount_nfs_fh(mrp, dir, fs_name, opts, mf);
+}
+
+static int sortfun P((exports *a, exports *b));
+static int sortfun(a, b)
+exports *a,*b;
+{
+ return strcmp((*a)->ex_dir, (*b)->ex_dir);
+}
+
+/*
+ * Get filehandle
+ */
+static int fetch_fhandle P((CLIENT *client, xdrproc_t xdr_mountres, char *dir, mountres *mrp));
+static int fetch_fhandle(client, xdr_mountres, dir, mrp)
+CLIENT *client;
+xdrproc_t xdr_mountres;
+char *dir;
+mountres *mrp;
+{
+ struct timeval tv;
+ enum clnt_stat clnt_stat;
+ int status;
+
+ /*
+ * Pick a number, any number...
+ */
+ tv.tv_sec = 20;
+ tv.tv_usec = 0;
+
+#ifdef DEBUG
+ dlog("Fetching fhandle for %s", dir);
+#endif /* DEBUG */
+ /*
+ * Call the mount daemon on the remote host to
+ * get the filehandle.
+ */
+ clnt_stat = clnt_call(client, MOUNTPROC_MNT, xdr_dirpath, &dir, xdr_mountres, &mrp->mr_mountres, tv);
+ if (clnt_stat == 0)
+ status = mrp->mr_fhstatus.fhs_status; /* XXX assumes fhstatus and mountres3 start the same */
+ if (clnt_stat != RPC_SUCCESS) {
+ extern char *clnt_sperrno();
+ char *msg = clnt_sperrno(clnt_stat);
+ plog(XLOG_ERROR, "mountd rpc failed: %s", msg);
+ return EIO;
+ }
+ /*
+ * Check status of filehandle
+ */
+ if (status) {
+#ifdef DEBUG
+ errno = status;
+ dlog("fhandle fetch failed: %m");
+#endif /* DEBUG */
+ return status;
+ }
+ return 0;
+}
+
+/*
+ * Scan mount table to see if something already mounted
+ */
+static int already_mounted P((mntlist *mlist, char*dir));
+static int already_mounted(mlist, dir)
+mntlist *mlist;
+char *dir;
+{
+ mntlist *ml;
+
+ for (ml = mlist; ml; ml = ml->mnext)
+ if (strcmp(ml->mnt->mnt_dir, dir) == 0)
+ return 1;
+ return 0;
+}
+
+/*
+ * Return TRUE if mount opts contains nfsv2 flag.
+ */
+static int forcev2(mf)
+mntfs *mf;
+{
+ struct mntent mnt;
+
+ mnt.mnt_dir = mf->mf_mount;
+ mnt.mnt_fsname = mf->mf_info;
+ mnt.mnt_type = MTAB_TYPE_NFS;
+ mnt.mnt_opts = mf->mf_mopts;
+ mnt.mnt_freq = 0;
+ mnt.mnt_passno = 0;
+
+ if (hasmntopt(&mnt, "nfsv2") != NULL)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+/*
+ * A helper for host_fmount.
+ */
+static int try_fmount P((mntfs *mf, int mountvers));
+static int try_fmount(mf, mountvers)
+mntfs *mf;
+int mountvers;
+{
+ xdrproc_t xdr_mountres;
+ struct timeval tv2;
+ CLIENT *client;
+ enum clnt_stat clnt_stat;
+ int n_export;
+ int j, k;
+ exports exlist = 0, ex;
+ exports *ep = 0;
+ mountres *mrp = 0;
+ char *host = mf->mf_server->fs_host;
+ int error = 0;
+ struct sockaddr_in sin;
+ int sock = RPC_ANYSOCK;
+ int ok = FALSE;
+ mntlist *mlist;
+ char fs_name[MAXPATHLEN], *rfs_dir;
+ char mntpt[MAXPATHLEN];
+ struct timeval tv;
+ tv.tv_sec = 10; tv.tv_usec = 0;
+
+ if (mountvers == MOUNTVERS)
+ xdr_mountres = xdr_fhstatus;
+ else
+ xdr_mountres = xdr_mountres3;
+
+ /*
+ * Read the mount list
+ */
+ mlist = read_mtab(mf->mf_mount);
+
+ /*
+ * Unlock the mount list
+ */
+ unlock_mntlist();
+
+ /*
+ * Take a copy of the server address
+ */
+ sin = *mf->mf_server->fs_ip;
+
+ /*
+ * Zero out the port - make sure we recompute
+ */
+ sin.sin_port = 0;
+ /*
+ * Make a client end-point.
+ * Try TCP first
+ */
+ if ((client = clnttcp_create(&sin, MOUNTPROG, mountvers, &sock, 0, 0)) == NULL &&
+ (client = clntudp_create(&sin, MOUNTPROG, mountvers, tv, &sock)) == NULL) {
+ plog(XLOG_ERROR, "Failed to make rpc connection to mountd on %s", host);
+ error = EIO;
+ goto out;
+ }
+
+ if (!nfs_auth) {
+ error = make_nfs_auth();
+ if (error)
+ goto out;
+ }
+
+ client->cl_auth = nfs_auth;
+
+#ifdef DEBUG
+ dlog("Fetching export list from %s", host);
+#endif /* DEBUG */
+
+ /*
+ * Fetch the export list
+ */
+ tv2.tv_sec = 10; tv2.tv_usec = 0;
+ clnt_stat = clnt_call(client, MOUNTPROC_EXPORT, xdr_void, 0, xdr_exports, &exlist, tv2);
+ if (clnt_stat != RPC_SUCCESS) {
+ /*clnt_perror(client, "rpc");*/
+ error = EIO;
+ goto out;
+ }
+
+ /*
+ * Figure out how many exports were returned
+ */
+ for (n_export = 0, ex = exlist; ex; ex = ex->ex_next) {
+ /*printf("export %s\n", ex->ex_dir);*/
+ n_export++;
+ }
+#ifdef DEBUG
+ /*dlog("%d exports returned\n", n_export);*/
+#endif /* DEBUG */
+
+ /*
+ * Allocate an array of pointers into the list
+ * so that they can be sorted. If the filesystem
+ * is already mounted then ignore it.
+ */
+ ep = (exports *) xmalloc(n_export * sizeof(exports));
+ for (j = 0, ex = exlist; ex; ex = ex->ex_next) {
+ MAKE_MNTPT(mntpt, ex, mf);
+ if (!already_mounted(mlist, mntpt))
+ ep[j++] = ex;
+ }
+ n_export = j;
+
+ /*
+ * Sort into order.
+ * This way the mounts are done in order down the tree,
+ * instead of any random order returned by the mount
+ * daemon (the protocol doesn't specify...).
+ */
+ qsort(ep, n_export, sizeof(exports), sortfun);
+
+ /*
+ * Allocate an array of filehandles
+ */
+ mrp = (mountres *) xmalloc(n_export * sizeof(mountres));
+ bzero(mrp, n_export * sizeof(mountres));
+
+ /*
+ * Try to obtain filehandles for each directory.
+ * If a fetch fails then just zero out the array
+ * reference but discard the error.
+ */
+ for (j = k = 0; j < n_export; j++) {
+ /* Check and avoid a duplicated export entry */
+ if (j > k && ep[k] && strcmp(ep[j]->ex_dir, ep[k]->ex_dir) == 0) {
+#ifdef DEBUG
+ dlog("avoiding dup fhandle requested for %s", ep[j]->ex_dir);
+#endif
+ ep[j] = 0;
+ } else {
+ k = j;
+ mrp[j].mr_version = mountvers;
+ if (error = fetch_fhandle(client, xdr_mountres, ep[j]->ex_dir, &mrp[j]))
+ ep[j] = 0;
+ }
+ }
+
+ /*
+ * Mount each filesystem for which we have a filehandle.
+ * If any of the mounts succeed then mark "ok" and return
+ * error code 0 at the end. If they all fail then return
+ * the last error code.
+ */
+ strncpy(fs_name, mf->mf_info, sizeof(fs_name));
+ if ((rfs_dir = strchr(fs_name, ':')) == (char *) 0) {
+ plog(XLOG_FATAL, "host_fmount: mf_info has no colon");
+ error = EINVAL;
+ goto out;
+ }
+ ++rfs_dir;
+ for (j = 0; j < n_export; j++) {
+ ex = ep[j];
+ if (ex) {
+ strcpy(rfs_dir, ex->ex_dir);
+ MAKE_MNTPT(mntpt, ex, mf);
+ if (do_mount(&mrp[j], mntpt, fs_name, mf->mf_mopts, mf) == 0)
+ ok = TRUE;
+ }
+ }
+
+ /*
+ * Clean up and exit
+ */
+out:
+ discard_mntlist(mlist);
+ if (ep)
+ free(ep);
+ if (mrp) {
+ for (j = 0; j < n_export; j++)
+ xdr_free(xdr_mountres, (char *) &mrp->mr_mountres);
+ free(mrp);
+ }
+ if (client)
+ clnt_destroy(client);
+ if (exlist)
+ xdr_pri_free(xdr_exports, &exlist);
+ if (ok)
+ return 0;
+ return error;
+}
+
+/*
+ * Mount the export tree from a host
+ */
+static int host_fmount P((mntfs *mf));
+static int host_fmount(mf)
+mntfs *mf;
+{
+ int error = -1;
+
+#ifdef DEBUG
+ dlog("host_fmount: trying to mount v3");
+#endif
+ if (!forcev2(mf))
+ error = try_fmount(mf, MOUNTVERS3);
+ if (error) {
+#ifdef DEBUG
+ dlog("host_fmount: trying to mount v2");
+#endif
+ error = try_fmount(mf, MOUNTVERS);
+ }
+
+ return error;
+}
+
+/*
+ * Return true if pref is a directory prefix of dir.
+ *
+ * TODO:
+ * Does not work if pref is "/".
+ */
+static int directory_prefix P((char *pref, char *dir));
+static int directory_prefix(pref, dir)
+char *pref;
+char *dir;
+{
+ int len = strlen(pref);
+ if (strncmp(pref, dir, len) != 0)
+ return FALSE;
+ if (dir[len] == '/' || dir[len] == '\0')
+ return TRUE;
+ return FALSE;
+}
+
+/*
+ * Unmount a mount tree
+ */
+static int host_fumount P((mntfs *mf));
+static int host_fumount(mf)
+mntfs *mf;
+{
+ mntlist *ml, *mprev;
+ int xerror = 0;
+
+ /*
+ * Read the mount list
+ */
+ mntlist *mlist = read_mtab(mf->mf_mount);
+
+ /*
+ * Unlock the mount list
+ */
+ unlock_mntlist();
+
+ /*
+ * Reverse list...
+ */
+ ml = mlist;
+ mprev = 0;
+ while (ml) {
+ mntlist *ml2 = ml->mnext;
+ ml->mnext = mprev;
+ mprev = ml;
+ ml = ml2;
+ }
+ mlist = mprev;
+
+ /*
+ * Unmount all filesystems...
+ */
+ for (ml = mlist; ml && !xerror; ml = ml->mnext) {
+ char *dir = ml->mnt->mnt_dir;
+ if (directory_prefix(mf->mf_mount, dir)) {
+ int error;
+#ifdef DEBUG
+ dlog("host: unmounts %s", dir);
+#endif /* DEBUG */
+ /*
+ * Unmount "dir"
+ */
+ error = UMOUNT_FS(dir);
+ /*
+ * Keep track of errors
+ */
+ if (error) {
+ if (!xerror)
+ xerror = error;
+ if (error != EBUSY) {
+ errno = error;
+ plog("Tree unmount of %s failed: %m", ml->mnt->mnt_dir);
+ }
+ } else {
+#ifdef HOST_MKDIRS
+ (void) rmdirs(dir);
+#endif /* HOST_MKDIRS */
+ }
+ }
+ }
+
+ /*
+ * Throw away mount list
+ */
+ discard_mntlist(mlist);
+
+ /*
+ * Try to remount, except when we are shutting down.
+ */
+ if (xerror && amd_state != Finishing) {
+ xerror = host_fmount(mf);
+ if (!xerror) {
+ /*
+ * Don't log this - it's usually too verbose
+ plog(XLOG_INFO, "Remounted host %s", mf->mf_info);
+ */
+ xerror = EBUSY;
+ }
+ }
+ return xerror;
+}
+
+/*
+ * Tell mountd we're done.
+ * This is not quite right, because we may still
+ * have other filesystems mounted, but the existing
+ * mountd protocol is badly broken anyway.
+ */
+static void host_umounted(mp)
+am_node *mp;
+{
+#ifdef INFORM_MOUNTD
+ mntfs *mf = mp->am_mnt;
+ char *host;
+ CLIENT *client;
+ enum clnt_stat clnt_stat;
+ struct sockaddr_in sin;
+ int sock = RPC_ANYSOCK;
+ struct timeval tv;
+ tv.tv_sec = 10; tv.tv_usec = 0;
+
+ if (mf->mf_error || mf->mf_refc > 1 || ! mf->mf_server)
+ return;
+
+ host = mf->mf_server->fs_host;
+ sin = *mf->mf_server->fs_ip;
+
+ /*
+ * Zero out the port - make sure we recompute
+ */
+ sin.sin_port = 0;
+ /*
+ * Make a client end-point.
+ * Try TCP first
+ */
+ if ((client = clnttcp_create(&sin, MOUNTPROG, MOUNTVERS, &sock, 0, 0)) == NULL &&
+ (client = clntudp_create(&sin, MOUNTPROG, MOUNTVERS, tv, &sock)) == NULL) {
+ plog(XLOG_ERROR, "Failed to make rpc connection to mountd on %s", host);
+ goto out;
+ }
+
+ if (!nfs_auth) {
+ if (make_nfs_auth())
+ goto out;
+ }
+
+ client->cl_auth = nfs_auth;
+
+#ifdef DEBUG
+ dlog("Unmounting all from %s", host);
+#endif /* DEBUG */
+
+ clnt_stat = clnt_call(client, MOUNTPROC_UMNTALL, xdr_void, 0, xdr_void, 0, tv);
+ if (clnt_stat != RPC_SUCCESS && clnt_stat != RPC_SYSTEMERROR) {
+ /* RPC_SYSTEMERROR seems to be returned for no good reason ...*/
+ extern char *clnt_sperrno();
+ char *msg = clnt_sperrno(clnt_stat);
+ plog(XLOG_ERROR, "unmount all from %s rpc failed: %s", host, msg, clnt_stat);
+ goto out;
+ }
+
+out:
+ if (client)
+ clnt_destroy(client);
+
+#endif /* INFORM_MOUNTD */
+}
+
+
+#else /* HOST_EXEC */
+
+static int host_exec P((char*op, char*host, char*fs, char*opts));
+static int host_exec(op, host, fs, opts)
+char *op;
+char *host;
+char *fs;
+char *opts;
+{
+ int error;
+ char *argv[7];
+
+ /*
+ * Build arg vector
+ */
+ argv[0] = host_helper;
+ argv[1] = host_helper;
+ argv[2] = op;
+ argv[3] = host;
+ argv[4] = fs;
+ argv[5] = opts && *opts ? opts : "rw,default";
+ argv[6] = 0;
+
+ /*
+ * Put stdout to stderr
+ */
+ (void) fclose(stdout);
+ (void) dup(fileno(logfp));
+ if (fileno(logfp) != fileno(stderr)) {
+ (void) fclose(stderr);
+ (void) dup(fileno(logfp));
+ }
+ /*
+ * Try the exec
+ */
+#ifdef DEBUG
+ Debug(D_FULL) {
+ char **cp = argv;
+ plog(XLOG_DEBUG, "executing (un)mount command...");
+ while (*cp) {
+ plog(XLOG_DEBUG, "arg[%d] = '%s'", cp-argv, *cp);
+ cp++;
+ }
+ }
+#endif /* DEBUG */
+ if (argv[0] == 0 || argv[1] == 0) {
+ errno = EINVAL;
+ plog(XLOG_USER, "1st/2nd args missing to (un)mount program");
+ } else {
+ (void) execv(argv[0], argv+1);
+ }
+ /*
+ * Save error number
+ */
+ error = errno;
+ plog(XLOG_ERROR, "exec %s failed: %m", argv[0]);
+
+ /*
+ * Return error
+ */
+ return error;
+}
+
+static int host_mount P((am_node *mp));
+static int host_mount(mp)
+am_node *mp;
+{
+ mntfs *mf = mp->am_mnt;
+
+ return host_exec("mount", mf->mf_server->fs_host, mf->mf_mount, mf->mf_opts);
+}
+
+static int host_umount P((am_node *mp));
+static int host_umount(mp)
+am_node *mp;
+{
+ mntfs *mf = mp->am_mnt;
+
+ return host_exec("unmount", mf->mf_server->fs_host, mf->mf_mount, "xxx");
+}
+
+#endif /* HOST_EXEC */
+
+/*
+ * Ops structure
+ */
+am_ops host_ops = {
+ "host",
+ host_match,
+ host_init,
+ auto_fmount,
+ host_fmount,
+ auto_fumount,
+ host_fumount,
+ efs_lookuppn,
+ efs_readdir,
+ 0, /* host_readlink */
+ 0, /* host_mounted */
+#ifdef HOST_EXEC
+ 0, /* host_umounted */
+#else
+ host_umounted,
+#endif
+ find_nfs_srvr,
+ FS_MKMNT|FS_BACKGROUND|FS_AMQINFO
+};
+
+#endif /* HAS_HOST */
diff --git a/usr.sbin/amd/amd/ifs_ops.c b/usr.sbin/amd/amd/ifs_ops.c
new file mode 100644
index 0000000..d52caa5
--- /dev/null
+++ b/usr.sbin/amd/amd/ifs_ops.c
@@ -0,0 +1,195 @@
+/*
+ * Copyright (c) 1989 Jan-Simon Pendry
+ * Copyright (c) 1989 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ifs_ops.c 8.1 (Berkeley) 6/6/93
+ *
+ * $Id$
+ *
+ */
+
+#include "am.h"
+
+#ifdef HAS_IFS
+
+/*
+ * Inheritance file system.
+ * This implements a filesystem restart.
+ *
+ * This is a *gross* hack - it knows far too
+ * much about the way other parts of the
+ * sytem work. See restart.c too.
+ */
+static char not_a_filesystem[] = "Attempting to inherit not-a-filesystem";
+/*
+ * This should never be called.
+ */
+/*ARGSUSED*/
+static char *ifs_match P((am_opts *fo));
+static char *ifs_match(fo)
+am_opts *fo;
+{
+ plog(XLOG_FATAL, "ifs_match called!");
+ return 0;
+}
+
+static int ifs_init P((mntfs *mf));
+static int ifs_init(mf)
+mntfs *mf;
+{
+ mntfs *mf_link = (mntfs *) mf->mf_private;
+ if (mf_link == 0) {
+ plog(XLOG_FATAL, not_a_filesystem);
+ return EINVAL;
+ }
+#ifdef notdef
+ /*
+ * Fill in attribute fields
+ */
+ mf_link->mf_fattr.type = NFLNK;
+ mf_link->mf_fattr.mode = NFSMODE_LNK | 0777;
+ mf_link->mf_fattr.nlink = 1;
+ mf_link->mf_fattr.size = MAXPATHLEN / 4;
+#endif
+ if (mf_link->mf_ops->fs_init)
+ return (*mf_link->mf_ops->fs_init)(mf_link);
+ return 0;
+}
+
+static mntfs *ifs_inherit P((mntfs *mf));
+static mntfs *ifs_inherit(mf)
+mntfs *mf;
+{
+ /*
+ * Take the linked mount point and
+ * propogate.
+ */
+ mntfs *mf_link = (mntfs *) mf->mf_private;
+ if (mf_link == 0) {
+ plog(XLOG_FATAL, not_a_filesystem);
+ return 0; /*XXX*/
+ }
+
+ mf_link->mf_fo = mf->mf_fo;
+#ifdef notdef
+ mf_link->mf_fattr.fileid = mf->mf_fattr.fileid;
+#endif /* notdef */
+
+ /*
+ * Discard the old map.
+ * Don't call am_unmounted since this
+ * node was never really mounted in the
+ * first place.
+ */
+ mf->mf_private = 0;
+ free_mntfs(mf);
+ /*
+ * Free the dangling reference
+ * to the mount link.
+ */
+ free_mntfs(mf_link);
+ /*
+ * Get a hold of the other entry
+ */
+ mf_link->mf_flags &= ~MFF_RESTART;
+
+ /* Say what happened */
+ plog(XLOG_INFO, "restarting %s on %s", mf_link->mf_info, mf_link->mf_mount);
+
+ return mf_link;
+}
+
+static int ifs_mount P((am_node *mp));
+static int ifs_mount(mp)
+am_node *mp;
+{
+ mntfs *newmf = ifs_inherit(mp->am_mnt);
+ if (newmf) {
+ mp->am_mnt = newmf;
+ /*
+ * XXX - must do the am_mounted call here
+ */
+ if (newmf->mf_ops->fs_flags & FS_MBACKGROUND)
+ am_mounted(mp);
+
+ new_ttl(mp);
+ return 0;
+ }
+ return EINVAL;
+}
+
+static int ifs_fmount P((mntfs *mf));
+static int ifs_fmount(mf)
+mntfs *mf;
+{
+ am_node *mp = find_mf(mf);
+ if (mp)
+ return ifs_mount(mp);
+ return ifs_inherit(mf) ? 0 : EINVAL;
+}
+
+/*ARGSUSED*/
+static int ifs_fumount P((mntfs *mf));
+static int ifs_fumount(mf)
+mntfs *mf;
+{
+ /*
+ * Always succeed
+ */
+ return 0;
+}
+
+/*
+ * Ops structure
+ */
+am_ops ifs_ops = {
+ "inherit",
+ ifs_match,
+ ifs_init,
+ ifs_mount,
+ ifs_fmount,
+ auto_fumount,
+ ifs_fumount,
+ efs_lookuppn,
+ efs_readdir,
+ 0, /* ifs_readlink */
+ 0, /* ifs_mounted */
+ 0, /* ifs_umounted */
+ find_afs_srvr,
+ FS_DISCARD
+};
+
+#endif /* HAS_IFS */
diff --git a/usr.sbin/amd/amd/info_file.c b/usr.sbin/amd/amd/info_file.c
new file mode 100644
index 0000000..4324863
--- /dev/null
+++ b/usr.sbin/amd/amd/info_file.c
@@ -0,0 +1,276 @@
+/*
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)info_file.c 8.1 (Berkeley) 6/6/93
+ *
+ * $Id$
+ *
+ */
+
+/*
+ * Get info from file
+ */
+
+#include "am.h"
+
+#ifdef HAS_FILE_MAPS
+#include <ctype.h>
+#include <sys/stat.h>
+
+#define MAX_LINE_LEN 2048
+
+static int read_line P((char *buf, int size, FILE *fp));
+static int read_line(buf, size, fp)
+char *buf;
+int size;
+FILE *fp;
+{
+ int done = 0;
+
+ do {
+ while (fgets(buf, size, fp)) {
+ int len = strlen(buf);
+ done += len;
+ if (len > 1 && buf[len-2] == '\\' &&
+ buf[len-1] == '\n') {
+ int ch;
+ buf += len - 2;
+ size -= len - 2;
+ *buf = '\n'; buf[1] = '\0';
+ /*
+ * Skip leading white space on next line
+ */
+ while ((ch = getc(fp)) != EOF &&
+ isascii(ch) && isspace(ch))
+ ;
+ (void) ungetc(ch, fp);
+ } else {
+ return done;
+ }
+ }
+ } while (size > 0 && !feof(fp));
+
+ return done;
+}
+
+/*
+ * Try to locate a key in a file
+ */
+static int search_or_reload_file P((FILE *fp, char *map, char *key, char **val, mnt_map *m, void (*fn)(mnt_map *m, char*, char*)));
+static int search_or_reload_file(fp, map, key, val, m, fn)
+FILE *fp;
+char *map;
+char *key;
+char **val;
+mnt_map *m;
+void (*fn) P((mnt_map*, char*, char*));
+{
+ char key_val[MAX_LINE_LEN];
+ int chuck = 0;
+ int line_no = 0;
+
+ while (read_line(key_val, sizeof(key_val), fp)) {
+ char *kp;
+ char *cp;
+ char *hash;
+ int len = strlen(key_val);
+ line_no++;
+
+ /*
+ * Make sure we got the whole line
+ */
+ if (key_val[len-1] != '\n') {
+ plog(XLOG_WARNING, "line %d in \"%s\" is too long", line_no, map);
+ chuck = 1;
+ } else {
+ key_val[len-1] = '\0';
+ }
+
+ /*
+ * Strip comments
+ */
+ hash = strchr(key_val, '#');
+ if (hash)
+ *hash = '\0';
+
+ /*
+ * Find start of key
+ */
+ for (kp = key_val; *kp && isascii(*kp) && isspace(*kp); kp++)
+ ;
+
+ /*
+ * Ignore blank lines
+ */
+ if (!*kp)
+ goto again;
+
+ /*
+ * Find end of key
+ */
+ for (cp = kp; *cp&&(!isascii(*cp)||!isspace(*cp)); cp++)
+ ;
+
+ /*
+ * Check whether key matches
+ */
+ if (*cp)
+ *cp++ = '\0';
+
+ if (fn || (*key == *kp && strcmp(key, kp) == 0)) {
+ while (*cp && isascii(*cp) && isspace(*cp))
+ cp++;
+ if (*cp) {
+ /*
+ * Return a copy of the data
+ */
+ char *dc = strdup(cp);
+ if (fn) {
+ (*fn)(m, strdup(kp), dc);
+ } else {
+ *val = dc;
+#ifdef DEBUG
+ dlog("%s returns %s", key, dc);
+#endif /* DEBUG */
+ }
+ if (!fn)
+ return 0;
+ } else {
+ plog(XLOG_USER, "%s: line %d has no value field", map, line_no);
+ }
+ }
+
+again:
+ /*
+ * If the last read didn't get a whole line then
+ * throw away the remainder before continuing...
+ */
+ if (chuck) {
+ while (fgets(key_val, sizeof(key_val), fp) &&
+ !strchr(key_val, '\n'))
+ ;
+ chuck = 0;
+ }
+ }
+
+ return fn ? 0 : ENOENT;
+}
+
+static FILE *file_open P((char *map, time_t *tp));
+static FILE *file_open(map, tp)
+char *map;
+time_t *tp;
+{
+ FILE *mapf = fopen(map, "r");
+ if (mapf && tp) {
+ struct stat stb;
+ if (fstat(fileno(mapf), &stb) < 0)
+ *tp = clocktime();
+ else
+ *tp = stb.st_mtime;
+ }
+ return mapf;
+}
+
+int file_init P((char *map, time_t *tp));
+int file_init(map, tp)
+char *map;
+time_t *tp;
+{
+ FILE *mapf = file_open(map, tp);
+ if (mapf) {
+ (void) fclose(mapf);
+ return 0;
+ }
+ return errno;
+}
+
+int file_reload P((mnt_map *m, char *map, void (*fn)()));
+int file_reload(m, map, fn)
+mnt_map *m;
+char *map;
+void (*fn)();
+{
+ FILE *mapf = file_open(map, (time_t *) 0);
+ if (mapf) {
+ int error = search_or_reload_file(mapf, map, 0, 0, m, fn);
+ (void) fclose(mapf);
+ return error;
+ }
+
+ return errno;
+}
+
+int file_search P((mnt_map *m, char *map, char *key, char **pval, time_t *tp));
+int file_search(m, map, key, pval, tp)
+mnt_map *m;
+char *map;
+char *key;
+char **pval;
+time_t *tp;
+{
+ time_t t;
+ FILE *mapf = file_open(map, &t);
+ if (mapf) {
+ int error;
+ if (*tp < t) {
+ *tp = t;
+ error = -1;
+ } else {
+ error = search_or_reload_file(mapf, map, key, pval, 0, 0);
+ }
+ (void) fclose(mapf);
+ return error;
+ }
+
+ return errno;
+}
+
+int file_mtime P((char *map, time_t *tp));
+int file_mtime(map, tp)
+char *map;
+time_t *tp;
+{
+ FILE *mapf = file_open(map, tp);
+ if (mapf) {
+ (void) fclose(mapf);
+ return 0;
+ }
+
+ return errno;
+}
+#endif /* HAS_FILE_MAPS */
diff --git a/usr.sbin/amd/amd/info_hes.c b/usr.sbin/amd/amd/info_hes.c
new file mode 100644
index 0000000..10ad21f
--- /dev/null
+++ b/usr.sbin/amd/amd/info_hes.c
@@ -0,0 +1,697 @@
+/*
+ * Copyright (c) 1989 Jan-Simon Pendry
+ * Copyright (c) 1989 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)info_hes.c 8.1 (Berkeley) 6/6/93
+ *
+ * $Id: info_hes.c,v 1.4 1997/02/22 16:01:31 peter Exp $
+ *
+ */
+
+/*
+ * Get info from Hesiod
+ *
+ * Zone transfer code from Bruce Cole <cole@cs.wisc.edu>
+ */
+
+#include "am.h"
+
+#ifdef HAS_HESIOD_MAPS
+#include <hesiod.h>
+
+#define HES_PREFIX "hesiod."
+#define HES_PREFLEN 7
+
+#ifdef HAS_HESIOD_RELOAD
+#include <arpa/nameser.h>
+#include <resolv.h>
+#include <sys/uio.h>
+#include <netdb.h>
+
+/*
+ * Patch up broken system include files
+ */
+#ifndef C_HS
+#define C_HS 4
+#endif
+#ifndef T_TXT
+#define T_TXT 16
+#endif
+
+static int soacnt;
+static struct timeval hs_timeout;
+static int servernum;
+#endif /* HAS_HESIOD_RELOAD */
+
+/*
+ * No easy way to probe the server - check the map name begins with "hesiod."
+ */
+int hesiod_init P((char *map, time_t *tp));
+int hesiod_init(map, tp)
+char *map;
+time_t *tp;
+{
+#ifdef DEBUG
+ dlog("hesiod_init(%s)", map);
+#endif
+ *tp = 0;
+ return strncmp(map, HES_PREFIX, HES_PREFLEN) == 0 ? 0 : ENOENT;
+}
+
+
+/*
+ * Make Hesiod name. Skip past the "hesiod."
+ * at the start of the map name and append
+ * ".automount". The net effect is that a lookup
+ * of /defaults in hesiod.home will result in a
+ * call to hes_resolve("/defaults", "home.automount");
+ */
+#ifdef notdef
+#define MAKE_HES_NAME(dest, src) sprintf(dest, "%s%s", src + HES_PREFLEN, ".automount")
+#endif
+
+/*
+ * Do a Hesiod nameserver call.
+ * Modify time is ignored by Hesiod - XXX
+ */
+int hesiod_search P((mnt_map *m, char *map, char *key, char **pval, time_t *tp));
+int hesiod_search(m, map, key, pval, tp)
+mnt_map *m;
+char *map;
+char *key;
+char **pval;
+time_t *tp;
+{
+ int error;
+ char hes_key[MAXPATHLEN];
+ char **rvec;
+#ifdef DEBUG
+ dlog("hesiod_search(m=%x, map=%s, key=%s, pval=%x tp=%x)", m, map, key, pval, tp);
+#endif
+ /*MAKE_HES_NAME(hes_map, map);*/
+ sprintf(hes_key, "%s.%s", key, map+HES_PREFLEN);
+
+ /*
+ * Call the resolver
+ */
+#ifdef DEBUG
+ dlog("hesiod_search: hes_resolve(%s, %s)", hes_key, "automount");
+#ifdef HAS_HESIOD_RELOAD
+ if (debug_flags & D_FULL)
+ _res.options |= RES_DEBUG;
+#endif
+#endif
+ rvec = hes_resolve(hes_key, "automount");
+ /*
+ * If a reply was forthcoming then return
+ * it (and free subsequent replies)
+ */
+ if (rvec && *rvec) {
+ *pval = *rvec;
+ while (*++rvec)
+ free(*rvec);
+ return 0;
+ }
+
+ /*
+ * Otherwise reflect the hesiod error into a Un*x error
+ */
+#ifdef DEBUG
+ dlog("hesiod_search: Error: %d", hes_error());
+#endif
+ switch (hes_error()) {
+ case HES_ER_NOTFOUND: error = ENOENT; break;
+ case HES_ER_CONFIG: error = EIO; break;
+ case HES_ER_NET: error = ETIMEDOUT; break;
+ default: error = EINVAL; break;
+ }
+#ifdef DEBUG
+ dlog("hesiod_search: Returning: %d", error);
+#endif
+ return error;
+}
+
+#ifdef HAS_HESIOD_RELOAD
+/*
+ * Zone transfer...
+ */
+
+#define MAXHSNS 8
+#define MAX_NSADDR 16
+
+static char *hs_domain;
+static mnt_map *hs_map;
+static int hs_nscount;
+static char nsaddr_list[MAX_NSADDR][sizeof(struct in_addr)];
+
+int hesiod_reload P((mnt_map *m, char *map, void (*fn)()));
+int hesiod_reload(m, map, fn)
+mnt_map *m;
+char *map;
+void (*fn)();
+{
+ char *zone_name, *cp;
+ short domainlen;
+ int status;
+
+#ifdef DEBUG
+ dlog("hesiod_reload (%x %s %x)", m, map, fn);
+#endif DEBUG
+ if (status = res_init()) {
+#ifdef DEBUG
+ dlog("hesiod_reload: res_init failed with %d", status);
+#endif
+ return(status);
+ }
+ _res.retrans = 90;
+ hs_map = m;
+ domainlen = strlen(hostdomain);
+ zone_name = hes_to_bind(map+HES_PREFLEN, "automount");
+ if (*zone_name == '.')
+ zone_name++;
+ hs_domain = zone_name;
+ /* Traverse the DNS tree until we find an SOA we can transfer from.
+ (Our initial zone_name is likely to just be a subtree of a
+ real zone). */
+ do {
+ /* If we can't find any NS records, go up a level in the
+ DNS tree */
+ if (hs_get_ns_list(zone_name) == 0 &&
+ hs_zone_transfer(zone_name) == 0)
+ return(0);
+ /* Move up DNS tree by one component */
+ if (cp = strchr(zone_name, '.'))
+ zone_name = ++cp;
+ else
+ break;
+ } while (strlen(zone_name) >= domainlen);
+#ifdef DEBUG
+ dlog("hesiod_reload: Giving up on %s", hs_domain);
+#endif
+ return(-1);
+}
+
+hs_zone_transfer(domain)
+char *domain;
+{
+ int status, len;
+ char buf[PACKETSZ];
+ /* Want to make sure ansbuf is well alligned */
+ long ansbuf[PACKETSZ/sizeof(long)];
+
+#ifdef DEBUG
+ dlog("hs_zone_transfer (%s)", domain);
+#endif
+ if ((len = res_mkquery(QUERY, domain, C_HS, T_AXFR,
+ (char *)NULL, 0, NULL, buf, PACKETSZ)) == -1) {
+#ifdef DEBUG
+ dlog("hs_zone_transfer: res_mkquery failed");
+#endif
+ errno = 0;
+ return(-1);
+ }
+ if ((status = hs_res_send(buf, len, (char *)ansbuf, PACKETSZ)) == -1) {
+#ifdef DEBUG
+ dlog("hs_zone_transfer: hs_res_send failed. status %d errno %d",
+ status, errno);
+#endif
+ errno = 0;
+ return(-1);
+ }
+ return(0);
+}
+
+#define hs_server_addr(ns) ((struct in_addr *) nsaddr_list[ns])
+
+hs_res_send(buf, buflen, answer, anslen)
+char *buf;
+int buflen;
+char *answer;
+int anslen;
+{
+ int retry, ns;
+ u_short id, len;
+ HEADER *hp = (HEADER *) buf;
+ struct iovec iov[2];
+ static int s = -1;
+ int status;
+ struct sockaddr_in server;
+
+ soacnt = 0;
+ id = hp->id;
+ /*
+ * Send request, RETRY times, or until successful
+ */
+ for (retry = _res.retry; retry > 0; retry--) {
+ for (ns = 0; ns < hs_nscount; ns++) {
+ hs_timeout.tv_sec =
+ (_res.retrans << (_res.retry - retry))
+ / hs_nscount;
+ if (hs_timeout.tv_sec <= 0)
+ hs_timeout.tv_sec = 1;
+ hs_timeout.tv_usec = 0;
+ if (s < 0) {
+ s = socket(AF_INET, SOCK_STREAM, 0);
+ if (s < 0) {
+ continue;
+ }
+ servernum = ns;
+ bcopy(hs_server_addr(ns), &server.sin_addr,
+ sizeof(struct in_addr));
+ server.sin_family = AF_INET;
+ server.sin_port = htons(NAMESERVER_PORT);
+
+ if (connect(s, &server,
+ sizeof(struct sockaddr)) < 0) {
+ (void) close(s);
+ s = -1;
+ continue;
+ }
+ }
+ /*
+ * Send length & message
+ */
+ len = htons((u_short)buflen);
+ iov[0].iov_base = (caddr_t)&len;
+ iov[0].iov_len = sizeof(len);
+ iov[1].iov_base = buf;
+ iov[1].iov_len = buflen;
+ if (writev(s, iov, 2) != sizeof(len) + buflen) {
+ (void) close(s);
+ s = -1;
+ continue;
+ }
+ status = 0;
+ while (s != -1 && soacnt < 2 && status != -2) {
+ if ((status =
+ hs_readresp(s, answer, anslen)) == -1) {
+ (void) close(s);
+ s = -1;
+ continue;
+ }
+ }
+ if (status == -2) {
+ /* There was a permanent error transfering this
+ zone. Give up. */
+ if (s != -1) {
+ (void) close(s);
+ s = -1;
+ }
+ return(-1);
+ }
+ if (s == -1)
+ continue;
+ return (0);
+ }
+ }
+ if (errno == 0)
+ errno = ETIMEDOUT;
+ return (-1);
+}
+
+/* Returns:
+ 0: Success
+ -1: Error
+ -2: Permanent failure
+*/
+hs_readresp(s, answer, anslen)
+int s;
+char *answer;
+int anslen;
+{
+ register int len, n;
+ char *cp;
+
+ cp = answer;
+ len = sizeof(short);
+ while (len != 0 &&
+ (n = hs_res_vcread(s, (char *)cp, (int)len, &hs_timeout)) > 0) {
+ cp += n;
+ len -= n;
+ }
+ if (n <= 0)
+ return(-1);
+ cp = answer;
+ if ((len = _getshort(cp)) > anslen) {
+#ifdef DEBUG
+ dlog("hs_readresp: response too long: %d", len);
+#endif
+ return(-1);
+ }
+ while (len != 0 &&
+ (n = hs_res_vcread(s, (char *)cp, (int)len, &hs_timeout)) > 0) {
+ cp += n;
+ len -= n;
+ }
+ if (n <= 0)
+ return(-1);
+ return(hs_parse(answer, answer+PACKETSZ));
+}
+
+hs_res_vcread(sock, buf, buflen, timeout)
+int sock, buflen;
+char *buf;
+struct timeval *timeout;
+{
+ register int n;
+
+ if ((n = hs_res_selwait(sock, timeout)) > 0)
+ return(read(sock, buf, buflen));
+ else
+ return(n);
+}
+
+hs_res_selwait(sock, timeout)
+int sock;
+struct timeval *timeout;
+{
+ fd_set dsmask;
+ register int n;
+
+ /*
+ * Wait for reply
+ */
+ FD_ZERO(&dsmask);
+ FD_SET(sock, &dsmask);
+ n = select(sock+1, &dsmask, (fd_set *)NULL,
+ (fd_set *)NULL, timeout);
+ return(n);
+}
+
+/* Returns:
+ 0: Success
+ -1: Error
+ -2: Permanent failure
+*/
+hs_parse(msg, eom)
+char *msg, *eom;
+{
+ register char *cp;
+ register HEADER *hp;
+ register int n, len;
+ int qdcount, ancount;
+ char key[PACKETSZ];
+ char *key_cpy, *value, *hs_make_value();
+ short type;
+
+ hp = (HEADER *)msg;
+ if (hp->rcode != NOERROR || hp->opcode != QUERY) {
+ char dq[20];
+#ifdef DEBUG
+ dlog("Bad response (%d) from nameserver %s", hp->rcode, inet_dquad(dq, hs_server_addr(servernum)->s_addr));
+#endif DEBUG
+ return(-1);
+ }
+ cp = msg + sizeof(HEADER);
+ ancount = ntohs(hp->ancount);
+ qdcount = ntohs(hp->qdcount);
+ while (qdcount-- > 0)
+ cp += dn_skipname(cp, eom) + QFIXEDSZ;
+ if (soacnt == 0 && ancount == 0) {
+ /* XXX We should look for NS records to find SOA */
+#ifdef DEBUG
+ dlog("No SOA found");
+#endif
+ return(-2);
+ }
+ while (ancount-- > 0 && cp < eom) {
+ if ((n = dn_expand(msg, eom, cp, key, PACKETSZ)) < 0)
+ break;
+ cp += n;
+ if ((type = _getshort(cp)) == T_SOA) {
+ soacnt++;
+ }
+ cp += 2*sizeof(u_short) + sizeof(u_long);
+ len = _getshort(cp);
+ cp += sizeof(u_short);
+ /* Check to see if key is in our domain */
+ if (type == T_TXT && hs_strip_our_domain(key)) {
+ value = hs_make_value(cp, len);
+ if (value == NULL)
+ return(-1);
+ key_cpy = strdup(key);
+#ifdef DEBUG
+ dlog("hs_parse: Parsed key: %s, value: %s", key,
+ value);
+#endif
+ mapc_add_kv(hs_map, key_cpy, value);
+ }
+ cp += len;
+ errno = 0;
+ }
+ return(0);
+}
+
+/* Check to see if the domain name in the supplied argument matches
+ hs_domain. Strip hs_domain from supplied argument if so. */
+hs_strip_our_domain(name)
+char *name;
+{
+ char *end_pos;
+ short targ_len, cur_len;
+
+ targ_len = strlen(hs_domain);
+ cur_len = strlen(name);
+ if (cur_len <= targ_len)
+ return(0);
+ end_pos = &name[cur_len - targ_len];
+ if (strcmp(end_pos, hs_domain) != 0)
+ return(0);
+ if (*--end_pos != '.')
+ return(0);
+ *end_pos = '\0';
+ return(1);
+}
+
+#define MAXDATA 8*1024
+
+char *
+hs_make_value(cp, len)
+char *cp;
+int len;
+{
+ char *value, *cpcpy, *valuep;
+ int cnt, nextcnt, totalcnt, lencpy;
+#ifdef DEBUG
+ char *dbgname;
+
+ dbgname = &cp[1];
+#endif DEBUG
+
+ lencpy = len;
+ cpcpy = cp;
+ totalcnt = 0;
+ cnt = *cpcpy++;
+ while (cnt) {
+ totalcnt += cnt;
+ lencpy -= cnt+1;
+ if (lencpy == 0)
+ break;
+ nextcnt = cpcpy[cnt];
+ cpcpy = &cpcpy[cnt+1];
+ cnt = nextcnt;
+ }
+ if (totalcnt < 1 || totalcnt > MAXDATA || totalcnt > len) {
+#ifdef DEBUG
+ dlog("TXT RR not of expected length (%d %d): %s", totalcnt,
+ len, dbgname);
+#endif DEBUG
+ return(NULL);
+ }
+ /* Allocate null terminated string */
+ value = (char *) xmalloc(totalcnt+1);
+ value[totalcnt] = '\0';
+ cnt = *cp++;
+ valuep = value;
+ while (cnt) {
+ bcopy(cp, valuep, cnt);
+ len -= cnt+1;
+ if (len == 0)
+ break;
+ valuep = &valuep[cnt];
+ nextcnt = cp[cnt];
+ cp = &cp[cnt+1];
+ cnt = nextcnt;
+ }
+ return(value);
+}
+
+hs_make_ns_query(domain, ansbuf)
+char *domain;
+char *ansbuf;
+{
+ int status, len;
+ char buf[PACKETSZ];
+
+ if ((len = res_mkquery(QUERY, domain, C_HS, T_NS,
+ (char *)NULL, 0, NULL, buf, PACKETSZ)) == -1) {
+#ifdef DEBUG
+ dlog("hs_get_ns_list: res_mkquery failed");
+#endif
+ errno = 0;
+ return(-1);
+ }
+ if ((status = res_send(buf, len, (char *)ansbuf, PACKETSZ)) == -1) {
+#ifdef DEBUG
+ dlog("hs_get_ns_list: res_send failed. status %d errno %d",
+ status, errno);
+#endif
+ errno = 0;
+ return(-1);
+ }
+ return(0);
+}
+
+static void
+add_address(addr)
+struct in_addr *addr;
+{
+ char dq[20];
+ bcopy((char *)addr, nsaddr_list[hs_nscount++], sizeof(struct in_addr));
+#ifdef DEBUG
+ dlog("Adding NS address %s", inet_dquad(dq, addr->s_addr));
+#endif DEBUG
+}
+
+hs_get_ns_list(domain)
+char *domain;
+{
+ register HEADER *hp;
+ int qdcount, nscount;
+ register char *cp;
+ register int n, len;
+ char key[PACKETSZ], name[PACKETSZ], msg[PACKETSZ], *eom;
+ register long **hptr;
+ struct hostent *ghp;
+ int numns;
+ char nsname[MAXHSNS][MAXDATA];
+ int nshaveaddr[MAXHSNS], i;
+ short type;
+
+ if (hs_make_ns_query(domain, msg) == -1)
+ return(-1);
+ numns = hs_nscount = 0;
+ eom = &msg[PACKETSZ];
+ bzero(nsname, sizeof(nsname));
+ hp = (HEADER *)msg;
+ if (hp->rcode != NOERROR || hp->opcode != QUERY) {
+#ifdef DEBUG
+ dlog("Bad response (%d) from nameserver %#x", hp->rcode,
+ hs_server_addr(servernum)->s_addr);
+#endif DEBUG
+ return(-1);
+ }
+ cp = msg + sizeof(HEADER);
+ qdcount = ntohs(hp->qdcount);
+ while (qdcount-- > 0)
+ cp += dn_skipname(cp, eom) + QFIXEDSZ;
+ nscount = ntohs(hp->ancount) + ntohs(hp->nscount) + ntohs(hp->arcount);
+#ifdef DEBUG
+ dlog("hs_get_ns_list: Processing %d response records", nscount);
+#endif
+ for (;nscount; nscount--) {
+ if ((n = dn_expand(msg, eom, cp, key, PACKETSZ)) < 0)
+ break;
+ cp += n;
+ type = _getshort(cp);
+ cp += 2*sizeof(u_short) + sizeof(u_long);
+ len = _getshort(cp);
+ cp += sizeof(u_short);
+#ifdef DEBUG
+ dlog("hs_get_ns_list: Record type: %d", type);
+#endif
+ switch (type) {
+ case T_NS:
+ if (numns >= MAXHSNS || strcasecmp(domain, key) != 0)
+ break;
+ if ((n = dn_expand(msg, eom, cp, name, PACKETSZ)) < 0)
+ break;
+#ifdef DEBUG
+ dlog("hs_get_ns_list: NS name: %s", name);
+#endif
+ for (i = 0; i < numns; i++)
+ if (strcasecmp(nsname[i], name) == 0)
+ break;
+ if (i == numns) {
+#ifdef DEBUG
+ dlog("hs_get_ns_list: Saving name %s", name);
+#endif
+ strncpy(nsname[numns], name, MAXDATA);
+ nshaveaddr[numns] = 0;
+ numns++;
+ }
+ break;
+ case T_A:
+ if (hs_nscount == MAX_NSADDR)
+ break;
+ for (i = 0; i < numns; i++) {
+ if (strcasecmp(nsname[i], domain) == 0) {
+ nshaveaddr[i]++;
+ add_address((struct in_addr *) cp);
+ break;
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ if (hs_nscount == MAX_NSADDR)
+ break;
+ cp += len;
+ errno = 0;
+ }
+#ifdef DEBUG
+ dlog("hs_get_ns_list: Found %d NS records", numns);
+#endif
+ for (i = 0; i < numns; i++) {
+ if (nshaveaddr[i])
+ continue;
+ if ((ghp = gethostbyname(nsname[i])) == 0)
+ continue;
+ for (hptr = (long **)ghp->h_addr_list;
+ *hptr && hs_nscount < MAX_NSADDR; hptr++) {
+ add_address((struct in_addr *) *hptr);
+ }
+ }
+ if (hs_nscount)
+ return(0);
+#ifdef DEBUG
+ dlog("No NS records found for %s", domain);
+ return(-1);
+#endif DEBUG
+}
+#endif /* HAS_HESIOD_RELOAD */
+#endif /* HAS_HESIOD_MAPS */
diff --git a/usr.sbin/amd/amd/info_ndbm.c b/usr.sbin/amd/amd/info_ndbm.c
new file mode 100644
index 0000000..fa3f457
--- /dev/null
+++ b/usr.sbin/amd/amd/info_ndbm.c
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 1989 Jan-Simon Pendry
+ * Copyright (c) 1989 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)info_ndbm.c 8.1 (Berkeley) 6/6/93
+ *
+ * $Id$
+ *
+ */
+
+/*
+ * Get info from NDBM map
+ */
+
+#include "am.h"
+
+#ifdef HAS_NDBM_MAPS
+
+#include <ndbm.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+
+static int search_ndbm P((DBM *db, char *key, char **val));
+static int search_ndbm(db, key, val)
+DBM *db;
+char *key;
+char **val;
+{
+ datum k, v;
+ k.dptr = key;
+ k.dsize = strlen(key) + 1;
+ v = dbm_fetch(db, k);
+ if (v.dptr) {
+ *val = strdup(v.dptr);
+ return 0;
+ }
+ return ENOENT;
+}
+
+int ndbm_search P((mnt_map *m, char *map, char *key, char **pval, time_t *tp));
+int ndbm_search(m, map, key, pval, tp)
+mnt_map *m;
+char *map;
+char *key;
+char **pval;
+time_t *tp;
+{
+ DBM *db;
+
+ db = dbm_open(map, O_RDONLY, 0);
+ if (db) {
+ struct stat stb;
+ int error;
+ error = fstat(dbm_pagfno(db), &stb);
+ if (!error && *tp < stb.st_mtime) {
+ *tp = stb.st_mtime;
+ error = -1;
+ } else {
+ error = search_ndbm(db, key, pval);
+ }
+ (void) dbm_close(db);
+ return error;
+ }
+
+ return errno;
+}
+
+int ndbm_init P((char *map, time_t *tp));
+int ndbm_init(map, tp)
+char *map;
+time_t *tp;
+{
+ DBM *db;
+
+ db = dbm_open(map, O_RDONLY, 0);
+ if (db) {
+ struct stat stb;
+
+ if (fstat(dbm_pagfno(db), &stb) < 0)
+ *tp = clocktime();
+ else
+ *tp = stb.st_mtime;
+ dbm_close(db);
+ return 0;
+ }
+
+ return errno;
+}
+
+#endif /* HAS_NDBM_MAPS */
diff --git a/usr.sbin/amd/amd/info_nis.c b/usr.sbin/amd/amd/info_nis.c
new file mode 100644
index 0000000..72309bb
--- /dev/null
+++ b/usr.sbin/amd/amd/info_nis.c
@@ -0,0 +1,247 @@
+/*
+ * Copyright (c) 1989 Jan-Simon Pendry
+ * Copyright (c) 1989 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)info_nis.c 8.1 (Berkeley) 6/6/93
+ *
+ * $Id$
+ *
+ */
+
+/*
+ * Get info from NIS map
+ */
+
+#include "am.h"
+
+#ifdef HAS_NIS_MAPS
+#include <rpcsvc/yp_prot.h>
+#include <rpcsvc/ypclnt.h>
+
+/*
+ * Figure out the nis domain name
+ */
+static int determine_nis_domain(P_void)
+{
+static int nis_not_running = 0;
+
+ char default_domain[YPMAXDOMAIN];
+
+ if (nis_not_running)
+ return ENOENT;
+
+ if (getdomainname(default_domain, sizeof(default_domain)) < 0) {
+ nis_not_running = 1;
+ plog(XLOG_ERROR, "getdomainname: %m");
+ return EIO;
+ }
+
+ if (!*default_domain) {
+ nis_not_running = 1;
+ plog(XLOG_WARNING, "NIS domain name is not set. NIS ignored.");
+ return ENOENT;
+ }
+
+ domain = strdup(default_domain);
+
+ return 0;
+}
+
+
+#ifdef HAS_NIS_RELOAD
+struct nis_callback_data {
+ mnt_map *ncd_m;
+ char *ncd_map;
+ void (*ncd_fn)();
+};
+
+/*
+ * Callback from yp_all
+ */
+static int callback(status, key, kl, val, vl, data)
+int status;
+char *key;
+int kl;
+char *val;
+int vl;
+struct nis_callback_data *data;
+{
+ if (status == YP_TRUE) {
+ /*
+ * Add to list of maps
+ */
+ char *kp = strnsave(key, kl);
+ char *vp = strnsave(val, vl);
+ (*data->ncd_fn)(data->ncd_m, kp, vp);
+
+ /*
+ * We want more ...
+ */
+ return FALSE;
+ } else {
+ /*
+ * NOMORE means end of map - otherwise log error
+ */
+ if (status != YP_NOMORE) {
+ /*
+ * Check what went wrong
+ */
+ int e = ypprot_err(status);
+
+#ifdef DEBUG
+ plog(XLOG_ERROR, "yp enumeration of %s: %s, status=%d, e=%d",
+ data->ncd_map, yperr_string(e), status, e);
+#else
+ plog(XLOG_ERROR, "yp enumeration of %s: %s", data->ncd_map, yperr_string(e));
+#endif
+ }
+
+ return TRUE;
+ }
+}
+
+int nis_reload P((mnt_map *m, char *map, void (*fn)()));
+int nis_reload(m, map, fn)
+mnt_map *m;
+char *map;
+void (*fn)();
+{
+ struct ypall_callback cbinfo;
+ int error;
+ struct nis_callback_data data;
+
+ if (!domain) {
+ error = determine_nis_domain();
+ if (error)
+ return error;
+ }
+
+ data.ncd_m = m;
+ data.ncd_map = map;
+ data.ncd_fn = fn;
+ cbinfo.data = (voidp) &data;
+ cbinfo.foreach = callback;
+
+ error = yp_all(domain, map, &cbinfo);
+
+ if (error)
+ plog(XLOG_ERROR, "error grabbing nis map of %s: %s", map, yperr_string(ypprot_err(error)));
+
+ return error;
+}
+#endif /* HAS_NIS_RELOAD */
+
+/*
+ * Try to locate a key using NIS.
+ */
+int nis_search P((mnt_map *m, char *map, char *key, char **val, time_t *tp));
+int nis_search(m, map, key, val, tp)
+mnt_map *m;
+char *map;
+char *key;
+char **val;
+time_t *tp;
+{
+ int outlen;
+ int res;
+ int order;
+
+ /*
+ * Make sure domain initialised
+ */
+ if (!domain) {
+ int error = determine_nis_domain();
+ if (error)
+ return error;
+ }
+
+ /*
+ * Check if map has changed
+ */
+ if (yp_order(domain, map, &order))
+ return EIO;
+ if ((time_t) order > *tp) {
+ *tp = (time_t) order;
+ return -1;
+ }
+
+ /*
+ * Lookup key
+ */
+ res = yp_match(domain, map, key, strlen(key), val, &outlen);
+
+ /*
+ * Do something interesting with the return code
+ */
+ switch (res) {
+ case 0:
+ return 0;
+
+ case YPERR_KEY:
+ return ENOENT;
+
+ default:
+ plog(XLOG_ERROR, "%s: %s", map, yperr_string(res));
+ return EIO;
+ }
+}
+
+int nis_init P((char *map, time_t *tp));
+int nis_init(map, tp)
+char *map;
+time_t *tp;
+{
+ int order;
+
+ if (!domain) {
+ int error = determine_nis_domain();
+ if (error)
+ return error;
+ }
+
+ /*
+ * To see if the map exists, try to find
+ * a master for it.
+ */
+ if (yp_order(domain, map, &order))
+ return ENOENT;
+ *tp = (time_t) order;
+#ifdef DEBUG
+ dlog("NIS master for %s@%s has order %d", map, domain, order);
+#endif
+ return 0;
+}
+#endif /* HAS_NIS_MAPS */
diff --git a/usr.sbin/amd/amd/info_passwd.c b/usr.sbin/amd/amd/info_passwd.c
new file mode 100644
index 0000000..067f8c3
--- /dev/null
+++ b/usr.sbin/amd/amd/info_passwd.c
@@ -0,0 +1,162 @@
+/*
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)info_passwd.c 8.1 (Berkeley) 6/6/93
+ *
+ * $Id$
+ *
+ */
+
+/*
+ * Get info from password "file"
+ *
+ * This is experimental and probably doesn't
+ * do what you expect.
+ */
+
+#include "am.h"
+
+#ifdef HAS_PASSWD_MAPS
+#include <pwd.h>
+
+#define PASSWD_MAP "/etc/passwd"
+
+/*
+ * Nothing to probe - check the map name is PASSWD_MAP.
+ */
+int passwd_init P((char *map, time_t *tp));
+int passwd_init(map, tp)
+char *map;
+time_t *tp;
+{
+ *tp = 0;
+ return strcmp(map, PASSWD_MAP) == 0 ? 0 : ENOENT;
+}
+
+
+/*
+ * Grab the entry via the getpwname routine
+ * Modify time is ignored by passwd - XXX
+ */
+int passwd_search P((mnt_map *m, char *map, char *key, char **pval, time_t *tp));
+int passwd_search(m, map, key, pval, tp)
+mnt_map *m;
+char *map;
+char *key;
+char **pval;
+time_t *tp;
+{
+ char *dir = 0;
+ struct passwd *pw;
+ if (strcmp(key, "/defaults") == 0) {
+ *pval = strdup("type:=nfs");
+ return 0;
+ }
+
+ pw = getpwnam(key);
+ if (pw) {
+ /*
+ * We chop the home directory up as follows:
+ * /anydir/dom1/dom2/dom3/user
+ *
+ * and return
+ * rfs:=/anydir/dom3;rhost:=dom3.dom2.dom1;sublink:=user
+ *
+ * This allows cross-domain entries in your passwd file.
+ * ... but forget about security!
+ */
+ char *user;
+ char *p, *q;
+ char val[MAXPATHLEN];
+ char rhost[MAXHOSTNAMELEN];
+ dir = strdup(pw->pw_dir);
+ /*
+ * Find user name. If no / then Invalid...
+ */
+ user = strrchr(dir, '/');
+ if (!user)
+ goto enoent;
+ *user++ = '\0';
+ /*
+ * Find start of host "path". If no / then Invalid...
+ */
+ p = strchr(dir+1, '/');
+ if (!p)
+ goto enoent;
+ *p++ = '\0';
+ /*
+ * At this point, p is dom1/dom2/dom3
+ * Copy, backwards, into rhost replacing
+ * / with .
+ */
+ rhost[0] = '\0';
+ do {
+ q = strrchr(p, '/');
+ if (q) {
+ strcat(rhost, q + 1);
+ strcat(rhost, ".");
+ *q = '\0';
+ } else {
+ strcat(rhost, p);
+ }
+ } while (q);
+ /*
+ * Sanity check
+ */
+ if (*rhost == '\0' || *user == '\0' || *dir == '\0')
+ goto enoent;
+ /*
+ * Make up return string
+ */
+ q = strchr(rhost, '.');
+ if (q)
+ *q = '\0';
+ sprintf(val, "rfs:=%s/%s;rhost:=%s;sublink:=%s;fs:=${autodir}%s",
+ dir, rhost, rhost, user, pw->pw_dir);
+ if (q)
+ *q = '.';
+ *pval = strdup(val);
+ return 0;
+ }
+
+enoent:
+ if (dir)
+ free(dir);
+
+ return ENOENT;
+}
+#endif /* HAS_PASSWD_MAPS */
diff --git a/usr.sbin/amd/amd/info_union.c b/usr.sbin/amd/amd/info_union.c
new file mode 100644
index 0000000..e72de44
--- /dev/null
+++ b/usr.sbin/amd/amd/info_union.c
@@ -0,0 +1,155 @@
+/*
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)info_union.c 8.1 (Berkeley) 6/6/93
+ *
+ * $Id$
+ *
+ */
+
+/*
+ * Get info from the system namespace
+ *
+ * NOTE: Cannot handle reads back through the automounter.
+ * THIS WILL CAUSE A DEADLOCK!
+ */
+
+#include "am.h"
+
+#ifdef HAS_UNION_MAPS
+
+#include <unistd.h> /* for _POSIX_VERSION ifdef */
+
+#if defined(_POSIX_SOURCE) || defined(_POSIX_VERSION)
+#include <dirent.h>
+#define DIRENT struct dirent
+#else
+#include <sys/dir.h>
+#define DIRENT struct direct
+#endif
+
+#define UNION_PREFIX "union:"
+#define UNION_PREFLEN 6
+
+/*
+ * No way to probe - check the map name begins with "union:"
+ */
+int union_init P((char *map, time_t *tp));
+int union_init(map, tp)
+char *map;
+time_t *tp;
+{
+ *tp = 0;
+ return strncmp(map, UNION_PREFIX, UNION_PREFLEN) == 0 ? 0 : ENOENT;
+}
+
+int union_search P((mnt_map *m, char *map, char *key, char **pval, time_t *tp));
+int union_search(m, map, key, pval, tp)
+mnt_map *m;
+char *map;
+char *key;
+char **pval;
+time_t *tp;
+{
+ char *mapd = strdup(map + UNION_PREFLEN);
+ char **v = strsplit(mapd, ':', '\"');
+ char **p;
+ for (p = v; p[1]; p++)
+ ;
+ *pval = xmalloc(strlen(*p) + 5);
+ sprintf(*pval, "fs:=%s", *p);
+ free(mapd);
+ free(v);
+ return 0;
+}
+
+int union_reload P((mnt_map *m, char *map, void (*fn)()));
+int union_reload(m, map, fn)
+mnt_map *m;
+char *map;
+void (*fn)();
+{
+ char *mapd = strdup(map + UNION_PREFLEN);
+ char **v = strsplit(mapd, ':', '\"');
+ char **dir;
+
+ /*
+ * Add fake /defaults entry
+ */
+ (*fn)(m, strdup("/defaults"), strdup("type:=link;opts:=nounmount;sublink:=${key}"));
+
+ for (dir = v; *dir; dir++) {
+ int dlen;
+ DIRENT *dp;
+ DIR *dirp = opendir(*dir);
+ if (!dirp) {
+ plog(XLOG_USER, "Cannot read directory %s: %m", *dir);
+ continue;
+ }
+ dlen = strlen(*dir);
+#ifdef DEBUG
+ dlog("Reading directory %s...", *dir);
+#endif
+ while (dp = readdir(dirp)) {
+ char *val;
+ if (dp->d_name[0] == '.' &&
+ (dp->d_name[1] == '\0' ||
+ (dp->d_name[1] == '.' && dp->d_name[2] == '\0')))
+ continue;
+
+#ifdef DEBUG
+ dlog("... gives %s", dp->d_name);
+#endif
+ val = xmalloc(dlen + 5);
+ sprintf(val, "fs:=%s", *dir);
+ (*fn)(m, strdup(dp->d_name), val);
+ }
+ closedir(dirp);
+ }
+ /*
+ * Add wildcard entry
+ */
+ { char *val = xmalloc(strlen(dir[-1]) + 5);
+ sprintf(val, "fs:=%s", dir[-1]);
+ (*fn)(m, strdup("*"), val);
+ }
+ free(mapd);
+ free(v);
+ return 0;
+}
+
+#endif /* HAS_UNION_MAPS */
diff --git a/usr.sbin/amd/amd/map.c b/usr.sbin/amd/amd/map.c
new file mode 100644
index 0000000..9aa480f
--- /dev/null
+++ b/usr.sbin/amd/amd/map.c
@@ -0,0 +1,1140 @@
+/*-
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id$
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)map.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "am.h"
+
+/*
+ * Generation Numbers.
+ *
+ * Generation numbers are allocated to every node created
+ * by amd. When a filehandle is computed and sent to the
+ * kernel, the generation number makes sure that it is safe
+ * to reallocate a node slot even when the kernel has a cached
+ * reference to its old incarnation.
+ * No garbage collection is done, since it is assumed that
+ * there is no way that 2^32 generation numbers could ever
+ * be allocated by a single run of amd - there is simply
+ * not enough cpu time available.
+ */
+static unsigned int am_gen = 2; /* Initial generation number */
+#define new_gen() (am_gen++)
+
+am_node **exported_ap = (am_node **) 0;
+int exported_ap_size = 0;
+int first_free_map = 0; /* First available free slot */
+int last_used_map = -1; /* Last unavailable used slot */
+static int timeout_mp_id; /* Id from last call to timeout */
+
+/*
+ * This is the default attributes field which
+ * is copied into every new node to be created.
+ * The individual filesystem fs_init() routines
+ * patch the copy to represent the particular
+ * details for the relevant filesystem type
+ */
+static struct fattr gen_fattr = {
+ NFLNK, /* type */
+ NFSMODE_LNK | 0777, /* mode */
+ 1, /* nlink */
+ 0, /* uid */
+ 0, /* gid */
+ 0, /* size */
+ 4096, /* blocksize */
+ 0, /* rdev */
+ 1, /* blocks */
+ 0, /* fsid */
+ 0, /* fileid */
+ { 0, 0 }, /* atime */
+ { 0, 0 }, /* mtime */
+ { 0, 0 }, /* ctime */
+};
+
+/*
+ * Resize exported_ap map
+ */
+static int exported_ap_realloc_map P((int nsize));
+static int exported_ap_realloc_map(nsize)
+int nsize;
+{
+#ifdef notdef
+ /*
+ * If a second realloc occasionally causes Amd to die
+ * in then include this check.
+ */
+ if (exported_ap_size != 0) /* XXX */
+ return 0;
+#endif
+
+ /*
+ * this shouldn't happen, but...
+ */
+ if (nsize < 0 || nsize == exported_ap_size)
+ return 0;
+
+ exported_ap = (am_node **) xrealloc((voidp) exported_ap, nsize * sizeof(am_node*));
+
+ if (nsize > exported_ap_size)
+ bzero((char*) (exported_ap+exported_ap_size),
+ (nsize - exported_ap_size) * sizeof(am_node*));
+ exported_ap_size = nsize;
+
+ return 1;
+}
+
+
+/*
+ * The root of the mount tree.
+ */
+am_node *root_node;
+
+/*
+ * Allocate a new mount slot and create
+ * a new node.
+ * Fills in the map number of the node,
+ * but leaves everything else uninitialised.
+ */
+am_node *exported_ap_alloc(P_void)
+{
+ am_node *mp, **mpp;
+
+ /*
+ * First check if there are any slots left, realloc if needed
+ */
+ if (first_free_map >= exported_ap_size)
+ if (!exported_ap_realloc_map(exported_ap_size + NEXP_AP))
+ return 0;
+
+ /*
+ * Grab the next free slot
+ */
+ mpp = exported_ap + first_free_map;
+ mp = *mpp = ALLOC(am_node);
+ bzero((char *) mp, sizeof(*mp));
+
+ mp->am_mapno = first_free_map++;
+
+ /*
+ * Update free pointer
+ */
+ while (first_free_map < exported_ap_size && exported_ap[first_free_map])
+ first_free_map++;
+
+ if (first_free_map > last_used_map)
+ last_used_map = first_free_map - 1;
+
+ /*
+ * Shrink exported_ap if reasonable
+ */
+ if (last_used_map < exported_ap_size - (NEXP_AP + NEXP_AP_MARGIN))
+ exported_ap_realloc_map(exported_ap_size - NEXP_AP);
+
+#ifdef DEBUG
+ /*dlog("alloc_exp: last_used_map = %d, first_free_map = %d\n",
+ last_used_map, first_free_map);*/
+#endif /* DEBUG */
+
+ return mp;
+}
+
+/*
+ * Free a mount slot
+ */
+void exported_ap_free P((am_node *mp));
+void exported_ap_free(mp)
+am_node *mp;
+{
+ /*
+ * Sanity check
+ */
+ if (!mp)
+ return;
+
+ /*
+ * Zero the slot pointer to avoid double free's
+ */
+ exported_ap[mp->am_mapno] = 0;
+
+ /*
+ * Update the free and last_used indices
+ */
+ if (mp->am_mapno == last_used_map)
+ while (last_used_map >= 0 && exported_ap[last_used_map] == 0)
+ --last_used_map;
+
+ if (first_free_map > mp->am_mapno)
+ first_free_map = mp->am_mapno;
+
+#ifdef DEBUG
+ /*dlog("free_exp: last_used_map = %d, first_free_map = %d\n",
+ last_used_map, first_free_map);*/
+#endif /* DEBUG */
+
+ /*
+ * Free the mount node
+ */
+ free((voidp) mp);
+}
+
+/*
+ * Insert mp into the correct place,
+ * where p_mp is its parent node.
+ * A new node gets placed as the youngest sibling
+ * of any other children, and the parent's child
+ * pointer is adjusted to point to the new child node.
+ */
+void insert_am(mp, p_mp)
+am_node *mp;
+am_node *p_mp;
+{
+ /*
+ * If this is going in at the root then flag it
+ * so that it cannot be unmounted by amq.
+ */
+ if (p_mp == root_node)
+ mp->am_flags |= AMF_ROOT;
+ /*
+ * Fill in n-way links
+ */
+ mp->am_parent = p_mp;
+ mp->am_osib = p_mp->am_child;
+ if (mp->am_osib)
+ mp->am_osib->am_ysib = mp;
+ p_mp->am_child = mp;
+}
+
+/*
+ * Remove am from its place in the mount tree
+ */
+void remove_am(mp)
+am_node *mp;
+{
+ /*
+ * 1. Consistency check
+ */
+ if (mp->am_child && mp->am_parent) {
+ plog(XLOG_WARNING, "children of \"%s\" still exist - deleting anyway", mp->am_path);
+ }
+
+ /*
+ * 2. Update parent's child pointer
+ */
+ if (mp->am_parent && mp->am_parent->am_child == mp)
+ mp->am_parent->am_child = mp->am_osib;
+
+ /*
+ * 3. Unlink from sibling chain
+ */
+ if (mp->am_ysib)
+ mp->am_ysib->am_osib = mp->am_osib;
+ if (mp->am_osib)
+ mp->am_osib->am_ysib = mp->am_ysib;
+}
+
+/*
+ * Compute a new time to live value for a node.
+ */
+void new_ttl(mp)
+am_node *mp;
+{
+ mp->am_timeo_w = 0;
+
+ mp->am_ttl = clocktime();
+ mp->am_fattr.atime.seconds = mp->am_ttl;
+ mp->am_ttl += mp->am_timeo; /* sun's -tl option */
+}
+
+void mk_fattr P((am_node *mp, ftype vntype));
+void mk_fattr(mp, vntype)
+am_node *mp;
+ftype vntype;
+{
+ switch (vntype) {
+ case NFDIR:
+ mp->am_fattr.type = NFDIR;
+ mp->am_fattr.mode = NFSMODE_DIR | 0555;
+ mp->am_fattr.nlink = 2;
+ mp->am_fattr.size = 512;
+ break;
+ case NFLNK:
+ mp->am_fattr.type = NFLNK;
+ mp->am_fattr.mode = NFSMODE_LNK | 0777;
+ mp->am_fattr.nlink = 1;
+ mp->am_fattr.size = 0;
+ break;
+ default:
+ plog(XLOG_FATAL, "Unknown fattr type %d - ignored", vntype);
+ break;
+ }
+}
+
+/*
+ * Initialise an allocated mount node.
+ * It is assumed that the mount node was bzero'd
+ * before getting here so anything that would
+ * be set to zero isn't done here.
+ */
+void init_map(mp, dir)
+am_node *mp;
+char *dir;
+{
+ /* mp->am_mapno initalised by exported_ap_alloc */
+ mp->am_mnt = new_mntfs();
+ mp->am_name = strdup(dir);
+ mp->am_path = strdup(dir);
+ /*mp->am_link = 0;*/
+ /*mp->am_parent = 0;*/
+ /*mp->am_ysib = 0;*/
+ /*mp->am_osib = 0;*/
+ /*mp->am_child = 0;*/
+ /*mp->am_flags = 0;*/
+ /*mp->am_error = 0;*/
+ mp->am_gen = new_gen();
+ /*mp->am_pref = 0;*/
+
+ mp->am_timeo = am_timeo;
+ mp->am_attr.status = NFS_OK;
+ mp->am_fattr = gen_fattr;
+ mp->am_fattr.fsid = 42;
+ mp->am_fattr.fileid = 0;
+ mp->am_fattr.atime.seconds = clocktime();
+ mp->am_fattr.atime.useconds = 0;
+ mp->am_fattr.mtime = mp->am_fattr.ctime = mp->am_fattr.atime;
+
+ new_ttl(mp);
+ mp->am_stats.s_mtime = mp->am_fattr.atime.seconds;
+ /*mp->am_private = 0;*/
+}
+
+/*
+ * Free a mount node.
+ * The node must be already unmounted.
+ */
+void free_map(mp)
+am_node *mp;
+{
+ remove_am(mp);
+
+ if (mp->am_link)
+ free(mp->am_link);
+ if (mp->am_name)
+ free(mp->am_name);
+ if (mp->am_path)
+ free(mp->am_path);
+ if (mp->am_pref)
+ free(mp->am_pref);
+
+ if (mp->am_mnt)
+ free_mntfs(mp->am_mnt);
+
+ exported_ap_free(mp);
+}
+
+/*
+ * Convert from file handle to
+ * automount node.
+ */
+am_node *fh_to_mp3(fhp, rp, c_or_d)
+nfs_fh *fhp;
+int *rp;
+int c_or_d;
+{
+ struct am_fh *fp = (struct am_fh *) fhp;
+ am_node *ap = 0;
+
+ /*
+ * Check process id matches
+ * If it doesn't then it is probably
+ * from an old kernel cached filehandle
+ * which is now out of date.
+ */
+ if (fp->fhh_pid != mypid)
+ goto drop;
+
+ /*
+ * Make sure the index is valid before
+ * exported_ap is referenced.
+ */
+ if (fp->fhh_id < 0 || fp->fhh_id >= exported_ap_size)
+ goto drop;
+
+ /*
+ * Get hold of the supposed mount node
+ */
+ ap = exported_ap[fp->fhh_id];
+
+ /*
+ * If it exists then maybe...
+ */
+ if (ap) {
+ /*
+ * Check the generation number in the node
+ * matches the one from the kernel. If not
+ * then the old node has been timed out and
+ * a new one allocated.
+ */
+ if (ap->am_gen != fp->fhh_gen) {
+ ap = 0;
+ goto drop;
+ }
+
+ /*
+ * If the node is hung then locate a new node
+ * for it. This implements the replicated filesystem
+ * retries.
+ */
+ if (ap->am_mnt && FSRV_ISDOWN(ap->am_mnt->mf_server) && ap->am_parent) {
+ int error;
+ am_node *orig_ap = ap;
+#ifdef DEBUG
+ dlog("fh_to_mp3: %s (%s) is hung:- call lookup",
+ orig_ap->am_path, orig_ap->am_mnt->mf_info);
+#endif /* DEBUG */
+ /*
+ * Update modify time of parent node.
+ * With any luck the kernel will re-stat
+ * the child node and get new information.
+ */
+ orig_ap->am_fattr.mtime.seconds = clocktime();
+
+ /*
+ * Call the parent's lookup routine for an object
+ * with the same name. This may return -1 in error
+ * if a mount is in progress. In any case, if no
+ * mount node is returned the error code is propagated
+ * to the caller.
+ */
+ if (c_or_d == VLOOK_CREATE) {
+ ap = (*orig_ap->am_parent->am_mnt->mf_ops->lookuppn)(orig_ap->am_parent,
+ orig_ap->am_name, &error, c_or_d);
+ } else {
+ ap = 0;
+ error = ESTALE;
+ }
+ if (ap == 0) {
+ if (error < 0 && amd_state == Finishing)
+ error = ENOENT;
+ *rp = error;
+ return 0;
+ }
+ /*
+ * Update last access to original node. This
+ * avoids timing it out and so sending ESTALE
+ * back to the kernel.
+ * XXX - Not sure we need this anymore (jsp, 90/10/6).
+ */
+ new_ttl(orig_ap);
+
+ }
+ /*
+ * Disallow references to objects being unmounted, unless
+ * they are automount points.
+ */
+ if (ap->am_mnt && (ap->am_mnt->mf_flags & MFF_UNMOUNTING) &&
+ !(ap->am_flags & AMF_ROOT)) {
+ if (amd_state == Finishing)
+ *rp = ENOENT;
+ else
+ *rp = -1;
+ return 0;
+ }
+ new_ttl(ap);
+ }
+
+drop:
+ if (!ap || !ap->am_mnt) {
+ /*
+ * If we are shutting down then it is likely
+ * that this node has disappeared because of
+ * a fast timeout. To avoid things thrashing
+ * just pretend it doesn't exist at all. If
+ * ESTALE is returned, some NFS clients just
+ * keep retrying (stupid or what - if it's
+ * stale now, what's it going to be in 5 minutes?)
+ */
+ if (amd_state == Finishing)
+ *rp = ENOENT;
+ else
+ *rp = ESTALE;
+ amd_stats.d_stale++;
+ }
+
+ return ap;
+}
+
+am_node *fh_to_mp(fhp)
+nfs_fh *fhp;
+{
+ int dummy;
+ return fh_to_mp2(fhp, &dummy);
+}
+
+/*
+ * Convert from automount node to
+ * file handle.
+ */
+void mp_to_fh(mp, fhp)
+am_node *mp;
+struct nfs_fh *fhp;
+{
+ struct am_fh *fp = (struct am_fh *) fhp;
+
+ /*
+ * Take the process id
+ */
+ fp->fhh_pid = mypid;
+ /*
+ * .. the map number
+ */
+ fp->fhh_id = mp->am_mapno;
+ /*
+ * .. and the generation number
+ */
+ fp->fhh_gen = mp->am_gen;
+ /*
+ * .. to make a "unique" triple that will never
+ * be reallocated except across reboots (which doesn't matter)
+ * or if we are unlucky enough to be given the same
+ * pid as a previous amd (very unlikely).
+ */
+}
+
+static am_node *find_ap2 P((char *dir, am_node *mp));
+static am_node *find_ap2(dir, mp)
+char *dir;
+am_node *mp;
+{
+ if (mp) {
+ am_node *mp2;
+ if (strcmp(mp->am_path, dir) == 0)
+ return mp;
+
+ if ((mp->am_mnt->mf_flags & MFF_MOUNTED) &&
+ strcmp(mp->am_mnt->mf_mount, dir) == 0)
+ return mp;
+
+ mp2 = find_ap2(dir, mp->am_osib);
+ if (mp2)
+ return mp2;
+ return find_ap2(dir, mp->am_child);
+ }
+
+ return 0;
+}
+
+/*
+ * Find the mount node corresponding
+ * to dir. dir can match either the
+ * automount path or, if the node is
+ * mounted, the mount location.
+ */
+am_node *find_ap P((char *dir));
+am_node *find_ap(dir)
+char *dir;
+{
+ int i;
+
+ for (i = last_used_map; i >= 0; --i) {
+ am_node *mp = exported_ap[i];
+ if (mp && (mp->am_flags & AMF_ROOT)) {
+ mp = find_ap2(dir, exported_ap[i]);
+ if (mp)
+ return mp;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Find the mount node corresponding
+ * to the mntfs structure.
+ */
+am_node *find_mf P((mntfs *mf));
+am_node *find_mf(mf)
+mntfs *mf;
+{
+ int i;
+
+ for (i = last_used_map; i >= 0; --i) {
+ am_node *mp = exported_ap[i];
+ if (mp && mp->am_mnt == mf)
+ return mp;
+ }
+ return 0;
+}
+
+/*
+ * Get the filehandle for a particular named directory.
+ * This is used during the bootstrap to tell the kernel
+ * the filehandles of the initial automount points.
+ */
+nfs_fh *root_fh(dir)
+char *dir;
+{
+ static nfs_fh nfh;
+ am_node *mp = root_ap(dir, TRUE);
+ if (mp) {
+ mp_to_fh(mp, &nfh);
+ /*
+ * Patch up PID to match main server...
+ */
+ if (!foreground) {
+ long pid = getppid();
+ ((struct am_fh *) &nfh)->fhh_pid = pid;
+#ifdef DEBUG
+ dlog("root_fh substitutes pid %d", pid);
+#endif
+ }
+ return &nfh;
+ }
+
+ /*
+ * Should never get here...
+ */
+ plog(XLOG_ERROR, "Can't find root filehandle for %s", dir);
+ return 0;
+}
+
+am_node *root_ap(dir, path)
+char *dir;
+int path;
+{
+ am_node *mp = find_ap(dir);
+ if (mp && mp->am_parent == root_node)
+ return mp;
+
+ return 0;
+}
+
+/*
+ * Timeout all nodes waiting on
+ * a given Fserver.
+ */
+void map_flush_srvr P((fserver *fs));
+void map_flush_srvr(fs)
+fserver *fs;
+{
+ int i;
+ int done = 0;
+
+ for (i = last_used_map; i >= 0; --i) {
+ am_node *mp = exported_ap[i];
+ if (mp && mp->am_mnt && mp->am_mnt->mf_server == fs) {
+ plog(XLOG_INFO, "Flushed %s; dependent on %s", mp->am_path, fs->fs_host);
+ mp->am_ttl = clocktime();
+ done = 1;
+ }
+ }
+ if (done)
+ reschedule_timeout_mp();
+}
+
+/*
+ * Mount a top level automount node
+ * by calling lookup in the parent
+ * (root) node which will cause the
+ * automount node to be automounted.
+ */
+int mount_auto_node P((char *dir, voidp arg));
+int mount_auto_node(dir, arg)
+char *dir;
+voidp arg;
+{
+ int error = 0;
+ (void) afs_ops.lookuppn((am_node *) arg, dir, &error, VLOOK_CREATE);
+ if (error > 0) {
+ errno = error; /* XXX */
+ plog(XLOG_ERROR, "Could not mount %s: %m", dir);
+ }
+ return error;
+}
+
+/*
+ * Cause all the top-level mount nodes
+ * to be automounted
+ */
+int mount_exported P((void));
+int mount_exported()
+{
+ /*
+ * Iterate over all the nodes to be started
+ */
+ return root_keyiter((void (*)P((char*,void*))) mount_auto_node, root_node);
+}
+
+/*
+ * Construct top-level node
+ */
+void make_root_node P((void));
+void make_root_node()
+{
+ mntfs *root_mnt;
+ char *rootmap = ROOT_MAP;
+ root_node = exported_ap_alloc();
+
+ /*
+ * Allocate a new map
+ */
+ init_map(root_node, "");
+ /*
+ * Allocate a new mounted filesystem
+ */
+ root_mnt = find_mntfs(&root_ops, (am_opts *) 0, "", rootmap, "", "", "");
+ /*
+ * Replace the initial null reference
+ */
+ free_mntfs(root_node->am_mnt);
+ root_node->am_mnt = root_mnt;
+
+ /*
+ * Initialise the root
+ */
+ if (root_mnt->mf_ops->fs_init)
+ (*root_mnt->mf_ops->fs_init)(root_mnt);
+
+ /*
+ * Mount the root
+ */
+ root_mnt->mf_error = (*root_mnt->mf_ops->mount_fs)(root_node);
+}
+
+/*
+ * Cause all the nodes to be unmounted by timing
+ * them out.
+ */
+void umount_exported(P_void)
+{
+ int i;
+ for (i = last_used_map; i >= 0; --i) {
+ am_node *mp = exported_ap[i];
+ if (mp) {
+ mntfs *mf = mp->am_mnt;
+ if (mf->mf_flags & MFF_UNMOUNTING) {
+ /*
+ * If this node is being unmounted then
+ * just ignore it. However, this could
+ * prevent amd from finishing if the
+ * unmount gets blocked since the am_node
+ * will never be free'd. am_unmounted needs
+ * telling about this possibility. - XXX
+ */
+ continue;
+ }
+ if (mf && !(mf->mf_ops->fs_flags & FS_DIRECTORY)) {
+ /*
+ * When shutting down this had better
+ * look like a directory, otherwise it
+ * can't be unmounted!
+ */
+ mk_fattr(mp, NFDIR);
+ }
+ if ((--immediate_abort < 0 && !(mp->am_flags & AMF_ROOT) && mp->am_parent) ||
+ (mf->mf_flags & MFF_RESTART)) {
+ /*
+ * Just throw this node away without
+ * bothering to unmount it. If the
+ * server is not known to be up then
+ * don't discard the mounted on directory
+ * or Amd might hang...
+ */
+ if (mf->mf_server &&
+ (mf->mf_server->fs_flags & (FSF_DOWN|FSF_VALID)) != FSF_VALID)
+ mf->mf_flags &= ~MFF_MKMNT;
+ am_unmounted(mp);
+ } else {
+ /*
+ * Any other node gets forcibly
+ * timed out
+ */
+ mp->am_flags &= ~AMF_NOTIMEOUT;
+ mp->am_mnt->mf_flags &= ~MFF_RSTKEEP;
+ mp->am_ttl = 0;
+ mp->am_timeo = 1;
+ mp->am_timeo_w = 0;
+ }
+ }
+ }
+}
+
+static int unmount_node P((am_node *mp));
+static int unmount_node(mp)
+am_node *mp;
+{
+ mntfs *mf = mp->am_mnt;
+ int error;
+
+ if ((mf->mf_flags & MFF_ERROR) || mf->mf_refc > 1) {
+ /*
+ * Just unlink
+ */
+#ifdef DEBUG
+ if (mf->mf_flags & MFF_ERROR)
+ dlog("No-op unmount of error node %s", mf->mf_info);
+#endif /* DEBUG */
+ error = 0;
+ } else {
+#ifdef DEBUG
+ dlog("Unmounting %s (%s)", mf->mf_mount, mf->mf_info);
+#endif /* DEBUG */
+ error = (*mf->mf_ops->umount_fs)(mp);
+ }
+
+ if (error) {
+#ifdef DEBUG
+ errno = error; /* XXX */
+ dlog("%s: unmount: %m", mf->mf_mount);
+#endif /* DEBUG */
+ }
+
+ return error;
+}
+
+#ifdef FLUSH_KERNEL_NAME_CACHE
+static void flush_kernel_name_cache P((am_node*));
+static void flush_kernel_name_cache(mp)
+am_node *mp;
+{
+ int islink = (mp->am_mnt->mf_fattr.type == NFLNK);
+ int isdir = (mp->am_mnt->mf_fattr.type == NFDIR);
+ int elog = 0;
+ if (islink) {
+ if (unlink(mp->am_path) < 0)
+ elog = 1;
+ } else if (isdir) {
+ if (rmdir(mp->am_path) < 0)
+ elog = 1;
+ }
+ if (elog)
+ plog(XLOG_WARNING, "failed to clear \"%s\" from dnlc: %m", mp->am_path);
+}
+#endif /* FLUSH_KERNEL_NAME_CACHE */
+
+static int unmount_node_wrap P((voidp vp));
+static int unmount_node_wrap(vp)
+voidp vp;
+{
+#ifndef FLUSH_KERNEL_NAME_CACHE
+ return unmount_node((am_node*) vp);
+#else /* FLUSH_KERNEL_NAME_CACHE */
+ /*
+ * This code should just say:
+ * return unmount_node((am_node *) vp);
+ *
+ * However...
+ * The kernel keeps a cached copy of filehandles,
+ * and doesn't ever uncache them (apparently). So
+ * when Amd times out a node the kernel will have a
+ * stale filehandle. When the kernel next uses the
+ * filehandle it gets ESTALE.
+ *
+ * The workaround:
+ * Arrange that when a node is removed an unlink or
+ * rmdir is done on that path so that the kernel
+ * cache is done. Yes - yuck.
+ *
+ * This can all be removed (and the background
+ * unmount flag in sfs_ops) if/when the kernel does
+ * something smarter.
+ *
+ * If the unlink or rmdir failed then just log a warning,
+ * don't fail the unmount. This can occur if the kernel
+ * client code decides that the object is still referenced
+ * and should be renamed rather than discarded.
+ *
+ * There is still a race condition here...
+ * if another process is trying to access the same
+ * filesystem at the time we get here, then
+ * it will block, since the MF_UNMOUNTING flag will
+ * be set. That may, or may not, cause the entire
+ * system to deadlock. Hmmm...
+ */
+ am_node *mp = (am_node *) vp;
+ int isauto = mp->am_parent && (mp->am_parent->am_mnt->mf_fattr.type == NFDIR);
+ int error = unmount_node(mp);
+ if (error)
+ return error;
+ if (isauto && (int)amd_state < (int)Finishing)
+ flush_kernel_name_cache(mp);
+
+ return 0;
+#endif /* FLUSH_KERNEL_NAME_CACHE */
+}
+
+static void free_map_if_success(rc, term, closure)
+int rc;
+int term;
+voidp closure;
+{
+ am_node *mp = (am_node *) closure;
+ mntfs *mf = mp->am_mnt;
+
+ /*
+ * Not unmounting any more
+ */
+ mf->mf_flags &= ~MFF_UNMOUNTING;
+
+ /*
+ * If a timeout was defered because the underlying filesystem
+ * was busy then arrange for a timeout as soon as possible.
+ */
+ if (mf->mf_flags & MFF_WANTTIMO) {
+ mf->mf_flags &= ~MFF_WANTTIMO;
+ reschedule_timeout_mp();
+ }
+
+ if (term) {
+ plog(XLOG_ERROR, "unmount for %s got signal %d", mp->am_path, term);
+#if defined(DEBUG) && defined(SIGTRAP)
+ /*
+ * dbx likes to put a trap on exit().
+ * Pretend it succeeded for now...
+ */
+ if (term == SIGTRAP) {
+ am_unmounted(mp);
+ }
+#endif /* DEBUG */
+ amd_stats.d_uerr++;
+ } else if (rc) {
+ if (rc == EBUSY) {
+ plog(XLOG_STATS, "\"%s\" on %s still active", mp->am_path, mf->mf_mount);
+ } else {
+ errno = rc; /* XXX */
+ plog(XLOG_ERROR, "%s: unmount: %m", mp->am_path);
+ }
+ amd_stats.d_uerr++;
+ } else {
+ am_unmounted(mp);
+ }
+
+ /*
+ * Wakeup anything waiting for this mount
+ */
+ wakeup((voidp) mf);
+}
+
+static int unmount_mp(mp)
+am_node *mp;
+{
+ int was_backgrounded = 0;
+ mntfs *mf = mp->am_mnt;
+
+#ifdef notdef
+ plog(XLOG_INFO, "\"%s\" on %s timed out", mp->am_path, mp->am_mnt->mf_mount);
+#endif /* notdef */
+
+ if ((mf->mf_ops->fs_flags & FS_UBACKGROUND) &&
+ (mf->mf_flags & MFF_MOUNTED)) {
+ if (mf->mf_refc == 1 && !FSRV_ISUP(mf->mf_server)) {
+ /*
+ * Don't try to unmount from a server that is known to be down
+ */
+ if (!(mf->mf_flags & MFF_LOGDOWN)) {
+ /* Only log this once, otherwise gets a bit boring */
+ plog(XLOG_STATS, "file server %s is down - timeout of \"%s\" ignored", mf->mf_server->fs_host, mp->am_path);
+ mf->mf_flags |= MFF_LOGDOWN;
+ }
+ } else {
+ /* Clear logdown flag - since the server must be up */
+ mf->mf_flags &= ~MFF_LOGDOWN;
+#ifdef DEBUG
+ dlog("\"%s\" on %s timed out", mp->am_path, mp->am_mnt->mf_mount);
+ /*dlog("Will background the unmount attempt");*/
+#endif /* DEBUG */
+ /*
+ * Note that we are unmounting this node
+ */
+ mf->mf_flags |= MFF_UNMOUNTING;
+ run_task(unmount_node_wrap, (voidp) mp,
+ free_map_if_success, (voidp) mp);
+ was_backgrounded = 1;
+#ifdef DEBUG
+ dlog("unmount attempt backgrounded");
+#endif /* DEBUG */
+ }
+ } else {
+#ifdef DEBUG
+ dlog("\"%s\" on %s timed out", mp->am_path, mp->am_mnt->mf_mount);
+ dlog("Trying unmount in foreground");
+#endif
+ mf->mf_flags |= MFF_UNMOUNTING;
+ free_map_if_success(unmount_node(mp), 0, (voidp) mp);
+#ifdef DEBUG
+ dlog("unmount attempt done");
+#endif /* DEBUG */
+ }
+
+ return was_backgrounded;
+}
+
+void timeout_mp()
+{
+#define NEVER (time_t) 0
+#define smallest_t(t1, t2) \
+ (t1 != NEVER ? (t2 != NEVER ? (t1 < t2 ? t1 : t2) : t1) : t2)
+#define IGNORE_FLAGS (MFF_MOUNTING|MFF_UNMOUNTING|MFF_RESTART)
+
+ int i;
+ time_t t = NEVER;
+ time_t now = clocktime();
+ int backoff = 0;
+
+#ifdef DEBUG
+ dlog("Timing out automount points...");
+#endif /* DEBUG */
+ for (i = last_used_map; i >= 0; --i) {
+ am_node *mp = exported_ap[i];
+ mntfs *mf;
+ /*
+ * Just continue if nothing mounted, or can't be timed out.
+ */
+ if (!mp || (mp->am_flags & AMF_NOTIMEOUT))
+ continue;
+ /*
+ * Pick up mounted filesystem
+ */
+ mf = mp->am_mnt;
+ if (!mf)
+ continue;
+ /*
+ * Don't delete last reference to a restarted filesystem.
+ */
+ if ((mf->mf_flags & MFF_RSTKEEP) && mf->mf_refc == 1)
+ continue;
+ /*
+ * If there is action on this filesystem then ignore it
+ */
+ if (!(mf->mf_flags & IGNORE_FLAGS)) {
+ int expired = 0;
+ mf->mf_flags &= ~MFF_WANTTIMO;
+#ifdef DEBUG
+ /*dlog("t is initially @%d, zero in %d secs", t, t - now);*/
+#endif /* DEBUG */
+ if (now >= mp->am_ttl) {
+ if (!backoff) {
+ expired = 1;
+ /*
+ * Move the ttl forward to avoid thrashing effects
+ * on the next call to timeout!
+ */
+ /* sun's -tw option */
+ if (mp->am_timeo_w < 4 * am_timeo_w)
+ mp->am_timeo_w += am_timeo_w;
+ mp->am_ttl = now + mp->am_timeo_w;
+ } else {
+ /*
+ * Just backoff this unmount for
+ * a couple of seconds to avoid
+ * many multiple unmounts being
+ * started in parallel.
+ */
+ mp->am_ttl = now + backoff + 1;
+ }
+ }
+ /*
+ * If the next ttl is smallest, use that
+ */
+ t = smallest_t(t, mp->am_ttl);
+
+#ifdef DEBUG
+ /*dlog("after ttl t is @%d, zero in %d secs", t, t - now);*/
+#endif /* DEBUG */
+
+ if (!mp->am_child && mf->mf_error >= 0 && expired) {
+ /*
+ * If the unmount was backgrounded then
+ * bump the backoff counter.
+ */
+ if (unmount_mp(mp)) {
+ backoff = 2;
+#ifdef DEBUG
+ /*dlog("backing off subsequent unmounts by at least %d seconds", backoff);*/
+#endif
+ }
+ }
+ } else if (mf->mf_flags & MFF_UNMOUNTING) {
+ mf->mf_flags |= MFF_WANTTIMO;
+ }
+ }
+
+ if (t == NEVER) {
+#ifdef DEBUG
+ dlog("No further timeouts");
+#endif /* DEBUG */
+ t = now + ONE_HOUR;
+ }
+
+ /*
+ * Sanity check to avoid runaways.
+ * Absolutely should never get this but
+ * if you do without this trap amd will thrash.
+ */
+ if (t <= now) {
+ t = now + 6; /* XXX */
+ plog(XLOG_ERROR, "Got a zero interval in timeout_mp()!");
+ }
+ /*
+ * XXX - when shutting down, make things happen faster
+ */
+ if ((int)amd_state >= (int)Finishing)
+ t = now + 1;
+#ifdef DEBUG
+ dlog("Next mount timeout in %ds", t - now);
+#endif /* DEBUG */
+
+ timeout_mp_id = timeout(t - now, timeout_mp, 0);
+
+#undef NEVER
+#undef smallest_t
+#undef IGNORE_FLAGS
+}
+
+/*
+ * Cause timeout_mp to be called soonest
+ */
+void reschedule_timeout_mp()
+{
+ if (timeout_mp_id)
+ untimeout(timeout_mp_id);
+ timeout_mp_id = timeout(0, timeout_mp, 0);
+}
diff --git a/usr.sbin/amd/amd/mapc.c b/usr.sbin/amd/amd/mapc.c
new file mode 100644
index 0000000..cd02f66
--- /dev/null
+++ b/usr.sbin/amd/amd/mapc.c
@@ -0,0 +1,914 @@
+/*-
+ * Copyright (c) 1989 Jan-Simon Pendry
+ * Copyright (c) 1989 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id$
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)mapc.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+/*
+ * Mount map cache
+ */
+
+#include "am.h"
+#ifdef HAS_REGEXP
+#include RE_HDR
+#endif
+
+/*
+ * Hash table size
+ */
+#define NKVHASH (1 << 2) /* Power of two */
+
+/*
+ * Wildcard key
+ */
+static char wildcard[] = "*";
+
+/*
+ * Map cache types
+ * default, none, incremental, all, regexp
+ * MAPC_RE implies MAPC_ALL and must be numerically
+ * greater.
+ */
+#define MAPC_DFLT 0x000
+#define MAPC_NONE 0x001
+#define MAPC_INC 0x002
+#define MAPC_ROOT 0x004
+#define MAPC_ALL 0x010
+#ifdef HAS_REGEXP
+#define MAPC_RE 0x020
+#define MAPC_ISRE(m) ((m)->alloc == MAPC_RE)
+#else
+#define MAPC_ISRE(m) FALSE
+#endif
+#define MAPC_CACHE_MASK 0x0ff
+#define MAPC_SYNC 0x100
+
+static struct opt_tab mapc_opt[] = {
+ { "all", MAPC_ALL },
+ { "default", MAPC_DFLT },
+ { "inc", MAPC_INC },
+ { "mapdefault", MAPC_DFLT },
+ { "none", MAPC_NONE },
+#ifdef HAS_REGEXP
+ { "re", MAPC_RE },
+ { "regexp", MAPC_RE },
+#endif
+ { "sync", MAPC_SYNC },
+ { 0, 0 }
+};
+
+/*
+ * Lookup recursion
+ */
+#define MREC_FULL 2
+#define MREC_PART 1
+#define MREC_NONE 0
+
+/*
+ * Cache map operations
+ */
+typedef void add_fn P((mnt_map*, char*, char*));
+typedef int init_fn P((char*, time_t*));
+typedef int search_fn P((mnt_map*, char*, char*, char**, time_t*));
+typedef int reload_fn P((mnt_map*, char*, add_fn*));
+typedef int mtime_fn P((char*, time_t*));
+
+static void mapc_sync P((mnt_map*));
+
+/*
+ * Map type
+ */
+typedef struct map_type map_type;
+struct map_type {
+ char *name; /* Name of this map type */
+ init_fn *init; /* Initialisation */
+ reload_fn *reload; /* Reload or fill */
+ search_fn *search; /* Search for new entry */
+ mtime_fn *mtime; /* Find modify time */
+ int def_alloc; /* Default allocation mode */
+};
+
+/*
+ * Key-value pair
+ */
+typedef struct kv kv;
+struct kv {
+ kv *next;
+ char *key;
+ char *val;
+};
+
+struct mnt_map {
+ qelem hdr;
+ int refc; /* Reference count */
+ short flags; /* Allocation flags */
+ short alloc; /* Allocation mode */
+ time_t modify; /* Modify time of map */
+ char *map_name; /* Name of this map */
+ char *wildcard; /* Wildcard value */
+ reload_fn *reload; /* Function to be used for reloads */
+ search_fn *search; /* Function to be used for searching */
+ mtime_fn *mtime; /* Modify time function */
+ kv *kvhash[NKVHASH]; /* Cached data */
+};
+
+/*
+ * Map for root node
+ */
+static mnt_map *root_map;
+
+/*
+ * List of known maps
+ */
+extern qelem map_list_head;
+qelem map_list_head = { &map_list_head, &map_list_head };
+
+/*
+ * Configuration
+ */
+
+/* ROOT MAP */
+static int root_init P((char*, time_t*));
+
+/* FILE MAPS */
+#ifdef HAS_FILE_MAPS
+extern int file_init P((char*, time_t*));
+extern int file_reload P((mnt_map*, char*, add_fn*));
+extern int file_search P((mnt_map*, char*, char*, char**, time_t*));
+extern int file_mtime P((char*, time_t*));
+#endif /* HAS_FILE_MAPS */
+
+/* Network Information Service (NIS) MAPS */
+#ifdef HAS_NIS_MAPS
+extern int nis_init P((char*, time_t*));
+#ifdef HAS_NIS_RELOAD
+extern int nis_reload P((mnt_map*, char*, add_fn*));
+#else
+#define nis_reload error_reload
+#endif
+extern int nis_search P((mnt_map*, char*, char*, char**, time_t*));
+#define nis_mtime nis_init
+#endif /* HAS_NIS_MAPS */
+
+/* NDBM MAPS */
+#ifdef HAS_NDBM_MAPS
+#ifdef OS_HAS_NDBM
+extern int ndbm_init P((char*, time_t*));
+extern int ndbm_search P((mnt_map*, char*, char*, char**, time_t*));
+#define ndbm_mtime ndbm_init
+#endif /* OS_HAS_NDBM */
+#endif /* HAS_NDBM_MAPS */
+
+/* PASSWD MAPS */
+#ifdef HAS_PASSWD_MAPS
+extern int passwd_init P((char*, time_t*));
+extern int passwd_search P((mnt_map*, char*, char*, char**, time_t*));
+#endif /* HAS_PASSWD_MAPS */
+
+/* HESIOD MAPS */
+#ifdef HAS_HESIOD_MAPS
+extern int hesiod_init P((char*, time_t*));
+#ifdef HAS_HESIOD_RELOAD
+extern int hesiod_reload P((mnt_map*, char*, add_fn*));
+#else
+#define hesiod_reload error_reload
+#endif
+extern int hesiod_search P((mnt_map*, char*, char*, char**, time_t*));
+#endif /* HAS_HESIOD_MAPS */
+
+/* UNION MAPS */
+#ifdef HAS_UNION_MAPS
+extern int union_init P((char*, time_t*));
+extern int union_search P((mnt_map*, char*, char*, char**, time_t*));
+extern int union_reload P((mnt_map*, char*, add_fn*));
+#endif /* HAS_UNION_MAPS */
+
+/* ERROR MAP */
+static int error_init P((char*, time_t*));
+static int error_reload P((mnt_map*, char*, add_fn*));
+static int error_search P((mnt_map*, char*, char*, char**, time_t*));
+static int error_mtime P((char*, time_t*));
+
+static map_type maptypes[] = {
+ { "root", root_init, error_reload, error_search, error_mtime, MAPC_ROOT },
+
+#ifdef HAS_PASSWD_MAPS
+ { "passwd", passwd_init, error_reload, passwd_search, error_mtime, MAPC_INC },
+#endif
+
+#ifdef HAS_HESIOD_MAPS
+ { "hesiod", hesiod_init, hesiod_reload, hesiod_search, error_mtime, MAPC_ALL },
+#endif
+
+#ifdef HAS_UNION_MAPS
+ { "union", union_init, union_reload, union_search, error_mtime, MAPC_ALL },
+#endif
+
+#ifdef HAS_NIS_MAPS
+ { "nis", nis_init, nis_reload, nis_search, nis_mtime, MAPC_INC },
+#endif
+
+#ifdef HAS_NDBM_MAPS
+ { "ndbm", ndbm_init, error_reload, ndbm_search, ndbm_mtime, MAPC_INC },
+#endif
+
+#ifdef HAS_FILE_MAPS
+ { "file", file_init, file_reload, file_search, file_mtime, MAPC_ALL },
+#endif
+
+ { "error", error_init, error_reload, error_search, error_mtime, MAPC_NONE },
+};
+
+/*
+ * Hash function
+ */
+static unsigned int kvhash_of P((char *key));
+static unsigned int kvhash_of(key)
+char *key;
+{
+ unsigned int i, j;
+
+ for (i = 0; j = *key++; i += j)
+ ;
+
+ return i % NKVHASH;
+}
+
+void mapc_showtypes P((FILE *fp));
+void mapc_showtypes(fp)
+FILE *fp;
+{
+ map_type *mt;
+ char *sep = "";
+ for (mt = maptypes; mt < maptypes+sizeof(maptypes)/sizeof(maptypes[0]); mt++) {
+ fprintf(fp, "%s%s", sep, mt->name);
+ sep = ", ";
+ }
+}
+
+static Const char *reg_error = "?";
+void regerror P((Const char *m));
+void regerror(m)
+Const char *m;
+{
+ reg_error = m;
+}
+
+/*
+ * Add key and val to the map m.
+ * key and val are assumed to be safe copies
+ */
+void mapc_add_kv P((mnt_map *m, char *key, char *val));
+void mapc_add_kv(m, key, val)
+mnt_map *m;
+char *key;
+char *val;
+{
+ kv **h;
+ kv *n;
+ int hash = kvhash_of(key);
+
+#ifdef DEBUG
+ dlog("add_kv: %s -> %s", key, val);
+#endif
+
+#ifdef HAS_REGEXP
+ if (MAPC_ISRE(m)) {
+ char keyb[MAXPATHLEN];
+ regexp *re;
+ /*
+ * Make sure the string is bound to the start and end
+ */
+ sprintf(keyb, "^%s$", key);
+ re = regcomp(keyb);
+ if (re == 0) {
+ plog(XLOG_USER, "error compiling RE \"%s\": %s", keyb, reg_error);
+ return;
+ } else {
+ free(key);
+ key = (char *) re;
+ }
+ }
+#endif
+
+ h = &m->kvhash[hash];
+ n = ALLOC(kv);
+ n->key = key;
+ n->val = val;
+ n->next = *h;
+ *h = n;
+}
+
+void mapc_repl_kv P((mnt_map *m, char *key, char *val));
+void mapc_repl_kv(m, key, val)
+mnt_map *m;
+char *key;
+char *val;
+{
+ kv *k;
+
+ /*
+ * Compute the hash table offset
+ */
+ k = m->kvhash[kvhash_of(key)];
+
+ /*
+ * Scan the linked list for the key
+ */
+ while (k && !FSTREQ(k->key, key))
+ k = k->next;
+
+ if (k) {
+ free(k->val);
+ k->val = val;
+ } else {
+ mapc_add_kv(m, key, val);
+ }
+
+}
+
+/*
+ * Search a map for a key.
+ * Calls map specific search routine.
+ * While map is out of date, keep re-syncing.
+ */
+static int search_map P((mnt_map *m, char *key, char **valp));
+static int search_map(m, key, valp)
+mnt_map *m;
+char *key;
+char **valp;
+{
+ int rc;
+ do {
+ rc = (*m->search)(m, m->map_name, key, valp, &m->modify);
+ if (rc < 0) {
+ plog(XLOG_MAP, "Re-synchronizing cache for map %s", m->map_name);
+ mapc_sync(m);
+ }
+ } while (rc < 0);
+
+ return rc;
+}
+
+/*
+ * Do a wildcard lookup in the map and
+ * save the result.
+ */
+static void mapc_find_wildcard P((mnt_map *m));
+static void mapc_find_wildcard(m)
+mnt_map *m;
+{
+ /*
+ * Attempt to find the wildcard entry
+ */
+ int rc = search_map(m, wildcard, &m->wildcard);
+
+ if (rc != 0)
+ m->wildcard = 0;
+}
+
+/*
+ * Make a duplicate reference to an existing map
+ */
+#define mapc_dup(m) ((m)->refc++, (m))
+
+/*
+ * Do a map reload
+ */
+static int mapc_reload_map(m)
+mnt_map *m;
+{
+ int error;
+#ifdef DEBUG
+ dlog("calling map reload on %s", m->map_name);
+#endif
+ error = (*m->reload)(m, m->map_name, mapc_add_kv);
+ if (error)
+ return error;
+ m->wildcard = 0;
+#ifdef DEBUG
+ dlog("calling mapc_search for wildcard");
+#endif
+ error = mapc_search(m, wildcard, &m->wildcard);
+ if (error)
+ m->wildcard = 0;
+ return 0;
+}
+
+/*
+ * Create a new map
+ */
+static mnt_map *mapc_create P((char *map, char *opt));
+static mnt_map *mapc_create(map, opt)
+char *map;
+char *opt;
+{
+ mnt_map *m = ALLOC(mnt_map);
+ map_type *mt;
+ time_t modify;
+ int alloc = 0;
+
+ (void) cmdoption(opt, mapc_opt, &alloc);
+
+ for (mt = maptypes; mt < maptypes+sizeof(maptypes)/sizeof(maptypes[0]); mt++)
+ if ((*mt->init)(map, &modify) == 0)
+ break;
+ /* assert: mt in maptypes */
+
+ m->flags = alloc & ~MAPC_CACHE_MASK;
+ alloc &= MAPC_CACHE_MASK;
+
+ if (alloc == MAPC_DFLT)
+ alloc = mt->def_alloc;
+ switch (alloc) {
+ default:
+ plog(XLOG_USER, "Ambiguous map cache type \"%s\"; using \"inc\"", opt);
+ alloc = MAPC_INC;
+ /* fallthrough... */
+ case MAPC_NONE:
+ case MAPC_INC:
+ case MAPC_ROOT:
+ break;
+ case MAPC_ALL:
+ /*
+ * If there is no support for reload and it was requested
+ * then back off to incremental instead.
+ */
+ if (mt->reload == error_reload) {
+ plog(XLOG_WARNING, "Map type \"%s\" does not support cache type \"all\"; using \"inc\"", mt->name);
+ alloc = MAPC_INC;
+ }
+ break;
+#ifdef HAS_REGEXP
+ case MAPC_RE:
+ if (mt->reload == error_reload) {
+ plog(XLOG_WARNING, "Map type \"%s\" does not support cache type \"re\"", mt->name);
+ mt = &maptypes[sizeof(maptypes)/sizeof(maptypes[0]) - 1];
+ /* assert: mt->name == "error" */
+ }
+ break;
+#endif
+ }
+
+#ifdef DEBUG
+ dlog("Map for %s coming from maptype %s", map, mt->name);
+#endif
+
+ m->alloc = alloc;
+ m->reload = mt->reload;
+ m->modify = modify;
+ m->search = alloc >= MAPC_ALL ? error_search : mt->search;
+ m->mtime = mt->mtime;
+ bzero((voidp) m->kvhash, sizeof(m->kvhash));
+ m->map_name = strdup(map);
+ m->refc = 1;
+ m->wildcard = 0;
+
+ /*
+ * synchronize cache with reality
+ */
+ mapc_sync(m);
+
+ return m;
+}
+
+/*
+ * Free the cached data in a map
+ */
+static void mapc_clear P((mnt_map *m));
+static void mapc_clear(m)
+mnt_map *m;
+{
+ int i;
+
+ /*
+ * For each of the hash slots, chain
+ * along free'ing the data.
+ */
+ for (i = 0; i < NKVHASH; i++) {
+ kv *k = m->kvhash[i];
+ while (k) {
+ kv *n = k->next;
+ free((voidp) k->key);
+ if (k->val)
+ free((voidp) k->val);
+ free((voidp) k);
+ k = n;
+ }
+ }
+ /*
+ * Zero the hash slots
+ */
+ bzero((voidp) m->kvhash, sizeof(m->kvhash));
+ /*
+ * Free the wildcard if it exists
+ */
+ if (m->wildcard) {
+ free(m->wildcard);
+ m->wildcard = 0;
+ }
+}
+
+/*
+ * Find a map, or create one if it does not exist
+ */
+mnt_map *mapc_find P((char *map, char *opt));
+mnt_map *mapc_find(map, opt)
+char *map;
+char *opt;
+{
+ mnt_map *m;
+
+ /*
+ * Search the list of known maps to see if
+ * it has already been loaded. If it is found
+ * then return a duplicate reference to it.
+ * Otherwise make a new map as required and
+ * add it to the list of maps
+ */
+ ITER(m, mnt_map, &map_list_head)
+ if (STREQ(m->map_name, map))
+ return mapc_dup(m);
+
+ m = mapc_create(map, opt);
+ ins_que(&m->hdr, &map_list_head);
+ return m;
+}
+
+/*
+ * Free a map.
+ */
+void mapc_free P((mnt_map *m));
+void mapc_free(m)
+mnt_map *m;
+{
+ /*
+ * Decrement the reference count.
+ * If the reference count hits zero
+ * then throw the map away.
+ */
+ if (m && --m->refc == 0) {
+ mapc_clear(m);
+ free((voidp) m->map_name);
+ rem_que(&m->hdr);
+ free((voidp) m);
+ }
+}
+
+/*
+ * Search the map for the key.
+ * Put a safe copy in *pval or return
+ * an error code
+ */
+int mapc_meta_search P((mnt_map *m, char *key, char **pval, int recurse));
+int mapc_meta_search(m, key, pval, recurse)
+mnt_map *m;
+char *key;
+char **pval;
+int recurse;
+{
+ int error = 0;
+ kv *k = 0;
+
+ /*
+ * Firewall
+ */
+ if (!m) {
+ plog(XLOG_ERROR, "Null map request for %s", key);
+ return ENOENT;
+ }
+
+ if (m->flags & MAPC_SYNC) {
+ /*
+ * Get modify time...
+ */
+ time_t t;
+ error = (*m->mtime)(m->map_name, &t);
+ if (error || t > m->modify) {
+ m->modify = t;
+ plog(XLOG_INFO, "Map %s is out of date", m->map_name);
+ mapc_sync(m);
+ }
+ }
+
+ if (!MAPC_ISRE(m)) {
+ /*
+ * Compute the hash table offset
+ */
+ k = m->kvhash[kvhash_of(key)];
+
+ /*
+ * Scan the linked list for the key
+ */
+ while (k && !FSTREQ(k->key, key)) k = k->next;
+
+ }
+#ifdef HAS_REGEXP
+ else if (recurse == MREC_FULL) {
+ /*
+ * Try for an RE match against the entire map.
+ * Note that this will be done in a "random"
+ * order.
+ */
+
+ int i;
+
+ for (i = 0; i < NKVHASH; i++) {
+ k = m->kvhash[i];
+ while (k) {
+ if (regexec((regexp *) k->key, key))
+ break;
+ k = k->next;
+ }
+ if (k)
+ break;
+ }
+ }
+#endif
+
+ /*
+ * If found then take a copy
+ */
+ if (k) {
+ if (k->val)
+ *pval = strdup(k->val);
+ else
+ error = ENOENT;
+ } else if (m->alloc >= MAPC_ALL) {
+ /*
+ * If the entire map is cached then this
+ * key does not exist.
+ */
+ error = ENOENT;
+ } else {
+ /*
+ * Otherwise search the map. If we are
+ * in incremental mode then add the key
+ * to the cache.
+ */
+ error = search_map(m, key, pval);
+ if (!error && m->alloc == MAPC_INC)
+ mapc_add_kv(m, strdup(key), strdup(*pval));
+ }
+
+ /*
+ * If an error, and a wildcard exists,
+ * and the key is not internal then
+ * return a copy of the wildcard.
+ */
+ if (error > 0) {
+ if (recurse == MREC_FULL && !MAPC_ISRE(m)) {
+ char wildname[MAXPATHLEN];
+ char *subp;
+ if (*key == '/')
+ return error;
+ /*
+ * Keep chopping sub-directories from the RHS
+ * and replacing with "/ *" and repeat the lookup.
+ * For example:
+ * "src/gnu/gcc" -> "src / gnu / *" -> "src / *"
+ */
+ strcpy(wildname, key);
+ while (error && (subp = strrchr(wildname, '/'))) {
+ strcpy(subp, "/*");
+#ifdef DEBUG
+ dlog("mapc recurses on %s", wildname);
+#endif
+ error = mapc_meta_search(m, wildname, pval, MREC_PART);
+ if (error)
+ *subp = 0;
+ }
+ if (error > 0 && m->wildcard) {
+ *pval = strdup(m->wildcard);
+ error = 0;
+ }
+ }
+ }
+
+ return error;
+}
+
+int mapc_search P((mnt_map *m, char *key, char **pval));
+int mapc_search(m, key, pval)
+mnt_map *m;
+char *key;
+char **pval;
+{
+ return mapc_meta_search(m, key, pval, MREC_FULL);
+}
+
+/*
+ * Get map cache in sync with physical representation
+ */
+static void mapc_sync P((mnt_map *m));
+static void mapc_sync(m)
+mnt_map *m;
+{
+ if (m->alloc != MAPC_ROOT) {
+ mapc_clear(m);
+
+ if (m->alloc >= MAPC_ALL)
+ if (mapc_reload_map(m))
+ m->alloc = MAPC_INC;
+ /*
+ * Attempt to find the wildcard entry
+ */
+ if (m->alloc < MAPC_ALL)
+ mapc_find_wildcard(m);
+ }
+}
+
+/*
+ * Reload all the maps
+ * Called when Amd gets hit by a SIGHUP.
+ */
+void mapc_reload(P_void);
+void mapc_reload()
+{
+ mnt_map *m;
+
+ /*
+ * For all the maps,
+ * Throw away the existing information.
+ * Do a reload
+ * Find the wildcard
+ */
+ ITER(m, mnt_map, &map_list_head)
+ mapc_sync(m);
+}
+
+/*
+ * Root map.
+ * The root map is used to bootstrap amd.
+ * All the require top-level mounts are added
+ * into the root map and then the map is iterated
+ * and a lookup is done on all the mount points.
+ * This causes the top level mounts to be automounted.
+ */
+
+static int root_init P((char *map, time_t *tp));
+static int root_init(map, tp)
+char *map;
+time_t *tp;
+{
+ *tp = clocktime();
+ return strcmp(map, ROOT_MAP) == 0 ? 0 : ENOENT;
+}
+
+/*
+ * Add a new entry to the root map
+ *
+ * dir - directory (key)
+ * opts - mount options
+ * map - map name
+ */
+void root_newmap P((char *dir, char *opts, char *map));
+void root_newmap(dir, opts, map)
+char *dir;
+char *opts;
+char *map;
+{
+ char str[MAXPATHLEN];
+
+ /*
+ * First make sure we have a root map to talk about...
+ */
+ if (!root_map)
+ root_map = mapc_find(ROOT_MAP, "mapdefault");
+
+ /*
+ * Then add the entry...
+ */
+ dir = strdup(dir);
+ if (map)
+ sprintf(str, "cache:=mapdefault;type:=toplvl;fs:=\"%s\";%s",
+ map, opts ? opts : "");
+ else
+ strcpy(str, opts);
+ mapc_repl_kv(root_map, dir, strdup(str));
+}
+
+int mapc_keyiter P((mnt_map *m, void (*fn)(char*,voidp), voidp arg));
+int mapc_keyiter(m, fn, arg)
+mnt_map *m;
+void (*fn)P((char*, voidp));
+voidp arg;
+{
+ int i;
+ int c = 0;
+
+ for (i = 0; i < NKVHASH; i++) {
+ kv *k = m->kvhash[i];
+ while (k) {
+ (*fn)(k->key, arg);
+ k = k->next;
+ c++;
+ }
+ }
+
+ return c;
+}
+
+/*
+ * Iterate of the the root map
+ * and call (*fn)() on the key
+ * of all the nodes.
+ * Finally throw away the root map.
+ */
+int root_keyiter P((void (*fn)(char*,voidp), voidp arg));
+int root_keyiter(fn, arg)
+void (*fn)P((char*,voidp));
+voidp arg;
+{
+ if (root_map) {
+ int c = mapc_keyiter(root_map, fn, arg);
+#ifdef notdef
+ mapc_free(root_map);
+ root_map = 0;
+#endif
+ return c;
+ }
+ return 0;
+}
+
+/*
+ * Error map
+ */
+static int error_init P((char *map, time_t *tp));
+static int error_init(map, tp)
+char *map;
+time_t *tp;
+{
+ plog(XLOG_USER, "No source data for map %s", map);
+ *tp = 0;
+ return 0;
+}
+
+/*ARGSUSED*/
+static int error_search P((mnt_map *m, char *map, char *key, char **pval, time_t *tp));
+static int error_search(m, map, key, pval, tp)
+mnt_map *m;
+char *map;
+char *key;
+char **pval;
+time_t *tp;
+{
+ return ENOENT;
+}
+
+/*ARGSUSED*/
+static int error_reload P((mnt_map *m, char *map, add_fn *fn));
+static int error_reload(m, map, fn)
+mnt_map *m;
+char *map;
+add_fn *fn;
+{
+ return ENOENT;
+}
+
+static int error_mtime P((char *map, time_t *tp));
+static int error_mtime(map, tp)
+char *map;
+time_t *tp;
+{
+ *tp = 0;
+ return 0;
+}
diff --git a/usr.sbin/amd/amd/misc_rpc.c b/usr.sbin/amd/amd/misc_rpc.c
new file mode 100644
index 0000000..b3f1e49
--- /dev/null
+++ b/usr.sbin/amd/amd/misc_rpc.c
@@ -0,0 +1,333 @@
+/*
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (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_rpc.c 8.1 (Berkeley) 6/6/93
+ *
+ * $Id$
+ *
+ */
+
+/*
+ * Additions to Sun RPC.
+ */
+
+#include "am.h"
+
+void rpc_msg_init P((struct rpc_msg *mp, u_long prog, u_long vers, u_long proc));
+void rpc_msg_init(mp, prog, vers, proc)
+struct rpc_msg *mp;
+unsigned long prog, vers, proc;
+{
+ /*
+ * Initialise the message
+ */
+ bzero((voidp) mp, sizeof(*mp));
+ mp->rm_xid = 0;
+ mp->rm_direction = CALL;
+ mp->rm_call.cb_rpcvers = RPC_MSG_VERSION;
+ mp->rm_call.cb_prog = prog;
+ mp->rm_call.cb_vers = vers;
+ mp->rm_call.cb_proc = proc;
+}
+
+/*
+ * Field reply to call to mountd
+ */
+int pickup_rpc_reply P((voidp pkt, int len, voidp where, xdrproc_t where_xdr));
+int pickup_rpc_reply(pkt, len, where, where_xdr)
+voidp pkt;
+int len;
+voidp where;
+xdrproc_t where_xdr;
+{
+ XDR reply_xdr;
+ int ok;
+ struct rpc_err err;
+ struct rpc_msg reply_msg;
+ int error = 0;
+
+ /*bzero((voidp) &err, sizeof(err));*/
+ bzero((voidp) &reply_msg, sizeof(reply_msg));
+
+ reply_msg.acpted_rply.ar_results.where = (caddr_t) where;
+ reply_msg.acpted_rply.ar_results.proc = where_xdr;
+
+ xdrmem_create(&reply_xdr, pkt, len, XDR_DECODE);
+
+ ok = xdr_replymsg(&reply_xdr, &reply_msg);
+ if (!ok) {
+ error = EIO;
+ goto drop;
+ }
+ _seterr_reply(&reply_msg, &err);
+ if (err.re_status != RPC_SUCCESS) {
+ error = EIO;
+ goto drop;
+ }
+
+drop:
+ if (reply_msg.rm_reply.rp_stat == MSG_ACCEPTED &&
+ reply_msg.acpted_rply.ar_verf.oa_base) {
+ reply_xdr.x_op = XDR_FREE;
+ (void)xdr_opaque_auth(&reply_xdr,
+ &reply_msg.acpted_rply.ar_verf);
+ }
+ xdr_destroy(&reply_xdr);
+
+ return error;
+}
+
+int make_rpc_packet P((char *buf, int buflen, unsigned long proc,
+ struct rpc_msg *mp, voidp arg, xdrproc_t arg_xdr, AUTH *auth));
+int make_rpc_packet(buf, buflen, proc, mp, arg, arg_xdr, auth)
+char *buf;
+int buflen;
+unsigned long proc;
+struct rpc_msg *mp;
+voidp arg;
+xdrproc_t arg_xdr;
+AUTH *auth;
+{
+ XDR msg_xdr;
+ int len;
+
+ xdrmem_create(&msg_xdr, buf, buflen, XDR_ENCODE);
+ /*
+ * Basic protocol header
+ */
+ if (!xdr_callhdr(&msg_xdr, mp))
+ return -EIO;
+ /*
+ * Called procedure number
+ */
+ if (!xdr_enum(&msg_xdr, (enum_t *)&proc))
+ return -EIO;
+ /*
+ * Authorization
+ */
+ if (!AUTH_MARSHALL(auth, &msg_xdr))
+ return -EIO;
+ /*
+ * Arguments
+ */
+ if (!(*arg_xdr)(&msg_xdr, arg))
+ return -EIO;
+ /*
+ * Determine length
+ */
+ len = xdr_getpos(&msg_xdr);
+ /*
+ * Throw away xdr
+ */
+ xdr_destroy(&msg_xdr);
+ return len;
+}
+
+
+/*
+ * Early RPC seems to be missing these..
+ * Extracted from the RPC 3.9 sources as indicated
+ */
+
+#ifdef NEED_XDR_POINTER
+/* @(#)xdr_reference.c 1.1 87/11/04 3.9 RPCSRC */
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * 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
+ */
+
+
+/*
+ * xdr_pointer():
+ *
+ * XDR a pointer to a possibly recursive data structure. This
+ * differs with xdr_reference in that it can serialize/deserialiaze
+ * trees correctly.
+ *
+ * What's sent is actually a union:
+ *
+ * union object_pointer switch (boolean b) {
+ * case TRUE: object_data data;
+ * case FALSE: void nothing;
+ * }
+ *
+ * > objpp: Pointer to the pointer to the object.
+ * > obj_size: size of the object.
+ * > xdr_obj: routine to XDR an object.
+ *
+ */
+bool_t
+xdr_pointer(xdrs,objpp,obj_size,xdr_obj)
+ register XDR *xdrs;
+ char **objpp;
+ u_int obj_size;
+ xdrproc_t xdr_obj;
+{
+
+ bool_t more_data;
+
+ more_data = (*objpp != NULL);
+ if (! xdr_bool(xdrs,&more_data)) {
+ return (FALSE);
+ }
+ if (! more_data) {
+ *objpp = NULL;
+ return (TRUE);
+ }
+ return (xdr_reference(xdrs,objpp,obj_size,xdr_obj));
+}
+#endif /* NEED_XDR_POINTER */
+
+#ifdef NEED_CLNT_SPERRNO
+/* @(#)clnt_perror.c 1.1 87/11/04 3.9 RPCSRC */
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * 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
+ */
+
+struct rpc_errtab {
+ enum clnt_stat status;
+ char *message;
+};
+
+static struct rpc_errtab rpc_errlist[] = {
+ { RPC_SUCCESS,
+ "RPC: Success" },
+ { RPC_CANTENCODEARGS,
+ "RPC: Can't encode arguments" },
+ { RPC_CANTDECODERES,
+ "RPC: Can't decode result" },
+ { RPC_CANTSEND,
+ "RPC: Unable to send" },
+ { RPC_CANTRECV,
+ "RPC: Unable to receive" },
+ { RPC_TIMEDOUT,
+ "RPC: Timed out" },
+ { RPC_VERSMISMATCH,
+ "RPC: Incompatible versions of RPC" },
+ { RPC_AUTHERROR,
+ "RPC: Authentication error" },
+ { RPC_PROGUNAVAIL,
+ "RPC: Program unavailable" },
+ { RPC_PROGVERSMISMATCH,
+ "RPC: Program/version mismatch" },
+ { RPC_PROCUNAVAIL,
+ "RPC: Procedure unavailable" },
+ { RPC_CANTDECODEARGS,
+ "RPC: Server can't decode arguments" },
+ { RPC_SYSTEMERROR,
+ "RPC: Remote system error" },
+ { RPC_UNKNOWNHOST,
+ "RPC: Unknown host" },
+/* { RPC_UNKNOWNPROTO,
+ "RPC: Unknown protocol" },*/
+ { RPC_PMAPFAILURE,
+ "RPC: Port mapper failure" },
+ { RPC_PROGNOTREGISTERED,
+ "RPC: Program not registered"},
+ { RPC_FAILED,
+ "RPC: Failed (unspecified error)"}
+};
+
+
+/*
+ * This interface for use by clntrpc
+ */
+char *
+clnt_sperrno(stat)
+ enum clnt_stat stat;
+{
+ int i;
+
+ for (i = 0; i < sizeof(rpc_errlist)/sizeof(struct rpc_errtab); i++) {
+ if (rpc_errlist[i].status == stat) {
+ return (rpc_errlist[i].message);
+ }
+ }
+ return ("RPC: (unknown error code)");
+}
+
+#endif /* NEED_CLNT_SPERRNO */
+
diff --git a/usr.sbin/amd/amd/mntfs.c b/usr.sbin/amd/amd/mntfs.c
new file mode 100644
index 0000000..a6d3dd0
--- /dev/null
+++ b/usr.sbin/amd/amd/mntfs.c
@@ -0,0 +1,367 @@
+/*-
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: mntfs.c,v 1.3 1997/02/22 16:01:35 peter Exp $
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)mntfs.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+
+#include "am.h"
+
+extern qelem mfhead;
+qelem mfhead = { &mfhead, &mfhead };
+
+int mntfs_allocated;
+
+#ifdef notdef
+/*
+ * This is the default attributes field which
+ * is copied into every new node to be created.
+ * The individual filesystem fs_init() routines
+ * patch the copy to represent the particular
+ * details for the relevant filesystem type
+ */
+static struct fattr gen_fattr = {
+ NFDIR, /* type */
+ NFSMODE_DIR | 0555, /* mode */
+ 2, /* nlink */
+ 0, /* uid */
+ 0, /* gid */
+ 512, /* size */
+ 4096, /* blocksize */
+ 0, /* rdev */
+ 1, /* blocks */
+ 0, /* fsid */
+ 0, /* fileid */
+ { 0, 0 }, /* atime */
+ { 0, 0 }, /* mtime */
+ { 0, 0 }, /* ctime */
+};
+#endif /* notdef */
+
+mntfs *dup_mntfs(mf)
+mntfs *mf;
+{
+ if (mf->mf_refc == 0) {
+ if (mf->mf_cid)
+ untimeout(mf->mf_cid);
+ mf->mf_cid = 0;
+#ifdef notdef
+ mf->mf_error = -1;
+ mf->mf_flags &= ~MFF_ERROR;
+#endif
+ }
+ mf->mf_refc++;
+ return mf;
+}
+
+static void init_mntfs P((mntfs *mf, am_ops *ops, am_opts *mo, char *mp, char *info, char *auto_opts, char *mopts, char *remopts));
+static void init_mntfs(mf, ops, mo, mp, info, auto_opts, mopts, remopts)
+mntfs *mf;
+am_ops *ops;
+am_opts *mo;
+char *mp;
+char *info;
+char *auto_opts;
+char *mopts;
+char *remopts;
+{
+ mf->mf_ops = ops;
+ mf->mf_fo = mo;
+ mf->mf_mount = strdup(mp);
+ mf->mf_info = strdup(info);
+ mf->mf_auto = strdup(auto_opts);
+ mf->mf_mopts = strdup(mopts);
+ mf->mf_remopts = strdup(remopts);
+ mf->mf_refc = 1;
+ mf->mf_flags = 0;
+ mf->mf_error = -1;
+ mf->mf_cid = 0;
+ mf->mf_private = 0;
+ mf->mf_prfree = 0;
+#ifdef notdef
+ mf->mf_attr.status = NFS_OK;
+ mf->mf_fattr = gen_fattr;
+ mf->mf_fattr.fsid = 42;
+ mf->mf_fattr.fileid = 0;
+ mf->mf_fattr.atime.seconds = clocktime();
+ mf->mf_fattr.atime.useconds = 0;
+ mf->mf_fattr.mtime = mf->mf_fattr.ctime = mf->mf_fattr.atime;
+#endif
+
+ if (ops->ffserver)
+ mf->mf_server = (*ops->ffserver)(mf);
+ else
+ mf->mf_server = 0;
+}
+
+static mntfs *alloc_mntfs P((am_ops *ops, am_opts *mo, char *mp, char *info, char *auto_opts, char *mopts, char *remopts));
+static mntfs *alloc_mntfs(ops, mo, mp, info, auto_opts, mopts, remopts)
+am_ops *ops;
+am_opts *mo;
+char *mp;
+char *info;
+char *auto_opts;
+char *mopts;
+char *remopts;
+{
+ mntfs *mf = ALLOC(mntfs);
+ init_mntfs(mf, ops, mo, mp, info, auto_opts, mopts, remopts);
+ ins_que(&mf->mf_q, &mfhead);
+ mntfs_allocated++;
+
+ return mf;
+}
+
+mntfs *find_mntfs P((am_ops *ops, am_opts *mo, char *mp, char *info, char *auto_opts, char *mopts, char *remopts));
+mntfs *find_mntfs(ops, mo, mp, info, auto_opts, mopts, remopts)
+am_ops *ops;
+am_opts *mo;
+char *mp;
+char *info;
+char *auto_opts;
+char *mopts;
+char *remopts;
+{
+ mntfs *mf;
+
+#ifdef DEBUG
+ dlog("Locating mntfs reference to %s", mp);
+#endif /* DEBUG */
+ ITER(mf, mntfs, &mfhead) {
+ if (STREQ(mf->mf_mount, mp)) {
+ /*
+ * Handle cases where error ops are involved
+ */
+ if (ops == &efs_ops) {
+ /*
+ * If the existing ops are not efs_ops
+ * then continue...
+ */
+ if (mf->mf_ops != &efs_ops)
+ continue;
+ } else /* ops != &efs_ops */ {
+ /*
+ * If the existing ops are efs_ops
+ * then continue...
+ */
+ if (mf->mf_ops == &efs_ops)
+ continue;
+ }
+
+ if ((mf->mf_flags & MFF_RESTART) && amd_state == Run) {
+ /*
+ * Restart a previously mounted filesystem.
+ */
+ mntfs *mf2 = alloc_mntfs(&ifs_ops, mo, mp, info, auto_opts, mopts, remopts);
+#ifdef DEBUG
+ dlog("Restarting filesystem %s", mf->mf_mount);
+#endif /* DEBUG */
+ /*
+ * Remember who we are restarting
+ */
+ mf2->mf_private = (voidp) dup_mntfs(mf);
+ mf2->mf_prfree = free_mntfs;
+ return mf2;
+ }
+ mf->mf_fo = mo;
+ if (!(mf->mf_flags & (MFF_MOUNTED|MFF_MOUNTING|MFF_UNMOUNTING))) {
+ fserver *fs;
+ mf->mf_flags &= ~MFF_ERROR;
+ mf->mf_error = -1;
+ mf->mf_auto = strealloc(mf->mf_auto, auto_opts);
+ mf->mf_mopts = strealloc(mf->mf_mopts, mopts);
+ mf->mf_remopts = strealloc(mf->mf_remopts, remopts);
+ mf->mf_info = strealloc(mf->mf_info, info);
+ if (mf->mf_private && mf->mf_prfree) {
+ (*mf->mf_prfree)(mf->mf_private);
+ mf->mf_private = 0;
+ }
+ fs = ops->ffserver ? (*ops->ffserver)(mf) : (fserver *) 0;
+ if (mf->mf_server)
+ free_srvr(mf->mf_server);
+ mf->mf_server = fs;
+ }
+ return dup_mntfs(mf);
+ }
+ }
+
+ return alloc_mntfs(ops, mo, mp, info, auto_opts, mopts, remopts);
+}
+
+mntfs *new_mntfs()
+{
+ return alloc_mntfs(&efs_ops, (am_opts *) 0, "//nil//", ".", "", "", "");
+}
+
+static void uninit_mntfs(mf, rmd)
+mntfs *mf;
+int rmd;
+{
+ if (mf->mf_auto) free((voidp) mf->mf_auto);
+ if (mf->mf_mopts) free((voidp) mf->mf_mopts);
+ if (mf->mf_remopts) free((voidp) mf->mf_remopts);
+ if (mf->mf_info) free((voidp) mf->mf_info);
+ if (mf->mf_private && mf->mf_prfree)
+ (*mf->mf_prfree)(mf->mf_private);
+ /*
+ * Clean up any directories that were made
+ */
+ if (rmd && (mf->mf_flags & MFF_MKMNT))
+ rmdirs(mf->mf_mount);
+ if (mf->mf_mount) free((voidp) mf->mf_mount);
+
+ /*
+ * Clean up the file server
+ */
+ if (mf->mf_server)
+ free_srvr(mf->mf_server);
+
+ /*
+ * Don't do a callback on this mount
+ */
+ if (mf->mf_cid) {
+ untimeout(mf->mf_cid);
+ mf->mf_cid = 0;
+ }
+}
+
+static void discard_mntfs(mf)
+mntfs *mf;
+{
+ rem_que(&mf->mf_q);
+ /*
+ * Free memory
+ */
+ uninit_mntfs(mf, TRUE);
+ free((voidp) mf);
+
+ --mntfs_allocated;
+}
+
+void flush_mntfs()
+{
+ mntfs *mf;
+
+ mf = FIRST(mntfs, &mfhead);
+ while (mf != HEAD(mntfs, &mfhead)) {
+ mntfs *mf2 = mf;
+ mf = NEXT(mntfs, mf);
+ if (mf2->mf_refc == 0 && mf2->mf_cid)
+ discard_mntfs(mf2);
+ }
+}
+
+void free_mntfs(mf)
+mntfs *mf;
+{
+ if (--mf->mf_refc == 0) {
+ if (mf->mf_flags & MFF_MOUNTED) {
+ int quoted;
+ mf->mf_flags &= ~MFF_MOUNTED;
+
+ /*
+ * Record for posterity
+ */
+ quoted = strchr(mf->mf_info, ' ') != 0; /* cheap */
+ plog(XLOG_INFO, "%s%s%s %sed fstype %s from %s",
+ quoted ? "\"" : "",
+ mf->mf_info,
+ quoted ? "\"" : "",
+ mf->mf_error ? "discard" : "unmount",
+ mf->mf_ops->fs_type, mf->mf_mount);
+ }
+
+ if (mf->mf_ops->fs_flags & FS_DISCARD) {
+#ifdef DEBUG
+ dlog("Immediately discarding mntfs for %s", mf->mf_mount);
+#endif /* DEBUG */
+ discard_mntfs(mf);
+ } else {
+#ifdef DEBUG
+ if (mf->mf_flags & MFF_RESTART) {
+ dlog("Discarding remount hook for %s", mf->mf_mount);
+ } else {
+ dlog("Discarding last mntfs reference to %s fstype %s",
+ mf->mf_mount, mf->mf_ops->fs_type);
+ }
+ if (mf->mf_flags & (MFF_MOUNTED|MFF_MOUNTING|MFF_UNMOUNTING))
+ dlog("mntfs reference for %s still active", mf->mf_mount);
+#endif /* DEBUG */
+ mf->mf_cid = timeout(ALLOWED_MOUNT_TIME, discard_mntfs, (voidp) mf);
+ }
+ }
+}
+
+mntfs *realloc_mntfs P((mntfs *mf, am_ops *ops, am_opts *mo, char *mp, char *info, char *auto_opts, char *mopts, char *remopts));
+mntfs *realloc_mntfs(mf, ops, mo, mp, info, auto_opts, mopts, remopts)
+mntfs *mf;
+am_ops *ops;
+am_opts *mo;
+char *mp;
+char *info;
+char *auto_opts;
+char *mopts;
+char *remopts;
+{
+ mntfs *mf2;
+ if (mf->mf_refc == 1 && mf->mf_ops == &ifs_ops && STREQ(mf->mf_mount, mp)) {
+ /*
+ * If we are inheriting then just return
+ * the same node...
+ */
+ return mf;
+ }
+
+ /*
+ * Re-use the existing mntfs if it is mounted.
+ * This traps a race in nfsx.
+ */
+ if (mf->mf_ops != &efs_ops &&
+ (mf->mf_flags & MFF_MOUNTED) &&
+ !FSRV_ISDOWN(mf->mf_server)) {
+ mf->mf_fo = mo;
+ return mf;
+ }
+
+ mf2 = find_mntfs(ops, mo, mp, info, auto_opts, mopts, remopts);
+ free_mntfs(mf);
+ return mf2;
+}
diff --git a/usr.sbin/amd/amd/mount_fs.c b/usr.sbin/amd/amd/mount_fs.c
new file mode 100644
index 0000000..2c3a34e
--- /dev/null
+++ b/usr.sbin/amd/amd/mount_fs.c
@@ -0,0 +1,274 @@
+/*
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (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_fs.c 8.1 (Berkeley) 6/6/93
+ *
+ * $Id$
+ *
+ */
+
+#include "am.h"
+#ifdef NFS_3
+typedef nfs_fh fhandle_t;
+#endif /* NFS_3 */
+#include <sys/mount.h>
+
+#include <sys/stat.h>
+
+/*
+ * Standard mount flags
+ */
+#ifdef hpux
+/*
+ * HP-UX has an annoying feature of printing
+ * error msgs on /dev/console
+ */
+#undef M_NOSUID
+#endif /* hpux */
+
+struct opt_tab mnt_flags[] = {
+ { "ro", M_RDONLY },
+#ifdef M_CACHE
+ { "nocache", M_NOCACHE },
+#endif /* M_CACHE */
+#ifdef M_GRPID
+ { "grpid", M_GRPID },
+#endif /* M_GRPID */
+#ifdef M_MULTI
+ { "multi", M_MULTI },
+#endif /* M_MULTI */
+#ifdef M_NODEV
+ { "nodev", M_NODEV },
+#endif /* M_NODEV */
+#ifdef M_NOEXEC
+ { "noexec", M_NOEXEC },
+#endif /* M_NOEXEC */
+#ifdef M_NOSUB
+ { "nosub", M_NOSUB },
+#endif /* M_NOSUB */
+#ifdef M_NOSUID
+ { "nosuid", M_NOSUID },
+#endif /* M_NOSUID */
+#ifdef M_SYNC
+ { "sync", M_SYNC },
+#endif /* M_SYNC */
+ { 0, 0 }
+};
+
+int compute_mount_flags(mnt)
+struct mntent *mnt;
+{
+ struct opt_tab *opt;
+ int flags;
+#ifdef NFS_4
+ flags = M_NEWTYPE;
+#else
+ flags = 0;
+#endif /* NFS_4 */
+
+ /*
+ * Crack basic mount options
+ */
+ for (opt = mnt_flags; opt->opt; opt++)
+ flags |= hasmntopt(mnt, opt->opt) ? opt->flag : 0;
+
+ return flags;
+}
+
+int mount_fs P((struct mntent *mnt, int flags, caddr_t mnt_data, int retry, MTYPE_TYPE type));
+int mount_fs(mnt, flags, mnt_data, retry, type)
+struct mntent *mnt;
+int flags;
+caddr_t mnt_data;
+int retry;
+MTYPE_TYPE type;
+{
+ int error = 0;
+#ifdef MNTINFO_DEV
+ struct stat stb;
+ char *xopts = 0;
+#endif /* MNTINFO_DEV */
+
+#ifdef DEBUG
+#ifdef NFS_4
+ dlog("%s fstype %s (%s) flags %#x (%s)",
+ mnt->mnt_dir, type, mnt->mnt_type, flags, mnt->mnt_opts);
+#else
+ dlog("%s fstype %d (%s) flags %#x (%s)",
+ mnt->mnt_dir, type, mnt->mnt_type, flags, mnt->mnt_opts);
+#endif /* NFS_4 */
+#endif /* DEBUG */
+
+ /*
+ * Fake some mount table entries for the automounter
+ */
+#ifdef FASCIST_DF_COMMAND
+ /*
+ * Some systems have a df command which blows up when
+ * presented with an unknown mount type.
+ */
+ if (STREQ(mnt->mnt_type, MNTTYPE_AUTO)) {
+ /*
+ * Try it with the normal name
+ */
+ mnt->mnt_type = FASCIST_DF_COMMAND;
+ }
+#endif /* FASCIST_DF_COMMAND */
+
+again:
+ clock_valid = 0;
+ error = MOUNT_TRAP(type, mnt, flags, mnt_data);
+ if (error < 0)
+ plog(XLOG_ERROR, "%s: mount: %m", mnt->mnt_dir);
+ if (error < 0 && --retry > 0) {
+ sleep(1);
+ goto again;
+ }
+ if (error < 0) {
+#ifdef notdef
+ if (automount)
+ going_down(errno);
+#endif
+ return errno;
+ }
+
+#ifdef UPDATE_MTAB
+#ifdef MNTINFO_DEV
+ /*
+ * Add the extra dev= field to the mount table.
+ */
+ if (lstat(mnt->mnt_dir, &stb) == 0) {
+ char *zopts = (char *) xmalloc(strlen(mnt->mnt_opts) + 32);
+ xopts = mnt->mnt_opts;
+ if (sizeof(stb.st_dev) == 2) {
+ /* e.g. SunOS 4.1 */
+ sprintf(zopts, "%s,%s=%s%04lx", xopts, MNTINFO_DEV,
+ MNTINFO_PREF, (u_long) stb.st_dev & 0xffff);
+ } else {
+ /* e.g. System Vr4 */
+ sprintf(zopts, "%s,%s=%s%08lx", xopts, MNTINFO_DEV,
+ MNTINFO_PREF, (u_long) stb.st_dev);
+ }
+ mnt->mnt_opts = zopts;
+ }
+#endif /* MNTINFO_DEV */
+
+#ifdef FIXUP_MNTENT
+ /*
+ * Additional fields in struct mntent
+ * are fixed up here
+ */
+ FIXUP_MNTENT(mnt);
+#endif
+
+ write_mntent(mnt);
+#ifdef MNTINFO_DEV
+ if (xopts) {
+ free(mnt->mnt_opts);
+ mnt->mnt_opts = xopts;
+ }
+#endif /* MNTINFO_DEV */
+#endif /* UPDATE_MTAB */
+
+ return 0;
+}
+
+#ifdef NEED_MNTOPT_PARSER
+/*
+ * Some systems don't provide these to the user,
+ * but amd needs them, so...
+ *
+ * From: Piete Brooks <pb@cl.cam.ac.uk>
+ */
+
+#include <ctype.h>
+
+static char *nextmntopt(p)
+char **p;
+{
+ char *cp = *p;
+ char *rp;
+ /*
+ * Skip past white space
+ */
+ while (*cp && isspace(*cp))
+ cp++;
+ /*
+ * Word starts here
+ */
+ rp = cp;
+ /*
+ * Scan to send of string or separator
+ */
+ while (*cp && *cp != ',')
+ cp++;
+ /*
+ * If separator found the overwrite with nul char.
+ */
+ if (*cp) {
+ *cp = '\0';
+ cp++;
+ }
+ /*
+ * Return value for next call
+ */
+ *p = cp;
+ return rp;
+}
+
+char *hasmntopt(mnt, opt)
+struct mntent *mnt;
+char *opt;
+{
+ char t[MNTMAXSTR];
+ char *f;
+ char *o = t;
+ int l = strlen(opt);
+ strncpy(t, mnt->mnt_opts, MNTMAXSTR - 1);
+ t[MNTMAXSTR - 1] = 0;
+
+ while (*(f = nextmntopt(&o)))
+ if (strncmp(opt, f, l) == 0)
+ return f - t + mnt->mnt_opts;
+
+ return 0;
+}
+#endif /* NEED_MNTOPT_PARSER */
+
+#ifdef MOUNT_HELPER_SOURCE
+#include MOUNT_HELPER_SOURCE
+#endif /* MOUNT_HELPER_SOURCE */
diff --git a/usr.sbin/amd/amd/mtab.c b/usr.sbin/amd/amd/mtab.c
new file mode 100644
index 0000000..cea9d46
--- /dev/null
+++ b/usr.sbin/amd/amd/mtab.c
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 1989 Jan-Simon Pendry
+ * Copyright (c) 1989 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)mtab.c 8.1 (Berkeley) 6/6/93
+ *
+ * $Id$
+ *
+ */
+
+#include "am.h"
+
+/*
+ * Firewall /etc/mtab entries
+ */
+void mnt_free P((struct mntent *mp));
+void mnt_free(mp)
+struct mntent *mp;
+{
+ free(mp->mnt_fsname);
+ free(mp->mnt_dir);
+ free(mp->mnt_type);
+ free(mp->mnt_opts);
+ free((voidp) mp);
+}
+
+/*
+ * Discard memory allocated for mount list
+ */
+void discard_mntlist P((mntlist *mp));
+void discard_mntlist(mp)
+mntlist *mp;
+{
+ mntlist *mp2;
+
+ while (mp2 = mp) {
+ mp = mp->mnext;
+ if (mp2->mnt)
+ mnt_free(mp2->mnt);
+ free((voidp) mp2);
+ }
+}
+
+/*
+ * Throw away a mount list
+ */
+void free_mntlist P((mntlist *mp));
+void free_mntlist(mp)
+mntlist *mp;
+{
+ discard_mntlist(mp);
+ unlock_mntlist();
+}
+
+/*
+ * Utility routine which determines the value of a
+ * numeric option in the mount options (such as port=%d).
+ * Returns 0 if the option is not specified.
+ */
+int hasmntval P((struct mntent *mnt, char *opt));
+int hasmntval(mnt, opt)
+struct mntent *mnt;
+char *opt;
+{
+ char *str = hasmntopt(mnt, opt);
+ if (str) {
+ char *eq = strchr(str, '=');
+ if (eq)
+ return atoi(eq+1);
+ else
+ plog(XLOG_USER, "bad numeric option \"%s\" in \"%s\"", opt, str);
+ }
+
+ return 0;
+}
diff --git a/usr.sbin/amd/amd/nfs_ops.c b/usr.sbin/amd/amd/nfs_ops.c
new file mode 100644
index 0000000..1d94943
--- /dev/null
+++ b/usr.sbin/amd/amd/nfs_ops.c
@@ -0,0 +1,883 @@
+/*-
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: nfs_ops.c,v 1.6 1997/02/22 16:01:37 peter Exp $
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)nfs_ops.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "am.h"
+#include <sys/stat.h>
+
+#ifdef HAS_NFS
+
+#define NFS
+#define NFSCLIENT
+#ifdef NFS_3
+typedef nfs_fh fhandle_t;
+#endif /* NFS_3 */
+#ifdef NFS_HDR
+#include NFS_HDR
+#endif /* NFS_HDR */
+#include <sys/mount.h>
+#include "mount.h"
+#include "mountres.h"
+
+/*
+ * Network file system
+ */
+
+/*
+ * Convert from nfsstat to UN*X error code
+ */
+#define unx_error(e) ((e) < 10000 ? (int)(e) \
+ : ((e) == MNT3ERR_NOTSUPP ? EOPNOTSUPP \
+ : ((e) == MNT3ERR_SERVERFAULT ? EIO \
+ : EINVAL)))
+
+/*
+ * The NFS layer maintains a cache of file handles.
+ * This is *fundamental* to the implementation and
+ * also allows quick remounting when a filesystem
+ * is accessed soon after timing out.
+ *
+ * The NFS server layer knows to flush this cache
+ * when a server goes down so avoiding stale handles.
+ *
+ * Each cache entry keeps a hard reference to
+ * the corresponding server. This ensures that
+ * the server keepalive information is maintained.
+ *
+ * The copy of the sockaddr_in here is taken so
+ * that the port can be twiddled to talk to mountd
+ * instead of portmap or the NFS server as used
+ * elsewhere.
+ * The port# is flushed if a server goes down.
+ * The IP address is never flushed - we assume
+ * that the address of a mounted machine never
+ * changes. If it does, then you have other
+ * problems...
+ */
+typedef struct fh_cache fh_cache;
+struct fh_cache {
+ qelem fh_q; /* List header */
+ voidp fh_wchan; /* Wait channel */
+ int fh_error; /* Valid data? */
+ int fh_id; /* Unique id */
+ int fh_cid; /* Callout id */
+ mountres fh_mountres; /* Result of mount rpc */
+ struct sockaddr_in fh_sin; /* Address of mountd */
+ fserver *fh_fs; /* Server holding filesystem */
+ char *fh_path; /* Filesystem on host */
+};
+
+/*
+ * FH_TTL is the time a file handle will remain in the cache since
+ * last being used. If the file handle becomes invalid, then it
+ * will be flushed anyway.
+ */
+#define FH_TTL (5 * 60) /* five minutes */
+#define FH_TTL_ERROR (30) /* 30 seconds */
+
+static int fh_id = 0;
+#define FHID_ALLOC() (++fh_id)
+extern qelem fh_head;
+qelem fh_head = { &fh_head, &fh_head };
+
+static int call_mountd P((fh_cache*, unsigned long, unsigned long, fwd_fun, voidp));
+
+AUTH *nfs_auth;
+
+static fh_cache *find_nfs_fhandle_cache P((voidp idv, int done));
+static fh_cache *find_nfs_fhandle_cache(idv, done)
+voidp idv;
+int done;
+{
+ fh_cache *fp, *fp2 = 0;
+ int id = (int) idv;
+
+ ITER(fp, fh_cache, &fh_head) {
+ if (fp->fh_id == id) {
+ fp2 = fp;
+ break;
+ }
+ }
+
+#ifdef DEBUG
+ if (fp2) {
+ dlog("fh cache gives fp %#x, fs %s", fp2, fp2->fh_path);
+ } else {
+ dlog("fh cache search failed");
+ }
+#endif /* DEBUG */
+
+ if (fp2 && !done) {
+ fp2->fh_error = ETIMEDOUT;
+ return 0;
+ }
+
+ return fp2;
+}
+
+/*
+ * Called when a filehandle appears
+ */
+static void got_nfs_fh P((voidp pkt, int len, struct sockaddr_in *sa,
+ struct sockaddr_in *ia, voidp idv, int done));
+static void got_nfs_fh(pkt, len, sa, ia, idv, done)
+voidp pkt;
+int len;
+struct sockaddr_in *sa, *ia;
+voidp idv;
+int done;
+{
+ fh_cache *fp = find_nfs_fhandle_cache(idv, done);
+ if (fp) {
+ if (fp->fh_mountres.mr_version == MOUNTVERS3) {
+ fp->fh_error = pickup_rpc_reply(pkt, len, (voidp) &fp->fh_mountres.mr_mountres3, xdr_mountres3);
+ if (fp->fh_error) {
+ /*
+ * Fall back to version 1 protocol.
+ */
+#ifdef DEBUG
+ dlog("mount version 3 refused, retrying with version 1");
+#endif
+ fp->fh_id = FHID_ALLOC();
+ fp->fh_mountres.mr_version = MOUNTVERS;
+ call_mountd(fp, MOUNTPROC_MNT, MOUNTVERS, got_nfs_fh, fp->fh_wchan);
+ }
+ } else
+ fp->fh_error = pickup_rpc_reply(pkt, len, (voidp) &fp->fh_mountres.mr_fhstatus, xdr_fhstatus);
+ if (!fp->fh_error) {
+#ifdef DEBUG
+ dlog("got filehandle for %s:%s, version=%d", fp->fh_fs->fs_host, fp->fh_path, fp->fh_mountres.mr_version);
+#endif /* DEBUG */
+ /*
+ * Wakeup anything sleeping on this filehandle
+ */
+ if (fp->fh_wchan) {
+#ifdef DEBUG
+ dlog("Calling wakeup on %#x", fp->fh_wchan);
+#endif /* DEBUG */
+ wakeup(fp->fh_wchan);
+ }
+ }
+ }
+}
+
+void flush_nfs_fhandle_cache P((fserver *fs));
+void flush_nfs_fhandle_cache(fs)
+fserver *fs;
+{
+ fh_cache *fp;
+ ITER(fp, fh_cache, &fh_head) {
+ if (fp->fh_fs == fs || fs == 0) {
+ fp->fh_sin.sin_port = (u_short) 0;
+ fp->fh_error = -1;
+ }
+ }
+}
+
+static void discard_fh P((fh_cache *fp));
+static void discard_fh(fp)
+fh_cache *fp;
+{
+ rem_que(&fp->fh_q);
+#ifdef DEBUG
+ dlog("Discarding filehandle for %s:%s", fp->fh_fs->fs_host, fp->fh_path);
+#endif /* DEBUG */
+ free_srvr(fp->fh_fs);
+ if (fp->fh_mountres.mr_version == MOUNTVERS3)
+ xdr_free(xdr_mountres3, (char *) &fp->fh_mountres.mr_mountres3);
+ free((voidp) fp->fh_path);
+ free((voidp) fp);
+}
+
+/*
+ * Determine the file handle for a node
+ */
+static int prime_nfs_fhandle_cache P((char *path, fserver *fs, int forcev2, struct mountres *mrbuf, voidp wchan));
+static int prime_nfs_fhandle_cache(path, fs, forcev2, mrbuf, wchan)
+char *path;
+fserver *fs;
+int forcev2;
+struct mountres *mrbuf;
+voidp wchan;
+{
+ fh_cache *fp, *fp_save = 0;
+ int error;
+ int reuse_id = FALSE;
+
+#ifdef DEBUG
+ dlog("Searching cache for %s:%s", fs->fs_host, path);
+#endif /* DEBUG */
+
+ /*
+ * First search the cache
+ */
+ ITER(fp, fh_cache, &fh_head) {
+ if (fs == fp->fh_fs && strcmp(path, fp->fh_path) == 0) {
+ switch (fp->fh_error) {
+ case 0:
+ if (fp->fh_mountres.mr_version == MOUNTVERS)
+ error = fp->fh_error = unx_error(fp->fh_mountres.mr_fhstatus.fhs_status);
+ else
+ error = fp->fh_error = unx_error(fp->fh_mountres.mr_mountres3.fhs_status);
+ if (error == 0) {
+ if (mrbuf)
+ bcopy((voidp) &fp->fh_mountres, (voidp) mrbuf,
+ sizeof(fp->fh_mountres));
+ if (fp->fh_cid)
+ untimeout(fp->fh_cid);
+ fp->fh_cid = timeout(FH_TTL, discard_fh, (voidp) fp);
+ } else if (error == EACCES) {
+ /*
+ * Now decode the file handle return code.
+ */
+ plog(XLOG_INFO, "Filehandle denied for \"%s:%s\"",
+ fs->fs_host, path);
+ } else {
+ errno = error; /* XXX */
+ plog(XLOG_INFO, "Filehandle error for \"%s:%s\": %m",
+ fs->fs_host, path);
+ }
+
+ /*
+ * The error was returned from the remote mount daemon.
+ * Policy: this error will be cached for now...
+ */
+ return error;
+
+ case -1:
+ /*
+ * Still thinking about it, but we can re-use.
+ */
+ fp_save = fp;
+ reuse_id = TRUE;
+ break;
+
+ default:
+ /*
+ * Return the error.
+ * Policy: make sure we recompute if required again
+ * in case this was caused by a network failure.
+ * This can thrash mountd's though... If you find
+ * your mountd going slowly then:
+ * 1. Add a fork() loop to main.
+ * 2. Remove the call to innetgr() and don't use
+ * netgroups, especially if you don't use YP.
+ */
+ error = fp->fh_error;
+ fp->fh_error = -1;
+ return error;
+ }
+ break;
+ }
+ }
+
+ /*
+ * Not in cache
+ */
+ if (fp_save) {
+ fp = fp_save;
+ /*
+ * Re-use existing slot
+ */
+ untimeout(fp->fh_cid);
+ free_srvr(fp->fh_fs);
+ free(fp->fh_path);
+ } else {
+ fp = ALLOC(fh_cache);
+ bzero((voidp) fp, sizeof(*fp));
+ ins_que(&fp->fh_q, &fh_head);
+ }
+ if (!reuse_id)
+ fp->fh_id = FHID_ALLOC();
+ fp->fh_wchan = wchan;
+ fp->fh_error = -1;
+ fp->fh_cid = timeout(FH_TTL, discard_fh, (voidp) fp);
+ if (forcev2) {
+ /*
+ * Force an NFSv2 mount.
+ */
+#ifdef DEBUG
+ dlog("forcing v2 mount");
+#endif
+ fp->fh_mountres.mr_version = MOUNTVERS;
+ } else {
+ /*
+ * Attempt v3 first.
+ */
+ fp->fh_mountres.mr_version = MOUNTVERS3;
+ fp->fh_mountres.mr_mountres3.mountres3_u.mountinfo.fhandle.fhandle3_val = NULL;
+ }
+
+ /*
+ * If the address has changed then don't try to re-use the
+ * port information
+ */
+ if (fp->fh_sin.sin_addr.s_addr != fs->fs_ip->sin_addr.s_addr) {
+ fp->fh_sin = *fs->fs_ip;
+ fp->fh_sin.sin_port = 0;
+ }
+ fp->fh_fs = dup_srvr(fs);
+ fp->fh_path = strdup(path);
+
+ error = call_mountd(fp, MOUNTPROC_MNT, fp->fh_mountres.mr_version, got_nfs_fh, wchan);
+ if (error) {
+ /*
+ * Local error - cache for a short period
+ * just to prevent thrashing.
+ */
+ untimeout(fp->fh_cid);
+ fp->fh_cid = timeout(error < 0 ? 2 * ALLOWED_MOUNT_TIME : FH_TTL_ERROR,
+ discard_fh, (voidp) fp);
+ fp->fh_error = error;
+ } else {
+ error = fp->fh_error;
+ }
+ return error;
+}
+
+int make_nfs_auth P((void))
+{
+#ifdef HAS_NFS_QUALIFIED_NAMES
+ /*
+ * From: Chris Metcalf <metcalf@masala.lcs.mit.edu>
+ * Use hostd, not just hostname. Note that uids
+ * and gids and the gidlist are type *int* and not the
+ * system uid_t and gid_t types.
+ */
+ static int group_wheel = 0;
+ nfs_auth = authunix_create(hostd, 0, 0, 1, &group_wheel);
+#else
+ nfs_auth = authunix_create_default();
+#endif
+ if (!nfs_auth)
+ return ENOBUFS;
+ return 0;
+}
+
+static int call_mountd P((fh_cache *fp, u_long proc, u_long version, fwd_fun f, voidp wchan));
+static int call_mountd(fp, proc, version, f, wchan)
+fh_cache *fp;
+u_long proc;
+u_long version;
+fwd_fun f;
+voidp wchan;
+{
+ struct rpc_msg mnt_msg;
+ int len;
+ char iobuf[8192];
+ int error;
+
+ if (!nfs_auth) {
+ error = make_nfs_auth();
+ if (error)
+ return error;
+ }
+
+ if (fp->fh_sin.sin_port == 0) {
+ u_short port;
+ error = nfs_srvr_port(fp->fh_fs, &port, wchan);
+ if (error)
+ return error;
+ fp->fh_sin.sin_port = port;
+ }
+
+ rpc_msg_init(&mnt_msg, MOUNTPROG, version, (unsigned long) 0);
+ len = make_rpc_packet(iobuf, sizeof(iobuf), proc,
+ &mnt_msg, (voidp) &fp->fh_path, xdr_nfspath, nfs_auth);
+
+ if (len > 0) {
+ error = fwd_packet(MK_RPC_XID(RPC_XID_MOUNTD, fp->fh_id),
+ (voidp) iobuf, len, &fp->fh_sin, &fp->fh_sin, (voidp) fp->fh_id, f);
+ } else {
+ error = -len;
+ }
+/*
+ * It may be the case that we're sending to the wrong MOUNTD port. This
+ * occurs if mountd is restarted on the server after the port has been
+ * looked up and stored in the filehandle cache somewhere. The correct
+ * solution, if we're going to cache port numbers is to catch the ICMP
+ * port unreachable reply from the server and cause the portmap request
+ * to be redone. The quick solution here is to invalidate the MOUNTD
+ * port.
+ */
+ fp->fh_sin.sin_port = 0;
+
+ return error;
+}
+
+/*-------------------------------------------------------------------------*/
+
+/*
+ * NFS needs the local filesystem, remote filesystem
+ * remote hostname.
+ * Local filesystem defaults to remote and vice-versa.
+ */
+static char *nfs_match(fo)
+am_opts *fo;
+{
+ char *xmtab;
+ if (fo->opt_fs && !fo->opt_rfs)
+ fo->opt_rfs = fo->opt_fs;
+ if (!fo->opt_rfs) {
+ plog(XLOG_USER, "nfs: no remote filesystem specified");
+ return FALSE;
+ }
+ if (!fo->opt_rhost) {
+ plog(XLOG_USER, "nfs: no remote host specified");
+ return FALSE;
+ }
+ /*
+ * Determine magic cookie to put in mtab
+ */
+ xmtab = (char *) xmalloc(strlen(fo->opt_rhost) + strlen(fo->opt_rfs) + 2);
+ sprintf(xmtab, "%s:%s", fo->opt_rhost, fo->opt_rfs);
+#ifdef DEBUG
+ dlog("NFS: mounting remote server \"%s\", remote fs \"%s\" on \"%s\"",
+ fo->opt_rhost, fo->opt_rfs, fo->opt_fs);
+#endif /* DEBUG */
+
+ return xmtab;
+}
+
+static int forcev2(mf)
+mntfs *mf;
+{
+ struct mntent mnt;
+
+ mnt.mnt_dir = mf->mf_mount;
+ mnt.mnt_fsname = mf->mf_info;
+ mnt.mnt_type = MTAB_TYPE_NFS;
+ mnt.mnt_opts = mf->mf_mopts;
+ mnt.mnt_freq = 0;
+ mnt.mnt_passno = 0;
+
+ if (hasmntopt(&mnt, "nfsv2") != NULL)
+ return TRUE;
+ else
+ return FALSE;
+}
+
+/*
+ * Initialise am structure for nfs
+ */
+static int nfs_init(mf)
+mntfs *mf;
+{
+ if (!mf->mf_private) {
+ int error;
+ struct mountres mr;
+
+ char *colon = strchr(mf->mf_info, ':');
+ if (colon == 0)
+ return ENOENT;
+
+ error = prime_nfs_fhandle_cache(colon+1, mf->mf_server, forcev2(mf), &mr, (voidp) mf);
+ if (!error) {
+ mf->mf_private = (voidp) ALLOC(mountres);
+ mf->mf_prfree = (void (*)()) free;
+ bcopy((voidp) &mr, mf->mf_private, sizeof(mr));
+ }
+ return error;
+ }
+
+ return 0;
+}
+
+int mount_nfs_fh P((struct mountres *mrp, char *dir, char *fs_name, char *opts, mntfs *mf));
+int mount_nfs_fh(mrp, dir, fs_name, opts, mf)
+struct mountres *mrp;
+char *dir;
+char *fs_name;
+char *opts;
+mntfs *mf;
+{
+ struct nfs_args nfs_args;
+ struct mntent mnt;
+ int retry;
+ char *colon;
+ /*char *path;*/
+ char host[MAXHOSTNAMELEN + MAXPATHLEN + 2];
+ fserver *fs = mf->mf_server;
+ int flags;
+ char *xopts;
+ int error;
+#ifdef notdef
+ unsigned short port;
+#endif /* notdef */
+
+ MTYPE_TYPE type = MOUNT_TYPE_NFS;
+
+ bzero((voidp) &nfs_args, sizeof(nfs_args)); /* Paranoid */
+
+ /*
+ * Extract host name to give to kernel
+ */
+ if (!(colon = strchr(fs_name, ':')))
+ return ENOENT;
+#ifndef NFS_ARGS_NEEDS_PATH
+ *colon = '\0';
+#endif
+ strncpy(host, fs_name, sizeof(host));
+#ifndef NFS_ARGS_NEEDS_PATH
+ *colon = ':';
+#endif /* NFS_ARGS_NEEDS_PATH */
+ /*path = colon + 1;*/
+
+ if (mf->mf_remopts && *mf->mf_remopts && !islocalnet(fs->fs_ip->sin_addr.s_addr))
+ xopts = strdup(mf->mf_remopts);
+ else
+ xopts = strdup(opts);
+
+ bzero((voidp) &nfs_args, sizeof(nfs_args));
+
+ mnt.mnt_dir = dir;
+ mnt.mnt_fsname = fs_name;
+ mnt.mnt_type = MTAB_TYPE_NFS;
+ mnt.mnt_opts = xopts;
+ mnt.mnt_freq = 0;
+ mnt.mnt_passno = 0;
+
+ retry = hasmntval(&mnt, "retry");
+ if (retry <= 0)
+ retry = 1; /* XXX */
+
+/*again:*/
+
+ /*
+ * set mount args
+ */
+ if (mrp->mr_version == MOUNTVERS) {
+ NFS_FH_DREF(nfs_args.fh, (NFS_FH_TYPE) mrp->mr_fhstatus.fhstatus_u.fhs_fhandle);
+ nfs_args.fhsize = FHSIZE;
+ } else {
+ NFS_FH_DREF(nfs_args.fh, (NFS_FH_TYPE) mrp->mr_mountres3.mountres3_u.mountinfo.fhandle.fhandle3_val);
+ nfs_args.fhsize = mrp->mr_mountres3.mountres3_u.mountinfo.fhandle.fhandle3_len;
+ nfs_args.flags |= NFSMNT_NFSV3;
+ }
+
+#ifdef ULTRIX_HACK
+ nfs_args.optstr = mnt.mnt_opts;
+#endif /* ULTRIX_HACK */
+
+ nfs_args.hostname = host;
+ nfs_args.flags |= NFSMNT_HOSTNAME;
+#ifdef HOSTNAMESZ
+ /*
+ * Most kernels have a name length restriction.
+ */
+ if (strlen(host) >= HOSTNAMESZ)
+ strcpy(host + HOSTNAMESZ - 3, "..");
+#endif /* HOSTNAMESZ */
+
+ if (nfs_args.rsize = hasmntval(&mnt, "rsize"))
+ nfs_args.flags |= NFSMNT_RSIZE;
+
+ if (nfs_args.wsize = hasmntval(&mnt, "wsize"))
+ nfs_args.flags |= NFSMNT_WSIZE;
+
+ if (nfs_args.timeo = hasmntval(&mnt, "timeo"))
+ nfs_args.flags |= NFSMNT_TIMEO;
+
+ if (nfs_args.retrans = hasmntval(&mnt, "retrans"))
+ nfs_args.flags |= NFSMNT_RETRANS;
+
+/*
+ * How is it possible that 4.4BSD has NFSMNT_RESVPORT but amd
+ * doesn't know about it?!!
+ */
+#ifdef NFSMNT_RESVPORT
+ if (hasmntopt(&mnt, "resvport") != NULL)
+ nfs_args.flags |= NFSMNT_RESVPORT;
+#endif
+
+#ifdef NFSMNT_BIODS
+ if (nfs_args.biods = hasmntval(&mnt, "biods"))
+ nfs_args.flags |= NFSMNT_BIODS;
+
+#endif /* NFSMNT_BIODS */
+
+#ifdef NFSMNT_MAXGRPS
+ if (nfs_args.maxgrouplist = hasmntval(&mnt, "maxgroups"))
+ nfs_args.flags |= NFSMNT_MAXGRPS;
+#endif /* NFSMNT_MAXGRPS */
+
+#ifdef notdef
+/*
+ * This isn't supported by the ping algorithm yet.
+ * In any case, it is all done in nfs_init().
+ */
+ if (port = hasmntval(&mnt, "port"))
+ sin.sin_port = htons(port);
+ else
+ sin.sin_port = htons(NFS_PORT); /* XXX should use portmapper */
+#endif /* notdef */
+
+ if (hasmntopt(&mnt, MNTOPT_SOFT) != NULL)
+ nfs_args.flags |= NFSMNT_SOFT;
+
+#ifdef NFSMNT_SPONGY
+ if (hasmntopt(&mnt, "spongy") != NULL) {
+ nfs_args.flags |= NFSMNT_SPONGY;
+ if (nfs_args.flags & NFSMNT_SOFT) {
+ plog(XLOG_USER, "Mount opts soft and spongy are incompatible - soft ignored");
+ nfs_args.flags &= ~NFSMNT_SOFT;
+ }
+ }
+#endif /* MNTOPT_SPONGY */
+
+#ifdef MNTOPT_INTR
+ if (hasmntopt(&mnt, MNTOPT_INTR) != NULL)
+ nfs_args.flags |= NFSMNT_INT;
+#endif /* MNTOPT_INTR */
+
+#ifdef MNTOPT_NODEVS
+ if (hasmntopt(&mnt, MNTOPT_NODEVS) != NULL)
+ nfs_args.flags |= NFSMNT_NODEVS;
+#endif /* MNTOPT_NODEVS */
+
+#ifdef MNTOPT_COMPRESS
+ if (hasmntopt(&mnt, "compress") != NULL)
+ nfs_args.flags |= NFSMNT_COMPRESS;
+#endif /* MNTOPT_COMPRESS */
+
+#ifdef MNTOPT_NOCONN
+ if (hasmntopt(&mnt, "noconn") != NULL)
+ nfs_args.flags |= NFSMNT_NOCONN;
+#endif /* MNTOPT_NOCONN */
+
+#ifdef NFSMNT_PGTHRESH
+ if (nfs_args.pg_thresh = hasmntval(&mnt, "pgthresh"))
+ nfs_args.flags |= NFSMNT_PGTHRESH;
+#endif /* NFSMNT_PGTHRESH */
+
+ NFS_SA_DREF(nfs_args, fs->fs_ip);
+
+ flags = compute_mount_flags(&mnt);
+
+#ifdef NFSMNT_NOCTO
+ if (hasmntopt(&mnt, "nocto") != NULL)
+ nfs_args.flags |= NFSMNT_NOCTO;
+#endif /* NFSMNT_NOCTO */
+
+#ifdef HAS_TCP_NFS
+ if (hasmntopt(&mnt, "tcp") != NULL)
+ nfs_args.sotype = SOCK_STREAM;
+#endif /* HAS_TCP_NFS */
+
+
+#ifdef ULTRIX_HACK
+ /*
+ * Ultrix passes the flags argument as part of the
+ * mount data structure, rather than using the
+ * flags argument to the system call. This is
+ * confusing...
+ */
+ if (!(nfs_args.flags & NFSMNT_PGTHRESH)) {
+ nfs_args.pg_thresh = 64; /* 64k - XXX */
+ nfs_args.flags |= NFSMNT_PGTHRESH;
+ }
+ nfs_args.gfs_flags = flags;
+ flags &= M_RDONLY;
+ if (flags & M_RDONLY)
+ nfs_args.flags |= NFSMNT_RONLY;
+#endif /* ULTRIX_HACK */
+
+ error = mount_fs(&mnt, flags, (caddr_t) &nfs_args, retry, type);
+ free(xopts);
+ return error;
+}
+
+static int mount_nfs(dir, fs_name, opts, mf)
+char *dir;
+char *fs_name;
+char *opts;
+mntfs *mf;
+{
+#ifdef notdef
+ int error;
+ struct fhstatus fhs;
+ char *colon;
+
+ if (!(colon = strchr(fs_name, ':')))
+ return ENOENT;
+
+#ifdef DEBUG
+ dlog("locating fhandle for %s", fs_name);
+#endif /* DEBUG */
+ error = prime_nfs_fhandle_cache(colon+1, mf->mf_server, forcev2(mf), &fhs, (voidp) 0);
+
+ if (error)
+ return error;
+
+ return mount_nfs_fh(&fhs, dir, fs_name, opts, mf);
+#endif
+ if (!mf->mf_private) {
+ plog(XLOG_ERROR, "Missing filehandle for %s", fs_name);
+ return EINVAL;
+ }
+
+ return mount_nfs_fh((struct mountres *) mf->mf_private, dir, fs_name, opts, mf);
+}
+
+static int nfs_fmount(mf)
+mntfs *mf;
+{
+ int error;
+
+ error = mount_nfs(mf->mf_mount, mf->mf_info, mf->mf_mopts, mf);
+
+#ifdef DEBUG
+ if (error) {
+ errno = error;
+ dlog("mount_nfs: %m");
+ }
+#endif /* DEBUG */
+ return error;
+}
+
+static int nfs_fumount(mf)
+mntfs *mf;
+{
+ int error = UMOUNT_FS(mf->mf_mount);
+ if (error)
+ return error;
+
+ return 0;
+}
+
+static void nfs_umounted(mp)
+am_node *mp;
+{
+#ifdef INFORM_MOUNTD
+ /*
+ * Don't bother to inform remote mountd
+ * that we are finished. Until a full
+ * track of filehandles is maintained
+ * the mountd unmount callback cannot
+ * be done correctly anyway...
+ */
+
+ mntfs *mf = mp->am_mnt;
+ fserver *fs;
+ char *colon, *path;
+
+ if (mf->mf_error || mf->mf_refc > 1)
+ return;
+
+ fs = mf->mf_server;
+
+ /*
+ * Call the mount daemon on the server to
+ * announce that we are not using the fs any more.
+ *
+ * This is *wrong*. The mountd should be called
+ * when the fhandle is flushed from the cache, and
+ * a reference held to the cached entry while the
+ * fs is mounted...
+ */
+ colon = path = strchr(mf->mf_info, ':');
+ if (fs && colon) {
+ fh_cache f;
+#ifdef DEBUG
+ dlog("calling mountd for %s", mf->mf_info);
+#endif /* DEBUG */
+ *path++ = '\0';
+ f.fh_path = path;
+ f.fh_sin = *fs->fs_ip;
+ f.fh_sin.sin_port = (u_short) 0;
+ f.fh_fs = fs;
+ f.fh_id = 0;
+ f.fh_error = 0;
+ (void) prime_nfs_fhandle_cache(colon+1, mf->mf_server, (struct fhstatus *) 0, (voidp) mf);
+ (void) call_mountd(&f, MOUNTPROC_UMNT, MOUNTVERS, (fwd_fun) 0, (voidp) 0);
+ *colon = ':';
+ }
+#endif /* INFORM_MOUNTD */
+
+#ifdef KICK_KERNEL
+ /* This should go into the mainline code, not in nfs_ops... */
+
+ /*
+ * Run lstat over the underlying directory in
+ * case this was a direct mount. This will
+ * get the kernel back in sync with reality.
+ */
+ if (mp->am_parent && mp->am_parent->am_path &&
+ STREQ(mp->am_parent->am_mnt->mf_ops->fs_type, "direct")) {
+ struct stat stb;
+ int pid;
+ if ((pid = background()) == 0) {
+ if (lstat(mp->am_parent->am_path, &stb) < 0) {
+ plog(XLOG_ERROR, "lstat(%s) after unmount: %m", mp->am_parent->am_path);
+#ifdef DEBUG
+ } else {
+ dlog("hack lstat(%s): ok", mp->am_parent->am_path);
+#endif /* DEBUG */
+ }
+ _exit(0);
+ }
+ }
+#endif /* KICK_KERNEL */
+}
+
+/*
+ * Network file system
+ */
+am_ops nfs_ops = {
+ "nfs",
+ nfs_match,
+ nfs_init,
+ auto_fmount,
+ nfs_fmount,
+ auto_fumount,
+ nfs_fumount,
+ efs_lookuppn,
+ efs_readdir,
+ 0, /* nfs_readlink */
+ 0, /* nfs_mounted */
+ nfs_umounted,
+ find_nfs_srvr,
+ FS_MKMNT|FS_BACKGROUND|FS_AMQINFO
+};
+
+#endif /* HAS_NFS */
diff --git a/usr.sbin/amd/amd/nfs_start.c b/usr.sbin/amd/amd/nfs_start.c
new file mode 100644
index 0000000..eb4e953
--- /dev/null
+++ b/usr.sbin/amd/amd/nfs_start.c
@@ -0,0 +1,438 @@
+/*
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE 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[] = "@(#)nfs_start.c 8.1 (Berkeley) 6/6/93";
+#endif
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#include "am.h"
+#include "amq.h"
+#include <sys/signal.h>
+#include <setjmp.h>
+extern jmp_buf select_intr;
+extern int select_intr_valid;
+
+#ifdef HAS_TFS
+/*
+ * Use replacement for RPC/UDP transport
+ * so that we do NFS gatewaying.
+ */
+#define svcudp_create svcudp2_create
+extern SVCXPRT *svcudp2_create P((int));
+#endif /* HAS_TFS */
+
+extern void nfs_program_2();
+extern void amq_program_1();
+
+unsigned short nfs_port;
+SVCXPRT *nfsxprt;
+
+extern int fwd_sock;
+
+#define MASKED_SIGS (sigmask(SIGINT)|sigmask(SIGTERM)|sigmask(SIGCHLD)|sigmask(SIGHUP))
+
+#ifndef FD_SET
+#define FD_SETSIZE 32 /* XXX kludge. bind does it this way */
+#endif
+
+#ifdef DEBUG
+/*
+ * Check that we are not burning resources
+ */
+static void checkup(P_void)
+{
+
+static int max_fd = 0;
+static char *max_mem = 0;
+
+ int next_fd = dup(0);
+ extern caddr_t sbrk P((int));
+ caddr_t next_mem = sbrk(0);
+ close(next_fd);
+
+ /*if (max_fd < 0) {
+ max_fd = next_fd;
+ } else*/ if (max_fd < next_fd) {
+ dlog("%d new fds allocated; total is %d",
+ next_fd - max_fd, next_fd);
+ max_fd = next_fd;
+ }
+
+ /*if (max_mem == 0) {
+ max_mem = next_mem;
+ } else*/ if (max_mem < next_mem) {
+ dlog("%#x bytes of memory allocated; total is %#x (%d pages)",
+ next_mem - max_mem,
+ next_mem,
+ ((int)next_mem+getpagesize()-1)/getpagesize());
+ max_mem = next_mem;
+ }
+}
+#endif /* DEBUG */
+
+static int do_select(smask, fds, fdp, tvp)
+int smask;
+int fds;
+int *fdp;
+struct timeval *tvp;
+{
+ int sig;
+ int nsel;
+ if (sig = setjmp(select_intr)) {
+ select_intr_valid = 0;
+ /* Got a signal */
+ switch (sig) {
+ case SIGINT:
+ case SIGTERM:
+ amd_state = Finishing;
+ reschedule_timeout_mp();
+ break;
+ }
+ nsel = -1;
+ errno = EINTR;
+ } else {
+ select_intr_valid = 1;
+ /*
+ * Invalidate the current clock value
+ */
+ clock_valid = 0;
+ /*
+ * Allow interrupts. If a signal
+ * occurs, then it will cause a longjmp
+ * up above.
+ */
+ (void) sigsetmask(smask);
+ /*
+ * Wait for input
+ */
+ nsel = select(fds, fdp, (int *) 0, (int *) 0,
+ tvp->tv_sec ? tvp : (struct timeval *) 0);
+
+ }
+
+ (void) sigblock(MASKED_SIGS);
+
+ /*
+ * Perhaps reload the cache?
+ */
+ if (do_mapc_reload < clocktime()) {
+ mapc_reload();
+ do_mapc_reload = clocktime() + ONE_HOUR;
+ }
+ return nsel;
+}
+
+/*
+ * Determine whether anything is left in
+ * the RPC input queue.
+ */
+static int rpc_pending_now()
+{
+ struct timeval tvv;
+ int nsel;
+#ifdef FD_SET
+ fd_set readfds;
+
+ FD_ZERO(&readfds);
+ FD_SET(fwd_sock, &readfds);
+#else
+ int readfds = (1 << fwd_sock);
+#endif /* FD_SET */
+
+ tvv.tv_sec = tvv.tv_usec = 0;
+ nsel = select(FD_SETSIZE, &readfds, (int *) 0, (int *) 0, &tvv);
+ if (nsel < 1)
+ return(0);
+#ifdef FD_SET
+ if (FD_ISSET(fwd_sock, &readfds))
+ return(1);
+#else
+ if (readfds & (1 << fwd_sock))
+ return(1);
+#endif
+ return(0);
+}
+
+static serv_state run_rpc(P_void)
+{
+ int smask = sigblock(MASKED_SIGS);
+
+ next_softclock = clocktime();
+
+ amd_state = Run;
+
+ /*
+ * Keep on trucking while we are in Run mode. This state
+ * is switched to Quit after all the file systems have
+ * been unmounted.
+ */
+ while ((int)amd_state <= (int)Finishing) {
+ struct timeval tvv;
+ int nsel;
+ time_t now;
+#ifdef RPC_4
+ fd_set readfds;
+ readfds = svc_fdset;
+ FD_SET(fwd_sock, &readfds);
+#else
+#ifdef FD_SET
+ fd_set readfds;
+ FD_ZERO(&readfds);
+ readfds.fds_bits[0] = svc_fds;
+ FD_SET(fwd_sock, &readfds);
+#else
+ int readfds = svc_fds | (1 << fwd_sock);
+#endif /* FD_SET */
+#endif /* RPC_4 */
+
+#ifdef DEBUG
+ checkup();
+#endif /* DEBUG */
+
+ /*
+ * If the full timeout code is not called,
+ * then recompute the time delta manually.
+ */
+ now = clocktime();
+
+ if (next_softclock <= now) {
+ if (amd_state == Finishing)
+ umount_exported();
+ tvv.tv_sec = softclock();
+ } else {
+ tvv.tv_sec = next_softclock - now;
+ }
+ tvv.tv_usec = 0;
+
+ if (amd_state == Finishing && last_used_map < 0) {
+ flush_mntfs();
+ amd_state = Quit;
+ break;
+ }
+
+#ifdef DEBUG
+ if (tvv.tv_sec)
+ dlog("Select waits for %ds", tvv.tv_sec);
+ else
+ dlog("Select waits for Godot");
+#endif /* DEBUG */
+
+ nsel = do_select(smask, FD_SETSIZE, &readfds, &tvv);
+
+
+ switch (nsel) {
+ case -1:
+ if (errno == EINTR) {
+#ifdef DEBUG
+ dlog("select interrupted");
+#endif /* DEBUG */
+ continue;
+ }
+ warn("select");
+ break;
+
+ case 0:
+#ifdef DEBUG
+ /*dlog("select returned 0");*/
+#endif /* DEBUG */
+ break;
+
+ default:
+ /* Read all pending NFS responses at once to avoid
+ having responses queue up as a consequence of
+ retransmissions. */
+#ifdef FD_SET
+ if (FD_ISSET(fwd_sock, &readfds)) {
+ FD_CLR(fwd_sock, &readfds);
+#else
+ if (readfds & (1 << fwd_sock)) {
+ readfds &= ~(1 << fwd_sock);
+#endif
+ --nsel;
+ do {
+ fwd_reply();
+ } while (rpc_pending_now() > 0);
+ }
+
+ if (nsel) {
+ /*
+ * Anything left must be a normal
+ * RPC request.
+ */
+#ifdef RPC_4
+ svc_getreqset(&readfds);
+#else
+#ifdef FD_SET
+ svc_getreq(readfds.fds_bits[0]);
+#else
+ svc_getreq(readfds);
+#endif /* FD_SET */
+#endif /* RPC_4 */
+ }
+ break;
+ }
+ }
+
+ (void) sigsetmask(smask);
+
+ if (amd_state == Quit)
+ amd_state = Done;
+
+ return amd_state;
+}
+
+static int bindnfs_port(so)
+int so;
+{
+ unsigned short port;
+ int error = bind_resv_port(so, &port);
+ if (error == 0)
+ nfs_port = port;
+ return error;
+}
+
+void unregister_amq(P_void)
+{
+#ifdef DEBUG
+ Debug(D_AMQ)
+#endif /* DEBUG */
+ (void) pmap_unset(AMQ_PROGRAM, AMQ_VERSION);
+}
+
+int mount_automounter(ppid)
+int ppid;
+{
+ int so = socket(AF_INET, SOCK_DGRAM, 0);
+ int so2 = socket(AF_INET, SOCK_STREAM, 0);
+ SVCXPRT *amqp;
+ int nmount;
+
+ if (so < 0 || bindnfs_port(so) < 0) {
+ warn("can't create privileged nfs port");
+ return 1;
+ }
+
+ if (so2 < 0 || bind_resv_port(so2, NULL) < 0) {
+ warn("can't create privileged port");
+ return 1;
+ }
+ if ((nfsxprt = svcudp_create(so)) == NULL) {
+ plog(XLOG_FATAL, "cannot create rpc/udp service");
+ return 2;
+ }
+ if ((amqp = svctcp_create(so2, 0, 0)) == NULL) {
+ plog(XLOG_FATAL, "cannot create rpc/tcp service");
+ return 2;
+ }
+
+ if (!svc_register(nfsxprt, NFS_PROGRAM, NFS_VERSION, nfs_program_2, 0)) {
+ plog(XLOG_FATAL, "unable to register (NFS_PROGRAM, NFS_VERSION, 0)");
+ return 3;
+ }
+
+ /*
+ * Start RPC forwarding
+ */
+ if (fwd_init() != 0)
+ return 3;
+
+ /*
+ * Construct the root automount node
+ */
+ make_root_node();
+
+ /*
+ * Pick up the pieces from a previous run
+ * This is likely to (indirectly) need the rpc_fwd package
+ * so it *must* come after the call to fwd_init().
+ */
+ if (restart_existing_mounts)
+ restart();
+
+ /*
+ * Mount the top-level auto-mountpoints
+ */
+ nmount = mount_exported();
+
+ /*
+ * Now safe to tell parent that we are up and running
+ */
+ if (ppid)
+ kill(ppid, SIGQUIT);
+
+ if (nmount == 0) {
+ plog(XLOG_FATAL, "No work to do - quitting");
+ amd_state = Done;
+ return 0;
+ }
+
+#ifdef DEBUG
+ Debug(D_AMQ) {
+#endif /* DEBUG */
+ /*
+ * Register with amq
+ */
+ unregister_amq();
+
+ if (!svc_register(amqp, AMQ_PROGRAM, AMQ_VERSION, amq_program_1, IPPROTO_TCP)) {
+ plog(XLOG_FATAL, "unable to register (AMQ_PROGRAM, AMQ_VERSION, tcp)");
+ return 3;
+ }
+#ifdef DEBUG
+ }
+#endif /* DEBUG */
+
+ /*
+ * Start timeout_mp rolling
+ */
+ reschedule_timeout_mp();
+
+ /*
+ * Start the server
+ */
+ if (run_rpc() != Done) {
+ plog(XLOG_FATAL, "run_rpc failed");
+ amd_state = Done;
+ }
+
+ return 0;
+}
diff --git a/usr.sbin/amd/amd/nfs_subr.c b/usr.sbin/amd/amd/nfs_subr.c
new file mode 100644
index 0000000..07d8cff
--- /dev/null
+++ b/usr.sbin/amd/amd/nfs_subr.c
@@ -0,0 +1,562 @@
+/*
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)nfs_subr.c 8.1 (Berkeley) 6/6/93
+ *
+ * $Id$
+ *
+ */
+
+#include "am.h"
+
+/*
+ * Convert from UN*X to NFS error code
+ */
+#ifdef NFS_ERROR_MAPPING
+NFS_ERROR_MAPPING
+#define nfs_error(e) \
+ ((nfsstat)((e) > NFS_LOMAP && (e) < NFS_HIMAP ? \
+ nfs_errormap[(e) - NFS_LOMAP] : (e)))
+#else
+#define nfs_error(e) ((nfsstat)(e))
+#endif /* NFS_ERROR_MAPPING */
+
+static char *do_readlink P((am_node *mp, int *error_return, struct attrstat **attrpp));
+static char *do_readlink(mp, error_return, attrpp)
+am_node *mp;
+int *error_return;
+struct attrstat **attrpp;
+{
+ char *ln;
+
+ /*
+ * If there is a readlink method, then use
+ * that, otherwise if a link exists use
+ * that, otherwise use the mount point.
+ */
+ if (mp->am_mnt->mf_ops->readlink) {
+ int retry = 0;
+ mp = (*mp->am_mnt->mf_ops->readlink)(mp, &retry);
+ if (mp == 0) {
+ *error_return = retry;
+ return 0;
+ }
+ /*reschedule_timeout_mp();*/
+ }
+ if (mp->am_link) {
+ ln = mp->am_link;
+ } else {
+ ln = mp->am_mnt->mf_mount;
+ }
+ if (attrpp)
+ *attrpp = &mp->am_attr;
+ return ln;
+}
+
+/*ARGSUSED*/
+voidp
+nfsproc_null_2(argp, rqstp)
+voidp argp;
+struct svc_req *rqstp;
+{
+ static char res;
+
+ return (voidp) &res;
+}
+
+
+/*ARGSUSED*/
+struct attrstat *
+nfsproc_getattr_2(argp, rqstp)
+struct nfs_fh *argp;
+struct svc_req *rqstp;
+{
+ static struct attrstat res;
+ am_node *mp;
+ int retry;
+
+#ifdef DEBUG
+ Debug(D_TRACE)
+ plog(XLOG_DEBUG, "gettattr:");
+#endif /* DEBUG */
+
+ mp = fh_to_mp2(argp, &retry);
+ if (mp == 0) {
+#ifdef PRECISE_SYMLINKS
+getattr_retry:
+#endif /* PRECISE_SYMLINKS */
+
+ if (retry < 0)
+ return 0;
+ res.status = nfs_error(retry);
+ } else {
+ struct attrstat *attrp = &mp->am_attr;
+#ifdef PRECISE_SYMLINKS
+ if (mp->am_fattr.type == NFLNK) {
+ /*
+ * Make sure we can read the link,
+ * and then determine the length.
+ */
+ char *ln = do_readlink(mp, &retry, &attrp);
+ if (ln == 0)
+ goto getattr_retry;
+ }
+#endif /* PRECISE_SYMLINKS */
+#ifdef DEBUG
+ Debug(D_TRACE)
+ plog(XLOG_DEBUG, "\tstat(%s), size = %d", mp->am_path, attrp->attrstat_u.attributes.size);
+#endif /* DEBUG */
+ mp->am_stats.s_getattr++;
+ return attrp;
+ }
+#if defined(HAVE_SYMLINK_CACHE) && !defined(NFSMNT_SYMTTL)
+ /*
+ * This code is needed to defeat Solaris 2.4's symlink values
+ * cache. It is not needed if the O/S has an nfs flag to
+ * turn off the symlink-cache at mount time (such as Irix
+ * 5.2 and 5.3). -Erez.
+ */
+ if (++res.attrstat_u.attributes.mtime.useconds == 0)
+ ++res.attrstat_u.attributes.mtime.seconds;
+#endif /* HAVE_SYMLINK_CACHE && !NFSMNT_SYMTTL */
+
+ return &res;
+}
+
+
+/*ARGSUSED*/
+struct attrstat *
+nfsproc_setattr_2(argp, rqstp)
+struct sattrargs *argp;
+struct svc_req *rqstp;
+{
+ static struct attrstat res;
+
+ if (!fh_to_mp(&argp->file))
+ res.status = nfs_error(ESTALE);
+ else
+ res.status = nfs_error(EROFS);
+
+ return &res;
+}
+
+
+/*ARGSUSED*/
+voidp
+nfsproc_root_2(argp, rqstp)
+voidp argp;
+struct svc_req *rqstp;
+{
+ static char res;
+
+ return (voidp)&res;
+}
+
+
+/*ARGSUSED*/
+struct diropres *
+nfsproc_lookup_2(argp, rqstp)
+struct diropargs *argp;
+struct svc_req *rqstp;
+{
+ static struct diropres res;
+ am_node *mp;
+ int retry;
+
+#ifdef DEBUG
+ Debug(D_TRACE)
+ plog(XLOG_DEBUG, "lookup:");
+#endif /* DEBUG */
+
+ mp = fh_to_mp2(&argp->dir, &retry);
+ if (mp == 0) {
+ if (retry < 0)
+ return 0;
+ res.status = nfs_error(retry);
+ } else {
+ int error;
+ am_node *ap;
+#ifdef DEBUG
+ Debug(D_TRACE)
+ plog(XLOG_DEBUG, "\tlookuppn(%s, %s)", mp->am_path, argp->name);
+#endif /* DEBUG */
+ ap = (*mp->am_mnt->mf_ops->lookuppn)(mp, argp->name, &error, VLOOK_CREATE);
+ if (ap == 0) {
+ if (error < 0) {
+#ifdef DEBUG
+ dlog("Not sending RPC reply");
+#endif /* DEBUG */
+ amd_stats.d_drops++;
+ return 0;
+ }
+ res.status = nfs_error(error);
+ } else {
+ mp_to_fh(ap, &res.diropres_u.diropres.file);
+ res.diropres_u.diropres.attributes = ap->am_fattr;
+ res.status = NFS_OK;
+ }
+ mp->am_stats.s_lookup++;
+ /*reschedule_timeout_mp();*/
+ }
+
+ return &res;
+}
+
+
+/*ARGSUSED*/
+struct readlinkres *
+nfsproc_readlink_2(argp, rqstp)
+struct nfs_fh *argp;
+struct svc_req *rqstp;
+{
+ static struct readlinkres res;
+ am_node *mp;
+ int retry;
+
+#ifdef DEBUG
+ Debug(D_TRACE)
+ plog(XLOG_DEBUG, "readlink:");
+#endif /* DEBUG */
+
+ mp = fh_to_mp2(argp, &retry);
+ if (mp == 0) {
+readlink_retry:
+ if (retry < 0)
+ return 0;
+ res.status = nfs_error(retry);
+ } else {
+ char *ln = do_readlink(mp, &retry, (struct attrstat **) 0);
+ if (ln == 0)
+ goto readlink_retry;
+ res.status = NFS_OK;
+#ifdef DEBUG
+ Debug(D_TRACE)
+ if (ln)
+ plog(XLOG_DEBUG, "\treadlink(%s) = %s", mp->am_path, ln);
+#endif /* DEBUG */
+ res.readlinkres_u.data = ln;
+ mp->am_stats.s_readlink++;
+ }
+
+ return &res;
+}
+
+
+/*ARGSUSED*/
+struct readres *
+nfsproc_read_2(argp, rqstp)
+struct readargs *argp;
+struct svc_req *rqstp;
+{
+ static struct readres res;
+
+ bzero((char *)&res, sizeof(res));
+
+ res.status = nfs_error(EACCES);
+
+ return &res;
+}
+
+
+/*ARGSUSED*/
+voidp
+nfsproc_writecache_2(argp, rqstp)
+voidp argp;
+struct svc_req *rqstp;
+{
+ static char res;
+
+ return (voidp) &res;
+}
+
+
+/*ARGSUSED*/
+struct attrstat *
+nfsproc_write_2(argp, rqstp)
+writeargs *argp;
+struct svc_req *rqstp;
+{
+ static struct attrstat res;
+
+ if (!fh_to_mp(&argp->file))
+ res.status = nfs_error(ESTALE);
+ else
+ res.status = nfs_error(EROFS);
+
+ return &res;
+}
+
+
+/*ARGSUSED*/
+struct diropres *
+nfsproc_create_2(argp, rqstp)
+createargs *argp;
+struct svc_req *rqstp;
+{
+ static struct diropres res;
+
+ if (!fh_to_mp(&argp->where.dir))
+ res.status = nfs_error(ESTALE);
+ else
+ res.status = nfs_error(EROFS);
+
+ return &res;
+}
+
+
+/*ARGSUSED*/
+static nfsstat *
+unlink_or_rmdir(argp, rqstp, unlinkp)
+struct diropargs *argp;
+struct svc_req *rqstp;
+int unlinkp;
+{
+ static nfsstat res;
+ int retry;
+ /*mntfs *mf;*/
+ am_node *mp = fh_to_mp3(&argp->dir, &retry, VLOOK_DELETE);
+ if (mp == 0) {
+ if (retry < 0)
+ return 0;
+ res = nfs_error(retry);
+ goto out;
+ }
+ /*mf = mp->am_mnt;*/
+ if (mp->am_fattr.type != NFDIR) {
+ res = nfs_error(ENOTDIR);
+ goto out;
+ }
+#ifdef DEBUG
+ Debug(D_TRACE)
+ plog(XLOG_DEBUG, "\tremove(%s, %s)", mp->am_path, argp->name);
+#endif /* DEBUG */
+ mp = (*mp->am_mnt->mf_ops->lookuppn)(mp, argp->name, &retry, VLOOK_DELETE);
+ if (mp == 0) {
+ /*
+ * Ignore retries...
+ */
+ if (retry < 0)
+ retry = 0;
+ /*
+ * Usual NFS workaround...
+ */
+ else if (retry == ENOENT)
+ retry = 0;
+ res = nfs_error(retry);
+ } else {
+ forcibly_timeout_mp(mp);
+ res = NFS_OK;
+ }
+
+out:
+ return &res;
+}
+
+
+/*ARGSUSED*/
+nfsstat *
+nfsproc_remove_2(argp, rqstp)
+struct diropargs *argp;
+struct svc_req *rqstp;
+{
+ return unlink_or_rmdir(argp, rqstp, TRUE);
+}
+
+/*ARGSUSED*/
+nfsstat *
+nfsproc_rename_2(argp, rqstp)
+renameargs *argp;
+struct svc_req *rqstp;
+{
+ static nfsstat res;
+ if (!fh_to_mp(&argp->from.dir) || !fh_to_mp(&argp->to.dir))
+ res = nfs_error(ESTALE);
+ /*
+ * If the kernel is doing clever things with referenced files
+ * then let it pretend...
+ */
+ else if (strncmp(argp->to.name, ".nfs", 4) == 0)
+ res = NFS_OK;
+ /*
+ * otherwise a failure
+ */
+ else
+ res = nfs_error(EROFS);
+ return &res;
+}
+
+
+/*ARGSUSED*/
+nfsstat *
+nfsproc_link_2(argp, rqstp)
+linkargs *argp;
+struct svc_req *rqstp;
+{
+ static nfsstat res;
+ if (!fh_to_mp(&argp->from) || !fh_to_mp(&argp->to.dir))
+ res = nfs_error(ESTALE);
+ else
+ res = nfs_error(EROFS);
+
+ return &res;
+}
+
+
+/*ARGSUSED*/
+nfsstat *
+nfsproc_symlink_2(argp, rqstp)
+symlinkargs *argp;
+struct svc_req *rqstp;
+{
+ static nfsstat res;
+ if (!fh_to_mp(&argp->from.dir))
+ res = nfs_error(ESTALE);
+ else
+ res = nfs_error(EROFS);
+
+ return &res;
+}
+
+
+/*ARGSUSED*/
+struct diropres *
+nfsproc_mkdir_2(argp, rqstp)
+createargs *argp;
+struct svc_req *rqstp;
+{
+ static struct diropres res;
+ if (!fh_to_mp(&argp->where.dir))
+ res.status = nfs_error(ESTALE);
+ else
+ res.status = nfs_error(EROFS);
+
+ return &res;
+}
+
+
+/*ARGSUSED*/
+nfsstat *
+nfsproc_rmdir_2(argp, rqstp)
+struct diropargs *argp;
+struct svc_req *rqstp;
+{
+ return unlink_or_rmdir(argp, rqstp, FALSE);
+}
+
+
+/*ARGSUSED*/
+struct readdirres *
+nfsproc_readdir_2(argp, rqstp)
+readdirargs *argp;
+struct svc_req *rqstp;
+{
+ static readdirres res;
+ static entry e_res[MAX_READDIR_ENTRIES];
+ am_node *mp;
+ int retry;
+
+#ifdef DEBUG
+ Debug(D_TRACE)
+ plog(XLOG_DEBUG, "readdir:");
+#endif /* DEBUG */
+
+ mp = fh_to_mp2(&argp->dir, &retry);
+ if (mp == 0) {
+ if (retry < 0)
+ return 0;
+ res.status = nfs_error(retry);
+ } else {
+#ifdef DEBUG
+ Debug(D_TRACE)
+ plog(XLOG_DEBUG, "\treaddir(%s)", mp->am_path);
+#endif /* DEBUG */
+ res.status = nfs_error((*mp->am_mnt->mf_ops->readdir)(mp, argp->cookie,
+ &res.readdirres_u.reply, e_res, argp->count));
+ mp->am_stats.s_readdir++;
+ }
+
+ return &res;
+}
+
+/*ARGSUSED*/
+struct statfsres *
+nfsproc_statfs_2(argp, rqstp)
+struct nfs_fh *argp;
+struct svc_req *rqstp;
+{
+ static statfsres res;
+ am_node *mp;
+ int retry;
+
+#ifdef DEBUG
+ Debug(D_TRACE)
+ plog(XLOG_DEBUG, "statfs:");
+#endif /* DEBUG */
+
+ mp = fh_to_mp2(argp, &retry);
+ if (mp == 0) {
+ if (retry < 0)
+ return 0;
+ res.status = nfs_error(retry);
+ } else {
+ statfsokres *fp;
+#ifdef DEBUG
+ Debug(D_TRACE)
+ plog(XLOG_DEBUG, "\tstat_fs(%s)", mp->am_path);
+#endif /* DEBUG */
+ /*
+ * just return faked up file system information
+ */
+
+ fp = &res.statfsres_u.reply;
+
+ fp->tsize = 1024;
+ fp->bsize = 4096;
+#ifdef HAS_EMPTY_AUTOMOUNTS
+ fp->blocks = 0;
+#else
+ fp->blocks = 1;
+#endif
+ fp->bfree = 0;
+ fp->bavail = 0;
+
+ res.status = NFS_OK;
+ mp->am_stats.s_statfs++;
+ }
+
+ return &res;
+}
diff --git a/usr.sbin/amd/amd/nfsx_ops.c b/usr.sbin/amd/amd/nfsx_ops.c
new file mode 100644
index 0000000..b96c0ea
--- /dev/null
+++ b/usr.sbin/amd/amd/nfsx_ops.c
@@ -0,0 +1,516 @@
+/*
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)nfsx_ops.c 8.1 (Berkeley) 6/6/93
+ *
+ * $Id$
+ *
+ */
+
+#include "am.h"
+
+#ifdef HAS_NFSX
+
+/*
+ * NFS hierarchical mounts
+ *
+ * TODO: Re-implement.
+ */
+
+/*
+ * The rfs field contains a list of mounts to be done from
+ * the remote host.
+ */
+typedef struct nfsx_mnt {
+ mntfs *n_mnt;
+ int n_error;
+} nfsx_mnt;
+
+struct nfsx {
+ int nx_c; /* Number of elements in nx_v */
+ nfsx_mnt *nx_v; /* Underlying mounts */
+ nfsx_mnt *nx_try;
+};
+
+static int nfsx_fmount P((mntfs*));
+
+static char *nfsx_match(fo)
+am_opts *fo;
+{
+ char *xmtab;
+ char *ptr;
+ int len;
+
+ if (!fo->opt_rfs) {
+ plog(XLOG_USER, "nfsx: no remote filesystem specified");
+ return FALSE;
+ }
+ if (!fo->opt_rhost) {
+ plog(XLOG_USER, "nfsx: no remote host specified");
+ return FALSE;
+ }
+
+#ifdef notdef
+ /* fiddle sublink, must be last... */
+ if (fo->opt_sublink) {
+ plog(XLOG_WARNING, "nfsx: sublink %s ignored", fo->opt_sublink);
+ free((voidp) fo->opt_sublink);
+ fo->opt_sublink = 0;
+ }
+#endif
+
+ /* set default sublink */
+ if (fo->opt_sublink == 0) {
+ ptr = strchr(fo->opt_rfs, ',');
+ if (ptr && ptr != (fo->opt_rfs + 1))
+ fo->opt_sublink = strnsave(fo->opt_rfs + 1, ptr - fo->opt_rfs - 1);
+ }
+
+ /*
+ * Remove trailing ",..." from ${fs}
+ * After deslashifying, overwrite the end of ${fs} with "/"
+ * to make sure it is unique.
+ */
+ if (ptr = strchr(fo->opt_fs, ','))
+ *ptr = '\0';
+ deslashify(fo->opt_fs);
+ /*
+ * Bump string length to allow trailing /
+ */
+ len = strlen(fo->opt_fs);
+ fo->opt_fs = xrealloc(fo->opt_fs, len + 1 + 1);
+ ptr = fo->opt_fs + len;
+ /*
+ * Make unique...
+ */
+ *ptr++ = '/';
+ *ptr = '\0';
+
+ /*
+ * Determine magic cookie to put in mtab
+ */
+ xmtab = str3cat((char *) 0, fo->opt_rhost, ":", fo->opt_rfs);
+#ifdef DEBUG
+ dlog("NFS: mounting remote server \"%s\", remote fs \"%s\" on \"%s\"",
+ fo->opt_rhost, fo->opt_rfs, fo->opt_fs);
+#endif /* DEBUG */
+
+ return xmtab;
+}
+
+static void nfsx_prfree P((voidp vp));
+static void nfsx_prfree(vp)
+voidp vp;
+{
+ struct nfsx *nx = (struct nfsx *) vp;
+ int i;
+
+ for (i = 0; i < nx->nx_c; i++) {
+ mntfs *m = nx->nx_v[i].n_mnt;
+ if (m)
+ free_mntfs(m);
+ }
+
+ free((voidp) nx->nx_v);
+ free((voidp) nx);
+}
+
+static int nfsx_init(mf)
+mntfs *mf;
+{
+ /*
+ * mf_info has the form:
+ * host:/prefix/path,sub,sub,sub
+ */
+ int i;
+ int glob_error;
+ struct nfsx *nx;
+ int asked_for_wakeup = 0;
+
+ nx = (struct nfsx *) mf->mf_private;
+
+ if (nx == 0) {
+ char **ivec;
+ char *info = 0;
+ char *host;
+ char *pref;
+ int error = 0;
+
+ info = strdup(mf->mf_info);
+ host = strchr(info, ':');
+ if (!host) {
+ error = EINVAL;
+ goto errexit;
+ }
+
+ pref = host+1;
+ host = info;
+
+ /*
+ * Split the prefix off from the suffices
+ */
+ ivec = strsplit(pref, ',', '\'');
+
+ /*
+ * Count array size
+ */
+ for (i = 0; ivec[i]; i++)
+ ;
+
+ nx = ALLOC(nfsx);
+ mf->mf_private = (voidp) nx;
+ mf->mf_prfree = nfsx_prfree;
+
+ nx->nx_c = i - 1; /* i-1 because we don't want the prefix */
+ nx->nx_v = (nfsx_mnt *) xmalloc(nx->nx_c * sizeof(nfsx_mnt));
+ { char *mp = 0;
+ char *xinfo = 0;
+ char *fs = mf->mf_fo->opt_fs;
+ char *rfs = 0;
+ for (i = 0; i < nx->nx_c; i++) {
+ char *path = ivec[i+1];
+ rfs = str3cat(rfs, pref, "/", path);
+ /*
+ * Determine the mount point.
+ * If this is the root, then don't remove
+ * the trailing slash to avoid mntfs name clashes.
+ */
+ mp = str3cat(mp, fs, "/", rfs);
+ normalize_slash(mp);
+ deslashify(mp);
+ /*
+ * Determine the mount info
+ */
+ xinfo = str3cat(xinfo, host, *path == '/' ? "" : "/", path);
+ normalize_slash(xinfo);
+ if (pref[1] != '\0')
+ deslashify(xinfo);
+#ifdef DEBUG
+ dlog("nfsx: init mount for %s on %s", xinfo, mp);
+#endif
+ nx->nx_v[i].n_error = -1;
+ nx->nx_v[i].n_mnt = find_mntfs(&nfs_ops, mf->mf_fo, mp, xinfo, "", mf->mf_mopts, mf->mf_remopts);
+ }
+ if (rfs) free(rfs);
+ if (mp) free(mp);
+ if (xinfo) free(xinfo);
+ }
+
+ free((voidp) ivec);
+errexit:
+ if (info)
+ free(info);
+ if (error)
+ return error;
+ }
+
+ /*
+ * Iterate through the mntfs's and call
+ * the underlying init routine on each
+ */
+ glob_error = 0;
+ for (i = 0; i < nx->nx_c; i++) {
+ nfsx_mnt *n = &nx->nx_v[i];
+ mntfs *m = n->n_mnt;
+ int error = (*m->mf_ops->fs_init)(m);
+ /*
+ * If HARD_NFSX_ERRORS is defined, make any
+ * initialisation failure a hard error and
+ * fail the entire group. Otherwise only fail
+ * if none of the group is mountable (see nfsx_fmount).
+ */
+#ifdef HARD_NFSX_ERRORS
+ if (error > 0)
+ return error;
+#else
+ if (error > 0)
+ n->n_error = error;
+#endif
+ else if (error < 0) {
+ glob_error = -1;
+ if (!asked_for_wakeup) {
+ asked_for_wakeup = 1;
+ sched_task(wakeup_task, (voidp) mf, (voidp) m);
+ }
+ }
+ }
+
+ return glob_error;
+}
+
+static void nfsx_cont P((int rc, int term, voidp closure));
+static void nfsx_cont(rc, term, closure)
+int rc;
+int term;
+voidp closure;
+{
+ mntfs *mf = (mntfs *) closure;
+ struct nfsx *nx = (struct nfsx *) mf->mf_private;
+ nfsx_mnt *n = nx->nx_try;
+
+ n->n_mnt->mf_flags &= ~(MFF_ERROR|MFF_MOUNTING);
+ mf->mf_flags &= ~MFF_ERROR;
+
+ /*
+ * Wakeup anything waiting for this mount
+ */
+ wakeup((voidp) n->n_mnt);
+
+ if (rc || term) {
+ if (term) {
+ /*
+ * Not sure what to do for an error code.
+ */
+ plog(XLOG_ERROR, "mount for %s got signal %d", n->n_mnt->mf_mount, term);
+ n->n_error = EIO;
+ } else {
+ /*
+ * Check for exit status
+ */
+ errno = rc; /* XXX */
+ plog(XLOG_ERROR, "%s: mount (nfsx_cont): %m", n->n_mnt->mf_mount);
+ n->n_error = rc;
+ }
+ free_mntfs(n->n_mnt);
+ n->n_mnt = new_mntfs();
+ n->n_mnt->mf_error = n->n_error;
+ n->n_mnt->mf_flags |= MFF_ERROR;
+ } else {
+ /*
+ * The mount worked.
+ */
+ mf_mounted(n->n_mnt);
+ n->n_error = 0;
+ }
+
+ /*
+ * Do the remaining bits
+ */
+ if (nfsx_fmount(mf) >= 0) {
+ wakeup((voidp) mf);
+ mf->mf_flags &= ~MFF_MOUNTING;
+ mf_mounted(mf);
+ }
+}
+
+static int try_nfsx_mount P((voidp mv));
+static int try_nfsx_mount(mv)
+voidp mv;
+{
+ mntfs *mf = (mntfs *) mv;
+ int error;
+
+ mf->mf_flags |= MFF_MOUNTING;
+ error = (*mf->mf_ops->fmount_fs)(mf);
+ mf->mf_flags &= ~MFF_MOUNTING;
+ return error;
+}
+
+static int nfsx_remount P((mntfs *mf, int fg));
+static int nfsx_remount(mf, fg)
+mntfs *mf;
+int fg;
+{
+ struct nfsx *nx = (struct nfsx *) mf->mf_private;
+ nfsx_mnt *n;
+ int glob_error = -1;
+
+ for (n = nx->nx_v; n < nx->nx_v + nx->nx_c; n++) {
+ mntfs *m = n->n_mnt;
+ if (n->n_error < 0) {
+ if (!(m->mf_flags & MFF_MKMNT) && m->mf_ops->fs_flags & FS_MKMNT) {
+ int error = mkdirs(m->mf_mount, 0555);
+ if (!error)
+ m->mf_flags |= MFF_MKMNT;
+ }
+ }
+ }
+
+ /*
+ * Iterate through the mntfs's and mount each filesystem
+ * which is not yet mounted.
+ */
+ for (n = nx->nx_v; n < nx->nx_v + nx->nx_c; n++) {
+ mntfs *m = n->n_mnt;
+ if (n->n_error < 0) {
+ /*
+ * Check fmount entry pt. exists
+ * and then mount...
+ */
+ if (!m->mf_ops->fmount_fs) {
+ n->n_error = EINVAL;
+ } else {
+#ifdef DEBUG
+ dlog("calling underlying fmount on %s", m->mf_mount);
+#endif
+ if (!fg && foreground && (m->mf_ops->fs_flags & FS_MBACKGROUND)) {
+ m->mf_flags |= MFF_MOUNTING; /* XXX */
+#ifdef DEBUG
+ dlog("backgrounding mount of \"%s\"", m->mf_info);
+#endif
+ nx->nx_try = n;
+ run_task(try_nfsx_mount, (voidp) m, nfsx_cont, (voidp) mf);
+ n->n_error = -1;
+ return -1;
+ } else {
+#ifdef DEBUG
+ dlog("foreground mount of \"%s\" ...", mf->mf_info);
+#endif
+ n->n_error = (*m->mf_ops->fmount_fs)(m);
+ }
+ }
+#ifdef DEBUG
+ if (n->n_error > 0) {
+ errno = n->n_error; /* XXX */
+ dlog("underlying fmount of %s failed: %m", m->mf_mount);
+ }
+#endif
+ if (n->n_error == 0) {
+ glob_error = 0;
+ } else if (glob_error < 0) {
+ glob_error = n->n_error;
+ }
+ }
+ }
+
+ return glob_error < 0 ? 0 : glob_error;
+}
+
+static int nfsx_fmount P((mntfs *mf));
+static int nfsx_fmount(mf)
+mntfs *mf;
+{
+ return nfsx_remount(mf, FALSE);
+}
+
+/*
+ * Unmount an NFS hierarchy.
+ * Note that this is called in the foreground
+ * and so may hang under extremely rare conditions.
+ */
+static int nfsx_fumount(mf)
+mntfs *mf;
+{
+ struct nfsx *nx = (struct nfsx *) mf->mf_private;
+ nfsx_mnt *n;
+ int glob_error = 0;
+
+ /*
+ * Iterate in reverse through the mntfs's and unmount each filesystem
+ * which is mounted.
+ */
+ for (n = nx->nx_v + nx->nx_c - 1; n >= nx->nx_v; --n) {
+ mntfs *m = n->n_mnt;
+ /*
+ * If this node has not been messed with
+ * and there has been no error so far
+ * then try and unmount.
+ * If an error had occured then zero
+ * the error code so that the remount
+ * only tries to unmount those nodes
+ * which had been successfully unmounted.
+ */
+ if (n->n_error == 0) {
+#ifdef DEBUG
+ dlog("calling underlying fumount on %s", m->mf_mount);
+#endif
+ n->n_error = (*m->mf_ops->fumount_fs)(m);
+ if (n->n_error) {
+ glob_error = n->n_error;
+ n->n_error = 0;
+ } else {
+ /*
+ * Make sure remount gets this node
+ */
+ n->n_error = -1;
+ }
+ }
+ }
+
+ /*
+ * If any unmounts failed then remount the
+ * whole lot...
+ */
+ if (glob_error) {
+ glob_error = nfsx_remount(mf, TRUE);
+ if (glob_error) {
+ errno = glob_error; /* XXX */
+ plog(XLOG_USER, "nfsx: remount of %s failed: %m", mf->mf_mount);
+ }
+ glob_error = EBUSY;
+ } else {
+ /*
+ * Remove all the mount points
+ */
+ for (n = nx->nx_v; n < nx->nx_v + nx->nx_c; n++) {
+ mntfs *m = n->n_mnt;
+ if (n->n_error < 0) {
+ if (m->mf_ops->fs_flags & FS_MKMNT) {
+ (void) rmdirs(m->mf_mount);
+ m->mf_flags &= ~MFF_MKMNT;
+ }
+ }
+ free_mntfs(m);
+ n->n_mnt = 0;
+ n->n_error = -1;
+ }
+ }
+
+ return glob_error;
+}
+
+/*
+ * Ops structure
+ */
+am_ops nfsx_ops = {
+ "nfsx",
+ nfsx_match,
+ nfsx_init,
+ auto_fmount,
+ nfsx_fmount,
+ auto_fumount,
+ nfsx_fumount,
+ efs_lookuppn,
+ efs_readdir,
+ 0, /* nfsx_readlink */
+ 0, /* nfsx_mounted */
+ 0, /* nfsx_umounted */
+ find_nfs_srvr, /* XXX */
+ /*FS_UBACKGROUND|*/FS_AMQINFO
+};
+
+#endif /* HAS_NFSX */
diff --git a/usr.sbin/amd/amd/opts.c b/usr.sbin/amd/amd/opts.c
new file mode 100644
index 0000000..7fc57ad
--- /dev/null
+++ b/usr.sbin/amd/amd/opts.c
@@ -0,0 +1,835 @@
+/*-
+ * Copyright (c) 1989 Jan-Simon Pendry
+ * Copyright (c) 1989 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id$
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)opts.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "am.h"
+
+extern char *getenv P((const char *));
+
+/*
+ * static copy of the options with
+ * which to play
+ */
+static struct am_opts fs_static;
+
+static char *opt_host = hostname;
+static char *opt_hostd = hostd;
+static char nullstr[] = "";
+static char *opt_key = nullstr;
+static char *opt_map = nullstr;
+static char *opt_path = nullstr;
+
+static char *vars[8];
+
+/*
+ * Length of longest option name
+ */
+#define NLEN 16 /* conservative */
+#define S(x) (x) , (sizeof(x)-1)
+static struct opt {
+ char *name; /* Name of the option */
+ int nlen; /* Length of option name */
+ char **optp; /* Pointer to option value string */
+ char **sel_p; /* Pointer to selector value string */
+} opt_fields[] = {
+ /* Options in something corresponding to frequency of use */
+ { S("opts"), &fs_static.opt_opts, 0 },
+ { S("host"), 0, &opt_host },
+ { S("hostd"), 0, &opt_hostd },
+ { S("type"), &fs_static.opt_type, 0 },
+ { S("rhost"), &fs_static.opt_rhost, 0 },
+ { S("rfs"), &fs_static.opt_rfs, 0 },
+ { S("fs"), &fs_static.opt_fs, 0 },
+ { S("key"), 0, &opt_key },
+ { S("map"), 0, &opt_map },
+ { S("sublink"), &fs_static.opt_sublink, 0 },
+ { S("arch"), 0, &arch },
+ { S("dev"), &fs_static.opt_dev, 0 },
+ { S("pref"), &fs_static.opt_pref, 0 },
+ { S("path"), 0, &opt_path },
+ { S("autodir"), 0, &auto_dir },
+ { S("delay"), &fs_static.opt_delay, 0 },
+ { S("domain"), 0, &hostdomain },
+ { S("karch"), 0, &karch },
+ { S("cluster"), 0, &cluster },
+ { S("wire"), 0, &wire },
+ { S("byte"), 0, &endian },
+ { S("os"), 0, &op_sys },
+ { S("remopts"), &fs_static.opt_remopts, 0 },
+ { S("mount"), &fs_static.opt_mount, 0 },
+ { S("unmount"), &fs_static.opt_unmount, 0 },
+ { S("cache"), &fs_static.opt_cache, 0 },
+ { S("user"), &fs_static.opt_user, 0 },
+ { S("group"), &fs_static.opt_group, 0 },
+ { S("var0"), &vars[0], 0 },
+ { S("var1"), &vars[1], 0 },
+ { S("var2"), &vars[2], 0 },
+ { S("var3"), &vars[3], 0 },
+ { S("var4"), &vars[4], 0 },
+ { S("var5"), &vars[5], 0 },
+ { S("var6"), &vars[6], 0 },
+ { S("var7"), &vars[7], 0 },
+ { 0, 0, 0, 0 },
+};
+
+typedef struct opt_apply opt_apply;
+struct opt_apply {
+ char **opt;
+ char *val;
+};
+
+/*
+ * Specially expand the remote host name first
+ */
+static opt_apply rhost_expansion[] = {
+ { &fs_static.opt_rhost, "${host}" },
+ { 0, 0 },
+};
+/*
+ * List of options which need to be expanded
+ * Note that this the order here _may_ be important.
+ */
+static opt_apply expansions[] = {
+/* { &fs_static.opt_dir, 0 }, */
+ { &fs_static.opt_sublink, 0 },
+ { &fs_static.opt_rfs, "${path}" },
+ { &fs_static.opt_fs, "${autodir}/${rhost}${rfs}" },
+ { &fs_static.opt_opts, "rw" },
+ { &fs_static.opt_remopts, "${opts}" },
+ { &fs_static.opt_mount, 0 },
+ { &fs_static.opt_unmount, 0 },
+ { 0, 0 },
+};
+
+/*
+ * List of options which need to be free'ed before re-use
+ */
+static opt_apply to_free[] = {
+ { &fs_static.fs_glob, 0 },
+ { &fs_static.fs_local, 0 },
+ { &fs_static.fs_mtab, 0 },
+/* { &fs_static.opt_dir, 0 }, */
+ { &fs_static.opt_sublink, 0 },
+ { &fs_static.opt_rfs, 0 },
+ { &fs_static.opt_fs, 0 },
+ { &fs_static.opt_rhost, 0 },
+ { &fs_static.opt_opts, 0 },
+ { &fs_static.opt_remopts, 0 },
+ { &fs_static.opt_mount, 0 },
+ { &fs_static.opt_unmount, 0 },
+ { &vars[0], 0 },
+ { &vars[1], 0 },
+ { &vars[2], 0 },
+ { &vars[3], 0 },
+ { &vars[4], 0 },
+ { &vars[5], 0 },
+ { &vars[6], 0 },
+ { &vars[7], 0 },
+ { 0, 0 },
+};
+
+/*
+ * Skip to next option in the string
+ */
+static char *opt P((char**));
+static char *opt(p)
+char **p;
+{
+ char *cp = *p;
+ char *dp = cp;
+ char *s = cp;
+
+top:
+ while (*cp && *cp != ';') {
+ if (*cp == '\"') {
+ /*
+ * Skip past string
+ */
+ cp++;
+ while (*cp && *cp != '\"')
+ *dp++ = *cp++;
+ if (*cp)
+ cp++;
+ } else {
+ *dp++ = *cp++;
+ }
+ }
+
+ /*
+ * Skip past any remaining ';'s
+ */
+ while (*cp == ';')
+ cp++;
+
+ /*
+ * If we have a zero length string
+ * and there are more fields, then
+ * parse the next one. This allows
+ * sequences of empty fields.
+ */
+ if (*cp && dp == s)
+ goto top;
+
+ *dp = '\0';
+
+ *p = cp;
+ return s;
+}
+
+static int eval_opts P((char*, char*));
+static int eval_opts(opts, mapkey)
+char *opts;
+char *mapkey;
+{
+ /*
+ * Fill in the global structure fs_static by
+ * cracking the string opts. opts may be
+ * scribbled on at will.
+ */
+ char *o = opts;
+ char *f;
+
+ /*
+ * For each user-specified option
+ */
+ while (*(f = opt(&o))) {
+ struct opt *op;
+ enum vs_opt { OldSyn, SelEQ, SelNE, VarAss } vs_opt;
+ char *eq = strchr(f, '=');
+ char *opt;
+ if (!eq || eq[1] == '\0' || eq == f) {
+ /*
+ * No value, just continue
+ */
+ plog(XLOG_USER, "key %s: No value component in \"%s\"", mapkey, f);
+ continue;
+ }
+
+ /*
+ * Check what type of operation is happening
+ * !=, =! is SelNE
+ * == is SelEQ
+ * := is VarAss
+ * = is OldSyn (either SelEQ or VarAss)
+ */
+ if (eq[-1] == '!') { /* != */
+ vs_opt = SelNE;
+ eq[-1] = '\0';
+ opt = eq + 1;
+ } else if (eq[-1] == ':') { /* := */
+ vs_opt = VarAss;
+ eq[-1] = '\0';
+ opt = eq + 1;
+ } else if (eq[1] == '=') { /* == */
+ vs_opt = SelEQ;
+ eq[0] = '\0';
+ opt = eq + 2;
+ } else if (eq[1] == '!') { /* =! */
+ vs_opt = SelNE;
+ eq[0] = '\0';
+ opt = eq + 2;
+ } else { /* = */
+ vs_opt = OldSyn;
+ eq[0] = '\0';
+ opt = eq + 1;
+ }
+
+ /*
+ * For each recognised option
+ */
+ for (op = opt_fields; op->name; op++) {
+ /*
+ * Check whether they match
+ */
+ if (FSTREQ(op->name, f)) {
+ switch (vs_opt) {
+#if AMD_COMPAT <= 5000108
+ case OldSyn:
+ plog(XLOG_WARNING, "key %s: Old syntax selector found: %s=%s", mapkey, f, opt);
+ if (!op->sel_p) {
+ *op->optp = opt;
+ break;
+ }
+ /* fall through ... */
+#endif /* 5000108 */
+ case SelEQ:
+ case SelNE:
+ if (op->sel_p && (STREQ(*op->sel_p, opt) == (vs_opt == SelNE))) {
+ plog(XLOG_MAP, "key %s: map selector %s (=%s) did not %smatch %s",
+ mapkey,
+ op->name,
+ *op->sel_p,
+ vs_opt == SelNE ? "not " : "",
+ opt);
+ return 0;
+ }
+ break;
+
+ case VarAss:
+ if (op->sel_p) {
+ plog(XLOG_USER, "key %s: Can't assign to a selector (%s)", mapkey, op->name);
+ return 0;
+ }
+ *op->optp = opt;
+ break;
+ }
+ break;
+ }
+ }
+
+ if (!op->name)
+ plog(XLOG_USER, "key %s: Unrecognised key/option \"%s\"", mapkey, f);
+ }
+
+ return 1;
+}
+
+/*
+ * Free an option
+ */
+static void free_op P((opt_apply*, int));
+/*ARGSUSED*/
+static void free_op(p, b)
+opt_apply *p;
+int b;
+{
+ if (*p->opt) {
+ free(*p->opt);
+ *p->opt = 0;
+ }
+}
+
+/*
+ * Normalize slashes in the string.
+ */
+void normalize_slash P((char *p));
+void normalize_slash(p)
+char *p;
+{
+ char *f = strchr(p, '/');
+ char *f0 = f;
+ if (f) {
+ char *t = f;
+ do {
+ /* assert(*f == '/'); */
+ if (f == f0 && f[0] == '/' && f[1] == '/') {
+ /* copy double slash iff first */
+ *t++ = *f++;
+ *t++ = *f++;
+ } else {
+ /* copy a single / across */
+ *t++ = *f++;
+ }
+
+ /* assert(f[-1] == '/'); */
+ /* skip past more /'s */
+ while (*f == '/')
+ f++;
+
+ /* assert(*f != '/'); */
+ /* keep copying up to next / */
+ while (*f && *f != '/') {
+ *t++ = *f++;
+ }
+
+ /* assert(*f == 0 || *f == '/'); */
+
+ } while (*f);
+ *t = 0; /* derived from fix by Steven Glassman */
+ }
+}
+
+/*
+ * Macro-expand an option. Note that this does not
+ * handle recursive expansions. They will go badly wrong.
+ * If sel is true then old expand selectors, otherwise
+ * don't expand selectors.
+ */
+static void expand_op P((opt_apply*, int));
+static void expand_op(p, sel_p)
+opt_apply *p;
+int sel_p;
+{
+/*
+ * The BUFSPACE macros checks that there is enough space
+ * left in the expansion buffer. If there isn't then we
+ * give up completely. This is done to avoid crashing the
+ * automounter itself (which would be a bad thing to do).
+ */
+#define BUFSPACE(ep, len) (((ep) + (len)) < expbuf+MAXPATHLEN)
+static char expand_error[] = "No space to expand \"%s\"";
+
+ char expbuf[MAXPATHLEN+1];
+ char nbuf[NLEN+1];
+ char *ep = expbuf;
+ char *cp = *p->opt;
+ char *dp;
+#ifdef DEBUG
+ char *cp_orig = *p->opt;
+#endif /* DEBUG */
+ struct opt *op;
+
+ while (dp = strchr(cp, '$')) {
+ char ch;
+ /*
+ * First copy up to the $
+ */
+ { int len = dp - cp;
+ if (BUFSPACE(ep, len)) {
+ strncpy(ep, cp, len);
+ ep += len;
+ } else {
+ plog(XLOG_ERROR, expand_error, *p->opt);
+ goto out;
+ }
+ }
+ cp = dp + 1;
+ ch = *cp++;
+ if (ch == '$') {
+ if (BUFSPACE(ep, 1)) {
+ *ep++ = '$';
+ } else {
+ plog(XLOG_ERROR, expand_error, *p->opt);
+ goto out;
+ }
+ } else if (ch == '{') {
+ /* Expansion... */
+ enum { E_All, E_Dir, E_File, E_Domain, E_Host } todo;
+ /*
+ * Find closing brace
+ */
+ char *br_p = strchr(cp, '}');
+ int len;
+ /*
+ * Check we found it
+ */
+ if (!br_p) {
+ /*
+ * Just give up
+ */
+ plog(XLOG_USER, "No closing '}' in \"%s\"", *p->opt);
+ goto out;
+ }
+ len = br_p - cp;
+ /*
+ * Figure out which part of the variable to grab.
+ */
+ if (*cp == '/') {
+ /*
+ * Just take the last component
+ */
+ todo = E_File;
+ cp++;
+ --len;
+ } else if (br_p[-1] == '/') {
+ /*
+ * Take all but the last component
+ */
+ todo = E_Dir;
+ --len;
+ } else if (*cp == '.') {
+ /*
+ * Take domain name
+ */
+ todo = E_Domain;
+ cp++;
+ --len;
+ } else if (br_p[-1] == '.') {
+ /*
+ * Take host name
+ */
+ todo = E_Host;
+ --len;
+ } else {
+ /*
+ * Take the whole lot
+ */
+ todo = E_All;
+ }
+ /*
+ * Truncate if too long. Since it won't
+ * match anyway it doesn't matter that
+ * it has been cut short.
+ */
+ if (len > NLEN)
+ len = NLEN;
+ /*
+ * Put the string into another buffer so
+ * we can do comparisons.
+ */
+ strncpy(nbuf, cp, len);
+ nbuf[len] = '\0';
+ /*
+ * Advance cp
+ */
+ cp = br_p + 1;
+ /*
+ * Search the option array
+ */
+ for (op = opt_fields; op->name; op++) {
+ /*
+ * Check for match
+ */
+ if (len == op->nlen && STREQ(op->name, nbuf)) {
+ char xbuf[NLEN+3];
+ char *val;
+ /*
+ * Found expansion. Copy
+ * the correct value field.
+ */
+ if (!(!op->sel_p == !sel_p)) {
+ /*
+ * Copy the string across unexpanded
+ */
+ sprintf(xbuf, "${%s%s%s}",
+ todo == E_File ? "/" :
+ todo == E_Domain ? "." : "",
+ nbuf,
+ todo == E_Dir ? "/" :
+ todo == E_Host ? "." : "");
+ val = xbuf;
+ /*
+ * Make sure expansion doesn't
+ * munge the value!
+ */
+ todo = E_All;
+ } else if (op->sel_p) {
+ val = *op->sel_p;
+ } else {
+ val = *op->optp;
+ }
+ if (val) {
+ /*
+ * Do expansion:
+ * ${/var} means take just the last part
+ * ${var/} means take all but the last part
+ * ${.var} means take all but first part
+ * ${var.} means take just the first part
+ * ${var} means take the whole lot
+ */
+ int vlen = strlen(val);
+ char *vptr = val;
+ switch (todo) {
+ case E_Dir:
+ vptr = strrchr(val, '/');
+ if (vptr)
+ vlen = vptr - val;
+ vptr = val;
+ break;
+ case E_File:
+ vptr = strrchr(val, '/');
+ if (vptr) {
+ vptr++;
+ vlen = strlen(vptr);
+ } else
+ vptr = val;
+ break;
+ case E_Domain:
+ vptr = strchr(val, '.');
+ if (vptr) {
+ vptr++;
+ vlen = strlen(vptr);
+ } else {
+ vptr = "";
+ vlen = 0;
+ }
+ break;
+ case E_Host:
+ vptr = strchr(val, '.');
+ if (vptr)
+ vlen = vptr - val;
+ vptr = val;
+ break;
+ case E_All:
+ break;
+ }
+#ifdef DEBUG
+ /*dlog("Expanding \"%s\" to \"%s\"", nbuf, val);*/
+#endif /* DEBUG */
+ if (BUFSPACE(ep, vlen)) {
+ strcpy(ep, vptr);
+ ep += vlen;
+ } else {
+ plog(XLOG_ERROR, expand_error, *p->opt);
+ goto out;
+ }
+ }
+ /*
+ * Done with this variable
+ */
+ break;
+ }
+ }
+ /*
+ * Check that the search was succesful
+ */
+ if (!op->name) {
+ /*
+ * If it wasn't then scan the
+ * environment for that name
+ * and use any value found
+ */
+ char *env = getenv(nbuf);
+ if (env) {
+ int vlen = strlen(env);
+
+ if (BUFSPACE(ep, vlen)) {
+ strcpy(ep, env);
+ ep += vlen;
+ } else {
+ plog(XLOG_ERROR, expand_error, *p->opt);
+ goto out;
+ }
+#ifdef DEBUG
+ Debug(D_STR)
+ plog(XLOG_DEBUG, "Environment gave \"%s\" -> \"%s\"", nbuf, env);
+#endif /* DEBUG */
+ } else {
+ plog(XLOG_USER, "Unknown sequence \"${%s}\"", nbuf);
+ }
+ }
+ } else {
+ /*
+ * Error, error
+ */
+ plog(XLOG_USER, "Unknown $ sequence in \"%s\"", *p->opt);
+ }
+ }
+
+out:
+ /*
+ * Handle common case - no expansion
+ */
+ if (cp == *p->opt) {
+ *p->opt = strdup(cp);
+ } else {
+ /*
+ * Finish off the expansion
+ */
+ if (BUFSPACE(ep, strlen(cp))) {
+ strcpy(ep, cp);
+ /*ep += strlen(ep);*/
+ } else {
+ plog(XLOG_ERROR, expand_error, *p->opt);
+ }
+
+ /*
+ * Save the exansion
+ */
+ *p->opt = strdup(expbuf);
+ }
+
+ normalize_slash(*p->opt);
+
+#ifdef DEBUG
+ Debug(D_STR) {
+ plog(XLOG_DEBUG, "Expansion of \"%s\"...", cp_orig);
+ plog(XLOG_DEBUG, "... is \"%s\"", *p->opt);
+ }
+#endif /* DEBUG */
+}
+
+/*
+ * Wrapper for expand_op
+ */
+static void expand_opts P((opt_apply*, int));
+static void expand_opts(p, sel_p)
+opt_apply *p;
+int sel_p;
+{
+ if (*p->opt) {
+ expand_op(p, sel_p);
+ } else if (p->val) {
+ /*
+ * Do double expansion, remembering
+ * to free the string from the first
+ * expansion...
+ */
+ char *s = *p->opt = expand_key(p->val);
+ expand_op(p, sel_p);
+ free(s);
+ }
+}
+
+/*
+ * Apply a function to a list of options
+ */
+static void apply_opts(op, ppp, b)
+void (*op)();
+opt_apply ppp[];
+int b;
+{
+ opt_apply *pp;
+ for (pp = ppp; pp->opt; pp++)
+ (*op)(pp, b);
+}
+
+/*
+ * Free the option table
+ */
+void free_opts(fo)
+am_opts *fo;
+{
+ /*
+ * Copy in the structure we are playing with
+ */
+ fs_static = *fo;
+
+ /*
+ * Free previously allocated memory
+ */
+ apply_opts(free_op, to_free, FALSE);
+}
+
+/*
+ * Expand lookup key
+ */
+char *expand_key(key)
+char *key;
+{
+ opt_apply oa;
+
+ oa.opt = &key; oa.val = 0;
+ expand_opts(&oa, TRUE);
+
+ return key;
+}
+
+/*
+ * Remove trailing /'s from a string
+ * unless the string is a single / (Steven Glassman)
+ */
+void deslashify P((char *s));
+void deslashify(s)
+char *s;
+{
+ if (s && *s) {
+ char *sl = s + strlen(s);
+ while (*--sl == '/' && sl > s)
+ *sl = '\0';
+ }
+}
+
+int eval_fs_opts(fo, opts, g_opts, path, key, map)
+am_opts *fo;
+char *opts, *g_opts, *path, *key, *map;
+{
+ int ok = TRUE;
+
+ free_opts(fo);
+
+ /*
+ * Clear out the option table
+ */
+ bzero((voidp) &fs_static, sizeof(fs_static));
+ bzero((voidp) vars, sizeof(vars));
+ bzero((voidp) fo, sizeof(*fo));
+
+ /*
+ * Set key, map & path before expansion
+ */
+ opt_key = key;
+ opt_map = map;
+ opt_path = path;
+
+ /*
+ * Expand global options
+ */
+ fs_static.fs_glob = expand_key(g_opts);
+
+ /*
+ * Expand local options
+ */
+ fs_static.fs_local = expand_key(opts);
+
+ /*
+ * Expand default (global) options
+ */
+ if (!eval_opts(fs_static.fs_glob, key))
+ ok = FALSE;
+
+ /*
+ * Expand local options
+ */
+ if (ok && !eval_opts(fs_static.fs_local, key))
+ ok = FALSE;
+
+ /*
+ * Normalise remote host name.
+ * 1. Expand variables
+ * 2. Normalize relative to host tables
+ * 3. Strip local domains from the remote host
+ * name before using it in other expansions.
+ * This makes mount point names and other things
+ * much shorter, while allowing cross domain
+ * sharing of mount maps.
+ */
+ apply_opts(expand_opts, rhost_expansion, FALSE);
+ if (ok && fs_static.opt_rhost && *fs_static.opt_rhost)
+ host_normalize(&fs_static.opt_rhost);
+
+ /*
+ * Macro expand the options.
+ * Do this regardless of whether we are accepting
+ * this mount - otherwise nasty things happen
+ * with memory allocation.
+ */
+ apply_opts(expand_opts, expansions, FALSE);
+
+ /*
+ * Strip trailing slashes from local pathname...
+ */
+ deslashify(fs_static.opt_fs);
+
+ /*
+ * ok... copy the data back out.
+ */
+ *fo = fs_static;
+
+ /*
+ * Clear defined options
+ */
+ opt_key = opt_map = opt_path = nullstr;
+
+ return ok;
+}
diff --git a/usr.sbin/amd/amd/pfs_ops.c b/usr.sbin/amd/amd/pfs_ops.c
new file mode 100644
index 0000000..8c9f4ac
--- /dev/null
+++ b/usr.sbin/amd/amd/pfs_ops.c
@@ -0,0 +1,169 @@
+/*
+ * Copyright (c) 1989 Jan-Simon Pendry
+ * Copyright (c) 1989 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)pfs_ops.c 8.1 (Berkeley) 6/6/93
+ *
+ * $Id$
+ *
+ */
+
+#include "am.h"
+
+#ifdef HAS_PFS
+
+/*
+ * Program file system
+ */
+
+/*
+ * Execute needs a mount and unmount command.
+ */
+static char *pfs_match(fo)
+am_opts *fo;
+{
+ char *prog;
+ if (!fo->opt_mount || !fo->opt_unmount) {
+ plog(XLOG_USER, "program: no mount/unmount specified");
+ return 0;
+ }
+ prog = strchr(fo->opt_mount, ' ');
+ return strdup(prog ? prog+1 : fo->opt_mount);
+}
+
+static int pfs_init(mf)
+mntfs *mf;
+{
+ /*
+ * Save unmount command
+ */
+ if (mf->mf_refc == 1) {
+ mf->mf_private = (voidp) strdup(mf->mf_fo->opt_unmount);
+ mf->mf_prfree = (void (*) ()) free;
+ }
+ return 0;
+}
+
+static int pfs_exec(info)
+char *info;
+{
+ char **xivec;
+ int error;
+ /*
+ * Split copy of command info string
+ */
+ info = strdup(info);
+ if (info == 0)
+ return ENOBUFS;
+ xivec = strsplit(info, ' ', '\'');
+ /*
+ * Put stdout to stderr
+ */
+ (void) fclose(stdout);
+ (void) dup(fileno(logfp));
+ if (fileno(logfp) != fileno(stderr)) {
+ (void) fclose(stderr);
+ (void) dup(fileno(logfp));
+ }
+ /*
+ * Try the exec
+ */
+#ifdef DEBUG
+ Debug(D_FULL) {
+ char **cp = xivec;
+ plog(XLOG_DEBUG, "executing (un)mount command...");
+ while (*cp) {
+ plog(XLOG_DEBUG, "arg[%d] = '%s'", cp-xivec, *cp);
+ cp++;
+ }
+ }
+#endif /* DEBUG */
+ if (xivec[0] == 0 || xivec[1] == 0) {
+ errno = EINVAL;
+ plog(XLOG_USER, "1st/2nd args missing to (un)mount program");
+ } else {
+ (void) execv(xivec[0], xivec+1);
+ }
+ /*
+ * Save error number
+ */
+ error = errno;
+ plog(XLOG_ERROR, "exec failed: %m");
+
+ /*
+ * Free allocate memory
+ */
+ free((voidp) info);
+ free((voidp) xivec);
+ /*
+ * Return error
+ */
+ return error;
+}
+
+static int pfs_fmount(mf)
+mntfs *mf;
+{
+ return pfs_exec(mf->mf_fo->opt_mount);
+}
+
+static int pfs_fumount(mf)
+mntfs *mf;
+{
+ return pfs_exec((char *) mf->mf_private);
+}
+
+/*
+ * Ops structure
+ */
+am_ops pfs_ops = {
+ "program",
+ pfs_match,
+ pfs_init,
+ auto_fmount,
+ pfs_fmount,
+ auto_fumount,
+ pfs_fumount,
+ efs_lookuppn,
+ efs_readdir,
+ 0, /* pfs_readlink */
+ 0, /* pfs_mounted */
+ 0, /* pfs_umounted */
+ find_afs_srvr,
+ FS_BACKGROUND|FS_AMQINFO
+};
+
+#endif /* HAS_PFS */
diff --git a/usr.sbin/amd/amd/restart.c b/usr.sbin/amd/amd/restart.c
new file mode 100644
index 0000000..29f6044
--- /dev/null
+++ b/usr.sbin/amd/amd/restart.c
@@ -0,0 +1,181 @@
+/*
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id$
+ *
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)restart.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include "am.h"
+
+/*
+ * Handle an amd restart.
+ *
+ * Scan through the mount list finding all "interesting" mount points.
+ * Next hack up partial data structures and add the mounted file
+ * system to the list of known filesystems. This will leave a
+ * dangling reference to that filesystems, so when the filesystem is
+ * finally inherited, an extra "free" must be done on it.
+ *
+ * This module relies on internal details of other components. If
+ * you change something else make *sure* restart() still works.
+ */
+void restart()
+{
+ /*
+ * Read the existing mount table
+ */
+ mntlist *ml, *mlp;
+
+ /*
+ * For each entry, find nfs, ufs or auto mounts
+ * and create a partial am_node to represent it.
+ */
+ for (mlp = ml = read_mtab("restart"); mlp; mlp = mlp->mnext) {
+ struct mntent *me = mlp->mnt;
+ am_ops *fs_ops = 0;
+ if (STREQ(me->mnt_type, MTAB_TYPE_UFS)) {
+ /*
+ * UFS entry
+ */
+ fs_ops = &ufs_ops;
+ } else if (STREQ(me->mnt_type, MTAB_TYPE_NFS)) {
+ /*
+ * NFS entry, or possibly an Amd entry...
+ */
+ int au_pid;
+ char *colon = strchr(me->mnt_fsname, ':');
+ if (colon && sscanf(colon, ":(pid%d)", &au_pid) == 1) {
+ plog(XLOG_WARNING, "%s is an existing automount point", me->mnt_dir);
+ fs_ops = &sfs_ops;
+ } else {
+ fs_ops = &nfs_ops;
+ }
+#ifdef MTAB_TYPE_MFS
+ } else if (STREQ(me->mnt_type, MTAB_TYPE_MFS)) {
+ /*
+ * MFS entry. Fake with a symlink.
+ */
+ fs_ops = &sfs_ops;
+#endif /* MTAB_TYPE_MFS */
+ } else {
+ /*
+ * Catch everything else with symlinks to
+ * avoid recursive mounts. This is debatable...
+ */
+ fs_ops = &sfs_ops;
+ }
+
+ /*
+ * If we found something to do
+ */
+ if (fs_ops) {
+ mntfs *mf;
+ am_opts mo;
+ char *cp;
+ cp = strchr(me->mnt_fsname, ':');
+ /*
+ * Partially fake up an opts structure
+ */
+ mo.opt_rhost = 0;
+ mo.opt_rfs = 0;
+ if (cp) {
+ *cp = '\0';
+ mo.opt_rhost = strdup(me->mnt_fsname);
+ mo.opt_rfs = strdup(cp+1);
+ *cp = ':';
+ } else if (fs_ops->ffserver == find_nfs_srvr) {
+ /*
+ * Prototype 4.4 BSD used to end up here -
+ * might as well keep the workaround for now
+ */
+ plog(XLOG_WARNING, "NFS server entry assumed to be %s:/", me->mnt_fsname);
+ mo.opt_rhost = strdup(me->mnt_fsname);
+ mo.opt_rfs = strdup("/");
+ me->mnt_fsname = str3cat(me->mnt_fsname, mo.opt_rhost, ":", "/");
+ }
+ mo.opt_fs = me->mnt_dir;
+ mo.opt_opts = me->mnt_opts;
+
+ /*
+ * Make a new mounted filesystem
+ */
+ mf = find_mntfs(fs_ops, &mo, me->mnt_dir,
+ me->mnt_fsname, "", me->mnt_opts, "");
+ if (mf->mf_refc == 1) {
+ mf->mf_flags |= MFF_RESTART|MFF_MOUNTED;
+ mf->mf_error = 0; /* Already mounted correctly */
+ mf->mf_fo = 0;
+ /*
+ * If the restarted type is a link then
+ * don't time out.
+ */
+ if (fs_ops == &sfs_ops || fs_ops == &ufs_ops)
+ mf->mf_flags |= MFF_RSTKEEP;
+ if (fs_ops->fs_init) {
+ /*
+ * Don't care whether this worked since
+ * it is checked again when the fs is
+ * inherited.
+ */
+ (void) (*fs_ops->fs_init)(mf);
+ }
+
+ plog(XLOG_INFO, "%s restarted fstype %s on %s",
+ me->mnt_fsname, fs_ops->fs_type, me->mnt_dir);
+ } else {
+ /* Something strange happened - two mounts at the same place! */
+ free_mntfs(mf);
+ }
+ /*
+ * Clean up mo
+ */
+ if (mo.opt_rhost)
+ free(mo.opt_rhost);
+ if (mo.opt_rfs)
+ free(mo.opt_rfs);
+ }
+ }
+
+ /*
+ * Free the mount list
+ */
+ free_mntlist(ml);
+}
diff --git a/usr.sbin/amd/amd/rpc_fwd.c b/usr.sbin/amd/amd/rpc_fwd.c
new file mode 100644
index 0000000..4fc5b17
--- /dev/null
+++ b/usr.sbin/amd/amd/rpc_fwd.c
@@ -0,0 +1,430 @@
+/*
+ * Copyright (c) 1989 Jan-Simon Pendry
+ * Copyright (c) 1989 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)rpc_fwd.c 8.1 (Berkeley) 6/6/93
+ *
+ * $Id$
+ *
+ */
+
+/*
+ * RPC packet forwarding
+ */
+
+#include "am.h"
+#include <sys/ioctl.h>
+#ifndef F_SETFL
+#include <fcntl.h>
+#endif /* F_SETFL */
+#ifndef FNDELAY
+#include <sys/file.h>
+#endif /* FNDELAY */
+
+/*
+ * Note that the ID field in the external packet is only
+ * ever treated as a 32 bit opaque data object, so there
+ * is no need to convert to and from network byte ordering.
+ */
+
+/*
+ * Each pending reply has an rpc_forward structure
+ * associated with it. These have a 15 second lifespan.
+ * If a new structure is required, then an expired
+ * one will be re-allocated if available, otherwise a fresh
+ * one is allocated. Whenever a reply is received the
+ * structure is discarded.
+ */
+typedef struct rpc_forward rpc_forward;
+struct rpc_forward {
+ qelem rf_q; /* Linked list */
+ time_t rf_ttl; /* Time to live */
+ u_int rf_xid; /* Packet id */
+ u_int rf_oldid; /* Original packet id */
+ fwd_fun rf_fwd; /* Forwarding function */
+ voidp rf_ptr;
+ struct sockaddr_in rf_sin;
+};
+
+/*
+ * Head of list of pending replies
+ */
+extern qelem rpc_head;
+qelem rpc_head = { &rpc_head, &rpc_head };
+
+static u_int xid;
+#define XID_ALLOC() (xid++)
+
+#define MAX_PACKET_SIZE 8192 /* Maximum UDP packet size */
+
+int fwd_sock;
+
+/*
+ * Allocate a rely structure
+ */
+static rpc_forward *fwd_alloc()
+{
+ time_t now = clocktime();
+ rpc_forward *p = 0, *p2;
+
+#ifdef DEBUG
+ /*dlog("fwd_alloca: rpc_head = %#x", rpc_head.q_forw);*/
+#endif /* DEBUG */
+ /*
+ * First search for an existing expired one.
+ */
+ ITER(p2, rpc_forward, &rpc_head) {
+ if (p2->rf_ttl <= now) {
+ p = p2;
+ break;
+ }
+ }
+
+ /*
+ * If one couldn't be found then allocate
+ * a new structure and link it at the
+ * head of the list.
+ */
+ if (p) {
+ /*
+ * Call forwarding function to say that
+ * this message was junked.
+ */
+#ifdef DEBUG
+ dlog("Re-using packet forwarding slot - id %#x", p->rf_xid);
+#endif /* DEBUG */
+ if (p->rf_fwd)
+ (*p->rf_fwd)(0, 0, 0, &p->rf_sin, p->rf_ptr, FALSE);
+ rem_que(&p->rf_q);
+ } else {
+ p = ALLOC(rpc_forward);
+ }
+ ins_que(&p->rf_q, &rpc_head);
+
+ /*
+ * Set the time to live field
+ * Timeout in 43 seconds
+ */
+ p->rf_ttl = now + 43;
+
+#ifdef DEBUG
+ /*dlog("fwd_alloca: rpc_head = %#x", rpc_head.q_forw);*/
+#endif /* DEBUG */
+ return p;
+}
+
+/*
+ * Free an allocated reply structure.
+ * First unlink it from the list, then
+ * discard it.
+ */
+static void fwd_free(p)
+rpc_forward *p;
+{
+#ifdef DEBUG
+ /*dlog("fwd_free: rpc_head = %#x", rpc_head.q_forw);*/
+#endif /* DEBUG */
+ rem_que(&p->rf_q);
+#ifdef DEBUG
+ /*dlog("fwd_free: rpc_head = %#x", rpc_head.q_forw);*/
+#endif /* DEBUG */
+ free((voidp) p);
+}
+
+/*
+ * Initialise the RPC forwarder
+ */
+int fwd_init()
+{
+ int on = 1;
+
+ /*
+ * Create ping socket
+ */
+ fwd_sock = socket(AF_INET, SOCK_DGRAM, 0);
+ if (fwd_sock < 0) {
+ plog(XLOG_ERROR, "Unable to create RPC forwarding socket: %m");
+ return errno;
+ }
+
+ /*
+ * Some things we talk to require a priv port - so make one here
+ */
+ if (bind_resv_port(fwd_sock, (unsigned short *) 0) < 0)
+ plog(XLOG_ERROR, "can't bind privileged port");
+
+ if (fcntl(fwd_sock, F_SETFL, FNDELAY) < 0 &&
+ ioctl(fwd_sock, FIONBIO, &on) < 0) {
+ plog(XLOG_ERROR, "Can't set non-block on forwarding socket: %m");
+ return errno;
+ }
+
+ return 0;
+}
+
+/*
+ * Locate a packet in the forwarding list
+ */
+static rpc_forward *fwd_locate(id)
+u_int id;
+{
+ rpc_forward *p;
+
+ ITER(p, rpc_forward, &rpc_head) {
+ if (p->rf_xid == id)
+ return p;
+ }
+
+ return 0;
+}
+
+/*
+ * This is called to forward a packet to another
+ * RPC server. The message id is changed and noted
+ * so that when a reply appears we can tie it up
+ * correctly. Just matching the reply's source address
+ * would not work because it might come from a
+ * different address.
+ */
+int fwd_packet(type_id, pkt, len, fwdto, replyto, i, cb)
+int type_id;
+voidp pkt;
+int len;
+struct sockaddr_in *fwdto, *replyto;
+voidp i;
+fwd_fun cb;
+{
+ rpc_forward *p;
+ u_int *pkt_int;
+ int error;
+
+ if ((int)amd_state >= (int)Finishing)
+ return ENOENT;
+
+ /*
+ * See if the type_id is fully specified.
+ * If so, then discard any old entries
+ * for this id.
+ * Otherwise make sure the type_id is
+ * fully qualified by allocating an id here.
+ */
+#ifdef DEBUG
+ switch (type_id & RPC_XID_MASK) {
+ case RPC_XID_PORTMAP: dlog("Sending PORTMAP request"); break;
+ case RPC_XID_MOUNTD: dlog("Sending MOUNTD request %#x", type_id); break;
+ case RPC_XID_NFSPING: dlog("Sending NFS ping"); break;
+ default: dlog("UNKNOWN RPC XID"); break;
+ }
+#endif /* DEBUG */
+
+ if (type_id & ~RPC_XID_MASK) {
+#ifdef DEBUG
+ /*dlog("Fully qualified rpc type provided");*/
+#endif /* DEBUG */
+ p = fwd_locate(type_id);
+ if (p) {
+#ifdef DEBUG
+ dlog("Discarding earlier rpc fwd handle");
+#endif /* DEBUG */
+ fwd_free(p);
+ }
+ } else {
+#ifdef DEBUG
+ dlog("Allocating a new xid...");
+#endif /* DEBUG */
+ type_id = MK_RPC_XID(type_id, XID_ALLOC());
+ }
+
+ p = fwd_alloc();
+ if (!p)
+ return ENOBUFS;
+
+ error = 0;
+
+ pkt_int = (u_int *) pkt;
+
+ /*
+ * Get the original packet id
+ */
+ p->rf_oldid = *pkt_int;
+
+ /*
+ * Replace with newly allocated id
+ */
+ p->rf_xid = *pkt_int = type_id;
+
+ /*
+ * The sendto may fail if, for example, the route
+ * to a remote host is lost because an intermediate
+ * gateway has gone down. Important to fill in the
+ * rest of "p" otherwise nasty things happen later...
+ */
+#ifdef DEBUG
+ { char dq[20];
+ dlog("Sending packet id %#x to %s.%d", p->rf_xid, inet_dquad(dq, fwdto->sin_addr.s_addr), ntohs(fwdto->sin_port));
+ }
+#endif /* DEBUG */
+ if (sendto(fwd_sock, (char *) pkt, len, 0,
+ (struct sockaddr *) fwdto, sizeof(*fwdto)) < 0)
+ error = errno;
+
+ /*
+ * Save callback function and return address
+ */
+ p->rf_fwd = cb;
+ if (replyto)
+ p->rf_sin = *replyto;
+ else
+ bzero((voidp) &p->rf_sin, sizeof(p->rf_sin));
+ p->rf_ptr = i;
+
+ return error;
+}
+
+/*
+ * Called when some data arrives on the forwarding socket
+ */
+void fwd_reply()
+{
+ int len;
+#ifdef DYNAMIC_BUFFERS
+ voidp pkt;
+#else
+ u_int pkt[MAX_PACKET_SIZE/sizeof(u_int)+1];
+#endif /* DYNAMIC_BUFFERS */
+ u_int *pkt_int;
+ int rc;
+ rpc_forward *p;
+ struct sockaddr_in src_addr;
+ int src_addr_len;
+
+ /*
+ * Determine the length of the packet
+ */
+#ifdef DYNAMIC_BUFFERS
+ if (ioctl(fwd_sock, FIONREAD, &len) < 0) {
+ plog(XLOG_ERROR, "Error reading packet size: %m");
+ return;
+ }
+
+ /*
+ * Allocate a buffer
+ */
+ pkt = (voidp) malloc((unsigned) len);
+ if (!pkt) {
+ plog(XLOG_ERROR, "Out of buffers in fwd_reply");
+ return;
+ }
+#else
+ len = MAX_PACKET_SIZE;
+#endif /* DYNAMIC_BUFFERS */
+
+ /*
+ * Read the packet and check for validity
+ */
+again:
+ src_addr_len = sizeof(src_addr);
+ rc = recvfrom(fwd_sock, (char *) pkt, len, 0,
+ (struct sockaddr *) &src_addr, &src_addr_len);
+ if (rc < 0 || src_addr_len != sizeof(src_addr) ||
+ src_addr.sin_family != AF_INET) {
+ if (rc < 0 && errno == EINTR)
+ goto again;
+ plog(XLOG_ERROR, "Error reading RPC reply: %m");
+ goto out;
+ }
+
+#ifdef DYNAMIC_BUFFERS
+ if (rc != len) {
+ plog(XLOG_ERROR, "Short read in fwd_reply");
+ goto out;
+ }
+#endif /* DYNAMIC_BUFFERS */
+
+ /*
+ * Do no more work if finishing soon
+ */
+ if ((int)amd_state >= (int)Finishing)
+ goto out;
+
+ /*
+ * Find packet reference
+ */
+ pkt_int = (u_int *) pkt;
+
+#ifdef DEBUG
+ switch (*pkt_int & RPC_XID_MASK) {
+ case RPC_XID_PORTMAP: dlog("Receiving PORTMAP reply"); break;
+ case RPC_XID_MOUNTD: dlog("Receiving MOUNTD reply %#x", *pkt_int); break;
+ case RPC_XID_NFSPING: dlog("Receiving NFS ping %#x", *pkt_int); break;
+ default: dlog("UNKNOWN RPC XID"); break;
+ }
+#endif /* DEBUG */
+
+ p = fwd_locate(*pkt_int);
+ if (!p) {
+#ifdef DEBUG
+ dlog("Can't forward reply id %#x", *pkt_int);
+#endif /* DEBUG */
+ goto out;
+ }
+
+ if (p->rf_fwd) {
+ /*
+ * Put the original message id back
+ * into the packet.
+ */
+ *pkt_int = p->rf_oldid;
+
+ /*
+ * Call forwarding function
+ */
+ (*p->rf_fwd)((voidp) pkt, rc, &src_addr, &p->rf_sin, p->rf_ptr, TRUE);
+ }
+
+ /*
+ * Free forwarding info
+ */
+ fwd_free(p);
+
+out:;
+#ifdef DYNAMIC_BUFFERS
+ /*
+ * Free the packet
+ */
+ free((voidp) pkt);
+#endif /* DYNAMIC_BUFFERS */
+}
diff --git a/usr.sbin/amd/amd/sched.c b/usr.sbin/amd/amd/sched.c
new file mode 100644
index 0000000..0e72abd
--- /dev/null
+++ b/usr.sbin/amd/amd/sched.c
@@ -0,0 +1,325 @@
+/*
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)sched.c 8.1 (Berkeley) 6/6/93
+ *
+ * $Id$
+ *
+ */
+
+/*
+ * Process scheduler
+ */
+
+#include "am.h"
+#include <sys/signal.h>
+#include WAIT
+#include <setjmp.h>
+extern jmp_buf select_intr;
+extern int select_intr_valid;
+
+typedef struct pjob pjob;
+struct pjob {
+ qelem hdr; /* Linked list */
+ int pid; /* Process ID of job */
+ cb_fun cb_fun; /* Callback function */
+ voidp cb_closure; /* Closure for callback */
+ union wait w; /* Status filled in by sigchld */
+ voidp wchan; /* Wait channel */
+};
+
+extern qelem proc_list_head;
+qelem proc_list_head = { &proc_list_head, &proc_list_head };
+extern qelem proc_wait_list;
+qelem proc_wait_list = { &proc_wait_list, &proc_wait_list };
+
+int task_notify_todo;
+
+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;
+}
+
+void rem_que(elem)
+qelem *elem;
+{
+ qelem *p = elem->q_forw;
+ qelem *p2 = elem->q_back;
+ p2->q_forw = p;
+ p->q_back = p2;
+}
+
+static pjob *sched_job(cf, ca)
+cb_fun cf;
+voidp ca;
+{
+ pjob *p = ALLOC(pjob);
+
+ p->cb_fun = cf;
+ p->cb_closure = ca;
+
+ /*
+ * Now place on wait queue
+ */
+ ins_que(&p->hdr, &proc_wait_list);
+
+ return p;
+}
+
+void run_task(tf, ta, cf, ca)
+task_fun tf;
+voidp ta;
+cb_fun cf;
+voidp ca;
+{
+ pjob *p = sched_job(cf, ca);
+ int mask;
+
+ p->wchan = (voidp) p;
+
+ mask = sigblock(sigmask(SIGCHLD));
+
+ if (p->pid = background()) {
+ sigsetmask(mask);
+ return;
+ }
+
+ exit((*tf)(ta));
+ /* firewall... */
+ abort();
+}
+
+/*
+ * Schedule a task to be run when woken up
+ */
+void sched_task(cf, ca, wchan)
+cb_fun cf;
+voidp ca;
+voidp wchan;
+{
+ /*
+ * Allocate a new task
+ */
+ pjob *p = sched_job(cf, ca);
+#ifdef DEBUG_SLEEP
+ dlog("SLEEP on %#x", wchan);
+#endif
+ p->wchan = wchan;
+ p->pid = 0;
+ bzero((voidp) &p->w, sizeof(p->w));
+}
+
+static void wakeupjob(p)
+pjob *p;
+{
+ rem_que(&p->hdr);
+ ins_que(&p->hdr, &proc_list_head);
+ task_notify_todo++;
+}
+
+void wakeup(wchan)
+voidp wchan;
+{
+ pjob *p, *p2;
+#ifdef DEBUG_SLEEP
+ int done = 0;
+#endif
+ if (!foreground)
+ return;
+
+#ifdef DEBUG_SLEEP
+ /*dlog("wakeup(%#x)", wchan);*/
+#endif
+ /*
+ * Can't user ITER() here because
+ * wakeupjob() juggles the list.
+ */
+ for (p = FIRST(pjob, &proc_wait_list);
+ p2 = NEXT(pjob, p), p != HEAD(pjob, &proc_wait_list);
+ p = p2) {
+ if (p->wchan == wchan) {
+#ifdef DEBUG_SLEEP
+ done = 1;
+#endif
+ wakeupjob(p);
+ }
+ }
+
+#ifdef DEBUG_SLEEP
+ if (!done)
+ dlog("Nothing SLEEPing on %#x", wchan);
+#endif
+}
+
+void wakeup_task(rc, term, cl)
+int rc;
+int term;
+voidp cl;
+{
+ wakeup(cl);
+}
+
+/*ARGSUSED*/
+
+void sigchld(sig)
+int sig;
+{
+ union wait w;
+ int pid;
+
+#ifdef SYS5_SIGNALS
+ if ((pid = wait(&w)) > 0) {
+#else
+ while ((pid = wait3((int *) &w, WNOHANG, (struct rusage *) 0)) > 0) {
+#endif /* SYS5_SIGNALS */
+ pjob *p, *p2;
+
+ if (WIFSIGNALED(w))
+ plog(XLOG_ERROR, "Process %d exited with signal %d",
+ pid, w.w_termsig);
+#ifdef DEBUG
+ else
+ dlog("Process %d exited with status %d",
+ pid, w.w_retcode);
+#endif /* DEBUG */
+
+ for (p = FIRST(pjob, &proc_wait_list);
+ p2 = NEXT(pjob, p), p != HEAD(pjob, &proc_wait_list);
+ p = p2) {
+ if (p->pid == pid) {
+ p->w = w;
+ wakeupjob(p);
+ break;
+ }
+ }
+
+#ifdef DEBUG
+ if (p) ; else dlog("can't locate task block for pid %d", pid);
+#endif /* DEBUG */
+ }
+
+#ifdef SYS5_SIGNALS
+ signal(sig, sigchld);
+#endif /* SYS5_SIGNALS */
+ if (select_intr_valid)
+ longjmp(select_intr, sig);
+}
+
+/*
+ * Run any pending tasks.
+ * This must be called with SIGCHLD disabled
+ */
+void do_task_notify(P_void)
+{
+ /*
+ * Keep taking the first item off the list and processing it.
+ *
+ * Done this way because the the callback can, quite reasonably,
+ * queue a new task, so no local reference into the list can be
+ * held here.
+ */
+ while (FIRST(pjob, &proc_list_head) != HEAD(pjob, &proc_list_head)) {
+ pjob *p = FIRST(pjob, &proc_list_head);
+ rem_que(&p->hdr);
+ /*
+ * This job has completed
+ */
+ --task_notify_todo;
+
+ /*
+ * Do callback if it exists
+ */
+ if (p->cb_fun)
+ (*p->cb_fun)(p->w.w_retcode,
+ p->w.w_termsig, p->cb_closure);
+
+ free((voidp) p);
+ }
+}
+
+#ifdef HAS_SVR3_SIGNALS
+/*
+ * 4.2 signal library based on svr3 (4.1+ bsd) interface
+ * From Stephen C. Pope <scp@acl.lanl.gov).
+ */
+
+static int current_mask = 0;
+
+int sigblock(mask)
+int mask;
+{
+ int sig;
+ int m;
+ int oldmask;
+
+ oldmask = current_mask;
+ for ( sig = 1, m = 1; sig <= MAXSIG; sig++, m <<= 1 ) {
+ if (mask & m) {
+ sighold(sig);
+ current_mask |= m;
+ }
+ }
+ return oldmask;
+}
+
+int sigsetmask(mask)
+int mask;
+{
+ int sig;
+ int m;
+ int oldmask;
+
+ oldmask = current_mask;
+ for ( sig = 1, m = 1; sig <= MAXSIG; sig++, m <<= 1 ) {
+ if (mask & m) {
+ sighold(sig);
+ current_mask |= m;
+ }
+ else {
+ sigrelse(sig);
+ current_mask &= ~m;
+ }
+ }
+ return oldmask;
+}
+
+#endif /* HAS_SVR3_SIGNALS */
diff --git a/usr.sbin/amd/amd/sfs_ops.c b/usr.sbin/amd/amd/sfs_ops.c
new file mode 100644
index 0000000..51cc4d8
--- /dev/null
+++ b/usr.sbin/amd/amd/sfs_ops.c
@@ -0,0 +1,208 @@
+/*
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)sfs_ops.c 8.1 (Berkeley) 6/6/93
+ *
+ * $Id$
+ *
+ */
+
+#include "am.h"
+
+#if defined(HAS_SFS) || defined(HAS_SFSX)
+#define NEED_SFS_MATCH
+#define NEED_SFS_UMOUNT
+#endif
+
+/*
+ * Symbol-link file system
+ */
+
+#ifdef HAS_SFSX
+#include <sys/stat.h>
+#endif
+
+#ifdef NEED_SFS_MATCH
+/*
+ * SFS needs a link.
+ */
+static char *sfs_match(fo)
+am_opts *fo;
+{
+ if (!fo->opt_fs) {
+ plog(XLOG_USER, "link: no fs specified");
+ return 0;
+ }
+
+ /*
+ * Bug report (14/12/89) from Jay Plett <jay@princeton.edu>
+ * If an automount point has the same name as an existing
+ * link type mount Amd hits a race condition and either hangs
+ * or causes a symlink loop.
+ *
+ * If fs begins with a '/' change the opt_fs & opt_sublink
+ * fields so that the fs option doesn't end up pointing at
+ * an existing symlink.
+ *
+ * If sublink is nil then set sublink to fs
+ * else set sublink to fs / sublink
+ *
+ * Finally set fs to ".".
+ */
+ if (*fo->opt_fs == '/') {
+ char *fullpath;
+ char *link = fo->opt_sublink;
+ if (link) {
+ if (*link == '/')
+ fullpath = strdup(link);
+ else
+ fullpath = str3cat((char *)0, fo->opt_fs, "/", link);
+ } else {
+ fullpath = strdup(fo->opt_fs);
+ }
+
+ if (fo->opt_sublink)
+ free(fo->opt_sublink);
+ fo->opt_sublink = fullpath;
+ fo->opt_fs = str3cat(fo->opt_fs, ".", fullpath, "");
+ }
+
+ return strdup(fo->opt_fs);
+}
+#endif
+
+#ifdef HAS_SFSX
+/*ARGUSED*/
+static int sfsx_mount P((am_node *mp));
+static int sfsx_mount(mp)
+am_node *mp;
+{
+ /*
+ * Check for existence of target.
+ */
+ struct stat stb;
+ char *ln;
+
+ if (mp->am_link)
+ ln = mp->am_link;
+ else /* should never occur */
+ ln = mp->am_mnt->mf_mount;
+
+ /*
+ * Use lstat, not stat, since we don't
+ * want to know if the ultimate target of
+ * a symlink chain exists, just the first.
+ */
+ if (lstat(ln, &stb) < 0)
+ return errno;
+
+ return 0;
+}
+#endif
+
+#ifdef HAS_SFS
+/*ARGUSED*/
+static int sfs_fmount(mf)
+mntfs *mf;
+{
+ /*
+ * Wow - this is hard to implement!
+ */
+
+ return 0;
+}
+#endif
+
+#ifdef NEED_SFS_UMOUNT
+/*ARGUSED*/
+static int sfs_fumount(mf)
+mntfs *mf;
+{
+ return 0;
+}
+#endif
+
+/*
+ * Ops structures
+ */
+#ifdef HAS_SFS
+am_ops sfs_ops = {
+ "link",
+ sfs_match,
+ 0, /* sfs_init */
+ auto_fmount,
+ sfs_fmount,
+ auto_fumount,
+ sfs_fumount,
+ efs_lookuppn,
+ efs_readdir,
+ 0, /* sfs_readlink */
+ 0, /* sfs_mounted */
+ 0, /* sfs_umounted */
+ find_afs_srvr,
+#ifdef FLUSH_KERNEL_NAME_CACHE
+ FS_UBACKGROUND
+#else /* FLUSH_KERNEL_NAME_CACHE */
+ 0
+#endif /* FLUSH_KERNEL_NAME_CACHE */
+};
+
+#endif /* HAS_SFS */
+
+#ifdef HAS_SFSX
+struct am_ops sfsx_ops = {
+ "linkx",
+ sfs_match,
+ 0, /* sfsx_init */
+ sfsx_mount,
+ 0,
+ auto_fumount,
+ sfs_fumount,
+ efs_lookuppn,
+ efs_readdir,
+ 0, /* sfsx_readlink */
+ 0, /* sfsx_mounted */
+ 0, /* sfsx_umounted */
+ find_afs_srvr,
+#ifdef FLUSH_KERNEL_NAME_CACHE
+ FS_BACKGROUND
+#else /* FLUSH_KERNEL_NAME_CACHE */
+ FS_MBACKGROUND
+#endif /* FLUSH_KERNEL_NAME_CACHE */
+};
+
+#endif /* HAS_SFSX */
diff --git a/usr.sbin/amd/amd/srvr_afs.c b/usr.sbin/amd/amd/srvr_afs.c
new file mode 100644
index 0000000..2bf8acb
--- /dev/null
+++ b/usr.sbin/amd/amd/srvr_afs.c
@@ -0,0 +1,205 @@
+/*
+ * Copyright (c) 1989 Jan-Simon Pendry
+ * Copyright (c) 1989 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)srvr_afs.c 8.1 (Berkeley) 6/6/93
+ *
+ * $Id$
+ *
+ */
+
+/*
+ * Automount FS server ("localhost") modeling
+ */
+
+#include "am.h"
+
+extern qelem afs_srvr_list;
+qelem afs_srvr_list = { &afs_srvr_list, &afs_srvr_list };
+
+static fserver *localhost;
+
+/*
+ * Find an nfs server for the local host
+ */
+fserver *find_afs_srvr P((mntfs *));
+fserver *find_afs_srvr(mf)
+mntfs *mf;
+{
+ fserver *fs = localhost;
+
+ if (!fs) {
+ fs = ALLOC(fserver);
+ fs->fs_refc = 0;
+ fs->fs_host = strdup("localhost");
+ fs->fs_ip = 0;
+ fs->fs_cid = 0;
+ fs->fs_pinger = 0;
+ fs->fs_flags = FSF_VALID;
+ fs->fs_type = "local";
+ fs->fs_private = 0;
+ fs->fs_prfree = 0;
+
+ ins_que(&fs->fs_q, &afs_srvr_list);
+
+ srvrlog(fs, "starts up");
+
+ localhost = fs;
+ }
+
+ fs->fs_refc++;
+
+ return fs;
+}
+
+/*------------------------------------------------------------------*/
+ /* Generic routines follow */
+
+/*
+ * Wakeup anything waiting for this server
+ */
+void wakeup_srvr P((fserver *fs));
+void wakeup_srvr(fs)
+fserver *fs;
+{
+ fs->fs_flags &= ~FSF_WANT;
+ wakeup((voidp) fs);
+}
+
+/*
+ * Called when final ttl of server has expired
+ */
+static void timeout_srvr P((fserver *fs));
+static void timeout_srvr(fs)
+fserver *fs;
+{
+ /*
+ * If the reference count is still zero then
+ * we are free to remove this node
+ */
+ if (fs->fs_refc == 0) {
+#ifdef DEBUG
+ dlog("Deleting file server %s", fs->fs_host);
+#endif /* DEBUG */
+ if (fs->fs_flags & FSF_WANT)
+ wakeup_srvr(fs);
+
+ /*
+ * Remove from queue.
+ */
+ rem_que(&fs->fs_q);
+ /*
+ * (Possibly) call the private free routine.
+ */
+ if (fs->fs_private && fs->fs_prfree)
+ (*fs->fs_prfree)(fs->fs_private);
+
+ /*
+ * Free the net address
+ */
+ if (fs->fs_ip)
+ free((voidp) fs->fs_ip);
+
+ /*
+ * Free the host name.
+ */
+ free((voidp) fs->fs_host);
+
+ /*
+ * Discard the fserver object.
+ */
+ free((voidp) fs);
+ }
+}
+
+/*
+ * Free a file server
+ */
+void free_srvr P((fserver *fs));
+void free_srvr(fs)
+fserver *fs;
+{
+ if (--fs->fs_refc == 0) {
+ /*
+ * The reference count is now zero,
+ * so arrange for this node to be
+ * removed in AM_TTL seconds if no
+ * other mntfs is referencing it.
+ */
+ int ttl = (fs->fs_flags & (FSF_DOWN|FSF_ERROR)) ? 19 : AM_TTL;
+#ifdef DEBUG
+ dlog("Last hard reference to file server %s - will timeout in %ds", fs->fs_host, ttl);
+#endif /* DEBUG */
+ if (fs->fs_cid) {
+ untimeout(fs->fs_cid);
+ /*
+ * Turn off pinging - XXX
+ */
+ fs->fs_flags &= ~FSF_PINGING;
+ }
+ /*
+ * Keep structure lying around for a while
+ */
+ fs->fs_cid = timeout(ttl, timeout_srvr, (voidp) fs);
+ /*
+ * Mark the fileserver down and invalid again
+ */
+ fs->fs_flags &= ~FSF_VALID;
+ fs->fs_flags |= FSF_DOWN;
+ }
+}
+
+/*
+ * Make a duplicate fserver reference
+ */
+fserver *dup_srvr P((fserver *fs));
+fserver *dup_srvr(fs)
+fserver *fs;
+{
+ fs->fs_refc++;
+ return fs;
+}
+
+/*
+ * Log state change
+ */
+void srvrlog P((fserver *fs, char *state));
+void srvrlog(fs, state)
+fserver *fs;
+char *state;
+{
+ plog(XLOG_INFO, "file server %s type %s %s", fs->fs_host, fs->fs_type, state);
+}
diff --git a/usr.sbin/amd/amd/srvr_nfs.c b/usr.sbin/amd/amd/srvr_nfs.c
new file mode 100644
index 0000000..22e99fc
--- /dev/null
+++ b/usr.sbin/amd/amd/srvr_nfs.c
@@ -0,0 +1,719 @@
+/*
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)srvr_nfs.c 8.1 (Berkeley) 6/6/93
+ *
+ * $Id$
+ *
+ */
+
+/*
+ * NFS server modeling
+ */
+
+#include "am.h"
+#include <netdb.h>
+#include <rpc/pmap_prot.h>
+#include "mount.h"
+
+extern qelem nfs_srvr_list;
+qelem nfs_srvr_list = { &nfs_srvr_list, &nfs_srvr_list };
+
+typedef struct nfs_private {
+ u_short np_mountd; /* Mount daemon port number */
+ char np_mountd_inval; /* Port *may* be invalid */
+ int np_ping; /* Number of failed ping attempts */
+ time_t np_ttl; /* Time when server is thought dead */
+ int np_xid; /* RPC transaction id for pings */
+ int np_error; /* Error during portmap request */
+} nfs_private;
+
+static int np_xid; /* For NFS pings */
+#define NPXID_ALLOC() (++np_xid)
+/*#define NPXID_ALLOC() ((++np_xid&0x0fffffff) == 0 ? npxid_gc() : np_xid)*/
+
+/*
+ * Number of pings allowed to fail before host is declared down
+ * - three-fifths of the allowed mount time...
+#define MAX_ALLOWED_PINGS ((((ALLOWED_MOUNT_TIME + 5 * AM_PINGER - 1) * 3) / 5) / AM_PINGER)
+ */
+#define MAX_ALLOWED_PINGS (3 + /* for luck ... */ 1)
+
+/*
+ * How often to ping when starting a new server
+ */
+#define FAST_NFS_PING 3
+
+#if (FAST_NFS_PING * MAX_ALLOWED_PINGS) >= ALLOWED_MOUNT_TIME
+ #error: sanity check failed
+/*
+ you cannot do things this way...
+ sufficient fast pings must be given the chance to fail
+ within the allowed mount time
+ */
+#endif /* (FAST_NFS_PING * MAX_ALLOWED_PINGS) >= ALLOWED_MOUNT_TIME */
+
+static int ping_len;
+static char ping_buf[sizeof(struct rpc_msg) + 32];
+
+/*
+ * Flush any cached data
+ */
+void flush_srvr_nfs_cache P((void));
+void flush_srvr_nfs_cache()
+{
+ fserver *fs = 0;
+
+ ITER(fs, fserver, &nfs_srvr_list) {
+ nfs_private *np = (nfs_private *) fs->fs_private;
+ if (np) {
+ np->np_mountd_inval = TRUE;
+ np->np_error = -1;
+ }
+ }
+}
+
+/*
+ * Startup the NFS ping
+ */
+static void start_ping(P_void);
+static void start_ping()
+{
+ XDR ping_xdr;
+ struct rpc_msg ping_msg;
+
+ rpc_msg_init(&ping_msg, NFS_PROGRAM, NFS_VERSION, NFSPROC_NULL);
+
+ /*
+ * Create an XDR endpoint
+ */
+ xdrmem_create(&ping_xdr, ping_buf, sizeof(ping_buf), XDR_ENCODE);
+
+ /*
+ * Create the NFS ping message
+ */
+ if (!xdr_callmsg(&ping_xdr, &ping_msg)) {
+ plog(XLOG_ERROR, "Couldn't create ping RPC message");
+ going_down(3);
+ }
+
+ /*
+ * Find out how long it is
+ */
+ ping_len = xdr_getpos(&ping_xdr);
+
+ /*
+ * Destroy the XDR endpoint - we don't need it anymore
+ */
+ xdr_destroy(&ping_xdr);
+}
+
+
+/*
+ * Called when a portmap reply arrives
+ */
+/*ARGSUSED*/
+static void got_portmap P((voidp pkt, int len, struct sockaddr_in *sa, struct sockaddr_in *ia, voidp idv, int done));
+static void got_portmap(pkt, len, sa, ia, idv, done)
+voidp pkt;
+int len;
+struct sockaddr_in *sa;
+struct sockaddr_in *ia;
+voidp idv;
+int done;
+{
+ fserver *fs2 = (fserver *) idv;
+ fserver *fs = 0;
+
+ /*
+ * Find which fileserver we are talking about
+ */
+ ITER(fs, fserver, &nfs_srvr_list)
+ if (fs == fs2)
+ break;
+
+ if (fs == fs2) {
+ u_long port = 0; /* XXX - should be short but protocol is naff */
+ int error = done ? pickup_rpc_reply(pkt, len, (voidp) &port, xdr_u_long) : -1;
+ nfs_private *np = (nfs_private *) fs->fs_private;
+ if (!error && port) {
+#ifdef DEBUG
+ dlog("got port (%d) for mountd on %s", port, fs->fs_host);
+#endif /* DEBUG */
+ /*
+ * Grab the port number. Portmap sends back
+ * an unsigned long in native ordering, so it
+ * needs converting to a unsigned short in
+ * network ordering.
+ */
+ np->np_mountd = htons((u_short) port);
+ np->np_mountd_inval = FALSE;
+ np->np_error = 0;
+ } else {
+#ifdef DEBUG
+ dlog("Error fetching port for mountd on %s", fs->fs_host);
+#endif /* DEBUG */
+ /*
+ * Almost certainly no mountd running on remote host
+ */
+ np->np_error = error ? error : ETIMEDOUT;
+ }
+ if (fs->fs_flags & FSF_WANT)
+ wakeup_srvr(fs);
+ } else if (done) {
+#ifdef DEBUG
+ dlog("Got portmap for old port request");
+#endif /* DEBUG */
+ } else {
+#ifdef DEBUG
+ dlog("portmap request timed out");
+#endif /* DEBUG */
+ }
+}
+
+/*
+ * Obtain portmap information
+ */
+static int call_portmap P((fserver *fs, AUTH *auth, unsigned long prog, unsigned long vers, unsigned long prot));
+static int call_portmap(fs, auth, prog, vers, prot)
+fserver *fs;
+AUTH *auth;
+unsigned long prog, vers, prot;
+{
+ struct rpc_msg pmap_msg;
+ int len;
+ char iobuf[UDPMSGSIZE];
+ int error;
+ struct pmap pmap;
+
+ rpc_msg_init(&pmap_msg, PMAPPROG, PMAPVERS, (unsigned long) 0);
+ pmap.pm_prog = prog;
+ pmap.pm_vers = vers;
+ pmap.pm_prot = prot;
+ pmap.pm_port = 0;
+ len = make_rpc_packet(iobuf, sizeof(iobuf), PMAPPROC_GETPORT,
+ &pmap_msg, (voidp) &pmap, xdr_pmap, auth);
+ if (len > 0) {
+ struct sockaddr_in sin;
+ bzero((voidp) &sin, sizeof(sin));
+ sin = *fs->fs_ip;
+ sin.sin_port = htons(PMAPPORT);
+ error = fwd_packet(RPC_XID_PORTMAP, (voidp) iobuf, len,
+ &sin, &sin, (voidp) fs, got_portmap);
+ } else {
+ error = -len;
+ }
+ return error;
+}
+
+static void nfs_keepalive P((fserver*));
+
+static void recompute_portmap P((fserver *fs));
+static void recompute_portmap(fs)
+fserver *fs;
+{
+ int error;
+
+ if (nfs_auth)
+ error = 0;
+ else
+ error = make_nfs_auth();
+
+ if (error) {
+ nfs_private *np = (nfs_private *) fs->fs_private;
+ np->np_error = error;
+ } else {
+ call_portmap(fs, nfs_auth, MOUNTPROG,
+ MOUNTVERS, (unsigned long) IPPROTO_UDP);
+ }
+}
+
+/*
+ * This is called when we get a reply to an RPC ping.
+ * The value of id was taken from the nfs_private
+ * structure when the ping was transmitted.
+ */
+/*ARGSUSED*/
+static void nfs_pinged P((voidp pkt, int len, struct sockaddr_in *sp, struct sockaddr_in *tsp, voidp idv, int done));
+static void nfs_pinged(pkt, len, sp, tsp, idv, done)
+voidp pkt;
+int len;
+struct sockaddr_in *sp;
+struct sockaddr_in *tsp;
+voidp idv;
+int done;
+{
+ int xid = (int) idv;
+ fserver *fs;
+#ifdef DEBUG
+ int found_map = 0;
+#endif /* DEBUG */
+
+ if (!done)
+ return;
+
+ /*
+ * For each node...
+ */
+ ITER(fs, fserver, &nfs_srvr_list) {
+ nfs_private *np = (nfs_private *) fs->fs_private;
+ if (np->np_xid == xid) {
+ /*
+ * Reset the ping counter.
+ * Update the keepalive timer.
+ * Log what happened.
+ */
+ if (fs->fs_flags & FSF_DOWN) {
+ fs->fs_flags &= ~FSF_DOWN;
+ if (fs->fs_flags & FSF_VALID) {
+ srvrlog(fs, "is up");
+ } else {
+ if (np->np_ping > 1)
+ srvrlog(fs, "ok");
+#ifdef DEBUG
+ else
+ srvrlog(fs, "starts up");
+#endif
+ fs->fs_flags |= FSF_VALID;
+ }
+
+#ifdef notdef
+ /* why ??? */
+ if (fs->fs_flags & FSF_WANT)
+ wakeup_srvr(fs);
+#endif /* notdef */
+ map_flush_srvr(fs);
+ } else {
+ if (fs->fs_flags & FSF_VALID) {
+#ifdef DEBUG
+ dlog("file server %s type nfs is still up", fs->fs_host);
+#endif /* DEBUG */
+ } else {
+ if (np->np_ping > 1)
+ srvrlog(fs, "ok");
+ fs->fs_flags |= FSF_VALID;
+ }
+ }
+
+ /*
+ * Adjust ping interval
+ */
+ untimeout(fs->fs_cid);
+ fs->fs_cid = timeout(fs->fs_pinger, nfs_keepalive, (voidp) fs);
+
+ /*
+ * Update ttl for this server
+ */
+ np->np_ttl = clocktime() +
+ (MAX_ALLOWED_PINGS - 1) * FAST_NFS_PING + fs->fs_pinger - 1;
+
+ /*
+ * New RPC xid...
+ */
+ np->np_xid = NPXID_ALLOC();
+
+ /*
+ * Failed pings is zero...
+ */
+ np->np_ping = 0;
+
+ /*
+ * Recompute portmap information if not known
+ */
+ if (np->np_mountd_inval)
+ recompute_portmap(fs);
+
+#ifdef DEBUG
+ found_map++;
+#endif /* DEBUG */
+ break;
+ }
+ }
+
+#ifdef DEBUG
+ if (found_map == 0)
+ dlog("Spurious ping packet");
+#endif /* DEBUG */
+}
+
+/*
+ * Called when no ping-reply received
+ */
+static void nfs_timed_out P((fserver *fs));
+static void nfs_timed_out(fs)
+fserver *fs;
+{
+ nfs_private *np = (nfs_private *) fs->fs_private;
+
+ /*
+ * Another ping has failed
+ */
+ np->np_ping++;
+
+ /*
+ * Not known to be up any longer
+ */
+ if (FSRV_ISUP(fs)) {
+ fs->fs_flags &= ~FSF_VALID;
+ if (np->np_ping > 1)
+ srvrlog(fs, "not responding");
+ }
+
+ /*
+ * If ttl has expired then guess that it is dead
+ */
+ if (np->np_ttl < clocktime()) {
+ int oflags = fs->fs_flags;
+ if ((fs->fs_flags & FSF_DOWN) == 0) {
+ /*
+ * Server was up, but is now down.
+ */
+ srvrlog(fs, "is down");
+ fs->fs_flags |= FSF_DOWN|FSF_VALID;
+ /*
+ * Since the server is down, the portmap
+ * information may now be wrong, so it
+ * must be flushed from the local cache
+ */
+ flush_nfs_fhandle_cache(fs);
+ np->np_error = -1;
+#ifdef notdef
+ /*
+ * Pretend just one ping has failed now
+ */
+ np->np_ping = 1;
+#endif
+ } else {
+ /*
+ * Known to be down
+ */
+#ifdef DEBUG
+ if ((fs->fs_flags & FSF_VALID) == 0)
+ srvrlog(fs, "starts down");
+#endif
+ fs->fs_flags |= FSF_VALID;
+ }
+ if (oflags != fs->fs_flags && (fs->fs_flags & FSF_WANT))
+ wakeup_srvr(fs);
+ } else {
+#ifdef DEBUG
+ if (np->np_ping > 1)
+ dlog("%d pings to %s failed - at most %d allowed", np->np_ping, fs->fs_host, MAX_ALLOWED_PINGS);
+#endif /* DEBUG */
+ }
+
+ /*
+ * Run keepalive again
+ */
+ nfs_keepalive(fs);
+}
+
+/*
+ * Keep track of whether a server is alive
+ */
+static void nfs_keepalive P((fserver *fs));
+static void nfs_keepalive(fs)
+fserver *fs;
+{
+ int error;
+ nfs_private *np = (nfs_private *) fs->fs_private;
+ int fstimeo = -1;
+
+ /*
+ * Send an NFS ping to this node
+ */
+
+ if (ping_len == 0)
+ start_ping();
+
+ /*
+ * Queue the packet...
+ */
+ error = fwd_packet(MK_RPC_XID(RPC_XID_NFSPING, np->np_xid), (voidp) ping_buf,
+ ping_len, fs->fs_ip, (struct sockaddr_in *) 0, (voidp) np->np_xid, nfs_pinged);
+
+ /*
+ * See if a hard error occured
+ */
+ switch (error) {
+ case ENETDOWN:
+ case ENETUNREACH:
+ case EHOSTDOWN:
+ case EHOSTUNREACH:
+ np->np_ping = MAX_ALLOWED_PINGS; /* immediately down */
+ np->np_ttl = (time_t) 0;
+ /*
+ * This causes an immediate call to nfs_timed_out
+ * whenever the server was thought to be up.
+ * See +++ below.
+ */
+ fstimeo = 0;
+ break;
+
+ case 0:
+#ifdef DEBUG
+ dlog("Sent NFS ping to %s", fs->fs_host);
+#endif /* DEBUG */
+ break;
+ }
+
+#ifdef DEBUG
+ /*dlog("keepalive, ping = %d", np->np_ping);*/
+#endif /* DEBUG */
+
+ /*
+ * Back off the ping interval if we are not getting replies and
+ * the remote system is know to be down.
+ */
+ switch (fs->fs_flags & (FSF_DOWN|FSF_VALID)) {
+ case FSF_VALID: /* Up */
+ if (fstimeo < 0) /* +++ see above */
+ fstimeo = FAST_NFS_PING;
+ break;
+
+ case FSF_VALID|FSF_DOWN: /* Down */
+ fstimeo = fs->fs_pinger;
+ break;
+
+ default: /* Unknown */
+ fstimeo = FAST_NFS_PING;
+ break;
+ }
+
+#ifdef DEBUG
+ dlog("NFS timeout in %d seconds", fstimeo);
+#endif /* DEBUG */
+
+ fs->fs_cid = timeout(fstimeo, nfs_timed_out, (voidp) fs);
+}
+
+int nfs_srvr_port P((fserver *fs, u_short *port, voidp wchan));
+int nfs_srvr_port(fs, port, wchan)
+fserver *fs;
+u_short *port;
+voidp wchan;
+{
+ int error = -1;
+ if ((fs->fs_flags & FSF_VALID) == FSF_VALID) {
+ if ((fs->fs_flags & FSF_DOWN) == 0) {
+ nfs_private *np = (nfs_private *) fs->fs_private;
+ if (np->np_error == 0) {
+ *port = np->np_mountd;
+ error = 0;
+ } else {
+ error = np->np_error;
+ }
+ /*
+ * Now go get the port mapping again in case it changed.
+ * Note that it is used even if (np_mountd_inval)
+ * is True. The flag is used simply as an
+ * indication that the mountd may be invalid, not
+ * that it is known to be invalid.
+ */
+ if (np->np_mountd_inval)
+ recompute_portmap(fs);
+ else
+ np->np_mountd_inval = TRUE;
+ } else {
+ error = EWOULDBLOCK;
+ }
+ }
+ if (error < 0 && wchan && !(fs->fs_flags & FSF_WANT)) {
+ /*
+ * If a wait channel is supplied, and no
+ * error has yet occured, then arrange
+ * that a wakeup is done on the wait channel,
+ * whenever a wakeup is done on this fs node.
+ * Wakeup's are done on the fs node whenever
+ * it changes state - thus causing control to
+ * come back here and new, better things to happen.
+ */
+ fs->fs_flags |= FSF_WANT;
+ sched_task(wakeup_task, wchan, (voidp) fs);
+ }
+ return error;
+}
+
+static void start_nfs_pings P((fserver *fs, int pingval));
+static void start_nfs_pings(fs, pingval)
+fserver *fs;
+int pingval;
+{
+ if (!(fs->fs_flags & FSF_PINGING)) {
+ fs->fs_flags |= FSF_PINGING;
+ if (fs->fs_cid)
+ untimeout(fs->fs_cid);
+ if (pingval < 0) {
+ srvrlog(fs, "wired up");
+ fs->fs_flags |= FSF_VALID;
+ fs->fs_flags &= ~FSF_DOWN;
+ } else {
+ nfs_keepalive(fs);
+ }
+ } else {
+#ifdef DEBUG
+ dlog("Already running pings to %s", fs->fs_host);
+#endif /* DEBUG */
+ }
+}
+
+/*
+ * Find an nfs server for a host.
+ */
+fserver *find_nfs_srvr P((mntfs *mf));
+fserver *find_nfs_srvr(mf)
+mntfs *mf;
+{
+ fserver *fs;
+ struct hostent *hp = 0;
+ char *host = mf->mf_fo->opt_rhost;
+ struct sockaddr_in *ip;
+ nfs_private *np;
+ int pingval;
+
+ /*
+ * Get ping interval from mount options.
+ * Current only used to decide whether pings
+ * are required or not. < 0 = no pings.
+ */
+ { struct mntent mnt;
+ mnt.mnt_opts = mf->mf_mopts;
+ pingval = hasmntval(&mnt, "ping");
+#ifdef HAS_TCP_NFS
+ /*
+ * Over TCP mount, don't bother to do pings.
+ * This is experimental - maybe you want to
+ * do pings anyway...
+ */
+ if (pingval == 0 && hasmntopt(&mnt, "tcp"))
+ pingval = -1;
+#endif /* HAS_TCP_NFS */
+ }
+
+
+ /*
+ * lookup host address and canonical name
+ */
+ hp = gethostbyname(host);
+
+ /*
+ * New code from Bob Harris <harris@basil-rathbone.mit.edu>
+ * Use canonical name to keep track of file server
+ * information. This way aliases do not generate
+ * multiple NFS pingers. (Except when we're normalizing
+ * hosts.)
+ */
+ if (hp && !normalize_hosts) host = hp->h_name;
+
+ ITER(fs, fserver, &nfs_srvr_list) {
+ if (STREQ(host, fs->fs_host)) {
+ start_nfs_pings(fs, pingval);
+ fs->fs_refc++;
+ return fs;
+ }
+ }
+
+
+
+ /*
+ * Get here if we can't find an entry
+ */
+ if (hp) {
+ switch (hp->h_addrtype) {
+ case AF_INET:
+ ip = ALLOC(sockaddr_in);
+ bzero((voidp) ip, sizeof(*ip));
+ ip->sin_family = AF_INET;
+ bcopy((voidp) hp->h_addr, (voidp) &ip->sin_addr, sizeof(ip->sin_addr));
+
+ ip->sin_port = htons(NFS_PORT);
+ break;
+
+ default:
+ ip = 0;
+ break;
+ }
+ } else {
+ plog(XLOG_USER, "Unknown host: %s", host);
+ ip = 0;
+ }
+
+ /*
+ * Allocate a new server
+ */
+ fs = ALLOC(fserver);
+ fs->fs_refc = 1;
+ fs->fs_host = strdup(hp ? hp->h_name : "unknown_hostname");
+ if (normalize_hosts) host_normalize(&fs->fs_host);
+ fs->fs_ip = ip;
+ fs->fs_cid = 0;
+ if (ip) {
+ fs->fs_flags = FSF_DOWN; /* Starts off down */
+ } else {
+ fs->fs_flags = FSF_ERROR|FSF_VALID;
+ mf->mf_flags |= MFF_ERROR;
+ mf->mf_error = ENOENT;
+ }
+ fs->fs_type = "nfs";
+ fs->fs_pinger = AM_PINGER;
+ np = ALLOC(nfs_private);
+ bzero((voidp) np, sizeof(*np));
+ np->np_mountd_inval = TRUE;
+ np->np_xid = NPXID_ALLOC();
+ np->np_error = -1;
+ /*
+ * Initially the server will be deemed dead after
+ * MAX_ALLOWED_PINGS of the fast variety have failed.
+ */
+ np->np_ttl = clocktime() + MAX_ALLOWED_PINGS * FAST_NFS_PING - 1;
+ fs->fs_private = (voidp) np;
+ fs->fs_prfree = (void (*)()) free;
+
+ if (!(fs->fs_flags & FSF_ERROR)) {
+ /*
+ * Start of keepalive timer
+ */
+ start_nfs_pings(fs, pingval);
+ }
+
+ /*
+ * Add to list of servers
+ */
+ ins_que(&fs->fs_q, &nfs_srvr_list);
+
+ return fs;
+}
diff --git a/usr.sbin/amd/amd/ufs_ops.c b/usr.sbin/amd/amd/ufs_ops.c
new file mode 100644
index 0000000..e586ac2
--- /dev/null
+++ b/usr.sbin/amd/amd/ufs_ops.c
@@ -0,0 +1,178 @@
+/*
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ufs_ops.c 8.1 (Berkeley) 6/6/93
+ *
+ * $Id$
+ *
+ */
+
+#include "am.h"
+
+#ifdef HAS_UFS
+
+#include <sys/stat.h>
+#ifdef NFS_3
+typedef nfs_fh fhandle_t;
+#endif /* NFS_3 */
+
+#ifdef UFS_HDR
+#include UFS_HDR
+#endif /* UFS_HDR */
+
+#include <sys/mount.h>
+
+/*
+ * UN*X file system
+ */
+
+/*
+ * UFS needs local filesystem and device.
+ */
+static char *ufs_match P((am_opts *fo));
+static char *ufs_match(fo)
+am_opts *fo;
+{
+ if (!fo->opt_dev) {
+ plog(XLOG_USER, "ufs: no device specified");
+ return 0;
+ }
+
+#ifdef DEBUG
+ dlog("UFS: mounting device \"%s\" on \"%s\"",
+ fo->opt_dev, fo->opt_fs);
+#endif /* DEBUG */
+
+ /*
+ * Determine magic cookie to put in mtab
+ */
+ return strdup(fo->opt_dev);
+}
+
+static mount_ufs(dir, fs_name, opts)
+char *dir;
+char *fs_name;
+char *opts;
+{
+ struct ufs_args ufs_args;
+ struct mntent mnt;
+ int flags;
+
+ /*
+ * Figure out the name of the file system type.
+ */
+#ifdef M_NEWTYPE
+ char *type = MOUNT_TYPE_UFS;
+#else
+ int type = MOUNT_TYPE_UFS;
+#endif /* M_NEWTYPE */
+
+ bzero((voidp) &ufs_args, sizeof(ufs_args)); /* Paranoid */
+
+ /*
+ * Fill in the mount structure
+ */
+ mnt.mnt_dir = dir;
+ mnt.mnt_fsname = fs_name;
+ mnt.mnt_type = MTAB_TYPE_UFS;
+ mnt.mnt_opts = opts;
+ mnt.mnt_freq = 1;
+ mnt.mnt_passno = 2;
+
+ flags = compute_mount_flags(&mnt);
+
+#ifdef ULTRIX_HACK
+ ufs_args.ufs_flags = flags;
+ ufs_args.ufs_pgthresh = 64; /* 64K - XXX */
+ flags &= M_RDONLY;
+#else
+ ufs_args.fspec = fs_name;
+#endif /* ULTRIX_HACK */
+
+ /*
+ * Call generic mount routine
+ */
+ return mount_fs(&mnt, flags, (caddr_t) &ufs_args, 0, type);
+}
+
+/*ARGSUSED*/
+static int ufs_fmount(mf)
+mntfs *mf;
+{
+ int error;
+
+ error = mount_ufs(mf->mf_mount, mf->mf_info, mf->mf_mopts);
+ if (error) {
+ errno = error;
+ plog(XLOG_ERROR, "mount_ufs: %m");
+ return error;
+ }
+
+ return 0;
+}
+
+static int ufs_fumount(mf)
+mntfs *mf;
+{
+ return UMOUNT_FS(mf->mf_mount);
+}
+
+/*
+ * Ops structure
+ */
+am_ops ufs_ops = {
+ "ufs",
+ ufs_match,
+ 0, /* ufs_init */
+ auto_fmount,
+ ufs_fmount,
+ auto_fumount,
+ ufs_fumount,
+ efs_lookuppn,
+ efs_readdir,
+ 0, /* ufs_readlink */
+ 0, /* ufs_mounted */
+ 0, /* ufs_umounted */
+ find_afs_srvr,
+#ifdef FLUSH_KERNEL_NAME_CACHE
+ FS_MKMNT|FS_NOTIMEOUT|FS_UBACKGROUND|FS_AMQINFO
+#else /* FLUSH_KERNEL_NAME_CACHE */
+ FS_MKMNT|FS_NOTIMEOUT|FS_UBACKGROUND|FS_AMQINFO
+#endif /* FLUSH_KERNEL_NAME_CACHE */
+};
+
+#endif /* HAS_UFS */
diff --git a/usr.sbin/amd/amd/umount_fs.c b/usr.sbin/amd/amd/umount_fs.c
new file mode 100644
index 0000000..bb48533
--- /dev/null
+++ b/usr.sbin/amd/amd/umount_fs.c
@@ -0,0 +1,225 @@
+/*
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)umount_fs.c 8.1 (Berkeley) 6/6/93
+ *
+ * $Id$
+ *
+ */
+
+#include "am.h"
+
+#ifdef NEED_UMOUNT_BSD
+
+int umount_fs P((char *fs_name));
+int umount_fs(fs_name)
+char *fs_name;
+{
+ int error;
+
+eintr:
+ error = unmount(fs_name, 0);
+ if (error < 0)
+ error = errno;
+
+ switch (error) {
+ case EINVAL:
+ case ENOTBLK:
+ case ENOENT:
+ plog(XLOG_WARNING, "unmount: %s is not mounted", fs_name);
+ error = 0; /* Not really an error */
+ break;
+
+ case EINTR:
+#ifdef DEBUG
+ /* not sure why this happens, but it does. ask kirk one day... */
+ dlog("%s: unmount: %m", fs_name);
+#endif /* DEBUG */
+ goto eintr;
+
+#ifdef DEBUG
+ default:
+ dlog("%s: unmount: %m", fs_name);
+ break;
+#endif /* DEBUG */
+ }
+
+ return error;
+}
+
+#endif /* NEED_UMOUNT_BSD */
+
+#ifdef NEED_UMOUNT_OSF
+
+#include <sys/mount.h> /* For MNT_NOFORCE */
+
+int umount_fs(fs_name)
+char *fs_name;
+{
+ int error;
+
+eintr:
+ error = umount(fs_name, MNT_NOFORCE);
+ if (error < 0)
+ error = errno;
+
+ switch (error) {
+ case EINVAL:
+ case ENOTBLK:
+ plog(XLOG_WARNING, "unmount: %s is not mounted", fs_name);
+ error = 0; /* Not really an error */
+ break;
+
+ case ENOENT:
+ plog(XLOG_ERROR, "mount point %s: %m", fs_name);
+ break;
+
+ case EINTR:
+#ifdef DEBUG
+ /* not sure why this happens, but it does. ask kirk one day... */
+ dlog("%s: unmount: %m", fs_name);
+#endif /* DEBUG */
+ goto eintr;
+
+#ifdef DEBUG
+ default:
+ dlog("%s: unmount: %m", fs_name);
+ break;
+#endif /* DEBUG */
+ }
+
+ return error;
+}
+
+#endif /* NEED_UMOUNT_OSF */
+
+#ifdef NEED_UMOUNT_FS
+
+int umount_fs(fs_name)
+char *fs_name;
+{
+ mntlist *mlist, *mp, *mp_save = 0;
+ int error = 0;
+
+ mp = mlist = read_mtab(fs_name);
+
+ /*
+ * Search the mount table looking for
+ * the correct (ie last) matching entry
+ */
+ while (mp) {
+ if (strcmp(mp->mnt->mnt_fsname, fs_name) == 0 ||
+ strcmp(mp->mnt->mnt_dir, fs_name) == 0)
+ mp_save = mp;
+ mp = mp->mnext;
+ }
+
+ if (mp_save) {
+#ifdef DEBUG
+ dlog("Trying unmount(%s)", mp_save->mnt->mnt_dir);
+#endif /* DEBUG */
+ /*
+ * This unmount may hang leaving this
+ * process with an exlusive lock on
+ * /etc/mtab. Therefore it is necessary
+ * to unlock mtab, do the unmount, then
+ * lock mtab (again) and reread it and
+ * finally update it.
+ */
+ unlock_mntlist();
+ if (UNMOUNT_TRAP(mp_save->mnt) < 0) {
+ switch (error = errno) {
+ case EINVAL:
+ case ENOTBLK:
+ plog(XLOG_WARNING, "unmount: %s is not mounted", mp_save->mnt->mnt_dir);
+ error = 0; /* Not really an error */
+ break;
+
+ case ENOENT:
+ plog(XLOG_ERROR, "mount point %s: %m", mp_save->mnt->mnt_dir);
+ break;
+
+ default:
+#ifdef DEBUG
+ dlog("%s: unmount: %m", mp_save->mnt->mnt_dir);
+#endif /* DEBUG */
+ break;
+ }
+ }
+#ifdef DEBUG
+ dlog("Finished unmount(%s)", mp_save->mnt->mnt_dir);
+#endif
+
+
+#ifdef UPDATE_MTAB
+ if (!error) {
+ free_mntlist(mlist);
+ mp = mlist = read_mtab(fs_name);
+
+ /*
+ * Search the mount table looking for
+ * the correct (ie last) matching entry
+ */
+ mp_save = 0;
+ while (mp) {
+ if (strcmp(mp->mnt->mnt_fsname, fs_name) == 0 ||
+ strcmp(mp->mnt->mnt_dir, fs_name) == 0)
+ mp_save = mp;
+ mp = mp->mnext;
+ }
+
+ if (mp_save) {
+ mnt_free(mp_save->mnt);
+ mp_save->mnt = 0;
+ rewrite_mtab(mlist);
+ }
+ }
+#endif /* UPDATE_MTAB */
+ } else {
+ plog(XLOG_ERROR, "Couldn't find how to unmount %s", fs_name);
+ /*
+ * Assume it is already unmounted
+ */
+ error = 0;
+ }
+
+ free_mntlist(mlist);
+
+ return error;
+}
+
+#endif /* NEED_UMOUNT_FS */
diff --git a/usr.sbin/amd/amd/util.c b/usr.sbin/amd/amd/util.c
new file mode 100644
index 0000000..837a9cd
--- /dev/null
+++ b/usr.sbin/amd/amd/util.c
@@ -0,0 +1,648 @@
+/*
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)util.c 8.1 (Berkeley) 6/6/93
+ *
+ * $Id$
+ *
+ */
+
+/*
+ * Utils
+ */
+
+#include "am.h"
+#include <ctype.h>
+#include <sys/stat.h>
+#include <netdb.h>
+
+
+char *strnsave(str, len)
+Const char *str;
+int len;
+{
+ char *sp = (char *) xmalloc(len+1);
+
+ bcopy(str, sp, len);
+ sp[len] = 0;
+
+ return sp;
+}
+
+char *strdup(s)
+Const char *s;
+{
+ return strnsave(s, strlen(s));
+}
+
+/*
+ * Concatenate three strings and store in buffer pointed to
+ * by p, making p large enough to hold the strings
+ */
+char *str3cat(p, s1, s2, s3)
+char *p;
+char *s1;
+char *s2;
+char *s3;
+{
+ int l1 = strlen(s1);
+ int l2 = strlen(s2);
+ int l3 = strlen(s3);
+ p = (char *) xrealloc(p, l1 + l2 + l3 + 1);
+ bcopy(s1, p, l1);
+ bcopy(s2, p + l1, l2);
+ bcopy(s3, p + l1 + l2, l3 + 1);
+ return p;
+}
+
+char *strealloc(p, s)
+char *p;
+char *s;
+{
+ int len = strlen(s) + 1;
+
+ p = (char *) xrealloc((voidp) p, len);
+
+ strcpy(p, s);
+#ifdef DEBUG_MEM
+ malloc_verify();
+#endif /* DEBUG_MEM */
+ return p;
+}
+
+char **strsplit P((char *s, int ch, int qc));
+char **strsplit(s, ch, qc)
+char *s;
+int ch;
+int qc;
+{
+ char **ivec;
+ int ic = 0;
+ int done = 0;
+
+ ivec = (char **) xmalloc((ic+1)*sizeof(char *));
+
+ while (!done) {
+ char *v;
+ /*
+ * skip to split char
+ */
+ while (*s && (ch == ' ' ? (isascii(*s) && isspace(*s)) : *s == ch))
+ *s++ = '\0';
+
+ /*
+ * End of string?
+ */
+ if (!*s)
+ break;
+
+ /*
+ * remember start of string
+ */
+ v = s;
+
+ /*
+ * skip to split char
+ */
+ while (*s && !(ch == ' ' ? (isascii(*s) && isspace(*s)) : *s == ch)) {
+ if (*s++ == qc) {
+ /*
+ * Skip past string.
+ */
+ s++;
+ while (*s && *s != qc)
+ s++;
+ if (*s == qc)
+ s++;
+ }
+ }
+
+ if (!*s)
+ done = 1;
+ *s++ = '\0';
+
+ /*
+ * save string in new ivec slot
+ */
+ ivec[ic++] = v;
+ ivec = (char **) xrealloc((voidp) ivec, (ic+1)*sizeof(char *));
+#ifdef DEBUG
+ Debug(D_STR)
+ plog(XLOG_DEBUG, "strsplit saved \"%s\"", v);
+#endif /* DEBUG */
+ }
+
+#ifdef DEBUG
+ Debug(D_STR)
+ plog(XLOG_DEBUG, "strsplit saved a total of %d strings", ic);
+#endif /* DEBUG */
+
+ ivec[ic] = 0;
+
+ return ivec;
+}
+
+/*
+ * Strip off the trailing part of a domain
+ * to produce a short-form domain relative
+ * to the local host domain.
+ * Note that this has no effect if the domain
+ * names do not have the same number of
+ * components. If that restriction proves
+ * to be a problem then the loop needs recoding
+ * to skip from right to left and do partial
+ * matches along the way -- ie more expensive.
+ */
+static void domain_strip P((char *otherdom, char *localdom));
+static void domain_strip(otherdom, localdom)
+char *otherdom, *localdom;
+{
+#ifdef PARTIAL_DOMAINS
+ char *p1 = otherdom-1;
+ char *p2 = localdom-1;
+
+ do {
+ if (p1 = strchr(p1+1, '.'))
+ if (p2 = strchr(p2+1, '.'))
+ if (strcmp(p1+1, p2+1) == 0) {
+ *p1 = '\0';
+ break;
+ }
+ } while (p1 && p2);
+#else
+ char *p1, *p2;
+
+ if ((p1 = strchr(otherdom, '.')) &&
+ (p2 = strchr(localdom, '.')) &&
+ (strcmp(p1+1, p2+1) == 0))
+ *p1 = '\0';
+#endif /* PARTIAL_DOMAINS */
+}
+
+/*
+ * Normalize a host name
+ */
+void host_normalize P((char **chp));
+void host_normalize(chp)
+char **chp;
+{
+ /*
+ * Normalize hosts is used to resolve host name aliases
+ * and replace them with the standard-form name.
+ * Invoked with "-n" command line option.
+ */
+ if (normalize_hosts) {
+ struct hostent *hp;
+ clock_valid = 0;
+ hp = gethostbyname(*chp);
+ if (hp && hp->h_addrtype == AF_INET) {
+#ifdef DEBUG
+ dlog("Hostname %s normalized to %s", *chp, hp->h_name);
+#endif /* DEBUG */
+ *chp = strealloc(*chp, hp->h_name);
+ }
+ }
+ domain_strip(*chp, hostd);
+}
+
+/*
+ * Make a dotted quad from a 32bit IP address
+ * addr is in network byte order.
+ * sizeof(buf) needs to be at least 16.
+ */
+char *inet_dquad P((char *buf, unsigned long addr));
+char *inet_dquad(buf, addr)
+char *buf;
+unsigned long addr;
+{
+ addr = ntohl(addr);
+ sprintf(buf, "%d.%d.%d.%d",
+ ((addr >> 24) & 0xff),
+ ((addr >> 16) & 0xff),
+ ((addr >> 8) & 0xff),
+ ((addr >> 0) & 0xff));
+ return buf;
+}
+
+/*
+ * Keys are not allowed to contain " ' ! or ; to avoid
+ * problems with macro expansions.
+ */
+static char invalid_keys[] = "\"'!;@ \t\n";
+int valid_key P((char *key));
+int valid_key(key)
+char *key;
+{
+ while (*key)
+ if (strchr(invalid_keys, *key++))
+ return FALSE;
+ return TRUE;
+}
+
+void going_down P((int rc));
+void going_down(rc)
+int rc;
+{
+ if (foreground) {
+ if (amd_state != Start) {
+ if (amd_state != Done)
+ return;
+ unregister_amq();
+ }
+ }
+ if (foreground) {
+ plog(XLOG_INFO, "Finishing with status %d", rc);
+ } else {
+#ifdef DEBUG
+ dlog("background process exiting with status %d", rc);
+#endif /* DEBUG */
+ }
+
+ exit(rc);
+}
+
+
+int bind_resv_port P((int so, u_short *pp));
+int bind_resv_port(so, pp)
+int so;
+u_short *pp;
+{
+ struct sockaddr_in sin;
+ int rc;
+ unsigned short port;
+
+ bzero((voidp) &sin, sizeof(sin));
+ sin.sin_family = AF_INET;
+
+ port = IPPORT_RESERVED;
+
+ do {
+ --port;
+ sin.sin_port = htons(port);
+ rc = bind(so, (struct sockaddr *) &sin, sizeof(sin));
+ } while (rc < 0 && port > IPPORT_RESERVED/2);
+
+ if (pp && rc == 0)
+ *pp = port;
+ return rc;
+}
+
+void forcibly_timeout_mp P((am_node *mp));
+void forcibly_timeout_mp(mp)
+am_node *mp;
+{
+ mntfs *mf = mp->am_mnt;
+ /*
+ * Arrange to timeout this node
+ */
+ if (mf && ((mp->am_flags & AMF_ROOT) ||
+ (mf->mf_flags & (MFF_MOUNTING|MFF_UNMOUNTING)))) {
+ if (!(mf->mf_flags & MFF_UNMOUNTING))
+ plog(XLOG_WARNING, "ignoring timeout request for active node %s", mp->am_path);
+ } else {
+ plog(XLOG_INFO, "\"%s\" forcibly timed out", mp->am_path);
+ mp->am_flags &= ~AMF_NOTIMEOUT;
+ mp->am_ttl = clocktime();
+ reschedule_timeout_mp();
+ }
+}
+
+void mf_mounted P((mntfs *mf));
+void mf_mounted(mf)
+mntfs *mf;
+{
+ int quoted;
+ int wasmounted = mf->mf_flags & MFF_MOUNTED;
+
+ if (!wasmounted) {
+ /*
+ * If this is a freshly mounted
+ * filesystem then update the
+ * mntfs structure...
+ */
+ mf->mf_flags |= MFF_MOUNTED;
+ mf->mf_error = 0;
+
+ /*
+ * Do mounted callback
+ */
+ if (mf->mf_ops->mounted)
+ (*mf->mf_ops->mounted)(mf);
+
+ mf->mf_fo = 0;
+ }
+
+ /*
+ * Log message
+ */
+ quoted = strchr(mf->mf_info, ' ') != 0;
+ plog(XLOG_INFO, "%s%s%s %s fstype %s on %s",
+ quoted ? "\"" : "",
+ mf->mf_info,
+ quoted ? "\"" : "",
+ wasmounted ? "referenced" : "mounted",
+ mf->mf_ops->fs_type, mf->mf_mount);
+}
+
+void am_mounted P((am_node *mp));
+void am_mounted(mp)
+am_node *mp;
+{
+ mntfs *mf = mp->am_mnt;
+
+ mf_mounted(mf);
+
+ /*
+ * Patch up path for direct mounts
+ */
+ if (mp->am_parent && mp->am_parent->am_mnt->mf_ops == &dfs_ops)
+ mp->am_path = str3cat(mp->am_path, mp->am_parent->am_path, "/", ".");
+
+ /*
+ * Check whether this mount should be cached permanently
+ */
+ if (mf->mf_ops->fs_flags & FS_NOTIMEOUT) {
+ mp->am_flags |= AMF_NOTIMEOUT;
+ } else if (mf->mf_mount[1] == '\0' && mf->mf_mount[0] == '/') {
+ mp->am_flags |= AMF_NOTIMEOUT;
+ } else {
+ struct mntent mnt;
+ if (mf->mf_mopts) {
+ mnt.mnt_opts = mf->mf_mopts;
+ if (hasmntopt(&mnt, "nounmount"))
+ mp->am_flags |= AMF_NOTIMEOUT;
+ if ((mp->am_timeo = hasmntval(&mnt, "utimeout")) == 0)
+ mp->am_timeo = am_timeo;
+ }
+ }
+
+ /*
+ * If this node is a symlink then
+ * compute the length of the returned string.
+ */
+ if (mp->am_fattr.type == NFLNK)
+ mp->am_fattr.size = strlen(mp->am_link ? mp->am_link : mp->am_mnt->mf_mount);
+
+ /*
+ * Record mount time
+ */
+ mp->am_fattr.mtime.seconds = mp->am_stats.s_mtime = clocktime();
+ new_ttl(mp);
+ /*
+ * Update mtime of parent node
+ */
+ if (mp->am_parent && mp->am_parent->am_mnt)
+ mp->am_parent->am_fattr.mtime.seconds = mp->am_stats.s_mtime;
+
+
+ /*
+ * Update stats
+ */
+ amd_stats.d_mok++;
+}
+
+int mount_node P((am_node *mp));
+int mount_node(mp)
+am_node *mp;
+{
+ mntfs *mf = mp->am_mnt;
+ int error;
+
+ mf->mf_flags |= MFF_MOUNTING;
+ error = (*mf->mf_ops->mount_fs)(mp);
+ mf = mp->am_mnt;
+ if (error >= 0)
+ mf->mf_flags &= ~MFF_MOUNTING;
+ if (!error && !(mf->mf_ops->fs_flags & FS_MBACKGROUND)) {
+ /* ...but see ifs_mount */
+ am_mounted(mp);
+ }
+
+ return error;
+}
+
+void am_unmounted P((am_node *mp));
+void am_unmounted(mp)
+am_node *mp;
+{
+ mntfs *mf = mp->am_mnt;
+
+ if (!foreground) /* firewall - should never happen */
+ return;
+
+#ifdef DEBUG
+ /*dlog("in am_unmounted(), foreground = %d", foreground);*/
+#endif /* DEBUG */
+
+ /*
+ * Do unmounted callback
+ */
+ if (mf->mf_ops->umounted)
+ (*mf->mf_ops->umounted)(mp);
+
+ /*
+ * Update mtime of parent node
+ */
+ if (mp->am_parent && mp->am_parent->am_mnt)
+ mp->am_parent->am_fattr.mtime.seconds = clocktime();
+
+ free_map(mp);
+}
+
+int auto_fmount P((am_node *mp));
+int auto_fmount(mp)
+am_node *mp;
+{
+ mntfs *mf = mp->am_mnt;
+ return (*mf->mf_ops->fmount_fs)(mf);
+}
+
+int auto_fumount P((am_node *mp));
+int auto_fumount(mp)
+am_node *mp;
+{
+ mntfs *mf = mp->am_mnt;
+ return (*mf->mf_ops->fumount_fs)(mf);
+}
+
+/*
+ * Fork the automounter
+ *
+ * TODO: Need a better strategy for handling errors
+ */
+static int dofork(P_void);
+static int dofork()
+{
+ int pid;
+top:
+ pid = fork();
+
+ if (pid < 0) {
+ sleep(1);
+ goto top;
+ }
+
+ if (pid == 0) {
+ mypid = getpid();
+ foreground = 0;
+ }
+
+ return pid;
+}
+
+int background(P_void);
+int background()
+{
+ int pid = dofork();
+ if (pid == 0) {
+#ifdef DEBUG
+ dlog("backgrounded");
+#endif
+ foreground = 0;
+ }
+
+ return pid;
+}
+
+/*
+ * Make all the directories in the path.
+ */
+int mkdirs P((char *path, int mode));
+int mkdirs(path, mode)
+char *path;
+int mode;
+{
+ /*
+ * take a copy in case path is in readonly store
+ */
+ char *p2 = strdup(path);
+ char *sp = p2;
+ struct stat stb;
+ int error_so_far = 0;
+
+ /*
+ * Skip through the string make the directories.
+ * Mostly ignore errors - the result is tested at the end.
+ *
+ * This assumes we are root so that we can do mkdir in a
+ * mode 555 directory...
+ */
+ while (sp = strchr(sp+1, '/')) {
+ *sp = '\0';
+ if (mkdir(p2, mode) < 0) {
+ error_so_far = errno;
+ } else {
+#ifdef DEBUG
+ dlog("mkdir(%s)", p2);
+#endif
+ }
+ *sp = '/';
+ }
+
+ if (mkdir(p2, mode) < 0) {
+ error_so_far = errno;
+ } else {
+#ifdef DEBUG
+ dlog("mkdir(%s)", p2);
+#endif
+ }
+
+#ifdef SUNOS4_WORKAROUND
+ /*
+ * Do a sync - if we do rmdirs() immediately
+ * and then the system crashes it leaves
+ * the filesystem in a state that fsck -p
+ * can't fix. (Observed more than once on
+ * SunOS 4 ...)
+ *
+ * The problem was caused by a bug somewhere
+ * in the UFS code which has since been fixed
+ * (at least at Berkeley).
+ *
+ * Attempted workaround - XXX.
+ */
+ sync();
+#endif /* SUNOS4_WORKAROUND */
+
+ free(p2);
+
+ return stat(path, &stb) == 0 &&
+ (stb.st_mode & S_IFMT) == S_IFDIR ? 0 : error_so_far;
+}
+
+/*
+ * Remove as many directories in the path as possible.
+ * Give up if the directory doesn't appear to have
+ * been created by Amd (not mode dr-x) or an rmdir
+ * fails for any reason.
+ */
+void rmdirs P((char *dir));
+void rmdirs(dir)
+char *dir;
+{
+ char *xdp = strdup(dir);
+ char *dp;
+
+ do {
+ struct stat stb;
+ /*
+ * Try to find out whether this was
+ * created by amd. Do this by checking
+ * for owner write permission.
+ */
+ if (stat(xdp, &stb) == 0 && (stb.st_mode & 0200) == 0) {
+ if (rmdir(xdp) < 0) {
+ if (errno != ENOTEMPTY &&
+ errno != EBUSY &&
+ errno != EEXIST &&
+ errno != EINVAL)
+ plog(XLOG_ERROR, "rmdir(%s): %m", xdp);
+ break;
+ } else {
+#ifdef DEBUG
+ dlog("rmdir(%s)", xdp);
+#endif
+ }
+ } else {
+ break;
+ }
+ dp = strrchr(xdp, '/');
+ if (dp)
+ *dp = '\0';
+ } while (dp && dp > xdp);
+ free(xdp);
+}
diff --git a/usr.sbin/amd/amd/wire.c b/usr.sbin/amd/amd/wire.c
new file mode 100644
index 0000000..340a284
--- /dev/null
+++ b/usr.sbin/amd/amd/wire.c
@@ -0,0 +1,281 @@
+/*
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)wire.c 8.1 (Berkeley) 6/6/93
+ *
+ * $Id$
+ *
+ */
+
+/*
+ * This function returns the subnet (address&netmask) for the primary network
+ * interface. If the resulting address has an entry in the hosts file, the
+ * corresponding name is retuned, otherwise the address is returned in
+ * standard internet format.
+ * As a side-effect, a list of local IP/net address is recorded for use
+ * by the islocalnet() function.
+ *
+ * Derived from original by Paul Anderson (23/4/90)
+ * Updates from Dirk Grunwald (11/11/91)
+ */
+
+#include "am.h"
+
+#include <sys/ioctl.h>
+
+#define NO_SUBNET "notknown"
+
+/*
+ * List of locally connected networks
+ */
+typedef struct addrlist addrlist;
+struct addrlist {
+ addrlist *ip_next;
+ unsigned long ip_addr;
+ unsigned long ip_mask;
+};
+static addrlist *localnets = 0;
+
+#ifdef SIOCGIFFLAGS
+#ifdef STELLIX
+#include <sys/sema.h>
+#endif /* STELLIX */
+#include <net/if.h>
+#include <netdb.h>
+
+#if defined(IFF_LOCAL_LOOPBACK) && !defined(IFF_LOOPBACK)
+#define IFF_LOOPBACK IFF_LOCAL_LOOPBACK
+#endif
+
+#define GFBUFLEN 1024
+#define clist (ifc.ifc_ifcu.ifcu_req)
+#define count (ifc.ifc_len/sizeof(struct ifreq))
+
+char *getwire P((void));
+char *getwire()
+{
+ struct hostent *hp;
+ struct netent *np;
+ struct ifconf ifc;
+ struct ifreq *ifr;
+ caddr_t cp, cplim;
+ unsigned long address, netmask, subnet;
+ char buf[GFBUFLEN], *s;
+ int sk = -1;
+ char *netname = 0;
+
+ /*
+ * Get suitable socket
+ */
+ if ((sk = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
+ goto out;
+
+ /*
+ * Fill in ifconf details
+ */
+ ifc.ifc_len = sizeof buf;
+ ifc.ifc_buf = buf;
+
+ /*
+ * Get network interface configurations
+ */
+ if (ioctl(sk, SIOCGIFCONF, (caddr_t) &ifc) < 0)
+ goto out;
+
+ /*
+ * Upper bound on array
+ */
+ cplim = buf + ifc.ifc_len;
+
+ /*
+ * This is some magic to cope with both "traditional" and the
+ * new 4.4BSD-style struct sockaddrs. The new structure has
+ * variable length and a size field to support longer addresses.
+ * AF_LINK is a new definition for 4.4BSD.
+ */
+#ifdef AF_LINK
+#define max(a, b) ((a) > (b) ? (a) : (b))
+#define size(ifr) (max((ifr)->ifr_addr.sa_len, sizeof((ifr)->ifr_addr)) + sizeof(ifr->ifr_name))
+#else
+#define size(ifr) sizeof(*ifr)
+#endif
+ /*
+ * Scan the list looking for a suitable interface
+ */
+ for (cp = buf; cp < cplim; cp += size(ifr)) {
+ addrlist *al;
+ ifr = (struct ifreq *) cp;
+
+ if (ifr->ifr_addr.sa_family != AF_INET)
+ continue;
+ else
+ address = ((struct sockaddr_in *) &ifr->ifr_addr)->sin_addr.s_addr;
+
+ /*
+ * Get interface flags
+ */
+ if (ioctl(sk, SIOCGIFFLAGS, (caddr_t) ifr) < 0)
+ continue;
+
+ /*
+ * If the interface is a loopback, or its not running
+ * then ignore it.
+ */
+ if ((ifr->ifr_flags & IFF_LOOPBACK) != 0)
+ continue;
+ if ((ifr->ifr_flags & IFF_RUNNING) == 0)
+ continue;
+
+ /*
+ * Get the netmask of this interface
+ */
+ if (ioctl(sk, SIOCGIFNETMASK, (caddr_t) ifr) < 0)
+ continue;
+
+ netmask = ((struct sockaddr_in *) &ifr->ifr_addr)->sin_addr.s_addr;
+
+ /*
+ * Add interface to local network list
+ */
+ al = ALLOC(addrlist);
+ al->ip_addr = address;
+ al->ip_mask = netmask;
+ al->ip_next = localnets;
+ localnets = al;
+
+ if (netname == 0) {
+ unsigned long net;
+ unsigned long mask;
+ unsigned long subnetshift;
+ /*
+ * Figure out the subnet's network address
+ */
+ subnet = address & netmask;
+
+#ifdef IN_CLASSA
+ subnet = ntohl(subnet);
+
+ if (IN_CLASSA(subnet)) {
+ mask = IN_CLASSA_NET;
+ subnetshift = 8;
+ } else if (IN_CLASSB(subnet)) {
+ mask = IN_CLASSB_NET;
+ subnetshift = 8;
+ } else {
+ mask = IN_CLASSC_NET;
+ subnetshift = 4;
+ }
+
+ /*
+ * If there are more bits than the standard mask
+ * would suggest, subnets must be in use.
+ * Guess at the subnet mask, assuming reasonable
+ * width subnet fields.
+ * XXX: Or-in at least 1 byte's worth of 1s to make
+ * sure the top bits remain set.
+ */
+ while (subnet &~ mask)
+ mask = (mask >> subnetshift) | 0xff000000;
+
+ net = subnet & mask;
+ while ((mask & 1) == 0)
+ mask >>= 1, net >>= 1;
+
+ /*
+ * Now get a usable name.
+ * First use the network database,
+ * then the host database,
+ * and finally just make a dotted quad.
+ */
+
+ np = getnetbyaddr(net, AF_INET);
+#else
+ /* This is probably very wrong. */
+ np = getnetbyaddr(subnet, AF_INET);
+#endif /* IN_CLASSA */
+ if (np)
+ s = np->n_name;
+ else {
+ subnet = address & netmask;
+ hp = gethostbyaddr((char *) &subnet, 4, AF_INET);
+ if (hp)
+ s = hp->h_name;
+ else
+ s = inet_dquad(buf, subnet);
+ }
+ netname = strdup(s);
+ }
+ }
+
+out:
+ if (sk >= 0)
+ (void) close(sk);
+ if (netname)
+ return netname;
+ return strdup(NO_SUBNET);
+}
+
+#else
+
+char *getwire P((void));
+char *getwire()
+{
+ return strdup(NO_SUBNET);
+}
+#endif /* SIOCGIFFLAGS */
+
+/*
+ * Determine whether a network is on a local network
+ * (addr) is in network byte order.
+ */
+int islocalnet P((unsigned long addr));
+int islocalnet(addr)
+unsigned long addr;
+{
+ addrlist *al;
+
+ for (al = localnets; al; al = al->ip_next)
+ if (((addr ^ al->ip_addr) & al->ip_mask) == 0)
+ return TRUE;
+
+#ifdef DEBUG
+ { char buf[16];
+ plog(XLOG_INFO, "%s is on a remote network", inet_dquad(buf, addr));
+ }
+#endif
+ return FALSE;
+}
diff --git a/usr.sbin/amd/amd/xutil.c b/usr.sbin/amd/amd/xutil.c
new file mode 100644
index 0000000..987588e
--- /dev/null
+++ b/usr.sbin/amd/amd/xutil.c
@@ -0,0 +1,494 @@
+/*
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE 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[] = "@(#)xutil.c 8.1 (Berkeley) 6/6/93";
+#endif
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#include "config.h"
+#ifdef HAS_SYSLOG
+#include <syslog.h>
+#endif /* HAS_SYSLOG */
+#ifdef HAS_STRERROR
+#include <string.h>
+#endif
+
+FILE *logfp = stderr; /* Log errors to stderr initially */
+#ifdef HAS_SYSLOG
+int syslogging;
+#endif /* HAS_SYSLOG */
+int xlog_level = XLOG_ALL & ~XLOG_MAP & ~XLOG_STATS;
+int xlog_level_init = ~0;
+
+/*
+ * List of log options
+ */
+struct opt_tab xlog_opt[] = {
+ { "all", XLOG_ALL }, /* All messages */
+#ifdef DEBUG
+ { "debug", XLOG_DEBUG }, /* Debug messages */
+#endif /* DEBUG */
+ { "error", XLOG_ERROR }, /* Non-fatal system errors */
+ { "fatal", XLOG_FATAL }, /* Fatal errors */
+ { "info", XLOG_INFO }, /* Information */
+ { "map", XLOG_MAP }, /* Map errors */
+ { "stats", XLOG_STATS }, /* Additional statistical information */
+ { "user", XLOG_USER }, /* Non-fatal user errors */
+ { "warn", XLOG_WARNING }, /* Warnings */
+ { "warning", XLOG_WARNING }, /* Warnings */
+ { 0, 0 }
+};
+
+voidp xmalloc(len)
+int len;
+{
+ voidp p;
+ int retries = 600;
+
+ /*
+ * Avoid malloc's which return NULL for malloc(0)
+ */
+ if (len == 0)
+ len = 1;
+
+ do {
+ p = (voidp) malloc((unsigned) len);
+ if (p) {
+#if defined(DEBUG) && defined(DEBUG_MEM)
+ Debug(D_MEM) plog(XLOG_DEBUG, "Allocated size %d; block %#x", len, p);
+#endif /* defined(DEBUG) && defined(DEBUG_MEM) */
+ return p;
+ }
+ if (retries > 0) {
+ plog(XLOG_ERROR, "Retrying memory allocation");
+ sleep(1);
+ }
+ } while (--retries);
+
+ plog(XLOG_FATAL, "Out of memory");
+ going_down(1);
+
+ abort();
+
+ return 0;
+}
+
+voidp xrealloc(ptr, len)
+voidp ptr;
+int len;
+{
+#if defined(DEBUG) && defined(DEBUG_MEM)
+ Debug(D_MEM) plog(XLOG_DEBUG, "Reallocated size %d; block %#x", len, ptr);
+#endif /* defined(DEBUG) && defined(DEBUG_MEM) */
+
+ if (len == 0)
+ len = 1;
+
+ if (ptr)
+ ptr = (voidp) realloc(ptr, (unsigned) len);
+ else
+ ptr = (voidp) xmalloc((unsigned) len);
+
+ if (!ptr) {
+ plog(XLOG_FATAL, "Out of memory in realloc");
+ going_down(1);
+ abort();
+ }
+ return ptr;
+}
+
+#if defined(DEBUG) && defined(DEBUG_MEM)
+xfree(f, l, p)
+char *f;
+int l;
+voidp p;
+{
+ Debug(D_MEM) plog(XLOG_DEBUG, "Free in %s:%d: block %#x", f, l, p);
+#undef free
+ free(p);
+}
+#endif /* defined(DEBUG) && defined(DEBUG_MEM) */
+#ifdef DEBUG_MEM
+static int mem_bytes;
+static int orig_mem_bytes;
+static void checkup_mem(P_void)
+{
+extern struct mallinfo __mallinfo;
+ if (mem_bytes != __mallinfo.uordbytes) {
+ if (orig_mem_bytes == 0)
+ mem_bytes = orig_mem_bytes = __mallinfo.uordbytes;
+ else {
+ fprintf(logfp, "amd[%d]: ", mypid);
+ if (mem_bytes < __mallinfo.uordbytes) {
+ fprintf(logfp, "ALLOC: %d bytes",
+ __mallinfo.uordbytes - mem_bytes);
+ } else {
+ fprintf(logfp, "FREE: %d bytes",
+ mem_bytes - __mallinfo.uordbytes);
+ }
+ mem_bytes = __mallinfo.uordbytes;
+ fprintf(logfp, ", making %d missing\n",
+ mem_bytes - orig_mem_bytes);
+ }
+ }
+ malloc_verify();
+}
+#endif /* DEBUG_MEM */
+
+/*
+ * Take a log format string and expand occurences of %m
+ * with the current error code take from errno.
+ */
+INLINE
+static void expand_error(f, e)
+char *f;
+char *e;
+{
+#ifndef HAS_STRERROR
+ extern int sys_nerr;
+ extern char *sys_errlist[];
+#endif
+ char *p;
+ int error = errno;
+
+ for (p = f; *e = *p; e++, p++) {
+ if (p[0] == '%' && p[1] == 'm') {
+ char *errstr;
+#ifdef HAS_STRERROR
+ errstr = strerror(error);
+#else
+ if (error < 0 || error >= sys_nerr)
+ errstr = 0;
+ else
+ errstr = sys_errlist[error];
+#endif
+ if (errstr)
+ strcpy(e, errstr);
+ else
+ sprintf(e, "Error %d", error);
+ e += strlen(e) - 1;
+ p++;
+ }
+ }
+}
+
+/*
+ * Output the time of day and hostname to the logfile
+ */
+static void show_time_host_and_name(lvl)
+int lvl;
+{
+static time_t last_t = 0;
+static char *last_ctime = 0;
+ time_t t = clocktime();
+ char *sev;
+ extern char *ctime();
+
+#if defined(DEBUG) && defined(PARANOID)
+extern char **gargv;
+#endif /* defined(DEBUG) && defined(PARANOID) */
+
+ if (t != last_t) {
+ last_ctime = ctime(&t);
+ last_t = t;
+ }
+
+ switch (lvl) {
+ case XLOG_FATAL: sev = "fatal:"; break;
+ case XLOG_ERROR: sev = "error:"; break;
+ case XLOG_USER: sev = "user: "; break;
+ case XLOG_WARNING: sev = "warn: "; break;
+ case XLOG_INFO: sev = "info: "; break;
+ case XLOG_DEBUG: sev = "debug:"; break;
+ case XLOG_MAP: sev = "map: "; break;
+ case XLOG_STATS: sev = "stats:"; break;
+ default: sev = "hmm: "; break;
+ }
+ fprintf(logfp, "%15.15s %s %s[%d]/%s ",
+ last_ctime+4, hostname,
+#if defined(DEBUG) && defined(PARANOID)
+ gargv[0],
+#else
+ "amd",
+#endif /* defined(DEBUG) && defined(PARANOID) */
+ mypid,
+ sev);
+}
+
+#ifdef DEBUG
+/*VARARGS1*/
+void dplog(fmt, j,s,_,p,e,n,d,r,y)
+char *fmt;
+char *j, *s, *_, *p, *e, *n, *d, *r, *y;
+{
+ plog(XLOG_DEBUG, fmt, j,s,_,p,e,n,d,r,y);
+}
+
+#endif /* DEBUG */
+/*VARARGS1*/
+void plog(lvl, fmt, j,s,_,p,e,n,d,r,y)
+int lvl;
+char *fmt;
+char *j, *s, *_, *p, *e, *n, *d, *r, *y;
+{
+ char msg[1024];
+ char efmt[1024];
+ char *ptr = msg;
+
+ if (!(xlog_level & lvl))
+ return;
+
+#ifdef DEBUG_MEM
+ checkup_mem();
+#endif /* DEBUG_MEM */
+
+ expand_error(fmt, efmt);
+ sprintf(ptr, efmt, j,s,_,p,e,n,d,r,y);
+ ptr += strlen(ptr);
+ if (ptr[-1] == '\n')
+ *--ptr = '\0';
+#ifdef HAS_SYSLOG
+ if (syslogging) {
+ switch(lvl) { /* from mike <mcooper@usc.edu> */
+ case XLOG_FATAL: lvl = LOG_CRIT; break;
+ case XLOG_ERROR: lvl = LOG_ERR; break;
+ case XLOG_USER: lvl = LOG_WARNING; break;
+ case XLOG_WARNING: lvl = LOG_WARNING; break;
+ case XLOG_INFO: lvl = LOG_INFO; break;
+ case XLOG_DEBUG: lvl = LOG_DEBUG; break;
+ case XLOG_MAP: lvl = LOG_DEBUG; break;
+ case XLOG_STATS: lvl = LOG_INFO; break;
+ default: lvl = LOG_ERR; break;
+ }
+ syslog(lvl, "%s", msg);
+ return;
+ }
+#endif /* HAS_SYSLOG */
+
+ *ptr++ = '\n';
+ *ptr = '\0';
+
+ /*
+ * Mimic syslog header
+ */
+ show_time_host_and_name(lvl);
+ fwrite(msg, ptr - msg, 1, logfp);
+ fflush(logfp);
+}
+
+void show_opts P((int ch, struct opt_tab *opts));
+void show_opts(ch, opts)
+int ch;
+struct opt_tab *opts;
+{
+ /*
+ * Display current debug options
+ */
+ int i;
+ int s = '{';
+ fprintf(stderr, " [-%c {no}", ch);
+ for (i = 0; opts[i].opt; i++) {
+ fprintf(stderr, "%c%s", s, opts[i].opt);
+ s = ',';
+ }
+ fputs("}]\n", stderr);
+}
+
+int cmdoption P((char *s, struct opt_tab *optb, int *flags));
+int cmdoption(s, optb, flags)
+char *s;
+struct opt_tab *optb;
+int *flags;
+{
+ char *p = s;
+ int errs = 0;
+
+ while (p && *p) {
+ int neg;
+ char *opt;
+ struct opt_tab *dp, *dpn = 0;
+
+ s = p;
+ p = strchr(p, ',');
+ if (p)
+ *p = '\0';
+
+ if (s[0] == 'n' && s[1] == 'o') {
+ opt = s + 2;
+ neg = 1;
+ } else {
+ opt = s;
+ neg = 0;
+ }
+
+ /*
+ * Scan the array of debug options to find the
+ * corresponding flag value. If it is found
+ * then set (or clear) the flag (depending on
+ * whether the option was prefixed with "no").
+ */
+ for (dp = optb; dp->opt; dp++) {
+ if (strcmp(opt, dp->opt) == 0)
+ break;
+ if (opt != s && !dpn && strcmp(s, dp->opt) == 0)
+ dpn = dp;
+ }
+
+ if (dp->opt || dpn) {
+ if (!dp->opt) {
+ dp = dpn;
+ neg = !neg;
+ }
+ if (neg)
+ *flags &= ~dp->flag;
+ else
+ *flags |= dp->flag;
+ } else {
+ /*
+ * This will log to stderr when parsing the command line
+ * since any -l option will not yet have taken effect.
+ */
+ plog(XLOG_USER, "option \"%s\" not recognised", s);
+ errs++;
+ }
+ /*
+ * Put the comma back
+ */
+ if (p)
+ *p++ = ',';
+ }
+
+ return errs;
+}
+
+/*
+ * Switch on/off logging options
+ */
+int switch_option(opt)
+char *opt;
+{
+ int xl = xlog_level;
+ int rc = cmdoption(opt, xlog_opt, &xl);
+ if (rc) {
+ rc = EINVAL;
+ } else {
+ /*
+ * Keep track of initial log level, and
+ * don't allow options to be turned off.
+ */
+ if (xlog_level_init == ~0)
+ xlog_level_init = xl;
+ else
+ xl |= xlog_level_init;
+ xlog_level = xl;
+ }
+ return rc;
+}
+
+/*
+ * Change current logfile
+ */
+int switch_to_logfile P((char *logfile));
+int switch_to_logfile(logfile)
+char *logfile;
+{
+ FILE *new_logfp = stderr;
+
+ if (logfile) {
+#ifdef HAS_SYSLOG
+ syslogging = 0;
+#endif /* HAS_SYSLOG */
+ if (strcmp(logfile, "/dev/stderr") == 0)
+ new_logfp = stderr;
+ else if (strcmp(logfile, "syslog") == 0) {
+#ifdef HAS_SYSLOG
+ syslogging = 1;
+ new_logfp = stderr;
+#if defined(LOG_CONS) && defined(LOG_NOWAIT)
+ openlog("amd", LOG_PID|LOG_CONS|LOG_NOWAIT,
+ LOG_DAEMON);
+#else
+ /* 4.2 compat mode - XXX */
+ openlog("amd", LOG_PID);
+#endif /* LOG_CONS && LOG_NOWAIT */
+#else
+ plog(XLOG_WARNING, "syslog option not supported, logging unchanged");
+#endif /* HAS_SYSLOG */
+ } else {
+ (void) umask(orig_umask);
+ new_logfp = fopen(logfile, "a");
+ umask(0);
+ }
+ }
+
+ /*
+ * If we couldn't open a new file, then continue using the old.
+ */
+ if (!new_logfp && logfile) {
+ plog(XLOG_USER, "%s: Can't open logfile: %m", logfile);
+ return 1;
+ }
+ /*
+ * Close the previous file
+ */
+ if (logfp && logfp != stderr)
+ (void) fclose(logfp);
+ logfp = new_logfp;
+ return 0;
+}
+
+time_t clock_valid = 0;
+time_t xclock_valid = 0;
+#ifndef clocktime
+time_t clocktime(P_void)
+{
+ time_t now = time(&clock_valid);
+ if (xclock_valid > now) {
+ /*
+ * Someone set the clock back!
+ */
+ plog(XLOG_WARNING, "system clock reset");
+ reschedule_timeouts(now, xclock_valid);
+ }
+ return xclock_valid = now;
+}
+#endif /* clocktime */
diff --git a/usr.sbin/amd/amq/Makefile b/usr.sbin/amd/amq/Makefile
new file mode 100644
index 0000000..2cb8124
--- /dev/null
+++ b/usr.sbin/amd/amq/Makefile
@@ -0,0 +1,25 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG = amq
+MAN8 = amq.8
+SRCS = amq.c amq_clnt.c amq_xdr.c misc_rpc.c
+CFLAGS+=-I${.OBJDIR}
+CFLAGS+=-I${.CURDIR}/../include
+CFLAGS+=-I${.CURDIR}/../rpcx
+CFLAGS+=-I${.CURDIR}/../config
+CFLAGS+=-DARCH_REP=\"${MACHINE}\"
+CFLAGS+=-DOS_REP=\"bsd44\"
+CFLAGS+=-DOS_HDR=\"os-bsd44.h\"
+.PATH: ${.CURDIR}/../rpcx ${.CURDIR}/../amd
+CLEANFILES+=nfs_prot.h
+RPCCOM = rpcgen
+NFS_PROT_X= ${DESTDIR}/usr/include/rpcsvc/nfs_prot.x
+DPSRCS= nfs_prot.h
+
+nfs_prot.h: ${NFS_PROT_X}
+ ${RPCCOM} -h ${NFS_PROT_X} -o ${.TARGET}
+
+${OBJS} beforedepend: nfs_prot.h
+
+.include "../../Makefile.inc"
+.include <bsd.prog.mk>
diff --git a/usr.sbin/amd/amq/amq.8 b/usr.sbin/amd/amq/amq.8
new file mode 100644
index 0000000..ba3b9ba
--- /dev/null
+++ b/usr.sbin/amd/amq/amq.8
@@ -0,0 +1,131 @@
+.\"
+.\" Copyright (c) 1990 Jan-Simon Pendry
+.\" Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+.\" Copyright (c) 1990, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" Jan-Simon Pendry at Imperial College, London.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)amq.8 8.3 (Berkeley) 4/18/94
+.\"
+.\" $Id$
+.\"
+.Dd March 16, 1991
+.Dt AMQ 8
+.Os
+.Sh NAME
+.Nm amq
+.Nd automounter query tool
+.Sh SYNOPSIS
+.Nm amq
+.Op Fl f
+.Op Fl h Ar hostname
+.Op Fl M Ar mountmap_entry
+.Op Fl m
+.Op Fl s
+.Op Fl u
+.Op Fl v
+.Op Ar directory
+.Ar ...
+.Sh DESCRIPTION
+.Nm Amq
+provides a simple way of determining the current state of the
+.Xr amd 8
+program.
+Communication is by
+.Tn RPC .
+Three modes of operation are supported by the current protocol.
+By default a list of mount points and auto-mounted filesystems
+is output.
+An alternative host can be specified using the
+.Fl h
+option.
+.Pp
+If directory names are given, as output by default,
+then per-filesystem information is displayed.
+.Sh OPTIONS
+.Bl -tag -width Ds
+.It Fl f
+Request automounter to flush the internal caches.
+.It Fl h Ar hostname
+Query alternate host
+.Ar hostname .
+By default the local host is used. In an
+.Tn HP-UX
+cluster, the root server is queried by default, since
+that is the system on which the automounter is normally run.
+.It Fl m
+Request the automounter to provide a list of mounted filesystems,
+including the number of references to each filesystem and any error
+which occurred while mounting.
+.It Fl s
+Request the automounter to provide system-wide mount statistics.
+.It Fl u
+Request the automounter to unmount the named filesystems
+instead of providing information about them. Unmounts are requested,
+not forced. They merely cause the mounted filesystem to timeout,
+which will be picked up by
+.Nm amd Ns \'s
+main scheduler thus causing the normal timeout action to be taken.
+.It Fl v
+Request the automounter to provide version information. This is a subset
+of the information provided by
+.Nm amd Ns \'s Fl v
+option.
+.It Fl M
+Request automounter to add the given map entry to the root map and then
+trigger a mount request for it.
+.El
+.Sh FILES
+.Bl -tag -width amq.xxxxx -compact
+.Bl -tag -width Ds
+.It Pa amq.x
+.Tn RPC
+protocol description.
+.El
+.Sh CAVEATS
+.Nm Amq
+uses a Sun registered
+.Tn RPC
+program number (300019 decimal) which may not
+be in the
+.Pa /etc/rpc
+database.
+.Sh SEE ALSO
+.Xr amd 8
+.Sh AUTHOR
+.An Jan-Simon Pendry
+<jsp@doc.ic.ac.uk>, Department of Computing, Imperial College, London, UK.
+.Sh HISTORY
+.Nm Amq
+first appeared in 4.4BSD.
+.At
diff --git a/usr.sbin/amd/amq/amq.c b/usr.sbin/amd/amq/amq.c
new file mode 100644
index 0000000..53d84f2
--- /dev/null
+++ b/usr.sbin/amd/amq/amq.c
@@ -0,0 +1,644 @@
+/*
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Automounter query tool
+ */
+
+#ifndef lint
+char copyright[] = "\
+@(#)Copyright (c) 1990 Jan-Simon Pendry\n\
+@(#)Copyright (c) 1990 Imperial College of Science, Technology & Medicine\n\
+@(#)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[] = "@(#)amq.c 8.1 (Berkeley) 6/7/93";
+#endif
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#include "am.h"
+#include "amq.h"
+#include <fcntl.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <unistd.h>
+
+static int privsock();
+static void usage __P((void));
+
+static int flush_flag;
+static int minfo_flag;
+static int unmount_flag;
+static int stats_flag;
+static int getvers_flag;
+static char *debug_opts;
+static char *logfile;
+static char *mount_map;
+static char *xlog_optstr;
+static char localhost[] = "localhost";
+static char *def_server = localhost;
+
+static struct timeval tmo = { 10, 0 };
+#define TIMEOUT tmo
+
+enum show_opt { Full, Stats, Calc, Short, ShowDone };
+
+/*
+ * If (e) is Calc then just calculate the sizes
+ * Otherwise display the mount node on stdout
+ */
+static void show_mti(mt, e, mwid, dwid, twid)
+amq_mount_tree *mt;
+enum show_opt e;
+int *mwid;
+int *dwid;
+int *twid;
+{
+ switch (e) {
+ case Calc: {
+ int mw = strlen(mt->mt_mountinfo);
+ int dw = strlen(mt->mt_directory);
+ int tw = strlen(mt->mt_type);
+ if (mw > *mwid) *mwid = mw;
+ if (dw > *dwid) *dwid = dw;
+ if (tw > *twid) *twid = tw;
+ } break;
+
+ case Full: {
+ struct tm *tp = localtime((time_t *) &mt->mt_mounttime);
+printf("%-*.*s %-*.*s %-*.*s %s\n\t%-5d %-7d %-6d %-7d %-7d %-6d %02d/%02d/%02d %02d:%02d:%02d\n",
+ *dwid, *dwid,
+ *mt->mt_directory ? mt->mt_directory : "/", /* XXX */
+ *twid, *twid,
+ mt->mt_type,
+ *mwid, *mwid,
+ mt->mt_mountinfo,
+ mt->mt_mountpoint,
+
+ mt->mt_mountuid,
+ mt->mt_getattr,
+ mt->mt_lookup,
+ mt->mt_readdir,
+ mt->mt_readlink,
+ mt->mt_statfs,
+
+ tp->tm_year > 99 ? tp->tm_year - 100 : tp->tm_year,
+ tp->tm_mon+1, tp->tm_mday,
+ tp->tm_hour, tp->tm_min, tp->tm_sec);
+ } break;
+
+ case Stats: {
+ struct tm *tp = localtime((time_t *) &mt->mt_mounttime);
+printf("%-*.*s %-5d %-7d %-6d %-7d %-7d %-6d %02d/%02d/%02d %02d:%02d:%02d\n",
+ *dwid, *dwid,
+ *mt->mt_directory ? mt->mt_directory : "/", /* XXX */
+
+ mt->mt_mountuid,
+ mt->mt_getattr,
+ mt->mt_lookup,
+ mt->mt_readdir,
+ mt->mt_readlink,
+ mt->mt_statfs,
+
+ tp->tm_year > 99 ? tp->tm_year - 100 : tp->tm_year,
+ tp->tm_mon+1, tp->tm_mday,
+ tp->tm_hour, tp->tm_min, tp->tm_sec);
+ } break;
+
+ case Short: {
+ printf("%-*.*s %-*.*s %-*.*s %s\n",
+ *dwid, *dwid,
+ *mt->mt_directory ? mt->mt_directory : "/",
+ *twid, *twid,
+ mt->mt_type,
+ *mwid, *mwid,
+ mt->mt_mountinfo,
+ mt->mt_mountpoint);
+ } break;
+ }
+}
+
+/*
+ * Display a mount tree.
+ */
+static void show_mt(mt, e, mwid, dwid, pwid)
+amq_mount_tree *mt;
+enum show_opt e;
+int *mwid;
+int *dwid;
+int *pwid;
+{
+ while (mt) {
+ show_mti(mt, e, mwid, dwid, pwid);
+ show_mt(mt->mt_next, e, mwid, dwid, pwid);
+ mt = mt->mt_child;
+ }
+}
+
+static void show_mi(ml, e, mwid, dwid, twid)
+amq_mount_info_list *ml;
+enum show_opt e;
+int *mwid;
+int *dwid;
+int *twid;
+{
+ int i;
+ switch (e) {
+ case Calc: {
+ for (i = 0; i < ml->amq_mount_info_list_len; i++) {
+ amq_mount_info *mi = &ml->amq_mount_info_list_val[i];
+ int mw = strlen(mi->mi_mountinfo);
+ int dw = strlen(mi->mi_mountpt);
+ int tw = strlen(mi->mi_type);
+ if (mw > *mwid) *mwid = mw;
+ if (dw > *dwid) *dwid = dw;
+ if (tw > *twid) *twid = tw;
+ }
+ } break;
+
+ case Full: {
+ for (i = 0; i < ml->amq_mount_info_list_len; i++) {
+ amq_mount_info *mi = &ml->amq_mount_info_list_val[i];
+ printf("%-*.*s %-*.*s %-*.*s %-3d %s is %s",
+ *mwid, *mwid, mi->mi_mountinfo,
+ *dwid, *dwid, mi->mi_mountpt,
+ *twid, *twid, mi->mi_type,
+ mi->mi_refc, mi->mi_fserver,
+ mi->mi_up > 0 ? "up" :
+ mi->mi_up < 0 ? "starting" : "down");
+ if (mi->mi_error > 0) {
+#ifdef HAS_STRERROR
+ printf(" (%s)", strerror(mi->mi_error));
+#else
+ extern char *sys_errlist[];
+ extern int sys_nerr;
+ if (mi->mi_error < sys_nerr)
+ printf(" (%s)", sys_errlist[mi->mi_error]);
+ else
+ printf(" (Error %d)", mi->mi_error);
+#endif
+ } else if (mi->mi_error < 0) {
+ fputs(" (in progress)", stdout);
+ }
+ fputc('\n', stdout);
+ }
+ } break;
+ }
+}
+
+/*
+ * Display general mount statistics
+ */
+static void show_ms(ms)
+amq_mount_stats *ms;
+{
+ printf("\
+requests stale mount mount unmount\n\
+deferred fhandles ok failed failed\n\
+%-9d %-9d %-9d %-9d %-9d\n",
+ ms->as_drops, ms->as_stale, ms->as_mok, ms->as_merr, ms->as_uerr);
+}
+
+static bool_t
+xdr_pri_free(xdr_args, args_ptr)
+xdrproc_t xdr_args;
+caddr_t args_ptr;
+{
+ XDR xdr;
+ xdr.x_op = XDR_FREE;
+ return ((*xdr_args)(&xdr, args_ptr));
+}
+
+#ifdef hpux
+#include <cluster.h>
+static char *cluster_server()
+{
+ struct cct_entry *cp;
+
+ if (cnodeid() == 0) {
+ /*
+ * Not clustered
+ */
+ return def_server;
+ }
+
+ while (cp = getccent())
+ if (cp->cnode_type == 'r')
+ return cp->cnode_name;
+
+
+ return def_server;
+}
+#endif /* hpux */
+
+/*
+ * MAIN
+ */
+main(argc, argv)
+int argc;
+char *argv[];
+{
+ int opt_ch;
+ int errs = 0;
+ char *server;
+ struct sockaddr_in server_addr;
+
+ /* In order to pass the Amd security check, we must use a priv port. */
+ int s;
+
+ CLIENT *clnt;
+ struct hostent *hp;
+ int nodefault = 0;
+
+ /*
+ * Parse arguments
+ */
+ while ((opt_ch = getopt(argc, argv, "fh:l:msuvx:D:M:")) != -1)
+ switch (opt_ch) {
+ case 'f':
+ flush_flag = 1;
+ nodefault = 1;
+ break;
+
+ case 'h':
+ def_server = optarg;
+ break;
+
+ case 'l':
+ logfile = optarg;
+ nodefault = 1;
+ break;
+
+ case 'm':
+ minfo_flag = 1;
+ nodefault = 1;
+ break;
+
+ case 's':
+ stats_flag = 1;
+ nodefault = 1;
+ break;
+
+ case 'u':
+ unmount_flag = 1;
+ nodefault = 1;
+ break;
+
+ case 'v':
+ getvers_flag = 1;
+ nodefault = 1;
+ break;
+
+ case 'x':
+ xlog_optstr = optarg;
+ nodefault = 1;
+ break;
+
+ case 'D':
+ debug_opts = optarg;
+ nodefault = 1;
+ break;
+
+ case 'M':
+ mount_map = optarg;
+ nodefault = 1;
+ break;
+
+ default:
+ errs = 1;
+ break;
+ }
+
+ if (optind == argc) {
+ if (unmount_flag)
+ errs = 1;
+ }
+
+ if (errs)
+ usage();
+
+#ifdef hpux
+ /*
+ * Figure out root server of cluster
+ */
+ if (def_server == localhost)
+ server = cluster_server();
+ else
+#endif /* hpux */
+ server = def_server;
+
+ /*
+ * Get address of server
+ */
+ if ((hp = gethostbyname(server)) == 0 && strcmp(server, localhost) != 0)
+ errx(1, "can't get address of %s", server);
+ bzero(&server_addr, sizeof server_addr);
+ server_addr.sin_family = AF_INET;
+ if (hp) {
+ bcopy((voidp) hp->h_addr, (voidp) &server_addr.sin_addr,
+ sizeof(server_addr.sin_addr));
+ } else {
+ /* fake "localhost" */
+ server_addr.sin_addr.s_addr = htonl(0x7f000001);
+ }
+
+ /*
+ * Create RPC endpoint
+ */
+ s = RPC_ANYSOCK;
+ clnt = clnttcp_create(&server_addr, AMQ_PROGRAM, AMQ_VERSION, &s, 0, 0);
+ if (clnt == 0) {
+ close(s);
+ s = privsock(SOCK_DGRAM);
+ clnt = clntudp_create(&server_addr, AMQ_PROGRAM, AMQ_VERSION, TIMEOUT, &s);
+ }
+ if (clnt == 0)
+ errx(1, "%s", clnt_spcreateerror(server));
+
+ /*
+ * Control debugging
+ */
+ if (debug_opts) {
+ int *rc;
+ amq_setopt opt;
+ opt.as_opt = AMOPT_DEBUG;
+ opt.as_str = debug_opts;
+ rc = amqproc_setopt_1(&opt, clnt);
+ if (rc && *rc < 0) {
+ warnx("daemon not compiled for debug");
+ errs = 1;
+ } else if (!rc || *rc > 0) {
+ warnx("debug setting for \"%s\" failed", debug_opts);
+ errs = 1;
+ }
+ }
+
+ /*
+ * Control logging
+ */
+ if (xlog_optstr) {
+ int *rc;
+ amq_setopt opt;
+ opt.as_opt = AMOPT_XLOG;
+ opt.as_str = xlog_optstr;
+ rc = amqproc_setopt_1(&opt, clnt);
+ if (!rc || *rc) {
+ warnx("setting log level to \"%s\" failed", xlog_optstr);
+ errs = 1;
+ }
+ }
+
+ /*
+ * Control log file
+ */
+ if (logfile) {
+ int *rc;
+ amq_setopt opt;
+ opt.as_opt = AMOPT_LOGFILE;
+ opt.as_str = logfile;
+ rc = amqproc_setopt_1(&opt, clnt);
+ if (!rc || *rc) {
+ warnx("setting logfile to \"%s\" failed", logfile);
+ errs = 1;
+ }
+ }
+
+ /*
+ * Flush map cache
+ */
+ if (flush_flag) {
+ int *rc;
+ amq_setopt opt;
+ opt.as_opt = AMOPT_FLUSHMAPC;
+ opt.as_str = "";
+ rc = amqproc_setopt_1(&opt, clnt);
+ if (!rc || *rc) {
+ warnx("amd on %s cannot flush the map cache", server);
+ errs = 1;
+ }
+ }
+
+ /*
+ * Mount info
+ */
+ if (minfo_flag) {
+ int dummy;
+ amq_mount_info_list *ml = amqproc_getmntfs_1(&dummy, clnt);
+ if (ml) {
+ int mwid = 0, dwid = 0, twid = 0;
+ show_mi(ml, Calc, &mwid, &dwid, &twid);
+ mwid++; dwid++; twid++;
+ show_mi(ml, Full, &mwid, &dwid, &twid);
+
+ } else {
+ warnx("amd on %s cannot provide mount info", server);
+ }
+ }
+
+ /*
+ * Mount map
+ */
+ if (mount_map) {
+ int *rc;
+ do {
+ rc = amqproc_mount_1(&mount_map, clnt);
+ } while (rc && *rc < 0);
+ if (!rc || *rc > 0) {
+ if (rc)
+ errno = *rc;
+ else
+ errno = ETIMEDOUT;
+ warn("could not start new autmount point");
+ }
+ }
+
+ /*
+ * Get Version
+ */
+ if (getvers_flag) {
+ amq_string *spp = amqproc_getvers_1((voidp) 0, clnt);
+ if (spp && *spp) {
+ printf("%s.\n", *spp);
+ free(*spp);
+ } else {
+ warnx("failed to get version information");
+ errs = 1;
+ }
+ }
+
+ /*
+ * Apply required operation to all remaining arguments
+ */
+ if (optind < argc) {
+ do {
+ char *fs = argv[optind++];
+ if (unmount_flag) {
+ /*
+ * Unmount request
+ */
+ amqproc_umnt_1(&fs, clnt);
+ } else {
+ /*
+ * Stats request
+ */
+ amq_mount_tree_p *mtp = amqproc_mnttree_1(&fs, clnt);
+ if (mtp) {
+ amq_mount_tree *mt = *mtp;
+ if (mt) {
+ int mwid = 0, dwid = 0, twid = 0;
+ show_mt(mt, Calc, &mwid, &dwid, &twid);
+ mwid++; dwid++, twid++;
+ printf("%-*.*s Uid Getattr Lookup RdDir RdLnk Statfs Mounted@\n",
+ dwid, dwid, "What");
+ show_mt(mt, Stats, &mwid, &dwid, &twid);
+ } else {
+ warnx("%s not automounted", fs);
+ }
+ xdr_pri_free(xdr_amq_mount_tree_p, (caddr_t) mtp);
+ } else {
+ warnx("%s", clnt_sperror(clnt, server));
+ errs = 1;
+ }
+ }
+ } while (optind < argc);
+ } else if (unmount_flag) {
+ usage();
+ } else if (stats_flag) {
+ amq_mount_stats *ms = amqproc_stats_1((voidp) 0, clnt);
+ if (ms) {
+ show_ms(ms);
+ } else {
+ warnx("%s", clnt_sperror(clnt, server));
+ errs = 1;
+ }
+ } else if (!nodefault) {
+ amq_mount_tree_list *mlp = amqproc_export_1((voidp) 0, clnt);
+ if (mlp) {
+ enum show_opt e = Calc;
+ int mwid = 0, dwid = 0, pwid = 0;
+ while (e != ShowDone) {
+ int i;
+ for (i = 0; i < mlp->amq_mount_tree_list_len; i++) {
+ show_mt(mlp->amq_mount_tree_list_val[i],
+ e, &mwid, &dwid, &pwid);
+ }
+ mwid++; dwid++, pwid++;
+ if (e == Calc) e = Short;
+ else if (e == Short) e = ShowDone;
+ }
+ } else {
+ warnx("%s", clnt_sperror(clnt, server));
+ errs = 1;
+ }
+ }
+
+ exit(errs);
+}
+
+static void
+usage()
+{
+ fprintf(stderr, "%s\n%s\n",
+"usage: amq [-h host] [[-f] [-m] [-v] [-s]] | [[-u] directory ...]] |",
+" [-l logfile|\"syslog\"] [-x log_flags] [-D dbg_opts] [-M mapent]");
+ exit(1);
+}
+
+/*
+ * udpresport creates a datagram socket and attempts to bind it to a
+ * secure port.
+ * returns: The bound socket, or -1 to indicate an error.
+ */
+static int inetresport(ty)
+int ty;
+{
+ int alport;
+ struct sockaddr_in addr;
+ int sock;
+
+ /* Use internet address family */
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = INADDR_ANY;
+ if ((sock = socket(AF_INET, ty, 0)) < 0)
+ return -1;
+ for (alport = IPPORT_RESERVED-1; alport > IPPORT_RESERVED/2 + 1; alport--) {
+ addr.sin_port = htons((u_short)alport);
+ if (bind(sock, (struct sockaddr *)&addr, sizeof (addr)) >= 0)
+ return sock;
+ if (errno != EADDRINUSE) {
+ close(sock);
+ return -1;
+ }
+ }
+ close(sock);
+ errno = EAGAIN;
+ return -1;
+}
+
+/*
+ * Privsock() calls inetresport() to attempt to bind a socket to a secure
+ * port. If inetresport() fails, privsock returns a magic socket number which
+ * indicates to RPC that it should make its own socket.
+ * returns: A privileged socket # or RPC_ANYSOCK.
+ */
+static int privsock(ty)
+int ty;
+{
+ int sock = inetresport(ty);
+
+ if (sock < 0) {
+ errno = 0;
+ /* Couldn't get a secure port, let RPC make an insecure one */
+ sock = RPC_ANYSOCK;
+ }
+ return sock;
+}
+
+#ifdef DEBUG
+xfree(f, l, p)
+char *f, *l;
+voidp p;
+{
+ free(p);
+}
+#endif /* DEBUG */
diff --git a/usr.sbin/amd/config/Configure b/usr.sbin/amd/config/Configure
new file mode 100644
index 0000000..5523b00
--- /dev/null
+++ b/usr.sbin/amd/config/Configure
@@ -0,0 +1,60 @@
+#!/bin/sh -
+#
+# Copyright (c) 1989 Jan-Simon Pendry
+# Copyright (c) 1989 Imperial College of Science, Technology & Medicine
+# Copyright (c) 1989, 1993
+# The Regents of the University of California. All rights reserved.
+#
+# This code is derived from software contributed to Berkeley by
+# Jan-Simon Pendry at Imperial College, London.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. All advertising materials mentioning features or use of this software
+# must display the following acknowledgement:
+# This product includes software developed by the University of
+# California, Berkeley and its contributors.
+# 4. Neither the name of the University nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# @(#)Configure 8.1 (Berkeley) 6/6/93
+#
+# $Id$
+#
+echo "Making ./arch and ./os-type executable ..."
+until chmod +x ./arch ./os-type; do echo "Error: chmod command failed" >&2; exit 1; done
+echo "Checking ./arch and ./os-type ..."
+echo ""
+arch="`sh ./arch 2>/dev/null`"
+os="`sh ./os-type 2>/dev/null`"
+case "$arch" in
+"") echo "./arch doesn't produce an answer - please check it" >&2; exit 1;;
+esac
+case "$os" in
+"") echo "./os-type doesn't produce an answer - please check it" >&2; exit 1;;
+esac
+cat << %
+This machine appears to be a "$arch" running "$os".
+If that is correct just run make.
+If those are incorrect please edit ./arch and ./os-type
+%
+exit 0
diff --git a/usr.sbin/amd/config/Makefile.aix3 b/usr.sbin/amd/config/Makefile.aix3
new file mode 100644
index 0000000..dce6015
--- /dev/null
+++ b/usr.sbin/amd/config/Makefile.aix3
@@ -0,0 +1,5 @@
+# @(#)Makefile.aix3 8.1 (Berkeley) 6/6/93
+#
+# $Id$
+#
+SYSLIB = -lbsd
diff --git a/usr.sbin/amd/config/Makefile.bsd44 b/usr.sbin/amd/config/Makefile.bsd44
new file mode 100644
index 0000000..ee49092
--- /dev/null
+++ b/usr.sbin/amd/config/Makefile.bsd44
@@ -0,0 +1,8 @@
+# @(#)Makefile.bsd44 8.1 (Berkeley) 6/6/93
+#
+# $Id$
+#
+# Extra Makefile definitions for 4.4 BSD
+#
+
+RPCLIB = -lrpc
diff --git a/usr.sbin/amd/config/Makefile.config b/usr.sbin/amd/config/Makefile.config
new file mode 100644
index 0000000..5dab6ca
--- /dev/null
+++ b/usr.sbin/amd/config/Makefile.config
@@ -0,0 +1,96 @@
+# @(#)Makefile.config 8.1 (Berkeley) 6/6/93
+#
+# $Id$
+#
+
+#
+# Comment/uncomment the following lines as required
+#
+
+#
+# Where local include files are stored
+#
+#XINCLUDE = -I/usr/local/athena/include
+
+#
+# Define RESOLV if your C library does not include support
+# for Hesiod and/or Named.
+#
+#RESOLV = -lhesiod -lresolv
+
+#
+# Define XLIBDIR if you have libraries not on the standard
+# search path.
+#
+#XLIBDIR = -L/usr/local/athena/lib
+
+#
+# Define DBM if your C library does not include
+# support for gdbm and/or ndbm.
+#
+#DBM = -lgdbm #-lndbm
+
+#
+# Define RPCLIB if your C library does not include
+# support for RPC
+#
+#RPCLIB = -lrpc
+
+#
+# Include support for Network Information Service (NIS)
+# Also define HAS_NIS_RELOAD to include map
+# enumeration code implementing "cache:=all"
+#
+#HAS_NIS_MAPS = -DHAS_NIS_MAPS -DHAS_NIS_RELOAD
+
+#
+# Include support for file maps
+#
+HAS_FILE_MAPS = -DHAS_FILE_MAPS
+
+#
+# Include support for Hesiod
+# Also define HAS_HESIOD_RELOAD to include zone
+# transfer code implementing "cache:=all"
+#
+#HAS_HESIOD_MAPS = -DHAS_HESIOD_MAPS -DHAS_HESIOD_RELOAD
+
+#
+# Include support for /etc/passwd
+#
+HAS_PASSWD_MAPS = -DHAS_PASSWD_MAPS
+
+#
+# Include support for union maps
+#
+HAS_UNION_MAPS = -DHAS_UNION_MAPS
+
+#
+# Include support for ndbm.
+# This removes support for gdbm and is only supported
+# if your operating system supports ndbm
+#
+#HAS_NDBM_MAPS = -DHAS_NDBM_MAPS
+
+#
+# Include support for "regexp" maps
+#
+HAS_REGEXP = -DHAS_REGEXP
+
+#
+# Make sure that the hostname passed in RPC authentication packets
+# contains a fully qualified domain name. See nfs_ops.c
+#
+#HAS_NFS_QUALIFIED_NAMES = -DHAS_NFS_QUALIFIED_NAMES
+
+#
+# Caches the contents of symlinks
+#
+HAS_SYMLINK_CACHE = -DHAS_SYMLINK_CACHE
+
+##############################################################
+# Do NOT edit the following lines
+#
+CONFIG = ${XINCLUDE} ${HAS_NIS_MAPS} ${HAS_FILE_MAPS} ${HAS_HESIOD_MAPS} \
+ ${HAS_NDBM_MAPS} ${HAS_MOUNTD_MAPS} ${HAS_PASSWD_MAPS} ${HAS_UNION_MAPS} \
+ ${HAS_REGEXP} ${HAS_NFS_QUALIFIED_NAMES} ${HAS_SYMLINK_CACHE}
diff --git a/usr.sbin/amd/config/Makefile.hpux b/usr.sbin/amd/config/Makefile.hpux
new file mode 100644
index 0000000..eacc7a4
--- /dev/null
+++ b/usr.sbin/amd/config/Makefile.hpux
@@ -0,0 +1,13 @@
+# @(#)Makefile.hpux 8.1 (Berkeley) 6/6/93
+#
+# $Id$
+#
+# Extra Makefile definitions for HP-UX
+#
+
+#CC = gcc ${GCCOPTS}
+# Works only on HP300
+CC = cc -Wc,-Nd2000
+SYSCC = $(CC)
+# Works only Hp800
+# CC = cc
diff --git a/usr.sbin/amd/config/Makefile.irix b/usr.sbin/amd/config/Makefile.irix
new file mode 100644
index 0000000..640acad
--- /dev/null
+++ b/usr.sbin/amd/config/Makefile.irix
@@ -0,0 +1,10 @@
+# @(#)Makefile.irix 8.1 (Berkeley) 6/6/93
+#
+# $Id$
+#
+# Extra Makefile definitions for IRIX
+#
+
+DEBUG = #-g -DDEBUG
+CCOPTS = -I/usr/include/sun -I/usr/include/bsd -DIRIX
+RESOLV = -lrpcsvc -lsun -lbsd
diff --git a/usr.sbin/amd/config/Makefile.irix3 b/usr.sbin/amd/config/Makefile.irix3
new file mode 100644
index 0000000..214b290
--- /dev/null
+++ b/usr.sbin/amd/config/Makefile.irix3
@@ -0,0 +1,13 @@
+# @(#)Makefile.irix3 8.1 (Berkeley) 6/6/93
+#
+# $Id$
+#
+# Extra Makefile definitions for IRIX
+#
+
+# For 3.3.x and earlier we might need to indicate the Sun and BSD include
+# paths.
+
+DEBUG = #-g -DDEBUG
+CCOPTS = -I/usr/include/sun -I/usr/include/bsd
+RESOLV = -lrpcsvc -lsun -lbsd
diff --git a/usr.sbin/amd/config/Makefile.irix4 b/usr.sbin/amd/config/Makefile.irix4
new file mode 100644
index 0000000..d3002a2
--- /dev/null
+++ b/usr.sbin/amd/config/Makefile.irix4
@@ -0,0 +1,14 @@
+# @(#)Makefile.irix4 8.1 (Berkeley) 6/6/93
+#
+# $Id$
+#
+# Extra Makefile definitions for IRIX
+#
+
+# For 4.0.X and later we need to specify the -cckr option - although amd
+# has prototypes - some of the rpc prototypes clash. The special include
+# paths are not required. -lsun always comes before -lbsd.
+
+DEBUG = -g
+CCOPTS = -cckr
+RESOLV = -lrpcsvc -lsun -lbsd
diff --git a/usr.sbin/amd/config/Makefile.stellix b/usr.sbin/amd/config/Makefile.stellix
new file mode 100644
index 0000000..8603661
--- /dev/null
+++ b/usr.sbin/amd/config/Makefile.stellix
@@ -0,0 +1,10 @@
+# @(#)Makefile.stellix 8.1 (Berkeley) 6/6/93
+#
+# $Id$
+#
+# Extra Makefile definitions for STELLIX
+#
+
+DEBUG = #-g -DDEBUG
+CCOPTS = -DSTELLIX
+RESOLV = -lrpcsvc
diff --git a/usr.sbin/amd/config/RELEASE b/usr.sbin/amd/config/RELEASE
new file mode 100644
index 0000000..36f8f1f
--- /dev/null
+++ b/usr.sbin/amd/config/RELEASE
@@ -0,0 +1 @@
+$Revision: 5.2.3.1 $ of $Date: 1993/06/01 11:43:31 $ bsd44
diff --git a/usr.sbin/amd/config/arch b/usr.sbin/amd/config/arch
new file mode 100644
index 0000000..cd19a01
--- /dev/null
+++ b/usr.sbin/amd/config/arch
@@ -0,0 +1,127 @@
+#! /bin/sh
+#
+# Copyright (c) 1989 Jan-Simon Pendry
+# Copyright (c) 1989 Imperial College of Science, Technology & Medicine
+# Copyright (c) 1989, 1993
+# The Regents of the University of California. All rights reserved.
+#
+# This code is derived from software contributed to Berkeley by
+# Jan-Simon Pendry at Imperial College, London.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. All advertising materials mentioning features or use of this software
+# must display the following acknowledgement:
+# This product includes software developed by the University of
+# California, Berkeley and its contributors.
+# 4. Neither the name of the University nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# @(#)arch 8.1 (Berkeley) 6/6/93
+#
+# $Id$
+#
+# Figure out machine architecture
+#
+
+PATH=/bin:/usr/bin:/usr/ucb:/etc:/usr/local/bin:${PATH} export PATH
+
+#
+# First try to find a standard command
+#
+a=arch # Sun compat
+m=machine # BSD compat
+u=uname # Sys5 compat
+
+if [ -f /etc/$a -o -f /bin/$a -o -f /usr/bin/$a -o -f /usr/local/bin/$a ]
+then
+ exec $a
+elif [ -f /etc/$m -o -f /bin/$m -o -f /usr/bin/$m -o -f /usr/ucb/$m -o -f /usr/local/bin/$m ]
+then
+ exec $m
+elif [ -f /etc/$u -o -f /bin/$u -o -f /usr/bin/$u -o -f /usr/local/bin/$u ]
+then
+ ARCH="`uname`"
+ case "$ARCH" in
+ "HP-UX") echo hp9000; exit 0;;
+ "FreeBSD") echo FreeBSD; exit 0;;
+ AIX*) MACH="`uname -m`"
+ case "$MACH" in
+ 00*) echo ibm6000; exit 0;;
+ 10*) echo ibm032; exit 0;;
+ 20*) echo ibm032; exit 0;;
+ esac
+ ;;
+ A/UX) echo macII ; exit 0 ;;
+ dgux) MACH="`uname -m`"
+ case "$MACH" in
+ AViiON) echo aviion; exit 0;;
+ esac
+ ;;
+ *) MACH="`uname -m`"
+ case "$MACH" in
+ IP6) echo mips; exit 0;;
+ IP7) echo mips; exit 0;;
+ *) ;;
+ esac
+ ;;
+ esac
+fi
+
+#
+# Take a pot-shot at your machine architecture
+#
+echo "# ... No ARCH= option specified; dynamically determining architecture" >&2
+
+case "`exec 2>/dev/null; head -2 /etc/motd`" in
+*"HP-UX"*) ARCH=hp9000;;
+*"Iris"*) ARCH=iris4d;;
+*"Ultrix"*) ARCH=vax;;
+*"RISC iX"*) ARCH=arm;;
+*"Umax 4.2"*) ARCH=encore;;
+*"Alliant Concentrix"*) ARCH=alliant;;
+*"FPS Model 500"*) ARCH=fps500;;
+*"HCX/UX"*) ARCH=harris;;
+*) ARCH=unknown;
+ if [ -d /usr/include/caif ]; then
+ ARCH=ibm032
+ elif [ -f /bin/pyr ]; then
+ if /bin/pyr; then
+ ARCH=pyr
+ fi
+ elif [ -d /NextApps ]; then
+ ARCH=next
+ elif [ -f /etc/comply ]; then
+ # Tex 4300 is essentially a sun 3.
+ ARCH=sun3
+ fi
+ ;;
+esac
+
+echo "# ... architecture appears to be \"${ARCH}\"" >&2
+echo $ARCH
+
+case "$ARCH" in
+unknown) exit 1
+esac
+
+exit 0
diff --git a/usr.sbin/amd/config/misc-aix3.h b/usr.sbin/amd/config/misc-aix3.h
new file mode 100644
index 0000000..595a96e
--- /dev/null
+++ b/usr.sbin/amd/config/misc-aix3.h
@@ -0,0 +1,96 @@
+/*
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (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-aix3.h 8.1 (Berkeley) 6/6/93
+ *
+ * $Id$
+ *
+ */
+
+struct ufs_args {
+ char *fspec; /* Block device */
+};
+
+struct nfs_args {
+ struct sockaddr_in addr; /* file server address */
+ fhandle_t fh; /* File handle to be mounted */
+ int flags; /* flags */
+ int wsize; /* write size in bytes */
+ int rsize; /* read size in bytes */
+ int timeo; /* initial timeout in .1 secs */
+ int retrans; /* times to retry send */
+ char *hostname; /* server's hostname */
+ int acregmin; /* attr cache file min secs */
+ int acregmax; /* attr cache file max secs */
+ int acdirmin; /* attr cache dir min secs */
+ int acdirmax; /* attr cache dir max secs */
+ char *netname; /* server's netname */
+ int biods; /* number of BIODS */
+};
+
+/*
+ * NFS mount option flags
+ */
+#define MNTOPT_RO "ro" /* read only */
+#define MNTOPT_RW "rw" /* read/write */
+#define MNTOPT_SOFT "soft" /* soft mount */
+#define MNTOPT_HARD "hard" /* hard mount */
+#define MNTOPT_NOSUID "nosuid"/* no set uid allowed */
+#define MNTOPT_NOAUTO "noauto"/* hide entry from mount -a */
+#define MNTOPT_INTR "intr" /* allow interrupts on hard mount */
+#define MNTOPT_SECURE "secure"/* use secure RPC for NFS */
+#define MNTOPT_GRPID "grpid" /* SysV-compatible group-id on create */
+#define MNTOPT_NOSUB "nosub" /* disallow mounts beneath this one */
+#define MNTOPT_MULTI "multi" /* Do multi-component lookup */
+#define MNTOPT_NOAC "noac" /* don't cache attributes */
+
+#define NFSMNT_SOFT 0x001 /* soft mount (hard is default) */
+#define NFSMNT_WSIZE 0x002 /* set write size */
+#define NFSMNT_RSIZE 0x004 /* set read size */
+#define NFSMNT_TIMEO 0x008 /* set initial timeout */
+#define NFSMNT_RETRANS 0x010 /* set number of request retrys */
+#define NFSMNT_HOSTNAME 0x020 /* set hostname for error printf */
+#define NFSMNT_INT 0x040 /* allow interrupts on hard mount */
+#define NFSMNT_NOAC 0x080 /* don't cache attributes */
+#define NFSMNT_ACREGMIN 0x0100 /* set min secs for file attr cache */
+#define NFSMNT_ACREGMAX 0x0200 /* set max secs for file attr cache */
+#define NFSMNT_ACDIRMIN 0x0400 /* set min secs for dir attr cache */
+#define NFSMNT_ACDIRMAX 0x0800 /* set max secs for dir attr cache */
+#define NFSMNT_SECURE 0x1000 /* secure mount */
+#define NFSMNT_BIODS 0x10000 /* Number of biods for the file system */
+
+#define DEF_BIODS 6
diff --git a/usr.sbin/amd/config/misc-bsd44l.h b/usr.sbin/amd/config/misc-bsd44l.h
new file mode 100644
index 0000000..e124c87
--- /dev/null
+++ b/usr.sbin/amd/config/misc-bsd44l.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (c) 1995 Jan-Simon Pendry
+ * Copyright (c) 1995
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (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-bsd44l.h 8.1 (Berkeley) 5/10/95
+ *
+ */
+
+#define NFS_NPROCS 26
+struct nfs_fattr;
+union nfsfh;
+typedef union nfsfh nfsfh_t;
+
+#include <sys/ucred.h>
+#define mount __kern_mount
+#include <sys/mount.h>
+#undef mount
+#include <nfs/rpcv2.h>
+#define KERNEL
+#define nfs_init __kern_nfs_init
+#include <nfs/nfs.h>
+#undef nfs_init
+#undef KERNEL
+
+#include <ufs/ufs/ufsmount.h>
diff --git a/usr.sbin/amd/config/misc-hpux.h b/usr.sbin/amd/config/misc-hpux.h
new file mode 100644
index 0000000..72014a5
--- /dev/null
+++ b/usr.sbin/amd/config/misc-hpux.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 1989 Jan-Simon Pendry
+ * Copyright (c) 1989 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (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-hpux.h 8.1 (Berkeley) 6/6/93
+ *
+ * $Id$
+ *
+ */
+
+/*
+ * These definitions are from <nfs/nfs.h>
+ * Unfortunately, that file cannot be included
+ * because it contains lots of structure definitions
+ * that are not wanted (they produce name clashes).
+ * Isn't HP-UX wonderful!
+ */
+
+/*
+ * HP-UX specific definitions
+ */
+struct nfs_args {
+ struct sockaddr_in *addr; /* file server address */
+ fhandle_t *fh; /* File handle to be mounted */
+ int flags; /* flags */
+ int wsize; /* write size in bytes */
+ int rsize; /* read size in bytes */
+ int timeo; /* initial timeout in .1 secs */
+ int retrans; /* times to retry send */
+ char *hostname; /* server's name */
+#ifdef __hp9000s700 /* XXX for HPUX 8.0 */
+ char *fsname; /* server's filesystem name */
+#endif
+};
+
+/*
+ * NFS mount option flags
+ */
+#define NFSMNT_SOFT 0x001 /* soft mount (hard is default) */
+#define NFSMNT_WSIZE 0x002 /* set write size */
+#define NFSMNT_RSIZE 0x004 /* set read size */
+#define NFSMNT_TIMEO 0x008 /* set initial timeout */
+#define NFSMNT_RETRANS 0x010 /* set number of request retrys */
+#define NFSMNT_HOSTNAME 0x020 /* set hostname for error printf */
+#define NFSMNT_INT 0x040 /* set option to have interruptable mounts */
+#define NFSMNT_NODEVS 0x080 /* turn off device file access (default on) */
diff --git a/usr.sbin/amd/config/misc-irix.h b/usr.sbin/amd/config/misc-irix.h
new file mode 100644
index 0000000..8bcb7dc
--- /dev/null
+++ b/usr.sbin/amd/config/misc-irix.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (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-irix.h 8.1 (Berkeley) 6/6/93
+ *
+ * $Id$
+ *
+ */
+
+#include <sys/fs/nfs_clnt.h>
+#include <sys/fsid.h>
+#include <sys/fstyp.h>
+
+struct ufs_args {
+ char *fspec;
+};
diff --git a/usr.sbin/amd/config/misc-next.h b/usr.sbin/amd/config/misc-next.h
new file mode 100644
index 0000000..92c8b14
--- /dev/null
+++ b/usr.sbin/amd/config/misc-next.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (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-next.h 8.1 (Berkeley) 6/6/93
+ *
+ * $Id$
+ *
+ */
+
+#include <nfs/nfs_mount.h>
diff --git a/usr.sbin/amd/config/misc-stellix.h b/usr.sbin/amd/config/misc-stellix.h
new file mode 100644
index 0000000..275a853
--- /dev/null
+++ b/usr.sbin/amd/config/misc-stellix.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (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-stellix.h 8.1 (Berkeley) 6/6/93
+ *
+ */
+
+#include <sys/fstyp.h>
+
+struct ufs_args {
+ char *fspec;
+};
+
+struct nfs_args {
+ struct sockaddr_in *addr; /* file server address */
+ fhandle_t *fh; /* File handle to be mounted */
+ int flags; /* flags */
+ int wsize; /* write size in bytes */
+ int rsize; /* read size in bytes */
+ int timeo; /* initial timeout in .1 secs *
+/
+ int retrans; /* times to retry send */
+ char *hostname; /* server's name */
+};
+#define NFSMNT_SOFT 0x001 /* soft mount (hard is default) */
+#define NFSMNT_WSIZE 0x002 /* set write size */
+#define NFSMNT_RSIZE 0x004 /* set read size */
+#define NFSMNT_TIMEO 0x008 /* set initial timeout (= 1.6 sec) */
+#define NFSMNT_RETRANS 0x010 /* set number of request retrys */
+#define NFSMNT_HOSTNAME 0x020 /* set hostname for error printf */
+#define NFSMNT_INT 0x040 /* allow interrupts on hard mount */
diff --git a/usr.sbin/amd/config/misc-ultrix.h b/usr.sbin/amd/config/misc-ultrix.h
new file mode 100644
index 0000000..18b55c8
--- /dev/null
+++ b/usr.sbin/amd/config/misc-ultrix.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (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-ultrix.h 8.1 (Berkeley) 6/6/93
+ *
+ * $Id$
+ *
+ */
+
+#include <nfs/nfs_gfs.h>
+#define KERNEL
+#include <sys/fs_types.h>
+#undef KERNEL
+
+#ifndef HOSTNAMESZ
+#include <nfs/nfs_clnt.h>
+#endif
+
+#include <ufs/ufs_mount.h>
+
+#define ufs_args ufs_specific
diff --git a/usr.sbin/amd/config/mount_aix.c b/usr.sbin/amd/config/mount_aix.c
new file mode 100644
index 0000000..b41f28e
--- /dev/null
+++ b/usr.sbin/amd/config/mount_aix.c
@@ -0,0 +1,153 @@
+/*
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (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_aix.c 8.1 (Berkeley) 6/6/93
+ *
+ * $Id$
+ *
+ */
+
+
+/*
+ * AIX 3 Mount helper
+ */
+
+#include "misc-aix3.h"
+
+static int aix3_mkvp(p, gfstype, flags, object, stub, host, info, info_size, args)
+char *p;
+int gfstype;
+int flags;
+char *object;
+char *stub;
+char *host;
+char *info;
+int info_size;
+char *args;
+{
+ struct vmount *vp = (struct vmount *) p;
+ bzero((voidp) vp, sizeof(*vp));
+ /*
+ * Fill in standard fields
+ */
+ vp->vmt_revision = VMT_REVISION;
+ vp->vmt_flags = flags;
+ vp->vmt_gfstype = gfstype;
+
+#define VMT_ROUNDUP(len) (4 * ((len + 3) / 4))
+#define VMT_ASSIGN(vp, idx, data, size) \
+ vp->vmt_data[idx].vmt_off = p - (char *) vp; \
+ vp->vmt_data[idx].vmt_size = size; \
+ bcopy(data, p, size); \
+ p += VMT_ROUNDUP(size);
+
+ /*
+ * Fill in all variable length data
+ */
+ p += sizeof(*vp);
+
+ VMT_ASSIGN(vp, VMT_OBJECT, object, strlen(object) + 1);
+ VMT_ASSIGN(vp, VMT_STUB, stub, strlen(stub) + 1);
+ VMT_ASSIGN(vp, VMT_HOST, host, strlen(host) + 1);
+ VMT_ASSIGN(vp, VMT_HOSTNAME, host, strlen(host) + 1);
+ VMT_ASSIGN(vp, VMT_INFO, info, info_size);
+ VMT_ASSIGN(vp, VMT_ARGS, args, strlen(args) + 1);
+
+#undef VMT_ASSIGN
+#undef VMT_ROUNDUP
+
+ /*
+ * Return length
+ */
+ return vp->vmt_length = p - (char *) vp;
+}
+
+/*
+ * Map from conventional mount arguments
+ * to AIX 3-style arguments.
+ */
+aix3_mount(fsname, dir, flags, type, data, args)
+char *fsname;
+char *dir;
+int flags;
+int type;
+void *data;
+char *args;
+{
+ char buf[4096];
+ int size;
+
+#ifdef DEBUG
+ dlog("aix3_mount: fsname %s, dir %s, type %d", fsname, dir, type);
+#endif /* DEBUG */
+
+/* aix3_mkvp(p, gfstype, flags, object, stub, host, info, info_size, args) */
+
+ switch (type) {
+
+ case MOUNT_TYPE_NFS: {
+ char *host = strdup(fsname);
+ char *rfs = strchr(host, ':');
+ int free_rfs = 0;
+ if (rfs) {
+ *rfs++ = '\0';
+ } else {
+ rfs = host;
+ free_rfs = 1;
+ host = strdup(hostname);
+ }
+
+ size = aix3_mkvp(buf, type, flags, rfs, dir, host, data, sizeof(struct nfs_args), args);
+ if (free_rfs)
+ free((voidp) rfs);
+ free(host);
+
+ } break;
+
+ case MOUNT_TYPE_UFS:
+ /* Need to open block device and extract log device info from sblk. */
+ return EINVAL;
+
+ default:
+ return EINVAL;
+ }
+#ifdef DEBUG
+ /*dlog("aix3_mkvp: flags %#x, size %d, args %s", flags, size, args);*/
+#endif /* DEBUG */
+
+ return vmount(buf, size);
+}
diff --git a/usr.sbin/amd/config/mount_irix.c b/usr.sbin/amd/config/mount_irix.c
new file mode 100644
index 0000000..caaebc1
--- /dev/null
+++ b/usr.sbin/amd/config/mount_irix.c
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (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_irix.c 8.1 (Berkeley) 6/6/93
+ *
+ * $Id$
+ *
+ */
+
+
+/*
+ * IRIX Mount helper
+ */
+
+#include "misc-irix.h"
+
+/*
+ * Map from conventional mount arguments
+ * to IRIX style arguments.
+ */
+irix_mount(fsname, dir, flags, type, data)
+char *fsname;
+char *dir;
+int flags;
+int type;
+void *data;
+{
+ int size;
+
+#ifdef DEBUG
+ dlog("irix_mount: fsname %s, dir %s, type %d", fsname, dir, type);
+#endif /* DEBUG */
+
+ if (type == MOUNT_TYPE_NFS) {
+
+ size = sizeof (struct nfs_args);
+
+ return mount(dir, dir, (MS_FSS|MS_DATA|flags),
+ type, (struct nfs_args *) data, size);
+
+ } else if (type == MOUNT_TYPE_UFS) {
+
+ return mount(fsname, dir, (MS_FSS|flags), type);
+
+ } else {
+ return EINVAL;
+ }
+
+}
diff --git a/usr.sbin/amd/config/mount_stellix.c b/usr.sbin/amd/config/mount_stellix.c
new file mode 100644
index 0000000..45703db
--- /dev/null
+++ b/usr.sbin/amd/config/mount_stellix.c
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (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_stellix.c 8.1 (Berkeley) 6/6/93
+ */
+
+/*
+ * IRIX Mount helper
+ */
+
+#include "misc-stellix.h"
+
+/*
+ * Map from conventional mount arguments
+ * to IRIX style arguments.
+ */
+stellix_mount(fsname, dir, flags, type, data)
+char *fsname;
+char *dir;
+int flags;
+int type;
+void *data;
+{
+
+#ifdef DEBUG
+ dlog("stellix_mount: fsname %s, dir %s, type %d", fsname, dir, type);
+#endif /* DEBUG */
+
+ if (type == MOUNT_TYPE_NFS) {
+
+ return mount(dir, dir, (MS_FSS|MS_NFS|flags),
+ type, (caddr_t) data );
+
+ } else if (type == MOUNT_TYPE_UFS) {
+
+ return mount(fsname, dir, (MS_FSS|flags), type);
+
+ } else {
+ return EINVAL;
+ }
+
+}
diff --git a/usr.sbin/amd/config/mtab_aix.c b/usr.sbin/amd/config/mtab_aix.c
new file mode 100644
index 0000000..ea90498
--- /dev/null
+++ b/usr.sbin/amd/config/mtab_aix.c
@@ -0,0 +1,137 @@
+/*
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)mtab_aix.c 8.1 (Berkeley) 6/6/93
+ *
+ * $Id$
+ *
+ */
+
+#include "am.h"
+
+#ifdef READ_MTAB_AIX3_STYLE
+
+#include <sys/mntctl.h>
+#include <sys/vmount.h>
+
+static struct mntent *mnt_dup(mp)
+struct vmount *mp;
+{
+ struct mntent *new_mp = ALLOC(mntent);
+
+ char *ty;
+ new_mp->mnt_fsname = strdup(vmt2dataptr(mp, VMT_OBJECT));
+ new_mp->mnt_dir = strdup(vmt2dataptr(mp, VMT_STUB));
+ new_mp->mnt_opts = strdup(vmt2dataptr(mp, VMT_ARGS));
+ switch (mp->vmt_gfstype) {
+ case MNT_JFS: ty = MTAB_TYPE_UFS; break;
+ case MNT_NFS:
+ ty = MTAB_TYPE_NFS;
+ new_mp->mnt_fsname = str3cat(new_mp->mnt_fsname,
+ vmt2dataptr(mp, VMT_HOSTNAME),
+ ":", new_mp->mnt_fsname);
+ break;
+ default: ty = "unknown"; break;
+ }
+ new_mp->mnt_type = strdup(ty);
+ new_mp->mnt_passno = mp->vmt_vfsnumber;
+ new_mp->mnt_freq = 0;
+
+ return new_mp;
+}
+
+/*
+ * Read a mount table into memory
+ */
+mntlist *read_mtab(fs)
+char *fs;
+{
+ mntlist **mpp, *mhp;
+
+ int i;
+ char *mntinfo = 0, *cp;
+ struct vmount *vp;
+ int ret;
+
+ /*
+ * First figure out size of mount table
+ * and allocate space for a copy...
+ * Then get mount table for real.
+ */
+ ret = mntctl(MCTL_QUERY, sizeof(i), &i);
+ if (ret == 0) {
+ mntinfo = xmalloc(i);
+ ret = mntctl(MCTL_QUERY, i, mntinfo);
+ }
+
+ if (ret <= 0) {
+ plog(XLOG_ERROR, "mntctl: %m");
+ goto out;
+ }
+#ifdef DEBUG
+ /*dlog("mntctl returns %d structures", ret);*/
+#endif /* DEBUG */
+
+ mpp = &mhp;
+ for (i = 0, cp = mntinfo; i < ret; i++, cp += vp->vmt_length) {
+ vp = (struct vmount *) cp;
+
+ /*
+ * Allocate a new slot
+ */
+ *mpp = ALLOC(mntlist);
+
+ /*
+ * Copy the data returned by mntctl
+ */
+ (*mpp)->mnt = mnt_dup(vp);
+
+ /*
+ * Move to next pointer
+ */
+ mpp = &(*mpp)->mnext;
+ }
+
+ *mpp = 0;
+
+out:
+ if (mntinfo)
+ free(mntinfo);
+ return mhp;
+}
+
+#endif /* READ_MTAB_AIX3_STYLE */
diff --git a/usr.sbin/amd/config/mtab_bsd.c b/usr.sbin/amd/config/mtab_bsd.c
new file mode 100644
index 0000000..39b4228
--- /dev/null
+++ b/usr.sbin/amd/config/mtab_bsd.c
@@ -0,0 +1,114 @@
+/*
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)mtab_bsd.c 8.1 (Berkeley) 6/6/93
+ *
+ * $Id$
+ *
+ */
+
+#include "am.h"
+
+#ifdef READ_MTAB_BSD_STYLE
+
+#include <sys/mount.h>
+
+static struct mntent *mnt_dup(mp)
+struct statfs *mp;
+{
+ struct mntent *new_mp = ALLOC(mntent);
+ char *ty;
+
+ new_mp->mnt_fsname = strdup(mp->f_mntfromname);
+ new_mp->mnt_dir = strdup(mp->f_mntonname);
+ switch (mp->f_type) {
+ case MOUNT_UFS: ty = MTAB_TYPE_UFS; break;
+ case MOUNT_NFS: ty = MTAB_TYPE_NFS; break;
+ case MOUNT_MFS: ty = MTAB_TYPE_MFS; break;
+ default: ty = "unknown"; break;
+ }
+ new_mp->mnt_type = strdup(ty);
+ new_mp->mnt_opts = strdup("unset");
+ new_mp->mnt_freq = 0;
+ new_mp->mnt_passno = 0;
+
+ return new_mp;
+}
+
+/*
+ * Read a mount table into memory
+ */
+mntlist *read_mtab(fs)
+char *fs;
+{
+ mntlist **mpp, *mhp;
+ struct statfs *mntbufp, *mntp;
+
+ int nloc = getmntinfo(&mntbufp, MNT_NOWAIT);
+
+ if (nloc == 0) {
+ plog(XLOG_ERROR, "Can't read mount table");
+ return 0;
+ }
+
+ mpp = &mhp;
+ for (mntp = mntbufp; mntp < mntbufp + nloc; mntp++) {
+ /*
+ * Allocate a new slot
+ */
+ *mpp = ALLOC(mntlist);
+
+ /*
+ * Copy the data returned by getmntent
+ */
+ (*mpp)->mnt = mnt_dup(mntp);
+
+ /*
+ * Move to next pointer
+ */
+ mpp = &(*mpp)->mnext;
+ }
+
+ /*
+ * Terminate the list
+ */
+ *mpp = 0;
+
+ return mhp;
+}
+
+#endif /* READ_MTAB_BSD_STYLE */
diff --git a/usr.sbin/amd/config/mtab_file.c b/usr.sbin/amd/config/mtab_file.c
new file mode 100644
index 0000000..f08cae8
--- /dev/null
+++ b/usr.sbin/amd/config/mtab_file.c
@@ -0,0 +1,472 @@
+/*
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)mtab_file.c 8.1 (Berkeley) 6/6/93
+ *
+ * $Id$
+ *
+ */
+
+#include "am.h"
+
+#ifdef READ_MTAB_FROM_FILE
+
+#ifdef USE_FCNTL
+#include <fcntl.h>
+#else
+#include <sys/file.h>
+#endif /* USE_FCNTL */
+
+#ifdef UPDATE_MTAB
+
+/*
+ * Do strict /etc/mtab locking
+ */
+#define MTAB_LOCKING
+
+/*
+ * Firewall mtab entries
+ */
+#define MTAB_STRIPNL
+
+#include <sys/stat.h>
+static FILE *mnt_file;
+
+/*
+ * If the system is being trashed by something, then
+ * opening mtab may fail with ENFILE. So, go to sleep
+ * for a second and try again. (Yes - this has happened to me.)
+ *
+ * Note that this *may* block the automounter, oh well.
+ * If we get to this state then things are badly wrong anyway...
+ *
+ * Give the system 10 seconds to recover but then give up.
+ * Hopefully something else will exit and free up some file
+ * table slots in that time.
+ */
+#define NFILE_RETRIES 10 /* seconds */
+
+#ifdef MTAB_LOCKING
+#ifdef LOCK_FCNTL
+static int lock(fd)
+{
+ int rc;
+ struct flock lk;
+
+ lk.l_type = F_WRLCK;
+ lk.l_whence = 0;
+ lk.l_start = 0;
+ lk.l_len = 0;
+
+again:
+ rc = fcntl(fd, F_SETLKW, (caddr_t) &lk);
+ if (rc < 0 && (errno == EACCES || errno == EAGAIN)) {
+#ifdef DEBUG
+ dlog("Blocked, trying to obtain exclusive mtab lock");
+#endif /* DEBUG */
+ sleep(1);
+ goto again;
+ }
+ return rc;
+}
+#else
+#define lock(fd) (flock((fd), LOCK_EX))
+#endif /* LOCK_FCNTL */
+#endif /* MTAB_LOCKING */
+
+static FILE *open_locked_mtab(mtab_file, mode, fs)
+char *mtab_file;
+char *mode;
+char *fs;
+{
+ FILE *mfp = 0;
+
+#ifdef UPDATE_MTAB
+ /*
+ * There is a possible race condition if two processes enter
+ * this routine at the same time. One will be blocked by the
+ * exclusive lock below (or by the shared lock in setmntent)
+ * and by the time the second process has the exclusive lock
+ * it will be on the wrong underlying object. To check for this
+ * the mtab file is stat'ed before and after all the locking
+ * sequence, and if it is a different file then we assume that
+ * it may be the wrong file (only "may", since there is another
+ * race between the initial stat and the setmntent).
+ *
+ * Simpler solutions to this problem are invited...
+ */
+ int racing = 2;
+#ifdef MTAB_LOCKING
+ int rc;
+ int retries = 0;
+ struct stat st_before, st_after;
+#endif /* MTAB_LOCKING */
+
+ if (mnt_file) {
+#ifdef DEBUG
+ dlog("Forced close on %s in read_mtab", mtab_file);
+#endif /* DEBUG */
+ endmntent(mnt_file);
+ mnt_file = 0;
+ }
+
+#ifdef MTAB_LOCKING
+again:
+ if (mfp) {
+ endmntent(mfp);
+ mfp = 0;
+ }
+
+ clock_valid = 0;
+ if (stat(mtab_file, &st_before) < 0) {
+ plog(XLOG_ERROR, "%s: stat: %m", mtab_file);
+ if (errno == ESTALE) {
+ /* happens occasionally */
+ sleep(1);
+ goto again;
+ }
+ return 0;
+ }
+#endif /* MTAB_LOCKING */
+#endif /* UPDATE_MTAB */
+
+eacces:
+ mfp = setmntent(mtab_file, mode);
+ if (!mfp) {
+ /*
+ * Since setmntent locks the descriptor, it
+ * is possible it can fail... so retry if
+ * needed.
+ */
+ if (errno == EACCES || errno == EAGAIN) {
+#ifdef DEBUG
+ dlog("Blocked, trying to obtain exclusive mtab lock");
+#endif /* DEBUG */
+ goto eacces;
+ } else if (errno == ENFILE && retries++ < NFILE_RETRIES) {
+ sleep(1);
+ goto eacces;
+ }
+
+ plog(XLOG_ERROR, "setmntent(\"%s\", \"%s\"): %m", mtab_file, mode);
+ return 0;
+ }
+
+#ifdef MTAB_LOCKING
+#ifdef UPDATE_MTAB
+ /*
+ * At this point we have an exclusive lock on the mount list,
+ * but it may be the wrong one so...
+ */
+
+ /*
+ * Need to get an exclusive lock on the current
+ * mount table until we have a new copy written
+ * out, when the lock is released in free_mntlist.
+ * flock is good enough since the mount table is
+ * not shared between machines.
+ */
+ do
+ rc = lock(fileno(mfp));
+ while (rc < 0 && errno == EINTR);
+ if (rc < 0) {
+ plog(XLOG_ERROR, "Couldn't lock %s: %m", mtab_file);
+ endmntent(mfp);
+ return 0;
+ }
+ /*
+ * Now check whether the mtab file has changed under our feet
+ */
+ if (stat(mtab_file, &st_after) < 0) {
+ plog(XLOG_ERROR, "%s: stat", mtab_file);
+ goto again;
+ }
+
+ if (st_before.st_dev != st_after.st_dev ||
+ st_before.st_ino != st_after.st_ino) {
+ struct timeval tv;
+ if (racing == 0) {
+ /* Sometimes print a warning */
+ plog(XLOG_WARNING,
+ "Possible mount table race - retrying %s", fs);
+ }
+ racing = (racing+1) & 3;
+ /*
+ * Take a nap. From: Doug Kingston <dpk@morgan.com>
+ */
+ tv.tv_sec = 0;
+ tv.tv_usec = (mypid & 0x07) << 17;
+ if (tv.tv_usec)
+ if (select(0, (voidp) 0, (voidp) 0, (voidp) 0, &tv) < 0)
+ plog(XLOG_WARNING, "mtab nap failed: %m");
+
+ goto again;
+ }
+#endif /* UPDATE_MTAB */
+#endif /* MTAB_LOCKING */
+
+ return mfp;
+}
+
+/*
+ * Unlock the mount table
+ */
+void unlock_mntlist P((void));
+void unlock_mntlist()
+{
+ /*
+ * Release file lock, by closing the file
+ */
+ if (mnt_file) {
+ endmntent(mnt_file);
+ mnt_file = 0;
+ }
+}
+
+/*
+ * Write out a mount list
+ */
+void rewrite_mtab(mp)
+mntlist *mp;
+{
+ FILE *mfp;
+ int error = 0;
+
+ /*
+ * Concoct a temporary name in the same
+ * directory as the target mount table
+ * so that rename() will work.
+ */
+ char tmpname[64];
+ int retries;
+ int tmpfd;
+ char *cp;
+ char *mcp = mtab;
+ cp = strrchr(mcp, '/');
+ if (cp) {
+ bcopy(mcp, tmpname, cp - mcp);
+ tmpname[cp-mcp] = '\0';
+ } else {
+ plog(XLOG_WARNING, "No '/' in mtab (%s), using \".\" as tmp directory", mtab);
+ tmpname[0] = '.'; tmpname[1] = '\0';
+ }
+ strcat(tmpname, "/mtabXXXXXX");
+ mktemp(tmpname);
+ retries = 0;
+enfile1:
+ if ((tmpfd = open(tmpname, O_RDWR|O_CREAT|O_TRUNC, 0644)) < 0) {
+ if (errno == ENFILE && retries++ < NFILE_RETRIES) {
+ sleep(1);
+ goto enfile1;
+ }
+ plog(XLOG_ERROR, "%s: open: %m", tmpname);
+ return;
+ }
+ if (close(tmpfd) < 0)
+ plog(XLOG_ERROR, "Couldn't close tmp file descriptor: %m");
+
+ retries = 0;
+enfile2:
+ mfp = setmntent(tmpname, "w");
+ if (!mfp) {
+ if (errno == ENFILE && retries++ < NFILE_RETRIES) {
+ sleep(1);
+ goto enfile2;
+ }
+ plog(XLOG_ERROR, "setmntent(\"%s\", \"w\"): %m", tmpname);
+ error = 1;
+ goto out;
+ }
+
+ while (mp) {
+ if (mp->mnt) {
+ if (addmntent(mfp, mp->mnt)) {
+ plog(XLOG_ERROR, "Can't write entry to %s", tmpname);
+ error = 1;
+ goto out;
+ }
+ }
+ mp = mp->mnext;
+ }
+
+ /*
+ * SunOS 4.1 manuals say that the return code from entmntent()
+ * is always 1 and to treat as a void. That means we need to
+ * call fflush() to make sure the new mtab file got written.
+ */
+ if (fflush(mfp)) {
+ plog(XLOG_ERROR, "flush new mtab file: %m");
+ error = 1;
+ goto out;
+ }
+
+ (void) endmntent(mfp);
+
+ /*
+ * Rename temporary mtab to real mtab
+ */
+ if (rename(tmpname, mtab) < 0) {
+ plog(XLOG_ERROR, "rename %s to %s: %m", tmpname, mtab);
+ error = 1;
+ goto out;
+ }
+
+out:
+ if (error)
+ (void) unlink(tmpname);
+}
+
+#ifdef MTAB_STRIPNL
+static void mtab_stripnl(s)
+char *s;
+{
+ do {
+ s = strchr(s, '\n');
+ if (s)
+ *s++ = ' ';
+ } while (s);
+}
+#endif /* MTAB_STRIPNL */
+
+/*
+ * Append a mntent structure to the
+ * current mount table.
+ */
+void write_mntent(mp)
+struct mntent *mp;
+{
+ int retries = 0;
+ FILE *mfp;
+enfile:
+ mfp = open_locked_mtab(mtab, "a", mp->mnt_dir);
+ if (mfp) {
+#ifdef MTAB_STRIPNL
+ mtab_stripnl(mp->mnt_opts);
+#endif /* MTAB_STRIPNL */
+ if (addmntent(mfp, mp))
+ plog(XLOG_ERROR, "Couldn't write %s: %m", mtab);
+ if (fflush(mfp))
+ plog(XLOG_ERROR, "Couldn't flush %s: %m", mtab);
+ (void) endmntent(mfp);
+ } else {
+ if (errno == ENFILE && retries < NFILE_RETRIES) {
+ sleep(1);
+ goto enfile;
+ }
+ plog(XLOG_ERROR, "setmntent(\"%s\", \"a\"): %m", mtab);
+ }
+}
+
+#endif /* UPDATE_MTAB */
+
+static struct mntent *mnt_dup(mp)
+struct mntent *mp;
+{
+ struct mntent *new_mp = ALLOC(mntent);
+
+ new_mp->mnt_fsname = strdup(mp->mnt_fsname);
+ new_mp->mnt_dir = strdup(mp->mnt_dir);
+ new_mp->mnt_type = strdup(mp->mnt_type);
+ new_mp->mnt_opts = strdup(mp->mnt_opts);
+
+ new_mp->mnt_freq = mp->mnt_freq;
+ new_mp->mnt_passno = mp->mnt_passno;
+
+#ifdef FIXUP_MNTENT_DUP
+ /*
+ * Additional fields get dup'ed here
+ */
+ FIXUP_MNTENT_DUP(new_mp, mp);
+#endif
+
+ return new_mp;
+}
+
+/*
+ * Read a mount table into memory
+ */
+mntlist *read_mtab(fs)
+char *fs;
+{
+ mntlist **mpp, *mhp;
+
+ struct mntent *mep;
+ FILE *mfp = open_locked_mtab(mtab, "r+", fs);
+
+ if (!mfp)
+ return 0;
+
+ mpp = &mhp;
+
+/*
+ * XXX - In SunOS 4 there is (yet another) memory leak
+ * which loses 1K the first time getmntent is called.
+ * (jsp)
+ */
+ while (mep = getmntent(mfp)) {
+ /*
+ * Allocate a new slot
+ */
+ *mpp = ALLOC(mntlist);
+
+ /*
+ * Copy the data returned by getmntent
+ */
+ (*mpp)->mnt = mnt_dup(mep);
+
+ /*
+ * Move to next pointer
+ */
+ mpp = &(*mpp)->mnext;
+ }
+ *mpp = 0;
+
+#ifdef UPDATE_MTAB
+ /*
+ * If we are not updating the mount table then we
+ * can free the resources held here, otherwise they
+ * must be held until the mount table update is complete
+ */
+ mnt_file = mfp;
+#else
+ endmntent(mfp);
+#endif /* UPDATE_MTAB */
+
+ return mhp;
+}
+
+#endif /* READ_MTAB_FROM_FILE */
diff --git a/usr.sbin/amd/config/mtab_ultrix.c b/usr.sbin/amd/config/mtab_ultrix.c
new file mode 100644
index 0000000..0ee36ea
--- /dev/null
+++ b/usr.sbin/amd/config/mtab_ultrix.c
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)mtab_ultrix.c 8.1 (Berkeley) 6/6/93
+ *
+ * $Id$
+ *
+ */
+
+#include "am.h"
+
+#ifdef READ_MTAB_ULTRIX_STYLE
+
+#include <sys/mount.h>
+#include <sys/fs_types.h>
+
+static struct mntent *mnt_dup(mp)
+struct fs_data *mp;
+{
+ struct mntent *new_mp = ALLOC(mntent);
+
+ new_mp->mnt_fsname = strdup(mp->fd_devname);
+ new_mp->mnt_dir = strdup(mp->fd_path);
+ if (mp->fd_fstype >= GT_NUMTYPES)
+ mp->fd_fstype = GT_UNKWN;
+ else if (gt_names[mp->fd_fstype] == 0)
+ mp->fd_fstype = GT_UNKWN;
+ new_mp->mnt_type = strdup(gt_names[mp->fd_fstype]);
+ new_mp->mnt_opts = strdup("unset");
+
+ new_mp->mnt_freq = 0;
+ new_mp->mnt_passno = mp->fd_dev;
+
+ return new_mp;
+}
+
+/*
+ * Read a mount table into memory
+ */
+mntlist *read_mtab(fs)
+char *fs;
+{
+ mntlist **mpp, *mhp;
+
+/* From: Piete Brooks <pb@cl.cam.ac.uk> */
+
+ int loc=0;
+#undef NMOUNT
+#define NMOUNT 20
+ struct fs_data mountbuffer[NMOUNT], *fs_data;
+ int ret;
+
+ mpp = &mhp;
+ while ((ret = getmountent(&loc, mountbuffer, NMOUNT)) > 0) {
+ for (fs_data = mountbuffer; fs_data < &mountbuffer[ret]; fs_data++) {
+ /*
+ * Allocate a new slot
+ */
+ *mpp = ALLOC(mntlist);
+
+ /*
+ * Copy the data returned by getmntent
+ */
+ (*mpp)->mnt = mnt_dup(fs_data);
+
+ /*
+ * Move to next pointer
+ */
+ mpp = &(*mpp)->mnext;
+ }
+ }
+ if (ret < 0) {
+ plog(XLOG_ERROR, "getmountent: %m");
+ return 0;
+ }
+ *mpp = 0;
+
+ return mhp;
+}
+
+#endif /* READ_MTAB_ULTRIX_STYLE */
diff --git a/usr.sbin/amd/config/newvers.sh b/usr.sbin/amd/config/newvers.sh
new file mode 100644
index 0000000..1664a2e
--- /dev/null
+++ b/usr.sbin/amd/config/newvers.sh
@@ -0,0 +1,88 @@
+#!/bin/sh -
+#
+# Copyright (c) 1989 Jan-Simon Pendry
+# Copyright (c) 1989 Imperial College of Science, Technology & Medicine
+# Copyright (c) 1989, 1993
+# The Regents of the University of California. All Rights Reserved.
+#
+# This code is derived from software contributed to Berkeley by
+# Jan-Simon Pendry at Imperial College, London.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. All advertising materials mentioning features or use of this software
+# must display the following acknowledgement:
+# This product includes software developed by the University of
+# California, Berkeley and its contributors.
+# 4. Neither the name of the University nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# @(#)newvers.sh 8.1 (Berkeley) 6/6/93
+#
+# $Id$
+#
+PATH=/usr/ucb:/bin:/usr/bin:$PATH
+if [ $# -ne 3 ]; then echo "Usage: newvers program arch os" >&2; exit 1; fi
+version="version.$1"
+if [ ! -r $version ]; then echo 0 > $version; chmod 444 $version; fi
+v=`cat $version`
+u=${USER-${LOGNAME-root}}
+h=`hostname`
+#h=`expr "$h" : '\([^.]*\)'`
+t=`LC_TIME=C date`
+if [ ! -s "$d../config/RELEASE" -o ! -s "$d../text/COPYRIGHT" ]; then
+ echo ERROR: config file missing >&2
+ exit 1
+fi
+rm -f vers.$1.c
+(
+cat << %%
+char copyright[] = "\\
+%%
+sed 's/$/\\n\\/' $d../text/COPYRIGHT
+cat << %%
+";
+char version[] = "\\
+%%
+cat << %%
+$1 \\
+%%
+sed \
+ -e 's/\$//g' \
+ -e 's/[A-Z][a-z]*://g' \
+ -e 's/ */ /g' \
+ -e 's/^ //' \
+ -e 's/$/\\/' \
+ $d../config/RELEASE
+cat << %%
+ #${v}: ${t}\\n\\
+Built by ${u}@${h} for \\
+%%
+case "$2" in
+[aeiou]*) echo "an \\" ;;
+*) echo "a \\";;
+esac
+echo "$2 running $3\";"
+) > vers.$1.c
+rm -f $version
+expr ${v} + 1 > $version
+chmod 444 $version
diff --git a/usr.sbin/amd/config/os-acis43.h b/usr.sbin/amd/config/os-acis43.h
new file mode 100644
index 0000000..5882bdb
--- /dev/null
+++ b/usr.sbin/amd/config/os-acis43.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 1989 Jan-Simon Pendry
+ * Copyright (c) 1989 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)os-acis43.h 8.1 (Berkeley) 6/6/93
+ *
+ * $Id$
+ *
+ * IBM RT ACIS4.3 definitions for Amd (automounter)
+ */
+
+/*
+ * Does the compiler grok void *
+ */
+#define VOIDP
+
+/*
+ * Which version of the Sun RPC library we are using
+ * This is the implementation release number, not
+ * the protocol revision number.
+ */
+#define RPC_3
+
+/*
+ * Which version of the NFS interface are we using.
+ * This is the implementation release number, not
+ * the protocol revision number.
+ */
+#define NFS_3
+
+/*
+ * Does this OS have NDBM support?
+ */
+#define OS_HAS_NDBM
+
+/*
+ * Name of filesystem types
+ */
+#define MOUNT_TYPE_NFS MOUNT_NFS
+#define MOUNT_TYPE_UFS MOUNT_UFS
+#undef MTAB_TYPE_UFS
+#define MTAB_TYPE_UFS "ufs"
+
+#undef ARCH_ENDIAN
+#define ARCH_ENDIAN "big"
+
+/*
+ * Need precise symlink lengths
+#define PRECISE_SYMLINKS
+ */
diff --git a/usr.sbin/amd/config/os-aix3.h b/usr.sbin/amd/config/os-aix3.h
new file mode 100644
index 0000000..3e35ecc
--- /dev/null
+++ b/usr.sbin/amd/config/os-aix3.h
@@ -0,0 +1,181 @@
+/*
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)os-aix3.h 8.1 (Berkeley) 6/6/93
+ *
+ * $Id$
+ *
+ * AIX 3.1 definitions for Amd (automounter)
+ */
+
+/*
+ * Does the compiler grok void *
+ */
+#define VOIDP
+
+/*
+ * Which version of the Sun RPC library we are using
+ * This is the implementation release number, not
+ * the protocol revision number.
+ */
+#define RPC_4
+
+/*
+ * Which version of the NFS interface are we using.
+ * This is the implementation release number, not
+ * the protocol revision number.
+ */
+#define NFS_AIX3
+
+/*
+ * Does this OS have NDBM support?
+ */
+#define OS_HAS_NDBM
+
+/*
+ * The mount table is obtained from the kernel
+ */
+#undef UPDATE_MTAB
+
+/*
+ * Pick up BSD bits from include files
+ * Try for 4.4 compatibility if available (AIX 3.2 and later)
+ */
+#define _BSD 44
+
+/*
+ * No mntent info on AIX 3
+ */
+#undef MNTENT_HDR
+#define MNTENT_HDR <sys/mntctl.h>
+
+/*
+ * Name of filesystem types
+ */
+#define MOUNT_TYPE_NFS MNT_NFS
+#define MOUNT_TYPE_UFS MNT_JFS
+#undef MTAB_TYPE_UFS
+#define MTAB_TYPE_UFS "jfs"
+
+/*
+ * How to unmount filesystems
+ */
+#undef MOUNT_TRAP
+#define MOUNT_TRAP(type, mnt, flag, mnt_data) \
+ aix3_mount(mnt->mnt_fsname, mnt->mnt_dir, flag, type, mnt_data, mnt->mnt_opts)
+#undef UNMOUNT_TRAP
+#define UNMOUNT_TRAP(mnt) uvmount(mnt->mnt_passno, 0)
+
+
+/*
+ * Byte ordering
+ */
+#ifndef BYTE_ORDER
+#include <sys/machine.h>
+#endif /* BYTE_ORDER */
+
+#undef ARCH_ENDIAN
+#if BYTE_ORDER == LITTLE_ENDIAN
+#define ARCH_ENDIAN "little"
+#else
+#if BYTE_ORDER == BIG_ENDIAN
+#define ARCH_ENDIAN "big"
+#else
+XXX - Probably no hope of running Amd on this machine!
+#endif /* BIG */
+#endif /* LITTLE */
+
+/*
+ * Miscellaneous AIX 3 bits
+ */
+#define NEED_MNTOPT_PARSER
+#define SHORT_MOUNT_NAME
+
+#define MNTMAXSTR 128
+
+#define MNTTYPE_UFS "jfs" /* Un*x file system */
+#define MNTTYPE_NFS "nfs" /* network file system */
+#define MNTTYPE_IGNORE "ignore" /* No type specified, ignore this entry */
+
+struct mntent {
+ char *mnt_fsname; /* name of mounted file system */
+ char *mnt_dir; /* file system path prefix */
+ char *mnt_type; /* MNTTYPE_* */
+ char *mnt_opts; /* MNTOPT* */
+ int mnt_freq; /* dump frequency, in days */
+ int mnt_passno; /* pass number on parallel fsck */
+};
+
+#define NFS_HDR "misc-aix3.h"
+#define UFS_HDR "misc-aix3.h"
+#undef NFS_FH_DREF
+#define NFS_FH_DREF(dst, src) { (dst) = *(src); }
+#undef NFS_SA_DREF
+#define NFS_SA_DREF(dst, src) { (dst).addr = *(src); }
+#define M_RDONLY MNT_READONLY
+
+/*
+ * How to get a mount list
+ */
+#undef READ_MTAB_FROM_FILE
+#define READ_MTAB_AIX3_STYLE
+
+/*
+ * The data for the mount syscall needs the path in addition to the
+ * host name since that is the only source of information about the
+ * mounted filesystem.
+#define NFS_ARGS_NEEDS_PATH
+ */
+
+#define NFS_LOMAP 34
+#define NFS_HIMAP 99
+#define NFS_ERROR_MAPPING \
+static nfs_errormap[] = { 0,75,77,99,99,99, \
+ 99,99,99,99,99,78,99,99,99,79, \
+ 99,99,70,99,35,36,37,38,39,40, \
+ 41,42,43,44,45,46,47,48,49,50, \
+ 51,52,53,54,55,56,57,58,60,61, \
+ 64,65,99,67,68,62,63,66,69,68, \
+ 99,99,99,71,99,99,99,99,99,99 \
+ };
+
+#define MOUNT_HELPER_SOURCE "mount_aix.c"
+
+/*
+ * Need this too
+ */
+#include <time.h>
diff --git a/usr.sbin/amd/config/os-aux.h b/usr.sbin/amd/config/os-aux.h
new file mode 100644
index 0000000..ca9449b
--- /dev/null
+++ b/usr.sbin/amd/config/os-aux.h
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 1989 Jan-Simon Pendry
+ * Copyright (c) 1989 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)os-aux.h 8.1 (Berkeley) 6/6/93
+ *
+ * $Id$
+ *
+ * A/UX macII definitions for Amd (automounter)
+ * Contributed by Julian Onions <jpo@cs.nott.ac.uk>
+ */
+
+/*
+ * Does the compiler grok void *
+ */
+#define VOIDP
+
+/*
+ * Which version of the Sun RPC library we are using
+ * This is the implementation release number, not
+ * the protocol revision number.
+ */
+#define RPC_3
+
+/*
+ * Which version of the NFS interface are we using.
+ * This is the implementation release number, not
+ * the protocol revision number.
+ */
+#define NFS_3
+
+/*
+ * Byte ordering
+ */
+#undef ARCH_ENDIAN
+#define ARCH_ENDIAN "big"
+
+/*
+ * No support for ndbm
+ */
+#undef OS_HAS_NDBM
+
+/*
+ * Name of filesystem types
+ */
+#define MOUNT_TYPE_UFS MOUNT_UFS
+#define MOUNT_TYPE_NFS MOUNT_NFS
+
+#undef MTAB_TYPE_UFS
+#define MTAB_TYPE_UFS "5.2"
+
+#define SIGCHLD SIGCLD
+#define SYS5_SIGNALS
+
+/*
+ * Use <fcntl.h> rather than <sys/file.h>
+ */
+#define USE_FCNTL
+
+/*
+ * Use fcntl() rather than flock()
+ */
+#define LOCK_FCNTL
+
+#ifdef __GNUC__
+#define alloca(sz) __builtin_alloca(sz)
+#endif
+
+#define bzero(ptr, len) memset(ptr, 0, len)
+#define bcopy(from, to, len) memcpy(to, from, len)
+#define getpagesize() (2048)
+#undef MOUNT_TRAP
+#define MOUNT_TRAP(type, mnt, flags, mnt_data) \
+ fsmount(type, mnt->mnt_dir, flags, mnt_data)
+#undef UNMOUNT_TRAP
+#define UNMOUNT_TRAP(mnt) unmount(mnt->mnt_dir)
+#define NFDS 30 /* conservative */
+
+/* not included in sys/param.h */
+#include <sys/types.h>
+/* not part of sys/time.h */
+#include <time.h>
+/* for NMOUNT */
+#include <sys/config.h>
diff --git a/usr.sbin/amd/config/os-bsd44.h b/usr.sbin/amd/config/os-bsd44.h
new file mode 100644
index 0000000..a0a2a76
--- /dev/null
+++ b/usr.sbin/amd/config/os-bsd44.h
@@ -0,0 +1,239 @@
+/*
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)os-bsd44.h 8.2 (Berkeley) 5/10/95
+ *
+ * $Id: os-bsd44.h,v 1.9 1997/03/12 08:29:44 peter Exp $
+ *
+ * 4.4 BSD definitions for Amd (automounter)
+ */
+
+/*
+ * Does the compiler grok void *
+ */
+#define VOIDP
+
+/*
+ * Which version of the Sun RPC library we are using
+ * This is the implementation release number, not
+ * the protocol revision number.
+ */
+#define RPC_4
+
+#include <sys/param.h>
+#if BSD >= 199506
+#define NFS_HDR "misc-bsd44l.h"
+#define UFS_HDR "misc-bsd44l.h"
+#endif
+
+/*
+ * Which version of the NFS interface are we using.
+ * This is the implementation release number, not
+ * the protocol revision number.
+ */
+#define NFS_4
+#define HAS_TCP_NFS
+#define NFSv3
+#define M_NEWTYPE 0
+
+/*
+ * Does this OS have NDBM support?
+ */
+#define OS_HAS_NDBM
+
+/*
+ * 4.4 doesn't provide NIS, but FreeBSD does.
+ */
+#define HAS_NIS_MAPS
+
+/*
+ * OS provides strerror()
+ */
+#define HAS_STRERROR
+
+/*
+ * The mount table is obtained from the kernel
+ */
+#undef UPDATE_MTAB
+
+/*
+ * No mntent info on 4.4 BSD
+ */
+#undef MNTENT_HDR
+
+
+/*
+ * How to unmount filesystems
+ */
+#undef UNMOUNT_TRAP
+#undef NEED_UMOUNT_FS
+#define NEED_UMOUNT_BSD
+
+/*
+ * How to copy an address into an NFS filehandle
+ */
+#undef NFS_SA_DREF
+#if BSD >= 199506
+#define NFS_SA_DREF(dst, src) { \
+ (dst).version = NFS_ARGSVERSION; \
+ (dst).addr = (struct sockaddr *) (src); \
+ (dst).addrlen = sizeof(*src); \
+ (dst).sotype = SOCK_DGRAM; \
+ (dst).proto = 0; \
+ (dst).wsize = NFS_WSIZE; \
+ (dst).rsize = NFS_RSIZE; \
+ (dst).readdirsize = NFS_READDIRSIZE; \
+ (dst).timeo = 10; \
+ (dst).retrans = NFS_RETRANS; \
+ (dst).maxgrouplist = NFS_MAXGRPS; \
+ (dst).readahead = NFS_DEFRAHEAD; \
+ (dst).leaseterm = 0; \
+ (dst).deadthresh = 0; \
+ }
+#else
+#define NFS_SA_DREF(dst, src) { \
+ (dst).addr = (struct sockaddr *) (src); \
+ (dst).addrlen = sizeof(*src); \
+ (dst).sotype = SOCK_DGRAM; \
+ (dst).proto = 0; \
+ }
+#endif
+
+/*
+ * Byte ordering
+ */
+#ifndef BYTE_ORDER
+#include <machine/endian.h>
+#endif /* BYTE_ORDER */
+
+#undef ARCH_ENDIAN
+#if BYTE_ORDER == LITTLE_ENDIAN
+#define ARCH_ENDIAN "little"
+#else
+#if BYTE_ORDER == BIG_ENDIAN
+#define ARCH_ENDIAN "big"
+#else
+XXX - Probably no hope of running Amd on this machine!
+#endif /* BIG */
+#endif /* LITTLE */
+
+/*
+ * Miscellaneous 4.4 BSD bits
+ */
+#define NEED_MNTOPT_PARSER
+#define SHORT_MOUNT_NAME
+
+#define MNTMAXSTR 128
+
+#define MNTTYPE_UFS "ufs" /* Un*x file system */
+#define MNTTYPE_NFS "nfs" /* network file system */
+#define MNTTYPE_MFS "mfs" /* memory file system */
+#define MNTTYPE_IGNORE "ignore" /* No type specified, ignore this entry */
+
+#define M_RDONLY MNT_RDONLY
+#define M_SYNC MNT_SYNCHRONOUS
+#define M_NOEXEC MNT_NOEXEC
+#define M_NOSUID MNT_NOSUID
+#define M_NODEV MNT_NODEV
+
+#define MNTOPT_SOFT "soft" /* soft mount */
+#define MNTOPT_INTR "intr" /* interrupts allowed */
+#define MNTOPT_NOCONN "noconn" /* no connection option allowed */
+
+#define NFSMNT_HOSTNAME 0 /* hostname on 4.4 is not optional */
+
+struct mntent {
+ char *mnt_fsname; /* name of mounted file system */
+ char *mnt_dir; /* file system path prefix */
+ char *mnt_type; /* MNTTYPE_* */
+ char *mnt_opts; /* MNTOPT* */
+ int mnt_freq; /* dump frequency, in days */
+ int mnt_passno; /* pass number on parallel fsck */
+};
+
+/*
+ * Type of a file handle
+ */
+#undef NFS_FH_TYPE
+#ifdef NFSv3
+#define NFS_FH_TYPE u_char *
+#else
+#define NFS_FH_TYPE nfsv2fh_t *
+#endif
+
+/*
+ * How to get a mount list
+ */
+#undef READ_MTAB_FROM_FILE
+#define READ_MTAB_BSD_STYLE
+
+/*
+ * The data for the mount syscall needs the path in addition to the
+ * host name since that is the only source of information about the
+ * mounted filesystem.
+ */
+#define NFS_ARGS_NEEDS_PATH
+
+/*
+ * 4.4 has RE support built in
+ */
+#undef RE_HDR
+#define RE_HDR <regexp.h>
+
+/*
+ * Need precise length links
+ */
+#define PRECISE_SYMLINKS
+
+#if BSD >= 199506
+#undef MTYPE_TYPE
+#define MTYPE_TYPE char *
+#endif
+
+struct mount;
+#define mount __kern_mount /* fsinfo also uses "struct mount" */
+#include <sys/mount.h>
+#undef mount
+
+/*
+ * Name of filesystem types
+ */
+#define MOUNT_TYPE_NFS "nfs"
+#define MOUNT_TYPE_UFS "ufs"
+#undef MTAB_TYPE_UFS
+#define MTAB_TYPE_UFS "ufs"
+#define MTAB_TYPE_MFS "mfs"
diff --git a/usr.sbin/amd/config/os-concentrix.h b/usr.sbin/amd/config/os-concentrix.h
new file mode 100644
index 0000000..865ea4e
--- /dev/null
+++ b/usr.sbin/amd/config/os-concentrix.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 1989 Jan-Simon Pendry
+ * Copyright (c) 1989 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)os-concentrix.h 8.1 (Berkeley) 6/6/93
+ *
+ * $Id$
+ *
+ * Alliant Concentrix 5.0.0 definitions for Amd (automounter)
+ */
+
+/*
+ * Does the compiler grok void *
+ */
+#undef VOIDP
+
+/*
+ * Which version of the Sun RPC library we are using
+ * This is the implementation release number, not
+ * the protocol revision number.
+ */
+#define RPC_3
+
+/*
+ * Which version of the NFS interface are we using.
+ * This is the implementation release number, not
+ * the protocol revision number.
+ */
+#define NFS_3
+
+/*
+ * Does this OS have NDBM support?
+ */
+#define OS_HAS_NDBM
+
+/*
+ * Byte ordering
+ */
+#undef ARCH_ENDIAN
+#define ARCH_ENDIAN "big"
+
+/*
+ * Name of filesystem types
+ */
+#define MOUNT_TYPE_NFS MOUNT_NFS
+#define MOUNT_TYPE_UFS MOUNT_UFS
diff --git a/usr.sbin/amd/config/os-convex.h b/usr.sbin/amd/config/os-convex.h
new file mode 100644
index 0000000..cab0be8
--- /dev/null
+++ b/usr.sbin/amd/config/os-convex.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)os-convex.h 8.1 (Berkeley) 6/6/93
+ *
+ * $Id$
+ *
+ * Convex C220, version 7.1 definitions for Amd (automounter)
+ * from Eitan Mizrotsky <eitan@shum.huji.ac.il>
+ */
+
+
+/*
+ * Does the compiler grok void *
+ */
+#undef VOIDP
+
+/*
+ * Which version of the Sun RPC library we are using
+ * This is the implementation release number, not
+ * the protocol revision number.
+ */
+#define RPC_3
+
+/*
+ * Which version of the NFS interface are we using.
+ * This is the implementation release number, not
+ * the protocol revision number.
+ */
+#define NFS_3
+
+/*
+ * Byte ordering
+ */
+#undef ARCH_ENDIAN
+#define ARCH_ENDIAN "big"
+
+/*
+ * Name of filesystem types
+ */
+#define MOUNT_TYPE_UFS MOUNT_UFS
+#define MOUNT_TYPE_NFS MOUNT_NFS
+
+
+#define strrchr rindex
+#define strchr index
diff --git a/usr.sbin/amd/config/os-defaults.h b/usr.sbin/amd/config/os-defaults.h
new file mode 100644
index 0000000..ebf396a
--- /dev/null
+++ b/usr.sbin/amd/config/os-defaults.h
@@ -0,0 +1,143 @@
+/*
+ * Copyright (c) 1989 Jan-Simon Pendry
+ * Copyright (c) 1989 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)os-defaults.h 8.1 (Berkeley) 6/6/93
+ *
+ * $Id$
+ *
+ * Common OS definitions. These may be overridden in
+ * the OS specific files ("os-foo.h").
+ */
+
+/*
+ * What level of AMD are we backward compatible with?
+ * This only applies to externally visible characteristics.
+ * Rev.Minor.Branch.Patch (2 digits each)
+ */
+#define AMD_COMPAT 5000000 /* 5.0 */
+
+/*
+ * What type is free(void*) returning?
+ */
+#define FREE_RETURN_TYPE void
+
+/*
+ * Is the mount table mirrored in software
+ */
+#define UPDATE_MTAB
+
+/*
+ * Where to get union wait
+ */
+#define WAIT <sys/wait.h>
+
+/*
+ * Where to get mount entry info
+ */
+#define MNTENT_HDR <mntent.h>
+
+/*
+ * Include support for syslog()
+ */
+#define HAS_SYSLOG
+
+/*
+ * Byte ordering
+ */
+#define ARCH_ENDIAN "unknown"
+
+/*
+ * Name of filesystem types
+ */
+#define MTAB_TYPE_NFS "nfs"
+#define MTAB_TYPE_UFS "4.2"
+
+/*
+ * Name of mount & unmount system calls
+ *
+ * NOTE:
+ * UNMOUNT_TRAP takes a struct mntent *
+ */
+#define MOUNT_TRAP(type, mnt, flags, mnt_data) \
+ mount(type, mnt->mnt_dir, flags, mnt_data)
+#define UNMOUNT_TRAP(mnt) unmount(mnt->mnt_dir)
+
+/*
+ * How to unmount filesystems.
+ * NEED_UMOUNT_FS includes code to scan the mount table
+ * to find the correct information for the unmount system
+ * call. Some systems, such as 4.4bsd, do not require
+ * this - they can just do an unmount system call directly.
+ */
+#define NEED_UMOUNT_FS
+#define UMOUNT_FS(dir) umount_fs(dir)
+
+/*
+ * Type of a file handle
+ */
+#define NFS_FH_TYPE fhandle_t *
+#define NFS_FH_DREF(dst, src) { (dst) = (src); }
+
+/*
+ * How to copy an address into an NFS filehandle
+ */
+#define NFS_SA_DREF(dst, src) { (dst).addr = (src); }
+
+/*
+ * Type of filesystem type
+ */
+#define MTYPE_TYPE int
+
+/*
+ * How to get a mount list
+ */
+#define READ_MTAB_FROM_FILE
+
+/*
+ * Make Amd automount points appear
+ * to be zero sized. undef this
+ * if the O/S has a divide by zero
+ * problem in df et al.
+ */
+#define HAS_EMPTY_AUTOMOUNTS
+
+/*
+ * For the RE matcher
+ */
+#define CHARBITS 0377
+#define STRCSPN
+#define RE_HDR "re.h"
diff --git a/usr.sbin/amd/config/os-dgux.h b/usr.sbin/amd/config/os-dgux.h
new file mode 100644
index 0000000..b96e001
--- /dev/null
+++ b/usr.sbin/amd/config/os-dgux.h
@@ -0,0 +1,114 @@
+/*
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)os-dgux.h 8.1 (Berkeley) 6/6/93
+ *
+ * $Id$
+ *
+ * dg/ux definitions for Amd (automounter)
+ */
+
+/*
+ * Does the compiler grok void *
+ */
+#define VOIDP
+
+/*
+ * Which version of the Sun RPC library we are using
+ * This is the implementation release number, not
+ * the protocol revision number.
+ */
+#define RPC_4
+
+/*
+ * Which version of the NFS interface are we using.
+ * This is the implementation release number, not
+ * the protocol revision number.
+ */
+#define NFS_4
+
+/*
+ * Name of filesystem types
+ */
+#define MOUNT_TYPE_NFS "nfs"
+#define MOUNT_TYPE_UFS "dg/ux"
+#undef MTAB_TYPE_UFS
+#define MTAB_TYPE_UFS "dg/ux"
+
+/*
+ * Need the following in more places than just NFS_HDR
+ */
+#include <sys/dg_mount.h>
+/*
+ * This is braindead
+ * dg/ux has nfs 4.0 but doesn't have the following options
+ */
+#define NFSMNT_HOSTNAME 0x0
+#define NFSMNT_INT 0x0
+#define M_NEWTYPE 0
+
+/*
+ * DG have their own filesystem.
+ */
+#define ufs_args dgux_args
+
+/*
+ * Byte ordering
+ */
+
+#undef ARCH_ENDIAN
+#define ARCH_ENDIAN "big"
+
+#define _BSD_WAIT_FLAVOR
+#define _BSD_TTY_FLAVOR
+#define _BSD_SIGNAL_FLAVOR
+#define _DGUX_SOURCE
+
+/*
+ * Use fcntl() rather than flock()
+ */
+#define LOCK_FCNTL
+
+#define bzero(ptr, len) memset(ptr, 0, len)
+#define bcopy(from, to, len) memcpy(to, from, len)
+#undef MOUNT_TRAP
+#define MOUNT_TRAP(type, mnt, flags, mnt_data) \
+ ((struct nfs_args *)mnt_data)->version = !strcmp(type, MOUNT_TYPE_UFS)?\
+ DG_MOUNT_DGUX_VERSION:DG_MOUNT_NFS_VERSION, \
+ dg_mount(type, mnt->mnt_dir, flags, mnt_data)
+#undef UNMOUNT_TRAP
+#define UNMOUNT_TRAP(mnt) umount(mnt->mnt_dir)
diff --git a/usr.sbin/amd/config/os-fpx4.h b/usr.sbin/amd/config/os-fpx4.h
new file mode 100644
index 0000000..a6daf22
--- /dev/null
+++ b/usr.sbin/amd/config/os-fpx4.h
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)os-fpx4.h 8.1 (Berkeley) 6/6/93
+ *
+ * $Id$
+ *
+ * Celerity FPX 4.1/2 definitions for Amd (automounter)
+ * from Stephen Pope <scp@grizzly.acl.lanl.gov>
+ */
+
+/*
+ * FPX wants to include sys headers multiple times
+ */
+#define INCLUDE_HEADERS
+
+/*
+ * FPX sys/mount.h includes sys/nfs.h; prevent this
+ */
+#define INCLUDED_nfs
+
+/*
+ * FPX doesn't define NMOUNT anywhere
+ */
+#define NMOUNT 40
+
+/*
+ * Does the compiler grok void *
+ */
+/* #define VOIDP */
+
+/*
+ * Which version of the Sun RPC library we are using
+ * This is the implementation release number, not
+ * the protocol revision number.
+ */
+#define RPC_4
+#define svc_fdset svc_fds
+#define svc_getreqset(p) svc_getreq((*p).fds_bits[0])
+
+/*
+ * Which version of the NFS interface are we using.
+ * This is the implementation release number, not
+ * the protocol revision number.
+ */
+#define NFS_3
+
+/*
+ * Byte ordering
+ */
+#undef ARCH_ENDIAN
+#define ARCH_ENDIAN "big"
+
+/*
+ * Name of filesystem types
+ */
+#define MOUNT_TYPE_NFS MOUNT_NFS
+#define MOUNT_TYPE_UFS MOUNT_UFS
diff --git a/usr.sbin/amd/config/os-hcx.h b/usr.sbin/amd/config/os-hcx.h
new file mode 100644
index 0000000..54408cd
--- /dev/null
+++ b/usr.sbin/amd/config/os-hcx.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)os-hcx.h 8.1 (Berkeley) 6/6/93
+ *
+ * $Id$
+ *
+ * Harris HCX/UX Release 3.0 definitions for Amd (automounter)
+ */
+
+/*
+ * Which version of the Sun RPC library we are using
+ * This is the implementation release number, not
+ * the protocol revision number.
+ */
+#define RPC_3
+
+/*
+ * Which version of the NFS interface are we using.
+ * This is the implementation release number, not
+ * the protocol revision number.
+ */
+#define NFS_3
+
+/*
+ * Deviant call necessary. The mount() routine in libc only works for UFS
+ * (it's a backward-compatible piece of C code which traps to mountsyscall).
+ */
+#undef MOUNT_TRAP
+#define MOUNT_TRAP(type, mnt, flags, mnt_data) \
+ mountsyscall(type, mnt->mnt_dir, flags, mnt_data)
+
+/*
+ * Name of filesystem types
+ */
+#define MOUNT_TYPE_NFS MOUNT_NFS
+#define MOUNT_TYPE_UFS MOUNT_UFS
+
+/*
+ * Byte ordering
+ */
+#undef ARCH_ENDIAN
+#ifdef _hcx
+#define ARCH_ENDIAN "big"
+#else
+XXX - bizarre!
+#endif
diff --git a/usr.sbin/amd/config/os-hlh42.h b/usr.sbin/amd/config/os-hlh42.h
new file mode 100644
index 0000000..df8aff5
--- /dev/null
+++ b/usr.sbin/amd/config/os-hlh42.h
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 1989 Jan-Simon Pendry
+ * Copyright (c) 1989 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)os-hlh42.h 8.1 (Berkeley) 6/6/93
+ *
+ * $Id$
+ *
+ * HLH OTS definitions for Amd (automounter)
+ */
+
+/*
+ * Does the compiler grok void *
+ */
+#undef VOIDP
+
+/*
+ * Which version of the Sun RPC library we are using
+ * This is the implementation release number, not
+ * the protocol revision number.
+ */
+#define RPC_3
+
+/*
+ * Which version of the NFS interface are we using.
+ * This is the implementation release number, not
+ * the protocol revision number.
+ */
+#define NFS_3
+
+/*
+ * Byte ordering
+ */
+#undef ARCH_ENDIAN
+#if defined(hlh)
+#define ARCH_ENDIAN "little"
+#endif
+
+/*
+ * Name of filesystem types
+ */
+#define MOUNT_TYPE_NFS MOUNT_NFS
+#define MOUNT_TYPE_UFS MOUNT_UFS
+
+/*
+ * Miscellaneous HLH 4.2 incantations
+ */
+#define strchr index
+#define strrchr rindex
+#define sigmask(x) (1 << ((x)-1))
+
+/*
+ * HLH's 4.2 needs the extra RPC definitions.
+ */
+#define NEED_XDR_POINTER
+#define NEED_CLNT_SPERRNO
diff --git a/usr.sbin/amd/config/os-hpux.h b/usr.sbin/amd/config/os-hpux.h
new file mode 100644
index 0000000..c21dab3
--- /dev/null
+++ b/usr.sbin/amd/config/os-hpux.h
@@ -0,0 +1,150 @@
+/*
+ * Copyright (c) 1989 Jan-Simon Pendry
+ * Copyright (c) 1989 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)os-hpux.h 8.1 (Berkeley) 6/6/93
+ *
+ * $Id$
+ *
+ * HP/9000 HP-UX definitions for Amd (automounter)
+ */
+
+/*
+ * Does the compiler grok void *
+ */
+#ifdef __GNUC__
+#define VOIDP
+#endif
+
+/*
+ * Which version of the Sun RPC library we are using
+ * This is the implementation release number, not
+ * the protocol revision number.
+ */
+#define RPC_3
+
+/*
+ * Which version of the NFS interface are we using.
+ * This is the implementation release number, not
+ * the protocol revision number.
+ */
+#define NFS_3
+
+/*
+ * Byte ordering
+ */
+#undef ARCH_ENDIAN
+#if defined(hp9000s200) || defined(hp9000s300) || defined(hp9000s800)
+#define ARCH_ENDIAN "big"
+#endif
+
+#ifndef __hpux
+#define HPUX_VERSION_6
+#endif
+
+/*
+ * No support for syslog() prior to 7.0
+ */
+#ifdef HPUX_VERSION_6
+#undef HAS_SYSLOG
+#endif
+
+/*
+ * No support for ndbm
+ */
+#undef OS_HAS_NDBM
+
+/*
+ * Name of filesystem types
+ */
+#define MOUNT_TYPE_UFS MOUNT_UFS
+#define MOUNT_TYPE_NFS MOUNT_NFS
+#undef MTAB_TYPE_UFS
+#define MTAB_TYPE_UFS "hfs"
+
+/*
+ * Where to get NFS definitions
+ */
+#define NFS_HDR "misc-hpux.h"
+
+/*
+ * Where to get union wait
+ */
+#undef WAIT
+#define WAIT "uwait.h"
+#ifdef HPUX_VERSION_6
+#define SIGCHLD SIGCLD
+#endif
+#define SYS5_SIGNALS
+
+/*
+ * Miscellaneous HP-UX definitions
+ */
+
+#define NEED_XDR_POINTER
+#define NEED_CLNT_SPERRNO
+
+/*
+ * Use <fcntl.h> rather than <sys/file.h>
+ */
+#define USE_FCNTL
+
+/*
+ * Use fcntl() rather than flock()
+ */
+#define LOCK_FCNTL
+
+/*
+ * Additional fields in struct mntent
+ * are fixed up here
+ */
+#define FIXUP_MNTENT(mntp) { \
+ (mntp)->mnt_time = clocktime(); \
+}
+#define FIXUP_MNTENT_DUP(mntp, mp) { \
+ (mntp)->mnt_time = (mp)->mnt_time; \
+}
+
+#define bzero(ptr, len) memset(ptr, 0, len)
+#define bcopy(from, to, len) memcpy(to, from, len)
+#define getpagesize() (2048)
+#undef MOUNT_TRAP
+#define MOUNT_TRAP(type, mnt, flags, mnt_data) \
+ vfsmount(type, mnt->mnt_dir, flags, mnt_data)
+#undef UNMOUNT_TRAP
+#define UNMOUNT_TRAP(mnt) umount(mnt->mnt_dir)
+#define NFDS 30 /* conservative */
+#define MOUNTED MNT_MNTTAB
diff --git a/usr.sbin/amd/config/os-irix.h b/usr.sbin/amd/config/os-irix.h
new file mode 100644
index 0000000..a60f486
--- /dev/null
+++ b/usr.sbin/amd/config/os-irix.h
@@ -0,0 +1,133 @@
+/*
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)os-irix.h 8.1 (Berkeley) 6/6/93
+ *
+ * $Id$
+ *
+ * IRIX 3.3 definitions for Amd (automounter)
+ * Contributed by Scott R. Presnell <srp@cgl.ucsf.edu>
+ */
+
+/*
+ * Does the compiler grok void *
+ */
+#define VOIDP
+
+/*
+ * Which version of the Sun RPC library we are using
+ * This is the implementation release number, not
+ * the protocol revision number.
+ */
+#define RPC_3
+
+/*
+ * Which version of the NFS interface are we using.
+ * This is the implementation release number, not
+ * the protocol revision number.
+ */
+#define NFS_3
+
+/*
+ * Byte ordering
+ */
+#undef ARCH_ENDIAN
+#define ARCH_ENDIAN "big"
+
+/*
+ * Has support for syslog()
+ */
+#define HAS_SYSLOG
+
+#define M_GRPID MS_GRPID
+#define M_RDONLY MS_RDONLY
+/*
+ * Support for ndbm
+ */
+#define OS_HAS_NDBM
+
+#define UPDATE_MTAB
+
+#undef MTAB_TYPE_NFS
+#define MTAB_TYPE_NFS "nfs"
+
+#undef MTAB_TYPE_UFS
+#define MTAB_TYPE_UFS "efs"
+
+#define NMOUNT 40 /* The std sun value */
+/*
+ * Name of filesystem types
+ */
+#define MOUNT_TYPE_UFS sysfs(GETFSIND, FSID_EFS)
+#define MOUNT_TYPE_NFS sysfs(GETFSIND, FSID_NFS)
+
+#define SYS5_SIGNALS
+
+/*
+ * Use <fcntl.h> rather than <sys/file.h>
+ */
+/*#define USE_FCNTL*/
+
+/*
+ * Use fcntl() rather than flock()
+ */
+/*#define LOCK_FCNTL*/
+
+#ifdef __GNUC__
+#define alloca(sz) __builtin_alloca(sz)
+#endif
+
+#define bzero(ptr, len) memset(ptr, 0, len)
+#define bcopy(from, to, len) memcpy(to, from, len)
+
+#undef MOUNT_TRAP
+#define MOUNT_TRAP(type, mnt, flags, mnt_data) \
+ irix_mount(mnt->mnt_fsname, mnt->mnt_dir,flags, type, mnt_data)
+#undef UNMOUNT_TRAP
+#define UNMOUNT_TRAP(mnt) umount(mnt->mnt_dir)
+#define NFDS 30 /* conservative */
+
+#define NFS_HDR "misc-irix.h"
+#define UFS_HDR "misc-irix.h"
+
+/* not included in sys/param.h */
+#include <sys/types.h>
+
+#define MOUNT_HELPER_SOURCE "mount_irix.c"
+
+#define MNTINFO_DEV "fsid"
+#define MNTINFO_PREF "0x"
diff --git a/usr.sbin/amd/config/os-irix3.h b/usr.sbin/amd/config/os-irix3.h
new file mode 100644
index 0000000..9e8161f
--- /dev/null
+++ b/usr.sbin/amd/config/os-irix3.h
@@ -0,0 +1,133 @@
+/*
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)os-irix3.h 8.1 (Berkeley) 6/6/93
+ *
+ * $Id$
+ *
+ * IRIX 3.3 definitions for Amd (automounter)
+ * Contributed by Scott R. Presnell <srp@cgl.ucsf.edu>
+ */
+
+/*
+ * Does the compiler grok void *
+ */
+#define VOIDP
+
+/*
+ * Which version of the Sun RPC library we are using
+ * This is the implementation release number, not
+ * the protocol revision number.
+ */
+#define RPC_3
+
+/*
+ * Which version of the NFS interface are we using.
+ * This is the implementation release number, not
+ * the protocol revision number.
+ */
+#define NFS_3
+
+/*
+ * Byte ordering
+ */
+#undef ARCH_ENDIAN
+#define ARCH_ENDIAN "big"
+
+/*
+ * Has support for syslog()
+ */
+#define HAS_SYSLOG
+
+#define M_GRPID MS_GRPID
+#define M_RDONLY MS_RDONLY
+/*
+ * Support for ndbm
+ */
+#define OS_HAS_NDBM
+
+#define UPDATE_MTAB
+
+#undef MTAB_TYPE_NFS
+#define MTAB_TYPE_NFS "nfs"
+
+#undef MTAB_TYPE_UFS
+#define MTAB_TYPE_UFS "efs"
+
+#define NMOUNT 40 /* The std sun value */
+/*
+ * Name of filesystem types
+ */
+#define MOUNT_TYPE_UFS sysfs(GETFSIND, FSID_EFS)
+#define MOUNT_TYPE_NFS sysfs(GETFSIND, FSID_NFS)
+
+#define SYS5_SIGNALS
+
+/*
+ * Use <fcntl.h> rather than <sys/file.h>
+ */
+/*#define USE_FCNTL*/
+
+/*
+ * Use fcntl() rather than flock()
+ */
+/*#define LOCK_FCNTL*/
+
+#ifdef __GNUC__
+#define alloca(sz) __builtin_alloca(sz)
+#endif
+
+#define bzero(ptr, len) memset(ptr, 0, len)
+#define bcopy(from, to, len) memcpy(to, from, len)
+
+#undef MOUNT_TRAP
+#define MOUNT_TRAP(type, mnt, flags, mnt_data) \
+ irix_mount(mnt->mnt_fsname, mnt->mnt_dir,flags, type, mnt_data)
+#undef UNMOUNT_TRAP
+#define UNMOUNT_TRAP(mnt) umount(mnt->mnt_dir)
+#define NFDS 30 /* conservative */
+
+#define NFS_HDR "misc-irix.h"
+#define UFS_HDR "misc-irix.h"
+
+/* not included in sys/param.h */
+#include <sys/types.h>
+
+#define MOUNT_HELPER_SOURCE "mount_irix.c"
+
+#define MNTINFO_DEV "fsid"
+#define MNTINFO_PREF "0x"
diff --git a/usr.sbin/amd/config/os-irix4.h b/usr.sbin/amd/config/os-irix4.h
new file mode 100644
index 0000000..6390db5
--- /dev/null
+++ b/usr.sbin/amd/config/os-irix4.h
@@ -0,0 +1,150 @@
+/*
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)os-irix4.h 8.1 (Berkeley) 6/6/93
+ *
+ * $Id$
+ *
+ * IRIX 4.0.X definitions for Amd (automounter)
+ * Contributed by Scott R. Presnell <srp@cgl.ucsf.edu>
+ */
+
+/*
+ * Does the compiler grok void *
+ */
+#define VOIDP
+
+/*
+ * Which version of the Sun RPC library we are using
+ * This is the implementation release number, not
+ * the protocol revision number.
+ */
+#define RPC_3
+
+/*
+ * Which version of the NFS interface are we using.
+ * This is the implementation release number, not
+ * the protocol revision number.
+ */
+#define NFS_3
+
+/*
+ * Byte ordering
+ */
+#undef ARCH_ENDIAN
+#define ARCH_ENDIAN "big"
+
+/*
+ * Has support for syslog()
+ */
+#define HAS_SYSLOG
+
+#define M_RDONLY MS_RDONLY
+#define M_GRPID MS_GRPID
+#define M_NOSUID MS_NOSUID
+#define M_NONDEV MS_NODEV
+
+/*
+ * Support for ndbm
+ */
+#define OS_HAS_NDBM
+
+#define UPDATE_MTAB
+
+#undef MTAB_TYPE_NFS
+#define MTAB_TYPE_NFS "nfs"
+
+#undef MTAB_TYPE_UFS
+#define MTAB_TYPE_UFS "efs"
+
+#define NMOUNT 40 /* The std sun value */
+/*
+ * Name of filesystem types
+ */
+#define MOUNT_TYPE_UFS sysfs(GETFSIND, FSID_EFS)
+#define MOUNT_TYPE_NFS sysfs(GETFSIND, FSID_NFS)
+
+#define SYS5_SIGNALS
+
+/*
+ * Use <fcntl.h> rather than <sys/file.h>
+ */
+/*#define USE_FCNTL*/
+
+/*
+ * Use fcntl() rather than flock()
+ */
+/*#define LOCK_FCNTL*/
+
+#ifdef __GNUC__
+#define alloca(sz) __builtin_alloca(sz)
+#endif
+
+#define bzero(ptr, len) memset(ptr, 0, len)
+#define bcopy(from, to, len) memcpy(to, from, len)
+
+#undef MOUNT_TRAP
+#define MOUNT_TRAP(type, mnt, flags, mnt_data) \
+ irix_mount(mnt->mnt_fsname, mnt->mnt_dir,flags, type, mnt_data)
+#undef UNMOUNT_TRAP
+#define UNMOUNT_TRAP(mnt) umount(mnt->mnt_dir)
+#define NFDS 30 /* conservative */
+
+#define NFS_HDR "misc-irix.h"
+#define UFS_HDR "misc-irix.h"
+
+/* not included in sys/param.h */
+#include <sys/types.h>
+
+#define MOUNT_HELPER_SOURCE "mount_irix.c"
+
+/*
+ * Under 4.0.X this information is in /usr/include/mntent.h
+ * Below is what is used to be for Irix 3.3.X.
+ */
+/*#define MNTINFO_DEV "fsid"*/
+/*#define MNTINFO_PREF "0x"*/
+
+#define MNTINFO_PREF ""
+
+/*
+ * Under Irix, mount type "auto" is probed by statfs() in df. A statfs() of
+ * a direct mount causes that mount to fire. So change the mount type in
+ * /etc/mtab to "ignore" to stop that (this is what SGI does for their
+ * automounter. Use the old FASCIST define for this.
+ */
+#define FASCIST_DF_COMMAND MNTTYPE_IGNORE
diff --git a/usr.sbin/amd/config/os-next.h b/usr.sbin/amd/config/os-next.h
new file mode 100644
index 0000000..0504c50
--- /dev/null
+++ b/usr.sbin/amd/config/os-next.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)os-next.h 8.1 (Berkeley) 6/6/93
+ *
+ * $Id$
+ *
+ * NeXT OS definitions for Amd (automounter)
+ * By Bill Trost, Reed College
+ * trost%reed@cse.ogi.edu,
+ *
+ * Derived from the Sun 3.2 definitions for Amd (os-sos3.h).
+ */
+
+/*
+ * Does the compiler grok void * (NeXT uses gcc)
+ */
+#define VOIDP
+
+/*
+ * Which version of the Sun RPC library we are using
+ * This is the implementation release number, not
+ * the protocol revision number.
+ */
+#define RPC_3
+
+/*
+ * Which version of the NFS interface are we using.
+ * This is the implementation release number, not
+ * the protocol revision number.
+ */
+#define NFS_3
+
+/*
+ * Name of filesystem types
+ */
+#define MOUNT_TYPE_UFS MOUNT_UFS
+#define MOUNT_TYPE_NFS MOUNT_NFS
+#undef MTAB_TYPE_UFS
+#define MTAB_TYPE_UFS "4.3"
+
+/*
+ * Where to get NFS definitions
+ */
+#define NFS_HDR "misc-next.h"
diff --git a/usr.sbin/amd/config/os-pyrOSx.h b/usr.sbin/amd/config/os-pyrOSx.h
new file mode 100644
index 0000000..8e2fc15
--- /dev/null
+++ b/usr.sbin/amd/config/os-pyrOSx.h
@@ -0,0 +1,80 @@
+/*
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)os-pyrOSx.h 8.1 (Berkeley) 6/6/93
+ *
+ * $Id$
+ *
+ * Pyramid OSx definitions for Amd (automounter)
+ * from Stefan Petri <petri@tubsibr.UUCP>
+ */
+
+/*
+ * Does the compiler grok void *
+ */
+#define VOIDP
+
+/*
+ * Which version of the Sun RPC library we are using
+ * This is the implementation release number, not
+ * the protocol revision number.
+ */
+#define RPC_3
+
+/*
+ * Which version of the NFS interface are we using.
+ * This is the implementation release number, not
+ * the protocol revision number.
+ */
+#define NFS_3
+
+/*
+ * Byte ordering
+ */
+#undef ARCH_ENDIAN
+#define ARCH_ENDIAN "big"
+
+/*
+ * Name of filesystem types
+ */
+#define MOUNT_TYPE_UFS MOUNT_UFS
+#define MOUNT_TYPE_NFS MOUNT_NFS
+
+#define strchr index
+#define strrchr rindex
+
+#define hostname mnthostname
diff --git a/usr.sbin/amd/config/os-riscix.h b/usr.sbin/amd/config/os-riscix.h
new file mode 100644
index 0000000..536c375
--- /dev/null
+++ b/usr.sbin/amd/config/os-riscix.h
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 1989 Jan-Simon Pendry
+ * Copyright (c) 1989 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)os-riscix.h 8.1 (Berkeley) 6/6/93
+ *
+ * $Id$
+ *
+ * Acorn Archimedes RISC iX definitions for Amd (automounter)
+ * Contributed by Piete Brooks.
+ */
+
+/*
+ * Does the compiler grok void *
+ */
+#define VOIDP
+
+/*
+ * Which version of the Sun RPC library we are using
+ * This is the implementation release number, not
+ * the protocol revision number.
+ */
+#define RPC_3
+
+/*
+ * Which version of the NFS interface are we using.
+ * This is the implementation release number, not
+ * the protocol revision number.
+ */
+#define NFS_3
+
+/*
+ * Does this OS have NDBM support?
+ */
+#define OS_HAS_NDBM
+
+/*
+ * Byte ordering
+ */
+#undef ARCH_ENDIAN
+#define ARCH_ENDIAN "little"
+
+/*
+ * Is the mount table mirrored in software
+ */
+#define UPDATE_MTAB
+
+/*
+ * Name of filesystem types
+ */
+#define MOUNT_TYPE_NFS MOUNT_NFS
+#define MOUNT_TYPE_UFS MOUNT_UFS
+
+#undef MTAB_TYPE_UFS
+#define MTAB_TYPE_UFS MNTTYPE_43
diff --git a/usr.sbin/amd/config/os-sos3.h b/usr.sbin/amd/config/os-sos3.h
new file mode 100644
index 0000000..e1ced9a
--- /dev/null
+++ b/usr.sbin/amd/config/os-sos3.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 1989 Jan-Simon Pendry
+ * Copyright (c) 1989 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)os-sos3.h 8.1 (Berkeley) 6/6/93
+ *
+ * $Id$
+ *
+ * SunOS 3.2 definitions for Amd (automounter)
+ */
+
+/*
+ * Does the compiler grok void *
+ */
+#define VOIDP
+
+/*
+ * Which version of the Sun RPC library we are using
+ * This is the implementation release number, not
+ * the protocol revision number.
+ */
+#define RPC_3
+
+/*
+ * Which version of the NFS interface are we using.
+ * This is the implementation release number, not
+ * the protocol revision number.
+ */
+#define NFS_3
+
+/*
+ * Byte ordering
+ */
+#undef ARCH_ENDIAN
+#if defined(mc68010) || defined(mc68020) || defined(sparc)
+#define ARCH_ENDIAN "big"
+#endif
+#if defined(i386)
+#define ARCH_ENDIAN "little"
+#endif
+
+/*
+ * Name of filesystem types
+ */
+#define MOUNT_TYPE_UFS MOUNT_UFS
+#define MOUNT_TYPE_NFS MOUNT_NFS
diff --git a/usr.sbin/amd/config/os-sos4.h b/usr.sbin/amd/config/os-sos4.h
new file mode 100644
index 0000000..e8d4af8
--- /dev/null
+++ b/usr.sbin/amd/config/os-sos4.h
@@ -0,0 +1,116 @@
+/*
+ * Copyright (c) 1989 Jan-Simon Pendry
+ * Copyright (c) 1989 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)os-sos4.h 8.1 (Berkeley) 6/6/93
+ *
+ * $Id$
+ *
+ * SunOS 4.0 definitions for Amd (automounter)
+ */
+
+/*
+ * Does the compiler grok void *
+ */
+#define VOIDP
+
+/*
+ * What type is free(void*) returning?
+ */
+#undef FREE_RETURN_TYPE
+#define FREE_RETURN_TYPE int
+
+/*
+ * Which version of the Sun RPC library we are using
+ * This is the implementation release number, not
+ * the protocol revision number.
+ */
+#define RPC_4
+
+/*
+ * Which version of the NFS interface are we using.
+ * This is the implementation release number, not
+ * the protocol revision number.
+ */
+#define NFS_4
+
+/*
+ * Does this OS have NDBM support?
+ */
+#define OS_HAS_NDBM
+
+/*
+ * Byte ordering
+ */
+#undef ARCH_ENDIAN
+#if defined(mc68010) || defined(mc68020) || defined(sparc)
+#define ARCH_ENDIAN "big"
+#endif
+#if defined(i386)
+#define ARCH_ENDIAN "little"
+#endif
+
+/*
+ * Name of filesystem types
+ */
+#define MOUNT_TYPE_NFS "nfs"
+#define MOUNT_TYPE_UFS "4.2"
+
+/*
+ * Type of a file handle
+ */
+#undef NFS_FH_TYPE
+#define NFS_FH_TYPE caddr_t
+
+/*
+ * Type of filesystem type
+ */
+#undef MTYPE_TYPE
+#define MTYPE_TYPE char *
+
+/*
+ * Add support for SunOS 4 automounter files
+ */
+#define SUNOS4_COMPAT
+
+/*
+ * System Vr4 / SunOS 4.1 compatibility
+ * - put dev= in the options list
+ *
+ * From: Brent Callaghan <brent@eng.sun.com>
+ */
+#define MNTINFO_DEV "dev"
+#define MNTINFO_PREF ""
diff --git a/usr.sbin/amd/config/os-stellix.h b/usr.sbin/amd/config/os-stellix.h
new file mode 100644
index 0000000..9c52569
--- /dev/null
+++ b/usr.sbin/amd/config/os-stellix.h
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 1989 Jan-Simon Pendry
+ * Copyright (c) 1989 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)os-stellix.h 8.1 (Berkeley) 6/6/93
+ *
+ * Amd (automounter) definitions for Stellix.
+ * From Stephen C. Pope <scp@acl.lanl.gov>
+ *
+ * $Id$
+ */
+
+#define RPC_3
+
+#define NFS_3
+
+/*
+ * Byte ordering
+ */
+#undef ARCH_ENDIAN
+#define ARCH_ENDIAN "big"
+
+#define HAS_SYSLOG
+
+#define OS_HAS_NDBM
+
+#define UPDATE_MTAB
+
+#define USE_FCNTL
+
+#define LOCK_FCNTL
+
+/*
+ * Name of filesystem types
+ */
+#undef MTAB_TYPE_UFS
+#define MTAB_TYPE_UFS "sfs"
+
+#define MOUNT_TYPE_UFS sysfs(GETFSIND, "SFS1")
+#define MOUNT_TYPE_NFS sysfs(GETFSIND, "NFS")
+
+#define SYS5_SIGNALS
+#define HAS_SVR3_SIGNALS
+
+#define MOUNT_HELPER_SOURCE "mount_stellix.c"
+
+/*
+ * Name of mount & unmount system calls
+ *
+ * NOTE:
+ * UNMOUNT_TRAP takes a struct mntent *
+ */
+#undef MOUNT_TRAP
+#define MOUNT_TRAP(type, mnt, flags, mnt_data) \
+ stellix_mount(mnt->mnt_fsname, mnt->mnt_dir, flags, type, mnt_data)
+#undef UNMOUNT_TRAP
+#define UNMOUNT_TRAP(mnt) umount(mnt->mnt_dir)
+
+/*
+ * How to unmount filesystems.
+ * NEED_UMOUNT_FS includes code to scan the mount table
+ * to find the correct information for the unmount system
+ * call. Some systems, such as 4.4bsd, do not require
+ * this - they can just do an unmount system call directly.
+ */
+/* #define NEED_UMOUNT_FS */
+/* #define UMOUNT_FS(dir) umount_fs(dir) */
+
+#define NFS_HDR "misc-stellix.h"
+#define UFS_HDR "misc-stellix.h"
+
+#define M_RDONLY 0x01 /* mount fs read only */
+
+#define bzero(ptr, len) memset(ptr, 0, len)
+#define bcopy(from, to, len) memcpy(to, from, len)
diff --git a/usr.sbin/amd/config/os-type b/usr.sbin/amd/config/os-type
new file mode 100644
index 0000000..9ac1f46
--- /dev/null
+++ b/usr.sbin/amd/config/os-type
@@ -0,0 +1,128 @@
+#!/bin/sh
+#
+# Copyright (c) 1989 Jan-Simon Pendry
+# Copyright (c) 1989 Imperial College of Science, Technology & Medicine
+# Copyright (c) 1989, 1993
+# The Regents of the University of California. All rights reserved.
+#
+# This code is derived from software contributed to Berkeley by
+# Jan-Simon Pendry at Imperial College, London.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. All advertising materials mentioning features or use of this software
+# must display the following acknowledgement:
+# This product includes software developed by the University of
+# California, Berkeley and its contributors.
+# 4. Neither the name of the University nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# @(#)os-type 8.1 (Berkeley) 6/6/93
+#
+# $Id$
+#
+
+#
+# Take a pot-shot at your os type
+#
+echo "# ... No OS= option specified; dynamically determining OS type" >&2
+
+#
+# First try poking around in /etc/motd
+#
+
+case "`exec 2>/dev/null; head -2 /etc/motd`" in
+*"Sun UNIX 4.2 Release 3."*) OS=sos3;;
+*"SunOS Release 4."*) OS=sos4;;
+*"HP-UX on the HP"*) OS=hpux;;
+*"Ultrix V2."*) OS=u2_2;;
+*"Ultrix V3."*) OS=u3_0;;
+*"Ultrix-32 V3."*) OS=u3_0;;
+*"Ultrix Worksystem V2."*) OS=u3_0;;
+*"ULTRIX V4.2"*) OS=u4_2;;
+*"ULTRIX V4."*) OS=u4_0;;
+*"HLH OTS Version 1."*) OS=hlh42;;
+*"RISC iX release 1."*) OS=riscix;;
+*"FPX 4."*) OS=fpx4;;
+*"HCX/UX"*) OS=hcx;;
+*"4.4 BSD UNIX"*) OS=bsd44;;
+*"4.3 BSD Reno UNIX"*) OS=bsd44;;
+*"4.3 BSD UNIX"*) if [ -f /etc/minidisk ]; then
+ OS=acis43
+ elif [ -f /sbin/nfsiod ]; then
+ OS=bsd44 # prototype
+ else
+ OS=xinu43
+ fi;;
+*"Alliant Concentrix"*) OS=concentrix;;
+*"Umax 4.3"*) OS=umax43;;
+*)
+#
+# Well, that didn't work so apply some heuristics
+# to the filesystem name space...
+#
+ echo "# ... inspecting File system ..." >&2
+ if [ -f /etc/comply ]; then
+ OS=utek
+ elif [ -d /usr/lib/methods -o -d /etc/methods ]; then
+ OS=aix3
+ elif [ -f /usr/bin/cat ]; then
+ OS=sos4
+ elif [ -f /etc/nd ]; then
+ OS=sos3
+ elif [ -f /etc/elcsd ]; then
+ echo "# ... Ultrix - assuming U4.0 ..." >&2
+ OS=u4_0
+ elif [ -f /hp-ux ]; then
+ OS=hpux
+ elif [ -f /etc/ttylocal ]; then
+ OS=xinu43
+ elif [ -f /etc/minidisk ]; then
+ OS=acis43
+ elif [ -f /etc/toolboxdaemon ]; then
+ OS=aux
+ elif [ -f /sbin/nfsiod ]; then
+ OS=bsd44
+ elif [ -d /vrm ]; then
+ OS=aix2
+ elif [ -f /bin/pyr ] && /bin/pyr; then
+ OS=pyrOSx
+ elif [ -d /NextApps ]; then
+ OS=next
+ elif [ -f /etc/gl/ucode ]; then
+ OS=irix3
+ elif [ -f /usr/gfx/ucode ]; then
+ OS=irix4
+ elif [ -f /stellix ]; then
+ OS=stellix
+ else
+ case "`(sh ../config/arch)2>/dev/null`" in
+ ibm032) OS=acis43;;
+ aviion) OS=dgux;;
+ *) OS=unknown;;
+ esac
+ fi;;
+esac
+
+echo "# ... OS appears to be \"${OS}\"" >&2
+echo "${OS}"
+exit 0
diff --git a/usr.sbin/amd/config/os-u2_2.h b/usr.sbin/amd/config/os-u2_2.h
new file mode 100644
index 0000000..ab18edb
--- /dev/null
+++ b/usr.sbin/amd/config/os-u2_2.h
@@ -0,0 +1,161 @@
+/*
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)os-u2_2.h 8.1 (Berkeley) 6/6/93
+ *
+ * $Id$
+ *
+ * Ultrix 2.2 definitions for Amd (automounter)
+ */
+
+/*
+ * Does the compiler grok void *
+ */
+#undef VOIDP
+
+/*
+ * Which version of the Sun RPC library we are using
+ * This is the implementation release number, not
+ * the protocol revision number.
+ */
+#define RPC_3
+
+/*
+ * Which version of the NFS interface are we using.
+ * This is the implementation release number, not
+ * the protocol revision number.
+ */
+#define NFS_3
+
+/*
+ * Byte ordering
+ */
+#undef ARCH_ENDIAN
+#if defined(vax)
+#define ARCH_ENDIAN "little"
+#endif
+
+/*
+ * The mount table is obtained from the kernel
+ */
+#undef UPDATE_MTAB
+
+/*
+ * No mntent info on Ultrix
+ */
+#undef MNTENT_HDR
+
+/*
+ * No support for syslog()
+ */
+#undef HAS_SYSLOG
+
+/*
+ * No support for ndbm
+ */
+#undef HAS_NDBM_MAPS
+
+/*
+ * Name of filesystem types
+ */
+#define MOUNT_TYPE_NFS GT_NFS
+#define MOUNT_TYPE_UFS GT_ULTRIX
+#undef MTAB_TYPE_UFS
+#define MTAB_TYPE_UFS "ufs"
+
+/*
+ * Name of mount & unmount system calls
+ */
+#undef MOUNT_TRAP
+#define MOUNT_TRAP(type, mnt, flag, mnt_data) \
+ mount(mnt->mnt_fsname, mnt->mnt_dir, flag, type, mnt_data)
+#undef UNMOUNT_TRAP
+#define UNMOUNT_TRAP(mnt) umount(mnt->mnt_passno)
+
+/*
+ * Miscellaneous Ultrix bits
+ */
+#define M_RDONLY M_RONLY
+
+#ifndef MNTMAXSTR
+#define MNTMAXSTR 128
+#endif
+
+#define MNTTYPE_UFS "ufs" /* Un*x file system */
+#define MNTTYPE_NFS "nfs" /* network file system */
+#define MNTTYPE_IGNORE "ignore" /* No type specified, ignore this entry */
+
+#define MNTOPT_RO "ro" /* read only */
+#define MNTOPT_RW "rw" /* read/write */
+#define MNTOPT_QUOTA "quota" /* quotas */
+#define MNTOPT_NOQUOTA "noquota" /* no quotas */
+#define MNTOPT_HARD "hard" /* hard mount */
+#define MNTOPT_SOFT "soft" /* soft mount */
+#define MNTOPT_INTR "intr" /* interrupts allowed */
+
+#define MNTOPT_NOSUID "nosuid" /* no set uid allowed */
+
+struct mntent {
+ char *mnt_fsname; /* name of mounted file system */
+ char *mnt_dir; /* file system path prefix */
+ char *mnt_type; /* MNTTYPE_* */
+ char *mnt_opts; /* MNTOPT* */
+ int mnt_freq; /* dump frequency, in days */
+ int mnt_passno; /* pass number on parallel fsck */
+};
+#define MOUNTED "/etc/mtab"
+
+#define NFS_HDR "misc-ultrix.h"
+#define UFS_HDR "misc-ultrix.h"
+
+#define NEED_XDR_POINTER
+#define NEED_CLNT_SPERRNO
+
+#define nfs_args nfs_gfs_mount
+#define ULTRIX_HACK /* Should be handled better than this !! */
+#define NEED_MNTOPT_PARSER
+
+/*
+ * How to get a mount list
+ */
+#undef READ_MTAB_FROM_FILE
+#define READ_MTAB_ULTRIX_STYLE
+
+/*
+ * Need precise length links
+ */
+#define PRECISE_SYMLINKS
diff --git a/usr.sbin/amd/config/os-u3_0.h b/usr.sbin/amd/config/os-u3_0.h
new file mode 100644
index 0000000..c17b437
--- /dev/null
+++ b/usr.sbin/amd/config/os-u3_0.h
@@ -0,0 +1,154 @@
+/*
+ * Copyright (c) 1989 Jan-Simon Pendry
+ * Copyright (c) 1989 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)os-u3_0.h 8.1 (Berkeley) 6/6/93
+ *
+ * $Id$
+ *
+ * Ultrix 3.0 definitions for Amd (automounter)
+ */
+
+/*
+ * Does the compiler grok void *
+ */
+#undef VOIDP
+
+/*
+ * Which version of the Sun RPC library we are using
+ * This is the implementation release number, not
+ * the protocol revision number.
+ */
+#define RPC_3
+
+/*
+ * Which version of the NFS interface are we using.
+ * This is the implementation release number, not
+ * the protocol revision number.
+ */
+#define NFS_3
+
+/*
+ * Byte ordering
+ */
+#undef ARCH_ENDIAN
+#if defined(vax) || defined(mips)
+#define ARCH_ENDIAN "little"
+#endif
+
+/*
+ * The mount table is obtained from the kernel
+ */
+#undef UPDATE_MTAB
+
+/*
+ * No mntent info on Ultrix
+ */
+#undef MNTENT_HDR
+
+/*
+ * No support for syslog()
+ */
+#undef HAS_SYSLOG
+
+/*
+ * Name of filesystem types
+ */
+#define MOUNT_TYPE_NFS GT_NFS
+#define MOUNT_TYPE_UFS GT_ULTRIX
+#undef MTAB_TYPE_UFS
+#define MTAB_TYPE_UFS "ufs"
+
+/*
+ * Name of mount & unmount system calls
+ */
+#undef MOUNT_TRAP
+#define MOUNT_TRAP(type, mnt, flag, mnt_data) \
+ mount(mnt->mnt_fsname, mnt->mnt_dir, flag, type, mnt_data)
+#undef UNMOUNT_TRAP
+#define UNMOUNT_TRAP(mnt) umount(mnt->mnt_passno)
+
+/*
+ * Miscellaneous Ultrix bits
+ */
+#define M_RDONLY M_RONLY
+
+#define MNTMAXSTR 128
+
+#define MNTTYPE_UFS "ufs" /* Un*x file system */
+#define MNTTYPE_NFS "nfs" /* network file system */
+#define MNTTYPE_IGNORE "ignore" /* No type specified, ignore this entry */
+
+#define MNTOPT_RO "ro" /* read only */
+#define MNTOPT_RW "rw" /* read/write */
+#define MNTOPT_QUOTA "quota" /* quotas */
+#define MNTOPT_NOQUOTA "noquota" /* no quotas */
+#define MNTOPT_HARD "hard" /* hard mount */
+#define MNTOPT_SOFT "soft" /* soft mount */
+#define MNTOPT_INTR "intr" /* interrupts allowed */
+
+#define MNTOPT_NOSUID "nosuid" /* no set uid allowed */
+
+struct mntent {
+ char *mnt_fsname; /* name of mounted file system */
+ char *mnt_dir; /* file system path prefix */
+ char *mnt_type; /* MNTTYPE_* */
+ char *mnt_opts; /* MNTOPT* */
+ int mnt_freq; /* dump frequency, in days */
+ int mnt_passno; /* pass number on parallel fsck */
+};
+#define MOUNTED "/etc/mtab"
+
+#define NFS_HDR "misc-ultrix.h"
+#define UFS_HDR "misc-ultrix.h"
+
+#define NEED_XDR_POINTER
+#define NEED_CLNT_SPERRNO
+
+#define nfs_args nfs_gfs_mount
+#define ULTRIX_HACK /* Should be handled better than this !! */
+#define NEED_MNTOPT_PARSER
+
+/*
+ * How to get a mount list
+ */
+#undef READ_MTAB_FROM_FILE
+#define READ_MTAB_ULTRIX_STYLE
+
+/*
+ * Need precise length links
+ */
+#define PRECISE_SYMLINKS
diff --git a/usr.sbin/amd/config/os-u4_0.h b/usr.sbin/amd/config/os-u4_0.h
new file mode 100644
index 0000000..96a7a7b
--- /dev/null
+++ b/usr.sbin/amd/config/os-u4_0.h
@@ -0,0 +1,158 @@
+/*
+ * Copyright (c) 1989 Jan-Simon Pendry
+ * Copyright (c) 1989 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)os-u4_0.h 8.1 (Berkeley) 6/6/93
+ *
+ * $Id$
+ *
+ * Ultrix 4.0 definitions for Amd (automounter)
+ * from Chris Lindblad <cjl@ai.mit.edu>
+ */
+
+/*
+ * Does the compiler grok void *
+ */
+#ifdef __STDC__
+#define VOIDP
+#else
+#undef VOIDP
+#endif
+
+/*
+ * Which version of the Sun RPC library we are using
+ * This is the implementation release number, not
+ * the protocol revision number.
+ */
+#define RPC_3
+
+/*
+ * Which version of the NFS interface are we using.
+ * This is the implementation release number, not
+ * the protocol revision number.
+ */
+#define NFS_3
+
+/*
+ * Byte ordering
+ */
+#undef ARCH_ENDIAN
+#if defined(vax) || defined(mips)
+#define ARCH_ENDIAN "little"
+#endif
+
+/*
+ * The mount table is obtained from the kernel
+ */
+#undef UPDATE_MTAB
+
+/*
+ * No mntent info on Ultrix
+ */
+#undef MNTENT_HDR
+
+/*
+ * No support for syslog()
+ */
+#define HAS_SYSLOG
+
+/*
+ * Name of filesystem types
+ */
+#define MOUNT_TYPE_NFS GT_NFS
+#define MOUNT_TYPE_UFS GT_ULTRIX
+#undef MTAB_TYPE_UFS
+#define MTAB_TYPE_UFS "ufs"
+
+/*
+ * Name of mount & unmount system calls
+ */
+#undef MOUNT_TRAP
+#define MOUNT_TRAP(type, mnt, flag, mnt_data) \
+ mount(mnt->mnt_fsname, mnt->mnt_dir, flag, type, mnt_data)
+#undef UNMOUNT_TRAP
+#define UNMOUNT_TRAP(mnt) umount(mnt->mnt_passno)
+
+/*
+ * Miscellaneous Ultrix bits
+ */
+#define M_RDONLY M_RONLY
+
+#define MNTMAXSTR 128
+
+#define MNTTYPE_UFS "ufs" /* Un*x file system */
+#define MNTTYPE_NFS "nfs" /* network file system */
+#define MNTTYPE_IGNORE "ignore" /* No type specified, ignore this entry */
+
+#define MNTOPT_RO "ro" /* read only */
+#define MNTOPT_RW "rw" /* read/write */
+#define MNTOPT_QUOTA "quota" /* quotas */
+#define MNTOPT_NOQUOTA "noquota" /* no quotas */
+#define MNTOPT_HARD "hard" /* hard mount */
+#define MNTOPT_SOFT "soft" /* soft mount */
+#define MNTOPT_INTR "intr" /* interrupts allowed */
+
+#define MNTOPT_NOSUID "nosuid" /* no set uid allowed */
+
+struct mntent {
+ char *mnt_fsname; /* name of mounted file system */
+ char *mnt_dir; /* file system path prefix */
+ char *mnt_type; /* MNTTYPE_* */
+ char *mnt_opts; /* MNTOPT* */
+ int mnt_freq; /* dump frequency, in days */
+ int mnt_passno; /* pass number on parallel fsck */
+};
+#define MOUNTED "/etc/mtab"
+
+#define NFS_HDR "misc-ultrix.h"
+#define UFS_HDR "misc-ultrix.h"
+
+#define NEED_CLNT_SPERRNO
+
+#define nfs_args nfs_gfs_mount
+#define ULTRIX_HACK /* Should be handled better than this !! */
+#define NEED_MNTOPT_PARSER
+
+/*
+ * How to get a mount list
+ */
+#undef READ_MTAB_FROM_FILE
+#define READ_MTAB_ULTRIX_STYLE
+
+/*
+ * Need precise length links
+ */
+#define PRECISE_SYMLINKS
diff --git a/usr.sbin/amd/config/os-u4_2.h b/usr.sbin/amd/config/os-u4_2.h
new file mode 100644
index 0000000..88b1e3a
--- /dev/null
+++ b/usr.sbin/amd/config/os-u4_2.h
@@ -0,0 +1,153 @@
+/*
+ * Copyright (c) 1989 Jan-Simon Pendry
+ * Copyright (c) 1989 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)os-u4_2.h 8.1 (Berkeley) 6/6/93
+ *
+ * $Id$
+ *
+ * Ultrix 4.2 definitions for Amd (automounter)
+ * from Chris Lindblad <cjl@ai.mit.edu>
+ * and Chris Metcalf <metcalf@lcs.mit.edu>
+ */
+
+/*
+ * Does the compiler grok void *
+ */
+#define VOIDP
+
+/*
+ * Which version of the Sun RPC library we are using
+ * This is the implementation release number, not
+ * the protocol revision number.
+ */
+#define RPC_4
+
+/*
+ * Which version of the NFS interface are we using.
+ * This is the implementation release number, not
+ * the protocol revision number.
+ */
+#define NFS_3
+
+/*
+ * Byte ordering
+ */
+#undef ARCH_ENDIAN
+#if defined(vax) || defined(mips)
+#define ARCH_ENDIAN "little"
+#endif
+
+/*
+ * The mount table is obtained from the kernel
+ */
+#undef UPDATE_MTAB
+
+/*
+ * No mntent info on Ultrix
+ */
+#undef MNTENT_HDR
+
+/*
+ * No support for syslog()
+ */
+#define HAS_SYSLOG
+
+/*
+ * Name of filesystem types
+ */
+#define MOUNT_TYPE_NFS GT_NFS
+#define MOUNT_TYPE_UFS GT_ULTRIX
+#undef MTAB_TYPE_UFS
+#define MTAB_TYPE_UFS "ufs"
+
+/*
+ * Name of mount & unmount system calls
+ */
+#undef MOUNT_TRAP
+#define MOUNT_TRAP(type, mnt, flag, mnt_data) \
+ mount(mnt->mnt_fsname, mnt->mnt_dir, flag, type, mnt_data)
+#undef UNMOUNT_TRAP
+#define UNMOUNT_TRAP(mnt) umount(mnt->mnt_passno)
+
+/*
+ * Miscellaneous Ultrix bits
+ */
+#define M_RDONLY M_RONLY
+
+#define MNTMAXSTR 128
+
+#define MNTTYPE_UFS "ufs" /* Un*x file system */
+#define MNTTYPE_NFS "nfs" /* network file system */
+#define MNTTYPE_IGNORE "ignore" /* No type specified, ignore this entry */
+
+#define MNTOPT_RO "ro" /* read only */
+#define MNTOPT_RW "rw" /* read/write */
+#define MNTOPT_QUOTA "quota" /* quotas */
+#define MNTOPT_NOQUOTA "noquota" /* no quotas */
+#define MNTOPT_HARD "hard" /* hard mount */
+#define MNTOPT_SOFT "soft" /* soft mount */
+#define MNTOPT_INTR "intr" /* interrupts allowed */
+
+#define MNTOPT_NOSUID "nosuid" /* no set uid allowed */
+
+struct mntent {
+ char *mnt_fsname; /* name of mounted file system */
+ char *mnt_dir; /* file system path prefix */
+ char *mnt_type; /* MNTTYPE_* */
+ char *mnt_opts; /* MNTOPT* */
+ int mnt_freq; /* dump frequency, in days */
+ int mnt_passno; /* pass number on parallel fsck */
+};
+#define MOUNTED "/etc/mtab"
+
+#define NFS_HDR "misc-ultrix.h"
+#define UFS_HDR "misc-ultrix.h"
+
+#define nfs_args nfs_gfs_mount
+#define ULTRIX_HACK /* Should be handled better than this !! */
+#define NEED_MNTOPT_PARSER
+
+/*
+ * How to get a mount list
+ */
+#undef READ_MTAB_FROM_FILE
+#define READ_MTAB_ULTRIX_STYLE
+
+/*
+ * Need precise length links
+ */
+#define PRECISE_SYMLINKS
diff --git a/usr.sbin/amd/config/os-umax43.h b/usr.sbin/amd/config/os-umax43.h
new file mode 100644
index 0000000..82efa72
--- /dev/null
+++ b/usr.sbin/amd/config/os-umax43.h
@@ -0,0 +1,79 @@
+/*
+ * Copyright (c) 1989 Jan-Simon Pendry
+ * Copyright (c) 1989 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)os-umax43.h 8.1 (Berkeley) 6/6/93
+ *
+ * $Id$
+ *
+ * UMAX 4.3 definitions for Amd (automounter)
+ */
+
+/*
+ * Does the compiler grok void *
+ */
+#define VOIDP
+
+/*
+ * Which version of the Sun RPC library we are using
+ * This is the implementation release number, not
+ * the protocol revision number.
+ */
+#define RPC_4
+
+/*
+ * Which version of the NFS interface are we using.
+ * This is the implementation release number, not
+ * the protocol revision number.
+ */
+#define NFS_3
+
+/*
+ * Does this OS have NDBM support?
+ */
+#define OS_HAS_NDBM
+
+/*
+ * Byte ordering
+ */
+#undef ARCH_ENDIAN
+#define ARCH_ENDIAN "little"
+
+/*
+ * Name of filesystem types
+ */
+#define MOUNT_TYPE_NFS MOUNT_NFS
+#define MOUNT_TYPE_UFS MOUNT_UFS
diff --git a/usr.sbin/amd/config/os-utek.h b/usr.sbin/amd/config/os-utek.h
new file mode 100644
index 0000000..226c3c0
--- /dev/null
+++ b/usr.sbin/amd/config/os-utek.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (c) 1989 Jan-Simon Pendry
+ * Copyright (c) 1989 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)os-utek.h 8.1 (Berkeley) 6/6/93
+ *
+ * $Id$
+ *
+ * Utek 4.0 definitions for Amd (automounter)
+ * from Bill Trost <trost%reed@cse.ogi.edu>
+ */
+
+#define UTEK
+#define __NFS_HEADER__ /* prevent re-inclusion of <sys/nfs.h> */
+/* ... and fake the rest */
+#include "os-sos3.h"
diff --git a/usr.sbin/amd/config/os-utx32.h b/usr.sbin/amd/config/os-utx32.h
new file mode 100644
index 0000000..f810f98
--- /dev/null
+++ b/usr.sbin/amd/config/os-utx32.h
@@ -0,0 +1,85 @@
+/* $Id$ */
+
+/*
+ * Gould UTX/32 definitions for Amd (automounter)
+ *
+ * Copyright (c) 1989 Jan-Simon Pendry
+ * Copyright (c) 1989 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)os-utx32.h 8.1 (Berkeley) 6/6/93
+ */
+
+/*
+ * Does the compiler grok void *
+ */
+#ifdef __GNUC__
+#define VOIDP
+#endif
+
+/*
+ * Which version of the Sun RPC library we are using
+ * This is the implementation release number, not
+ * the protocol revision number.
+ */
+#define RPC_3
+
+/*
+ * Which version of the NFS interface are we using.
+ * This is the implementation release number, not
+ * the protocol revision number.
+ */
+#define NFS_3
+
+/*
+ * Does this OS have NDBM support?
+ */
+#define OS_HAS_NDBM
+
+/*
+ * Byte ordering
+ */
+#undef ARCH_ENDIAN
+#if defined(gould) || defined(GOULD_PN)
+#define ARCH_ENDIAN "big"
+#endif
+
+/*
+ * Name of filesystem types
+ */
+#define MOUNT_TYPE_NFS MOUNT_NFS
+#define MOUNT_TYPE_UFS MOUNT_UFS
+#undef MTAB_TYPE_UFS
+#define MTAB_TYPE_UFS "4.3"
diff --git a/usr.sbin/amd/config/os-xinu43.h b/usr.sbin/amd/config/os-xinu43.h
new file mode 100644
index 0000000..c1e4222
--- /dev/null
+++ b/usr.sbin/amd/config/os-xinu43.h
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 1989 Jan-Simon Pendry
+ * Copyright (c) 1989 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)os-xinu43.h 8.1 (Berkeley) 6/6/93
+ *
+ * $Id$
+ *
+ * mt Xinu 4.3 (MORE/bsd) definitions for Amd (automounter)
+ * Should work on both Vax and HP ...
+ */
+
+/*
+ * Does the compiler grok void *
+ */
+#ifdef __GNUC__
+#define VOIDP
+#endif
+
+/*
+ * Which version of the Sun RPC library we are using
+ * This is the implementation release number, not
+ * the protocol revision number.
+ */
+#define RPC_4
+
+/*
+ * mt Xinu have a compatibility problem
+ * with getreq vs. getreqset. On SunOS
+ * getreqset takes a pointer to an fd_set,
+ * whereas on MORE/bsd, getreq takes a
+ * fd_set directly (cf. an integer on SunOS).
+ */
+#define svc_getreqset(p) svc_getreq(*p)
+
+/*
+ * Which version of the NFS interface are we using.
+ * This is the implementation release number, not
+ * the protocol revision number.
+ */
+#define NFS_4
+
+/*
+ * Name of filesystem types
+ */
+#define MOUNT_TYPE_NFS "nfs"
+#define MOUNT_TYPE_UFS "ufs"
+#undef MTAB_TYPE_UFS
+#define MTAB_TYPE_UFS "ufs"
+
+/*
+ * Byte ordering
+ */
+#ifndef BYTE_ORDER
+#include <machine/endian.h>
+#endif /* BYTE_ORDER */
+
+#undef ARCH_ENDIAN
+#if BYTE_ORDER == LITTLE_ENDIAN
+#define ARCH_ENDIAN "little"
+#else
+#if BYTE_ORDER == BIG_ENDIAN
+#define ARCH_ENDIAN "big"
+#else
+XXX - Probably no hope of running Amd on this machine!
+#endif /* BIG */
+#endif /* LITTLE */
+
+/*
+ * Type of a file handle
+ */
+#undef NFS_FH_TYPE
+#define NFS_FH_TYPE caddr_t
+
+/*
+ * Type of filesystem type
+ */
+#undef MTYPE_TYPE
+#define MTYPE_TYPE char *
diff --git a/usr.sbin/amd/doc/Makefile b/usr.sbin/amd/doc/Makefile
new file mode 100644
index 0000000..5a24cb0
--- /dev/null
+++ b/usr.sbin/amd/doc/Makefile
@@ -0,0 +1,46 @@
+# $Id: Makefile,v 1.5 1997/02/22 16:03:10 peter Exp $
+#
+# Copyright (c) 1990 Jan-Simon Pendry
+# Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+# Copyright (c) 1990 The Regents of the University of California.
+# All rights reserved.
+#
+# This code is derived from software contributed to Berkeley by
+# Jan-Simon Pendry at Imperial College, London.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. All advertising materials mentioning features or use of this software
+# must display the following acknowledgement:
+# This product includes software developed by the University of
+# California, Berkeley and its contributors.
+# 4. Neither the name of the University nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+#
+
+INFO = amdref
+INFOSECTION= "System Utilities"
+INFOENTRY_amdref= "* amdref: (amdref). AMD Reference Manual."
+
+.include <bsd.info.mk>
diff --git a/usr.sbin/amd/doc/amdref.cps b/usr.sbin/amd/doc/amdref.cps
new file mode 100644
index 0000000..a146372
--- /dev/null
+++ b/usr.sbin/amd/doc/amdref.cps
@@ -0,0 +1,381 @@
+\initial {/}
+\entry {/etc/amd.start}{40}
+\entry {/etc/passwd maps}{15}
+\entry {/etc/rc.local additions}{40}
+\entry {/vol}{70}
+\initial {A}
+\entry {Additions to /etc/rc.local}{40}
+\entry {Aliased hostnames}{27}
+\entry {Alternate locations}{7}
+\entry {Amd command line options}{25}
+\entry {Amq command}{40}
+\entry {arch, FSinfo host attribute}{50}
+\entry {arch, mount selector}{20}
+\entry {Architecture dependent volumes}{68}
+\entry {Architecture sharing}{68}
+\entry {Architecture specific mounts}{69}
+\entry {Atomic NFS mounts}{33}
+\entry {auto, filesystem type}{36}
+\entry {autodir, mount selector}{20}
+\entry {Automatic generation of user maps}{15}
+\entry {Automount directory}{25}
+\entry {Automount filesystem}{36}
+\entry {Automounter configuration maps}{12}
+\entry {Automounter fundamentals}{5}
+\initial {B}
+\entry {Background mounts}{7}
+\entry {Binding names to filesystems}{6}
+\entry {bootparams, FSinfo prefix}{59}
+\entry {Bug reports}{3}
+\entry {byte, mount selector}{20}
+\initial {C}
+\entry {Cache interval}{26}
+\entry {cache, mount option}{36}
+\entry {Catch-all mount point}{70}
+\entry {Changing the interval before a filesystem times out}{26}
+\entry {Cluster names}{30}
+\entry {cluster, FSinfo host attribute}{50}
+\entry {cluster, mount selector}{20}
+\entry {Command line options, Amd}{25}
+\entry {Command line options, FSinfo}{58}
+\entry {config, FSinfo host attribute}{49}
+\entry {Configuration map types}{12}
+\entry {Controlling Amd}{41}
+\entry {Creating a pid file}{27}
+\initial {D}
+\entry {Debug options}{30}
+\entry {Defining a host, FSinfo}{48}
+\entry {Defining an Amd mount map, FSinfo}{56}
+\entry {Defining host attributes, FSinfo}{48}
+\entry {delay, mount option}{21}
+\entry {Delaying mounts from specific locations}{21}
+\entry {Determining the map type}{12}
+\entry {dev, mount option}{34}
+\entry {Direct automount filesystem}{37}
+\entry {direct, filesystem type}{37}
+\entry {Discovering version information}{28}
+\entry {Discovering what is going on at run-time}{41}
+\entry {Disk filesystems}{34}
+\entry {Displaying the process id}{27}
+\entry {Domain name}{26}
+\entry {Domain stripping}{18}
+\entry {domain, mount selector}{20}
+\entry {Domainname operators}{18}
+\entry {dumpset, FSinfo filesystems option}{55}
+\entry {dumpset, FSinfo prefix}{59}
+\entry {Duplicated volumes}{6}
+\initial {E}
+\entry {Environment variables}{18}
+\entry {Error filesystem}{38}
+\entry {error, filesystem type}{38}
+\entry {Example of architecture specific mounts}{69}
+\entry {Example of mounting home directories}{66}
+\entry {export, FSinfo special fstype}{52}
+\entry {exportfs, FSinfo mount option}{54}
+\entry {exports, FSinfo prefix}{59}
+\initial {F}
+\entry {File map syntactic conventions}{12}
+\entry {File maps}{12}
+\entry {Fileserver}{5}
+\entry {Filesystem}{5}
+\entry {Filesystem info package}{46}
+\entry {Filesystem type; auto}{36}
+\entry {Filesystem type; direct}{37}
+\entry {Filesystem type; error}{38}
+\entry {Filesystem type; host}{32}
+\entry {Filesystem type; inherit}{39}
+\entry {Filesystem type; link}{35}
+\entry {Filesystem type; nfs}{31}
+\entry {Filesystem type; nfsx}{33}
+\entry {Filesystem type; program}{34}
+\entry {Filesystem type; root}{39}
+\entry {Filesystem type; toplvl}{38}
+\entry {Filesystem type; ufs}{34}
+\entry {Filesystem type; union}{38}
+\entry {Filesystem types}{31}
+\entry {Flat file maps}{12}
+\entry {Flushing the map cache}{43}
+\entry {Forcing filesystem to time out}{45}
+\entry {freq, FSinfo filesystems option}{53}
+\entry {fs, mount option}{21}
+\entry {FSinfo}{46}
+\entry {FSinfo arch host attribute}{50}
+\entry {FSinfo automount definitions}{56}
+\entry {FSinfo cluster host attribute}{50}
+\entry {FSinfo command line options}{58}
+\entry {FSinfo config host attribute}{49}
+\entry {FSinfo dumpset filesystems option}{55}
+\entry {FSinfo error messages}{61}
+\entry {FSinfo filesystems}{51}
+\entry {FSinfo freq filesystems option}{53}
+\entry {FSinfo fstype filesystems option}{52}
+\entry {FSinfo grammar}{47}
+\entry {FSinfo host attributes}{48}
+\entry {FSinfo host definitions}{48}
+\entry {FSinfo log filesystems option}{55}
+\entry {FSinfo mount filesystems option}{54}
+\entry {FSinfo opts filesystems option}{53}
+\entry {FSinfo os host attribute}{50}
+\entry {FSinfo overview}{46}
+\entry {FSinfo passno filesystems option}{53}
+\entry {FSinfo static mounts}{55}
+\entry {fstab, FSinfo prefix}{60}
+\entry {fstype, FSinfo filesystems option}{52}
+\initial {G}
+\entry {Generic volume name}{70}
+\entry {Global statistics}{44}
+\entry {Grammar, FSinfo}{47}
+\initial {H}
+\entry {Hesiod maps}{15}
+\entry {Home directories}{66}
+\entry {host, filesystem type}{32}
+\entry {host, mount selector}{20}
+\entry {hostd, mount selector}{20}
+\entry {Hostname normalisation}{27}
+\entry {hostname, FSinfo command line option}{60}
+\entry {How keys are looked up}{16}
+\entry {How locations are parsed}{17}
+\entry {How to access environment variables in maps}{18}
+\entry {How to discover your version of Amd}{28}
+\entry {How to mount a local disk}{34}
+\entry {How to mount a UFS filesystems}{34}
+\entry {How to mount all NFS exported filesystems}{32}
+\entry {How to mount an atomic group of NFS filesystems}{33}
+\entry {How to mount and NFS filesystem}{31}
+\entry {How to reference part of the local name space}{35}
+\entry {How to select log messages}{29}
+\entry {How to set default map parameters}{18}
+\entry {How to set map cache parameters}{36}
+\entry {How to start a direct automount point}{37}
+\entry {How to start an indirect automount point}{36}
+\entry {How variables are expanded}{18}
+\initial {I}
+\entry {inherit, filesystem type}{39}
+\entry {Inheritance filesystem}{39}
+\entry {Interval before a filesystem times out}{26}
+\entry {Introduction}{4}
+\initial {K}
+\entry {karch, mount selector}{20}
+\entry {Keep-alives}{8}
+\entry {Key lookup}{16}
+\entry {key, mount selector}{20}
+\initial {L}
+\entry {License Information}{2}
+\entry {link, filesystem type}{35}
+\entry {Listing currently mounted filesystems}{41}
+\entry {Location format}{17}
+\entry {Location lists}{7}
+\entry {Log filename}{27}
+\entry {Log message selection}{29}
+\entry {log, FSinfo filesystems option}{55}
+\entry {Looking up keys}{16}
+\initial {M}
+\entry {Machine architecture names}{11}
+\entry {Machine architectures supported by Amd}{11}
+\entry {Mailing list}{3}
+\entry {Map cache options}{36}
+\entry {Map cache synchronising}{36}
+\entry {Map cache types}{36}
+\entry {Map cache, flushing}{43}
+\entry {Map defaults}{18}
+\entry {Map entry format}{17}
+\entry {Map lookup}{16}
+\entry {Map options}{21}
+\entry {Map types}{12}
+\entry {map, mount selector}{20}
+\entry {maps, FSinfo command line option}{60}
+\entry {Mount a filesystem under program control}{34}
+\entry {Mount home directories}{66}
+\entry {Mount information}{12}
+\entry {Mount map types}{12}
+\entry {Mount maps}{12}
+\entry {Mount option; cache}{36}
+\entry {Mount option; delay}{21}
+\entry {Mount option; dev}{34}
+\entry {Mount option; fs}{21}
+\entry {Mount option; mount}{34}
+\entry {Mount option; opts}{22}
+\entry {Mount option; rfs}{31}
+\entry {Mount option; rhost}{31}
+\entry {Mount option; sublink}{23}
+\entry {Mount option; type}{24}
+\entry {Mount option; unmount}{34}
+\entry {Mount retries}{7}
+\entry {Mount selector; arch}{20}
+\entry {Mount selector; autodir}{20}
+\entry {Mount selector; byte}{20}
+\entry {Mount selector; cluster}{20}
+\entry {Mount selector; domain}{20}
+\entry {Mount selector; host}{20}
+\entry {Mount selector; hostd}{20}
+\entry {Mount selector; karch}{20}
+\entry {Mount selector; key}{20}
+\entry {Mount selector; map}{20}
+\entry {Mount selector; os}{20}
+\entry {Mount selector; path}{20}
+\entry {Mount selector; wire}{21}
+\entry {mount system call}{22}
+\entry {mount system call flags}{22}
+\entry {Mount types}{31}
+\entry {mount, FSinfo filesystems option}{54}
+\entry {mount, mount option}{34}
+\entry {Mounting a local disk}{34}
+\entry {Mounting a UFS filesystem}{34}
+\entry {Mounting a volume}{7}
+\entry {Mounting an atomic group of NFS filesystems}{33}
+\entry {Mounting an NFS filesystem}{31}
+\entry {Mounting entire export trees}{32}
+\entry {Mounting part of the local name space}{35}
+\entry {Mounting user filesystems}{65}
+\entry {Multiple-threaded server}{9}
+\initial {N}
+\entry {Namespace}{6}
+\entry {ndbm maps}{13}
+\entry {Network filesystem group}{33}
+\entry {Network host filesystem}{32}
+\entry {Network-wide naming}{6}
+\entry {NFS}{31}
+\entry {NFS ping}{8}
+\entry {nfs, filesystem type}{31}
+\entry {nfsx, filesystem type}{33}
+\entry {NIS (YP) domain name}{30}
+\entry {NIS (YP) maps}{14}
+\entry {Nodes generated on a restart}{39}
+\entry {Non-blocking operation}{9}
+\entry {Normalising hostnames}{27}
+\initial {O}
+\entry {Obtaining the source code}{3}
+\entry {Operating system names}{10}
+\entry {Operating systems supported by Amd}{10}
+\entry {Operational principles}{7}
+\entry {opts, FSinfo filesystems option}{53}
+\entry {opts, mount option}{22}
+\entry {os, FSinfo host attribute}{50}
+\entry {os, mount selector}{20}
+\entry {Overriding defaults on the command line}{25}
+\entry {Overriding the default mount point}{21}
+\entry {Overriding the local domain name}{26}
+\entry {Overriding the NIS (YP) domain name}{30}
+\initial {P}
+\entry {Passing parameters to the mount system call}{22}
+\entry {passno, FSinfo filesystems option}{53}
+\entry {Password file maps}{15}
+\entry {path, mount selector}{20}
+\entry {Pathname operators}{18}
+\entry {Picking up existing mounts}{28}
+\entry {pid file, creating with -p option}{27}
+\entry {Primary server}{21}
+\entry {Process id}{27}
+\entry {process id of Amd daemon}{27}
+\entry {Program filesystem}{34}
+\entry {program, filesystem type}{34}
+\initial {Q}
+\entry {Querying an alternate host}{43}
+\entry {quiet, FSinfo command line option}{61}
+\initial {R}
+\entry {Referencing part of the local name space}{35}
+\entry {Regular expressions in maps}{36}
+\entry {Replacement volumes}{6}
+\entry {Replicated volumes}{6}
+\entry {Resolving aliased hostnames}{27}
+\entry {Restarting existing mounts}{28}
+\entry {rfs, mount option}{31}
+\entry {rhost, mount option}{31}
+\entry {Root filesystem}{39}
+\entry {root, filesystem type}{39}
+\entry {RPC retries}{9}
+\entry {Run-time administration}{40}
+\entry {rwho servers}{69}
+\initial {S}
+\entry {Secondary server}{21}
+\entry {sel, FSinfo mount option}{54}
+\entry {Selecting specific log messages}{29}
+\entry {Selector; arch}{20}
+\entry {Selector; autodir}{20}
+\entry {Selector; byte}{20}
+\entry {Selector; cluster}{20}
+\entry {Selector; domain}{20}
+\entry {Selector; host}{20}
+\entry {Selector; hostd}{20}
+\entry {Selector; karch}{20}
+\entry {Selector; key}{20}
+\entry {Selector; map}{20}
+\entry {Selector; os}{20}
+\entry {Selector; path}{20}
+\entry {Selector; wire}{21}
+\entry {Selectors}{19}
+\entry {Server crashes}{8}
+\entry {Setting a delay on a mount location}{21}
+\entry {Setting Amd's RPC parameters}{28}
+\entry {Setting debug flags}{30}
+\entry {Setting default map parameters}{18}
+\entry {Setting map cache parameters}{36}
+\entry {Setting map options}{21}
+\entry {Setting system mount options}{22}
+\entry {Setting the cluster name}{30}
+\entry {Setting the default mount directory}{25}
+\entry {Setting the filesystem type option}{24}
+\entry {Setting the interval before a filesystem times out}{26}
+\entry {Setting the interval between unmount attempts}{28}
+\entry {Setting the Kernel architecture}{26}
+\entry {Setting the local domain name}{26}
+\entry {Setting the local mount point}{21}
+\entry {Setting the log file}{27}
+\entry {Setting the NIS (YP) domain name}{30}
+\entry {Setting the sublink option}{23}
+\entry {Sharing a fileserver between architectures}{68}
+\entry {SIGHUP signal}{36}
+\entry {SIGINT signal}{41}
+\entry {SIGTERM signal}{41}
+\entry {Source code distribution}{3}
+\entry {Starting Amd}{40}
+\entry {Statically mounts filesystems, FSinfo}{55}
+\entry {Statistics}{44}
+\entry {Stopping Amd}{41}
+\entry {Stripping the local domain name}{18}
+\entry {sublink}{5}
+\entry {sublink, mount option}{23}
+\entry {Supported machine architectures}{11}
+\entry {Supported operating systems}{10}
+\entry {Symbolic link filesystem}{35}
+\entry {symlink, link filesystem type}{35}
+\entry {Synchronising the map cache}{36}
+\entry {syslog}{27}
+\entry {syslog priorities}{29}
+\initial {T}
+\entry {The mount system call}{22}
+\entry {Top level filesystem}{38}
+\entry {toplvl, filesystem type}{38}
+\entry {type, mount option}{24}
+\entry {Types of configuration map}{12}
+\entry {Types of filesystem}{31}
+\entry {Types of mount map}{12}
+\initial {U}
+\entry {UFS}{34}
+\entry {ufs, filesystem type}{34}
+\entry {Union file maps}{16}
+\entry {Union filesystem}{38}
+\entry {union, filesystem type}{38}
+\entry {Unix filesystem}{34}
+\entry {Unix namespace}{6}
+\entry {unmount attempt backoff interval}{28}
+\entry {unmount, mount option}{34}
+\entry {Unmounting a filesystem}{45}
+\entry {User filesystems}{65}
+\entry {User maps, automatic generation}{15}
+\entry {Using FSinfo}{46}
+\entry {Using syslog to log errors}{27}
+\entry {Using the password file as a map}{15}
+\initial {V}
+\entry {Variable expansion}{18}
+\entry {verbose, FSinfo command line option}{61}
+\entry {Version information}{28}
+\entry {volname, FSinfo mount option}{54}
+\entry {Volume}{5}
+\entry {Volume binding}{6}
+\entry {Volume names}{6}
+\initial {W}
+\entry {Wildcards in maps}{16}
+\entry {wire, mount selector}{21}
+\initial {Y}
+\entry {YP domain name}{30}
diff --git a/usr.sbin/amd/doc/amdref.ps b/usr.sbin/amd/doc/amdref.ps
new file mode 100644
index 0000000..17e10f5
--- /dev/null
+++ b/usr.sbin/amd/doc/amdref.ps
@@ -0,0 +1,6429 @@
+%!PS-Adobe-2.0
+%%Creator: dvipsk 5.512a Copyright 1986, 1993 Radical Eye Software
+%%Title: amdref.dvi
+%%Pages: 62
+%%PageOrder: Ascend
+%%BoundingBox: 0 0 612 792
+%%EndComments
+%DVIPSCommandLine: dvips amdref.dvi -o
+%DVIPSSource: TeX output 1993.06.10:1525
+%%BeginProcSet: tex.pro
+/TeXDict 250 dict def TeXDict begin /N{def}def /B{bind def}N /S{exch}N /X{S N}
+B /TR{translate}N /isls false N /vsize 11 72 mul N /@rigin{isls{[0 -1 1 0 0 0]
+concat}if 72 Resolution div 72 VResolution div neg scale isls{Resolution hsize
+-72 div mul 0 TR}if Resolution VResolution vsize -72 div 1 add mul TR matrix
+currentmatrix dup dup 4 get round 4 exch put dup dup 5 get round 5 exch put
+setmatrix}N /@landscape{/isls true N}B /@manualfeed{statusdict /manualfeed
+true put}B /@copies{/#copies X}B /FMat[1 0 0 -1 0 0]N /FBB[0 0 0 0]N /nn 0 N
+/IE 0 N /ctr 0 N /df-tail{/nn 8 dict N nn begin /FontType 3 N /FontMatrix
+fntrx N /FontBBox FBB N string /base X array /BitMaps X /BuildChar{
+CharBuilder}N /Encoding IE N end dup{/foo setfont}2 array copy cvx N load 0 nn
+put /ctr 0 N[}B /df{/sf 1 N /fntrx FMat N df-tail}B /dfs{div /sf X /fntrx[sf 0
+0 sf neg 0 0]N df-tail}B /E{pop nn dup definefont setfont}B /ch-width{ch-data
+dup length 5 sub get}B /ch-height{ch-data dup length 4 sub get}B /ch-xoff{128
+ch-data dup length 3 sub get sub}B /ch-yoff{ch-data dup length 2 sub get 127
+sub}B /ch-dx{ch-data dup length 1 sub get}B /ch-image{ch-data dup type
+/stringtype ne{ctr get /ctr ctr 1 add N}if}B /id 0 N /rw 0 N /rc 0 N /gp 0 N
+/cp 0 N /G 0 N /sf 0 N /CharBuilder{save 3 1 roll S dup /base get 2 index get
+S /BitMaps get S get /ch-data X pop /ctr 0 N ch-dx 0 ch-xoff ch-yoff ch-height
+sub ch-xoff ch-width add ch-yoff setcachedevice ch-width ch-height true[1 0 0
+-1 -.1 ch-xoff sub ch-yoff .1 add]{ch-image}imagemask restore}B /D{/cc X dup
+type /stringtype ne{]}if nn /base get cc ctr put nn /BitMaps get S ctr S sf 1
+ne{dup dup length 1 sub dup 2 index S get sf div put}if put /ctr ctr 1 add N}
+B /I{cc 1 add D}B /bop{userdict /bop-hook known{bop-hook}if /SI save N @rigin
+0 0 moveto /V matrix currentmatrix dup 1 get dup mul exch 0 get dup mul add
+.99 lt{/QV}{/RV}ifelse load def pop pop}N /eop{SI restore showpage userdict
+/eop-hook known{eop-hook}if}N /@start{userdict /start-hook known{start-hook}
+if pop /VResolution X /Resolution X 1000 div /DVImag X /IE 256 array N 0 1 255
+{IE S 1 string dup 0 3 index put cvn put}for 65781.76 div /vsize X 65781.76
+div /hsize X}N /p{show}N /RMat[1 0 0 -1 0 0]N /BDot 260 string N /rulex 0 N
+/ruley 0 N /v{/ruley X /rulex X V}B /V{}B /RV statusdict begin /product where{
+pop product dup length 7 ge{0 7 getinterval dup(Display)eq exch 0 4
+getinterval(NeXT)eq or}{pop false}ifelse}{false}ifelse end{{gsave TR -.1 -.1
+TR 1 1 scale rulex ruley false RMat{BDot}imagemask grestore}}{{gsave TR -.1
+-.1 TR rulex ruley scale 1 1 false RMat{BDot}imagemask grestore}}ifelse B /QV{
+gsave transform round exch round exch itransform moveto rulex 0 rlineto 0
+ruley neg rlineto rulex neg 0 rlineto fill grestore}B /a{moveto}B /delta 0 N
+/tail{dup /delta X 0 rmoveto}B /M{S p delta add tail}B /b{S p tail}B /c{-4 M}
+B /d{-3 M}B /e{-2 M}B /f{-1 M}B /g{0 M}B /h{1 M}B /i{2 M}B /j{3 M}B /k{4 M}B
+/w{0 rmoveto}B /l{p -4 w}B /m{p -3 w}B /n{p -2 w}B /o{p -1 w}B /q{p 1 w}B /r{
+p 2 w}B /s{p 3 w}B /t{p 4 w}B /x{0 S rmoveto}B /y{3 2 roll p a}B /bos{/SS save
+N}B /eos{SS restore}B end
+%%EndProcSet
+TeXDict begin 40258431 52099146 1000 300 300 (amdref.dvi) @start
+/Fa 1 59 df<78FCFCFCFC7806067B8510>58 D E /Fb 1 59 df<60F0F06004047D830B>58
+D E /Fc 68 122 df<00FE7C0381C60603CE0E03841C03801C03801C03801C03801C03801C0380
+FFFFF01C03801C03801C03801C03801C03801C03801C03801C03801C03801C03801C03801C0380
+1C03801C0380FF8FF0171A809916>11 D<00FE000381000601800E03801C01001C00001C00001C
+00001C00001C0000FFFF801C03801C03801C03801C03801C03801C03801C03801C03801C03801C
+03801C03801C03801C03801C0380FF8FF0141A809915>I<00FF800383800603800E03801C0380
+1C03801C03801C03801C03801C0380FFFF801C03801C03801C03801C03801C03801C03801C0380
+1C03801C03801C03801C03801C03801C03801C0380FF9FF0141A809915>I<60F0F86808080810
+102040050B7D990B>39 D<00800100020004000C00080018003000300030006000600060006000
+E000E000E000E000E000E000E000E000E000E0006000600060006000300030003000180008000C
+00040002000100008009267D9B0F>I<8000400020001000180008000C00060006000600030003
+000300030003800380038003800380038003800380038003800300030003000300060006000600
+0C0008001800100020004000800009267E9B0F>I<60F0F07010101020204040040B7D830B>44
+D<FFC0FFC00A0280880D>I<60F0F06004047D830B>I<0004000C00180018001800300030003000
+600060006000C000C000C00180018001800300030003000600060006000C000C000C0018001800
+1800300030003000600060006000C000C0000E257E9B13>I<07E01C38381C300C700E60066006
+E007E007E007E007E007E007E007E007E007E00760066006700E300C381C1C3807E010187F9713
+>I<03000700FF0007000700070007000700070007000700070007000700070007000700070007
+0007000700070007007FF80D187D9713>I<0F80106020304038803CC01CE01C401C003C003800
+380070006000C001800100020004040804100430083FF87FF8FFF80E187E9713>I<07E0183820
+1C601E700E201E001E001C001C0038007007E00038001C000E000F000FE00FE00FC00F400E601C
+183807E010187F9713>I<001800180038007800F800B801380238023804380838183810382038
+4038C038FFFF00380038003800380038003803FF10187F9713>I<30183FF03FE03F8020002000
+2000200020002FC03060203000380018001C001C401CE01CE01C80184038403030E00F800E187E
+9713>I<01F807040C06180E300E300070006000E000E3E0E418E80CF00EE006E007E007E00760
+0760077006300E180C0C3807E010187F9713>I<40007FFF7FFE7FFE4004800880108010002000
+400040008001800100030003000700060006000E000E000E000E000E00040010197E9813>I<07
+E01818300C2006600660067006780C3E181F3007C003E00CF8307C601E600FC007C003C003C003
+60022004181807E010187F9713>I<07E01C303018700C600EE006E006E007E007E0076007700F
+3017182707C700070006000E000C700C7018603030600F8010187F9713>I<60F0F06000000000
+0000000060F0F0701010102020404004177D8F0B>59 D<000C0000000C0000000C0000001E0000
+001E0000002F000000270000002700000043800000438000004380000081C0000081C0000181E0
+000100E0000100E00003FFF000020070000200700004003800040038000400380008001C000800
+1C003C001E00FF00FFC01A1A7F991D>65 D<FFFF800E00E00E00700E00380E003C0E003C0E003C
+0E003C0E003C0E00780E00700E01E00FFFC00E00F00E00780E003C0E001C0E001E0E001E0E001E
+0E001E0E001C0E003C0E00780E00F0FFFFC0171A7F991B>I<003F0201C0C603002E0E001E1C00
+0E1C0006380006780002700002700002F00000F00000F00000F00000F00000F000007000027000
+027800023800041C00041C00080E000803003001C0C0003F00171A7E991C>I<FFFF80000E00E0
+000E0070000E0038000E001C000E000E000E000E000E0007000E0007000E0007800E0007800E00
+07800E0007800E0007800E0007800E0007800E0007800E0007000E0007000E000F000E000E000E
+001C000E001C000E0078000E00E000FFFF8000191A7F991D>I<FFFFF80E00380E00180E00080E
+000C0E00040E00040E00040E01000E01000E01000E03000FFF000E03000E01000E01000E01000E
+00020E00020E00020E00060E00040E00040E000C0E003CFFFFFC171A7F991A>I<FFFFF80E0038
+0E00180E00080E000C0E00040E00040E00040E01000E01000E01000E03000FFF000E03000E0100
+0E01000E01000E00000E00000E00000E00000E00000E00000E00000E0000FFE000161A7F9919>
+I<003F020001C0C60003002E000E001E001C000E001C0006003800060078000200700002007000
+0200F0000000F0000000F0000000F0000000F0000000F001FFC070000E0070000E0078000E0038
+000E001C000E001C000E000E000E000300160001C06600003F82001A1A7E991E>I<FFE1FFC00E
+001C000E001C000E001C000E001C000E001C000E001C000E001C000E001C000E001C000E001C00
+0E001C000FFFFC000E001C000E001C000E001C000E001C000E001C000E001C000E001C000E001C
+000E001C000E001C000E001C000E001C00FFE1FFC01A1A7F991D>I<FF801C001C001C001C001C
+001C001C001C001C001C001C001C001C001C001C001C001C001C001C001C001C001C001C001C00
+FF80091A7E990E>I<FFE01FC00E000F000E000C000E0008000E0010000E0020000E0040000E01
+80000E0200000E0400000E0C00000E1C00000E2E00000E4700000E8380000F0380000E01C0000E
+00E0000E00E0000E0070000E0038000E0038000E001C000E001E000E001F00FFE07FC01A1A7F99
+1E>75 D<FFE0000E00000E00000E00000E00000E00000E00000E00000E00000E00000E00000E00
+000E00000E00000E00000E00000E00000E00080E00080E00080E00180E00100E00300E00700E00
+F0FFFFF0151A7F9918>I<FF0000FF0F0000F00F0000F00B8001700B80017009C0027009C00270
+09C0027008E0047008E00470087008700870087008700870083810700838107008381070081C20
+70081C2070080E4070080E4070080E40700807807008078070080300701C030070FF8307FF201A
+7F9923>I<FE007FC00F000E000F0004000B80040009C0040009C0040008E00400087004000870
+040008380400081C0400081C0400080E04000807040008038400080384000801C4000800E40008
+00E4000800740008003C0008003C0008001C0008000C001C000C00FF8004001A1A7F991D>I<00
+7F000001C1C000070070000E0038001C001C003C001E0038000E0078000F0070000700F0000780
+F0000780F0000780F0000780F0000780F0000780F0000780F000078078000F0078000F0038000E
+003C001E001C001C000E0038000700700001C1C000007F0000191A7E991E>I<FFFF800E01E00E
+00700E00780E00380E003C0E003C0E003C0E003C0E00380E00780E00700E01E00FFF800E00000E
+00000E00000E00000E00000E00000E00000E00000E00000E00000E0000FFE000161A7F991A>I<
+007F000001C1C000070070000E0038001C001C003C001E0038000E0078000F0070000700F00007
+80F0000780F0000780F0000780F0000780F0000780F0000780F00007807000070078000F003800
+0E003C1C1E001C221C000E4138000741F00001E1C000007F80800000C0800000C0800000E18000
+007F0000007F0000003E0000001C0019217E991E>I<FFFF00000E01C0000E0070000E0078000E
+003C000E003C000E003C000E003C000E003C000E0078000E0070000E01C0000FFF00000E038000
+0E00C0000E00E0000E0070000E0070000E0070000E0078000E0078000E0078000E0078400E003C
+400E001C80FFE00F001A1A7F991C>I<0FC21836200E6006C006C002C002C002E00070007E003F
+E01FF803FC007E000E00070003800380038003C002C006E004D81887E0101A7E9915>I<7FFFFF
+00701C0700401C0100401C0100C01C0180801C0080801C0080801C0080001C0000001C0000001C
+0000001C0000001C0000001C0000001C0000001C0000001C0000001C0000001C0000001C000000
+1C0000001C0000001C0000001C0000001C000003FFE000191A7F991C>I<FFE07FC00E000E000E
+0004000E0004000E0004000E0004000E0004000E0004000E0004000E0004000E0004000E000400
+0E0004000E0004000E0004000E0004000E0004000E0004000E0004000E00040006000800070008
+00030010000180200000E0C000003F00001A1A7F991D>I<FF801FC01C0007001C0006000E0004
+000E0004000E000400070008000700080003801000038010000380100001C0200001C0200000E0
+400000E0400000E040000070800000708000007980000039000000390000001E0000001E000000
+1E0000000C0000000C00001A1A7F991D>I<FF81FF07F03C007801C01C007800801C007800801C
+007800800E009C01000E009C01000E009C010007010E020007010E020007010E020003830F0400
+038207040003820704000382070C0001C403880001C403880001C403880000E801D00000E801D0
+0000E801D000007000E000007000E000007000E000003000C0000020004000241A7F9927>I<FF
+801FE01E0007001E0006000F00040007000C00078008000380100001C0100001E0200000E06000
+007040000078800000388000001D0000001F0000000E0000000E0000000E0000000E0000000E00
+00000E0000000E0000000E0000000E0000000E000000FFC0001B1A7F991D>89
+D<1FC000387000383800101C00001C00001C0003FC001E1C00381C00701C00E01C00E01C80E01C
+80E03C80705F801F8F0011107F8F13>97 D<FC00001C00001C00001C00001C00001C00001C0000
+1C00001C00001C00001CFC001D07001E03801C01C01C00C01C00E01C00E01C00E01C00E01C00E0
+1C00E01C01C01C01801E030019060010F800131A809915>I<07F81C1C381C70087000E000E000
+E000E000E000E0007000700438081C1807E00E107F8F11>I<003F000007000007000007000007
+0000070000070000070000070000070003E7000C1700180F00300700700700E00700E00700E007
+00E00700E00700E00700600700700700380F001C370007C7E0131A7F9915>I<07C01C30301870
+18600CE00CFFFCE000E000E000E0006000700438081C1807E00E107F8F11>I<01F007180E381C
+101C001C001C001C001C001C00FFC01C001C001C001C001C001C001C001C001C001C001C001C00
+1C001C00FF800D1A80990C>I<0FCF001871803030007038007038007038007038003030001860
+002FC0006000006000007000003FF0003FFC001FFE00600F00C00300C00300C00300C003006006
+00381C0007E00011187F8F13>I<FC00001C00001C00001C00001C00001C00001C00001C00001C
+00001C00001C7C001D87001E03801E03801C03801C03801C03801C03801C03801C03801C03801C
+03801C03801C03801C0380FF9FF0141A809915>I<183C3C18000000000000FC1C1C1C1C1C1C1C
+1C1C1C1C1C1C1CFF081A80990A>I<FC00001C00001C00001C00001C00001C00001C00001C0000
+1C00001C00001C1FC01C0F001C0C001C18001C20001C40001CE0001DE0001E70001C78001C3800
+1C1C001C1E001C0F001C0F80FF9FE0131A809914>107 D<FC001C001C001C001C001C001C001C
+001C001C001C001C001C001C001C001C001C001C001C001C001C001C001C001C001C00FF80091A
+80990A>I<FC7C1F001D8E63801E0781C01E0781C01C0701C01C0701C01C0701C01C0701C01C07
+01C01C0701C01C0701C01C0701C01C0701C01C0701C01C0701C0FF9FE7F81D107F8F20>I<FC7C
+001D87001E03801E03801C03801C03801C03801C03801C03801C03801C03801C03801C03801C03
+801C0380FF9FF01410808F15>I<07E01C38300C700E6006E007E007E007E007E007E007600670
+0E381C1C3807E010107F8F13>I<FCFC001D07001E03801C01C01C01C01C00E01C00E01C00E01C
+00E01C00E01C00E01C01C01C01801E03001D06001CF8001C00001C00001C00001C00001C00001C
+0000FF80001317808F15>I<03E1000C1300180B00300F00700700E00700E00700E00700E00700
+E00700E00700700700700700380F001C370007C700000700000700000700000700000700000700
+003FE013177F8F14>I<FC781D9C1E1C1E081C001C001C001C001C001C001C001C001C001C001C
+00FF800E10808F0F>I<1F2060E04020C020C020F0007F003FC01FE000F080708030C030C020F0
+408F800C107F8F0F>I<0800080008000800180018003800FFC038003800380038003800380038
+003800382038203820382018201C4007800B177F960F>I<FC1F801C03801C03801C03801C0380
+1C03801C03801C03801C03801C03801C03801C03801C07800C07800E0B8003F3F01410808F15>
+I<FF0F803C07001C06001C04001C04000E08000E080007100007100007100003A00003A00001C0
+0001C00001C00000800011107F8F14>I<FE7F1F80381C07003C1C06001C0C04001C0E04000E16
+08000E1708000E170800072310000723900007A3900003C1A00003C1E0000180C0000180C00001
+80C00019107F8F1C>I<FE3F803C1E001C08000E10000F300007600003C00001C00001E00003E0
+00027000043800083800181C00381E00FC3FC012107F8F14>I<FF0F803C07001C06001C04001C
+04000E08000E080007100007100007100003A00003A00001C00001C00001C00000800000800001
+0000010000E10000E20000E4000078000011177F8F14>I E /Fd 13 119
+df<7FFFF0FFFFF8FFFFF87FFFF015047D921C>45 D<FFFF00FFFFC0FFFFE01E03F01E00F01E00
+781E007C1E003C1E003E1E001E1E001E1E001E1E000F1E000F1E000F1E000F1E000F1E000F1E00
+0F1E000F1E000F1E001F1E001E1E001E1E001E1E003C1E007C1E00781E00F81E03F0FFFFE0FFFF
+C0FFFF0018217FA01C>68 D<FFFFC0FFFFC0FFFFC001E00001E00001E00001E00001E00001E000
+01E00001E00001E00001E00001E00001E00001E00001E00001E00001E00001E00001E00001E000
+01E00001E00001E00001E00001E00001E00001E00001E000FFFFC0FFFFC0FFFFC012217BA01C>
+73 D<7FE0FFC0FFF1FFE07FE0FFC00F001E000F001E000F001E000F001E000F001E000F001E00
+0F001E000F001E000F001E000F001E000F001E000F001E000F001E000F001E000F001E000F001E
+000F001E000F001E000F001E000F001E000F001E000F001E0007001C0007803C0007803C0003C0
+780001E0F00000FFE000007FC000001F00001B2180A01C>85 D<0FF8001FFE003FFF803C0F8018
+03C00001E00001E00001E0003FE003FFE00FFFE03FC1E07E01E07801E0F001E0F001E0F001E0F0
+01E07803E07C0FE03FFFFF1FFEFF03F03F18177D961C>97 D<FF0000FF0000FF00000F00000F00
+000F00000F00000F00000F00000F00000F1F800F7FE00FFFF00FE0F80FC07C0F803C0F001E0F00
+1E0F000F0F000F0F000F0F000F0F000F0F000F0F000F0F001E0F801E0F803C0FC07C0FE0F80FFF
+F00F7FC0071F0018217FA01C>I<000FF0000FF0000FF00000F00000F00000F00000F00000F000
+00F00000F000F8F003FEF00FFFF01F07F03E03F03C01F07800F07800F0F000F0F000F0F000F0F0
+00F0F000F0F000F0F000F07800F07801F03C01F03E03F01F07F00FFFFF07FEFF01F8FF18217EA0
+1C>100 D<00FC0003FF000FFFC01F03E03E01E03C00F07800F0780070F00078F00078FFFFF8FF
+FFF8FFFFF8F00000F000007800007800783C00783E00F81F81F00FFFE003FFC000FE0015177D96
+1C>I<0003F8001FFC003FFE007C1E00780C00F00000F00000F00000F00000F0007FFFFCFFFFFC
+FFFFFC00F00000F00000F00000F00000F00000F00000F00000F00000F00000F00000F00000F000
+00F00000F00000F00000F00000F0007FFFE07FFFE07FFFE017217FA01C>I<FF000000FF000000
+FF0000000F0000000F0000000F0000000F0000000F0000000F0000000F0000000F0F80000F3FE0
+000FFFF0000FF0F0000FC078000F8078000F8078000F0078000F0078000F0078000F0078000F00
+78000F0078000F0078000F0078000F0078000F0078000F0078000F0078000F007800FFF1FF80FF
+F1FF80FFF1FF8019217FA01C>104 D<7E783C00FEFE7F007FFFFF001FCFE7801F0F87801F0F87
+801E0F07801E0F07801E0F07801E0F07801E0F07801E0F07801E0F07801E0F07801E0F07801E0F
+07801E0F07801E0F07801E0F07801E0F07807F8FC7E0FFCFE7F07F8FC7E01C1780961C>109
+D<00FC780003FF78000FFFF8001F03F8003E01F8003C00F8007800F80078007800F0007800F000
+7800F0007800F0007800F0007800F0007800F0007800780078007800F8003C01F8003E01F8001F
+07F8000FFFF80003FE780000F87800000078000000780000007800000078000000780000007800
+0000780000007800000078000007FF800007FF800007FF8019237E961C>113
+D<7FE3FF00FFE3FF807FE3FF000F0078000F0078000F80F8000780F0000780F00007C1F00003C1
+E00003C1E00003C1E00001E3C00001E3C00001E3C00000E3800000F7800000F7800000F7800000
+7F0000007F0000007F0000003E000019177F961C>118 D E /Fe 24 122
+df<FFF8FFF8FFF80D037D8C12>45 D<1FFFFFFFE07FFFFFFFF07FFFFFFFF00000000000000000
+00000000000000000000000000000000000000000000000000000000000000000000000000FFFF
+FFFFC0FFFFFFFFC07FFFFFFF80240F7B942A>61 D<000001000000000180000000038000000007
+8000000007800000000F800000000FC000000013C000000013C000000023C000000023C0000000
+43E000000041E000000081E000000081E000000101E000000101F000000200F000000200F00000
+0400F000000400F000000800F00000080078000010007800003FFFF800003FFFF8000040007800
+0040007C000080003C000080003C000100003C000100003C000200003E000200001E000600001E
+001F00003E00FFC001FFF0FFC003FFE024267EA528>65 D<007F80007F8000FF0000E00000E000
+00E00000E00000E00001C00001C00001C00001C00001C00001C000038000038000038000038000
+0380000380000700000700000700000700000700000700000E00000E00000E00000E00000E0000
+0E00001C00001C00001C00001C00001C00001C0000380000380000380000380000380000380000
+700000700000700000700000700000700000E00000E00000FF0000FF0000FF000011377DA80F>
+91 D<007F80007F8000FF00000700000700000700000700000700000E00000E00000E00000E00
+000E00000E00001C00001C00001C00001C00001C00001C00003800003800003800003800003800
+00380000700000700000700000700000700000700000E00000E00000E00000E00000E00000E000
+01C00001C00001C00001C00001C00001C000038000038000038000038000038000038000070000
+070000FF0000FF0000FF0000113781A80F>93 D<01FF000701C00780E00F80F00F807807007800
+00780000780000F0003FF001F0F00780F01E00F03C00F07801E07801E1F001E1F001E1F003E1F0
+05E2700DE23C11FC0FE07818177D961B>97 D<03C0003FC0003FC00007C00007C0000780000780
+000780000780000780000780000F00000F00000F00000F00000F0FC00F30701EC0181F001C1E00
+0E1E000E1E000F1E000F3C000F3C000F3C000F3C000F3C000F3C000E78001E78001C78003C7800
+387800707800E0E401C0C3070080FC0018267BA51E>I<007FC001C0700300F00E01F01E01F01C
+00E0380000780000780000F00000F00000F00000F00000F00000F00000F00000F0000070002070
+00403800C01801000E060003F80014177C9618>I<000003C000003FC000003FC0000007C00000
+07C000000780000007800000078000000780000007800000078000000F0000000F0000000F0000
+000F00003F0F0000E0CF0003803E0006003E000E001E001C001E0038001E0078001E0078003C00
+F0003C00F0003C00F0003C00F0003C00F0003C00F0007800F0007800F0007800700078007000F8
+003801F8001802F8000E0CFF0003F0FF001A267CA51E>I<007E0001C1800700E00E00601C0070
+3C0070380070780070780070FFFFF0F00000F00000F00000F00000F00000F00000F00000700020
+7000403800801801000E060003F80014177C9618>I<0000F800038C000E1E001E3E003C3E003C
+3C00780000780000780000780000780000F00000F00000F00000F0001FFF801FFF8001E00001E0
+0001E00001E00001E00001E00003C00003C00003C00003C00003C00003C0000780000780000780
+000780000780000780000F8000FFF800FFF80017267FA510>I<003C000003FC000003FC000000
+7C0000007C000000780000007800000078000000780000007800000078000000F0000000F00000
+00F0000000F0000000F0FE0000F3070001E4038001E803C001F003C001F003C001E003C001E003
+C003C0078003C0078003C0078003C0078003C0078003C0078007800F0007800F0007800F000780
+0F0007800F0007800F000F801F00FFF1FFE0FFF1FFE01B267FA51E>104
+D<0038007C007C0078003000000000000000000000000000000000000000F00FF00FE001E001E0
+01E001E001E003C003C003C003C003C003C00780078007800780078007800F80FFF0FFF00E257F
+A40F>I<00F0FE01FC001FF307060E001FE40388070001E803D0078001F003E0078001F003E007
+8001E003C0078001E003C0078003C007800F0003C007800F0003C007800F0003C007800F0003C0
+07800F0003C007800F0007800F001E0007800F001E0007800F001E0007800F001E0007800F001E
+0007800F001E000F801F003E00FFF1FFE3FFC0FFF1FFE3FFC02A177F962D>109
+D<00F0FE000FF307001FE4038001E803C001F003C001F003C001E003C001E003C003C0078003C0
+078003C0078003C0078003C0078003C0078007800F0007800F0007800F0007800F0007800F0007
+800F000F801F00FFF1FFE0FFF1FFE01B177F961E>I<003F0001C1C00300600E00701C00381C00
+3838003C78003C78003CF0003CF0003CF0003CF0003CF00038F00078F00078F00070F000E07001
+E03801C01807000E0E0003F00016177C961B>I<003C3F0003FCC1C007FB00E0007C0070007800
+78007800380078003C0078003C00F0003C00F0003C00F0003C00F0003C00F0003C00F0007801E0
+007801E0007001E000F001E000E001E001C001E0038003D0070003CC1C0003C3F00003C0000003
+C0000003C0000007800000078000000780000007800000078000000F800000FFF00000FFF00000
+1E2281961E>I<003E0601E18603808C07005C0E007C1C003C3C003C78003C780078F00078F000
+78F00078F00078F00078F000F0F000F0F000F07000F07001F03802F01805E00E19E003E1E00001
+E00001E00001E00003C00003C00003C00003C00003C00007C0007FF8007FF817227C961C>I<00
+F1F00FF2381FE47801E87801F03001F00001E00001E00003E00003C00003C00003C00003C00003
+C0000780000780000780000780000780000780000F8000FFF800FFF80015177F9615>I<00FE20
+0381E00600C00C00401C00401C00401C00401E00001FC0001FFC000FFE0007FF0000FF80000F80
+400780400380600380600380600300600700F00600C81C0087F00013177E9615>I<0040008000
+8000800080018001800300030007000F003FFEFFFE1E001E001E001E001E001E003C003C003C00
+3C003C003C0478087808780878087808781038201C600F800F227BA115>I<07800F7F80FFFF01
+FE0F001E0F001E0F001E0F001E0F001E1E003C1E003C1E003C1E003C1E003C1E003C3C00783C00
+783C00783C00F83C00F83C01781C02F80E0CFF03F0FF18177C961E>I<0FFE1FF01FFC1FF001F8
+0F8000F00C000078080000781000003C2000003E4000001E8000001F0000000F0000000F800000
+0F8000001BC0000013C0000021E0000041E0000080F0000100F800030078001F00FC00FF81FFC0
+FF81FFC01C177F961C>120 D<07FF03FC07FF03FC00F801E000F8008000780180007801000078
+020000780200003C0400003C0400003C0800003C0800003E1000001E2000001E2000001E400000
+1F4000000F8000000F8000000F0000000E00000006000000040000000400000008000000080000
+0010000030200000F8200000F8400000F8C00000F1800000C30000003C0000001E2280961C>I
+E /Ff 28 122 df<FFFEFFFEFFFE0F037D8E14>45 D<387C7EFC7C3807067B8510>I<00000020
+0000000060000000007000000000F000000000F000000001F000000001F000000003F000000003
+F800000004F80000000CF800000008F800000010F800000010FC000000207C000000207C000000
+407C000000407C000000807E000000803E000001003E000001003E000002003E000002003F0000
+04001F000004001F000008001F00000FFFFF00001FFFFF000010001F800020000F800060000F80
+0040000F800080000F800080000FC001000007C001000007C003000007C007000007C01F80000F
+E0FFE000FFFEFFE000FFFE272A7EA92C>65 D<03FFFFFFF803FFFFFFF8001F8001F8001F000078
+001F000038001F000018001F000018001F000018003E000018003E000018003E000008003E0000
+08003E002008003E002008007C004000007C004000007C004000007C00C000007C03C000007FFF
+C00000FFFF800000F803800000F801800000F801800000F800800000F800800001F001000001F0
+01000001F000000001F000000001F000000001F000000003E000000003E000000003E000000003
+E000000003E000000003E000000007E0000000FFFF800000FFFF80000025297DA826>70
+D<03FFFC03FFFC001F80001F00001F00001F00001F00001F00003E00003E00003E00003E00003E
+00003E00007C00007C00007C00007C00007C00007C0000F80000F80000F80000F80000F80000F8
+0001F00001F00001F00001F00001F00001F00003E00003E00003E00003E00003E00003E00007E0
+00FFFF00FFFF0016297DA815>73 D<03FF8000FFF003FFC000FFF0001FC0001F800017C0000600
+0013E00004000013E00004000011F00004000011F00004000020F80008000020F800080000207C
+00080000207C00080000203E00080000203E00080000401F00100000401F00100000401F801000
+00400F80100000400FC01000004007C01000008007E02000008003E02000008003F02000008001
+F02000008001F82000008000F82000010000FC40000100007C40000100007E40000100003E4000
+0100003F40000100001F40000200001F80000200000F80000200000F8000020000078000020000
+0780000600000380001F8000030000FFF000010000FFF0000100002C297DA82C>78
+D<0001FC020007FF06001E038E003800DC0070007C00E0003C01E0001C03C0001C03C0001C0380
+000807800008078000080780000807C0000807C0000007E0000003F0000003FE000001FFE00001
+FFFC0000FFFF00003FFF800007FFC00000FFE000000FE0000003F0000001F0000001F0000001F0
+200000F0200000F0200000F0200000E0600001E0600001E0700001C0700003C0780007807C0007
+00E6001E00E3C07C00C1FFF000803FC0001F2B7DA921>83 D<003F800001C0E000020070000780
+380007C03C000F803C0007803C0002003C0000003C0000003C0000003C00003FF80001F0780007
+C078000F0078001E0078003C0078007C00F040F800F040F800F040F800F040F801F040F802F080
+7C04F0803E187F0007E03C001A1A7D991D>97 D<001FE000701801C00403803C07003E0F007C1E
+003C3E00103C00007C00007C0000F80000F80000F80000F80000F80000F80000F80000F8000078
+00107800103C00201C00400E008007070001F800171A7C991A>99 D<0000007800000FF800000F
+F8000000F0000000F0000000F0000000F0000000F0000000F0000001E0000001E0000001E00000
+01E0000001E0000001E0000003C0000FC3C0007833C001E00BC003800BC0070007C00F0007801E
+0007803E0007803C0007807C0007807C00078078000F00F8000F00F8000F00F8000F00F8000F00
+F8000F00F8001E00F8001E0078001E0078001E0038003E001C005E000E00BE0007073FE001F83F
+E01D2A7CA921>I<003F8000E0E001C0700780700F00780E00381E003C3C003C3C003C7C003C7C
+007CFFFFF8F80000F80000F80000F80000F80000F80000F800007800107800103800201C00400C
+018007060001F800161A7C991A>I<00007C0001C200070F000F1F001E1F001C0F003C04003C00
+003C0000780000780000780000780000780000780000F0001FFFC01FFFC000F00000F00000F000
+01E00001E00001E00001E00001E00001E00003C00003C00003C00003C00003C00003C000078000
+0780000780000780000780000780000F8000FFFC00FFFC00182A7EA912>I<000000780007E084
+003C3B1C00701C1C00E01E0801E01E0003E01F0003C01F0007C01F0007C01F0007C01F0007C01E
+0007C03E0007C03C0003C0780001C0700002E1E000063F000004000000040000000C0000000C00
+00000E00000007FFE00007FFF80003FFFC000E003E0018000F00380007007000070070000700E0
+000700E0000700E0000700E0000E0070001C0030003800180070000F03C00001FE00001E287F9A
+1D>I<001E000003FE000003FE0000003C0000003C0000003C0000003C0000003C0000003C0000
+00780000007800000078000000780000007800000078000000F0000000F07F0000F1838000F201
+C000F401E000F801E001F001E001F001E001E001E001E001E001E001E001E001E003C003C003C0
+03C003C003C003C003C003C003C003C003C0078007800780078007800780078007800780078007
+8007800F800F80FFF8FFF8FFF8FFF81D2A7EA921>I<0038007C00FC00FC007C00780000000000
+0000000000000000000000000000F00FF00FF001F001F001E001E001E001E001E001E003C003C0
+03C003C003C003C00780078007800780078007800F80FFF0FFF00E297EA811>I<001E000003FE
+000003FE0000003C0000003C0000003C0000003C0000003C0000003C0000007800000078000000
+78000000780000007800000078000000F0000000F00FFC00F00FFC00F007C000F0070000F00400
+01E0080001E0100001E0200001E0800001E1800001E3800003C7C00003D3C00003E3E00003C3E0
+0003C1E00003C1F0000780F0000780F8000780780007807C0007803C0007803E000F807F00FFF9
+FFE0FFF9FFE01E2A7EA91F>107 D<001E01FE03FE003C003C003C003C003C003C007800780078
+00780078007800F000F000F000F000F000F001E001E001E001E001E001E003C003C003C003C003
+C003C00780078007800780078007800F80FFF0FFF00F2A7EA911>I<00F07F007F000FF1838183
+801FF201C201C001F401E401E001F801E801E001F001F001E001F001F001E001E001E001E001E0
+01E001E001E001E001E001E001E001E003C003C003C003C003C003C003C003C003C003C003C003
+C003C003C003C003C003C003C00780078007800780078007800780078007800780078007800780
+078007800780078007800F800F800F80FFF8FFF8FFF8FFF8FFF8FFF82D1A7E9931>I<00F07F00
+0FF183801FF201C001F401E001F801E001F001E001F001E001E001E001E001E001E001E001E001
+E003C003C003C003C003C003C003C003C003C003C003C003C00780078007800780078007800780
+078007800780078007800F800F80FFF8FFF8FFF8FFF81D1A7E9921>I<001FC00070F001C03803
+801C07001E0E000E1E000F3C000F3C000F7C000F7C000F78001FF8001FF8001FF8001FF8001FF8
+003EF8003EF8003C78007C7800783800F03C01E01E03C007070001F800181A7C991D>I<003C1F
+8003FC60E007FD8078007E003C003C003C007C001E0078001F0078001F0078001F0078001F0078
+001F00F0001F00F0001F00F0001F00F0001F00F0001E00F0003E01E0003E01E0007C01E0007801
+E000F001E001E001F001C003C8078003C40E0003C3F80003C0000003C0000003C0000007800000
+07800000078000000780000007800000078000000F800000FFF80000FFF800002026809921>I<
+00F0F80FF11C0FF63E01F43E01F83C01F03C01F00001F00001E00001E00001E00003C00003C000
+03C00003C00003C00003C0000780000780000780000780000780000780000F8000FFFC00FFFC00
+171A7E9917>114 D<007F0801C0D80300380600180E00180C00101C00101E00101E00001F8000
+0FFC0007FF0003FF8000FFC0000FE00003E02001E06000E06000E06000E06000E06000C0700180
+E80300C40E0083F800151A7E9917>I<00200000200000200000600000400000C00000C00001C0
+0001C00003C0000780001FFF80FFFF800780000780000780000F00000F00000F00000F00000F00
+000F00001E00001E00001E00001E00001E01001E01003C02003C02003C02003C02003C04001C04
+001C08000E100003E00011257BA417>I<078007807F807F80FF80FF800F800F800F800F800F00
+0F000F000F000F000F000F000F000F000F000F000F001E001E001E001E001E001E001E001E001E
+001E001E001E003C003C003C003C003C003C003C007C003C007C003C00BC001C017C000E067FC0
+03F87FC01A1A7B9921>I<FFF01FF0FFF01FF00F8007800F0007000F8006000780040007800C00
+0780080007C0100003C0100003C0200003C0200003C0400001E0400001E0800001E1000001E100
+0001F2000000F2000000F4000000F4000000F80000007800000070000000600000006000001C1A
+7B991F>I<FFF3FFC3FEFFF3FF83FE1F803C00F80F003C00600F003C00400F003C00400F003C00
+8007805C008007805E010007809E010007809E020007811E060007C10E040003C20E0C0003C20F
+080003C40F100003C40F100003C807200001E807200001F007C00001F007C00001E007800001E0
+07800001C003000000C00300000080020000271A7B992A>I<07FF80FF8007FF80FF80007C003C
+000078003800007C003000003C002000003C006000003C004000003E008000001E008000001E01
+0000001E010000001E020000000F020000000F040000000F080000000F080000000F9000000007
+9000000007A000000007A000000007C000000003C0000000038000000003000000000300000000
+0200000000020000000004000000000C00000000080000007010000000F810000000F820000000
+F040000000F080000000C1000000003E00000000212680991F>121 D E
+/Fg 28 122 df<7FFFFEFFFFFFFFFFFF7FFFFE18047D931F>45 D<00000600000F00000F00001F
+00001E00003E00003C00007C0000780000F80000F00001F00001E00003E00003C00007C0000780
+000780000F80000F00001F00001E00003E00003C00007C0000780000F80000F00001F00001E000
+01E00003E00003C00007C0000780000F80000F00001F00001E00003E00003C00007C0000780000
+F80000F00000F00000600000182F7DA91F>47 D<387CFEFEFE7C38000000000000000000000000
+387CFEFEFE7C38071A74991F>58 D<7FFFFF80FFFFFFC0FFFFFFC07FFFFF800000000000000000
+000000000000000000000000000000007FFFFF80FFFFFFC0FFFFFFC07FFFFF801A0E7E981F>61
+D<001F81C0007FF1C001FFFBC003E07FC007C01FC00F800FC01F0007C01E0007C03C0003C03C00
+03C0780003C0780003C07800000070000000F0000000F0000000F0000000F0000000F0000000F0
+000000F0000000F0000000F0000000700000007800000078000000780003C03C0003C03C0003C0
+1E0003C01F0007800F800F8007C01F0003E07E0001FFFC00007FF000001FC0001A257EA41F>67
+D<7FFF8000FFFFE0007FFFF8000F00FC000F003E000F001E000F001F000F000F800F0007800F00
+07800F0003C00F0003C00F0003C00F0003E00F0001E00F0001E00F0001E00F0001E00F0001E00F
+0001E00F0001E00F0001E00F0001E00F0001E00F0003C00F0003C00F0003C00F0007C00F000780
+0F000F800F000F000F001E000F003E000F00FC007FFFF800FFFFE0007FFF80001B257FA41F>I<
+07FC00001FFF00003FFFC0003E03E0003E01F0001C00F000000078000000780000007800000078
+00003FF80001FFF80007FFF8001FE078003E0078007C00780078007800F0007800F0007800F000
+7800F00078007800F8007E03F8003FFFFFE00FFF3FE003FC0FE01B1A7D991F>97
+D<007FE001FFF807FFFC0FC07C1F007C3E00383C0000780000780000700000F00000F00000F000
+00F00000F00000F000007000007800007800003C003C3E003C1F007C0FC0F807FFF001FFE0007F
+80161A7C991F>99 D<0001FE000003FE000001FE0000001E0000001E0000001E0000001E000000
+1E0000001E0000001E0000001E00007E1E0001FF9E0007FFDE000F81FE001F00FE003E007E003C
+003E0078001E0078001E00F0001E00F0001E00F0001E00F0001E00F0001E00F0001E00F0001E00
+F0001E0078001E0078003E003C003E003C007E001E00FE000F83FE0007FFDFE003FF1FF0007C1F
+E01C257EA41F>I<007F0001FFC007FFE00F81F01F00783E00783C003C78003C78001E70001EF0
+001EFFFFFEFFFFFEFFFFFEF00000F000007800007800007800003C001E1E001E1F003E0FC0FC03
+FFF801FFF0003F80171A7D991F>I<0001FC000007FF00001FFF80003F0F80003C0F8000780700
+00780000007800000078000000780000007800007FFFFE00FFFFFE00FFFFFE0000780000007800
+000078000000780000007800000078000000780000007800000078000000780000007800000078
+000000780000007800000078000000780000007800000078000000780000007800003FFFF0007F
+FFF8003FFFF00019257FA41F>I<007C0F8001FF3FC007FFFFE00F83F1E01F01F1C01E00F0003C
+0078003C0078003C0078003C0078003C0078003C0078001E00F0001F01F0000F83E0001FFFC000
+1DFF00001C7C00003C0000003C0000003C0000001E0000001FFFE0000FFFF8000FFFFE001FFFFF
+003C003F8078000F80780007C0F00003C0F00003C0F00003C0F00003C078000780780007803E00
+1F001F807E000FFFFC0003FFF000007F80001B287E991F>I<7F800000FF8000007F8000000780
+0000078000000780000007800000078000000780000007800000078000000783F000078FFC0007
+BFFE0007FC1F0007F00F0007E0078007C0078007C0078007800780078007800780078007800780
+078007800780078007800780078007800780078007800780078007800780078007800780078007
+80078007807FF87FF8FFFCFFFC7FF87FF81E2580A41F>I<00700000F80000F80000F800007000
+0000000000000000000000000000000000000000007FF800FFF8007FF800007800007800007800
+007800007800007800007800007800007800007800007800007800007800007800007800007800
+007800007800007800007800FFFFF8FFFFF8FFFFF815267BA51F>I<7F800000FF8000007F8000
+0007800000078000000780000007800000078000000780000007800000078000000787FFC00787
+FFE00787FFC007807E000780FC000781F8000783F0000787E000078FC000079F800007BF000007
+FF000007FF800007FFC00007F3C00007E3E00007C1F0000780F0000780F80007807C0007803C00
+07801E0007801F007FF87FE0FFFCFFF07FF87FE01C257FA41F>107 D<FFF800FFF800FFF80000
+780000780000780000780000780000780000780000780000780000780000780000780000780000
+780000780000780000780000780000780000780000780000780000780000780000780000780000
+7800007800007800007800007800FFFFFCFFFFFCFFFFFC16257CA41F>I<FE3C0F00FEFE3F80FF
+FF7FC01FCFF3C01F87E1E01F07C1E01F07C1E01E0781E01E0781E01E0781E01E0781E01E0781E0
+1E0781E01E0781E01E0781E01E0781E01E0781E01E0781E01E0781E01E0781E01E0781E01E0781
+E01E0781E0FFC7F1FCFFCFF3FCFFC7F1FC1E1A80991F>I<7F83F000FF8FFC007FBFFE0007FC1F
+0007F00F0007E0078007C0078007C0078007800780078007800780078007800780078007800780
+07800780078007800780078007800780078007800780078007800780078007800780078007807F
+F87FF8FFFCFFFC7FF87FF81E1A80991F>I<00FC0003FF0007FF801F87E01E01E03C00F07C00F8
+780078780078F0003CF0003CF0003CF0003CF0003CF0003CF0003CF8007C7800787800787C00F8
+3C00F01E01E01F87E007FF8003FF0000FC00161A7C991F>I<7F83E000FF9FFC007FBFFE0007FC
+1F0007F0078007E003C007C003C0078001E0078001E0078000F0078000F0078000F0078000F007
+8000F0078000F0078000F0078000F0078001E007C001E007C003C007E007C007F00F8007F81F00
+07BFFE00079FF8000787E000078000000780000007800000078000000780000007800000078000
+000780000007800000078000007FF80000FFFC00007FF800001C2780991F>I<7FE07E00FFE1FF
+807FE3FFC001EF87C001FF07C001FC038001F8000001F8000001F0000001F0000001F0000001E0
+000001E0000001E0000001E0000001E0000001E0000001E0000001E0000001E0000001E0000001
+E0000001E000007FFFE000FFFFE0007FFFE0001A1A7E991F>114 D<03FC700FFFF03FFFF07C03
+F07001F0E000F0E000F0E000F0F000F07C00003FE0001FFF0007FFC000FFF00003F80000787000
+3CF0001CF0001CF8001CF8001CFC0038FF00F0FFFFF0E7FFC0E1FE00161A7C991F>I<00700000
+00F0000000F0000000F0000000F0000000F0000000F000007FFFFE00FFFFFE00FFFFFE0000F000
+0000F0000000F0000000F0000000F0000000F0000000F0000000F0000000F0000000F0000000F0
+000000F0000000F0000000F0078000F0078000F0078000F0078000F0078000F00F00007C1F0000
+3FFE00001FFC000007F00019217FA01F>I<7F807F80FF80FF807F807F80078007800780078007
+800780078007800780078007800780078007800780078007800780078007800780078007800780
+078007800780078007800780078007800780078007800F8007801F8003C07F8001FFFFF800FFE7
+FC003F87F81E1A80991F>I<7FF0FFE0FFF0FFF07FF0FFE00F000F0007801E0007801E0007801E
+0003C03C0003C03C0003C03C0003E07C0001E0780001E0780001E0780000F0F00000F0F00000F0
+F0000079E0000079E0000079E0000039C0000039C000003FC000003FC000001F8000001F80001C
+1A7F991F>I<FFE07FF0FFF0FFF0FFE07FF01E0007801E0007801E0007801E0007801E0007800F
+000F000F000F000F0F0F000F0F0F000F1F8F000F1F8F00071B8E00079B9E00079B9E0007BBDE00
+07BBDE0007B1DE0003B1DC0003B1DC0003F1FC0003E0FC0003E0FC0001E078001C1A7F991F>I<
+7FF1FFC07FF1FFE07FF1FFC007C07C0003E0780001E0F80000F1F00000F9E000007FE000003FC0
+00003F8000001F8000000F0000001F0000001F8000003FC0000079E00000F9E00000F0F00001E0
+F80003E07C0003C03C0007803E007FF0FFE0FFF9FFF07FF0FFE01C1A7F991F>I<7FF0FFE0FFF8
+FFF07FF0FFE00F800F0007801E0007801E0003C01E0003C03C0003C03C0001E03C0001E07C0001
+E0780000F0780000F0780000F0F0000078F0000078F0000038E0000039E000003DE000001DC000
+001DC000001FC000000FC000000F8000000F8000000F8000000F0000000F0000001F0000001E00
+00001E0000383E00007C3C00007C7C00007CF800007FF000003FE000000F8000001C277F991F>
+I E /Fh 3 110 df<00000001800000000003C00000000003C00000000007C00000000007C000
+0000000FC0000000000FE0000000001FE0000000001FE00000000037E00000000037E000000000
+67F00000000063F000000000C3F000000000C3F00000000183F00000000183F00000000303F800
+00000301F80000000601F80000000601F80000000C01F80000000C01FC0000001800FC00000018
+00FC0000003000FC0000003000FC0000006000FE000000E0007E000000C0007E000001C0007E00
+000180007E000003FFFFFE000003FFFFFF00000600003F00000600003F00000C00003F00000C00
+003F00001800003F80001800001F80003000001F80003000001F80006000001F80006000001F80
+00C000001FC001C000000FC003E000000FC00FF000003FE0FFFE0003FFFFFFFC0003FFFF30327E
+B135>65 D<00000007C0000000FFC0000000FFC00000000FC00000000FC00000000F800000000F
+800000000F800000000F800000000F800000000F800000001F000000001F000000001F00000000
+1F000000001F000000001F000000003E000000003E000003F83E00001E063E000038013E0000E0
+00BE0001C000FC000380007C000780007C000F00007C001F00007C001E00007C003E0000F8003C
+0000F8007C0000F8007C0000F8007C0000F8007C0000F800F80001F000F80001F000F80001F000
+F80001F000780001F000780001F000780003E000780003E0003C0003E0003C0007E0001C000FE0
+000E001BE000070037F00001C1C7FF00007F07FE0022327BB127>100 D<003E03F8003F800FFE
+0C1E00C1E00FFE300F0300F000FE4007840078007C8007C8007C007D0007D0007C007E0007E000
+7C007E0007E0007C007C0007C0007C007C0007C0007C00FC000FC000F800F8000F8000F800F800
+0F8000F800F8000F8000F800F8000F8000F800F8000F8000F801F0001F0001F001F0001F0001F0
+01F0001F0001F001F0001F0001F001F0001F0001F001F0001F0001F003E0003E0003E003E0003E
+0003E003E0003E0003E003E0003E0003E003E0003E0003E007E0007E0007E007E0007E0007E0FF
+FF0FFFF0FFFFFFFF0FFFF0FFFF381F7E9E3C>109 D E /Fi 49 122 df<0007FC00003FFF0000
+FE078003F007C007E00FC007E00FC00FC00FC00FC00FC00FC00FC00FC003000FC000000FC00000
+0FC000000FC07FE0FFFFFFE0FFFFFFE00FC007E00FC007E00FC007E00FC007E00FC007E00FC007
+E00FC007E00FC007E00FC007E00FC007E00FC007E00FC007E00FC007E00FC007E00FC007E00FC0
+07E00FC007E00FC007E00FC007E00FC007E0FFFC7FFEFFFC7FFE1F267FA522>12
+D<FFFEFFFEFFFEFFFEFFFE0F057F8E14>45 D<3C7EFFFFFFFF7E3C08087C8711>I<007F800003
+FFF00007E1F8000F807C001F003E003F003F003E001F007E001F807E001F807E001F807E001F80
+FE001FC0FE001FC0FE001FC0FE001FC0FE001FC0FE001FC0FE001FC0FE001FC0FE001FC0FE001F
+C0FE001FC0FE001FC0FE001FC07E001F807E001F807E001F807E001F803F003F003F003F001F00
+3E000F807C0007E1F80003FFF000007F80001A237EA21F>48 D<001C00003C0000FC00FFFC00FF
+FC0000FC0000FC0000FC0000FC0000FC0000FC0000FC0000FC0000FC0000FC0000FC0000FC0000
+FC0000FC0000FC0000FC0000FC0000FC0000FC0000FC0000FC0000FC0000FC0000FC0000FC0000
+FC0000FC0000FC007FFFFC7FFFFC16237CA21F>I<01FF0007FFC01E07F03803F86001FC7C00FE
+FE00FEFE00FFFE007FFE007F7C007F3800FF0000FF0000FE0000FE0001FC0001F80003F00007E0
+000780000F00001E00003C0000700000E00301C0030380070700060600060FFFFE1FFFFE3FFFFE
+7FFFFCFFFFFCFFFFFC18237DA21F>I<01FF0007FFE01E03F03801F83C01FC7E00FE7E00FE7E00
+FE3E00FE1C01FE0001FC0001FC0003F80007F0000FC001FF0001FF000007E00001F00001F80000
+FC0000FE0000FF0000FF1000FF7C00FFFE00FFFE00FFFE00FEFE00FE7C01FC7001F83E07F00FFF
+C001FF0018237DA21F>I<0000380000007800000078000000F8000001F8000003F8000007F800
+0006F800000CF800001CF8000038F8000030F8000060F80000E0F80001C0F8000180F8000300F8
+000700F8000E00F8001C00F8001800F8003000F8007000F800E000F800FFFFFFC0FFFFFFC00001
+F8000001F8000001F8000001F8000001F8000001F8000001F800007FFFC0007FFFC01A237EA21F
+>I<18000C1F007C1FFFF81FFFF01FFFE01FFFC01FFF801FFC0018000018000018000018000018
+000018FF001BFFE01F03F01C00F80800FC00007E00007E00007E00007F00007F78007FFC007FFC
+007FFC007FFC007EF8007E6000FC7000FC3801F81E07E007FFC001FE0018237DA21F>I<001FC0
+007FF001F03803E00C07803E0F807E1F007E3F007E3F007E7E003C7E00007E00007E0000FE3FC0
+FE7FF0FE80F8FF80FCFF007CFF007EFE007EFE007FFE007FFE007FFE007F7E007F7E007F7E007F
+7E007F3E007E3F007E1F007C0F80F807C1F003FFC0007F0018237DA21F>I<300000003C000000
+3FFFFFC03FFFFFC03FFFFF807FFFFF007FFFFE007FFFFC006000180060001800E0003000C00060
+00C000C00000018000000180000003000000060000000E0000000E0000001C0000001C0000003C
+0000003C0000007800000078000000F8000000F8000000F8000000F8000001F8000001F8000001
+F8000001F8000001F8000001F8000000F00000006000001A257DA41F>I<00FF8003FFE00F01F8
+1C007C38003C38001E78001E78001E7C001E7E001E7F803C7FE03C3FF8781FFDF01FFFC00FFFC0
+03FFE003FFF80FFFFC1E1FFC3C07FE7803FE7800FFF0003FF0001FF0000FF0000FF0000FF0000E
+78000E78001C3C00381F80F007FFE001FF0018237DA21F>I<00FF0003FFC00F83E01F00F03F00
+F87E007C7E007C7E007EFE007EFE007EFE007EFE007FFE007FFE007FFE007F7E007F7E00FF3E00
+FF3F01FF1F017F0FFE7F03FC7F00007F00007E00007E3C007E7E00FC7E00FC7E00F87E00F07C01
+F03003E01C0F800FFF0003F80018237DA21F>I<FFFFFFE00000FFFFFFFC000003F800FF000003
+F8001FC00003F80007E00003F80003F00003F80001F80003F80001FC0003F80000FC0003F80000
+FE0003F80000FE0003F800007F0003F800007F0003F800007F0003F800007F8003F800007F8003
+F800007F8003F800007F8003F800007F8003F800007F8003F800007F8003F800007F8003F80000
+7F8003F800007F8003F800007F0003F800007F0003F800007F0003F80000FE0003F80000FE0003
+F80001FC0003F80001F80003F80003F00003F80007E00003F8001FC00003F800FF8000FFFFFFFE
+0000FFFFFFE0000029257EA42F>68 D<FFFFFFFF00FFFFFFFF0003F8007F0003F8000F8003F800
+078003F800038003F800038003F800018003F800018003F800018003F80000C003F80600C003F8
+0600C003F806000003F806000003F80E000003F81E000003FFFE000003FFFE000003F81E000003
+F80E000003F806000003F806000003F806006003F806006003F800006003F80000C003F80000C0
+03F80000C003F80000C003F80001C003F80003C003F80003C003F8000F8003F8003F80FFFFFFFF
+80FFFFFFFF8023257EA428>I<FFFFFFFE00FFFFFFFE0003F800FE0003F8001F0003F8000F0003
+F800070003F800070003F800030003F800030003F800030003F800018003F803018003F8030180
+03F803000003F803000003F807000003F80F000003FFFF000003FFFF000003F80F000003F80700
+0003F803000003F803000003F803000003F803000003F800000003F800000003F800000003F800
+000003F800000003F800000003F800000003F800000003F800000003F8000000FFFFF00000FFFF
+F0000021257EA427>I<FFFFE0FFFFE0FFFFE0FFFFE003F80003F80003F80003F80003F80003F8
+0003F80003F80003F80003F80003F80003F80003F80003F80003F80003F80003F80003F80003F8
+0003F80003F80003F80003F80003F80003F80003F80003F80003F80003F80003F80003FFFFFFF8
+0003FFFFFFF80003F80003F80003F80003F80003F80003F80003F80003F80003F80003F80003F8
+0003F80003F80003F80003F80003F80003F80003F80003F80003F80003F80003F80003F80003F8
+0003F80003F80003F80003F80003F80003F80003F80003F800FFFFE0FFFFE0FFFFE0FFFFE02B25
+7EA430>72 D<FFFFE0FFFFE003F80003F80003F80003F80003F80003F80003F80003F80003F800
+03F80003F80003F80003F80003F80003F80003F80003F80003F80003F80003F80003F80003F800
+03F80003F80003F80003F80003F80003F80003F80003F80003F80003F80003F800FFFFE0FFFFE0
+13257EA417>I<FFF8000000FFF8FFFC000001FFF803FC000001FE00037E0000037E00037E0000
+037E00037E0000037E00033F0000067E00033F0000067E00031F80000C7E00031F80000C7E0003
+0FC000187E00030FC000187E000307E000307E000307E000307E000307E000307E000303F00060
+7E000303F000607E000301F800C07E000301F800C07E000300FC01807E000300FC01807E000300
+7E03007E0003007E03007E0003007E03007E0003003F06007E0003003F06007E0003001F8C007E
+0003001F8C007E0003000FD8007E0003000FD8007E00030007F0007E00030007F0007E00030007
+F0007E00030003E0007E00078003E0007E00FFFC01C01FFFF8FFFC01C01FFFF835257EA43A>77
+D<FFF80007FFE0FFFC0007FFE003FE00003C0003FF00001800037F00001800033F80001800031F
+C0001800031FE0001800030FF00018000307F80018000303F80018000301FC0018000300FE0018
+000300FF00180003007F80180003003FC0180003001FC0180003000FE0180003000FF018000300
+07F81800030003FC1800030001FC1800030000FE18000300007F18000300007F98000300003FD8
+000300001FF8000300000FF80003000007F80003000003F80003000003F80003000001F8000300
+0000F800030000007800078000003800FFFC00001800FFFC000018002B257EA430>I<0003FF80
+00001FFFF000007F01FC0001FC007F0003F0001F8007E0000FC00FE0000FE01FC00007F01F8000
+03F03F800003F83F800003F87F800003FC7F000001FC7F000001FCFF000001FEFF000001FEFF00
+0001FEFF000001FEFF000001FEFF000001FEFF000001FEFF000001FEFF000001FE7F000001FC7F
+000001FC7F800003FC3F800003F83F800003F81FC00007F01FC00007F00FE0000FE007F0001FC0
+03F8003F8001FC007F00007F01FC00001FFFF0000003FF800027257DA42E>I<FFFFFFE000FFFF
+FFFC0003F800FF0003F8003F8003F8001FC003F8001FE003F8000FE003F8000FF003F8000FF003
+F8000FF003F8000FF003F8000FF003F8000FF003F8000FE003F8001FE003F8001FC003F8003F80
+03F800FF0003FFFFFC0003FFFFE00003F800000003F800000003F800000003F800000003F80000
+0003F800000003F800000003F800000003F800000003F800000003F800000003F800000003F800
+000003F800000003F8000000FFFFE00000FFFFE0000024257EA42A>I<00FF008007FFE3800F80
+F7801E001F803C000F807800078078000380F8000380F8000180F8000180FC000180FC000000FF
+0000007FE000007FFE00003FFFE0003FFFF8001FFFFE0007FFFF0003FFFF80007FFF800003FFC0
+00003FC000000FE0000007E0000007E0C00003E0C00003E0C00003E0C00003C0E00003C0F00007
+C0F8000780FC000F00FFC03E00E3FFF800803FE0001B257DA422>83 D<FFFFE00FFFC0FFFFE00F
+FFC003F80000780003F80000300003F80000300003F80000300003F80000300003F80000300003
+F80000300003F80000300003F80000300003F80000300003F80000300003F80000300003F80000
+300003F80000300003F80000300003F80000300003F80000300003F80000300003F80000300003
+F80000300003F80000300003F80000300003F80000300003F80000300003F80000300003F80000
+300001F80000600001FC0000600000FC0000C000007C0000C000003E00018000001F0007000000
+0FE03E00000003FFF8000000007FC000002A257EA42F>85 D<FFFF8001FFE0FFFF8001FFE007F8
+00001C0003F80000180003F80000180003FC0000380001FC0000300001FE0000700000FE000060
+0000FF00006000007F0000C000007F8000C000003F80018000003F80018000003FC0038000001F
+C0030000001FE0070000000FE0060000000FF00600000007F00C00000007F00C00000003F81800
+000003F81800000003FC3800000001FC3000000001FE7000000000FE6000000000FF6000000000
+7FC0000000007FC0000000003F80000000003F80000000003F80000000001F00000000001F0000
+0000000E00000000000E0000002B257FA42E>I<07FF00001FFFE0003E03F0003F00F8003F00FC
+003F007E001E007E0000007E0000007E0000007E00001FFE0003FE7E000FC07E001F007E003E00
+7E007E007E00FC007E00FC007E00FC007E00FC00BE007E01BE003F073E001FFE1FE007F00FE01B
+187E971E>97 D<FFC00000FFC000000FC000000FC000000FC000000FC000000FC000000FC00000
+0FC000000FC000000FC000000FC000000FC000000FC000000FC1FC000FCFFF000FFC0FC00FF007
+E00FC003F00FC003F00FC001F80FC001F80FC001FC0FC001FC0FC001FC0FC001FC0FC001FC0FC0
+01FC0FC001FC0FC001FC0FC001F80FC001F80FC003F00FE003F00FF007E00F1C1F800E0FFF000C
+03F8001E267FA522>I<007FE003FFF807C07C1F80FC1F00FC3F00FC7E00787E0000FE0000FE00
+00FE0000FE0000FE0000FE0000FE0000FE00007E00007F00003F000C1F800C1FC01807E07003FF
+E0007F0016187E971B>I<0000FFC00000FFC000000FC000000FC000000FC000000FC000000FC0
+00000FC000000FC000000FC000000FC000000FC000000FC000000FC0007F0FC003FFCFC00FE0FF
+C01F803FC03F000FC03F000FC07E000FC07E000FC0FE000FC0FE000FC0FE000FC0FE000FC0FE00
+0FC0FE000FC0FE000FC0FE000FC07E000FC07E000FC03F000FC03F001FC01F803FC00FC0EFC003
+FFCFFC00FE0FFC1E267EA522>I<007F0003FFC007C1F00F80F81F00F83F007C7E007C7E007EFE
+007EFE007EFFFFFEFFFFFEFE0000FE0000FE00007E00007E00007E00063F00061F000C0F801807
+E07003FFE0007F8017187E971C>I<001FC0007FF001F8F003E1F807E1F807C1F80FC0F00FC000
+0FC0000FC0000FC0000FC0000FC0000FC000FFFF00FFFF000FC0000FC0000FC0000FC0000FC000
+0FC0000FC0000FC0000FC0000FC0000FC0000FC0000FC0000FC0000FC0000FC0000FC0000FC000
+0FC0000FC000FFFE00FFFE0015267EA513>I<01FF07C007FFDFE00F83F1E01F01F1E03E00F800
+7E00FC007E00FC007E00FC007E00FC007E00FC007E00FC003E00F8001F01F0000F83E0000FFFC0
+0011FF00003000000030000000380000003C0000003FFFE0001FFFFC001FFFFE000FFFFF001FFF
+FF803C003F8078000FC0F80007C0F80007C0F80007C0F80007C07C000F803E001F001F807E0007
+FFF80000FFC0001B247E971F>I<FFC00000FFC000000FC000000FC000000FC000000FC000000F
+C000000FC000000FC000000FC000000FC000000FC000000FC000000FC000000FC0FE000FC3FF80
+0FCE0FC00FD80FC00FD007E00FE007E00FE007E00FC007E00FC007E00FC007E00FC007E00FC007
+E00FC007E00FC007E00FC007E00FC007E00FC007E00FC007E00FC007E00FC007E00FC007E00FC0
+07E0FFFC7FFEFFFC7FFE1F267EA522>I<0F001F803FC03FC03FC03FC01F800F00000000000000
+00000000000000007FC07FC00FC00FC00FC00FC00FC00FC00FC00FC00FC00FC00FC00FC00FC00F
+C00FC00FC00FC00FC00FC00FC0FFF8FFF80D277EA611>I<FFC00000FFC000000FC000000FC000
+000FC000000FC000000FC000000FC000000FC000000FC000000FC000000FC000000FC000000FC0
+00000FC01FF00FC01FF00FC007800FC00E000FC01C000FC030000FC060000FC1C0000FC380000F
+C780000FDF80000FFFC0000FE7E0000FC3F0000F81F0000F81F8000F80FC000F807E000F803F00
+0F803F000F801F800F800FC0FFF83FF8FFF83FF81D267FA520>107 D<FFC0FFC00FC00FC00FC0
+0FC00FC00FC00FC00FC00FC00FC00FC00FC00FC00FC00FC00FC00FC00FC00FC00FC00FC00FC00F
+C00FC00FC00FC00FC00FC00FC00FC00FC00FC00FC00FC0FFFCFFFC0E267EA511>I<FF80FE007F
+00FF83FF81FFC00F8E0FC707E00F980FCC07E00F9007E803F00FA007F003F00FA007F003F00FC0
+07E003F00FC007E003F00FC007E003F00FC007E003F00FC007E003F00FC007E003F00FC007E003
+F00FC007E003F00FC007E003F00FC007E003F00FC007E003F00FC007E003F00FC007E003F00FC0
+07E003F00FC007E003F0FFFC7FFE3FFFFFFC7FFE3FFF30187E9733>I<FF80FE00FF83FF800F8E
+0FC00F980FC00F9007E00FA007E00FA007E00FC007E00FC007E00FC007E00FC007E00FC007E00F
+C007E00FC007E00FC007E00FC007E00FC007E00FC007E00FC007E00FC007E00FC007E00FC007E0
+FFFC7FFEFFFC7FFE1F187E9722>I<007F800003FFF00007C0F8001F807E003F003F003F003F00
+7E001F807E001F80FE001FC0FE001FC0FE001FC0FE001FC0FE001FC0FE001FC0FE001FC0FE001F
+C07E001F807E001F803F003F003F003F001F807E000FC0FC0003FFF000007F80001A187E971F>
+I<FFC1FC00FFCFFF000FFC1FC00FF007E00FC007F00FC003F00FC003F80FC001F80FC001FC0FC0
+01FC0FC001FC0FC001FC0FC001FC0FC001FC0FC001FC0FC001FC0FC003F80FC003F80FC003F00F
+E007F00FF00FE00FDC1F800FCFFF000FC3F8000FC000000FC000000FC000000FC000000FC00000
+0FC000000FC000000FC000000FC00000FFFC0000FFFC00001E237F9722>I<007F00C003FFC1C0
+07E0E3C01FC033C01F801FC03F001FC07F000FC07F000FC0FE000FC0FE000FC0FE000FC0FE000F
+C0FE000FC0FE000FC0FE000FC0FE000FC07E000FC07F000FC03F000FC03F801FC01F803FC00FE0
+EFC003FF8FC000FE0FC000000FC000000FC000000FC000000FC000000FC000000FC000000FC000
+000FC000000FC00000FFFC0000FFFC1E237E9720>I<FF83E0FF8FF80F8C7C0F90FC0FB0FC0FA0
+FC0FA0780FE0000FC0000FC0000FC0000FC0000FC0000FC0000FC0000FC0000FC0000FC0000FC0
+000FC0000FC0000FC000FFFE00FFFE0016187F9719>I<07F8C01FFFC03C07C07001C0F000C0F0
+00C0F000C0FC0000FF80007FFC007FFE003FFF800FFFC003FFC0001FE00003E0C001E0C001E0E0
+01E0E001C0F003C0FC0780EFFF00C3FC0013187E9718>I<00C00000C00000C00000C00001C000
+01C00001C00003C00007C0000FC0001FC000FFFFC0FFFFC00FC0000FC0000FC0000FC0000FC000
+0FC0000FC0000FC0000FC0000FC0000FC0000FC0000FC0600FC0600FC0600FC0600FC0600FC060
+07E0C007E1C001FF80007E0013237FA218>I<FFC07FE0FFC07FE00FC007E00FC007E00FC007E0
+0FC007E00FC007E00FC007E00FC007E00FC007E00FC007E00FC007E00FC007E00FC007E00FC007
+E00FC007E00FC007E00FC007E00FC00FE00FC00FE007C017E007E067E003FFC7FE007F07FE1F18
+7E9722>I<FFF80FF8FFF80FF80FC001C00FC0018007E0030007E0030007F0070003F0060003F8
+0E0001F80C0001FC0C0000FC180000FE1800007E3000007E3000003F6000003F6000003FE00000
+1FC000001FC000000F8000000F800000070000000700001D187F9720>I<FFF9FFE0FF80FFF9FF
+E0FF801FC03F001C000FC01F0018000FC01F80180007E01F80300007E01F80300007F03FC07000
+03F037C0600003F037E0600001F863E0C00001F863E0C00001FCE3F1C00000FCC1F1800000FCC1
+F98000007F80FB0000007F80FB0000007F80FF0000003F007E0000003F007E0000001E003C0000
+001E003C0000001E003C0000000C0018000029187F972C>I<FFF83FF0FFF83FF00FC00F0007E0
+0E0003F01C0003F8380001FC700000FC6000007EC000003F8000003F8000001F8000000FC00000
+1FE000001FF0000033F8000061F80000E0FC0001C07E0003807F0007003F800F001F80FFC07FF8
+FFC07FF81D187F9720>I<FFF80FF8FFF80FF80FC001C00FC0018007E0030007E0030007F00700
+03F0060003F80E0001F80C0001FC0C0000FC180000FE1800007E3000007E3000003F6000003F60
+00003FE000001FC000001FC000000F8000000F800000070000000700000006000000060000000C
+0000300C0000781C0000FC180000FC300000FC70000068E000007FC000001F0000001D237F9720
+>I E /Fj 1 59 df<70F8F8F87005057C840D>58 D E /Fk 34 122 df<000700000007000000
+070000000F8000000F8000001FC000001FC000001FC000003FE0000037E0000037E0000063F000
+0063F0000063F00000C1F80000C1F80000C1F8000180FC000180FC000180FC0003007E0003FFFE
+0007FFFF0006003F0006003F000E001F800C001F800C001F801C000FC0FF80FFF8FF80FFF81D1F
+7E9E22>65 D<FFFFF000FFFFFC000F807E000F803F000F803F000F803F800F803F800F801F800F
+803F800F803F800F803F800F803F000F807E000F80FE000FFFF8000FFFFE000F803F000F801F80
+0F800FC00F800FC00F800FE00F800FE00F800FE00F800FE00F800FE00F800FC00F801FC00F801F
+800F803F80FFFFFE00FFFFF8001B1F7E9E20>I<000FF010007FFC7001FC0EF003E003F00FC001
+F01F8000F01F8000F03F0000703F0000707E0000307E000030FE000030FE000000FE000000FE00
+0000FE000000FE000000FE000000FE000000FE0000007E0000307E0000303F0000303F0000701F
+8000601F8000E00FC000C003E0038001FC0F00007FFC00000FF0001C1F7E9E21>I<FFFFF000FF
+FFFE000FC07F000FC01F800FC007C00FC007E00FC003F00FC003F00FC001F80FC001F80FC001F8
+0FC001FC0FC001FC0FC001FC0FC001FC0FC001FC0FC001FC0FC001FC0FC001FC0FC001FC0FC001
+F80FC001F80FC001F80FC003F00FC003F00FC003E00FC007E00FC00FC00FC03F00FFFFFE00FFFF
+F0001E1F7E9E23>I<FFFFFF00FFFFFF000FC01F000FC00F000FC007000FC003000FC003800FC0
+01800FC001800FC181800FC181800FC180000FC180000FC380000FFF80000FFF80000FC380000F
+C180000FC180000FC180C00FC180C00FC000C00FC001800FC001800FC001800FC003800FC00380
+0FC007800FC03F00FFFFFF00FFFFFF001A1F7E9E1E>I<FFFFFEFFFFFE0FC03E0FC00E0FC00E0F
+C0060FC0070FC0030FC0030FC1830FC1830FC1800FC1800FC3800FFF800FFF800FC3800FC1800F
+C1800FC1800FC1800FC0000FC0000FC0000FC0000FC0000FC0000FC0000FC000FFFE00FFFE0018
+1F7E9E1D>I<FFFCFFFC0FC00FC00FC00FC00FC00FC00FC00FC00FC00FC00FC00FC00FC00FC00F
+C00FC00FC00FC00FC00FC00FC00FC00FC00FC00FC00FC00FC0FFFCFFFC0E1F7E9E12>73
+D<FFC0000FFCFFE0001FFC0FE0001FC00FE0001FC00DF00037C00DF00037C00DF00037C00CF800
+67C00CF80067C00C7C00C7C00C7C00C7C00C7C00C7C00C3E0187C00C3E0187C00C3E0187C00C1F
+0307C00C1F0307C00C1F0307C00C0F8607C00C0F8607C00C07CC07C00C07CC07C00C07CC07C00C
+03F807C00C03F807C00C03F807C00C01F007C00C01F007C00C00E007C0FFC0E07FFCFFC0E07FFC
+261F7E9E2B>77 D<FFC007FEFFC007FE0FE000600FF000600DF800600DF800600CFC00600C7E00
+600C7E00600C3F00600C1F80600C0FC0600C0FC0600C07E0600C03F0600C03F8600C01F8600C00
+FC600C007E600C007E600C003F600C001FE00C000FE00C000FE00C0007E00C0003E00C0003E00C
+0001E00C0000E0FFC00060FFC000601F1F7E9E24>I<003FE00000FFF80003F07E0007C01F000F
+800F801F800FC03F0007E03F0007E07F0007F07E0003F07E0003F0FE0003F8FE0003F8FE0003F8
+FE0003F8FE0003F8FE0003F8FE0003F8FE0003F8FE0003F87E0003F07E0003F07F0007F03F0007
+E03F0007E01F800FC00FC01F8007E03F0003F07E0000FFF800003FE0001D1F7E9E22>I<03F040
+0FFDC01C0FC03803C07001C07001C0F000C0F000C0F000C0F80000FC0000FF80007FF8003FFE00
+3FFF801FFFC007FFC000FFE0000FE00003F00001F00000F0C000F0C000F0C000F0E000E0E001E0
+F801C0FE0380EFFF0081FC00141F7E9E19>83 D<FFFC0FFCFFFC0FFC0FC000C00FC000C00FC000
+C00FC000C00FC000C00FC000C00FC000C00FC000C00FC000C00FC000C00FC000C00FC000C00FC0
+00C00FC000C00FC000C00FC000C00FC000C00FC000C00FC000C00FC000C00FC000C00FC000C007
+C0018007E0018003E0030001F0070000F81E00003FF800000FE0001E1F7E9E23>85
+D<FFF803FEFFF803FE0FC000700FC000600FC0006007E000C007E000C007F001C003F0018003F0
+018001F8030001F8030001FC070000FC060000FC0600007E0C00007E0C00007E0C00003F180000
+3F1800003FB800001FB000001FB000000FE000000FE000000FE0000007C0000007C0000007C000
+00038000000380001F1F7F9E22>I<7FFC3FF87FFC3FF807E0070007F0060003F00E0001F80C00
+01FC1C0000FC180000FE3000007E7000003F6000003FC000001FC000001FC000000FC0000007E0
+000007F000000FF000000FF8000019F8000038FC000030FE0000607E0000E07F0000C03F000180
+1F8003801FC003000FC007000FE0FFE07FFEFFE07FFE1F1F7F9E22>88 D<0FF0003FFC007E1E00
+7E1F007E0F807E0F80180F80000F8000FF800FFF801F0F807C0F807C0F80F80F80F80F80F80F80
+F817807C37803FE3F00F81F014147F9316>97 D<03F8000FFE001F3F003E3F007E3F007C3F007C
+0C00FC0000FC0000FC0000FC0000FC0000FC00007C00007C00007E01803E03801F87000FFE0003
+F80011147F9314>99 D<001FE0001FE00003E00003E00003E00003E00003E00003E00003E00003
+E00003E00003E003F3E00FFFE01F0FE03E03E07C03E07C03E07C03E0FC03E0FC03E0FC03E0FC03
+E0FC03E0FC03E0FC03E07C03E07C03E03E07E01F0FE00FFBFC03F3FC16207F9F19>I<03F8000F
+FE001F0F003E07803C07807C07C07C07C0FC07C0FFFFC0FFFFC0FC0000FC0000FC00007C00007C
+00003E00C03E00C01F038007FF0001FC0012147F9315>I<007F0001FF8007C7C00F8FC00F0FC0
+1F0FC01F07801F00001F00001F00001F00001F0000FFF000FFF0001F00001F00001F00001F0000
+1F00001F00001F00001F00001F00001F00001F00001F00001F00001F00001F00001F0000FFF000
+FFF00012207F9F0E>I<03F0E00FFDF01E1EF03C0FF07C0F807C0F807C0F807C0F807C0F803C0F
+001E1E001FFC0033F0003000003000003800003FFE003FFF801FFFC03FFFE07803F07000F0F000
+F0F000F0F000F0F000F07801E03E07C01FFF8003FC00141E7F9317>I<FF0000FF00001F00001F
+00001F00001F00001F00001F00001F00001F00001F00001F00001F1F001F3FC01F63C01F83E01F
+83E01F03E01F03E01F03E01F03E01F03E01F03E01F03E01F03E01F03E01F03E01F03E01F03E01F
+03E0FFE7FCFFE7FC16207E9F19>I<1C003E007F007F007F003E001C0000000000000000000000
+0000FF00FF001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F00FF
+E0FFE00B217FA00C>I<FF0000FF00001F00001F00001F00001F00001F00001F00001F00001F00
+001F00001F00001F0FF01F0FF01F03801F06001F0C001F18001F30001F70001FF0001FF8001F7C
+001E3C001E1E001E1F001E0F001E0F801E07801E03C0FFCFF8FFCFF815207F9F18>107
+D<FF00FF001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F001F00
+1F001F001F001F001F001F001F001F001F001F001F00FFE0FFE00B207F9F0C>I<FE0F80F800FE
+3FC3FC001E63E63E001EC1FC1F001E81F81F001F01F01F001F01F01F001F01F01F001F01F01F00
+1F01F01F001F01F01F001F01F01F001F01F01F001F01F01F001F01F01F001F01F01F001F01F01F
+001F01F01F00FFE7FE7FE0FFE7FE7FE023147E9326>I<FE1F00FE3FC01E63C01E83E01E83E01F
+03E01F03E01F03E01F03E01F03E01F03E01F03E01F03E01F03E01F03E01F03E01F03E01F03E0FF
+E7FCFFE7FC16147E9319>I<01F8000FFF001F0F803E07C07C03E07C03E07C03E0FC03F0FC03F0
+FC03F0FC03F0FC03F0FC03F0FC03F07C03E07C03E03E07C01F0F800FFF0003FC0014147F9317>
+I<FE3C00FE7F001EDF801E9F801F9F801F1F801F06001F00001F00001F00001F00001F00001F00
+001F00001F00001F00001F00001F0000FFF000FFF0001114809313>114
+D<0FD83FF87038E018E018E018F800FF807FF03FF81FFC03FE003EC00EC00EE00EE00CF81CFFF8
+C7E00F147F9312>I<0300030003000300070007000F000F003F00FFF8FFF81F001F001F001F00
+1F001F001F001F001F001F001F0C1F0C1F0C1F0C1F0C0F9807F003E00E1D7F9C12>I<FF1FE0FF
+1FE01F03E01F03E01F03E01F03E01F03E01F03E01F03E01F03E01F03E01F03E01F03E01F03E01F
+03E01F03E01F07E00F0FE007FBFC03F3FC16147E9319>I<FFCFF1FCFFCFF1FC1F03C0701F03C0
+601F07E0600F87E0C00F87E0C00F8CF0C007CCF18007CCF18007D8798003F87B0003F87B0003F8
+7F0003F03F0001F03E0001F03E0001E01E0000E01C0000C00C001E147F9321>119
+D<FFCFF0FFCFF01F03800F830007860007CE0003FC0001F80001F00000F80000780000FC0001FE
+00039E00031F00060F800E07800C07C0FF1FF8FF1FF815147F9318>I<FFC3F8FFC3F81F00C01F
+00C00F81800F81800F818007C30007C30007E70003E60003E60001FC0001FC0001FC0000F80000
+F80000F80000700000700000600000600078E000FCC000FCC000C18000E380007F00003C000015
+1D7F9318>I E /Fl 92 126 df<70F8F8F8F8F8F8F8F8F8F8F8F8F8F8F8F870000000000070F8
+F8F870051C779B18>33 D<4010E038F078E038E038E038E038E038E038E038E038E038E0386030
+0D0E7B9C18>I<030600078F00078F00078F00078F00078F00078F007FFFC0FFFFE0FFFFE07FFF
+C00F1E000F1E000F1E000F1E000F1E000F1E007FFFC0FFFFE0FFFFE07FFFC01E3C001E3C001E3C
+001E3C001E3C001E3C000C1800131C7E9B18>I<00C00001C00001C00001C00003F0000FFC003F
+FE007DCF0071C700E1C380E1C780E1C780E1C780F1C00079C0003FC0001FE0000FF80001FC0001
+DE0001CF0001C70061C380F1C380F1C380E1C380E1C70071C70079DE003FFE001FF80007E00001
+C00001C00001C00000C00011247D9F18>I<3803007C07807C0780EE0F80EE0F00EE0F00EE1F00
+EE1E00EE1E00EE3E007C3C007C3C00387C0000780000780000F80000F00001F00001E00001E000
+03E00003C00003C00007C0000783800787C00F87C00F0EE00F0EE01F0EE01E0EE01E0EE03E0EE0
+3C07C03C07C018038013247E9F18>I<01C00007E0000FF0000E70001C38001C38001C38001C38
+001C73F81CF3F81CE3F80FC1C00FC3800F83800F03801F07003F87007B8E0071CE00E1FC00E0FC
+00E07C00E07870E0787070FE707FFFE03FC7E00F03C0151C7F9B18>I<387C7C7E3E0E0E0E1C1C
+38F8F0C0070E789B18>I<007000F001E003C007800F001E001C00380038007000700070007000
+E000E000E000E000E000E000E000E0007000700070007000380038001C001E000F00078003C001
+F000F000700C24799F18>I<6000F00078003C001E000F000780038001C001C000E000E000E000
+E00070007000700070007000700070007000E000E000E000E001C001C0038007800F001E003C00
+7800F00060000C247C9F18>I<01C00001C00001C00001C00041C100F1C780FDDF807FFF001FFC
+0007F00007F0001FFC007FFF00FDDF80F1C78041C10001C00001C00001C00001C00011147D9718
+>I<00600000F00000F00000F00000F00000F00000F00000F0007FFFC0FFFFE0FFFFE07FFFC000
+F00000F00000F00000F00000F00000F00000F00000600013147E9718>I<1C3E7E7F3F1F070E1E
+7CF860080C788518>I<7FFFC0FFFFE0FFFFE07FFFC013047E8F18>I<3078FCFC78300606778518
+>I<000300000780000780000F80000F00001F00001E00001E00003E00003C00007C0000780000
+780000F80000F00001F00001E00003E00003C00003C00007C0000780000F80000F00000F00001F
+00001E00003E00003C00003C00007C0000780000F80000F00000F0000060000011247D9F18>I<
+01F00007FC000FFE001F1F001C07003803807803C07001C07001C0E000E0E000E0E000E0E000E0
+E000E0E000E0E000E0E000E0E000E0F001E07001C07001C07803C03803801C07001F1F000FFE00
+07FC0001F000131C7E9B18>I<01800380038007800F803F80FF80FB8043800380038003800380
+0380038003800380038003800380038003800380038003807FFCFFFE7FFC0F1C7B9B18>I<03F0
+000FFE003FFF007C0F807003C0E001C0F000E0F000E06000E00000E00000E00001C00001C00003
+C0000780000F00001E00003C0000780000F00001E00007C0000F80001E00E03C00E07FFFE0FFFF
+E07FFFE0131C7E9B18>I<07F8001FFE003FFF007807807803C07801C03001C00001C000038000
+0380000F0003FF0003FE0003FF000007800003C00001C00000E00000E00000E0F000E0F000E0F0
+01C0F003C07C07803FFF001FFE0003F800131C7E9B18>I<001F00003F0000770000770000E700
+01E70001C7000387000787000707000E07001E07003C0700380700780700F00700FFFFF8FFFFF8
+FFFFF8000700000700000700000700000700000700007FF0007FF0007FF0151C7F9B18>I<3FFF
+803FFF803FFF803800003800003800003800003800003800003800003800003BF8003FFE003FFF
+003C07803003C00001C00000E00000E06000E0F000E0F000E0E001C07003C07C0F803FFF001FFC
+0003F000131C7E9B18>I<007E0001FF0007FF800F83C01E03C01C03C038018038000070000070
+0000E1F800E7FE00FFFF00FE0780F803C0F001C0F000E0E000E0F000E07000E07000E07000E038
+01C03C03C01E07800FFF0007FE0001F800131C7E9B18>I<E00000FFFFE0FFFFE0FFFFC0E00380
+E00700000F00001E00001C0000380000380000700000F00000E00000E00001C00001C00001C000
+038000038000038000038000070000070000070000070000070000070000070000131D7E9C18>
+I<03F8000FFE001FFF003E0F803803807001C07001C07001C07001C03803803C07801FFF0007FC
+000FFE001F1F003C07807001C0F001E0E000E0E000E0E000E0E000E07001C07803C03E0F801FFF
+000FFE0003F800131C7E9B18>I<03F0000FFC001FFE003C0F00780780700380E001C0E001C0E0
+01C0E001E0E001E07001E07803E03C0FE01FFFE00FFEE003F0E00000E00001C00001C00001C030
+0380780780780F00783E003FFC001FF00007C000131C7E9B18>I<3078FCFC7830000000000000
+00003078FCFC78300614779318>I<183C7E7E3C180000000000000000183C7E7E3E1E0E1C3C78
+F060071A789318>I<0000C00003E00007E0001FC0003F8000FE0001FC0007F0000FE0003F8000
+7F0000FC0000FC00007F00003F80000FE00007F00001FC0000FE00003F80001FC00007E00003E0
+0000C013187E9918>I<7FFFC0FFFFE0FFFFE0FFFFE0000000000000000000000000FFFFE0FFFF
+E0FFFFE07FFFC0130C7E9318>I<600000F80000FC00007F00003F80000FE00007F00001FC0000
+FE00003F80001FC00007E00007E0001FC0003F8000FE0001FC0007F0000FE0003F80007F0000FC
+0000F8000060000013187E9918>I<007C0001FE0007FF000F87801E03C03C1DC0387FC070FFE0
+71E3E071C1E0E1C1E0E380E0E380E0E380E0E380E0E380E0E380E0E1C1C071C1C071E3C070FF80
+387F003C1C001E00E00F83E007FFC001FF80007E00131C7E9B18>64 D<00700000F80000F80000
+D80000D80001DC0001DC0001DC00018C00038E00038E00038E00038E0007070007070007070007
+07000707000FFF800FFF800FFF800E03801C01C01C01C01C01C07F07F0FF07F87F07F0151C7F9B
+18>I<7FFC00FFFF007FFF801C03C01C01C01C00E01C00E01C00E01C00E01C01E01C01C01C07C0
+1FFF801FFF001FFFC01C03C01C00E01C00F01C00701C00701C00701C00701C00F01C00E01C03E0
+7FFFC0FFFF807FFE00141C7F9B18>I<00F8E003FEE007FFE00F07E01E03E03C01E03800E07000
+E07000E0700000E00000E00000E00000E00000E00000E00000E00000E000007000007000E07000
+E03800E03C00E01E01C00F07C007FF8003FE0000F800131C7E9B18>I<7FF800FFFE007FFF001C
+0F801C03C01C03C01C01E01C00E01C00E01C00F01C00701C00701C00701C00701C00701C00701C
+00701C00701C00F01C00E01C00E01C01E01C01C01C03C01C0F807FFF00FFFE007FF800141C7F9B
+18>I<FFFFF0FFFFF0FFFFF01C00701C00701C00701C00701C00001C00001C0E001C0E001C0E00
+1FFE001FFE001FFE001C0E001C0E001C0E001C00001C00001C00381C00381C00381C00381C0038
+FFFFF8FFFFF8FFFFF8151C7F9B18>I<FFFFF8FFFFF8FFFFF81C00381C00381C00381C00381C00
+001C00001C07001C07001C07001FFF001FFF001FFF001C07001C07001C07001C00001C00001C00
+001C00001C00001C00001C0000FFC000FFC000FFC000151C7F9B18>I<01F1C003FDC00FFFC01F
+0FC01C03C03803C03801C07001C07001C0700000E00000E00000E00000E00000E00000E00FF0E0
+1FF0E00FF07001C07001C07003C03803C03803C01C07C01F0FC00FFFC003FDC001F1C0141C7E9B
+18>I<7F07F0FF8FF87F07F01C01C01C01C01C01C01C01C01C01C01C01C01C01C01C01C01C01C0
+1FFFC01FFFC01FFFC01C01C01C01C01C01C01C01C01C01C01C01C01C01C01C01C01C01C01C01C0
+7F07F0FF8FF87F07F0151C7F9B18>I<7FFF00FFFF807FFF0001C00001C00001C00001C00001C0
+0001C00001C00001C00001C00001C00001C00001C00001C00001C00001C00001C00001C00001C0
+0001C00001C00001C00001C0007FFF00FFFF807FFF00111C7D9B18>I<01FFC001FFC001FFC000
+0E00000E00000E00000E00000E00000E00000E00000E00000E00000E00000E00000E00000E0000
+0E00000E00000E00000E00000E00000E00F00E00F00E00F03C007FFC003FF0000FC000121C7D9B
+18>I<7F07F0FF87F87F07F01C03C01C07801C07001C0E001C1E001C3C001C38001C70001CF000
+1DF0001DF0001FB8001FB8001F1C001E1C001C0E001C0E001C07001C07001C03801C03801C01C0
+7F03F0FF87F87F03F0151C7F9B18>I<FFC000FFC000FFC0001C00001C00001C00001C00001C00
+001C00001C00001C00001C00001C00001C00001C00001C00001C00001C00001C00001C00001C00
+E01C00E01C00E01C00E01C00E0FFFFE0FFFFE0FFFFE0131C7E9B18>I<FC01F8FE03F8FE03F83B
+06E03B06E03B06E03B06E03B8EE03B8EE0398CE0398CE039DCE039DCE039DCE038D8E038D8E038
+F8E03870E03870E03800E03800E03800E03800E03800E03800E0FE03F8FE03F8FE03F8151C7F9B
+18>I<7E07F0FF0FF87F07F01D81C01D81C01D81C01DC1C01CC1C01CC1C01CE1C01CE1C01CE1C0
+1C61C01C71C01C71C01C31C01C39C01C39C01C39C01C19C01C19C01C1DC01C0DC01C0DC01C0DC0
+7F07C0FF87C07F03C0151C7F9B18>I<0FFE003FFF807FFFC07803C07001C0F001E0E000E0E000
+E0E000E0E000E0E000E0E000E0E000E0E000E0E000E0E000E0E000E0E000E0E000E0E000E0E000
+E0E000E0F001E07001C07C07C07FFFC03FFF800FFE00131C7E9B18>I<FFFE00FFFF80FFFFC01C
+03C01C01E01C00E01C00701C00701C00701C00701C00701C00E01C01E01C03C01FFFC01FFF801F
+FE001C00001C00001C00001C00001C00001C00001C00001C0000FF8000FF8000FF8000141C7F9B
+18>I<0FFE003FFF807FFFC07803C07001C0F001E0E000E0E000E0E000E0E000E0E000E0E000E0
+E000E0E000E0E000E0E000E0E000E0E000E0E000E0E000E0E070E0E070E0F079E07039C0783FC0
+7FFFC03FFF800FFE00000F000007800007800003C00001C00001C013227E9B18>I<7FF800FFFE
+007FFF001C0F801C03801C03C01C01C01C01C01C01C01C03C01C03801C0F801FFF001FFE001FFE
+001C0F001C07001C03801C03801C03801C03801C03801C039C1C039C1C039C7F01F8FF81F87F00
+F0161C7F9B18>I<03F1C01FFFC03FFFC07C0FC07003C0E001C0E001C0E001C0E0000070000078
+00003F00001FF00007FE0000FF00000F800003C00001C00000E00000E06000E0E000E0E001E0F0
+01C0F80780FFFF80FFFE00E7F800131C7E9B18>I<7FFFF8FFFFF8FFFFF8E07038E07038E07038
+E07038007000007000007000007000007000007000007000007000007000007000007000007000
+00700000700000700000700000700000700007FF0007FF0007FF00151C7F9B18>I<FF83FEFF83
+FEFF83FE1C00701C00701C00701C00701C00701C00701C00701C00701C00701C00701C00701C00
+701C00701C00701C00701C00701C00701C00701C00700E00E00F01E00783C003FF8001FF00007C
+00171C809B18>I<FE03F8FF07F8FE03F83C01E01C01C01C01C01C01C01E03C00E03800E03800E
+03800E0380070700070700070700070700038E00038E00038E00038E00018C0001DC0001DC0001
+DC0000D80000F80000F800007000151C7F9B18>I<FE03F8FE03F8FE03F8700070700070700070
+3800E03800E03800E03800E03800E038F8E039FCE039DCE039DCE019DCC019DCC019DCC0198CC0
+198CC01D8DC01D8DC01D8DC01D05C00D05800F07800F07800E0380151C7F9B18>I<7F0FE07F9F
+E07F0FE00E07000F0700070E00078E00039C0003DC0001F80001F80000F80000F00000700000F0
+0000F80001FC0001DC00039E00038E00070F000707000E07800E03801E03C07F07F0FF07F87F07
+F0151C7F9B18>I<FE03F8FF07F8FE03F81C01C01E03C00E03800F0780070700070700038E0003
+8E0001DC0001DC0001DC0000F80000F80000700000700000700000700000700000700000700000
+700000700001FC0003FE0001FC00151C7F9B18>I<3FFFE07FFFE07FFFE07001C07003C0700780
+700700000F00001E00001C00003C0000780000700000F00001E00001C00003C000078000070000
+0F00001E00E01C00E03C00E07800E07000E0FFFFE0FFFFE0FFFFE0131C7E9B18>I<FFF8FFF8FF
+F8E000E000E000E000E000E000E000E000E000E000E000E000E000E000E000E000E000E000E000
+E000E000E000E000E000E000E000E000E000E000E000FFF8FFF8FFF80D24779F18>I<600000F0
+0000F00000F800007800007C00003C00003C00003E00001E00001F00000F00000F00000F800007
+800007C00003C00003C00003E00001E00001F00000F00000F800007800007800007C00003C0000
+3E00001E00001E00001F00000F00000F8000078000078000030011247D9F18>I<FFF8FFF8FFF8
+003800380038003800380038003800380038003800380038003800380038003800380038003800
+380038003800380038003800380038003800380038FFF8FFF8FFF80D247F9F18>I<018007C01F
+F07EFCF83EE00E0F067C9B18>I<7FFFC0FFFFE0FFFFE07FFFC013047E7F18>I<061E3E387070E0
+E0E0F8FC7C7C38070E789E18>I<0FF0001FFC003FFE003C0F0018070000038000038000FF8007
+FF801FFF807F0380780380E00380E00380E00380F00780780F803FFFF81FFDF807F0F815147E93
+18>I<7E0000FE00007E00000E00000E00000E00000E00000E00000E3E000EFF800FFFC00FC1E0
+0F80E00F00700E00700E00380E00380E00380E00380E00380E00380F00700F00700F80E00FC1E0
+0FFFC00EFF80063E00151C809B18>I<01FE0007FF001FFF803E0780380300700000700000E000
+00E00000E00000E00000E00000E000007000007001C03801C03E03C01FFF8007FF0001FC001214
+7D9318>I<001F80003F80001F8000038000038000038000038000038003E3800FFB801FFF803C
+1F80380F80700780700380E00380E00380E00380E00380E00380E00380700780700780380F803C
+1F801FFFF00FFBF803E3F0151C7E9B18>I<01F00007FC001FFE003E0F00380780700380700380
+E001C0E001C0FFFFC0FFFFC0FFFFC0E000007000007001C03801C03E07C01FFF8007FF0001F800
+12147D9318>I<001F80007FC000FFE000E1E001C0C001C00001C00001C0007FFFC0FFFFC0FFFF
+C001C00001C00001C00001C00001C00001C00001C00001C00001C00001C00001C00001C00001C0
+0001C0007FFF007FFF007FFF00131C7F9B18>I<01E1F007FFF80FFFF81E1E301C0E0038070038
+07003807003807003807001C0E001E1E001FFC001FF80039E0003800001C00001FFE001FFFC03F
+FFE07801F0700070E00038E00038E00038E000387800F07E03F01FFFC00FFF8001FC00151F7F93
+18>I<7E0000FE00007E00000E00000E00000E00000E00000E00000E3E000EFF800FFFC00FC1C0
+0F80E00F00E00E00E00E00E00E00E00E00E00E00E00E00E00E00E00E00E00E00E00E00E00E00E0
+7FC7FCFFE7FE7FC7FC171C809B18>I<038007C007C007C0038000000000000000007FC0FFC07F
+C001C001C001C001C001C001C001C001C001C001C001C001C001C001C0FFFFFFFFFFFF101D7C9C
+18>I<0038007C007C007C003800000000000000000FFC0FFC0FFC001C001C001C001C001C001C
+001C001C001C001C001C001C001C001C001C001C001C001C001C001C001C001C6038F078FFF07F
+E03F800E277E9C18>I<7E0000FE00007E00000E00000E00000E00000E00000E00000E3FF00E3F
+F00E3FF00E07800E0F000E1E000E3C000E78000EF0000FF8000FFC000F9C000F0E000E0F000E07
+000E03800E03C07FC7F8FFC7F87FC7F8151C7F9B18>I<FFC000FFC000FFC00001C00001C00001
+C00001C00001C00001C00001C00001C00001C00001C00001C00001C00001C00001C00001C00001
+C00001C00001C00001C00001C00001C00001C000FFFF80FFFF80FFFF80111C7D9B18>I<F9C1C0
+FFF7F0FFFFF03E3E383C3C383C3C38383838383838383838383838383838383838383838383838
+383838383838383838FE3E3EFE7E7EFE3E3E1714809318>I<7E3E00FEFF807FFFC00FC1C00F80
+E00F00E00E00E00E00E00E00E00E00E00E00E00E00E00E00E00E00E00E00E00E00E00E00E07FC7
+FCFFE7FE7FC7FC1714809318>I<01F0000FFE001FFF003E0F803803807001C07001C0E000E0E0
+00E0E000E0E000E0E000E0F001E07001C07803C03C07803E0F801FFF000FFE0001F00013147E93
+18>I<7E3E00FEFF807FFFC00FC1E00F80E00F00700E00700E00380E00380E00380E00380E0038
+0E00380F00700F00700F80E00FC1E00FFFC00EFF800E3E000E00000E00000E00000E00000E0000
+0E00000E00007FC000FFE0007FC000151E809318>I<01F38007FB801FFF803E1F80380F807007
+80700780E00380E00380E00380E00380E00380E00380700780700780380F803C1F801FFF800FFB
+8003E380000380000380000380000380000380000380000380001FF0003FF8001FF0151E7E9318
+>I<FF0FC0FF3FE0FF7FF007F0F007E06007C00007800007800007000007000007000007000007
+0000070000070000070000070000FFFC00FFFE00FFFC0014147E9318>I<07F7003FFF007FFF00
+780F00E00700E00700E007007C00007FE0001FFC0003FE00001F00600780E00380E00380F00380
+F80F00FFFF00FFFC00E7F00011147D9318>I<0180000380000380000380000380007FFFC0FFFF
+C0FFFFC00380000380000380000380000380000380000380000380000380000380400380E00380
+E00380E001C1C001FFC000FF80003E0013197F9818>I<7E07E0FE0FE07E07E00E00E00E00E00E
+00E00E00E00E00E00E00E00E00E00E00E00E00E00E00E00E00E00E00E00E01E00F03E007FFFC03
+FFFE01FCFC1714809318>I<7F8FF0FF8FF87F8FF01C01C00E03800E03800E0380070700070700
+070700078F00038E00038E00038E0001DC0001DC0001DC0000F80000F80000700015147F9318>
+I<FF07F8FF8FF8FF07F83800E03800E03800E03800E01C01C01C71C01CF9C01CF9C01CD9C01DDD
+C00DDD800DDD800DDD800D8D800F8F800F8F8007070015147F9318>I<7F8FF07F9FF07F8FF00F
+0700078E00039E0001DC0001F80000F80000700000F00000F80001DC00039E00038E000707000E
+07807F8FF0FF8FF87F8FF015147F9318>I<7F8FF0FF8FF87F8FF00E01C00E03800E0380070380
+070700070700038700038700038E0001CE0001CE0001CC0000CC0000DC00007800007800007800
+00700000700000700000F00000E00079E0007BC0007F80003F00001E0000151E7F9318>I<3FFF
+F07FFFF07FFFF07001E07003C0700780000F00001E00003C0000F80001F00003C0000780000F00
+701E00703C0070780070FFFFF0FFFFF0FFFFF014147F9318>I<0007E0001FE0007FE000780000
+E00000E00000E00000E00000E00000E00000E00000E00000E00000E00000E00001E0007FC000FF
+8000FF80007FC00001E00000E00000E00000E00000E00000E00000E00000E00000E00000E00000
+E00000E000007800007FE0001FE00007E013247E9F18>I<60F0F0F0F0F0F0F0F0F0F0F0F0F0F0
+F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0F0600424769F18>I<7C0000FF0000FFC00003C0
+0000E00000E00000E00000E00000E00000E00000E00000E00000E00000E00000E00000F000007F
+C0003FE0003FE0007FC000F00000E00000E00000E00000E00000E00000E00000E00000E00000E0
+0000E00000E00003C000FFC000FF00007C000013247E9F18>I E /Fm 46
+122 df<00003F000000000000FF800000000003E0C00000000007C060000000000F8070000000
+001F8030000000003F0038000000003F0038000000003F0038000000007F0038000000007F0038
+000000007F0070000000007F0060000000007F80E0000000007F80C0000000007F818000000000
+7F8300000000003F8600000000003FCC0000FFFE003FD80000FFFE003FF00000FFFE001FE00000
+0780001FE000000700001FF000000E00000FF000001C00000FF800001C00000FF800003800001F
+FC000038000033FE000070000063FE0000E00001C1FF0000E00003C1FF8001C00007C0FF800380
+000F807FC00700001F807FE00700003F803FF00E00007F801FF81C00007F800FFC380000FF8007
+FC700000FF8003FEE00000FF8001FFC00000FFC000FF800000FFC0007FC0000E7FC0003FF0000E
+7FE0007FF8001C3FF001F7FE003C1FF80FE1FF81F807FFFF807FFFF001FFFE001FFFC0003FE000
+01FE0037327DB13F>38 D<FFFFF8FFFFF8FFFFF8FFFFF8FFFFF8FFFFF8FFFFF815077F921B>45
+D<0001C0000003C000000FC000007FC0001FFFC000FFFFC000FFBFC000E03FC000003FC000003F
+C000003FC000003FC000003FC000003FC000003FC000003FC000003FC000003FC000003FC00000
+3FC000003FC000003FC000003FC000003FC000003FC000003FC000003FC000003FC000003FC000
+003FC000003FC000003FC000003FC000003FC000003FC000003FC000003FC000003FC000003FC0
+00003FC000003FC000003FC000003FC0007FFFFFF07FFFFFF07FFFFFF01C2E7AAD28>49
+D<003FE00001FFFE0007FFFF800F80FFC01E003FE038001FF07C000FF87E0007FCFF0007FCFF80
+07FEFF8007FEFF8003FEFF8003FE7F0003FE3E0007FE000007FE000007FC000007FC00000FF800
+000FF800000FF000001FE000001FC000003F8000007F0000007E000000F8000001F0000003E000
+0007C000000F0000001E000E003C000E0038000E0070001E00E0001C01C0001C0300003C07FFFF
+FC0FFFFFFC1FFFFFFC3FFFFFFC7FFFFFF8FFFFFFF8FFFFFFF8FFFFFFF81F2E7CAD28>I<001FF8
+000000FFFF000003FFFFC00007E01FF0000F0007F8001F8007FC003FC007FC003FC003FE003FC0
+03FE003FC003FE003FC003FE001F8003FE000F0007FE00000007FC00000007FC00000007F80000
+000FF00000001FE00000003F80000000FF0000003FF80000003FFF800000001FE00000000FF000
+000007F800000003FC00000003FE00000001FF00000001FF00000001FF80000001FF80000001FF
+801C0001FF803E0001FF807F0001FF80FF8001FF80FF8001FF00FF8001FF00FF8003FE007F0003
+FE007E0007FC003C0007F8001FC01FF0000FFFFFC00003FFFF0000003FF80000212E7DAD28>I<
+0000007000000000F000000001F000000003F000000007F00000000FF00000000FF00000001FF0
+0000003FF000000077F0000000F7F0000000E7F0000001C7F000000387F000000707F000000F07
+F000000E07F000001C07F000003807F000007007F00000F007F00000E007F00001C007F0000380
+07F000070007F0000F0007F0000E0007F0001C0007F000380007F000700007F000E00007F000FF
+FFFFFFE0FFFFFFFFE0FFFFFFFFE000000FF00000000FF00000000FF00000000FF00000000FF000
+00000FF00000000FF00000000FF00000000FF000000FFFFFE0000FFFFFE0000FFFFFE0232E7EAD
+28>I<0C0000300FC007F00FFFFFE00FFFFFC00FFFFF800FFFFF000FFFFE000FFFF8000FFFF000
+0FFF80000E0000000E0000000E0000000E0000000E0000000E0000000E0000000E0000000E1FF0
+000EFFFE000FE03F800F000FC00E0007E00C0007F0000007F8000003F8000003FC000003FC0000
+03FE000003FE180003FE3E0003FE7F0003FEFF0003FEFF0003FEFF0003FCFF0003FCFE0003FC78
+0007F8780007F03C000FE01E001FC00FC07F8007FFFF0001FFFC00003FE0001F2E7CAD28>I<00
+00FF00000007FFE000001FFFF000007F80F80000FE003C0001F8007C0003F000FE0007F001FE00
+0FE001FE000FE001FE001FC001FE003FC000FC003FC00078003FC00000007F800000007F800000
+007F80000000FF83FC0000FF8FFF8000FF9C0FC000FFB003F000FFB001F800FFE001FC00FFC001
+FE00FFC000FE00FFC000FF00FFC000FF00FF8000FF80FF8000FF80FF8000FF80FF8000FF807F80
+00FF807F8000FF807F8000FF807F8000FF803F8000FF003FC000FF001FC000FF001FC000FE000F
+C001FC0007E001FC0003F003F80001FC0FE00000FFFFC000003FFF0000000FFC0000212E7DAD28
+>I<38000000003E000000003FFFFFFFC03FFFFFFFC03FFFFFFFC03FFFFFFF807FFFFFFF007FFF
+FFFE007FFFFFFC007FFFFFF80078000038007000007000700000E000F00001C000E000038000E0
+00070000E00007000000000E000000001C00000000380000000078000000007000000000F00000
+0001E000000001E000000003E000000003C000000007C000000007C00000000FC00000000FC000
+00001FC00000001F800000001F800000003F800000003F800000003F800000003F800000007F80
+0000007F800000007F800000007F800000007F800000007F800000007F800000007F800000003F
+000000001E00000022307CAF28>I<000FFC0000007FFF800001FFFFE00003F00FF00007C003F8
+000F8000FC001F0000FC001F00007E003F00007E003F00007E003F00007E003F80007E003FC000
+7E003FF000FC003FFC00F8001FFE01F8001FFF81F0000FFFE3C00007FFFF800003FFFE000001FF
+FF000000FFFFC000003FFFE00000FFFFF00003E3FFF80007C1FFFC000F807FFE001F001FFF003E
+000FFF007E0003FF807E0000FF80FC00007F80FC00003F80FC00001F80FC00001F80FC00001F80
+FC00001F00FE00001F007E00001F007E00003E003F00007C001FC000F8000FF007F00003FFFFE0
+0000FFFF8000001FF80000212E7DAD28>I<000FF80000007FFF000001FFFF800003F80FC00007
+E007E0000FC003F0001FC001F8003FC001FC007F8001FC007F8001FE007F8000FE00FF8000FF00
+FF8000FF00FF8000FF00FF8000FF00FF8000FF80FF8000FF80FF8000FF80FF8000FF807F8001FF
+807F8001FF803F8001FF803FC001FF801FC003FF800FC006FF8007E006FF8001F81CFF8000FFF8
+FF80001FE0FF80000000FF00000000FF00000000FF00000000FF000F0001FE001F8001FE003FC0
+01FC003FC001FC003FC003F8003FC003F0003F8007E0001F000FC0001E001F80000F807F000007
+FFFE000001FFF80000007FC00000212E7DAD28>I<0000007800000000000078000000000000FC
+000000000000FC000000000000FC000000000001FE000000000001FE000000000003FF00000000
+0003FF000000000007FF800000000007FF800000000007FF80000000000FFFC0000000000E7FC0
+000000001E7FE0000000001C3FE0000000001C3FE000000000383FF000000000381FF000000000
+781FF800000000700FF800000000700FF800000000E00FFC00000000E007FC00000001E007FE00
+000001C003FE00000001C003FE000000038001FF000000038001FF000000078001FF8000000700
+00FF8000000F0000FFC000000FFFFFFFC000000FFFFFFFC000001FFFFFFFE000001C00003FE000
+003C00003FF000003800001FF000003800001FF000007000001FF800007000000FF80000F00000
+0FFC0000E0000007FC0000E0000007FC0001C0000007FE0003E0000003FE00FFFF0001FFFFFCFF
+FF0001FFFFFCFFFF0001FFFFFC36317DB03D>65 D<000003FF80018000003FFFF003800001FFFF
+FC07800007FF003F0F80001FF800079F80003FC00001FF8000FF800000FF8001FE0000007F8003
+FC0000003F8007FC0000001F8007F80000000F800FF00000000F801FF000000007801FF0000000
+07803FE000000007803FE000000003807FE000000003807FE000000003807FC000000000007FC0
+0000000000FFC00000000000FFC00000000000FFC00000000000FFC00000000000FFC000000000
+00FFC00000000000FFC00000000000FFC00000000000FFC000000000007FC000000000007FC000
+000000007FE000000000007FE000000003803FE000000003803FE000000003801FF00000000380
+1FF000000007800FF0000000070007F8000000070007FC0000000E0003FC0000001E0001FE0000
+001C0000FF8000007800003FC00000F000001FF80003E0000007FF003F80000001FFFFFE000000
+003FFFF80000000003FF80000031317CB03A>67 D<FFFFFFFFF00000FFFFFFFFFF0000FFFFFFFF
+FFC00000FF8000FFF00000FF80000FF80000FF800003FE0000FF800001FF0000FF800000FF8000
+FF8000007FC000FF8000003FC000FF8000001FE000FF8000001FF000FF8000000FF000FF800000
+0FF800FF8000000FF800FF80000007FC00FF80000007FC00FF80000007FC00FF80000007FC00FF
+80000007FE00FF80000007FE00FF80000007FE00FF80000007FE00FF80000007FE00FF80000007
+FE00FF80000007FE00FF80000007FE00FF80000007FE00FF80000007FE00FF80000007FE00FF80
+000007FC00FF80000007FC00FF80000007FC00FF80000007FC00FF8000000FF800FF8000000FF8
+00FF8000000FF000FF8000001FF000FF8000001FE000FF8000003FE000FF8000007FC000FF8000
+007F8000FF800001FF0000FF800003FE0000FF80000FFC0000FF80007FF000FFFFFFFFFFC000FF
+FFFFFFFF0000FFFFFFFFF0000037317EB03E>I<FFFFFFFFFFF0FFFFFFFFFFF0FFFFFFFFFFF000
+FF80003FF000FF800007F800FF800003F800FF800000F800FF800000F800FF8000007800FF8000
+007800FF8000003800FF8000003800FF8000003800FF8000001C00FF8007001C00FF8007001C00
+FF8007001C00FF8007000000FF8007000000FF800F000000FF801F000000FF803F000000FFFFFF
+000000FFFFFF000000FFFFFF000000FF803F000000FF801F000000FF800F000000FF8007000000
+FF8007000000FF8007000700FF8007000700FF8007000700FF8000000E00FF8000000E00FF8000
+000E00FF8000000E00FF8000001E00FF8000001E00FF8000003C00FF8000003C00FF8000007C00
+FF800000FC00FF800001FC00FF800007FC00FF80003FFCFFFFFFFFFFF8FFFFFFFFFFF8FFFFFFFF
+FFF830317EB035>I<FFFFFFFFFFE0FFFFFFFFFFE0FFFFFFFFFFE000FF80007FE000FF80000FF0
+00FF800003F000FF800001F000FF800001F000FF800000F000FF800000F000FF8000007000FF80
+00007000FF8000007000FF8000003800FF8000003800FF8007003800FF8007003800FF80070000
+00FF8007000000FF8007000000FF800F000000FF801F000000FF803F000000FFFFFF000000FFFF
+FF000000FFFFFF000000FF803F000000FF801F000000FF800F000000FF8007000000FF80070000
+00FF8007000000FF8007000000FF8007000000FF8000000000FF8000000000FF8000000000FF80
+00000000FF8000000000FF8000000000FF8000000000FF8000000000FF8000000000FF80000000
+00FF8000000000FF80000000FFFFFFC00000FFFFFFC00000FFFFFFC000002D317EB033>I<FFFF
+FF80FFFFFF80FFFFFF8000FF800000FF800000FF800000FF800000FF800000FF800000FF800000
+FF800000FF800000FF800000FF800000FF800000FF800000FF800000FF800000FF800000FF8000
+00FF800000FF800000FF800000FF800000FF800000FF800000FF800000FF800000FF800000FF80
+0000FF800000FF800000FF800000FF800000FF800000FF800000FF800000FF800000FF800000FF
+800000FF800000FF800000FF800000FF800000FF800000FF8000FFFFFF80FFFFFF80FFFFFF8019
+317EB01E>73 D<FFFFFF800000FFFFFF800000FFFFFF80000001FF0000000001FF0000000001FF
+0000000001FF0000000001FF0000000001FF0000000001FF0000000001FF0000000001FF000000
+0001FF0000000001FF0000000001FF0000000001FF0000000001FF0000000001FF0000000001FF
+0000000001FF0000000001FF0000000001FF0000000001FF0000000001FF0000000001FF000000
+0001FF0000000001FF0000000001FF0000000001FF0000000001FF0000000001FF0000038001FF
+0000038001FF0000038001FF0000038001FF0000078001FF0000070001FF0000070001FF00000F
+0001FF00000F0001FF00000F0001FF00001F0001FF00003F0001FF00007F0001FF0000FF0001FF
+0001FE0001FF000FFE00FFFFFFFFFE00FFFFFFFFFE00FFFFFFFFFE0029317DB030>76
+D<FFFFC000000003FFFFFFFFC000000003FFFFFFFFE000000007FFFF00FFE000000007FF0000EF
+F00000000EFF0000EFF00000000EFF0000EFF00000000EFF0000E7F80000001CFF0000E7F80000
+001CFF0000E3FC00000038FF0000E3FC00000038FF0000E1FE00000070FF0000E1FE00000070FF
+0000E0FF000000E0FF0000E0FF000000E0FF0000E07F800001C0FF0000E07F800001C0FF0000E0
+3FC0000380FF0000E03FC0000380FF0000E03FC0000380FF0000E01FE0000700FF0000E01FE000
+0700FF0000E00FF0000E00FF0000E00FF0000E00FF0000E007F8001C00FF0000E007F8001C00FF
+0000E003FC003800FF0000E003FC003800FF0000E001FE007000FF0000E001FE007000FF0000E0
+00FF00E000FF0000E000FF00E000FF0000E000FF00E000FF0000E0007F81C000FF0000E0007F81
+C000FF0000E0003FC38000FF0000E0003FC38000FF0000E0001FE70000FF0000E0001FE70000FF
+0000E0000FFE0000FF0000E0000FFE0000FF0000E00007FC0000FF0000E00007FC0000FF0000E0
+0007FC0000FF0000E00003F80000FF0001F00003F80000FF00FFFFE001F000FFFFFFFFFFE001F0
+00FFFFFFFFFFE000E000FFFFFF48317EB04D>I<00000FFF0000000000FFFFF000000007FC03FE
+0000001FE0007F8000003F80001FC000007F00000FE00001FE000007F80003FC000003FC0007F8
+000001FE0007F8000001FE000FF0000000FF001FF0000000FF801FE00000007F803FE00000007F
+C03FE00000007FC03FE00000007FC07FC00000003FE07FC00000003FE07FC00000003FE0FFC000
+00003FF0FFC00000003FF0FFC00000003FF0FFC00000003FF0FFC00000003FF0FFC00000003FF0
+FFC00000003FF0FFC00000003FF0FFC00000003FF0FFC00000003FF0FFC00000003FF07FC00000
+003FE07FE00000007FE07FE00000007FE07FE00000007FE03FE00000007FC03FE00000007FC01F
+F0000000FF801FF0000000FF800FF8000001FF0007F8000001FE0007FC000003FE0003FC000003
+FC0001FE000007F80000FF00000FF000003FC0003FC000001FE0007F80000007FC03FE00000000
+FFFFF0000000000FFF00000034317CB03D>79 D<FFFFFFFFE000FFFFFFFFFE00FFFFFFFFFF8000
+FF8000FFE000FF80003FF000FF80000FF800FF800007FC00FF800007FC00FF800003FE00FF8000
+03FE00FF800003FF00FF800003FF00FF800003FF00FF800003FF00FF800003FF00FF800003FF00
+FF800003FF00FF800003FE00FF800003FE00FF800007FC00FF800007F800FF80000FF800FF8000
+3FE000FF8000FFC000FFFFFFFF0000FFFFFFF80000FF8000000000FF8000000000FF8000000000
+FF8000000000FF8000000000FF8000000000FF8000000000FF8000000000FF8000000000FF8000
+000000FF8000000000FF8000000000FF8000000000FF8000000000FF8000000000FF8000000000
+FF8000000000FF8000000000FF8000000000FF80000000FFFFFF800000FFFFFF800000FFFFFF80
+000030317EB037>I<FFFFFFFF80000000FFFFFFFFF8000000FFFFFFFFFE00000000FF8003FF80
+000000FF80007FE0000000FF80001FF0000000FF80000FF8000000FF80000FF8000000FF80000F
+FC000000FF800007FC000000FF800007FE000000FF800007FE000000FF800007FE000000FF8000
+07FE000000FF800007FE000000FF800007FE000000FF800007FC000000FF80000FFC000000FF80
+000FF8000000FF80001FF0000000FF80003FE0000000FF80007FC0000000FF8003FF00000000FF
+FFFFF800000000FFFFFFE000000000FF8007F800000000FF8001FC00000000FF8000FE00000000
+FF80007F00000000FF80007F80000000FF80003FC0000000FF80003FC0000000FF80003FE00000
+00FF80003FE0000000FF80003FE0000000FF80003FE0000000FF80003FE0000000FF80003FF000
+0000FF80003FF0000000FF80003FF0000000FF80003FF0000000FF80003FF0038000FF80003FF8
+038000FF80001FF8038000FF80001FF8030000FF80000FFC0700FFFFFF8003FE0E00FFFFFF8001
+FFFC00FFFFFF80001FF00039317EB03C>82 D<001FF8018000FFFF038003FFFFC78007F007EF80
+0F8000FF801F00007F803E00001F803E00000F807C00000F807C00000780FC00000780FC000007
+80FC00000380FE00000380FE00000380FF00000000FFC00000007FF00000007FFF8000003FFFF8
+00003FFFFF80001FFFFFF0000FFFFFF80007FFFFFC0003FFFFFF0000FFFFFF00003FFFFF800001
+FFFFC000001FFFE0000001FFE00000003FE00000001FF00000000FF000000007F060000007F0E0
+000003F0E0000003F0E0000003F0E0000003E0F0000003E0F0000003E0F8000007C0FC000007C0
+FF00000F80FFC0001F00FBFC00FE00F1FFFFF800E03FFFF000C003FF800024317CB02D>I<7FFF
+FFFFFFFF007FFFFFFFFFFF007FFFFFFFFFFF007FC00FF801FF007E000FF8003F007C000FF8001F
+0078000FF8000F0078000FF8000F0070000FF8000700F0000FF8000780F0000FF8000780F0000F
+F8000780E0000FF8000380E0000FF8000380E0000FF8000380E0000FF8000380E0000FF8000380
+00000FF800000000000FF800000000000FF800000000000FF800000000000FF800000000000FF8
+00000000000FF800000000000FF800000000000FF800000000000FF800000000000FF800000000
+000FF800000000000FF800000000000FF800000000000FF800000000000FF800000000000FF800
+000000000FF800000000000FF800000000000FF800000000000FF800000000000FF80000000000
+0FF800000000000FF800000000000FF800000000000FF800000000000FF800000000000FF80000
+00007FFFFFFF0000007FFFFFFF0000007FFFFFFF000031307DAF38>I<00FFF0000003FFFF0000
+0F803F80000FC00FE0001FE007F0001FE007F0001FE003F8000FC003FC00078003FC00000003FC
+00000003FC00000003FC00000003FC000000FFFC00001FFFFC0000FFE3FC0003FC03FC000FF003
+FC001FC003FC003FC003FC007F8003FC007F8003FC00FF0003FC00FF0003FC00FF0003FC00FF00
+07FC00FF0007FC007F800DFC003FC01DFE001FE078FFF007FFE07FF000FF803FF024207E9F27>
+97 D<01F8000000FFF8000000FFF8000000FFF80000000FF800000007F800000007F800000007
+F800000007F800000007F800000007F800000007F800000007F800000007F800000007F8000000
+07F800000007F800000007F800000007F83FE00007F8FFFC0007FBE07F0007FF001F8007FE000F
+C007FC000FE007F80007F007F80007F807F80007F807F80003FC07F80003FC07F80003FC07F800
+03FE07F80003FE07F80003FE07F80003FE07F80003FE07F80003FE07F80003FE07F80003FE07F8
+0003FC07F80003FC07F80003FC07F80007F807F80007F807F80007F007FC000FE007FE000FC007
+E7003F8007C3C0FE000780FFF80007003FC00027327EB12D>I<000FFF00007FFFC001FC01F003
+F003F007E007F80FE007F81FC007F83FC003F03FC001E07F8000007F8000007F800000FF800000
+FF800000FF800000FF800000FF800000FF800000FF800000FF8000007F8000007F8000007F8000
+003FC0001C3FC0001C1FC000380FE0003807E0007003F001E001FC07C0007FFF00000FF8001E20
+7D9F24>I<0000000FC0000007FFC0000007FFC0000007FFC00000007FC00000003FC00000003F
+C00000003FC00000003FC00000003FC00000003FC00000003FC00000003FC00000003FC0000000
+3FC00000003FC00000003FC00000003FC00007F83FC0003FFF3FC000FE07BFC003F801FFC007E0
+007FC00FE0007FC01FC0003FC03FC0003FC03FC0003FC07F80003FC07F80003FC07F80003FC0FF
+80003FC0FF80003FC0FF80003FC0FF80003FC0FF80003FC0FF80003FC0FF80003FC0FF80003FC0
+7F80003FC07F80003FC07F80003FC03FC0003FC03FC0003FC01FC0003FC00FE0007FC007E000FF
+C003F003FFE001FC0F3FFE007FFE3FFE000FF03FFE27327DB12D>I<000FFC00007FFF8001FC0F
+C003F003E007E001F00FE001F81FC000FC3FC000FE3FC000FE7F80007E7F80007F7F80007FFF80
+007FFF80007FFFFFFFFFFFFFFFFFFF800000FF800000FF800000FF8000007F8000007F8000007F
+8000003FC000071FC000071FC0000E0FE0000E07F0001C03F8007800FE03E0003FFFC00007FE00
+20207E9F25>I<0001FE00000FFF80001FC3C0007F07E000FE0FF001FE0FF001FC0FF003FC0FF0
+03FC07E003FC018003FC000003FC000003FC000003FC000003FC000003FC000003FC000003FC00
+00FFFFFC00FFFFFC00FFFFFC0003FC000003FC000003FC000003FC000003FC000003FC000003FC
+000003FC000003FC000003FC000003FC000003FC000003FC000003FC000003FC000003FC000003
+FC000003FC000003FC000003FC000003FC000003FC000003FC000003FC000003FC000003FC0000
+7FFFF0007FFFF0007FFFF0001C327EB119>I<001FF007C000FFFE3FE001F83F79F007E00FC3F0
+0FE00FE1F00FC007E0E01FC007F0001FC007F0003FC007F8003FC007F8003FC007F8003FC007F8
+003FC007F8001FC007F0001FC007F0000FC007E0000FE00FE00007E00FC00003F83F000006FFFE
+00000E1FF000000E000000001E000000001E000000001F000000001F800000001FFFFF80000FFF
+FFF0000FFFFFFC0007FFFFFE0003FFFFFF0003FFFFFF800FFFFFFFC03F00007FC07E00001FE07C
+00000FE0FC000007E0FC000007E0FC000007E0FC000007E07E00000FC03E00000F803F00001F80
+0FC0007E0007F803FC0001FFFFF000001FFF0000242F7E9F28>I<03C00007E0000FF0001FF800
+1FF8001FF8001FF8000FF00007E00003C000000000000000000000000000000000000000000000
+00000000000001F8007FF8007FF8007FF80007F80007F80007F80007F80007F80007F80007F800
+07F80007F80007F80007F80007F80007F80007F80007F80007F80007F80007F80007F80007F800
+07F80007F80007F80007F80007F800FFFF80FFFF80FFFF8011337DB217>105
+D<01F8000000FFF8000000FFF8000000FFF80000000FF800000007F800000007F800000007F800
+000007F800000007F800000007F800000007F800000007F800000007F800000007F800000007F8
+00000007F800000007F800000007F800FFF807F800FFF807F800FFF807F8003F0007F8003C0007
+F800780007F800F00007F803C00007F807800007F80F000007F81E000007F878000007F8FC0000
+07F9FE000007FBFE000007FFFF000007FE7F800007FC7FC00007F83FC00007F01FE00007F00FF0
+0007F00FF80007F007FC0007F003FC0007F001FE0007F000FF0007F000FF8007F0007F8007F000
+7FC0FFFF81FFFEFFFF81FFFEFFFF81FFFE27327EB12B>107 D<01F800FFF800FFF800FFF8000F
+F80007F80007F80007F80007F80007F80007F80007F80007F80007F80007F80007F80007F80007
+F80007F80007F80007F80007F80007F80007F80007F80007F80007F80007F80007F80007F80007
+F80007F80007F80007F80007F80007F80007F80007F80007F80007F80007F80007F80007F80007
+F80007F80007F80007F800FFFFC0FFFFC0FFFFC012327DB117>I<03F007F8001FE000FFF03FFE
+00FFF800FFF0783F01E0FC00FFF0C03F8300FE000FF1801FC6007F0007F3001FCC007F0007F600
+1FF8007F8007FC001FF0007F8007FC001FF0007F8007FC001FF0007F8007F8001FE0007F8007F8
+001FE0007F8007F8001FE0007F8007F8001FE0007F8007F8001FE0007F8007F8001FE0007F8007
+F8001FE0007F8007F8001FE0007F8007F8001FE0007F8007F8001FE0007F8007F8001FE0007F80
+07F8001FE0007F8007F8001FE0007F8007F8001FE0007F8007F8001FE0007F8007F8001FE0007F
+8007F8001FE0007F8007F8001FE0007F8007F8001FE0007F80FFFFC3FFFF0FFFFCFFFFC3FFFF0F
+FFFCFFFFC3FFFF0FFFFC3E207D9F43>I<03F007F800FFF03FFE00FFF0783F00FFF0C03F800FF1
+801FC007F3001FC007F6001FE007FC001FE007FC001FE007FC001FE007F8001FE007F8001FE007
+F8001FE007F8001FE007F8001FE007F8001FE007F8001FE007F8001FE007F8001FE007F8001FE0
+07F8001FE007F8001FE007F8001FE007F8001FE007F8001FE007F8001FE007F8001FE007F8001F
+E007F8001FE0FFFFC3FFFFFFFFC3FFFFFFFFC3FFFF28207D9F2D>I<0007FC0000007FFFC00001
+FC07F00003F001F80007E000FC000FC0007E001FC0007F003FC0007F803F80003F807F80003FC0
+7F80003FC07F80003FC0FF80003FE0FF80003FE0FF80003FE0FF80003FE0FF80003FE0FF80003F
+E0FF80003FE0FF80003FE07F80003FC07F80003FC07F80003FC03FC0007F803FC0007F801FC000
+7F000FE000FE0007E000FC0003F803F80001FE0FF000007FFFC0000007FC000023207E9F28>I<
+01F83FE000FFF8FFFC00FFFBE07F00FFFF003F8007FE001FC007FC000FE007F8000FF007F80007
+F807F80007F807F80007FC07F80003FC07F80003FC07F80003FE07F80003FE07F80003FE07F800
+03FE07F80003FE07F80003FE07F80003FE07F80003FE07F80003FC07F80007FC07F80007FC07F8
+0007F807F80007F807F8000FF007FC000FE007FE001FC007FF003F8007FBC0FE0007F8FFF80007
+F83FC00007F800000007F800000007F800000007F800000007F800000007F800000007F8000000
+07F800000007F800000007F800000007F8000000FFFFC00000FFFFC00000FFFFC00000272E7E9F
+2D>I<03F03F00FFF07FC0FFF1C3E0FFF187E00FF30FF007F60FF007F60FF007FC07E007FC03C0
+07FC000007FC000007F8000007F8000007F8000007F8000007F8000007F8000007F8000007F800
+0007F8000007F8000007F8000007F8000007F8000007F8000007F8000007F8000007F8000007F8
+0000FFFFE000FFFFE000FFFFE0001C207E9F21>114 D<01FF860007FFFE001F00FE003C003E00
+78001E0078000E00F8000E00F8000E00F8000E00FC000000FF800000FFFC00007FFFC0003FFFF0
+003FFFF8001FFFFC0007FFFE0001FFFF00003FFF000000FF8000003F8060001F80E0000F80E000
+0F80F0000F80F0000F00F8000F00FC001E00FE001C00FF807800F3FFF000C07F800019207D9F20
+>I<001C0000001C0000001C0000001C0000001C0000003C0000003C0000003C0000007C000000
+7C000000FC000001FC000003FC000007FC00001FFFFE00FFFFFE00FFFFFE0003FC000003FC0000
+03FC000003FC000003FC000003FC000003FC000003FC000003FC000003FC000003FC000003FC00
+0003FC000003FC000003FC000003FC000003FC038003FC038003FC038003FC038003FC038003FC
+038003FC038001FC038001FC070000FE0700007F0E00003FFC000007F000192E7FAD1F>I<01F8
+0007E0FFF803FFE0FFF803FFE0FFF803FFE00FF8003FE007F8001FE007F8001FE007F8001FE007
+F8001FE007F8001FE007F8001FE007F8001FE007F8001FE007F8001FE007F8001FE007F8001FE0
+07F8001FE007F8001FE007F8001FE007F8001FE007F8001FE007F8001FE007F8001FE007F8001F
+E007F8003FE007F8003FE003F8007FE003F8007FE001FC00DFF000FE039FFF007FFF1FFF000FFC
+1FFF28207D9F2D>I<FFFF001FFCFFFF001FFCFFFF001FFC0FF80003C007F800038007FC000780
+03FC00070003FE00070001FE000E0001FF000E0000FF001C0000FF001C0000FF803C00007F8038
+00007FC07800003FC07000003FE0F000001FE0E000001FF1E000000FF1C000000FF9C0000007FB
+80000007FB80000007FF80000003FF00000003FF00000001FE00000001FE00000000FC00000000
+FC00000000780000000078000026207E9F2B>I<FFFF1FFFE03FF8FFFF1FFFE03FF8FFFF1FFFE0
+3FF80FF000FE0007800FF800FE00038007F800FF00070007F8007F00070007FC007F000F0003FC
+00FF800E0003FC00FF800E0001FE01FFC01C0001FE01DFC01C0001FF01DFC03C0000FF038FE038
+0000FF038FE03800007F878FF07000007F8707F07000007FC707F0F000003FCF07F8E000003FCE
+03F8E000001FFE03F9C000001FFC01FDC000001FFC01FFC000000FFC01FF8000000FF800FF8000
+000FF800FF80000007F0007F00000007F0007F00000003F0007E00000003E0003E00000003E000
+3E00000001C0001C000035207E9F3A>I<7FFF807FFC7FFF807FFC7FFF807FFC03FC000F0001FE
+001E0000FF003C0000FF803800007FC07800003FC0F000001FE1E000000FF3C000000FFF800000
+07FF00000003FE00000001FE00000000FF00000000FF80000000FFC0000001FFC0000003DFE000
+00078FF00000078FF800000F07FC00001E03FE00003C01FE00007800FF0000F000FF8001E0007F
+C003E0003FE0FFFC01FFFFFFFC01FFFFFFFC01FFFF28207F9F2B>I<FFFF001FFCFFFF001FFCFF
+FF001FFC0FF80003C007F800038007FC00078003FC00070003FE00070001FE000E0001FF000E00
+00FF001C0000FF001C0000FF803C00007F803800007FC07800003FC07000003FE0F000001FE0E0
+00001FF1E000000FF1C000000FF9C0000007FB80000007FB80000007FF80000003FF00000003FF
+00000001FE00000001FE00000000FC00000000FC00000000780000000078000000007000000000
+7000000000F000000000E000000001E000007C01C00000FE03C00000FE03800000FE07000000FE
+0F000000FC1E000000787C0000003FF00000000FC0000000262E7E9F2B>I
+E /Fn 2 16 df<0000FF00000007FFE000001F00F8000078001E0000E000070001800001800300
+0000C006000000600C000000300C000000301800000018300000000C300000000C600000000660
+0000000660000000066000000006C000000003C000000003C000000003C000000003C000000003
+C000000003C000000003C000000003C00000000360000000066000000006600000000660000000
+06300000000C300000000C18000000180C000000300C00000030060000006003000000C0018000
+018000E00007000078001E00001F00F8000007FFE0000000FF0000282B7EA02D>13
+D<03F0000FFC001FFE003FFF007FFF807FFF80FFFFC0FFFFC0FFFFC0FFFFC0FFFFC0FFFFC07FFF
+807FFF803FFF001FFE000FFC0003F00012127E9317>15 D E /Fo 82 125
+df<001F83E000F06E3001C078780380F8780300F0300700700007007000070070000700700007
+0070000700700007007000FFFFFF80070070000700700007007000070070000700700007007000
+070070000700700007007000070070000700700007007000070070000700700007007000070070
+0007007000070070003FE3FF001D20809F1B>11 D<003F0000E0C001C0C00381E00701E00701E0
+070000070000070000070000070000070000FFFFE00700E00700E00700E00700E00700E00700E0
+0700E00700E00700E00700E00700E00700E00700E00700E00700E00700E00700E00700E03FC3FC
+1620809F19>I<003FE000E0E001C1E00381E00700E00700E00700E00700E00700E00700E00700
+E00700E0FFFFE00700E00700E00700E00700E00700E00700E00700E00700E00700E00700E00700
+E00700E00700E00700E00700E00700E00700E00700E03FE7FC1620809F19>I<001F81F80000F0
+4F040001C07C06000380F80F000300F00F000700F00F0007007000000700700000070070000007
+0070000007007000000700700000FFFFFFFF000700700700070070070007007007000700700700
+070070070007007007000700700700070070070007007007000700700700070070070007007007
+000700700700070070070007007007000700700700070070070007007007003FE3FE3FE0232080
+9F26>I<7038F87CFC7EFC7E743A0402040204020804080410081008201040200F0E7F9F17>34
+D<0078000000840000018400000302000007020000070200000702000007020000070400000704
+000007080000070800000390000003A00FFC03C001E003C000C001C0008001C0010002E0010004
+E00200087002001878040030380800703C0800701C1000F00E1000F00F2000F007C000F0038004
+7001C0047802E008380470181C183C3007E00FC01E227EA023>38 D<70F8FCFC74040404080810
+102040060E7C9F0D>I<0040008001000300060004000C00180018003800300030007000600060
+0060006000E000E000E000E000E000E000E000E000E000E000E000E00060006000600060007000
+300030003800180018000C000400060003000100008000400A2E7BA112>I<8000400020003000
+180008000C00060006000700030003000380018001800180018001C001C001C001C001C001C001
+C001C001C001C001C001C001800180018001800380030003000700060006000C00080018003000
+2000400080000A2E7EA112>I<70F0F8F878080808101010202040050E7C840D>44
+D<FFF0FFF00C02808A0F>I<70F8F8F87005057C840D>I<0000400000C000018000018000018000
+0300000300000300000600000600000C00000C00000C0000180000180000180000300000300000
+600000600000600000C00000C00000C00001800001800001800003000003000006000006000006
+00000C00000C00000C0000180000180000300000300000300000600000600000600000C00000C0
+0000122D7EA117>I<03F0000E1C001C0E00180600380700700380700380700380700380F003C0
+F003C0F003C0F003C0F003C0F003C0F003C0F003C0F003C0F003C0F003C0F003C0F003C0700380
+7003807003807807803807001806001C0E000E1C0003F000121F7E9D17>I<008003800F80F380
+038003800380038003800380038003800380038003800380038003800380038003800380038003
+80038003800380038007C0FFFE0F1E7C9D17>I<03F0000C1C00100E00200700400780800780F0
+07C0F803C0F803C0F803C02007C00007C0000780000780000F00000E00001C0000380000700000
+600000C0000180000300000600400C00401800401000803FFF807FFF80FFFF80121E7E9D17>I<
+03F0000C1C00100E00200F00780F80780780780780380F80000F80000F00000F00001E00001C00
+00700007F000003C00000E00000F000007800007800007C02007C0F807C0F807C0F807C0F00780
+400780400F00200E00183C0007F000121F7E9D17>I<000600000600000E00000E00001E00002E
+00002E00004E00008E00008E00010E00020E00020E00040E00080E00080E00100E00200E00200E
+00400E00C00E00FFFFF0000E00000E00000E00000E00000E00000E00000E0000FFE0141E7F9D17
+>I<1803001FFE001FFC001FF8001FE00010000010000010000010000010000010000011F00016
+1C00180E001007001007800003800003800003C00003C00003C07003C0F003C0F003C0E0038040
+0380400700200600100C0008380007E000121F7E9D17>I<007C000182000701000E03800C0780
+180780380300380000780000700000700000F1F000F21C00F40600F80700F80380F80380F003C0
+F003C0F003C0F003C0F003C07003C07003C07003803803803807001807000C0E00061C0001F000
+121F7E9D17>I<4000007FFFE07FFFC07FFFC04000808001008001008002000004000004000008
+0000100000100000200000200000600000600000E00000C00001C00001C00001C00001C00003C0
+0003C00003C00003C00003C00003C00003C000018000131F7E9D17>I<03F0000C0C0010060030
+03002001806001806001806001807001807803003E03003F06001FC8000FF00003F80007FC000C
+7E00103F00300F806007806001C0C001C0C000C0C000C0C000C0C000806001802001001002000C
+0C0003F000121F7E9D17>I<03F0000E18001C0C00380600380700700700700380F00380F00380
+F003C0F003C0F003C0F003C0F003C07007C07007C03807C0180BC00E13C003E3C0000380000380
+000380000700300700780600780E00700C002018001070000FC000121F7E9D17>I<70F8F8F870
+0000000000000000000070F8F8F87005147C930D>I<70F8F8F8700000000000000000000070F0
+F8F878080808101010202040051D7C930D>I<000100000003800000038000000380000007C000
+0007C0000007C0000009E0000009E0000009E0000010F0000010F0000010F00000207800002078
+000020780000403C0000403C0000C03E0000801E0000801E0001FFFF0001000F0001000F000200
+07800200078002000780040003C0040003C00C0003C01E0003E0FF801FFE1F207F9F22>65
+D<FFFFE0000F0078000F001E000F001E000F000F000F000F800F000F800F000F800F000F800F00
+0F800F000F000F001F000F001E000F007C000FFFF0000F007C000F001F000F000F800F0007C00F
+0003C00F0003E00F0003E00F0003E00F0003E00F0003E00F0003C00F0007C00F0007800F000F00
+0F003E00FFFFF0001B1F7E9E20>I<000FE01000381C3000E0027003C00170078000F00F000070
+1E0000701E0000303C0000303C0000107C00001078000010F8000000F8000000F8000000F80000
+00F8000000F8000000F8000000F8000000F8000000780000007C0000103C0000103C0000101E00
+00201E0000200F0000200780004003C0008000E0030000380C00000FF0001C217E9F21>I<FFFF
+F80007801E0007800780078003C0078001E0078000F00780007007800078078000780780003C07
+80003C0780003C0780003E0780003E0780003E0780003E0780003E0780003E0780003E0780003E
+0780003C0780003C0780007C0780007807800078078000F0078001E0078003C00780078007801E
+00FFFFF8001F1F7F9E23>I<FFFFFF800F000F800F0003800F0001800F0000800F0000C00F0000
+400F0000400F0000400F0040400F0040000F0040000F00C0000F01C0000FFFC0000F01C0000F00
+C0000F0040000F0040000F0040000F0000200F0000200F0000200F0000400F0000400F0000400F
+0000C00F0001C00F0003800F000F80FFFFFF801B1F7E9E1F>I<FFFFFF80078007800780018007
+80018007800080078000C007800040078000400780004007800040078020000780200007802000
+078060000780E00007FFE0000780E0000780600007802000078020000780200007800000078000
+0007800000078000000780000007800000078000000780000007C00000FFFE00001A1F7F9E1E>
+I<000FE01000381C3000E0027003C00170078000F00F0000701E0000701E0000303C0000303C00
+00107C00001078000010F8000000F8000000F8000000F8000000F8000000F8000000F8000000F8
+003FFEF80001F0780000F07C0000F03C0000F03C0000F01E0000F01E0000F00F0000F0078000F0
+03C0017000E0023000380C10000FF0001F217E9F24>I<FFF07FF80F0007800F0007800F000780
+0F0007800F0007800F0007800F0007800F0007800F0007800F0007800F0007800F0007800F0007
+800FFFFF800F0007800F0007800F0007800F0007800F0007800F0007800F0007800F0007800F00
+07800F0007800F0007800F0007800F0007800F0007800F000780FFF07FF81D1F7E9E22>I<FFF0
+0F000F000F000F000F000F000F000F000F000F000F000F000F000F000F000F000F000F000F000F
+000F000F000F000F000F000F000F000F000F00FFF00C1F7E9E10>I<07FFC0003E00001E00001E
+00001E00001E00001E00001E00001E00001E00001E00001E00001E00001E00001E00001E00001E
+00001E00001E00001E00001E00001E00001E00201E00F81E00F81E00F81E00F01C00403C006038
+001070000FC00012207F9E17>I<FFF007FC0F0003E00F0001800F0001000F0002000F0004000F
+0008000F0010000F0020000F0040000F0080000F0100000F0300000F0780000F0F80000F13C000
+0F21E0000F41E0000F80F0000F0078000F0078000F003C000F001E000F001E000F000F000F0007
+800F0007800F0003C00F0003E00F0003F0FFF01FFE1F1F7E9E23>I<FFF8000F80000F00000F00
+000F00000F00000F00000F00000F00000F00000F00000F00000F00000F00000F00000F00000F00
+000F00000F00000F00000F00020F00020F00020F00020F00060F00040F00040F000C0F001C0F00
+7CFFFFFC171F7E9E1C>I<FF800007FE07800007C007800007C005C0000BC005C0000BC004E000
+13C004E00013C004E00013C004700023C004700023C004380043C004380043C004380043C0041C
+0083C0041C0083C0040E0103C0040E0103C0040E0103C004070203C004070203C004070203C004
+038403C004038403C00401C803C00401C803C00401C803C00400F003C00400F003C004006003C0
+1F006003C0FFE0607FFE271F7F9E2A>I<FF000FF80F8003E00F8000800BC0008009E0008009E0
+008008F000800878008008780080083C0080081E0080081E0080080F0080080780800807808008
+03C0800801E0800801E0800800F080080078800800788008003C8008001E8008001E8008000F80
+080007800800078008000380080001803E000180FF8000801D1F7E9E22>I<001FE00000703800
+01C00E0003800700070003800F0003C01E0001E03C0000F03C0000F07C0000F87C0000F8780000
+78F800007CF800007CF800007CF800007CF800007CF800007CF800007CF800007CF800007C7800
+00787C0000F87C0000F83C0000F03E0001F01E0001E00F0003C0070003800380070001E01E0000
+703800001FE0001E217E9F23>I<FFFFE0000F007C000F001E000F000F000F0007800F0007800F
+0007C00F0007C00F0007C00F0007C00F0007800F0007800F000F000F001E000F007C000FFFE000
+0F0000000F0000000F0000000F0000000F0000000F0000000F0000000F0000000F0000000F0000
+000F0000000F0000000F0000000F000000FFF000001A1F7E9E1F>I<001FE0000070380001C00E
+0003800700070003800F0003C01E0001E03E0001F03C0000F07C0000F87C0000F878000078F800
+007CF800007CF800007CF800007CF800007CF800007CF800007CF800007CF800007C780000787C
+0000F87C0000F83C0000F03E0781F01E0841E00F1023C0071023800390170001D01E0000783804
+001FF80400001C0400000C0C00000E1C00000FF800000FF8000007F8000007F0000001E01E297E
+9F23>I<FFFF80000F00F0000F003C000F001E000F000F000F000F000F000F800F000F800F000F
+800F000F800F000F000F000F000F001E000F003C000F00F0000FFF80000F01C0000F0070000F00
+70000F0038000F003C000F003C000F003C000F003E000F003E000F003E000F003E040F003F040F
+001F040F000F08FFF00788000001F01E207E9E21>I<03F0400C0CC01803C03001C06000C06000
+C0E000C0E00040E00040E00040F00000F800007C00007F80003FF8001FFF0007FF8000FFC0001F
+E00003E00001E00000F0000070800070800070800070800070C00060C000E0E000C0F80180C603
+0081FC0014217E9F19>I<7FFFFFE0780F01E0600F0060400F0020400F0020C00F0030800F0010
+800F0010800F0010800F0010000F0000000F0000000F0000000F0000000F0000000F0000000F00
+00000F0000000F0000000F0000000F0000000F0000000F0000000F0000000F0000000F0000000F
+0000000F0000000F0000001F800003FFFC001C1F7E9E21>I<FFF00FF80F0003E00F0000800F00
+00800F0000800F0000800F0000800F0000800F0000800F0000800F0000800F0000800F0000800F
+0000800F0000800F0000800F0000800F0000800F0000800F0000800F0000800F0000800F000080
+0F0000800700010007800100038001000380020001C0040000E0080000383000000FC0001D207E
+9E22>I<FFF003FE1F8000F80F0000600F00002007800040078000400780004003C0008003C000
+8003E0018001E0010001E0010000F0020000F0020000F802000078040000780400003C0800003C
+0800003C0800001E1000001E1000001F1000000F2000000F20000007C0000007C0000007C00000
+0380000003800000038000000100001F207F9E22>I<FFF07FF81FF01F000FC007C00F00078001
+800F00078001000F0007C001000F8007C00300078007C00200078009E0020007C009E0020003C0
+09E0040003C019F0040003C010F0040001E010F0080001E010F0080001E02078080000F0207810
+0000F02078100000F0403C10000078403C20000078403C20000078801E2000007C801E6000003C
+801E4000003D000F4000003F000F4000001F000F8000001F000F8000001E00078000000E000700
+00000E00070000000C000300000004000200002C207F9E2F>I<7FF81FF80FE007C007C0030003
+C0020003E0060001F0040000F0080000F8180000781000003C2000003E6000001E4000000F8000
+000F8000000780000003C0000007E0000005E0000008F0000018F8000010780000207C0000603E
+0000401E0000801F0001000F8001000780020007C0060003C01F0007E0FFC01FFE1F1F7F9E22>
+I<FFF001FF1F8000780F8000600780006007C0004003C0008003E0008001F0010000F0010000F8
+0200007C0600003C0400003E0800001E0800001F1000000FB0000007A0000007E0000003C00000
+03C0000003C0000003C0000003C0000003C0000003C0000003C0000003C0000003C0000003C000
+0003C000003FFC00201F7F9E22>I<FFFFC0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0
+C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0C0FFFF082D7CA10D>91 D<08041008201020104020
+4020804080408040B85CFC7EFC7E7C3E381C0F0E7A9F17>I<FFFF030303030303030303030303
+0303030303030303030303030303030303030303030303030303030303FFFF082D80A10D>I<08
+1020204040808080B8FCFC7C38060E7D9F0D>96 D<1FE000303000781800781C00300E00000E00
+000E00000E0000FE00078E001E0E00380E00780E00F00E10F00E10F00E10F01E10781E10386720
+0F83C014147E9317>I<1C0000FC00001C00001C00001C00001C00001C00001C00001C00001C00
+001C00001C00001C7C001D87001E01801E00C01C00E01C00701C00701C00781C00781C00781C00
+781C00781C00781C00701C00F01C00E01E00C01A0180198700107C0015207E9F19>I<01FC0007
+06001C0F00380F00380600780000700000F00000F00000F00000F00000F00000F0000070000078
+00003800803800801C010007060001F80011147F9314>I<0001C0000FC00001C00001C00001C0
+0001C00001C00001C00001C00001C00001C00001C001F1C0070DC00C03C01801C03801C07801C0
+7001C0F001C0F001C0F001C0F001C0F001C0F001C07001C07001C03801C01803C00C03C0070DC0
+01F1F815207F9F19>I<03F0000E1C001C0E00380700380700700700700380F00380F00380FFFF
+80F00000F00000F000007000007000003800803800801C010007060001F80011147F9314>I<00
+7C01C6030F070F0E060E000E000E000E000E000E000E00FFF00E000E000E000E000E000E000E00
+0E000E000E000E000E000E000E000E000E000E000E007FE01020809F0E>I<0000E003E3300E3C
+301C1C30380E00780F00780F00780F00780F00780F00380E001C1C001E380033E0002000002000
+003000003000003FFE001FFF801FFFC03001E0600070C00030C00030C00030C000306000603000
+C01C038003FC00141F7F9417>I<1C0000FC00001C00001C00001C00001C00001C00001C00001C
+00001C00001C00001C00001C7C001C86001D03001E03801E03801C03801C03801C03801C03801C
+03801C03801C03801C03801C03801C03801C03801C03801C03801C0380FF8FF014207E9F19>I<
+38007C007C007C0038000000000000000000000000001C00FC001C001C001C001C001C001C001C
+001C001C001C001C001C001C001C001C001C001C00FF80091F7F9E0C>I<00E001F001F001F000
+E0000000000000000000000000007007F000F00070007000700070007000700070007000700070
+007000700070007000700070007000700070007000706070F060F0C061803F000C28829E0E>I<
+1C0000FC00001C00001C00001C00001C00001C00001C00001C00001C00001C00001C00001C1FE0
+1C07801C06001C04001C08001C10001C20001C60001CE0001DF0001E70001C38001C3C001C1C00
+1C0E001C0F001C07001C07801C07C0FF9FF014207E9F18>I<1C00FC001C001C001C001C001C00
+1C001C001C001C001C001C001C001C001C001C001C001C001C001C001C001C001C001C001C001C
+001C001C001C001C00FF8009207F9F0C>I<1C3E03E000FCC30C30001D039038001E01E01C001E
+01E01C001C01C01C001C01C01C001C01C01C001C01C01C001C01C01C001C01C01C001C01C01C00
+1C01C01C001C01C01C001C01C01C001C01C01C001C01C01C001C01C01C001C01C01C00FF8FF8FF
+8021147E9326>I<1C7C00FC86001D03001E03801E03801C03801C03801C03801C03801C03801C
+03801C03801C03801C03801C03801C03801C03801C03801C0380FF8FF014147E9319>I<01F800
+070E001C03803801C03801C07000E07000E0F000F0F000F0F000F0F000F0F000F0F000F07000E0
+7000E03801C03801C01C0380070E0001F80014147F9317>I<1C7C00FD87001E01801E01C01C00
+E01C00F01C00701C00781C00781C00781C00781C00781C00781C00701C00F01C00E01E01C01E03
+801D87001C7C001C00001C00001C00001C00001C00001C00001C00001C0000FF8000151D7E9319
+>I<01F040070CC00E02C01C03C03801C07801C07001C0F001C0F001C0F001C0F001C0F001C0F0
+01C07001C07801C03801C01C03C00C05C00709C001F1C00001C00001C00001C00001C00001C000
+01C00001C00001C0000FF8151D7F9318>I<1CF0FD181E3C1E3C1E181C001C001C001C001C001C
+001C001C001C001C001C001C001C001C00FFC00E147E9312>I<0FC830386018C008C008C008E0
+007C003FE01FF007F8003C800E8006C006C006C004E00CD81887E00F147F9312>I<0200020002
+00060006000E000E003E00FFF80E000E000E000E000E000E000E000E000E000E000E000E040E04
+0E040E040E040708030801F00E1C7F9B12>I<1C0380FC1F801C03801C03801C03801C03801C03
+801C03801C03801C03801C03801C03801C03801C03801C03801C03801C07800C0780061B8003E3
+F014147E9319>I<FF83F83E00E01C00C00E00800E00800E008007010007010007830003820003
+820001C40001C40001E40000E80000E80000700000700000700000200015147F9318>I<FF9FE1
+FC3C0780701C0300601C0380200E0380400E0380400E03C0400704C0800704E0800704E0800388
+6100038871000388710001D0320001D03A0001D03E0000E01C0000E01C0000601800004008001E
+147F9321>I<FF87F81E03C00E01800E030007020003840001C80001D80000F000007000007800
+00F800009C00010E00020E000607000403800C03C03C03E0FE07FC16147F9318>I<FF83F83E00
+E01C00C00E00800E00800E008007010007010007830003820003820001C40001C40001E40000E8
+0000E800007000007000007000002000002000004000004000004000F08000F08000F100006200
+003C0000151D7F9318>I<7FFF700E600E401C40384078407000E001E001C00380078007010E01
+1E011C0338027006700EFFFE10147F9314>I<FFFFFC1601808C17>I<FFFFFFFFFFF02C01808C2D
+>I E /Fp 41 123 df<0003F07C001E0DC600380F0F00701E0F00E01E0E00E00C0001C01C0001
+C01C0001C01C0001C01C0001C01C00038038007FFFFFC003803800038038000380380003803800
+0700700007007000070070000700700007007000070070000E00E0000E00E0000E00E0000E00E0
+000E00E0000E00E0001C01C0001E01E000FF8FFC0020207E9F1B>11 D<0003E0001C1800381800
+703C00E03C00E03801C00001C00001C00001C00001C0000380007FFFF003807003807003807003
+80700700E00700E00700E00700E00700E00700E00E01C00E01C00E01C00E01C00E01C00E01C01C
+03801E03C0FF0FF016207E9F19>I<0006000C00100030006000C0008001800300030006000E00
+0C000C0018001800380038003000300070007000600060006000E000E000E000E000E000E000E0
+00E00060006000600060006000300030001000180008000C00040002000F2E7AA112>40
+D<008000C00040002000300030001800180018000C000C000C000C000C000C000C000C000C000C
+000C000C000C001C001C001C0018001800380038003000300070006000E000C000C00180010003
+0006000400080018003000400080000E2E80A112>I<FFF0FFF00C027E8A0F>45
+D<3078F8787005057C840D>I<0018003801F80E700070007000700070007000E000E000E000E0
+00E000E001C001C001C001C001C001C003800380038003800380038007000780FFFC0E1E7B9D17
+>49 D<1FFFFFFE3FFFFFFF00000000000000000000000000000000000000000000000000000000
+00000000FFFFFFFC7FFFFFF8200C7D9023>61 D<0000080000000C0000001C0000003C0000003C
+0000007C0000007E0000009E0000009E0000011E0000011E0000021E0000020F0000040F000004
+0F0000080F0000080F0000100F80001007800020078000200780007FFF80004007C0008003C000
+8003C0010003C0010003C0020003C0020001E0060001E01F0003E0FF801FFE1F207F9F22>65
+D<07FFFFF800F80078007800380078001800F0001800F0000800F0000800F0000800F0000800F0
+000801E0080001E0080001E0080001E0180001E0380001FFF80003C0300003C0100003C0100003
+C0100003C0100003C000000780000007800000078000000780000007800000078000000F000000
+0F800000FFFC00001D1F7E9E1E>70 D<07FF83FFC000F8007C000078003C000078003C0000F000
+780000F000780000F000780000F000780000F000780000F000780001E000F00001E000F00001E0
+00F00001E000F00001FFFFF00001E000F00003C001E00003C001E00003C001E00003C001E00003
+C001E00003C001E000078003C000078003C000078003C000078003C000078003C000078003C000
+0F000780000F8007C000FFF07FF800221F7E9E22>72 D<07FF8000F80000780000780000F00000
+F00000F00000F00000F00000F00001E00001E00001E00001E00001E00001E00003C00003C00003
+C00003C00003C00003C0000780000780000780000780000780000780000F00000F8000FFF00011
+1F7E9E10>I<07F8007FC0007C001F00007C000C00005E000400009E000800008F000800008F00
+0800008780080000878008000083C008000103C010000101E010000101E010000100F010000100
+F010000100781000020078200002003C200002003C200002001E200002001E200002000F200004
+000F4000040007C000040007C000040003C000040003C000040001C0000C000180001E00008000
+FF80008000221F7E9E22>78 D<001F8200706600C01E01800E03000E07000C0600040E00040E00
+040E00040F00000F00000F800007F00007FF0003FFC001FFE0003FF00003F80000F80000780000
+3C00003C400038400038400038400030600070600060F000C0E80180C6030081FC0017217E9F19
+>83 D<00FF01FE0180018001800180018003000300030003000300030006000600060006000600
+06000C000C000C000C000C000C0018001800180018001800180030003000300030003000300060
+0060006000600060006000FF00FF00102D7EA10D>91 D<00FF01FE00060006000600060006000C
+000C000C000C000C000C0018001800180018001800180030003000300030003000300060006000
+6000600060006000C000C000C000C000C000C0018001800180018001800180FF00FF00102D82A1
+0D>93 D<07F8000C0C001E06001E07001C070000070000070000070000FF0007C7001E07003C0E
+00780E00F00E10F00E10F00E10F01E10F02E20784F401F878014147D9317>97
+D<0700003F00000F00000700000700000E00000E00000E00000E00000E00000E00001C00001C7C
+001D83001E01801C01C01C00E03800E03800F03800F03800F03800F03800F07001E07001E07001
+C07003C0700380700700E80E00CC380083E00014207B9F19>I<00FE000383000E07801C078038
+0700380000780000F00000F00000F00000F00000E00000E00000E00000F00000F0010070020038
+04001C180007E00011147D9314>I<0000380001F8000078000038000038000070000070000070
+0000700000700000700000E000FCE00382E00601E01C01E03C00E03801C07801C0F001C0F001C0
+F001C0F001C0E00380E00380E00380E00380F00380700780380F001C378007C7E015207D9F19>
+I<00F800070E000E07001C0700380380780380700380F00380F00380FFFF80F00000E00000E000
+00E00000E00000F001007002003004001C180007E00011147D9314>I<0007C0001C600030F000
+60F000E0E000C00001C00001C00001C00001C00001C0000380003FFC0003800003800003800003
+80000700000700000700000700000700000700000E00000E00000E00000E00000E00000E00001C
+00001E0000FFC00014207F9F0E>I<00000E003E1100E1A301C1C20381E00780E00701E00F01E0
+0F01E00F01E00703C007038007870004FC000800000800001800001C00000FFF000FFFC00FFFE0
+1800F0300030600030C00030C00030C000306000603000C01C070007FC00181F809417>I<00E0
+0007E00001E00000E00000E00001C00001C00001C00001C00001C00001C000038000038F800390
+E003A0E003C0600380600780E00700E00700E00700E00700E00700E00E01C00E01C00E01C00E01
+C00E01C00E01C01C03801E03C0FF8FF014207E9F19>I<01C003E003E003C00180000000000000
+00000000000003801F800780038003800700070007000700070007000E000E000E000E000E000E
+001C001E00FF800B1F7F9E0C>I<00E00007E00001E00000E00000E00001C00001C00001C00001
+C00001C00001C0000380000381FC0380F00380C003818003810007040007080007180007380007
+7C00071C000E1C000E0E000E0E000E0F000E07000E07801C03801E07C0FF8FF016207E9F18>
+107 D<00E007E001E000E000E001C001C001C001C001C001C00380038003800380038003800700
+070007000700070007000E000E000E000E000E000E001C001E00FF800B207F9F0C>I<0387C07C
+001F9861860007A072070003C03403000380380300078078070007007007000700700700070070
+0700070070070007007007000E00E00E000E00E00E000E00E00E000E00E00E000E00E00E000E00
+E00E001C01C01C001E01E01E00FFCFFCFFC022147E9326>I<038F801F90E007A0E003C0600380
+600780E00700E00700E00700E00700E00700E00E01C00E01C00E01C00E01C00E01C00E01C01C03
+801E03C0FF8FF014147E9319>I<00FC000387000E01801C00C03800E03800E07000F0F000F0F0
+00F0F000F0F000F0E001E0E001E0E001C0E003C0F00380700700380E001C1C0007E00014147D93
+17>I<00E3E00007EC380000F01C0000E00E0000E00F0001C0070001C0078001C0078001C00780
+01C0078001C0078003800F0003800F0003800E0003801E0003801C0003803800074070000761C0
+00071F00000700000007000000070000000E0000000E0000000E0000000E0000001E000000FFC0
+0000191D809319>I<00FC200382600702601E01E03C01E03801C07801C0F001C0F001C0F001C0
+F001C0E00380E00380F00380F00380F00780700780380F001C370007C700000700000700000700
+000E00000E00000E00000E00001E0000FFC0131D7D9318>I<038E001FB38007C78003C7800383
+000780000700000700000700000700000700000E00000E00000E00000E00000E00000E00001C00
+001E0000FFC00011147E9312>I<01F9060708031803180138023C001F001FF007FC01FE001F40
+074003400360036006F004C81887E010147F9312>I<0080010001000100030007000F001E00FF
+F80E000E000E000E001C001C001C001C001C001C00380038103810381038103820382018400F80
+0D1C7C9B12>I<1C0380FC1F803C07801C03801C03803807003807003807003807003807003807
+00700E00700E00700E00700E00701E00701E00703C00305E001F9F8011147B9319>I<FF83F81E
+00E01C00C01C00801E00800E01000E03000E02000E040007040007080007080007100003900003
+A00003E00003C00003800001800001000015147C9318>I<FF9FE1FC3C0780701C0300601C0380
+601C0380401C0380800E0780800E0D81000E0981000E19C2000E11C2000F21C4000720C4000740
+C8000740E8000780F0000780F0000300E00003006000020040001E147C9321>I<1FF0FF03C078
+01C07001C04000E0C000E180007300007600003C00003C00001C00002E00004E00008700010700
+0203800403800C01C03C03E0FE07FC18147F9318>I<0FF83F8001E00E0001C00C0001C0080001
+E0080000E0100000E0300000E0200000E040000070400000708000007080000071000000390000
+003A0000003E0000003C0000003800000018000000100000001000000020000000200000004000
+0070C00000F0800000F1000000E600000078000000191D809318>I<0FFFE00E01E00C01C00803
+80080700100E00101C0000380000700000700000E00001C0000380800700800E00801C01001C01
+00380300700E00FFFE0013147F9314>I E /Fq 63 122 df<0001FF0000001FFFC000007F81E0
+0000FC01E00001F807F00003F807F00007F007F00007F007F00007F007F00007F007F00007F001
+C00007F000000007F000000007F000000007F03FF800FFFFFFF800FFFFFFF800FFFFFFF80007F0
+03F80007F003F80007F003F80007F003F80007F003F80007F003F80007F003F80007F003F80007
+F003F80007F003F80007F003F80007F003F80007F003F80007F003F80007F003F80007F003F800
+07F003F80007F003F80007F003F80007F003F80007F003F8007FFF3FFF807FFF3FFF807FFF3FFF
+80212A7FA925>12 D<0003F0000000000FF8000000001F1C000000003C0E000000007C06000000
+00FC0700000000F80700000001F80700000001F80700000001F80600000001F80E00000001FC0C
+00000001FC1800000001FC3800000001FC3000000000FE6000FFF800FEC000FFF800FF8000FFF8
+00FF00000E00007F00000E00007F00001C00007F80003800007FC000380000FFC0007000019FE0
+00E000038FE000E000070FF001C0000F07F80380001E07FC0380003E03FE0700007E01FE0E0000
+FE00FF1C0000FE007FB80000FE003FF00000FE001FE00000FF000FF000707F0007F800707F801F
+FE00E03FC0FEFF83E01FFFF87FFFC007FFE00FFF8000FF0001FE002D2A7DA934>38
+D<3C007F00FF80FF80FFC0FFC0FFC07FC03EC000C000C00180018001800300030006000E001C00
+380010000A157BA913>I<0006000C00180038007000E001E003C003C0078007800F800F001F00
+1F003E003E003E007E007E007E007C007C00FC00FC00FC00FC00FC00FC00FC00FC00FC00FC00FC
+00FC00FC00FC007C007C007E007E007E003E003E003E001F001F000F000F800780078003C003C0
+01E000E0007000380018000C00060F3C7AAC1A>I<C0006000300038001C000E000F0007800780
+03C003C003E001E001F001F000F800F800F800FC00FC00FC007C007C007E007E007E007E007E00
+7E007E007E007E007E007E007E007E007E007C007C00FC00FC00FC00F800F800F801F001F001E0
+03E003C003C0078007800F000E001C00380030006000C0000F3C7CAC1A>I<FFFF80FFFF80FFFF
+80FFFF80FFFF80FFFF8011067F9016>45 D<1C007F007F00FF80FF80FF807F007F001C0009097B
+8813>I<003F800001FFF00007E0FC000FC07E001F803F001F001F003F001F803E000F807E000F
+C07E000FC07E000FC07E000FC0FE000FE0FE000FE0FE000FE0FE000FE0FE000FE0FE000FE0FE00
+0FE0FE000FE0FE000FE0FE000FE0FE000FE0FE000FE0FE000FE0FE000FE0FE000FE07E000FC07E
+000FC07E000FC07E000FC03F001F803F001F801F001F001F803F000FC07E0007E0FC0001FFF000
+003F80001B277DA622>48 D<000E00001E00007E0007FE00FFFE00FFFE00F8FE0000FE0000FE00
+00FE0000FE0000FE0000FE0000FE0000FE0000FE0000FE0000FE0000FE0000FE0000FE0000FE00
+00FE0000FE0000FE0000FE0000FE0000FE0000FE0000FE0000FE0000FE0000FE0000FE0000FE00
+00FE00FFFFFEFFFFFEFFFFFE17277BA622>I<00FF800007FFF0000FFFFC001E03FE003800FF80
+7C003F80FE003FC0FF001FC0FF001FE0FF000FE0FF000FE07E000FE03C001FE000001FE000001F
+C000001FC000003F8000003F0000007E000000FC000000F8000001F0000003E00000078000000F
+0000001E0000003C00E0007000E000E000E001C001C0038001C0060001C00FFFFFC01FFFFFC03F
+FFFFC07FFFFFC0FFFFFF80FFFFFF80FFFFFF801B277DA622>I<007F800003FFF00007FFFC000F
+81FE001F007F003F807F003F803F803F803F803F803F801F803F801F003F8000007F0000007F00
+00007E000000FC000001F8000007F00000FFC00000FFC0000001F80000007E0000003F0000003F
+8000001FC000001FC000001FE000001FE03C001FE07E001FE0FF001FE0FF001FE0FF001FC0FF00
+3FC0FE003F807C007F003F01FE001FFFFC0007FFF00000FF80001B277DA622>I<00000F000000
+0F0000001F0000003F0000007F000000FF000001FF000001FF000003BF0000073F00000E3F0000
+1C3F00003C3F0000383F0000703F0000E03F0001C03F0003803F0007803F0007003F000E003F00
+1C003F0038003F0070003F00F0003F00FFFFFFF8FFFFFFF8FFFFFFF800007F0000007F0000007F
+0000007F0000007F0000007F0000007F0000007F00001FFFF8001FFFF8001FFFF81D277EA622>
+I<180003001F801F001FFFFE001FFFFC001FFFF8001FFFF0001FFFC0001FFF00001C0000001C00
+00001C0000001C0000001C0000001C0000001C0000001C7FC0001DFFF8001F80FC001E003F0008
+003F0000001F8000001FC000001FC000001FE000001FE018001FE07C001FE0FE001FE0FE001FE0
+FE001FE0FE001FC0FC001FC078003F8078003F803C007F001F01FE000FFFFC0003FFF00000FF80
+001B277DA622>I<0007F800003FFE0000FFFF0001FC078003F00FC007C01FC00F801FC01F801F
+C01F001FC03F000F803F0000007E0000007E0000007E000000FE020000FE1FF000FE3FFC00FE60
+3E00FE801F00FF801F80FF000FC0FF000FC0FE000FE0FE000FE0FE000FE0FE000FE07E000FE07E
+000FE07E000FE07E000FE03E000FE03F000FC01F000FC01F001F800F801F0007E07E0003FFFC00
+01FFF800003FC0001B277DA622>I<380000003E0000003FFFFFF03FFFFFF03FFFFFF07FFFFFE0
+7FFFFFC07FFFFF807FFFFF0070000E0070000E0070001C00E0003800E0007000E000E0000001C0
+000001C000000380000007800000070000000F0000001F0000001E0000003E0000003E0000007E
+0000007C0000007C000000FC000000FC000000FC000000FC000001FC000001FC000001FC000001
+FC000001FC000001FC000001FC000000F80000007000001C297CA822>I<003FC00001FFF00003
+FFFC0007C07E000F003F001E001F001E000F803E000F803E000F803F000F803F800F803FC00F00
+3FF01F001FFC1E001FFE3C000FFFF80007FFE00003FFF00001FFFC0001FFFE0007FFFF000F0FFF
+801E07FFC03E01FFC07C007FE07C001FE0F8000FE0F80007E0F80003E0F80003E0F80003E0F800
+03C07C0003C07E0007803F000F001FC03F000FFFFC0003FFF800007FC0001B277DA622>I<007F
+800001FFF00007FFF8000FC0FC001F803E003F001F007E001F807E001F807E000F80FE000FC0FE
+000FC0FE000FC0FE000FE0FE000FE0FE000FE0FE000FE0FE000FE07E001FE07E001FE03F003FE0
+1F002FE00F80CFE007FF8FE001FF0FE000080FE000000FC000000FC000000FC000001F803E001F
+807F001F807F003F007F003E007F007E007E00FC003E03F8001FFFE0000FFF800001FE00001B27
+7DA622>I<00000780000000000780000000000FC0000000000FC0000000000FC0000000001FE0
+000000001FE0000000003FF0000000003FF0000000003FF00000000077F80000000077F8000000
+00F7FC00000000E3FC00000000E3FC00000001C1FE00000001C1FE00000003C1FF0000000380FF
+0000000380FF00000007007F80000007007F8000000F007FC000000E003FC000000E003FC00000
+1C001FE000001C001FE000003FFFFFF000003FFFFFF000003FFFFFF00000700007F80000700007
+F80000F00007FC0000E00003FC0001E00003FE0001C00001FE0001C00001FE0003C00001FF00FF
+FE003FFFFCFFFE003FFFFCFFFE003FFFFC2E297EA833>65 D<FFFFFFF800FFFFFFFF00FFFFFFFF
+C003F8001FE003F8000FF003F80007F803F80003F803F80003FC03F80003FC03F80001FC03F800
+01FC03F80001FC03F80003FC03F80003F803F80003F803F80007F003F8000FF003F8001FC003F8
+00FF8003FFFFFE0003FFFFFFC003F8000FF003F80003F803F80001FC03F80001FE03F80000FE03
+F80000FE03F80000FF03F80000FF03F80000FF03F80000FF03F80000FF03F80000FF03F80000FE
+03F80001FE03F80003FC03F80007FC03F8001FF8FFFFFFFFE0FFFFFFFFC0FFFFFFFE0028297DA8
+30>I<00007FE0030007FFFC07001FFFFF0F007FF00F9F00FF0001FF01FC0000FF03F800007F07
+F000003F0FE000001F1FC000001F1FC000000F3F8000000F3F800000077F800000077F80000007
+7F00000000FF00000000FF00000000FF00000000FF00000000FF00000000FF00000000FF000000
+00FF00000000FF000000007F000000007F800000007F800000073F800000073F800000071FC000
+00071FC000000E0FE000000E07F000001C03F800003C01FC00007800FF0001F0007FF007C0001F
+FFFF800007FFFE0000007FF00028297CA831>I<FFFFFFFC0000FFFFFFFF8000FFFFFFFFE00003
+FC001FF80003FC0003FC0003FC0000FE0003FC00007F0003FC00003F8003FC00001FC003FC0000
+1FC003FC00000FE003FC00000FE003FC000007F003FC000007F003FC000007F003FC000007F003
+FC000007F803FC000007F803FC000007F803FC000007F803FC000007F803FC000007F803FC0000
+07F803FC000007F803FC000007F803FC000007F803FC000007F003FC000007F003FC000007F003
+FC00000FE003FC00000FE003FC00000FC003FC00001FC003FC00003F8003FC00007F0003FC0000
+FF0003FC0003FC0003FC001FF800FFFFFFFFF000FFFFFFFF8000FFFFFFFC00002D297EA834>I<
+FFFFFFFFE0FFFFFFFFE0FFFFFFFFE003FC001FE003FC0007F003FC0001F003FC0001F003FC0000
+F003FC00007003FC00007003FC00007003FC01C07803FC01C03803FC01C03803FC01C03803FC03
+C00003FC03C00003FC0FC00003FFFFC00003FFFFC00003FFFFC00003FC0FC00003FC03C00003FC
+03C00003FC01C00E03FC01C00E03FC01C00E03FC01C01C03FC00001C03FC00001C03FC00001C03
+FC00003C03FC00003803FC00007803FC0000F803FC0001F803FC0003F803FC001FF8FFFFFFFFF0
+FFFFFFFFF0FFFFFFFFF027297EA82C>I<FFFFFFFFC0FFFFFFFFC0FFFFFFFFC003FC003FC003FC
+000FE003FC0003E003FC0001E003FC0001E003FC0000E003FC0000E003FC0000E003FC0000F003
+FC01C07003FC01C07003FC01C07003FC01C00003FC03C00003FC03C00003FC0FC00003FFFFC000
+03FFFFC00003FFFFC00003FC0FC00003FC03C00003FC03C00003FC01C00003FC01C00003FC01C0
+0003FC01C00003FC00000003FC00000003FC00000003FC00000003FC00000003FC00000003FC00
+000003FC00000003FC000000FFFFFC0000FFFFFC0000FFFFFC000024297EA82A>I<00007FE003
+000007FFFC0700001FFFFF0F00007FF00F9F0000FF0001FF0001FC0000FF0003F800007F0007F0
+00003F000FE000001F001FC000001F001FC000000F003F8000000F003F80000007007F80000007
+007F80000007007F0000000000FF0000000000FF0000000000FF0000000000FF0000000000FF00
+00000000FF0000000000FF0000000000FF0000000000FF0000FFFFF87F0000FFFFF87F8000FFFF
+F87F800000FF003F800000FF003F800000FF001FC00000FF001FC00000FF000FE00000FF0007F0
+0000FF0003F80000FF0001FC0000FF0000FF0001FF00007FF007FF00001FFFFF9F000007FFFE0F
+0000007FF003002D297CA835>I<FFFFF00FFFFFFFFFF00FFFFFFFFFF00FFFFF03FC00003FC003
+FC00003FC003FC00003FC003FC00003FC003FC00003FC003FC00003FC003FC00003FC003FC0000
+3FC003FC00003FC003FC00003FC003FC00003FC003FC00003FC003FC00003FC003FC00003FC003
+FC00003FC003FFFFFFFFC003FFFFFFFFC003FFFFFFFFC003FC00003FC003FC00003FC003FC0000
+3FC003FC00003FC003FC00003FC003FC00003FC003FC00003FC003FC00003FC003FC00003FC003
+FC00003FC003FC00003FC003FC00003FC003FC00003FC003FC00003FC003FC00003FC003FC0000
+3FC003FC00003FC0FFFFF00FFFFFFFFFF00FFFFFFFFFF00FFFFF30297EA835>I<FFFFF0FFFFF0
+FFFFF003FC0003FC0003FC0003FC0003FC0003FC0003FC0003FC0003FC0003FC0003FC0003FC00
+03FC0003FC0003FC0003FC0003FC0003FC0003FC0003FC0003FC0003FC0003FC0003FC0003FC00
+03FC0003FC0003FC0003FC0003FC0003FC0003FC0003FC0003FC0003FC00FFFFF0FFFFF0FFFFF0
+14297EA819>I<00FFFFF800FFFFF800FFFFF80000FF000000FF000000FF000000FF000000FF00
+0000FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF
+000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000FF000000
+FF000000FF000000FF001800FF007E00FF00FF00FF00FF00FF00FF00FF00FF00FE007E01FC007C
+01F8003E07F0000FFFE00003FF00001D297EA823>I<FFFFF000FFFEFFFFF000FFFEFFFFF000FF
+FE03FC00000F0003FC00001E0003FC00003C0003FC0000780003FC0000E00003FC0003C00003FC
+0007800003FC000F000003FC001E000003FC003C000003FC00F0000003FC01E0000003FC03C000
+0003FC07C0000003FC0FC0000003FC1FE0000003FC7FF0000003FCFFF8000003FDE7F8000003FF
+C3FC000003FF83FE000003FE01FF000003FC00FF000003FC007F800003FC007FC00003FC003FE0
+0003FC001FE00003FC000FF00003FC000FF80003FC0007F80003FC0003FC0003FC0001FE0003FC
+0001FF0003FC0000FF0003FC00007F80FFFFF00FFFFEFFFFF00FFFFEFFFFF00FFFFE2F297EA835
+>I<FFFFFC0000FFFFFC0000FFFFFC000003FC00000003FC00000003FC00000003FC00000003FC
+00000003FC00000003FC00000003FC00000003FC00000003FC00000003FC00000003FC00000003
+FC00000003FC00000003FC00000003FC00000003FC00000003FC00000003FC00000003FC000000
+03FC00000003FC0001C003FC0001C003FC0001C003FC0001C003FC0003C003FC00038003FC0003
+8003FC00078003FC00078003FC000F8003FC000F8003FC001F8003FC007F8003FC01FF00FFFFFF
+FF00FFFFFFFF00FFFFFFFF0022297EA828>I<FFFE0000003FFF80FFFE0000003FFF80FFFF0000
+007FFF8003FF0000007FE00003FF0000007FE00003BF800000EFE00003BF800000EFE000039FC0
+0001CFE000039FC00001CFE000038FE000038FE000038FE000038FE000038FE000038FE0000387
+F000070FE0000387F000070FE0000383F8000E0FE0000383F8000E0FE0000381FC001C0FE00003
+81FC001C0FE0000381FC001C0FE0000380FE00380FE0000380FE00380FE00003807F00700FE000
+03807F00700FE00003803F80E00FE00003803F80E00FE00003803F80E00FE00003801FC1C00FE0
+0003801FC1C00FE00003800FE3800FE00003800FE3800FE000038007F7000FE000038007F7000F
+E000038007F7000FE000038003FE000FE000038003FE000FE000038001FC000FE000038001FC00
+0FE000038000F8000FE000FFFE00F803FFFF80FFFE00F803FFFF80FFFE007003FFFF8039297DA8
+40>I<FFFC00007FFFFFFE00007FFFFFFF00007FFF03FF800001C003FFC00001C003BFE00001C0
+039FE00001C0039FF00001C0038FF80001C00387FC0001C00383FE0001C00381FF0001C00380FF
+8001C003807F8001C003807FC001C003803FE001C003801FF001C003800FF801C0038007FC01C0
+038003FC01C0038003FE01C0038001FF01C0038000FF81C00380007FC1C00380003FE1C0038000
+1FF1C00380000FF1C00380000FF9C003800007FDC003800003FFC003800001FFC003800000FFC0
+038000007FC0038000007FC0038000003FC0038000001FC0038000000FC00380000007C0FFFE00
+0003C0FFFE000001C0FFFE000001C030297EA835>I<0000FFC00000000FFFFC0000003F807F00
+0000FE001FC00001F80007E00003F00003F00007E00001F8000FE00001FC001FC00000FE001FC0
+0000FE003F8000007F003F8000007F007F8000007F807F0000003F807F0000003F807F0000003F
+80FF0000003FC0FF0000003FC0FF0000003FC0FF0000003FC0FF0000003FC0FF0000003FC0FF00
+00003FC0FF0000003FC0FF0000003FC0FF0000003FC07F0000003F807F8000007F807F8000007F
+803F8000007F003F8000007F001FC00000FE001FC00000FE000FE00001FC0007F00003F80003F8
+0007F00001FC000FE00000FE001FC000003FC0FF0000000FFFFC00000000FFC000002A297CA833
+>I<FFFFFFF800FFFFFFFF00FFFFFFFFC003FC003FE003FC0007F003FC0003F803FC0003FC03FC
+0001FC03FC0001FE03FC0001FE03FC0001FE03FC0001FE03FC0001FE03FC0001FE03FC0001FE03
+FC0001FC03FC0003FC03FC0003F803FC0007F003FC003FE003FFFFFF8003FFFFFE0003FC000000
+03FC00000003FC00000003FC00000003FC00000003FC00000003FC00000003FC00000003FC0000
+0003FC00000003FC00000003FC00000003FC00000003FC00000003FC00000003FC000000FFFFF0
+0000FFFFF00000FFFFF0000027297EA82E>I<FFFFFFE00000FFFFFFFE0000FFFFFFFF800003FC
+003FE00003FC000FF00003FC0007F80003FC0003FC0003FC0001FC0003FC0001FE0003FC0001FE
+0003FC0001FE0003FC0001FE0003FC0001FE0003FC0001FE0003FC0001FC0003FC0003F80003FC
+0007F80003FC000FE00003FC003FC00003FFFFFE000003FFFFFE000003FC00FF800003FC003FC0
+0003FC001FE00003FC000FF00003FC0007F80003FC0007F80003FC0007F80003FC0007F80003FC
+0007F80003FC0007F80003FC0007F80003FC0007F80003FC0007F80003FC0007F80E03FC0007F8
+0E03FC0003F80E03FC0001FC1CFFFFF000FE1CFFFFF0007FF8FFFFF0000FE02F297EA832>82
+D<00FF806003FFF0E00FFFF8E01F80FDE03F001FE03E0007E07C0003E07C0003E0FC0001E0FC00
+01E0FC0000E0FE0000E0FE0000E0FF000000FFC000007FFC00007FFFE0003FFFF8001FFFFE001F
+FFFF0007FFFF8003FFFFC000FFFFC0000FFFE000007FE000001FF000000FF0000007F0E00003F0
+E00003F0E00003F0E00003F0F00003E0F00003E0F80007E0FC0007C0FF000F80FFE03F80E3FFFE
+00E1FFFC00C01FF0001C297CA825>I<7FFFFFFFFF807FFFFFFFFF807FFFFFFFFF807F807F807F
+807C007F800F8078007F80078078007F80078070007F800380F0007F8003C0F0007F8003C0E000
+7F8001C0E0007F8001C0E0007F8001C0E0007F8001C0E0007F8001C000007F80000000007F8000
+0000007F80000000007F80000000007F80000000007F80000000007F80000000007F8000000000
+7F80000000007F80000000007F80000000007F80000000007F80000000007F80000000007F8000
+0000007F80000000007F80000000007F80000000007F80000000007F80000000007F8000000000
+7F80000000FFFFFFC00000FFFFFFC00000FFFFFFC0002A287EA72F>I<FFFFF000FFFEFFFFF000
+FFFEFFFFF000FFFE03FC0000038003FC0000038003FC0000038003FC0000038003FC0000038003
+FC0000038003FC0000038003FC0000038003FC0000038003FC0000038003FC0000038003FC0000
+038003FC0000038003FC0000038003FC0000038003FC0000038003FC0000038003FC0000038003
+FC0000038003FC0000038003FC0000038003FC0000038003FC0000038003FC0000038003FC0000
+038003FC0000038003FC0000038003FC0000038001FC0000070001FE0000070000FE00000E0000
+7F00000E00003F00003C00001FC0007800000FF003F0000007FFFFE0000000FFFF800000001FFC
+00002F297EA834>I<FFFFF0007FFFFFFFF0007FFFFFFFF0007FFF03FE000001C001FE00000380
+01FE0000038001FF0000078000FF0000070000FF80000F00007F80000E00007FC0000E00003FC0
+001C00003FC0001C00003FE0003C00001FE0003800001FF0007800000FF0007000000FF8007000
+0007F800E0000007F800E0000003FC01C0000003FC01C0000003FE03C0000001FE0380000001FF
+0780000000FF0700000000FF87000000007F8E000000007F8E000000007FDE000000003FDC0000
+00003FFC000000001FF8000000001FF8000000000FF0000000000FF0000000000FF00000000007
+E00000000007E00000000003C00000000003C0000030297FA833>I<FFFFE07FFFE01FFFC0FFFF
+E07FFFE01FFFC0FFFFE07FFFE01FFFC003FC0003FC0000700003FC0003FC0000700003FE0001FE
+0000700001FE0001FE0000E00001FE0001FE0000E00001FF0001FF0001E00000FF0001FF0001C0
+0000FF0003FF8001C00000FF8003FF8003C000007F8003FF80038000007F8007FFC0038000003F
+C0073FC0070000003FC0073FC0070000003FE00E1FE00F0000001FE00E1FE00E0000001FE00E1F
+F00E0000001FF01C0FF01E0000000FF01C0FF01C0000000FF03C0FF81C00000007F83807F83800
+000007F83807F83800000007F87807FC3800000003FC7003FC7000000003FC7003FC7000000003
+FEE001FEF000000001FEE001FEE000000001FFE001FFE000000001FFC000FFE000000000FFC000
+FFC000000000FFC000FFC0000000007F80007F80000000007F80007F80000000007F80007F8000
+0000003F00003F00000000003F00003F00000000003E00001F00000000001E00001E0000000000
+1E00001E00000042297FA845>I<020007000E001C00180030003000600060006000C000C000DF
+00FF80FFC0FFC0FFC07FC07FC03F800F000A157CA913>96 D<03FF80000FFFF0001F01FC003F80
+FE003F807F003F803F003F803F801F003F8000003F8000003F8000003F8000003F80003FFF8001
+FC3F800FE03F801F803F803F003F807E003F80FC003F80FC003F80FC003F80FC003F80FC005F80
+7E00DF803F839FFC1FFE0FFC03FC03FC1E1B7E9A21>I<FFE00000FFE00000FFE000000FE00000
+0FE000000FE000000FE000000FE000000FE000000FE000000FE000000FE000000FE000000FE000
+000FE000000FE1FE000FEFFF800FFE07E00FF803F00FF001F80FE000FC0FE000FC0FE0007E0FE0
+007E0FE0007F0FE0007F0FE0007F0FE0007F0FE0007F0FE0007F0FE0007F0FE0007F0FE0007E0F
+E0007E0FE0007E0FE000FC0FE000FC0FF001F80FF803F00F9C0FE00F0FFF800E01FC00202A7EA9
+25>I<003FF00001FFFC0003F03E000FC07F001F807F003F007F003F007F007F003E007E000000
+7E000000FE000000FE000000FE000000FE000000FE000000FE000000FE0000007E0000007E0000
+007F0000003F0003803F8003801F8007000FE00E0003F83C0001FFF800003FC000191B7E9A1E>
+I<00007FF000007FF000007FF0000007F0000007F0000007F0000007F0000007F0000007F00000
+07F0000007F0000007F0000007F0000007F0000007F0003F87F001FFF7F007F03FF00FC00FF01F
+8007F03F0007F03F0007F07E0007F07E0007F07E0007F0FE0007F0FE0007F0FE0007F0FE0007F0
+FE0007F0FE0007F0FE0007F0FE0007F07E0007F07E0007F03F0007F03F0007F01F800FF00FC01F
+F007E07FFF01FFE7FF007F87FF202A7EA925>I<003FC00001FFF00003E07C000F803E001F801F
+001F001F003F000F807E000F807E000FC07E000FC0FE0007C0FE0007C0FFFFFFC0FFFFFFC0FE00
+0000FE000000FE0000007E0000007E0000007F0000003F0001C01F0001C00F80038007C0070003
+F01E0000FFFC00003FE0001A1B7E9A1F>I<0007F8003FFC007E3E01FC7F03F87F03F07F07F07F
+07F03E07F00007F00007F00007F00007F00007F00007F000FFFFC0FFFFC0FFFFC007F00007F000
+07F00007F00007F00007F00007F00007F00007F00007F00007F00007F00007F00007F00007F000
+07F00007F00007F00007F00007F00007F0007FFF807FFF807FFF80182A7EA915>I<00FF80F003
+FFE3F80FC1FE1C1F007C7C3F007E7C3E003E107E003F007E003F007E003F007E003F007E003F00
+7E003F003E003E003F007E001F007C000FC1F8000BFFE00018FF80001800000038000000380000
+003C0000003FFFF8003FFFFF001FFFFFC00FFFFFE007FFFFF01FFFFFF03C0007F07C0001F8F800
+00F8F80000F8F80000F8F80000F87C0001F07C0001F03F0007E00FC01F8007FFFF00007FF0001E
+287E9A22>I<FFE00000FFE00000FFE000000FE000000FE000000FE000000FE000000FE000000F
+E000000FE000000FE000000FE000000FE000000FE000000FE000000FE07E000FE1FF800FE30FC0
+0FE40FE00FE807E00FF807F00FF007F00FF007F00FE007F00FE007F00FE007F00FE007F00FE007
+F00FE007F00FE007F00FE007F00FE007F00FE007F00FE007F00FE007F00FE007F00FE007F00FE0
+07F00FE007F0FFFE3FFFFFFE3FFFFFFE3FFF202A7DA925>I<07000F801FC03FE03FE03FE01FC0
+0F8007000000000000000000000000000000FFE0FFE0FFE00FE00FE00FE00FE00FE00FE00FE00F
+E00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE0FFFEFFFEFFFE0F2B7EAA12>
+I<FFE00000FFE00000FFE000000FE000000FE000000FE000000FE000000FE000000FE000000FE0
+00000FE000000FE000000FE000000FE000000FE000000FE01FFC0FE01FFC0FE01FFC0FE007800F
+E00F000FE01E000FE03C000FE078000FE0E0000FE3C0000FE7C0000FEFE0000FFFF0000FFFF800
+0FF3F8000FE1FC000FC0FE000FC07F000FC07F000FC03F800FC01FC00FC00FE00FC00FE00FC007
+F0FFFC1FFFFFFC1FFFFFFC1FFF202A7FA923>107 D<FFE0FFE0FFE00FE00FE00FE00FE00FE00F
+E00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE0
+0FE00FE00FE00FE00FE00FE00FE00FE00FE00FE00FE0FFFEFFFEFFFE0F2A7EA912>I<FFC07F00
+1FC000FFC1FFC07FF000FFC307E0C1F8000FC407F101FC000FC803F200FC000FD803FE00FE000F
+D003FC00FE000FD003FC00FE000FE003F800FE000FE003F800FE000FE003F800FE000FE003F800
+FE000FE003F800FE000FE003F800FE000FE003F800FE000FE003F800FE000FE003F800FE000FE0
+03F800FE000FE003F800FE000FE003F800FE000FE003F800FE000FE003F800FE000FE003F800FE
+000FE003F800FE00FFFE3FFF8FFFE0FFFE3FFF8FFFE0FFFE3FFF8FFFE0331B7D9A38>I<FFC07E
+00FFC1FF80FFC30FC00FC40FE00FC807E00FD807F00FD007F00FD007F00FE007F00FE007F00FE0
+07F00FE007F00FE007F00FE007F00FE007F00FE007F00FE007F00FE007F00FE007F00FE007F00F
+E007F00FE007F00FE007F00FE007F0FFFE3FFFFFFE3FFFFFFE3FFF201B7D9A25>I<003FE00001
+FFFC0003F07E000FC01F801F800FC03F0007E03F0007E07E0003F07E0003F07E0003F0FE0003F8
+FE0003F8FE0003F8FE0003F8FE0003F8FE0003F8FE0003F8FE0003F87E0003F07E0003F03F0007
+E03F0007E01F800FC00FC01F8007F07F0001FFFC00003FE0001D1B7E9A22>I<FFE1FE00FFEFFF
+80FFFE0FE00FF803F00FF001F80FE001FC0FE000FC0FE000FE0FE000FE0FE0007F0FE0007F0FE0
+007F0FE0007F0FE0007F0FE0007F0FE0007F0FE0007F0FE0007E0FE000FE0FE000FE0FE000FC0F
+E001FC0FF001F80FF807F00FFC0FE00FEFFF800FE1FC000FE000000FE000000FE000000FE00000
+0FE000000FE000000FE000000FE000000FE00000FFFE0000FFFE0000FFFE000020277E9A25>I<
+FFC1F0FFC7FCFFC63E0FCC7F0FD87F0FD07F0FD07F0FF03E0FE0000FE0000FE0000FE0000FE000
+0FE0000FE0000FE0000FE0000FE0000FE0000FE0000FE0000FE0000FE0000FE000FFFF00FFFF00
+FFFF00181B7F9A1B>114 D<03FE300FFFF03E03F07800F07000F0F00070F00070F80070FE0000
+FFE0007FFF007FFFC03FFFE01FFFF007FFF800FFF80007FC0000FCE0007CE0003CF0003CF00038
+F80038FC0070FF01E0E7FFC0C1FF00161B7E9A1B>I<00E00000E00000E00000E00001E00001E0
+0001E00003E00003E00007E0000FE0001FFFE0FFFFE0FFFFE00FE0000FE0000FE0000FE0000FE0
+000FE0000FE0000FE0000FE0000FE0000FE0000FE0000FE0000FE0700FE0700FE0700FE0700FE0
+700FE0700FE07007F0E003F0C001FF80007F0014267FA51A>I<FFE07FF0FFE07FF0FFE07FF00F
+E007F00FE007F00FE007F00FE007F00FE007F00FE007F00FE007F00FE007F00FE007F00FE007F0
+0FE007F00FE007F00FE007F00FE007F00FE007F00FE007F00FE007F00FE007F00FE00FF00FE00F
+F007E017F003F067FF01FFC7FF007F87FF201B7D9A25>I<FFFC03FFFFFC03FFFFFC03FF0FF000
+F007F000E007F800E003F801C003F801C003FC03C001FC038001FE078000FE070000FF0700007F
+0E00007F0E00007F9E00003F9C00003FFC00001FF800001FF800000FF000000FF000000FF00000
+07E0000007E0000003C0000003C000201B7F9A23>I<FFFC7FFC1FFCFFFC7FFC1FFCFFFC7FFC1F
+FC0FE00FE001C007F007E0038007F007E0038007F807F0078003F807F0070003F80FF8070003FC
+0FF80F0001FC0FF80E0001FC1FFC0E0000FE1CFC1C0000FE1CFE1C0000FF387E3C00007F387E38
+00007F787F3800003FF03F7000003FF03FF000003FE01FF000001FE01FE000001FE01FE000000F
+C00FC000000FC00FC000000F8007C0000007800780000007800780002E1B7F9A31>I<FFFC1FFE
+FFFC1FFEFFFC1FFE07F0038003F8078003FC0F0001FE1E0000FE3C00007F3800007FF800003FF0
+00001FE000000FE000000FF0000007F800000FF800001FFC00003CFE000038FF0000787F0000F0
+3F8001E01FC003C01FE003800FE0FFF03FFFFFF03FFFFFF03FFF201B7F9A23>I<FFFC03FFFFFC
+03FFFFFC03FF0FF000F007F000E007F800E003F801C003F801C003FC03C001FC038001FE078000
+FE070000FF0700007F0E00007F0E00007F9E00003F9C00003FFC00001FF800001FF800000FF000
+000FF000000FF0000007E0000007E0000003C0000003C000000380000003800000078000380700
+007C0F0000FE0E0000FE1E0000FE1C0000FE38000074F000003FE000000F80000020277F9A23>
+I E /Fr 22 118 df<0F003FC07FE07FE0FFF0FFF0FFF0FFF07FE07FE03FC00F000C0C798B1B>
+46 D<0000000F80000000000F80000000001F80000000003F80000000007F8000000000FF8000
+000000FF8000000001FF8000000003FF8000000007FF8000000007FF800000000FFF800000001E
+FF800000003EFF800000007CFF8000000078FF80000000F0FF80000001E0FF80000003E0FF8000
+0003C0FF8000000780FF8000000F00FF8000001F00FF8000003E00FF8000003C00FF8000007800
+FF800000F000FF800001F000FF800001E000FF800003C000FF8000078000FF80000F8000FF8000
+1F0000FF80001E0000FF80003C0000FF8000780000FF8000F80000FF8000FFFFFFFFFF80FFFFFF
+FFFF80FFFFFFFFFF80FFFFFFFFFF80000001FF8000000001FF8000000001FF8000000001FF8000
+000001FF8000000001FF8000000001FF8000000001FF8000000001FF8000000001FF80000003FF
+FFFF800003FFFFFF800003FFFFFF800003FFFFFF8029377DB630>52 D<00000001E00000000000
+000003F00000000000000003F00000000000000007F80000000000000007F80000000000000007
+F8000000000000000FFC000000000000000FFC000000000000001FFE000000000000001FFE0000
+00000000001FFE000000000000003FFF000000000000003FFF000000000000007FFF8000000000
+00007BFF800000000000007BFF80000000000000F3FFC0000000000000F1FFC0000000000001F1
+FFE0000000000001E0FFE0000000000003E0FFF0000000000003C0FFF0000000000003C07FF000
+0000000007C07FF8000000000007803FF800000000000F803FFC00000000000F001FFC00000000
+000F001FFC00000000001F001FFE00000000001E000FFE00000000003E000FFF00000000003C00
+07FF00000000003C0007FF0000000000780007FF8000000000780003FF8000000000F80003FFC0
+00000000F00001FFC000000000F00001FFC000000001FFFFFFFFE000000001FFFFFFFFE0000000
+03FFFFFFFFF000000003FFFFFFFFF000000007C000007FF8000000078000007FF8000000078000
+003FF80000000F8000003FFC0000000F0000001FFC0000001F0000001FFE0000001E0000000FFE
+0000001E0000000FFE0000003E0000000FFF0000003C00000007FF0000007C00000007FF800000
+7800000003FF800000FC00000003FF8000FFFFF00003FFFFFFC0FFFFF00003FFFFFFC0FFFFF000
+03FFFFFFC0FFFFF00003FFFFFFC0423B7DBA49>65 D<FFFFFFFFFF800000FFFFFFFFFFF80000FF
+FFFFFFFFFF0000FFFFFFFFFFFF8000007FE00003FFE000007FE00000FFF000007FE000003FF800
+007FE000001FFC00007FE000001FFC00007FE000000FFE00007FE000000FFE00007FE000000FFF
+00007FE0000007FF00007FE0000007FF00007FE0000007FF00007FE0000007FF00007FE0000007
+FF00007FE0000007FF00007FE000000FFE00007FE000000FFE00007FE000000FFE00007FE00000
+1FFC00007FE000003FF800007FE000003FF000007FE00000FFE000007FE00001FFC000007FE000
+0FFF0000007FFFFFFFFC0000007FFFFFFFFC0000007FFFFFFFFF8000007FE00000FFE000007FE0
+00003FF800007FE000000FFC00007FE000000FFE00007FE0000007FF00007FE0000003FF80007F
+E0000003FF80007FE0000001FFC0007FE0000001FFC0007FE0000001FFE0007FE0000001FFE000
+7FE0000001FFE0007FE0000001FFE0007FE0000001FFE0007FE0000001FFE0007FE0000001FFE0
+007FE0000001FFE0007FE0000001FFC0007FE0000003FFC0007FE0000003FF80007FE0000007FF
+80007FE000000FFF00007FE000001FFE00007FE000003FFC00007FE00001FFF800FFFFFFFFFFFF
+F000FFFFFFFFFFFFC000FFFFFFFFFFFF0000FFFFFFFFFFF000003B3B7CBA45>I<FFFFFFFFFF80
+0000FFFFFFFFFFF80000FFFFFFFFFFFF0000FFFFFFFFFFFFC000007FF00007FFE000007FF00000
+7FF800007FF000001FFC00007FF000000FFE00007FF0000003FF00007FF0000001FF80007FF000
+0000FFC0007FF00000007FE0007FF00000007FE0007FF00000003FF0007FF00000003FF8007FF0
+0000001FF8007FF00000001FF8007FF00000001FFC007FF00000001FFC007FF00000000FFE007F
+F00000000FFE007FF00000000FFE007FF00000000FFE007FF00000000FFE007FF00000000FFF00
+7FF00000000FFF007FF00000000FFF007FF00000000FFF007FF00000000FFF007FF00000000FFF
+007FF00000000FFF007FF00000000FFF007FF00000000FFF007FF00000000FFF007FF00000000F
+FF007FF00000000FFF007FF00000000FFE007FF00000000FFE007FF00000000FFE007FF0000000
+0FFE007FF00000000FFC007FF00000001FFC007FF00000001FFC007FF00000001FF8007FF00000
+003FF8007FF00000003FF0007FF00000007FF0007FF00000007FE0007FF0000000FFC0007FF000
+0001FFC0007FF0000003FF80007FF0000007FF00007FF000001FFE00007FF000007FF800007FF0
+0007FFF000FFFFFFFFFFFFC000FFFFFFFFFFFF0000FFFFFFFFFFF80000FFFFFFFFFF800000403B
+7CBA4A>68 D<FFFFF00000000003FFFFE0FFFFF80000000007FFFFE0FFFFF80000000007FFFFE0
+FFFFFC000000000FFFFFE0007FFC000000000FFFC000007FFC000000000FFFC000007BFE000000
+001EFFC000007BFE000000001EFFC0000079FF000000003CFFC0000079FF000000003CFFC00000
+78FF8000000078FFC0000078FF8000000078FFC0000078FF8000000078FFC00000787FC0000000
+F0FFC00000787FC0000000F0FFC00000783FE0000001E0FFC00000783FE0000001E0FFC0000078
+1FF0000003C0FFC00000781FF0000003C0FFC00000781FF0000003C0FFC00000780FF800000780
+FFC00000780FF800000780FFC000007807FC00000F00FFC000007807FC00000F00FFC000007803
+FE00001E00FFC000007803FE00001E00FFC000007803FE00001E00FFC000007801FF00003C00FF
+C000007801FF00003C00FFC000007800FF80007800FFC000007800FF80007800FFC0000078007F
+C000F000FFC0000078007FC000F000FFC0000078007FC000F000FFC0000078003FE001E000FFC0
+000078003FE001E000FFC0000078001FF003C000FFC0000078001FF003C000FFC0000078000FF8
+078000FFC0000078000FF8078000FFC00000780007FC0F0000FFC00000780007FC0F0000FFC000
+00780007FC0F0000FFC00000780003FE1E0000FFC00000780003FE1E0000FFC00000780001FF3C
+0000FFC00000780001FF3C0000FFC00000780000FFF80000FFC00000780000FFF80000FFC00000
+780000FFF80000FFC000007800007FF00000FFC000007800007FF00000FFC000007800003FE000
+00FFC000007800003FE00000FFC00000FC00001FC00000FFC000FFFFFC001FC001FFFFFFE0FFFF
+FC001FC001FFFFFFE0FFFFFC000F8001FFFFFFE0FFFFFC00070001FFFFFFE0533B7CBA5C>77
+D<FFFFFFFFF800000000FFFFFFFFFFC0000000FFFFFFFFFFF8000000FFFFFFFFFFFE000000007F
+F0001FFF000000007FF00003FFC00000007FF00000FFE00000007FF000007FF00000007FF00000
+3FF80000007FF000003FF80000007FF000003FFC0000007FF000001FFC0000007FF000001FFC00
+00007FF000001FFE0000007FF000001FFE0000007FF000001FFE0000007FF000001FFE0000007F
+F000001FFE0000007FF000001FFE0000007FF000001FFC0000007FF000001FFC0000007FF00000
+3FFC0000007FF000003FF80000007FF000007FF00000007FF000007FE00000007FF00001FFC000
+00007FF00003FF800000007FF0001FFE000000007FFFFFFFF8000000007FFFFFFFC0000000007F
+FFFFFFC0000000007FF0007FF0000000007FF0001FF8000000007FF0000FFC000000007FF00007
+FE000000007FF00003FF000000007FF00003FF800000007FF00001FF800000007FF00001FF8000
+00007FF00001FFC00000007FF00001FFC00000007FF00001FFC00000007FF00001FFC00000007F
+F00001FFC00000007FF00001FFE00000007FF00001FFE00000007FF00001FFE00000007FF00001
+FFE00000007FF00001FFE00000007FF00001FFE001E0007FF00001FFE001E0007FF00000FFF001
+E0007FF00000FFF001E0007FF00000FFF003C0007FF000007FF803C0FFFFFFF8003FFC0780FFFF
+FFF8001FFE0F80FFFFFFF80007FFFF00FFFFFFF80001FFFC000000000000001FF000433C7CBA48
+>82 D<0003FF000300001FFFE0070000FFFFFC0F0001FFFFFE1F0003FE00FF3F0007F0001FFF00
+0FE00007FF001FC00001FF003F800000FF003F800000FF007F0000007F007F0000003F007F0000
+003F00FF0000001F00FF0000001F00FF0000001F00FF8000000F00FF8000000F00FFC000000F00
+FFC000000F00FFF0000000007FFC000000007FFF800000003FFFF80000003FFFFFC000001FFFFF
+FC00001FFFFFFF00000FFFFFFFC00007FFFFFFF00003FFFFFFF80000FFFFFFFC00007FFFFFFE00
+001FFFFFFE000003FFFFFF0000001FFFFF80000001FFFF800000000FFFC000000003FFC0000000
+00FFC0000000007FE0000000007FE0700000003FE0F00000003FE0F00000001FE0F00000001FE0
+F00000001FE0F80000001FE0F80000001FC0F80000001FC0FC0000001FC0FC0000003F80FE0000
+003F80FF0000003F00FFC000007F00FFE00000FE00FFFC0001FC00FDFFC00FF800F87FFFFFF000
+F01FFFFFC000E003FFFF0000C0003FF800002B3D7BBB36>I<3FFFFFFFFFFFFFC03FFFFFFFFFFF
+FFC03FFFFFFFFFFFFFC03FFFFFFFFFFFFFC03FF8007FF001FFC07FC0007FF0003FE07F80007FF0
+001FE07F00007FF0000FE07E00007FF00007E07C00007FF00003E07C00007FF00003E07C00007F
+F00003E07800007FF00001E07800007FF00001E07800007FF00001E07800007FF00001E0F00000
+7FF00000F0F000007FF00000F0F000007FF00000F0F000007FF00000F0F000007FF00000F00000
+007FF00000000000007FF00000000000007FF00000000000007FF00000000000007FF000000000
+00007FF00000000000007FF00000000000007FF00000000000007FF00000000000007FF0000000
+0000007FF00000000000007FF00000000000007FF00000000000007FF00000000000007FF00000
+000000007FF00000000000007FF00000000000007FF00000000000007FF00000000000007FF000
+00000000007FF00000000000007FF00000000000007FF00000000000007FF00000000000007FF0
+0000000000007FF00000000000007FF00000000000007FF00000000000007FF00000000000007F
+F00000000000007FF00000000000007FF00000000000007FF0000000000FFFFFFFFF8000000FFF
+FFFFFF8000000FFFFFFFFF8000000FFFFFFFFF80003C3A7DB943>I<003FFE00000001FFFFE000
+0007FFFFF800000FE007FC00000FF001FE00001FF800FF00001FF8007F80001FF8007FC0001FF8
+003FC0000FF0003FE00007E0003FE00003C0003FE0000000003FE0000000003FE0000000003FE0
+000000003FE0000000FFFFE000001FFFFFE000007FF83FE00003FF803FE00007FC003FE0000FF0
+003FE0001FE0003FE0003FE0003FE0007FC0003FE0007FC0003FE000FF80003FE000FF80003FE0
+00FF80003FE000FF80003FE000FF80007FE0007FC0007FE0007FC000DFE0003FE0039FF0001FF8
+0F0FFFE007FFFE0FFFE001FFFC07FFE0003FE000FFE02B267DA52F>97 D<0001FFF000000FFFFE
+00003FFFFF8000FF801FC001FE003FC003FC007FE007F8007FE00FF0007FE01FF0007FE03FE000
+3FC03FE0001F807FE0000F007FC00000007FC0000000FFC0000000FFC0000000FFC0000000FFC0
+000000FFC0000000FFC0000000FFC0000000FFC0000000FFC0000000FFC00000007FC00000007F
+E00000007FE00000003FE00000003FF00000F01FF00000F00FF80001E007F80001E003FC0003C0
+01FF000F8000FFC03F00003FFFFE00000FFFF8000001FFC00024267DA52B>99
+D<000000003F800000003FFF800000003FFF800000003FFF800000003FFF8000000001FF800000
+0000FF8000000000FF8000000000FF8000000000FF8000000000FF8000000000FF8000000000FF
+8000000000FF8000000000FF8000000000FF8000000000FF8000000000FF8000000000FF800000
+0000FF8000000000FF8000000000FF800000FF80FF80000FFFF0FF80003FFFFCFF8000FFC03FFF
+8001FE000FFF8003FC0003FF8007F80001FF800FF00000FF801FF00000FF803FE00000FF803FE0
+0000FF807FE00000FF807FC00000FF807FC00000FF807FC00000FF80FFC00000FF80FFC00000FF
+80FFC00000FF80FFC00000FF80FFC00000FF80FFC00000FF80FFC00000FF80FFC00000FF80FFC0
+0000FF807FC00000FF807FC00000FF807FC00000FF803FE00000FF803FE00000FF801FE00000FF
+800FF00001FF8007F80003FF8003F80007FF8001FE001FFFC000FF807EFFFE007FFFF8FFFE000F
+FFE0FFFE0001FF00FFFE2F3C7DBB36>I<0001FF8000000FFFF000007FFFFC0000FF81FE0003FE
+007F8007F8003F800FF0001FC00FF0000FE01FE0000FE03FE0000FF03FE00007F07FC00007F07F
+C00007F87FC00007F8FFC00007F8FFC00007F8FFFFFFFFF8FFFFFFFFF8FFFFFFFFF8FFC0000000
+FFC0000000FFC0000000FFC00000007FC00000007FC00000007FC00000003FE00000003FE00000
+781FE00000781FF00000780FF00000F007F80001F003FC0003E001FE000FC000FFC07F80003FFF
+FE00000FFFF8000000FFC00025267DA52C>I<00001FF0000000FFFC000003FFFF00000FF83F80
+001FE07F80003FC0FFC0007F80FFC000FF80FFC000FF80FFC001FF007F8001FF003F0001FF001E
+0001FF00000001FF00000001FF00000001FF00000001FF00000001FF00000001FF00000001FF00
+000001FF00000001FF000000FFFFFF8000FFFFFF8000FFFFFF8000FFFFFF800001FF00000001FF
+00000001FF00000001FF00000001FF00000001FF00000001FF00000001FF00000001FF00000001
+FF00000001FF00000001FF00000001FF00000001FF00000001FF00000001FF00000001FF000000
+01FF00000001FF00000001FF00000001FF00000001FF00000001FF00000001FF00000001FF0000
+0001FF00000001FF00000001FF00000001FF00000001FF0000007FFFFE00007FFFFE00007FFFFE
+00007FFFFE0000223C7DBB1E>I<00FE00000000FFFE00000000FFFE00000000FFFE00000000FF
+FE0000000007FE0000000003FE0000000003FE0000000003FE0000000003FE0000000003FE0000
+000003FE0000000003FE0000000003FE0000000003FE0000000003FE0000000003FE0000000003
+FE0000000003FE0000000003FE0000000003FE0000000003FE0000000003FE00FF800003FE03FF
+F00003FE0FFFF80003FE1E03FC0003FE3801FE0003FE6001FF0003FEC000FF0003FFC000FF8003
+FF8000FF8003FF0000FF8003FF0000FF8003FF0000FF8003FE0000FF8003FE0000FF8003FE0000
+FF8003FE0000FF8003FE0000FF8003FE0000FF8003FE0000FF8003FE0000FF8003FE0000FF8003
+FE0000FF8003FE0000FF8003FE0000FF8003FE0000FF8003FE0000FF8003FE0000FF8003FE0000
+FF8003FE0000FF8003FE0000FF8003FE0000FF8003FE0000FF8003FE0000FF8003FE0000FF80FF
+FFF83FFFFEFFFFF83FFFFEFFFFF83FFFFEFFFFF83FFFFE2F3C7CBB36>104
+D<00FE00FFFE00FFFE00FFFE00FFFE0007FE0003FE0003FE0003FE0003FE0003FE0003FE0003FE
+0003FE0003FE0003FE0003FE0003FE0003FE0003FE0003FE0003FE0003FE0003FE0003FE0003FE
+0003FE0003FE0003FE0003FE0003FE0003FE0003FE0003FE0003FE0003FE0003FE0003FE0003FE
+0003FE0003FE0003FE0003FE0003FE0003FE0003FE0003FE0003FE0003FE0003FE0003FE0003FE
+0003FE0003FE0003FE0003FE00FFFFF8FFFFF8FFFFF8FFFFF8153C7DBB1A>108
+D<01FC007FC0000FF80000FFFC03FFF8007FFF0000FFFC0FFFFC01FFFF8000FFFC1F03FE03E07F
+C000FFFC3800FF07001FE00007FC7000FF8E001FF00003FCC0007F98000FF00003FDC0007FF800
+0FF80003FD80007FF0000FF80003FF00007FE0000FF80003FF00007FE0000FF80003FF00007FE0
+000FF80003FE00007FC0000FF80003FE00007FC0000FF80003FE00007FC0000FF80003FE00007F
+C0000FF80003FE00007FC0000FF80003FE00007FC0000FF80003FE00007FC0000FF80003FE0000
+7FC0000FF80003FE00007FC0000FF80003FE00007FC0000FF80003FE00007FC0000FF80003FE00
+007FC0000FF80003FE00007FC0000FF80003FE00007FC0000FF80003FE00007FC0000FF80003FE
+00007FC0000FF80003FE00007FC0000FF80003FE00007FC0000FF80003FE00007FC0000FF80003
+FE00007FC0000FF80003FE00007FC0000FF80003FE00007FC0000FF800FFFFF81FFFFF03FFFFE0
+FFFFF81FFFFF03FFFFE0FFFFF81FFFFF03FFFFE0FFFFF81FFFFF03FFFFE04B267CA552>I<01FC
+00FF8000FFFC03FFF000FFFC0FFFF800FFFC1E03FC00FFFC3801FE0007FC6001FF0003FCC000FF
+0003FDC000FF8003FD8000FF8003FF0000FF8003FF0000FF8003FF0000FF8003FE0000FF8003FE
+0000FF8003FE0000FF8003FE0000FF8003FE0000FF8003FE0000FF8003FE0000FF8003FE0000FF
+8003FE0000FF8003FE0000FF8003FE0000FF8003FE0000FF8003FE0000FF8003FE0000FF8003FE
+0000FF8003FE0000FF8003FE0000FF8003FE0000FF8003FE0000FF8003FE0000FF8003FE0000FF
+8003FE0000FF80FFFFF83FFFFEFFFFF83FFFFEFFFFF83FFFFEFFFFF83FFFFE2F267CA536>I<00
+01FFC00000000FFFF80000007FFFFF000000FF80FF800003FE003FE00007F8000FF0000FF00007
+F8000FF00007F8001FE00003FC003FE00003FE003FE00003FE007FC00001FF007FC00001FF007F
+C00001FF007FC00001FF00FFC00001FF80FFC00001FF80FFC00001FF80FFC00001FF80FFC00001
+FF80FFC00001FF80FFC00001FF80FFC00001FF80FFC00001FF807FC00001FF007FC00001FF007F
+C00001FF003FE00003FE003FE00003FE001FE00003FC001FF00007FC000FF00007F80007F8000F
+F00003FE003FE00000FF80FF8000007FFFFF0000000FFFF800000001FFC0000029267DA530>I<
+01FC03F000FFFC0FFC00FFFC1FFF00FFFC3C3F80FFFC707F8007FCE0FFC003FCC0FFC003FD80FF
+C003FD80FFC003FF807F8003FF003F0003FF001E0003FF00000003FE00000003FE00000003FE00
+000003FE00000003FE00000003FE00000003FE00000003FE00000003FE00000003FE00000003FE
+00000003FE00000003FE00000003FE00000003FE00000003FE00000003FE00000003FE00000003
+FE00000003FE00000003FE000000FFFFFC0000FFFFFC0000FFFFFC0000FFFFFC000022267DA528
+>114 D<000F0000000F0000000F0000000F0000000F0000001F0000001F0000001F0000001F00
+00003F0000003F0000007F0000007F000000FF000001FF000003FF000007FF00001FFFFFF0FFFF
+FFF0FFFFFFF0FFFFFFF001FF000001FF000001FF000001FF000001FF000001FF000001FF000001
+FF000001FF000001FF000001FF000001FF000001FF000001FF000001FF000001FF000001FF0000
+01FF000001FF000001FF003C01FF003C01FF003C01FF003C01FF003C01FF003C01FF003C01FF00
+3C00FF007800FF8078007F80F0003FC1E0001FFFC0000FFF800001FE001E377EB626>116
+D<00FE00003F80FFFE003FFF80FFFE003FFF80FFFE003FFF80FFFE003FFF8007FE0001FF8003FE
+0000FF8003FE0000FF8003FE0000FF8003FE0000FF8003FE0000FF8003FE0000FF8003FE0000FF
+8003FE0000FF8003FE0000FF8003FE0000FF8003FE0000FF8003FE0000FF8003FE0000FF8003FE
+0000FF8003FE0000FF8003FE0000FF8003FE0000FF8003FE0000FF8003FE0000FF8003FE0000FF
+8003FE0000FF8003FE0000FF8003FE0001FF8003FE0001FF8003FE0003FF8001FE0003FF8001FE
+0006FF8000FF000CFFC0007F8078FFFE003FFFF0FFFE001FFFE0FFFE0003FF80FFFE2F267CA536
+>I E end
+%%EndProlog
+%%BeginSetup
+%%Feature: *Resolution 300dpi
+TeXDict begin
+
+%%EndSetup
+%%Page: 1 1
+1 0 bop 871 1042 a Fr(Amd)399 1229 y(The)33 b(4.4)g(BSD)f(Automoun)m(ter)592
+1415 y(Reference)h(Man)m(ual)702 1602 y Fq(Jan-Simon)24 b(P)n(endry)937
+1727 y Fp(and)767 1851 y Fq(Nic)n(k)f(Williams)719 2163 y Fo(Last)15
+b(up)q(dated)h(Marc)o(h)e(1991)510 2225 y(Do)q(cumen)o(tation)h(for)g(soft)o
+(w)o(are)e(revision)k(5.3)d(Alpha)p eop
+%%Page: 2 2
+2 1 bop 0 295 a Fo(Cop)o(yrigh)o(t)226 294 y(c)214 295 y Fn(\015)15
+b Fo(1989)f(Jan-Simon)j(P)o(endry)0 370 y(Cop)o(yrigh)o(t)226
+369 y(c)214 370 y Fn(\015)e Fo(1989)f(Imp)q(erial)j(College)f(of)f(Science,)i
+(T)l(ec)o(hnology)e(&)h(Medicine)0 445 y(Cop)o(yrigh)o(t)226
+444 y(c)214 445 y Fn(\015)f Fo(1989)f(The)i(Regen)o(ts)f(of)g(the)g(Univ)o
+(ersit)o(y)h(of)f(California.)0 582 y(All)h(Righ)o(ts)g(Reserv)o(ed.)0
+738 y(P)o(ermission)k(to)g(cop)o(y)f(this)i(do)q(cumen)o(t,)g(or)e(an)o(y)g
+(p)q(ortion)i(of)e(it,)i(as)e(necessary)h(for)g(use)g(of)f(this)h(soft)o(w)o
+(are)e(is)0 801 y(gran)o(ted)d(pro)o(vided)h(this)f(cop)o(yrigh)o(t)g(notice)
+h(and)f(statemen)o(t)f(of)h(p)q(ermission)i(are)e(included.)p
+eop
+%%Page: 1 3
+1 2 bop 0 -83 a Fo(Source)16 b(Distribution)1357 b(SMM:13-1)0
+158 y Fm(Preface)62 299 y Fo(This)11 b(man)o(ual)g(do)q(cumen)o(ts)g(the)f
+(use)h(of)f(the)h(4.4)f(BSD)g(automoun)o(ter|)p Fp(Amd)p Fo(.)18
+b(This)11 b(is)g(primarily)h(a)e(reference)0 349 y(man)o(ual.)20
+b(Unfortunately)l(,)15 b(no)g(tutorial)h(exists.)62 419 y(This)22
+b(man)o(ual)f(comes)f(in)i(t)o(w)o(o)e(forms:)30 b(the)21 b(published)i(form)
+d(and)h(the)g(Info)g(form.)36 b(The)21 b(Info)g(form)f(is)0
+469 y(for)f(on-line)i(p)q(erusal)f(with)g(the)g(INF)o(O)f(program)g(whic)o(h)
+h(is)g(distributed)h(along)e(with)h(GNU)f(Emacs.)32 b(Both)0
+519 y(forms)16 b(con)o(tain)h(substan)o(tially)h(the)f(same)f(text)h(and)g
+(are)f(generated)h(from)f(a)h(common)f(source)h(\014le,)h(whic)o(h)f(is)0
+569 y(distributed)g(with)e(the)h Fp(Amd)h Fo(source.)0 783
+y Fm(License)62 924 y Fp(Amd)f Fo(is)f(not)e(in)i(the)f(public)i(domain;)e
+(it)g(is)h(cop)o(yrigh)o(ted)f(and)g(there)g(are)f(restrictions)i(on)f(its)g
+(distribution.)62 994 y(Redistribution)k(and)d(use)g(in)h(source)f(and)g
+(binary)h(forms)e(are)g(p)q(ermitted)i(pro)o(vided)g(that:)j(\(1\))14
+b(source)h(dis-)0 1044 y(tributions)f(retain)g(this)g(en)o(tire)h(cop)o
+(yrigh)o(t)e(notice)h(and)g(commen)o(t,)f(and)h(\(2\))f(distributions)i
+(including)i(binaries)0 1094 y(displa)o(y)e(the)e(follo)o(wing)h(ac)o(kno)o
+(wledgemen)o(t:)19 b(\\This)14 b(pro)q(duct)g(includes)h(soft)o(w)o(are)d
+(dev)o(elop)q(ed)j(b)o(y)e(The)h(Univ)o(er-)0 1144 y(sit)o(y)f(of)g
+(California,)h(Berk)o(eley)g(and)f(its)h(Con)o(tributors")e(in)i(the)f(do)q
+(cumen)o(tation)g(or)g(other)g(materials)g(pro)o(vided)0 1193
+y(with)18 b(the)f(distribution)i(and)e(in)h(all)g(adv)o(ertising)g(materials)
+f(men)o(tioning)i(features)d(or)h(use)h(of)e(this)i(soft)o(w)o(are.)0
+1243 y(neither)h(the)e(name)h(of)f(the)g(Univ)o(ersit)o(y)i(nor)e(the)g
+(names)h(of)f(its)h(Con)o(tributors)f(ma)o(y)f(b)q(e)j(used)f(to)f(endorse)g
+(or)0 1293 y(promote)d(pro)q(ducts)i(deriv)o(ed)g(from)f(this)g(soft)o(w)o
+(are)f(without)h(sp)q(eci\014c)i(prior)e(written)g(p)q(ermission.)62
+1364 y(THIS)h(SOFTW)-5 b(ARE)15 b(IS)g(PR)o(O)o(VIDED)g(\\AS)g(IS")g(AND)g
+(WITHOUT)g(ANY)g(EXPRESS)h(OR)g(IMPLIED)0 1413 y(W)-5 b(ARRANTIES,)21
+b(INCLUDING,)e(WITHOUT)h(LIMIT)l(A)l(TION,)i(THE)d(IMPLIED)h(W)-5
+b(ARRANTIES)21 b(OF)0 1463 y(MER)o(CHANT)l(ABILITY)c(AND)e(FITNESS)g(F)o(OR)h
+(A)f(P)l(AR)l(TICULAR)i(PURPOSE.)0 1678 y Fm(Source)e(Distribution)62
+1818 y Fo(If)g(y)o(ou)f(ha)o(v)o(e)h(access)f(to)g(the)h(In)o(ternet,)g(y)o
+(ou)f(can)h(get)f(the)g(latest)h(distribution)h(v)o(ersion)f(of)f
+Fp(Amd)j Fo(from)c(host)0 1868 y(`)p Fl(usc.edu)p Fo(')g(using)i(anon)o
+(ymous)g(FTP)l(.)e(Mo)o(v)o(e)h(to)g(the)h(directory)g(`)p
+Fl(/pub/amd)p Fo(')e(on)h(that)g(host)h(and)g(fetc)o(h)f(the)h(\014le)0
+1918 y(`)p Fl(amd.tar.Z)p Fo('.)62 1988 y(If)23 b(y)o(ou)e(are)h(in)h(the)g
+(UK,)f(y)o(ou)g(can)g(get)g(the)g(latest)g(distribution)i(v)o(ersion)e(of)g
+Fp(Amd)i Fo(from)d(the)h(UKnet)0 2038 y(info-serv)o(er.)e(Start)14
+b(b)o(y)i(sending)g(email)g(to)f(`)p Fl(info-server@doc.ic.ac)o(.uk)p
+Fo('.)62 2109 y(Sites)g(on)f(the)g(UK)g(JANET)g(net)o(w)o(ork)f(can)h(get)g
+(the)g(latest)f(distribution)j(b)o(y)e(using)g(anon)o(ymous)g(NIFTP)g(to)0
+2159 y(fetc)o(h)h(the)g(\014le)i(`)p Fl(<AMD>amd.tar.Z)p Fo(')12
+b(from)j(host)f(`)p Fl(uk.ac.imperial.doc.src)p Fo('.)62 2229
+y(Revision)j(5.2)d(w)o(as)h(part)f(of)h(the)g(4.3)g(BSD)g(Reno)h
+(distribution.)62 2300 y(Revision)c(5.3bsdnet,)f(a)f(late)g(alpha)h(v)o
+(ersion)f(of)g(5.3,)g(w)o(as)g(part)f(of)h(the)g(BSD)h(net)o(w)o(ork)e(v)o
+(ersion)h(2)g(distribution)0 2504 y Fq(Bug)15 b(Rep)r(orts)62
+2595 y Fo(Send)i(all)f(bug)g(rep)q(orts)f(to)f(`)p Fl(jsp@doc.ic.ac.uk)p
+Fo(')f(quoting)j(the)f(details)i(of)e(the)g(release)h(and)g(y)o(our)f
+(con\014g-)0 2645 y(uration.)20 b(These)15 b(can)h(b)q(e)g(obtained)g(b)o(y)f
+(running)h(the)f(command)g(`)p Fl(amd)g(-v)p Fo('.)p eop
+%%Page: 2 4
+2 3 bop 15 -83 a Fo(SMM:13-2)912 b(4.4)15 b(BSD)g(Automoun)o(ter)f(Reference)
+j(Man)o(ual)0 158 y Fq(Mailing)g(List)62 250 y Fo(There)11
+b(is)g(a)f(mailing)i(list)f(for)f(p)q(eople)i(in)o(terested)f(in)h(k)o
+(eeping)f(upto)q(date)g(with)f(dev)o(elopmen)o(ts.)19 b(T)l(o)11
+b(subscrib)q(e,)0 299 y(send)16 b(a)f(note)g(to)f(`)p Fl
+(amd-workers-request@acl.l)o(anl.gov)o Fo('.)0 533 y Fm(In)n(tro)r(duction)62
+674 y Fo(An)e Fp(automoun)o(ter)i Fo(main)o(tains)e(a)f(cac)o(he)h(of)g(moun)
+o(ted)f(\014lesystems.)20 b(Filesystems)12 b(are)f(moun)o(ted)h(on)f(demand)0
+724 y(when)16 b(they)f(are)g(\014rst)g(referenced,)g(and)h(unmoun)o(ted)f
+(after)g(a)g(p)q(erio)q(d)h(of)f(inactivit)o(y)l(.)62 795 y
+Fp(Amd)k Fo(ma)o(y)d(b)q(e)h(used)g(as)g(a)f(replacemen)o(t)h(for)f(Sun's)h
+(automoun)o(ter.)23 b(The)17 b(c)o(hoice)h(of)e(whic)o(h)h(\014lesystem)h(to)
+0 844 y(moun)o(t)10 b(can)h(b)q(e)h(con)o(trolled)f(dynamically)i(with)e
+Fp(selectors)p Fo(.)19 b(Selectors)11 b(allo)o(w)g(decisions)h(of)f(the)g
+(form)f(\\hostname)0 894 y(is)15 b Fp(this)p Fo(,")f(or)f(\\arc)o(hitecture)h
+(is)h(not)e Fp(that)p Fo(.")19 b(Selectors)c(ma)o(y)e(b)q(e)i(com)o(bined)g
+(arbitrarily)l(.)20 b Fp(Amd)c Fo(also)e(supp)q(orts)g(a)0
+944 y(v)m(ariet)o(y)e(of)f(\014lesystem)i(t)o(yp)q(es,)f(including)i(NFS,)e
+(UFS)f(and)h(the)g(no)o(v)o(el)g Fp(program)f Fo(\014lesystem.)19
+b(The)12 b(com)o(bination)0 994 y(of)22 b(selectors)h(and)g(m)o(ultiple)h
+(\014lesystem)f(t)o(yp)q(es)g(allo)o(ws)g(iden)o(tical)h(con\014guration)f
+(\014les)g(to)f(b)q(e)h(used)h(on)e(all)0 1044 y(mac)o(hines)16
+b(so)f(reducing)h(the)g(administrativ)o(e)f(o)o(v)o(erhead.)62
+1114 y Fp(Amd)h Fo(ensures)f(that)f(it)g(will)i(not)e(hang)g(if)g(a)g(remote)
+g(serv)o(er)g(go)q(es)g(do)o(wn.)19 b(Moreo)o(v)o(er,)12 b
+Fp(Amd)17 b Fo(can)d(determine)0 1164 y(when)f(a)f(remote)g(serv)o(er)g(has)g
+(b)q(ecome)h(inaccessible)i(and)d(then)h(moun)o(t)f(replacemen)o(t)h
+(\014lesystems)g(as)f(and)g(when)0 1214 y(they)j(b)q(ecome)h(a)o(v)m
+(ailable.)62 1284 y Fp(Amd)h Fo(con)o(tains)f(no)f(proprietary)g(source)g(co)
+q(de)h(and)f(has)g(b)q(een)i(p)q(orted)e(to)g(n)o(umerous)g(\015a)o(v)o(ours)
+f(of)h(Unix.)0 1516 y Fm(1)41 b(Ov)n(erview)62 1658 y Fp(Amd)20
+b Fo(main)o(tains)e(a)g(cac)o(he)g(of)f(moun)o(ted)h(\014lesystems.)28
+b(Filesystems)18 b(are)f Fp(demand-moun)o(ted)k Fo(when)d(they)0
+1708 y(are)12 b(\014rst)g(referenced,)i(and)e(unmoun)o(ted)h(after)f(a)g(p)q
+(erio)q(d)i(of)e(inactivit)o(y)l(.)20 b Fp(Amd)14 b Fo(ma)o(y)e(b)q(e)h(used)
+g(as)f(a)g(replacemen)o(t)0 1757 y(for)17 b(Sun's)g Fk(automoun)o(t)p
+Fo(\(8\))f(program.)25 b(It)18 b(con)o(tains)f(no)g(proprietary)h(source)f
+(co)q(de)h(and)f(has)h(b)q(een)g(p)q(orted)g(to)0 1807 y(n)o(umerous)d(\015a)
+o(v)o(ours)f(of)h(Unix.)21 b(See)16 b(Section)g(2.1)e([Supp)q(orted)i(Op)q
+(erating)g(Systems],)e(page)30 b(SMM:13-6.)62 1878 y Fp(Amd)25
+b Fo(w)o(as)c(designed)j(as)e(the)h(basis)g(for)f(exp)q(erimen)o(ting)i(with)
+e(\014lesystem)i(la)o(y)o(out)d(and)i(managemen)o(t.)0 1928
+y(Although)15 b Fp(Amd)i Fo(has)e(man)o(y)g(direct)g(applications)i(it)e(is)g
+(loaded)h(with)f(additional)i(features)d(whic)o(h)i(ha)o(v)o(e)f(little)0
+1977 y(practical)h(use.)21 b(A)o(t)15 b(some)g(p)q(oin)o(t)h(the)g(infrequen)
+o(tly)h(used)f(comp)q(onen)o(ts)f(ma)o(y)g(b)q(e)h(remo)o(v)o(ed)f(to)g
+(streamline)h(the)0 2027 y(pro)q(duction)g(system.)0 2234 y
+Fq(1.1)33 b(F)-6 b(undamen)n(tals)62 2325 y Fo(The)15 b(fundamen)o(tal)g
+(concept)f(b)q(ehind)j Fp(Amd)f Fo(is)f(the)f(abilit)o(y)i(to)d(separate)h
+(the)g(name)h(used)g(to)e(refer)h(to)g(a)g(\014le)0 2375 y(from)d(the)g(name)
+g(used)h(to)f(refer)g(to)g(its)g(ph)o(ysical)i(storage)d(lo)q(cation.)19
+b(This)12 b(allo)o(ws)g(the)f(same)g(\014les)i(to)d(b)q(e)i(accessed)0
+2425 y(with)g(the)h(same)f(name)g(regardless)g(of)g(where)g(in)h(the)f(net)o
+(w)o(ork)g(the)g(name)g(is)h(used.)19 b(This)13 b(is)f(v)o(ery)g(di\013eren)o
+(t)h(from)0 2475 y(placing)i(`)p Fl(/n/hostname)p Fo(')10 b(in)k(fron)o(t)f
+(of)f(the)i(pathname)f(since)h(that)f(includes)i(lo)q(cation)f(dep)q(enden)o
+(t)h(information)0 2525 y(whic)o(h)h(ma)o(y)f(c)o(hange)g(if)g(\014les)i(are)
+e(mo)o(v)o(ed)f(to)h(another)g(mac)o(hine.)62 2595 y(By)i(placing)i(the)e
+(required)h(mappings)f(in)h(a)e(cen)o(trally)i(administered)g(database,)f
+(\014lesystems)g(can)g(b)q(e)h(re-)0 2645 y(organised)d(without)h(requiring)g
+(c)o(hanges)f(to)g(con\014guration)g(\014les,)h(shell)h(scripts)e(and)h(so)e
+(on.)p eop
+%%Page: 3 5
+3 4 bop 0 -83 a Fo(Chapter)15 b(1:)k(Ov)o(erview)1328 b(SMM:13-3)0
+158 y Fq(1.2)33 b(Filesystems)15 b(and)h(V)-6 b(olumes)62 250
+y Fp(Amd)19 b Fo(views)e(the)g(w)o(orld)g(as)g(a)f(set)h(of)f(\014leserv)o
+(ers,)i(eac)o(h)f(con)o(taing)g(one)g(or)f(more)g(\014lesystems)i(where)f
+(eac)o(h)0 299 y(\014lesystem)g(con)o(tains)f(one)h(or)f(more)g
+Fp(v)o(olumes)p Fo(.)23 b(Here)17 b(the)f(term)g Fp(v)o(olume)j
+Fo(is)e(used)g(to)f(refer)g(to)g(a)g(coheren)o(t)g(set)0 349
+y(of)f(\014les)h(suc)o(h)f(as)g(a)g(user's)g(home)g(directory)h(or)e(a)h(T)
+899 359 y(E)925 349 y(X)g(distribution.)62 420 y(In)i(order)e(to)h(access)g
+(the)g(con)o(ten)o(ts)f(of)g(a)h(v)o(olume,)g Fp(Amd)i Fo(m)o(ust)d(b)q(e)i
+(told)f(in)g(whic)o(h)h(\014lesystem)g(the)f(v)o(olume)0 470
+y(resides)j(and)g(whic)o(h)g(host)g(o)o(wns)e(the)i(\014lesystem.)30
+b(By)19 b(default)g(the)g(host)f(is)h(assumed)f(to)g(b)q(e)i(lo)q(cal)f(and)g
+(the)0 519 y(v)o(olume)c(is)f(assumed)g(to)g(b)q(e)h(the)f(en)o(tire)h
+(\014lesystem.)20 b(If)14 b(a)g(\014lesystem)h(con)o(tains)f(more)g(than)g
+(one)g(v)o(olume,)h(then)0 569 y(a)e Fp(sublink)18 b Fo(is)13
+b(used)h(to)f(refer)g(to)f(the)h(sub-directory)h(within)h(the)e(\014lesystem)
+h(where)f(the)g(v)o(olume)h(can)f(b)q(e)h(found.)0 722 y Fq(1.3)33
+b(V)-6 b(olume)15 b(Naming)62 814 y Fo(V)l(olume)23 b(names)g(are)f
+(de\014ned)h(to)f(b)q(e)h(unique)g(across)f(the)g(en)o(tire)h(net)o(w)o(ork.)
+40 b(A)22 b(v)o(olume)h(name)f(is)h(the)0 864 y(pathname)17
+b(to)g(the)h(v)o(olume's)f(ro)q(ot)g(as)g(kno)o(wn)g(b)o(y)h(the)g(users)f
+(of)g(that)g(v)o(olume.)27 b(Since)19 b(this)f(name)g(uniquely)0
+913 y(iden)o(ti\014es)g(the)e(v)o(olume)h(con)o(ten)o(ts,)e(all)i(v)o(olumes)
+g(can)f(b)q(e)h(named)f(and)h(accessed)f(from)g(eac)o(h)g(host,)g(sub)s(ject)
+g(to)0 963 y(administrativ)o(e)g(con)o(trols.)62 1034 y(V)l(olumes)k(ma)o(y)e
+(b)q(e)h(replicated)h(or)e(duplicated.)32 b(Replicated)21 b(v)o(olumes)e(con)
+o(tain)g(iden)o(tical)h(copies)g(of)e(the)0 1084 y(same)11
+b(data)g(and)g(reside)i(at)d(t)o(w)o(o)g(or)h(more)g(lo)q(cations)h(in)h(the)
+e(net)o(w)o(ork.)18 b(Eac)o(h)11 b(of)g(the)g(replicated)i(v)o(olumes)f(can)f
+(b)q(e)0 1133 y(used)16 b(in)o(terc)o(hangeably)l(.)22 b(Duplicated)17
+b(v)o(olumes)f(eac)o(h)f(ha)o(v)o(e)g(the)h(same)f(name)g(but)h(con)o(tain)g
+(di\013eren)o(t,)f(though)0 1183 y(functionally)21 b(iden)o(tical,)h(data.)32
+b(F)l(or)19 b(example,)i(`)p Fl(/vol/tex)p Fo(')d(migh)o(t)h(b)q(e)h(the)g
+(name)f(of)g(a)g(T)1638 1193 y(E)1664 1183 y(X)g(distribution)0
+1233 y(whic)o(h)d(v)m(aried)g(for)f(eac)o(h)g(mac)o(hine)h(arc)o(hitecture.)
+62 1304 y Fp(Amd)k Fo(pro)o(vides)e(facilities)h(to)e(tak)o(e)g(adv)m(an)o
+(tage)g(of)g(b)q(oth)g(replicated)i(and)f(duplicated)h(v)o(olumes.)27
+b(Con\014g-)0 1353 y(uration)18 b(options)g(allo)o(w)f(a)h(single)h(set)e(of)
+h(con\014guration)f(data)g(to)h(b)q(e)g(shared)g(across)f(an)g(en)o(tire)h
+(net)o(w)o(ork)f(b)o(y)0 1403 y(taking)e(adv)m(an)o(tage)g(of)g(replicated)h
+(and)g(duplicated)h(v)o(olumes.)62 1474 y Fp(Amd)d Fo(can)f(tak)o(e)e(adv)m
+(an)o(tage)h(of)f(replacemen)o(t)i(v)o(olumes)f(b)o(y)h(moun)o(ting)f(them)g
+(as)f(required)j(should)f(an)f(activ)o(e)0 1524 y(\014leserv)o(er)k(b)q
+(ecome)g(una)o(v)m(ailable.)0 1670 y Fq(1.4)33 b(V)-6 b(olume)15
+b(Binding)62 1761 y Fo(Unix)22 b(implemen)o(ts)h(a)d(namespace)i(of)e
+(hierarc)o(hically)k(moun)o(ted)d(\014lesystems.)38 b(Tw)o(o)20
+b(forms)g(of)h(binding)0 1811 y(b)q(et)o(w)o(een)15 b(names)g(and)g(\014les)g
+(are)g(pro)o(vided.)20 b(A)15 b Fp(hard)g(link)k Fo(completes)c(the)g
+(binding)h(when)g(the)e(name)h(is)g(added)0 1861 y(to)f(the)h(\014lesystem.)
+20 b(A)15 b Fp(soft)f(link)19 b Fo(dela)o(ys)c(the)g(binding)i(un)o(til)f
+(the)f(name)f(is)i(accessed.)k(An)15 b Fp(automoun)o(ter)i
+Fo(adds)0 1911 y(a)e(further)g(form)f(in)i(whic)o(h)g(the)g(binding)h(of)e
+(name)g(to)f(\014lesystem)i(is)g(dela)o(y)o(ed)g(un)o(til)g(the)f(name)h(is)f
+(accessed.)62 1981 y(The)h(target)f(v)o(olume,)g(in)i(its)f(general)g(form,)f
+(is)h(a)f(tuple)i(\(host,)d(\014lesystem,)i(sublink\))i(whic)o(h)e(can)g(b)q
+(e)g(used)0 2031 y(to)f(name)g(the)g(ph)o(ysical)i(lo)q(cation)f(of)e(an)o(y)
+h(v)o(olume)h(in)g(the)f(net)o(w)o(ork.)62 2102 y(When)22 b(a)f(target)f(is)i
+(referenced,)h Fp(Amd)g Fo(ignores)e(the)h(sublink)h(elemen)o(t)f(and)f
+(determines)h(whether)g(the)0 2152 y(required)c(\014lesystem)f(is)g(already)g
+(moun)o(ted.)25 b(This)17 b(is)g(done)g(b)o(y)g(computing)g(the)g(lo)q(cal)g
+(moun)o(t)g(p)q(oin)o(t)g(for)f(the)0 2201 y(\014lesystem)g(and)g(c)o(hec)o
+(king)h(for)e(an)h(existing)h(\014lesystem)f(moun)o(ted)g(at)f(the)h(same)g
+(place.)23 b(If)16 b(suc)o(h)g(a)f(\014lesystem)0 2251 y(already)i(exists)h
+(then)f(it)h(is)f(assumed)h(to)e(b)q(e)i(functionally)h(iden)o(tical)g(to)e
+(the)g(target)f(\014lesystem.)26 b(By)17 b(default)0 2301 y(there)d(is)g(a)f
+(one-to-one)g(mapping)i(b)q(et)o(w)o(een)e(the)h(pair)g(\(host,)f
+(\014lesystem\))g(and)h(the)g(lo)q(cal)g(moun)o(t)f(p)q(oin)o(t)h(so)g(this)0
+2351 y(assumption)h(is)h(v)m(alid.)0 2504 y Fq(1.5)33 b(Op)r(erational)15
+b(Principl)q(es)62 2595 y Fp(Amd)e Fo(op)q(erates)e(b)o(y)f(in)o(tro)q
+(ducing)j(new)e(moun)o(t)f(p)q(oin)o(ts)h(in)o(to)g(the)g(namespace.)19
+b(These)11 b(are)g(called)h Fp(automoun)o(t)0 2645 y Fo(p)q(oin)o(ts.)22
+b(The)16 b(k)o(ernel)h(sees)f(these)g(automoun)o(t)e(p)q(oin)o(ts)j(as)e(NFS)
+h(\014lesystems)g(b)q(eing)h(serv)o(ed)f(b)o(y)g Fp(Amd)p Fo(.)22
+b(Ha)o(ving)p eop
+%%Page: 4 6
+4 5 bop 15 -83 a Fo(SMM:13-4)912 b(4.4)15 b(BSD)g(Automoun)o(ter)f(Reference)
+j(Man)o(ual)0 158 y(attac)o(hed)f(itself)h(to)f(the)h(namespace,)g
+Fp(Amd)h Fo(is)f(no)o(w)f(able)i(to)e(con)o(trol)g(the)g(view)i(the)e(rest)g
+(of)g(the)h(system)f(has)0 208 y(of)f(those)g(moun)o(t)f(p)q(oin)o(ts.)21
+b(RPC)15 b(calls)h(are)f(receiv)o(ed)i(from)d(the)h(k)o(ernel)h(one)g(at)e(a)
+h(time.)62 279 y(When)k(a)e Fp(lo)q(okup)j Fo(call)f(is)g(receiv)o(ed)g
+Fp(Amd)h Fo(c)o(hec)o(ks)e(whether)g(the)g(name)g(is)h(already)f(kno)o(wn.)28
+b(If)18 b(it)g(is)h(not,)0 329 y(the)g(required)h(v)o(olume)f(is)g(moun)o
+(ted.)30 b(A)19 b(sym)o(b)q(olic)h(link)g(p)q(oin)o(ting)g(to)e(the)h(v)o
+(olume)g(ro)q(ot)f(is)h(then)g(returned.)0 378 y(Once)g(the)e(sym)o(b)q(olic)
+i(link)g(is)f(returned,)g(the)g(k)o(ernel)g(will)i(send)e(all)g(other)g
+(requests)f(direct)h(to)f(the)h(moun)o(ted)0 428 y(\014lesystem.)62
+499 y(If)d(a)f(v)o(olume)g(is)h(not)f(y)o(et)g(moun)o(ted,)g
+Fp(Amd)i Fo(consults)f(a)f(con\014guration)g Fp(moun)o(t-map)h
+Fo(corresp)q(onding)g(to)f(the)0 549 y(automoun)o(t)h(p)q(oin)o(t.)24
+b Fp(Amd)19 b Fo(then)e(mak)o(es)f(a)g(run)o(time)h(decision)h(on)e(what)g
+(and)h(where)f(to)g(moun)o(t)g(a)g(\014lesystem)0 598 y(based)g(on)f(the)g
+(information)g(obtained)h(from)f(the)g(map.)62 669 y Fp(Amd)21
+b Fo(do)q(es)e(not)g(implemen)o(t)h(all)g(the)f(NFS)g(requests;)h(only)g
+(those)e(relev)m(an)o(t)i(to)e(name)h(binding)i(suc)o(h)e(as)0
+719 y Fp(lo)q(okup)p Fo(,)g Fp(readlink)j Fo(and)c Fp(readdir)p
+Fo(.)28 b(Some)18 b(other)g(calls)g(are)g(also)g(implemen)o(ted)h(but)f(most)
+f(simply)i(return)f(an)0 769 y(error)c(co)q(de;)i(for)e(example)j
+Fp(mkdir)h Fo(alw)o(a)o(ys)d(returns)g(\\read-only)h(\014lesystem".)0
+933 y Fq(1.6)33 b(Moun)n(ting)16 b(a)f(V)-6 b(olume)62 1024
+y Fo(Eac)o(h)21 b(automoun)o(t)e(p)q(oin)o(t)i(has)g(a)f(corresp)q(onding)i
+(moun)o(t)e(map.)36 b(The)20 b(moun)o(t)h(map)f(con)o(tains)h(a)f(list)h(of)0
+1074 y(k)o(ey{v)m(alue)15 b(pairs.)20 b(The)15 b(k)o(ey)f(is)h(the)f(name)h
+(of)f(the)g(v)o(olume)h(to)e(b)q(e)j(moun)o(ted.)j(The)c(v)m(alue)g(is)g(a)f
+(list)h(of)f(lo)q(cations)0 1124 y(describing)i(where)d(the)h(\014lesystem)h
+(is)f(stored)f(in)i(the)e(net)o(w)o(ork.)19 b(In)14 b(the)g(source)g(for)f
+(the)g(map)h(the)g(v)m(alue)h(w)o(ould)0 1173 y(lo)q(ok)g(lik)o(e)120
+1244 y(lo)q(cation1)31 b(lo)q(cation2)g Fj(:)8 b(:)g(:)28 b
+Fo(lo)q(cationN)62 1335 y Fp(Amd)14 b Fo(examines)f(eac)o(h)f(lo)q(cation)g
+(in)h(turn.)19 b(Eac)o(h)12 b(lo)q(cation)g(ma)o(y)f(con)o(tain)h
+Fp(selectors)i Fo(whic)o(h)f(con)o(trol)f(whether)0 1385 y
+Fp(Amd)19 b Fo(can)e(use)h(that)e(lo)q(cation.)27 b(F)l(or)16
+b(example,)i(the)g(lo)q(cation)g(ma)o(y)e(b)q(e)i(restricted)f(to)g(use)g(b)o
+(y)g(certain)h(hosts.)0 1435 y(Those)d(lo)q(cations)h(whic)o(h)g(cannot)f(b)q
+(e)h(used)g(are)f(ignored.)62 1506 y Fp(Amd)23 b Fo(attempts)d(to)g(moun)o(t)
+g(the)h(\014lesystem)g(describ)q(ed)i(b)o(y)d(eac)o(h)h(remaining)h(lo)q
+(cation)f(un)o(til)h(a)e(moun)o(t)0 1555 y(succeeds)c(or)f
+Fp(Amd)i Fo(can)f(no)f(longer)g(pro)q(ceed.)21 b(The)15 b(latter)g(can)g(o)q
+(ccur)h(in)g(three)f(w)o(a)o(ys:)37 1626 y Fn(\017)30 b Fo(If)16
+b(none)h(of)e(the)i(lo)q(cations)f(could)i(b)q(e)e(used,)h(or)e(if)i(all)g
+(of)f(the)g(lo)q(cations)h(caused)f(an)g(error,)f(then)i(the)f(last)90
+1676 y(error)e(is)i(returned.)37 1738 y Fn(\017)30 b Fo(If)17
+b(a)f(lo)q(cation)i(could)f(b)q(e)g(used)h(but)e(w)o(as)g(b)q(eing)i(moun)o
+(ted)f(in)g(the)g(bac)o(kground)f(then)h Fp(Amd)i Fo(marks)d(that)90
+1788 y(moun)o(t)k(as)f(b)q(eing)j(\\in)f(progress")e(and)h(con)o(tin)o(ues)h
+(with)g(the)f(next)g(request;)j(no)d(reply)h(is)f(sen)o(t)g(to)g(the)90
+1838 y(k)o(ernel.)37 1900 y Fn(\017)30 b Fo(Lastly)l(,)23 b(one)e(or)g(more)f
+(of)h(the)g(moun)o(ts)g(ma)o(y)f(ha)o(v)o(e)h(b)q(een)h Fp(deferred)p
+Fo(.)38 b(A)21 b(moun)o(t)g(is)g(deferred)h(if)g(extra)90 1950
+y(information)11 b(is)h(required)g(b)q(efore)f(the)h(moun)o(t)e(can)h(pro)q
+(ceed.)20 b(When)11 b(the)g(information)h(b)q(ecomes)f(a)o(v)m(ailable)90
+1999 y(the)k(moun)o(t)e(will)k(tak)o(e)c(place,)i(but)g(in)g(the)g(mean)f
+(time)h(no)f(reply)i(is)e(sen)o(t)h(to)e(the)i(k)o(ernel.)20
+b(If)15 b(the)g(moun)o(t)e(is)90 2049 y(deferred,)i Fp(Amd)j
+Fo(con)o(tin)o(ues)d(to)g(try)f(an)o(y)h(remaining)i(lo)q(cations.)62
+2141 y(Once)g(a)d(v)o(olume)i(has)f(b)q(een)i(moun)o(ted,)e
+Fp(Amd)i Fo(establishes)f(a)f Fp(v)o(olume)h(mapping)k Fo(whic)o(h)c(is)g
+(used)g(to)e(satisfy)0 2190 y(subsequen)o(t)i(requests.)0 2355
+y Fq(1.7)33 b(Automatic)15 b(Unmoun)n(ting)62 2446 y Fo(T)l(o)e(a)o(v)o(oid)f
+(an)g(ev)o(er)h(increasing)h(n)o(um)o(b)q(er)e(of)h(\014lesystem)g(moun)o
+(ts,)f Fp(Amd)i Fo(remo)o(v)o(es)e(v)o(olume)h(mappings)g(whic)o(h)0
+2496 y(ha)o(v)o(e)19 b(not)g(b)q(een)h(used)g(recen)o(tly)l(.)33
+b(A)19 b(time-to-liv)o(e)i(in)o(terv)m(al)f(is)g(asso)q(ciated)f(with)h(eac)o
+(h)f(mapping)h(and)f(when)0 2545 y(that)g(expires)i(the)e(mapping)i(is)f
+(remo)o(v)o(ed.)32 b(When)20 b(the)g(last)g(reference)g(to)f(a)g
+(\014lesystem)i(is)f(remo)o(v)o(ed,)g(that)0 2595 y(\014lesystem)14
+b(is)h(unmoun)o(ted.)k(If)14 b(the)g(unmoun)o(t)g(fails,)g(for)f(example)i
+(the)f(\014lesystem)g(is)g(still)h(busy)l(,)f(the)g(mapping)0
+2645 y(is)k(re-instated)f(and)g(its)h(time-to-liv)o(e)g(in)o(terv)m(al)g(is)g
+(extended.)26 b(The)18 b(global)g(default)f(for)g(this)g(grace)g(p)q(erio)q
+(d)h(is)p eop
+%%Page: 5 7
+5 6 bop 0 -83 a Fo(Chapter)15 b(1:)k(Ov)o(erview)1328 b(SMM:13-5)0
+158 y(con)o(trolled)14 b(b)o(y)g(the)f(\\-w")g(command-line)i(option)f(\(see)
+f(Section)h(4.11)e([-w)h(Option],)h(page)27 b(SMM:13-19\).)17
+b(It)c(is)0 208 y(also)i(p)q(ossible)i(to)e(set)g(this)g(v)m(alue)i(on)e(a)g
+(p)q(er-moun)o(t)g(basis)h(\(see)f(Section)h(3.3.4.3)d([opts],)g(page)31
+b(SMM:13-15\).)62 279 y(Filesystems)20 b(can)g(b)q(e)f(forcefully)i(timed)f
+(out)f(using)h(the)f Fp(Amq)h Fo(command.)32 b(See)20 b(Chapter)f(6)g
+([Run-time)0 329 y(Administration],)d(page)30 b(SMM:13-27.)0
+513 y Fq(1.8)j(Keep-aliv)n(es)62 604 y Fo(Use)14 b(of)g(some)g(\014lesystem)g
+(t)o(yp)q(es)g(requires)h(the)f(presence)h(of)e(a)h(serv)o(er)g(on)f(another)
+h(mac)o(hine.)20 b(If)14 b(a)g(mac)o(hine)0 654 y(crashes)19
+b(then)g(it)g(is)h(of)e(no)h(concern)g(to)f(pro)q(cesses)i(on)e(that)h(mac)o
+(hine)g(that)f(the)h(\014lesystem)h(is)f(una)o(v)m(ailable.)0
+704 y(Ho)o(w)o(ev)o(er,)14 b(to)h(pro)q(cesses)h(on)f(a)h(remote)f(host)g
+(using)h(that)f(mac)o(hine)h(as)f(a)g(\014leserv)o(er)i(this)e(ev)o(en)o(t)h
+(is)g(imp)q(ortan)o(t.)0 753 y(This)h(situation)f(is)h(most)e(widely)i
+(recognised)g(when)g(an)f(NFS)g(serv)o(er)g(crashes)g(and)g(the)g(b)q(eha)o
+(viour)h(observ)o(ed)0 803 y(on)h(clien)o(t)i(mac)o(hines)f(is)g(that)f(more)
+g(and)h(more)f(pro)q(cesses)h(hang.)29 b(In)19 b(order)f(to)g(pro)o(vide)h
+(the)g(p)q(ossibilit)o(y)h(of)0 853 y(reco)o(v)o(ery)l(,)f
+Fp(Amd)h Fo(implemen)o(ts)g(a)e Fp(k)o(eep-aliv)o(e)23 b Fo(in)o(terv)m(al)c
+(timer)g(for)f(some)g(\014lesystem)i(t)o(yp)q(es.)30 b(Curren)o(tly)18
+b(only)0 903 y(NFS)d(mak)o(es)g(use)g(of)g(this)h(service.)62
+973 y(The)k(basis)h(of)e(the)h(NFS)g(k)o(eep-aliv)o(e)i(implemen)o(tation)f
+(is)f(the)h(observ)m(ation)f(that)f(most)g(sites)h(main)o(tain)0
+1023 y(replicated)j(copies)e(of)g(common)g(system)f(data)h(suc)o(h)g(as)g
+(man)o(ual)g(pages,)h(most)e(or)h(all)g(programs,)g(system)0
+1073 y(source)c(co)q(de)g(and)g(so)f(on.)24 b(If)17 b(one)g(of)f(those)h
+(serv)o(ers)f(go)q(es)h(do)o(wn)f(it)h(w)o(ould)g(b)q(e)g(reasonable)g(to)f
+(moun)o(t)h(one)f(of)0 1123 y(the)f(others)g(as)g(a)g(replacemen)o(t.)62
+1193 y(The)j(\014rst)g(part)f(of)h(the)g(pro)q(cess)g(is)h(to)e(k)o(eep)h
+(trac)o(k)f(of)h(whic)o(h)g(\014leserv)o(ers)h(are)f(up)g(and)g(whic)o(h)h
+(are)f(do)o(wn.)0 1243 y Fp(Amd)j Fo(do)q(es)e(this)g(b)o(y)g(sending)h(RPC)f
+(requests)f(to)h(the)f(serv)o(ers')g(NFS)h Fl(NullProc)f Fo(and)h(c)o(hec)o
+(king)g(whether)g(a)0 1293 y(reply)13 b(is)f(returned.)19 b(While)13
+b(the)f(serv)o(er)f(state)g(is)h(uncertain)h(the)f(requests)g(are)f
+(re-transmitted)h(at)f(three)g(second)0 1343 y(in)o(terv)m(als)19
+b(and)g(if)f(no)h(reply)g(is)f(receiv)o(ed)i(after)d(four)h(attempts)f(the)i
+(serv)o(er)f(is)g(mark)o(ed)g(do)o(wn.)29 b(If)18 b(a)g(reply)h(is)0
+1393 y(receiv)o(ed)f(the)g(\014leserv)o(er)f(is)h(mark)o(ed)f(up)g(and)h(sta)
+o(ys)e(in)i(that)e(state)h(for)f(30)h(seconds)g(at)g(whic)o(h)h(time)f
+(another)0 1442 y(NFS)e(ping)h(is)g(sen)o(t.)62 1513 y(Once)21
+b(a)f(\014leserv)o(er)g(is)h(mark)o(ed)e(do)o(wn,)h(requests)g(con)o(tin)o
+(ue)h(to)e(b)q(e)h(sen)o(t)g(ev)o(ery)g(30)f(seconds)h(in)h(order)f(to)0
+1563 y(determine)c(when)g(the)f(\014leserv)o(er)h(comes)f(bac)o(k)g(up.)21
+b(During)15 b(this)h(time)f(an)o(y)g(reference)h(through)f
+Fp(Amd)i Fo(to)e(the)0 1613 y(\014lesystems)h(on)f(that)g(serv)o(er)g(fail)h
+(with)g(the)f(error)g(\\Op)q(eration)h(w)o(ould)g(blo)q(c)o(k".)21
+b(If)15 b(a)h(replacemen)o(t)g(v)o(olume)f(is)0 1662 y(a)o(v)m(ailable)i
+(then)e(it)h(will)h(b)q(e)f(moun)o(ted,)e(otherwise)i(the)f(error)g(is)g
+(returned)h(to)e(the)i(user.)62 1733 y(Although)g(this)g(action)g(do)q(es)g
+(not)f(protect)g(user)g(\014les,)h(whic)o(h)h(are)e(unique)i(on)e(the)h(net)o
+(w)o(ork,)e(or)h(pro)q(cesses)0 1783 y(whic)o(h)g(do)g(not)f(access)h
+(\014les)g(via)g Fp(Amd)h Fo(or)e(already)h(ha)o(v)o(e)f(op)q(en)i(\014les)f
+(on)f(the)h(h)o(ung)g(\014lesystem,)g(it)f(can)h(prev)o(en)o(t)0
+1833 y(most)f(new)i(pro)q(cesses)f(from)g(hanging.)62 1903
+y(By)k(default,)g(\014leserv)o(er)h(state)d(is)i(not)f(main)o(tained)i(for)e
+(NFS/TCP)f(moun)o(ts.)30 b(The)18 b(remote)g(\014leserv)o(er)h(is)0
+1953 y(alw)o(a)o(ys)14 b(assumed)i(to)e(b)q(e)i(up.)0 2135
+y Fq(1.9)33 b(Non-blo)r(c)n(king)16 b(Op)r(eration)62 2226
+y Fo(Since)e(there)f(is)g(only)g(one)g(instance)g(of)f Fp(Amd)j
+Fo(for)d(eac)o(h)g(automoun)o(t)g(p)q(oin)o(t,)h(and)f(usually)i(only)f(one)g
+(instance)0 2276 y(on)18 b(eac)o(h)g(mac)o(hine,)h(it)g(is)f(imp)q(ortan)o(t)
+g(that)f(it)h(is)h(alw)o(a)o(ys)e(a)o(v)m(ailable)j(to)d(service)i(k)o(ernel)
+g(calls.)30 b Fp(Amd)20 b Fo(go)q(es)e(to)0 2325 y(great)12
+b(lengths)h(to)g(ensure)g(that)f(it)h(do)q(es)g(not)g(blo)q(c)o(k)g(in)h(a)f
+(system)f(call.)20 b(As)13 b(a)g(last)f(resort)g Fp(Amd)j Fo(will)f(fork)f(b)
+q(efore)0 2375 y(it)j(attempts)e(a)i(system)f(call)i(that)d(ma)o(y)h(blo)q(c)
+o(k)i(inde\014nitely)l(,)h(suc)o(h)e(as)f(moun)o(ting)h(an)f(NFS)h
+(\014lesystem.)22 b(Other)0 2425 y(tasks)12 b(suc)o(h)g(as)h(obtaining)g
+(\014lehandle)i(information)d(for)g(an)g(NFS)h(\014lesystem,)g(are)f(done)h
+(using)g(a)f(purp)q(ose)i(built)0 2475 y(non-blo)q(c)o(king)i(RPC)f(library)g
+(whic)o(h)g(is)g(in)o(tegrated)f(with)h Fp(Amd)r Fo('s)f(task)g(sc)o
+(heduler.)21 b(This)15 b(library)g(is)g(also)f(used)0 2525
+y(to)h(implemen)o(t)h(NFS)f(k)o(eep-aliv)o(es)i(\(see)e(Section)h(1.8)e
+([Keep-aliv)o(es],)i(page)30 b(SMM:13-5\).)62 2595 y(Whenev)o(er)11
+b(a)f(moun)o(t)g(is)g(deferred)h(or)f(bac)o(kgrounded,)h Fp(Amd)i
+Fo(m)o(ust)c(w)o(ait)h(for)g(it)g(to)g(complete)h(b)q(efore)g(replying)0
+2645 y(to)f(the)i(k)o(ernel.)19 b(Ho)o(w)o(ev)o(er,)10 b(this)i(w)o(ould)f
+(cause)h Fp(Amd)h Fo(to)d(blo)q(c)o(k)i(w)o(aiting)f(for)f(a)h(reply)h(to)f
+(b)q(e)g(constructed.)19 b(Rather)p eop
+%%Page: 6 8
+6 7 bop 15 -83 a Fo(SMM:13-6)912 b(4.4)15 b(BSD)g(Automoun)o(ter)f(Reference)
+j(Man)o(ual)0 158 y(than)h(do)g(this,)h Fp(Amd)h Fo(simply)f
+Fp(drops)h Fo(the)e(call)h(under)g(the)f(assumption)g(that)f(the)h(k)o(ernel)
+h(RPC)f(mec)o(hanism)0 208 y(will)f(automatically)e(retry)g(the)g(request.)0
+445 y Fm(2)41 b(Supp)r(orted)15 b(Platforms)62 590 y Fp(Amd)20
+b Fo(has)e(b)q(een)g(p)q(orted)g(to)f(a)h(wide)h(v)m(ariet)o(y)e(of)h(mac)o
+(hines)g(and)g(op)q(erating)g(systems.)27 b(The)18 b(table)g(b)q(elo)o(w)0
+640 y(lists)e(those)f(platforms)g(supp)q(orted)g(b)o(y)g(the)h(curren)o(t)f
+(release.)0 852 y Fq(2.1)33 b(Supp)r(orted)16 b(Op)r(erating)g(Systems)62
+943 y Fo(The)j(follo)o(wing)g(op)q(erating)f(systems)g(are)g(curren)o(tly)g
+(supp)q(orted)h(b)o(y)f Fp(Amd)p Fo(.)30 b Fp(Amd)r Fo('s)17
+b(con)o(v)o(en)o(tional)i(name)0 993 y(for)c(eac)o(h)g(system)g(is)g(giv)o
+(en.)0 1064 y Fl(acis43)96 b Fo(4.3)14 b(BSD)i(for)e(IBM)i(R)l(T.)f(Con)o
+(tributed)g(b)o(y)g(Jan-Simon)i(P)o(endry)e Fl(<jsp@doc.ic.ac.uk>)0
+1132 y(aix3)144 b Fo(AIX)16 b(3.1.)j(Con)o(tributed)c(b)o(y)g(Jan-Simon)i(P)o
+(endry)e Fl(<jsp@doc.ic.ac.uk>)0 1200 y(aux)168 b Fo(System)15
+b(V)g(for)g(Mac-I)q(I.)h(Con)o(tributed)f(b)o(y)g(Julian)i(Onions)f
+Fl(<jpo@cs.nott.ac.uk>)0 1269 y(bsd44)120 b Fo(4.4)14 b(BSD.)h(Con)o
+(tributed)h(b)o(y)f(Jan-Simon)h(P)o(endry)f Fl(<jsp@doc.ic.ac.uk>)0
+1337 y(concentrix)240 1405 y Fo(Concen)o(trix)g(5.0.)k(Con)o(tributed)d(b)o
+(y)f(Sjo)q(erd)g(Mullender)j Fl(<sjoerd@cwi.nl>)0 1473 y(convex)96
+b Fo(Con)o(v)o(ex)15 b(OS)g(7.1.)k(Con)o(tributed)d(b)o(y)f(Eitan)g
+(Mizrotsky)g Fl(<eitan@shumuji.ac.il>)0 1542 y(dgux)144 b Fo(Data)14
+b(General)i(DG/UX.)e(Con)o(tributed)h(b)o(y)g(Mark)g(Da)o(vies)g
+Fl(<mark@comp.vuw.ac.nz>)0 1610 y(fpx4)144 b Fo(Celerit)o(y)16
+b(FPX)f(4.1/2.)j(Con)o(tributed)d(b)o(y)h(Stephen)g(P)o(op)q(e)f
+Fl(<scp@grizzly.acl.lanl.gov>)0 1678 y(hcx)168 b Fo(Harris)15
+b(HCX/UX.)g(Con)o(tributed)g(b)o(y)g(Chris)h(Metcalf)f Fl
+(<metcalf@masala.lcs.mit.edu)o(>)0 1746 y(hlh42)120 b Fo(HLH)16
+b(OTS)f(1.)p Fp(x)j Fo(\(4.2)c(BSD\).)g(Con)o(tributed)i(b)o(y)f(Jan-Simon)h
+(P)o(endry)g Fl(<jsp@doc.ic.ac.uk>)0 1815 y(hpux)144 b Fo(HP-UX)16
+b(6.)p Fp(x)h Fo(or)e(7.0.)k(Con)o(tributed)c(b)o(y)g(Jan-Simon)i(P)o(endry)e
+Fl(<jsp@doc.ic.ac.uk>)0 1883 y(irix)144 b Fo(SGI)15 b(Irix.)21
+b(Con)o(tributed)16 b(b)o(y)f(Scott)f(R.)i(Presnell)g Fl(<srp@cgl.ucsf.edu>)0
+1951 y(next)144 b Fo(Mac)o(h)15 b(for)f(NeXT.)h(Con)o(tributed)h(b)o(y)f
+(Bill)i(T)l(rost)d Fl(<trost\045reed@cse.ogi.edu>)0 2019 y(pyrOSx)96
+b Fo(Pyramid)15 b(OSx.)21 b(Con)o(tributed)15 b(b)o(y)g(Stefan)h(P)o(etri)f
+Fl(<petri@tubsibr.UUCP>)0 2088 y(riscix)96 b Fo(Acorn)15 b(RISC)h(iX.)g(Con)o
+(tributed)f(b)o(y)g(Piete)h(Bro)q(oks)f Fl(<pb@cam.cl.ac.uk>)0
+2156 y(sos3)144 b Fo(SunOS)17 b(3.4)d(&)h(3.5.)k(Con)o(tributed)d(b)o(y)f
+(Jan-Simon)h(P)o(endry)f Fl(<jsp@doc.ic.ac.uk>)0 2224 y(sos4)144
+b Fo(SunOS)17 b(4.)p Fp(x)p Fo(.)i(Con)o(tributed)c(b)o(y)g(Jan-Simon)i(P)o
+(endry)e Fl(<jsp@doc.ic.ac.uk>)0 2292 y(u2_2)144 b Fo(Ultrix)16
+b(2.2.)j(Con)o(tributed)c(b)o(y)g(Piete)h(Bro)q(oks)f Fl(<pb@cam.cl.ac.uk>)0
+2361 y(u3_0)144 b Fo(Ultrix)16 b(3.)k(Con)o(tributed)15 b(b)o(y)g(Piete)h
+(Bro)q(oks)f Fl(<pb@cam.cl.ac.uk>)0 2429 y(u4_0)144 b Fo(Ultrix)16
+b(4.0.)j(Con)o(tributed)c(b)o(y)g(Chris)h(Lindblad)i Fl(<cjl@ai.mit.edu>)0
+2497 y(umax43)96 b Fo(Umax)15 b(4.3)f(BSD.)h(Con)o(tributed)g(b)o(y)h(Sjo)q
+(erd)f(Mullender)i Fl(<sjoerd@cwi.nl>)0 2565 y(utek)144 b Fo(Utek)15
+b(4.0.)k(Con)o(tributed)c(b)o(y)h(Bill)h(T)l(rost)d Fl
+(<trost\045reed@cse.ogi.edu>)0 2634 y(xinu43)96 b Fo(m)o(t)14
+b(Xin)o(u)j(MORE/bsd.)j(Con)o(tributed)15 b(b)o(y)h(Jan-Simon)g(P)o(endry)f
+Fl(<jsp@doc.ic.ac.uk>)p eop
+%%Page: 7 9
+7 8 bop 0 -83 a Fo(Chapter)15 b(3:)k(Moun)o(t)c(Maps)1258 b(SMM:13-7)0
+158 y Fq(2.2)33 b(Supp)r(orted)16 b(Mac)n(hine)g(Arc)n(hitectures)0
+250 y Fl(alliant)72 b Fo(Allian)o(t)17 b(FX/4)0 313 y Fl(arm)168
+b Fo(Acorn)15 b(ARM)0 377 y Fl(aviion)96 b Fo(Data)14 b(General)i(A)-5
+b(ViiON)0 440 y Fl(encore)96 b Fo(Encore)0 504 y Fl(fps500)g
+Fo(FPS)15 b(Mo)q(del)h(500)0 567 y Fl(hp9000)96 b Fo(HP)15
+b(9000/300)e(family)0 631 y Fl(hp9k8)120 b Fo(HP)15 b(9000/800)e(family)0
+694 y Fl(ibm032)96 b Fo(IBM)15 b(R)l(T)0 758 y Fl(ibm6000)72
+b Fo(IBM)15 b(RISC)i(System/6000)0 821 y Fl(iris4d)96 b Fo(SGI)15
+b(Iris)h(4D)0 885 y Fl(macII)120 b Fo(Apple)17 b(Mac)d(I)q(I)0
+948 y Fl(mips)144 b Fo(MIPS)15 b(RISC)0 1012 y Fl(multimax)48
+b Fo(Encore)15 b(Multimax)0 1075 y Fl(orion105)48 b Fo(HLH)16
+b(Orion)g(1/05)0 1139 y Fl(sun3)144 b Fo(Sun-3)16 b(family)0
+1202 y Fl(sun4)144 b Fo(Sun-4)16 b(family)0 1266 y Fl(tahoe)120
+b Fo(T)l(aho)q(e)15 b(family)0 1329 y Fl(vax)168 b Fo(DEC)15
+b(V)l(ax)0 1513 y Fm(3)41 b(Moun)n(t)15 b(Maps)62 1639 y Fp(Amd)k
+Fo(has)f(no)f(built-in)i(kno)o(wledge)f(of)f(mac)o(hines)h(or)e
+(\014lesystems.)27 b(External)17 b Fp(moun)o(t-maps)i Fo(are)e(used)h(to)0
+1688 y(pro)o(vide)12 b(the)g(required)h(information.)19 b(Sp)q(eci\014cally)l
+(,)c Fp(Amd)f Fo(needs)e(to)f(kno)o(w)g(when)h(and)g(under)h(what)e
+(conditions)0 1738 y(it)k(should)i(moun)o(t)d(\014lesystems.)62
+1809 y(The)k(map)e(en)o(try)h(corresp)q(onding)h(to)e(the)h(requested)h(name)
+f(con)o(tains)g(a)g(list)h(of)e(p)q(ossible)j(lo)q(cations)f(from)0
+1859 y(whic)o(h)e(to)f(resolv)o(e)h(the)f(request.)21 b(Eac)o(h)15
+b(lo)q(cation)i(sp)q(eci\014es)g(\014lesystem)f(t)o(yp)q(e,)f(information)h
+(required)g(b)o(y)g(that)0 1908 y(\014lesystem)11 b(\(for)f(example)h(the)g
+(blo)q(c)o(k)g(sp)q(ecial)h(device)g(in)g(the)e(case)h(of)f(UFS\),)g(and)g
+(some)g(information)h(describing)0 1958 y(where)k(to)f(moun)o(t)h(the)g
+(\014lesystem)g(\(see)g(Section)h(3.3.4.2)c([fs)j(Option],)g(page)29
+b(SMM:13-14\).)18 b(A)d(lo)q(cation)h(ma)o(y)0 2008 y(also)f(con)o(tain)h
+Fp(selectors)h Fo(\(see)e(Section)h(3.3.3)e([Selectors],)g(page)31
+b(SMM:13-13\).)0 2184 y Fq(3.1)i(Map)15 b(T)n(yp)r(es)62 2276
+y Fo(A)k(moun)o(t-map)f(pro)o(vides)i(the)f(run-time)g(con\014guration)g
+(information)g(to)f Fp(Amd)p Fo(.)31 b(Maps)19 b(can)g(b)q(e)g(imple-)0
+2325 y(men)o(ted)f(in)h(man)o(y)f(w)o(a)o(ys.)27 b(Some)18
+b(of)g(the)g(forms)f(supp)q(orted)i(b)o(y)f Fp(Amd)i Fo(are)e(regular)g
+(\014les,)h(ndbm)g(databases,)0 2375 y(NIS)d(maps)f(the)g Fp(Hesio)q(d)j
+Fo(name)d(serv)o(er)g(and)h(ev)o(en)f(the)g(passw)o(ord)g(\014le.)62
+2446 y(A)g(moun)o(t-map)f Fp(name)k Fo(is)d(a)f(sequence)i(of)f(c)o
+(haracters.)k(When)c(an)f(automoun)o(t)g(p)q(oin)o(t)h(is)g(created)g(a)g
+(handle)0 2496 y(on)j(the)h(moun)o(t-map)f(is)g(obtained.)31
+b(F)l(or)17 b(eac)o(h)i(map)f(t)o(yp)q(e)g(con\014gured)h Fp(Amd)i
+Fo(attempts)c(to)h(reference)h(the)f(a)0 2545 y(map)g(of)g(the)h(appropriate)
+f(t)o(yp)q(e.)29 b(If)19 b(a)f(map)g(is)h(found,)g Fp(Amd)i
+Fo(notes)d(the)g(t)o(yp)q(e)h(for)e(future)i(use)f(and)h(deletes)0
+2595 y(the)14 b(reference,)g(for)f(example)i(closing)g(an)o(y)e(op)q(en)h
+(\014le)h(descriptors.)20 b(The)14 b(a)o(v)m(ailable)h(maps)f(are)f
+(con\014gure)h(when)0 2645 y Fp(Amd)j Fo(is)f(built)g(and)g(can)f(b)q(e)h
+(displa)o(y)o(ed)h(b)o(y)e(running)h(the)f(command)g(`)p Fl(amd)g(-v)p
+Fo('.)p eop
+%%Page: 8 10
+8 9 bop 15 -83 a Fo(SMM:13-8)912 b(4.4)15 b(BSD)g(Automoun)o(ter)f(Reference)
+j(Man)o(ual)62 158 y(By)i(default,)g Fp(Amd)h Fo(cac)o(hes)f(data)f(in)h(a)f
+(mo)q(de)g(dep)q(enden)o(t)i(on)e(the)h(t)o(yp)q(e)f(of)g(map.)29
+b(This)19 b(is)g(the)f(same)g(as)0 208 y(sp)q(ecifying)g(`)p
+Fl(cache:=mapdefault)p Fo(')13 b(and)k(selects)f(a)g(suitable)i(default)e
+(cac)o(he)h(mo)q(de)f(dep)q(ending)j(on)d(the)g(map)0 258 y(t)o(yp)q(e.)j
+(The)12 b(individual)k(defaults)c(are)g(describ)q(ed)i(b)q(elo)o(w.)20
+b(The)12 b Fp(cac)o(he)j Fo(option)e(can)f(b)q(e)h(sp)q(eci\014ed)h(on)e
+(automoun)o(t)0 308 y(p)q(oin)o(ts)j(to)f(alter)g(the)h(cac)o(hing)g(b)q(eha)
+o(viour)g(\(see)g(Section)g(5.8)f([Automoun)o(t)f(Filesystem],)i(page)28
+b(SMM:13-24\).)62 378 y(The)13 b(follo)o(wing)f(map)g(t)o(yp)q(es)g(ha)o(v)o
+(e)g(b)q(een)h(implemen)o(ted,)h(though)e(some)g(are)f(not)h(a)o(v)m(ailable)
+i(on)e(all)h(mac)o(hines.)0 428 y(Run)j(the)f(command)g(`)p
+Fl(amd)g(-v)p Fo(')f(to)h(obtain)g(a)g(list)h(of)f(map)g(t)o(yp)q(es)g
+(con\014gured)h(on)f(y)o(our)g(mac)o(hine.)0 582 y Fi(3.1.1)30
+b(File)15 b(maps)62 673 y Fo(When)i Fp(Amd)i Fo(searc)o(hes)e(a)f(\014le)i
+(for)e(a)g(map)g(en)o(try)h(it)g(do)q(es)f(a)h(simple)h(scan)f(of)f(the)h
+(\014le)g(and)g(supp)q(orts)g(b)q(oth)0 723 y(commen)o(ts)e(and)g(con)o(tin)o
+(uation)h(lines.)62 794 y(Con)o(tin)o(uation)g(lines)h(are)e(indicated)i(b)o
+(y)e(a)g(bac)o(kslash)h(c)o(haracter)e(\(`)p Fl(\\)p Fo('\))g(as)h(the)g
+(last)h(c)o(haracter)e(of)h(a)g(line)i(in)0 843 y(the)h(\014le.)30
+b(The)18 b(bac)o(kslash,)h(newline)h(c)o(haracter)e Fp(and)g(an)o(y)g
+(leading)i(white)e(space)h(on)f(the)g(follo)o(wing)h(line)j
+Fo(are)0 893 y(discarded.)g(A)16 b(maxim)o(um)g(line)h(length)f(of)g(2047)e
+(c)o(haracters)h(is)h(enforced)g(after)f(con)o(tin)o(uation)h(lines)i(are)d
+(read)0 943 y(but)h(b)q(efore)h(commen)o(ts)f(are)g(stripp)q(ed.)24
+b(Eac)o(h)16 b(line)i(m)o(ust)e(end)h(with)g(a)f(newline)i(c)o(haracter;)e
+(that)f(is)i(newlines)0 993 y(are)e(terminators,)f(not)h(separators.)j(The)e
+(follo)o(wing)g(examples)g(illustrate)g(this:)120 1063 y Fl(key)119
+b(valA)71 b(valB;)g(\\)359 1113 y(valC)62 1205 y Fo(sp)q(eci\014es)17
+b Fp(three)h Fo(lo)q(cations,)e(and)f(is)h(iden)o(tical)h(to)120
+1275 y Fl(key)119 b(valA)71 b(valB;)g(valC)62 1366 y Fo(Ho)o(w)o(ev)o(er,)120
+1437 y Fl(key)119 b(valA)71 b(valB;\\)359 1487 y(valC)62 1578
+y Fo(sp)q(eci\014es)17 b(only)f Fp(t)o(w)o(o)g Fo(lo)q(cations,)g(and)f(is)h
+(iden)o(tical)h(to)120 1649 y Fl(key)119 b(valA)71 b(valB;valC)62
+1740 y Fo(After)20 b(a)h(complete)g(line)h(has)f(b)q(een)g(read)g(from)f(the)
+h(\014le,)h(including)h(con)o(tin)o(uations,)f Fp(Amd)h Fo(determines)0
+1790 y(whether)d(there)g(is)h(a)e(commen)o(t)h(on)f(the)i(line.)35
+b(A)20 b(commen)o(t)g(b)q(egins)h(with)f(a)g(hash)g(\(\\`)p
+Fl(#)p Fo('"\))e(c)o(haracter)h(and)0 1840 y(con)o(tin)o(ues)12
+b(to)g(the)f(end)i(of)e(the)h(line.)21 b(There)12 b(is)g(no)g(w)o(a)o(y)e(to)
+i(escap)q(e)g(or)f(c)o(hange)h(the)g(commen)o(t)f(lead-in)j(c)o(haracter.)62
+1910 y(Note)f(that)g(con)o(tin)o(uation)h(lines)h(and)f(commen)o(t)f(supp)q
+(ort)g Fp(only)18 b Fo(apply)d(to)e(\014le)h(maps,)f(or)g(ndbm)h(maps)g
+(built)0 1960 y(with)i(the)f Fl(mk-amd-map)f Fo(program.)62
+2031 y(When)e(cac)o(hing)f(is)h(enabled,)h(\014le)f(maps)e(ha)o(v)o(e)h(a)f
+(default)i(cac)o(he)f(mo)q(de)g(of)g Fl(all)f Fo(\(see)h(Section)h(5.8)e
+([Automoun)o(t)0 2080 y(Filesystem],)15 b(page)30 b(SMM:13-24\).)0
+2234 y Fi(3.1.2)g(ndbm)15 b(maps)62 2325 y Fo(An)20 b(ndbm)f(map)g(ma)o(y)g
+(b)q(e)h(used)f(as)g(a)g(fast)f(access)h(form)g(of)f(a)h(\014le)h(map.)32
+b(The)19 b(program,)g Fl(mk-amd-map)p Fo(,)0 2375 y(con)o(v)o(erts)c(a)g
+(normal)g(map)g(\014le)h(in)o(to)g(an)f(ndbm)h(database.)k(This)c(program)e
+(supp)q(orts)h(the)h(same)f(con)o(tin)o(uation)0 2425 y(and)f(commen)o(t)f
+(con)o(v)o(en)o(tions)h(that)f(are)g(pro)o(vided)i(for)e(\014le)h(maps.)19
+b(Note)14 b(that)f(ndbm)h(format)e(\014les)j(ma)o(y)e Fp(not)h
+Fo(b)q(e)0 2475 y(sharable)h(across)f(mac)o(hine)i(arc)o(hitectures.)j(The)c
+(notion)g(of)f(sp)q(eed)i(generally)g(only)f(applies)h(to)e(large)h(maps;)f
+(a)0 2525 y(small)i(map,)f(less)g(than)g(a)g(single)i(disk)f(blo)q(c)o(k,)f
+(is)h(almost)f(certainly)h(b)q(etter)f(implemen)o(ted)i(as)e(a)g(\014le)h
+(map.)62 2595 y(ndbm)e(maps)e(do)h(not)f(supp)q(ort)h(cac)o(he)g(mo)q(de)f(`)
+p Fl(all)p Fo(')g(and,)h(when)g(cac)o(hing)g(is)h(enabled,)g(ha)o(v)o(e)e(a)g
+(default)i(cac)o(he)0 2645 y(mo)q(de)h(of)g(`)p Fl(inc)p Fo(')f(\(see)h
+(Section)h(5.8)f([Automoun)o(t)f(Filesystem],)h(page)30 b(SMM:13-24\).)p
+eop
+%%Page: 9 11
+9 10 bop 0 -83 a Fo(Chapter)15 b(3:)k(Moun)o(t)c(Maps)1258
+b(SMM:13-9)0 158 y Fi(3.1.3)30 b(NIS)15 b(maps)62 250 y Fo(When)j(using)h
+(NIS)f(\(formerly)f(YP\),)g(an)g Fp(Amd)j Fo(map)d(is)h(implemen)o(ted)i
+(directly)f(b)o(y)e(the)h(underlying)h(NIS)0 299 y(map.)f(Commen)o(ts)11
+b(and)h(con)o(tin)o(uation)g(lines)h(are)f Fp(not)g Fo(supp)q(orted)g(in)g
+(the)g(automoun)o(ter)f(and)g(m)o(ust)g(b)q(e)i(stripp)q(ed)0
+349 y(when)j(constructing)f(the)g(NIS)h(serv)o(er's)f(database.)62
+420 y(NIS)j(maps)e(do)h(not)f(supp)q(ort)h(cac)o(he)f(mo)q(de)h
+Fl(all)f Fo(and,)h(when)g(cac)o(hing)h(is)f(enabled,)h(ha)o(v)o(e)e(a)g
+(default)i(cac)o(he)0 470 y(mo)q(de)d(of)g Fl(inc)g Fo(\(see)g(Section)h(5.8)
+e([Automoun)o(t)h(Filesystem],)g(page)30 b(SMM:13-24\).)62
+540 y(The)15 b(follo)o(wing)h(rule)f(illustrates)h(what)e(could)h(b)q(e)h
+(added)f(to)f(y)o(our)g(NIS)i(`)p Fl(Makefile)p Fo(',)c(in)k(this)f(case)f
+(causing)0 590 y(the)h(`)p Fl(amd.home)p Fo(')f(map)h(to)f(b)q(e)i(rebuilt:)
+120 661 y Fl($\(YPTSDIR\)/amd.home.time:)k($\(ETCDIR\)/amd.home)311
+710 y(-@sed)j(-e)h("s/#.*$$//")e(-e)i("/^$$/d")e($\(ETCDIR\)/amd.home)g(|)i
+(\\)359 760 y(awk)f('{)48 b(\\)526 810 y(for)23 b(\(i)h(=)f(1;)h(i)g(<=)f
+(NF;)h(i++\))f(\\)621 860 y(if)h(\(i)f(==)h(NF\))f({)h(\\)717
+910 y(if)f(\(substr\($$i,)f(length\($$i\),)h(1\))g(==)h("\\\\"\))f(\\)812
+959 y(printf\("\045s",)f(substr\($$i,)h(1,)g(length\($$i\))g(-)g(1\)\);)120
+1009 y(\\)717 1059 y(else)g(\\)812 1109 y(printf\("\045s\\n",)f($$i\);)h(\\)
+621 1159 y(})h(\\)621 1209 y(else)f(\\)717 1258 y(printf\("\045s)f(",)i
+($$i\);)f(\\)430 1308 y(}')h(|)g(\\)311 1358 y($\(MAKEDBM\))e(-)i
+($\(YPDBDIR\)/amd.home;)d(\\)311 1408 y(touch)i($\(YPTSDIR\)/amd.home.time;)e
+(\\)311 1458 y(echo)i("updated)g(amd.home";)f(\\)311 1507 y(if)h([)h(!)g
+($\(NOPUSH\))f(];)g(then)g(\\)502 1557 y($\(YPPUSH\))f(amd.home;)h(\\)502
+1607 y(echo)g("pushed)g(amd.home";)g(\\)311 1657 y(else)g(\\)502
+1707 y(:)h(;)f(\\)311 1757 y(fi)62 1848 y Fo(Here)16 b Fl($\(YPTSDIR\))e
+Fo(con)o(tains)h(the)g(time)h(stamp)e(\014les,)i(and)f Fl($\(YPDBDIR\))f
+Fo(con)o(tains)i(the)f(dbm)g(format)f(NIS)0 1898 y(\014les.)0
+2083 y Fi(3.1.4)30 b(Hesio)r(d)15 b(maps)62 2174 y Fo(When)f(the)g(map)g
+(name)f(b)q(egins)i(with)f(the)g(string)g(`)p Fl(hesiod.)p
+Fo(')e(lo)q(okups)i(are)f(made)h(using)h(the)e Fp(Hesio)q(d)k
+Fo(name)0 2224 y(serv)o(er.)i(The)13 b(string)g(follo)o(wing)g(the)g(dot)g
+(is)g(used)h(as)e(a)h(name)g(quali\014er)h(and)f(is)h(prep)q(ended)h(with)e
+(the)g(k)o(ey)g(b)q(eing)0 2274 y(lo)q(cated.)24 b(The)16 b(en)o(tire)h
+(string)f(is)h(then)g(resolv)o(ed)g(in)g(the)f Fl(automount)f
+Fo(con)o(text.)23 b(F)l(or)15 b(example,)j(if)e(the)h(the)f(k)o(ey)0
+2324 y(is)f(`)p Fl(jsp)p Fo(')e(and)h(map)h(name)f(is)h(`)p
+Fl(hesiod.homes)p Fo(')d(then)i Fp(Hesio)q(d)k Fo(is)c(ask)o(ed)h(to)e
+(resolv)o(e)i(`)p Fl(jsp.homes.automount)p Fo(')o(.)62 2394
+y(Hesio)q(d)21 b(maps)e(do)g(not)g(supp)q(ort)g(cac)o(he)g(mo)q(de)h(`)p
+Fl(all)p Fo(')e(and,)i(when)g(cac)o(hing)g(is)f(enabled,)j(ha)o(v)o(e)c(a)h
+(default)0 2444 y(cac)o(he)c(mo)q(de)h(of)f(`)p Fl(inc)p Fo(')f(\(see)h
+(Section)h(5.8)e([Automoun)o(t)g(Filesystem],)h(page)31 b(SMM:13-24\).)62
+2515 y(The)16 b(follo)o(wing)g(is)f(an)g(example)h(of)f(a)g
+Fp(Hesio)q(d)j Fo(map)d(en)o(try:)120 2585 y Fl(jsp.homes.automount)21
+b(HS)j(TXT)f("rfs:=/home/charm;rhost:=cha)o(rm;subli)o(nk:=jsp)o(")120
+2635 y(njw.homes.automount)e(HS)j(TXT)f("rfs:=/home/dylan/dk2;rhost:)o
+(=dylan;s)o(ublink:)o(=njw")p eop
+%%Page: 10 12
+10 11 bop 15 -83 a Fo(SMM:13-10)889 b(4.4)15 b(BSD)g(Automoun)o(ter)f
+(Reference)j(Man)o(ual)0 158 y Fi(3.1.5)30 b(P)n(assw)n(ord)16
+b(maps)62 250 y Fo(The)g(passw)o(ord)g(map)f(supp)q(ort)i(is)f(unlik)o(e)i
+(the)e(four)f(previous)i(map)f(t)o(yp)q(es.)22 b(When)16 b(the)h(map)e(name)h
+(is)h(the)0 299 y(string)g(`)p Fl(/etc/passwd)p Fo(')d Fp(Amd)19
+b Fo(can)e(lo)q(okup)h(a)e(user)h(name)g(in)h(the)f(passw)o(ord)f(\014le)i
+(and)f(re-arrange)f(the)h(home)0 349 y(directory)e(\014eld)i(to)d(pro)q(duce)
+j(a)d(usable)j(map)e(en)o(try)l(.)62 420 y Fp(Amd)20 b Fo(assumes)e(the)g
+(home)g(directory)g(has)g(the)g(format)f(`)p Fl(/)p Fp(an)o(ydir)s
+Fl(/)p Fp(dom1)t Fl(/../)p Fp(domN)5 b Fl(/)p Fp(login)p Fo('.)27
+b(It)18 b(breaks)0 470 y(this)d(string)g(in)o(to)g(a)f(map)g(en)o(try)h
+(where)g Fl(${rfs})f Fo(has)g(the)h(v)m(alue)h(`)p Fl(/)p Fp(an)o(ydir)s
+Fl(/)p Fp(domN)5 b Fo(',)14 b Fl(${rhost})f Fo(has)i(the)g(v)m(alue)0
+519 y(`)p Fp(domN)5 b Fl(.)p Fp(...)p Fl(.)p Fp(dom1)t Fo(',)12
+b(and)j Fl(${sublink})f Fo(has)h(the)g(v)m(alue)i(`)p Fl(login)p
+Fo('.)62 590 y(Th)o(us)e(if)h(the)f(passw)o(ord)g(\014le)h(en)o(try)f(w)o(as)
+120 661 y Fl(/home/achilles/jsp)62 752 y Fo(the)h(map)f(en)o(try)f(used)i(b)o
+(y)f Fp(Amd)i Fo(w)o(ould)f(b)q(e)120 823 y Fl(rfs:=/home/achilles;rhost:)o
+(=achill)o(es;subli)o(nk:=jsp)62 914 y Fo(Similarly)l(,)h(if)f(the)f(passw)o
+(ord)g(\014le)h(en)o(try)f(w)o(as)120 984 y Fl(/home/cc/sugar/mjh)62
+1076 y Fo(the)h(map)f(en)o(try)f(used)i(b)o(y)f Fp(Amd)i Fo(w)o(ould)f(b)q(e)
+120 1146 y Fl(rfs:=/home/sugar;rhost:=su)o(gar.cc;)o(sublink:)o(=jsp)0
+1279 y Fi(3.1.6)30 b(Union)15 b(maps)62 1371 y Fo(The)d(union)h(map)e(supp)q
+(ort)g(is)h(pro)o(vided)h(sp)q(eci\014cally)h(for)d(use)h(with)f(the)h(union)
+h(\014lesystem,)f(see)g(Section)g(5.10)0 1420 y([Union)k(Filesystem],)f(page)
+30 b(SMM:13-25.)62 1491 y(It)17 b(is)g(iden)o(ti\014ed)h(b)o(y)e(the)h
+(string)f(`)p Fl(union:)p Fo(')f(whic)o(h)i(is)g(follo)o(w)o(ed)g(b)o(y)f(a)g
+(colon)h(separated)f(list)h(of)f(directories.)0 1541 y(The)e(directories)g
+(are)f(read)g(in)i(order,)e(and)g(the)h(names)f(of)g(all)h(en)o(tries)g(are)f
+(recorded)h(in)g(the)f(map)h(cac)o(he.)19 b(Later)0 1591 y(directories)f(tak)
+o(e)e(precedence)j(o)o(v)o(er)d(earlier)i(ones.)25 b(The)18
+b(union)g(\014lesystem)f(t)o(yp)q(e)g(then)h(uses)f(the)g(map)g(cac)o(he)0
+1640 y(to)e(determine)h(the)f(union)h(of)f(the)g(names)h(in)g(all)g(the)f
+(directories.)0 1790 y Fq(3.2)33 b(Ho)n(w)14 b(k)n(eys)h(are)g(lo)r(ok)n(ed)h
+(up)62 1881 y Fo(The)g(k)o(ey)f(is)h(lo)q(cated)g(in)g(the)g(map)f(whose)g(t)
+o(yp)q(e)g(w)o(as)g(determined)i(when)e(the)h(automoun)o(t)e(p)q(oin)o(t)i(w)
+o(as)e(\014rst)0 1931 y(created.)19 b(In)14 b(general)g(the)f(k)o(ey)g(is)h
+(a)f(pathname)g(comp)q(onen)o(t.)19 b(In)14 b(some)f(circumstances)h(this)f
+(ma)o(y)g(b)q(e)h(mo)q(di\014ed)0 1981 y(b)o(y)h(v)m(ariable)i(expansion)f
+(\(see)f(Section)h(3.3.2)e([V)l(ariable)i(Expansion],)f(page)31
+b(SMM:13-12\))13 b(and)j(pre\014xing.)21 b(If)0 2031 y(the)13
+b(automoun)o(t)e(p)q(oin)o(t)i(has)g(a)f(pre\014x,)h(sp)q(eci\014ed)i(b)o(y)e
+(the)f Fp(pref)22 b Fo(option,)13 b(then)g(that)f(is)h(prep)q(ended)h(to)e
+(the)h(searc)o(h)0 2081 y(k)o(ey)i(b)q(efore)h(the)f(map)g(is)h(searc)o(hed.)
+62 2151 y(If)e(the)f(map)g(cac)o(he)h(is)g(a)e(`)p Fl(regexp)p
+Fo(')g(cac)o(he)i(then)f(the)h(k)o(ey)f(is)h(treated)e(as)h(an)g(egrep-st)o
+(yle)h(regular)f(expression,)0 2201 y(otherwise)i(a)g(normal)g(string)h
+(comparison)f(is)h(made.)62 2271 y(If)k(the)f(k)o(ey)h(cannot)f(b)q(e)h
+(found)g(then)f(a)g Fp(wildcard)k Fo(matc)o(h)c(is)h(attempted.)31
+b Fp(Amd)22 b Fo(rep)q(eatedly)e(strips)g(the)0 2321 y(basename)15
+b(from)f(the)h(k)o(ey)l(,)f(app)q(ends)i(`)p Fl(/*)p Fo(')e(and)h(attempts)e
+(a)i(lo)q(okup.)20 b(Finally)l(,)c Fp(Amd)h Fo(attempts)d(to)g(lo)q(cate)h
+(the)0 2371 y(sp)q(ecial)i(k)o(ey)e(`)p Fl(*)p Fo('.)62 2421
+y(F)l(or)g(example,)g(the)h(follo)o(wing)g(sequence)g(w)o(ould)g(b)q(e)g(c)o
+(hec)o(k)o(ed)f(if)h(`)p Fl(home/dylan/dk2)p Fo(')c(w)o(as)j(b)q(eing)h(lo)q
+(cated:)192 2492 y Fl(home/dylan/dk2)192 2541 y(home/dylan/*)192
+2591 y(home/*)192 2641 y(*)p eop
+%%Page: 11 13
+11 12 bop 0 -83 a Fo(Chapter)15 b(3:)k(Moun)o(t)c(Maps)1236
+b(SMM:13-11)62 158 y(A)o(t)14 b(an)o(y)f(p)q(oin)o(t)h(when)h(a)e(wildcard)i
+(is)g(found,)f Fp(Amd)i Fo(pro)q(ceeds)e(as)g(if)g(an)g(exact)f(matc)o(h)h
+(had)g(b)q(een)h(found)f(and)0 208 y(the)19 b(v)m(alue)h(\014eld)g(is)f(then)
+g(used)g(to)f(resolv)o(e)h(the)g(moun)o(t)f(request,)h(otherwise)g(an)f
+(error)g(co)q(de)h(is)h(propagated)0 258 y(bac)o(k)15 b(to)g(the)g(k)o
+(ernel.)21 b(\(see)15 b(Chapter)g(5)f([Filesystem)i(T)o(yp)q(es],)f(page)30
+b(SMM:13-20\).)0 437 y Fq(3.3)j(Lo)r(cation)15 b(F)-6 b(ormat)62
+528 y Fo(The)17 b(v)m(alue)h(\014eld)f(from)f(the)h(lo)q(okup)g(pro)o(vides)g
+(the)f(information)h(required)h(to)d(moun)o(t)h(a)g(\014lesystem.)25
+b(The)0 578 y(information)15 b(is)h(parsed)f(according)h(to)f(the)g(syn)o
+(tax)f(sho)o(wn)h(b)q(elo)o(w.)120 648 y Fp(lo)q(cation-list)q
+Fo(:)393 698 y Fp(lo)q(cation-selection)393 748 y(lo)q(cation-list)j
+(white-space)g Fl(||)d Fp(white-space)k(lo)q(cation-selection)120
+798 y(lo)q(cation-selection)p Fo(:)393 848 y Fp(lo)q(cation)393
+897 y(lo)q(cation-selection)e(white-space)i(lo)q(cation)120
+947 y(lo)q(cation)p Fo(:)393 997 y Fp(lo)q(cation-info)393
+1047 y Fl(-)p Fp(lo)q(cation-info)393 1097 y Fl(-)120 1147
+y Fp(lo)q(cation-info)r Fo(:)393 1196 y Fp(sel-or-opt)393 1246
+y(lo)q(cation-info)r Fl(;)p Fp(sel-or-opt)393 1296 y Fl(;)120
+1346 y Fp(sel-or-opt)q Fo(:)393 1396 y Fp(selection)393 1445
+y(opt-ass)120 1495 y(selection)p Fo(:)393 1545 y(selector)p
+Fl(==)p Fp(v)m(alue)393 1595 y Fo(selector)p Fl(!=)p Fp(v)m(alue)120
+1645 y(opt-ass)r Fo(:)393 1694 y(option)p Fl(:=)p Fp(v)m(alue)120
+1744 y(white-space)s Fo(:)393 1794 y(space)393 1844 y(tab)62
+1935 y(Note)g(that)f(unquoted)h(whitespace)h(is)f(not)f(allo)o(w)o(ed)h(in)h
+(a)e(lo)q(cation)i(description.)32 b(White)19 b(space)g(is)g(only)0
+1985 y(allo)o(w)o(ed,)c(and)h(is)f(mandatory)l(,)f(where)i(sho)o(wn)f(with)g
+(non-terminal)i(`)p Fl(white-space)p Fo('.)62 2056 y(A)j Fp(lo)q
+(cation-selection)h Fo(is)f(a)f(list)h(of)f(p)q(ossible)i(v)o(olumes)f(with)f
+(whic)o(h)i(to)d(satisfy)h(the)h(request.)32 b Fp(lo)q(cation-)0
+2105 y(selection)p Fo(s)23 b(are)f(separated)f(b)o(y)h(the)g(`)p
+Fl(||)p Fo(')f(op)q(erator.)39 b(The)22 b(e\013ect)f(of)h(this)g(op)q(erator)
+f(is)h(to)g(prev)o(en)o(t)f(use)h(of)0 2155 y(lo)q(cation-selections)d(to)c
+(its)i(righ)o(t)f(if)h(an)o(y)f(of)g(the)g(lo)q(cation-selections)j(on)d(its)
+h(left)f(w)o(ere)g(selected)i(whether)e(or)0 2205 y(not)f(an)o(y)g(of)f(them)
+i(w)o(ere)f(successfully)i(moun)o(ted)e(\(see)g(Section)h(3.3.3)e
+([Selectors],)g(page)31 b(SMM:13-13\).)62 2276 y(The)17 b(lo)q
+(cation-selection,)h(and)e(singleton)h Fp(lo)q(cation-list)p
+Fo(,)h(`)p Fl(type:=ufs;dev:=/dev/)o(xd1g)p Fo(')12 b(w)o(ould)17
+b(inform)0 2325 y Fp(Amd)g Fo(to)e(moun)o(t)f(a)h(UFS)g(\014lesystem)h(from)f
+(the)g(blo)q(c)o(k)h(sp)q(ecial)h(device)g(`)p Fl(/dev/xd1g)p
+Fo('.)62 2396 y(The)h Fp(sel-or-opt)g Fo(comp)q(onen)o(t)f(is)h(either)f(the)
+h(name)f(of)f(an)h(option)h(required)g(b)o(y)f(a)g(sp)q(eci\014c)i
+(\014lesystem,)e(or)0 2446 y(it)h(is)g(the)g(name)f(of)h(a)f(built-in,)j
+(prede\014ned)f(selector)f(suc)o(h)g(as)g(the)f(arc)o(hitecture)h(t)o(yp)q
+(e.)28 b(The)17 b(v)m(alue)i(ma)o(y)e(b)q(e)0 2496 y(quoted)g(with)g(double)h
+(quotes)f(`)p Fl(")p Fo(',)f(for)g(example)i(`)p Fl(type:="ufs";dev:="/dev/)o
+(xd1g")p Fo(')o(.)k(These)c(quotes)e(are)0 2545 y(stripp)q(ed)k(when)g(the)g
+(v)m(alue)g(is)g(parsed)f(and)h(there)f(is)h(no)f(w)o(a)o(y)f(to)h(get)g(a)g
+(double)h(quote)f(in)o(to)h(a)f(v)m(alue)h(\014eld.)0 2595
+y(Double)h(quotes)g(are)f(used)h(to)f(get)h(white)g(space)g(in)o(to)f(a)h(v)m
+(alue)h(\014eld,)h(whic)o(h)e(is)g(needed)h(for)e(the)h(program)0
+2645 y(\014lesystem)16 b(\(see)f(Section)h(5.5)e([Program)g(Filesystem],)h
+(page)30 b(SMM:13-23\).)p eop
+%%Page: 12 14
+12 13 bop 15 -83 a Fo(SMM:13-12)889 b(4.4)15 b(BSD)g(Automoun)o(ter)f
+(Reference)j(Man)o(ual)0 158 y Fi(3.3.1)30 b(Map)15 b(Defaults)62
+250 y Fo(A)h(lo)q(cation)h(b)q(eginning)h(with)e(a)g(dash)g(`)p
+Fl(-)p Fo(')f(is)h(used)h(to)e(sp)q(ecify)i(default)g(v)m(alues)g(for)e
+(subsequen)o(t)i(lo)q(cations.)0 299 y(An)o(y)12 b(previously)i(sp)q
+(eci\014ed)g(defaults)f(in)g(the)f(lo)q(cation-list)i(are)e(discarded.)20
+b(The)13 b(default)g(string)f(can)g(b)q(e)h(empt)o(y)0 349
+y(in)j(whic)o(h)g(case)f(no)g(defaults)h(apply)l(.)62 420 y(The)c(lo)q
+(cation)h(`)p Fl(-fs:=/mnt;opts:=ro)p Fo(')8 b(w)o(ould)k(set)g(the)f(lo)q
+(cal)i(moun)o(t)e(p)q(oin)o(t)h(to)f(`)p Fl(/mnt)p Fo(')g(and)h(cause)g(moun)
+o(ts)0 470 y(to)h(b)q(e)g(read-only)h(b)o(y)f(default.)20 b(Defaults)13
+b(sp)q(eci\014ed)i(this)f(w)o(a)o(y)e(are)h(app)q(ended)i(to,)d(and)i(so)e(o)
+o(v)o(erride,)i(an)o(y)e(global)0 519 y(map)j(defaults)h(giv)o(en)f(with)h(`)
+p Fl(/defaults)p Fo('\).)0 805 y Fi(3.3.2)30 b(V)-5 b(ariable)15
+b(Expansion)62 896 y Fo(T)l(o)g(allo)o(w)g(generic)h(lo)q(cation)g(sp)q
+(eci\014cations)h Fp(Amd)g Fo(do)q(es)e(v)m(ariable)i(expansion)f(on)f(eac)o
+(h)g(lo)q(cation)h(and)f(also)0 946 y(on)f(some)f(of)g(the)h(option)g
+(strings.)19 b(An)o(y)13 b(option)h(or)f(selector)h(app)q(earing)h(in)f(the)g
+(form)f Fl($)p Fp(v)m(ar)j Fo(is)e(replaced)h(b)o(y)f(the)0
+996 y(curren)o(t)f(v)m(alue)i(of)e(that)g(option)h(or)f(selector.)19
+b(F)l(or)13 b(example,)h(if)g(the)g(v)m(alue)h(of)e Fl(${key})f
+Fo(w)o(as)h(`)p Fl(bin)p Fo(',)f Fl(${autodir})0 1046 y Fo(w)o(as)k(`)p
+Fl(/a)p Fo(')f(and)i Fl(${fs})f Fo(w)o(as)g(`)p Fl(${autodir}/local/${key})p
+Fo(')d(then)k(after)f(expansion)i Fl(${fs})e Fo(w)o(ould)h(ha)o(v)o(e)f(the)0
+1095 y(v)m(alue)g(`)p Fl(/a/local/bin)p Fo('.)i(An)o(y)d(en)o(vironmen)o(t)h
+(v)m(ariable)g(can)f(b)q(e)h(accessed)g(in)g(a)f(similar)h(w)o(a)o(y)l(.)62
+1166 y(Tw)o(o)f(pathname)g(op)q(erators)g(are)g(a)o(v)m(ailable)i(when)f
+(expanding)h(a)e(v)m(ariable.)23 b(If)16 b(the)f(v)m(ariable)i(name)f(b)q
+(egins)0 1216 y(with)h(`)p Fl(/)p Fo(')g(then)g(only)h(the)f(last)g(comp)q
+(onen)o(t)g(of)g(then)g(pathname)g(is)h(substituted.)26 b(F)l(or)16
+b(example,)i(if)g Fl(${path})0 1266 y Fo(w)o(as)f(`)p Fl(/foo/bar)p
+Fo(')e(then)j Fl(${/path})e Fo(w)o(ould)i(b)q(e)g(expanded)h(to)d(`)p
+Fl(bar)p Fo('.)26 b(Similarly)l(,)20 b(if)e(the)f(v)m(ariable)i(name)e(ends)0
+1315 y(with)f(`)p Fl(/)p Fo(')e(then)h(all)i(but)e(the)g(last)g(comp)q(onen)o
+(t)h(of)f(the)g(pathname)g(is)h(substituted.)k(In)c(the)g(previous)f
+(example,)0 1365 y Fl(${path/})f Fo(w)o(ould)i(b)q(e)g(expanded)g(to)e(`)p
+Fl(/foo)p Fo('.)62 1436 y(Tw)o(o)i(domain)g(name)h(op)q(erators)e(are)h(also)
+g(pro)o(vided.)24 b(If)16 b(the)h(v)m(ariable)g(name)f(b)q(egins)i(with)f(`)p
+Fl(.)p Fo(')e(then)h(only)0 1486 y(the)h(domain)g(part)f(of)g(the)g(name)h
+(is)g(substituted.)25 b(F)l(or)16 b(example,)h(if)g Fl(${rhost})e
+Fo(w)o(as)h(`)p Fl(swan.doc.ic.ac.uk)p Fo(')0 1535 y(then)g
+Fl(${.rhost})f Fo(w)o(ould)i(b)q(e)f(expanded)h(to)f(`)p Fl(doc.ic.ac.uk)p
+Fo('.)k(Similarly)l(,)e(if)e(the)h(v)m(ariable)g(name)f(ends)h(with)0
+1585 y(`)p Fl(.)p Fo(')h(then)h(only)g(the)g(host)f(comp)q(onen)o(t)h(is)g
+(substituted.)31 b(In)19 b(the)g(previous)g(example,)h Fl(${rhost.})d
+Fo(w)o(ould)i(b)q(e)0 1635 y(expanded)d(to)f(`)p Fl(swan)p
+Fo('.)62 1706 y(V)l(ariable)k(expansion)f(is)g(a)f(t)o(w)o(o)f(phase)h(pro)q
+(cess.)26 b(Before)18 b(a)f(lo)q(cation)h(is)f(parsed,)h(all)g(references)g
+(to)f(selec-)0 1755 y(tors,)f Fp(eg)k Fl(${path})p Fo(,)c(are)g(expanded.)25
+b(The)17 b(lo)q(cation)g(is)g(then)g(parsed,)g(selections)h(are)e(ev)m
+(aluated)i(and)f(option)0 1805 y(assignmen)o(ts)h(recorded.)31
+b(If)19 b(there)g(w)o(ere)f(no)h(selections)h(or)e(they)g(all)i(succeeded)g
+(the)f(lo)q(cation)g(is)h(used)f(and)0 1855 y(the)c(v)m(alues)h(of)f(the)g
+(follo)o(wing)h(options)f(are)g(expanded)h(in)g(the)f(order)g(giv)o(en:)20
+b Fp(sublink)p Fo(,)d Fp(rfs)p Fo(,)d Fp(fs)p Fo(,)h Fp(opts)p
+Fo(,)f Fp(remopts)p Fo(,)0 1905 y Fp(moun)o(t)i Fo(and)f Fp(unmoun)o(t)p
+Fo(.)62 1975 y(Note)f(that)f(expansion)h(of)g(option)f(v)m(alues)i(is)g(done)
+f(after)f Fp(all)j Fo(assignmen)o(ts)e(ha)o(v)o(e)f(b)q(een)i(completed)f
+(and)g(not)0 2025 y(in)20 b(a)f(purely)h(left)g(to)e(righ)o(t)h(order)g(as)g
+(is)g(done)h(b)o(y)f(the)g(shell.)33 b(This)20 b(generally)g(has)f(the)h
+(desired)g(e\013ect)f(but)0 2075 y(care)14 b(m)o(ust)g(b)q(e)h(tak)o(en)f(if)
+g(one)h(of)f(the)g(options)h(references)g(another,)e(in)i(whic)o(h)h(case)e
+(the)g(ordering)h(can)f(b)q(ecome)0 2125 y(signi\014can)o(t.)62
+2195 y(There)i(are)f(t)o(w)o(o)e(sp)q(ecial)k(cases)f(concerning)g(v)m
+(ariable)h(expansion:)25 2266 y(1.)29 b(b)q(efore)12 b(a)f(map)h(is)g
+(consulted,)h(an)o(y)e(selectors)h(in)g(the)g(name)g(receiv)o(ed)h(from)d
+(the)i(k)o(ernel)h(are)e(expanded.)20 b(F)l(or)90 2316 y(example,)f(if)f(the)
+f(request)h(from)e(the)i(k)o(ernel)g(w)o(as)f(for)g(`)p Fl(${arch}.bin)p
+Fo(')e(and)j(the)f(mac)o(hine)i(arc)o(hitecture)90 2366 y(w)o(as)14
+b(`)p Fl(vax)p Fo(',)g(the)h(v)m(alue)i(giv)o(en)e(to)g Fl(${key})f
+Fo(w)o(ould)i(b)q(e)g(`)p Fl(vax.bin)p Fo('.)25 2446 y(2.)29
+b(the)13 b(v)m(alue)h(of)e Fl(${rhost})g Fo(is)h(expanded)h(and)f(normalized)
+i(b)q(efore)e(the)g(other)f(options)h(are)g(expanded.)20 b(The)90
+2496 y(normalization)g(pro)q(cess)g(strips)g(an)o(y)f(lo)q(cal)i(sub-domain)g
+(comp)q(onen)o(ts.)33 b(F)l(or)19 b(example,)i(if)f Fl(${domain})90
+2545 y Fo(w)o(as)12 b(`)p Fl(Berkeley.EDU)p Fo(')e(and)j Fl(${rhost})f
+Fo(w)o(as)g(initially)j(`)p Fl(snow.Berkeley.EDU)p Fo(',)10
+b(after)i(the)h(normalization)90 2595 y(it)21 b(w)o(ould)g(simply)h(b)q(e)g
+(`)p Fl(snow)p Fo('.)35 b(Hostname)21 b(normalization)g(is)h(curren)o(tly)f
+(done)g(in)h(a)e Fp(case-dep)q(enden)o(t)90 2645 y Fo(manner.)p
+eop
+%%Page: 13 15
+13 14 bop 0 -83 a Fo(Chapter)15 b(3:)k(Moun)o(t)c(Maps)1236
+b(SMM:13-13)0 158 y Fi(3.3.3)30 b(Selectors)62 250 y Fo(Selectors)15
+b(are)g(used)g(to)f(con)o(trol)g(the)h(use)g(of)f(a)h(lo)q(cation.)20
+b(It)15 b(is)g(p)q(ossible)h(to)e(share)h(a)f(moun)o(t)g(map)h(b)q(et)o(w)o
+(een)0 299 y(man)o(y)c(mac)o(hines)h(in)g(suc)o(h)g(a)f(w)o(a)o(y)g(that)f
+(\014lesystem)i(lo)q(cation,)h(arc)o(hitecture)f(and)f(op)q(erating)h(system)
+f(di\013erences)0 349 y(are)18 b(hidden)j(from)d(the)h(users.)31
+b(A)18 b(selector)h(of)g(the)g(form)f(`)p Fl(arch==sun3;os==sos4)p
+Fo(')d(w)o(ould)k(only)g(apply)h(on)0 399 y(Sun-3s)c(running)g(SunOS)h(4.x.)
+62 470 y(Selectors)22 b(are)f(ev)m(aluated)h(left)g(to)e(righ)o(t.)38
+b(If)21 b(a)g(selector)g(fails)h(then)g(that)e(lo)q(cation)i(is)g(ignored.)38
+b(Th)o(us)0 519 y(the)21 b(selectors)h(form)f(a)g(conjunction)h(and)g(the)f
+(lo)q(cations)h(form)f(a)g(disjunction.)40 b(If)21 b(all)i(the)e(lo)q
+(cations)h(are)0 569 y(ignored)17 b(or)f(otherwise)h(fail)h(then)f
+Fp(Amd)h Fo(uses)f(the)g Fp(error)i Fo(\014lesystem)e(\(see)g(Section)g(5.11)
+f([Error)f(Filesystem],)0 619 y(page)43 b(SMM:13-26\).)38 b(This)22
+b(is)g(equiv)m(alen)o(t)i(to)d(ha)o(ving)h(a)f(lo)q(cation)i(`)p
+Fl(type:=error)p Fo(')c(at)i(the)h(end)h(of)e(eac)o(h)0 669
+y(moun)o(t-map)15 b(en)o(try)l(.)62 739 y(The)h(selectors)f(curren)o(tly)h
+(implemen)o(ted)h(are:)0 823 y(`)p Fl(arch)p Fo(')118 b(the)19
+b(mac)o(hine)g(arc)o(hitecture)g(whic)o(h)g(w)o(as)e(automatically)i
+(determined)h(at)e(compile)i(time.)30 b(The)240 873 y(arc)o(hitecture)16
+b(t)o(yp)q(e)f(can)h(b)q(e)g(displa)o(y)o(ed)h(b)o(y)e(running)i(the)f
+(command)f(`)p Fl(amd)f(-v)p Fo('.)20 b(See)d(Section)f(2.2)240
+923 y([Supp)q(orted)g(Mac)o(hine)g(Arc)o(hitectures],)f(page)30
+b(SMM:13-7.)0 985 y(`)p Fl(autodir)p Fo(')46 b(the)16 b(default)h(directory)g
+(under)g(whic)o(h)g(to)f(moun)o(t)g(\014lesystems.)24 b(This)17
+b(ma)o(y)f(b)q(e)h(c)o(hanged)g(b)o(y)f(the)240 1035 y(\\-a")e(command)i
+(line)g(option.)21 b(See)15 b(the)h Fp(fs)g Fo(option.)0 1098
+y(`)p Fl(byte)p Fo(')118 b(the)16 b(mac)o(hine's)g(b)o(yte)f(ordering.)21
+b(This)c(is)f(either)g(`)p Fl(little)p Fo(',)e(indicating)j(little-endian,)h
+(or)e(`)p Fl(big)p Fo(',)240 1148 y(indicating)k(big-endian.)31
+b(One)18 b(p)q(ossible)i(use)f(is)f(to)g(share)g(`)p Fl(rwho)p
+Fo(')e(databases)i(\(see)g(Section)h(8.5)240 1197 y([rwho)c(serv)o(ers],)h
+(page)32 b(SMM:13-49\).)21 b(Another)16 b(is)g(to)g(share)g(ndbm)h
+(databases,)e(ho)o(w)o(ev)o(er)h(this)240 1247 y(use)g(can)f(b)q(e)h
+(considered)g(a)f(courageous)g(juggling)h(act.)0 1310 y(`)p
+Fl(cluster)p Fo(')46 b(is)19 b(pro)o(vided)h(as)e(a)h(ho)q(ok)g(for)f(the)h
+(name)g(of)f(the)h(lo)q(cal)h(cluster.)32 b(This)19 b(can)g(b)q(e)h(used)f
+(to)f(decide)240 1360 y(whic)o(h)h(serv)o(ers)f(to)g(use)g(for)g(copies)h(of)
+f(replicated)i(\014lesystems.)30 b Fl(${cluster})16 b Fo(defaults)j(to)f(the)
+240 1410 y(v)m(alue)e(of)f Fl(${domain})f Fo(unless)i(a)f(di\013eren)o(t)h(v)
+m(alue)g(is)g(set)f(with)g(the)h(\\-C")e(command)h(line)i(option.)0
+1472 y(`)p Fl(domain)p Fo(')70 b(the)15 b(lo)q(cal)i(domain)e(name)g(as)g(sp)
+q(eci\014ed)j(b)o(y)d(the)g(\\-d")g(command)g(line)i(option.)j(See)c(`)p
+Fl(host)p Fo('.)0 1535 y(`)p Fl(host)p Fo(')118 b(the)13 b(lo)q(cal)h
+(hostname)e(as)h(determined)h(b)o(y)f Fk(gethostname)p Fo(\(2\).)18
+b(If)13 b(no)g(domain)g(name)g(w)o(as)f(sp)q(eci\014ed)240
+1585 y(on)19 b(the)g(command)f(line)j(and)e(the)f(hostname)h(con)o(tains)g(a)
+f(p)q(erio)q(d)i(`)p Fl(.)p Fo(')e(then)h(the)g(string)g(b)q(efore)240
+1635 y(the)i(p)q(erio)q(d)g(is)g(used)g(as)f(the)h(host)f(name,)h(and)g(the)f
+(string)h(after)e(the)i(p)q(erio)q(d)h(is)f(assigned)g(to)240
+1684 y Fl(${domain})p Fo(.)d(F)l(or)13 b(example,)h(if)f(the)h(hostname)f(is)
+g(`)p Fl(styx.doc.ic.ac.uk)p Fo(')d(then)k Fl(host)f Fo(w)o(ould)g(b)q(e)240
+1734 y(`)p Fl(styx)p Fo(')h(and)h Fl(domain)g Fo(w)o(ould)g(b)q(e)h(`)p
+Fl(doc.ic.ac.uk)p Fo('.)h Fl(hostd)e Fo(w)o(ould)h(b)q(e)f(`)p
+Fl(styx.doc.ic.ac.uk)p Fo('.)0 1797 y(`)p Fl(hostd)p Fo(')94
+b(is)16 b Fl(${host})e Fo(and)i Fl(${domain})e Fo(concatenated)h(with)h(a)f
+(`)p Fl(.)p Fo(')f(inserted)i(b)q(et)o(w)o(een)g(them)f(if)h(required.)240
+1847 y(If)f Fl(${domain})f Fo(is)i(an)f(empt)o(y)g(string)g(then)h
+Fl(${host})e Fo(and)h Fl(${hostd})f Fo(will)j(b)q(e)f(iden)o(tical.)0
+1909 y(`)p Fl(karch)p Fo(')94 b(is)13 b(pro)o(vided)g(as)f(a)g(ho)q(ok)h(for)
+e(the)i(k)o(ernel)g(arc)o(hitecture.)19 b(This)13 b(is)g(used)g(on)g(SunOS)g
+(4,)g(for)e(example,)240 1959 y(to)i(distinguish)i(b)q(et)o(w)o(een)f
+(di\013eren)o(t)f(`)p Fl(/usr/kvm)p Fo(')f(v)o(olumes.)20 b
+Fl(${karch})12 b Fo(defaults)i(to)e(the)i(v)m(alue)g(of)240
+2009 y Fl(${arch})g Fo(unless)j(a)d(di\013eren)o(t)i(v)m(alue)g(is)g(set)f
+(with)g(the)h(\\-k")e(command)i(line)g(option.)0 2072 y(`)p
+Fl(os)p Fo(')166 b(the)15 b(op)q(erating)g(system.)k(Lik)o(e)d(the)f(mac)o
+(hine)h(arc)o(hitecture,)f(this)g(is)g(automatically)h(determined)240
+2122 y(at)11 b(compile)h(time.)19 b(The)12 b(op)q(erating)f(system)g(name)g
+(can)h(b)q(e)g(displa)o(y)o(ed)g(b)o(y)f(running)i(the)e(command)240
+2171 y(`)p Fl(amd)j(-v)p Fo('.)20 b(See)15 b(Section)h(2.1)f([Supp)q(orted)g
+(Op)q(erating)h(Systems],)f(page)30 b(SMM:13-6.)62 2263 y(The)15
+b(follo)o(wing)g(selectors)g(are)f(also)h(pro)o(vided.)20 b(Unlik)o(e)c(the)f
+(other)f(selectors,)h(they)f(v)m(ary)h(for)f(eac)o(h)h(lo)q(okup.)0
+2313 y(Note)h(that)h(when)g(the)g(name)g(from)f(the)h(k)o(ernel)g(is)g
+(expanded)h(prior)f(to)f(a)h(map)g(lo)q(okup,)g(these)g(selectors)g(are)0
+2362 y(all)f(de\014ned)h(as)e(empt)o(y)f(strings.)0 2433 y(`)p
+Fl(key)p Fo(')142 b(the)13 b(name)f(b)q(eing)i(resolv)o(ed.)19
+b(F)l(or)12 b(example,)i(if)e(`)p Fl(/home)p Fo(')f(is)i(an)g(automoun)o(t)e
+(p)q(oin)o(t,)i(then)g(accessing)240 2483 y(`)p Fl(/home/foo)p
+Fo(')c(w)o(ould)i(set)g Fl(${key})f Fo(to)g(the)h(string)f(`)p
+Fl(foo)p Fo('.)18 b(The)11 b(k)o(ey)f(is)i(pre\014xed)f(b)o(y)g(the)g
+Fp(pref)20 b Fo(option)240 2533 y(set)15 b(in)g(the)g(paren)o(t)f(moun)o(t)h
+(p)q(oin)o(t.)20 b(The)15 b(default)g(pre\014x)g(is)h(an)e(empt)o(y)h
+(string.)20 b(If)15 b(the)g(pre\014x)g(w)o(as)240 2582 y(`)p
+Fl(blah/)p Fo(')f(then)h Fl(${key})g Fo(w)o(ould)g(b)q(e)h(set)f(to)g(`)p
+Fl(blah/foo)p Fo('.)0 2645 y(`)p Fl(map)p Fo(')142 b(the)15
+b(name)g(of)g(the)h(moun)o(t)e(map)h(b)q(eing)i(used.)p eop
+%%Page: 14 16
+14 15 bop 15 -83 a Fo(SMM:13-14)889 b(4.4)15 b(BSD)g(Automoun)o(ter)f
+(Reference)j(Man)o(ual)0 158 y(`)p Fl(path)p Fo(')118 b(the)12
+b(full)i(pathname)e(of)g(the)h(name)f(b)q(eing)i(resolv)o(ed.)19
+b(F)l(or)12 b(example)h(`)p Fl(/home/foo)p Fo(')d(in)k(the)e(example)240
+208 y(ab)q(o)o(v)o(e.)0 272 y(`)p Fl(wire)p Fo(')118 b(the)21
+b(name)g(of)g(the)g(net)o(w)o(ork)f(to)h(whic)o(h)h(the)f(primary)g(net)o(w)o
+(ork)g(in)o(terface)g(is)h(attac)o(hed.)37 b(If)21 b(a)240
+322 y(sym)o(b)q(olic)i(name)e(cannot)h(b)q(e)g(found)g(in)g(the)g(net)o(w)o
+(orks)e(or)h(hosts)g(database)g(then)h(dotted)g(IP)240 372
+y(address)15 b(format)f(is)i(used.)k(This)c(v)m(alue)g(is)g(also)f(output)g
+(b)o(y)g(the)h(\\-v")f(option.)62 463 y(Selectors)j(can)f(b)q(e)h(negated)f
+(b)o(y)h(using)f(`)p Fl(!=)p Fo(')g(instead)g(of)g(`)p Fl(==)p
+Fo('.)25 b(F)l(or)16 b(example)j(to)d(select)i(a)f(lo)q(cation)h(on)f(all)0
+513 y(non-V)l(ax)f(mac)o(hines)g(the)f(selector)g(`)p Fl(arch!=vax)p
+Fo(')e(w)o(ould)j(b)q(e)g(used.)0 669 y Fi(3.3.4)30 b(Map)15
+b(Options)62 760 y Fo(Options)e(are)f(parsed)g(concurren)o(tly)h(with)g
+(selectors.)19 b(The)12 b(di\013erence)i(is)f(that)e(when)i(an)f(option)g(is)
+h(seen)g(the)0 810 y(string)g(follo)o(wing)i(the)e(`)p Fl(:=)p
+Fo(')g(is)h(recorded)g(for)e(later)i(use.)20 b(As)13 b(a)g(minim)o(um)i(the)e
+Fp(t)o(yp)q(e)j Fo(option)e(m)o(ust)f(b)q(e)h(sp)q(eci\014ed.)0
+860 y(Eac)o(h)k(\014lesystem)h(t)o(yp)q(e)g(has)f(other)g(options)g(whic)o(h)
+i(m)o(ust)e(also)g(b)q(e)h(sp)q(eci\014ed.)31 b(See)19 b(Chapter)g(5)f
+([Filesystem)0 910 y(T)o(yp)q(es],)d(page)30 b(SMM:13-20,)13
+b(for)h(details)j(on)e(the)g(\014lesystem)h(sp)q(eci\014c)h(options.)62
+980 y(Sup)q(er\015uous)g(option)e(sp)q(eci\014cations)i(are)e(ignored)h(and)f
+(are)g(not)g(rep)q(orted)g(as)g(errors.)62 1051 y(The)h(follo)o(wing)g
+(options)f(apply)h(to)f(more)f(than)h(one)h(\014lesystem)g(t)o(yp)q(e.)0
+1207 y Fi(3.3.4.1)30 b(dela)n(y)15 b(Option)62 1298 y Fo(The)21
+b(dela)o(y)l(,)h(in)f(seconds,)h(b)q(efore)f(an)f(attempt)g(will)i(b)q(e)f
+(made)f(to)g(moun)o(t)g(from)g(the)g(curren)o(t)h(lo)q(cation.)0
+1348 y(Auxilliary)g(data,)d(suc)o(h)g(as)g(net)o(w)o(ork)f(address,)i(\014le)
+g(handles)h(and)e(so)g(on)h(are)f(computed)g(regardless)h(of)f(this)0
+1398 y(v)m(alue.)62 1468 y(A)23 b(dela)o(y)g(can)g(b)q(e)h(used)f(to)f
+(implemen)o(t)i(the)f(notion)g(of)f(primary)h(and)g(secondary)g(\014le)h
+(serv)o(ers.)42 b(The)0 1518 y(secondary)14 b(serv)o(ers)g(w)o(ould)h(ha)o(v)
+o(e)e(a)h(dela)o(y)h(of)f(a)g(few)g(seconds,)g(th)o(us)g(giving)i(the)e
+(primary)g(serv)o(ers)g(a)g(c)o(hance)h(to)0 1568 y(resp)q(ond)h(\014rst.)0
+1724 y Fi(3.3.4.2)30 b(fs)15 b(Option)62 1815 y Fo(The)h(lo)q(cal)g(moun)o(t)
+f(p)q(oin)o(t.)20 b(The)15 b(seman)o(tics)h(of)f(this)g(option)h(v)m(ary)f(b)
+q(et)o(w)o(een)g(\014lesystems.)62 1885 y(F)l(or)i(NFS)h(and)g(UFS)f
+(\014lesystems)h(the)g(v)m(alue)h(of)e Fl(${fs})g Fo(is)h(used)g(as)f(the)h
+(lo)q(cal)h(moun)o(t)e(p)q(oin)o(t.)27 b(F)l(or)17 b(other)0
+1935 y(\014lesystem)f(t)o(yp)q(es)f(it)g(has)g(other)f(meanings)i(whic)o(h)f
+(are)g(describ)q(ed)i(in)f(the)f(section)g(describing)i(the)e(resp)q(ectiv)o
+(e)0 1985 y(\014lesystem)d(t)o(yp)q(e.)19 b(It)11 b(is)h(imp)q(ortan)o(t)f
+(that)g(this)h(string)f(uniquely)j(iden)o(ti\014es)f(the)f(\014lesystem)g(b)q
+(eing)h(moun)o(ted.)18 b(T)l(o)0 2035 y(satisfy)d(this)g(requiremen)o(t,)g
+(it)g(should)h(con)o(tain)f(the)g(name)g(of)f(the)h(host)f(on)h(whic)o(h)h
+(the)f(\014lesystem)g(is)g(residen)o(t)0 2085 y(and)g(the)h(pathname)f(of)g
+(the)g(\014lesystem)h(on)f(the)g(lo)q(cal)h(or)f(remote)g(host.)62
+2155 y(The)21 b(reason)g(for)f(requiring)i(the)f(hostname)f(is)h(clear)h(if)f
+(replicated)h(\014lesystems)g(are)e(considered.)38 b(If)21
+b(a)0 2205 y(\014leserv)o(er)g(go)q(es)f(do)o(wn)f(and)i(a)e(replacemen)o(t)i
+(\014lesystem)g(is)f(moun)o(ted)g(then)g(the)h Fp(lo)q(cal)i
+Fo(moun)o(t)c(p)q(oin)o(t)i Fp(m)o(ust)0 2255 y Fo(b)q(e)e(di\013eren)o(t)f
+(from)g(that)g(of)f(the)i(\014lesystem)g(whic)o(h)g(is)g(h)o(ung.)29
+b(Some)18 b(enco)q(ding)i(of)e(the)g(\014lesystem)h(name)f(is)0
+2305 y(required)e(if)g(more)f(than)g(one)g(\014lesystem)h(is)g(to)e(b)q(e)i
+(moun)o(ted)f(from)g(an)o(y)g(giv)o(en)g(host.)62 2375 y(If)21
+b(the)f(hostname)f(is)i(\014rst)e(in)i(the)f(path)g(then)g(all)h(moun)o(ts)f
+(from)f(a)h(particular)g(host)g(will)h(b)q(e)g(gathered)0 2425
+y(b)q(elo)o(w)16 b(a)e(single)j(directory)l(.)j(If)c(that)e(serv)o(er)h(go)q
+(es)g(do)o(wn)f(then)i(the)f(h)o(ung)g(moun)o(t)g(p)q(oin)o(ts)g(are)g(less)h
+(lik)o(ely)h(to)d(b)q(e)0 2475 y(acciden)o(tally)j(referenced,)e(for)g
+(example)g(when)h Fk(get)o(wd)p Fo(\(3\))d(tra)o(v)o(erses)h(the)h(namespace)
+g(to)f(\014nd)i(the)f(pathname)0 2525 y(of)g(the)g(curren)o(t)g(directory)l
+(.)62 2595 y(The)i(`)p Fl(fs)p Fo(')e(option)h(defaults)g(to)g
+Fl(${autodir}/${rhost}${rfs)o(})p Fo(.)k(In)c(addition,)h(`)p
+Fl(rhost)p Fo(')e(defaults)h(to)g(the)0 2645 y(lo)q(cal)k(host)e(name)h(\()p
+Fl(${host})p Fo(\))e(and)i(`)p Fl(rfs)p Fo(')e(defaults)i(to)f(the)h(v)m
+(alue)h(of)e Fl(${path})p Fo(,)g(whic)o(h)i(is)f(the)g(full)h(path)e(of)p
+eop
+%%Page: 15 17
+15 16 bop 0 -83 a Fo(Chapter)15 b(3:)k(Moun)o(t)c(Maps)1236
+b(SMM:13-15)0 158 y(the)15 b(requested)h(\014le;)g(`)p Fl(/home/foo)p
+Fo(')d(in)j(the)f(example)h(ab)q(o)o(v)o(e)f(\(see)g(Section)h(3.3.3)d
+([Selectors],)i(page)30 b(SMM:13-)0 208 y(13\).)23 b Fl(${autodir})16
+b Fo(defaults)h(to)f(`)p Fl(/a)p Fo(')f(but)i(ma)o(y)f(b)q(e)i(c)o(hanged)f
+(with)g(the)f(\\-a")g(command)h(line)h(option.)25 b(Sun's)0
+258 y(automoun)o(ter)15 b(defaults)i(to)e(`)p Fl(/tmp_mnt)p
+Fo('.)22 b(Note)15 b(that)h(there)g(is)h(no)f(`)p Fl(/)p Fo(')f(b)q(et)o(w)o
+(een)i(the)f Fl(${rhost})f Fo(and)i Fl(${rfs})0 308 y Fo(since)f
+Fl(${rfs})f Fo(b)q(egins)h(with)g(a)f(`)p Fl(/)p Fo('.)0 437
+y Fi(3.3.4.3)30 b(opts)15 b(Option)62 528 y Fo(The)20 b(options)f(to)f(pass)h
+(to)g(the)g(moun)o(t)g(system)g(call.)32 b(A)20 b(leading)g(`)p
+Fl(-)p Fo(')e(is)i(silen)o(tly)h(ignored.)32 b(The)19 b(moun)o(t)0
+578 y(options)h(supp)q(orted)h(generally)g(corresp)q(ond)f(to)f(those)h(used)
+h(b)o(y)f Fk(moun)o(t)p Fo(\(8\))e(and)i(are)g(listed)h(b)q(elo)o(w.)35
+b(Some)0 628 y(additional)17 b(pseudo-options)f(are)f(in)o(terpreted)g(b)o(y)
+h Fp(Amd)h Fo(and)e(are)g(also)g(listed.)62 698 y(Unless)k(sp)q(eci\014cally)
+h(o)o(v)o(erridden,)e(eac)o(h)f(of)g(the)g(system)g(default)h(moun)o(t)e
+(options)i(applies.)28 b(An)o(y)17 b(options)0 748 y(not)12
+b(recognised)h(are)f(ignored.)19 b(If)12 b(no)g(options)h(list)g(is)f
+(supplied)j(the)d(string)g(`)p Fl(rw,defaults)p Fo(')e(is)j(used)f(and)h(all)
+g(the)0 798 y(system)k(default)g(moun)o(t)g(options)g(apply)l(.)26
+b(Options)18 b(whic)o(h)f(are)g(not)g(applicable)i(for)d(a)h(particular)h(op)
+q(erating)0 848 y(system)d(are)h(silen)o(tly)h(ignored.)22
+b(F)l(or)15 b(example,)h(only)g(4.4)f(BSD)h(is)g(kno)o(wn)f(to)g(implemen)o
+(t)i(the)f Fl(compress)f Fo(and)0 898 y Fl(spongy)f Fo(options.)0
+968 y Fl(compress)48 b Fo(Use)15 b(NFS)h(compression)f(proto)q(col.)0
+1027 y Fl(grpid)120 b Fo(Use)15 b(BSD)h(directory)f(group-id)h(seman)o(tics.)
+0 1087 y Fl(intr)144 b Fo(Allo)o(w)16 b(k)o(eyb)q(oard)f(in)o(terrupts)g(on)g
+(hard)h(moun)o(ts.)0 1146 y Fl(noconn)96 b Fo(Don't)14 b(mak)o(e)h(a)g
+(connection)h(on)f(datagram)f(transp)q(orts.)0 1205 y Fl(nocto)120
+b Fo(No)15 b(close-to-op)q(en)h(consistency)l(.)0 1265 y Fl(nodevs)96
+b Fo(Don't)14 b(allo)o(w)i(lo)q(cal)g(sp)q(ecial)h(devices)f(on)g(this)f
+(\014lesystem.)0 1324 y Fl(nosuid)96 b Fo(Don't)14 b(allo)o(w)i(set-uid)g(or)
+f(set-gid)g(executables)i(on)e(this)g(\014lesystem.)0 1383
+y Fl(quota)120 b Fo(Enable)16 b(quota)f(c)o(hec)o(king)h(on)f(this)h(moun)o
+(t.)0 1442 y Fl(retrans=)p Fp(n)240 1502 y Fo(The)21 b(n)o(um)o(b)q(er)g(of)f
+(NFS)h(retransmits)f(made)h(b)q(efore)f(a)h(user)g(error)f(is)h(generated)f
+(b)o(y)h(a)f(`)p Fl(soft)p Fo(')240 1551 y(moun)o(ted)15 b(\014lesystem,)h
+(and)g(b)q(efore)f(a)g(`)p Fl(hard)p Fo(')f(moun)o(ted)i(\014lesystem)g(rep)q
+(orts)f(`)p Fl(NFS)f(server)g Fp(y)o(o)o(y)o(o)240 1601 y Fl(not)h
+(responding)f(still)g(trying)p Fo('.)0 1660 y Fl(ro)192 b Fo(Moun)o(t)14
+b(this)i(\014lesystem)g(readonly)l(.)0 1720 y Fl(rsize=)p Fp(n)71
+b Fo(The)12 b(NFS)g(read)f(pac)o(k)o(et)g(size.)20 b(Y)l(ou)12
+b(ma)o(y)f(need)h(to)f(set)h(this)g(if)g(y)o(ou)f(are)h(using)g(NFS/UDP)f
+(through)240 1770 y(a)k(gatew)o(a)o(y)l(.)0 1829 y Fl(soft)144
+b Fo(Giv)o(e)15 b(up)h(after)e Fp(retrans)j Fo(retransmissions.)0
+1888 y Fl(spongy)96 b Fo(Lik)o(e)16 b(`)p Fl(soft)p Fo(')e(for)h(status)f
+(requests,)h(and)g(`)p Fl(hard)p Fo(')f(for)g(data)h(transfers.)0
+1947 y Fl(tcp)168 b Fo(Use)15 b(TCP/IP)f(instead)h(of)f(UDP/IP)l(,)g(ignored)
+h(if)g(the)g(NFS)f(implemen)o(tation)i(do)q(es)f(not)f(supp)q(ort)240
+1997 y(TCP/IP)h(moun)o(ts.)0 2056 y Fl(timeo=)p Fp(n)71 b Fo(The)15
+b(NFS)h(timeout,)e(in)i(ten)o(th-seconds,)g(b)q(efore)f(a)g(request)g(is)h
+(retransmitted.)0 2116 y Fl(wsize=)p Fp(n)71 b Fo(The)21 b(NFS)h(write)f(pac)
+o(k)o(et)g(size.)39 b(Y)l(ou)21 b(ma)o(y)g(need)h(to)e(set)h(this)h(if)g(y)o
+(ou)f(are)g(using)h(NFS/UDP)240 2165 y(through)15 b(a)g(gatew)o(a)o(y)l(.)62
+2257 y(The)h(follo)o(wing)g(options)f(are)g(implemen)o(ted)i(b)o(y)e
+Fp(Amd)p Fo(,)g(rather)g(than)g(b)q(eing)h(passed)g(to)e(the)h(k)o(ernel.)0
+2327 y Fl(nounmount)240 2387 y Fo(Con\014gures)f(the)g(moun)o(t)g(so)g(that)g
+(its)g(time-to-liv)o(e)h(will)h(nev)o(er)f(expire.)20 b(This)15
+b(is)g(also)f(the)g(default)240 2436 y(for)h(some)f(\014lesystem)i(t)o(yp)q
+(es.)0 2496 y Fl(ping=)p Fp(n)95 b Fo(The)16 b(in)o(terv)m(al,)h(in)g
+(seconds,)f(b)q(et)o(w)o(een)h(k)o(eep-aliv)o(e)g(pings.)23
+b(When)17 b(four)f(consecutiv)o(e)h(pings)f(ha)o(v)o(e)240
+2545 y(failed)h(the)f(moun)o(t)f(p)q(oin)o(t)h(is)g(mark)o(ed)f(as)h(h)o
+(ung.)21 b(This)16 b(in)o(terv)m(al)h(defaults)f(to)f(30)g(seconds.)22
+b(If)16 b(the)240 2595 y(ping)g(in)o(terv)m(al)h(is)f(less)g(than)g(zero,)f
+(no)g(pings)i(are)e(sen)o(t)g(and)h(the)g(host)f(is)h(assumed)g(to)e(b)q(e)j
+(alw)o(a)o(ys)240 2645 y(up.)j(By)c(default,)f(pings)h(are)f(not)g(sen)o(t)g
+(for)f(an)h(NFS/TCP)g(moun)o(t.)p eop
+%%Page: 16 18
+16 17 bop 15 -83 a Fo(SMM:13-16)889 b(4.4)15 b(BSD)g(Automoun)o(ter)f
+(Reference)j(Man)o(ual)0 158 y Fl(retry=)p Fp(n)71 b Fo(The)15
+b(n)o(um)o(b)q(er)h(of)f(times)g(to)g(retry)f(the)i(moun)o(t)e(system)h
+(call.)0 220 y Fl(utimeout=)p Fp(n)240 282 y Fo(The)k(in)o(terv)m(al,)h(in)g
+(seconds,)f(b)o(y)g(whic)o(h)h(the)e(moun)o(t's)g(time-to-liv)o(e)i(is)f
+(extended)h(after)e(an)g(un-)240 332 y(moun)o(t)k(attempt)f(has)h(failed.)42
+b(In)23 b(fact)f(the)g(in)o(terv)m(al)h(is)g(extended)g(b)q(efore)g(the)f
+(unmoun)o(t)g(is)240 382 y(attempted)13 b(to)g(a)o(v)o(oid)g(thrashing.)20
+b(The)14 b(default)g(v)m(alue)h(is)f(120)e(seconds)i(\(t)o(w)o(o)e(min)o
+(utes\))i(or)f(as)g(set)240 432 y(b)o(y)i(the)g(\\-w")g(command)g(line)i
+(option.)0 572 y Fi(3.3.4.4)30 b(remopts)15 b(Option)62 663
+y Fo(This)j(option)f(has)g(the)h(same)f(use)g(as)g Fl(${opts})f
+Fo(but)h(applies)i(only)f(when)f(the)h(remote)e(host)h(is)h(on)f(a)g(non-)0
+713 y(lo)q(cal)g(net)o(w)o(ork.)j(F)l(or)15 b(example,)h(when)g(using)g(NFS)g
+(across)e(a)i(gatew)o(a)o(y)d(it)j(is)g(often)g(necessary)f(to)g(use)h
+(smaller)0 763 y(v)m(alues)g(for)e(the)g(data)g(read)g(and)h(write)g(sizes.)
+20 b(This)15 b(can)g(simply)h(b)q(e)f(done)g(b)o(y)f(sp)q(ecifying)j(the)d
+(small)h(v)m(alues)h(in)0 812 y Fp(remopts)p Fo(.)j(When)d(a)f(non-lo)q(cal)h
+(host)f(is)h(accessed,)f(the)h(smaller)g(sizes)g(will)g(automatically)g(b)q
+(e)g(used.)62 883 y Fp(Amd)21 b Fo(determines)f(whether)f(a)f(host)g(is)i(lo)
+q(cal)f(b)o(y)g(examining)h(the)f(net)o(w)o(ork)f(in)o(terface)h
+(con\014guration)g(at)0 933 y(startup.)24 b(An)o(y)17 b(in)o(terface)h(c)o
+(hanges)f(made)g(after)f Fp(Amd)j Fo(has)e(b)q(een)h(started)e(will)j(not)d
+(b)q(e)i(noticed.)26 b(The)17 b(lik)o(ely)0 983 y(e\013ect)e(will)i(b)q(e)f
+(that)e(a)h(host)g(ma)o(y)f(incorrectly)j(b)q(e)e(declared)i(non-lo)q(cal.)62
+1053 y(Unless)f(otherwise)g(set,)e(the)i(v)m(alue)g(of)f Fl(${rem})f
+Fo(is)i(the)f(same)g(as)g(the)g(v)m(alue)i(of)d Fl(${opts})p
+Fo(.)0 1193 y Fi(3.3.4.5)30 b(sublink)16 b(Option)62 1284 y
+Fo(The)22 b(sub)q(directory)h(within)g(the)e(moun)o(ted)h(\014lesystem)g(to)f
+(whic)o(h)i(the)f(reference)g(should)h(p)q(oin)o(t.)39 b(This)0
+1334 y(can)16 b(b)q(e)h(used)f(to)f(prev)o(en)o(t)h(duplicate)h(moun)o(ts)f
+(in)g(cases)g(where)g(m)o(ultiple)i(directories)f(in)g(the)e(same)h(moun)o
+(ted)0 1384 y(\014lesystem)g(are)f(used.)0 1524 y Fi(3.3.4.6)30
+b(t)n(yp)r(e)15 b(Option)62 1615 y Fo(The)h(\014lesystem)g(t)o(yp)q(e)f(to)g
+(b)q(e)h(used.)k(See)c(Chapter)f(5)g([Filesystem)h(T)o(yp)q(es],)e(page)31
+b(SMM:13-20,)13 b(for)i(a)g(full)0 1665 y(description)i(of)d(eac)o(h)i(t)o
+(yp)q(e.)0 1853 y Fm(4)41 b Fh(Amd)16 b Fm(Command)g(Line)f(Options)62
+1973 y Fo(Man)o(y)g(of)g Fp(Amd)r Fo('s)g(parameters)g(can)g(b)q(e)h(set)f
+(from)g(the)h(command)f(line.)22 b(The)16 b(command)f(line)i(is)f(also)f
+(used)0 2022 y(to)g(sp)q(ecify)h(automoun)o(t)e(p)q(oin)o(ts)i(and)f(maps.)62
+2093 y(The)h(general)f(format)f(of)h(a)g(command)g(line)i(is)120
+2164 y Fl(amd)23 b([)p Fp(options)r Fl(])h({)g Fp(directory)k(map-name)e
+Fl([-)p Fp(map-options)r Fl(])e(})g(...)62 2255 y Fo(F)l(or)11
+b(eac)o(h)h(directory)g(and)f(map-name)h(giv)o(en,)g Fp(Amd)i
+Fo(establishes)f(an)e(automoun)o(t)g(p)q(oin)o(t.)19 b(The)12
+b Fp(map-options)0 2305 y Fo(ma)o(y)h(b)q(e)i(an)o(y)e(sequence)j(of)d
+(options)h(or)f(selectors|see)i(Section)g(3.3)e([Lo)q(cation)h(F)l(ormat],)e
+(page)28 b(SMM:13-11.)0 2355 y(The)15 b Fp(map-options)j Fo(apply)e(only)f
+(to)g Fp(Amd)r Fo('s)g(moun)o(t)f(p)q(oin)o(t.)62 2425 y(`)p
+Fl(type:=toplvl;cache:=mapde)o(fault;fs)o(:=${map)o(})p Fo(')c(is)j(the)f
+(default)i(v)m(alue)g(for)e(the)g(map)h(options.)19 b(De-)0
+2475 y(fault)12 b(options)f(for)g(a)h(map)f(are)g(read)h(from)f(a)g(sp)q
+(ecial)i(en)o(try)f(in)g(the)g(map)f(whose)h(k)o(ey)f(is)h(the)g(string)f(`)p
+Fl(/defaults)p Fo('.)0 2525 y(When)16 b(default)g(options)f(are)g(giv)o(en)h
+(they)f(are)g(prep)q(ended)j(to)c(an)o(y)h(options)h(sp)q(eci\014ed)h(in)f
+(the)g(moun)o(t-map)e(lo-)0 2575 y(cations)h(as)g(explained)i(in.)k(See)16
+b(Section)g(3.3.1)e([Map)g(Defaults],)g(page)31 b(SMM:13-12,)13
+b(for)h(more)h(details.)62 2645 y(The)h Fp(options)h Fo(are)e(an)o(y)g(com)o
+(bination)g(of)g(those)g(listed)i(b)q(elo)o(w.)p eop
+%%Page: 17 19
+17 18 bop 0 -83 a Fo(Chapter)15 b(4:)k Fp(Amd)f Fo(Command)c(Line)j(Options)
+899 b(SMM:13-17)62 158 y(Once)14 b(the)f(command)g(line)h(has)f(b)q(een)h
+(parsed,)f(the)g(automoun)o(t)f(p)q(oin)o(ts)h(are)g(moun)o(ted.)19
+b(The)13 b(moun)o(t)g(p)q(oin)o(ts)0 208 y(are)21 b(created)g(if)g(they)g(do)
+g(not)f(already)h(exist,)i(in)e(whic)o(h)h(case)f(they)g(will)i(b)q(e)e(remo)
+o(v)o(ed)g(when)g Fp(Amd)i Fo(exits.)0 258 y(Finally)l(,)17
+b Fp(Amd)g Fo(disasso)q(ciates)f(itself)g(from)e(its)i(con)o(trolling)g
+(terminal)g(and)f(forks)f(in)o(to)i(the)f(bac)o(kground.)62
+329 y(Note:)k(Ev)o(en)c(if)f Fp(Amd)j Fo(has)d(b)q(een)h(built)h(with)f(`)p
+Fl(-DDEBUG)p Fo(')d(it)j(will)h(still)f(bac)o(kground)g(itself)g(and)f
+(disasso)q(ciate)0 378 y(itself)j(from)e(the)h(con)o(trolling)g(terminal.)23
+b(T)l(o)15 b(use)h(a)g(debugger)g(it)g(is)g(necessary)g(to)f(sp)q(ecify)i(`)p
+Fl(-D)e(nodaemon)p Fo(')f(on)0 428 y(the)h(command)g(line.)0
+648 y Fq(4.1)33 b Fg(-a)14 b Ff(directory)62 739 y Fo(Sp)q(eci\014es)25
+b(the)d(default)h(moun)o(t)f(directory)l(.)41 b(This)23 b(option)g(c)o
+(hanges)f(the)h(v)m(ariable)g Fl(${autodir})e Fo(whic)o(h)0
+789 y(otherwise)15 b(defaults)h(to)f(`)p Fl(/a)p Fo('.)j(F)l(or)d(example,)h
+(some)f(sites)g(prefer)g(`)p Fl(/amd)p Fo('.)120 860 y Fl(amd)23
+b(-a)h(/amd)f(...)0 1080 y Fq(4.2)33 b Fg(-c)14 b Ff(cac)n(he-in)n(terv)m(al)
+62 1171 y Fo(Selects)k(the)e(p)q(erio)q(d,)i(in)f(seconds,)g(for)e(whic)o(h)j
+(a)e(name)g(is)h(cac)o(hed)g(b)o(y)f Fp(Amd)p Fo(.)23 b(If)17
+b(no)f(reference)h(is)g(made)g(to)0 1221 y(the)e(v)o(olume)h(in)g(this)g(p)q
+(erio)q(d,)g Fp(Amd)h Fo(discards)f(the)f(v)o(olume)h(name)f(to)f
+(\014lesystem)i(mapping.)62 1291 y(Once)e(the)f(last)g(reference)h(to)e(a)g
+(\014lesystem)i(has)f(b)q(een)h(remo)o(v)o(ed,)e Fp(Amd)j Fo(attempts)d(to)g
+(unmoun)o(t)h(the)g(\014lesys-)0 1341 y(tem.)35 b(If)21 b(the)f(unmoun)o(t)h
+(fails)g(the)f(in)o(terv)m(al)i(is)f(extended)g(b)o(y)g(a)f(further)g(p)q
+(erio)q(d)i(as)e(sp)q(eci\014ed)i(b)o(y)f(the)f(`)p Fl(-w)p
+Fo(')0 1391 y(command)15 b(line)i(option)e(or)g(b)o(y)g(the)g(`)p
+Fl(utimeout)p Fo(')f(moun)o(t)g(option.)62 1461 y(The)i(default)f
+Fp(cac)o(he-in)o(terv)m(al)k Fo(is)d(300)e(seconds)i(\(\014v)o(e)f(min)o
+(utes\).)0 1691 y Fq(4.3)33 b Fg(-d)14 b Ff(domain)62 1782
+y Fo(Sp)q(eci\014es)i(the)e(host's)e(domain.)20 b(This)14 b(sets)g(the)f(in)o
+(ternal)i(v)m(ariable)g Fl(${domain})d Fo(and)i(a\013ects)e(the)i
+Fl(${hostd})0 1832 y Fo(v)m(ariable.)62 1902 y(If)i(this)h(option)f(is)h(not)
+e(sp)q(eci\014ed)j(and)e(the)g(hostname)g(already)g(con)o(tains)g(the)g(lo)q
+(cal)h(domain)g(then)f(that)f(is)0 1952 y(used,)g(otherwise)h(the)f(default)h
+(v)m(alue)g(of)f Fl(${domain})f Fo(is)i(`)p Fl(unknown.domain)p
+Fo('.)62 2023 y(F)l(or)f(example,)g(if)h(the)f(lo)q(cal)i(domain)e(w)o(as)g
+(`)p Fl(doc.ic.ac.uk)p Fo(',)d Fp(Amd)17 b Fo(could)g(b)q(e)e(started)g(as)g
+(follo)o(ws:)120 2093 y Fl(amd)23 b(-d)h(doc.ic.ac.uk)e(...)0
+2313 y Fq(4.4)33 b Fg(-k)14 b Ff(k)n(ernel-arc)n(hitecture)62
+2404 y Fo(Sp)q(eci\014es)19 b(the)e(k)o(ernel)g(arc)o(hitecture)g(of)f(the)g
+(system.)24 b(This)17 b(is)g(usually)h(the)f(output)f(of)g(`)p
+Fl(arch)e(-k)p Fo(')i(and)h(its)0 2454 y(only)d(e\013ect)f(is)h(to)f(set)g
+(the)h(v)m(ariable)h Fl(${karch})p Fo(.)j(If)c(this)g(option)f(is)h(not)f
+(giv)o(en,)h Fl(${karch})f Fo(has)g(the)h(same)f(v)m(alue)0
+2504 y(as)i Fl(${arch})p Fo(.)62 2575 y(This)h(w)o(ould)g(b)q(e)f(used)h(as)f
+(follo)o(ws:)120 2645 y Fl(amd)23 b(-k)h(`arch)f(-k`)h(...)p
+eop
+%%Page: 18 20
+18 19 bop 15 -83 a Fo(SMM:13-18)889 b(4.4)15 b(BSD)g(Automoun)o(ter)f
+(Reference)j(Man)o(ual)0 158 y Fq(4.5)33 b Fg(-l)14 b Ff(log-option)62
+250 y Fo(Selects)j(the)e(form)f(of)h(logging)h(to)e(b)q(e)i(made.)k(Tw)o(o)14
+b(sp)q(ecial)j Fp(log-options)h Fo(are)c(recognised.)25 320
+y(1.)29 b(If)15 b Fp(log-option)h Fo(is)g(the)f(string)g(`)p
+Fl(syslog)p Fo(',)e Fp(Amd)18 b Fo(will)e(use)g(the)f Fk(syslog)p
+Fo(\(3\))f(mec)o(hanism.)25 384 y(2.)29 b(If)12 b Fp(log-option)h
+Fo(is)g(the)f(string)g(`)p Fl(/dev/stderr)p Fo(',)e Fp(Amd)k
+Fo(will)g(use)f(standard)f(error,)f(whic)o(h)i(is)g(also)f(the)g(default)90
+434 y(target)i(for)h(log)g(messages.)k(T)l(o)c(implemen)o(t)i(this,)e
+Fp(Amd)i Fo(sim)o(ulates)f(the)f(e\013ect)g(of)g(the)g(`)p
+Fl(/dev/fd)p Fo(')e(driv)o(er.)62 525 y(An)o(y)18 b(other)f(string)g(is)h
+(tak)o(en)e(as)h(a)g(\014lename)i(to)d(use)i(for)f(logging.)26
+b(Log)17 b(messages)g(are)g(app)q(ended)i(to)e(the)0 575 y(\014le)f(if)f(it)g
+(already)g(exists,)g(otherwise)g(a)f(new)h(\014le)h(is)g(created.)j(The)c
+(\014le)h(is)f(op)q(ened)h(once)g(and)e(then)i(held)g(op)q(en,)0
+625 y(rather)f(than)g(b)q(eing)h(re-op)q(ened)h(for)d(eac)o(h)h(message.)62
+695 y(If)j(the)f(`)p Fl(syslog)p Fo(')e(option)j(is)f(sp)q(eci\014ed)j(but)d
+(the)g(system)g(do)q(es)g(not)g(supp)q(ort)g(syslog)g(or)g(if)g(the)h(named)f
+(\014le)0 745 y(cannot)e(b)q(e)h(op)q(ened)h(or)e(created,)g
+Fp(Amd)i Fo(will)g(use)f(standard)f(error.)k(Error)c(messages)g(generated)g
+(b)q(efore)h Fp(Amd)0 795 y Fo(has)f(\014nished)i(parsing)e(the)h(command)f
+(line)i(are)e(prin)o(ted)h(on)f(standard)f(error.)62 866 y(Using)i(`)p
+Fl(syslog)p Fo(')e(is)h(usually)i(b)q(est,)e(in)h(whic)o(h)g(case)f
+Fp(Amd)i Fo(w)o(ould)f(b)q(e)g(started)e(as)h(follo)o(ws:)120
+936 y Fl(amd)23 b(-l)h(syslog)f(...)0 1107 y Fq(4.6)33 b Fg(-n)62
+1199 y Fo(Normalises)17 b(the)f(remote)g(hostname)g(b)q(efore)g(using)h(it.)
+24 b(Normalisation)16 b(is)h(done)g(b)o(y)f(replacing)h(the)g(v)m(alue)0
+1248 y(of)e Fl(${rhost})f Fo(with)h(the)h(primary)f(name)g(returned)h(b)o(y)f
+(a)g(hostname)f(lo)q(okup.)62 1319 y(This)i(option)f(should)i(b)q(e)f(used)f
+(if)h(sev)o(eral)f(names)g(are)g(used)h(to)f(refer)g(to)f(a)h(single)i(host)d
+(in)i(a)f(moun)o(t)g(map.)0 1490 y Fq(4.7)33 b Fg(-p)62 1581
+y Fo(Causes)14 b Fp(Amd)r Fo('s)f(pro)q(cess)h(id)g(to)f(b)q(e)h(prin)o(ted)h
+(on)e(standard)g(output.)20 b(This)14 b(can)f(b)q(e)i(redirected)f(to)f(a)h
+(suitable)0 1631 y(\014le)i(for)f(use)g(with)h(kill:)120 1702
+y Fl(amd)23 b(-p)h(>)g(/var/run/amd.pid)d(...)62 1793 y Fo(This)c(option)f
+(only)h(has)f(an)g(a\013ect)f(if)h Fp(Amd)i Fo(is)f(running)g(in)g(daemon)f
+(mo)q(de.)22 b(If)17 b Fp(Amd)h Fo(is)e(started)g(with)g(the)0
+1843 y Fl(-D)f(nodaemon)f Fo(debug)i(\015ag,)e(this)i(option)f(is)h(ignored.)
+0 2014 y Fq(4.8)33 b Fg(-r)62 2105 y Fo(T)l(ells)16 b Fp(Amd)f
+Fo(to)f(restart)e(existing)j(moun)o(ts)e(\(see)h(Section)h(5.14)e
+([Inheritance)i(Filesystem],)f(page)27 b(SMM:13-)0 2155 y(26\).)0
+2334 y Fq(4.9)33 b Fg(-t)14 b Ff(timeout.retransmit)62 2425
+y Fo(Sp)q(eci\014es)i(the)d(RPC)g Fp(timeout)i Fo(and)e Fp(retransmit)h
+Fo(in)o(terv)m(als)g(used)g(b)o(y)f(the)g(k)o(ernel)h(to)f(comm)o(unicate)h
+(to)e Fp(Amd)p Fo(.)0 2475 y(These)k(are)e(used)i(to)f(set)g(the)g(`)p
+Fl(timeo)p Fo(')f(and)h(`)p Fl(retrans)p Fo(')f(moun)o(t)g(options.)62
+2545 y Fp(Amd)21 b Fo(relies)g(on)e(the)g(k)o(ernel)h(RPC)f(retransmit)g(mec)
+o(hanism)h(to)e(trigger)h(moun)o(t)g(retries.)32 b(The)19 b(v)m(alue)h(of)0
+2595 y(this)14 b(parameter)g(c)o(hanges)g(the)g(retry)f(in)o(terv)m(al.)21
+b(T)l(o)q(o)13 b(long)i(an)f(in)o(terv)m(al)h(giv)o(es)f(p)q(o)q(or)g(in)o
+(teractiv)o(e)g(resp)q(onse,)h(to)q(o)0 2645 y(short)g(an)g(in)o(terv)m(al)h
+(causes)f(excessiv)o(e)h(retries.)p eop
+%%Page: 19 21
+19 20 bop 0 -83 a Fo(Chapter)15 b(4:)k Fp(Amd)f Fo(Command)c(Line)j(Options)
+899 b(SMM:13-19)0 158 y Fq(4.10)32 b Fg(-v)62 250 y Fo(Prin)o(t)15
+b(v)o(ersion)h(information)f(on)g(standard)g(error)g(and)g(then)h(exit.)k
+(The)15 b(output)g(is)h(of)f(the)g(form:)120 320 y Fl(amd)23
+b(5.2.1.11)g(of)h(91/03/17)f(18:04:05)f(5.3Alpha11)h(#0:)g(Sun)h(Mar)f(17)h
+(18:07:28)f(GMT)g(1991)120 370 y(Built)g(by)h(pendry@vangogh.Berkeley.)o(EDU)
+d(for)i(a)h(hp300)f(running)g(bsd44)g(\(big-endian\).)120 420
+y(Map)g(support)g(for:)h(root,)f(passwd,)g(union,)g(file,)g(error.)120
+470 y(FS:)g(ufs,)h(nfs,)f(nfsx,)g(host,)g(link,)h(program,)e(union,)h(auto,)h
+(direct,)f(toplvl,)f(error.)120 519 y(Primary)h(network)g(is)g(128.32.130.0.)
+62 611 y Fo(The)12 b(information)f(includes)i(the)f(v)o(ersion)f(n)o(um)o(b)q
+(er,)h(release)g(date)f(and)g(name)g(of)g(the)g(release.)19
+b(The)11 b(arc)o(hitec-)0 661 y(ture)16 b(\(see)f(Section)i(2.2)e([Supp)q
+(orted)h(Mac)o(hine)h(Arc)o(hitectures],)e(page)32 b(SMM:13-7\),)14
+b(op)q(erating)i(system)f(\(see)0 710 y(Section)j(2.1)f([Supp)q(orted)h(Op)q
+(erating)g(Systems],)f(page)35 b(SMM:13-6\))15 b(and)j(b)o(yte)f(ordering)h
+(are)f(also)g(prin)o(ted)0 760 y(as)e(they)g(app)q(ear)g(in)h(the)g
+Fl(${os})p Fo(,)e Fl(${arch})g Fo(and)h Fl(${byte})g Fo(v)m(ariables.)0
+918 y Fq(4.11)32 b Fg(-w)15 b Ff(w)n(ait-timeout)62 1009 y
+Fo(Selects)j(the)e(in)o(terv)m(al)i(in)f(seconds)g(b)q(et)o(w)o(een)g(unmoun)
+o(t)g(attempts)e(after)h(the)h(initial)h(time-to-liv)o(e)g(has)f(ex-)0
+1059 y(pired.)62 1129 y(This)f(defaults)g(to)e(120)h(seconds)g(\(t)o(w)o(o)f
+(min)o(utes\).)0 1293 y Fq(4.12)32 b Fg(-x)15 b Ff(opts)62
+1384 y Fo(Sp)q(eci\014es)k(the)d(t)o(yp)q(e)h(and)f(v)o(erb)q(osit)o(y)h(of)f
+(log)g(messages.)23 b Fp(opts)18 b Fo(is)f(a)f(comma)g(separated)g(list)h
+(selected)h(from)0 1434 y(the)d(follo)o(wing)h(options:)0 1505
+y Fl(fatal)120 b Fo(F)l(atal)15 b(errors)0 1567 y Fl(error)120
+b Fo(Non-fatal)15 b(errors)0 1629 y Fl(user)144 b Fo(Non-fatal)15
+b(user)g(errors)0 1691 y Fl(warn)144 b Fo(Reco)o(v)o(erable)16
+b(errors)0 1753 y Fl(warning)72 b Fo(Alias)16 b(for)f Fl(warn)0
+1815 y(info)144 b Fo(Information)15 b(messages)0 1877 y Fl(map)168
+b Fo(Moun)o(t)14 b(map)h(usage)0 1940 y Fl(stats)120 b Fo(Additional)17
+b(statistics)0 2002 y Fl(all)168 b Fo(All)16 b(of)f(the)h(ab)q(o)o(v)o(e)62
+2093 y(Initially)g(a)d(set)h(of)f(default)h(logging)g(\015ags)f(is)h
+(enabled.)21 b(This)14 b(is)g(as)f(if)h(`)p Fl(-x)g(all,nomap,nostats)p
+Fo(')d(had)j(b)q(een)0 2143 y(selected.)23 b(The)16 b(command)g(line)h(is)g
+(parsed)f(and)g(logging)g(is)g(con)o(trolled)h(b)o(y)e(the)h(\\-x")g(option.)
+22 b(The)16 b(v)o(ery)f(\014rst)0 2193 y(set)h(of)g(logging)h(\015ags)g(is)g
+(sa)o(v)o(ed)f(and)g(can)h(not)f(b)q(e)h(subsequen)o(tly)h(disabled)h(using)e
+Fp(Amq)p Fo(.)24 b(This)17 b(default)g(set)f(of)0 2243 y(options)f(is)h
+(useful)g(for)f(general)h(pro)q(duction)g(use.)62 2313 y(The)h(`)p
+Fl(info)p Fo(')f(messages)g(include)j(details)f(of)e(what)g(is)h(moun)o(ted)g
+(and)g(unmoun)o(ted)g(and)g(when)g(\014lesystems)0 2363 y(ha)o(v)o(e)e(timed)
+h(out.)k(If)c(y)o(ou)f(w)o(an)o(t)f(to)h(ha)o(v)o(e)g(the)h(default)g(set)f
+(of)g(messages)g(without)g(the)h(`)p Fl(info)p Fo(')e(messages)h(then)0
+2413 y(y)o(ou)f(simply)i(need)f(`)p Fl(-x)f(noinfo)p Fo('.)19
+b(The)14 b(messages)g(giv)o(en)h(b)o(y)f(`)p Fl(user)p Fo(')f(relate)i(to)e
+(errors)h(in)h(the)g(moun)o(t)e(maps,)h(so)0 2463 y(these)g(are)f(useful)i
+(when)g(new)f(maps)f(are)h(installed.)21 b(The)14 b(follo)o(wing)h(table)f
+(lists)g(the)g(syslog)g(priorites)g(used)h(for)0 2512 y(eac)o(h)g(of)g(the)g
+(message)g(t)o(yp)q(es.)0 2583 y Fl(fatal)120 b Fo(LOG)p 342
+2583 14 2 v 17 w(CRIT)0 2645 y Fl(error)g Fo(LOG)p 342 2645
+V 17 w(ERR)p eop
+%%Page: 20 22
+20 21 bop 15 -83 a Fo(SMM:13-20)889 b(4.4)15 b(BSD)g(Automoun)o(ter)f
+(Reference)j(Man)o(ual)0 158 y Fl(user)144 b Fo(LOG)p 342 158
+14 2 v 17 w(W)-5 b(ARNING)0 220 y Fl(warning)72 b Fo(LOG)p
+342 220 V 17 w(W)-5 b(ARNING)0 281 y Fl(info)144 b Fo(LOG)p
+342 281 V 17 w(INF)o(O)0 343 y Fl(debug)120 b Fo(LOG)p 342
+343 V 17 w(DEBUG)0 404 y Fl(map)168 b Fo(LOG)p 342 404 V 17
+w(DEBUG)0 466 y Fl(stats)120 b Fo(LOG)p 342 466 V 17 w(INF)o(O)62
+557 y(The)15 b(options)g(can)f(b)q(e)h(pre\014xed)h(b)o(y)e(the)h(string)f(`)
+p Fl(no)p Fo(')g(to)f(indicate)j(that)e(this)h(option)g(should)g(b)q(e)g
+(turned)g(o\013.)0 607 y(F)l(or)g(example,)g(to)g(obtain)g(all)h(but)g(`)p
+Fl(info)p Fo(')e(messages)g(the)h(option)h(`)p Fl(-x)e(all,noinfo)p
+Fo(')g(w)o(ould)h(b)q(e)h(used.)62 678 y(If)f Fp(Amd)i Fo(w)o(as)d(built)j
+(with)e(debugging)h(enabled)g(the)f Fl(debug)f Fo(option)i(is)f
+(automatically)g(enabled)i(regardless)0 728 y(of)e(the)g(command)g(line)i
+(options.)0 889 y Fq(4.13)32 b Fg(-y)15 b Ff(NIS-domain)62
+980 y Fo(Selects)d(an)e(alternate)g(NIS)h(domain.)19 b(This)11
+b(is)f(useful)i(for)e(debugging)h(and)f(cross-domain)h(shared)f(moun)o(ting.)
+0 1030 y(If)15 b(this)h(\015ag)f(is)h(sp)q(eci\014ed,)h Fp(Amd)g
+Fo(immediately)g(attempts)d(to)g(bind)j(to)d(a)h(serv)o(er)g(for)g(this)g
+(domain.)0 1192 y Fq(4.14)32 b Fg(-C)15 b Ff(cluster-name)62
+1283 y Fo(Sp)q(eci\014es)f(the)e(name)g(of)f(the)h(cluster)g(of)g(whic)o(h)g
+(the)g(lo)q(cal)h(mac)o(hine)f(is)h(a)e(mem)o(b)q(er.)19 b(The)12
+b(only)g(e\013ect)g(is)g(to)f(set)0 1333 y(the)k(v)m(ariable)i
+Fl(${cluster})p Fo(.)h(The)e Fp(cluster-name)j Fo(is)c(will)i(usually)g
+(obtained)f(b)o(y)f(running)h(another)f(command)0 1383 y(whic)o(h)k(uses)f(a)
+f(database)g(to)g(map)h(the)g(lo)q(cal)h(hostname)e(in)o(to)h(a)f(cluster)h
+(name.)28 b Fl(${cluster})16 b Fo(can)i(then)g(b)q(e)0 1433
+y(used)f(as)f(a)f(selector)i(to)e(restrict)h(moun)o(ting)h(of)e(replicated)j
+(data.)k(If)16 b(this)h(option)f(is)h(not)f(giv)o(en,)g Fl(${cluster})0
+1482 y Fo(has)f(the)g(same)g(v)m(alue)i(as)d Fl(${domain})p
+Fo(.)19 b(This)d(w)o(ould)f(b)q(e)h(used)g(as)f(follo)o(ws:)120
+1553 y Fl(amd)23 b(-C)h(`clustername`)e(...)0 1708 y Fq(4.15)32
+b Fg(-D)15 b Ff(opts)62 1799 y Fo(Con)o(trols)20 b(the)h(v)o(erb)q(osit)o(y)f
+(and)h(co)o(v)o(erage)f(of)g(the)g(debugging)i(trace;)h Fp(opts)f
+Fo(is)f(a)f(comma)g(separated)g(list)0 1849 y(of)g(debugging)h(options.)36
+b(The)20 b(\\-D")g(option)h(is)g(only)f(a)o(v)m(ailable)j(if)d
+Fp(Amd)j Fo(w)o(as)c(compiled)j(with)f(`)p Fl(-DDEBUG)p Fo('.)0
+1899 y(The)c(memory)g(debugging)h(facilities)h(are)e(only)h(a)o(v)m(ailable)h
+(if)f Fp(Amd)h Fo(w)o(as)d(compiled)j(with)f(`)p Fl(-DDEBUG_MEM)p
+Fo(')c(\(in)0 1948 y(addition)i(to)f(`)p Fl(-DDEBUG)p Fo('\).)62
+2019 y(The)j(most)f(common)g(options)h(to)f(use)h(are)f(`)p
+Fl(-D)d(trace)p Fo(')j(and)h(`)p Fl(-D)c(test)p Fo(')j(\(whic)o(h)h(turns)f
+(on)h(all)g(the)g(useful)0 2069 y(debug)e(options\).)k(See)15
+b(the)h(program)e(source)h(for)g(a)f(more)h(detailed)i(explanation)f(of)f
+(the)g(a)o(v)m(ailable)i(options.)0 2258 y Fm(5)41 b(Filesystem)13
+b(T)n(yp)r(es)62 2375 y Fo(T)l(o)i(moun)o(t)g(a)g(v)o(olume,)g
+Fp(Amd)i Fo(m)o(ust)e(b)q(e)h(told)f(the)g(t)o(yp)q(e)h(of)e(\014lesystem)i
+(to)f(b)q(e)h(used.)k(Eac)o(h)15 b(\014lesystem)h(t)o(yp)q(e)0
+2425 y(t)o(ypically)h(requires)f(additional)g(information)g(suc)o(h)f(as)g
+(the)g(\014leserv)o(er)h(name)f(for)g(NFS.)62 2496 y(F)l(rom)j(the)i(p)q(oin)
+o(t)f(of)g(view)g(of)g Fp(Amd)p Fo(,)g(a)g Fp(\014lesystem)h
+Fo(is)f(an)o(ything)g(that)g(can)g(resolv)o(e)g(an)g(incoming)h(name)0
+2545 y(lo)q(okup.)g(An)12 b(imp)q(ortan)o(t)f(feature)g(is)h(supp)q(ort)g
+(for)f(m)o(ultiple)j(\014lesystem)e(t)o(yp)q(es.)19 b(Some)12
+b(of)f(these)h(\014lesystems)g(are)0 2595 y(implemen)o(ted)k(in)g(the)f(lo)q
+(cal)g(k)o(ernel)h(and)f(some)f(on)g(remote)g(\014leserv)o(ers,)h(whilst)h
+(the)f(others)f(are)g(implemen)o(ted)0 2645 y(in)o(ternally)j(b)o(y)e
+Fp(Amd)p Fo(.)p eop
+%%Page: 21 23
+21 22 bop 0 -83 a Fo(Chapter)15 b(5:)k(Filesystem)d(T)o(yp)q(es)1145
+b(SMM:13-21)62 158 y(The)21 b(t)o(w)o(o)f(common)g(\014lesystem)i(t)o(yp)q
+(es)f(are)f(UFS)h(and)g(NFS.)g(F)l(our)f(other)h(user)g(accessible)h
+(\014lesystems)0 208 y(\(`)p Fl(link)p Fo(',)c(`)p Fl(program)p
+Fo(',)g(`)p Fl(auto)p Fo(')g(and)i(`)p Fl(direct)p Fo('\))d(are)i(also)g
+(implemen)o(ted)i(in)o(ternally)g(b)o(y)e Fp(Amd)i Fo(and)f(these)f(are)0
+258 y(describ)q(ed)d(b)q(elo)o(w.)21 b(There)14 b(are)h(t)o(w)o(o)e
+(additional)j(\014lesystem)f(t)o(yp)q(es)f(in)o(ternal)i(to)e
+Fp(Amd)i Fo(whic)o(h)f(are)g(not)f(directly)0 308 y(accessible)j(to)e(the)g
+(user)h(\(`)p Fl(inherit)p Fo(')d(and)j(`)p Fl(error)p Fo('\).)j(Their)d(use)
+f(is)h(describ)q(ed)i(since)e(they)g(ma)o(y)e(still)j(ha)o(v)o(e)e(an)0
+358 y(e\013ect)g(visible)i(to)e(the)g(user.)0 508 y Fq(5.1)33
+b(Net)n(w)n(ork)15 b(Filesystem)g(\(`)p Fg(type:=nfs)p Fq('\))62
+599 y Fo(The)h Fp(nfs)h Fo(\014lesystem)f(t)o(yp)q(e)f(pro)o(vides)h(access)f
+(to)f(Sun's)i(NFS.)0 670 y(The)f(follo)o(wing)h(options)g(m)o(ust)e(b)q(e)i
+(sp)q(eci\014ed:)0 751 y Fl(rhost)120 b Fo(the)17 b(remote)g(\014leserv)o
+(er.)26 b(This)17 b(m)o(ust)g(b)q(e)h(an)f(en)o(try)f(in)i(the)f(hosts)g
+(database.)25 b(IP)17 b(addresses)g(are)240 801 y(not)i(accepted.)34
+b(The)20 b(default)g(v)m(alue)h(is)f(tak)o(en)g(from)e(the)i(lo)q(cal)h(host)
+e(name)h(\()p Fl(${host})p Fo(\))e(if)i(no)240 851 y(other)15
+b(v)m(alue)h(is)g(sp)q(eci\014ed.)0 911 y Fl(rfs)168 b Fo(the)19
+b(remote)g(\014lesystem.)33 b(If)19 b(no)g(v)m(alue)i(is)f(sp)q(eci\014ed)h
+(for)e(this)g(option,)h(an)f(in)o(ternal)i(default)e(of)240
+961 y Fl(${path})14 b Fo(is)i(used.)62 1052 y(NFS)d(moun)o(ts)g(require)g(a)g
+(t)o(w)o(o)f(stage)g(pro)q(cess.)19 b(First,)13 b(the)g Fp(\014le)h(handle)j
+Fo(of)c(the)g(remote)f(\014le)i(system)f(m)o(ust)f(b)q(e)0
+1102 y(obtained)i(from)f(the)h(serv)o(er.)19 b(Then)14 b(a)f(moun)o(t)g
+(system)g(call)i(m)o(ust)d(b)q(e)j(done)f(on)f(the)h(lo)q(cal)g(system.)19
+b Fp(Amd)d Fo(k)o(eeps)0 1152 y(a)f(cac)o(he)g(of)g(\014le)h(handles)h(for)d
+(remote)h(\014le)h(systems.)k(The)15 b(cac)o(he)g(en)o(tries)h(ha)o(v)o(e)f
+(a)g(lifetime)i(of)d(a)h(few)g(min)o(utes.)62 1223 y(If)g(a)f(required)h
+(\014le)g(handle)h(is)f(not)f(in)h(the)f(cac)o(he,)g Fp(Amd)j
+Fo(sends)d(a)g(request)h(to)e(the)i(remote)e(serv)o(er)h(to)g(obtain)0
+1272 y(it.)19 b Fp(Amd)14 b(do)q(es)d(not)i Fo(w)o(ait)e(for)g(a)g(resp)q
+(onse;)i(it)f(notes)f(that)g(one)h(of)f(the)h(lo)q(cations)g(needs)h
+(retrying,)f(but)f(con)o(tin)o(ues)0 1322 y(with)17 b(an)o(y)f(remaining)h
+(lo)q(cations.)24 b(When)17 b(the)g(\014le)g(handle)h(b)q(ecomes)f(a)o(v)m
+(ailable,)h(and)e(assuming)h(none)g(of)f(the)0 1372 y(other)11
+b(lo)q(cations)h(w)o(as)e(successfully)j(moun)o(ted,)e Fp(Amd)j
+Fo(will)e(retry)f(the)g(moun)o(t.)18 b(This)12 b(mec)o(hanism)g(allo)o(ws)f
+(sev)o(eral)0 1422 y(NFS)17 b(\014lesystems)h(to)e(b)q(e)i(moun)o(ted)f(in)h
+(parallel.)28 b(The)17 b(\014rst)g(one)g(whic)o(h)h(resp)q(onds)g(with)g(a)f
+(v)m(alid)h(\014le)h(handle)0 1472 y(will)e(b)q(e)f(used.)0
+1542 y(An)f(NFS)h(en)o(try)e(migh)o(t)i(b)q(e:)120 1613 y Fl(jsp)47
+b(host!=charm;type:=nfs;rhost:)o(=charm;r)o(fs:=/ho)o(me/char)o(m;sublin)o
+(k:=jsp)62 1704 y Fo(The)16 b(moun)o(t)f(system)g(call)i(and)f(an)o(y)f
+(unmoun)o(t)h(attempts)f(are)g(alw)o(a)o(ys)g(done)h(in)g(a)g(new)g(task)e
+(to)h(a)o(v)o(oid)h(the)0 1754 y(p)q(ossibilt)o(y)h(of)e(blo)q(c)o(king)h
+Fp(Amd)p Fo(.)0 1914 y Fq(5.2)33 b(Net)n(w)n(ork)15 b(Host)f(Filesystem)h
+(\(`)p Fg(type:=host)p Fq('\))62 2006 y Fo(The)d Fp(host)g
+Fo(\014lesystem)g(allo)o(ws)g(access)g(to)f(the)g(en)o(tire)h(exp)q(ort)g
+(tree)f(of)g(an)h(NFS)g(serv)o(er.)18 b(The)12 b(implemen)o(tation)0
+2056 y(is)18 b(la)o(y)o(ered)g(ab)q(o)o(v)o(e)f(the)h(`)p Fl(nfs)p
+Fo(')e(implemen)o(tation)j(so)e(k)o(eep-aliv)o(es)i(w)o(ork)d(in)j(the)e
+(same)h(w)o(a)o(y)l(.)26 b(The)18 b(only)g(option)0 2105 y(whic)o(h)e(needs)g
+(to)f(sp)q(eci\014ed)i(is)f(`)p Fl(rhost)p Fo(')d(whic)o(h)j(is)g(the)f(name)
+h(of)e(the)i(\014leserv)o(er)g(to)e(moun)o(t.)62 2176 y(The)e(`)p
+Fl(host)p Fo(')e(\014lesystem)j(t)o(yp)q(e)e(w)o(orks)g(b)o(y)g(querying)i
+(the)e(moun)o(t)h(daemon)f(on)h(the)f(giv)o(en)h(\014leserv)o(er)h(to)e
+(obtain)0 2226 y(its)i(exp)q(ort)f(list.)19 b Fp(Amd)c Fo(then)d(obtains)h
+(\014lehandles)h(for)e(eac)o(h)h(of)e(the)i(exp)q(orted)f(\014lesystems.)20
+b(An)o(y)12 b(errors)g(at)g(this)0 2276 y(stage)17 b(cause)i(that)e
+(particular)i(\014lesystem)g(to)f(b)q(e)g(ignored.)30 b(Finally)19
+b(eac)o(h)g(\014lesystem)f(is)h(moun)o(ted.)29 b(Again,)0 2325
+y(errors)14 b(are)g(logged)g(but)h(ignored.)20 b(One)15 b(common)f(reason)g
+(for)g(moun)o(ts)g(to)g(fail)h(is)g(that)e(the)i(moun)o(t)f(p)q(oin)o(t)h(do)
+q(es)0 2375 y(not)e(exist.)19 b(Although)13 b Fp(Amd)i Fo(attempts)d(to)g
+(automatically)i(create)e(the)h(moun)o(t)g(p)q(oin)o(t,)g(it)g(ma)o(y)g(b)q
+(e)g(on)g(a)g(remote)0 2425 y(\014lesystem)j(to)e(whic)o(h)i
+Fp(Amd)i Fo(do)q(es)d(not)g(ha)o(v)o(e)g(write)g(p)q(ermission.)62
+2496 y(When)j(an)f(attempt)f(to)h(unmoun)o(t)g(a)g(`)p Fl(host)p
+Fo(')e(\014lesystem)j(moun)o(t)f(fails,)h Fp(Amd)h Fo(remoun)o(ts)e(an)o(y)f
+(\014lesystems)0 2545 y(whic)o(h)21 b(had)e(succesfully)j(b)q(een)f(unmoun)o
+(ted.)34 b(T)l(o)19 b(do)h(this)g Fp(Amd)i Fo(queries)e(the)g(moun)o(t)f
+(daemon)h(again)g(and)0 2595 y(obtains)d(a)g(fresh)g(cop)o(y)g(of)g(the)g
+(exp)q(ort)g(list.)26 b Fp(Amd)19 b Fo(then)f(tries)f(to)g(moun)o(t)f(an)o(y)
+h(exp)q(orted)g(\014lesystems)h(whic)o(h)0 2645 y(are)d(not)g(curren)o(tly)g
+(moun)o(ted.)p eop
+%%Page: 22 24
+22 23 bop 15 -83 a Fo(SMM:13-22)889 b(4.4)15 b(BSD)g(Automoun)o(ter)f
+(Reference)j(Man)o(ual)62 158 y(Sun's)22 b(automoun)o(ter)e(pro)o(vides)i(a)f
+(sp)q(ecial)i(`)p Fl(-hosts)p Fo(')d(map.)38 b(T)l(o)21 b(ac)o(hiev)o(e)h
+(the)f(same)g(e\013ect)g(with)h Fp(Amd)0 208 y Fo(requires)16
+b(t)o(w)o(o)e(steps.)20 b(First)14 b(a)h(moun)o(t)g(map)g(m)o(ust)g(b)q(e)h
+(created)f(as)g(follo)o(ws:)120 279 y Fl(/defaults)46 b
+(type:=host;fs:=${autodir}/${rh)o(ost}/ro)o(ot;rhos)o(t:=${key)o(})120
+329 y(*)238 b(opts:=rw,nosuid,grpid)0 420 y Fo(and)15 b(then)h(start)e
+Fp(Amd)j Fo(with)f(the)f(follo)o(wing)h(command)120 490 y Fl(amd)23
+b(/n)h(net.map)0 582 y Fo(where)17 b(`)p Fl(net.map)p Fo(')d(is)j(the)g(name)
+f(of)g(map)g(describ)q(ed)i(ab)q(o)o(v)o(e.)23 b(Note)16 b(that)g(the)g(v)m
+(alue)i(of)e Fl(${fs})g Fo(is)g(o)o(v)o(erridden)0 632 y(in)f(the)f(map.)19
+b(This)c(is)f(done)h(to)e(a)o(v)o(oid)h(a)g(clash)h(b)q(et)o(w)o(een)f(the)g
+(moun)o(t)g(tree)f(and)i(an)o(y)e(other)h(\014lesystem)h(already)0
+681 y(moun)o(ted)g(from)g(the)g(same)g(\014leserv)o(er.)62
+752 y(If)g(di\013eren)o(t)f(moun)o(t)g(options)g(are)g(needed)h(for)f
+(di\013eren)o(t)g(hosts)g(then)g(additional)i(en)o(tries)f(can)f(b)q(e)h
+(added)g(to)0 802 y(the)g(map,)g(for)f(example)120 872 y Fl(host2)166
+b(opts:=ro,nosuid,soft)0 964 y Fo(w)o(ould)16 b(soft)e(moun)o(t)h(`)p
+Fl(host2)p Fo(')e(read-only)l(.)0 1130 y Fq(5.3)33 b(Net)n(w)n(ork)15
+b(Filesystem)g(Group)h(\(`)p Fg(type:=nfsx)p Fq('\))62 1221
+y Fo(The)f Fp(nfsx)k Fo(\014lesystem)c(allo)o(ws)g(a)g(group)g(of)f
+(\014lesystems)i(to)e(b)q(e)h(moun)o(ted)g(from)g(a)f(single)i(NFS)f(serv)o
+(er.)20 b(The)0 1271 y(implemen)o(tation)c(is)g(la)o(y)o(ered)g(ab)q(o)o(v)o
+(e)e(the)i(`)p Fl(nfs)p Fo(')e(implemen)o(tation)i(so)f(k)o(eep-aliv)o(es)i
+(w)o(ork)d(in)i(the)f(same)g(w)o(a)o(y)l(.)62 1342 y(The)h(options)f(are)g
+(the)g(same)g(as)g(for)f(the)i(`)p Fl(nfs)p Fo(')e(\014lesystem)i(with)f(one)
+g(di\013erence.)0 1412 y(The)g(follo)o(wing)h(options)g(m)o(ust)e(b)q(e)i(sp)
+q(eci\014ed:)0 1483 y Fl(rhost)120 b Fo(the)17 b(remote)g(\014leserv)o(er.)26
+b(This)17 b(m)o(ust)g(b)q(e)h(an)f(en)o(try)f(in)i(the)f(hosts)g(database.)25
+b(IP)17 b(addresses)g(are)240 1533 y(not)i(accepted.)34 b(The)20
+b(default)g(v)m(alue)h(is)f(tak)o(en)g(from)e(the)i(lo)q(cal)h(host)e(name)h
+(\()p Fl(${host})p Fo(\))e(if)i(no)240 1582 y(other)15 b(v)m(alue)h(is)g(sp)q
+(eci\014ed.)0 1644 y Fl(rfs)168 b Fo(as)15 b(a)f(list)i(of)f(\014lesystems)g
+(to)f(moun)o(t.)20 b(The)15 b(list)h(is)f(in)h(the)f(form)f(of)h(a)f(comma)h
+(separated)f(strings.)0 1736 y(F)l(or)h(example:)120 1806 y
+Fl(pub)143 b(type:=nfsx;rhost:=gould;)o(\\)120 1856 y
+(rfs:=/public,/,graphics,us)o(enet;fs)o(:=${auto)o(dir}/${)o(rhost}/)o(root)
+62 1947 y Fo(The)14 b(\014rst)g(string)f(de\014nes)i(the)f(ro)q(ot)f(of)g
+(the)h(tree,)g(and)g(is)g(applied)i(as)d(a)h(pre\014x)g(to)f(the)h(remaining)
+h(mem)o(b)q(ers)0 1997 y(of)e(the)h(list)h(whic)o(h)g(de\014ne)g(the)e
+(individual)k(\014lesystems.)j(The)15 b(\014rst)e(string)h(is)g
+Fp(not)g Fo(used)h(as)e(a)h(\014lesystem)g(name.)0 2047 y(A)h(parallel)h(op)q
+(eration)f(is)h(used)f(to)g(determine)h(the)e(lo)q(cal)i(moun)o(t)f(p)q(oin)o
+(ts)g(to)f(ensure)i(a)e(consisten)o(t)i(la)o(y)o(out)e(of)g(a)0
+2097 y(tree)h(of)g(moun)o(ts.)62 2167 y(Here,)20 b(the)e Fp(three)k
+Fo(\014lesystems,)d(`)p Fl(/public)p Fo(',)f(`)p Fl(/public/graphics)p
+Fo(')d(and)k(`)p Fl(/public/usenet)p Fo(',)d(w)o(ould)j(b)q(e)0
+2217 y(moun)o(ted.)62 2288 y(A)g(lo)q(cal)g(moun)o(t)f(p)q(oin)o(t,)h
+Fl(${fs})p Fo(,)f Fp(m)o(ust)h Fo(b)q(e)g(sp)q(eci\014ed.)32
+b(The)19 b(default)g(lo)q(cal)g(moun)o(t)f(p)q(oin)o(t)h(will)h(not)e(w)o
+(ork)0 2338 y(correctly)d(in)h(the)g(general)f(case.)20 b(A)c(suggestion)f
+(is)g(to)g(use)h(`)p Fl(fs:=${autodir}/${rho)o(st}/roo)o(t)p
+Fo('.)0 2504 y Fq(5.4)33 b(Unix)16 b(Filesystem)g(\(`)p Fg(type:=ufs)p
+Fq('\))62 2595 y Fo(The)22 b Fp(ufs)i Fo(\014lesystem)e(t)o(yp)q(e)g(pro)o
+(vides)g(access)g(to)f(the)h(system's)e(standard)i(disk)g
+(\014lesystem|usually)i(a)0 2645 y(deriv)m(ativ)o(e)16 b(of)f(the)h(Berk)o
+(eley)g(F)l(ast)e(Filesystem.)p eop
+%%Page: 23 25
+23 24 bop 0 -83 a Fo(Chapter)15 b(5:)k(Filesystem)d(T)o(yp)q(es)1145
+b(SMM:13-23)0 158 y(The)15 b(follo)o(wing)h(option)g(m)o(ust)e(b)q(e)i(sp)q
+(eci\014ed:)0 246 y Fl(dev)168 b Fo(the)15 b(blo)q(c)o(k)h(sp)q(ecial)h
+(device)g(to)d(b)q(e)i(moun)o(ted.)62 338 y(A)g(UFS)f(en)o(try)f(migh)o(t)i
+(b)q(e:)120 408 y Fl(jsp)71 b(host==charm;type:=ufs;dev:=)o(/dev/xd0)o
+(g;subli)o(nk:=jsp)0 617 y Fq(5.5)33 b(Program)15 b(Filesystem)g(\(`)p
+Fg(type:=program)p Fq('\))62 709 y Fo(The)21 b Fp(program)f
+Fo(\014lesystem)i(t)o(yp)q(e)f(allo)o(ws)g(a)f(program)g(to)g(b)q(e)i(run)f
+(whenev)o(er)g(a)g(moun)o(t)f(or)h(unmoun)o(t)f(is)0 758 y(required.)43
+b(This)24 b(allo)o(ws)f(easy)f(addition)i(of)e(supp)q(ort)h(for)f(other)g
+(\014lesystem)i(t)o(yp)q(es,)g(suc)o(h)f(as)f(MIT's)g(Re-)0
+808 y(mote)16 b(Virtual)h(Disk)g(\(R)-5 b(VD\))16 b(whic)o(h)h(has)g(a)f
+(programmatic)g(in)o(terface)g(via)h(the)g(commands)f(`)p Fl(rvdmount)p
+Fo(')f(and)0 858 y(`)p Fl(rvdunmount)p Fo('.)0 929 y(The)g(follo)o(wing)h
+(options)g(m)o(ust)e(b)q(e)i(sp)q(eci\014ed:)0 1017 y Fl(mount)120
+b Fo(the)15 b(program)f(whic)o(h)i(will)h(p)q(erform)e(the)h(moun)o(t.)0
+1084 y Fl(unmount)72 b Fo(the)15 b(program)f(whic)o(h)i(will)h(p)q(erform)e
+(the)h(unmoun)o(t.)62 1175 y(The)f(exit)h(co)q(de)f(from)f(these)h(t)o(w)o(o)
+f(programs)f(is)i(in)o(terpreted)h(as)e(a)h(Unix)h(error)e(co)q(de.)20
+b(As)15 b(usual,)g(exit)g(co)q(de)0 1225 y(zero)i(indicates)h(success.)26
+b(T)l(o)17 b(execute)g(the)g(program)f Fp(Amd)j Fo(splits)f(the)f(string)g
+(on)g(whitespace)h(to)e(create)h(an)0 1275 y(arra)o(y)h(of)h(substrings.)33
+b(Single)21 b(quotes)e(`)p Fl(')p Fo(')g(can)g(b)q(e)h(used)g(to)f(quote)g
+(whitespace)i(if)f(that)e(is)i(required)h(in)f(an)0 1325 y(argumen)o(t.)f
+(There)d(is)f(no)g(w)o(a)o(y)g(to)f(escap)q(e)i(or)f(c)o(hange)g(the)g(quote)
+g(c)o(haracter.)62 1395 y(T)l(o)d(run)g(the)f(program)g(`)p
+Fl(rvdmount)p Fo(')f(with)i(a)f(host)g(name)h(and)g(\014lesystem)g(as)g
+(argumen)o(ts)e(w)o(ould)i(b)q(e)h(sp)q(eci\014ed)0 1445 y(b)o(y)i(`)p
+Fl(mount:="/etc/rvdmount)d(rvdmount)i(fserver)g(${path}")p
+Fo('.)62 1516 y(The)j(\014rst)f(elemen)o(t)h(in)g(the)f(arra)o(y)f(is)i(tak)o
+(en)f(as)g(the)g(pathname)h(of)f(the)g(program)f(to)h(execute.)24
+b(The)16 b(other)0 1566 y(mem)o(b)q(ers)g(of)f(the)h(arra)o(y)e(form)h(the)h
+(argumen)o(t)f(v)o(ector)g(to)g(b)q(e)h(passed)g(to)f(the)h(program,)e
+Fp(including)19 b(argumen)o(t)0 1615 y(zero)p Fo(.)27 b(This)18
+b(means)f(that)g(the)h(split)g(string)g(m)o(ust)e(ha)o(v)o(e)i(at)e(least)i
+(t)o(w)o(o)e(elemen)o(ts.)28 b(The)17 b(program)g(is)h(directly)0
+1665 y(executed)23 b(b)o(y)f Fp(Amd)p Fo(,)h(not)f(via)g(a)f(shell.)42
+b(This)22 b(means)g(that)g(scripts)g(m)o(ust)f(b)q(egin)i(with)g(a)e
+Fl(#!)h Fo(in)o(terpreter)0 1715 y(sp)q(eci\014cation.)62 1786
+y(If)c(a)g(\014lesystem)g(t)o(yp)q(e)g(is)g(to)f(b)q(e)i(hea)o(vily)g(used,)f
+(it)g(ma)o(y)f(b)q(e)i(w)o(orth)o(while)f(adding)g(a)g(new)f(\014lesystem)i
+(t)o(yp)q(e)0 1835 y(in)o(to)c Fp(Amd)p Fo(,)g(but)h(for)e(most)g(uses)i(the)
+f(program)f(\014lesystem)i(should)g(su\016ce.)62 1906 y(When)k(the)f(program)
+f(is)i(run,)g(standard)e(input)i(and)g(standard)e(error)h(are)g(inherited)i
+(from)d(the)h(curren)o(t)0 1956 y(v)m(alues)f(used)g(b)o(y)f
+Fp(Amd)p Fo(.)26 b(Standard)17 b(output)g(is)h(a)f(duplicate)i(of)e(standard)
+f(error.)25 b(The)18 b(v)m(alue)g(sp)q(eci\014ed)i(with)0 2006
+y(the)15 b(\\-l")h(command)f(line)i(option)e(has)g(no)g(e\013ect)g(on)g
+(standard)g(error.)0 2213 y Fq(5.6)33 b(Sym)n(b)r(olic)17 b(Link)g
+(Filesystem)f(\(`)p Fg(type:=link)p Fq('\))62 2305 y Fo(Eac)o(h)k
+(\014lesystem)g(t)o(yp)q(e)f(creates)h(a)f(sym)o(b)q(olic)h(link)h(to)e(p)q
+(oin)o(t)h(from)f(the)h(v)o(olume)g(name)f(to)g(the)h(ph)o(ysical)0
+2355 y(moun)o(t)15 b(p)q(oin)o(t.)22 b(The)16 b(`)p Fl(link)p
+Fo(')e(\014lesystem)i(do)q(es)g(the)g(same)f(without)h(an)o(y)f(other)g(side)
+i(e\013ects.)j(This)d(allo)o(ws)e(an)o(y)0 2404 y(part)g(of)f(the)i(mac)o
+(hines)g(name)f(space)g(to)g(b)q(e)h(accessed)g(via)f Fp(Amd)p
+Fo(.)62 2475 y(One)h(common)f(use)g(for)f(the)h(symlink)i(\014lesystem)e(is)h
+(`)p Fl(/homes)p Fo(')d(whic)o(h)j(can)f(b)q(e)h(made)f(to)f(con)o(tain)h(an)
+g(en)o(try)0 2525 y(for)e(eac)o(h)g(user)g(whic)o(h)i(p)q(oin)o(ts)e(to)g
+(their)h(\(auto-moun)o(ted\))e(home)h(directory)l(.)20 b(Although)14
+b(this)f(ma)o(y)g(seem)h(rather)0 2575 y(exp)q(ensiv)o(e,)i(it)g(pro)o(vides)
+g(a)e(great)h(deal)h(of)e(administrativ)o(e)i(\015exibili)q(t)o(y)l(.)0
+2645 y(The)f(follo)o(wing)h(option)g(m)o(ust)e(b)q(e)i(de\014ned:)p
+eop
+%%Page: 24 26
+24 25 bop 15 -83 a Fo(SMM:13-24)889 b(4.4)15 b(BSD)g(Automoun)o(ter)f
+(Reference)j(Man)o(ual)0 158 y Fl(fs)192 b Fo(The)16 b(v)m(alue)h(of)f
+Fp(fs)h Fo(option)f(sp)q(eci\014es)i(the)e(destination)g(of)g(the)g(link,)h
+(as)e(mo)q(di\014ed)i(b)o(y)f(the)g Fp(sublink)240 208 y Fo(option.)k(If)14
+b Fp(sublink)k Fo(is)c(non-n)o(ull,)i(it)e(is)g(app)q(ended)i(to)d
+Fl(${fs}/)g Fo(and)h(the)f(resulting)i(string)f(is)g(used)240
+258 y(as)h(the)g(target.)62 349 y(The)g(`)p Fl(link)p Fo(')f(\014lesystem)h
+(can)g(b)q(e)h(though)f(of)f(as)g(iden)o(tical)j(to)d(the)h(`)p
+Fl(ufs)p Fo(')f(\014lesystem)h(but)g(without)g(actually)0 399
+y(moun)o(ting)g(an)o(ything.)62 470 y(An)h(example)g(en)o(try)f(migh)o(t)g(b)
+q(e:)120 540 y Fl(jsp)71 b(host==charm;type:=link;fs:=)o(/home/ch)o(arm;sub)o
+(link:=j)o(sp)62 632 y Fo(whic)o(h)16 b(w)o(ould)g(return)f(a)g(sym)o(b)q
+(olic)h(link)h(p)q(oin)o(ting)f(to)f(`)p Fl(/home/charm/jsp)p
+Fo('.)0 792 y Fq(5.7)33 b(Sym)n(b)r(olic)17 b(Link)g(Filesystem)f(I)r(I)f
+(\(`)p Fg(type:=link)p Fq('\))62 884 y Fo(The)h(`)p Fl(linkx)p
+Fo(')e(\014lesystem)i(t)o(yp)q(e)f(is)h(iden)o(tical)h(to)e(`)p
+Fl(link)p Fo(')f(with)i(the)f(exception)i(that)d(the)i(target)e(of)h(the)h
+(link)0 933 y(m)o(ust)f(exist.)20 b(Existence)c(is)g(c)o(hec)o(k)o(ed)f(with)
+h(the)f(`)p Fl(lstat)p Fo(')f(system)h(call.)62 1004 y(The)f(`)p
+Fl(linkx)p Fo(')e(\014lesystem)j(t)o(yp)q(e)f(is)g(particularly)h(useful)g
+(for)e(wildcard)i(map)e(en)o(tries.)20 b(In)15 b(this)f(case,)f(a)h(list)g
+(of)0 1054 y(p)q(ossible)j(targets)d(can)h(b)q(e)h(giv)o(e)f(and)h
+Fp(Amd)h Fo(will)g(c)o(ho)q(ose)e(the)g(\014rst)g(one)h(whic)o(h)g(exists)f
+(on)g(the)g(lo)q(cal)i(mac)o(hine.)0 1213 y Fq(5.8)33 b(Automoun)n(t)15
+b(Filesystem)h(\(`)p Fg(type:=auto)p Fq('\))62 1305 y Fo(The)j
+Fp(auto)g Fo(\014lesystem)g(t)o(yp)q(e)f(creates)g(a)g(new)g(automoun)o(t)f
+(p)q(oin)o(t)h(b)q(elo)o(w)h(an)f(existing)h(automoun)o(t)e(p)q(oin)o(t.)0
+1354 y(T)l(op-lev)o(el)g(automoun)o(t)e(p)q(oin)o(ts)h(app)q(ear)f(as)h
+(system)f(moun)o(t)g(p)q(oin)o(ts.)22 b(An)16 b(automoun)o(t)e(moun)o(t)h(p)q
+(oin)o(t)h(can)g(also)0 1404 y(app)q(ear)h(as)g(a)g(sub-directory)h(of)f(an)g
+(existing)i(automoun)o(t)d(p)q(oin)o(t.)26 b(This)18 b(allo)o(ws)g(some)e
+(additional)j(structure)0 1454 y(to)c(b)q(e)g(added,)h(for)e(example)i(to)f
+(mimic)h(the)g(moun)o(t)e(tree)h(of)g(another)g(mac)o(hine.)62
+1525 y(The)h(follo)o(wing)g(options)f(ma)o(y)f(b)q(e)i(sp)q(eci\014ed:)0
+1605 y Fl(cache)120 b Fo(sp)q(eci\014es)19 b(whether)e(the)g(data)f(in)i
+(this)g(moun)o(t-map)e(should)i(b)q(e)g(cac)o(hed.)25 b(The)18
+b(default)f(v)m(alue)h(is)240 1655 y(`)p Fl(none)p Fo(',)13
+b(in)i(whic)o(h)g(case)f(no)g(cac)o(hing)h(is)g(done)f(in)h(order)f(to)g
+(conserv)o(e)g(memory)l(.)19 b(Ho)o(w)o(ev)o(er,)13 b(b)q(etter)240
+1705 y(p)q(erformance)i(and)h(reliabilit)o(y)h(can)f(b)q(e)g(obtained)f(b)o
+(y)h(cac)o(hing)f(some)g(or)g(all)h(of)f(a)g(moun)o(t-map.)240
+1765 y(If)f(the)g(cac)o(he)g(option)g(sp)q(eci\014es)h(`)p
+Fl(all)p Fo(',)e(the)g(en)o(tire)i(map)e(is)h(en)o(umerated)g(when)g(the)g
+(moun)o(t)f(p)q(oin)o(t)240 1815 y(is)j(created.)240 1875 y(If)h(the)g(cac)o
+(he)g(option)h(sp)q(eci\014es)g(`)p Fl(inc)p Fo(',)e(cac)o(hing)i(is)f(done)g
+(incremen)o(tally)i(as)e(and)g(when)g(data)f(is)240 1925 y(required.)k(Some)
+13 b(map)f(t)o(yp)q(es)h(do)g(not)f(supp)q(ort)h(cac)o(he)g(mo)q(de)f(`)p
+Fl(all)p Fo(',)g(in)i(whic)o(h)f(case)g(`)p Fl(inc)p Fo(')e(is)i(used)240
+1975 y(whenev)o(er)j(`)p Fl(all)p Fo(')e(is)h(requested.)240
+2035 y(Cac)o(hing)h(can)f(b)q(e)h(en)o(tirely)g(disabled)h(b)o(y)e(using)h
+(cac)o(he)g(mo)q(de)f(`)p Fl(none)p Fo('.)240 2095 y(If)f(the)g(cac)o(he)h
+(option)f(sp)q(eci\014es)h(`)p Fl(regexp)p Fo(')e(then)h(the)g(en)o(tire)h
+(map)e(will)j(b)q(e)f(en)o(umerated)f(and)g(eac)o(h)240 2145
+y(k)o(ey)i(will)i(b)q(e)g(treated)d(as)i(an)f(egrep-st)o(yle)h(regular)f
+(expression.)25 b(The)17 b(order)f(in)h(whic)o(h)g(a)f(cac)o(hed)240
+2195 y(map)f(is)i(searc)o(hed)f(do)q(es)f(not)h(corresp)q(ond)g(to)f(the)g
+(ordering)h(in)h(the)f(source)f(map)h(so)f(the)h(regular)240
+2245 y(expressions)g(should)g(b)q(e)g(m)o(utually)g(exclusiv)o(e)h(to)d(a)o
+(v)o(oid)h(confusion.)240 2305 y(Eac)o(h)i(moun)o(t)g(map)g(t)o(yp)q(e)h(has)
+f(a)g(default)h(cac)o(he)g(t)o(yp)q(e,)g(usually)g(`)p Fl(inc)p
+Fo(',)f(whic)o(h)h(can)g(b)q(e)g(selected)240 2355 y(b)o(y)d(sp)q(ecifying)i
+(`)p Fl(mapdefault)p Fo('.)240 2415 y(The)e(cac)o(he)g(mo)q(de)g(for)f(a)g
+(moun)o(t)g(map)h(can)g(only)g(b)q(e)g(selected)h(on)f(the)f(command)h(line.)
+21 b(Starting)240 2465 y Fp(Amd)c Fo(with)f(the)f(command:)360
+2525 y Fl(amd)23 b(/homes)g(hesiod.homes)g(-cache:=inc)240
+2595 y Fo(will)17 b(cause)f(`)p Fl(/homes)p Fo(')d(to)i(b)q(e)h(automoun)o
+(ted)f(using)h(the)f Fp(Hesio)q(d)j Fo(name)e(serv)o(er)f(with)g(lo)q(cal)i
+(incre-)240 2645 y(men)o(tal)e(cac)o(hing)h(of)f(all)h(succesfully)h(resolv)o
+(ed)f(names.)p eop
+%%Page: 25 27
+25 26 bop 0 -83 a Fo(Chapter)15 b(5:)k(Filesystem)d(T)o(yp)q(es)1145
+b(SMM:13-25)240 158 y(All)21 b(cac)o(hed)f(data)g(is)g(forgotten)e(whenev)o
+(er)j Fp(Amd)h Fo(receiv)o(es)e(a)g(`)p Fl(SIGHUP)p Fo(')e(signal)j(and,)f
+(if)h(cac)o(he)240 208 y(`)p Fl(all)p Fo(')14 b(mo)q(de)i(w)o(as)f(selected,)
+i(the)e(cac)o(he)h(will)i(b)q(e)e(reloaded.)22 b(This)16 b(can)g(b)q(e)g
+(used)g(to)f(inform)h Fp(Amd)240 258 y Fo(that)i(a)g(map)g(has)g(b)q(een)h
+(up)q(dated.)30 b(In)19 b(addition,)h(whenev)o(er)f(a)f(cac)o(he)g(lo)q(okup)
+h(fails)g(and)g Fp(Amd)240 308 y Fo(needs)g(to)f(examine)h(a)f(map,)h(the)f
+(map's)g(mo)q(dify)h(time)g(is)g(examined.)31 b(If)18 b(the)h(cac)o(he)f(is)h
+(out)f(of)240 358 y(date)d(with)h(resp)q(ect)f(to)g(the)g(map)g(then)h(it)f
+(is)h(\015ushed)g(as)f(if)h(a)e(`)p Fl(SIGHUP)p Fo(')g(had)h(b)q(een)i
+(receiv)o(ed.)240 422 y(An)c(additional)g(option)g(\(`)p Fl(sync)p
+Fo('\))d(ma)o(y)i(b)q(e)h(sp)q(eci\014ed)h(to)e(force)g Fp(Amd)i
+Fo(to)e(c)o(hec)o(k)g(the)h(map's)e(mo)q(dify)240 472 y(time)h(whenev)o(er)h
+(a)f(cac)o(hed)g(en)o(try)g(is)g(b)q(eing)i(used.)19 b(F)l(or)11
+b(example,)i(an)f(incremen)o(tal,)i(sync)o(hronised)240 522
+y(cac)o(he)h(w)o(ould)h(b)q(e)g(created)f(b)o(y)g(the)h(follo)o(wing)f
+(command:)360 587 y Fl(amd)23 b(/homes)g(hesiod.homes)g(-cache:=inc,sync)0
+652 y(fs)192 b Fo(sp)q(eci\014es)17 b(the)e(name)h(of)e(the)i(moun)o(t)e(map)
+h(to)g(use)g(for)g(the)g(new)h(moun)o(t)e(p)q(oin)o(t.)240
+716 y(Arguably)g(this)g(should)g(ha)o(v)o(e)f(b)q(een)i(sp)q(eci\014ed)h
+(with)d(the)h Fl(${rfs})f Fo(option)g(but)h(w)o(e)f(are)g(no)o(w)g(stuc)o(k)
+240 766 y(with)j(it)f(due)h(to)e(historical)j(acciden)o(t.)0
+831 y Fl(pref)144 b Fo(alters)13 b(the)h(name)f(that)g(is)h(lo)q(ok)o(ed)g
+(up)g(in)g(the)g(moun)o(t)f(map.)19 b(If)13 b Fl(${pref})p
+Fo(,)g(the)g Fp(pre\014x)p Fo(,)h(is)g(non-n)o(ull)240 881
+y(then)i(it)f(is)h(prep)q(ended)h(to)d(the)i(name)f(requested)g(b)o(y)h(the)f
+(k)o(ernel)h Fp(b)q(efore)i Fo(the)d(map)g(is)h(searc)o(hed.)62
+972 y(The)22 b(serv)o(er)e(`)p Fl(dylan.doc.ic.ac.uk)p Fo(')e(has)j(t)o(w)o
+(o)f(user)h(disks:)33 b(`)p Fl(/dev/dsk/2s0)p Fo(')18 b(and)k(`)p
+Fl(/dev/dsk/5s0)p Fo('.)0 1022 y(These)e(are)f(accessed)h(as)f(`)p
+Fl(/home/dylan/dk2)p Fo(')e(and)i(`)p Fl(/home/dylan/dk5)p
+Fo(')e(resp)q(ectiv)o(ely)l(.)35 b(Since)20 b(`)p Fl(/home)p
+Fo(')e(is)0 1072 y(already)d(an)h(automoun)o(t)e(p)q(oin)o(t,)h(this)h
+(naming)f(is)h(ac)o(hiev)o(ed)g(with)g(the)f(follo)o(wing)h(map)f(en)o
+(tries:)120 1142 y Fl(dylan)190 b(type:=auto;fs:=${map};pref:=)o(${key}/)120
+1192 y(dylan/dk2)94 b(type:=ufs;dev:=/dev/dsk/2s0)120 1242
+y(dylan/dk5)g(type:=ufs;dev:=/dev/dsk/5s0)0 1432 y Fq(5.9)33
+b(Direct)15 b(Automoun)n(t)h(Filesystem)g(\(`)p Fg(type:=direct)p
+Fq(')o(\))62 1523 y Fo(The)h Fp(direct)i Fo(\014lesystem)e(is)h(almost)e
+(iden)o(tical)j(to)d(the)h(automoun)o(t)f(\014lesystem.)25
+b(Instead)18 b(of)e(app)q(earing)i(to)0 1573 y(b)q(e)e(a)f(directory)g(of)g
+(moun)o(t)f(p)q(oin)o(ts,)h(it)h(app)q(ears)f(as)g(a)f(sym)o(b)q(olic)j(link)
+f(to)f(a)g(moun)o(ted)g(\014lesystem.)20 b(The)15 b(moun)o(t)0
+1622 y(is)f(done)h(at)e(the)h(time)g(the)g(link)h(is)f(accessed.)20
+b(See)15 b(Section)g(5.8)d([Automoun)o(t)h(Filesystem],)h(page)28
+b(SMM:13-24)0 1672 y(for)15 b(a)f(list)i(of)f(required)i(options.)62
+1743 y(Direct)c(automoun)o(t)e(p)q(oin)o(ts)i(are)g(created)f(b)o(y)h(sp)q
+(ecifying)h(the)f(`)p Fl(direct)p Fo(')e(\014lesystem)i(t)o(yp)q(e)g(on)f
+(the)h(command)0 1793 y(line:)120 1863 y Fl(amd)23 b(...)h(/usr/man)f
+(auto.direct)f(-type:=direct)62 1955 y Fo(where)16 b(`)p Fl(auto.direct)p
+Fo(')d(w)o(ould)i(con)o(tain)h(an)f(en)o(try)f(suc)o(h)i(as:)120
+2025 y Fl(usr/man)94 b(-type:=nfs;rfs:=/usr/man)21 b(\\)382
+2075 y(rhost:=man-server1)46 b(rhost:=man-server2)62 2166 y
+Fo(In)19 b(this)f(example,)h(`)p Fl(man-server1)p Fo(')d(and)i(`)p
+Fl(man-server2)p Fo(')d(are)j(\014le)h(serv)o(ers)e(whic)o(h)i(exp)q(ort)f
+(copies)g(of)g(the)0 2216 y(man)o(ual)e(pages.)22 b(Note)16
+b(that)f(the)i(k)o(ey)f(whic)o(h)g(is)h(lo)q(ok)o(ed)f(up)h(is)g(the)f(name)g
+(of)f(the)i(automoun)o(t)d(p)q(oin)o(t)j(without)0 2266 y(the)e(leading)i(`)p
+Fl(/)p Fo('.)0 2454 y Fq(5.10)32 b(Union)16 b(Filesystem)g(\(`)p
+Fg(type:=union)p Fq('\))62 2545 y Fo(The)21 b Fp(union)g Fo(\014lesystem)g(t)
+o(yp)q(e)f(allo)o(ws)h(the)f(con)o(ten)o(ts)g(of)g(sev)o(eral)g(directories)i
+(to)d(b)q(e)i(merged)f(and)h(made)0 2595 y(visible)16 b(in)f(a)f(single)h
+(directory)l(.)20 b(This)14 b(can)g(b)q(e)h(used)g(to)e(o)o(v)o(ercome)g(one)
+h(of)g(the)g(ma)s(jor)e(limitations)j(of)f(the)g(Unix)0 2645
+y(moun)o(t)h(mec)o(hanism)h(whic)o(h)g(only)f(allo)o(ws)h(complete)g
+(directories)g(to)e(b)q(e)i(moun)o(ted.)p eop
+%%Page: 26 28
+26 27 bop 15 -83 a Fo(SMM:13-26)889 b(4.4)15 b(BSD)g(Automoun)o(ter)f
+(Reference)j(Man)o(ual)62 158 y(F)l(or)g(example,)i(supp)q(osing)g(`)p
+Fl(/tmp)p Fo(')e(and)h(`)p Fl(/var/tmp)p Fo(')d(w)o(ere)j(to)f(b)q(e)h
+(merged)g(in)o(to)g(a)f(new)h(directory)g(called)0 208 y(`)p
+Fl(/mtmp)p Fo(',)j(with)g(\014les)h(in)g(`)p Fl(/var/tmp)p
+Fo(')e(taking)h(precedence.)39 b(The)22 b(follo)o(wing)g(command)f(could)h(b)
+q(e)g(used)g(to)0 258 y(ac)o(hiev)o(e)16 b(this)f(e\013ect:)120
+329 y Fl(amd)23 b(...)h(/mtmp)f(union:/tmp:/var/tmp)e(-type:=union)62
+420 y Fo(Curren)o(tly)l(,)12 b(the)f(unioned)h(directories)g(m)o(ust)f
+Fp(not)g Fo(b)q(e)h(automoun)o(ted.)18 b(That)10 b(w)o(ould)h(cause)g(a)g
+(deadlo)q(c)o(k.)19 b(This)0 470 y(seriously)d(limits)h(the)e(curren)o(t)g
+(usefulness)i(of)e(this)h(\014lesystem)f(t)o(yp)q(e)h(and)f(the)h(problem)g
+(will)g(b)q(e)g(addressed)g(in)0 519 y(a)f(future)g(release)h(of)f
+Fp(Amd)p Fo(.)62 590 y(Files)20 b(created)e(in)i(the)e(union)i(directory)e
+(are)h(actually)g(created)f(in)i(the)e(last)g(named)h(directory)l(.)30
+b(This)19 b(is)0 640 y(done)h(b)o(y)g(creating)f(a)h(wildcard)h(en)o(try)e
+(whic)o(h)i(p)q(oin)o(ts)f(to)f(the)g(correct)h(directory)l(.)33
+b(The)20 b(wildcard)h(en)o(try)e(is)0 690 y(visible)e(if)f(the)f(union)i
+(directory)e(is)h(listed,)g(so)f(allo)o(wing)h(y)o(ou)f(to)f(see)i(whic)o(h)g
+(directory)f(has)g(priorit)o(y)l(.)62 760 y(The)j(\014les)g(visible)h(in)f
+(the)f(union)h(directory)f(are)g(computed)g(at)g(the)g(time)g
+Fp(Amd)i Fo(is)f(started,)e(and)i(are)e(not)0 810 y(k)o(ept)c(upto)q(date)g
+(with)g(resp)q(ect)h(to)e(the)h(underlying)i(directories.)20
+b(Similarly)l(,)15 b(if)d(a)g(link)h(is)g(remo)o(v)o(ed,)f(for)f(example)0
+860 y(with)16 b(the)f(`)p Fl(rm)p Fo(')f(command,)h(it)g(will)i(b)q(e)f(lost)
+f(forev)o(er.)0 1019 y Fq(5.11)32 b(Error)16 b(Filesystem)g(\(`)p
+Fg(type:=error)p Fq('\))62 1111 y Fo(The)i Fp(error)i Fo(\014lesystem)e(t)o
+(yp)q(e)f(is)h(used)g(in)o(ternally)h(as)e(a)g(catc)o(h-all)h(in)g(the)f
+(case)h(where)f(none)h(of)f(the)g(other)0 1160 y(\014lesystems)g(w)o(as)e
+(selected,)i(or)e(some)h(other)g(error)f(o)q(ccurred.)23 b(Lo)q(okups)16
+b(and)h(moun)o(ts)e(alw)o(a)o(ys)g(fail)i(with)f(\\No)0 1210
+y(suc)o(h)g(\014le)g(or)e(directory".)20 b(All)d(other)e(op)q(erations)g
+(trivially)i(succeed.)62 1281 y(The)f(error)e(\014lesystem)i(is)g(not)f
+(directly)h(accessible.)0 1440 y Fq(5.12)32 b(T)-6 b(op-lev)n(el)17
+b(Filesystem)f(\(`)p Fg(type:=toplvl)p Fq('\))62 1532 y Fo(The)23
+b Fp(toplvl)i Fo(\014lesystems)d(is)h(deriv)o(ed)g(from)f(the)g(`)p
+Fl(auto)p Fo(')f(\014lesystem)i(and)f(is)h(used)g(to)e(moun)o(t)h(the)g(top-)
+0 1581 y(lev)o(el)17 b(automoun)o(t)d(no)q(des.)21 b(Requests)16
+b(of)f(this)g(t)o(yp)q(e)h(are)f(automatically)h(generated)f(from)g(the)g
+(command)g(line)0 1631 y(argumen)o(ts)f(and)i(can)f(also)g(b)q(e)h(passed)g
+(in)g(b)o(y)f(using)h(the)f(\\-M")f(option)i(of)f(the)g Fp(Amq)h
+Fo(command.)0 1787 y Fq(5.13)32 b(Ro)r(ot)15 b(Filesystem)62
+1879 y Fo(The)j Fp(ro)q(ot)g Fo(\(`)p Fl(type:=root)p Fo('\))e(\014lesystem)i
+(t)o(yp)q(e)g(acts)f(as)h(an)g(in)o(ternal)h(placeholder)g(on)o(to)e(whic)o
+(h)i Fp(Amd)h Fo(can)0 1929 y(pin)c(`)p Fl(toplvl)p Fo(')e(moun)o(ts.)20
+b(Only)d(one)e(no)q(de)h(of)f(this)h(t)o(yp)q(e)g(need)g(ev)o(er)f(exist)h
+(and)g(one)f(is)h(created)g(automatically)0 1978 y(during)g(startup.)j(The)d
+(e\013ect)f(of)f(creating)i(a)f(second)g(ro)q(ot)g(no)q(de)h(is)f
+(unde\014ned.)0 2135 y Fq(5.14)32 b(Inheritance)17 b(Filesystem)62
+2226 y Fo(The)c Fp(inheritance)k Fo(\(`)p Fl(type:=inherit)p
+Fo('\))10 b(\014lesystem)k(is)f(not)f(directly)j(accessible.)20
+b(Instead,)14 b(in)o(ternal)f(moun)o(t)0 2276 y(no)q(des)j(of)f(this)h(t)o
+(yp)q(e)f(are)g(automatically)h(generated)f(when)h Fp(Amd)i
+Fo(is)e(started)e(with)i(the)f(\\-r")g(option.)21 b(A)o(t)15
+b(this)0 2325 y(time)j(the)g(system)f(moun)o(t)g(table)h(is)g(scanned)g(to)f
+(lo)q(cate)h(an)o(y)f(\014lesystems)h(whic)o(h)h(are)e(already)h(moun)o(ted.)
+27 b(If)0 2375 y(an)o(y)17 b(reference)h(to)f(these)h(\014lesystems)g(is)g
+(made)f(through)g Fp(Amd)i Fo(then)f(instead)g(of)f(attempting)g(to)g(moun)o
+(t)g(it,)0 2425 y Fp(Amd)k Fo(sim)o(ulates)e(the)g(moun)o(t)f(and)h
+Fp(inherits)j Fo(the)d(\014lesystem.)31 b(This)19 b(allo)o(ws)g(a)f(new)h(v)o
+(ersion)g(of)g Fp(Amd)h Fo(to)e(b)q(e)0 2475 y(installed)d(on)e(a)g(liv)o(e)i
+(system)e(simply)h(b)o(y)f(killing)j(the)d(old)h(daemon)f(with)h
+Fl(SIGTERM)e Fo(and)i(starting)e(the)i(new)f(one.)62 2545 y(This)18
+b(\014lesystem)g(t)o(yp)q(e)f(is)g(not)g(generally)h(visible)h(externally)l
+(,)g(but)e(it)g(is)h(p)q(ossible)g(that)f(the)g(output)g(from)0
+2595 y(`)p Fl(amq)d(-m)p Fo(')g(ma)o(y)f(list)h(`)p Fl(inherit)p
+Fo(')f(as)g(the)h(\014lesystem)h(t)o(yp)q(e.)k(This)c(happ)q(ens)g(when)f(an)
+g(inherit)h(op)q(eration)f(cannot)0 2645 y(b)q(e)i(completed)g(for)f(some)g
+(reason,)f(usually)i(b)q(ecause)h(a)e(\014leserv)o(er)g(is)h(do)o(wn.)p
+eop
+%%Page: 27 29
+27 28 bop 0 -83 a Fo(Chapter)15 b(6:)k(Run-time)e(Administration)987
+b(SMM:13-27)0 158 y Fm(6)41 b(Run-time)14 b(Administration)0
+379 y Fq(6.1)33 b(Starting)16 b Ff(Amd)62 470 y Fp(Amd)h Fo(is)f(b)q(est)g
+(started)e(from)g(`)p Fl(/etc/rc.local)p Fo(':)120 540 y Fl(if)24
+b([)f(-f)h(/etc/amd.start)e(];)h(then)311 590 y(sh)g(/etc/amd.start;)f
+(\(echo)h(-n)h(')g(amd'\))142 b(>/dev/console)120 640 y(fi)0
+731 y Fo(The)15 b(shell)i(script,)e(`)p Fl(amd.start)p Fo(',)e(con)o(tains:)
+120 802 y Fl(#!/bin/sh)23 b(-)120 852 y(PATH=/etc:/bin:/usr/bin:/u)o(sr/ucb:)
+o($PATH)e(export)i(PATH)120 951 y(#)120 1001 y(#)h(Either)f(name)g(of)h
+(logfile)f(or)g("syslog")120 1051 y(#)120 1101 y(LOGFILE=syslog)120
+1151 y(#LOGFILE=/var/log/amd)120 1250 y(#)120 1300 y(#)h(Figure)f(out)g
+(whether)g(domain)g(name)g(is)h(in)g(host)f(name)120 1350 y(#)h(If)f(the)h
+(hostname)f(is)g(just)g(the)h(machine)f(name)g(then)120 1400
+y(#)h(pass)f(in)h(the)f(name)g(of)h(the)f(local)h(domain)f(so)g(that)h(the)
+120 1450 y(#)g(hostnames)e(in)i(the)f(map)h(are)f(domain)g(stripped)g
+(correctly.)120 1499 y(#)120 1549 y(case)g(`hostname`)g(in)120
+1599 y(*.*\))g(dmn=)h(;;)120 1649 y(*\))g(dmn='-d)e(doc.ic.ac.uk')120
+1699 y(esac)120 1798 y(#)120 1848 y(#)i(Zap)f(earlier)g(log)h(file)120
+1898 y(#)120 1948 y(case)f("$LOGFILE")g(in)120 1998 y(*/*\))311
+2047 y(mv)g("$LOGFILE")g("$LOGFILE"-)311 2097 y(>)h("$LOGFILE")311
+2147 y(;;)120 2197 y(syslog\))311 2247 y(:)g(nothing)311 2296
+y(;;)120 2346 y(esac)120 2446 y(cd)g(/usr/sbin)120 2496 y(#)120
+2545 y(#)g(-r)286 b(restart)120 2595 y(#)24 b(-d)f(dmn)191
+b(local)23 b(domain)120 2645 y(#)h(-w)f(wait)167 b(wait)23
+b(between)g(unmount)g(attempts)p eop
+%%Page: 28 30
+28 29 bop 15 -83 a Fo(SMM:13-28)889 b(4.4)15 b(BSD)g(Automoun)o(ter)f
+(Reference)j(Man)o(ual)120 158 y Fl(#)24 b(-l)f(log)191 b(logfile)23
+b(or)g("syslog")120 208 y(#)120 258 y(eval)g(./amd)g(-r)h($dmn)f(-w)h(240)f
+(-l)h("$LOGFILE")f(\\)311 308 y(/homes)g(amd.homes)g(-cache:=inc)f(\\)311
+358 y(/home)h(amd.home)g(-cache:=inc)f(\\)311 407 y(/vol)h(amd.vol)g
+(-cache:=inc)f(\\)311 457 y(/n)h(amd.net)g(-cache:=inc)62 549
+y Fo(If)12 b(the)g(list)g(of)f(automoun)o(t)f(p)q(oin)o(ts)i(and)g(maps)f(is)
+h(con)o(tained)g(in)h(a)e(\014le)i(or)e(NIS)h(map)f(it)h(is)g(easily)g
+(incorp)q(orated)0 598 y(on)o(to)i(the)i(command)f(line:)120
+669 y Fl(...)120 719 y(eval)23 b(./amd)g(-r)h($dmn)f(-w)h(240)f(-l)h
+("$LOGFILE")f(`ypcat)g(-k)g(auto.master`)0 904 y Fq(6.2)33
+b(Stopping)16 b Ff(Amd)62 995 y Fp(Amd)h Fo(stops)e(in)h(resp)q(onse)g(to)e
+(t)o(w)o(o)g(signals.)0 1066 y(`)p Fl(SIGTERM)p Fo(')46 b(causes)18
+b(the)f(top-lev)o(el)i(automoun)o(t)d(p)q(oin)o(ts)i(to)f(b)q(e)h(unmoun)o
+(ted)f(and)h(then)g Fp(Amd)h Fo(to)e(exit.)27 b(An)o(y)240
+1116 y(automoun)o(ted)16 b(\014lesystems)g(are)g(left)h(moun)o(ted.)23
+b(They)17 b(can)f(b)q(e)h(reco)o(v)o(ered)f(b)o(y)g(restarting)g
+Fp(Amd)240 1166 y Fo(with)g(the)f(\\-r")f(command)h(line)i(option.)0
+1230 y(`)p Fl(SIGINT)p Fo(')70 b(causes)22 b Fp(Amd)i Fo(to)d(attempt)g(to)g
+(unmoun)o(t)h(an)o(y)g(\014lesystems)g(whic)o(h)h(it)f(has)g(automoun)o(ted,)
+g(in)240 1280 y(addition)16 b(to)f(the)g(actions)g(of)g(`)p
+Fl(SIGTERM)p Fo('.)j(This)e(signal)g(is)g(primarly)g(used)g(for)e(debugging.)
+62 1371 y(Actions)i(tak)o(en)f(for)f(other)h(signals)h(are)f(unde\014ned.)0
+1556 y Fq(6.3)33 b(Con)n(trolling)17 b Ff(Amd)62 1647 y Fo(It)f(is)h
+(sometimes)f(desirable)i(or)d(necessary)i(to)e(exercise)i(external)g(con)o
+(trol)f(o)o(v)o(er)f(some)h(of)f Fp(Amd)r Fo('s)h(in)o(ternal)0
+1697 y(state.)j(T)l(o)c(supp)q(ort)g(this)h(requiremen)o(t,)f
+Fp(Amd)i Fo(implemen)o(ts)g(an)e(RPC)g(in)o(terface)g(whic)o(h)h(is)g(used)g
+(b)o(y)f(the)g Fp(Amq)0 1746 y Fo(program.)k(A)c(v)m(ariet)o(y)g(of)g
+(information)g(is)h(a)o(v)m(ailable.)62 1817 y Fp(Amq)e Fo(generally)h
+(applies)f(an)f(op)q(eration,)h(sp)q(eci\014ed)h(b)o(y)e(a)g(single)h(letter)
+f(option,)h(to)e(a)h(list)h(of)e(moun)o(t)h(p)q(oin)o(ts.)0
+1867 y(The)i(default)g(op)q(eration)g(is)g(to)f(obtain)g(statistics)h(ab)q
+(out)f(eac)o(h)h(moun)o(t)f(p)q(oin)o(t.)20 b(This)15 b(is)g(similar)h(to)e
+(the)g(output)0 1917 y(sho)o(wn)f(ab)q(o)o(v)o(e)g(but)h(includes)h
+(information)f(ab)q(out)f(the)h(n)o(um)o(b)q(er)g(and)f(t)o(yp)q(e)h(of)f
+(accesses)h(to)e(eac)o(h)i(moun)o(t)f(p)q(oin)o(t.)0 2077 y
+Fi(6.3.1)30 b Fe(Amq)16 b Fi(default)f(information)62 2169
+y Fo(With)k(no)f(argumen)o(ts,)f Fp(Amq)i Fo(obtains)f(a)g(brief)h(list)g(of)
+e(all)i(existing)h(moun)o(ts)d(created)h(b)o(y)g Fp(Amd)p Fo(.)29
+b(This)18 b(is)0 2218 y(di\013eren)o(t)d(from)g(the)g(list)h(displa)o(y)o(ed)
+h(b)o(y)e Fk(df)p Fo(\(1\))f(since)j(the)e(latter)g(only)g(includes)j(system)
+d(moun)o(t)f(p)q(oin)o(ts.)0 2289 y(The)h(output)g(from)g(this)h(option)f
+(includes)i(the)f(follo)o(wing)g(information:)37 2360 y Fn(\017)30
+b Fo(the)15 b(automoun)o(t)f(p)q(oin)o(t,)37 2424 y Fn(\017)30
+b Fo(the)15 b(\014lesystem)h(t)o(yp)q(e,)37 2489 y Fn(\017)30
+b Fo(the)15 b(moun)o(t)g(map)g(or)g(moun)o(t)f(information,)37
+2554 y Fn(\017)30 b Fo(the)15 b(in)o(ternal,)h(or)f(system)f(moun)o(t)h(p)q
+(oin)o(t.)0 2645 y(F)l(or)g(example:)p eop
+%%Page: 29 31
+29 30 bop 0 -83 a Fo(Chapter)15 b(6:)k(Run-time)e(Administration)987
+b(SMM:13-29)120 158 y Fl(/)286 b(root)71 b("root")477 b(sky:\(pid75\))120
+208 y(/homes)166 b(toplvl)23 b(/usr/local/etc/amd.homes)45
+b(/homes)120 258 y(/home)190 b(toplvl)23 b(/usr/local/etc/amd.home)69
+b(/home)120 308 y(/homes/jsp)h(nfs)95 b(charm:/home/charm)213
+b(/a/charm/home/charm/jsp)120 358 y(/homes/phjk)46 b(nfs)95
+b(toytown:/home/toytown)117 b(/a/toytown/home/toytown/)o(ai/phjk)0
+449 y Fo(If)15 b(an)h(argumen)o(t)e(is)i(giv)o(en)f(then)h(statistics)f(for)g
+(that)f(v)o(olume)i(name)f(will)i(b)q(e)f(output.)j(F)l(or)c(example:)120
+519 y Fl(What)286 b(Uid)71 b(Getattr)23 b(Lookup)g(RdDir)71
+b(RdLnk)g(Statfs)23 b(Mounted@)120 569 y(/homes)238 b(0)119
+b(1196)95 b(512)g(22)143 b(0)167 b(30)119 b(90/09/14)23 b(12:32:55)120
+619 y(/homes/jsp)142 b(0)119 b(0)167 b(0)143 b(0)167 b(1180)95
+b(0)143 b(90/10/13)23 b(12:56:58)0 690 y(What)144 b Fo(the)15
+b(v)o(olume)h(name.)0 752 y Fl(Uid)168 b Fo(ignored.)0 815
+y Fl(Getattr)72 b Fo(the)21 b(coun)o(t)g(of)f(NFS)h Fp(getattr)i
+Fo(requests)e(on)g(this)g(no)q(de.)38 b(This)21 b(should)h(only)g(b)q(e)f
+(non-zero)h(for)240 865 y(directory)15 b(no)q(des.)0 928 y
+Fl(Lookup)96 b Fo(the)21 b(coun)o(t)g(of)g(NFS)g Fp(lo)q(okup)j
+Fo(requests)d(on)g(this)h(no)q(de.)38 b(This)22 b(should)g(only)g(b)q(e)g
+(non-zero)g(for)240 978 y(directory)15 b(no)q(des.)0 1040 y
+Fl(RdDir)120 b Fo(the)21 b(coun)o(t)f(of)g(NFS)h Fp(readdir)j
+Fo(requests)d(on)g(this)g(no)q(de.)36 b(This)22 b(should)f(only)g(b)q(e)h
+(non-zero)f(for)240 1090 y(directory)15 b(no)q(des.)0 1153
+y Fl(RdLnk)120 b Fo(the)19 b(coun)o(t)g(of)g(NFS)g Fp(readlink)k
+Fo(requests)c(on)h(this)f(no)q(de.)32 b(This)20 b(should)g(b)q(e)g(zero)f
+(for)g(directory)240 1203 y(no)q(des.)0 1266 y Fl(Statfs)96
+b Fo(the)11 b(could)h(of)f(NFS)g Fp(statfs)h Fo(requests)f(on)g(this)h(no)q
+(de.)19 b(This)11 b(should)h(only)g(b)q(e)g(non-zero)f(for)g(top-lev)o(el)240
+1315 y(automoun)o(t)j(p)q(oin)o(ts.)0 1378 y Fl(Mounted@)48
+b Fo(the)15 b(date)g(and)h(time)f(the)g(v)o(olume)h(name)f(w)o(as)g(\014rst)f
+(referenced.)0 1523 y Fi(6.3.2)30 b Fe(Amq)16 b Fi(-f)f(option)62
+1615 y Fo(The)i(\\-f)t(")f(option)h(causes)g Fp(Amd)i Fo(to)d(\015ush)h(the)g
+(in)o(ternal)h(moun)o(t)e(map)g(cac)o(he.)25 b(This)18 b(is)f(useful)h(for)e
+(Hesio)q(d)0 1664 y(maps)f(since)i Fp(Amd)g Fo(will)g(not)e(automatically)h
+(notice)g(when)g(they)g(ha)o(v)o(e)f(b)q(een)i(up)q(dated.)k(The)16
+b(map)f(cac)o(he)h(can)0 1714 y(also)g(b)q(e)g(sync)o(hronised)g(with)g(the)g
+(map)f(source)h(b)o(y)g(using)g(the)f(`)p Fl(sync)p Fo(')g(option)h(\(see)f
+(Section)h(5.8)f([Automoun)o(t)0 1764 y(Filesystem],)g(page)30
+b(SMM:13-24\).)0 1909 y Fi(6.3.3)g Fe(Amq)16 b Fi(-h)g(option)62
+2000 y Fo(By)g(default)g(the)g(lo)q(cal)g(host)f(is)h(used.)22
+b(In)16 b(an)f(HP-UX)h(cluster)g(the)g(ro)q(ot)e(serv)o(er)h(is)h(used)g
+(since)h(that)e(is)h(the)0 2050 y(only)k(place)g(in)g(the)f(cluster)h(where)f
+Fp(Amd)i Fo(will)g(b)q(e)f(running.)33 b(T)l(o)19 b(query)g
+Fp(Amd)i Fo(on)e(another)g(host)g(the)g(\\-h")0 2100 y(option)c(should)i(b)q
+(e)e(used.)0 2245 y Fi(6.3.4)30 b Fe(Amq)16 b Fi(-m)g(option)62
+2336 y Fo(The)c(\\-m")e(option)h(displa)o(ys)h(similar)h(information)e(ab)q
+(out)g(moun)o(ted)g(\014lesystems,)h(rather)e(than)h(automoun)o(t)0
+2386 y(p)q(oin)o(ts.)20 b(The)c(output)f(includes)i(the)e(follo)o(wing)h
+(information:)37 2457 y Fn(\017)30 b Fo(the)15 b(moun)o(t)g(information,)37
+2520 y Fn(\017)30 b Fo(the)15 b(moun)o(t)g(p)q(oin)o(t,)37
+2582 y Fn(\017)30 b Fo(the)15 b(\014lesystem)h(t)o(yp)q(e,)37
+2645 y Fn(\017)30 b Fo(the)15 b(n)o(um)o(b)q(er)h(of)f(references)g(to)g
+(this)h(\014lesystem,)p eop
+%%Page: 30 32
+30 31 bop 15 -83 a Fo(SMM:13-30)889 b(4.4)15 b(BSD)g(Automoun)o(ter)f
+(Reference)j(Man)o(ual)37 158 y Fn(\017)30 b Fo(the)15 b(serv)o(er)g
+(hostname,)37 221 y Fn(\017)30 b Fo(the)15 b(state)g(of)f(the)i(\014le)g
+(serv)o(er,)37 284 y Fn(\017)30 b Fo(an)o(y)15 b(error)f(whic)o(h)i(has)f(o)q
+(ccured.)62 375 y(F)l(or)g(example:)120 446 y Fl("root")262
+b(truth:\(pid602\))117 b(root)71 b(1)24 b(localhost)e(is)i(up)120
+496 y(hesiod.home)142 b(/home)333 b(toplvl)23 b(1)h(localhost)e(is)i(up)120
+546 y(hesiod.vol)166 b(/vol)357 b(toplvl)23 b(1)h(localhost)e(is)i(up)120
+595 y(hesiod.homes)118 b(/homes)309 b(toplvl)23 b(1)h(localhost)e(is)i(up)120
+645 y(amy:/home/amy)94 b(/a/amy/home/amy)f(nfs)i(5)24 b(amy)f(is)h(up)120
+695 y(swan:/home/swan)46 b(/a/swan/home/swan)f(nfs)95 b(0)24
+b(swan)f(is)h(up)f(\(Permission)g(denied\))120 745 y(ex:/home/ex)142
+b(/a/ex/home/ex)f(nfs)95 b(0)24 b(ex)f(is)h(down)62 836 y Fo(When)15
+b(the)g(reference)g(coun)o(t)f(is)h(zero)g(the)f(\014lesystem)i(is)f(not)f
+(moun)o(ted)g(but)h(the)g(moun)o(t)f(p)q(oin)o(t)h(and)f(serv)o(er)0
+886 y(information)h(is)h(still)h(b)q(eing)f(main)o(tained)g(b)o(y)f
+Fp(Amd)p Fo(.)0 1032 y Fi(6.3.5)30 b Fe(Amq)16 b Fi(-M)g(option)62
+1123 y Fo(The)j(\\-M")f(option)i(passes)e(a)h(new)g(map)g(en)o(try)f(to)g
+Fp(Amd)j Fo(and)e(w)o(aits)g(for)f(it)h(to)f(b)q(e)i(ev)m(aluated,)g(p)q
+(ossibly)0 1173 y(causing)i(a)f(moun)o(t.)38 b(F)l(or)21 b(example,)j(the)d
+(follo)o(wing)h(command)g(w)o(ould)f(cause)h(`)p Fl(/home/toytown)p
+Fo(')d(on)i(host)0 1223 y(`)p Fl(toytown)p Fo(')13 b(to)i(b)q(e)h(moun)o(ted)
+f(lo)q(cally)i(on)e(`)p Fl(/mnt/toytown)p Fo('.)120 1293 y
+Fl(amq)23 b(-M)h('/mnt/toytown)e(type:=nfs;rfs:=/home/toytow)o(n;rhost)o
+(:=toytow)o(n;fs:=$)o({key}')62 1385 y Fp(Amd)13 b Fo(applies)g(some)e
+(simple)i(securit)o(y)e(c)o(hec)o(ks)h(b)q(efore)f(allo)o(wing)h(this)g(op)q
+(eration.)18 b(The)12 b(c)o(hec)o(k)f(tests)g(whether)0 1434
+y(the)16 b(incoming)h(request)f(is)g(from)f(a)h(privileged)i(UDP)d(p)q(ort)h
+(on)g(the)f(lo)q(cal)i(mac)o(hine.)23 b(\\P)o(ermission)16
+b(denied")h(is)0 1484 y(returned)f(if)f(the)h(c)o(hec)o(k)f(fails.)62
+1555 y(A)f(future)f(release)h(of)e Fp(Amd)j Fo(will)g(include)h(co)q(de)e(to)
+e(allo)o(w)i(the)f Fk(moun)o(t)p Fo(\(8\))f(command)h(to)f(moun)o(t)h
+(automoun)o(t)0 1605 y(p)q(oin)o(ts:)120 1675 y Fl(mount)23
+b(-t)h(amd)f(/vol)h(hesiod.vol)62 1766 y Fo(This)16 b(will)h(then)e(allo)o(w)
+h Fp(Amd)h Fo(to)e(b)q(e)g(con)o(trolled)h(from)f(the)g(standard)g(system)g
+(\014lesystem)h(moun)o(t)e(list.)0 1912 y Fi(6.3.6)30 b Fe(Amq)16
+b Fi(-s)g(option)62 2004 y Fo(The)h(\\-s")f(option)h(displa)o(ys)h(global)f
+(statistics.)24 b(If)17 b(an)o(y)g(other)f(options)h(are)f(sp)q(eci\014ed)j
+(or)d(an)o(y)g(\014lesystems)0 2053 y(named)f(then)h(this)g(option)f(is)h
+(ignored.)k(F)l(or)15 b(example:)120 2124 y Fl(requests)47
+b(stale)118 b(mount)h(mount)g(unmount)120 2174 y(deferred)47
+b(fhandles)f(ok)191 b(failed)95 b(failed)120 2224 y(1054)143
+b(1)214 b(487)167 b(290)g(7017)0 2294 y Fo(`)p Fl(Deferred)14
+b(requests)p Fo(')240 2357 y(are)k(those)f(for)h(whic)o(h)h(an)f(immediate)h
+(reply)f(could)h(not)f(b)q(e)h(constructed.)28 b(F)l(or)17
+b(example,)j(this)240 2407 y(w)o(ould)c(happ)q(en)g(if)g(a)e(bac)o(kground)i
+(moun)o(t)e(w)o(as)h(required.)0 2470 y(`)p Fl(Stale)f(filehandles)p
+Fo(')240 2532 y(coun)o(ts)f(the)g(n)o(um)o(b)q(er)g(of)g(times)g(the)g(k)o
+(ernel)h(passes)f(a)g(stale)g(\014lehandle)i(to)e Fp(Amd)p
+Fo(.)19 b(Large)13 b(n)o(um)o(b)q(ers)240 2582 y(indicate)k(problems.)0
+2645 y(`)p Fl(Mount)d(ok)p Fo(')32 b(coun)o(ts)15 b(the)g(n)o(um)o(b)q(er)h
+(of)f(automoun)o(ts)f(whic)o(h)i(w)o(ere)e(successful.)p eop
+%%Page: 31 33
+31 32 bop 0 -83 a Fo(Chapter)15 b(7:)k(FSinfo)1362 b(SMM:13-31)0
+158 y(`)p Fl(Mount)14 b(failed)p Fo(')240 222 y(coun)o(ts)h(the)g(n)o(um)o(b)
+q(er)h(of)f(automoun)o(ts)f(whic)o(h)i(failed.)0 286 y(`)p
+Fl(Unmount)e(failed)p Fo(')240 350 y(coun)o(ts)h(the)g(n)o(um)o(b)q(er)g(of)f
+(times)h(a)g(\014lesystem)g(could)h(not)f(b)q(e)g(unmoun)o(ted.)21
+b(V)l(ery)15 b(large)g(n)o(um)o(b)q(ers)240 400 y(here)h(indicate)g(that)f
+(the)g(time)h(b)q(et)o(w)o(een)f(unmoun)o(t)g(attempts)f(should)j(b)q(e)f
+(increased.)0 554 y Fi(6.3.7)30 b Fe(Amq)16 b Fi(-u)g(option)62
+645 y Fo(The)f(\\-u")g(option)g(causes)h(the)f(time-to-liv)o(e)h(in)o(terv)m
+(al)g(of)e(the)h(named)h(moun)o(t)e(p)q(oin)o(ts)h(to)g(b)q(e)g(expired,)h
+(th)o(us)0 695 y(causing)f(an)e(unmoun)o(t)h(attempt.)19 b(This)14
+b(is)g(the)g(only)h(safe)e(w)o(a)o(y)g(to)g(unmoun)o(t)h(an)g(automoun)o(ted)
+f(\014lesystem.)20 b(It)0 745 y(is)c(not)f(p)q(ossible)h(to)f(unmoun)o(t)g(a)
+g(\014lesystem)h(whic)o(h)g(has)f(b)q(een)h(moun)o(ted)f(with)h(the)f(`)p
+Fl(nounmount)p Fo(')e(\015ag.)0 899 y Fi(6.3.8)30 b Fe(Amq)16
+b Fi(-v)g(option)62 990 y Fo(The)g(\\-v")e(option)i(displa)o(ys)g(the)f(v)o
+(ersion)h(of)f Fp(Amd)i Fo(in)f(a)f(similar)h(w)o(a)o(y)e(to)h
+Fp(Amd)r Fo('s)f(\\-v")h(option.)0 1145 y Fi(6.3.9)30 b(Other)15
+b Fe(Amq)h Fi(options)62 1236 y Fo(Three)j(other)g(op)q(erations)f(are)h
+(implemen)o(ted.)32 b(These)19 b(mo)q(dify)g(the)g(state)f(of)g
+Fp(Amd)j Fo(as)d(a)h(whole,)h(rather)0 1286 y(than)e(an)o(y)g(particular)h
+(\014lesystem.)30 b(The)19 b(\\-l",)g(\\-x")f(and)h(\\-D")e(options)i(ha)o(v)
+o(e)f(exactly)h(the)f(same)g(e\013ect)g(as)0 1336 y Fp(Amd)r
+Fo('s)g(corresp)q(onding)h(command)g(line)h(options.)30 b(The)18
+b(\\-l")h(option)g(is)g(rejected)f(b)o(y)h Fp(Amd)h Fo(in)g(the)e(curren)o(t)
+0 1385 y(v)o(ersion)e(for)e(ob)o(vious)i(securit)o(y)g(reasons.)j(When)d
+Fp(Amd)h Fo(receiv)o(es)g(a)e(\\-x"\015ag)f(it)i(limits)g(the)g(log)f
+(options)h(b)q(eing)0 1435 y(mo)q(di\014ed)g(to)e(those)h(whic)o(h)g(w)o(ere)
+g(not)f(enabled)j(at)d(startup.)19 b(This)c(prev)o(en)o(ts)g(a)f(user)h
+(turning)g Fp(o\013)23 b Fo(an)o(y)15 b(logging)0 1485 y(option)h(whic)o(h)h
+(w)o(as)f(sp)q(eci\014ed)i(at)d(startup,)h(though)g(an)o(y)g(whic)o(h)h(ha)o
+(v)o(e)e(b)q(een)j(turned)e(o\013)f(since)j(then)e(can)h(still)0
+1535 y(b)q(e)f(turned)f(o\013.)20 b(The)15 b(\\-D")f(option)i(has)f(a)g
+(similar)h(b)q(eha)o(viour.)0 1737 y Fm(7)41 b(FSinfo)0 1964
+y Fq(7.1)33 b Ff(FSinfo)16 b Fq(o)n(v)n(erview)62 2056 y Fp(FSinfo)h
+Fo(is)d(a)f(\014lesystem)h(managemen)o(t)f(to)q(ol.)19 b(It)14
+b(has)g(b)q(een)g(designed)h(to)e(w)o(ork)g(with)h Fp(Amd)i
+Fo(to)c(help)j(system)0 2105 y(administrators)g(k)o(eep)g(trac)o(k)g(of)f
+(the)i(ev)o(er)f(increasing)h(\014lesystem)g(namespace)g(under)g(their)f(con)
+o(trol.)62 2176 y(The)20 b(purp)q(ose)g(of)f Fp(FSinfo)j Fo(is)e(to)e
+(generate)h(all)i(the)e(imp)q(ortan)o(t)g(standard)g(\014lesystem)h(data)e
+(\014les)j(from)d(a)0 2226 y(single)g(set)e(of)g(input)h(data.)23
+b(Starting)16 b(with)h(a)f(single)h(data)f(source)h(guaran)o(tees)e(that)h
+(all)h(the)g(generated)f(\014les)0 2276 y(are)i(self-consisten)o(t.)28
+b(One)19 b(of)e(the)h(p)q(ossible)i(output)e(data)f(formats)f(is)j(a)e(set)h
+(of)f Fp(Amd)j Fo(maps)e(whic)o(h)g(can)g(b)q(e)0 2325 y(used)e(amongst)e
+(the)h(set)g(of)g(hosts)g(describ)q(ed)i(in)f(the)f(input)h(data.)62
+2396 y Fp(FSinfo)i Fo(implemen)o(ts)f(a)e(declarativ)o(e)h(language.)k(This)c
+(language)g(is)g(sp)q(eci\014cally)i(designed)e(for)f(describing)0
+2446 y(\014lesystem)21 b(namespace)g(and)f(ph)o(ysical)i(la)o(y)o(outs.)34
+b(The)21 b(basic)g(declaration)g(de\014nes)g(a)f(moun)o(ted)g(\014lesystem)0
+2496 y(including)e(its)e(device)h(name,)e(moun)o(t)g(p)q(oin)o(t,)g(and)h
+(all)g(the)g(v)o(olumes)g(and)f(access)h(p)q(ermissions.)22
+b Fp(FSinfo)c Fo(reads)0 2545 y(this)e(information)g(and)f(builds)j(an)d(in)o
+(ternal)h(map)f(of)h(the)f(en)o(tire)h(net)o(w)o(ork)e(of)h(hosts.)21
+b(Using)16 b(this)f(map,)g(man)o(y)0 2595 y(di\013eren)o(t)d(data)g(formats)f
+(can)h(b)q(e)h(pro)q(duced)h(including)h(`)p Fl(/etc/fstab)p
+Fo(',)10 b(`)p Fl(/etc/exports)p Fo(',)g Fp(Amd)k Fo(moun)o(t)e(maps)0
+2645 y(and)j(`)p Fl(/etc/bootparams)p Fo('.)p eop
+%%Page: 32 34
+32 33 bop 15 -83 a Fo(SMM:13-32)889 b(4.4)15 b(BSD)g(Automoun)o(ter)f
+(Reference)j(Man)o(ual)0 158 y Fq(7.2)33 b(Using)15 b Ff(FSinfo)62
+250 y Fo(The)20 b(basic)h(strategy)d(when)i(using)h Fp(FSinfo)h
+Fo(is)e(to)f(gather)g(all)i(the)f(information)g(ab)q(out)f(all)i(disks)f(on)g
+(all)0 299 y(mac)o(hines)i(in)o(to)e(one)h(set)g(of)g(declarations.)37
+b(F)l(or)20 b(eac)o(h)h(mac)o(hine)h(b)q(eing)g(managed,)g(the)f(follo)o
+(wing)g(data)f(is)0 349 y(required:)37 420 y Fn(\017)30 b Fo(Hostname)37
+482 y Fn(\017)g Fo(List)16 b(of)f(all)h(\014lesystems)f(and,)g(optionally)l
+(,)i(their)e(moun)o(t)g(p)q(oin)o(ts.)37 544 y Fn(\017)30 b
+Fo(Names)15 b(of)g(v)o(olumes)g(stored)g(on)g(eac)o(h)g(\014lesystem.)37
+606 y Fn(\017)30 b Fo(NFS)15 b(exp)q(ort)g(information)h(for)e(eac)o(h)h(v)o
+(olume.)37 668 y Fn(\017)30 b Fo(The)15 b(list)h(of)f(static)g(\014lesystem)h
+(moun)o(ts.)62 759 y(The)g(follo)o(wing)g(information)f(can)h(also)f(b)q(e)h
+(en)o(tered)g(in)o(to)f(the)h(same)f(con\014guration)g(\014les)i(so)e(that)f
+(all)j(data)0 809 y(can)e(b)q(e)h(k)o(ept)f(in)h(one)g(place.)37
+879 y Fn(\017)30 b Fo(List)16 b(of)f(net)o(w)o(ork)f(in)o(terfaces)37
+941 y Fn(\017)30 b Fo(IP)16 b(address)f(of)g(eac)o(h)g(in)o(terface)37
+1003 y Fn(\017)30 b Fo(Hardw)o(are)14 b(address)i(of)e(eac)o(h)i(in)o
+(terface)37 1065 y Fn(\017)30 b Fo(Dumpset)15 b(to)g(whic)o(h)h(eac)o(h)f
+(\014lesystem)h(b)q(elongs)37 1127 y Fn(\017)30 b Fo(and)15
+b(more)g Fj(:)8 b(:)g(:)62 1218 y Fo(T)l(o)13 b(generate)f
+Fp(Amd)j Fo(moun)o(t)d(maps,)g(the)h(automoun)o(t)f(tree)g(m)o(ust)g(also)h
+(b)q(e)g(de\014ned)h(\(see)f(Section)g(7.8)f([FSinfo)0 1268
+y(automoun)o(t)k(de\014nitions],)j(page)34 b(SMM:13-39\).)23
+b(This)18 b(will)h(ha)o(v)o(e)d(b)q(een)j(designed)f(at)f(the)g(time)g(the)h
+(v)o(olume)0 1318 y(names)f(w)o(ere)f(allo)q(cated.)26 b(Some)17
+b(v)o(olume)g(names)f(will)j(not)d(b)q(e)i(automoun)o(ted,)e(so)g
+Fp(FSinfo)k Fo(needs)d(an)g(explicit)0 1368 y(list)f(of)f(whic)o(h)h(v)o
+(olumes)f(should)i(b)q(e)e(automoun)o(ted.)62 1438 y(Hostnames)k(are)f
+(required)j(at)d(sev)o(eral)h(places)h(in)g(the)f Fp(FSinfo)j
+Fo(language.)32 b(It)19 b(is)g(imp)q(ortan)o(t)g(to)f(stic)o(k)i(to)0
+1488 y(either)15 b(fully)h(quali\014ed)g(names)e(or)g(unquali\014ed)j(names.)
+i(Using)c(a)f(mixture)h(of)f(the)g(t)o(w)o(o)f(will)j(inevitably)h(result)0
+1538 y(in)f(confusion.)62 1609 y(Sometimes)c(v)o(olumes)f(need)h(to)e(b)q(e)i
+(referenced)g(whic)o(h)g(are)e(not)h(de\014ned)h(in)g(the)f(set)g(of)f(hosts)
+h(b)q(eing)h(managed)0 1658 y(with)19 b Fp(FSinfo)p Fo(.)31
+b(The)18 b(required)i(action)f(is)g(to)f(add)h(a)f(dumm)o(y)h(set)f(of)g
+(de\014nitions)j(for)d(the)h(host)f(and)h(v)o(olume)0 1708
+y(names)d(required.)24 b(Since)17 b(the)g(\014les)g(generated)f(for)f(those)h
+(particular)h(hosts)f(will)i(not)d(b)q(e)i(used)g(on)f(them,)g(the)0
+1758 y(exact)f(v)m(alues)h(used)g(is)g(not)f(critical.)0 1914
+y Fq(7.3)33 b Ff(FSinfo)16 b Fq(grammar)62 2006 y Fp(FSinfo)24
+b Fo(has)d(a)g(relativ)o(ely)i(simple)g(grammar.)36 b(Distinct)22
+b(syn)o(tactic)f(constructs)g(exist)h(for)f(eac)o(h)g(of)g(the)0
+2056 y(di\013eren)o(t)c(t)o(yp)q(es)f(of)g(data,)g(though)g(they)h(share)f(a)
+g(common)g(\015a)o(v)o(our.)23 b(Sev)o(eral)17 b(con)o(v)o(en)o(tions)f(are)h
+(used)g(in)g(the)0 2105 y(grammar)d(fragmen)o(ts)g(b)q(elo)o(w.)62
+2176 y(The)g(notation,)f Fp(list\()t Fl(xxx)p Fp(\))p Fo(,)g(indicates)i(a)e
+(list)h(of)f(zero)h(or)e(more)h Fl(xxx)p Fo('s.)19 b(The)13
+b(notation,)g Fp(opt\()t Fl(xxx)p Fp(\))p Fo(,)g(indicates)0
+2226 y(zero)22 b(or)g(one)g Fl(xxx)p Fo(.)41 b(Items)23 b(in)g(double)g
+(quotes,)h Fp(eg)i Fl("host")p Fo(,)d(represen)o(t)f(input)h(tok)o(ens.)41
+b(Items)22 b(in)h(angle)0 2276 y(brac)o(k)o(ets,)d Fp(eg)j
+Fl(<)p Fp(hostname)p Fl(>)p Fo(,)d(represen)o(t)g(strings)g(in)g(the)g
+(input.)35 b(Strings)20 b(need)g(not)g(b)q(e)g(in)h(double)g(quotes,)0
+2325 y(except)d(to)e(di\013eren)o(tiate)i(them)f(from)f(reserv)o(ed)i(w)o
+(ords.)25 b(Quoted)17 b(strings)g(ma)o(y)g(include)i(the)f(usual)f(set)g(of)g
+(C)0 2375 y(\\)p Fl(\\)p Fo(")12 b(escap)q(e)h(sequences)g(with)f(one)h
+(exception:)19 b(a)12 b(bac)o(kslash-newline-whi)q(tespace)j(sequence)e(is)g
+(squashed)g(in)o(to)0 2425 y(a)i(single)i(space)g(c)o(haracter.)j(T)l(o)c
+(defeat)f(this)i(feature,)e(put)h(a)f(further)h(bac)o(kslash)g(at)f(the)h
+(start)e(of)i(the)f(second)0 2475 y(line.)62 2545 y(A)o(t)f(the)h(outermost)e
+(lev)o(el)j(of)e(the)g(grammar,)f(the)i(input)g(consists)g(of)f(a)g(sequence)
+h(of)f(host)g(and)h(automoun)o(t)0 2595 y(declarations.)34
+b(These)20 b(declarations)h(are)e(all)i(parsed)f(b)q(efore)g(they)g(are)f
+(analyzed.)35 b(This)20 b(means)g(they)g(can)0 2645 y(app)q(ear)15
+b(in)h(an)o(y)f(order)g(and)h(cyclic)h(host)d(references)i(are)f(p)q
+(ossible.)p eop
+%%Page: 33 35
+33 34 bop 0 -83 a Fo(Chapter)15 b(7:)k(FSinfo)1362 b(SMM:13-33)120
+158 y Fl(fsinfo)142 b(:)24 b Fp(list\()t Fl(fsinfo_attr)p Fp(\))g
+Fl(;)120 258 y(fsinfo_attr)e(:)i(host)f(|)h(automount)f(;)0
+460 y Fq(7.4)33 b Ff(FSinfo)16 b Fq(host)f(de\014nitions)62
+552 y Fo(A)h(host)g(declaration)g(consists)g(of)g(three)g(parts:)k(a)c(set)f
+(of)h(mac)o(hine)g(attribute)g(data,)f(a)g(list)i(of)e(\014lesystems)0
+601 y(ph)o(ysically)i(attac)o(hed)e(to)f(the)i(mac)o(hine,)f(and)h(a)e(list)i
+(of)f(additional)i(statically)f(moun)o(ted)f(\014lesystems.)120
+672 y Fl(host)190 b(:)24 b("host")f(host_data)g Fp(list\()t
+Fl(filesystem)p Fp(\))h(list\()t Fl(mount)p Fp(\))g Fl(;)62
+763 y Fo(Eac)o(h)15 b(host)f(m)o(ust)g(b)q(e)i(declared)g(in)f(this)h(w)o(a)o
+(y)d(exactly)i(once.)20 b(Suc)o(h)c(things)f(as)f(the)h(hardw)o(are)f
+(address,)h(the)0 813 y(arc)o(hitecture)e(and)f(op)q(erating)h(system)f(t)o
+(yp)q(es)h(and)f(the)h(cluster)g(name)g(are)f(all)h(sp)q(eci\014ed)i(within)e
+(the)g Fp(host)f(data)p Fo(.)62 884 y(All)h(the)e(disks)h(the)f(mac)o(hine)h
+(has)f(should)h(then)g(b)q(e)f(describ)q(ed)j(in)e(the)f Fp(list)h(of)e
+(\014lesystems)p Fo(.)20 b(When)11 b(describing)0 933 y(disks,)17
+b(y)o(ou)f(can)h(sp)q(ecify)h(what)e Fp(v)o(olname)j Fo(the)e(disk/partition)
+g(should)h(ha)o(v)o(e)e(and)h(all)g(suc)o(h)g(en)o(tries)g(are)g(built)0
+983 y(up)f(in)o(to)f(a)g(dictionary)h(whic)o(h)g(can)f(then)h(b)q(e)g(used)f
+(for)g(building)j(the)d(automoun)o(ter)f(maps.)62 1054 y(The)f
+Fp(list)g(of)e(moun)o(ts)j Fo(sp)q(eci\014es)g(all)f(the)f(\014lesystems)h
+(that)e(should)i(b)q(e)g(statically)g(moun)o(ted)f(on)g(the)g(mac)o(hine.)0
+1263 y Fq(7.5)33 b Ff(FSinfo)16 b Fq(host)f(attributes)62 1354
+y Fo(The)20 b(host)g(data,)f Fp(host)p 472 1354 14 2 v 16 w(data)p
+Fo(,)h(alw)o(a)o(ys)f(includes)j(the)e Fp(hostname)p Fo(.)33
+b(In)20 b(addition,)i(sev)o(eral)e(other)f(host)g(at-)0 1404
+y(tributes)d(can)f(b)q(e)h(giv)o(en.)120 1474 y Fl(host_data)70
+b(:)24 b(<)p Fp(hostname)p Fl(>)406 1524 y(|)g("{")g Fp(list\()t
+Fl(host_attrs)p Fp(\))f Fl("}")h(<)p Fp(hostname)p Fl(>)406
+1574 y(;)120 1674 y(host_attrs)46 b(:)24 b(host_attr)f("=")g(<)p
+Fp(string)p Fl(>)406 1724 y(|)h(netif)406 1773 y(;)120 1873
+y(host_attr)70 b(:)24 b("config")406 1923 y(|)g("arch")406
+1973 y(|)g("os")406 2022 y(|)g("cluster")406 2072 y(;)62 2164
+y Fo(The)16 b Fp(hostname)h Fo(is,)e(t)o(ypically)l(,)i(the)e(fully)i
+(quali\014ed)g(hostname)e(of)f(the)i(mac)o(hine.)62 2234 y(Examples:)120
+2305 y Fl(host)23 b(dylan.doc.ic.ac.uk)120 2404 y(host)g({)215
+2454 y(os)h(=)g(hpux)215 2504 y(arch)g(=)f(hp300)120 2554 y(})h
+(dougal.doc.ic.ac.uk)62 2645 y Fo(The)16 b(options)f(that)g(can)g(b)q(e)h
+(giv)o(en)f(as)g(host)g(attributes)g(are)g(sho)o(wn)g(b)q(elo)o(w.)p
+eop
+%%Page: 34 36
+34 35 bop 15 -83 a Fo(SMM:13-34)889 b(4.4)15 b(BSD)g(Automoun)o(ter)f
+(Reference)j(Man)o(ual)0 158 y Fi(7.5.1)30 b(netif)15 b(Option)62
+250 y Fo(This)j(de\014nes)g(the)f(set)g(of)g(net)o(w)o(ork)f(in)o(terfaces)h
+(con\014gured)h(on)f(the)g(mac)o(hine.)26 b(The)18 b(in)o(terface)f
+(attributes)0 299 y(collected)k(b)o(y)e Fp(FSinfo)j Fo(are)d(the)g(IP)g
+(address,)h(subnet)g(mask)e(and)i(hardw)o(are)e(address.)32
+b(Multiple)21 b(in)o(terfaces)0 349 y(ma)o(y)c(b)q(e)h(de\014ned)h(for)d
+(hosts)h(with)h(sev)o(eral)g(in)o(terfaces)f(b)o(y)h(an)f(en)o(try)g(for)g
+(eac)o(h)g(in)o(terface.)27 b(The)18 b(v)m(alues)g(giv)o(en)0
+399 y(are)d(sanit)o(y)g(c)o(hec)o(k)o(ed,)g(but)h(are)e(curren)o(tly)i(un)o
+(used)g(for)f(an)o(ything)g(else.)120 470 y Fl(netif)166 b(:)24
+b("netif")f(<)p Fp(string)p Fl(>)h("{")f Fp(list\()t Fl(netif_attrs)p
+Fp(\))h Fl("}")f(;)120 569 y(netif_attrs)f(:)i(netif_attr)f("=")g(<)p
+Fp(string)p Fl(>)h(;)120 669 y(netif_attr)46 b(:)24 b("inaddr")f(|)h
+("netmask")e(|)i("hwaddr")f(;)62 760 y Fo(Examples:)120 831
+y Fl(netif)g(ie0)h({)215 881 y(inaddr)47 b(=)24 b(129.31.81.37)215
+930 y(netmask)f(=)h(0xfffffe00)215 980 y(hwaddr)47 b(=)24 b
+("08:00:20:01:a6:a5")120 1030 y(})120 1130 y(netif)f(ec0)h({)f(})0
+1292 y Fi(7.5.2)30 b(con\014g)15 b(Option)62 1383 y Fo(This)g(option)e(allo)o
+(ws)h(y)o(ou)g(to)f(sp)q(ecify)i(con\014guration)e(v)m(ariables)i(for)e(the)h
+(startup)f(scripts)h(\(`)p Fl(rc)p Fo(')e(scripts\).)20 b(A)0
+1433 y(simple)d(string)e(should)h(immediately)h(follo)o(w)e(the)g(k)o(eyw)o
+(ord.)62 1503 y(Example:)120 1574 y Fl(config)23 b("NFS_SERVER=true")120
+1624 y(config)g("ZEPHYR=true")62 1715 y Fo(This)16 b(option)f(is)h(curren)o
+(tly)g(unsupp)q(orted.)0 1877 y Fi(7.5.3)30 b(arc)n(h)16 b(Option)62
+1968 y Fo(This)g(de\014nes)g(the)g(arc)o(hitecture)f(of)g(the)g(mac)o(hine.)
+21 b(F)l(or)14 b(example:)120 2039 y Fl(arch)23 b(=)h(hp300)62
+2130 y Fo(This)12 b(is)g(in)o(tended)h(to)e(b)q(e)h(of)f(use)h(when)g
+(building)i(arc)o(hitecture)e(sp)q(eci\014c)h(moun)o(tmaps,)e(ho)o(w)o(ev)o
+(er,)g(the)h(option)0 2180 y(is)k(curren)o(tly)f(unsupp)q(orted.)0
+2342 y Fi(7.5.4)30 b(os)15 b(Option)62 2433 y Fo(This)h(de\014nes)g(the)g(op)
+q(erating)f(system)g(t)o(yp)q(e)g(of)g(the)g(host.)k(F)l(or)c(example:)120
+2504 y Fl(os)24 b(=)f(hpux)62 2595 y Fo(This)16 b(information)g(is)g(used)g
+(when)g(creating)g(the)g(`)p Fl(fstab)p Fo(')e(\014les,)i(for)f(example)i(in)
+f(c)o(ho)q(osing)g(whic)o(h)g(format)0 2645 y(to)f(use)g(for)g(the)g(`)p
+Fl(fstab)p Fo(')f(en)o(tries)h(within)i(the)e(\014le.)p eop
+%%Page: 35 37
+35 36 bop 0 -83 a Fo(Chapter)15 b(7:)k(FSinfo)1362 b(SMM:13-35)0
+158 y Fi(7.5.5)30 b(cluster)16 b(Option)62 250 y Fo(This)g(is)g(used)g(for)e
+(sp)q(ecifying)j(in)f(whic)o(h)g(cluster)g(the)f(mac)o(hine)h(b)q(elongs.)21
+b(F)l(or)15 b(example:)120 320 y Fl(cluster)23 b(=)h("theory")62
+412 y Fo(The)13 b(cluster)f(is)h(in)o(tended)g(to)e(b)q(e)i(used)g(when)f
+(generating)g(the)h(automoun)o(t)d(maps,)i(although)h(it)f(is)g(curren)o(tly)
+0 461 y(unsupp)q(orted.)0 657 y Fq(7.6)33 b Ff(FSinfo)16 b
+Fq(\014lesystems)62 748 y Fo(The)h(list)g(of)f(ph)o(ysically)j(attac)o(hed)d
+(\014lesystems)h(follo)o(ws)f(the)h(mac)o(hine)g(attributes.)24
+b(These)16 b(should)i(de\014ne)0 798 y(all)e(the)f(\014lesystems)h(a)o(v)m
+(ailable)g(from)f(this)g(mac)o(hine,)h(whether)f(exp)q(orted)g(or)g(not.)k
+(In)d(addition)g(to)e(the)i(device)0 848 y(name,)g(\014lesystems)g(ha)o(v)o
+(e)f(sev)o(eral)h(attributes,)f(suc)o(h)i(as)e(\014lesystem)h(t)o(yp)q(e,)g
+(moun)o(t)f(options,)h(and)g(`)p Fl(fsck)p Fo(')e(pass)0 897
+y(n)o(um)o(b)q(er)i(whic)o(h)g(are)e(needed)j(to)e(generate)f(`)p
+Fl(fstab)p Fo(')g(en)o(tries.)120 968 y Fl(filesystem)46 b(:)24
+b("fs")f(<)p Fp(device)p Fl(>)i("{")f Fp(list\()t Fl(fs_data)p
+Fp(\))g Fl("}")g(;)120 1068 y(fs_data)118 b(:)24 b(fs_data_attr)e("=")i(<)p
+Fp(string)p Fl(>)406 1117 y(|)g(mount)406 1167 y(;)120 1267
+y(fs_data_attr)406 1317 y(:)g("fstype")f(|)h("opts")f(|)g("passno")406
+1367 y(|)h("freq")f(|)h("dumpset")e(|)i("log")406 1416 y(;)62
+1508 y Fo(Here,)15 b Fl(<)p Fp(device)p Fl(>)h Fo(is)g(the)f(device)h(name)f
+(of)g(the)g(disk)h(\(for)e(example,)h(`)p Fl(/dev/dsk/2s0)p
+Fo('\).)i(The)f(device)g(name)0 1558 y(is)g(used)h(for)e(building)j(the)e
+(moun)o(t)g(maps)f(and)h(for)f(the)h(`)p Fl(fstab)p Fo(')f(\014le.)23
+b(The)16 b(attributes)f(that)g(can)h(b)q(e)h(sp)q(eci\014ed)0
+1607 y(are)e(sho)o(wn)g(in)h(the)f(follo)o(wing)h(section.)62
+1678 y(The)g Fp(FSinfo)i Fo(con\014guration)d(\014le)h(for)f
+Fl(dylan.doc.ic.ac.uk)d Fo(is)k(listed)h(b)q(elo)o(w.)120 1748
+y Fl(host)23 b(dylan.doc.ic.ac.uk)120 1848 y(fs)h(/dev/dsk/0s0)e({)120
+1898 y(fstype)h(=)h(swap)120 1948 y(})120 2047 y(fs)g(/dev/dsk/0s0)e({)120
+2097 y(fstype)h(=)h(hfs)120 2147 y(opts)f(=)h(rw,noquota,grpid)120
+2197 y(passno)f(=)h(0;)120 2247 y(freq)f(=)h(1;)120 2296 y(mount)f(/)h({)g(})
+120 2346 y(})120 2446 y(fs)g(/dev/dsk/1s0)e({)120 2496 y(fstype)h(=)h(hfs)120
+2545 y(opts)f(=)h(defaults)120 2595 y(passno)f(=)h(1;)120 2645
+y(freq)f(=)h(1;)p eop
+%%Page: 36 38
+36 37 bop 15 -83 a Fo(SMM:13-36)889 b(4.4)15 b(BSD)g(Automoun)o(ter)f
+(Reference)j(Man)o(ual)120 158 y Fl(mount)23 b(/usr)g({)120
+208 y(local)g({)120 258 y(exportfs)g("dougal)g(eden)g(dylan)g(zebedee)g
+(brian")120 308 y(volname)g(/nfs/hp300/local)120 358 y(})120
+407 y(})120 457 y(})120 557 y(fs)h(/dev/dsk/2s0)e({)120 607
+y(fstype)h(=)h(hfs)120 656 y(opts)f(=)h(defaults)120 706 y(passno)f(=)h(1;)
+120 756 y(freq)f(=)h(1;)120 806 y(mount)f(default)g({)120 856
+y(exportfs)g("toytown_clients)e(hangers_on")120 906 y(volname)i
+(/home/dylan/dk2)120 955 y(})120 1005 y(})120 1105 y(fs)h(/dev/dsk/3s0)e({)
+120 1155 y(fstype)h(=)h(hfs)120 1204 y(opts)f(=)h(defaults)120
+1254 y(passno)f(=)h(1;)120 1304 y(freq)f(=)h(1;)120 1354 y(mount)f(default)g
+({)120 1404 y(exportfs)g("toytown_clients)e(hangers_on")120
+1453 y(volname)i(/home/dylan/dk3)120 1503 y(})120 1553 y(})120
+1653 y(fs)h(/dev/dsk/5s0)e({)120 1703 y(fstype)h(=)h(hfs)120
+1752 y(opts)f(=)h(defaults)120 1802 y(passno)f(=)h(1;)120 1852
+y(freq)f(=)h(1;)120 1902 y(mount)f(default)g({)120 1952 y(exportfs)g
+("toytown_clients)e(hangers_on")120 2001 y(volname)i(/home/dylan/dk5)120
+2051 y(})120 2101 y(})0 2234 y Fi(7.6.1)30 b(fst)n(yp)r(e)15
+b(Option)62 2325 y Fo(This)g(sp)q(eci\014es)h(the)f(t)o(yp)q(e)f(of)g
+(\014lesystem)h(b)q(eing)h(declared)f(and)g(will)h(b)q(e)e(placed)i(in)o(to)e
+(the)h(`)p Fl(fstab)p Fo(')e(\014le)i(as)f(is.)0 2375 y(The)g(v)m(alue)g(of)f
+(this)h(option)f(will)i(b)q(e)f(handed)g(to)f Fl(mount)g Fo(as)f(the)i
+(\014lesystem)g(t)o(yp)q(e|it)g(should)g(ha)o(v)o(e)f(suc)o(h)h(v)m(alues)0
+2425 y(as)h Fl(4.2)p Fo(,)f Fl(nfs)h Fo(or)g Fl(swap)p Fo(.)k(The)c(v)m(alue)
+i(is)e(not)g(examined)i(for)d(correctness.)62 2496 y(There)21
+b(is)g(one)f(sp)q(ecial)i(case.)35 b(If)21 b(the)f(\014lesystem)h(t)o(yp)q(e)
+f(is)h(sp)q(eci\014ed)i(as)d(`)p Fl(export)p Fo(')e(then)j(the)f
+(\014lesystem)0 2545 y(information)d(will)h(not)f(b)q(e)g(added)h(to)e(the)h
+(host's)f(`)p Fl(fstab)p Fo(')f(information,)i(but)g(it)g(will)h(still)h(b)q
+(e)e(visible)i(on)e(the)0 2595 y(net)o(w)o(ork.)h(This)13 b(is)f(useful)h
+(for)f(de\014ning)i(hosts)d(whic)o(h)i(con)o(tain)g(referenced)g(v)o(olumes)f
+(but)h(whic)o(h)g(are)e(not)h(under)0 2645 y(full)17 b(con)o(trol)d(of)h
+Fp(FSinfo)p Fo(.)p eop
+%%Page: 37 39
+37 38 bop 0 -83 a Fo(Chapter)15 b(7:)k(FSinfo)1362 b(SMM:13-37)62
+158 y(Example:)120 229 y Fl(fstype)23 b(=)h(swap)0 378 y Fi(7.6.2)30
+b(opts)15 b(Option)62 470 y Fo(This)h(de\014nes)g(an)o(y)f(options)g(that)g
+(should)h(b)q(e)g(giv)o(en)g(to)e Fk(moun)o(t)p Fo(\(8\))g(in)i(the)f(`)p
+Fl(fstab)p Fo(')f(\014le.)21 b(F)l(or)15 b(example:)120 540
+y Fl(opts)23 b(=)h(rw,nosuid,grpid)0 690 y Fi(7.6.3)30 b(passno)15
+b(Option)62 781 y Fo(This)g(de\014nes)g(the)f Fk(fsc)o(k)p
+Fo(\(8\))f(pass)h(n)o(um)o(b)q(er)g(in)h(whic)o(h)g(to)f(c)o(hec)o(k)g(the)g
+(\014lesystem.)20 b(This)15 b(v)m(alue)g(will)h(b)q(e)f(placed)0
+831 y(in)o(to)g(the)g(`)p Fl(fstab)p Fo(')f(\014le.)62 902
+y(Example:)120 972 y Fl(passno)23 b(=)h(1)0 1122 y Fi(7.6.4)30
+b(freq)15 b(Option)62 1213 y Fo(This)k(de\014nes)f(the)g(in)o(terv)m(al)h
+(\(in)f(da)o(ys\))f(b)q(et)o(w)o(een)h(dumps.)28 b(The)18 b(v)m(alue)h(is)f
+(placed)h(as)f(is)g(in)o(to)g(the)f(`)p Fl(fstab)p Fo(')0 1263
+y(\014le.)62 1333 y(Example:)120 1404 y Fl(freq)23 b(=)h(3)0
+1553 y Fi(7.6.5)30 b(moun)n(t)15 b(Option)62 1645 y Fo(This)e(de\014nes)g
+(the)f(moun)o(tp)q(oin)o(t)g(at)f(whic)o(h)i(to)f(place)h(the)f
+(\014lesystem.)19 b(If)12 b(the)h(moun)o(tp)q(oin)o(t)f(of)f(the)h
+(\014lesystem)0 1694 y(is)19 b(sp)q(eci\014ed)h(as)e Fl(default)p
+Fo(,)f(then)i(the)f(\014lesystem)h(will)g(b)q(e)g(moun)o(ted)f(in)h(the)f
+(automoun)o(ter's)f(tree)h(under)h(its)0 1744 y(v)o(olume)d(name)f(and)g(the)
+g(moun)o(t)g(will)i(automatically)f(b)q(e)f(inherited)j(b)o(y)d(the)g
+(automoun)o(ter.)62 1815 y(F)l(ollo)o(wing)21 b(the)e(moun)o(tp)q(oin)o(t,)i
+(namespace)f(information)f(for)g(the)h(\014lesystem)g(ma)o(y)f(b)q(e)i
+(describ)q(ed.)35 b(The)0 1865 y(options)15 b(that)g(can)g(b)q(e)h(giv)o(en)g
+(here)f(are)g Fl(exportfs)p Fo(,)f Fl(volname)g Fo(and)h Fl(sel)p
+Fo(.)62 1935 y(The)h(format)e(is:)120 2006 y Fl(mount)166 b(:)24
+b("mount")f(vol_tree)g(;)120 2105 y(vol_tree)94 b(:)24 b Fp(list\()t
+Fl(vol_tree_attr)p Fp(\))f Fl(;)120 2205 y(vol_tree_attr)406
+2255 y(:)48 b(<)p Fp(string)p Fl(>)24 b("{")f Fp(list\()t Fl(vol_tree_info)p
+Fp(\))g Fl(vol_tree)g("}")h(;)120 2355 y(vol_tree_info)406
+2404 y(:)g("exportfs")f(<)p Fp(exp)q(ort-data)p Fl(>)406 2454
+y(|)h("volname")f(<)p Fp(v)o(olname)p Fl(>)406 2504 y(|)h("sel")f(<)p
+Fp(selector-list)p Fl(>)406 2554 y(;)62 2645 y Fo(Example:)p
+eop
+%%Page: 38 40
+38 39 bop 15 -83 a Fo(SMM:13-38)889 b(4.4)15 b(BSD)g(Automoun)o(ter)f
+(Reference)j(Man)o(ual)120 158 y Fl(mount)23 b(default)g({)215
+208 y(exportfs)g("dylan)g(dougal)g(florence)g(zebedee")215
+258 y(volname)g(/vol/andrew)120 308 y(})62 399 y Fo(In)15 b(the)f(ab)q(o)o(v)
+o(e)f(example,)i(the)f(\014lesystem)g(curren)o(tly)h(b)q(eing)g(declared)g
+(will)g(ha)o(v)o(e)f(an)f(en)o(try)h(placed)h(in)o(to)f(the)0
+449 y(`)p Fl(exports)p Fo(')d(\014le)j(allo)o(wing)g(the)f(\014lesystem)h(to)
+e(b)q(e)i(exp)q(orted)f(to)g(the)g(mac)o(hines)g Fl(dylan)p
+Fo(,)g Fl(dougal)p Fo(,)f Fl(florence)g Fo(and)0 499 y Fl(zebedee)p
+Fo(.)18 b(The)d(v)o(olume)f(name)g(b)o(y)g(whic)o(h)h(the)f(\014lesystem)g
+(will)i(b)q(e)f(referred)f(to)f(remotely)l(,)h(is)h(`)p Fl(/vol/andrew)p
+Fo('.)0 549 y(By)h(declaring)i(the)e(moun)o(tp)q(oin)o(t)g(to)f(b)q(e)i
+Fl(default)p Fo(,)e(the)h(\014lesystem)h(will)g(b)q(e)g(moun)o(ted)f(on)g
+(the)g(lo)q(cal)h(mac)o(hine)0 598 y(in)f(the)f(automoun)o(ter)f(tree,)h
+(where)g Fp(Amd)j Fo(will)f(automatically)e(inherit)i(the)e(moun)o(t)g(as)f
+(`)p Fl(/vol/andrew)p Fo('.)0 669 y(`)p Fl(exportfs)p Fo(')240
+732 y(a)g(string)h(de\014ning)h(whic)o(h)f(mac)o(hines)g(the)g(\014lesystem)g
+(ma)o(y)f(b)q(e)h(exp)q(orted)g(to.)k(This)c(is)g(copied,)g(as)240
+781 y(is,)g(in)o(to)h(the)f(`)p Fl(exports)p Fo(')e(\014le|no)k(sanit)o(y)e
+(c)o(hec)o(king)h(is)g(p)q(erformed)f(on)g(this)h(string.)0
+844 y(`)p Fl(volname)p Fo(')46 b(a)19 b(string)h(whic)o(h)g(declares)h(the)f
+(remote)f(name)h(b)o(y)f(whic)o(h)i(to)e(reference)h(the)g(\014lesystem.)34
+b(The)240 894 y(string)18 b(is)h(en)o(tered)g(in)o(to)f(a)h(dictionary)g(and)
+g(allo)o(ws)f(y)o(ou)g(to)g(refer)g(to)g(this)h(\014lesystem)g(in)g(other)240
+944 y(places)d(b)o(y)f(this)h(v)o(olume)f(name.)0 1006 y(`)p
+Fl(sel)p Fo(')142 b(a)15 b(string)g(whic)o(h)h(is)g(placed)g(in)o(to)f(the)h
+(automoun)o(ter)e(maps)h(as)f(a)h(selector)h(for)e(the)i(\014lesystem.)0
+1150 y Fi(7.6.6)30 b(dumpset)15 b(Option)62 1242 y Fo(This)d(pro)o(vides)f
+(supp)q(ort)g(for)f(Imp)q(erial)j(College's)e(lo)q(cal)h(\014le)g(bac)o(kup)g
+(to)q(ols)e(and)h(is)h(not)e(do)q(cumen)o(ted)i(further)0 1292
+y(here.)0 1436 y Fi(7.6.7)30 b(log)14 b(Option)62 1527 y Fo(Sp)q(eci\014es)f
+(the)e(log)g(device)i(for)d(the)h(curren)o(t)g(\014lesystem.)19
+b(This)11 b(is)h(ignored)f(if)h(not)e(required)i(b)o(y)f(the)g(particular)0
+1577 y(\014lesystem)16 b(t)o(yp)q(e.)0 1744 y Fq(7.7)33 b Ff(FSinfo)16
+b Fq(static)g(moun)n(ts)62 1836 y Fo(Eac)o(h)j(host)g(ma)o(y)f(also)h(ha)o(v)
+o(e)g(a)g(n)o(um)o(b)q(er)g(of)g(statically)h(moun)o(ted)f(\014lesystems.)32
+b(F)l(or)19 b(example,)h(the)f(host)0 1885 y(ma)o(y)14 b(b)q(e)h(a)f
+(diskless)i(w)o(orkstation)e(in)h(whic)o(h)g(case)g(it)g(will)h(ha)o(v)o(e)e
+(no)h Fl(fs)f Fo(declarations.)20 b(In)15 b(this)g(case)g(the)g
+Fl(mount)0 1935 y Fo(declaration)i(is)g(used)f(to)g(determine)h(from)e(where)
+i(its)f(\014lesystems)h(will)h(b)q(e)e(moun)o(ted.)23 b(In)17
+b(addition)g(to)f(b)q(eing)0 1985 y(added)e(to)e(the)h(`)p
+Fl(fstab)p Fo(')f(\014le,)i(this)g(information)f(can)g(also)g(b)q(e)h(used)g
+(to)e(generate)h(a)g(suitable)h(`)p Fl(bootparams)p Fo(')d(\014le.)120
+2056 y Fl(mount)166 b(:)24 b("mount")f(<)p Fp(v)o(olname)p
+Fl(>)h Fp(list\()t Fl(localinfo)p Fp(\))g Fl(;)120 2155 y(localinfo)70
+b(:)24 b(localinfo_attr)e(<)p Fp(string)p Fl(>)i(;)120 2255
+y(localinfo_attr)406 2305 y(:)g("as")406 2355 y(|)g("from")406
+2404 y(|)g("fstype")406 2454 y(|)g("opts")406 2504 y(;)62 2595
+y Fo(The)17 b(\014lesystem)g(sp)q(eci\014ed)h(to)e(b)q(e)h(moun)o(ted)f(will)
+i(b)q(e)f(searc)o(hed)f(for)g(in)h(the)f(dictionary)i(of)d(v)o(olume)i(names)
+0 2645 y(built)g(when)e(scanning)h(the)g(list)g(of)e(hosts')h(de\014nitions.)
+p eop
+%%Page: 39 41
+39 40 bop 0 -83 a Fo(Chapter)15 b(7:)k(FSinfo)1362 b(SMM:13-39)62
+158 y(The)16 b(attributes)f(ha)o(v)o(e)f(the)i(follo)o(wing)g(seman)o(tics:)0
+229 y(`)p Fl(from)e Fp(mac)o(hine)s Fo(')240 293 y(moun)o(t)h(the)g
+(\014lesystem)h(from)e(the)i(mac)o(hine)g(with)f(the)g(hostname)g(of)g
+Fp(mac)o(hine)p Fo(.)0 356 y(`)p Fl(as)g Fp(moun)o(tp)q(oin)o(t)q
+Fo(')240 420 y(moun)o(t)e(the)h(\014lesystem)g(lo)q(cally)i(as)d(the)h(name)f
+(giv)o(en,)h(in)h(case)e(this)h(is)h(di\013eren)o(t)f(from)e(the)i(adv)o(er-)
+240 470 y(tised)i(v)o(olume)f(name)h(of)e(the)i(\014lesystem.)0
+534 y(`)p Fl(opts)e Fp(options)r Fo(')240 598 y(nativ)o(e)h
+Fk(moun)o(t)p Fo(\(8\))f(options.)0 661 y(`)p Fl(fstype)g Fp(t)o(yp)q(e)s
+Fo(')240 725 y(t)o(yp)q(e)h(of)g(\014lesystem)h(to)e(b)q(e)i(moun)o(ted.)62
+816 y(An)g(example:)120 887 y Fl(mount)23 b(/export/exec/hp300/local)e(as)i
+(/usr/local)62 978 y Fo(If)15 b(the)g(moun)o(tp)q(oin)o(t)g(sp)q(eci\014ed)i
+(is)f(either)g(`)p Fl(/)p Fo(')e(or)g(`)p Fl(swap)p Fo(',)f(the)i(mac)o(hine)
+h(will)h(b)q(e)e(considered)i(to)d(b)q(e)h(b)q(o)q(oting)0
+1028 y(o\013)f(the)h(net)f(and)h(this)g(will)i(b)q(e)e(noted)g(for)f(use)h
+(in)g(generating)g(a)g(`)p Fl(bootparams)p Fo(')d(\014le)k(for)e(the)h(host)f
+(whic)o(h)i(o)o(wns)0 1078 y(the)f(\014lesystems.)0 1254 y
+Fq(7.8)33 b(De\014ning)15 b(an)h Ff(Amd)g Fq(Moun)n(t)g(Map)f(in)h
+Ff(FSinfo)62 1346 y Fo(The)i(maps)f(used)i(b)o(y)e Fp(Amd)j
+Fo(can)d(b)q(e)i(constructed)e(from)g Fp(FSinfo)j Fo(b)o(y)e(de\014ning)h
+(all)f(the)g(automoun)o(t)f(trees.)0 1396 y Fp(FSinfo)h Fo(tak)o(es)c(all)i
+(the)g(de\014nitions)h(found)e(and)h(builds)h(one)e(map)g(for)g(eac)o(h)g
+(top)g(lev)o(el)h(tree.)62 1466 y(The)23 b(automoun)o(t)e(tree)i(is)g
+(usually)h(de\014ned)f(last.)42 b(A)23 b(single)g(automoun)o(t)f
+(con\014guration)g(will)j(usually)0 1516 y(apply)e(to)f(an)g(en)o(tire)h
+(managemen)o(t)e(domain.)41 b(One)23 b Fl(automount)e Fo(declaration)i(is)g
+(needed)h(for)d(eac)o(h)i Fp(Amd)0 1566 y Fo(automoun)o(t)11
+b(p)q(oin)o(t.)20 b Fp(FSinfo)15 b Fo(determines)f(whether)f(the)f(automoun)o
+(t)g(p)q(oin)o(t)h(is)g Fp(direct)h Fo(\(see)f(Section)g(5.9)f([Direct)0
+1616 y(Automoun)o(t)20 b(Filesystem],)i(page)41 b(SMM:13-25\))18
+b(or)j Fp(indirect)i Fo(\(see)d(Section)i(5.12)d([T)l(op-lev)o(el)j
+(Filesystem],)0 1665 y(page)e(SMM:13-26\).)c(Direct)11 b(automoun)o(t)e(p)q
+(oin)o(ts)h(are)g(distinguished)j(b)o(y)d(the)g(fact)g(that)f(there)h(is)h
+(no)f(underlying)0 1715 y Fp(automoun)o(t)p 220 1715 14 2 v
+15 w(tree)p Fo(.)120 1786 y Fl(automount)70 b(:)24 b("automount")e
+(opt\(auto_opts)p Fp(\))h Fl(automount_tree)f(;)120 1885 y(auto_opts)70
+b(:)24 b("opts")f(<)p Fp(moun)o(t-options)p Fl(>)h(;)120 1985
+y(automount_tree)406 2035 y(:)g Fp(list\()t Fl(automount_attr)p
+Fp(\))406 2085 y Fl(;)120 2184 y(automount_attr)406 2234 y(:)g(<)p
+Fp(string)p Fl(>)g("=")f(<)p Fp(v)o(olname)p Fl(>)406 2284
+y(|)h(<)p Fp(string)p Fl(>)g("->")f(<)p Fp(symlink)p Fl(>)406
+2334 y(|)h(<)p Fp(string)p Fl(>)g("{")f(automount_tree)f("}")406
+2384 y(;)62 2475 y Fo(If)15 b Fl(<)p Fp(moun)o(t-options)p
+Fl(>)e Fo(is)i(giv)o(en,)f(then)g(it)g(is)h(the)f(string)g(to)f(b)q(e)i
+(placed)g(in)g(the)f(maps)g(for)f Fp(Amd)j Fo(for)d(the)i Fl(opts)0
+2525 y Fo(option.)62 2595 y(A)d Fp(map)g Fo(is)g(t)o(ypically)h(a)e(tree)g
+(of)g(\014lesystems,)i(for)d(example)j(`)p Fl(home)p Fo(')d(normally)i(con)o
+(tains)f(a)g(tree)g(of)g(\014lesystems)0 2645 y(represen)o(ting)16
+b(other)f(mac)o(hines)h(in)g(the)f(net)o(w)o(ork.)p eop
+%%Page: 40 42
+40 41 bop 15 -83 a Fo(SMM:13-40)889 b(4.4)15 b(BSD)g(Automoun)o(ter)f
+(Reference)j(Man)o(ual)62 158 y(A)g(map)g(can)h(either)f(b)q(e)h(giv)o(en)g
+(as)f(a)f(name)h(represen)o(ting)h(an)f(already)h(de\014ned)g(v)o(olume)g
+(name,)f(or)f(it)i(can)0 208 y(b)q(e)g(a)g(tree.)27 b(A)18
+b(tree)g(is)g(represen)o(ted)g(b)o(y)g(placing)h(braces)f(after)f(the)h
+(name.)28 b(F)l(or)17 b(example,)i(to)e(de\014ne)i(a)e(tree)0
+258 y(`)p Fl(/vol)p Fo(',)c(the)j(follo)o(wing)g(map)f(w)o(ould)g(b)q(e)h
+(de\014ned:)120 329 y Fl(automount)23 b(/vol)g({)h(})62 420
+y Fo(Within)17 b(a)d(tree,)h(the)g(only)h(items)g(that)e(can)h(app)q(ear)h
+(are)f(more)f(maps.)20 b(F)l(or)15 b(example:)120 490 y Fl(automount)23
+b(/vol)g({)215 540 y(andrew)g({)h(})215 590 y(X11)g({)g(})120
+640 y(})62 731 y Fo(In)13 b(this)g(case,)g Fp(FSinfo)i Fo(will)f(lo)q(ok)e
+(for)g(v)o(olumes)h(named)f(`)p Fl(/vol/andrew)p Fo(')e(and)j(`)p
+Fl(/vol/X11)p Fo(')e(and)h(a)g(map)g(en)o(try)0 781 y(will)17
+b(b)q(e)e(generated)h(for)e(eac)o(h.)20 b(If)15 b(the)g(v)o(olumes)h(are)f
+(de\014ned)h(more)f(than)g(once,)g(then)g Fp(FSinfo)j Fo(will)e(generate)f(a)
+0 831 y(series)h(of)f(alternate)g(en)o(tries)g(for)g(them)g(in)h(the)f(maps.)
+62 901 y(Instead)j(of)g(a)f(tree,)h(either)g(a)f(link)i(\()p
+Fp(name)h Fl(->)d Fp(destination)p Fo(\))i(or)e(a)g(reference)h(can)g(b)q(e)g
+(sp)q(eci\014ed)i(\()p Fp(name)g Fl(=)0 951 y Fp(destination)p
+Fo(\).)i(A)16 b(link)h(creates)f(a)f(sym)o(b)q(olic)i(link)g(to)f(the)f
+(string)h(sp)q(eci\014ed,)i(without)e(further)f(pro)q(cessing)i(the)0
+1001 y(en)o(try)l(.)i(A)13 b(reference)h(will)h(examine)f(the)f(destination)h
+(\014lesystem)g(and)g(optimise)g(the)f(reference.)20 b(F)l(or)12
+b(example,)0 1051 y(to)j(create)g(an)g(en)o(try)f(for)h Fl(njw)g
+Fo(in)h(the)f(`)p Fl(/homes)p Fo(')f(map,)g(either)i(of)f(the)g(t)o(w)o(o)f
+(forms)h(can)g(b)q(e)h(used:)120 1121 y Fl(automount)23 b(/homes)g({)215
+1171 y(njw)h(->)f(/home/dylan/njw)120 1221 y(})62 1312 y Fo(or)120
+1383 y Fl(automount)g(/homes)g({)215 1433 y(njw)h(=)g(/home/dylan/njw)120
+1483 y(})62 1574 y Fo(In)13 b(the)e(\014rst)h(example,)g(when)h(`)p
+Fl(/homes/njw)p Fo(')c(is)j(referenced)h(from)e Fp(Amd)p Fo(,)h(a)f(link)i
+(will)h(b)q(e)e(created)g(leading)h(to)0 1624 y(`)p Fl(/home/dylan/njw)p
+Fo(')f(and)j(the)g(automoun)o(ter)e(will)j(b)q(e)g(referenced)g(a)e(second)h
+(time)g(to)f(resolv)o(e)h(this)g(\014lename.)0 1673 y(The)g(map)g(en)o(try)g
+(w)o(ould)h(b)q(e:)120 1744 y Fl(njw)23 b(type:=link;fs:=/home/dylan/nj)o(w)
+62 1835 y Fo(In)17 b(the)g(second)g(example,)g(the)f(destination)i(directory)
+e(is)h(analysed)g(and)g(found)g(to)e(b)q(e)j(in)f(the)f(\014lesystem)0
+1885 y(`)p Fl(/home/dylan)p Fo(')c(whic)o(h)j(has)g(previously)g(b)q(een)h
+(de\014ned)g(in)f(the)f(maps.)20 b(Hence)15 b(the)f(map)g(en)o(try)g(will)j
+(lo)q(ok)d(lik)o(e:)120 1956 y Fl(njw)23 b(rhost:=dylan;rfs:=/home/dylan)o
+(;sublink)o(:=njw)62 2047 y Fo(Creating)15 b(only)h(one)f(sym)o(b)q(olic)i
+(link,)f(and)f(one)h(access)f(to)g Fp(Amd)p Fo(.)0 2292 y Fq(7.9)33
+b Ff(FSinfo)16 b Fq(Command)f(Line)h(Options)62 2384 y Fp(FSinfo)i
+Fo(is)e(started)e(from)h(the)g(command)g(line)i(b)o(y)e(using)h(the)f
+(command:)120 2454 y Fl(fsinfo)23 b([)p Fp(options)r Fl(])h(files)f(...)62
+2545 y Fo(The)16 b(input)h(to)d Fp(FSinfo)19 b Fo(is)d(a)f(single)i(set)e(of)
+g(de\014nitions)i(of)e(mac)o(hines)i(and)e(automoun)o(t)g(maps.)20
+b(If)c(m)o(ultiple)0 2595 y(\014les)21 b(are)e(giv)o(en)h(on)f(the)h
+(command-line,)i(then)e(the)f(\014les)i(are)e(concatenated)h(together)f(to)f
+(form)h(the)h(input)0 2645 y(source.)g(The)15 b(\014les)i(are)d(passed)i
+(individuall)q(y)i(through)d(the)g(C)g(pre-pro)q(cessor)g(b)q(efore)h(b)q
+(eing)g(parsed.)p eop
+%%Page: 41 43
+41 42 bop 0 -83 a Fo(Chapter)15 b(7:)k(FSinfo)1362 b(SMM:13-41)62
+158 y(Sev)o(eral)18 b(options)f(de\014ne)i(a)e(pre\014x)h(for)e(the)i(name)f
+(of)g(an)g(output)g(\014le.)27 b(If)18 b(the)f(pre\014x)h(is)g(not)f(sp)q
+(eci\014ed)i(no)0 208 y(output)14 b(of)f(that)g(t)o(yp)q(e)h(is)g(pro)q
+(duced.)20 b(The)14 b(su\016x)g(used)g(will)i(corresp)q(ond)e(either)g(to)f
+(the)h(hostname)g(to)f(whic)o(h)h(a)0 258 y(\014le)j(b)q(elongs,)f(or)f(to)g
+(the)g(t)o(yp)q(e)h(of)f(output)g(if)h(only)g(one)g(\014le)g(is)g(pro)q
+(duced.)22 b(Dumpsets)16 b(and)g(the)f(`)p Fl(bootparams)p
+Fo(')0 308 y(\014le)h(are)e(in)h(the)g(latter)f(class.)20 b(T)l(o)14
+b(put)g(the)h(output)f(in)o(to)h(a)f(sub)q(directory)h(simply)h(put)f(a)f(`)p
+Fl(/)p Fo(')f(at)h(the)h(end)g(of)f(the)0 358 y(pre\014x,)h(making)h(sure)f
+(that)f(the)i(directory)f(has)g(already)h(b)q(een)g(made)f(b)q(efore)h
+(running)g(`)p Fl(fsinfo)p Fo('.)0 550 y Fi(7.9.1)30 b Fd(-a)15
+b Fe(auto)q(dir)62 642 y Fo(Sp)q(eci\014es)h(the)e(directory)g(name)g(in)g
+(whic)o(h)h(to)e(place)i(the)f(automoun)o(ter's)e(moun)o(tp)q(oin)o(ts.)19
+b(This)14 b(defaults)h(to)0 691 y(`)p Fl(/a)p Fo('.)k(Some)c(sites)h(ha)o(v)o
+(e)e(the)i(auto)q(dir)f(set)g(to)g(b)q(e)g(`)p Fl(/amd)p Fo(',)f(and)h(this)h
+(w)o(ould)g(b)q(e)f(ac)o(hiev)o(ed)i(b)o(y:)120 762 y Fl(fsinfo)23
+b(-a)h(/amd)f(...)0 955 y Fi(7.9.2)30 b Fd(-b)15 b Fe(b)q(o)q(otparams)62
+1046 y Fo(This)i(sp)q(eci\014es)h(the)f(pre\014x)g(for)f(the)g(`)p
+Fl(bootparams)p Fo(')e(\014lename.)25 b(If)16 b(it)h(is)g(not)f(giv)o(en,)h
+(then)f(the)h(\014le)g(will)h(not)0 1096 y(b)q(e)h(generated.)27
+b(The)18 b(`)p Fl(bootparams)p Fo(')e(\014le)j(will)h(b)q(e)e(constructed)g
+(for)f(the)h(destination)h(mac)o(hine)g(and)f(will)h(b)q(e)0
+1146 y(placed)g(in)o(to)f(a)f(\014le)i(named)f(`)p Fl(bootparams)p
+Fo(')e(and)i(pre\014xed)h(b)o(y)f(this)g(string.)28 b(The)18
+b(\014le)h(generated)e(con)o(tains)h(a)0 1195 y(list)e(of)f(en)o(tries)g
+(describing)i(eac)o(h)f(diskless)g(clien)o(t)h(that)d(can)i(b)q(o)q(ot)f
+(from)f(the)h(destination)h(mac)o(hine.)62 1266 y(As)f(an)g(example,)g(to)f
+(create)g(a)h(`)p Fl(bootparams)p Fo(')d(\014le)k(in)g(the)e(directory)h(`)p
+Fl(generic)p Fo(',)e(the)i(follo)o(wing)g(w)o(ould)g(b)q(e)0
+1316 y(used:)120 1386 y Fl(fsinfo)23 b(-b)h(generic/)e(...)0
+1579 y Fi(7.9.3)30 b Fd(-d)15 b Fe(dumpsets)62 1670 y Fo(This)k(sp)q
+(eci\014es)g(the)f(pre\014x)g(for)f(the)h(`)p Fl(dumpsets)p
+Fo(')d(\014le.)29 b(If)18 b(it)g(is)g(not)f(sp)q(eci\014ed,)j(then)e(the)g
+(\014le)h(will)g(not)e(b)q(e)0 1720 y(generated.)h(The)12 b(\014le)g(will)g
+(b)q(e)g(for)e(the)h(destination)h(mac)o(hine)f(and)h(will)g(b)q(e)g(placed)g
+(in)o(to)f(a)f(\014lename)i(`)p Fl(dumpsets)p Fo(',)0 1770
+y(pre\014xed)k(b)o(y)f(this)h(string.)k(The)15 b(`)p Fl(dumpsets)p
+Fo(')e(\014le)k(is)e(for)g(use)h(b)o(y)f(Imp)q(erial)i(College's)e(lo)q(cal)i
+(bac)o(kup)e(system.)62 1840 y(F)l(or)22 b(example,)i(to)d(create)g(a)h
+(dumpsets)g(\014le)h(in)g(the)f(directory)g(`)p Fl(generic)p
+Fo(',)g(then)g(y)o(ou)f(w)o(ould)i(use)f(the)0 1890 y(follo)o(wing:)120
+1961 y Fl(fsinfo)h(-d)h(generic/)e(...)0 2153 y Fi(7.9.4)30
+b Fd(-e)15 b Fe(exp)q(ortfs)62 2245 y Fo(De\014nes)d(the)g(pre\014x)g(for)f
+(the)g(`)p Fl(exports)p Fo(')f(\014les.)20 b(If)11 b(it)h(is)g(not)f(giv)o
+(en,)i(then)e(the)h(\014le)h(will)g(not)e(b)q(e)h(generated.)19
+b(F)l(or)0 2295 y(eac)o(h)14 b(mac)o(hine)h(de\014ned)g(in)f(the)g
+(con\014guration)g(\014les)h(as)e(ha)o(ving)h(disks,)h(an)f(`)p
+Fl(exports)p Fo(')e(\014le)j(is)f(constructed)g(and)0 2344
+y(giv)o(en)k(a)e(\014lename)j(determined)f(b)o(y)f(the)g(name)g(of)g(the)g
+(mac)o(hine,)h(pre\014xed)g(with)g(this)f(string.)26 b(If)17
+b(a)g(mac)o(hine)0 2394 y(is)j(de\014ned)h(as)e(diskless,)j(then)e(no)f(`)p
+Fl(exports)p Fo(')f(\014le)j(will)g(b)q(e)f(created)g(for)e(it.)34
+b(The)19 b(\014les)i(con)o(tain)f(en)o(tries)g(for)0 2444 y(directories)c(on)
+f(the)h(mac)o(hine)g(that)e(ma)o(y)h(b)q(e)g(exp)q(orted)h(to)f(clien)o(ts.)
+62 2515 y(Example:)k(T)l(o)10 b(create)h(the)g(`)p Fl(exports)p
+Fo(')e(\014les)j(for)e(eac)o(h)h(diskful)h(mac)o(hine)g(and)f(place)h(them)f
+(in)o(to)g(the)g(directory)0 2564 y(`)p Fl(exports)p Fo(':)120
+2635 y Fl(fsinfo)23 b(-e)h(exports/)e(...)p eop
+%%Page: 42 44
+42 43 bop 15 -83 a Fo(SMM:13-42)889 b(4.4)15 b(BSD)g(Automoun)o(ter)f
+(Reference)j(Man)o(ual)0 158 y Fi(7.9.5)30 b Fd(-f)15 b Fe(fstab)62
+250 y Fo(This)f(de\014nes)g(the)f(pre\014x)h(for)e(the)h(`)p
+Fl(fstab)p Fo(')f(\014les.)20 b(The)13 b(\014les)i(will)f(only)g(b)q(e)g
+(created)f(if)g(this)h(pre\014x)f(is)h(de\014ned.)0 299 y(F)l(or)21
+b(eac)o(h)h(mac)o(hine)g(de\014ned)h(in)g(the)f(con\014guration)g(\014les,)i
+(a)d(`)p Fl(fstab)p Fo(')f(\014le)j(is)f(created)g(with)g(the)g(\014lename)0
+349 y(determined)c(b)o(y)f(pre\014xing)h(this)g(string)f(with)g(the)g(name)h
+(of)e(the)h(mac)o(hine.)27 b(These)17 b(\014les)h(con)o(tain)f(en)o(tries)h
+(for)0 399 y(\014lesystems)e(and)f(partitions)h(to)e(moun)o(t)h(at)f(b)q(o)q
+(ot)h(time.)62 470 y(Example,)h(to)e(create)h(the)g(\014les)i(in)f(the)f
+(directory)g(`)p Fl(fstabs)p Fo(':)120 540 y Fl(fsinfo)23 b(-f)h(fstabs/)f
+(...)0 686 y Fi(7.9.6)30 b Fd(-h)15 b Fe(hostname)62 778 y
+Fo(De\014nes)d(the)g(hostname)f(of)g(the)h(destination)g(mac)o(hine)g(to)f
+(pro)q(cess)h(for.)18 b(If)12 b(this)g(is)g(not)f(sp)q(eci\014ed,)j(it)d
+(defaults)0 827 y(to)k(the)g(lo)q(cal)h(mac)o(hine)g(name,)f(as)g(returned)g
+(b)o(y)h Fk(gethostname)p Fo(\(2\).)62 898 y(Example:)120 969
+y Fl(fsinfo)23 b(-h)h(dylan.doc.ic.ac.uk)d(...)0 1115 y Fi(7.9.7)30
+b Fd(-m)15 b Fe(moun)o(t-maps)62 1206 y Fo(De\014nes)k(the)f(pre\014x)g(for)g
+(the)g(automoun)o(ter)f(\014les.)29 b(The)18 b(maps)g(will)i(only)e(b)q(e)h
+(pro)q(duced)g(if)g(this)f(pre\014x)g(is)0 1256 y(de\014ned.)j(The)15
+b(moun)o(t)f(maps)g(suitable)i(for)e(the)h(net)o(w)o(ork)e(de\014ned)j(b)o(y)
+f(the)g(con\014guration)g(\014les)g(will)h(b)q(e)g(placed)0
+1306 y(in)o(to)f(\014les)h(with)g(names)f(calculated)i(b)o(y)e(pre\014xing)h
+(this)g(string)f(to)f(the)i(name)f(of)g(eac)o(h)g(map.)62 1376
+y(F)l(or)g(example,)g(to)g(create)g(the)g(automoun)o(ter)f(maps)h(and)h
+(place)g(them)f(in)h(the)f(directory)h(`)p Fl(automaps)p Fo(':)120
+1447 y Fl(fsinfo)23 b(-m)h(automaps/)e(...)0 1593 y Fi(7.9.8)30
+b Fd(-q)62 1684 y Fo(Selects)21 b(quiet)f(mo)q(de.)34 b Fp(FSinfo)23
+b Fo(suppress)d(the)g(\\running)g(commen)o(tary")f(and)h(only)g(outputs)f(an)
+o(y)h(error)0 1734 y(messages)15 b(whic)o(h)h(are)f(generated.)0
+1880 y Fi(7.9.9)30 b Fd(-v)62 1971 y Fo(Selects)21 b(v)o(erb)q(ose)e(mo)q
+(de.)34 b(When)19 b(this)h(is)g(activ)m(ated,)h(the)f(program)e(will)j
+(displa)o(y)g(more)e(messages,)h(and)0 2021 y(displa)o(y)c(all)g(the)e
+(information)h(disco)o(v)o(ered)h(when)f(p)q(erforming)g(the)g(seman)o(tic)g
+(analysis)g(phase.)20 b(Eac)o(h)15 b(v)o(erb)q(ose)0 2071 y(message)g(is)g
+(output)g(to)g(`)p Fl(stdout)p Fo(')f(on)h(a)g(line)h(starting)f(with)h(a)e
+(`)p Fl(#)p Fo(')h(c)o(haracter.)0 2217 y Fi(7.9.10)29 b Fd(-D)16
+b Fe(name[=defn])62 2308 y Fo(De\014nes)f(a)g(sym)o(b)q(ol)f
+Fp(name)j Fo(for)d(the)h(prepro)q(cessor)g(when)g(reading)g(the)f
+(con\014guration)h(\014les.)21 b(Equiv)m(alen)o(t)16 b(to)0
+2358 y Fl(#define)e Fo(directiv)o(e.)0 2504 y Fi(7.9.11)29
+b Fd(-I)16 b Fe(directory)62 2595 y Fo(This)g(option)g(is)g(passed)g(in)o(to)
+f(the)h(prepro)q(cessor)f(for)g(the)h(con\014guration)f(\014les.)22
+b(It)15 b(sp)q(eci\014es)j(directories)e(in)0 2645 y(whic)o(h)g(to)f(\014nd)h
+(include)h(\014les)p eop
+%%Page: 43 45
+43 44 bop 0 -83 a Fo(Chapter)15 b(7:)k(FSinfo)1362 b(SMM:13-43)0
+158 y Fi(7.9.12)29 b Fd(-U)16 b Fe(name)62 250 y Fo(Remo)o(v)o(es)f(an)o(y)g
+(initial)i(de\014nition)g(of)e(the)h(sym)o(b)q(ol)f Fp(name)p
+Fo(.)20 b(In)o(v)o(erse)15 b(of)g(the)g Fl(-D)g Fo(option.)0
+423 y Fq(7.10)32 b(Errors)16 b(pro)r(duced)g(b)n(y)g Ff(FSinfo)62
+514 y Fo(The)g(follo)o(wing)g(table)f(do)q(cumen)o(ts)h(the)f(errors)f(and)i
+(w)o(arnings)f(whic)o(h)h Fp(FSinfo)i Fo(ma)o(y)c(pro)q(duce.)0
+585 y Fl(can't)23 b(open)g Fp(\014lename)28 b Fl(for)c(writing)240
+648 y Fo(Occurs)16 b(if)g(an)o(y)e(errors)h(are)g(encoun)o(tered)h(when)f(op)
+q(ening)i(an)e(output)g(\014le.)0 712 y Fl(unknown)23 b(host)g(attribute)240
+775 y Fo(Occurs)16 b(if)g(an)f(unrecognised)h(k)o(eyw)o(ord)f(is)g(used)h
+(when)g(de\014ning)h(a)d(host.)0 839 y Fl(unknown)23 b(filesystem)f
+(attribute)240 902 y Fo(Occurs)16 b(if)g(an)f(unrecognised)h(k)o(eyw)o(ord)f
+(is)g(used)h(when)g(de\014ning)h(a)d(host's)h(\014lesystems.)0
+965 y Fl(not)23 b(allowed)g('/')h(in)f(a)h(directory)f(name)240
+1029 y Fo(When)14 b(reading)g(the)g(con\014guration)g(input,)h(if)f(there)g
+(is)g(a)f(\014lesystem)i(de\014nition)g(whic)o(h)g(con)o(tains)240
+1079 y(a)f(pathname)g(with)g(m)o(ultiple)i(directories)f(for)e(an)o(y)h(part)
+f(of)g(the)i(moun)o(tp)q(oin)o(t)f(elemen)o(t,)g(and)g(it)g(is)240
+1128 y(not)h(a)g(single)h(absolute)g(path,)e(then)i(this)g(message)e(will)j
+(b)q(e)f(pro)q(duced)g(b)o(y)g(the)f(parser.)0 1192 y Fl(unknown)23
+b(directory)g(attribute)240 1255 y Fo(If)d(an)g(unkno)o(wn)h(k)o(eyw)o(ord)e
+(is)h(found)h(while)g(reading)g(the)f(de\014nition)i(of)e(a)f(hosts's)g
+(\014lesystem)240 1305 y(moun)o(t)c(option.)0 1368 y Fl(unknown)23
+b(mount)g(attribute)240 1432 y Fo(Occurs)16 b(if)g(an)f(unrecognised)h(k)o
+(eyw)o(ord)f(is)g(found)h(while)h(parsing)e(the)g(list)h(of)f(static)g(moun)o
+(ts.)0 1495 y Fl(")24 b(expected)240 1558 y Fo(Occurs)16 b(if)g(an)f(unescap)
+q(ed)h(newline)i(is)d(found)h(in)g(a)f(quoted)g(string.)0 1622
+y Fl(unknown)23 b(\\)h(sequence)240 1685 y Fo(Occurs)12 b(if)h(an)e(unkno)o
+(wn)h(escap)q(e)h(sequence)g(is)f(found)g(inside)i(a)d(string.)19
+b(Within)13 b(a)e(string,)h(y)o(ou)g(can)240 1735 y(giv)o(e)j(the)g(standard)
+g(C)g(escap)q(e)h(sequences)g(for)e(strings,)h(suc)o(h)h(as)e(newlines)j(and)
+e(tab)g(c)o(haracters.)0 1798 y Fp(\014lename)s Fl(:)24 b(cannot)f(open)h
+(for)f(reading)240 1862 y Fo(If)18 b(a)f(\014le)h(sp)q(eci\014ed)i(on)d(the)h
+(command)f(line)i(as)e(con)o(taining)h(con\014guration)g(data)f(could)h(not)f
+(b)q(e)240 1912 y(op)q(ened.)0 1975 y Fl(end)23 b(of)h(file)f(within)g
+(comment)240 2038 y Fo(A)15 b(commen)o(t)g(w)o(as)f(un)o(terminated)i(b)q
+(efore)g(the)f(end)h(of)e(one)i(of)f(the)g(con\014guration)g(\014les.)0
+2102 y Fl(host)23 b(field)g(")p Fp(\014eld-name)s Fl(")i(already)e(set)240
+2165 y Fo(If)15 b(duplicate)i(de\014nitions)g(are)e(giv)o(en)h(for)f(an)o(y)f
+(of)h(the)g(\014elds)i(with)e(a)g(host)g(de\014nition.)0 2229
+y Fl(duplicate)23 b(host)g Fp(hostname)s Fl(!)240 2292 y Fo(If)15
+b(a)g(host)g(has)g(more)g(than)g(one)g(de\014nition.)0 2355
+y Fl(netif)23 b(field)g Fp(\014eld-name)28 b Fl(already)23
+b(set)240 2419 y Fo(Occurs)16 b(if)g(y)o(ou)e(attempt)h(to)f(de\014ne)j(an)e
+(attribute)g(of)f(an)i(in)o(terface)f(more)g(than)g(once.)0
+2482 y Fl(malformed)23 b(IP)g(dotted)g(quad:)g Fp(address)240
+2545 y Fo(If)e(the)g(In)o(ternet)h(address)f(of)f(an)h(in)o(terface)g(is)h
+(incorrectly)g(sp)q(eci\014ed.)39 b(An)22 b(In)o(ternet)f(address)240
+2595 y(de\014nition)e(is)e(handled)i(to)d Fk(inet)p 801 2595
+14 3 v 17 w(addr)p Fo(\(3N\))h(to)f(see)i(if)f(it)g(can)g(cop)q(e.)26
+b(If)17 b(not,)g(then)g(this)g(message)240 2645 y(will)g(b)q(e)f(displa)o(y)o
+(ed.)p eop
+%%Page: 44 46
+44 45 bop 15 -83 a Fo(SMM:13-44)889 b(4.4)15 b(BSD)g(Automoun)o(ter)f
+(Reference)j(Man)o(ual)0 158 y Fl(malformed)23 b(netmask:)f
+Fp(netmask)240 223 y Fo(If)16 b(the)g(netmask)f(cannot)g(b)q(e)i(deco)q(ded)g
+(as)e(though)h(it)g(w)o(ere)f(a)g(hexadecimal)j(n)o(um)o(b)q(er,)d(then)i
+(this)240 273 y(message)i(will)h(b)q(e)g(displa)o(y)o(ed.)32
+b(It)19 b(will)i(t)o(ypically)f(b)q(e)g(caused)g(b)o(y)e(incorrect)i(c)o
+(haracters)e(in)i(the)240 322 y Fp(netmask)e Fo(v)m(alue.)0
+387 y Fl(fs)24 b(field)f(")p Fp(\014eld-name)s Fl(")h(already)f(set)240
+451 y Fo(Occurs)16 b(when)g(m)o(ultiple)h(de\014nitions)g(are)e(giv)o(en)h
+(for)f(one)g(of)g(the)g(attributes)h(of)e(a)h(host's)g(\014lesys-)240
+501 y(tem.)0 565 y Fl(mount)23 b(tree)g(field)h(")p Fp(\014eld-name)s
+Fl(")g(already)f(set)240 630 y Fo(Occurs)13 b(when)h(the)f
+Fp(\014eld-name)j Fo(is)e(de\014ned)g(more)e(than)h(once)g(during)h(the)f
+(de\014nition)h(of)e(a)h(\014lesys-)240 680 y(tems)i(moun)o(tp)q(oin)o(t.)0
+744 y Fl(mount)23 b(field)g(")p Fp(\014eld-name)s Fl(")i(already)e(set)240
+809 y Fo(Occurs)16 b(when)g(a)e(static)h(moun)o(t)g(has)g(m)o(ultiple)i
+(de\014nitions)g(of)e(the)g(same)g(\014eld.)0 873 y Fl(no)24
+b(disk)f(mounts)g(on)h Fp(hostname)240 937 y Fo(If)11 b(there)g(are)g(no)g
+(static)g(moun)o(ts,)g(nor)f(lo)q(cal)i(disk)g(moun)o(ts)f(sp)q(eci\014ed)i
+(for)d(a)h(mac)o(hine,)h(this)f(message)240 987 y(will)17 b(b)q(e)f(displa)o
+(y)o(ed.)0 1052 y Fp(host)q Fl(:)p Fp(device)27 b Fl(needs)d(field)f(")p
+Fp(\014eld-name)s Fl(")240 1116 y Fo(Occurs)13 b(when)f(a)g(\014lesystem)g
+(is)h(missing)f(a)g(required)h(\014eld.)20 b Fp(\014eld-name)c
+Fo(could)d(b)q(e)g(one)f(of)f Fl(fstype)p Fo(,)240 1166 y Fl(opts)p
+Fo(,)j Fl(passno)h Fo(or)f Fl(mount)p Fo(.)0 1230 y Fp(\014lesystem)25
+b Fl(has)e(a)h(volname)f(but)g(no)h(exportfs)e(data)240 1295
+y Fo(Occurs)13 b(when)f(a)g(v)o(olume)h(name)f(is)g(declared)i(for)d(a)h
+(\014le)h(system,)f(but)g(the)g(string)g(sp)q(ecifying)j(what)240
+1345 y(mac)o(hines)h(the)f(\014lesystem)h(can)f(b)q(e)h(exp)q(orted)g(to)e
+(is)i(missing.)0 1409 y Fl(sub-directory)22 b Fp(directory)28
+b Fl(of)c Fp(directory-tree)i Fl(starts)d(with)h('/')240 1473
+y Fo(Within)13 b(the)f(\014lesystem)h(sp)q(eci\014cation)h(for)d(a)h(host,)f
+(if)i(an)f(elemen)o(t)g Fp(directory)k Fo(of)c(the)g(moun)o(tp)q(oin)o(t)240
+1523 y(b)q(egins)k(with)g(a)f(`)p Fl(/)p Fo(')f(and)h(it)h(is)g(not)e(the)i
+(start)e(of)g(the)i(tree.)0 1588 y Fp(host)q Fl(:)p Fp(device)27
+b Fl(has)d(no)f(mount)h(point)240 1652 y Fo(Occurs)16 b(if)g(the)f(`)p
+Fl(mount)p Fo(')f(option)h(is)h(not)e(sp)q(eci\014ed)k(for)c(a)h(host's)g
+(\014lesystem.)0 1717 y Fp(host)q Fl(:)p Fp(device)27 b Fl(has)d(more)f(than)
+g(one)h(mount)f(point)240 1781 y Fo(Occurs)17 b(if)g(the)g(moun)o(t)f(option)
+h(for)f(a)g(host's)g(\014lesystem)h(sp)q(eci\014es)h(m)o(ultiple)h(trees)d
+(at)g(whic)o(h)i(to)240 1831 y(place)e(the)f(moun)o(tp)q(oin)o(t.)0
+1895 y Fl(no)24 b(volname)e(given)i(for)f Fp(host)q Fl(:)p
+Fp(device)240 1960 y Fo(Occurs)15 b(when)h(a)e(\014lesystem)h(is)g(de\014ned)
+h(to)e(b)q(e)i(moun)o(ted)e(on)h(`)p Fl(default)p Fo(',)e(but)h(no)h(v)o
+(olume)g(name)240 2009 y(is)h(giv)o(en)f(for)g(the)g(\014le)h(system,)f(then)
+g(the)h(moun)o(tp)q(oin)o(t)f(cannot)g(b)q(e)h(determined.)0
+2074 y Fp(host)q Fl(:mount)23 b(field)g(specified)f(for)i(swap)f(partition)
+240 2138 y Fo(Occurs)16 b(if)g(a)e(moun)o(tp)q(oin)o(t)i(is)f(giv)o(en)h(for)
+f(a)f(\014lesystem)i(whose)f(t)o(yp)q(e)h(is)f(declared)i(to)d(b)q(e)i
+Fl(swap)p Fo(.)0 2203 y Fl(ambiguous)23 b(mount:)g Fp(v)o(olume)k
+Fl(is)c(a)h(replicated)e(filesystem)240 2267 y Fo(If)17 b(sev)o(eral)f
+(\014lesystems)h(are)f(declared)h(as)f(ha)o(ving)g(the)h(same)f(v)o(olume)g
+(name,)h(they)f(will)i(b)q(e)f(con-)240 2317 y(sidered)j(replicated)h
+(\014lesystems.)33 b(T)l(o)19 b(moun)o(t)g(a)g(replicated)h(\014lesystem)g
+(statically)l(,)h(a)e(sp)q(eci\014c)240 2367 y(host)e(will)h(need)g(to)e(b)q
+(e)i(named,)f(to)g(sa)o(y)f(whic)o(h)i(particular)f(cop)o(y)g(to)g(try)f(and)
+h(moun)o(t,)g(else)h(this)240 2417 y(error)c(will)j(result.)0
+2481 y Fl(cannot)23 b(determine)g(localname)f(since)h(volname)g
+Fp(v)o(olume)k Fl(is)d(not)f(uniquely)g(defined)240 2545 y
+Fo(If)c(a)g(v)o(olume)h(is)g(replicated)g(and)g(an)f(attempt)f(is)i(made)f
+(to)f(moun)o(t)h(the)g(\014lesystem)h(statically)240 2595 y(without)d(sp)q
+(ecifying)j(a)d(lo)q(cal)h(moun)o(tp)q(oin)o(t,)g Fp(FSinfo)i
+Fo(cannot)d(calculate)h(a)f(moun)o(tp)q(oin)o(t,)h(as)f(the)240
+2645 y(desired)f(pathname)g(w)o(ould)f(b)q(e)h(am)o(biguous.)p
+eop
+%%Page: 45 47
+45 46 bop 0 -83 a Fo(Chapter)15 b(8:)k(Examples)1300 b(SMM:13-45)0
+158 y Fl(volname)23 b Fp(v)o(olume)k Fl(is)c(unknown)240 223
+y Fo(Occurs)14 b(if)g(an)f(attempt)f(is)i(made)f(to)f(moun)o(t)h(or)g
+(reference)g(a)g(v)o(olume)h(name)f(whic)o(h)h(has)f(not)g(b)q(een)240
+273 y(declared)j(during)g(the)g(host)f(\014lesystem)g(de\014nitions.)0
+338 y Fl(volname)23 b Fp(v)o(olume)k Fl(not)c(exported)g(from)g
+Fp(mac)o(hine)240 402 y Fo(Occurs)d(if)h(y)o(ou)e(attempt)g(to)g(moun)o(t)g
+(the)h(v)o(olume)g Fp(v)o(olume)j Fo(from)c(a)g(mac)o(hine)i(whic)o(h)g(has)e
+(not)240 452 y(declared)d(itself)h(to)d(ha)o(v)o(e)h(suc)o(h)g(a)g
+(\014lesystem)h(a)o(v)m(ailable.)0 517 y Fl(network)23 b(booting)g(requires)g
+(both)g(root)g(and)h(swap)f(areas)240 581 y Fo(Occurs)18 b(if)g(a)f(mac)o
+(hine)h(has)f(moun)o(t)g(declarations)h(for)e(either)i(the)f(ro)q(ot)g
+(partition)g(or)g(the)h(sw)o(ap)240 631 y(area,)12 b(but)f(not)h(b)q(oth.)19
+b(Y)l(ou)12 b(cannot)f(de\014ne)i(a)f(mac)o(hine)g(to)g(only)g(partially)h(b)
+q(o)q(ot)e(via)i(the)e(net)o(w)o(ork.)0 696 y Fl(unknown)23
+b(volname)g Fp(v)o(olume)k Fl(automounted)22 b Fp([)29 b Fl(on)24
+b(<name>)f Fp(])240 761 y Fo(Occurs)c(if)f Fp(v)o(olume)j Fo(is)e(used)g(in)g
+(a)e(de\014nition)j(of)e(an)g(automoun)o(t)f(map)h(but)g(the)g(v)o(olume)h
+(name)240 810 y(has)c(not)g(b)q(een)h(declared)h(during)f(the)f(host)g
+(\014lesystem)h(de\014nitions.)0 875 y Fl(not)23 b(allowed)g('/')h(in)f(a)h
+(directory)f(name)240 940 y Fo(Occurs)15 b(when)g(a)f(pathname)g(with)h(m)o
+(ultiple)h(directory)f(elemen)o(ts)g(is)g(sp)q(eci\014ed)i(as)d(the)g(name)h
+(for)240 990 y(an)g(automoun)o(ter)f(tree.)20 b(A)15 b(tree)g(should)h(only)g
+(ha)o(v)o(e)f(one)g(name)g(at)g(eac)o(h)g(lev)o(el.)0 1054
+y Fp(device)28 b Fl(has)23 b(duplicate)g(exportfs)g(data)240
+1119 y Fo(Pro)q(duced)16 b(if)h(the)e(`)p Fl(exportfs)p Fo(')f(option)i(is)g
+(used)g(m)o(ultiple)i(times)d(within)i(the)f(same)f(branc)o(h)h(of)f(a)240
+1169 y(\014lesytem)e(de\014nition.)21 b(F)l(or)12 b(example,)h(if)g(y)o(ou)f
+(attempt)f(to)h(set)g(the)h(`)p Fl(exportfs)p Fo(')d(data)i(at)g(di\013eren)o
+(t)240 1219 y(lev)o(els)k(of)f(the)h(moun)o(tp)q(oin)o(t)f(directory)g(tree.)
+0 1283 y Fl(sub-directory)22 b(of)i Fp(directory-tree)i Fl(is)e(named)f
+("default")240 1348 y Fo(`)p Fl(default)p Fo(')17 b(is)h(a)g(k)o(eyw)o(ord)g
+(used)h(to)f(sp)q(ecify)h(if)g(a)f(moun)o(tp)q(oin)o(t)g(should)i(b)q(e)f
+(automatically)f(cal-)240 1398 y(culated)g(b)o(y)f Fp(FSinfo)p
+Fo(.)25 b(If)17 b(y)o(ou)g(attempt)f(to)g(sp)q(ecify)i(a)f(directory)g(name)g
+(as)f(this,)i(it)f(will)i(use)e(the)240 1448 y(\014lename)f(of)f(`)p
+Fl(default)p Fo(')f(but)h(will)i(pro)q(duce)f(this)f(w)o(arning.)0
+1512 y Fl(pass)23 b(number)g(for)h Fp(host)q Fl(:)p Fp(device)j
+Fl(is)d(non-zero)240 1577 y Fo(Occurs)14 b(if)h Fp(device)i
+Fo(has)d(its)g(`)p Fl(fstype)p Fo(')e(declared)j(to)e(b)q(e)h(`)p
+Fl(swap)p Fo(')f(or)g(`)p Fl(export)p Fo(')f(and)i(the)g Fk(fsc)o(k)p
+Fo(\(8\))e(pass)240 1627 y(n)o(um)o(b)q(er)i(is)h(set.)k(Sw)o(ap)14
+b(devices)h(should)g(not)e(b)q(e)i(fsc)o(k'd.)k(See)14 b(Section)h(7.6.1)d
+([FSinfo)i(\014lesystems)240 1677 y(fst)o(yp)q(e],)g(page)31
+b(SMM:13-36)0 1741 y Fl(dump)23 b(frequency)g(for)g Fp(host)q
+Fl(:)p Fp(device)28 b Fl(is)23 b(non-zero)240 1806 y Fo(Occurs)12
+b(if)h Fp(device)i Fo(has)d(its)g(`)p Fl(fstype)p Fo(')e(declared)j(to)e(b)q
+(e)h(`)p Fl(swap)p Fo(')e(or)i(`)p Fl(export)p Fo(')e(and)i(the)f(`)p
+Fl(dump)p Fo(')g(option)240 1856 y(is)16 b(set)f(to)f(a)h(v)m(alue)i(greater)
+d(than)h(zero.)20 b(Sw)o(ap)15 b(devices)h(should)g(not)f(b)q(e)h(dump)q(ed.)
+0 2065 y Fm(8)41 b(Examples)0 2313 y Fq(8.1)33 b(User)14 b(Filesystems)62
+2404 y Fo(With)g(more)g(than)g(one)g(\014leserv)o(er,)g(the)g(directories)h
+(most)e(frequen)o(tly)i(cross-moun)o(ted)e(are)h(those)g(con)o(tain-)0
+2454 y(ing)19 b(user)g(home)f(directories.)31 b(A)19 b(common)f(con)o(v)o(en)
+o(tion)h(used)g(at)f(Imp)q(erial)i(College)f(is)g(to)f(moun)o(t)g(the)h(user)
+0 2504 y(disks)d(under)g Fl(/home/)p Fp(mac)o(hine)p Fo(.)62
+2575 y(T)o(ypically)l(,)h(the)e(`)p Fl(/etc/fstab)p Fo(')e(\014le)j(con)o
+(tained)g(a)f(long)h(list)g(of)e(en)o(tries)i(suc)o(h)f(as:)120
+2645 y Fp(mac)o(hine)s Fl(:/home/)p Fp(mac)o(hine)27 b Fl(/home/)p
+Fp(mac)o(hine)f Fl(nfs)e(...)p eop
+%%Page: 46 48
+46 47 bop 15 -83 a Fo(SMM:13-46)889 b(4.4)15 b(BSD)g(Automoun)o(ter)f
+(Reference)j(Man)o(ual)62 158 y(for)e(eac)o(h)g(\014leserv)o(er)h(on)f(the)g
+(net)o(w)o(ork.)62 229 y(There)20 b(are)e(n)o(umerous)h(problems)h(with)f
+(this)h(system.)31 b(The)19 b(moun)o(t)f(list)i(can)f(b)q(ecome)h(quite)g
+(large)f(and)0 279 y(some)d(of)h(the)f(mac)o(hines)i(ma)o(y)e(b)q(e)h(do)o
+(wn)g(when)g(a)f(system)h(is)g(b)q(o)q(oted.)24 b(When)18 b(a)e(new)h
+(\014leserv)o(er)g(is)g(installed,)0 329 y(`)p Fl(/etc/fstab)p
+Fo(')d(m)o(ust)h(b)q(e)i(up)q(dated)g(on)f(ev)o(ery)g(mac)o(hine,)g(the)g
+(moun)o(t)g(directory)g(created)g(and)g(the)g(\014lesystem)0
+378 y(moun)o(ted.)62 449 y(In)f(man)o(y)f(en)o(vironmen)o(ts)h(most)e(p)q
+(eople)j(use)f(the)f(same)g(few)g(w)o(orkstations,)f(but)i(it)f(is)h(con)o(v)
+o(enien)o(t)g(to)f(go)g(to)0 499 y(a)i(colleague's)g(mac)o(hine)h(and)f
+(access)g(y)o(our)f(o)o(wn)h(\014les.)23 b(When)16 b(a)f(serv)o(er)h(go)q(es)
+f(do)o(wn,)h(it)g(can)g(cause)g(a)g(pro)q(cess)0 549 y(on)e(a)g(clien)o(t)h
+(mac)o(hine)g(to)e(hang.)20 b(By)14 b(minimising)i(the)e(moun)o(ted)g
+(\014lesystems)h(to)e(only)i(include)h(those)e(activ)o(ely)0
+598 y(b)q(eing)i(used,)g(there)f(is)h(less)g(c)o(hance)f(that)g(a)g
+(\014lesystem)h(will)h(b)q(e)e(moun)o(ted)h(when)f(a)g(serv)o(er)g(go)q(es)g
+(do)o(wn.)62 669 y(The)f(follo)o(wing)g(is)g(a)f(short)g(extract)f(from)h(a)g
+(map)g(tak)o(en)g(from)g(a)g(researc)o(h)g(\014leserv)o(er)i(at)d(Imp)q
+(erial)j(College.)62 739 y(Note)g(the)h(en)o(try)f(for)f(`)p
+Fl(localhost)p Fo(')g(whic)o(h)i(is)f(used)h(for)f(users)g(suc)o(h)h(as)f
+(the)g(op)q(erator)g(\(`)p Fl(opr)p Fo('\))e(who)i(ha)o(v)o(e)g(a)0
+789 y(home)g(directory)h(on)f(most)f(mac)o(hine)i(as)f(`)p
+Fl(/home/localhost/opr)p Fo('.)120 860 y Fl(/defaults)166 b
+(opts:=rw,intr,grpid,nosui)o(d)120 910 y(charm)262 b
+(host!=${key};type:=nfs;rh)o(ost:=${)o(key};rf)o(s:=/home)o(/${key})20
+b(\\)502 959 y(host==${key};type:=ufs;de)o(v:=/dev)o(/xd0g)120
+1009 y(#)120 1059 y(...)120 1159 y(#)120 1209 y(localhost)166
+b(type:=link;fs:=${host})120 1258 y(...)120 1308 y(#)120 1358
+y(#)24 b(dylan)f(has)g(two)h(user)f(disks)g(so)h(have)f(a)120
+1408 y(#)h(top)f(directory)g(in)g(which)h(to)f(mount)g(them.)120
+1458 y(#)120 1507 y(dylan)262 b(type:=auto;fs:=${map};pre)o(f:=${ke)o(y}/)120
+1557 y(#)120 1607 y(dylan/dk2)166 b(host!=dylan;type:=nfs;rho)o(st:=dyl)o
+(an;rfs:)o(=/home/$)o({key})21 b(\\)502 1657 y(host==dylan;type:=ufs;dev)o
+(:=/dev/)o(dsk/2s0)120 1707 y(#)120 1757 y(dylan/dk5)166 b
+(host!=dylan;type:=nfs;rho)o(st:=dyl)o(an;rfs:)o(=/home/$)o({key})21
+b(\\)502 1806 y(host==dylan;type:=ufs;dev)o(:=/dev/)o(dsk/5s0)120
+1856 y(...)120 1906 y(#)120 1956 y(toytown)214 b(host!=${key};type:=nfs;rh)o
+(ost:=${)o(key};rf)o(s:=/home)o(/${key})20 b(\\)502 2006 y
+(host==${key};type:=ufs;de)o(v:=/dev)o(/xy1g)120 2055 y(...)120
+2105 y(#)120 2155 y(zebedee)214 b(host!=${key};type:=nfs;rh)o(ost:=${)o
+(key};rf)o(s:=/home)o(/${key})20 b(\\)502 2205 y(host==${key};type:=ufs;de)o
+(v:=/dev)o(/dsk/1s)o(0)120 2255 y(#)120 2304 y(#)k(Just)f(for)g(access...)120
+2354 y(#)120 2404 y(gould)262 b(type:=auto;fs:=${map};pre)o(f:=${ke)o(y}/)120
+2454 y(gould/staff)118 b(host!=gould;type:=nfs;rho)o(st:=gou)o(ld;rfs:)o
+(=/home/$)o({key})120 2504 y(#)120 2554 y(gummo)262 b
+(host!=${key};type:=nfs;rh)o(ost:=${)o(key};rf)o(s:=/home)o(/${key})120
+2603 y(...)p eop
+%%Page: 47 49
+47 48 bop 0 -83 a Fo(Chapter)15 b(8:)k(Examples)1300 b(SMM:13-47)62
+158 y(This)17 b(map)e(is)h(shared)g(b)o(y)g(most)e(of)i(the)f(mac)o(hines)i
+(listed)g(so)e(on)g(those)h(systems)f(an)o(y)g(of)h(the)f(user)h(disks)g(is)0
+208 y(accessible)h(via)f(a)e(consisten)o(t)i(name.)k Fp(Amd)d
+Fo(is)f(started)e(with)i(the)f(follo)o(wing)h(command)120 279
+y Fl(amd)23 b(/home)h(amd.home)62 370 y Fo(Note)16 b(that)g(when)g(moun)o
+(ting)h(a)e(remote)h(\014lesystem,)h(the)f Fp(automoun)o(ted)h
+Fo(moun)o(t)f(p)q(oin)o(t)h(is)f(referenced,)h(so)0 420 y(that)c(the)h
+(\014lesystem)h(will)g(b)q(e)g(moun)o(ted)f(if)g(it)g(is)g(not)g(y)o(et)f
+(\(at)g(the)h(time)g(the)g(remote)g(`)p Fl(mountd)p Fo(')e(obtains)i(the)g
+(\014le)0 470 y(handle\).)0 628 y Fq(8.2)33 b(Home)14 b(Directories)62
+719 y Fo(One)e(con)o(v)o(en)o(tion)e(for)g(home)h(directories)h(is)f(to)f(lo)
+q(cate)h(them)f(in)i(`)p Fl(/homes)p Fo(')d(so)h(user)h(`)p
+Fl(jsp)p Fo(''s)e(home)i(directory)f(is)0 769 y(`)p Fl(/homes/jsp)p
+Fo('.)17 b(With)e(more)f(than)h(a)f(single)i(\014leserv)o(er)f(it)f(is)h(con)
+o(v)o(enien)o(t)h(to)d(spread)i(user)g(\014les)g(across)f(sev)o(eral)0
+819 y(mac)o(hines.)34 b(All)21 b(that)f(is)g(required)h(is)f(a)f(moun)o
+(t-map)h(whic)o(h)g(con)o(v)o(erts)f(login)i(names)f(to)f(an)h(automoun)o
+(ted)0 868 y(directory)l(.)62 939 y(Suc)o(h)c(a)f(map)g(migh)o(t)g(b)q(e)h
+(started)e(b)o(y)h(the)h(command:)120 1010 y Fl(amd)23 b(/homes)g(amd.homes)
+62 1101 y Fo(where)16 b(the)f(map)g(`)p Fl(amd.homes)p Fo(')e(con)o(tained)j
+(the)f(en)o(tries:)120 1171 y Fl(/defaults)70 b(type:=link)h(#)23
+b(All)h(the)f(entries)g(are)h(of)f(type:=link)120 1221 y(jsp)214
+b(fs:=/home/charm/jsp)120 1271 y(njw)g(fs:=/home/dylan/dk5/njw)120
+1321 y(...)120 1371 y(phjk)190 b(fs:=/home/toytown/ai/phjk)120
+1421 y(sjv)214 b(fs:=/home/ganymede/sjv)62 1512 y Fo(Whenev)o(er)21
+b(a)e(login)i(name)f(is)g(accessed)h(in)g(`)p Fl(/homes)p Fo(')d(a)i(sym)o(b)
+q(olic)h(link)g(app)q(ears)f(p)q(oin)o(ting)h(to)f(the)g(real)0
+1562 y(lo)q(cation)12 b(of)f(that)f(user's)h(home)h(directory)l(.)19
+b(In)12 b(this)f(example,)i(`)p Fl(/homes/jsp)p Fo(')c(w)o(ould)j(app)q(ear)f
+(to)g(b)q(e)h(a)f(sym)o(b)q(olic)0 1611 y(link)17 b(p)q(oin)o(ting)f(to)e(`)p
+Fl(/home/charm/jsp)p Fo('.)k(Of)d(course,)g(`)p Fl(/home)p
+Fo(')f(w)o(ould)h(also)g(b)q(e)h(an)f(automoun)o(t)f(p)q(oin)o(t.)62
+1682 y(This)j(system)f(causes)h(an)f(extra)g(lev)o(el)h(of)f(sym)o(b)q(olic)i
+(links)f(to)f(b)q(e)h(used.)24 b(Although)17 b(that)e(turns)i(out)f(to)f(b)q
+(e)0 1732 y(relativ)o(ely)i(inexp)q(ensiv)o(e,)i(an)d(alternativ)o(e)g(is)h
+(to)f(directly)h(moun)o(t)f(the)g(required)h(\014lesystems)g(in)g(the)g(`)p
+Fl(/homes)p Fo(')0 1782 y(map.)i(The)14 b(required)g(map)f(is)h(simple,)h
+(but)e(long,)h(and)f(its)h(creation)f(is)h(b)q(est)g(automated.)k(The)c(en)o
+(try)e(for)h(`)p Fl(jsp)p Fo(')0 1831 y(could)j(b)q(e:)120
+1902 y Fl(jsp)71 b(-sublink:=${key};rfs:=/home)o(/charm)21
+b(\\)478 1952 y(host==charm;type:=ufs;dev:)o(=/dev/x)o(d0g)g(\\)478
+2002 y(host!=charm;type:=nfs;rhos)o(t:=char)o(m)62 2093 y Fo(This)14
+b(map)g(can)f(b)q(ecome)h(quite)h(big)f(if)f(it)h(con)o(tains)g(a)f(large)g
+(n)o(um)o(b)q(er)h(of)f(en)o(tries.)20 b(By)14 b(com)o(bining)g(t)o(w)o(o)e
+(other)0 2143 y(features)j(of)g Fp(Amd)i Fo(it)e(can)h(b)q(e)f(greatly)g
+(simpli\014ed.)62 2213 y(First)e(the)g(UFS)g(partitions)g(should)h(b)q(e)g
+(moun)o(ted)f(under)h(the)f(con)o(trol)f(of)h(`)p Fl(/etc/fstab)p
+Fo(',)e(taking)i(care)g(that)0 2263 y(they)j(are)f(moun)o(ted)g(in)i(the)e
+(same)h(place)g(that)f Fp(Amd)i Fo(w)o(ould)f(ha)o(v)o(e)f(automoun)o(ted)g
+(them.)21 b(In)16 b(most)f(cases)h(this)0 2313 y(w)o(ould)e(b)q(e)h
+(something)f(lik)o(e)h(`)p Fl(/a/)p Fp(host)q Fl(/home/)p Fp(host)q
+Fo(')c(and)j(`)p Fl(/etc/fstab)p Fo(')e(on)i(host)f(`)p Fl(charm)p
+Fo(')f(w)o(ould)j(ha)o(v)o(e)e(a)h(line:)120 2384 y Fl(/dev/xy0g)23
+b(/a/charm/home/charm)e(4.2)i(rw,nosuid,grpid)f(1)i(5)62 2475
+y Fo(The)16 b(map)f(can)g(then)h(b)q(e)f(c)o(hanged)h(to:)120
+2545 y Fl(/defaults)94 b(type:=nfs;sublink:=${key};op)o(ts:=rw,)o(intr,no)o
+(suid,grp)o(id)120 2595 y(jsp)238 b(rhost:=charm;rfs:=/home/char)o(m)120
+2645 y(njw)g(rhost:=dylan;rfs:=/home/dyla)o(n/dk5)p eop
+%%Page: 48 50
+48 49 bop 15 -83 a Fo(SMM:13-48)889 b(4.4)15 b(BSD)g(Automoun)o(ter)f
+(Reference)j(Man)o(ual)120 158 y Fl(...)120 208 y(phjk)214
+b(rhost:=toytown;rfs:=/home/to)o(ytown;s)o(ublink:)o(=ai/${ke)o(y})120
+258 y(sjv)238 b(rhost:=ganymede;rfs:=/home/g)o(anymede)62 349
+y Fo(This)17 b(map)e(op)q(erates)h(as)f(usual)h(on)g(a)f(remote)h(mac)o(hine)
+g(\()p Fp(ie)j Fl(${host})c Fo(not)g(equal)h(to)g Fl(${rhost})p
+Fo(\).)k(On)c(the)0 399 y(mac)o(hine)f(where)f(the)f(\014lesystem)i(is)f
+(stored)f(\()p Fp(ie)k Fl(${host})c Fo(equal)h(to)g Fl(${rhost})p
+Fo(\),)e Fp(Amd)k Fo(will)f(construct)e(a)h(lo)q(cal)0 449
+y(\014lesystem)k(moun)o(t)g(p)q(oin)o(t)g(whic)o(h)h(corresp)q(onds)f(to)f
+(the)h(name)g(of)f(the)h(lo)q(cally)h(moun)o(ted)f(UFS)g(partition.)28
+b(If)0 499 y Fp(Amd)18 b Fo(is)f(started)f(with)h(the)f(\\-r")g(option)g
+(then)h(instead)g(of)f(attempting)g(an)g(NFS)h(moun)o(t,)e
+Fp(Amd)k Fo(will)f(simply)0 549 y(inherit)d(the)f(UFS)g(moun)o(t)f(\(see)h
+(Section)h(5.14)d([Inheritance)j(Filesystem],)f(page)28 b(SMM:13-26\).)17
+b(If)d(\\-r")f(is)h(not)0 598 y(used)i(then)g(a)f(lo)q(opbac)o(k)g(NFS)h
+(moun)o(t)e(will)j(b)q(e)f(made.)21 b(This)16 b(t)o(yp)q(e)f(of)g(moun)o(t)g
+(is)h(kno)o(wn)f(to)f(cause)i(a)f(deadlo)q(c)o(k)0 648 y(on)g(man)o(y)g
+(systems.)0 810 y Fq(8.3)33 b(Arc)n(hitecture)16 b(Sharing)62
+902 y Fo(Often)h(a)f(\014lesystem)h(will)h(b)q(e)f(shared)f(b)o(y)h(mac)o
+(hines)g(of)f(di\013eren)o(t)g(arc)o(hitectures.)24 b(Separate)16
+b(trees)g(can)g(b)q(e)0 951 y(main)o(tained)f(for)f(the)g(executable)h
+(images)f(for)g(eac)o(h)g(arc)o(hitecture,)g(but)h(it)f(ma)o(y)g(b)q(e)g
+(more)g(con)o(v)o(enien)o(t)h(to)e(ha)o(v)o(e)0 1001 y(a)i(shared)g(tree,)g
+(with)g(distinct)i(sub)q(directories.)62 1072 y(A)23 b(shared)f(tree)h(migh)o
+(t)f(ha)o(v)o(e)g(the)h(follo)o(wing)g(structure)f(on)g(the)h(\014leserv)o
+(er)g(\(called)h(`)p Fl(fserver)p Fo(')d(in)i(the)0 1122 y(example\):)120
+1192 y Fl(local/tex)120 1242 y(local/tex/fonts)120 1292 y(local/tex/lib)120
+1342 y(local/tex/bin)120 1391 y(local/tex/bin/sun3)120 1441
+y(local/tex/bin/sun4)120 1491 y(local/tex/bin/hp9000)120 1541
+y(...)62 1632 y Fo(In)15 b(this)g(example,)g(the)g(sub)q(directories)h(of)e
+(`)p Fl(local/tex/bin)p Fo(')e(should)j(b)q(e)g(hidden)h(when)f(accessed)g
+(via)g(the)0 1682 y(automoun)o(t)f(p)q(oin)o(t)i(\(con)o(v)o(en)o(tionally)g
+(`)p Fl(/vol)p Fo('\).)i(A)d(moun)o(t-map)g(for)f(`)p Fl(/vol)p
+Fo(')g(to)h(ac)o(hiev)o(e)h(this)f(w)o(ould)h(lo)q(ok)f(lik)o(e:)120
+1752 y Fl(/defaults)70 b(sublink:=${/key};rhost:=fserv)o(er;type)o(:=link)120
+1802 y(tex)214 b(type:=auto;fs:=${map};pref:=$)o({key}/)120
+1852 y(tex/fonts)70 b(host!=fserver;type:=nfs;rfs:=)o(/vol/te)o(x)21
+b(\\)406 1902 y(host==fserver;fs:=/usr/local/)o(tex)120 1952
+y(tex/lib)118 b(host!=fserver;type:=nfs;rfs:=)o(/vol/te)o(x)21
+b(\\)406 2002 y(host==fserver;fs:=/usr/local/)o(tex)120 2051
+y(tex/bin)118 b(-sublink:=${/key}/${arch})21 b(host!=fserver;type:=nfs;r)o
+(fs:=/vo)o(l/tex)120 2101 y(\\)406 2151 y(host:=fserver;fs:=/usr/local/)o
+(tex)62 2242 y Fo(When)12 b(`)p Fl(/vol/tex/bin)p Fo(')d(is)k(referenced,)g
+(the)e(curren)o(t)h(mac)o(hine)g(arc)o(hitecture)g(is)g(automatically)g(app)q
+(ended)0 2292 y(to)j(the)h(path)g(b)o(y)f(the)h Fl(${sublink})e
+Fo(v)m(ariable.)23 b(This)17 b(means)e(that)g(users)h(can)g(ha)o(v)o(e)g(`)p
+Fl(/vol/tex/bin)p Fo(')d(in)j(their)0 2342 y(`)p Fl(PATH)p
+Fo(')e(without)h(concern)h(for)e(arc)o(hitecture)i(dep)q(endencies.)0
+2504 y Fq(8.4)33 b(Wildcard)17 b(names)e(&)g(Replicated)i(Serv)n(ers)62
+2595 y Fo(By)h(using)g(the)g(wildcard)h(facilit)o(y)l(,)g Fp(Amd)h
+Fo(can)e Fp(o)o(v)o(erla)o(y)j Fo(an)c(existing)i(directory)f(with)g
+(additional)h(en)o(tries.)0 2645 y(The)12 b(system)f(\014les)i(are)e(usually)
+j(moun)o(ted)d(under)i(`)p Fl(/usr)p Fo('.)k(If)12 b(instead)g
+Fp(Amd)i Fo(is)e(moun)o(ted)g(on)g(`)p Fl(/usr)p Fo(',)f(additional)p
+eop
+%%Page: 49 51
+49 50 bop 0 -83 a Fo(Chapter)15 b(8:)k(Examples)1300 b(SMM:13-49)0
+158 y(names)18 b(can)g(b)q(e)h(o)o(v)o(erla)o(y)o(ed)e(to)h(augmen)o(t)f(or)h
+(replace)h(names)f(in)g(the)h(\\master")d(`)p Fl(/usr)p Fo('.)27
+b(A)18 b(map)g(to)g(do)g(this)0 208 y(w)o(ould)e(ha)o(v)o(e)e(the)i(form:)120
+279 y Fl(local)47 b(type:=auto;fs:=local-map)120 329 y(share)g
+(type:=auto;fs:=share-map)120 378 y(*)143 b(-type:=nfs;rfs:=/export/ex)o
+(ec/${arc)o(h};subl)o(ink:="$)o({key}")21 b(\\)311 428 y(rhost:=fserv1)46
+b(rhost:=fserv2)g(rhost:=fserv3)62 519 y Fo(Note)11 b(that)g(the)g(assignmen)
+o(t)g(to)g Fl(${sublink})f Fo(is)i(surrounded)g(b)o(y)f(double)i(quotes)e(to)
+f(prev)o(en)o(t)h(the)h(incoming)0 569 y(k)o(ey)j(from)f(causing)i(the)f(map)
+f(to)h(b)q(e)g(misin)o(terpreted.)21 b(This)16 b(map)f(has)f(the)h(e\013ect)g
+(of)g(directing)h(an)o(y)f(access)g(to)0 619 y(`)p Fl(/usr/local)p
+Fo(')e(or)i(`)p Fl(/usr/share)p Fo(')e(to)h(another)h(automoun)o(t)f(p)q(oin)
+o(t.)62 690 y(In)i(this)g(example,)g(it)g(is)f(assumed)h(that)f(the)g(`)p
+Fl(/usr)p Fo(')f(\014les)i(are)f(replicated)i(on)f(three)f(\014leserv)o(ers:)
+21 b(`)p Fl(fserv1)p Fo(',)0 739 y(`)p Fl(fserv2)p Fo(')15
+b(and)h(`)p Fl(fserv3)p Fo('.)21 b(F)l(or)16 b(an)o(y)g(references)h(other)f
+(than)g(to)f(`)p Fl(local)p Fo(')g(and)h(`)p Fl(share)p Fo(')f(one)i(of)e
+(the)i(serv)o(ers)e(is)0 789 y(used)j(and)g(a)f(sym)o(b)q(olic)i(link)g(to)e
+Fl(${autodir}/${rhost}/expo)o(rt/exec/)o(${arch})o(/)p Fp(whatev)o(er)g
+Fo(is)i(returned)0 839 y(once)d(an)f(appropriate)g(\014lesystem)h(has)f(b)q
+(een)h(moun)o(ted.)0 1011 y Fq(8.5)33 b(`)p Fg(rwho)p Fq(')13
+b(serv)n(ers)62 1102 y Fo(The)j(`)p Fl(/usr/spool/rwho)p Fo(')c(directory)j
+(is)h(a)f(go)q(o)q(d)g(candidate)h(for)f(automoun)o(ting.)k(F)l(or)c
+(e\016ciency)h(reasons)0 1152 y(it)d(is)f(b)q(est)h(to)f(capture)g(the)h
+(rwho)e(data)h(on)g(a)g(small)i(n)o(um)o(b)q(er)e(of)g(mac)o(hines)h(and)g
+(then)f(moun)o(t)g(that)g(information)0 1202 y(on)o(to)g(a)h(large)g(n)o(um)o
+(b)q(er)h(of)f(clien)o(ts.)20 b(The)14 b(data)e(written)i(in)o(to)f(the)g
+(rwho)g(\014les)h(is)g(b)o(yte)f(order)g(dep)q(enden)o(t)i(so)d(only)0
+1252 y(serv)o(ers)j(with)g(the)h(correct)e(b)o(yte)h(ordering)h(can)f(b)q(e)h
+(used)g(b)o(y)f(a)g(clien)o(t:)120 1322 y Fl(/defaults)214
+b(type:=nfs)120 1372 y(usr/spool/rwho)94 b(-byte==little;rfs:=/usr)o(/spool/)
+o(rwho)21 b(\\)645 1422 y(rhost:=vaxA)46 b(rhost:=vaxB)23 b(\\)550
+1472 y(||)g(-rfs:=/usr/spool/rwho)e(\\)645 1521 y(rhost:=sun4)46
+b(rhost:=hp300)0 1694 y Fq(8.6)33 b(`)p Fg(/vol)p Fq(')62 1786
+y Fo(`)p Fl(/vol)p Fo(')14 b(is)i(used)g(as)e(a)h(catc)o(h-all)h(for)f(v)o
+(olumes)g(whic)o(h)i(do)e(not)f(ha)o(v)o(e)h(other)g(con)o(v)o(en)o(tional)h
+(names.)62 1856 y(Belo)o(w)21 b(is)f(part)g(of)f(the)h(`)p
+Fl(/vol)p Fo(')f(map)h(for)g(the)g(domain)g(`)p Fl(doc.ic.ac.uk)p
+Fo('.)32 b(The)21 b(`)p Fl(r+d)p Fo(')d(tree)i(is)h(used)g(for)0
+1906 y(new)15 b(or)e(exp)q(erimen)o(tal)j(soft)o(w)o(are)d(that)g(needs)i(to)
+f(b)q(e)h(a)o(v)m(ailable)h(ev)o(erywhere)e(without)h(installing)h(it)e(on)h
+(all)g(the)0 1956 y(\014leserv)o(ers.)24 b(Users)16 b(wishing)i(to)d(try)h
+(out)g(the)g(new)h(soft)o(w)o(are)d(then)j(simply)h(include)g(`)p
+Fl(/vol/r+d/{bin,ucb})p Fo(')0 2006 y(in)e(their)g(path.)62
+2076 y(The)e(main)g(tree)f(resides)h(on)g(one)f(host)g(`)p
+Fl(gould.doc.ic.ac.uk)p Fo(',)d(whic)o(h)15 b(has)e(di\013eren)o(t)h(`)p
+Fl(bin)p Fo(',)e(`)p Fl(etc)p Fo(',)g(`)p Fl(lib)p Fo(')0 2126
+y(and)k(`)p Fl(ucb)p Fo(')e(sub-directories)k(for)d(eac)o(h)g(mac)o(hine)i
+(arc)o(hitecture.)k(F)l(or)15 b(example,)i(`)p Fl(/vol/r+d/bin)p
+Fo(')c(for)i(a)g(Sun-4)0 2176 y(w)o(ould)e(b)q(e)g(stored)e(in)j(the)e
+(sub-directory)h(`)p Fl(bin/sun4)p Fo(')d(of)i(the)h(\014lesystem)g(`)p
+Fl(/usr/r+d)p Fo('.)k(When)12 b(it)h(w)o(as)e(accessed)0 2226
+y(a)k(sym)o(b)q(olic)h(link)h(p)q(oin)o(ting)f(to)f(`)p Fl
+(/a/gould/usr/r+d/bin/s)o(un4)p Fo(')d(w)o(ould)j(b)q(e)h(returned.)120
+2296 y Fl(/defaults)94 b(type:=nfs;opts:=rw,grpid,nos)o(uid,int)o(r,soft)120
+2346 y(wp)262 b(-opts:=rw,grpid,nosuid;rhost)o(:=charm)20 b(\\)430
+2396 y(host==charm;type:=link;fs:=/)o(usr/loc)o(al/wp)h(\\)430
+2446 y(host!=charm;type:=nfs;rfs:=/)o(vol/wp)120 2496 y(...)120
+2545 y(#)120 2595 y(src)238 b(-opts:=rw,grpid,nosuid;rhost)o(:=charm)20
+b(\\)430 2645 y(host==charm;type:=link;fs:=/)o(usr/src)g(\\)p
+eop
+%%Page: 50 52
+50 51 bop 15 -83 a Fo(SMM:13-50)889 b(4.4)15 b(BSD)g(Automoun)o(ter)f
+(Reference)j(Man)o(ual)430 158 y Fl(host!=charm;type:=nfs;rfs:=/)o(vol/src)
+120 208 y(#)120 258 y(r+d)238 b(type:=auto;fs:=${map};pref:=)o(r+d/)120
+308 y(#)24 b(per)f(architecture)f(bin,etc,lib&ucb...)120 358
+y(r+d/bin)142 b(rhost:=gould.doc.ic.ac.uk;rf)o(s:=/usr)o(/r+d;su)o(blink:=$)o
+({/key}/)o(${arch})120 407 y(r+d/etc)g(rhost:=gould.doc.ic.ac.uk;rf)o
+(s:=/usr)o(/r+d;su)o(blink:=$)o({/key}/)o(${arch})120 457 y(r+d/include)46
+b(rhost:=gould.doc.ic.ac.uk;rf)o(s:=/usr)o(/r+d;su)o(blink:=$)o({/key})120
+507 y(r+d/lib)142 b(rhost:=gould.doc.ic.ac.uk;rf)o(s:=/usr)o(/r+d;su)o
+(blink:=$)o({/key}/)o(${arch})120 557 y(r+d/man)g
+(rhost:=gould.doc.ic.ac.uk;rf)o(s:=/usr)o(/r+d;su)o(blink:=$)o({/key})120
+607 y(r+d/src)g(rhost:=gould.doc.ic.ac.uk;rf)o(s:=/usr)o(/r+d;su)o(blink:=$)o
+({/key})120 656 y(r+d/ucb)g(rhost:=gould.doc.ic.ac.uk;rf)o(s:=/usr)o(/r+d;su)
+o(blink:=$)o({/key}/)o(${arch})120 706 y(#)24 b(hades)f(pictures)120
+756 y(pictures)118 b(-opts:=rw,grpid,nosuid;rhost)o(:=thpfs)20
+b(\\)430 806 y(host==thpfs;type:=link;fs:=/)o(nbsd/pi)o(ctures)g(\\)430
+856 y(host!=thpfs;type:=nfs;rfs:=/)o(nbsd;su)o(blink:=)o(pictures)120
+906 y(#)k(hades)f(tools)120 955 y(hades)190 b(-opts:=rw,grpid,nosuid;rhost)o
+(:=thpfs)20 b(\\)430 1005 y(host==thpfs;type:=link;fs:=/)o(nbsd/ha)o(des)h
+(\\)430 1055 y(host!=thpfs;type:=nfs;rfs:=/)o(nbsd;su)o(blink:=)o(hades)120
+1105 y(#)j(bsd)f(tools)g(for)h(hp.)120 1155 y(bsd)238 b
+(-opts:=rw,grpid,nosuid;arch=)o(=hp9000)o(;rhost:)o(=thpfs)21
+b(\\)430 1204 y(host==thpfs;type:=link;fs:=/)o(nbsd/bs)o(d)g(\\)430
+1254 y(host!=thpfs;type:=nfs;rfs:=/)o(nbsd;su)o(blink:=)o(bsd)0
+1468 y Fm(9)41 b(In)n(ternals)0 1709 y Fq(9.1)33 b(Log)14 b(Messages)62
+1800 y Fo(In)h(the)f(follo)o(wing)h(sections)g(a)f(brief)g(explanation)i(is)e
+(giv)o(en)h(of)e(some)h(of)g(the)g(log)g(messages)g(made)g(b)o(y)g
+Fp(Amd)p Fo(.)0 1850 y(Where)20 b(the)f(message)g(is)h(in)h(`)p
+Fl(typewriter)p Fo(')c(fon)o(t,)i(it)h(corresp)q(onds)g(exactly)g(to)f(the)g
+(message)g(pro)q(duced)i(b)o(y)0 1900 y Fp(Amd)p Fo(.)i(W)l(ords)16
+b(in)i Fp(italic)i Fo(are)c(replaced)i(b)o(y)e(an)g(appropriate)g(string.)24
+b(V)l(ariables,)17 b Fl(${var})p Fo(,)e(indicate)j(that)e(the)0
+1950 y(v)m(alue)g(of)f(the)g(appropriate)h(v)m(ariable)g(is)g(output.)62
+2020 y(Log)h(messages)f(are)g(either)h(sen)o(t)g(direct)g(to)f(a)g(\014le,)i
+(or)e(logged)h(via)g(the)f Fk(syslog)p Fo(\(3\))g(mec)o(hanism.)25
+b(Messages)0 2070 y(are)15 b(logged)g(with)g(facilit)o(y)h(`)p
+Fl(LOG_DAEMON)p Fo(')d(when)i(using)h Fk(syslog)p Fo(\(3\).)j(In)c(either)h
+(case,)f(en)o(tries)g(in)h(the)f(\014le)h(are)e(of)0 2120 y(the)h(form:)120
+2191 y Fp(date-string)52 b(hostname)26 b Fl(amd[)p Fp(pid)r
+Fl(])48 b Fp(message)0 2355 y Fi(9.1.1)30 b(F)-5 b(atal)14
+b(errors)62 2446 y Fp(Amd)20 b Fo(attempts)c(to)h(deal)h(with)g(un)o(usual)g
+(ev)o(en)o(ts.)27 b(Whenev)o(er)17 b(it)h(is)g(not)f(p)q(ossible)i(to)e(deal)
+h(with)g(suc)o(h)g(an)0 2496 y(error,)d Fp(Amd)k Fo(will)f(log)e(an)g
+(appropriate)h(message)e(and,)i(if)f(it)h(cannot)f(p)q(ossibly)i(con)o(tin)o
+(ue,)f(will)h(either)f(exit)f(or)0 2545 y(ab)q(ort.)24 b(These)17
+b(messages)f(are)g(selected)i(b)o(y)e(`)p Fl(-x)f(fatal)p Fo(')g(on)i(the)g
+(command)f(line.)26 b(When)17 b Fk(syslog)p Fo(\(3\))f(is)h(b)q(eing)0
+2595 y(used,)22 b(they)e(are)g(logged)h(with)g(lev)o(el)g(`)p
+Fl(LOG_FATAL)p Fo('.)34 b(Ev)o(en)20 b(if)h Fp(Amd)h Fo(con)o(tin)o(ues)f(to)
+f(op)q(erate)g(it)h(is)f(lik)o(ely)j(to)0 2645 y(remain)16
+b(in)g(a)f(precarious)g(state)g(and)g(should)h(b)q(e)g(restarted)e(at)h(the)g
+(earliest)h(opp)q(ortunit)o(y)l(.)p eop
+%%Page: 51 53
+51 52 bop 0 -83 a Fo(Chapter)15 b(9:)k(In)o(ternals)1317 b(SMM:13-51)0
+158 y Fl(Attempting)22 b(to)i(inherit)f(not-a-filesystem)240
+219 y Fo(The)14 b(protot)o(yp)q(e)g(moun)o(t)g(p)q(oin)o(t)g(created)h
+(during)g(a)f(\014lesystem)h(restart)e(did)i(not)f(con)o(tain)g(a)g(refer-)
+240 269 y(ence)i(to)f(the)g(restarted)f(\014lesystem.)21 b(This)16
+b(erorr)e(\\should)i(nev)o(er)f(happ)q(en".)0 330 y Fl(Can't)23
+b(bind)g(to)h(domain)f(")p Fp(NIS-domain)p Fl(")240 391 y Fo(A)e(sp)q
+(eci\014c)i(NIS)e(domain)h(w)o(as)e(requested)h(on)g(the)g(command)f(line,)k
+(but)d(no)g(serv)o(er)f(for)h(that)240 441 y(domain)16 b(is)f(a)o(v)m
+(ailable)i(on)e(the)h(lo)q(cal)g(net.)0 502 y Fl(Can't)23 b(determine)g(IP)g
+(address)g(of)h(this)f(host)h(\()p Fp(hostname)s Fl(\))240
+563 y Fo(When)11 b Fp(Amd)h Fo(starts)d(it)i(determines)g(its)g(o)o(wn)e(IP)i
+(address.)18 b(If)11 b(this)g(lo)q(okup)g(fails)g(then)g Fp(Amd)h
+Fo(cannot)240 612 y(con)o(tin)o(ue.)19 b(The)11 b(hostname)e(it)i(lo)q(oks)g
+(up)g(is)f(that)g(obtained)h(returned)g(b)o(y)f Fk(gethostname)p
+Fo(\(2\))f(system)240 662 y(call.)0 723 y Fl(Can't)23 b(find)g(root)h(file)f
+(handle)g(for)h Fp(automoun)o(t)14 b(p)q(oin)o(t)240 784 y(Amd)19
+b Fo(creates)d(its)h(o)o(wn)g(\014le)g(handles)h(for)f(the)f(automoun)o(t)g
+(p)q(oin)o(ts.)25 b(When)17 b(it)g(moun)o(ts)g(itself)g(as)240
+834 y(a)i(serv)o(er,)i(it)f(m)o(ust)f(pass)h(these)g(\014le)g(handles)h(to)f
+(the)g(lo)q(cal)g(k)o(ernel.)35 b(If)20 b(the)g(\014lehandle)i(is)e(not)240
+884 y(obtainable)c(the)g(moun)o(t)e(p)q(oin)o(t)i(is)g(ignored.)k(This)c
+(error)e(\\should)i(nev)o(er)g(happ)q(en".)0 945 y Fl(Must)23
+b(be)h(root)f(to)h(mount)f(filesystems)f(\(euid)i(=)f Fp(euid)r
+Fl(\))240 1006 y Fo(T)l(o)13 b(prev)o(en)o(t)f(em)o(barrassmen)o(t,)g
+Fp(Amd)j Fo(mak)o(es)d(sure)h(it)g(has)g(appropriate)g(system)f(privileges.)
+21 b(This)240 1055 y(amoun)o(ts)12 b(to)g(ha)o(ving)h(an)f(euid)i(of)f(0.)18
+b(The)13 b(c)o(hec)o(k)g(is)g(made)g(after)f(argumen)o(t)g(pro)q(cessing)h
+(complete)240 1105 y(to)i(giv)o(e)g(non-ro)q(ot)g(users)g(a)g(c)o(hance)h(to)
+e(access)h(the)h(\\-v")f(option.)0 1166 y Fl(No)24 b(work)f(to)g(do)h(-)g
+(quitting)240 1227 y Fo(No)15 b(automoun)o(t)f(p)q(oin)o(ts)i(w)o(ere)f(giv)o
+(en)g(on)g(the)h(command)f(line)i(and)e(so)g(there)g(is)h(no)f(w)o(ork)f(to)h
+(do.)0 1288 y Fl(Out)23 b(of)h(memory)f(in)h(realloc)240 1349
+y Fo(While)c(attempting)e(to)g(reallo)q(c)h(some)f(memory)l(,)h(the)f(memory)
+g(space)h(a)o(v)m(ailable)h(to)d Fp(Amd)k Fo(w)o(as)240 1399
+y(exhausted.)f(This)c(is)g(an)f(unreco)o(v)o(erable)h(error.)0
+1460 y Fl(Out)23 b(of)h(memory)240 1521 y Fo(While)c(attempting)e(to)g(mallo)
+q(c)h(some)f(memory)l(,)h(the)f(memory)g(space)h(a)o(v)m(ailable)h(to)d
+Fp(Amd)k Fo(w)o(as)240 1570 y(exhausted.)f(This)c(is)g(an)f(unreco)o(v)o
+(erable)h(error.)0 1631 y Fl(cannot)23 b(create)g(rpc/udp)g(service)240
+1692 y Fo(Either)16 b(the)f(NFS)g(or)g(AMQ)g(endp)q(oin)o(t)h(could)h(not)d
+(b)q(e)i(created.)0 1753 y Fl(gethostname:)e Fp(description)240
+1814 y Fo(The)h Fk(gethostname)p Fo(\(2\))f(system)h(call)i(failed)f(during)g
+(startup.)0 1875 y Fl(host)23 b(name)h(is)f(not)h(set)240 1936
+y Fo(The)14 b Fk(gethostname)p Fo(\(2\))g(system)f(call)j(returned)e(a)g
+(zero)h(length)f(host)g(name.)20 b(This)15 b(can)f(happ)q(en)h(if)240
+1986 y Fp(Amd)i Fo(is)f(started)e(in)i(single)h(user)e(mo)q(de)h(just)f
+(after)f(b)q(o)q(oting)i(the)f(system.)0 2047 y Fl(ifs_match)23
+b(called!)240 2108 y Fo(An)i(in)o(ternal)g(error)f(o)q(ccurred)h(while)i
+(restarting)d(a)g(pre-moun)o(ted)h(\014lesystem.)48 b(This)26
+b(error)240 2158 y(\\should)16 b(nev)o(er)f(happ)q(en".)0 2219
+y Fl(mount_afs:)f Fp(description)240 2279 y Fo(An)h(error)g(o)q(ccured)h
+(while)h Fp(Amd)g Fo(w)o(as)d(moun)o(ting)i(itself.)0 2340
+y Fl(run_rpc)23 b(failed)240 2401 y Fo(Someho)o(w)15 b(the)g(main)h(NFS)f
+(serv)o(er)g(lo)q(op)g(failed.)22 b(This)15 b(error)g(\\should)h(nev)o(er)f
+(happ)q(en".)0 2462 y Fl(unable)23 b(to)h(free)f(rpc)g(arguments)g(in)h
+(amqprog_1)240 2523 y Fo(The)15 b(incoming)i(argumen)o(ts)d(to)h(the)g(AMQ)g
+(serv)o(er)g(could)h(not)f(b)q(e)h(free'ed.)0 2584 y Fl(unable)23
+b(to)h(free)f(rpc)g(arguments)g(in)h(nfs_program_1)240 2645
+y Fo(The)15 b(incoming)i(argumen)o(ts)d(to)h(the)g(NFS)g(serv)o(er)g(could)h
+(not)f(b)q(e)h(free'ed.)p eop
+%%Page: 52 54
+52 53 bop 15 -83 a Fo(SMM:13-52)889 b(4.4)15 b(BSD)g(Automoun)o(ter)f
+(Reference)j(Man)o(ual)0 158 y Fl(unable)23 b(to)h(register)e(\(AMQ_PROGRAM,)
+g(AMQ_VERSION,)h(udp\))240 221 y Fo(The)d(AMQ)g(serv)o(er)g(could)g(not)g(b)q
+(e)g(registered)h(with)f(the)g(lo)q(cal)h(p)q(ortmapp)q(er)f(or)f(the)h(in)o
+(ternal)240 271 y(RPC)15 b(dispatc)o(her.)0 334 y Fl(unable)23
+b(to)h(register)e(\(NFS_PROGRAM,)g(NFS_VERSION,)h(0\))240 397
+y Fo(The)15 b(NFS)h(serv)o(er)e(could)j(not)d(b)q(e)i(registered)g(with)f
+(the)h(in)o(ternal)g(RPC)f(dispatc)o(her.)0 543 y Fi(9.1.2)30
+b(Info)14 b(messages)62 634 y Fp(Amd)f Fo(generates)e(information)g(messages)
+g(to)f(record)i(state)e(c)o(hanges.)18 b(These)12 b(messages)e(are)h
+(selected)i(b)o(y)e(`)p Fl(-x)0 684 y(info)p Fo(')j(on)h(the)h(command)f
+(line.)21 b(When)16 b Fk(syslog)p Fo(\(3\))e(is)i(b)q(eing)g(used,)g(they)f
+(are)g(logged)g(with)h(lev)o(el)g(`)p Fl(LOG_INFO)p Fo('.)62
+754 y(The)h(messages)f(listed)i(b)q(elo)o(w)g(can)f(b)q(e)g(generated)g(and)g
+(are)f(in)i(a)e(format)f(suitable)j(for)e(simple)j(statistical)0
+804 y(analysis.)24 b Fp(moun)o(t-info)18 b Fo(is)f(the)g(string)f(that)f(is)i
+(displa)o(y)o(ed)h(b)o(y)e Fp(Amq)h Fo(in)g(its)g(moun)o(t)e(information)i
+(column)g(and)0 854 y(placed)f(in)g(the)g(system)e(moun)o(t)h(table.)0
+924 y Fl(mount)23 b(of)h("${)p Fp(path)p Fl(}")f(on)g(${)p
+Fp(fs)r Fl(})h(timed)f(out)240 987 y Fo(A)o(ttempts)13 b(to)h(moun)o(t)f(a)h
+(\014lesystem)h(for)f(the)g(giv)o(en)h(automoun)o(t)e(p)q(oin)o(t)h(ha)o(v)o
+(e)g(failed)i(to)d(complete)240 1037 y(within)j(30)f(seconds.)0
+1100 y Fl("${)p Fp(path)p Fl(}")23 b(forcibly)g(timed)g(out)240
+1163 y Fo(An)15 b(automoun)o(t)g(p)q(oin)o(t)g(has)g(b)q(een)i(timed)e(out)g
+(b)o(y)g(the)h Fp(Amq)g Fo(command.)0 1226 y Fl(restarting)22
+b Fp(moun)o(t-info)27 b Fl(on)c(${)p Fp(fs)r Fl(})240 1288
+y Fo(A)15 b(pre-moun)o(ted)h(\014le)g(system)f(has)g(b)q(een)h(noted.)0
+1351 y Fl("${)p Fp(path)p Fl(}")23 b(has)h(timed)f(out)240
+1414 y Fo(No)15 b(access)g(to)g(the)g(automoun)o(t)f(p)q(oin)o(t)i(has)f(b)q
+(een)h(made)f(within)i(the)e(timeout)g(p)q(erio)q(d.)0 1477
+y Fl(file)23 b(server)g(${)p Fp(rhost)q Fl(})g(is)h(down)f(-)h(timeout)f(of)g
+("${)p Fp(path)p Fl(}")g(ignored)240 1540 y Fo(An)18 b(automoun)o(t)e(p)q
+(oin)o(t)i(has)f(timed)h(out,)f(but)h(the)g(corresp)q(onding)g(\014le)g(serv)
+o(er)f(is)h(kno)o(wn)g(to)e(b)q(e)240 1590 y(do)o(wn.)23 b(This)17
+b(message)f(is)h(only)g(pro)q(duced)g(once)g(for)f(eac)o(h)g(moun)o(t)g(p)q
+(oin)o(t)h(for)f(whic)o(h)h(the)f(serv)o(er)240 1639 y(is)g(do)o(wn.)0
+1702 y Fl(Re-synchronizing)22 b(cache)h(for)g(map)h(${)p Fp(map)q
+Fl(})240 1765 y Fo(The)15 b(named)h(map)f(has)g(b)q(een)h(mo)q(di\014ed)h
+(and)e(the)h(in)o(ternal)g(cac)o(he)f(is)h(b)q(eing)g(re-sync)o(hronized.)0
+1828 y Fl(Filehandle)22 b(denied)i(for)f("${)p Fp(rhost)q Fl(}:${)p
+Fp(rfs)r Fl(}")240 1891 y Fo(The)15 b(moun)o(t)g(daemon)g(refused)h(to)f
+(return)g(a)g(\014le)h(handle)g(for)f(the)g(requested)h(\014lesystem.)0
+1954 y Fl(Filehandle)22 b(error)i(for)f("${)p Fp(rhost)q Fl(}:${)p
+Fp(rfs)r Fl(}":)13 b Fp(description)240 2017 y Fo(The)i(moun)o(t)g(daemon)g
+(ga)o(v)o(e)g(some)f(other)h(error)g(for)f(the)i(requested)f(\014lesystem.)0
+2079 y Fl(file)23 b(server)g(${)p Fp(rhost)q Fl(})g(type)h(nfs)f(starts)g(up)
+240 2142 y Fo(A)15 b(new)h(NFS)f(\014le)h(serv)o(er)f(has)g(b)q(een)h
+(referenced)g(and)g(is)g(kno)o(wn)e(to)h(b)q(e)h(up.)0 2205
+y Fl(file)23 b(server)g(${)p Fp(rhost)q Fl(})g(type)h(nfs)f(starts)g(down)240
+2268 y Fo(A)15 b(new)h(NFS)f(\014le)h(serv)o(er)f(has)g(b)q(een)h(referenced)
+g(and)g(is)g(kno)o(wn)e(to)h(b)q(e)h(do)o(wn.)0 2331 y Fl(file)23
+b(server)g(${)p Fp(rhost)q Fl(})g(type)h(nfs)f(is)h(up)240
+2394 y Fo(An)15 b(NFS)h(\014le)g(serv)o(er)f(that)f(w)o(as)h(previously)h(do)
+o(wn)f(is)h(no)o(w)e(up.)0 2457 y Fl(file)23 b(server)g(${)p
+Fp(rhost)q Fl(})g(type)h(nfs)f(is)h(down)240 2519 y Fo(An)15
+b(NFS)h(\014le)g(serv)o(er)f(that)f(w)o(as)h(previously)h(up)g(is)f(no)o(w)g
+(do)o(wn.)0 2582 y Fl(Finishing)23 b(with)g(status)g Fp(exit-status)240
+2645 y(Amd)17 b Fo(is)f(ab)q(out)f(to)f(exit)i(with)g(the)f(giv)o(en)h(exit)f
+(status.)p eop
+%%Page: 53 55
+53 54 bop 0 -83 a Fo(Index)1613 b(SMM:13-53)0 158 y Fp(moun)o(t-info)26
+b Fl(mounted)d(fstype)g(${)p Fp(t)o(yp)q(e)s Fl(})g(on)h(${)p
+Fp(fs)r Fl(})240 221 y Fo(A)15 b(new)h(\014le)g(system)f(has)g(b)q(een)h
+(moun)o(ted.)0 283 y Fp(moun)o(t-info)26 b Fl(restarted)d(fstype)g(${)p
+Fp(t)o(yp)q(e)s Fl(})g(on)h(${)p Fp(fs)r Fl(})240 345 y Fp(Amd)17
+b Fo(is)f(using)g(a)f(pre-moun)o(ted)g(\014lesystem)h(to)f(satisfy)g(a)f
+(moun)o(t)h(request.)0 407 y Fp(moun)o(t-info)26 b Fl(unmounted)d(fstype)g
+(${)p Fp(t)o(yp)q(e)s Fl(})g(from)g(${)p Fp(fs)r Fl(})240 470
+y Fo(A)15 b(\014le)h(system)f(has)g(b)q(een)i(unmoun)o(ted.)0
+532 y Fp(moun)o(t-info)26 b Fl(unmounted)d(fstype)g(${)p Fp(t)o(yp)q(e)s
+Fl(})g(from)g(${)p Fp(fs)r Fl(})h(link)f(${)p Fp(fs)r Fl(}/${)p
+Fp(sublink)s Fl(})240 594 y Fo(A)15 b(\014le)h(system)f(of)g(whic)o(h)h(only)
+g(a)f(sub-directory)h(w)o(as)e(in)i(use)g(has)f(b)q(een)h(unmoun)o(ted.)0
+784 y Fm(Ac)n(kno)n(wledgemen)n(ts)e(&)h(T)-7 b(rademarks)62
+904 y Fo(Thanks)19 b(to)g(the)g(F)l(ormal)f(Metho)q(ds)h(Group)g(at)f(Imp)q
+(erial)j(College)f(for)e(su\013ering)i(patien)o(tly)f(while)i
+Fp(Amd)0 954 y Fo(w)o(as)14 b(b)q(eing)j(dev)o(elop)q(ed)g(on)e(their)h(mac)o
+(hines.)62 1024 y(Thanks)j(to)f(the)h(man)o(y)f(p)q(eople)i(who)e(ha)o(v)o(e)
+h(help)q(ed)h(with)f(the)g(dev)o(elopmen)o(t)h(of)e Fp(Amd)p
+Fo(,)h(esp)q(ecially)i(Piete)0 1074 y(Bro)q(oks)c(at)g(the)h(Cam)o(bridge)g
+(Univ)o(ersit)o(y)g(Computing)g(Lab)g(for)f(man)o(y)g(hours)h(of)f(testing,)h
+(exp)q(erimen)o(tation)0 1124 y(and)d(discussion.)37 1195 y
+Fn(\017)30 b Fk(DEC)p Fo(,)14 b Fk(V)-5 b(AX)16 b Fo(and)g
+Fk(Ultrix)g Fo(are)f(registered)h(trademarks)e(of)h(Digital)h(Equipmen)o(t)g
+(Corp)q(oration.)37 1257 y Fn(\017)30 b Fk(AIX)16 b Fo(and)f
+Fk(IBM)h Fo(are)f(registered)g(trademarks)g(of)f(In)o(ternational)i(Business)
+h(Mac)o(hines)e(Corp)q(oration.)37 1319 y Fn(\017)30 b Fk(Sun)p
+Fo(,)16 b Fk(NFS)e Fo(and)h Fk(SunOS)i Fo(are)e(registered)g(trademarks)f(of)
+h(Sun)h(Microsystems,)e(Inc.)37 1381 y Fn(\017)30 b Fk(Unix)20
+b Fo(is)f(a)g(registered)g(trademark)f(of)h(A)l(T&T)g(Unix)h(Systems)f(Lab)q
+(oratories)g(in)h(the)f(USA)g(and)g(other)90 1431 y(coun)o(tries.)0
+1606 y Fm(Index)0 1826 y Fc(/etc/amd.start)t Fb(:)7 b(:)f(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)17 b Fc(33)0 1871
+y(/etc/passwd)d(maps)9 b Fb(:)e(:)f(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)22 b Fc(13)0 1917 y(/etc/rc.lo)q(cal)15 b(additions)e
+Fb(:)6 b(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)23 b Fc(33)0 1963
+y(/v)o(ol)11 b Fb(:)6 b(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)22 b
+Fc(56)0 2008 y(Additions)15 b(to)e(/etc/rc.lo)q(cal)7 b Fb(:)h(:)f(:)f(:)g(:)
+g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)20 b Fc(33)0 2054 y(Aliased)15 b(hostnames)8 b Fb(:)f(:)f(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)20 b Fc(22)0 2100 y(Alternate)14
+b(lo)q(cations)t Fb(:)8 b(:)e(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)17 b Fc(6)0 2145 y(Amd)c(command)h(line)g(options)8 b Fb(:)g(:)e(:)g(:)g
+(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)21 b Fc(21)0 2191 y(Amq)13 b(command)d Fb(:)d(:)f(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)22 b Fc(33)0 2236 y(arc)o(h,)13
+b(FSinfo)h(host)g(attribute)6 b Fb(:)h(:)f(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)
+g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)19
+b Fc(41)0 2282 y(arc)o(h,)13 b(moun)o(t)h(selector)c Fb(:)c(:)g(:)g(:)g(:)g
+(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f
+(:)g(:)g(:)g(:)g(:)g(:)g(:)22 b Fc(16)0 2328 y(Arc)o(hitecture)14
+b(dep)q(enden)o(t)h(v)o(olumes)e Fb(:)6 b(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)24 b Fc(55)0 2373 y(Arc)o(hitecture)14
+b(sharing)8 b Fb(:)g(:)e(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)21
+b Fc(55)0 2419 y(Arc)o(hitecture)14 b(sp)q(eci\014c)h(moun)o(ts)9
+b Fb(:)e(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)22 b Fc(56)0 2465 y(A)o(tomic)13 b(NFS)g(moun)o(ts)8
+b Fb(:)f(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)20 b Fc(27)0
+2510 y(auto,)13 b(\014lesystem)i(t)o(yp)q(e)6 b Fb(:)g(:)g(:)g(:)g(:)h(:)f(:)
+g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)19 b Fc(29)0 2556 y(auto)q(dir,)14 b(moun)o(t)g(selector)c
+Fb(:)c(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)
+g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)22 b Fc(16)0 2602 y(Automatic)14
+b(generation)h(of)d(user)i(maps)t Fb(:)6 b(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)
+g(:)g(:)g(:)g(:)g(:)g(:)g(:)17 b Fc(13)1015 1826 y(Automoun)o(t)d(directory)f
+Fb(:)6 b(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)24 b Fc(21)1015
+1871 y(Automoun)o(t)14 b(\014lesystem)t Fb(:)8 b(:)f(:)f(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)18 b Fc(29)1015 1917 y(Automoun)o(ter)c(con\014guration)i(maps)t
+Fb(:)7 b(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g
+(:)17 b Fc(11)1015 1963 y(Automoun)o(ter)d(fundamen)o(tals)f
+Fb(:)6 b(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)h(:)23 b Fc(5)1015 2008 y(Bac)o(kground)15
+b(moun)o(ts)5 b Fb(:)i(:)f(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)
+g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)18
+b Fc(6)1015 2054 y(Binding)e(names)e(to)f(\014lesystems)6 b
+Fb(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)
+g(:)g(:)g(:)g(:)g(:)19 b Fc(6)1015 2100 y(b)q(o)q(otparams,)c(FSinfo)f
+(pre\014x)s Fb(:)7 b(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)16 b Fc(47)1015
+2145 y(Bug)e(rep)q(orts)5 b Fb(:)i(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)
+g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)18 b Fc(3)1015 2191
+y(b)o(yte,)c(moun)o(t)f(selector)c Fb(:)e(:)f(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g
+(:)g(:)22 b Fc(16)1015 2236 y(Cac)o(he)14 b(in)o(terv)n(al)9
+b Fb(:)f(:)e(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)22
+b Fc(21)1015 2282 y(cac)o(he,)14 b(moun)o(t)f(option)e Fb(:)6
+b(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)23 b Fc(29)1015
+2328 y(Catc)o(h-all)15 b(moun)o(t)e(p)q(oin)o(t)f Fb(:)6 b(:)g(:)h(:)f(:)g(:)
+g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)24 b Fc(56)1015 2373 y(Changing)15 b(the)e(in)o(terv)n(al)i(b)
+q(efore)e(a)g(\014lesystem)i(times)f(out)1098 2419 y Fb(:)6
+b(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)18 b Fc(21)1015 2465 y(Cluster)c(names)9
+b Fb(:)e(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)22
+b Fc(24)1015 2510 y(cluster,)14 b(FSinfo)g(host)g(attribute)t
+Fb(:)7 b(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)17 b Fc(41)1015 2556 y(cluster,)d(moun)o(t)g(selector)6
+b Fb(:)h(:)g(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)20 b Fc(17)1015
+2602 y(Command)14 b(line)h(options,)f(Amd)6 b Fb(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)19
+b Fc(21)p eop
+%%Page: 54 56
+54 55 bop 15 -83 a Fo(SMM:13-54)889 b(4.4)15 b(BSD)g(Automoun)o(ter)f
+(Reference)j(Man)o(ual)0 158 y Fc(Command)d(line)g(options,)g(FSinfo)9
+b Fb(:)f(:)e(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)22 b Fc(47)0 204 y(con\014g,)14 b(FSinfo)g(host)f(attribute)f
+Fb(:)6 b(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)23 b Fc(40)0 250 y(Con\014guration)15 b(map)f(t)o(yp)q(es)
+6 b Fb(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)
+g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)18 b Fc(11)0 295 y(Con)o(trolling)e(Amd)t
+Fb(:)6 b(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)17
+b Fc(34)0 341 y(Creating)d(a)f(pid)h(\014le)e Fb(:)6 b(:)g(:)g(:)g(:)g(:)g(:)
+g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)h(:)f(:)g(:)g(:)23 b Fc(22)0 387 y(Debug)14 b(options)8
+b Fb(:)f(:)g(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h
+(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)20
+b Fc(24)0 432 y(De\014ning)15 b(a)e(host,)g(FSinfo)g Fb(:)6
+b(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)24 b Fc(39)0 478 y(De\014ning)15
+b(an)e(Amd)g(moun)o(t)h(map,)f(FSinfo)s Fb(:)7 b(:)f(:)g(:)h(:)f(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)16 b Fc(45)0 524 y(De\014ning)f(host)e
+(attributes,)i(FSinfo)7 b Fb(:)g(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)20 b Fc(39)0 569 y(dela)o(y)m(,)14
+b(moun)o(t)f(option)t Fb(:)8 b(:)e(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)
+g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)
+17 b Fc(18)0 615 y(Dela)o(ying)e(moun)o(ts)f(from)f(sp)q(eci\014c)h(lo)q
+(cations)8 b Fb(:)h(:)d(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)21
+b Fc(18)0 661 y(Determining)15 b(the)f(map)f(t)o(yp)q(e)8 b
+Fb(:)f(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)
+g(:)g(:)g(:)g(:)g(:)g(:)21 b Fc(11)0 706 y(dev,)13 b(moun)o(t)h(option)8
+b Fb(:)g(:)e(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)21
+b Fc(28)0 752 y(Direct)14 b(automoun)o(t)g(\014lesystem)e Fb(:)6
+b(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f
+(:)g(:)g(:)23 b Fc(30)0 798 y(direct,)14 b(\014lesystem)g(t)o(yp)q(e)t
+Fb(:)7 b(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)17 b Fc(30)0
+843 y(Disco)o(v)o(ering)f(v)o(ersion)e(information)8 b Fb(:)h(:)d(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)21
+b Fc(23)0 889 y(Disco)o(v)o(ering)16 b(what)d(is)g(going)i(on)e(at)g
+(run-time)7 b Fb(:)g(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)20
+b Fc(34)0 935 y(Disk)14 b(\014lesystems)t Fb(:)8 b(:)e(:)g(:)g(:)g(:)g(:)g(:)
+g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)17 b Fc(28)0 980 y(Displa)o(yin)q(g)f(the)
+d(pro)q(cess)h(id)t Fb(:)6 b(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)16 b
+Fc(22)0 1026 y(Domain)f(name)s Fb(:)7 b(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)16 b Fc(21)0 1072 y(Domain)f(stripping)7
+b Fb(:)i(:)d(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)20
+b Fc(15)0 1117 y(domain,)14 b(moun)o(t)g(selector)9 b Fb(:)e(:)f(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)22 b Fc(17)0 1163 y(Domainname)15 b(op)q(erators)c
+Fb(:)6 b(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)23 b Fc(15)0 1209 y(dumpset,)14
+b(FSinfo)g(\014lesystems)g(option)6 b Fb(:)i(:)e(:)g(:)g(:)g(:)g(:)h(:)f(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)19 b Fc(44)0 1254 y(dumpset,)14
+b(FSinfo)g(pre\014x)5 b Fb(:)i(:)f(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)
+g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)18
+b Fc(48)0 1300 y(Duplicated)e(v)o(olumes)7 b Fb(:)h(:)e(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h
+(:)f(:)g(:)g(:)g(:)g(:)g(:)20 b Fc(5)0 1346 y(En)o(vironmen)o(t)15
+b(v)n(ariables)9 b Fb(:)f(:)f(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)22
+b Fc(15)0 1391 y(Error)13 b(\014lesystem)t Fb(:)8 b(:)e(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)17 b Fc(31)0 1437 y(error,)c
+(\014lesystem)h(t)o(yp)q(e)e Fb(:)6 b(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)24
+b Fc(31)0 1483 y(Example)14 b(of)f(arc)o(hitecture)i(sp)q(eci\014c)f(moun)o
+(ts)6 b Fb(:)h(:)f(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)19
+b Fc(56)0 1528 y(Example)14 b(of)f(moun)o(ting)i(home)e(directories)8
+b Fb(:)h(:)d(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)21 b
+Fc(54)0 1574 y(exp)q(ort,)13 b(FSinfo)h(sp)q(ecial)i(fst)o(yp)q(e)8
+b Fb(:)e(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f
+(:)g(:)g(:)g(:)g(:)21 b Fc(43)0 1620 y(exp)q(ortfs,)13 b(FSinfo)h(moun)o(t)g
+(option)f Fb(:)6 b(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)
+g(:)g(:)g(:)g(:)g(:)24 b Fc(44)0 1665 y(exp)q(orts,)14 b(FSinfo)g(pre\014x)6
+b Fb(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)19 b Fc(48)0
+1711 y(File)14 b(map)g(syn)o(tactic)g(con)o(v)o(en)o(tions)6
+b Fb(:)j(:)d(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g
+(:)g(:)g(:)19 b Fc(11)0 1757 y(File)14 b(maps)t Fb(:)7 b(:)f(:)g(:)h(:)f(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)17
+b Fc(11)0 1802 y(Fileserv)o(er)6 b Fb(:)i(:)e(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)19 b
+Fc(5)0 1848 y(Filesystem)c(info)f(pac)o(k)n(age)7 b Fb(:)g(:)f(:)g(:)g(:)g(:)
+g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g
+(:)g(:)g(:)20 b Fc(38)0 1893 y(Filesystem)15 b(t)o(yp)q(e;)e(auto)7
+b Fb(:)g(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)20 b Fc(29)0
+1939 y(Filesystem)15 b(t)o(yp)q(e;)e(direct)5 b Fb(:)i(:)f(:)g(:)g(:)h(:)f(:)
+g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)18 b Fc(30)0 1985 y(Filesystem)d(t)o(yp)q(e;)e(error)t
+Fb(:)6 b(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)17 b Fc(31)0
+2030 y(Filesystem)e(t)o(yp)q(e;)e(host)d Fb(:)c(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g
+(:)g(:)g(:)22 b Fc(26)0 2076 y(Filesystem)15 b(t)o(yp)q(e;)e(inherit)6
+b Fb(:)i(:)e(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)19 b Fc(32)0 2122 y(Filesystem)c(t)o
+(yp)q(e;)e(linkx)s Fb(:)8 b(:)e(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)16
+b Fc(29)0 2167 y(Filesystem)f(t)o(yp)q(e;)e(link)5 b Fb(:)j(:)e(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)h(:)17 b Fc(29)0 2213 y(Filesystem)e(t)o(yp)q(e;)e
+(nfsx)e Fb(:)6 b(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)23 b
+Fc(27)0 2259 y(Filesystem)15 b(t)o(yp)q(e;)e(nfs)t Fb(:)6 b(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)16 b Fc(26)0 2304 y(Filesystem)f(t)o(yp)q(e;)e
+(program)8 b Fb(:)f(:)f(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)21 b Fc(28)0 2350
+y(Filesystem)15 b(t)o(yp)q(e;)e(ro)q(ot)d Fb(:)c(:)g(:)h(:)f(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)23 b Fc(31)0 2396 y(Filesystem)15 b(t)o(yp)q(e;)e(toplvl)5
+b Fb(:)j(:)e(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)17 b Fc(31)0 2441
+y(Filesystem)e(t)o(yp)q(e;)e(ufs)t Fb(:)6 b(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h
+(:)f(:)g(:)g(:)16 b Fc(28)0 2487 y(Filesystem)f(t)o(yp)q(e;)e(union)6
+b Fb(:)i(:)e(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)19 b Fc(31)0 2533
+y(Filesystem)c(t)o(yp)q(es)6 b Fb(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h
+(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)19 b Fc(26)0 2578 y(Filesystem)7 b Fb(:)h(:)e(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)19
+b Fc(5)0 2624 y(Flat)14 b(\014le)g(maps)t Fb(:)7 b(:)f(:)g(:)g(:)g(:)g(:)g(:)
+g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)17 b Fc(11)1015
+158 y(Flushing)f(the)d(map)h(cac)o(he)6 b Fb(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g
+(:)19 b Fc(35)1015 204 y(F)m(orcing)c(\014lesystem)f(to)f(time)g(out)5
+b Fb(:)i(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)h(:)18 b Fc(37)1015 250 y(freq,)13 b(FSinfo)h(\014lesystems)h
+(option)e Fb(:)6 b(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)
+g(:)g(:)g(:)g(:)g(:)25 b Fc(43)1015 295 y(fs,)13 b(moun)o(t)g(option)7
+b Fb(:)h(:)e(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)21
+b Fc(18)1015 341 y(FSinfo)15 b(arc)o(h)e(host)g(attribute)s
+Fb(:)8 b(:)e(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)16 b Fc(41)1015 387 y(FSinfo)f(automoun)o(t)f
+(de\014nitions)s Fb(:)9 b(:)d(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)16 b Fc(45)1015 432 y(FSinfo)f(cluster)f
+(host)f(attribute)c Fb(:)e(:)g(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)
+g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)22 b Fc(41)1015 478 y(FSinfo)15
+b(command)f(line)g(options)f Fb(:)6 b(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)24 b Fc(47)1015
+524 y(FSinfo)15 b(con\014g)f(host)f(attribute)7 b Fb(:)g(:)f(:)h(:)f(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)20
+b Fc(40)1015 569 y(FSinfo)15 b(dumpset)f(\014lesystems)g(option)f
+Fb(:)6 b(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)25
+b Fc(44)1015 615 y(FSinfo)15 b(error)e(messages)6 b Fb(:)g(:)g(:)g(:)h(:)f(:)
+g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)19 b Fc(49)1015 661 y(FSinfo)c(\014lesystems)t
+Fb(:)8 b(:)e(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)17
+b Fc(41)1015 706 y(FSinfo)e(freq)e(\014lesystems)h(option)8
+b Fb(:)g(:)e(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)22 b Fc(43)1015 752 y(FSinfo)15 b(fst)o(yp)q(e)e
+(\014lesystems)h(option)7 b Fb(:)h(:)e(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)
+g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)20 b Fc(43)1015 798 y(FSinfo)15
+b(grammar)9 b Fb(:)d(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f
+(:)22 b Fc(39)1015 843 y(FSinfo)15 b(host)e(attributes)t Fb(:)7
+b(:)f(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h
+(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)17 b Fc(39)1015 889
+y(FSinfo)e(host)e(de\014nitions)8 b Fb(:)h(:)d(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)
+f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)
+21 b Fc(39)1015 935 y(FSinfo)15 b(log)e(\014lesystems)i(option)7
+b Fb(:)h(:)e(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)21 b Fc(45)1015 980 y(FSinfo)15 b(moun)o(t)e
+(\014lesystems)i(option)t Fb(:)7 b(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)
+g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)17 b Fc(44)1015 1026 y(FSinfo)e(opts)e
+(\014lesystems)i(option)5 b Fb(:)i(:)f(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)
+g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)18 b Fc(43)1015 1072
+y(FSinfo)d(os)e(host)g(attribute)5 b Fb(:)i(:)f(:)g(:)h(:)f(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)18
+b Fc(41)1015 1117 y(FSinfo)d(o)o(v)o(erview)t Fb(:)7 b(:)f(:)g(:)g(:)g(:)g(:)
+g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)17 b Fc(38)1015 1163
+y(FSinfo)e(passno)f(\014lesystems)g(option)e Fb(:)6 b(:)g(:)g(:)h(:)f(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)24 b Fc(43)1015
+1209 y(FSinfo)15 b(static)e(moun)o(ts)5 b Fb(:)j(:)e(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)19 b Fc(45)1015 1254 y(FSinfo)6 b Fb(:)i(:)e(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g
+(:)19 b Fc(38)1015 1300 y(fstab,)13 b(FSinfo)h(pre\014x)e Fb(:)6
+b(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)23 b Fc(48)1015
+1346 y(fst)o(yp)q(e,)13 b(FSinfo)i(\014lesystems)f(option)e
+Fb(:)6 b(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)24 b Fc(43)1015 1391 y(Generic)15 b(v)o(olume)f(name)7 b
+Fb(:)g(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)
+g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)20 b Fc(56)1015
+1437 y(Global)15 b(statistics)d Fb(:)6 b(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)h(:)f(:)g(:)23 b Fc(36)1015 1483 y(Grammar,)14 b(FSinfo)7
+b Fb(:)g(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)20
+b Fc(39)1015 1528 y(Hesio)q(d)15 b(maps)5 b Fb(:)h(:)g(:)g(:)h(:)f(:)g(:)g(:)
+g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)18 b Fc(13)1015
+1574 y(Home)13 b(directories)t Fb(:)c(:)d(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)18 b Fc(54)1015 1620 y(host,)c(\014lesystem)g(t)o(yp)q(e)8
+b Fb(:)f(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)21 b Fc(26)1015
+1665 y(host,)14 b(moun)o(t)f(selector)e Fb(:)6 b(:)g(:)g(:)g(:)g(:)g(:)g(:)h
+(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g
+(:)g(:)g(:)g(:)23 b Fc(17)1015 1711 y(hostd,)14 b(moun)o(t)f(selector)8
+b Fb(:)f(:)f(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)21 b Fc(17)1015
+1757 y(Hostname)14 b(normalisation)f Fb(:)6 b(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)23
+b Fc(22)1015 1802 y(hostname,)14 b(FSinfo)g(command)g(line)h(option)t
+Fb(:)7 b(:)f(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)17 b
+Fc(48)1015 1848 y(Ho)o(w)c(k)o(eys)g(are)h(lo)q(ok)o(ed)g(up)6
+b Fb(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)19 b Fc(14)1015 1893
+y(Ho)o(w)13 b(lo)q(cations)i(are)e(parsed)s Fb(:)8 b(:)e(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)17 b Fc(14)1015 1939 y(Ho)o(w)c(to)g(access)g(en)o(vironmen)o(t)i(v)n
+(ariables)h(in)d(maps)5 b Fb(:)i(:)f(:)g(:)g(:)g(:)g(:)18 b
+Fc(15)1015 1985 y(Ho)o(w)13 b(to)g(disco)o(v)o(er)h(y)o(our)g(v)o(ersion)g
+(of)f(Amd)t Fb(:)7 b(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)18
+b Fc(23)1015 2030 y(Ho)o(w)13 b(to)g(moun)o(t)g(a)g(lo)q(cal)i(disk)9
+b Fb(:)e(:)f(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)h(:)f(:)g(:)g(:)g(:)g(:)22 b Fc(28)1015 2076 y(Ho)o(w)13
+b(to)g(moun)o(t)g(a)g(UFS)g(\014lesystems)6 b Fb(:)i(:)e(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)19 b Fc(28)1015
+2122 y(Ho)o(w)13 b(to)g(moun)o(t)g(all)i(NFS)e(exp)q(orted)h(\014lesystems)9
+b Fb(:)f(:)e(:)g(:)g(:)g(:)g(:)g(:)22 b Fc(26)1015 2167 y(Ho)o(w)12
+b(to)g(moun)o(t)h(an)f(atomic)h(group)g(of)f(NFS)g(\014lesystems)5
+b Fb(:)j(:)17 b Fc(27)1015 2213 y(Ho)o(w)c(to)g(moun)o(t)g(and)h(NFS)f
+(\014lesystem)c Fb(:)f(:)e(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)
+g(:)g(:)22 b Fc(26)1015 2259 y(Ho)o(w)13 b(to)g(reference)g(an)g(existing)j
+(part)d(of)g(the)g(lo)q(cal)h(name)1092 2304 y(space)f Fb(:)6
+b(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)25 b Fc(29)1015 2350 y(Ho)o(w)13 b(to)g(reference)g(part)g(of)g(the)g
+(lo)q(cal)i(name)e(space)8 b Fb(:)f(:)f(:)g(:)g(:)g(:)21 b
+Fc(29)1015 2396 y(Ho)o(w)13 b(to)g(select)h(log)g(messages)6
+b Fb(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h
+(:)f(:)g(:)g(:)g(:)g(:)g(:)19 b Fc(23)1015 2441 y(Ho)o(w)13
+b(to)g(set)g(default)h(map)f(parameters)f Fb(:)6 b(:)g(:)g(:)g(:)g(:)g(:)g(:)
+h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)24 b Fc(15)1015 2487 y(Ho)o(w)13
+b(to)g(set)g(map)g(cac)o(he)h(parameters)6 b Fb(:)h(:)f(:)g(:)g(:)g(:)h(:)f
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)20 b Fc(29)1015
+2533 y(Ho)o(w)13 b(to)g(start)g(a)g(direct)h(automoun)o(t)g(p)q(oin)o(t)9
+b Fb(:)e(:)f(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)22 b
+Fc(30)1015 2578 y(Ho)o(w)13 b(to)g(start)g(an)g(indirect)i(automoun)o(t)f(p)q
+(oin)o(t)8 b Fb(:)g(:)e(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)21 b
+Fc(29)1015 2624 y(Ho)o(w)13 b(v)n(ariables)i(are)e(expanded)5
+b Fb(:)j(:)e(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)h(:)f(:)g(:)g(:)18 b Fc(15)p eop
+%%Page: 55 57
+55 56 bop 0 -83 a Fo(Index)1613 b(SMM:13-55)0 158 y Fc(inherit,)14
+b(\014lesystem)h(t)o(yp)q(e)5 b Fb(:)i(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)
+g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)18
+b Fc(32)0 204 y(Inheritance)d(\014lesystem)6 b Fb(:)h(:)f(:)h(:)f(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)19 b Fc(32)0 250 y(In)o(terv)n(al)14 b(b)q(efore)f(a)g
+(\014lesystem)i(times)e(out)5 b Fb(:)i(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)
+g(:)g(:)g(:)g(:)18 b Fc(21)0 295 y(In)o(tro)q(duction)8 b Fb(:)g(:)e(:)g(:)g
+(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h
+(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)20
+b Fc(4)0 341 y(k)n(arc)o(h,)13 b(moun)o(t)h(selector)9 b Fb(:)e(:)f(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)h(:)21 b Fc(17)0 387 y(Keep-aliv)o(es)11
+b Fb(:)c(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)22 b Fc(7)0 432 y(Key)13 b(lo)q(okup)7 b Fb(:)h(:)e(:)g(:)g(:)
+g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)20
+b Fc(14)0 478 y(k)o(ey)m(,)13 b(moun)o(t)g(selector)e Fb(:)6
+b(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)23 b Fc(17)0
+524 y(License)14 b(Information)6 b Fb(:)i(:)e(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)h(:)f(:)18 b Fc(2)0 569 y(link,)c(\014lesystem)h(t)o(yp)q(e)s
+Fb(:)7 b(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)16 b
+Fc(29)0 615 y(linkx,)f(\014lesystem)f(t)o(yp)q(e)d Fb(:)6 b(:)g(:)g(:)g(:)g
+(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f
+(:)g(:)g(:)g(:)g(:)g(:)23 b Fc(29)0 661 y(Listing)15 b(curren)o(tly)f(moun)o
+(ted)g(\014lesystems)e Fb(:)6 b(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)23 b Fc(34)0 706 y(Lo)q(cation)14 b(format)d Fb(:)6
+b(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)23
+b Fc(14)0 752 y(Lo)q(cation)14 b(lists)8 b Fb(:)h(:)d(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)21 b Fc(6)0
+798 y(Log)13 b(\014lename)5 b Fb(:)j(:)e(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h
+(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)18 b Fc(22)0 843 y(Log)13
+b(message)h(selection)6 b Fb(:)i(:)e(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)18
+b Fc(23)0 889 y(log,)c(FSinfo)g(\014lesystems)g(option)e Fb(:)6
+b(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g
+(:)g(:)23 b Fc(45)0 935 y(Lo)q(oking)15 b(up)e(k)o(eys)c Fb(:)e(:)f(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)21 b Fc(14)0
+980 y(Mac)o(hine)15 b(arc)o(hitecture)f(names)6 b Fb(:)g(:)g(:)h(:)f(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)18
+b Fc(9)0 1026 y(Mac)o(hine)d(arc)o(hitectures)f(supp)q(orted)h(b)o(y)e(Amd)s
+Fb(:)7 b(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)16 b Fc(9)0
+1072 y(Mailing)g(list)9 b Fb(:)e(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)21 b Fc(3)0 1117 y(Map)14
+b(cac)o(he)f(options)6 b Fb(:)i(:)e(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)19 b Fc(29)0 1163 y(Map)14 b(cac)o(he)f(sync)o(hronising)6
+b Fb(:)j(:)d(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)18 b Fc(29)0 1209 y(Map)c(cac)o(he)f(t)o
+(yp)q(es)6 b Fb(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f
+(:)18 b Fc(29)0 1254 y(Map)c(cac)o(he,)f(\015ushing)5 b Fb(:)j(:)e(:)g(:)g(:)
+g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)18 b Fc(35)0 1300 y(Map)c(defaults)d
+Fb(:)6 b(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h
+(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g
+(:)23 b Fc(15)0 1346 y(Map)14 b(en)o(try)f(format)t Fb(:)6
+b(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)17 b
+Fc(14)0 1391 y(Map)d(lo)q(okup)f Fb(:)6 b(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)24 b Fc(14)0 1437 y(Map)14
+b(options)7 b Fb(:)g(:)f(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)h(:)19 b Fc(17)0 1483 y(Map)14 b(t)o(yp)q(es)6
+b Fb(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)19 b Fc(11)0 1528 y(map,)13 b(moun)o(t)h(selector)9
+b Fb(:)e(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)22 b Fc(17)0
+1574 y(maps,)13 b(FSinfo)h(command)g(line)h(option)6 b Fb(:)h(:)f(:)g(:)h(:)f
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)19 b Fc(48)0
+1620 y(Moun)o(t)14 b(a)f(\014lesystem)h(under)g(program)g(con)o(trol)t
+Fb(:)7 b(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)17 b Fc(28)0 1665
+y(Moun)o(t)d(home)f(directories)6 b Fb(:)i(:)f(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)
+g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)19
+b Fc(54)0 1711 y(Moun)o(t)14 b(information)t Fb(:)8 b(:)e(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)h(:)f(:)g(:)16 b Fc(11)0 1757 y(Moun)o(t)e(map)f(t)o(yp)q(es)6
+b Fb(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)18
+b Fc(11)0 1802 y(Moun)o(t)c(maps)6 b Fb(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)19 b Fc(11)0 1848
+y(Moun)o(t)14 b(option;)g(cac)o(he)8 b Fb(:)e(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)21 b Fc(29)0 1893 y(Moun)o(t)14 b(option;)g(dela)o(y)d
+Fb(:)6 b(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)22 b Fc(18)0
+1939 y(Moun)o(t)14 b(option;)g(dev)7 b Fb(:)f(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)20 b Fc(28)0 1985 y(Moun)o(t)14 b(option;)g(fs)6
+b Fb(:)f(:)h(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h
+(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)18
+b Fc(18)0 2030 y(Moun)o(t)c(option;)g(moun)o(t)8 b Fb(:)f(:)f(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)21 b Fc(28)0 2076 y(Moun)o(t)14 b(option;)g(opts)c
+Fb(:)c(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)
+g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)22 b Fc(18)0
+2122 y(Moun)o(t)14 b(option;)g(remopts)e Fb(:)6 b(:)g(:)g(:)g(:)g(:)g(:)g(:)h
+(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g
+(:)24 b Fc(19)0 2167 y(Moun)o(t)14 b(option;)g(rfs)7 b Fb(:)e(:)i(:)f(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)19 b Fc(26)0 2213 y(Moun)o(t)14
+b(option;)g(rhost)d Fb(:)6 b(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)23
+b Fc(26)0 2259 y(Moun)o(t)14 b(option;)g(sublink)e Fb(:)7 b(:)f(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)23 b Fc(20)0 2304 y(Moun)o(t)14 b(option;)g(t)o(yp)q(e)8
+b Fb(:)e(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h
+(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)21 b
+Fc(20)0 2350 y(Moun)o(t)14 b(option;)g(unmoun)o(t)t Fb(:)7
+b(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)16 b Fc(28)0 2396 y(Moun)o(t)e(retries)7
+b Fb(:)g(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)20 b Fc(6)0 2441 y(Moun)o(t)14 b(selector;)f(arc)o(h)8
+b Fb(:)f(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)20 b Fc(16)0
+2487 y(Moun)o(t)14 b(selector;)f(auto)q(dir)8 b Fb(:)g(:)e(:)g(:)g(:)g(:)g(:)
+g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)h(:)20 b Fc(16)0 2533 y(Moun)o(t)14 b(selector;)f(b)o(yte)7
+b Fb(:)g(:)f(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)20 b Fc(16)0
+2578 y(Moun)o(t)14 b(selector;)f(cluster)5 b Fb(:)j(:)e(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)18 b Fc(17)0 2624 y(Moun)o(t)c(selector;)f(domain)7
+b Fb(:)h(:)e(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)20 b Fc(17)1015 158
+y(Moun)o(t)14 b(selector;)g(hostd)6 b Fb(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g
+(:)g(:)19 b Fc(17)1015 204 y(Moun)o(t)14 b(selector;)g(host)8
+b Fb(:)f(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)21 b Fc(17)1015
+250 y(Moun)o(t)14 b(selector;)g(k)n(arc)o(h)7 b Fb(:)g(:)f(:)g(:)g(:)g(:)g(:)
+g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)h(:)f(:)20 b Fc(17)1015 295 y(Moun)o(t)14 b(selector;)g(k)o(ey)7
+b Fb(:)f(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)20 b
+Fc(17)1015 341 y(Moun)o(t)14 b(selector;)g(map)7 b Fb(:)g(:)f(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)h(:)20 b Fc(17)1015 387 y(Moun)o(t)14 b(selector;)g(os)9
+b Fb(:)d(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)22
+b Fc(17)1015 432 y(Moun)o(t)14 b(selector;)g(path)5 b Fb(:)i(:)f(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)h(:)f(:)g(:)18 b Fc(17)1015 478 y(Moun)o(t)c(selector;)g(wire)
+8 b Fb(:)f(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)
+g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)21 b Fc(17)1015
+524 y(moun)o(t)14 b(system)f(call)i(\015ags)9 b Fb(:)e(:)f(:)g(:)g(:)g(:)g(:)
+g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h
+(:)f(:)22 b Fc(18)1015 569 y(moun)o(t)14 b(system)f(call)f
+Fb(:)6 b(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)24
+b Fc(18)1015 615 y(Moun)o(t)14 b(t)o(yp)q(es)6 b Fb(:)h(:)f(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)19 b
+Fc(26)1015 661 y(moun)o(t,)14 b(FSinfo)g(\014lesystems)g(option)7
+b Fb(:)h(:)e(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g
+(:)20 b Fc(44)1015 706 y(moun)o(t,)14 b(moun)o(t)f(option)e
+Fb(:)6 b(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h
+(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)23 b Fc(28)1015
+752 y(Moun)o(ting)15 b(a)e(lo)q(cal)i(disk)6 b Fb(:)i(:)e(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)h(:)19 b Fc(28)1015 798 y(Moun)o(ting)c(a)e(UFS)g(\014lesystem)g
+Fb(:)6 b(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)24 b Fc(28)1015 843 y(Moun)o(ting)15
+b(a)e(v)o(olume)e Fb(:)6 b(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)
+g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)
+23 b Fc(6)1015 889 y(Moun)o(ting)15 b(an)f(atomic)g(group)f(of)g(NFS)g
+(\014lesystems)7 b Fb(:)h(:)e(:)g(:)g(:)g(:)20 b Fc(27)1015
+935 y(Moun)o(ting)15 b(an)e(existing)i(part)e(of)g(the)g(lo)q(cal)i(name)e
+(space)5 b Fb(:)i(:)18 b Fc(29)1015 980 y(Moun)o(ting)d(an)f(NFS)f
+(\014lesystem)8 b Fb(:)g(:)e(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)22 b Fc(26)1015 1026 y(Moun)o(ting)15
+b(en)o(tire)f(exp)q(ort)g(trees)7 b Fb(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)
+g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)20 b Fc(26)1015
+1072 y(Moun)o(ting)15 b(part)f(of)e(the)i(lo)q(cal)g(name)g(space)8
+b Fb(:)f(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)22 b
+Fc(29)1015 1117 y(Moun)o(ting)15 b(user)f(\014lesystems)5 b
+Fb(:)j(:)e(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)
+g(:)g(:)g(:)g(:)g(:)g(:)g(:)19 b Fc(53)1015 1163 y(Multiple-thre)q(aded)d
+(serv)o(er)9 b Fb(:)e(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)22 b Fc(8)1015
+1209 y(Namespace)12 b Fb(:)6 b(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)
+g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)24 b Fc(6)1015 1254 y(ndbm)14
+b(maps)5 b Fb(:)i(:)f(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)h(:)f(:)18 b Fc(12)1015 1300 y(Net)o(w)o(ork)13
+b(\014lesystem)i(group)10 b Fb(:)c(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)
+g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)23
+b Fc(27)1015 1346 y(Net)o(w)o(ork)13 b(host)h(\014lesystem)5
+b Fb(:)j(:)e(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)19 b Fc(26)1015 1391
+y(Net)o(w)o(ork-wide)14 b(naming)6 b Fb(:)i(:)e(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f
+(:)g(:)g(:)19 b Fc(5)1015 1437 y(NFS)13 b(ping)7 b Fb(:)h(:)e(:)g(:)g(:)g(:)g
+(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)20
+b Fc(7)1015 1483 y(nfs,)13 b(\014lesystem)i(t)o(yp)q(e)c Fb(:)6
+b(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)24 b Fc(26)1015
+1528 y(nfsx,)13 b(\014lesystem)i(t)o(yp)q(e)9 b Fb(:)e(:)f(:)g(:)g(:)g(:)g(:)
+g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)h(:)f(:)22 b Fc(27)1015 1574 y(NFS)6 b Fb(:)h(:)f(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f
+(:)g(:)g(:)19 b Fc(26)1015 1620 y(NIS)13 b(\(YP\))g(domain)h(name)5
+b Fb(:)j(:)e(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)19 b Fc(24)1015 1665
+y(NIS)13 b(\(YP\))g(maps)6 b Fb(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)20 b Fc(12)1015 1711 y(No)q(des)14 b(generated)g(on)f
+(a)g(restart)5 b Fb(:)i(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)18 b Fc(32)1015 1757 y(Non-blo)q(c)o(king)
+e(op)q(eration)c Fb(:)6 b(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)23
+b Fc(8)1015 1802 y(Normalising)16 b(hostnames)c Fb(:)6 b(:)g(:)g(:)h(:)f(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g
+(:)g(:)g(:)24 b Fc(22)1015 1848 y(Obtaining)16 b(the)d(source)h(co)q(de)s
+Fb(:)7 b(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)17 b Fc(3)1015 1893 y(Op)q(erating)e
+(system)e(names)8 b Fb(:)f(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)
+g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)21
+b Fc(9)1015 1939 y(Op)q(erating)15 b(systems)e(supp)q(orted)i(b)o(y)e(Amd)6
+b Fb(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)19
+b Fc(9)1015 1985 y(Op)q(erational)d(principles)t Fb(:)9 b(:)d(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)h(:)16 b Fc(6)1015 2030 y(opts,)e(FSinfo)g(\014lesystems)g
+(option)8 b Fb(:)g(:)e(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)
+g(:)g(:)g(:)g(:)g(:)22 b Fc(43)1015 2076 y(opts,)14 b(moun)o(t)f(option)g
+Fb(:)6 b(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)24 b
+Fc(18)1015 2122 y(os,)13 b(FSinfo)h(host)g(attribute)8 b Fb(:)f(:)f(:)g(:)g
+(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g
+(:)g(:)g(:)g(:)21 b Fc(41)1015 2167 y(os,)13 b(moun)o(t)h(selector)e
+Fb(:)6 b(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)24
+b Fc(17)1015 2213 y(Ov)o(erriding)16 b(defaults)e(on)f(the)g(command)h(line)6
+b Fb(:)i(:)e(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)19 b Fc(21)1015
+2259 y(Ov)o(erriding)d(the)d(default)h(moun)o(t)f(p)q(oin)o(t)f
+Fb(:)6 b(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)24
+b Fc(18)1015 2304 y(Ov)o(erriding)16 b(the)d(lo)q(cal)h(domain)h(name)t
+Fb(:)6 b(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)17
+b Fc(21)1015 2350 y(Ov)o(erriding)f(the)d(NIS)g(\(YP\))f(domain)j(name)t
+Fb(:)7 b(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)18 b
+Fc(24)1015 2396 y(P)o(assing)d(parameters)f(to)f(the)g(moun)o(t)g(system)h
+(call)7 b Fb(:)g(:)g(:)f(:)g(:)g(:)g(:)20 b Fc(18)1015 2441
+y(passno,)14 b(FSinfo)g(\014lesystems)h(option)5 b Fb(:)j(:)e(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)18 b Fc(43)1015
+2487 y(P)o(assw)o(ord)c(\014le)g(maps)t Fb(:)6 b(:)g(:)g(:)g(:)g(:)h(:)f(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)17 b Fc(13)1015 2533 y(path,)d(moun)o(t)f(selector)7
+b Fb(:)g(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)20 b Fc(17)1015
+2578 y(P)o(athname)14 b(op)q(erators)e Fb(:)6 b(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)h(:)f(:)g(:)23 b Fc(15)1015 2624 y(Pic)o(king)16 b(up)d(existing)i(moun)o
+(ts)d Fb(:)6 b(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)
+g(:)g(:)g(:)g(:)g(:)h(:)f(:)24 b Fc(23)p eop
+%%Page: 56 58
+56 57 bop 15 -83 a Fo(SMM:13-56)889 b(4.4)15 b(BSD)g(Automoun)o(ter)f
+(Reference)j(Man)o(ual)0 158 y Fc(pid)d(\014le,)g(creating)g(with)f(-p)g
+(option)g Fb(:)6 b(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)
+h(:)f(:)g(:)g(:)23 b Fc(22)0 204 y(Primary)14 b(serv)o(er)t
+Fb(:)7 b(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)17
+b Fc(18)0 250 y(pro)q(cess)d(id)g(of)e(Amd)h(daemon)8 b Fb(:)f(:)f(:)g(:)g(:)
+g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f
+(:)20 b Fc(22)0 295 y(Pro)q(cess)14 b(id)c Fb(:)c(:)g(:)g(:)g(:)g(:)h(:)f(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)22 b
+Fc(22)0 341 y(Program)14 b(\014lesystem)e Fb(:)6 b(:)g(:)g(:)g(:)g(:)g(:)g(:)
+g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)h(:)f(:)g(:)g(:)23 b Fc(28)0 387 y(program,)13 b(\014lesystem)i(t)o(yp)q
+(e)7 b Fb(:)g(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)20 b Fc(28)0 432 y(Querying)15
+b(an)e(alternate)h(host)t Fb(:)7 b(:)f(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)
+g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)17 b Fc(35)0
+478 y(quiet,)d(FSinfo)g(command)g(line)g(option)8 b Fb(:)g(:)e(:)g(:)g(:)g(:)
+g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)20 b Fc(49)0 524
+y(Referencing)15 b(an)e(existing)i(part)e(of)g(the)g(lo)q(cal)i(name)e(space)
+82 569 y Fb(:)6 b(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)18 b Fc(29)0
+615 y(Referencing)d(part)e(of)g(the)g(lo)q(cal)h(name)g(space)8
+b Fb(:)f(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)21 b Fc(29)0
+661 y(Regular)15 b(expressions)g(in)f(maps)d Fb(:)6 b(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)23
+b Fc(29)0 706 y(remopts,)13 b(moun)o(t)h(option)5 b Fb(:)i(:)f(:)g(:)g(:)g(:)
+g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h
+(:)f(:)g(:)g(:)17 b Fc(19)0 752 y(Replacemen)o(t)e(v)o(olumes)9
+b Fb(:)e(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)21 b Fc(5)0
+798 y(Replicated)15 b(v)o(olumes)d Fb(:)6 b(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)23 b Fc(5)0 843 y(Resolving)16 b(aliased)f(hostnames)c
+Fb(:)6 b(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f
+(:)g(:)g(:)g(:)g(:)23 b Fc(22)0 889 y(Restarting)15 b(existing)g(moun)o(ts)5
+b Fb(:)h(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)h(:)f(:)g(:)17 b Fc(23)0 935 y(rfs,)12 b(moun)o(t)i(option)8
+b Fb(:)g(:)e(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)21
+b Fc(26)0 980 y(rhost,)13 b(moun)o(t)h(option)s Fb(:)8 b(:)e(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)h(:)f(:)g(:)g(:)g(:)16 b Fc(26)0 1026 y(Ro)q(ot)d(\014lesystem)8
+b Fb(:)g(:)e(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)21
+b Fc(31)0 1072 y(ro)q(ot,)13 b(\014lesystem)h(t)o(yp)q(e)9
+b Fb(:)e(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)21 b Fc(31)0
+1117 y(RPC)13 b(retries)t Fb(:)7 b(:)f(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)
+g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)17 b Fc(8)0 1163 y(Run-time)d
+(administration)7 b Fb(:)i(:)d(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)
+f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)19 b Fc(33)0
+1209 y(rwho)13 b(serv)o(ers)7 b Fb(:)g(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)
+g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)20 b Fc(56)0 1254 y(Secondary)15
+b(serv)o(er)t Fb(:)7 b(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)
+f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g
+(:)17 b Fc(18)0 1300 y(sel,)c(FSinfo)h(moun)o(t)g(option)6
+b Fb(:)i(:)e(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)19 b Fc(44)0 1346 y(Selecting)c(sp)q
+(eci\014c)g(log)f(messages)s Fb(:)7 b(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)16 b Fc(23)0 1391
+y(Selector;)e(arc)o(h)7 b Fb(:)f(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)h(:)19 b Fc(16)0 1437 y(Selector;)14
+b(auto)q(dir)7 b Fb(:)g(:)f(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)h(:)19 b Fc(16)0 1483 y(Selector;)14 b(b)o(yte)6 b Fb(:)h(:)f(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)19 b
+Fc(16)0 1528 y(Selector;)14 b(cluster)t Fb(:)7 b(:)f(:)g(:)g(:)g(:)g(:)g(:)h
+(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)17 b Fc(17)0 1574 y(Selector;)d(domain)6
+b Fb(:)i(:)e(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)19
+b Fc(17)0 1620 y(Selector;)14 b(hostd)5 b Fb(:)i(:)f(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)18 b Fc(17)0 1665 y(Selector;)c(host)7
+b Fb(:)g(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)20 b Fc(17)0 1711 y(Selector;)14 b(k)n(arc)o(h)6 b Fb(:)h(:)f(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)19 b Fc(17)0
+1757 y(Selector;)14 b(k)o(ey)6 b Fb(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)18 b Fc(17)0 1802 y(Selector;)c(map)6
+b Fb(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g
+(:)19 b Fc(17)0 1848 y(Selector;)14 b(os)8 b Fb(:)f(:)f(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)21 b
+Fc(17)0 1893 y(Selector;)14 b(path)t Fb(:)7 b(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h
+(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)17 b Fc(17)0 1939 y(Selector;)d(wire)7
+b Fb(:)g(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g
+(:)20 b Fc(17)0 1985 y(Selectors)t Fb(:)8 b(:)e(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)17
+b Fc(16)0 2030 y(Serv)o(er)d(crashes)e Fb(:)6 b(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h
+(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)24 b Fc(7)0 2076
+y(Setting)14 b(a)f(dela)o(y)i(on)e(a)g(moun)o(t)g(lo)q(cation)6
+b Fb(:)j(:)d(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)19
+b Fc(18)0 2122 y(Setting)14 b(Amd's)f(RPC)g(parameters)7 b
+Fb(:)g(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)
+g(:)20 b Fc(23)0 2167 y(Setting)14 b(debug)g(\015ags)f Fb(:)6
+b(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)24 b Fc(24)0
+2213 y(Setting)14 b(default)g(map)g(parameters)8 b Fb(:)e(:)g(:)h(:)f(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)20 b
+Fc(15)0 2259 y(Setting)14 b(map)g(cac)o(he)f(parameters)s Fb(:)8
+b(:)e(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g
+(:)g(:)16 b Fc(29)0 2304 y(Setting)e(map)g(options)e Fb(:)6
+b(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)23 b Fc(17)0 2350
+y(Setting)14 b(system)g(moun)o(t)f(options)i(for)e(non-lo)q(cal)i(net)o(w)o
+(orks)82 2396 y Fb(:)6 b(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)18 b
+Fc(19)0 2441 y(Setting)c(system)g(moun)o(t)f(options)6 b Fb(:)i(:)e(:)g(:)g
+(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)19
+b Fc(18)0 2487 y(Setting)14 b(the)f(cluster)h(name)8 b Fb(:)f(:)f(:)g(:)h(:)f
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g
+(:)g(:)g(:)21 b Fc(24)0 2533 y(Setting)14 b(the)f(default)h(moun)o(t)g
+(directory)9 b Fb(:)e(:)g(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)22 b Fc(21)0 2578 y(Setting)14 b(the)f(\014lesystem)i(t)o(yp)q(e)e(option)
+7 b Fb(:)h(:)e(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)
+20 b Fc(20)0 2624 y(Setting)14 b(the)f(in)o(terv)n(al)i(b)q(efore)e(a)g
+(\014lesystem)i(times)f(out)c Fb(:)c(:)g(:)22 b Fc(21)1015
+158 y(Setting)15 b(the)e(in)o(terv)n(al)i(b)q(et)o(w)o(een)e(unmoun)o(t)h
+(attempts)e Fb(:)6 b(:)g(:)g(:)25 b Fc(23)1015 204 y(Setting)15
+b(the)e(Kernel)h(arc)o(hitecture)5 b Fb(:)i(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)h
+(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)18 b Fc(22)1015
+250 y(Setting)d(the)e(lo)q(cal)i(domain)f(name)8 b Fb(:)f(:)f(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)21
+b Fc(21)1015 295 y(Setting)15 b(the)e(lo)q(cal)i(moun)o(t)e(p)q(oin)o(t)8
+b Fb(:)g(:)e(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)22 b Fc(18)1015 341 y(Setting)15 b(the)e(log)h(\014le)8
+b Fb(:)f(:)f(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)22
+b Fc(22)1015 387 y(Setting)15 b(the)e(NIS)g(\(YP\))f(domain)j(name)9
+b Fb(:)d(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)22
+b Fc(24)1015 432 y(Setting)15 b(the)e(sublink)i(option)s Fb(:)9
+b(:)d(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)17 b Fc(20)1015 478 y(Sharing)e(a)e(\014leserv)o(er)i
+(b)q(et)o(w)o(een)e(arc)o(hitectures)g Fb(:)6 b(:)g(:)g(:)g(:)g(:)g(:)g(:)h
+(:)f(:)24 b Fc(55)1015 524 y(SIGHUP)13 b(signal)8 b Fb(:)h(:)d(:)g(:)g(:)g(:)
+g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)22 b Fc(29)1015
+569 y(SIGINT)13 b(signal)7 b Fb(:)h(:)e(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h
+(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)20 b Fc(34)1015 615 y(SIGTERM)14
+b(signal)9 b Fb(:)f(:)e(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)22
+b Fc(34)1015 661 y(Source)14 b(co)q(de)g(distribution)g Fb(:)6
+b(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)24 b Fc(3)1015 706 y(Starting)15
+b(Amd)5 b Fb(:)h(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)18 b Fc(33)1015 752 y(Statically)e(moun)o(ts)e(\014lesystems,)g
+(FSinfo)7 b Fb(:)h(:)e(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)20
+b Fc(45)1015 798 y(Statistics)12 b Fb(:)6 b(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)23 b
+Fc(36)1015 843 y(Stopping)16 b(Amd)7 b Fb(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)20 b Fc(34)1015 889 y(Stripping)c(the)d
+(lo)q(cal)i(domain)f(name)7 b Fb(:)g(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h
+(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)20 b Fc(15)1015 935 y(sublink,)c(moun)o(t)d
+(option)g Fb(:)6 b(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)
+g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)25 b Fc(20)1015
+980 y(sublink)13 b Fb(:)6 b(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)23 b Fc(5)1015
+1026 y(Supp)q(orted)15 b(mac)o(hine)g(arc)o(hitectures)8 b
+Fb(:)f(:)f(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)
+g(:)21 b Fc(9)1015 1072 y(Supp)q(orted)15 b(op)q(erating)g(systems)5
+b Fb(:)i(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)h(:)f(:)18 b Fc(9)1015 1117 y(Sym)o(b)q(olic)e(link)f
+(\014lesystem)f(I)q(I)8 b Fb(:)e(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)21 b Fc(29)1015
+1163 y(Sym)o(b)q(olic)16 b(link)f(\014lesystem)s Fb(:)8 b(:)e(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)17 b Fc(29)1015 1209 y(symlink,)e(link)g(\014lesystem)f(t)o(yp)q
+(e)9 b Fb(:)e(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)22 b Fc(29)1015 1254 y(symlink,)15 b(linkx)g
+(\014lesystem)g(t)o(yp)q(e)7 b Fb(:)f(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)20 b Fc(29)1015
+1300 y(Sync)o(hronisi)q(ng)c(the)d(map)h(cac)o(he)5 b Fb(:)h(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)18
+b Fc(29)1015 1346 y(syslog)d(priorities)6 b Fb(:)j(:)d(:)g(:)g(:)g(:)g(:)h(:)
+f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)20 b Fc(23)1015 1391 y(syslog)13
+b Fb(:)6 b(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)
+g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)24 b Fc(22)1015 1437 y(The)13
+b(moun)o(t)h(system)f(call)5 b Fb(:)j(:)e(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)18
+b Fc(18)1015 1483 y(T)m(op)13 b(lev)o(el)i(\014lesystem)7 b
+Fb(:)h(:)e(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)
+g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)20 b Fc(31)1015
+1528 y(toplvl,)15 b(\014lesystem)f(t)o(yp)q(e)s Fb(:)8 b(:)e(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)17 b Fc(31)1015 1574 y(t)o(yp)q(e,)d(moun)o(t)f(option)e
+Fb(:)6 b(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)23 b
+Fc(20)1015 1620 y(T)o(yp)q(es)14 b(of)f(con\014guration)i(map)8
+b Fb(:)f(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)22 b Fc(11)1015 1665 y(T)o(yp)q(es)14
+b(of)f(\014lesystem)f Fb(:)6 b(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)
+g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)
+24 b Fc(26)1015 1711 y(T)o(yp)q(es)14 b(of)f(moun)o(t)g(map)5
+b Fb(:)h(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)18 b Fc(11)1015
+1757 y(ufs,)13 b(\014lesystem)i(t)o(yp)q(e)c Fb(:)6 b(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g
+(:)g(:)g(:)g(:)g(:)g(:)24 b Fc(28)1015 1802 y(UFS)6 b Fb(:)h(:)f(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h
+(:)f(:)g(:)g(:)19 b Fc(28)1015 1848 y(Union)c(\014le)e(maps)6
+b Fb(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)19
+b Fc(13)1015 1893 y(Union)c(\014lesystem)7 b Fb(:)g(:)g(:)f(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)20 b Fc(31)1015 1939 y(union,)15
+b(\014lesystem)f(t)o(yp)q(e)5 b Fb(:)i(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)
+f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)
+18 b Fc(31)1015 1985 y(Unix)c(\014lesystem)9 b Fb(:)f(:)e(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)22 b Fc(28)1015 2030
+y(Unix)14 b(namespace)e Fb(:)6 b(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)h(:)23 b Fc(6)1015 2076 y(unmoun)o(t)15 b(attempt)e(bac)o(k)o
+(o\013)h(in)o(terv)n(al)8 b Fb(:)g(:)e(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)
+f(:)g(:)g(:)g(:)g(:)g(:)21 b Fc(23)1015 2122 y(unmoun)o(t,)14
+b(moun)o(t)g(option)5 b Fb(:)j(:)e(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)
+f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)18
+b Fc(28)1015 2167 y(Unmoun)o(ting)d(a)e(\014lesystem)6 b Fb(:)i(:)e(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)19 b Fc(37)1015 2213 y(User)13 b(\014lesystems)t
+Fb(:)8 b(:)e(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)17
+b Fc(53)1015 2259 y(User)c(maps,)g(automatic)i(generation)t
+Fb(:)8 b(:)e(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)18 b Fc(13)1015 2304 y(Using)c(FSinfo)s Fb(:)8 b(:)e(:)g(:)g(:)g(:)g(:)g
+(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)17 b Fc(38)1015
+2350 y(Using)d(syslog)h(to)e(log)h(errors)c Fb(:)c(:)g(:)g(:)g(:)g(:)h(:)f(:)
+g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)23
+b Fc(22)1015 2396 y(Using)14 b(the)g(passw)o(ord)f(\014le)h(as)f(a)g(map)6
+b Fb(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g
+(:)19 b Fc(13)1015 2441 y(V)m(ariable)c(expansion)5 b Fb(:)j(:)e(:)g(:)g(:)g
+(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)18 b Fc(15)1015 2487 y(v)o(erb)q(ose,)c
+(FSinfo)g(command)g(line)h(option)t Fb(:)8 b(:)e(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)18 b Fc(49)1015 2533 y(V)m(ersion)c(information)h(at)e
+(run-time)6 b Fb(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)19 b Fc(37)1015 2578 y(V)m(ersion)14 b(information)6
+b Fb(:)i(:)e(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)19 b
+Fc(23)1015 2624 y(v)o(olname,)c(FSinfo)f(moun)o(t)f(option)f
+Fb(:)6 b(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g
+(:)g(:)g(:)23 b Fc(44)p eop
+%%Page: 57 59
+57 58 bop 0 -83 a Fo(Index)1613 b(SMM:13-57)0 158 y Fc(V)m(olume)14
+b(binding)6 b Fb(:)j(:)d(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)19 b Fc(6)0 204 y(V)m(olume)14 b(names)8 b Fb(:)f(:)f(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)21 b
+Fc(5)0 250 y(V)m(olume)7 b Fb(:)g(:)f(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)20 b
+Fc(5)0 295 y(Wildcards)c(in)d(maps)5 b Fb(:)j(:)e(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)h(:)f(:)g(:)g(:)18 b Fc(14)1015 158 y(wire,)13 b(moun)o(t)h(selector)d
+Fb(:)6 b(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g(:)23 b Fc(17)1015
+204 y(YP)13 b(domain)i(name)8 b Fb(:)e(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)
+g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)g(:)g
+(:)g(:)g(:)g(:)21 b Fc(24)p eop
+%%Page: -1 60
+-1 59 bop 1756 -83 a Fo(SMM:13-i)0 158 y Fm(T)-7 b(able)15
+b(of)g(Con)n(ten)n(ts)0 308 y Fq(Preface)c Fa(:)e(:)i(:)f(:)g(:)g(:)h(:)f(:)g
+(:)g(:)g(:)h(:)f(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g
+(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g
+(:)h(:)f(:)g(:)g(:)g(:)34 b Fq(1)0 420 y(License)13 b Fa(:)e(:)f(:)g(:)g(:)g
+(:)h(:)f(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h
+(:)f(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f
+(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)35 b Fq(1)0 532 y(Source)23
+b(Distribution)15 b Fa(:)d(:)e(:)h(:)f(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)
+g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)h(:)f(:)g(:)g
+(:)g(:)h(:)f(:)g(:)37 b Fq(1)149 594 y Fo(Bug)16 b(Rep)q(orts)e
+Fj(:)7 b(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g
+(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g
+(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g
+(:)g(:)g(:)28 b Fo(1)149 644 y(Mailing)17 b(List)8 b Fj(:)g(:)f(:)g(:)h(:)f
+(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h
+(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g
+(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)22
+b Fo(2)0 750 y Fq(In)n(tro)r(duction)14 b Fa(:)e(:)e(:)h(:)f(:)g(:)g(:)g(:)h
+(:)f(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f
+(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)36
+b Fq(2)0 862 y(1)67 b(Ov)n(erview)11 b Fa(:)f(:)h(:)f(:)g(:)g(:)h(:)f(:)g(:)g
+(:)g(:)h(:)f(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g
+(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)33
+b Fq(2)149 925 y Fo(1.1)45 b(F)l(undamen)o(tals)10 b Fj(:)d(:)g(:)h(:)f(:)g
+(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f
+(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h
+(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)24 b Fo(2)149 975 y(1.2)45
+b(Filesystems)16 b(and)f(V)l(olumes)10 b Fj(:)e(:)f(:)g(:)h(:)f(:)g(:)g(:)g
+(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g
+(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)24
+b Fo(3)149 1024 y(1.3)45 b(V)l(olume)16 b(Naming)9 b Fj(:)e(:)h(:)f(:)g(:)g
+(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g
+(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f
+(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)23 b Fo(3)149 1074 y(1.4)45
+b(V)l(olume)16 b(Binding)8 b Fj(:)i(:)d(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h
+(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g
+(:)h(:)f(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g
+(:)h(:)22 b Fo(3)149 1124 y(1.5)45 b(Op)q(erational)16 b(Principles)6
+b Fj(:)k(:)d(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g
+(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f
+(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)20 b Fo(3)149 1174 y(1.6)45
+b(Moun)o(ting)15 b(a)g(V)l(olume)e Fj(:)7 b(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h
+(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g
+(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)26
+b Fo(4)149 1224 y(1.7)45 b(Automatic)15 b(Unmoun)o(ting)e Fj(:)7
+b(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g
+(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f
+(:)g(:)g(:)g(:)27 b Fo(4)149 1273 y(1.8)45 b(Keep-aliv)o(es)5
+b Fj(:)10 b(:)d(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f
+(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h
+(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g
+(:)20 b Fo(5)149 1323 y(1.9)45 b(Non-blo)q(c)o(king)17 b(Op)q(eration)10
+b Fj(:)e(:)f(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g
+(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g
+(:)g(:)g(:)h(:)f(:)g(:)24 b Fo(5)0 1430 y Fq(2)67 b(Supp)r(orted)24
+b(Platforms)11 b Fa(:)e(:)i(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)h(:)f(:)g(:)g
+(:)g(:)h(:)f(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)h(:)f(:)g(:)g(:)33
+b Fq(6)149 1492 y Fo(2.1)45 b(Supp)q(orted)16 b(Op)q(erating)g(Systems)11
+b Fj(:)c(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g
+(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)25
+b Fo(6)149 1542 y(2.2)45 b(Supp)q(orted)16 b(Mac)o(hine)g(Arc)o(hitectures)6
+b Fj(:)i(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g
+(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)20
+b Fo(7)0 1648 y Fq(3)67 b(Moun)n(t)23 b(Maps)11 b Fa(:)f(:)g(:)g(:)g(:)g(:)h
+(:)f(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f
+(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)34
+b Fq(7)149 1710 y Fo(3.1)45 b(Map)15 b(T)o(yp)q(es)5 b Fj(:)j(:)f(:)g(:)g(:)g
+(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g
+(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g
+(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)20 b Fo(7)299
+1760 y(3.1.1)44 b(File)16 b(maps)9 b Fj(:)e(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g
+(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f
+(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h
+(:)23 b Fo(8)299 1810 y(3.1.2)44 b(ndbm)16 b(maps)10 b Fj(:)d(:)g(:)g(:)g(:)h
+(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g
+(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g
+(:)g(:)h(:)24 b Fo(8)299 1859 y(3.1.3)44 b(NIS)16 b(maps)9
+b Fj(:)e(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g
+(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g
+(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)23 b Fo(9)299 1909
+y(3.1.4)44 b(Hesio)q(d)16 b(maps)10 b Fj(:)d(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f
+(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h
+(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)24
+b Fo(9)299 1959 y(3.1.5)44 b(P)o(assw)o(ord)14 b(maps)f Fj(:)7
+b(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f
+(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h
+(:)f(:)28 b Fo(10)299 2009 y(3.1.6)44 b(Union)16 b(maps)6 b
+Fj(:)h(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)
+g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g
+(:)g(:)h(:)f(:)g(:)g(:)g(:)21 b Fo(10)149 2059 y(3.2)45 b(Ho)o(w)15
+b(k)o(eys)g(are)f(lo)q(ok)o(ed)i(up)c Fj(:)7 b(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)
+g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f
+(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)26 b
+Fo(10)149 2108 y(3.3)45 b(Lo)q(cation)16 b(F)l(ormat)11 b Fj(:)5
+b(:)i(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g
+(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f
+(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)25 b Fo(11)299 2158
+y(3.3.1)44 b(Map)15 b(Defaults)c Fj(:)6 b(:)i(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g
+(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g
+(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)25 b
+Fo(12)299 2208 y(3.3.2)44 b(V)l(ariable)16 b(Expansion)e Fj(:)7
+b(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g
+(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)28
+b Fo(12)299 2258 y(3.3.3)44 b(Selectors)8 b Fj(:)f(:)g(:)h(:)f(:)g(:)g(:)g(:)
+h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g
+(:)h(:)f(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g
+(:)h(:)22 b Fo(13)299 2308 y(3.3.4)44 b(Map)15 b(Options)5
+b Fj(:)i(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h
+(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g
+(:)h(:)f(:)g(:)g(:)g(:)h(:)19 b Fo(14)448 2357 y(3.3.4.1)44
+b(dela)o(y)15 b(Option)5 b Fj(:)j(:)g(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h
+(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g
+(:)h(:)f(:)g(:)20 b Fo(14)448 2407 y(3.3.4.2)44 b(fs)15 b(Option)c
+Fj(:)c(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)h(:)f(:)
+g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)
+25 b Fo(14)448 2457 y(3.3.4.3)44 b(opts)14 b(Option)5 b Fj(:)j(:)g(:)f(:)g(:)
+g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g
+(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)20 b Fo(15)448
+2507 y(3.3.4.4)44 b(remopts)14 b(Option)7 b Fj(:)i(:)e(:)g(:)g(:)h(:)f(:)g(:)
+g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g
+(:)g(:)g(:)h(:)f(:)22 b Fo(16)448 2557 y(3.3.4.5)44 b(sublink)17
+b(Option)6 b Fj(:)h(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g
+(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)20
+b Fo(16)448 2607 y(3.3.4.6)44 b(t)o(yp)q(e)15 b(Option)f Fj(:)7
+b(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g
+(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)28
+b Fo(16)p eop
+%%Page: -2 61
+-2 60 bop 15 -83 a Fo(SMM:13-ii)911 b(4.4)15 b(BSD)g(Automoun)o(ter)f
+(Reference)j(Man)o(ual)0 17 y Fq(4)67 b Ff(Amd)24 b Fq(Command)e(Line)h
+(Options)6 b Fa(:)11 b(:)f(:)h(:)f(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)
+g(:)g(:)h(:)f(:)g(:)g(:)h(:)f(:)29 b Fq(16)149 79 y Fo(4.1)45
+b Fl(-a)15 b Fp(directory)8 b Fj(:)f(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g
+(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g
+(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g
+(:)g(:)h(:)f(:)g(:)19 b Fo(17)149 129 y(4.2)45 b Fl(-c)15 b
+Fp(cac)o(he-in)o(terv)m(al)c Fj(:)e(:)e(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h
+(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g
+(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)24
+b Fo(17)149 178 y(4.3)45 b Fl(-d)15 b Fp(domain)d Fj(:)c(:)f(:)g(:)g(:)g(:)h
+(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g
+(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g
+(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)27 b Fo(17)149 228
+y(4.4)45 b Fl(-k)15 b Fp(k)o(ernel-arc)o(hitecture)f Fj(:)7
+b(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h
+(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g
+(:)h(:)f(:)g(:)g(:)25 b Fo(17)149 278 y(4.5)45 b Fl(-l)15 b
+Fp(log-option)6 b Fj(:)h(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f
+(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h
+(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h
+(:)20 b Fo(18)149 328 y(4.6)45 b Fl(-n)12 b Fj(:)7 b(:)g(:)g(:)g(:)h(:)f(:)g
+(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g
+(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f
+(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)27
+b Fo(18)149 378 y(4.7)45 b Fl(-p)12 b Fj(:)7 b(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)
+h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g
+(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g
+(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)27
+b Fo(18)149 428 y(4.8)45 b Fl(-r)12 b Fj(:)7 b(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)
+h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g
+(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g
+(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)27
+b Fo(18)149 477 y(4.9)45 b Fl(-t)15 b Fp(timeout.retransmit)10
+b Fj(:)c(:)h(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g
+(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g
+(:)g(:)h(:)f(:)g(:)g(:)g(:)24 b Fo(18)149 527 y(4.10)45 b Fl(-v)11
+b Fj(:)6 b(:)i(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)
+f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h
+(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g
+(:)h(:)f(:)g(:)g(:)g(:)26 b Fo(19)149 577 y(4.11)45 b Fl(-w)15
+b Fp(w)o(ait-timeout)8 b Fj(:)f(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f
+(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h
+(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)22
+b Fo(19)149 627 y(4.12)45 b Fl(-x)15 b Fp(opts)d Fj(:)7 b(:)g(:)h(:)f(:)g(:)g
+(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g
+(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f
+(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)26 b Fo(19)149
+677 y(4.13)45 b Fl(-y)15 b Fp(NIS-domain)6 b Fj(:)i(:)f(:)g(:)g(:)h(:)f(:)g
+(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f
+(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f
+(:)g(:)g(:)g(:)h(:)20 b Fo(20)149 726 y(4.14)45 b Fl(-C)15
+b Fp(cluster-name)9 b Fj(:)f(:)f(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g
+(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g
+(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)22
+b Fo(20)149 776 y(4.15)45 b Fl(-D)15 b Fp(opts)d Fj(:)7 b(:)g(:)h(:)f(:)g(:)g
+(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g
+(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f
+(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)26 b Fo(20)0
+883 y Fq(5)67 b(Filesystem)23 b(T)n(yp)r(es)7 b Fa(:)j(:)h(:)f(:)g(:)g(:)g(:)
+h(:)f(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f
+(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)30 b Fq(20)149 945 y
+Fo(5.1)45 b(Net)o(w)o(ork)14 b(Filesystem)i(\(`)p Fl(type:=nfs)p
+Fo('\))7 b Fj(:)e(:)i(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f
+(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)22
+b Fo(21)149 995 y(5.2)45 b(Net)o(w)o(ork)14 b(Host)g(Filesystem)i(\(`)p
+Fl(type:=host)p Fo('\))10 b Fj(:)d(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)
+g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)27 b Fo(21)149
+1045 y(5.3)45 b(Net)o(w)o(ork)14 b(Filesystem)i(Group)f(\(`)p
+Fl(type:=nfsx)p Fo('\))5 b Fj(:)t(:)j(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h
+(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)20 b Fo(22)149
+1094 y(5.4)45 b(Unix)16 b(Filesystem)g(\(`)p Fl(type:=ufs)p
+Fo('\))10 b Fj(:)d(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)
+g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)
+27 b Fo(22)149 1144 y(5.5)45 b(Program)14 b(Filesystem)i(\(`)p
+Fl(type:=program)p Fo('\))6 b Fj(:)f(:)i(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g
+(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)22
+b Fo(23)149 1194 y(5.6)45 b(Sym)o(b)q(olic)17 b(Link)f(Filesystem)g(\(`)p
+Fl(type:=link)p Fo('\))7 b Fj(:)t(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g
+(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)21 b Fo(23)149
+1244 y(5.7)45 b(Sym)o(b)q(olic)17 b(Link)f(Filesystem)g(I)q(I)g(\(`)p
+Fl(type:=link)p Fo('\))10 b Fj(:)d(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)
+g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)27 b Fo(24)149 1294
+y(5.8)45 b(Automoun)o(t)14 b(Filesystem)i(\(`)p Fl(type:=auto)p
+Fo('\))6 b Fj(:)t(:)i(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g
+(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)20 b Fo(24)149
+1343 y(5.9)45 b(Direct)15 b(Automoun)o(t)g(Filesystem)h(\(`)p
+Fl(type:=direct)p Fo('\))10 b Fj(:)d(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g
+(:)h(:)f(:)g(:)g(:)g(:)h(:)28 b Fo(25)149 1393 y(5.10)45 b(Union)16
+b(Filesystem)g(\(`)p Fl(type:=union)p Fo('\))5 b Fj(:)g(:)i(:)g(:)g(:)g(:)h
+(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h
+(:)f(:)g(:)g(:)g(:)h(:)20 b Fo(25)149 1443 y(5.11)45 b(Error)14
+b(Filesystem)i(\(`)p Fl(type:=error)p Fo('\))9 b Fj(:)e(:)g(:)h(:)f(:)g(:)g
+(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g
+(:)g(:)g(:)h(:)f(:)g(:)27 b Fo(26)149 1493 y(5.12)45 b(T)l(op-lev)o(el)16
+b(Filesystem)g(\(`)p Fl(type:=toplvl)p Fo('\))11 b Fj(:)c(:)h(:)f(:)g(:)g(:)g
+(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g
+(:)29 b Fo(26)149 1543 y(5.13)45 b(Ro)q(ot)15 b(Filesystem)t
+Fj(:)8 b(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g
+(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g
+(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)19 b Fo(26)149
+1592 y(5.14)45 b(Inheritance)16 b(Filesystem)d Fj(:)7 b(:)g(:)h(:)f(:)g(:)g
+(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g
+(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)27
+b Fo(26)0 1699 y Fq(6)67 b(Run-time)24 b(Administration)6 b
+Fa(:)13 b(:)d(:)h(:)f(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)h(:)f(:)g
+(:)g(:)g(:)h(:)f(:)g(:)g(:)h(:)f(:)29 b Fq(27)149 1761 y Fo(6.1)45
+b(Starting)15 b Fp(Amd)c Fj(:)c(:)g(:)g(:)h(:)f(:)g(:)g(:)h(:)f(:)g(:)g(:)g
+(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g
+(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g
+(:)g(:)24 b Fo(27)149 1811 y(6.2)45 b(Stopping)16 b Fp(Amd)e
+Fj(:)7 b(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g
+(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g
+(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)26 b
+Fo(28)149 1861 y(6.3)45 b(Con)o(trolling)16 b Fp(Amd)10 b Fj(:)d(:)h(:)f(:)g
+(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f
+(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h
+(:)f(:)g(:)g(:)g(:)h(:)f(:)23 b Fo(28)299 1911 y(6.3.1)44 b
+Fp(Amq)16 b Fo(default)g(information)9 b Fj(:)e(:)g(:)g(:)g(:)g(:)h(:)f(:)g
+(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f
+(:)g(:)g(:)g(:)h(:)23 b Fo(28)299 1960 y(6.3.2)44 b Fp(Amq)16
+b Fo(-f)f(option)t Fj(:)8 b(:)f(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g
+(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g
+(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)19 b Fo(29)299 2010
+y(6.3.3)44 b Fp(Amq)16 b Fo(-h)f(option)9 b Fj(:)f(:)f(:)g(:)g(:)g(:)h(:)f(:)
+g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f
+(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)23
+b Fo(29)299 2060 y(6.3.4)44 b Fp(Amq)16 b Fo(-m)f(option)e
+Fj(:)7 b(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g
+(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g
+(:)g(:)h(:)f(:)27 b Fo(29)299 2110 y(6.3.5)44 b Fp(Amq)16 b
+Fo(-M)f(option)10 b Fj(:)e(:)f(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)
+g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g
+(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)25 b Fo(30)299 2160 y(6.3.6)44
+b Fp(Amq)16 b Fo(-s)f(option)e Fj(:)7 b(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h
+(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h
+(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)27 b Fo(30)299
+2209 y(6.3.7)44 b Fp(Amq)16 b Fo(-u)f(option)9 b Fj(:)f(:)f(:)g(:)g(:)g(:)h
+(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g
+(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)23
+b Fo(31)299 2259 y(6.3.8)44 b Fp(Amq)16 b Fo(-v)f(option)9
+b Fj(:)f(:)f(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g
+(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g
+(:)g(:)h(:)f(:)g(:)24 b Fo(31)299 2309 y(6.3.9)44 b(Other)15
+b Fp(Amq)h Fo(options)d Fj(:)7 b(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g
+(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g
+(:)g(:)g(:)h(:)f(:)g(:)27 b Fo(31)p eop
+%%Page: -3 62
+-3 61 bop 1730 -83 a Fo(SMM:13-iii)0 17 y Fq(7)67 b(FSinfo)11
+b Fa(:)f(:)h(:)f(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)h(:)f(:)g(:)g
+(:)g(:)h(:)f(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)h(:)f(:)g(:)g(:)g
+(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)h(:)33 b Fq(31)149 79
+y Fo(7.1)45 b Fp(FSinfo)18 b Fo(o)o(v)o(erview)8 b Fj(:)f(:)g(:)g(:)g(:)g(:)h
+(:)f(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h
+(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g
+(:)h(:)f(:)g(:)g(:)g(:)23 b Fo(31)149 129 y(7.2)45 b(Using)16
+b Fp(FSinfo)9 b Fj(:)f(:)f(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)
+h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g
+(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)22
+b Fo(32)149 178 y(7.3)45 b Fp(FSinfo)18 b Fo(grammar)12 b Fj(:)c(:)f(:)g(:)g
+(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g
+(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g
+(:)g(:)g(:)h(:)f(:)g(:)g(:)29 b Fo(32)149 228 y(7.4)45 b Fp(FSinfo)18
+b Fo(host)d(de\014nitions)f Fj(:)7 b(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g
+(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g
+(:)g(:)g(:)h(:)f(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)27 b Fo(33)149
+278 y(7.5)45 b Fp(FSinfo)18 b Fo(host)d(attributes)8 b Fj(:)e(:)h(:)h(:)f(:)g
+(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f
+(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h
+(:)22 b Fo(33)299 328 y(7.5.1)44 b(netif)16 b(Option)e Fj(:)7
+b(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g
+(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f
+(:)g(:)g(:)g(:)h(:)28 b Fo(34)299 378 y(7.5.2)44 b(con\014g)15
+b(Option)10 b Fj(:)f(:)e(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g
+(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g
+(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)25 b Fo(34)299 428 y(7.5.3)44
+b(arc)o(h)15 b(Option)6 b Fj(:)i(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f
+(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h
+(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)21 b
+Fo(34)299 477 y(7.5.4)44 b(os)15 b(Option)8 b Fj(:)f(:)h(:)f(:)g(:)g(:)g(:)h
+(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g
+(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g
+(:)g(:)23 b Fo(34)299 527 y(7.5.5)44 b(cluster)16 b(Option)d
+Fj(:)7 b(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f
+(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h
+(:)f(:)g(:)g(:)g(:)28 b Fo(35)149 577 y(7.6)45 b Fp(FSinfo)18
+b Fo(\014lesystems)8 b Fj(:)g(:)f(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g
+(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g
+(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)23
+b Fo(35)299 627 y(7.6.1)44 b(fst)o(yp)q(e)15 b(Option)9 b Fj(:)f(:)f(:)g(:)h
+(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g
+(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g
+(:)24 b Fo(36)299 677 y(7.6.2)44 b(opts)15 b(Option)6 b Fj(:)i(:)f(:)g(:)h(:)
+f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h
+(:)f(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h
+(:)f(:)21 b Fo(37)299 726 y(7.6.3)44 b(passno)15 b(Option)e
+Fj(:)7 b(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)h(:)f(:)g
+(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f
+(:)g(:)g(:)g(:)h(:)27 b Fo(37)299 776 y(7.6.4)44 b(freq)15
+b(Option)10 b Fj(:)e(:)f(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g
+(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f
+(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)25 b Fo(37)299 826
+y(7.6.5)44 b(moun)o(t)14 b(Option)5 b Fj(:)j(:)f(:)h(:)f(:)g(:)g(:)g(:)h(:)f
+(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h
+(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)h(:)19
+b Fo(37)299 876 y(7.6.6)44 b(dumpset)15 b(Option)g Fj(:)7 b(:)h(:)f(:)g(:)g
+(:)g(:)h(:)f(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g
+(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)29
+b Fo(38)299 926 y(7.6.7)44 b(log)15 b(Option)9 b Fj(:)f(:)f(:)h(:)f(:)g(:)g
+(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g
+(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f
+(:)g(:)24 b Fo(38)149 976 y(7.7)45 b Fp(FSinfo)18 b Fo(static)d(moun)o(ts)10
+b Fj(:)c(:)h(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g
+(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g
+(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)24 b Fo(38)149 1025 y(7.8)45
+b(De\014ning)16 b(an)f Fp(Amd)i Fo(Moun)o(t)e(Map)g(in)h Fp(FSinfo)f
+Fj(:)7 b(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g
+(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)27 b Fo(39)149 1075 y(7.9)45
+b Fp(FSinfo)18 b Fo(Command)d(Line)h(Options)6 b Fj(:)i(:)g(:)f(:)g(:)g(:)g
+(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g
+(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)21 b Fo(40)299 1125
+y(7.9.1)44 b Fl(-a)15 b Fp(auto)q(dir)e Fj(:)7 b(:)g(:)g(:)g(:)h(:)f(:)g(:)g
+(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g
+(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)24
+b Fo(41)299 1175 y(7.9.2)44 b Fl(-b)15 b Fp(b)q(o)q(otparams)7
+b Fj(:)g(:)g(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g
+(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f
+(:)g(:)g(:)g(:)h(:)20 b Fo(41)299 1225 y(7.9.3)44 b Fl(-d)15
+b Fp(dumpsets)c Fj(:)c(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)
+g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g
+(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)24 b Fo(41)299 1274
+y(7.9.4)44 b Fl(-e)15 b Fp(exp)q(ortfs)f Fj(:)7 b(:)g(:)g(:)h(:)f(:)g(:)g(:)g
+(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g
+(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)27
+b Fo(41)299 1324 y(7.9.5)44 b Fl(-f)15 b Fp(fstab)5 b Fj(:)i(:)g(:)g(:)g(:)g
+(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g
+(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g
+(:)g(:)g(:)h(:)f(:)g(:)19 b Fo(42)299 1374 y(7.9.6)44 b Fl(-h)15
+b Fp(hostname)10 b Fj(:)d(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)h
+(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g
+(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)23 b Fo(42)299 1424
+y(7.9.7)44 b Fl(-m)15 b Fp(moun)o(t-maps)c Fj(:)c(:)g(:)h(:)f(:)g(:)g(:)g(:)h
+(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)h
+(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)25 b
+Fo(42)299 1474 y(7.9.8)44 b Fl(-q)11 b Fj(:)6 b(:)h(:)h(:)f(:)g(:)g(:)g(:)h
+(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g
+(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g
+(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)25 b Fo(42)299 1523 y(7.9.9)44
+b Fl(-v)11 b Fj(:)6 b(:)h(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g
+(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f
+(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h
+(:)25 b Fo(42)299 1573 y(7.9.10)43 b Fl(-D)15 b Fp(name[=defn])8
+b Fj(:)g(:)f(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g
+(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g
+(:)g(:)h(:)21 b Fo(42)299 1623 y(7.9.11)43 b Fl(-I)15 b Fp(directory)h
+Fj(:)7 b(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f
+(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h
+(:)f(:)g(:)g(:)g(:)h(:)26 b Fo(42)299 1673 y(7.9.12)43 b Fl(-U)15
+b Fp(name)c Fj(:)c(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)
+g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g
+(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)23 b Fo(43)149
+1723 y(7.10)45 b(Errors)14 b(pro)q(duced)i(b)o(y)f Fp(FSinfo)e
+Fj(:)8 b(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g
+(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f
+(:)25 b Fo(43)0 1829 y Fq(8)67 b(Examples)18 b Fa(:)10 b(:)g(:)g(:)g(:)h(:)f
+(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g
+(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)h(:)f(:)g(:)40
+b Fq(45)149 1891 y Fo(8.1)45 b(User)15 b(Filesystems)10 b Fj(:)e(:)g(:)f(:)g
+(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f
+(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h
+(:)f(:)g(:)g(:)g(:)h(:)f(:)25 b Fo(45)149 1941 y(8.2)45 b(Home)15
+b(Directories)5 b Fj(:)j(:)f(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g
+(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g
+(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)20
+b Fo(47)149 1991 y(8.3)45 b(Arc)o(hitecture)16 b(Sharing)10
+b Fj(:)e(:)f(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h
+(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g
+(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)25 b Fo(48)149 2041 y(8.4)45
+b(Wildcard)16 b(names)f(&)h(Replicated)h(Serv)o(ers)12 b Fj(:)7
+b(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g
+(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)27 b Fo(48)149 2091 y(8.5)45
+b(`)p Fl(rwho)p Fo(')14 b(serv)o(ers)d Fj(:)c(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g
+(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f
+(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h
+(:)f(:)g(:)g(:)g(:)27 b Fo(49)149 2140 y(8.6)45 b(`)p Fl(/vol)p
+Fo(')6 b Fj(:)g(:)h(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f
+(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h
+(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g
+(:)h(:)f(:)g(:)g(:)g(:)21 b Fo(49)0 2240 y Fq(9)67 b(In)n(ternals)15
+b Fa(:)c(:)f(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h
+(:)f(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f
+(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)38 b Fq(50)149 2302 y Fo(9.1)45
+b(Log)15 b(Messages)c Fj(:)c(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f
+(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h
+(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g
+(:)27 b Fo(50)299 2352 y(9.1.1)44 b(F)l(atal)15 b(errors)10
+b Fj(:)c(:)h(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g
+(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g
+(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)25 b Fo(50)299 2402 y(9.1.2)44
+b(Info)15 b(messages)10 b Fj(:)d(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g
+(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g
+(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)25 b Fo(52)0
+2508 y Fq(Ac)n(kno)n(wledgemen)n(ts)d(&)h(T)-6 b(rademarks)9
+b Fa(:)h(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g
+(:)g(:)h(:)31 b Fq(53)0 2630 y(Index)7 b Fa(:)12 b(:)e(:)g(:)g(:)g(:)h(:)f(:)
+g(:)g(:)g(:)h(:)f(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)h(:)f(:)g(:)g
+(:)g(:)h(:)f(:)g(:)g(:)h(:)f(:)g(:)g(:)g(:)h(:)f(:)g(:)g(:)h(:)f(:)g(:)g(:)g
+(:)h(:)f(:)g(:)g(:)h(:)f(:)30 b Fq(53)p eop
+%%Trailer
+end
+userdict /end-hook known{end-hook}if
+%%EOF
diff --git a/usr.sbin/amd/doc/amdref.texinfo b/usr.sbin/amd/doc/amdref.texinfo
new file mode 100644
index 0000000..c305e14
--- /dev/null
+++ b/usr.sbin/amd/doc/amdref.texinfo
@@ -0,0 +1,4557 @@
+\input texinfo @c -*-texinfo-*-
+@c
+@c Copyright (c) 1989 Jan-Simon Pendry
+@c Copyright (c) 1989 Imperial College of Science, Technology & Medicine
+@c Copyright (c) 1989 The Regents of the University of California.
+@c All rights reserved.
+@c
+@c This code is derived from software contributed to Berkeley by
+@c Jan-Simon Pendry at Imperial College, London.
+@c
+@c Redistribution and use in source and binary forms, with or without
+@c modification, are permitted provided that the following conditions
+@c are met:
+@c 1. Redistributions of source code must retain the above copyright
+@c notice, this list of conditions and the following disclaimer.
+@c 2. Redistributions in binary form must reproduce the above copyright
+@c notice, this list of conditions and the following disclaimer in the
+@c documentation and/or other materials provided with the distribution.
+@c 3. All advertising materials mentioning features or use of this software
+@c must display the following acknowledgement:
+@c This product includes software developed by the University of
+@c California, Berkeley and its contributors.
+@c 4. Neither the name of the University nor the names of its contributors
+@c may be used to endorse or promote products derived from this software
+@c without specific prior written permission.
+@c
+@c THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+@c ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+@c IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+@c ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+@c FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+@c DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+@c OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+@c HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+@c LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+@c OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+@c
+@c @(#)amdref.texinfo 8.1 (Berkeley) 6/6/93
+@c
+@c $Id: amdref.texinfo,v 1.4 1997/02/22 16:03:12 peter Exp $
+@c
+@setfilename amdref.info
+@c @setfilename /usr/local/emacs/info/amd
+@tex
+\overfullrule=0pt
+@end tex
+
+@settitle 4.4 BSD Automounter Reference Manual
+@titlepage
+@sp 6
+@center @titlefont{Amd}
+@sp 2
+@center @titlefont{The 4.4 BSD Automounter}
+@sp 2
+@center @titlefont{Reference Manual}
+@sp 2
+@center @authorfont{Jan-Simon Pendry}
+@sp
+@center @i{and}
+@sp
+@center @authorfont{Nick Williams}
+@sp 4
+@center Last updated March 1991
+@center Documentation for software revision 5.3 Alpha
+@page
+Copyright @copyright{} 1989 Jan-Simon Pendry
+@sp -1
+Copyright @copyright{} 1989 Imperial College of Science, Technology & Medicine
+@sp -1
+Copyright @copyright{} 1989 The Regents of the University of California.
+@sp 0
+All Rights Reserved.
+@vskip 1ex
+Permission to copy this document, or any portion of it, as
+necessary for use of this software is granted provided this
+copyright notice and statement of permission are included.
+@end titlepage
+@page
+@ifinfo
+@node Top, License, , (DIR)
+
+Amd - The 4.4 BSD Automounter
+*****************************
+
+Amd is the 4.4 BSD Automounter. This Info file describes how
+to use and understand Amd.
+@end ifinfo
+
+@menu
+* License:: Explains the terms and conditions for using
+ and distributing Amd.
+* Distrib:: How to get the latest Amd distribution.
+* Intro:: An introduction to Automounting concepts.
+* Overview:: An overview of Amd.
+* Supported Platforms:: Machines and Systems supported by Amd.
+* Mount Maps:: Details of mount maps
+* Amd Command Line Options:: All the Amd command line options explained.
+* Filesystem Types:: The different mount types supported by Amd.
+* Run-time Administration:: How to start, stop and control Amd.
+* FSinfo:: The FSinfo filesystem management tool.
+* Internals:: Internals.
+* Acknowledgements & Trademarks:: Legal notes.
+* Examples:: Some examples showing how Amd might be used.
+* Internals:: Implementation details.
+* Acknowledgements & Trademarks::
+
+Indexes
+* Index:: An item for each concept.
+@end menu
+
+@iftex
+@unnumbered Preface
+
+This manual documents the use of the 4.4 BSD automounter---@i{Amd}.
+This is primarily a reference manual. Unfortunately, no tutorial
+exists.
+
+This manual comes in two forms: the published form and the Info form.
+The Info form is for on-line perusal with the INFO program which is
+distributed along with GNU Emacs. Both forms contain substantially the
+same text and are generated from a common source file, which is
+distributed with the @i{Amd} source.
+@end iftex
+
+@node License, Distrib, Top, Top
+@unnumbered License
+@cindex License Information
+
+@i{Amd} is not in the public domain; it is copyrighted and there are
+restrictions on its distribution.
+
+Redistribution and use in source and binary forms are permitted provided
+that: (1) source distributions retain this entire copyright notice and
+comment, and (2) distributions including binaries display the following
+acknowledgement: ``This product includes software developed by The
+University of California, Berkeley and its Contributors'' in the
+documentation or other materials provided with the distribution and in
+all advertising materials mentioning features or use of this software.
+neither the name of the University nor the names 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.
+
+@node Distrib, Intro, License, Top
+@unnumbered Source Distribution
+@cindex Source code distribution
+@cindex Obtaining the source code
+
+If you have access to the Internet, you can get the latest distribution
+version of @i{Amd} from host @file{usc.edu} using anonymous FTP. Move to
+the directory @file{/pub/amd} on that host and fetch the file @file{amd.tar.Z}.
+
+If you are in the UK, you can get the latest distribution version of
+@i{Amd} from the UKnet info-server. Start by sending email to
+@file{info-server@@doc.ic.ac.uk}.
+
+Sites on the UK JANET network can get the latest distribution by using
+anonymous NIFTP to fetch the file @samp{<AMD>amd.tar.Z} from host
+@samp{uk.ac.imperial.doc.src}.
+
+Revision 5.2 was part of the 4.3 BSD Reno distribution.
+
+Revision 5.3bsdnet, a late alpha version of 5.3, was part
+of the BSD network version 2 distribution
+
+@unnumberedsec Bug Reports
+@cindex Bug reports
+
+Send all bug reports to @file{jsp@@doc.ic.ac.uk} quoting the details of
+the release and your configuration. These can be obtained by running
+the command @samp{amd -v}.
+
+@unnumberedsec Mailing List
+@cindex Mailing list
+
+There is a mailing list for people interested in keeping uptodate with
+developments. To subscribe, send a note to @file{amd-workers-request@@acl.lanl.gov}.
+
+@node Intro, Overview, Distrib, Top
+@unnumbered Introduction
+@cindex Introduction
+
+An @dfn{automounter} maintains a cache of mounted filesystems.
+Filesystems are mounted on demand when they are first referenced,
+and unmounted after a period of inactivity.
+
+@i{Amd} may be used as a replacement for Sun's automounter. The choice
+of which filesystem to mount can be controlled dynamically with
+@dfn{selectors}. Selectors allow decisions of the form ``hostname is
+@var{this},'' or ``architecture is not @var{that}.'' Selectors may be
+combined arbitrarily. @i{Amd} also supports a variety of filesystem
+types, including NFS, UFS and the novel @dfn{program} filesystem. The
+combination of selectors and multiple filesystem types allows identical
+configuration files to be used on all machines so reducing the
+administrative overhead.
+
+@i{Amd} ensures that it will not hang if a remote server goes down.
+Moreover, @i{Amd} can determine when a remote server has become
+inaccessible and then mount replacement filesystems as and when they
+become available.
+
+@i{Amd} contains no proprietary source code and has been ported to
+numerous flavours of Unix.
+
+@node Overview, Supported Platforms, Intro, Top
+@chapter Overview
+
+@i{Amd} maintains a cache of mounted filesystems. Filesystems are
+@dfn{demand-mounted} when they are first referenced, and unmounted after
+a period of inactivity. @i{Amd} may be used as a replacement for Sun's
+@b{automount}(8) program. It contains no proprietary source code and
+has been ported to numerous flavours of Unix. @xref{Supported Operating
+Systems}.@refill
+
+@i{Amd} was designed as the basis for experimenting with filesystem
+layout and management. Although @i{Amd} has many direct applications it
+is loaded with additional features which have little practical use. At
+some point the infrequently used components may be removed to streamline
+the production system.
+
+@c @i{Amd} supports the notion of @dfn{replicated} filesystems by evaluating
+@c each member of a list of possible filesystem locations in parallel.
+@c @i{Amd} checks that each cached mapping remains valid. Should a mapping be
+@c lost -- such as happens when a fileserver goes down -- @i{Amd} automatically
+@c selects a replacement should one be available.
+@c
+@menu
+* Fundamentals::
+* Filesystems and Volumes::
+* Volume Naming::
+* Volume Binding::
+* Operational Principles::
+* Mounting a Volume::
+* Automatic Unmounting::
+* Keep-alives::
+* Non-blocking Operation::
+@end menu
+
+@node Fundamentals, Filesystems and Volumes, Overview, Overview
+@comment node-name, next, previous, up
+@section Fundamentals
+@cindex Automounter fundamentals
+
+The fundamental concept behind @i{Amd} is the ability to separate the
+name used to refer to a file from the name used to refer to its physical
+storage location. This allows the same files to be accessed with the
+same name regardless of where in the network the name is used. This is
+very different from placing @file{/n/hostname} in front of the pathname
+since that includes location dependent information which may change if
+files are moved to another machine.
+
+By placing the required mappings in a centrally administered database,
+filesystems can be re-organised without requiring changes to
+configuration files, shell scripts and so on.
+
+@node Filesystems and Volumes, Volume Naming, Fundamentals, Overview
+@comment node-name, next, previous, up
+@section Filesystems and Volumes
+@cindex Filesystem
+@cindex Volume
+@cindex Fileserver
+@cindex sublink
+
+@i{Amd} views the world as a set of fileservers, each containg one or
+more filesystems where each filesystem contains one or more
+@dfn{volumes}. Here the term @dfn{volume} is used to refer to a
+coherent set of files such as a user's home directory or a @TeX{}
+distribution.@refill
+
+In order to access the contents of a volume, @i{Amd} must be told in
+which filesystem the volume resides and which host owns the filesystem.
+By default the host is assumed to be local and the volume is assumed to
+be the entire filesystem. If a filesystem contains more than one
+volume, then a @dfn{sublink} is used to refer to the sub-directory
+within the filesystem where the volume can be found.
+
+@node Volume Naming, Volume Binding, Filesystems and Volumes, Overview
+@comment node-name, next, previous, up
+@section Volume Naming
+@cindex Volume names
+@cindex Network-wide naming
+@cindex Replicated volumes
+@cindex Duplicated volumes
+@cindex Replacement volumes
+
+Volume names are defined to be unique across the entire network. A
+volume name is the pathname to the volume's root as known by the users
+of that volume. Since this name uniquely identifies the volume
+contents, all volumes can be named and accessed from each host, subject
+to administrative controls.
+
+Volumes may be replicated or duplicated. Replicated volumes contain
+identical copies of the same data and reside at two or more locations in
+the network. Each of the replicated volumes can be used
+interchangeably. Duplicated volumes each have the same name but contain
+different, though functionally identical, data. For example,
+@samp{/vol/tex} might be the name of a @TeX{} distribution which varied
+for each machine architecture.@refill
+
+@i{Amd} provides facilities to take advantage of both replicated and
+duplicated volumes. Configuration options allow a single set of
+configuration data to be shared across an entire network by taking
+advantage of replicated and duplicated volumes.
+
+@i{Amd} can take advantage of replacement volumes by mounting them as
+required should an active fileserver become unavailable.
+
+@node Volume Binding, Operational Principles, Volume Naming, Overview
+@comment node-name, next, previous, up
+@section Volume Binding
+@cindex Volume binding
+@cindex Unix namespace
+@cindex Namespace
+@cindex Binding names to filesystems
+
+Unix implements a namespace of hierarchically mounted filesystems. Two
+forms of binding between names and files are provided. A @dfn{hard
+link} completes the binding when the name is added to the filesystem. A
+@dfn{soft link} delays the binding until the name is accessed. An
+@dfn{automounter} adds a further form in which the binding of name to
+filesystem is delayed until the name is accessed.@refill
+
+The target volume, in its general form, is a tuple (host, filesystem,
+sublink) which can be used to name the physical location of any volume
+in the network.
+
+When a target is referenced, @i{Amd} ignores the sublink element and
+determines whether the required filesystem is already mounted. This is
+done by computing the local mount point for the filesystem and checking
+for an existing filesystem mounted at the same place. If such a
+filesystem already exists then it is assumed to be functionally
+identical to the target filesystem. By default there is a one-to-one
+mapping between the pair (host, filesystem) and the local mount point so
+this assumption is valid.
+
+@node Operational Principles, Mounting a Volume, Volume Binding, Overview
+@comment node-name, next, previous, up
+@section Operational Principles
+@cindex Operational principles
+
+@i{Amd} operates by introducing new mount points into the namespace.
+These are called @dfn{automount} points. The kernel sees these
+automount points as NFS filesystems being served by @i{Amd}. Having
+attached itself to the namespace, @i{Amd} is now able to control the
+view the rest of the system has of those mount points. RPC calls are
+received from the kernel one at a time.
+
+When a @dfn{lookup} call is received @i{Amd} checks whether the name is
+already known. If it is not, the required volume is mounted. A
+symbolic link pointing to the volume root is then returned. Once the
+symbolic link is returned, the kernel will send all other requests
+direct to the mounted filesystem.
+
+If a volume is not yet mounted, @i{Amd} consults a configuration
+@dfn{mount-map} corresponding to the automount point. @i{Amd} then
+makes a runtime decision on what and where to mount a filesystem based
+on the information obtained from the map.
+
+@i{Amd} does not implement all the NFS requests; only those relevant
+to name binding such as @dfn{lookup}, @dfn{readlink} and @dfn{readdir}.
+Some other calls are also implemented but most simply return an error
+code; for example @dfn{mkdir} always returns ``read-only filesystem''.
+
+@node Mounting a Volume, Automatic Unmounting, Operational Principles, Overview
+@comment node-name, next, previous, up
+@section Mounting a Volume
+@cindex Mounting a volume
+@cindex Location lists
+@cindex Alternate locations
+@cindex Mount retries
+@cindex Background mounts
+
+Each automount point has a corresponding mount map. The mount map
+contains a list of key--value pairs. The key is the name of the volume
+to be mounted. The value is a list of locations describing where the
+filesystem is stored in the network. In the source for the map the
+value would look like
+
+@display
+location1 location2 @dots{} locationN
+@end display
+
+@i{Amd} examines each location in turn. Each location may contain
+@dfn{selectors} which control whether @i{Amd} can use that location.
+For example, the location may be restricted to use by certain hosts.
+Those locations which cannot be used are ignored.
+
+@i{Amd} attempts to mount the filesystem described by each remaining
+location until a mount succeeds or @i{Amd} can no longer proceed. The
+latter can occur in three ways:
+
+@itemize @bullet
+@item
+If none of the locations could be used, or if all of the locations
+caused an error, then the last error is returned.
+
+@item
+If a location could be used but was being mounted in the background then
+@i{Amd} marks that mount as being ``in progress'' and continues with
+the next request; no reply is sent to the kernel.
+
+@item
+Lastly, one or more of the mounts may have been @dfn{deferred}. A mount
+is deferred if extra information is required before the mount can
+proceed. When the information becomes available the mount will take
+place, but in the mean time no reply is sent to the kernel. If the
+mount is deferred, @i{Amd} continues to try any remaining locations.
+@end itemize
+
+Once a volume has been mounted, @i{Amd} establishes a @dfn{volume
+mapping} which is used to satisfy subsequent requests.@refill
+
+@node Automatic Unmounting, Keep-alives, Mounting a Volume, Overview
+@comment node-name, next, previous, up
+@section Automatic Unmounting
+
+To avoid an ever increasing number of filesystem mounts, @i{Amd} removes
+volume mappings which have not been used recently. A time-to-live
+interval is associated with each mapping and when that expires the
+mapping is removed. When the last reference to a filesystem is removed,
+that filesystem is unmounted. If the unmount fails, for example the
+filesystem is still busy, the mapping is re-instated and its
+time-to-live interval is extended. The global default for this grace
+period is controlled by the ``-w'' command-line option (@pxref{-w
+Option, -w}). It is also possible to set this value on a per-mount
+basis (@pxref{opts Option, opts, opts}).@refill
+
+Filesystems can be forcefully timed out using the @i{Amq} command.
+@xref{Run-time Administration}.
+
+@node Keep-alives, Non-blocking Operation, Automatic Unmounting, Overview
+@comment node-name, next, previous, up
+@section Keep-alives
+@cindex Keep-alives
+@cindex Server crashes
+@cindex NFS ping
+
+Use of some filesystem types requires the presence of a server on
+another machine. If a machine crashes then it is of no concern to
+processes on that machine that the filesystem is unavailable. However,
+to processes on a remote host using that machine as a fileserver this
+event is important. This situation is most widely recognised when an
+NFS server crashes and the behaviour observed on client machines is that
+more and more processes hang. In order to provide the possibility of
+recovery, @i{Amd} implements a @dfn{keep-alive} interval timer for some
+filesystem types. Currently only NFS makes use of this service.
+
+The basis of the NFS keep-alive implementation is the observation that
+most sites maintain replicated copies of common system data such as
+manual pages, most or all programs, system source code and so on. If
+one of those servers goes down it would be reasonable to mount one of
+the others as a replacement.
+
+The first part of the process is to keep track of which fileservers are
+up and which are down. @i{Amd} does this by sending RPC requests to the
+servers' NFS @code{NullProc} and checking whether a reply is returned.
+While the server state is uncertain the requests are re-transmitted at
+three second intervals and if no reply is received after four attempts
+the server is marked down. If a reply is received the fileserver is
+marked up and stays in that state for 30 seconds at which time another
+NFS ping is sent.
+
+Once a fileserver is marked down, requests continue to be sent every 30
+seconds in order to determine when the fileserver comes back up. During
+this time any reference through @i{Amd} to the filesystems on that
+server fail with the error ``Operation would block''. If a replacement
+volume is available then it will be mounted, otherwise the error is
+returned to the user.
+
+@c @i{Amd} keeps track of which servers are up and which are down.
+@c It does this by sending RPC requests to the servers' NFS {\sc NullProc} and
+@c checking whether a reply is returned. If no replies are received after a
+@c short period, @i{Amd} marks the fileserver @dfn{down}.
+@c RPC requests continue to be sent so that it will notice when a fileserver
+@c comes back up.
+@c ICMP echo packets \cite{rfc:icmp} are not used because it is the availability
+@c of the NFS service that is important, not the existence of a base kernel.
+@c Whenever a reference to a fileserver which is down is made via @i{Amd}, an alternate
+@c filesystem is mounted if one is available.
+@c
+Although this action does not protect user files, which are unique on
+the network, or processes which do not access files via @i{Amd} or
+already have open files on the hung filesystem, it can prevent most new
+processes from hanging.
+
+By default, fileserver state is not maintained for NFS/TCP mounts. The
+remote fileserver is always assumed to be up.
+@c
+@c With a suitable combination of filesystem management and mount-maps,
+@c machines can be protected against most server downtime. This can be
+@c enhanced by allocating boot-servers dynamically which allows a diskless
+@c workstation to be quickly restarted if necessary. Once the root filesystem
+@c is mounted, @i{Amd} can be started and allowed to mount the remainder of
+@c the filesystem from whichever fileservers are available.
+
+@node Non-blocking Operation, , Keep-alives, Overview
+@comment node-name, next, previous, up
+@section Non-blocking Operation
+@cindex Non-blocking operation
+@cindex Multiple-threaded server
+@cindex RPC retries
+
+Since there is only one instance of @i{Amd} for each automount point,
+and usually only one instance on each machine, it is important that it
+is always available to service kernel calls. @i{Amd} goes to great
+lengths to ensure that it does not block in a system call. As a last
+resort @i{Amd} will fork before it attempts a system call that may block
+indefinitely, such as mounting an NFS filesystem. Other tasks such as
+obtaining filehandle information for an NFS filesystem, are done using a
+purpose built non-blocking RPC library which is integrated with
+@i{Amd}'s task scheduler. This library is also used to implement NFS
+keep-alives (@pxref{Keep-alives}).
+
+Whenever a mount is deferred or backgrounded, @i{Amd} must wait for it
+to complete before replying to the kernel. However, this would cause
+@i{Amd} to block waiting for a reply to be constructed. Rather than do
+this, @i{Amd} simply @dfn{drops} the call under the assumption that the
+kernel RPC mechanism will automatically retry the request.
+
+@node Supported Platforms, Mount Maps, Overview, Top
+@comment node-name, next, previous, up
+@chapter Supported Platforms
+
+@i{Amd} has been ported to a wide variety of machines and operating systems.
+The table below lists those platforms supported by the current release.
+
+@menu
+* Supported Operating Systems::
+* Supported Machine Architectures::
+@end menu
+
+@node Supported Operating Systems, Supported Machine Architectures, Supported Platforms, Supported Platforms
+@comment node-name, next, previous, up
+@section Supported Operating Systems
+@cindex Operating system names
+@cindex Operating systems supported by Amd
+@cindex Supported operating systems
+
+The following operating systems are currently supported by @i{Amd}.
+@i{Amd}'s conventional name for each system is given.
+
+@table @code
+@item acis43
+4.3 BSD for IBM RT. Contributed by Jan-Simon Pendry @t{<jsp@@doc.ic.ac.uk>}
+@item aix3
+AIX 3.1. Contributed by Jan-Simon Pendry @t{<jsp@@doc.ic.ac.uk>}
+@item aux
+System V for Mac-II. Contributed by Julian Onions @t{<jpo@@cs.nott.ac.uk>}
+@item bsd44
+4.4 BSD. Contributed by Jan-Simon Pendry @t{<jsp@@doc.ic.ac.uk>}
+@item concentrix
+Concentrix 5.0. Contributed by Sjoerd Mullender @t{<sjoerd@@cwi.nl>}
+@item convex
+Convex OS 7.1. Contributed by Eitan Mizrotsky @t{<eitan@@shumuji.ac.il>}
+@item dgux
+Data General DG/UX. Contributed by Mark Davies @t{<mark@@comp.vuw.ac.nz>}
+@item fpx4
+Celerity FPX 4.1/2. Contributed by Stephen Pope @t{<scp@@grizzly.acl.lanl.gov>}
+@item hcx
+Harris HCX/UX. Contributed by Chris Metcalf @t{<metcalf@@masala.lcs.mit.edu>}
+@item hlh42
+HLH OTS 1.@i{x} (4.2 BSD). Contributed by Jan-Simon Pendry @t{<jsp@@doc.ic.ac.uk>}
+@item hpux
+HP-UX 6.@i{x} or 7.0. Contributed by Jan-Simon Pendry @t{<jsp@@doc.ic.ac.uk>}
+@item irix
+SGI Irix. Contributed by Scott R. Presnell @t{<srp@@cgl.ucsf.edu>}
+@item next
+Mach for NeXT. Contributed by Bill Trost @t{<trost%reed@@cse.ogi.edu>}
+@item pyrOSx
+Pyramid OSx. Contributed by Stefan Petri @t{<petri@@tubsibr.UUCP>}
+@item riscix
+Acorn RISC iX. Contributed by Piete Brooks @t{<pb@@cam.cl.ac.uk>}
+@item sos3
+SunOS 3.4 & 3.5. Contributed by Jan-Simon Pendry @t{<jsp@@doc.ic.ac.uk>}
+@item sos4
+SunOS 4.@i{x}. Contributed by Jan-Simon Pendry @t{<jsp@@doc.ic.ac.uk>}
+@item u2_2
+Ultrix 2.2. Contributed by Piete Brooks @t{<pb@@cam.cl.ac.uk>}
+@item u3_0
+Ultrix 3. Contributed by Piete Brooks @t{<pb@@cam.cl.ac.uk>}
+@item u4_0
+Ultrix 4.0. Contributed by Chris Lindblad @t{<cjl@@ai.mit.edu>}
+@item umax43
+Umax 4.3 BSD. Contributed by Sjoerd Mullender @t{<sjoerd@@cwi.nl>}
+@item utek
+Utek 4.0. Contributed by Bill Trost @t{<trost%reed@@cse.ogi.edu>}
+@item xinu43
+mt Xinu MORE/bsd. Contributed by Jan-Simon Pendry @t{<jsp@@doc.ic.ac.uk>}
+@end table
+
+@node Supported Machine Architectures, , Supported Operating Systems, Supported Platforms
+@comment node-name, next, previous, up
+@section Supported Machine Architectures
+@cindex Supported machine architectures
+@cindex Machine architecture names
+@cindex Machine architectures supported by Amd
+
+@table @code
+@item alliant
+Alliant FX/4
+@item arm
+Acorn ARM
+@item aviion
+Data General AViiON
+@item encore
+Encore
+@item fps500
+FPS Model 500
+@item hp9000
+HP 9000/300 family
+@item hp9k8
+HP 9000/800 family
+@item ibm032
+IBM RT
+@item ibm6000
+IBM RISC System/6000
+@item iris4d
+SGI Iris 4D
+@item macII
+Apple Mac II
+@item mips
+MIPS RISC
+@item multimax
+Encore Multimax
+@item orion105
+HLH Orion 1/05
+@item sun3
+Sun-3 family
+@item sun4
+Sun-4 family
+@item tahoe
+Tahoe family
+@item vax
+DEC Vax
+@end table
+
+@node Mount Maps, Amd Command Line Options, Supported Platforms, Top
+@comment node-name, next, previous, up
+@chapter Mount Maps
+@cindex Mount maps
+@cindex Automounter configuration maps
+@cindex Mount information
+
+@i{Amd} has no built-in knowledge of machines or filesystems.
+External @dfn{mount-maps} are used to provide the required information.
+Specifically, @i{Amd} needs to know when and under what conditions it
+should mount filesystems.
+
+The map entry corresponding to the requested name contains a list of
+possible locations from which to resolve the request. Each location
+specifies filesystem type, information required by that filesystem (for
+example the block special device in the case of UFS), and some
+information describing where to mount the filesystem (@pxref{fs Option}). A
+location may also contain @dfn{selectors} (@pxref{Selectors}).@refill
+
+@menu
+* Map Types::
+* Key Lookup::
+* Location Format::
+@end menu
+
+@node Map Types, Key Lookup, Mount Maps, Mount Maps
+@comment node-name, next, previous, up
+@section Map Types
+@cindex Mount map types
+@cindex Map types
+@cindex Configuration map types
+@cindex Types of mount map
+@cindex Types of configuration map
+@cindex Determining the map type
+
+A mount-map provides the run-time configuration information to @i{Amd}.
+Maps can be implemented in many ways. Some of the forms supported by
+@i{Amd} are regular files, ndbm databases, NIS maps the @dfn{Hesiod}
+name server and even the password file.
+
+A mount-map @dfn{name} is a sequence of characters. When an automount
+point is created a handle on the mount-map is obtained. For each map
+type configured @i{Amd} attempts to reference the a map of the
+appropriate type. If a map is found, @i{Amd} notes the type for future
+use and deletes the reference, for example closing any open file
+descriptors. The available maps are configure when @i{Amd} is built and
+can be displayed by running the command @samp{amd -v}.
+
+By default, @i{Amd} caches data in a mode dependent on the type of map.
+This is the same as specifying @samp{cache:=mapdefault} and selects a
+suitable default cache mode depending on the map type. The individual
+defaults are described below. The @var{cache} option can be specified
+on automount points to alter the caching behaviour (@pxref{Automount
+Filesystem}).@refill
+
+The following map types have been implemented, though some are not
+available on all machines. Run the command @samp{amd -v} to obtain a
+list of map types configured on your machine.
+
+@menu
+* File maps::
+* ndbm maps::
+* NIS maps::
+* Hesiod maps::
+* Password maps::
+* Union maps::
+@end menu
+
+@node File maps, ndbm maps, Map Types, Map Types
+@comment node-name, next, previous, up
+@subsection File maps
+@cindex File maps
+@cindex Flat file maps
+@cindex File map syntactic conventions
+
+When @i{Amd} searches a file for a map entry it does a simple scan of
+the file and supports both comments and continuation lines.
+
+Continuation lines are indicated by a backslash character (@samp{\}) as
+the last character of a line in the file. The backslash, newline character
+@emph{and any leading white space on the following line} are discarded. A maximum
+line length of 2047 characters is enforced after continuation lines are read
+but before comments are stripped. Each line must end with
+a newline character; that is newlines are terminators, not separators.
+The following examples illustrate this:
+
+@example
+key valA valB; \
+ valC
+@end example
+
+specifies @emph{three} locations, and is identical to
+
+@example
+key valA valB; valC
+@end example
+
+However,
+
+@example
+key valA valB;\
+ valC
+@end example
+
+specifies only @emph{two} locations, and is identical to
+
+@example
+key valA valB;valC
+@end example
+
+After a complete line has been read from the file, including
+continuations, @i{Amd} determines whether there is a comment on the
+line. A comment begins with a hash (``@samp{#}'') character and
+continues to the end of the line. There is no way to escape or change
+the comment lead-in character.
+
+Note that continuation lines and comment support @dfn{only} apply to
+file maps, or ndbm maps built with the @code{mk-amd-map} program.
+
+When caching is enabled, file maps have a default cache mode of
+@code{all} (@pxref{Automount Filesystem}).
+
+@node ndbm maps, NIS maps, File maps, Map Types
+@comment node-name, next, previous, up
+@subsection ndbm maps
+@cindex ndbm maps
+
+An ndbm map may be used as a fast access form of a file map. The program,
+@code{mk-amd-map}, converts a normal map file into an ndbm database.
+This program supports the same continuation and comment conventions that
+are provided for file maps. Note that ndbm format files may @emph{not}
+be sharable across machine architectures. The notion of speed generally
+only applies to large maps; a small map, less than a single disk block,
+is almost certainly better implemented as a file map.
+
+ndbm maps do not support cache mode @samp{all} and, when caching is
+enabled, have a default cache mode of @samp{inc} (@pxref{Automount Filesystem}).
+
+@node NIS maps, Hesiod maps, ndbm maps, Map Types
+@comment node-name, next, previous, up
+@subsection NIS maps
+@cindex NIS (YP) maps
+
+When using NIS (formerly YP), an @i{Amd} map is implemented directly
+by the underlying NIS map. Comments and continuation lines are
+@emph{not} supported in the automounter and must be stripped when
+constructing the NIS server's database.
+
+NIS maps do not support cache mode @code{all} and, when caching is
+enabled, have a default cache mode of @code{inc} (@pxref{Automount Filesystem}).
+
+The following rule illustrates what could be added to your NIS @file{Makefile},
+in this case causing the @file{amd.home} map to be rebuilt:
+@example
+$(YPTSDIR)/amd.home.time: $(ETCDIR)/amd.home
+ -@@sed -e "s/#.*$$//" -e "/^$$/d" $(ETCDIR)/amd.home | \
+ awk '@{ \
+ 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); \
+ @}' | \
+ $(MAKEDBM) - $(YPDBDIR)/amd.home; \
+ touch $(YPTSDIR)/amd.home.time; \
+ echo "updated amd.home"; \
+ if [ ! $(NOPUSH) ]; then \
+ $(YPPUSH) amd.home; \
+ echo "pushed amd.home"; \
+ else \
+ : ; \
+ fi
+@end example
+
+Here @code{$(YPTSDIR)} contains the time stamp files, and @code{$(YPDBDIR)} contains
+the dbm format NIS files.
+
+@node Hesiod maps, Password maps, NIS maps, Map Types
+@comment node-name, next, previous, up
+@subsection Hesiod maps
+@cindex Hesiod maps
+
+When the map name begins with the string @samp{hesiod.} lookups are made
+using the @dfn{Hesiod} name server. The string following the dot is
+used as a name qualifier and is prepended with the key being located.
+The entire string is then resolved in the @code{automount} context. For
+example, if the the key is @samp{jsp} and map name is
+@samp{hesiod.homes} then @dfn{Hesiod} is asked to resolve
+@samp{jsp.homes.automount}.
+
+Hesiod maps do not support cache mode @samp{all} and, when caching is
+enabled, have a default cache mode of @samp{inc} (@pxref{Automount Filesystem}).
+
+The following is an example of a @dfn{Hesiod} map entry:
+
+@example
+jsp.homes.automount HS TXT "rfs:=/home/charm;rhost:=charm;sublink:=jsp"
+njw.homes.automount HS TXT "rfs:=/home/dylan/dk2;rhost:=dylan;sublink:=njw"
+@end example
+
+@node Password maps, Union maps, Hesiod maps, Map Types
+@comment node-name, next, previous, up
+@subsection Password maps
+@cindex Password file maps
+@cindex /etc/passwd maps
+@cindex User maps, automatic generation
+@cindex Automatic generation of user maps
+@cindex Using the password file as a map
+
+The password map support is unlike the four previous map types. When
+the map name is the string @file{/etc/passwd} @i{Amd} can lookup a user
+name in the password file and re-arrange the home directory field to
+produce a usable map entry.
+
+@i{Amd} assumes the home directory has the format
+`@t{/}@i{anydir}@t{/}@i{dom1}@t{/../}@i{domN}@t{/}@i{login}'.
+@c @footnote{This interpretation is not necessarily exactly what you want.}
+It breaks this string into a map entry where @code{$@{rfs@}} has the
+value `@t{/}@i{anydir}@t{/}@i{domN}', @code{$@{rhost@}} has the value
+`@i{domN}@t{.}@i{...}@t{.}@i{dom1}', and @code{$@{sublink@}} has the
+value @samp{login}.@refill
+
+Thus if the password file entry was
+
+@example
+/home/achilles/jsp
+@end example
+
+the map entry used by @i{Amd} would be
+
+@example
+rfs:=/home/achilles;rhost:=achilles;sublink:=jsp
+@end example
+
+Similarly, if the password file entry was
+
+@example
+/home/cc/sugar/mjh
+@end example
+
+the map entry used by @i{Amd} would be
+
+@example
+rfs:=/home/sugar;rhost:=sugar.cc;sublink:=jsp
+@end example
+
+@node Union maps, , Password maps, Map Types
+@comment node-name, next, previous, up
+@subsection Union maps
+@cindex Union file maps
+
+The union map support is provided specifically for use with the union
+filesystem, @pxref{Union Filesystem}.
+
+It is identified by the string @samp{union:} which is followed by a
+colon separated list of directories. The directories are read in order,
+and the names of all entries are recorded in the map cache. Later
+directories take precedence over earlier ones. The union filesystem
+type then uses the map cache to determine the union of the names in all
+the directories.
+
+@c subsection Gdbm
+
+@node Key Lookup, Location Format, Map Types, Mount Maps
+@comment node-name, next, previous, up
+@section How keys are looked up
+@cindex Key lookup
+@cindex Map lookup
+@cindex Looking up keys
+@cindex How keys are looked up
+@cindex Wildcards in maps
+
+The key is located in the map whose type was determined when the
+automount point was first created. In general the key is a pathname
+component. In some circumstances this may be modified by variable
+expansion (@pxref{Variable Expansion}) and prefixing. If the automount
+point has a prefix, specified by the @var{pref} option, then that is
+prepended to the search key before the map is searched.
+
+If the map cache is a @samp{regexp} cache then the key is treated as an
+egrep-style regular expression, otherwise a normal string comparison is
+made.
+
+If the key cannot be found then a @dfn{wildcard} match is attempted.
+@i{Amd} repeatedly strips the basename from the key, appends @samp{/*} and
+attempts a lookup. Finally, @i{Amd} attempts to locate the special key @samp{*}.
+
+@group
+For example, the following sequence would be checked if @file{home/dylan/dk2} was
+being located:
+
+@example
+ home/dylan/dk2
+ home/dylan/*
+ home/*
+ *
+@end example
+@end group
+
+At any point when a wildcard is found, @i{Amd} proceeds as if an exact
+match had been found and the value field is then used to resolve the
+mount request, otherwise an error code is propagated back to the kernel.
+(@pxref{Filesystem Types}).@refill
+
+@node Location Format, , Key Lookup, Mount Maps
+@comment node-name, next, previous, up
+@section Location Format
+@cindex Location format
+@cindex Map entry format
+@cindex How locations are parsed
+
+The value field from the lookup provides the information required to
+mount a filesystem. The information is parsed according to the syntax
+shown below.
+
+@display
+@i{location-list}:
+ @i{location-selection}
+ @i{location-list} @i{white-space} @t{||} @i{white-space} @i{location-selection}
+@i{location-selection}:
+ @i{location}
+ @i{location-selection} @i{white-space} @i{location}
+@i{location}:
+ @i{location-info}
+ @t{-}@i{location-info}
+ @t{-}
+@i{location-info}:
+ @i{sel-or-opt}
+ @i{location-info}@t{;}@i{sel-or-opt}
+ @t{;}
+@i{sel-or-opt}:
+ @i{selection}
+ @i{opt-ass}
+@i{selection}:
+ selector@t{==}@i{value}
+ selector@t{!=}@i{value}
+@i{opt-ass}:
+ option@t{:=}@i{value}
+@i{white-space}:
+ space
+ tab
+@end display
+
+Note that unquoted whitespace is not allowed in a location description.
+White space is only allowed, and is mandatory, where shown with non-terminal
+@samp{white-space}.
+
+A @dfn{location-selection} is a list of possible volumes with which to
+satisfy the request. @dfn{location-selection}s are separated by the
+@samp{||} operator. The effect of this operator is to prevent use of
+location-selections to its right if any of the location-selections on
+its left were selected whether or not any of them were successfully
+mounted (@pxref{Selectors}).@refill
+
+The location-selection, and singleton @dfn{location-list},
+@samp{type:=ufs;dev:=/dev/xd1g} would inform @i{Amd} to mount a UFS
+filesystem from the block special device @file{/dev/xd1g}.
+
+The @dfn{sel-or-opt} component is either the name of an option required
+by a specific filesystem, or it is the name of a built-in, predefined
+selector such as the architecture type. The value may be quoted with
+double quotes @samp{"}, for example
+@samp{type:="ufs";dev:="/dev/xd1g"}. These quotes are stripped when the
+value is parsed and there is no way to get a double quote into a value
+field. Double quotes are used to get white space into a value field,
+which is needed for the program filesystem (@pxref{Program Filesystem}).@refill
+
+@menu
+* Map Defaults::
+* Variable Expansion::
+* Selectors::
+* Map Options::
+@end menu
+
+@node Map Defaults, Variable Expansion, Location Format, Location Format
+@comment node-name, next, previous, up
+@subsection Map Defaults
+@cindex Map defaults
+@cindex How to set default map parameters
+@cindex Setting default map parameters
+
+A location beginning with a dash @samp{-} is used to specify default
+values for subsequent locations. Any previously specified defaults in
+the location-list are discarded. The default string can be empty in
+which case no defaults apply.
+
+The location @samp{-fs:=/mnt;opts:=ro} would set the local mount point
+to @file{/mnt} and cause mounts to be read-only by default. Defaults
+specified this way are appended to, and so override, any global map
+defaults given with @samp{/defaults}).
+@c
+@c A @samp{/defaults} value @dfn{gdef} and a location list
+@c \begin{quote}
+@c $@samp{-}@dfn{def}_a $\verb*+ +$ @dfn{loc}_{a_1} $\verb*+ +$ @dfn{loc}_{a_2} $\verb*+ +$ @samp{-}@dfn{def}_b $\verb*+ +$ @dfn{loc}_{b_1} \ldots$
+@c \end{quote}
+@c is equivalent to
+@c \begin{quote}
+@c $@samp{-}@dfn{gdef}@samp{;}@dfn{def}_a $\verb*+ +$ @dfn{loc}_{a_1} $\verb*+ +$ @dfn{loc}_{a_2} $\verb*+ +$ @samp{-}@dfn{gdef}@samp{;}@dfn{def}_b $\verb*+ +$ @dfn{loc}_{b_1} \ldots$
+@c \end{quote}
+@c which is equivalent to
+@c \begin{quote}
+@c $@dfn{gdef}@samp{;}@dfn{def}_a@samp{;}@dfn{loc}_{a_1} $\verb*+ +$@dfn{gdef}@samp{;}@dfn{def}_a@samp{;}@dfn{loc}_{a_2} $\verb*+ +$@dfn{gdef}@samp{;}@dfn{def}_b@samp{;}@dfn{loc}_{b_1} \ldots$
+@c \end{quote}
+
+@node Variable Expansion, Selectors, Map Defaults, Location Format
+@comment node-name, next, previous, up
+@subsection Variable Expansion
+@cindex Variable expansion
+@cindex How variables are expanded
+@cindex Pathname operators
+@cindex Domain stripping
+@cindex Domainname operators
+@cindex Stripping the local domain name
+@cindex Environment variables
+@cindex How to access environment variables in maps
+
+To allow generic location specifications @i{Amd} does variable expansion
+on each location and also on some of the option strings. Any option or
+selector appearing in the form @code{$@dfn{var}} is replaced by the
+current value of that option or selector. For example, if the value of
+@code{$@{key@}} was @samp{bin}, @code{$@{autodir@}} was @samp{/a} and
+@code{$@{fs@}} was `@t{$@{autodir@}}@t{/local/}@t{$@{key@}}' then
+after expansion @code{$@{fs@}} would have the value @samp{/a/local/bin}.
+Any environment variable can be accessed in a similar way.@refill
+
+Two pathname operators are available when expanding a variable. If the
+variable name begins with @samp{/} then only the last component of
+then pathname is substituted. For example, if @code{$@{path@}} was
+@samp{/foo/bar} then @code{$@{/path@}} would be expanded to @samp{bar}.
+Similarly, if the variable name ends with @samp{/} then all but the
+last component of the pathname is substituted. In the previous example,
+@code{$@{path/@}} would be expanded to @samp{/foo}.@refill
+
+Two domain name operators are also provided. If the variable name
+begins with @samp{.} then only the domain part of the name is
+substituted. For example, if @code{$@{rhost@}} was
+@samp{swan.doc.ic.ac.uk} then @code{$@{.rhost@}} would be expanded to
+@samp{doc.ic.ac.uk}. Similarly, if the variable name ends with @samp{.}
+then only the host component is substituted. In the previous example,
+@code{$@{rhost.@}} would be expanded to @samp{swan}.@refill
+
+Variable expansion is a two phase process. Before a location is parsed,
+all references to selectors, @i{eg} @code{$@{path@}}, are expanded. The
+location is then parsed, selections are evaluated and option assignments
+recorded. If there were no selections or they all succeeded the
+location is used and the values of the following options are expanded in
+the order given: @var{sublink}, @var{rfs}, @var{fs}, @var{opts},
+@var{remopts}, @var{mount} and @var{unmount}.
+
+Note that expansion of option values is done after @dfn{all} assignments
+have been completed and not in a purely left to right order as is done
+by the shell. This generally has the desired effect but care must be
+taken if one of the options references another, in which case the
+ordering can become significant.
+
+There are two special cases concerning variable expansion:
+
+@enumerate
+@item
+before a map is consulted, any selectors in the name received
+from the kernel are expanded. For example, if the request from the
+kernel was for `@t{$@{arch@}}@t{.bin}' and the machine architecture
+was @samp{vax}, the value given to @code{$@{key@}} would be
+@samp{vax.bin}.@refill
+
+@item
+the value of @code{$@{rhost@}} is expanded and normalized before the
+other options are expanded. The normalization process strips any local
+sub-domain components. For example, if @code{$@{domain@}} was
+@samp{Berkeley.EDU} and @code{$@{rhost@}} was initially
+@samp{snow.Berkeley.EDU}, after the normalization it would simply be
+@samp{snow}. Hostname normalization is currently done in a
+@emph{case-dependent} manner.@refill
+@end enumerate
+
+@node Selectors, Map Options, Variable Expansion, Location Format
+@comment node-name, next, previous, up
+@subsection Selectors
+@cindex Selectors
+
+Selectors are used to control the use of a location. It is possible to
+share a mount map between many machines in such a way that filesystem
+location, architecture and operating system differences are hidden from
+the users. A selector of the form @samp{arch==sun3;os==sos4} would only
+apply on Sun-3s running SunOS 4.x.
+
+Selectors are evaluated left to right. If a selector fails then that
+location is ignored. Thus the selectors form a conjunction and the
+locations form a disjunction. If all the locations are ignored or
+otherwise fail then @i{Amd} uses the @dfn{error} filesystem
+(@pxref{Error Filesystem}). This is equivalent to having a location
+@samp{type:=error} at the end of each mount-map entry.@refill
+
+The selectors currently implemented are:
+
+@table @samp
+@cindex arch, mount selector
+@cindex Mount selector; arch
+@cindex Selector; arch
+@item arch
+the machine architecture which was automatically determined at compile
+time. The architecture type can be displayed by running the command
+@samp{amd -v}. @xref{Supported Machine Architectures}.@refill
+
+@item autodir
+@cindex autodir, mount selector
+@cindex Mount selector; autodir
+@cindex Selector; autodir
+the default directory under which to mount filesystems. This may be
+changed by the ``-a'' command line option. See the @var{fs} option.
+
+@item byte
+@cindex byte, mount selector
+@cindex Mount selector; byte
+@cindex Selector; byte
+the machine's byte ordering. This is either @samp{little}, indicating
+little-endian, or @samp{big}, indicating big-endian. One possible use
+is to share @samp{rwho} databases (@pxref{rwho servers}). Another is to
+share ndbm databases, however this use can be considered a courageous
+juggling act.
+
+@item cluster
+@cindex cluster, mount selector
+@cindex Mount selector; cluster
+@cindex Selector; cluster
+is provided as a hook for the name of the local cluster. This can be
+used to decide which servers to use for copies of replicated
+filesystems. @code{$@{cluster@}} defaults to the value of
+@code{$@{domain@}} unless a different value is set with the ``-C''
+command line option.
+
+@item domain
+@cindex domain, mount selector
+@cindex Mount selector; domain
+@cindex Selector; domain
+the local domain name as specified by the ``-d'' command line option.
+See @samp{host}.
+
+@item host
+@cindex host, mount selector
+@cindex Mount selector; host
+@cindex Selector; host
+the local hostname as determined by @b{gethostname}(2). If no domain
+name was specified on the command line and the hostname contains a
+period @samp{.} then the string before the period is used as the
+host name, and the string after the period is assigned to
+@code{$@{domain@}}. For example, if the hostname is
+@samp{styx.doc.ic.ac.uk} then @code{host} would be @samp{styx} and
+@code{domain} would be @samp{doc.ic.ac.uk}. @code{hostd} would be
+@samp{styx.doc.ic.ac.uk}.@refill
+
+@item hostd
+@cindex hostd, mount selector
+@cindex Mount selector; hostd
+@cindex Selector; hostd
+is @code{$@{host@}} and @code{$@{domain@}} concatenated with a
+@samp{.} inserted between them if required. If @code{$@{domain@}}
+is an empty string then @code{$@{host@}} and @code{$@{hostd@}} will be
+identical.
+
+@item karch
+@cindex karch, mount selector
+@cindex Mount selector; karch
+@cindex Selector; karch
+is provided as a hook for the kernel architecture. This is used on
+SunOS 4, for example, to distinguish between different @samp{/usr/kvm}
+volumes. @code{$@{karch@}} defaults to the value of @code{$@{arch@}}
+unless a different value is set with the ``-k'' command line option.
+
+@item os
+@cindex os, mount selector
+@cindex Mount selector; os
+@cindex Selector; os
+the operating system. Like the machine architecture, this is
+automatically determined at compile time. The operating system name can
+be displayed by running the command @samp{amd -v}. @xref{Supported
+Operating Systems}.@refill
+
+@end table
+
+The following selectors are also provided. Unlike the other selectors,
+they vary for each lookup. Note that when the name from the kernel is
+expanded prior to a map lookup, these selectors are all defined as empty
+strings.
+
+@table @samp
+@item key
+@cindex key, mount selector
+@cindex Mount selector; key
+@cindex Selector; key
+the name being resolved. For example, if @file{/home} is an automount
+point, then accessing @file{/home/foo} would set @code{$@{key@}} to the
+string @samp{foo}. The key is prefixed by the @var{pref} option set in
+the parent mount point. The default prefix is an empty string. If the
+prefix was @file{blah/} then @code{$@{key@}} would be set to
+@file{blah/foo}.@refill
+
+@item map
+@cindex map, mount selector
+@cindex Mount selector; map
+@cindex Selector; map
+the name of the mount map being used.
+
+@item path
+@cindex path, mount selector
+@cindex Mount selector; path
+@cindex Selector; path
+the full pathname of the name being resolved. For example
+@file{/home/foo} in the example above.
+
+@item wire
+@cindex wire, mount selector
+@cindex Mount selector; wire
+@cindex Selector; wire
+the name of the network to which the primary network interface is
+attached. If a symbolic name cannot be found in the networks or hosts
+database then dotted IP address format is used. This value is also
+output by the ``-v'' option.
+
+@end table
+
+Selectors can be negated by using @samp{!=} instead of @samp{==}. For
+example to select a location on all non-Vax machines the selector
+@samp{arch!=vax} would be used.
+
+@node Map Options, , Selectors, Location Format
+@comment node-name, next, previous, up
+@subsection Map Options
+@cindex Map options
+@cindex Setting map options
+
+Options are parsed concurrently with selectors. The difference is that
+when an option is seen the string following the @samp{:=} is
+recorded for later use. As a minimum the @var{type} option must be
+specified. Each filesystem type has other options which must also be
+specified. @xref{Filesystem Types}, for details on the filesystem
+specific options.@refill
+
+Superfluous option specifications are ignored and are not reported
+as errors.
+
+The following options apply to more than one filesystem type.
+
+@menu
+* delay Option::
+* fs Option::
+* opts Option::
+* remopts Option::
+* sublink Option::
+* type Option::
+@end menu
+
+@node delay Option, fs Option, Map Options, Map Options
+@comment node-name, next, previous, up
+@subsubsection delay Option
+@cindex Setting a delay on a mount location
+@cindex Delaying mounts from specific locations
+@cindex Primary server
+@cindex Secondary server
+@cindex delay, mount option
+@cindex Mount option; delay
+
+The delay, in seconds, before an attempt will be made to mount from the current location.
+Auxilliary data, such as network address, file handles and so on are computed
+regardless of this value.
+
+A delay can be used to implement the notion of primary and secondary file servers.
+The secondary servers would have a delay of a few seconds,
+thus giving the primary servers a chance to respond first.
+
+@node fs Option, opts Option, delay Option, Map Options
+@comment node-name, next, previous, up
+@subsubsection fs Option
+@cindex Setting the local mount point
+@cindex Overriding the default mount point
+@cindex fs, mount option
+@cindex Mount option; fs
+
+The local mount point. The semantics of this option vary between
+filesystems.
+
+For NFS and UFS filesystems the value of @code{$@{fs@}} is used as the
+local mount point. For other filesystem types it has other meanings
+which are described in the section describing the respective filesystem
+type. It is important that this string uniquely identifies the
+filesystem being mounted. To satisfy this requirement, it should
+contain the name of the host on which the filesystem is resident and the
+pathname of the filesystem on the local or remote host.
+
+The reason for requiring the hostname is clear if replicated filesystems
+are considered. If a fileserver goes down and a replacement filesystem
+is mounted then the @dfn{local} mount point @dfn{must} be different from
+that of the filesystem which is hung. Some encoding of the filesystem
+name is required if more than one filesystem is to be mounted from any
+given host.
+
+If the hostname is first in the path then all mounts from a particular
+host will be gathered below a single directory. If that server goes
+down then the hung mount points are less likely to be accidentally
+referenced, for example when @b{getwd}(3) traverses the namespace to
+find the pathname of the current directory.
+
+The @samp{fs} option defaults to
+@code{$@{autodir@}/$@{rhost@}$@{rfs@}}. In addition,
+@samp{rhost} defaults to the local host name (@code{$@{host@}}) and
+@samp{rfs} defaults to the value of @code{$@{path@}}, which is the full
+path of the requested file; @samp{/home/foo} in the example above
+(@pxref{Selectors}). @code{$@{autodir@}} defaults to @samp{/a} but may
+be changed with the ``-a'' command line option. Sun's automounter
+defaults to @samp{/tmp_mnt}. Note that there is no @samp{/} between
+the @code{$@{rhost@}} and @code{$@{rfs@}} since @code{$@{rfs@}} begins
+with a @samp{/}.@refill
+
+@node opts Option, remopts Option, fs Option, Map Options
+@comment node-name, next, previous, up
+@subsubsection opts Option
+@cindex Setting system mount options
+@cindex Passing parameters to the mount system call
+@cindex mount system call
+@cindex mount system call flags
+@cindex The mount system call
+@cindex opts, mount option
+@cindex Mount option; opts
+
+The options to pass to the mount system call. A leading @samp{-} is
+silently ignored. The mount options supported generally correspond to
+those used by @b{mount}(8) and are listed below. Some additional
+pseudo-options are interpreted by @i{Amd} and are also listed.
+
+Unless specifically overridden, each of the system default mount options
+applies. Any options not recognised are ignored. If no options list is
+supplied the string @samp{rw,defaults} is used and all the system
+default mount options apply. Options which are not applicable for a
+particular operating system are silently ignored. For example, only 4.4
+BSD is known to implement the @code{compress} and @code{spongy} options.
+
+@table @code
+@item compress
+Use NFS compression protocol.
+@item grpid
+Use BSD directory group-id semantics.
+@item intr
+Allow keyboard interrupts on hard mounts.
+@item nfsv2
+Use the version 2 NFS protocol in preference to version 3.
+@item noconn
+Don't make a connection on datagram transports.
+@item nocto
+No close-to-open consistency.
+@item nodevs
+Don't allow local special devices on this filesystem.
+@item nosuid
+Don't allow set-uid or set-gid executables on this filesystem.
+@item quota
+Enable quota checking on this mount.
+@item retrans=@i{n}
+The number of NFS retransmits made before a user error is generated by a
+@samp{soft} mounted filesystem, and before a @samp{hard} mounted
+filesystem reports @samp{NFS server @dfn{yoyo} not responding still
+trying}.
+@item ro
+Mount this filesystem readonly.
+@item rsize=@var{n}
+The NFS read packet size. You may need to set this if you are using
+NFS/UDP through a gateway.
+@item soft
+Give up after @dfn{retrans} retransmissions.
+@item spongy
+Like @samp{soft} for status requests, and @samp{hard} for data transfers.
+@item tcp
+Use TCP/IP instead of UDP/IP, ignored if the NFS implementation does not
+support TCP/IP mounts.
+@item timeo=@var{n}
+The NFS timeout, in tenth-seconds, before a request is retransmitted.
+@item wsize=@var{n}
+The NFS write packet size. You may need to set this if you are using
+NFS/UDP through a gateway.
+@end table
+
+The following options are implemented by @i{Amd}, rather than being
+passed to the kernel.
+
+@table @code
+@item nounmount
+Configures the mount so that its time-to-live will
+never expire. This is also the default for some filesystem types.
+@c
+@c Implementation broken:
+@item ping=@var{n}
+The interval, in seconds, between keep-alive pings. When four
+consecutive pings have failed the mount point is marked as hung. This
+interval defaults to 30 seconds. If the ping interval is less than zero,
+no pings are sent and the host is assumed to be always
+up. By default, pings are not sent for an NFS/TCP mount.
+@item retry=@var{n}
+The number of times to retry the mount system call.
+@item utimeout=@var{n}
+The interval, in seconds, by which the mount's
+time-to-live is extended after an unmount attempt
+has failed. In fact the interval is extended before the unmount is
+attempted to avoid thrashing. The default value is 120 seconds (two
+minutes) or as set by the ``-w'' command line option.
+@end table
+
+@node remopts Option, sublink Option, opts Option, Map Options
+@comment node-name, next, previous, up
+@subsubsection remopts Option
+@cindex Setting system mount options for non-local networks
+@cindex remopts, mount option
+@cindex Mount option; remopts
+
+This option has the same use as @code{$@{opts@}} but applies only when
+the remote host is on a non-local network. For example, when using NFS
+across a gateway it is often necessary to use smaller values for the
+data read and write sizes. This can simply be done by specifying the
+small values in @var{remopts}. When a non-local host is accessed, the
+smaller sizes will automatically be used.
+
+@i{Amd} determines whether a host is local by examining the network
+interface configuration at startup. Any interface changes made after
+@i{Amd} has been started will not be noticed. The likely effect will
+be that a host may incorrectly be declared non-local.
+
+Unless otherwise set, the value of @code{$@{rem@}} is the same as the
+value of @code{$@{opts@}}.
+
+@node sublink Option, type Option, remopts Option, Map Options
+@comment node-name, next, previous, up
+@subsubsection sublink Option
+@cindex Setting the sublink option
+@cindex sublink, mount option
+@cindex Mount option; sublink
+
+The subdirectory within the mounted filesystem to which the reference
+should point. This can be used to prevent duplicate mounts in cases
+where multiple directories in the same mounted filesystem are used.
+
+@node type Option, , sublink Option, Map Options
+@comment node-name, next, previous, up
+@subsubsection type Option
+@cindex Setting the filesystem type option
+@cindex type, mount option
+@cindex Mount option; type
+
+The filesystem type to be used. @xref{Filesystem Types}, for a full
+description of each type.@refill
+
+@node Amd Command Line Options, Filesystem Types, Mount Maps, Top
+@comment node-name, next, previous, up
+@chapter @i{Amd} Command Line Options
+@cindex Command line options, Amd
+@cindex Amd command line options
+@cindex Overriding defaults on the command line
+
+Many of @i{Amd}'s parameters can be set from the command line. The
+command line is also used to specify automount points and maps.
+
+The general format of a command line is
+
+@example
+amd [@i{options}] @{ @i{directory} @i{map-name} [-@i{map-options}] @} ...
+@end example
+
+For each directory and map-name given, @i{Amd} establishes an
+automount point. The @dfn{map-options} may be any sequence of options
+or selectors---@pxref{Location Format}. The @dfn{map-options}
+apply only to @i{Amd}'s mount point.
+
+@samp{type:=toplvl;cache:=mapdefault;fs:=$@{map@}} is the default value for the
+map options. Default options for a map are read from a special entry in
+the map whose key is the string @samp{/defaults}. When default options
+are given they are prepended to any options specified in the mount-map
+locations as explained in. @xref{Map Defaults}, for more details.
+
+The @dfn{options} are any combination of those listed below.
+
+Once the command line has been parsed, the automount points are mounted.
+The mount points are created if they do not already exist, in which case they
+will be removed when @i{Amd} exits.
+Finally, @i{Amd} disassociates itself from its controlling terminal and
+forks into the background.
+
+Note: Even if @i{Amd} has been built with @samp{-DDEBUG} it will still
+background itself and disassociate itself from the controlling terminal.
+To use a debugger it is necessary to specify @samp{-D nodaemon} on the
+command line.
+
+@menu
+* -a Option:: Automount directory.
+* -c Option:: Cache timeout interval.
+* -d Option:: Domain name.
+* -k Option:: Kernel architecture.
+* -l Option:: Log file.
+* -n Option:: Hostname normalisation.
+* -p Option:: Output process id.
+* -r Option:: Restart existing mounts.
+* -t Option:: Kernel RPC timeout.
+* -v Option:: Version information.
+* -w Option:: Wait interval after failed unmount.
+* -x Option:: Log options.
+* -y Option:: NIS domain.
+* -C-Option:: Cluster name.
+* -D-Option:: Debug flags.
+@end menu
+
+@node -a Option, -c Option, Amd Command Line Options, Amd Command Line Options
+@comment node-name, next, previous, up
+@section @code{-a} @var{directory}
+@cindex Automount directory
+@cindex Setting the default mount directory
+
+Specifies the default mount directory. This option changes the variable
+@code{$@{autodir@}} which otherwise defaults to @file{/a}. For example,
+some sites prefer @file{/amd}.
+
+@example
+amd -a /amd ...
+@end example
+
+@node -c Option, -d Option, -a Option, Amd Command Line Options
+@comment node-name, next, previous, up
+@section @code{-c} @var{cache-interval}
+@cindex Cache interval
+@cindex Interval before a filesystem times out
+@cindex Setting the interval before a filesystem times out
+@cindex Changing the interval before a filesystem times out
+
+Selects the period, in seconds, for which a name is cached by @i{Amd}.
+If no reference is made to the volume in this period, @i{Amd} discards
+the volume name to filesystem mapping.
+
+Once the last reference to a filesystem has been removed, @i{Amd}
+attempts to unmount the filesystem. If the unmount fails the interval
+is extended by a further period as specified by the @samp{-w} command
+line option or by the @samp{utimeout} mount option.
+
+The default @dfn{cache-interval} is 300 seconds (five minutes).
+
+@node -d Option, -k Option, -c Option, Amd Command Line Options
+@comment node-name, next, previous, up
+@section @code{-d} @var{domain}
+@cindex Domain name
+@cindex Setting the local domain name
+@cindex Overriding the local domain name
+
+Specifies the host's domain. This sets the internal variable
+@code{$@{domain@}} and affects the @code{$@{hostd@}} variable.
+
+If this option is not specified and the hostname already contains the
+local domain then that is used, otherwise the default value of
+@code{$@{domain@}} is @samp{unknown.domain}.
+
+For example, if the local domain was @samp{doc.ic.ac.uk}, @i{Amd} could
+be started as follows:
+
+@example
+amd -d doc.ic.ac.uk ...
+@end example
+
+@node -k Option, -l Option, -d Option, Amd Command Line Options
+@comment node-name, next, previous, up
+@section @code{-k} @var{kernel-architecture}
+@cindex Setting the Kernel architecture
+
+Specifies the kernel architecture of the system. This is usually the
+output of @samp{arch -k} and its only effect is to set the variable
+@code{$@{karch@}}. If this option is not given, @code{$@{karch@}} has
+the same value as @code{$@{arch@}}.
+
+This would be used as follows:
+
+@example
+amd -k `arch -k` ...
+@end example
+
+@node -l Option, -n Option, -k Option, Amd Command Line Options
+@comment node-name, next, previous, up
+@section @code{-l} @var{log-option}
+@cindex Log filename
+@cindex Setting the log file
+@cindex Using syslog to log errors
+@cindex syslog
+
+Selects the form of logging to be made. Two special @dfn{log-options}
+are recognised.
+
+@enumerate
+@item
+If @dfn{log-option} is the string @samp{syslog}, @i{Amd} will use the
+@b{syslog}(3) mechanism.@refill
+
+@item
+If @dfn{log-option} is the string @samp{/dev/stderr}, @i{Amd} will use
+standard error, which is also the default target for log messages. To
+implement this, @i{Amd} simulates the effect of the @samp{/dev/fd}
+driver.
+@end enumerate
+
+Any other string is taken as a filename to use for logging. Log
+messages are appended to the file if it already exists, otherwise a new
+file is created. The file is opened once and then held open, rather
+than being re-opened for each message.
+
+If the @samp{syslog} option is specified but the system does not support
+syslog or if the named file cannot be opened or created, @i{Amd} will
+use standard error. Error messages generated before @i{Amd} has
+finished parsing the command line are printed on standard error.
+
+Using @samp{syslog} is usually best, in which case @i{Amd} would be
+started as follows:
+
+@example
+amd -l syslog ...
+@end example
+
+@node -n Option, -p Option, -l Option, Amd Command Line Options
+@comment node-name, next, previous, up
+@section @code{-n}
+@cindex Hostname normalisation
+@cindex Aliased hostnames
+@cindex Resolving aliased hostnames
+@cindex Normalising hostnames
+
+Normalises the remote hostname before using it. Normalisation is done
+by replacing the value of @code{$@{rhost@}} with the primary name
+returned by a hostname lookup.
+
+This option should be used if several names are used to refer to a
+single host in a mount map.
+
+@node -p Option, -r Option, -n Option, Amd Command Line Options
+@comment node-name, next, previous, up
+@section @code{-p}
+@cindex Process id
+@cindex Displaying the process id
+@cindex process id of Amd daemon
+@cindex pid file, creating with -p option
+@cindex Creating a pid file
+
+Causes @i{Amd}'s process id to be printed on standard output.
+This can be redirected to a suitable file for use with kill:
+
+@example
+amd -p > /var/run/amd.pid ...
+@end example
+
+This option only has an affect if @i{Amd} is running in daemon mode.
+If @i{Amd} is started with the @code{-D nodaemon} debug flag, this
+option is ignored.
+
+@node -r Option, -t Option, -p Option, Amd Command Line Options
+@comment node-name, next, previous, up
+@section @code{-r}
+@cindex Restarting existing mounts
+@cindex Picking up existing mounts
+
+Tells @i{Amd} to restart existing mounts (@pxref{Inheritance Filesystem}).
+@c @dfn{This option will be made the default in the next release.}
+
+@node -t Option, -v Option, -r Option, Amd Command Line Options
+@comment node-name, next, previous, up
+@section @code{-t} @var{timeout.retransmit}
+@cindex Setting Amd's RPC parameters
+
+Specifies the RPC @dfn{timeout} and @dfn{retransmit} intervals used by
+the kernel to communicate to @i{Amd}. These are used to set the
+@samp{timeo} and @samp{retrans} mount options.
+
+@i{Amd} relies on the kernel RPC retransmit mechanism to trigger mount
+retries. The value of this parameter changes the retry interval. Too
+long an interval gives poor interactive response, too short an interval
+causes excessive retries.
+
+@node -v Option, -w Option, -t Option, Amd Command Line Options
+@comment node-name, next, previous, up
+@section @code{-v}
+@cindex Version information
+@cindex Discovering version information
+@cindex How to discover your version of Amd
+
+Print version information on standard error and then exit. The output
+is of the form:
+
+@example
+amd 5.2.1.11 of 91/03/17 18:04:05 5.3Alpha11 #0: Sun Mar 17 18:07:28 GMT 1991
+Built by pendry@@vangogh.Berkeley.EDU for a hp300 running bsd44 (big-endian).
+Map support for: root, passwd, union, file, error.
+FS: ufs, nfs, nfsx, host, link, program, union, auto, direct, toplvl, error.
+Primary network is 128.32.130.0.
+@end example
+
+The information includes the version number, release date and name of
+the release. The architecture (@pxref{Supported Machine Architectures}),
+operating system (@pxref{Supported Operating Systems})
+and byte ordering are also printed as they appear in the @code{$@{os@}},
+@code{$@{arch@}} and @code{$@{byte@}} variables.@refill
+
+@node -w Option, -x Option, -v Option, Amd Command Line Options
+@comment node-name, next, previous, up
+@section @code{-w} @var{wait-timeout}
+@cindex Setting the interval between unmount attempts
+@cindex unmount attempt backoff interval
+
+Selects the interval in seconds between unmount attempts after the
+initial time-to-live has expired.
+
+This defaults to 120 seconds (two minutes).
+
+@node -x Option, -y Option, -w Option, Amd Command Line Options
+@comment node-name, next, previous, up
+@section @code{-x} @var{opts}
+@cindex Log message selection
+@cindex Selecting specific log messages
+@cindex How to select log messages
+@cindex syslog priorities
+
+Specifies the type and verbosity of log messages. @dfn{opts} is
+a comma separated list selected from the following options:
+
+@table @code
+@item fatal
+Fatal errors
+@item error
+Non-fatal errors
+@item user
+Non-fatal user errors
+@item warn
+Recoverable errors
+@item warning
+Alias for @code{warn}
+@item info
+Information messages
+@item map
+Mount map usage
+@item stats
+Additional statistics
+@item all
+All of the above
+@end table
+
+Initially a set of default logging flags is enabled. This is as if
+@samp{-x all,nomap,nostats} had been selected. The command line is
+parsed and logging is controlled by the ``-x'' option. The very first
+set of logging flags is saved and can not be subsequently disabled using
+@i{Amq}. This default set of options is useful for general production
+use.@refill
+
+The @samp{info} messages include details of what is mounted and
+unmounted and when filesystems have timed out. If you want to have the
+default set of messages without the @samp{info} messages then you simply
+need @samp{-x noinfo}. The messages given by @samp{user} relate to
+errors in the mount maps, so these are useful when new maps are
+installed. The following table lists the syslog priorites used for each
+of the message types.@refill
+
+@table @code
+@item fatal
+LOG_CRIT
+@item error
+LOG_ERR
+@item user
+LOG_WARNING
+@item warning
+LOG_WARNING
+@item info
+LOG_INFO
+@item debug
+LOG_DEBUG
+@item map
+LOG_DEBUG
+@item stats
+LOG_INFO
+@end table
+
+
+The options can be prefixed by the string @samp{no} to indicate
+that this option should be turned off. For example, to obtain all
+but @samp{info} messages the option @samp{-x all,noinfo} would be used.
+
+If @i{Amd} was built with debugging enabled the @code{debug} option is
+automatically enabled regardless of the command line options.
+
+@node -y Option, -C-Option, -x Option, Amd Command Line Options
+@comment node-name, next, previous, up
+@section @code{-y} @var{NIS-domain}
+@cindex NIS (YP) domain name
+@cindex Overriding the NIS (YP) domain name
+@cindex Setting the NIS (YP) domain name
+@cindex YP domain name
+
+Selects an alternate NIS domain. This is useful for debugging and
+cross-domain shared mounting. If this flag is specified, @i{Amd}
+immediately attempts to bind to a server for this domain.
+@c @i{Amd} refers to NIS maps when it starts, unless the ``-m'' option
+@c is specified, and whenever required in a mount map.
+
+@node -C-Option, -D-Option, -y Option, Amd Command Line Options
+@comment node-name, next, previous, up
+@section @code{-C} @var{cluster-name}
+@cindex Cluster names
+@cindex Setting the cluster name
+
+Specifies the name of the cluster of which the local machine is a member.
+The only effect is to set the variable @code{$@{cluster@}}.
+The @dfn{cluster-name} is will usually obtained by running another command which uses
+a database to map the local hostname into a cluster name.
+@code{$@{cluster@}} can then be used as a selector to restrict mounting of
+replicated data.
+If this option is not given, @code{$@{cluster@}} has the same value as @code{$@{domain@}}.
+This would be used as follows:
+
+@example
+amd -C `clustername` ...
+@end example
+
+@node -D-Option, , -C-Option, Amd Command Line Options
+@comment node-name, next, previous, up
+@section @code{-D} @var{opts}
+@cindex Debug options
+@cindex Setting debug flags
+
+Controls the verbosity and coverage of the debugging trace; @dfn{opts}
+is a comma separated list of debugging options. The ``-D'' option is
+only available if @i{Amd} was compiled with @samp{-DDEBUG}. The memory
+debugging facilities are only available if @i{Amd} was compiled with
+@samp{-DDEBUG_MEM} (in addition to @samp{-DDEBUG}).
+
+The most common options to use are @samp{-D trace} and @samp{-D test}
+(which turns on all the useful debug options). See the program source
+for a more detailed explanation of the available options.
+
+@node Filesystem Types, Run-time Administration, Amd Command Line Options, Top
+@comment node-name, next, previous, up
+@chapter Filesystem Types
+@cindex Filesystem types
+@cindex Mount types
+@cindex Types of filesystem
+
+To mount a volume, @i{Amd} must be told the type of filesystem to be
+used. Each filesystem type typically requires additional information
+such as the fileserver name for NFS.
+
+From the point of view of @i{Amd}, a @dfn{filesystem} is anything that
+can resolve an incoming name lookup. An important feature is support
+for multiple filesystem types. Some of these filesystems are
+implemented in the local kernel and some on remote fileservers, whilst
+the others are implemented internally by @i{Amd}.@refill
+
+The two common filesystem types are UFS and NFS. Four other user
+accessible filesystems (@samp{link}, @samp{program}, @samp{auto} and
+@samp{direct}) are also implemented internally by @i{Amd} and these are
+described below. There are two additional filesystem types internal to
+@i{Amd} which are not directly accessible to the user (@samp{inherit}
+and @samp{error}). Their use is described since they may still have an
+effect visible to the user.@refill
+
+@menu
+* Network Filesystem:: A single NFS filesystem.
+* Network Host Filesystem:: NFS mount a host's entire export tree.
+* Network Filesystem Group:: An atomic group of NFS filesystems.
+* Unix Filesystem:: Native disk filesystem.
+* Program Filesystem:: Generic Program mounts.
+* Symbolic Link Filesystem:: Local link referencing existing filesystem.
+* Symbolic Link Filesystem II:: More on referencing existing filesystems.
+* Automount Filesystem::
+* Direct Automount Filesystem::
+* Union Filesystem::
+* Error Filesystem::
+* Top-level Filesystem::
+* Root Filesystem::
+* Inheritance Filesystem::
+@end menu
+
+@node Network Filesystem, Network Host Filesystem, Filesystem Types, Filesystem Types
+@comment node-name, next, previous, up
+@section Network Filesystem (@samp{type:=nfs})
+@cindex NFS
+@cindex Mounting an NFS filesystem
+@cindex How to mount and NFS filesystem
+@cindex nfs, filesystem type
+@cindex Filesystem type; nfs
+
+The @dfn{nfs} filesystem type provides access to Sun's NFS.
+
+@noindent
+The following options must be specified:
+
+@table @code
+@cindex rhost, mount option
+@cindex Mount option; rhost
+@item rhost
+the remote fileserver. This must be an entry in the hosts database. IP
+addresses are not accepted. The default value is taken
+from the local host name (@code{$@{host@}}) if no other value is
+specified.
+
+@cindex rfs, mount option
+@cindex Mount option; rfs
+@item rfs
+the remote filesystem.
+If no value is specified for this option, an internal default of
+@code{$@{path@}} is used.
+@end table
+
+NFS mounts require a two stage process. First, the @dfn{file handle} of
+the remote file system must be obtained from the server. Then a mount
+system call must be done on the local system. @i{Amd} keeps a cache
+of file handles for remote file systems. The cache entries have a
+lifetime of a few minutes.
+
+If a required file handle is not in the cache, @i{Amd} sends a request
+to the remote server to obtain it. @i{Amd} @dfn{does not} wait for
+a response; it notes that one of the locations needs retrying, but
+continues with any remaining locations. When the file handle becomes
+available, and assuming none of the other locations was successfully
+mounted, @i{Amd} will retry the mount. This mechanism allows several
+NFS filesystems to be mounted in parallel.
+@c @footnote{The mechanism
+@c is general, however NFS is the only filesystem
+@c for which the required hooks have been written.}
+The first one which responds with a valid file handle will be used.
+
+@noindent
+An NFS entry might be:
+
+@example
+jsp host!=charm;type:=nfs;rhost:=charm;rfs:=/home/charm;sublink:=jsp
+@end example
+
+The mount system call and any unmount attempts are always done
+in a new task to avoid the possibilty of blocking @i{Amd}.
+
+@node Network Host Filesystem, Network Filesystem Group, Network Filesystem, Filesystem Types
+@comment node-name, next, previous, up
+@section Network Host Filesystem (@samp{type:=host})
+@cindex Network host filesystem
+@cindex Mounting entire export trees
+@cindex How to mount all NFS exported filesystems
+@cindex host, filesystem type
+@cindex Filesystem type; host
+
+@c NOTE: the current implementation of the @dfn{host} filesystem type
+@c sometimes fails to maintain a consistent view of the remote mount tree.
+@c This happens when the mount times out and only some of the remote mounts
+@c are successfully unmounted. To prevent this from occuring, use the
+@c @samp{nounmount} mount option.
+
+The @dfn{host} filesystem allows access to the entire export tree of an
+NFS server. The implementation is layered above the @samp{nfs}
+implementation so keep-alives work in the same way. The only option
+which needs to specified is @samp{rhost} which is the name of the
+fileserver to mount.
+
+The @samp{host} filesystem type works by querying the mount daemon on
+the given fileserver to obtain its export list. @i{Amd} then obtains
+filehandles for each of the exported filesystems. Any errors at this
+stage cause that particular filesystem to be ignored. Finally each
+filesystem is mounted. Again, errors are logged but ignored. One
+common reason for mounts to fail is that the mount point does not exist.
+Although @i{Amd} attempts to automatically create the mount point, it
+may be on a remote filesystem to which @i{Amd} does not have write
+permission.
+
+When an attempt to unmount a @samp{host} filesystem mount fails, @i{Amd}
+remounts any filesystems which had succesfully been unmounted. To do
+this @i{Amd} queries the mount daemon again and obtains a fresh copy of
+the export list. @i{Amd} then tries to mount any exported filesystems
+which are not currently mounted.
+
+Sun's automounter provides a special @samp{-hosts} map. To achieve the
+same effect with @i{Amd} requires two steps. First a mount map must
+be created as follows:
+
+@example
+/defaults type:=host;fs:=$@{autodir@}/$@{rhost@}/root;rhost:=$@{key@}
+* opts:=rw,nosuid,grpid
+@end example
+
+@noindent
+and then start @i{Amd} with the following command
+
+@example
+amd /n net.map
+@end example
+
+@noindent
+where @samp{net.map} is the name of map described above. Note that the
+value of @code{$@{fs@}} is overridden in the map. This is done to avoid
+a clash between the mount tree and any other filesystem already mounted
+from the same fileserver.
+
+If different mount options are needed for different hosts then
+additional entries can be added to the map, for example
+
+@example
+host2 opts:=ro,nosuid,soft
+@end example
+
+@noindent
+would soft mount @samp{host2} read-only.
+
+@node Network Filesystem Group, Unix Filesystem, Network Host Filesystem, Filesystem Types
+@comment node-name, next, previous, up
+@section Network Filesystem Group (@samp{type:=nfsx})
+@cindex Network filesystem group
+@cindex Atomic NFS mounts
+@cindex Mounting an atomic group of NFS filesystems
+@cindex How to mount an atomic group of NFS filesystems
+@cindex nfsx, filesystem type
+@cindex Filesystem type; nfsx
+
+The @dfn{nfsx} filesystem allows a group of filesystems to be mounted
+from a single NFS server. The implementation is layered above the
+@samp{nfs} implementation so keep-alives work in the same way.
+
+The options are the same as for the @samp{nfs} filesystem with one
+difference.
+
+@noindent
+The following options must be specified:
+
+@table @code
+@item rhost
+the remote fileserver. This must be an entry in the hosts database. IP
+addresses are not accepted. The default value is taken from the local
+host name (@code{$@{host@}}) if no other value is specified.
+
+@item rfs
+as a list of filesystems to mount. The list is in the form of a comma
+separated strings.
+@end table
+
+@noindent
+For example:
+
+@example
+pub type:=nfsx;rhost:=gould;\
+ rfs:=/public,/,graphics,usenet;fs:=$@{autodir@}/$@{rhost@}/root
+@end example
+
+The first string defines the root of the tree, and is applied as a
+prefix to the remaining members of the list which define the individual
+filesystems. The first string is @emph{not} used as a filesystem name.
+A parallel operation is used to determine the local mount points to
+ensure a consistent layout of a tree of mounts.
+
+Here, the @emph{three} filesystems, @samp{/public},
+@samp{/public/graphics} and @samp{/public/usenet}, would be mounted.@refill
+
+A local mount point, @code{$@{fs@}}, @emph{must} be specified. The
+default local mount point will not work correctly in the general case.
+A suggestion is to use @samp{fs:=$@{autodir@}/$@{rhost@}/root}.@refill
+
+@node Unix Filesystem, Program Filesystem, Network Filesystem Group, Filesystem Types
+@comment node-name, next, previous, up
+@section Unix Filesystem (@samp{type:=ufs})
+@cindex Unix filesystem
+@cindex UFS
+@cindex Mounting a UFS filesystem
+@cindex Mounting a local disk
+@cindex How to mount a UFS filesystems
+@cindex How to mount a local disk
+@cindex Disk filesystems
+@cindex ufs, filesystem type
+@cindex Filesystem type; ufs
+
+The @dfn{ufs} filesystem type provides access to the system's
+standard disk filesystem---usually a derivative of the Berkeley Fast Filesystem.
+
+@noindent
+The following option must be specified:
+
+@table @code
+@cindex dev, mount option
+@cindex Mount option; dev
+@item dev
+the block special device to be mounted.
+@end table
+
+A UFS entry might be:
+
+@example
+jsp host==charm;type:=ufs;dev:=/dev/xd0g;sublink:=jsp
+@end example
+
+@node Program Filesystem, Symbolic Link Filesystem, Unix Filesystem, Filesystem Types
+@comment node-name, next, previous, up
+@section Program Filesystem (@samp{type:=program})
+@cindex Program filesystem
+@cindex Mount a filesystem under program control
+@cindex program, filesystem type
+@cindex Filesystem type; program
+
+The @dfn{program} filesystem type allows a program to be run whenever a
+mount or unmount is required. This allows easy addition of support for
+other filesystem types, such as MIT's Remote Virtual Disk (RVD)
+which has a programmatic interface via the commands
+@samp{rvdmount} and @samp{rvdunmount}.
+
+@noindent
+The following options must be specified:
+
+@table @code
+@cindex mount, mount option
+@cindex Mount option; mount
+@item mount
+the program which will perform the mount.
+
+@cindex unmount, mount option
+@cindex Mount option; unmount
+@item unmount
+the program which will perform the unmount.
+@end table
+
+The exit code from these two programs is interpreted as a Unix error
+code. As usual, exit code zero indicates success. To execute the
+program @i{Amd} splits the string on whitespace to create an array of
+substrings. Single quotes @samp{'} can be used to quote whitespace
+if that is required in an argument. There is no way to escape or change
+the quote character.
+
+To run the program @samp{rvdmount} with a host name and filesystem as
+arguments would be specified by @samp{mount:="/etc/rvdmount rvdmount
+fserver $@{path@}"}.
+
+The first element in the array is taken as the pathname of the program
+to execute. The other members of the array form the argument vector to
+be passed to the program, @dfn{including argument zero}. This means
+that the split string must have at least two elements. The program is
+directly executed by @i{Amd}, not via a shell. This means that scripts
+must begin with a @code{#!} interpreter specification.
+
+If a filesystem type is to be heavily used, it may be worthwhile adding
+a new filesystem type into @i{Amd}, but for most uses the program
+filesystem should suffice.
+
+When the program is run, standard input and standard error are inherited
+from the current values used by @i{Amd}. Standard output is a
+duplicate of standard error. The value specified with the ``-l''
+command line option has no effect on standard error.
+
+@node Symbolic Link Filesystem, Symbolic Link Filesystem II, Program Filesystem, Filesystem Types
+@comment node-name, next, previous, up
+@section Symbolic Link Filesystem (@samp{type:=link})
+@cindex Symbolic link filesystem
+@cindex Referencing part of the local name space
+@cindex Mounting part of the local name space
+@cindex How to reference part of the local name space
+@cindex link, filesystem type
+@cindex symlink, link filesystem type
+@cindex Filesystem type; link
+
+Each filesystem type creates a symbolic link to point from the volume
+name to the physical mount point. The @samp{link} filesystem does the
+same without any other side effects. This allows any part of the
+machines name space to be accessed via @i{Amd}.
+
+One common use for the symlink filesystem is @file{/homes} which can be
+made to contain an entry for each user which points to their
+(auto-mounted) home directory. Although this may seem rather expensive,
+it provides a great deal of administrative flexibility.
+
+@noindent
+The following option must be defined:
+
+@table @code
+@item fs
+The value of @var{fs} option specifies the destination of the link, as
+modified by the @var{sublink} option. If @var{sublink} is non-null, it
+is appended to @code{$@{fs@}}@code{/} and the resulting string is used
+as the target.
+@end table
+
+The @samp{link} filesystem can be though of as identical to the
+@samp{ufs} filesystem but without actually mounting anything.
+
+An example entry might be:
+
+@example
+jsp host==charm;type:=link;fs:=/home/charm;sublink:=jsp
+@end example
+which would return a symbolic link pointing to @file{/home/charm/jsp}.
+
+@node Symbolic Link Filesystem II, Automount Filesystem, Symbolic Link Filesystem, Filesystem Types,
+@comment node-name, next, previous, up
+@section Symbolic Link Filesystem II (@samp{type:=link})
+@cindex Symbolic link filesystem II
+@cindex Referencing an existing part of the local name space
+@cindex Mounting an existing part of the local name space
+@cindex How to reference an existing part of the local name space
+@cindex linkx, filesystem type
+@cindex symlink, linkx filesystem type
+@cindex Filesystem type; linkx
+
+The @samp{linkx} filesystem type is identical to @samp{link} with the
+exception that the target of the link must exist. Existence is checked
+with the @samp{lstat} system call.
+
+The @samp{linkx} filesystem type is particularly useful for wildcard map
+entries. In this case, a list of possible targets can be give and
+@i{Amd} will choose the first one which exists on the local machine.
+
+@node Automount Filesystem, Direct Automount Filesystem, Symbolic Link Filesystem II, Filesystem Types
+@comment node-name, next, previous, up
+@section Automount Filesystem (@samp{type:=auto})
+@cindex Automount filesystem
+@cindex Map cache types
+@cindex Setting map cache parameters
+@cindex How to set map cache parameters
+@cindex How to start an indirect automount point
+@cindex auto, filesystem type
+@cindex Filesystem type; auto
+@cindex SIGHUP signal
+@cindex Map cache synchronising
+@cindex Synchronising the map cache
+@cindex Map cache options
+@cindex Regular expressions in maps
+
+The @dfn{auto} filesystem type creates a new automount point below an
+existing automount point. Top-level automount points appear as system
+mount points. An automount mount point can also appear as a
+sub-directory of an existing automount point. This allows some
+additional structure to be added, for example to mimic the mount tree of
+another machine.
+
+The following options may be specified:
+
+@table @code
+@cindex cache, mount option
+@cindex Mount option; cache
+@item cache
+specifies whether the data in this mount-map should be
+cached. The default value is @samp{none}, in which case
+no caching is done in order to conserve memory.
+However, better performance and reliability can be obtained by caching
+some or all of a mount-map.
+
+If the cache option specifies @samp{all},
+the entire map is enumerated when the mount point is created.
+
+If the cache option specifies @samp{inc}, caching is done incrementally
+as and when data is required.
+Some map types do not support cache mode @samp{all}, in which case @samp{inc}
+is used whenever @samp{all} is requested.
+
+Caching can be entirely disabled by using cache mode @samp{none}.
+
+If the cache option specifies @samp{regexp} then the entire map will be
+enumerated and each key will be treated as an egrep-style regular
+expression. The order in which a cached map is searched does not
+correspond to the ordering in the source map so the regular expressions
+should be mutually exclusive to avoid confusion.
+
+Each mount map type has a default cache type, usually @samp{inc}, which
+can be selected by specifying @samp{mapdefault}.
+
+The cache mode for a mount map can only be selected on the command line.
+Starting @i{Amd} with the command:
+
+@example
+amd /homes hesiod.homes -cache:=inc
+@end example
+
+will cause @samp{/homes} to be automounted using the @dfn{Hesiod} name
+server with local incremental caching of all succesfully resolved names.
+
+All cached data is forgotten whenever @i{Amd} receives a @samp{SIGHUP}
+signal and, if cache @samp{all} mode was selected, the cache will be
+reloaded. This can be used to inform @i{Amd} that a map has been
+updated. In addition, whenever a cache lookup fails and @i{Amd} needs
+to examine a map, the map's modify time is examined. If the cache is
+out of date with respect to the map then it is flushed as if a
+@samp{SIGHUP} had been received.
+
+An additional option (@samp{sync}) may be specified to force @i{Amd} to
+check the map's modify time whenever a cached entry is being used. For
+example, an incremental, synchronised cache would be created by the
+following command:
+
+@example
+amd /homes hesiod.homes -cache:=inc,sync
+@end example
+
+@item fs
+specifies the name of the mount map to use for the new mount point.
+
+Arguably this should have been specified with the @code{$@{rfs@}} option but
+we are now stuck with it due to historical accident.
+
+@c %If the string @samp{.} is used then the same map is used;
+@c %in addition the lookup prefix is set to the name of the mount point followed
+@c %by a slash @samp{/}.
+@c %This is the same as specifying @samp{fs:=\$@{map@};pref:=\$@{key@}/}.
+@c
+
+@item pref
+alters the name that is looked up in the mount map. If
+@code{$@{pref@}}, the @dfn{prefix}, is non-null then it is prepended to
+the name requested by the kernel @dfn{before} the map is searched.
+@end table
+
+The server @samp{dylan.doc.ic.ac.uk} has two user disks:
+@samp{/dev/dsk/2s0} and @samp{/dev/dsk/5s0}. These are accessed as
+@samp{/home/dylan/dk2} and @samp{/home/dylan/dk5} respectively. Since
+@samp{/home} is already an automount point, this naming is achieved with
+the following map entries:@refill
+
+@example
+dylan type:=auto;fs:=$@{map@};pref:=$@{key@}/
+dylan/dk2 type:=ufs;dev:=/dev/dsk/2s0
+dylan/dk5 type:=ufs;dev:=/dev/dsk/5s0
+@end example
+
+@node Direct Automount Filesystem, Union Filesystem, Automount Filesystem, Filesystem Types
+@comment node-name, next, previous, up
+@section Direct Automount Filesystem (@samp{type:=direct})
+@cindex Direct automount filesystem
+@cindex How to start a direct automount point
+@cindex direct, filesystem type
+@cindex Filesystem type; direct
+
+The @dfn{direct} filesystem is almost identical to the automount
+filesystem. Instead of appearing to be a directory of mount points, it
+appears as a symbolic link to a mounted filesystem. The mount is done
+at the time the link is accessed. @xref{Automount Filesystem} for a
+list of required options.
+
+Direct automount points are created by specifying the @samp{direct}
+filesystem type on the command line:
+
+@example
+amd ... /usr/man auto.direct -type:=direct
+@end example
+
+where @samp{auto.direct} would contain an entry such as:
+
+@example
+usr/man -type:=nfs;rfs:=/usr/man \
+ rhost:=man-server1 rhost:=man-server2
+@end example
+
+In this example, @samp{man-server1} and @samp{man-server2} are file
+servers which export copies of the manual pages. Note that the key
+which is looked up is the name of the automount point without the
+leading @samp{/}.
+
+@node Union Filesystem, Error Filesystem, Direct Automount Filesystem, Filesystem Types
+@comment node-name, next, previous, up
+@section Union Filesystem (@samp{type:=union})
+@cindex Union filesystem
+@cindex union, filesystem type
+@cindex Filesystem type; union
+
+The @dfn{union} filesystem type allows the contents of several
+directories to be merged and made visible in a single directory. This
+can be used to overcome one of the major limitations of the Unix mount
+mechanism which only allows complete directories to be mounted.
+
+For example, supposing @file{/tmp} and @file{/var/tmp} were to be merged
+into a new directory called @file{/mtmp}, with files in @file{/var/tmp}
+taking precedence. The following command could be used to achieve this
+effect:
+
+@example
+amd ... /mtmp union:/tmp:/var/tmp -type:=union
+@end example
+
+Currently, the unioned directories must @emph{not} be automounted. That
+would cause a deadlock. This seriously limits the current usefulness of
+this filesystem type and the problem will be addressed in a future
+release of @i{Amd}.
+
+Files created in the union directory are actually created in the last
+named directory. This is done by creating a wildcard entry which points
+to the correct directory. The wildcard entry is visible if the union
+directory is listed, so allowing you to see which directory has
+priority.
+
+The files visible in the union directory are computed at the time
+@i{Amd} is started, and are not kept uptodate with respect to the
+underlying directories. Similarly, if a link is removed, for example
+with the @samp{rm} command, it will be lost forever.
+
+@node Error Filesystem, Top-level Filesystem, Union Filesystem, Filesystem Types
+@comment node-name, next, previous, up
+@section Error Filesystem (@samp{type:=error})
+@cindex Error filesystem
+@cindex error, filesystem type
+@cindex Filesystem type; error
+
+The @dfn{error} filesystem type is used internally as a catch-all in
+the case where none of the other filesystems was selected, or some other
+error occurred.
+Lookups and mounts always fail with ``No such file or directory''.
+All other operations trivially succeed.
+
+The error filesystem is not directly accessible.
+
+@node Top-level Filesystem, Root Filesystem, Error Filesystem, Filesystem Types
+@comment node-name, next, previous, up
+@section Top-level Filesystem (@samp{type:=toplvl})
+@cindex Top level filesystem
+@cindex toplvl, filesystem type
+@cindex Filesystem type; toplvl
+
+The @dfn{toplvl} filesystems is derived from the @samp{auto} filesystem
+and is used to mount the top-level automount nodes. Requests of this
+type are automatically generated from the command line arguments and
+can also be passed in by using the ``-M'' option of the @dfn{Amq} command.
+
+@node Root Filesystem, Inheritance Filesystem, Top-level Filesystem, Filesystem Types
+@comment node-name, next, previous, up
+@section Root Filesystem
+@cindex Root filesystem
+@cindex root, filesystem type
+@cindex Filesystem type; root
+
+The @dfn{root} (@samp{type:=root}) filesystem type acts as an internal
+placeholder onto which @i{Amd} can pin @samp{toplvl} mounts. Only one
+node of this type need ever exist and one is created automatically
+during startup. The effect of creating a second root node is undefined.
+
+@node Inheritance Filesystem, , Root Filesystem, Filesystem Types
+@comment node-name, next, previous, up
+@section Inheritance Filesystem
+@cindex Inheritance filesystem
+@cindex Nodes generated on a restart
+@cindex inherit, filesystem type
+@cindex Filesystem type; inherit
+
+The @dfn{inheritance} (@samp{type:=inherit}) filesystem is not directly
+accessible. Instead, internal mount nodes of this type are
+automatically generated when @i{Amd} is started with the ``-r'' option.
+At this time the system mount table is scanned to locate any filesystems
+which are already mounted. If any reference to these filesystems is
+made through @i{Amd} then instead of attempting to mount it, @i{Amd}
+simulates the mount and @dfn{inherits} the filesystem. This allows a
+new version of @i{Amd} to be installed on a live system simply by
+killing the old daemon with @code{SIGTERM} and starting the new one.@refill
+
+This filesystem type is not generally visible externally, but it is
+possible that the output from @samp{amq -m} may list @samp{inherit} as
+the filesystem type. This happens when an inherit operation cannot
+be completed for some reason, usually because a fileserver is down.
+
+@node Run-time Administration, FSinfo, Filesystem Types, Top
+@comment node-name, next, previous, up
+@chapter Run-time Administration
+@cindex Run-time administration
+@cindex Amq command
+
+@menu
+* Starting Amd::
+* Stopping Amd::
+* Controlling Amd::
+@end menu
+
+@node Starting Amd, Stopping Amd, Run-time Administration, Run-time Administration
+@comment node-name, next, previous, up
+@section Starting @i{Amd}
+@cindex Starting Amd
+@cindex Additions to /etc/rc.local
+@cindex /etc/rc.local additions
+@cindex /etc/amd.start
+
+@i{Amd} is best started from @samp{/etc/rc.local}:
+
+@example
+if [ -f /etc/amd.start ]; then
+ sh /etc/amd.start; (echo -n ' amd') >/dev/console
+fi
+@end example
+
+@noindent
+The shell script, @samp{amd.start}, contains:
+
+@example
+#!/bin/sh -
+PATH=/etc:/bin:/usr/bin:/usr/ucb:$PATH export PATH
+
+#
+# Either name of logfile or "syslog"
+#
+LOGFILE=syslog
+#LOGFILE=/var/log/amd
+
+#
+# Figure out whether domain name is in host name
+# If the hostname is just the machine name then
+# pass in the name of the local domain so that the
+# hostnames in the map are domain stripped correctly.
+#
+case `hostname` in
+*.*) dmn= ;;
+*) dmn='-d doc.ic.ac.uk'
+esac
+
+#
+# Zap earlier log file
+#
+case "$LOGFILE" in
+*/*)
+ mv "$LOGFILE" "$LOGFILE"-
+ > "$LOGFILE"
+ ;;
+syslog)
+ : nothing
+ ;;
+esac
+
+cd /usr/sbin
+#
+# -r restart
+# -d dmn local domain
+# -w wait wait between unmount attempts
+# -l log logfile or "syslog"
+#
+eval ./amd -r $dmn -w 240 -l "$LOGFILE" \
+ /homes amd.homes -cache:=inc \
+ /home amd.home -cache:=inc \
+ /vol amd.vol -cache:=inc \
+ /n amd.net -cache:=inc
+@end example
+
+If the list of automount points and maps is contained in a file or NIS map
+it is easily incorporated onto the command line:
+
+@example
+...
+eval ./amd -r $dmn -w 240 -l "$LOGFILE" `ypcat -k auto.master`
+@end example
+
+@node Stopping Amd, Controlling Amd, Starting Amd, Run-time Administration
+@comment node-name, next, previous, up
+@section Stopping @i{Amd}
+@cindex Stopping Amd
+@cindex SIGTERM signal
+@cindex SIGINT signal
+
+@i{Amd} stops in response to two signals.
+
+@table @samp
+@item SIGTERM
+causes the top-level automount points to be unmounted and then @i{Amd}
+to exit. Any automounted filesystems are left mounted. They can be
+recovered by restarting @i{Amd} with the ``-r'' command line option.@refill
+
+@item SIGINT
+causes @i{Amd} to attempt to unmount any filesystems which it has
+automounted, in addition to the actions of @samp{SIGTERM}. This signal
+is primarly used for debugging.@refill
+@end table
+
+Actions taken for other signals are undefined.
+
+@node Controlling Amd, , Stopping Amd, Run-time Administration
+@comment node-name, next, previous, up
+@section Controlling @i{Amd}
+@cindex Controlling Amd
+@cindex Discovering what is going on at run-time
+@cindex Listing currently mounted filesystems
+
+It is sometimes desirable or necessary to exercise external control
+over some of @i{Amd}'s internal state. To support this requirement,
+@i{Amd} implements an RPC interface which is used by the @dfn{Amq} program.
+A variety of information is available.
+
+@i{Amq} generally applies an operation, specified by a single letter option,
+to a list of mount points. The default operation is to obtain statistics
+about each mount point. This is similar to the output shown above
+but includes information about the number and type of accesses to each
+mount point.
+
+@menu
+* Amq default:: Default command behaviour.
+* Amq -f option:: Flushing the map cache.
+* Amq -h option:: Controlling a non-local host.
+* Amq -m option:: Obtaining mount statistics.
+* Amq -M-option:: Mounting a volume.
+* Amq -s option:: Obtaining global statistics.
+* Amq -u option:: Forcing volumes to time out.
+* Amq -v option:: Version information.
+* Other Amq options:: Three other special options.
+@end menu
+
+@node Amq default, Amq -f option, Controlling Amd, Controlling Amd
+@comment node-name, next, previous, up
+@subsection @i{Amq} default information
+
+With no arguments, @dfn{Amq} obtains a brief list of all existing
+mounts created by @i{Amd}. This is different from the list displayed by
+@b{df}(1) since the latter only includes system mount points.
+
+@noindent
+The output from this option includes the following information:
+
+@itemize @bullet
+@item
+the automount point,
+@item
+the filesystem type,
+@item
+the mount map or mount information,
+@item
+the internal, or system mount point.
+@end itemize
+
+@noindent
+For example:
+
+@example
+/ root "root" sky:(pid75)
+/homes toplvl /usr/local/etc/amd.homes /homes
+/home toplvl /usr/local/etc/amd.home /home
+/homes/jsp nfs charm:/home/charm /a/charm/home/charm/jsp
+/homes/phjk nfs toytown:/home/toytown /a/toytown/home/toytown/ai/phjk
+@end example
+
+@noindent
+If an argument is given then statistics for that volume name will
+be output. For example:
+
+@example
+What Uid Getattr Lookup RdDir RdLnk Statfs Mounted@@
+/homes 0 1196 512 22 0 30 90/09/14 12:32:55
+/homes/jsp 0 0 0 0 1180 0 90/10/13 12:56:58
+@end example
+
+@table @code
+@item What
+the volume name.
+
+@item Uid
+ignored.
+
+@item Getattr
+the count of NFS @dfn{getattr} requests on this node. This should only be
+non-zero for directory nodes.
+
+@item Lookup
+the count of NFS @dfn{lookup} requests on this node. This should only be
+non-zero for directory nodes.
+
+@item RdDir
+the count of NFS @dfn{readdir} requests on this node. This should only
+be non-zero for directory nodes.
+
+@item RdLnk
+the count of NFS @dfn{readlink} requests on this node. This should be
+zero for directory nodes.
+
+@item Statfs
+the could of NFS @dfn{statfs} requests on this node. This should only
+be non-zero for top-level automount points.
+
+@item Mounted@@
+the date and time the volume name was first referenced.
+@end table
+
+@node Amq -f option, Amq -h option, Amq default, Controlling Amd
+@comment node-name, next, previous, up
+@subsection @i{Amq} -f option
+@cindex Flushing the map cache
+@cindex Map cache, flushing
+
+The ``-f'' option causes @i{Amd} to flush the internal mount map cache.
+This is useful for Hesiod maps since @i{Amd} will not automatically
+notice when they have been updated. The map cache can also be
+synchronised with the map source by using the @samp{sync} option
+(@pxref{Automount Filesystem}).@refill
+
+@node Amq -h option, Amq -m option, Amq -f option, Controlling Amd
+@comment node-name, next, previous, up
+@subsection @i{Amq} -h option
+@cindex Querying an alternate host
+
+By default the local host is used. In an HP-UX cluster the root server
+is used since that is the only place in the cluster where @i{Amd} will
+be running. To query @i{Amd} on another host the ``-h'' option should
+be used.
+
+@node Amq -m option, Amq -M-option, Amq -h option, Controlling Amd
+@comment node-name, next, previous, up
+@subsection @i{Amq} -m option
+
+The ``-m'' option displays similar information about mounted
+filesystems, rather than automount points. The output includes the
+following information:
+
+@itemize @bullet
+@item
+the mount information,
+@item
+the mount point,
+@item
+the filesystem type,
+@item
+the number of references to this filesystem,
+@item
+the server hostname,
+@item
+the state of the file server,
+@item
+any error which has occured.
+@end itemize
+
+For example:
+
+@example
+"root" truth:(pid602) root 1 localhost is up
+hesiod.home /home toplvl 1 localhost is up
+hesiod.vol /vol toplvl 1 localhost is up
+hesiod.homes /homes toplvl 1 localhost is up
+amy:/home/amy /a/amy/home/amy nfs 5 amy is up
+swan:/home/swan /a/swan/home/swan nfs 0 swan is up (Permission denied)
+ex:/home/ex /a/ex/home/ex nfs 0 ex is down
+@end example
+
+When the reference count is zero the filesystem is not mounted but
+the mount point and server information is still being maintained
+by @i{Amd}.
+
+@node Amq -M-option, Amq -s option, Amq -m option, Controlling Amd
+@comment node-name, next, previous, up
+@subsection @i{Amq} -M option
+
+The ``-M'' option passes a new map entry to @i{Amd} and waits for it to
+be evaluated, possibly causing a mount. For example, the following
+command would cause @samp{/home/toytown} on host @samp{toytown} to be
+mounted locally on @samp{/mnt/toytown}.
+
+@example
+amq -M '/mnt/toytown type:=nfs;rfs:=/home/toytown;rhost:=toytown;fs:=$@{key@}'
+@end example
+
+@i{Amd} applies some simple security checks before allowing this
+operation. The check tests whether the incoming request is from a
+privileged UDP port on the local machine. ``Permission denied'' is
+returned if the check fails.
+
+A future release of @i{Amd} will include code to allow the @b{mount}(8)
+command to mount automount points:
+
+@example
+mount -t amd /vol hesiod.vol
+@end example
+
+This will then allow @i{Amd} to be controlled from the standard system
+filesystem mount list.
+
+@node Amq -s option, Amq -u option, Amq -M-option, Controlling Amd
+@comment node-name, next, previous, up
+@subsection @i{Amq} -s option
+@cindex Global statistics
+@cindex Statistics
+
+The ``-s'' option displays global statistics. If any other options are specified
+or any filesystems named then this option is ignored. For example:
+
+@example
+requests stale mount mount unmount
+deferred fhandles ok failed failed
+1054 1 487 290 7017
+@end example
+
+@table @samp
+@item Deferred requests
+are those for which an immediate reply could not be constructed. For
+example, this would happen if a background mount was required.
+
+@item Stale filehandles
+counts the number of times the kernel passes a stale filehandle to @i{Amd}.
+Large numbers indicate problems.
+
+@item Mount ok
+counts the number of automounts which were successful.
+
+@item Mount failed
+counts the number of automounts which failed.
+
+@item Unmount failed
+counts the number of times a filesystem could not be unmounted. Very
+large numbers here indicate that the time between unmount attempts
+should be increased.
+@end table
+
+@node Amq -u option, Amq -v option, Amq -s option, Controlling Amd
+@comment node-name, next, previous, up
+@subsection @i{Amq} -u option
+@cindex Forcing filesystem to time out
+@cindex Unmounting a filesystem
+
+The ``-u'' option causes the time-to-live interval of the named mount
+points to be expired, thus causing an unmount attempt. This is the only
+safe way to unmount an automounted filesystem. It is not possible to
+unmount a filesystem which has been mounted with the @samp{nounmount}
+flag.
+
+@c The ``-H'' option informs @i{Amd} that the specified mount point has hung -
+@c as if its keepalive timer had expired.
+
+@node Amq -v option, Other Amq options, Amq -u option, Controlling Amd
+@comment node-name, next, previous, up
+@subsection @i{Amq} -v option
+@cindex Version information at run-time
+
+The ``-v'' option displays the version of @i{Amd} in a similar way to
+@i{Amd}'s ``-v'' option.
+
+@node Other Amq options, , Amq -v option, Controlling Amd
+@comment node-name, next, previous, up
+@subsection Other @i{Amq} options
+
+Three other operations are implemented. These modify the state of
+@i{Amd} as a whole, rather than any particular filesystem. The ``-l'',
+``-x'' and ``-D'' options have exactly the same effect as @i{Amd}'s
+corresponding command line options. The ``-l'' option is rejected by
+@i{Amd} in the current version for obvious security reasons. When
+@i{Amd} receives a ``-x''flag it limits the log options being modified
+to those which were not enabled at startup. This prevents a user
+turning @emph{off} any logging option which was specified at startup,
+though any which have been turned off since then can still be turned
+off. The ``-D'' option has a similar behaviour.
+
+@node FSinfo, Examples, Run-time Administration, Top
+@comment node-name, next, previous, up
+@chapter FSinfo
+@cindex FSinfo
+@cindex Filesystem info package
+
+@menu
+* FSinfo Overview:: Introduction to FSinfo.
+* Using FSinfo:: Basic concepts.
+* FSinfo Grammar:: Language syntax, semantics and examples.
+* FSinfo host definitions:: Defining a new host.
+* FSinfo host attributes:: Definable host attributes.
+* FSinfo filesystems:: Defining locally attached filesystems.
+* FSinfo static mounts:: Defining additional static mounts.
+* FSinfo automount definitions::
+* FSinfo Command Line Options::
+* FSinfo errors::
+@end menu
+
+@node FSinfo Overview, Using FSinfo, FSinfo, FSinfo
+@comment node-name, next, previous, up
+@section @i{FSinfo} overview
+@cindex FSinfo overview
+
+@i{FSinfo} is a filesystem management tool. It has been designed to
+work with @i{Amd} to help system administrators keep track of the ever
+increasing filesystem namespace under their control.
+
+The purpose of @i{FSinfo} is to generate all the important standard
+filesystem data files from a single set of input data. Starting with a
+single data source guarantees that all the generated files are
+self-consistent. One of the possible output data formats is a set of
+@i{Amd} maps which can be used amongst the set of hosts described in the
+input data.
+
+@i{FSinfo} implements a declarative language. This language is
+specifically designed for describing filesystem namespace and physical
+layouts. The basic declaration defines a mounted filesystem including
+its device name, mount point, and all the volumes and access
+permissions. @i{FSinfo} reads this information and builds an internal
+map of the entire network of hosts. Using this map, many different data
+formats can be produced including @file{/etc/fstab},
+@file{/etc/exports}, @i{Amd} mount maps and
+@file{/etc/bootparams}.@refill
+
+@node Using FSinfo, FSinfo Grammar, FSinfo Overview, FSinfo
+@comment node-name, next, previous, up
+@section Using @i{FSinfo}
+@cindex Using FSinfo
+
+The basic strategy when using @i{FSinfo} is to gather all the
+information about all disks on all machines into one set of
+declarations. For each machine being managed, the following data is
+required:
+
+@itemize @bullet
+@item
+Hostname
+@item
+List of all filesystems and, optionally, their mount points.
+@item
+Names of volumes stored on each filesystem.
+@item
+NFS export information for each volume.
+@item
+The list of static filesystem mounts.
+@end itemize
+
+The following information can also be entered into the same
+configuration files so that all data can be kept in one place.
+
+@itemize @bullet
+@item
+List of network interfaces
+@item
+IP address of each interface
+@item
+Hardware address of each interface
+@item
+Dumpset to which each filesystem belongs
+@item
+and more @dots{}
+@end itemize
+
+To generate @i{Amd} mount maps, the automount tree must also be defined
+(@pxref{FSinfo automount definitions}). This will have been designed at
+the time the volume names were allocated. Some volume names will not be
+automounted, so @i{FSinfo} needs an explicit list of which volumes
+should be automounted.@refill
+
+Hostnames are required at several places in the @i{FSinfo} language. It
+is important to stick to either fully qualified names or unqualified
+names. Using a mixture of the two will inevitably result in confusion.
+
+Sometimes volumes need to be referenced which are not defined in the set
+of hosts being managed with @i{FSinfo}. The required action is to add a
+dummy set of definitions for the host and volume names required. Since
+the files generated for those particular hosts will not be used on them,
+the exact values used is not critical.
+
+@node FSinfo Grammar, FSinfo host definitions, Using FSinfo, FSinfo
+@comment node-name, next, previous, up
+@section @i{FSinfo} grammar
+@cindex FSinfo grammar
+@cindex Grammar, FSinfo
+
+@i{FSinfo} has a relatively simple grammar. Distinct syntactic
+constructs exist for each of the different types of data, though they
+share a common flavour. Several conventions are used in the grammar
+fragments below.
+
+The notation, @i{list(}@t{xxx}@i{)}, indicates a list of zero or more
+@t{xxx}'s. The notation, @i{opt(}@t{xxx}@i{)}, indicates zero or one
+@t{xxx}. Items in double quotes, @i{eg} @t{"host"}, represent input
+tokens. Items in angle brackets, @i{eg} @var{<hostname>}, represent
+strings in the input. Strings need not be in double quotes, except to
+differentiate them from reserved words. Quoted strings may include the
+usual set of C ``@t{\}'' escape sequences with one exception: a
+backslash-newline-whitespace sequence is squashed into a single space
+character. To defeat this feature, put a further backslash at the start
+of the second line.
+
+At the outermost level of the grammar, the input consists of a
+sequence of host and automount declarations. These declarations are
+all parsed before they are analyzed. This means they can appear in
+any order and cyclic host references are possible.
+
+@example
+fsinfo : @i{list(}fsinfo_attr@i{)} ;
+
+fsinfo_attr : host | automount ;
+@end example
+
+@menu
+* FSinfo host definitions::
+* FSinfo automount definitions::
+@end menu
+
+@node FSinfo host definitions, FSinfo host attributes, FSinfo Grammar, FSinfo
+@comment node-name, next, previous, up
+@section @i{FSinfo} host definitions
+@cindex FSinfo host definitions
+@cindex Defining a host, FSinfo
+
+A host declaration consists of three parts: a set of machine attribute
+data, a list of filesystems physically attached to the machine, and a
+list of additional statically mounted filesystems.
+
+@example
+host : "host" host_data @i{list(}filesystem@i{@i{)}} @i{list(}mount@i{@i{)}} ;
+@end example
+
+Each host must be declared in this way exactly once. Such things as the
+hardware address, the architecture and operating system types and the
+cluster name are all specified within the @dfn{host data}.
+
+All the disks the machine has should then be described in the @dfn{list
+of filesystems}. When describing disks, you can specify what
+@dfn{volname} the disk/partition should have and all such entries are
+built up into a dictionary which can then be used for building the
+automounter maps.
+
+The @dfn{list of mounts} specifies all the filesystems that should be
+statically mounted on the machine.
+
+@menu
+* FSinfo host attributes::
+* FSinfo filesystems::
+* FSinfo static mounts::
+@end menu
+
+@node FSinfo host attributes, FSinfo filesystems, FSinfo host definitions , FSinfo host definitions
+@comment node-name, next, previous, up
+@section @i{FSinfo} host attributes
+@cindex FSinfo host attributes
+@cindex Defining host attributes, FSinfo
+
+The host data, @dfn{host_data}, always includes the @dfn{hostname}. In
+addition, several other host attributes can be given.
+
+@example
+host_data : @var{<hostname>}
+ | "@{" @i{list(}host_attrs@i{)} "@}" @var{<hostname>}
+ ;
+
+host_attrs : host_attr "=" @var{<string>}
+ | netif
+ ;
+
+host_attr : "config"
+ | "arch"
+ | "os"
+ | "cluster"
+ ;
+@end example
+
+The @dfn{hostname} is, typically, the fully qualified hostname of the
+machine.
+
+Examples:
+
+@example
+host dylan.doc.ic.ac.uk
+
+host @{
+ os = hpux
+ arch = hp300
+@} dougal.doc.ic.ac.uk
+@end example
+
+The options that can be given as host attributes are shown below.
+
+@menu
+* netif Option: FSinfo host netif
+* config Option: FSinfo host config
+* arch Option: FSinfo host arch
+* os Option: FSinfo host os
+* cluster Option: FSinfo host cluster
+@end menu
+
+@node FSinfo host netif, FSinfo host config, , FSinfo host attributes
+@comment node-name, next, previous, up
+@subsection netif Option
+
+This defines the set of network interfaces configured on the machine.
+The interface attributes collected by @i{FSinfo} are the IP address,
+subnet mask and hardware address. Multiple interfaces may be defined
+for hosts with several interfaces by an entry for each interface. The
+values given are sanity checked, but are currently unused for anything
+else.
+
+@example
+netif : "netif" @var{<string>} "@{" @i{list(}netif_attrs@i{)} "@}" ;
+
+netif_attrs : netif_attr "=" @var{<string>} ;
+
+netif_attr : "inaddr" | "netmask" | "hwaddr" ;
+@end example
+
+Examples:
+
+@example
+netif ie0 @{
+ inaddr = 129.31.81.37
+ netmask = 0xfffffe00
+ hwaddr = "08:00:20:01:a6:a5"
+@}
+
+netif ec0 @{ @}
+@end example
+
+@node FSinfo host config, FSinfo host arch, FSinfo host netif, FSinfo host attributes
+@comment node-name, next, previous, up
+@subsection config Option
+@cindex FSinfo config host attribute
+@cindex config, FSinfo host attribute
+
+This option allows you to specify configuration variables for the
+startup scripts (@file{rc} scripts). A simple string should immediately
+follow the keyword.
+
+Example:
+
+@example
+config "NFS_SERVER=true"
+config "ZEPHYR=true"
+@end example
+
+This option is currently unsupported.
+
+@node FSinfo host arch, FSinfo host os, FSinfo host config, FSinfo host attributes
+@comment node-name, next, previous, up
+@subsection arch Option
+@cindex FSinfo arch host attribute
+@cindex arch, FSinfo host attribute
+
+This defines the architecture of the machine. For example:
+
+@example
+arch = hp300
+@end example
+
+This is intended to be of use when building architecture specific
+mountmaps, however, the option is currently unsupported.
+
+@node FSinfo host os, FSinfo host cluster, FSinfo host arch, FSinfo host attributes
+@comment node-name, next, previous, up
+@subsection os Option
+@cindex FSinfo os host attribute
+@cindex os, FSinfo host attribute
+
+This defines the operating system type of the host. For example:
+
+@example
+os = hpux
+@end example
+
+This information is used when creating the @file{fstab} files, for
+example in choosing which format to use for the @file{fstab} entries
+within the file.
+
+@node FSinfo host cluster, , FSinfo host os, FSinfo host attributes
+@comment node-name, next, previous, up
+@subsection cluster Option
+@cindex FSinfo cluster host attribute
+@cindex cluster, FSinfo host attribute
+
+This is used for specifying in which cluster the machine belongs. For
+example:
+
+@example
+cluster = "theory"
+@end example
+
+The cluster is intended to be used when generating the automount maps,
+although it is currently unsupported.
+
+@node FSinfo filesystems, FSinfo static mounts, FSinfo host attributes, FSinfo host definitions
+@comment node-name, next, previous, up
+@section @i{FSinfo} filesystems
+@cindex FSinfo filesystems
+
+The list of physically attached filesystems follows the machine
+attributes. These should define all the filesystems available from this
+machine, whether exported or not. In addition to the device name,
+filesystems have several attributes, such as filesystem type, mount
+options, and @samp{fsck} pass number which are needed to generate
+@file{fstab} entries.
+
+@example
+filesystem : "fs" @var{<device>} "@{" @i{list(}fs_data@i{)} "@}" ;
+
+fs_data : fs_data_attr "=" @var{<string>}
+ | mount
+ ;
+
+fs_data_attr
+ : "fstype" | "opts" | "passno"
+ | "freq" | "dumpset" | "log"
+ ;
+@end example
+
+Here, @var{<device>} is the device name of the disk (for example,
+@file{/dev/dsk/2s0}). The device name is used for building the mount
+maps and for the @file{fstab} file. The attributes that can be
+specified are shown in the following section.
+
+The @i{FSinfo} configuration file for @code{dylan.doc.ic.ac.uk} is listed below.
+
+@example
+host dylan.doc.ic.ac.uk
+
+fs /dev/dsk/0s0 @{
+ fstype = swap
+@}
+
+fs /dev/dsk/0s0 @{
+ fstype = hfs
+ opts = rw,noquota,grpid
+ passno = 0;
+ freq = 1;
+ mount / @{ @}
+@}
+
+fs /dev/dsk/1s0 @{
+ fstype = hfs
+ opts = defaults
+ passno = 1;
+ freq = 1;
+ mount /usr @{
+ local @{
+ exportfs "dougal eden dylan zebedee brian"
+ volname /nfs/hp300/local
+ @}
+ @}
+@}
+
+fs /dev/dsk/2s0 @{
+ fstype = hfs
+ opts = defaults
+ passno = 1;
+ freq = 1;
+ mount default @{
+ exportfs "toytown_clients hangers_on"
+ volname /home/dylan/dk2
+ @}
+@}
+
+fs /dev/dsk/3s0 @{
+ fstype = hfs
+ opts = defaults
+ passno = 1;
+ freq = 1;
+ mount default @{
+ exportfs "toytown_clients hangers_on"
+ volname /home/dylan/dk3
+ @}
+@}
+
+fs /dev/dsk/5s0 @{
+ fstype = hfs
+ opts = defaults
+ passno = 1;
+ freq = 1;
+ mount default @{
+ exportfs "toytown_clients hangers_on"
+ volname /home/dylan/dk5
+ @}
+@}
+@end example
+
+@menu
+* fstype Option: FSinfo filesystems fstype
+* opts Option: FSinfo filesystems opts
+* passno Option: FSinfo filesystems passno
+* freq Option: FSinfo filesystems freq
+* mount Option: FSinfo filesystems mount
+* dumpset Option: FSinfo filesystems dumpset
+* log Option: FSinfo filesystems log
+@end menu
+
+@node FSinfo filesystems fstype, FSinfo filesystems opts, , FSinfo filesystems
+@comment node-name, next, previous, up
+@subsection fstype Option
+@cindex FSinfo fstype filesystems option
+@cindex fstype, FSinfo filesystems option
+@cindex export, FSinfo special fstype
+
+This specifies the type of filesystem being declared and will be placed
+into the @file{fstab} file as is. The value of this option will be
+handed to @code{mount} as the filesystem type---it should have such
+values as @code{4.2}, @code{nfs} or @code{swap}. The value is not
+examined for correctness.
+
+There is one special case. If the filesystem type is specified as
+@samp{export} then the filesystem information will not be added to the
+host's @file{fstab} information, but it will still be visible on the
+network. This is useful for defining hosts which contain referenced
+volumes but which are not under full control of @i{FSinfo}.
+
+Example:
+
+@example
+fstype = swap
+@end example
+
+@node FSinfo filesystems opts, FSinfo filesystems passno,FSinfo filesystems fstype, FSinfo filesystems
+@comment node-name, next, previous, up
+@subsection opts Option
+@cindex FSinfo opts filesystems option
+@cindex opts, FSinfo filesystems option
+
+This defines any options that should be given to @b{mount}(8) in the
+@file{fstab} file. For example:
+
+@example
+opts = rw,nosuid,grpid
+@end example
+
+@node FSinfo filesystems passno, FSinfo filesystems freq, FSinfo filesystems opts, FSinfo filesystems
+@comment node-name, next, previous, up
+@subsection passno Option
+@cindex FSinfo passno filesystems option
+@cindex passno, FSinfo filesystems option
+
+This defines the @b{fsck}(8) pass number in which to check the
+filesystem. This value will be placed into the @file{fstab} file.
+
+Example:
+
+@example
+passno = 1
+@end example
+
+@node FSinfo filesystems freq, FSinfo filesystems mount, FSinfo filesystems passno, FSinfo filesystems
+@comment node-name, next, previous, up
+@subsection freq Option
+@cindex FSinfo freq filesystems option
+@cindex freq, FSinfo filesystems option
+
+This defines the interval (in days) between dumps. The value is placed
+as is into the @file{fstab} file.
+
+Example:
+
+@example
+freq = 3
+@end example
+
+@node FSinfo filesystems mount, FSinfo filesystems dumpset, FSinfo filesystems freq, FSinfo filesystems
+@comment node-name, next, previous, up
+@subsection mount Option
+@cindex FSinfo mount filesystems option
+@cindex mount, FSinfo filesystems option
+@cindex exportfs, FSinfo mount option
+@cindex volname, FSinfo mount option
+@cindex sel, FSinfo mount option
+
+This defines the mountpoint at which to place the filesystem. If the
+mountpoint of the filesystem is specified as @code{default}, then the
+filesystem will be mounted in the automounter's tree under its volume
+name and the mount will automatically be inherited by the automounter.
+
+Following the mountpoint, namespace information for the filesystem may
+be described. The options that can be given here are @code{exportfs},
+@code{volname} and @code{sel}.
+
+The format is:
+
+@example
+mount : "mount" vol_tree ;
+
+vol_tree : @i{list(}vol_tree_attr@i{)} ;
+
+vol_tree_attr
+ : @var{<string>} "@{" @i{list(}vol_tree_info@i{)} vol_tree "@}" ;
+
+vol_tree_info
+ : "exportfs" @var{<export-data>}
+ | "volname" @var{<volname>}
+ | "sel" @var{<selector-list>}
+ ;
+@end example
+
+Example:
+
+@example
+mount default @{
+ exportfs "dylan dougal florence zebedee"
+ volname /vol/andrew
+@}
+@end example
+
+In the above example, the filesystem currently being declared will have
+an entry placed into the @file{exports} file allowing the filesystem to
+be exported to the machines @code{dylan}, @code{dougal}, @code{florence}
+and @code{zebedee}. The volume name by which the filesystem will be
+referred to remotely, is @file{/vol/andrew}. By declaring the
+mountpoint to be @code{default}, the filesystem will be mounted on the
+local machine in the automounter tree, where @i{Amd} will automatically
+inherit the mount as @file{/vol/andrew}.@refill
+
+@table @samp
+@item exportfs
+a string defining which machines the filesystem may be exported to.
+This is copied, as is, into the @file{exports} file---no sanity checking
+is performed on this string.@refill
+
+@item volname
+a string which declares the remote name by which to reference the
+filesystem. The string is entered into a dictionary and allows you to
+refer to this filesystem in other places by this volume name.@refill
+
+@item sel
+a string which is placed into the automounter maps as a selector for the
+filesystem.@refill
+
+@end table
+
+@node FSinfo filesystems dumpset, FSinfo filesystems log, FSinfo filesystems mount, FSinfo filesystems
+@comment node-name, next, previous, up
+@subsection dumpset Option
+@cindex FSinfo dumpset filesystems option
+@cindex dumpset, FSinfo filesystems option
+
+This provides support for Imperial College's local file backup tools and
+is not documented further here.
+
+@node FSinfo filesystems log, , FSinfo filesystems dumpset, FSinfo filesystems
+@comment node-name, next, previous, up
+@subsection log Option
+@cindex FSinfo log filesystems option
+@cindex log, FSinfo filesystems option
+
+Specifies the log device for the current filesystem. This is ignored if
+not required by the particular filesystem type.
+
+@node FSinfo static mounts, FSinfo automount definitions , FSinfo filesystems, FSinfo host definitions
+@comment node-name, next, previous, up
+@section @i{FSinfo} static mounts
+@cindex FSinfo static mounts
+@cindex Statically mounts filesystems, FSinfo
+
+Each host may also have a number of statically mounted filesystems. For
+example, the host may be a diskless workstation in which case it will
+have no @code{fs} declarations. In this case the @code{mount}
+declaration is used to determine from where its filesystems will be
+mounted. In addition to being added to the @file{fstab} file, this
+information can also be used to generate a suitable @file{bootparams}
+file.@refill
+
+@example
+mount : "mount" @var{<volname>} @i{list(}localinfo@i{)} ;
+
+localinfo : localinfo_attr @var{<string>} ;
+
+localinfo_attr
+ : "as"
+ | "from"
+ | "fstype"
+ | "opts"
+ ;
+@end example
+
+The filesystem specified to be mounted will be searched for in the
+dictionary of volume names built when scanning the list of hosts'
+definitions.
+
+The attributes have the following semantics:
+@table @samp
+@item from @var{machine}
+mount the filesystem from the machine with the hostname of
+@dfn{machine}.@refill
+
+@item as @var{mountpoint}
+mount the filesystem locally as the name given, in case this is
+different from the advertised volume name of the filesystem.
+
+@item opts @var{options}
+native @b{mount}(8) options.
+
+@item fstype @var{type}
+type of filesystem to be mounted.
+@end table
+
+An example:
+
+@example
+mount /export/exec/hp300/local as /usr/local
+@end example
+
+If the mountpoint specified is either @file{/} or @file{swap}, the
+machine will be considered to be booting off the net and this will be
+noted for use in generating a @file{bootparams} file for the host which
+owns the filesystems.
+
+@node FSinfo automount definitions, FSinfo Command Line Options, FSinfo static mounts, FSinfo
+@comment node-name, next, previous, up
+@section Defining an @i{Amd} Mount Map in @i{FSinfo}
+@cindex FSinfo automount definitions
+@cindex Defining an Amd mount map, FSinfo
+
+The maps used by @i{Amd} can be constructed from @i{FSinfo} by defining
+all the automount trees. @i{FSinfo} takes all the definitions found and
+builds one map for each top level tree.
+
+The automount tree is usually defined last. A single automount
+configuration will usually apply to an entire management domain. One
+@code{automount} declaration is needed for each @i{Amd} automount point.
+@i{FSinfo} determines whether the automount point is @dfn{direct}
+(@pxref{Direct Automount Filesystem}) or @dfn{indirect}
+(@pxref{Top-level Filesystem}). Direct automount points are
+distinguished by the fact that there is no underlying
+@dfn{automount_tree}.@refill
+
+@example
+automount : "automount" opt(auto_opts@i{)} automount_tree ;
+
+auto_opts : "opts" @var{<mount-options>} ;
+
+automount_tree
+ : @i{list(}automount_attr@i{)}
+ ;
+
+automount_attr
+ : @var{<string>} "=" @var{<volname>}
+ | @var{<string>} "->" @var{<symlink>}
+ | @var{<string>} "@{" automount_tree "@}"
+ ;
+@end example
+
+If @var{<mount-options>} is given, then it is the string to be placed in
+the maps for @i{Amd} for the @code{opts} option.
+
+A @dfn{map} is typically a tree of filesystems, for example @file{home}
+normally contains a tree of filesystems representing other machines in
+the network.
+
+A map can either be given as a name representing an already defined
+volume name, or it can be a tree. A tree is represented by placing
+braces after the name. For example, to define a tree @file{/vol}, the
+following map would be defined:
+
+@example
+automount /vol @{ @}
+@end example
+
+Within a tree, the only items that can appear are more maps.
+For example:
+
+@example
+automount /vol @{
+ andrew @{ @}
+ X11 @{ @}
+@}
+@end example
+
+In this case, @i{FSinfo} will look for volumes named @file{/vol/andrew}
+and @file{/vol/X11} and a map entry will be generated for each. If the
+volumes are defined more than once, then @i{FSinfo} will generate
+a series of alternate entries for them in the maps.@refill
+
+Instead of a tree, either a link (@var{name} @code{->}
+@var{destination}) or a reference can be specified (@var{name} @code{=}
+@var{destination}). A link creates a symbolic link to the string
+specified, without further processing the entry. A reference will
+examine the destination filesystem and optimise the reference. For
+example, to create an entry for @code{njw} in the @file{/homes} map,
+either of the two forms can be used:@refill
+
+@example
+automount /homes @{
+ njw -> /home/dylan/njw
+@}
+@end example
+
+or
+
+@example
+automount /homes @{
+ njw = /home/dylan/njw
+@}
+@end example
+
+In the first example, when @file{/homes/njw} is referenced from @i{Amd},
+a link will be created leading to @file{/home/dylan/njw} and the
+automounter will be referenced a second time to resolve this filename.
+The map entry would be:
+
+@example
+njw type:=link;fs:=/home/dylan/njw
+@end example
+
+In the second example, the destination directory is analysed and found
+to be in the filesystem @file{/home/dylan} which has previously been
+defined in the maps. Hence the map entry will look like:
+
+@example
+njw rhost:=dylan;rfs:=/home/dylan;sublink:=njw
+@end example
+
+Creating only one symbolic link, and one access to @i{Amd}.
+
+@c ---------------------------------------------
+@node FSinfo Command Line Options, FSinfo errors, FSinfo automount definitions, FSinfo
+@comment node-name, next, previous, up
+@section @i{FSinfo} Command Line Options
+@cindex FSinfo command line options
+@cindex Command line options, FSinfo
+
+@i{FSinfo} is started from the command line by using the command:
+
+@example
+fsinfo [@i{options}] files ...
+@end example
+
+The input to @i{FSinfo} is a single set of definitions of machines and
+automount maps. If multiple files are given on the command-line, then
+the files are concatenated together to form the input source. The files
+are passed individually through the C pre-processor before being parsed.
+
+Several options define a prefix for the name of an output file. If the
+prefix is not specified no output of that type is produced. The suffix
+used will correspond either to the hostname to which a file belongs, or
+to the type of output if only one file is produced. Dumpsets and the
+@file{bootparams} file are in the latter class. To put the output into
+a subdirectory simply put a @file{/} at the end of the prefix, making
+sure that the directory has already been made before running
+@samp{fsinfo}.
+
+@menu
+* -a FSinfo Option:: Amd automount directory:
+* -b FSinfo Option:: Prefix for bootparams files.
+* -d FSinfo Option:: Prefix for dumpset data files.
+* -e FSinfo Option:: Prefix for exports files.
+* -f FSinfo Option:: Prefix for fstab files.
+* -h FSinfo Option:: Local hostname.
+* -m FSinfo Option:: Prefix for automount maps.
+* -q FSinfo Option:: Ultra quiet mode.
+* -v FSinfo Option:: Verbose mode.
+* -I FSinfo Option:: Define new #include directory.
+* -D-FSinfo Option:: Define macro.
+* -U FSinfo Option:: Undefine macro.
+@end menu
+
+@node -a FSinfo Option, -b FSinfo Option, FSinfo Command Line Options, FSinfo Command Line Options
+@comment node-name, next, previous, up
+@subsection @code{-a} @var{autodir}
+
+Specifies the directory name in which to place the automounter's
+mountpoints. This defaults to @file{/a}. Some sites have the autodir set
+to be @file{/amd}, and this would be achieved by:
+
+@example
+fsinfo -a /amd ...
+@end example
+
+@node -b FSinfo Option, -d FSinfo Option, -a FSinfo Option, FSinfo Command Line Options
+@comment node-name, next, previous, up
+@subsection @code{-b} @var{bootparams}
+@cindex bootparams, FSinfo prefix
+
+This specifies the prefix for the @file{bootparams} filename. If it is
+not given, then the file will not be generated. The @file{bootparams}
+file will be constructed for the destination machine and will be placed
+into a file named @file{bootparams} and prefixed by this string. The
+file generated contains a list of entries describing each diskless
+client that can boot from the destination machine.
+
+As an example, to create a @file{bootparams} file in the directory
+@file{generic}, the following would be used:
+
+@example
+fsinfo -b generic/ ...
+@end example
+
+@node -d FSinfo Option, -e FSinfo Option, -b FSinfo Option, FSinfo Command Line Options
+@comment node-name, next, previous, up
+@subsection @code{-d} @var{dumpsets}
+@cindex dumpset, FSinfo prefix
+
+This specifies the prefix for the @file{dumpsets} file. If it is not
+specified, then the file will not be generated. The file will be for
+the destination machine and will be placed into a filename
+@file{dumpsets}, prefixed by this string. The @file{dumpsets} file is
+for use by Imperial College's local backup system.
+
+For example, to create a dumpsets file in the directory @file{generic},
+then you would use the following:
+
+@example
+fsinfo -d generic/ ...
+@end example
+
+@node -e FSinfo Option, -f FSinfo Option, -d FSinfo Option, FSinfo Command Line Options
+@comment node-name, next, previous, up
+@subsection @code{-e} @var{exportfs}
+@cindex exports, FSinfo prefix
+
+Defines the prefix for the @file{exports} files. If it is not given,
+then the file will not be generated. For each machine defined in the
+configuration files as having disks, an @file{exports} file is
+constructed and given a filename determined by the name of the machine,
+prefixed with this string. If a machine is defined as diskless, then no
+@file{exports} file will be created for it. The files contain entries
+for directories on the machine that may be exported to clients.
+
+Example: To create the @file{exports} files for each diskful machine
+and place them into the directory @file{exports}:
+
+@example
+fsinfo -e exports/ ...
+@end example
+
+@node -f FSinfo Option, -h FSinfo Option, -e FSinfo Option, FSinfo Command Line Options
+@comment node-name, next, previous, up
+@subsection @code{-f} @var{fstab}
+@cindex fstab, FSinfo prefix
+
+This defines the prefix for the @file{fstab} files. The files will only
+be created if this prefix is defined. For each machine defined in the
+configuration files, a @file{fstab} file is created with the filename
+determined by prefixing this string with the name of the machine. These
+files contain entries for filesystems and partitions to mount at boot
+time.
+
+Example, to create the files in the directory @file{fstabs}:
+
+@example
+fsinfo -f fstabs/ ...
+@end example
+
+@node -h FSinfo Option, -m FSinfo Option, -f FSinfo Option, FSinfo Command Line Options
+@comment node-name, next, previous, up
+@subsection @code{-h} @var{hostname}
+@cindex hostname, FSinfo command line option
+
+Defines the hostname of the destination machine to process for. If this
+is not specified, it defaults to the local machine name, as returned by
+@b{gethostname}(2).
+
+Example:
+
+@example
+fsinfo -h dylan.doc.ic.ac.uk ...
+@end example
+
+@node -m FSinfo Option, -q FSinfo Option, -h FSinfo Option, FSinfo Command Line Options
+@comment node-name, next, previous, up
+@subsection @code{-m} @var{mount-maps}
+@cindex maps, FSinfo command line option
+
+Defines the prefix for the automounter files. The maps will only be
+produced if this prefix is defined. The mount maps suitable for the
+network defined by the configuration files will be placed into files
+with names calculated by prefixing this string to the name of each map.
+
+For example, to create the automounter maps and place them in the
+directory @file{automaps}:
+
+@example
+fsinfo -m automaps/ ...
+@end example
+
+@node -q FSinfo Option, -v FSinfo Option, -m FSinfo Option, FSinfo Command Line Options
+@comment node-name, next, previous, up
+@subsection @code{-q}
+@cindex quiet, FSinfo command line option
+
+Selects quiet mode. @i{FSinfo} suppress the ``running commentary'' and
+only outputs any error messages which are generated.
+
+@node -v FSinfo Option, -D-FSinfo Option, -q FSinfo Option, FSinfo Command Line Options
+@comment node-name, next, previous, up
+@subsection @code{-v}
+@cindex verbose, FSinfo command line option
+
+Selects verbose mode. When this is activated, the program will display
+more messages, and display all the information discovered when
+performing the semantic analysis phase. Each verbose message is output
+to @file{stdout} on a line starting with a @samp{#} character.
+
+@node -D-FSinfo Option, -I FSinfo Option, -v FSinfo Option, FSinfo Command Line Options
+@comment node-name, next, previous, up
+@subsection @code{-D} @var{name[=defn]}
+
+Defines a symbol @dfn{name} for the preprocessor when reading the
+configuration files. Equivalent to @code{#define} directive.
+
+@node -I FSinfo Option, -U FSinfo Option, -D-FSinfo Option, FSinfo Command Line Options
+@comment node-name, next, previous, up
+@subsection @code{-I} @var{directory}
+
+This option is passed into the preprocessor for the configuration files.
+It specifies directories in which to find include files
+
+@node -U FSinfo Option, , -I FSinfo Option, FSinfo Command Line Options
+@comment node-name, next, previous, up
+@subsection @code{-U} @var{name}
+
+Removes any initial definition of the symbol @dfn{name}. Inverse of the
+@code{-D} option.
+
+@node FSinfo errors, , FSinfo Command Line Options, FSinfo
+@comment node-name, next, previous, up
+@section Errors produced by @i{FSinfo}
+@cindex FSinfo error messages
+
+The following table documents the errors and warnings which @i{FSinfo} may produce.
+
+@table @t
+
+@item can't open @var{filename} for writing
+Occurs if any errors are encountered when opening an output file.@refill
+
+@item unknown host attribute
+Occurs if an unrecognised keyword is used when defining a host.@refill
+
+@item unknown filesystem attribute
+Occurs if an unrecognised keyword is used when defining a host's
+filesystems.@refill
+
+@item not allowed '/' in a directory name
+When reading the configuration input, if there is a filesystem
+definition which contains a pathname with multiple directories for any
+part of the mountpoint element, and it is not a single absolute path,
+then this message will be produced by the parser.@refill
+
+@item unknown directory attribute
+If an unknown keyword is found while reading the definition of a hosts's
+filesystem mount option.
+
+@item unknown mount attribute
+Occurs if an unrecognised keyword is found while parsing the list of
+static mounts.@refill
+
+@item " expected
+Occurs if an unescaped newline is found in a quoted string.
+
+@item unknown \ sequence
+Occurs if an unknown escape sequence is found inside a string. Within a
+string, you can give the standard C escape sequences for strings, such
+as newlines and tab characters.@refill
+
+@item @var{filename}: cannot open for reading
+If a file specified on the command line as containing configuration data
+could not be opened.@refill
+
+@item end of file within comment
+A comment was unterminated before the end of one of the configuration
+files.
+
+@item host field "@var{field-name}" already set
+If duplicate definitions are given for any of the fields with a host
+definition.
+
+@item duplicate host @var{hostname}!
+If a host has more than one definition.
+
+@item netif field @var{field-name} already set
+Occurs if you attempt to define an attribute of an interface more than
+once.
+
+@item malformed IP dotted quad: @var{address}
+If the Internet address of an interface is incorrectly specified. An
+Internet address definition is handled to @b{inet_addr}(3N) to see if it
+can cope. If not, then this message will be displayed.
+
+@item malformed netmask: @var{netmask}
+If the netmask cannot be decoded as though it were a hexadecimal number,
+then this message will be displayed. It will typically be caused by
+incorrect characters in the @var{netmask} value.@refill
+
+@item fs field "@var{field-name}" already set
+Occurs when multiple definitions are given for one of the attributes of a
+host's filesystem.
+
+@item mount tree field "@var{field-name}" already set
+Occurs when the @var{field-name} is defined more than once during the
+definition of a filesystems mountpoint.
+
+@item mount field "@var{field-name}" already set
+Occurs when a static mount has multiple definitions of the same field.
+
+@item no disk mounts on @var{hostname}
+If there are no static mounts, nor local disk mounts specified for a
+machine, this message will be displayed.
+
+@item @var{host}:@var{device} needs field "@var{field-name}"
+Occurs when a filesystem is missing a required field. @var{field-name} could
+be one of @code{fstype}, @code{opts}, @code{passno} or
+@code{mount}.@refill
+
+@item @var{filesystem} has a volname but no exportfs data
+Occurs when a volume name is declared for a file system, but the string
+specifying what machines the filesystem can be exported to is
+missing.
+
+@item sub-directory @var{directory} of @var{directory-tree} starts with '/'
+Within the filesystem specification for a host, if an element
+@var{directory} of the mountpoint begins with a @samp{/} and it is not
+the start of the tree.@refill
+
+@item @var{host}:@var{device} has no mount point
+Occurs if the @samp{mount} option is not specified for a host's
+filesystem.@refill
+
+@item @var{host}:@var{device} has more than one mount point
+Occurs if the mount option for a host's filesystem specifies multiple
+trees at which to place the mountpoint.@refill
+
+@item no volname given for @var{host}:@var{device}
+Occurs when a filesystem is defined to be mounted on @file{default}, but
+no volume name is given for the file system, then the mountpoint cannot
+be determined.@refill
+
+@item @var{host}:mount field specified for swap partition
+Occurs if a mountpoint is given for a filesystem whose type is declared
+to be @code{swap}.@refill
+
+@item ambiguous mount: @var{volume} is a replicated filesystem
+If several filesystems are declared as having the same volume name, they
+will be considered replicated filesystems. To mount a replicated
+filesystem statically, a specific host will need to be named, to say
+which particular copy to try and mount, else this error will
+result.@refill
+
+@item cannot determine localname since volname @var{volume} is not uniquely defined
+If a volume is replicated and an attempt is made to mount the filesystem
+statically without specifying a local mountpoint, @i{FSinfo} cannot
+calculate a mountpoint, as the desired pathname would be
+ambiguous.@refill
+
+@item volname @var{volume} is unknown
+Occurs if an attempt is made to mount or reference a volume name which
+has not been declared during the host filesystem definitions.@refill
+
+@item volname @var{volume} not exported from @var{machine}
+Occurs if you attempt to mount the volume @var{volume} from a machine
+which has not declared itself to have such a filesystem
+available.@refill
+
+@item network booting requires both root and swap areas
+Occurs if a machine has mount declarations for either the root partition
+or the swap area, but not both. You cannot define a machine to only
+partially boot via the network.@refill
+
+@item unknown volname @var{volume} automounted @i{[} on <name> @i{]}
+Occurs if @var{volume} is used in a definition of an automount map but the volume
+name has not been declared during the host filesystem definitions.@refill
+
+@item not allowed '/' in a directory name
+Occurs when a pathname with multiple directory elements is specified as
+the name for an automounter tree. A tree should only have one name at
+each level.
+
+@item @var{device} has duplicate exportfs data
+Produced if the @samp{exportfs} option is used multiple times within the
+same branch of a filesytem definition. For example, if you attempt to
+set the @samp{exportfs} data at different levels of the mountpoint
+directory tree.@refill
+
+@item sub-directory of @var{directory-tree} is named "default"
+@samp{default} is a keyword used to specify if a mountpoint should be
+automatically calculated by @i{FSinfo}. If you attempt to specify a
+directory name as this, it will use the filename of @file{default} but
+will produce this warning.@refill
+
+@item pass number for @var{host}:@var{device} is non-zero
+Occurs if @var{device} has its @samp{fstype} declared to be @samp{swap}
+or @samp{export} and the @b{fsck}(8) pass number is set. Swap devices should not be
+fsck'd. @xref{FSinfo filesystems fstype}@refill
+
+@item dump frequency for @var{host}:@var{device} is non-zero
+Occurs if @var{device} has its @samp{fstype} declared to be @samp{swap}
+or @samp{export} and the @samp{dump} option is set to a value greater
+than zero. Swap devices should not be dumped.@refill
+
+@end table
+
+@node Examples, Internals, FSinfo, Top
+@comment node-name, next, previous, up
+@chapter Examples
+
+@menu
+* User Filesystems::
+* Home Directories::
+* Architecture Sharing::
+* Wildcard Names::
+* rwho servers::
+* /vol::
+@end menu
+
+@node User Filesystems, Home Directories, Examples, Examples
+@comment node-name, next, previous, up
+@section User Filesystems
+@cindex User filesystems
+@cindex Mounting user filesystems
+
+With more than one fileserver, the directories most frequently
+cross-mounted are those containing user home directories. A common
+convention used at Imperial College is to mount the user disks under
+@t{/home/}@i{machine}.
+
+Typically, the @samp{/etc/fstab} file contained a long list of entries
+such as:
+
+@example
+@i{machine}:/home/@i{machine} /home/@i{machine} nfs ...
+@end example
+
+for each fileserver on the network.
+
+There are numerous problems with this system. The mount list can become
+quite large and some of the machines may be down when a system is
+booted. When a new fileserver is installed, @samp{/etc/fstab} must be
+updated on every machine, the mount directory created and the filesystem
+mounted.
+
+In many environments most people use the same few workstations, but
+it is convenient to go to a colleague's machine and access your own
+files. When a server goes down, it can cause a process on a client
+machine to hang. By minimising the mounted filesystems to only include
+those actively being used, there is less chance that a filesystem will
+be mounted when a server goes down.
+
+The following is a short extract from a map taken from a research fileserver
+at Imperial College.
+
+Note the entry for @samp{localhost} which is used for users such as
+the operator (@samp{opr}) who have a home directory on most machine as
+@samp{/home/localhost/opr}.
+
+@example
+/defaults opts:=rw,intr,grpid,nosuid
+charm host!=$@{key@};type:=nfs;rhost:=$@{key@};rfs:=/home/$@{key@} \
+ host==$@{key@};type:=ufs;dev:=/dev/xd0g
+#
+...
+
+#
+localhost type:=link;fs:=$@{host@}
+...
+#
+# dylan has two user disks so have a
+# top directory in which to mount them.
+#
+dylan type:=auto;fs:=$@{map@};pref:=$@{key@}/
+#
+dylan/dk2 host!=dylan;type:=nfs;rhost:=dylan;rfs:=/home/$@{key@} \
+ host==dylan;type:=ufs;dev:=/dev/dsk/2s0
+#
+dylan/dk5 host!=dylan;type:=nfs;rhost:=dylan;rfs:=/home/$@{key@} \
+ host==dylan;type:=ufs;dev:=/dev/dsk/5s0
+...
+#
+toytown host!=$@{key@};type:=nfs;rhost:=$@{key@};rfs:=/home/$@{key@} \
+ host==$@{key@};type:=ufs;dev:=/dev/xy1g
+...
+#
+zebedee host!=$@{key@};type:=nfs;rhost:=$@{key@};rfs:=/home/$@{key@} \
+ host==$@{key@};type:=ufs;dev:=/dev/dsk/1s0
+#
+# Just for access...
+#
+gould type:=auto;fs:=$@{map@};pref:=$@{key@}/
+gould/staff host!=gould;type:=nfs;rhost:=gould;rfs:=/home/$@{key@}
+#
+gummo host!=$@{key@};type:=nfs;rhost:=$@{key@};rfs:=/home/$@{key@}
+...
+@end example
+
+This map is shared by most of the machines listed so on those
+systems any of the user disks is accessible via a consistent name.
+@i{Amd} is started with the following command
+
+@example
+amd /home amd.home
+@end example
+
+Note that when mounting a remote filesystem, the @dfn{automounted}
+mount point is referenced, so that the filesystem will be mounted if
+it is not yet (at the time the remote @samp{mountd} obtains the file handle).
+
+@node Home Directories, Architecture Sharing, User Filesystems, Examples
+@comment node-name, next, previous, up
+@section Home Directories
+@cindex Home directories
+@cindex Example of mounting home directories
+@cindex Mount home directories
+
+One convention for home directories is to locate them in @samp{/homes}
+so user @samp{jsp}'s home directory is @samp{/homes/jsp}. With more
+than a single fileserver it is convenient to spread user files across
+several machines. All that is required is a mount-map which converts
+login names to an automounted directory.
+
+Such a map might be started by the command:
+
+@example
+amd /homes amd.homes
+@end example
+
+where the map @samp{amd.homes} contained the entries:
+
+@example
+/defaults type:=link # All the entries are of type:=link
+jsp fs:=/home/charm/jsp
+njw fs:=/home/dylan/dk5/njw
+...
+phjk fs:=/home/toytown/ai/phjk
+sjv fs:=/home/ganymede/sjv
+@end example
+
+Whenever a login name is accessed in @samp{/homes} a symbolic link
+appears pointing to the real location of that user's home directory. In
+this example, @samp{/homes/jsp} would appear to be a symbolic link
+pointing to @samp{/home/charm/jsp}. Of course, @samp{/home} would also
+be an automount point.
+
+This system causes an extra level of symbolic links to be used.
+Although that turns out to be relatively inexpensive, an alternative is
+to directly mount the required filesystems in the @samp{/homes}
+map. The required map is simple, but long, and its creation is best automated.
+The entry for @samp{jsp} could be:
+
+@example
+jsp -sublink:=$@{key@};rfs:=/home/charm \
+ host==charm;type:=ufs;dev:=/dev/xd0g \
+ host!=charm;type:=nfs;rhost:=charm
+@end example
+
+This map can become quite big if it contains a large number of entries.
+By combining two other features of @i{Amd} it can be greatly simplified.
+
+First the UFS partitions should be mounted under the control of
+@samp{/etc/fstab}, taking care that they are mounted in the same place
+that @i{Amd} would have automounted them. In most cases this would be
+something like @samp{/a/@dfn{host}/home/@dfn{host}} and
+@samp{/etc/fstab} on host @samp{charm} would have a line:@refill
+
+@example
+/dev/xy0g /a/charm/home/charm 4.2 rw,nosuid,grpid 1 5
+@end example
+
+The map can then be changed to:
+
+@example
+/defaults type:=nfs;sublink:=$@{key@};opts:=rw,intr,nosuid,grpid
+jsp rhost:=charm;rfs:=/home/charm
+njw rhost:=dylan;rfs:=/home/dylan/dk5
+...
+phjk rhost:=toytown;rfs:=/home/toytown;sublink:=ai/$@{key@}
+sjv rhost:=ganymede;rfs:=/home/ganymede
+@end example
+
+This map operates as usual on a remote machine (@i{ie} @code{$@{host@}}
+not equal to @code{$@{rhost@}}). On the machine where the filesystem is
+stored (@i{ie} @code{$@{host@}} equal to @code{$@{rhost@}}), @i{Amd}
+will construct a local filesystem mount point which corresponds to the
+name of the locally mounted UFS partition. If @i{Amd} is started with
+the ``-r'' option then instead of attempting an NFS mount, @i{Amd} will
+simply inherit the UFS mount (@pxref{Inheritance Filesystem}). If
+``-r'' is not used then a loopback NFS mount will be made. This type of
+mount is known to cause a deadlock on many systems.
+
+@node Architecture Sharing, Wildcard Names, Home Directories, Examples
+@comment node-name, next, previous, up
+@section Architecture Sharing
+@cindex Architecture sharing
+@cindex Sharing a fileserver between architectures
+@cindex Architecture dependent volumes
+
+@c %At the moment some of the research machines have sets of software
+@c %mounted in @samp{/vol}. This contains subdirectories for \TeX,
+@c %system sources, local sources, prolog libraries and so on.
+Often a filesystem will be shared by machines of different architectures.
+Separate trees can be maintained for the executable images for each
+architecture, but it may be more convenient to have a shared tree,
+with distinct subdirectories.
+
+A shared tree might have the following structure on the fileserver (called
+@samp{fserver} in the example):
+
+@example
+local/tex
+local/tex/fonts
+local/tex/lib
+local/tex/bin
+local/tex/bin/sun3
+local/tex/bin/sun4
+local/tex/bin/hp9000
+...
+@end example
+
+In this example, the subdirectories of @samp{local/tex/bin} should be
+hidden when accessed via the automount point (conventionally @samp{/vol}).
+A mount-map for @samp{/vol} to achieve this would look like:
+
+@example
+/defaults sublink:=$@{/key@};rhost:=fserver;type:=link
+tex type:=auto;fs:=$@{map@};pref:=$@{key@}/
+tex/fonts host!=fserver;type:=nfs;rfs:=/vol/tex \
+ host==fserver;fs:=/usr/local/tex
+tex/lib host!=fserver;type:=nfs;rfs:=/vol/tex \
+ host==fserver;fs:=/usr/local/tex
+tex/bin -sublink:=$@{/key@}/$@{arch@} host!=fserver;type:=nfs;rfs:=/vol/tex \
+ host:=fserver;fs:=/usr/local/tex
+@end example
+
+When @samp{/vol/tex/bin} is referenced, the current machine architecture
+is automatically appended to the path by the @code{$@{sublink@}}
+variable. This means that users can have @samp{/vol/tex/bin} in their
+@samp{PATH} without concern for architecture dependencies.
+
+@node Wildcard Names, rwho servers, Architecture Sharing, Examples
+@comment node-name, next, previous, up
+@section Wildcard names & Replicated Servers
+
+By using the wildcard facility, @i{Amd} can @dfn{overlay} an existing
+directory with additional entries.
+The system files are usually mounted under @samp{/usr}. If instead
+@i{Amd} is mounted on @samp{/usr}, additional
+names can be overlayed to augment or replace names in the ``master'' @samp{/usr}.
+A map to do this would have the form:
+
+@example
+local type:=auto;fs:=local-map
+share type:=auto;fs:=share-map
+* -type:=nfs;rfs:=/export/exec/$@{arch@};sublink:="$@{key@}" \
+ rhost:=fserv1 rhost:=fserv2 rhost:=fserv3
+@end example
+
+Note that the assignment to @code{$@{sublink@}} is surrounded by double
+quotes to prevent the incoming key from causing the map to be
+misinterpreted. This map has the effect of directing any access to
+@samp{/usr/local} or @samp{/usr/share} to another automount point.
+
+In this example, it is assumed that the @samp{/usr} files are replicated
+on three fileservers: @samp{fserv1}, @samp{fserv2} and @samp{fserv3}.
+For any references other than to @samp{local} and @samp{share} one of
+the servers is used and a symbolic link to
+@t{$@{autodir@}/$@{rhost@}/export/exec/$@{arch@}/@i{whatever}} is
+returned once an appropriate filesystem has been mounted.@refill
+
+@node rwho servers, /vol, Wildcard Names, Examples
+@comment node-name, next, previous, up
+@section @samp{rwho} servers
+@cindex rwho servers
+@cindex Architecture specific mounts
+@cindex Example of architecture specific mounts
+
+The @samp{/usr/spool/rwho} directory is a good candidate for automounting.
+For efficiency reasons it is best to capture the rwho data on a small
+number of machines and then mount that information onto a large number
+of clients. The data written into the rwho files is byte order dependent
+so only servers with the correct byte ordering can be used by a client:
+
+@example
+/defaults type:=nfs
+usr/spool/rwho -byte==little;rfs:=/usr/spool/rwho \
+ rhost:=vaxA rhost:=vaxB \
+ || -rfs:=/usr/spool/rwho \
+ rhost:=sun4 rhost:=hp300
+@end example
+
+@node /vol, , rwho servers, Examples
+@comment node-name, next, previous, up
+@section @samp{/vol}
+@cindex /vol
+@cindex Catch-all mount point
+@cindex Generic volume name
+
+@samp{/vol} is used as a catch-all for volumes which do not have other
+conventional names.
+
+Below is part of the @samp{/vol} map for the domain @samp{doc.ic.ac.uk}.
+The @samp{r+d} tree is used for new or experimental software that needs
+to be available everywhere without installing it on all the fileservers.
+Users wishing to try out the new software then simply include
+@samp{/vol/r+d/@{bin,ucb@}} in their path.@refill
+
+The main tree resides on one host @samp{gould.doc.ic.ac.uk}, which has
+different @samp{bin}, @samp{etc}, @samp{lib} and @samp{ucb}
+sub-directories for each machine architecture. For example,
+@samp{/vol/r+d/bin} for a Sun-4 would be stored in the sub-directory
+@samp{bin/sun4} of the filesystem @samp{/usr/r+d}. When it was accessed
+a symbolic link pointing to @samp{/a/gould/usr/r+d/bin/sun4} would be
+returned.@refill
+
+@example
+/defaults type:=nfs;opts:=rw,grpid,nosuid,intr,soft
+wp -opts:=rw,grpid,nosuid;rhost:=charm \
+ host==charm;type:=link;fs:=/usr/local/wp \
+ host!=charm;type:=nfs;rfs:=/vol/wp
+...
+#
+src -opts:=rw,grpid,nosuid;rhost:=charm \
+ host==charm;type:=link;fs:=/usr/src \
+ host!=charm;type:=nfs;rfs:=/vol/src
+#
+r+d type:=auto;fs:=$@{map@};pref:=r+d/
+# per architecture bin,etc,lib&ucb...
+r+d/bin rhost:=gould.doc.ic.ac.uk;rfs:=/usr/r+d;sublink:=$@{/key@}/$@{arch@}
+r+d/etc rhost:=gould.doc.ic.ac.uk;rfs:=/usr/r+d;sublink:=$@{/key@}/$@{arch@}
+r+d/include rhost:=gould.doc.ic.ac.uk;rfs:=/usr/r+d;sublink:=$@{/key@}
+r+d/lib rhost:=gould.doc.ic.ac.uk;rfs:=/usr/r+d;sublink:=$@{/key@}/$@{arch@}
+r+d/man rhost:=gould.doc.ic.ac.uk;rfs:=/usr/r+d;sublink:=$@{/key@}
+r+d/src rhost:=gould.doc.ic.ac.uk;rfs:=/usr/r+d;sublink:=$@{/key@}
+r+d/ucb rhost:=gould.doc.ic.ac.uk;rfs:=/usr/r+d;sublink:=$@{/key@}/$@{arch@}
+# hades pictures
+pictures -opts:=rw,grpid,nosuid;rhost:=thpfs \
+ host==thpfs;type:=link;fs:=/nbsd/pictures \
+ host!=thpfs;type:=nfs;rfs:=/nbsd;sublink:=pictures
+# hades tools
+hades -opts:=rw,grpid,nosuid;rhost:=thpfs \
+ host==thpfs;type:=link;fs:=/nbsd/hades \
+ host!=thpfs;type:=nfs;rfs:=/nbsd;sublink:=hades
+# bsd tools for hp.
+bsd -opts:=rw,grpid,nosuid;arch==hp9000;rhost:=thpfs \
+ host==thpfs;type:=link;fs:=/nbsd/bsd \
+ host!=thpfs;type:=nfs;rfs:=/nbsd;sublink:=bsd
+@end example
+
+@node Internals, Acknowledgements & Trademarks, Examples, Top
+@comment node-name, next, previous, up
+@chapter Internals
+
+@menu
+* Log Messages::
+@end menu
+
+@node Log Messages, , Internals, Internals
+@comment node-name, next, previous, up
+@section Log Messages
+
+In the following sections a brief explanation is given of some of the
+log messages made by @i{Amd}. Where the message is in @samp{typewriter}
+font, it corresponds exactly to the message produced by @i{Amd}. Words
+in @dfn{italic} are replaced by an appropriate string. Variables,
+@code{$@{var@}}, indicate that the value of the appropriate variable is
+output.
+
+Log messages are either sent direct to a file,
+or logged via the @b{syslog}(3) mechanism.
+Messages are logged with facility @samp{LOG_DAEMON} when using @b{syslog}(3).
+In either case, entries in the file are of the form:
+@example
+@i{date-string} @i{hostname} @t{amd[}@i{pid}@t{]} @i{message}
+@end example
+
+@menu
+* Fatal errors::
+* Info messages::
+@end menu
+
+@node Fatal errors, Info messages, Log Messages, Log Messages
+@comment node-name, next, previous, up
+@subsection Fatal errors
+
+@i{Amd} attempts to deal with unusual events. Whenever it is not
+possible to deal with such an error, @i{Amd} will log an appropriate
+message and, if it cannot possibly continue, will either exit or abort.
+These messages are selected by @samp{-x fatal} on the command line.
+When @b{syslog}(3) is being used, they are logged with level
+@samp{LOG_FATAL}. Even if @i{Amd} continues to operate it is likely to
+remain in a precarious state and should be restarted at the earliest
+opportunity.
+
+@table @asis
+@item @t{Attempting to inherit not-a-filesystem}
+The prototype mount point created during a filesystem restart did not
+contain a reference to the restarted filesystem. This erorr ``should
+never happen''.
+
+@item @t{Can't bind to domain "@i{NIS-domain}"}
+A specific NIS domain was requested on the command line, but no server
+for that domain is available on the local net.
+
+@item @t{Can't determine IP address of this host (@i{hostname})}
+When @i{Amd} starts it determines its own IP address. If this lookup
+fails then @i{Amd} cannot continue. The hostname it looks up is that
+obtained returned by @b{gethostname}(2) system call.
+
+@item @t{Can't find root file handle for @i{automount point}}
+@i{Amd} creates its own file handles for the automount points. When it
+mounts itself as a server, it must pass these file handles to the local
+kernel. If the filehandle is not obtainable the mount point is ignored.
+This error ``should never happen''.
+
+@item @t{Must be root to mount filesystems (euid = @i{euid})}
+To prevent embarrassment, @i{Amd} makes sure it has appropriate system
+privileges. This amounts to having an euid of 0. The check is made
+after argument processing complete to give non-root users a chance to
+access the ``-v'' option.
+
+@item @t{No work to do - quitting}
+No automount points were given on the command line and so there is no
+work to do.
+
+@item @t{Out of memory in realloc}
+While attempting to realloc some memory, the memory space available to
+@i{Amd} was exhausted. This is an unrecoverable error.
+
+@item @t{Out of memory}
+While attempting to malloc some memory, the memory space available to
+@i{Amd} was exhausted. This is an unrecoverable error.
+
+@item @t{cannot create rpc/udp service}
+Either the NFS or AMQ endpoint could not be created.
+
+@item @t{gethostname:} @i{description}
+The @b{gethostname}(2) system call failed during startup.
+
+@item @t{host name is not set}
+The @b{gethostname}(2) system call returned a zero length host name.
+This can happen if @i{Amd} is started in single user mode just after
+booting the system.
+
+@item @t{ifs_match called!}
+An internal error occurred while restarting a pre-mounted filesystem.
+This error ``should never happen''.
+
+@item @t{mount_afs:} @i{description}
+An error occured while @i{Amd} was mounting itself.
+
+@item @t{run_rpc failed}
+Somehow the main NFS server loop failed. This error ``should never
+happen''.
+
+@item @t{unable to free rpc arguments in amqprog_1}
+The incoming arguments to the AMQ server could not be free'ed.
+
+@item @t{unable to free rpc arguments in nfs_program_1}
+The incoming arguments to the NFS server could not be free'ed.
+
+@item @t{unable to register (AMQ_PROGRAM, AMQ_VERSION, udp)}
+The AMQ server could not be registered with the local portmapper or the
+internal RPC dispatcher.
+
+@item @t{unable to register (NFS_PROGRAM, NFS_VERSION, 0)}
+The NFS server could not be registered with the internal RPC dispatcher.
+
+@end table
+
+@node Info messages, , Fatal errors, Log Messages
+@comment node-name, next, previous, up
+@subsection Info messages
+
+@i{Amd} generates information messages to record state changes. These
+messages are selected by @samp{-x info} on the command line. When
+@b{syslog}(3) is being used, they are logged with level @samp{LOG_INFO}.
+
+The messages listed below can be generated and are in a format suitable
+for simple statistical analysis. @dfn{mount-info} is the string
+that is displayed by @dfn{Amq} in its mount information column and
+placed in the system mount table.
+
+@table @asis
+@item @t{mount of "@t{$@{@i{path}@}}" on @t{$@{@i{fs}@}} timed out}
+Attempts to mount a filesystem for the given automount point have failed
+to complete within 30 seconds.
+
+@item @t{"@t{$@{@i{path}@}}" forcibly timed out}
+An automount point has been timed out by the @i{Amq} command.
+
+@item @t{restarting @i{mount-info} on @t{$@{@i{fs}@}}}
+A pre-mounted file system has been noted.
+
+@item @t{"@t{$@{@i{path}@}}" has timed out}
+No access to the automount point has been made within the timeout
+period.
+
+@item @t{file server @t{$@{@i{rhost}@}} is down - timeout of "@t{$@{@i{path}@}}" ignored}
+An automount point has timed out, but the corresponding file server is
+known to be down. This message is only produced once for each mount
+point for which the server is down.
+
+@item @t{Re-synchronizing cache for map @t{$@{@i{map}@}}}
+The named map has been modified and the internal cache is being re-synchronized.
+
+@item @t{Filehandle denied for "@t{$@{@i{rhost}@}}:@t{$@{@i{rfs}@}}"}
+The mount daemon refused to return a file handle for the requested filesystem.
+
+@item @t{Filehandle error for "$@{@i{rhost}@}:$@{@i{rfs}@}":} @i{description}
+The mount daemon gave some other error for the requested filesystem.
+
+@item @t{file server @t{$@{@i{rhost}@}} type nfs starts up}
+A new NFS file server has been referenced and is known to be up.
+
+@item @t{file server @t{$@{@i{rhost}@}} type nfs starts down}
+A new NFS file server has been referenced and is known to be down.
+
+@item @t{file server @t{$@{@i{rhost}@}} type nfs is up}
+An NFS file server that was previously down is now up.
+
+@item @t{file server @t{$@{@i{rhost}@}} type nfs is down}
+An NFS file server that was previously up is now down.
+
+@item @t{Finishing with status @i{exit-status}}
+@i{Amd} is about to exit with the given exit status.
+
+@item @t{@i{mount-info} mounted fstype @t{$@{@i{type}@}} on @t{$@{@i{fs}@}}}
+A new file system has been mounted.
+
+@item @t{@i{mount-info} restarted fstype @t{$@{@i{type}@}} on @t{$@{@i{fs}@}}}
+@i{Amd} is using a pre-mounted filesystem to satisfy a mount request.
+
+@item @t{@i{mount-info} unmounted fstype @t{$@{@i{type}@}} from @t{$@{@i{fs}@}}}
+A file system has been unmounted.
+
+@item @t{@i{mount-info} unmounted fstype @t{$@{@i{type}@}} from @t{$@{@i{fs}@}} link @t{$@{@i{fs}@}}/@t{$@{@i{sublink}@}}}
+A file system of which only a sub-directory was in use has been unmounted.
+
+@end table
+
+@node Acknowledgements & Trademarks, Index, Internals, Top
+@comment node-name, next, previous, up
+@unnumbered Acknowledgements & Trademarks
+
+Thanks to the Formal Methods Group at Imperial College for
+suffering patiently while @i{Amd} was being developed on their machines.
+
+Thanks to the many people who have helped with the development of
+@i{Amd}, especially Piete Brooks at the Cambridge University Computing
+Lab for many hours of testing, experimentation and discussion.
+
+@itemize @bullet
+@item
+@b{DEC}, @b{VAX} and @b{Ultrix} are registered trademarks of Digital
+Equipment Corporation.
+@item
+@b{AIX} and @b{IBM} are registered trademarks of International Business
+Machines Corporation.
+@item
+@b{Sun}, @b{NFS} and @b{SunOS} are registered trademarks of Sun
+Microsystems, Inc.
+@item
+@b{Unix} is a registered trademark of AT&T Unix Systems Laboratories
+in the USA and other countries.
+@end itemize
+
+@node Index, , Acknowledgements & Trademarks, Top
+@comment node-name, next, previous, up
+@unnumbered Index
+
+@printindex cp
+
+@contents
+@bye
diff --git a/usr.sbin/amd/doc/texinfo.tex b/usr.sbin/amd/doc/texinfo.tex
new file mode 100644
index 0000000..73528ed
--- /dev/null
+++ b/usr.sbin/amd/doc/texinfo.tex
@@ -0,0 +1,2192 @@
+%% TeX macros to handle texinfo files
+
+% Copyright (C) 1985, 1986, 1988 Free Software Foundation, Inc.
+
+%GNU CC is free software; you can redistribute it and/or modify
+%it under the terms of the GNU General Public License as published by
+%the Free Software Foundation; either version 1, or (at your option)
+%any later version.
+
+%GNU CC is distributed in the hope that it will be useful,
+%but WITHOUT ANY WARRANTY; without even the implied warranty of
+%MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+%GNU General Public License for more details.
+
+%You should have received a copy of the GNU General Public License
+%along with GNU CC; see the file COPYING. If not, write to
+%the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+
+
+%In other words, you are welcome to use, share and improve this program.
+%You are forbidden to forbid anyone else to use, share and improve
+%what you give them. Help stamp out software-hoarding!
+
+\def\texinfoversion{1.26}
+\message{Loading texinfo package [Version \texinfoversion]:}
+\message{}
+
+% Save some parts of plain tex whose names we will redefine.
+
+\let\ptexlbrace=\{
+\let\ptexrbrace=\}
+\let\ptexdot=\.
+\let\ptexstar=\*
+\let\ptexend=\end
+\let\ptexbullet=\bullet
+\let\ptexb=\b
+\let\ptexc=\c
+\let\ptexi=\i
+\let\ptext=\t
+\let\ptexl=\l
+\let\ptexL=\L
+
+\def\tie{\penalty 10000\ } % Save plain tex definition of ~.
+
+\message{Basics,}
+\chardef\other=12
+
+\hyphenation{ap-pen-dix}
+\hyphenation{mini-buf-fer mini-buf-fers}
+\hyphenation{eshell}
+
+% Margin to add to right of even pages, to left of odd pages.
+\newdimen \bindingoffset \bindingoffset=0pt
+\newdimen \normaloffset \normaloffset=\hoffset
+\newdimen\pagewidth \newdimen\pageheight
+\pagewidth=\hsize \pageheight=\vsize
+
+%---------------------Begin change-----------------------
+%
+% Dimensions to add cropmarks at corners Added by P. A. MacKay, 12 Nov. 1986
+%
+\newdimen\cornerlong \newdimen\cornerthick
+\newdimen \topandbottommargin
+\newdimen \outerhsize \newdimen \outervsize
+\cornerlong=1pc\cornerthick=.3pt % These set size of cropmarks
+\outerhsize=7in
+\outervsize=9.5in
+\topandbottommargin=.75in
+%
+%---------------------End change-----------------------
+
+% \onepageout takes a vbox as an argument. Note that \pagecontents
+% does insertions itself, but you have to call it yourself.
+\chardef\PAGE=255 \output={\onepageout{\pagecontents\PAGE}}
+\def\onepageout#1{\hoffset=\normaloffset
+\ifodd\pageno \advance\hoffset by \bindingoffset
+\else \advance\hoffset by -\bindingoffset\fi
+\shipout\vbox{{\let\hsize=\pagewidth \makeheadline} \pagebody{#1}%
+ {\let\hsize=\pagewidth \makefootline}}
+\advancepageno \ifnum\outputpenalty>-20000 \else\dosupereject\fi}
+
+
+% Here is a modification of the main output routine for Near East Publications
+% This provides right-angle cropmarks at all four corners.
+% The contents of the page are centerlined into the cropmarks,
+% and any desired binding offset is added as an \hskip on either
+% site of the centerlined box. (P. A. MacKay, 12 November, 1986)
+%
+\def\croppageout#1{\hoffset=0pt % make sure this doesn't mess things up
+ \shipout
+ \vbox to \outervsize{\hsize=\outerhsize
+ \vbox{\line{\ewtop\hfill\ewtop}}
+ \nointerlineskip
+ \line{\vbox{\moveleft\cornerthick\nstop}
+ \hfill
+ \vbox{\moveright\cornerthick\nstop}}
+ \vskip \topandbottommargin
+ \centerline{\ifodd\pageno\hskip\bindingoffset\fi
+ \vbox{
+ {\let\hsize=\pagewidth \makeheadline}
+ \pagebody{#1}
+ {\let\hsize=\pagewidth \makefootline}}
+ \ifodd\pageno\else\hskip\bindingoffset\fi}
+ \vskip \topandbottommargin plus1fill minus1fill
+ \boxmaxdepth\cornerthick
+ \line{\vbox{\moveleft\cornerthick\nsbot}
+ \hfill
+ \vbox{\moveright\cornerthick\nsbot}}
+ \nointerlineskip
+ \vbox{\line{\ewbot\hfill\ewbot}}
+ }
+ \advancepageno
+ \ifnum\outputpenalty>-20000 \else\dosupereject\fi}
+%
+% Do @cropmarks to get crop marks
+\def\cropmarks{\let\onepageout=\croppageout }
+
+\def\pagebody#1{\vbox to\pageheight{\boxmaxdepth=\maxdepth #1}}
+{\catcode`\@ =11
+\gdef\pagecontents#1{\ifvoid\topins\else\unvbox\topins\fi
+\dimen@=\dp#1 \unvbox#1
+\ifvoid\footins\else\vskip\skip\footins\footnoterule \unvbox\footins\fi
+\ifr@ggedbottom \kern-\dimen@ \vfil \fi}
+}
+
+%
+% Here are the rules for the cropmarks. Note that they are
+% offset so that the space between them is truly \outerhsize or \outervsize
+% (P. A. MacKay, 12 November, 1986)
+%
+\def\ewtop{\vrule height\cornerthick depth0pt width\cornerlong}
+\def\nstop{\vbox
+ {\hrule height\cornerthick depth\cornerlong width\cornerthick}}
+\def\ewbot{\vrule height0pt depth\cornerthick width\cornerlong}
+\def\nsbot{\vbox
+ {\hrule height\cornerlong depth\cornerthick width\cornerthick}}
+
+% Parse an argument, then pass it to #1.
+% The argument can be delimited with [...] or with "..." or braces
+% or it can be a whole line.
+% #1 should be a macro which expects
+% an ordinary undelimited TeX argument.
+
+\def\parsearg #1{\let\next=#1\begingroup\obeylines\futurelet\temp\parseargx}
+
+\def\parseargx{%
+\ifx \obeyedspace\temp \aftergroup\parseargdiscardspace \else%
+\aftergroup \parseargline %
+\fi \endgroup}
+
+{\obeyspaces %
+\gdef\parseargdiscardspace {\begingroup\obeylines\futurelet\temp\parseargx}}
+
+\gdef\obeyedspace{\ }
+
+\def\parseargline{\begingroup \obeylines \parsearglinex}
+{\obeylines %
+\gdef\parsearglinex #1^^M{\endgroup \next {#1}}}
+
+\def\flushcr{\ifx\par\lisppar \def\next##1{}\else \let\next=\relax \fi \next}
+
+%% These are used to keep @begin/@end levels from running away
+%% Call \inENV within environments (after a \begingroup)
+\newif\ifENV \ENVfalse \def\inENV{\ifENV\relax\else\ENVtrue\fi}
+\def\ENVcheck{%
+\ifENV\errmessage{Still within an environment. Type Return to continue.}
+\endgroup\fi} % This is not perfect, but it should reduce lossage
+
+% @begin foo is the same as @foo, for now.
+\newhelp\EMsimple{Type <Return> to continue}
+
+\outer\def\begin{\parsearg\beginxxx}
+
+\def\beginxxx #1{%
+\expandafter\ifx\csname #1\endcsname\relax
+{\errhelp=\EMsimple \errmessage{Undefined command @begin #1}}\else
+\csname #1\endcsname\fi}
+
+%% @end foo executes the definition of \Efoo.
+%% foo can be delimited by doublequotes or brackets.
+
+\def\end{\parsearg\endxxx}
+
+\def\endxxx #1{%
+\expandafter\ifx\csname E#1\endcsname\relax
+\expandafter\ifx\csname #1\endcsname\relax
+\errmessage{Undefined command @end #1}\else
+\errorE{#1}\fi\fi
+\csname E#1\endcsname}
+\def\errorE#1{
+{\errhelp=\EMsimple \errmessage{@end #1 not within #1 environment}}}
+
+% Single-spacing is done by various environments.
+
+\newskip\singlespaceskip \singlespaceskip = \baselineskip
+\def\singlespace{%
+{\advance \baselineskip by -\singlespaceskip
+\kern \baselineskip}%
+\baselineskip=\singlespaceskip
+}
+
+%% Simple single-character @ commands
+
+% @@ prints an @
+% Kludge this until the fonts are right (grr).
+\def\@{{\sf \char '100}}
+
+% Define @` and @' to be the same as ` and '
+% but suppressing ligatures.
+\def\`{{`}}
+\def\'{{'}}
+
+% Used to generate quoted braces.
+
+\def\mylbrace {{\tt \char '173}}
+\def\myrbrace {{\tt \char '175}}
+\let\{=\mylbrace
+\let\}=\myrbrace
+
+% @: forces normal size whitespace following.
+\def\:{\spacefactor=1000 }
+
+% @* forces a line break.
+\def\*{\hfil\break}
+
+% @. is an end-of-sentence period.
+\def\.{.\spacefactor=3000 }
+
+% @w prevents a word break
+\def\w #1{\hbox{#1}}
+
+% @group ... @end group forces ... to be all on one page.
+
+\def\group{\begingroup% \inENV ???
+\def \Egroup{\egroup\endgroup}
+\vbox\bgroup}
+
+% @br forces paragraph break
+
+\let\br = \par
+
+% @dots{} output some dots
+
+\def\dots{$\ldots$}
+
+% @page forces the start of a new page
+
+\def\page{\par\vfill\supereject}
+
+% @exdent text....
+% outputs text on separate line in roman font, starting at standard page margin
+
+\def\exdent{\errmessage{@exdent in filled text}}
+ % @lisp, etc, define \exdent locally from \internalexdent
+
+{\obeyspaces
+\gdef\internalexdent{\parsearg\exdentzzz}}
+
+\def\exdentzzz #1{{\advance \leftskip by -\lispnarrowing
+\advance \hsize by -\leftskip
+\advance \hsize by -\rightskip
+\leftline{{\rm#1}}}}
+
+% @include file insert text of that file as input.
+
+\def\include{\parsearg\includezzz}
+\def\includezzz #1{{\def\thisfile{#1}\input #1
+}}
+
+\def\thisfile{}
+
+% @center line outputs that line, centered
+
+\def\center{\parsearg\centerzzz}
+\def\centerzzz #1{{\advance\hsize by -\leftskip
+\advance\hsize by -\rightskip
+\centerline{#1}}}
+
+% @sp n outputs n lines of vertical space
+
+\def\sp{\parsearg\spxxx}
+\def\spxxx #1{\par \vskip #1\baselineskip}
+
+% @comment ...line which is ignored...
+% @c is the same as @comment
+% @ignore ... @end ignore is another way to write a comment
+
+\def\comment{\parsearg \commentxxx}
+
+\def\commentxxx #1{}
+
+\let\c=\comment
+
+% Prevent errors for section commands.
+% Used in @ignore and in failing conditionals.
+\def\ignoresections{%
+\let\chapter=\relax
+\let\unnumbered=\relax
+\let\unnumberedsec=\relax
+\let\unnumberedsection=\relax
+\let\unnumberedsubsec=\relax
+\let\unnumberedsubsection=\relax
+\let\unnumberedsubsubsec=\relax
+\let\unnumberedsubsubsection=\relax
+\let\section=\relax
+\let\subsec=\relax
+\let\subsubsec=\relax
+\let\subsection=\relax
+\let\subsubsection=\relax
+\let\appendix=\relax
+\let\appendixsec=\relax
+\let\appendixsection=\relax
+\let\appendixsubsec=\relax
+\let\appendixsubsection=\relax
+\let\appendixsubsubsec=\relax
+\let\appendixsubsubsection=\relax
+}
+
+\def\ignore{\begingroup\ignoresections\ignorexxx}
+\long\def\ignorexxx #1\end ignore{\endgroup}
+
+% Conditionals to test whether a flag is set.
+
+\outer\def\ifset{\begingroup\ignoresections\parsearg\ifsetxxx}
+
+\def\ifsetxxx #1{\endgroup
+\expandafter\ifx\csname IF#1\endcsname\relax \let\temp=\ifsetfail
+\else \let\temp=\relax \fi
+\temp}
+\def\Eifset{}
+\def\ifsetfail{\begingroup\ignoresections\ifsetfailxxx}
+\long\def\ifsetfailxxx #1\end ifset{\endgroup}
+
+\outer\def\ifclear{\begingroup\ignoresections\parsearg\ifclearxxx}
+
+\def\ifclearxxx #1{\endgroup
+\expandafter\ifx\csname IF#1\endcsname\relax \let\temp=\relax
+\else \let\temp=\ifclearfail \fi
+\temp}
+\def\Eifclear{}
+\def\ifclearfail{\begingroup\ignoresections\ifclearfailxxx}
+\long\def\ifclearfailxxx #1\end ifclear{\endgroup}
+
+% Some texinfo constructs that are trivial in tex
+
+\def\iftex{}
+\def\Eiftex{}
+\def\ifinfo{\begingroup\ignoresections\ifinfoxxx}
+\long\def\ifinfoxxx #1\end ifinfo{\endgroup}
+\long\def\menu #1\end menu{}
+\def\asis#1{#1}
+
+\def\node{\parsearg\nodezzz}
+\def\nodezzz#1{\nodexxx [#1,]}
+\def\nodexxx[#1,#2]{\gdef\lastnode{#1}}
+\let\lastnode=\relax
+
+\def\donoderef{\ifx\lastnode\relax\else
+\expandafter\expandafter\expandafter\setref{\lastnode}\fi
+\let\lastnode=\relax}
+
+\def\unnumbnoderef{\ifx\lastnode\relax\else
+\expandafter\expandafter\expandafter\unnumbsetref{\lastnode}\fi
+\let\lastnode=\relax}
+
+\let\refill=\relax
+
+% @setfilename is done at the beginning of every texinfo file.
+% So open here the files we need to have open while reading the input.
+% This makes it possible to make a .fmt file for texinfo.
+\def\setfilename{%
+ \readauxfile
+ \opencontents
+ \openindices
+ \fixbackslash % Turn off hack to swallow `\input texinfo'.
+ \comment % Ignore the actual filename.
+}
+
+\outer\def\bye{\pagealignmacro\tracingstats=1\ptexend}
+
+\def\inforef #1{\inforefzzz #1,,,,**}
+\def\inforefzzz #1,#2,#3,#4**{See Info file \file{\losespace#3{}}, node `\losespace#1{}'}
+\def\losespace #1{#1}
+
+\message{fonts,}
+
+% Font-change commands.
+
+%% Try out Computer Modern fonts at \magstephalf
+\font\tenrm=cmr10 scaled \magstephalf
+\font\tentt=cmtt10 scaled \magstephalf
+% Instead of cmb10, you many want to use cmbx10.
+% cmbx10 is a prettier font on its own, but cmb10
+% looks better when embedded in a line with cmr10.
+\font\tenbf=cmb10 scaled \magstephalf
+\font\tenit=cmti10 scaled \magstephalf
+\font\tensl=cmsl10 scaled \magstephalf
+\font\tensf=cmss10 scaled \magstephalf
+\def\li{\sf}
+\font\tensc=cmcsc10 scaled \magstephalf
+
+% Fonts for @defun, etc.
+\font\defbf=cmbx10 scaled \magstep1 %was 1314
+\let\deftt=\tentt
+\def\df{\let\tt=\deftt \defbf}
+
+% Font for title
+\font\titlerm = cmbx10 scaled \magstep5
+
+% Fonts for indices
+\font\indit=cmti9 \font\indrm=cmr9
+\def\indbf{\indrm} \def\indsl{\indit}
+\def\indexfonts{\let\it=\indit \let\sl=\indsl \let\bf=\indbf \let\rm=\indrm}
+
+% Fonts for headings
+\font\chaprm=cmbx10 scaled \magstep3
+\font\chapit=cmti10 scaled \magstep3
+\font\chapsl=cmsl10 scaled \magstep3
+\font\chaptt=cmtt10 scaled \magstep3
+\font\chapsf=cmss10 scaled \magstep3
+\let\chapbf=\chaprm
+
+\font\secrm=cmbx10 scaled \magstep2
+\font\secit=cmti10 scaled \magstep2
+\font\secsl=cmsl10 scaled \magstep2
+\font\sectt=cmtt10 scaled \magstep2
+\font\secsf=cmss10 scaled \magstep2
+\let\secbf=\secrm
+
+% \font\ssecrm=cmbx10 scaled \magstep1 % This size an fontlooked bad.
+% \font\ssecit=cmti10 scaled \magstep1 % The letters were too crowded.
+% \font\ssecsl=cmsl10 scaled \magstep1
+% \font\ssectt=cmtt10 scaled \magstep1
+% \font\ssecsf=cmss10 scaled \magstep1
+
+\font\ssecrm=cmb10 at 13pt % Note the use of cmb rather than cmbx.
+\font\ssecit=cmti10 at 13pt % Also, the size is a little larger than
+\font\ssecsl=cmsl10 at 13pt % being scaled magstep1.
+\font\ssectt=cmtt10 at 13pt
+\font\ssecsf=cmss10 at 13pt
+
+\let\ssecbf=\ssecrm
+
+\def\textfonts{\let\rm=\tenrm\let\it=\tenit\let\sl=\tensl\let\bf=\tenbf%
+\let\smallcaps=\tensc\let\sf=\tensf}
+\def\chapfonts{\let\rm=\chaprm\let\it=\chapit\let\sl=\chapsl\let\bf=\chapbf\let\tt=\chaptt\let\sf=\chapsf}
+\def\secfonts{\let\rm=\secrm\let\it=\secit\let\sl=\secsl\let\bf=\secbf\let\tt=\sectt\let\sf=\secsf}
+\def\subsecfonts{\let\rm=\ssecrm\let\it=\ssecit\let\sl=\ssecsl\let\bf=\ssecbf\let\tt=\ssectt\let\sf=\ssecsf}
+% Count depth in font-changes, for error checks
+\newcount\fontdepth \fontdepth=0
+
+% Font for table of contents.
+\font\truesecrm=cmr12
+
+%% Add scribe-like font environments, plus @l for inline lisp (usually sans
+%% serif) and @ii for TeX italic
+
+\def\i#1{{\sl #1}}
+\let\var=\i
+\let\dfn=\i
+\let\emph=\i
+\let\cite=\i
+
+\def\b#1{{\bf #1}}
+\let\strong=\b
+
+\def\t#1{{\tt \rawbackslash \frenchspacing #1}\null}
+\let\ttfont = \t
+\let\kbd=\t
+\let\code=\t
+\def\samp #1{`{\tt \rawbackslash \frenchspacing #1}'\null}
+\def\key #1{{\tt \uppercase{#1}}\null}
+\def\ctrl #1{{\tt \rawbackslash \hat}#1}
+
+\let\file=\samp
+
+\def\l#1{{\li #1}\null} %
+
+\def\r#1{{\rm #1}} % roman font
+\def\sc#1{{\\smallcaps #1}} % smallcaps font
+\def\ii#1{{\it #1}} % italic font
+
+\def\titlefont#1{{\titlerm #1}}
+
+% Make altmode in file print out right
+
+\catcode `\^^[=\active \def^^[{$\diamondsuit$}
+
+\message{page headings,}
+
+\newskip\titlepagetopglue \titlepagetopglue = 1.5in
+\newskip\titlepagebottomglue \titlepagebottomglue = 2pc
+
+% First the title page. Must do @settitle before @titlepage.
+\font\titlerm = cmbx12 scaled \magstep2
+\def\titlefont#1{{\titlerm #1}}
+
+\newtoks\realeverypar
+\newif\ifseenauthor
+
+\def\titlepage{\begingroup \parindent=0pt \textfonts
+ \font\subtitlerm = cmr10 scaled \magstephalf
+ \def\subtitlefont{\subtitlerm \normalbaselineskip = 12pt \normalbaselines}%
+ %
+ \font\authorrm = cmbx12 scaled \magstep1
+ \def\authorfont{\authorrm \normalbaselineskip = 16pt \normalbaselines}%
+ %
+ % The first subtitle should have some space before it, but not the
+ % others. They all should be ragged left.
+% ??? This code turned off because (1) it is wrong for all old title
+% pages, and (2) it makes an extra group which never is ended.
+% \begingroup \realeverypar = {\leftskip = 2in plus 3em minus 1em
+% \parfillskip = 0pt}%
+% \everypar = {\vglue \baselineskip \the\realeverypar
+% \everypar={\the\realeverypar}}%
+ %
+ % Now you can print the title using @title.
+ \def\title{\parsearg\titlezzz}%
+ \def\titlezzz##1{\leftline{\titlefont{##1}
+ \vskip4pt \hrule height 4pt \vskip4pt}%
+ \vglue\titlepagetopglue
+ %
+ % Now you can put text using @subtitle.
+ \def\subtitle{\parsearg\subtitlezzz}%
+ \def\subtitlezzz##1{{\subtitlefont \rightline{##1}}}%
+ %
+ % @author should come last, but may come many times.
+ \def\author{\parsearg\authorzzz}%
+ \def\authorzzz##1{\ifseenauthor\else\vskip 0pt plus 1filll\seenauthortrue\fi
+ {\authorfont \leftline{##1}}}%
+ %
+ % Most title ``pages'' are actually two pages long, with space
+ % at the top of the second. We don't want the ragged left on the second.
+ \let\oldpage = \page
+% \def\page{\vskip4pt \hrule height 2pt \vskip\titlepagebottomglue
+% \oldpage \endgroup\hrule height0pt\relax}%
+ \def\page{\oldpage \hbox{}}}
+}
+
+\def\Etitlepage{\endgroup\page\HEADINGSon}
+
+%%% Set up page headings and footings.
+
+\let\thispage=\folio
+
+\newtoks \evenheadline % Token sequence for heading line of even pages
+\newtoks \oddheadline % Token sequence for heading line of odd pages
+\newtoks \evenfootline % Token sequence for footing line of even pages
+\newtoks \oddfootline % Token sequence for footing line of odd pages
+
+% Now make Tex use those variables
+\headline={{\textfonts\rm \ifodd\pageno \the\oddheadline \else \the\evenheadline \fi}}
+\footline={{\textfonts\rm \ifodd\pageno \the\oddfootline \else \the\evenfootline \fi}}
+
+% Commands to set those variables.
+% For example, this is what @headings on does
+% @evenheading @thistitle|@thispage|@thischapter
+% @oddheading @thischapter|@thispage|@thistitle
+% @evenfooting @thisfile||
+% @oddfooting ||@thisfile
+
+\def\evenheading{\parsearg\evenheadingxxx}
+\def\oddheading{\parsearg\oddheadingxxx}
+\def\everyheading{\parsearg\everyheadingxxx}
+
+\def\evenfooting{\parsearg\evenfootingxxx}
+\def\oddfooting{\parsearg\oddfootingxxx}
+\def\everyfooting{\parsearg\everyfootingxxx}
+
+{\catcode`\@=0 %
+
+\gdef\evenheadingxxx #1{\evenheadingyyy #1@|@|@|@|\finish}
+\gdef\evenheadingyyy #1@|#2@|#3@|#4\finish{%
+\global\evenheadline={\rlap{\centerline{#2}}\line{#1\hfil#3}}}
+
+\gdef\oddheadingxxx #1{\oddheadingyyy #1@|@|@|@|\finish}
+\gdef\oddheadingyyy #1@|#2@|#3@|#4\finish{%
+\global\oddheadline={\rlap{\centerline{#2}}\line{#1\hfil#3}}}
+
+\gdef\everyheadingxxx #1{\everyheadingyyy #1@|@|@|@|\finish}
+\gdef\everyheadingyyy #1@|#2@|#3@|#4\finish{%
+\global\evenheadline={\rlap{\centerline{#2}}\line{#1\hfil#3}}
+\global\oddheadline={\rlap{\centerline{#2}}\line{#1\hfil#3}}}
+
+\gdef\evenfootingxxx #1{\evenfootingyyy #1@|@|@|@|\finish}
+\gdef\evenfootingyyy #1@|#2@|#3@|#4\finish{%
+\global\evenfootline={\rlap{\centerline{#2}}\line{#1\hfil#3}}}
+
+\gdef\oddfootingxxx #1{\oddfootingyyy #1@|@|@|@|\finish}
+\gdef\oddfootingyyy #1@|#2@|#3@|#4\finish{%
+\global\oddfootline={\rlap{\centerline{#2}}\line{#1\hfil#3}}}
+
+\gdef\everyfootingxxx #1{\everyfootingyyy #1@|@|@|@|\finish}
+\gdef\everyfootingyyy #1@|#2@|#3@|#4\finish{%
+\global\evenfootline={\rlap{\centerline{#2}}\line{#1\hfil#3}}
+\global\oddfootline={\rlap{\centerline{#2}}\line{#1\hfil#3}}}
+%
+}% unbind the catcode of @.
+
+% @headings double turns headings on for double-sided printing.
+% @headings single turns headings on for single-sided printing.
+% @headings off turns them off.
+% @headings on same as @headings double, retained for compatibility.
+% By default, they are off.
+
+\def\headings #1 {\csname HEADINGS#1\endcsname}
+
+\def\HEADINGSoff{
+\global\evenheadline={\hfil} \global\evenfootline={\hfil}
+\global\oddheadline={\hfil} \global\oddfootline={\hfil}}
+\HEADINGSoff
+% When we turn headings on, set the page number to 1.
+% For double-sided printing, put current file name in lower left corner,
+% chapter name on inside top of right hand pages, document
+% title on inside top of left hand pages, and page numbers on outside top
+% edge of all pages.
+\def\HEADINGSdouble{
+%\pagealignmacro
+\global\pageno=1
+\global\evenfootline={\hfil}
+\global\oddfootline={\hfil}
+\global\evenheadline={\line{\folio\hfil\thistitle}}
+\global\oddheadline={\line{\thischapter\hfil\folio}}
+}
+% For single-sided printing, chapter title goes across top left of page,
+% page number on top right.
+\def\HEADINGSsingle{
+%\pagealignmacro
+\global\pageno=1
+\global\evenfootline={\hfil}
+\global\oddfootline={\hfil}
+\global\evenheadline={\line{\thischapter\hfil\folio}}
+\global\oddheadline={\line{\thischapter\hfil\folio}}
+}
+\def\HEADINGSon{\HEADINGSdouble}
+
+% Subroutines used in generating headings
+% Produces Day Month Year style of output.
+\def\today{\number\day\space
+\ifcase\month\or
+January\or February\or March\or April\or May\or June\or
+July\or August\or September\or October\or November\or December\fi
+\space\number\year}
+
+% Use this if you want the Month Day, Year style of output.
+%\def\today{\ifcase\month\or
+%January\or February\or March\or April\or May\or June\or
+%July\or August\or September\or October\or November\or December\fi
+%\space\number\day, \number\year}
+
+% @settitle line... specifies the title of the document, for headings
+% It generates no output of its own
+
+\def\thistitle{No Title}
+\def\settitle{\parsearg\settitlezzz}
+\def\settitlezzz #1{\gdef\thistitle{#1}}
+
+\message{tables,}
+
+% Tables -- @table, @ftable, @item(x), @kitem(x), @xitem(x).
+
+% default indentation of table text
+\newdimen\tableindent \tableindent=.8in
+% default indentation of @itemize and @enumerate text
+\newdimen\itemindent \itemindent=.3in
+% margin between end of table item and start of table text.
+\newdimen\itemmargin \itemmargin=.1in
+
+% used internally for \itemindent minus \itemmargin
+\newdimen\itemmax
+
+% Note @table and @ftable define @item, @itemx, etc., with these defs.
+% They also define \itemindex
+% to index the item name in whatever manner is desired (perhaps none).
+
+\def\internalBitem{\smallbreak \parsearg\itemzzz}
+\def\internalBitemx{\par \parsearg\itemzzz}
+
+\def\internalBxitem "#1"{\def\xitemsubtopix{#1} \smallbreak \parsearg\xitemzzz}
+\def\internalBxitemx "#1"{\def\xitemsubtopix{#1} \par \parsearg\xitemzzz}
+
+\def\internalBkitem{\smallbreak \parsearg\kitemzzz}
+\def\internalBkitemx{\par \parsearg\kitemzzz}
+
+\def\kitemzzz #1{\dosubind {kw}{\code{#1}}{for {\bf \lastfunction}}\itemzzz {#1}}
+
+\def\xitemzzz #1{\dosubind {kw}{\code{#1}}{for {\bf \xitemsubtopic}}\itemzzz {#1}}
+
+\def\itemzzz #1{\begingroup %
+\advance \hsize by -\rightskip %
+\advance \hsize by -\leftskip %
+\setbox0=\hbox{\itemfont{#1}}%
+\itemindex{#1}%
+\parskip=0in %
+\noindent %
+\ifdim \wd0>\itemmax %
+\vadjust{\penalty 10000}%
+\hbox to \hsize{\hskip -\tableindent\box0\hss}\ %
+\else %
+\hbox to 0pt{\hskip -\tableindent\box0\hss}%
+\fi %
+\endgroup %
+}
+
+\def\item{\errmessage{@item while not in a table}}
+\def\itemx{\errmessage{@itemx while not in a table}}
+\def\kitem{\errmessage{@kitem while not in a table}}
+\def\kitemx{\errmessage{@kitemx while not in a table}}
+\def\xitem{\errmessage{@xitem while not in a table}}
+\def\xitemx{\errmessage{@xitemx while not in a table}}
+
+%% Contains a kludge to get @end[description] to work
+\def\description{\tablez{\dontindex}{1}{}{}{}{}}
+
+\def\table{\begingroup\inENV\obeylines\obeyspaces\tablex}
+{\obeylines\obeyspaces%
+\gdef\tablex #1^^M{%
+\tabley\dontindex#1 \endtabley}}
+
+\def\ftable{\begingroup\inENV\obeylines\obeyspaces\ftablex}
+{\obeylines\obeyspaces%
+\gdef\ftablex #1^^M{%
+\tabley\fnitemindex#1 \endtabley}}
+
+\def\dontindex #1{}
+\def\fnitemindex #1{\doind {fn}{\code{#1}}}%
+
+{\obeyspaces %
+\gdef\tabley#1#2 #3 #4 #5 #6 #7\endtabley{\endgroup%
+\tablez{#1}{#2}{#3}{#4}{#5}{#6}}}
+
+\def\tablez #1#2#3#4#5#6{%
+\aboveenvbreak %
+\begingroup %
+\def\Edescription{\Etable}% Neccessary kludge.
+\let\itemindex=#1%
+\ifnum 0#3>0 \advance \leftskip by #3\mil \fi %
+\ifnum 0#4>0 \tableindent=#4\mil \fi %
+\ifnum 0#5>0 \advance \rightskip by #5\mil \fi %
+\def\itemfont{#2}%
+\itemmax=\tableindent %
+\advance \itemmax by -\itemmargin %
+\advance \leftskip by \tableindent %
+\parindent = 0pt
+\parskip = \smallskipamount
+\ifdim \parskip=0pt \parskip=2pt \fi%
+\def\Etable{\endgraf\endgroup\afterenvbreak}%
+\let\item = \internalBitem %
+\let\itemx = \internalBitemx %
+\let\kitem = \internalBkitem %
+\let\kitemx = \internalBkitemx %
+\let\xitem = \internalBxitem %
+\let\xitemx = \internalBxitemx %
+}
+
+% This is the counter used by @enumerate, which is really @itemize
+
+\newcount \itemno
+
+\def\itemize{\parsearg\itemizezzz}
+
+\def\itemizezzz #1{\itemizey {#1}{\Eitemize}}
+
+\def\itemizey #1#2{%
+\aboveenvbreak %
+\begingroup %
+\itemno = 0 %
+\itemmax=\itemindent %
+\advance \itemmax by -\itemmargin %
+\advance \leftskip by \itemindent %
+\parindent = 0pt
+\parskip = \smallskipamount
+\ifdim \parskip=0pt \parskip=2pt \fi%
+\def#2{\endgraf\endgroup\afterenvbreak}%
+\def\itemcontents{#1}%
+\let\item=\itemizeitem}
+
+\def\bullet{$\ptexbullet$}
+\def\minus{$-$}
+
+\def\enumerate{\itemizey{\the\itemno.}\Eenumerate\flushcr}
+
+% Definition of @item while inside @itemize.
+
+\def\itemizeitem{%
+\advance\itemno by 1
+{\let\par=\endgraf \smallbreak}%
+\ifhmode \errmessage{\in hmode at itemizeitem}\fi
+{\parskip=0in \hskip 0pt
+\hbox to 0pt{\hss \itemcontents\hskip \itemmargin}%
+\vadjust{\penalty 300}}%
+\flushcr}
+
+\message{indexing,}
+% Index generation facilities
+
+% Define \newwrite to be identical to plain tex's \newwrite
+% except not \outer, so it can be used within \newindex.
+{\catcode`\@=11
+\gdef\newwrite{\alloc@7\write\chardef\sixt@@n}}
+
+% \newindex {foo} defines an index named foo.
+% It automatically defines \fooindex such that
+% \fooindex ...rest of line... puts an entry in the index foo.
+% It also defines \fooindfile to be the number of the output channel for
+% the file that accumulates this index. The file's extension is foo.
+% The name of an index should be no more than 2 characters long
+% for the sake of vms.
+
+\def\newindex #1{
+\expandafter\newwrite \csname#1indfile\endcsname% Define number for output file
+\openout \csname#1indfile\endcsname \jobname.#1 % Open the file
+\expandafter\xdef\csname#1index\endcsname{% % Define \xxxindex
+\noexpand\doindex {#1}}
+}
+
+% @defindex foo == \newindex{foo}
+
+\def\defindex{\parsearg\newindex}
+
+% Define @defcodeindex, like @defindex except put all entries in @code.
+
+\def\newcodeindex #1{
+\expandafter\newwrite \csname#1indfile\endcsname% Define number for output file
+\openout \csname#1indfile\endcsname \jobname.#1 % Open the file
+\expandafter\xdef\csname#1index\endcsname{% % Define \xxxindex
+\noexpand\docodeindex {#1}}
+}
+
+\def\defcodeindex{\parsearg\newcodeindex}
+
+% @synindex foo bar makes index foo feed into index bar.
+% Do this instead of @defindex foo if you don't want it as a separate index.
+\def\synindex #1 #2 {%
+\expandafter\xdef\csname#1index\endcsname{% % Define \xxxindex
+\noexpand\doindex {#2}}%
+}
+
+% @syncodeindex foo bar similar, but put all entries made for index foo
+% inside @code.
+\def\syncodeindex #1 #2 {%
+\expandafter\xdef\csname#1index\endcsname{% % Define \xxxindex
+\noexpand\docodeindex {#2}}%
+}
+
+% Define \doindex, the driver for all \fooindex macros.
+% Argument #1 is generated by the calling \fooindex macro,
+% and it is "foo", the name of the index.
+
+% \doindex just uses \parsearg; it calls \doind for the actual work.
+% This is because \doind is more useful to call from other macros.
+
+% There is also \dosubind {index}{topic}{subtopic}
+% which makes an entry in a two-level index such as the operation index.
+
+\def\doindex#1{\edef\indexname{#1}\parsearg\singleindexer}
+\def\singleindexer #1{\doind{\indexname}{#1}}
+
+% like the previous two, but they put @code around the argument.
+\def\docodeindex#1{\edef\indexname{#1}\parsearg\singlecodeindexer}
+\def\singlecodeindexer #1{\doind{\indexname}{\code{#1}}}
+
+\def\indexdummies{%
+\def\bf{\realbackslash bf }%
+\def\rm{\realbackslash rm }%
+\def\sl{\realbackslash sl }%
+\def\dots{\realbackslash dots }%
+\def\copyright{\realbackslash copyright }%
+}
+
+% \indexnofonts no-ops all font-change commands.
+% This is used when outputting the strings to sort the index by.
+\def\indexdummyfont#1{#1}
+\def\indexnofonts{%
+\let\code=\indexdummyfont
+\let\samp=\indexdummyfont
+\let\kbd=\indexdummyfont
+\let\key=\indexdummyfont
+\let\var=\indexdummyfont
+}
+
+% To define \realbackslash, we must make \ not be an escape.
+% We must first make another character (@) an escape
+% so we do not become unable to do a definition.
+
+{\catcode`\@=0 \catcode`\\=\other
+@gdef@realbackslash{\}}
+
+\let\indexbackslash=0 %overridden during \printindex.
+
+\def\doind #1#2{%
+{\indexdummies % Must do this here, since \bf, etc expand at this stage
+\count10=\lastpenalty %
+\escapechar=`\\%
+{\let\folio=0% Expand all macros now EXCEPT \folio
+\def\rawbackslashxx{\indexbackslash}% \indexbackslash isn't defined now
+% so it will be output as is; and it will print as backslash in the indx.
+%
+% Now process the index-string once, with all font commands turned off,
+% to get the string to sort the index by.
+{\indexnofonts
+\xdef\temp1{#2}%
+}%
+% Now produce the complete index entry. We process the index-string again,
+% this time with font commands expanded, to get what to print in the index.
+\edef\temp{%
+\write \csname#1indfile\endcsname{%
+\realbackslash entry {\temp1}{\folio}{#2}}}%
+\temp }%
+\penalty\count10}}
+
+\def\dosubind #1#2#3{%
+{\indexdummies % Must do this here, since \bf, etc expand at this stage
+\count10=\lastpenalty %
+\escapechar=`\\%
+{\let\folio=0%
+\def\rawbackslashxx{\indexbackslash}%
+%
+% Now process the index-string once, with all font commands turned off,
+% to get the string to sort the index by.
+{\indexnofonts
+\xdef\temp1{#2 #3}%
+}%
+% Now produce the complete index entry. We process the index-string again,
+% this time with font commands expanded, to get what to print in the index.
+\edef\temp{%
+\write \csname#1indfile\endcsname{%
+\realbackslash entry {\temp1}{\folio}{#2}{#3}}}%
+\temp }%
+\penalty\count10}}
+
+% The index entry written in the file actually looks like
+% \entry {sortstring}{page}{topic}
+% or
+% \entry {sortstring}{page}{topic}{subtopic}
+% The texindex program reads in these files and writes files
+% containing these kinds of lines:
+% \initial {c}
+% before the first topic whose initial is c
+% \entry {topic}{pagelist}
+% for a topic that is used without subtopics
+% \primary {topic}
+% for the beginning of a topic that is used with subtopics
+% \secondary {subtopic}{pagelist}
+% for each subtopic.
+
+% Define the user-accessible indexing commands
+% @findex, @vindex, @kindex, @cindex.
+
+\def\findex {\fnindex}
+\def\kindex {\kyindex}
+\def\cindex {\cpindex}
+\def\vindex {\vrindex}
+\def\tindex {\tpindex}
+\def\pindex {\pgindex}
+
+\def\cindexsub {\begingroup\obeylines\cindexsub}
+{\obeylines %
+\gdef\cindexsub "#1" #2^^M{\endgroup %
+\dosubind{cp}{#2}{#1}}}
+
+% Define the macros used in formatting output of the sorted index material.
+
+% This is what you call to cause a particular index to get printed.
+% Write
+% @unnumbered Function Index
+% @printindex fn
+
+\def\printindex{\parsearg\doprintindex}
+
+\def\doprintindex#1{\tex %
+\catcode`\%=\other\catcode`\&=\other\catcode`\#=\other
+\catcode`\$=\other\catcode`\_=\other
+\catcode`\~=\other
+\def\indexbackslash{\rawbackslashxx}
+\indexfonts\rm \tolerance=9500 \advance\baselineskip -1pt
+\begindoublecolumns
+\openin 1 \jobname.#1s
+\ifeof 1 \else \closein 1 \input \jobname.#1s
+\fi
+\enddoublecolumns
+\Etex}
+
+% These macros are used by the sorted index file itself.
+% Change them to control the appearance of the index.
+
+% Same as \bigskipamount except no shrink.
+% \balancecolumns gets confused if there is any shrink.
+\newskip\initialskipamount \initialskipamount 12pt plus4pt
+
+\outer\def\initial #1{%
+{\let\tentt=\sectt \let\sf=\sectt
+\ifdim\lastskip<\initialskipamount
+\removelastskip \penalty-200 \vskip \initialskipamount\fi
+\line{\secbf#1\hfill}\kern 2pt\penalty3000}}
+
+\outer\def\entry #1#2{
+{\parfillskip=0in \parskip=0in \parindent=0in
+\hangindent=1in \hangafter=1%
+\noindent\hbox{#1}\dotfill #2\par
+}}
+
+\def\primary #1{\line{#1\hfil}}
+
+\newskip\secondaryindent \secondaryindent=0.5cm
+
+\def\secondary #1#2{
+{\parfillskip=0in \parskip=0in
+\hangindent =1in \hangafter=1
+\noindent\hskip\secondaryindent\hbox{#1}\dotfill #2\par
+}}
+
+%% Define two-column mode, which is used in indexes.
+%% Adapted from the TeXBook, page 416
+\catcode `\@=11
+
+\newbox\partialpage
+
+\newdimen\doublecolumnhsize \doublecolumnhsize = 3.11in
+\newdimen\doublecolumnvsize \doublecolumnvsize = 19.1in
+
+\def\begindoublecolumns{\begingroup
+ \output={\global\setbox\partialpage=\vbox{\unvbox255\kern -\topskip \kern \baselineskip}}\eject
+ \output={\doublecolumnout} \hsize=\doublecolumnhsize \vsize=\doublecolumnvsize}
+\def\enddoublecolumns{\output={\balancecolumns}\eject
+ \endgroup \pagegoal=\vsize}
+
+\def\doublecolumnout{\splittopskip=\topskip \splitmaxdepth=\maxdepth
+ \dimen@=\pageheight \advance\dimen@ by-\ht\partialpage
+ \setbox0=\vsplit255 to\dimen@ \setbox2=\vsplit255 to\dimen@
+ \onepageout\pagesofar \unvbox255 \penalty\outputpenalty}
+\def\pagesofar{\unvbox\partialpage %
+ \hsize=\doublecolumnhsize % have to restore this since output routine
+% changes it to set cropmarks (P. A. MacKay, 12 Nov. 1986)
+ \wd0=\hsize \wd2=\hsize \hbox to\pagewidth{\box0\hfil\box2}}
+\def\balancecolumns{\setbox0=\vbox{\unvbox255} \dimen@=\ht0
+ \advance\dimen@ by\topskip \advance\dimen@ by-\baselineskip
+ \divide\dimen@ by2 \splittopskip=\topskip
+ {\vbadness=10000 \loop \global\setbox3=\copy0
+ \global\setbox1=\vsplit3 to\dimen@
+ \ifdim\ht3>\dimen@ \global\advance\dimen@ by1pt \repeat}
+ \setbox0=\vbox to\dimen@{\unvbox1} \setbox2=\vbox to\dimen@{\unvbox3}
+ \pagesofar}
+
+\catcode `\@=\other
+\message{sectioning,}
+% Define chapters, sections, etc.
+
+\newcount \chapno
+\newcount \secno
+\newcount \subsecno
+\newcount \subsubsecno
+
+% This counter is funny since it counts through charcodes of letters A, B, ...
+\newcount \appendixno \appendixno = `\@
+\def\appendixletter{\char\the\appendixno}
+
+\newwrite \contentsfile
+% This is called from \setfilename.
+\def\opencontents{\openout \contentsfile = \jobname.toc}
+
+% Each @chapter defines this as the name of the chapter.
+% page headings and footings can use it. @section does likewise
+
+\def\thischapter{} \def\thissection{}
+\def\seccheck#1{\if \pageno<0 %
+\errmessage{@#1 not allowed after generating table of contents}\fi
+%
+}
+
+\outer\def\chapter{\parsearg\chapterzzz}
+\def\chapterzzz #1{\seccheck{chapter}%
+\secno=0 \subsecno=0 \subsubsecno=0 \global\advance \chapno by 1 \message{Chapter \the\chapno}%
+\chapmacro {#1}{\the\chapno}%
+\gdef\thissection{#1}\gdef\thischapter{#1}%
+\let\rawbackslash=\relax%
+\let\frenchspacing=\relax%
+\edef\temp{{\realbackslash chapentry {#1}{\the\chapno}{\noexpand\folio}}}%
+\escapechar=`\\%
+\write \contentsfile \temp %
+\donoderef %
+}
+
+\outer\def\appendix{\parsearg\appendixzzz}
+\def\appendixzzz #1{\seccheck{appendix}%
+\secno=0 \subsecno=0 \subsubsecno=0 \global\advance \appendixno by 1 \message{Appendix \appendixletter}%
+\chapmacro {#1}{Appendix \appendixletter}%
+\gdef\thischapter{#1}\gdef\thissection{#1}%
+\let\rawbackslash=\relax%
+\let\frenchspacing=\relax%
+\edef\temp{{\realbackslash chapentry {#1}{Appendix \appendixletter}{\noexpand\folio}}}%
+\escapechar=`\\%
+\write \contentsfile \temp %
+\unnumbnoderef %
+}
+
+\outer\def\unnumbered{\parsearg\unnumberedzzz}
+\def\unnumberedzzz #1{\seccheck{unnumbered}%
+\secno=0 \subsecno=0 \subsubsecno=0 \message{(#1)}
+\unnumbchapmacro {#1}%
+\gdef\thischapter{#1}\gdef\thissection{#1}%
+\let\rawbackslash=\relax%
+\let\frenchspacing=\relax%
+\edef\temp{{\realbackslash unnumbchapentry {#1}{\noexpand\folio}}}%
+\escapechar=`\\%
+\write \contentsfile \temp %
+\unnumbnoderef %
+}
+
+\outer\def\section{\parsearg\sectionzzz}
+\def\sectionzzz #1{\seccheck{section}%
+\subsecno=0 \subsubsecno=0 \global\advance \secno by 1 %
+\gdef\thissection{#1}\secheading {#1}{\the\chapno}{\the\secno}%
+\let\rawbackslash=\relax%
+\let\frenchspacing=\relax%
+\edef\temp{{\realbackslash secentry %
+{#1}{\the\chapno}{\the\secno}{\noexpand\folio}}}%
+\escapechar=`\\%
+\write \contentsfile \temp %
+\donoderef %
+\penalty 10000 %
+}
+
+\outer\def\appendixsection{\parsearg\appendixsectionzzz}
+\outer\def\appendixsec{\parsearg\appendixsectionzzz}
+\def\appendixsectionzzz #1{\seccheck{appendixsection}%
+\subsecno=0 \subsubsecno=0 \global\advance \secno by 1 %
+\gdef\thissection{#1}\secheading {#1}{\appendixletter}{\the\secno}%
+\let\rawbackslash=\relax%
+\let\frenchspacing=\relax%
+\edef\temp{{\realbackslash secentry %
+{#1}{\appendixletter}{\the\secno}{\noexpand\folio}}}%
+\escapechar=`\\%
+\write \contentsfile \temp %
+\unnumbnoderef %
+\penalty 10000 %
+}
+
+\outer\def\unnumberedsec{\parsearg\unnumberedseczzz}
+\def\unnumberedseczzz #1{\seccheck{unnumberedsec}%
+\plainsecheading {#1}\gdef\thissection{#1}%
+\let\rawbackslash=\relax%
+\let\frenchspacing=\relax%
+\edef\temp{{\realbackslash unnumbsecentry{#1}{\noexpand\folio}}}%
+\escapechar=`\\%
+\write \contentsfile \temp %
+\unnumbnoderef %
+\penalty 10000 %
+}
+
+\outer\def\subsection{\parsearg\subsectionzzz}
+\def\subsectionzzz #1{\seccheck{subsection}%
+\gdef\thissection{#1}\subsubsecno=0 \global\advance \subsecno by 1 %
+\subsecheading {#1}{\the\chapno}{\the\secno}{\the\subsecno}%
+\let\rawbackslash=\relax%
+\let\frenchspacing=\relax%
+\edef\temp{{\realbackslash subsecentry %
+{#1}{\the\chapno}{\the\secno}{\the\subsecno}{\noexpand\folio}}}%
+\escapechar=`\\%
+\write \contentsfile \temp %
+\donoderef %
+\penalty 10000 %
+}
+
+\outer\def\appendixsubsec{\parsearg\appendixsubseczzz}
+\def\appendixsubseczzz #1{\seccheck{appendixsubsec}%
+\gdef\thissection{#1}\subsubsecno=0 \global\advance \subsecno by 1 %
+\subsecheading {#1}{\appendixletter}{\the\secno}{\the\subsecno}%
+\let\rawbackslash=\relax%
+\let\frenchspacing=\relax%
+\edef\temp{{\realbackslash subsecentry %
+{#1}{\appendixletter}{\the\secno}{\the\subsecno}{\noexpand\folio}}}%
+\escapechar=`\\%
+\write \contentsfile \temp %
+\unnumbnoderef %
+\penalty 10000 %
+}
+
+\outer\def\unnumberedsubsec{\parsearg\unnumberedsubseczzz}
+\def\unnumberedsubseczzz #1{\seccheck{unnumberedsubsec}%
+\plainsecheading {#1}\gdef\thissection{#1}%
+\let\rawbackslash=\relax%
+\let\frenchspacing=\relax%
+\edef\temp{{\realbackslash unnumbsubsecentry{#1}{\noexpand\folio}}}%
+\escapechar=`\\%
+\write \contentsfile \temp %
+\unnumbnoderef %
+\penalty 10000 %
+}
+
+\outer\def\subsubsection{\parsearg\subsubsectionzzz}
+\def\subsubsectionzzz #1{\seccheck{subsubsection}%
+\gdef\thissection{#1}\global\advance \subsubsecno by 1 %
+\subsubsecheading {#1}{\the\chapno}{\the\secno}{\the\subsecno}{\the\subsubsecno}%
+\let\rawbackslash=\relax%
+\let\frenchspacing=\relax%
+\edef\temp{{\realbackslash subsubsecentry %
+{#1}{\the\chapno}{\the\secno}{\the\subsecno}{\the\subsubsecno}{\noexpand\folio}}}%\
+\escapechar=`\\%
+\write \contentsfile \temp %
+\donoderef %
+\penalty 10000 %
+}
+
+\outer\def\appendixsubsubsec{\parsearg\appendixsubsubseczzz}
+\def\appendixsubsubseczzz #1{\seccheck{appendixsubsubsec}%
+\gdef\thissection{#1}\global\advance \subsubsecno by 1 %
+\subsubsecheading {#1}{\appendixletter}{\the\secno}{\the\subsecno}{\the\subsubsecno}%
+\let\rawbackslash=\relax%
+\let\frenchspacing=\relax%
+\edef\temp{{\realbackslash subsubsecentry{#1}%
+{\appendixletter}{\the\secno}{\the\subsecno}{\the\subsubsecno}{\noexpand\folio}}}%\
+\escapechar=`\\%
+\write \contentsfile \temp %
+\unnumbnoderef %
+\penalty 10000 %
+}
+
+\outer\def\unnumberedsubsubsec{\parsearg\unnumberedsubsubseczzz}
+\def\unnumberedsubsubseczzz #1{\seccheck{unnumberedsubsubsec}%
+\plainsecheading {#1}\gdef\thissection{#1}%
+\let\rawbackslash=\relax%
+\let\frenchspacing=\relax%
+\edef\temp{{\realbackslash unnumbsubsubsecentry{#1}{\noexpand\folio}}}%
+\escapechar=`\\%
+\write \contentsfile \temp %
+\unnumbnoderef %
+\penalty 10000 %
+}
+
+% These are variants which are not "outer", so they can appear in @ifinfo.
+\def\infounnumbered{\parsearg\unnumberedzzz}
+\def\infounnumberedsec{\parsearg\unnumberedseczzz}
+\def\infounnumberedsubsec{\parsearg\unnumberedsubseczzz}
+\def\infounnumberedsubsubsec{\parsearg\unnumberedsubsubseczzz}
+
+\def\infoappendix{\parsearg\appendixzzz}
+\def\infoappendixsec{\parsearg\appendixseczzz}
+\def\infoappendixsubsec{\parsearg\appendixsubseczzz}
+\def\infoappendixsubsubsec{\parsearg\appendixsubsubseczzz}
+
+\def\infochapter{\parsearg\chapterzzz}
+\def\infosection{\parsearg\sectionzzz}
+\def\infosubsection{\parsearg\subsectionzzz}
+\def\infosubsubsection{\parsearg\subsubsectionzzz}
+
+% Define @majorheading, @heading and @subheading
+
+\def\majorheading #1{%
+{\advance\chapheadingskip by 10pt \chapbreak }%
+{\chapfonts \line{\rm #1\hfill}}\bigskip \par\penalty 200}
+
+\def\chapheading #1{\chapbreak %
+{\chapfonts \line{\rm #1\hfill}}\bigskip \par\penalty 200}
+
+\def\heading{\parsearg\secheadingi}
+
+% These macros generate a chapter, section, etc. heading only
+% (including whitespace, linebreaking, etc. around it),
+% given all the information in convenient, parsed form.
+
+%%% Args are the skip and penalty (usually negative)
+\def\dobreak#1#2{\par\ifdim\lastskip<#1\removelastskip\penalty#2\vskip#1\fi}
+
+\def\setchapterstyle #1 {\csname CHAPF#1\endcsname}
+
+%%% Define plain chapter starts, and page on/off switching for it
+% Parameter controlling skip before chapter headings (if needed)
+
+\newskip \chapheadingskip \chapheadingskip = 30pt plus 8pt minus 4pt
+
+\def\chapbreak{\dobreak \chapheadingskip {-4000}}
+\def\chappager{\par\vfill\supereject}
+\def\chapoddpage{\chappager \ifodd\pageno \else \hbox to 0pt{} \chappager\fi}
+
+\def\setchapternewpage #1 {\csname CHAPPAG#1\endcsname}
+
+\def\CHAPPAGoff{
+\global\let\pchapsepmacro=\chapbreak
+\global\let\pagealignmacro=\chappager}
+
+\def\CHAPPAGon{
+\global\let\pchapsepmacro=\chappager
+\global\let\pagealignmacro=\chappager
+\global\def\HEADINGSon{\HEADINGSsingle}}
+
+\def\CHAPPAGodd{
+\global\let\pchapsepmacro=\chapoddpage
+\global\let\pagealignmacro=\chapoddpage
+\global\def\HEADINGSon{\HEADINGSdouble}}
+
+\CHAPPAGon
+
+\def\CHAPFplain{
+\global\let\chapmacro=\chfplain
+\global\let\unnumbchapmacro=\unnchfplain}
+
+\def\chfplain #1#2{%
+\pchapsepmacro %
+{\chapfonts \line{\rm #2.\enspace #1\hfill}}\bigskip \par\penalty 5000 %
+}
+
+\def\unnchfplain #1{%
+\pchapsepmacro %
+{\chapfonts \line{\rm #1\hfill}}\bigskip \par\penalty 10000 %
+}
+\CHAPFplain % The default
+
+\def\unnchfopen #1{%
+\chapoddpage {\chapfonts \line{\rm #1\hfill}}\bigskip \par\penalty 10000 %
+}
+
+\def\chfopen #1#2{\chapoddpage {\chapfonts
+\vbox to 3in{\vfil \hbox to\hsize{\hfil #2} \hbox to\hsize{\hfil #1} \vfil}}%
+\par\penalty 5000 %
+}
+
+\def\CHAPFopen{
+\global\let\chapmacro=\chfopen
+\global\let\unnumbchapmacro=\unnchfopen}
+
+% Parameter controlling skip before section headings.
+
+\newskip \subsecheadingskip \subsecheadingskip = 17pt plus 8pt minus 4pt
+\def\subsecheadingbreak{\dobreak \subsecheadingskip {-500}}
+
+\newskip \secheadingskip \secheadingskip = 21pt plus 8pt minus 4pt
+\def\secheadingbreak{\dobreak \secheadingskip {-1000}}
+
+
+% Section fonts are the base font at magstep2, which produces
+% a size a bit more than 14 points in the default situation.
+
+\def\secheading #1#2#3{\secheadingi {#2.#3\enspace #1}}
+\def\plainsecheading #1{\secheadingi {#1}}
+\def\secheadingi #1{{\advance \secheadingskip by \parskip %
+\secheadingbreak}%
+{\secfonts \line{\rm #1\hfill}}%
+\ifdim \parskip<10pt \kern 10pt\kern -\parskip\fi \penalty 10000 }
+
+
+% Subsection fonts are the base font at magstep1,
+% which produces a size of 12 points.
+
+\def\subsecheading #1#2#3#4{{\advance \subsecheadingskip by \parskip %
+\subsecheadingbreak}%
+{\subsecfonts \line{\rm#2.#3.#4\enspace #1\hfill}}%
+\ifdim \parskip<10pt \kern 10pt\kern -\parskip\fi \penalty 10000 }
+
+\def\subsubsecfonts{\subsecfonts} % Maybe this should change:
+ % Perhaps make sssec fonts scaled
+ % magstep half
+\def\subsubsecheading #1#2#3#4#5{{\advance \subsecheadingskip by \parskip %
+\subsecheadingbreak}%
+{\subsubsecfonts \line{\rm#2.#3.#4.#5\enspace #1\hfill}}%
+\ifdim \parskip<10pt \kern 10pt\kern -\parskip\fi \penalty 10000}
+
+
+\message{toc printing,}
+
+% Finish up the main text and prepare to read what we've written
+% to \contentsfile.
+
+\def\startcontents#1{%
+ \ifnum \pageno>0
+ \pagealignmacro
+ \immediate\closeout \contentsfile
+ \pageno = -1 % Request roman numbered pages.
+ \fi
+ \unnumbchapmacro{#1}\def\thischapter{#1}%
+ \begingroup % Set up to handle contents files properly.
+ \catcode`\\=0 \catcode`\{=1 \catcode`\}=2 \catcode`\@=11
+ \raggedbottom % Worry more about breakpoints than the bottom.
+ \advance\hsize by -1in % Don't use the full line length.
+}
+
+
+% Normal (long) toc.
+\outer\def\contents{%
+ \startcontents{Table of Contents}%
+ \input \jobname.toc
+ \endgroup
+ \vfill \eject
+}
+
+% And just the chapters.
+\outer\def\summarycontents{%
+ \startcontents{Short Contents}%
+ %
+ \let\chapentry = \shortchapentry
+ \let\unnumbchapentry = \shortunnumberedentry
+ % We want a true roman here for the page numbers.
+ \secfonts \let\rm = \truesecrm \rm
+ \advance\baselineskip by 1pt % Open it up a little.
+ \def\secentry ##1##2##3##4{}
+ \def\unnumbsecentry ##1##2{}
+ \def\subsecentry ##1##2##3##4##5{}
+ \def\unnumbsubsecentry ##1##2{}
+ \def\subsubsecentry ##1##2##3##4##5##6{}
+ \def\unnumbsubsubsecentry ##1##2{}
+ \input \jobname.toc
+ \endgroup
+ \vfill \eject
+}
+\let\shortcontents = \summarycontents
+
+% These macros generate individual entries in the table of contents.
+% The first argument is the chapter or section name.
+% The last argument is the page number.
+% The arguments in between are the chapter number, section number, ...
+
+% Chapter-level things, for both the long and short contents.
+\def\chapentry#1#2#3{\dochapentry{#2\labelspace#1}{#3}}
+\def\shortchapentry#1#2#3{%
+ \line{{#2\labelspace #1}\dotfill\doshortpageno{#3}}%
+}
+
+\def\unnumbchapentry#1#2{\dochapentry{#1}{#2}}
+\def\shortunnumberedentry#1#2{%
+ \line{#1\dotfill\doshortpageno{#2}}%
+}
+
+% Sections.
+\def\secentry#1#2#3#4{\dosecentry{#2.#3\labelspace#1}{#4}}
+\def\unnumbsecentry#1#2{\dosecentry{#1}{#2}}
+
+% Subsections.
+\def\subsecentry#1#2#3#4#5{\dosubsecentry{#2.#3.#4\labelspace#1}{#5}}
+\def\unnumbsubsecentry#1#2{\dosubsecentry{#1}{#2}}
+
+% And subsubsections.
+\def\subsubsecentry#1#2#3#4#5#6{\dosubsubsecentry{#2.#3.#4.#5\labelspace#1}{#6}}
+\def\unnumbsubsecentry#1#2{\dosubsubsecentry{#1}{#2}}
+
+
+% This parameter controls the indentation of the various levels.
+\newdimen\tocindent \tocindent = 3pc
+
+% Now for the actual typesetting. In all these, #1 is the text and #2 is the
+% page number.
+%
+% If the toc has to be broken over pages, we would want to be at chapters
+% if at all possible; hence the \penalty.
+\def\dochapentry#1#2{%
+ \penalty-300 \vskip\baselineskip
+ \line{\chapentryfonts #1\dotfill \dopageno{#2}}%
+ \nobreak\vskip .25\baselineskip
+}
+
+\def\dosecentry#1#2{%
+ \line{\secentryfonts \hskip\tocindent #1\dotfill \dopageno{#2}}%
+}
+
+\def\dosubsecentry#1#2{%
+ \line{\subsecentryfonts \hskip2\tocindent #1\dotfill \dopageno{#2}}%
+}
+
+\def\dosubsubsecentry#1#2{%
+ \line{\subsubsecentryfonts \hskip3\tocindent #1\dotfill \dopageno{#2}}%
+}
+
+% Space between chapter (or whatever) number and the title.
+\def\labelspace{\hskip1em \relax}
+
+\def\dopageno#1{{\rm #1}}
+\def\doshortpageno#1{{\rm #1}}
+
+\def\chapentryfonts{\secfonts \let\rm = \sf \sf}
+\def\secentryfonts{\textfonts}
+\let\subsecentryfonts = \textfonts
+\let\subsubsecentryfonts = \textfonts
+
+
+\message{environments,}
+
+% @tex ... @end tex escapes into raw Tex temporarily.
+% One exception: @ is still an escape character, so that @end tex works.
+% But \@ or @@ will get a plain tex @ character.
+
+\def\tex{\begingroup
+\catcode `\\=0 \catcode `\{=1 \catcode `\}=2
+\catcode `\$=3 \catcode `\&=4 \catcode `\#=6
+\catcode `\^=7 \catcode `\_=8 \catcode `\~=13 \let~=\tie
+\catcode `\%=14
+\catcode`\"=12
+\catcode`\|=12
+\catcode`\<=12
+\catcode`\>=12
+\escapechar=`\\
+%
+\let\{=\ptexlbrace
+\let\}=\ptexrbrace
+\let\.=\ptexdot
+\let\*=\ptexstar
+\def\@={@}%
+\let\bullet=\ptexbullet
+\let\b=\ptexb \let\c=\ptexc \let\i=\ptexi \let\t=\ptext \let\l=\ptexl
+\let\L=\ptexL
+%
+\let\Etex=\endgroup}
+
+% Define @lisp ... @endlisp.
+% @lisp does a \begingroup so it can rebind things,
+% including the definition of @endlisp (which normally is erroneous).
+
+% Amount to narrow the margins by for @lisp.
+\newskip\lispnarrowing \lispnarrowing=0.4in
+
+% This is the definition that ^M gets inside @lisp
+% phr: changed space to \null, to avoid overfull hbox problems.
+{\obeyspaces%
+\gdef\lisppar{\null\endgraf}}
+
+% Cause \obeyspaces to make each Space cause a word-separation
+% rather than the default which is that it acts punctuation.
+% This is because space in tt font looks funny.
+{\obeyspaces %
+\gdef\sepspaces{\def {\ }}}
+
+\newskip\aboveenvskipamount \aboveenvskipamount= 0pt
+\def\aboveenvbreak{{\advance\aboveenvskipamount by \parskip
+\endgraf \ifdim\lastskip<\aboveenvskipamount
+\removelastskip \penalty-50 \vskip\aboveenvskipamount \fi}}
+
+\def\afterenvbreak{\endgraf \ifdim\lastskip<\aboveenvskipamount
+\removelastskip \penalty-50 \vskip\aboveenvskipamount \fi}
+
+\def\lisp{\aboveenvbreak\begingroup\inENV %This group ends at the end of the @lisp body
+\hfuzz=12truept % Don't be fussy
+% Make spaces be word-separators rather than space tokens.
+\sepspaces %
+% Single space lines
+\singlespace %
+% The following causes blank lines not to be ignored
+% by adding a space to the end of each line.
+\let\par=\lisppar
+\def\Elisp{\endgroup\afterenvbreak}%
+\parskip=0pt
+\advance \leftskip by \lispnarrowing
+\parindent=0pt
+\let\exdent=\internalexdent
+\obeyspaces \obeylines \tt \rawbackslash
+\def\next##1{}\next}
+
+
+\let\example=\lisp
+\def\Eexample{\Elisp}
+
+\let\smallexample=\lisp
+\def\Esmallexample{\Elisp}
+
+% Macro for 9 pt. examples, necessary to print with 5" lines.
+% From Pavel@xerox. This is not really used unless the
+% @smallbook command is given.
+
+\def\smalllispx{\aboveenvbreak\begingroup\inENV
+% This group ends at the end of the @lisp body
+\hfuzz=12truept % Don't be fussy
+% Make spaces be word-separators rather than space tokens.
+\sepspaces %
+% Single space lines
+\singlespace %
+% The following causes blank lines not to be ignored
+% by adding a space to the end of each line.
+\let\par=\lisppar
+\def\Esmalllisp{\endgroup\afterenvbreak}%
+\parskip=0pt
+\advance \leftskip by \lispnarrowing
+\parindent=0pt
+\let\exdent=\internalexdent
+\obeyspaces \obeylines \ninett \rawbackslash
+\def\next##1{}\next}
+
+% This is @display; same as @lisp except use roman font.
+
+\def\display{\begingroup\inENV %This group ends at the end of the @display body
+\aboveenvbreak
+% Make spaces be word-separators rather than space tokens.
+\sepspaces %
+% Single space lines
+\singlespace %
+% The following causes blank lines not to be ignored
+% by adding a space to the end of each line.
+\let\par=\lisppar
+\def\Edisplay{\endgroup\afterenvbreak}%
+\parskip=0pt
+\advance \leftskip by \lispnarrowing
+\parindent=0pt
+\let\exdent=\internalexdent
+\obeyspaces \obeylines
+\def\next##1{}\next}
+
+% This is @format; same as @lisp except use roman font and don't narrow margins
+
+\def\format{\begingroup\inENV %This group ends at the end of the @format body
+\aboveenvbreak
+% Make spaces be word-separators rather than space tokens.
+\sepspaces %
+\singlespace %
+% The following causes blank lines not to be ignored
+% by adding a space to the end of each line.
+\let\par=\lisppar
+\def\Eformat{\endgroup\afterenvbreak}
+\parskip=0pt \parindent=0pt
+\obeyspaces \obeylines
+\def\next##1{}\next}
+
+% @flushleft and @flushright
+
+\def\flushleft{\begingroup\inENV %This group ends at the end of the @format body
+\aboveenvbreak
+% Make spaces be word-separators rather than space tokens.
+\sepspaces %
+% The following causes blank lines not to be ignored
+% by adding a space to the end of each line.
+% This also causes @ to work when the directive name
+% is terminated by end of line.
+\let\par=\lisppar
+\def\Eflushleft{\endgroup\afterenvbreak}%
+\parskip=0pt \parindent=0pt
+\obeyspaces \obeylines
+\def\next##1{}\next}
+
+\def\flushright{\begingroup\inENV %This group ends at the end of the @format body
+\aboveenvbreak
+% Make spaces be word-separators rather than space tokens.
+\sepspaces %
+% The following causes blank lines not to be ignored
+% by adding a space to the end of each line.
+% This also causes @ to work when the directive name
+% is terminated by end of line.
+\let\par=\lisppar
+\def\Eflushright{\endgroup\afterenvbreak}%
+\parskip=0pt \parindent=0pt
+\advance \leftskip by 0pt plus 1fill
+\obeyspaces \obeylines
+\def\next##1{}\next}
+
+% @quotation - narrow the margins.
+
+\def\quotation{\begingroup\inENV %This group ends at the end of the @quotation body
+{\parskip=0pt % because we will skip by \parskip too, later
+\aboveenvbreak}%
+\singlespace
+\parindent=0pt
+\def\Equotation{\par\endgroup\afterenvbreak}%
+\advance \rightskip by \lispnarrowing
+\advance \leftskip by \lispnarrowing}
+
+\message{defuns,}
+% Define formatter for defuns
+% First, allow user to change definition object font (\df) internally
+\def\setdeffont #1 {\csname DEF#1\endcsname}
+
+\newskip\defbodyindent \defbodyindent=36pt
+\newskip\defargsindent \defargsindent=50pt
+\newskip\deftypemargin \deftypemargin=12pt
+\newskip\deflastargmargin \deflastargmargin=18pt
+
+\newcount\parencount
+% define \functionparens, which makes ( and ) and & do special things.
+% \functionparens affects the group it is contained in.
+\def\activeparens{%
+\catcode`\(=\active \catcode`\)=\active \catcode`\&=\active
+\catcode`\[=\active \catcode`\]=\active}
+{\activeparens % Now, smart parens don't turn on until &foo (see \amprm)
+\gdef\functionparens{\boldbrax\let&=\amprm\parencount=0 }
+\gdef\boldbrax{\let(=\opnr\let)=\clnr\let[=\lbrb\let]=\rbrb}
+
+% Definitions of (, ) and & used in args for functions.
+% This is the definition of ( outside of all parentheses.
+\gdef\oprm#1 {{\rm\char`\(}#1 \bf \let(=\opnested %
+\global\advance\parencount by 1 }
+%
+% This is the definition of ( when already inside a level of parens.
+\gdef\opnested{\char`\(\global\advance\parencount by 1 }
+%
+\gdef\clrm{% Print a paren in roman if it is taking us back to depth of 0.
+% also in that case restore the outer-level definition of (.
+\ifnum \parencount=1 {\rm \char `\)}\sl \let(=\oprm \else \char `\) \fi
+\global\advance \parencount by -1 }
+% If we encounter &foo, then turn on ()-hacking afterwards
+\gdef\amprm#1 {{\rm\&#1}\let(=\oprm \let)=\clrm\ }
+%
+\gdef\normalparens{\boldbrax\let&=\ampnr}
+} % End of definition inside \activeparens
+%% These parens (in \boldbrax) actually are a little bolder than the
+%% contained text. This is especially needed for [ and ]
+\def\opnr{{\sf\char`\(}} \def\clnr{{\sf\char`\)}} \def\ampnr{\&}
+\def\lbrb{{\tt\char`\[}} \def\rbrb{{\tt\char`\]}}
+
+% First, defname, which formats the header line itself.
+% #1 should be the function name.
+% #2 should be the type of definition, such as "Function".
+
+\def\defname #1#2{%
+\leftskip = 0in %
+\noindent %
+\setbox0=\hbox{\hskip \deflastargmargin{\rm #2}\hskip \deftypemargin}%
+\dimen0=\hsize \advance \dimen0 by -\wd0 % compute size for first line
+\dimen1=\hsize \advance \dimen1 by -\defargsindent %size for continuations
+\parshape 2 0in \dimen0 \defargsindent \dimen1 %
+% Now output arg 2 ("Function" or some such)
+% ending at \deftypemargin from the right margin,
+% but stuck inside a box of width 0 so it does not interfere with linebreaking
+\rlap{\rightline{{\rm #2}\hskip \deftypemargin}}%
+\tolerance=10000 \hbadness=10000 % Make all lines underfull and no complaints
+{\df #1}\enskip % Generate function name
+}
+
+% Actually process the body of a definition
+% #1 should be the terminating control sequence, such as \Edefun.
+% #2 should be the "another name" control sequence, such as \defunx.
+% #3 should be the control sequence that actually processes the header,
+% such as \defunheader.
+
+\def\defparsebody #1#2#3{\begingroup\inENV% Environment for definitionbody
+\medbreak %
+% Define the end token that this defining construct specifies
+% so that it will exit this group.
+\def#1{\endgraf\endgroup\medbreak}%
+\def#2{\begingroup\obeylines\activeparens\spacesplit#3}%
+\parindent=0in \leftskip=\defbodyindent %
+\begingroup\obeylines\activeparens\spacesplit#3}
+
+\def\defmethparsebody #1#2#3#4 {\begingroup\inENV %
+\medbreak %
+% Define the end token that this defining construct specifies
+% so that it will exit this group.
+\def#1{\endgraf\endgroup\medbreak}%
+\def#2##1 {\begingroup\obeylines\activeparens\spacesplit{#3{##1}}}%
+\parindent=0in \leftskip=\defbodyindent %
+\begingroup\obeylines\activeparens\spacesplit{#3{#4}}}
+
+\def\defopparsebody #1#2#3#4#5 {\begingroup\inENV %
+\medbreak %
+% Define the end token that this defining construct specifies
+% so that it will exit this group.
+\def#1{\endgraf\endgroup\medbreak}%
+\def#2##1 ##2 {\def#4{##1}%
+\begingroup\obeylines\activeparens\spacesplit{#3{##2}}}%
+\parindent=0in \leftskip=\defbodyindent %
+\begingroup\obeylines\activeparens\spacesplit{#3{#5}}}
+
+% Split up #2 at the first space token.
+% call #1 with two arguments:
+% the first is all of #2 before the space token,
+% the second is all of #2 after that space token.
+% If #2 contains no space token, all of it is passed as the first arg
+% and the second is passed as empty.
+
+{\obeylines
+\gdef\spacesplit#1#2^^M{\endgroup\spacesplitfoo{#1}#2 \relax\spacesplitfoo}%
+\long\gdef\spacesplitfoo#1#2 #3#4\spacesplitfoo{%
+\ifx\relax #3%
+#1{#2}{}\else #1{#2}{#3#4}\fi}}
+
+% So much for the things common to all kinds of definitions.
+
+% Define @defun.
+
+% First, define the processing that is wanted for arguments of \defun
+% Use this to expand the args and terminate the paragraph they make up
+
+\def\defunargs #1{\functionparens \sl #1%
+\ifnum\parencount=0 \else \errmessage{unbalanced parens in @def arguments}\fi%
+\interlinepenalty=10000
+\endgraf\vskip -\parskip \penalty 10000}
+
+% Do complete processing of one @defun or @defunx line already parsed.
+
+% @deffn Command forward-char nchars
+
+\def\deffn{\defmethparsebody\Edeffn\deffnx\deffnheader}
+
+\def\deffnheader #1#2#3{\doind {fn}{\code{#2}}%
+\begingroup\defname {#2}{#1}\defunargs{#3}\endgroup}
+
+% @defun == @deffn Function
+
+\def\defun{\defparsebody\Edefun\defunx\defunheader}
+
+\def\defunheader #1#2{\doind {fn}{\code{#1}}% Make entry in function index
+\begingroup\defname {#1}{Function}%
+\defunargs {#2}\endgroup %
+}
+
+% @defmac == @deffn Macro
+
+\def\defmac{\defparsebody\Edefmac\defmacx\defmacheader}
+
+\def\defmacheader #1#2{\doind {fn}{\code{#1}}% Make entry in function index
+\begingroup\defname {#1}{Macro}%
+\defunargs {#2}\endgroup %
+}
+
+% @defspec == @deffn Special Form
+
+\def\defspec{\defparsebody\Edefspec\defspecx\defspecheader}
+
+\def\defspecheader #1#2{\doind {fn}{\code{#1}}% Make entry in function index
+\begingroup\defname {#1}{Special form}%
+\defunargs {#2}\endgroup %
+}
+
+% This definition is run if you use @defunx
+% anywhere other than immediately after a @defun or @defunx.
+
+\def\deffnx #1 {\errmessage{@deffnx in invalid context}}
+\def\defunx #1 {\errmessage{@defunx in invalid context}}
+\def\defmacx #1 {\errmessage{@defmacx in invalid context}}
+\def\defspecx #1 {\errmessage{@defspecx in invalid context}}
+
+% @defmethod, and so on
+
+% @defop {Funny Method} foo-class frobnicate argument
+
+\def\defop #1 {\def\defoptype{#1}%
+\defopparsebody\Edefop\defopx\defopheader\defoptype}
+
+\def\defopheader #1#2#3{\dosubind {fn}{\code{#2}}{on #1}% Make entry in function index
+\begingroup\defname {#2}{\defoptype{} on #1}%
+\defunargs {#3}\endgroup %
+}
+
+% @defmethod == @defop Method
+
+\def\defmethod{\defmethparsebody\Edefmethod\defmethodx\defmethodheader}
+
+\def\defmethodheader #1#2#3{\dosubind {fn}{\code{#2}}{on #1}% entry in function index
+\begingroup\defname {#2}{Operation on #1}%
+\defunargs {#3}\endgroup %
+}
+
+% @defcv {Class Option} foo-class foo-flag
+
+\def\defcv #1 {\def\defcvtype{#1}%
+\defopparsebody\Edefcv\defcvx\defcvheader\defcvtype}
+
+\def\defcvarheader #1#2#3{%
+\dosubind {vr}{\code{#2}}{of #1}% Make entry in var index
+\begingroup\defname {#2}{\defcvtype of #1}%
+\defvarargs {#3}\endgroup %
+}
+
+% @defivar == @defcv {Instance Variable}
+
+\def\defivar{\defmethparsebody\Edefivar\defivarx\defivarheader}
+
+\def\defivarheader #1#2#3{%
+\dosubind {vr}{\code{#2}}{of #1}% Make entry in var index
+\begingroup\defname {#2}{Instance variable of #1}%
+\defvarargs {#3}\endgroup %
+}
+
+% These definitions are run if you use @defmethodx, etc.,
+% anywhere other than immediately after a @defmethod, etc.
+
+\def\defopx #1 {\errmessage{@defopx in invalid context}}
+\def\defmethodx #1 {\errmessage{@defmethodx in invalid context}}
+\def\defcvx #1 {\errmessage{@defcvx in invalid context}}
+\def\defivarx #1 {\errmessage{@defivarx in invalid context}}
+
+% Now @defvar
+
+% First, define the processing that is wanted for arguments of @defvar.
+% This is actually simple: just print them in roman.
+% This must expand the args and terminate the paragraph they make up
+\def\defvarargs #1{\normalparens #1%
+\interlinepenalty=10000
+\endgraf\vskip -\parskip \penalty 10000}
+
+% @defvr Counter foo-count
+
+\def\defvr{\defmethparsebody\Edefvr\defvrx\defvrheader}
+
+\def\defvrheader #1#2#3{\doind {vr}{\code{#2}}%
+\begingroup\defname {#2}{#1}\defvarargs{#3}\endgroup}
+
+% @defvar == @defvr Variable
+
+\def\defvar{\defparsebody\Edefvar\defvarx\defvarheader}
+
+\def\defvarheader #1#2{\doind {vr}{\code{#1}}% Make entry in var index
+\begingroup\defname {#1}{Variable}%
+\defvarargs {#2}\endgroup %
+}
+
+% @defopt == @defvr {User Option}
+
+\def\defopt{\defparsebody\Edefopt\defoptx\defoptheader}
+
+\def\defoptheader #1#2{\doind {vr}{\code{#1}}% Make entry in var index
+\begingroup\defname {#1}{User Option}%
+\defvarargs {#2}\endgroup %
+}
+
+% This definition is run if you use @defvarx
+% anywhere other than immediately after a @defvar or @defvarx.
+
+\def\defvrx #1 {\errmessage{@defvrx in invalid context}}
+\def\defvarx #1 {\errmessage{@defvarx in invalid context}}
+\def\defoptx #1 {\errmessage{@defoptx in invalid context}}
+
+% Now define @deftp
+% Args are printed in bold, a slight difference from @defvar.
+
+\def\deftpargs #1{\bf \defvarargs{#1}}
+
+% @deftp Class window height width ...
+
+\def\deftp{\defmethparsebody\Edeftp\deftpx\deftpheader}
+
+\def\deftpheader #1#2#3{\doind {tp}{\code{#2}}%
+\begingroup\defname {#2}{#1}\deftpargs{#3}\endgroup}
+
+% This definition is run if you use @deftpx, etc
+% anywhere other than immediately after a @deftp, etc.
+
+\def\deftpx #1 {\errmessage{@deftpx in invalid context}}
+
+\message{cross reference,}
+% Define cross-reference macros
+\newwrite \auxfile
+
+% \setref{foo} defines a cross-reference point named foo.
+
+\def\setref#1{%
+\dosetq{#1-pg}{Ypagenumber}%
+\dosetq{#1-snt}{Ysectionnumberandtype}}
+
+\def\unnumbsetref#1{%
+\dosetq{#1-pg}{Ypagenumber}%
+\dosetq{#1-snt}{Ynothing}}
+
+% \xref and \pxref generate cross references to specified points.
+
+\def\pxref #1{see \xrefX [#1,,,,,,,]}
+\def\xref #1{See \xrefX [#1,,,,,,,]}
+\def\xrefX [#1,#2,#3,#4,#5,#6]{%
+\setbox1=\hbox{\i{\losespace#5{}}}%
+\setbox0=\hbox{\losespace#3{}}%
+\ifdim \wd0 =0pt \setbox0=\hbox{\losespace#1{}}\fi%
+\ifdim \wd1 >0pt%
+section \unhbox0{} in \unhbox1%
+\else%
+\refx{#1-snt} [\unhbox0], page\tie \refx{#1-pg}%
+\fi }
+
+% \dosetq is the interface for calls from other macros
+
+\def\dosetq #1#2{{\let\folio=0%
+\edef\next{\write\auxfile{\internalsetq {#1}{#2}}}%
+\next}}
+
+% \internalsetq {foo}{page} expands into CHARACTERS 'xrdef {foo}{...expansion of \Ypage...}
+% When the aux file is read, ' is the escape character
+
+\def\internalsetq #1#2{'xrdef {#1}{\csname #2\endcsname}}
+
+% Things to be expanded by \internalsetq
+
+\def\Ypagenumber{\folio}
+
+\def\Ynothing{}
+
+\def\Ysectionnumberandtype{%
+\ifnum\secno=0 chapter\xreftie\the\chapno %
+\else \ifnum \subsecno=0 section\xreftie\the\chapno.\the\secno %
+\else \ifnum \subsubsecno=0 %
+section\xreftie\the\chapno.\the\secno.\the\subsecno %
+\else %
+section\xreftie\the\chapno.\the\secno.\the\subsecno.\the\subsubsecno %
+\fi \fi \fi }
+
+\gdef\xreftie{'tie}
+
+% Define @refx to reference a specific cross-reference string.
+
+\def\refx#1{%
+{%
+\expandafter\ifx\csname X#1\endcsname\relax
+% If not defined, say something at least.
+\expandafter\gdef\csname X#1\endcsname {$<$undefined$>$}%
+\message {WARNING: Cross-reference "#1" used but not yet defined}%
+\message {}%
+\fi %
+\csname X#1\endcsname %It's defined, so just use it.
+}}
+
+% Read the last existing aux file, if any. No error if none exists.
+
+% This is the macro invoked by entries in the aux file.
+\def\xrdef #1#2{
+{\catcode`\'=\other\expandafter \gdef \csname X#1\endcsname {#2}}}
+
+\def\readauxfile{%
+\begingroup
+\catcode `\^^@=\other
+\catcode `\=\other
+\catcode `\=\other
+\catcode `\^^C=\other
+\catcode `\^^D=\other
+\catcode `\^^E=\other
+\catcode `\^^F=\other
+\catcode `\^^G=\other
+\catcode `\^^H=\other
+\catcode `\ =\other
+\catcode `\^^L=\other
+\catcode `\=\other
+\catcode `\=\other
+\catcode `\=\other
+\catcode `\=\other
+\catcode `\=\other
+\catcode `\=\other
+\catcode `\=\other
+\catcode `\=\other
+\catcode `\=\other
+\catcode `\=\other
+\catcode `\=\other
+\catcode `\=\other
+\catcode `\=\other
+\catcode `\^^[=\other
+\catcode `\^^\=\other
+\catcode `\^^]=\other
+\catcode `\^^^=\other
+\catcode `\^^_=\other
+\catcode `\@=\other
+\catcode `\^=\other
+\catcode `\~=\other
+\catcode `\[=\other
+\catcode `\]=\other
+\catcode`\"=\other
+\catcode`\_=\other
+\catcode`\|=\other
+\catcode`\<=\other
+\catcode`\>=\other
+\catcode `\$=\other
+\catcode `\#=\other
+\catcode `\&=\other
+% the aux file uses ' as the escape.
+% Turn off \ as an escape so we do not lose on
+% entries which were dumped with control sequences in their names.
+% For example, 'xrdef {$\leq $-fun}{page ...} made by @defun ^^
+% Reference to such entries still does not work the way one would wish,
+% but at least they do not bomb out when the aux file is read in.
+\catcode `\{=1 \catcode `\}=2
+\catcode `\%=\other
+\catcode `\'=0
+\catcode `\\=\other
+\openin 1 \jobname.aux
+\ifeof 1 \else \closein 1 \input \jobname.aux
+\fi
+% Open the new aux file. Tex will close it automatically at exit.
+\openout \auxfile=\jobname.aux
+\endgroup}
+
+
+% Footnotes.
+
+\newcount \footnoteno
+
+\def\supereject{\par\penalty -20000\footnoteno =0 }
+
+\let\ptexfootnote=\footnote
+
+{\catcode `\@=11
+\gdef\footnote{\global\advance \footnoteno by \@ne
+\edef\thisfootno{$^{\the\footnoteno}$}%
+\let\@sf\empty
+\ifhmode\edef\@sf{\spacefactor\the\spacefactor}\/\fi
+\thisfootno\@sf\parsearg\footnotezzz}
+
+\gdef\footnotezzz #1{\insert\footins{
+\interlinepenalty\interfootnotelinepenalty
+\splittopskip\ht\strutbox % top baseline for broken footnotes
+\splitmaxdepth\dp\strutbox \floatingpenalty\@MM
+\leftskip\z@skip \rightskip\z@skip \spaceskip\z@skip \xspaceskip\z@skip
+\footstrut\hang\textindent{\thisfootno}#1\strut}}
+
+}%end \catcode `\@=11
+
+% End of control word definitions.
+
+\message{and turning on texinfo input format.}
+
+\def\openindices{%
+ \newindex{cp}%
+ \newcodeindex{fn}%
+ \newcodeindex{vr}%
+ \newcodeindex{tp}%
+ \newcodeindex{ky}%
+ \newcodeindex{pg}%
+}
+
+% Set some numeric style parameters, for 8.5 x 11 format.
+
+\hsize = 6.5in
+\parindent 15pt
+\parskip 18pt plus 1pt
+\baselineskip 15pt
+\advance\topskip by 1.2cm
+
+% Prevent underfull vbox error messages.
+\vbadness=10000
+
+% Use @smallbook to reset parameters for 7x9.5 format
+\def\smallbook{
+\global\lispnarrowing = 0.3in
+\global\baselineskip 12pt
+\global\parskip 3pt plus 1pt
+\global\hsize = 5in
+\global\doublecolumnhsize=2.4in \global\doublecolumnvsize=15.0in
+\global\vsize=7.5in
+\global\tolerance=700
+\global\hfuzz=1pt
+
+\global\pagewidth=\hsize
+\global\pageheight=\vsize
+\global\font\ninett=cmtt9
+
+\global\let\smalllisp=\smalllispx
+\global\let\smallexample=\smalllispx
+\global\def\Esmallexample{\Esmalllisp}
+}
+
+%% For a final copy, take out the rectangles
+%% that mark overfull boxes (in case you have decided
+%% that the text looks ok even though it passes the margin).
+\def\finalout{\overfullrule=0pt}
+
+% Turn off all special characters except @
+% (and those which the user can use as if they were ordinary)
+% Define certain chars to be always in tt font.
+
+\catcode`\"=\active
+\def\activedoublequote{{\tt \char '042}}
+\let"=\activedoublequote
+\catcode`\~=\active
+\def~{{\tt \char '176}}
+\chardef\hat=`\^
+\catcode`\^=\active
+\def^{{\tt \hat}}
+\catcode`\_=\active
+\def_{{\tt \char '137}}
+\catcode`\|=\active
+\def|{{\tt \char '174}}
+\chardef \less=`\<
+\catcode`\<=\active
+\def<{{\tt \less}}
+\chardef \gtr=`\>
+\catcode`\>=\active
+\def>{{\tt \gtr}}
+
+\catcode`\@=0
+
+% \rawbackslashxx output one backslash character in current font
+{\catcode`\\=\other
+@gdef@rawbackslashxx{\}}
+
+% \rawbackslash redefines \ as input to do \rawbackslashxx.
+{\catcode`\\=\active
+@gdef@rawbackslash{@let\=@rawbackslashxx }}
+
+% \normalbackslash outputs one backslash in fixed width font.
+\def\normalbackslash{{\tt\rawbackslashxx}}
+
+% Say @foo, not \foo, in error messages.
+\escapechar=`\@
+
+@c \catcode 17=0 @c Define control-q
+\catcode`\\=\active
+
+% If a .fmt file is being used, we don't want the `\input texinfo' to show up.
+% That is what \eatinput is for; after that, the `\' should revert to printing
+% a backslash.
+%
+@gdef@eatinput input texinfo{@fixbackslash}
+@global@let\ = @eatinput
+
+% On the other hand, perhaps the file did not have a `\input texinfo'. Then
+% the first `\{ in the file would cause an error. This macro tries to fix
+% that, assuming it is called before the first `\' could plausibly occur.
+%
+@gdef@fixbackslash{@ifx\@eatinput @let\ = @normalbackslash @fi}
+
+%% These look ok in all fonts, so just make them not special. The @rm below
+%% makes sure that the current font starts out as the newly loaded cmr10
+@catcode`@$=@other @catcode`@%=@other @catcode`@&=@other @catcode`@#=@other
+
+@textfonts
+@rm
diff --git a/usr.sbin/amd/fsinfo/Makefile b/usr.sbin/amd/fsinfo/Makefile
new file mode 100644
index 0000000..c55a2a9
--- /dev/null
+++ b/usr.sbin/amd/fsinfo/Makefile
@@ -0,0 +1,32 @@
+# @(#)Makefile 8.1 (Berkeley) 6/28/93
+# $Id: Makefile,v 1.5 1997/02/22 16:03:14 peter Exp $
+
+PROG= fsinfo
+MAN8= fsinfo.8
+SRCS= fsinfo.c fsi_gram.c fsi_gram.h fsi_lex.c \
+ fsi_util.c fsi_analyze.c fsi_dict.c \
+ wr_atab.c wr_bparam.c wr_dumpset.c \
+ wr_exportfs.c wr_fstab.c
+CLEANFILES= \
+ fsi_gram.c y.tab.c fsi_gram.h y.tab.h \
+ fsi_lex.c lex.yy.c y.output
+CFLAGS+=-I.
+CFLAGS+=-I${.CURDIR}/../include
+CFLAGS+=-I${.CURDIR}/../config
+CFLAGS+=-DOS_HDR=\"os-bsd44.h\"
+
+fsi_lex.o fsinfo.o: fsi_gram.h
+.ORDER: fsi_gram.c fsi_gram.h
+fsi_gram.c fsi_gram.h: ../fsinfo/fsi_gram.y
+ @echo "# expect 2 shift/reduce conflicts"
+ ${YACC} -d ${.CURDIR}/fsi_gram.y
+ mv -f y.tab.c fsi_gram.c
+ mv -f y.tab.h fsi_gram.h
+
+fsi_lex.c: ../fsinfo/fsi_lex.l
+ ${LEX} ${.CURDIR}/fsi_lex.l
+ mv -f lex.yy.c fsi_lex.c
+
+.PATH: ${.CURDIR}/../config
+.include "../../Makefile.inc"
+.include <bsd.prog.mk>
diff --git a/usr.sbin/amd/fsinfo/conf/automounts b/usr.sbin/amd/fsinfo/conf/automounts
new file mode 100644
index 0000000..ee4b4ee
--- /dev/null
+++ b/usr.sbin/amd/fsinfo/conf/automounts
@@ -0,0 +1,48 @@
+host localhost.doc.ic.ac.uk
+
+fs localhost:/localhost {
+ fstype = export
+ mount default {
+ exportfs ""
+ volname /home/localhost
+ }
+}
+
+automount opts "opts:=rw,nosuid,grpid" /vol {
+ rwho {}
+ r+d {}
+ public {}
+ src { gnu{} athena{} gould{} utx{} sos4{} xinu43{} }
+ export {
+ exec {
+ sun3 {}
+ sun4 {}
+ }
+ roots {}
+ swaps {
+ tsun1 {} tsun2 {} tsun3 {} tsun4 {} tsun5 {} tsun6 {}
+ tsun7 {} tsun8 {} tsun9 {} tsun10 {} tsun11 {} tsun12 {}
+ tsun13 {} tsun14 {} tsun15 {} tsun16 {} tsun17 {} tsun18 {}
+ tsun19 {}
+ tcsun1 {} tcsun2 {} tcsun3 {} tcsun4 {} tcsun5 {}
+ }
+ misc {}
+ }
+}
+
+automount opts "opts:=rw,nosuid,grpid" /home {
+ achilles {}
+ toytown {}
+ gummo {}
+ dylan { dk2{} dk5 {} }
+ ganymede {}
+ gould { staff{} teach{} }
+ "localhost" -> localhost
+}
+
+automount opts "opts:=rw,nosuid,grpid" /homes {
+ opr -> /home/localhost/opr
+#include "users"
+}
+
+automount /usr/achilles = /home/achilles
diff --git a/usr.sbin/amd/fsinfo/conf/csg_sun3 b/usr.sbin/amd/fsinfo/conf/csg_sun3
new file mode 100644
index 0000000..7d75db2
--- /dev/null
+++ b/usr.sbin/amd/fsinfo/conf/csg_sun3
@@ -0,0 +1,18 @@
+// $Id$
+// standard setups for DoC tsuns.
+// note that no /var/spool/rwho is mounted as we now expect amd to do this as /vol/rwho
+
+// a sun3
+#ifndef SOS4_SYS_OPTS
+#define SOS4_SYS_OPTS grpid,hard,intr
+#endif
+#define CSG_SUN3(HOST,DOMAIN,BOOT,EXEC) \
+host HOST.DOMAIN \
+\
+mount /vol/export/roots/HOST as / from BOOT opts rw,SOS4_SYS_OPTS \
+mount /vol/export/swaps/HOST fstype swap as swap from BOOT opts swap \
+mount /vol/export/exec/sun3 as /usr from EXEC opts ro,SOS4_SYS_OPTS \
+mount /vol/export/misc/crash/HOST as /var/crash/HOST from EXEC opts rw,nosuid,SOS4_SYS_OPTS \
+mount /vol/export/misc/tmp/HOST as /tmp from EXEC opts rw,nosuid,SOS4_SYS_OPTS \
+mount /vol/export/misc/usr.tmp/HOST as /var/tmp from EXEC opts rw,nosuid,SOS4_SYS_OPTS \
+mount /var/mmdf from BOOT opts rw,nosuid,SOS4_SYS_OPTS
diff --git a/usr.sbin/amd/fsinfo/conf/csg_vax b/usr.sbin/amd/fsinfo/conf/csg_vax
new file mode 100644
index 0000000..c1e7658
--- /dev/null
+++ b/usr.sbin/amd/fsinfo/conf/csg_vax
@@ -0,0 +1,67 @@
+// $Id$
+// csg vax config - really just for {s,r}vax
+
+#define FSTYPE_UFS 4.2
+#define DEFAULT_OPTS rw
+
+#define CSG_VAX(HOST,DOMAIN) \
+host HOST.DOMAIN\
+\
+/*\
+arch vax\
+os xinu43\
+cluster csg\
+dumphost flamingo.doc.ic.ac.uk\
+*/\
+\
+// root\
+fs /dev/hp0a {\
+ fstype = FSTYPE_UFS\
+ opts = DEFAULT_OPTS\
+ freq = 1\
+ passno = 1\
+ mount / {}\
+}\
+\
+// swap\
+fs /dev/hp0b {\
+ fstype = swap\
+}\
+\
+// usr\
+fs /dev/hp0e {\
+ fstype = FSTYPE_UFS\
+ opts = DEFAULT_OPTS\
+ freq = 1\
+ passno = 2\
+ mount /usr {\
+ exportfs "\\\
+ sky.doc.ic.ac.uk\\\
+ svax.doc.ic.ac.uk\\\
+ rvax.doc.ic.ac.uk\\\
+ ivax.doc.ic.ac.uk\\\
+ "\
+ }\
+}\
+\
+// var\
+fs /dev/hp0d {\
+ fstype = FSTYPE_UFS\
+ opts = DEFAULT_OPTS\
+ freq = 1\
+ passno = 3\
+ mount /var {}\
+}\
+\
+// home directories\
+fs /dev/hp0f {\
+ fstype = FSTYPE_UFS\
+ opts = DEFAULT_OPTS\
+ freq = 1\
+ passno = 3\
+ mount /a/HOST/home/HOST {\
+ exportfs "\\\
+ teach_hosts\\\
+ "\
+ }\
+}
diff --git a/usr.sbin/amd/fsinfo/conf/diskless_sun3_sos4 b/usr.sbin/amd/fsinfo/conf/diskless_sun3_sos4
new file mode 100644
index 0000000..0cacb20
--- /dev/null
+++ b/usr.sbin/amd/fsinfo/conf/diskless_sun3_sos4
@@ -0,0 +1,9 @@
+#define DISKLESS_SUN3_SOS4(HOST,DOMAIN,BOOTSERVER) \
+host HOST.DOMAIN \
+\
+mount /export/root/HOST as / from BOOTSERVER opts rw,grpid,intr \
+mount /export/swap/HOST as swap fstype swap from BOOTSERVER opts swap \
+mount /export/exec/sun3 as /usr from BOOTSERVER opts rw,grpid,intr \
+mount /var/clients/HOST as /var from BOOTSERVER opts rw,grpid,intr,nosuid \
+mount /var/clients/HOST.tmp as /tmp from BOOTSERVER opts rw,grpid,intr,nosuid \
+mount /var/spool/mail from BOOTSERVER opts rw,grpid,intr,nosuid
diff --git a/usr.sbin/amd/fsinfo/conf/hosts/achilles.doc.ic.ac.uk b/usr.sbin/amd/fsinfo/conf/hosts/achilles.doc.ic.ac.uk
new file mode 100644
index 0000000..6a46ce8
--- /dev/null
+++ b/usr.sbin/amd/fsinfo/conf/hosts/achilles.doc.ic.ac.uk
@@ -0,0 +1,116 @@
+// mkfsinfo
+
+host achilles.doc.ic.ac.uk
+
+/*
+arch sun4
+os sos4
+cluster theory
+dumphost achilles.doc.ic.ac.uk
+*/
+
+// SWAP Partitions
+fs /dev/xd0b {
+ fstype = swap
+}
+
+fs /dev/xd1b {
+ fstype = swap
+}
+
+// ROOT
+fs /dev/xd0a {
+ fstype = 4.2
+ opts = rw,noquota,grpid
+ passno = 1;
+ freq = 1;
+ mount / { }
+}
+
+// ROOT Backup
+fs /dev/xd1a {
+ fstype = ignore
+ opts = rw,noquota,grpid
+ passno = 1;
+ freq = 1;
+ mount /backup { }
+}
+
+fs /dev/xd1d {
+ fstype = 4.2
+ opts = rw,noquota,grpid
+ passno = 1;
+ freq = 1;
+ mount /export {
+ root {
+ truth { exportfs "-root=truth,access=truth" }
+ }
+ swap {
+ truth { exportfs "-root=truth,access=truth" }
+ }
+ exec {
+ sun4 { exportfs "-access=toytown_clients:hangers_on:gummo:harpo:opus,rw=dylan:truth:florence:toytown" }
+ }
+ }
+}
+
+fs /dev/xd1f {
+ fstype = 4.2
+ opts = rw,noquota,grpid
+ passno = 1;
+ freq = 1;
+ mount /var {
+ clients {
+ truth { exportfs "-root=truth,access=truth" }
+ truth.tmp { exportfs "-root=truth,access=truth" }
+ }
+ spool {
+ mail { exportfs "-root=truth,access=truth" }
+ rwho { exportfs "ro" volname /vol/rwho sel "byte==big" }
+ }
+ }
+}
+
+fs /dev/xd0d {
+ fstype = 4.2
+ opts = rw,noquota,grpid
+ passno = 1;
+ freq = 1;
+ mount /tmp {
+ X11NeWS { exportfs "-ro" }
+ }
+}
+
+fs /dev/xd0g {
+ fstype = 4.2
+ opts = rw,noquota,grpid
+ passno = 1
+ freq = 1
+ mount default {
+ exportfs "-access=toytown_clients:hangers_on"
+ volname /home/achilles
+ }
+}
+
+fs /dev/xd1g {
+ fstype = 4.2
+ opts = rw,noquota
+ passno = 1
+ freq = 1
+ mount /usr {
+ exportfs "-access=toytown_clients:hangers_on:gummo:harpo:opus,rw=dylan:truth:florence:toytown"
+ share {
+ volname "/usr/share"
+ //exportfs "blah"
+ }
+ src {
+ local {
+ //exportfs "-access=toytown:zebedee:dougal:dylan:florence:opus,rw=dylan:florence,root=dylan:florence"
+ bits { gnu { volname "/vol/src/gnu" } }
+ athena { volname "/vol/src/athena" }
+ }
+ }
+ }
+}
+
+/*mount /export/exec/sun3 fstype nfs from gould.doc.ic.ac.uk as /usr opts "rw"*/
diff --git a/usr.sbin/amd/fsinfo/conf/hosts/bigears.doc.ic.ac.uk b/usr.sbin/amd/fsinfo/conf/hosts/bigears.doc.ic.ac.uk
new file mode 100644
index 0000000..bda2fd8
--- /dev/null
+++ b/usr.sbin/amd/fsinfo/conf/hosts/bigears.doc.ic.ac.uk
@@ -0,0 +1,3 @@
+#include "../diskless_sun3_sos4"
+
+DISKLESS_SUN3_SOS4(bigears,doc.ic.ac.uk,toytown.doc.ic.ac.uk)
diff --git a/usr.sbin/amd/fsinfo/conf/hosts/dylan.doc.ic.ac.uk b/usr.sbin/amd/fsinfo/conf/hosts/dylan.doc.ic.ac.uk
new file mode 100644
index 0000000..5284147
--- /dev/null
+++ b/usr.sbin/amd/fsinfo/conf/hosts/dylan.doc.ic.ac.uk
@@ -0,0 +1,68 @@
+// mkfsinfo
+
+host dylan.doc.ic.ac.uk
+
+// SWAP
+fs /dev/dsk/0s0 {
+ fstype = swap
+}
+
+// SWAP
+fs /dev/dsk/1s0 {
+ fstype = swap
+}
+
+// ROOT
+fs /dev/dsk/0s0 {
+ fstype = hfs
+ opts = rw,noquota,grpid
+ passno = 0;
+ freq = 1;
+ mount / { }
+}
+
+fs /dev/dsk/1s0 {
+ fstype = hfs
+ opts = defaults
+ passno = 1;
+ freq = 1;
+ mount /usr {
+ local {
+ exportfs "dougal eden dylan zebedee brian"
+ volname /nfs/hp300/local
+ }
+ }
+}
+
+fs /dev/dsk/2s0 {
+ fstype = hfs
+ opts = defaults
+ passno = 1;
+ freq = 1;
+ mount default {
+ exportfs "toytown_clients hangers_on"
+ volname /home/dylan/dk2
+ }
+}
+
+fs /dev/dsk/3s0 {
+ fstype = hfs
+ opts = defaults
+ passno = 1;
+ freq = 1;
+ mount default {
+ exportfs "toytown_clients hangers_on"
+ volname /home/dylan/dk3
+ }
+}
+
+fs /dev/dsk/5s0 {
+ fstype = hfs
+ opts = defaults
+ passno = 1;
+ freq = 1;
+ mount default {
+ exportfs "toytown_clients hangers_on"
+ volname /home/dylan/dk5
+ }
+}
diff --git a/usr.sbin/amd/fsinfo/conf/hosts/flamingo.doc.ic.ac.uk b/usr.sbin/amd/fsinfo/conf/hosts/flamingo.doc.ic.ac.uk
new file mode 100644
index 0000000..31f1be0
--- /dev/null
+++ b/usr.sbin/amd/fsinfo/conf/hosts/flamingo.doc.ic.ac.uk
@@ -0,0 +1,104 @@
+// mkfsinfo for flamingo
+// $Id$
+
+host flamingo.doc.ic.ac.uk
+
+/*
+arch sun3
+os sos4
+cluster csg
+dumphost flamingo.doc.ic.ac.uk
+*/
+
+#define FSTYPE_UFS 4.2
+#define DEFAULT_OPTS rw,noquota,nosuid,grpid
+
+// swap
+fs /dev/xy0b {
+ fstype = swap
+}
+
+// root
+fs /dev/xy0a {
+ fstype = FSTYPE_UFS
+ opts = DEFAULT_OPTS
+ passno = 1
+ freq = 1
+ dumpset = csg_sun3_vax
+ mount / {}
+}
+
+// usr
+fs /dev/xy0f {
+ fstype = FSTYPE_UFS
+ opts = DEFAULT_OPTS
+ freq = 1
+ passno = 2
+ dumpset = csg_sun3_vax
+ mount /usr {
+ volname /vol/export/exec/sun3
+ exportfs "-ro,access=teach_hosts:ssun2.doc.ic.ac.uk:pelican:gould,\
+ root=gould:pelican:ssun2.doc.ic.ac.uk"
+ }
+}
+
+// tmp
+fs /dev/xy0d {
+ fstype = FSTYPE_UFS
+ opts = DEFAULT_OPTS,nosuid
+ freq = 1
+ passno = 3
+ mount /tmp {
+ exportfs "-access=ssun1:tsunfs,root=tsunfs"
+ }
+}
+
+// var
+fs /dev/xy0e {
+ fstype = FSTYPE_UFS
+ opts = DEFAULT_OPTS
+ freq = 1
+ passno = 4
+ dumpset = csg_sun3_vax
+ mount /var {
+ tmp {
+ exportfs "-access=ssun1:sky.doc.ic.ac.uk"
+ }
+ crash {
+ exportfs "-access=ssun1"
+ }
+ misc {
+ exportfs "-access=teach_hosts"
+ }
+ spool {
+ rwho {
+ volname /vol/rwho
+ exportfs "-ro,access=teach_hosts"
+ sel "byte==big"
+ }
+ }
+ }
+}
+
+// source - sos4
+fs /dev/xy0h {
+ fstype = FSTYPE_UFS
+ opts = DEFAULT_OPTS
+ freq = 1
+ passno = 6
+ mount /usr/src {
+ volname /vol/src/sos4
+ exportfs "-access=svax:pelican:gould,root=pelican:svax"
+ }
+}
+
+// home directories
+fs /dev/xy0g {
+ fstype = FSTYPE_UFS
+ opts = DEFAULT_OPTS
+ freq = 1
+ passno = 5
+ mount /a/flamingo/home/flamingo {
+ exportfs "-access=teach_hosts:thp_hosts:ssun2:obsidian:truth,root=gould"
+ }
+}
diff --git a/usr.sbin/amd/fsinfo/conf/hosts/ganymede.doc.ic.ac.uk b/usr.sbin/amd/fsinfo/conf/hosts/ganymede.doc.ic.ac.uk
new file mode 100644
index 0000000..e847c85
--- /dev/null
+++ b/usr.sbin/amd/fsinfo/conf/hosts/ganymede.doc.ic.ac.uk
@@ -0,0 +1,33 @@
+host ganymede.doc.ic.ac.uk
+
+fs /dev/xy0a {
+ fstype = 4.2
+ opts = rw
+ freq = 1
+ passno = 1
+ mount / {
+ }
+}
+
+fs /dev/xy0g {
+ fstype = 4.2
+ opts = rw
+ freq = 1
+ passno = 2
+ mount /usr {
+ exportfs "-access=toytown:toytown_clients"
+ }
+}
+
+fs /dev/xy0h {
+ fstype = 4.2
+ opts = rw
+ freq = 1
+ passno = 3
+ mount /home/ganymede {
+ exportfs "-access=toytown_clients:samson:hangers_on"
+ }
+}
+
+mount /home/toytown opts rw,bg,nosuid
+mount /usr/local from toytown.doc.ic.ac.uk opts ro,bg
diff --git a/usr.sbin/amd/fsinfo/conf/hosts/gould.doc.ic.ac.uk b/usr.sbin/amd/fsinfo/conf/hosts/gould.doc.ic.ac.uk
new file mode 100644
index 0000000..8804c8e
--- /dev/null
+++ b/usr.sbin/amd/fsinfo/conf/hosts/gould.doc.ic.ac.uk
@@ -0,0 +1,477 @@
+// mkfsinfo for gould
+// $Id$
+
+host gould.doc.ic.ac.uk
+
+/*
+arch powernode
+os utx21
+cluster csg
+dumphost flamingo.doc.ic.ac.uk
+*/
+
+#define FSTYPE_UFS 4.3
+#define DEFAULT_OPTS rw,noquota
+
+// swap
+fs /dev/dk0b {
+ fstype = swap
+}
+
+fs /dev/dk1b {
+ fstype = swap
+}
+
+fs /dev/dk4b {
+ fstype = swap
+}
+
+// root
+fs /dev/dk0a {
+ fstype = FSTYPE_UFS
+ opts = DEFAULT_OPTS
+ passno = 1
+ freq = 1
+ dumpset = csg_nightly
+ mount / {}
+}
+
+// root backup
+fs /dev/dk4a {
+ fstype = FSTYPE_UFS
+ opts = DEFAULT_OPTS
+ freq = 1
+ passno = 4
+ mount /backup {}
+}
+
+// usr
+fs /dev/dk4d {
+ fstype = FSTYPE_UFS
+ opts = DEFAULT_OPTS
+ freq = 1
+ passno = 2
+ dumpset = csg_nightly
+ mount /usr {}
+}
+
+// tmp
+fs /dev/dk1a {
+ fstype = FSTYPE_UFS
+ opts = DEFAULT_OPTS,nosuid
+ freq = 0
+ passno = 2
+ mount /tmp {}
+}
+
+// var
+fs /dev/dk4g {
+ fstype = FSTYPE_UFS
+ opts = DEFAULT_OPTS
+ freq = 1
+ passno = 3
+ dumpset = csg_nightly
+ mount /var {}
+}
+
+// shared stuff - usually for Suns
+fs /dev/dk5f {
+ fstype = FSTYPE_UFS
+ opts = DEFAULT_OPTS
+ freq = 1
+ passno = 6
+ mount /usr/share {
+ exportfs "-rdonly=1 \
+ ivax.doc.ic.ac.uk \
+ rvax.doc.ic.ac.uk \
+ ssun1.doc.ic.ac.uk \
+ ssun2.doc.ic.ac.uk \
+ svax.doc.ic.ac.uk \
+ tsun1.doc.ic.ac.uk \
+ tsun10.doc.ic.ac.uk \
+ tsun11.doc.ic.ac.uk \
+ tsun12.doc.ic.ac.uk \
+ tsun13.doc.ic.ac.uk \
+ tsun14.doc.ic.ac.uk \
+ tsun15.doc.ic.ac.uk \
+ tsun16.doc.ic.ac.uk \
+ tsun17.doc.ic.ac.uk \
+ tsun18.doc.ic.ac.uk \
+ tsun19.doc.ic.ac.uk \
+ tsun2.doc.ic.ac.uk \
+ tsun3.doc.ic.ac.uk \
+ tsun4.doc.ic.ac.uk \
+ tsun5.doc.ic.ac.uk \
+ tsun6.doc.ic.ac.uk \
+ tsun7.doc.ic.ac.uk \
+ tsun8.doc.ic.ac.uk \
+ tsun9.doc.ic.ac.uk \
+ tsunfs.doc.ic.ac.uk \
+ flamingo.doc.ic.ac.uk \
+ pelican.doc.ic.ac.uk \
+ oriona \
+ sky.doc.ic.ac.uk \
+ whoops.doc.ic.ac.uk \
+ whoops \
+ "
+ }
+}
+
+// spool stuff, including the news
+fs /dev/dk4f {
+ fstype = FSTYPE_UFS
+ opts = DEFAULT_OPTS
+ freq = 1
+ passno = 5
+ mount /var/spool {
+ exportfs "\
+ oriona \
+ rpcsfg \
+ "
+ }
+}
+
+fs /dev/dk3h {
+ fstype = FSTYPE_UFS
+ opts = DEFAULT_OPTS
+ freq = 1
+ passno = 2
+ mount /var/spool/News {}
+}
+
+// this is the public ftp area
+fs /dev/dk3f {
+ fstype = FSTYPE_UFS
+ opts = DEFAULT_OPTS
+ freq = 1
+ passno = 4
+ mount /usr/reserve {
+ PUBLIC {
+ volname /vol/public
+ exportfs "\
+ oriona \
+ ssun1.doc.ic.ac.uk \
+ ssun2.doc.ic.ac.uk \
+ obsidian \
+ gummo \
+ tsunfs.doc.ic.ac.uk \
+ flamingo.doc.ic.ac.uk \
+ "
+ }
+ }
+}
+
+// sources - local and public
+fs /dev/dk7c {
+ fstype = FSTYPE_UFS
+ opts = DEFAULT_OPTS
+ freq = 1
+ passno = 2
+ dumpset = csg_nightly
+ mount /usr/src {
+ volname /vol/src/gould
+ exportfs "\
+ flamingo.doc.ic.ac.uk \
+ pelican.doc.ic.ac.uk \
+ oriona \
+ ssun1.doc.ic.ac.uk \
+ svax.doc.ic.ac.uk \
+ rvax.doc.ic.ac.uk \
+ obsidian \
+ tsunfs \
+ "
+ }
+}
+
+// sources - utx
+fs /dev/dk4e {
+ fstype = FSTYPE_UFS
+ opts = DEFAULT_OPTS
+ freq = 1
+ passno = 6
+ dumpset = csg_nightly
+ mount /usr/src/utx {
+ volname /vol/src/utx
+ exportfs "-rdonly=1 \
+ flamingo.doc.ic.ac.uk \
+ "
+ }
+}
+
+// home directories
+fs /dev/dk1h {
+ fstype = FSTYPE_UFS
+ opts = DEFAULT_OPTS
+ freq = 1
+ passno = 3
+ dumpset = csg_nightly
+ mount /home/gould/teach {
+ exportfs "\
+ thp1 \
+ thp2 \
+ thp3 \
+ thp4 \
+ thp5 \
+ thp6 \
+ thp7 \
+ thp8 \
+ thp9 \
+ thp10 \
+ thpfs \
+ ivax.doc.ic.ac.uk \
+ rvax.doc.ic.ac.uk \
+ ssun1.doc.ic.ac.uk \
+ ssun2.doc.ic.ac.uk \
+ svax.doc.ic.ac.uk \
+ tsun1.doc.ic.ac.uk \
+ tsun10.doc.ic.ac.uk \
+ tsun11.doc.ic.ac.uk \
+ tsun12.doc.ic.ac.uk \
+ tsun13.doc.ic.ac.uk \
+ tsun14.doc.ic.ac.uk \
+ tsun15.doc.ic.ac.uk \
+ tsun16.doc.ic.ac.uk \
+ tsun17.doc.ic.ac.uk \
+ tsun18.doc.ic.ac.uk \
+ tsun19.doc.ic.ac.uk \
+ tsun2.doc.ic.ac.uk \
+ tsun3.doc.ic.ac.uk \
+ tsun4.doc.ic.ac.uk \
+ tsun5.doc.ic.ac.uk \
+ tsun6.doc.ic.ac.uk \
+ tsun7.doc.ic.ac.uk \
+ tsun8.doc.ic.ac.uk \
+ tsun9.doc.ic.ac.uk \
+ tsunfs.doc.ic.ac.uk \
+ flamingo.doc.ic.ac.uk \
+ pelican.doc.ic.ac.uk \
+ oriona \
+ sky.doc.ic.ac.uk \
+ whoops.doc.ic.ac.uk \
+ whoops \
+ vlsi.doc.ic.ac.uk \
+ vlsi \
+ "
+ }
+}
+
+fs /dev/dk0h {
+ fstype = FSTYPE_UFS
+ opts = rw,quota
+ freq = 1
+ passno = 2
+ dumpset = csg_nightly
+ mount /home/gould/staff {
+ exportfs "\
+ achilles \
+ thp1 \
+ thp2 \
+ thp3 \
+ thp4 \
+ thp5 \
+ thp6 \
+ thp7 \
+ thp8 \
+ thp9 \
+ thp10 \
+ thpfs \
+ ivax.doc.ic.ac.uk \
+ rvax.doc.ic.ac.uk \
+ ssun1.doc.ic.ac.uk \
+ ssun2.doc.ic.ac.uk \
+ svax.doc.ic.ac.uk \
+ tsun1.doc.ic.ac.uk \
+ tsun10.doc.ic.ac.uk \
+ tsun11.doc.ic.ac.uk \
+ tsun12.doc.ic.ac.uk \
+ tsun13.doc.ic.ac.uk \
+ tsun14.doc.ic.ac.uk \
+ tsun15.doc.ic.ac.uk \
+ tsun16.doc.ic.ac.uk \
+ tsun17.doc.ic.ac.uk \
+ tsun18.doc.ic.ac.uk \
+ tsun19.doc.ic.ac.uk \
+ tsun2.doc.ic.ac.uk \
+ tsun3.doc.ic.ac.uk \
+ tsun4.doc.ic.ac.uk \
+ tsun5.doc.ic.ac.uk \
+ tsun6.doc.ic.ac.uk \
+ tsun7.doc.ic.ac.uk \
+ tsun8.doc.ic.ac.uk \
+ tsun9.doc.ic.ac.uk \
+ tsunfs.doc.ic.ac.uk \
+ flamingo.doc.ic.ac.uk \
+ pelican.doc.ic.ac.uk \
+ oriona \
+ sky.doc.ic.ac.uk \
+ whoops.doc.ic.ac.uk \
+ whoops \
+ vlsi.doc.ic.ac.uk \
+ vlsi \
+ vlsi02 \
+ "
+ }
+}
+
+// booting diskless suns
+fs /dev/dk5e {
+ fstype = FSTYPE_UFS
+ opts = DEFAULT_OPTS
+ freq = 1
+ passno = 3
+ dumpset = csg_nightly
+ mount /export {
+#ifndef ok
+ volname /vol/export
+#endif
+ exportfs "\
+ -rootid=0 \
+ whoops \
+ whoops.doc.ic.ac.uk \
+ "
+ misc {
+ volname /vol/export/misc
+ }
+ }
+}
+
+fs /dev/dk5a {
+ fstype = FSTYPE_UFS
+ opts = DEFAULT_OPTS
+ freq = 1
+ passno = 4
+ dumpset = csg_nightly
+ mount /export/roots {
+ volname /vol/export/roots
+ exportfs "\
+ -rootid=0 \
+ whoops \
+ whoops.doc.ic.ac.uk \
+ "
+ }
+}
+
+fs /dev/dk5d {
+ fstype = FSTYPE_UFS
+ opts = DEFAULT_OPTS
+ freq = 1
+ passno = 5
+ dumpset = csg_nightly
+ mount /export/exec/sun3 {
+ volname /vol/export/exec/sun3
+ exportfs "\
+ ssun1.doc.ic.ac.uk \
+ ssun2.doc.ic.ac.uk \
+ tsun1.doc.ic.ac.uk \
+ tsun10.doc.ic.ac.uk \
+ tsun11.doc.ic.ac.uk \
+ tsun12.doc.ic.ac.uk \
+ tsun13.doc.ic.ac.uk \
+ tsun14.doc.ic.ac.uk \
+ tsun15.doc.ic.ac.uk \
+ tsun16.doc.ic.ac.uk \
+ tsun17.doc.ic.ac.uk \
+ tsun18.doc.ic.ac.uk \
+ tsun19.doc.ic.ac.uk \
+ tsun2.doc.ic.ac.uk \
+ tsun3.doc.ic.ac.uk \
+ tsun4.doc.ic.ac.uk \
+ tsun5.doc.ic.ac.uk \
+ tsun6.doc.ic.ac.uk \
+ tsun7.doc.ic.ac.uk \
+ tsun8.doc.ic.ac.uk \
+ tsun9.doc.ic.ac.uk \
+ tsunfs.doc.ic.ac.uk \
+ flamingo.doc.ic.ac.uk \
+ pelican.doc.ic.ac.uk \
+ whoops.doc.ic.ac.uk \
+ whoops \
+ "
+ }
+}
+
+// various r+d things - used for athena, etc
+fs /dev/dk5g {
+ fstype = FSTYPE_UFS
+ opts = DEFAULT_OPTS
+ freq = 1
+ passno = 2
+ mount /usr/r+d {
+ volname /vol/r+d
+ exportfs "\
+ ivax.doc.ic.ac.uk \
+ rvax.doc.ic.ac.uk \
+ ssun1.doc.ic.ac.uk \
+ ssun2.doc.ic.ac.uk \
+ svax.doc.ic.ac.uk \
+ tsun1.doc.ic.ac.uk \
+ tsun10.doc.ic.ac.uk \
+ tsun11.doc.ic.ac.uk \
+ tsun12.doc.ic.ac.uk \
+ tsun13.doc.ic.ac.uk \
+ tsun14.doc.ic.ac.uk \
+ tsun15.doc.ic.ac.uk \
+ tsun16.doc.ic.ac.uk \
+ tsun17.doc.ic.ac.uk \
+ tsun18.doc.ic.ac.uk \
+ tsun19.doc.ic.ac.uk \
+ tsun2.doc.ic.ac.uk \
+ tsun3.doc.ic.ac.uk \
+ tsun4.doc.ic.ac.uk \
+ tsun5.doc.ic.ac.uk \
+ tsun6.doc.ic.ac.uk \
+ tsun7.doc.ic.ac.uk \
+ tsun8.doc.ic.ac.uk \
+ tsun9.doc.ic.ac.uk \
+ tsunfs.doc.ic.ac.uk \
+ flamingo.doc.ic.ac.uk \
+ pelican.doc.ic.ac.uk \
+ oriona \
+ sky.doc.ic.ac.uk \
+ whoops.doc.ic.ac.uk \
+ whoops \
+ "
+ }
+}
+
+fs /dev/dk3d {
+ fstype = FSTYPE_UFS
+ opts = DEFAULT_OPTS
+ freq = 1
+ passno = 3
+ mount /usr/r+d/r1 {
+ exportfs "\
+ ssun1.doc.ic.ac.uk \
+ ssun2.doc.ic.ac.uk \
+ "
+ }
+}
+
+fs /dev/dk4h {
+ fstype = FSTYPE_UFS
+ opts = DEFAULT_OPTS
+ freq = 1
+ passno = 7
+ mount /usr/r+d/r2 {
+ exportfs "\
+ achilles \
+ gummo \
+ harpo \
+ oriona \
+ ssun1.doc.ic.ac.uk \
+ ssun2.doc.ic.ac.uk \
+ thpfs \
+ toytown \
+ obsidian \
+ "
+ }
+}
+
+// this bit of disc needs a name !
+fs /dev/dk5h {
+ fstype = FSTYPE_UFS
+ opts = DEFAULT_OPTS
+ freq = 1
+ passno = 7
+ mount /mnt2 {}
+}
diff --git a/usr.sbin/amd/fsinfo/conf/hosts/gummo.doc.ic.ac.uk b/usr.sbin/amd/fsinfo/conf/hosts/gummo.doc.ic.ac.uk
new file mode 100644
index 0000000..aae6a5d
--- /dev/null
+++ b/usr.sbin/amd/fsinfo/conf/hosts/gummo.doc.ic.ac.uk
@@ -0,0 +1,12 @@
+// mkfsinfo
+
+host gummo.doc.ic.ac.uk
+
+// ROOT
+fs /dev/xxx1 {
+ fstype = export
+ mount default {
+ exportfs ""
+ volname /home/gummo
+ }
+}
diff --git a/usr.sbin/amd/fsinfo/conf/hosts/ivax.doc.ic.ac.uk b/usr.sbin/amd/fsinfo/conf/hosts/ivax.doc.ic.ac.uk
new file mode 100644
index 0000000..c8fcb5d
--- /dev/null
+++ b/usr.sbin/amd/fsinfo/conf/hosts/ivax.doc.ic.ac.uk
@@ -0,0 +1,84 @@
+// $Id$
+// ivax
+
+#define FSTYPE_UFS 4.2
+#define DEFAULT_OPTS rw
+
+host {
+ config "NFS_SERVER=true"
+ config "NFS_CLIENT=true"
+ config "YP_SERVER=false"
+ config "YP_CLIENT=false"
+
+ arch = vax
+ os = xinu43
+ cluster = teach.doc.ic.ac.uk
+ netif il0 { hwaddr = "08:08:08:08:08:08" netmask = 0xfffffe00 inaddr = 129.31.80.36 }
+
+} ivax.doc.ic.ac.uk
+
+// root
+fs /dev/hp0a {
+ fstype = FSTYPE_UFS
+ opts = DEFAULT_OPTS
+ freq = 1
+ passno = 1
+ mount / {}
+}
+
+// swap
+fs /dev/hp0b {
+ fstype = swap
+}
+
+// usr
+fs /dev/hp0e {
+ fstype = FSTYPE_UFS
+ opts = DEFAULT_OPTS
+ freq = 1
+ passno = 2
+ mount /usr {
+ exportfs "\
+ sky.doc.ic.ac.uk\
+ svax.doc.ic.ac.uk\
+ rvax.doc.ic.ac.uk\
+ ivax.doc.ic.ac.uk\
+ "
+ }
+}
+
+// var
+fs /dev/hp0d {
+ fstype = FSTYPE_UFS
+ opts = DEFAULT_OPTS
+ freq = 1
+ passno = 3
+ mount /var {}
+}
+
+// home directories
+fs /dev/hp0f {
+ fstype = FSTYPE_UFS
+ opts = DEFAULT_OPTS
+ freq = 1
+ passno = 3
+ mount default {
+ volname /home/ivax
+ exportfs "\
+ sky.doc.ic.ac.uk\
+ "
+ }
+}
+
+// sources which are used by the gould for the infoserver
+fs /dev/hp2c {
+ fstype = FSTYPE_UFS
+ opts = DEFAULT_OPTS
+ freq = 1
+ passno = 3
+ mount /public {
+ exportfs "\
+ gould\
+ "
+ }
+}
diff --git a/usr.sbin/amd/fsinfo/conf/hosts/obsidian.doc.ic.ac.uk b/usr.sbin/amd/fsinfo/conf/hosts/obsidian.doc.ic.ac.uk
new file mode 100644
index 0000000..dd9f0f7
--- /dev/null
+++ b/usr.sbin/amd/fsinfo/conf/hosts/obsidian.doc.ic.ac.uk
@@ -0,0 +1,28 @@
+// $Id$
+// conf for obsidian
+
+#define FSTYPE_UFS hfs
+#define DEFAULT_OPTS rw
+
+host obsidian.doc.ic.ac.uk
+
+fs /dev/dsk/0s0 {
+ fstype = FSTYPE_UFS
+ opts = DEFAULT_OPTS
+ freq = 1
+ passno = 1
+ mount / {}
+}
+
+fs /dev/dsk/1s0 {
+ fstype = FSTYPE_UFS
+ opts = DEFAULT_OPTS
+ freq = 1
+ passno = 1
+ mount default {
+ volname /home/obsidian
+ exportfs "\
+ gould\
+ "
+ }
+}
diff --git a/usr.sbin/amd/fsinfo/conf/hosts/pelican.doc.ic.ac.uk b/usr.sbin/amd/fsinfo/conf/hosts/pelican.doc.ic.ac.uk
new file mode 100644
index 0000000..1ffd8d7
--- /dev/null
+++ b/usr.sbin/amd/fsinfo/conf/hosts/pelican.doc.ic.ac.uk
@@ -0,0 +1,208 @@
+// mkfsinfo for pelican
+// $Id$
+
+host pelican.doc.ic.ac.uk
+
+/*
+arch sun3
+os sos4
+cluster csg
+dumphost flamingo.doc.ic.ac.uk
+*/
+
+#define FSTYPE_UFS 4.2
+#define DEFAULT_OPTS rw,noquota,nosuid,grpid
+
+// swap
+fs /dev/xd0b {
+ fstype = swap
+}
+
+// root
+fs /dev/xd0a {
+ fstype = FSTYPE_UFS
+ opts = DEFAULT_OPTS
+ passno = 1
+ freq = 1
+ dumpset = csg_sun3_vax
+ mount / {
+ exportfs "-ro,access=tsunfs,root=tsunfs"
+ }
+}
+
+// usr
+fs /dev/xd0f {
+ fstype = FSTYPE_UFS
+ opts = DEFAULT_OPTS
+ freq = 1
+ passno = 2
+ dumpset = csg_sun3_vax
+ mount /usr {
+ volname /vol/export/exec/sun3
+ exportfs "-ro,access=teach_hosts,root=gould:flamingo:tsunfs"
+ }
+}
+
+// tmp
+fs /dev/xd0d {
+ fstype = FSTYPE_UFS
+ opts = DEFAULT_OPTS,nosuid
+ freq = 1
+ passno = 3
+ mount /tmp {
+ exportfs "\
+ -access=\
+ flamingo:\
+ tsun16.doc.ic.ac.uk:\
+ tsun17.doc.ic.ac.uk:\
+ tsun18.doc.ic.ac.uk:\
+ tsun19.doc.ic.ac.uk"
+ }
+}
+
+// var
+fs /dev/xd0e {
+ fstype = FSTYPE_UFS
+ opts = DEFAULT_OPTS
+ freq = 1
+ passno = 4
+ dumpset = csg_sun3_vax
+ mount /var {
+ misc {
+ // this is due to differences between tsunfs and pelican
+ volname /vol/export/misc
+ exportfs "\
+ -access=teach_hosts,\
+ root=\
+ tcsun1.doc.ic.ac.uk:\
+ tcsun2.doc.ic.ac.uk:\
+ tcsun3.doc.ic.ac.uk:\
+ tcsun4.doc.ic.ac.uk:\
+ tcsun5.doc.ic.ac.uk:\
+ tsun16.doc.ic.ac.uk:\
+ tsun17.doc.ic.ac.uk:\
+ tsun18.doc.ic.ac.uk:\
+ tsun19.doc.ic.ac.uk"
+ }
+ mmdf {
+ exportfs "\
+ -access=teach_hosts,\
+ root=\
+ tcsun1.doc.ic.ac.uk:\
+ tcsun2.doc.ic.ac.uk:\
+ tcsun3.doc.ic.ac.uk:\
+ tcsun4.doc.ic.ac.uk:\
+ tcsun5.doc.ic.ac.uk:\
+ tsun16.doc.ic.ac.uk:\
+ tsun17.doc.ic.ac.uk:\
+ tsun18.doc.ic.ac.uk:\
+ tsun19.doc.ic.ac.uk"
+ }
+ spool {
+ rwho {
+ volname /vol/rwho
+ exportfs "-ro,access=teach_hosts"
+ sel "byte==big"
+ }
+ }
+ }
+}
+
+// export for diskless clients
+fs /dev/xd0h {
+ fstype = FSTYPE_UFS
+ opts = DEFAULT_OPTS
+ freq = 1
+ passno = 6
+ mount /export {
+ roots {
+ tcsun1 {
+ volname /vol/export/roots/tcsun1
+ exportfs "-root=tcsun1.doc.ic.ac.uk,access=tcsun1.doc.ic.ac.uk"
+ }
+ tcsun2 {
+ volname /vol/export/roots/tcsun2
+ exportfs "-root=tcsun2.doc.ic.ac.uk,access=tcsun2.doc.ic.ac.uk"
+ }
+ tcsun3 {
+ volname /vol/export/roots/tcsun3
+ exportfs "-root=tcsun3.doc.ic.ac.uk,access=tcsun3.doc.ic.ac.uk"
+ }
+ tcsun4 {
+ volname /vol/export/roots/tcsun4
+ exportfs "-root=tcsun4.doc.ic.ac.uk,access=tcsun4.doc.ic.ac.uk"
+ }
+ tcsun5 {
+ volname /vol/export/roots/tcsun5
+ exportfs "-root=tcsun5.doc.ic.ac.uk,access=tcsun5.doc.ic.ac.uk"
+ }
+
+ tsun16 {
+ volname /vol/export/roots/tsun16
+ exportfs "-root=tsun16.doc.ic.ac.uk,access=tsun16.doc.ic.ac.uk"
+ }
+ tsun17 {
+ volname /vol/export/roots/tsun17
+ exportfs "-root=tsun17.doc.ic.ac.uk,access=tsun17.doc.ic.ac.uk"
+ }
+ tsun18 {
+ volname /vol/export/roots/tsun18
+ exportfs "-root=tsun18.doc.ic.ac.uk,access=tsun18.doc.ic.ac.uk"
+ }
+ tsun19 {
+ volname /vol/export/roots/tsun19
+ exportfs "-root=tsun19.doc.ic.ac.uk,access=tsun19.doc.ic.ac.uk"
+ }
+ }
+ swaps {
+ tcsun1 {
+ volname /vol/export/swaps/tcsun1
+ exportfs "-root=tcsun1.doc.ic.ac.uk,access=tcsun1.doc.ic.ac.uk"
+ }
+ tcsun2 {
+ volname /vol/export/swaps/tcsun2
+ exportfs "-root=tcsun2.doc.ic.ac.uk,access=tcsun2.doc.ic.ac.uk"
+ }
+ tcsun3 {
+ volname /vol/export/swaps/tcsun3
+ exportfs "-root=tcsun3.doc.ic.ac.uk,access=tcsun3.doc.ic.ac.uk"
+ }
+ tcsun4 {
+ volname /vol/export/swaps/tcsun4
+ exportfs "-root=tcsun4.doc.ic.ac.uk,access=tcsun4.doc.ic.ac.uk"
+ }
+ tcsun5 {
+ volname /vol/export/swaps/tcsun5
+ exportfs "-root=tcsun5.doc.ic.ac.uk,access=tcsun5.doc.ic.ac.uk"
+ }
+ tsun16 {
+ volname /vol/export/swaps/tsun16
+ exportfs "-root=tsun16.doc.ic.ac.uk,access=tsun16.doc.ic.ac.uk"
+ }
+ tsun17 {
+ volname /vol/export/swaps/tsun17
+ exportfs "-root=tsun17.doc.ic.ac.uk,access=tsun17.doc.ic.ac.uk"
+ }
+ tsun18 {
+ volname /vol/export/swaps/tsun18
+ exportfs "-root=tsun18.doc.ic.ac.uk,access=tsun18.doc.ic.ac.uk"
+ }
+ tsun19 {
+ volname /vol/export/swaps/tsun19
+ exportfs "-root=tsun19.doc.ic.ac.uk,access=tsun19.doc.ic.ac.uk"
+ }
+ }
+ }
+}
+
+// home directories
+fs /dev/xd0g {
+ fstype = FSTYPE_UFS
+ opts = DEFAULT_OPTS
+ freq = 1
+ passno = 5
+ mount default {
+ volname /home/pelican
+ exportfs "-access=teach_hosts:thp_hosts:ssun2:obsidian:truth,root=gould"
+ }
+}
diff --git a/usr.sbin/amd/fsinfo/conf/hosts/rvax.doc.ic.ac.uk b/usr.sbin/amd/fsinfo/conf/hosts/rvax.doc.ic.ac.uk
new file mode 100644
index 0000000..896b9a0
--- /dev/null
+++ b/usr.sbin/amd/fsinfo/conf/hosts/rvax.doc.ic.ac.uk
@@ -0,0 +1,66 @@
+// $Id$
+// rvax
+
+#define FSTYPE_UFS 4.2
+#define DEFAULT_OPTS rw
+
+host rvax.doc.ic.ac.uk
+
+/*
+arch vax
+os xinu43
+cluster csg
+dumphost flamingo.doc.ic.ac.uk
+*/
+
+// root
+fs /dev/hp0a {
+ fstype = FSTYPE_UFS
+ opts = DEFAULT_OPTS
+ freq = 1
+ passno = 1
+ mount / {}
+}
+
+// swap
+fs /dev/hp0b {
+ fstype = swap
+}
+
+// usr
+fs /dev/hp0e {
+ fstype = FSTYPE_UFS
+ opts = DEFAULT_OPTS
+ freq = 1
+ passno = 2
+ mount /usr {
+ exportfs "\
+ sky.doc.ic.ac.uk\
+ svax.doc.ic.ac.uk\
+ rvax.doc.ic.ac.uk\
+ ivax.doc.ic.ac.uk\
+ "
+ }
+}
+
+// var
+fs /dev/hp0d {
+ fstype = FSTYPE_UFS
+ opts = DEFAULT_OPTS
+ freq = 1
+ passno = 3
+ mount /var {}
+}
+
+// home directories
+fs /dev/hp0f {
+ fstype = FSTYPE_UFS
+ opts = DEFAULT_OPTS
+ freq = 1
+ passno = 3
+ mount /a/rvax/home/rvax {
+ exportfs "\
+ teach_hosts\
+ "
+ }
+}
diff --git a/usr.sbin/amd/fsinfo/conf/hosts/sky.doc.ic.ac.uk b/usr.sbin/amd/fsinfo/conf/hosts/sky.doc.ic.ac.uk
new file mode 100644
index 0000000..b82605f
--- /dev/null
+++ b/usr.sbin/amd/fsinfo/conf/hosts/sky.doc.ic.ac.uk
@@ -0,0 +1,79 @@
+// $Id$
+// sky
+
+#define FSTYPE_UFS 4.2
+#define DEFAULT_OPTS rw
+
+host sky.doc.ic.ac.uk
+
+/*
+arch vax
+os xinu43
+cluster csg
+dumphost flamingo.doc.ic.ac.uk
+*/
+
+// root
+fs /dev/hp0a {
+ fstype = FSTYPE_UFS
+ opts = DEFAULT_OPTS
+ freq = 1
+ passno = 1
+ mount / {}
+}
+
+// swap
+fs /dev/hp0b {
+ fstype = swap
+}
+
+// usr
+fs /dev/hp0e {
+ fstype = FSTYPE_UFS
+ opts = DEFAULT_OPTS
+ freq = 1
+ passno = 2
+ mount /usr {
+ exportfs "\
+ sky.doc.ic.ac.uk\
+ svax.doc.ic.ac.uk\
+ rvax.doc.ic.ac.uk\
+ ivax.doc.ic.ac.uk\
+ "
+ }
+}
+
+// var
+fs /dev/hp0d {
+ fstype = FSTYPE_UFS
+ opts = DEFAULT_OPTS
+ freq = 1
+ passno = 3
+ mount /var {}
+}
+
+// home directories
+fs /dev/hp0f {
+ fstype = FSTYPE_UFS
+ opts = DEFAULT_OPTS
+ freq = 1
+ passno = 3
+ mount /a/sky/home/sky {}
+}
+
+// 4.3 sources
+fs /dev/hp1g {
+ fstype = FSTYPE_UFS
+ opts = DEFAULT_OPTS
+ freq = 1
+ passno = 2
+ mount /usr/src {
+ volname /vol/src/xinu43
+ exportfs "\
+ svax.doc.ic.ac.uk\
+ rvax.doc.ic.ac.uk\
+ sky.doc.ic.ac.uk\
+ ivax.doc.ic.ac.uk\
+ "
+ }
+}
diff --git a/usr.sbin/amd/fsinfo/conf/hosts/svax.doc.ic.ac.uk b/usr.sbin/amd/fsinfo/conf/hosts/svax.doc.ic.ac.uk
new file mode 100644
index 0000000..d9703eb
--- /dev/null
+++ b/usr.sbin/amd/fsinfo/conf/hosts/svax.doc.ic.ac.uk
@@ -0,0 +1,66 @@
+// $Id$
+// svax
+
+#define FSTYPE_UFS 4.2
+#define DEFAULT_OPTS rw
+
+host svax.doc.ic.ac.uk
+
+/*
+arch vax
+os xinu43
+cluster csg
+dumphost flamingo.doc.ic.ac.uk
+*/
+
+// root
+fs /dev/hp0a {
+ fstype = FSTYPE_UFS
+ opts = DEFAULT_OPTS
+ freq = 1
+ passno = 1
+ mount / {}
+}
+
+// swap
+fs /dev/hp0b {
+ fstype = swap
+}
+
+// usr
+fs /dev/hp0e {
+ fstype = FSTYPE_UFS
+ opts = DEFAULT_OPTS
+ freq = 1
+ passno = 2
+ mount /usr {
+ exportfs "\
+ sky.doc.ic.ac.uk\
+ svax.doc.ic.ac.uk\
+ rvax.doc.ic.ac.uk\
+ ivax.doc.ic.ac.uk\
+ "
+ }
+}
+
+// var
+fs /dev/hp0d {
+ fstype = FSTYPE_UFS
+ opts = DEFAULT_OPTS
+ freq = 1
+ passno = 3
+ mount /var {}
+}
+
+// home directories
+fs /dev/hp0f {
+ fstype = FSTYPE_UFS
+ opts = DEFAULT_OPTS
+ freq = 1
+ passno = 3
+ mount /a/svax/home/svax {
+ exportfs "\
+ teach_hosts\
+ "
+ }
+}
diff --git a/usr.sbin/amd/fsinfo/conf/hosts/tcsun1.doc.ic.ac.uk b/usr.sbin/amd/fsinfo/conf/hosts/tcsun1.doc.ic.ac.uk
new file mode 100644
index 0000000..1b10d84
--- /dev/null
+++ b/usr.sbin/amd/fsinfo/conf/hosts/tcsun1.doc.ic.ac.uk
@@ -0,0 +1,3 @@
+#include "../csg_sun3"
+
+CSG_SUN3(tcsun1,doc.ic.ac.uk,pelican.doc.ic.ac.uk,pelican.doc.ic.ac.uk)
diff --git a/usr.sbin/amd/fsinfo/conf/hosts/tcsun2.doc.ic.ac.uk b/usr.sbin/amd/fsinfo/conf/hosts/tcsun2.doc.ic.ac.uk
new file mode 100644
index 0000000..933d2f4
--- /dev/null
+++ b/usr.sbin/amd/fsinfo/conf/hosts/tcsun2.doc.ic.ac.uk
@@ -0,0 +1,3 @@
+#include "../csg_sun3"
+
+CSG_SUN3(tcsun2,doc.ic.ac.uk,pelican.doc.ic.ac.uk,pelican.doc.ic.ac.uk)
diff --git a/usr.sbin/amd/fsinfo/conf/hosts/tcsun3.doc.ic.ac.uk b/usr.sbin/amd/fsinfo/conf/hosts/tcsun3.doc.ic.ac.uk
new file mode 100644
index 0000000..ccef4d4
--- /dev/null
+++ b/usr.sbin/amd/fsinfo/conf/hosts/tcsun3.doc.ic.ac.uk
@@ -0,0 +1,3 @@
+#include "../csg_sun3"
+
+CSG_SUN3(tcsun3,doc.ic.ac.uk,pelican.doc.ic.ac.uk,pelican.doc.ic.ac.uk)
diff --git a/usr.sbin/amd/fsinfo/conf/hosts/tcsun4.doc.ic.ac.uk b/usr.sbin/amd/fsinfo/conf/hosts/tcsun4.doc.ic.ac.uk
new file mode 100644
index 0000000..9de9a9a
--- /dev/null
+++ b/usr.sbin/amd/fsinfo/conf/hosts/tcsun4.doc.ic.ac.uk
@@ -0,0 +1,3 @@
+#include "../csg_sun3"
+
+CSG_SUN3(tcsun4,doc.ic.ac.uk,pelican.doc.ic.ac.uk,pelican.doc.ic.ac.uk)
diff --git a/usr.sbin/amd/fsinfo/conf/hosts/tcsun5.doc.ic.ac.uk b/usr.sbin/amd/fsinfo/conf/hosts/tcsun5.doc.ic.ac.uk
new file mode 100644
index 0000000..7f268a0
--- /dev/null
+++ b/usr.sbin/amd/fsinfo/conf/hosts/tcsun5.doc.ic.ac.uk
@@ -0,0 +1,3 @@
+#include "../csg_sun3"
+
+CSG_SUN3(tcsun5,doc.ic.ac.uk,pelican.doc.ic.ac.uk,pelican.doc.ic.ac.uk)
diff --git a/usr.sbin/amd/fsinfo/conf/hosts/toytown.doc.ic.ac.uk b/usr.sbin/amd/fsinfo/conf/hosts/toytown.doc.ic.ac.uk
new file mode 100644
index 0000000..7b27f16
--- /dev/null
+++ b/usr.sbin/amd/fsinfo/conf/hosts/toytown.doc.ic.ac.uk
@@ -0,0 +1,116 @@
+host toytown.doc.ic.ac.uk
+
+fs /dev/xy0a {
+ fstype = 4.2
+ opts = rw,noquota,grpid
+ freq = 25
+ passno = 1
+ mount / {
+ }
+}
+
+fs /dev/xy0g {
+ fstype = 4.2
+ opts = rw,noquota,grpid
+ freq = 25
+ passno = 2
+ mount /usr {
+ exportfs "-access=toytown_clients:hangers_on:pythagoras,ro"
+ sun3 { }
+ local { }
+ }
+}
+
+fs /dev/xy1g {
+ fstype = 4.2
+ opts = rw,grpid,nosuid
+ freq = 6
+ passno = 2
+ mount /home/toytown {
+ exportfs "-access=toytown_clients:hangers_on,root=achilles"
+ }
+}
+
+fs /dev/xy0f {
+ fstype = 4.2
+ opts = rw,noquota,grpid,nosuid
+ freq = 25
+ passno = 4
+ mount /var {
+ spool {
+ exportfs "-access=toytown_clients:hangers_on"
+ mail { }
+ rwho { volname /vol/rwho sel "byte==big" }
+/*
+ mail { exportfs "-access=toytown_clients:hangers_on" }
+ rwho { exportfs "ro" volname /vol/rwho sel "byte==big" }
+*/
+ }
+ clients {
+ archimedes { exportfs "-access=archimedes,root=archimedes" }
+ archimedes.tmp { exportfs "-access=archimedes,root=archimedes" }
+ aver { exportfs "-access=aver,root=aver" }
+ aver.tmp { exportfs "-access=aver,root=aver" }
+ bigears { exportfs "-access=bigears,root=bigears" }
+ bigears.tmp { exportfs "-access=bigears,root=bigears" }
+ diadem { exportfs "-access=diadem,root=diadem" }
+ diadem.tmp { exportfs "-access=diadem,root=diadem" }
+ montague { exportfs "-access=montague,root=montague" }
+ montague.tmp { exportfs "-access=montague,root=montague" }
+ noddy { exportfs "-access=noddy,root=noddy" }
+ noddy.tmp { exportfs "-access=noddy,root=noddy" }
+ pcplod { exportfs "-access=pcplod,root=pcplod" }
+ pcplod.tmp { exportfs "-access=pcplod,root=pcplod" }
+ samson { exportfs "-access=samson,root=samson" }
+ samson.tmp { exportfs "-access=samson,root=samson" }
+ turing { exportfs "-access=turing,root=turing" }
+ turing.tmp { exportfs "-access=turing,root=turing" }
+ }
+ }
+}
+
+fs /dev/xy0d {
+ fstype = 4.2
+ opts = rw,noquota,grpid,nosuid
+ freq = 25
+ passno = 3
+ mount /export {
+ exec {
+ sun3 { exportfs "-access=toytown_clients:hangers_on:pythagoras" }
+ }
+ root {
+ archimedes { exportfs "-access=archimedes,root=archimedes" }
+ aver { exportfs "-access=aver,root=aver" }
+ bigears { exportfs "-access=bigears,root=bigears" }
+ diadem { exportfs "-access=diadem,root=diadem" }
+ montague { exportfs "-access=montague,root=montague" }
+ noddy { exportfs "-access=noddy,root=noddy" }
+ pcplod { exportfs "-access=pcplod,root=pcplod" }
+ samson { exportfs "-access=samson,root=samson" }
+ turing { exportfs "-access=turing,root=turing" }
+ }
+ swap {
+ archimedes { exportfs "-access=archimedes,root=archimedes" }
+ aver { exportfs "-access=aver,root=aver" }
+ bigears { exportfs "-access=bigears,root=bigears" }
+ diadem { exportfs "-access=diadem,root=diadem" }
+ montague { exportfs "-access=montague,root=montague" }
+ noddy { exportfs "-access=noddy,root=noddy" }
+ pcplod { exportfs "-access=pcplod,root=pcplod" }
+ samson { exportfs "-access=samson,root=samson" }
+ turing { exportfs "-access=turing,root=turing" }
+ }
+ }
+}
+
+fs /dev/xy0b {
+ fstype = swap
+}
+
+fs /dev/xy1b {
+ fstype = swap
+}
+
+mount /home/ganymede opts rw,grpid,nosuid,bg,intr
+mount /home/achilles opts rw,grpid,nosuid,bg,intr
+mount /usr/src from achilles.doc.ic.ac.uk opts rw,grpid,nosuid,bg,intr
diff --git a/usr.sbin/amd/fsinfo/conf/hosts/truth.doc.ic.ac.uk b/usr.sbin/amd/fsinfo/conf/hosts/truth.doc.ic.ac.uk
new file mode 100644
index 0000000..0139279
--- /dev/null
+++ b/usr.sbin/amd/fsinfo/conf/hosts/truth.doc.ic.ac.uk
@@ -0,0 +1,9 @@
+host truth.doc.ic.ac.uk
+
+mount /export/exec/sun4 from achilles.doc.ic.ac.uk as /usr opts "rw,grpid,intr"
+mount /export/root/truth from achilles.doc.ic.ac.uk as / opts "rw,grpid,intr"
+mount /export/swap/truth from achilles.doc.ic.ac.uk fstype swap as swap opts swap
+mount /var/clients/truth.tmp from achilles.doc.ic.ac.uk as /tmp opts "rw,nosuid,grpid,intr"
+mount /var/clients/truth from achilles.doc.ic.ac.uk as /var opts "rw,nosuid,grpid,intr"
+mount /var/spool/mail from achilles.doc.ic.ac.uk as /var/spool/mail opts "rw,nosuid,grpid,intr"
+mount /usr/src from achilles.doc.ic.ac.uk as /usr/src opts "rw,nosuid,grpid,intr,bg"
diff --git a/usr.sbin/amd/fsinfo/conf/hosts/tsun1.doc.ic.ac.uk b/usr.sbin/amd/fsinfo/conf/hosts/tsun1.doc.ic.ac.uk
new file mode 100644
index 0000000..ff5016c
--- /dev/null
+++ b/usr.sbin/amd/fsinfo/conf/hosts/tsun1.doc.ic.ac.uk
@@ -0,0 +1,3 @@
+#include "../csg_sun3"
+
+CSG_SUN3(tsun1,doc.ic.ac.uk,tsunfs.doc.ic.ac.uk,gould.doc.ic.ac.uk)
diff --git a/usr.sbin/amd/fsinfo/conf/hosts/tsun10.doc.ic.ac.uk b/usr.sbin/amd/fsinfo/conf/hosts/tsun10.doc.ic.ac.uk
new file mode 100644
index 0000000..2496cdb
--- /dev/null
+++ b/usr.sbin/amd/fsinfo/conf/hosts/tsun10.doc.ic.ac.uk
@@ -0,0 +1,3 @@
+#include "../csg_sun3"
+
+CSG_SUN3(tsun10,doc.ic.ac.uk,tsunfs.doc.ic.ac.uk,gould.doc.ic.ac.uk)
diff --git a/usr.sbin/amd/fsinfo/conf/hosts/tsun11.doc.ic.ac.uk b/usr.sbin/amd/fsinfo/conf/hosts/tsun11.doc.ic.ac.uk
new file mode 100644
index 0000000..d6e7b9d
--- /dev/null
+++ b/usr.sbin/amd/fsinfo/conf/hosts/tsun11.doc.ic.ac.uk
@@ -0,0 +1,3 @@
+#include "../csg_sun3"
+
+CSG_SUN3(tsun11,doc.ic.ac.uk,tsunfs.doc.ic.ac.uk,gould.doc.ic.ac.uk)
diff --git a/usr.sbin/amd/fsinfo/conf/hosts/tsun12.doc.ic.ac.uk b/usr.sbin/amd/fsinfo/conf/hosts/tsun12.doc.ic.ac.uk
new file mode 100644
index 0000000..a8439bb
--- /dev/null
+++ b/usr.sbin/amd/fsinfo/conf/hosts/tsun12.doc.ic.ac.uk
@@ -0,0 +1,3 @@
+#include "../csg_sun3"
+
+CSG_SUN3(tsun12,doc.ic.ac.uk,tsunfs.doc.ic.ac.uk,gould.doc.ic.ac.uk)
diff --git a/usr.sbin/amd/fsinfo/conf/hosts/tsun13.doc.ic.ac.uk b/usr.sbin/amd/fsinfo/conf/hosts/tsun13.doc.ic.ac.uk
new file mode 100644
index 0000000..44e4956
--- /dev/null
+++ b/usr.sbin/amd/fsinfo/conf/hosts/tsun13.doc.ic.ac.uk
@@ -0,0 +1,3 @@
+#include "../csg_sun3"
+
+CSG_SUN3(tsun13,doc.ic.ac.uk,tsunfs.doc.ic.ac.uk,gould.doc.ic.ac.uk)
diff --git a/usr.sbin/amd/fsinfo/conf/hosts/tsun14.doc.ic.ac.uk b/usr.sbin/amd/fsinfo/conf/hosts/tsun14.doc.ic.ac.uk
new file mode 100644
index 0000000..66a5051
--- /dev/null
+++ b/usr.sbin/amd/fsinfo/conf/hosts/tsun14.doc.ic.ac.uk
@@ -0,0 +1,3 @@
+#include "../csg_sun3"
+
+CSG_SUN3(tsun14,doc.ic.ac.uk,tsunfs.doc.ic.ac.uk,gould.doc.ic.ac.uk)
diff --git a/usr.sbin/amd/fsinfo/conf/hosts/tsun15.doc.ic.ac.uk b/usr.sbin/amd/fsinfo/conf/hosts/tsun15.doc.ic.ac.uk
new file mode 100644
index 0000000..3246df3
--- /dev/null
+++ b/usr.sbin/amd/fsinfo/conf/hosts/tsun15.doc.ic.ac.uk
@@ -0,0 +1,3 @@
+#include "../csg_sun3"
+
+CSG_SUN3(tsun15,doc.ic.ac.uk,tsunfs.doc.ic.ac.uk,gould.doc.ic.ac.uk)
diff --git a/usr.sbin/amd/fsinfo/conf/hosts/tsun16.doc.ic.ac.uk b/usr.sbin/amd/fsinfo/conf/hosts/tsun16.doc.ic.ac.uk
new file mode 100644
index 0000000..4ab7bd0
--- /dev/null
+++ b/usr.sbin/amd/fsinfo/conf/hosts/tsun16.doc.ic.ac.uk
@@ -0,0 +1,3 @@
+#include "../csg_sun3"
+
+CSG_SUN3(tsun16,doc.ic.ac.uk,pelican.doc.ic.ac.uk,pelican.doc.ic.ac.uk)
diff --git a/usr.sbin/amd/fsinfo/conf/hosts/tsun17.doc.ic.ac.uk b/usr.sbin/amd/fsinfo/conf/hosts/tsun17.doc.ic.ac.uk
new file mode 100644
index 0000000..11ef757
--- /dev/null
+++ b/usr.sbin/amd/fsinfo/conf/hosts/tsun17.doc.ic.ac.uk
@@ -0,0 +1,3 @@
+#include "../csg_sun3"
+
+CSG_SUN3(tsun17,doc.ic.ac.uk,pelican.doc.ic.ac.uk,pelican.doc.ic.ac.uk)
diff --git a/usr.sbin/amd/fsinfo/conf/hosts/tsun18.doc.ic.ac.uk b/usr.sbin/amd/fsinfo/conf/hosts/tsun18.doc.ic.ac.uk
new file mode 100644
index 0000000..fbdf879
--- /dev/null
+++ b/usr.sbin/amd/fsinfo/conf/hosts/tsun18.doc.ic.ac.uk
@@ -0,0 +1,3 @@
+#include "../csg_sun3"
+
+CSG_SUN3(tsun18,doc.ic.ac.uk,pelican.doc.ic.ac.uk,pelican.doc.ic.ac.uk)
diff --git a/usr.sbin/amd/fsinfo/conf/hosts/tsun19.doc.ic.ac.uk b/usr.sbin/amd/fsinfo/conf/hosts/tsun19.doc.ic.ac.uk
new file mode 100644
index 0000000..da9aba8
--- /dev/null
+++ b/usr.sbin/amd/fsinfo/conf/hosts/tsun19.doc.ic.ac.uk
@@ -0,0 +1,3 @@
+#include "../csg_sun3"
+
+CSG_SUN3(tsun19,doc.ic.ac.uk,pelican.doc.ic.ac.uk,pelican.doc.ic.ac.uk)
diff --git a/usr.sbin/amd/fsinfo/conf/hosts/tsun2.doc.ic.ac.uk b/usr.sbin/amd/fsinfo/conf/hosts/tsun2.doc.ic.ac.uk
new file mode 100644
index 0000000..b6fca77
--- /dev/null
+++ b/usr.sbin/amd/fsinfo/conf/hosts/tsun2.doc.ic.ac.uk
@@ -0,0 +1,3 @@
+#include "../csg_sun3"
+
+CSG_SUN3(tsun2,doc.ic.ac.uk,tsunfs.doc.ic.ac.uk,gould.doc.ic.ac.uk)
diff --git a/usr.sbin/amd/fsinfo/conf/hosts/tsun3.doc.ic.ac.uk b/usr.sbin/amd/fsinfo/conf/hosts/tsun3.doc.ic.ac.uk
new file mode 100644
index 0000000..e40bd16
--- /dev/null
+++ b/usr.sbin/amd/fsinfo/conf/hosts/tsun3.doc.ic.ac.uk
@@ -0,0 +1,3 @@
+#include "../csg_sun3"
+
+CSG_SUN3(tsun3,doc.ic.ac.uk,tsunfs.doc.ic.ac.uk,gould.doc.ic.ac.uk)
diff --git a/usr.sbin/amd/fsinfo/conf/hosts/tsun4.doc.ic.ac.uk b/usr.sbin/amd/fsinfo/conf/hosts/tsun4.doc.ic.ac.uk
new file mode 100644
index 0000000..cd97358
--- /dev/null
+++ b/usr.sbin/amd/fsinfo/conf/hosts/tsun4.doc.ic.ac.uk
@@ -0,0 +1,3 @@
+#include "../csg_sun3"
+
+CSG_SUN3(tsun4,doc.ic.ac.uk,tsunfs.doc.ic.ac.uk,gould.doc.ic.ac.uk)
diff --git a/usr.sbin/amd/fsinfo/conf/hosts/tsun5.doc.ic.ac.uk b/usr.sbin/amd/fsinfo/conf/hosts/tsun5.doc.ic.ac.uk
new file mode 100644
index 0000000..3a8c7e2
--- /dev/null
+++ b/usr.sbin/amd/fsinfo/conf/hosts/tsun5.doc.ic.ac.uk
@@ -0,0 +1,3 @@
+#include "../csg_sun3"
+
+CSG_SUN3(tsun5,doc.ic.ac.uk,tsunfs.doc.ic.ac.uk,gould.doc.ic.ac.uk)
diff --git a/usr.sbin/amd/fsinfo/conf/hosts/tsun6.doc.ic.ac.uk b/usr.sbin/amd/fsinfo/conf/hosts/tsun6.doc.ic.ac.uk
new file mode 100644
index 0000000..4c6ea76
--- /dev/null
+++ b/usr.sbin/amd/fsinfo/conf/hosts/tsun6.doc.ic.ac.uk
@@ -0,0 +1,3 @@
+#include "../csg_sun3"
+
+CSG_SUN3(tsun6,doc.ic.ac.uk,tsunfs.doc.ic.ac.uk,gould.doc.ic.ac.uk)
diff --git a/usr.sbin/amd/fsinfo/conf/hosts/tsun7.doc.ic.ac.uk b/usr.sbin/amd/fsinfo/conf/hosts/tsun7.doc.ic.ac.uk
new file mode 100644
index 0000000..9df32a9
--- /dev/null
+++ b/usr.sbin/amd/fsinfo/conf/hosts/tsun7.doc.ic.ac.uk
@@ -0,0 +1,3 @@
+#include "../csg_sun3"
+
+CSG_SUN3(tsun7,doc.ic.ac.uk,tsunfs.doc.ic.ac.uk,gould.doc.ic.ac.uk)
diff --git a/usr.sbin/amd/fsinfo/conf/hosts/tsun8.doc.ic.ac.uk b/usr.sbin/amd/fsinfo/conf/hosts/tsun8.doc.ic.ac.uk
new file mode 100644
index 0000000..e2b5e1f
--- /dev/null
+++ b/usr.sbin/amd/fsinfo/conf/hosts/tsun8.doc.ic.ac.uk
@@ -0,0 +1,3 @@
+#include "../csg_sun3"
+
+CSG_SUN3(tsun8,doc.ic.ac.uk,tsunfs.doc.ic.ac.uk,gould.doc.ic.ac.uk)
diff --git a/usr.sbin/amd/fsinfo/conf/hosts/tsun9.doc.ic.ac.uk b/usr.sbin/amd/fsinfo/conf/hosts/tsun9.doc.ic.ac.uk
new file mode 100644
index 0000000..e82e815
--- /dev/null
+++ b/usr.sbin/amd/fsinfo/conf/hosts/tsun9.doc.ic.ac.uk
@@ -0,0 +1,3 @@
+#include "../csg_sun3"
+
+CSG_SUN3(tsun9,doc.ic.ac.uk,tsunfs.doc.ic.ac.uk,gould.doc.ic.ac.uk)
diff --git a/usr.sbin/amd/fsinfo/conf/hosts/tsunfs.doc.ic.ac.uk b/usr.sbin/amd/fsinfo/conf/hosts/tsunfs.doc.ic.ac.uk
new file mode 100644
index 0000000..b09db9d
--- /dev/null
+++ b/usr.sbin/amd/fsinfo/conf/hosts/tsunfs.doc.ic.ac.uk
@@ -0,0 +1,211 @@
+// mkfsinfo for tsunfs
+// $Id$
+
+host tsunfs.doc.ic.ac.uk
+
+/*
+arch sun3
+os sos4
+cluster csg
+dumphost flamingo.doc.ic.ac.uk
+*/
+
+#define FSTYPE_UFS 4.2
+#define DEFAULT_OPTS rw,noquota,nosuid,grpid
+
+// swap
+fs /dev/xy0b {
+ fstype = swap
+}
+
+// root
+fs /dev/xy0a {
+ fstype = FSTYPE_UFS
+ opts = DEFAULT_OPTS
+ passno = 1
+ freq = 1
+ dumpset = csg_sun3_vax
+ mount / {}
+}
+
+// usr
+fs /dev/xy0f {
+ fstype = FSTYPE_UFS
+ opts = rw,grpid
+ freq = 1
+ passno = 2
+ dumpset = csg_sun3_vax
+ mount /usr {}
+}
+
+// var
+fs /dev/xy0e {
+ fstype = FSTYPE_UFS
+ opts = DEFAULT_OPTS
+ freq = 1
+ passno = 4
+ dumpset = csg_sun3_vax
+ mount /var {
+ mmdf {
+ exportfs "-access=teach_hosts,root=\
+ tsun1.doc.ic.ac.uk:\
+ tsun2.doc.ic.ac.uk:\
+ tsun3.doc.ic.ac.uk:\
+ tsun4.doc.ic.ac.uk:\
+ tsun5.doc.ic.ac.uk:\
+ tsun6.doc.ic.ac.uk:\
+ tsun7.doc.ic.ac.uk:\
+ tsun8.doc.ic.ac.uk:\
+ tsun9.doc.ic.ac.uk:\
+ tsun10.doc.ic.ac.uk:\
+ tsun11.doc.ic.ac.uk:\
+ tsun12.doc.ic.ac.uk:\
+ tsun13.doc.ic.ac.uk:\
+ tsun14.doc.ic.ac.uk:\
+ tsun15.doc.ic.ac.uk:\
+ ssun1.doc.ic.ac.uk:\
+ whoops.doc.ic.ac.uk:\
+ "
+ }
+ }
+}
+
+// root filesystems for diskless clients
+fs /dev/xy0d {
+ fstype = FSTYPE_UFS
+ opts = rw,grpid
+ freq = 1
+ passno = 3
+ mount /export/roots {
+ tsun1 {
+ volname /vol/export/roots/tsun1
+ exportfs "-root=tsun1.doc.ic.ac.uk,-access=tsun1.doc.ic.ac.uk"
+ }
+ tsun2 {
+ volname /vol/export/roots/tsun2
+ exportfs "-root=tsun2.doc.ic.ac.uk,-access=tsun2.doc.ic.ac.uk"
+ }
+ tsun3 {
+ volname /vol/export/roots/tsun3
+ exportfs "-root=tsun3.doc.ic.ac.uk,-access=tsun3.doc.ic.ac.uk"
+ }
+ tsun4 {
+ volname /vol/export/roots/tsun4
+ exportfs "-root=tsun4.doc.ic.ac.uk,-access=tsun4.doc.ic.ac.uk"
+ }
+ tsun5 {
+ volname /vol/export/roots/tsun5
+ exportfs "-root=tsun5.doc.ic.ac.uk,-access=tsun5.doc.ic.ac.uk"
+ }
+ tsun6 {
+ volname /vol/export/roots/tsun6
+ exportfs "-root=tsun6.doc.ic.ac.uk,-access=tsun6.doc.ic.ac.uk"
+ }
+ tsun7 {
+ volname /vol/export/roots/tsun7
+ exportfs "-root=tsun7.doc.ic.ac.uk,-access=tsun7.doc.ic.ac.uk"
+ }
+ tsun8 {
+ volname /vol/export/roots/tsun8
+ exportfs "-root=tsun8.doc.ic.ac.uk,-access=tsun8.doc.ic.ac.uk"
+ }
+ tsun9 {
+ volname /vol/export/roots/tsun9
+ exportfs "-root=tsun9.doc.ic.ac.uk,-access=tsun9.doc.ic.ac.uk"
+ }
+ tsun10 {
+ volname /vol/export/roots/tsun10
+ exportfs "-root=tsun10.doc.ic.ac.uk,-access=tsun10.doc.ic.ac.uk"
+ }
+ tsun11 {
+ volname /vol/export/roots/tsun11
+ exportfs "-root=tsun11.doc.ic.ac.uk,-access=tsun11.doc.ic.ac.uk"
+ }
+ tsun12 {
+ volname /vol/export/roots/tsun12
+ exportfs "-root=tsun12.doc.ic.ac.uk,-access=tsun12.doc.ic.ac.uk"
+ }
+ tsun13 {
+ volname /vol/export/roots/tsun13
+ exportfs "-root=tsun13.doc.ic.ac.uk,-access=tsun13.doc.ic.ac.uk"
+ }
+ tsun14 {
+ volname /vol/export/roots/tsun14
+ exportfs "-root=tsun14.doc.ic.ac.uk,-access=tsun14.doc.ic.ac.uk"
+ }
+ tsun15 {
+ volname /vol/export/roots/tsun15
+ exportfs "-root=tsun15.doc.ic.ac.uk,-access=tsun15.doc.ic.ac.uk"
+ }
+ }
+}
+
+// swap for diskless clients
+fs /dev/xy1c {
+ fstype = FSTYPE_UFS
+ opts = DEFAULT_OPTS
+ freq = 1
+ passno = 5
+ mount /export/swaps {
+ tsun1 {
+ volname /vol/export/swaps/tsun1
+ exportfs "-root=tsun1.doc.ic.ac.uk,-access=tsun1.doc.ic.ac.uk"
+ }
+ tsun2 {
+ volname /vol/export/swaps/tsun2
+ exportfs "-root=tsun2.doc.ic.ac.uk,-access=tsun2.doc.ic.ac.uk"
+ }
+ tsun3 {
+ volname /vol/export/swaps/tsun3
+ exportfs "-root=tsun3.doc.ic.ac.uk,-access=tsun3.doc.ic.ac.uk"
+ }
+ tsun4 {
+ volname /vol/export/swaps/tsun4
+ exportfs "-root=tsun4.doc.ic.ac.uk,-access=tsun4.doc.ic.ac.uk"
+ }
+ tsun5 {
+ volname /vol/export/swaps/tsun5
+ exportfs "-root=tsun5.doc.ic.ac.uk,-access=tsun5.doc.ic.ac.uk"
+ }
+ tsun6 {
+ volname /vol/export/swaps/tsun6
+ exportfs "-root=tsun6.doc.ic.ac.uk,-access=tsun6.doc.ic.ac.uk"
+ }
+ tsun7 {
+ volname /vol/export/swaps/tsun7
+ exportfs "-root=tsun7.doc.ic.ac.uk,-access=tsun7.doc.ic.ac.uk"
+ }
+ tsun8 {
+ volname /vol/export/swaps/tsun8
+ exportfs "-root=tsun8.doc.ic.ac.uk,-access=tsun8.doc.ic.ac.uk"
+ }
+ tsun9 {
+ volname /vol/export/swaps/tsun9
+ exportfs "-root=tsun9.doc.ic.ac.uk,-access=tsun9.doc.ic.ac.uk"
+ }
+ tsun10 {
+ volname /vol/export/swaps/tsun10
+ exportfs "-root=tsun10.doc.ic.ac.uk,-access=tsun10.doc.ic.ac.uk"
+ }
+ tsun11 {
+ volname /vol/export/swaps/tsun11
+ exportfs "-root=tsun11.doc.ic.ac.uk,-access=tsun11.doc.ic.ac.uk"
+ }
+ tsun12 {
+ volname /vol/export/swaps/tsun12
+ exportfs "-root=tsun12.doc.ic.ac.uk,-access=tsun12.doc.ic.ac.uk"
+ }
+ tsun13 {
+ volname /vol/export/swaps/tsun13
+ exportfs "-root=tsun13.doc.ic.ac.uk,-access=tsun13.doc.ic.ac.uk"
+ }
+ tsun14 {
+ volname /vol/export/swaps/tsun14
+ exportfs "-root=tsun14.doc.ic.ac.uk,-access=tsun14.doc.ic.ac.uk"
+ }
+ tsun15 {
+ volname /vol/export/swaps/tsun15
+ exportfs "-root=tsun15.doc.ic.ac.uk,-access=tsun15.doc.ic.ac.uk"
+ }
+ }
+}
diff --git a/usr.sbin/amd/fsinfo/conf/hosts/whoops.doc.ic.ac.uk b/usr.sbin/amd/fsinfo/conf/hosts/whoops.doc.ic.ac.uk
new file mode 100644
index 0000000..831053b
--- /dev/null
+++ b/usr.sbin/amd/fsinfo/conf/hosts/whoops.doc.ic.ac.uk
@@ -0,0 +1,21 @@
+// $Id$
+// sm's bastardised csg_sun3
+// note that no /var/spool/rwho is mounted as we now expect amd to do this as /vol/rwho
+
+#define HOST whoops
+#define DOMAIN doc.ic.ac.uk
+#define BOOT gould.doc.ic.ac.uk
+#define EXEC gould.doc.ic.ac.uk
+#define MAIL tsunfs.doc.ic.ac.uk
+
+// a sun3
+host HOST.DOMAIN
+
+mount /vol/export/roots/HOST as / from BOOT opts rw,grpid,hard,intr
+mount /vol/export/swaps/HOST fstype swap as swap from BOOT opts swap
+mount /vol/export/exec/sun3 as /usr from EXEC opts ro,grpid,hard,intr
+mount /vol/export/misc/crash/HOST as /var/crash/HOST from EXEC opts rw,nosuid,grpid,hard,intr
+mount /vol/export/misc/tmp/HOST as /tmp from EXEC opts rw,nosuid,grpid,hard,intr
+mount /vol/export/misc/usr.tmp/HOST as /var/tmp from EXEC opts rw,nosuid,grpid,hard,intr
+mount /var/mmdf from MAIL opts rw,nosuid,grpid,hard,intr
+
diff --git a/usr.sbin/amd/fsinfo/conf/users b/usr.sbin/amd/fsinfo/conf/users
new file mode 100644
index 0000000..9dc83d9
--- /dev/null
+++ b/usr.sbin/amd/fsinfo/conf/users
@@ -0,0 +1,106 @@
+audit -> /etc/security/audit
+bin -> /bin
+daemon -> /
+games -> /usr/games
+ingres -> /usr/ingres
+news -> /var/spool/news
+nobody -> /nonexistent
+root -> /
+sync -> /
+sys -> /
+sysdiag -> /usr/diag/sysdiag
+uucp -> /var/spool/uucppublic
+acwf = /home/toytown/others/acwf
+adh = /home/dylan/dk2/adh
+ae = /home/toytown/samson/ae
+aj = /home/toytown/samson/aj
+athena = /vol/src/athena
+bh = /home/toytown/jim/bh
+bp = /home/toytown/others/bp
+brg = /home/gummo/users/brg
+bt = /home/toytown/samson/bt
+ca = /home/toytown/diadem/ca
+ccm = /home/toytown/ai/ccm
+chlo = /home/toytown/samson/chlo
+clh = /home/toytown/ai/clh
+cr = /home/toytown/samson/cr
+cw = /home/toytown/genesis/cw
+dds = /home/toytown/genesis/dds
+dg = /home/toytown/dov/dg
+dgb = /home/dylan/dk2/dgb
+dme = /home/achilles/dme
+dp = /home/gummo/usersdiana
+ds = /home/toytown/ai/ds
+dwj = /home/achilles/dwj
+eaa = /home/toytown/dov/eaa
+esh = /home/toytown/ai/esh
+fcs = /home/ganymede/fcs
+fst = /home/dylan/dk2/fst
+glb = /home/toytown/ai/glb
+grace = /home/toytown/others/grace
+guest = /home/toytown/others/guest
+hd = /home/toytown/others/hd
+hf = /home/toytown/genesis/hf
+iccp = /home/toytown/samson/iccp
+ids = /home/toytown/samson/ids
+ih = /home/toytown/others/ih
+ja = /home/toytown/ai/ja
+jfc = /home/toytown/jim/jfc
+jg = /home/toytown/genesis/jg
+jjc = /home/toytown/genesis/jjc
+js = /home/toytown/samson/js
+jsp = /home/achilles/jsp
+jvp = /home/toytown/jim/jvp
+kdr = /home/dylan/dk2/kdr
+kevin = /home/toytown/others/kpt
+kpt = /home/toytown/others/kpt
+ksa = /home/toytown/ai/ksa
+lkcl = /home/dylan/dk2/lkcl
+ll = /home/toytown/dov/ll
+ll1 = /home/toytown/others/ll1
+lmjm = /home/toytown/others/lmjm
+lsh = /home/toytown/ai/lsh
+mb = /home/toytown/jim/mb
+md = /home/achilles/md
+mdr = /home/achilles/mdr
+mg = /home/ganymede/mg
+mjh = /home/toytown/others/mjh
+mrs = /home/achilles/mrs
+mwg = /home/achilles/mwg
+mwt = /home/achilles/mwt
+nd = /home/gummo/users/nd
+njw = /home/dylan/dk2/njw
+ok = /home/ganymede/ok
+pah = /home/toytown/jim/pah
+pdg = /home/toytown/samson/pdg
+phjk = /home/toytown/ai/phjk
+pm = /home/achilles/pm
+pm2 = /home/dylan/dk2/pm2
+ps = /home/toytown/genesis/ps
+pt = /home/toytown/dov/pt
+pvr = /home/toytown/jim/pvr
+rgc = /home/toytown/jim/rgc
+rjc = /home/toytown/jim/rjc
+rjq = /home/achilles/rjq
+sa = /home/toytown/samson/sa
+shb = /home/toytown/others/shb
+shc = /home/dylan/dk2/shc
+sjk = /home/toytown/jim/sjk
+sjl2 = /home/achilles/sjl2
+sjv = /home/ganymede/sjv
+sm = /home/toytown/others/sm
+sme = /home/ganymede/sme
+sph = /home/toytown/ai/sph
+ssp = /home/toytown/others/ssp
+sw = /home/toytown/others/sw
+sza = /home/ganymede/sza
+teb = /home/dylan/dk2/teb
+thp = /home/achilles/thp
+tm = /home/toytown/ai/tm
+tsem = /home/toytown/genesis/tsem
+umacd20 = /home/ganymede/umacd20
+wmvh = /home/dylan/dk2/wmvh
+wrdo = /home/ganymede/wrdo
+ygal = /home/toytown/samson/ygal
+zmact03 = /home/toytown/jim/zmact03
+zmacy26 = /home/toytown/jim/zmacy26
diff --git a/usr.sbin/amd/fsinfo/fsi_analyze.c b/usr.sbin/amd/fsinfo/fsi_analyze.c
new file mode 100644
index 0000000..108333e
--- /dev/null
+++ b/usr.sbin/amd/fsinfo/fsi_analyze.c
@@ -0,0 +1,645 @@
+/*
+ * Copyright (c) 1989 Jan-Simon Pendry
+ * Copyright (c) 1989 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)fsi_analyze.c 8.1 (Berkeley) 6/6/93
+ *
+ * $Id$
+ *
+ */
+
+/*
+ * Analyze filesystem declarations
+ *
+ * Note: most of this is magic!
+ */
+
+#include "../fsinfo/fsinfo.h"
+
+char *disk_fs_strings[] = {
+ "fstype", "opts", "dumpset", "passno", "freq", "mount", "log", 0,
+};
+
+char *mount_strings[] = {
+ "volname", "exportfs", 0,
+};
+
+char *fsmount_strings[] = {
+ "as", "volname", "fstype", "opts", "from", 0,
+};
+
+char *host_strings[] = {
+ "host", "netif", "config", "arch", "cluster", "os", 0,
+};
+
+char *ether_if_strings[] = {
+ "inaddr", "netmask", "hwaddr", 0,
+};
+
+/*
+ * Strip off the trailing part of a domain
+ * to produce a short-form domain relative
+ * to the local host domain.
+ * Note that this has no effect if the domain
+ * names do not have the same number of
+ * components. If that restriction proves
+ * to be a problem then the loop needs recoding
+ * to skip from right to left and do partial
+ * matches along the way -- ie more expensive.
+ */
+void domain_strip(otherdom, localdom)
+char *otherdom, *localdom;
+{
+#ifdef PARTIAL_DOMAINS
+ char *p1 = otherdom-1;
+ char *p2 = localdom-1;
+
+ do {
+ if (p1 = strchr(p1+1, '.'))
+ if (p2 = strchr(p2+1, '.'))
+ if (STREQ(p1+1, p2+1)) {
+ *p1 = '\0';
+ break;
+ }
+ } while (p1 && p2);
+#else
+ char *p1, *p2;
+
+ if ((p1 = strchr(otherdom, '.')) &&
+ (p2 = strchr(localdom, '.')) &&
+ (strcmp(p1+1, p2+1) == 0))
+ *p1 = '\0';
+#endif /* PARTIAL_DOMAINS */
+}
+
+/*
+ * Take a little-endian domain name and
+ * transform into a big-endian Un*x pathname.
+ * For example: kiska.doc.ic -> ic/doc/kiska
+ */
+static char *compute_hostpath(hn)
+char *hn;
+{
+ char *p = strdup(hn);
+ char *d;
+ char path[MAXPATHLEN];
+
+ domain_strip(p, hostname);
+ path[0] = '\0';
+
+ do {
+ d = strrchr(p, '.');
+ if (d) {
+ *d = 0;
+ strcat(path, d+1);
+ strcat(path, "/");
+ } else {
+ strcat(path, p);
+ }
+ } while (d);
+
+ log("hostpath of '%s' is '%s'", hn, path);
+
+ strcpy(p, path);
+ return p;
+}
+
+static dict_ent *find_volname(nn)
+char *nn;
+{
+ dict_ent *de;
+ char *p = strdup(nn);
+ char *q;
+
+ do {
+ log("Searching for volname %s", p);
+ de = dict_locate(dict_of_volnames, p);
+ q = strrchr(p, '/');
+ if (q) *q = '\0';
+ } while (!de && q);
+
+ free(p);
+ return de;
+}
+
+static show_required(l, mask, info, hostname, strings)
+ioloc *l;
+int mask;
+char *info;
+char *hostname;
+char *strings[];
+{
+ int i;
+ log("mask left for %s:%s is %#x", hostname, info, mask);
+
+ for (i = 0; strings[i]; i++)
+ if (ISSET(mask, i))
+ lerror(l, "%s:%s needs field \"%s\"", hostname, info, strings[i]);
+}
+
+/*
+ * Check and fill in "exportfs" details.
+ * Make sure the m_exported field references
+ * the most local node with an "exportfs" entry.
+ */
+static int check_exportfs(q, e)
+qelem *q;
+mount *e;
+{
+ mount *mp;
+ int errors = 0;
+
+ ITER(mp, mount, q) {
+ if (ISSET(mp->m_mask, DM_EXPORTFS)) {
+ if (e)
+ lwarning(mp->m_ioloc, "%s has duplicate exportfs data", mp->m_name);
+ mp->m_exported = mp;
+ if (!ISSET(mp->m_mask, DM_VOLNAME))
+ set_mount(mp, DM_VOLNAME, strdup(mp->m_name));
+ } else {
+ mp->m_exported = e;
+ }
+
+ /*
+ * Recursively descend the mount tree
+ */
+ if (mp->m_mount)
+ errors += check_exportfs(mp->m_mount, mp->m_exported);
+
+ /*
+ * If a volume name has been specified, but this node and none
+ * of its parents has been exported, report an error.
+ */
+ if (ISSET(mp->m_mask, DM_VOLNAME) && !mp->m_exported) {
+ lerror(mp->m_ioloc, "%s has a volname but no exportfs data", mp->m_name);
+ errors++;
+ }
+ }
+
+ return errors;
+}
+
+static int analyze_dkmount_tree(q, parent, dk)
+qelem *q;
+mount *parent;
+disk_fs *dk;
+{
+ mount *mp;
+ int errors = 0;
+
+ ITER(mp, mount, q) {
+ log("Mount %s:", mp->m_name);
+ if (parent) {
+ char n[MAXPATHLEN];
+ sprintf(n, "%s/%s", parent->m_name, mp->m_name);
+ if (*mp->m_name == '/')
+ lerror(mp->m_ioloc, "sub-directory %s of %s starts with '/'", mp->m_name, parent->m_name);
+ else if (STREQ(mp->m_name, "default"))
+ lwarning(mp->m_ioloc, "sub-directory of %s is named \"default\"", parent->m_name);
+ log("Changing name %s to %s", mp->m_name, n);
+ free(mp->m_name);
+ mp->m_name = strdup(n);
+ }
+ mp->m_name_len = strlen(mp->m_name);
+ mp->m_parent = parent;
+ mp->m_dk = dk;
+ if (mp->m_mount)
+ analyze_dkmount_tree(mp->m_mount, mp, dk);
+ }
+
+ return errors;
+}
+
+/*
+ * The mount tree is a singleton list
+ * containing the top-level mount
+ * point for a disk.
+ */
+static int analyze_dkmounts(dk, q)
+disk_fs *dk;
+qelem *q;
+{
+ int errors = 0;
+ mount *mp, *mp2 = 0;
+ int i = 0;
+
+ /*
+ * First scan the list of subdirs to make
+ * sure there is only one - and remember it
+ */
+ if (q) {
+ ITER(mp, mount, q) {
+ mp2 = mp;
+ i++;
+ }
+ }
+
+ /*
+ * Check...
+ */
+ if (i < 1) {
+ lerror(dk->d_ioloc, "%s:%s has no mount point", dk->d_host->h_hostname, dk->d_dev);
+ return 1;
+ }
+ if (i > 1) {
+ lerror(dk->d_ioloc, "%s:%s has more than one mount point", dk->d_host->h_hostname, dk->d_dev);
+ errors++;
+ }
+ /*
+ * Now see if a default mount point is required
+ */
+ if (STREQ(mp2->m_name, "default")) {
+ if (ISSET(mp2->m_mask, DM_VOLNAME)) {
+ char nbuf[1024];
+ compute_automount_point(nbuf, dk->d_host, mp2->m_volname);
+ free(mp2->m_name);
+ mp2->m_name = strdup(nbuf);
+ log("%s:%s has default mount on %s", dk->d_host->h_hostname, dk->d_dev, mp2->m_name);
+ } else {
+ lerror(dk->d_ioloc, "no volname given for %s:%s", dk->d_host->h_hostname, dk->d_dev);
+ errors++;
+ }
+ }
+ /*
+ * Fill in the disk mount point
+ */
+ if (!errors && mp2 && mp2->m_name)
+ dk->d_mountpt = strdup(mp2->m_name);
+ else
+ dk->d_mountpt = strdup("error");
+
+ /*
+ * Analyze the mount tree
+ */
+ errors += analyze_dkmount_tree(q, 0, dk);
+
+ /*
+ * Analyze the export tree
+ */
+ errors += check_exportfs(q, 0);
+
+ return errors;
+}
+
+static void fixup_required_disk_info(dp)
+disk_fs *dp;
+{
+ /*
+ * "fstype"
+ */
+ if (ISSET(dp->d_mask, DF_FSTYPE)) {
+ if (STREQ(dp->d_fstype, "swap")) {
+ /*
+ * Fixup for a swap device
+ */
+ if (!ISSET(dp->d_mask, DF_PASSNO)) {
+ dp->d_passno = 0;
+ BITSET(dp->d_mask, DF_PASSNO);
+ } else if (dp->d_freq != 0) {
+ lwarning(dp->d_ioloc,
+ "Pass number for %s:%s is non-zero",
+ dp->d_host->h_hostname, dp->d_dev);
+ }
+
+ /*
+ * "freq"
+ */
+ if (!ISSET(dp->d_mask, DF_FREQ)) {
+ dp->d_freq = 0;
+ BITSET(dp->d_mask, DF_FREQ);
+ } else if (dp->d_freq != 0) {
+ lwarning(dp->d_ioloc,
+ "dump frequency for %s:%s is non-zero",
+ dp->d_host->h_hostname, dp->d_dev);
+ }
+
+ /*
+ * "opts"
+ */
+ if (!ISSET(dp->d_mask, DF_OPTS))
+ set_disk_fs(dp, DF_OPTS, strdup("swap"));
+
+ /*
+ * "mount"
+ */
+ if (!ISSET(dp->d_mask, DF_MOUNT)) {
+ qelem *q = new_que();
+ mount *m = new_mount();
+ m->m_name = strdup("swap");
+ m->m_mount = new_que();
+ ins_que(&m->m_q, q->q_back);
+ dp->d_mount = q;
+ BITSET(dp->d_mask, DF_MOUNT);
+ } else {
+ lerror(dp->d_ioloc, "%s: mount field specified for swap partition", dp->d_host->h_hostname);
+ }
+ } else if (STREQ(dp->d_fstype, "export")) {
+ /*
+ * "passno"
+ */
+ if (!ISSET(dp->d_mask, DF_PASSNO)) {
+ dp->d_passno = 0;
+ BITSET(dp->d_mask, DF_PASSNO);
+ } else if (dp->d_passno != 0) {
+ lwarning(dp->d_ioloc,
+ "pass number for %s:%s is non-zero",
+ dp->d_host->h_hostname, dp->d_dev);
+ }
+
+ /*
+ * "freq"
+ */
+ if (!ISSET(dp->d_mask, DF_FREQ)) {
+ dp->d_freq = 0;
+ BITSET(dp->d_mask, DF_FREQ);
+ } else if (dp->d_freq != 0) {
+ lwarning(dp->d_ioloc,
+ "dump frequency for %s:%s is non-zero",
+ dp->d_host->h_hostname, dp->d_dev);
+ }
+
+ /*
+ * "opts"
+ */
+ if (!ISSET(dp->d_mask, DF_OPTS))
+ set_disk_fs(dp, DF_OPTS, strdup("rw,defaults"));
+
+ }
+ }
+}
+
+static void fixup_required_mount_info(fp, de)
+fsmount *fp;
+dict_ent *de;
+{
+ if (!ISSET(fp->f_mask, FM_FROM)) {
+ if (de->de_count != 1) {
+ lerror(fp->f_ioloc, "ambiguous mount: %s is a replicated filesystem", fp->f_volname);
+ } else {
+ dict_data *dd;
+ mount *mp = 0;
+ ITER(dd, dict_data, &de->de_q) {
+ mp = (mount *) dd->dd_data;
+ break;
+ }
+ if (!mp)
+ abort();
+ fp->f_ref = mp;
+ set_fsmount(fp, FM_FROM, mp->m_dk->d_host->h_hostname);
+ log("set: %s comes from %s", fp->f_volname, fp->f_from);
+ }
+ }
+
+ if (!ISSET(fp->f_mask, FM_FSTYPE)) {
+ set_fsmount(fp, FM_FSTYPE, strdup("nfs"));
+ log("set: fstype is %s", fp->f_fstype);
+ }
+
+ if (!ISSET(fp->f_mask, FM_OPTS)) {
+ set_fsmount(fp, FM_OPTS, strdup("rw,nosuid,grpid,defaults"));
+ log("set: opts are %s", fp->f_opts);
+ }
+
+ if (!ISSET(fp->f_mask, FM_LOCALNAME)) {
+ if (fp->f_ref) {
+ set_fsmount(fp, FM_LOCALNAME, strdup(fp->f_volname));
+ log("set: localname is %s", fp->f_localname);
+ } else {
+ lerror(fp->f_ioloc, "cannot determine localname since volname %s is not uniquely defined", fp->f_volname);
+ }
+ }
+}
+
+/*
+ * For each disk on a host
+ * analyze the mount information
+ * and fill in any derivable
+ * details.
+ */
+static void analyze_drives(hp)
+host *hp;
+{
+ qelem *q = hp->h_disk_fs;
+ disk_fs *dp;
+
+ ITER(dp, disk_fs, q) {
+ int req;
+ log("Disk %s:", dp->d_dev);
+ dp->d_host = hp;
+ fixup_required_disk_info(dp);
+ req = ~dp->d_mask & DF_REQUIRED;
+ if (req)
+ show_required(dp->d_ioloc, req, dp->d_dev, hp->h_hostname, disk_fs_strings);
+ analyze_dkmounts(dp, dp->d_mount);
+ }
+}
+
+/*
+ * Check that all static mounts make sense and
+ * that the source volumes exist.
+ */
+static void analyze_mounts(hp)
+host *hp;
+{
+ qelem *q = hp->h_mount;
+ fsmount *fp;
+ int netbootp = 0;
+
+ ITER(fp, fsmount, q) {
+ char *p;
+ char *nn = strdup(fp->f_volname);
+ int req;
+ dict_ent *de;
+ int found = 0;
+ int matched = 0;
+ do {
+ p = 0;
+ de = find_volname(nn);
+ log("Mount: %s (trying %s)", fp->f_volname, nn);
+
+ if (de) {
+ found = 1;
+ /*
+ * Check that the from field is really exporting
+ * the filesystem requested.
+ */
+ if (ISSET(fp->f_mask, FM_FROM)) {
+ dict_data *dd;
+ mount *mp2 = 0;
+ ITER(dd, dict_data, &de->de_q) {
+ mount *mp = (mount *) dd->dd_data;
+ if (STREQ(mp->m_dk->d_host->h_hostname, fp->f_from)) {
+ mp2 = mp;
+ break;
+ }
+ }
+
+ if (mp2) {
+ fp->f_ref = mp2;
+ matched = 1;
+ break;
+ }
+ } else {
+ matched = 1;
+ break;
+ }
+ }
+ p = strrchr(nn, '/');
+ if (p)
+ *p = 0;
+ } while (de && p);
+ free(nn);
+
+ if (!found) {
+ lerror(fp->f_ioloc, "volname %s unknown", fp->f_volname);
+ } else if (matched) {
+ fixup_required_mount_info(fp, de);
+ req = ~fp->f_mask & FM_REQUIRED;
+ if (req) {
+ show_required(fp->f_ioloc, req, fp->f_volname, hp->h_hostname,
+ fsmount_strings);
+ } else if (strcmp(fp->f_localname, "/") == 0) {
+ hp->h_netroot = fp;
+ netbootp |= FM_NETROOT;
+ } else if (strcmp(fp->f_localname, "swap") == 0) {
+ hp->h_netswap = fp;
+ netbootp |= FM_NETSWAP;
+ }
+ } else {
+ lerror(fp->f_ioloc, "volname %s not exported from %s", fp->f_volname,
+ fp->f_from ? fp->f_from : "anywhere");
+ }
+ }
+
+ if (netbootp && (netbootp != FM_NETBOOT))
+ lerror(hp->h_ioloc, "network booting requires both root and swap areas");
+}
+
+void analyze_hosts(q)
+qelem *q;
+{
+ host *hp;
+
+ show_area_being_processed("analyze hosts", 5);
+
+ /*
+ * Check all drives
+ */
+ ITER(hp, host, q) {
+ log("disks on host %s", hp->h_hostname);
+ show_new("ana-host");
+ hp->h_hostpath = compute_hostpath(hp->h_hostname);
+
+ if (hp->h_disk_fs)
+ analyze_drives(hp);
+
+ }
+
+ show_area_being_processed("analyze mounts", 5);
+
+ /*
+ * Check static mounts
+ */
+ ITER(hp, host, q) {
+ log("mounts on host %s", hp->h_hostname);
+ show_new("ana-mount");
+ if (hp->h_mount)
+ analyze_mounts(hp);
+
+ }
+}
+
+/*
+ * Check an automount request
+ */
+static void analyze_automount(ap)
+automount *ap;
+{
+ dict_ent *de = find_volname(ap->a_volname);
+ if (de) {
+ ap->a_mounted = de;
+ } else {
+ if (STREQ(ap->a_volname, ap->a_name))
+ lerror(ap->a_ioloc, "unknown volname %s automounted", ap->a_volname);
+ else
+ lerror(ap->a_ioloc, "unknown volname %s automounted on %s", ap->a_volname, ap->a_name);
+ }
+}
+
+static void analyze_automount_tree(q, pref, lvl)
+qelem *q;
+char *pref;
+int lvl;
+{
+ automount *ap;
+
+ ITER(ap, automount, q) {
+ char nname[1024];
+ if (lvl > 0 || ap->a_mount)
+ if (ap->a_name[1] && strchr(ap->a_name+1, '/'))
+ lerror(ap->a_ioloc, "not allowed '/' in a directory name");
+ sprintf(nname, "%s/%s", pref, ap->a_name);
+ free(ap->a_name);
+ ap->a_name = strdup(nname[1] == '/' ? nname+1 : nname);
+ log("automount point %s:", ap->a_name);
+ show_new("ana-automount");
+ if (ap->a_mount) {
+ analyze_automount_tree(ap->a_mount, ap->a_name, lvl+1);
+ } else if (ap->a_volname) {
+ log("\tautomount from %s", ap->a_volname);
+ analyze_automount(ap);
+ } else if (ap->a_symlink) {
+ log("\tsymlink to %s", ap->a_symlink);
+ } else {
+ ap->a_volname = strdup(ap->a_name);
+ log("\timplicit automount from %s", ap->a_volname);
+ analyze_automount(ap);
+ }
+ }
+}
+
+void analyze_automounts(q)
+qelem *q;
+{
+ auto_tree *tp;
+
+ show_area_being_processed("analyze automount", 5);
+ /*
+ * q is a list of automounts
+ */
+ ITER(tp, auto_tree, q)
+ analyze_automount_tree(tp->t_mount, "", 0);
+}
diff --git a/usr.sbin/amd/fsinfo/fsi_data.h b/usr.sbin/amd/fsinfo/fsi_data.h
new file mode 100644
index 0000000..a8d9384
--- /dev/null
+++ b/usr.sbin/amd/fsinfo/fsi_data.h
@@ -0,0 +1,236 @@
+/*
+ * Copyright (c) 1989 Jan-Simon Pendry
+ * Copyright (c) 1989 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)fsi_data.h 8.1 (Berkeley) 6/6/93
+ *
+ * $Id$
+ *
+ */
+
+typedef struct auto_tree auto_tree;
+typedef struct automount automount;
+typedef struct dict dict;
+typedef struct dict_data dict_data;
+typedef struct dict_ent dict_ent;
+typedef struct disk_fs disk_fs;
+typedef struct ether_if ether_if;
+typedef struct fsmount fsmount;
+typedef struct host host;
+typedef struct ioloc ioloc;
+typedef struct mount mount;
+typedef struct qelem qelem;
+
+/*
+ * Linked lists...
+ */
+struct qelem {
+ qelem *q_forw;
+ qelem *q_back;
+};
+
+/*
+ * Automount tree
+ */
+struct automount {
+ qelem a_q;
+ ioloc *a_ioloc;
+ char *a_name; /* Automount key */
+ char *a_volname; /* Equivalent volume to be referenced */
+ char *a_symlink; /* Symlink representation */
+ qelem *a_mount; /* Tree representation */
+ dict_ent *a_mounted;
+};
+
+/*
+ * List of automount trees
+ */
+struct auto_tree {
+ qelem t_q;
+ ioloc *t_ioloc;
+ char *t_defaults;
+ qelem *t_mount;
+};
+
+/*
+ * A host
+ */
+struct host {
+ qelem q;
+ int h_mask;
+ ioloc *h_ioloc;
+ fsmount *h_netroot, *h_netswap;
+#define HF_HOST 0
+ char *h_hostname; /* The full name of the host */
+ char *h_lochost; /* The name of the host with local domains stripped */
+ char *h_hostpath; /* The filesystem path to the host (cf compute_hostpath) */
+#define HF_ETHER 1
+ qelem *h_ether;
+#define HF_CONFIG 2
+ qelem *h_config;
+#define HF_ARCH 3
+ char *h_arch;
+#define HF_CLUSTER 4
+ char *h_cluster;
+#define HF_OS 5
+ char *h_os;
+ qelem *h_disk_fs;
+ qelem *h_mount;
+};
+
+/*
+ * An ethernet interface
+ */
+struct ether_if {
+ qelem e_q;
+ int e_mask;
+ ioloc *e_ioloc;
+ char *e_if;
+#define EF_INADDR 0
+ struct in_addr e_inaddr;
+#define EF_NETMASK 1
+ u_long e_netmask;
+#define EF_HWADDR 2
+ char *e_hwaddr;
+};
+
+/*
+ * Disk filesystem structure.
+ *
+ * If the DF_* numbers are changed
+ * disk_fs_strings in analyze.c will
+ * need updating.
+ */
+struct disk_fs {
+ qelem d_q;
+ int d_mask;
+ ioloc *d_ioloc;
+ host *d_host;
+ char *d_mountpt;
+ char *d_dev;
+#define DF_FSTYPE 0
+ char *d_fstype;
+#define DF_OPTS 1
+ char *d_opts;
+#define DF_DUMPSET 2
+ char *d_dumpset;
+#define DF_PASSNO 3
+ int d_passno;
+#define DF_FREQ 4
+ int d_freq;
+#define DF_MOUNT 5
+ qelem *d_mount;
+#define DF_LOG 6
+ char *d_log;
+};
+#define DF_REQUIRED ((1<<DF_FSTYPE)|(1<<DF_OPTS)|(1<<DF_PASSNO)|(1<<DF_MOUNT))
+
+/*
+ * A mount tree
+ */
+struct mount {
+ qelem m_q;
+ ioloc *m_ioloc;
+ int m_mask;
+#define DM_VOLNAME 0
+ char *m_volname;
+#define DM_EXPORTFS 1
+ char *m_exportfs;
+#define DM_SEL 2
+ char *m_sel;
+ char *m_name;
+ int m_name_len;
+ mount *m_parent;
+ disk_fs *m_dk;
+ mount *m_exported;
+ qelem *m_mount;
+};
+
+/*
+ * Additional filesystem mounts
+ *
+ * If the FM_* numbers are changed
+ * disk_fs_strings in analyze.c will
+ * need updating.
+ */
+struct fsmount {
+ qelem f_q;
+ mount *f_ref;
+ ioloc *f_ioloc;
+ int f_mask;
+#define FM_LOCALNAME 0
+ char *f_localname;
+#define FM_VOLNAME 1
+ char *f_volname;
+#define FM_FSTYPE 2
+ char *f_fstype;
+#define FM_OPTS 3
+ char *f_opts;
+#define FM_FROM 4
+ char *f_from;
+};
+#define FM_REQUIRED ((1<<FM_VOLNAME)|(1<<FM_FSTYPE)|(1<<FM_OPTS)|(1<<FM_FROM)|(1<<FM_LOCALNAME))
+#define FM_NETROOT 0x01
+#define FM_NETSWAP 0x02
+#define FM_NETBOOT (FM_NETROOT|FM_NETSWAP)
+
+#define DICTHASH 5
+struct dict_ent {
+ dict_ent *de_next;
+ char *de_key;
+ int de_count;
+ qelem de_q;
+};
+
+/*
+ * Dictionaries ...
+ */
+struct dict_data {
+ qelem dd_q;
+ char *dd_data;
+};
+
+struct dict {
+ dict_ent *de[DICTHASH];
+};
+
+/*
+ * Source text location for error reports
+ */
+struct ioloc {
+ int i_line;
+ char *i_file;
+};
diff --git a/usr.sbin/amd/fsinfo/fsi_dict.c b/usr.sbin/amd/fsinfo/fsi_dict.c
new file mode 100644
index 0000000..68e03f8
--- /dev/null
+++ b/usr.sbin/amd/fsinfo/fsi_dict.c
@@ -0,0 +1,125 @@
+/*
+ * Copyright (c) 1989 Jan-Simon Pendry
+ * Copyright (c) 1989 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)fsi_dict.c 8.1 (Berkeley) 6/6/93
+ *
+ * $Id$
+ *
+ */
+
+/*
+ * Dictionary support
+ */
+
+#include "../fsinfo/fsinfo.h"
+
+static int dict_hash(k)
+char *k;
+{
+ unsigned int h;
+
+ for (h = 0; *k; h += *k++)
+ ;
+ return h % DICTHASH;
+}
+
+dict *new_dict()
+{
+ dict *dp = ALLOC(dict);
+ return dp;
+}
+
+static void dict_add_data(de, v)
+dict_ent *de;
+char *v;
+{
+ dict_data *dd = ALLOC(dict_data);
+ dd->dd_data = v;
+ ins_que(&dd->dd_q, de->de_q.q_back);
+ de->de_count++;
+}
+
+static dict_ent *new_dict_ent(k)
+char *k;
+{
+ dict_ent *de = ALLOC(dict_ent);
+ de->de_key = k;
+ init_que(&de->de_q);
+ return de;
+}
+
+dict_ent *dict_locate(dp, k)
+dict *dp;
+char *k;
+{
+ dict_ent *de = dp->de[dict_hash(k)];
+ while (de && !STREQ(de->de_key, k))
+ de = de->de_next;
+
+ return de;
+}
+
+void dict_add(dp, k, v)
+dict *dp;
+char *k, *v;
+{
+ dict_ent *de = dict_locate(dp, k);
+ if (!de) {
+ dict_ent **dep = &dp->de[dict_hash(k)];
+ de = new_dict_ent(k);
+ de->de_next = *dep;
+ *dep = de;
+ }
+ dict_add_data(de, v);
+}
+
+int dict_iter(dp, fn)
+dict *dp;
+int (*fn)();
+{
+ int i;
+ int errors = 0;
+
+ for (i = 0; i < DICTHASH; i++) {
+ dict_ent *de = dp->de[i];
+ while (de) {
+ errors += (*fn)(&de->de_q);
+ de = de->de_next;
+ }
+ }
+ return errors;
+}
diff --git a/usr.sbin/amd/fsinfo/fsi_gram.y b/usr.sbin/amd/fsinfo/fsi_gram.y
new file mode 100644
index 0000000..412f3c5
--- /dev/null
+++ b/usr.sbin/amd/fsinfo/fsi_gram.y
@@ -0,0 +1,394 @@
+/*
+ * Copyright (c) 1989 Jan-Simon Pendry
+ * Copyright (c) 1989 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)fsi_gram.y 8.1 (Berkeley) 6/6/93
+ *
+ * $Id$
+ *
+ */
+
+%{
+#include "../fsinfo/fsinfo.h"
+#include <stdio.h>
+
+extern qelem *list_of_hosts, *list_of_automounts;
+%}
+
+%union {
+ auto_tree *a;
+ disk_fs *d;
+ ether_if *e;
+ host *h;
+ qelem *q;
+ char *s;
+ mount *m;
+ fsmount *f;
+}
+
+%token tARCH
+%token tAS
+%token tAUTOMOUNT
+%token tCLUSTER
+%token tCONFIG
+%token tDUMPSET
+%token tEQ
+%token tEXPORTFS
+%token tFREQ
+%token tFROM
+%token tFS
+%token tFSTYPE
+%token tHWADDR
+%token tINADDR
+%token tHOST
+%token tLOCALHOST
+%token tLOG
+%token tMOUNT
+%token tNETMASK
+%token tNETIF
+%token tVOLNAME
+%token tOPTS
+%token tOS
+%token tPASSNO
+%token tSEL
+%token <s> tSTR
+
+%start list_of_hosts
+
+%type <a> automount
+%type <q> automount_tree
+%type <e> ether_attr
+%type <m> dir_tree_info
+%type <d> filesystem fs_info_list
+%type <h> host host_attr host_attr_list
+%type <q> list_of_hosts list_of_filesystems list_of_mounts dir_tree
+%type <f> localinfo_list
+%type <s> opt_auto_opts
+
+%%
+
+list_of_hosts :
+ /* empty */
+ { $$ = new_que(); }
+
+ | list_of_hosts host
+ { if ($2) ins_que((qelem *) $2, list_of_hosts->q_back);
+ $$ = $1; }
+
+ | list_of_hosts automount
+ { if ($2) ins_que((qelem *) $2, list_of_automounts->q_back);
+ $$ = $1; }
+ ;
+
+/*
+ * A new host:
+ *
+ * host foo.domain
+ */
+host :
+ tHOST host_attr list_of_filesystems list_of_mounts
+ { $$ = $2; $$->h_disk_fs = $3; $$->h_mount = $4; }
+
+ | error tHOST host_attr list_of_filesystems list_of_mounts
+ { $$ = $3; $$->h_disk_fs = $4; $$->h_mount = $5; }
+
+ ;
+
+host_attr :
+ tSTR
+ { $$ = new_host(); set_host($$, HF_HOST, $1); }
+
+ | '{' host_attr_list '}' tSTR
+ { $$ = $2; set_host($$, HF_HOST, $4); }
+
+ ;
+
+host_attr_list :
+ /* empty */
+ { $$ = new_host(); }
+
+ | host_attr_list tNETIF tSTR '{' ether_attr '}'
+ { if ($5) {
+ $5->e_if = $3;
+ $$ = $1; set_host($$, HF_ETHER, $5); }
+ }
+
+ | host_attr_list tCONFIG tSTR
+ { $$ = $1; set_host($$, HF_CONFIG, $3); }
+
+ | host_attr_list tARCH '=' tSTR
+ { $$ = $1; set_host($$, HF_ARCH, $4); }
+
+ | host_attr_list tOS '=' tSTR
+ { $$ = $1; set_host($$, HF_OS, $4); }
+
+ | host_attr_list tCLUSTER '=' tSTR
+ { $$ = $1; set_host($$, HF_CLUSTER, $4); }
+
+ | host_attr_list error '=' tSTR
+ { yyerror("unknown host attribute"); }
+ ;
+
+ether_attr :
+ /* empty */
+ { $$ = new_ether_if(); }
+
+ | ether_attr tINADDR '=' tSTR
+ { $$ = $1; set_ether_if($$, EF_INADDR, $4); }
+ | ether_attr tNETMASK '=' tSTR
+ { $$ = $1; set_ether_if($$, EF_NETMASK, $4); }
+ | ether_attr tHWADDR '=' tSTR
+ { $$ = $1; set_ether_if($$, EF_HWADDR, $4); }
+ ;
+
+/*
+ * A new automount tree:
+ *
+ * automount /mountpoint { ... }
+ */
+automount :
+ tAUTOMOUNT opt_auto_opts automount_tree
+ { if ($3) {
+ $$ = new_auto_tree($2, $3);
+ } else {
+ $$ = 0;
+ }
+ }
+
+ | tAUTOMOUNT error
+ { $$ = 0; }
+ ;
+
+opt_auto_opts :
+ /* empty */
+ { $$ = strdup(""); }
+
+ | tOPTS tSTR
+ { $$ = $2; }
+ ;
+
+list_of_filesystems :
+ /* empty */
+ { $$ = 0; }
+
+ | list_of_filesystems filesystem
+ { if ($2) {
+ if ($1)
+ $$ = $1;
+ else
+ $$ = new_que();
+ ins_que(&$2->d_q, $$->q_back);
+ } else {
+ $$ = $1;
+ }
+ }
+ ;
+
+/*
+ * A new filesystem:
+ *
+ * fs /dev/whatever { ... }
+ */
+filesystem :
+ tFS tSTR '{' fs_info_list '}'
+ { $4->d_dev = $2; $$ = $4; }
+
+ | tFS error '}'
+ { $$ = (disk_fs *) 0; }
+ ;
+
+/*
+ * Per-filesystem information:
+ *
+ * fstype - the type of the filesystem (4.2, nfs, swap, export)
+ * opts - the mount options ("rw,grpid")
+ * passno - fsck pass number
+ * freq - dump frequency
+ * dumpset - tape set for filesystem dumps
+ * mount - where to mount this filesystem
+ * log - log device
+ */
+fs_info_list :
+ /* empty */
+ { $$ = new_disk_fs(); }
+
+ | fs_info_list tFSTYPE '=' tSTR
+ { $$ = $1; set_disk_fs($$, DF_FSTYPE, $4); }
+
+ | fs_info_list tOPTS '=' tSTR
+ { $$ = $1; set_disk_fs($$, DF_OPTS, $4); }
+
+ | fs_info_list tPASSNO '=' tSTR
+ { $$ = $1; set_disk_fs($$, DF_PASSNO, $4); }
+
+ | fs_info_list tFREQ '=' tSTR
+ { $$ = $1; set_disk_fs($$, DF_FREQ, $4); }
+
+ | fs_info_list tMOUNT dir_tree
+ { $$ = $1; set_disk_fs($$, DF_MOUNT, (char *) $3); }
+
+ | fs_info_list tDUMPSET '=' tSTR
+ { $$ = $1; set_disk_fs($$, DF_DUMPSET, $4); }
+
+ | fs_info_list tLOG '=' tSTR
+ { $$ = $1; set_disk_fs($$, DF_LOG, $4); }
+
+ | fs_info_list error '=' tSTR
+ { yyerror("unknown filesystem attribute"); }
+ ;
+
+/*
+ * An automount tree:
+ *
+ * name = "volname" name is a reference to volname
+ * name -> "string" name is a link to "string"
+ * name { ... } name is an automount tree
+ */
+automount_tree :
+ /* empty */
+ { $$ = 0; }
+
+ | automount_tree tSTR '=' tSTR
+ { automount *a = new_automount($2);
+ a->a_volname = $4;
+ if ($1)
+ $$ = $1;
+ else
+ $$ = new_que();
+ ins_que(&a->a_q, $$->q_back);
+ }
+
+ | automount_tree tSTR tEQ tSTR
+ { automount *a = new_automount($2);
+ a->a_symlink = $4;
+ if ($1)
+ $$ = $1;
+ else
+ $$ = new_que();
+ ins_que(&a->a_q, $$->q_back);
+ }
+
+ | automount_tree tSTR '{' automount_tree '}'
+ { automount *a = new_automount($2);
+ a->a_mount = $4;
+ if ($1)
+ $$ = $1;
+ else
+ $$ = new_que();
+ ins_que(&a->a_q, $$->q_back);
+ }
+ ;
+
+dir_tree :
+ /* empty */
+ { $$ = 0; }
+
+ | dir_tree tSTR '{' dir_tree_info dir_tree '}'
+ { $4->m_mount = $5;
+ $4->m_name = $2;
+ if ($2[0] != '/' && $2[1] && strchr($2+1, '/'))
+ yyerror("not allowed '/' in a directory name");
+ if ($1)
+ $$ = $1;
+ else
+ $$ = new_que();
+ ins_que(&$4->m_q, $$->q_back);
+ }
+ ;
+
+dir_tree_info :
+ /* empty */
+ { $$ = new_mount(); }
+
+ | dir_tree_info tEXPORTFS tSTR
+ { $$ = $1; set_mount($$, DM_EXPORTFS, $3); }
+
+ | dir_tree_info tVOLNAME tSTR
+ { $$ = $1; set_mount($$, DM_VOLNAME, $3); }
+
+ | dir_tree_info tSEL tSTR
+ { $$ = $1; set_mount($$, DM_SEL, $3); }
+
+ | dir_tree_info error '=' tSTR
+ { yyerror("unknown directory attribute"); }
+ ;
+
+/*
+ * Additional mounts on a host
+ *
+ * mount "volname" ...
+ */
+list_of_mounts :
+ /* empty */
+ { $$ = 0; }
+
+ | list_of_mounts tMOUNT tSTR localinfo_list
+ { set_fsmount($4, FM_VOLNAME, $3);
+ if ($1)
+ $$ = $1;
+ else
+ $$ = new_que();
+ ins_que(&$4->f_q, $$->q_back);
+ }
+ ;
+
+/*
+ * Mount info:
+ *
+ * from "hostname" - obtain the object from the named host
+ * as "string" - where to mount, if different from the volname
+ * opts "string" - mount options
+ * fstype "type" - type of filesystem mount, if not nfs
+ */
+localinfo_list :
+ /* empty */
+ { $$ = new_fsmount(); }
+
+ | localinfo_list tAS tSTR
+ { $$ = $1; set_fsmount($$, FM_LOCALNAME, $3); }
+
+ | localinfo_list tFROM tSTR
+ { $$ = $1; set_fsmount($$, FM_FROM, $3); }
+
+ | localinfo_list tFSTYPE tSTR
+ { $$ = $1; set_fsmount($$, FM_FSTYPE, $3); }
+
+ | localinfo_list tOPTS tSTR
+ { $$ = $1; set_fsmount($$, FM_OPTS, $3); }
+
+ | localinfo_list error '=' tSTR
+ { yyerror("unknown mount attribute"); }
+ ;
diff --git a/usr.sbin/amd/fsinfo/fsi_lex.l b/usr.sbin/amd/fsinfo/fsi_lex.l
new file mode 100644
index 0000000..36de3e2
--- /dev/null
+++ b/usr.sbin/amd/fsinfo/fsi_lex.l
@@ -0,0 +1,403 @@
+%{
+/*
+ * Copyright (c) 1989 Jan-Simon Pendry
+ * Copyright (c) 1989 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)fsi_lex.l 8.2 (Berkeley) 2/17/94
+ *
+ * $Id$
+ *
+ */
+
+/*
+ * Lexical analyzer for fsinfo.
+ * TODO: Needs rewriting.
+ */
+
+static int xinput();
+static void xunput();
+
+#ifdef FLEX_SCANNER
+static int yylineno;
+/* Flex support with help from Vern Paxson <vern@helios.ee.lbl.gov> */
+#undef YY_INPUT
+#define YY_INPUT(buf,result,max_size) \
+{ \
+ int i; \
+ for (i = 0; i < max_size; i++) { \
+ int ch = xinput(i == 0); \
+ if (ch == 0) \
+ break; \
+ buf[i] = ch; \
+ } \
+ result = i; \
+}
+
+#define INIT_STATE { \
+ switch ((yy_start - 1) / 2) { \
+ case 0: \
+ BEGIN F; \
+ break; \
+ } \
+}
+
+
+#else
+/*
+ * Using old lex...
+ */
+#undef unput
+#define unput(ch) xunput(ch)
+#undef input
+#define input() xinput(1)
+
+#define INIT_STATE { \
+ switch (yybgin - yysvec - 1) { \
+ case 0: \
+ BEGIN F; \
+ break; \
+ } \
+}
+
+#endif /* FLEX_SCANNER */
+
+#include "../fsinfo/fsinfo.h"
+#include "fsi_gram.h"
+#include <ctype.h>
+
+static char *filename;
+static char *optr;
+static char ostr[1024];
+static find_resword();
+static unsigned char ibuf[64];
+static unsigned char *iptr = ibuf;
+static int quoted;
+static int lastch, nextch = '\n';
+YYSTYPE yylval;
+
+struct r {
+ char *rw;
+ int tok;
+} rr[] = {
+ { "->", tEQ },
+ { "arch", tARCH },
+ { "as", tAS },
+ { "automount", tAUTOMOUNT },
+ { "cluster", tCLUSTER },
+ { "config", tCONFIG },
+ { "dumpset", tDUMPSET },
+ { "exportfs", tEXPORTFS },
+ { "freq", tFREQ },
+ { "from", tFROM },
+ { "fs", tFS },
+ { "fstype", tFSTYPE },
+ { "host", tHOST },
+ { "hwaddr", tHWADDR },
+ { "inaddr", tINADDR },
+ { "localhost", tLOCALHOST },
+ { "log", tLOG },
+ { "mount", tMOUNT },
+ { "netif", tNETIF },
+ { "netmask", tNETMASK },
+ { "opts", tOPTS },
+ { "os", tOS },
+ { "passno", tPASSNO },
+ { "sel", tSEL },
+ { "volname", tVOLNAME },
+ { 0, 0 },
+};
+#define NRES_WORDS (sizeof(rr)/sizeof(rr[0])-1)
+
+%}
+
+%start F Q
+
+%%
+ INIT_STATE; /* witchcraft */
+
+<F>[^ \t\n"={}]+ { return find_resword(yytext); }
+<F>[ \t] ;
+<F>"\n" { yylineno++; }
+<F>[={}] { return *yytext; }
+
+<F>\" { BEGIN Q; optr = ostr; quoted = 1; }
+<Q>\n { yylineno++; yyerror("\" expected"); BEGIN F; }
+<Q>\\b { *optr++ = '\b'; /* escape */ }
+<Q>\\t { *optr++ = '\t'; /* escape */ }
+<Q>\\\" { *optr++ = '\"'; /* escape */ }
+<Q>\\\\ { *optr++ = '\\'; /* escape */ }
+<Q>\\\n { yylineno++; /* continue */ }
+<Q>\\r { *optr++ = '\r'; /* escape */ }
+<Q>\\n { *optr++ = '\n'; /* escape */ }
+<Q>\\f { *optr++ = '\f'; /* escape */ }
+<Q>"\\ " { *optr++ = ' '; /* force space */ }
+<Q>\\. { yyerror("Unknown \\ sequence"); }
+<Q>([ \t]|"\\\n"){2,} { char *p = yytext-1; while (p = strchr(p+1, '\n')) yylineno++; }
+<Q>\" { BEGIN F; quoted = 0;
+ *optr = '\0';
+ yylval.s = strdup(ostr);
+ return tSTR;
+ }
+<Q>. { *optr++ = *yytext; }
+
+%%
+
+static int find_resword(s)
+char *s;
+{
+ int tok = 0;
+
+ int l = 0, m = NRES_WORDS/2, h = NRES_WORDS-1;
+ int rc = 0;
+
+ m = NRES_WORDS/2;
+
+#define FSTRCMP(p, q) ((*(p) == *(q)) ? strcmp((p)+1, (q)+1) : *(p) - *(q))
+
+ while ((l <= h) && (rc = FSTRCMP(s, rr[m].rw))) {
+ /*fprintf(stderr, "failed to cmp(%s, %s), %d, %d, %d\n", s, rr[m].rw, l, m, h);*/
+ if (rc < 0)
+ h = m - 1;
+ else
+ l = m + 1;
+ m = (h + l) / 2;
+ }
+
+ if (rc == 0)
+ tok = rr[m].tok;
+
+ switch (tok) {
+ case tLOCALHOST:
+ s = "${host}";
+ /* fall through... */
+ case 0:
+ yylval.s = strdup(s);
+ tok = tSTR;
+ /* fall through... */
+ default:
+ return tok;
+ }
+
+}
+
+int yyerror(s, s1, s2, s3, s4)
+char *s;
+char *s1, *s2, *s3, *s4;
+{
+ col_cleanup(0);
+ fprintf(stderr, "%s:%d: ", filename ? filename : "/dev/stdin", yylineno);
+ fprintf(stderr, s, s1, s2, s3, s4);
+ fputc('\n', stderr);
+ parse_errors++;
+}
+
+ioloc *current_location()
+{
+ ioloc *ip = ALLOC(ioloc);
+ ip->i_line = yylineno;
+ ip->i_file = filename;
+ return ip;
+}
+
+#ifdef FLEX_SCANNER
+#undef yywrap
+#endif
+
+int yywrap()
+{
+static int first = 1;
+ if (first) {
+ char prog[16*1024];
+ strcpy(prog, "for file in ");
+ while (*++g_argv) {
+ if (access(*g_argv, 4) < 0) {
+ error("\"%s\": Cannot open for reading", *g_argv);
+ file_io_errors++;
+ } else {
+ strcat(prog, *g_argv);
+ strcat(prog, " ");
+ }
+ }
+ strcat(prog, "; do /usr/bin/cpp ");
+ strcat(prog, idvbuf);
+ strcat(prog, " -DHOSTNAME=\'");
+ strcat(prog, hostname);
+ strcat(prog, "\' \"$file\"; done");
+ yyin = popen(prog, "r");
+ if (yyin) {
+ /*if (filename) free(filename);*/
+ filename = strdup("unknown");
+ yylineno = 1;
+ first = 0;
+ return 0;
+ } else {
+ perror(prog);
+ }
+ }
+
+ if (!first && yyin && pclose(yyin) != 0)
+ parse_errors++;
+
+ return 1;
+}
+
+#define xgetc(fp) ((iptr > ibuf) ? (*--iptr) : (lastch = nextch, nextch = getc(fp), (nextch == EOF ? nextch = lastch, EOF : nextch)))
+
+static int xinput(need)
+int need;
+{
+static int c_comment = 0;
+ int ch, ch2;
+
+ do {
+ ch = xgetc(yyin);
+ /* fprintf(stderr, "ch = %c, %#x, %d\n", ch, ibuf,iptr-ibuf); */
+ if (ch == EOF) return 0;
+ if (quoted)
+ return ch;
+ if (c_comment) {
+ ch2 = ch;
+ do {
+ if (ch2 == '\n') {
+ nextch = '\n';
+ return ch2;
+ }
+ /* C style comment */
+ do {
+ ch2 = getc(yyin);
+ if (ch2 == '\n') {
+ nextch = '\n';
+ return ch2;
+ }
+ } while (ch2 != '*' && ch2 != EOF);
+
+ while (ch2 == '*')
+ ch2 = getc(yyin);
+ } while (ch2 != '/' && ch2 != EOF);
+ c_comment = 0;
+ if (ch2 == EOF)
+ break;
+ continue;
+ }
+
+ if (ch == '#') {
+ /*log("lastch = '%c' (%#x)", lastch, lastch);*/
+ if (lastch == '\n') {
+ char fname[MAXPATHLEN];
+ char *fptr;
+ if (!need) {
+ xunput('#');
+ nextch = '\n';
+ return 0;
+ }
+ fname[0] = '\0';
+ /* Skip past space */
+ do {
+ ch2 = getc(yyin);
+ } while (ch2 != EOF && ch2 != '\n' && !isdigit(ch2));
+ if (isdigit(ch2)) {
+ /* Read in line number */
+ fptr = fname;
+ do {
+ *fptr++ = ch2;
+ ch2 = getc(yyin);
+ } while (isdigit(ch2));
+ *fptr = '\0';
+ if (fptr != fname)
+ yylineno = atoi(fname) - 1;
+ }
+ /* Skip past space */
+ while (ch2 != EOF && ch2 != '\"' && ch2 != '\n')
+ ch2 = getc(yyin);
+ if (ch2 == '\"') {
+ /* Read file name */
+ fptr = fname;
+ ch2 = getc(yyin);
+ while (ch2 != '\"' && ch2 != EOF && ch2 != EOF) {
+ *fptr++ = ch2;
+ ch2 = getc(yyin);
+ }
+ *fptr = '\0';
+ if (fname[0]) {
+ log("Setting filename to \"%s\"", fname);
+ /*if (filename) free(filename);*/
+ filename = strdup(fname);
+ }
+ }
+ while (ch2 != '\n' && ch2 != EOF)
+ ch2 = getc(yyin);
+ } else do {
+ ch2 = getc(yyin);
+ } while (ch2 != '\n' && ch2 != EOF);
+ if (ch2 == '\n') {
+ nextch = '\n';
+ return ch2;
+ }
+ } else if (ch == '/') {
+ ch2 = getc(yyin);
+ if (ch2 == '/') {
+ /* C++ style comment */
+ do {
+ ch2 = getc(yyin);
+ } while (ch2 != '\n' && ch2 != EOF);
+ if (ch2 == '\n') {
+ nextch = '\n';
+ return ch2;
+ }
+ } else if (ch2 == '*') {
+ c_comment = 1;
+ continue;
+ } else {
+ xunput(ch2);
+ return ch;
+ }
+ } else {
+ return ch;
+ }
+ } while (ch2 != EOF);
+ error("End of file within comment");
+ return 0;
+}
+
+static void xunput(c)
+int c;
+{
+ if (c && c != EOF) {
+ if (iptr == ibuf + sizeof(ibuf) - 1)
+ fatal("Out of space in lexical pushback");
+ *iptr++ = c;
+ }
+}
diff --git a/usr.sbin/amd/fsinfo/fsi_util.c b/usr.sbin/amd/fsinfo/fsi_util.c
new file mode 100644
index 0000000..b62b9b9
--- /dev/null
+++ b/usr.sbin/amd/fsinfo/fsi_util.c
@@ -0,0 +1,576 @@
+/*
+ * Copyright (c) 1989 Jan-Simon Pendry
+ * Copyright (c) 1989 Imperial College of Science, Technology & Medicine
+ * 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[] = "@(#)fsi_util.c 8.1 (Berkeley) 6/6/93";
+#endif
+static const char rcsid[] =
+ "$Id$";
+#endif
+
+#include "../fsinfo/fsinfo.h"
+
+/*
+ * Lots of ways of reporting errors...
+ */
+void error(s, s1, s2, s3, s4)
+char *s, *s1, *s2, *s3, *s4;
+{
+ col_cleanup(0);
+ fprintf(stderr, "fsinfo: error, ");
+ fprintf(stderr, s, s1, s2, s3, s4);
+ fputc('\n', stderr);
+ errors++;
+}
+
+void lerror(l, s, s1, s2, s3, s4)
+ioloc *l;
+char *s, *s1, *s2, *s3, *s4;
+{
+ col_cleanup(0);
+ fprintf(stderr, "%s:%d: ", l->i_file, l->i_line);
+ fprintf(stderr, s, s1, s2, s3, s4);
+ fputc('\n', stderr);
+ errors++;
+}
+
+void lwarning(l, s, s1, s2, s3, s4)
+ioloc *l;
+char *s, *s1, *s2, *s3, *s4;
+{
+ col_cleanup(0);
+ fprintf(stderr, "%s:%d: ", l->i_file, l->i_line);
+ fprintf(stderr, s, s1, s2, s3, s4);
+ fputc('\n', stderr);
+
+}
+
+void fatal(s, s1, s2, s3, s4)
+char *s, *s1, *s2, *s3, *s4;
+{
+ col_cleanup(1);
+ fprintf(stderr, "fsinfo: fatal, ");
+ fprintf(stderr, s, s1, s2, s3, s4);
+ fputc('\n', stderr);
+ exit(1);
+}
+
+/*
+ * Dup a string
+ */
+char *strdup(s)
+char *s;
+{
+ int len = strlen(s);
+ char *sp = (char *) xmalloc(len+1);
+
+ bcopy(s, sp, len);
+ sp[len] = 0;
+
+ return sp;
+}
+
+/*
+ * Debug log
+ */
+void log(s, s1, s2, s3, s4)
+char *s, *s1, *s2, *s3, *s4;
+{
+ if (verbose > 0) {
+ fputc('#', stdout);
+ fprintf(stdout, "fsinfo: ");
+ fprintf(stdout, s, s1, s2, s3, s4);
+ putc('\n', stdout);
+ }
+}
+
+void info_hdr(ef, info)
+FILE *ef;
+char *info;
+{
+ fprintf(ef, "# *** NOTE: This file contains %s info\n", info);
+}
+
+void gen_hdr(ef, hn)
+FILE *ef;
+char *hn;
+{
+ fprintf(ef, "# *** NOTE: Only for use on %s\n", hn);
+}
+
+static void make_banner(fp)
+FILE *fp;
+{
+ time_t t = time((time_t*) 0);
+ char *ctime(), *cp = ctime(&t);
+
+ fprintf(fp,
+"\
+# *** This file was automatically generated -- DO NOT EDIT HERE ***\n\
+# \"fsinfo\" run by %s@%s on %s\
+#\n\
+",
+ username, hostname, cp);
+}
+
+static int show_range = 10;
+static int col = 0;
+static int total_shown = 0;
+static int total_mmm = 8;
+
+static int col_output(len)
+int len;
+{
+ int wrapped = 0;
+ col += len;
+ if (col > 77) {
+ fputc('\n', stdout);
+ col = len;
+ wrapped = 1;
+ }
+ return wrapped;
+}
+
+static void show_total()
+{
+ if (total_mmm != -show_range+1) {
+ char n[8];
+ int len;
+ if (total_mmm < 0)
+ fputc('*', stdout);
+ sprintf(n, "%d", total_shown);
+ len = strlen(n);
+ if (col_output(len))
+ fputc(' ', stdout);
+ fputs(n, stdout); fflush(stdout);
+ total_mmm = -show_range;
+ }
+}
+
+col_cleanup(eoj)
+int eoj;
+{
+ if (verbose < 0) return;
+ if (eoj) {
+ show_total();
+ fputs(")]", stdout);
+ }
+ if (col) {
+ fputc('\n', stdout);
+ col = 0;
+ }
+}
+
+void show_new(msg)
+char *msg;
+{
+ if (verbose < 0) return;
+ total_shown++;
+ if (total_mmm > show_range) {
+ show_total();
+ } else if (total_mmm == 0) {
+ fputc('*', stdout); fflush(stdout);
+ col += 1;
+ }
+ total_mmm++;
+}
+
+void show_area_being_processed(area, n)
+char *area;
+int n;
+{
+static char *last_area = 0;
+ if (verbose < 0) return;
+ if (last_area) {
+ if (total_shown)
+ show_total();
+ fputs(")", stdout);
+ col += 1;
+ }
+ if (!last_area || strcmp(area, last_area) != 0) {
+ if (last_area) {
+ col_cleanup(0);
+ total_shown = 0;
+ total_mmm = show_range+1;
+ }
+ (void) col_output(strlen(area)+2);
+ fprintf(stdout, "[%s", area);
+ last_area = area;
+ }
+
+ fputs(" (", stdout);
+ col += 2;
+ show_range = n;
+ total_mmm = n + 1;
+
+ fflush(stdout);
+}
+
+/*
+ * Open a file with the given prefix and name
+ */
+FILE *pref_open(pref, hn, hdr, arg)
+char *pref;
+char *hn;
+void (*hdr)();
+char *arg;
+{
+ char p[MAXPATHLEN];
+ FILE *ef;
+ sprintf(p, "%s%s", pref, hn);
+ log("Writing %s info for %s to %s", pref, hn, p);
+ ef = fopen(p, "w");
+ if (ef) {
+ (*hdr)(ef, arg);
+ make_banner(ef, hn);
+ } else {
+ error("can't open %s for writing", p);
+ }
+
+ return ef;
+}
+
+int pref_close(fp)
+FILE *fp;
+{
+ return fclose(fp) == 0;
+}
+
+/*
+ * Determine where Amd would automount the host/volname pair
+ */
+void compute_automount_point(buf, hp, vn)
+char *buf;
+host *hp;
+char *vn;
+{
+#ifdef AMD_USES_HOSTPATH
+ sprintf(buf, "%s/%s%s", autodir, hp->h_hostpath, vn);
+#else
+ sprintf(buf, "%s/%s%s", autodir, hp->h_lochost, vn);
+#endif
+}
+
+char *xcalloc(i, s)
+int i;
+int s;
+{
+ char *p = (char *) calloc(i, (unsigned) s);
+ if (!p)
+ fatal("Out of memory");
+ return p;
+}
+
+char *xmalloc(i)
+int i;
+{
+ char *p = (char *) malloc(i);
+ if (!p)
+ fatal("Out of memory");
+ return p;
+}
+
+/*
+ * Data constructors..
+ */
+
+automount *new_automount(name)
+char *name;
+{
+ automount *ap = ALLOC(automount);
+ ap->a_ioloc = current_location();
+ ap->a_name = name;
+ ap->a_volname = 0;
+ ap->a_mount = 0;
+ show_new("automount");
+ return ap;
+}
+
+auto_tree *new_auto_tree(def, ap)
+char *def;
+qelem *ap;
+{
+ auto_tree *tp = ALLOC(auto_tree);
+ tp->t_ioloc = current_location();
+ tp->t_defaults = def;
+ tp->t_mount = ap;
+ show_new("auto_tree");
+ return tp;
+}
+
+host *new_host()
+{
+ host *hp = ALLOC(host);
+ hp->h_ioloc = current_location();
+ hp->h_mask = 0;
+ show_new("host");
+ return hp;
+}
+
+void set_host(hp, k, v)
+host *hp;
+int k;
+char *v;
+{
+ int m = 1 << k;
+ if (hp->h_mask & m) {
+ yyerror("host field \"%s\" already set", host_strings[k]);
+ return;
+ }
+
+ hp->h_mask |= m;
+
+ switch (k) {
+ case HF_HOST: {
+ char *p = strdup(v);
+ dict_ent *de = dict_locate(dict_of_hosts, v);
+ if (de)
+ yyerror("duplicate host %s!", v);
+ else
+ dict_add(dict_of_hosts, v, (char *) hp);
+ hp->h_hostname = v;
+ domain_strip(p, hostname);
+ if (strchr(p, '.') != 0)
+ free(p);
+ else
+ hp->h_lochost = p;
+ } break;
+ case HF_CONFIG: {
+ qelem *q;
+ qelem *vq = (qelem *) v;
+ hp->h_mask &= ~m;
+ if (hp->h_config)
+ q = hp->h_config;
+ else
+ q = hp->h_config = new_que();
+ ins_que(vq, q->q_back);
+ } break;
+ case HF_ETHER: {
+ qelem *q;
+ qelem *vq = (qelem *) v;
+ hp->h_mask &= ~m;
+ if (hp->h_ether)
+ q = hp->h_ether;
+ else
+ q = hp->h_ether = new_que();
+ ins_que(vq, q->q_back);
+ } break;
+ case HF_ARCH: hp->h_arch = v; break;
+ case HF_OS: hp->h_os = v; break;
+ case HF_CLUSTER: hp->h_cluster = v; break;
+ default: abort(); break;
+ }
+}
+
+ether_if *new_ether_if()
+{
+ ether_if *ep = ALLOC(ether_if);
+ ep->e_mask = 0;
+ ep->e_ioloc = current_location();
+ show_new("ether_if");
+ return ep;
+}
+
+void set_ether_if(ep,k, v)
+ether_if *ep;
+int k;
+char *v;
+{
+ int m = 1 << k;
+ if (ep->e_mask & m) {
+ yyerror("netif field \"%s\" already set", ether_if_strings[k]);
+ return;
+ }
+
+ ep->e_mask |= m;
+
+ switch (k) {
+ case EF_INADDR: {
+ extern u_long inet_addr();
+ ep->e_inaddr.s_addr = inet_addr(v);
+ if (ep->e_inaddr.s_addr == (u_long) -1)
+ yyerror("malformed IP dotted quad: %s", v);
+ free(v);
+ } break;
+ case EF_NETMASK: {
+ u_long nm = 0;
+ if ((sscanf(v, "0x%lx", &nm) == 1 || sscanf(v, "%lx", &nm) == 1) && nm != 0)
+ ep->e_netmask = htonl(nm);
+ else
+ yyerror("malformed netmask: %s", v);
+ free(v);
+ } break;
+ case EF_HWADDR:
+ ep->e_hwaddr = v;
+ break;
+ default: abort(); break;
+ }
+}
+
+void set_disk_fs(dp, k, v)
+disk_fs *dp;
+int k;
+char *v;
+{
+ int m = 1 << k;
+ if (dp->d_mask & m) {
+ yyerror("fs field \"%s\" already set", disk_fs_strings[k]);
+ return;
+ }
+
+ dp->d_mask |= m;
+
+ switch (k) {
+ case DF_FSTYPE: dp->d_fstype = v; break;
+ case DF_OPTS: dp->d_opts = v; break;
+ case DF_DUMPSET: dp->d_dumpset = v; break;
+ case DF_LOG: dp->d_log = v; break;
+ case DF_PASSNO: dp->d_passno = atoi(v); free(v); break;
+ case DF_FREQ: dp->d_freq = atoi(v); free(v); break;
+ case DF_MOUNT: dp->d_mount = &((mount *) v)->m_q; break;
+ default: abort(); break;
+ }
+}
+
+disk_fs *new_disk_fs()
+{
+ disk_fs *dp = ALLOC(disk_fs);
+ dp->d_ioloc = current_location();
+ show_new("disk_fs");
+ return dp;
+}
+
+void set_mount(mp, k, v)
+mount *mp;
+int k;
+char *v;
+{
+ int m = 1 << k;
+ if (mp->m_mask & m) {
+ yyerror("mount tree field \"%s\" already set", mount_strings[k]);
+ return;
+ }
+
+ mp->m_mask |= m;
+
+ switch (k) {
+ case DM_VOLNAME:
+ dict_add(dict_of_volnames, v, (char *) mp);
+ mp->m_volname = v;
+ break;
+ case DM_EXPORTFS:
+ mp->m_exportfs = v;
+ break;
+ case DM_SEL:
+ mp->m_sel = v;
+ break;
+ default: abort(); break;
+ }
+}
+
+mount *new_mount()
+{
+ mount *fp = ALLOC(mount);
+ fp->m_ioloc = current_location();
+ show_new("mount");
+ return fp;
+}
+
+void set_fsmount(fp, k, v)
+fsmount *fp;
+int k;
+char *v;
+{
+ int m = 1 << k;
+ if (fp->f_mask & m) {
+ yyerror("mount field \"%s\" already set", fsmount_strings[k]);
+ return;
+ }
+
+ fp->f_mask |= m;
+
+ switch (k) {
+ case FM_LOCALNAME: fp->f_localname = v; break;
+ case FM_VOLNAME: fp->f_volname = v; break;
+ case FM_FSTYPE: fp->f_fstype = v; break;
+ case FM_OPTS: fp->f_opts = v; break;
+ case FM_FROM: fp->f_from = v; break;
+ default: abort(); break;
+ }
+}
+
+fsmount *new_fsmount()
+{
+ fsmount *fp = ALLOC(fsmount);
+ fp->f_ioloc = current_location();
+ show_new("fsmount");
+ return fp;
+}
+
+void init_que(q)
+qelem *q;
+{
+ q->q_forw = q->q_back = q;
+}
+
+qelem *new_que()
+{
+ qelem *q = ALLOC(qelem);
+ init_que(q);
+ return q;
+}
+
+void ins_que(elem, pred)
+qelem *elem, *pred;
+{
+ qelem *p;
+ p = pred->q_forw;
+ elem->q_back = pred;
+ elem->q_forw = p;
+ pred->q_forw = elem;
+ p->q_back = elem;
+}
+
+void rem_que(elem)
+qelem *elem;
+{
+ qelem *p, *p2;
+ p = elem->q_forw;
+ p2 = elem->q_back;
+
+ p2->q_forw = p;
+ p->q_back = p2;
+}
diff --git a/usr.sbin/amd/fsinfo/fsinfo.8 b/usr.sbin/amd/fsinfo/fsinfo.8
new file mode 100644
index 0000000..34db7c2
--- /dev/null
+++ b/usr.sbin/amd/fsinfo/fsinfo.8
@@ -0,0 +1,77 @@
+.\" Copyright (c) 1993 Jan-Simon Pendry.
+.\" 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.
+.\"
+.\" @(#)fsinfo.8 8.1 (Berkeley) 6/28/93
+.\"
+.Dd June 28, 1993
+.Dt FSINFO 8
+.Os
+.Sh NAME
+.Nm fsinfo
+.Nd co-ordinate site-wide filesystem information
+.Sh SYNOPSIS
+.Nm \&fsinfo
+.Op Fl v
+.Op Fl a Ar autodir
+.Op Fl b Ar bootparams
+.Op Fl d Ar dumpsets
+.Op Fl e Ar exports
+.Op Fl f Ar fstabs
+.Op Fl h Ar hostname
+.Op Fl m Ar automounts
+.Op Fl I Ar dir
+.Op Fl D Ar string[=string]]
+.Op Fl U Ar string[=string]]
+.Ar config ...
+.Sh DESCRIPTION
+The
+.Nm fsinfo
+utility takes a set of system configuration information, and generates
+a co-ordinated set of
+.Xr amd ,
+.Xr mount
+and
+.Xr mountd
+configuration files.
+.Pp
+The
+.Nm fsinfo
+command is fully described in the document
+.%T "Amd - The 4.4BSD Automounter"
+.Sh "SEE ALSO"
+.Xr amd 8 ,
+.Xr mount 8 ,
+.Xr mountd 8 .
+.Sh HISTORY
+The
+.Nm fsinfo
+command first appeared in 4.4BSD.
diff --git a/usr.sbin/amd/fsinfo/fsinfo.c b/usr.sbin/amd/fsinfo/fsinfo.c
new file mode 100644
index 0000000..0fcb5b2
--- /dev/null
+++ b/usr.sbin/amd/fsinfo/fsinfo.c
@@ -0,0 +1,267 @@
+/*
+ * Copyright (c) 1989 Jan-Simon Pendry
+ * Copyright (c) 1989 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 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[] = "@(#)fsinfo.c 8.1 (Berkeley) 6/6/93";
+#endif
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+/*
+ * fsinfo
+ */
+
+#include "../fsinfo/fsinfo.h"
+#include "fsi_gram.h"
+#include <err.h>
+#include <pwd.h>
+#include <unistd.h>
+
+qelem *list_of_hosts;
+qelem *list_of_automounts;
+dict *dict_of_volnames;
+dict *dict_of_hosts;
+char *autodir = "/a";
+char hostname[MAXHOSTNAMELEN+1];
+char *username;
+int file_io_errors;
+int parse_errors;
+int errors;
+int verbose;
+char idvbuf[1024];
+
+char **g_argv;
+
+static void usage __P((void));
+
+/*
+ * Output file prefixes
+ */
+char *exportfs_pref;
+char *fstab_pref;
+char *dumpset_pref;
+char *mount_pref;
+char *bootparams_pref;
+
+/*
+ * Argument cracking...
+ */
+static void get_args(c, v)
+int c;
+char *v[];
+{
+ int ch;
+ int usageflg = 0;
+ char *iptr = idvbuf;
+
+ while ((ch = getopt(c, v, "a:b:d:e:f:h:m:D:U:I:qv")) != -1)
+ switch (ch) {
+ case 'a':
+ autodir = optarg;
+ break;
+ case 'b':
+ if (bootparams_pref)
+ fatal("-b option specified twice");
+ bootparams_pref = optarg;
+ break;
+ case 'd':
+ if (dumpset_pref)
+ fatal("-d option specified twice");
+ dumpset_pref = optarg;
+ break;
+ case 'h':
+ strncpy(hostname, optarg, sizeof(hostname)-1);
+ break;
+ case 'e':
+ if (exportfs_pref)
+ fatal("-e option specified twice");
+ exportfs_pref = optarg;
+ break;
+ case 'f':
+ if (fstab_pref)
+ fatal("-f option specified twice");
+ fstab_pref = optarg;
+ break;
+ case 'm':
+ if (mount_pref)
+ fatal("-m option specified twice");
+ mount_pref = optarg;
+ break;
+ case 'q':
+ verbose = -1;
+ break;
+ case 'v':
+ verbose = 1;
+ break;
+ case 'I': case 'D': case 'U':
+ sprintf(iptr, "-%c%s ", ch, optarg);
+ iptr += strlen(iptr);
+ break;
+ default:
+ usageflg++;
+ break;
+ }
+
+ if (c != optind) {
+ g_argv = v + optind - 1;
+ if (yywrap())
+ fatal("Cannot read any input files");
+ } else {
+ usageflg++;
+ }
+
+ if (usageflg)
+ usage();
+
+ if (g_argv[0])
+ log("g_argv[0] = %s", g_argv[0]);
+ else
+ log("g_argv[0] = (nil)");
+}
+
+static void
+usage()
+{
+ fprintf(stderr, "%s\n%s\n%s\n",
+"usage: fsinfo [-v] [-a autodir] [-h hostname] [-b bootparams] [-d dumpsets]",
+" [-e exports] [-f fstabs] [-m automounts]",
+" [-I dir] [-D|-U string[=string]] config ...");
+ exit(1);
+}
+
+/*
+ * Determine username of caller
+ */
+static char *find_username()
+{
+ extern char *getlogin();
+ extern char *getenv();
+ char *u = getlogin();
+ if (!u) {
+ struct passwd *pw = getpwuid(getuid());
+ if (pw)
+ u = pw->pw_name;
+ }
+ if (!u)
+ u = getenv("USER");
+ if (!u)
+ u = getenv("LOGNAME");
+ if (!u)
+ u = "root";
+
+ return strdup(u);
+}
+
+/*
+ * MAIN
+ */
+main(argc, argv)
+int argc;
+char *argv[];
+{
+ /*
+ * Process arguments
+ */
+ get_args(argc, argv);
+
+ /*
+ * If no hostname given then use the local name
+ */
+ if (!*hostname && gethostname(hostname, sizeof(hostname)) < 0)
+ err(1, "gethostname");
+
+ /*
+ * Get the username
+ */
+ username = find_username();
+
+ /*
+ * New hosts and automounts
+ */
+ list_of_hosts = new_que();
+ list_of_automounts = new_que();
+
+ /*
+ * New dictionaries
+ */
+ dict_of_volnames = new_dict();
+ dict_of_hosts = new_dict();
+
+ /*
+ * Parse input
+ */
+ show_area_being_processed("read config", 11);
+ if (yyparse())
+ errors = 1;
+ errors += file_io_errors + parse_errors;
+
+ if (errors == 0) {
+ /*
+ * Do semantic analysis of input
+ */
+ analyze_hosts(list_of_hosts);
+ analyze_automounts(list_of_automounts);
+ }
+
+ /*
+ * Give up if errors
+ */
+ if (errors == 0) {
+ /*
+ * Output data files
+ */
+
+ write_atab(list_of_automounts);
+ write_bootparams(list_of_hosts);
+ write_dumpset(list_of_hosts);
+ write_exportfs(list_of_hosts);
+ write_fstab(list_of_hosts);
+ }
+
+ col_cleanup(1);
+
+ exit(errors);
+}
diff --git a/usr.sbin/amd/fsinfo/fsinfo.h b/usr.sbin/amd/fsinfo/fsinfo.h
new file mode 100644
index 0000000..e74ea73
--- /dev/null
+++ b/usr.sbin/amd/fsinfo/fsinfo.h
@@ -0,0 +1,159 @@
+/*
+ * Copyright (c) 1989 Jan-Simon Pendry
+ * Copyright (c) 1989 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)fsinfo.h 8.1 (Berkeley) 6/6/93
+ *
+ * $Id: fsinfo.h,v 1.5 1997/02/22 16:03:21 peter Exp $
+ *
+ */
+
+/*
+ * Get this in now so that OS_HDR can use it
+ */
+#ifdef __STDC__
+#define P(x) x
+#define P_void void
+#define Const const
+#else
+#define P(x) ()
+#define P_void /* as nothing */
+#define Const /* as nothing */
+#endif /* __STDC__ */
+
+#ifdef __GNUC__
+#define INLINE /* __inline */
+#else
+#define INLINE
+#endif /* __GNUC__ */
+
+/*
+ * Pick up target dependent definitions
+ */
+#include "os-defaults.h"
+#include OS_HDR
+
+#ifdef VOIDP
+typedef void *voidp;
+#else
+typedef char *voidp;
+#endif /* VOIDP */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/param.h>
+
+/*
+ * Bogosity to deal with ether { ... }
+ */
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <netinet/in.h>
+#include <net/if.h>
+#include <net/ethernet.h>
+
+#include "fsi_data.h"
+
+extern char* strchr P((Const char*, int)); /* C */
+extern char* strrchr P((Const char*, int)); /* C */
+extern char *strdup P((char*)); /* C */
+extern void fatal();
+extern void warning();
+extern void error();
+extern void analyze_automounts P((qelem*));
+extern void analyze_hosts P((qelem*));
+extern void compute_automount_point P((char*, host*, char*));
+extern automount *new_automount P((char*));
+extern auto_tree *new_auto_tree P((char*, qelem*));
+extern host *new_host P((void));
+extern disk_fs *new_disk_fs P((void));
+extern void set_disk_fs P((disk_fs*, int, char*));
+extern ether_if *new_ether_if P((void));
+extern mount *new_mount P((void));
+extern void set_mount P((mount*, int, char*));
+extern fsmount *new_fsmount P((void));
+extern void set_fsmount P((fsmount*, int, char*));
+extern qelem *new_que P((void));
+extern void init_que P((qelem*));
+extern void ins_que P((qelem*, qelem*));
+extern void rem_que P((qelem*));
+extern dict *new_dict P((void));
+extern dict_ent *dict_locate P((dict*, char*));
+extern void dict_add P((dict*, char*, char*));
+extern int dict_iter P((dict*, int (*)()));
+extern void info_hdr();
+extern void gen_hdr();
+extern FILE *pref_open();
+extern int pref_close();
+extern ioloc *current_location();
+
+extern char *disk_fs_strings[];
+extern char *mount_strings[];
+extern char *fsmount_strings[];
+extern char *host_strings[];
+extern char *ether_if_strings[];
+extern char *autodir;
+extern char hostname[];
+extern char *username;
+extern char **g_argv;
+extern char *fstab_pref;
+extern char *exportfs_pref;
+extern char *mount_pref;
+extern char *dumpset_pref;
+extern char *bootparams_pref;
+extern char idvbuf[];
+
+extern int file_io_errors;
+extern int parse_errors;
+extern int errors;
+extern int verbose;
+
+extern dict *dict_of_hosts;
+extern dict *dict_of_volnames;
+
+extern char *xcalloc();
+extern char *xmalloc();
+#define ALLOC(x) ((struct x *) xcalloc(1, sizeof(struct x)))
+#define STREQ(s,t) (*(s) == *(t) && strcmp((s)+1,(t)+1) == 0)
+#define ISSET(m,b) ((m) & (1<<(b)))
+#define BITSET(m,b) ((m) |= (1<<(b)))
+
+#define FIRST(ty, q) ((ty *) ((q)->q_forw))
+#define LAST(ty, q) ((ty *) ((q)->q_back))
+#define NEXT(ty, q) ((ty *) (((qelem *) q)->q_forw))
+#define HEAD(ty, q) ((ty *) q)
+#define ITER(v, ty, q) \
+ for ((v) = FIRST(ty,(q)); (v) != HEAD(ty,(q)); (v) = NEXT(ty,(v)))
diff --git a/usr.sbin/amd/fsinfo/wr_atab.c b/usr.sbin/amd/fsinfo/wr_atab.c
new file mode 100644
index 0000000..33ca64b
--- /dev/null
+++ b/usr.sbin/amd/fsinfo/wr_atab.c
@@ -0,0 +1,284 @@
+/*
+ * Copyright (c) 1989 Jan-Simon Pendry
+ * Copyright (c) 1989 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)wr_atab.c 8.1 (Berkeley) 6/6/93
+ *
+ * $Id$
+ *
+ */
+
+#include "../fsinfo/fsinfo.h"
+
+/*
+ * Write a sequence of automount mount map entries
+ */
+static int write_amount_info(af, ap, sk)
+FILE *af;
+automount *ap;
+int sk;
+{
+ int errors = 0;
+ if (ap->a_mount) {
+ /*
+ * A pseudo-directory.
+ * This can also be a top-level directory, in which
+ * case the type:=auto is not wanted...
+ *
+ * type:=auto;fs:=${map};pref:=whatever/
+ */
+ automount *ap2;
+ if (strlen(ap->a_name) > sk) {
+ fprintf(af, "%s type:=auto;fs:=${map};pref:=%s/\n",
+ ap->a_name + sk, ap->a_name + sk);
+ }
+ ITER(ap2, automount, ap->a_mount)
+ errors += write_amount_info(af, ap2, sk);
+ } else if (ap->a_mounted) {
+ /*
+ * A mounted partition
+ * type:=link [ link entries ] type:=nfs [ nfs entries ]
+ */
+ dict_data *dd;
+ dict_ent *de = ap->a_mounted;
+ int done_type_link = 0;
+ char *key = ap->a_name + sk;
+
+ /*
+ * Output the map key
+ */
+ fputs(key, af);
+
+ /*
+ * First output any Link locations that would not
+ * otherwise be correctly mounted. These refer
+ * to filesystem which are not mounted in the same
+ * place which the automounter would use.
+ */
+ ITER(dd, dict_data, &de->de_q) {
+ mount *mp = (mount *) dd->dd_data;
+ /*
+ * If the mount point and the exported volname are the
+ * same then this filesystem will be recognised by
+ * the restart code - so we don't need to put out a
+ * special rule for it.
+ */
+ if (mp->m_dk->d_host->h_lochost) {
+ char amountpt[1024];
+ compute_automount_point(amountpt, mp->m_dk->d_host, mp->m_exported->m_volname);
+ if (strcmp(mp->m_dk->d_mountpt, amountpt) != 0) {
+ /*
+ * ap->a_volname is the name of the aliased volume
+ * mp->m_name is the mount point of the filesystem
+ * mp->m_volname is the volume name of the filesystems
+ */
+
+ /*
+ * Find length of key and volume names
+ */
+ int avlen = strlen(ap->a_volname);
+ int mnlen = strlen(mp->m_volname);
+ /*
+ * Make sure a -type:=link is output once
+ */
+ if (!done_type_link) {
+ done_type_link = 1;
+ fputs(" -type:=link", af);
+ }
+ /*
+ * Output a selector for the hostname,
+ * the device from which to mount and
+ * where to mount. This will correspond
+ * to the values output for the fstab.
+ */
+ if (mp->m_dk->d_host->h_lochost)
+ fprintf(af, " host==%s", mp->m_dk->d_host->h_lochost);
+ else
+ fprintf(af, " hostd==%s", mp->m_dk->d_host->h_hostname);
+ fprintf(af, ";fs:=%s", mp->m_name);
+ /*
+ * ... and a sublink if needed
+ */
+ if (mnlen < avlen) {
+ char *sublink = ap->a_volname + mnlen + 1;
+ fprintf(af, "/%s", sublink);
+ }
+ fputs(" ||", af);
+ }
+ }
+ }
+
+ /*
+ * Next do the NFS locations
+ */
+
+ if (done_type_link)
+ fputs(" -", af);
+
+ ITER(dd, dict_data, &de->de_q) {
+ mount *mp = (mount *) dd->dd_data;
+ int namelen = mp->m_name_len;
+ int exp_namelen = mp->m_exported->m_name_len;
+ int volnlen = strlen(ap->a_volname);
+ int mvolnlen = strlen(mp->m_volname);
+ fputc(' ', af);
+#ifdef notdef
+ fprintf(af, "\\\n /* avolname = %s, mname = %s,\n * mvolname = %s, mexp_name = %s,\n * mexp_volname = %s\n */\\\n",
+ ap->a_volname, mp->m_name, mp->m_volname, mp->m_exported->m_name, mp->m_exported->m_volname);
+#endif
+ /*
+ * Output any selectors
+ */
+ if (mp->m_sel)
+ fprintf(af, "%s;", mp->m_sel);
+ /*
+ * Print host and volname of exported filesystem
+ */
+ fprintf(af, "rhost:=%s",
+ mp->m_dk->d_host->h_lochost ?
+ mp->m_dk->d_host->h_lochost :
+ mp->m_dk->d_host->h_hostname);
+ fprintf(af, ";rfs:=%s", mp->m_exported->m_volname);
+ /*
+ * Now determine whether a sublink is required.
+ */
+ if (exp_namelen < namelen || mvolnlen < volnlen) {
+ char sublink[1024];
+ sublink[0] = '\0';
+ if (exp_namelen < namelen) {
+ strcat(sublink, mp->m_name + exp_namelen + 1);
+ if (mvolnlen < volnlen)
+ strcat(sublink, "/");
+ }
+ if (mvolnlen < volnlen)
+ strcat(sublink, ap->a_volname + mvolnlen + 1);
+
+ fprintf(af, ";sublink:=%s", sublink);
+ }
+ }
+ fputc('\n', af);
+ } else if (ap->a_symlink) {
+ /*
+ * A specific link.
+ *
+ * type:=link;fs:=whatever
+ */
+ fprintf(af, "%s type:=link;fs:=%s\n", ap->a_name + sk, ap->a_symlink);
+ }
+ return errors;
+}
+
+/*
+ * Write a single automount configuration file
+ */
+static int write_amount(q, def)
+qelem *q;
+char *def;
+{
+ automount *ap;
+ int errors = 0;
+ int direct = 0;
+
+ /*
+ * Output all indirect maps
+ */
+ ITER(ap, automount, q) {
+ FILE *af;
+ char *p;
+ /*
+ * If there is no a_mount node then this is really
+ * a direct mount, so just keep a count and continue.
+ * Direct mounts are output into a special file during
+ * the second pass below.
+ */
+ if (!ap->a_mount) {
+ direct++;
+ continue;
+ }
+ p = strrchr(ap->a_name, '/');
+ if (!p) p = ap->a_name;
+ else p++;
+ af = pref_open(mount_pref, p, gen_hdr, ap->a_name);
+ if (af) {
+ show_new(ap->a_name);
+ fputs("/defaults ", af);
+ if (*def)
+ fprintf(af, "%s;", def);
+ fputs("type:=nfs\n", af);
+ errors += write_amount_info(af, ap, strlen(ap->a_name) + 1);
+ errors += pref_close(af);
+ }
+ }
+
+ /*
+ * Output any direct map entries which were found during the
+ * previous pass over the data.
+ */
+ if (direct) {
+ FILE *af = pref_open(mount_pref, "direct.map", info_hdr, "direct mount");
+ if (af) {
+ show_new("direct mounts");
+ fputs("/defaults ", af);
+ if (*def)
+ fprintf(af, "%s;", def);
+ fputs("type:=nfs\n", af);
+ ITER(ap, automount, q)
+ if (!ap->a_mount)
+ errors += write_amount_info(af, ap, 1);
+ errors += pref_close(af);
+ }
+ }
+
+ return errors;
+}
+
+/*
+ * Write all the needed automount configuration files
+ */
+write_atab(q)
+qelem *q;
+{
+ int errors = 0;
+
+ if (mount_pref) {
+ auto_tree *tp;
+ show_area_being_processed("write automount", "");
+ ITER(tp, auto_tree, q)
+ errors += write_amount(tp->t_mount, tp->t_defaults);
+ }
+
+ return errors;
+}
diff --git a/usr.sbin/amd/fsinfo/wr_bparam.c b/usr.sbin/amd/fsinfo/wr_bparam.c
new file mode 100644
index 0000000..83180ac
--- /dev/null
+++ b/usr.sbin/amd/fsinfo/wr_bparam.c
@@ -0,0 +1,101 @@
+/*
+ * Copyright (c) 1989 Jan-Simon Pendry
+ * Copyright (c) 1989 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)wr_bparam.c 8.1 (Berkeley) 6/6/93
+ *
+ * $Id$
+ *
+ */
+
+#include "../fsinfo/fsinfo.h"
+
+/*
+ * Write a host/path in NFS format
+ */
+static int write_nfsname(ef, fp, hn)
+FILE *ef;
+fsmount *fp;
+char *hn;
+{
+ int errors = 0;
+ char *h = strdup(fp->f_ref->m_dk->d_host->h_hostname);
+ domain_strip(h, hn);
+ fprintf(ef, "%s:%s", h, fp->f_volname);
+ free(h);
+ return errors;
+}
+
+/*
+ * Write a bootparams entry for a host
+ */
+static int write_boot_info(ef, hp)
+FILE *ef;
+host *hp;
+{
+ int errors = 0;
+ fprintf(ef, "%s\troot=", hp->h_hostname);
+ errors += write_nfsname(ef, hp->h_netroot, hp->h_hostname);
+ fputs(" swap=", ef);
+ errors += write_nfsname(ef, hp->h_netswap, hp->h_hostname);
+ fputs("\n", ef);
+
+ return 0;
+}
+
+/*
+ * Output a bootparams file
+ */
+int write_bootparams(q)
+qelem *q;
+{
+ int errors = 0;
+
+ if (bootparams_pref) {
+ FILE *ef = pref_open(bootparams_pref, "bootparams", info_hdr, "bootparams");
+ if (ef) {
+ host *hp;
+ ITER(hp, host, q)
+ if (hp->h_netroot && hp->h_netswap)
+ errors += write_boot_info(ef, hp);
+ errors += pref_close(ef);
+ } else {
+ errors++;
+ }
+ }
+
+ return errors;
+}
diff --git a/usr.sbin/amd/fsinfo/wr_dumpset.c b/usr.sbin/amd/fsinfo/wr_dumpset.c
new file mode 100644
index 0000000..e19436d
--- /dev/null
+++ b/usr.sbin/amd/fsinfo/wr_dumpset.c
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 1989 Jan-Simon Pendry
+ * Copyright (c) 1989 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)wr_dumpset.c 8.1 (Berkeley) 6/6/93
+ *
+ * $Id$
+ *
+ */
+
+#include "../fsinfo/fsinfo.h"
+
+static int write_dumpset_info(ef, q)
+FILE *ef;
+qelem *q;
+{
+ int errors = 0;
+ disk_fs *dp;
+
+ ITER(dp, disk_fs, q) {
+ if (dp->d_dumpset) {
+ fprintf(ef, "%s\t%s:%-30s\t# %s\n",
+ dp->d_dumpset,
+ dp->d_host->h_lochost ?
+ dp->d_host->h_lochost :
+ dp->d_host->h_hostname,
+ dp->d_mountpt,
+ dp->d_dev);
+ }
+ }
+ return errors;
+}
+
+int write_dumpset(q)
+qelem *q;
+{
+ int errors = 0;
+
+ if (dumpset_pref) {
+ FILE *ef = pref_open(dumpset_pref, "dumpsets", info_hdr, "exabyte dumpset");
+ if (ef) {
+ host *hp;
+ ITER(hp, host, q) {
+ if (hp->h_disk_fs) {
+ errors += write_dumpset_info(ef, hp->h_disk_fs);
+ }
+ }
+ errors += pref_close(ef);
+ } else {
+ errors++;
+ }
+ }
+
+ return errors;
+}
diff --git a/usr.sbin/amd/fsinfo/wr_exportfs.c b/usr.sbin/amd/fsinfo/wr_exportfs.c
new file mode 100644
index 0000000..338ebfa
--- /dev/null
+++ b/usr.sbin/amd/fsinfo/wr_exportfs.c
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 1989 Jan-Simon Pendry
+ * Copyright (c) 1989 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)wr_exportfs.c 8.1 (Berkeley) 6/6/93
+ *
+ * $Id$
+ *
+ */
+
+#include "../fsinfo/fsinfo.h"
+
+static int write_export_info(ef, q, errors)
+FILE *ef;
+qelem *q;
+int errors;
+{
+ mount *mp;
+
+ ITER(mp, mount, q) {
+ if (mp->m_mask & (1<<DM_EXPORTFS))
+ fprintf(ef, "%s\t%s\n", mp->m_volname, mp->m_exportfs);
+ if (mp->m_mount)
+ errors += write_export_info(ef, mp->m_mount, 0);
+ }
+
+ return errors;
+}
+
+static int write_dkexports(ef, q)
+FILE *ef;
+qelem *q;
+{
+ int errors = 0;
+ disk_fs *dp;
+
+ ITER(dp, disk_fs, q) {
+ if (dp->d_mount)
+ errors += write_export_info(ef, dp->d_mount, 0);
+ }
+ return errors;
+}
+
+int write_exportfs(q)
+qelem *q;
+{
+ int errors = 0;
+
+ if (exportfs_pref) {
+ host *hp;
+ show_area_being_processed("write exportfs", "");
+ ITER(hp, host, q) {
+ if (hp->h_disk_fs) {
+ FILE *ef = pref_open(exportfs_pref, hp->h_hostname, gen_hdr, hp->h_hostname);
+ if (ef) {
+ show_new(hp->h_hostname);
+ errors += write_dkexports(ef, hp->h_disk_fs);
+ errors += pref_close(ef);
+ } else {
+ errors++;
+ }
+ }
+ }
+ }
+
+ return errors;
+}
diff --git a/usr.sbin/amd/fsinfo/wr_fstab.c b/usr.sbin/amd/fsinfo/wr_fstab.c
new file mode 100644
index 0000000..ee805ef
--- /dev/null
+++ b/usr.sbin/amd/fsinfo/wr_fstab.c
@@ -0,0 +1,303 @@
+/*
+ * Copyright (c) 1989 Jan-Simon Pendry
+ * Copyright (c) 1989 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)wr_fstab.c 8.1 (Berkeley) 6/6/93
+ *
+ * $Id$
+ *
+ */
+
+#include "../fsinfo/fsinfo.h"
+
+/* ---------- AIX 1 ------------------------------ */
+
+/*
+ * AIX 1 format
+ */
+static void write_aix1_dkfstab(ef, dp)
+FILE *ef;
+disk_fs *dp;
+{
+ char *hp = strdup(dp->d_host->h_hostname);
+ char *p = strchr(hp, '.');
+ if (p)
+ *p = '\0';
+
+ fprintf(ef, "\n%s:\n\tdev = %s\n\tvfs = %s\n\ttype = %s\n\tlog = %s\n\tvol = %s\n\topts = %s\n\tmount = true\n\tcheck = true\n\tfree = false\n",
+ dp->d_mountpt,
+ dp->d_dev,
+ dp->d_fstype,
+ dp->d_fstype,
+ dp->d_log,
+ dp->d_mountpt,
+ dp->d_opts);
+ free(hp);
+}
+
+static void write_aix1_dkrmount(ef, hn, fp)
+FILE *ef;
+char *hn;
+fsmount *fp;
+{
+ char *h = strdup(fp->f_ref->m_dk->d_host->h_hostname);
+ char *hp = strdup(h);
+ char *p = strchr(hp, '.');
+ if (p)
+ *p = '\0';
+ domain_strip(h, hn);
+ fprintf(ef, "\n%s:\n\tsite = %s\n\tdev = %s:%s\n\tvfs = %s\n\ttype = %s\n\tvol = %s\n\topts = %s\n\tmount = true\n\tcheck = true\n\tfree = false\n",
+ fp->f_localname,
+ hp,
+ h,
+ fp->f_volname,
+ fp->f_fstype,
+ fp->f_fstype,
+ fp->f_localname,
+ fp->f_opts);
+
+ free(hp);
+ free(h);
+}
+
+/* ---------- AIX 3 ------------------------------ */
+
+/*
+ * AIX 3 format
+ */
+static void write_aix3_dkfstab(ef, dp)
+FILE *ef;
+disk_fs *dp;
+{
+ if (strcmp(dp->d_fstype, "jfs") == 0 && strncmp(dp->d_dev, "/dev/", 5) == 0 && !dp->d_log)
+ error("aix 3 needs a log device for journalled filesystem (jfs) mounts");
+
+ fprintf(ef, "\n%s:\n\tdev = %s\n\tvfs = %s\n\ttype = %s\n\tlog = %s\n\tvol = %s\n\topts = %s\n\tmount = true\n\tcheck = true\n\tfree = false\n",
+ dp->d_mountpt,
+ dp->d_dev,
+ dp->d_fstype,
+ dp->d_fstype,
+ dp->d_log,
+ dp->d_mountpt,
+ dp->d_opts);
+}
+
+static void write_aix3_dkrmount(ef, hn, fp)
+FILE *ef;
+char *hn;
+fsmount *fp;
+{
+ char *h = strdup(fp->f_ref->m_dk->d_host->h_hostname);
+ domain_strip(h, hn);
+ fprintf(ef, "\n%s:\n\tdev = %s:%s\n\tvfs = %s\n\ttype = %s\n\tvol = %s\n\topts = %s\n\tmount = true\n\tcheck = true\n\tfree = false\n",
+ fp->f_localname,
+ h,
+ fp->f_volname,
+ fp->f_fstype,
+ fp->f_fstype,
+ fp->f_localname,
+ fp->f_opts);
+
+ free(h);
+}
+
+/* ---------- Ultrix ----------------------------- */
+
+static void write_ultrix_dkfstab(ef, dp)
+FILE *ef;
+disk_fs *dp;
+{
+ fprintf(ef, "%s:%s:%s:%s:%d:%d\n",
+ dp->d_dev,
+ dp->d_mountpt,
+ dp->d_fstype,
+ dp->d_opts,
+ dp->d_freq,
+ dp->d_passno);
+}
+
+static void write_ultrix_dkrmount(ef, hn, fp)
+FILE *ef;
+char *hn;
+fsmount *fp;
+{
+ char *h = strdup(fp->f_ref->m_dk->d_host->h_hostname);
+ domain_strip(h, hn);
+ fprintf(ef, "%s@%s:%s:%s:%s:0:0\n",
+ fp->f_volname,
+ h,
+ fp->f_localname,
+ fp->f_fstype,
+ fp->f_opts);
+ free(h);
+}
+
+/* ---------- Generic ---------------------------- */
+
+/*
+ * Generic (BSD, SunOS, HPUX) format
+ */
+static void write_generic_dkfstab(ef, dp)
+FILE *ef;
+disk_fs *dp;
+{
+ fprintf(ef, "%s %s %s %s %d %d\n",
+ dp->d_dev,
+ dp->d_mountpt,
+ dp->d_fstype,
+ dp->d_opts,
+ dp->d_freq,
+ dp->d_passno);
+}
+
+static void write_generic_dkrmount(ef, hn, fp)
+FILE *ef;
+char *hn;
+fsmount *fp;
+{
+ char *h = strdup(fp->f_ref->m_dk->d_host->h_hostname);
+ domain_strip(h, hn);
+ fprintf(ef, "%s:%s %s %s %s 0 0\n",
+ h,
+ fp->f_volname,
+ fp->f_localname,
+ fp->f_fstype,
+ fp->f_opts);
+ free(h);
+}
+
+/* ----------------------------------------------- */
+
+static struct os_fstab_type {
+ char *os_name;
+ void (*op_fstab)();
+ void (*op_mount)();
+} os_tabs[] = {
+ { "aix1", write_aix1_dkfstab, write_aix1_dkrmount }, /* AIX 1 */
+ { "aix3", write_aix3_dkfstab, write_aix3_dkrmount }, /* AIX 3 */
+ { "generic", write_generic_dkfstab, write_generic_dkrmount }, /* Generic */
+ { "u2_0", write_ultrix_dkfstab, write_ultrix_dkrmount }, /* Ultrix */
+ { "u3_0", write_ultrix_dkfstab, write_ultrix_dkrmount }, /* Ultrix */
+ { "u4_0", write_ultrix_dkfstab, write_ultrix_dkrmount }, /* Ultrix */
+ { 0, 0, 0 }
+};
+
+#define GENERIC_OS_NAME "generic"
+
+static struct os_fstab_type *find_fstab_type(hp)
+host *hp;
+{
+ struct os_fstab_type *op = 0;
+ char *os_name = 0;
+
+again:;
+ if (os_name == 0) {
+ if (ISSET(hp->h_mask, HF_OS))
+ os_name = hp->h_os;
+ else
+ os_name = GENERIC_OS_NAME;
+ }
+
+ for (op = os_tabs; op->os_name; op++)
+ if (strcmp(os_name, op->os_name) == 0)
+ return op;
+
+ os_name = GENERIC_OS_NAME;
+ goto again;
+}
+
+static int write_dkfstab(ef, q, output)
+FILE *ef;
+qelem *q;
+void (*output)();
+{
+ int errors = 0;
+ disk_fs *dp;
+
+ ITER(dp, disk_fs, q)
+ if (strcmp(dp->d_fstype, "export") != 0)
+ (*output)(ef, dp);
+
+ return errors;
+}
+
+static int write_dkrmount(ef, q, hn, output)
+FILE *ef;
+qelem *q;
+char *hn;
+void (*output)();
+{
+ int errors = 0;
+ fsmount *fp;
+
+ ITER(fp, fsmount, q)
+ (*output)(ef, hn, fp);
+
+ return errors;
+}
+
+int write_fstab(q)
+qelem *q;
+{
+ int errors = 0;
+
+ if (fstab_pref) {
+ host *hp;
+ show_area_being_processed("write fstab", 4);
+ ITER(hp, host, q) {
+ if (hp->h_disk_fs || hp->h_mount) {
+ FILE *ef = pref_open(fstab_pref, hp->h_hostname, gen_hdr, hp->h_hostname);
+ if (ef) {
+ struct os_fstab_type *op = find_fstab_type(hp);
+ show_new(hp->h_hostname);
+ if (hp->h_disk_fs)
+ errors += write_dkfstab(ef, hp->h_disk_fs, op->op_fstab);
+ else
+ log("No local disk mounts on %s", hp->h_hostname);
+
+ if (hp->h_mount)
+ errors += write_dkrmount(ef, hp->h_mount, hp->h_hostname, op->op_mount);
+
+ pref_close(ef);
+ }
+ } else {
+ error("no disk mounts on %s", hp->h_hostname);
+ }
+ }
+ }
+
+ return errors;
+}
diff --git a/usr.sbin/amd/include/am.h b/usr.sbin/amd/include/am.h
new file mode 100644
index 0000000..58a1d8e
--- /dev/null
+++ b/usr.sbin/amd/include/am.h
@@ -0,0 +1,567 @@
+/*
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)am.h 5.6 (Berkeley) 6/6/93
+ *
+ * $Id$
+ *
+ */
+
+#include "config.h"
+
+/*
+ * Global declarations
+ */
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <rpc/rpc.h>
+#include "nfs_prot.h"
+#ifdef MNTENT_HDR
+#include MNTENT_HDR
+#endif /* MNTENT_HDR */
+#include <assert.h>
+
+#ifdef DEBUG_MEM
+#include <malloc.h>
+#endif /* DEBUG_MEM */
+
+#ifndef MAXHOSTNAMELEN
+#define MAXHOSTNAMELEN 64
+#endif /* MAXHOSTNAMELEN */
+
+#ifndef MNTTYPE_AUTO
+#define MNTTYPE_AUTO "auto"
+#endif /* MNTTYPE_AUTO */
+
+#ifndef FALSE
+#define FALSE 0
+#define TRUE 1
+#endif /* FALSE */
+
+#ifndef ROOT_MAP
+#define ROOT_MAP "\"root\""
+#endif /* ROOT_MAP */
+
+/*
+ * Flags from command line
+ */
+extern int print_pid; /* Print pid to stdout */
+extern int normalize_hosts; /* Normalize host names before use */
+extern int restart_existing_mounts;
+#ifdef HAS_NIS_MAPS
+extern char *domain; /* NIS domain to use */
+#endif /* HAS_NIS_MAPS */
+extern int am_timeo; /* Cache period */
+extern int afs_timeo; /* AFS timeout */
+extern int afs_retrans; /* AFS retrans */
+extern int am_timeo_w; /* Unmount timeout */
+extern char *mtab; /* Mount table */
+
+typedef enum {
+ Start,
+ Run,
+ Finishing,
+ Quit,
+ Done
+} serv_state;
+
+extern serv_state amd_state; /* Should we go now */
+extern int immediate_abort; /* Should close-down unmounts be retried */
+extern time_t do_mapc_reload; /* Flush & reload mount map cache */
+
+/*
+ * Useful constants
+ */
+extern char pid_fsname[]; /* kiska.southseas.nz:(pid%d) */
+extern char hostd[]; /* "kiska.southseas.nz" */
+extern char *hostdomain; /* "southseas.nz" */
+extern char *op_sys; /* "sos4" */
+extern char *arch; /* "sun4" */
+extern char *karch; /* "sun4c" */
+extern char *cluster; /* "r+d-kluster" */
+extern char *endian; /* "big" */
+extern char *auto_dir; /* "/a" */
+extern char copyright[]; /* Copyright info */
+extern char version[]; /* Version info */
+
+typedef struct am_ops am_ops;
+typedef struct am_node am_node;
+typedef struct am_opts am_opts;
+typedef struct mntfs mntfs;
+typedef struct fserver fserver;
+typedef struct fsrvinfo fsrvinfo;
+
+/*
+ * Debug defns.
+ */
+#ifdef DEBUG
+#define DEBUG_MTAB "./mtab"
+
+extern int debug_flags; /* Debug options */
+
+#define D_DAEMON 0x0001 /* Enter daemon mode */
+#define D_TRACE 0x0002 /* Do protocol trace */
+#define D_FULL 0x0004 /* Do full trace */
+#define D_MTAB 0x0008 /* Use local mtab */
+#define D_AMQ 0x0010 /* Register amq program */
+#define D_STR 0x0020 /* Debug string munging */
+#define D_MEM 0x0040 /* Trace memory allocations */
+
+/*
+ * Normally, don't enter daemon mode, and don't register amq
+ */
+#define D_TEST (~(D_DAEMON|D_MEM|D_STR))
+#endif /* DEBUG */
+
+/*
+ * Global variables.
+ */
+extern unsigned short nfs_port; /* Our NFS service port */
+extern struct in_addr myipaddr; /* (An) IP address of this host */
+
+extern int foreground; /* Foreground process */
+extern time_t next_softclock; /* Time to call softclock() */
+extern int task_notify_todo; /* Task notifier needs running */
+#ifdef HAS_TFS
+extern int nfs_server_code_available;
+#endif /* HAS_TFS */
+extern int last_used_map; /* Last map being used for mounts */
+extern AUTH *nfs_auth; /* Dummy uthorisation for remote servers */
+extern am_node **exported_ap; /* List of nodes */
+extern int first_free_map; /* First free node */
+extern am_node *root_node; /* Node for "root" */
+extern char *wire; /* Name of primary connected network */
+#define NEXP_AP (254)
+#define NEXP_AP_MARGIN (128)
+
+typedef int (*task_fun)P((voidp));
+typedef void (*cb_fun)P((int, int, voidp));
+typedef void (*fwd_fun)P((voidp, int, struct sockaddr_in *,
+ struct sockaddr_in *, voidp, int));
+
+/*
+ * String comparison macros
+ */
+#define STREQ(s1, s2) (strcmp((s1), (s2)) == 0)
+#define FSTREQ(s1, s2) ((*(s1) == *(s2)) && STREQ((s1),(s2)))
+
+/*
+ * Linked list
+ */
+typedef struct qelem qelem;
+struct qelem {
+ qelem *q_forw;
+ qelem *q_back;
+};
+#define FIRST(ty, q) ((ty *) ((q)->q_forw))
+#define LAST(ty, q) ((ty *) ((q)->q_back))
+#define NEXT(ty, q) ((ty *) (((qelem *) q)->q_forw))
+#define PREV(ty, q) ((ty *) (((qelem *) q)->q_back))
+#define HEAD(ty, q) ((ty *) q)
+#define ITER(v, ty, q) \
+ for ((v) = FIRST(ty,(q)); (v) != HEAD(ty,(q)); (v) = NEXT(ty,(v)))
+
+/*
+ * List of mount table entries
+ */
+typedef struct mntlist mntlist;
+struct mntlist {
+ struct mntlist *mnext;
+ struct mntent *mnt;
+};
+
+/*
+ * Mount map
+ */
+typedef struct mnt_map mnt_map;
+
+/*
+ * Global routines
+ */
+extern int atoi P((Const char *)); /* C */
+extern void am_mounted P((am_node*));
+extern void am_unmounted P((am_node*));
+extern int background(P_void);
+extern int bind_resv_port P((int, unsigned short*));
+extern int compute_mount_flags P((struct mntent *));
+extern int softclock(P_void);
+#ifdef DEBUG
+extern int debug_option P((char*));
+#endif /* DEBUG */
+extern void deslashify P((char*));
+/*extern void domain_strip P((char*, char*));*/
+extern mntfs* dup_mntfs P((mntfs*));
+extern fserver* dup_srvr P((fserver*));
+extern int eval_fs_opts P((am_opts*, char*, char*, char*, char*, char*));
+extern char* expand_key P((char*));
+extern am_node* exported_ap_alloc(P_void);
+extern am_node* find_ap P((char*));
+extern am_node* find_mf P((mntfs*));
+extern mntfs* find_mntfs P((am_ops*, am_opts*, char*, char*, char*, char*, char*));
+extern void flush_mntfs(P_void);
+extern void flush_nfs_fhandle_cache P((fserver*));
+extern void forcibly_timeout_mp P((am_node*));
+extern FREE_RETURN_TYPE free P((voidp)); /* C */
+extern void free_mntfs P((mntfs*));
+extern void free_opts P((am_opts*));
+extern void free_map P((am_node*));
+extern void free_mntlist P((mntlist*));
+extern void free_srvr P((fserver*));
+extern int fwd_init(P_void);
+extern int fwd_packet P((int, voidp, int, struct sockaddr_in *,
+ struct sockaddr_in *, voidp, fwd_fun));
+extern void fwd_reply(P_void);
+extern void get_args P((int, char*[]));
+extern char *getwire P((void));
+#ifdef NEED_MNTOPT_PARSER
+extern char *hasmntopt P((struct mntent*, char*));
+#endif /* NEED_MNTOPT_PARSER */
+extern int hasmntval P((struct mntent*, char*));
+extern void host_normalize P((char **));
+extern char *inet_dquad P((char*, unsigned long));
+extern void init_map P((am_node*, char*));
+extern void insert_am P((am_node*, am_node*));
+extern void ins_que P((qelem*, qelem*));
+extern int islocalnet P((unsigned long));
+extern int make_nfs_auth P((void));
+extern void make_root_node(P_void);
+extern int make_rpc_packet P((char*, int, u_long, struct rpc_msg*, voidp, xdrproc_t, AUTH*));
+extern void map_flush_srvr P((fserver*));
+extern void mapc_add_kv P((mnt_map*, char*, char*));
+extern mnt_map* mapc_find P((char*, char*));
+extern void mapc_free P((mnt_map*));
+extern int mapc_keyiter P((mnt_map*, void (*)(char*,voidp), voidp));
+extern int mapc_search P((mnt_map*, char*, char**));
+extern void mapc_reload(P_void);
+extern void mapc_showtypes P((FILE*));
+extern int mkdirs P((char*, int));
+extern void mk_fattr P((am_node*, enum ftype));
+extern void mnt_free P((struct mntent*));
+extern int mount_auto_node P((char*, voidp));
+extern int mount_automounter P((int));
+extern int mount_exported(P_void);
+extern int mount_fs P((struct mntent*, int, caddr_t, int, MTYPE_TYPE));
+/*extern int mount_nfs_fh P((struct fhstatus*, char*, char*, char*, mntfs*));*/
+extern int mount_node P((am_node*));
+extern mntfs* new_mntfs(P_void);
+extern void new_ttl P((am_node*));
+extern am_node* next_map P((int*));
+extern int nfs_srvr_port P((fserver*, u_short*, voidp));
+extern void normalize_slash P((char*));
+extern void ops_showfstypes P((FILE*));
+extern int pickup_rpc_reply P((voidp, int, voidp, xdrproc_t));
+extern mntlist* read_mtab P((char*));
+extern mntfs* realloc_mntfs P((mntfs*, am_ops*, am_opts*, char*, char*, char*, char*, char*));
+extern void rem_que P((qelem*));
+extern void reschedule_timeout_mp(P_void);
+extern void restart(P_void);
+#ifdef UPDATE_MTAB
+extern void rewrite_mtab P((mntlist *));
+#endif /* UPDATE_MTAB */
+extern void rmdirs P((char*));
+extern am_node* root_ap P((char*, int));
+extern int root_keyiter P((void (*)(char*,voidp), voidp));
+extern void root_newmap P((char*, char*, char*));
+extern void rpc_msg_init P((struct rpc_msg*, u_long, u_long, u_long));
+extern void run_task P((task_fun, voidp, cb_fun, voidp));
+extern void sched_task P((cb_fun, voidp, voidp));
+extern void show_rcs_info P((Const char*, char*));
+extern void sigchld P((int));
+extern void srvrlog P((fserver*, char*));
+extern char* str3cat P((char*, char*, char*, char*));
+extern char* strcat P((char*, Const char*)); /* C */
+extern int strcmp P((Const char*, Const char*)); /* C */
+extern char* strdup P((Const char*));
+extern int strlen P((Const char*)); /* C */
+extern char* strnsave P((Const char*, int));
+extern char* strrchr P((Const char*, int)); /* C */
+extern char* strealloc P((char*, char *));
+extern char** strsplit P((char*, int, int));
+extern int switch_option P((char*));
+extern int switch_to_logfile P((char*));
+extern void do_task_notify(P_void);
+extern int timeout P((unsigned int, void (*fn)(), voidp));
+extern void timeout_mp(P_void);
+extern void umount_exported(P_void);
+extern int umount_fs P((char*));
+/*extern int unmount_node P((am_node*));
+extern int unmount_node_wrap P((voidp));*/
+extern void unregister_amq(P_void);
+extern void untimeout P((int));
+extern int valid_key P((char*));
+extern void wakeup P((voidp));
+extern void wakeup_task P((int,int,voidp));
+extern void wakeup_srvr P((fserver*));
+extern void write_mntent P((struct mntent*));
+#ifdef UPDATE_MTAB
+extern void unlock_mntlist P((void));
+#else
+#define unlock_mntlist()
+#endif /* UPDATE_MTAB */
+
+
+#define ALLOC(ty) ((struct ty *) xmalloc(sizeof(struct ty)))
+
+/*
+ * Options
+ */
+struct am_opts {
+ char *fs_glob; /* Smashed copy of global options */
+ char *fs_local; /* Expanded copy of local options */
+ char *fs_mtab; /* Mount table entry */
+ /* Other options ... */
+ char *opt_dev;
+ char *opt_delay;
+ char *opt_dir;
+ char *opt_fs;
+ char *opt_group;
+ char *opt_mount;
+ char *opt_opts;
+ char *opt_remopts;
+ char *opt_pref;
+ char *opt_cache;
+ char *opt_rfs;
+ char *opt_rhost;
+ char *opt_sublink;
+ char *opt_type;
+ char *opt_unmount;
+ char *opt_user;
+};
+
+/*
+ * File Handle
+ *
+ * This is interpreted by indexing the exported array
+ * by fhh_id.
+ *
+ * The whole structure is mapped onto a standard fhandle_t
+ * when transmitted.
+ */
+struct am_fh {
+ int fhh_pid; /* process id */
+ int fhh_id; /* map id */
+ int fhh_gen; /* generation number */
+};
+
+extern am_node *fh_to_mp P((nfs_fh*));
+extern am_node *fh_to_mp3 P((nfs_fh*,int*,int));
+extern void mp_to_fh P((am_node*, nfs_fh*));
+#define fh_to_mp2(fhp, rp) fh_to_mp3(fhp, rp, VLOOK_CREATE)
+extern int auto_fmount P((am_node *mp));
+extern int auto_fumount P((am_node *mp));
+
+#define MAX_READDIR_ENTRIES 16
+
+typedef char* (*vfs_match)P((am_opts*));
+typedef int (*vfs_init)P((mntfs*));
+typedef int (*vmount_fs)P((am_node*));
+typedef int (*vfmount_fs)P((mntfs*));
+typedef int (*vumount_fs)P((am_node*));
+typedef int (*vfumount_fs)P((mntfs*));
+typedef am_node*(*vlookuppn)P((am_node*, char*, int*, int));
+typedef int (*vreaddir)P((am_node*, nfscookie, dirlist*, entry*, int));
+typedef am_node*(*vreadlink)P((am_node*, int*));
+typedef void (*vmounted)P((mntfs*));
+typedef void (*vumounted)P((am_node*));
+typedef fserver*(*vffserver)P((mntfs*));
+
+struct am_ops {
+ char *fs_type;
+ vfs_match fs_match;
+ vfs_init fs_init;
+ vmount_fs mount_fs;
+ vfmount_fs fmount_fs;
+ vumount_fs umount_fs;
+ vfumount_fs fumount_fs;
+ vlookuppn lookuppn;
+ vreaddir readdir;
+ vreadlink readlink;
+ vmounted mounted;
+ vumounted umounted;
+ vffserver ffserver;
+ int fs_flags;
+};
+extern am_node *efs_lookuppn P((am_node*, char*, int*, int));
+extern int efs_readdir P((am_node*, nfscookie, dirlist*, entry*, int));
+
+#define VLOOK_CREATE 0x1
+#define VLOOK_DELETE 0x2
+
+#define FS_DIRECTORY 0x0001 /* This looks like a dir, not a link */
+#define FS_MBACKGROUND 0x0002 /* Should background this mount */
+#define FS_NOTIMEOUT 0x0004 /* Don't bother with timeouts */
+#define FS_MKMNT 0x0008 /* Need to make the mount point */
+#define FS_UBACKGROUND 0x0010 /* Unmount in background */
+#define FS_BACKGROUND (FS_MBACKGROUND|FS_UBACKGROUND)
+#define FS_DISCARD 0x0020 /* Discard immediately on last reference */
+#define FS_AMQINFO 0x0040 /* Amq is interested in this fs type */
+
+#ifdef SUNOS4_COMPAT
+extern am_ops *sunos4_match P((am_opts*, char*, char*, char*, char*, char*));
+#endif /* SUNOS4_COMPAT */
+extern am_ops *ops_match P((am_opts*, char*, char*, char*, char*, char*));
+#include "fstype.h"
+
+/*
+ * Per-mountpoint statistics
+ */
+struct am_stats {
+ time_t s_mtime; /* Mount time */
+ u_short s_uid; /* Uid of mounter */
+ int s_getattr; /* Count of getattrs */
+ int s_lookup; /* Count of lookups */
+ int s_readdir; /* Count of readdirs */
+ int s_readlink; /* Count of readlinks */
+ int s_statfs; /* Count of statfs */
+};
+typedef struct am_stats am_stats;
+
+/*
+ * System statistics
+ */
+struct amd_stats {
+ int d_drops; /* Dropped requests */
+ int d_stale; /* Stale NFS handles */
+ int d_mok; /* Succesful mounts */
+ int d_merr; /* Failed mounts */
+ int d_uerr; /* Failed unmounts */
+};
+extern struct amd_stats amd_stats;
+
+/*
+ * List of fileservers
+ */
+struct fserver {
+ qelem fs_q; /* List of fileservers */
+ int fs_refc; /* Number of references to this node */
+ char *fs_host; /* Normalized hostname of server */
+ struct sockaddr_in *fs_ip; /* Network address of server */
+ int fs_cid; /* Callout id */
+ int fs_pinger; /* Ping (keepalive) interval */
+ int fs_flags; /* Flags */
+ char *fs_type; /* File server type */
+ voidp fs_private; /* Private data */
+ void (*fs_prfree)(); /* Free private data */
+};
+#define FSF_VALID 0x0001 /* Valid information available */
+#define FSF_DOWN 0x0002 /* This fileserver is thought to be down */
+#define FSF_ERROR 0x0004 /* Permanent error has occured */
+#define FSF_WANT 0x0008 /* Want a wakeup call */
+#define FSF_PINGING 0x0010 /* Already doing pings */
+#define FSRV_ISDOWN(fs) (((fs)->fs_flags & (FSF_DOWN|FSF_VALID)) == (FSF_DOWN|FSF_VALID))
+#define FSRV_ISUP(fs) (((fs)->fs_flags & (FSF_DOWN|FSF_VALID)) == (FSF_VALID))
+
+/*
+ * List of mounted filesystems
+ */
+struct mntfs {
+ qelem mf_q; /* List of mounted filesystems */
+ am_ops *mf_ops; /* Operations on this mountpoint */
+ am_opts *mf_fo; /* File opts */
+ char *mf_mount; /* "/a/kiska/home/kiska" */
+ char *mf_info; /* Mount info */
+ char *mf_auto; /* Automount opts */
+ char *mf_mopts; /* FS mount opts */
+ char *mf_remopts; /* Remote FS mount opts */
+ fserver *mf_server; /* File server */
+ int mf_flags; /* Flags */
+ int mf_error; /* Error code from background mount */
+ int mf_refc; /* Number of references to this node */
+ int mf_cid; /* Callout id */
+ void (*mf_prfree)(); /* Free private space */
+ voidp mf_private; /* Private - per-fs data */
+};
+
+#define MFF_MOUNTED 0x0001 /* Node is mounted */
+#define MFF_MOUNTING 0x0002 /* Mount is in progress */
+#define MFF_UNMOUNTING 0x0004 /* Unmount is in progress */
+#define MFF_RESTART 0x0008 /* Restarted node */
+#define MFF_MKMNT 0x0010 /* Delete this node's am_mount */
+#define MFF_ERROR 0x0020 /* This node failed to mount */
+#define MFF_LOGDOWN 0x0040 /* Logged that this mount is down */
+#define MFF_RSTKEEP 0x0080 /* Don't timeout this filesystem - restarted */
+#define MFF_WANTTIMO 0x0100 /* Need a timeout call when not busy */
+
+/*
+ * Map of auto-mount points.
+ */
+struct am_node {
+ int am_mapno; /* Map number */
+ mntfs *am_mnt; /* Mounted filesystem */
+ char *am_name; /* "kiska"
+ Name of this node */
+ char *am_path; /* "/home/kiska"
+ Path of this node's mount point */
+ char *am_link; /* "/a/kiska/home/kiska/this/that"
+ Link to sub-directory */
+ am_node *am_parent, /* Parent of this node */
+ *am_ysib, /* Younger sibling of this node */
+ *am_osib, /* Older sibling of this node */
+ *am_child; /* First child of this node */
+ struct attrstat am_attr; /* File attributes */
+#define am_fattr am_attr.attrstat_u.attributes
+ int am_flags; /* Boolean flags */
+ int am_error; /* Specific mount error */
+ time_t am_ttl; /* Time to live */
+ int am_timeo_w; /* Wait interval */
+ int am_timeo; /* Timeout interval */
+ unsigned int am_gen; /* Generation number */
+ char *am_pref; /* Mount info prefix */
+ am_stats am_stats; /* Statistics gathering */
+};
+
+#define AMF_NOTIMEOUT 0x0001 /* This node never times out */
+#define AMF_ROOT 0x0002 /* This is a root node */
+
+#define ONE_HOUR (60 * 60) /* One hour in seconds */
+
+/*
+ * The following values can be tuned...
+ */
+#define ALLOWED_MOUNT_TIME 40 /* 40s for a mount */
+#define AM_TTL (5 * 60) /* Default cache period */
+#define AM_TTL_W (2 * 60) /* Default unmount interval */
+#define AM_PINGER 30 /* NFS ping interval for live systems */
+#define AFS_TIMEO 8 /* Default afs timeout - .8s */
+#define AFS_RETRANS ((ALLOWED_MOUNT_TIME*10+5*afs_timeo)/afs_timeo * 2)
+ /* Default afs retrans - 1/10th seconds */
+
+#define RPC_XID_PORTMAP 0
+#define RPC_XID_MOUNTD 1
+#define RPC_XID_NFSPING 2
+#define RPC_XID_MASK (0x0f) /* 16 id's for now */
+#define MK_RPC_XID(type_id, uniq) ((type_id) | ((uniq) << 4))
diff --git a/usr.sbin/amd/include/config.h b/usr.sbin/amd/include/config.h
new file mode 100644
index 0000000..81a4c6d
--- /dev/null
+++ b/usr.sbin/amd/include/config.h
@@ -0,0 +1,141 @@
+/*
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (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
+ *
+ * $Id: config.h,v 1.3 1997/02/22 16:03:52 peter Exp $
+ *
+ */
+
+/*
+ * Get this in now so that OS_HDR can use it
+ */
+#ifdef __STDC__
+#define P(x) x
+#define P_void void
+#define Const const
+#else
+#define P(x) ()
+#define P_void /* as nothing */
+#define Const /* as nothing */
+#endif /* __STDC__ */
+
+#ifdef __GNUC__
+#define INLINE /* __inline */
+#else
+#define INLINE
+#endif /* __GNUC__ */
+
+/*
+ * Pick up target dependent definitions
+ */
+#include "os-defaults.h"
+#include OS_HDR
+
+#ifdef VOIDP
+typedef void *voidp;
+#else
+typedef char *voidp;
+#endif /* VOIDP */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/errno.h>
+extern int errno;
+#include <sys/time.h>
+
+#define clocktime() (clock_valid ? clock_valid : time(&clock_valid))
+extern time_t time P((time_t *));
+extern time_t clock_valid; /* Clock needs recalculating */
+
+extern char hostname[]; /* "kiska" */
+extern int mypid; /* Current process id */
+
+#ifdef HAS_SYSLOG
+extern int syslogging; /* Really using syslog */
+#endif /* HAS_SYSLOG */
+extern FILE *logfp; /* Log file */
+extern int xlog_level; /* Logging level */
+extern int xlog_level_init;
+
+extern int orig_umask; /* umask() on startup */
+
+#define XLOG_FATAL 0x0001
+#define XLOG_ERROR 0x0002
+#define XLOG_USER 0x0004
+#define XLOG_WARNING 0x0008
+#define XLOG_INFO 0x0010
+#define XLOG_DEBUG 0x0020
+#define XLOG_MAP 0x0040
+#define XLOG_STATS 0x0080
+
+#define XLOG_DEFSTR "all,nomap,nostats" /* Default log options */
+#define XLOG_ALL (XLOG_FATAL|XLOG_ERROR|XLOG_USER|XLOG_WARNING|XLOG_INFO|XLOG_MAP|XLOG_STATS)
+
+#ifdef DEBUG
+#define D_ALL (~0)
+
+#ifdef DEBUG_MEM
+#define free(x) xfree(__FILE__,__LINE__,x)
+#endif /* DEBUG_MEM */
+
+#define Debug(x) if (!(debug_flags & (x))) ; else
+#define dlog Debug(D_FULL) dplog
+#endif /* DEBUG */
+
+/*
+ * Option tables
+ */
+struct opt_tab {
+ char *opt;
+ int flag;
+};
+
+extern struct opt_tab xlog_opt[];
+
+extern int cmdoption P((char*, struct opt_tab*, int*));
+extern void going_down P((int));
+#ifdef DEBUG
+extern void dplog ();
+/*extern void dplog P((char*, ...));*/
+#endif /* DEBUG */
+extern void plog ();
+/*extern void plog P((int, char*, ...));*/
+extern void show_opts P((int ch, struct opt_tab*));
+extern char* strchr P((const char*, int)); /* C */
+extern voidp xmalloc P((int));
+extern voidp xrealloc P((voidp, int));
diff --git a/usr.sbin/amd/include/fstype.h b/usr.sbin/amd/include/fstype.h
new file mode 100644
index 0000000..5e2dbac
--- /dev/null
+++ b/usr.sbin/amd/include/fstype.h
@@ -0,0 +1,148 @@
+/*
+ * Copyright (c) 1989 Jan-Simon Pendry
+ * Copyright (c) 1989 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)fstype.h 8.1 (Berkeley) 6/6/93
+ *
+ * $Id$
+ *
+ */
+
+/*
+ * File system types
+ */
+
+/*
+ * Automount File System
+ */
+#define HAS_AFS
+extern am_ops afs_ops; /* Automount file system (this!) */
+extern am_ops toplvl_ops; /* Top-level automount file system */
+extern am_ops root_ops; /* Root file system */
+extern qelem afs_srvr_list;
+extern fserver *find_afs_srvr P((mntfs*));
+
+/*
+ * Direct Automount File System
+ */
+#define HAS_DFS
+extern am_ops dfs_ops; /* Direct Automount file system (this too) */
+
+/*
+ * Error File System
+ */
+#define HAS_EFS
+extern am_ops efs_ops; /* Error file system */
+
+/*
+ * Inheritance File System
+ */
+#define HAS_IFS
+extern am_ops ifs_ops; /* Inheritance file system */
+
+/*
+ * Loopback File System
+ * LOFS is optional - you can compile without it.
+ */
+#ifdef OS_HAS_LOFS
+/*
+ * Most systems can't support this, and in
+ * any case most of the functionality is
+ * available with Symlink FS. In fact,
+ * lofs_ops is not yet available.
+ */
+#define HAS_LOFS
+extern am_ops lofs_ops;
+#endif
+
+/*
+ * Netw*rk File System
+ * Good, slow, NFS.
+ * NFS host - a whole tree
+ */
+#define HAS_NFS
+#define HAS_HOST
+#define HAS_NFSX
+extern am_ops nfs_ops; /* NFS */
+extern am_ops nfsx_ops; /* NFS X */
+extern am_ops host_ops; /* NFS host */
+#ifdef HOST_EXEC
+extern char *host_helper; /* "/usr/local/etc/amd-host" */
+#endif
+extern qelem nfs_srvr_list;
+extern fserver *find_nfs_srvr P((mntfs*));
+
+/*
+ * Program File System
+ * PFS is optional - you can compile without it.
+ * This is useful for things like RVD.
+ */
+#define HAS_PFS
+extern am_ops pfs_ops; /* PFS */
+
+/*
+ * Translucent File System
+ * TFS is optional - you can compile without it.
+ * This is just plain cute.
+ */
+#ifdef notdef
+extern am_ops tfs_ops; /* TFS */
+#endif
+#undef HAS_TFS
+
+/*
+ * Un*x File System
+ * Normal local disk file system.
+ */
+#define HAS_UFS
+extern am_ops ufs_ops; /* Un*x file system */
+
+/*
+ * Symbolic-link file system
+ * A "filesystem" which is just a symbol link.
+ *
+ * sfsx also checks that the target of the link exists.
+ */
+#define HAS_SFS
+extern am_ops sfs_ops; /* Symlink FS */
+#define HAS_SFSX
+extern am_ops sfsx_ops; /* Symlink FS with existence check */
+
+/*
+ * Union file system
+ */
+#define HAS_UNION_FS
+extern am_ops union_ops; /* Union FS */
diff --git a/usr.sbin/amd/include/mountres.h b/usr.sbin/amd/include/mountres.h
new file mode 100644
index 0000000..cb3681d
--- /dev/null
+++ b/usr.sbin/amd/include/mountres.h
@@ -0,0 +1,40 @@
+/*-
+ * Copyright (c) 1997 Doug Rabson
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id$
+ */
+
+/*
+ * A reply from mountd, either v1 (for nfsv2) or v2 (for nfsv3).
+ */
+typedef struct mountres {
+ int mr_version; /* 1 or 3 */
+ union {
+ struct fhstatus mru_fhstatus; /* mount v1 result */
+ struct mountres3 mru_mountres3; /* mount v3 result */
+ } mr_mountres;
+} mountres;
+#define mr_fhstatus mr_mountres.mru_fhstatus
+#define mr_mountres3 mr_mountres.mru_mountres3
diff --git a/usr.sbin/amd/include/re.h b/usr.sbin/amd/include/re.h
new file mode 100644
index 0000000..73d6bf4
--- /dev/null
+++ b/usr.sbin/amd/include/re.h
@@ -0,0 +1,21 @@
+/*
+ * Definitions etc. for regexp(3) routines.
+ *
+ * Caveat: this is V8 regexp(3) [actually, a reimplementation thereof],
+ * not the System V one.
+ */
+#define NSUBEXP 10
+typedef struct regexp {
+ char *startp[NSUBEXP];
+ char *endp[NSUBEXP];
+ char regstart; /* Internal use only. */
+ char reganch; /* Internal use only. */
+ char *regmust; /* Internal use only. */
+ int regmlen; /* Internal use only. */
+ char program[1]; /* Unwarranted chumminess with compiler. */
+} regexp;
+
+extern regexp *regcomp();
+extern int regexec();
+extern void regsub();
+extern void regerror();
diff --git a/usr.sbin/amd/include/remagic.h b/usr.sbin/amd/include/remagic.h
new file mode 100644
index 0000000..5acf447
--- /dev/null
+++ b/usr.sbin/amd/include/remagic.h
@@ -0,0 +1,5 @@
+/*
+ * The first byte of the regexp internal "program" is actually this magic
+ * number; the start node begins in the second byte.
+ */
+#define MAGIC 0234
diff --git a/usr.sbin/amd/include/uwait.h b/usr.sbin/amd/include/uwait.h
new file mode 100644
index 0000000..bce19f4
--- /dev/null
+++ b/usr.sbin/amd/include/uwait.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 1989 Jan-Simon Pendry
+ * Copyright (c) 1989 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)uwait.h 8.1 (Berkeley) 6/6/93
+ *
+ * $Id$
+ *
+ */
+
+#if defined(mc68k) || defined(mc68000) || defined(mc68020) || defined(sparc) || defined(hp9000s300) || defined(hp9000s800)
+#define BITS_BIGENDIAN
+#endif
+#if defined(vax) || defined(i386)
+#define BITS_LITTLENDIAN
+#endif
+#if !defined BITS_BIGENDIAN && !defined BITS_LITTLENDIAN
+ #error Do not know my byte ordering
+#endif
+
+/*
+ * Structure of the information in the first word returned by both
+ * wait and wait3. If w_stopval==WSTOPPED, then the second structure
+ * describes the information returned, else the first. See WUNTRACED below.
+ */
+union wait {
+ int w_status; /* used in syscall */
+ /*
+ * Terminated process status.
+ */
+ struct {
+#ifdef BITS_LITTLENDIAN
+ unsigned short w_Termsig:7; /* termination signal */
+ unsigned short w_Coredump:1; /* core dump indicator */
+ unsigned short w_Retcode:8; /* exit code if w_termsig==0 */
+#endif
+#ifdef BITS_BIGENDIAN
+ unsigned short w_Fill1:16; /* high 16 bits unused */
+ unsigned short w_Retcode:8; /* exit code if w_termsig==0 */
+ unsigned short w_Coredump:1; /* core dump indicator */
+ unsigned short w_Termsig:7; /* termination signal */
+#endif
+ } w_U;
+};
+#define w_termsig w_U.w_Termsig
+#define w_coredump w_U.w_Coredump
+#define w_retcode w_U.w_Retcode
+
+#define WIFSIGNALED(x) ((x).w_termsig != 0)
+#define WIFEXITED(x) ((x).w_termsig == 0)
diff --git a/usr.sbin/amd/maps/a_master b/usr.sbin/amd/maps/a_master
new file mode 100644
index 0000000..2f60dde
--- /dev/null
+++ b/usr.sbin/amd/maps/a_master
@@ -0,0 +1,79 @@
+#machine opts info
+achilles -opts:=rw,grpid,nosuid \
+ type:=ufs;hostd==achilles.doc;dev:=/dev/xy1g \
+ type:=nfs;hostd!=achilles.doc;rhost:=achilles.doc;rfs:=/home/achilles
+#
+dougal -opts:=rw,grpid,nosuid \
+ type:=ufs;hostd==dougal.doc;dev:=/dev/dsk/1s0 \
+ type:=nfs;hostd!=dougal.doc;rhost:=dougal.doc;rfs:=/home/dougal
+#
+dylan type:=auto;fs:=${map};pref:=${key}/
+dylan/dk2 -opts:=rw,grpid,nosuid \
+ hostd==dylan.doc;type:=ufs;dev:=/dev/dsk/2s0 \
+ hostd!=dylan.doc;type:=nfs;rhost:=dylan.doc;rfs:=/home/dylan/dk2
+#
+dylan/dk3 -opts:=rw,grpid,nosuid \
+ hostd==dylan.doc;type:=ufs;dev:=/dev/dsk/3s0 \
+ hostd!=dylan.doc;type:=nfs;rhost:=dylan.doc;rfs:=/home/dylan/dk3
+#
+dylan/dk5 -opts:=rw,grpid,nosuid \
+ hostd==dylan.doc;type:=ufs;dev:=/dev/dsk/5s0 \
+ hostd!=dylan.doc;type:=nfs;rhost:=dylan.doc;rfs:=/home/dylan/dk5
+#
+ganymede -opts:=rw,grpid,nosuid \
+ hostd!=${key}.${domain};type:=nfs;rhost:=${key}.${domain};rfs:=/home/${key}
+gummo -opts:=rw,grpid,nosuid \
+ hostd!=gummo.doc;type:=nfs;rhost:=gummo.doc;rfs:=/home/gummo
+#
+# Wildcard match
+* -opts:=rw,grpid,nosuid \
+ hostd!=${key}.${domain};type:=nfs;rhost:=${key}.${domain};rfs:=/home/${key}
+#
+#
+gould -opts:=rw,grpid,nosuid \
+ hostd!=gould.doc;type:=nfs;rhost:=gould.doc;rfs:=/home/gould
+toytown -opts:=rw,grpid,nosuid \
+ hostd!=toytown.doc;type:=nfs;rhost:=toytown.doc;rfs:=/home/${key}
+zebedee -opts:=rw,grpid,nosuid \
+ hostd!=zebedee.doc;type:=nfs;rhost:=zebedee.doc;rfs:=/home/zebedee
+#
+# Should be ENOENT from mountd on toytown...
+#
+testing -opts:=rw,grpid,nosuid \
+ hostd!=toytown.doc;type:=nfs;rhost:=toytown.doc;rfs:=/this/that
+#
+# Somewhere else
+#
+pebbles -opts:=rw,grpid,nosuid \
+ hostd!=pebbles.cc;type:=nfs;rhost:=pebbles.cc;rfs:=/home/cc/pebbles
+#
+# Specify where to mount
+#
+xtoy -opts:=rw,grpid,nosuid \
+ type:=nfs;rhost:=toytown.doc;rfs:=/home/toytown;fs:=/tmp/junk99
+#
+# Links...
+#
+alink type:=link;hostd==achilles.doc;fs:=/etc
+tlink type:=link;hostd==truth.doc;fs:=/etc
+uucp type:=link;hostd==truth.doc;fs:=/etc;sublink:=uucp
+#
+# Duplicate mounts to the same place
+#
+dup1 -opts:=rw,grpid,nosuid \
+ type:=nfs;rhost:=toytown.doc;rfs:=/home/toytown;fs:=/tmp/tt-home
+dup2 -opts:=rw,grpid,nosuid \
+ type:=nfs;rhost:=ganymede.doc;rfs:=/home/ganymede;fs:=/tmp/tt-home
+#
+# Symlink
+#
+link type:=link;fs:=dylan/dk2/adh
+#
+# Program mount
+#
+exec type:=program;mount:="/bin/true false";unmount:="/bin/true true"
+#
+# Alternate mount locations.
+#
+alt -host==truth;type:=nfs;rfs:=/var/spool/mail \
+ rhost:=toytown rhost:=charm rhost:=gummo
diff --git a/usr.sbin/amd/maps/a_net b/usr.sbin/amd/maps/a_net
new file mode 100644
index 0000000..ea2492b
--- /dev/null
+++ b/usr.sbin/amd/maps/a_net
@@ -0,0 +1,3 @@
+/defaults fs:=${autodir}/${rhost}/root/${rfs}
+* rhost:=${key};type=host;rfs:=/
+
diff --git a/usr.sbin/amd/mk-amd-map/Makefile b/usr.sbin/amd/mk-amd-map/Makefile
new file mode 100644
index 0000000..3e79074
--- /dev/null
+++ b/usr.sbin/amd/mk-amd-map/Makefile
@@ -0,0 +1,21 @@
+# @(#)Makefile 8.1 (Berkeley) 6/28/93
+
+PROG= mk-amd-map
+CFLAGS+=-I${.OBJDIR}
+CFLAGS+=-I${.CURDIR}/../include
+CFLAGS+=-I${.CURDIR}/../rpcx
+CFLAGS+=-I${.CURDIR}/../config
+CFLAGS+=-DOS_HDR=\"os-bsd44.h\"
+MAN8= mk-amd-map.8
+CLEANFILES+=nfs_prot.h
+RPCCOM = rpcgen
+NFS_PROT_X= ${DESTDIR}/usr/include/rpcsvc/nfs_prot.x
+DPSRCS= nfs_prot.h
+
+nfs_prot.h: ${NFS_PROT_X}
+ ${RPCCOM} -h ${NFS_PROT_X} -o ${.TARGET}
+
+${OBJS} beforedepend: nfs_prot.h
+
+.include "../../Makefile.inc"
+.include <bsd.prog.mk>
diff --git a/usr.sbin/amd/mk-amd-map/mk-amd-map.8 b/usr.sbin/amd/mk-amd-map/mk-amd-map.8
new file mode 100644
index 0000000..aa877a7
--- /dev/null
+++ b/usr.sbin/amd/mk-amd-map/mk-amd-map.8
@@ -0,0 +1,59 @@
+.\" Copyright (c) 1993 Jan-Simon Pendry
+.\" 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.
+.\"
+.\" @(#)mk-amd-map.8 8.1 (Berkeley) 6/28/93
+.\"
+.Dd June 28, 1993
+.Dt MK-AMD-MAP 8
+.Os BSD 4.4
+.Sh NAME
+.Nm mk-amd-map
+.Nd create database maps for Amd
+.Sh SYNOPSIS
+.Nm
+.Op Fl p
+.Ar mapname
+.Sh DESCRIPTION
+.Nm
+creates the database maps used by the keyed map lookups in
+.Xr amd 8 .
+It reads input from the named file
+and outputs them to a correspondingly named
+hashed database.
+.Pp
+The
+.Fl p
+option prints the map on standard output instead of generating
+a database. This is usually used to merge continuation lines
+into one physical line.
+.Sh SEE ALSO
+.Xr amd 8
diff --git a/usr.sbin/amd/mk-amd-map/mk-amd-map.c b/usr.sbin/amd/mk-amd-map/mk-amd-map.c
new file mode 100644
index 0000000..2a7735e
--- /dev/null
+++ b/usr.sbin/amd/mk-amd-map/mk-amd-map.c
@@ -0,0 +1,392 @@
+/*
+ * Copyright (c) 1990, 1993 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Convert a file map into an ndbm map
+ */
+
+#ifndef lint
+char copyright[] = "\
+@(#)Copyright (c) 1990, 1993 Jan-Simon Pendry\n\
+@(#)Copyright (c) 1990 Imperial College of Science, Technology & Medicine\n\
+@(#)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[] = "@(#)mk-amd-map.c 8.1 (Berkeley) 6/28/93";
+#endif
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#include "am.h"
+#include <unistd.h>
+
+#ifndef SIGINT
+#include <signal.h>
+#endif
+
+#ifdef OS_HAS_NDBM
+#define HAS_DATABASE
+#include <ndbm.h>
+
+#ifdef DBM_SUFFIX
+#define USING_DB
+#endif
+
+#define create_database(name) dbm_open(name, O_RDWR|O_CREAT, 0644)
+
+static void usage __P((void));
+
+static int store_data(db, k, v)
+voidp db;
+char *k, *v;
+{
+ datum key, val;
+
+ key.dptr = k; val.dptr = v;
+ key.dsize = strlen(k) + 1;
+ val.dsize = strlen(v) + 1;
+ return dbm_store((DBM *) db, key, val, DBM_INSERT);
+}
+
+#endif /* OS_HAS_NDBM */
+
+#ifdef HAS_DATABASE
+#include <fcntl.h>
+#include <ctype.h>
+
+static int read_line(buf, size, fp)
+char *buf;
+int size;
+FILE *fp;
+{
+ int done = 0;
+
+ do {
+ while (fgets(buf, size, fp)) {
+ int len = strlen(buf);
+ done += len;
+ if (len > 1 && buf[len-2] == '\\' &&
+ buf[len-1] == '\n') {
+ int ch;
+ buf += len - 2;
+ size -= len - 2;
+ *buf = '\n'; buf[1] = '\0';
+ /*
+ * Skip leading white space on next line
+ */
+ while ((ch = getc(fp)) != EOF &&
+ isascii(ch) && isspace(ch))
+ ;
+ (void) ungetc(ch, fp);
+ } else {
+ return done;
+ }
+ }
+ } while (size > 0 && !feof(fp));
+
+ return done;
+}
+
+/*
+ * Read through a map
+ */
+static int read_file(fp, map, db)
+FILE *fp;
+char *map;
+voidp db;
+{
+ char key_val[2048];
+ int chuck = 0;
+ int line_no = 0;
+ int errs = 0;
+
+ while (read_line(key_val, sizeof(key_val), fp)) {
+ char *kp;
+ char *cp;
+ char *hash;
+ int len = strlen(key_val);
+ line_no++;
+
+ /*
+ * Make sure we got the whole line
+ */
+ if (key_val[len-1] != '\n') {
+ fprintf(stderr, "line %d in \"%s\" is too long", line_no, map);
+ chuck = 1;
+ } else {
+ key_val[len-1] = '\0';
+ }
+
+ /*
+ * Strip comments
+ */
+ hash = strchr(key_val, '#');
+ if (hash)
+ *hash = '\0';
+
+ /*
+ * Find start of key
+ */
+ for (kp = key_val; *kp && isascii(*kp) && isspace(*kp); kp++)
+ ;
+
+ /*
+ * Ignore blank lines
+ */
+ if (!*kp)
+ goto again;
+
+ /*
+ * Find end of key
+ */
+ for (cp = kp; *cp&&(!isascii(*cp)||!isspace(*cp)); cp++)
+ ;
+
+ /*
+ * Check whether key matches, or whether
+ * the entry is a wildcard entry.
+ */
+ if (*cp)
+ *cp++ = '\0';
+ while (*cp && isascii(*cp) && isspace(*cp))
+ cp++;
+ if (*kp == '+') {
+ fprintf(stderr, "Can't interpolate %s\n", kp);
+ errs++;
+ } else if (*cp) {
+ if (db) {
+ if (store_data(db, kp, cp) < 0) {
+ fprintf(stderr, "Could store %s -> %s\n", kp, cp);
+ errs++;
+ }
+ } else {
+ printf("%s\t%s\n", kp, cp);
+ }
+ } else {
+ fprintf(stderr, "%s: line %d has no value field", map, line_no);
+ errs++;
+ }
+
+again:
+ /*
+ * If the last read didn't get a whole line then
+ * throw away the remainder before continuing...
+ */
+ if (chuck) {
+ while (fgets(key_val, sizeof(key_val), fp) &&
+ !strchr(key_val, '\n'))
+ ;
+ chuck = 0;
+ }
+ }
+ return errs;
+}
+
+static int remove_file(f)
+char *f;
+{
+ if (unlink(f) < 0 && errno != ENOENT)
+ return -1;
+ return 0;
+}
+
+main(argc, argv)
+int argc;
+char *argv[];
+{
+ FILE *mapf;
+ char *map;
+ int rc = 0;
+ DBM *mapd;
+ static char maptmp[] = "dbmXXXXXX";
+ char maptpag[16];
+ char *mappag;
+#ifndef USING_DB
+ char maptdir[16];
+ char *mapdir;
+#endif
+ int len;
+ char *sl;
+ int printit = 0;
+ int usageflg = 0;
+ int ch;
+
+ while ((ch = getopt(argc, argv, "p")) != -1)
+ switch (ch) {
+ case 'p':
+ printit = 1;
+ break;
+ default:
+ usageflg++;
+ break;
+ }
+
+ if (usageflg || optind != (argc - 1)) {
+ usage();
+ }
+
+ map = argv[optind];
+ sl = strrchr(map, '/');
+ if (sl) {
+ *sl = '\0';
+ if (chdir(map) < 0) {
+ fputs("Can't chdir to ", stderr);
+ perror(map);
+ exit(1);
+ }
+ map = sl + 1;
+ }
+
+ if (!printit) {
+ len = strlen(map);
+#ifdef USING_DB
+ mappag = (char *) malloc(len + 5);
+ if (!mappag) {
+ perror("mk-amd-map: malloc");
+ exit(1);
+ }
+ mktemp(maptmp);
+ sprintf(maptpag, "%s%s", maptmp, DBM_SUFFIX);
+ if (remove_file(maptpag) < 0) {
+ fprintf(stderr, "Can't remove existing temporary file");
+ perror(maptpag);
+ exit(1);
+ }
+#else
+ mappag = (char *) malloc(len + 5);
+ mapdir = (char *) malloc(len + 5);
+ if (!mappag || !mapdir) {
+ perror("mk-amd-map: malloc");
+ exit(1);
+ }
+ mktemp(maptmp);
+ sprintf(maptpag, "%s.pag", maptmp);
+ sprintf(maptdir, "%s.dir", maptmp);
+ if (remove_file(maptpag) < 0 || remove_file(maptdir) < 0) {
+ fprintf(stderr, "Can't remove existing temporary files; %s and", maptpag);
+ perror(maptdir);
+ exit(1);
+ }
+#endif
+ }
+
+ mapf = fopen(map, "r");
+ if (mapf && !printit)
+ mapd = create_database(maptmp);
+ else
+ mapd = 0;
+
+#ifndef DEBUG
+ signal(SIGINT, SIG_IGN);
+#endif
+
+ if (mapd || printit) {
+ int error = read_file(mapf, map, mapd);
+ if (mapd)
+ dbm_close(mapd);
+ (void) fclose(mapf);
+ if (printit) {
+ if (error) {
+ fprintf(stderr, "Error creating ndbm map for %s\n", map);
+ rc = 1;
+ }
+ } else {
+ if (error) {
+ fprintf(stderr, "Error reading source file %s\n", map);
+ rc = 1;
+ } else {
+#ifdef USING_DB
+ sprintf(mappag, "%s%s", map, DBM_SUFFIX);
+ if (rename(maptpag, mappag) < 0) {
+ fprintf(stderr, "Couldn't rename %s to ", maptpag);
+ perror(mappag);
+ /* Throw away the temporary map */
+ unlink(maptpag);
+ rc = 1;
+ }
+#else
+ sprintf(mappag, "%s.pag", map);
+ sprintf(mapdir, "%s.dir", map);
+ if (rename(maptpag, mappag) < 0) {
+ fprintf(stderr, "Couldn't rename %s to ", maptpag);
+ perror(mappag);
+ /* Throw away the temporary map */
+ unlink(maptpag);
+ unlink(maptdir);
+ rc = 1;
+ } else if (rename(maptdir, mapdir) < 0) {
+ fprintf(stderr, "Couldn't rename %s to ", maptdir);
+ perror(mapdir);
+ /* Put the .pag file back */
+ rename(mappag, maptpag);
+ /* Throw away remaining part of original map */
+ unlink(mapdir);
+ fprintf(stderr,
+ "WARNING: existing map \"%s.{dir,pag}\" destroyed\n",
+ map);
+ rc = 1;
+ }
+#endif
+ }
+ }
+ } else {
+ fprintf(stderr, "Can't open \"%s.{dir,pag}\" for ", map);
+ perror("writing");
+ rc = 1;
+ }
+ exit(rc);
+}
+
+static void
+usage()
+{
+ fprintf(stderr, "usage: mk-amd-map [-p] file-map\n");
+ exit(1);
+}
+
+#else
+main()
+{
+ fputs("mk-amd-map: This system does not support hashed database files\n", stderr);
+ exit(1);
+}
+#endif /* HAS_DATABASE */
diff --git a/usr.sbin/amd/rpcx/amq.h b/usr.sbin/amd/rpcx/amq.h
new file mode 100644
index 0000000..622762e
--- /dev/null
+++ b/usr.sbin/amd/rpcx/amq.h
@@ -0,0 +1,153 @@
+/*
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)amq.h 8.1 (Berkeley) 6/6/93
+ *
+ * $Id$
+ *
+ */
+
+#define AMQ_STRLEN 1024
+
+typedef char *amq_string;
+bool_t xdr_amq_string();
+
+
+typedef long time_type;
+bool_t xdr_time_type();
+
+
+struct amq_mount_tree {
+ amq_string mt_mountinfo;
+ amq_string mt_directory;
+ amq_string mt_mountpoint;
+ amq_string mt_type;
+ time_type mt_mounttime;
+ u_short mt_mountuid;
+ int mt_getattr;
+ int mt_lookup;
+ int mt_readdir;
+ int mt_readlink;
+ int mt_statfs;
+ struct amq_mount_tree *mt_next;
+ struct amq_mount_tree *mt_child;
+};
+typedef struct amq_mount_tree amq_mount_tree;
+bool_t xdr_amq_mount_tree();
+
+
+typedef amq_mount_tree *amq_mount_tree_p;
+bool_t xdr_amq_mount_tree_p();
+
+
+struct amq_mount_info {
+ amq_string mi_type;
+ amq_string mi_mountpt;
+ amq_string mi_mountinfo;
+ amq_string mi_fserver;
+ int mi_error;
+ int mi_refc;
+ int mi_up;
+};
+typedef struct amq_mount_info amq_mount_info;
+bool_t xdr_amq_mount_info();
+
+
+typedef struct {
+ u_int amq_mount_info_list_len;
+ amq_mount_info *amq_mount_info_list_val;
+} amq_mount_info_list;
+bool_t xdr_amq_mount_info_list();
+
+
+typedef struct {
+ u_int amq_mount_tree_list_len;
+ amq_mount_tree_p *amq_mount_tree_list_val;
+} amq_mount_tree_list;
+bool_t xdr_amq_mount_tree_list();
+
+
+struct amq_mount_stats {
+ int as_drops;
+ int as_stale;
+ int as_mok;
+ int as_merr;
+ int as_uerr;
+};
+typedef struct amq_mount_stats amq_mount_stats;
+bool_t xdr_amq_mount_stats();
+
+
+enum amq_opt {
+ AMOPT_DEBUG = 0,
+ AMOPT_LOGFILE = 1,
+ AMOPT_XLOG = 2,
+ AMOPT_FLUSHMAPC = 3
+};
+typedef enum amq_opt amq_opt;
+bool_t xdr_amq_opt();
+
+
+struct amq_setopt {
+ amq_opt as_opt;
+ amq_string as_str;
+};
+typedef struct amq_setopt amq_setopt;
+bool_t xdr_amq_setopt();
+
+
+#define AMQ_PROGRAM ((u_long)300019)
+#define AMQ_VERSION ((u_long)1)
+#define AMQPROC_NULL ((u_long)0)
+extern voidp amqproc_null_1();
+#define AMQPROC_MNTTREE ((u_long)1)
+extern amq_mount_tree_p *amqproc_mnttree_1();
+#define AMQPROC_UMNT ((u_long)2)
+extern voidp amqproc_umnt_1();
+#define AMQPROC_STATS ((u_long)3)
+extern amq_mount_stats *amqproc_stats_1();
+#define AMQPROC_EXPORT ((u_long)4)
+extern amq_mount_tree_list *amqproc_export_1();
+#define AMQPROC_SETOPT ((u_long)5)
+extern int *amqproc_setopt_1();
+#define AMQPROC_GETMNTFS ((u_long)6)
+extern amq_mount_info_list *amqproc_getmntfs_1();
+#define AMQPROC_MOUNT ((u_long)7)
+extern int *amqproc_mount_1();
+#define AMQPROC_GETVERS ((u_long)8)
+extern amq_string *amqproc_getvers_1();
+
diff --git a/usr.sbin/amd/rpcx/amq.x b/usr.sbin/amd/rpcx/amq.x
new file mode 100644
index 0000000..9a18905
--- /dev/null
+++ b/usr.sbin/amd/rpcx/amq.x
@@ -0,0 +1,185 @@
+/*
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)amq.x 8.1 (Berkeley) 6/6/93
+ *
+ * $Id$
+ *
+ */
+
+/*
+ * Protocol description used by the amq program
+ */
+
+const AMQ_STRLEN = 1024; /* Maximum length of a pathname */
+
+/*
+ * The type dirpath is the pathname of a directory
+ */
+typedef string amq_string<AMQ_STRLEN>;
+
+/*
+ * The type time_type should correspond to the system time_t
+ */
+typedef long time_type;
+
+/*
+ * A tree of what is mounted
+ */
+struct amq_mount_tree {
+ amq_string mt_mountinfo; /* Mounted filesystem */
+ amq_string mt_directory; /* Virtual mount */
+ amq_string mt_mountpoint; /* Mount point */
+ amq_string mt_type; /* Filesystem type */
+ time_type mt_mounttime; /* Mount time */
+ u_short mt_mountuid; /* Mounter */
+ int mt_getattr; /* Count of getattrs */
+ int mt_lookup; /* Count of lookups */
+ int mt_readdir; /* Count of readdirs */
+ int mt_readlink; /* Count of readlinks */
+ int mt_statfs; /* Count of statfss */
+ amq_mount_tree *mt_next; /* Sibling mount tree */
+ amq_mount_tree *mt_child; /* Child mount tree */
+};
+typedef amq_mount_tree *amq_mount_tree_p;
+
+/*
+ * List of mounted filesystems
+ */
+struct amq_mount_info {
+ amq_string mi_type; /* Type of mount */
+ amq_string mi_mountpt; /* Mount point */
+ amq_string mi_mountinfo; /* Mount info */
+ amq_string mi_fserver; /* Fileserver */
+ int mi_error; /* Error code */
+ int mi_refc; /* References */
+ int mi_up; /* Filesystem available */
+};
+typedef amq_mount_info amq_mount_info_list<>;
+
+/*
+ * A list of mount trees
+ */
+typedef amq_mount_tree_p amq_mount_tree_list<>;
+
+/*
+ * System wide stats
+ */
+struct amq_mount_stats {
+ int as_drops; /* Dropped requests */
+ int as_stale; /* Stale NFS handles */
+ int as_mok; /* Succesful mounts */
+ int as_merr; /* Failed mounts */
+ int as_uerr; /* Failed unmounts */
+};
+
+enum amq_opt {
+ AMOPT_DEBUG=0,
+ AMOPT_LOGFILE=1,
+ AMOPT_XLOG=2,
+ AMOPT_FLUSHMAPC=3
+};
+
+struct amq_setopt {
+ amq_opt as_opt; /* Option */
+ amq_string as_str; /* String */
+};
+
+program AMQ_PROGRAM {
+ version AMQ_VERSION {
+ /*
+ * Does no work. It is made available in all RPC services
+ * to allow server reponse testing and timing
+ */
+ void
+ AMQPROC_NULL(void) = 0;
+
+ /*
+ * Returned the mount tree descending from
+ * the given directory. The directory must
+ * be a top-level mount point of the automounter.
+ */
+ amq_mount_tree_p
+ AMQPROC_MNTTREE(amq_string) = 1;
+
+ /*
+ * Force a timeout unmount on the specified directory.
+ */
+ void
+ AMQPROC_UMNT(amq_string) = 2;
+
+ /*
+ * Obtain system wide statistics from the automounter
+ */
+ amq_mount_stats
+ AMQPROC_STATS(void) = 3;
+
+ /*
+ * Obtain full tree
+ */
+ amq_mount_tree_list
+ AMQPROC_EXPORT(void) = 4;
+
+ /*
+ * Control debug options.
+ * Return status:
+ * -1: debug not available
+ * 0: everything wonderful
+ * >0: number of options not recognised
+ */
+ int
+ AMQPROC_SETOPT(amq_setopt) = 5;
+
+ /*
+ * List of mounted filesystems
+ */
+ amq_mount_info_list
+ AMQPROC_GETMNTFS(void) = 6;
+
+ /*
+ * Mount a filesystem
+ */
+ int
+ AMQPROC_MOUNT(amq_string) = 7;
+
+ /*
+ * Get version info
+ */
+ amq_string
+ AMQPROC_GETVERS(void) = 8;
+ } = 1;
+} = 300019; /* Allocated by Sun, 89/8/29 */
diff --git a/usr.sbin/amd/rpcx/amq_clnt.c b/usr.sbin/amd/rpcx/amq_clnt.c
new file mode 100644
index 0000000..40288ad
--- /dev/null
+++ b/usr.sbin/amd/rpcx/amq_clnt.c
@@ -0,0 +1,181 @@
+/*
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)amq_clnt.c 8.1 (Berkeley) 6/6/93
+ *
+ * $Id$
+ *
+ */
+
+#include "am.h"
+#include "amq.h"
+
+static struct timeval TIMEOUT = { ALLOWED_MOUNT_TIME, 0 };
+
+voidp
+amqproc_null_1(argp, clnt)
+ voidp argp;
+ CLIENT *clnt;
+{
+ static char res;
+
+ bzero((char *)&res, sizeof(res));
+ if (clnt_call(clnt, AMQPROC_NULL, xdr_void, argp, xdr_void, &res, TIMEOUT) != RPC_SUCCESS) {
+ return (NULL);
+ }
+ return ((voidp)&res);
+}
+
+
+amq_mount_tree_p *
+amqproc_mnttree_1(argp, clnt)
+ amq_string *argp;
+ CLIENT *clnt;
+{
+ static amq_mount_tree_p res;
+
+ bzero((char *)&res, sizeof(res));
+ if (clnt_call(clnt, AMQPROC_MNTTREE, xdr_amq_string, argp, xdr_amq_mount_tree_p, &res, TIMEOUT) != RPC_SUCCESS) {
+ return (NULL);
+ }
+ return (&res);
+}
+
+
+voidp
+amqproc_umnt_1(argp, clnt)
+ amq_string *argp;
+ CLIENT *clnt;
+{
+ static char res;
+
+ bzero((char *)&res, sizeof(res));
+ if (clnt_call(clnt, AMQPROC_UMNT, xdr_amq_string, argp, xdr_void, &res, TIMEOUT) != RPC_SUCCESS) {
+ return (NULL);
+ }
+ return ((voidp)&res);
+}
+
+
+amq_mount_stats *
+amqproc_stats_1(argp, clnt)
+ voidp argp;
+ CLIENT *clnt;
+{
+ static amq_mount_stats res;
+
+ bzero((char *)&res, sizeof(res));
+ if (clnt_call(clnt, AMQPROC_STATS, xdr_void, argp, xdr_amq_mount_stats, &res, TIMEOUT) != RPC_SUCCESS) {
+ return (NULL);
+ }
+ return (&res);
+}
+
+
+amq_mount_tree_list *
+amqproc_export_1(argp, clnt)
+ voidp argp;
+ CLIENT *clnt;
+{
+ static amq_mount_tree_list res;
+
+ bzero((char *)&res, sizeof(res));
+ if (clnt_call(clnt, AMQPROC_EXPORT, xdr_void, argp, xdr_amq_mount_tree_list, &res, TIMEOUT) != RPC_SUCCESS) {
+ return (NULL);
+ }
+ return (&res);
+}
+
+int *
+amqproc_setopt_1(argp, clnt)
+ amq_setopt *argp;
+ CLIENT *clnt;
+{
+ static int res;
+
+ bzero((char *)&res, sizeof(res));
+ if (clnt_call(clnt, AMQPROC_SETOPT, xdr_amq_setopt, argp, xdr_int, &res, TIMEOUT) != RPC_SUCCESS) {
+ return (NULL);
+ }
+ return (&res);
+}
+
+
+amq_mount_info_list *
+amqproc_getmntfs_1(argp, clnt)
+ voidp argp;
+ CLIENT *clnt;
+{
+ static amq_mount_info_list res;
+
+ bzero((char *)&res, sizeof(res));
+ if (clnt_call(clnt, AMQPROC_GETMNTFS, xdr_void, argp, xdr_amq_mount_info_list, &res, TIMEOUT) != RPC_SUCCESS) {
+ return (NULL);
+ }
+ return (&res);
+}
+
+
+int *
+amqproc_mount_1(argp, clnt)
+ voidp argp;
+ CLIENT *clnt;
+{
+ static int res;
+
+ bzero((char *)&res, sizeof(res));
+ if (clnt_call(clnt, AMQPROC_MOUNT, xdr_amq_string, argp, xdr_int, &res, TIMEOUT) != RPC_SUCCESS) {
+ return (NULL);
+ }
+ return (&res);
+}
+
+
+amq_string *
+amqproc_getvers_1(argp, clnt)
+ voidp argp;
+ CLIENT *clnt;
+{
+ static amq_string res;
+
+ bzero((char *)&res, sizeof(res));
+ if (clnt_call(clnt, AMQPROC_GETVERS, xdr_void, argp, xdr_amq_string, &res, TIMEOUT) != RPC_SUCCESS) {
+ return (NULL);
+ }
+ return (&res);
+}
+
diff --git a/usr.sbin/amd/rpcx/amq_svc.c b/usr.sbin/amd/rpcx/amq_svc.c
new file mode 100644
index 0000000..5688644
--- /dev/null
+++ b/usr.sbin/amd/rpcx/amq_svc.c
@@ -0,0 +1,136 @@
+/*
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)amq_svc.c 8.1 (Berkeley) 6/6/93
+ *
+ * $Id: amq_svc.c,v 1.3 1997/02/22 16:04:03 peter Exp $
+ *
+ */
+
+#include "am.h"
+#include "amq.h"
+extern bool_t xdr_amq_mount_info_qelem();
+
+void
+amq_program_1(rqstp, transp)
+ struct svc_req *rqstp;
+ SVCXPRT *transp;
+{
+ union {
+ amq_string amqproc_mnttree_1_arg;
+ amq_string amqproc_umnt_1_arg;
+ amq_setopt amqproc_setopt_1_arg;
+ amq_string amqproc_mount_1_arg;
+ } argument;
+ char *result;
+ bool_t (*xdr_argument)(), (*xdr_result)();
+ char *(*local)();
+
+ switch (rqstp->rq_proc) {
+ case AMQPROC_NULL:
+ xdr_argument = xdr_void;
+ xdr_result = xdr_void;
+ local = (char *(*)()) amqproc_null_1;
+ break;
+
+ case AMQPROC_MNTTREE:
+ xdr_argument = xdr_amq_string;
+ xdr_result = xdr_amq_mount_tree_p;
+ local = (char *(*)()) amqproc_mnttree_1;
+ break;
+
+ case AMQPROC_UMNT:
+ xdr_argument = xdr_amq_string;
+ xdr_result = xdr_void;
+ local = (char *(*)()) amqproc_umnt_1;
+ break;
+
+ case AMQPROC_STATS:
+ xdr_argument = xdr_void;
+ xdr_result = xdr_amq_mount_stats;
+ local = (char *(*)()) amqproc_stats_1;
+ break;
+
+ case AMQPROC_EXPORT:
+ xdr_argument = xdr_void;
+ xdr_result = xdr_amq_mount_tree_list;
+ local = (char *(*)()) amqproc_export_1;
+ break;
+
+ case AMQPROC_SETOPT:
+ xdr_argument = xdr_amq_setopt;
+ xdr_result = xdr_int;
+ local = (char *(*)()) amqproc_setopt_1;
+ break;
+
+ case AMQPROC_GETMNTFS:
+ xdr_argument = xdr_void;
+ xdr_result = xdr_amq_mount_info_qelem;
+ local = (char *(*)()) amqproc_getmntfs_1;
+ break;
+
+ case AMQPROC_MOUNT:
+ xdr_argument = xdr_amq_string;
+ xdr_result = xdr_int;
+ local = (char *(*)()) amqproc_mount_1;
+ break;
+
+ case AMQPROC_GETVERS:
+ xdr_argument = xdr_void;
+ xdr_result = xdr_amq_string;
+ local = (char *(*)()) amqproc_getvers_1;
+ break;
+
+ default:
+ svcerr_noproc(transp);
+ return;
+ }
+ bzero((char *)&argument, sizeof(argument));
+ if (!svc_getargs(transp, xdr_argument, (caddr_t) &argument)) {
+ svcerr_decode(transp);
+ return;
+ }
+ result = (*local)(&argument, rqstp);
+ if (result != NULL && !svc_sendreply(transp, xdr_result, result)) {
+ svcerr_systemerr(transp);
+ }
+ if (!svc_freeargs(transp, xdr_argument, (caddr_t) &argument)) {
+ plog(XLOG_FATAL, "unable to free rpc arguments in amqprog_1");
+ going_down(1);
+ }
+}
+
diff --git a/usr.sbin/amd/rpcx/amq_xdr.c b/usr.sbin/amd/rpcx/amq_xdr.c
new file mode 100644
index 0000000..9666b14
--- /dev/null
+++ b/usr.sbin/amd/rpcx/amq_xdr.c
@@ -0,0 +1,251 @@
+/*
+ * Copyright (c) 1990 Jan-Simon Pendry
+ * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Jan-Simon Pendry at Imperial College, London.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)amq_xdr.c 8.1 (Berkeley) 6/6/93
+ *
+ * $Id$
+ *
+ */
+
+#include "am.h"
+#include "amq.h"
+
+
+bool_t
+xdr_amq_string(xdrs, objp)
+ XDR *xdrs;
+ amq_string *objp;
+{
+ if (!xdr_string(xdrs, objp, AMQ_STRLEN)) {
+ return (FALSE);
+ }
+ return (TRUE);
+}
+
+
+
+
+bool_t
+xdr_time_type(xdrs, objp)
+ XDR *xdrs;
+ time_type *objp;
+{
+ if (!xdr_long(xdrs, objp)) {
+ return (FALSE);
+ }
+ return (TRUE);
+}
+
+
+
+
+bool_t
+xdr_amq_mount_tree(xdrs, objp)
+ XDR *xdrs;
+ amq_mount_tree *objp;
+{
+ if (!xdr_amq_string(xdrs, &objp->mt_mountinfo)) {
+ return (FALSE);
+ }
+ if (!xdr_amq_string(xdrs, &objp->mt_directory)) {
+ return (FALSE);
+ }
+ if (!xdr_amq_string(xdrs, &objp->mt_mountpoint)) {
+ return (FALSE);
+ }
+ if (!xdr_amq_string(xdrs, &objp->mt_type)) {
+ return (FALSE);
+ }
+ if (!xdr_time_type(xdrs, &objp->mt_mounttime)) {
+ return (FALSE);
+ }
+ if (!xdr_u_short(xdrs, &objp->mt_mountuid)) {
+ return (FALSE);
+ }
+ if (!xdr_int(xdrs, &objp->mt_getattr)) {
+ return (FALSE);
+ }
+ if (!xdr_int(xdrs, &objp->mt_lookup)) {
+ return (FALSE);
+ }
+ if (!xdr_int(xdrs, &objp->mt_readdir)) {
+ return (FALSE);
+ }
+ if (!xdr_int(xdrs, &objp->mt_readlink)) {
+ return (FALSE);
+ }
+ if (!xdr_int(xdrs, &objp->mt_statfs)) {
+ return (FALSE);
+ }
+ if (!xdr_pointer(xdrs, (char **)&objp->mt_next, sizeof(amq_mount_tree), xdr_amq_mount_tree)) {
+ return (FALSE);
+ }
+ if (!xdr_pointer(xdrs, (char **)&objp->mt_child, sizeof(amq_mount_tree), xdr_amq_mount_tree)) {
+ return (FALSE);
+ }
+ return (TRUE);
+}
+
+
+
+
+bool_t
+xdr_amq_mount_tree_p(xdrs, objp)
+ XDR *xdrs;
+ amq_mount_tree_p *objp;
+{
+ if (!xdr_pointer(xdrs, (char **)objp, sizeof(amq_mount_tree), xdr_amq_mount_tree)) {
+ return (FALSE);
+ }
+ return (TRUE);
+}
+
+
+
+bool_t
+xdr_amq_mount_info(xdrs, objp)
+ XDR *xdrs;
+ amq_mount_info *objp;
+{
+ if (!xdr_amq_string(xdrs, &objp->mi_type)) {
+ return (FALSE);
+ }
+ if (!xdr_amq_string(xdrs, &objp->mi_mountpt)) {
+ return (FALSE);
+ }
+ if (!xdr_amq_string(xdrs, &objp->mi_mountinfo)) {
+ return (FALSE);
+ }
+ if (!xdr_amq_string(xdrs, &objp->mi_fserver)) {
+ return (FALSE);
+ }
+ if (!xdr_int(xdrs, &objp->mi_error)) {
+ return (FALSE);
+ }
+ if (!xdr_int(xdrs, &objp->mi_refc)) {
+ return (FALSE);
+ }
+ if (!xdr_int(xdrs, &objp->mi_up)) {
+ return (FALSE);
+ }
+ return (TRUE);
+}
+
+
+
+bool_t
+xdr_amq_mount_info_list(xdrs, objp)
+ XDR *xdrs;
+ amq_mount_info_list *objp;
+{
+ if (!xdr_array(xdrs, (char **)&objp->amq_mount_info_list_val, (u_int *)&objp->amq_mount_info_list_len, ~0, sizeof(amq_mount_info), xdr_amq_mount_info)) {
+ return (FALSE);
+ }
+ return (TRUE);
+}
+
+
+
+bool_t
+xdr_amq_mount_tree_list(xdrs, objp)
+ XDR *xdrs;
+ amq_mount_tree_list *objp;
+{
+ if (!xdr_array(xdrs, (char **)&objp->amq_mount_tree_list_val, (u_int *)&objp->amq_mount_tree_list_len, ~0, sizeof(amq_mount_tree_p), xdr_amq_mount_tree_p)) {
+ return (FALSE);
+ }
+ return (TRUE);
+}
+
+
+
+
+bool_t
+xdr_amq_mount_stats(xdrs, objp)
+ XDR *xdrs;
+ amq_mount_stats *objp;
+{
+ if (!xdr_int(xdrs, &objp->as_drops)) {
+ return (FALSE);
+ }
+ if (!xdr_int(xdrs, &objp->as_stale)) {
+ return (FALSE);
+ }
+ if (!xdr_int(xdrs, &objp->as_mok)) {
+ return (FALSE);
+ }
+ if (!xdr_int(xdrs, &objp->as_merr)) {
+ return (FALSE);
+ }
+ if (!xdr_int(xdrs, &objp->as_uerr)) {
+ return (FALSE);
+ }
+ return (TRUE);
+}
+
+
+
+
+bool_t
+xdr_amq_opt(xdrs, objp)
+ XDR *xdrs;
+ amq_opt *objp;
+{
+ if (!xdr_enum(xdrs, (enum_t *)objp)) {
+ return (FALSE);
+ }
+ return (TRUE);
+}
+
+
+
+
+bool_t
+xdr_amq_setopt(xdrs, objp)
+ XDR *xdrs;
+ amq_setopt *objp;
+{
+ if (!xdr_amq_opt(xdrs, &objp->as_opt)) {
+ return (FALSE);
+ }
+ if (!xdr_amq_string(xdrs, &objp->as_str)) {
+ return (FALSE);
+ }
+ return (TRUE);
+}
+
+
diff --git a/usr.sbin/amd/text/COPYRIGHT b/usr.sbin/amd/text/COPYRIGHT
new file mode 100644
index 0000000..f2e96a6
--- /dev/null
+++ b/usr.sbin/amd/text/COPYRIGHT
@@ -0,0 +1,3 @@
+Copyright (c) 1990 Jan-Simon Pendry
+Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+Copyright (c) 1990, 1993 The Regents of the University of California.
diff --git a/usr.sbin/amd/text/INSTALL b/usr.sbin/amd/text/INSTALL
new file mode 100644
index 0000000..398cb08
--- /dev/null
+++ b/usr.sbin/amd/text/INSTALL
@@ -0,0 +1,194 @@
+Installation Notes for Amd.
+
+NOTE: Please read all of this before starting.
+ It is not very long and may save you time in the long term.
+
+1. ``Getting started...''
+
+If you don't know what an Automounter does for you then read the
+documentation in doc/amdref.texinfo. You can either use TeX to print
+it out or read it directly using the GNU info package.
+
+2. ``Find out what version of UN*X you are running...''
+
+To install Amd you need a port for your version of UN*X. In the
+config/ directory are several files called os-*.h. One of these
+should correspond to your version of UN*X. Run the program
+"config/os-type" to find out what system Amd thinks you have. Check
+the correspondong config/os-??? file to make sure that you and Amd are
+in agreement. If os-type returns "unknown" then either no-one has yet
+done a port, or your version of UN*X is so braindead that a port is
+not possible (e.g. System V without reliable signals). The current
+known operating systems (grouped by architecture) are:
+
+ acis43 (AOS) ACIS 4.3BSD on an IBM RT
+ aix3 AIX 3.2
+ aux Apple A/UX
+ bsd44 4.4 BSD on whatever
+ concentrix Concentrix on an Alliant
+ dgux Data General AViiON
+ fpx4 Celerity FPX 4.1/2
+ hlh42 4.2 BSD on HLH Orion 1/05
+ hpux HP-UX 6.* and 7.* on a HP9000/300
+ irix3 SGI Iris
+ irix4 SGI Iris w/Irix 4.0.x
+ next NeXT
+ riscix 4.3 BSD on an Acorn Archimedes
+ sos3, sos4 SunOS 3.* and 4.* on a Sun-3 and Sun-4
+ u2_2 Ultrix 2.2 (or 2.*?) on a VAX (broken)
+ u3_0 Ultrix 3.0 (or 3.*?) on a VAX (broken)
+ u4_2 Ultrix 4.2
+ umax43 4.3 BSD on an Encore Multimax
+ xinu43 More/BSD (4.3 BSD) on a VAX or HP9000/300
+
+ + some others...
+
+If you do define a new operating system type foo, you may need to create a
+file called Makefile.foo which defines the special Makefile parameters.
+
+3. ``Hacking the Makefile...''
+
+Amd tries very hard to determine what type of machine you are using
+and how best to compile itself. If this does not work then you will
+have to find some heuristic which can differentiate your
+configuration. You may need to edit "config/arch" and
+"config/os-type". If you do make sure your changes can cope if
+/etc/motd is missing and please send it to the address below.
+
+To check whether things are working, run:
+ sh config/arch
+ sh config/os-type
+
+You may care to tailor some site specific preferences in "Makefile.com". The
+variables most likely to be changes are at the top. Any changes should be
+added to a file called config/Makefile.local (if they are applicable to all
+operating systems at your site) or Makefile.local.foo (where foo is the OS type
+as determined in part 2).
+
+Additionally, some configuration options may be altered in
+"config/Makefile.config". This means that you should not need to edit any
+distributed files apart from "config/Makefile.config". As a minimum, you
+should check:
+
+* You are using the correct C compiler. Amd, as shipped, does not use GCC.
+ Note that using GCC version 1.34 or later (e.g. 1.36) gives structure
+ passing problems with some parts of Sun's RPC library at least on Sun-4's.
+ The current workaround is to use the system CC to compile the part of the
+ automounter that gets hit by this problem. [[This is not the same problem
+ that is fixed by -fpcc-struct-return.]] Amd contains no "register"
+ declarations, so using old PCC based code generators is probably bad news.
+
+ To use GNU CC, add the following to config/Makefile.local{.os-type}:
+
+ CC = gcc ${GCCOPTS}
+
+* The installation directory (ETC) is set up correctly.
+
+* If you are running tests then it may be worth switching on the DEBUG flag
+ which will cause a running commentary to be printed to the log file. To
+ compile in the debug code, add the following to
+ config/Makefile.local{.os-type}:
+
+ DEBUG = -DDEBUG
+ CCOPTS = -g
+
+ The -g option will also allow you to use gdb. Using dbx is not advisable
+ since it puts a breakpoint on exit() which causes all of Amd's child
+ processes to dump core. gdb does not suffer from this problem.
+
+4. ``Build the executable...''
+
+Now you need to compile the automounter. To do this you type:
+
+ make
+
+in the top-level directory. You can also go into each of the program
+directories and just run make there.
+
+If you are porting to a new machine you may want to do:
+
+ make OS=foo
+
+where foo is the name of your version of UN*X as determined in part 1, until
+you have made the changes to config/os-type and/or config/arch. When the
+compilation is complete you will end up with a program called "A.arch_foo/amd".
+
+Try running:
+
+ A.arch_foo/amd -v
+
+and check the output. It should look something like:
+
+ Copyright (c) 1990 Jan-Simon Pendry
+ Copyright (c) 1990 Imperial College of Science, Technology & Medicine
+ Copyright (c) 1990 The Regents of the University of California.
+ amd 5.2.1.5 of 90/09/16 13:22:46 5.3Alpha5 #0: Sun Sep 16 13:23:28 BST 1990
+ Built by pendry@okeeffe.Berkeley.EDU for a tahoe running bsd44 (big-endian)
+ Map support for: root, passwd, nis, file, error.
+ fstypes: ufs, nfs, nfsx, host, link, program, auto, direct, toplvl, error.
+
+Make sure the O/S and architecture types were correctly derived during the
+build.
+
+5. ``Installation...''
+
+If you are not just testing Amd, then you can install it by typing:
+
+ make install
+
+to install "A.arch_foo/amd" in "/usr/local/etc/amd" (or as otherwise
+modified in part 3).
+
+6. ``Update /etc/rpc''
+
+Amq uses Sun RPC to talk to Amd using program number 300019 which has
+been registered with Sun. Add the following lines to /etc/rpc or your
+YP or Hesiod master:
+
+# Automount control protocol
+amd 300019 amq
+
+Amd does not require this addition - it just keeps rpcinfo happy.
+
+7. ``Hanging your machine...''
+
+WARNING: THIS MAY HANG YOUR MACHINE IF YOU GET IT WRONG.
+
+Running Amd with a carelessly thought out mount map can cause your Amd to
+enter a deadlock inside the kernel. For example, attempting to automount a
+directory which is automounted. This will cause the automounter to issue a mount
+request causing the kernel to send an NFS request back to the same automounter,
+which is currently stuck in a system call and unable to respond - even
+kill -KILL won't get you out of this one.
+
+There is nothing you can do to fix it without rebooting your machine, so...
+
+Find a diskless workstation and play with that first before trying this on
+your main 200 user service machine (unless you hate your users). Something
+like a diskless Sun-4 is best for development testing - you can compile on a
+Sun-4 server and run the binary on the diskless node. They reboot very fast
+as well between tests.
+
+Now you can try running Amd. Please read the documentation in doc/Amd.tex
+for more details. The configuration file "maps/a_master" provides a sample for
+you to play with. Something like:
+
+ ./amd -c 40 -D test,nodaemon /tmp/amnt ../maps/a_master &
+
+is good for testing. Note that Amd will clean up correctly if you send it a
+SIGINT or SIGTERM. Other signals are either ignored or will blow it away,
+leaving your machine in a potentially dangerous state.
+
+Remember that Amd needs to run as root in order to do mounts/unmounts
+though it does check this condition somewhere near line one of main().
+It will also need write permission in the working directory if you
+have built it with DEBUG defined and your system's mount table is
+reflected in a file. In this case watch out for NFS stepping in and
+mapping root to nobody.
+
+8. ``Report what happened...''
+
+If anything interesting happened, eg it didn't work, please report it to me
+-- Jan-Simon Pendry <jsp@doc.ic.ac.uk> -- as detailed in the README file.
+
+$Id$
diff --git a/usr.sbin/amd/text/README b/usr.sbin/amd/text/README
new file mode 100644
index 0000000..4a41831
--- /dev/null
+++ b/usr.sbin/amd/text/README
@@ -0,0 +1,37 @@
+This program is an automounter.
+
+This automounter is a value-added, replacement for the SunOS 4
+automount(8) program. Though based on that program in spirit, it
+contains no proprietary UN*X source code.
+
+The version you have here is release 5.3Alpha.
+
+This program is NOT in the Public Domain - it is covered by
+the usual Berkeley software distribution license - but feel free
+to take it and change it.
+
+It is believed to work correctly on Sun-3's (SunOS 3.5, 4.0, 4.1),
+Sun-4's (SunOS 4.0, 4.1), HP-9000/300 (HP-UX, MORE/bsd & BSD 4.3 Reno),
+IBM RTs (AOS 4.3), IBM RISC System/6000 (AIX 3.1), VAXen (Ultrix 4.0,
+MORE/bsd & BSD 4.3 Reno) and a wide variety of other systems. If
+your machine is not supported please feel free to try a port, but be
+sure to send me a record of the changes you had to make.
+
+
+This is the file text/README.
+
+See the file text/INSTALL for installation instructions.
+
+The documentation is in doc/amdref.texinfo. This is in GNU TeXinfo format
+and you will need a TeX system before you can print it out.
+
+Please forward *all* bug reports to Jan-Simon Pendry <jsp@doc.ic.ac.uk>
+quoting the details of the release and your configuration, which can be
+obtained by running the command "amd -v". Also send any additional
+information which may be relevant such as command line options and the maps
+being used. Thanks.
+
+The manual page (amd/amd.8) only lists the command line options. See the
+texinfo document doc/amdref.texinfo for a more detailed discussion.
+
+$Id$
diff --git a/usr.sbin/amd/text/amd.start.ex b/usr.sbin/amd/text/amd.start.ex
new file mode 100644
index 0000000..d6fff9c
--- /dev/null
+++ b/usr.sbin/amd/text/amd.start.ex
@@ -0,0 +1,87 @@
+#!/bin/sh -
+#
+# Copyright (c) 1989 Jan-Simon Pendry
+# Copyright (c) 1989 Imperial College of Science, Technology & Medicine
+# Copyright (c) 1989, 1993
+# The Regents of the University of California. All rights reserved.
+#
+# This code is derived from software contributed to Berkeley by
+# Jan-Simon Pendry at Imperial College, London.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. All advertising materials mentioning features or use of this software
+# must display the following acknowledgement:
+# This product includes software developed by the University of
+# California, Berkeley and its contributors.
+# 4. Neither the name of the University nor the names of its contributors
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# @(#)amd.start.ex 8.1 (Berkeley) 6/6/93
+#
+# Start amd
+#
+# $Id$
+#
+PATH=/usr/sbin:/bin:/usr/bin:$PATH export PATH
+
+#
+# Either name of logfile or "syslog"
+#
+#LOGFILE=syslog
+LOGFILE=/var/run/amd.log
+
+#
+# Figure out whether domain name is in host name
+# If the hostname is just the machine name then
+# pass in the name of the local domain so that the
+# hostnames in the map are domain stripped correctly.
+#
+case `hostname` in
+*.*) dmn= ;;
+*) dmn='-d doc.ic.ac.uk'
+esac
+
+#
+# Zap earlier log file
+#
+case "$LOGFILE" in
+*/*)
+ mv "$LOGFILE" "$LOGFILE"-
+ > "$LOGFILE"
+ ;;
+syslog)
+ : nothing
+ ;;
+esac
+
+cd /usr/sbin
+#
+# -r restart
+# -d dmn local domain
+# -w wait wait between unmount attempts
+# -l log logfile or "syslog"
+#
+eval nice --4 ./amd -p > /var/run/amd.pid -r $dmn -w 240 -l "$LOGFILE" \
+ /homes amd.homes -cache:=inc \
+ /home amd.home -cache:=inc \
+ /vol amd.vol -cache:=inc
diff --git a/usr.sbin/apm/Makefile b/usr.sbin/apm/Makefile
new file mode 100644
index 0000000..7f04f9b
--- /dev/null
+++ b/usr.sbin/apm/Makefile
@@ -0,0 +1,8 @@
+# $Id$
+
+PROG= apm
+LINKS= ${BINDIR}/apm ${BINDIR}/zzz
+MAN8= apm.8
+MLINKS= apm.8 zzz.8
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/apm/apm.8 b/usr.sbin/apm/apm.8
new file mode 100644
index 0000000..9652a18
--- /dev/null
+++ b/usr.sbin/apm/apm.8
@@ -0,0 +1,97 @@
+.\" LP (Laptop Package)
+.\"
+.\" Copyright (c) 1994 by HOSOKAWA, Tatsumi <hosokawa@mt.cs.keio.ac.jp>
+.\"
+.\" This software may be used, modified, copied, and distributed, in
+.\" both source and binary form provided that the above copyright and
+.\" these terms are retained. Under no circumstances is the author
+.\" responsible for the proper functioning of this software, nor does
+.\" the author assume any responsibility for damages incurred with its
+.\" use.
+.Dd November 1, 1994
+.Dt APM 8
+.Os
+.Sh NAME
+.Nm apm, zzz
+.Nd control the APM BIOS and display its information
+.Sh SYNOPSIS
+.Nm apm
+.Op Fl ablsz
+.Op Fl d Ar 1|0
+.Pp
+.Nm zzz
+.Sh DESCRIPTION
+.Nm Apm
+controls the Intel / Microsoft APM (Advanced Power Management) BIOS and
+displays the current status of APM on laptop PCs.
+.Nm Zzz
+suspends the system by controlling APM.
+.Pp
+The following options are available for
+.Nm apm
+(no options are available for
+.Nm zzz
+).
+If no options are specified,
+.Nm apm
+displays information and current status of APM in verbose mode.
+.Bl -tag -width indent
+.It Fl a
+Display the current AC-line status as an integer value. The values
+0 and 1 correspond to the
+.Dq off-line
+state or
+.Dq on-line
+state, respectively.
+.It Fl b
+Display an integer value reflecting the current battery status.
+The values 0, 1, 2, 3, correspond to the
+.Dq high
+status,
+.Dq low
+status,
+.Dq critical
+status,
+.Dq charging
+status respectively.
+.It Fl d
+Disable/enable suspending of the display separately from a normal suspend
+using using the values
+.Ar 1
+or
+.Ar 0
+respectively.
+.It Fl l
+Display the remaining battery percentage. If your laptop does not
+support this function, 255 is displayed.
+.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 z
+Suspend the system. It is equivalent to
+.Nm zzz.
+.Sh BUGS
+Some APM implementations do not support parameters needed by
+.Nm apm.
+On such systems,
+.Nm apm
+displays them as unknown.
+.Pp
+Some APM implementations cannot handle events such as pushing the
+power button or closing the cover. On such implementations, the system
+.Ar must
+be suspended
+.Ar only
+by using
+.Nm apm
+or
+.Nm zzz.
+.Sh SEE ALSO
+.Xr apm 4 ,
+.Xr apmconf 8
+.Sh AUTHOR
+HOSOKAWA, Tatsumi <hosokawa@mt.cs.keio.ac.jp> (Keio Univ., Japan)
diff --git a/usr.sbin/apm/apm.c b/usr.sbin/apm/apm.c
new file mode 100644
index 0000000..98e9852
--- /dev/null
+++ b/usr.sbin/apm/apm.c
@@ -0,0 +1,187 @@
+/*
+ * apm / zzz APM BIOS utility for FreeBSD
+ *
+ * Copyright (C) 1994-1996 by HOSOKAWA Tatasumi <hosokawa@mt.cs.keio.ac.jp>
+ *
+ * This software may be used, modified, copied, distributed, and sold,
+ * in both source and binary form provided that the above copyright and
+ * these terms are retained. Under no circumstances is the author
+ * responsible for the proper functioning of this software, nor does
+ * the author assume any responsibility for damages incurred with its
+ * use.
+ *
+ * Sep., 1994 Implemented on FreeBSD 1.1.5.1R (Toshiba AVS001WD)
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id: apm.c,v 1.10 1997/09/02 06:36:39 charnier Exp $";
+#endif /* not lint */
+
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/file.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+#include <machine/apm_bios.h>
+
+#define APMDEV "/dev/apm"
+
+void
+usage()
+{
+ fprintf(stderr, "%s\n%s\n",
+ "usage: apm [-ablsz] [-d 1|0]",
+ " zzz");
+ exit(1);
+}
+
+void
+apm_suspend(int fd)
+{
+ if (ioctl(fd, APMIO_SUSPEND, NULL) == -1)
+ err(1, NULL);
+}
+
+void
+apm_getinfo(int fd, apm_info_t aip)
+{
+ if (ioctl(fd, APMIO_GETINFO, aip) == -1)
+ err(1, NULL);
+}
+
+void
+print_all_info(apm_info_t aip)
+{
+ printf("APM version: %d.%d\n", aip->ai_major, aip->ai_minor);
+ printf("APM Managment: %s\n", (aip->ai_status ? "Enabled" : "Disabled"));
+ printf("AC Line status: ");
+ if (aip->ai_acline == 255)
+ printf("unknown");
+ else if (aip->ai_acline > 1)
+ printf("invalid value (0x%x)", aip->ai_acline);
+ else {
+ char messages[][10] = {"off-line", "on-line"};
+ printf("%s", messages[aip->ai_acline]);
+ }
+ printf("\n");
+ printf("Battery status: ");
+ if (aip->ai_batt_stat == 255)
+ printf("unknown");
+ else if (aip->ai_batt_stat > 3)
+ printf("invalid value (0x%x)", aip->ai_batt_stat);
+ else {
+ char messages[][10] = {"high", "low", "critical", "charging"};
+ printf("%s", messages[aip->ai_batt_stat]);
+ }
+ printf("\n");
+ printf("Remaining battery life: ");
+ if (aip->ai_batt_life == 255)
+ printf("unknown");
+ else if (aip->ai_batt_life <= 100)
+ printf("%d%%", aip->ai_batt_life);
+ else
+ printf("invalid value (0x%x)", aip->ai_batt_life);
+ printf("\n");
+}
+
+
+/*
+ * currently, it can turn off the display, but the display never comes
+ * back until the machine suspend/resumes :-).
+ */
+void
+apm_display(int fd, int newstate)
+{
+ if (ioctl(fd, APMIO_DISPLAY, &newstate) == -1)
+ err(1, NULL);
+}
+
+
+int
+main(int argc, char *argv[])
+{
+ int c, fd;
+ int sleep = 0, all_info = 1, apm_status = 0, batt_status = 0;
+ int display = 0, batt_life = 0, ac_status = 0;
+ char *cmdname;
+
+
+ if ((cmdname = strrchr(argv[0], '/')) != NULL)
+ cmdname++;
+ else
+ cmdname = argv[0];
+
+ if (strcmp(cmdname, "zzz") == 0) {
+ sleep = 1;
+ all_info = 0;
+ goto finish_option;
+ }
+ while ((c = getopt(argc, argv, "ablszd:")) != -1) {
+ switch (c) {
+ case 'a':
+ ac_status = 1;
+ all_info = 0;
+ break;
+ case 'b':
+ batt_status = 1;
+ all_info = 0;
+ break;
+ case 'd':
+ display = *optarg - '0';
+ if (display < 0 || display > 1) {
+ warnx("argument of option '-%c' is invalid", c);
+ usage();
+ }
+ display++;
+ all_info = 0;
+ break;
+ case 'l':
+ batt_life = 1;
+ all_info = 0;
+ break;
+ case 's':
+ apm_status = 1;
+ all_info = 0;
+ break;
+ case 'z':
+ sleep = 1;
+ all_info = 0;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+ }
+finish_option:
+ fd = open(APMDEV, O_RDWR);
+ if (fd == -1) {
+ warn("can't open %s", APMDEV);
+ return 1;
+ }
+ if (sleep)
+ apm_suspend(fd);
+ else {
+ struct apm_info info;
+
+ apm_getinfo(fd, &info);
+ if (all_info)
+ print_all_info(&info);
+ if (batt_status)
+ printf("%d\n", info.ai_batt_stat);
+ if (batt_life)
+ printf("%d\n", info.ai_batt_life);
+ if (ac_status)
+ printf("%d\n", info.ai_acline);
+ if (apm_status)
+ printf("%d\n", info.ai_status);
+ if (display)
+ apm_display(fd, display - 1);
+ }
+ close(fd);
+ return 0;
+}
diff --git a/usr.sbin/apmconf/Makefile b/usr.sbin/apmconf/Makefile
new file mode 100644
index 0000000..b451367
--- /dev/null
+++ b/usr.sbin/apmconf/Makefile
@@ -0,0 +1,5 @@
+
+PROG= apmconf
+MAN8= apmconf.8
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/apmconf/apmconf.8 b/usr.sbin/apmconf/apmconf.8
new file mode 100644
index 0000000..7296eab
--- /dev/null
+++ b/usr.sbin/apmconf/apmconf.8
@@ -0,0 +1,60 @@
+.\" LP (Laptop Package)
+.\"
+.\" Copyright (c) 1994 by HOSOKAWA, Tatsumi <hosokawa@mt.cs.keio.ac.jp>
+.\"
+.\" This software may be used, modified, copied, and distributed, in
+.\" both source and binary form provided that the above copyright and
+.\" these terms are retained. Under no circumstances is the author
+.\" responsible for the proper functioning of this software, nor does
+.\" the author assume any responsibility for damages incurred with its
+.\" use.
+.Dd November 1, 1994
+.Dt APMCONF 8
+.Os
+.Sh NAME
+.Nm apmconf
+.Nd configure APM BIOS driver
+.Sh SYNOPSIS
+.Nm apmconf
+.Op Fl e
+.Op Fl d
+.Op Fl h
+.Op Fl t
+.Sh DESCRIPTION
+.Nm Apmconf
+is used to configure the APM (Advanced Power Management) BIOS driver
+.Xr apm 4
+on laptop PCs.
+.Pp
+The following options are available.
+.Bl -tag -width indent
+.It Fl e
+Enable power management.
+.It Fl d
+Disable power management.
+.El
+.Pp
+These options enable/disable power management functions provided by
+.Xr apm 4 .
+.Bl -tag -width indent
+.It Fl h
+Enable HLT instruction in kernel context switch routine.
+.It Fl t
+Disable HLT instruction in kernel context switch routine.
+.El
+.Pp
+These options are not necessary for almost all APM implementations,
+but for some implementations whose
+.Dq Pa Idle CPU
+call executes both CPU clock slowdown and HLT instruction,
+.Fl t
+is necessary to prevent the system from reducing its peak performance.
+See
+.Xr apm 4
+for details.
+.Sh SEE ALSO
+.Xr apm 4 ,
+.Xr apm 8 ,
+.Xr zzz 8
+.Sh AUTHOR
+HOSOKAWA, Tatsumi <hosokawa@mt.cs.keio.ac.jp> (Keio Univ., Japan)
diff --git a/usr.sbin/apmconf/apmconf.c b/usr.sbin/apmconf/apmconf.c
new file mode 100644
index 0000000..de0822e
--- /dev/null
+++ b/usr.sbin/apmconf/apmconf.c
@@ -0,0 +1,147 @@
+/*
+ * LP (Laptop Package)
+ *
+ * Copyright (C) 1994 by HOSOKAWA Tatasumi <hosokawa@mt.cs.keio.ac.jp>
+ *
+ * This software may be used, modified, copied, distributed, and sold,
+ * in both source and binary form provided that the above copyright and
+ * these terms are retained. Under no circumstances is the author
+ * responsible for the proper functioning of this software, nor does
+ * the author assume any responsibility for damages incurred with its
+ * use.
+ *
+ * Sep., 1994 Implemented on FreeBSD 1.1.5.1R (Toshiba AVS001WD)
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/file.h>
+#include <sys/ioctl.h>
+#include <machine/apm_bios.h>
+
+#define APMDEV "/dev/apm"
+
+static int enable = 0, disable = 0;
+static int haltcpu = 0, nothaltcpu = 0;
+static int main_argc;
+static char **main_argv;
+
+static void
+parse_option(void)
+{
+ int i, option;
+ enum {OPT_NONE, OPT_ENABLE, OPT_DISABLE, OPT_HALTCPU, OPT_NOTHALTCPU} mode;
+
+ for (i = 1; i < main_argc; i++) {
+ option = 0;
+ mode = OPT_NONE;
+ if (main_argv[i][0] == '-') {
+ switch (main_argv[i][1]) {
+ case 'e':
+ mode = OPT_ENABLE;
+ option = 0;
+ break;
+ case 'd':
+ mode = OPT_DISABLE;
+ option = 0;
+ break;
+ case 'h':
+ mode = OPT_HALTCPU;
+ option = 0;
+ break;
+ case 't':
+ mode = OPT_NOTHALTCPU;
+ option = 0;
+ break;
+ default:
+ errx(1, "unknown option '%s'", main_argv[i]);
+ }
+ }
+ if (option) {
+ if (i == main_argc - 1)
+ errx(1, "option '%s' needs arguments", main_argv[i]);
+ optarg = main_argv[++i];
+ }
+
+ switch (mode) {
+ case OPT_ENABLE:
+ enable = 1;
+ break;
+ case OPT_DISABLE:
+ disable = 1;
+ break;
+ case OPT_HALTCPU:
+ haltcpu = 1;
+ break;
+ case OPT_NOTHALTCPU:
+ nothaltcpu = 1;
+ break;
+ case OPT_NONE:
+ break;
+ }
+ }
+}
+
+static void
+enable_apm(int dh)
+{
+ if (ioctl(dh, APMIO_ENABLE, NULL) == -1)
+ errx(1, "can't ioctl APMIO_ENABLE");
+}
+
+static void
+disable_apm(int dh)
+{
+ if (ioctl(dh, APMIO_DISABLE, NULL) == -1)
+ errx(1, "can't ioctl APMIO_DISABLE");
+}
+
+static void
+haltcpu_apm(int dh)
+{
+ if (ioctl(dh, APMIO_HALTCPU, NULL) == -1)
+ errx(1, "can't ioctl APMIO_HALTCPU");
+}
+
+static void
+nothaltcpu_apm(int dh)
+{
+ if (ioctl(dh, APMIO_NOTHALTCPU, NULL) == -1)
+ errx(1, "can't ioctl APMIO_NOTHALTCPU");
+}
+
+int
+main(int argc, char *argv[])
+{
+ int dh;
+
+ main_argc = argc;
+ main_argv = argv;
+ if ((dh = open(APMDEV, O_RDWR)) == -1)
+ errx(1, "can't open '%s'", APMDEV);
+ parse_option();
+
+ /* disable operation is executed first */
+ if (disable) {
+ disable_apm(dh);
+ }
+ if (haltcpu) {
+ haltcpu_apm(dh);
+ }
+ if (nothaltcpu) {
+ nothaltcpu_apm(dh);
+ }
+ /* enable operation is executed last */
+ if (enable) {
+ enable_apm(dh);
+ }
+ return 0;
+}
diff --git a/usr.sbin/arp/Makefile b/usr.sbin/arp/Makefile
new file mode 100644
index 0000000..a26439f
--- /dev/null
+++ b/usr.sbin/arp/Makefile
@@ -0,0 +1,7 @@
+# @(#)Makefile 8.2 (Berkeley) 4/18/94
+
+PROG= arp
+MAN8= arp.8
+MAN4= arp.4
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/arp/arp.4 b/usr.sbin/arp/arp.4
new file mode 100644
index 0000000..92caed3
--- /dev/null
+++ b/usr.sbin/arp/arp.4
@@ -0,0 +1,142 @@
+.\" Copyright (c) 1985, 1986, 1988, 1994
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)arp4.4 6.5 (Berkeley) 4/18/94
+.\"
+.Dd April 18, 1994
+.Dt ARP 4
+.Os BSD 4
+.Sh NAME
+.Nm arp
+.Nd Address Resolution Protocol
+.Sh SYNOPSIS
+.Em "pseudo-device ether"
+.Sh DESCRIPTION
+The Address Resolution Protocol (ARP) is a protocol used to dynamically
+map between Internet host addresses and 10Mb/s Ethernet addresses.
+It is used by all the 10Mb/s Ethernet interface drivers.
+It is not specific to Internet protocols or to 10Mb/s Ethernet,
+but this implementation currently supports only that combination.
+.Pp
+ARP caches Internet-Ethernet address mappings.
+When an interface requests a mapping for an address not in the cache,
+ARP queues the message which requires the mapping and broadcasts
+a message on the associated network requesting the address mapping.
+If a response is provided, the new mapping is cached and any pending
+message is transmitted.
+ARP will queue at most one packet while waiting for a response to a
+mapping request;
+only the most recently ``transmitted'' packet is kept.
+If the target host does not respond after several requests,
+the host is considered to be down for a short period (normally 20 seconds),
+allowing an error to be returned to transmission attempts during this
+interval.
+The error is
+.Li EHOSTDOWN
+for a non-responding destination host, and
+.Li EHOSTUNREACH
+for a non-responding router.
+.Pp
+The ARP cache is stored in the system routing table as
+dynamically-created host routes.
+The route to a directly-attached Ethernet network is installed as a
+.Dq cloning
+route (one with the
+.Li RTF_CLONING
+flag set),
+causing routes to individual hosts on that network to be created on
+demand.
+These routes time out periodically (normally 20 minutes after validated;
+entries are not validated when not in use).
+An entry for a host which is not responding is a
+.Dq reject
+route (one with the
+.Li RTF_REJECT
+flag set).
+.Pp
+ARP entries may be added, deleted or changed with the
+.Xr arp 8
+utility.
+Manually-added entries may be temporary or permanent,
+and may be
+.Dq published ,
+in which case the system will respond to ARP requests for that host
+as if it were the target of the request.
+.Pp
+In the past,
+ARP was used to negotiate the use of a trailer encapsulation.
+This is no longer supported.
+.Pp
+ARP watches passively for hosts impersonating the local host (i.e. a host
+which responds to an ARP mapping request for the local host's address).
+.Sh DIAGNOSTICS
+.Em "arp: %x:%x:%x:%x:%x:%x is using my IP address %d.%d.%d.%d!" :
+ARP has discovered another host on the local network which responds to
+mapping requests for its own Internet address with a different Ethernet
+address, generally indicating that two hosts are attempting to use the
+same Internet address.
+.Pp
+.Em "arp: ether address is broadcast for IP address %d.%d.%d.%d!" :
+ARP requested information for a host, and received an answer indicating
+that the host's ethernet address is the ethernet broadcast address.
+This indicates a misconfigured or broken device.
+.Pp
+.Em "arp: %d.%d.%d.%d moved from %x:%x:%x:%x:%x:%x to %x:%x:%x:%x:%x:%x" :
+ARP had a cached value for the ethernet address of the referenced host,
+but received a reply indicating that the host is at a new address. This
+can happen normally when host hardware addresses change, or when a mobile
+node arrives or leaves the local subnet. It can also indicate a problem
+with proxy ARP.
+.Pp
+.Em "arpresolve: can't allocate llinfo for %d.%d.%d.%d" :
+The route for the referenced host points to a device upon which ARP is
+required, but ARP was unable to allocate a routing table entry in which
+to store the host's MAC address. This usually points to a misconfigured
+routing table. It can also occur if the kernel cannot allocate memory.
+.Sh SEE ALSO
+.Xr inet 4 ,
+.Xr route 4 ,
+.Xr arp 8 ,
+.Xr ifconfig 8 ,
+.Xr route 8
+.sp
+.Rs
+.%A Plummer, D.
+.%B "An Ethernet Address Resolution Protocol"
+.%T RFC826
+.Re
+.Rs
+.%A Leffler, S.J.
+.%A Karels, M.J.
+.%B "Trailer Encapsulations
+.%T RFC893
+.Re
+
diff --git a/usr.sbin/arp/arp.8 b/usr.sbin/arp/arp.8
new file mode 100644
index 0000000..fc7df53
--- /dev/null
+++ b/usr.sbin/arp/arp.8
@@ -0,0 +1,140 @@
+.\" Copyright (c) 1985, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)arp.8 8.1 (Berkeley) 6/6/93
+.\"
+.Dd June 6, 1993
+.Dt ARP 8
+.Os BSD 4.3
+.Sh NAME
+.Nm arp
+.Nd address resolution display and control
+.Sh SYNOPSIS
+.Nm arp
+.Op Fl n
+.Ar hostname
+.Nm arp
+.Op Fl n
+.Fl a
+.Nm arp
+.Fl d Ar hostname
+.Nm arp
+.Fl s Ar hostname ether_addr
+.Op Ar temp
+.Op Ar pub
+.Nm arp
+.Fl S Ar hostname ether_addr
+.Op Ar temp
+.Op Ar pub
+.Nm arp
+.Fl f Ar filename
+.Sh DESCRIPTION
+The
+.Nm
+program displays and modifies the Internet-to-Ethernet address translation
+tables used by the address resolution protocol
+.Pq Xr arp 4 .
+With no flags, the program displays the current
+.Tn ARP
+entry for
+.Ar hostname .
+The host may be specified by name or by number,
+using Internet dot notation.
+.Pp
+Available options:
+.Bl -tag -width Ds
+.It Fl a
+The program displays 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.
+.It Fl n
+Show network addresses as numbers (normally
+.Nm
+attempts to display addresses symbolically).
+.It Fl s Ar hostname ether_addr
+Create an
+.Tn ARP
+entry for the host called
+.Ar hostname
+with the Ethernet address
+.Ar ether_addr .
+The Ethernet address is given as six hex bytes separated by colons.
+The entry will be permanent unless the word
+.Ar temp
+is given in the command.
+If the word
+.Ar pub
+is given, the entry will be "published"; i.e., this system will
+act as an
+.Tn ARP
+server,
+responding to requests for
+.Ar hostname
+even though the host address is not its own.
+In this case the ether_addr can be given as ``auto''
+in which case the interfaces on this host will be examined,
+and if one of them is found to occupy the same subnet, its
+ether_addr will be used.
+.It Fl S Ar hostname ether_addr
+Is just like
+.Fl s
+except any existing arp entry for this host will be deleted first.
+.It Fl f
+Causes the file
+.Ar filename
+to be read and multiple entries to be set in the
+.Tn ARP
+tables. Entries
+in the file should be of the form
+.Pp
+.Bd -filled -offset indent -compact
+.Ar hostname ether_addr
+.Op Ar temp
+.Op Ar pub
+.Ed
+.Pp
+with argument meanings as given above.
+.El
+.Sh SEE ALSO
+.Xr inet 3 ,
+.Xr arp 4 ,
+.Xr ifconfig 8
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 4.3 .
diff --git a/usr.sbin/arp/arp.c b/usr.sbin/arp/arp.c
new file mode 100644
index 0000000..f27230e
--- /dev/null
+++ b/usr.sbin/arp/arp.c
@@ -0,0 +1,635 @@
+/*
+ * Copyright (c) 1984, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Sun Microsystems, Inc.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char const copyright[] =
+"@(#) Copyright (c) 1984, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char const sccsid[] = "@(#)from: arp.c 8.2 (Berkeley) 1/2/94";
+#endif
+static const char rcsid[] =
+ "$Id$";
+#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_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 <arpa/inet.h>
+
+#include <err.h>
+#include <errno.h>
+#include <netdb.h>
+#include <nlist.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <unistd.h>
+
+void dump(u_long addr);
+int delete(char *host, char *info);
+void ether_print(u_char *cp);
+void usage(void);
+int set(int argc, char **argv);
+void get(char *host);
+int file(char *name);
+void getsocket(void);
+int my_ether_aton(char *a, u_char *n);
+int rtmsg(int cmd);
+int get_ether_addr(u_long ipaddr, u_char *hwaddr);
+
+static int pid;
+static int nflag;
+static int s = -1;
+
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ int ch;
+
+ pid = getpid();
+ while ((ch = getopt(argc, argv, "andfsS")) != -1)
+ switch((char)ch) {
+ case 'a':
+ dump(0);
+ exit(0);
+ case 'd':
+ if (argc < 3 || argc > 4)
+ usage();
+ delete(argv[2], argv[3]);
+ exit(0);
+ case 'n':
+ nflag = 1;
+ continue;
+ case 'S':
+ delete(argv[2], NULL);
+ /* FALL THROUGH */
+ case 's':
+ if (argc < 4 || argc > 7)
+ usage();
+ exit(set(argc-2, &argv[2]) ? 1 : 0);
+ case 'f' :
+ if (argc != 3)
+ usage();
+ return (file(argv[2]));
+ case '?':
+ default:
+ usage();
+ }
+ if (argc != 2)
+ usage();
+ get(argv[1]);
+ exit(0);
+}
+
+/*
+ * Process a file to set standard arp entries
+ */
+int
+file(char *name)
+{
+ FILE *fp;
+ int i, retval;
+ char line[100], arg[5][50], *args[5];
+
+ if ((fp = fopen(name, "r")) == NULL)
+ errx(1, "cannot open %s", name);
+ args[0] = &arg[0][0];
+ args[1] = &arg[1][0];
+ args[2] = &arg[2][0];
+ args[3] = &arg[3][0];
+ args[4] = &arg[4][0];
+ retval = 0;
+ while(fgets(line, 100, fp) != NULL) {
+ i = sscanf(line, "%s %s %s %s %s", arg[0], arg[1], arg[2],
+ arg[3], arg[4]);
+ if (i < 2) {
+ warnx("bad line: %s", line);
+ retval = 1;
+ continue;
+ }
+ if (set(i, args))
+ retval = 1;
+ }
+ fclose(fp);
+ return (retval);
+}
+
+void
+getsocket(void)
+{
+ if (s < 0) {
+ s = socket(PF_ROUTE, SOCK_RAW, 0);
+ if (s < 0)
+ err(1, "socket");
+ }
+}
+
+struct sockaddr_in so_mask = {8, 0, 0, { 0xffffffff}};
+struct sockaddr_inarp blank_sin = {sizeof(blank_sin), AF_INET }, sin_m;
+struct sockaddr_dl blank_sdl = {sizeof(blank_sdl), AF_LINK }, sdl_m;
+int expire_time, flags, export_only, doing_proxy, found_entry;
+struct {
+ struct rt_msghdr m_rtm;
+ char m_space[512];
+} m_rtmsg;
+
+/*
+ * Set an individual arp entry
+ */
+int
+set(int argc, char **argv)
+{
+ struct hostent *hp;
+ register struct sockaddr_inarp *sin = &sin_m;
+ register struct sockaddr_dl *sdl;
+ register struct rt_msghdr *rtm = &(m_rtmsg.m_rtm);
+ u_char *ea;
+ char *host = argv[0], *eaddr = argv[1];
+
+ getsocket();
+ argc -= 2;
+ argv += 2;
+ sdl_m = blank_sdl;
+ sin_m = blank_sin;
+ sin->sin_addr.s_addr = inet_addr(host);
+ if (sin->sin_addr.s_addr == -1) {
+ if (!(hp = gethostbyname(host))) {
+ warnx("%s: %s", host, hstrerror(h_errno));
+ return (1);
+ }
+ bcopy((char *)hp->h_addr, (char *)&sin->sin_addr,
+ sizeof sin->sin_addr);
+ }
+ doing_proxy = flags = export_only = expire_time = 0;
+ while (argc-- > 0) {
+ if (strncmp(argv[0], "temp", 4) == 0) {
+ struct timeval time;
+ gettimeofday(&time, 0);
+ expire_time = time.tv_sec + 20 * 60;
+ }
+ else if (strncmp(argv[0], "pub", 3) == 0) {
+ flags |= RTF_ANNOUNCE;
+ doing_proxy = SIN_PROXY;
+ } else if (strncmp(argv[0], "trail", 5) == 0) {
+ printf("%s: Sending trailers is no longer supported\n",
+ host);
+ }
+ argv++;
+ }
+ ea = (u_char *)LLADDR(&sdl_m);
+ if (doing_proxy && !strcmp(eaddr, "auto")) {
+ if (!get_ether_addr(sin->sin_addr.s_addr, ea)) {
+ return (1);
+ }
+ sdl_m.sdl_alen = 6;
+ } else {
+ if (my_ether_aton(eaddr, ea) == 0)
+ sdl_m.sdl_alen = 6;
+ }
+tryagain:
+ if (rtmsg(RTM_GET) < 0) {
+ warn("%s", host);
+ return (1);
+ }
+ sin = (struct sockaddr_inarp *)(rtm + 1);
+ sdl = (struct sockaddr_dl *)(sin->sin_len + (char *)sin);
+ if (sin->sin_addr.s_addr == sin_m.sin_addr.s_addr) {
+ if (sdl->sdl_family == AF_LINK &&
+ (rtm->rtm_flags & RTF_LLINFO) &&
+ !(rtm->rtm_flags & RTF_GATEWAY)) switch (sdl->sdl_type) {
+ case IFT_ETHER: case IFT_FDDI: case IFT_ISO88023:
+ case IFT_ISO88024: case IFT_ISO88025:
+ goto overwrite;
+ }
+ if (doing_proxy == 0) {
+ printf("set: can only proxy for %s\n", host);
+ return (1);
+ }
+ if (sin_m.sin_other & SIN_PROXY) {
+ printf("set: proxy entry exists for non 802 device\n");
+ return(1);
+ }
+ sin_m.sin_other = SIN_PROXY;
+ export_only = 1;
+ goto tryagain;
+ }
+overwrite:
+ if (sdl->sdl_family != AF_LINK) {
+ printf("cannot intuit interface index and type for %s\n", host);
+ return (1);
+ }
+ sdl_m.sdl_type = sdl->sdl_type;
+ sdl_m.sdl_index = sdl->sdl_index;
+ return (rtmsg(RTM_ADD));
+}
+
+/*
+ * Display an individual arp entry
+ */
+void
+get(char *host)
+{
+ struct hostent *hp;
+ struct sockaddr_inarp *sin = &sin_m;
+
+ sin_m = blank_sin;
+ sin->sin_addr.s_addr = inet_addr(host);
+ if (sin->sin_addr.s_addr == -1) {
+ if (!(hp = gethostbyname(host)))
+ errx(1, "%s: %s", host, hstrerror(h_errno));
+ bcopy((char *)hp->h_addr, (char *)&sin->sin_addr,
+ sizeof sin->sin_addr);
+ }
+ dump(sin->sin_addr.s_addr);
+ if (found_entry == 0) {
+ printf("%s (%s) -- no entry\n",
+ host, inet_ntoa(sin->sin_addr));
+ exit(1);
+ }
+}
+
+/*
+ * Delete an arp entry
+ */
+int
+delete(char *host, char *info)
+{
+ struct hostent *hp;
+ register struct sockaddr_inarp *sin = &sin_m;
+ register struct rt_msghdr *rtm = &m_rtmsg.m_rtm;
+ struct sockaddr_dl *sdl;
+
+ if (info && strncmp(info, "pro", 3) )
+ export_only = 1;
+ getsocket();
+ sin_m = blank_sin;
+ sin->sin_addr.s_addr = inet_addr(host);
+ if (sin->sin_addr.s_addr == -1) {
+ if (!(hp = gethostbyname(host))) {
+ warnx("%s: %s", host, hstrerror(h_errno));
+ return (1);
+ }
+ bcopy((char *)hp->h_addr, (char *)&sin->sin_addr,
+ sizeof sin->sin_addr);
+ }
+tryagain:
+ if (rtmsg(RTM_GET) < 0) {
+ warn("%s", host);
+ return (1);
+ }
+ sin = (struct sockaddr_inarp *)(rtm + 1);
+ sdl = (struct sockaddr_dl *)(sin->sin_len + (char *)sin);
+ if (sin->sin_addr.s_addr == sin_m.sin_addr.s_addr) {
+ if (sdl->sdl_family == AF_LINK &&
+ (rtm->rtm_flags & RTF_LLINFO) &&
+ !(rtm->rtm_flags & RTF_GATEWAY)) switch (sdl->sdl_type) {
+ case IFT_ETHER: case IFT_FDDI: case IFT_ISO88023:
+ case IFT_ISO88024: case IFT_ISO88025:
+ goto delete;
+ }
+ }
+ if (sin_m.sin_other & SIN_PROXY) {
+ fprintf(stderr, "delete: can't locate %s\n",host);
+ return (1);
+ } else {
+ sin_m.sin_other = SIN_PROXY;
+ goto tryagain;
+ }
+delete:
+ if (sdl->sdl_family != AF_LINK) {
+ printf("cannot locate %s\n", host);
+ return (1);
+ }
+ if (rtmsg(RTM_DELETE) == 0) {
+ printf("%s (%s) deleted\n", host, inet_ntoa(sin->sin_addr));
+ return (0);
+ }
+ return (1);
+}
+
+/*
+ * Dump the entire arp table
+ */
+void
+dump(u_long addr)
+{
+ int mib[6];
+ size_t needed;
+ char *host, *lim, *buf, *next;
+ struct rt_msghdr *rtm;
+ struct sockaddr_inarp *sin;
+ struct sockaddr_dl *sdl;
+ extern int h_errno;
+ struct hostent *hp;
+
+ mib[0] = CTL_NET;
+ mib[1] = PF_ROUTE;
+ mib[2] = 0;
+ mib[3] = AF_INET;
+ mib[4] = NET_RT_FLAGS;
+ mib[5] = RTF_LLINFO;
+ if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0)
+ errx(1, "route-sysctl-estimate");
+ if ((buf = malloc(needed)) == NULL)
+ errx(1, "malloc");
+ if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0)
+ errx(1, "actual retrieval of routing table");
+ lim = buf + needed;
+ for (next = buf; next < lim; next += rtm->rtm_msglen) {
+ rtm = (struct rt_msghdr *)next;
+ sin = (struct sockaddr_inarp *)(rtm + 1);
+ sdl = (struct sockaddr_dl *)(sin + 1);
+ if (addr) {
+ if (addr != sin->sin_addr.s_addr)
+ continue;
+ found_entry = 1;
+ }
+ if (nflag == 0)
+ hp = gethostbyaddr((caddr_t)&(sin->sin_addr),
+ sizeof sin->sin_addr, AF_INET);
+ else
+ hp = 0;
+ if (hp)
+ host = hp->h_name;
+ else {
+ host = "?";
+ if (h_errno == TRY_AGAIN)
+ nflag = 1;
+ }
+ printf("%s (%s) at ", host, inet_ntoa(sin->sin_addr));
+ if (sdl->sdl_alen)
+ ether_print(LLADDR(sdl));
+ else
+ printf("(incomplete)");
+ if (rtm->rtm_rmx.rmx_expire == 0)
+ printf(" permanent");
+ if (sin->sin_other & SIN_PROXY)
+ printf(" published (proxy only)");
+ if (rtm->rtm_addrs & RTA_NETMASK) {
+ sin = (struct sockaddr_inarp *)
+ (sdl->sdl_len + (char *)sdl);
+ if (sin->sin_addr.s_addr == 0xffffffff)
+ printf(" published");
+ if (sin->sin_len != 8)
+ printf("(wierd)");
+ }
+ printf("\n");
+ }
+}
+
+void
+ether_print(u_char *cp)
+{
+ printf("%x:%x:%x:%x:%x:%x", cp[0], cp[1], cp[2], cp[3], cp[4], cp[5]);
+}
+
+int
+my_ether_aton(char *a, u_char *n)
+{
+ int i, o[6];
+
+ i = sscanf(a, "%x:%x:%x:%x:%x:%x", &o[0], &o[1], &o[2],
+ &o[3], &o[4], &o[5]);
+ if (i != 6) {
+ warnx("invalid Ethernet address '%s'", a);
+ return (1);
+ }
+ for (i=0; i<6; i++)
+ n[i] = o[i];
+ return (0);
+}
+
+void
+usage(void)
+{
+ fprintf(stderr, "%s\n%s\n%s\n%s\n%s\n%s\n",
+ "usage: arp [-n] hostname",
+ " arp [-n] -a [kernel] [kernel_memory]",
+ " arp -d hostname",
+ " arp -s hostname ether_addr [temp] [pub]",
+ " arp -S hostname ether_addr [temp] [pub]",
+ " arp -f filename");
+ exit(1);
+}
+
+int
+rtmsg(int cmd)
+{
+ static int seq;
+ int rlen;
+ register struct rt_msghdr *rtm = &m_rtmsg.m_rtm;
+ register char *cp = m_rtmsg.m_space;
+ register int l;
+
+ errno = 0;
+ if (cmd == RTM_DELETE)
+ goto doit;
+ bzero((char *)&m_rtmsg, sizeof(m_rtmsg));
+ rtm->rtm_flags = flags;
+ rtm->rtm_version = RTM_VERSION;
+
+ switch (cmd) {
+ default:
+ errx(1, "internal wrong cmd");
+ case RTM_ADD:
+ rtm->rtm_addrs |= RTA_GATEWAY;
+ rtm->rtm_rmx.rmx_expire = expire_time;
+ rtm->rtm_inits = RTV_EXPIRE;
+ rtm->rtm_flags |= (RTF_HOST | RTF_STATIC);
+ sin_m.sin_other = 0;
+ if (doing_proxy) {
+ if (export_only)
+ sin_m.sin_other = SIN_PROXY;
+ else {
+ rtm->rtm_addrs |= RTA_NETMASK;
+ rtm->rtm_flags &= ~RTF_HOST;
+ }
+ }
+ /* FALLTHROUGH */
+ case RTM_GET:
+ rtm->rtm_addrs |= RTA_DST;
+ }
+#define NEXTADDR(w, s) \
+ if (rtm->rtm_addrs & (w)) { \
+ bcopy((char *)&s, cp, sizeof(s)); cp += sizeof(s);}
+
+ NEXTADDR(RTA_DST, sin_m);
+ NEXTADDR(RTA_GATEWAY, sdl_m);
+ NEXTADDR(RTA_NETMASK, so_mask);
+
+ rtm->rtm_msglen = cp - (char *)&m_rtmsg;
+doit:
+ l = rtm->rtm_msglen;
+ rtm->rtm_seq = ++seq;
+ rtm->rtm_type = cmd;
+ if ((rlen = write(s, (char *)&m_rtmsg, l)) < 0) {
+ if (errno != ESRCH || cmd != RTM_DELETE) {
+ warn("writing to routing socket");
+ return (-1);
+ }
+ }
+ do {
+ l = read(s, (char *)&m_rtmsg, sizeof(m_rtmsg));
+ } while (l > 0 && (rtm->rtm_seq != seq || rtm->rtm_pid != pid));
+ if (l < 0)
+ warn("read from routing socket");
+ return (0);
+}
+
+/*
+ * get_ether_addr - get the hardware address of an interface on the
+ * the same subnet as ipaddr.
+ */
+#define MAX_IFS 32
+
+int
+get_ether_addr(u_long ipaddr, u_char *hwaddr)
+{
+ struct ifreq *ifr, *ifend, *ifp;
+ u_long ina, mask;
+ struct sockaddr_dl *dla;
+ struct ifreq ifreq;
+ struct ifconf ifc;
+ struct ifreq ifs[MAX_IFS];
+ int s;
+
+ s = socket(AF_INET, SOCK_DGRAM, 0);
+ if (s < 0)
+ err(1, "socket");
+
+ ifc.ifc_len = sizeof(ifs);
+ ifc.ifc_req = ifs;
+ if (ioctl(s, SIOCGIFCONF, &ifc) < 0) {
+ warnx("ioctl(SIOCGIFCONF)");
+ close(s);
+ return 0;
+ }
+
+ /*
+ * Scan through looking for an interface with an Internet
+ * address on the same subnet as `ipaddr'.
+ */
+ ifend = (struct ifreq *) (ifc.ifc_buf + ifc.ifc_len);
+ for (ifr = ifc.ifc_req; ifr < ifend; ) {
+ if (ifr->ifr_addr.sa_family == AF_INET) {
+ ina = ((struct sockaddr_in *)
+ &ifr->ifr_addr)->sin_addr.s_addr;
+ strncpy(ifreq.ifr_name, ifr->ifr_name,
+ sizeof(ifreq.ifr_name));
+ /*
+ * Check that the interface is up,
+ * and not point-to-point or loopback.
+ */
+ if (ioctl(s, SIOCGIFFLAGS, &ifreq) < 0)
+ continue;
+ if ((ifreq.ifr_flags &
+ (IFF_UP|IFF_BROADCAST|IFF_POINTOPOINT|
+ IFF_LOOPBACK|IFF_NOARP))
+ != (IFF_UP|IFF_BROADCAST))
+ goto nextif;
+ /*
+ * Get its netmask and check that it's on
+ * the right subnet.
+ */
+ if (ioctl(s, SIOCGIFNETMASK, &ifreq) < 0)
+ continue;
+ mask = ((struct sockaddr_in *)
+ &ifreq.ifr_addr)->sin_addr.s_addr;
+ if ((ipaddr & mask) != (ina & mask))
+ goto nextif;
+ break;
+ }
+nextif:
+ ifr = (struct ifreq *)
+ ((char *)&ifr->ifr_addr + ifr->ifr_addr.sa_len);
+ }
+
+ if (ifr >= ifend) {
+ close(s);
+ return 0;
+ }
+
+ /*
+ * Now scan through again looking for a link-level address
+ * for this interface.
+ */
+ ifp = ifr;
+ for (ifr = ifc.ifc_req; ifr < ifend; ) {
+ if (strcmp(ifp->ifr_name, ifr->ifr_name) == 0
+ && ifr->ifr_addr.sa_family == AF_LINK) {
+ /*
+ * Found the link-level address - copy it out
+ */
+ dla = (struct sockaddr_dl *) &ifr->ifr_addr;
+ memcpy(hwaddr, LLADDR(dla), dla->sdl_alen);
+ close (s);
+ printf("using interface %s for proxy with address ",
+ ifp->ifr_name);
+ ether_print(hwaddr);
+ printf("\n");
+ return dla->sdl_alen;
+ }
+ ifr = (struct ifreq *)
+ ((char *)&ifr->ifr_addr + ifr->ifr_addr.sa_len);
+ }
+ return 0;
+}
diff --git a/usr.sbin/bad144/Makefile b/usr.sbin/bad144/Makefile
new file mode 100644
index 0000000..305ce73
--- /dev/null
+++ b/usr.sbin/bad144/Makefile
@@ -0,0 +1,10 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= bad144
+SRCS= bad144.c dkcksum.c
+MAN8= bad144.8
+MLINKS= bad144.8 ../bad144.8
+MANSUBDIR=/${MACHINE}
+.PATH: ${.CURDIR}/../../sbin/disklabel
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/bad144/bad144.8 b/usr.sbin/bad144/bad144.8
new file mode 100644
index 0000000..d1418cb
--- /dev/null
+++ b/usr.sbin/bad144/bad144.8
@@ -0,0 +1,191 @@
+.\" Copyright (c) 1980, 1988, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)bad144.8 8.1 (Berkeley) 6/6/93
+.\"
+.Dd May 13, 1995
+.Dt BAD144 8
+.Os BSD 4
+.Sh NAME
+.Nm bad144
+.Nd read/write dec standard 144 bad sector information
+.Sh SYNOPSIS
+.Nm bad144
+.Op Fl c
+.Op Fl f
+.Op Fl v
+.Ar disk
+.Oo
+.Ar sno
+.Op Ar bad ...
+.Oc
+.Nm bad144
+.Fl a
+.Op Fl c
+.Op Fl f
+.Op Fl v
+.Ar disk
+.Op Ar bad ...
+.Nm bad144
+.Op Fl s
+.Op Fl v
+.Ar disk
+.Sh DESCRIPTION
+.Nm Bad144
+can be used to inspect the information stored on a disk that is used by
+the disk drivers to implement bad sector forwarding.
+.Pp
+Available options:
+.Pp
+.Bl -tag -width Ds
+.It Fl a
+The argument list consists of new bad sectors to be added to an existing
+list.
+The new sectors are sorted into the list,
+which must have been in order.
+Replacement sectors are moved to accommodate the additions;
+the new replacement sectors are cleared.
+.It Fl c
+Forces an attempt to copy the old sector to the replacement,
+and may be useful when replacing an unreliable sector.
+.It Fl f
+For a RP06, RM03, RM05, Fujitsu Eagle,
+or
+.Tn SMD
+disk on a Massbus, the
+.Fl f
+option may be used to mark the new bad sectors as ``bad''
+by reformatting them as unusable sectors.
+This option is
+.Em required unless
+the sectors have already been marked bad,
+or the system will not be notified that it should use the replacement sector.
+This option may be used while running multiuser; it is no longer necessary
+to perform format operations while running single-user.
+.It Fl s
+The entire disk is scanned for bad blocks.
+.It Fl v
+The entire process is described as it happens in gory detail if
+.Fl v
+(verbose) is given.
+.El
+.Pp
+The format of
+the information is specified by
+.Tn DEC
+standard 144, as follows.
+The bad sector information is located in the first 5 even numbered sectors
+of the last track of the disk pack. There are five identical copies of
+the information, described by the
+.Ar dkbad
+structure.
+.Pp
+Replacement sectors are allocated starting with the first sector before
+the bad sector information and working backwards towards the beginning
+of the disk. A maximum of 126 bad sectors are supported. The position
+of the bad sector in the bad sector table determines the replacement
+sector to which it corresponds.
+The bad sectors must be listed in ascending order.
+.Pp
+The bad sector information and replacement sectors are conventionally
+only accessible through the ``c'' file system partition of the disk. If
+that partition is used for a file system, the user is responsible for
+making sure that it does not overlap the bad sector information or any
+replacement sectors.
+Thus, one track plus 126 sectors must be reserved to allow use
+of all of the possible bad sector replacements.
+.Pp
+The bad sector structure is as follows:
+.Bd -literal
+struct dkbad {
+ long bt_csn; /* cartridge serial number */
+ u_short bt_mbz; /* unused; should be 0 */
+ u_short bt_flag; /* -1 => alignment cartridge */
+ struct bt_bad {
+ u_short bt_cyl; /* bad sector cylinder number */
+ u_short bt_trksec; /* track and sector number */
+ } bt_bad[126];
+};
+.Ed
+.Pp
+Unused slots in the
+.Ar bt_bad
+array are filled with all bits set, a putatively
+illegal value.
+.Pp
+.Nm Bad144
+is invoked by giving a device name (e.g. hk0, hp1, etc.).
+With no optional arguments
+it reads the first sector of the last track
+of the corresponding disk and prints out the bad sector information.
+It issues a warning if the bad sectors are out of order.
+.Nm Bad144
+may also be invoked with a serial number for the pack and a list
+of bad sectors.
+It will write the supplied information into all copies
+of the bad-sector file, replacing any previous information.
+Note, however, that
+.Nm bad144
+does not arrange for the specified sectors to be marked bad in this case.
+This procedure should only be used to restore known bad sector information which
+was destroyed.
+.Pp
+It is no longer necessary to reboot to allow the kernel
+to reread the bad-sector table from the drive.
+.Sh SEE ALSO
+.Xr badsect 8
+.Sh BUGS
+It should be possible to format disks on-line under
+.Tn UNIX .
+.Pp
+It should be possible to mark bad sectors on drives of all type.
+.Pp
+On an 11/750,
+the standard bootstrap drivers used to boot the system do
+not understand bad sectors,
+handle
+.Tn ECC
+errors, or the special
+.Tn SSE
+(skip sector) errors of RM80-type disks.
+This means that none of these errors can occur when reading the file
+.Pa /kernel
+to boot. Sectors 0-15 of the disk drive
+must also not have any of these errors.
+.Pp
+The drivers which write a system core image on disk after a crash do not
+handle errors; thus the crash dump area must be free of errors and bad
+sectors.
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 4.1 .
diff --git a/usr.sbin/bad144/bad144.c b/usr.sbin/bad144/bad144.c
new file mode 100644
index 0000000..907509c
--- /dev/null
+++ b/usr.sbin/bad144/bad144.c
@@ -0,0 +1,756 @@
+/*
+ * Copyright (c) 1993, 198019861988
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 1993, 198019861988\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static const char sccsid[] = "@(#)bad144.c 8.1 (Berkeley) 6/6/93";
+#endif
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+/*
+ * bad144
+ *
+ * This program prints and/or initializes a bad block record for a pack,
+ * in the format used by the DEC standard 144.
+ * It can also add bad sector(s) to the record, moving the sector
+ * replacements as necessary.
+ *
+ * It is preferable to write the bad information with a standard formatter,
+ * but this program will do.
+ *
+ * RP06 sectors are marked as bad by inverting the format bit in the
+ * header; on other drives the valid-sector bit is cleared.
+ */
+#include <sys/param.h>
+#include <sys/dkbad.h>
+#include <sys/ioctl.h>
+#include <sys/file.h>
+#include <sys/disklabel.h>
+#include <ufs/ffs/fs.h>
+
+#include <err.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define RETRIES 10 /* number of retries on reading old sectors */
+
+int fflag, add, copy, verbose, nflag, sflag;
+int dups;
+int badfile = -1; /* copy of badsector table to use, -1 if any */
+#define MAXSECSIZE 1024
+struct dkbad curbad, oldbad;
+#define DKBAD_MAGIC 0x4321
+
+daddr_t size;
+struct disklabel *dp;
+char *name;
+
+u_short dkcksum __P((struct disklabel *lp));
+
+int main __P((int argc, char *argv[]));
+void blkzero __P((int f, daddr_t sn));
+int compare __P((const void *cvb1, const void *cvb2));
+daddr_t badsn __P((struct bt_bad *bt));
+daddr_t getold __P((int f, struct dkbad *bad));
+int blkcopy __P((int f, daddr_t s1, daddr_t s2));
+void shift __P((int f, int new, int old));
+int checkold __P((struct dkbad *oldbad));
+static void usage __P((void));
+
+void
+bad_scan(argc, argv, dp, f, bstart, bend)
+ int *argc;
+ char ***argv;
+ struct disklabel *dp;
+ int f;
+ daddr_t bstart,bend;
+{
+ int curr_sec, n;
+ int spc = dp->d_secpercyl;
+ int ss = dp->d_secsize;
+ int trk = dp->d_nsectors;
+ int i;
+ char **nargv,*buf;
+ int nargc;
+
+ setbuf(stdout, NULL);
+ setbuf(stderr, NULL);
+
+ nargv = (char **)malloc(sizeof *nargv * DKBAD_MAXBAD);
+ if (!nargv)
+ errx(20,"malloc failed");
+ i = 1;
+ n = ioctl(f,DIOCSBADSCAN,&i);
+ if (n < 0)
+ warn("couldn't set disk in \"badscan\" mode");
+ nargc = *argc;
+ memcpy(nargv,*argv,nargc * sizeof nargv[0]);
+
+ buf = alloca((unsigned)(trk*ss));
+ if (buf == (char *)NULL)
+ errx(20,"alloca failed");
+
+ /* scan the entire disk a sector at a time. Because of all the
+ * clustering in the kernel, we cannot scan a track at a time,
+ * If we do, we may have to read twice over the block to find
+ * exactly which one failed, and it may not fail second time.
+ */
+ for (curr_sec = bstart; curr_sec < size; curr_sec++) {
+
+ if (verbose) {
+ if ((curr_sec % spc) == 0)
+ printf("\r%7d of %7lu blocks (%3lu%%)",
+ curr_sec,bend,(curr_sec*100/bend));
+ }
+
+ lseek(f, curr_sec * ss, L_SET);
+
+ if ((n = read(f, buf, ss)) != ss) {
+ if (verbose)
+ printf("\rBlock: %7d will be marked BAD.\n",
+ curr_sec);
+ else
+ warnx("found bad sector: %d", curr_sec);
+ sprintf(buf,"%d",curr_sec);
+ nargv[nargc++] = strdup(buf);
+ if (nargc >= DKBAD_MAXBAD)
+ errx(1, "too many bad sectors, can only handle %d per slice",
+ DKBAD_MAXBAD);
+ }
+ }
+ fprintf(stderr, "\n");
+ nargv[nargc] = 0;
+ *argc = nargc;
+ *argv = &nargv[0];
+ i = 0;
+ n = ioctl(f,DIOCSBADSCAN,&i);
+ if (n < 0)
+ warn("couldn't reset disk from \"badscan\" mode");
+}
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ register struct bt_bad *bt;
+ daddr_t sn, bn[DKBAD_MAXBAD];
+ int i, f, nbad, new, bad, errs;
+ daddr_t bstart, bend;
+ char label[BBSIZE];
+
+ argc--, argv++;
+ while (argc > 0 && **argv == '-') {
+ (*argv)++;
+ while (**argv) {
+ switch (**argv) {
+#if vax
+ case 'f':
+ fflag++;
+ break;
+#endif
+ case 'a':
+ add++;
+ break;
+ case 'c':
+ copy++;
+ break;
+ case 'v':
+ verbose++;
+ break;
+ case 'n':
+ nflag++;
+ verbose++;
+ break;
+ case 's': /* scan partition */
+ sflag++;
+ add++;
+ break;
+ default:
+ if (**argv >= '0' && **argv <= '4') {
+ badfile = **argv - '0';
+ break;
+ }
+ usage();
+ }
+ (*argv)++;
+ }
+ argc--, argv++;
+ }
+ if (argc < 1)
+ usage();
+ if (argv[0][0] != '/')
+ (void)sprintf(label, "%sr%s%c", _PATH_DEV, argv[0],
+ 'a' + RAW_PART);
+ else
+ strcpy(label, argv[0]);
+ name = strdup(label);
+ f = open(name, !sflag && argc == 1? O_RDONLY : O_RDWR);
+ if (f < 0)
+ err(4, "%s", name);
+ if (read(f, label, sizeof(label)) < 0)
+ err(4, "read");
+ for (dp = (struct disklabel *)(label + LABELOFFSET);
+ dp < (struct disklabel *)
+ (label + sizeof(label) - sizeof(struct disklabel));
+ dp = (struct disklabel *)((char *)dp + 64))
+ if (dp->d_magic == DISKMAGIC && dp->d_magic2 == DISKMAGIC)
+ break;
+ if (dp->d_magic != DISKMAGIC || dp->d_magic2 != DISKMAGIC)
+ errx(1, "bad pack magic number (pack is unlabeled)");
+ if (dp->d_secsize > MAXSECSIZE || dp->d_secsize <= 0)
+ errx(7, "disk sector size too large/small (%lu)", dp->d_secsize);
+ size = dp->d_secperunit;
+
+ /*
+ * bstart is 0 since we should always be doing partition c of a slice
+ * bend is the size of the slice, less the bad block map track
+ * and the DKBAD_MAXBAD replacement blocks
+ */
+ bstart = 0;
+ bend = size - (dp->d_nsectors + DKBAD_MAXBAD);
+
+ if (verbose) {
+ printf("cyl: %ld, tracks: %ld, secs: %ld, "
+ "sec/cyl: %ld, start: %ld, end: %ld\n",
+ dp->d_ncylinders, dp->d_ntracks, dp->d_nsectors,
+ dp->d_secpercyl, bstart, bend);
+ }
+
+ argc--;
+ argv++;
+
+ if (sflag)
+ bad_scan(&argc,&argv,dp,f,bstart,bend);
+
+ if (argc == 0) {
+ sn = getold(f, &oldbad);
+ printf("bad block information at sector %ld in %s:\n",
+ sn, name);
+ printf("cartridge serial number: %ld(10)\n", oldbad.bt_csn);
+ switch (oldbad.bt_flag) {
+
+ case (u_short)-1:
+ printf("alignment cartridge\n");
+ break;
+
+ case DKBAD_MAGIC:
+ break;
+
+ default:
+ printf("bt_flag=%x(16)?\n", oldbad.bt_flag);
+ break;
+ }
+ bt = oldbad.bt_bad;
+ for (i = 0; i < DKBAD_MAXBAD; i++) {
+ bad = (bt->bt_cyl<<16) + bt->bt_trksec;
+ if (bad < 0)
+ break;
+ printf("sn=%ld, cn=%d, tn=%d, sn=%d\n", badsn(bt),
+ bt->bt_cyl, bt->bt_trksec>>8, bt->bt_trksec&0xff);
+ bt++;
+ }
+ (void) checkold(&oldbad);
+ exit(0);
+ }
+ if (add) {
+ /*
+ * Read in the old badsector table.
+ * Verify that it makes sense, and the bad sectors
+ * are in order. Copy the old table to the new one.
+ */
+ (void) getold(f, &oldbad);
+ i = checkold(&oldbad);
+ if (verbose)
+ printf("Had %d bad sectors, adding %d\n", i, argc);
+ if (i + argc > DKBAD_MAXBAD) {
+ printf("bad144: not enough room for %d more sectors\n",
+ argc);
+ printf("limited to %d by information format\n",
+ DKBAD_MAXBAD);
+ exit(1);
+ }
+ curbad = oldbad;
+ } else {
+ curbad.bt_csn = atoi(*argv++);
+ argc--;
+ curbad.bt_mbz = 0;
+ curbad.bt_flag = DKBAD_MAGIC;
+ if (argc > DKBAD_MAXBAD) {
+ printf("bad144: too many bad sectors specified\n");
+ printf("limited to %d by information format\n",
+ DKBAD_MAXBAD);
+ exit(1);
+ }
+ i = 0;
+ }
+ errs = 0;
+ new = argc;
+ while (argc > 0) {
+ sn = atoi(*argv++);
+ argc--;
+ if (sn < 0 || sn >= bend) {
+ printf("%ld: out of range [0,%ld) for disk %s\n",
+ sn, bend, dp->d_typename);
+ errs++;
+ continue;
+ }
+ bn[i] = sn;
+ curbad.bt_bad[i].bt_cyl = sn / (dp->d_nsectors*dp->d_ntracks);
+ sn %= (dp->d_nsectors*dp->d_ntracks);
+ curbad.bt_bad[i].bt_trksec =
+ ((sn/dp->d_nsectors) << 8) + (sn%dp->d_nsectors);
+ i++;
+ }
+ if (errs)
+ exit(1);
+ nbad = i;
+ while (i < DKBAD_MAXBAD) {
+ curbad.bt_bad[i].bt_trksec = DKBAD_NOTRKSEC;
+ curbad.bt_bad[i].bt_cyl = DKBAD_NOCYL;
+ i++;
+ }
+ if (add) {
+ /*
+ * Sort the new bad sectors into the list.
+ * Then shuffle the replacement sectors so that
+ * the previous bad sectors get the same replacement data.
+ */
+ qsort(curbad.bt_bad, nbad, sizeof (struct bt_bad), compare);
+ if (dups)
+ errx(3,
+ "bad sectors have been duplicated; can't add existing sectors");
+ shift(f, nbad, nbad-new);
+ }
+ if (badfile == -1)
+ i = 0;
+ else
+ i = badfile * 2;
+ for (; i < 10 && i < dp->d_nsectors; i += 2) {
+ if (lseek(f, (off_t)dp->d_secsize * (size - dp->d_nsectors + i),
+ SEEK_SET) < 0)
+ err(4, "lseek");
+ if (verbose)
+ printf("write badsect file at %lu\n",
+ size - dp->d_nsectors + i);
+ if (nflag == 0 && write(f, (caddr_t)&curbad, sizeof(curbad)) !=
+ sizeof(curbad)) {
+ char msg[80];
+ (void)sprintf(msg, "write bad sector file %d", i/2);
+ warn("%s", msg);
+ }
+ if (badfile != -1)
+ break;
+ }
+#ifdef vax
+ if (nflag == 0 && fflag)
+ for (i = nbad - new; i < nbad; i++)
+ format(f, bn[i]);
+#endif
+ if (nflag == 0 && (dp->d_flags & D_BADSECT) == 0) {
+ dp->d_flags |= D_BADSECT;
+ dp->d_checksum = 0;
+ dp->d_checksum = dkcksum(dp);
+ if (ioctl(f, DIOCWDINFO, dp) < 0)
+ err(1, "can't write disklabel to enable bad sector handling");
+ }
+#ifdef DIOCSBAD
+ if (nflag == 0 && ioctl(f, DIOCSBAD, (caddr_t)&curbad) < 0)
+ warnx("can't sync bad-sector file; reboot for changes to take effect");
+#endif
+ exit(0);
+}
+
+static void
+usage()
+{
+ fprintf(stderr, "%s\n%s\n%s\n",
+ "usage: bad144 [-f] disk [snum [bn ...]]",
+ " bad144 -a [-f] [-c] disk bn ...",
+ " bad144 -s [-v] disk");
+ exit(1);
+}
+
+daddr_t
+getold(f, bad)
+ int f;
+ struct dkbad *bad;
+{
+ register int i;
+ daddr_t sn;
+ char msg[80];
+
+ if (badfile == -1)
+ i = 0;
+ else
+ i = badfile * 2;
+ for (; i < 10 && i < dp->d_nsectors; i += 2) {
+ sn = size - dp->d_nsectors + i;
+ if (lseek(f, dp->d_secsize * (off_t)sn, SEEK_SET) < 0)
+ err(4, "lseek");
+ if (read(f, (char *) bad, dp->d_secsize) == dp->d_secsize) {
+ if (i > 0)
+ printf("Using bad-sector file %d\n", i/2);
+ return(sn);
+ }
+ (void)sprintf(msg, "read bad sector file at sn %ld", sn);
+ warn("%s", msg);
+ if (badfile != -1)
+ break;
+ }
+ errx(1, "%s: can't read bad block info", name);
+ /*NOTREACHED*/
+}
+
+int
+checkold(oldbad)
+ struct dkbad *oldbad;
+{
+ register int i;
+ register struct bt_bad *bt;
+ daddr_t sn, lsn=0;
+ int errors = 0, warned = 0;
+
+ if (oldbad->bt_flag != DKBAD_MAGIC) {
+ warnx("%s: bad flag in bad-sector table", name);
+ errors++;
+ }
+ if (oldbad->bt_mbz != 0) {
+ warnx("%s: bad magic number", name);
+ errors++;
+ }
+ bt = oldbad->bt_bad;
+ for (i = 0; i < DKBAD_MAXBAD; i++, bt++) {
+ if (bt->bt_cyl == DKBAD_NOCYL &&
+ bt->bt_trksec == DKBAD_NOTRKSEC)
+ break;
+ if ((bt->bt_cyl >= dp->d_ncylinders) ||
+ ((bt->bt_trksec >> 8) >= dp->d_ntracks) ||
+ ((bt->bt_trksec & 0xff) >= dp->d_nsectors)) {
+ warnx(
+ "cyl/trk/sect out of range in existing entry: sn=%ld, cn=%d, tn=%d, sn=%d",
+ badsn(bt), bt->bt_cyl, bt->bt_trksec>>8, bt->bt_trksec & 0xff);
+ errors++;
+ }
+ sn = (bt->bt_cyl * dp->d_ntracks +
+ (bt->bt_trksec >> 8)) *
+ dp->d_nsectors + (bt->bt_trksec & 0xff);
+ if (i > 0 && sn < lsn && !warned) {
+ warnx("bad sector file is out of order");
+ errors++;
+ warned++;
+ }
+ if (i > 0 && sn == lsn) {
+ warnx("bad sector file contains duplicates (sn %ld)", sn);
+ errors++;
+ }
+ lsn = sn;
+ }
+ if (errors)
+ exit(1);
+ return (i);
+}
+
+/*
+ * Move the bad sector replacements
+ * to make room for the new bad sectors.
+ * new is the new number of bad sectors, old is the previous count.
+ */
+void
+shift(f, new, old)
+ int f, new, old;
+{
+ daddr_t repl;
+
+ /*
+ * First replacement is last sector of second-to-last track.
+ */
+ repl = size - dp->d_nsectors - 1;
+ new--; old--;
+ while (new >= 0 && new != old) {
+ if (old < 0 ||
+ compare(&curbad.bt_bad[new], &oldbad.bt_bad[old]) > 0) {
+ /*
+ * Insert new replacement here-- copy original
+ * sector if requested and possible,
+ * otherwise write a zero block.
+ */
+ if (!copy ||
+ !blkcopy(f, badsn(&curbad.bt_bad[new]), repl - new))
+ blkzero(f, repl - new);
+ } else {
+ if (blkcopy(f, repl - old, repl - new) == 0)
+ warnx("can't copy replacement sector %ld to %ld",
+ repl-old, repl-new);
+ old--;
+ }
+ new--;
+ }
+}
+
+/*
+ * Copy disk sector s1 to s2.
+ */
+int
+blkcopy(f, s1, s2)
+ int f;
+ daddr_t s1, s2;
+{
+ register tries, n;
+ char *buf;
+
+ buf = alloca((unsigned)dp->d_secsize);
+ if (buf == (char *)NULL)
+ errx(20, "alloca failed");
+
+ for (tries = 0; tries < RETRIES; tries++) {
+ if (lseek(f, (off_t)dp->d_secsize * s1, SEEK_SET) < 0)
+ err(4, "lseek");
+ if ((n = read(f, buf, dp->d_secsize)) == dp->d_secsize)
+ break;
+ }
+ if (n != dp->d_secsize) {
+ if (n < 0)
+ warn("can't read sector, %ld", s1);
+ else
+ warnx("can't read sector, %ld", s1);
+ return(0);
+ }
+ if (lseek(f, (off_t)dp->d_secsize * s2, SEEK_SET) < 0)
+ err(4, "lseek");
+ if (verbose)
+ printf("copying %ld to %ld\n", s1, s2);
+ if (nflag == 0 && write(f, buf, dp->d_secsize) != dp->d_secsize) {
+ warn("can't write replacement sector, %ld", s2);
+ return(0);
+ }
+ return(1);
+}
+
+
+void
+blkzero(f, sn)
+ int f;
+ daddr_t sn;
+{
+ char *zbuf;
+
+ zbuf = alloca((unsigned)dp->d_secsize);
+ if (zbuf == (char *)NULL)
+ errx(20, "alloca failed");
+
+ memset(zbuf, 0, dp->d_secsize);
+
+ if (lseek(f, (off_t)dp->d_secsize * sn, SEEK_SET) < 0)
+ err(4, "lseek");
+ if (verbose)
+ printf("zeroing %ld\n", sn);
+ if (nflag == 0 && write(f, zbuf, dp->d_secsize) != dp->d_secsize)
+ warn("can't write replacement sector, %ld", sn);
+}
+
+int
+compare(cvb1, cvb2)
+ const void *cvb1, *cvb2;
+{
+ const struct bt_bad *b1 = cvb1, *b2 = cvb2;
+
+ if (b1->bt_cyl > b2->bt_cyl)
+ return(1);
+ if (b1->bt_cyl < b2->bt_cyl)
+ return(-1);
+ if (b1->bt_trksec == b2->bt_trksec)
+ dups++;
+ return (b1->bt_trksec - b2->bt_trksec);
+}
+
+daddr_t
+badsn(bt)
+ register struct bt_bad *bt;
+{
+ return ((bt->bt_cyl*dp->d_ntracks + (bt->bt_trksec>>8)) * dp->d_nsectors
+ + (bt->bt_trksec&0xff));
+}
+
+#ifdef vax
+
+struct rp06hdr {
+ short h_cyl;
+ short h_trksec;
+ short h_key1;
+ short h_key2;
+ char h_data[512];
+#define RP06_FMT 010000 /* 1 == 16 bit, 0 == 18 bit */
+};
+
+/*
+ * Most massbus and unibus drives
+ * have headers of this form
+ */
+struct hpuphdr {
+ u_short hpup_cyl;
+ u_char hpup_sect;
+ u_char hpup_track;
+ char hpup_data[512];
+#define HPUP_OKSECT 0xc000 /* this normally means sector is good */
+#define HPUP_16BIT 0x1000 /* 1 == 16 bit format */
+};
+int rp06format(), hpupformat();
+
+struct formats {
+ char *f_name; /* disk name */
+ int f_bufsize; /* size of sector + header */
+ int f_bic; /* value to bic in hpup_cyl */
+ int (*f_routine)(); /* routine for special handling */
+} formats[] = {
+ { "rp06", sizeof (struct rp06hdr), RP06_FMT, rp06format },
+ { "eagle", sizeof (struct hpuphdr), HPUP_OKSECT, hpupformat },
+ { "capricorn", sizeof (struct hpuphdr), HPUP_OKSECT, hpupformat },
+ { "rm03", sizeof (struct hpuphdr), HPUP_OKSECT, hpupformat },
+ { "rm05", sizeof (struct hpuphdr), HPUP_OKSECT, hpupformat },
+ { "9300", sizeof (struct hpuphdr), HPUP_OKSECT, hpupformat },
+ { "9766", sizeof (struct hpuphdr), HPUP_OKSECT, hpupformat },
+ { 0, 0, 0, 0 }
+};
+
+/*ARGSUSED*/
+hpupformat(fp, dp, blk, buf, count)
+ struct formats *fp;
+ struct disklabel *dp;
+ daddr_t blk;
+ char *buf;
+ int count;
+{
+ struct hpuphdr *hdr = (struct hpuphdr *)buf;
+ int sect;
+
+ if (count < sizeof(struct hpuphdr)) {
+ hdr->hpup_cyl = (HPUP_OKSECT | HPUP_16BIT) |
+ (blk / (dp->d_nsectors * dp->d_ntracks));
+ sect = blk % (dp->d_nsectors * dp->d_ntracks);
+ hdr->hpup_track = (u_char)(sect / dp->d_nsectors);
+ hdr->hpup_sect = (u_char)(sect % dp->d_nsectors);
+ }
+ return (0);
+}
+
+/*ARGSUSED*/
+rp06format(fp, dp, blk, buf, count)
+ struct formats *fp;
+ struct disklabel *dp;
+ daddr_t blk;
+ char *buf;
+ int count;
+{
+
+ if (count < sizeof(struct rp06hdr)) {
+ warnx("can't read header on blk %ld, can't reformat", blk);
+ return (-1);
+ }
+ return (0);
+}
+
+format(fd, blk)
+ int fd;
+ daddr_t blk;
+{
+ register struct formats *fp;
+ static char *buf;
+ static char bufsize;
+ struct format_op fop;
+ int n;
+
+ for (fp = formats; fp->f_name; fp++)
+ if (strcmp(dp->d_typename, fp->f_name) == 0)
+ break;
+ if (fp->f_name == 0)
+ errx(2, "don't know how to format %s disks", dp->d_typename);
+ if (buf && bufsize < fp->f_bufsize) {
+ free(buf);
+ buf = NULL;
+ }
+ if (buf == NULL)
+ buf = malloc((unsigned)fp->f_bufsize);
+ if (buf == NULL)
+ errx(3, "can't allocate sector buffer");
+ bufsize = fp->f_bufsize;
+ /*
+ * Here we do the actual formatting. All we really
+ * do is rewrite the sector header and flag the bad sector
+ * according to the format table description. If a special
+ * purpose format routine is specified, we allow it to
+ * process the sector as well.
+ */
+ if (verbose)
+ printf("format blk %ld\n", blk);
+ bzero((char *)&fop, sizeof(fop));
+ fop.df_buf = buf;
+ fop.df_count = fp->f_bufsize;
+ fop.df_startblk = blk;
+ bzero(buf, fp->f_bufsize);
+ if (ioctl(fd, DIOCRFORMAT, &fop) < 0)
+ warn("read format");
+ if (fp->f_routine &&
+ (*fp->f_routine)(fp, dp, blk, buf, fop.df_count) != 0)
+ return;
+ if (fp->f_bic) {
+ struct hpuphdr *xp = (struct hpuphdr *)buf;
+
+ xp->hpup_cyl &= ~fp->f_bic;
+ }
+ if (nflag)
+ return;
+ bzero((char *)&fop, sizeof(fop));
+ fop.df_buf = buf;
+ fop.df_count = fp->f_bufsize;
+ fop.df_startblk = blk;
+ if (ioctl(fd, DIOCWFORMAT, &fop) < 0)
+ err(4, "write format");
+ if (fop.df_count != fp->f_bufsize) {
+ char msg[80];
+ (void)sprintf(msg, "write format %ld", blk);
+ warn("%s", msg);
+ }
+}
+#endif
diff --git a/usr.sbin/bootparamd/Makefile b/usr.sbin/bootparamd/Makefile
new file mode 100644
index 0000000..c4f6198
--- /dev/null
+++ b/usr.sbin/bootparamd/Makefile
@@ -0,0 +1,4 @@
+
+SUBDIR= bootparamd callbootd
+
+.include <bsd.subdir.mk>
diff --git a/usr.sbin/bootparamd/Makefile.inc b/usr.sbin/bootparamd/Makefile.inc
new file mode 100644
index 0000000..26c6f1c
--- /dev/null
+++ b/usr.sbin/bootparamd/Makefile.inc
@@ -0,0 +1,3 @@
+# @(#)Makefile.inc 5.1 (Berkeley) 5/11/90
+
+BINDIR?= /usr/sbin
diff --git a/usr.sbin/bootparamd/bootparamd/Makefile b/usr.sbin/bootparamd/bootparamd/Makefile
new file mode 100644
index 0000000..3d8cbc8
--- /dev/null
+++ b/usr.sbin/bootparamd/bootparamd/Makefile
@@ -0,0 +1,22 @@
+# from: @(#)Makefile 5.8 (Berkeley) 7/28/90
+# $Id$
+
+PROG= bootparamd
+SRCS= bootparam_prot_xdr.c bootparam_prot_svc.c bootparamd.c main.c
+MAN8= bootparamd.8
+MAN5= bootparams.5
+CFLAGS+= -DTFTP_DIR=\"/tftpboot\" -I.
+CLEANFILES= bootparam_prot_svc.c bootparam_prot_xdr.c bootparam_prot.h
+
+RPCSRC= ${DESTDIR}/usr/include/rpcsvc/bootparam_prot.x
+
+bootparam_prot_svc.c: ${RPCSRC} bootparam_prot.h
+ rpcgen -m -o ${.TARGET} ${RPCSRC}
+
+bootparam_prot_xdr.c: ${RPCSRC} bootparam_prot.h
+ rpcgen -c -o ${.TARGET} ${RPCSRC}
+
+bootparam_prot.h: ${RPCSRC}
+ rpcgen -h -o ${.TARGET} ${RPCSRC}
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/bootparamd/bootparamd/README b/usr.sbin/bootparamd/bootparamd/README
new file mode 100644
index 0000000..85a2a54
--- /dev/null
+++ b/usr.sbin/bootparamd/bootparamd/README
@@ -0,0 +1,75 @@
+
+
+This directory contains a version of the rpc.bootparamd, which have been
+written using the Sun's RPC protocol for bootparamd. To use it you must
+have a copy of the bootparam_prot.x file which on Sun systems you find in
+
+ /usr/include/rpcsvc/bootparam_prot.x
+
+(( This file was retrieved from the Sun-RPC source package ))
+
+
+This code is not copyright, and is placed in the public domain. Feel free to
+use and modify. Please send modifications and/or suggestions + bug fixes to
+
+ Klas Heggemann <klas@nada.kth.se>
+
+
+RPC.BOOTPARAMD
+
+
+The rpc.bootparamd program does NOT use the yellow pages for the bootparams
+database. This data should recide in /etc/bootparams on the local host,
+or another file given when the server is started.
+
+The default router is set to the address of the machine running the server.
+This may not be a good thing to do, so it can be modified using the -r
+option when startning the daemon.
+
+This program was written with the need to keep short hostnames in the
+/etc/bootparams file and long (canonical) names in the hosts database.
+It probably also will work in conjunction with a nameserver, since matching
+is done by comparing the canonical name of the booting machine with the
+canonical name of the hosts found in the bootparams database.
+
+It is kept simple, e g there is no caching of data, but the bootparameter file
+is read at each request.
+
+
+CALLBOOTD
+
+The debugging tool callbootd is used to check the response you get
+to a specific (booting) request. It can be used as
+ callbootd server inet-adress
+or
+ callbootd server hostname file
+where "server" is a machine running the rpc.bootparamd program, "inet-address"
+is the internet address of a booting machine, "hostname" is the name of a
+booting machine and "file" the requested file, typically "root", "swap" or
+"dump".
+
+You may also use "all" instead of a specific server, in which case a RPC
+broadcast is performed. The broadcast is performed 4 times and then the
+program times out, after printing all responses.
+
+
+TODO
+
+Cache the date, instead of rereading it.
+Maybe match by comparing the inet address instead. (But beware that caching
+will prevent the server from detecting that a machine has changed name
+or address.)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/usr.sbin/bootparamd/bootparamd/bootparamd.8 b/usr.sbin/bootparamd/bootparamd/bootparamd.8
new file mode 100644
index 0000000..3573137
--- /dev/null
+++ b/usr.sbin/bootparamd/bootparamd/bootparamd.8
@@ -0,0 +1,48 @@
+.\" @(#)bootparamd.8
+.Dd November 8, 1989
+.Dt BOOTPARAMD 8
+.Os
+.Sh NAME
+.Nm bootparamd
+.Nd boot parameter server
+.Sh SYNOPSIS
+.Nm
+.Op Fl ds
+.Op Fl r Ar router
+.Op Fl f Ar file
+.Sh DESCRIPTION
+.Nm Bootparamd
+is a server process that provides information to diskless clients
+necessary for booting. It consults the
+.Pa /etc/bootparams
+file.
+.Pp
+This version will allow the use of aliases on the hostname in the
+.Pa /etc/bootparams
+file. The returned hostname to the whoami request done by the booting client
+will be the name that appears in
+.Pa /etc/bootparams
+and not the canonical name. In this way you can keep the answer short enough
+so that machines that can not handle long hostnames won't fail during boot.
+.Sh OPTIONS
+.Bl -tag -width Fl
+.It Fl d
+Display the debugging information.
+.It Fl s
+Log the debugging information with syslog.
+.It Fl r Ar router
+The default router (a machine or an IP-address).
+This defaults to the machine running the server.
+.It Fl f Ar file
+The file to use as boot parameter file instead of
+.Pa /etc/bootparams .
+.El
+.Sh FILES
+.Bl -tag -width /etc/bootparams -compact
+.It /etc/bootparams
+.El
+.Sh BUGS
+You may find the syslog loggings to be verbose.
+.Sh AUTHOR
+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..c19fb53
--- /dev/null
+++ b/usr.sbin/bootparamd/bootparamd/bootparamd.c
@@ -0,0 +1,340 @@
+/*
+
+This code is not copyright, and is placed in the public domain. Feel free to
+use and modify. Please send modifications and/or suggestions + bug fixes to
+
+ Klas Heggemann <klas@nada.kth.se>
+
+*/
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id: bootparamd.c,v 1.7 1997/09/04 11:49:24 charnier Exp $";
+#endif /* not lint */
+
+#include <rpc/rpc.h>
+#include <rpcsvc/yp_prot.h>
+#include <rpcsvc/ypclnt.h>
+#include "bootparam_prot.h"
+#include <ctype.h>
+#include <err.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+extern int debug, dolog;
+extern unsigned long route_addr;
+extern char *bootpfile;
+
+#define MAXLEN 800
+
+struct hostent *he;
+static char buffer[MAXLEN];
+static char hostname[MAX_MACHINE_NAME];
+static char askname[MAX_MACHINE_NAME];
+static char path[MAX_PATH_LEN];
+static char domain_name[MAX_MACHINE_NAME];
+
+int getthefile __P((char *, char *, char *));
+int 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)) {
+ 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)
+char *askname;
+char *fileid, *buffer;
+{
+ 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 {
+ sprintf(buffer,"%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..844e71c
--- /dev/null
+++ b/usr.sbin/bootparamd/bootparamd/bootparams.5
@@ -0,0 +1,78 @@
+.\"
+.\" Copyright (c) 1994 Gordon W. Ross
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. The name of the author may not be used to endorse or promote products
+.\" derived from this software without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+.\"
+.\" from: Id: bootparams.5,v 1.2 1994/10/03 19:26:13 gwr Exp
+.\" $Id: bootparams.5,v 1.6 1997/02/22 16:04:23 peter Exp $
+.\"
+.Dd October 2, 1994
+.Dt BOOTPARAMS 5
+.Os
+.Sh NAME
+.Nm bootparams
+.Nd boot parameter database
+.Sh SYNOPSIS
+.Nm /etc/bootparams
+.Sh DESCRIPTION
+The
+.Nm
+file specifies the boot parameters that
+diskless
+clients may request when booting over the network.
+Each client supported by this server must have an entry in the
+.Nm
+file containing the pathnames for its
+.Nm root
+and (optionally)
+.Nm swap
+areas.
+.Pp
+Each line in the file
+(other than comment lines that begin with a #)
+specifies the client name followed by the pathnames that
+the client may request by their logical names.
+The components of the line are delimited with blank or tab,
+and may be continued onto multiple lines with a backslash.
+.Pp
+For example:
+.Bd -literal -offset indent
+dummy root=host:/export/dummy/root \\
+ swap=host:/export/dummy/swap \\
+ dump=host:/export/dummy/swap
+.Ed
+.Pp
+When the client named "dummy" requests the pathname for
+its logical "root" it will be given the pathname
+.Dq Pa "host:/export/dummy/root"
+as the response to its
+.Tn RPC
+request. The "host:" component must be supplied.
+.Sh FILES
+.Bl -tag -width /etc/bootparams -compact
+.It Pa /etc/bootparams
+default configuration file
+.El
+.Sh SEE ALSO
+.Xr bootparamd 8
diff --git a/usr.sbin/bootparamd/bootparamd/main.c b/usr.sbin/bootparamd/bootparamd/main.c
new file mode 100644
index 0000000..fb79060
--- /dev/null
+++ b/usr.sbin/bootparamd/bootparamd/main.c
@@ -0,0 +1,117 @@
+/*
+
+This code is not copyright, and is placed in the public domain. Feel free to
+use and modify. Please send modifications and/or suggestions + bug fixes to
+
+ Klas Heggemann <klas@nada.kth.se>
+
+*/
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id$";
+#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 "bootparam_prot.h"
+
+int debug = 0;
+int dolog = 0;
+unsigned long route_addr = -1, inet_addr();
+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..ebf161b
--- /dev/null
+++ b/usr.sbin/bootparamd/callbootd/Makefile
@@ -0,0 +1,21 @@
+# from: @(#)Makefile 5.8 (Berkeley) 7/28/90
+# $Id$
+
+PROG= callbootd
+SRCS= bootparam_prot_xdr.c bootparam_prot_clnt.c callbootd.c
+NOMAN=
+CFLAGS+= -I.
+CLEANFILES= bootparam_prot_clnt.c bootparam_prot_xdr.c bootparam_prot.h
+
+RPCSRC= ${DESTDIR}/usr/include/rpcsvc/bootparam_prot.x
+
+bootparam_prot_clnt.c: ${RPCSRC} bootparam_prot.h
+ rpcgen -l -o ${.TARGET} ${RPCSRC}
+
+bootparam_prot_xdr.c: ${RPCSRC} bootparam_prot.h
+ 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..6108fc7
--- /dev/null
+++ b/usr.sbin/bootparamd/callbootd/callbootd.c
@@ -0,0 +1,196 @@
+/*
+
+This code is not copyright, and is placed in the public domain. Feel free to
+use and modify. Please send modifications and/or suggestions + bug fixes to
+
+ Klas Heggemann <klas@nada.kth.se>
+
+*/
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#include "bootparam_prot.h"
+#include <rpc/rpc.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <err.h>
+#include <netdb.h>
+
+
+/* #define bp_address_u bp_address */
+#include <stdio.h>
+#include <string.h>
+
+int broadcast;
+
+char cln[MAX_MACHINE_NAME+1];
+char dmn[MAX_MACHINE_NAME+1];
+char path[MAX_PATH_LEN+1];
+extern char *inet_ntoa();
+static void usage __P((void));
+int printgetfile __P((bp_getfile_res *));
+int printwhoami __P((bp_whoami_res *));
+
+int
+eachres_whoami(resultp, raddr)
+bp_whoami_res *resultp;
+struct sockaddr_in *raddr;
+{
+ struct hostent *he;
+
+ he = gethostbyaddr((char *)&raddr->sin_addr.s_addr,4,AF_INET);
+ printf("%s answered:\n", he ? he->h_name : inet_ntoa(raddr->sin_addr));
+ printwhoami(resultp);
+ printf("\n");
+ return(0);
+}
+
+eachres_getfile(resultp, raddr)
+bp_getfile_res *resultp;
+struct sockaddr_in *raddr;
+{
+ struct hostent *he;
+
+ he = gethostbyaddr((char *)&raddr->sin_addr.s_addr,4,AF_INET);
+ printf("%s answered:\n", he ? he->h_name : inet_ntoa(raddr->sin_addr));
+ printgetfile(resultp);
+ printf("\n");
+ return(0);
+}
+
+
+int
+main(argc, argv)
+int argc;
+char **argv;
+{
+ char *server;
+
+ bp_whoami_arg whoami_arg;
+ bp_whoami_res *whoami_res, stat_whoami_res;
+ bp_getfile_arg getfile_arg;
+ bp_getfile_res *getfile_res, stat_getfile_res;
+
+
+ long the_inet_addr;
+ CLIENT *clnt;
+ enum clnt_stat clnt_stat;
+
+ stat_whoami_res.client_name = cln;
+ stat_whoami_res.domain_name = dmn;
+
+ stat_getfile_res.server_name = cln;
+ stat_getfile_res.server_path = path;
+
+ if (argc < 3)
+ usage();
+
+ server = argv[1];
+ if ( ! strcmp(server , "all") ) broadcast = 1;
+
+ if ( ! broadcast ) {
+ clnt = clnt_create(server,BOOTPARAMPROG, BOOTPARAMVERS, "udp");
+ }
+
+ if ( clnt == NULL )
+ errx(1, "could not contact bootparam server on host %s", server);
+
+ switch (argc) {
+ case 3:
+ whoami_arg.client_address.address_type = IP_ADDR_TYPE;
+ the_inet_addr = inet_addr(argv[2]);
+ if ( the_inet_addr == -1)
+ errx(2, "bogus addr %s", argv[2]);
+ bcopy(&the_inet_addr,&whoami_arg.client_address.bp_address_u.ip_addr,4);
+
+ if (! broadcast ) {
+ whoami_res = bootparamproc_whoami_1(&whoami_arg, clnt);
+ printf("Whoami returning:\n");
+ if (printwhoami(whoami_res)) {
+ errx(1, "bad answer returned from server %s", server);
+ } else
+ exit(0);
+ } else {
+ clnt_stat=clnt_broadcast(BOOTPARAMPROG, BOOTPARAMVERS,
+ BOOTPARAMPROC_WHOAMI,
+ xdr_bp_whoami_arg, &whoami_arg,
+ xdr_bp_whoami_res, &stat_whoami_res, eachres_whoami);
+ exit(0);
+ }
+
+ case 4:
+
+ getfile_arg.client_name = argv[2];
+ getfile_arg.file_id = argv[3];
+
+ if (! broadcast ) {
+ getfile_res = bootparamproc_getfile_1(&getfile_arg,clnt);
+ printf("getfile returning:\n");
+ if (printgetfile(getfile_res)) {
+ errx(1, "bad answer returned from server %s", server);
+ } else
+ exit(0);
+ } else {
+ clnt_stat=clnt_broadcast(BOOTPARAMPROG, BOOTPARAMVERS,
+ BOOTPARAMPROC_GETFILE,
+ xdr_bp_getfile_arg, &getfile_arg,
+ xdr_bp_getfile_res, &stat_getfile_res,eachres_getfile);
+ exit(0);
+ }
+
+ default:
+
+ usage();
+ }
+
+}
+
+
+static void
+usage()
+{
+ fprintf(stderr,
+ "usage: callbootd server procnum (IP-addr | host fileid)\n");
+ exit(1);
+}
+
+int
+printwhoami(res)
+bp_whoami_res *res;
+{
+ if ( res) {
+ printf("client_name:\t%s\ndomain_name:\t%s\n",
+ res->client_name, res->domain_name);
+ printf("router:\t%d.%d.%d.%d\n",
+ 255 & res->router_address.bp_address_u.ip_addr.net,
+ 255 & res->router_address.bp_address_u.ip_addr.host,
+ 255 & res->router_address.bp_address_u.ip_addr.lh,
+ 255 & res->router_address.bp_address_u.ip_addr.impno);
+ return(0);
+ } else {
+ warnx("null answer!!!");
+ return(1);
+ }
+ }
+
+
+
+
+int
+printgetfile(res)
+bp_getfile_res *res;
+{
+ if (res) {
+ printf("server_name:\t%s\nserver_address:\t%s\npath:\t%s\n",
+ res->server_name,
+ inet_ntoa(res->server_address.bp_address_u.ip_addr),
+ res->server_path);
+ return(0);
+ } else {
+ warnx("null answer!!!");
+ return(1);
+ }
+ }
diff --git a/usr.sbin/cdcontrol/Makefile b/usr.sbin/cdcontrol/Makefile
new file mode 100644
index 0000000..86a42eb
--- /dev/null
+++ b/usr.sbin/cdcontrol/Makefile
@@ -0,0 +1,4 @@
+PROG= cdcontrol
+CFLAGS+= -Wall
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/cdcontrol/cdcontrol.1 b/usr.sbin/cdcontrol/cdcontrol.1
new file mode 100644
index 0000000..fec0297
--- /dev/null
+++ b/usr.sbin/cdcontrol/cdcontrol.1
@@ -0,0 +1,162 @@
+.\" $Id: cdcontrol.1,v 1.11 1997/09/04 11:52:26 charnier Exp $
+.\"
+.Dd July 3, 1995
+.Dt CDCONTROL 1
+.Os FreeBSD
+.Sh NAME
+.Nm cdcontrol
+.Nd compact disc control utility
+.Sh SYNOPSIS
+.Nm cdcontrol
+.Op Fl s
+.Op Fl v
+.Op Fl f Ar discname
+.Op Ar command args ...
+.Sh DESCRIPTION
+.Nm Cdcontrol
+is a program to control audio features of a CD drive. The device is a name such
+as cd0 or mcd0.
+.Pp
+If the device not specified, the environment variable
+.Ev DISC
+will be used to find the cd device.
+.Pp
+If no command is given, then
+.Nm
+enters an interactive mode, reading commands from the standard input.
+.Pp
+The following options are available:
+.Bl -tag -width flag
+.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 discname
+Specifies a device name, such as /dev/cd0c or mcd0.
+Both absolute path and relative to /dev filename are possible.
+Suffix `c' is added to the device name if needed.
+.El
+.Pp
+The available commands are listed below. Only as many
+characters as are required to uniquely identify a command
+need be specified. Word
+.Nm play
+can be omitted.
+.Bl -tag -width Cm
+
+.It Cm play Ar first_track Op Ar last_track
+Play from track
+.Nm first_track
+to track
+.Nm last_track.
+The first track has number 1.
+can be omitted in all cases.
+
+.It Cm play Ar start_m:start_s.start_f Op Ar end_m:end_s.end_f
+Play from the absolute address
+(MSF) defined by
+.Nm start_m
+in minutes,
+.Nm start_s,
+in seconds and
+.Nm start_f
+(frame number) to the absolute address defined by
+.Nm end_m
+in minutes,
+.Nm end_s,
+in seconds and
+.Nm end_f
+(frame number). Minutes are in the range 0-99. Seconds are in the range 0-59.
+Frame numbers are in the range 0-74.
+
+.It Cm play Op Ar #start_block Op length
+Play starting from the logical block
+.Nm start_block
+using
+.Nm length
+logical blocks.
+
+.It Cm pause
+Stop playing. Do not stop the disc.
+
+.It Cm resume
+Resume playing. Used after the
+.Nm pause
+ command.
+
+.It Cm stop
+Stop the disc.
+
+.It Cm eject
+Eject the disc.
+
+.It Cm close
+Inject the disc.
+
+.It Cm volume Ar left_channel Ar right_channel
+Set the volume of left channel to
+.Nm left_channel
+and the volume of right channel to
+.Nm right_channel.
+Allowed values are in the range 0-255.
+
+.It Cm volume Ar mute
+Turn the sound off.
+
+.It Cm volume Ar mono
+Set the mono mode.
+
+.It Cm volume Ar stereo
+Set the stereo mode.
+
+.It Cm volume Ar left
+Play the left subtrack on both left and right channels.
+
+.It Cm volume Ar right
+Play the right subtrack on both left and right channels.
+
+.It Cm info
+Print the table of contents.
+
+.It Cm status
+Print the information about the disc:
+the current playing status and position,
+the current media catalog status,
+the current values of the volume for left and right channels.
+
+.It Cm help
+Print the list of available commands.
+
+.It Cm debug Ar on
+Enable the debugging mode of the CD device driver.
+
+.It Cm debug Ar off
+Disable the driver debugging mode.
+
+.It Cm reset
+Perform the hardware reset of the device.
+
+.It Cm set Ar msf
+Set minute-second-frame ioctl mode (default).
+
+.It Cm set Ar lba
+Set LBA ioctl mode.
+
+.It Cm quit
+Quit the program.
+
+.Sh FILES
+.Bl -tag -width /dev/rmcd0c -compact
+.It Pa /dev/rcd0c
+.It Pa /dev/rmcd0c
+.It Pa /dev/rwcd0c
+.El
+.Sh AUTHORS
+Jean-Marc Zucconi,
+Andrey A.\ Chernov,
+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..092410d
--- /dev/null
+++ b/usr.sbin/cdcontrol/cdcontrol.c
@@ -0,0 +1,1009 @@
+/*
+ * Compact Disc Control Utility by Serge V. Vakulenko <vak@cronyx.ru>.
+ * Based on the non-X based CD player by Jean-Marc Zucconi and
+ * Andrey A. Chernov.
+ *
+ * Fixed and further modified on 5-Sep-1995 by Jukka Ukkonen <jau@funet.fi>.
+ *
+ * 11-Sep-1995: Jukka A. Ukkonen <jau@funet.fi>
+ * A couple of further fixes to my own earlier "fixes".
+ *
+ * 18-Sep-1995: Jukka A. Ukkonen <jau@funet.fi>
+ * Added an ability to specify addresses relative to the
+ * beginning of a track. This is in fact a variation of
+ * doing the simple play_msf() call.
+ *
+ * 11-Oct-1995: Serge V.Vakulenko <vak@cronyx.ru>
+ * New eject algorithm.
+ * Some code style reformatting.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/file.h>
+#include <sys/cdio.h>
+#include <sys/ioctl.h>
+
+#define VERSION "2.0"
+
+#define ASTS_INVALID 0x00 /* Audio status byte not valid */
+#define ASTS_PLAYING 0x11 /* Audio play operation in progress */
+#define ASTS_PAUSED 0x12 /* Audio play operation paused */
+#define ASTS_COMPLETED 0x13 /* Audio play operation successfully completed */
+#define ASTS_ERROR 0x14 /* Audio play operation stopped due to error */
+#define ASTS_VOID 0x15 /* No current audio status to return */
+
+#ifndef DEFAULT_CD_DRIVE
+# define DEFAULT_CD_DRIVE "/dev/cd0c"
+#endif
+
+#ifndef DEFAULT_CD_PARTITION
+# define DEFAULT_CD_PARTITION "c"
+#endif
+
+#define CMD_DEBUG 1
+#define CMD_EJECT 2
+#define CMD_HELP 3
+#define CMD_INFO 4
+#define CMD_PAUSE 5
+#define CMD_PLAY 6
+#define CMD_QUIT 7
+#define CMD_RESUME 8
+#define CMD_STOP 9
+#define CMD_VOLUME 10
+#define CMD_CLOSE 11
+#define CMD_RESET 12
+#define CMD_SET 13
+#define CMD_STATUS 14
+
+struct cmdtab {
+ int command;
+ char *name;
+ unsigned min;
+ char *args;
+} cmdtab[] = {
+{ CMD_CLOSE, "close", 1, "" },
+{ CMD_DEBUG, "debug", 1, "on | off" },
+{ CMD_EJECT, "eject", 1, "" },
+{ CMD_HELP, "?", 1, 0 },
+{ CMD_HELP, "help", 1, "" },
+{ CMD_INFO, "info", 1, "" },
+{ CMD_PAUSE, "pause", 2, "" },
+{ CMD_PLAY, "play", 1, "min1:sec1[.fram1] [min2:sec2[.fram2]]" },
+{ CMD_PLAY, "play", 1, "track1[.index1] [track2[.index2]]" },
+{ CMD_PLAY, "play", 1, "tr1 m1:s1[.f1] [[tr2] [m2:s2[.f2]]]" },
+{ CMD_PLAY, "play", 1, "[#block [len]]" },
+{ CMD_QUIT, "quit", 1, "" },
+{ CMD_RESET, "reset", 4, "" },
+{ CMD_RESUME, "resume", 1, "" },
+{ CMD_SET, "set", 2, "msf | lba" },
+{ CMD_STATUS, "status", 1, "" },
+{ CMD_STOP, "stop", 3, "" },
+{ CMD_VOLUME, "volume", 1, "<l> <r> | left | right | mute | mono | stereo" },
+{ 0, }
+};
+
+struct cd_toc_entry toc_buffer[100];
+
+const char *cdname;
+int fd = -1;
+int verbose = 1;
+int msf = 1;
+
+int setvol __P((int, int));
+int read_toc_entrys __P((int));
+int play_msf __P((int, int, int, int, int, int));
+int play_track __P((int, int, int, int));
+int get_vol __P((int *, int *));
+int status __P((int *, int *, int *, int *));
+int open_cd __P((void));
+int play __P((char *arg));
+int info __P((char *arg));
+int pstatus __P((char *arg));
+char *input __P((int *));
+void prtrack __P((struct cd_toc_entry *e, int lastflag));
+void lba2msf __P((unsigned long lba,
+ u_char *m, u_char *s, u_char *f));
+unsigned int msf2lba __P((u_char m, u_char s, u_char f));
+int play_blocks __P((int blk, int len));
+int run __P((int cmd, char *arg));
+char *parse __P((char *buf, int *cmd));
+
+void help ()
+{
+ struct cmdtab *c;
+ char *s, n;
+ int i;
+
+ for (c=cmdtab; c->name; ++c) {
+ if (! c->args)
+ continue;
+ printf("\t");
+ for (i = c->min, s = c->name; *s; s++, i--) {
+ if (i > 0)
+ n = toupper(*s);
+ else
+ n = *s;
+ putchar(n);
+ }
+ if (*c->args)
+ printf (" %s", c->args);
+ printf ("\n");
+ }
+ printf ("\n\tThe word \"play\" is not required for the play commands.\n");
+ printf ("\tThe plain target address is taken as a synonym for play.\n");
+}
+
+void usage ()
+{
+ fprintf (stderr, "usage: cdcontrol [-vs] [-f disc] [command args ...]\n");
+ exit (1);
+}
+
+int main (int argc, char **argv)
+{
+ int cmd;
+ char *arg;
+
+ cdname = getenv ("MUSIC_CD");
+ if (! cdname)
+ cdname = getenv ("CD_DRIVE");
+ if (! cdname)
+ cdname = getenv ("DISC");
+ if (! cdname)
+ cdname = getenv ("CDPLAY");
+
+ for (;;) {
+ switch (getopt (argc, argv, "svhf:")) {
+ case EOF:
+ break;
+ case 's':
+ verbose = 0;
+ continue;
+ case 'v':
+ verbose = 2;
+ continue;
+ case 'f':
+ cdname = optarg;
+ continue;
+ case 'h':
+ default:
+ usage ();
+ }
+ break;
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc > 0 && ! strcasecmp (*argv, "help"))
+ usage ();
+
+ if (! cdname) {
+ cdname = DEFAULT_CD_DRIVE;
+ warnx("no CD device name specified, defaulting to %s", cdname);
+ }
+
+ if (argc > 0) {
+ char buf[80], *p;
+ int len;
+
+ for (p=buf; argc-->0; ++argv) {
+ len = strlen (*argv);
+
+ if (p + len >= buf + sizeof (buf) - 1)
+ usage ();
+
+ if (p > buf)
+ *p++ = ' ';
+
+ strcpy (p, *argv);
+ p += len;
+ }
+ *p = 0;
+ arg = parse (buf, &cmd);
+ return (run (cmd, arg));
+ }
+
+ if (verbose == 1)
+ verbose = isatty (0);
+
+ if (verbose) {
+ printf ("Compact Disc Control utility, version %s\n", VERSION);
+ printf ("Type `?' for command list\n\n");
+ }
+
+ for (;;) {
+ arg = input (&cmd);
+ if (run (cmd, arg) < 0) {
+ if (verbose)
+ warn(NULL);
+ close (fd);
+ fd = -1;
+ }
+ fflush (stdout);
+ }
+}
+
+int run (int cmd, char *arg)
+{
+ int l, r, rc;
+
+ switch (cmd) {
+
+ case CMD_QUIT:
+ exit (0);
+
+ case CMD_INFO:
+ if (fd < 0 && ! open_cd ())
+ return (0);
+
+ return info (arg);
+
+ case CMD_STATUS:
+ if (fd < 0 && ! open_cd ())
+ return (0);
+
+ return pstatus (arg);
+
+ case CMD_PAUSE:
+ if (fd < 0 && ! open_cd ())
+ return (0);
+
+ return ioctl (fd, CDIOCPAUSE);
+
+ case CMD_RESUME:
+ if (fd < 0 && ! open_cd ())
+ return (0);
+
+ return ioctl (fd, CDIOCRESUME);
+
+ case CMD_STOP:
+ if (fd < 0 && ! open_cd ())
+ return (0);
+
+ rc = ioctl (fd, CDIOCSTOP);
+
+ (void) ioctl (fd, CDIOCALLOW);
+
+ return (rc);
+
+ case CMD_RESET:
+ if (fd < 0 && ! open_cd ())
+ return (0);
+
+ rc = ioctl (fd, CDIOCRESET);
+ if (rc < 0)
+ return rc;
+ close(fd);
+ fd = -1;
+ return (0);
+
+ case CMD_DEBUG:
+ if (fd < 0 && ! open_cd ())
+ return (0);
+
+ if (! strcasecmp (arg, "on"))
+ return ioctl (fd, CDIOCSETDEBUG);
+
+ if (! strcasecmp (arg, "off"))
+ return ioctl (fd, CDIOCCLRDEBUG);
+
+ warnx("invalid command arguments");
+
+ return (0);
+
+ case CMD_EJECT:
+ if (fd < 0 && ! open_cd ())
+ return (0);
+
+ (void) ioctl (fd, CDIOCALLOW);
+ rc = ioctl (fd, CDIOCEJECT);
+ if (rc < 0)
+ return (rc);
+ return (0);
+
+ case CMD_CLOSE:
+ if (fd < 0 && ! open_cd ())
+ return (0);
+
+ (void) ioctl (fd, CDIOCALLOW);
+ rc = ioctl (fd, CDIOCCLOSE);
+ if (rc < 0)
+ return (rc);
+ close(fd);
+ fd = -1;
+ return (0);
+
+ case CMD_PLAY:
+ if (fd < 0 && ! open_cd ())
+ return (0);
+
+ while (isspace (*arg))
+ arg++;
+
+ return play (arg);
+
+ case CMD_SET:
+ if (! strcasecmp (arg, "msf"))
+ msf = 1;
+ else if (! strcasecmp (arg, "lba"))
+ msf = 0;
+ else
+ warnx("invalid command arguments");
+ return (0);
+
+ case CMD_VOLUME:
+ if (fd < 0 && !open_cd ())
+ return (0);
+
+ if (! strncasecmp (arg, "left", strlen(arg)))
+ return ioctl (fd, CDIOCSETLEFT);
+
+ if (! strncasecmp (arg, "right", strlen(arg)))
+ return ioctl (fd, CDIOCSETRIGHT);
+
+ if (! strncasecmp (arg, "mono", strlen(arg)))
+ return ioctl (fd, CDIOCSETMONO);
+
+ if (! strncasecmp (arg, "stereo", strlen(arg)))
+ return ioctl (fd, CDIOCSETSTERIO);
+
+ if (! strncasecmp (arg, "mute", strlen(arg)))
+ return ioctl (fd, CDIOCSETMUTE);
+
+ if (2 != sscanf (arg, "%d %d", &l, &r)) {
+ warnx("invalid command arguments");
+ return (0);
+ }
+
+ return setvol (l, r);
+
+ default:
+ case CMD_HELP:
+ help ();
+ return (0);
+
+ }
+}
+
+int play (char *arg)
+{
+ struct ioc_toc_header h;
+ int rc, n, start, end = 0, istart = 1, iend = 1;
+
+ rc = ioctl (fd, CDIOREADTOCHEADER, &h);
+
+ if (rc < 0)
+ return (rc);
+
+ n = h.ending_track - h.starting_track + 1;
+ rc = read_toc_entrys ((n + 1) * sizeof (struct cd_toc_entry));
+
+ if (rc < 0)
+ return (rc);
+
+ if (! arg || ! *arg) {
+ /* Play the whole disc */
+ if (msf)
+ return play_blocks (0, msf2lba (toc_buffer[n].addr.msf.minute,
+ toc_buffer[n].addr.msf.second,
+ toc_buffer[n].addr.msf.frame));
+ else
+ return play_blocks (0, ntohl(toc_buffer[n].addr.lba));
+ }
+
+ if (strchr (arg, '#')) {
+ /* Play block #blk [ len ] */
+ int blk, len = 0;
+
+ if (2 != sscanf (arg, "#%d%d", &blk, &len) &&
+ 1 != sscanf (arg, "#%d", &blk))
+ goto Clean_up;
+
+ if (len == 0) {
+ if (msf)
+ len = msf2lba (toc_buffer[n].addr.msf.minute,
+ toc_buffer[n].addr.msf.second,
+ toc_buffer[n].addr.msf.frame) - blk;
+ else
+ len = ntohl(toc_buffer[n].addr.lba) - blk;
+ }
+ return play_blocks (blk, len);
+ }
+
+ if (strchr (arg, ':')) {
+ /*
+ * Play MSF m1:s1 [ .f1 ] [ m2:s2 [ .f2 ] ]
+ *
+ * Will now also undestand timed addresses relative
+ * to the beginning of a track in the form...
+ *
+ * tr1 m1:s1[.f1] [[tr2] [m2:s2[.f2]]]
+ */
+ unsigned tr1, tr2;
+ unsigned m1, m2, s1, s2, f1, f2;
+ unsigned char tm, ts, tf;
+
+ tr2 = m2 = s2 = f2 = f1 = 0;
+ if (8 == sscanf (arg, "%d %d:%d.%d %d %d:%d.%d",
+ &tr1, &m1, &s1, &f1, &tr2, &m2, &s2, &f2))
+ goto Play_Relative_Addresses;
+
+ tr2 = m2 = s2 = f2 = f1 = 0;
+ if (7 == sscanf (arg, "%d %d:%d %d %d:%d.%d",
+ &tr1, &m1, &s1, &tr2, &m2, &s2, &f2))
+ goto Play_Relative_Addresses;
+
+ tr2 = m2 = s2 = f2 = f1 = 0;
+ if (7 == sscanf (arg, "%d %d:%d.%d %d %d:%d",
+ &tr1, &m1, &s1, &f1, &tr2, &m2, &s2))
+ goto Play_Relative_Addresses;
+
+ tr2 = m2 = s2 = f2 = f1 = 0;
+ if (7 == sscanf (arg, "%d %d:%d.%d %d:%d.%d",
+ &tr1, &m1, &s1, &f1, &m2, &s2, &f2))
+ goto Play_Relative_Addresses;
+
+ tr2 = m2 = s2 = f2 = f1 = 0;
+ if (6 == sscanf (arg, "%d %d:%d.%d %d:%d",
+ &tr1, &m1, &s1, &f1, &m2, &s2))
+ goto Play_Relative_Addresses;
+
+ tr2 = m2 = s2 = f2 = f1 = 0;
+ if (6 == sscanf (arg, "%d %d:%d %d:%d.%d",
+ &tr1, &m1, &s1, &m2, &s2, &f2))
+ goto Play_Relative_Addresses;
+
+ tr2 = m2 = s2 = f2 = f1 = 0;
+ if (6 == sscanf (arg, "%d %d:%d.%d %d %d",
+ &tr1, &m1, &s1, &f1, &tr2, &m2))
+ goto Play_Relative_Addresses;
+
+ tr2 = m2 = s2 = f2 = f1 = 0;
+ if (5 == sscanf (arg, "%d %d:%d %d:%d", &tr1, &m1, &s1, &m2, &s2))
+ goto Play_Relative_Addresses;
+
+ tr2 = m2 = s2 = f2 = f1 = 0;
+ if (5 == sscanf (arg, "%d %d:%d %d %d",
+ &tr1, &m1, &s1, &tr2, &m2))
+ goto Play_Relative_Addresses;
+
+ tr2 = m2 = s2 = f2 = f1 = 0;
+ if (5 == sscanf (arg, "%d %d:%d.%d %d",
+ &tr1, &m1, &s1, &f1, &tr2))
+ goto Play_Relative_Addresses;
+
+ tr2 = m2 = s2 = f2 = f1 = 0;
+ if (4 == sscanf (arg, "%d %d:%d %d", &tr1, &m1, &s1, &tr2))
+ goto Play_Relative_Addresses;
+
+ tr2 = m2 = s2 = f2 = f1 = 0;
+ if (4 == sscanf (arg, "%d %d:%d.%d", &tr1, &m1, &s1, &f1))
+ goto Play_Relative_Addresses;
+
+ tr2 = m2 = s2 = f2 = f1 = 0;
+ if (3 == sscanf (arg, "%d %d:%d", &tr1, &m1, &s1))
+ goto Play_Relative_Addresses;
+
+ tr2 = m2 = s2 = f2 = f1 = 0;
+ goto Try_Absolute_Timed_Addresses;
+
+Play_Relative_Addresses:
+ if (! tr1)
+ tr1 = 1;
+ else if (tr1 > n)
+ tr1 = n;
+
+ if (msf) {
+ tm = toc_buffer[tr1].addr.msf.minute;
+ ts = toc_buffer[tr1].addr.msf.second;
+ tf = toc_buffer[tr1].addr.msf.frame;
+ } else
+ lba2msf(ntohl(toc_buffer[tr1].addr.lba),
+ &tm, &ts, &tf);
+ if ((m1 > tm)
+ || ((m1 == tm)
+ && ((s1 > ts)
+ || ((s1 == ts)
+ && (f1 > tf))))) {
+ printf ("Track %d is not that long.\n", tr1);
+ return (0);
+ }
+
+ tr1--;
+
+ f1 += tf;
+ if (f1 >= 75) {
+ s1 += f1 / 75;
+ f1 %= 75;
+ }
+
+ s1 += ts;
+ if (s1 >= 60) {
+ m1 += s1 / 60;
+ s1 %= 60;
+ }
+
+ m1 += tm;
+
+ if (! tr2) {
+ if (m2 || s2 || f2) {
+ tr2 = tr1;
+ f2 += f1;
+ if (f2 >= 75) {
+ s2 += f2 / 75;
+ f2 %= 75;
+ }
+
+ s2 += s1;
+ if (s2 > 60) {
+ m2 += s2 / 60;
+ s2 %= 60;
+ }
+
+ m2 += m1;
+ } else {
+ tr2 = n;
+ if (msf) {
+ m2 = toc_buffer[n].addr.msf.minute;
+ s2 = toc_buffer[n].addr.msf.second;
+ f2 = toc_buffer[n].addr.msf.frame;
+ } else {
+ lba2msf(ntohl(toc_buffer[n].addr.lba),
+ &tm, &ts, &tf);
+ m2 = tm;
+ s2 = ts;
+ f2 = tf;
+ }
+ }
+ } else if (tr2 > n) {
+ tr2 = n;
+ m2 = s2 = f2 = 0;
+ } else {
+ if (m2 || s2 || f2)
+ tr2--;
+ if (msf) {
+ tm = toc_buffer[tr2].addr.msf.minute;
+ ts = toc_buffer[tr2].addr.msf.second;
+ tf = toc_buffer[tr2].addr.msf.frame;
+ } else
+ lba2msf(ntohl(toc_buffer[tr2].addr.lba),
+ &tm, &ts, &tf);
+ f2 += tf;
+ if (f2 >= 75) {
+ s2 += f2 / 75;
+ f2 %= 75;
+ }
+
+ s2 += ts;
+ if (s2 > 60) {
+ m2 += s2 / 60;
+ s2 %= 60;
+ }
+
+ m2 += tm;
+ }
+
+ if (msf) {
+ tm = toc_buffer[n].addr.msf.minute;
+ ts = toc_buffer[n].addr.msf.second;
+ tf = toc_buffer[n].addr.msf.frame;
+ } else
+ lba2msf(ntohl(toc_buffer[n].addr.lba),
+ &tm, &ts, &tf);
+ if ((tr2 < n)
+ && ((m2 > tm)
+ || ((m2 == tm)
+ && ((s2 > ts)
+ || ((s2 == ts)
+ && (f2 > tf)))))) {
+ printf ("The playing time of the disc is not that long.\n");
+ return (0);
+ }
+ return (play_msf (m1, s1, f1, m2, s2, f2));
+
+Try_Absolute_Timed_Addresses:
+ if (6 != sscanf (arg, "%d:%d.%d%d:%d.%d",
+ &m1, &s1, &f1, &m2, &s2, &f2) &&
+ 5 != sscanf (arg, "%d:%d.%d%d:%d", &m1, &s1, &f1, &m2, &s2) &&
+ 5 != sscanf (arg, "%d:%d%d:%d.%d", &m1, &s1, &m2, &s2, &f2) &&
+ 3 != sscanf (arg, "%d:%d.%d", &m1, &s1, &f1) &&
+ 4 != sscanf (arg, "%d:%d%d:%d", &m1, &s1, &m2, &s2) &&
+ 2 != sscanf (arg, "%d:%d", &m1, &s1))
+ goto Clean_up;
+
+ if (m2 == 0) {
+ if (msf) {
+ m2 = toc_buffer[n].addr.msf.minute;
+ s2 = toc_buffer[n].addr.msf.second;
+ f2 = toc_buffer[n].addr.msf.frame;
+ } else {
+ lba2msf(ntohl(toc_buffer[n].addr.lba),
+ &tm, &ts, &tf);
+ m2 = tm;
+ s2 = ts;
+ f2 = tf;
+ }
+ }
+ return play_msf (m1, s1, f1, m2, s2, f2);
+ }
+
+ /*
+ * Play track trk1 [ .idx1 ] [ trk2 [ .idx2 ] ]
+ */
+ if (4 != sscanf (arg, "%d.%d%d.%d", &start, &istart, &end, &iend) &&
+ 3 != sscanf (arg, "%d.%d%d", &start, &istart, &end) &&
+ 3 != sscanf (arg, "%d%d.%d", &start, &end, &iend) &&
+ 2 != sscanf (arg, "%d.%d", &start, &istart) &&
+ 2 != sscanf (arg, "%d%d", &start, &end) &&
+ 1 != sscanf (arg, "%d", &start))
+ goto Clean_up;
+
+ if (end == 0)
+ end = n;
+ return (play_track (start, istart, end, iend));
+
+Clean_up:
+ warnx("invalid command arguments");
+ return (0);
+}
+
+char *strstatus (int sts)
+{
+ switch (sts) {
+ case ASTS_INVALID: return ("invalid");
+ case ASTS_PLAYING: return ("playing");
+ case ASTS_PAUSED: return ("paused");
+ case ASTS_COMPLETED: return ("completed");
+ case ASTS_ERROR: return ("error");
+ case ASTS_VOID: return ("void");
+ default: return ("??");
+ }
+}
+
+int pstatus (char *arg)
+{
+ struct ioc_vol v;
+ struct ioc_read_subchannel ss;
+ struct cd_sub_channel_info data;
+ int rc, trk, m, s, f;
+
+ 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");
+
+ bzero (&ss, sizeof (ss));
+ ss.data = &data;
+ ss.data_len = sizeof (data);
+ ss.address_format = msf ? CD_MSF_FORMAT : CD_LBA_FORMAT;
+ ss.data_format = CD_MEDIA_CATALOG;
+ rc = ioctl (fd, CDIOCREADSUBCHANNEL, (char *) &ss);
+ if (rc >= 0) {
+ printf("Media catalog is %sactive",
+ ss.data->what.media_catalog.mc_valid ? "": "in");
+ if (ss.data->what.media_catalog.mc_valid &&
+ ss.data->what.media_catalog.mc_number[0])
+ printf(", number \"%.15s\"",
+ ss.data->what.media_catalog.mc_number);
+ putchar('\n');
+ } else
+ printf("No media catalog info available\n");
+
+ rc = ioctl (fd, CDIOCGETVOL, &v);
+ if (rc >= 0)
+ if (verbose)
+ printf ("Left volume = %d, right volume = %d\n",
+ v.vol[0], v.vol[1]);
+ else
+ printf ("%d %d\n", v.vol[0], v.vol[1]);
+ else
+ printf ("No volume level info available\n");
+ return(0);
+}
+
+int info (char *arg)
+{
+ struct ioc_toc_header h;
+ int rc, i, n;
+
+ rc = ioctl (fd, CDIOREADTOCHEADER, &h);
+ if (rc >= 0) {
+ if (verbose)
+ printf ("Starting track = %d, ending track = %d, TOC size = %d bytes\n",
+ h.starting_track, h.ending_track, h.len);
+ else
+ printf ("%d %d %d\n", h.starting_track,
+ h.ending_track, h.len);
+ } else {
+ warn("getting toc header");
+ return (rc);
+ }
+
+ n = h.ending_track - h.starting_track + 1;
+ rc = read_toc_entrys ((n + 1) * sizeof (struct cd_toc_entry));
+ if (rc < 0)
+ return (rc);
+
+ if (verbose) {
+ printf ("track start duration block length type\n");
+ printf ("-------------------------------------------------\n");
+ }
+
+ for (i = 0; i < n; i++) {
+ printf ("%5d ", toc_buffer[i].track);
+ prtrack (toc_buffer + i, 0);
+ }
+ printf ("%5d ", toc_buffer[n].track);
+ prtrack (toc_buffer + n, 1);
+ return (0);
+}
+
+void lba2msf (unsigned long lba, u_char *m, u_char *s, u_char *f)
+{
+ lba += 150; /* block start offset */
+ lba &= 0xffffff; /* negative lbas use only 24 bits */
+ *m = lba / (60 * 75);
+ lba %= (60 * 75);
+ *s = lba / 75;
+ *f = lba % 75;
+}
+
+unsigned int msf2lba (u_char m, u_char s, u_char f)
+{
+ return (((m * 60) + s) * 75 + f) - 150;
+}
+
+void prtrack (struct cd_toc_entry *e, int lastflag)
+{
+ int block, next, len;
+ u_char m, s, f;
+
+ if (msf) {
+ /* Print track start */
+ printf ("%2d:%02d.%02d ", e->addr.msf.minute,
+ e->addr.msf.second, e->addr.msf.frame);
+
+ block = msf2lba (e->addr.msf.minute, e->addr.msf.second,
+ e->addr.msf.frame);
+ } else {
+ block = ntohl(e->addr.lba);
+ lba2msf(block, &m, &s, &f);
+ /* Print track start */
+ printf ("%2d:%02d.%02d ", m, s, f);
+ }
+ if (lastflag) {
+ /* Last track -- print block */
+ printf (" - %6d - -\n", block);
+ return;
+ }
+
+ if (msf)
+ next = msf2lba (e[1].addr.msf.minute, e[1].addr.msf.second,
+ e[1].addr.msf.frame);
+ else
+ next = ntohl(e[1].addr.lba);
+ len = next - block;
+ lba2msf (len, &m, &s, &f);
+
+ /* Print duration, block, length, type */
+ printf ("%2d:%02d.%02d %6d %6d %5s\n", m, s, f, block, len,
+ (e->control & 4) ? "data" : "audio");
+}
+
+int play_track (int tstart, int istart, int tend, int iend)
+{
+ struct ioc_play_track t;
+
+ t.start_track = tstart;
+ t.start_index = istart;
+ t.end_track = tend;
+ t.end_index = iend;
+
+ return ioctl (fd, CDIOCPLAYTRACKS, &t);
+}
+
+int play_blocks (int blk, int len)
+{
+ struct ioc_play_blocks t;
+
+ t.blk = blk;
+ t.len = len;
+
+ return ioctl (fd, CDIOCPLAYBLOCKS, &t);
+}
+
+int setvol (int left, int right)
+{
+ struct ioc_vol v;
+
+ v.vol[0] = left;
+ v.vol[1] = right;
+ v.vol[2] = 0;
+ v.vol[3] = 0;
+
+ return ioctl (fd, CDIOCSETVOL, &v);
+}
+
+int read_toc_entrys (int len)
+{
+ struct ioc_read_toc_entry t;
+
+ t.address_format = msf ? CD_MSF_FORMAT : CD_LBA_FORMAT;
+ t.starting_track = 0;
+ t.data_len = len;
+ t.data = toc_buffer;
+
+ return (ioctl (fd, CDIOREADTOCENTRYS, (char *) &t));
+}
+
+int play_msf (int start_m, int start_s, int start_f,
+ int end_m, int end_s, int end_f)
+{
+ struct ioc_play_msf a;
+
+ a.start_m = start_m;
+ a.start_s = start_s;
+ a.start_f = start_f;
+ a.end_m = end_m;
+ a.end_s = end_s;
+ a.end_f = end_f;
+
+ return ioctl (fd, CDIOCPLAYMSF, (char *) &a);
+}
+
+int status (int *trk, int *min, int *sec, int *frame)
+{
+ struct ioc_read_subchannel s;
+ struct cd_sub_channel_info data;
+ u_char mm, ss, ff;
+
+ bzero (&s, sizeof (s));
+ s.data = &data;
+ s.data_len = sizeof (data);
+ s.address_format = msf ? CD_MSF_FORMAT : CD_LBA_FORMAT;
+ s.data_format = CD_CURRENT_POSITION;
+
+ if (ioctl (fd, CDIOCREADSUBCHANNEL, (char *) &s) < 0)
+ return -1;
+
+ *trk = s.data->what.position.track_number;
+ if (msf) {
+ *min = s.data->what.position.reladdr.msf.minute;
+ *sec = s.data->what.position.reladdr.msf.second;
+ *frame = s.data->what.position.reladdr.msf.frame;
+ } else {
+ lba2msf(ntohl(s.data->what.position.reladdr.lba),
+ &mm, &ss, &ff);
+ *min = mm;
+ *sec = ss;
+ *frame = ff;
+ }
+
+ return s.data->header.audio_status;
+}
+
+char *input (int *cmd)
+{
+ static char buf[80];
+ char *p;
+
+ do {
+ if (verbose)
+ fprintf (stderr, "cdcontrol> ");
+ if (! fgets (buf, sizeof (buf), stdin)) {
+ *cmd = CMD_QUIT;
+ fprintf (stderr, "\r\n");
+ return (0);
+ }
+ p = parse (buf, cmd);
+ } while (! p);
+ return (p);
+}
+
+char *parse (char *buf, int *cmd)
+{
+ struct cmdtab *c;
+ char *p;
+ int len;
+
+ for (p=buf; isspace (*p); p++)
+ continue;
+
+ if (isdigit (*p) || (p[0] == '#' && isdigit (p[1]))) {
+ *cmd = CMD_PLAY;
+ return (p);
+ }
+
+ for (buf = p; *p && ! isspace (*p); p++)
+ continue;
+
+ len = p - buf;
+ if (! len)
+ return (0);
+
+ if (*p) { /* It must be a spacing character! */
+ char *q;
+
+ *p++ = 0;
+ for (q=p; *q && *q != '\n' && *q != '\r'; q++)
+ continue;
+ *q = 0;
+ }
+
+ *cmd = -1;
+ for (c=cmdtab; c->name; ++c) {
+ /* Is it an exact match? */
+ if (! strcasecmp (buf, c->name)) {
+ *cmd = c->command;
+ break;
+ }
+
+ /* Try short hand forms then... */
+ if (len >= c->min && ! strncasecmp (buf, c->name, len)) {
+ if (*cmd != -1 && *cmd != c->command) {
+ warnx("ambiguous command");
+ return (0);
+ }
+ *cmd = c->command;
+ }
+ }
+
+ if (*cmd == -1) {
+ warnx("invalid command, enter ``help'' for commands");
+ return (0);
+ }
+
+ while (isspace (*p))
+ p++;
+ return p;
+}
+
+int open_cd ()
+{
+ char devbuf[80];
+
+ if (fd > -1)
+ return (1);
+
+ if (*cdname == '/')
+ strcpy (devbuf, cdname);
+ else if (*cdname == 'r')
+ sprintf (devbuf, "/dev/%s", cdname);
+ else
+ sprintf (devbuf, "/dev/r%s", cdname);
+
+ fd = open (devbuf, O_RDONLY);
+
+ if (fd < 0 && errno == ENOENT) {
+ strcat (devbuf, DEFAULT_CD_PARTITION);
+ fd = open (devbuf, O_RDONLY);
+ }
+
+ if (fd < 0) {
+ if (errno == ENXIO) {
+ /* ENXIO has an overloaded meaning here.
+ * The original "Device not configured" should
+ * be interpreted as "No disc in drive %s". */
+ warnx("no disc in drive %s", devbuf);
+ return (0);
+ }
+ err(1, "%s", devbuf);
+ }
+ return (1);
+}
diff --git a/usr.sbin/chown/Makefile b/usr.sbin/chown/Makefile
new file mode 100644
index 0000000..9a4974c
--- /dev/null
+++ b/usr.sbin/chown/Makefile
@@ -0,0 +1,9 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= chown
+CFLAGS+=-DSUPPORT_DOT
+MAN1= chgrp.1
+MAN8= chown.8
+LINKS= ${BINDIR}/chown /usr/bin/chgrp
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/chown/chgrp.1 b/usr.sbin/chown/chgrp.1
new file mode 100644
index 0000000..67122c4
--- /dev/null
+++ b/usr.sbin/chown/chgrp.1
@@ -0,0 +1,133 @@
+.\" Copyright (c) 1983, 1990, 1993, 1994
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" the Institute of Electrical and Electronics Engineers, Inc.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)chgrp.1 8.3 (Berkeley) 3/31/94
+.\"
+.Dd March 31, 1994
+.Dt CHGRP 1
+.Os BSD 4.2
+.Sh NAME
+.Nm chgrp
+.Nd change group
+.Sh SYNOPSIS
+.Nm chgrp
+.Oo
+.Fl R
+.Op Fl H | Fl L | Fl P
+.Oc
+.Op Fl f
+.Op Fl h
+.Ar group
+.Ar files ...
+.Sh DESCRIPTION
+The
+.Nm
+utility sets the group ID of the file named by each
+.Ar file
+operand to the
+.Ar group
+ID specified by the group operand.
+.Pp
+Options:
+.Bl -tag -width Ds
+.It Fl H
+If the
+.Fl R
+option is specified, symbolic links on the command line are followed.
+(Symbolic links encountered in the tree traversal are not followed).
+.It Fl L
+If the
+.Fl R
+option is specified, all symbolic links are followed.
+.It Fl P
+If the
+.Fl R
+option is specified, no symbolic links are followed.
+.It Fl R
+Change the group ID for the file hierarchies rooted
+in the files instead of just the files themselves.
+.It Fl f
+The force option ignores errors, except for usage errors and doesn't
+query about strange modes (unless the user does not have proper permissions).
+.It Fl h
+If the file is a symbolic link, the group ID of the link itself is changed
+rather than the file that is pointed to.
+.El
+.Pp
+The
+.Fl H ,
+.Fl L
+and
+.Fl P
+options are ignored unless the
+.Fl R
+option is specified.
+In addition, these options override each other and the
+command's actions are determined by the last one specified.
+.Pp
+The
+.Ar group
+operand can be either a group name from the group database,
+or a numeric group ID.
+If a group name is also a numeric group ID, the operand is used as a
+group name.
+.Pp
+The user invoking
+.Nm
+must belong to the specified group and be the owner of the file,
+or be the super-user.
+.Pp
+The
+.Nm
+utility exits 0 on success, and >0 if an error occurs.
+.Sh COMPATIBILITY
+In previous versions of this system, symbolic links did not have groups.
+.Sh FILES
+.Bl -tag -width /etc/group -compact
+.It Pa /etc/group
+Group ID file
+.El
+.Sh SEE ALSO
+.Xr chown 2 ,
+.Xr fts 3 ,
+.Xr group 5 ,
+.Xr passwd 5 ,
+.Xr symlink 7 ,
+.Xr chown 8
+.Sh STANDARDS
+The
+.Nm
+utility is expected to be
+.St -p1003.2
+compatible.
diff --git a/usr.sbin/chown/chown.8 b/usr.sbin/chown/chown.8
new file mode 100644
index 0000000..38372c2
--- /dev/null
+++ b/usr.sbin/chown/chown.8
@@ -0,0 +1,152 @@
+.\" Copyright (c) 1990, 1991, 1993, 1994
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)chown.8 8.3 (Berkeley) 3/31/94
+.\" $Id: chown.8,v 1.5 1997/03/31 13:03:47 peter Exp $
+.\"
+.Dd March 31, 1994
+.Dt CHOWN 8
+.Os BSD 4
+.Sh NAME
+.Nm chown
+.Nd change file owner and group
+.Sh SYNOPSIS
+.Nm chown
+.Oo
+.Fl R
+.Op Fl H | Fl L | Fl P
+.Oc
+.Op Fl f
+.Op Fl h
+.Ar owner Op Ar :group
+.Ar file ...
+.Nm chown
+.Oo
+.Fl R
+.Op Fl H | Fl L | Fl P
+.Oc
+.Op Fl f
+.Op Fl h
+.Ar :group
+.Ar
+.Sh DESCRIPTION
+.Nm Chown
+sets the user ID and/or the group ID of the specified files.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl H
+If the
+.Fl R
+option is specified, symbolic links on the command line are followed.
+(Symbolic links encountered in the tree traversal are not followed).
+.It Fl L
+If the
+.Fl R
+option is specified, all symbolic links are followed.
+.It Fl P
+If the
+.Fl R
+option is specified, no symbolic links are followed.
+.It Fl R
+Change the user ID and/or the group ID for the file hierarchies rooted
+in the files instead of just the files themselves.
+.It Fl f
+Don't report any failure to change file owner or group, nor modify
+the exit status to reflect such failures.
+.It Fl h
+If the file is a symbolic link, change the user ID and/or the group ID
+of the link itself rather than the file that the link points to.
+.El
+.Pp
+The
+.Fl H ,
+.Fl L
+and
+.Fl P
+options are ignored unless the
+.Fl R
+option is specified.
+In addition, these options override each other and the
+command's actions are determined by the last one specified.
+.Pp
+The
+.Ar owner
+and
+.Ar group
+operands are both optional, however, one must be specified.
+If the
+.Ar group
+operand is specified, it must be preceded by a colon (``:'') character.
+.Pp
+The
+.Ar owner
+may be either a numeric user ID or a user name.
+If a user name is also a numeric user ID, the operand is used as a
+user name.
+The
+.Ar group
+may be either a numeric group ID or a group name.
+If a group name is also a numeric group ID, the operand is used as a
+group name.
+.Pp
+The ownership of a file may only be altered by a super-user for
+obvious security reasons.
+.Pp
+The
+.Nm
+utility exits 0 on success, and >0 if an error occurs.
+.Sh COMPATIBILITY
+Previous versions of the
+.Nm
+utility used the dot (``.'') character to distinguish the group name.
+This has been changed to be a colon (``:'') character so that user and
+group names may contain the dot character.
+.Pp
+On previous versions of this system, symbolic links did not have
+owners.
+.Sh SEE ALSO
+.Xr chgrp 1 ,
+.Xr find 1 ,
+.Xr chown 2 ,
+.Xr fts 3 ,
+.Xr symlink 7
+.Sh STANDARDS
+The
+.Nm
+command is expected to be
+.St -p1003.2
+compliant.
+.Sh HISTORY
+A
+.Nm
+command appeared in
+.At v1 .
diff --git a/usr.sbin/chown/chown.c b/usr.sbin/chown/chown.c
new file mode 100644
index 0000000..918b8d9
--- /dev/null
+++ b/usr.sbin/chown/chown.c
@@ -0,0 +1,273 @@
+/*
+ * Copyright (c) 1988, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 1988, 1993, 1994\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)chown.c 8.8 (Berkeley) 4/4/94";
+#else
+static const char rcsid[] =
+ "$Id: chown.c,v 1.8 1997/09/04 11:55:45 charnier Exp $";
+#endif
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include <ctype.h>
+#include <dirent.h>
+#include <err.h>
+#include <errno.h>
+#include <fts.h>
+#include <grp.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+void a_gid __P((char *));
+void a_uid __P((char *));
+void chownerr __P((char *));
+u_long id __P((char *, char *));
+void usage __P((void));
+
+uid_t uid;
+gid_t gid;
+int Rflag, ischown, fflag, hflag;
+char *gname, *myname;
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ FTS *ftsp;
+ FTSENT *p;
+ int Hflag, Lflag, Pflag, ch, fts_options, hflag, rval;
+ char *cp;
+
+ myname = (cp = rindex(*argv, '/')) ? cp + 1 : *argv;
+ ischown = myname[2] == 'o';
+
+ Hflag = Lflag = Pflag = hflag = 0;
+ while ((ch = getopt(argc, argv, "HLPRfh")) != -1)
+ switch (ch) {
+ case 'H':
+ Hflag = 1;
+ Lflag = Pflag = 0;
+ break;
+ case 'L':
+ Lflag = 1;
+ Hflag = Pflag = 0;
+ break;
+ case 'P':
+ Pflag = 1;
+ Hflag = Lflag = 0;
+ break;
+ case 'R':
+ Rflag = 1;
+ break;
+ case 'f':
+ fflag = 1;
+ break;
+ case 'h':
+ hflag = 1;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argv += optind;
+ argc -= optind;
+
+ if (argc < 2)
+ usage();
+
+ fts_options = FTS_PHYSICAL;
+ if (Rflag) {
+ if (hflag)
+ errx(1, "the -R and -h options may not be specified together");
+ if (Hflag)
+ fts_options |= FTS_COMFOLLOW;
+ if (Lflag) {
+ fts_options &= ~FTS_PHYSICAL;
+ fts_options |= FTS_LOGICAL;
+ }
+ }
+
+ uid = gid = -1;
+ if (ischown) {
+ if ((cp = strchr(*argv, ':')) != NULL) {
+ *cp++ = '\0';
+ a_gid(cp);
+ }
+#ifdef SUPPORT_DOT
+ else if ((cp = strchr(*argv, '.')) != NULL) {
+ *cp++ = '\0';
+ a_gid(cp);
+ }
+#endif
+ a_uid(*argv);
+ } else
+ a_gid(*argv);
+
+ if ((ftsp = fts_open(++argv, fts_options, 0)) == NULL)
+ err(1, NULL);
+
+ for (rval = 0; (p = fts_read(ftsp)) != NULL;) {
+ switch (p->fts_info) {
+ case FTS_D: /* Change it at FTS_DP. */
+ if (!Rflag)
+ fts_set(ftsp, p, FTS_SKIP);
+ continue;
+ case FTS_DNR: /* Warn, chown, continue. */
+ warnx("%s: %s", p->fts_path, strerror(p->fts_errno));
+ rval = 1;
+ break;
+ case FTS_ERR: /* Warn, continue. */
+ case FTS_NS:
+ warnx("%s: %s", p->fts_path, strerror(p->fts_errno));
+ rval = 1;
+ continue;
+ case FTS_SL: /* Ignore. */
+ case FTS_SLNONE:
+ /*
+ * The only symlinks that end up here are ones that
+ * don't point to anything and ones that we found
+ * doing a physical walk.
+ */
+ if (hflag)
+ break;
+ else
+ continue;
+ default:
+ break;
+ }
+ if (hflag) {
+ if (lchown(p->fts_accpath, uid, gid) && !fflag) {
+ chownerr(p->fts_path);
+ rval = 1;
+ }
+ } else {
+ if (chown(p->fts_accpath, uid, gid) && !fflag) {
+ chownerr(p->fts_path);
+ rval = 1;
+ }
+ }
+ }
+ if (errno)
+ err(1, "fts_read");
+ exit(rval);
+}
+
+void
+a_gid(s)
+ char *s;
+{
+ struct group *gr;
+
+ if (*s == '\0') /* Argument was "uid[:.]". */
+ return;
+ gname = s;
+ gid = ((gr = getgrnam(s)) == NULL) ? id(s, "group") : gr->gr_gid;
+}
+
+void
+a_uid(s)
+ char *s;
+{
+ struct passwd *pw;
+
+ if (*s == '\0') /* Argument was "[:.]gid". */
+ return;
+ uid = ((pw = getpwnam(s)) == NULL) ? id(s, "user") : pw->pw_uid;
+}
+
+u_long
+id(name, type)
+ char *name, *type;
+{
+ u_long val;
+ char *ep;
+
+ /*
+ * XXX
+ * We know that uid_t's and gid_t's are unsigned longs.
+ */
+ errno = 0;
+ val = strtoul(name, &ep, 10);
+ if (errno)
+ err(1, "%s", name);
+ if (*ep != '\0')
+ errx(1, "%s: illegal %s name", name, type);
+ return (val);
+}
+
+void
+chownerr(file)
+ char *file;
+{
+ static int euid = -1, ngroups = -1;
+ gid_t groups[NGROUPS];
+
+ /* Check for chown without being root. */
+ if (errno != EPERM ||
+ (uid != -1 && euid == -1 && (euid = geteuid()) != 0))
+ err(1, "%s", file);
+
+ /* Check group membership; kernel just returns EPERM. */
+ if (gid != -1 && ngroups == -1 &&
+ euid == -1 && (euid = geteuid()) != 0) {
+ ngroups = getgroups(NGROUPS, groups);
+ while (--ngroups >= 0 && gid != groups[ngroups]);
+ if (ngroups < 0)
+ errx(1, "you are not a member of group %s", gname);
+ }
+ warn("%s", file);
+}
+
+void
+usage()
+{
+ (void)fprintf(stderr, "%s\n%s\n%s\n",
+ "usage: chown [-R [-H | -L | -P]] [-f] [-h] owner[:group] file ...",
+ " chown [-R [-H | -L | -P]] [-f] [-h] :group file ...",
+ " chgrp [-R [-H | -L | -P]] [-f] [-h] group file ...");
+ exit(1);
+}
diff --git a/usr.sbin/chroot/Makefile b/usr.sbin/chroot/Makefile
new file mode 100644
index 0000000..69fe8b8
--- /dev/null
+++ b/usr.sbin/chroot/Makefile
@@ -0,0 +1,6 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= chroot
+MAN8= chroot.8
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/chroot/chroot.8 b/usr.sbin/chroot/chroot.8
new file mode 100644
index 0000000..607cfe3
--- /dev/null
+++ b/usr.sbin/chroot/chroot.8
@@ -0,0 +1,80 @@
+.\" Copyright (c) 1988, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)chroot.8 8.1 (Berkeley) 6/9/93
+.\"
+.Dd June 9, 1993
+.Dt CHROOT 8
+.Os BSD 4.3
+.Sh NAME
+.Nm chroot
+.Nd change root directory
+.Sh SYNOPSIS
+.Nm chroot
+.Ar newroot
+.Op Ar command
+.Sh DESCRIPTION
+The
+.Nm
+command changes its root directory to the supplied directory
+.Ar newroot
+and exec's
+.Ar command ,
+if supplied, or an interactive copy of your shell.
+.Pp
+Note,
+.Ar command
+or the shell are run as your real-user-id.
+.Sh ENVIRONMENT
+The following environment variable is referenced by
+.Nm chroot :
+.Bl -tag -width SHELL
+.It Ev SHELL
+If set,
+the string specified by
+.Ev SHELL
+is interpreted as the name of
+the shell to exec.
+If the variable
+.Ev SHELL
+is not set,
+.Pa /bin/sh
+is used.
+.El
+.Sh SEE ALSO
+.Xr chdir 2 ,
+.Xr chroot 2 ,
+.Xr environ 7
+.Sh HISTORY
+The
+.Nm
+utility first appeared in
+.Bx 4.4 .
diff --git a/usr.sbin/chroot/chroot.c b/usr.sbin/chroot/chroot.c
new file mode 100644
index 0000000..4049c6c
--- /dev/null
+++ b/usr.sbin/chroot/chroot.c
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 1988, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)chroot.c 8.1 (Berkeley) 6/9/93";
+#endif
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#include <err.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+static void usage __P((void));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int ch;
+ char *shell;
+
+ while ((ch = getopt(argc, argv, "")) != -1)
+ switch(ch) {
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc < 1)
+ usage();
+
+ if (chdir(argv[0]) || chroot("."))
+ err(1, "%s", argv[0]);
+
+ if (argv[1]) {
+ execvp(argv[1], &argv[1]);
+ err(1, "%s", argv[1]);
+ }
+
+ if (!(shell = getenv("SHELL")))
+ shell = _PATH_BSHELL;
+ execlp(shell, shell, "-i", NULL);
+ err(1, "%s", shell);
+ /* NOTREACHED */
+}
+
+static void
+usage()
+{
+ (void)fprintf(stderr, "usage: chroot newroot [command]\n");
+ exit(1);
+}
diff --git a/usr.sbin/ckdist/Makefile b/usr.sbin/ckdist/Makefile
new file mode 100644
index 0000000..884fe9e
--- /dev/null
+++ b/usr.sbin/ckdist/Makefile
@@ -0,0 +1,12 @@
+# $Id: Makefile,v 1.1 1997/01/14 14:50:52 rnordier Exp $
+
+PROG= ckdist
+
+.PATH: ${.CURDIR}/../../usr.bin/cksum
+
+SRCS= ckdist.c crc.c
+
+DPADD= ${LIBMD}
+LDADD= -lmd
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/ckdist/ckdist.1 b/usr.sbin/ckdist/ckdist.1
new file mode 100644
index 0000000..dd9b78e
--- /dev/null
+++ b/usr.sbin/ckdist/ckdist.1
@@ -0,0 +1,96 @@
+.\" Copyright (c) 1997 Robert Nordier
+.\" All rights reserved.
+.\"
+.\" $Id: ckdist.1,v 1.1.1.1 1997/01/21 12:58:53 jkh Exp $
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS
+.\" OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+.\" WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+.\" DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT,
+.\" INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+.\" (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+.\" SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+.\" STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+.\" IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+.\" POSSIBILITY OF SUCH DAMAGE.
+.\"
+.Dd January 20, 1997
+.Dt CKDIST 1
+.Os
+.Sh NAME
+.Nm ckdist
+.Nd check software distributions
+.Sh SYNOPSIS
+.Nm ckdist
+.Bq Fl airsx
+.Bq Fl d Ar dir
+.Bq Fl n Ar name
+.Bq Fl t Ar type
+.Ar
+.Sh DESCRIPTION
+The
+.Nm
+utility reads "checksum" files (which are assumed to specify components
+of a software distribution) and verifies the integrity of the
+distribution by validating the checksum of each component file.
+Both MD5 (128-bit "message digest") and .inf (32-bit CRC) checksum
+formats are supported.
+.Pp
+The
+.Ar file
+operands may refer to regular files or to directories. Regular files
+named "md5", or which have an ".md5" or an ".inf" extension, are
+assumed to be of the implied type, otherwise format is determined from
+content. If a directory is specified, it is searched for
+appropriately-named files only.
+.Pp
+Options are as follows:
+.Bl -tag -width 8n -offset indent
+.It Fl a
+Report on all distribution components, not just those in respect of
+which errors are detected.
+.It Fl i
+Ignore missing distribution components.
+.It Fl r
+Search specified directories recursively.
+.It Fl s
+Suppress complaints about inaccessible checksum files and directories.
+.It Fl x
+Verify the existence of distribution components (and also check sizes,
+in the case of .inf files), but omit the more time-consuming step of
+actually computing and comparing checksums.
+.It Fl d Ar dir
+Look for distribution components in the directory
+.Ar dir .
+.It Fl n Ar name
+Access distribution components using the filename
+.Ar name .
+When accessing .inf file components, append the appropriate
+extension to the filename.
+.It Fl t Ar type
+Assume that all specified checksum files are of the format
+.Ar type ,
+and search directories only for files in this format (where
+.Ar type
+is either "md5" or "inf").
+.El
+.Sh SEE ALSO
+cksum(1), md5(1)
+.Sh DIAGNOSTICS
+Exit status is 0 if no errors were detected, 1 if errors were found in
+a distribution, and 2 if usage errors, inaccessible input files, or
+other system errors were encountered.
+.Sh NOTES
+Both BSD and DOS versions of
+.Nm
+are available.
diff --git a/usr.sbin/ckdist/ckdist.c b/usr.sbin/ckdist/ckdist.c
new file mode 100644
index 0000000..e71a581
--- /dev/null
+++ b/usr.sbin/ckdist/ckdist.c
@@ -0,0 +1,440 @@
+/*
+ * Copyright (c) 1997 Robert Nordier
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in
+ * the documentation and/or other materials provided with the
+ * distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
+ * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <fts.h>
+#include <md5.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+extern int crc(int fd, u_long * cval, u_long * clen);
+
+#define DISTMD5 1 /* MD5 format */
+#define DISTINF 2 /* .inf format */
+#define DISTTYPES 2 /* types supported */
+
+#define E_UNKNOWN 1 /* Unknown format */
+#define E_BADMD5 2 /* Invalid MD5 format */
+#define E_BADINF 3 /* Invalid .inf format */
+#define E_NAME 4 /* Can't derive component name */
+#define E_LENGTH 5 /* Length mismatch */
+#define E_CHKSUM 6 /* Checksum mismatch */
+#define E_ERRNO 7 /* sys_errlist[errno] */
+
+#define isfatal(err) ((err) && (err) <= E_NAME)
+
+#define NAMESIZE 256 /* filename buffer size */
+#define MDSUMLEN 32 /* length of MD5 message digest */
+
+#define isstdin(path) ((path)[0] == '-' && !(path)[1])
+
+static const char *opt_dir; /* where to look for components */
+static const char *opt_name; /* name for accessing components */
+static int opt_all; /* report on all components */
+static int opt_ignore; /* ignore missing components */
+static int opt_recurse; /* search directories recursively */
+static int opt_silent; /* silent about inaccessible files */
+static int opt_type; /* dist type: md5 or inf */
+static int opt_exist; /* just verify existence */
+
+static int ckdist(const char *path, int type);
+static int chkmd5(FILE * fp, const char *path);
+static int chkinf(FILE * fp, const char *path);
+static int report(const char *path, const char *name, int error);
+static const char *distname(const char *path, const char *name,
+ const char *ext);
+static char *stripath(const char *path);
+static int distfile(const char *path);
+static int disttype(const char *name);
+static int fail(const char *path, const char *msg);
+static void usage(void);
+
+int
+main(int argc, char *argv[])
+{
+ static char *arg[2];
+ struct stat sb;
+ FTS *ftsp;
+ FTSENT *f;
+ int rval, c, type;
+
+ while ((c = getopt(argc, argv, "ad:in:rst:x")) != -1)
+ switch (c) {
+ case 'a':
+ opt_all = 1;
+ break;
+ case 'd':
+ opt_dir = optarg;
+ break;
+ case 'i':
+ opt_ignore = 1;
+ break;
+ case 'n':
+ opt_name = optarg;
+ break;
+ case 'r':
+ opt_recurse = 1;
+ break;
+ case 's':
+ opt_silent = 1;
+ break;
+ case 't':
+ if ((opt_type = disttype(optarg)) == 0) {
+ warnx("illegal argument to -t option");
+ usage();
+ }
+ break;
+ case 'x':
+ opt_exist = 1;
+ break;
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+ if (argc < 1)
+ usage();
+ if (opt_dir) {
+ if (stat(opt_dir, &sb))
+ err(2, opt_dir);
+ if (!S_ISDIR(sb.st_mode))
+ errx(2, "%s: not a directory", opt_dir);
+ }
+ rval = 0;
+ do {
+ if (isstdin(*argv))
+ rval |= ckdist(*argv, opt_type);
+ else if (stat(*argv, &sb))
+ rval |= fail(*argv, NULL);
+ else if (S_ISREG(sb.st_mode))
+ rval |= ckdist(*argv, opt_type);
+ else {
+ arg[0] = *argv;
+ if ((ftsp = fts_open(arg, FTS_LOGICAL, NULL)) == NULL)
+ err(2, "fts_open");
+ while ((f = fts_read(ftsp)) != NULL)
+ switch (f->fts_info) {
+ case FTS_DC:
+ rval = fail(f->fts_path, "Directory causes a cycle");
+ break;
+ case FTS_DNR:
+ case FTS_ERR:
+ case FTS_NS:
+ rval = fail(f->fts_path, sys_errlist[f->fts_errno]);
+ break;
+ case FTS_D:
+ if (!opt_recurse && f->fts_level > FTS_ROOTLEVEL &&
+ fts_set(ftsp, f, FTS_SKIP))
+ err(2, "fts_set");
+ break;
+ case FTS_F:
+ if ((type = distfile(f->fts_name)) != 0 &&
+ (!opt_type || type == opt_type))
+ rval |= ckdist(f->fts_path, type);
+ break;
+ default: ;
+ }
+ if (errno)
+ err(2, "fts_read");
+ if (fts_close(ftsp))
+ err(2, "fts_close");
+ }
+ } while (*++argv);
+ return rval;
+}
+
+static int
+ckdist(const char *path, int type)
+{
+ FILE *fp;
+ int rval, c;
+
+ if (isstdin(path)) {
+ path = "(stdin)";
+ fp = stdin;
+ } else if ((fp = fopen(path, "r")) == NULL)
+ return fail(path, NULL);
+ if (!type) {
+ if (fp != stdin)
+ type = distfile(path);
+ if (!type)
+ if ((c = fgetc(fp)) != EOF) {
+ type = c == 'M' ? DISTMD5 : c == 'P' ? DISTINF : 0;
+ (void)ungetc(c, fp);
+ }
+ }
+ switch (type) {
+ case DISTMD5:
+ rval = chkmd5(fp, path);
+ break;
+ case DISTINF:
+ rval = chkinf(fp, path);
+ break;
+ default:
+ rval = report(path, NULL, E_UNKNOWN);
+ }
+ if (ferror(fp))
+ warn(path);
+ if (fp != stdin && fclose(fp))
+ err(2, path);
+ return rval;
+}
+
+static int
+chkmd5(FILE * fp, const char *path)
+{
+ char buf[298]; /* "MD5 (NAMESIZE = MDSUMLEN" */
+ char name[NAMESIZE + 1];
+ char sum[MDSUMLEN + 1], chk[MDSUMLEN + 1];
+ const char *dname;
+ char *s;
+ int rval, error, c, fd;
+ char ch;
+
+ rval = 0;
+ while (fgets(buf, sizeof(buf), fp)) {
+ dname = NULL;
+ error = 0;
+ if (((c = sscanf(buf, "MD5 (%256s = %32s%c", name, sum,
+ &ch)) != 3 && (!feof(fp) || c != 2)) ||
+ (c == 3 && ch != '\n') ||
+ (s = strrchr(name, ')')) == NULL ||
+ strlen(sum) != MDSUMLEN)
+ error = E_BADMD5;
+ else {
+ *s = 0;
+ if ((dname = distname(path, name, NULL)) == NULL)
+ error = E_NAME;
+ else if (opt_exist) {
+ if ((fd = open(dname, O_RDONLY)) == -1)
+ error = E_ERRNO;
+ else if (close(fd))
+ err(2, dname);
+ } else if (!MD5File((char *)dname, chk))
+ error = E_ERRNO;
+ else if (strcmp(chk, sum))
+ error = E_CHKSUM;
+ }
+ if (opt_ignore && error == E_ERRNO && errno == ENOENT)
+ continue;
+ if (error || opt_all)
+ rval |= report(path, dname, error);
+ if (isfatal(error))
+ break;
+ }
+ return rval;
+}
+
+static int
+chkinf(FILE * fp, const char *path)
+{
+ char buf[30]; /* "cksum.2 = 10 6" */
+ char ext[3];
+ struct stat sb;
+ const char *dname;
+ u_long sum, len, chk;
+ int rval, error, c, pieces, cnt, fd;
+ char ch;
+
+ rval = 0;
+ for (cnt = -1; fgets(buf, sizeof(buf), fp); cnt++) {
+ fd = -1;
+ dname = NULL;
+ error = 0;
+ if (cnt == -1) {
+ if ((c = sscanf(buf, "Pieces = %d%c", &pieces, &ch)) != 2 ||
+ ch != '\n' || pieces < 1)
+ error = E_BADINF;
+ } else if (((c = sscanf(buf, "cksum.%2s = %lu %lu%c", ext, &sum,
+ &len, &ch)) != 4 &&
+ (!feof(fp) || c != 3)) || (c == 4 && ch != '\n') ||
+ ext[0] != 'a' + cnt / 26 || ext[1] != 'a' + cnt % 26)
+ error = E_BADINF;
+ else if ((dname = distname(fp == stdin ? NULL : path, NULL,
+ ext)) == NULL)
+ error = E_NAME;
+ else if ((fd = open(dname, O_RDONLY)) == -1)
+ error = E_ERRNO;
+ else if (fstat(fd, &sb))
+ error = E_ERRNO;
+ else if (sb.st_size != (off_t)len)
+ error = E_LENGTH;
+ else if (!opt_exist) {
+ if (crc(fd, &chk, &len))
+ error = E_ERRNO;
+ else if (chk != sum)
+ error = E_CHKSUM;
+ }
+ if (fd != -1 && close(fd))
+ err(2, dname);
+ if (opt_ignore && error == E_ERRNO && errno == ENOENT)
+ continue;
+ if (error || (opt_all && cnt >= 0))
+ rval |= report(path, dname, error);
+ if (isfatal(error))
+ break;
+ }
+ return rval;
+}
+
+static int
+report(const char *path, const char *name, int error)
+{
+ if (name)
+ name = stripath(name);
+ switch (error) {
+ case E_UNKNOWN:
+ printf("%s: Unknown format\n", path);
+ break;
+ case E_BADMD5:
+ printf("%s: Invalid MD5 format\n", path);
+ break;
+ case E_BADINF:
+ printf("%s: Invalid .inf format\n", path);
+ break;
+ case E_NAME:
+ printf("%s: Can't derive component name\n", path);
+ break;
+ case E_LENGTH:
+ printf("%s: %s: Size mismatch\n", path, name);
+ break;
+ case E_CHKSUM:
+ printf("%s: %s: Checksum mismatch\n", path, name);
+ break;
+ case E_ERRNO:
+ printf("%s: %s: %s\n", path, name, sys_errlist[errno]);
+ break;
+ default:
+ printf("%s: %s: OK\n", path, name);
+ }
+ return error != 0;
+}
+
+static const char *
+distname(const char *path, const char *name, const char *ext)
+{
+ static char buf[NAMESIZE];
+ size_t plen, nlen;
+ char *s;
+
+ if (opt_name)
+ name = opt_name;
+ else if (!name) {
+ if (!path)
+ return NULL;
+ name = stripath(path);
+ }
+ nlen = strlen(name);
+ if (ext && nlen > 4 && name[nlen - 4] == '.' &&
+ disttype(name + nlen - 3) == DISTINF)
+ nlen -= 4;
+ if (opt_dir) {
+ path = opt_dir;
+ plen = strlen(path);
+ } else
+ plen = path && (s = strrchr(path, '/')) != NULL ?
+ (size_t)(s - path) : 0;
+ if (plen + (plen > 0) + nlen + (ext ? 3 : 0) >= sizeof(buf))
+ return NULL;
+ s = buf;
+ if (plen) {
+ memcpy(s, path, plen);
+ s += plen;
+ *s++ = '/';
+ }
+ memcpy(s, name, nlen);
+ s += nlen;
+ if (ext) {
+ *s++ = '.';
+ memcpy(s, ext, 2);
+ s += 2;
+ }
+ *s = 0;
+ return buf;
+}
+
+static char *
+stripath(const char *path)
+{
+ const char *s;
+
+ return (char *)((s = strrchr(path, '/')) != NULL && s[1] ?
+ s + 1 : path);
+}
+
+static int
+distfile(const char *path)
+{
+ const char *s;
+ int type;
+
+ if ((type = disttype(path)) == DISTMD5 ||
+ ((s = strrchr(path, '.')) != NULL && s > path &&
+ (type = disttype(s + 1)) != 0))
+ return type;
+ return 0;
+}
+
+static int
+disttype(const char *name)
+{
+ static const char dname[DISTTYPES][4] = {"md5", "inf"};
+ int i;
+
+ for (i = 0; i < DISTTYPES; i++)
+ if (!strcmp(dname[i], name))
+ return 1 + i;
+ return 0;
+}
+
+static int
+fail(const char *path, const char *msg)
+{
+ if (opt_silent)
+ return 0;
+ warnx("%s: %s", path, msg ? msg : sys_errlist[errno]);
+ return 2;
+}
+
+static void
+usage(void)
+{
+ fprintf(stderr,
+ "usage: ckdist [-airsx] [-d dir] [-n name] [-t type] file ...\n");
+ exit(2);
+}
diff --git a/usr.sbin/config/Makefile b/usr.sbin/config/Makefile
new file mode 100644
index 0000000..5a8b5ad
--- /dev/null
+++ b/usr.sbin/config/Makefile
@@ -0,0 +1,17 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= config
+CFLAGS+=-I. -I${.CURDIR}
+SRCS= config.c main.c lang.c mkioconf.c mkmakefile.c mkglue.c mkheaders.c \
+ mkoptions.c mkswapconf.c
+MAN8= config.8
+DPADD= ${LIBL}
+LDADD= -ll
+CLEANFILES+= config.c lang.c lex.yy.c y.tab.[ch]
+.if defined(CONFIG_NO_CLOBBER_EVER)
+CFLAGS+=-DNO_CLOBBER_EVER
+.endif
+
+.include <bsd.prog.mk>
+
+$(OBJS): configvers.h
diff --git a/usr.sbin/config/SMM.doc/0.t b/usr.sbin/config/SMM.doc/0.t
new file mode 100644
index 0000000..ae5bf77
--- /dev/null
+++ b/usr.sbin/config/SMM.doc/0.t
@@ -0,0 +1,88 @@
+.\" Copyright (c) 1983, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)0.t 8.1 (Berkeley) 7/5/93
+.\"
+.bd S B 3
+.de UX
+.ie \\n(GA>0 \\$2UNIX\\$1
+.el \{\
+.if n \\$2UNIX\\$1*
+.if t \\$2UNIX\\$1\\f1\(dg\\fP
+.FS
+.if n *UNIX
+.if t \(dgUNIX
+.ie \\$3=1 is a Footnote of Bell Laboratories.
+.el is a Trademark of Bell Laboratories.
+.FE
+.nr GA 1\}
+..
+.de BR
+\fB\\$1\fP\\$2
+..
+.TL
+Building 4.4BSD Kernels with Config
+.AU
+Samuel J. Leffler and Michael J. Karels
+.AI
+Computer Systems Research Group
+Department of Electrical Engineering and Computer Science
+University of California, Berkeley
+Berkeley, California 94720
+.de IR
+\fI\\$1\fP\\$2
+..
+.de DT
+.TA 8 16 24 32 40 48 56 64 72 80
+..
+.AB
+.PP
+This document describes the use of
+\fIconfig\fP\|(8) to configure and create bootable
+4.4BSD system images.
+It discusses the structure of system
+configuration files and how to configure
+systems with non-standard hardware configurations.
+Sections describing the preferred way to
+add new code to the system and how the system's autoconfiguration
+process operates are included. An appendix
+contains a summary of the rules used by the system
+in calculating the size of system data structures,
+and also indicates some of the standard system size
+limitations (and how to change them).
+Other configuration options are also listed.
+.sp
+.LP
+Revised July 5, 1993
+.AE
+.LP
+.OH 'Building 4.4BSD Kernels with Config''SMM:2-%'
+.EH 'SMM:2-%''Building 4.4BSD Kernels with Config'
diff --git a/usr.sbin/config/SMM.doc/1.t b/usr.sbin/config/SMM.doc/1.t
new file mode 100644
index 0000000..453041b
--- /dev/null
+++ b/usr.sbin/config/SMM.doc/1.t
@@ -0,0 +1,61 @@
+.\" Copyright (c) 1983, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)1.t 8.1 (Berkeley) 6/8/93
+.\"
+.\".ds RH Introduction
+.ne 2i
+.sp 3
+.NH
+INTRODUCTION
+.PP
+.I Config
+is a tool used in building 4.4BSD system images (the UNIX kernel).
+It takes a file describing a system's tunable parameters and
+hardware support, and generates a collection
+of files which are then used to build a copy of UNIX appropriate
+to that configuration.
+.I Config
+simplifies system maintenance by isolating system dependencies
+in a single, easy to understand, file.
+.PP
+This document describes the content and
+format of system configuration
+files and the rules which must be followed when creating
+these files. Example configuration files are constructed
+and discussed.
+.PP
+Later sections suggest guidelines to be used in modifying
+system source and explain some of the inner workings of the
+autoconfiguration process. Appendix D summarizes the rules
+used in calculating the most important system data structures
+and indicates some inherent system data structure size
+limitations (and how to go about modifying them).
diff --git a/usr.sbin/config/SMM.doc/2.t b/usr.sbin/config/SMM.doc/2.t
new file mode 100644
index 0000000..34e6b63
--- /dev/null
+++ b/usr.sbin/config/SMM.doc/2.t
@@ -0,0 +1,188 @@
+.\" Copyright (c) 1983, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)2.t 8.1 (Berkeley) 6/8/93
+.\"
+.\".ds RH "Configuration File Contents
+.ne 2i
+.NH
+CONFIGURATION FILE CONTENTS
+.PP
+A system configuration must include at least the following
+pieces of information:
+.IP \(bu 3
+machine type
+.IP \(bu 3
+cpu type
+.IP \(bu 3
+system identification
+.IP \(bu 3
+timezone
+.IP \(bu 3
+maximum number of users
+.IP \(bu 3
+location of the root file system
+.IP \(bu 3
+available hardware
+.PP
+.I Config
+allows multiple system images to be generated from a single
+configuration description. Each system image is configured
+for identical hardware, but may have different locations for the root
+file system and, possibly, other system devices.
+.NH 2
+Machine type
+.PP
+The
+.I "machine type"
+indicates if the system is going to operate on a DEC VAX-11\(dg computer,
+.FS
+\(dg DEC, VAX, UNIBUS, MASSBUS and MicroVAX are trademarks of Digital
+Equipment Corporation.
+.FE
+or some other machine on which 4.4BSD operates. The machine type
+is used to locate certain data files which are machine specific, and
+also to select rules used in constructing the resultant
+configuration files.
+.NH 2
+Cpu type
+.PP
+The
+.I "cpu type"
+indicates which, of possibly many, cpu's the system is to operate on.
+For example, if the system is being configured for a VAX-11, it could
+be running on a VAX 8600, VAX-11/780, VAX-11/750, VAX-11/730 or MicroVAX II.
+(Other VAX cpu types, including the 8650, 785 and 725, are configured using
+the cpu designation for compatible machines introduced earlier.)
+Specifying
+more than one cpu type implies that the system should be configured to run
+on any of the cpu's specified. For some types of machines this is not
+possible and
+.I config
+will print a diagnostic indicating such.
+.NH 2
+System identification
+.PP
+The
+.I "system identification"
+is a moniker attached to the system, and often the machine on which the
+system is to run. For example, at Berkeley we have machines named Ernie
+(Co-VAX), Kim (No-VAX), and so on. The system identifier selected is used to
+create a global C ``#define'' which may be used to isolate system dependent
+pieces of code in the kernel. For example, Ernie's Varian driver used
+to be special cased because its interrupt vectors were wired together. The
+code in the driver which understood how to handle this non-standard hardware
+configuration was conditionally compiled in only if the system
+was for Ernie.
+.PP
+The system identifier ``GENERIC'' is given to a system which
+will run on any cpu of a particular machine type; it should not
+otherwise be used for a system identifier.
+.NH 2
+Timezone
+.PP
+The timezone in which the system is to run is used to define the
+information returned by the \fIgettimeofday\fP\|(2)
+system call. This value is specified as the number of hours east
+or west of GMT. Negative numbers indicate a value east of GMT.
+The timezone specification may also indicate the
+type of daylight savings time rules to be applied.
+.NH 2
+Maximum number of users
+.PP
+The system allocates many system data structures at boot time
+based on the maximum number of users the system will support.
+This number is normally between 8 and 40, depending
+on the hardware and expected job mix. The rules
+used to calculate system data structures are discussed in
+Appendix D.
+.NH 2
+Root file system location
+.PP
+When the system boots it must know the location of
+the root of the file system
+tree. This location and the part(s) of the disk(s) to be used
+for paging and swapping must be specified in order to create
+a complete configuration description.
+.I Config
+uses many rules to calculate default locations for these items;
+these are described in Appendix B.
+.PP
+When a generic system is configured, the root file system is left
+undefined until the system is booted. In this case, the root file
+system need not be specified, only that the system is a generic system.
+.NH 2
+Hardware devices
+.PP
+When the system boots it goes through an
+.I autoconfiguration
+phase. During this period, the system searches for all
+those hardware devices
+which the system builder has indicated might be present. This probing
+sequence requires certain pieces of information such as register
+addresses, bus interconnects, etc. A system's hardware may be configured
+in a very flexible manner or be specified without any flexibility
+whatsoever. Most people do not configure hardware devices into the
+system unless they are currently present on the machine, expect
+them to be present in the near future, or are simply guarding
+against a hardware
+failure somewhere else at the site (it is often wise to configure in
+extra disks in case an emergency requires moving one off a machine which
+has hardware problems).
+.PP
+The specification of hardware devices usually occupies the majority of
+the configuration file. As such, a large portion of this document will
+be spent understanding it. Section 6.3 contains a description of
+the autoconfiguration process, as it applies to those planning to
+write, or modify existing, device drivers.
+.NH 2
+Pseudo devices
+.PP
+Several system facilities are configured in a manner like that used
+for hardware devices although they are not associated with specific hardware.
+These system options are configured as
+.IR pseudo-devices .
+Some pseudo devices allow an optional parameter that sets the limit
+on the number of instances of the device that are active simultaneously.
+.NH 2
+System options
+.PP
+Other than the mandatory pieces of information described above, it
+is also possible to include various optional system facilities
+or to modify system behavior and/or limits.
+For example, 4.4BSD can be configured to support binary compatibility for
+programs built under 4.3BSD. Also, optional support is provided
+for disk quotas and tracing the performance of the virtual memory
+subsystem. Any optional facilities to be configured into
+the system are specified in the configuration file. The resultant
+files generated by
+.I config
+will automatically include the necessary pieces of the system.
diff --git a/usr.sbin/config/SMM.doc/3.t b/usr.sbin/config/SMM.doc/3.t
new file mode 100644
index 0000000..e0b6234
--- /dev/null
+++ b/usr.sbin/config/SMM.doc/3.t
@@ -0,0 +1,299 @@
+.\" Copyright (c) 1983, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)3.t 8.1 (Berkeley) 6/8/93
+.\"
+.\".ds RH "System Building Process
+.ne 2i
+.NH
+SYSTEM BUILDING PROCESS
+.PP
+In this section we consider the steps necessary to build a bootable system
+image. We assume the system source is located in the ``/sys'' directory
+and that, initially, the system is being configured from source code.
+.PP
+Under normal circumstances there are 5 steps in building a system.
+.IP 1) 3
+Create a configuration file for the system.
+.IP 2) 3
+Make a directory for the system to be constructed in.
+.IP 3) 3
+Run
+.I config
+on the configuration file to generate the files required
+to compile and load the system image.
+.IP 4)
+Construct the source code interdependency rules for the
+configured system with
+.I make depend
+using
+.IR make (1).
+.IP 5)
+Compile and load the system with
+.IR make .
+.PP
+Steps 1 and 2 are usually done only once. When a system configuration
+changes it usually suffices to just run
+.I config
+on the modified configuration file, rebuild the source code dependencies,
+and remake the system. Sometimes,
+however, configuration dependencies may not be noticed in which case
+it is necessary to clean out the relocatable object files saved
+in the system's directory; this will be discussed later.
+.NH 2
+Creating a configuration file
+.PP
+Configuration files normally reside in the directory ``/sys/conf''.
+A configuration file is most easily constructed by copying an
+existing configuration file and modifying it. The 4.4BSD distribution
+contains a number of configuration files for machines at Berkeley;
+one may be suitable or, in worst case, a copy
+of the generic configuration file may be edited.
+.PP
+The configuration file must have the same name as the directory in
+which the configured system is to be built.
+Further,
+.I config
+assumes this directory is located in the parent directory of
+the directory in which it
+is run. For example, the generic
+system has a configuration file ``/sys/conf/GENERIC'' and an accompanying
+directory named ``/sys/GENERIC''.
+Although it is not required that the system sources and configuration
+files reside in ``/sys,'' the configuration and compilation procedure
+depends on the relative locations of directories within that hierarchy,
+as most of the system code and the files created by
+.I config
+use pathnames of the form ``../''.
+If the system files are not located in ``/sys,''
+it is desirable to make a symbolic link there for use in installation
+of other parts of the system that share files with the kernel.
+.PP
+When building the configuration file, be sure to include the items
+described in section 2. In particular, the machine type,
+cpu type, timezone, system identifier, maximum users, and root device
+must be specified. The specification of the hardware present may take
+a bit of work; particularly if your hardware is configured at non-standard
+places (e.g. device registers located at funny places or devices not
+supported by the system). Section 4 of this document
+gives a detailed description of the configuration file syntax,
+section 5 explains some sample configuration files, and
+section 6 discusses how to add new devices to
+the system. If the devices to be configured are not already
+described in one of the existing configuration files you should check
+the manual pages in section 4 of the UNIX Programmers Manual. For each
+supported device, the manual page synopsis entry gives a
+sample configuration line.
+.PP
+Once the configuration file is complete, run it through
+.I config
+and look for any errors. Never try and use a system which
+.I config
+has complained about; the results are unpredictable.
+For the most part,
+.IR config 's
+error diagnostics are self explanatory. It may be the case that
+the line numbers given with the error messages are off by one.
+.PP
+A successful run of
+.I config
+on your configuration file will generate a number of files in
+the configuration directory. These files are:
+.IP \(bu 3
+A file to be used by \fImake\fP\|(1)
+in compiling and loading the system,
+.IR Makefile .
+.IP \(bu 3
+One file for each possible system image for this machine,
+.IR swapxxx.c ,
+where
+.I xxx
+is the name of the system image,
+which describes where swapping, the root file system, and other
+miscellaneous system devices are located.
+.IP \(bu 3
+A collection of header files, one per possible device the
+system supports, which define the hardware configured.
+.IP \(bu 3
+A file containing the I/O configuration tables used by the system
+during its
+.I autoconfiguration
+phase,
+.IR ioconf.c .
+.IP \(bu 3
+An assembly language file of interrupt vectors which
+connect interrupts from the machine's external buses to the main
+system path for handling interrupts,
+and a file that contains counters and names for the interrupt vectors.
+.PP
+Unless you have reason to doubt
+.IR config ,
+or are curious how the system's autoconfiguration scheme
+works, you should never have to look at any of these files.
+.NH 2
+Constructing source code dependencies
+.PP
+When
+.I config
+is done generating the files needed to compile and link your system it
+will terminate with a message of the form ``Don't forget to run make depend''.
+This is a reminder that you should change over to the configuration
+directory for the system just configured and type ``make depend''
+to build the rules used by
+.I make
+to recognize interdependencies in the system source code.
+This will insure that any changes to a piece of the system
+source code will result in the proper modules being recompiled
+the next time
+.I make
+is run.
+.PP
+This step is particularly important if your site makes changes
+to the system include files. The rules generated specify which source code
+files are dependent on which include files. Without these rules,
+.I make
+will not recognize when it must rebuild modules
+due to the modification of a system header file.
+The dependency rules are generated by a pass of the C preprocessor
+and reflect the global system options.
+This step must be repeated when the configuration file is changed
+and
+.I config
+is used to regenerate the system makefile.
+.NH 2
+Building the system
+.PP
+The makefile constructed by
+.I config
+should allow a new system to be rebuilt by simply typing ``make image-name''.
+For example, if you have named your bootable system image ``kernel'',
+then ``make kernel''
+will generate a bootable image named ``kernel''. Alternate system image names
+are used when the root file system location and/or swapping configuration
+is done in more than one way. The makefile which
+.I config
+creates has entry points for each system image defined in
+the configuration file.
+Thus, if you have configured ``kernel'' to be a system with the root file
+system on an ``hp'' device and ``hkkernel'' to be a system with the root
+file system on an ``hk'' device, then ``make kernel hkkernel'' will generate
+binary images for each.
+As the system will generally use the disk from which it is loaded
+as the root filesystem, separate system images are only required
+to support different swap configurations.
+.PP
+Note that the name of a bootable image is different from the system
+identifier. All bootable images are configured for the same system;
+only the information about the root file system and paging devices differ.
+(This is described in more detail in section 4.)
+.PP
+The last step in the system building process is to rearrange certain commonly
+used symbols in the symbol table of the system image; the makefile
+generated by
+.I config
+does this automatically for you.
+This is advantageous for programs such as
+\fInetstat\fP\|(1) and \fIvmstat\fP\|(1),
+which run much faster when the symbols they need are located at
+the front of the symbol table.
+Remember also that many programs expect
+the currently executing system to be named ``/kernel''. If you install
+a new system and name it something other than ``/kernel'', many programs
+are likely to give strange results.
+.NH 2
+Sharing object modules
+.PP
+If you have many systems which are all built on a single machine
+there are at least two approaches to saving time in building system
+images. The best way is to have a single system image which is run on
+all machines. This is attractive since it minimizes disk space used
+and time required to rebuild systems after making changes. However,
+it is often the case that one or more systems will require a separately
+configured system image. This may be due to limited memory (building
+a system with many unused device drivers can be expensive), or to
+configuration requirements (one machine may be a development machine
+where disk quotas are not needed, while another is a production machine
+where they are), etc. In these cases it is possible
+for common systems to share relocatable object modules which are not
+configuration dependent; most of the modules in the directory ``/sys/sys''
+are of this sort.
+.PP
+To share object modules, a generic system should be built. Then, for
+each system configure the system as before, but before recompiling and
+linking the system, type ``make links'' in the system compilation directory.
+This will cause the system
+to be searched for source modules which are safe to share between systems
+and generate symbolic links in the current directory to the appropriate
+object modules in the directory ``../GENERIC''. A shell script,
+``makelinks'' is generated with this request and may be checked for
+correctness. The file ``/sys/conf/defines'' contains a list of symbols
+which we believe are safe to ignore when checking the source code
+for modules which may be shared. Note that this list includes the definitions
+used to conditionally compile in the virtual memory tracing facilities, and
+the trace point support used only rarely (even at Berkeley).
+It may be necessary
+to modify this file to reflect local needs. Note further that
+interdependencies which are not directly visible
+in the source code are not caught. This means that if you place
+per-system dependencies in an include file, they will not be recognized
+and the shared code may be selected in an unexpected fashion.
+.NH 2
+Building profiled systems
+.PP
+It is simple to configure a system which will automatically
+collect profiling information as it operates. The profiling data
+may be collected with \fIkgmon\fP\|(8) and processed with
+\fIgprof\fP\|(1)
+to obtain information regarding the system's operation. Profiled
+systems maintain histograms of the program counter as well as the
+number of invocations of each routine. The \fIgprof\fP
+command will also generate a dynamic call graph of the executing
+system and propagate time spent in each routine along the arcs
+of the call graph (consult the \fIgprof\fP documentation for elaboration).
+The program counter sampling can be driven by the system clock, or
+if you have an alternate real time clock, this can be used. The
+latter is highly recommended, as use of the system clock will result
+in statistical anomalies, and time spent in the clock routine will
+not be accurately attributed.
+.PP
+To configure a profiled system, the
+.B \-p
+option should be supplied to \fIconfig\fP.
+A profiled system is about 5-10% larger in its text space due to
+the calls to count the subroutine invocations. When the system
+executes, the profiling data is stored in a buffer which is 1.2
+times the size of the text space. The overhead for running a
+profiled system varies; under normal load we see anywhere from 5-25%
+of the system time spent in the profiling code.
+.PP
+Note that systems configured for profiling should not be shared as
+described above unless all the other shared systems are also to be
+profiled.
diff --git a/usr.sbin/config/SMM.doc/4.t b/usr.sbin/config/SMM.doc/4.t
new file mode 100644
index 0000000..7498185
--- /dev/null
+++ b/usr.sbin/config/SMM.doc/4.t
@@ -0,0 +1,442 @@
+.\" Copyright (c) 1983, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)4.t 8.1 (Berkeley) 6/8/93
+.\"
+.\".ds RH "Configuration File Syntax
+.ne 2i
+.NH
+CONFIGURATION FILE SYNTAX
+.PP
+In this section we consider the specific rules used in writing
+a configuration file. A complete grammar for the input language
+can be found in Appendix A and may be of use if you should have
+problems with syntax errors.
+.PP
+A configuration file is broken up into three logical pieces:
+.IP \(bu 3
+configuration parameters global to all system images
+specified in the configuration file,
+.IP \(bu 3
+parameters specific to each
+system image to be generated, and
+.IP \(bu 3
+device specifications.
+.NH 2
+Global configuration parameters
+.PP
+The global configuration parameters are the type of machine,
+cpu types, options, timezone, system identifier, and maximum users.
+Each is specified with a separate line in the configuration file.
+.IP "\fBmachine\fP \fItype\fP"
+.br
+The system is to run on the machine type specified. No more than
+one machine type can appear in the configuration file. Legal values
+are
+.B vax
+and
+\fBsun\fP.
+.IP "\fBcpu\fP ``\fItype\fP''"
+.br
+This system is to run on the cpu type specified.
+More than one cpu type specification
+can appear in a configuration file.
+Legal types for a
+.B vax
+machine are
+\fBVAX8600\fP, \fBVAX780\fP, \fBVAX750\fP,
+\fBVAX730\fP
+and
+\fBVAX630\fP (MicroVAX II).
+The 8650 is listed as an 8600, the 785 as a 780, and a 725 as a 730.
+.IP "\fBoptions\fP \fIoptionlist\fP"
+.br
+Compile the listed optional code into the system.
+Options in this list are separated by commas.
+Possible options are listed at the top of the generic makefile.
+A line of the form ``options FUNNY,HAHA'' generates global ``#define''s
+\-DFUNNY \-DHAHA in the resultant makefile.
+An option may be given a value by following its name with ``\fB=\fP'',
+then the value enclosed in (double) quotes.
+The following are major options are currently in use:
+COMPAT (include code for compatibility with 4.1BSD binaries),
+INET (Internet communication protocols),
+NS (Xerox NS communication protocols),
+and
+QUOTA (enable disk quotas).
+Other kernel options controlling system sizes and limits
+are listed in Appendix D;
+options for the network are found in Appendix E.
+There are additional options which are associated with certain
+peripheral devices; those are listed in the Synopsis section
+of the manual page for the device.
+.IP "\fBmakeoptions\fP \fIoptionlist\fP"
+.br
+Options that are used within the system makefile
+and evaluated by
+.I make
+are listed as
+.IR makeoptions .
+Options are listed with their values with the form
+``makeoptions name=value,name2=value2.''
+The values must be enclosed in double quotes if they include numerals
+or begin with a dash.
+.IP "\fBtimezone\fP \fInumber\fP [ \fBdst\fP [ \fInumber\fP ] ]"
+.br
+Specifies the timezone used by the system. This is measured in the
+number of hours your timezone is west of GMT.
+EST is 5 hours west of GMT, PST is 8. Negative numbers
+indicate hours east of GMT. If you specify
+\fBdst\fP, the system will operate under daylight savings time.
+An optional integer or floating point number may be included
+to specify a particular daylight saving time correction algorithm;
+the default value is 1, indicating the United States.
+Other values are: 2 (Australian style), 3 (Western European),
+4 (Middle European), and 5 (Eastern European). See
+\fIgettimeofday\fP\|(2) and \fIctime\fP\|(3) for more information.
+.IP "\fBident\fP \fIname\fP"
+.br
+This system is to be known as
+.IR name .
+This is usually a cute name like ERNIE (short for Ernie Co-Vax) or
+VAXWELL (for Vaxwell Smart).
+This value is defined for use in conditional compilation,
+and is also used to locate an optional list of source files specific
+to this system.
+.IP "\fBmaxusers\fP \fInumber\fP"
+.br
+The maximum expected number of simultaneously active user on this system is
+.IR number .
+This number is used to size several system data structures.
+.NH 2
+System image parameters
+.PP
+Multiple bootable images may be specified in a single configuration
+file. The systems will have the same global configuration parameters
+and devices, but the location of the root file system and other
+system specific devices may be different. A system image is specified
+with a ``config'' line:
+.IP
+\fBconfig\fP\ \fIsysname\fP\ \fIconfig-clauses\fP
+.LP
+The
+.I sysname
+field is the name given to the loaded system image; almost everyone
+names their standard system image ``kernel''. The configuration clauses
+are one or more specifications indicating where the root file system
+is located and the number and location of paging devices.
+The device used by the system to process argument lists during
+.IR execve (2)
+calls may also be specified, though in practice this is almost
+always selected by
+.I config
+using one of its rules for selecting default locations for
+system devices.
+.PP
+A configuration clause is one of the following
+.IP
+.nf
+\fBroot\fP [ \fBon\fP ] \fIroot-device\fP
+\fBswap\fP [ \fBon\fP ] \fIswap-device\fP [ \fBand\fP \fIswap-device\fP ] ...
+\fBdumps\fP [ \fBon\fP ] \fIdump-device\fP
+\fBargs\fP [ \fBon\fP ] \fIarg-device\fP
+.LP
+(the ``on'' is optional.) Multiple configuration clauses
+are separated by white space;
+.I config
+allows specifications to be continued across multiple lines
+by beginning the continuation line with a tab character.
+The ``root'' clause specifies where the root file system
+is located, the ``swap'' clause indicates swapping and paging
+area(s), the ``dumps'' clause can be used to force system dumps
+to be taken on a particular device, and the ``args'' clause
+can be used to specify that argument list processing for
+.I execve
+should be done on a particular device.
+.PP
+The device names supplied in the clauses may be fully specified
+as a device, unit, and file system partition; or underspecified
+in which case
+.I config
+will use builtin rules to select default unit numbers and file
+system partitions. The defaulting rules are a bit complicated
+as they are dependent on the overall system configuration.
+For example, the swap area need not be specified at all if
+the root device is specified; in this case the swap area is
+placed in the ``b'' partition of the same disk where the root
+file system is located. Appendix B contains a complete list
+of the defaulting rules used in selecting system configuration
+devices.
+.PP
+The device names are translated to the
+appropriate major and minor device
+numbers on a per-machine basis. A file,
+``/sys/conf/devices.machine'' (where ``machine''
+is the machine type specified in the configuration file),
+is used to map a device name to its major block device number.
+The minor device number is calculated using the standard
+disk partitioning rules: on unit 0, partition ``a'' is minor device
+0, partition ``b'' is minor device 1, and so on; for units
+other than 0, add 8 times the unit number to get the minor
+device.
+.PP
+If the default mapping of device name to major/minor device
+number is incorrect for your configuration, it can be replaced
+by an explicit specification of the major/minor device.
+This is done by substituting
+.IP
+\fBmajor\fP \fIx\fP \fBminor\fP \fIy\fP
+.LP
+where the device name would normally be found. For example,
+.IP
+.nf
+\fBconfig\fP kernel \fBroot\fP \fBon\fP \fBmajor\fP 99 \fBminor\fP 1
+.fi
+.PP
+Normally, the areas configured for swap space are sized by the system
+at boot time. If a non-standard size is to be used for one
+or more swap areas (less than the full partition),
+this can also be specified. To do this, the
+device name specified for a swap area should have a ``size''
+specification appended. For example,
+.IP
+.nf
+\fBconfig\fP kernel \fBroot\fP \fBon\fP hp0 \fBswap\fP \fBon\fP hp0b \fBsize\fP 1200
+.fi
+.LP
+would force swapping to be done in partition ``b'' of ``hp0'' and
+the swap partition size would be set to 1200 sectors. A swap area
+sized larger than the associated disk partition is trimmed to the
+partition size.
+.PP
+To create a generic configuration, only the clause ``swap generic''
+should be specified; any extra clauses will cause an error.
+.NH 2
+Device specifications
+.PP
+Each device attached to a machine must be specified
+to
+.I config
+so that the system generated will know to probe for it during
+the autoconfiguration process carried out at boot time. Hardware
+specified in the configuration need not actually be present on
+the machine where the generated system is to be run. Only the
+hardware actually found at boot time will be used by the system.
+.PP
+The specification of hardware devices in the configuration file
+parallels the interconnection hierarchy of the machine to be
+configured. On the VAX, this means that a configuration file must
+indicate what MASSBUS and UNIBUS adapters are present, and to
+which \fInexi\fP they might be connected.*
+.FS
+* While VAX-11/750's and VAX-11/730 do not actually have
+nexi, the system treats them as having
+.I "simulated nexi"
+to simplify device configuration.
+.FE
+Similarly, devices
+and controllers must be indicated as possibly being connected
+to one or more adapters. A device description may provide a
+complete definition of the possible configuration parameters
+or it may leave certain parameters undefined and make the system
+probe for all the possible values. The latter allows a single
+device configuration list to match many possible physical
+configurations. For example, a disk may be indicated as present
+at UNIBUS adapter 0, or at any UNIBUS adapter which the system
+locates at boot time. The latter scheme, termed
+.IR wildcarding ,
+allows more flexibility in the physical configuration of a system;
+if a disk must be moved around for some reason, the system will
+still locate it at the alternate location.
+.PP
+A device specification takes one of the following forms:
+.IP
+.nf
+\fBmaster\fP \fIdevice-name\fP \fIdevice-info\fP
+\fBcontroller\fP \fIdevice-name\fP \fIdevice-info\fP [ \fIinterrupt-spec\fP ]
+\fBdevice\fP \fIdevice-name\fP \fIdevice-info\fP \fIinterrupt-spec\fP
+\fBdisk\fP \fIdevice-name\fP \fIdevice-info\fP
+\fBtape\fP \fIdevice-name\fP \fIdevice-info\fP
+.fi
+.LP
+A ``master'' is a MASSBUS tape controller; a ``controller'' is a
+disk controller, a UNIBUS tape controller, a MASSBUS adapter, or
+a UNIBUS adapter. A ``device'' is an autonomous device which
+connects directly to a UNIBUS adapter (as opposed to something
+like a disk which connects through a disk controller). ``Disk''
+and ``tape'' identify disk drives and tape drives connected to
+a ``controller'' or ``master.''
+.PP
+The
+.I device-name
+is one of the standard device names, as
+indicated in section 4 of the UNIX Programmers Manual,
+concatenated with the
+.I logical
+unit number to be assigned the device (the
+.I logical
+unit number may be different than the
+.I physical
+unit number indicated on the front of something
+like a disk; the
+.I logical
+unit number is used to refer to the UNIX device, not
+the physical unit number). For example, ``hp0'' is logical
+unit 0 of a MASSBUS storage device, even though it might
+be physical unit 3 on MASSBUS adapter 1.
+.PP
+The
+.I device-info
+clause specifies how the hardware is
+connected in the interconnection hierarchy. On the VAX,
+UNIBUS and MASSBUS adapters are connected to the internal
+system bus through
+a \fInexus\fP.
+Thus, one of the following
+specifications would be used:
+.IP
+.ta 1.5i 2.5i 4.0i
+.nf
+\fBcontroller\fP mba0 \fBat\fP \fBnexus\fP \fIx\fP
+\fBcontroller\fP uba0 \fBat\fP \fBnexus\fP \fIx\fP
+.fi
+.LP
+To tie a controller to a specific nexus, ``x'' would be supplied
+as the number of that nexus; otherwise ``x'' may be specified as
+``?'', in which
+case the system will probe all nexi present looking
+for the specified controller.
+.PP
+The remaining interconnections on the VAX are:
+.IP \(bu 3
+a controller
+may be connected to another controller (e.g. a disk controller attached
+to a UNIBUS adapter),
+.IP \(bu 3
+a master is always attached to a controller (a MASSBUS adapter),
+.IP \(bu 3
+a tape is always attached to a master (for MASSBUS
+tape drives),
+.IP \(bu 3
+a disk is always attached to a controller, and
+.IP \(bu 3
+devices
+are always attached to controllers (e.g. UNIBUS controllers attached
+to UNIBUS adapters).
+.LP
+The following lines give an example of each of these interconnections:
+.IP
+.ta 1.5i 2.5i 4.0i
+.nf
+\fBcontroller\fP hk0 \fBat\fP uba0 ...
+\fBmaster\fP ht0 \fBat\fP mba0 ...
+\fBdisk\fP hp0 \fBat\fP mba0 ...
+\fBtape\fP tu0 \fBat\fP ht0 ...
+\fBdisk\fP rk1 \fBat\fP hk0 ...
+\fBdevice\fP dz0 \fBat\fP uba0 ...
+.fi
+.LP
+Any piece of hardware which may be connected to a specific
+controller may also be wildcarded across multiple controllers.
+.PP
+The final piece of information needed by the system to configure
+devices is some indication of where or how a device will interrupt.
+For tapes and disks, simply specifying the \fIslave\fP or \fIdrive\fP
+number is sufficient to locate the control status register for the
+device.
+\fIDrive\fP numbers may be wildcarded
+on MASSBUS devices, but not on disks on a UNIBUS controller.
+For controllers, the control status register must be
+given explicitly, as well the number of interrupt vectors used and
+the names of the routines to which they should be bound.
+Thus the example lines given above might be completed as:
+.IP
+.ta 1.5i 2.5i 4.0i
+.nf
+\fBcontroller\fP hk0 \fBat\fP uba0 \fBcsr\fP 0177440 \fBvector\fP rkintr
+\fBmaster\fP ht0 \fBat\fP mba0 \fBdrive\fP 0
+\fBdisk\fP hp0 \fBat\fP mba0 \fBdrive\fP ?
+\fBtape\fP tu0 \fBat\fP ht0 \fBslave\fP 0
+\fBdisk\fP rk1 \fBat\fP hk0 \fBdrive\fP 1
+\fBdevice\fP dz0 \fBat\fP uba0 \fBcsr\fP 0160100 \fBvector\fP dzrint dzxint
+.fi
+.PP
+Certain device drivers require extra information passed to them
+at boot time to tailor their operation to the actual hardware present.
+The line printer driver, for example, needs to know how many columns
+are present on each non-standard line printer (i.e. a line printer
+with other than 80 columns). The drivers for the terminal multiplexors
+need to know which lines are attached to modem lines so that no one will
+be allowed to use them unless a connection is present. For this reason,
+one last parameter may be specified to a
+.IR device ,
+a
+.I flags
+field. It has the syntax
+.IP
+\fBflags\fP \fInumber\fP
+.LP
+and is usually placed after the
+.I csr
+specification. The
+.I number
+is passed directly to the associated driver. The manual pages
+in section 4 should be consulted to determine how each driver
+uses this value (if at all).
+Communications interface drivers commonly use the flags
+to indicate whether modem control signals are in use.
+.PP
+The exact syntax for each specific device is given in the Synopsis
+section of its manual page in section 4 of the manual.
+.NH 2
+Pseudo-devices
+.PP
+A number of drivers and software subsystems
+are treated like device drivers without any associated hardware.
+To include any of these pieces, a ``pseudo-device'' specification
+must be used. A specification for a pseudo device takes the form
+.IP
+.DT
+.nf
+\fBpseudo-device\fP \fIdevice-name\fP [ \fIhowmany\fP ]
+.fi
+.PP
+Examples of pseudo devices are
+\fBpty\fP, the pseudo terminal driver (where the optional
+.I howmany
+value indicates the number of pseudo terminals to configure, 32 default),
+and \fBloop\fP, the software loopback network pseudo-interface.
+Other pseudo devices for the network include
+\fBimp\fP (required when a CSS or ACC imp is configured)
+and \fBether\fP (used by the Address Resolution Protocol
+on 10 Mb/sec Ethernets).
+More information on configuring each of these can also be found
+in section 4 of the manual.
diff --git a/usr.sbin/config/SMM.doc/5.t b/usr.sbin/config/SMM.doc/5.t
new file mode 100644
index 0000000..81f2a52
--- /dev/null
+++ b/usr.sbin/config/SMM.doc/5.t
@@ -0,0 +1,271 @@
+.\" Copyright (c) 1983, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)5.t 8.1 (Berkeley) 6/8/93
+.\"
+.\".ds RH "Sample Configuration Files
+.ne 2i
+.NH
+SAMPLE CONFIGURATION FILES
+.PP
+In this section we will consider how to configure a
+sample VAX-11/780 system on which the hardware can be
+reconfigured to guard against various hardware mishaps.
+We then study the rules needed to configure a VAX-11/750
+to run in a networking environment.
+.NH 2
+VAX-11/780 System
+.PP
+Our VAX-11/780 is configured with hardware
+recommended in the document ``Hints on Configuring a VAX for 4.2BSD''
+(this is one of the high-end configurations).
+Table 1 lists the pertinent hardware to be configured.
+.DS B
+.TS
+box;
+l | l | l | l | l
+l | l | l | l | l.
+Item Vendor Connection Name Reference
+_
+cpu DEC VAX780
+MASSBUS controller Emulex nexus ? mba0 hp(4)
+disk Fujitsu mba0 hp0
+disk Fujitsu mba0 hp1
+MASSBUS controller Emulex nexus ? mba1
+disk Fujitsu mba1 hp2
+disk Fujitsu mba1 hp3
+UNIBUS adapter DEC nexus ?
+tape controller Emulex uba0 tm0 tm(4)
+tape drive Kennedy tm0 te0
+tape drive Kennedy tm0 te1
+terminal multiplexor Emulex uba0 dh0 dh(4)
+terminal multiplexor Emulex uba0 dh1
+terminal multiplexor Emulex uba0 dh2
+.TE
+.DE
+.ce
+Table 1. VAX-11/780 Hardware support.
+.LP
+We will call this machine ANSEL and construct a configuration
+file one step at a time.
+.PP
+The first step is to fill in the global configuration parameters.
+The machine is a VAX, so the
+.I "machine type"
+is ``vax''. We will assume this system will
+run only on this one processor, so the
+.I "cpu type"
+is ``VAX780''. The options are empty since this is going to
+be a ``vanilla'' VAX. The system identifier, as mentioned before,
+is ``ANSEL,'' and the maximum number of users we plan to support is
+about 40. Thus the beginning of the configuration file looks like
+this:
+.DS
+.ta 1.5i 2.5i 4.0i
+#
+# ANSEL VAX (a picture perfect machine)
+#
+machine vax
+cpu VAX780
+timezone 8 dst
+ident ANSEL
+maxusers 40
+.DE
+.PP
+To this we must then add the specifications for three
+system images. The first will be our standard system with the
+root on ``hp0'' and swapping on the same drive as the root.
+The second will have the root file system in the same location,
+but swap space interleaved among drives on each controller.
+Finally, the third will be a generic system,
+to allow us to boot off any of the four disk drives.
+.DS
+.ta 1.5i 2.5i
+config kernel root on hp0
+config hpkernel root on hp0 swap on hp0 and hp2
+config genkernel swap generic
+.DE
+.PP
+Finally, the hardware must be specified. Let us first just try
+transcribing the information from Table 1.
+.DS
+.ta 1.5i 2.5i 4.0i
+controller mba0 at nexus ?
+disk hp0 at mba0 disk 0
+disk hp1 at mba0 disk 1
+controller mba1 at nexus ?
+disk hp2 at mba1 disk 2
+disk hp3 at mba1 disk 3
+controller uba0 at nexus ?
+controller tm0 at uba0 csr 0172520 vector tmintr
+tape te0 at tm0 drive 0
+tape te1 at tm0 drive 1
+device dh0 at uba0 csr 0160020 vector dhrint dhxint
+device dm0 at uba0 csr 0170500 vector dmintr
+device dh1 at uba0 csr 0160040 vector dhrint dhxint
+device dh2 at uba0 csr 0160060 vector dhrint dhxint
+.DE
+.LP
+(Oh, I forgot to mention one panel of the terminal multiplexor
+has modem control, thus the ``dm0'' device.)
+.PP
+This will suffice, but leaves us with little flexibility. Suppose
+our first disk controller were to break. We would like to recable the
+drives normally on the second controller so that all our disks could
+still be used without reconfiguring the system. To do this we wildcard
+the MASSBUS adapter connections and also the slave numbers. Further,
+we wildcard the UNIBUS adapter connections in case we decide some time
+in the future to purchase another adapter to offload the single UNIBUS
+we currently have. The revised device specifications would then be:
+.DS
+.ta 1.5i 2.5i 4.0i
+controller mba0 at nexus ?
+disk hp0 at mba? disk ?
+disk hp1 at mba? disk ?
+controller mba1 at nexus ?
+disk hp2 at mba? disk ?
+disk hp3 at mba? disk ?
+controller uba0 at nexus ?
+controller tm0 at uba? csr 0172520 vector tmintr
+tape te0 at tm0 drive 0
+tape te1 at tm0 drive 1
+device dh0 at uba? csr 0160020 vector dhrint dhxint
+device dm0 at uba? csr 0170500 vector dmintr
+device dh1 at uba? csr 0160040 vector dhrint dhxint
+device dh2 at uba? csr 0160060 vector dhrint dhxint
+.DE
+.LP
+The completed configuration file for ANSEL is shown in Appendix C.
+.NH 2
+VAX-11/750 with network support
+.PP
+Our VAX-11/750 system will be located on two 10Mb/s Ethernet
+local area networks and also the DARPA Internet. The system
+will have a MASSBUS drive for the root file system and two
+UNIBUS drives. Paging is interleaved among all three drives.
+We have sold our standard DEC terminal multiplexors since this
+machine will be accessed solely through the network. This
+machine is not intended to have a large user community, it
+does not have a great deal of memory. First the global parameters:
+.DS
+.ta 1.5i 2.5i 4.0i
+#
+# UCBVAX (Gateway to the world)
+#
+machine vax
+cpu "VAX780"
+cpu "VAX750"
+ident UCBVAX
+timezone 8 dst
+maxusers 32
+options INET
+options NS
+.DE
+.PP
+The multiple cpu types allow us to replace UCBVAX with a
+more powerful cpu without reconfiguring the system. The
+value of 32 given for the maximum number of users is done to
+force the system data structures to be over-allocated. That
+is desirable on this machine because, while it is not expected
+to support many users, it is expected to perform a great deal
+of work.
+The ``INET'' indicates that we plan to use the
+DARPA standard Internet protocols on this machine,
+and ``NS'' also includes support for Xerox NS protocols.
+Note that unlike 4.2BSD configuration files,
+the network protocol options do not require corresponding pseudo devices.
+.PP
+The system images and disks are configured next.
+.DS
+.ta 1.5i 2.5i 4.0i
+config kernel root on hp swap on hp and rk0 and rk1
+config upkernel root on up
+config hkkernel root on hk swap on rk0 and rk1
+
+controller mba0 at nexus ?
+controller uba0 at nexus ?
+disk hp0 at mba? drive 0
+disk hp1 at mba? drive 1
+controller sc0 at uba? csr 0176700 vector upintr
+disk up0 at sc0 drive 0
+disk up1 at sc0 drive 1
+controller hk0 at uba? csr 0177440 vector rkintr
+disk rk0 at hk0 drive 0
+disk rk1 at hk0 drive 1
+.DE
+.PP
+UCBVAX requires heavy interleaving of its paging area to keep up
+with all the mail traffic it handles. The limiting factor on this
+system's performance is usually the number of disk arms, as opposed
+to memory or cpu cycles. The extra UNIBUS controller, ``sc0'',
+is in case the MASSBUS controller breaks and a spare controller
+must be installed (most of our old UNIBUS controllers have been
+replaced with the newer MASSBUS controllers, so we have a number
+of these around as spares).
+.PP
+Finally, we add in the network devices.
+Pseudo terminals are needed to allow users to
+log in across the network (remember the only hardwired terminal
+is the console).
+The software loopback device is used for on-machine communications.
+The connection to the Internet is through
+an IMP, this requires yet another
+.I pseudo-device
+(in addition to the actual hardware device used by the
+IMP software). And, finally, there are the two Ethernet devices.
+These use a special protocol, the Address Resolution Protocol (ARP),
+to map between Internet and Ethernet addresses. Thus, yet another
+.I pseudo-device
+is needed. The additional device specifications are show below.
+.DS
+.ta 1.5i 2.5i 4.0i
+pseudo-device pty
+pseudo-device loop
+pseudo-device imp
+device acc0 at uba? csr 0167600 vector accrint accxint
+pseudo-device ether
+device ec0 at uba? csr 0164330 vector ecrint eccollide ecxint
+device il0 at uba? csr 0164000 vector ilrint ilcint
+.DE
+.LP
+The completed configuration file for UCBVAX is shown in Appendix C.
+.NH 2
+Miscellaneous comments
+.PP
+It should be noted in these examples that neither system was
+configured to use disk quotas or the 4.1BSD compatibility mode.
+To use these optional facilities, and others, we would probably
+clean out our current configuration, reconfigure the system, then
+recompile and relink the system image(s). This could, of course,
+be avoided by figuring out which relocatable object files are
+affected by the reconfiguration, then reconfiguring and recompiling
+only those files affected by the configuration change. This technique
+should be used carefully.
diff --git a/usr.sbin/config/SMM.doc/6.t b/usr.sbin/config/SMM.doc/6.t
new file mode 100644
index 0000000..f02baed
--- /dev/null
+++ b/usr.sbin/config/SMM.doc/6.t
@@ -0,0 +1,239 @@
+.\" Copyright (c) 1983, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)6.t 8.1 (Berkeley) 6/8/93
+.\"
+.\".ds RH "Adding New Devices
+.ne 2i
+.NH
+ADDING NEW SYSTEM SOFTWARE
+.PP
+This section is not for the novice, it describes
+some of the inner workings of the configuration process as
+well as the pertinent parts of the system autoconfiguration process.
+It is intended to give
+those people who intend to install new device drivers and/or
+other system facilities sufficient information to do so in the
+manner which will allow others to easily share the changes.
+.PP
+This section is broken into four parts:
+.IP \(bu 3
+general guidelines to be followed in modifying system code,
+.IP \(bu 3
+how to add non-standard system facilities to 4.4BSD,
+.IP \(bu 3
+how to add a device driver to 4.4BSD, and
+.NH 2
+Modifying system code
+.PP
+If you wish to make site-specific modifications to the system
+it is best to bracket them with
+.DS
+#ifdef SITENAME
+\&...
+#endif
+.DE
+to allow your source to be easily distributed to others, and
+also to simplify \fIdiff\fP\|(1) listings. If you choose not
+to use a source code control system (e.g. SCCS, RCS), and
+perhaps even if you do, it is
+recommended that you save the old code with something
+of the form:
+.DS
+#ifndef SITENAME
+\&...
+#endif
+.DE
+We try to isolate our site-dependent code in individual files
+which may be configured with pseudo-device specifications.
+.PP
+Indicate machine-specific code with ``#ifdef vax'' (or other machine,
+as appropriate).
+4.4BSD underwent extensive work to make it extremely portable to
+machines with similar architectures\- you may someday find
+yourself trying to use a single copy of the source code on
+multiple machines.
+.NH 2
+Adding non-standard system facilities
+.PP
+This section considers the work needed to augment
+.IR config 's
+data base files for non-standard system facilities.
+.I Config
+uses a set of files that list the source modules that may be required
+when building a system.
+The data bases are taken from the directory in which
+.I config
+is run, normally /sys/conf.
+Three such files may be used:
+.IR files ,
+.IR files .machine,
+and
+.IR files .ident.
+The first is common to all systems,
+the second contains files unique to a single machine type,
+and the third is an optional list of modules for use on a specific machine.
+This last file may override specifications in the first two.
+The format of the
+.I files
+file has grown somewhat complex over time. Entries are normally of
+the form
+.IP
+.nf
+.DT
+\fIdir/source.c\fP \fItype\fP \fIoption-list\fP \fImodifiers\fP
+.LP
+for example,
+.IP
+.nf
+.DT
+\fIvaxuba/foo.c\fP \fBoptional\fP foo \fBdevice-driver\fP
+.LP
+The
+.I type
+is one of
+.B standard
+or
+.BR optional .
+Files marked as standard are included in all system configurations.
+Optional file specifications include a list of one or more system
+options that together require the inclusion of this module.
+The options in the list may be either names of devices that may
+be in the configuration file,
+or the names of system options that may be defined.
+An optional file may be listed multiple times with different options;
+if all of the options for any of the entries are satisfied,
+the module is included.
+.PP
+If a file is specified as a
+.IR device-driver ,
+any special compilation options for device drivers will be invoked.
+On the VAX this results in the use of the
+.B \-i
+option for the C optimizer. This is required when pointer references
+are made to memory locations in the VAX I/O address space.
+.PP
+Two other optional keywords modify the usage of the file.
+.I Config
+understands that certain files are used especially for
+kernel profiling. These files are indicated in the
+.I files
+files with a
+.I profiling-routine
+keyword. For example, the current profiling subroutines
+are sequestered off in a separate file with the following
+entry:
+.IP
+.nf
+.DT
+\fIsys/subr_mcount.c\fP \fBoptional\fP \fBprofiling-routine\fP
+.fi
+.LP
+The
+.I profiling-routine
+keyword forces
+.I config
+not to compile the source file with the
+.B \-pg
+option.
+.PP
+The second keyword which can be of use is the
+.I config-dependent
+keyword. This causes
+.I config
+to compile the indicated module with the global configuration
+parameters. This allows certain modules, such as
+.I machdep.c
+to size system data structures based on the maximum number
+of users configured for the system.
+.NH 2
+Adding device drivers to 4.4BSD
+.PP
+The I/O system and
+.I config
+have been designed to easily allow new device support to be added.
+The system source directories are organized as follows:
+.DS
+.TS
+lw(1.0i) l.
+/sys/h machine independent include files
+/sys/sys machine-independent system source files
+/sys/conf site configuration files and basic templates
+/sys/net network-protocol-independent, but network-related code
+/sys/netinet DARPA Internet code
+/sys/netimp IMP support code
+/sys/netns Xerox NS code
+/sys/vax VAX-specific mainline code
+/sys/vaxif VAX network interface code
+/sys/vaxmba VAX MASSBUS device drivers and related code
+/sys/vaxuba VAX UNIBUS device drivers and related code
+.TE
+.DE
+.PP
+Existing block and character device drivers for the VAX
+reside in ``/sys/vax'', ``/sys/vaxmba'', and ``/sys/vaxuba''. Network
+interface drivers reside in ``/sys/vaxif''. Any new device
+drivers should be placed in the appropriate source code directory
+and named so as not to conflict with existing devices.
+Normally, definitions for things like device registers are placed in
+a separate file in the same directory. For example, the ``dh''
+device driver is named ``dh.c'' and its associated include file is
+named ``dhreg.h''.
+.PP
+Once the source for the device driver has been placed in a directory,
+the file ``/sys/conf/files.machine'', and possibly
+``/sys/conf/devices.machine'' should be modified. The
+.I files
+files in the conf directory contain a line for each C source or binary-only
+file in the system. Those files which are machine independent are
+located in ``/sys/conf/files,'' while machine specific files
+are in ``/sys/conf/files.machine.'' The ``devices.machine'' file
+is used to map device names to major block device numbers. If the device
+driver being added provides support for a new disk
+you will want to modify this file (the format is obvious).
+.PP
+In addition to including the driver in the
+.I files
+file, it must also be added to the device configuration tables. These
+are located in ``/sys/vax/conf.c'', or similar for machines other than
+the VAX. If you don't understand what to add to this file, you should
+study an entry for an existing driver.
+Remember that the position in the
+device table specifies the major device number.
+The block major number is needed in the ``devices.machine'' file
+if the device is a disk.
+.PP
+With the configuration information in place, your configuration
+file appropriately modified, and a system reconfigured and rebooted
+you should incorporate the shell commands needed to install the special
+files in the file system to the file ``/dev/MAKEDEV'' or
+``/dev/MAKEDEV.local''. This is discussed in the document ``Installing
+and Operating 4.4BSD''.
diff --git a/usr.sbin/config/SMM.doc/Makefile b/usr.sbin/config/SMM.doc/Makefile
new file mode 100644
index 0000000..3a2f188
--- /dev/null
+++ b/usr.sbin/config/SMM.doc/Makefile
@@ -0,0 +1,10 @@
+# @(#)Makefile 8.1 (Berkeley) 6/8/93
+
+DIR= smm/02.config
+SRCS= 0.t 1.t 2.t 3.t 4.t 5.t 6.t a.t b.t c.t d.t e.t
+MACROS= -ms
+
+paper.ps: ${SRCS}
+ ${TBL} ${SRCS} | ${ROFF} > ${.TARGET}
+
+.include <bsd.doc.mk>
diff --git a/usr.sbin/config/SMM.doc/a.t b/usr.sbin/config/SMM.doc/a.t
new file mode 100644
index 0000000..dfcb954
--- /dev/null
+++ b/usr.sbin/config/SMM.doc/a.t
@@ -0,0 +1,162 @@
+.\" Copyright (c) 1983, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)a.t 8.1 (Berkeley) 6/8/93
+.\"
+.\".ds RH "Configuration File Grammar
+.bp
+.LG
+.B
+.ce
+APPENDIX A. CONFIGURATION FILE GRAMMAR
+.sp
+.R
+.NL
+.PP
+The following grammar is a compressed form of the actual
+\fIyacc\fP\|(1) grammar used by
+.I config
+to parse configuration files.
+Terminal symbols are shown all in upper case, literals
+are emboldened; optional clauses are enclosed in brackets, ``[''
+and ``]''; zero or more instantiations are denoted with ``*''.
+.sp
+.nf
+.DT
+Configuration ::= [ Spec \fB;\fP ]*
+
+Spec ::= Config_spec
+ | Device_spec
+ | \fBtrace\fP
+ | /* lambda */
+
+/* configuration specifications */
+
+Config_spec ::= \fBmachine\fP ID
+ | \fBcpu\fP ID
+ | \fBoptions\fP Opt_list
+ | \fBident\fP ID
+ | System_spec
+ | \fBtimezone\fP [ \fB\-\fP ] NUMBER [ \fBdst\fP [ NUMBER ] ]
+ | \fBtimezone\fP [ \fB\-\fP ] FPNUMBER [ \fBdst\fP [ NUMBER ] ]
+ | \fBmaxusers\fP NUMBER
+
+/* system configuration specifications */
+
+System_spec ::= \fBconfig\fP ID System_parameter [ System_parameter ]*
+
+System_parameter ::= swap_spec | root_spec | dump_spec | arg_spec
+
+swap_spec ::= \fBswap\fP [ \fBon\fP ] swap_dev [ \fBand\fP swap_dev ]*
+
+swap_dev ::= dev_spec [ \fBsize\fP NUMBER ]
+
+root_spec ::= \fBroot\fP [ \fBon\fP ] dev_spec
+
+dump_spec ::= \fBdumps\fP [ \fBon\fP ] dev_spec
+
+arg_spec ::= \fBargs\fP [ \fBon\fP ] dev_spec
+
+dev_spec ::= dev_name | major_minor
+
+major_minor ::= \fBmajor\fP NUMBER \fBminor\fP NUMBER
+
+dev_name ::= ID [ NUMBER [ ID ] ]
+
+/* option specifications */
+
+Opt_list ::= Option [ \fB,\fP Option ]*
+
+Option ::= ID [ \fB=\fP Opt_value ]
+
+Opt_value ::= ID | NUMBER
+
+Mkopt_list ::= Mkoption [ \fB,\fP Mkoption ]*
+
+Mkoption ::= ID \fB=\fP Opt_value
+
+/* device specifications */
+
+Device_spec ::= \fBdevice\fP Dev_name Dev_info Int_spec
+ | \fBmaster\fP Dev_name Dev_info
+ | \fBdisk\fP Dev_name Dev_info
+ | \fBtape\fP Dev_name Dev_info
+ | \fBcontroller\fP Dev_name Dev_info [ Int_spec ]
+ | \fBpseudo-device\fP Dev [ NUMBER ]
+
+Dev_name ::= Dev NUMBER
+
+Dev ::= \fBuba\fP | \fBmba\fP | ID
+
+Dev_info ::= Con_info [ Info ]*
+
+Con_info ::= \fBat\fP Dev NUMBER
+ | \fBat\fP \fBnexus\fP NUMBER
+
+Info ::= \fBcsr\fP NUMBER
+ | \fBdrive\fP NUMBER
+ | \fBslave\fP NUMBER
+ | \fBflags\fP NUMBER
+
+Int_spec ::= \fBvector\fP ID [ ID ]*
+ | \fBpriority\fP NUMBER
+.fi
+.sp
+.SH
+Lexical Conventions
+.LP
+The terminal symbols are loosely defined as:
+.IP ID
+.br
+One or more alphabetics, either upper or lower case, and underscore,
+``_''.
+.IP NUMBER
+.br
+Approximately the C language specification for an integer number.
+That is, a leading ``0x'' indicates a hexadecimal value,
+a leading ``0'' indicates an octal value, otherwise the number is
+expected to be a decimal value. Hexadecimal numbers may use either
+upper or lower case alphabetics.
+.IP FPNUMBER
+.br
+A floating point number without exponent. That is a number of the
+form ``nnn.ddd'', where the fractional component is optional.
+.LP
+In special instances a question mark, ``?'', can be substituted for
+a ``NUMBER'' token. This is used to effect wildcarding in device
+interconnection specifications.
+.LP
+Comments in configuration files are indicated by a ``#'' character
+at the beginning of the line; the remainder of the line is discarded.
+.LP
+A specification
+is interpreted as a continuation of the previous line
+if the first character of the line is tab.
diff --git a/usr.sbin/config/SMM.doc/b.t b/usr.sbin/config/SMM.doc/b.t
new file mode 100644
index 0000000..901a009
--- /dev/null
+++ b/usr.sbin/config/SMM.doc/b.t
@@ -0,0 +1,137 @@
+.\" Copyright (c) 1983, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)b.t 8.1 (Berkeley) 6/8/93
+.\"
+.\".ds RH "Device Defaulting Rules
+.bp
+.LG
+.B
+.ce
+APPENDIX B. RULES FOR DEFAULTING SYSTEM DEVICES
+.sp
+.R
+.NL
+.PP
+When \fIconfig\fP processes a ``config'' rule which does
+not fully specify the location of the root file system,
+paging area(s), device for system dumps, and device for
+argument list processing it applies a set of rules to
+define those values left unspecified. The following list
+of rules are used in defaulting system devices.
+.IP 1) 3
+If a root device is not specified, the swap
+specification must indicate a ``generic'' system is to be built.
+.IP 2) 3
+If the root device does not specify a unit number, it
+defaults to unit 0.
+.IP 3) 3
+If the root device does not include a partition specification,
+it defaults to the ``a'' partition.
+.IP 4) 3
+If no swap area is specified, it defaults to the ``b''
+partition of the root device.
+.IP 5) 3
+If no device is specified for processing argument lists, the
+first swap partition is selected.
+.IP 6) 3
+If no device is chosen for system dumps, the first swap
+partition is selected (see below to find out where dumps are
+placed within the partition).
+.PP
+The following table summarizes the default partitions selected
+when a device specification is incomplete, e.g. ``hp0''.
+.DS
+.TS
+l l.
+Type Partition
+_
+root ``a''
+swap ``b''
+args ``b''
+dumps ``b''
+.TE
+.DE
+.SH
+Multiple swap/paging areas
+.PP
+When multiple swap partitions are specified, the system treats the
+first specified as a ``primary'' swap area which is always used.
+The remaining partitions are then interleaved into the paging
+system at the time a
+.IR swapon (2)
+system call is made. This is normally done at boot time with
+a call to
+.IR swapon (8)
+from the /etc/rc file.
+.SH
+System dumps
+.PP
+System dumps are automatically taken after a system crash,
+provided the device driver for the ``dumps'' device supports
+this. The dump contains the contents of memory, but not
+the swap areas. Normally the dump device is a disk in
+which case the information is copied to a location at the
+back of the partition. The dump is placed in the back of the
+partition because the primary swap and dump device are commonly
+the same device and this allows the system to be rebooted without
+immediately overwriting the saved information. When a dump has
+occurred, the system variable \fIdumpsize\fP
+is set to a non-zero value indicating the size (in bytes) of
+the dump. The \fIsavecore\fP\|(8)
+program then copies the information from the dump partition to
+a file in a ``crash'' directory and also makes a copy of the
+system which was running at the time of the crash (usually
+``/kernel''). The offset to the system dump is defined in the
+system variable \fIdumplo\fP (a sector offset from
+the front of the dump partition). The
+.I savecore
+program operates by reading the contents of \fIdumplo\fP, \fIdumpdev\fP,
+and \fIdumpmagic\fP from /dev/kmem, then comparing the value
+of \fIdumpmagic\fP read from /dev/kmem to that located in
+corresponding location in the dump area of the dump partition.
+If a match is found,
+.I savecore
+assumes a crash occurred and reads \fIdumpsize\fP from the dump area
+of the dump partition. This value is then used in copying the
+system dump. Refer to
+\fIsavecore\fP\|(8)
+for more information about its operation.
+.PP
+The value \fIdumplo\fP is calculated to be
+.DS
+\fIdumpdev-size\fP \- \fImemsize\fP
+.DE
+where \fIdumpdev-size\fP is the size of the disk partition
+where system dumps are to be placed, and
+\fImemsize\fP is the size of physical memory.
+If the disk partition is not large enough to hold a full
+dump, \fIdumplo\fP is set to 0 (the start of the partition).
diff --git a/usr.sbin/config/SMM.doc/c.t b/usr.sbin/config/SMM.doc/c.t
new file mode 100644
index 0000000..67b63ec
--- /dev/null
+++ b/usr.sbin/config/SMM.doc/c.t
@@ -0,0 +1,109 @@
+.\" Copyright (c) 1983, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)c.t 8.1 (Berkeley) 6/8/93
+.\"
+.\".ds RH "Sample Config Files
+.bp
+.LG
+.B
+.ce
+APPENDIX C. SAMPLE CONFIGURATION FILES
+.sp
+.R
+.NL
+.PP
+The following configuration files are developed in section 5;
+they are included here for completeness.
+.sp 2
+.nf
+.ta 1.5i 2.5i 4.0i
+#
+# ANSEL VAX (a picture perfect machine)
+#
+machine vax
+cpu VAX780
+timezone 8 dst
+ident ANSEL
+maxusers 40
+
+config kernel root on hp0
+config hpkernel root on hp0 swap on hp0 and hp2
+config genkernel swap generic
+
+controller mba0 at nexus ?
+disk hp0 at mba? disk ?
+disk hp1 at mba? disk ?
+controller mba1 at nexus ?
+disk hp2 at mba? disk ?
+disk hp3 at mba? disk ?
+controller uba0 at nexus ?
+controller tm0 at uba? csr 0172520 vector tmintr
+tape te0 at tm0 drive 0
+tape te1 at tm0 drive 1
+device dh0 at uba? csr 0160020 vector dhrint dhxint
+device dm0 at uba? csr 0170500 vector dmintr
+device dh1 at uba? csr 0160040 vector dhrint dhxint
+device dh2 at uba? csr 0160060 vector dhrint dhxint
+.bp
+#
+# UCBVAX - Gateway to the world
+#
+machine vax
+cpu "VAX780"
+cpu "VAX750"
+ident UCBVAX
+timezone 8 dst
+maxusers 32
+options INET
+options NS
+
+config kernel root on hp swap on hp and rk0 and rk1
+config upkernel root on up
+config hkkernel root on hk swap on rk0 and rk1
+
+controller mba0 at nexus ?
+controller uba0 at nexus ?
+disk hp0 at mba? drive 0
+disk hp1 at mba? drive 1
+controller sc0 at uba? csr 0176700 vector upintr
+disk up0 at sc0 drive 0
+disk up1 at sc0 drive 1
+controller hk0 at uba? csr 0177440 vector rkintr
+disk rk0 at hk0 drive 0
+disk rk1 at hk0 drive 1
+pseudo-device pty
+pseudo-device loop
+pseudo-device imp
+device acc0 at uba? csr 0167600 vector accrint accxint
+pseudo-device ether
+device ec0 at uba? csr 0164330 vector ecrint eccollide ecxint
+device il0 at uba? csr 0164000 vector ilrint ilcint
diff --git a/usr.sbin/config/SMM.doc/d.t b/usr.sbin/config/SMM.doc/d.t
new file mode 100644
index 0000000..db9ab80
--- /dev/null
+++ b/usr.sbin/config/SMM.doc/d.t
@@ -0,0 +1,272 @@
+.\" Copyright (c) 1983, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)d.t 8.1 (Berkeley) 6/8/93
+.\"
+.\".ds RH "Data Structure Sizing Rules
+.bp
+.LG
+.B
+.ce
+APPENDIX D. VAX KERNEL DATA STRUCTURE SIZING RULES
+.sp
+.R
+.NL
+.PP
+Certain system data structures are sized at compile time
+according to the maximum number of simultaneous users expected,
+while others are calculated at boot time based on the
+physical resources present, e.g. memory. This appendix lists
+both sets of rules and also includes some hints on changing
+built-in limitations on certain data structures.
+.SH
+Compile time rules
+.PP
+The file \fI/sys/conf\|/param.c\fP contains the definitions of
+almost all data structures sized at compile time. This file
+is copied into the directory of each configured system to allow
+configuration-dependent rules and values to be maintained.
+(Each copy normally depends on the copy in /sys/conf,
+and global modifications cause the file to be recopied unless
+the makefile is modified.)
+The rules implied by its contents are summarized below (here
+MAXUSERS refers to the value defined in the configuration file
+in the ``maxusers'' rule).
+Most limits are computed at compile time and stored in global variables
+for use by other modules; they may generally be patched in the system
+binary image before rebooting to test new values.
+.IP \fBnproc\fP
+.br
+The maximum number of processes which may be running at any time.
+It is referred to in other calculations as NPROC and is defined to be
+.DS
+20 + 8 * MAXUSERS
+.DE
+.IP \fBntext\fP
+.br
+The maximum number of active shared text segments.
+The constant is intended to allow for network servers and common commands
+that remain in the table.
+It is defined as
+.DS
+36 + MAXUSERS.
+.DE
+.IP \fBninode\fP
+.br
+The maximum number of files in the file system which may be
+active at any time. This includes files in use by users, as
+well as directory files being read or written by the system
+and files associated with bound sockets in the UNIX IPC domain.
+It is defined as
+.DS
+(NPROC + 16 + MAXUSERS) + 32
+.DE
+.IP \fBnfile\fP
+.br
+The number of ``file table'' structures. One file
+table structure is used for each open, unshared, file descriptor.
+Multiple file descriptors may reference a single file table
+entry when they are created through a \fIdup\fP call, or as the
+result of a \fIfork\fP. This is defined to be
+.DS
+16 * (NPROC + 16 + MAXUSERS) / 10 + 32
+.DE
+.IP \fBncallout\fP
+.br
+The number of ``callout'' structures. One callout
+structure is used per internal system event handled with
+a timeout. Timeouts are used for terminal delays,
+watchdog routines in device drivers, protocol timeout processing, etc.
+This is defined as
+.DS
+16 + NPROC
+.DE
+.IP \fBnclist\fP
+.br
+The number of ``c-list'' structures. C-list structures are
+used in terminal I/O, and currently each holds 60 characters.
+Their number is defined as
+.DS
+60 + 12 * MAXUSERS
+.DE
+.IP \fBnmbclusters\fP
+.br
+The maximum number of pages which may be allocated by the network.
+This is defined as 256 (a quarter megabyte of memory) in /sys/h/mbuf.h.
+In practice, the network rarely uses this much memory. It starts off
+by allocating 8 kilobytes of memory, then requesting more as
+required. This value represents an upper bound.
+.IP \fBnquota\fP
+.br
+The number of ``quota'' structures allocated. Quota structures
+are present only when disc quotas are configured in the system. One
+quota structure is kept per user. This is defined to be
+.DS
+(MAXUSERS * 9) / 7 + 3
+.DE
+.IP \fBndquot\fP
+.br
+The number of ``dquot'' structures allocated. Dquot structures
+are present only when disc quotas are configured in the system.
+One dquot structure is required per user, per active file system quota.
+That is, when a user manipulates a file on a file system on which
+quotas are enabled, the information regarding the user's quotas on
+that file system must be in-core. This information is cached, so
+that not all information must be present in-core all the time.
+This is defined as
+.DS
+NINODE + (MAXUSERS * NMOUNT) / 4
+.DE
+where NMOUNT is the maximum number of mountable file systems.
+.LP
+In addition to the above values, the system page tables (used to
+map virtual memory in the kernel's address space) are sized at
+compile time by the SYSPTSIZE definition in the file /sys/vax/vmparam.h.
+This is defined to be
+.DS
+20 + MAXUSERS
+.DE
+pages of page tables.
+Its definition affects
+the size of many data structures allocated at boot time because
+it constrains the amount of virtual memory which may be addressed
+by the running system. This is often the limiting factor
+in the size of the buffer cache, in which case a message is printed
+when the system configures at boot time.
+.SH
+Run-time calculations
+.PP
+The most important data structures sized at run-time are those used in
+the buffer cache. Allocation is done by allocating physical memory
+(and system virtual memory) immediately after the system
+has been started up; look in the file /sys/vax/machdep.c.
+The amount of physical memory which may be allocated to the buffer
+cache is constrained by the size of the system page tables, among
+other things. While the system may calculate
+a large amount of memory to be allocated to the buffer cache,
+if the system page
+table is too small to map this physical
+memory into the virtual address space
+of the system, only as much as can be mapped will be used.
+.PP
+The buffer cache is comprised of a number of ``buffer headers''
+and a pool of pages attached to these headers. Buffer headers
+are divided into two categories: those used for swapping and
+paging, and those used for normal file I/O. The system tries
+to allocate 10% of the first two megabytes and 5% of the remaining
+available physical memory for the buffer
+cache (where \fIavailable\fP does not count that space occupied by
+the system's text and data segments). If this results in fewer
+than 16 pages of memory allocated, then 16 pages are allocated.
+This value is kept in the initialized variable \fIbufpages\fP
+so that it may be patched in the binary image (to allow tuning
+without recompiling the system),
+or the default may be overridden with a configuration-file option.
+For example, the option \fBoptions BUFPAGES="3200"\fP
+causes 3200 pages (3.2M bytes) to be used by the buffer cache.
+A sufficient number of file I/O buffer headers are then allocated
+to allow each to hold 2 pages each.
+Each buffer maps 8K bytes.
+If the number of buffer pages is larger than can be mapped
+by the buffer headers, the number of pages is reduced.
+The number of buffer headers allocated
+is stored in the global variable \fInbuf\fP,
+which may be patched before the system is booted.
+The system option \fBoptions NBUF="1000"\fP forces the allocation
+of 1000 buffer headers.
+Half as many swap I/O buffer headers as file I/O buffers
+are allocated,
+but no more than 256.
+.SH
+System size limitations
+.PP
+As distributed, the sum of the virtual sizes of the core-resident
+processes is limited to 256M bytes. The size of the text
+segment of a single process is currently limited to 6M bytes.
+It may be increased to no greater than the data segment size limit
+(see below) by redefining MAXTSIZ.
+This may be done with a configuration file option,
+e.g. \fBoptions MAXTSIZ="(10*1024*1024)"\fP
+to set the limit to 10 million bytes.
+Other per-process limits discussed here may be changed with similar options
+with names given in parentheses.
+Soft, user-changeable limits are set to 512K bytes for stack (DFLSSIZ)
+and 6M bytes for the data segment (DFLDSIZ) by default;
+these may be increased up to the hard limit
+with the \fIsetrlimit\fP\|(2) system call.
+The data and stack segment size hard limits are set by a system configuration
+option to one of 17M, 33M or 64M bytes.
+One of these sizes is chosen based on the definition of MAXDSIZ;
+with no option, the limit is 17M bytes; with an option
+\fBoptions MAXDSIZ="(32*1024*1024)"\fP (or any value between 17M and 33M),
+the limit is increased to 33M bytes, and values larger than 33M
+result in a limit of 64M bytes.
+You must be careful in doing this that you have adequate paging space.
+As normally configured , the system has 16M or 32M bytes per paging area,
+depending on disk size.
+The best way to get more space is to provide multiple, thereby
+interleaved, paging areas.
+Increasing the virtual memory limits results in interleaving of
+swap space in larger sections (from 500K bytes to 1M or 2M bytes).
+.PP
+By default, the virtual memory system allocates enough memory
+for system page tables mapping user page tables
+to allow 256 megabytes of simultaneous active virtual memory.
+That is, the sum of the virtual memory sizes of all (completely- or partially-)
+resident processes can not exceed this limit.
+If the limit is exceeded, some process(es) must be swapped out.
+To increase the amount of resident virtual space possible,
+you can alter the constant USRPTSIZE (in
+/sys/vax/vmparam.h).
+Each page of system page tables allows 8 megabytes of user virtual memory.
+.PP
+Because the file system block numbers are stored in
+page table \fIpg_blkno\fP
+entries, the maximum size of a file system is limited to
+2^24 1024 byte blocks. Thus no file system can be larger than 8 gigabytes.
+.PP
+The number of mountable file systems is set at 20 by the definition
+of NMOUNT in /sys/h/param.h.
+This should be sufficient; if not, the value can be increased up to 255.
+If you have many disks, it makes sense to make some of
+them single file systems, and the paging areas don't count in this total.
+.PP
+The limit to the number of files that a process may have open simultaneously
+is set to 64.
+This limit is set by the NOFILE definition in /sys/h/param.h.
+It may be increased arbitrarily, with the caveat that the user structure
+expands by 5 bytes for each file, and thus UPAGES (/sys/vax/machparam.h)
+must be increased accordingly.
+.PP
+The amount of physical memory is currently limited to 64 Mb
+by the size of the index fields in the core-map (/sys/h/cmap.h).
+The limit may be increased by following instructions in that file
+to enlarge those fields.
diff --git a/usr.sbin/config/SMM.doc/e.t b/usr.sbin/config/SMM.doc/e.t
new file mode 100644
index 0000000..0a9505b
--- /dev/null
+++ b/usr.sbin/config/SMM.doc/e.t
@@ -0,0 +1,114 @@
+.\" Copyright (c) 1983, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)e.t 8.1 (Berkeley) 6/8/93
+.\"
+.\".ds RH "Network configuration options
+.bp
+.LG
+.B
+.ce
+APPENDIX E. NETWORK CONFIGURATION OPTIONS
+.sp
+.R
+.NL
+.PP
+The network support in the kernel is self-configuring
+according to the protocol support options (INET and NS) and the network
+hardware discovered during autoconfiguration.
+There are several changes that may be made to customize network behavior
+due to local restrictions.
+Within the Internet protocol routines, the following options
+set in the system configuration file are supported:
+.IP \fBGATEWAY\fP
+.br
+The machine is to be used as a gateway.
+This option currently makes only minor changes.
+First, the size of the network routing hash table is increased.
+Secondly, machines that have only a single hardware network interface
+will not forward IP packets; without this option, they will also refrain
+from sending any error indication to the source of unforwardable packets.
+Gateways with only a single interface are assumed to have missing
+or broken interfaces, and will return ICMP unreachable errors to hosts
+sending them packets to be forwarded.
+.IP \fBTCP_COMPAT_42\fP
+.br
+This option forces the system to limit its initial TCP sequence numbers
+to positive numbers.
+Without this option, 4.4BSD systems may have problems with TCP connections
+to 4.2BSD systems that connect but never transfer data.
+The problem is a bug in the 4.2BSD TCP.
+.IP \fBIPFORWARDING\fP
+.br
+Normally, 4.4BSD machines with multiple network interfaces
+will forward IP packets received that should be resent to another host.
+If the line ``options IPFORWARDING="0"'' is in the system configuration
+file, IP packet forwarding will be disabled.
+.IP \fBIPSENDREDIRECTS\fP
+.br
+When forwarding IP packets, 4.4BSD IP will note when a packet is forwarded
+using the same interface on which it arrived.
+When this is noted, if the source machine is on the directly-attached
+network, an ICMP redirect is sent to the source host.
+If the packet was forwarded using a route to a host or to a subnet,
+a host redirect is sent, otherwise a network redirect is sent.
+The generation of redirects may be inhibited with the configuration
+option ``options IPSENDREDIRECTS="0".''
+.br
+.IP \fBSUBNETSARELOCAL\fP
+TCP calculates a maximum segment size to use for each connection,
+and sends no datagrams larger than that size.
+This size will be no larger than that supported on the outgoing
+interface.
+Furthermore, if the destination is not on the local network,
+the size will be no larger than 576 bytes.
+For this test, other subnets of a directly-connected subnetted
+network are considered to be local unless the line
+``options SUBNETSARELOCAL="0"'' is used in the system configuration file.
+.LP
+The following options are supported by the Xerox NS protocols:
+.IP \fBNSIP\fP
+.br
+This option allows NS IDP datagrams to be encapsulated in Internet IP
+packets for transmission to a collaborating NSIP host.
+This may be used to pass IDP packets through IP-only link layer networks.
+See
+.IR nsip (4P)
+for details.
+.IP \fBTHREEWAYSHAKE\fP
+.br
+The NS Sequenced Packet Protocol does not require a three-way handshake
+before considering a connection to be in the established state.
+(A three-way handshake consists of a connection request, an acknowledgement
+of the request along with a symmetrical opening indication,
+and then an acknowledgement of the reciprocal opening packet.)
+This option forces a three-way handshake before data may be transmitted
+on Sequenced Packet sockets.
diff --git a/usr.sbin/config/SMM.doc/spell.ok b/usr.sbin/config/SMM.doc/spell.ok
new file mode 100644
index 0000000..50c4ef2
--- /dev/null
+++ b/usr.sbin/config/SMM.doc/spell.ok
@@ -0,0 +1,306 @@
+ACC
+ANSEL
+ARP
+Autoconfiguration
+BUFPAGES
+CANTWAIT
+CH
+COMPAT
+CSS
+Co
+Config
+Config''SMM:2
+DCLR
+DFLDSIZ
+DFLSSIZ
+DFUNNY
+DHAHA
+DMA
+Dev
+Dquot
+ECC
+EMULEX
+Emulex
+Ethernet
+FPNUMBER
+FUNNY,HAHA
+HAVEBDP
+ICMP
+IDP
+IE
+INET
+IP
+IPC
+IPFORWARDING
+IPL
+IPSENDREDIRECTS
+Info
+Karels
+LH
+Leffler
+MAKEDEV
+MAKEDEV.local
+MASSBUS
+MAXDSIZ
+MAXTSIZ
+Makefile
+Mb
+MicroVAX
+Mkopt
+Mkoption
+NBUF
+NEED16
+NEEDBDP
+NINODE
+NMOUNT
+NOFILE
+NPROC
+NS
+NSC
+NSIP
+NUP
+PST
+RCS
+RDY
+RH
+RK07
+RK611
+SCCS
+SITENAME
+SMM:2
+SUBNETSARELOCAL
+SYSPTSIZE
+TCP
+THREEWAYSHAKE
+Timezone
+UCBVAX
+UDP
+UNIBUS
+UPAGES
+UPCS2
+USRPTSIZE
+VAX
+VAX630
+VAX730
+VAX750
+VAX780
+VAX8600
+VAXWELL
+VAXen
+Vax
+Vaxwell
+acc0
+accrint
+accxint
+addr
+arg
+args
+assym.s
+autoconfiguration
+autoconfigure
+autoconfigured
+backpointer
+badaddr
+blkno
+br
+br5
+buf
+bufpages
+buses
+caddr
+callout
+catchall
+cmap.h
+cmd
+conf
+conf.c
+config
+csr
+ct.c
+ctlr
+cvec
+datagrams
+define''s
+dev
+devices.machine
+dgo
+dh.c
+dh0
+dh1
+dh2
+dhreg.h
+dhrint
+dhxint
+dinfo
+dk
+dk.h
+dm0
+dmintr
+dname
+dquot
+dst
+dumpdev
+dumplo
+dumpmagic
+dumpsize
+dz.c
+dz0
+dzrint
+dzxint
+ec0
+eccollide
+ecrint
+ecxint
+endif
+es
+files.machine
+filesystem
+foo
+foo.c
+genkernel
+gettimeofday
+gigabytes
+gprof
+hardwired
+hd
+hk
+hk0
+hkkernel
+howmany
+hp0
+hp0b
+hp1
+hp2
+hp3
+hpkernel
+ht0
+hz
+ident
+ifdef
+ifndef
+il0
+ilcint
+ilrint
+info
+intr
+ioconf.c
+kgmon
+linterrs
+loopback
+machdep.c
+machparam.h
+makefile
+makelinks
+makeoptions
+maxusers
+mba
+mba0
+mba1
+mbuf.h
+mcount.c
+memsize
+minfo
+mname
+moniker
+mspw
+nbuf
+ncallout
+nclist
+ndquot
+ndrive
+netimp
+netinet
+netns
+netstat
+nexi
+nexus
+nfile
+ninode
+nmbclusters
+nnn.ddd
+nproc
+nquota
+nsip
+ntext
+optionlist
+param.c
+param.h
+pathnames
+pg
+physaddr
+pty
+rc
+reg
+rk.c
+rk0
+rk1
+rkintr
+savecore
+sc
+sc0
+sc1
+scdriver
+setrlimit
+sizeof
+softc
+source.c
+subr
+swapxxx.c
+sysname
+te0
+te1
+timezone
+tm0
+tmintr
+tu0
+uba
+uba.c
+uba0
+ubago
+uballoc
+ubamem
+ubanum
+ubareg.h
+ubarelse
+ubavar.h
+ubglue.s
+ubinfo
+ud
+ui
+um
+up.c
+up0
+up1
+up2
+upaddr
+upattach
+upba
+upcs1
+upcs2
+updevice
+updgo
+updinfo
+updtab
+upintr
+upip
+upmaptype
+upminfo
+upprobe
+upslave
+upstd
+upkernel
+upwatch
+upwstart
+value,name2
+value2
+vax
+vaxif
+vaxmba
+vaxuba
+vmparam.h
+kernel
+wildcard
+wildcarded
+wildcarding
+xclu
+xxx
diff --git a/usr.sbin/config/config.8 b/usr.sbin/config/config.8
new file mode 100644
index 0000000..2cd8699
--- /dev/null
+++ b/usr.sbin/config/config.8
@@ -0,0 +1,186 @@
+.\" Copyright (c) 1980, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)config.8 8.2 (Berkeley) 4/19/94
+.\"
+.Dd April 19, 1994
+.Dt CONFIG 8
+.Os BSD 4
+.Sh NAME
+.Nm config
+.Nd build system configuration files
+.Sh SYNOPSIS
+.Nm config
+.Op Fl gpn
+.Ar SYSTEM_NAME
+.Sh DESCRIPTION
+This is the old version of the
+.Nm
+program.
+It understands the old autoconfiguration scheme
+used on the HP300, i386, DECstation, and derivative platforms.
+The new version of config is used with the
+SPARC platform.
+Only the version of
+.Nm
+applicable to the architecture that you are running
+will be installed on your machine.
+.Pp
+.Nm Config
+builds a set of system configuration files from the file
+.Ar SYSTEM_NAME
+which describes
+the system to configure.
+A second file
+tells
+.Nm
+what files are needed to generate a system and
+can be augmented by configuration specific set of files
+that give alternate files for a specific machine
+(see the
+.Sx FILES
+section below).
+.Pp
+Available options and operands:
+.Pp
+.Bl -tag -width SYSTEM_NAME
+.It Fl g
+Configure a system for debugging.
+.It Fl p
+Configure a system for profiling; for example,
+.Xr kgmon 8
+and
+.Xr gprof 1 .
+If two or more
+.Fl p
+options are supplied,
+.Nm
+will configure a system for high resolution profiling.
+.It Fl n
+Do not remove the old compile directory (see below).
+.It Ar SYSTEM_NAME
+Specifies the name of the system configuration file
+containing device specifications, configuration options
+and other system parameters for one system configuration.
+.El
+.Pp
+.Nm Config
+should be run from the
+.Pa conf
+subdirectory of the system source (usually
+.Pa /sys/ARCH/conf ) .
+.Nm Config
+will create the directory
+.Pa ../../compile/SYSTEM_NAME
+as necessary and place all output files there.
+If the directory already exists, it will be removed
+first unless the
+.Fl n
+flag was specified or the environment variable
+.Ev NO_CONFIG_CLOBBER
+is set.
+The output of
+.Nm
+consists of a number of files; for the
+.Tn i386 ,
+they are:
+.Pa ioconf.c ,
+a description
+of what I/O devices are attached to the system;
+.Pa vector.h ,
+definitions of
+macros related to counting interrupts;
+.Pa Makefile ,
+used by
+.Xr make 1
+in building the system;
+header files,
+definitions of
+the number of various devices that will be compiled into the system;
+so-called swap configuration files,
+definitions for
+the disk areas to be used for the root file system
+and system dumps.
+.Pp
+After running
+.Nm config ,
+it is necessary to run
+.Dq Li make depend
+in the directory where the new makefile
+was created.
+.Nm Config
+prints a reminder of this when it completes.
+.Pp
+If any other error messages are produced by
+.Nm config ,
+the problems in the configuration file should be corrected and
+.Nm
+should be run again.
+Attempts to compile a system that had configuration errors
+are likely to fail.
+.Pp
+If the option "INCLUDE_CONFIG_FILE" is used in the configuration file the
+entire input file is embedded in the new kernel. This means that
+.Xr strings 1
+can be used to extract it from a kernel.
+.Pp
+Strings | grep ___ will print just the configure information.
+.Sh FILES
+.Bl -tag -width /sys/i386/conf/Makefile.i386 -compact
+.It Pa /sys/conf/files
+list of common files system is built from
+.It Pa /sys/i386/conf/Makefile.i386
+generic makefile for the
+.Tn i386
+.It Pa /sys/i386/conf/files.i386
+list of
+.Tn i386
+specific files
+.It Pa /sys/i386/conf/devices.i386
+name to major device mapping file for the
+.Tn i386
+.It Pa /sys/i386/conf/files. Ns Em ERNIE
+list of files specific to
+.Em ERNIE
+system
+.El
+.Sh SEE ALSO
+The SYNOPSIS portion of each device in section 4.
+.Rs
+.%T "Building 4.3 BSD UNIX System with Config"
+.Re
+.Sh BUGS
+The line numbers reported in error messages are usually off by one.
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 4.1 .
diff --git a/usr.sbin/config/config.h b/usr.sbin/config/config.h
new file mode 100644
index 0000000..d72448d
--- /dev/null
+++ b/usr.sbin/config/config.h
@@ -0,0 +1,232 @@
+/*
+ * Copyright (c) 1980, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)config.h 8.1 (Berkeley) 6/6/93
+ */
+
+/*
+ * Config.
+ */
+#include <sys/types.h>
+#include <stdlib.h>
+#include <string.h>
+
+#define NODEV ((dev_t)-1)
+
+struct file_list {
+ struct file_list *f_next;
+ char *f_fn; /* the name */
+ int f_type; /* type or count */
+ u_char f_flags; /* see below */
+ char *f_special; /* special make rule if present */
+ char *f_depends; /* additional dependancies */
+ char *f_clean; /* File list to add to clean rule */
+ char *f_needs;
+ /*
+ * Random values:
+ * swap space parameters for swap areas
+ * root device, etc. for system specifications
+ */
+ union {
+ struct { /* when swap specification */
+ dev_t fuw_swapdev;
+ int fuw_swapsize;
+ int fuw_swapflag;
+ } fuw;
+ struct { /* when system specification */
+ dev_t fus_rootdev;
+ dev_t fus_dumpdev;
+ } fus;
+ struct { /* when component dev specification */
+ dev_t fup_compdev;
+ int fup_compinfo;
+ } fup;
+ } fun;
+#define f_swapdev fun.fuw.fuw_swapdev
+#define f_swapsize fun.fuw.fuw_swapsize
+#define f_swapflag fun.fuw.fuw_swapflag
+#define f_rootdev fun.fus.fus_rootdev
+#define f_dumpdev fun.fus.fus_dumpdev
+#define f_compdev fun.fup.fup_compdev
+#define f_compinfo fun.fup.fup_compinfo
+};
+
+/*
+ * Types.
+ */
+#define DRIVER 1
+#define NORMAL 2
+#define INVISIBLE 3
+#define PROFILING 4
+#define SYSTEMSPEC 5
+#define SWAPSPEC 6
+#define COMPDEVICE 7
+#define COMPSPEC 8
+#define DEVDONE 0x80000000
+#define TYPEMASK 0x7fffffff
+
+/*
+ * Attributes (flags).
+ */
+#define CONFIGDEP 1
+#define NO_IMPLCT_RULE 2
+#define NO_OBJ 4
+#define BEFORE_DEPEND 8
+
+struct idlst {
+ char *id;
+ struct idlst *id_next;
+};
+
+struct device {
+ int d_type; /* CONTROLLER, DEVICE, bus adaptor */
+ struct device *d_conn; /* what it is connected to */
+ char *d_name; /* name of device (e.g. rk11) */
+ struct idlst *d_vec; /* interrupt vectors */
+ int d_pri; /* interrupt priority */
+ int d_addr; /* address of csr */
+ int d_unit; /* unit number */
+ int d_drive; /* drive number */
+ int d_target; /* target number */
+ int d_lun; /* unit number */
+ int d_slave; /* slave number */
+#define QUES -1 /* -1 means '?' */
+#define UNKNOWN -2 /* -2 means not set yet */
+ int d_dk; /* if init 1 set to number for iostat */
+ int d_flags; /* flags for device init */
+ int d_conflicts; /* I'm allowed to conflict */
+ int d_disabled; /* nonzero to skip probe/attach */
+ char *d_port; /* io port base manifest constant */
+ int d_portn; /* io port base (if number not manifest) */
+ char *d_mask; /* interrupt mask */
+ int d_maddr; /* io memory base */
+ int d_msize; /* io memory size */
+ int d_drq; /* DMA request */
+ int d_irq; /* interrupt request */
+ struct device *d_next; /* Next one in list */
+};
+#define TO_NEXUS (struct device *)-1
+#define TO_VBA (struct device *)-2
+
+struct config {
+ char *c_dev;
+ char *s_sysname;
+};
+
+/*
+ * Config has a global notion of which machine type is
+ * being used. It uses the name of the machine in choosing
+ * files and directories. Thus if the name of the machine is ``vax'',
+ * it will build from ``Makefile.vax'' and use ``../vax/inline''
+ * in the makerules, etc.
+ */
+int machine;
+char *machinename;
+#define MACHINE_VAX 1
+#define MACHINE_TAHOE 2
+#define MACHINE_HP300 3
+#define MACHINE_I386 4
+#define MACHINE_MIPS 5
+#define MACHINE_PMAX 6
+#define MACHINE_LUNA68K 7
+#define MACHINE_NEWS3400 8
+
+/*
+ * 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;
+
+char *ident;
+char *ns();
+char *tc();
+char *qu();
+char *get_word();
+char *get_quoted_word();
+char *path();
+char *raise();
+void moveifchanged();
+
+int do_trace;
+
+#if MACHINE_VAX
+int seen_mba, seen_uba;
+#endif
+#if MACHINE_TAHOE
+int seen_vba;
+#endif
+#if MACHINE_I386
+int seen_isa;
+int seen_scbus;
+#endif
+int seen_cd;
+
+struct device *connect();
+struct device *dtab;
+dev_t nametodev();
+char *devtoname();
+
+char errbuf[80];
+int yyline;
+
+struct file_list *ftab, *conf_list, **confp, *comp_list, **compp;
+
+int profiling;
+int debugging;
+
+int maxusers;
+u_int loadaddress;
+
+extern int old_config_present; /* Old config/build directory still there */
+
+#define eq(a,b) (!strcmp(a,b))
diff --git a/usr.sbin/config/config.y b/usr.sbin/config/config.y
new file mode 100644
index 0000000..268f6bc
--- /dev/null
+++ b/usr.sbin/config/config.y
@@ -0,0 +1,1139 @@
+%union {
+ char *str;
+ int val;
+ struct file_list *file;
+ struct idlst *lst;
+}
+
+%token AND
+%token ANY
+%token ARGS
+%token AT
+%token BIO
+%token BUS
+%token CAM
+%token COMMA
+%token CONFIG
+%token CONFLICTS
+%token CONTROLLER
+%token CPU
+%token CSR
+%token DEVICE
+%token DISABLE
+%token DISK
+%token DRIVE
+%token DRQ
+%token DUMPS
+%token EQUALS
+%token FLAGS
+%token IDENT
+%token INTERLEAVE
+%token IOMEM
+%token IOSIZ
+%token IRQ
+%token MACHINE
+%token MAJOR
+%token MASTER
+%token MAXUSERS
+%token MINOR
+%token MINUS
+%token NET
+%token NEXUS
+%token ON
+%token OPTIONS
+%token MAKEOPTIONS
+%token PORT
+%token PRIORITY
+%token PSEUDO_DEVICE
+%token ROOT
+%token SEMICOLON
+%token SEQUENTIAL
+%token SIZE
+%token SLAVE
+%token SWAP
+%token TARGET
+%token TTY
+%token TRACE
+%token UNIT
+%token VECTOR
+
+%token <str> ID
+%token <val> NUMBER
+%token <val> FPNUMBER
+
+%type <str> Save_id
+%type <str> Opt_value
+%type <str> Dev
+%type <lst> Id_list
+%type <val> optional_size
+%type <val> optional_sflag
+%type <str> device_name
+%type <val> major_minor
+%type <val> arg_device_spec
+%type <val> root_device_spec root_device_specs
+%type <val> dump_device_spec
+%type <file> swap_device_spec
+%type <file> comp_device_spec
+
+%{
+
+/*
+ * Copyright (c) 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)config.y 8.1 (Berkeley) 6/6/93
+ */
+
+#include "config.h"
+
+#include <sys/disklabel.h>
+#include <sys/diskslice.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <stdio.h>
+#include <string.h>
+
+struct device cur;
+struct device *curp = 0;
+
+#define ns(s) strdup(s)
+
+int alreadychecked __P((dev_t, dev_t[], dev_t *));
+void deverror __P((char *, char *));
+int finddev __P((dev_t));
+void init_dev __P((struct device *));
+void verifycomp __P((struct file_list *));
+
+%}
+%%
+Configuration:
+ Many_specs
+ = { verifysystemspecs(); }
+ ;
+
+Many_specs:
+ Many_specs Spec
+ |
+ /* lambda */
+ ;
+
+Spec:
+ Device_spec SEMICOLON
+ = { newdev(&cur); } |
+ Config_spec SEMICOLON
+ |
+ TRACE SEMICOLON
+ = { do_trace = !do_trace; } |
+ SEMICOLON
+ |
+ error SEMICOLON
+ ;
+
+Config_spec:
+ MACHINE Save_id
+ = {
+ if (!strcmp($2, "vax")) {
+ machine = MACHINE_VAX;
+ machinename = "vax";
+ } else if (!strcmp($2, "tahoe")) {
+ machine = MACHINE_TAHOE;
+ machinename = "tahoe";
+ } else if (!strcmp($2, "hp300")) {
+ machine = MACHINE_HP300;
+ machinename = "hp300";
+ } else if (!strcmp($2, "i386")) {
+ machine = MACHINE_I386;
+ machinename = "i386";
+ } else if (!strcmp($2, "mips")) {
+ machine = MACHINE_MIPS;
+ machinename = "mips";
+ } else if (!strcmp($2, "pmax")) {
+ machine = MACHINE_PMAX;
+ machinename = "pmax";
+ } else if (!strcmp($2, "luna68k")) {
+ machine = MACHINE_LUNA68K;
+ machinename = "luna68k";
+ } else if (!strcmp($2, "news3400")) {
+ machine = MACHINE_NEWS3400;
+ machinename = "news3400";
+ } else
+ yyerror("Unknown machine type");
+ } |
+ CPU Save_id
+ = {
+ struct cputype *cp =
+ (struct cputype *)malloc(sizeof (struct cputype));
+ memset(cp, 0, sizeof(*cp));
+ cp->cpu_name = $2;
+ cp->cpu_next = cputype;
+ cputype = cp;
+ } |
+ OPTIONS Opt_list
+ |
+ MAKEOPTIONS Mkopt_list
+ |
+ IDENT ID
+ = { ident = $2; } |
+ System_spec
+ |
+ MAXUSERS NUMBER
+ = { maxusers = $2; };
+
+System_spec:
+ System_id System_parameter_list
+ = { checksystemspec(*confp); }
+ ;
+
+System_id:
+ CONFIG Save_id
+ = { mkconf($2); }
+ ;
+
+System_parameter_list:
+ System_parameter_list System_parameter
+ | System_parameter
+ ;
+
+System_parameter:
+ addr_spec
+ | swap_spec
+ | root_spec
+ | dump_spec
+ | arg_spec
+ ;
+
+addr_spec:
+ AT NUMBER
+ = { loadaddress = $2; }
+ ;
+
+swap_spec:
+ SWAP optional_on swap_device_list
+ ;
+
+swap_device_list:
+ swap_device_list AND swap_device
+ | swap_device
+ ;
+
+swap_device:
+ swap_device_spec optional_size optional_sflag
+ = { mkswap(*confp, $1, $2, $3); }
+ ;
+
+swap_device_spec:
+ device_name
+ = {
+ struct file_list *fl = newflist(SWAPSPEC);
+
+ if (eq($1, "generic"))
+ fl->f_fn = $1;
+ else {
+ fl->f_swapdev = nametodev($1, 0,
+ COMPATIBILITY_SLICE, 'b');
+ fl->f_fn = devtoname(fl->f_swapdev);
+ }
+ $$ = fl;
+ }
+ | major_minor
+ = {
+ struct file_list *fl = newflist(SWAPSPEC);
+
+ fl->f_swapdev = $1;
+ fl->f_fn = devtoname($1);
+ $$ = fl;
+ }
+ ;
+
+root_spec:
+ ROOT optional_on root_device_specs
+ = {
+ struct file_list *fl = *confp;
+
+ if (fl && fl->f_rootdev != NODEV)
+ yyerror("extraneous root device specification");
+ else
+ fl->f_rootdev = $3;
+ }
+ ;
+
+root_device_specs:
+ root_device_spec AND root_device_specs
+ = {
+ warnx("extraneous root devices ignored");
+ $$ = $1;
+ }
+ | root_device_spec
+ ;
+
+root_device_spec:
+ device_name
+ = { $$ = nametodev($1, 0, COMPATIBILITY_SLICE, 'a'); }
+ | major_minor
+ ;
+
+dump_spec:
+ DUMPS optional_on dump_device_spec
+ = {
+ struct file_list *fl = *confp;
+
+ if (fl && fl->f_dumpdev != NODEV)
+ yyerror("extraneous dump device specification");
+ else
+ fl->f_dumpdev = $3;
+ }
+
+ ;
+
+dump_device_spec:
+ device_name
+ = { $$ = nametodev($1, 0, COMPATIBILITY_SLICE, 'b'); }
+ | major_minor
+ ;
+
+arg_spec:
+ ARGS optional_on arg_device_spec
+ = { yyerror("arg device specification obsolete, ignored"); }
+ ;
+
+arg_device_spec:
+ device_name
+ = { $$ = nametodev($1, 0, COMPATIBILITY_SLICE, 'b'); }
+ | major_minor
+ ;
+
+major_minor:
+ MAJOR NUMBER MINOR NUMBER
+ = { $$ = makedev($2, $4); }
+ ;
+
+optional_on:
+ ON
+ | /* empty */
+ ;
+
+optional_size:
+ SIZE NUMBER
+ = { $$ = $2; }
+ | /* empty */
+ = { $$ = 0; }
+ ;
+
+optional_sflag:
+ SEQUENTIAL
+ = { $$ = 2; }
+ | /* empty */
+ = { $$ = 0; }
+ ;
+
+device_name:
+ Save_id
+ = { $$ = $1; }
+ | Save_id NUMBER
+ = {
+ char buf[80];
+
+ (void) snprintf(buf, 80, "%s%d", $1, $2);
+ $$ = ns(buf); free($1);
+ }
+ | Save_id NUMBER ID
+ = {
+ char buf[80];
+
+ (void) snprintf(buf, 80, "%s%d%s", $1, $2, $3);
+ $$ = ns(buf); free($1);
+ }
+ | Save_id NUMBER ID NUMBER
+ = {
+ char buf[80];
+
+ (void) snprintf(buf, 80, "%s%d%s%d", $1, $2, $3, $4);
+ $$ = ns(buf); free($1);
+ }
+ | Save_id NUMBER ID NUMBER ID
+ = {
+ char buf[80];
+
+ (void) snprintf(buf, 80, "%s%d%s%d%s",
+ $1, $2, $3, $4, $5);
+ $$ = ns(buf); free($1);
+ }
+ ;
+
+Opt_list:
+ Opt_list COMMA Option
+ |
+ Option
+ ;
+
+Option:
+ Save_id
+ = {
+ struct opt *op = (struct opt *)malloc(sizeof (struct opt));
+ char *s;
+ memset(op, 0, sizeof(*op));
+ op->op_name = $1;
+ op->op_next = opt;
+ op->op_value = 0;
+ opt = op;
+ if ((s = strchr(op->op_name, '='))) {
+ /* AARGH!!!! Old-style bogon */
+ *s = '\0';
+ op->op_value = ns(s + 1);
+ }
+ } |
+ Save_id EQUALS Opt_value
+ = {
+ struct opt *op = (struct opt *)malloc(sizeof (struct opt));
+ memset(op, 0, sizeof(*op));
+ op->op_name = $1;
+ op->op_next = opt;
+ op->op_value = $3;
+ opt = op;
+ } ;
+
+Opt_value:
+ ID
+ = { $$ = $1; } |
+ NUMBER
+ = {
+ char nb[16];
+ (void) sprintf(nb, "%d", $1);
+ $$ = ns(nb);
+ } ;
+
+
+Save_id:
+ ID
+ = { $$ = $1; }
+ ;
+
+Mkopt_list:
+ Mkopt_list COMMA Mkoption
+ |
+ Mkoption
+ ;
+
+Mkoption:
+ Save_id EQUALS Opt_value
+ = {
+ struct opt *op = (struct opt *)malloc(sizeof (struct opt));
+ memset(op, 0, sizeof(*op));
+ op->op_name = $1;
+ op->op_ownfile = 0; /* for now */
+ op->op_next = mkopt;
+ op->op_value = $3;
+ mkopt = op;
+ } ;
+
+Dev:
+ ID
+ = { $$ = $1; }
+ ;
+
+Device_spec:
+ DEVICE Dev_name Dev_info Int_spec
+ = { cur.d_type = DEVICE; } |
+ MASTER Dev_name Dev_info Int_spec
+ = { cur.d_type = MASTER; } |
+ DISK Dev_name Dev_info Int_spec
+ = { cur.d_dk = 1; cur.d_type = DEVICE; } |
+ CONTROLLER Dev_name Dev_info Int_spec
+ = { cur.d_type = CONTROLLER; } |
+ PSEUDO_DEVICE Init_dev Dev
+ = {
+ cur.d_name = $3;
+ cur.d_type = PSEUDO_DEVICE;
+ } |
+ PSEUDO_DEVICE Init_dev Dev NUMBER
+ = {
+ cur.d_name = $3;
+ cur.d_type = PSEUDO_DEVICE;
+ cur.d_slave = $4;
+ } |
+ PSEUDO_DEVICE Dev_name Cdev_init Cdev_info
+ = {
+ if (!eq(cur.d_name, "cd"))
+ yyerror("improper spec for pseudo-device");
+ seen_cd = 1;
+ cur.d_type = DEVICE;
+ verifycomp(*compp);
+ };
+
+Cdev_init:
+ /* lambda */
+ = { mkcomp(&cur); };
+
+Cdev_info:
+ optional_on comp_device_list comp_option_list
+ ;
+
+comp_device_list:
+ comp_device_list AND comp_device
+ | comp_device
+ ;
+
+comp_device:
+ comp_device_spec
+ = { addcomp(*compp, $1); }
+ ;
+
+comp_device_spec:
+ device_name
+ = {
+ struct file_list *fl = newflist(COMPSPEC);
+
+ fl->f_compdev = nametodev($1, 0, COMPATIBILITY_SLICE,
+ 'c');
+ fl->f_fn = devtoname(fl->f_compdev);
+ $$ = fl;
+ }
+ | major_minor
+ = {
+ struct file_list *fl = newflist(COMPSPEC);
+
+ fl->f_compdev = $1;
+ fl->f_fn = devtoname($1);
+ $$ = fl;
+ }
+ ;
+
+comp_option_list:
+ comp_option_list comp_option
+ |
+ /* lambda */
+ ;
+
+comp_option:
+ INTERLEAVE NUMBER
+ = { cur.d_pri = $2; } |
+ FLAGS NUMBER
+ = { cur.d_flags = $2; };
+
+Dev_name:
+ Init_dev Dev NUMBER
+ = {
+ cur.d_name = $2;
+ if (eq($2, "mba"))
+ seen_mba = 1;
+ else if (eq($2, "uba"))
+ seen_uba = 1;
+ else if (eq($2, "vba"))
+ seen_vba = 1;
+ else if (eq($2, "isa"))
+ seen_isa = 1;
+ else if (eq($2, "scbus"))
+ seen_scbus = 1;
+ cur.d_unit = $3;
+ };
+
+Init_dev:
+ /* lambda */
+ = { init_dev(&cur); };
+
+Dev_info:
+ Con_info Info_list
+ |
+ /* lambda */
+ ;
+
+Con_info:
+ AT Dev NUMBER
+ = {
+ if (eq(cur.d_name, "mba") || eq(cur.d_name, "uba")) {
+ (void) sprintf(errbuf,
+ "%s must be connected to a nexus", cur.d_name);
+ yyerror(errbuf);
+ }
+ cur.d_conn = connect($2, $3);
+ } |
+ AT NEXUS NUMBER
+ = { check_nexus(&cur, $3); cur.d_conn = TO_NEXUS; };
+
+Info_list:
+ Info_list Info
+ |
+ /* lambda */
+ ;
+
+Info:
+ CSR NUMBER
+ = { cur.d_addr = $2; } |
+ BUS NUMBER
+ = {
+ if (cur.d_conn != 0 && cur.d_conn->d_type == CONTROLLER)
+ cur.d_slave = $2;
+ else
+ yyerror("can't specify a bus to something "
+ "other than a controller");
+ } |
+ TARGET NUMBER
+ = { cur.d_target = $2; } |
+ UNIT NUMBER
+ = { cur.d_lun = $2; } |
+ DRIVE NUMBER
+ = { cur.d_drive = $2; } |
+ SLAVE NUMBER
+ = {
+ if (cur.d_conn != 0 && cur.d_conn != TO_NEXUS &&
+ cur.d_conn->d_type == MASTER)
+ cur.d_slave = $2;
+ else
+ yyerror("can't specify slave--not to master");
+ } |
+ IRQ NUMBER
+ = { cur.d_irq = $2; } |
+ DRQ NUMBER
+ = { cur.d_drq = $2; } |
+ IOMEM NUMBER
+ = { cur.d_maddr = $2; } |
+ IOSIZ NUMBER
+ = { cur.d_msize = $2; } |
+ PORT device_name
+ = { cur.d_port = $2; } |
+ PORT NUMBER
+ = { cur.d_portn = $2; } |
+ TTY
+ = { cur.d_mask = "tty"; } |
+ BIO
+ = { cur.d_mask = "bio"; } |
+ CAM
+ = { cur.d_mask = "cam"; } |
+ NET
+ = { cur.d_mask = "net"; } |
+ FLAGS NUMBER
+ = { cur.d_flags = $2; } |
+ DISABLE
+ = { cur.d_disabled = 1; } |
+ CONFLICTS
+ = { cur.d_conflicts = 1; };
+
+Int_spec:
+ VECTOR Id_list
+ = { cur.d_vec = $2; } |
+ PRIORITY NUMBER
+ = { cur.d_pri = $2; } |
+ /* lambda */
+ ;
+
+Id_list:
+ Save_id
+ = {
+ struct idlst *a = (struct idlst *)malloc(sizeof(struct idlst));
+ memset(a, 0, sizeof(*a));
+ a->id = $1; a->id_next = 0; $$ = a;
+ } |
+ Save_id Id_list =
+ {
+ struct idlst *a = (struct idlst *)malloc(sizeof(struct idlst));
+ memset(a, 0, sizeof(*a));
+ a->id = $1; a->id_next = $2; $$ = a;
+ };
+
+%%
+
+void
+yyerror(s)
+ char *s;
+{
+
+ warnx("line %d: %s", yyline + 1, s);
+}
+
+/*
+ * add a device to the list of devices
+ */
+void
+newdev(dp)
+ register struct device *dp;
+{
+ register struct device *np;
+
+ np = (struct device *) malloc(sizeof *np);
+ memset(np, 0, sizeof(*np));
+ *np = *dp;
+ np->d_next = 0;
+ if (curp == 0)
+ dtab = np;
+ else
+ curp->d_next = np;
+ curp = np;
+}
+
+/*
+ * note that a configuration should be made
+ */
+void
+mkconf(sysname)
+ char *sysname;
+{
+ register struct file_list *fl, **flp;
+
+ fl = (struct file_list *) malloc(sizeof *fl);
+ memset(fl, 0, sizeof(*fl));
+ fl->f_type = SYSTEMSPEC;
+ fl->f_needs = sysname;
+ fl->f_rootdev = NODEV;
+ fl->f_dumpdev = NODEV;
+ fl->f_fn = 0;
+ fl->f_next = 0;
+ for (flp = confp; *flp; flp = &(*flp)->f_next)
+ ;
+ *flp = fl;
+ confp = flp;
+}
+
+struct file_list *
+newflist(ftype)
+ u_char ftype;
+{
+ struct file_list *fl = (struct file_list *)malloc(sizeof (*fl));
+ memset(fl, 0, sizeof(*fl));
+
+ fl->f_type = ftype;
+ fl->f_next = 0;
+ fl->f_swapdev = NODEV;
+ fl->f_swapsize = 0;
+ fl->f_needs = 0;
+ fl->f_fn = 0;
+ return (fl);
+}
+
+/*
+ * Add a swap device to the system's configuration
+ */
+void
+mkswap(system, fl, size, flag)
+ struct file_list *system, *fl;
+ int size, flag;
+{
+ register struct file_list **flp;
+
+ if (system == 0 || system->f_type != SYSTEMSPEC) {
+ yyerror("\"swap\" spec precedes \"config\" specification");
+ return;
+ }
+ if (size < 0) {
+ yyerror("illegal swap partition size");
+ return;
+ }
+ /*
+ * Append swap description to the end of the list.
+ */
+ flp = &system->f_next;
+ for (; *flp && (*flp)->f_type == SWAPSPEC; flp = &(*flp)->f_next)
+ ;
+ fl->f_next = *flp;
+ *flp = fl;
+ fl->f_swapsize = size;
+ fl->f_swapflag = flag;
+ /*
+ * If first swap device for this system,
+ * set up f_fn field to insure swap
+ * files are created with unique names.
+ */
+ if (system->f_fn)
+ return;
+ if (eq(fl->f_fn, "generic"))
+ system->f_fn = ns(fl->f_fn);
+ else
+ system->f_fn = ns(system->f_needs);
+}
+
+void
+mkcomp(dp)
+ register struct device *dp;
+{
+ register struct file_list *fl, **flp;
+ char buf[80];
+
+ fl = (struct file_list *) malloc(sizeof *fl);
+ memset(fl, 0, sizeof(*fl));
+ fl->f_type = COMPDEVICE;
+ fl->f_compinfo = dp->d_unit;
+ fl->f_fn = ns(dp->d_name);
+ (void) sprintf(buf, "%s%d", dp->d_name, dp->d_unit);
+ fl->f_needs = ns(buf);
+ fl->f_next = 0;
+ for (flp = compp; *flp; flp = &(*flp)->f_next)
+ ;
+ *flp = fl;
+ compp = flp;
+}
+
+void
+addcomp(compdev, fl)
+ struct file_list *compdev, *fl;
+{
+ register struct file_list **flp;
+
+ if (compdev == 0 || compdev->f_type != COMPDEVICE) {
+ yyerror("component spec precedes device specification");
+ return;
+ }
+ /*
+ * Append description to the end of the list.
+ */
+ flp = &compdev->f_next;
+ for (; *flp && (*flp)->f_type == COMPSPEC; flp = &(*flp)->f_next)
+ ;
+ fl->f_next = *flp;
+ *flp = fl;
+}
+
+/*
+ * find the pointer to connect to the given device and number.
+ * returns 0 if no such device and prints an error message
+ */
+struct device *
+connect(dev, num)
+ register char *dev;
+ register int num;
+{
+ register struct device *dp;
+ struct device *huhcon();
+
+ if (num == QUES)
+ return (huhcon(dev));
+ for (dp = dtab; dp != 0; dp = dp->d_next) {
+ if ((num != dp->d_unit) || !eq(dev, dp->d_name))
+ continue;
+ if (dp->d_type != CONTROLLER && dp->d_type != MASTER) {
+ (void) sprintf(errbuf,
+ "%s connected to non-controller", dev);
+ yyerror(errbuf);
+ return (0);
+ }
+ return (dp);
+ }
+ (void) sprintf(errbuf, "%s %d not defined", dev, num);
+ yyerror(errbuf);
+ return (0);
+}
+
+/*
+ * connect to an unspecific thing
+ */
+struct device *
+huhcon(dev)
+ register char *dev;
+{
+ register struct device *dp, *dcp;
+ struct device rdev;
+ int oldtype;
+
+ /*
+ * First make certain that there are some of these to wildcard on
+ */
+ for (dp = dtab; dp != 0; dp = dp->d_next)
+ if (eq(dp->d_name, dev))
+ break;
+ if (dp == 0) {
+ (void) sprintf(errbuf, "no %s's to wildcard", dev);
+ yyerror(errbuf);
+ return (0);
+ }
+ oldtype = dp->d_type;
+ dcp = dp->d_conn;
+ /*
+ * Now see if there is already a wildcard entry for this device
+ * (e.g. Search for a "uba ?")
+ */
+ for (; dp != 0; dp = dp->d_next)
+ if (eq(dev, dp->d_name) && dp->d_unit == -1)
+ break;
+ /*
+ * If there isn't, make one because everything needs to be connected
+ * to something.
+ */
+ if (dp == 0) {
+ dp = &rdev;
+ init_dev(dp);
+ dp->d_unit = QUES;
+ dp->d_name = ns(dev);
+ dp->d_type = oldtype;
+ newdev(dp);
+ dp = curp;
+ /*
+ * Connect it to the same thing that other similar things are
+ * connected to, but make sure it is a wildcard unit
+ * (e.g. up connected to sc ?, here we make connect sc? to a
+ * uba?). If other things like this are on the NEXUS or
+ * if they aren't connected to anything, then make the same
+ * connection, else call ourself to connect to another
+ * unspecific device.
+ */
+ if (dcp == TO_NEXUS || dcp == 0)
+ dp->d_conn = dcp;
+ else
+ dp->d_conn = connect(dcp->d_name, QUES);
+ }
+ return (dp);
+}
+
+void
+init_dev(dp)
+ register struct device *dp;
+{
+
+ dp->d_name = "OHNO!!!";
+ dp->d_type = DEVICE;
+ dp->d_conn = 0;
+ dp->d_conflicts = 0;
+ dp->d_disabled = 0;
+ dp->d_vec = 0;
+ dp->d_addr = dp->d_flags = dp->d_dk = 0;
+ dp->d_pri = -1;
+ dp->d_slave = dp->d_lun = dp->d_target = dp->d_drive = dp->d_unit = UNKNOWN;
+ dp->d_port = (char *)0;
+ dp->d_portn = -1;
+ dp->d_irq = -1;
+ dp->d_drq = -1;
+ dp->d_maddr = 0;
+ dp->d_msize = 0;
+ dp->d_mask = "null";
+}
+
+/*
+ * make certain that this is a reasonable type of thing to connect to a nexus
+ */
+void
+check_nexus(dev, num)
+ register struct device *dev;
+ int num;
+{
+
+ switch (machine) {
+
+ case MACHINE_VAX:
+ if (!eq(dev->d_name, "uba") && !eq(dev->d_name, "mba") &&
+ !eq(dev->d_name, "bi"))
+ yyerror("only uba's, mba's, and bi's should be connected to the nexus");
+ if (num != QUES)
+ yyerror("can't give specific nexus numbers");
+ break;
+
+ case MACHINE_TAHOE:
+ if (!eq(dev->d_name, "vba"))
+ yyerror("only vba's should be connected to the nexus");
+ break;
+
+ case MACHINE_HP300:
+ case MACHINE_LUNA68K:
+ if (num != QUES)
+ dev->d_addr = num;
+ break;
+
+ case MACHINE_I386:
+ if (!eq(dev->d_name, "isa"))
+ yyerror("only isa's should be connected to the nexus");
+ break;
+
+ case MACHINE_NEWS3400:
+ if (!eq(dev->d_name, "iop") && !eq(dev->d_name, "hb") &&
+ !eq(dev->d_name, "vme"))
+ yyerror("only iop's, hb's and vme's should be connected to the nexus");
+ break;
+ }
+}
+
+/*
+ * Check system specification and apply defaulting
+ * rules on root, argument, dump, and swap devices.
+ */
+void
+checksystemspec(fl)
+ register struct file_list *fl;
+{
+ char buf[BUFSIZ];
+ register struct file_list *swap;
+ int generic;
+
+ if (fl == 0 || fl->f_type != SYSTEMSPEC) {
+ yyerror("internal error, bad system specification");
+ exit(1);
+ }
+ swap = fl->f_next;
+ generic = swap && swap->f_type == SWAPSPEC && eq(swap->f_fn, "generic");
+ if (fl->f_rootdev == NODEV && !generic) {
+ yyerror("no root device specified");
+ exit(1);
+ }
+ /*
+ * Default swap area to be in 'b' partition of root's
+ * device. If root specified to be other than on 'a'
+ * partition, give warning, something probably amiss.
+ */
+ if (swap == 0 || swap->f_type != SWAPSPEC) {
+ dev_t dev;
+
+ swap = newflist(SWAPSPEC);
+ dev = fl->f_rootdev;
+ if (dkpart(dev) != 0) {
+ (void) sprintf(buf,
+"Warning, swap defaulted to 'b' partition with root on '%c' partition",
+ dkpart(dev) + 'a');
+ yyerror(buf);
+ }
+ swap->f_swapdev = dkmodpart(dev, SWAP_PART);
+ swap->f_fn = devtoname(swap->f_swapdev);
+ mkswap(fl, swap, 0);
+ }
+ /*
+ * Make sure a generic swap isn't specified, along with
+ * other stuff (user must really be confused).
+ */
+ if (generic) {
+ if (fl->f_rootdev != NODEV)
+ yyerror("root device specified with generic swap");
+ if (fl->f_dumpdev != NODEV)
+ yyerror("dump device specified with generic swap");
+ return;
+ }
+ /*
+ * Warn if dump device is not a swap area.
+ */
+ if (fl->f_dumpdev != NODEV && fl->f_dumpdev != swap->f_swapdev) {
+ struct file_list *p = swap->f_next;
+
+ for (; p && p->f_type == SWAPSPEC; p = p->f_next)
+ if (fl->f_dumpdev == p->f_swapdev)
+ return;
+ (void) sprintf(buf,
+ "Warning: dump device is not a swap partition");
+ yyerror(buf);
+ }
+}
+
+/*
+ * Verify all devices specified in the system specification
+ * are present in the device specifications.
+ */
+void
+verifysystemspecs()
+{
+ register struct file_list *fl;
+ dev_t checked[50], *verifyswap();
+ register dev_t *pchecked = checked;
+
+ for (fl = conf_list; fl; fl = fl->f_next) {
+ if (fl->f_type != SYSTEMSPEC)
+ continue;
+ if (!finddev(fl->f_rootdev))
+ deverror(fl->f_needs, "root");
+ *pchecked++ = fl->f_rootdev;
+ pchecked = verifyswap(fl->f_next, checked, pchecked);
+ if (!alreadychecked(fl->f_dumpdev, checked, pchecked)) {
+ if (!finddev(fl->f_dumpdev))
+ deverror(fl->f_needs, "dump");
+ *pchecked++ = fl->f_dumpdev;
+ }
+ }
+}
+
+/*
+ * Do as above, but for swap devices.
+ */
+dev_t *
+verifyswap(fl, checked, pchecked)
+ register struct file_list *fl;
+ dev_t checked[];
+ register dev_t *pchecked;
+{
+
+ for (;fl && fl->f_type == SWAPSPEC; fl = fl->f_next) {
+ if (eq(fl->f_fn, "generic"))
+ continue;
+ if (alreadychecked(fl->f_swapdev, checked, pchecked))
+ continue;
+ if (!finddev(fl->f_swapdev))
+ warnx("swap device %s not configured", fl->f_fn);
+ *pchecked++ = fl->f_swapdev;
+ }
+ return (pchecked);
+}
+
+/*
+ * Verify that components of a compound device have themselves been config'ed
+ */
+void
+verifycomp(fl)
+ register struct file_list *fl;
+{
+ char *dname = fl->f_needs;
+
+ for (fl = fl->f_next; fl; fl = fl->f_next) {
+ if (fl->f_type != COMPSPEC || finddev(fl->f_compdev))
+ continue;
+ warnx("%s: component device %s not configured", dname, fl->f_needs);
+ }
+}
+
+/*
+ * Has a device already been checked
+ * for its existence in the configuration?
+ */
+int
+alreadychecked(dev, list, last)
+ dev_t dev, list[];
+ register dev_t *last;
+{
+ register dev_t *p;
+
+ for (p = list; p < last; p++)
+ if (dkmodpart(*p, 0) != dkmodpart(dev, 0))
+ return (1);
+ return (0);
+}
+
+void
+deverror(systemname, devtype)
+ char *systemname, *devtype;
+{
+
+ warnx("%s: %s device not configured", systemname, devtype);
+}
+
+/*
+ * Look for the device in the list of
+ * configured hardware devices. Must
+ * take into account stuff wildcarded.
+ */
+/*ARGSUSED*/
+int
+finddev(dev)
+ dev_t dev;
+{
+
+ /* punt on this right now */
+ return (1);
+}
diff --git a/usr.sbin/config/configvers.h b/usr.sbin/config/configvers.h
new file mode 100644
index 0000000..1c74a87
--- /dev/null
+++ b/usr.sbin/config/configvers.h
@@ -0,0 +1,11 @@
+/*
+ * 6 digits of version. The most significant are branch indicators
+ * (eg: RELENG_2_2 = 22, -current presently = 30 etc). The least
+ * significant digits are incremented for each incompatable change.
+ *
+ * The numbering scheme is inspired by the sys/conf/newvers.sh RELDATE
+ * and <osreldate.h> system.
+ *
+ * $Id: configvers.h,v 1.1 1997/10/22 00:38:48 peter Exp $
+ */
+#define CONFIGVERS 300003
diff --git a/usr.sbin/config/lang.l b/usr.sbin/config/lang.l
new file mode 100644
index 0000000..5e0cd21
--- /dev/null
+++ b/usr.sbin/config/lang.l
@@ -0,0 +1,236 @@
+%{
+/*-
+ * Copyright (c) 1980, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)lang.l 8.1 (Berkeley) 6/6/93
+ */
+
+#include <ctype.h>
+#include <string.h>
+#include "y.tab.h"
+#include "config.h"
+
+#define tprintf if (do_trace) printf
+
+/*
+ * Key word table
+ */
+
+struct kt {
+ char *kt_name;
+ int kt_val;
+} key_words[] = {
+ { "and", AND },
+ { "args", ARGS },
+ { "at", AT },
+#if MACHINE_I386
+ { "bio", BIO },
+ { "bus", BUS },
+ { "cam", CAM},
+ { "conflicts", CONFLICTS },
+#endif
+ { "config", CONFIG },
+ { "controller", CONTROLLER },
+ { "cpu", CPU },
+ { "csr", CSR },
+ { "device", DEVICE },
+#if MACHINE_I386
+ { "disable", DISABLE },
+#endif
+ { "disk", DISK },
+ { "drive", DRIVE },
+#if MACHINE_I386
+ { "drq", DRQ },
+#endif
+ { "dumps", DUMPS },
+ { "flags", FLAGS },
+ { "ident", IDENT },
+ { "interleave", INTERLEAVE },
+#if MACHINE_I386
+ { "iomem", IOMEM },
+ { "iosiz", IOSIZ },
+ { "irq", IRQ },
+#endif
+ { "machine", MACHINE },
+ { "major", MAJOR },
+ { "makeoptions", MAKEOPTIONS },
+ { "master", MASTER },
+ { "maxusers", MAXUSERS },
+ { "minor", MINOR },
+#if MACHINE_I386
+ { "net", NET },
+#endif
+ { "nexus", NEXUS },
+ { "on", ON },
+ { "options", OPTIONS },
+#if MACHINE_I386
+ { "port", PORT },
+#endif
+ { "priority", PRIORITY },
+ { "pseudo-device",PSEUDO_DEVICE },
+ { "root", ROOT },
+#if MACHINE_HP300 || MACHINE_LUNA68K
+ { "scode", NEXUS },
+#endif
+ { "sequential", SEQUENTIAL },
+ { "size", SIZE },
+ { "slave", SLAVE },
+ { "swap", SWAP },
+ { "tape", DEVICE },
+ { "target", TARGET },
+#if MACHINE_I386
+ { "tty", TTY },
+#endif
+ { "trace", TRACE },
+ { "unit", UNIT },
+ { "vector", VECTOR },
+ { 0, 0 },
+};
+
+
+int kw_lookup __P((char *));
+int octal __P((char *));
+int hex __P((char *));
+
+%}
+WORD [A-Za-z_][-A-Za-z_]*
+%%
+{WORD} {
+ int i;
+
+ if ((i = kw_lookup(yytext)) == -1)
+ {
+ yylval.str = strdup(yytext);
+ tprintf("id(%s) ", yytext);
+ return ID;
+ }
+ tprintf("(%s) ", yytext);
+ return i;
+ }
+\\\"[^"]+\\\" {
+ yytext[strlen(yytext)-2] = '"';
+ yytext[strlen(yytext)-1] = '\0';
+ yylval.str = strdup(yytext + 1);
+ return ID;
+ }
+\"[^"]+\" {
+ yytext[strlen(yytext)-1] = '\0';
+ yylval.str = strdup(yytext + 1);
+ return ID;
+ }
+0[0-7]* {
+ yylval.val = octal(yytext);
+ tprintf("#O:%o ", yylval.val);
+ return NUMBER;
+ }
+0x[0-9a-fA-F]+ {
+ yylval.val = hex(yytext);
+ tprintf("#X:%x ", yylval.val);
+ return NUMBER;
+ }
+[1-9][0-9]* {
+ yylval.val = atoi(yytext);
+ tprintf("#D:%d ", yylval.val);
+ return NUMBER;
+ }
+[0-9]"."[0-9]* {
+ double atof();
+ yylval.val = (int) (60 * atof(yytext) + 0.5);
+ return FPNUMBER;
+ }
+"-" {
+ return MINUS;
+ }
+"?" {
+ yylval.val = -1;
+ tprintf("? ");
+ return NUMBER;
+ }
+\n/[ \t] {
+ yyline++;
+ tprintf("\n... ");
+ }
+\n {
+ yyline++;
+ tprintf("\n");
+ return SEMICOLON;
+ }
+#.* { /* Ignored (comment) */; }
+[ \t\f]* { /* Ignored (white space) */; }
+";" { return SEMICOLON; }
+"," { return COMMA; }
+"=" { return EQUALS; }
+"@" { return AT; }
+. { return yytext[0]; }
+
+%%
+/*
+ * kw_lookup
+ * Look up a string in the keyword table. Returns a -1 if the
+ * string is not a keyword otherwise it returns the keyword number
+ */
+
+int
+kw_lookup(word)
+register char *word;
+{
+ register struct kt *kp;
+
+ for (kp = key_words; kp->kt_name != 0; kp++)
+ if (eq(word, kp->kt_name))
+ return kp->kt_val;
+ return -1;
+}
+
+/*
+ * Number conversion routines
+ */
+
+int
+octal(str)
+char *str;
+{
+ int num;
+
+ (void) sscanf(str, "%o", &num);
+ return num;
+}
+
+int
+hex(str)
+char *str;
+{
+ int num;
+
+ (void) sscanf(str+2, "%x", &num);
+ return num;
+}
diff --git a/usr.sbin/config/main.c b/usr.sbin/config/main.c
new file mode 100644
index 0000000..06aa355
--- /dev/null
+++ b/usr.sbin/config/main.c
@@ -0,0 +1,436 @@
+/*
+ * Copyright (c) 1980, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 1980, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)main.c 8.1 (Berkeley) 6/6/93";
+#endif
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/file.h>
+#include <sys/mman.h>
+#include <ctype.h>
+#include <err.h>
+#include <stdio.h>
+#include <sysexits.h>
+#include <unistd.h>
+#include "y.tab.h"
+#include "config.h"
+
+#ifndef TRUE
+#define TRUE (1)
+#endif
+
+#ifndef FALSE
+#define FALSE (0)
+#endif
+
+static char *PREFIX;
+static int no_config_clobber = FALSE;
+int old_config_present;
+
+static void usage __P((void));
+void configfile __P((void));
+
+/*
+ * Config builds a set of files for building a UNIX
+ * system given a description of the desired system.
+ */
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+
+ struct stat buf;
+ int ch;
+ char *p;
+
+ while ((ch = getopt(argc, argv, "gpn")) != -1)
+ switch (ch) {
+ case 'g':
+ debugging++;
+ break;
+ case 'p':
+ profiling++;
+ break;
+ case 'n':
+ no_config_clobber = TRUE;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc != 1)
+ usage();
+
+ if (freopen(PREFIX = *argv, "r", stdin) == NULL)
+ err(2, "%s", PREFIX);
+ if (getenv("NO_CONFIG_CLOBBER"))
+ no_config_clobber = TRUE;
+
+ 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);
+ }
+#ifndef NO_CLOBBER_EVER
+ else if (!no_config_clobber) {
+ char tmp[strlen(p) + 8];
+
+ fprintf(stderr, "Removing old directory %s: ", p);
+ fflush(stderr);
+ sprintf(tmp, "rm -rf %s", p);
+ if (system(tmp)) {
+ fprintf(stderr, "Failed!\n");
+ err(2, "%s", tmp);
+ }
+ fprintf(stderr, "Done.\n");
+ if (mkdir(p, 0777))
+ err(2, "%s", p);
+ }
+#endif
+ else
+ old_config_present++;
+
+ loadaddress = -1;
+ dtab = NULL;
+ confp = &conf_list;
+ compp = &comp_list;
+ if (yyparse())
+ exit(3);
+ switch (machine) {
+
+ case MACHINE_VAX:
+ vax_ioconf(); /* Print ioconf.c */
+ ubglue(); /* Create ubglue.s */
+ break;
+
+ case MACHINE_TAHOE:
+ tahoe_ioconf();
+ vbglue();
+ break;
+
+ case MACHINE_HP300:
+ case MACHINE_LUNA68K:
+ hp300_ioconf();
+ hpglue();
+ break;
+
+ case MACHINE_I386:
+ i386_ioconf(); /* Print ioconf.c */
+ vector(); /* Create vector.s */
+ break;
+
+ case MACHINE_MIPS:
+ case MACHINE_PMAX:
+ pmax_ioconf();
+ break;
+
+ case MACHINE_NEWS3400:
+ news_ioconf();
+ break;
+
+ default:
+ printf("Specify machine type, e.g. ``machine vax''\n");
+ exit(1);
+ }
+ /*
+ * make symbolic links in compilation directory
+ * for "sys" (to make genassym.c work along with #include <sys/xxx>)
+ * and similarly for "machine".
+ */
+ {
+ char xxx[80];
+
+ (void) sprintf(xxx, "../../%s/include", machinename);
+ (void) symlink(xxx, path("machine"));
+ }
+ options(); /* make options .h files */
+ makefile(); /* build Makefile */
+ headers(); /* make a lot of .h files */
+ swapconf(); /* swap config files */
+ configfile(); /* put config file into kernel*/
+ printf("Kernel build directory is %s\n", p);
+ exit(0);
+}
+
+static void
+usage()
+{
+ fprintf(stderr, "usage: config [-gpn] sysname\n");
+ exit(1);
+}
+
+/*
+ * get_word
+ * returns EOF on end of file
+ * NULL on end of line
+ * pointer to the word otherwise
+ */
+char *
+get_word(fp)
+ register FILE *fp;
+{
+ static char line[80];
+ register int ch;
+ register char *cp;
+ int escaped_nl = 0;
+
+begin:
+ while ((ch = getc(fp)) != EOF)
+ if (ch != ' ' && ch != '\t')
+ break;
+ if (ch == EOF)
+ return ((char *)EOF);
+ if (ch == '\\'){
+ escaped_nl = 1;
+ goto begin;
+ }
+ if (ch == '\n')
+ if (escaped_nl){
+ escaped_nl = 0;
+ goto begin;
+ }
+ else
+ return (NULL);
+ cp = line;
+ *cp++ = ch;
+ while ((ch = getc(fp)) != EOF) {
+ if (isspace(ch))
+ break;
+ *cp++ = ch;
+ }
+ *cp = 0;
+ if (ch == EOF)
+ return ((char *)EOF);
+ (void) ungetc(ch, fp);
+ return (line);
+}
+
+/*
+ * get_quoted_word
+ * like get_word but will accept something in double or single quotes
+ * (to allow embedded spaces).
+ */
+char *
+get_quoted_word(fp)
+ register FILE *fp;
+{
+ static char line[256];
+ register int ch;
+ register char *cp;
+ int escaped_nl = 0;
+
+begin:
+ while ((ch = getc(fp)) != EOF)
+ if (ch != ' ' && ch != '\t')
+ break;
+ if (ch == EOF)
+ return ((char *)EOF);
+ if (ch == '\\'){
+ escaped_nl = 1;
+ goto begin;
+ }
+ if (ch == '\n')
+ if (escaped_nl){
+ escaped_nl = 0;
+ goto begin;
+ }
+ else
+ return (NULL);
+ cp = line;
+ if (ch == '"' || ch == '\'') {
+ register int quote = ch;
+
+ while ((ch = getc(fp)) != EOF) {
+ if (ch == quote)
+ break;
+ if (ch == '\n') {
+ *cp = 0;
+ printf("config: missing quote reading `%s'\n",
+ line);
+ exit(2);
+ }
+ *cp++ = ch;
+ }
+ } else {
+ *cp++ = ch;
+ while ((ch = getc(fp)) != EOF) {
+ if (isspace(ch))
+ break;
+ *cp++ = ch;
+ }
+ if (ch != EOF)
+ (void) ungetc(ch, fp);
+ }
+ *cp = 0;
+ if (ch == EOF)
+ return ((char *)EOF);
+ return (line);
+}
+
+/*
+ * prepend the path to a filename
+ */
+char *
+path(file)
+ char *file;
+{
+ register char *cp;
+
+#define CDIR "../../compile/"
+ cp = malloc((unsigned int)(sizeof(CDIR) + strlen(PREFIX) +
+ (file ? strlen(file) : 0) + 2));
+ (void) strcpy(cp, CDIR);
+ (void) strcat(cp, PREFIX);
+ if (file) {
+ (void) strcat(cp, "/");
+ (void) strcat(cp, file);
+ }
+ return (cp);
+}
+
+void
+configfile()
+{
+ FILE *fi, *fo;
+ char *p;
+ int i;
+
+ fi = fopen(PREFIX,"r");
+ if(!fi)
+ err(2, "%s", PREFIX);
+ fo = fopen(p=path("config.c.new"),"w");
+ if(!fo)
+ err(2, "%s", p);
+ fprintf(fo,"#include \"opt_config.h\"\n");
+ fprintf(fo,"#ifdef INCLUDE_CONFIG_FILE \n");
+ fprintf(fo,"static char *config = \"\n");
+ fprintf(fo,"START CONFIG FILE %s\n___",PREFIX);
+ while (EOF != (i=getc(fi))) {
+ if(i == '\n') {
+ fprintf(fo,"\n___");
+ } else if(i == '\"') {
+ fprintf(fo,"\\\"");
+ } else if(i == '\\') {
+ fprintf(fo,"\\\\");
+ } else {
+ putc(i,fo);
+ }
+ }
+ fprintf(fo,"\nEND CONFIG FILE %s\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);
+ 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, %s)", from_name);
+ }
+
+#ifdef DIAG
+ if (changed)
+ printf("CHANGED! rename (%s, %s)\n", from_name, to_name);
+ else
+ printf("SAME! unlink (%s)\n", from_name);
+#endif
+
+ return;
+}
diff --git a/usr.sbin/config/mkglue.c b/usr.sbin/config/mkglue.c
new file mode 100644
index 0000000..bbf0da0
--- /dev/null
+++ b/usr.sbin/config/mkglue.c
@@ -0,0 +1,411 @@
+/*
+ * Copyright (c) 1980, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)mkglue.c 8.1 (Berkeley) 6/6/93";
+#endif
+static const char rcsid[] =
+ "$Id: mkglue.c,v 1.12 1997/09/15 06:37:08 charnier Exp $";
+#endif /* not lint */
+
+/*
+ * Make the bus adaptor interrupt glue files.
+ */
+#include <ctype.h>
+#include <err.h>
+#include <stdio.h>
+#include "config.h"
+#include "y.tab.h"
+
+void vector_devtab __P((FILE *, char *, int *));
+void vector __P((void));
+void dump_ctrs __P((FILE *));
+void dump_intname __P((FILE *, char *, int));
+void dump_std __P((FILE *, FILE *));
+void dump_vbavec __P((FILE *, char *, int));
+void dump_ubavec __P((FILE *, char *, int));
+
+/*
+ * Create the UNIBUS interrupt vector glue file.
+ */
+void
+ubglue()
+{
+ register FILE *fp, *gp;
+ register struct device *dp, *mp;
+
+ fp = fopen(path("ubglue.s"), "w");
+ if (fp == 0)
+ err(1, "%s", path("ubglue.s"));
+ gp = fopen(path("ubvec.s"), "w");
+ if (gp == 0)
+ err(1, "%s", path("ubvec.s"));
+ for (dp = dtab; dp != 0; dp = dp->d_next) {
+ mp = dp->d_conn;
+ if (mp != 0 && mp != (struct device *)-1 &&
+ !eq(mp->d_name, "mba")) {
+ struct idlst *id, *id2;
+
+ for (id = dp->d_vec; id; id = id->id_next) {
+ for (id2 = dp->d_vec; id2; id2 = id2->id_next) {
+ if (id2 == id) {
+ dump_ubavec(fp, id->id,
+ dp->d_unit);
+ break;
+ }
+ if (!strcmp(id->id, id2->id))
+ break;
+ }
+ }
+ }
+ }
+ dump_std(fp, gp);
+ for (dp = dtab; dp != 0; dp = dp->d_next) {
+ mp = dp->d_conn;
+ if (mp != 0 && mp != (struct device *)-1 &&
+ !eq(mp->d_name, "mba")) {
+ struct idlst *id, *id2;
+
+ for (id = dp->d_vec; id; id = id->id_next) {
+ for (id2 = dp->d_vec; id2; id2 = id2->id_next) {
+ if (id2 == id) {
+ dump_intname(fp, id->id,
+ dp->d_unit);
+ break;
+ }
+ if (!strcmp(id->id, id2->id))
+ break;
+ }
+ }
+ }
+ }
+ dump_ctrs(fp);
+ (void) fclose(fp);
+ (void) fclose(gp);
+}
+
+static int cntcnt = 0; /* number of interrupt counters allocated */
+
+/*
+ * Print a UNIBUS interrupt vector.
+ */
+void
+dump_ubavec(fp, vector, number)
+ register FILE *fp;
+ char *vector;
+ int number;
+{
+ char nbuf[80];
+ register char *v = nbuf;
+
+ (void) sprintf(v, "%s%d", vector, number);
+ fprintf(fp, "\t.globl\t_X%s\n\t.align\t2\n_X%s:\n\tpushr\t$0x3f\n",
+ v, v);
+ fprintf(fp, "\tincl\t_fltintrcnt+(4*%d)\n", cntcnt++);
+ if (strncmp(vector, "dzx", 3) == 0)
+ fprintf(fp, "\tmovl\t$%d,r0\n\tjmp\tdzdma\n\n", number);
+ else if (strncmp(vector, "dpx", 3) == 0)
+ fprintf(fp, "\tmovl\t$%d,r0\n\tjmp\tdpxdma\n\n", number);
+ else if (strncmp(vector, "dpr", 3) == 0)
+ fprintf(fp, "\tmovl\t$%d,r0\n\tjmp\tdprdma\n\n", number);
+ else {
+ if (strncmp(vector, "uur", 3) == 0) {
+ fprintf(fp, "#ifdef UUDMA\n");
+ fprintf(fp, "\tmovl\t$%d,r0\n\tjsb\tuudma\n", number);
+ fprintf(fp, "#endif\n");
+ }
+ fprintf(fp, "\tpushl\t$%d\n", number);
+ fprintf(fp, "\tcalls\t$1,_%s\n\tpopr\t$0x3f\n", vector);
+ fprintf(fp, "\tincl\t_cnt+V_INTR\n\trei\n\n");
+ }
+}
+
+/*
+ * Create the VERSAbus interrupt vector glue file.
+ */
+void
+vbglue()
+{
+ register FILE *fp, *gp;
+ register struct device *dp, *mp;
+
+ fp = fopen(path("vbglue.s"), "w");
+ if (fp == 0)
+ err(1, "%s", path("vbglue.s"));
+ gp = fopen(path("vbvec.s"), "w");
+ if (gp == 0)
+ err(1, "%s", path("vbvec.s"));
+ for (dp = dtab; dp != 0; dp = dp->d_next) {
+ struct idlst *id, *id2;
+
+ mp = dp->d_conn;
+ if (mp == 0 || mp == (struct device *)-1 ||
+ eq(mp->d_name, "mba"))
+ continue;
+ for (id = dp->d_vec; id; id = id->id_next)
+ for (id2 = dp->d_vec; id2; id2 = id2->id_next) {
+ if (id == id2) {
+ dump_vbavec(fp, id->id, dp->d_unit);
+ break;
+ }
+ if (eq(id->id, id2->id))
+ break;
+ }
+ }
+ dump_std(fp, gp);
+ for (dp = dtab; dp != 0; dp = dp->d_next) {
+ mp = dp->d_conn;
+ if (mp != 0 && mp != (struct device *)-1 &&
+ !eq(mp->d_name, "mba")) {
+ struct idlst *id, *id2;
+
+ for (id = dp->d_vec; id; id = id->id_next) {
+ for (id2 = dp->d_vec; id2; id2 = id2->id_next) {
+ if (id2 == id) {
+ dump_intname(fp, id->id,
+ dp->d_unit);
+ break;
+ }
+ if (eq(id->id, id2->id))
+ break;
+ }
+ }
+ }
+ }
+ dump_ctrs(fp);
+ (void) fclose(fp);
+ (void) fclose(gp);
+}
+
+/*
+ * Print a VERSAbus interrupt vector
+ */
+void
+dump_vbavec(fp, vector, number)
+ register FILE *fp;
+ char *vector;
+ int number;
+{
+ char nbuf[80];
+ register char *v = nbuf;
+
+ (void) sprintf(v, "%s%d", vector, number);
+ fprintf(fp, "SCBVEC(%s):\n", v);
+ fprintf(fp, "\tCHECK_SFE(4)\n");
+ fprintf(fp, "\tSAVE_FPSTAT(4)\n");
+ fprintf(fp, "\tPUSHR\n");
+ fprintf(fp, "\tincl\t_fltintrcnt+(4*%d)\n", cntcnt++);
+ fprintf(fp, "\tpushl\t$%d\n", number);
+ fprintf(fp, "\tcallf\t$8,_%s\n", vector);
+ fprintf(fp, "\tincl\t_cnt+V_INTR\n");
+ fprintf(fp, "\tPOPR\n");
+ fprintf(fp, "\tREST_FPSTAT\n");
+ fprintf(fp, "\trei\n\n");
+}
+
+/*
+ * HP9000/300 interrupts are auto-vectored.
+ * Code is hardwired in locore.s
+ */
+void
+hpglue() {}
+
+static char *vaxinames[] = {
+ "clock", "cnr", "cnx", "tur", "tux",
+ "mba0", "mba1", "mba2", "mba3",
+ "uba0", "uba1", "uba2", "uba3"
+};
+static char *tahoeinames[] = {
+ "clock", "cnr", "cnx", "rmtr", "rmtx", "buserr",
+};
+static struct stdintrs {
+ char **si_names; /* list of standard interrupt names */
+ int si_n; /* number of such names */
+} stdintrs[] = {
+ { vaxinames, sizeof (vaxinames) / sizeof (vaxinames[0]) },
+ { tahoeinames, (sizeof (tahoeinames) / sizeof (tahoeinames[0])) }
+};
+/*
+ * Start the interrupt name table with the names
+ * of the standard vectors not directly associated
+ * with a bus. Also, dump the defines needed to
+ * reference the associated counters into a separate
+ * file which is prepended to locore.s.
+ */
+void
+dump_std(fp, gp)
+ register FILE *fp, *gp;
+{
+ register struct stdintrs *si = &stdintrs[machine-1];
+ register char **cpp;
+ register int i;
+
+ fprintf(fp, "\n\t.globl\t_intrnames\n");
+ fprintf(fp, "\n\t.globl\t_eintrnames\n");
+ fprintf(fp, "\t.data\n");
+ fprintf(fp, "_intrnames:\n");
+ cpp = si->si_names;
+ for (i = 0; i < si->si_n; i++) {
+ register char *cp, *tp;
+ char buf[80];
+
+ cp = *cpp;
+ if (cp[0] == 'i' && cp[1] == 'n' && cp[2] == 't') {
+ cp += 3;
+ if (*cp == 'r')
+ cp++;
+ }
+ for (tp = buf; *cp; cp++)
+ if (islower(*cp))
+ *tp++ = toupper(*cp);
+ else
+ *tp++ = *cp;
+ *tp = '\0';
+ fprintf(gp, "#define\tI_%s\t%d\n", buf, i*sizeof (long));
+ fprintf(fp, "\t.asciz\t\"%s\"\n", *cpp);
+ cpp++;
+ }
+}
+
+void
+dump_intname(fp, vector, number)
+ register FILE *fp;
+ char *vector;
+ int number;
+{
+ register char *cp = vector;
+
+ fprintf(fp, "\t.asciz\t\"");
+ /*
+ * Skip any "int" or "intr" in the name.
+ */
+ while (*cp)
+ if (cp[0] == 'i' && cp[1] == 'n' && cp[2] == 't') {
+ cp += 3;
+ if (*cp == 'r')
+ cp++;
+ } else {
+ putc(*cp, fp);
+ cp++;
+ }
+ fprintf(fp, "%d\"\n", number);
+}
+
+/*
+ * Reserve space for the interrupt counters.
+ */
+void
+dump_ctrs(fp)
+ register FILE *fp;
+{
+ struct stdintrs *si = &stdintrs[machine-1];
+
+ fprintf(fp, "_eintrnames:\n");
+ fprintf(fp, "\n\t.globl\t_intrcnt\n");
+ fprintf(fp, "\n\t.globl\t_eintrcnt\n");
+ fprintf(fp, "\t.align 2\n");
+ fprintf(fp, "_intrcnt:\n");
+ fprintf(fp, "\t.space\t4 * %d\n", si->si_n);
+ fprintf(fp, "_fltintrcnt:\n");
+ fprintf(fp, "\t.space\t4 * %d\n", cntcnt);
+ fprintf(fp, "_eintrcnt:\n\n");
+ fprintf(fp, "\t.text\n");
+}
+
+/*
+ * Create the ISA interrupt vector glue file.
+ *
+ * The interrupt handlers are hardwired into vector.s and are attached
+ * at runtime depending on the data in ioconf.c and on the results of
+ * probing. Here we only need to generate the names of the interrupt
+ * handlers in an ancient form suitable for vmstat (the _eintrcnt label
+ * can't be expressed in C). We give the names of all devices to
+ * simplify the correspondence between devices and interrupt handlers.
+ * The order must match that in mkioconf.c.
+ */
+void
+vector()
+{
+ int dev_id;
+ FILE *fp;
+
+ fp = fopen(path("vector.h.new"), "w");
+ if (fp == NULL)
+ err(1, "%s", path("vector.h.new"));
+ fprintf(fp, "/*\n");
+ fprintf(fp, " * vector.h\n");
+ fprintf(fp, " * Macros for interrupt vector routines\n");
+ fprintf(fp, " * Generated by config program\n");
+ fprintf(fp, " */\n\n");
+ fprintf(fp, "#define\tDEVICE_NAMES \"\\\n");
+ fprintf(fp, "clk0 irqnn\\0\\\n");
+ fprintf(fp, "rtc0 irqnn\\0\\\n");
+ fprintf(fp, "pci irqnn\\0\\\n");
+ fprintf(fp, "pci irqnn\\0\\\n");
+ fprintf(fp, "pci irqnn\\0\\\n");
+ fprintf(fp, "pci irqnn\\0\\\n");
+ fprintf(fp, "ipi irqnn\\0\\\n");
+ fprintf(fp, "ipi irqnn\\0\\\n");
+ fprintf(fp, "ipi irqnn\\0\\\n");
+ fprintf(fp, "ipi irqnn\\0\\\n");
+ dev_id = 10;
+ vector_devtab(fp, "bio", &dev_id);
+ vector_devtab(fp, "tty", &dev_id);
+ vector_devtab(fp, "net", &dev_id);
+ vector_devtab(fp, "cam", &dev_id);
+ vector_devtab(fp, "null", &dev_id);
+ fprintf(fp, "\"\n\n");
+ fprintf(fp, "#define\tNR_DEVICES\t%d\n", dev_id);
+ (void) fclose(fp);
+ moveifchanged(path("vector.h.new"), path("vector.h"));
+}
+
+void
+vector_devtab(fp, table, dev_idp)
+ FILE *fp;
+ char *table;
+ int *dev_idp;
+{
+ register struct device *dp, *mp;
+
+ for (dp = dtab; dp != NULL; dp = dp->d_next) {
+ if (dp->d_unit == QUES || !eq(dp->d_mask, table))
+ continue;
+ mp = dp->d_conn;
+ if (mp == NULL || mp == TO_NEXUS || !eq(mp->d_name, "isa"))
+ continue;
+ fprintf(fp, "%s%d irqnn\\0\\\n", dp->d_name, dp->d_unit);
+ (*dev_idp)++;
+ }
+}
diff --git a/usr.sbin/config/mkheaders.c b/usr.sbin/config/mkheaders.c
new file mode 100644
index 0000000..74e92ee
--- /dev/null
+++ b/usr.sbin/config/mkheaders.c
@@ -0,0 +1,229 @@
+/*
+ * Copyright (c) 1980, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)mkheaders.c 8.1 (Berkeley) 6/6/93";
+#endif
+static const char rcsid[] =
+ "$Id: mkheaders.c,v 1.7 1997/10/28 07:21:02 joerg Exp $";
+#endif /* not lint */
+
+/*
+ * Make all the .h files for the optional entries
+ */
+
+#include <ctype.h>
+#include <err.h>
+#include <stdio.h>
+#include <string.h>
+#include "config.h"
+#include "y.tab.h"
+
+#define ns(s) strdup(s)
+
+void do_header __P((char *, char *, int));
+void do_count __P((char *, char *, int));
+
+void
+headers()
+{
+ register struct file_list *fl;
+ struct device *dp;
+
+ for (fl = ftab; fl != 0; fl = fl->f_next)
+ if (fl->f_needs != 0)
+ do_count(fl->f_needs, fl->f_needs, 1);
+ for (dp = dtab; dp != 0; dp = dp->d_next)
+ if ((dp->d_type & TYPEMASK) == PSEUDO_DEVICE) {
+ if (!(dp->d_type & DEVDONE))
+ printf("Warning: pseudo-device \"%s\" is unknown\n",
+ dp->d_name);
+ else
+ dp->d_type &= TYPEMASK;
+ }
+}
+
+/*
+ * count all the devices of a certain type and recurse to count
+ * whatever the device is connected to
+ */
+void
+do_count(dev, hname, search)
+ register char *dev, *hname;
+ int search;
+{
+ register struct device *dp, *mp;
+ register int count, hicount;
+
+ /*
+ * After this loop, "count" will be the actual number of units,
+ * and "hicount" will be the highest unit declared. do_header()
+ * must use this higher of these values.
+ */
+ for (hicount = count = 0, dp = dtab; dp != 0; dp = dp->d_next)
+ if (dp->d_unit != -1 && eq(dp->d_name, dev)) {
+ if ((dp->d_type & TYPEMASK) == PSEUDO_DEVICE) {
+ count =
+ dp->d_slave != UNKNOWN ? dp->d_slave : 1;
+ dp->d_type |= DEVDONE;
+ break;
+ }
+ count++;
+ /*
+ * Allow holes in unit numbering,
+ * assumption is unit numbering starts
+ * at zero.
+ */
+ if (dp->d_unit + 1 > hicount)
+ hicount = dp->d_unit + 1;
+ if (search) {
+ mp = dp->d_conn;
+ if (mp != 0 && mp != TO_NEXUS &&
+ mp->d_conn != 0 && mp->d_conn != TO_NEXUS) {
+ do_count(mp->d_name, hname, 0);
+ search = 0;
+ }
+ }
+ }
+ do_header(dev, hname, count > hicount ? count : hicount);
+}
+
+void
+do_header(dev, hname, count)
+ char *dev, *hname;
+ int count;
+{
+ char *file, *name, *inw, *toheader(), *tomacro();
+ struct file_list *fl, *fl_head, *tflp;
+ FILE *inf, *outf;
+ int inc, oldcount;
+
+ file = toheader(hname);
+ name = tomacro(dev);
+ inf = fopen(file, "r");
+ oldcount = -1;
+ if (inf == 0) {
+ outf = fopen(file, "w");
+ if (outf == 0)
+ err(1, "%s", file);
+ fprintf(outf, "#define %s %d\n", name, count);
+ (void) fclose(outf);
+ return;
+ }
+ fl_head = NULL;
+ for (;;) {
+ char *cp;
+ if ((inw = get_word(inf)) == 0 || inw == (char *)EOF)
+ break;
+ if ((inw = get_word(inf)) == 0 || inw == (char *)EOF)
+ break;
+ inw = ns(inw);
+ cp = get_word(inf);
+ if (cp == 0 || cp == (char *)EOF)
+ break;
+ inc = atoi(cp);
+ if (eq(inw, name)) {
+ oldcount = inc;
+ inc = count;
+ }
+ cp = get_word(inf);
+ if (cp == (char *)EOF)
+ break;
+ fl = (struct file_list *) malloc(sizeof *fl);
+ bzero(fl, sizeof(*fl));
+ fl->f_fn = inw; /* malloced */
+ fl->f_type = inc;
+ fl->f_next = fl_head;
+ fl_head = fl;
+ }
+ (void) fclose(inf);
+ if (count == oldcount) {
+ for (fl = fl_head; fl != NULL; fl = tflp) {
+ tflp = fl->f_next;
+ free(fl->f_fn);
+ free(fl);
+ }
+ return;
+ }
+ if (oldcount == -1) {
+ fl = (struct file_list *) malloc(sizeof *fl);
+ bzero(fl, sizeof(*fl));
+ fl->f_fn = ns(name);
+ fl->f_type = count;
+ fl->f_next = fl_head;
+ fl_head = fl;
+ }
+ outf = fopen(file, "w");
+ if (outf == 0)
+ err(1, "%s", file);
+ for (fl = fl_head; fl != NULL; fl = tflp) {
+ fprintf(outf,
+ "#define %s %u\n", fl->f_fn, count ? fl->f_type : 0);
+ tflp = fl->f_next;
+ free(fl->f_fn);
+ free(fl);
+ }
+ (void) fclose(outf);
+}
+
+/*
+ * convert a dev name to a .h file name
+ */
+char *
+toheader(dev)
+ char *dev;
+{
+ static char hbuf[80];
+
+ (void) strcpy(hbuf, path(dev));
+ (void) strcat(hbuf, ".h");
+ return (hbuf);
+}
+
+/*
+ * convert a dev name to a macro name
+ */
+char *tomacro(dev)
+ register char *dev;
+{
+ static char mbuf[20];
+ register char *cp;
+
+ cp = mbuf;
+ *cp++ = 'N';
+ while (*dev)
+ *cp++ = islower(*dev) ? toupper(*dev++) : *dev++;
+ *cp++ = 0;
+ return (mbuf);
+}
diff --git a/usr.sbin/config/mkioconf.c b/usr.sbin/config/mkioconf.c
new file mode 100644
index 0000000..425e66c
--- /dev/null
+++ b/usr.sbin/config/mkioconf.c
@@ -0,0 +1,1327 @@
+/*
+ * Copyright (c) 1980, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)mkioconf.c 8.2 (Berkeley) 1/21/94";
+#endif
+static const char rcsid[] =
+ "$Id: mkioconf.c,v 1.32 1997/09/17 06:20:44 charnier Exp $";
+#endif /* not lint */
+
+#include <err.h>
+#include <stdio.h>
+#include "y.tab.h"
+#include "config.h"
+
+/*
+ * build the ioconf.c file
+ */
+char *qu();
+char *intv();
+char *wnum();
+void pseudo_ioconf();
+void comp_config __P((FILE *));
+void scbus_devtab __P((FILE *, FILE *, int *));
+void isa_devtab __P((FILE *, char *, int *));
+void isa_biotab __P((FILE *, char *));
+void i386_ioconf __P((void));
+void hp300_ioconf __P((void));
+int hpbadslave __P((struct device *, struct device *));
+void tahoe_ioconf __P((void));
+void vax_ioconf __P((void));
+
+#if MACHINE_VAX
+void
+vax_ioconf()
+{
+ register struct device *dp, *mp, *np;
+ register int uba_n, slave;
+ FILE *fp;
+
+ fp = fopen(path("ioconf.c"), "w");
+ if (fp == 0)
+ err(1, "%s", path("ioconf.c"));
+ fprintf(fp, "#include <vax/include/pte.h>\n");
+ fprintf(fp, "#include <sys/param.h>\n");
+ fprintf(fp, "#include <sys/buf.h>\n");
+ fprintf(fp, "\n");
+ fprintf(fp, "#include <vax/mba/mbavar.h>\n");
+ fprintf(fp, "#include <vax/uba/ubavar.h>\n\n");
+ fprintf(fp, "\n");
+ fprintf(fp, "#define C (caddr_t)\n\n");
+ /*
+ * First print the mba initialization structures
+ */
+ if (seen_mba) {
+ for (dp = dtab; dp != 0; dp = dp->d_next) {
+ mp = dp->d_conn;
+ if (mp == 0 || mp == TO_NEXUS ||
+ !eq(mp->d_name, "mba"))
+ continue;
+ fprintf(fp, "extern struct mba_driver %sdriver;\n",
+ dp->d_name);
+ }
+ fprintf(fp, "\nstruct mba_device mbdinit[] = {\n");
+ fprintf(fp, "\t/* Device, Unit, Mba, Drive, Dk */\n");
+ for (dp = dtab; dp != 0; dp = dp->d_next) {
+ mp = dp->d_conn;
+ if (dp->d_unit == QUES || mp == 0 ||
+ mp == TO_NEXUS || !eq(mp->d_name, "mba"))
+ continue;
+ if (dp->d_addr) {
+ printf("can't specify csr address on mba for %s%d\n",
+ dp->d_name, dp->d_unit);
+ continue;
+ }
+ if (dp->d_vec != 0) {
+ printf("can't specify vector for %s%d on mba\n",
+ dp->d_name, dp->d_unit);
+ continue;
+ }
+ if (dp->d_drive == UNKNOWN) {
+ printf("drive not specified for %s%d\n",
+ dp->d_name, dp->d_unit);
+ continue;
+ }
+ if (dp->d_slave != UNKNOWN) {
+ printf("can't specify slave number for %s%d\n",
+ dp->d_name, dp->d_unit);
+ continue;
+ }
+ fprintf(fp, "\t{ &%sdriver, %d, %s,",
+ dp->d_name, dp->d_unit, qu(mp->d_unit));
+ fprintf(fp, " %s, %d },\n",
+ qu(dp->d_drive), dp->d_dk);
+ }
+ fprintf(fp, "\t0\n};\n\n");
+ /*
+ * Print the mbsinit structure
+ * Driver Controller Unit Slave
+ */
+ fprintf(fp, "struct mba_slave mbsinit [] = {\n");
+ fprintf(fp, "\t/* Driver, Ctlr, Unit, Slave */\n");
+ for (dp = dtab; dp != 0; dp = dp->d_next) {
+ /*
+ * All slaves are connected to something which
+ * is connected to the massbus.
+ */
+ if ((mp = dp->d_conn) == 0 || mp == TO_NEXUS)
+ continue;
+ np = mp->d_conn;
+ if (np == 0 || np == TO_NEXUS ||
+ !eq(np->d_name, "mba"))
+ continue;
+ fprintf(fp, "\t{ &%sdriver, %s",
+ mp->d_name, qu(mp->d_unit));
+ fprintf(fp, ", %2d, %s },\n",
+ dp->d_unit, qu(dp->d_slave));
+ }
+ fprintf(fp, "\t0\n};\n\n");
+ }
+ /*
+ * Now generate interrupt vectors for the unibus
+ */
+ for (dp = dtab; dp != 0; dp = dp->d_next) {
+ if (dp->d_vec != 0) {
+ struct idlst *ip;
+ mp = dp->d_conn;
+ if (mp == 0 || mp == TO_NEXUS ||
+ (!eq(mp->d_name, "uba") && !eq(mp->d_name, "bi")))
+ continue;
+ fprintf(fp,
+ "extern struct uba_driver %sdriver;\n",
+ dp->d_name);
+ fprintf(fp, "extern ");
+ ip = dp->d_vec;
+ for (;;) {
+ fprintf(fp, "X%s%d()", ip->id, dp->d_unit);
+ ip = ip->id_next;
+ if (ip == 0)
+ break;
+ fprintf(fp, ", ");
+ }
+ fprintf(fp, ";\n");
+ fprintf(fp, "int\t (*%sint%d[])() = { ", dp->d_name,
+ dp->d_unit);
+ ip = dp->d_vec;
+ for (;;) {
+ fprintf(fp, "X%s%d", ip->id, dp->d_unit);
+ ip = ip->id_next;
+ if (ip == 0)
+ break;
+ fprintf(fp, ", ");
+ }
+ fprintf(fp, ", 0 } ;\n");
+ }
+ }
+ fprintf(fp, "\nstruct uba_ctlr ubminit[] = {\n");
+ fprintf(fp, "/*\t driver,\tctlr,\tubanum,\talive,\tintr,\taddr */\n");
+ for (dp = dtab; dp != 0; dp = dp->d_next) {
+ mp = dp->d_conn;
+ if (dp->d_type != CONTROLLER || mp == TO_NEXUS || mp == 0 ||
+ !eq(mp->d_name, "uba"))
+ continue;
+ if (dp->d_vec == 0) {
+ printf("must specify vector for %s%d\n",
+ dp->d_name, dp->d_unit);
+ continue;
+ }
+ if (dp->d_addr == 0) {
+ printf("must specify csr address for %s%d\n",
+ dp->d_name, dp->d_unit);
+ continue;
+ }
+ if (dp->d_drive != UNKNOWN || dp->d_slave != UNKNOWN) {
+ printf("drives need their own entries; dont ");
+ printf("specify drive or slave for %s%d\n",
+ dp->d_name, dp->d_unit);
+ continue;
+ }
+ if (dp->d_flags) {
+ printf("controllers (e.g. %s%d) ",
+ dp->d_name, dp->d_unit);
+ printf("don't have flags, only devices do\n");
+ continue;
+ }
+ fprintf(fp,
+ "\t{ &%sdriver,\t%d,\t%s,\t0,\t%sint%d, C 0%o },\n",
+ dp->d_name, dp->d_unit, qu(mp->d_unit),
+ dp->d_name, dp->d_unit, dp->d_addr);
+ }
+ fprintf(fp, "\t0\n};\n");
+/* unibus devices */
+ fprintf(fp, "\nstruct uba_device ubdinit[] = {\n");
+ fprintf(fp,
+"\t/* driver, unit, ctlr, ubanum, slave, intr, addr, dk, flags*/\n");
+ for (dp = dtab; dp != 0; dp = dp->d_next) {
+ mp = dp->d_conn;
+ if (dp->d_unit == QUES || dp->d_type != DEVICE || mp == 0 ||
+ mp == TO_NEXUS || mp->d_type == MASTER ||
+ eq(mp->d_name, "mba"))
+ continue;
+ np = mp->d_conn;
+ if (np != 0 && np != TO_NEXUS && eq(np->d_name, "mba"))
+ continue;
+ np = 0;
+ if (eq(mp->d_name, "uba")) {
+ if (dp->d_vec == 0) {
+ printf("must specify vector for device %s%d\n",
+ dp->d_name, dp->d_unit);
+ continue;
+ }
+ if (dp->d_addr == 0) {
+ printf("must specify csr for device %s%d\n",
+ dp->d_name, dp->d_unit);
+ continue;
+ }
+ if (dp->d_drive != UNKNOWN || dp->d_slave != UNKNOWN) {
+ printf("drives/slaves can be specified ");
+ printf("only for controllers, ");
+ printf("not for device %s%d\n",
+ dp->d_name, dp->d_unit);
+ continue;
+ }
+ uba_n = mp->d_unit;
+ slave = QUES;
+ } else {
+ if ((np = mp->d_conn) == 0) {
+ printf("%s%d isn't connected to anything ",
+ mp->d_name, mp->d_unit);
+ printf(", so %s%d is unattached\n",
+ dp->d_name, dp->d_unit);
+ continue;
+ }
+ uba_n = np->d_unit;
+ if (dp->d_drive == UNKNOWN) {
+ printf("must specify ``drive number'' ");
+ printf("for %s%d\n", dp->d_name, dp->d_unit);
+ continue;
+ }
+ /* NOTE THAT ON THE UNIBUS ``drive'' IS STORED IN */
+ /* ``SLAVE'' AND WE DON'T WANT A SLAVE SPECIFIED */
+ if (dp->d_slave != UNKNOWN) {
+ printf("slave numbers should be given only ");
+ printf("for massbus tapes, not for %s%d\n",
+ dp->d_name, dp->d_unit);
+ continue;
+ }
+ if (dp->d_vec != 0) {
+ printf("interrupt vectors should not be ");
+ printf("given for drive %s%d\n",
+ dp->d_name, dp->d_unit);
+ continue;
+ }
+ if (dp->d_addr != 0) {
+ printf("csr addresses should be given only ");
+ printf("on controllers, not on %s%d\n",
+ dp->d_name, dp->d_unit);
+ continue;
+ }
+ slave = dp->d_drive;
+ }
+ fprintf(fp, "\t{ &%sdriver, %2d, %s,",
+ eq(mp->d_name, "uba") ? dp->d_name : mp->d_name, dp->d_unit,
+ eq(mp->d_name, "uba") ? " -1" : qu(mp->d_unit));
+ fprintf(fp, " %s, %2d, %s, C 0%-6o, %d, 0x%x },\n",
+ qu(uba_n), slave, intv(dp), dp->d_addr, dp->d_dk,
+ dp->d_flags);
+ }
+ fprintf(fp, "\t0\n};\n");
+ pseudo_ioconf(fp);
+ (void) fclose(fp);
+}
+#endif
+
+#if MACHINE_TAHOE
+void
+tahoe_ioconf()
+{
+ register struct device *dp, *mp, *np;
+ register int vba_n, slave;
+ FILE *fp;
+
+ fp = fopen(path("ioconf.c"), "w");
+ if (fp == 0)
+ err(1, "%s", path("ioconf.c"));
+ fprintf(fp, "#include <sys/param.h>\n");
+ fprintf(fp, "#include <tahoe/include/pte.h>\n");
+ fprintf(fp, "#include <sys/buf.h>\n");
+ fprintf(fp, "\n");
+ fprintf(fp, "#include <tahoe/vba/vbavar.h>\n");
+ fprintf(fp, "\n");
+ fprintf(fp, "#define C (caddr_t)\n\n");
+ /*
+ * Now generate interrupt vectors for the versabus
+ */
+ for (dp = dtab; dp != 0; dp = dp->d_next) {
+ mp = dp->d_conn;
+ if (mp == 0 || mp == TO_NEXUS || !eq(mp->d_name, "vba"))
+ continue;
+ if (dp->d_vec != 0) {
+ struct idlst *ip;
+ fprintf(fp,
+ "extern struct vba_driver %sdriver;\n",
+ dp->d_name);
+ fprintf(fp, "extern ");
+ ip = dp->d_vec;
+ for (;;) {
+ fprintf(fp, "X%s%d()", ip->id, dp->d_unit);
+ ip = ip->id_next;
+ if (ip == 0)
+ break;
+ fprintf(fp, ", ");
+ }
+ fprintf(fp, ";\n");
+ fprintf(fp, "int\t (*%sint%d[])() = { ", dp->d_name,
+ dp->d_unit);
+ ip = dp->d_vec;
+ for (;;) {
+ fprintf(fp, "X%s%d", ip->id, dp->d_unit);
+ ip = ip->id_next;
+ if (ip == 0)
+ break;
+ fprintf(fp, ", ");
+ }
+ fprintf(fp, ", 0 } ;\n");
+ } else if (dp->d_type == DRIVER) /* devices w/o interrupts */
+ fprintf(fp,
+ "extern struct vba_driver %sdriver;\n",
+ dp->d_name);
+ }
+ fprintf(fp, "\nstruct vba_ctlr vbminit[] = {\n");
+ fprintf(fp, "/*\t driver,\tctlr,\tvbanum,\talive,\tintr,\taddr */\n");
+ for (dp = dtab; dp != 0; dp = dp->d_next) {
+ mp = dp->d_conn;
+ if (dp->d_type != CONTROLLER || mp == TO_NEXUS || mp == 0 ||
+ !eq(mp->d_name, "vba"))
+ continue;
+ if (dp->d_vec == 0) {
+ printf("must specify vector for %s%d\n",
+ dp->d_name, dp->d_unit);
+ continue;
+ }
+ if (dp->d_addr == 0) {
+ printf("must specify csr address for %s%d\n",
+ dp->d_name, dp->d_unit);
+ continue;
+ }
+ if (dp->d_drive != UNKNOWN || dp->d_slave != UNKNOWN) {
+ printf("drives need their own entries; dont ");
+ printf("specify drive or slave for %s%d\n",
+ dp->d_name, dp->d_unit);
+ continue;
+ }
+ if (dp->d_flags) {
+ printf("controllers (e.g. %s%d) ",
+ dp->d_name, dp->d_unit);
+ printf("don't have flags, only devices do\n");
+ continue;
+ }
+ fprintf(fp,
+ "\t{ &%sdriver,\t%d,\t%s,\t0,\t%sint%d, C 0x%x },\n",
+ dp->d_name, dp->d_unit, qu(mp->d_unit),
+ dp->d_name, dp->d_unit, dp->d_addr);
+ }
+ fprintf(fp, "\t0\n};\n");
+/* versabus devices */
+ fprintf(fp, "\nstruct vba_device vbdinit[] = {\n");
+ fprintf(fp,
+"\t/* driver, unit, ctlr, vbanum, slave, intr, addr, dk, flags*/\n");
+ for (dp = dtab; dp != 0; dp = dp->d_next) {
+ mp = dp->d_conn;
+ if (dp->d_unit == QUES || dp->d_type != DEVICE || mp == 0 ||
+ mp == TO_NEXUS || mp->d_type == MASTER ||
+ eq(mp->d_name, "mba"))
+ continue;
+ np = mp->d_conn;
+ if (np != 0 && np != TO_NEXUS && eq(np->d_name, "mba"))
+ continue;
+ np = 0;
+ if (eq(mp->d_name, "vba")) {
+ if (dp->d_vec == 0)
+ printf(
+ "Warning, no interrupt vector specified for device %s%d\n",
+ dp->d_name, dp->d_unit);
+ if (dp->d_addr == 0) {
+ printf("must specify csr for device %s%d\n",
+ dp->d_name, dp->d_unit);
+ continue;
+ }
+ if (dp->d_drive != UNKNOWN || dp->d_slave != UNKNOWN) {
+ printf("drives/slaves can be specified ");
+ printf("only for controllers, ");
+ printf("not for device %s%d\n",
+ dp->d_name, dp->d_unit);
+ continue;
+ }
+ vba_n = mp->d_unit;
+ slave = QUES;
+ } else {
+ if ((np = mp->d_conn) == 0) {
+ printf("%s%d isn't connected to anything ",
+ mp->d_name, mp->d_unit);
+ printf(", so %s%d is unattached\n",
+ dp->d_name, dp->d_unit);
+ continue;
+ }
+ vba_n = np->d_unit;
+ if (dp->d_drive == UNKNOWN) {
+ printf("must specify ``drive number'' ");
+ printf("for %s%d\n", dp->d_name, dp->d_unit);
+ continue;
+ }
+ /* NOTE THAT ON THE UNIBUS ``drive'' IS STORED IN */
+ /* ``SLAVE'' AND WE DON'T WANT A SLAVE SPECIFIED */
+ if (dp->d_slave != UNKNOWN) {
+ printf("slave numbers should be given only ");
+ printf("for massbus tapes, not for %s%d\n",
+ dp->d_name, dp->d_unit);
+ continue;
+ }
+ if (dp->d_vec != 0) {
+ printf("interrupt vectors should not be ");
+ printf("given for drive %s%d\n",
+ dp->d_name, dp->d_unit);
+ continue;
+ }
+ if (dp->d_addr != 0) {
+ printf("csr addresses should be given only ");
+ printf("on controllers, not on %s%d\n",
+ dp->d_name, dp->d_unit);
+ continue;
+ }
+ slave = dp->d_drive;
+ }
+ fprintf(fp, "\t{ &%sdriver, %2d, %s,",
+ eq(mp->d_name, "vba") ? dp->d_name : mp->d_name, dp->d_unit,
+ eq(mp->d_name, "vba") ? " -1" : qu(mp->d_unit));
+ fprintf(fp, " %s, %2d, %s, C 0x%-6x, %d, 0x%x },\n",
+ qu(vba_n), slave, intv(dp), dp->d_addr, dp->d_dk,
+ dp->d_flags);
+ }
+ fprintf(fp, "\t0\n};\n");
+ pseudo_ioconf(fp);
+ (void) fclose(fp);
+}
+#endif
+
+#if MACHINE_HP300 || MACHINE_LUNA68K
+void
+hp300_ioconf()
+{
+ register struct device *dp, *mp;
+ register int hpib, slave;
+ FILE *fp;
+
+ fp = fopen(path("ioconf.c"), "w");
+ if (fp == 0)
+ err(1, "%s", path("ioconf.c"));
+ fprintf(fp, "#include <sys/param.h>\n");
+ fprintf(fp, "#include <sys/buf.h>\n");
+ fprintf(fp, "\n");
+ if (machine == MACHINE_HP300)
+ fprintf(fp, "#include <hp/dev/device.h>\n\n");
+ else
+ fprintf(fp, "#include <luna68k/dev/device.h>\n\n");
+ fprintf(fp, "\n");
+ fprintf(fp, "#define C (caddr_t)\n");
+ fprintf(fp, "#define D (struct driver *)\n\n");
+ /*
+ * First print the hpib controller initialization structures
+ */
+ for (dp = dtab; dp != 0; dp = dp->d_next) {
+ mp = dp->d_conn;
+ if (dp->d_unit == QUES || mp == 0)
+ continue;
+ fprintf(fp, "extern struct driver %sdriver;\n", dp->d_name);
+ }
+ fprintf(fp, "\nstruct hp_ctlr hp_cinit[] = {\n");
+ fprintf(fp, "/*\tdriver,\t\tunit,\talive,\taddr,\tflags */\n");
+ for (dp = dtab; dp != 0; dp = dp->d_next) {
+ mp = dp->d_conn;
+ if (dp->d_unit == QUES ||
+ (dp->d_type != MASTER && dp->d_type != CONTROLLER))
+ continue;
+ if (mp != TO_NEXUS) {
+ printf("%s%s must be attached to an sc (nexus)\n",
+ dp->d_name, wnum(dp->d_unit));
+ continue;
+ }
+ if (dp->d_drive != UNKNOWN || dp->d_slave != UNKNOWN) {
+ printf("can't specify drive/slave for %s%s\n",
+ dp->d_name, wnum(dp->d_unit));
+ continue;
+ }
+ fprintf(fp,
+ "\t{ &%sdriver,\t%d,\t0,\tC 0x%x,\t0x%x },\n",
+ dp->d_name, dp->d_unit, dp->d_addr, dp->d_flags);
+ }
+ fprintf(fp, "\t0\n};\n");
+/* devices */
+ fprintf(fp, "\nstruct hp_device hp_dinit[] = {\n");
+ fprintf(fp,
+ "/*driver,\tcdriver,\tunit,\tctlr,\tslave,\taddr,\tdk,\tflags*/\n");
+ for (dp = dtab; dp != 0; dp = dp->d_next) {
+ mp = dp->d_conn;
+ if (mp == 0 || dp->d_type != DEVICE || hpbadslave(mp, dp))
+ continue;
+ if (mp == TO_NEXUS) {
+ if (dp->d_drive != UNKNOWN || dp->d_slave != UNKNOWN) {
+ printf("can't specify drive/slave for %s%s\n",
+ dp->d_name, wnum(dp->d_unit));
+ continue;
+ }
+ slave = QUES;
+ hpib = QUES;
+ } else {
+ if (dp->d_addr != 0) {
+ printf("can't specify sc for device %s%s\n",
+ dp->d_name, wnum(dp->d_unit));
+ continue;
+ }
+ if (mp->d_type == CONTROLLER) {
+ if (dp->d_drive == UNKNOWN) {
+ printf("must specify drive for %s%s\n",
+ dp->d_name, wnum(dp->d_unit));
+ continue;
+ }
+ slave = dp->d_drive;
+ } else {
+ if (dp->d_slave == UNKNOWN) {
+ printf("must specify slave for %s%s\n",
+ dp->d_name, wnum(dp->d_unit));
+ continue;
+ }
+ slave = dp->d_slave;
+ }
+ hpib = mp->d_unit;
+ }
+ fprintf(fp, "{ &%sdriver,\t", dp->d_name);
+ if (mp == TO_NEXUS)
+ fprintf(fp, "D 0x0,\t");
+ else
+ fprintf(fp, "&%sdriver,", mp->d_name);
+ fprintf(fp, "\t%d,\t%d,\t%d,\tC 0x%x,\t%d,\t0x%x },\n",
+ dp->d_unit, hpib, slave,
+ dp->d_addr, dp->d_dk, dp->d_flags);
+ }
+ fprintf(fp, "0\n};\n");
+ pseudo_ioconf(fp);
+ (void) fclose(fp);
+}
+
+#define ishpibdev(n) (eq(n,"rd") || eq(n,"ct") || eq(n,"mt") || eq(n,"ppi"))
+#define isscsidev(n) (eq(n,"sd") || eq(n,"st") || eq(n,"ac"))
+
+int
+hpbadslave(mp, dp)
+ register struct device *dp, *mp;
+{
+
+ if ((mp == TO_NEXUS && ishpibdev(dp->d_name)) ||
+ (mp != TO_NEXUS && eq(mp->d_name, "hpib") &&
+ !ishpibdev(dp->d_name))) {
+ printf("%s%s must be attached to an hpib\n",
+ dp->d_name, wnum(dp->d_unit));
+ return (1);
+ }
+ if ((mp == TO_NEXUS && isscsidev(dp->d_name)) ||
+ (mp != TO_NEXUS && eq(mp->d_name, "scsi") &&
+ !isscsidev(dp->d_name))) {
+ printf("%s%s must be attached to a scsi\n",
+ dp->d_name, wnum(dp->d_unit));
+ return (1);
+ }
+ return (0);
+}
+#endif
+
+#if MACHINE_I386
+char *shandler();
+char *sirq();
+
+void
+i386_ioconf()
+{
+ register struct device *dp, *mp;
+ int dev_id;
+ FILE *fp, *fp1;
+ static char *old_d_name;
+ static char old_shandler[32 + 1];
+
+ fp = fopen(path("ioconf.c.new"), "w");
+ if (fp == 0)
+ err(1, "%s", path("ioconf.c.new"));
+ fprintf(fp, "/*\n");
+ fprintf(fp, " * I/O configuration.\n");
+ fprintf(fp, " * DO NOT EDIT-- this file is automatically generated.\n");
+ fprintf(fp, " */\n");
+ fprintf(fp, "\n");
+ fprintf(fp, "#include <sys/param.h>\n");
+ fprintf(fp, "#include \"ioconf.h\"\n");
+ fprintf(fp, "\n");
+ fprintf(fp, "#define C (caddr_t)\n");
+ fp1 = fopen(path("ioconf.h.new"), "w");
+ if (fp1 == 0)
+ err(1, "%s", path("ioconf.h.new"));
+ fprintf(fp1, "/*\n");
+ fprintf(fp1, " * Extern declarations for I/O configuration.\n");
+ fprintf(fp1, " * DO NOT EDIT-- this file is automatically generated.\n");
+ fprintf(fp1, " */\n");
+ fprintf(fp1, "\n");
+ fprintf(fp1, "#ifndef IOCONF_H\n");
+ fprintf(fp1, "#define\tIOCONF_H\n");
+ /*
+ * First print the isa initialization structures
+ */
+ if (seen_isa) {
+ int seen_wdc = 0, seen_fdc = 0;
+
+ fprintf(fp, "\n");
+ fprintf(fp, "/*\n");
+ fprintf(fp, " * ISA devices.\n");
+ fprintf(fp, " */\n");
+ fprintf(fp, "\n");
+ fprintf(fp, "#include <i386/isa/icu.h>\n");
+ fprintf(fp, "#include <i386/isa/isa.h>\n");
+ fprintf(fp1, "\n");
+ fprintf(fp1, "#include <i386/isa/isa_device.h>\n");
+ fprintf(fp1, "\n");
+ for (dp = dtab; dp != 0; dp = dp->d_next) {
+ int printed = 0;
+
+ mp = dp->d_conn;
+ if (mp == 0 || mp == TO_NEXUS ||
+ !eq(mp->d_name, "isa"))
+ continue;
+ if (old_d_name == NULL || !eq(dp->d_name, old_d_name)) {
+ old_d_name = dp->d_name;
+ fprintf(fp1,
+ "extern struct isa_driver %3sdriver;",
+ old_d_name);
+ printed = 1;
+ }
+ if (eq(dp->d_name, "wdc"))
+ seen_wdc++;
+ if (eq(dp->d_name, "fdc"))
+ seen_fdc++;
+ if (dp->d_irq == 2) {
+ fprintf(stderr,
+ "remapped irq 2 to irq 9, please update your config file\n");
+ dp->d_irq = 9;
+ }
+ if (dp->d_vec != NULL && dp->d_vec->id != NULL &&
+ !eq(shandler(dp), old_shandler)) {
+ strcpy(old_shandler, shandler(dp));
+ fprintf(fp1, " inthand2_t %s;", old_shandler);
+ printed = 1;
+ }
+ if (printed)
+ fprintf(fp1, "\n");
+ }
+ dev_id = 10; /* XXX must match mkglue.c */
+ isa_devtab(fp, "bio", &dev_id);
+ if (seen_wdc)
+ isa_biotab(fp, "wdc");
+ if (seen_fdc)
+ isa_biotab(fp, "fdc");
+ isa_devtab(fp, "tty", &dev_id);
+ isa_devtab(fp, "net", &dev_id);
+ isa_devtab(fp, "cam", &dev_id);
+ isa_devtab(fp, "null", &dev_id);
+ }
+ if (seen_scbus)
+ scbus_devtab(fp, fp1, &dev_id);
+
+ /* XXX David did this differently!!! */
+ /* pseudo_ioconf(fp); */
+ (void) fclose(fp);
+ fprintf(fp1, "\n");
+ fprintf(fp1, "#endif /* IOCONF_H */\n");
+ (void) fclose(fp1);
+ moveifchanged(path("ioconf.c.new"), path("ioconf.c"));
+ moveifchanged(path("ioconf.h.new"), path("ioconf.h"));
+}
+
+void
+isa_biotab(fp, table)
+ FILE *fp;
+ char *table;
+{
+ register struct device *dp, *mp;
+
+ fprintf(fp, "\n");
+ fprintf(fp, "struct isa_device isa_biotab_%s[] = {\n", table);
+ fprintf(fp, "\
+/* id driver iobase irq drq maddr msiz intr unit flags drive alive ri_flags reconfig enabled conflicts next */\n");
+ for (dp = dtab; dp != 0; dp = dp->d_next) {
+ mp = dp->d_conn;
+ if (dp->d_unit == QUES || mp == 0 ||
+ mp == TO_NEXUS || !eq(mp->d_name, table))
+ continue;
+ fprintf(fp, "{ -1, &%3sdriver, %8s,",
+ mp->d_name, mp->d_port);
+ fprintf(fp, "%6s, %2d, C 0x%05X, %5d, %8s, %3d, 0x%04X, %5d, 0, 0, 0, %6d, %8d, 0 },\n",
+ sirq(mp->d_irq), mp->d_drq, mp->d_maddr,
+ mp->d_msize, shandler(mp), dp->d_unit,
+ dp->d_flags, dp->d_drive, !dp->d_disabled,
+ dp->d_conflicts);
+ }
+ fprintf(fp, "0\n};\n");
+}
+
+/*
+ * Generized routine for isa bus device table, instead of repeating
+ * all this 4 times, call this with the table argument.
+ *
+ * 4/26/93 rgrimes
+ */
+void
+isa_devtab(fp, table, dev_idp)
+ FILE *fp;
+ char *table;
+ int *dev_idp;
+{
+ register struct device *dp, *mp;
+
+ fprintf(fp, "\n");
+ fprintf(fp, "struct isa_device isa_devtab_%s[] = {\n", table);
+ fprintf(fp, "\
+/* id driver iobase irq drq maddr msiz intr unit flags scsiid alive ri_flags reconfig enabled conflicts next */\n");
+ for (dp = dtab; dp != 0; dp = dp->d_next) {
+ if (dp->d_unit == QUES || !eq(dp->d_mask, table))
+ continue;
+ mp = dp->d_conn;
+ if (mp == NULL || mp == TO_NEXUS || !eq(mp->d_name, "isa"))
+ continue;
+ fprintf(fp, "{ %2d, &%3sdriver,", (*dev_idp)++, dp->d_name);
+ if (dp->d_port)
+ fprintf(fp, " %8s,", dp->d_port);
+ else if (dp->d_portn == -1)
+ fprintf(fp, " %d,", dp->d_portn);
+ else
+ fprintf(fp, " 0x%04x,", dp->d_portn);
+ fprintf(fp, "%6s, %2d, C 0x%05X, %5d, %8s, %3d, 0x%04X, 0, 0, 0, 0, %6d, %8d, 0 },\n",
+ sirq(dp->d_irq), dp->d_drq, dp->d_maddr,
+ dp->d_msize, shandler(dp), dp->d_unit,
+ dp->d_flags, !dp->d_disabled, dp->d_conflicts);
+ }
+ fprintf(fp, "0\n};\n");
+}
+
+static char *
+id(int unit)
+{
+ char *s;
+ switch(unit)
+ {
+ case UNKNOWN:
+ s ="SCCONF_UNSPEC";
+ break;
+
+ case QUES:
+ s ="SCCONF_ANY";
+ break;
+
+ default:
+ s = qu(unit);
+ }
+
+ return s;
+}
+
+static void id_put(fp, unit, s)
+ FILE *fp;
+ int unit;
+ char *s;
+{
+ fprintf(fp, "%s%s", id(unit), s);
+}
+
+struct node
+{
+ char *id;
+ struct node *next;
+};
+
+static void
+add_unique(struct node *node, char *id)
+{
+ struct node *prev = node;
+
+ for (prev = node; node; node = node->next)
+ {
+ if (strcmp(node->id, id) == 0) /* Already there */
+ return;
+
+ prev = node;
+ }
+
+ node = (struct node *)malloc(sizeof(node));
+ prev->next = node;
+
+ node->id = id;
+ node->next = 0;
+}
+
+static int
+is_old_scsi_device(char *name)
+{
+ static char *tab[] = {"cd", "ch", "sd", "st", "od", "uk"};
+ int i;
+ for (i = 0; i < sizeof(tab) / sizeof(tab[0]); i++)
+ if (eq(tab[i], name))
+ return 1;
+
+ return 0;
+}
+
+/* XXX: dufault@hda.com: wiped out mkioconf.c locally:
+ * All that nice "conflicting SCSI ID checking" is now
+ * lost and should be put back in.
+ */
+void
+scbus_devtab(fp, fp1, dev_idp)
+ FILE *fp;
+ FILE *fp1;
+ int *dev_idp;
+{
+ register struct device *dp, *mp;
+ struct node unique, *node;
+ unique.id = "unique";
+ unique.next = 0;
+
+ fprintf(fp, "\n");
+ fprintf(fp, "/*\n");
+ fprintf(fp, " * SCSI devices.\n");
+ fprintf(fp, " */\n");
+ fprintf(fp, "\n");
+ fprintf(fp, "#include <scsi/scsiconf.h>\n");
+ fprintf(fp, "\n");
+ fprintf(fp, "struct scsi_ctlr_config scsi_cinit[] = {\n");
+ fprintf(fp, "/* scbus, driver, driver unit, ctlr bus */\n");
+
+ /* XXX: Why do we always get an entry such as:
+ * { '?', "ncr", '?', '?' },
+ */
+
+ for (dp = dtab; dp; dp = dp->d_next) {
+ mp = dp->d_conn;
+ if (dp->d_type != CONTROLLER || mp == TO_NEXUS || mp == 0 ||
+ !eq(dp->d_name, "scbus")) {
+ continue;
+ }
+ fprintf(fp, "{ %s, ", id(dp->d_unit));
+ fprintf(fp, "\"%s\", ", mp->d_name);
+ fprintf(fp, "%s, ", id(mp->d_unit));
+ fprintf(fp, "%s },\n", id(dp->d_slave));
+ }
+ fprintf(fp, "{ 0, 0, 0, 0 }\n");
+ fprintf(fp, "};\n");
+
+ fprintf(fp, "\n");
+ fprintf(fp, "struct scsi_device_config scsi_dinit[] = {\n");
+ fprintf(fp, "/* name unit cunit target LUN flags */\n");
+ for (dp = dtab; dp; dp = dp->d_next) {
+ if (dp->d_type == CONTROLLER || dp->d_type == MASTER ||
+ dp->d_type == PSEUDO_DEVICE)
+ continue;
+
+ /* For backward compatability we must add the original
+ * SCSI devices by name even if we don't know it is
+ * connected to a SCSI bus.
+ */
+ if (is_old_scsi_device(dp->d_name))
+ add_unique(&unique, dp->d_name);
+
+ mp = dp->d_conn;
+ if (mp == 0 || !eq(mp->d_name, "scbus")) {
+ continue;
+ }
+
+ if (mp->d_conn == 0 &&
+ (dp->d_target != UNKNOWN && dp->d_target != QUES)) {
+ fprintf(stderr,
+ "Warning: %s%s is configured at ",
+ dp->d_name, wnum(dp->d_unit));
+
+ fprintf(stderr,
+ "%s%s which is not fixed at a single adapter.\n",
+ mp->d_name, wnum(mp->d_unit));
+ }
+
+ fprintf(fp, "{ ");
+ fprintf(fp, "\"%s\", ", dp->d_name);
+ id_put(fp, dp->d_unit, ", ");
+ id_put(fp, mp->d_unit, ", ");
+ id_put(fp, dp->d_target, ", ");
+ id_put(fp, dp->d_lun, ", ");
+ fprintf(fp, " 0x%x },\n", dp->d_flags);
+ add_unique(&unique, dp->d_name);
+ }
+ fprintf(fp, "{ 0, 0, 0, 0, 0, 0 }\n");
+ fprintf(fp, "};\n");
+
+ fprintf(fp1, "\n");
+ for (node = unique.next; node; node = node->next)
+ fprintf(fp1, "void %sinit __P((void));\n", node->id);
+
+ fprintf(fp, "\n");
+ fprintf(fp, "void (*scsi_tinit[]) __P((void)) = {\n");
+ for (node = unique.next; node; node = node->next)
+ fprintf(fp, "\t%sinit,\n", node->id);
+ fprintf(fp, "\t0,\n");
+ fprintf(fp, "};\n");
+}
+
+/*
+ * XXX - there should be a general function to print devtabs instead of these
+ * little pieces of it.
+ */
+
+char *
+shandler(dp)
+ register struct device *dp;
+{
+ static char buf[32 + 1];
+
+ if (dp->d_vec == NULL || dp->d_vec->id == NULL)
+ return "NULL";
+ /*
+ * This is for ISA. We only support one interrupt handler in the
+ * devtabs. Handlers in the config file after the first for each
+ * device are ignored. Special handlers may be registered at
+ * runtime.
+ */
+ sprintf(buf, "%.32s", dp->d_vec->id);
+ return (buf);
+}
+
+char *
+sirq(num)
+{
+
+ if (num == -1)
+ return ("0");
+ sprintf(errbuf, "IRQ%d", num);
+ return (errbuf);
+}
+#endif
+
+#if MACHINE_PMAX
+void
+pmax_ioconf()
+{
+ register struct device *dp, *mp;
+ FILE *fp;
+
+ fp = fopen(path("ioconf.c"), "w");
+ if (fp == 0)
+ err(1, "%s", path("ioconf.c"));
+ fprintf(fp, "#include <sys/types.h>\n");
+ fprintf(fp, "#include <sys/time.h>\n");
+ fprintf(fp, "#include <pmax/dev/device.h>\n\n");
+ fprintf(fp, "#define C (char *)\n\n");
+
+ /* print controller initialization structures */
+ for (dp = dtab; dp != 0; dp = dp->d_next) {
+ if (dp->d_type == PSEUDO_DEVICE)
+ continue;
+ fprintf(fp, "extern struct driver %sdriver;\n", dp->d_name);
+ }
+ fprintf(fp, "\nstruct pmax_ctlr pmax_cinit[] = {\n");
+ fprintf(fp, "/*\tdriver,\t\tunit,\taddr,\t\tpri,\tflags */\n");
+ for (dp = dtab; dp != 0; dp = dp->d_next) {
+ if (dp->d_type != CONTROLLER && dp->d_type != MASTER)
+ continue;
+ if (dp->d_conn != TO_NEXUS) {
+ printf("%s%s must be attached to a nexus (internal bus)\n",
+ dp->d_name, wnum(dp->d_unit));
+ continue;
+ }
+ if (dp->d_drive != UNKNOWN || dp->d_slave != UNKNOWN) {
+ printf("can't specify drive/slave for %s%s\n",
+ dp->d_name, wnum(dp->d_unit));
+ continue;
+ }
+ if (dp->d_unit == UNKNOWN || dp->d_unit == QUES)
+ dp->d_unit = 0;
+ fprintf(fp,
+ "\t{ &%sdriver,\t%d,\tC 0x%x,\t%d,\t0x%x },\n",
+ dp->d_name, dp->d_unit, dp->d_addr, dp->d_pri,
+ dp->d_flags);
+ }
+ fprintf(fp, "\t0\n};\n");
+
+ /* print devices connected to other controllers */
+ fprintf(fp, "\nstruct scsi_device scsi_dinit[] = {\n");
+ fprintf(fp,
+ "/*driver,\tcdriver,\tunit,\tctlr,\tdrive,\tslave,\tdk,\tflags*/\n");
+ for (dp = dtab; dp != 0; dp = dp->d_next) {
+ if (dp->d_type == CONTROLLER || dp->d_type == MASTER ||
+ dp->d_type == PSEUDO_DEVICE)
+ continue;
+ mp = dp->d_conn;
+ if (mp == 0 ||
+ (!eq(mp->d_name, "asc") && !eq(mp->d_name, "sii"))) {
+ printf("%s%s: devices must be attached to a SCSI (asc or sii) controller\n",
+ dp->d_name, wnum(dp->d_unit));
+ continue;
+ }
+ if ((unsigned)dp->d_drive > 6) {
+ printf("%s%s: SCSI drive must be in the range 0..6\n",
+ dp->d_name, wnum(dp->d_unit));
+ continue;
+ }
+ /* may want to allow QUES later */
+ if ((unsigned)dp->d_slave > 7) {
+ printf("%s%s: SCSI slave (LUN) must be in the range 0..7\n",
+ dp->d_name, wnum(dp->d_unit));
+ continue;
+ }
+ fprintf(fp, "{ &%sdriver,\t&%sdriver,", dp->d_name, mp->d_name);
+ fprintf(fp, "\t%d,\t%d,\t%d,\t%d,\t%d,\t0x%x },\n",
+ dp->d_unit, mp->d_unit, dp->d_drive, dp->d_slave,
+ dp->d_dk, dp->d_flags);
+ }
+ fprintf(fp, "0\n};\n");
+ pseudo_ioconf(fp);
+ (void) fclose(fp);
+}
+#endif
+
+#if MACHINE_NEWS3400
+int have_iop = 0;
+int have_hb = 0;
+int have_vme = 0;
+
+void
+news_ioconf()
+{
+ register struct device *dp, *mp;
+ register int slave;
+ FILE *fp;
+
+ fp = fopen(path("ioconf.c"), "w");
+ if (fp == 0)
+ err(1, "%s", path("ioconf.c"));
+ fprintf(fp, "#include <sys/param.h>\n");
+ fprintf(fp, "#include <sys/buf.h>\n");
+ fprintf(fp, "#include <vm/vm.h>\n");
+ fprintf(fp, "#include \"iop.h\"\n");
+ fprintf(fp, "#include \"hb.h\"\n");
+ fprintf(fp, "\n");
+ fprintf(fp, "#if NIOP > 0\n");
+ fprintf(fp, "#include <news3400/iop/iopvar.h>\n");
+ fprintf(fp, "#endif\n");
+ fprintf(fp, "#if NHB > 0\n");
+ fprintf(fp, "#include <news3400/hbdev/hbvar.h>\n");
+ fprintf(fp, "#endif\n");
+ fprintf(fp, "\n");
+ fprintf(fp, "#define C (caddr_t)\n\n");
+ fprintf(fp, "\n");
+
+/* BEGIN HB */
+ fprintf(fp, "#if NHB > 0\n");
+ /*
+ * Now generate interrupt vectors for the HYPER-BUS
+ */
+ for (dp = dtab; dp != 0; dp = dp->d_next) {
+ if (dp->d_pri >= 0) {
+ mp = dp->d_conn;
+ if (mp == 0 || mp == TO_NEXUS ||
+ !eq(mp->d_name, "hb"))
+ continue;
+ fprintf(fp, "extern struct hb_driver %sdriver;\n",
+ dp->d_name);
+ have_hb++;
+ }
+ }
+ /*
+ * Now spew forth the hb_cinfo structure
+ */
+ fprintf(fp, "\nstruct hb_ctlr hminit[] = {\n");
+ fprintf(fp, "/*\t driver,\tctlr,\talive,\taddr,\tintpri */\n");
+ for (dp = dtab; dp != 0; dp = dp->d_next) {
+ mp = dp->d_conn;
+ if ((dp->d_type != MASTER && dp->d_type != CONTROLLER)
+ || mp == TO_NEXUS || mp == 0 ||
+ !eq(mp->d_name, "hb"))
+ continue;
+ if (dp->d_pri < 0) {
+ printf("must specify priority for %s%d\n",
+ dp->d_name, dp->d_unit);
+ continue;
+ }
+ if (dp->d_drive != UNKNOWN || dp->d_slave != UNKNOWN) {
+ printf("drives need their own entries; ");
+ printf("dont specify drive or slave for %s%d\n",
+ dp->d_name, dp->d_unit);
+ continue;
+ }
+ if (dp->d_flags) {
+ printf(
+ "controllers (e.g. %s%d) don't have flags, only devices do\n",
+ dp->d_name, dp->d_unit);
+ continue;
+ }
+ fprintf(fp, "\t{ &%sdriver,\t%d,\t0,\tC 0x%x,\t%d },\n",
+ dp->d_name, dp->d_unit, dp->d_addr, dp->d_pri);
+ }
+ fprintf(fp, "\t0\n};\n");
+ /*
+ * Now we go for the hb_device stuff
+ */
+ fprintf(fp, "\nstruct hb_device hdinit[] = {\n");
+ fprintf(fp,
+"\t/* driver, unit, ctlr, slave, addr, pri, dk, flags*/\n");
+ for (dp = dtab; dp != 0; dp = dp->d_next) {
+ mp = dp->d_conn;
+ if (dp->d_unit == QUES || dp->d_type != DEVICE || mp == 0 ||
+ mp == TO_NEXUS || /* mp->d_type == MASTER || */
+ eq(mp->d_name, "iop") || eq(mp->d_name, "vme"))
+ continue;
+ if (eq(mp->d_name, "hb")) {
+ if (dp->d_pri < 0) {
+ printf("must specify vector for device %s%d\n",
+ dp->d_name, dp->d_unit);
+ continue;
+ }
+ if (dp->d_drive != UNKNOWN || dp->d_slave != UNKNOWN) {
+ printf("drives/slaves can be specified only ");
+ printf("for controllers, not for device %s%d\n",
+ dp->d_name, dp->d_unit);
+ continue;
+ }
+ slave = QUES;
+ } else {
+ if (mp->d_conn == 0) {
+ printf("%s%d isn't connected to anything, ",
+ mp->d_name, mp->d_unit);
+ printf("so %s%d is unattached\n",
+ dp->d_name, dp->d_unit);
+ continue;
+ }
+ if (dp->d_drive == UNKNOWN) {
+ printf("must specify ``drive number'' for %s%d\n",
+ dp->d_name, dp->d_unit);
+ continue;
+ }
+ /* NOTE THAT ON THE IOP ``drive'' IS STORED IN */
+ /* ``SLAVE'' AND WE DON'T WANT A SLAVE SPECIFIED */
+ if (dp->d_slave != UNKNOWN) {
+ printf("slave numbers should be given only ");
+ printf("for massbus tapes, not for %s%d\n",
+ dp->d_name, dp->d_unit);
+ continue;
+ }
+ if (dp->d_pri >= 0) {
+ printf("interrupt priority should not be ");
+ printf("given for drive %s%d\n",
+ dp->d_name, dp->d_unit);
+ continue;
+ }
+ if (dp->d_addr != 0) {
+ printf("csr addresses should be given only");
+ printf("on controllers, not on %s%d\n",
+ dp->d_name, dp->d_unit);
+ continue;
+ }
+ slave = dp->d_drive;
+ }
+ fprintf(fp,
+"\t{ &%sdriver, %2d, %s, %2d, C 0x%x, %d, %d, 0x%x },\n",
+ eq(mp->d_name, "hb") ? dp->d_name : mp->d_name, dp->d_unit,
+ eq(mp->d_name, "hb") ? " -1" : qu(mp->d_unit),
+ slave, dp->d_addr, dp->d_pri, dp->d_dk, dp->d_flags);
+ }
+ fprintf(fp, "\t0\n};\n\n");
+ fprintf(fp, "#endif\n\n");
+/* END HB */
+ pseudo_ioconf(fp);
+ (void) fclose(fp);
+}
+#endif
+
+char *
+intv(dev)
+ register struct device *dev;
+{
+ static char buf[20];
+
+ if (dev->d_vec == 0)
+ return (" 0");
+ (void) sprintf(buf, "%sint%d", dev->d_name, dev->d_unit);
+ return (buf);
+}
+
+char *
+qu(num)
+{
+
+ if (num == QUES)
+ return ("'?'");
+ if (num == UNKNOWN)
+ return (" -1");
+ (void) sprintf(errbuf, "%3d", num);
+ return (errbuf);
+}
+
+char *
+wnum(num)
+{
+
+ if (num == QUES || num == UNKNOWN)
+ return ("?");
+ (void) sprintf(errbuf, "%d", num);
+ return (errbuf);
+}
+
+void
+pseudo_ioconf(fp)
+ register FILE *fp;
+{
+ register struct device *dp;
+
+ (void)fprintf(fp, "\n#include <sys/device.h>\n\n");
+ for (dp = dtab; dp != NULL; dp = dp->d_next)
+ if (dp->d_type == PSEUDO_DEVICE)
+ (void)fprintf(fp, "extern void %sattach __P((int));\n",
+ dp->d_name);
+ /*
+ * XXX concatonated disks are pseudo-devices but appear as DEVICEs
+ * since they don't adhere to normal pseudo-device conventions
+ * (i.e. one entry with total count in d_slave).
+ */
+ if (seen_cd)
+ (void)fprintf(fp, "extern void cdattach __P((int));\n");
+ /* XXX temporary for HP300, others */
+ (void)fprintf(fp, "\n#include <sys/systm.h> /* XXX */\n");
+ (void)fprintf(fp, "#define etherattach (void (*)__P((int)))nullop\n");
+ (void)fprintf(fp, "#define iteattach (void (*) __P((int)))nullop\n");
+ (void)fprintf(fp, "\nstruct pdevinit pdevinit[] = {\n");
+ for (dp = dtab; dp != NULL; dp = dp->d_next)
+ if (dp->d_type == PSEUDO_DEVICE)
+ (void)fprintf(fp, "\t{ %sattach, %d },\n", dp->d_name,
+ dp->d_slave > 0 ? dp->d_slave : 1);
+ /*
+ * XXX count up cds and put out an entry
+ */
+ if (seen_cd) {
+ struct file_list *fl;
+ int cdmax = -1;
+
+ for (fl = comp_list; fl != NULL; fl = fl->f_next)
+ if (fl->f_type == COMPDEVICE && fl->f_compinfo > cdmax)
+ cdmax = fl->f_compinfo;
+ (void)fprintf(fp, "\t{ cdattach, %d },\n", cdmax+1);
+ }
+ (void)fprintf(fp, "\t{ 0, 0 }\n};\n");
+ if (seen_cd)
+ comp_config(fp);
+}
+
+void
+comp_config(fp)
+ FILE *fp;
+{
+ register struct file_list *fl;
+ register struct device *dp;
+
+ fprintf(fp, "\n#include <dev/cdvar.h>\n");
+ fprintf(fp, "\nstruct cddevice cddevice[] = {\n");
+ fprintf(fp, "/*\tunit\tileave\tflags\tdk\tdevs\t\t\t\t*/\n");
+
+ fl = comp_list;
+ while (fl) {
+ if (fl->f_type != COMPDEVICE) {
+ fl = fl->f_next;
+ continue;
+ }
+ for (dp = dtab; dp != 0; dp = dp->d_next)
+ if (dp->d_type == DEVICE &&
+ eq(dp->d_name, fl->f_fn) &&
+ dp->d_unit == fl->f_compinfo)
+ break;
+ if (dp == 0)
+ continue;
+ fprintf(fp, "\t%d,\t%d,\t%d,\t%d,\t{",
+ dp->d_unit, dp->d_pri < 0 ? 0 : dp->d_pri,
+ dp->d_flags, 1);
+ for (fl = fl->f_next; fl->f_type == COMPSPEC; fl = fl->f_next)
+ fprintf(fp, " 0x%x,", fl->f_compdev);
+ fprintf(fp, " NODEV },\n");
+ }
+ fprintf(fp, "\t-1,\t0,\t0,\t0,\t{ 0 },\n};\n");
+}
diff --git a/usr.sbin/config/mkmakefile.c b/usr.sbin/config/mkmakefile.c
new file mode 100644
index 0000000..93674d2
--- /dev/null
+++ b/usr.sbin/config/mkmakefile.c
@@ -0,0 +1,856 @@
+/*
+ * Copyright (c) 1993, 19801990
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)mkmakefile.c 8.1 (Berkeley) 6/6/93";
+#endif
+static const char rcsid[] =
+ "$Id: mkmakefile.c,v 1.23 1997/10/22 00:38:48 peter Exp $";
+#endif /* not lint */
+
+/*
+ * Build the makefile for the system, from
+ * the information in the files files and the
+ * additional files for the machine being compiled to.
+ */
+
+#include <ctype.h>
+#include <err.h>
+#include <stdio.h>
+#include <string.h>
+#include "y.tab.h"
+#include "config.h"
+#include "configvers.h"
+
+#define next_word(fp, wd) \
+ { register char *word = get_word(fp); \
+ if (word == (char *)EOF) \
+ return; \
+ else \
+ wd = word; \
+ }
+#define next_quoted_word(fp, wd) \
+ { register char *word = get_quoted_word(fp); \
+ if (word == (char *)EOF) \
+ return; \
+ else \
+ wd = word; \
+ }
+
+#define ns(s) strdup(s)
+
+static struct file_list *fcur;
+char *tail();
+extern int old_config_present;
+
+void do_swapspec __P((FILE *, char *));
+void do_clean __P((FILE *));
+void do_load __P((FILE *));
+void do_rules __P((FILE *));
+void do_sfiles __P((FILE *));
+void do_cfiles __P((FILE *));
+void do_objs __P((FILE *));
+void do_before_depend __P((FILE *));
+int opteq __P((char *, char *));
+void read_files __P((void));
+void makefile __P((void));
+
+/*
+ * Lookup a file, by name.
+ */
+struct file_list *
+fl_lookup(file)
+ register char *file;
+{
+ register struct file_list *fp;
+
+ for (fp = ftab ; fp != 0; fp = fp->f_next) {
+ if (eq(fp->f_fn, file))
+ return (fp);
+ }
+ return (0);
+}
+
+/*
+ * Lookup a file, by final component name.
+ */
+struct file_list *
+fltail_lookup(file)
+ register char *file;
+{
+ register struct file_list *fp;
+
+ for (fp = ftab ; fp != 0; fp = fp->f_next) {
+ if (eq(tail(fp->f_fn), tail(file)))
+ return (fp);
+ }
+ return (0);
+}
+
+/*
+ * Make a new file list entry
+ */
+struct file_list *
+new_fent()
+{
+ register struct file_list *fp;
+
+ fp = (struct file_list *) malloc(sizeof *fp);
+ bzero(fp, sizeof *fp);
+ if (fcur == 0)
+ fcur = ftab = fp;
+ else
+ fcur->f_next = fp;
+ fcur = fp;
+ return (fp);
+}
+
+static struct users {
+ int u_default;
+ int u_min;
+ int u_max;
+} users[] = {
+ { 8, 2, 512 }, /* MACHINE_VAX */
+ { 8, 2, 512 }, /* MACHINE_TAHOE */
+ { 8, 2, 512 }, /* MACHINE_HP300 */
+ { 8, 2, 512 }, /* MACHINE_I386 */
+ { 8, 2, 512 }, /* MACHINE_MIPS */
+ { 8, 2, 512 }, /* MACHINE_PMAX */
+ { 8, 2, 512 }, /* MACHINE_LUNA68K */
+ { 8, 2, 512 }, /* MACHINE_NEWS3400 */
+};
+#define NUSERS (sizeof (users) / sizeof (users[0]))
+
+/*
+ * Build the makefile from the skeleton
+ */
+void
+makefile()
+{
+ FILE *ifp, *ofp;
+ char line[BUFSIZ];
+ struct opt *op;
+ struct users *up;
+ int warn_make_clean = 0;
+ int versreq;
+
+ read_files();
+ strcpy(line, "Makefile.");
+ (void) strcat(line, machinename);
+ ifp = fopen(line, "r");
+ if (ifp == 0)
+ err(1, "%s", line);
+ ofp = fopen(path("Makefile.new"), "w");
+ if (ofp == 0)
+ err(1, "%s", path("Makefile.new"));
+ fprintf(ofp, "KERN_IDENT=%s\n", raise(ident));
+ fprintf(ofp, "IDENT=");
+ if (profiling)
+ fprintf(ofp, " -DGPROF");
+
+ if (cputype == 0) {
+ printf("cpu type must be specified\n");
+ exit(1);
+ }
+#if 0
+ /* XXX: moved to cputype.h */
+ { struct cputype *cp;
+ for (cp = cputype; cp; cp = cp->cpu_next)
+ fprintf(ofp, " -D%s", cp->cpu_name);
+ }
+#endif
+ for (op = opt; op; op = op->op_next) {
+ if (!op->op_ownfile) {
+ warn_make_clean++;
+ if (op->op_value)
+ fprintf(ofp, " -D%s=%s", op->op_name, op->op_value);
+ else
+ fprintf(ofp, " -D%s", op->op_name);
+ }
+ }
+ fprintf(ofp, "\n");
+ if ((unsigned)machine > NUSERS) {
+ printf("maxusers config info isn't present, using vax\n");
+ up = &users[MACHINE_VAX-1];
+ } else
+ up = &users[machine-1];
+ if (maxusers == 0) {
+ printf("maxusers not specified; %d assumed\n", up->u_default);
+ maxusers = up->u_default;
+ } else if (maxusers < up->u_min) {
+ printf("minimum of %d maxusers assumed\n", up->u_min);
+ maxusers = up->u_min;
+ } else if (maxusers > up->u_max)
+ printf("warning: maxusers > %d (%d)\n", up->u_max, maxusers);
+ fprintf(ofp, "PARAM=-DMAXUSERS=%d\n", maxusers);
+ if (loadaddress != -1) {
+ fprintf(ofp, "LOAD_ADDRESS=%X\n", loadaddress);
+ }
+ for (op = mkopt; op; op = op->op_next)
+ fprintf(ofp, "%s=%s\n", op->op_name, op->op_value);
+ if (debugging)
+ fprintf(ofp, "DEBUG=-g\n");
+ if (profiling) {
+ fprintf(ofp, "PROF=-pg\n");
+ fprintf(ofp, "PROFLEVEL=%d\n", profiling);
+ }
+ while (fgets(line, BUFSIZ, ifp) != 0) {
+ if (*line != '%') {
+ fprintf(ofp, "%s", line);
+ continue;
+ }
+ if (eq(line, "%BEFORE_DEPEND\n"))
+ do_before_depend(ofp);
+ else if (eq(line, "%OBJS\n"))
+ do_objs(ofp);
+ else if (eq(line, "%CFILES\n"))
+ do_cfiles(ofp);
+ else if (eq(line, "%SFILES\n"))
+ do_sfiles(ofp);
+ else if (eq(line, "%RULES\n"))
+ do_rules(ofp);
+ else if (eq(line, "%LOAD\n"))
+ do_load(ofp);
+ else if (eq(line, "%CLEAN\n"))
+ do_clean(ofp);
+ else if (strncmp(line, "%VERSREQ=", sizeof("%VERSREQ=") - 1) == 0) {
+ versreq = atoi(line + sizeof("%VERSREQ=") - 1);
+ if (versreq != CONFIGVERS) {
+ fprintf(stderr, "WARNING: version of config(8) does not match kernel!\n");
+ fprintf(stderr, "config version = %d, ", CONFIGVERS);
+ fprintf(stderr, "version required = %d\n", versreq);
+ }
+ } else
+ fprintf(stderr,
+ "Unknown %% construct in generic makefile: %s",
+ line);
+ }
+ (void) fclose(ifp);
+ (void) fclose(ofp);
+ moveifchanged(path("Makefile.new"), path("Makefile"));
+#ifdef notyet
+ if (warn_make_clean) {
+ printf("WARNING: Unknown options used (not in ../../conf/options or ./options.%s).\n", machinename);
+ if (old_config_present) {
+ printf("It is VERY important that you do a ``make clean'' before recompiling!\n");
+ }
+ }
+ printf("Don't forget to do a ``make depend''\n");
+#endif
+}
+
+/*
+ * Read in the information about files used in making the system.
+ * Store it in the ftab linked list.
+ */
+void
+read_files()
+{
+ FILE *fp;
+ register struct file_list *tp, *pf;
+ register struct device *dp;
+ struct device *save_dp;
+ register struct opt *op;
+ char *wd, *this, *needs, *special, *depends, *clean;
+ char fname[80];
+ int nreqs, first = 1, configdep, isdup, std, filetype,
+ imp_rule, no_obj, before_depend, mandatory;
+
+ ftab = 0;
+ (void) snprintf(fname, sizeof fname, "../../conf/files");
+openit:
+ fp = fopen(fname, "r");
+ if (fp == 0)
+ err(1, "%s", fname);
+ if(ident == NULL) {
+ printf("no ident line specified\n");
+ exit(1);
+ }
+next:
+ /*
+ * filename [ standard | mandatory | optional ] [ config-dependent ]
+ * [ dev* | profiling-routine ] [ device-driver] [ no-obj ]
+ * [ compile-with "compile rule" [no-implicit-rule] ]
+ * [ dependency "dependency-list"] [ before-depend ]
+ * [ clean "file-list"]
+ */
+ wd = get_word(fp);
+ if (wd == (char *)EOF) {
+ (void) fclose(fp);
+ if (first == 1) {
+ (void) snprintf(fname, sizeof fname, "files.%s", machinename);
+ first++;
+ goto openit;
+ }
+ if (first == 2) {
+ (void) snprintf(fname, sizeof fname, "files.%s", raise(ident));
+ first++;
+ fp = fopen(fname, "r");
+ if (fp != 0)
+ goto next;
+ }
+ return;
+ }
+ if (wd == 0)
+ goto next;
+ /*************************************************\
+ * If it's a comment ignore to the end of the line *
+ \*************************************************/
+ if(wd[0] == '#')
+ {
+ while( ((wd = get_word(fp)) != (char *)EOF) && wd)
+ ;
+ goto next;
+ }
+ this = ns(wd);
+ next_word(fp, wd);
+ if (wd == 0) {
+ printf("%s: No type for %s.\n",
+ fname, this);
+ exit(1);
+ }
+ if ((pf = fl_lookup(this)) && (pf->f_type != INVISIBLE || pf->f_flags))
+ isdup = 1;
+ else
+ isdup = 0;
+ tp = 0;
+ if (first == 3 && (tp = fltail_lookup(this)) != 0)
+ printf("%s: Local file %s overrides %s.\n",
+ fname, this, tp->f_fn);
+ nreqs = 0;
+ special = 0;
+ depends = 0;
+ clean = 0;
+ configdep = 0;
+ needs = 0;
+ std = mandatory = 0;
+ imp_rule = 0;
+ no_obj = 0;
+ before_depend = 0;
+ filetype = NORMAL;
+ if (eq(wd, "standard"))
+ std = 1;
+ /*
+ * If an entry is marked "mandatory", config will abort if it's
+ * not called by a configuration line in the config file. Apart
+ * from this, the device is handled like one marked "optional".
+ */
+ else if (eq(wd, "mandatory"))
+ mandatory = 1;
+ else if (!eq(wd, "optional")) {
+ printf("%s: %s must be optional, mandatory or standard\n",
+ fname, this);
+ exit(1);
+ }
+nextparam:
+ next_word(fp, wd);
+ if (wd == 0)
+ goto doneparam;
+ if (eq(wd, "config-dependent")) {
+ configdep++;
+ goto nextparam;
+ }
+ if (eq(wd, "no-obj")) {
+ no_obj++;
+ goto nextparam;
+ }
+ if (eq(wd, "no-implicit-rule")) {
+ if (special == 0) {
+ printf("%s: alternate rule required when "
+ "\"no-implicit-rule\" is specified.\n",
+ fname);
+ }
+ imp_rule++;
+ goto nextparam;
+ }
+ if (eq(wd, "before-depend")) {
+ before_depend++;
+ goto nextparam;
+ }
+ if (eq(wd, "dependency")) {
+ next_quoted_word(fp, wd);
+ if (wd == 0) {
+ printf("%s: %s missing compile command string.\n",
+ fname, this);
+ exit(1);
+ }
+ depends = ns(wd);
+ goto nextparam;
+ }
+ if (eq(wd, "clean")) {
+ next_quoted_word(fp, wd);
+ if (wd == 0) {
+ printf("%s: %s missing clean file list.\n",
+ fname, this);
+ exit(1);
+ }
+ clean = ns(wd);
+ goto nextparam;
+ }
+ if (eq(wd, "compile-with")) {
+ next_quoted_word(fp, wd);
+ if (wd == 0) {
+ printf("%s: %s missing compile command string.\n",
+ fname, this);
+ exit(1);
+ }
+ special = ns(wd);
+ goto nextparam;
+ }
+ nreqs++;
+ if (eq(wd, "device-driver")) {
+ filetype = DRIVER;
+ goto nextparam;
+ }
+ if (eq(wd, "profiling-routine")) {
+ filetype = PROFILING;
+ goto nextparam;
+ }
+ if (needs == 0 && nreqs == 1)
+ needs = ns(wd);
+ if (isdup)
+ goto invis;
+ for (dp = dtab; dp != 0; save_dp = dp, dp = dp->d_next)
+ if (eq(dp->d_name, wd)) {
+ if (std && dp->d_type == PSEUDO_DEVICE &&
+ dp->d_slave <= 0)
+ dp->d_slave = 1;
+ goto nextparam;
+ }
+ if (mandatory) {
+ printf("%s: mandatory device \"%s\" not found\n",
+ fname, wd);
+ exit(1);
+ }
+ if (std) {
+ dp = (struct device *) malloc(sizeof *dp);
+ bzero(dp, sizeof *dp);
+ init_dev(dp);
+ dp->d_name = ns(wd);
+ dp->d_type = PSEUDO_DEVICE;
+ dp->d_slave = 1;
+ save_dp->d_next = dp;
+ goto nextparam;
+ }
+ for (op = opt; op != 0; op = op->op_next)
+ if (op->op_value == 0 && opteq(op->op_name, wd)) {
+ if (nreqs == 1) {
+ free(needs);
+ needs = 0;
+ }
+ goto nextparam;
+ }
+invis:
+ while ((wd = get_word(fp)) != 0)
+ ;
+ if (tp == 0)
+ tp = new_fent();
+ tp->f_fn = this;
+ tp->f_type = INVISIBLE;
+ tp->f_needs = needs;
+ tp->f_flags = isdup;
+ tp->f_special = special;
+ tp->f_depends = depends;
+ tp->f_clean = clean;
+ goto next;
+
+doneparam:
+ if (std == 0 && nreqs == 0) {
+ printf("%s: what is %s optional on?\n",
+ fname, this);
+ exit(1);
+ }
+
+save:
+ if (wd) {
+ printf("%s: syntax error describing %s\n",
+ fname, this);
+ exit(1);
+ }
+ if (filetype == PROFILING && profiling == 0)
+ goto next;
+ if (tp == 0)
+ tp = new_fent();
+ tp->f_fn = this;
+ tp->f_type = filetype;
+ tp->f_flags = 0;
+ if (configdep)
+ tp->f_flags |= CONFIGDEP;
+ if (imp_rule)
+ tp->f_flags |= NO_IMPLCT_RULE;
+ if (no_obj)
+ tp->f_flags |= NO_OBJ;
+ if (before_depend)
+ tp->f_flags |= BEFORE_DEPEND;
+ if (imp_rule)
+ tp->f_flags |= NO_IMPLCT_RULE;
+ if (no_obj)
+ tp->f_flags |= NO_OBJ;
+ tp->f_needs = needs;
+ tp->f_special = special;
+ tp->f_depends = depends;
+ tp->f_clean = clean;
+ if (pf && pf->f_type == INVISIBLE)
+ pf->f_flags = 1; /* mark as duplicate */
+ goto next;
+}
+
+int
+opteq(cp, dp)
+ char *cp, *dp;
+{
+ char c, d;
+
+ for (; ; cp++, dp++) {
+ if (*cp != *dp) {
+ c = isupper(*cp) ? tolower(*cp) : *cp;
+ d = isupper(*dp) ? tolower(*dp) : *dp;
+ if (c != d)
+ return (0);
+ }
+ if (*cp == 0)
+ return (1);
+ }
+}
+
+void
+do_before_depend(fp)
+ FILE *fp;
+{
+ register struct file_list *tp;
+ register int lpos, len;
+
+ fputs("BEFORE_DEPEND=", fp);
+ lpos = 15;
+ for (tp = ftab; tp; tp = tp->f_next)
+ if (tp->f_flags & BEFORE_DEPEND) {
+ len = strlen(tp->f_fn);
+ if ((len = 3 + len) + lpos > 72) {
+ lpos = 8;
+ fputs("\\\n\t", fp);
+ }
+ if (tp->f_flags & NO_IMPLCT_RULE)
+ fprintf(fp, "%s ", tp->f_fn);
+ else
+ fprintf(fp, "$S/%s ", tp->f_fn);
+ lpos += len + 1;
+ }
+ if (lpos != 8)
+ putc('\n', fp);
+}
+
+void
+do_objs(fp)
+ FILE *fp;
+{
+ register struct file_list *tp, *fl;
+ register int lpos, len;
+ register char *cp, och, *sp;
+ char swapname[32];
+
+ fprintf(fp, "OBJS=");
+ lpos = 6;
+ for (tp = ftab; tp != 0; tp = tp->f_next) {
+ if (tp->f_type == INVISIBLE || tp->f_flags & NO_OBJ)
+ continue;
+ sp = tail(tp->f_fn);
+ for (fl = conf_list; fl; fl = fl->f_next) {
+ if (fl->f_type != SWAPSPEC)
+ continue;
+ (void) sprintf(swapname, "swap%s.c", fl->f_fn);
+ if (eq(sp, swapname))
+ goto cont;
+ }
+ cp = sp + (len = strlen(sp)) - 1;
+ och = *cp;
+ *cp = 'o';
+ if (len + lpos > 72) {
+ lpos = 8;
+ fprintf(fp, "\\\n\t");
+ }
+ fprintf(fp, "%s ", sp);
+ lpos += len + 1;
+ *cp = och;
+cont:
+ ;
+ }
+ if (lpos != 8)
+ putc('\n', fp);
+}
+
+void
+do_cfiles(fp)
+ FILE *fp;
+{
+ register struct file_list *tp, *fl;
+ register int lpos, len;
+ char swapname[32];
+
+ fputs("CFILES=", fp);
+ lpos = 8;
+ for (tp = ftab; tp; tp = tp->f_next)
+ if (tp->f_type != INVISIBLE) {
+ len = strlen(tp->f_fn);
+ if (tp->f_fn[len - 1] != 'c')
+ continue;
+ if ((len = 3 + len) + lpos > 72) {
+ lpos = 8;
+ fputs("\\\n\t", fp);
+ }
+ fprintf(fp, "$S/%s ", tp->f_fn);
+ lpos += len + 1;
+ }
+ for (fl = conf_list; fl; fl = fl->f_next)
+ if (fl->f_type == SYSTEMSPEC) {
+ (void) sprintf(swapname, "swap%s.c", fl->f_fn);
+ if ((len = 3 + strlen(swapname)) + lpos > 72) {
+ lpos = 8;
+ fputs("\\\n\t", fp);
+ }
+ if (eq(fl->f_fn, "generic"))
+ fprintf(fp, "$S/%s/%s/%s ",
+ machinename, machinename, swapname);
+ else
+ fprintf(fp, "%s ", swapname);
+ lpos += len + 1;
+ }
+ if (lpos != 8)
+ putc('\n', fp);
+}
+
+void
+do_sfiles(fp)
+ FILE *fp;
+{
+ register struct file_list *tp;
+ register int lpos, len;
+
+ fputs("SFILES=", fp);
+ lpos = 8;
+ for (tp = ftab; tp; tp = tp->f_next)
+ if (tp->f_type != INVISIBLE) {
+ len = strlen(tp->f_fn);
+ if (tp->f_fn[len - 1] != 'S' && tp->f_fn[len - 1] != 's')
+ continue;
+ if ((len = 3 + len) + lpos > 72) {
+ lpos = 8;
+ fputs("\\\n\t", fp);
+ }
+ fprintf(fp, "$S/%s ", tp->f_fn);
+ lpos += len + 1;
+ }
+ if (lpos != 8)
+ putc('\n', fp);
+}
+
+
+char *
+tail(fn)
+ char *fn;
+{
+ register char *cp;
+
+ cp = rindex(fn, '/');
+ if (cp == 0)
+ return (fn);
+ return (cp+1);
+}
+
+/*
+ * Create the makerules for each file
+ * which is part of the system.
+ * Devices are processed with the special c2 option -i
+ * which avoids any problem areas with i/o addressing
+ * (e.g. for the VAX); assembler files are processed by as.
+ */
+void
+do_rules(f)
+ FILE *f;
+{
+ register char *cp, *np, och, *tp;
+ register struct file_list *ftp;
+ char *special;
+
+ for (ftp = ftab; ftp != 0; ftp = ftp->f_next) {
+ if (ftp->f_type == INVISIBLE)
+ continue;
+ cp = (np = ftp->f_fn) + strlen(ftp->f_fn) - 1;
+ och = *cp;
+ if (ftp->f_flags & NO_IMPLCT_RULE) {
+ if (ftp->f_depends)
+ fprintf(f, "%s: %s\n", np, ftp->f_depends );
+ else
+ fprintf(f, "%s: \n", np );
+ }
+ else {
+ *cp = '\0';
+ if (och == 'o') {
+ fprintf(f, "%so:\n\t-cp $S/%so .\n\n",
+ tail(np), np);
+ continue;
+ }
+ if (ftp->f_depends)
+ fprintf(f, "%so: $S/%s%c %s\n", tail(np),
+ np, och, ftp->f_depends);
+ else
+ fprintf(f, "%so: $S/%s%c\n", tail(np),
+ np, och);
+ }
+ tp = tail(np);
+ special = ftp->f_special;
+ if (special == 0) {
+ char *ftype;
+ static char cmd[128];
+
+ switch (ftp->f_type) {
+
+ case NORMAL:
+ ftype = "NORMAL";
+ break;
+
+ case DRIVER:
+ ftype = "DRIVER";
+ break;
+
+ case PROFILING:
+ if (!profiling)
+ continue;
+ ftype = "PROFILE";
+ break;
+
+ default:
+ printf("config: don't know rules for %s\n", np);
+ break;
+ }
+ (void)sprintf(cmd, "${%s_%c%s}", ftype, toupper(och),
+ ftp->f_flags & CONFIGDEP? "_C" : "");
+ special = cmd;
+ }
+ *cp = och;
+ fprintf(f, "\t%s\n\n", special);
+ }
+}
+
+/*
+ * Create the load strings
+ */
+void
+do_load(f)
+ register FILE *f;
+{
+ register struct file_list *fl;
+ register int first;
+ struct file_list *do_systemspec();
+
+ for (first = 1, fl = conf_list; fl; first = 0)
+ fl = fl->f_type == SYSTEMSPEC ?
+ do_systemspec(f, fl, first) : fl->f_next;
+ fputs("all:", f);
+ for (fl = conf_list; fl; fl = fl->f_next)
+ if (fl->f_type == SYSTEMSPEC)
+ fprintf(f, " %s", fl->f_needs);
+ putc('\n', f);
+}
+
+void
+do_clean(fp)
+ FILE *fp;
+{
+ register struct file_list *tp;
+ register int lpos, len;
+
+ fputs("CLEAN=", fp);
+ lpos = 7;
+ for (tp = ftab; tp; tp = tp->f_next)
+ if (tp->f_clean) {
+ len = strlen(tp->f_clean);
+ if (len + lpos > 72) {
+ lpos = 8;
+ fputs("\\\n\t", fp);
+ }
+ fprintf(fp, "%s ", tp->f_clean);
+ lpos += len + 1;
+ }
+ if (lpos != 8)
+ putc('\n', fp);
+}
+
+struct file_list *
+do_systemspec(f, fl, first)
+ FILE *f;
+ register struct file_list *fl;
+ int first;
+{
+
+ fprintf(f, "%s: ${SYSTEM_DEP} swap%s.o", fl->f_needs, fl->f_fn);
+ if (first)
+ fprintf(f, " vers.o");
+ fprintf(f, "\n\t${SYSTEM_LD_HEAD}\n");
+ fprintf(f, "\t${SYSTEM_LD} swap%s.o\n", fl->f_fn);
+ fprintf(f, "\t${SYSTEM_LD_TAIL}\n\n");
+ do_swapspec(f, fl->f_fn);
+ for (fl = fl->f_next; fl; fl = fl->f_next)
+ if (fl->f_type != SWAPSPEC)
+ break;
+ return (fl);
+}
+
+void
+do_swapspec(f, name)
+ FILE *f;
+ register char *name;
+{
+
+ if (!eq(name, "generic"))
+ fprintf(f, "swap%s.o: swap%s.c\n", name, name);
+ else
+ fprintf(f, "swapgeneric.o: $S/%s/%s/swapgeneric.c\n",
+ machinename, machinename);
+ fprintf(f, "\t${NORMAL_C}\n\n");
+}
+
+char *
+raise(str)
+ register char *str;
+{
+ register char *cp = str;
+
+ while (*str) {
+ if (islower(*str))
+ *str = toupper(*str);
+ str++;
+ }
+ return (cp);
+}
+
diff --git a/usr.sbin/config/mkoptions.c b/usr.sbin/config/mkoptions.c
new file mode 100644
index 0000000..16a2d8f
--- /dev/null
+++ b/usr.sbin/config/mkoptions.c
@@ -0,0 +1,323 @@
+/*
+ * Copyright (c) 1995 Peter Wemm
+ * Copyright (c) 1980, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)mkheaders.c 8.1 (Berkeley) 6/6/93";
+#endif
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+/*
+ * Make all the .h files for the optional entries
+ */
+
+#include <ctype.h>
+#include <err.h>
+#include <stdio.h>
+#include <string.h>
+#include "config.h"
+#include "y.tab.h"
+
+#define ns(s) strdup(s)
+
+static char *lower __P((char *));
+void read_options __P((void));
+void do_option __P((char *));
+
+void
+options()
+{
+ struct opt_list *ol;
+
+ /* fake the cpu types as options */
+ /* Please forgive me for this hack.. :-) */
+ struct cputype *cp;
+
+ for (cp = cputype; cp; cp = cp->cpu_next) {
+ struct opt *op = (struct opt *)malloc(sizeof (struct opt));
+ memset(op, 0, sizeof(*op));
+ op->op_name = ns(cp->cpu_name);
+ op->op_value = 0;
+ op->op_next = opt;
+ opt = op;
+ }
+
+ read_options();
+ for (ol = otab; ol != 0; ol = ol->o_next)
+ do_option(ol->o_name);
+}
+
+/*
+ * Generate an <options>.h file
+ */
+
+void
+do_option(name)
+ char *name;
+{
+ char *file, *inw, *tooption();
+ struct opt *op, *op_head, *topp;
+ FILE *inf, *outf;
+ char *value;
+ char *oldvalue;
+ int seen;
+
+ file = tooption(name);
+
+ /*
+ * Check to see if the option was specified..
+ */
+ value = NULL;
+ for (op = opt; op; op = op->op_next) {
+ if (eq(name, op->op_name)) {
+ value = op->op_value;
+ if (!value)
+ value = ns("1");
+ op->op_ownfile++;
+ }
+ }
+
+ inf = fopen(file, "r");
+ if (inf == 0) {
+ outf = fopen(file, "w");
+ if (outf == 0)
+ err(1, "%s", file);
+
+ /* was the option in the config file? */
+ if (value) {
+ fprintf(outf, "#define %s %s\n", name, value);
+ } /* else empty file */
+
+ (void) fclose(outf);
+ return;
+ }
+ oldvalue = NULL;
+ op_head = NULL;
+ seen = 0;
+ for (;;) {
+ char *cp;
+ char *invalue;
+
+ /* get the #define */
+ if ((inw = get_word(inf)) == 0 || inw == (char *)EOF)
+ break;
+ /* get the option name */
+ if ((inw = get_word(inf)) == 0 || inw == (char *)EOF)
+ break;
+ inw = ns(inw);
+ cp = get_word(inf);
+ if (cp == 0 || cp == (char *)EOF)
+ break;
+ /* option value */
+ invalue = ns(cp); /* malloced */
+ if (eq(inw, name)) {
+ oldvalue = invalue;
+ invalue = value;
+ seen++;
+ }
+ op = (struct opt *) malloc(sizeof *op);
+ bzero(op, sizeof(*op));
+ op->op_name = inw;
+ op->op_value = invalue;
+ op->op_next = op_head;
+ op_head = op;
+
+ /* EOL? */
+ cp = get_word(inf);
+ if (cp == (char *)EOF)
+ break;
+ }
+ (void) fclose(inf);
+ if ((value == NULL && oldvalue == NULL) ||
+ (value && oldvalue && eq(value,oldvalue))) {
+ for (op = op_head; op != NULL; op = topp) {
+ topp = op->op_next;
+ free(op->op_name);
+ free(op->op_value);
+ free(op);
+ }
+ return;
+ }
+
+ if (value && !seen) {
+ /* New option appears */
+ op = (struct opt *) malloc(sizeof *op);
+ bzero(op, sizeof(*op));
+ op->op_name = ns(name);
+ op->op_value = value ? ns(value) : NULL;
+ op->op_next = op_head;
+ op_head = op;
+ }
+
+ outf = fopen(file, "w");
+ if (outf == 0)
+ err(1, "%s", file);
+ for (op = op_head; op != NULL; op = topp) {
+ /* was the option in the config file? */
+ if (op->op_value) {
+ fprintf(outf, "#define %s %s\n",
+ op->op_name, op->op_value);
+ }
+ topp = op->op_next;
+ free(op->op_name);
+ free(op->op_value);
+ free(op);
+ }
+ (void) fclose(outf);
+}
+
+/*
+ * Find the filename to store the option spec into.
+ */
+char *
+tooption(name)
+ char *name;
+{
+ static char hbuf[80];
+ char nbuf[80];
+ struct opt_list *po;
+
+ /* "cannot happen"? the otab list should be complete.. */
+ (void) strcpy(nbuf, "options.h");
+
+ for (po = otab ; po != 0; po = po->o_next) {
+ if (eq(po->o_name, name)) {
+ strcpy(nbuf, po->o_file);
+ break;
+ }
+ }
+
+ (void) strcpy(hbuf, path(nbuf));
+ return (hbuf);
+}
+
+/*
+ * read the options and options.<machine> files
+ */
+void
+read_options()
+{
+ FILE *fp;
+ char fname[80];
+ char *wd, *this, *val;
+ struct opt_list *po;
+ int first = 1;
+ char genopt[80];
+
+ otab = 0;
+ (void) snprintf(fname, sizeof fname, "../../conf/options");
+openit:
+ fp = fopen(fname, "r");
+ if (fp == 0) {
+ return;
+ }
+ if(ident == NULL) {
+ printf("no ident line specified\n");
+ exit(1);
+ }
+next:
+ wd = get_word(fp);
+ if (wd == (char *)EOF) {
+ (void) fclose(fp);
+ if (first == 1) {
+ (void) snprintf(fname, sizeof fname, "options.%s", machinename);
+ first++;
+ goto openit;
+ }
+ if (first == 2) {
+ (void) snprintf(fname, sizeof fname, "options.%s", raise(ident));
+ first++;
+ fp = fopen(fname, "r");
+ if (fp != 0)
+ goto next;
+ }
+ return;
+ }
+ if (wd == 0)
+ goto next;
+ /*************************************************\
+ * If it's a comment ignore to the end of the line *
+ \*************************************************/
+ if(wd[0] == '#')
+ {
+ while( ((wd = get_word(fp)) != (char *)EOF) && wd)
+ ;
+ goto next;
+ }
+ this = ns(wd);
+ val = get_word(fp);
+ if (val == (char *)EOF)
+ return;
+ if (val == 0) {
+ char *s = ns(this);
+ (void) snprintf(genopt, sizeof genopt, "opt_%s.h", lower(s));
+ val = genopt;
+ free(s);
+ }
+ val = ns(val);
+
+ for (po = otab ; po != 0; po = po->o_next) {
+ if (eq(po->o_name, this)) {
+ printf("%s: Duplicate option %s.\n",
+ fname, this);
+ exit(1);
+ }
+ }
+
+ po = (struct opt_list *) malloc(sizeof *po);
+ bzero(po, sizeof(*po));
+ po->o_name = this;
+ po->o_file = val;
+ po->o_next = otab;
+ otab = po;
+
+ goto next;
+}
+
+static char *
+lower(str)
+ register char *str;
+{
+ register char *cp = str;
+
+ while (*str) {
+ if (isupper(*str))
+ *str = tolower(*str);
+ str++;
+ }
+ return (cp);
+}
+
diff --git a/usr.sbin/config/mkswapconf.c b/usr.sbin/config/mkswapconf.c
new file mode 100644
index 0000000..e563968
--- /dev/null
+++ b/usr.sbin/config/mkswapconf.c
@@ -0,0 +1,269 @@
+/*
+ * Copyright (c) 1980, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)mkswapconf.c 8.1 (Berkeley) 6/6/93";
+#endif
+static const char rcsid[] =
+ "$Id: mkswapconf.c,v 1.13 1997/09/15 06:37:10 charnier Exp $";
+#endif /* not lint */
+
+/*
+ * Build a swap configuration file.
+ */
+#include <err.h>
+#include <unistd.h>
+#include "config.h"
+
+#include <sys/disklabel.h>
+#include <sys/diskslice.h>
+
+#include <ctype.h>
+#include <stdio.h>
+#include <string.h>
+
+#define ns(s) strdup(s)
+
+void initdevtable __P((void));
+
+void
+swapconf()
+{
+ register struct file_list *fl;
+ struct file_list *do_swap();
+
+ fl = conf_list;
+ while (fl) {
+ if (fl->f_type != SYSTEMSPEC) {
+ fl = fl->f_next;
+ continue;
+ }
+ fl = do_swap(fl);
+ }
+}
+
+struct file_list *
+do_swap(fl)
+ register struct file_list *fl;
+{
+ FILE *fp;
+ char newswapname[80];
+ char swapname[80];
+ register struct file_list *swap;
+
+ if (eq(fl->f_fn, "generic")) {
+ fl = fl->f_next;
+ return (fl->f_next);
+ }
+ (void) sprintf(swapname, "swap%s.c", fl->f_fn);
+ (void) sprintf(newswapname, "swap%s.c.new", fl->f_fn);
+ fp = fopen(path(newswapname), "w");
+ if (fp == 0)
+ err(1, "%s", path(newswapname));
+ fprintf(fp, "#include <sys/param.h>\n");
+ fprintf(fp, "#include <sys/conf.h>\n");
+ fprintf(fp, "\n");
+ /*
+ * If there aren't any swap devices
+ * specified, just return, the error
+ * has already been noted.
+ */
+ swap = fl->f_next;
+ if (swap == 0 || swap->f_type != SWAPSPEC) {
+ (void) unlink(path(newswapname));
+ fclose(fp);
+ return (swap);
+ }
+ fprintf(fp, "dev_t\trootdev = makedev(%d, 0x%08x);\t\t/* %s */\n",
+ major(fl->f_rootdev), minor(fl->f_rootdev),
+ devtoname(fl->f_rootdev));
+ if (fl->f_dumpdev != NODEV) {
+ fprintf(fp, "dev_t\tdumpdev = makedev(%d, 0x%08x);\t\t/* %s */\n",
+ major(fl->f_dumpdev), minor(fl->f_dumpdev),
+ devtoname(fl->f_dumpdev));
+ } else {
+ fprintf(fp, "dev_t\tdumpdev = NODEV;\t\t\t/* unconfigured */\n");
+ }
+ fprintf(fp, "\n");
+ fprintf(fp, "void\nsetconf()\n{\n}\n");
+ fclose(fp);
+ moveifchanged(path(newswapname), path(swapname));
+ return (swap);
+}
+
+static int devtablenotread = 1;
+static struct devdescription {
+ char *dev_name;
+ int dev_major;
+ struct devdescription *dev_next;
+} *devtable;
+
+/*
+ * Given a device name specification figure out:
+ * major device number
+ * partition
+ * device name
+ * unit number
+ * This is a hack, but the system still thinks in
+ * terms of major/minor instead of string names.
+ */
+dev_t
+nametodev(name, defunit, defslice, defpartition)
+ char *name;
+ int defunit;
+ int defslice;
+ char defpartition;
+{
+ char *cp, partition;
+ int unit, slice;
+ register struct devdescription *dp;
+
+ cp = name;
+ if (cp == 0)
+ errx(1, "internal error, nametodev");
+ while (*cp && !isdigit(*cp))
+ cp++;
+ unit = *cp ? atoi(cp) : defunit;
+ if (unit < 0 || unit > 31) {
+ warnx(
+ "%s: invalid device specification, unit out of range", name);
+ unit = defunit; /* carry on more checking */
+ }
+ if (*cp) {
+ *cp++ = '\0';
+ while (*cp && isdigit(*cp))
+ cp++;
+ }
+ slice = defslice;
+ if (*cp == 's') {
+ ++cp;
+ if (*cp) {
+ slice = atoi(cp);
+ if (slice < 0 || slice >= MAX_SLICES - 1) {
+ warnx(
+ "%s: invalid device specification, slice out of range",
+ cp);
+ slice = defslice;
+ }
+ if (slice != COMPATIBILITY_SLICE)
+ slice++;
+ *cp++ = '\0';
+ while (*cp && isdigit(*cp))
+ cp++;
+ }
+ }
+ partition = *cp ? *cp : defpartition;
+ if (partition < 'a' || partition > 'h') {
+ warnx("%c: invalid device specification, bad partition", *cp);
+ partition = defpartition; /* carry on */
+ }
+ if (devtablenotread)
+ initdevtable();
+ for (dp = devtable; dp; dp = dp->dev_next)
+ if (eq(name, dp->dev_name))
+ break;
+ if (dp == 0) {
+ warnx("%s: unknown device", name);
+ return (NODEV);
+ }
+ return (makedev(dp->dev_major,
+ dkmakeminor(unit, slice, partition - 'a')));
+}
+
+char *
+devtoname(dev)
+ dev_t dev;
+{
+ char buf[80];
+ register struct devdescription *dp;
+ int part;
+ char partname[2];
+ int slice;
+ char slicename[32];
+
+ if (devtablenotread)
+ initdevtable();
+ for (dp = devtable; dp; dp = dp->dev_next)
+ if (major(dev) == dp->dev_major)
+ break;
+ if (dp == 0)
+ dp = devtable;
+ part = dkpart(dev);
+ slice = dkslice(dev);
+ slicename[0] = partname[0] = '\0';
+ if (slice != WHOLE_DISK_SLICE || part != RAW_PART) {
+ partname[0] = 'a' + part;
+ partname[1] = '\0';
+ if (slice != COMPATIBILITY_SLICE)
+ sprintf(slicename, "s%d", slice - 1);
+ }
+ (void) sprintf(buf, "%s%d%s%s", dp->dev_name,
+ dkunit(dev), slicename, partname);
+ return (ns(buf));
+}
+
+void
+initdevtable()
+{
+ char linebuf[256];
+ char buf[BUFSIZ];
+ int maj;
+ register struct devdescription **dp = &devtable;
+ FILE *fp;
+
+ (void) sprintf(buf, "../conf/devices.%s", machinename);
+ fp = fopen(buf, "r");
+ if (fp == NULL)
+ errx(1, "can't open %s", buf);
+ while(fgets(linebuf,256,fp)) {
+ /*******************************\
+ * Allow a comment *
+ \*******************************/
+ if(linebuf[0] == '#') continue;
+
+ if (sscanf(linebuf, "%s\t%d\n", buf, &maj) == 2) {
+ *dp = (struct devdescription *)malloc(sizeof (**dp));
+ memset(*dp, 0, sizeof(**dp));
+ (*dp)->dev_name = ns(buf);
+ (*dp)->dev_major = maj;
+ dp = &(*dp)->dev_next;
+ } else {
+ fprintf(stderr,"illegal line in devices file\n");
+ break;
+ }
+ }
+ *dp = 0;
+ fclose(fp);
+ devtablenotread = 0;
+}
diff --git a/usr.sbin/config/mkubglue.c b/usr.sbin/config/mkubglue.c
new file mode 100644
index 0000000..c72766b
--- /dev/null
+++ b/usr.sbin/config/mkubglue.c
@@ -0,0 +1,198 @@
+/*-
+ * Copyright (c) 1980, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)mkubglue.c 8.1 (Berkeley) 6/6/93";
+#endif
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+/*
+ * Make the uba interrupt file ubglue.s
+ */
+#include <stdio.h>
+#include "config.h"
+#include "y.tab.h"
+
+ubglue()
+{
+ register FILE *fp;
+ register struct device *dp, *mp;
+
+ fp = fopen(path("ubglue.s"), "w");
+ if (fp == 0)
+ err(1, "%s", path("ubglue.s"));
+ for (dp = dtab; dp != 0; dp = dp->d_next) {
+ mp = dp->d_conn;
+ if (mp != 0 && mp != (struct device *)-1 &&
+ !eq(mp->d_name, "mba")) {
+ struct idlst *id, *id2;
+
+ for (id = dp->d_vec; id; id = id->id_next) {
+ for (id2 = dp->d_vec; id2; id2 = id2->id_next) {
+ if (id2 == id) {
+ dump_vec(fp, id->id, dp->d_unit);
+ break;
+ }
+ if (!strcmp(id->id, id2->id))
+ break;
+ }
+ }
+ }
+ }
+ dump_std(fp);
+ for (dp = dtab; dp != 0; dp = dp->d_next) {
+ mp = dp->d_conn;
+ if (mp != 0 && mp != (struct device *)-1 &&
+ !eq(mp->d_name, "mba")) {
+ struct idlst *id, *id2;
+
+ for (id = dp->d_vec; id; id = id->id_next) {
+ for (id2 = dp->d_vec; id2; id2 = id2->id_next) {
+ if (id2 == id) {
+ dump_intname(fp, id->id,
+ dp->d_unit);
+ break;
+ }
+ if (!strcmp(id->id, id2->id))
+ break;
+ }
+ }
+ }
+ }
+ dump_ctrs(fp);
+ (void) fclose(fp);
+}
+
+static int cntcnt = 0; /* number of interrupt counters allocated */
+
+/*
+ * print an interrupt vector
+ */
+dump_vec(fp, vector, number)
+ register FILE *fp;
+ char *vector;
+ int number;
+{
+ char nbuf[80];
+ register char *v = nbuf;
+
+ (void) sprintf(v, "%s%d", vector, number);
+ fprintf(fp, "\t.globl\t_X%s\n\t.align\t2\n_X%s:\n\tpushr\t$0x3f\n",
+ v, v);
+ fprintf(fp, "\tincl\t_fltintrcnt+(4*%d)\n", cntcnt++);
+ if (strncmp(vector, "dzx", 3) == 0)
+ fprintf(fp, "\tmovl\t$%d,r0\n\tjmp\tdzdma\n\n", number);
+ else if (strncmp(vector, "dpx", 3) == 0)
+ fprintf(fp, "\tmovl\t$%d,r0\n\tjmp\tdpxdma\n\n", number);
+ else if (strncmp(vector, "dpr", 3) == 0)
+ fprintf(fp, "\tmovl\t$%d,r0\n\tjmp\tdprdma\n\n", number);
+ else {
+ if (strncmp(vector, "uur", 3) == 0) {
+ fprintf(fp, "#ifdef UUDMA\n");
+ fprintf(fp, "\tmovl\t$%d,r0\n\tjsb\tuudma\n", number);
+ fprintf(fp, "#endif\n");
+ }
+ fprintf(fp, "\tpushl\t$%d\n", number);
+ fprintf(fp, "\tcalls\t$1,_%s\n\tpopr\t$0x3f\n", vector);
+ fprintf(fp, "\tincl\t_cnt+V_INTR\n\trei\n\n");
+ }
+}
+
+/*
+ * Start the interrupt name table with the names
+ * of the standard vectors not on the unibus.
+ * The number and order of these names should correspond
+ * with the definitions in scb.s.
+ */
+dump_std(fp)
+ register FILE *fp;
+{
+ fprintf(fp, "\n\t.globl\t_intrnames\n");
+ fprintf(fp, "\n\t.globl\t_eintrnames\n");
+ fprintf(fp, "\t.data\n");
+ fprintf(fp, "_intrnames:\n");
+ fprintf(fp, "\t.asciz\t\"clock\"\n");
+ fprintf(fp, "\t.asciz\t\"cnr\"\n");
+ fprintf(fp, "\t.asciz\t\"cnx\"\n");
+ fprintf(fp, "\t.asciz\t\"tur\"\n");
+ fprintf(fp, "\t.asciz\t\"tux\"\n");
+ fprintf(fp, "\t.asciz\t\"mba0\"\n");
+ fprintf(fp, "\t.asciz\t\"mba1\"\n");
+ fprintf(fp, "\t.asciz\t\"mba2\"\n");
+ fprintf(fp, "\t.asciz\t\"mba3\"\n");
+ fprintf(fp, "\t.asciz\t\"uba0\"\n");
+ fprintf(fp, "\t.asciz\t\"uba1\"\n");
+ fprintf(fp, "\t.asciz\t\"uba2\"\n");
+ fprintf(fp, "\t.asciz\t\"uba3\"\n");
+#define I_FIXED 13 /* number of names above */
+}
+
+dump_intname(fp, vector, number)
+ register FILE *fp;
+ char *vector;
+ int number;
+{
+ register char *cp = vector;
+
+ fprintf(fp, "\t.asciz\t\"");
+ /*
+ * Skip any "int" or "intr" in the name.
+ */
+ while (*cp)
+ if (cp[0] == 'i' && cp[1] == 'n' && cp[2] == 't') {
+ cp += 3;
+ if (*cp == 'r')
+ cp++;
+ } else {
+ putc(*cp, fp);
+ cp++;
+ }
+ fprintf(fp, "%d\"\n", number);
+}
+
+dump_ctrs(fp)
+ register FILE *fp;
+{
+ fprintf(fp, "_eintrnames:\n");
+ fprintf(fp, "\n\t.globl\t_intrcnt\n");
+ fprintf(fp, "\n\t.globl\t_eintrcnt\n");
+ fprintf(fp, "_intrcnt:\n", I_FIXED);
+ fprintf(fp, "\t.space\t4 * %d\n", I_FIXED);
+ fprintf(fp, "_fltintrcnt:\n", cntcnt);
+ fprintf(fp, "\t.space\t4 * %d\n", cntcnt);
+ fprintf(fp, "_eintrcnt:\n\n");
+ fprintf(fp, "\t.text\n");
+}
diff --git a/usr.sbin/cron/Makefile b/usr.sbin/cron/Makefile
new file mode 100644
index 0000000..1131cff
--- /dev/null
+++ b/usr.sbin/cron/Makefile
@@ -0,0 +1,3 @@
+SUBDIR= lib cron crontab
+
+.include <bsd.subdir.mk>
diff --git a/usr.sbin/cron/cron/Makefile b/usr.sbin/cron/cron/Makefile
new file mode 100644
index 0000000..ddb06a8
--- /dev/null
+++ b/usr.sbin/cron/cron/Makefile
@@ -0,0 +1,22 @@
+BINDIR?= /usr/sbin
+
+PROG= cron
+SRCS= cron.c database.c do_command.c job.c user.c popen.c
+MAN8= cron.8
+
+CFLAGS+=-DLOGIN_CAP
+
+.if exists(${.OBJDIR}/../lib)
+LDDESTDIR+= -L${.OBJDIR}/../lib
+DPADD+= ${.OBJDIR}/../lib/libcron.a
+.else
+LDDESTDIR+= -L${.CURDIR}/../lib
+DPADD+= ${.CURDIR}/../lib/libcron.a
+.endif
+
+LDADD+= -lcron
+
+LDADD+= -lutil
+DPADD+= ${LIBUTIL}
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/cron/cron/compat.h b/usr.sbin/cron/cron/compat.h
new file mode 100644
index 0000000..e286e0f
--- /dev/null
+++ b/usr.sbin/cron/cron/compat.h
@@ -0,0 +1,140 @@
+/* Copyright 1993,1994 by Paul Vixie
+ * All rights reserved
+ *
+ * Distribute freely, except: don't remove my name from the source or
+ * documentation (don't take credit for my work), mark your changes (don't
+ * get me blamed for your possible bugs), don't alter or remove this
+ * notice. May be sold if buildable source is provided to buyer. No
+ * warrantee of any kind, express or implied, is included with this
+ * software; use at your own risk, responsibility for damages (if any) to
+ * anyone resulting from the use of this software rests entirely with the
+ * user.
+ *
+ * Send bug reports, bug fixes, enhancements, requests, flames, etc., and
+ * I'll try to keep a version up to date. I can be reached as follows:
+ * Paul Vixie <paul@vix.com> uunet!decwrl!vixie!paul
+ */
+
+/*
+ * $Id$
+ */
+
+#ifndef __P
+# ifdef __STDC__
+# define __P(x) x
+# else
+# define __P(x) ()
+# define const
+# endif
+#endif
+
+#if defined(UNIXPC) || defined(unixpc)
+# define UNIXPC 1
+# define ATT 1
+#endif
+
+#if defined(hpux) || defined(_hpux) || defined(__hpux)
+# define HPUX 1
+# define seteuid(e) setresuid(-1,e,-1)
+# define setreuid(r,e) setresuid(r,e,-1)
+#endif
+
+#if defined(_IBMR2)
+# define AIX 1
+#endif
+
+#if defined(__convex__)
+# define CONVEX 1
+#endif
+
+#if defined(sgi) || defined(_sgi) || defined(__sgi)
+# define IRIX 1
+/* IRIX 4 hdrs are broken: one cannot #include both <stdio.h>
+ * and <stdlib.h> because they disagree on system(), perror().
+ * Therefore we must zap the "const" keyword BEFORE including
+ * either of them.
+ */
+# define const
+#endif
+
+#if defined(_UNICOS)
+# define UNICOS 1
+#endif
+
+#ifndef POSIX
+# if (BSD >= 199103) || defined(__linux) || defined(ultrix) || defined(AIX) ||\
+ defined(HPUX) || defined(CONVEX) || defined(IRIX)
+# define POSIX
+# endif
+#endif
+
+#ifndef BSD
+# if defined(ultrix)
+# define BSD 198902
+# endif
+#endif
+
+/*****************************************************************/
+
+#if !defined(BSD) && !defined(HPUX) && !defined(CONVEX) && !defined(__linux)
+# define NEED_VFORK
+#endif
+
+#if (!defined(BSD) || (BSD < 198902)) && !defined(__linux) && \
+ !defined(IRIX) && !defined(NeXT) && !defined(HPUX)
+# define NEED_STRCASECMP
+#endif
+
+#if (!defined(BSD) || (BSD < 198911)) && !defined(__linux) &&\
+ !defined(IRIX) && !defined(UNICOS) && !defined(HPUX)
+# define NEED_STRDUP
+#endif
+
+#if (!defined(BSD) || (BSD < 198911)) && !defined(POSIX) && !defined(NeXT)
+# define NEED_STRERROR
+#endif
+
+#if defined(HPUX) || defined(AIX) || defined(UNIXPC)
+# define NEED_FLOCK
+#endif
+
+#ifndef POSIX
+# define NEED_SETSID
+#endif
+
+#if (defined(POSIX) && !defined(BSD)) && !defined(__linux)
+# define NEED_GETDTABLESIZE
+#endif
+
+#ifdef POSIX
+#include <unistd.h>
+#ifdef _POSIX_SAVED_IDS
+# define HAVE_SAVED_UIDS
+#endif
+#endif
+
+#if !defined(ATT) && !defined(__linux) && !defined(IRIX) && !defined(UNICOS)
+# define USE_SIGCHLD
+#endif
+
+#if !defined(AIX) && !defined(UNICOS)
+# define SYS_TIME_H 1
+#else
+# define SYS_TIME_H 0
+#endif
+
+#if defined(BSD) && !defined(POSIX)
+# define USE_UTIMES
+#endif
+
+#if defined(AIX) || defined(HPUX) || defined(IRIX)
+# define NEED_SETENV
+#endif
+
+#if !defined(UNICOS) && !defined(UNIXPC)
+# define HAS_FCHOWN
+#endif
+
+#if !defined(UNICOS) && !defined(UNIXPC)
+# define HAS_FCHMOD
+#endif
diff --git a/usr.sbin/cron/cron/config.h b/usr.sbin/cron/cron/config.h
new file mode 100644
index 0000000..d704888
--- /dev/null
+++ b/usr.sbin/cron/cron/config.h
@@ -0,0 +1,87 @@
+/* Copyright 1988,1990,1993,1994 by Paul Vixie
+ * All rights reserved
+ *
+ * Distribute freely, except: don't remove my name from the source or
+ * documentation (don't take credit for my work), mark your changes (don't
+ * get me blamed for your possible bugs), don't alter or remove this
+ * notice. May be sold if buildable source is provided to buyer. No
+ * warrantee of any kind, express or implied, is included with this
+ * software; use at your own risk, responsibility for damages (if any) to
+ * anyone resulting from the use of this software rests entirely with the
+ * user.
+ *
+ * Send bug reports, bug fixes, enhancements, requests, flames, etc., and
+ * I'll try to keep a version up to date. I can be reached as follows:
+ * Paul Vixie <paul@vix.com> uunet!decwrl!vixie!paul
+ */
+
+/* config.h - configurables for Vixie Cron
+ *
+ * $Id$
+ */
+
+#if !defined(_PATH_SENDMAIL)
+# define _PATH_SENDMAIL "/usr/lib/sendmail"
+#endif /*SENDMAIL*/
+
+/*
+ * these are site-dependent
+ */
+
+#ifndef DEBUGGING
+#define DEBUGGING 1 /* 1 or 0 -- do you want debugging code built in? */
+#endif
+
+ /*
+ * choose one of these MAILCMD commands. I use
+ * /bin/mail for speed; it makes biff bark but doesn't
+ * do aliasing. /usr/lib/sendmail does aliasing but is
+ * a hog for short messages. aliasing is not needed
+ * if you make use of the MAILTO= feature in crontabs.
+ * (hint: MAILTO= was added for this reason).
+ */
+
+#define MAILCMD _PATH_SENDMAIL /*-*/
+#define MAILARGS "%s -FCronDaemon -odi -oem -oi -t" /*-*/
+ /* -Fx = set full-name of sender
+ * -odi = Option Deliverymode Interactive
+ * -oem = Option Errors Mailedtosender
+ * -oi = Option dot message terminator
+ * -t = read recipients from header of message
+ */
+
+/* #define MAILCMD "/bin/mail" */ /*-*/
+/* #define MAILARGS "%s -d %s" */ /*-*/
+ /* -d = undocumented but common flag: deliver locally?
+ */
+
+/* #define MAILCMD "/usr/mmdf/bin/submit" */ /*-*/
+/* #define MAILARGS "%s -mlrxto %s" */ /*-*/
+
+/* #define MAIL_DATE */ /*-*/
+ /* should we include an ersatz Date: header in
+ * generated mail? if you are using sendmail
+ * for MAILCMD, it is better to let sendmail
+ * generate the Date: header.
+ */
+
+ /* if ALLOW_FILE and DENY_FILE are not defined or are
+ * defined but neither exists, should crontab(1) be
+ * usable only by root?
+ */
+/* #define ALLOW_ONLY_ROOT */ /*-*/
+
+ /* if you want to use syslog(3) instead of appending
+ * to CRONDIR/LOG_FILE (/var/cron/log, e.g.), define
+ * SYSLOG here. Note that quite a bit of logging
+ * info is written, and that you probably don't want
+ * to use this on 4.2bsd since everything goes in
+ * /usr/spool/mqueue/syslog. On 4.[34]bsd you can
+ * tell /etc/syslog.conf to send cron's logging to
+ * a separate file.
+ *
+ * Note that if this and LOG_FILE in "pathnames.h"
+ * are both defined, then logging will go to both
+ * places.
+ */
+#define SYSLOG /*-*/
diff --git a/usr.sbin/cron/cron/cron.8 b/usr.sbin/cron/cron/cron.8
new file mode 100644
index 0000000..dddea8f
--- /dev/null
+++ b/usr.sbin/cron/cron/cron.8
@@ -0,0 +1,75 @@
+.\"/* Copyright 1988,1990,1993 by Paul Vixie
+.\" * All rights reserved
+.\" *
+.\" * Distribute freely, except: don't remove my name from the source or
+.\" * documentation (don't take credit for my work), mark your changes (don't
+.\" * get me blamed for your possible bugs), don't alter or remove this
+.\" * notice. May be sold if buildable source is provided to buyer. No
+.\" * warrantee of any kind, express or implied, is included with this
+.\" * software; use at your own risk, responsibility for damages (if any) to
+.\" * anyone resulting from the use of this software rests entirely with the
+.\" * user.
+.\" *
+.\" * Send bug reports, bug fixes, enhancements, requests, flames, etc., and
+.\" * I'll try to keep a version up to date. I can be reached as follows:
+.\" * Paul Vixie <paul@vix.com> uunet!decwrl!vixie!paul
+.\" */
+.\"
+.\" $Id: cron.8,v 1.4 1997/02/22 16:04:39 peter Exp $
+.\"
+.Dd December 20, 1993
+.Dt CRON 8
+.Os
+.Sh NAME
+.Nm cron
+.Nd daemon to execute scheduled commands (Vixie Cron)
+.Sh SYNOPSIS
+.Nm cron
+.Op Fl x Ar debugflag Ns Op ,...
+.Sh DESCRIPTION
+.Nm Cron
+should be started from
+.Pa /etc/rc
+or
+.Pa /etc/rc.local .
+It will return immediately,
+so you don't need to start it with '&'.
+.Pp
+.Nm Cron
+searches
+.Pa /var/cron/tabs
+for crontab files which are named after accounts in
+.Pa /etc/passwd ;
+crontabs found are loaded into memory.
+.Nm Cron
+also searches for
+.Pa /etc/crontab
+which is in a different format (see
+.Xr crontab 5 ).
+.Nm Cron
+then wakes up every minute, examining all stored crontabs, checking each
+command to see if it should be run in the current minute. When executing
+commands, any output is mailed to the owner of the crontab (or to the user
+named in the
+.Ev MAILTO
+environment variable in the crontab, if such exists).
+.Pp
+Additionally,
+.Nm
+checks each minute to see if its spool directory's modtime (or the modtime
+on
+.Pa /etc/crontab )
+has changed, and if it has,
+.Nm
+will then examine the modtime on all crontabs and reload those which have
+changed. Thus
+.Nm
+need not be restarted whenever a crontab file is modified. Note that the
+.Xr crontab 1
+command updates the modtime of the spool directory whenever it changes a
+crontab.
+.Sh SEE ALSO
+.Xr crontab 1 ,
+.Xr crontab 5
+.Sh AUTHOR
+.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..0b997ca
--- /dev/null
+++ b/usr.sbin/cron/cron/cron.c
@@ -0,0 +1,310 @@
+/* Copyright 1988,1990,1993,1994 by Paul Vixie
+ * All rights reserved
+ *
+ * Distribute freely, except: don't remove my name from the source or
+ * documentation (don't take credit for my work), mark your changes (don't
+ * get me blamed for your possible bugs), don't alter or remove this
+ * notice. May be sold if buildable source is provided to buyer. No
+ * warrantee of any kind, express or implied, is included with this
+ * software; use at your own risk, responsibility for damages (if any) to
+ * anyone resulting from the use of this software rests entirely with the
+ * user.
+ *
+ * Send bug reports, bug fixes, enhancements, requests, flames, etc., and
+ * I'll try to keep a version up to date. I can be reached as follows:
+ * Paul Vixie <paul@vix.com> uunet!decwrl!vixie!paul
+ */
+
+#if !defined(lint) && !defined(LINT)
+static const char rcsid[] =
+ "$Id: cron.c,v 1.5 1997/03/31 05:09:54 imp Exp $";
+#endif
+
+#define MAIN_PROGRAM
+
+
+#include "cron.h"
+#include <sys/signal.h>
+#if SYS_TIME_H
+# include <sys/time.h>
+#else
+# include <time.h>
+#endif
+
+
+static void usage __P((void)),
+ run_reboot_jobs __P((cron_db *)),
+ cron_tick __P((cron_db *)),
+ cron_sync __P((void)),
+ cron_sleep __P((void)),
+#ifdef USE_SIGCHLD
+ sigchld_handler __P((int)),
+#endif
+ sighup_handler __P((int)),
+ parse_args __P((int c, char *v[]));
+
+
+static void
+usage() {
+ char **dflags;
+
+ fprintf(stderr, "usage: cron [-x debugflag[,...]]\n");
+ fprintf(stderr, "\ndebugflags: ");
+
+ for(dflags = DebugFlagNames; *dflags; dflags++) {
+ fprintf(stderr, "%s ", *dflags);
+ }
+ fprintf(stderr, "\n");
+
+ exit(ERROR_EXIT);
+}
+
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ cron_db database;
+
+ ProgramName = argv[0];
+
+#if defined(BSD)
+ setlinebuf(stdout);
+ setlinebuf(stderr);
+#endif
+
+ parse_args(argc, argv);
+
+#ifdef USE_SIGCHLD
+ (void) signal(SIGCHLD, sigchld_handler);
+#else
+ (void) signal(SIGCLD, SIG_IGN);
+#endif
+ (void) signal(SIGHUP, sighup_handler);
+
+ acquire_daemonlock(0);
+ set_cron_uid();
+ set_cron_cwd();
+
+#if defined(POSIX)
+ setenv("PATH", _PATH_DEFPATH, 1);
+#endif
+
+ /* if there are no debug flags turned on, fork as a daemon should.
+ */
+# if DEBUGGING
+ if (DebugFlags) {
+# else
+ if (0) {
+# endif
+ (void) fprintf(stderr, "[%d] cron started\n", getpid());
+ } else {
+ switch (fork()) {
+ case -1:
+ log_it("CRON",getpid(),"DEATH","can't fork");
+ exit(0);
+ break;
+ case 0:
+ /* child process */
+ log_it("CRON",getpid(),"STARTUP","fork ok");
+ (void) setsid();
+ break;
+ default:
+ /* parent process should just die */
+ _exit(0);
+ }
+ }
+
+ acquire_daemonlock(0);
+ database.head = NULL;
+ database.tail = NULL;
+ database.mtime = (time_t) 0;
+ load_database(&database);
+ run_reboot_jobs(&database);
+ cron_sync();
+ while (TRUE) {
+# if DEBUGGING
+ /* if (!(DebugFlags & DTEST)) */
+# endif /*DEBUGGING*/
+ cron_sleep();
+
+ load_database(&database);
+
+ /* do this iteration
+ */
+ cron_tick(&database);
+
+ /* sleep 1 minute
+ */
+ TargetTime += 60;
+ }
+}
+
+
+static void
+run_reboot_jobs(db)
+ cron_db *db;
+{
+ register user *u;
+ register entry *e;
+
+ for (u = db->head; u != NULL; u = u->next) {
+ for (e = u->crontab; e != NULL; e = e->next) {
+ if (e->flags & WHEN_REBOOT) {
+ job_add(e, u);
+ }
+ }
+ }
+ (void) job_runqueue();
+}
+
+
+static void
+cron_tick(db)
+ cron_db *db;
+{
+ register struct tm *tm = localtime(&TargetTime);
+ register int minute, hour, dom, month, dow;
+ register user *u;
+ register entry *e;
+
+ /* make 0-based values out of these so we can use them as indicies
+ */
+ minute = tm->tm_min -FIRST_MINUTE;
+ hour = tm->tm_hour -FIRST_HOUR;
+ dom = tm->tm_mday -FIRST_DOM;
+ month = tm->tm_mon +1 /* 0..11 -> 1..12 */ -FIRST_MONTH;
+ dow = tm->tm_wday -FIRST_DOW;
+
+ Debug(DSCH, ("[%d] tick(%d,%d,%d,%d,%d)\n",
+ getpid(), minute, hour, dom, month, dow))
+
+ /* the dom/dow situation is odd. '* * 1,15 * Sun' will run on the
+ * first and fifteenth AND every Sunday; '* * * * Sun' will run *only*
+ * on Sundays; '* * 1,15 * *' will run *only* the 1st and 15th. this
+ * is why we keep 'e->dow_star' and 'e->dom_star'. yes, it's bizarre.
+ * like many bizarre things, it's the standard.
+ */
+ for (u = db->head; u != NULL; u = u->next) {
+ for (e = u->crontab; e != NULL; e = e->next) {
+ Debug(DSCH|DEXT, ("user [%s:%d:%d:...] cmd=\"%s\"\n",
+ env_get("LOGNAME", e->envp),
+ e->uid, e->gid, e->cmd))
+ if (bit_test(e->minute, minute)
+ && bit_test(e->hour, hour)
+ && bit_test(e->month, month)
+ && ( ((e->flags & DOM_STAR) || (e->flags & DOW_STAR))
+ ? (bit_test(e->dow,dow) && bit_test(e->dom,dom))
+ : (bit_test(e->dow,dow) || bit_test(e->dom,dom))
+ )
+ ) {
+ job_add(e, u);
+ }
+ }
+ }
+}
+
+
+/* the task here is to figure out how long it's going to be until :00 of the
+ * following minute and initialize TargetTime to this value. TargetTime
+ * will subsequently slide 60 seconds at a time, with correction applied
+ * implicitly in cron_sleep(). it would be nice to let cron execute in
+ * the "current minute" before going to sleep, but by restarting cron you
+ * could then get it to execute a given minute's jobs more than once.
+ * instead we have the chance of missing a minute's jobs completely, but
+ * that's something sysadmin's know to expect what with crashing computers..
+ */
+static void
+cron_sync() {
+ register struct tm *tm;
+
+ TargetTime = time((time_t*)0);
+ tm = localtime(&TargetTime);
+ TargetTime += (60 - tm->tm_sec);
+}
+
+
+static void
+cron_sleep() {
+ register int seconds_to_wait;
+
+ do {
+ seconds_to_wait = (int) (TargetTime - time((time_t*)0));
+ Debug(DSCH, ("[%d] TargetTime=%ld, sec-to-wait=%d\n",
+ getpid(), TargetTime, seconds_to_wait))
+
+ /* if we intend to sleep, this means that it's finally
+ * time to empty the job queue (execute it).
+ *
+ * if we run any jobs, we'll probably screw up our timing,
+ * so go recompute.
+ *
+ * note that we depend here on the left-to-right nature
+ * of &&, and the short-circuiting.
+ */
+ } while (seconds_to_wait > 0 && job_runqueue());
+
+ while (seconds_to_wait > 0) {
+ Debug(DSCH, ("[%d] sleeping for %d seconds\n",
+ getpid(), seconds_to_wait))
+ seconds_to_wait = (int) sleep((unsigned int) seconds_to_wait);
+ }
+}
+
+
+#ifdef USE_SIGCHLD
+static void
+sigchld_handler(x) {
+ WAIT_T waiter;
+ PID_T pid;
+
+ for (;;) {
+#ifdef POSIX
+ pid = waitpid(-1, &waiter, WNOHANG);
+#else
+ pid = wait3(&waiter, WNOHANG, (struct rusage *)0);
+#endif
+ switch (pid) {
+ case -1:
+ Debug(DPROC,
+ ("[%d] sigchld...no children\n", getpid()))
+ return;
+ case 0:
+ Debug(DPROC,
+ ("[%d] sigchld...no dead kids\n", getpid()))
+ return;
+ default:
+ Debug(DPROC,
+ ("[%d] sigchld...pid #%d died, stat=%d\n",
+ getpid(), pid, WEXITSTATUS(waiter)))
+ }
+ }
+}
+#endif /*USE_SIGCHLD*/
+
+
+static void
+sighup_handler(x) {
+ log_close();
+}
+
+
+static void
+parse_args(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int argch;
+
+ while ((argch = getopt(argc, argv, "x:")) != -1) {
+ switch (argch) {
+ case 'x':
+ if (!set_debug_flags(optarg))
+ usage();
+ break;
+ default:
+ usage();
+ }
+ }
+}
diff --git a/usr.sbin/cron/cron/cron.h b/usr.sbin/cron/cron/cron.h
new file mode 100644
index 0000000..9513271
--- /dev/null
+++ b/usr.sbin/cron/cron/cron.h
@@ -0,0 +1,289 @@
+/* Copyright 1988,1990,1993,1994 by Paul Vixie
+ * All rights reserved
+ *
+ * Distribute freely, except: don't remove my name from the source or
+ * documentation (don't take credit for my work), mark your changes (don't
+ * get me blamed for your possible bugs), don't alter or remove this
+ * notice. May be sold if buildable source is provided to buyer. No
+ * warrantee of any kind, express or implied, is included with this
+ * software; use at your own risk, responsibility for damages (if any) to
+ * anyone resulting from the use of this software rests entirely with the
+ * user.
+ *
+ * Send bug reports, bug fixes, enhancements, requests, flames, etc., and
+ * I'll try to keep a version up to date. I can be reached as follows:
+ * Paul Vixie <paul@vix.com> uunet!decwrl!vixie!paul
+ */
+
+/* cron.h - header for vixie's cron
+ *
+ * $Id: cron.h,v 1.7 1997/09/15 06:39:04 charnier Exp $
+ *
+ * vix 14nov88 [rest of log is in RCS]
+ * vix 14jan87 [0 or 7 can be sunday; thanks, mwm@berkeley]
+ * vix 30dec86 [written]
+ */
+
+/* reorder these #include's at your peril */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include "compat.h"
+
+#include <bitstring.h>
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdio.h>
+#include <time.h>
+#include <sys/wait.h>
+
+#include "pathnames.h"
+#include "config.h"
+#include "externs.h"
+
+ /* these are really immutable, and are
+ * defined for symbolic convenience only
+ * TRUE, FALSE, and ERR must be distinct
+ * ERR must be < OK.
+ */
+#define TRUE 1
+#define FALSE 0
+ /* system calls return this on success */
+#define OK 0
+ /* or this on error */
+#define ERR (-1)
+
+ /* turn this on to get '-x' code */
+#ifndef DEBUGGING
+#define DEBUGGING FALSE
+#endif
+
+#define READ_PIPE 0 /* which end of a pipe pair do you read? */
+#define WRITE_PIPE 1 /* or write to? */
+#define STDIN 0 /* what is stdin's file descriptor? */
+#define STDOUT 1 /* stdout's? */
+#define STDERR 2 /* stderr's? */
+#define ERROR_EXIT 1 /* exit() with this will scare the shell */
+#define OK_EXIT 0 /* exit() with this is considered 'normal' */
+#define MAX_FNAME 100 /* max length of internally generated fn */
+#define MAX_COMMAND 1000 /* max length of internally generated cmd */
+#define MAX_ENVSTR 1000 /* max length of envvar=value\0 strings */
+#define MAX_TEMPSTR 100 /* obvious */
+#define MAX_UNAME 20 /* max length of username, should be overkill */
+#define ROOT_UID 0 /* don't change this, it really must be root */
+#define ROOT_USER "root" /* ditto */
+
+ /* NOTE: these correspond to DebugFlagNames,
+ * defined below.
+ */
+#define DEXT 0x0001 /* extend flag for other debug masks */
+#define DSCH 0x0002 /* scheduling debug mask */
+#define DPROC 0x0004 /* process control debug mask */
+#define DPARS 0x0008 /* parsing debug mask */
+#define DLOAD 0x0010 /* database loading debug mask */
+#define DMISC 0x0020 /* misc debug mask */
+#define DTEST 0x0040 /* test mode: don't execute any commands */
+#define DBIT 0x0080 /* bit twiddling shown (long) */
+
+#define CRON_TAB(u) "%s/%s", SPOOL_DIR, u
+#define REG register
+#define PPC_NULL ((char **)NULL)
+
+#ifndef MAXHOSTNAMELEN
+#define MAXHOSTNAMELEN 64
+#endif
+
+#define Skip_Blanks(c, f) \
+ while (c == '\t' || c == ' ') \
+ c = get_char(f);
+
+#define Skip_Nonblanks(c, f) \
+ while (c!='\t' && c!=' ' && c!='\n' && c != EOF) \
+ c = get_char(f);
+
+#define Skip_Line(c, f) \
+ do {c = get_char(f);} while (c != '\n' && c != EOF);
+
+#if DEBUGGING
+# define Debug(mask, message) \
+ if ( (DebugFlags & (mask) ) == (mask) ) \
+ printf message;
+#else /* !DEBUGGING */
+# define Debug(mask, message) \
+ ;
+#endif /* DEBUGGING */
+
+#define MkLower(ch) (isupper(ch) ? tolower(ch) : ch)
+#define MkUpper(ch) (islower(ch) ? toupper(ch) : ch)
+#define Set_LineNum(ln) {Debug(DPARS|DEXT,("linenum=%d\n",ln)); \
+ LineNumber = ln; \
+ }
+
+#define FIRST_MINUTE 0
+#define LAST_MINUTE 59
+#define MINUTE_COUNT (LAST_MINUTE - FIRST_MINUTE + 1)
+
+#define FIRST_HOUR 0
+#define LAST_HOUR 23
+#define HOUR_COUNT (LAST_HOUR - FIRST_HOUR + 1)
+
+#define FIRST_DOM 1
+#define LAST_DOM 31
+#define DOM_COUNT (LAST_DOM - FIRST_DOM + 1)
+
+#define FIRST_MONTH 1
+#define LAST_MONTH 12
+#define MONTH_COUNT (LAST_MONTH - FIRST_MONTH + 1)
+
+/* note on DOW: 0 and 7 are both Sunday, for compatibility reasons. */
+#define FIRST_DOW 0
+#define LAST_DOW 7
+#define DOW_COUNT (LAST_DOW - FIRST_DOW + 1)
+
+#ifdef LOGIN_CAP
+/* see init.c */
+#define RESOURCE_RC "daemon"
+#endif
+
+ /* each user's crontab will be held as a list of
+ * the following structure.
+ *
+ * These are the cron commands.
+ */
+
+typedef struct _entry {
+ struct _entry *next;
+ uid_t uid;
+ gid_t gid;
+#ifdef LOGIN_CAP
+ char *class;
+#endif
+ char **envp;
+ char *cmd;
+ bitstr_t bit_decl(minute, MINUTE_COUNT);
+ bitstr_t bit_decl(hour, HOUR_COUNT);
+ bitstr_t bit_decl(dom, DOM_COUNT);
+ bitstr_t bit_decl(month, MONTH_COUNT);
+ bitstr_t bit_decl(dow, DOW_COUNT);
+ int flags;
+#define DOM_STAR 0x01
+#define DOW_STAR 0x02
+#define WHEN_REBOOT 0x04
+} entry;
+
+ /* the crontab database will be a list of the
+ * following structure, one element per user
+ * plus one for the system.
+ *
+ * These are the crontabs.
+ */
+
+typedef struct _user {
+ struct _user *next, *prev; /* links */
+ char *name;
+ time_t mtime; /* last modtime of crontab */
+ entry *crontab; /* this person's crontab */
+} user;
+
+typedef struct _cron_db {
+ user *head, *tail; /* links */
+ time_t mtime; /* last modtime on spooldir */
+} cron_db;
+
+
+void set_cron_uid __P((void)),
+ set_cron_cwd __P((void)),
+ load_database __P((cron_db *)),
+ open_logfile __P((void)),
+ sigpipe_func __P((void)),
+ job_add __P((entry *, user *)),
+ do_command __P((entry *, user *)),
+ link_user __P((cron_db *, user *)),
+ unlink_user __P((cron_db *, user *)),
+ free_user __P((user *)),
+ env_free __P((char **)),
+ unget_char __P((int, FILE *)),
+ free_entry __P((entry *)),
+ acquire_daemonlock __P((int)),
+ skip_comments __P((FILE *)),
+ log_it __P((char *, int, char *, char *)),
+ log_close __P((void));
+
+int job_runqueue __P((void)),
+ set_debug_flags __P((char *)),
+ get_char __P((FILE *)),
+ get_string __P((char *, int, FILE *, char *)),
+ swap_uids __P((void)),
+ load_env __P((char *, FILE *)),
+ cron_pclose __P((FILE *)),
+ strcmp_until __P((char *, char *, int)),
+ allowed __P((char *)),
+ strdtb __P((char *));
+
+char *env_get __P((char *, char **)),
+ *arpadate __P((time_t *)),
+ *mkprints __P((unsigned char *, unsigned int)),
+ *first_word __P((char *, char *)),
+ **env_init __P((void)),
+ **env_copy __P((char **)),
+ **env_set __P((char **, char *));
+
+user *load_user __P((int, struct passwd *, char *)),
+ *find_user __P((cron_db *, char *));
+
+entry *load_entry __P((FILE *, void (*)(),
+ struct passwd *, char **));
+
+FILE *cron_popen __P((char *, char *));
+
+
+ /* in the C tradition, we only create
+ * variables for the main program, just
+ * extern them elsewhere.
+ */
+
+#ifdef MAIN_PROGRAM
+# if !defined(LINT) && !defined(lint)
+char *copyright[] = {
+ "@(#) Copyright 1988,1989,1990,1993,1994 by Paul Vixie",
+ "@(#) All rights reserved"
+ };
+# endif
+
+char *MonthNames[] = {
+ "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec",
+ NULL
+ };
+
+char *DowNames[] = {
+ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun",
+ NULL
+ };
+
+char *ProgramName;
+int LineNumber;
+time_t TargetTime;
+
+# if DEBUGGING
+int DebugFlags;
+char *DebugFlagNames[] = { /* sync with #defines */
+ "ext", "sch", "proc", "pars", "load", "misc", "test", "bit",
+ NULL /* NULL must be last element */
+ };
+# endif /* DEBUGGING */
+#else /*MAIN_PROGRAM*/
+extern char *copyright[],
+ *MonthNames[],
+ *DowNames[],
+ *ProgramName;
+extern int LineNumber;
+extern time_t TargetTime;
+# if DEBUGGING
+extern int DebugFlags;
+extern char *DebugFlagNames[];
+# endif /* DEBUGGING */
+#endif /*MAIN_PROGRAM*/
diff --git a/usr.sbin/cron/cron/database.c b/usr.sbin/cron/cron/database.c
new file mode 100644
index 0000000..7d8bf6f
--- /dev/null
+++ b/usr.sbin/cron/cron/database.c
@@ -0,0 +1,263 @@
+/* Copyright 1988,1990,1993,1994 by Paul Vixie
+ * All rights reserved
+ *
+ * Distribute freely, except: don't remove my name from the source or
+ * documentation (don't take credit for my work), mark your changes (don't
+ * get me blamed for your possible bugs), don't alter or remove this
+ * notice. May be sold if buildable source is provided to buyer. No
+ * warrantee of any kind, express or implied, is included with this
+ * software; use at your own risk, responsibility for damages (if any) to
+ * anyone resulting from the use of this software rests entirely with the
+ * user.
+ *
+ * Send bug reports, bug fixes, enhancements, requests, flames, etc., and
+ * I'll try to keep a version up to date. I can be reached as follows:
+ * Paul Vixie <paul@vix.com> uunet!decwrl!vixie!paul
+ */
+
+#if !defined(lint) && !defined(LINT)
+static const char rcsid[] =
+ "$Id: database.c,v 1.6 1997/02/22 16:04:42 peter Exp $";
+#endif
+
+/* vix 26jan87 [RCS has the log]
+ */
+
+
+#include "cron.h"
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/file.h>
+
+
+#define TMAX(a,b) ((a)>(b)?(a):(b))
+
+
+static void process_crontab __P((char *, char *, char *,
+ struct stat *,
+ cron_db *, cron_db *));
+
+
+void
+load_database(old_db)
+ cron_db *old_db;
+{
+ DIR *dir;
+ struct stat statbuf;
+ struct stat syscron_stat;
+ DIR_T *dp;
+ cron_db new_db;
+ user *u, *nu;
+
+ Debug(DLOAD, ("[%d] load_database()\n", getpid()))
+
+ /* before we start loading any data, do a stat on SPOOL_DIR
+ * so that if anything changes as of this moment (i.e., before we've
+ * cached any of the database), we'll see the changes next time.
+ */
+ if (stat(SPOOL_DIR, &statbuf) < OK) {
+ log_it("CRON", getpid(), "STAT FAILED", SPOOL_DIR);
+ (void) exit(ERROR_EXIT);
+ }
+
+ /* track system crontab file
+ */
+ if (stat(SYSCRONTAB, &syscron_stat) < OK)
+ syscron_stat.st_mtime = 0;
+
+ /* if spooldir's mtime has not changed, we don't need to fiddle with
+ * the database.
+ *
+ * Note that old_db->mtime is initialized to 0 in main(), and
+ * so is guaranteed to be different than the stat() mtime the first
+ * time this function is called.
+ */
+ if (old_db->mtime == TMAX(statbuf.st_mtime, syscron_stat.st_mtime)) {
+ Debug(DLOAD, ("[%d] spool dir mtime unch, no load needed.\n",
+ getpid()))
+ return;
+ }
+
+ /* something's different. make a new database, moving unchanged
+ * elements from the old database, reloading elements that have
+ * actually changed. Whatever is left in the old database when
+ * we're done is chaff -- crontabs that disappeared.
+ */
+ new_db.mtime = TMAX(statbuf.st_mtime, syscron_stat.st_mtime);
+ new_db.head = new_db.tail = NULL;
+
+ if (syscron_stat.st_mtime) {
+ process_crontab("root", "*system*",
+ SYSCRONTAB, &syscron_stat,
+ &new_db, old_db);
+ }
+
+ /* we used to keep this dir open all the time, for the sake of
+ * efficiency. however, we need to close it in every fork, and
+ * we fork a lot more often than the mtime of the dir changes.
+ */
+ if (!(dir = opendir(SPOOL_DIR))) {
+ log_it("CRON", getpid(), "OPENDIR FAILED", SPOOL_DIR);
+ (void) exit(ERROR_EXIT);
+ }
+
+ while (NULL != (dp = readdir(dir))) {
+ char fname[MAXNAMLEN+1],
+ tabname[MAXNAMLEN+1];
+
+ /* avoid file names beginning with ".". this is good
+ * because we would otherwise waste two guaranteed calls
+ * to getpwnam() for . and .., and also because user names
+ * starting with a period are just too nasty to consider.
+ */
+ if (dp->d_name[0] == '.')
+ continue;
+
+ (void) strncpy(fname, dp->d_name, sizeof(fname));
+ fname[sizeof(fname)-1] = '\0';
+ (void) snprintf(tabname, sizeof tabname, CRON_TAB(fname));
+
+ process_crontab(fname, fname, tabname,
+ &statbuf, &new_db, old_db);
+ }
+ closedir(dir);
+
+ /* if we don't do this, then when our children eventually call
+ * getpwnam() in do_command.c's child_process to verify MAILTO=,
+ * they will screw us up (and v-v).
+ */
+ endpwent();
+
+ /* whatever's left in the old database is now junk.
+ */
+ Debug(DLOAD, ("unlinking old database:\n"))
+ for (u = old_db->head; u != NULL; u = nu) {
+ Debug(DLOAD, ("\t%s\n", u->name))
+ nu = u->next;
+ unlink_user(old_db, u);
+ free_user(u);
+ }
+
+ /* overwrite the database control block with the new one.
+ */
+ *old_db = new_db;
+ Debug(DLOAD, ("load_database is done\n"))
+}
+
+
+void
+link_user(db, u)
+ cron_db *db;
+ user *u;
+{
+ if (db->head == NULL)
+ db->head = u;
+ if (db->tail)
+ db->tail->next = u;
+ u->prev = db->tail;
+ u->next = NULL;
+ db->tail = u;
+}
+
+
+void
+unlink_user(db, u)
+ cron_db *db;
+ user *u;
+{
+ if (u->prev == NULL)
+ db->head = u->next;
+ else
+ u->prev->next = u->next;
+
+ if (u->next == NULL)
+ db->tail = u->prev;
+ else
+ u->next->prev = u->prev;
+}
+
+
+user *
+find_user(db, name)
+ cron_db *db;
+ char *name;
+{
+ char *env_get();
+ user *u;
+
+ for (u = db->head; u != NULL; u = u->next)
+ if (!strcmp(u->name, name))
+ break;
+ return u;
+}
+
+
+static void
+process_crontab(uname, fname, tabname, statbuf, new_db, old_db)
+ char *uname;
+ char *fname;
+ char *tabname;
+ struct stat *statbuf;
+ cron_db *new_db;
+ cron_db *old_db;
+{
+ struct passwd *pw = NULL;
+ int crontab_fd = OK - 1;
+ user *u;
+
+ if (strcmp(fname, "*system*") && !(pw = getpwnam(uname))) {
+ /* file doesn't have a user in passwd file.
+ */
+ log_it(fname, getpid(), "ORPHAN", "no passwd entry");
+ goto next_crontab;
+ }
+
+ if ((crontab_fd = open(tabname, O_RDONLY, 0)) < OK) {
+ /* crontab not accessible?
+ */
+ log_it(fname, getpid(), "CAN'T OPEN", tabname);
+ goto next_crontab;
+ }
+
+ if (fstat(crontab_fd, statbuf) < OK) {
+ log_it(fname, getpid(), "FSTAT FAILED", tabname);
+ goto next_crontab;
+ }
+
+ Debug(DLOAD, ("\t%s:", fname))
+ u = find_user(old_db, fname);
+ if (u != NULL) {
+ /* if crontab has not changed since we last read it
+ * in, then we can just use our existing entry.
+ */
+ if (u->mtime == statbuf->st_mtime) {
+ Debug(DLOAD, (" [no change, using old data]"))
+ unlink_user(old_db, u);
+ link_user(new_db, u);
+ goto next_crontab;
+ }
+
+ /* before we fall through to the code that will reload
+ * the user, let's deallocate and unlink the user in
+ * the old database. This is more a point of memory
+ * efficiency than anything else, since all leftover
+ * users will be deleted from the old database when
+ * we finish with the crontab...
+ */
+ Debug(DLOAD, (" [delete old data]"))
+ unlink_user(old_db, u);
+ free_user(u);
+ log_it(fname, getpid(), "RELOAD", tabname);
+ }
+ u = load_user(crontab_fd, pw, fname);
+ if (u != NULL) {
+ u->mtime = statbuf->st_mtime;
+ link_user(new_db, u);
+ }
+
+next_crontab:
+ if (crontab_fd >= OK) {
+ Debug(DLOAD, (" [done]\n"))
+ close(crontab_fd);
+ }
+}
diff --git a/usr.sbin/cron/cron/do_command.c b/usr.sbin/cron/cron/do_command.c
new file mode 100644
index 0000000..03236f4
--- /dev/null
+++ b/usr.sbin/cron/cron/do_command.c
@@ -0,0 +1,541 @@
+/* Copyright 1988,1990,1993,1994 by Paul Vixie
+ * All rights reserved
+ *
+ * Distribute freely, except: don't remove my name from the source or
+ * documentation (don't take credit for my work), mark your changes (don't
+ * get me blamed for your possible bugs), don't alter or remove this
+ * notice. May be sold if buildable source is provided to buyer. No
+ * warrantee of any kind, express or implied, is included with this
+ * software; use at your own risk, responsibility for damages (if any) to
+ * anyone resulting from the use of this software rests entirely with the
+ * user.
+ *
+ * Send bug reports, bug fixes, enhancements, requests, flames, etc., and
+ * I'll try to keep a version up to date. I can be reached as follows:
+ * Paul Vixie <paul@vix.com> uunet!decwrl!vixie!paul
+ */
+
+#if !defined(lint) && !defined(LINT)
+static const char rcsid[] =
+ "$Id: do_command.c,v 1.13 1997/09/15 06:39:06 charnier Exp $";
+#endif
+
+
+#include "cron.h"
+#include <sys/signal.h>
+#if defined(sequent)
+# include <sys/universe.h>
+#endif
+#if defined(SYSLOG)
+# include <syslog.h>
+#endif
+#if defined(LOGIN_CAP)
+# include <login_cap.h>
+#endif
+
+
+static void child_process __P((entry *, user *)),
+ do_univ __P((user *));
+
+
+void
+do_command(e, u)
+ entry *e;
+ user *u;
+{
+ Debug(DPROC, ("[%d] do_command(%s, (%s,%d,%d))\n",
+ getpid(), e->cmd, u->name, e->uid, e->gid))
+
+ /* fork to become asynchronous -- parent process is done immediately,
+ * and continues to run the normal cron code, which means return to
+ * tick(). the child and grandchild don't leave this function, alive.
+ *
+ * vfork() is unsuitable, since we have much to do, and the parent
+ * needs to be able to run off and fork other processes.
+ */
+ switch (fork()) {
+ case -1:
+ log_it("CRON",getpid(),"error","can't fork");
+ break;
+ case 0:
+ /* child process */
+ acquire_daemonlock(1);
+ child_process(e, u);
+ Debug(DPROC, ("[%d] child process done, exiting\n", getpid()))
+ _exit(OK_EXIT);
+ break;
+ default:
+ /* parent process */
+ break;
+ }
+ Debug(DPROC, ("[%d] main process returning to work\n", getpid()))
+}
+
+
+static void
+child_process(e, u)
+ entry *e;
+ user *u;
+{
+ int stdin_pipe[2], stdout_pipe[2];
+ register char *input_data;
+ char *usernm, *mailto;
+ int children = 0;
+# if defined(LOGIN_CAP)
+ struct passwd *pwd;
+ login_cap_t *lc;
+# endif
+
+ Debug(DPROC, ("[%d] child_process('%s')\n", getpid(), e->cmd))
+
+ /* mark ourselves as different to PS command watchers by upshifting
+ * our program name. This has no effect on some kernels.
+ */
+ /*local*/{
+ register char *pch;
+
+ for (pch = ProgramName; *pch; pch++)
+ *pch = MkUpper(*pch);
+ }
+
+ /* discover some useful and important environment settings
+ */
+ usernm = env_get("LOGNAME", e->envp);
+ mailto = env_get("MAILTO", e->envp);
+
+#ifdef USE_SIGCHLD
+ /* our parent is watching for our death by catching SIGCHLD. we
+ * do not care to watch for our children's deaths this way -- we
+ * use wait() explictly. so we have to disable the signal (which
+ * was inherited from the parent).
+ */
+ (void) signal(SIGCHLD, SIG_IGN);
+#else
+ /* on system-V systems, we are ignoring SIGCLD. we have to stop
+ * ignoring it now or the wait() in cron_pclose() won't work.
+ * because of this, we have to wait() for our children here, as well.
+ */
+ (void) signal(SIGCLD, SIG_DFL);
+#endif /*BSD*/
+
+ /* create some pipes to talk to our future child
+ */
+ pipe(stdin_pipe); /* child's stdin */
+ pipe(stdout_pipe); /* child's stdout */
+
+ /* since we are a forked process, we can diddle the command string
+ * we were passed -- nobody else is going to use it again, right?
+ *
+ * if a % is present in the command, previous characters are the
+ * command, and subsequent characters are the additional input to
+ * the command. Subsequent %'s will be transformed into newlines,
+ * but that happens later.
+ *
+ * If there are escaped %'s, remove the escape character.
+ */
+ /*local*/{
+ register int escaped = FALSE;
+ register int ch;
+ register char *p;
+
+ for (input_data = p = e->cmd; (ch = *input_data);
+ input_data++, p++) {
+ if (p != input_data)
+ *p = ch;
+ if (escaped) {
+ if (ch == '%' || ch == '\\')
+ *--p = ch;
+ escaped = FALSE;
+ continue;
+ }
+ if (ch == '\\') {
+ escaped = TRUE;
+ continue;
+ }
+ if (ch == '%') {
+ *input_data++ = '\0';
+ break;
+ }
+ }
+ *p = '\0';
+ }
+
+ /* fork again, this time so we can exec the user's command.
+ */
+ switch (vfork()) {
+ case -1:
+ log_it("CRON",getpid(),"error","can't vfork");
+ exit(ERROR_EXIT);
+ /*NOTREACHED*/
+ case 0:
+ Debug(DPROC, ("[%d] grandchild process Vfork()'ed\n",
+ getpid()))
+
+ /* write a log message. we've waited this long to do it
+ * because it was not until now that we knew the PID that
+ * the actual user command shell was going to get and the
+ * PID is part of the log message.
+ */
+ /*local*/{
+ char *x = mkprints((u_char *)e->cmd, strlen(e->cmd));
+
+ log_it(usernm, getpid(), "CMD", x);
+ free(x);
+ }
+
+ /* that's the last thing we'll log. close the log files.
+ */
+#ifdef SYSLOG
+ closelog();
+#endif
+
+ /* get new pgrp, void tty, etc.
+ */
+ (void) setsid();
+
+ /* close the pipe ends that we won't use. this doesn't affect
+ * the parent, who has to read and write them; it keeps the
+ * kernel from recording us as a potential client TWICE --
+ * which would keep it from sending SIGPIPE in otherwise
+ * appropriate circumstances.
+ */
+ close(stdin_pipe[WRITE_PIPE]);
+ close(stdout_pipe[READ_PIPE]);
+
+ /* grandchild process. make std{in,out} be the ends of
+ * pipes opened by our daddy; make stderr go to stdout.
+ */
+ close(STDIN); dup2(stdin_pipe[READ_PIPE], STDIN);
+ close(STDOUT); dup2(stdout_pipe[WRITE_PIPE], STDOUT);
+ close(STDERR); dup2(STDOUT, STDERR);
+
+ /* close the pipes we just dup'ed. The resources will remain.
+ */
+ close(stdin_pipe[READ_PIPE]);
+ close(stdout_pipe[WRITE_PIPE]);
+
+ /* set our login universe. Do this in the grandchild
+ * so that the child can invoke /usr/lib/sendmail
+ * without surprises.
+ */
+ do_univ(u);
+
+# if defined(LOGIN_CAP)
+ /* Set user's entire context, but skip the environment
+ * as cron provides a separate interface for this
+ */
+ if ((pwd = getpwnam(usernm)) == NULL)
+ pwd = getpwuid(e->uid);
+ lc = NULL;
+ if (pwd != NULL) {
+ pwd->pw_gid = e->gid;
+ if (e->class != NULL)
+ lc = login_getclass(e->class);
+ }
+ if (pwd &&
+ setusercontext(lc, pwd, e->uid,
+ LOGIN_SETALL & ~(LOGIN_SETPATH|LOGIN_SETENV)) == 0)
+ (void) endpwent();
+ else {
+ /* fall back to the old method */
+ (void) endpwent();
+# endif
+ /* set our directory, uid and gid. Set gid first,
+ * since once we set uid, we've lost root privledges.
+ */
+ setgid(e->gid);
+# if defined(BSD)
+ initgroups(usernm, e->gid);
+# endif
+ setlogin(usernm);
+ setuid(e->uid); /* we aren't root after this..*/
+#if defined(LOGIN_CAP)
+ }
+#endif
+ chdir(env_get("HOME", e->envp));
+
+ /* exec the command.
+ */
+ {
+ char *shell = env_get("SHELL", e->envp);
+
+# if DEBUGGING
+ if (DebugFlags & DTEST) {
+ fprintf(stderr,
+ "debug DTEST is on, not exec'ing command.\n");
+ fprintf(stderr,
+ "\tcmd='%s' shell='%s'\n", e->cmd, shell);
+ _exit(OK_EXIT);
+ }
+# endif /*DEBUGGING*/
+ execle(shell, shell, "-c", e->cmd, (char *)0, e->envp);
+ warn("execl: couldn't exec `%s'", shell);
+ _exit(ERROR_EXIT);
+ }
+ break;
+ default:
+ /* parent process */
+ break;
+ }
+
+ children++;
+
+ /* middle process, child of original cron, parent of process running
+ * the user's command.
+ */
+
+ Debug(DPROC, ("[%d] child continues, closing pipes\n", getpid()))
+
+ /* close the ends of the pipe that will only be referenced in the
+ * grandchild process...
+ */
+ close(stdin_pipe[READ_PIPE]);
+ close(stdout_pipe[WRITE_PIPE]);
+
+ /*
+ * write, to the pipe connected to child's stdin, any input specified
+ * after a % in the crontab entry. while we copy, convert any
+ * additional %'s to newlines. when done, if some characters were
+ * written and the last one wasn't a newline, write a newline.
+ *
+ * Note that if the input data won't fit into one pipe buffer (2K
+ * or 4K on most BSD systems), and the child doesn't read its stdin,
+ * we would block here. thus we must fork again.
+ */
+
+ if (*input_data && fork() == 0) {
+ register FILE *out = fdopen(stdin_pipe[WRITE_PIPE], "w");
+ register int need_newline = FALSE;
+ register int escaped = FALSE;
+ register int ch;
+
+ Debug(DPROC, ("[%d] child2 sending data to grandchild\n", getpid()))
+
+ /* close the pipe we don't use, since we inherited it and
+ * are part of its reference count now.
+ */
+ close(stdout_pipe[READ_PIPE]);
+
+ /* translation:
+ * \% -> %
+ * % -> \n
+ * \x -> \x for all x != %
+ */
+ while ((ch = *input_data++)) {
+ if (escaped) {
+ if (ch != '%')
+ putc('\\', out);
+ } else {
+ if (ch == '%')
+ ch = '\n';
+ }
+
+ if (!(escaped = (ch == '\\'))) {
+ putc(ch, out);
+ need_newline = (ch != '\n');
+ }
+ }
+ if (escaped)
+ putc('\\', out);
+ if (need_newline)
+ putc('\n', out);
+
+ /* close the pipe, causing an EOF condition. fclose causes
+ * stdin_pipe[WRITE_PIPE] to be closed, too.
+ */
+ fclose(out);
+
+ Debug(DPROC, ("[%d] child2 done sending to grandchild\n", getpid()))
+ exit(0);
+ }
+
+ /* close the pipe to the grandkiddie's stdin, since its wicked uncle
+ * ernie back there has it open and will close it when he's done.
+ */
+ close(stdin_pipe[WRITE_PIPE]);
+
+ children++;
+
+ /*
+ * read output from the grandchild. it's stderr has been redirected to
+ * it's stdout, which has been redirected to our pipe. if there is any
+ * output, we'll be mailing it to the user whose crontab this is...
+ * when the grandchild exits, we'll get EOF.
+ */
+
+ Debug(DPROC, ("[%d] child reading output from grandchild\n", getpid()))
+
+ /*local*/{
+ register FILE *in = fdopen(stdout_pipe[READ_PIPE], "r");
+ register int ch = getc(in);
+
+ if (ch != EOF) {
+ register FILE *mail;
+ register int bytes = 1;
+ int status = 0;
+
+ Debug(DPROC|DEXT,
+ ("[%d] got data (%x:%c) from grandchild\n",
+ getpid(), ch, ch))
+
+ /* get name of recipient. this is MAILTO if set to a
+ * valid local username; USER otherwise.
+ */
+ if (mailto) {
+ /* MAILTO was present in the environment
+ */
+ if (!*mailto) {
+ /* ... but it's empty. set to NULL
+ */
+ mailto = NULL;
+ }
+ } else {
+ /* MAILTO not present, set to USER.
+ */
+ mailto = usernm;
+ }
+
+ /* if we are supposed to be mailing, MAILTO will
+ * be non-NULL. only in this case should we set
+ * up the mail command and subjects and stuff...
+ */
+
+ if (mailto) {
+ register char **env;
+ auto char mailcmd[MAX_COMMAND];
+ auto char hostname[MAXHOSTNAMELEN];
+
+ (void) gethostname(hostname, MAXHOSTNAMELEN);
+ (void) snprintf(mailcmd, sizeof(mailcmd),
+ MAILARGS, MAILCMD);
+ if (!(mail = cron_popen(mailcmd, "w"))) {
+ warn("%s", MAILCMD);
+ (void) _exit(ERROR_EXIT);
+ }
+ fprintf(mail, "From: root (Cron Daemon)\n");
+ fprintf(mail, "To: %s\n", mailto);
+ fprintf(mail, "Subject: Cron <%s@%s> %s\n",
+ usernm, first_word(hostname, "."),
+ e->cmd);
+# if defined(MAIL_DATE)
+ fprintf(mail, "Date: %s\n",
+ arpadate(&TargetTime));
+# endif /* MAIL_DATE */
+ for (env = e->envp; *env; env++)
+ fprintf(mail, "X-Cron-Env: <%s>\n",
+ *env);
+ fprintf(mail, "\n");
+
+ /* this was the first char from the pipe
+ */
+ putc(ch, mail);
+ }
+
+ /* we have to read the input pipe no matter whether
+ * we mail or not, but obviously we only write to
+ * mail pipe if we ARE mailing.
+ */
+
+ while (EOF != (ch = getc(in))) {
+ bytes++;
+ if (mailto)
+ putc(ch, mail);
+ }
+
+ /* only close pipe if we opened it -- i.e., we're
+ * mailing...
+ */
+
+ if (mailto) {
+ Debug(DPROC, ("[%d] closing pipe to mail\n",
+ getpid()))
+ /* Note: the pclose will probably see
+ * the termination of the grandchild
+ * in addition to the mail process, since
+ * it (the grandchild) is likely to exit
+ * after closing its stdout.
+ */
+ status = cron_pclose(mail);
+ }
+
+ /* if there was output and we could not mail it,
+ * log the facts so the poor user can figure out
+ * what's going on.
+ */
+ if (mailto && status) {
+ char buf[MAX_TEMPSTR];
+
+ snprintf(buf, sizeof(buf),
+ "mailed %d byte%s of output but got status 0x%04x\n",
+ bytes, (bytes==1)?"":"s",
+ status);
+ log_it(usernm, getpid(), "MAIL", buf);
+ }
+
+ } /*if data from grandchild*/
+
+ Debug(DPROC, ("[%d] got EOF from grandchild\n", getpid()))
+
+ fclose(in); /* also closes stdout_pipe[READ_PIPE] */
+ }
+
+ /* wait for children to die.
+ */
+ for (; children > 0; children--)
+ {
+ WAIT_T waiter;
+ PID_T pid;
+
+ Debug(DPROC, ("[%d] waiting for grandchild #%d to finish\n",
+ getpid(), children))
+ pid = wait(&waiter);
+ if (pid < OK) {
+ Debug(DPROC, ("[%d] no more grandchildren--mail written?\n",
+ getpid()))
+ break;
+ }
+ Debug(DPROC, ("[%d] grandchild #%d finished, status=%04x",
+ getpid(), pid, WEXITSTATUS(waiter)))
+ if (WIFSIGNALED(waiter) && WCOREDUMP(waiter))
+ Debug(DPROC, (", dumped core"))
+ Debug(DPROC, ("\n"))
+ }
+}
+
+
+static void
+do_univ(u)
+ user *u;
+{
+#if defined(sequent)
+/* Dynix (Sequent) hack to put the user associated with
+ * the passed user structure into the ATT universe if
+ * necessary. We have to dig the gecos info out of
+ * the user's password entry to see if the magic
+ * "universe(att)" string is present.
+ */
+
+ struct passwd *p;
+ char *s;
+ int i;
+
+ p = getpwuid(u->uid);
+ (void) endpwent();
+
+ if (p == NULL)
+ return;
+
+ s = p->pw_gecos;
+
+ for (i = 0; i < 4; i++)
+ {
+ if ((s = strchr(s, ',')) == NULL)
+ return;
+ s++;
+ }
+ if (strcmp(s, "universe(att)"))
+ return;
+
+ (void) universe(U_ATT);
+#endif
+}
diff --git a/usr.sbin/cron/cron/externs.h b/usr.sbin/cron/cron/externs.h
new file mode 100644
index 0000000..3efe605
--- /dev/null
+++ b/usr.sbin/cron/cron/externs.h
@@ -0,0 +1,145 @@
+/* Copyright 1993,1994 by Paul Vixie
+ * All rights reserved
+ *
+ * Distribute freely, except: don't remove my name from the source or
+ * documentation (don't take credit for my work), mark your changes (don't
+ * get me blamed for your possible bugs), don't alter or remove this
+ * notice. May be sold if buildable source is provided to buyer. No
+ * warrantee of any kind, express or implied, is included with this
+ * software; use at your own risk, responsibility for damages (if any) to
+ * anyone resulting from the use of this software rests entirely with the
+ * user.
+ *
+ * Send bug reports, bug fixes, enhancements, requests, flames, etc., and
+ * I'll try to keep a version up to date. I can be reached as follows:
+ * Paul Vixie <paul@vix.com> uunet!decwrl!vixie!paul
+ */
+
+#if defined(POSIX) || defined(ATT)
+# include <stdlib.h>
+# include <unistd.h>
+# include <string.h>
+# include <dirent.h>
+# define DIR_T struct dirent
+# define WAIT_T int
+# define WAIT_IS_INT 1
+extern char *tzname[2];
+# define TZONE(tm) tzname[(tm).tm_isdst]
+#endif
+
+#if defined(UNIXPC)
+# undef WAIT_T
+# undef WAIT_IS_INT
+# define WAIT_T union wait
+#endif
+
+#if defined(POSIX)
+# define SIG_T sig_t
+# define TIME_T time_t
+# define PID_T pid_t
+#endif
+
+#if defined(ATT)
+# define SIG_T void
+# define TIME_T long
+# define PID_T int
+#endif
+
+#if !defined(POSIX) && !defined(ATT)
+/* classic BSD */
+extern time_t time();
+extern unsigned sleep();
+extern struct tm *localtime();
+extern struct passwd *getpwnam();
+extern int errno;
+extern void perror(), exit(), free();
+extern char *getenv(), *strcpy(), *strchr(), *strtok();
+extern void *malloc(), *realloc();
+# define SIG_T void
+# define TIME_T long
+# define PID_T int
+# define WAIT_T union wait
+# define DIR_T struct direct
+# include <sys/dir.h>
+# define TZONE(tm) (tm).tm_zone
+#endif
+
+/* getopt() isn't part of POSIX. some systems define it in <stdlib.h> anyway.
+ * of those that do, some complain that our definition is different and some
+ * do not. to add to the misery and confusion, some systems define getopt()
+ * in ways that we cannot predict or comprehend, yet do not define the adjunct
+ * external variables needed for the interface.
+ */
+#if (!defined(BSD) || (BSD < 198911)) && !defined(ATT) && !defined(UNICOS)
+int getopt __P((int, char * const *, const char *));
+#endif
+
+#if (!defined(BSD) || (BSD < 199103))
+extern char *optarg;
+extern int optind, opterr, optopt;
+#endif
+
+#if WAIT_IS_INT
+# ifndef WEXITSTATUS
+# define WEXITSTATUS(x) (((x) >> 8) & 0xff)
+# endif
+# ifndef WTERMSIG
+# define WTERMSIG(x) ((x) & 0x7f)
+# endif
+# ifndef WCOREDUMP
+# define WCOREDUMP(x) ((x) & 0x80)
+# endif
+#else /*WAIT_IS_INT*/
+# ifndef WEXITSTATUS
+# define WEXITSTATUS(x) ((x).w_retcode)
+# endif
+# ifndef WTERMSIG
+# define WTERMSIG(x) ((x).w_termsig)
+# endif
+# ifndef WCOREDUMP
+# define WCOREDUMP(x) ((x).w_coredump)
+# endif
+#endif /*WAIT_IS_INT*/
+
+#ifndef WIFSIGNALED
+#define WIFSIGNALED(x) (WTERMSIG(x) != 0)
+#endif
+#ifndef WIFEXITED
+#define WIFEXITED(x) (WTERMSIG(x) == 0)
+#endif
+
+#ifdef NEED_STRCASECMP
+extern int strcasecmp __P((char *, char *));
+#endif
+
+#ifdef NEED_STRDUP
+extern char *strdup __P((char *));
+#endif
+
+#ifdef NEED_STRERROR
+extern char *strerror __P((int));
+#endif
+
+#ifdef NEED_FLOCK
+extern int flock __P((int, int));
+# define LOCK_SH 1
+# define LOCK_EX 2
+# define LOCK_NB 4
+# define LOCK_UN 8
+#endif
+
+#ifdef NEED_SETSID
+extern int setsid __P((void));
+#endif
+
+#ifdef NEED_GETDTABLESIZE
+extern int getdtablesize __P((void));
+#endif
+
+#ifdef NEED_SETENV
+extern int setenv __P((char *, char *, int));
+#endif
+
+#ifdef NEED_VFORK
+extern PID_T vfork __P((void));
+#endif
diff --git a/usr.sbin/cron/cron/job.c b/usr.sbin/cron/cron/job.c
new file mode 100644
index 0000000..01ed0d6
--- /dev/null
+++ b/usr.sbin/cron/cron/job.c
@@ -0,0 +1,76 @@
+/* Copyright 1988,1990,1993,1994 by Paul Vixie
+ * All rights reserved
+ *
+ * Distribute freely, except: don't remove my name from the source or
+ * documentation (don't take credit for my work), mark your changes (don't
+ * get me blamed for your possible bugs), don't alter or remove this
+ * notice. May be sold if buildable source is provided to buyer. No
+ * warrantee of any kind, express or implied, is included with this
+ * software; use at your own risk, responsibility for damages (if any) to
+ * anyone resulting from the use of this software rests entirely with the
+ * user.
+ *
+ * Send bug reports, bug fixes, enhancements, requests, flames, etc., and
+ * I'll try to keep a version up to date. I can be reached as follows:
+ * Paul Vixie <paul@vix.com> uunet!decwrl!vixie!paul
+ */
+
+#if !defined(lint) && !defined(LINT)
+static const char rcsid[] =
+ "$Id: job.c,v 1.4 1997/02/22 16:04:44 peter Exp $";
+#endif
+
+
+#include "cron.h"
+
+
+typedef struct _job {
+ struct _job *next;
+ entry *e;
+ user *u;
+} job;
+
+
+static job *jhead = NULL, *jtail = NULL;
+
+
+void
+job_add(e, u)
+ register entry *e;
+ register user *u;
+{
+ register job *j;
+
+ /* if already on queue, keep going */
+ for (j=jhead; j; j=j->next)
+ if (j->e == e && j->u == u) { return; }
+
+ /* build a job queue element */
+ if ((j = (job*)malloc(sizeof(job))) == NULL)
+ return;
+ j->next = (job*) NULL;
+ j->e = e;
+ j->u = u;
+
+ /* add it to the tail */
+ if (!jhead) { jhead=j; }
+ else { jtail->next=j; }
+ jtail = j;
+}
+
+
+int
+job_runqueue()
+{
+ register job *j, *jn;
+ register int run = 0;
+
+ for (j=jhead; j; j=jn) {
+ do_command(j->e, j->u);
+ jn = j->next;
+ free(j);
+ run++;
+ }
+ jhead = jtail = NULL;
+ return run;
+}
diff --git a/usr.sbin/cron/cron/pathnames.h b/usr.sbin/cron/cron/pathnames.h
new file mode 100644
index 0000000..5c58967
--- /dev/null
+++ b/usr.sbin/cron/cron/pathnames.h
@@ -0,0 +1,81 @@
+/* Copyright 1993,1994 by Paul Vixie
+ * All rights reserved
+ *
+ * Distribute freely, except: don't remove my name from the source or
+ * documentation (don't take credit for my work), mark your changes (don't
+ * get me blamed for your possible bugs), don't alter or remove this
+ * notice. May be sold if buildable source is provided to buyer. No
+ * warrantee of any kind, express or implied, is included with this
+ * software; use at your own risk, responsibility for damages (if any) to
+ * anyone resulting from the use of this software rests entirely with the
+ * user.
+ *
+ * Send bug reports, bug fixes, enhancements, requests, flames, etc., and
+ * I'll try to keep a version up to date. I can be reached as follows:
+ * Paul Vixie <paul@vix.com> uunet!decwrl!vixie!paul
+ */
+
+/*
+ * $Id$
+ */
+
+#if (defined(BSD)) && (BSD >= 199103) || defined(__linux) || defined(AIX)
+# include <paths.h>
+#endif /*BSD*/
+
+#ifndef CRONDIR
+ /* CRONDIR is where crond(8) and crontab(1) both chdir
+ * to; SPOOL_DIR, ALLOW_FILE, DENY_FILE, and LOG_FILE
+ * are all relative to this directory.
+ */
+#define CRONDIR "/var/cron"
+#endif
+
+ /* SPOOLDIR is where the crontabs live.
+ * This directory will have its modtime updated
+ * whenever crontab(1) changes a crontab; this is
+ * the signal for crond(8) to look at each individual
+ * crontab file and reload those whose modtimes are
+ * newer than they were last time around (or which
+ * didn't exist last time around...)
+ */
+#define SPOOL_DIR "tabs"
+
+ /* undefining these turns off their features. note
+ * that ALLOW_FILE and DENY_FILE must both be defined
+ * in order to enable the allow/deny code. If neither
+ * LOG_FILE or SYSLOG is defined, we don't log. If
+ * both are defined, we log both ways.
+ */
+#define ALLOW_FILE "allow" /*-*/
+#define DENY_FILE "deny" /*-*/
+/*#define LOG_FILE "log"*/ /*-*/
+
+ /* where should the daemon stick its PID?
+ */
+#ifdef _PATH_VARRUN
+# define PIDDIR _PATH_VARRUN
+#else
+# define PIDDIR "/etc/"
+#endif
+#define PIDFILE "%scron.pid"
+
+ /* 4.3BSD-style crontab */
+#define SYSCRONTAB "/etc/crontab"
+
+ /* what editor to use if no EDITOR or VISUAL
+ * environment variable specified.
+ */
+#if defined(_PATH_VI)
+# define EDITOR _PATH_VI
+#else
+# define EDITOR "/usr/ucb/vi"
+#endif
+
+#ifndef _PATH_BSHELL
+# define _PATH_BSHELL "/bin/sh"
+#endif
+
+#ifndef _PATH_DEFPATH
+# define _PATH_DEFPATH "/usr/bin:/bin"
+#endif
diff --git a/usr.sbin/cron/cron/popen.c b/usr.sbin/cron/cron/popen.c
new file mode 100644
index 0000000..34b2f02
--- /dev/null
+++ b/usr.sbin/cron/cron/popen.c
@@ -0,0 +1,170 @@
+/*
+ * Copyright (c) 1988 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software written by Ken Arnold and
+ * published in UNIX Review, Vol. 6, No. 8.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ */
+
+/* this came out of the ftpd sources; it's been modified to avoid the
+ * globbing stuff since we don't need it. also execvp instead of execv.
+ */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)popen.c 5.7 (Berkeley) 2/14/89";
+#endif
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#include "cron.h"
+#include <sys/signal.h>
+
+
+#define MAX_ARGS 100
+#define WANT_GLOBBING 0
+
+/*
+ * Special version of popen which avoids call to shell. This insures noone
+ * may create a pipe to a hidden program as a side effect of a list or dir
+ * command.
+ */
+static PID_T *pids;
+static int fds;
+
+FILE *
+cron_popen(program, type)
+ char *program, *type;
+{
+ register char *cp;
+ FILE *iop;
+ int argc, pdes[2];
+ PID_T pid;
+ char *argv[MAX_ARGS + 1];
+#if WANT_GLOBBING
+ char **pop, *vv[2];
+ int gargc;
+ char *gargv[1000];
+ extern char **glob(), **copyblk();
+#endif
+
+ if ((*type != 'r' && *type != 'w') || type[1])
+ return(NULL);
+
+ if (!pids) {
+ if ((fds = getdtablesize()) <= 0)
+ return(NULL);
+ if (!(pids = (PID_T *)malloc((u_int)(fds * sizeof(PID_T)))))
+ return(NULL);
+ bzero((char *)pids, fds * sizeof(PID_T));
+ }
+ if (pipe(pdes) < 0)
+ return(NULL);
+
+ /* break up string into pieces */
+ for (argc = 0, cp = program; argc < MAX_ARGS; cp = NULL)
+ if (!(argv[argc++] = strtok(cp, " \t\n")))
+ break;
+
+#if WANT_GLOBBING
+ /* glob each piece */
+ gargv[0] = argv[0];
+ for (gargc = argc = 1; argv[argc]; argc++) {
+ if (!(pop = glob(argv[argc]))) { /* globbing failed */
+ vv[0] = argv[argc];
+ vv[1] = NULL;
+ pop = copyblk(vv);
+ }
+ argv[argc] = (char *)pop; /* save to free later */
+ while (*pop && gargc < 1000)
+ gargv[gargc++] = *pop++;
+ }
+ gargv[gargc] = NULL;
+#endif
+
+ iop = NULL;
+ switch(pid = vfork()) {
+ case -1: /* error */
+ (void)close(pdes[0]);
+ (void)close(pdes[1]);
+ goto pfree;
+ /* NOTREACHED */
+ case 0: /* child */
+ if (*type == 'r') {
+ 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]);
+ }
+ (void)close(pdes[1]);
+ }
+#if WANT_GLOBBING
+ execvp(gargv[0], gargv);
+#else
+ execvp(argv[0], argv);
+#endif
+ _exit(1);
+ }
+ /* parent; assume fdopen can't fail... */
+ if (*type == 'r') {
+ iop = fdopen(pdes[0], type);
+ (void)close(pdes[1]);
+ } else {
+ iop = fdopen(pdes[1], type);
+ (void)close(pdes[0]);
+ }
+ pids[fileno(iop)] = pid;
+
+pfree:
+#if WANT_GLOBBING
+ for (argc = 1; argv[argc] != NULL; argc++) {
+/* blkfree((char **)argv[argc]); */
+ free((char *)argv[argc]);
+ }
+#endif
+ return(iop);
+}
+
+int
+cron_pclose(iop)
+ FILE *iop;
+{
+ register int fdes;
+ int omask;
+ WAIT_T stat_loc;
+ PID_T pid;
+
+ /*
+ * pclose returns -1 if stream is not associated with a
+ * `popened' command, or, if already `pclosed'.
+ */
+ if (pids == 0 || pids[fdes = fileno(iop)] == 0)
+ return(-1);
+ (void)fclose(iop);
+ omask = sigblock(sigmask(SIGINT)|sigmask(SIGQUIT)|sigmask(SIGHUP));
+ while ((pid = wait(&stat_loc)) != pids[fdes] && pid != -1)
+ ;
+ (void)sigsetmask(omask);
+ pids[fdes] = 0;
+ return (pid == -1 ? -1 : WEXITSTATUS(stat_loc));
+}
diff --git a/usr.sbin/cron/cron/user.c b/usr.sbin/cron/cron/user.c
new file mode 100644
index 0000000..d583361
--- /dev/null
+++ b/usr.sbin/cron/cron/user.c
@@ -0,0 +1,128 @@
+/* Copyright 1988,1990,1993,1994 by Paul Vixie
+ * All rights reserved
+ *
+ * Distribute freely, except: don't remove my name from the source or
+ * documentation (don't take credit for my work), mark your changes (don't
+ * get me blamed for your possible bugs), don't alter or remove this
+ * notice. May be sold if buildable source is provided to buyer. No
+ * warrantee of any kind, express or implied, is included with this
+ * software; use at your own risk, responsibility for damages (if any) to
+ * anyone resulting from the use of this software rests entirely with the
+ * user.
+ *
+ * Send bug reports, bug fixes, enhancements, requests, flames, etc., and
+ * I'll try to keep a version up to date. I can be reached as follows:
+ * Paul Vixie <paul@vix.com> uunet!decwrl!vixie!paul
+ */
+
+#if !defined(lint) && !defined(LINT)
+static const char rcsid[] =
+ "$Id: user.c,v 1.6 1997/09/15 06:39:07 charnier Exp $";
+#endif
+
+/* vix 26jan87 [log is in RCS file]
+ */
+
+
+#include "cron.h"
+
+static char *User_name;
+
+void
+free_user(u)
+ user *u;
+{
+ entry *e, *ne;
+
+ free(u->name);
+ for (e = u->crontab; e != NULL; e = ne) {
+ ne = e->next;
+ free_entry(e);
+ }
+ free(u);
+}
+
+static void
+log_error(msg)
+ char *msg;
+{
+ log_it(User_name, getpid(), "PARSE", msg);
+}
+
+user *
+load_user(crontab_fd, pw, name)
+ int crontab_fd;
+ struct passwd *pw; /* NULL implies syscrontab */
+ char *name;
+{
+ char envstr[MAX_ENVSTR];
+ FILE *file;
+ user *u;
+ entry *e;
+ int status;
+ char **envp, **tenvp;
+
+ if (!(file = fdopen(crontab_fd, "r"))) {
+ warn("fdopen on crontab_fd in load_user");
+ return NULL;
+ }
+
+ Debug(DPARS, ("load_user()\n"))
+
+ /* file is open. build user entry, then read the crontab file.
+ */
+ if ((u = (user *) malloc(sizeof(user))) == NULL) {
+ errno = ENOMEM;
+ return NULL;
+ }
+ if ((u->name = strdup(name)) == NULL) {
+ free(u);
+ errno = ENOMEM;
+ return NULL;
+ }
+ u->crontab = NULL;
+
+ /*
+ * init environment. this will be copied/augmented for each entry.
+ */
+ if ((envp = env_init()) == NULL) {
+ free(u->name);
+ free(u);
+ return NULL;
+ }
+
+ /*
+ * load the crontab
+ */
+ while ((status = load_env(envstr, file)) >= OK) {
+ switch (status) {
+ case ERR:
+ free_user(u);
+ u = NULL;
+ goto done;
+ case FALSE:
+ User_name = u->name; /* for log_error */
+ e = load_entry(file, log_error, pw, envp);
+ if (e) {
+ e->next = u->crontab;
+ u->crontab = e;
+ }
+ break;
+ case TRUE:
+ if ((tenvp = env_set(envp, envstr))) {
+ envp = tenvp;
+ } else {
+ free_user(u);
+ u = NULL;
+ goto done;
+ }
+ break;
+ }
+ }
+
+ done:
+ env_free(envp);
+ fclose(file);
+ Debug(DPARS, ("...load_user() done\n"))
+ return u;
+}
diff --git a/usr.sbin/cron/crontab/Makefile b/usr.sbin/cron/crontab/Makefile
new file mode 100644
index 0000000..1c9a486
--- /dev/null
+++ b/usr.sbin/cron/crontab/Makefile
@@ -0,0 +1,26 @@
+BINDIR?= /usr/bin
+
+PROG= crontab
+SRCS= crontab.c
+CFLAGS+= -I${.CURDIR}/../cron
+MAN1= crontab.1
+MAN5= crontab.5
+
+.if exists(${.OBJDIR}/../lib)
+LDDESTDIR+= -L${.OBJDIR}/../lib
+DPADD+= ${.OBJDIR}/../lib/libcron.a
+.else
+LDDESTDIR+= -L${.CURDIR}/../lib
+DPADD+= ${.CURDIR}/../lib/libcron.a
+.endif
+
+LDADD+= -lcron
+
+LDADD+= -lutil
+DPADD+= ${LIBUTIL}
+
+BINOWN= root
+BINMODE=4555
+INSTALLFLAGS=-fschg
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/cron/crontab/crontab.1 b/usr.sbin/cron/crontab/crontab.1
new file mode 100644
index 0000000..9d13f4b
--- /dev/null
+++ b/usr.sbin/cron/crontab/crontab.1
@@ -0,0 +1,115 @@
+.\"/* Copyright 1988,1990,1993 by Paul Vixie
+.\" * All rights reserved
+.\" *
+.\" * Distribute freely, except: don't remove my name from the source or
+.\" * documentation (don't take credit for my work), mark your changes (don't
+.\" * get me blamed for your possible bugs), don't alter or remove this
+.\" * notice. May be sold if buildable source is provided to buyer. No
+.\" * warrantee of any kind, express or implied, is included with this
+.\" * software; use at your own risk, responsibility for damages (if any) to
+.\" * anyone resulting from the use of this software rests entirely with the
+.\" * user.
+.\" *
+.\" * Send bug reports, bug fixes, enhancements, requests, flames, etc., and
+.\" * I'll try to keep a version up to date. I can be reached as follows:
+.\" * Paul Vixie <paul@vix.com> uunet!decwrl!vixie!paul
+.\" */
+.\"
+.\" $Id: crontab.1,v 1.3 1997/02/22 16:04:51 peter Exp $
+.\"
+.Dd December 29, 1993
+.Dt CRONTAB 1
+.Os
+.Sh NAME
+.Nm crontab
+.Nd maintain crontab files for individual users (V3)
+.Sh SYNOPSIS
+.Nm crontab
+.Op Fl u Ar user
+.Ar file
+.Nm crontab
+.Op Fl u Ar user
+{
+.Fl l |
+.Fl r |
+.Fl e
+}
+.Sh DESCRIPTION
+.Nm Crontab
+is the program used to install, deinstall or list the tables
+used to drive the
+.Xr cron 8
+daemon in Vixie Cron. Each user can have their own crontab, and though
+these are files in
+.Pa /var ,
+they are not intended to be edited directly.
+.Pp
+If the
+.Pa allow
+file exists, then you must be listed therein in order to be allowed to use
+this command. If the
+.Pa allow
+file does not exist but the
+.Pa deny
+file does exist, then you must
+.Em not
+be listed in the
+.Pa deny
+file in order to use this command. If neither of these files exists, then
+depending on site-dependent configuration parameters, only the super user
+will be allowed to use this command, or all users will be able to use this
+command.
+.Pp
+The first form of this command is used to install a new crontab from some
+named file or standard input if the pseudo-filename ``-'' is given.
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl u
+Specify the name of the user whose crontab is to be
+tweaked. If this option is not given,
+.Nm
+examines "your" crontab, i.e., the crontab of the person executing the
+command. Note that
+.Xr su 8
+can confuse
+.Nm
+and that if you are running inside of
+.Xr su 8
+you should always use the
+.Fl u
+option for safety's sake.
+.It Fl l
+Display the current crontab on standard output.
+.It Fl r
+Remove the current crontab.
+.It Fl e
+Edit the current crontab using the editor specified by
+the
+.Ev VISUAL
+or
+.Ev EDITOR
+environment variables. After you exit
+from the editor, the modified crontab will be installed automatically.
+.El
+.Sh SEE ALSO
+.Xr crontab 5 ,
+.Xr cron 8
+.Sh FILES
+.Bl -tag -width /var/cron/allow -compact
+.It Pa /var/cron/allow
+.It Pa /var/cron/deny
+.El
+.Sh STANDARDS
+The
+.Nm
+command conforms to
+.St -p1003.2 .
+This new command syntax
+differs from previous versions of Vixie Cron, as well as from the classic
+SVR3 syntax.
+.Sh DIAGNOSTICS
+A fairly informative usage message appears if you run it with a bad command
+line.
+.Sh AUTHOR
+.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..c9d5bb5
--- /dev/null
+++ b/usr.sbin/cron/crontab/crontab.5
@@ -0,0 +1,225 @@
+.\"/* Copyright 1988,1990,1993,1994 by Paul Vixie
+.\" * All rights reserved
+.\" *
+.\" * Distribute freely, except: don't remove my name from the source or
+.\" * documentation (don't take credit for my work), mark your changes (don't
+.\" * get me blamed for your possible bugs), don't alter or remove this
+.\" * notice. May be sold if buildable source is provided to buyer. No
+.\" * warrantee of any kind, express or implied, is included with this
+.\" * software; use at your own risk, responsibility for damages (if any) to
+.\" * anyone resulting from the use of this software rests entirely with the
+.\" * user.
+.\" *
+.\" * Send bug reports, bug fixes, enhancements, requests, flames, etc., and
+.\" * I'll try to keep a version up to date. I can be reached as follows:
+.\" * Paul Vixie <paul@vix.com> uunet!decwrl!vixie!paul
+.\" */
+.\"
+.\" $Id: crontab.5,v 1.6 1997/09/29 19:11:36 wosch Exp $
+.\"
+.Dd January 24, 1994
+.Dt CRONTAB 5
+.Os
+.Sh NAME
+.Nm crontab
+.Nd tables for driving cron
+.Sh DESCRIPTION
+A
+.Nm
+file contains instructions to the
+.Xr cron 8
+daemon of the general form: ``run this command at this time on this date''.
+Each user has their own crontab, and commands in any given crontab will be
+executed as the user who owns the crontab. Uucp and News will usually have
+their own crontabs, eliminating the need for explicitly running
+.Xr su 1
+as part of a cron command.
+.Pp
+Blank lines and leading spaces and tabs are ignored. Lines whose first
+non-space character is a pound-sign (#) are comments, and are ignored.
+Note that comments are not allowed on the same line as cron commands, since
+they will be taken to be part of the command. Similarly, comments are not
+allowed on the same line as environment variable settings.
+.Pp
+An active line in a crontab will be either an environment setting or a cron
+command. An environment setting is of the form,
+.Pp
+ name = value
+.Pp
+where the spaces around the equal-sign (=) are optional, and any subsequent
+non-leading spaces in
+.Em value
+will be part of the value assigned to
+.Em name .
+The
+.Em value
+string may be placed in quotes (single or double, but matching) to preserve
+leading or trailing blanks.
+.Pp
+Several environment variables are set up
+automatically by the
+.Xr cron 8
+daemon.
+.Ev SHELL
+is set to
+.Pa /bin/sh ,
+and
+.Ev LOGNAME
+and
+.Ev HOME
+are set from the
+.Pa /etc/passwd
+line of the crontab's owner.
+.Ev HOME
+and
+.Ev SHELL
+may be overridden by settings in the crontab;
+.Ev LOGNAME
+may not.
+.Pp
+(Another note: the
+.Ev LOGNAME
+variable is sometimes called
+.Ev USER
+on BSD systems...
+on these systems,
+.Ev USER
+will be set also).
+.Pp
+In addition to
+.Ev LOGNAME ,
+.Ev HOME ,
+and
+.Ev SHELL ,
+.Xr cron 8
+will look at
+.Ev MAILTO
+if it has any reason to send mail as a result of running
+commands in ``this'' crontab. If
+.Ev MAILTO
+is defined (and non-empty), mail is
+sent to the user so named. If
+.Ev MAILTO
+is defined but empty (MAILTO=""), no
+mail will be sent. Otherwise mail is sent to the owner of the crontab. This
+option is useful if you decide on
+.Pa /bin/mail
+instead of
+.Pa /usr/lib/sendmail
+as
+your mailer when you install cron --
+.Pa /bin/mail
+doesn't do aliasing, and UUCP
+usually doesn't read its mail.
+.Pp
+The format of a cron command is very much the V7 standard, with a number of
+upward-compatible extensions. Each line has five time and date fields,
+followed by a user name
+(with optional ``:<group>'' and ``/<login-class>'' suffixes)
+if this is the system crontab file,
+followed by a command. Commands are executed by
+.Xr cron 8
+when the minute, hour, and month of year fields match the current time,
+.Em and
+when at least one of the two day fields (day of month, or day of week)
+match the current time (see ``Note'' below).
+.Xr cron 8
+examines cron entries once every minute.
+The time and date fields are:
+.Bd -literal -offset indent
+field allowed values
+----- --------------
+minute 0-59
+hour 0-23
+day of month 1-31
+month 1-12 (or names, see below)
+day of week 0-7 (0 or 7 is Sun, or use names)
+.Ed
+.Pp
+A field may be an asterisk (*), which always stands for ``first\-last''.
+.Pp
+Ranges of numbers are allowed. Ranges are two numbers separated
+with a hyphen. The specified range is inclusive. For example,
+8-11 for an ``hours'' entry specifies execution at hours 8, 9, 10
+and 11.
+.Pp
+Lists are allowed. A list is a set of numbers (or ranges)
+separated by commas. Examples: ``1,2,5,9'', ``0-4,8-12''.
+.Pp
+Step values can be used in conjunction with ranges. Following
+a range with ``/<number>'' specifies skips of the number's value
+through the range. For example, ``0-23/2'' can be used in the hours
+field to specify command execution every other hour (the alternative
+in the V7 standard is ``0,2,4,6,8,10,12,14,16,18,20,22''). Steps are
+also permitted after an asterisk, so if you want to say ``every two
+hours'', just use ``*/2''.
+.Pp
+Names can also be used for the ``month'' and ``day of week''
+fields. Use the first three letters of the particular
+day or month (case doesn't matter). Ranges or
+lists of names are not allowed.
+.Pp
+The ``sixth'' field (the rest of the line) specifies the command to be
+run.
+The entire command portion of the line, up to a newline or %
+character, will be executed by
+.Pa /bin/sh
+or by the shell
+specified in the
+.Ev SHELL
+variable of the cronfile.
+Percent-signs (%) in the command, unless escaped with backslash
+(\\), will be changed into newline characters, and all data
+after the first % will be sent to the command as standard
+input.
+.Pp
+Note: The day of a command's execution can be specified by two
+fields \(em day of month, and day of week. If both fields are
+restricted (ie, aren't *), the command will be run when
+.Em either
+field matches the current time. For example,
+.br
+``30 4 1,15 * 5''
+would cause a command to be run at 4:30 am on the 1st and 15th of each
+month, plus every Friday.
+.Sh EXAMPLE CRON FILE
+.Bd -literal
+
+# use /bin/sh to run commands, no matter what /etc/passwd says
+SHELL=/bin/sh
+# mail any output to `paul', no matter whose crontab this is
+MAILTO=paul
+#
+# run five minutes after midnight, every day
+5 0 * * * $HOME/bin/daily.job >> $HOME/tmp/out 2>&1
+# run at 2:15pm on the first of every month -- output mailed to paul
+15 14 1 * * $HOME/bin/monthly
+# run at 10 pm on weekdays, annoy Joe
+0 22 * * 1-5 mail -s "It's 10pm" joe%Joe,%%Where are your kids?%
+23 0-23/2 * * * echo "run 23 minutes after midn, 2am, 4am ..., everyday"
+5 4 * * sun echo "run at 5 after 4 every sunday"
+.Ed
+.Sh SEE ALSO
+.Xr crontab 1 ,
+.Xr cron 8
+.Sh EXTENSIONS
+When specifying day of week, both day 0 and day 7 will be considered Sunday.
+BSD and ATT seem to disagree about this.
+.Pp
+Lists and ranges are allowed to co-exist in the same field. "1-3,7-9" would
+be rejected by ATT or BSD cron -- they want to see "1-3" or "7,8,9" ONLY.
+.Pp
+Ranges can include "steps", so "1-9/2" is the same as "1,3,5,7,9".
+.Pp
+Names of months or days of the week can be specified by name.
+.Pp
+Environment variables can be set in the crontab. In BSD or ATT, the
+environment handed to child processes is basically the one from
+.Pa /etc/rc .
+.Pp
+Command output is mailed to the crontab owner (BSD can't do this), can be
+mailed to a person other than the crontab owner (SysV can't do this), or the
+feature can be turned off and no mail will be sent at all (SysV can't do this
+either).
+.Sh AUTHOR
+.An Paul Vixie Aq paul@vix.com
diff --git a/usr.sbin/cron/crontab/crontab.c b/usr.sbin/cron/crontab/crontab.c
new file mode 100644
index 0000000..89a2d75
--- /dev/null
+++ b/usr.sbin/cron/crontab/crontab.c
@@ -0,0 +1,593 @@
+/* Copyright 1988,1990,1993,1994 by Paul Vixie
+ * All rights reserved
+ *
+ * Distribute freely, except: don't remove my name from the source or
+ * documentation (don't take credit for my work), mark your changes (don't
+ * get me blamed for your possible bugs), don't alter or remove this
+ * notice. May be sold if buildable source is provided to buyer. No
+ * warrantee of any kind, express or implied, is included with this
+ * software; use at your own risk, responsibility for damages (if any) to
+ * anyone resulting from the use of this software rests entirely with the
+ * user.
+ *
+ * Send bug reports, bug fixes, enhancements, requests, flames, etc., and
+ * I'll try to keep a version up to date. I can be reached as follows:
+ * Paul Vixie <paul@vix.com> uunet!decwrl!vixie!paul
+ * From Id: crontab.c,v 2.13 1994/01/17 03:20:37 vixie Exp
+ */
+
+#if !defined(lint) && !defined(LINT)
+static const char rcsid[] =
+ "$Id: crontab.c,v 1.10 1997/03/31 05:09:58 imp Exp $";
+#endif
+
+/* crontab - install and manage per-user crontab files
+ * vix 02may87 [RCS has the rest of the log]
+ * vix 26jan87 [original]
+ */
+
+#define MAIN_PROGRAM
+
+#include "cron.h"
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#ifdef USE_UTIMES
+# include <sys/time.h>
+#else
+# include <time.h>
+# include <utime.h>
+#endif
+#if defined(POSIX)
+# include <locale.h>
+#endif
+
+
+#define NHEADER_LINES 3
+
+
+enum opt_t { opt_unknown, opt_list, opt_delete, opt_edit, opt_replace };
+
+#if DEBUGGING
+static char *Options[] = { "???", "list", "delete", "edit", "replace" };
+#endif
+
+
+static PID_T Pid;
+static char User[MAX_UNAME], RealUser[MAX_UNAME];
+static char Filename[MAX_FNAME];
+static FILE *NewCrontab;
+static int CheckErrorCount;
+static enum opt_t Option;
+static struct passwd *pw;
+static void list_cmd __P((void)),
+ delete_cmd __P((void)),
+ edit_cmd __P((void)),
+ poke_daemon __P((void)),
+ check_error __P((char *)),
+ parse_args __P((int c, char *v[]));
+static int replace_cmd __P((void));
+
+
+static void
+usage(msg)
+ char *msg;
+{
+ fprintf(stderr, "crontab: usage error: %s\n", msg);
+ fprintf(stderr, "%s\n%s\n",
+ "usage: crontab [-u user] file",
+ " crontab [-u user] { -e | -l | -r }");
+ exit(ERROR_EXIT);
+}
+
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int exitstatus;
+
+ Pid = getpid();
+ ProgramName = argv[0];
+
+#if defined(POSIX)
+ setlocale(LC_ALL, "");
+#endif
+
+#if defined(BSD)
+ setlinebuf(stderr);
+#endif
+ parse_args(argc, argv); /* sets many globals, opens a file */
+ set_cron_uid();
+ set_cron_cwd();
+ if (!allowed(User)) {
+ warnx("you (%s) are not allowed to use this program", User);
+ log_it(RealUser, Pid, "AUTH", "crontab command not allowed");
+ exit(ERROR_EXIT);
+ }
+ exitstatus = OK_EXIT;
+ switch (Option) {
+ case opt_list: list_cmd();
+ break;
+ case opt_delete: delete_cmd();
+ break;
+ case opt_edit: edit_cmd();
+ break;
+ case opt_replace: if (replace_cmd() < 0)
+ exitstatus = ERROR_EXIT;
+ break;
+ case opt_unknown:
+ break;
+ }
+ exit(0);
+ /*NOTREACHED*/
+}
+
+
+static void
+parse_args(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int argch;
+
+ if (!(pw = getpwuid(getuid())))
+ errx(ERROR_EXIT, "your UID isn't in the passwd file, bailing out");
+ (void) strncpy(User, pw->pw_name, (sizeof User)-1);
+ User[(sizeof User)-1] = '\0';
+ strcpy(RealUser, User);
+ Filename[0] = '\0';
+ Option = opt_unknown;
+ while ((argch = getopt(argc, argv, "u:lerx:")) != -1) {
+ switch (argch) {
+ case 'x':
+ if (!set_debug_flags(optarg))
+ usage("bad debug option");
+ break;
+ case 'u':
+ if (getuid() != ROOT_UID)
+ errx(ERROR_EXIT, "must be privileged to use -u");
+ if (!(pw = getpwnam(optarg)))
+ errx(ERROR_EXIT, "user `%s' unknown", optarg);
+ (void) strncpy(User, pw->pw_name, (sizeof User)-1);
+ User[(sizeof User)-1] = '\0';
+ break;
+ case 'l':
+ if (Option != opt_unknown)
+ usage("only one operation permitted");
+ Option = opt_list;
+ break;
+ case 'r':
+ if (Option != opt_unknown)
+ usage("only one operation permitted");
+ Option = opt_delete;
+ break;
+ case 'e':
+ if (Option != opt_unknown)
+ usage("only one operation permitted");
+ Option = opt_edit;
+ break;
+ default:
+ usage("unrecognized option");
+ }
+ }
+
+ endpwent();
+
+ if (Option != opt_unknown) {
+ if (argv[optind] != NULL) {
+ usage("no arguments permitted after this option");
+ }
+ } else {
+ if (argv[optind] != NULL) {
+ Option = opt_replace;
+ (void) strncpy (Filename, argv[optind], (sizeof Filename)-1);
+ Filename[(sizeof Filename)-1] = '\0';
+
+ } else {
+ usage("file name must be specified for replace");
+ }
+ }
+
+ if (Option == opt_replace) {
+ /* we have to open the file here because we're going to
+ * chdir(2) into /var/cron before we get around to
+ * reading the file.
+ */
+ if (!strcmp(Filename, "-")) {
+ NewCrontab = stdin;
+ } else {
+ /* relinquish the setuid status of the binary during
+ * the open, lest nonroot users read files they should
+ * not be able to read. we can't use access() here
+ * since there's a race condition. thanks go out to
+ * Arnt Gulbrandsen <agulbra@pvv.unit.no> for spotting
+ * the race.
+ */
+
+ if (swap_uids() < OK)
+ err(ERROR_EXIT, "swapping uids");
+ if (!(NewCrontab = fopen(Filename, "r")))
+ err(ERROR_EXIT, "%s", Filename);
+ if (swap_uids() < OK)
+ err(ERROR_EXIT, "swapping uids back");
+ }
+ }
+
+ Debug(DMISC, ("user=%s, file=%s, option=%s\n",
+ User, Filename, Options[(int)Option]))
+}
+
+
+static void
+list_cmd() {
+ char n[MAX_FNAME];
+ FILE *f;
+ int ch;
+
+ log_it(RealUser, Pid, "LIST", User);
+ (void) sprintf(n, CRON_TAB(User));
+ if (!(f = fopen(n, "r"))) {
+ if (errno == ENOENT)
+ errx(ERROR_EXIT, "no crontab for %s", User);
+ else
+ err(ERROR_EXIT, "%s", n);
+ }
+
+ /* file is open. copy to stdout, close.
+ */
+ Set_LineNum(1)
+ while (EOF != (ch = get_char(f)))
+ putchar(ch);
+ fclose(f);
+}
+
+
+static void
+delete_cmd() {
+ char n[MAX_FNAME];
+
+ log_it(RealUser, Pid, "DELETE", User);
+ (void) sprintf(n, CRON_TAB(User));
+ if (unlink(n)) {
+ if (errno == ENOENT)
+ errx(ERROR_EXIT, "no crontab for %s", User);
+ else
+ err(ERROR_EXIT, "%s", n);
+ }
+ poke_daemon();
+}
+
+
+static void
+check_error(msg)
+ char *msg;
+{
+ CheckErrorCount++;
+ fprintf(stderr, "\"%s\":%d: %s\n", Filename, LineNumber-1, msg);
+}
+
+
+static void
+edit_cmd() {
+ char n[MAX_FNAME], q[MAX_TEMPSTR], *editor;
+ FILE *f;
+ int ch, t, x;
+ struct stat statbuf;
+ time_t mtime;
+ WAIT_T waiter;
+ PID_T pid, xpid;
+ mode_t um;
+
+ log_it(RealUser, Pid, "BEGIN EDIT", User);
+ (void) sprintf(n, CRON_TAB(User));
+ if (!(f = fopen(n, "r"))) {
+ if (errno != ENOENT)
+ err(ERROR_EXIT, "%s", n);
+ warnx("no crontab for %s - using an empty one", User);
+ if (!(f = fopen("/dev/null", "r")))
+ err(ERROR_EXIT, "/dev/null");
+ }
+
+ um = umask(077);
+ (void) sprintf(Filename, "/tmp/crontab.XXXXXXXXXX");
+ if ((t = mkstemp(Filename)) == -1) {
+ warn("%s", Filename);
+ (void) umask(um);
+ goto fatal;
+ }
+ (void) umask(um);
+#ifdef HAS_FCHOWN
+ if (fchown(t, getuid(), getgid()) < 0) {
+#else
+ if (chown(Filename, getuid(), getgid()) < 0) {
+#endif
+ warn("fchown");
+ goto fatal;
+ }
+ if (!(NewCrontab = fdopen(t, "w"))) {
+ warn("fdopen");
+ goto fatal;
+ }
+
+ Set_LineNum(1)
+
+ /* ignore the top few comments since we probably put them there.
+ */
+ for (x = 0; x < NHEADER_LINES; x++) {
+ ch = get_char(f);
+ if (EOF == ch)
+ break;
+ if ('#' != ch) {
+ putc(ch, NewCrontab);
+ break;
+ }
+ while (EOF != (ch = get_char(f)))
+ if (ch == '\n')
+ break;
+ if (EOF == ch)
+ break;
+ }
+
+ /* copy the rest of the crontab (if any) to the temp file.
+ */
+ if (EOF != ch)
+ while (EOF != (ch = get_char(f)))
+ putc(ch, NewCrontab);
+ fclose(f);
+ if (fclose(NewCrontab))
+ err(ERROR_EXIT, "%s", Filename);
+ again:
+ if (stat(Filename, &statbuf) < 0) {
+ warn("stat");
+ fatal: unlink(Filename);
+ exit(ERROR_EXIT);
+ }
+ mtime = statbuf.st_mtime;
+
+ if ((!(editor = getenv("VISUAL")))
+ && (!(editor = getenv("EDITOR")))
+ ) {
+ editor = EDITOR;
+ }
+
+ /* we still have the file open. editors will generally rewrite the
+ * original file rather than renaming/unlinking it and starting a
+ * new one; even backup files are supposed to be made by copying
+ * rather than by renaming. if some editor does not support this,
+ * then don't use it. the security problems are more severe if we
+ * close and reopen the file around the edit.
+ */
+
+ switch (pid = fork()) {
+ case -1:
+ warn("fork");
+ goto fatal;
+ case 0:
+ /* child */
+ if (setuid(getuid()) < 0)
+ err(ERROR_EXIT, "setuid(getuid())");
+ if (chdir("/tmp") < 0)
+ err(ERROR_EXIT, "chdir(/tmp)");
+ if (strlen(editor) + strlen(Filename) + 2 >= MAX_TEMPSTR)
+ errx(ERROR_EXIT, "editor or filename too long");
+ execlp(editor, editor, Filename, NULL);
+ err(ERROR_EXIT, "%s", editor);
+ /*NOTREACHED*/
+ default:
+ /* parent */
+ break;
+ }
+
+ /* parent */
+ {
+ void (*f[4])();
+ f[0] = signal(SIGHUP, SIG_IGN);
+ f[1] = signal(SIGINT, SIG_IGN);
+ f[2] = signal(SIGTERM, SIG_IGN);
+ xpid = wait(&waiter);
+ signal(SIGHUP, f[0]);
+ signal(SIGINT, f[1]);
+ signal(SIGTERM, f[2]);
+ }
+ if (xpid != pid) {
+ warnx("wrong PID (%d != %d) from \"%s\"", xpid, pid, editor);
+ goto fatal;
+ }
+ if (WIFEXITED(waiter) && WEXITSTATUS(waiter)) {
+ warnx("\"%s\" exited with status %d", editor, WEXITSTATUS(waiter));
+ goto fatal;
+ }
+ if (WIFSIGNALED(waiter)) {
+ warnx("\"%s\" killed; signal %d (%score dumped)",
+ editor, WTERMSIG(waiter), WCOREDUMP(waiter) ?"" :"no ");
+ goto fatal;
+ }
+ if (stat(Filename, &statbuf) < 0) {
+ warn("stat");
+ goto fatal;
+ }
+ if (mtime == statbuf.st_mtime) {
+ warnx("no changes made to crontab");
+ goto remove;
+ }
+ warnx("installing new crontab");
+ if (!(NewCrontab = fopen(Filename, "r"))) {
+ warn("%s", Filename);
+ goto fatal;
+ }
+ switch (replace_cmd()) {
+ case 0:
+ break;
+ case -1:
+ for (;;) {
+ printf("Do you want to retry the same edit? ");
+ fflush(stdout);
+ q[0] = '\0';
+ (void) fgets(q, sizeof q, stdin);
+ switch (islower(q[0]) ? q[0] : tolower(q[0])) {
+ case 'y':
+ goto again;
+ case 'n':
+ goto abandon;
+ default:
+ fprintf(stderr, "Enter Y or N\n");
+ }
+ }
+ /*NOTREACHED*/
+ case -2:
+ abandon:
+ warnx("edits left in %s", Filename);
+ goto done;
+ default:
+ warnx("panic: bad switch() in replace_cmd()");
+ goto fatal;
+ }
+ remove:
+ unlink(Filename);
+ done:
+ log_it(RealUser, Pid, "END EDIT", User);
+}
+
+
+/* returns 0 on success
+ * -1 on syntax error
+ * -2 on install error
+ */
+static int
+replace_cmd() {
+ char n[MAX_FNAME], envstr[MAX_ENVSTR], tn[MAX_FNAME];
+ FILE *tmp;
+ int ch, eof;
+ entry *e;
+ time_t now = time(NULL);
+ char **envp = env_init();
+
+ if (envp == NULL) {
+ warnx("cannot allocate memory");
+ return (-2);
+ }
+
+ (void) sprintf(n, "tmp.%d", Pid);
+ (void) sprintf(tn, CRON_TAB(n));
+ if (!(tmp = fopen(tn, "w+"))) {
+ warn("%s", tn);
+ return (-2);
+ }
+
+ /* write a signature at the top of the file.
+ *
+ * VERY IMPORTANT: make sure NHEADER_LINES agrees with this code.
+ */
+ fprintf(tmp, "# DO NOT EDIT THIS FILE - edit the master and reinstall.\n");
+ fprintf(tmp, "# (%s installed on %-24.24s)\n", Filename, ctime(&now));
+ fprintf(tmp, "# (Cron version -- %s)\n", rcsid);
+
+ /* copy the crontab to the tmp
+ */
+ Set_LineNum(1)
+ while (EOF != (ch = get_char(NewCrontab)))
+ putc(ch, tmp);
+ fclose(NewCrontab);
+ ftruncate(fileno(tmp), ftell(tmp));
+ fflush(tmp); rewind(tmp);
+
+ if (ferror(tmp)) {
+ warnx("error while writing new crontab to %s", tn);
+ fclose(tmp); unlink(tn);
+ return (-2);
+ }
+
+ /* check the syntax of the file being installed.
+ */
+
+ /* BUG: was reporting errors after the EOF if there were any errors
+ * in the file proper -- kludged it by stopping after first error.
+ * vix 31mar87
+ */
+ Set_LineNum(1 - NHEADER_LINES)
+ CheckErrorCount = 0; eof = FALSE;
+ while (!CheckErrorCount && !eof) {
+ switch (load_env(envstr, tmp)) {
+ case ERR:
+ eof = TRUE;
+ break;
+ case FALSE:
+ e = load_entry(tmp, check_error, pw, envp);
+ if (e)
+ free(e);
+ break;
+ case TRUE:
+ break;
+ }
+ }
+
+ if (CheckErrorCount != 0) {
+ warnx("errors in crontab file, can't install");
+ fclose(tmp); unlink(tn);
+ return (-1);
+ }
+
+#ifdef HAS_FCHOWN
+ if (fchown(fileno(tmp), ROOT_UID, -1) < OK)
+#else
+ if (chown(tn, ROOT_UID, -1) < OK)
+#endif
+ {
+ warn("chown");
+ fclose(tmp); unlink(tn);
+ return (-2);
+ }
+
+#ifdef HAS_FCHMOD
+ if (fchmod(fileno(tmp), 0600) < OK)
+#else
+ if (chmod(tn, 0600) < OK)
+#endif
+ {
+ warn("chown");
+ fclose(tmp); unlink(tn);
+ return (-2);
+ }
+
+ if (fclose(tmp) == EOF) {
+ warn("fclose");
+ unlink(tn);
+ return (-2);
+ }
+
+ (void) sprintf(n, CRON_TAB(User));
+ if (rename(tn, n)) {
+ warn("error renaming %s to %s", tn, n);
+ unlink(tn);
+ return (-2);
+ }
+ log_it(RealUser, Pid, "REPLACE", User);
+
+ poke_daemon();
+
+ return (0);
+}
+
+
+static void
+poke_daemon() {
+#ifdef USE_UTIMES
+ struct timeval tvs[2];
+ struct timezone tz;
+
+ (void) gettimeofday(&tvs[0], &tz);
+ tvs[1] = tvs[0];
+ if (utimes(SPOOL_DIR, tvs) < OK) {
+ warn("can't update mtime on spooldir %s", SPOOL_DIR);
+ return;
+ }
+#else
+ if (utime(SPOOL_DIR, NULL) < OK) {
+ warn("can't update mtime on spooldir %s", SPOOL_DIR);
+ return;
+ }
+#endif /*USE_UTIMES*/
+}
diff --git a/usr.sbin/cron/doc/CHANGES b/usr.sbin/cron/doc/CHANGES
new file mode 100644
index 0000000..59f6803
--- /dev/null
+++ b/usr.sbin/cron/doc/CHANGES
@@ -0,0 +1,155 @@
+Vixie Cron Changes from V2 to V3
+Paul Vixie
+29-Dec-1993
+
+The crontab command now conforms to POSIX 1003.2. This means that when you
+install it, if you have any "crontab" command lines floating around in shell
+scripts (such as /etc/rc or /etc/rc.local), you will need to change them.
+
+I have integrated several changes made by BSDi for their BSD/386 operating
+system; these were offerred to me before I started consulting for them, so
+it is safe to say that they were intended for publication. Most notably,
+the name of the cron daemon has changed from "crond" to "cron". This was
+done for compatibility with 4.3BSD. Another change made for the same reason
+is the ability to read in an /etc/crontab file which has an extra field in
+each entry, between the time fields and the command. This field is a user
+name, and it permits the /etc/crontab command to contain commands which are
+to be run by any user on the system. /etc/crontab is not "installed" via
+the crontab(1) command; it is automatically read at startup time and it will
+be reread whenever it changes.
+
+I also added a "-e" option to crontab(1). Nine people also sent me diffs
+to add this option, but I had already implemented it on my own. I actually
+released an interrim version (V2.2, I think) for limited testing, and got a
+chance to fix a bad security bug in the "-e" option thanks to XXX.
+
+The daemon used to be extraordinarily sloppy in its use of file descriptors.
+A heck of a lot of them were left open in spawned jobs, which caused problems
+for the daemon and also caused problems with the spawned jobs if they were
+shell scripts since "sh" and "csh" have traditionally used hidden file
+descriptors to pass information to subshells, and cron was causing them to
+think they were subshells. If you had trouble with "sh" or "csh" scripts in
+V2, chances are good that V3 will fix your problems.
+
+About a dozen people have reminded me that I forgot to initialize
+"crontab_fd" in database.c. Keith Cantrell was the first, so he gets the
+point.
+
+Steve Simmons reminded me that once an account has been deleted from the
+system, "crontab -u USER -d" will not work. My solution is to suggest to
+all of you that before you delete a user's account, you first delete that
+user's crontab file if any. From cron's point of view, usernames can never
+be treated as arbitrary strings. Either they are valid user names, or they
+are not. I will not make an exception for the "-d" case, for security
+reasons that I consider reasonable. It is trivial for a root user to delete
+the entry by hand if necessary.
+
+Dan O'Neil reminded me that I forgot to reset "log_fd" in misc.c. A lot of
+others also reminded me of this, but Dan gets the point. I didn't fix it
+there, since the real bug was that it should have been open in the parent.
+
+Peter Kabal reminded me that I forgot to "#ifdef DEBUGGING" some code in
+misc.c. Hans Trompert actually told me first, but Peter sent the patch so
+he gets the point.
+
+Russell Nelson told me that I'd forgotten to "#include <syslog.h>" in misc.c,
+which explains why a lot of other people complained that it wasn't using
+syslog even when they configured it that way :-). Steve Simmons told me
+first, though, so he gets the point.
+
+An interrim version of the daemon tried to "stat" every file before
+executing it; this turned out to be a horribly bad idea since finding the
+name of a file from a shell command is a hard job (that's why we have
+shells, right?) I removed this bogus code. Dave Burgess gets the point.
+
+Dennis R. Conley sent a suggestion for MMDF systems, which I've added to the
+comments in cron.h.
+
+Mike Heisler noted that I use comments in the CONVERSION file which are
+documented as illegal in the man pages. Thanks, Mike.
+
+Irving Wolfe sent me some very cheerful changes for a NeXT system, but I
+consider the system itself broken and I can't bring myself to #ifdef for
+something as screwed up as this system seems to be. However, various others
+did send me smaller patches which appear to have cause cron to build and run
+correctly on (the latest) NeXT machines, with or without the "-posix" CFLAG.
+Irving also asked for a per-job MAILTO, and this was finally added later when
+I integrated the BSD/386 changes contributed by BSDi, and generalized some of
+the parsing.
+
+Lots of folks complained that the autogenerated "Date:" header wasn't in
+ARPA format. I didn't understand this -- either folks will use Sendmail and
+not generate a Date: at all (since Sendmail will do it), or folks will use
+something other than Sendmail which won't care about Date: formats. But
+I've "fixed" it anyway...
+
+Several people suggested that "*" should be able to take a "/step". One person
+suggested that "N/step" ought to mean "N-last/step", but that's stretching things
+a bit far. "*/step" seems quite intuitive to me, so I've added it. Colin Plumb
+sent in the first and most polite request for this feature.
+
+As with every release of Cron, BIND, and seemingly everything else I do, one
+user stands out with the most critical but also the most useful analysis.
+Cron V3's high score belongs to Peter Holzer, who sent in the nicest looking
+patch for the "%" interpretation problem and also helped me understand a
+tricky bit of badness in the "log_fd" problem.
+
+agulbra@flode.nvg.unit.no wins the honors for being the first to point out the
+nasty security hole in "crontab -r". 'Nuff said.
+
+Several folks pointed out that log_it() needed to exist even if logging was
+disabled. Some day I will create a tool that will compile a subsystem with
+every possible combination and permutation of #ifdef options, but meanwhile
+thanks to everybody.
+
+job_runqueue() was using storage after freeing it, since Jordan told me back
+in 1983 that C let you do that, and I believed him in 1986 when I wrote all
+this junk. Linux was the first to die from this error, and the Linux people
+sent me the most amazing, um, collection of patches for this problem. Thanks
+for all the fish.
+
+Jeremy Bettis reminded me that popen() isn't safe. I grabbed Ken Arnold's
+version of popen/pclose from the ftpd and hacked it to taste. We're safe now,
+from this at least.
+
+Branko Lankester sent me a very timely and helpful fix for a looming security
+problem in my "crontab -e" implementation.
+
+--------
+
+Vixie Cron Changes from V1 to V2
+Paul Vixie
+8-Feb-1988
+
+Many changes were made in a rash of activity about six months ago, the exact
+list of which is no longer clear in my memory. I know that V1 used a file
+called POKECRON in /usr/spool/cron to tell it that it was time to re-read
+all the crontab files; V2 uses the modtime the crontab directory as a flag to
+check out the crontab files; those whose modtime has changed will be re-read,
+and the others left alone. Note that the crontab(1) command will do a utimes
+call to make sure the mtime of the dir changes, since the filename/inode will
+often remain the same after a replacement and the mtime wouldn't change in
+that case.
+
+8-Feb-88: made it possible to use much larger environment variable strings.
+ V1 allowed 100 characters; V2 allows 1000. This was needed for PATH
+ variables on some systems. Thanks to Toerless Eckert for this idea.
+ E-mail: UUCP: ...pyramid!fauern!faui10!eckert
+
+16-Feb-88: added allow/deny, moved /usr/spool/cron/crontabs to
+ /usr/lib/cron/tabs. allow and deny are /usr/lib/cron/{allow,deny},
+ since the sysv naming for this depends on 'at' using the same
+ dir, which would be stupid (hint: use /usr/{lib,spool}/at).
+
+22-Feb-88: made it read the spool directory for crontabs and look each one
+ up using getpwnam() rather than reading all passwds with getpwent()
+ and trying to open each crontab.
+
+9-Dec-88: made it sync to :00 after the minute, makes cron predictable.
+ added logging to /var/cron/log.
+
+14-Apr-90: (actually, changes since December 1989)
+ fixed a number of bugs reported from the net and from John Gilmore.
+ added syslog per Keith Bostic. security features including not
+ being willing to run a command owned or writable by other than
+ the owner of the crontab 9not working well yet)
diff --git a/usr.sbin/cron/doc/CONVERSION b/usr.sbin/cron/doc/CONVERSION
new file mode 100644
index 0000000..c18be0e
--- /dev/null
+++ b/usr.sbin/cron/doc/CONVERSION
@@ -0,0 +1,85 @@
+$Id$
+
+Conversion of BSD 4.[23] crontab files:
+
+Edit your current crontab (/usr/lib/crontab) into little pieces, with each
+users' commands in a different file. This is different on 4.2 and 4.3,
+but I'll get to that below. The biggest feature of this cron is that you
+can move 'news' and 'uucp' cron commands into files owned and maintainable
+by those two users. You also get to rip all the fancy 'su' footwork out
+of the cron commands. On 4.3, there's no need for the 'su' stuff since the
+user name appears on each command -- but I'd still rather have separate
+crontabs with seperate environments and so on.
+
+Leave the original /usr/lib/crontab! This cron doesn't use it, so you may
+as well keep it around for a while in case something goes wakko with this
+fancy version.
+
+Most commands in most crontabs are run by root, have to run by root, and
+should continue to be run by root. They still have to be in their own file;
+I recommend /etc/crontab.src or /usr/adm/crontab.src.
+
+'uucp's commands need their own file; how about /usr/lib/uucp/crontab.src?
+'news' also, perhaps in /usr/lib/news/crontab.src...
+
+I say `how about' and `perhaps' because it really doesn't matter to anyone
+(except you) where you put the crontab source files. The `crontab' command
+COPIES them into a protected directory (CRONDIR/SPOOL_DIR in cron.h), named
+after the user whose crontab it is. If you want to examine, replace, or
+delete a crontab, the `crontab' command does all of those things. The
+various `crontab.src' (my suggested name for them) files are just source
+files---they have to be copied to SPOOLDIR using `crontab' before they'll be
+executed.
+
+On 4.2, your crontab might have a few lines like this:
+
+ 5 * * * * su uucp < /usr/lib/uucp/uudemon.hr
+ 10 4 * * * su uucp < /usr/lib/uucp/uudemon.day
+ 15 5 * * 0 su uucp < /usr/lib/uucp/uudemon.wk
+
+...or like this:
+
+ 5 * * * * echo /usr/lib/uucp/uudemon.hr | su uucp
+ 10 4 * * * echo /usr/lib/uucp/uudemon.day | su uucp
+ 15 5 * * 0 echo /usr/lib/uucp/uudemon.wk | su uucp
+
+On 4.3, they'd look a little bit better, but not much:
+
+ 5 * * * * uucp /usr/lib/uucp/uudemon.hr
+ 10 4 * * * uucp /usr/lib/uucp/uudemon.day
+ 15 5 * * 0 uucp /usr/lib/uucp/uudemon.wk
+
+For this cron, you'd create /usr/lib/uucp/crontab.src (or wherever you want
+to keep uucp's commands) which would look like this:
+
+ # /usr/lib/uucp/crontab.src - uucp's crontab
+ #
+ PATH=/usr/lib/uucp:/bin:/usr/bin
+ SHELL=/bin/sh
+ HOME=/usr/lib/uucp
+ #
+ 5 * * * * uudemon.hr
+ 10 4 * * * uudemon.day
+ 15 5 * * 0 uudemon.wk
+
+The application to the `news' cron commands (if any) is left for you to
+figure out. Likewise if there are any other cruddy-looking 'su' commands in
+your crontab commands, you don't need them anymore: just find a good place
+to put the `crontab.src' (or whatever you want to call it) file for that
+user, put the cron commands into it, and install it using the `crontab'
+command (probably with "-u USERNAME", but see the man page).
+
+If you run a 4.2-derived cron, you could of course just install your current
+crontab in toto as root's crontab. It would work exactly the way your
+current one does, barring the extra steps in installing or changing it.
+There would still be advantages to this cron, mostly that you get mail if
+there is any output from your cron commands.
+
+One note about getting mail from cron: you will probably find, after you
+install this version of cron, that your cron commands are generating a lot
+of irritating output. The work-around for this is to redirect all EXPECTED
+output to a per-execution log file, which you can examine if you want to
+see the output from the "last time" a command was executed; if you get any
+UNEXPECTED output, it will be mailed to you. This takes a while to get
+right, but it's amazingly convenient. Trust me.
+
diff --git a/usr.sbin/cron/doc/FEATURES b/usr.sbin/cron/doc/FEATURES
new file mode 100644
index 0000000..3c19cef
--- /dev/null
+++ b/usr.sbin/cron/doc/FEATURES
@@ -0,0 +1,84 @@
+$Id$
+
+Features of Vixie's cron relative to BSD 4.[23] and SysV crons:
+
+-- Environment variables can be set in each crontab. SHELL, USER,
+ LOGNAME, and HOME are set from the user's passwd entry; all except
+ USER can be changed in the crontab. PATH is especially useful to
+ set there. TZ can be set, but cron ignores it other than passing
+ it on through to the commands it runs. Format is
+
+ variable=value
+
+ Blanks surrounding the '=' will be eaten; other blanks in value are
+ okay. Leading or trailing blanks can be preserved by quoting, single
+ or double quotes are okay, just so they match.
+
+ PATH=.:/bin:/usr/bin
+ SHELL=/bin/sh
+ FOOBAR = this is a long blanky example
+
+ Above, FOOBAR would get "this is a long blanky example" as its value.
+
+ SHELL and HOME will be used when it's time to run a command; if
+ you don't set them, HOME defaults to your /etc/passwd entry
+ and SHELL defaults to /bin/sh.
+
+ MAILTO, if set to the login name of a user on your system, will be the
+ person that cron mails the output of commands in that crontab. This is
+ useful if you decide on BINMAIL when configuring cron.h, since binmail
+ doesn't know anything about aliasing.
+
+-- Weekdays can be specified by name. Case is not significant, but only
+ the first three letters should be specified.
+
+-- Months can likewise be specified by name. Three letters only.
+
+-- Ranges and lists can be mixed. Standard crons won't allow '1,3-5'.
+
+-- Ranges can specify 'step' values. '10-16/2' is like '10,12,14,16'.
+
+-- Sunday is both day 0 and day 7 -- apparently BSD and ATT disagree
+ about this.
+
+-- Each user gets their own crontab file. This is a win over BSD 4.2,
+ where only root has one, and over BSD 4.3, where they made the crontab
+ format incompatible and although the commands can be run by non-root
+ uid's, root is still the only one who can edit the crontab file. This
+ feature mimics the SysV cron.
+
+-- The 'crontab' command is loosely compatible with SysV, but has more
+ options which just generally make more sense. Running crontab with
+ no arguments will print a cute little summary of the command syntax.
+
+-- Comments and blank lines are allowed in the crontab file. Comments
+ must be on a line by themselves; leading whitespace is ignored, and
+ a '#' introduces the comment.
+
+-- (big win) If the `crontab' command changes anything in any crontab,
+ the 'cron' daemon will reload all the tables before running the
+ next iteration. In some crons, you have to kill and restart the
+ daemon whenever you change a crontab. In other crons, the crontab
+ file is reread and reparsed every minute even if it didn't change.
+
+-- In order to support the automatic reload, the crontab files are not
+ readable or writable except by 'crontab' or 'cron'. This is not a
+ problem, since 'crontab' will let you do pretty much whatever you
+ want to your own crontab, or if you are root, to anybody's crontab.
+
+-- If any output is generated by a command (on stdout OR stderr), it will
+ be mailed to the owner of the crontab that contained the command (or
+ MAILTO, see discussion of environment variables, above). The headers
+ of the mail message will include the command that was run, and a
+ complete list of the environment that was passed to it, which will
+ contain (at least) the USER (LOGNAME on SysV), HOME, and SHELL.
+
+-- the dom/dow situation is odd. '* * 1,15 * Sun' will run on the
+ first and fifteenth AND every Sunday; '* * * * Sun' will run *only*
+ on Sundays; '* * 1,15 * *' will run *only* the 1st and 15th. this
+ is why we keep 'e->dow_star' and 'e->dom_star'. I didn't think up
+ this behaviour; it's how cron has always worked but the documentation
+ hasn't been very clear. I have been told that some AT&T crons do not
+ act this way and do the more reasonable thing, which is (IMHO) to "or"
+ the various field-matches together. In that sense this cron may not
+ be completely similar to some AT&T crons.
diff --git a/usr.sbin/cron/doc/INSTALL b/usr.sbin/cron/doc/INSTALL
new file mode 100644
index 0000000..0537c6d
--- /dev/null
+++ b/usr.sbin/cron/doc/INSTALL
@@ -0,0 +1,87 @@
+/* Copyright 1993,1994 by Paul Vixie
+ * All rights reserved
+ *
+ * Distribute freely, except: don't remove my name from the source or
+ * documentation (don't take credit for my work), mark your changes (don't
+ * get me blamed for your possible bugs), don't alter or remove this
+ * notice. May be sold if buildable source is provided to buyer. No
+ * warrantee of any kind, express or implied, is included with this
+ * software; use at your own risk, responsibility for damages (if any) to
+ * anyone resulting from the use of this software rests entirely with the
+ * user.
+ *
+ * Send bug reports, bug fixes, enhancements, requests, flames, etc., and
+ * I'll try to keep a version up to date. I can be reached as follows:
+ * Paul Vixie <paul@vix.com> uunet!decwrl!vixie!paul
+ */
+
+$Id$
+
+Read the comments at the top of the Makefile, then edit the area marked
+'configurable stuff'.
+
+Edit config.h. The stuff I expect you to change is down a bit from the
+top of the file, but it's clearly marked. Also look at pathnames.h.
+
+You don't have to create the /var/cron or /var/cron/tabs directories, since
+both the daemon and the `crontab' program will do this the first time they
+run if they don't exist. You do need to have a /var, though -- just "mkdir
+/var" if you don't have one, or you can "mkdir /usr/var; ln -s /usr/var /var"
+if you expect your /var to have a lot of stuff in it.
+
+You will also need /usr/local/etc and /usr/local/bin directories unless you
+change the Makefile. These will have to be created by hand, but if you are
+a long-time Usenet user you probably have them already. /usr/local/man is
+where I keep my man pages, but I have the source for `man' and you probably
+do not. Therefore you may have to put the man pages into /usr/man/manl,
+which will be hard since there will be name collisions. (Note that the man
+command was originally written by Bill Joy before he left Berkeley, and it
+contains no AT&T code, so it is in UUNET's archive of freely-distributable
+BSD code.)
+
+LINUX note: /usr/include/paths.h on some linux systems shows _PATH_SENDMAIL
+ to be /usr/bin/sendmail even though sendmail is installed in /usr/lib.
+ you should check this out.
+
+say:
+ make all
+
+su and say:
+ make install
+
+Note that if I can get you to "su and say" something just by asking, you have
+a very serious security problem on your system and you should look into it.
+
+Edit your /usr/lib/crontab file into little pieces -- see the CONVERSION file
+for help on this.
+
+Use the `crontab' command to install all the little pieces you just created.
+Some examples (see below before trying any of these!)
+
+ crontab -u uucp -r /usr/lib/uucp/crontab.src
+ crontab -u news -r /usr/lib/news/crontab.src
+ crontab -u root -r /usr/adm/crontab.src
+
+Notes on above examples: (1) the .src files are copied at the time the
+command is issued; changing the source files later will have no effect until
+they are reinstalled with another `crontab -r' command. (2) The crontab
+command will affect the crontab of the person using the command unless `-u
+USER' is given; `-u' only works for root. When using most `su' commands
+under most BSD's, `crontab' will still think of you as yourself even though
+you may think of yourself as root -- so use `-u' liberally. (3) the `-r'
+option stands for `replace'; check the man page for crontab(1) for other
+possibilities.
+
+Kill your existing cron daemon -- do `ps aux' and look for /etc/cron.
+
+Edit your /etc/rc or /etc/rc.local, looking for the line that starts up
+/etc/cron. Comment it out and add a line to start the new cron daemon
+-- usually /usr/local/etc/cron, unless you changed it in the Makefile.
+
+Start up this cron daemon yourself as root. Just type /usr/local/etc/cron
+(or whatever); no '&' is needed since the daemon forks itself and the
+process you executed returns immediately.
+
+ATT notes: for those people unfortunate enough to be stuck on a AT&T UNIX,
+you will need the public-domain "libndir", found in the B News source and in
+any comp.sources.unix archive. You will also need to hack the code some.
diff --git a/usr.sbin/cron/doc/MAIL b/usr.sbin/cron/doc/MAIL
new file mode 100644
index 0000000..551575e
--- /dev/null
+++ b/usr.sbin/cron/doc/MAIL
@@ -0,0 +1,475 @@
+[ this is really old mail that came to me in response to my 1986 posting
+ to usenet asking for feature suggestions before releasing the first
+ version of cron. it is presented here for its entertainment value.
+ --vix ]
+
+$Id$
+
+From ptsfa!lll-crg!ames!acornrc!bob Wed Dec 31 10:07:08 1986
+Date: Wed, 31 Dec 86 08:59:31 pst
+From: lll-crg!ames!acornrc!bob (Bob Weissman)
+To: ptsfa!vixie!paul
+Status: RO
+
+Sure, here's a suggestion: I'd like to be able to run a program, say,
+every two hours. Current cron requires me to write
+0,2,4,6,8,10,12,14,16,18,20,22 in the hours field. How about a notation
+to handle this more elegantly?
+
+<< Okay, I've allowed 0-22/2 as a means of handling this.
+ The time specification for my cron is as follows:
+ specification = range {"," range}
+ range = (start "-" finish ["/" step]) | single-unit
+ This allows "1,3,5-7", which the current cron doesn't (it won't
+ do a range inside a list), and handles your specific need. >>
+
+From drw@mit-eddie Wed Dec 31 18:25:27 1986
+Date: Wed, 31 Dec 86 14:28:19 est
+From: drw@mit-eddie (Dale Worley)
+To: mit-eddie!vixie!paul
+Status: RO
+
+We have a lot of lines in our crontab of the form
+
+ 00 12 * * * su user < /usr/users/user/script.file
+
+This barfs (silently!) on our system (Dec Ultrix 1.2 == 4.2bsd) if
+user's shell is csh. This, I am told, is because csh requires that
+the environment be set up in certain ways, which cron doesn't do.
+(Actually, I believe, it is because /etc/rc, which runs cron, doesn't
+set up the environment enough for csh to run, and cron just inherits
+the situation.) Anyway, the point is that if you find out what csh
+really needs in its environment, you might want to set up cron to
+provide some reasonable defaults (if it isn't supplied by cron's
+parent). Also, could you tell me what csh needs, if you find out, so
+we can hack our /etc/rc?
+
+<< well, the environment IS a problem. processes that cron forks
+ will inherit the environment of the person who ran the cron
+ daemon... I plan to edit out such useless things as TERMCAP,
+ TERM, and the like; supply correct values for HOME, USER, CWD,
+ and whatever else comes to mind. I'll make sure csh works... >>
+From ptsfa!ames!seismo!dgis!generous Thu Jan 1 07:33:17 1987
+Date: Thu Jan 1 10:29:20 1987
+From: ames!seismo!dgis!generous (Curtis Generous)
+To: nike!ptsfa!vixie!paul
+Status: RO
+
+Paul:
+
+One of the limitations of the present versions of cron is the lack
+of the capability of specifying a way to execute a command every
+n units of time.
+
+Here is a good example:
+
+# Present method to start up uucico
+02,12,22,32,42,52 * * * * exec /usr/lib/uucp/uucico -r1
+
+# New method ?? (the ':' here is just one possibility for syntax)
+02:10 * * * * exec /usr/lib/uucp/uucico -r1
+
+This method would prove very helpful for those programs that get started
+every few minutes, making the entry long and not easily readable. The first
+number would specify the base time, and the second number the repetition
+interval.
+
+<< Good idea, but bob@acornrc beat you to it. I used '/' instead of
+ ':'. This is my personal preference, and seems intuitive when you
+ think of the divide operator in C... Does anyone have a preference? >>
+
+From ptsfa!lll-lcc!seismo!decuac!c3pe!c3engr!charles Thu Jan 1 17:04:24 1987
+From: lll-lcc!seismo!c3pe!c3engr!charles (Charles Green)
+To: c3pe!decuac!dolqci!vrdxhq!seismo!lll-lcc!ptsfa!vixie!paul
+Date: Thu Jan 1 19:22:47 1987
+Status: RO
+
+Well, this isn't a compatible extension, but I have in times past wondered
+about a facility to let you start a process at intervals of, say, 17 minutes,
+instead of particular minutes out of each hour.
+
+<< This was a popular request! >>
+
+From seismo!uwvax!astroatc!nicmad!norvax!mann Sun Jan 4 13:04:01 1987
+Date: Fri, 2 Jan 87 09:23:53 cst
+From: lll-lcc!seismo!uwvax!astroatc!nicmad!norvax!mann (Tom Mann)
+To: ptsfa!vixie!paul
+Status: RO
+
+I'm not sure if it is in cron (either SysV or BSD ... if it is, I haven't
+figured it out ) but a comment feature would SURE BE NICE!.
+There are times when I want to comment out an entry
+for a period of time; it might also make it a lot more legible.
+
+<< My cron allows blank lines and standard #-type comments. I know
+ that one BSD4.2 cron I've used had it. I don't know about SysV. >>
+
+From ptsfa!hoptoad!hugh Mon Jan 5 10:26:46 1987
+Date: Mon, 5 Jan 87 01:22:17 PST
+From: hoptoad!hugh (Hugh Daniel)
+To: ptsfa!vixie!paul
+Status: RO
+
+ Hi, I do have a BIG one that I would like. I want to log ALL output
+from command lines into a file for each line. Thus I might have a chance
+of finding out why my crontab entry did not work.
+ This would seem to work best if done by cron, as it is now I have a google
+of shell scripts laying about just to put the error output where I can see
+it.
+
+<< My cron (and the SysV cron) will send mail to the owner of the
+ particular crontab file if a command generates any output on stdout
+ or stderr. This can be irritating, but if you write a script such
+ that any output means a problem occurred, you can avoid most logfile
+ needs, and not generate mail except in unforeseen circumstances. >>
+
+From ptsfa!dual!ucbvax!ihnp4!anvil!es!Robert_Toxen Mon Jan 5 13:08:46 1987
+From: dual!ucbvax!ihnp4!anvil!es!Robert_Toxen
+Date: Fri, 2 Jan 87 14:25:29 EST
+To: anvil!ihnp4!ucbvax!dual!ptsfa!vixie!paul
+Status: RO
+
+Here are some suggestions:
+1. Run it through the C preprocessor via "/lib/<whatever>".
+
+<< hmmm. this seems of limited utility, and if you really wanted
+ to do it that way, you could do it yourself (since users can
+ write to their own crontab files). I'll add '-' (read stdin)
+ to the crontab installer program to facilitate this. >>
+
+2. Allow specifying every Nth day of week, i.e., every second Wednesday.
+ I did this to calendar by separating the day of week (Wed=4, which one
+ to start on and N with slashes). I took modulo the day of year as a
+ starting point so that someone with a desk calendar documenting such
+ things can easily determine the offset (second number). I did this
+ while at SGI; alas I don't have a copy of the code.
+
+<< I can see how this could be useful, but I'm not sure how I'd
+ implement it. Cron currently doesn't keep track of the last time
+ a given command was run; whether the current Wednesday is the first
+ or second since the command was last run would be pretty hard to
+ figure out. I'd have to keep a database of commands and their
+ execution around, and purge it when the crontab was overwritten.
+ This is too much work for me, but if someone adds it, let me know. >>
+
+From ptsfa!ames!seismo!cbmvax!devon!paul Tue Jan 6 05:50:17 1987
+From: ames!seismo!cbmvax!devon!paul
+To: cbmvax!seismo!nike!ptsfa!vixie!paul
+Date: Mon Jan 5 09:29:57 1987
+Status: RO
+
+One problem that has always plagued me with cron is the assumed ORing.
+I'd like to see some type of ANDing implemented. I guess I can best
+describe this by example. Say I have the following line in my crontab
+file:
+
+* * 4-31 * 1-6 /usr/bin/command
+
+What this does is run 'command' on the 4th thru 31st days of the
+month, AND on Monday thru Saturday; which probably means running it
+every day of the month (unless Sunday falls on days 1-3). This
+happens because cron runs the command if the day-of-month OR the
+day-of-week is true.
+
+What I'd like to happen with the above line is to run the command ONLY
+on Monday thru Saturday any time after the 3rd of the month, e.g. if
+the day-of-month AND the day-of-week are true.
+
+My proposal to you is to implement some special chars for the first
+five fields. Examples:
+
+* * !1-3 * 1-6 /usr/bin/command
+
+(run command Mon-Sat, but NOT [!] on the first 3 days of the month)
+
+* * &4-31 * &1-6 /usr/bin/command
+
+(run command if day-of-month AND day-of-week are true)
+
+Get the picture? This would be compatable with existing versions of
+cron (which wouldn't currently be using any special characters, so
+that old crontabs would be handled correctly).
+
+<< This message made me aware of the actual boolean expression involved
+ in a crontab entry. I'd assumed that it was
+ (minute && hour && DoM && month && DoW)
+ But it's really
+ (minute && hour && month && (DoM || DoW))
+
+ I can see some value in changing this, but with a fixed order of
+ fields, operators get to be kindof unary, which && and || really
+ aren't. If someone has an idea on a syntax that allows useful
+ variations to the standard (&& && && (||)) default, please suggest. >>
+
+From bobkat!pedz Tue Jan 6 20:02:10 1987
+From: pedz@bobkat.UUCP (Pedz Thing)
+Date: 2 Jan 87 17:34:44 GMT
+Status: RO
+
+Log files! It would be nice to be able to specify a log for cron
+itself and also a log for each program's stdout and stderr to go to.
+The latter can of course be done with > and 2> but it would be nice if
+there could be a single line with some sort of pattern like
+`> /usr/spool/log/%' and the command would be substituted for the %.
+Another thing which would be nice is to be able to specify which shell
+to call to give the command to.
+
+<< Log files are done with mail. The '%' idea could be useful if
+ a different character were used (% is special to cron, see man
+ page); a different directory would have to be chosen, since each
+ user has their own crontab file; and something intelligent would
+ have to be done in the file naming, since the first word of the
+ command might be ambiguous (with other commands). In short, it's
+ too much work. Sorry. >>
+
+From guy%gorodish@sun Tue Jan 6 20:03:13 1987
+From: guy%gorodish@sun (Guy Harris)
+Message-ID: <10944@sun.uucp>
+Date: 5 Jan 87 12:09:09 GMT
+References: <429@vixie.UUCP> <359@bobkat.UUCP>
+Sender: news@sun.uucp
+Status: RO
+
+> Another thing which would be nice is to be able to specify which shell
+> to call to give the command to.
+
+Well, the obvious choice would be the user's shell, but this wouldn't work
+for accounts like "uucico".
+
+<< I use the owning user's shell, and to handle "uucico" I check a
+ list of "acceptable shells" (currently compiled in, does anybody
+ mind?), substituting a default (compiled in) shell if the user's
+ shell isn't on the list.
+
+ BTW, "compiled in" means that it's in a .h file, easily changed
+ during installation, but requiring recompilation to modify. You
+ don't have to go digging through the code to find it... >>
+
+From qantel!hplabs!ucbvax!mwm@violet.berkeley.edu Tue Jan 6 21:24:48 1987
+To: hplabs!qantel!vixie!paul (Paul Vixie Esq)
+Date: 04 Jan 87 00:42:35 PST (Sun)
+From: Mike Meyer <mwm@violet.berkeley.edu>
+Status: RO
+
+<<[Discussion of RMS/FSF, and mwm's GNU Cron deleted]>>
+
+Oh, yeah - here are the extensions on my cron:
+
+1) Sunday is both day 0 and day 7, so it complies with both SysV and
+BSD cron.
+
+<< Good idea. I did it too, thanks for informing me. >>
+
+2) At is integrated into the cron. Instead of atrun to scan the
+/usr/spool/at directory, at files are put into the /usr/lib/cron
+directory along with users cron files, and cron fabricates a line from
+a crontab file to run them. This is considered a major win by all who
+use it.
+
+<< I don't use 'at', and my cron doesn't do anything with it. To run
+ 'at', I use 'atrun' the same way the current BSD cron does. My
+ crontab files are in /usr/spool/cron/crontabs, in the SysV
+ tradition -- not in /usr/lib/cron. This is a configuration
+ parameter, of course. >>
+
+There are two known restrictions:
+
+1) I don't support any of the SysV security hooks. I don't have a use
+for them, and RMS didn't like the idea at all :-).
+
+<< This means cron.allow and cron.deny. I plan to support them, as
+ they've been quite helpful at various HPUX sites I've administered. >>
+
+2) Cron expects to be able to create files with names longer than 14
+characters, which makes it hard to run on SysV. At least one person
+was working on a port, but I don't know how it's going. That might
+make for a good reason for releasing yours, right there.
+
+<< If someone has SysV (with the 14-character limit), they probably
+ won't want my cron, since it doesn't add much to the standard
+ version (which they may have support for). My cron is not currently
+ portable to non-BSD systems, since it relies on interval timers (I
+ needed to sleep for intervals more granular than seconds alone would
+ allow). The port would be trivial, and I will do it if a lot of
+ people ask for it... >>
+
+Oh, yeah - I'm going to see about getting this cron integrated into
+the next 4BSD release.
+
+<< How does one go about this? I have a few nifty gadgets I'd like
+ to contribute, this cron being one of them... >>
+
+<<[more FSF/GNU discussion deleted]>>
+
+From qantel!hplabs!ames!ut-sally!ut-ngp!melpad!bigtex!james Tue Jan 6 21:24:57 1987
+Posted-Date: Fri, 2 Jan 87 19:26:16 est
+Date: Fri, 2 Jan 87 19:26:16 est
+From: hplabs!ames!ut-sally!ut-ngp!bigtex!james
+To: vixie!paul
+Status: RO
+
+Yes!!! There are several critical failures in System V cron...
+
+1. Pass all variables in cron's environment into the environment of things
+ cron starts up, or at least into the crontab entries started up (at jobs
+ will inherit the environment of the user). If nothing else it is critically
+ important that the TZ variable be passed on. PATH should be passed on too.
+ Basically, passing environment values allows one to design a standard
+ environment with TZ and PATH and have that run by everything. If anyone
+ tells you this is no big deal, consider what happens when uucico is
+ started by cron in CA to make a long distance phone link... Unless the
+ administrator is really on his/her toes, calls scheduled at 5pm will really
+ go at two in the afternoon, needlessly incurring huge phone bills, all
+ because System V refuses to pass the TZ from its environment down. There
+ are work arounds, but only putting it in cron will really work. This is
+ not a security hole.
+
+<< delete TERM and TERMCAP; modify HOME, USER, and CWD; pass TZ and
+ PATH through undisturbed. any other requests out there?
+
+ BSD doesn't have this problem -- TZ is passed right on through if
+ you define it in the shell before starting my cron daemon. However,
+ the BSD I'm running this on doesn't need TZ to be defined anyway...
+ The default in the kernel has been just fine so far... But just the
+ same, if/when I port to SysV (I guess I really should), I'll make
+ sure this works right.
+
+ I guess I've been spoiled. HPUX is SysV-based, and I never had a
+ problem with cron and TZ when I used it. >>
+
+2. A way to avoid logging stuff in /usr/lib/cron/log. I have a cron entry
+ run uudemon.hr every 10 minutes. This is 144 times/day. Each run generates
+ three lines of text, for a total of 432 lines of text I don't want to see.
+ Obviously this should be optional, but it would be nice if there were a
+ way to flag an entry so that it wasn't logged at all unless there was an
+ error.
+
+<< I don't know nothin' 'bout no /usr/lib/cron/log. What is this file?
+ I don't see any reason to create log entries, given the mail-the-
+ output behaviour. Opinions, anyone? >>
+
+I will come up with other ideas no doubt, but I can always implement them
+myself.
+
+<< That's what I like about PD software. Please send me the diffs! >>
+
+The other problem you have is making sure you can run standard
+crontabs. I would suggest something like this: if the command part of the
+entry starts with an unescaped -, then flags and options follow immediately
+thereafter. As in:
+
+2,12,22,32,42,52 * * * * -q /usr/lib/uucp/uudemon.hr
+
+This could mean do not log the uudemon.hr run unless there is a problem of
+some kind. This is probably safe as not many filenames start with "-", and
+those that do are already a problem for people.
+
+<< Since I don't plan on supporting /usr/lib/cron/log in ANY form unless
+ many people request it, I won't be needing -q as you've defined it.
+ I could use something like this to avoid sending mail on errors, for
+ the occasional script that you don't want to bullet-proof.
+
+ The compatibility issue is CRITICAL. The 4.3BSD crontab format is
+ a crime against the whole philosophy of Unix(TM), in my opinion. >>
+
+One other minor thing to consider is the ulimit: can different users get
+different ulimits for their crontab entries?
+
+<< Boy I'm ignorant today. What's a ulimit, and what should I do with
+ it in a crontab? Suggestions, enlightenment, etc ?? >>
+
+From qantel!lll-crg!ames!uw-beaver!uw-nsr!john Tue Jan 6 23:32:44 1987
+Date: Thu, 1 Jan 87 10:53:05 pst
+From: lll-crg!ames!uw-beaver!uw-nsr!john (John Sambrook 5-7433)
+To: vixie!paul
+Status: RO
+
+How about not hardwiring the default environment that cron builds for its
+children in the cron program itself? Our cron does this and it's the pits
+because we are TZ=PST8PDT not TZ=EST5EDT !
+
+<< yeachk. I assure you, I will not hardwire the TZ! >>
+From ptsfa!well!dv Fri Jan 9 04:01:50 1987
+Date: Thu, 8 Jan 87 23:50:40 pst
+From: well!dv (David W. Vezie)
+To: ptsfa!vixie!paul
+Status: RO
+
+6, have a special notation called 'H' which would expand to weekends
+ and holidays (you'd have to keep a database somewhere of real
+ holidays), and also 'W' for workdays (neither weekend or holiday).
+
+<< Too much work. There should be a standard way to define and
+ detect holidays under Unix(TM); if there were, I'd use it. As
+ it is, I'll leave this for someone else to add.
+
+ I can see the usefulness; it just doesn't quite seem worth it. >>
+From qantel!gatech!akgua!blnt1!jat Wed Jan 14 20:00:40 1987
+Date: Tue, 13 Jan 87 16:39:38 EST
+From: gatech!akgua!blnt1!jat
+Status: RO
+
+1) Add some way to tell cron to reread the files, say kill -1 <pid>
+
+<< whenever the 'crontab' program is run and updates a crontab file,
+ a file /usr/spool/cron/POKECRON is created; next time the cron
+ daemon wakes up, it sees it, and re-reads the crontab files.
+
+ I thought of handling the signal; even implemented it. Then this
+ clever idea hit me and I ripped it all out and added a single
+ IF-statement to handle the POKECRON file. >>
+
+2) Have some kind of retry time so that if a command fails, cron will try to
+ execute it again after a certain period. This is useful if you have some
+ type of cleanup program that can run at the scheduled time for some reason
+ (such as locked device, unmounted filesystem, etc).
+
+<< Hmmm, sounds useful. I could do this by submitting an 'at' job...
+ I'll think about it. >>
+From ptsfa!dual!ucbvax!ihnp4!mtuxo!ender Sat Jan 3 16:54:00 1987
+From: dual!ucbvax!ihnp4!mtuxo!ender
+Date: Sat, 3 Jan 87 14:05:13 PST
+To: ucbvax!dual!ptsfa!vixie!paul
+Status: RO
+
+It would be nice if nonprivileged users can setup personal crontab files
+(~/.cronrc, say) and be able to run personal jobs at regular intervals.
+
+<< this is done, but in the SysV style: the 'crontab' program installs
+ a new crontab file for the executing user (can be overridden by root
+ for setup of uucp and news). the advantage of this is that (1) when
+ a crontab is changed, the daemon can be informed automatically; and
+ (2) the file can be syntax-checked before installation. >>
+From ptsfa!ames!seismo!ihnp4!lcc!richard Fri Jan 16 04:47:33 1987
+Date: Fri, 16 Jan 87 07:44:57 EST
+To: nike!ptsfa!vixie!paul
+Status: RO
+
+The System V cron is nice, but it has a few annoying features. One is that
+its mail files will say that the previous message is the output of "one of your
+cron commands." I wish it would say WHICH cron commmand.
+
+<< Done. Also which shell, which user (useful when the mail gets
+ forwarded), which home directory, and other useful crud. >>
+
+Another problem is with timezones. It is necessary to specify TZ=PST8PDT (or
+whatever) when you invoke cron (from inittab, or /etc/rc) and it is also
+necessary to add TZ=PST8PDT to each crontab line which might need it. Cron
+should automatically export its idea of the "TZ" to each invoked command, and
+it should be possible to put a line in the crontab file which overrides that
+for every command in the file (e.g., most users are on EST, so cron is run
+with TZ=EST5EDT; but one user is usually on PST and wants all of his cron
+commands to run with TZ=PST8PDT). This might be extended to allow any
+environment variable to be specified once for the whole crontab file (e.g.,
+PATH).
+
+<< Well, since I run the user's shell, you could put this into .cshrc.
+ generic environment-variable setting could be useful, though. Since
+ I have to modify the environment anyway, I'll consider this. >>
+
+A log file might be a nice idea, but the System V cron log is too verbose.
+I seem to remember that cron keeps it open, too; so you can't even have
+something go and periodically clean it out.
+
+<< I don't do /usr/lib/cron/log. I wasn't aware of this file until I
+ got all these suggestions. Do people want this file? Tell me! >>
diff --git a/usr.sbin/cron/doc/Makefile.vixie b/usr.sbin/cron/doc/Makefile.vixie
new file mode 100644
index 0000000..f659223
--- /dev/null
+++ b/usr.sbin/cron/doc/Makefile.vixie
@@ -0,0 +1,128 @@
+#/* Copyright 1988,1990,1993,1994 by Paul Vixie
+# * All rights reserved
+# *
+# * Distribute freely, except: don't remove my name from the source or
+# * documentation (don't take credit for my work), mark your changes (don't
+# * get me blamed for your possible bugs), don't alter or remove this
+# * notice. May be sold if buildable source is provided to buyer. No
+# * warrantee of any kind, express or implied, is included with this
+# * software; use at your own risk, responsibility for damages (if any) to
+# * anyone resulting from the use of this software rests entirely with the
+# * user.
+# *
+# * Send bug reports, bug fixes, enhancements, requests, flames, etc., and
+# * I'll try to keep a version up to date. I can be reached as follows:
+# * Paul Vixie <paul@vix.com> uunet!decwrl!vixie!paul
+# */
+
+# Makefile for vixie's cron
+#
+# $Id$
+#
+# vix 03mar88 [moved to RCS, rest of log is in there]
+# vix 30mar87 [goodbye, time.c; hello, getopt]
+# vix 12feb87 [cleanup for distribution]
+# vix 30dec86 [written]
+
+# NOTES:
+# 'make' can be done by anyone
+# 'make install' must be done by root
+#
+# this package needs getopt(3), bitstring(3), and BSD install(8).
+#
+# the configurable stuff in this makefile consists of compilation
+# options (use -O, cron runs forever) and destination directories.
+# SHELL is for the 'augumented make' systems where 'make' imports
+# SHELL from the environment and then uses it to run its commands.
+# if your environment SHELL variable is /bin/csh, make goes real
+# slow and sometimes does the wrong thing.
+#
+# this package needs the 'bitstring macros' library, which is
+# available from me or from the comp.sources.unix archive. if you
+# put 'bitstring.h' in a non-standard place (i.e., not intuited by
+# cc(1)), you will have to define INCLUDE to set the include
+# directory for cc. INCLUDE should be `-Isomethingorother'.
+#
+# there's more configuration info in config.h; edit that first!
+
+#################################### begin configurable stuff
+#<<DESTROOT is assumed to have ./etc, ./bin, and ./man subdirectories>>
+DESTROOT = $(DESTDIR)/usr
+DESTSBIN = $(DESTROOT)/sbin
+DESTBIN = $(DESTROOT)/bin
+DESTMAN = $(DESTROOT)/share/man
+#<<need bitstring.h>>
+INCLUDE = -I.
+#INCLUDE =
+#<<need getopt()>>
+LIBS =
+#<<optimize or debug?>>
+#OPTIM = -O
+OPTIM = -g
+#<<ATT or BSD or POSIX?>>
+# (ATT untested)
+#COMPAT = -DATT
+#(BSD is only needed if <sys/params.h> does not define it, as on ULTRIX)
+#COMPAT = -DBSD
+# (POSIX)
+#COMPAT = -DPOSIX
+#<<lint flags of choice?>>
+LINTFLAGS = -hbxa $(INCLUDE) $(COMPAT) $(DEBUGGING)
+#<<want to use a nonstandard CC?>>
+#CC = vcc
+#<<manifest defines>>
+DEFS =
+#(SGI IRIX systems need this)
+#DEFS = -D_BSD_SIGNALS -Dconst=
+#<<the name of the BSD-like install program>>
+#INSTALL = installbsd
+INSTALL = install
+#<<any special load flags>>
+LDFLAGS =
+#################################### end configurable stuff
+
+SHELL = /bin/sh
+CFLAGS = $(OPTIM) $(INCLUDE) $(COMPAT) $(DEFS)
+
+INFOS = README CHANGES FEATURES INSTALL CONVERSION THANKS MAIL
+MANPAGES = bitstring.3 crontab.5 crontab.1 cron.8 putman.sh
+HEADERS = bitstring.h cron.h config.h pathnames.h \
+ externs.h compat.h
+SOURCES = cron.c crontab.c database.c do_command.c entry.c \
+ env.c job.c user.c popen.c misc.c compat.c
+SHAR_SOURCE = $(INFOS) $(MANPAGES) Makefile $(HEADERS) $(SOURCES)
+LINT_CRON = cron.c database.c user.c entry.c compat.c \
+ misc.c job.c do_command.c env.c popen.c
+LINT_CRONTAB = crontab.c misc.c entry.c env.c compat.c
+CRON_OBJ = cron.o database.o user.o entry.o job.o do_command.o \
+ misc.o env.o popen.o compat.o
+CRONTAB_OBJ = crontab.o misc.o entry.o env.o compat.o
+
+all : cron crontab
+
+lint :
+ lint $(LINTFLAGS) $(LINT_CRON) $(LIBS) \
+ |grep -v "constant argument to NOT" 2>&1
+ lint $(LINTFLAGS) $(LINT_CRONTAB) $(LIBS) \
+ |grep -v "constant argument to NOT" 2>&1
+
+cron : $(CRON_OBJ)
+ $(CC) $(LDFLAGS) -o cron $(CRON_OBJ) $(LIBS)
+
+crontab : $(CRONTAB_OBJ)
+ $(CC) $(LDFLAGS) -o crontab $(CRONTAB_OBJ) $(LIBS)
+
+install : all
+ $(INSTALL) -c -m 111 -o root -s cron $(DESTSBIN)/
+ $(INSTALL) -c -m 4111 -o root -s crontab $(DESTBIN)/
+ sh putman.sh crontab.1 $(DESTMAN)
+ sh putman.sh cron.8 $(DESTMAN)
+ sh putman.sh crontab.5 $(DESTMAN)
+
+clean :; rm -f *.o cron crontab a.out core tags *~ #*
+
+kit : $(SHAR_SOURCE)
+ makekit -m -s99k $(SHAR_SOURCE)
+
+$(CRON_OBJ) : cron.h compat.h config.h externs.h pathnames.h Makefile
+$(CRONTAB_OBJ) : cron.h compat.h config.h externs.h pathnames.h Makefile
diff --git a/usr.sbin/cron/doc/README b/usr.sbin/cron/doc/README
new file mode 100644
index 0000000..d06e905
--- /dev/null
+++ b/usr.sbin/cron/doc/README
@@ -0,0 +1,72 @@
+#/* Copyright 1988,1990,1993 by Paul Vixie
+# * All rights reserved
+# *
+# * Distribute freely, except: don't remove my name from the source or
+# * documentation (don't take credit for my work), mark your changes (don't
+# * get me blamed for your possible bugs), don't alter or remove this
+# * notice. May be sold if buildable source is provided to buyer. No
+# * warrantee of any kind, express or implied, is included with this
+# * software; use at your own risk, responsibility for damages (if any) to
+# * anyone resulting from the use of this software rests entirely with the
+# * user.
+# *
+# * Send bug reports, bug fixes, enhancements, requests, flames, etc., and
+# * I'll try to keep a version up to date. I can be reached as follows:
+# * Paul Vixie <paul@vix.com> uunet!decwrl!vixie!paul
+# */
+
+Vixie Cron V3.0
+December 27, 1993
+[V2.2 was some time in 1992]
+[V2.1 was May 29, 1991]
+[V2.0 was July 5, 1990]
+[V2.0-beta was December 9, 1988]
+[V1.0 was May 6, 1987]
+Paul Vixie
+
+This is a version of 'cron' that is known to run on BSD 4.[23] systems. It
+is functionally based on the SysV cron, which means that each user can have
+their own crontab file (all crontab files are stored in a read-protected
+directory, usually /var/cron/tabs). No direct support is provided for
+'at'; you can continue to run 'atrun' from the crontab as you have been
+doing. If you don't have atrun (i.e., System V) you are in trouble.
+
+A messages is logged each time a command is executed; also, the files
+"allow" and "deny" in /var/cron can be used to control access to the
+"crontab" command (which installs crontabs). It hasn't been tested on
+SysV, although some effort has gone into making the port an easy one.
+
+This is more or less the copyright that USENET contributed software usually
+has. Since ATT couldn't use this version if they had to freely distribute
+source, and since I'd love to see them use it, I'll offer some rediculously
+low license fee just to have them take it. In the unlikely event that they
+do this, I will continue to support and distribute the pseudo-PD version, so
+please, don't flame me for wanting my work to see a wider distribution.
+
+To use this: Sorry, folks, there is no cutesy 'Configure' script. You'll
+have to go edit a couple of files... So, here's the checklist:
+
+ Read all the FEATURES, INSTALL, and CONVERSION files
+ Edit config.h
+ Edit Makefile
+ (both of these files have instructions inside; note that
+ some things in config.h are definable in Makefile and are
+ therefore surrounded by #ifndef...#endif)
+ 'make'
+ 'su' and 'make install'
+ (you may have to install the man pages by hand)
+ kill your existing cron process
+ (actually you can run your existing cron if you want, but why?)
+ build new crontabs using /usr/lib/{crontab,crontab.local}
+ (either put them all in "root"'s crontab, or divide it up
+ and rip out all the 'su' commands, collapse the lengthy
+ lists into ranges with steps -- basically, this step is
+ as much work as you want to make it)
+ start up the new cron
+ (must be done as root)
+ watch it. test it with 'crontab -r' and watch the daemon track your
+ changes.
+ if you like it, change your /etc/{rc,rc.local} to use it instead of
+ the old one.
+
+$Id$
diff --git a/usr.sbin/cron/doc/README.1ST b/usr.sbin/cron/doc/README.1ST
new file mode 100644
index 0000000..66cd0b2
--- /dev/null
+++ b/usr.sbin/cron/doc/README.1ST
@@ -0,0 +1,4 @@
+Sources substantially rearranged 26 Aug 94, Jordan Hubbard. Content
+remains faithful to the original 3.0 cron source from Paul Vixie, but
+any bugs introduced by this reorganization or the port to FreeBSD remain
+purely my own. - Jordan
diff --git a/usr.sbin/cron/doc/THANKS b/usr.sbin/cron/doc/THANKS
new file mode 100644
index 0000000..3787c29
--- /dev/null
+++ b/usr.sbin/cron/doc/THANKS
@@ -0,0 +1,29 @@
+15 January 1990
+Paul Vixie
+
+Many people have contributed to cron. Many more than I can remember, in fact.
+Rich Salz and Carl Gutekunst were each of enormous help to me in V1; Carl for
+helping me understand UNIX well enough to write it, and Rich for helping me
+get the features right.
+
+John Gilmore wrote me a wonderful review of V2, which took me a whole year to
+answer even though it made me clean up some really awful things in the code.
+(According to John the most awful things are still in here, of course.)
+
+Paul Close made a suggestion which led to /etc/crond.pid and the mutex locking
+on it. Kevin Braunsdorf of Purdue made a suggestion that led to @reboot and
+its brothers and sisters; he also sent some diffs that lead cron toward compil-
+ability with System V, though without at(1) capabilities, this cron isn't going
+to be that useful on System V. Bob Alverson fixed a silly bug in the line
+number counting. Brian Reid made suggestions which led to the run queue and
+the source-file labelling in installed crontabs.
+
+Scott Narveson ported V2 to a Sequent, and sent in the most useful single batch
+of diffs I got from anybody. Changes attributable to Scott are:
+ -> sendmail won't time out if the command is slow to generate output
+ -> day-of-week names aren't off by one anymore
+ -> crontab says the right thing if you do something you shouldn't do
+ -> crontab(5) man page is longer and more informative
+ -> misc changes related to the side effects of fclose()
+ -> Sequent "universe" support added (may also help on Pyramids)
+ -> null pw_shell is dealt with now; default is /bin/sh
diff --git a/usr.sbin/cron/lib/Makefile b/usr.sbin/cron/lib/Makefile
new file mode 100644
index 0000000..5270131
--- /dev/null
+++ b/usr.sbin/cron/lib/Makefile
@@ -0,0 +1,12 @@
+LIB= cron
+
+SRCS= entry.c env.c misc.c
+CFLAGS+=-I${.CURDIR}/../cron
+CFLAGS+=-DLOGIN_CAP
+NOPIC= yes
+NOPROFILE= yes
+
+install:
+ @echo -n
+
+.include <bsd.lib.mk>
diff --git a/usr.sbin/cron/lib/compat.c b/usr.sbin/cron/lib/compat.c
new file mode 100644
index 0000000..2143bc4
--- /dev/null
+++ b/usr.sbin/cron/lib/compat.c
@@ -0,0 +1,236 @@
+/* Copyright 1988,1990,1993,1994 by Paul Vixie
+ * All rights reserved
+ *
+ * Distribute freely, except: don't remove my name from the source or
+ * documentation (don't take credit for my work), mark your changes (don't
+ * get me blamed for your possible bugs), don't alter or remove this
+ * notice. May be sold if buildable source is provided to buyer. No
+ * warrantee of any kind, express or implied, is included with this
+ * software; use at your own risk, responsibility for damages (if any) to
+ * anyone resulting from the use of this software rests entirely with the
+ * user.
+ *
+ * Send bug reports, bug fixes, enhancements, requests, flames, etc., and
+ * I'll try to keep a version up to date. I can be reached as follows:
+ * Paul Vixie <paul@vix.com> uunet!decwrl!vixie!paul
+ */
+
+#if !defined(lint) && !defined(LINT)
+static char rcsid[] = "$Id$";
+#endif
+
+/* vix 30dec93 [broke this out of misc.c - see RCS log for history]
+ * vix 15jan87 [added TIOCNOTTY, thanks csg@pyramid]
+ */
+
+
+#include "cron.h"
+#ifdef NEED_GETDTABLESIZE
+# include <limits.h>
+#endif
+#if defined(NEED_SETSID) && defined(BSD)
+# include <sys/ioctl.h>
+#endif
+#include <errno.h>
+
+
+/* the code does not depend on any of vfork's
+ * side-effects; it just uses it as a quick
+ * fork-and-exec.
+ */
+#ifdef NEED_VFORK
+PID_T
+vfork() {
+ return (fork());
+}
+#endif
+
+
+#ifdef NEED_STRDUP
+char *
+strdup(str)
+ char *str;
+{
+ char *temp;
+
+ if ((temp = malloc(strlen(str) + 1)) == NULL) {
+ errno = ENOMEM;
+ return NULL;
+ }
+ (void) strcpy(temp, str);
+ return temp;
+}
+#endif
+
+
+#ifdef NEED_STRERROR
+char *
+strerror(error)
+ int error;
+{
+ extern char *sys_errlist[];
+ extern int sys_nerr;
+ static char buf[32];
+
+ if ((error <= sys_nerr) && (error > 0)) {
+ return sys_errlist[error];
+ }
+
+ sprintf(buf, "Unknown error: %d", error);
+ return buf;
+}
+#endif
+
+
+#ifdef NEED_STRCASECMP
+int
+strcasecmp(left, right)
+ char *left;
+ char *right;
+{
+ while (*left && (MkLower(*left) == MkLower(*right))) {
+ left++;
+ right++;
+ }
+ return MkLower(*left) - MkLower(*right);
+}
+#endif
+
+
+#ifdef NEED_SETSID
+int
+setsid()
+{
+ int newpgrp;
+# if defined(BSD)
+ int fd;
+# if defined(POSIX)
+ newpgrp = setpgid((pid_t)0, getpid());
+# else
+ newpgrp = setpgrp(0, getpid());
+# endif
+ if ((fd = open("/dev/tty", 2)) >= 0)
+ {
+ (void) ioctl(fd, TIOCNOTTY, (char*)0);
+ (void) close(fd);
+ }
+# else /*BSD*/
+ newpgrp = setpgrp();
+
+ (void) close(STDIN); (void) open("/dev/null", 0);
+ (void) close(STDOUT); (void) open("/dev/null", 1);
+ (void) close(STDERR); (void) open("/dev/null", 2);
+# endif /*BSD*/
+ return newpgrp;
+}
+#endif /*NEED_SETSID*/
+
+
+#ifdef NEED_GETDTABLESIZE
+int
+getdtablesize() {
+#ifdef _SC_OPEN_MAX
+ return sysconf(_SC_OPEN_MAX);
+#else
+ return _POSIX_OPEN_MAX;
+#endif
+}
+#endif
+
+
+#ifdef NEED_FLOCK
+/* The following flock() emulation snarfed intact *) from the HP-UX
+ * "BSD to HP-UX porting tricks" maintained by
+ * system@alchemy.chem.utoronto.ca (System Admin (Mike Peterson))
+ * from the version "last updated: 11-Jan-1993"
+ * Snarfage done by Jarkko Hietaniemi <Jarkko.Hietaniemi@hut.fi>
+ * *) well, almost, had to K&R the function entry, HPUX "cc"
+ * does not grok ANSI function prototypes */
+
+/*
+ * flock (fd, operation)
+ *
+ * This routine performs some file locking like the BSD 'flock'
+ * on the object described by the int file descriptor 'fd',
+ * which must already be open.
+ *
+ * The operations that are available are:
+ *
+ * LOCK_SH - get a shared lock.
+ * LOCK_EX - get an exclusive lock.
+ * LOCK_NB - don't block (must be ORed with LOCK_SH or LOCK_EX).
+ * LOCK_UN - release a lock.
+ *
+ * Return value: 0 if lock successful, -1 if failed.
+ *
+ * Note that whether the locks are enforced or advisory is
+ * controlled by the presence or absence of the SETGID bit on
+ * the executable.
+ *
+ * Note that there is no difference between shared and exclusive
+ * locks, since the 'lockf' system call in SYSV doesn't make any
+ * distinction.
+ *
+ * The file "<sys/file.h>" should be modified to contain the definitions
+ * of the available operations, which must be added manually (see below
+ * for the values).
+ */
+
+/* this code has been reformatted by vixie */
+
+int
+flock(fd, operation)
+ int fd;
+ int operation;
+{
+ int i;
+
+ switch (operation) {
+ case LOCK_SH: /* get a shared lock */
+ case LOCK_EX: /* get an exclusive lock */
+ i = lockf (fd, F_LOCK, 0);
+ break;
+
+ case LOCK_SH|LOCK_NB: /* get a non-blocking shared lock */
+ case LOCK_EX|LOCK_NB: /* get a non-blocking exclusive lock */
+ i = lockf (fd, F_TLOCK, 0);
+ if (i == -1)
+ if ((errno == EAGAIN) || (errno == EACCES))
+ errno = EWOULDBLOCK;
+ break;
+
+ case LOCK_UN: /* unlock */
+ i = lockf (fd, F_ULOCK, 0);
+ break;
+
+ default: /* can't decipher operation */
+ i = -1;
+ errno = EINVAL;
+ break;
+ }
+
+ return (i);
+}
+#endif /*NEED_FLOCK*/
+
+
+#ifdef NEED_SETENV
+int
+setenv(name, value, overwrite)
+ char *name, *value;
+ int overwrite;
+{
+ char *tmp;
+
+ if (overwrite && getenv(name))
+ return -1;
+
+ if (!(tmp = malloc(strlen(name) + strlen(value) + 2))) {
+ errno = ENOMEM;
+ return -1;
+ }
+
+ sprintf(tmp, "%s=%s", name, value);
+ return putenv(tmp); /* intentionally orphan 'tmp' storage */
+}
+#endif
diff --git a/usr.sbin/cron/lib/entry.c b/usr.sbin/cron/lib/entry.c
new file mode 100644
index 0000000..b7dba59
--- /dev/null
+++ b/usr.sbin/cron/lib/entry.c
@@ -0,0 +1,552 @@
+/* Copyright 1988,1990,1993,1994 by Paul Vixie
+ * All rights reserved
+ *
+ * Distribute freely, except: don't remove my name from the source or
+ * documentation (don't take credit for my work), mark your changes (don't
+ * get me blamed for your possible bugs), don't alter or remove this
+ * notice. May be sold if buildable source is provided to buyer. No
+ * warrantee of any kind, express or implied, is included with this
+ * software; use at your own risk, responsibility for damages (if any) to
+ * anyone resulting from the use of this software rests entirely with the
+ * user.
+ *
+ * Send bug reports, bug fixes, enhancements, requests, flames, etc., and
+ * I'll try to keep a version up to date. I can be reached as follows:
+ * Paul Vixie <paul@vix.com> uunet!decwrl!vixie!paul
+ */
+
+#if !defined(lint) && !defined(LINT)
+static const char rcsid[] =
+ "$Id: entry.c,v 1.7 1997/09/15 06:39:21 charnier Exp $";
+#endif
+
+/* vix 26jan87 [RCS'd; rest of log is in RCS file]
+ * vix 01jan87 [added line-level error recovery]
+ * vix 31dec86 [added /step to the from-to range, per bob@acornrc]
+ * vix 30dec86 [written]
+ */
+
+
+#include "cron.h"
+#include <grp.h>
+#ifdef LOGIN_CAP
+#include <login_cap.h>
+#endif
+
+typedef enum ecode {
+ e_none, e_minute, e_hour, e_dom, e_month, e_dow,
+ e_cmd, e_timespec, e_username, e_group
+#ifdef LOGIN_CAP
+ , e_class
+#endif
+} ecode_e;
+
+static char get_list __P((bitstr_t *, int, int, char *[], int, FILE *)),
+ get_range __P((bitstr_t *, int, int, char *[], int, FILE *)),
+ get_number __P((int *, int, char *[], int, FILE *));
+static int set_element __P((bitstr_t *, int, int, int));
+
+static char *ecodes[] =
+ {
+ "no error",
+ "bad minute",
+ "bad hour",
+ "bad day-of-month",
+ "bad month",
+ "bad day-of-week",
+ "bad command",
+ "bad time specifier",
+ "bad username",
+ "bad group name",
+#ifdef LOGIN_CAP
+ "bad class name",
+#endif
+ };
+
+
+void
+free_entry(e)
+ entry *e;
+{
+#ifdef LOGIN_CAP
+ if (e->class != NULL)
+ free(e->class);
+#endif
+ free(e->cmd);
+ env_free(e->envp);
+ free(e);
+}
+
+
+/* return NULL if eof or syntax error occurs;
+ * otherwise return a pointer to a new entry.
+ */
+entry *
+load_entry(file, error_func, pw, envp)
+ FILE *file;
+ void (*error_func)();
+ struct passwd *pw;
+ char **envp;
+{
+ /* this function reads one crontab entry -- the next -- from a file.
+ * it skips any leading blank lines, ignores comments, and returns
+ * EOF if for any reason the entry can't be read and parsed.
+ *
+ * the entry is also parsed here.
+ *
+ * syntax:
+ * user crontab:
+ * minutes hours doms months dows cmd\n
+ * system crontab (/etc/crontab):
+ * minutes hours doms months dows USERNAME cmd\n
+ */
+
+ ecode_e ecode = e_none;
+ entry *e;
+ int ch;
+ char cmd[MAX_COMMAND];
+ char envstr[MAX_ENVSTR];
+
+ Debug(DPARS, ("load_entry()...about to eat comments\n"))
+
+ skip_comments(file);
+
+ ch = get_char(file);
+ if (ch == EOF)
+ return NULL;
+
+ /* ch is now the first useful character of a useful line.
+ * it may be an @special or it may be the first character
+ * of a list of minutes.
+ */
+
+ e = (entry *) calloc(sizeof(entry), sizeof(char));
+
+ if (ch == '@') {
+ /* all of these should be flagged and load-limited; i.e.,
+ * instead of @hourly meaning "0 * * * *" it should mean
+ * "close to the front of every hour but not 'til the
+ * system load is low". Problems are: how do you know
+ * what "low" means? (save me from /etc/cron.conf!) and:
+ * how to guarantee low variance (how low is low?), which
+ * means how to we run roughly every hour -- seems like
+ * we need to keep a history or let the first hour set
+ * the schedule, which means we aren't load-limited
+ * anymore. too much for my overloaded brain. (vix, jan90)
+ * HINT
+ */
+ ch = get_string(cmd, MAX_COMMAND, file, " \t\n");
+ if (!strcmp("reboot", cmd)) {
+ e->flags |= WHEN_REBOOT;
+ } else if (!strcmp("yearly", cmd) || !strcmp("annually", cmd)){
+ bit_set(e->minute, 0);
+ bit_set(e->hour, 0);
+ bit_set(e->dom, 0);
+ bit_set(e->month, 0);
+ bit_nset(e->dow, 0, (LAST_DOW-FIRST_DOW+1));
+ } else if (!strcmp("monthly", cmd)) {
+ bit_set(e->minute, 0);
+ bit_set(e->hour, 0);
+ bit_set(e->dom, 0);
+ bit_nset(e->month, 0, (LAST_MONTH-FIRST_MONTH+1));
+ bit_nset(e->dow, 0, (LAST_DOW-FIRST_DOW+1));
+ } else if (!strcmp("weekly", cmd)) {
+ bit_set(e->minute, 0);
+ bit_set(e->hour, 0);
+ bit_nset(e->dom, 0, (LAST_DOM-FIRST_DOM+1));
+ bit_nset(e->month, 0, (LAST_MONTH-FIRST_MONTH+1));
+ bit_set(e->dow, 0);
+ } else if (!strcmp("daily", cmd) || !strcmp("midnight", cmd)) {
+ bit_set(e->minute, 0);
+ bit_set(e->hour, 0);
+ bit_nset(e->dom, 0, (LAST_DOM-FIRST_DOM+1));
+ bit_nset(e->month, 0, (LAST_MONTH-FIRST_MONTH+1));
+ bit_nset(e->dow, 0, (LAST_DOW-FIRST_DOW+1));
+ } else if (!strcmp("hourly", cmd)) {
+ bit_set(e->minute, 0);
+ bit_set(e->hour, (LAST_HOUR-FIRST_HOUR+1));
+ bit_nset(e->dom, 0, (LAST_DOM-FIRST_DOM+1));
+ bit_nset(e->month, 0, (LAST_MONTH-FIRST_MONTH+1));
+ bit_nset(e->dow, 0, (LAST_DOW-FIRST_DOW+1));
+ } else {
+ ecode = e_timespec;
+ goto eof;
+ }
+ } else {
+ Debug(DPARS, ("load_entry()...about to parse numerics\n"))
+
+ ch = get_list(e->minute, FIRST_MINUTE, LAST_MINUTE,
+ PPC_NULL, ch, file);
+ if (ch == EOF) {
+ ecode = e_minute;
+ goto eof;
+ }
+
+ /* hours
+ */
+
+ ch = get_list(e->hour, FIRST_HOUR, LAST_HOUR,
+ PPC_NULL, ch, file);
+ if (ch == EOF) {
+ ecode = e_hour;
+ goto eof;
+ }
+
+ /* DOM (days of month)
+ */
+
+ if (ch == '*')
+ e->flags |= DOM_STAR;
+ ch = get_list(e->dom, FIRST_DOM, LAST_DOM,
+ PPC_NULL, ch, file);
+ if (ch == EOF) {
+ ecode = e_dom;
+ goto eof;
+ }
+
+ /* month
+ */
+
+ ch = get_list(e->month, FIRST_MONTH, LAST_MONTH,
+ MonthNames, ch, file);
+ if (ch == EOF) {
+ ecode = e_month;
+ goto eof;
+ }
+
+ /* DOW (days of week)
+ */
+
+ if (ch == '*')
+ e->flags |= DOW_STAR;
+ ch = get_list(e->dow, FIRST_DOW, LAST_DOW,
+ DowNames, ch, file);
+ if (ch == EOF) {
+ ecode = e_dow;
+ goto eof;
+ }
+ }
+
+ /* make sundays equivilent */
+ if (bit_test(e->dow, 0) || bit_test(e->dow, 7)) {
+ bit_set(e->dow, 0);
+ bit_set(e->dow, 7);
+ }
+
+ /* ch is the first character of a command, or a username */
+ unget_char(ch, file);
+
+ if (!pw) {
+ char *username = cmd; /* temp buffer */
+ char *s, *group;
+ struct group *grp;
+
+ Debug(DPARS, ("load_entry()...about to parse username\n"))
+ ch = get_string(username, MAX_COMMAND, file, " \t");
+
+ Debug(DPARS, ("load_entry()...got %s\n",username))
+ if (ch == EOF) {
+ ecode = e_cmd;
+ goto eof;
+ }
+
+#ifdef LOGIN_CAP
+ if ((s = strrchr(username, '/')) != NULL) {
+ *s = '\0';
+ e->class = strdup(s + 1);
+ } else
+ e->class = strdup(RESOURCE_RC);
+ if (login_getclass(e->class) == NULL) {
+ ecode = e_class;
+ goto eof;
+ }
+#endif
+ grp = NULL;
+ if ((s = strrchr(username, ':')) != NULL) {
+ *s = '\0';
+ if ((grp = getgrnam(s + 1)) == NULL) {
+ ecode = e_group;
+ goto eof;
+ }
+ }
+
+ pw = getpwnam(username);
+ if (pw == NULL) {
+ ecode = e_username;
+ goto eof;
+ }
+ if (grp != NULL)
+ pw->pw_gid = grp->gr_gid;
+ Debug(DPARS, ("load_entry()...uid %d, gid %d\n",pw->pw_uid,pw->pw_gid))
+#ifdef LOGIN_CAP
+ Debug(DPARS, ("load_entry()...class %s\n",e->class))
+#endif
+ }
+
+ if (pw->pw_expire && time(NULL) >= pw->pw_expire) {
+ ecode = e_username;
+ goto eof;
+ }
+
+ e->uid = pw->pw_uid;
+ e->gid = pw->pw_gid;
+
+ /* copy and fix up environment. some variables are just defaults and
+ * others are overrides.
+ */
+ e->envp = env_copy(envp);
+ if (!env_get("SHELL", e->envp)) {
+ sprintf(envstr, "SHELL=%s", _PATH_BSHELL);
+ e->envp = env_set(e->envp, envstr);
+ }
+ sprintf(envstr, "HOME=%s", pw->pw_dir);
+ e->envp = env_set(e->envp, envstr);
+ if (!env_get("PATH", e->envp)) {
+ sprintf(envstr, "PATH=%s", _PATH_DEFPATH);
+ e->envp = env_set(e->envp, envstr);
+ }
+ sprintf(envstr, "%s=%s", "LOGNAME", pw->pw_name);
+ e->envp = env_set(e->envp, envstr);
+#if defined(BSD)
+ sprintf(envstr, "%s=%s", "USER", pw->pw_name);
+ e->envp = env_set(e->envp, envstr);
+#endif
+
+ Debug(DPARS, ("load_entry()...about to parse command\n"))
+
+ /* Everything up to the next \n or EOF is part of the command...
+ * too bad we don't know in advance how long it will be, since we
+ * need to malloc a string for it... so, we limit it to MAX_COMMAND.
+ * XXX - should use realloc().
+ */
+ ch = get_string(cmd, MAX_COMMAND, file, "\n");
+
+ /* a file without a \n before the EOF is rude, so we'll complain...
+ */
+ if (ch == EOF) {
+ ecode = e_cmd;
+ goto eof;
+ }
+
+ /* got the command in the 'cmd' string; save it in *e.
+ */
+ e->cmd = strdup(cmd);
+
+ Debug(DPARS, ("load_entry()...returning successfully\n"))
+
+ /* success, fini, return pointer to the entry we just created...
+ */
+ return e;
+
+ eof:
+ free(e);
+ if (ecode != e_none && error_func)
+ (*error_func)(ecodes[(int)ecode]);
+ while (ch != EOF && ch != '\n')
+ ch = get_char(file);
+ return NULL;
+}
+
+
+static char
+get_list(bits, low, high, names, ch, file)
+ bitstr_t *bits; /* one bit per flag, default=FALSE */
+ int low, high; /* bounds, impl. offset for bitstr */
+ char *names[]; /* NULL or *[] of names for these elements */
+ int ch; /* current character being processed */
+ FILE *file; /* file being read */
+{
+ register int done;
+
+ /* we know that we point to a non-blank character here;
+ * must do a Skip_Blanks before we exit, so that the
+ * next call (or the code that picks up the cmd) can
+ * assume the same thing.
+ */
+
+ Debug(DPARS|DEXT, ("get_list()...entered\n"))
+
+ /* list = range {"," range}
+ */
+
+ /* clear the bit string, since the default is 'off'.
+ */
+ bit_nclear(bits, 0, (high-low+1));
+
+ /* process all ranges
+ */
+ done = FALSE;
+ while (!done) {
+ ch = get_range(bits, low, high, names, ch, file);
+ if (ch == ',')
+ ch = get_char(file);
+ else
+ done = TRUE;
+ }
+
+ /* exiting. skip to some blanks, then skip over the blanks.
+ */
+ Skip_Nonblanks(ch, file)
+ Skip_Blanks(ch, file)
+
+ Debug(DPARS|DEXT, ("get_list()...exiting w/ %02x\n", ch))
+
+ return ch;
+}
+
+
+static char
+get_range(bits, low, high, names, ch, file)
+ bitstr_t *bits; /* one bit per flag, default=FALSE */
+ int low, high; /* bounds, impl. offset for bitstr */
+ char *names[]; /* NULL or names of elements */
+ int ch; /* current character being processed */
+ FILE *file; /* file being read */
+{
+ /* range = number | number "-" number [ "/" number ]
+ */
+
+ register int i;
+ auto int num1, num2, num3;
+
+ Debug(DPARS|DEXT, ("get_range()...entering, exit won't show\n"))
+
+ if (ch == '*') {
+ /* '*' means "first-last" but can still be modified by /step
+ */
+ num1 = low;
+ num2 = high;
+ ch = get_char(file);
+ if (ch == EOF)
+ return EOF;
+ } else {
+ if (EOF == (ch = get_number(&num1, low, names, ch, file)))
+ return EOF;
+
+ if (ch != '-') {
+ /* not a range, it's a single number.
+ */
+ if (EOF == set_element(bits, low, high, num1))
+ return EOF;
+ return ch;
+ } else {
+ /* eat the dash
+ */
+ ch = get_char(file);
+ if (ch == EOF)
+ return EOF;
+
+ /* get the number following the dash
+ */
+ ch = get_number(&num2, low, names, ch, file);
+ if (ch == EOF)
+ return EOF;
+ }
+ }
+
+ /* check for step size
+ */
+ if (ch == '/') {
+ /* eat the slash
+ */
+ ch = get_char(file);
+ if (ch == EOF)
+ return EOF;
+
+ /* get the step size -- note: we don't pass the
+ * names here, because the number is not an
+ * element id, it's a step size. 'low' is
+ * sent as a 0 since there is no offset either.
+ */
+ ch = get_number(&num3, 0, PPC_NULL, ch, file);
+ if (ch == EOF)
+ return EOF;
+ } else {
+ /* no step. default==1.
+ */
+ num3 = 1;
+ }
+
+ /* range. set all elements from num1 to num2, stepping
+ * by num3. (the step is a downward-compatible extension
+ * proposed conceptually by bob@acornrc, syntactically
+ * designed then implmented by paul vixie).
+ */
+ for (i = num1; i <= num2; i += num3)
+ if (EOF == set_element(bits, low, high, i))
+ return EOF;
+
+ return ch;
+}
+
+
+static char
+get_number(numptr, low, names, ch, file)
+ int *numptr; /* where does the result go? */
+ int low; /* offset applied to result if symbolic enum used */
+ char *names[]; /* symbolic names, if any, for enums */
+ int ch; /* current character */
+ FILE *file; /* source */
+{
+ char temp[MAX_TEMPSTR], *pc;
+ int len, i, all_digits;
+
+ /* collect alphanumerics into our fixed-size temp array
+ */
+ pc = temp;
+ len = 0;
+ all_digits = TRUE;
+ while (isalnum(ch)) {
+ if (++len >= MAX_TEMPSTR)
+ return EOF;
+
+ *pc++ = ch;
+
+ if (!isdigit(ch))
+ all_digits = FALSE;
+
+ ch = get_char(file);
+ }
+ *pc = '\0';
+
+ /* try to find the name in the name list
+ */
+ if (names) {
+ for (i = 0; names[i] != NULL; i++) {
+ Debug(DPARS|DEXT,
+ ("get_num, compare(%s,%s)\n", names[i], temp))
+ if (!strcasecmp(names[i], temp)) {
+ *numptr = i+low;
+ return ch;
+ }
+ }
+ }
+
+ /* no name list specified, or there is one and our string isn't
+ * in it. either way: if it's all digits, use its magnitude.
+ * otherwise, it's an error.
+ */
+ if (all_digits) {
+ *numptr = atoi(temp);
+ return ch;
+ }
+
+ return EOF;
+}
+
+
+static int
+set_element(bits, low, high, number)
+ bitstr_t *bits; /* one bit per flag, default=FALSE */
+ int low;
+ int high;
+ int number;
+{
+ Debug(DPARS|DEXT, ("set_element(?,%d,%d,%d)\n", low, high, number))
+
+ if (number < low || number > high)
+ return EOF;
+
+ bit_set(bits, (number-low));
+ return OK;
+}
diff --git a/usr.sbin/cron/lib/env.c b/usr.sbin/cron/lib/env.c
new file mode 100644
index 0000000..d66e1a3
--- /dev/null
+++ b/usr.sbin/cron/lib/env.c
@@ -0,0 +1,203 @@
+/* Copyright 1988,1990,1993,1994 by Paul Vixie
+ * All rights reserved
+ *
+ * Distribute freely, except: don't remove my name from the source or
+ * documentation (don't take credit for my work), mark your changes (don't
+ * get me blamed for your possible bugs), don't alter or remove this
+ * notice. May be sold if buildable source is provided to buyer. No
+ * warrantee of any kind, express or implied, is included with this
+ * software; use at your own risk, responsibility for damages (if any) to
+ * anyone resulting from the use of this software rests entirely with the
+ * user.
+ *
+ * Send bug reports, bug fixes, enhancements, requests, flames, etc., and
+ * I'll try to keep a version up to date. I can be reached as follows:
+ * Paul Vixie <paul@vix.com> uunet!decwrl!vixie!paul
+ */
+
+#if !defined(lint) && !defined(LINT)
+static const char rcsid[] =
+ "$Id: env.c,v 1.5 1997/02/22 16:05:07 peter Exp $";
+#endif
+
+
+#include "cron.h"
+
+
+char **
+env_init()
+{
+ register char **p = (char **) malloc(sizeof(char **));
+
+ if (p)
+ p[0] = NULL;
+ return (p);
+}
+
+
+void
+env_free(envp)
+ char **envp;
+{
+ char **p;
+
+ for (p = envp; *p; p++)
+ free(*p);
+ free(envp);
+}
+
+
+char **
+env_copy(envp)
+ register char **envp;
+{
+ register int count, i;
+ register char **p;
+
+ for (count = 0; envp[count] != NULL; count++)
+ ;
+ p = (char **) malloc((count+1) * sizeof(char *)); /* 1 for the NULL */
+ if (p == NULL) {
+ errno = ENOMEM;
+ return NULL;
+ }
+ for (i = 0; i < count; i++)
+ if ((p[i] = strdup(envp[i])) == NULL) {
+ while (--i >= 0)
+ (void) free(p[i]);
+ free(p);
+ errno = ENOMEM;
+ return NULL;
+ }
+ p[count] = NULL;
+ return (p);
+}
+
+
+char **
+env_set(envp, envstr)
+ char **envp;
+ char *envstr;
+{
+ register int count, found;
+ register char **p;
+
+ /*
+ * count the number of elements, including the null pointer;
+ * also set 'found' to -1 or index of entry if already in here.
+ */
+ found = -1;
+ for (count = 0; envp[count] != NULL; count++) {
+ if (!strcmp_until(envp[count], envstr, '='))
+ found = count;
+ }
+ count++; /* for the NULL */
+
+ if (found != -1) {
+ /*
+ * it exists already, so just free the existing setting,
+ * save our new one there, and return the existing array.
+ */
+ free(envp[found]);
+ if ((envp[found] = strdup(envstr)) == NULL) {
+ envp[found] = "";
+ errno = ENOMEM;
+ return NULL;
+ }
+ return (envp);
+ }
+
+ /*
+ * it doesn't exist yet, so resize the array, move null pointer over
+ * one, save our string over the old null pointer, and return resized
+ * array.
+ */
+ p = (char **) realloc((void *) envp,
+ (unsigned) ((count+1) * sizeof(char **)));
+ if (p == NULL) {
+ errno = ENOMEM;
+ return NULL;
+ }
+ p[count] = p[count-1];
+ if ((p[count-1] = strdup(envstr)) == NULL) {
+ errno = ENOMEM;
+ return NULL;
+ }
+ return (p);
+}
+
+
+/* return ERR = end of file
+ * FALSE = not an env setting (file was repositioned)
+ * TRUE = was an env setting
+ */
+int
+load_env(envstr, f)
+ char *envstr;
+ FILE *f;
+{
+ long filepos;
+ int fileline;
+ char name[MAX_ENVSTR], val[MAX_ENVSTR];
+ int fields;
+
+ filepos = ftell(f);
+ fileline = LineNumber;
+ skip_comments(f);
+ if (EOF == get_string(envstr, MAX_ENVSTR, f, "\n"))
+ return (ERR);
+
+ Debug(DPARS, ("load_env, read <%s>\n", envstr))
+
+ name[0] = val[0] = '\0';
+ fields = sscanf(envstr, "%[^ =] = %[^\n#]", name, val);
+ if (fields != 2) {
+ Debug(DPARS, ("load_env, not 2 fields (%d)\n", fields))
+ fseek(f, filepos, 0);
+ Set_LineNum(fileline);
+ return (FALSE);
+ }
+
+ /* 2 fields from scanf; looks like an env setting
+ */
+
+ /*
+ * process value string
+ */
+ /*local*/{
+ int len = strdtb(val);
+
+ if (len >= 2) {
+ if (val[0] == '\'' || val[0] == '"') {
+ if (val[len-1] == val[0]) {
+ val[len-1] = '\0';
+ (void) strcpy(val, val+1);
+ }
+ }
+ }
+ }
+
+ if (strlen(name) + 1 + strlen(val) >= MAX_ENVSTR-1)
+ return (FALSE);
+ (void) sprintf(envstr, "%s=%s", name, val);
+ Debug(DPARS, ("load_env, <%s> <%s> -> <%s>\n", name, val, envstr))
+ return (TRUE);
+}
+
+
+char *
+env_get(name, envp)
+ register char *name;
+ register char **envp;
+{
+ register int len = strlen(name);
+ register char *p, *q;
+
+ while ((p = *envp++)) {
+ if (!(q = strchr(p, '=')))
+ continue;
+ if ((q - p) == len && !strncmp(p, name, len))
+ return (q+1);
+ }
+ return (NULL);
+}
diff --git a/usr.sbin/cron/lib/misc.c b/usr.sbin/cron/lib/misc.c
new file mode 100644
index 0000000..6c098de
--- /dev/null
+++ b/usr.sbin/cron/lib/misc.c
@@ -0,0 +1,645 @@
+/* Copyright 1988,1990,1993,1994 by Paul Vixie
+ * All rights reserved
+ *
+ * Distribute freely, except: don't remove my name from the source or
+ * documentation (don't take credit for my work), mark your changes (don't
+ * get me blamed for your possible bugs), don't alter or remove this
+ * notice. May be sold if buildable source is provided to buyer. No
+ * warrantee of any kind, express or implied, is included with this
+ * software; use at your own risk, responsibility for damages (if any) to
+ * anyone resulting from the use of this software rests entirely with the
+ * user.
+ *
+ * Send bug reports, bug fixes, enhancements, requests, flames, etc., and
+ * I'll try to keep a version up to date. I can be reached as follows:
+ * Paul Vixie <paul@vix.com> uunet!decwrl!vixie!paul
+ */
+
+#if !defined(lint) && !defined(LINT)
+static const char rcsid[] =
+ "$Id: misc.c,v 1.5 1997/02/22 16:05:08 peter Exp $";
+#endif
+
+/* vix 26jan87 [RCS has the rest of the log]
+ * vix 30dec86 [written]
+ */
+
+
+#include "cron.h"
+#if SYS_TIME_H
+# include <sys/time.h>
+#else
+# include <time.h>
+#endif
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <err.h>
+#include <errno.h>
+#include <string.h>
+#include <fcntl.h>
+#if defined(SYSLOG)
+# include <syslog.h>
+#endif
+
+
+#if defined(LOG_DAEMON) && !defined(LOG_CRON)
+#define LOG_CRON LOG_DAEMON
+#endif
+
+
+static int LogFD = ERR;
+
+
+int
+strcmp_until(left, right, until)
+ char *left;
+ char *right;
+ int until;
+{
+ register int diff;
+
+ while (*left && *left != until && *left == *right) {
+ left++;
+ right++;
+ }
+
+ if ((*left=='\0' || *left == until) &&
+ (*right=='\0' || *right == until)) {
+ diff = 0;
+ } else {
+ diff = *left - *right;
+ }
+
+ return diff;
+}
+
+
+/* strdtb(s) - delete trailing blanks in string 's' and return new length
+ */
+int
+strdtb(s)
+ char *s;
+{
+ char *x = s;
+
+ /* scan forward to the null
+ */
+ while (*x)
+ x++;
+
+ /* scan backward to either the first character before the string,
+ * or the last non-blank in the string, whichever comes first.
+ */
+ do {x--;}
+ while (x >= s && isspace(*x));
+
+ /* one character beyond where we stopped above is where the null
+ * goes.
+ */
+ *++x = '\0';
+
+ /* the difference between the position of the null character and
+ * the position of the first character of the string is the length.
+ */
+ return x - s;
+}
+
+
+int
+set_debug_flags(flags)
+ char *flags;
+{
+ /* debug flags are of the form flag[,flag ...]
+ *
+ * if an error occurs, print a message to stdout and return FALSE.
+ * otherwise return TRUE after setting ERROR_FLAGS.
+ */
+
+#if !DEBUGGING
+
+ printf("this program was compiled without debugging enabled\n");
+ return FALSE;
+
+#else /* DEBUGGING */
+
+ char *pc = flags;
+
+ DebugFlags = 0;
+
+ while (*pc) {
+ char **test;
+ int mask;
+
+ /* try to find debug flag name in our list.
+ */
+ for ( test = DebugFlagNames, mask = 1;
+ *test && strcmp_until(*test, pc, ',');
+ test++, mask <<= 1
+ )
+ ;
+
+ if (!*test) {
+ fprintf(stderr,
+ "unrecognized debug flag <%s> <%s>\n",
+ flags, pc);
+ return FALSE;
+ }
+
+ DebugFlags |= mask;
+
+ /* skip to the next flag
+ */
+ while (*pc && *pc != ',')
+ pc++;
+ if (*pc == ',')
+ pc++;
+ }
+
+ if (DebugFlags) {
+ int flag;
+
+ fprintf(stderr, "debug flags enabled:");
+
+ for (flag = 0; DebugFlagNames[flag]; flag++)
+ if (DebugFlags & (1 << flag))
+ fprintf(stderr, " %s", DebugFlagNames[flag]);
+ fprintf(stderr, "\n");
+ }
+
+ return TRUE;
+
+#endif /* DEBUGGING */
+}
+
+
+void
+set_cron_uid()
+{
+#if defined(BSD) || defined(POSIX)
+ if (seteuid(ROOT_UID) < OK)
+ err(ERROR_EXIT, "seteuid");
+#else
+ if (setuid(ROOT_UID) < OK)
+ err(ERROR_EXIT, "setuid");
+#endif
+}
+
+
+void
+set_cron_cwd()
+{
+ struct stat sb;
+
+ /* first check for CRONDIR ("/var/cron" or some such)
+ */
+ if (stat(CRONDIR, &sb) < OK && errno == ENOENT) {
+ warn("%s", CRONDIR);
+ if (OK == mkdir(CRONDIR, 0700)) {
+ warnx("%s: created", CRONDIR);
+ stat(CRONDIR, &sb);
+ } else {
+ err(ERROR_EXIT, "%s: mkdir", CRONDIR);
+ }
+ }
+ if (!(sb.st_mode & S_IFDIR))
+ err(ERROR_EXIT, "'%s' is not a directory, bailing out", CRONDIR);
+ if (chdir(CRONDIR) < OK)
+ err(ERROR_EXIT, "cannot chdir(%s), bailing out", CRONDIR);
+
+ /* CRONDIR okay (now==CWD), now look at SPOOL_DIR ("tabs" or some such)
+ */
+ if (stat(SPOOL_DIR, &sb) < OK && errno == ENOENT) {
+ warn("%s", SPOOL_DIR);
+ if (OK == mkdir(SPOOL_DIR, 0700)) {
+ warnx("%s: created", SPOOL_DIR);
+ stat(SPOOL_DIR, &sb);
+ } else {
+ err(ERROR_EXIT, "%s: mkdir", SPOOL_DIR);
+ }
+ }
+ if (!(sb.st_mode & S_IFDIR))
+ err(ERROR_EXIT, "'%s' is not a directory, bailing out", SPOOL_DIR);
+}
+
+
+/* acquire_daemonlock() - write our PID into /etc/cron.pid, unless
+ * another daemon is already running, which we detect here.
+ *
+ * note: main() calls us twice; once before forking, once after.
+ * we maintain static storage of the file pointer so that we
+ * can rewrite our PID into the PIDFILE after the fork.
+ *
+ * it would be great if fflush() disassociated the file buffer.
+ */
+void
+acquire_daemonlock(closeflag)
+ int closeflag;
+{
+ static FILE *fp = NULL;
+
+ if (closeflag && fp) {
+ fclose(fp);
+ fp = NULL;
+ return;
+ }
+
+ if (!fp) {
+ char pidfile[MAX_FNAME];
+ char buf[MAX_TEMPSTR];
+ int fd, otherpid;
+
+ (void) sprintf(pidfile, PIDFILE, PIDDIR);
+ if ((-1 == (fd = open(pidfile, O_RDWR|O_CREAT, 0644)))
+ || (NULL == (fp = fdopen(fd, "r+")))
+ ) {
+ sprintf(buf, "can't open or create %s: %s",
+ pidfile, strerror(errno));
+ log_it("CRON", getpid(), "DEATH", buf);
+ errx(ERROR_EXIT, "%s", buf);
+ }
+
+ if (flock(fd, LOCK_EX|LOCK_NB) < OK) {
+ int save_errno = errno;
+
+ fscanf(fp, "%d", &otherpid);
+ sprintf(buf, "can't lock %s, otherpid may be %d: %s",
+ pidfile, otherpid, strerror(save_errno));
+ log_it("CRON", getpid(), "DEATH", buf);
+ errx(ERROR_EXIT, "%s", buf);
+ }
+
+ (void) fcntl(fd, F_SETFD, 1);
+ }
+
+ rewind(fp);
+ fprintf(fp, "%d\n", getpid());
+ fflush(fp);
+ (void) ftruncate(fileno(fp), ftell(fp));
+
+ /* abandon fd and fp even though the file is open. we need to
+ * keep it open and locked, but we don't need the handles elsewhere.
+ */
+}
+
+/* get_char(file) : like getc() but increment LineNumber on newlines
+ */
+int
+get_char(file)
+ FILE *file;
+{
+ int ch;
+
+ ch = getc(file);
+ if (ch == '\n')
+ Set_LineNum(LineNumber + 1)
+ return ch;
+}
+
+
+/* unget_char(ch, file) : like ungetc but do LineNumber processing
+ */
+void
+unget_char(ch, file)
+ int ch;
+ FILE *file;
+{
+ ungetc(ch, file);
+ if (ch == '\n')
+ Set_LineNum(LineNumber - 1)
+}
+
+
+/* get_string(str, max, file, termstr) : like fgets() but
+ * (1) has terminator string which should include \n
+ * (2) will always leave room for the null
+ * (3) uses get_char() so LineNumber will be accurate
+ * (4) returns EOF or terminating character, whichever
+ */
+int
+get_string(string, size, file, terms)
+ char *string;
+ int size;
+ FILE *file;
+ char *terms;
+{
+ int ch;
+
+ while (EOF != (ch = get_char(file)) && !strchr(terms, ch)) {
+ if (size > 1) {
+ *string++ = (char) ch;
+ size--;
+ }
+ }
+
+ if (size > 0)
+ *string = '\0';
+
+ return ch;
+}
+
+
+/* skip_comments(file) : read past comment (if any)
+ */
+void
+skip_comments(file)
+ FILE *file;
+{
+ int ch;
+
+ while (EOF != (ch = get_char(file))) {
+ /* ch is now the first character of a line.
+ */
+
+ while (ch == ' ' || ch == '\t')
+ ch = get_char(file);
+
+ if (ch == EOF)
+ break;
+
+ /* ch is now the first non-blank character of a line.
+ */
+
+ if (ch != '\n' && ch != '#')
+ break;
+
+ /* ch must be a newline or comment as first non-blank
+ * character on a line.
+ */
+
+ while (ch != '\n' && ch != EOF)
+ ch = get_char(file);
+
+ /* ch is now the newline of a line which we're going to
+ * ignore.
+ */
+ }
+ if (ch != EOF)
+ unget_char(ch, file);
+}
+
+
+/* int in_file(char *string, FILE *file)
+ * return TRUE if one of the lines in file matches string exactly,
+ * FALSE otherwise.
+ */
+static int
+in_file(string, file)
+ char *string;
+ FILE *file;
+{
+ char line[MAX_TEMPSTR];
+
+ rewind(file);
+ while (fgets(line, MAX_TEMPSTR, file)) {
+ if (line[0] != '\0')
+ line[strlen(line)-1] = '\0';
+ if (0 == strcmp(line, string))
+ return TRUE;
+ }
+ return FALSE;
+}
+
+
+/* int allowed(char *username)
+ * returns TRUE if (ALLOW_FILE exists and user is listed)
+ * or (DENY_FILE exists and user is NOT listed)
+ * or (neither file exists but user=="root" so it's okay)
+ */
+int
+allowed(username)
+ char *username;
+{
+ static int init = FALSE;
+ static FILE *allow, *deny;
+
+ if (!init) {
+ init = TRUE;
+#if defined(ALLOW_FILE) && defined(DENY_FILE)
+ allow = fopen(ALLOW_FILE, "r");
+ deny = fopen(DENY_FILE, "r");
+ Debug(DMISC, ("allow/deny enabled, %d/%d\n", !!allow, !!deny))
+#else
+ allow = NULL;
+ deny = NULL;
+#endif
+ }
+
+ if (allow)
+ return (in_file(username, allow));
+ if (deny)
+ return (!in_file(username, deny));
+
+#if defined(ALLOW_ONLY_ROOT)
+ return (strcmp(username, ROOT_USER) == 0);
+#else
+ return TRUE;
+#endif
+}
+
+
+void
+log_it(username, xpid, event, detail)
+ char *username;
+ int xpid;
+ char *event;
+ char *detail;
+{
+ PID_T pid = xpid;
+#if defined(LOG_FILE)
+ char *msg;
+ TIME_T now = time((TIME_T) 0);
+ register struct tm *t = localtime(&now);
+#endif /*LOG_FILE*/
+
+#if defined(SYSLOG)
+ static int syslog_open = 0;
+#endif
+
+#if defined(LOG_FILE)
+ /* we assume that MAX_TEMPSTR will hold the date, time, &punctuation.
+ */
+ msg = malloc(strlen(username)
+ + strlen(event)
+ + strlen(detail)
+ + MAX_TEMPSTR);
+
+ if (LogFD < OK) {
+ LogFD = open(LOG_FILE, O_WRONLY|O_APPEND|O_CREAT, 0600);
+ if (LogFD < OK) {
+ warn("can't open log file %s", LOG_FILE);
+ } else {
+ (void) fcntl(LogFD, F_SETFD, 1);
+ }
+ }
+
+ /* we have to sprintf() it because fprintf() doesn't always write
+ * everything out in one chunk and this has to be atomically appended
+ * to the log file.
+ */
+ sprintf(msg, "%s (%02d/%02d-%02d:%02d:%02d-%d) %s (%s)\n",
+ username,
+ t->tm_mon+1, t->tm_mday, t->tm_hour, t->tm_min, t->tm_sec, pid,
+ event, detail);
+
+ /* we have to run strlen() because sprintf() returns (char*) on old BSD
+ */
+ if (LogFD < OK || write(LogFD, msg, strlen(msg)) < OK) {
+ if (LogFD >= OK)
+ warn("%s", LOG_FILE);
+ warnx("can't write to log file");
+ write(STDERR, msg, strlen(msg));
+ }
+
+ free(msg);
+#endif /*LOG_FILE*/
+
+#if defined(SYSLOG)
+ if (!syslog_open) {
+ /* we don't use LOG_PID since the pid passed to us by
+ * our client may not be our own. therefore we want to
+ * print the pid ourselves.
+ */
+# ifdef LOG_DAEMON
+ openlog(ProgramName, LOG_PID, LOG_CRON);
+# else
+ openlog(ProgramName, LOG_PID);
+# endif
+ syslog_open = TRUE; /* assume openlog success */
+ }
+
+ syslog(LOG_INFO, "(%s) %s (%s)\n", username, event, detail);
+
+#endif /*SYSLOG*/
+
+#if DEBUGGING
+ if (DebugFlags) {
+ fprintf(stderr, "log_it: (%s %d) %s (%s)\n",
+ username, pid, event, detail);
+ }
+#endif
+}
+
+
+void
+log_close() {
+ if (LogFD != ERR) {
+ close(LogFD);
+ LogFD = ERR;
+ }
+}
+
+
+/* two warnings:
+ * (1) this routine is fairly slow
+ * (2) it returns a pointer to static storage
+ */
+char *
+first_word(s, t)
+ register char *s; /* string we want the first word of */
+ register char *t; /* terminators, implicitly including \0 */
+{
+ static char retbuf[2][MAX_TEMPSTR + 1]; /* sure wish C had GC */
+ static int retsel = 0;
+ register char *rb, *rp;
+
+ /* select a return buffer */
+ retsel = 1-retsel;
+ rb = &retbuf[retsel][0];
+ rp = rb;
+
+ /* skip any leading terminators */
+ while (*s && (NULL != strchr(t, *s))) {
+ s++;
+ }
+
+ /* copy until next terminator or full buffer */
+ while (*s && (NULL == strchr(t, *s)) && (rp < &rb[MAX_TEMPSTR])) {
+ *rp++ = *s++;
+ }
+
+ /* finish the return-string and return it */
+ *rp = '\0';
+ return rb;
+}
+
+
+/* warning:
+ * heavily ascii-dependent.
+ */
+void
+mkprint(dst, src, len)
+ register char *dst;
+ register unsigned char *src;
+ register int len;
+{
+ while (len-- > 0)
+ {
+ register unsigned char ch = *src++;
+
+ if (ch < ' ') { /* control character */
+ *dst++ = '^';
+ *dst++ = ch + '@';
+ } else if (ch < 0177) { /* printable */
+ *dst++ = ch;
+ } else if (ch == 0177) { /* delete/rubout */
+ *dst++ = '^';
+ *dst++ = '?';
+ } else { /* parity character */
+ sprintf(dst, "\\%03o", ch);
+ dst += 4;
+ }
+ }
+ *dst = '\0';
+}
+
+
+/* warning:
+ * returns a pointer to malloc'd storage, you must call free yourself.
+ */
+char *
+mkprints(src, len)
+ register unsigned char *src;
+ register unsigned int len;
+{
+ register char *dst = malloc(len*4 + 1);
+
+ mkprint(dst, src, len);
+
+ return dst;
+}
+
+
+#ifdef MAIL_DATE
+/* Sat, 27 Feb 93 11:44:51 CST
+ * 123456789012345678901234567
+ */
+char *
+arpadate(clock)
+ time_t *clock;
+{
+ time_t t = clock ?*clock :time(0L);
+ struct tm *tm = localtime(&t);
+ static char ret[30]; /* zone name might be >3 chars */
+
+ (void) sprintf(ret, "%s, %2d %s %2d %02d:%02d:%02d %s",
+ DowNames[tm->tm_wday],
+ tm->tm_mday,
+ MonthNames[tm->tm_mon],
+ tm->tm_year,
+ tm->tm_hour,
+ tm->tm_min,
+ tm->tm_sec,
+ TZONE(*tm));
+ return ret;
+}
+#endif /*MAIL_DATE*/
+
+
+#ifdef HAVE_SAVED_UIDS
+static int save_euid;
+int swap_uids() { save_euid = geteuid(); return seteuid(getuid()); }
+int swap_uids_back() { return seteuid(save_euid); }
+#else /*HAVE_SAVED_UIDS*/
+int swap_uids() { return setreuid(geteuid(), getuid()); }
+int swap_uids_back() { return swap_uids(); }
+#endif /*HAVE_SAVED_UIDS*/
diff --git a/usr.sbin/crunch/COPYRIGHT b/usr.sbin/crunch/COPYRIGHT
new file mode 100644
index 0000000..c7b4d2f
--- /dev/null
+++ b/usr.sbin/crunch/COPYRIGHT
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 1994 University of Maryland
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of U.M. not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission. U.M. makes no representations about the
+ * suitability of this software for any purpose. It is provided "as is"
+ * without express or implied warranty.
+ *
+ * U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M.
+ * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: James da Silva, Systems Design and Analysis Group
+ * Computer Science Department
+ * University of Maryland at College Park
+ */
diff --git a/usr.sbin/crunch/Makefile b/usr.sbin/crunch/Makefile
new file mode 100644
index 0000000..a38e0b9
--- /dev/null
+++ b/usr.sbin/crunch/Makefile
@@ -0,0 +1,4 @@
+
+SUBDIR=crunchgen crunchide
+
+.include <bsd.subdir.mk>
diff --git a/usr.sbin/crunch/Makefile.inc b/usr.sbin/crunch/Makefile.inc
new file mode 100644
index 0000000..da42105
--- /dev/null
+++ b/usr.sbin/crunch/Makefile.inc
@@ -0,0 +1,2 @@
+# modify to taste
+BINDIR?= /usr/bin
diff --git a/usr.sbin/crunch/README b/usr.sbin/crunch/README
new file mode 100644
index 0000000..27c2d02
--- /dev/null
+++ b/usr.sbin/crunch/README
@@ -0,0 +1,88 @@
+
+CRUNCH 0.2 README 6/14/94
+
+Crunch is available via anonymous ftp to ftp.cs.umd.edu in
+ pub/bsd/crunch-0.2.tar.gz
+
+
+WHAT'S NEW IN 0.2
+
+* The prototype awk script has been replaced by a more capable and
+ hopefully more robust C program.
+* No fragile template makefiles or dependencies on the details of the
+ bsd build environment.
+* You can build crunched binaries even with no sources on-line, you
+ just need the .o files. Crunchgen still will try to figure out as
+ much as possible on its own, but you can override its guessing by
+ specifying the list of .o files explicitly.
+* Crunch itself has been bmake'd and some man pages written, so it
+ should be ready to install.
+
+
+INTRODUCTION
+
+Crunch is a little package that helps create "crunched" binaries for use
+on boot, install, and fixit floppies. A crunched binary in this case is
+one where many programs have been linked together into one a.out file.
+The different programs are run depending on the value of argv[0], so
+hard links to the crunched binary suffice to simulate a perfectly normal
+system.
+
+As an example, I have created an 980K crunched "fixit" binary containing
+the following programs in their entirety:
+
+ cat chmod cp date dd df echo ed expr hostname kill ln ls mkdir
+ mt mv pwd rcp rm rmdir sh sleep stty sync test [ badsect chown
+ clri disklabel dump rdump dmesg fdisk fsck halt ifconfig init
+ mknod mount newfs ping reboot restore rrestore swapon umount
+ ftp rsh sed telnet rlogin vi cpio gzip gunzip gzcat
+
+Note carefully: vi, cpio, gzip, ed, sed, dump/restore, some networking
+utilities, and the disk management utilities, all in a binary small
+enough to fit on a 1.2 MB root filesystem floppy (albeit with the kernel
+on its own boot floppy). A more reasonable subset can be made to fit
+easily with a kernel for a decent one-disk fixit filesystem.
+
+The linking together of different programs by hand is an old
+space-saving technique. Crunch automates the process by building the
+necessary stub files and makefile for you (via the crunchgen program),
+and by doctoring the symbol tables of the component .o files to allow
+them to link without "symbol multiply defined" conflicts (via the
+crunchide program).
+
+
+BUILDING CRUNCH
+
+Just type make, then make install.
+
+Crunch was written and tested under NetBSD/i386, but should work under
+other PC BSD systems that use GNU ld.
+
+The crunchgen(1) and crunchide(1) man pages have more details on using
+crunch, and the examples subdirectory contains some working .conf files
+and a sample Makefile.
+
+CREDITS
+
+Thanks to the NetBSD team for a consistently high quality effort in
+bringing together a solid, state of the art development environment.
+
+Thanks to the FreeBSD guys; Rod Grimes, Nate Williams and Jordan
+Hubbard; and to Bruce Evans, for immediate and detailed feedback on
+crunch 0.1, and for pressing me to make the prototype more useable.
+
+Crunch was written for the Maruti Hard Real-Time Operating System
+project at the University of Maryland, to help make for better install
+and recovery procedures for our NetBSD-based development environment. It
+is copyright (c) 1994 by the University of Maryland under a UCB-style
+freely- redistributable notice. See the file COPYRIGHT for details.
+
+Please let me know of any problems or of enhancements you make to this
+package. I'm particularly interested in the details of what you found
+was good to put on your fixit or install disks. Thanks!
+
+Share and Enjoy,
+Jaime
+............................................................................
+: Stand on my shoulders, : jds@cs.umd.edu : James da Silva
+: not on my toes. : uunet!mimsy!jds : http://www.cs.umd.edu/users/jds
diff --git a/usr.sbin/crunch/crunchgen/Makefile b/usr.sbin/crunch/crunchgen/Makefile
new file mode 100644
index 0000000..abdc21ea
--- /dev/null
+++ b/usr.sbin/crunch/crunchgen/Makefile
@@ -0,0 +1,10 @@
+
+PROG=crunchgen
+SRCS=crunchgen.c crunched_skel.c
+CFLAGS+=-Wall
+CLEANFILES+= crunched_skel.c
+
+crunched_skel.c: crunched_main.c
+ sh -e ${.CURDIR}/mkskel.sh ${.CURDIR}/crunched_main.c >crunched_skel.c
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/crunch/crunchgen/crunched_main.c b/usr.sbin/crunch/crunchgen/crunched_main.c
new file mode 100644
index 0000000..5ebf04f
--- /dev/null
+++ b/usr.sbin/crunch/crunchgen/crunched_main.c
@@ -0,0 +1,117 @@
+/*
+ * Copyright (c) 1994 University of Maryland
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of U.M. not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission. U.M. makes no representations about the
+ * suitability of this software for any purpose. It is provided "as is"
+ * without express or implied warranty.
+ *
+ * U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M.
+ * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: James da Silva, Systems Design and Analysis Group
+ * Computer Science Department
+ * University of Maryland at College Park
+ */
+/*
+ * crunched_main.c - main program for crunched binaries, it branches to a
+ * particular subprogram based on the value of argv[0]. Also included
+ * is a little program invoked when the crunched binary is called via
+ * its EXECNAME. This one prints out the list of compiled-in binaries,
+ * or calls one of them based on argv[1]. This allows the testing of
+ * the crunched binary without creating all the links.
+ */
+#include <stdio.h>
+#include <string.h>
+
+struct stub {
+ char *name;
+ int (*f)();
+};
+
+extern struct stub entry_points[];
+
+int main(int argc, char **argv)
+{
+ char *slash, *basename;
+ struct stub *ep;
+
+ if(argv[0] == NULL || *argv[0] == '\0')
+ crunched_usage();
+
+ slash = strrchr(argv[0], '/');
+ basename = slash? slash+1 : argv[0];
+
+ for(ep=entry_points; ep->name != NULL; ep++)
+ if(!strcmp(basename, ep->name)) break;
+
+ if(ep->name)
+ return ep->f(argc, argv);
+ else {
+ fprintf(stderr, "%s: %s not compiled in\n", EXECNAME, basename);
+ crunched_usage();
+ }
+}
+
+
+int crunched_here(char *path)
+{
+ char *slash, *basename;
+ struct stub *ep;
+
+ slash = strrchr(path, '/');
+ basename = slash? slash+1 : path;
+
+ for(ep=entry_points; ep->name != NULL; ep++)
+ if(!strcmp(basename, ep->name))
+ return 1;
+ return 0;
+}
+
+
+int crunched_main(int argc, char **argv)
+{
+ struct stub *ep;
+ int columns, len;
+
+ if(argc <= 1)
+ crunched_usage();
+
+ return main(--argc, ++argv);
+}
+
+
+int crunched_usage()
+{
+ int columns, len;
+ struct stub *ep;
+
+ fprintf(stderr, "usage: %s <prog> <args> ..., where <prog> is one of:\n",
+ EXECNAME);
+ columns = 0;
+ for(ep=entry_points; ep->name != NULL; ep++) {
+ len = strlen(ep->name) + 1;
+ if(columns+len < 80)
+ columns += len;
+ else {
+ fprintf(stderr, "\n");
+ columns = len;
+ }
+ fprintf(stderr, " %s", ep->name);
+ }
+ fprintf(stderr, "\n");
+ exit(1);
+}
+
+/* end of crunched_main.c */
+
diff --git a/usr.sbin/crunch/crunchgen/crunchgen.1 b/usr.sbin/crunch/crunchgen/crunchgen.1
new file mode 100644
index 0000000..47243b7
--- /dev/null
+++ b/usr.sbin/crunch/crunchgen/crunchgen.1
@@ -0,0 +1,278 @@
+.\"
+.\" Copyright (c) 1994 University of Maryland
+.\" All Rights Reserved.
+.\"
+.\" Permission to use, copy, modify, distribute, and sell this software and its
+.\" documentation for any purpose is hereby granted without fee, provided that
+.\" the above copyright notice appear in all copies and that both that
+.\" copyright notice and this permission notice appear in supporting
+.\" documentation, and that the name of U.M. not be used in advertising or
+.\" publicity pertaining to distribution of the software without specific,
+.\" written prior permission. U.M. makes no representations about the
+.\" suitability of this software for any purpose. It is provided "as is"
+.\" without express or implied warranty.
+.\"
+.\" U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M.
+.\" BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+.\" IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+.\"
+.\" Author: James da Silva, Systems Design and Analysis Group
+.\" Computer Science Department
+.\" University of Maryland at College Park
+.\"
+.Dd September 29, 1997
+.Dt CRUNCHGEN 1
+.Os BSD 4
+.Sh NAME
+.Nm \&crunchgen
+.Nd generates build environment for a crunched binary
+.Sh SYNOPSIS
+.Nm \&crunchgen
+.Op Fl fql
+.Op Fl m Ar makefile-name
+.Op Fl c Ar c-file-name
+.Op Fl e Ar exec-file-name
+.Op Ar conf-file
+.Sh DESCRIPTION
+
+A crunched binary is a program made up of many other programs linked
+together into a single executable. The crunched binary main()
+function determines which component program to run by the contents of
+argv[0]. The main reason to crunch programs together is for fitting
+as many programs as possible onto an installation or system recovery
+floppy.
+
+.Pp
+.Nm Crunchgen
+reads in the specifications in
+.Ar conf-file
+for a crunched binary, and generates a Makefile and accompanying
+top-level C source file that when built create the crunched executable
+file from the component programs. For each component program,
+.Nm crunchgen
+can optionally attempt to determine the object (.o) files that make up
+the program from its source directory Makefile. This information is
+cached between runs.
+.Nm Crunchgen
+uses the companion program
+.Nm crunchide
+to eliminate link-time conflicts between the component programs by
+hiding all unnecessary symbols.
+
+.Pp
+After
+.Nm
+is run, the crunched binary can be built by running ``make -f
+<conf-name>.mk''. The component programs' object files must already
+be built. A ``objs'' target, included in the output makefile, will
+run make in each component program's source dir to build the object
+files for the user. This is not done automatically since in release
+engineering circumstances it is generally not desirable to be
+modifying objects in other directories.
+
+.Pp
+The options are as follows:
+.Bl -tag -width indent
+.It Fl c Ar c-file-name
+Set output C file name to
+.Ar c-file-name .
+The default name is ``<conf-name>.c''.
+.It Fl e Ar exec-file-name
+Set crunched binary executable file name to
+.Ar exec-file-name .
+The default name is ``<conf-name>''.
+.It Fl f
+Flush cache. Forces the recalculation of cached parameters.
+.It Fl l
+List names. Lists the names this binary will respond to.
+.It Fl m Ar makefile-name
+Set output Makefile name to
+.Ar makefile-name .
+The default name is ``<conf-name>.mk''.
+.It Fl q
+Quiet operation. Status messages are suppressed.
+.El
+.Sh CRUNCHGEN CONFIGURATION FILE COMMANDS
+
+.Nm Crunchgen
+reads specifications from the
+.Ar conf-file
+that describe the components of the crunched binary. In its simplest
+use, the component program names are merely listed along with the
+top-level source directories in which their sources can be found.
+.Nm Crunchgen
+then calculates (via the source makefiles) and caches the
+list of object files and their locations. For more specialized
+situations, the user can specify by hand all the parameters that
+.Nm
+needs.
+.Pp
+The
+.Ar conf-file
+commands are as follows:
+.Bl -tag -width indent
+.It Nm srcdirs Ar dirname ...
+A list of source trees in which the source directories of the
+component programs can be found. These dirs are searched using the
+BSD ``<source-dir>/<progname>/'' convention. Multiple
+.Nm srcdirs
+lines can be specified. The directories are searched in the order
+they are given.
+.It Nm progs Ar progname ...
+A list of programs that make up the crunched binary. Multiple
+.Nm progs
+lines can be specified.
+.It Nm libs Ar libspec ...
+A list of library specifications to be included in the crunched binary link.
+Multiple
+.Nm libs
+lines can be specified.
+.It Nm ln Ar progname linkname
+Causes the crunched binary to invoke
+.Ar progname
+whenever
+.Ar linkname
+appears in argv[0]. This allows programs that change their behavior when
+run under different names to operate correctly.
+.El
+
+To handle specialized situations, such as when the source is not
+available or not built via a conventional Makefile, the following
+.Nm special
+commands can be used to set
+.Nm
+parameters for a component program.
+.Bl -tag -width indent
+.It Nm special Ar progname Nm srcdir Ar pathname
+Set the source directory for
+.Ar progname .
+This is normally calculated by searching the specified
+.Nm srcdirs
+for a directory named
+.Ar progname .
+.It Nm special Ar progname Nm objdir Ar pathname
+Set the obj directory for
+.Ar progname .
+This is normally calculated by looking for a directory named
+.Dq Pa obj
+under the
+.Ar srcdir ,
+and if that is not found, the
+.Ar srcdir
+itself becomes the
+.Ar objdir .
+.It Nm special Ar progname Nm objs Ar object-file-name ...
+Set the list of object files for program
+.Ar progname .
+This is normally calculated by constructing a temporary makefile that includes
+.Dq Nm srcdir / Pa Makefile
+and outputs the value of $(OBJS).
+.It Nm special Ar progname Nm objpaths Ar full-pathname-to-object-file ...
+Sets the pathnames of the object files for program
+.Ar progname .
+This is normally calculated by prepending the
+.Nm objdir
+pathname to each file in the
+.Nm objs
+list.
+.It Nm special Ar progname Nm keep Ar symbol-name ...
+Add specified list of symbols to the keep list for program
+.Ar progname .
+An underscore is prepended to each symbol and it becomes the argument to a
+.Fl k
+option for the
+.Xr crunchide 1
+phase. This option is to be used as a last resort as its use can cause a
+symbol conflict, however in certain instances it may be the only way to
+have a symbol resolve.
+.El
+
+.Pp
+Only the
+.Nm objpaths
+parameter is actually needed by
+.Nm crunchgen ,
+but it is calculated from
+.Nm objdir
+and
+.Nm objs ,
+which are in turn calculated from
+.Nm srcdir ,
+so is sometimes convenient to specify the earlier parameters and let
+.Nm
+calculate forward from there if it can.
+
+.Pp
+The makefile produced by
+.Nm
+contains an optional
+.Ar objs
+target that will build the object files for each component program by
+running make inside that program's source directory. For this to work the
+.Nm srcdir
+and
+.Nm objs
+parameters must also be valid. If they are not valid for a particular program, that
+program is skipped in the
+.Ar objs
+target.
+.Sh EXAMPLE
+Here is an example
+.Nm
+input conf file, named
+.Dq Pa kcopy.conf :
+.Pp
+.nf
+ srcdirs /usr/src/bin /usr/src/sbin
+
+ progs test cp echo sh fsck halt init mount umount myinstall
+ ln test [ # test can be invoked via [
+ ln sh -sh # init invokes the shell with "-sh" in argv[0]
+
+ special myprog objpaths /homes/leroy/src/myinstall.o # no sources
+
+ libs -lutil -lcrypt
+.fi
+.Pp
+This conf file specifies a small crunched binary consisting of some
+basic system utilities plus a homegrown install program ``myinstall'',
+for which no source directory is specified, but its object file is
+specified directly with the
+.Nm special
+line.
+.Pp
+The crunched binary ``kcopy'' can be built as follows:
+.Pp
+.nf
+ % crunchgen -m Makefile kcopy.conf # gen Makefile and kcopy.c
+ % make objs # build the component programs' .o files
+ % make # build the crunched binary kcopy
+ % kcopy sh # test that this invokes a sh shell
+ $ # it works!
+.fi
+.Pp
+At this point the binary ``kcopy'' can be copied onto an install floppy
+and hard-linked to the names of the component programs.
+.Sh SEE ALSO
+.Xr crunchide 1
+.Sh CAVEATS
+While
+.Nm crunch
+takes care to eliminate link conflicts between the component programs
+of a crunched binary, conflicts are still possible between the
+libraries that are linked in. Some shuffling in the order of
+libraries may be required, and in some rare cases two libraries may
+have an unresolvable conflict and thus cannot be crunched together.
+.Pp
+Some versions of the BSD build environment do not by default build the
+intermediate object file for single-source file programs. The ``make
+objs'' target must then be used to get those object files built, or
+some other arrangements made.
+.Sh AUTHOR
+.Nm Crunch
+was written by James da Silva <jds@cs.umd.edu>.
+.sp 0
+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..ae0a78f
--- /dev/null
+++ b/usr.sbin/crunch/crunchgen/crunchgen.c
@@ -0,0 +1,878 @@
+/*
+ * Copyright (c) 1994 University of Maryland
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of U.M. not be used in advertising or
+ * publicity pertaining to distribution of the software without specific,
+ * written prior permission. U.M. makes no representations about the
+ * suitability of this software for any purpose. It is provided "as is"
+ * without express or implied warranty.
+ *
+ * U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M.
+ * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * Author: James da Silva, Systems Design and Analysis Group
+ * Computer Science Department
+ * University of Maryland at College Park
+ */
+/*
+ * ========================================================================
+ * crunchgen.c
+ *
+ * Generates a Makefile and main C file for a crunched executable,
+ * from specs given in a .conf file.
+ */
+#include <ctype.h>
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+
+#define CRUNCH_VERSION "0.2"
+
+#define MAXLINELEN 16384
+#define MAXFIELDS 2048
+
+
+/* internal representation of conf file: */
+
+/* simple lists of strings suffice for most parms */
+
+typedef struct strlst {
+ struct strlst *next;
+ char *str;
+} strlst_t;
+
+/* progs have structure, each field can be set with "special" or calculated */
+
+typedef struct prog {
+ struct prog *next;
+ char *name, *ident;
+ char *srcdir, *objdir;
+ strlst_t *objs, *objpaths;
+ strlst_t *keeplist;
+ strlst_t *links;
+ int goterror;
+} prog_t;
+
+
+/* global state */
+
+strlst_t *srcdirs = NULL;
+strlst_t *libs = NULL;
+prog_t *progs = NULL;
+
+char line[MAXLINELEN];
+
+char confname[MAXPATHLEN], infilename[MAXPATHLEN];
+char outmkname[MAXPATHLEN], outcfname[MAXPATHLEN], execfname[MAXPATHLEN];
+char tempfname[MAXPATHLEN], cachename[MAXPATHLEN], curfilename[MAXPATHLEN];
+int linenum = -1;
+int goterror = 0;
+
+int verbose, readcache; /* options */
+int reading_cache;
+
+int list_mode;
+
+/* general library routines */
+
+void status(char *str);
+void out_of_memory(void);
+void add_string(strlst_t **listp, char *str);
+int is_dir(char *pathname);
+int is_nonempty_file(char *pathname);
+
+/* helper routines for main() */
+
+void usage(void);
+void parse_conf_file(void);
+void gen_outputs(void);
+
+
+int main(int argc, char **argv)
+{
+ char *p;
+ int optc;
+
+ verbose = 1;
+ readcache = 1;
+ *outmkname = *outcfname = *execfname = '\0';
+
+ while((optc = getopt(argc, argv, "lm:c:e:fq")) != -1) {
+ switch(optc) {
+ case 'f': readcache = 0; break;
+ case 'q': verbose = 0; break;
+
+ case 'm': strcpy(outmkname, optarg); break;
+ case 'c': strcpy(outcfname, optarg); break;
+ case 'e': strcpy(execfname, optarg); break;
+ case 'l': list_mode++; verbose = 0; break;
+
+ case '?':
+ default: usage();
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if(argc != 1) usage();
+
+ /*
+ * generate filenames
+ */
+
+ strcpy(infilename, argv[0]);
+
+ /* confname = `basename infilename .conf` */
+
+ if((p=strrchr(infilename, '/')) != NULL) strcpy(confname, p+1);
+ else strcpy(confname, infilename);
+ if((p=strrchr(confname, '.')) != NULL && !strcmp(p, ".conf")) *p = '\0';
+
+ if(!*outmkname) sprintf(outmkname, "%s.mk", confname);
+ if(!*outcfname) sprintf(outcfname, "%s.c", confname);
+ if(!*execfname) sprintf(execfname, "%s", confname);
+
+ sprintf(cachename, "%s.cache", confname);
+ sprintf(tempfname, ".tmp_%sXXXXXX", confname);
+ if(mktemp(tempfname) == NULL) {
+ perror(tempfname);
+ exit(1);
+ }
+
+ parse_conf_file();
+ if (list_mode)
+ exit(goterror);
+
+ gen_outputs();
+
+ exit(goterror);
+}
+
+
+void usage(void)
+{
+ fprintf(stderr, "%s\n%s\n",
+ "usage: crunchgen [-fq] [-m <makefile>] [-c <c file>]",
+ " [-e <exec file>] <conffile>");
+ exit(1);
+}
+
+
+/*
+ * ========================================================================
+ * parse_conf_file subsystem
+ *
+ */
+
+/* helper routines for parse_conf_file */
+
+void parse_one_file(char *filename);
+void parse_line(char *line, int *fc, char **fv, int nf);
+void add_srcdirs(int argc, char **argv);
+void add_progs(int argc, char **argv);
+void add_link(int argc, char **argv);
+void add_libs(int argc, char **argv);
+void add_special(int argc, char **argv);
+
+prog_t *find_prog(char *str);
+void add_prog(char *progname);
+
+
+void parse_conf_file(void)
+{
+ if(!is_nonempty_file(infilename))
+ errx(1, "fatal: input file \"%s\" not found", infilename);
+ parse_one_file(infilename);
+ if(readcache && is_nonempty_file(cachename)) {
+ reading_cache = 1;
+ parse_one_file(cachename);
+ }
+}
+
+
+void parse_one_file(char *filename)
+{
+ char *fieldv[MAXFIELDS];
+ int fieldc;
+ void (*f)(int c, char **v);
+ FILE *cf;
+
+ sprintf(line, "reading %s", filename);
+ status(line);
+ strcpy(curfilename, filename);
+
+ if((cf = fopen(curfilename, "r")) == NULL) {
+ warn("%s", curfilename);
+ goterror = 1;
+ return;
+ }
+
+ linenum = 0;
+ while(fgets(line, MAXLINELEN, cf) != NULL) {
+ linenum++;
+ parse_line(line, &fieldc, fieldv, MAXFIELDS);
+ if(fieldc < 1) continue;
+ if(!strcmp(fieldv[0], "srcdirs")) f = add_srcdirs;
+ else if(!strcmp(fieldv[0], "progs")) f = add_progs;
+ else if(!strcmp(fieldv[0], "ln")) f = add_link;
+ else if(!strcmp(fieldv[0], "libs")) f = add_libs;
+ else if(!strcmp(fieldv[0], "special")) f = add_special;
+ else {
+ warnx("%s:%d: skipping unknown command `%s'",
+ curfilename, linenum, fieldv[0]);
+ goterror = 1;
+ continue;
+ }
+ if(fieldc < 2) {
+ warnx("%s:%d: %s command needs at least 1 argument, skipping",
+ curfilename, linenum, fieldv[0]);
+ goterror = 1;
+ continue;
+ }
+ f(fieldc, fieldv);
+ }
+
+ if(ferror(cf)) {
+ warn("%s", curfilename);
+ goterror = 1;
+ }
+ fclose(cf);
+}
+
+
+void parse_line(char *line, int *fc, char **fv, int nf)
+{
+ char *p;
+
+ p = line;
+ *fc = 0;
+ while(1) {
+ while(isspace(*p)) p++;
+ if(*p == '\0' || *p == '#') break;
+
+ if(*fc < nf) fv[(*fc)++] = p;
+ while(*p && !isspace(*p) && *p != '#') p++;
+ if(*p == '\0' || *p == '#') break;
+ *p++ = '\0';
+ }
+ if(*p) *p = '\0'; /* needed for '#' case */
+}
+
+
+void add_srcdirs(int argc, char **argv)
+{
+ int i;
+
+ for(i=1;i<argc;i++) {
+ if(is_dir(argv[i]))
+ add_string(&srcdirs, argv[i]);
+ else {
+ warnx("%s:%d: `%s' is not a directory, skipping it",
+ curfilename, linenum, argv[i]);
+ goterror = 1;
+ }
+ }
+}
+
+
+void add_progs(int argc, char **argv)
+{
+ int i;
+
+ for(i=1;i<argc;i++)
+ add_prog(argv[i]);
+}
+
+
+void add_prog(char *progname)
+{
+ prog_t *p1, *p2;
+
+ /* add to end, but be smart about dups */
+
+ for(p1 = NULL, p2 = progs; p2 != NULL; p1 = p2, p2 = p2->next)
+ if(!strcmp(p2->name, progname)) return;
+
+ p2 = malloc(sizeof(prog_t));
+ if(p2) {
+ memset(p2, 0, sizeof(prog_t));
+ p2->name = strdup(progname);
+ }
+ if(!p2 || !p2->name)
+ out_of_memory();
+
+ p2->next = NULL;
+ if(p1 == NULL) progs = p2;
+ else p1->next = p2;
+
+ p2->ident = p2->srcdir = p2->objdir = NULL;
+ p2->links = p2->objs = p2->keeplist = NULL;
+ p2->goterror = 0;
+ if (list_mode)
+ printf("%s\n",progname);
+}
+
+
+void add_link(int argc, char **argv)
+{
+ int i;
+ prog_t *p = find_prog(argv[1]);
+
+ if(p == NULL) {
+ warnx("%s:%d: no prog %s previously declared, skipping link",
+ curfilename, linenum, argv[1]);
+ goterror = 1;
+ return;
+ }
+ for(i=2;i<argc;i++) {
+ if (list_mode)
+ printf("%s\n",argv[i]);
+ add_string(&p->links, argv[i]);
+ }
+}
+
+
+void add_libs(int argc, char **argv)
+{
+ int i;
+
+ for(i=1;i<argc;i++)
+ add_string(&libs, argv[i]);
+}
+
+
+void add_special(int argc, char **argv)
+{
+ int i;
+ prog_t *p = find_prog(argv[1]);
+
+ if(p == NULL) {
+ if(reading_cache) return;
+ warnx("%s:%d: no prog %s previously declared, skipping special",
+ curfilename, linenum, argv[1]);
+ goterror = 1;
+ return;
+ }
+
+ if(!strcmp(argv[2], "ident")) {
+ if(argc != 4) goto argcount;
+ if((p->ident = strdup(argv[3])) == NULL)
+ out_of_memory();
+ }
+ else if(!strcmp(argv[2], "srcdir")) {
+ if(argc != 4) goto argcount;
+ if((p->srcdir = strdup(argv[3])) == NULL)
+ out_of_memory();
+ }
+ else if(!strcmp(argv[2], "objdir")) {
+ if(argc != 4) goto argcount;
+ if((p->objdir = strdup(argv[3])) == NULL)
+ out_of_memory();
+ }
+ else if(!strcmp(argv[2], "objs")) {
+ p->objs = NULL;
+ for(i=3;i<argc;i++)
+ add_string(&p->objs, argv[i]);
+ }
+ else if(!strcmp(argv[2], "objpaths")) {
+ p->objpaths = NULL;
+ for(i=3;i<argc;i++)
+ add_string(&p->objpaths, argv[i]);
+ }
+ else if(!strcmp(argv[2], "keep")) {
+ p->keeplist = NULL;
+ for(i=3;i<argc;i++)
+ add_string(&p->keeplist, argv[i]);
+ }
+ else {
+ warnx("%s:%d: bad parameter name `%s', skipping line",
+ curfilename, linenum, argv[2]);
+ goterror = 1;
+ }
+ return;
+
+
+ argcount:
+ warnx("%s:%d: too %s arguments, expected \"special %s %s <string>\"",
+ curfilename, linenum, argc < 4? "few" : "many", argv[1], argv[2]);
+ goterror = 1;
+}
+
+
+prog_t *find_prog(char *str)
+{
+ prog_t *p;
+
+ for(p = progs; p != NULL; p = p->next)
+ if(!strcmp(p->name, str)) return p;
+
+ return NULL;
+}
+
+
+/*
+ * ========================================================================
+ * gen_outputs subsystem
+ *
+ */
+
+/* helper subroutines */
+
+void remove_error_progs(void);
+void fillin_program(prog_t *p);
+void gen_specials_cache(void);
+void gen_output_makefile(void);
+void gen_output_cfile(void);
+
+void fillin_program_objs(prog_t *p, char *path);
+void top_makefile_rules(FILE *outmk);
+void prog_makefile_rules(FILE *outmk, prog_t *p);
+void output_strlst(FILE *outf, strlst_t *lst);
+char *genident(char *str);
+char *dir_search(char *progname);
+
+
+void gen_outputs(void)
+{
+ prog_t *p;
+
+ for(p = progs; p != NULL; p = p->next)
+ fillin_program(p);
+
+ remove_error_progs();
+ gen_specials_cache();
+ gen_output_cfile();
+ gen_output_makefile();
+ status("");
+ fprintf(stderr,
+ "Run \"make -f %s objs exe\" to build crunched binary.\n",
+ outmkname);
+}
+
+
+void fillin_program(prog_t *p)
+{
+ char path[MAXPATHLEN];
+ char *srcparent;
+ strlst_t *s;
+
+ sprintf(line, "filling in parms for %s", p->name);
+ status(line);
+
+ if(!p->ident)
+ p->ident = genident(p->name);
+ if(!p->srcdir) {
+ srcparent = dir_search(p->name);
+ if(srcparent)
+ sprintf(path, "%s/%s", srcparent, p->name);
+ if(is_dir(path))
+ p->srcdir = strdup(path);
+ }
+ if(!p->objdir && p->srcdir) {
+ FILE *f;
+
+ sprintf(path, "cd %s && echo -n /usr/obj/`pwd`", p->srcdir);
+ p->objdir = p->srcdir;
+ f = popen(path,"r");
+ if (f) {
+ fgets(path,sizeof path, f);
+ if (!pclose(f)) {
+ if(is_dir(path))
+ p->objdir = strdup(path);
+ }
+ }
+
+
+ }
+
+ if(p->srcdir) sprintf(path, "%s/Makefile", p->srcdir);
+ if(!p->objs && p->srcdir && is_nonempty_file(path))
+ fillin_program_objs(p, path);
+
+ if(!p->objpaths && p->objdir && p->objs)
+ for(s = p->objs; s != NULL; s = s->next) {
+ sprintf(line, "%s/%s", p->objdir, s->str);
+ add_string(&p->objpaths, line);
+ }
+
+ if(!p->srcdir && verbose)
+ warnx("%s: %s: warning: could not find source directory",
+ infilename, p->name);
+ if(!p->objs && verbose)
+ warnx("%s: %s: warning: could not find any .o files",
+ infilename, p->name);
+
+ if(!p->objpaths) {
+ warnx("%s: %s: error: no objpaths specified or calculated",
+ infilename, p->name);
+ p->goterror = goterror = 1;
+ }
+}
+
+void fillin_program_objs(prog_t *p, char *path)
+{
+ char *obj, *cp;
+ int rc;
+ FILE *f;
+
+ /* discover the objs from the srcdir Makefile */
+
+ if((f = fopen(tempfname, "w")) == NULL) {
+ warn("%s", tempfname);
+ goterror = 1;
+ return;
+ }
+
+ fprintf(f, ".include \"%s\"\n", path);
+ fprintf(f, ".if defined(PROG) && !defined(OBJS)\n");
+ fprintf(f, "OBJS=${PROG}.o\n");
+ fprintf(f, ".endif\n");
+ fprintf(f, "crunchgen_objs:\n\t@echo 'OBJS= '${OBJS}\n");
+ fclose(f);
+
+ sprintf(line, "make -f %s crunchgen_objs 2>&1", tempfname);
+ if((f = popen(line, "r")) == NULL) {
+ warn("submake pipe");
+ goterror = 1;
+ return;
+ }
+
+ while(fgets(line, MAXLINELEN, f)) {
+ if(strncmp(line, "OBJS= ", 6)) {
+ warnx("make error: %s", line);
+ goterror = 1;
+ continue;
+ }
+ cp = line + 6;
+ while(isspace(*cp)) cp++;
+ while(*cp) {
+ obj = cp;
+ while(*cp && !isspace(*cp)) cp++;
+ if(*cp) *cp++ = '\0';
+ add_string(&p->objs, obj);
+ while(isspace(*cp)) cp++;
+ }
+ }
+ if((rc=pclose(f)) != 0) {
+ warnx("make error: make returned %d", rc);
+ goterror = 1;
+ }
+ unlink(tempfname);
+}
+
+void remove_error_progs(void)
+{
+ prog_t *p1, *p2;
+
+ p1 = NULL; p2 = progs;
+ while(p2 != NULL) {
+ if(!p2->goterror)
+ p1 = p2, p2 = p2->next;
+ else {
+ /* delete it from linked list */
+ warnx("%s: %s: ignoring program because of errors",
+ infilename, p2->name);
+ if(p1) p1->next = p2->next;
+ else progs = p2->next;
+ p2 = p2->next;
+ }
+ }
+}
+
+void gen_specials_cache(void)
+{
+ FILE *cachef;
+ prog_t *p;
+
+ sprintf(line, "generating %s", cachename);
+ status(line);
+
+ if((cachef = fopen(cachename, "w")) == NULL) {
+ warn("%s", cachename);
+ goterror = 1;
+ return;
+ }
+
+ fprintf(cachef, "# %s - parm cache generated from %s by crunchgen %s\n\n",
+ cachename, infilename, CRUNCH_VERSION);
+
+ for(p = progs; p != NULL; p = p->next) {
+ fprintf(cachef, "\n");
+ if(p->srcdir)
+ fprintf(cachef, "special %s srcdir %s\n", p->name, p->srcdir);
+ if(p->objdir)
+ fprintf(cachef, "special %s objdir %s\n", p->name, p->objdir);
+ if(p->objs) {
+ fprintf(cachef, "special %s objs", p->name);
+ output_strlst(cachef, p->objs);
+ }
+ fprintf(cachef, "special %s objpaths", p->name);
+ output_strlst(cachef, p->objpaths);
+ }
+ fclose(cachef);
+}
+
+
+void gen_output_makefile(void)
+{
+ prog_t *p;
+ FILE *outmk;
+
+ sprintf(line, "generating %s", outmkname);
+ status(line);
+
+ if((outmk = fopen(outmkname, "w")) == NULL) {
+ warn("%s", outmkname);
+ goterror = 1;
+ return;
+ }
+
+ fprintf(outmk, "# %s - generated from %s by crunchgen %s\n\n",
+ outmkname, infilename, CRUNCH_VERSION);
+
+ top_makefile_rules(outmk);
+
+ for(p = progs; p != NULL; p = p->next)
+ prog_makefile_rules(outmk, p);
+
+ fprintf(outmk, "\n# ========\n");
+ fclose(outmk);
+}
+
+
+void gen_output_cfile(void)
+{
+ extern char *crunched_skel[];
+ char **cp;
+ FILE *outcf;
+ prog_t *p;
+ strlst_t *s;
+
+ sprintf(line, "generating %s", outcfname);
+ status(line);
+
+ if((outcf = fopen(outcfname, "w")) == NULL) {
+ warn("%s", outcfname);
+ goterror = 1;
+ return;
+ }
+
+ fprintf(outcf,
+ "/* %s - generated from %s by crunchgen %s */\n",
+ outcfname, infilename, CRUNCH_VERSION);
+
+ fprintf(outcf, "#define EXECNAME \"%s\"\n", execfname);
+ for(cp = crunched_skel; *cp != NULL; cp++)
+ fprintf(outcf, "%s\n", *cp);
+
+ for(p = progs; p != NULL; p = p->next)
+ fprintf(outcf, "extern int _crunched_%s_stub();\n", p->ident);
+
+ fprintf(outcf, "\nstruct stub entry_points[] = {\n");
+ for(p = progs; p != NULL; p = p->next) {
+ fprintf(outcf, "\t{ \"%s\", _crunched_%s_stub },\n",
+ p->name, p->ident);
+ for(s = p->links; s != NULL; s = s->next)
+ fprintf(outcf, "\t{ \"%s\", _crunched_%s_stub },\n",
+ s->str, p->ident);
+ }
+
+ fprintf(outcf, "\t{ EXECNAME, crunched_main },\n");
+ fprintf(outcf, "\t{ NULL, NULL }\n};\n");
+ fclose(outcf);
+}
+
+
+char *genident(char *str)
+{
+ char *n,*s,*d;
+
+ /*
+ * generates a Makefile/C identifier from a program name, mapping '-' to
+ * '_' and ignoring all other non-identifier characters. This leads to
+ * programs named "foo.bar" and "foobar" to map to the same identifier.
+ */
+
+ if((n = strdup(str)) == NULL)
+ return NULL;
+ for(d = s = n; *s != '\0'; s++) {
+ if(*s == '-') *d++ = '_';
+ else if(*s == '_' || isalnum(*s)) *d++ = *s;
+ }
+ *d = '\0';
+ return n;
+}
+
+
+char *dir_search(char *progname)
+{
+ char path[MAXPATHLEN];
+ strlst_t *dir;
+
+ for(dir=srcdirs; dir != NULL; dir=dir->next) {
+ sprintf(path, "%s/%s", dir->str, progname);
+ if(is_dir(path)) return dir->str;
+ }
+ return NULL;
+}
+
+
+void top_makefile_rules(FILE *outmk)
+{
+ prog_t *p;
+
+ fprintf(outmk, "LIBS=");
+ output_strlst(outmk, libs);
+
+ fprintf(outmk, "CRUNCHED_OBJS=");
+ for(p = progs; p != NULL; p = p->next)
+ fprintf(outmk, " %s.lo", p->name);
+ fprintf(outmk, "\n");
+
+ fprintf(outmk, "SUBMAKE_TARGETS=");
+ for(p = progs; p != NULL; p = p->next)
+ fprintf(outmk, " %s_make", p->ident);
+ fprintf(outmk, "\n\n");
+
+ fprintf(outmk, "%s: %s.o $(CRUNCHED_OBJS)\n",
+ execfname, execfname);
+ fprintf(outmk, "\t$(CC) -static -o %s %s.o $(CRUNCHED_OBJS) $(LIBS)\n",
+ execfname, execfname);
+ fprintf(outmk, "\tstrip %s\n", execfname);
+ fprintf(outmk, "all: objs exe\nobjs: $(SUBMAKE_TARGETS)\n");
+ fprintf(outmk, "exe: %s\n", execfname);
+ fprintf(outmk, "clean:\n\trm -f %s *.lo *.o *_stub.c\n",
+ execfname);
+}
+
+
+void prog_makefile_rules(FILE *outmk, prog_t *p)
+{
+ strlst_t *lst;
+
+ fprintf(outmk, "\n# -------- %s\n\n", p->name);
+
+ if(p->srcdir && p->objs) {
+ fprintf(outmk, "%s_SRCDIR=%s\n", p->ident, p->srcdir);
+ fprintf(outmk, "%s_OBJS=", p->ident);
+ output_strlst(outmk, p->objs);
+ fprintf(outmk, "%s_make:\n", p->ident);
+ fprintf(outmk, "\t(cd $(%s_SRCDIR) && make depend && make $(%s_OBJS))\n\n",
+ p->ident, p->ident);
+ }
+ else
+ fprintf(outmk, "%s_make:\n\t@echo \"** cannot make objs for %s\"\n\n",
+ p->ident, p->name);
+
+ fprintf(outmk, "%s_OBJPATHS=", p->ident);
+ output_strlst(outmk, p->objpaths);
+
+ fprintf(outmk, "%s_stub.c:\n", p->name);
+ fprintf(outmk, "\techo \""
+ "int _crunched_%s_stub(int argc, char **argv, char **envp)"
+ "{return main(argc,argv,envp);}\" >%s_stub.c\n",
+ p->ident, p->name);
+ fprintf(outmk, "%s.lo: %s_stub.o $(%s_OBJPATHS)\n",
+ p->name, p->name, p->ident);
+ fprintf(outmk, "\tld -dc -r -o %s.lo %s_stub.o $(%s_OBJPATHS)\n",
+ p->name, p->name, p->ident);
+ fprintf(outmk, "\tcrunchide -k __crunched_%s_stub ", p->ident);
+ for(lst = p->keeplist; lst != NULL; lst = lst->next)
+ fprintf(outmk, "-k _%s ", lst->str);
+ fprintf(outmk, "%s.lo\n", p->name);
+}
+
+void output_strlst(FILE *outf, strlst_t *lst)
+{
+ for(; lst != NULL; lst = lst->next)
+ fprintf(outf, " %s", lst->str);
+ fprintf(outf, "\n");
+}
+
+
+/*
+ * ========================================================================
+ * general library routines
+ *
+ */
+
+void status(char *str)
+{
+ static int lastlen = 0;
+ int len, spaces;
+
+ if(!verbose) return;
+
+ len = strlen(str);
+ spaces = lastlen - len;
+ if(spaces < 1) spaces = 1;
+
+ fprintf(stderr, " [%s]%*.*s\r", str, spaces, spaces, " ");
+ fflush(stderr);
+ lastlen = len;
+}
+
+
+void out_of_memory(void)
+{
+ errx(1, "%s: %d: out of memory, stopping", infilename, linenum);
+}
+
+
+void add_string(strlst_t **listp, char *str)
+{
+ strlst_t *p1, *p2;
+
+ /* add to end, but be smart about dups */
+
+ for(p1 = NULL, p2 = *listp; p2 != NULL; p1 = p2, p2 = p2->next)
+ if(!strcmp(p2->str, str)) return;
+
+ p2 = malloc(sizeof(strlst_t));
+ if(p2) {
+ memset(p2, 0, sizeof(strlst_t));
+ p2->str = strdup(str);
+ }
+ if(!p2 || !p2->str)
+ out_of_memory();
+
+ p2->next = NULL;
+ if(p1 == NULL) *listp = p2;
+ else p1->next = p2;
+}
+
+
+int is_dir(char *pathname)
+{
+ struct stat buf;
+
+ if(stat(pathname, &buf) == -1)
+ return 0;
+ return S_ISDIR(buf.st_mode);
+}
+
+int is_nonempty_file(char *pathname)
+{
+ struct stat buf;
+
+ if(stat(pathname, &buf) == -1)
+ return 0;
+
+ return S_ISREG(buf.st_mode) && buf.st_size > 0;
+}
diff --git a/usr.sbin/crunch/crunchgen/mkskel.sh b/usr.sbin/crunch/crunchgen/mkskel.sh
new file mode 100644
index 0000000..fd53d78
--- /dev/null
+++ b/usr.sbin/crunch/crunchgen/mkskel.sh
@@ -0,0 +1,15 @@
+#! /bin/sh
+# idea and sed lines taken straight from flex
+
+cat <<!EOF
+/* File created via mkskel.sh */
+
+char *crunched_skel[] = {
+!EOF
+
+sed 's/\\/&&/g' $* | sed 's/"/\\"/g' | sed 's/.*/ "&",/'
+
+cat <<!EOF
+ 0
+};
+!EOF
diff --git a/usr.sbin/crunch/crunchide/Makefile b/usr.sbin/crunch/crunchide/Makefile
new file mode 100644
index 0000000..f6e1a8a
--- /dev/null
+++ b/usr.sbin/crunch/crunchide/Makefile
@@ -0,0 +1,4 @@
+
+PROG= crunchide
+
+.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..38a04cf
--- /dev/null
+++ b/usr.sbin/crunch/crunchide/crunchide.1
@@ -0,0 +1,68 @@
+.\"
+.\" Copyright (c) 1994 University of Maryland
+.\" All Rights Reserved.
+.\"
+.\" Permission to use, copy, modify, distribute, and sell this software and its
+.\" documentation for any purpose is hereby granted without fee, provided that
+.\" the above copyright notice appear in all copies and that both that
+.\" copyright notice and this permission notice appear in supporting
+.\" documentation, and that the name of U.M. not be used in advertising or
+.\" publicity pertaining to distribution of the software without specific,
+.\" written prior permission. U.M. makes no representations about the
+.\" suitability of this software for any purpose. It is provided "as is"
+.\" without express or implied warranty.
+.\"
+.\" U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M.
+.\" BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+.\" WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+.\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR
+.\" IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+.\"
+.\" Author: James da Silva, Systems Design and Analysis Group
+.\" Computer Science Department
+.\" University of Maryland at College Park
+.\"
+.Dd June 14, 1994
+.Dt CRUNCHIDE 1
+.Os BSD 4
+.Sh NAME
+.Nm crunchide
+.Nd hides symbol names from ld, for crunching programs together
+.Sh SYNOPSIS
+.Nm crunchide
+.Op Fl f Ar keep-list-file
+.Op Fl k Ar keep-symbol
+.Op Ar object-file ...
+.Sh DESCRIPTION
+
+.Nm Crunchide
+hides the global symbols of
+.Ar object-file
+such that they are ignored by subsequent runs of the linker,
+.Xr ld 1 .
+Some symbols may be left visible via the
+.Fl k Ar keep-symbol
+and
+.Fl f Ar keep-list-file
+options. The
+.Ar keep-list-file
+must contain a list of symbols to keep visible, one symbol per line.
+Note that the C compiler prepends an underscore in front of
+symbols, so to keep the C function ``foo'' visible, the option
+\&``-k _foo'' must be used.
+
+.Pp
+.Nm Crunchide
+is designed as a companion program for
+.Xr crunchgen 1 ,
+which automates the process of creating crunched binaries from
+multiple component programs.
+.Sh SEE ALSO
+.Xr crunchgen 1 ,
+.Xr ld 1
+.Sh AUTHOR
+.Nm Crunch
+was written by James da Silva <jds@cs.umd.edu>.
+.sp 0
+Copyright (c) 1994 University of Maryland. 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..2edc082
--- /dev/null
+++ b/usr.sbin/crunch/crunchide/crunchide.c
@@ -0,0 +1,314 @@
+/*
+ * 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 <a.out.h>
+#include <err.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/errno.h>
+
+void usage(void);
+
+void add_to_keep_list(char *symbol);
+void add_file_to_keep_list(char *filename);
+
+void hide_syms(char *filename);
+
+
+int main(argc, argv)
+int argc;
+char **argv;
+{
+ int ch;
+
+ while ((ch = getopt(argc, argv, "k:f:")) != -1)
+ switch(ch) {
+ case 'k':
+ add_to_keep_list(optarg);
+ break;
+ case 'f':
+ add_file_to_keep_list(optarg);
+ break;
+ default:
+ usage();
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if(argc == 0) usage();
+
+ while(argc) {
+ hide_syms(*argv);
+ argc--, argv++;
+ }
+
+ return 0;
+}
+
+void usage(void)
+{
+ fprintf(stderr,
+ "usage: crunchide [-k <symbol-name>] [-f <keep-list-file>] <files> ...\n");
+ 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;
+
+ 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) {
+ errx(1, "out of memory for keep list");
+ }
+
+ newp->next = curp;
+ if(prevp) prevp->next = newp;
+ else keep_list = newp;
+}
+
+int in_keep_list(char *symbol)
+{
+ struct keep *curp;
+ int cmp;
+
+ 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) {
+ warn("%s", 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);
+}
+
+/* ---------------------- */
+
+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)
+
+void check_reloc(char *filename, struct relocation_info *relp);
+
+void hide_syms(char *filename)
+{
+ int inf, rc;
+ struct stat infstat;
+ struct relocation_info *relp;
+ struct nlist *symp;
+
+ /*
+ * Open the file and do some error checking.
+ */
+
+ if((inf = open(filename, O_RDWR)) == -1) {
+ warn("%s", filename);
+ return;
+ }
+
+ if(fstat(inf, &infstat) == -1) {
+ warn("%s", filename);
+ close(inf);
+ return;
+ }
+
+ if(infstat.st_size < sizeof(struct exec)) {
+ warnx("%s: short file", filename);
+ close(inf);
+ return;
+ }
+
+ /*
+ * 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) {
+ warnx("%s: too big to read into memory", filename);
+ close(inf);
+ return;
+ }
+
+ if((rc = read(inf, aoutdata, infstat.st_size)) < infstat.st_size) {
+ warnx("%s: read error: %s", filename,
+ rc == -1? strerror(errno) : "short read");
+ close(inf);
+ return;
+ }
+
+ /*
+ * Check the header and calculate offsets and sizes from it.
+ */
+
+ hdrp = (struct exec *) aoutdata;
+
+ if(N_BADMAG(*hdrp)) {
+ warnx("%s: bad magic: not an a.out file", filename);
+ close(inf);
+ return;
+ }
+
+#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) && !in_keep_list(SYMSTR(symp)))
+ 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) {
+ warnx("%s: write error: %s", filename,
+ rc == -1? strerror(errno) : "short write");
+ }
+
+ close(inf);
+}
+
+
+void check_reloc(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) {
+ errx(1, "%s: oops, have hanging relocation for %s: bailing out!",
+ filename, SYMSTR(&symbase[relp->r_symbolnum]));
+ }
+}
diff --git a/usr.sbin/crunch/examples/Makefile b/usr.sbin/crunch/examples/Makefile
new file mode 100644
index 0000000..861e302
--- /dev/null
+++ b/usr.sbin/crunch/examples/Makefile
@@ -0,0 +1,32 @@
+
+CRUNCHED= fixit
+
+# below is boiler-plate to make $(CRUNCHED) from $(CRUNCHED).conf
+# I'd use PROG instead of CRUNCHED, but the system makefiles REALLY want
+# to build things in the normal way if you use PROG.
+
+CONF= $(CRUNCHED).conf
+
+OUTMK= $(CRUNCHED).mk
+OUTPUTS= $(OUTMK) $(CRUNCHED).c $(CRUNCHED).cache
+
+NOMAN=
+CLEANFILES+=$(CRUNCHED) *.o *.lo *.c *.mk *.cache
+CLEANDIRFILES+=$(OUTPUTS)
+
+all: $(CRUNCHED)
+exe: $(CRUNCHED)
+
+$(OUTPUTS): $(CONF)
+ crunchgen ${.CURDIR}/$(CONF)
+
+$(CRUNCHED): $(OUTPUTS) submake
+
+submake:
+ make -f $(OUTMK)
+objs:
+ make -f $(OUTMK) objs
+cleandir:
+ rm -f $(CLEANDIRFILES)
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/crunch/examples/filesystem.conf b/usr.sbin/crunch/examples/filesystem.conf
new file mode 100644
index 0000000..18fd8bf
--- /dev/null
+++ b/usr.sbin/crunch/examples/filesystem.conf
@@ -0,0 +1,31 @@
+# $Id$
+
+srcdirs /usr/src/bin /usr/src/sbin /usr/src/gnu/usr.bin /usr/src/usr.sbin
+srcdirs /usr/src/sbin/i386
+
+# /bin
+progs sh expr ls mkdir rm sync test
+ln test [
+
+# These are needed because of UN*X's idiotic way of indicating that something
+# is a login shell.
+ln sh -
+ln sh -sh
+
+# /sbin
+progs disklabel fdisk init mount newfs reboot umount
+ln reboot halt
+ln reboot fastboot
+ln reboot fasthalt
+
+
+# /usr/bin
+progs cpio gzip
+ln gzip gunzip
+ln gzip gzcat
+ln gzip zcat
+
+# /usr/sbin
+progs bad144
+
+libs -ll -ledit -ltermcap -lutil -lscrypt
diff --git a/usr.sbin/crunch/examples/fixit.conf b/usr.sbin/crunch/examples/fixit.conf
new file mode 100644
index 0000000..80a2346
--- /dev/null
+++ b/usr.sbin/crunch/examples/fixit.conf
@@ -0,0 +1,45 @@
+# fixit.conf - put in anything we think we might want on a fixit floppy
+
+# first, we list the source dirs that our programs reside in. These are
+# searched in order listed to find the dir containing each program.
+
+srcdirs /usr/src/bin /usr/src/sbin /usr/src/usr.bin /usr/src/usr.sbin
+srcdirs /usr/src/gnu/usr.bin /usr/src/usr.bin/vi
+srcdirs /usr/src/sbin/i386
+
+# second, we list all the programs we want to include in our crunched binary.
+# The order doesn't matter. Any program that needs hard links to it gets an
+# `ln' directive.
+
+# /bin stuff
+
+progs cat chmod cp date dd df echo ed expr hostname kill ln ls mkdir
+progs mt mv pwd rcp rm rmdir sh sleep stty sync test
+
+ln test [
+ln sh -sh # init invokes the shell this way
+
+# /sbin stuff
+
+progs badsect chown clri disklabel dump dmesg fdisk fsck ifconfig init
+progs mknod mount newfs ping reboot restore swapon umount
+ln dump rdump
+ln restore rrestore
+
+# /usr/bin stuff
+
+progs ftp rsh sed telnet rlogin common find
+ln common vi
+ln common view
+ln common ex
+
+# gnu stuff
+
+progs cpio gzip
+ln gzip gunzip
+ln gzip gzcat
+
+# finally, we specify the libraries to link in with our binary
+
+libs -lcrypt -ltelnet -lutil -ll
+libs -lcurses -ltermcap -ledit -lkvm
diff --git a/usr.sbin/crunch/examples/kcopy.conf b/usr.sbin/crunch/examples/kcopy.conf
new file mode 100644
index 0000000..c0ddbc6
--- /dev/null
+++ b/usr.sbin/crunch/examples/kcopy.conf
@@ -0,0 +1,21 @@
+# $Id$
+
+srcdirs /usr/src/bin /usr/src/sbin
+
+# Programs from bin/
+progs sh cp echo test
+ln test [
+
+# These are needed because of UN*X's idiotic way of indicating that something
+# is a login shell.
+ln sh -
+ln sh -sh
+
+#
+# Programs from sbin/
+progs mount mount_cd9660 fsck init reboot umount
+ln reboot halt
+ln reboot fastboot
+ln reboot fasthalt
+
+libs -ll -ledit -ltermcap -lcompat -lutil -lscrypt
diff --git a/usr.sbin/crunch/examples/really-big.conf b/usr.sbin/crunch/examples/really-big.conf
new file mode 100644
index 0000000..17c7c64
--- /dev/null
+++ b/usr.sbin/crunch/examples/really-big.conf
@@ -0,0 +1,155 @@
+# really-big.conf - just about everything, just for testing.
+# This ends up having some good examples of the use of specials for
+# those hard-to-reach programs. I stopped when I got tired, but we
+# could probably get even more stuff (like libexec stuff) in here.
+#
+# This produces a 4608000 byte binary. Pretty sick and twisted, eh?
+
+# =========================================================================
+
+srcdirs /usr/src/bin
+
+progs cat chmod cp csh date dd df domainname echo ed expr hostname kill
+progs ln ls mkdir mt mv ps pwd rcp rm rmail rmdir sh sleep stty sync test
+
+ln test [
+ln sh -sh
+
+
+# =========================================================================
+
+srcdirs /usr/src/sbin
+
+progs badsect bim clri disklabel dmesg dump dumpfs fdisk fsck halt
+progs ifconfig init mknod modload modunload mount mount_fdesc mount_isofs
+progs mount_kernfs mount_lofs mount_msdos mount_portal mount_procfs mountd
+progs newfs nfsd nfsiod ping quotacheck reboot restore route routed savecore
+progs shutdown slattach swapon ttyflags tunefs umount
+# shell scripts: fastboot
+
+ln dump rdump
+ln restore rrestore
+
+
+# =========================================================================
+
+srcdirs /usr/src/usr.bin
+
+progs apropos ar asa at basename biff cal calendar cap_mkdb checknr chpass
+progs cksum cmp col colcrt colrm column comm compress crontab ctags cut
+progs dirname du env error expand false file find finger fmt fold fpr from
+progs fsplit fstat ftp getconf getopt gprof head hexdump id indent ipcrm
+progs ipcs join kdump ktrace last lastcomm leave lex lock logger locate
+progs login logname look m4 machine mail make man mesg mkfifo
+progs mkstr modstat more msgs netstat newsyslog nfsstat nice nm nohup
+progs pagesize passwd paste patch pr printenv printf quota ranlib
+progs renice rev rlogin rpcgen rpcinfo rsh rup ruptime rusers rwall rwho
+progs script sed showmount size soelim split strings strip su tail talk
+progs tcopy tee telnet tftp time tip tn3270 touch tput tr true tset tsort
+progs tty ul uname unexpand unifdef uniq units unvis users uudecode uuencode
+progs vacation vgrind vi vis vmstat w wall wc what whatis whereis who
+progs whois window write xargs xinstall xstr yacc yes ypcat ypmatch ypwhich
+
+# shell scripts: lorder mkdep shar which
+# problems: rdist uses libcompat.a(regex.o), which conflicts with
+# libedit(readline.o) over regerror().
+
+# special requirements
+
+special locate srcdir /usr/src/usr.bin/locate/locate
+special tn3270 srcdir /usr/src/usr.bin/tn3270/tn3270
+
+
+# =========================================================================
+
+srcdirs /usr/src/usr.sbin
+
+progs ac accton amd arp bad144 catman chown chroot config config.new cron
+progs dev_mkdb diskpart edquota flcopy gettable grfinfo hilinfo htable inetd
+progs iostat iteconfig kvm_mkdb mrouted mtree named portmap pppd
+progs pstat pwd_mkdb quot quotaon rarpd rbootd repquota rmt rpc.bootparamd
+progs rwhod sa sliplogin slstats spray sysctl syslogd tcpdump
+progs traceroute trpt trsp update vipw vnconfig ypbind yppoll ypset
+
+special amd srcdir /usr/src/usr.sbin/amd/amd
+special amd objs vers.amd.o afs_ops.o am_ops.o clock.o util.o xutil.o efs_ops.o mapc.o info_file.o info_hes.o info_ndbm.o info_passwd.o info_nis.o info_union.o map.o srvr_afs.o srvr_nfs.o mntfs.o misc_rpc.o mount_fs.o mtab.o mtab_bsd.o nfs_ops.o nfs_prot_svc.o nfs_start.o nfs_subr.o opts.o pfs_ops.o rpc_fwd.o sched.o sfs_ops.o amq_svc.o amq_subr.o umount_fs.o host_ops.o nfsx_ops.o ufs_ops.o ifs_ops.o amd.o get_args.o restart.o wire.o
+
+
+srcdirs /usr/src/usr.sbin/lpr # lpr subsystem
+progs lpr lpc lpq lprm pac lptest
+special lpr srcdir /usr/src/usr.sbin/lpr/lpr
+
+srcdirs /usr/src/usr.sbin/sendmail # sendmail subsystem
+progs mailstats makemap praliases sendmail
+special sendmail srcdir /usr/src/usr.sbin/sendmail/src
+ln sendmail newaliases
+ln sendmail mailq
+
+srcdirs /usr/src/usr.sbin/timed # timed & timedc
+progs timed timedc
+special timed srcdir /usr/src/usr.sbin/timed/timed
+
+srcdirs /usr/src/usr.sbin/xntpd # NTP subsystem
+# xntpd uses a gross hack to pass some information in the global
+# variable `progname' between the actual program (ntpdate in this
+# case), and the NTP library. Add `progname' to the keep list.
+progs ntpdate
+special ntpdate srcdir /usr/src/usr.sbin/xntpd/ntpdate
+special ntpdate keep progname
+libs -L/usr/src/usr.sbin/xntpd/lib -lntp
+
+srcdirs /usr/src/usr.sbin/yp # yp subsystem
+progs ypbind ypwhich ypcat ypmatch ypset yppoll
+
+
+# =========================================================================
+
+srcdirs /usr/src/gnu/usr.bin
+
+progs bc cpio diff diff3 gas gawk grep gzip sdiff sort tar
+# shell scripts: send-pr
+
+srcdirs /usr/src/gnu/usr.bin/ld # ldd and ldconfig
+progs ld ldd ldconfig
+
+# rcs stuff loses because there are cross dependencies between librcs.a and
+# the individual programs. The solution would be to specify the objpaths
+# directly for each one, and include the full path to librcs.a each the
+# objpaths.
+
+# srcdirs /usr/src/gnu/usr.bin/rcs # rcs subsystem
+# progs ci co ident merge rcs rcsclean rcsdiff rcsmerge rlog
+# # shell script: rcsfreeze
+# special rcs srcdir /usr/src/gnu/usr.bin/rcs/rcs
+# libs /usr/src/gnu/usr.bin/rcs/lib/obj/librcs.a
+
+# gdb loses too
+# progs gdb
+# special gdb srcdir /usr/src/gnu/usr.bin/gdb/gdb
+# libs /usr/src/gnu/usr.bin/gdb/bfd/obj/libbfd.a
+# libs /usr/src/gnu/usr.bin/gdb/readline/obj/libreadline.a
+# libs /usr/src/gnu/usr.bin/gdb/libiberty/obj/libiberty.a
+
+# groff has the same problem as rcs
+# srcdirs /usr/src/gnu/usr.bin/groff # groff subsystem
+# progs groff troff tbl pic eqn grops grotty grodvi refer lookbib
+# progs indxbib lkbib tfmtodit addftinfo pfbtops psbb
+# shell script: nroff
+# special groff srcdir /usr/src/gnu/usr.bin/groff/groff
+# libs /usr/src/gnu/usr.bin/groff/libgroff/obj/libgroff.a
+# libs /usr/src/gnu/usr.bin/groff/libbib/obj/libbib.a
+# libs /usr/src/gnu/usr.bin/groff/libdriver/obj/libdriver.a
+
+srcdirs /usr/src/gnu/usr.bin/gcc2 # gcc & friends
+progs cc cpp cc1
+
+# cc1 has the same problem as rcs and groff, but since there's only one program
+# I'll go ahead and solve it as an example.
+
+special cc1 objpaths /usr/src/gnu/usr.bin/gcc2/cc1/obj/c-parse.o /usr/src/gnu/usr.bin/gcc2/cc1/obj/c-lang.o /usr/src/gnu/usr.bin/gcc2/cc1/obj/c-lex.o /usr/src/gnu/usr.bin/gcc2/cc1/obj/c-pragma.o /usr/src/gnu/usr.bin/gcc2/cc1/obj/c-decl.o /usr/src/gnu/usr.bin/gcc2/cc1/obj/c-typeck.o /usr/src/gnu/usr.bin/gcc2/cc1/obj/c-convert.o /usr/src/gnu/usr.bin/gcc2/cc1/obj/c-aux-info.o /usr/src/gnu/usr.bin/gcc2/cc1/obj/c-iterate.o /usr/src/gnu/usr.bin/gcc2/common/obj/libcc1.a
+
+ln gzip gunzip
+ln gzip gzcat
+
+libs -ledit -lgnumalloc -lc -lcrypt -ltermcap -lcurses -ltelnet -lutil -lkvm
+libs -ll -ly -lm -lresolv -lrpcsvc -lcompat
diff --git a/usr.sbin/ctm/Makefile b/usr.sbin/ctm/Makefile
new file mode 100644
index 0000000..89ae78a
--- /dev/null
+++ b/usr.sbin/ctm/Makefile
@@ -0,0 +1,5 @@
+# $Id: Makefile,v 1.5 1997/02/22 16:05:14 peter Exp $
+
+SUBDIR= ctm ctm_rmail ctm_smail ctm_dequeue
+
+.include <bsd.subdir.mk>
diff --git a/usr.sbin/ctm/Makefile.inc b/usr.sbin/ctm/Makefile.inc
new file mode 100644
index 0000000..2ecf884
--- /dev/null
+++ b/usr.sbin/ctm/Makefile.inc
@@ -0,0 +1,5 @@
+# $Id$
+
+.if exists(${.CURDIR}/../../Makefile.inc)
+.include "${.CURDIR}/../../Makefile.inc"
+.endif
diff --git a/usr.sbin/ctm/README b/usr.sbin/ctm/README
new file mode 100644
index 0000000..d887912
--- /dev/null
+++ b/usr.sbin/ctm/README
@@ -0,0 +1,97 @@
+# ----------------------------------------------------------------------------
+# "THE BEER-WARE LICENSE" (Revision 42):
+# <phk@login.dknet.dk> wrote this file. As long as you retain this notice you
+# can do whatever you want with this stuff. If we meet some day, and you think
+# this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp
+# ----------------------------------------------------------------------------
+#
+# $Id$
+#
+
+What will I not find in this file ?
+-----------------------------------
+Instructions on how to obtain FreeBSD via CTM.
+Contact <phk@freefall.cdrom.com> for that.
+
+What is CTM ?
+-------------
+CTM was originally "Cvs Through eMail", but has since changed scope to be
+much more general.
+CTM is now meant to be the definitive way to make and apply a delta between
+two versions of a directory tree.
+There are two parts to this, making the delta and applying it. These are two
+entirely different things. CTM concentrates the computation-burden on the
+generation og the deltas, as a delta very often is applied more times than
+it is made. Second CTM tries to make the minimal size delta.
+
+Why not use diff/patch ?
+------------------------
+Good question. Primarily because diff and patch doesn't do their job very
+well. They don't deal with binary files (in this case files with '\0' or
+'\0377' characters in them or files that doesn't end in '\n') which isn't
+a big surprise: they were made to deal with text-files only. As a second
+gripe, with patch you send the entire file to delete it. Not particular
+efficient.
+
+So what does CTM do exactly ?
+-----------------------------
+CTM will produce a file, (a delta) containing the instructions and data needed
+to take another copy of the tree from the old to the new status. CTM means to
+do this in the exact sense, and therefore the delta contains MD5 checksums to
+verify that the tree it is applied to is indeed in the state CTM expects.
+
+This means that if you have modified the tree locally, CTM might not be able
+to upgrade your copy.
+
+How do I make a CTM-delta ?
+---------------------------
+Don't. Send me email before you even try. This is yet not quite as trivial
+as I would like. This is not to discourage you from using CTM, it is merely
+to warn you that it is slightly tedious and takes much diskspace.
+
+How do I apply a CTM-delta ?
+----------------------------
+You pass it to the 'ctm' command. You can pass a CTM-delta on stdin, or
+you can give the filename as an argument. If you do the latter, you make
+life a lot easier for your self, since the program can accept gzip'ed files
+and since it will not have to make a temporary copy of your file. You can
+specify multiple deltas at one time, they will be proccessed one at a time.
+
+The ctm command runs in a number of passes. It will process the entire
+input file in each pass, before commencing with the next pass.
+
+Pass 1 will validate that the input file is OK. The syntax, the data and
+the global MD5 checksum will be checked. If any of these fail, ctm will
+never be able to do anything with the file, so it will simply reject it.
+
+Pass 2 will validate that the directory tree is in the state expected by
+the CTM-delta. This is done by looking for files and directories which
+should/should not exists and by checking the MD5 checksums of files.
+
+Pass 3 will actually apply the delta.
+
+Should I delete the delta when I have applied it ?
+--------------------------------------------------
+No. You might want to selectively reconstruct a file latter on.
+
+What features are are planned ?
+-------------------------------
+This list isn't exhaustive, and it isn't sorted in priority.
+ Reconstruct subset of tree.
+ Make tar-copy of things which will be affected.
+ Verify.
+ Internal editor instead of ed(1)
+ Support for mode
+ Support for uid/gid
+ Support for hardlinks
+ Support for symlinks
+
+Isn't this a bit thin yet ?
+---------------------------
+Yes.
+
+Can I say something ?
+---------------------
+Yes, email me: <phk@freefall.cdrom.com>
+
+Poul-Henning
diff --git a/usr.sbin/ctm/ctm/Makefile b/usr.sbin/ctm/ctm/Makefile
new file mode 100644
index 0000000..43fb100
--- /dev/null
+++ b/usr.sbin/ctm/ctm/Makefile
@@ -0,0 +1,25 @@
+#
+# ----------------------------------------------------------------------------
+# "THE BEER-WARE LICENSE" (Revision 42):
+# <phk@login.dknet.dk> wrote this file. As long as you retain this notice you
+# can do whatever you want with this stuff. If we meet some day, and you think
+# this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp
+# ----------------------------------------------------------------------------
+#
+# $Id$
+#
+
+PROG= ctm
+NOTYET= ctm_ed.c
+SRCS= ctm.c ctm_input.c ctm_pass1.c ctm_pass2.c ctm_pass3.c \
+ ctm_passb.c ctm_syntax.c ctm_ed.c
+LDADD+= -lmd
+DPADD+= ${LIBMD}
+MAN1= ctm.1
+MAN5= ctm.5
+CFLAGS+= -Wall
+
+.if exists(${.CURDIR}/../../Makefile.inc)
+.include "${.CURDIR}/../../Makefile.inc"
+.endif
+.include <bsd.prog.mk>
diff --git a/usr.sbin/ctm/ctm/ctm.1 b/usr.sbin/ctm/ctm/ctm.1
new file mode 100644
index 0000000..e175f52
--- /dev/null
+++ b/usr.sbin/ctm/ctm/ctm.1
@@ -0,0 +1,301 @@
+.\"----------------------------------------------------------------------------
+.\""THE BEER-WARE LICENSE" (Revision 42):
+.\"<joerg@freebsd.org> wrote this file. As long as you retain this notice you
+.\"can do whatever you want with this stuff. If we meet some day, and you think
+.\"this stuff is worth it, you can buy me a beer in return. Joerg Wunsch
+.\"----------------------------------------------------------------------------
+.\"
+.\" This manual page is partially obtained from Poul-Hennings CTM README
+.\" file.
+.\"
+.\" CTM and ctm(1) by <phk@login.dknet.dk>
+.\"
+.\" $Id: ctm.1,v 1.12 1997/02/22 16:05:19 peter Exp $
+.\"
+.Dd Mar 25, 1995
+.Os
+.Dt CTM 1
+.Sh NAME
+.Nm ctm
+.Nd source code mirror program
+.Sh SYNOPSIS
+.Nm ctm
+.Op Fl cFklquv
+.Op Fl b Ar basedir
+.Op Fl B Ar backup-file
+.Op Fl e Ar include-regex
+.Op Fl t Ar tar-command
+.Op Fl T Ar tmpdir
+.Op Fl V Ar level
+.Op Fl x Ar exclude-regex
+.Ar
+.Sh DESCRIPTION
+.Nm Ctm
+was originally
+.Dq Cvs Through eMail ,
+but now instead it seems more fitting to call it
+.Dq Current Through eMail .
+
+.Nm Ctm
+is now meant to be the definitive way to make and apply a delta between
+two versions of a directory tree.
+
+There are two parts to this, making the delta and applying it. These are two
+entirely different things.
+
+.Ss Usage
+
+To apply a CTM delta, you pass it to the
+.Nm
+command. You can pass a CTM delta on stdin, or you can give the
+filename as an argument. If you do the latter, you make life a lot
+easier for your self, since the program can accept gzip'ed files and
+since it will not have to make a temporary copy of your file. You can
+specify multiple deltas at one time, they will be proccessed one at a
+time. Deltas that are already applied will be ignored.
+
+The
+.Nm
+command runs in a number of passes. It will process the entire
+input file in each pass, before commencing with the next pass.
+
+Before working on a file
+.Ar name
+.Nm
+first checks for the existence of the file
+.Ar name.ctm .
+If this file exists,
+.Nm
+works on it instead.
+
+Pass 1 will verify that the input file is OK. The syntax, the data
+and the global MD5 checksum will be checked. If any of these fail,
+.Nm
+will simply reject the input file.
+
+Pass 2 will validate that the directory tree is in the state expected by
+the CTM delta. This is done by looking for files and directories which
+should/should not exist and by checking the MD5 checksums of files.
+
+If a
+.Ar backup-file
+had been specified using the
+.Fl B
+option, all files that would be modified by this
+.Nm
+invocation are backed up
+to this file using the archiver command specified by the
+.Fl t
+option. The default archiver command is
+.Nm "tar -rf %s -T -" .
+
+Pass 3 will actually apply the delta.
+
+The list of files that would be modified by
+.Nm
+is subject to filtering regular expressions specified
+using the
+.Fl e
+and
+.Fl x
+options.
+The
+.Fl e
+and
+.Fl x
+options are applied in order of appearance on the command line. The last
+filter that matched a given file name determines whether the file would be
+operated on or left alone by
+.Nm ctm .
+
+.Nm Ctm
+will extract the file hierarchy below its working directory. Absolute
+filenames or filenames containing references through
+.Sq \&.
+and
+.Sq \&.\&.
+are explicitly prohibited as a security measure.
+
+.Ss Options
+
+.Bl -tag -width indent -compact
+
+.It Fl b Ar basedir
+Prepend the path
+.Ar basedir
+to every filename.
+
+.It Fl B Ar backup-file
+Backup all files that would be modified by this CTM run to
+.Ar backup-file .
+If any filters are specified using the
+.Fl e
+and
+.Fl x
+options, then the final set of files backed up are those that would be
+modified by CTM after the filters are applied.
+
+.It Fl c
+Check it out, don't do anything.
+
+.It Fl e Ar regular_expression
+Match each name in the CTM file against
+.Ar regular_expression ,
+and if it matches process the file, otherwise leave it alone. There may be
+any number of these options. Use of this option disables the
+.Pa .ctm_status
+sequence number checks. For example, the expression
+.Ic ^usr.sbin/ctm
+for example, will select the
+.Nm usr.sbin/ctm
+source directory and all pathnames under it.
+
+Pathnames can be disabled from being considered by CTM using the
+.Fl x
+option.
+
+.It Fl F
+Force.
+
+.It Fl k
+Keep files and directories and don't remove them even if the CTM file
+specifies they are to be removed. If the
+.Fl B
+option is specified, these files and directories will not be backed up.
+
+.It Fl l
+List files that would be modified by this invocation of CTM and the
+actions that would be performed on them. Use of the
+.Fl l
+option disables the
+.Pa .ctm_status
+checks and integrity checks on the source tree being operated on. The
+.Fl l
+option can be combined with the
+.Fl e
+and
+.Fl x
+options to determine which files would be modified by the given set of
+command line options.
+
+
+.It Fl q
+Tell us less.
+
+.It Fl t Ar tar-command
+Use
+.Ar tar-command
+instead of the default archiver
+.Nm tar .
+This option takes effect only if a backup file had been specified using the
+.Fl B
+option. A %s in the tar command will be replaced by the name of the backup
+file.
+
+
+.It Fl T Ar tmpdir
+Put temporary files under
+.Ar tmpdir .
+
+.It Fl u
+Set modification time of created and modified files to the CTM delta
+creation time.
+
+.It Fl v
+Tell us more.
+
+.It Fl V Ar level
+Tell us more.
+.Ar Level
+is the level of verbosity.
+
+.It Fl x Ar regular_expression
+Match each name in the CTM file against
+.Ar regular_expression
+and if it matches, leave the file alone. There may be any number of these
+options. Use of this option disables the
+.Pa .ctm_status
+sequence number checks.
+
+Pathnames can be selected for CTM's consideration using the
+.Fl e
+option.
+
+.El
+
+.Sh ENVIRONMENT
+.Ev TMPDIR,
+if set to a pathname, will cause ctm to use that pathname
+as the location of temporary file.
+See
+.Xr tempnam 3 ,
+for more details on this.
+The same effect may be achieved with the
+.Fl T
+flag.
+
+.Sh FILES
+
+.Pa .ctm_status
+contains the sequence number of the last CTM delta applied. Changing
+or removing this file will greatly confuse
+.Nm ctm .
+
+Using the
+.Fl e
+and
+.Fl x
+options can update a partial subset of the source tree and causes sources
+to be in an inconsistent state. It is assumed that you know what you are
+doing when you use these options.
+
+.Sh EXAMPLES
+
+.Bd -literal
+
+cd ~cvs
+/usr/sbin/ctm ~ctm/cvs-*
+
+.Ed
+
+To extract and patch all sources under `lib'
+.Bd -literal
+cd ~/lib-srcs
+/usr/sbin/ctm -e '^lib' ~ctm/src-cur*
+.Ed
+.Sh DIAGNOSTICS
+
+Numerous messages, hopefully self-explanatory. The
+.Dq noise level
+can be adjusted with the
+.Fl q ,
+.Fl v
+and
+.Fl V
+options.
+
+.Sh SEE ALSO
+.Xr ctm_rmail 1 ,
+.Xr ctm 5
+
+.Sh HISTORY
+
+Initial trials were run during the work on
+.Fx 1.1.5 ,
+and many bugs and
+methods were hashed out.
+
+The
+.Nm
+command appeared in
+.Fx 2.1 .
+
+.Sh AUTHORS
+
+The CTM system has been designed and implemented by
+.An Poul-Henning Kamp
+.Aq phk@FreeBSD.org .
+
+.An Joerg Wunsch
+.Aq joerg@FreeBSD.org
+wrote this man-page.
diff --git a/usr.sbin/ctm/ctm/ctm.5 b/usr.sbin/ctm/ctm/ctm.5
new file mode 100644
index 0000000..6f2cf96
--- /dev/null
+++ b/usr.sbin/ctm/ctm/ctm.5
@@ -0,0 +1,214 @@
+.\"----------------------------------------------------------------------------
+.\""THE BEER-WARE LICENSE" (Revision 42):
+.\"<joerg@freebsd.org> wrote this file. As long as you retain this notice you
+.\"can do whatever you want with this stuff. If we meet some day, and you think
+.\"this stuff is worth it, you can buy me a beer in return. Joerg Wunsch
+.\"----------------------------------------------------------------------------
+.\"
+.\" This manual page is partially obtained from Poul-Hennings CTM README
+.\" file.
+.\"
+.\" CTM and ctm(1) by <phk@login.dknet.dk>
+.\"
+.\" $Id: ctm.5,v 1.5 1997/02/22 16:05:20 peter Exp $
+.\"
+.Dd March 25, 1995
+.Os
+.Dt CTM 5
+.Sh NAME
+.Nm ctm
+.Nd source code mirror system
+
+.Sh DESCRIPTION
+The
+.Nm
+transfers data in a specific file format, called a CTM delta.
+
+CTM deltas consist of control lines and data chunks. Each control
+line starts with the letters
+.Dq CTM ,
+followed by a CTM statement and control data, and ends with a '\en'
+character.
+
+Data chunks always belong to the preceeding control line, and the
+last field on that control line is the number of bytes in the data
+chunk.
+A trailing newline '\en' character follows each data chunk, this
+newline is not part of the chunk and isn't included in the count.
+
+The CTM statements are as follows.
+.Bl -tag -width indent
+
+.It _BEGIN Ar version name number timestamp prefix
+
+This is the overall begin of a CTM delta file. The
+.Ar version
+field must match the program version
+.Pq currently 2.0 .
+.Ar Name
+is the name and
+.Ar number
+the sequence number of the CTM service, it is matched against the file
+.Pa .ctm_status
+to see if the delta has already been applied.
+.Ar Timestamp
+contains the year, month, day, hour, minute, and second of the
+time of delta creation for reference
+.Po
+followed by the letter
+.Sq Z
+meaning this is a UTC timestamp
+.Pc .
+The
+.Ar prefix
+field is currently not implemented.
+
+.It _END Ar md5
+
+This statement ends the CTM delta, the global
+.Ar md5
+checksum is matched against the MD5 checksum of the entire delta, up to
+and including the space (0x20) character following ``_END''.
+
+.It \&FM Ar name uid gid mode md5 count
+
+Make the file
+.Ar name ,
+the original file had the uid
+.Ar uid
+.Pq numerical, decimal ,
+the gid
+.Ar gid
+.Pq numerical, decimal ,
+mode
+.Ar mode
+.Pq numerical, octal ,
+and the MD5 checksum
+.Ar md5 .
+
+The following
+.Ar count
+bytes data are the contents of the new file.
+
+.It \&FS Ar name uid gid mode md5before md5after count
+
+Substitute the contents of file
+.Ar name ,
+the original file had the new uid
+.Ar uid
+.Pq numerical, decimal ,
+the new gid
+.Ar gid
+.Pq numerical, decimal ,
+new mode
+.Ar mode
+.Pq numerical, octal ,
+the old MD5 checksum
+.Ar md5before ,
+and the new MD5 checksum
+.Ar md5after .
+
+The following
+.Ar count
+bytes data are the contents of the new file.
+
+File substitution is used if the commands to edit a file would exceed
+the total file length, so substituting it is more efficient.
+
+.It \&FN Ar name uid gid mode md5before md5after count
+
+Edit the file
+.Ar name .
+The arguments are as above, but the data sections contains an
+.Xr diff 1
+-n script which should be applied to the file in question.
+
+.It \&FR Ar name md5
+
+Remove the file
+.Ar name ,
+which must match the MD5 checksum
+.Ar md5 .
+
+.It \&AS Ar name uid gid mode
+
+The original file
+.Ar name
+changed its owner to
+.Ar uid ,
+its group to
+.Ar gid ,
+and/or its mode to
+.Ar mode .
+
+.It \&DM Ar name uid gid mode
+
+The directory
+.Ar name
+is to be created, it had originally the owner
+.Ar uid ,
+group
+.Ar gid ,
+and mode
+.Ar mode .
+
+.It \&DR name
+
+The directory
+.Ar name
+is to be removed.
+
+.El
+
+.Sh EXAMPLES
+
+In the following example, long lines have been folded to make them
+printable
+.Pq marked by backslashes .
+
+.Bd -literal
+
+CTM_BEGIN 2.0 cvs-cur 485 19950324214652Z .
+CTMFR src/sys/gnu/i386/isa/scd.c,v 5225f13aa3c7e458f9dd0d4bb637b18d
+CTMFR src/sys/gnu/i386/isa/scdreg.h,v e5af42b8a06f2c8030b93a7d71afb223
+CTMDM src/sys/gnu/i386/isa/Attic 0 552 775
+CTMFS .ctm_status 545 552 664 d9ccd2a84a9dbb8db56ba85663adebf0 \\
+e2a10c6f66428981782a0a18a789ee2e 12
+cvs-cur 485
+
+CTMFN CVSROOT/commitlogs/gnu 545 552 664 \\
+5d7bc3549140d860bd9641b5782c002d 7fb04ed84b48160c9b8eea84b4c0b6e3 394
+a6936 21
+ache 95/03/24 09:59:50
+
+ Modified: gnu/lib/libdialog kernel.c prgbox.c
+ Log:
+[...]
+CTM_END 74ddd298d76215ae45a077a4b6a74e9c
+
+.Ed
+
+.Sh SEE ALSO
+
+.Xr ctm 1 ,
+.Xr ctm_rmail 1 ,
+.Xr ed 1 .
+
+.Sh HISTORY
+
+Initial trials ran during the
+.Fx 1.1.5 ,
+and many bugs and
+methods were hashed out.
+The CTM system has been made publically available in
+.Fx 2.1 .
+
+.Sh AUTHORS
+
+The CTM system has been designed and implemented by
+.An Poul-Henning Kamp
+.Aq phk@FreeBSD.org .
+
+.An Joerg Wunsch
+.Aq joerg@FreeBSD.org
+wrote this man-page.
diff --git a/usr.sbin/ctm/ctm/ctm.c b/usr.sbin/ctm/ctm/ctm.c
new file mode 100644
index 0000000..a05b2d5
--- /dev/null
+++ b/usr.sbin/ctm/ctm/ctm.c
@@ -0,0 +1,318 @@
+/*
+ * ----------------------------------------------------------------------------
+ * "THE BEER-WARE LICENSE" (Revision 42):
+ * <phk@login.dknet.dk> wrote this file. As long as you retain this notice you
+ * can do whatever you want with this stuff. If we meet some day, and you think
+ * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp
+ * ----------------------------------------------------------------------------
+ *
+ * $Id: ctm.c,v 1.16 1997/02/22 16:05:21 peter Exp $
+ *
+ * This is the client program of 'CTM'. It will apply a CTM-patch to a
+ * collection of files.
+ *
+ * Options we'd like to see:
+ *
+ * -a Attempt best effort.
+ * -d <int> Debug TBD.
+ * -m <mail-addr> Email me instead.
+ * -r <name> Reconstruct file.
+ * -R <file> Read list of files to reconstruct.
+ *
+ * Options we have:
+ * -b <dir> Base-dir
+ * -B <file> Backup to tar-file.
+ * -t Tar command (default as in TARCMD).
+ * -c Check it out, don't do anything.
+ * -F Force
+ * -q Tell us less.
+ * -T <tmpdir>. Temporary files.
+ * -u Set all file modification times to the timestamp
+ * -v Tell us more.
+ * -V <level> Tell us more level = number of -v
+ * -k Keep files and directories that would have been removed.
+ * -l List actions.
+ *
+ * Options we don't actually use:
+ * -p Less paranoid.
+ * -P Paranoid.
+ */
+
+#define EXTERN /* */
+#include "ctm.h"
+
+#define CTM_STATUS ".ctm_status"
+
+extern int Proc(char *, unsigned applied);
+
+int
+main(int argc, char **argv)
+{
+ int stat=0, err=0;
+ int c;
+ unsigned applied = 0;
+ FILE *statfile;
+ struct CTM_Filter *nfilter = NULL; /* new filter */
+ u_char * basedir;
+
+ basedir = NULL;
+ Verbose = 1;
+ Paranoid = 1;
+ SetTime = 0;
+ KeepIt = 0;
+ ListIt = 0;
+ BackupFile = NULL;
+ TarCmd = TARCMD;
+ LastFilter = FilterList = NULL;
+ setbuf(stderr,0);
+ setbuf(stdout,0);
+
+ while((c=getopt(argc,argv,"ab:B:cd:e:Fklm:pPqr:R:t:T:uV:vx:")) != -1) {
+ switch (c) {
+ case 'b': basedir = optarg; break; /* Base Directory */
+ case 'B': BackupFile = optarg; break;
+ case 'c': CheckIt++; break; /* Only check it */
+ case 'F': Force = 1; break;
+ case 'k': KeepIt++; break; /* Don't do removes */
+ case 'l': ListIt++; break; /* Only list actions and files */
+ case 'p': Paranoid--; break; /* Less Paranoid */
+ case 'P': Paranoid++; break; /* More Paranoid */
+ case 'q': Verbose--; break; /* Quiet */
+ case 't': TarCmd = optarg; break; /* archiver command */
+ case 'T': TmpDir = optarg; break; /* set temporary directory */
+ case 'u': SetTime++; break; /* Set timestamp on files */
+ case 'v': Verbose++; break; /* Verbose */
+ case 'V': sscanf(optarg,"%d", &c); /* Verbose */
+ Verbose += c;
+ break;
+ case 'e': /* filter expressions */
+ case 'x':
+ if (NULL == (nfilter = Malloc(sizeof(struct CTM_Filter)))) {
+ warnx("out of memory for expressions: \"%s\"", optarg);
+ stat++;
+ break;
+ }
+
+ (void) memset(nfilter, 0, sizeof(struct CTM_Filter));
+
+ if (0 != (err =
+ regcomp(&nfilter->CompiledRegex, optarg, REG_NOSUB))) {
+
+ char errmsg[128];
+
+ regerror(err, &nfilter->CompiledRegex, errmsg,
+ sizeof(errmsg));
+ warnx("regular expression: \"%s\"", errmsg);
+ stat++;
+ break;
+ }
+
+ /* note whether the filter enables or disables on match */
+ nfilter->Action =
+ (('e' == c) ? CTM_FILTER_ENABLE : CTM_FILTER_DISABLE);
+
+ /* link in the expression into the list */
+ nfilter->Next = NULL;
+ if (NULL == FilterList) {
+ LastFilter = FilterList = nfilter; /* init head and tail */
+ } else { /* place at tail */
+ LastFilter->Next = nfilter;
+ LastFilter = nfilter;
+ }
+ break;
+ case ':':
+ warnx("option '%c' requires an argument",optopt);
+ stat++;
+ break;
+ case '?':
+ warnx("option '%c' not supported",optopt);
+ stat++;
+ break;
+ default:
+ warnx("option '%c' not yet implemented",optopt);
+ break;
+ }
+ }
+
+ if(stat) {
+ warnx("%d errors during option processing",stat);
+ return Exit_Pilot;
+ }
+ stat = Exit_Done;
+ argc -= optind;
+ argv += optind;
+
+ if (basedir == NULL) {
+ Buffer = (u_char *)Malloc(BUFSIZ + strlen(SUBSUFF) +1);
+ CatPtr = Buffer;
+ *Buffer = '\0';
+ } else {
+ Buffer = (u_char *)Malloc(strlen(basedir)+ BUFSIZ + strlen(SUBSUFF) +1);
+ strcpy(Buffer, basedir);
+ CatPtr = Buffer + strlen(basedir);
+ if (CatPtr[-1] != '/') {
+ strcat(Buffer, "/");
+ CatPtr++;
+ }
+ }
+ strcat(Buffer, CTM_STATUS);
+
+ if(ListIt)
+ applied = 0;
+ else
+ if((statfile = fopen(Buffer, "r")) == NULL) {
+ if (Verbose > 0)
+ warnx("warning: %s not found", Buffer);
+ } else {
+ fscanf(statfile, "%*s %u", &applied);
+ fclose(statfile);
+ }
+
+ if(!argc)
+ stat |= Proc("-", applied);
+
+ while(argc-- && stat == Exit_Done) {
+ stat |= Proc(*argv++, applied);
+ stat &= ~(Exit_Version | Exit_NoMatch);
+ }
+
+ if(stat == Exit_Done)
+ stat = Exit_OK;
+
+ if(Verbose > 0)
+ warnx("exit(%d)",stat);
+
+ if (FilterList)
+ for (nfilter = FilterList; nfilter; ) {
+ struct CTM_Filter *tmp = nfilter->Next;
+ Free(nfilter);
+ nfilter = tmp;
+ }
+ return stat;
+}
+
+int
+Proc(char *filename, unsigned applied)
+{
+ FILE *f;
+ int i;
+ char *p = strrchr(filename,'.');
+
+ if(!strcmp(filename,"-")) {
+ p = 0;
+ f = stdin;
+ } else if(p && (!strcmp(p,".gz") || !strcmp(p,".Z"))) {
+ p = alloca(20 + strlen(filename));
+ strcpy(p,"gunzip < ");
+ strcat(p,filename);
+ f = popen(p,"r");
+ if(!f) { warn("%s", p); return Exit_Garbage; }
+ } else {
+ p = 0;
+ f = fopen(filename,"r");
+ }
+ if(!f) {
+ warn("%s", filename);
+ return Exit_Garbage;
+ }
+
+ if(Verbose > 1)
+ fprintf(stderr,"Working on <%s>\n",filename);
+
+ Delete(FileName);
+ FileName = String(filename);
+
+ /* If we cannot seek, we're doomed, so copy to a tmp-file in that case */
+ if(!p && -1 == fseek(f,0,SEEK_END)) {
+ char *fn = tempnam(TmpDir,"CTMclient");
+ FILE *f2 = fopen(fn,"w+");
+ int i;
+
+ if(!f2) {
+ warn("%s", fn);
+ fclose(f);
+ return Exit_Broke;
+ }
+ unlink(fn);
+ if (Verbose > 0)
+ fprintf(stderr,"Writing tmp-file \"%s\"\n",fn);
+ while(EOF != (i=getc(f)))
+ if(EOF == putc(i,f2)) {
+ fclose(f2);
+ return Exit_Broke;
+ }
+ fclose(f);
+ f = f2;
+ }
+
+ if(!p)
+ rewind(f);
+
+ if((i=Pass1(f, applied)))
+ goto exit_and_close;
+
+ if(ListIt) {
+ i = Exit_Done;
+ goto exit_and_close;
+ }
+
+ if(!p) {
+ rewind(f);
+ } else {
+ pclose(f);
+ f = popen(p,"r");
+ if(!f) { warn("%s", p); return Exit_Broke; }
+ }
+
+ i=Pass2(f);
+
+ if(!p) {
+ rewind(f);
+ } else {
+ pclose(f);
+ f = popen(p,"r");
+ if(!f) { warn("%s", p); return Exit_Broke; }
+ }
+
+ if(i) {
+ if((!Force) || (i & ~Exit_Forcible))
+ goto exit_and_close;
+ }
+
+ if(CheckIt) {
+ if (Verbose > 0)
+ fprintf(stderr,"All checks out ok.\n");
+ i = Exit_Done;
+ goto exit_and_close;
+ }
+
+ /* backup files if requested */
+ if(BackupFile) {
+
+ i = PassB(f);
+
+ if(!p) {
+ rewind(f);
+ } else {
+ pclose(f);
+ f = popen(p,"r");
+ if(!f) { warn("%s", p); return Exit_Broke; }
+ }
+ }
+
+ i=Pass3(f);
+
+exit_and_close:
+ if(!p)
+ fclose(f);
+ else
+ pclose(f);
+
+ if(i)
+ return i;
+
+ if (Verbose > 0)
+ fprintf(stderr,"All done ok\n");
+
+ return Exit_Done;
+}
diff --git a/usr.sbin/ctm/ctm/ctm.h b/usr.sbin/ctm/ctm/ctm.h
new file mode 100644
index 0000000..c53eb44
--- /dev/null
+++ b/usr.sbin/ctm/ctm/ctm.h
@@ -0,0 +1,164 @@
+/*
+ * ----------------------------------------------------------------------------
+ * "THE BEER-WARE LICENSE" (Revision 42):
+ * <phk@login.dknet.dk> wrote this file. As long as you retain this notice you
+ * can do whatever you want with this stuff. If we meet some day, and you think
+ * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp
+ * ----------------------------------------------------------------------------
+ *
+ * $Id: ctm.h,v 1.12 1997/02/22 16:05:21 peter Exp $
+ *
+ */
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <md5.h>
+#include <regex.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/file.h>
+#include <sys/time.h>
+
+#define VERSION "2.0"
+#define MAXSIZE (1024*1024*10)
+
+#define SUBSUFF ".ctm"
+#define TMPSUFF ".ctmtmp"
+#define TARCMD "tar -rf %s -T -"
+
+/* The fields... */
+#define CTM_F_MASK 0xff
+#define CTM_F_Name 0x01
+#define CTM_F_Uid 0x02
+#define CTM_F_Gid 0x03
+#define CTM_F_Mode 0x04
+#define CTM_F_MD5 0x05
+#define CTM_F_Count 0x06
+#define CTM_F_Bytes 0x07
+
+/* The qualifiers... */
+#define CTM_Q_MASK 0xff00
+#define CTM_Q_Name_File 0x0100
+#define CTM_Q_Name_Dir 0x0200
+#define CTM_Q_Name_New 0x0400
+#define CTM_Q_Name_Subst 0x0800
+#define CTM_Q_MD5_After 0x0100
+#define CTM_Q_MD5_Before 0x0200
+#define CTM_Q_MD5_Chunk 0x0400
+#define CTM_Q_MD5_Force 0x0800
+
+struct CTM_Syntax {
+ char *Key; /* CTM key for operation */
+ int *List; /* List of operations */
+ };
+
+extern struct CTM_Syntax Syntax[];
+
+struct CTM_Filter {
+ struct CTM_Filter *Next; /* next filter in the list */
+ int Action; /* enable or disable */
+ regex_t CompiledRegex; /* compiled regex */
+};
+
+#define CTM_FILTER_DISABLE 0
+#define CTM_FILTER_ENABLE 1
+
+#define Malloc malloc
+#define Free free
+#define Delete(foo) if (!foo) ; else {Free(foo); foo = 0; }
+#define String(foo) strdup(foo)
+
+#ifndef EXTERN
+# define EXTERN extern
+#endif
+EXTERN u_char *Version;
+EXTERN u_char *Name;
+EXTERN u_char *Nbr;
+EXTERN u_char *TimeStamp;
+EXTERN u_char *Prefix;
+EXTERN u_char *FileName;
+EXTERN u_char *TmpDir;
+EXTERN u_char *CatPtr;
+EXTERN u_char *Buffer;
+EXTERN u_char *BackupFile;
+EXTERN u_char *TarCmd;
+
+/*
+ * Paranoid -- Just in case they should be after us...
+ * 0 not at all.
+ * 1 normal.
+ * 2 somewhat.
+ * 3 you bet!.
+ *
+ * Verbose -- What to tell mom...
+ * 0 Nothing which wouldn't surprise.
+ * 1 Normal.
+ * 2 Show progress '.'.
+ * 3 Show progress names, and actions.
+ * 4 even more...
+ * and so on
+ *
+ * ExitCode -- our Epitaph
+ * 0 Perfect, all input digested, no problems
+ * 1 Bad input, no point in retrying.
+ * 2 Pilot error, commandline problem &c
+ * 4 Out of resources.
+ * 8 Destination-tree not correct.
+ * 16 Destination-tree not correct, can force.
+ * 32 Internal problems.
+ *
+ */
+
+EXTERN int Paranoid;
+EXTERN int Verbose;
+EXTERN int Exit;
+EXTERN int Force;
+EXTERN int CheckIt;
+EXTERN int KeepIt;
+EXTERN int ListIt;
+EXTERN int SetTime;
+EXTERN struct timeval Times[2];
+EXTERN struct CTM_Filter *FilterList;
+EXTERN struct CTM_Filter *LastFilter;
+
+#define Exit_OK 0
+#define Exit_Garbage 1
+#define Exit_Pilot 2
+#define Exit_Broke 4
+#define Exit_NotOK 8
+#define Exit_Forcible 16
+#define Exit_Mess 32
+#define Exit_Done 64
+#define Exit_Version 128
+#define Exit_NoMatch 256
+
+void Fatal_(int ln, char *fn, char *kind);
+#define Fatal(foo) Fatal_(__LINE__,__FILE__,foo)
+#define Assert() Fatal_(__LINE__,__FILE__,"Assert failed.")
+#define WRONG {Assert(); return Exit_Mess;}
+
+u_char * Ffield(FILE *fd, MD5_CTX *ctx,u_char term);
+u_char * Fname(FILE *fd, MD5_CTX *ctx,u_char term,int qual, int verbose);
+
+int Fbytecnt(FILE *fd, MD5_CTX *ctx, u_char term);
+
+u_char * Fdata(FILE *fd, int u_chars, MD5_CTX *ctx);
+
+#define GETFIELD(p,q) if(!((p)=Ffield(fd,&ctx,(q)))) return BADREAD
+#define GETFIELDCOPY(p,q) if(!((p)=Ffield(fd,&ctx,(q)))) return BADREAD; else p=String(p)
+#define GETBYTECNT(p,q) if(0 >((p)= Fbytecnt(fd,&ctx,(q)))) return BADREAD
+#define GETDATA(p,q) if(!((p) = Fdata(fd,(q),&ctx))) return BADREAD
+#define GETNAMECOPY(p,q,r,v) if(!((p)=Fname(fd,&ctx,(q),(r),(v)))) return BADREAD; else p=String(p)
+
+int Pass1(FILE *fd, unsigned applied);
+int Pass2(FILE *fd);
+int PassB(FILE *fd);
+int Pass3(FILE *fd);
+
+int ctm_edit(u_char *script, int length, char *filein, char *fileout);
diff --git a/usr.sbin/ctm/ctm/ctm_ed.c b/usr.sbin/ctm/ctm/ctm_ed.c
new file mode 100644
index 0000000..31c5729
--- /dev/null
+++ b/usr.sbin/ctm/ctm/ctm_ed.c
@@ -0,0 +1,114 @@
+/*
+ * ----------------------------------------------------------------------------
+ * "THE BEER-WARE LICENSE" (Revision 42):
+ * <phk@login.dknet.dk> wrote this file. As long as you retain this notice you
+ * can do whatever you want with this stuff. If we meet some day, and you think
+ * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp
+ * ----------------------------------------------------------------------------
+ *
+ * $Id: ctm_ed.c,v 1.8 1997/02/22 16:05:23 peter Exp $
+ *
+ */
+
+#include "ctm.h"
+
+int
+ctm_edit(u_char *script, int length, char *filein, char *fileout)
+{
+ u_char *ep, cmd;
+ int ln, ln2, iln, ret=0, c;
+ FILE *fi=0,*fo=0;
+
+ fi = fopen(filein,"r");
+ if(!fi) {
+ warn("%s", filein);
+ return 8;
+ }
+
+ fo = fopen(fileout,"w");
+ if(!fo) {
+ warn("%s", fileout);
+ fclose(fi);
+ return 4;
+ }
+ iln = 1;
+ for(ep=script;ep < script+length;) {
+ cmd = *ep++;
+ if(cmd != 'a' && cmd != 'd') { ret = 1; goto bye; }
+ ln = 0;
+ while(isdigit(*ep)) {
+ ln *= 10;
+ ln += (*ep++ - '0');
+ }
+ if(*ep++ != ' ') { ret = 1; goto bye; }
+ ln2 = 0;
+ while(isdigit(*ep)) {
+ ln2 *= 10;
+ ln2 += (*ep++ - '0');
+ }
+ if(*ep++ != '\n') { ret = 1; goto bye; }
+
+
+ if(cmd == 'd') {
+ while(iln < ln) {
+ c = getc(fi);
+ if(c == EOF) { ret = 1; goto bye; }
+ putc(c,fo);
+ if(c == '\n')
+ iln++;
+ }
+ while(ln2) {
+ c = getc(fi);
+ if(c == EOF) { ret = 1; goto bye; }
+ if(c != '\n')
+ continue;
+ ln2--;
+ iln++;
+ }
+ continue;
+ }
+ if(cmd == 'a') {
+ while(iln <= ln) {
+ c = getc(fi);
+ if(c == EOF) { ret = 1; goto bye; }
+ putc(c,fo);
+ if(c == '\n')
+ iln++;
+ }
+ while(ln2) {
+ c = *ep++;
+ putc(c,fo);
+ if(c != '\n')
+ continue;
+ ln2--;
+ }
+ continue;
+ }
+ ret = 1;
+ goto bye;
+ }
+ while(1) {
+ c = getc(fi);
+ if(c == EOF) break;
+ putc(c,fo);
+ }
+ ret = 0;
+bye:
+ if(fi) {
+ if(fclose(fi) != 0) {
+ warn("%s", filein);
+ ret = 1;
+ }
+ }
+ if(fo) {
+ if(fflush(fo) != 0) {
+ warn("%s", fileout);
+ ret = 1;
+ }
+ if(fclose(fo) != 0) {
+ warn("%s", fileout);
+ ret = 1;
+ }
+ }
+ return ret;
+}
diff --git a/usr.sbin/ctm/ctm/ctm_input.c b/usr.sbin/ctm/ctm/ctm_input.c
new file mode 100644
index 0000000..2bbbbb6
--- /dev/null
+++ b/usr.sbin/ctm/ctm/ctm_input.c
@@ -0,0 +1,138 @@
+/*
+ * ----------------------------------------------------------------------------
+ * "THE BEER-WARE LICENSE" (Revision 42):
+ * <phk@login.dknet.dk> wrote this file. As long as you retain this notice you
+ * can do whatever you want with this stuff. If we meet some day, and you think
+ * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp
+ * ----------------------------------------------------------------------------
+ *
+ * $Id$
+ *
+ */
+
+#include "ctm.h"
+
+/*---------------------------------------------------------------------------*/
+void
+Fatal_(int ln, char *fn, char *kind)
+{
+ if(Verbose > 2)
+ fprintf(stderr,"Fatal error. (%s:%d)\n",fn,ln);
+ fprintf(stderr,"%s Fatal error: %s\n",FileName, kind);
+}
+#define Fatal(foo) Fatal_(__LINE__,__FILE__,foo)
+#define Assert() Fatal_(__LINE__,__FILE__,"Assert failed.")
+
+/*---------------------------------------------------------------------------*/
+/* get next field, check that the terminating whitespace is what we expect */
+u_char *
+Ffield(FILE *fd, MD5_CTX *ctx,u_char term)
+{
+ static u_char buf[BUFSIZ];
+ int i,l;
+
+ for(l=0;;) {
+ if((i=getc(fd)) == EOF) {
+ Fatal("Truncated patch.");
+ return 0;
+ }
+ buf[l++] = i;
+ if(isspace(i))
+ break;
+ if(l >= sizeof buf) {
+ Fatal("Corrupt patch.");
+ printf("Token is too long.\n");
+ return 0;
+ }
+ }
+ buf[l] = '\0';
+ MD5Update(ctx,buf,l);
+ if(buf[l-1] != term) {
+ Fatal("Corrupt patch.");
+ fprintf(stderr,"Expected \"%s\" but didn't find it {%02x}.\n",
+ term == '\n' ? "\\n" : " ",buf[l-1]);
+ if(Verbose > 4)
+ fprintf(stderr,"{%s}\n",buf);
+ return 0;
+ }
+ buf[--l] = '\0';
+ if(Verbose > 4)
+ fprintf(stderr,"<%s>\n",buf);
+ return buf;
+}
+
+int
+Fbytecnt(FILE *fd, MD5_CTX *ctx, u_char term)
+{
+ u_char *p,*q;
+ int u_chars=0;
+
+ p = Ffield(fd,ctx,term);
+ if(!p) return -1;
+ for(q=p;*q;q++) {
+ if(!isdigit(*q)) {
+ Fatal("Bytecount contains non-digit.");
+ return -1;
+ }
+ u_chars *= 10;
+ u_chars += (*q - '0');
+ }
+ if(u_chars > MAXSIZE) {
+ Fatal("Bytecount too large.");
+ return -1;
+ }
+ return u_chars;
+}
+
+u_char *
+Fdata(FILE *fd, int u_chars, MD5_CTX *ctx)
+{
+ u_char *p = Malloc(u_chars+1);
+
+ if(u_chars+1 != fread(p,1,u_chars+1,fd)) {
+ Fatal("Truncated patch.");
+ return 0;
+ }
+ MD5Update(ctx,p,u_chars+1);
+ if(p[u_chars] != '\n') {
+ if(Verbose > 3)
+ printf("FileData wasn't followed by a newline.\n");
+ Fatal("Corrupt patch.");
+ return 0;
+ }
+ p[u_chars] = '\0';
+ return p;
+}
+
+/*---------------------------------------------------------------------------*/
+/* get the filename in the next field, prepend BaseDir and give back the result
+ strings. The sustitute filename is return (the one with the suffix SUBSUFF)
+ if it exists and the qualifier contains CTM_Q_Name_Subst
+ NOTA: Buffer is already initialize with BaseDir, CatPtr is the insertion
+ point on this buffer + the length test in Ffield() is enough for Fname() */
+
+u_char *
+Fname(FILE *fd, MD5_CTX *ctx,u_char term,int qual, int verbose)
+{
+ u_char * p;
+ struct stat st;
+
+ if ((p = Ffield(fd,ctx,term)) == NULL) return(NULL);
+
+ strcpy(CatPtr, p);
+
+ if (!(qual & CTM_Q_Name_Subst)) return(Buffer);
+
+ p = Buffer + strlen(Buffer);
+
+ strcat(Buffer, SUBSUFF);
+
+ if ( -1 == stat(Buffer, &st) ) {
+ *p = '\0';
+ } else {
+ if(verbose > 2)
+ fprintf(stderr,"Using %s as substitute file\n", Buffer);
+ }
+
+ return (Buffer);
+}
diff --git a/usr.sbin/ctm/ctm/ctm_pass1.c b/usr.sbin/ctm/ctm/ctm_pass1.c
new file mode 100644
index 0000000..b98c0d1
--- /dev/null
+++ b/usr.sbin/ctm/ctm/ctm_pass1.c
@@ -0,0 +1,250 @@
+/*
+ * ----------------------------------------------------------------------------
+ * "THE BEER-WARE LICENSE" (Revision 42):
+ * <phk@login.dknet.dk> wrote this file. As long as you retain this notice you
+ * can do whatever you want with this stuff. If we meet some day, and you think
+ * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp
+ * ----------------------------------------------------------------------------
+ *
+ * $Id$
+ *
+ */
+
+#include "ctm.h"
+#define BADREAD 1
+
+/*---------------------------------------------------------------------------*/
+/* Pass1 -- Validate the incoming CTM-file.
+ */
+
+int
+Pass1(FILE *fd, unsigned applied)
+{
+ u_char *p,*q;
+ MD5_CTX ctx;
+ int i,j,sep,cnt;
+ u_char *md5=0,*name=0,*trash=0;
+ struct CTM_Syntax *sp;
+ int slashwarn=0, match=0, total_matches=0;
+ unsigned current;
+ char md5_1[33];
+
+ if(Verbose>3)
+ printf("Pass1 -- Checking integrity of incoming CTM-patch\n");
+ MD5Init (&ctx);
+
+ GETFIELD(p,' '); /* CTM_BEGIN */
+ if(strcmp(p,"CTM_BEGIN")) {
+ Fatal("Probably not a CTM-patch at all.");
+ if(Verbose>3)
+ fprintf(stderr,"Expected \"CTM_BEGIN\" got \"%s\".\n",p);
+ return 1;
+ }
+
+ GETFIELDCOPY(Version,' '); /* <Version> */
+ if(strcmp(Version,VERSION)) {
+ Fatal("CTM-patch is wrong version.");
+ if(Verbose>3)
+ fprintf(stderr,"Expected \"%s\" got \"%s\".\n",VERSION,p);
+ return 1;
+ }
+
+ GETFIELDCOPY(Name,' '); /* <Name> */
+ GETFIELDCOPY(Nbr,' '); /* <Nbr> */
+ GETFIELDCOPY(TimeStamp,' '); /* <TimeStamp> */
+ GETFIELDCOPY(Prefix,'\n'); /* <Prefix> */
+
+ sscanf(Nbr, "%u", &current);
+ if (FilterList || ListIt)
+ current = 0; /* ignore if -l or if filters are present */
+ if(current && current <= applied) {
+ if(Verbose > 0)
+ fprintf(stderr,"Delta number %u is already applied; ignoring.\n",
+ current);
+ return Exit_Version;
+ }
+
+ for(;;) {
+ Delete(md5);
+ Delete(name);
+ Delete(trash);
+ cnt = -1;
+ /* if a filter list is defined we assume that all pathnames require
+ an action opposite to that requested by the first filter in the
+ list.
+ If no filter is defined, all pathnames are assumed to match. */
+ match = (FilterList ? !(FilterList->Action) : CTM_FILTER_ENABLE);
+
+ GETFIELD(p,' '); /* CTM_something */
+
+ if (p[0] != 'C' || p[1] != 'T' || p[2] != 'M') {
+ Fatal("Expected CTM keyword.");
+ fprintf(stderr,"Got [%s]\n",p);
+ return 1;
+ }
+
+ if(!strcmp(p+3,"_END"))
+ break;
+
+ for(sp=Syntax;sp->Key;sp++)
+ if(!strcmp(p+3,sp->Key))
+ goto found;
+ Fatal("Expected CTM keyword.");
+ fprintf(stderr,"Got [%s]\n",p);
+ return 1;
+ found:
+ if(Verbose > 5)
+ fprintf(stderr,"%s ",sp->Key);
+ for(i=0;(j = sp->List[i]);i++) {
+ if (sp->List[i+1] && (sp->List[i+1] & CTM_F_MASK) != CTM_F_Bytes)
+ sep = ' ';
+ else
+ sep = '\n';
+
+ if(Verbose > 5)
+ fprintf(stderr," %x(%d)",sp->List[i],sep);
+
+ switch (j & CTM_F_MASK) {
+ case CTM_F_Name: /* XXX check for garbage and .. */
+ GETFIELDCOPY(name,sep);
+ j = strlen(name);
+ if(name[j-1] == '/' && !slashwarn) {
+ fprintf(stderr,"Warning: contains trailing slash\n");
+ slashwarn++;
+ }
+ if (name[0] == '/') {
+ Fatal("Absolute paths are illegal.");
+ return Exit_Mess;
+ }
+ q = name;
+ for (;;) {
+ if (q[0] == '.' && q[1] == '.')
+ if (q[2] == '/' || q[2] == '\0') {
+ Fatal("Paths containing '..' are illegal.");
+ return Exit_Mess;
+ }
+ if ((q = strchr(q, '/')) == NULL)
+ break;
+ q++;
+ }
+
+ /* if we have been asked to `keep' files then skip
+ removes; i.e. we don't match these entries at
+ all. */
+ if (KeepIt &&
+ (!strcmp(sp->Key,"DR") || !strcmp(sp->Key,"FR"))) {
+ match = CTM_FILTER_DISABLE;
+ break;
+ }
+
+ /* If filter expression have been defined, match the
+ path name against the expression list. */
+
+ if (FilterList) {
+ struct CTM_Filter *filter;
+
+ for (filter = FilterList; filter;
+ filter = filter->Next) {
+ if (0 == regexec(&filter->CompiledRegex, name,
+ 0, 0, 0))
+ /* if the name matches, adopt the
+ action */
+ match = filter->Action;
+ }
+ }
+
+ /* Add up the total number of matches */
+ total_matches += match;
+ break;
+ case CTM_F_Uid:
+ GETFIELD(p,sep);
+ while(*p) {
+ if(!isdigit(*p)) {
+ Fatal("Non-digit in uid.");
+ return 32;
+ }
+ p++;
+ }
+ break;
+ case CTM_F_Gid:
+ GETFIELD(p,sep);
+ while(*p) {
+ if(!isdigit(*p)) {
+ Fatal("Non-digit in gid.");
+ return 32;
+ }
+ p++;
+ }
+ break;
+ case CTM_F_Mode:
+ GETFIELD(p,sep);
+ while(*p) {
+ if(!isdigit(*p)) {
+ Fatal("Non-digit in mode.");
+ return 32;
+ }
+ p++;
+ }
+ break;
+ case CTM_F_MD5:
+ if(j & CTM_Q_MD5_Chunk) {
+ GETFIELDCOPY(md5,sep); /* XXX check for garbage */
+ } else if(j & CTM_Q_MD5_Before) {
+ GETFIELD(p,sep); /* XXX check for garbage */
+ } else if(j & CTM_Q_MD5_After) {
+ GETFIELD(p,sep); /* XXX check for garbage */
+ } else {
+ fprintf(stderr,"List = 0x%x\n",j);
+ Fatal("Unqualified MD5.");
+ return 32;
+ }
+ break;
+ case CTM_F_Count:
+ GETBYTECNT(cnt,sep);
+ break;
+ case CTM_F_Bytes:
+ if(cnt < 0) WRONG
+ GETDATA(trash,cnt);
+ p = MD5Data(trash,cnt,md5_1);
+ if(md5 && strcmp(md5,p)) {
+ Fatal("Internal MD5 failed.");
+ return Exit_Garbage;
+ default:
+ fprintf(stderr,"List = 0x%x\n",j);
+ Fatal("List had garbage.");
+ return Exit_Garbage;
+ }
+ }
+ }
+ if(Verbose > 5)
+ putc('\n',stderr);
+ if(ListIt && match)
+ printf("> %s %s\n", sp->Key, name);
+ }
+
+ Delete(md5);
+ Delete(name);
+ Delete(trash);
+
+ q = MD5End (&ctx,md5_1);
+ if(Verbose > 2)
+ printf("Expecting Global MD5 <%s>\n",q);
+ GETFIELD(p,'\n'); /* <MD5> */
+ if(Verbose > 2)
+ printf("Reference Global MD5 <%s>\n",p);
+ if(strcmp(q,p)) {
+ Fatal("MD5 sum doesn't match.");
+ fprintf(stderr,"\tI have:<%s>\n",q);
+ fprintf(stderr,"\tShould have been:<%s>\n",p);
+ return Exit_Garbage;
+ }
+ if (-1 != getc(fd)) {
+ if(!Force) {
+ Fatal("Trailing junk in CTM-file. Can Force with -F.");
+ return 16;
+ }
+ }
+ if ((Verbose > 1) && (0 == total_matches))
+ printf("No matches in \"%s\"\n", FileName);
+ return (total_matches ? Exit_OK : Exit_NoMatch);
+}
diff --git a/usr.sbin/ctm/ctm/ctm_pass2.c b/usr.sbin/ctm/ctm/ctm_pass2.c
new file mode 100644
index 0000000..7c38ff0
--- /dev/null
+++ b/usr.sbin/ctm/ctm/ctm_pass2.c
@@ -0,0 +1,245 @@
+/*
+ * ----------------------------------------------------------------------------
+ * "THE BEER-WARE LICENSE" (Revision 42):
+ * <phk@login.dknet.dk> wrote this file. As long as you retain this notice you
+ * can do whatever you want with this stuff. If we meet some day, and you think
+ * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp
+ * ----------------------------------------------------------------------------
+ *
+ * $Id: ctm_pass2.c,v 1.15 1997/02/22 16:05:26 peter Exp $
+ *
+ */
+
+#include "ctm.h"
+#define BADREAD 32
+
+/*---------------------------------------------------------------------------*/
+/* Pass2 -- Validate the incoming CTM-file.
+ */
+
+int
+Pass2(FILE *fd)
+{
+ u_char *p,*q,*md5=0;
+ MD5_CTX ctx;
+ int i,j,sep,cnt;
+ u_char *trash=0,*name=0;
+ struct CTM_Syntax *sp;
+ struct stat st;
+ int ret = 0;
+ int match = 0;
+ char md5_1[33];
+ struct CTM_Filter *filter;
+ FILE *ed = NULL;
+
+ if(Verbose>3)
+ printf("Pass2 -- Checking if CTM-patch will apply\n");
+ MD5Init (&ctx);
+
+ GETFIELD(p,' '); if(strcmp("CTM_BEGIN",p)) WRONG
+ GETFIELD(p,' '); if(strcmp(Version,p)) WRONG
+ GETFIELD(p,' '); if(strcmp(Name,p)) WRONG
+ /* XXX Lookup name in /etc/ctm,conf, read stuff */
+ GETFIELD(p,' '); if(strcmp(Nbr,p)) WRONG
+ /* XXX Verify that this is the next patch to apply */
+ GETFIELD(p,' '); if(strcmp(TimeStamp,p)) WRONG
+ GETFIELD(p,'\n'); if(strcmp(Prefix,p)) WRONG
+ /* XXX drop or use ? */
+
+ for(;;) {
+ Delete(trash);
+ Delete(name);
+ Delete(md5);
+ cnt = -1;
+
+ /* if a filter list was specified, check file name against
+ the filters specified
+ if no filter was given operate on all files. */
+ match = (FilterList ?
+ !(FilterList->Action) : CTM_FILTER_ENABLE);
+
+ GETFIELD(p,' ');
+
+ if (p[0] != 'C' || p[1] != 'T' || p[2] != 'M') WRONG
+
+ if(!strcmp(p+3,"_END"))
+ break;
+
+ for(sp=Syntax;sp->Key;sp++)
+ if(!strcmp(p+3,sp->Key))
+ goto found;
+ WRONG
+ found:
+ for(i=0;(j = sp->List[i]);i++) {
+ if (sp->List[i+1] && (sp->List[i+1] & CTM_F_MASK) != CTM_F_Bytes)
+ sep = ' ';
+ else
+ sep = '\n';
+
+ switch (j & CTM_F_MASK) {
+ case CTM_F_Name:
+ GETNAMECOPY(name,sep,j,0);
+ /* If `keep' was specified, we won't remove any files,
+ so don't check if the file exists */
+ if (KeepIt &&
+ (!strcmp(sp->Key,"FR") || !strcmp(sp->Key,"DR"))) {
+ match = CTM_FILTER_DISABLE;
+ break;
+ }
+
+ for (filter = FilterList; filter; filter = filter->Next) if (0 == regexec(&filter->CompiledRegex, name,
+ 0, 0, 0)) {
+ match = filter->Action;
+ }
+
+ if (CTM_FILTER_DISABLE == match)
+ break; /* should ignore this file */
+
+ /* XXX Check DR DM rec's for parent-dir */
+ if(j & CTM_Q_Name_New) {
+ /* XXX Check DR FR rec's for item */
+ if(-1 != stat(name,&st)) {
+ fprintf(stderr," %s: %s exists.\n",
+ sp->Key,name);
+ ret |= Exit_Forcible;
+ }
+ break;
+ }
+ if(-1 == stat(name,&st)) {
+ fprintf(stderr," %s: %s doesn't exist.\n",
+ sp->Key,name);
+ if (sp->Key[1] == 'R')
+ ret |= Exit_Forcible;
+ else
+ ret |= Exit_NotOK;
+ break;
+ }
+ if (SetTime && getuid() && (getuid() != st.st_uid)) {
+ fprintf(stderr,
+ " %s: %s not mine, cannot set time.\n",
+ sp->Key,name);
+ ret |= Exit_NotOK;
+ }
+ if (j & CTM_Q_Name_Dir) {
+ if((st.st_mode & S_IFMT) != S_IFDIR) {
+ fprintf(stderr,
+ " %s: %s exist, but isn't dir.\n",
+ sp->Key,name);
+ ret |= Exit_NotOK;
+ }
+ break;
+ }
+ if (j & CTM_Q_Name_File) {
+ if((st.st_mode & S_IFMT) != S_IFREG) {
+ fprintf(stderr,
+ " %s: %s exist, but isn't file.\n",
+ sp->Key,name);
+ ret |= Exit_NotOK;
+ }
+ break;
+ }
+ break;
+ case CTM_F_Uid:
+ case CTM_F_Gid:
+ case CTM_F_Mode:
+ GETFIELD(p,sep);
+ break;
+ case CTM_F_MD5:
+ if(!name) WRONG
+ if(j & CTM_Q_MD5_Before) {
+ char *tmp;
+ GETFIELD(p,sep);
+ if(match && (st.st_mode & S_IFMT) == S_IFREG &&
+ (tmp = MD5File(name,md5_1)) != NULL &&
+ strcmp(tmp,p)) {
+ fprintf(stderr," %s: %s md5 mismatch.\n",
+ sp->Key,name);
+ if(j & CTM_Q_MD5_Force) {
+ if(Force)
+ fprintf(stderr," Can and will force.\n");
+ else
+ fprintf(stderr," Could have forced.\n");
+ ret |= Exit_Forcible;
+ } else {
+ ret |= Exit_NotOK;
+ }
+ }
+ break;
+ }
+ if(j & CTM_Q_MD5_After) {
+ GETFIELDCOPY(md5,sep);
+ break;
+ }
+ /* Unqualified MD5 */
+ WRONG
+ break;
+ case CTM_F_Count:
+ GETBYTECNT(cnt,sep);
+ break;
+ case CTM_F_Bytes:
+ if(cnt < 0) WRONG
+ GETDATA(trash,cnt);
+ if (!match)
+ break;
+ if(!strcmp(sp->Key,"FN")) {
+ p = tempnam(TmpDir,"CTMclient");
+ j = ctm_edit(trash,cnt,name,p);
+ if(j) {
+ fprintf(stderr," %s: %s edit returned %d.\n",
+ sp->Key,name,j);
+ ret |= j;
+ unlink(p);
+ Free(p);
+ return ret;
+ } else if(strcmp(md5,MD5File(p,md5_1))) {
+ fprintf(stderr," %s: %s edit fails.\n",
+ sp->Key,name);
+ ret |= Exit_Mess;
+ unlink(p);
+ Free(p);
+ return ret;
+ }
+ unlink(p);
+ Free(p);
+ } else if (!strcmp(sp->Key,"FE")) {
+ p = tempnam(TmpDir,"CTMclient");
+ ed = popen("ed","w");
+ if (!ed) {
+ WRONG
+ }
+ fprintf(ed,"e %s\n", name);
+ if (cnt != fwrite(trash,1,cnt,ed)) {
+ warn("%s", name);
+ pclose(ed);
+ WRONG
+ }
+ fprintf(ed,"w %s\n",p);
+ if (pclose(ed)) {
+ warn("%s", p);
+ WRONG
+ }
+ if(strcmp(md5,MD5File(p,md5_1))) {
+ fprintf(stderr,"%s %s MD5 didn't come out right\n",
+ sp->Key, name);
+ WRONG
+ }
+ unlink(p);
+ Free(p);
+ }
+
+ break;
+ default: WRONG
+ }
+ }
+ }
+
+ Delete(trash);
+ Delete(name);
+ Delete(md5);
+
+ q = MD5End (&ctx,md5_1);
+ GETFIELD(p,'\n'); /* <MD5> */
+ if(strcmp(q,p)) WRONG
+ if (-1 != getc(fd)) WRONG
+ return ret;
+}
diff --git a/usr.sbin/ctm/ctm/ctm_pass3.c b/usr.sbin/ctm/ctm/ctm_pass3.c
new file mode 100644
index 0000000..9ca19d8
--- /dev/null
+++ b/usr.sbin/ctm/ctm/ctm_pass3.c
@@ -0,0 +1,295 @@
+/*
+ * ----------------------------------------------------------------------------
+ * "THE BEER-WARE LICENSE" (Revision 42):
+ * <phk@login.dknet.dk> wrote this file. As long as you retain this notice you
+ * can do whatever you want with this stuff. If we meet some day, and you think
+ * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp
+ * ----------------------------------------------------------------------------
+ *
+ * $Id: ctm_pass3.c,v 1.17 1997/02/22 16:05:27 peter Exp $
+ *
+ */
+
+#include "ctm.h"
+#define BADREAD 32
+
+/*---------------------------------------------------------------------------*/
+/* Pass3 -- Validate the incoming CTM-file.
+ */
+
+int
+settime(const char *name, const struct timeval *times)
+{
+ if (SetTime)
+ if (utimes(name,times)) {
+ warn("utimes(): %s", name);
+ return -1;
+ }
+ return 0;
+}
+
+int
+Pass3(FILE *fd)
+{
+ u_char *p,*q,buf[BUFSIZ];
+ MD5_CTX ctx;
+ int i,j,sep,cnt;
+ u_char *md5=0,*md5before=0,*trash=0,*name=0,*uid=0,*gid=0,*mode=0;
+ struct CTM_Syntax *sp;
+ FILE *ed=0;
+ struct stat st;
+ char md5_1[33];
+ int match=0;
+ struct timeval times[2];
+ struct CTM_Filter *filter = NULL;
+ if(Verbose>3)
+ printf("Pass3 -- Applying the CTM-patch\n");
+ MD5Init (&ctx);
+
+ GETFIELD(p,' '); if(strcmp("CTM_BEGIN",p)) WRONG
+ GETFIELD(p,' '); if(strcmp(Version,p)) WRONG
+ GETFIELD(p,' '); if(strcmp(Name,p)) WRONG
+ GETFIELD(p,' '); if(strcmp(Nbr,p)) WRONG
+ GETFIELD(p,' '); if(strcmp(TimeStamp,p)) WRONG
+ GETFIELD(p,'\n'); if(strcmp(Prefix,p)) WRONG
+
+ /*
+ * This would be cleaner if mktime() worked in UTC rather than
+ * local time.
+ */
+ if (SetTime) {
+ struct tm tm;
+ char *tz;
+ char buf[5];
+ int i;
+
+#define SUBSTR(off,len) strncpy(buf, &TimeStamp[off], len), buf[len] = '\0'
+#define WRONGDATE { fprintf(stderr, " %s failed date validation\n",\
+ TimeStamp); WRONG}
+
+ if (strlen(TimeStamp) != 15 || TimeStamp[14] != 'Z') WRONGDATE
+ for (i = 0; i < 14; i++)
+ if (!isdigit(TimeStamp[i])) WRONGDATE
+
+ tz = getenv("TZ");
+ if (setenv("TZ", "UTC", 1) < 0) WRONG
+ tzset();
+
+ tm.tm_isdst = tm.tm_gmtoff = 0;
+
+ SUBSTR(0, 4);
+ tm.tm_year = atoi(buf) - 1900;
+ SUBSTR(4, 2);
+ tm.tm_mon = atoi(buf) - 1;
+ if (tm.tm_mon < 0 || tm.tm_mon > 11) WRONGDATE
+ SUBSTR(6, 2);
+ tm.tm_mday = atoi(buf);
+ if (tm.tm_mday < 1 || tm.tm_mday > 31) WRONG;
+ SUBSTR(8, 2);
+ tm.tm_hour = atoi(buf);
+ if (tm.tm_hour > 24) WRONGDATE
+ SUBSTR(10, 2);
+ tm.tm_min = atoi(buf);
+ if (tm.tm_min > 59) WRONGDATE
+ SUBSTR(12, 2);
+ tm.tm_sec = atoi(buf);
+ if (tm.tm_min > 62) WRONGDATE /* allow leap seconds */
+
+ times[0].tv_sec = times[1].tv_sec = mktime(&tm);
+ if (times[0].tv_sec == -1) WRONGDATE
+ times[0].tv_usec = times[1].tv_usec = 0;
+
+ if (tz) {
+ if (setenv("TZ", tz, 1) < 0) WRONGDATE
+ } else {
+ unsetenv("TZ");
+ }
+ }
+
+ for(;;) {
+ Delete(md5);
+ Delete(uid);
+ Delete(gid);
+ Delete(mode);
+ Delete(md5before);
+ Delete(trash);
+ Delete(name);
+ cnt = -1;
+
+ GETFIELD(p,' ');
+
+ if (p[0] != 'C' || p[1] != 'T' || p[2] != 'M') WRONG
+
+ if(!strcmp(p+3,"_END"))
+ break;
+
+ for(sp=Syntax;sp->Key;sp++)
+ if(!strcmp(p+3,sp->Key))
+ goto found;
+ WRONG
+ found:
+ for(i=0;(j = sp->List[i]);i++) {
+ if (sp->List[i+1] && (sp->List[i+1] & CTM_F_MASK) != CTM_F_Bytes)
+ sep = ' ';
+ else
+ sep = '\n';
+
+ switch (j & CTM_F_MASK) {
+ case CTM_F_Name: GETNAMECOPY(name,sep,j, Verbose); break;
+ case CTM_F_Uid: GETFIELDCOPY(uid,sep); break;
+ case CTM_F_Gid: GETFIELDCOPY(gid,sep); break;
+ case CTM_F_Mode: GETFIELDCOPY(mode,sep); break;
+ case CTM_F_MD5:
+ if(j & CTM_Q_MD5_Before)
+ GETFIELDCOPY(md5before,sep);
+ else
+ GETFIELDCOPY(md5,sep);
+ break;
+ case CTM_F_Count: GETBYTECNT(cnt,sep); break;
+ case CTM_F_Bytes: GETDATA(trash,cnt); break;
+ default: WRONG
+ }
+ }
+ /* XXX This should go away. Disallow trailing '/' */
+ j = strlen(name)-1;
+ if(name[j] == '/') name[j] = '\0';
+
+ /*
+ * If a filter list is specified, run thru the filter list and
+ * match `name' against filters. If the name matches, set the
+ * required action to that specified in the filter.
+ * The default action if no filterlist is given is to match
+ * everything.
+ */
+
+ match = (FilterList ? !(FilterList->Action) : CTM_FILTER_ENABLE);
+ for (filter = FilterList; filter; filter = filter->Next) {
+ if (0 == regexec(&filter->CompiledRegex, name,
+ 0, 0, 0)) {
+ match = filter->Action;
+ }
+ }
+
+ if (CTM_FILTER_DISABLE == match) /* skip file if disabled */
+ continue;
+
+ if (Verbose > 0)
+ fprintf(stderr,"> %s %s\n",sp->Key,name);
+ if(!strcmp(sp->Key,"FM") || !strcmp(sp->Key, "FS")) {
+ i = open(name,O_WRONLY|O_CREAT|O_TRUNC,0666);
+ if(i < 0) {
+ warn("%s", name);
+ WRONG
+ }
+ if(cnt != write(i,trash,cnt)) {
+ warn("%s", name);
+ WRONG
+ }
+ close(i);
+ if(strcmp(md5,MD5File(name,md5_1))) {
+ fprintf(stderr," %s %s MD5 didn't come out right\n",
+ sp->Key,name);
+ WRONG
+ }
+ if (settime(name,times)) WRONG
+ continue;
+ }
+ if(!strcmp(sp->Key,"FE")) {
+ ed = popen("ed","w");
+ if(!ed) {
+ WRONG
+ }
+ fprintf(ed,"e %s\n",name);
+ if(cnt != fwrite(trash,1,cnt,ed)) {
+ warn("%s", name);
+ pclose(ed);
+ WRONG
+ }
+ fprintf(ed,"w %s\n",name);
+ if(pclose(ed)) {
+ warn("ed");
+ WRONG
+ }
+ if(strcmp(md5,MD5File(name,md5_1))) {
+ fprintf(stderr," %s %s MD5 didn't come out right\n",
+ sp->Key,name);
+ WRONG
+ }
+ if (settime(name,times)) WRONG
+ continue;
+ }
+ if(!strcmp(sp->Key,"FN")) {
+ strcpy(buf,name);
+ strcat(buf,TMPSUFF);
+ i = ctm_edit(trash,cnt,name,buf);
+ if(i) {
+ fprintf(stderr," %s %s Edit failed with code %d.\n",
+ sp->Key,name,i);
+ WRONG
+ }
+ if(strcmp(md5,MD5File(buf,md5_1))) {
+ fprintf(stderr," %s %s Edit failed MD5 check.\n",
+ sp->Key,name);
+ WRONG
+ }
+ if (rename(buf,name) == -1)
+ WRONG
+ if (settime(name,times)) WRONG
+ continue;
+ }
+ if(!strcmp(sp->Key,"DM")) {
+ if(0 > mkdir(name,0777)) {
+ sprintf(buf,"mkdir -p %s",name);
+ system(buf);
+ }
+ if(0 > stat(name,&st) || ((st.st_mode & S_IFMT) != S_IFDIR)) {
+ fprintf(stderr,"<%s> mkdir failed\n",name);
+ WRONG
+ }
+ if (settime(name,times)) WRONG
+ continue;
+ }
+ if(!strcmp(sp->Key,"FR")) {
+ if (KeepIt) {
+ if (Verbose > 1)
+ printf("<%s> not removed\n", name);
+ }
+ else if (0 != unlink(name)) {
+ fprintf(stderr,"<%s> unlink failed\n",name);
+ if (!Force)
+ WRONG
+ }
+ continue;
+ }
+ if(!strcmp(sp->Key,"DR")) {
+ /*
+ * We cannot use rmdir() because we do not get the directories
+ * in '-depth' order (cvs-cur.0018.gz for examples)
+ */
+ if (KeepIt) {
+ if (Verbose > 1) {
+ printf("<%s> not removed\n", name);
+ }
+ } else {
+ sprintf(buf,"rm -rf %s",name);
+ system(buf);
+ }
+ continue;
+ }
+ WRONG
+ }
+
+ Delete(md5);
+ Delete(uid);
+ Delete(gid);
+ Delete(mode);
+ Delete(md5before);
+ Delete(trash);
+ Delete(name);
+
+ q = MD5End (&ctx,md5_1);
+ GETFIELD(p,'\n');
+ if(strcmp(q,p)) WRONG
+ if (-1 != getc(fd)) WRONG
+ return 0;
+}
diff --git a/usr.sbin/ctm/ctm/ctm_passb.c b/usr.sbin/ctm/ctm/ctm_passb.c
new file mode 100644
index 0000000..0cf657f
--- /dev/null
+++ b/usr.sbin/ctm/ctm/ctm_passb.c
@@ -0,0 +1,142 @@
+/*
+ * ----------------------------------------------------------------------------
+ * "THE BEER-WARE LICENSE" (Revision 42):
+ * <koshy@india.hp.com> wrote this file. As long as you retain this notice you
+ * can do whatever you want with this stuff. If we meet some day, and you think
+ * this stuff is worth it, you can buy me a beer in return. Joseph Koshy
+ * ----------------------------------------------------------------------------
+ *
+ * $Id: ctm_passb.c,v 1.3 1997/02/22 16:05:28 peter Exp $
+ *
+ */
+
+#include "ctm.h"
+#define BADREAD 32
+
+/*---------------------------------------------------------------------------*/
+/* PassB -- Backup modified files.
+ */
+
+int
+PassB(FILE *fd)
+{
+ u_char *p,*q;
+ MD5_CTX ctx;
+ int i,j,sep,cnt;
+ u_char *md5=0,*md5before=0,*trash=0,*name=0,*uid=0,*gid=0,*mode=0;
+ struct CTM_Syntax *sp;
+ FILE *b = 0; /* backup command */
+ u_char buf[BUFSIZ];
+ char md5_1[33];
+ int ret = 0;
+ int match = 0;
+ struct CTM_Filter *filter = NULL;
+
+ if(Verbose>3)
+ printf("PassB -- Backing up files which would be changed.\n");
+
+ MD5Init (&ctx);
+ sprintf(buf, TarCmd, BackupFile);
+ b=popen(buf, "w");
+ if(!b) { warn("%s", buf); return Exit_Garbage; }
+
+ GETFIELD(p,' '); if(strcmp("CTM_BEGIN",p)) WRONG
+ GETFIELD(p,' '); if(strcmp(Version,p)) WRONG
+ GETFIELD(p,' '); if(strcmp(Name,p)) WRONG
+ GETFIELD(p,' '); if(strcmp(Nbr,p)) WRONG
+ GETFIELD(p,' '); if(strcmp(TimeStamp,p)) WRONG
+ GETFIELD(p,'\n'); if(strcmp(Prefix,p)) WRONG
+
+ for(;;) {
+ Delete(md5);
+ Delete(uid);
+ Delete(gid);
+ Delete(mode);
+ Delete(md5before);
+ Delete(trash);
+ Delete(name);
+ cnt = -1;
+
+ GETFIELD(p,' ');
+
+ if (p[0] != 'C' || p[1] != 'T' || p[2] != 'M') WRONG
+
+ if(!strcmp(p+3,"_END"))
+ break;
+
+ for(sp=Syntax;sp->Key;sp++)
+ if(!strcmp(p+3,sp->Key))
+ goto found;
+ WRONG
+ found:
+ for(i=0;(j = sp->List[i]);i++) {
+ if (sp->List[i+1] && (sp->List[i+1] & CTM_F_MASK) != CTM_F_Bytes)
+ sep = ' ';
+ else
+ sep = '\n';
+
+ switch (j & CTM_F_MASK) {
+ case CTM_F_Name: GETNAMECOPY(name,sep,j, Verbose); break;
+ case CTM_F_Uid: GETFIELDCOPY(uid,sep); break;
+ case CTM_F_Gid: GETFIELDCOPY(gid,sep); break;
+ case CTM_F_Mode: GETFIELDCOPY(mode,sep); break;
+ case CTM_F_MD5:
+ if(j & CTM_Q_MD5_Before)
+ GETFIELDCOPY(md5before,sep);
+ else
+ GETFIELDCOPY(md5,sep);
+ break;
+ case CTM_F_Count: GETBYTECNT(cnt,sep); break;
+ case CTM_F_Bytes: GETDATA(trash,cnt); break;
+ default: WRONG
+ }
+ }
+ /* XXX This should go away. Disallow trailing '/' */
+ j = strlen(name)-1;
+ if(name[j] == '/') name[j] = '\0';
+
+ if (KeepIt &&
+ (!strcmp(sp->Key,"DR") || !strcmp(sp->Key,"FR")))
+ continue;
+
+ /* match the name against the elements of the filter list. The
+ action associated with the last matched filter determines whether
+ this file should be ignored or backed up. */
+ match = (FilterList ? !(FilterList->Action) : CTM_FILTER_ENABLE);
+ for (filter = FilterList; filter; filter = filter->Next) {
+ if (0 == regexec(&filter->CompiledRegex, name, 0, 0, 0))
+ match = filter->Action;
+ }
+
+ if (CTM_FILTER_DISABLE == match)
+ continue;
+
+ if (!strcmp(sp->Key,"FS") || !strcmp(sp->Key,"FN") ||
+ !strcmp(sp->Key,"AS") || !strcmp(sp->Key,"DR") ||
+ !strcmp(sp->Key,"FR")) {
+ /* send name to the archiver for a backup */
+ cnt = strlen(name);
+ if (cnt != fwrite(name,1,cnt,b) || EOF == fputc('\n',b)) {
+ warn("%s", name);
+ pclose(b);
+ WRONG;
+ }
+ }
+ }
+
+ ret = pclose(b);
+
+ Delete(md5);
+ Delete(uid);
+ Delete(gid);
+ Delete(mode);
+ Delete(md5before);
+ Delete(trash);
+ Delete(name);
+
+ q = MD5End (&ctx,md5_1);
+ GETFIELD(p,'\n'); /* <MD5> */
+ if(strcmp(q,p)) WRONG
+ if (-1 != getc(fd)) WRONG
+ return ret;
+}
diff --git a/usr.sbin/ctm/ctm/ctm_syntax.c b/usr.sbin/ctm/ctm/ctm_syntax.c
new file mode 100644
index 0000000..509aef1
--- /dev/null
+++ b/usr.sbin/ctm/ctm/ctm_syntax.c
@@ -0,0 +1,67 @@
+/*
+ * ----------------------------------------------------------------------------
+ * "THE BEER-WARE LICENSE" (Revision 42):
+ * <phk@login.dknet.dk> wrote this file. As long as you retain this notice you
+ * can do whatever you want with this stuff. If we meet some day, and you think
+ * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp
+ * ----------------------------------------------------------------------------
+ *
+ * $Id$
+ *
+ */
+
+#include "ctm.h"
+
+/* The fields... */
+#define Name CTM_F_Name
+#define Uid CTM_F_Uid
+#define Gid CTM_F_Gid
+#define Mode CTM_F_Mode
+#define MD5 CTM_F_MD5
+#define Count CTM_F_Count
+#define Bytes CTM_F_Bytes
+
+/* The qualifiers... */
+#define File CTM_Q_Name_File
+#define Dir CTM_Q_Name_Dir
+#define New CTM_Q_Name_New
+#define Subst CTM_Q_Name_Subst
+#define After CTM_Q_MD5_After
+#define Before CTM_Q_MD5_Before
+#define Chunk CTM_Q_MD5_Chunk
+#define Force CTM_Q_MD5_Force
+
+static int ctmFM[] = /* File Make */
+ { Name|File|New|Subst, Uid, Gid, Mode,
+ MD5|After|Chunk, Count, Bytes,0 };
+
+static int ctmFS[] = /* File Substitute */
+ { Name|File|Subst, Uid, Gid, Mode,
+ MD5|Before|Force, MD5|After|Chunk, Count, Bytes,0 };
+
+static int ctmFE[] = /* File Edit */
+ { Name|File|Subst, Uid, Gid, Mode,
+ MD5|Before, MD5|After, Count, Bytes,0 };
+
+static int ctmFR[] = /* File Remove */
+ { Name|File|Subst, MD5|Before, 0 };
+
+static int ctmAS[] = /* Attribute Substitute */
+ { Name|Subst, Uid, Gid, Mode, 0 };
+
+static int ctmDM[] = /* Directory Make */
+ { Name|Dir|New , Uid, Gid, Mode, 0 };
+
+static int ctmDR[] = /* Directory Remove */
+ { Name|Dir, 0 };
+
+struct CTM_Syntax Syntax[] = {
+ { "FM", ctmFM },
+ { "FS", ctmFS },
+ { "FE", ctmFE },
+ { "FN", ctmFE },
+ { "FR", ctmFR },
+ { "AS", ctmAS },
+ { "DM", ctmDM },
+ { "DR", ctmDR },
+ { 0, 0} };
diff --git a/usr.sbin/ctm/ctm_dequeue/Makefile b/usr.sbin/ctm/ctm_dequeue/Makefile
new file mode 100644
index 0000000..543e77e
--- /dev/null
+++ b/usr.sbin/ctm/ctm_dequeue/Makefile
@@ -0,0 +1,8 @@
+PROG= ctm_dequeue
+SRCS= ctm_dequeue.c error.c
+NOMAN= yes
+CFLAGS+= -Wall -I${.CURDIR}/../ctm_rmail
+
+.PATH: ${.CURDIR}/../ctm_rmail
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/ctm/ctm_dequeue/ctm_dequeue.c b/usr.sbin/ctm/ctm_dequeue/ctm_dequeue.c
new file mode 100644
index 0000000..9e09864
--- /dev/null
+++ b/usr.sbin/ctm/ctm_dequeue/ctm_dequeue.c
@@ -0,0 +1,209 @@
+/*
+ * Copyright (c) 1996, Gary J. Palmer
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id$
+ */
+
+/*
+ * ctm_dequeue: Dequeue queued delta pieces and mail them.
+ *
+ * The pieces have already been packaged up as mail messages by ctm_smail,
+ * and will be simply passed to sendmail in alphabetical order.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <fts.h>
+#include <limits.h>
+#include <errno.h>
+#include <paths.h>
+#include "error.h"
+#include "options.h"
+
+#define DEFAULT_NUM 1 /* Default number of pieces mailed per run. */
+
+int fts_sort(const FTSENT **, const FTSENT **);
+int run_sendmail(int ifd);
+
+int
+main(int argc, char **argv)
+{
+ char *log_file = NULL;
+ char *queue_dir = NULL;
+ char *list[2];
+ int num_to_send = DEFAULT_NUM, chunk;
+ int fd;
+ FTS *fts;
+ FTSENT *ftsent;
+ int piece, npieces;
+ char filename[PATH_MAX];
+
+ err_prog_name(argv[0]);
+
+ OPTIONS("[-l log] [-n num] queuedir")
+ NUMBER('n', num_to_send)
+ STRING('l', log_file)
+ ENDOPTS
+
+ if (argc != 2)
+ usage();
+
+ if (log_file)
+ err_set_log(log_file);
+
+ queue_dir = argv[1];
+ list[0] = queue_dir;
+ list[1] = NULL;
+
+ fts = fts_open(list, FTS_PHYSICAL|FTS_COMFOLLOW, fts_sort);
+ if (fts == NULL)
+ {
+ err("fts failed on `%s'", queue_dir);
+ exit(1);
+ }
+
+ ftsent = fts_read(fts);
+ if (ftsent == NULL || ftsent->fts_info != FTS_D)
+ {
+ err("not a directory: %s", queue_dir);
+ exit(1);
+ }
+
+ ftsent = fts_children(fts, 0);
+ if (ftsent == NULL && errno)
+ {
+ err("*ftschildren failed");
+ exit(1);
+ }
+
+ for (chunk = 1; ftsent != NULL; ftsent = ftsent->fts_link)
+ {
+ /*
+ * Skip non-files and ctm_smail tmp files (ones starting with `.')
+ */
+ if (ftsent->fts_info != FTS_F || ftsent->fts_name[0] == '.')
+ continue;
+
+ sprintf(filename, "%s/%s", queue_dir, ftsent->fts_name);
+ fd = open(filename, O_RDONLY);
+ if (fd < 0)
+ {
+ err("*open: %s", filename);
+ exit(1);
+ }
+
+ if (run_sendmail(fd))
+ exit(1);
+
+ close(fd);
+
+ if (unlink(filename) < 0)
+ {
+ err("*unlink: %s", filename);
+ exit(1);
+ }
+
+ /*
+ * Deduce the delta, piece number, and number of pieces from
+ * the name of the file in the queue. Ideally, we should be
+ * able to get the mail alias name too.
+ *
+ * NOTE: This depends intimately on the queue name used in ctm_smail.
+ */
+ npieces = atoi(&ftsent->fts_name[ftsent->fts_namelen-3]);
+ piece = atoi(&ftsent->fts_name[ftsent->fts_namelen-7]);
+ err("%.*s %d/%d sent", ftsent->fts_namelen-8, ftsent->fts_name,
+ piece, npieces);
+
+ if (chunk++ == num_to_send)
+ break;
+ }
+
+ fts_close(fts);
+
+ return(0);
+}
+
+int
+fts_sort(const FTSENT ** a, const FTSENT ** b)
+{
+ if ((*a)->fts_info != FTS_F)
+ return(0);
+ if ((*a)->fts_info != FTS_F)
+ return(0);
+
+ return (strcmp((*a)->fts_name, (*b)->fts_name));
+}
+
+/*
+ * Run sendmail with the given file descriptor as standard input.
+ * Sendmail will decode the destination from the message contents.
+ * Returns 0 on success, 1 on failure.
+ */
+int
+run_sendmail(int ifd)
+{
+ pid_t child, pid;
+ int status;
+
+ switch (child = fork())
+ {
+ case -1:
+ err("*fork");
+ return(1);
+
+ case 0: /* Child */
+ dup2(ifd, 0);
+ execl(_PATH_SENDMAIL, _PATH_SENDMAIL, "-odq", "-t", NULL);
+ err("*exec: %s", _PATH_SENDMAIL);
+ _exit(1);
+
+ default: /* Parent */
+ while ((pid = wait(&status)) != child)
+ {
+ if (pid == -1 && errno != EINTR)
+ {
+ err("*wait");
+ return(1);
+ }
+ }
+ if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
+ {
+ err("sendmail failed");
+ return(1);
+ }
+ }
+
+ return(0);
+}
diff --git a/usr.sbin/ctm/ctm_rmail/Makefile b/usr.sbin/ctm/ctm_rmail/Makefile
new file mode 100644
index 0000000..f904368
--- /dev/null
+++ b/usr.sbin/ctm/ctm_rmail/Makefile
@@ -0,0 +1,7 @@
+PROG= ctm_rmail
+SRCS= ctm_rmail.c error.c
+CFLAGS+= -Wall
+MLINKS+= ctm_rmail.1 ctm_smail.1
+MLINKS+= ctm_rmail.1 ctm_dequeue.1
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/ctm/ctm_rmail/ctm_rmail.1 b/usr.sbin/ctm/ctm_rmail/ctm_rmail.1
new file mode 100644
index 0000000..2338acf
--- /dev/null
+++ b/usr.sbin/ctm/ctm_rmail/ctm_rmail.1
@@ -0,0 +1,479 @@
+.\" NOTICE: This is free documentation. I hope you get some use from these
+.\" words. In return you should think about all the nice people who sweat
+.\" blood to document their free software. Maybe you should write some
+.\" documentation and give it away. Maybe with a free program attached!
+.\"
+.\" Author: Stephen McKay
+.\"
+.Dd January 24, 1995
+.Dt CTM_MAIL 1
+.Os
+.Sh NAME
+.Nm ctm_smail, ctm_rmail
+.Nd send and receive
+.Nm ctm
+deltas via mail
+.Sh SYNOPSIS
+.Nm ctm_smail
+.Op Fl l Ar log
+.Op Fl m Ar maxmsgsize
+.Op Fl c Ar maxctmsize
+.Op Fl q Ar queue-dir
+.Ar ctm-delta
+.Ar mail-alias
+.Nm ctm_dequeue
+.Op Fl l Ar log
+.Op Fl n Ar numchunks
+.Ar queue-dir
+.Nm ctm_rmail
+.Op Fl Dfuv
+.Op Fl l Ar log
+.Op Fl p Ar piecedir
+.Op Fl d Ar deltadir
+.Op Fl b Ar basedir
+.Op Ar
+.Sh DESCRIPTION
+In conjunction with the
+.Xr ctm 1
+command,
+.Nm ctm_smail ,
+.Nm ctm_dequeue
+and
+.Nm ctm_rmail
+are used to distribute changes to a source tree via email.
+.Nm ctm_smail
+is given a compressed
+.Xr ctm
+delta, and a mailing list to send it to. It splits the delta into manageable
+pieces, encodes them as mail messages and sends them to the mailing list
+(optionally queued to spread the mail load).
+Each recipient uses
+.Nm ctm_rmail
+(either manually or automatically) to decode and reassemble the delta, and
+optionally call
+.Xr ctm
+to apply it to the source tree.
+At the moment,
+several source trees are distributed, and by several sites. These include
+the FreeBSD-current source and CVS trees, distributed by
+.Li freefall.FreeBSD.org .
+.Pp
+Command line arguments for
+.Nm ctm_smail :
+.Bl -tag -width indent
+.It Fl l Ar log
+Instead of appearing on
+.Em stderr ,
+error diagnostics and informational messages (other than command line errors)
+are time stamped and written to the file
+.Em log .
+.It Fl m Ar maxmsgsize
+Limit the maximum size mail message that
+.Nm ctm_smail
+is allowed to send. It is approximate since mail headers and other niceties
+are not counted in this limit. If not specified, it will default to 64000
+bytes, leaving room for 1535 bytes of headers before the rumoured 64k mail
+limit.
+.It Fl c Ar maxctmsize
+Limit the maximum size delta that will be sent. Deltas bigger that this
+limit will cause an apology mail message to be sent to the mailing list.
+This is to prevent massive changes overwhelming users' mail boxes. Note that
+this is the size before encoding. Encoding causes a 4/3 size increase before
+mail headers are added. If not specified, there is no limit.
+.It Fl q Ar queue-dir
+Instead of mailing the delta pieces now, store them in the given directory
+to be mailed later using
+.Nm ctm_dequeue .
+This feature allows the mailing of large deltas to be spread out over
+hours or even days to limit the impact on recipients with limited network
+bandwidth or small mail spool areas.
+.El
+.Pp
+.Ar ctm-delta
+is the delta to be sent, and
+.Ar mail-alias
+is the mailing list to send the delta to.
+The mail messages are sent using
+.Xr sendmail 8 .
+.Pp
+Command line arguments for
+.Nm ctm_dequeue :
+.Bl -tag -width indent
+.It Fl l Ar log
+Instead of appearing on
+.Em stderr ,
+error diagnostics and informational messages (other than command line errors)
+are time stamped and written to the file
+.Em log .
+.It Fl n Ar numchunks
+Limit the number of mail messages that
+.Nm ctm_dequeue
+will send per run. By default,
+.Nm ctm_dequeue
+will send one mail message per run.
+.El
+.Pp
+.Ar queuedir
+is the directory containing the mail messages stored by
+.Nm ctm_smail .
+Up to
+.Ar numchunks
+mail messages will be sent in each run. The recipient mailing list is already
+encoded in the queued files.
+.Pp
+It is safe to run
+.Nm ctm_dequeue
+while
+.Nm ctm_smail
+is adding entries to the queue, or even to run
+.Nm ctm_smail
+multiple times concurrently, but a separate queue directory should be used
+for each tree being distributed. This is because entries are served in
+alphabetical order, and one tree will be unfairly serviced before any others,
+based on the delta names, not delta creation times.
+.Pp
+Command line arguments for
+.Nm ctm_rmail :
+.Bl -tag -width indent
+.It Fl l Ar log
+Instead of appearing on
+.Em stderr ,
+error diagnostics and informational messages (other than command line errors)
+are time stamped and written to the file
+.Em log .
+.It Fl p Ar piecedir
+Collect pieces of deltas in this directory. Each piece corresponds to a
+single mail message. Pieces are removed when complete deltas are built.
+If this flag is not given, no input files will be read, but completed
+deltas may still be applied with
+.Xr ctm
+if the
+.Fl b
+flag is given.
+.It Fl d Ar deltadir
+Collect completed deltas in this directory. Deltas are built from one or
+more pieces when all pieces are present.
+.It Fl b Ar basedir
+Apply any completed deltas to this source tree. If this flag is not given,
+deltas will be stored, but not applied. The user may then apply the deltas
+manually, or by using
+.Nm ctm_rmail
+without the
+.Fl p
+flag.
+Deltas will not be applied if they do not match the
+.Li .ctm_status
+file in
+.Ar basedir
+(or if
+.Li .ctm_status
+does not exist).
+.It Fl D
+Delete deltas after successful application by
+.Xr ctm .
+It is probably a good idea to avoid this flag (and keep all the deltas) as
+.Xr ctm
+has the ability to recover small groups of files from a full set of deltas.
+.It Fl f
+Fork and execute in the background while applying deltas with
+.Xr ctm .
+This is useful when automatically invoking
+.Nm ctm_rmail
+from
+.Xr sendmail
+because
+.Xr ctm
+can take a very long time to complete, causing other people's mail to
+be delayed, and can in theory cause spurious
+mail retransmission due to the remote
+.Xr sendmail
+timing out, or even termination of
+.Nm ctm_rmail
+by mail filters such as
+.Xr "MH's"
+.Xr slocal .
+Don't worry about zillions of background
+.Xr ctm
+processes loading your machine, since locking is used to prevent more than one
+.Xr ctm
+invocation at a time.
+.It Fl u
+Pass the
+.Fl u
+flag to the
+.Xr ctm
+command when applying the complete deltas, causing it to set the modification
+time of created and modified files to the CTM delta creation time.
+.It Fl v
+Pass the
+.Fl v
+flag to the
+.Xr ctm
+command when applying the complete deltas, causing a more informative
+output. All
+.Xr ctm
+output appears in the
+.Nm ctm_rmail
+log file.
+.El
+.Pp
+The file arguments (or
+.Em stdin ,
+if there are none) are scanned for delta pieces. Multiple delta pieces
+can be read from a single file, so an entire maildrop can be scanned
+and processed with a single command.
+.Pp
+It is safe to invoke
+.Nm ctm_rmail
+multiple times concurrently (with different input files),
+as might happen when
+.Xr sendmail
+.nh
+is delivering mail asynchronously. This is because locking is used to
+keep things orderly.
+.Sh FILE FORMAT
+Following are the important parts of an actual (very small) delta piece:
+.Bd -literal
+From: owner-src-cur
+To: src-cur
+Subject: ctm-mail src-cur.0003.gz 1/4
+
+CTM_MAIL BEGIN src-cur.0003.gz 1 4
+H4sIAAAAAAACA3VU72/bNhD9bP0VByQoEiyRSZEUSQP9kKTeYCR2gDTdsGFAwB/HRogtG5K8NCj6
+v4+UZSdtUQh6Rz0eee/xaF/dzx8up3/MFlDkBNrGnbttAwyo1pxoRgoiBNX/QJ5d3c9/X8DcPGGo
+lggkPiXngE4W1gUjKPJCYyk5MZRbIqmNW/ASglIFcdwIzTUxaAqhnCPcBqloKEkJVNDMF0Azk+Bo
+dDzzk0Ods/+A5gXv9YyJHjMCtJwQNeESNma7hOmXDRxn
+CTM_MAIL END 61065
+.Ed
+.Pp
+The subject of the message always begins with
+.Dq ctm-mail
+followed by the name of the delta, which piece this is, and how many total
+pieces there are. The data are bracketed by
+.Dq CTM_MAIL BEGIN
+and
+.Dq CTM_MAIL END
+lines, duplicating the information in the subject line, plus a simple checksum.
+.Pp
+If the delta exceeds
+.Ar maxctmsize ,
+then a message like this will be received instead:
+.Bd -literal
+From: owner-src-cur
+To: src-cur
+Subject: ctm-notice src-cur.0999.gz
+
+src-cur.0999.gz is 792843 bytes. The limit is 300000 bytes.
+
+You can retrieve this delta via ftpmail, or your good mate at the university.
+.Ed
+.Pp
+You are then on your own!
+.Sh EXAMPLES
+To send delta 32 of
+.Em src-cur
+to a group of wonderful code hackers known to
+.Xr sendmail
+as
+.Em src-guys ,
+limiting the mail size to roughly 60000 bytes, you could use:
+.Bd -literal -offset indent
+ctm_smail -m 60000 /wherever/it/is/src-cur.0032.gz src-guys
+.Ed
+.Pp
+To decode every
+.Nm ctm-mail
+message in your mailbox, assemble them into complete deltas, then apply
+any deltas built or lying around, you could use:
+.Bd -literal -offset indent
+ctm_rmail -p ~/pieces -d ~/deltas -b /usr/ctm-src-cur $MAIL
+.Ed
+.Pp
+(Note that no messages are deleted by
+.Nm ctm_rmail .
+Any mail reader could be used for that purpose.)
+.Pp
+To create a mail alias called
+.Em receiver-dude
+that will automatically decode and assemble deltas, but not apply them,
+you could put the following lines in your
+.Pa /etc/aliases
+file (assuming the
+.Pa /ctm/tmp
+and
+.Pa /ctm/deltas
+directories and
+.Pa /ctm/log
+file are writable by user
+.Em daemon
+or group
+.Em wheel ) :
+.Bd -literal -offset indent
+receiver-dude: "|ctm_rmail -p /ctm/tmp -d /ctm/deltas -l /ctm/log"
+owner-receiver-dude: real_dude@wherever.you.like
+.Ed
+.Pp
+The second line will catch failures and drop them into your regular mailbox,
+or wherever else you like.
+.Pp
+To apply all the deltas collected, and delete those applied, you could use:
+.Bd -literal -offset indent
+ctm_rmail -D -d /ctm/deltas -b /ctm/src-cur -l /ctm/apply.log
+.Ed
+.Pp
+For maximum flexibility, consider this excerpt from a
+.Xr procmail
+script:
+.Bd -literal -offset indent
+PATH=$HOME/bin:$PATH
+
+:0 w
+* ^Subject: ctm-mail cvs-cur
+| ctm_incoming
+.Ed
+.Pp
+together with the
+shell script
+.Pa ~/bin/ctm_incoming :
+.Bd -literal -offset indent
+#! /bin/sh
+PATH="$HOME/bin:/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin"
+export PATH
+
+cd $HOME/ctm && ctm_rmail -f -p pieces -d deltas -l log -b /ctm
+.Ed
+.Pp
+which will deposit all
+.Xr ctm
+deltas in
+.Pa ~/ctm/deltas ,
+apply them to the tree in
+.Pa /ctm ,
+and drop any failures into your regular mail box.
+Note the
+.Ev PATH
+manipulation in
+.Pa ctm_incoming
+which allows
+.Nm ctm_rmail
+to execute
+.Xr ctm
+on the (non-FreeBSD) machine that this example was taken from.
+.Sh SECURITY
+If you automatically take your mail and pass it to a file tree patcher, you
+might think you are handing the keys to your system to the hackers! Happily,
+the window for mischief is quite small.
+.Nm ctm_rmail
+is careful to write only to the directories given to it (by not believing any
+.Dq /
+characters in the delta name), and the latest
+.Xr ctm
+disallows absolute pathnames and
+.Dq \&\.\.
+in files it manipulates, so the worst you
+could lose are a few source tree files (recoverable from your deltas).
+Since
+.Xr ctm
+requires that a
+.Xr md5
+checksum match before it touches a file, only fellow
+source recipients would be able to generate a fake delta, and they're such
+nice folk that they wouldn't even think of it! :-)
+.Pp
+Even this possibility could be removed by using cryptographic signatures.
+A possible future enhancement would be to use
+.Nm PGP
+to provide a secure wrapper.
+.\" This next request is for sections 1, 6, 7 & 8 only
+.Sh ENVIRONMENT
+If deltas are to be applied then
+.Xr ctm 1
+and
+.Xr gunzip 1
+must be in your
+.Ev PATH .
+.Sh FILES
+.Bl -tag -width indent
+.It Pa QUEUEDIR/*
+Pieces of deltas encoded as mail messages waiting to be sent to the
+mailing list.
+.It Pa PIECEDIR/*
+Pieces of deltas waiting for the rest to arrive.
+.It Pa DELTADIR/*
+Completed deltas.
+.It Pa BASEDIR/.ctm_status
+File containing the name and number of the next delta to be applied to this
+source tree.
+.\" This next request is for sections 1, 6, 7 & 8 only
+.\" (command return values (to shell) and fprintf/stderr type diagnostics)
+.Sh DIAGNOSTICS
+.Nm ctm_smail ,
+.Nm ctm_dequeue
+and
+.Nm ctm_rmail
+return exit status 0 for success, and 1 for various failures.
+.Nm ctm_rmail
+is expected to be called from a mail transfer program, and thus signals
+failure only when the input mail message should be bounced (preferably into
+your regular maildrop, not back to the sender). In short, failure to
+apply a completed delta with
+.Xr ctm
+is not considered an error important enough to bounce the mail, and
+.Nm ctm_rmail
+returns an exit status of 0.
+.Pp
+In normal operation,
+.Nm ctm_smail
+will report messages like:
+.Bd -literal -offset indent
+ctm_smail: src-cur.0250.gz 1/2 sent to src-guys
+.Ed
+.Pp
+or, if queueing,
+.Bd -literal -offset indent
+ctm_smail: src-cur.0250.gz 1/2 queued for src-guys
+.Ed
+.Pp
+.Nm ctm_dequeue
+will report messages like:
+.Bd -literal -offset indent
+ctm_dequeue: src-cur.0250.gz 1/2 sent
+.Ed
+.Pp
+.Nm ctm_rmail
+will report messages like:
+.Bd -literal -offset indent
+ctm_rmail: src-cur.0250.gz 1/2 stored
+ctm_rmail: src-cur.0250.gz 2/2 stored
+ctm_rmail: src-cur.0250.gz complete
+.Ed
+.Pp
+If any of the input files do not contain a valid delta piece,
+.Nm ctm_rmail
+will report:
+.Bd -literal -offset indent
+ctm_rmail: message contains no delta
+.Ed
+.sp \n(Ppu
+and return an exit status of 1. You can use this to redirect wayward messages
+back into your real mailbox if your mail filter goes wonky.
+.Pp
+These messages go to
+.Em stderr
+or to the log file. Messages from
+.Xr ctm
+turn up here too. Error messages should be self explanatory.
+.\" The next request is for sections 2 and 3 error and signal handling only.
+.\" .Sh ERRORS
+.Sh SEE ALSO
+.Xr ctm 1 ,
+.Xr ctm 5
+.\" .Sh STANDARDS
+.\" .Sh HISTORY
+.Sh AUTHOR
+.An Stephen McKay Aq mckay@FreeBSD.org
+.\" .Sh BUGS
+.\" Gosh! No bugs here!
+.\" This message brought to you by the Coalition for More Humour in Man Pages.
+.\"
+.\" $Id: ctm_rmail.1,v 1.11 1997/08/30 11:05:34 jmg Exp $
diff --git a/usr.sbin/ctm/ctm_rmail/ctm_rmail.c b/usr.sbin/ctm/ctm_rmail/ctm_rmail.c
new file mode 100644
index 0000000..6b01922
--- /dev/null
+++ b/usr.sbin/ctm/ctm_rmail/ctm_rmail.c
@@ -0,0 +1,661 @@
+/*
+ * Accept one (or more) ASCII encoded chunks that together make a compressed
+ * CTM delta. Decode them and reconstruct the deltas. Any completed
+ * deltas may be passed to ctm for unpacking.
+ *
+ * Author: Stephen McKay
+ *
+ * NOTICE: This is free software. I hope you get some use from this program.
+ * In return you should think about all the nice people who give away software.
+ * Maybe you should write some free software too.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <ctype.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <limits.h>
+#include "error.h"
+#include "options.h"
+
+#define CTM_STATUS ".ctm_status"
+
+char *piece_dir = NULL; /* Where to store pieces of deltas. */
+char *delta_dir = NULL; /* Where to store completed deltas. */
+char *base_dir = NULL; /* The tree to apply deltas to. */
+int delete_after = 0; /* Delete deltas after ctm applies them. */
+int apply_verbose = 0; /* Run with '-v' */
+int set_time = 0; /* Set the time of the files that is changed. */
+
+void apply_complete(void);
+int read_piece(char *input_file);
+int combine_if_complete(char *delta, int pce, int npieces);
+int combine(char *delta, int npieces, char *dname, char *pname, char *tname);
+int decode_line(char *line, char *out_buf);
+int lock_file(char *name);
+
+/*
+ * If given a '-p' flag, read encoded delta pieces from stdin or file
+ * arguments, decode them and assemble any completed deltas. If given
+ * a '-b' flag, pass any completed deltas to 'ctm' for application to
+ * the source tree. The '-d' flag is mandatory, but either of '-p' or
+ * '-b' can be omitted. If given the '-l' flag, notes and errors will
+ * be timestamped and written to the given file.
+ *
+ * Exit status is 0 for success or 1 for indigestible input. That is,
+ * 0 means the encode input pieces were decoded and stored, and 1 means
+ * some input was discarded. If a delta fails to apply, this won't be
+ * reflected in the exit status. In this case, the delta is left in
+ * 'deltadir'.
+ */
+int
+main(int argc, char **argv)
+ {
+ char *log_file = NULL;
+ int status = 0;
+ int fork_ctm = 0;
+
+ err_prog_name(argv[0]);
+
+ OPTIONS("[-Dfuv] [-p piecedir] [-d deltadir] [-b basedir] [-l log] [file ...]")
+ FLAG('D', delete_after)
+ FLAG('f', fork_ctm)
+ FLAG('u', set_time)
+ FLAG('v', apply_verbose)
+ STRING('p', piece_dir)
+ STRING('d', delta_dir)
+ STRING('b', base_dir)
+ STRING('l', log_file)
+ ENDOPTS
+
+ if (delta_dir == NULL)
+ usage();
+
+ if (piece_dir == NULL && (base_dir == NULL || argc > 1))
+ usage();
+
+ if (log_file != NULL)
+ err_set_log(log_file);
+
+ /*
+ * Digest each file in turn, or just stdin if no files were given.
+ */
+ if (argc <= 1)
+ {
+ if (piece_dir != NULL)
+ status = read_piece(NULL);
+ }
+ else
+ {
+ while (*++argv != NULL)
+ status |= read_piece(*argv);
+ }
+
+ /*
+ * Maybe it's time to look for and apply completed deltas with ctm.
+ *
+ * Shall we report back to sendmail immediately, and let a child do
+ * the work? Sendmail will be waiting for us to complete, delaying
+ * other mail, and possibly some intermediate process (like MH slocal)
+ * will terminate us if we take too long!
+ *
+ * If fork() fails, it's unlikely we'll be able to run ctm, so give up.
+ * Also, the child exit status is unimportant.
+ */
+ if (base_dir != NULL)
+ if (!fork_ctm || fork() == 0)
+ apply_complete();
+
+ return status;
+ }
+
+
+/*
+ * Construct the file name of a piece of a delta.
+ */
+#define mk_piece_name(fn,d,p,n) \
+ sprintf((fn), "%s/%s+%03d-%03d", piece_dir, (d), (p), (n))
+
+/*
+ * Construct the file name of an assembled delta.
+ */
+#define mk_delta_name(fn,d) \
+ sprintf((fn), "%s/%s", delta_dir, (d))
+
+/*
+ * If the next required delta is now present, let ctm lunch on it and any
+ * contiguous deltas.
+ */
+void
+apply_complete()
+ {
+ int i, dn;
+ int lfd;
+ FILE *fp, *ctm;
+ struct stat sb;
+ char class[20];
+ char delta[30];
+ char junk[2];
+ char fname[PATH_MAX];
+ char here[PATH_MAX];
+ char buf[PATH_MAX*2];
+
+ /*
+ * Grab a lock on the ctm mutex file so that we can be sure we are
+ * working alone, not fighting another ctm_rmail!
+ */
+ strcpy(fname, delta_dir);
+ strcat(fname, "/.mutex_apply");
+ if ((lfd = lock_file(fname)) < 0)
+ return;
+
+ /*
+ * Find out which delta ctm needs next.
+ */
+ sprintf(fname, "%s/%s", base_dir, CTM_STATUS);
+ if ((fp = fopen(fname, "r")) == NULL)
+ {
+ close(lfd);
+ return;
+ }
+
+ i = fscanf(fp, "%s %d %c", class, &dn, junk);
+ fclose(fp);
+ if (i != 2)
+ {
+ close(lfd);
+ return;
+ }
+
+ /*
+ * We might need to convert the delta filename to an absolute pathname.
+ */
+ here[0] = '\0';
+ if (delta_dir[0] != '/')
+ {
+ getcwd(here, sizeof(here)-1);
+ i = strlen(here) - 1;
+ if (i >= 0 && here[i] != '/')
+ {
+ here[++i] = '/';
+ here[++i] = '\0';
+ }
+ }
+
+ /*
+ * Keep applying deltas until we run out or something bad happens.
+ */
+ for (;;)
+ {
+ sprintf(delta, "%s.%04d.gz", class, ++dn);
+ mk_delta_name(fname, delta);
+
+ if (stat(fname, &sb) < 0)
+ break;
+
+ sprintf(buf, "(cd %s && ctm %s%s%s%s) 2>&1", base_dir,
+ set_time ? "-u " : "",
+ apply_verbose ? "-v " : "", here, fname);
+ if ((ctm = popen(buf, "r")) == NULL)
+ {
+ err("ctm failed to apply %s", delta);
+ break;
+ }
+
+ while (fgets(buf, sizeof(buf), ctm) != NULL)
+ {
+ i = strlen(buf) - 1;
+ if (i >= 0 && buf[i] == '\n')
+ buf[i] = '\0';
+ err("ctm: %s", buf);
+ }
+
+ if (pclose(ctm) != 0)
+ {
+ err("ctm failed to apply %s", delta);
+ break;
+ }
+
+ if (delete_after)
+ unlink(fname);
+
+ err("%s applied%s", delta, delete_after ? " and deleted" : "");
+ }
+
+ /*
+ * Closing the lock file clears the lock.
+ */
+ close(lfd);
+ }
+
+
+/*
+ * This cheap plastic checksum effectively rotates our checksum-so-far
+ * left one, then adds the character. We only want 16 bits of it, and
+ * don't care what happens to the rest. It ain't much, but it's small.
+ */
+#define add_ck(sum,x) \
+ ((sum) += ((x)&0xff) + (sum) + (((sum)&0x8000) ? 1 : 0))
+
+
+/*
+ * Decode the data between BEGIN and END, and stash it in the staging area.
+ * Multiple pieces can be present in a single file, bracketed by BEGIN/END.
+ * If we have all pieces of a delta, combine them. Returns 0 on success,
+ * and 1 for any sort of failure.
+ */
+int
+read_piece(char *input_file)
+ {
+ int status = 0;
+ FILE *ifp, *ofp = 0;
+ int decoding = 0;
+ int got_one = 0;
+ int line_no = 0;
+ int i, n;
+ int pce, npieces;
+ unsigned claimed_cksum;
+ unsigned short cksum = 0;
+ char out_buf[200];
+ char line[200];
+ char delta[30];
+ char pname[PATH_MAX];
+ char tname[PATH_MAX];
+ char junk[2];
+
+ ifp = stdin;
+ if (input_file != NULL && (ifp = fopen(input_file, "r")) == NULL)
+ {
+ err("cannot open '%s' for reading", input_file);
+ return 1;
+ }
+
+ while (fgets(line, sizeof(line), ifp) != NULL)
+ {
+ line_no++;
+
+ /*
+ * Remove all trailing white space.
+ */
+ i = strlen(line) - 1;
+ while (i > 0 && isspace(line[i]))
+ line[i--] = '\0';
+
+ /*
+ * Look for the beginning of an encoded piece.
+ */
+ if (!decoding)
+ {
+ char *s;
+
+ if (sscanf(line, "CTM_MAIL BEGIN %s %d %d %c",
+ delta, &pce, &npieces, junk) != 3)
+ continue;
+
+ while ((s = strchr(delta, '/')) != NULL)
+ *s = '_';
+
+ got_one++;
+ strcpy(tname, piece_dir);
+ strcat(tname, "/p.XXXXXX");
+ if (mktemp(tname) == NULL)
+ {
+ err("*mktemp: '%s'", tname);
+ status++;
+ continue;
+ }
+ if ((ofp = fopen(tname, "w")) == NULL)
+ {
+ err("cannot open '%s' for writing", tname);
+ status++;
+ continue;
+ }
+
+ cksum = 0xffff;
+ decoding++;
+ continue;
+ }
+
+ /*
+ * We are decoding. Stop if we see the end flag.
+ */
+ if (sscanf(line, "CTM_MAIL END %d %c", &claimed_cksum, junk) == 1)
+ {
+ int e;
+
+ decoding = 0;
+
+ fflush(ofp);
+ e = ferror(ofp);
+ fclose(ofp);
+
+ if (e)
+ err("error writing %s", tname);
+
+ if (cksum != claimed_cksum)
+ err("checksum: read %d, calculated %d", claimed_cksum, cksum);
+
+ if (e || cksum != claimed_cksum)
+ {
+ err("%s %d/%d discarded", delta, pce, npieces);
+ unlink(tname);
+ status++;
+ continue;
+ }
+
+ mk_piece_name(pname, delta, pce, npieces);
+ if (rename(tname, pname) < 0)
+ {
+ err("*rename: '%s' to '%s'", tname, pname);
+ err("%s %d/%d lost!", delta, pce, npieces);
+ unlink(tname);
+ status++;
+ continue;
+ }
+
+ err("%s %d/%d stored", delta, pce, npieces);
+
+ if (!combine_if_complete(delta, pce, npieces))
+ status++;
+ continue;
+ }
+
+ /*
+ * Must be a line of encoded data. Decode it, sum it, and save it.
+ */
+ n = decode_line(line, out_buf);
+ if (n <= 0)
+ {
+ err("line %d: illegal character: '%c'", line_no, line[-n]);
+ err("%s %d/%d discarded", delta, pce, npieces);
+
+ fclose(ofp);
+ unlink(tname);
+
+ status++;
+ decoding = 0;
+ continue;
+ }
+
+ for (i = 0; i < n; i++)
+ add_ck(cksum, out_buf[i]);
+
+ fwrite(out_buf, sizeof(char), n, ofp);
+ }
+
+ if (decoding)
+ {
+ err("truncated file");
+ err("%s %d/%d discarded", delta, pce, npieces);
+
+ fclose(ofp);
+ unlink(tname);
+
+ status++;
+ }
+
+ if (ferror(ifp))
+ {
+ err("error reading %s", input_file == NULL ? "stdin" : input_file);
+ status++;
+ }
+
+ if (input_file != NULL)
+ fclose(ifp);
+
+ if (!got_one)
+ {
+ err("message contains no delta");
+ status++;
+ }
+
+ return (status != 0);
+ }
+
+
+/*
+ * Put the pieces together to form a delta, if they are all present.
+ * Returns 1 on success (even if we didn't do anything), and 0 on failure.
+ */
+int
+combine_if_complete(char *delta, int pce, int npieces)
+ {
+ int i, e;
+ int lfd;
+ struct stat sb;
+ char pname[PATH_MAX];
+ char dname[PATH_MAX];
+ char tname[PATH_MAX];
+
+ /*
+ * We can probably just rename() it into place if it is a small delta.
+ */
+ if (npieces == 1)
+ {
+ mk_delta_name(dname, delta);
+ mk_piece_name(pname, delta, 1, 1);
+ if (rename(pname, dname) == 0)
+ {
+ err("%s complete", delta);
+ return 1;
+ }
+ }
+
+ /*
+ * Grab a lock on the reassembly mutex file so that we can be sure we are
+ * working alone, not fighting another ctm_rmail!
+ */
+ strcpy(tname, delta_dir);
+ strcat(tname, "/.mutex_build");
+ if ((lfd = lock_file(tname)) < 0)
+ return 0;
+
+ /*
+ * Are all of the pieces present? Of course the current one is,
+ * unless all pieces are missing because another ctm_rmail has
+ * processed them already.
+ */
+ for (i = 1; i <= npieces; i++)
+ {
+ if (i == pce)
+ continue;
+ mk_piece_name(pname, delta, i, npieces);
+ if (stat(pname, &sb) < 0)
+ {
+ close(lfd);
+ return 1;
+ }
+ }
+
+ /*
+ * Stick them together. Let combine() use our file name buffers, since
+ * we're such good buddies. :-)
+ */
+ e = combine(delta, npieces, dname, pname, tname);
+ close(lfd);
+ return e;
+ }
+
+
+/*
+ * Put the pieces together to form a delta.
+ * Returns 1 on success, and 0 on failure.
+ * Note: dname, pname, and tname are room for some file names that just
+ * happened to by lying around in the calling routine. Waste not, want not!
+ */
+int
+combine(char *delta, int npieces, char *dname, char *pname, char *tname)
+ {
+ FILE *dfp, *pfp;
+ int i, n, e;
+ char buf[BUFSIZ];
+
+ strcpy(tname, delta_dir);
+ strcat(tname, "/d.XXXXXX");
+ if (mktemp(tname) == NULL)
+ {
+ err("*mktemp: '%s'", tname);
+ return 0;
+ }
+ if ((dfp = fopen(tname, "w")) == NULL)
+ {
+ err("cannot open '%s' for writing", tname);
+ return 0;
+ }
+
+ /*
+ * Reconstruct the delta by reading each piece in order.
+ */
+ for (i = 1; i <= npieces; i++)
+ {
+ mk_piece_name(pname, delta, i, npieces);
+ if ((pfp = fopen(pname, "r")) == NULL)
+ {
+ err("cannot open '%s' for reading", pname);
+ fclose(dfp);
+ unlink(tname);
+ return 0;
+ }
+ while ((n = fread(buf, sizeof(char), sizeof(buf), pfp)) != 0)
+ fwrite(buf, sizeof(char), n, dfp);
+ e = ferror(pfp);
+ fclose(pfp);
+ if (e)
+ {
+ err("error reading '%s'", pname);
+ fclose(dfp);
+ unlink(tname);
+ return 0;
+ }
+ }
+ fflush(dfp);
+ e = ferror(dfp);
+ fclose(dfp);
+ if (e)
+ {
+ err("error writing '%s'", tname);
+ unlink(tname);
+ return 0;
+ }
+
+ mk_delta_name(dname, delta);
+ if (rename(tname, dname) < 0)
+ {
+ err("*rename: '%s' to '%s'", tname, dname);
+ unlink(tname);
+ return 0;
+ }
+
+ /*
+ * Throw the pieces away.
+ */
+ for (i = 1; i <= npieces; i++)
+ {
+ mk_piece_name(pname, delta, i, npieces);
+ if (unlink(pname) < 0)
+ err("*unlink: '%s'", pname);
+ }
+
+ err("%s complete", delta);
+ return 1;
+ }
+
+
+/*
+ * MIME BASE64 decode table.
+ */
+static unsigned char from_b64[0x80] =
+ {
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0xff, 0xff, 0x3e, 0xff, 0xff, 0xff, 0x3f,
+ 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b,
+ 0x3c, 0x3d, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06,
+ 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
+ 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
+ 0x17, 0x18, 0x19, 0xff, 0xff, 0xff, 0xff, 0xff,
+ 0xff, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20,
+ 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
+ 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30,
+ 0x31, 0x32, 0x33, 0xff, 0xff, 0xff, 0xff, 0xff
+ };
+
+
+/*
+ * Decode a line of ASCII into binary. Returns the number of bytes in
+ * the output buffer, or < 0 on indigestable input. Error output is
+ * the negative of the index of the inedible character.
+ */
+int
+decode_line(char *line, char *out_buf)
+ {
+ unsigned char *ip = (unsigned char *)line;
+ unsigned char *op = (unsigned char *)out_buf;
+ unsigned long bits;
+ unsigned x;
+
+ for (;;)
+ {
+ if (*ip >= 0x80 || (x = from_b64[*ip]) >= 0x40)
+ break;
+ bits = x << 18;
+ ip++;
+ if (*ip < 0x80 && (x = from_b64[*ip]) < 0x40)
+ {
+ bits |= x << 12;
+ *op++ = bits >> 16;
+ ip++;
+ if (*ip < 0x80 && (x = from_b64[*ip]) < 0x40)
+ {
+ bits |= x << 6;
+ *op++ = bits >> 8;
+ ip++;
+ if (*ip < 0x80 && (x = from_b64[*ip]) < 0x40)
+ {
+ bits |= x;
+ *op++ = bits;
+ ip++;
+ }
+ }
+ }
+ }
+
+ if (*ip == '\0' || *ip == '\n')
+ return op - (unsigned char *)out_buf;
+ else
+ return -(ip - (unsigned char *)line);
+ }
+
+
+/*
+ * Create and lock the given file.
+ *
+ * Clearing the lock is as simple as closing the file descriptor we return.
+ */
+int
+lock_file(char *name)
+ {
+ int lfd;
+
+ if ((lfd = open(name, O_WRONLY|O_CREAT, 0600)) < 0)
+ {
+ err("*open: '%s'", name);
+ return -1;
+ }
+ if (flock(lfd, LOCK_EX) < 0)
+ {
+ close(lfd);
+ err("*flock: '%s'", name);
+ return -1;
+ }
+ return lfd;
+ }
diff --git a/usr.sbin/ctm/ctm_rmail/error.c b/usr.sbin/ctm/ctm_rmail/error.c
new file mode 100644
index 0000000..724b117
--- /dev/null
+++ b/usr.sbin/ctm/ctm_rmail/error.c
@@ -0,0 +1,97 @@
+/*
+ * Routines for logging error messages or other informative messages.
+ *
+ * Log messages can easily contain the program name, a time stamp, system
+ * error messages, and arbitrary printf-style strings, and can be directed
+ * to stderr or a log file.
+ *
+ * Author: Stephen McKay
+ *
+ * NOTICE: This is free software. I hope you get some use from this program.
+ * In return you should think about all the nice people who give away software.
+ * Maybe you should write some free software too.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <time.h>
+#include <errno.h>
+#include "error.h"
+
+static FILE *error_fp = NULL;
+static char *prog = NULL;
+
+
+/*
+ * Log errors to the given file.
+ */
+void
+err_set_log(char *log_file)
+ {
+ FILE *fp;
+
+ if ((fp = fopen(log_file, "a")) == NULL)
+ err("cannot log to '%s'", log_file);
+ else
+ error_fp = fp;
+ }
+
+
+/*
+ * Set the error prefix if not logging to a file.
+ */
+void
+err_prog_name(char *name)
+ {
+ if ((prog = strrchr(name, '/')) == NULL)
+ prog = name;
+ else
+ prog++;
+ }
+
+
+/*
+ * Log an error.
+ *
+ * A leading '*' in the message format means we want the system errno
+ * decoded and appended.
+ */
+void
+err(char *fmt, ...)
+ {
+ va_list ap;
+ time_t now;
+ struct tm *tm;
+ FILE *fp;
+ int x = errno;
+ int want_errno;
+
+ if ((fp = error_fp) == NULL)
+ {
+ fp = stderr;
+ if (prog != NULL)
+ fprintf(fp, "%s: ", prog);
+ }
+ else
+ {
+ time(&now);
+ tm = localtime(&now);
+ fprintf(fp, "%04d-%02d-%02d %02d:%02d ", tm->tm_year+1900,
+ tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min);
+ }
+
+ want_errno = 0;
+ if (*fmt == '*')
+ want_errno++, fmt++;
+
+ va_start(ap, fmt);
+ vfprintf(fp, fmt, ap);
+ va_end(ap);
+
+ if (want_errno)
+ fprintf(fp, ": %s", strerror(x));
+
+ fprintf(fp, "\n");
+ fflush(fp);
+ }
diff --git a/usr.sbin/ctm/ctm_rmail/error.h b/usr.sbin/ctm/ctm_rmail/error.h
new file mode 100644
index 0000000..b8bc452
--- /dev/null
+++ b/usr.sbin/ctm/ctm_rmail/error.h
@@ -0,0 +1,3 @@
+extern void err_set_log(char *log_file);
+extern void err_prog_name(char *name);
+extern void err(char *fmt, ...);
diff --git a/usr.sbin/ctm/ctm_rmail/options.h b/usr.sbin/ctm/ctm_rmail/options.h
new file mode 100644
index 0000000..18b844c
--- /dev/null
+++ b/usr.sbin/ctm/ctm_rmail/options.h
@@ -0,0 +1,137 @@
+/*
+ * Macros for processing command arguments.
+ *
+ * Conforms closely to the command option requirements of intro(1) in System V
+ * and intro(C) in Xenix.
+ *
+ * A command consists of: cmdname [ options ] [ cmdarguments ]
+ *
+ * Options consist of a leading dash '-' and a flag letter. An argument may
+ * follow optionally preceded by white space.
+ * Options without arguments may be grouped behind a single dash.
+ * A dash on its own is interpreted as the end of the options and is retained
+ * as a command argument.
+ * A double dash '--' is interpreted as the end of the options and is discarded.
+ *
+ * For example:
+ * zap -xz -f flame -q34 -- -x
+ *
+ * where zap.c contains the following in main():
+ *
+ * OPTIONS("[-xz] [-q queue-id] [-f dump-file] user")
+ * FLAG('x', xecute)
+ * FLAG('z', zot)
+ * STRING('f', file)
+ * fp = fopen(file, "w");
+ * NUMBER('q', queue)
+ * ENDOPTS
+ *
+ * Results in:
+ * xecute = 1
+ * zot = 1
+ * file = "flame"
+ * fp = fopen("flame", "w")
+ * queue = 34
+ * argc = 2
+ * argv[0] = "zap"
+ * argv[1] = "-x"
+ *
+ * Should the user enter unknown flags or leave out required arguments,
+ * the message:
+ *
+ * Usage: zap [-xz] [-q queue-id] [-f dump-file] user
+ *
+ * will be printed. This message can be printed by calling pusage(), or
+ * usage(). usage() will also cause program termination with exit code 1.
+ *
+ * Author: Stephen McKay, February 1991
+ *
+ * Based on recollection of the original options.h produced at the University
+ * of Queensland by Ross Patterson (and possibly others).
+ */
+
+static char *O_usage;
+static char *O_name;
+extern long atol();
+
+void
+pusage()
+ {
+ /*
+ * Avoid gratuitously loading stdio.
+ */
+ write(2, "Usage: ", 7);
+ write(2, O_name, strlen(O_name));
+ write(2, " ", 1);
+ write(2, O_usage, strlen(O_usage));
+ write(2, "\n", 1);
+ }
+
+#define usage() (pusage(), exit(1))
+
+#define OPTIONS(usage_msg) \
+ { \
+ char O_cont; \
+ O_usage = (usage_msg); \
+ O_name = argv[0]; \
+ while (*++argv && **argv == '-') \
+ { \
+ if ((*argv)[1] == '\0') \
+ break; \
+ argc--; \
+ if ((*argv)[1] == '-' && (*argv)[2] == '\0') \
+ { \
+ argv++; \
+ break; \
+ } \
+ O_cont = 1; \
+ while (O_cont) \
+ switch (*++*argv) \
+ { \
+ default: \
+ case '-': \
+ usage(); \
+ case '\0': \
+ O_cont = 0;
+
+#define FLAG(x,flag) \
+ break; \
+ case (x): \
+ (flag) = 1;
+
+#define CHAR(x,ch) \
+ break; \
+ case (x): \
+ O_cont = 0; \
+ if (*++*argv == '\0' && (--argc, *++argv == 0)) \
+ usage(); \
+ (ch) = **argv;
+
+#define NUMBER(x,n) \
+ break; \
+ case (x): \
+ O_cont = 0; \
+ if (*++*argv == '\0' && (--argc, *++argv == 0)) \
+ usage(); \
+ (n) = atol(*argv);
+
+#define STRING(x,str) \
+ break; \
+ case (x): \
+ O_cont = 0; \
+ if (*++*argv == '\0' && (--argc, *++argv == 0)) \
+ usage(); \
+ (str) = *argv;
+
+#define SUFFIX(x,str) \
+ break; \
+ case (x): \
+ (str) = ++*argv; \
+ O_cont = 0;
+
+#define ENDOPTS \
+ break; \
+ } \
+ } \
+ *--argv = O_name; \
+ }
diff --git a/usr.sbin/ctm/ctm_smail/Makefile b/usr.sbin/ctm/ctm_smail/Makefile
new file mode 100644
index 0000000..e9eb846
--- /dev/null
+++ b/usr.sbin/ctm/ctm_smail/Makefile
@@ -0,0 +1,7 @@
+PROG= ctm_smail
+SRCS= ctm_smail.c error.c
+NOMAN= 1
+CFLAGS+= -Wall -I${.CURDIR}/../ctm_rmail
+.PATH: ${.CURDIR}/../ctm_rmail
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/ctm/ctm_smail/ctm_smail.c b/usr.sbin/ctm/ctm_smail/ctm_smail.c
new file mode 100644
index 0000000..afd7cdd
--- /dev/null
+++ b/usr.sbin/ctm/ctm_smail/ctm_smail.c
@@ -0,0 +1,469 @@
+/*
+ * Send a compressed CTM delta to a recipient mailing list by encoding it
+ * in safe ASCII characters, in mailer-friendly chunks, and passing them
+ * to sendmail. Optionally, the chunks can be queued to be sent later by
+ * ctm_dequeue in controlled bursts. The encoding is almost the same as
+ * MIME BASE64, and is protected by a simple checksum.
+ *
+ * Author: Stephen McKay
+ *
+ * NOTICE: This is free software. I hope you get some use from this program.
+ * In return you should think about all the nice people who give away software.
+ * Maybe you should write some free software too.
+ *
+ * $Id$
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <paths.h>
+#include <limits.h>
+#include "error.h"
+#include "options.h"
+
+#define DEF_MAX_MSG 64000 /* Default maximum mail msg minus headers. */
+
+#define LINE_LENGTH 76 /* Chars per encoded line. Divisible by 4. */
+
+int chop_and_send_or_queue(FILE *dfp, char *delta, off_t ctm_size,
+ long max_msg_size, char *mail_alias, char *queue_dir);
+int chop_and_send(FILE *dfp, char *delta, long msg_size, int npieces,
+ char *mail_alias);
+int chop_and_queue(FILE *dfp, char *delta, long msg_size, int npieces,
+ char *mail_alias, char *queue_dir);
+void clean_up_queue(char *queue_dir);
+int encode_body(FILE *sm_fp, FILE *delta_fp, long msg_size, unsigned *sum);
+void write_header(FILE *sfp, char *mail_alias, char *delta, int pce,
+ int npieces);
+void write_trailer(FILE *sfp, unsigned sum);
+int apologise(char *delta, off_t ctm_size, long max_ctm_size,
+ char *mail_alias);
+FILE *open_sendmail(void);
+int close_sendmail(FILE *fp);
+
+int
+main(int argc, char **argv)
+ {
+ int status = 0;
+ char *delta_file;
+ char *mail_alias;
+ long max_msg_size = DEF_MAX_MSG;
+ long max_ctm_size = 0;
+ char *log_file = NULL;
+ char *queue_dir = NULL;
+ char *delta;
+ FILE *dfp;
+ struct stat sb;
+
+ err_prog_name(argv[0]);
+
+ OPTIONS("[-l log] [-m maxmsgsize] [-c maxctmsize] [-q queuedir] ctm-delta mail-alias")
+ NUMBER('m', max_msg_size)
+ NUMBER('c', max_ctm_size)
+ STRING('l', log_file)
+ STRING('q', queue_dir)
+ ENDOPTS
+
+ if (argc != 3)
+ usage();
+
+ if (log_file != NULL)
+ err_set_log(log_file);
+
+ delta_file = argv[1];
+ mail_alias = argv[2];
+
+ if ((delta = strrchr(delta_file, '/')) == NULL)
+ delta = delta_file;
+ else
+ delta++;
+
+ if ((dfp = fopen(delta_file, "r")) == NULL || fstat(fileno(dfp), &sb) < 0)
+ {
+ err("*%s", delta_file);
+ exit(1);
+ }
+
+ if (max_ctm_size != 0 && sb.st_size > max_ctm_size)
+ status = apologise(delta, sb.st_size, max_ctm_size, mail_alias);
+ else
+ status = chop_and_send_or_queue(dfp, delta, sb.st_size, max_msg_size,
+ mail_alias, queue_dir);
+
+ fclose(dfp);
+
+ return status;
+ }
+
+
+/*
+ * Carve our CTM delta into pieces, encode them, and send or queue them.
+ * Returns 0 on success, and 1 on failure.
+ */
+int
+chop_and_send_or_queue(FILE *dfp, char *delta, off_t ctm_size,
+ long max_msg_size, char *mail_alias, char *queue_dir)
+ {
+ int npieces;
+ long msg_size;
+ long exp_size;
+ int status;
+
+#undef howmany
+#define howmany(x,y) (((x)+((y)-1)) / (y))
+
+ /*
+ * Work out how many pieces we need, bearing in mind that each piece
+ * grows by 4/3 when encoded. We count the newlines too, but ignore
+ * all mail headers and piece headers. They are a "small" (almost
+ * constant) per message overhead that we make the user worry about. :-)
+ */
+ exp_size = ctm_size * 4 / 3;
+ exp_size += howmany(exp_size, LINE_LENGTH);
+ npieces = howmany(exp_size, max_msg_size);
+ msg_size = howmany(ctm_size, npieces);
+
+#undef howmany
+
+ if (queue_dir == NULL)
+ status = chop_and_send(dfp, delta, msg_size, npieces, mail_alias);
+ else
+ {
+ status = chop_and_queue(dfp, delta, msg_size, npieces, mail_alias,
+ queue_dir);
+ if (status)
+ clean_up_queue(queue_dir);
+ }
+
+ return status;
+ }
+
+
+/*
+ * Carve our CTM delta into pieces, encode them, and send them.
+ * Returns 0 on success, and 1 on failure.
+ */
+int
+chop_and_send(FILE *dfp, char *delta, long msg_size, int npieces,
+ char *mail_alias)
+ {
+ int pce;
+ FILE *sfp;
+ unsigned sum;
+
+ /*
+ * Send each chunk directly to sendmail as it is generated.
+ * No temporary files necessary. If things turn ugly, we just
+ * have to live with the fact the we have sent only part of
+ * the delta.
+ */
+ for (pce = 1; pce <= npieces; pce++)
+ {
+ int read_error;
+
+ if ((sfp = open_sendmail()) == NULL)
+ return 1;
+
+ write_header(sfp, mail_alias, delta, pce, npieces);
+ read_error = encode_body(sfp, dfp, msg_size, &sum);
+ if (!read_error)
+ write_trailer(sfp, sum);
+
+ if (!close_sendmail(sfp) || read_error)
+ return 1;
+
+ err("%s %d/%d sent to %s", delta, pce, npieces, mail_alias);
+ }
+
+ return 0;
+ }
+
+
+/*
+ * Construct the tmp queue file name of a delta piece.
+ */
+#define mk_tmp_name(fn,qd,p) \
+ sprintf((fn), "%s/.%08ld.%03d", (qd), (long)getpid(), (p))
+
+/*
+ * Construct the final queue file name of a delta piece.
+ */
+#define mk_queue_name(fn,qd,d,p,n) \
+ sprintf((fn), "%s/%s+%03d-%03d", (qd), (d), (p), (n))
+
+/*
+ * Carve our CTM delta into pieces, encode them, and queue them.
+ * Returns 0 on success, and 1 on failure.
+ */
+int
+chop_and_queue(FILE *dfp, char *delta, long msg_size, int npieces,
+ char *mail_alias, char *queue_dir)
+ {
+ int pce;
+ FILE *qfp;
+ unsigned sum;
+ char tname[PATH_MAX];
+ char qname[PATH_MAX];
+
+ /*
+ * Store each piece in the queue directory, but under temporary names,
+ * so that they can be deleted without unpleasant consequences if
+ * anything goes wrong. We could easily fill up a disk, for example.
+ */
+ for (pce = 1; pce <= npieces; pce++)
+ {
+ int write_error;
+
+ mk_tmp_name(tname, queue_dir, pce);
+ if ((qfp = fopen(tname, "w")) == NULL)
+ {
+ err("cannot open '%s' for writing", tname);
+ return 1;
+ }
+
+ write_header(qfp, mail_alias, delta, pce, npieces);
+ if (encode_body(qfp, dfp, msg_size, &sum))
+ return 1;
+ write_trailer(qfp, sum);
+
+ fflush(qfp);
+ write_error = ferror(qfp);
+ fclose(qfp);
+ if (write_error)
+ {
+ err("error writing '%s'", tname);
+ return 1;
+ }
+
+ /*
+ * Give the warm success message now, instead of all in a rush
+ * during the rename phase.
+ */
+ err("%s %d/%d queued for %s", delta, pce, npieces, mail_alias);
+ }
+
+ /*
+ * Rename the pieces into place. If an error occurs now, we are
+ * stuffed, but there is no neat way to back out. rename() should
+ * only fail now under extreme circumstances.
+ */
+ for (pce = 1; pce <= npieces; pce++)
+ {
+ mk_tmp_name(tname, queue_dir, pce);
+ mk_queue_name(qname, queue_dir, delta, pce, npieces);
+ if (rename(tname, qname) < 0)
+ {
+ err("*rename: '%s' to '%s'", tname, qname);
+ unlink(tname);
+ }
+ }
+
+ return 0;
+ }
+
+
+/*
+ * There may be temporary files cluttering up the queue directory.
+ */
+void
+clean_up_queue(char *queue_dir)
+ {
+ int pce;
+ char tname[PATH_MAX];
+
+ err("discarding queued delta pieces");
+ for (pce = 1; ; pce++)
+ {
+ mk_tmp_name(tname, queue_dir, pce);
+ if (unlink(tname) < 0)
+ break;
+ }
+ }
+
+
+/*
+ * MIME BASE64 encode table.
+ */
+static char to_b64[0x40] =
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+
+/*
+ * This cheap plastic checksum effectively rotates our checksum-so-far
+ * left one, then adds the character. We only want 16 bits of it, and
+ * don't care what happens to the rest. It ain't much, but it's small.
+ */
+#define add_ck(sum,x) \
+ ((sum) += ((x)&0xff) + (sum) + (((sum)&0x8000) ? 1 : 0))
+
+/*
+ * Encode the body. Use an encoding almost the same as MIME BASE64.
+ *
+ * Characters are read from delta_fp and encoded characters are written
+ * to sm_fp. At most 'msg_size' characters should be read from delta_fp.
+ *
+ * The body consists of lines of up to LINE_LENGTH characters. Each group
+ * of 4 characters encodes 3 input characters. Each output character encodes
+ * 6 bits. Thus 64 different characters are needed in this representation.
+ */
+int
+encode_body(FILE *sm_fp, FILE *delta_fp, long msg_size, unsigned *sum)
+ {
+ unsigned short cksum = 0xffff;
+ unsigned char *ip;
+ char *op;
+ int want, n, i;
+ unsigned char inbuf[LINE_LENGTH*3/4];
+ char outbuf[LINE_LENGTH+1];
+
+ /*
+ * Round up to the nearest line boundary, for the tiniest of gains,
+ * and lots of neatness. :-)
+ */
+ msg_size += (LINE_LENGTH*3/4) - 1;
+ msg_size -= msg_size % (LINE_LENGTH*3/4);
+
+ while (msg_size > 0)
+ {
+ want = (msg_size < sizeof(inbuf)) ? msg_size : sizeof(inbuf);
+ if ((n = fread(inbuf, sizeof(char), want, delta_fp)) == 0)
+ break;
+ msg_size -= n;
+
+ for (i = 0; i < n; i++)
+ add_ck(cksum, inbuf[i]);
+
+ /*
+ * Produce a line of encoded data. Every line length will be a
+ * multiple of 4, except for, perhaps, the last line.
+ */
+ ip = inbuf;
+ op = outbuf;
+ while (n >= 3)
+ {
+ *op++ = to_b64[ip[0] >> 2];
+ *op++ = to_b64[(ip[0] << 4 & 0x3f) | ip[1] >> 4];
+ *op++ = to_b64[(ip[1] << 2 & 0x3f) | ip[2] >> 6];
+ *op++ = to_b64[ip[2] & 0x3f];
+ ip += 3;
+ n -= 3;
+ }
+ if (n > 0)
+ {
+ *op++ = to_b64[ip[0] >> 2];
+ *op++ = to_b64[(ip[0] << 4 & 0x3f) | ip[1] >> 4];
+ if (n >= 2)
+ *op++ = to_b64[ip[1] << 2 & 0x3f];
+ }
+ *op++ = '\n';
+ fwrite(outbuf, sizeof(char), op - outbuf, sm_fp);
+ }
+
+ if (ferror(delta_fp))
+ {
+ err("error reading input file.");
+ return 1;
+ }
+
+ *sum = cksum;
+
+ return 0;
+ }
+
+
+/*
+ * Write the mail header and data header.
+ */
+void
+write_header(FILE *sfp, char *mail_alias, char *delta, int pce, int npieces)
+ {
+ fprintf(sfp, "From: owner-%s\n", mail_alias);
+ fprintf(sfp, "To: %s\n", mail_alias);
+ fprintf(sfp, "Subject: ctm-mail %s %d/%d\n\n", delta, pce, npieces);
+
+ fprintf(sfp, "CTM_MAIL BEGIN %s %d %d\n", delta, pce, npieces);
+ }
+
+
+/*
+ * Write the data trailer.
+ */
+void
+write_trailer(FILE *sfp, unsigned sum)
+ {
+ fprintf(sfp, "CTM_MAIL END %ld\n", (long)sum);
+ }
+
+
+/*
+ * We're terribly sorry, but the delta is too big to send.
+ * Returns 0 on success, 1 on failure.
+ */
+int
+apologise(char *delta, off_t ctm_size, long max_ctm_size, char *mail_alias)
+ {
+ FILE *sfp;
+
+ sfp = open_sendmail();
+ if (sfp == NULL)
+ return 1;
+
+ fprintf(sfp, "From: owner-%s\n", mail_alias);
+ fprintf(sfp, "To: %s\n", mail_alias);
+ fprintf(sfp, "Subject: ctm-notice %s\n\n", delta);
+
+ fprintf(sfp, "%s is %ld bytes. The limit is %ld bytes.\n\n", delta,
+ (long)ctm_size, max_ctm_size);
+ fprintf(sfp, "You can retrieve this delta via ftpmail, "
+ "or your good mate at the university.\n");
+
+ if (!close_sendmail(sfp))
+ return 1;
+
+ return 0;
+ }
+
+
+/*
+ * Start a pipe to sendmail. Sendmail will decode the destination
+ * from the message contents.
+ */
+FILE *
+open_sendmail()
+ {
+ FILE *fp;
+ char buf[100];
+
+ sprintf(buf, "%s -odq -t", _PATH_SENDMAIL);
+ if ((fp = popen(buf, "w")) == NULL)
+ err("cannot start sendmail");
+ return fp;
+ }
+
+
+/*
+ * Close a pipe to sendmail. Sendmail will then do its bit.
+ * Return 1 on success, 0 on failure.
+ */
+int
+close_sendmail(FILE *fp)
+ {
+ int status;
+
+ fflush(fp);
+ if (ferror(fp))
+ {
+ err("error writing to sendmail");
+ return 0;
+ }
+
+ if ((status = pclose(fp)) != 0)
+ err("sendmail failed with status %d", status);
+
+ return (status == 0);
+ }
diff --git a/usr.sbin/ctm/mkCTM/Makefile b/usr.sbin/ctm/mkCTM/Makefile
new file mode 100644
index 0000000..4e67fcd
--- /dev/null
+++ b/usr.sbin/ctm/mkCTM/Makefile
@@ -0,0 +1,25 @@
+
+PROG= mkctm
+SRCS= mkctm.c
+LDADD= -lmd
+NOMAN= 1
+CFLAGS= -g -Wall
+
+test: mkctm
+ rm -f tst.out*
+ time ./mkctm -v -v /3c/210src /a/r1/usr/src \
+ 2>a | md5 -p > /a/tst.out
+ ls -l /a/tst.out
+ gzip -9 -v /a/tst.out
+ ls -l /a/tst.out.gz
+ # cd /usr/src/release && ctm -c -v -v ${.CURDIR}/tst.out
+
+test1: mkctm
+ rm -f tst.out*
+ time ./mkctm -v -v /3c/210src /home/ncvs/src \
+ 2> b | md5 -p > /a/tst2.out
+ ls -l /a/tst2.out
+ gzip -9 -v /a/tst2.out
+ ls -l /a/tst2.out.gz
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/ctm/mkCTM/ctm_conf.cvs-cur b/usr.sbin/ctm/mkCTM/ctm_conf.cvs-cur
new file mode 100644
index 0000000..fbb5bf2
--- /dev/null
+++ b/usr.sbin/ctm/mkCTM/ctm_conf.cvs-cur
@@ -0,0 +1,9 @@
+#!/usr/local/bin/tclsh
+
+set CTMname cvs-cur
+set CTMref /home/ncvs
+set CTMignore {^src/secure|^src/eBones|^src/kerberosIV|^CVSROOT/val-tags$|CVSROOT/\\.#}
+set CTMbogus {\\.core$|/#cvs|/\\.#}
+set CTMmail ctm-cvs-cur-fast@freebsd.org
+set CTMqueuemail ctm-cvs-cur@freebsd.org
+set CTMqueue /home/ctm/queue/ctm-cvs-cur
diff --git a/usr.sbin/ctm/mkCTM/ctm_conf.gnats b/usr.sbin/ctm/mkCTM/ctm_conf.gnats
new file mode 100644
index 0000000..c2223f0
--- /dev/null
+++ b/usr.sbin/ctm/mkCTM/ctm_conf.gnats
@@ -0,0 +1,8 @@
+#!/usr/local/bin/tclsh
+
+#set CTMfirst 1
+set CTMname gnats
+set CTMref /home/gnats
+set CTMdest $CTMSW/../CTM-pub/$CTMname
+set CTMignore {\\.lock$}
+set CTMmail ctm-gnats@freebsd.org
diff --git a/usr.sbin/ctm/mkCTM/ctm_conf.ports-cur b/usr.sbin/ctm/mkCTM/ctm_conf.ports-cur
new file mode 100644
index 0000000..b9d3d13
--- /dev/null
+++ b/usr.sbin/ctm/mkCTM/ctm_conf.ports-cur
@@ -0,0 +1,7 @@
+#!/usr/local/bin/tclsh
+
+set CTMname ports-cur
+set CTMref /usr/ports
+set CTMignore {/CVS$|/CVS/|^distfiles}
+set CTMbogus {\\.core$|/#cvs|/\\.#}
+set CTMmail ctm-ports-cur@freebsd.org
diff --git a/usr.sbin/ctm/mkCTM/ctm_conf.smp-cur b/usr.sbin/ctm/mkCTM/ctm_conf.smp-cur
new file mode 100644
index 0000000..5c3e1d1
--- /dev/null
+++ b/usr.sbin/ctm/mkCTM/ctm_conf.smp-cur
@@ -0,0 +1,7 @@
+#!/usr/local/bin/tclsh
+
+set CTMname smp-cur
+set CTMref /home/smp
+set CTMignore {^CVSROOT/history.*$|^CVSROOT/val-tags$|^CVSROOT/\\.#}
+set CTMbogus {\\.core$|/#cvs|/\\.#}
+set CTMmail smp-cvs-cur@freebsd.org
diff --git a/usr.sbin/ctm/mkCTM/ctm_conf.src-cur b/usr.sbin/ctm/mkCTM/ctm_conf.src-cur
new file mode 100644
index 0000000..8589d04
--- /dev/null
+++ b/usr.sbin/ctm/mkCTM/ctm_conf.src-cur
@@ -0,0 +1,9 @@
+#!/usr/local/bin/tclsh
+
+set CTMname src-cur
+set CTMref /c/src
+set CTMignore {/CVS$|/CVS/|^/secure|^/eBones}
+set CTMbogus {\\.core$|/#cvs|/\\.#}
+set CTMmail ctm-src-cur-fast@freebsd.org
+set CTMqueue /home/ctm/queue/ctm-src-cur
+set CTMqueuemail ctm-src-cur@freebsd.org
diff --git a/usr.sbin/ctm/mkCTM/ctm_conf.src-special b/usr.sbin/ctm/mkCTM/ctm_conf.src-special
new file mode 100644
index 0000000..2a8ca70
--- /dev/null
+++ b/usr.sbin/ctm/mkCTM/ctm_conf.src-special
@@ -0,0 +1,9 @@
+#!/usr/local/bin/tclsh
+
+set CTMname src-cur
+set CTMref $CTMSW/../$CTMname
+set CTMcopy /c/phk/20R/usr/src
+set CTMdont {\.core$|/CVS$|/CVS/|^/secure|^/eBones|/#cvs|/\.#}
+set CTMtest 1
+set CTMspecial 1
+set CTMsuff R20
diff --git a/usr.sbin/ctm/mkCTM/dequeue b/usr.sbin/ctm/mkCTM/dequeue
new file mode 100755
index 0000000..c8fe67d
--- /dev/null
+++ b/usr.sbin/ctm/mkCTM/dequeue
@@ -0,0 +1,6 @@
+#! /bin/sh
+# $Id$
+
+L=/home/ctm/log.dequeue
+/usr/sbin/ctm_dequeue -n 1 -l $L /home/ctm/queue/ctm-cvs-cur
+/usr/sbin/ctm_dequeue -n 1 -l $L /home/ctm/queue/ctm-src-cur
diff --git a/usr.sbin/ctm/mkCTM/mkCTM b/usr.sbin/ctm/mkCTM/mkCTM
new file mode 100644
index 0000000..684fb6a
--- /dev/null
+++ b/usr.sbin/ctm/mkCTM/mkCTM
@@ -0,0 +1,186 @@
+#!/usr/local/bin/tclsh7.4
+
+#############################################################################
+### Do we already have this delta ?
+#############################################################################
+
+proc find_delta {nbr} {
+ global CTMname CTMdest
+ if {[file exists [format "%s/$CTMname.%04d" $CTMdest $nbr]]} { return 1 }
+ if {[file exists [format "%s/$CTMname.%04d.gz" $CTMdest $nbr]]} { return 1 }
+ return 0
+}
+
+#############################################################################
+### The top level code...
+#############################################################################
+
+set CTMSW /home/ctm/SW
+
+cd $CTMSW
+
+# Defaults...
+set CTMapply 1
+set CTMignore {^///}
+set CTMbogus {\.core$}
+set CTMmail {}
+set CTMqueue {}
+set CTMqueuemail {}
+set CTMmaxctm 10000000
+set CTMmaxmsg 100000
+set CTMsuff {}
+set CTMdate [exec date -u +%Y%m%d%H%M%SZ]
+set CTMtmp {}
+set CTMcopy {}
+set CTMdest {}
+set CTMprefix .
+set CTMtest 0
+set CTMspecial 0
+set CTMscan .
+set CTMfirst 0
+set max_damage 100
+
+set damage 0
+set changes 0
+
+source $argv
+exec sh -c "date -u '+%y%m%d%H%M%S $argv'" >> ${CTMSW}/log
+
+if {$CTMtmp == ""} {
+ set CTMtmp $CTMSW/../tmp/${CTMname}_${CTMsuff}
+}
+if {$CTMcopy == ""} {
+ set CTMcopy $CTMSW/../$CTMname
+}
+if {$CTMdest == ""} {
+ set CTMdest $CTMSW/../CTM-pub/$CTMname
+}
+
+# Make sure we only run one at a time...
+
+set CTMlock Lck.${CTMname}.${CTMdate}.[pid]
+exec rm -f ${CTMlock}
+exec echo starting > ${CTMlock}
+if {[catch "exec ln $CTMlock LCK.$CTMname" a]} {
+ puts "Not going, lock exists..."
+ exec rm -f $CTMlock
+ exit 1
+}
+exec rm -f $CTMlock
+set CTMlock LCK.$CTMname
+
+set CTMscratch ${CTMtmp}.tmp
+
+while 1 {
+ if { ! $CTMspecial} {
+ if {$CTMfirst} {
+ set CTMnbr 0
+ } else {
+ set CTMnbr [lindex [exec cat $CTMcopy/.ctm_status] 1]
+ }
+
+ if {$CTMnbr > 0 && ![find_delta $CTMnbr]} {
+ puts "$CTMname delta $CTMnbr doesn't exist..."
+ exec rm -f $CTMlock
+ exit 1
+ }
+
+ incr CTMnbr
+
+ if {[find_delta $CTMnbr]} {
+ puts "$CTMname delta $CTMnbr does already exist..."
+ exec rm -f $CTMlock
+ exit 1
+ }
+
+ set fo [open $CTMref/.ctm_status w]
+ puts $fo "$CTMname $CTMnbr"
+ close $fo
+ incr changes -1
+
+ } else {
+ set CTMnbr [lindex [exec cat $CTMref/.ctm_status] 1]
+ }
+
+ puts "Doing CTMname $CTMname CTMnbr $CTMnbr$CTMsuff CTMdate $CTMdate"
+ flush stdout
+ exec sh -c "rm -f ${CTMtmp}.* ${CTMtmp}:*" >&@ stdout
+
+ set nm [format "%s.%04d%s" $CTMname $CTMnbr $CTMsuff]
+
+ set x1 $CTMcopy
+ if {$x1 == ""} {
+ exec mkdir ${CTMtmp}.dir
+ set x1 ${CTMtmp}.dir
+ }
+ set r1 [catch "exec ${CTMSW}/mkctm -I ${CTMignore} -B ${CTMbogus} -l ${CTMtmp}.log -D $max_damage $CTMname $CTMnbr $CTMdate . $x1 $CTMref | md5 -p | gzip -9 > ${CTMtmp}:${nm}.gz 2>@ stderr" r2]
+
+ if {$r1} {
+ if {[lindex $errorCode 2] == 4} {
+ puts "No changes, stopping."
+ exec rm -f $CTMlock
+ exit 0
+ }
+ puts "problems, stopping now."
+ puts "errorCode $errorCode"
+ puts "$r2"
+ exec rm -f $CTMlock
+ exit 1
+ }
+
+ puts "mkctm done"
+
+ if {$CTMtest} {
+ puts "testing, stopping now."
+ exec rm -f $CTMlock
+ exit 0
+ }
+ if {$CTMapply} {
+ puts "Applying delta"
+ flush stdout
+ exec echo now applying > $CTMlock
+ exec sh -e -c "cd $CTMcopy ; $CTMSW/ctm -v -v -v ${CTMtmp}:${nm}.gz" >& ${CTMtmp}.apply
+ exec echo did apply > $CTMlock
+ }
+ puts "Moving delta"
+ flush stdout
+ exec mv ${CTMtmp}:${nm}.gz $CTMdest/.CTMtmp_${nm}.gz >&@ stdout
+ exec mv $CTMdest/.CTMtmp_${nm}.gz $CTMdest/${nm}.gz >&@ stdout
+ exec echo moved > $CTMlock
+
+ exec sh -c "rm -rf ${CTMtmp}.*" >&@ stdout
+
+ if {$CTMmail != ""} {
+ puts "Mailing delta"
+ flush stdout
+ exec $CTMSW/ctm_smail -m $CTMmaxmsg -c $CTMmaxctm $CTMdest/${nm}.gz $CTMmail >&@ stdout
+ if {$CTMqueue != "" && $CTMqueuemail != ""} {
+ puts "Queueing delta"
+ flush stdout
+ exec $CTMSW/ctm_smail -m $CTMmaxmsg -c $CTMmaxctm -q $CTMqueue $CTMdest/${nm}.gz $CTMqueuemail >&@ stdout
+ puts "Sending initial two deltas"
+ flush stdout
+ exec $CTMSW/ctm_dequeue -n 2 $CTMqueue >&@ stdout
+ }
+ }
+ exec echo mailed > $CTMlock
+
+ # If we did an absolute delta: stop.
+ if {$CTMsuff != ""} break
+
+ # Make a absolute delta (!) every 100 deltas
+ if {$CTMnbr == 0 || ($CTMnbr % 100)} break
+
+ # Make an absolute delta too...
+ set CTMref $CTMcopy
+ set CTMsuff A
+ set CTMcopy ""
+ set CTMmail ""
+ set CTMqueue ""
+ set CTMqueuemail ""
+ set CTMapply 0
+ set CTMspecial 1
+ exec rm -f $CTMlock
+}
+puts "done."
+exec rm -f $CTMlock
diff --git a/usr.sbin/ctm/mkCTM/mkctm.c b/usr.sbin/ctm/mkCTM/mkctm.c
new file mode 100644
index 0000000..55e2361
--- /dev/null
+++ b/usr.sbin/ctm/mkCTM/mkctm.c
@@ -0,0 +1,593 @@
+/* Still missing:
+ *
+ * mkctm
+ * -B regex Bogus
+ * -I regex Ignore
+ * -D int Damage
+ * -q decrease verbosity
+ * -v increase verbosity
+ * -l file logfile
+ * name cvs-cur
+ * prefix src/secure
+ * dir1 "Soll"
+ * dir2 "Ist"
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <sys/wait.h>
+#include <dirent.h>
+#include <regex.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <md5.h>
+#include <err.h>
+#include <signal.h>
+
+#define DEFAULT_IGNORE "/CVS$|/\\.#|00_TRANS\\.TBL$"
+#define DEFAULT_BOGUS "\\.core$|\\.orig$|\\.rej$|\\.o$"
+regex_t reg_ignore, reg_bogus;
+int flag_ignore, flag_bogus;
+
+int verbose;
+int damage, damage_limit;
+int change;
+
+FILE *logf;
+
+u_long s1_ignored, s2_ignored;
+u_long s1_bogus, s2_bogus;
+u_long s1_wrong, s2_wrong;
+u_long s_new_dirs, s_new_files, s_new_bytes;
+u_long s_del_dirs, s_del_files, s_del_bytes;
+u_long s_files_chg, s_bytes_add, s_bytes_del;
+u_long s_same_dirs, s_same_files, s_same_bytes;
+u_long s_edit_files, s_edit_bytes, s_edit_saves;
+u_long s_sub_files, s_sub_bytes;
+
+void
+Usage(void)
+{
+ fprintf(stderr,
+ "usage: mkctm [-options] name number timestamp prefix dir1 dir2\n");
+ fprintf(stderr, "options:\n");
+ fprintf(stderr, "\t\t-B bogus_regexp\n");
+ fprintf(stderr, "\t\t-D damage_limit\n");
+ fprintf(stderr, "\t\t-I ignore_regexp\n");
+ fprintf(stderr, "\t\t-q\n");
+ fprintf(stderr, "\t\t-v\n");
+}
+
+void
+print_stat(FILE *fd, char *pre)
+{
+ fprintf(fd, "%sNames:\n", pre);
+ fprintf(fd, "%s ignore: %5lu ref %5lu target\n",
+ pre, s1_ignored, s2_ignored);
+ fprintf(fd, "%s bogus: %5lu ref %5lu target\n",
+ pre, s1_bogus, s2_bogus);
+ fprintf(fd, "%s wrong: %5lu ref %5lu target\n",
+ pre, s1_wrong, s2_wrong);
+ fprintf(fd, "%sDelta:\n", pre);
+ fprintf(fd, "%s new: %5lu dirs %5lu files %9lu plus\n",
+ pre, s_new_dirs, s_new_files, s_new_bytes);
+ fprintf(fd, "%s del: %5lu dirs %5lu files %9lu minus\n",
+ pre, s_del_dirs, s_del_files, s_del_bytes);
+ fprintf(fd, "%s chg: %5lu files %9lu plus %9lu minus\n",
+ pre, s_files_chg, s_bytes_add, s_bytes_del);
+ fprintf(fd, "%s same: %5lu dirs %5lu files %9lu bytes\n",
+ pre, s_same_dirs, s_same_files, s_same_bytes);
+ fprintf(fd, "%sMethod:\n", pre);
+ fprintf(fd, "%s edit: %5lu files %9lu bytes %9lu saved\n",
+ pre, s_edit_files, s_edit_bytes, s_edit_saves);
+ fprintf(fd, "%s sub: %5lu files %9lu bytes\n",
+ pre, s_sub_files, s_sub_bytes);
+
+}
+
+void
+stat_info(int foo)
+{
+ signal(SIGINFO, stat_info);
+ print_stat(stderr, "INFO: ");
+}
+
+void DoDir(const char *dir1, const char *dir2, const char *name);
+
+static struct stat st;
+static __inline struct stat *
+StatFile(char *name)
+{
+ if (lstat(name, &st) < 0)
+ err(1, "couldn't stat %s", name);
+ return &st;
+}
+
+int
+dirselect(struct dirent *de)
+{
+ if (!strcmp(de->d_name, ".")) return 0;
+ if (!strcmp(de->d_name, "..")) return 0;
+ return 1;
+}
+
+void
+name_stat(const char *pfx, const char *dir, const char *name, struct dirent *de)
+{
+ char *buf = alloca(strlen(dir) + strlen(name) +
+ strlen(de->d_name) + 3);
+ struct stat *st;
+
+ strcpy(buf, dir);
+ strcat(buf, "/"); strcat(buf, name);
+ strcat(buf, "/"); strcat(buf, de->d_name);
+ st = StatFile(buf);
+ printf("%s %s%s %u %u %o",
+ pfx, name, de->d_name,
+ st->st_uid, st->st_gid, st->st_mode & ~S_IFMT);
+ fprintf(logf, "%s %s%s\n", pfx, name, de->d_name);
+ if (verbose > 1) {
+ fprintf(stderr, "%s %s%s\n", pfx, name, de->d_name);
+ }
+}
+
+void
+Equ(const char *dir1, const char *dir2, const char *name, struct dirent *de)
+{
+ if (de->d_type == DT_DIR) {
+ char *p = alloca(strlen(name)+strlen(de->d_name)+2);
+
+ strcpy(p, name); strcat(p, de->d_name); strcat(p, "/");
+ DoDir(dir1, dir2, p);
+ s_same_dirs++;
+ } else {
+ char *buf1 = alloca(strlen(dir1) + strlen(name) +
+ strlen(de->d_name) + 3);
+ char *buf2 = alloca(strlen(dir2) + strlen(name) +
+ strlen(de->d_name) + 3);
+ char *m1, md5_1[33], *m2, md5_2[33];
+ u_char *p1, *p2;
+ int fd1, fd2;
+ struct stat s1, s2;
+
+ strcpy(buf1, dir1);
+ strcat(buf1, "/"); strcat(buf1, name);
+ strcat(buf1, "/"); strcat(buf1, de->d_name);
+ fd1 = open(buf1, O_RDONLY);
+ if(fd1 < 0) { err(3, "%s", buf1); }
+ fstat(fd1, &s1);
+ strcpy(buf2, dir2);
+ strcat(buf2, "/"); strcat(buf2, name);
+ strcat(buf2, "/"); strcat(buf2, de->d_name);
+ fd2 = open(buf2, O_RDONLY);
+ if(fd2 < 0) { err(3, "%s", buf2); }
+ fstat(fd2, &s2);
+#if 0
+ /* XXX if we could just trust the size to change... */
+ if (s1.st_size == s2.st_size) {
+ s_same_files++;
+ s_same_bytes += s1.st_size;
+ close(fd1);
+ close(fd2);
+ goto finish;
+ }
+#endif
+ p1=mmap(0, s1.st_size, PROT_READ, MAP_PRIVATE, fd1, 0);
+ if (p1 == (u_char *)MAP_FAILED) { err(3, "%s", buf1); }
+ close(fd1);
+
+ p2=mmap(0, s2.st_size, PROT_READ, MAP_PRIVATE, fd2, 0);
+ if (p2 == (u_char *)MAP_FAILED) { err(3, "%s", buf2); }
+ close(fd2);
+
+ /* If identical, we're done. */
+ if((s1.st_size == s2.st_size) && !memcmp(p1, p2, s1.st_size)) {
+ s_same_files++;
+ s_same_bytes += s1.st_size;
+ goto finish;
+ }
+
+ s_files_chg++;
+ change++;
+ if (s1.st_size > s2.st_size)
+ s_bytes_del += (s1.st_size - s2.st_size);
+ else
+ s_bytes_add += (s2.st_size - s1.st_size);
+
+ m1 = MD5Data(p1, s1.st_size, md5_1);
+ m2 = MD5Data(p2, s2.st_size, md5_2);
+
+ /* Just a curiosity... */
+ if(!strcmp(m1, m2)) {
+ if (s1.st_size != s2.st_size)
+ fprintf(stderr,
+ "Notice: MD5 same for files of diffent size:\n\t%s\n\t%s\n",
+ buf1, buf2);
+ goto finish;
+ }
+
+ {
+ u_long l = s2.st_size + 2;
+ u_char *cmd = alloca(strlen(buf1)+strlen(buf2)+100);
+ u_char *ob = alloca(l), *p;
+ int j;
+ FILE *F;
+
+ if (s1.st_size && p1[s1.st_size-1] != '\n') {
+ if (verbose > 0)
+ fprintf(stderr,
+ "last char != \\n in %s\n",
+ buf1);
+ goto subst;
+ }
+
+ if (s2.st_size && p2[s2.st_size-1] != '\n') {
+ if (verbose > 0)
+ fprintf(stderr,
+ "last char != \\n in %s\n",
+ buf2);
+ goto subst;
+ }
+
+ for (p=p1; p<p1+s1.st_size; p++)
+ if (!*p) {
+ if (verbose > 0)
+ fprintf(stderr,
+ "NULL char in %s\n",
+ buf1);
+ goto subst;
+ }
+
+ for (p=p2; p<p2+s2.st_size; p++)
+ if (!*p) {
+ if (verbose > 0)
+ fprintf(stderr,
+ "NULL char in %s\n",
+ buf2);
+ goto subst;
+ }
+
+ strcpy(cmd, "diff -n ");
+ strcat(cmd, buf1);
+ strcat(cmd, " ");
+ strcat(cmd, buf2);
+ F = popen(cmd, "r");
+ for (j = 1, l = 0; l < s2.st_size; ) {
+ j = fread(ob+l, 1, s2.st_size - l, F);
+ if (j < 1)
+ break;
+ l += j;
+ continue;
+ }
+ if (j) {
+ l = 0;
+ while (EOF != fgetc(F))
+ continue;
+ }
+ pclose(F);
+
+ if (l && l < s2.st_size) {
+ name_stat("CTMFN", dir2, name, de);
+ printf(" %s %s %d\n", m1, m2, (unsigned)l);
+ fwrite(ob, 1, l, stdout);
+ putchar('\n');
+ s_edit_files++;
+ s_edit_bytes += l;
+ s_edit_saves += (s2.st_size - l);
+ } else {
+ subst:
+ name_stat("CTMFS", dir2, name, de);
+ printf(" %s %s %u\n", m1, m2, (unsigned)s2.st_size);
+ fwrite(p2, 1, s2.st_size, stdout);
+ putchar('\n');
+ s_sub_files++;
+ s_sub_bytes += s2.st_size;
+ }
+ }
+ finish:
+ munmap(p1, s1.st_size);
+ munmap(p2, s2.st_size);
+ }
+}
+
+void
+Add(const char *dir1, const char *dir2, const char *name, struct dirent *de)
+{
+ change++;
+ if (de->d_type == DT_DIR) {
+ char *p = alloca(strlen(name)+strlen(de->d_name)+2);
+ strcpy(p, name); strcat(p, de->d_name); strcat(p, "/");
+ name_stat("CTMDM", dir2, name, de);
+ putchar('\n');
+ s_new_dirs++;
+ DoDir(dir1, dir2, p);
+ } else if (de->d_type == DT_REG) {
+ char *buf2 = alloca(strlen(dir2) + strlen(name) +
+ strlen(de->d_name) + 3);
+ char *m2, md5_2[33];
+ u_char *p1;
+ struct stat st;
+ int fd1;
+
+ strcpy(buf2, dir2);
+ strcat(buf2, "/"); strcat(buf2, name);
+ strcat(buf2, "/"); strcat(buf2, de->d_name);
+ fd1 = open(buf2, O_RDONLY);
+ if (fd1 < 0) { err(3, "%s", buf2); }
+ fstat(fd1, &st);
+ p1=mmap(0, st.st_size, PROT_READ, MAP_PRIVATE, fd1, 0);
+ if (p1 == (u_char *)MAP_FAILED) { err(3, "%s", buf2); }
+ close(fd1);
+ m2 = MD5Data(p1, st.st_size, md5_2);
+ name_stat("CTMFM", dir2, name, de);
+ printf(" %s %u\n", m2, (unsigned)st.st_size);
+ fwrite(p1, 1, st.st_size, stdout);
+ putchar('\n');
+ munmap(p1, st.st_size);
+ s_new_files++;
+ s_new_bytes += st.st_size;
+ }
+}
+
+void
+Del (const char *dir1, const char *dir2, const char *name, struct dirent *de)
+{
+ damage++;
+ change++;
+ if (de->d_type == DT_DIR) {
+ char *p = alloca(strlen(name)+strlen(de->d_name)+2);
+ strcpy(p, name); strcat(p, de->d_name); strcat(p, "/");
+ DoDir(dir1, dir2, p);
+ printf("CTMDR %s%s\n", name, de->d_name);
+ fprintf(logf, "CTMDR %s%s\n", name, de->d_name);
+ if (verbose > 1) {
+ fprintf(stderr, "CTMDR %s%s\n", name, de->d_name);
+ }
+ s_del_dirs++;
+ } else if (de->d_type == DT_REG) {
+ char *buf1 = alloca(strlen(dir1) + strlen(name) +
+ strlen(de->d_name) + 3);
+ char *m1, md5_1[33];
+ strcpy(buf1, dir1);
+ strcat(buf1, "/"); strcat(buf1, name);
+ strcat(buf1, "/"); strcat(buf1, de->d_name);
+ m1 = MD5File(buf1, md5_1);
+ printf("CTMFR %s%s %s\n", name, de->d_name, m1);
+ fprintf(logf, "CTMFR %s%s %s\n", name, de->d_name, m1);
+ if (verbose > 1) {
+ fprintf(stderr, "CTMFR %s%s\n", name, de->d_name);
+ }
+ s_del_files++;
+ s_del_bytes += StatFile(buf1)->st_size;
+ }
+}
+
+void
+GetNext(int *i, int *n, struct dirent **nl, const char *dir, const char *name, u_long *ignored, u_long *bogus, u_long *wrong)
+{
+ char buf[BUFSIZ];
+ char buf1[BUFSIZ];
+
+ for (;;) {
+ for (;;) {
+ (*i)++;
+ if (*i >= *n)
+ return;
+ strcpy(buf1, name);
+ if (buf1[strlen(buf1)-1] != '/')
+ strcat(buf1, "/");
+ strcat(buf1, nl[*i]->d_name);
+ if (flag_ignore &&
+ !regexec(&reg_ignore, buf1, 0, 0, 0)) {
+ (*ignored)++;
+ fprintf(logf, "Ignore %s\n", buf1);
+ if (verbose > 2) {
+ fprintf(stderr, "Ignore %s\n", buf1);
+ }
+ } else if (flag_bogus &&
+ !regexec(&reg_bogus, buf1, 0, 0, 0)) {
+ (*bogus)++;
+ fprintf(logf, "Bogus %s\n", buf1);
+ fprintf(stderr, "Bogus %s\n", buf1);
+ damage++;
+ } else {
+ *buf = 0;
+ if (*dir != '/')
+ strcat(buf, "/");
+ strcat(buf, dir);
+ if (buf[strlen(buf)-1] != '/')
+ strcat(buf, "/");
+ strcat(buf, buf1);
+ break;
+ }
+ free(nl[*i]); nl[*i] = 0;
+ }
+ /* If the filesystem didn't tell us, find type */
+ if (nl[*i]->d_type == DT_UNKNOWN)
+ nl[*i]->d_type = IFTODT(StatFile(buf)->st_mode);
+ if (nl[*i]->d_type == DT_REG || nl[*i]->d_type == DT_DIR)
+ break;
+ (*wrong)++;
+ if (verbose > 0)
+ fprintf(stderr, "Wrong %s\n", buf);
+ free(nl[*i]); nl[*i] = 0;
+ }
+}
+
+void
+DoDir(const char *dir1, const char *dir2, const char *name)
+{
+ int i1, i2, n1, n2, i;
+ struct dirent **nl1, **nl2;
+ char *buf1 = alloca(strlen(dir1) + strlen(name) + 4);
+ char *buf2 = alloca(strlen(dir2) + strlen(name) + 4);
+
+ strcpy(buf1, dir1); strcat(buf1, "/"); strcat(buf1, name);
+ strcpy(buf2, dir2); strcat(buf2, "/"); strcat(buf2, name);
+ n1 = scandir(buf1, &nl1, dirselect, alphasort);
+ n2 = scandir(buf2, &nl2, dirselect, alphasort);
+ i1 = i2 = -1;
+ GetNext(&i1, &n1, nl1, dir1, name, &s1_ignored, &s1_bogus, &s1_wrong);
+ GetNext(&i2, &n2, nl2, dir2, name, &s2_ignored, &s2_bogus, &s2_wrong);
+ for (;i1 < n1 || i2 < n2;) {
+
+ if (damage_limit && damage > damage_limit)
+ break;
+
+ /* Get next item from list 1 */
+ if (i1 < n1 && !nl1[i1])
+ GetNext(&i1, &n1, nl1, dir1, name,
+ &s1_ignored, &s1_bogus, &s1_wrong);
+
+ /* Get next item from list 2 */
+ if (i2 < n2 && !nl2[i2])
+ GetNext(&i2, &n2, nl2, dir2, name,
+ &s2_ignored, &s2_bogus, &s2_wrong);
+
+ if (i1 >= n1 && i2 >= n2) {
+ /* Done */
+ break;
+ } else if (i1 >= n1 && i2 < n2) {
+ /* end of list 1, add anything left on list 2 */
+ Add(dir1, dir2, name, nl2[i2]);
+ free(nl2[i2]); nl2[i2] = 0;
+ } else if (i1 < n1 && i2 >= n2) {
+ /* end of list 2, delete anything left on list 1 */
+ Del(dir1, dir2, name, nl1[i1]);
+ free(nl1[i1]); nl1[i1] = 0;
+ } else if (!(i = strcmp(nl1[i1]->d_name, nl2[i2]->d_name))) {
+ /* Identical names */
+ if (nl1[i1]->d_type == nl2[i2]->d_type) {
+ /* same type */
+ Equ(dir1, dir2, name, nl1[i1]);
+ } else {
+ /* different types */
+ Del(dir1, dir2, name, nl1[i1]);
+ Add(dir1, dir2, name, nl2[i2]);
+ }
+ free(nl1[i1]); nl1[i1] = 0;
+ free(nl2[i2]); nl2[i2] = 0;
+ } else if (i < 0) {
+ /* Something extra in list 1, delete it */
+ Del(dir1, dir2, name, nl1[i1]);
+ free(nl1[i1]); nl1[i1] = 0;
+ } else {
+ /* Something extra in list 2, add it */
+ Add(dir1, dir2, name, nl2[i2]);
+ free(nl2[i2]); nl2[i2] = 0;
+ }
+ }
+ if (n1 >= 0)
+ free(nl1);
+ if (n2 >= 0)
+ free(nl2);
+}
+
+int
+main(int argc, char **argv)
+{
+ int i;
+ extern char *optarg;
+ extern int optind;
+
+ setbuf(stderr, NULL);
+
+#if 0
+ if (regcomp(&reg_bogus, DEFAULT_BOGUS, REG_EXTENDED | REG_NEWLINE))
+ /* XXX use regerror to explain it */
+ errx(1, "default regular expression argument to -B is botched");
+ flag_bogus = 1;
+
+ if (regcomp(&reg_ignore, DEFAULT_IGNORE, REG_EXTENDED | REG_NEWLINE))
+ /* XXX use regerror to explain it */
+ errx(1, "default regular expression argument to -I is botched");
+ flag_ignore = 1;
+#endif
+
+ while ((i = getopt(argc, argv, "D:I:B:l:qv")) != -1)
+ switch (i) {
+ case 'D':
+ damage_limit = strtol(optarg, 0, 0);
+ if (damage_limit < 0)
+ errx(1, "damage limit must be positive");
+ break;
+ case 'I':
+ if (flag_ignore)
+ regfree(&reg_ignore);
+ flag_ignore = 0;
+ if (!*optarg)
+ break;
+ if (regcomp(&reg_ignore, optarg,
+ REG_EXTENDED | REG_NEWLINE))
+ /* XXX use regerror to explain it */
+ errx(1, "regular expression argument to -I is botched");
+ flag_ignore = 1;
+ break;
+ case 'B':
+ if (flag_bogus)
+ regfree(&reg_bogus);
+ flag_bogus = 0;
+ if (!*optarg)
+ break;
+ if (regcomp(&reg_bogus, optarg,
+ REG_EXTENDED | REG_NEWLINE))
+ /* XXX use regerror to explain it */
+ errx(1, "regular expression argument to -B is botched");
+ flag_bogus = 1;
+ break;
+ case 'l':
+ logf = fopen(optarg, "w");
+ if (!logf)
+ err(1, "%s", optarg);
+ break;
+ case 'q':
+ verbose--;
+ break;
+ case 'v':
+ verbose++;
+ break;
+ case '?':
+ default:
+ Usage();
+ return (1);
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (!logf)
+ logf = fopen("/dev/null", "w");
+
+ setbuf(stdout, 0);
+
+ if (argc != 6) {
+ Usage();
+ return (1);
+ }
+
+ signal(SIGINFO, stat_info);
+
+ fprintf(stderr, "CTM_BEGIN 2.0 %s %s %s %s\n",
+ argv[0], argv[1], argv[2], argv[3]);
+ fprintf(logf, "CTM_BEGIN 2.0 %s %s %s %s\n",
+ argv[0], argv[1], argv[2], argv[3]);
+ printf("CTM_BEGIN 2.0 %s %s %s %s\n",
+ argv[0], argv[1], argv[2], argv[3]);
+ DoDir(argv[4], argv[5], "");
+ if (damage_limit && damage > damage_limit) {
+ print_stat(stderr, "DAMAGE: ");
+ errx(1, "damage of %d would exceed %d files",
+ damage, damage_limit);
+ } else if (change < 2) {
+ errx(4, "no changes");
+ } else {
+ printf("CTM_END ");
+ fprintf(logf, "CTM_END\n");
+ print_stat(stderr, "END: ");
+ }
+ exit(0);
+}
diff --git a/usr.sbin/dev_mkdb/Makefile b/usr.sbin/dev_mkdb/Makefile
new file mode 100644
index 0000000..8737b50
--- /dev/null
+++ b/usr.sbin/dev_mkdb/Makefile
@@ -0,0 +1,6 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= dev_mkdb
+MAN8= dev_mkdb.8
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/dev_mkdb/dev_mkdb.8 b/usr.sbin/dev_mkdb/dev_mkdb.8
new file mode 100644
index 0000000..5488a93
--- /dev/null
+++ b/usr.sbin/dev_mkdb/dev_mkdb.8
@@ -0,0 +1,81 @@
+.\" Copyright (c) 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)dev_mkdb.8 8.1 (Berkeley) 6/6/93
+.\"
+.Dd June 6, 1993
+.Os
+.Dt DEV_MKDB 8
+.Sh NAME
+.Nm dev_mkdb
+.Nd create
+.Pa /dev
+database
+.Sh SYNOPSIS
+.Nm dev_mkdb
+.Sh DESCRIPTION
+The
+.Nm
+command creates a
+.Xr db 3
+hash access method database in
+.Pa /var/run/dev.db
+which contains the names of all of the character and block special
+files in the
+.Pa /dev
+directory, using the file type and the
+.Fa st_rdev
+field as the key.
+.Pp
+Keys are a structure containing a mode_t followed by a dev_t,
+with any padding zero'd out.
+The former is the type of the file (st_mode & S_IFMT),
+the latter is the st_rdev field.
+.Sh FILES
+.Bl -tag -width /var/run/dev.db -compact
+.It Pa /dev
+Device directory.
+.It Pa /var/run/dev.db
+Database file.
+.El
+.Sh SEE ALSO
+.Xr ps 1 ,
+.Xr stat 2 ,
+.Xr db 3 ,
+.Xr devname 3 ,
+.Xr kvm_nlist 3 ,
+.Xr ttyname 3 ,
+.Xr kvm_mkdb 8
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 4.4 .
diff --git a/usr.sbin/dev_mkdb/dev_mkdb.c b/usr.sbin/dev_mkdb/dev_mkdb.c
new file mode 100644
index 0000000..ef113d1
--- /dev/null
+++ b/usr.sbin/dev_mkdb/dev_mkdb.c
@@ -0,0 +1,153 @@
+/*-
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 1990, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)dev_mkdb.c 8.1 (Berkeley) 6/6/93";
+#endif
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include <db.h>
+#include <dirent.h>
+#include <err.h>
+#include <fcntl.h>
+#include <kvm.h>
+#include <nlist.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+static void usage __P((void));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ register DIR *dirp;
+ register struct dirent *dp;
+ struct stat sb;
+ struct {
+ mode_t type;
+ dev_t dev;
+ } bkey;
+ DB *db;
+ DBT data, key;
+ int ch;
+ u_char buf[MAXNAMLEN + 1];
+ char dbtmp[MAXPATHLEN + 1], dbname[MAXPATHLEN + 1];
+
+ while ((ch = getopt(argc, argv, "")) != -1)
+ switch((char)ch) {
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc > 0)
+ usage();
+
+ if (chdir(_PATH_DEV))
+ err(1, "%s", _PATH_DEV);
+
+ dirp = opendir(".");
+
+ (void)snprintf(dbtmp, sizeof(dbtmp), "%sdev.tmp", _PATH_VARRUN);
+ (void)snprintf(dbname, sizeof(dbtmp), "%sdev.db", _PATH_VARRUN);
+ db = dbopen(dbtmp, O_CREAT|O_EXLOCK|O_RDWR|O_TRUNC,
+ S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH, DB_HASH, NULL);
+ if (db == NULL)
+ err(1, "%s", dbtmp);
+
+ /*
+ * Keys are a mode_t followed by a dev_t. The former is the type of
+ * the file (mode & S_IFMT), the latter is the st_rdev field. Note
+ * that the structure may contain padding, so we have to clear it
+ * out here.
+ */
+ bzero(&bkey, sizeof(bkey));
+ key.data = &bkey;
+ key.size = sizeof(bkey);
+ data.data = buf;
+ while ((dp = readdir(dirp))) {
+ if (lstat(dp->d_name, &sb)) {
+ warn("%s", dp->d_name);
+ continue;
+ }
+
+ /* Create the key. */
+ if (S_ISCHR(sb.st_mode))
+ bkey.type = S_IFCHR;
+ else if (S_ISBLK(sb.st_mode))
+ bkey.type = S_IFBLK;
+ else
+ continue;
+ bkey.dev = sb.st_rdev;
+
+ /*
+ * Create the data; nul terminate the name so caller doesn't
+ * have to.
+ */
+ bcopy(dp->d_name, buf, dp->d_namlen);
+ buf[dp->d_namlen] = '\0';
+ data.size = dp->d_namlen + 1;
+ if ((db->put)(db, &key, &data, 0))
+ err(1, "dbput %s", dbtmp);
+ }
+ (void)(db->close)(db);
+ if (rename(dbtmp, dbname))
+ err(1, "rename %s to %s", dbtmp, dbname);
+ exit(0);
+}
+
+static void
+usage()
+{
+ (void)fprintf(stderr, "usage: dev_mkdb\n");
+ exit(1);
+}
diff --git a/usr.sbin/diskpart/Makefile b/usr.sbin/diskpart/Makefile
new file mode 100644
index 0000000..bf623cb
--- /dev/null
+++ b/usr.sbin/diskpart/Makefile
@@ -0,0 +1,6 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= diskpart
+MAN8= diskpart.8
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/diskpart/diskpart.8 b/usr.sbin/diskpart/diskpart.8
new file mode 100644
index 0000000..e13613a
--- /dev/null
+++ b/usr.sbin/diskpart/diskpart.8
@@ -0,0 +1,143 @@
+.\" Copyright (c) 1983, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)diskpart.8 8.1 (Berkeley) 6/6/93
+.\"
+.Dd June 6, 1993
+.Dt DISKPART 8
+.Os BSD 4
+.Sh NAME
+.Nm diskpart
+.Nd calculate default disk partition sizes
+.Sh SYNOPSIS
+.Nm diskpart
+.Op Fl p
+.Op Fl d
+.Op Fl s Ar size
+.Ar disk-type
+.Sh DESCRIPTION
+.Nm Diskpart
+is used to calculate the disk partition sizes based on the
+default rules used at Berkeley.
+.Pp
+Available options and operands:
+.Bl -tag -width Fl
+.It Fl p
+Tables suitable for inclusion in a device driver
+are produced.
+.It Fl d
+An entry suitable for inclusion in the disk
+description file
+.Pa /etc/disktab
+is generated; for example,
+.Xr disktab 5 .
+.It Fl s Ar size
+The size of the disk may be limited to
+.Ar size
+with the
+.Fl s
+option.
+.El
+.Pp
+On disks that use
+.Xr bad144 8
+type of
+bad-sector forwarding,
+space is normally left in the last partition on the disk
+for a bad sector forwarding table, although this space
+is not reflected in the tables produced. The space reserved
+is one track for the replicated copies of the table and
+sufficient tracks to hold a pool of 126 sectors to which bad sectors
+are mapped. For more information, see
+.Xr bad144 8 .
+The
+.Fl s
+option is intended for other controllers which reserve some space at the end
+of the disk for bad-sector replacements or other control areas,
+even if not a multiple of cylinders.
+.Pp
+The disk partition sizes are based on the total amount of
+space on the disk as given in the table below (all values
+are supplied in units of sectors). The
+.Ql c
+partition
+is, by convention, used to access the entire physical disk.
+The device driver tables include
+the space reserved for the bad sector forwarding table in the
+.Ql c
+partition;
+those used in the disktab and default formats exclude reserved tracks.
+In normal operation, either the
+.Ql g
+partition is used, or the
+.Ql d ,
+.Ql e ,
+and
+.Ql f
+partitions are used. The
+.Ql g
+and
+.Ql f
+partitions
+are variable-sized, occupying whatever space remains after allocation
+of the fixed sized partitions.
+If the disk is smaller than 20 Megabytes, then
+.Nm
+aborts with the message
+.Dq Li disk too small, calculate by hand .
+.Bl -column Partition 20-60\ MB 61-205\ MB 206-355\ MB 356+\ MB
+Partition 20-60 MB 61-205 MB 206-355 MB 356+ MB
+a 15884 15884 15884 15884
+b 10032 33440 33440 66880
+d 15884 15884 15884 15884
+e unused 55936 55936 307200
+h unused unused 291346 291346
+.El
+.Pp
+If an unknown disk type is specified,
+.Nm
+will prompt for the required disk geometry information.
+.Sh SEE ALSO
+.Xr disktab 5 ,
+.Xr bad144 8
+.Sh BUGS
+Most default partition sizes are based on historical artifacts
+(like the RP06), and may result in unsatisfactory layouts.
+.Pp
+When using the
+.Fl d
+flag, alternate disk names are not included
+in the output.
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 4.2 .
diff --git a/usr.sbin/diskpart/diskpart.c b/usr.sbin/diskpart/diskpart.c
new file mode 100644
index 0000000..86b4122
--- /dev/null
+++ b/usr.sbin/diskpart/diskpart.c
@@ -0,0 +1,496 @@
+/*
+ * Copyright (c) 1983, 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 1983, 1988, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)diskpart.c 8.3 (Berkeley) 11/30/94";
+#endif
+static const char rcsid[] =
+ "$Id: diskpart.c,v 1.8 1997/02/22 16:05:46 peter Exp $";
+#endif /* not lint */
+
+/*
+ * Program to calculate standard disk partition sizes.
+ */
+#include <sys/param.h>
+#define DKTYPENAMES
+#include <sys/disklabel.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <stdio.h>
+
+#define for_now /* show all of `c' partition for disklabel */
+#define NPARTITIONS 8
+#define PART(x) (x - 'a')
+
+/*
+ * Default partition sizes, where they exist.
+ */
+#define NDEFAULTS 4
+int defpart[NDEFAULTS][NPARTITIONS] = {
+ { 15884, 66880, 0, 15884, 307200, 0, 0, 291346 }, /* ~ 356+ Mbytes */
+ { 15884, 33440, 0, 15884, 55936, 0, 0, 291346 }, /* ~ 206-355 Mbytes */
+ { 15884, 33440, 0, 15884, 55936, 0, 0, 0 }, /* ~ 61-205 Mbytes */
+ { 15884, 10032, 0, 15884, 0, 0, 0, 0 }, /* ~ 20-60 Mbytes */
+};
+
+/*
+ * Each array defines a layout for a disk;
+ * that is, the collection of partitions totally
+ * covers the physical space on a disk.
+ */
+#define NLAYOUTS 3
+char layouts[NLAYOUTS][NPARTITIONS] = {
+ { 'a', 'b', 'h', 'g' },
+ { 'a', 'b', 'h', 'd', 'e', 'f' },
+ { 'c' },
+};
+
+/*
+ * Default disk block and disk block fragment
+ * sizes for each file system. Those file systems
+ * with zero block and frag sizes are special cases
+ * (e.g. swap areas or for access to the entire device).
+ */
+struct partition defparam[NPARTITIONS] = {
+ { 0, 0, 1024, FS_UNUSED, 8, 0 }, /* a */
+ { 0, 0, 1024, FS_SWAP, 8, 0 }, /* b */
+ { 0, 0, 1024, FS_UNUSED, 8, 0 }, /* c */
+ { 0, 0, 512, FS_UNUSED, 8, 0 }, /* d */
+ { 0, 0, 1024, FS_UNUSED, 8, 0 }, /* e */
+ { 0, 0, 1024, FS_UNUSED, 8, 0 }, /* f */
+ { 0, 0, 1024, FS_UNUSED, 8, 0 }, /* g */
+ { 0, 0, 1024, FS_UNUSED, 8, 0 } /* h */
+};
+
+/*
+ * Each disk has some space reserved for a bad sector
+ * forwarding table. DEC standard 144 uses the first
+ * 5 even numbered sectors in the last track of the
+ * last cylinder for replicated storage of the bad sector
+ * table; another 126 sectors past this is needed as a
+ * pool of replacement sectors.
+ */
+int badsecttable = 126; /* # sectors */
+
+int pflag; /* print device driver partition tables */
+int dflag; /* print disktab entry */
+
+struct disklabel *promptfordisk();
+static void usage __P((void));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ struct disklabel *dp;
+ register int curcyl, spc, def, part, layout, j;
+ int threshhold, numcyls[NPARTITIONS], startcyl[NPARTITIONS];
+ int totsize = 0;
+ char *lp, *tyname;
+
+ argc--, argv++;
+ if (argc < 1)
+ usage();
+ if (argc > 0 && strcmp(*argv, "-p") == 0) {
+ pflag++;
+ argc--, argv++;
+ }
+ if (argc > 0 && strcmp(*argv, "-d") == 0) {
+ dflag++;
+ argc--, argv++;
+ }
+ if (argc > 1 && strcmp(*argv, "-s") == 0) {
+ totsize = atoi(argv[1]);
+ argc += 2, argv += 2;
+ }
+ dp = getdiskbyname(*argv);
+ if (dp == NULL) {
+ if (isatty(0))
+ dp = promptfordisk(*argv);
+ if (dp == NULL)
+ errx(2, "%s: unknown disk type", *argv);
+ } else {
+ if (dp->d_flags & D_REMOVABLE)
+ tyname = "removable";
+ else if (dp->d_flags & D_RAMDISK)
+ tyname = "simulated";
+ else
+ tyname = "winchester";
+ }
+ spc = dp->d_secpercyl;
+ /*
+ * Bad sector table contains one track for the replicated
+ * copies of the table and enough full tracks preceding
+ * the last track to hold the pool of free blocks to which
+ * bad sectors are mapped.
+ * If disk size was specified explicitly, use specified size.
+ */
+ if (dp->d_type == DTYPE_SMD && dp->d_flags & D_BADSECT &&
+ totsize == 0) {
+ badsecttable = dp->d_nsectors +
+ roundup(badsecttable, dp->d_nsectors);
+ threshhold = howmany(spc, badsecttable);
+ } else {
+ badsecttable = 0;
+ threshhold = 0;
+ }
+ /*
+ * If disk size was specified, recompute number of cylinders
+ * that may be used, and set badsecttable to any remaining
+ * fraction of the last cylinder.
+ */
+ if (totsize != 0) {
+ dp->d_ncylinders = howmany(totsize, spc);
+ badsecttable = spc * dp->d_ncylinders - totsize;
+ }
+
+ /*
+ * Figure out if disk is large enough for
+ * expanded swap area and 'd', 'e', and 'f'
+ * partitions. Otherwise, use smaller defaults
+ * based on RK07.
+ */
+ for (def = 0; def < NDEFAULTS; def++) {
+ curcyl = 0;
+ for (part = PART('a'); part < NPARTITIONS; part++)
+ curcyl += howmany(defpart[def][part], spc);
+ if (curcyl < dp->d_ncylinders - threshhold)
+ break;
+ }
+ if (def >= NDEFAULTS)
+ errx(3, "%s: disk too small, calculate by hand", *argv);
+
+ /*
+ * Calculate number of cylinders allocated to each disk
+ * partition. We may waste a bit of space here, but it's
+ * in the interest of (very backward) compatibility
+ * (for mixed disk systems).
+ */
+ for (curcyl = 0, part = PART('a'); part < NPARTITIONS; part++) {
+ numcyls[part] = 0;
+ if (defpart[def][part] != 0) {
+ numcyls[part] = howmany(defpart[def][part], spc);
+ curcyl += numcyls[part];
+ }
+ }
+ numcyls[PART('f')] = dp->d_ncylinders - curcyl;
+ numcyls[PART('g')] =
+ numcyls[PART('d')] + numcyls[PART('e')] + numcyls[PART('f')];
+ numcyls[PART('c')] = dp->d_ncylinders;
+ defpart[def][PART('f')] = numcyls[PART('f')] * spc - badsecttable;
+ defpart[def][PART('g')] = numcyls[PART('g')] * spc - badsecttable;
+ defpart[def][PART('c')] = numcyls[PART('c')] * spc;
+#ifndef for_now
+ if (totsize || !pflag)
+#else
+ if (totsize)
+#endif
+ defpart[def][PART('c')] -= badsecttable;
+
+ /*
+ * Calculate starting cylinder number for each partition.
+ * Note the 'h' partition is physically located before the
+ * 'g' or 'd' partition. This is reflected in the layout
+ * arrays defined above.
+ */
+ for (layout = 0; layout < NLAYOUTS; layout++) {
+ curcyl = 0;
+ for (lp = layouts[layout]; *lp != 0; lp++) {
+ startcyl[PART(*lp)] = curcyl;
+ curcyl += numcyls[PART(*lp)];
+ }
+ }
+
+ if (pflag) {
+ printf("}, %s_sizes[%d] = {\n", dp->d_typename, NPARTITIONS);
+ for (part = PART('a'); part < NPARTITIONS; part++) {
+ if (numcyls[part] == 0) {
+ printf("\t0,\t0,\n");
+ continue;
+ }
+ if (dp->d_type != DTYPE_MSCP) {
+ printf("\t%d,\t%d,\t\t/* %c=cyl %d thru %d */\n",
+ defpart[def][part], startcyl[part],
+ 'A' + part, startcyl[part],
+ startcyl[part] + numcyls[part] - 1);
+ continue;
+ }
+ printf("\t%d,\t%d,\t\t/* %c=sectors %d thru %d */\n",
+ defpart[def][part], spc * startcyl[part],
+ 'A' + part, spc * startcyl[part],
+ spc * startcyl[part] + defpart[def][part] - 1);
+ }
+ exit(0);
+ }
+ if (dflag) {
+ int nparts;
+
+ /*
+ * In case the disk is in the ``in-between'' range
+ * where the 'g' partition is smaller than the 'h'
+ * partition, reverse the frag sizes so the /usr partition
+ * is always set up with a frag size larger than the
+ * user's partition.
+ */
+ if (defpart[def][PART('g')] < defpart[def][PART('h')]) {
+ int temp;
+
+ temp = defparam[PART('h')].p_fsize;
+ defparam[PART('h')].p_fsize =
+ defparam[PART('g')].p_fsize;
+ defparam[PART('g')].p_fsize = temp;
+ }
+ printf("%s:\\\n", dp->d_typename);
+ printf("\t:ty=%s:ns#%d:nt#%d:nc#%d:", tyname,
+ dp->d_nsectors, dp->d_ntracks, dp->d_ncylinders);
+ if (dp->d_secpercyl != dp->d_nsectors * dp->d_ntracks)
+ printf("sc#%d:", dp->d_secpercyl);
+ if (dp->d_type == DTYPE_SMD && dp->d_flags & D_BADSECT)
+ printf("sf:");
+ printf("\\\n\t:dt=%s:", dktypenames[dp->d_type]);
+ for (part = NDDATA - 1; part >= 0; part--)
+ if (dp->d_drivedata[part])
+ break;
+ for (j = 0; j <= part; j++)
+ printf("d%d#%d:", j, dp->d_drivedata[j]);
+ printf("\\\n");
+ for (nparts = 0, part = PART('a'); part < NPARTITIONS; part++)
+ if (defpart[def][part] != 0)
+ nparts++;
+ for (part = PART('a'); part < NPARTITIONS; part++) {
+ if (defpart[def][part] == 0)
+ continue;
+ printf("\t:p%c#%d:", 'a' + part, defpart[def][part]);
+ printf("o%c#%d:b%c#%d:f%c#%d:",
+ 'a' + part, spc * startcyl[part],
+ 'a' + part,
+ defparam[part].p_frag * defparam[part].p_fsize,
+ 'a' + part, defparam[part].p_fsize);
+ if (defparam[part].p_fstype == FS_SWAP)
+ printf("t%c=swap:", 'a' + part);
+ nparts--;
+ printf("%s\n", nparts > 0 ? "\\" : "");
+ }
+#ifdef for_now
+ defpart[def][PART('c')] -= badsecttable;
+ part = PART('c');
+ printf("#\t:p%c#%d:", 'a' + part, defpart[def][part]);
+ printf("o%c#%d:b%c#%d:f%c#%d:\n",
+ 'a' + part, spc * startcyl[part],
+ 'a' + part,
+ defparam[part].p_frag * defparam[part].p_fsize,
+ 'a' + part, defparam[part].p_fsize);
+#endif
+ exit(0);
+ }
+ printf("%s: #sectors/track=%d, #tracks/cylinder=%d #cylinders=%d\n",
+ dp->d_typename, dp->d_nsectors, dp->d_ntracks,
+ dp->d_ncylinders);
+ printf("\n Partition\t Size\t Offset\t Range\n");
+ for (part = PART('a'); part < NPARTITIONS; part++) {
+ printf("\t%c\t", 'a' + part);
+ if (numcyls[part] == 0) {
+ printf(" unused\n");
+ continue;
+ }
+ printf("%7d\t%7d\t%4d - %d%s\n",
+ defpart[def][part], startcyl[part] * spc,
+ startcyl[part], startcyl[part] + numcyls[part] - 1,
+ defpart[def][part] % spc ? "*" : "");
+ }
+}
+
+static void
+usage()
+{
+ fprintf(stderr, "usage: disktab [-p] [-d] [-s size] disk-type\n");
+ exit(1);
+}
+
+struct disklabel disk;
+
+struct field {
+ char *f_name;
+ char *f_defaults;
+ u_int32_t *f_location;
+} fields[] = {
+ { "sector size", "512", &disk.d_secsize },
+ { "#sectors/track", 0, &disk.d_nsectors },
+ { "#tracks/cylinder", 0, &disk.d_ntracks },
+ { "#cylinders", 0, &disk.d_ncylinders },
+ { 0, 0, 0 },
+};
+
+char *
+mygets(buf)
+ char *buf;
+{
+ size_t len;
+
+ if (fgets(buf, BUFSIZ, stdin) == NULL)
+ buf[0] = '\0';
+ len = strlen(buf);
+ if (len != 0 && buf[len - 1] == '\n')
+ buf[len - 1] = '\0';
+ return (buf);
+}
+
+struct disklabel *
+promptfordisk(name)
+ char *name;
+{
+ struct disklabel *dp = &disk;
+ struct field *fp;
+ int i;
+ char buf[BUFSIZ], **tp, *cp;
+
+ strncpy(dp->d_typename, name, sizeof(dp->d_typename));
+ fprintf(stderr,
+ "%s: unknown disk type, want to supply parameters (y/n)? ",
+ name);
+ (void) mygets(buf);
+ if (*buf != 'y')
+ return ((struct disklabel *)0);
+ for (;;) {
+ fprintf(stderr, "Disk/controller type (%s)? ", dktypenames[1]);
+ (void) mygets(buf);
+ if (buf[0] == 0) {
+ dp->d_type = 1;
+ break;
+ }
+ if ((i = gettype(buf, dktypenames)) >= 0) {
+ dp->d_type = i;
+ break;
+ }
+ fprintf(stderr, "%s: unrecognized controller type\n", buf);
+ fprintf(stderr, "use one of:\n", buf);
+ for (tp = dktypenames; *tp; tp++)
+ if (index(*tp, ' ') == 0)
+ fprintf(stderr, "\t%s\n", *tp);
+ }
+gettype:
+ dp->d_flags = 0;
+ fprintf(stderr, "type (winchester|removable|simulated)? ");
+ (void) mygets(buf);
+ if (strcmp(buf, "removable") == 0)
+ dp->d_flags = D_REMOVABLE;
+ else if (strcmp(buf, "simulated") == 0)
+ dp->d_flags = D_RAMDISK;
+ else if (strcmp(buf, "winchester")) {
+ fprintf(stderr, "%s: bad disk type\n", buf);
+ goto gettype;
+ }
+ strncpy(dp->d_typename, buf, sizeof(dp->d_typename));
+ fprintf(stderr, "(type <cr> to get default value, if only one)\n");
+ if (dp->d_type == DTYPE_SMD)
+ fprintf(stderr, "Do %ss support bad144 bad block forwarding (yes)? ",
+ dp->d_typename);
+ (void) mygets(buf);
+ if (*buf != 'n')
+ dp->d_flags |= D_BADSECT;
+ for (fp = fields; fp->f_name != NULL; fp++) {
+again:
+ fprintf(stderr, "%s ", fp->f_name);
+ if (fp->f_defaults != NULL)
+ fprintf(stderr, "(%s)", fp->f_defaults);
+ fprintf(stderr, "? ");
+ cp = mygets(buf);
+ if (*cp == '\0') {
+ if (fp->f_defaults == NULL) {
+ fprintf(stderr, "no default value\n");
+ goto again;
+ }
+ cp = fp->f_defaults;
+ }
+ *fp->f_location = atol(cp);
+ if (*fp->f_location == 0) {
+ fprintf(stderr, "%s: bad value\n", cp);
+ goto again;
+ }
+ }
+ fprintf(stderr, "sectors/cylinder (%d)? ",
+ dp->d_nsectors * dp->d_ntracks);
+ (void) mygets(buf);
+ if (buf[0] == 0)
+ dp->d_secpercyl = dp->d_nsectors * dp->d_ntracks;
+ else
+ dp->d_secpercyl = atol(buf);
+ fprintf(stderr, "Drive-type-specific parameters, <cr> to terminate:\n");
+ for (i = 0; i < NDDATA; i++) {
+ fprintf(stderr, "d%d? ", i);
+ (void) mygets(buf);
+ if (buf[0] == 0)
+ break;
+ dp->d_drivedata[i] = atol(buf);
+ }
+ return (dp);
+}
+
+gettype(t, names)
+ char *t;
+ char **names;
+{
+ register char **nm;
+
+ for (nm = names; *nm; nm++)
+ if (ustrcmp(t, *nm) == 0)
+ return (nm - names);
+ if (isdigit(*t))
+ return (atoi(t));
+ return (-1);
+}
+
+ustrcmp(s1, s2)
+ register char *s1, *s2;
+{
+#define lower(c) (islower(c) ? (c) : tolower(c))
+
+ for (; *s1; s1++, s2++) {
+ if (*s1 == *s2)
+ continue;
+ if (isalpha(*s1) && isalpha(*s2) &&
+ lower(*s1) == lower(*s2))
+ continue;
+ return (*s2 - *s1);
+ }
+ return (0);
+}
diff --git a/usr.sbin/edquota/Makefile b/usr.sbin/edquota/Makefile
new file mode 100644
index 0000000..1eb5de9
--- /dev/null
+++ b/usr.sbin/edquota/Makefile
@@ -0,0 +1,6 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= edquota
+MAN8= edquota.8
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/edquota/edquota.8 b/usr.sbin/edquota/edquota.8
new file mode 100644
index 0000000..5909425
--- /dev/null
+++ b/usr.sbin/edquota/edquota.8
@@ -0,0 +1,174 @@
+.\" Copyright (c) 1983, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" Robert Elz at The University of Melbourne.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)edquota.8 8.1 (Berkeley) 6/6/93
+.\"
+.Dd June 6, 1993
+.Dt EDQUOTA 8
+.Os
+.Sh NAME
+.Nm edquota
+.Nd edit user quotas
+.Sh SYNOPSIS
+.Nm edquota
+.Op Fl u
+.Op Fl p Ar proto-username
+.Ar username ...
+.Nm edquota
+.Fl g
+.Op Fl p Ar proto-groupname
+.Ar groupname ...
+.Nm edquota
+.Fl t
+.Op Fl u
+.Nm edquota
+.Fl t
+.Fl g
+.br
+.Sh DESCRIPTION
+.Nm Edquota
+is a quota editor.
+By default, or if the
+.Fl u
+flag is specified,
+one or more users may be specified on the command line.
+For each user a temporary file is created
+with an
+.Tn ASCII
+representation of the current
+disk quotas for that user.
+The list of filesystems with user quotas is determined from
+.Pa /etc/fstab .
+An editor is invoked on the
+.Tn ASCII
+file.
+The editor invoked is
+.Xr vi 1
+unless the environment variable
+.Ev EDITOR
+specifies otherwise.
+.Pp
+The quotas may then be modified, new quotas added, etc.
+Setting a quota to zero indicates that no quota should be imposed.
+Setting a hard limit to one indicates that no allocations should
+be permitted.
+Setting a soft limit to one with a hard limit of zero
+indicates that allocations should be permitted only on
+a temporary basis (see
+.Fl t
+below).
+The current usage information in the file is for informational purposes;
+only the hard and soft limits can be changed.
+.Pp
+On leaving the editor,
+.Nm
+reads the temporary file and modifies the binary
+quota files to reflect the changes made.
+.Pp
+If the
+.Fl p
+option is specified,
+.Nm
+will duplicate the quotas of the prototypical user
+specified for each user specified.
+This is the normal mechanism used to
+initialize quotas for groups of users.
+If the user given to assign quotas to is a numerical uid
+range (e.g. 1000-2000), then
+.Nm
+will duplicate the quotas of the prototypical user
+for each uid in the range specified. This allows
+for easy setup of default quotas for a group of users.
+The uids in question do not have to be currently assigned in
+.Pa /etc/passwd .
+.Pp
+If the
+.Fl g
+flag is specified,
+.Nm
+is invoked to edit the quotas of
+one or more groups specified on the command line.
+The
+.Fl p
+flag can be specified in conjunction with
+the
+.Fl g
+flag to specify a prototypical group
+to be duplicated among the listed set of groups.
+.Pp
+Users are permitted to exceed their soft limits
+for a grace period that may be specified per filesystem.
+Once the grace period has expired,
+the soft limit is enforced as a hard limit.
+The default grace period for a filesystem is specified in
+.Pa /usr/include/ufs/ufs/quota.h .
+The
+.Fl t
+flag can be used to change the grace period.
+By default, or when invoked with the
+.Fl u
+flag,
+the grace period is set for all the filesystems with user
+quotas specified in
+.Pa /etc/fstab .
+When invoked with the
+.Fl g
+flag the grace period is
+set for all the filesystems with group quotas specified in
+.Pa /etc/fstab .
+The grace period may be specified in days, hours, minutes, or seconds.
+Setting a grace period to zero indicates that the default
+grace period should be imposed.
+Setting a grace period to one second indicates that no
+grace period should be granted.
+.Pp
+Only the super-user may edit quotas.
+.Sh FILES
+.Bl -tag -width quota.group -compact
+.It Pa quota.user
+at the filesystem root with user quotas
+.It Pa quota.group
+at the filesystem root with group quotas
+.It Pa /etc/fstab
+to find filesystem names and locations
+.El
+.Sh SEE ALSO
+.Xr quota 1 ,
+.Xr fstab 2 ,
+.Xr quotactl 2 ,
+.Xr quotacheck 8 ,
+.Xr quotaon 8 ,
+.Xr repquota 8
+.Sh DIAGNOSTICS
+Various messages about inaccessible files; self-explanatory.
diff --git a/usr.sbin/edquota/edquota.c b/usr.sbin/edquota/edquota.c
new file mode 100644
index 0000000..0110371
--- /dev/null
+++ b/usr.sbin/edquota/edquota.c
@@ -0,0 +1,754 @@
+/*
+ * Copyright (c) 1980, 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Robert Elz at The University of Melbourne.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 1980, 1990, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)edquota.c 8.1 (Berkeley) 6/6/93";
+#endif
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+/*
+ * Disk quota editor.
+ */
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/file.h>
+#include <sys/wait.h>
+#include <ufs/ufs/quota.h>
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <fstab.h>
+#include <grp.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include "pathnames.h"
+
+char *qfname = QUOTAFILENAME;
+char *qfextension[] = INITQFNAMES;
+char *quotagroup = QUOTAGROUP;
+char tmpfil[] = _PATH_TMP;
+
+struct quotause {
+ struct quotause *next;
+ long flags;
+ struct dqblk dqblk;
+ char fsname[MAXPATHLEN + 1];
+ char qfname[1]; /* actually longer */
+};
+#define FOUND 0x01
+
+int alldigits __P((char *s));
+int cvtatos __P((time_t, char *, time_t *));
+char *cvtstoa __P((time_t));
+int editit __P((char *));
+void freeprivs __P((struct quotause *));
+int getentry __P((char *, int));
+struct quotause *getprivs __P((long, int));
+int hasquota __P((struct fstab *, int, char **));
+void putprivs __P((long, int, struct quotause *));
+int readprivs __P((struct quotause *, char *));
+int readtimes __P((struct quotause *, char *));
+static void usage __P((void));
+int writetimes __P((struct quotause *, int, int));
+int writeprivs __P((struct quotause *, int, char *, int));
+
+int
+main(argc, argv)
+ register char **argv;
+ int argc;
+{
+ register struct quotause *qup, *protoprivs, *curprivs;
+ register long id, protoid;
+ register int quotatype, tmpfd;
+ register uid_t startuid, enduid;
+ char *protoname, *cp, ch;
+ int tflag = 0, pflag = 0;
+ char buf[30];
+
+ if (argc < 2)
+ usage();
+ if (getuid())
+ errx(1, "permission denied");
+ quotatype = USRQUOTA;
+ while ((ch = getopt(argc, argv, "ugtp:")) != -1) {
+ switch(ch) {
+ case 'p':
+ protoname = optarg;
+ pflag++;
+ break;
+ case 'g':
+ quotatype = GRPQUOTA;
+ break;
+ case 'u':
+ quotatype = USRQUOTA;
+ break;
+ case 't':
+ tflag++;
+ break;
+ default:
+ usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+ if (pflag) {
+ if ((protoid = getentry(protoname, quotatype)) == -1)
+ exit(1);
+ protoprivs = getprivs(protoid, quotatype);
+ for (qup = protoprivs; qup; qup = qup->next) {
+ qup->dqblk.dqb_btime = 0;
+ qup->dqblk.dqb_itime = 0;
+ }
+ while (argc-- > 0) {
+ if (isdigit(*argv[0]) &&
+ (cp = strchr(*argv, '-')) != NULL) {
+ *cp++ = '\0';
+ startuid = atoi(*argv);
+ enduid = atoi(cp);
+ if (enduid < startuid)
+ errx(1,
+ "ending uid (%d) must be >= starting uid (%d) when using uid ranges",
+ enduid, startuid);
+ for ( ; startuid <= enduid; startuid++) {
+ snprintf(buf, sizeof(buf), "%d",
+ startuid);
+ if ((id = getentry(buf, quotatype)) < 0)
+ continue;
+ putprivs(id, quotatype, protoprivs);
+ }
+ continue;
+ }
+ if ((id = getentry(*argv++, quotatype)) < 0)
+ continue;
+ putprivs(id, quotatype, protoprivs);
+ }
+ exit(0);
+ }
+ tmpfd = mkstemp(tmpfil);
+ fchown(tmpfd, getuid(), getgid());
+ if (tflag) {
+ protoprivs = getprivs(0, quotatype);
+ if (writetimes(protoprivs, tmpfd, quotatype) == 0)
+ exit(1);
+ if (editit(tmpfil) && readtimes(protoprivs, tmpfil))
+ putprivs(0, quotatype, protoprivs);
+ freeprivs(protoprivs);
+ close(tmpfd);
+ unlink(tmpfil);
+ exit(0);
+ }
+ for ( ; argc > 0; argc--, argv++) {
+ if ((id = getentry(*argv, quotatype)) == -1)
+ continue;
+ curprivs = getprivs(id, quotatype);
+ if (writeprivs(curprivs, tmpfd, *argv, quotatype) == 0)
+ continue;
+ if (editit(tmpfil) && readprivs(curprivs, tmpfil))
+ putprivs(id, quotatype, curprivs);
+ freeprivs(curprivs);
+ }
+ close(tmpfd);
+ unlink(tmpfil);
+ exit(0);
+}
+
+static void
+usage()
+{
+ fprintf(stderr, "%s\n%s\n%s\n%s\n",
+ "usage: edquota [-u] [-p username] username ...",
+ " edquota -g [-p groupname] groupname ...",
+ " edquota [-u] -t",
+ " edquota -g -t");
+ exit(1);
+}
+
+/*
+ * This routine converts a name for a particular quota type to
+ * an identifier. This routine must agree with the kernel routine
+ * getinoquota as to the interpretation of quota types.
+ */
+int
+getentry(name, quotatype)
+ char *name;
+ int quotatype;
+{
+ struct passwd *pw;
+ struct group *gr;
+
+ if (alldigits(name))
+ return (atoi(name));
+ switch(quotatype) {
+ case USRQUOTA:
+ if ((pw = getpwnam(name)))
+ return (pw->pw_uid);
+ warnx("%s: no such user", name);
+ break;
+ case GRPQUOTA:
+ if ((gr = getgrnam(name)))
+ return (gr->gr_gid);
+ warnx("%s: no such group", name);
+ break;
+ default:
+ warnx("%d: unknown quota type", quotatype);
+ break;
+ }
+ sleep(1);
+ return (-1);
+}
+
+/*
+ * Collect the requested quota information.
+ */
+struct quotause *
+getprivs(id, quotatype)
+ register long id;
+ int quotatype;
+{
+ register struct fstab *fs;
+ register struct quotause *qup, *quptail;
+ struct quotause *quphead;
+ int qcmd, qupsize, fd;
+ char *qfpathname;
+ static int warned = 0;
+
+ setfsent();
+ quphead = (struct quotause *)0;
+ qcmd = QCMD(Q_GETQUOTA, quotatype);
+ while ((fs = getfsent())) {
+ if (strcmp(fs->fs_vfstype, "ufs"))
+ continue;
+ if (!hasquota(fs, quotatype, &qfpathname))
+ continue;
+ qupsize = sizeof(*qup) + strlen(qfpathname);
+ if ((qup = (struct quotause *)malloc(qupsize)) == NULL)
+ errx(2, "out of memory");
+ if (quotactl(fs->fs_file, qcmd, id, &qup->dqblk) != 0) {
+ if (errno == EOPNOTSUPP && !warned) {
+ warned++;
+ warnx("warning: quotas are not compiled into this kernel");
+ sleep(3);
+ }
+ if ((fd = open(qfpathname, O_RDONLY)) < 0) {
+ fd = open(qfpathname, O_RDWR|O_CREAT, 0640);
+ if (fd < 0 && errno != ENOENT) {
+ warn("%s", qfpathname);
+ free(qup);
+ continue;
+ }
+ warnx("creating quota file %s", qfpathname);
+ sleep(3);
+ (void) fchown(fd, getuid(),
+ getentry(quotagroup, GRPQUOTA));
+ (void) fchmod(fd, 0640);
+ }
+ lseek(fd, (long)(id * sizeof(struct dqblk)), L_SET);
+ switch (read(fd, &qup->dqblk, sizeof(struct dqblk))) {
+ case 0: /* EOF */
+ /*
+ * Convert implicit 0 quota (EOF)
+ * into an explicit one (zero'ed dqblk)
+ */
+ bzero((caddr_t)&qup->dqblk,
+ sizeof(struct dqblk));
+ break;
+
+ case sizeof(struct dqblk): /* OK */
+ break;
+
+ default: /* ERROR */
+ warn("read error in %s", qfpathname);
+ close(fd);
+ free(qup);
+ continue;
+ }
+ close(fd);
+ }
+ strcpy(qup->qfname, qfpathname);
+ strcpy(qup->fsname, fs->fs_file);
+ if (quphead == NULL)
+ quphead = qup;
+ else
+ quptail->next = qup;
+ quptail = qup;
+ qup->next = 0;
+ }
+ endfsent();
+ return (quphead);
+}
+
+/*
+ * Store the requested quota information.
+ */
+void
+putprivs(id, quotatype, quplist)
+ long id;
+ int quotatype;
+ struct quotause *quplist;
+{
+ register struct quotause *qup;
+ int qcmd, fd;
+
+ qcmd = QCMD(Q_SETQUOTA, quotatype);
+ for (qup = quplist; qup; qup = qup->next) {
+ if (quotactl(qup->fsname, qcmd, id, &qup->dqblk) == 0)
+ continue;
+ if ((fd = open(qup->qfname, O_WRONLY)) < 0) {
+ warn("%s", qup->qfname);
+ } else {
+ lseek(fd, (long)id * (long)sizeof (struct dqblk), 0);
+ if (write(fd, &qup->dqblk, sizeof (struct dqblk)) !=
+ sizeof (struct dqblk)) {
+ warn("%s", qup->qfname);
+ }
+ close(fd);
+ }
+ }
+}
+
+/*
+ * Take a list of priviledges and get it edited.
+ */
+int
+editit(tmpfile)
+ char *tmpfile;
+{
+ long omask;
+ int pid, stat;
+
+ omask = sigblock(sigmask(SIGINT)|sigmask(SIGQUIT)|sigmask(SIGHUP));
+ top:
+ if ((pid = fork()) < 0) {
+
+ if (errno == EPROCLIM) {
+ warnx("you have too many processes");
+ return(0);
+ }
+ if (errno == EAGAIN) {
+ sleep(1);
+ goto top;
+ }
+ warn("fork");
+ return (0);
+ }
+ if (pid == 0) {
+ register char *ed;
+
+ sigsetmask(omask);
+ setgid(getgid());
+ setuid(getuid());
+ if ((ed = getenv("EDITOR")) == (char *)0)
+ ed = _PATH_VI;
+ execlp(ed, ed, tmpfile, 0);
+ err(1, "%s", ed);
+ }
+ waitpid(pid, &stat, 0);
+ sigsetmask(omask);
+ if (!WIFEXITED(stat) || WEXITSTATUS(stat) != 0)
+ return (0);
+ return (1);
+}
+
+/*
+ * Convert a quotause list to an ASCII file.
+ */
+int
+writeprivs(quplist, outfd, name, quotatype)
+ struct quotause *quplist;
+ int outfd;
+ char *name;
+ int quotatype;
+{
+ register struct quotause *qup;
+ FILE *fd;
+
+ ftruncate(outfd, 0);
+ lseek(outfd, 0, L_SET);
+ if ((fd = fdopen(dup(outfd), "w")) == NULL)
+ err(1, "%s", tmpfil);
+ fprintf(fd, "Quotas for %s %s:\n", qfextension[quotatype], name);
+ for (qup = quplist; qup; qup = qup->next) {
+ fprintf(fd, "%s: %s %lu, limits (soft = %lu, hard = %lu)\n",
+ qup->fsname, "blocks in use:",
+ (unsigned long)(dbtob(qup->dqblk.dqb_curblocks) / 1024),
+ (unsigned long)(dbtob(qup->dqblk.dqb_bsoftlimit) / 1024),
+ (unsigned long)(dbtob(qup->dqblk.dqb_bhardlimit) / 1024));
+ fprintf(fd, "%s %lu, limits (soft = %lu, hard = %lu)\n",
+ "\tinodes in use:", qup->dqblk.dqb_curinodes,
+ qup->dqblk.dqb_isoftlimit, qup->dqblk.dqb_ihardlimit);
+ }
+ fclose(fd);
+ return (1);
+}
+
+/*
+ * Merge changes to an ASCII file into a quotause list.
+ */
+int
+readprivs(quplist, inname)
+ struct quotause *quplist;
+ char *inname;
+{
+ register struct quotause *qup;
+ FILE *fd;
+ int cnt;
+ register char *cp;
+ struct dqblk dqblk;
+ char *fsp, line1[BUFSIZ], line2[BUFSIZ];
+
+ fd = fopen(inname, "r");
+ if (fd == NULL) {
+ warnx("can't re-read temp file!!");
+ return (0);
+ }
+ /*
+ * Discard title line, then read pairs of lines to process.
+ */
+ (void) fgets(line1, sizeof (line1), fd);
+ while (fgets(line1, sizeof (line1), fd) != NULL &&
+ fgets(line2, sizeof (line2), fd) != NULL) {
+ if ((fsp = strtok(line1, " \t:")) == NULL) {
+ warnx("%s: bad format", line1);
+ return (0);
+ }
+ if ((cp = strtok((char *)0, "\n")) == NULL) {
+ warnx("%s: %s: bad format", fsp, &fsp[strlen(fsp) + 1]);
+ return (0);
+ }
+ cnt = sscanf(cp,
+ " blocks in use: %lu, limits (soft = %lu, hard = %lu)",
+ &dqblk.dqb_curblocks, &dqblk.dqb_bsoftlimit,
+ &dqblk.dqb_bhardlimit);
+ if (cnt != 3) {
+ warnx("%s:%s: bad format", fsp, cp);
+ return (0);
+ }
+ dqblk.dqb_curblocks = btodb(dqblk.dqb_curblocks * 1024);
+ dqblk.dqb_bsoftlimit = btodb(dqblk.dqb_bsoftlimit * 1024);
+ dqblk.dqb_bhardlimit = btodb(dqblk.dqb_bhardlimit * 1024);
+ if ((cp = strtok(line2, "\n")) == NULL) {
+ warnx("%s: %s: bad format", fsp, line2);
+ return (0);
+ }
+ cnt = sscanf(cp,
+ "\tinodes in use: %lu, limits (soft = %lu, hard = %lu)",
+ &dqblk.dqb_curinodes, &dqblk.dqb_isoftlimit,
+ &dqblk.dqb_ihardlimit);
+ if (cnt != 3) {
+ warnx("%s: %s: bad format", fsp, line2);
+ return (0);
+ }
+ for (qup = quplist; qup; qup = qup->next) {
+ if (strcmp(fsp, qup->fsname))
+ continue;
+ /*
+ * Cause time limit to be reset when the quota
+ * is next used if previously had no soft limit
+ * or were under it, but now have a soft limit
+ * and are over it.
+ */
+ if (dqblk.dqb_bsoftlimit &&
+ qup->dqblk.dqb_curblocks >= dqblk.dqb_bsoftlimit &&
+ (qup->dqblk.dqb_bsoftlimit == 0 ||
+ qup->dqblk.dqb_curblocks <
+ qup->dqblk.dqb_bsoftlimit))
+ qup->dqblk.dqb_btime = 0;
+ if (dqblk.dqb_isoftlimit &&
+ qup->dqblk.dqb_curinodes >= dqblk.dqb_isoftlimit &&
+ (qup->dqblk.dqb_isoftlimit == 0 ||
+ qup->dqblk.dqb_curinodes <
+ qup->dqblk.dqb_isoftlimit))
+ qup->dqblk.dqb_itime = 0;
+ qup->dqblk.dqb_bsoftlimit = dqblk.dqb_bsoftlimit;
+ qup->dqblk.dqb_bhardlimit = dqblk.dqb_bhardlimit;
+ qup->dqblk.dqb_isoftlimit = dqblk.dqb_isoftlimit;
+ qup->dqblk.dqb_ihardlimit = dqblk.dqb_ihardlimit;
+ qup->flags |= FOUND;
+ if (dqblk.dqb_curblocks == qup->dqblk.dqb_curblocks &&
+ dqblk.dqb_curinodes == qup->dqblk.dqb_curinodes)
+ break;
+ warnx("%s: cannot change current allocation", fsp);
+ break;
+ }
+ }
+ fclose(fd);
+ /*
+ * Disable quotas for any filesystems that have not been found.
+ */
+ for (qup = quplist; qup; qup = qup->next) {
+ if (qup->flags & FOUND) {
+ qup->flags &= ~FOUND;
+ continue;
+ }
+ qup->dqblk.dqb_bsoftlimit = 0;
+ qup->dqblk.dqb_bhardlimit = 0;
+ qup->dqblk.dqb_isoftlimit = 0;
+ qup->dqblk.dqb_ihardlimit = 0;
+ }
+ return (1);
+}
+
+/*
+ * Convert a quotause list to an ASCII file of grace times.
+ */
+int
+writetimes(quplist, outfd, quotatype)
+ struct quotause *quplist;
+ int outfd;
+ int quotatype;
+{
+ register struct quotause *qup;
+ FILE *fd;
+
+ ftruncate(outfd, 0);
+ lseek(outfd, 0, L_SET);
+ if ((fd = fdopen(dup(outfd), "w")) == NULL)
+ err(1, "%s", tmpfil);
+ fprintf(fd, "Time units may be: days, hours, minutes, or seconds\n");
+ fprintf(fd, "Grace period before enforcing soft limits for %ss:\n",
+ qfextension[quotatype]);
+ for (qup = quplist; qup; qup = qup->next) {
+ fprintf(fd, "%s: block grace period: %s, ",
+ qup->fsname, cvtstoa(qup->dqblk.dqb_btime));
+ fprintf(fd, "file grace period: %s\n",
+ cvtstoa(qup->dqblk.dqb_itime));
+ }
+ fclose(fd);
+ return (1);
+}
+
+/*
+ * Merge changes of grace times in an ASCII file into a quotause list.
+ */
+int
+readtimes(quplist, inname)
+ struct quotause *quplist;
+ char *inname;
+{
+ register struct quotause *qup;
+ FILE *fd;
+ int cnt;
+ register char *cp;
+ time_t itime, btime, iseconds, bseconds;
+ char *fsp, bunits[10], iunits[10], line1[BUFSIZ];
+
+ fd = fopen(inname, "r");
+ if (fd == NULL) {
+ warnx("can't re-read temp file!!");
+ return (0);
+ }
+ /*
+ * Discard two title lines, then read lines to process.
+ */
+ (void) fgets(line1, sizeof (line1), fd);
+ (void) fgets(line1, sizeof (line1), fd);
+ while (fgets(line1, sizeof (line1), fd) != NULL) {
+ if ((fsp = strtok(line1, " \t:")) == NULL) {
+ warnx("%s: bad format", line1);
+ return (0);
+ }
+ if ((cp = strtok((char *)0, "\n")) == NULL) {
+ warnx("%s: %s: bad format", fsp, &fsp[strlen(fsp) + 1]);
+ return (0);
+ }
+ cnt = sscanf(cp,
+ " block grace period: %ld %s file grace period: %ld %s",
+ &btime, bunits, &itime, iunits);
+ if (cnt != 4) {
+ warnx("%s:%s: bad format", fsp, cp);
+ return (0);
+ }
+ if (cvtatos(btime, bunits, &bseconds) == 0)
+ return (0);
+ if (cvtatos(itime, iunits, &iseconds) == 0)
+ return (0);
+ for (qup = quplist; qup; qup = qup->next) {
+ if (strcmp(fsp, qup->fsname))
+ continue;
+ qup->dqblk.dqb_btime = bseconds;
+ qup->dqblk.dqb_itime = iseconds;
+ qup->flags |= FOUND;
+ break;
+ }
+ }
+ fclose(fd);
+ /*
+ * reset default grace periods for any filesystems
+ * that have not been found.
+ */
+ for (qup = quplist; qup; qup = qup->next) {
+ if (qup->flags & FOUND) {
+ qup->flags &= ~FOUND;
+ continue;
+ }
+ qup->dqblk.dqb_btime = 0;
+ qup->dqblk.dqb_itime = 0;
+ }
+ return (1);
+}
+
+/*
+ * Convert seconds to ASCII times.
+ */
+char *
+cvtstoa(time)
+ time_t time;
+{
+ static char buf[20];
+
+ if (time % (24 * 60 * 60) == 0) {
+ time /= 24 * 60 * 60;
+ sprintf(buf, "%ld day%s", time, time == 1 ? "" : "s");
+ } else if (time % (60 * 60) == 0) {
+ time /= 60 * 60;
+ sprintf(buf, "%ld hour%s", time, time == 1 ? "" : "s");
+ } else if (time % 60 == 0) {
+ time /= 60;
+ sprintf(buf, "%ld minute%s", time, time == 1 ? "" : "s");
+ } else
+ sprintf(buf, "%ld second%s", time, time == 1 ? "" : "s");
+ return (buf);
+}
+
+/*
+ * Convert ASCII input times to seconds.
+ */
+int
+cvtatos(time, units, seconds)
+ time_t time;
+ char *units;
+ time_t *seconds;
+{
+
+ if (bcmp(units, "second", 6) == 0)
+ *seconds = time;
+ else if (bcmp(units, "minute", 6) == 0)
+ *seconds = time * 60;
+ else if (bcmp(units, "hour", 4) == 0)
+ *seconds = time * 60 * 60;
+ else if (bcmp(units, "day", 3) == 0)
+ *seconds = time * 24 * 60 * 60;
+ else {
+ printf("%s: bad units, specify %s\n", units,
+ "days, hours, minutes, or seconds");
+ return (0);
+ }
+ return (1);
+}
+
+/*
+ * Free a list of quotause structures.
+ */
+void
+freeprivs(quplist)
+ struct quotause *quplist;
+{
+ register struct quotause *qup, *nextqup;
+
+ for (qup = quplist; qup; qup = nextqup) {
+ nextqup = qup->next;
+ free(qup);
+ }
+}
+
+/*
+ * Check whether a string is completely composed of digits.
+ */
+int
+alldigits(s)
+ register char *s;
+{
+ register c;
+
+ c = *s++;
+ do {
+ if (!isdigit(c))
+ return (0);
+ } while ((c = *s++));
+ return (1);
+}
+
+/*
+ * Check to see if a particular quota is to be enabled.
+ */
+int
+hasquota(fs, type, qfnamep)
+ register struct fstab *fs;
+ int type;
+ char **qfnamep;
+{
+ register char *opt;
+ char *cp;
+ static char initname, usrname[100], grpname[100];
+ static char buf[BUFSIZ];
+
+ if (!initname) {
+ sprintf(usrname, "%s%s", qfextension[USRQUOTA], qfname);
+ sprintf(grpname, "%s%s", qfextension[GRPQUOTA], qfname);
+ initname = 1;
+ }
+ strcpy(buf, fs->fs_mntops);
+ for (opt = strtok(buf, ","); opt; opt = strtok(NULL, ",")) {
+ if ((cp = index(opt, '=')))
+ *cp++ = '\0';
+ if (type == USRQUOTA && strcmp(opt, usrname) == 0)
+ break;
+ if (type == GRPQUOTA && strcmp(opt, grpname) == 0)
+ break;
+ }
+ if (!opt)
+ return (0);
+ if (cp) {
+ *qfnamep = cp;
+ return (1);
+ }
+ (void) sprintf(buf, "%s/%s.%s", fs->fs_file, qfname, qfextension[type]);
+ *qfnamep = buf;
+ return (1);
+}
diff --git a/usr.sbin/edquota/pathnames.h b/usr.sbin/edquota/pathnames.h
new file mode 100644
index 0000000..92546f7
--- /dev/null
+++ b/usr.sbin/edquota/pathnames.h
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)pathnames.h 8.1 (Berkeley) 6/6/93
+ */
+
+#include <paths.h>
+
+#undef _PATH_TMP
+#define _PATH_TMP "/tmp/EdP.aXXXXX"
diff --git a/usr.sbin/fdcontrol/Makefile b/usr.sbin/fdcontrol/Makefile
new file mode 100644
index 0000000..068cade
--- /dev/null
+++ b/usr.sbin/fdcontrol/Makefile
@@ -0,0 +1,4 @@
+PROG = fdcontrol
+MAN8 = fdcontrol.8
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/fdcontrol/fdcontrol.8 b/usr.sbin/fdcontrol/fdcontrol.8
new file mode 100644
index 0000000..ace5cf2
--- /dev/null
+++ b/usr.sbin/fdcontrol/fdcontrol.8
@@ -0,0 +1,98 @@
+.\"
+.\" Copyright (C) 1994 by Joerg Wunsch, Dresden
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY
+.\" EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+.\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+.\" OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+.\" BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+.\" LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+.\" USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+.\" DAMAGE.
+.\"
+.Dd May 22, 1994
+.Os
+.Dt FDCONTROL 8
+.Sh NAME
+.Nm fdcontrol
+.Nd modify floppy disk parameters
+.Sh SYNOPSIS
+.Nm fdcontrol
+.Op Fl d Ar 0|1
+.Ar device
+.Nm fdcontrol
+.Op Fl s
+.Ar device
+.Sh DESCRIPTION
+.Nm Fdcontrol
+allows the modification of the run-time behavior of the floppy
+disk device specified by
+.Ar device .
+.Ar Device
+should be a character device.
+.Pp
+.Nm Fdcontrol
+currently supports the specification of device parameters for the
+floppy disk drive
+.Po
+.Fl s ,
+also default mode
+.Pc ,
+or it allows the modification of the driver debug level, in case the
+floppy driver has been compiled into the kernel with the
+.Em DEBUG
+option set
+.Pq Fl d .
+.Pp
+Since the implications of such actions are considered harmful, the
+underlying
+.Xr ioctl 2
+command is restricted to the super-user.
+.Pp
+When requesting a new parameter specification, the command asks the
+user for each individual tunable parameter, defaulting to the
+currently used value.
+.Sh DIAGNOSTICS
+Error codes for the underlying
+.Xr ioctl 2
+commands are printed by the
+.Xr perror 3
+facility.
+.Sh BUGS
+The
+.Nm
+command is currently under development. It's user interface is rather
+silly and likely to change in future, options should be provided to
+allow anything being modified from the command line.
+.Pp
+The driver does actually support only two debug levels
+.Pq 0 and 1 ,
+where debug level 1 will generate huge amounts of output. It is likely
+to overflow the syslog if not used with extreme care.
+.Sh SEE ALSO
+.Xr ioctl 2 ,
+.Xr perror 3 ,
+.Xr fdc 4
+.Sh HISTORY
+.Nm Fdcontrol
+is currently under development. It's user interface and overall
+functionality are subjects to future improvements and changes.
+.Sh AUTHOR
+The program has been contributed by
+.if n Joerg Wunsch,
+.if t J\(:org Wunsch,
+Dresden.
diff --git a/usr.sbin/fdcontrol/fdcontrol.c b/usr.sbin/fdcontrol/fdcontrol.c
new file mode 100644
index 0000000..c914b93
--- /dev/null
+++ b/usr.sbin/fdcontrol/fdcontrol.c
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 1994 by Joerg Wunsch, Dresden
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <machine/ioctl_fd.h>
+#include <sys/file.h>
+
+int
+getnumber(void)
+{
+ int i;
+ char b[80];
+
+ fgets(b, 80, stdin);
+ if(b[0] == '\n') return -1;
+
+ sscanf(b, " %i", &i);
+ return i;
+}
+
+void
+usage(void)
+{
+ fprintf(stderr, "usage: fdcontrol [-d 0|1] | [-s] device-node\n");
+ exit(2);
+}
+
+
+#define ask(name, fmt) \
+printf(#name "? [" fmt "]: ", ft.name); fflush(stdout); \
+if((i = getnumber()) != -1) ft.name = i
+
+int
+main(int argc, char **argv)
+{
+ struct fd_type ft;
+ int fd, i;
+ int debug = -1, settype = 1;
+
+ while((i = getopt(argc, argv, "d:s")) != -1)
+ switch(i)
+ {
+ case 'd':
+ debug = atoi(optarg);
+ settype = 0;
+ break;
+
+ case 's':
+ debug = -1;
+ settype = 1;
+ break;
+
+ case '?':
+ default:
+ usage();
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if(argc != 1)
+ usage();
+
+ if((fd = open(argv[0], 0)) < 0)
+ {
+ warn("open(floppy)");
+ return 1;
+ }
+
+ if(debug != -1)
+ {
+ if(ioctl(fd, FD_DEBUG, &debug) < 0)
+ {
+ warn("ioctl(FD_DEBUG)");
+ return 1;
+ }
+ return 0;
+ }
+
+ if(settype)
+ {
+ if(ioctl(fd, FD_GTYPE, &ft) < 0)
+ {
+ warn("ioctl(FD_GTYPE)");
+ return 1;
+ }
+
+ ask(sectrac, "%d");
+ ask(secsize, "%d");
+ ask(datalen, "0x%x");
+ ask(gap, "0x%x");
+ ask(tracks, "%d");
+ ask(size, "%d");
+ ask(steptrac, "%d");
+ ask(trans, "%d");
+ ask(heads, "%d");
+ ask(f_gap, "0x%x");
+ ask(f_inter, "%d");
+
+ if(ioctl(fd, FD_STYPE, &ft) < 0)
+ {
+ warn("ioctl(FD_STYPE)");
+ return 1;
+ }
+ return 0;
+ }
+
+ return 0;
+}
diff --git a/usr.sbin/fdformat/Makefile b/usr.sbin/fdformat/Makefile
new file mode 100644
index 0000000..e489ebc
--- /dev/null
+++ b/usr.sbin/fdformat/Makefile
@@ -0,0 +1,9 @@
+#
+PROG = fdformat
+
+# the -I's seem to be confusing, but necessery this way
+# (so the right <unistd.h> will be found in /usr/include, and the
+# "../i386/isa/ic/nec765.h" included from fdreg.h is accessible, too)
+CFLAGS+= -Wall
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/fdformat/fdformat.1 b/usr.sbin/fdformat/fdformat.1
new file mode 100644
index 0000000..e4bcff4
--- /dev/null
+++ b/usr.sbin/fdformat/fdformat.1
@@ -0,0 +1,157 @@
+.\" Copyright (C) 1993, 1994, 1995 by Joerg Wunsch, Dresden
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS
+.\" OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+.\" WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+.\" DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT,
+.\" INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+.\" (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+.\" SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+.\" STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+.\" IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+.\" POSSIBILITY OF SUCH DAMAGE.
+.\"
+.Dd September 16, 1993
+.Os
+.Dt FDFORMAT 1
+.Sh NAME
+.Nm fdformat
+.Nd format floppy disks
+.Sh SYNOPSIS
+.Nm fdformat
+.Op Fl q
+.Op Fl v | Fl n
+.Op Fl f Ar capacity
+.Op Fl c Ar cyls
+.Op Fl s Ar secs
+.Op Fl h Ar heads
+.br
+.Op Fl r Ar rate
+.Op Fl g Ar gap3len
+.Op Fl i Ar intleave
+.Op Fl S Ar secshft
+.Op Fl F Ar fillbyte
+.Op Fl t Ar steps_per_track
+.Ar device_name
+.Sh DESCRIPTION
+.Nm Fdformat
+formats a floppy disk at device
+.Ar device_name .
+.Ar Device_name
+should be a character device; it may be given either with a full path
+name of a raw device node for a floppy disk drive
+.Pq e.\ g. Pa /dev/rfd0 ,
+or default name in an abbreviated form
+.Pq e.\ g. Em fd0 .
+In the latter case, the name is constructed by prepending
+.Pa /dev/r
+and appending a
+.Em .capacity
+to the
+.Ar device_name .
+Note that any geometry constraints of the device node
+.Pq minor device number
+are meaningless, since they're overridden by
+.Nm fdformat .
+.Pp
+The options are as follows:
+.Bl -tag -width 10n -offset indent
+.It Fl q
+Suppress any normal output from the command, and don't ask the
+user for a confirmation whether to format the floppy disk at
+.Ar device_name .
+.It Fl f Ar capacity
+The normal way to specify the desired formatting parameters.
+.Ar Capacity
+is the number of kilobytes to format. Valid choices are 360, 720, 800, 820,
+1200, 1440, 1480 or 1720.
+.It Fl n
+Don't verify floppy after formatting.
+.It Fl v
+Don't format, verify only.
+.It Fl c Ar cyls
+Number of cylinders: 40 or 80.
+.It Fl s Ar secs
+Number of sectors per track: 9, 10, 15 or 18.
+.It Fl h Ar heads
+Number of floppy heads: 1 or 2.
+.It Fl r Ar rate
+Data rate: 250, 300 or 500 kbps.
+.It Fl g Ar gap3len
+Gap length.
+.It Fl i Ar intleave
+Interleave factor.
+.It Fl S Ar secshft
+Sector size: 0=128, 1=256, 2=512 bytes.
+.It Fl F Ar fillbyte
+Fill byte.
+.It Fl t Ar steps_per_track
+Number of steps per track.
+An alternate method to specify the geometry data to write to the floppy disk.
+.El
+
+If the
+.Fl q
+flag has not been specified, the user is asked for a confirmation
+of the intended formatting process. In order to continue, an answer
+of
+.Dq y
+must be given.
+.Pp
+Note that
+.Nm
+does only perform low-level formatting. In case you wish to create
+a file system on the medium, see the commands
+.Xr newfs 8
+for an
+.Em ufs
+file system, or
+.Xr mkdosfs 1
+for an
+.Em MS-DOS (FAT)
+file system.
+.Sh DIAGNOSTICS
+Unless
+.Fl q
+has been specified, a single letter is printed to standard output
+to inform the user about the progress of work.
+First, an
+.Sq Em F
+is printed when the track(s) is being formatted, then a
+.Sq Em V
+while it's being verified, and if an error has been detected, it
+will finally change to
+.Sq Em E .
+.Pp
+An exit status of 0 is returned upon successful operation. Exit status
+1 is returned on any errors during floppy formatting, and an exit status
+of 2 reflects invalid arguments given to the program (along with an
+appropriate information written to diagnostic output).
+.Sh SEE ALSO
+.Xr mkdosfs 1 ,
+.Xr fdc 4 ,
+.Xr newfs 8
+.Sh HISTORY
+.Nm Fdformat
+has been developed for 386BSD 0.1
+and upgraded to the new
+.Xr fd 4
+floppy disk driver. It later became part of the
+.Fx 1.1
+system.
+.Sh AUTHOR
+The program has been contributed by
+.if n Joerg Wunsch,
+.if t J\(:org Wunsch,
+Dresden, with changes by Serge Vakulenko and Andrey A. Chernov, Moscow.
diff --git a/usr.sbin/fdformat/fdformat.c b/usr.sbin/fdformat/fdformat.c
new file mode 100644
index 0000000..a2014be
--- /dev/null
+++ b/usr.sbin/fdformat/fdformat.c
@@ -0,0 +1,354 @@
+/*
+ * Copyright (C) 1992-1994 by Joerg Wunsch, Dresden
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+ * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * FreeBSD:
+ * format a floppy disk
+ *
+ * Added FD_GTYPE ioctl, verifying, proportional indicators.
+ * Serge Vakulenko, vak@zebub.msk.su
+ * Sat Dec 18 17:45:47 MSK 1993
+ *
+ * Final adaptation, change format/verify logic, add separate
+ * format gap/interleave values
+ * Andrew A. Chernov, ache@astral.msk.su
+ * Thu Jan 27 00:47:24 MSK 1994
+ */
+
+#include <ctype.h>
+#include <err.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <unistd.h>
+
+#include <machine/ioctl_fd.h>
+
+static void
+format_track(int fd, int cyl, int secs, int head, int rate,
+ int gaplen, int secsize, int fill,int interleave)
+{
+ struct fd_formb f;
+ register int i,j;
+ int il[FD_MAX_NSEC + 1];
+
+ memset(il,0,sizeof il);
+ for(j = 0, i = 1; i <= secs; i++) {
+ while(il[(j%secs)+1]) j++;
+ il[(j%secs)+1] = i;
+ j += interleave;
+ }
+
+ f.format_version = FD_FORMAT_VERSION;
+ f.head = head;
+ f.cyl = cyl;
+ f.transfer_rate = rate;
+
+ f.fd_formb_secshift = secsize;
+ f.fd_formb_nsecs = secs;
+ f.fd_formb_gaplen = gaplen;
+ f.fd_formb_fillbyte = fill;
+ for(i = 0; i < secs; i++) {
+ f.fd_formb_cylno(i) = cyl;
+ f.fd_formb_headno(i) = head;
+ f.fd_formb_secno(i) = il[i+1];
+ f.fd_formb_secsize(i) = secsize;
+ }
+ if(ioctl(fd, FD_FORM, (caddr_t)&f) < 0)
+ err(1, "ioctl(FD_FORM)");
+}
+
+static int
+verify_track(int fd, int track, int tracksize)
+{
+ static char *buf = 0;
+ static int bufsz = 0;
+ int fdopts = -1, ofdopts, rv = 0;
+
+ if (ioctl(fd, FD_GOPTS, &fdopts) < 0)
+ warn("warning: ioctl(FD_GOPTS)");
+ else {
+ ofdopts = fdopts;
+ fdopts |= FDOPT_NORETRY;
+ (void)ioctl(fd, FD_SOPTS, &fdopts);
+ }
+
+ if (bufsz < tracksize) {
+ if (buf)
+ free (buf);
+ bufsz = tracksize;
+ buf = 0;
+ }
+ if (! buf)
+ buf = malloc (bufsz);
+ if (! buf)
+ errx(2, "out of memory");
+ if (lseek (fd, (long) track*tracksize, 0) < 0)
+ rv = -1;
+ /* try twice reading it, without using the normal retrier */
+ else if (read (fd, buf, tracksize) != tracksize
+ && read (fd, buf, tracksize) != tracksize)
+ rv = -1;
+ if(fdopts != -1)
+ (void)ioctl(fd, FD_SOPTS, &ofdopts);
+ return (rv);
+}
+
+static const char *
+makename(const char *arg, const char *suffix)
+{
+ static char namebuff[20]; /* big enough for "/dev/rfd0a"... */
+
+ memset(namebuff, 0, 20);
+ if(*arg == '\0') /* ??? */
+ return arg;
+ if(*arg == '/') /* do not convert absolute pathnames */
+ return arg;
+ strcpy(namebuff, "/dev/r");
+ strncat(namebuff, arg, 3);
+ strcat(namebuff, suffix);
+ return namebuff;
+}
+
+static void
+usage (void)
+{
+ fprintf(stderr, "%s\n%s\n",
+ "usage: fdformat [-q] [-n | -v] [-f #] [-c #] [-s #] [-h #]",
+ " [-r #] [-g #] [-i #] [-S #] [-F #] [-t #] devname");
+ exit(2);
+}
+
+static int
+yes (void)
+{
+ char reply [256], *p;
+
+ reply[sizeof(reply)-1] = 0;
+ for (;;) {
+ fflush(stdout);
+ if (! fgets (reply, sizeof(reply)-1, stdin))
+ return (0);
+ for (p=reply; *p==' ' || *p=='\t'; ++p)
+ continue;
+ if (*p=='y' || *p=='Y')
+ return (1);
+ if (*p=='n' || *p=='N' || *p=='\n' || *p=='\r')
+ return (0);
+ printf("Answer `yes' or `no': ");
+ }
+}
+
+int
+main(int argc, char **argv)
+{
+ int format = -1, cyls = -1, secs = -1, heads = -1, intleave = -1;
+ int rate = -1, gaplen = -1, secsize = -1, steps = -1;
+ int fill = 0xf6, quiet = 0, verify = 1, verify_only = 0;
+ int fd, c, track, error, tracks_per_dot, bytes_per_track, errs;
+ const char *devname, *suffix;
+ struct fd_type fdt;
+
+ while((c = getopt(argc, argv, "f:c:s:h:r:g:S:F:t:i:qvn")) != -1)
+ switch(c) {
+ case 'f': /* format in kilobytes */
+ format = atoi(optarg);
+ break;
+
+ case 'c': /* # of cyls */
+ cyls = atoi(optarg);
+ break;
+
+ case 's': /* # of secs per track */
+ secs = atoi(optarg);
+ break;
+
+ case 'h': /* # of heads */
+ heads = atoi(optarg);
+ break;
+
+ case 'r': /* transfer rate, kilobyte/sec */
+ rate = atoi(optarg);
+ break;
+
+ case 'g': /* length of GAP3 to format with */
+ gaplen = atoi(optarg);
+ break;
+
+ case 'S': /* sector size shift factor (1 << S)*128 */
+ secsize = atoi(optarg);
+ break;
+
+ case 'F': /* fill byte, C-like notation allowed */
+ fill = (int)strtol(optarg, (char **)0, 0);
+ break;
+
+ case 't': /* steps per track */
+ steps = atoi(optarg);
+ break;
+
+ case 'i': /* interleave factor */
+ intleave = atoi(optarg);
+ break;
+
+ case 'q':
+ quiet = 1;
+ break;
+
+ case 'n':
+ verify = 0;
+ break;
+
+ case 'v':
+ verify = 1;
+ verify_only = 1;
+ break;
+
+ case '?': default:
+ usage();
+ }
+
+ if(optind != argc - 1)
+ usage();
+
+ switch(format) {
+ default:
+ errx(2, "bad floppy size: %dK", format);
+ case -1: suffix = ""; break;
+ case 360: suffix = ".360"; break;
+ case 720: suffix = ".720"; break;
+ case 800: suffix = ".800"; break;
+ case 820: suffix = ".820"; break;
+ case 1200: suffix = ".1200"; break;
+ case 1440: suffix = ".1440"; break;
+ case 1480: suffix = ".1480"; break;
+ case 1720: suffix = ".1720"; break;
+ }
+
+ devname = makename(argv[optind], suffix);
+
+ if((fd = open(devname, O_RDWR)) < 0)
+ err(1, "%s", devname);
+
+ if(ioctl(fd, FD_GTYPE, &fdt) < 0)
+ errx(1, "not a floppy disk: %s", devname);
+
+ switch(rate) {
+ case -1: break;
+ case 250: fdt.trans = FDC_250KBPS; break;
+ case 300: fdt.trans = FDC_300KBPS; break;
+ case 500: fdt.trans = FDC_500KBPS; break;
+ default:
+ errx(2, "invalid transfer rate: %d", rate);
+ }
+
+ if (cyls >= 0) fdt.tracks = cyls;
+ if (secs >= 0) fdt.sectrac = secs;
+ if (fdt.sectrac > FD_MAX_NSEC)
+ errx(2, "too many sectors per track, max value is %d", FD_MAX_NSEC);
+ if (heads >= 0) fdt.heads = heads;
+ if (gaplen >= 0) fdt.f_gap = gaplen;
+ if (secsize >= 0) fdt.secsize = secsize;
+ if (steps >= 0) fdt.steptrac = steps;
+ if (intleave >= 0) fdt.f_inter = intleave;
+
+ bytes_per_track = fdt.sectrac * (1<<fdt.secsize) * 128;
+ tracks_per_dot = fdt.tracks * fdt.heads / 40;
+
+ if (verify_only) {
+ if(!quiet)
+ printf("Verify %dK floppy `%s'.\n",
+ fdt.tracks * fdt.heads * bytes_per_track / 1024,
+ devname);
+ }
+ else if(!quiet) {
+ printf("Format %dK floppy `%s'? (y/n): ",
+ fdt.tracks * fdt.heads * bytes_per_track / 1024,
+ devname);
+ if(! yes ()) {
+ printf("Not confirmed.\n");
+ return 0;
+ }
+ }
+
+ /*
+ * Formatting.
+ */
+ if(!quiet) {
+ printf("Processing ----------------------------------------\r");
+ printf("Processing ");
+ fflush(stdout);
+ }
+
+ error = errs = 0;
+
+ for (track = 0; track < fdt.tracks * fdt.heads; track++) {
+ if (!verify_only) {
+ format_track(fd, track / fdt.heads, fdt.sectrac,
+ track % fdt.heads, fdt.trans, fdt.f_gap,
+ fdt.secsize, fill, fdt.f_inter);
+ if(!quiet && !((track + 1) % tracks_per_dot)) {
+ putchar('F');
+ fflush(stdout);
+ }
+ }
+ if (verify) {
+ if (verify_track(fd, track, bytes_per_track) < 0)
+ error = errs = 1;
+ if(!quiet && !((track + 1) % tracks_per_dot)) {
+ if (!verify_only)
+ putchar('\b');
+ if (error) {
+ putchar('E');
+ error = 0;
+ }
+ else
+ putchar('V');
+ fflush(stdout);
+ }
+ }
+ }
+ if(!quiet)
+ printf(" done.\n");
+
+ return errs;
+}
+/*
+ * Local Variables:
+ * c-indent-level: 8
+ * c-continued-statement-offset: 8
+ * c-continued-brace-offset: 0
+ * c-brace-offset: -8
+ * c-brace-imaginary-offset: 0
+ * c-argdecl-indent: 8
+ * c-label-offset: -8
+ * c++-hanging-braces: 1
+ * c++-access-specifier-offset: -8
+ * c++-empty-arglist-indent: 8
+ * c++-friend-offset: 0
+ * End:
+ */
diff --git a/usr.sbin/fdwrite/Makefile b/usr.sbin/fdwrite/Makefile
new file mode 100644
index 0000000..7af331d
--- /dev/null
+++ b/usr.sbin/fdwrite/Makefile
@@ -0,0 +1,16 @@
+#
+# ----------------------------------------------------------------------------
+# "THE BEER-WARE LICENSE" (Revision 42):
+# <phk@login.dkuug.dk> wrote this file. As long as you retain this notice you
+# can do whatever you want with this stuff. If we meet some day, and you think
+# this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp
+# ----------------------------------------------------------------------------
+#
+# $Id$
+#
+#
+
+PROG = fdwrite
+CFLAGS+= -Wall
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/fdwrite/fdwrite.1 b/usr.sbin/fdwrite/fdwrite.1
new file mode 100644
index 0000000..53cb70e
--- /dev/null
+++ b/usr.sbin/fdwrite/fdwrite.1
@@ -0,0 +1,121 @@
+.\"
+.\" ----------------------------------------------------------------------------
+.\" "THE BEER-WARE LICENSE" (Revision 42):
+.\" <phk@login.dkuug.dk> wrote this file. As long as you retain this notice you
+.\" can do whatever you want with this stuff. If we meet some day, and you think
+.\" this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp
+.\" ----------------------------------------------------------------------------
+.\"
+.\" $Id: fdwrite.1,v 1.5 1997/05/19 16:33:27 eivind Exp $
+.\"
+.\"
+.Dd September 16, 1993
+.Os FreeBSD
+.Dt FDWRITE 1
+.Sh NAME
+.Nm fdwrite
+.Nd format and write floppy disks
+.Sh SYNOPSIS
+.Nm fdwrite
+.Op Fl v
+.Op Fl y
+.Op Fl f Ar inputfile
+.Op Fl d Ar device
+.Sh DESCRIPTION
+.Nm Fdwrite
+formats and writes one and more floppy disks.
+Any floppy disk device capable of formatting can be used.
+
+.Nm Fdwrite
+will ask the user
+.Pq on /dev/tty
+to insert a new floppy and press return.
+The device will then be opened, and queried for its parameters,
+then each track will be formatted, written with data from the
+.Ar inputfile ,
+read back and compared.
+When the floppy disk if filled, the process is repeated, with the next disk.
+This continues until the program is interrupted or EOF is encountered on the
+.Ar inputfile .
+
+The options are as follows:
+.Bl -tag -width 10n -offset indent
+.It Fl v
+Toggle verbosity on stdout.
+Default is ``on''.
+After
+.Ar device
+is opened first time the format will be printed.
+During operation progress will be reported with the number of tracks
+remaining on the current floppy disk, and the letters I, Z, F, W,
+R and C, which indicates completion of Input, Zero-fill, Format
+Write, Read and Compare of current track respectively.
+.It Fl y
+Don't ask for presence of a floppy disk in the drive. This non-interactive flag
+is useful for shell scripts.
+.It Fl f Ar inputfile
+Input file to read. If none is given, stdin is assumed.
+.It Fl d Ar device
+The name of the floppy device to write to. Default is ``/dev/rfd0''.
+.El
+
+.Nm Fdwrite
+actually closes the
+.Ar device
+while it waits for the user to press return,
+it is thus quite possible to use the drive for other purposes at this
+time and later resume writing with the next floppy.
+
+The parameters returned from
+.Ar device
+are used for formatting.
+If custom formatting is needed, please use
+.Xr fdformat 1
+instead.
+
+.Sh EXAMPLE
+.Nm Fdwrite
+was planned as a tool to make life easier when writing a set of floppies,
+one such use could be to write a tar-archive:
+
+.ce 1
+tar cf - . | gzip -9 | fdwrite -d /dev/rfd0.1720 -v
+
+The main difference from using
+.Xr tar 1 's
+multivolume facility is of course the formatting of the floppies, which
+here is done on the fly,
+thus reducing the amount of work for the floppy-jockey.
+
+.Sh SEE ALSO
+.Xr fdformat 1 .
+.Sh HISTORY
+.Nm Fdwrite
+was written while waiting for ``make world'' to complete.
+Some of the code was taken from
+.Xr fdformat 1 .
+.Sh AUTHOR
+The program has been contributed by
+.An Poul-Henning Kamp Aq phk@login.dknet.dk
+.Sh BUGS
+Diagnostics are less than complete at present.
+
+If a floppy is sick, and the
+.Ar inputfile
+is seekable, it should ask the user to frisbee the disk, insert
+another, and rewind to the right spot and continue.
+
+This concept could be extended to cover non-seekable input also
+by employing a temporary file .
+
+An option (defaulting to zero) should allow the user to ask for
+retries in case of failure.
+
+At present a suitable tool for reading back a multivolume set
+of floppies is missing.
+Programs like
+.Xr tar 1
+for instance, will do the job, if the data has not been compressed.
+One can always trust
+.Xr dd 1
+to help out in this situation of course
diff --git a/usr.sbin/fdwrite/fdwrite.c b/usr.sbin/fdwrite/fdwrite.c
new file mode 100644
index 0000000..e0c176b
--- /dev/null
+++ b/usr.sbin/fdwrite/fdwrite.c
@@ -0,0 +1,194 @@
+/*
+ * ----------------------------------------------------------------------------
+ * "THE BEER-WARE LICENSE" (Revision 42):
+ * <phk@login.dkuug.dk> wrote this file. As long as you retain this notice you
+ * can do whatever you want with this stuff. If we meet some day, and you think
+ * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp
+ * ----------------------------------------------------------------------------
+ *
+ * $Id: fdwrite.c,v 1.6 1997/02/22 16:05:49 peter Exp $
+ *
+ */
+
+#include <ctype.h>
+#include <err.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <unistd.h>
+
+#include <machine/ioctl_fd.h>
+
+int
+format_track(int fd, int cyl, int secs, int head, int rate,
+ int gaplen, int secsize, int fill,int interleave)
+{
+ struct fd_formb f;
+ register int i,j;
+ int il[100];
+
+ memset(il,0,sizeof il);
+ for(j = 0, i = 1; i <= secs; i++) {
+ while(il[(j%secs)+1]) j++;
+ il[(j%secs)+1] = i;
+ j += interleave;
+ }
+
+ f.format_version = FD_FORMAT_VERSION;
+ f.head = head;
+ f.cyl = cyl;
+ f.transfer_rate = rate;
+
+ f.fd_formb_secshift = secsize;
+ f.fd_formb_nsecs = secs;
+ f.fd_formb_gaplen = gaplen;
+ f.fd_formb_fillbyte = fill;
+ for(i = 0; i < secs; i++) {
+ f.fd_formb_cylno(i) = cyl;
+ f.fd_formb_headno(i) = head;
+ f.fd_formb_secno(i) = il[i+1];
+ f.fd_formb_secsize(i) = secsize;
+ }
+ return ioctl(fd, FD_FORM, (caddr_t)&f);
+}
+
+static void
+usage()
+{
+ fprintf(stderr, "usage: fdwrite [-v] [-y] [-f inputfile] [-d device]\n");
+ exit(2);
+}
+
+int
+main(int argc, char **argv)
+{
+ int inputfd = -1, c, fdn = 0, i,j,fd;
+ int bpt, verbose=1, nbytes=0, track;
+ int interactive = 1;
+ char *device= "/dev/rfd0", *trackbuf = 0,*vrfybuf = 0;
+ struct fd_type fdt;
+ FILE *tty;
+
+ setbuf(stdout,0);
+ while((c = getopt(argc, argv, "d:f:vy")) != -1)
+ switch(c) {
+ case 'd': /* Which drive */
+ device = optarg;
+ break;
+
+ case 'f': /* input file */
+ if (inputfd >= 0)
+ close(inputfd);
+ inputfd = open(optarg,O_RDONLY);
+ if (inputfd < 0)
+ err(1, "%s", optarg);
+ break;
+
+ case 'v': /* Toggle verbosity */
+ verbose = !verbose;
+ break;
+
+ case 'y': /* Don't confirm? */
+ interactive = 0;
+ break;
+
+ case '?': default:
+ usage();
+ }
+
+ if (inputfd < 0)
+ inputfd = 0;
+
+ if (!isatty(1))
+ interactive = 0;
+
+ if(optind < argc)
+ usage();
+
+ tty = fopen("/dev/tty","r+");
+ if(!tty)
+ err(1, "/dev/tty");
+ setbuf(tty,0);
+
+ for(j=1;j > 0;) {
+ fdn++;
+ if (interactive) {
+ fprintf(tty,
+ "Please insert floppy #%d in drive %s and press return >",
+ fdn,device);
+ while(1) {
+ i = getc(tty);
+ if(i == '\n') break;
+ }
+ }
+
+ if((fd = open(device, O_RDWR)) < 0)
+ err(1, "%s", device);
+
+ if(ioctl(fd, FD_GTYPE, &fdt) < 0)
+ errx(1, "not a floppy disk: %s", device);
+
+ bpt = fdt.sectrac * (1<<fdt.secsize) * 128;
+ if(!trackbuf) {
+ trackbuf = malloc(bpt);
+ if(!trackbuf) errx(1, "malloc");
+ }
+ if(!vrfybuf) {
+ vrfybuf = malloc(bpt);
+ if(!vrfybuf) errx(1, "malloc");
+ }
+
+ if(fdn == 1) {
+ if(verbose) {
+ printf("Format: %d cylinders, %d heads, %d sectors, %d bytes = %dkb\n",
+ fdt.tracks,fdt.heads,fdt.sectrac,(1<<fdt.secsize) * 128,
+ fdt.tracks*bpt*fdt.heads/1024);
+
+ }
+ memset(trackbuf,0,bpt);
+ for(j=0;inputfd >= 0 && j<bpt;j+=i) {
+ if(!(i = read(inputfd,trackbuf+j,bpt-j))) {
+ close(inputfd);
+ inputfd = -1;
+ break;
+ }
+ nbytes += i;
+ }
+ }
+ for (track = 0; track < fdt.tracks * fdt.heads; track++) {
+ if(verbose) printf("\r%3d ",fdt.tracks * fdt.heads-track);
+ if(verbose) putc((j ? 'I':'Z'),stdout);
+ format_track(fd, track / fdt.heads, fdt.sectrac, track % fdt.heads,
+ fdt.trans, fdt.f_gap, fdt.secsize, 0xe6,
+ fdt.f_inter);
+ if(verbose) putc('F',stdout);
+
+ if (lseek (fd, (long) track*bpt, 0) < 0) err(1, "lseek");
+ if (write (fd, trackbuf, bpt) != bpt) err(1, "write");
+ if(verbose) putc('W',stdout);
+
+ if (lseek (fd, (long) track*bpt, 0) < 0) err(1, "lseek");
+ if (read (fd, vrfybuf, bpt) != bpt) err(1, "read");
+ if(verbose) putc('R',stdout);
+
+ if (memcmp(trackbuf,vrfybuf,bpt)) err(1, "compare");
+ if(verbose) putc('C',stdout);
+
+ memset(trackbuf,0,bpt);
+ for(j=0;inputfd >= 0 && j<bpt;j+=i) {
+ if(!(i = read(inputfd,trackbuf+j,bpt-j))) {
+ close(inputfd);
+ inputfd = -1;
+ break;
+ }
+ nbytes += i;
+ }
+ }
+ close(fd);
+ putc('\r',stdout);
+ }
+ if(verbose)
+ printf("%d bytes on %d flopp%s\n",nbytes,fdn,fdn==1?"y":"ies");
+ exit(0);
+}
diff --git a/usr.sbin/inetd/Makefile b/usr.sbin/inetd/Makefile
new file mode 100644
index 0000000..2682ea5
--- /dev/null
+++ b/usr.sbin/inetd/Makefile
@@ -0,0 +1,13 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= inetd
+MAN8= inetd.8
+MLINKS= inetd.8 inetd.conf.5
+
+COPTS+= -Wall -DLOGIN_CAP
+#COPTS+= -DSANITY_CHECK
+
+DPADD+= ${LIBUTIL}
+LDADD+= -lutil
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/inetd/inetd.8 b/usr.sbin/inetd/inetd.8
new file mode 100644
index 0000000..125c4e9
--- /dev/null
+++ b/usr.sbin/inetd/inetd.8
@@ -0,0 +1,511 @@
+.\" Copyright (c) 1985, 1991, 1993, 1994
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" from: @(#)inetd.8 8.3 (Berkeley) 4/13/94
+.\" $Id: inetd.8,v 1.16 1997/10/28 13:46:51 ache Exp $
+.\"
+.Dd February 7, 1996
+.Dt INETD 8
+.Os BSD 4.4
+.Sh NAME
+.Nm inetd
+.Nd internet
+.Dq super-server
+.Sh SYNOPSIS
+.Nm inetd
+.Op Fl d
+.Op Fl l
+.Op Fl R Ar rate
+.Op Fl a Ar address
+.Op Fl p Ar filename
+.Op Ar configuration file
+.Sh DESCRIPTION
+The
+.Nm
+program
+should be run at boot time by
+.Pa /etc/rc
+(see
+.Xr rc 8 ) .
+It then listens for connections on certain
+internet sockets. When a connection is found on one
+of its sockets, it decides what service the socket
+corresponds to, and invokes a program to service the request.
+The server program is invoked with the service socket
+as its standard input, output and error descriptors.
+After the program is
+finished,
+.Nm
+continues to listen on the socket (except in some cases which
+will be described below). Essentially,
+.Nm
+allows running one daemon to invoke several others,
+reducing load on the system.
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl d
+Turn on debugging.
+.It Fl l
+Turn on logging.
+.It Fl R Ar rate
+Specify the maximum number of times a service can be invoked
+in one minute; the default is 256.
+.It Fl a
+Specify a specific IP address to bind to.
+.It Fl p
+Specify an alternate file in which to store the process ID.
+.El
+.Pp
+Upon execution,
+.Nm
+reads its configuration information from a configuration
+file which, by default, is
+.Pa /etc/inetd.conf .
+There must be an entry for each field of the configuration
+file, with entries for each field separated by a tab or
+a space. Comments are denoted by a ``#'' at the beginning
+of a line. There must be an entry for each field. The
+fields of the configuration file are as follows:
+.Pp
+.Bd -unfilled -offset indent -compact
+service name
+socket type
+protocol
+{wait|nowait}[/max-child[/max-connections-per-ip-per-minute]]
+user[:group][/login-class]
+server program
+server program arguments
+.Ed
+.Pp
+To specify an
+.No Tn "ONC RPC" Ns -based
+service, the entry would contain these fields:
+.Pp
+.Bd -unfilled -offset indent -compact
+service name/version
+socket type
+rpc/protocol
+user[:group][/login-class]
+server program
+server program arguments
+.Ed
+.Pp
+There are two types of services that
+.Nm
+can start: standard and TCPMUX.
+A standard service has a well-known port assigned to it;
+it may be a service that implements an official Internet standard or is a
+BSD-specific service.
+As described in
+.Tn RFC 1078 ,
+TCPMUX services are nonstandard services that do not have a
+well-known port assigned to them.
+They are invoked from
+.Nm
+when a program connects to the
+.Dq tcpmux
+well-known port and specifies
+the service name.
+This feature is useful for adding locally-developed servers.
+.Pp
+The
+.Em service-name
+entry is the name of a valid service in
+the file
+.Pa /etc/services .
+For
+.Dq internal
+services (discussed below), the service
+name
+.Em must
+be the official name of the service (that is, the first entry in
+.Pa /etc/services ) .
+When used to specify an
+.No Tn "ONC RPC" Ns -based
+service, this field is a valid RPC service name in
+the file
+.Pa /etc/rpc .
+The part on the right of the
+.Dq /
+is the RPC version number. This
+can simply be a single numeric argument or a range of versions.
+A range is bounded by the low version to the high version -
+.Dq rusers/1-3 .
+For TCPMUX services, the value of the
+.Em service-name
+field consists of the string
+.Dq tcpmux
+followed by a slash and the
+locally-chosen service name.
+The service names listed in
+.Pa /etc/services
+and the name
+.Dq help
+are reserved.
+Try to choose unique names for your TCPMUX services by prefixing them with
+your organization's name and suffixing them with a version number.
+.Pp
+The
+.Em socket-type
+should be one of
+.Dq stream ,
+.Dq dgram ,
+.Dq raw ,
+.Dq rdm ,
+or
+.Dq seqpacket ,
+depending on whether the socket is a stream, datagram, raw,
+reliably delivered message, or sequenced packet socket.
+TCPMUX services must use
+.Dq stream .
+.Pp
+The
+.Em protocol
+must be a valid protocol as given in
+.Pa /etc/protocols .
+Examples might be
+.Dq tcp
+or
+.Dq udp .
+Rpc based services are specified with the
+.Dq rpc/tcp
+or
+.Dq rpc/udp
+service type.
+TCPMUX services must use
+.Dq tcp .
+.Pp
+The
+.Em wait/nowait
+entry specifies whether the server that is invoked by
+.Nm
+will take over
+the socket associated with the service access point, and thus whether
+.Nm
+should wait for the server to exit before listening for new service
+requests.
+Datagram servers must use
+.Dq wait ,
+as they are always invoked with the original datagram socket bound
+to the specified service address.
+These servers must read at least one datagram from the socket
+before exiting.
+If a datagram server connects
+to its peer, freeing the socket so
+.Nm
+can received further messages on the socket, it is said to be
+a
+.Dq multi-threaded
+server;
+it should read one datagram from the socket and create a new socket
+connected to the peer.
+It should fork, and the parent should then exit
+to allow
+.Nm
+to check for new service requests to spawn new servers.
+Datagram servers which process all incoming datagrams
+on a socket and eventually time out are said to be
+.Dq single-threaded .
+.Xr Comsat 8 ,
+.Pq Xr biff 1
+and
+.Xr talkd 8
+are both examples of the latter type of
+datagram server.
+.Xr Tftpd 8
+is an example of a multi-threaded datagram server.
+.Pp
+Servers using stream sockets generally are multi-threaded and
+use the
+.Dq nowait
+entry.
+Connection requests for these services are accepted by
+.Nm inetd ,
+and the server is given only the newly-accepted socket connected
+to a client of the service.
+Most stream-based services operate in this manner.
+Stream-based servers that use
+.Dq wait
+are started with the listening service socket, and must accept
+at least one connection request before exiting.
+Such a server would normally accept and process incoming connection
+requests until a timeout.
+TCPMUX services must use
+.Dq nowait .
+.Pp
+The maximum number of outstanding child processes (or ``threads'')
+for a ``nowait'' service may be explicitly specified by appending a
+``/'' followed by the number to the ``nowait'' keyword. Normally
+(or if a value of zero is specified) there is no maximum. Otherwise,
+once the maximum is reached, further connection attempts will be
+queued up until an existing child process exits. This also works
+in the case of ``wait'' mode, although a value other than one (the
+default) might not make sense in some cases.
+You can also specify the maximum number of connections per minute
+for a given IP address by appending
+a ``/'' followed by the number to the maximum number of
+outstanding child processes. Once the maximum is reached, further
+conections from this IP address will be dropped until the end of the
+minute.
+.Pp
+The
+.Em user
+entry should contain the user name of the user as whom the server
+should run. This allows for servers to be given less permission
+than root.
+Optional
+.Em group
+part separated by ``:'' allows to specify group name different
+than default group for this user.
+Optional
+.Em login-class
+part separated by ``/'' allows to specify login class different
+than default ``daemon'' login class.
+.Pp
+The
+.Em server-program
+entry should contain the pathname of the program which is to be
+executed by
+.Nm
+when a request is found on its socket. If
+.Nm
+provides this service internally, this entry should
+be
+.Dq internal .
+.Pp
+The
+.Em server program arguments
+should be just as arguments
+normally are, starting with argv[0], which is the name of
+the program. If the service is provided internally, the
+word
+.Dq internal
+should take the place of this entry.
+.Pp
+The
+.Nm
+program
+provides several
+.Dq trivial
+services internally by use of
+routines within itself. These services are
+.Dq echo ,
+.Dq discard ,
+.Dq chargen
+(character generator),
+.Dq daytime
+(human readable time), and
+.Dq time
+(machine readable time, in the form of the number of seconds since
+midnight, January 1, 1900). All of these services are available in
+both TCP and UDP versions; the UDP versions will refuse service if the
+request specifies a reply port corresponding to any internal service.
+(This is done as a defense against looping attacks; the remote IP address
+is logged.)
+For details of these services, consult the
+appropriate
+.Tn RFC
+document.
+.Pp
+When given the
+.Fl l
+option
+.Nm
+will log an entry to syslog each time an
+.Xr accept 2
+is made, which notes the
+service selected and the IP-number of the remote requestor.
+.Pp
+The
+.Nm
+program
+rereads its configuration file when it receives a hangup signal,
+.Dv SIGHUP .
+Services may be added, deleted or modified when the configuration file
+is reread.
+Except when started in debugging mode,
+.Nm
+records its process ID in the file
+.Pa /var/run/inetd.pid
+to assist in reconfiguration.
+.Sh TCPMUX
+.Pp
+.Tn RFC 1078
+describes the TCPMUX protocol:
+``A TCP client connects to a foreign host on TCP port 1. It sends the
+service name followed by a carriage-return line-feed <CRLF>. The
+service name is never case sensitive. The server replies with a
+single character indicating positive (+) or negative (\-)
+acknowledgment, immediately followed by an optional message of
+explanation, terminated with a <CRLF>. If the reply was positive,
+the selected protocol begins; otherwise the connection is closed.''
+The program is passed the TCP connection as file descriptors 0 and 1.
+.Pp
+If the TCPMUX service name begins with a ``+'',
+.Nm
+returns the positive reply for the program.
+This allows you to invoke programs that use stdin/stdout
+without putting any special server code in them.
+.Pp
+The special service name
+.Dq help
+causes
+.Nm
+to list TCPMUX services in
+.Pa inetd.conf .
+.Sh "FILES"
+.Bl -tag -width /var/run/inetd.pid -compact
+.It Pa /etc/inetd.conf
+configuration file.
+.It Pa /etc/rpc
+translation of service names to RPC program numbers.
+.It Pa /etc/services
+translation of service names to port numbers.
+.It Pa /var/run/inetd.pid
+the pid of the currently running
+.Nm inetd .
+.El
+.Sh "EXAMPLES"
+.Pp
+Here are several example service entries for the various types of services:
+.Bd -literal
+ftp stream tcp nowait root /usr/libexec/ftpd ftpd -l
+ntalk dgram udp wait root /usr/libexec/ntalkd ntalkd
+tcpmux/+date stream tcp nowait guest /bin/date date
+tcpmux/phonebook stream tcp nowait guest /usr/local/bin/phonebook phonebook
+rstatd/1-3 dgram rpc/udp wait root /usr/libexec/rpc.rstatd rpc.rstatd
+.Ed
+.Sh "ERROR MESSAGES"
+The
+.Nm
+server
+logs error messages using
+.Xr syslog 3 .
+Important error messages and their explanations are:
+.Pp
+.Bl -ohang -compact
+.It Xo
+.Ar service Ns / Ns Ar protocol
+.No " server failing (looping), service terminated."
+.Xc
+The number of requests for the specified service in the past minute
+exceeded the limit. The limit exists to prevent a broken program
+or a malicious user from swamping the system.
+This message may occur for several reasons:
+.Bl -enum -offset indent
+.It
+There are many hosts requesting the service within a short time period.
+.It
+A broken client program is requesting the service too frequently.
+.It
+A malicious user is running a program to invoke the service in
+a denial-of-service attack.
+.It
+The invoked service program has an error that causes clients
+to retry quickly.
+.El
+.Pp
+Use the
+.Fl R Ar rate
+option,
+as described above, to change the rate limit.
+Once the limit is reached, the service will be
+reenabled automatically in 10 minutes.
+.Pp
+.It Xo
+.Ar service Ns / Ns Ar protocol :
+.No \&No such user
+.Ar user ,
+.No service ignored
+.Xc
+.It Xo
+.Ar service Ns / Ns Ar protocol :
+.No getpwnam :
+.Ar user :
+.No \&No such user
+.Xc
+No entry for
+.Ar user
+exists in the
+.Xr passwd 5
+database. The first message
+occurs when
+.Nm
+(re)reads the configuration file. The second message occurs when the
+service is invoked.
+.Pp
+.It Xo
+.Ar service :
+.No can't set uid
+.Ar uid
+.Xc
+.It Xo
+.Ar service :
+.No can't set gid
+.Ar gid
+.Xc
+The user or group ID for the entry's
+.Ar user
+field is invalid.
+.Pp
+.It "setsockopt(SO_PRIVSTATE): Operation not supported"
+The
+.Nm
+program attempted to renounce the privileged state associated with a
+socket but was unable to.
+.El
+.Sh SEE ALSO
+.Xr login.conf 5 ,
+.Xr passwd 5 ,
+.Xr rpc 5 ,
+.Xr services 5 ,
+.Xr comsat 8 ,
+.Xr fingerd 8 ,
+.Xr ftpd 8 ,
+.Xr portmap 8 ,
+.Xr rexecd 8 ,
+.Xr rlogind 8 ,
+.Xr rshd 8 ,
+.Xr telnetd 8 ,
+.Xr tftpd 8
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 4.3 .
+TCPMUX is based on code and documentation by Mark Lottor.
+Support for
+.Tn "ONC RPC"
+based services is modeled after that
+provided by
+.Tn SunOS
+4.1.
diff --git a/usr.sbin/inetd/inetd.c b/usr.sbin/inetd/inetd.c
new file mode 100644
index 0000000..bffaee6
--- /dev/null
+++ b/usr.sbin/inetd/inetd.c
@@ -0,0 +1,1883 @@
+/*
+ * Copyright (c) 1983, 1991, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 1983, 1991, 1993, 1994\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)from: inetd.c 8.4 (Berkeley) 4/13/94";
+#endif
+static const char rcsid[] =
+ "$Id: inetd.c,v 1.28 1997/10/28 13:46:52 ache Exp $";
+#endif /* not lint */
+
+/*
+ * Inetd - Internet super-server
+ *
+ * This program invokes all internet services as needed. Connection-oriented
+ * services are invoked each time a connection is made, by creating a process.
+ * This process is passed the connection as file descriptor 0 and is expected
+ * to do a getpeername to find out the source host and port.
+ *
+ * Datagram oriented services are invoked when a datagram
+ * arrives; a process is created and passed a pending message
+ * on file descriptor 0. Datagram servers may either connect
+ * to their peer, freeing up the original socket for inetd
+ * to receive further messages on, or ``take over the socket'',
+ * processing all arriving datagrams and, eventually, timing
+ * out. The first type of server is said to be ``multi-threaded'';
+ * the second type of server ``single-threaded''.
+ *
+ * Inetd uses a configuration file which is read at startup
+ * and, possibly, at some later time in response to a hangup signal.
+ * The configuration file is ``free format'' with fields given in the
+ * order shown below. Continuation lines for an entry must being with
+ * a space or tab. All fields must be present in each entry.
+ *
+ * service name must be in /etc/services or must
+ * name a tcpmux service
+ * socket type stream/dgram/raw/rdm/seqpacket
+ * protocol must be in /etc/protocols
+ * wait/nowait single-threaded/multi-threaded
+ * user user to run daemon as
+ * server program full path name
+ * server program arguments maximum of MAXARGS (20)
+ *
+ * TCP services without official port numbers are handled with the
+ * RFC1078-based tcpmux internal service. Tcpmux listens on port 1 for
+ * requests. When a connection is made from a foreign host, the service
+ * requested is passed to tcpmux, which looks it up in the servtab list
+ * and returns the proper entry for the service. Tcpmux returns a
+ * negative reply if the service doesn't exist, otherwise the invoked
+ * server is expected to return the positive reply if the service type in
+ * inetd.conf file has the prefix "tcpmux/". If the service type has the
+ * prefix "tcpmux/+", tcpmux will return the positive reply for the
+ * process; this is for compatibility with older server code, and also
+ * allows you to invoke programs that use stdin/stdout without putting any
+ * special server code in them. Services that use tcpmux are "nowait"
+ * because they do not have a well-known port and hence cannot listen
+ * for new requests.
+ *
+ * For RPC services
+ * service name/version must be in /etc/rpc
+ * socket type stream/dgram/raw/rdm/seqpacket
+ * protocol must be in /etc/protocols
+ * wait/nowait single-threaded/multi-threaded
+ * user user to run daemon as
+ * server program full path name
+ * server program arguments maximum of MAXARGS
+ *
+ * Comment lines are indicated by a `#' in column 1.
+ */
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <rpc/rpc.h>
+#include <rpc/pmap_clnt.h>
+
+#include <errno.h>
+#include <err.h>
+#include <fcntl.h>
+#include <grp.h>
+#include <netdb.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <libutil.h>
+#include <sysexits.h>
+
+#ifdef LOGIN_CAP
+#include <login_cap.h>
+
+/* see init.c */
+#define RESOURCE_RC "daemon"
+
+#endif
+
+#include "pathnames.h"
+
+#define TOOMANY 256 /* don't start more than TOOMANY */
+#define CNT_INTVL 60 /* servers in CNT_INTVL sec. */
+#define RETRYTIME (60*10) /* retry after bind or server fail */
+#define MAX_MAXCHLD 32767 /* max allowable max children */
+
+#define SIGBLOCK (sigmask(SIGCHLD)|sigmask(SIGHUP)|sigmask(SIGALRM))
+
+int debug = 0;
+int log = 0;
+int nsock, maxsock;
+fd_set allsock;
+int options;
+int timingout;
+int toomany = TOOMANY;
+struct servent *sp;
+struct rpcent *rpc;
+struct in_addr bind_address;
+
+struct servtab {
+ char *se_service; /* name of service */
+ int se_socktype; /* type of socket to use */
+ char *se_proto; /* protocol used */
+ int se_maxchild; /* max number of children */
+ int se_maxcpm; /* max connects per IP per minute */
+ int se_numchild; /* current number of children */
+ pid_t *se_pids; /* array of child pids */
+ char *se_user; /* user name to run as */
+ char *se_group; /* group name to run as */
+#ifdef LOGIN_CAP
+ char *se_class; /* login class name to run with */
+#endif
+ struct biltin *se_bi; /* if built-in, description */
+ char *se_server; /* server program */
+#define MAXARGV 20
+ char *se_argv[MAXARGV+1]; /* program arguments */
+ int se_fd; /* open descriptor */
+ struct sockaddr_in se_ctrladdr;/* bound address */
+ u_char se_type; /* type: normal, mux, or mux+ */
+ u_char se_checked; /* looked at during merge */
+ u_char se_accept; /* i.e., wait/nowait mode */
+ u_char se_rpc; /* ==1 if RPC service */
+ int se_rpc_prog; /* RPC program number */
+ u_int se_rpc_lowvers; /* RPC low version */
+ u_int se_rpc_highvers; /* RPC high version */
+ int se_count; /* number started since se_time */
+ struct timeval se_time; /* start of se_count */
+ struct servtab *se_next;
+} *servtab;
+
+#define NORM_TYPE 0
+#define MUX_TYPE 1
+#define MUXPLUS_TYPE 2
+#define ISMUX(sep) (((sep)->se_type == MUX_TYPE) || \
+ ((sep)->se_type == MUXPLUS_TYPE))
+#define ISMUXPLUS(sep) ((sep)->se_type == MUXPLUS_TYPE)
+
+
+void chargen_dg __P((int, struct servtab *));
+void chargen_stream __P((int, struct servtab *));
+void close_sep __P((struct servtab *));
+void config __P((int));
+void daytime_dg __P((int, struct servtab *));
+void daytime_stream __P((int, struct servtab *));
+void discard_dg __P((int, struct servtab *));
+void discard_stream __P((int, struct servtab *));
+void echo_dg __P((int, struct servtab *));
+void echo_stream __P((int, struct servtab *));
+void endconfig __P((void));
+struct servtab *enter __P((struct servtab *));
+void freeconfig __P((struct servtab *));
+struct servtab *getconfigent __P((void));
+void machtime_dg __P((int, struct servtab *));
+void machtime_stream __P((int, struct servtab *));
+char *newstr __P((char *));
+char *nextline __P((FILE *));
+void print_service __P((char *, struct servtab *));
+void addchild __P((struct servtab *, int));
+void reapchild __P((int));
+void enable __P((struct servtab *));
+void disable __P((struct servtab *));
+void retry __P((int));
+int setconfig __P((void));
+void setup __P((struct servtab *));
+char *sskip __P((char **));
+char *skip __P((char **));
+struct servtab *tcpmux __P((int));
+int cpmip __P((struct servtab *, int));
+
+void unregisterrpc __P((register struct servtab *sep));
+
+struct biltin {
+ char *bi_service; /* internally provided service name */
+ int bi_socktype; /* type of socket supported */
+ short bi_fork; /* 1 if should fork before call */
+ int bi_maxchild; /* max number of children (default) */
+ void (*bi_fn)(); /* function which performs it */
+} biltins[] = {
+ /* Echo received data */
+ { "echo", SOCK_STREAM, 1, 0, echo_stream },
+ { "echo", SOCK_DGRAM, 0, 0, echo_dg },
+
+ /* Internet /dev/null */
+ { "discard", SOCK_STREAM, 1, 0, discard_stream },
+ { "discard", SOCK_DGRAM, 0, 0, discard_dg },
+
+ /* Return 32 bit time since 1970 */
+ { "time", SOCK_STREAM, 0, 0, machtime_stream },
+ { "time", SOCK_DGRAM, 0, 0, machtime_dg },
+
+ /* Return human-readable time */
+ { "daytime", SOCK_STREAM, 0, 0, daytime_stream },
+ { "daytime", SOCK_DGRAM, 0, 0, daytime_dg },
+
+ /* Familiar character generator */
+ { "chargen", SOCK_STREAM, 1, 0, chargen_stream },
+ { "chargen", SOCK_DGRAM, 0, 0, chargen_dg },
+
+ { "tcpmux", SOCK_STREAM, 1, 0, (void (*)())tcpmux },
+
+ { NULL }
+};
+
+#define NUMINT (sizeof(intab) / sizeof(struct inent))
+char *CONFIG = _PATH_INETDCONF;
+char *pid_file = _PATH_INETDPID;
+
+#ifdef OLD_SETPROCTITLE
+char **Argv;
+char *LastArg;
+#endif
+
+int
+main(argc, argv, envp)
+ int argc;
+ char *argv[], *envp[];
+{
+ struct servtab *sep;
+ struct passwd *pwd;
+ struct group *grp;
+ struct sigvec sv;
+ int tmpint, ch, dofork;
+ pid_t pid;
+ char buf[50];
+ struct sockaddr_in peer;
+ int i;
+#ifdef LOGIN_CAP
+ login_cap_t *lc = NULL;
+#endif
+
+
+#ifdef OLD_SETPROCTITLE
+ Argv = argv;
+ if (envp == 0 || *envp == 0)
+ envp = argv;
+ while (*envp)
+ envp++;
+ LastArg = envp[-1] + strlen(envp[-1]);
+#endif
+
+ openlog("inetd", LOG_PID | LOG_NOWAIT, LOG_DAEMON);
+
+ bind_address.s_addr = htonl(INADDR_ANY);
+ while ((ch = getopt(argc, argv, "dlR:a:p:")) != -1)
+ switch(ch) {
+ case 'd':
+ debug = 1;
+ options |= SO_DEBUG;
+ break;
+ case 'l':
+ log = 1;
+ break;
+ case 'R': { /* invocation rate */
+ char *p;
+
+ tmpint = strtol(optarg, &p, 0);
+ if (tmpint < 1 || *p)
+ syslog(LOG_ERR,
+ "-R %s: bad value for service invocation rate",
+ optarg);
+ else
+ toomany = tmpint;
+ break;
+ }
+ case 'a':
+ if (!inet_aton(optarg, &bind_address)) {
+ syslog(LOG_ERR,
+ "-a %s: invalid IP address", optarg);
+ exit(EX_USAGE);
+ }
+ break;
+ case 'p':
+ pid_file = optarg;
+ break;
+ case '?':
+ default:
+ syslog(LOG_ERR,
+ "usage: inetd [-dl] [-a address] [-R rate]"
+ " [-p pidfile] [conf-file]");
+ exit(EX_USAGE);
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc > 0)
+ CONFIG = argv[0];
+ if (debug == 0) {
+ FILE *fp;
+ if (daemon(0, 0) < 0) {
+ syslog(LOG_WARNING, "daemon(0,0) failed: %m");
+ }
+ /*
+ * In case somebody has started inetd manually, we need to
+ * clear the logname, so that old servers run as root do not
+ * get the user's logname..
+ */
+ if (setlogin("") < 0) {
+ syslog(LOG_WARNING, "cannot clear logname: %m");
+ /* no big deal if it fails.. */
+ }
+ pid = getpid();
+ fp = fopen(pid_file, "w");
+ if (fp) {
+ fprintf(fp, "%ld\n", (long)pid);
+ fclose(fp);
+ } else {
+ syslog(LOG_WARNING, "%s: %m", pid_file);
+ }
+ }
+ memset(&sv, 0, sizeof(sv));
+ sv.sv_mask = SIGBLOCK;
+ sv.sv_handler = retry;
+ sigvec(SIGALRM, &sv, (struct sigvec *)0);
+ config(SIGHUP);
+ sv.sv_handler = config;
+ sigvec(SIGHUP, &sv, (struct sigvec *)0);
+ sv.sv_handler = reapchild;
+ sigvec(SIGCHLD, &sv, (struct sigvec *)0);
+
+ {
+ /* 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);
+ }
+
+ for (;;) {
+ int n, ctrl;
+ fd_set readable;
+
+ if (nsock == 0) {
+ (void) sigblock(SIGBLOCK);
+ while (nsock == 0)
+ sigpause(0L);
+ (void) sigsetmask(0L);
+ }
+ readable = allsock;
+ if ((n = select(maxsock + 1, &readable, (fd_set *)0,
+ (fd_set *)0, (struct timeval *)0)) <= 0) {
+ if (n < 0 && errno != EINTR) {
+ syslog(LOG_WARNING, "select: %m");
+ sleep(1);
+ }
+ continue;
+ }
+ for (sep = servtab; n && sep; sep = sep->se_next)
+ if (sep->se_fd != -1 && FD_ISSET(sep->se_fd, &readable)) {
+ n--;
+ if (debug)
+ warnx("someone wants %s", sep->se_service);
+ if (sep->se_accept && sep->se_socktype == SOCK_STREAM) {
+ ctrl = accept(sep->se_fd, (struct sockaddr *)0,
+ (int *)0);
+ if (debug)
+ warnx("accept, ctrl %d", ctrl);
+ if (ctrl < 0) {
+ if (errno != EINTR)
+ syslog(LOG_WARNING,
+ "accept (for %s): %m",
+ sep->se_service);
+ continue;
+ }
+ if (cpmip(sep, ctrl) < 0) {
+ close(ctrl);
+ continue;
+ }
+ if (log) {
+ i = sizeof peer;
+ if (getpeername(ctrl, (struct sockaddr *)
+ &peer, &i)) {
+ syslog(LOG_WARNING,
+ "getpeername(for %s): %m",
+ sep->se_service);
+ close(ctrl);
+ continue;
+ }
+ syslog(LOG_INFO,"%s from %s",
+ sep->se_service,
+ inet_ntoa(peer.sin_addr));
+ }
+ /*
+ * Call tcpmux to find the real service to exec.
+ */
+ if (sep->se_bi &&
+ sep->se_bi->bi_fn == (void (*)()) tcpmux) {
+ struct servtab *tsep;
+
+ tsep = tcpmux(ctrl);
+ if (tsep == NULL) {
+ close(ctrl);
+ continue;
+ }
+ sep = tsep;
+ }
+ } else
+ ctrl = sep->se_fd;
+ (void) sigblock(SIGBLOCK);
+ pid = 0;
+ dofork = (sep->se_bi == 0 || sep->se_bi->bi_fork);
+ if (dofork) {
+ if (sep->se_count++ == 0)
+ (void)gettimeofday(&sep->se_time,
+ (struct timezone *)0);
+ else if (sep->se_count >= toomany) {
+ struct timeval now;
+
+ (void)gettimeofday(&now, (struct timezone *)0);
+ if (now.tv_sec - sep->se_time.tv_sec >
+ CNT_INTVL) {
+ sep->se_time = now;
+ sep->se_count = 1;
+ } else {
+ syslog(LOG_ERR,
+ "%s/%s server failing (looping), service terminated",
+ sep->se_service, sep->se_proto);
+ close_sep(sep);
+ sigsetmask(0L);
+ if (!timingout) {
+ timingout = 1;
+ alarm(RETRYTIME);
+ }
+ continue;
+ }
+ }
+ pid = fork();
+ }
+ if (pid < 0) {
+ syslog(LOG_ERR, "fork: %m");
+ if (sep->se_accept &&
+ sep->se_socktype == SOCK_STREAM)
+ close(ctrl);
+ sigsetmask(0L);
+ sleep(1);
+ continue;
+ }
+ if (pid)
+ addchild(sep, pid);
+ sigsetmask(0L);
+ if (pid == 0) {
+ if (dofork) {
+ if (debug)
+ warnx("+ closing from %d", maxsock);
+ for (tmpint = maxsock; tmpint > 2; tmpint--)
+ if (tmpint != ctrl)
+ (void) close(tmpint);
+ }
+ if (sep->se_bi) {
+ (*sep->se_bi->bi_fn)(ctrl, sep);
+ /* NOTREACHED */
+ } else {
+ if (debug)
+ warnx("%d execl %s",
+ getpid(), sep->se_server);
+ dup2(ctrl, 0);
+ close(ctrl);
+ dup2(0, 1);
+ dup2(0, 2);
+ if ((pwd = getpwnam(sep->se_user)) == NULL) {
+ syslog(LOG_ERR,
+ "%s/%s: %s: No such user",
+ sep->se_service, sep->se_proto,
+ sep->se_user);
+ if (sep->se_socktype != SOCK_STREAM)
+ recv(0, buf, sizeof (buf), 0);
+ _exit(EX_NOUSER);
+ }
+ grp = NULL;
+ if ( sep->se_group != NULL
+ && (grp = getgrnam(sep->se_group)) == NULL
+ ) {
+ syslog(LOG_ERR,
+ "%s/%s: %s: No such group",
+ sep->se_service, sep->se_proto,
+ sep->se_group);
+ if (sep->se_socktype != SOCK_STREAM)
+ recv(0, buf, sizeof (buf), 0);
+ _exit(EX_NOUSER);
+ }
+ if (grp != NULL)
+ pwd->pw_gid = grp->gr_gid;
+#ifdef LOGIN_CAP
+ if ((lc = login_getclass(sep->se_class)) == NULL) {
+ /* error syslogged by getclass */
+ syslog(LOG_ERR,
+ "%s/%s: %s: login class error",
+ sep->se_service, sep->se_proto);
+ 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
+ execv(sep->se_server, sep->se_argv);
+ if (sep->se_socktype != SOCK_STREAM)
+ recv(0, buf, sizeof (buf), 0);
+ syslog(LOG_ERR,
+ "cannot execute %s: %m", sep->se_server);
+ _exit(EX_OSERR);
+ }
+ }
+ if (sep->se_accept && sep->se_socktype == SOCK_STREAM)
+ close(ctrl);
+ }
+ }
+}
+
+/*
+ * Record a new child pid for this service. If we've reached the
+ * limit on children, then stop accepting incoming requests.
+ */
+
+void
+addchild(struct servtab *sep, pid_t pid)
+{
+#ifdef SANITY_CHECK
+ if (sep->se_numchild >= sep->se_maxchild) {
+ syslog(LOG_ERR, "%s: %d >= %d",
+ __FUNCTION__, sep->se_numchild, sep->se_maxchild);
+ exit(EX_SOFTWARE);
+ }
+#endif
+ if (sep->se_maxchild == 0)
+ return;
+ sep->se_pids[sep->se_numchild++] = pid;
+ if (sep->se_numchild == sep->se_maxchild)
+ disable(sep);
+}
+
+/*
+ * Some child process has exited. See if it's on somebody's list.
+ */
+
+void
+reapchild(signo)
+ int signo;
+{
+ 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
+config(signo)
+ int signo;
+{
+ 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: login class error, service ignored",
+ new->se_service, new->se_proto);
+ continue;
+ }
+#endif
+ for (sep = servtab; sep; sep = sep->se_next)
+ if (strcmp(sep->se_service, new->se_service) == 0 &&
+ strcmp(sep->se_proto, new->se_proto) == 0)
+ break;
+ if (sep != 0) {
+ int i;
+
+#define SWAP(a, b) { typeof(a) c = a; a = b; b = c; }
+ omask = sigblock(SIGBLOCK);
+ /* copy over outstanding child pids */
+ if (sep->se_maxchild && new->se_maxchild) {
+ new->se_numchild = sep->se_numchild;
+ if (new->se_numchild > new->se_maxchild)
+ new->se_numchild = new->se_maxchild;
+ memcpy(new->se_pids, sep->se_pids,
+ new->se_numchild * sizeof(*new->se_pids));
+ }
+ SWAP(sep->se_pids, new->se_pids);
+ sep->se_maxchild = new->se_maxchild;
+ sep->se_numchild = new->se_numchild;
+ sep->se_maxcpm = new->se_maxcpm;
+ /* might need to turn on or off service now */
+ if (sep->se_fd >= 0) {
+ if (sep->se_maxchild
+ && sep->se_numchild == sep->se_maxchild) {
+ if (FD_ISSET(sep->se_fd, &allsock))
+ disable(sep);
+ } else {
+ if (!FD_ISSET(sep->se_fd, &allsock))
+ enable(sep);
+ }
+ }
+ sep->se_accept = new->se_accept;
+ SWAP(sep->se_user, new->se_user);
+ SWAP(sep->se_group, new->se_group);
+#ifdef LOGIN_CAP
+ SWAP(sep->se_class, new->se_class);
+#endif
+ SWAP(sep->se_server, new->se_server);
+ for (i = 0; i < MAXARGV; i++)
+ SWAP(sep->se_argv[i], new->se_argv[i]);
+ sigsetmask(omask);
+ freeconfig(new);
+ if (debug)
+ print_service("REDO", sep);
+ } else {
+ sep = enter(new);
+ if (debug)
+ print_service("ADD ", sep);
+ }
+ sep->se_checked = 1;
+ if (ISMUX(sep)) {
+ sep->se_fd = -1;
+ continue;
+ }
+ if (!sep->se_rpc) {
+ sp = getservbyname(sep->se_service, sep->se_proto);
+ if (sp == 0) {
+ syslog(LOG_ERR, "%s/%s: unknown service",
+ sep->se_service, sep->se_proto);
+ sep->se_checked = 0;
+ continue;
+ }
+ if (sp->s_port != sep->se_ctrladdr.sin_port) {
+ sep->se_ctrladdr.sin_family = AF_INET;
+ sep->se_ctrladdr.sin_addr = bind_address;
+ sep->se_ctrladdr.sin_port = sp->s_port;
+ if (sep->se_fd >= 0)
+ close_sep(sep);
+ }
+ } else {
+ rpc = getrpcbyname(sep->se_service);
+ if (rpc == 0) {
+ syslog(LOG_ERR, "%s/%s unknown RPC service.",
+ sep->se_service, sep->se_proto);
+ if (sep->se_fd != -1)
+ (void) close(sep->se_fd);
+ sep->se_fd = -1;
+ continue;
+ }
+ if (rpc->r_number != sep->se_rpc_prog) {
+ if (sep->se_rpc_prog)
+ unregisterrpc(sep);
+ sep->se_rpc_prog = rpc->r_number;
+ if (sep->se_fd != -1)
+ (void) close(sep->se_fd);
+ sep->se_fd = -1;
+ }
+ }
+ if (sep->se_fd == -1)
+ setup(sep);
+ }
+ endconfig();
+ /*
+ * Purge anything not looked at above.
+ */
+ omask = sigblock(SIGBLOCK);
+ sepp = &servtab;
+ while ((sep = *sepp)) {
+ if (sep->se_checked) {
+ sepp = &sep->se_next;
+ continue;
+ }
+ *sepp = sep->se_next;
+ if (sep->se_fd >= 0)
+ close_sep(sep);
+ if (debug)
+ print_service("FREE", sep);
+ if (sep->se_rpc && sep->se_rpc_prog > 0)
+ unregisterrpc(sep);
+ freeconfig(sep);
+ free((char *)sep);
+ }
+ (void) sigsetmask(omask);
+}
+
+void
+unregisterrpc(sep)
+ struct servtab *sep;
+{
+ int i;
+ struct servtab *sepp;
+ long omask;
+
+ omask = sigblock(SIGBLOCK);
+ for (sepp = servtab; sepp; sepp = sepp->se_next) {
+ if (sepp == sep)
+ continue;
+ if (sep->se_checked == 0 ||
+ !sepp->se_rpc ||
+ sep->se_rpc_prog != sepp->se_rpc_prog)
+ continue;
+ return;
+ }
+ if (debug)
+ print_service("UNREG", sep);
+ for (i = sep->se_rpc_lowvers; i <= sep->se_rpc_highvers; i++)
+ pmap_unset(sep->se_rpc_prog, i);
+ if (sep->se_fd != -1)
+ (void) close(sep->se_fd);
+ sep->se_fd = -1;
+ (void) sigsetmask(omask);
+}
+
+void
+retry(signo)
+ int signo;
+{
+ struct servtab *sep;
+
+ timingout = 0;
+ for (sep = servtab; sep; sep = sep->se_next)
+ if (sep->se_fd == -1 && !ISMUX(sep))
+ setup(sep);
+}
+
+void
+setup(sep)
+ struct servtab *sep;
+{
+ int on = 1;
+
+ if ((sep->se_fd = socket(AF_INET, sep->se_socktype, 0)) < 0) {
+ if (debug)
+ warn("socket failed on %s/%s",
+ sep->se_service, sep->se_proto);
+ syslog(LOG_ERR, "%s/%s: socket: %m",
+ sep->se_service, sep->se_proto);
+ return;
+ }
+#define turnon(fd, opt) \
+setsockopt(fd, SOL_SOCKET, opt, (char *)&on, sizeof (on))
+ if (strcmp(sep->se_proto, "tcp") == 0 && (options & SO_DEBUG) &&
+ turnon(sep->se_fd, SO_DEBUG) < 0)
+ syslog(LOG_ERR, "setsockopt (SO_DEBUG): %m");
+ if (turnon(sep->se_fd, SO_REUSEADDR) < 0)
+ syslog(LOG_ERR, "setsockopt (SO_REUSEADDR): %m");
+#ifdef SO_PRIVSTATE
+ if (turnon(sep->se_fd, SO_PRIVSTATE) < 0)
+ syslog(LOG_ERR, "setsockopt (SO_PRIVSTATE): %m");
+#endif
+#undef turnon
+ if (bind(sep->se_fd, (struct sockaddr *)&sep->se_ctrladdr,
+ sizeof (sep->se_ctrladdr)) < 0) {
+ if (debug)
+ warn("bind failed on %s/%s",
+ sep->se_service, sep->se_proto);
+ syslog(LOG_ERR, "%s/%s: bind: %m",
+ sep->se_service, sep->se_proto);
+ (void) close(sep->se_fd);
+ sep->se_fd = -1;
+ if (!timingout) {
+ timingout = 1;
+ alarm(RETRYTIME);
+ }
+ return;
+ }
+ if (sep->se_rpc) {
+ int i, len = sizeof(struct sockaddr);
+
+ if (getsockname(sep->se_fd,
+ (struct sockaddr*)&sep->se_ctrladdr, &len) < 0){
+ syslog(LOG_ERR, "%s/%s: getsockname: %m",
+ sep->se_service, sep->se_proto);
+ (void) close(sep->se_fd);
+ sep->se_fd = -1;
+ return;
+ }
+ if (debug)
+ print_service("REG ", sep);
+ for (i = sep->se_rpc_lowvers; i <= sep->se_rpc_highvers; i++) {
+ pmap_unset(sep->se_rpc_prog, i);
+ pmap_set(sep->se_rpc_prog, i,
+ (sep->se_socktype == SOCK_DGRAM)
+ ? IPPROTO_UDP : IPPROTO_TCP,
+ ntohs(sep->se_ctrladdr.sin_port));
+ }
+
+ }
+ if (sep->se_socktype == SOCK_STREAM)
+ listen(sep->se_fd, 64);
+ enable(sep);
+ if (debug) {
+ warnx("registered %s on %d",
+ sep->se_server, sep->se_fd);
+ }
+}
+
+/*
+ * Finish with a service and its socket.
+ */
+void
+close_sep(sep)
+ struct servtab *sep;
+{
+ if (sep->se_fd >= 0) {
+ if (FD_ISSET(sep->se_fd, &allsock))
+ disable(sep);
+ (void) close(sep->se_fd);
+ sep->se_fd = -1;
+ }
+ sep->se_count = 0;
+ sep->se_numchild = 0; /* forget about any existing children */
+}
+
+struct servtab *
+enter(cp)
+ struct servtab *cp;
+{
+ struct servtab *sep;
+ long omask;
+
+ sep = (struct servtab *)malloc(sizeof (*sep));
+ if (sep == (struct servtab *)0) {
+ syslog(LOG_ERR, "Out of memory.");
+ exit(EX_OSERR);
+ }
+ *sep = *cp;
+ sep->se_fd = -1;
+ omask = sigblock(SIGBLOCK);
+ sep->se_next = servtab;
+ servtab = sep;
+ sigsetmask(omask);
+ return (sep);
+}
+
+void
+enable(struct servtab *sep)
+{
+ if (debug)
+ warnx(
+ "enabling %s, fd %d", sep->se_service, sep->se_fd);
+#ifdef SANITY_CHECK
+ if (sep->se_fd < 0) {
+ syslog(LOG_ERR,
+ "%s: %s: bad fd", __FUNCTION__, sep->se_service);
+ exit(EX_SOFTWARE);
+ }
+ if (ISMUX(sep)) {
+ syslog(LOG_ERR,
+ "%s: %s: is mux", __FUNCTION__, sep->se_service);
+ exit(EX_SOFTWARE);
+ }
+ if (FD_ISSET(sep->se_fd, &allsock)) {
+ syslog(LOG_ERR,
+ "%s: %s: not off", __FUNCTION__, sep->se_service);
+ exit(EX_SOFTWARE);
+ }
+#endif
+ FD_SET(sep->se_fd, &allsock);
+ nsock++;
+ if (sep->se_fd > maxsock)
+ maxsock = sep->se_fd;
+}
+
+void
+disable(struct servtab *sep)
+{
+ if (debug)
+ warnx(
+ "disabling %s, fd %d", sep->se_service, sep->se_fd);
+#ifdef SANITY_CHECK
+ if (sep->se_fd < 0) {
+ syslog(LOG_ERR,
+ "%s: %s: bad fd", __FUNCTION__, sep->se_service);
+ exit(EX_SOFTWARE);
+ }
+ if (ISMUX(sep)) {
+ syslog(LOG_ERR,
+ "%s: %s: is mux", __FUNCTION__, sep->se_service);
+ exit(EX_SOFTWARE);
+ }
+ if (!FD_ISSET(sep->se_fd, &allsock)) {
+ syslog(LOG_ERR,
+ "%s: %s: not on", __FUNCTION__, sep->se_service);
+ exit(EX_SOFTWARE);
+ }
+ if (nsock == 0) {
+ syslog(LOG_ERR, "%s: nsock=0", __FUNCTION__);
+ exit(EX_SOFTWARE);
+ }
+#endif
+ FD_CLR(sep->se_fd, &allsock);
+ nsock--;
+ if (sep->se_fd == maxsock)
+ maxsock--;
+}
+
+FILE *fconfig = NULL;
+struct servtab serv;
+char line[LINE_MAX];
+
+int
+setconfig()
+{
+
+ if (fconfig != NULL) {
+ fseek(fconfig, 0L, SEEK_SET);
+ return (1);
+ }
+ fconfig = fopen(CONFIG, "r");
+ return (fconfig != NULL);
+}
+
+void
+endconfig()
+{
+ if (fconfig) {
+ (void) fclose(fconfig);
+ fconfig = NULL;
+ }
+}
+
+struct servtab *
+getconfigent()
+{
+ struct servtab *sep = &serv;
+ int argc;
+ char *cp, *arg, *s;
+ char *versp;
+ static char TCPMUX_TOKEN[] = "tcpmux/";
+#define MUX_LEN (sizeof(TCPMUX_TOKEN)-1)
+
+more:
+ while ((cp = nextline(fconfig)) && (*cp == '#' || *cp == '\0'))
+ ;
+ if (cp == NULL)
+ return ((struct servtab *)0);
+ /*
+ * clear the static buffer, since some fields (se_ctrladdr,
+ * for example) don't get initialized here.
+ */
+ memset((caddr_t)sep, 0, sizeof *sep);
+ arg = skip(&cp);
+ if (cp == NULL) {
+ /* got an empty line containing just blanks/tabs. */
+ goto more;
+ }
+ if (strncmp(arg, TCPMUX_TOKEN, MUX_LEN) == 0) {
+ char *c = arg + MUX_LEN;
+ if (*c == '+') {
+ sep->se_type = MUXPLUS_TYPE;
+ c++;
+ } else
+ sep->se_type = MUX_TYPE;
+ sep->se_service = newstr(c);
+ } else {
+ sep->se_service = newstr(arg);
+ sep->se_type = NORM_TYPE;
+ }
+ arg = sskip(&cp);
+ if (strcmp(arg, "stream") == 0)
+ sep->se_socktype = SOCK_STREAM;
+ else if (strcmp(arg, "dgram") == 0)
+ sep->se_socktype = SOCK_DGRAM;
+ else if (strcmp(arg, "rdm") == 0)
+ sep->se_socktype = SOCK_RDM;
+ else if (strcmp(arg, "seqpacket") == 0)
+ sep->se_socktype = SOCK_SEQPACKET;
+ else if (strcmp(arg, "raw") == 0)
+ sep->se_socktype = SOCK_RAW;
+ else
+ sep->se_socktype = -1;
+ sep->se_proto = newstr(sskip(&cp));
+ if (strncmp(sep->se_proto, "rpc/", 4) == 0) {
+ memmove(sep->se_proto, sep->se_proto + 4,
+ strlen(sep->se_proto) + 1 - 4);
+ sep->se_rpc = 1;
+ sep->se_rpc_prog = sep->se_rpc_lowvers =
+ sep->se_rpc_lowvers = 0;
+ sep->se_ctrladdr.sin_family = AF_INET;
+ sep->se_ctrladdr.sin_port = 0;
+ sep->se_ctrladdr.sin_addr = bind_address;
+ if ((versp = rindex(sep->se_service, '/'))) {
+ *versp++ = '\0';
+ switch (sscanf(versp, "%d-%d",
+ &sep->se_rpc_lowvers,
+ &sep->se_rpc_highvers)) {
+ case 2:
+ break;
+ case 1:
+ sep->se_rpc_highvers =
+ sep->se_rpc_lowvers;
+ break;
+ default:
+ syslog(LOG_ERR,
+ "bad RPC version specifier; %s\n",
+ sep->se_service);
+ freeconfig(sep);
+ goto more;
+ }
+ }
+ else {
+ sep->se_rpc_lowvers =
+ sep->se_rpc_highvers = 1;
+ }
+ }
+ arg = sskip(&cp);
+ if (!strncmp(arg, "wait", 4))
+ sep->se_accept = 0;
+ else if (!strncmp(arg, "nowait", 6))
+ sep->se_accept = 1;
+ else {
+ syslog(LOG_ERR,
+ "%s: bad wait/nowait for service %s",
+ CONFIG, sep->se_service);
+ goto more;
+ }
+ sep->se_maxchild = -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;
+ }
+ sep->se_maxchild = val;
+ if (*eptr == '/')
+ sep->se_maxcpm = strtol(eptr + 1, &eptr, 10);
+ /*
+ * explicitly do not check for \0 for future expansion /
+ * backwards compatibility
+ */
+ }
+ if (ISMUX(sep)) {
+ /*
+ * Silently enforce "nowait" mode for TCPMUX services
+ * since they don't have an assigned port to listen on.
+ */
+ sep->se_accept = 1;
+ if (strcmp(sep->se_proto, "tcp")) {
+ syslog(LOG_ERR,
+ "%s: bad protocol for tcpmux service %s",
+ CONFIG, sep->se_service);
+ goto more;
+ }
+ if (sep->se_socktype != SOCK_STREAM) {
+ syslog(LOG_ERR,
+ "%s: bad socket type for tcpmux service %s",
+ CONFIG, sep->se_service);
+ goto more;
+ }
+ }
+ sep->se_user = newstr(sskip(&cp));
+#ifdef LOGIN_CAP
+ if ((s = strrchr(sep->se_user, '/')) != NULL) {
+ *s = '\0';
+ sep->se_class = newstr(s + 1);
+ } else
+ sep->se_class = newstr(RESOURCE_RC);
+#endif
+ if ((s = strrchr(sep->se_user, ':')) != NULL) {
+ *s = '\0';
+ sep->se_group = newstr(s + 1);
+ } else
+ sep->se_group = NULL;
+ sep->se_server = newstr(sskip(&cp));
+ if (strcmp(sep->se_server, "internal") == 0) {
+ struct biltin *bi;
+
+ for (bi = biltins; bi->bi_service; bi++)
+ if (bi->bi_socktype == sep->se_socktype &&
+ strcmp(bi->bi_service, sep->se_service) == 0)
+ break;
+ if (bi->bi_service == 0) {
+ syslog(LOG_ERR, "internal service %s unknown",
+ sep->se_service);
+ goto more;
+ }
+ sep->se_accept = 1; /* force accept mode for built-ins */
+ sep->se_bi = bi;
+ } else
+ sep->se_bi = NULL;
+ if (sep->se_maxchild < 0) /* apply default max-children */
+ if (sep->se_bi)
+ sep->se_maxchild = sep->se_bi->bi_maxchild;
+ else
+ sep->se_maxchild = sep->se_accept ? 0 : 1;
+ if (sep->se_maxchild) {
+ sep->se_pids = malloc(sep->se_maxchild * sizeof(*sep->se_pids));
+ if (sep->se_pids == NULL) {
+ syslog(LOG_ERR, "Out of memory.");
+ exit(EX_OSERR);
+ }
+ }
+ argc = 0;
+ for (arg = skip(&cp); cp; arg = skip(&cp))
+ if (argc < MAXARGV) {
+ sep->se_argv[argc++] = newstr(arg);
+ } else {
+ syslog(LOG_ERR,
+ "%s: too many arguments for service %s",
+ CONFIG, sep->se_service);
+ goto more;
+ }
+ while (argc <= MAXARGV)
+ sep->se_argv[argc++] = NULL;
+ return (sep);
+}
+
+void
+freeconfig(cp)
+ struct servtab *cp;
+{
+ int i;
+
+ if (cp->se_service)
+ free(cp->se_service);
+ if (cp->se_proto)
+ free(cp->se_proto);
+ if (cp->se_user)
+ free(cp->se_user);
+ if (cp->se_group)
+ free(cp->se_group);
+#ifdef LOGIN_CAP
+ if (cp->se_class)
+ free(cp->se_class);
+#endif
+ if (cp->se_server)
+ free(cp->se_server);
+ if (cp->se_pids)
+ free(cp->se_pids);
+ for (i = 0; i < MAXARGV; i++)
+ if (cp->se_argv[i])
+ free(cp->se_argv[i]);
+}
+
+
+/*
+ * Safe skip - if skip returns null, log a syntax error in the
+ * configuration file and exit.
+ */
+char *
+sskip(cpp)
+ char **cpp;
+{
+ char *cp;
+
+ cp = skip(cpp);
+ if (cp == NULL) {
+ syslog(LOG_ERR, "%s: syntax error", CONFIG);
+ exit(EX_DATAERR);
+ }
+ return (cp);
+}
+
+char *
+skip(cpp)
+ char **cpp;
+{
+ char *cp = *cpp;
+ char *start;
+ char quote = '\0';
+
+again:
+ while (*cp == ' ' || *cp == '\t')
+ cp++;
+ if (*cp == '\0') {
+ int c;
+
+ c = getc(fconfig);
+ (void) ungetc(c, fconfig);
+ if (c == ' ' || c == '\t')
+ if ((cp = nextline(fconfig)))
+ goto again;
+ *cpp = (char *)0;
+ return ((char *)0);
+ }
+ if (*cp == '"' || *cp == '\'')
+ quote = *cp++;
+ start = cp;
+ if (quote)
+ while (*cp && *cp != quote)
+ cp++;
+ else
+ while (*cp && *cp != ' ' && *cp != '\t')
+ cp++;
+ if (*cp != '\0')
+ *cp++ = '\0';
+ *cpp = cp;
+ return (start);
+}
+
+char *
+nextline(fd)
+ FILE *fd;
+{
+ char *cp;
+
+ if (fgets(line, sizeof (line), fd) == NULL)
+ return ((char *)0);
+ cp = strchr(line, '\n');
+ if (cp)
+ *cp = '\0';
+ return (line);
+}
+
+char *
+newstr(cp)
+ char *cp;
+{
+ if ((cp = strdup(cp ? cp : "")))
+ return (cp);
+ syslog(LOG_ERR, "strdup: %m");
+ exit(EX_OSERR);
+}
+
+#ifdef OLD_SETPROCTITLE
+void
+inetd_setproctitle(a, s)
+ char *a;
+ int s;
+{
+ int size;
+ char *cp;
+ struct sockaddr_in sin;
+ char buf[80];
+
+ cp = Argv[0];
+ size = sizeof(sin);
+ if (getpeername(s, (struct sockaddr *)&sin, &size) == 0)
+ (void) sprintf(buf, "-%s [%s]", a, inet_ntoa(sin.sin_addr));
+ else
+ (void) sprintf(buf, "-%s", a);
+ strncpy(cp, buf, LastArg - cp);
+ cp += strlen(cp);
+ while (cp < LastArg)
+ *cp++ = ' ';
+}
+#else
+void
+inetd_setproctitle(a, s)
+ char *a;
+ int s;
+{
+ int size;
+ struct sockaddr_in sin;
+ char buf[80];
+
+ size = sizeof(sin);
+ if (getpeername(s, (struct sockaddr *)&sin, &size) == 0)
+ (void) sprintf(buf, "%s [%s]", a, inet_ntoa(sin.sin_addr));
+ else
+ (void) sprintf(buf, "%s", a);
+ setproctitle("%s", buf);
+}
+#endif
+
+
+/*
+ * Internet services provided internally by inetd:
+ */
+#define BUFSIZE 8192
+
+/* ARGSUSED */
+void
+echo_stream(s, sep) /* Echo service -- echo data back */
+ int s;
+ struct servtab *sep;
+{
+ char buffer[BUFSIZE];
+ int i;
+
+ inetd_setproctitle(sep->se_service, s);
+ while ((i = read(s, buffer, sizeof(buffer))) > 0 &&
+ write(s, buffer, i) > 0)
+ ;
+ exit(0);
+}
+
+int check_loop(sin, sep)
+ struct sockaddr_in *sin;
+ struct servtab *sep;
+{
+ struct servtab *se2;
+
+ for (se2 = servtab; se2; se2 = se2->se_next) {
+ if (!se2->se_bi || se2->se_socktype != SOCK_DGRAM)
+ continue;
+
+ if (sin->sin_port == se2->se_ctrladdr.sin_port) {
+ syslog(LOG_WARNING,
+ "%s/%s:%s/%s loop request REFUSED from %s",
+ sep->se_service, sep->se_proto,
+ se2->se_service, se2->se_proto,
+ inet_ntoa(sin->sin_addr));
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/* ARGSUSED */
+void
+echo_dg(s, sep) /* Echo service -- echo data back */
+ int s;
+ struct servtab *sep;
+{
+ char buffer[BUFSIZE];
+ int i, size;
+ struct sockaddr_in sin;
+
+ size = sizeof(sin);
+ if ((i = recvfrom(s, buffer, sizeof(buffer), 0,
+ (struct sockaddr *)&sin, &size)) < 0)
+ return;
+
+ if (check_loop(&sin, sep))
+ return;
+
+ (void) sendto(s, buffer, i, 0, (struct sockaddr *)&sin,
+ sizeof(sin));
+}
+
+/* ARGSUSED */
+void
+discard_stream(s, sep) /* Discard service -- ignore data */
+ int s;
+ struct servtab *sep;
+{
+ int ret;
+ char buffer[BUFSIZE];
+
+ inetd_setproctitle(sep->se_service, s);
+ while (1) {
+ while ((ret = read(s, buffer, sizeof(buffer))) > 0)
+ ;
+ if (ret == 0 || errno != EINTR)
+ break;
+ }
+ exit(0);
+}
+
+/* ARGSUSED */
+void
+discard_dg(s, sep) /* Discard service -- ignore data */
+ int s;
+ struct servtab *sep;
+{
+ char buffer[BUFSIZE];
+
+ (void) read(s, buffer, sizeof(buffer));
+}
+
+#include <ctype.h>
+#define LINESIZ 72
+char ring[128];
+char *endring;
+
+void
+initring()
+{
+ int i;
+
+ endring = ring;
+
+ for (i = 0; i <= 128; ++i)
+ if (isprint(i))
+ *endring++ = i;
+}
+
+/* ARGSUSED */
+void
+chargen_stream(s, sep) /* Character generator */
+ int s;
+ struct servtab *sep;
+{
+ int len;
+ char *rs, text[LINESIZ+2];
+
+ inetd_setproctitle(sep->se_service, s);
+
+ if (!endring) {
+ initring();
+ rs = ring;
+ }
+
+ text[LINESIZ] = '\r';
+ text[LINESIZ + 1] = '\n';
+ for (rs = ring;;) {
+ if ((len = endring - rs) >= LINESIZ)
+ memmove(text, rs, LINESIZ);
+ else {
+ memmove(text, rs, len);
+ memmove(text + len, ring, LINESIZ - len);
+ }
+ if (++rs == endring)
+ rs = ring;
+ if (write(s, text, sizeof(text)) != sizeof(text))
+ break;
+ }
+ exit(0);
+}
+
+/* ARGSUSED */
+void
+chargen_dg(s, sep) /* Character generator */
+ int s;
+ struct servtab *sep;
+{
+ struct sockaddr_in sin;
+ static char *rs;
+ int len, size;
+ char text[LINESIZ+2];
+
+ if (endring == 0) {
+ initring();
+ rs = ring;
+ }
+
+ size = sizeof(sin);
+ if (recvfrom(s, text, sizeof(text), 0,
+ (struct sockaddr *)&sin, &size) < 0)
+ return;
+
+ if (check_loop(&sin, sep))
+ return;
+
+ if ((len = endring - rs) >= LINESIZ)
+ memmove(text, rs, LINESIZ);
+ else {
+ memmove(text, rs, len);
+ memmove(text + len, ring, LINESIZ - len);
+ }
+ if (++rs == endring)
+ rs = ring;
+ text[LINESIZ] = '\r';
+ text[LINESIZ + 1] = '\n';
+ (void) sendto(s, text, sizeof(text), 0,
+ (struct sockaddr *)&sin, sizeof(sin));
+}
+
+/*
+ * Return a machine readable date and time, in the form of the
+ * number of seconds since midnight, Jan 1, 1900. Since gettimeofday
+ * returns the number of seconds since midnight, Jan 1, 1970,
+ * we must add 2208988800 seconds to this figure to make up for
+ * some seventy years Bell Labs was asleep.
+ */
+
+long
+machtime()
+{
+ struct timeval tv;
+
+ if (gettimeofday(&tv, (struct timezone *)0) < 0) {
+ if (debug)
+ warnx("unable to get time of day");
+ return (0L);
+ }
+#define OFFSET ((u_long)25567 * 24*60*60)
+ return (htonl((long)(tv.tv_sec + OFFSET)));
+#undef OFFSET
+}
+
+/* ARGSUSED */
+void
+machtime_stream(s, sep)
+ int s;
+ struct servtab *sep;
+{
+ long result;
+
+ result = machtime();
+ (void) write(s, (char *) &result, sizeof(result));
+}
+
+/* ARGSUSED */
+void
+machtime_dg(s, sep)
+ int s;
+ struct servtab *sep;
+{
+ long result;
+ struct sockaddr_in sin;
+ int size;
+
+ size = sizeof(sin);
+ if (recvfrom(s, (char *)&result, sizeof(result), 0,
+ (struct sockaddr *)&sin, &size) < 0)
+ return;
+
+ if (check_loop(&sin, sep))
+ return;
+
+ result = machtime();
+ (void) sendto(s, (char *) &result, sizeof(result), 0,
+ (struct sockaddr *)&sin, sizeof(sin));
+}
+
+/* ARGSUSED */
+void
+daytime_stream(s, sep) /* Return human-readable time of day */
+ int s;
+ struct servtab *sep;
+{
+ char buffer[256];
+ time_t clock;
+
+ clock = time((time_t *) 0);
+
+ (void) sprintf(buffer, "%.24s\r\n", ctime(&clock));
+ (void) write(s, buffer, strlen(buffer));
+}
+
+/* ARGSUSED */
+void
+daytime_dg(s, sep) /* Return human-readable time of day */
+ int s;
+ struct servtab *sep;
+{
+ char buffer[256];
+ time_t clock;
+ struct sockaddr_in sin;
+ int size;
+
+ clock = time((time_t *) 0);
+
+ size = sizeof(sin);
+ if (recvfrom(s, buffer, sizeof(buffer), 0,
+ (struct sockaddr *)&sin, &size) < 0)
+ return;
+
+ if (check_loop(&sin, sep))
+ return;
+
+ (void) sprintf(buffer, "%.24s\r\n", ctime(&clock));
+ (void) sendto(s, buffer, strlen(buffer), 0,
+ (struct sockaddr *)&sin, sizeof(sin));
+}
+
+/*
+ * print_service:
+ * Dump relevant information to stderr
+ */
+void
+print_service(action, sep)
+ char *action;
+ struct servtab *sep;
+{
+ fprintf(stderr,
+#ifdef LOGIN_CAP
+ "%s: %s proto=%s accept=%d max=%d user=%s group=%s class=%s builtin=%x server=%s\n",
+#else
+ "%s: %s proto=%s accept=%d max=%d user=%s group=%s builtin=%x server=%s\n",
+#endif
+ action, sep->se_service, sep->se_proto,
+ sep->se_accept, sep->se_maxchild, sep->se_user, sep->se_group,
+#ifdef LOGIN_CAP
+ sep->se_class,
+#endif
+ (int)sep->se_bi, sep->se_server);
+}
+
+/*
+ * Based on TCPMUX.C by Mark K. Lottor November 1988
+ * sri-nic::ps:<mkl>tcpmux.c
+ */
+
+
+static int /* # of characters upto \r,\n or \0 */
+getline(fd, buf, len)
+ int fd;
+ char *buf;
+ int len;
+{
+ int count = 0, n;
+
+ do {
+ n = read(fd, buf, len-count);
+ if (n == 0)
+ return (count);
+ if (n < 0)
+ return (-1);
+ while (--n >= 0) {
+ if (*buf == '\r' || *buf == '\n' || *buf == '\0')
+ return (count);
+ count++;
+ buf++;
+ }
+ } while (count < len);
+ return (count);
+}
+
+#define MAX_SERV_LEN (256+2) /* 2 bytes for \r\n */
+
+#define strwrite(fd, buf) (void) write(fd, buf, sizeof(buf)-1)
+
+struct servtab *
+tcpmux(s)
+ int s;
+{
+ struct servtab *sep;
+ char service[MAX_SERV_LEN+1];
+ int len;
+
+ /* Get requested service name */
+ if ((len = getline(s, service, MAX_SERV_LEN)) < 0) {
+ strwrite(s, "-Error reading service name\r\n");
+ return (NULL);
+ }
+ service[len] = '\0';
+
+ if (debug)
+ warnx("tcpmux: someone wants %s", service);
+
+ /*
+ * Help is a required command, and lists available services,
+ * one per line.
+ */
+ if (!strcasecmp(service, "help")) {
+ for (sep = servtab; sep; sep = sep->se_next) {
+ if (!ISMUX(sep))
+ continue;
+ (void)write(s,sep->se_service,strlen(sep->se_service));
+ strwrite(s, "\r\n");
+ }
+ return (NULL);
+ }
+
+ /* Try matching a service in inetd.conf with the request */
+ for (sep = servtab; sep; sep = sep->se_next) {
+ if (!ISMUX(sep))
+ continue;
+ if (!strcasecmp(service, sep->se_service)) {
+ if (ISMUXPLUS(sep)) {
+ strwrite(s, "+Go\r\n");
+ }
+ return (sep);
+ }
+ }
+ strwrite(s, "-Service not available\r\n");
+ return (NULL);
+}
+
+#define CPMHSIZE 256
+#define CPMHMASK (CPMHSIZE-1)
+#define CHTGRAN 10
+#define CHTSIZE 6
+
+typedef struct CTime {
+ unsigned long ct_Ticks;
+ int ct_Count;
+} CTime;
+
+typedef struct CHash {
+ struct in_addr ch_Addr;
+ time_t ch_LTime;
+ char *ch_Service;
+ CTime ch_Times[CHTSIZE];
+} CHash;
+
+CHash CHashAry[CPMHSIZE];
+
+int
+cpmip(sep, ctrl)
+ struct servtab *sep;
+ int ctrl;
+{
+ struct sockaddr_in rsin;
+ int rsinLen = sizeof(rsin);
+ int r = 0;
+
+ /*
+ * If getpeername() fails, just let it through (if logging is
+ * enabled the condition is caught elsewhere)
+ */
+
+ if (sep->se_maxcpm > 0 &&
+ getpeername(ctrl, (struct sockaddr *)&rsin, &rsinLen) == 0 ) {
+ time_t t = time(NULL);
+ int hv = 0xABC3D20F;
+ int i;
+ int cnt = 0;
+ CHash *chBest = NULL;
+ unsigned int ticks = t / CHTGRAN;
+
+ {
+ char *p;
+ int i;
+
+ for (i = 0, p = (char *)&rsin.sin_addr;
+ i < sizeof(rsin.sin_addr);
+ ++i, ++p) {
+ hv = (hv << 5) ^ (hv >> 23) ^ *p;
+ }
+ hv = (hv ^ (hv >> 16));
+ }
+ for (i = 0; i < 5; ++i) {
+ CHash *ch = &CHashAry[(hv + i) & CPMHMASK];
+
+ if (rsin.sin_addr.s_addr == ch->ch_Addr.s_addr &&
+ ch->ch_Service && strcmp(sep->se_service,
+ ch->ch_Service) == 0) {
+ chBest = ch;
+ break;
+ }
+ if (chBest == NULL || ch->ch_LTime == 0 ||
+ ch->ch_LTime < chBest->ch_LTime) {
+ chBest = ch;
+ }
+ }
+ if (rsin.sin_addr.s_addr != chBest->ch_Addr.s_addr ||
+ chBest->ch_Service == NULL ||
+ strcmp(sep->se_service, chBest->ch_Service) != 0) {
+ chBest->ch_Addr = rsin.sin_addr;
+ if (chBest->ch_Service)
+ free(chBest->ch_Service);
+ chBest->ch_Service = strdup(sep->se_service);
+ bzero(chBest->ch_Times, sizeof(chBest->ch_Times));
+ }
+ chBest->ch_LTime = t;
+ {
+ CTime *ct = &chBest->ch_Times[ticks % CHTSIZE];
+ if (ct->ct_Ticks != ticks) {
+ ct->ct_Ticks = ticks;
+ ct->ct_Count = 0;
+ }
+ ++ct->ct_Count;
+ }
+ for (i = 0; i < CHTSIZE; ++i) {
+ CTime *ct = &chBest->ch_Times[i];
+ if (ct->ct_Ticks <= ticks &&
+ ct->ct_Ticks >= ticks - CHTSIZE) {
+ cnt += ct->ct_Count;
+ }
+ }
+ if (cnt * (CHTSIZE * CHTGRAN) / 60 > sep->se_maxcpm) {
+ r = -1;
+ syslog(LOG_ERR,
+ "%s from %s exceeded counts/min limit %d/%d",
+ sep->se_service, inet_ntoa(rsin.sin_addr), cnt,
+ sep->se_maxcpm );
+ }
+ }
+ return(r);
+}
diff --git a/usr.sbin/inetd/pathnames.h b/usr.sbin/inetd/pathnames.h
new file mode 100644
index 0000000..503cd21
--- /dev/null
+++ b/usr.sbin/inetd/pathnames.h
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)pathnames.h 8.1 (Berkeley) 6/6/93
+ * $Id$
+ */
+
+#include <paths.h>
+
+#define _PATH_INETDCONF "/etc/inetd.conf"
+#define _PATH_INETDPID _PATH_VARRUN "inetd.pid"
diff --git a/usr.sbin/iostat/Makefile b/usr.sbin/iostat/Makefile
new file mode 100644
index 0000000..b67da20
--- /dev/null
+++ b/usr.sbin/iostat/Makefile
@@ -0,0 +1,11 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= iostat
+CFLAGS+=-I${.CURDIR}/../../usr.bin/vmstat -I${.CURDIR}/../../sys
+MAN8= iostat.8
+DPADD= ${LIBKVM}
+LDADD= -lkvm
+BINGRP= kmem
+BINMODE=2555
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/iostat/iostat.8 b/usr.sbin/iostat/iostat.8
new file mode 100644
index 0000000..861acef
--- /dev/null
+++ b/usr.sbin/iostat/iostat.8
@@ -0,0 +1,144 @@
+.\" 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 June 6, 1993
+.Dt IOSTAT 8
+.Os BSD 4
+.Sh NAME
+.Nm iostat
+.Nd report
+.Tn I/O
+statistics
+.Sh SYNOPSIS
+.Nm iostat
+.Op Fl c Ar count
+.Op Fl M Ar core
+.Op Fl N Ar system
+.Op Fl w Ar wait
+.Op Ar drives
+.Sh DESCRIPTION
+.Nm Iostat
+displays kernel
+.Tn I/O
+statistics on terminal, disk and cpu
+operations.
+.Pp
+The options are as follows:
+.Bl -tag -width flag
+.It Fl c
+Repeat the display
+.Ar count
+times.
+The first display is for the time since a reboot and each subsequent
+report is for the time period since the last display.
+If no
+.Ar wait
+interval is specified, the default is 1 second.
+.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
+Extract the name list from the specified system instead of the default
+.Dq Pa /kernel .
+.It Fl w
+Pause
+.Ar wait
+seconds between each display.
+If no repeat
+.Ar count
+is specified, the default is infinity.
+.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 disks
+Disk operations (this field is system dependent).
+The header of the field is the disk name and unit number.
+If more than four disk drives are configured in the system,
+.Nm iostat
+displays only the first four drives.
+To force
+.Nm iostat
+to display specific drives, their names may be supplied on the command
+line.
+.Pp
+.Bl -tag -width indent -compact
+.It sps
+sectors transferred per second
+.It tps
+transfers per second
+.It msps
+milliseconds per average seek (including implied
+seeks and rotational latency)
+.El
+.It cpu
+.Bl -tag -width indent -compact
+.It \&us
+% of cpu time in user mode
+.It \&ni
+% of cpu time in user mode running niced processes
+.It \&sy
+% of cpu time in system mode
+.It \&in
+% of cpu time in interrupt mode
+.It \&id
+% of cpu time in idle mode
+.El
+.El
+.Sh FILES
+.Bl -tag -width /dev/kmem -compact
+.It Pa /kernel
+Default kernel namelist.
+.It Pa /dev/kmem
+Default memory file.
+.El
+.Sh 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" .
diff --git a/usr.sbin/iostat/iostat.c b/usr.sbin/iostat/iostat.c
new file mode 100644
index 0000000..d1531ea
--- /dev/null
+++ b/usr.sbin/iostat/iostat.c
@@ -0,0 +1,399 @@
+/*-
+ * 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.
+ */
+
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 1986, 1991, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)iostat.c 8.2 (Berkeley) 1/26/94";
+#endif
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/buf.h>
+#include <sys/dkstat.h>
+
+#include <err.h>
+#include <ctype.h>
+#include <fcntl.h>
+#include <kvm.h>
+#include <limits.h>
+#include <nlist.h>
+#include <paths.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+struct nlist namelist[] = {
+#define X_DK_TIME 0
+ { "_dk_time" },
+#define X_DK_XFER 1
+ { "_dk_xfer" },
+#define X_DK_WDS 2
+ { "_dk_wds" },
+#define X_TK_NIN 3
+ { "_tk_nin" },
+#define X_TK_NOUT 4
+ { "_tk_nout" },
+#define X_DK_SEEK 5
+ { "_dk_seek" },
+#define X_CP_TIME 6
+ { "_cp_time" },
+#define X_DK_WPMS 7
+ { "_dk_wpms" },
+#define X_HZ 8
+ { "_hz" },
+#define X_STATHZ 9
+ { "_stathz" },
+#define X_DK_NDRIVE 10
+ { "_dk_ndrive" },
+#define X_END 10
+#if defined(hp300) || defined(luna68k)
+#define X_HPDINIT (X_END+1)
+ { "_hp_dinit" },
+#endif
+#if defined(i386)
+#define X_DK_NAMES (X_END+1)
+ { "_dk_names" },
+#endif
+#ifdef mips
+#define X_SCSI_DINIT (X_END+1)
+ { "_scsi_dinit" },
+#endif
+#ifdef tahoe
+#define X_VBDINIT (X_END+1)
+ { "_vbdinit" },
+#endif
+#ifdef vax
+ { "_mbdinit" },
+#define X_MBDINIT (X_END+1)
+ { "_ubdinit" },
+#define X_UBDINIT (X_END+2)
+#endif
+ { NULL },
+};
+
+struct _disk {
+ long cp_time[CPUSTATES];
+ long *dk_time;
+ long *dk_wds;
+ long *dk_seek;
+ long *dk_xfer;
+ long tk_nin;
+ long tk_nout;
+} cur, last;
+
+kvm_t *kd;
+double etime;
+long *dk_wpms;
+int dk_ndrive, *dr_select, hz, kmemfd, ndrives;
+char **dr_name;
+
+#define nlread(x, v) \
+ kvm_read(kd, namelist[x].n_value, &(v), sizeof(v))
+
+#include "names.c" /* XXX */
+
+void cpustats __P((void));
+void dkstats __P((void));
+void phdr __P((int));
+static void usage __P((void));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ register int i;
+ long tmp;
+ int ch, hdrcnt, reps, interval, stathz, ndrives;
+ char **cp, *memf, *nlistf, buf[30];
+ char errbuf[_POSIX2_LINE_MAX];
+
+ interval = reps = 0;
+ nlistf = memf = NULL;
+ while ((ch = getopt(argc, argv, "c:M:N:w:")) != -1)
+ switch(ch) {
+ case 'c':
+ if ((reps = atoi(optarg)) <= 0)
+ errx(1, "repetition count <= 0");
+ break;
+ case 'M':
+ memf = optarg;
+ break;
+ case 'N':
+ nlistf = optarg;
+ break;
+ case 'w':
+ if ((interval = atoi(optarg)) <= 0)
+ errx(1, "interval <= 0");
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ /*
+ * Discard setgid privileges if not the running kernel so that bad
+ * guys can't print interesting stuff from kernel memory.
+ */
+ if (nlistf != NULL || memf != NULL)
+ setgid(getgid());
+
+ kd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, errbuf);
+ if (kd == 0)
+ errx(1, "kvm_openfiles: %s", errbuf);
+ if (kvm_nlist(kd, namelist) == -1)
+ errx(1, "kvm_nlist: %s", kvm_geterr(kd));
+ if (namelist[X_DK_NDRIVE].n_type == 0)
+ errx(1, "dk_ndrive not found in namelist");
+ (void)nlread(X_DK_NDRIVE, dk_ndrive);
+ if (dk_ndrive < 0)
+ errx(1, "invalid dk_ndrive %d", dk_ndrive);
+
+ cur.dk_time = calloc(dk_ndrive, sizeof(long));
+ cur.dk_wds = calloc(dk_ndrive, sizeof(long));
+ cur.dk_seek = calloc(dk_ndrive, sizeof(long));
+ cur.dk_xfer = calloc(dk_ndrive, sizeof(long));
+ last.dk_time = calloc(dk_ndrive, sizeof(long));
+ last.dk_wds = calloc(dk_ndrive, sizeof(long));
+ last.dk_seek = calloc(dk_ndrive, sizeof(long));
+ last.dk_xfer = calloc(dk_ndrive, sizeof(long));
+ dr_select = calloc(dk_ndrive, sizeof(int));
+ dr_name = calloc(dk_ndrive, sizeof(char *));
+ dk_wpms = calloc(dk_ndrive, sizeof(long));
+
+ for (i = 0; i < dk_ndrive; i++) {
+ (void)sprintf(buf, "dk%d", i);
+ dr_name[i] = strdup(buf);
+ }
+ if (!read_names())
+ exit(1);
+ (void)nlread(X_HZ, hz);
+ (void)nlread(X_STATHZ, stathz);
+ if (stathz)
+ hz = stathz;
+ (void)kvm_read(kd, namelist[X_DK_WPMS].n_value, dk_wpms,
+ dk_ndrive * sizeof(dk_wpms));
+
+ /*
+ * Choose drives to be displayed. Priority goes to (in order) drives
+ * supplied as arguments and default drives. If everything isn't
+ * filled in and there are drives not taken care of, display the first
+ * few that fit.
+ *
+ * The backward compatibility #ifdefs permit the syntax:
+ * iostat [ drives ] [ interval [ count ] ]
+ */
+#define BACKWARD_COMPATIBILITY
+ for (ndrives = 0; *argv; ++argv) {
+#ifdef BACKWARD_COMPATIBILITY
+ if (isdigit(**argv))
+ break;
+#endif
+ for (i = 0; i < dk_ndrive; i++) {
+ if (strcmp(dr_name[i], *argv))
+ continue;
+ dr_select[i] = 1;
+ ++ndrives;
+ }
+ }
+#ifdef BACKWARD_COMPATIBILITY
+ if (*argv) {
+ interval = atoi(*argv);
+ if (*++argv)
+ reps = atoi(*argv);
+ }
+#endif
+
+ if (interval) {
+ if (!reps)
+ reps = -1;
+ } else
+ if (reps)
+ interval = 1;
+
+ for (i = 0; i < dk_ndrive && ndrives < 4; i++) {
+ if (dr_select[i] || dk_wpms[i] == 0)
+ continue;
+ for (cp = defdrives; *cp; cp++)
+ if (strcmp(dr_name[i], *cp) == 0) {
+ dr_select[i] = 1;
+ ++ndrives;
+ break;
+ }
+ }
+ for (i = 0; i < dk_ndrive && ndrives < 4; i++) {
+ if (dr_select[i])
+ continue;
+ dr_select[i] = 1;
+ ++ndrives;
+ }
+
+ (void)signal(SIGCONT, phdr);
+
+ for (hdrcnt = 1;;) {
+ if (!--hdrcnt) {
+ phdr(0);
+ hdrcnt = 20;
+ }
+ (void)kvm_read(kd, namelist[X_DK_TIME].n_value,
+ cur.dk_time, dk_ndrive * sizeof(long));
+ (void)kvm_read(kd, namelist[X_DK_XFER].n_value,
+ cur.dk_xfer, dk_ndrive * sizeof(long));
+ (void)kvm_read(kd, namelist[X_DK_WDS].n_value,
+ cur.dk_wds, dk_ndrive * sizeof(long));
+ (void)kvm_read(kd, namelist[X_DK_SEEK].n_value,
+ cur.dk_seek, dk_ndrive * sizeof(long));
+ (void)kvm_read(kd, namelist[X_TK_NIN].n_value,
+ &cur.tk_nin, sizeof(cur.tk_nin));
+ (void)kvm_read(kd, namelist[X_TK_NOUT].n_value,
+ &cur.tk_nout, sizeof(cur.tk_nout));
+ (void)kvm_read(kd, namelist[X_CP_TIME].n_value,
+ cur.cp_time, sizeof(cur.cp_time));
+ for (i = 0; i < dk_ndrive; i++) {
+ if (!dr_select[i])
+ continue;
+#define X(fld) tmp = cur.fld[i]; cur.fld[i] -= last.fld[i]; last.fld[i] = tmp
+ X(dk_xfer);
+ X(dk_seek);
+ X(dk_wds);
+ X(dk_time);
+ }
+ tmp = cur.tk_nin;
+ cur.tk_nin -= last.tk_nin;
+ last.tk_nin = tmp;
+ tmp = cur.tk_nout;
+ cur.tk_nout -= last.tk_nout;
+ last.tk_nout = tmp;
+ etime = 0;
+ for (i = 0; i < CPUSTATES; i++) {
+ X(cp_time);
+ etime += cur.cp_time[i];
+ }
+ if (etime == 0.0)
+ etime = 1.0;
+ etime /= (float)hz;
+ (void)printf("%4.0f%5.0f",
+ cur.tk_nin / etime, cur.tk_nout / etime);
+ dkstats();
+ cpustats();
+ (void)printf("\n");
+ (void)fflush(stdout);
+
+ if (reps >= 0 && --reps <= 0)
+ break;
+ (void)sleep(interval);
+ }
+ exit(0);
+}
+
+/* ARGUSED */
+void
+phdr(signo)
+ int signo;
+{
+ register int i;
+
+ (void)printf(" tty");
+ for (i = 0; i < dk_ndrive; i++)
+ if (dr_select[i])
+ (void)printf(" %4.4s ", dr_name[i]);
+ (void)printf(" cpu\n tin tout");
+ for (i = 0; i < dk_ndrive; i++)
+ if (dr_select[i])
+ (void)printf(" sps tps msps ");
+ (void)printf(" us ni sy in id\n");
+}
+
+void
+dkstats()
+{
+ register int dn;
+ double atime, itime, msps, words, xtime;
+
+ for (dn = 0; dn < dk_ndrive; ++dn) {
+ if (!dr_select[dn])
+ continue;
+ words = (double)cur.dk_wds[dn] * 32; /* words xfer'd */
+ (void)printf("%4.0f", /* sectors */
+ words / (DEV_BSIZE / 2) / etime);
+
+ (void)printf("%4.0f", cur.dk_xfer[dn] / etime);
+
+ if (dk_wpms[dn] && cur.dk_xfer[dn]) {
+ atime = cur.dk_time[dn]; /* ticks disk busy */
+ atime /= (float)hz; /* ticks to seconds */
+ xtime = words / dk_wpms[dn]; /* transfer time */
+ itime = atime - xtime; /* time not xfer'ing */
+ if (itime < 0)
+ msps = 0;
+ else
+ msps = itime * 1000 / cur.dk_xfer[dn];
+ } else
+ msps = 0;
+ (void)printf("%5.1f ", msps);
+ }
+}
+
+void
+cpustats()
+{
+ register int state;
+ double time;
+
+ time = 0;
+ for (state = 0; state < CPUSTATES; ++state)
+ time += cur.cp_time[state];
+ for (state = 0; state < CPUSTATES; ++state)
+ (void)printf("%3.0f",
+ 100. * cur.cp_time[state] / (time ? time : 1));
+}
+
+static void
+usage()
+{
+ (void)fprintf(stderr,
+"usage: iostat [-c count] [-M core] [-N system] [-w wait] [drives]\n");
+ exit(1);
+}
diff --git a/usr.sbin/kbdcontrol/Makefile b/usr.sbin/kbdcontrol/Makefile
new file mode 100644
index 0000000..ad91cb0
--- /dev/null
+++ b/usr.sbin/kbdcontrol/Makefile
@@ -0,0 +1,6 @@
+PROG= kbdcontrol
+SRCS= kbdcontrol.c lex.l
+DPADD+= ${LIBL}
+LDADD+= -ll
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/kbdcontrol/kbdcontrol.1 b/usr.sbin/kbdcontrol/kbdcontrol.1
new file mode 100644
index 0000000..603d6e4
--- /dev/null
+++ b/usr.sbin/kbdcontrol/kbdcontrol.1
@@ -0,0 +1,101 @@
+.\"
+.\" kbdcontrol - a utility for manipulating the syscons keyboard driver section
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" @(#)kbdcontrol.1
+.\"
+.Dd May 22, 1994
+.Dt kbdcontrol 1
+.Os FreeBSD
+.Sh NAME
+.Nm kbdcontrol
+.Nd a utility for manipulating the syscons keyboard driver section
+.Sh SYNOPSIS
+.Nm
+.Op Fl dFx
+.Op Fl b Ar duration.pitch | Ar belltype
+.Op Fl r Ar delay.repeat | Ar speed
+.Op Fl l Ar mapfile
+.Op Fl f Ar # Ar string
+.Op Fl h Ar size
+.Op Fl L Ar mapfile
+.Sh DESCRIPTION
+The
+.Nm
+command is used to set various keyboard related options for the syscons
+console driver, such as keymap, keyboard repeat & delay rates, bell
+characteristics etc.
+.Pp
+The following command line options are supported:
+.Bl -tag -width indent
+.It Fl b Ar duration.pitch | Ar belltype
+Set the bell duration and pitch values.
+If a
+.Ar belltype
+argument is specified, it may be one of
+.Ar normal
+which set sound parameters back to normal values, or
+.Ar visual
+which set the bell to visual mode, i.e. flashes the screen instead.
+.It Fl r Ar delay.repeat | Ar speed
+Set keyboard
+.Ar delay (250, 500, 750, 1000)
+and
+.Ar repeat (34, 38, 42, 46, 50, 55, 59, 63, 68, 76, 84, 92, 100, 110, 118, 126,
+136, 152, 168, 184, 200, 220, 236, 252, 272, 304, 336, 368, 400, 440,
+472, 504)
+rates, or if a
+.Ar speed
+argument is specified, it may be one of
+.Ar slow
+(1000.504),
+.Ar fast
+(250.34)
+or
+.Ar normal
+(500.126).
+.It Fl l Ar mapfile
+Install keyboard map file from
+.Ar mapfile .
+.It Fl d
+Dump the current keyboard map onto stdout.
+.It Fl f Ar # Ar string
+.BI "\-f\ " #\ string
+Set function key number
+.Ar #
+to send
+.Ar string .
+.It Fl F
+Set function keys back to the standard definitions.
+.It Fl x
+Use hexadecimal numbers in keyboard map dump.
+.It Fl h Ar size
+Set history buffer size to
+.Ar size
+lines.
+.It Fl L Ar mapfile
+Load keyboard map file from
+.Ar mapfile
+and write the
+.Ft "struct keymap"
+compiled from it to stdout.
+.El
+.Sh FILES
+.Bl -tag -width /usr/share/syscons/keymaps -compact
+.Pa /usr/share/syscons/keymaps
+.Sh "BUGS"
+Report when found.
+.Sh "SEE ALSO"
+.Xr vidcontrol 1 ,
+.Xr keyboard 4 ,
+.Xr screen 4
+.Sh AUTHORS
+.An Søren Schmidt Aq sos@FreeBSD.org
diff --git a/usr.sbin/kbdcontrol/kbdcontrol.c b/usr.sbin/kbdcontrol/kbdcontrol.c
new file mode 100644
index 0000000..b6f3a84
--- /dev/null
+++ b/usr.sbin/kbdcontrol/kbdcontrol.c
@@ -0,0 +1,601 @@
+/*-
+ * Copyright (c) 1994-1995 Søren Schmidt
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * in this position and unchanged.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software withough specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#include <ctype.h>
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <machine/console.h>
+#include "path.h"
+#include "lex.h"
+
+char ctrl_names[32][4] = {
+ "nul", "soh", "stx", "etx", "eot", "enq", "ack", "bel",
+ "bs ", "ht ", "nl ", "vt ", "ff ", "cr ", "so ", "si ",
+ "dle", "dc1", "dc2", "dc3", "dc4", "nak", "syn", "etb",
+ "can", "em ", "sub", "esc", "fs ", "gs ", "rs ", "ns "
+ };
+
+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;
+
+static void usage __P((void));
+
+char *
+nextarg(int ac, char **av, int *indp, int oc)
+{
+ if (*indp < ac)
+ return(av[(*indp)++]);
+ warnx("option requires two arguments -- %c", oc);
+ usage();
+ return("");
+}
+
+
+char *
+mkfullname(const char *s1, const char *s2, const char *s3)
+{
+ static char *buf = NULL;
+ static int bufl = 0;
+ int f;
+
+ f = strlen(s1) + strlen(s2) + strlen(s3) + 1;
+ if (f > bufl)
+ if (buf)
+ buf = (char *)realloc(buf, f);
+ else
+ buf = (char *)malloc(f);
+ if (!buf) {
+ bufl = 0;
+ return(NULL);
+ }
+
+ bufl = f;
+ strcpy(buf, s1);
+ strcat(buf, s2);
+ strcat(buf, s3);
+ return(buf);
+}
+
+
+int
+get_entry()
+{
+ switch (yylex()) {
+ case TNOP:
+ return NOP | 0x100;
+ case TLSH:
+ return LSH | 0x100;
+ case TRSH:
+ return RSH | 0x100;
+ case TCLK:
+ return CLK | 0x100;
+ case TNLK:
+ return NLK | 0x100;
+ case TSLK:
+ return SLK | 0x100;
+ case TBTAB:
+ return BTAB | 0x100;
+ case TLALT:
+ return LALT | 0x100;
+ case TLCTR:
+ return LCTR | 0x100;
+ case TNEXT:
+ return NEXT | 0x100;
+ case TRCTR:
+ return RCTR | 0x100;
+ case TRALT:
+ return RALT | 0x100;
+ case TALK:
+ return ALK | 0x100;
+ case TASH:
+ return ASH | 0x100;
+ case TMETA:
+ return META | 0x100;
+ case TRBT:
+ return RBT | 0x100;
+ case TDBG:
+ return DBG | 0x100;
+ case TSUSP:
+ return SUSP | 0x100;
+ case TFUNC:
+ if (F(number) > L_FN)
+ return -1;
+ return F(number) | 0x100;
+ case TSCRN:
+ if (S(number) > L_SCR)
+ return -1;
+ return S(number) | 0x100;
+ case TLET:
+ return (unsigned char)letter;
+ case TNUM:
+ if (number < 0 || number > 255)
+ return -1;
+ return number;
+ default:
+ return -1;
+ }
+}
+
+
+int
+get_key_definition_line(FILE* fd, keymap_t *map)
+{
+ int i, def, scancode;
+
+ yyin = fd;
+
+ /* get scancode number */
+ if (yylex() != TNUM)
+ return -1;
+ 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 (yylex() != TFLAG)
+ return -1;
+ map->key[scancode].flgs = number;
+ return scancode;
+}
+
+
+void
+print_entry(FILE *fp, int value)
+{
+ int val = value & 0xFF;
+
+ switch (value) {
+ case NOP | 0x100:
+ fprintf(fp, " nop ");
+ break;
+ case LSH | 0x100:
+ fprintf(fp, " lshift");
+ break;
+ case RSH | 0x100:
+ fprintf(fp, " rshift");
+ break;
+ case CLK | 0x100:
+ fprintf(fp, " clock ");
+ break;
+ case NLK | 0x100:
+ fprintf(fp, " nlock ");
+ break;
+ case SLK | 0x100:
+ fprintf(fp, " slock ");
+ break;
+ case BTAB | 0x100:
+ fprintf(fp, " btab ");
+ break;
+ case LALT | 0x100:
+ fprintf(fp, " lalt ");
+ break;
+ case LCTR | 0x100:
+ fprintf(fp, " lctrl ");
+ break;
+ case NEXT | 0x100:
+ fprintf(fp, " nscr ");
+ break;
+ case RCTR | 0x100:
+ fprintf(fp, " rctrl ");
+ break;
+ case RALT | 0x100:
+ fprintf(fp, " ralt ");
+ break;
+ case ALK | 0x100:
+ fprintf(fp, " alock ");
+ break;
+ case ASH | 0x100:
+ fprintf(fp, " ashift");
+ break;
+ case META | 0x100:
+ fprintf(fp, " meta ");
+ break;
+ case RBT | 0x100:
+ fprintf(fp, " boot ");
+ break;
+ case DBG | 0x100:
+ fprintf(fp, " debug ");
+ break;
+ 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 (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 key_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
+load_keymap(char *opt, int dumponly)
+{
+ keymap_t map;
+ FILE *fd;
+ int scancode, i, j;
+ struct key_t *kp;
+ char *name, *cp;
+ char *prefix[] = {"", "", KEYMAP_PATH, NULL};
+ char *postfix[] = {"", ".kbd", ".kbd"};
+
+ for (i=0; prefix[i]; i++) {
+ name = mkfullname(prefix[i], opt, postfix[i]);
+ if ((fd = fopen(name, "r")))
+ break;
+ }
+ if (fd == NULL) {
+ warn("keymap file not found");
+ return;
+ }
+ memset(&map, 0, sizeof(map));
+ while (1) {
+ if ((scancode = get_key_definition_line(fd, &map)) < 0)
+ break;
+ if (scancode > map.n_keys) map.n_keys = scancode;
+ }
+ 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("static const struct keymap keymap_%s = {\n"
+ "/*\n * Automatically generated from %s.\n"
+ " * DO NOT EDIT!\n */\n\n\t%u,\n{\n",
+ opt, name, (unsigned)map.n_keys);
+ for (i = 0; i < map.n_keys; i++) {
+ kp = &map.key[i];
+
+ printf("\t{{ ");
+ for (j = 0; j < NUM_STATES; j++)
+ printf("0x%02x%c", (unsigned)kp->map[j],
+ j == NUM_STATES-1? '}': ',');
+ printf(", 0x%x, 0x%x },\n",
+ (unsigned)kp->spcl, (unsigned)kp->flgs);
+ }
+ printf("}\n};\n\n");
+ return;
+ }
+ if (ioctl(0, PIO_KEYMAP, &map) < 0) {
+ warn("setting keymap");
+ fclose(fd);
+ return;
+ }
+}
+
+
+void
+print_keymap()
+{
+ keymap_t map;
+ int i;
+
+ if (ioctl(0, GIO_KEYMAP, &map) < 0)
+ err(1, "getting keymap");
+ 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<map.n_keys; i++)
+ print_key_definition_line(stdout, i, &map.key[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;
+
+ if (!strcmp(opt, "visual"))
+ bell = 1, duration = 1, pitch = 800;
+ else if (!strcmp(opt, "normal"))
+ bell = 0, duration = 1, pitch = 800;
+ else {
+ char *v1;
+
+ bell = 0;
+ duration = strtol(opt, &v1, 0);
+ if ((duration < 0) || (*v1 != '.'))
+ goto badopt;
+ opt = ++v1;
+ pitch = strtol(opt, &v1, 0);
+ if ((pitch < 0) || (*opt == '\0') || (*v1 != '\0')) {
+badopt:
+ warnx("argument to -b must be DURATION.PITCH");
+ return;
+ }
+ }
+
+ ioctl(0, CONS_BELLTYPE, &bell);
+ if (!bell)
+ fprintf(stderr, "[=%d;%dB", pitch, duration);
+}
+
+
+void
+set_keyrates(char *opt)
+{
+struct {
+ int rep:5;
+ int del:2;
+ int pad:1;
+ }rate;
+
+ if (!strcmp(opt, "slow"))
+ rate.del = 3, rate.rep = 31;
+ else if (!strcmp(opt, "normal"))
+ rate.del = 1, rate.rep = 15;
+ else if (!strcmp(opt, "fast"))
+ rate.del = rate.rep = 0;
+ else {
+ int n;
+ int delay, repeat;
+ char *v1;
+
+ delay = strtol(opt, &v1, 0);
+ if ((delay < 0) || (*v1 != '.'))
+ goto badopt;
+ opt = ++v1;
+ repeat = strtol(opt, &v1, 0);
+ if ((repeat < 0) || (*opt == '\0') || (*v1 != '\0')) {
+badopt:
+ warnx("argument to -r must be delay.repeat");
+ return;
+ }
+ for (n = 0; n < ndelays - 1; n++)
+ if (delay <= delays[n])
+ break;
+ rate.del = n;
+ for (n = 0; n < nrepeats - 1; n++)
+ if (repeat <= repeats[n])
+ break;
+ rate.rep = n;
+ }
+
+ if (ioctl(0, KDSETRAD, rate) < 0)
+ warn("setting keyboard rate");
+}
+
+
+void
+set_history(char *opt)
+{
+ int size;
+
+ size = atoi(opt);
+ if ((*opt == '\0') || size < 0) {
+ warnx("argument must be a positive number");
+ return;
+ }
+ if (ioctl(0, CONS_HISTORY, &size) == -1)
+ warn("setting history buffer size");
+}
+
+
+static void
+usage()
+{
+ fprintf(stderr, "%s\n%s\n%s\n",
+"usage: kbdcontrol [-dFx] [-b duration.pitch | belltype]",
+" [-r delay.repeat | speed] [-l mapfile] [-f # string]",
+" [-h size] [-L mapfile]");
+ exit(1);
+}
+
+
+void
+main(int argc, char **argv)
+{
+ int opt;
+
+ while((opt = getopt(argc, argv, "b:df:h:Fl:L:r:x")) != -1)
+ switch(opt) {
+ case 'b':
+ set_bell_values(optarg);
+ break;
+ case 'd':
+ print_keymap();
+ break;
+ case 'l':
+ load_keymap(optarg, 0);
+ break;
+ case 'L':
+ load_keymap(optarg, 1);
+ break;
+ case 'f':
+ set_functionkey(optarg,
+ nextarg(argc, argv, &optind, 'f'));
+ break;
+ case 'F':
+ load_default_functionkeys();
+ break;
+ case 'h':
+ set_history(optarg);
+ break;
+ case 'r':
+ set_keyrates(optarg);
+ break;
+ case 'x':
+ hex = 1;
+ break;
+ default:
+ usage();
+ }
+ if ((optind != argc) || (argc == 1))
+ usage();
+ exit(0);
+}
+
+
diff --git a/usr.sbin/kbdcontrol/lex.h b/usr.sbin/kbdcontrol/lex.h
new file mode 100644
index 0000000..ad5c7fb
--- /dev/null
+++ b/usr.sbin/kbdcontrol/lex.h
@@ -0,0 +1,57 @@
+/*-
+ * Copyright (c) 1994-1995 Søren Schmidt
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * in this position and unchanged.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software withough specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $Id$
+ */
+
+#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
+
+extern int number;
+extern char letter;
+extern FILE *yyin;
diff --git a/usr.sbin/kbdcontrol/lex.l b/usr.sbin/kbdcontrol/lex.l
new file mode 100644
index 0000000..8b1d3b8
--- /dev/null
+++ b/usr.sbin/kbdcontrol/lex.l
@@ -0,0 +1,117 @@
+/*-
+ * Copyright (c) 1994-1995 Søren Schmidt
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * in this position and unchanged.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software withough specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $Id$
+ */
+
+%{
+
+#include "lex.h"
+
+%}
+
+D [0-9]
+X [0-9a-fA-F]
+A .
+L [OCNB]
+
+%%
+
+nop { return TNOP; }
+lshift { return TLSH; }
+rshift { return TRSH; }
+clock { return TCLK; }
+nlock { return TNLK; }
+slock { return TSLK; }
+lalt|alt { return TLALT; }
+btab { return TBTAB; }
+lctrl|ctrl { return TLCTR; }
+nscr { return TNEXT; }
+rctrl { return TRCTR; }
+ralt { return TRALT; }
+alock { return TALK; }
+ashift { return TASH; }
+meta { return TMETA; }
+boot { return TRBT; }
+debug { return TDBG; }
+susp { return TSUSP; }
+
+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; }
+SP|sp { number = 32; return TNUM; }
+DEL|del { number = 127; return TNUM; }
+
+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 */ }
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..c3af12d
--- /dev/null
+++ b/usr.sbin/kbdmap/Makefile
@@ -0,0 +1,16 @@
+# Makefile for kbdmap / vidfont
+# $Id$
+
+SCRIPT= ${.CURDIR}/kbdmap.pl
+LINKS= ${BINDIR}/kbdmap ${BINDIR}/vidfont
+MAN1= kbdmap.1
+MLINKS= kbdmap.1 vidfont.1
+
+all:
+ @echo -n
+
+beforeinstall:
+ ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \
+ ${SCRIPT} ${DESTDIR}${BINDIR}/kbdmap
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/kbdmap/TODO b/usr.sbin/kbdmap/TODO
new file mode 100644
index 0000000..0735569
--- /dev/null
+++ b/usr.sbin/kbdmap/TODO
@@ -0,0 +1,5 @@
+o kbdmap.5 man page
+o remember some hackers to translate Languages.phrases into
+ Norwegian, Russian, Danish, Swedish
+
+95/04/03 Wolfram
diff --git a/usr.sbin/kbdmap/kbdmap.1 b/usr.sbin/kbdmap/kbdmap.1
new file mode 100644
index 0000000..7985037
--- /dev/null
+++ b/usr.sbin/kbdmap/kbdmap.1
@@ -0,0 +1,131 @@
+.\" Copyright (c) March 1995 Wolfram Schneider <wosch@FreeBSD.org>. Berlin.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $Id: kbdmap.1,v 1.12 1997/08/30 12:22:44 jmg Exp $
+
+.Dd Mar 25, 1995
+.Dt KBDMAP 1
+.Os FreeBSD
+.Sh NAME
+.Nm kbdmap ,
+.Nm vidfont
+.Nd front end for syscons
+.Sh SYNOPSIS
+.Nm kbdmap
+.Op Fl K
+.Op Fl V
+.Op Fl d | Fl default
+.Op Fl h | Fl help
+.Op Fl l | Fl lang Ar language
+.Op Fl p | Fl print
+.Op Fl r | Fl restore
+.Op Fl s | Fl show
+.Op Fl v | Fl verbose
+.Sh DESCRIPTION
+.Nm kbdmap
+allows easy setting of available keymaps.
+The
+.Nm vidfont
+command allows the setting of fonts.
+Both examine a database for the keymaps and fonts.
+Descriptions are in English by default, but may also be
+in other languages.
+.Pp
+It is strongly recommended to not choose
+.Tn MSDOS
+codepage keymaps
+or fonts. Use the
+.Tn ISO
+standard version if available!
+.Tn X11
+does not
+support
+.Tn MSDOS
+codepage.
+.Sh OPTIONS
+.Bl -tag -width Ds
+.It Fl K
+Run as command
+.Xr kbdmap 1 .
+.It Fl V
+Run as command
+.Xr vidfont 1 .
+.It Fl d , Fl default
+Use default language. Ignore $LANG.
+.It Fl h , Fl help
+Print options and exit.
+.It Fl l , Fl lang Ar language
+Use
+.Ar language
+for description and menu
+.It Fl p , Fl print
+Print description of available keymaps or fonts
+to stdout and exit.
+.It Fl r , Fl restore
+Load default font from
+.Pa /etc/rc.conf .
+.It Fl s , Fl show
+Show currently supported languages and exit.
+.It Fl v , Fl verbose
+More warnings.
+.Sh ENVIRONMENT
+.Bl -tag -width /etc/master.passwdxx -compact
+.Ev LANG
+Preferred language.
+.Sh FILES
+.Bl -tag -width /usr/share/syscons/keymaps/INDEX.keymaps -compact
+.It Pa /usr/share/syscons/keymaps/INDEX.keymaps
+Database for keymaps.
+.It Pa /usr/share/syscons/fonts/INDEX.fonts
+Database for fonts.
+.It Pa /etc/rc.conf
+Default font.
+.It Pa /usr/X11/lib/X11/locale/locale.alias
+Describe common LANG values.
+.Sh BUGS
+.\" .Nm kbdmap/vidfont
+.\" does not know which font is in use. E.g. if the current font
+.\" is iso-8859-1 and you chose lang 'ru' (for Russian)
+.\" you get funny latin1 characters and not russkij shrift.
+.\"
+.Nm vidcontrol
+and
+.Nm kbdcontrol
+work only on a (virtual) console and not with X11.
+.Sh SEE ALSO
+.Xr dialog 1 ,
+.Xr kbdcontrol 1 ,
+.Xr vidcontrol 1 ,
+.Xr rc.conf 5
+.Sh HISTORY
+The
+.Nm kbdmap
+and
+.Nm vidfont
+commands appeared in
+.Fx 2.1 .
+.Sh AUTHOR
+.An Wolfram Schneider
+.Aq wosch@FreeBSD.org ,
+Berlin.
diff --git a/usr.sbin/kbdmap/kbdmap.pl b/usr.sbin/kbdmap/kbdmap.pl
new file mode 100644
index 0000000..66180da
--- /dev/null
+++ b/usr.sbin/kbdmap/kbdmap.pl
@@ -0,0 +1,317 @@
+#!/usr/bin/perl
+#
+# Copyright (c) March 1995 Wolfram Schneider <wosch@FreeBSD.org>. Berlin.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# kbdmap/vidfont - front end for syscons
+#
+# $Id: kbdmap.pl,v 1.6 1997/02/22 16:06:02 peter Exp $
+
+
+# simple test if syscons works
+$x11 = system("kbdcontrol -d >/dev/null");
+if ($x11) {
+ warn "You are not on a virtual console - " .
+ "expect certain strange side-effects\n";
+ sleep 2;
+}
+
+sub variables_static {
+ $lang_default = "en"; # set default language
+ $lang = $ENV{'LC_CTYPE'} || $ENV{'LANG'} || $lang_default;
+ $lang = &lang($lang);
+ $program = $0; $program =~ s|.*/||; $program =~ s/\.(pl|perl)$//;
+ $keymapdir = "/usr/share/syscons/keymaps";
+ $fontdir = "/usr/share/syscons/fonts";
+ $sysconfig = "/etc/rc.conf";
+
+ # for test only
+ #$keymapdir = "/tmp/kbdmap/syscons/keymaps";
+ #$fontdir = "/tmp/kbdmap/syscons/fonts";
+
+ # read current font from rc.conf
+ $font_default = "cp437-8x16.fnt";
+ $font_current = &font_current($font_default);
+
+ if ($program eq "kbdmap") {
+ $dir = $keymapdir;
+ } else {
+ $dir = $fontdir;
+ }
+
+ @langsupport = ('MENU', 'FONT'); # lang depended variables
+ $show = 0; # show which languages currently supported
+ $index = "INDEX"; # Keyboard language database
+ $verbose = 0;
+ %keymap = '';
+}
+
+sub lang {
+ local($lang) = @_;
+
+ #$lang =~ s/_.*//; # strip country and font
+ $lang =~ s/^(C)$/en/; # aliases
+ #$lang =~ s/^(..).*/$1/; # use only first to characters
+
+ return $lang;
+}
+
+sub font_current {
+ local($font) = @_;
+ local($font_current);
+
+ open(F, "$sysconfig") || warn "$sysconfig: $!\n";
+
+ while(<F>) {
+ /^#/ && next;
+ if (/^\s*font[0-9]+x[0-9]+\s*=\s*(\S+)/) {
+ $font_current = $1 if $1 ne "NO";
+ }
+ }
+ close F;
+
+ return $font_current if $font_current;
+ return $font;
+}
+
+sub vidcontrol {
+ local($font) = @_;
+
+ return $x11 if $x11; # syscons test failed
+
+ if ($font =~ /.*([0-9]+x[0-9]+)(\.fnt)?$/) {
+ warn "vidcontrol -f $1 $font\n" if $verbose;
+ return system("vidcontrol -f $1 $font");
+ } else {
+ warn "Which font size? ``$font''\n";
+ return 1;
+ }
+}
+
+sub menu_read {
+ local($e,@a,$mark,$ext);
+ local($keym, $lg, $dialect, $desc);
+ local(@langlist) = $lang_default;
+
+ $ext = $dir; $ext =~ s|.*/||;
+ # en_US.ISO8859-1 -> en_..\.ISO8859-1
+ ($dialect = $lang) =~ s/^(..)_..(.+)$/$1_..$2/;
+ # en_US.ISO8859-1 -> en
+ ($lang_abk = $lang) =~ s/^(..)_.*$/$1/;
+
+ # read index database
+ open(I, "$dir/$index.$ext") || warn "$dir/$index.$ext: $!\n";
+ while(<I>) {
+ # skip blank lines and comments
+ /^#/ && next;
+ s/^\s+//;
+ /^\w/ || next;
+ s/\s+$//;
+
+ ($keym, $lg, $desc) = split(/:/);
+ if (! -r "$keym" && ! -r "$dir/$keym" &&
+ !grep(/$keym/, @langsupport)) {
+ warn "$keym not found!\n" if $verbose;
+ next;
+ }
+
+ # set empty language to default language
+ $lg = $lang_default if $lg eq "";
+
+ # save language
+ if ($show) {
+ foreach $e (split(/,/, $lg)) {
+ push(@langlist, $e) if !grep($_ eq $e, @langlist);
+ }
+ }
+
+ # 4) your choise if exist
+ # 3) long match e.g. en_GB.ISO8859-1 is equal to en_..\.ISO8859-1
+ # 2) short match 'de'
+ # 1) default langlist 'en'
+ # 0) any language
+ #
+ # language may be a kommalist
+ # higher match overwrite lower
+ # last entry overwrite previous if exist twice in database
+
+ # found your favorite language :-)
+ if ($lg =~ /^(.+,)?$lang(,.+)?$/) {
+ $keymap{$keym} = $desc;
+ $mark{$keym} = 4;
+ } elsif ($mark{$keym} <= 3 && $lg =~ /^(.+,)?$dialect(,.+)?$/) {
+ # dialect
+ $keymap{$keym} = $desc;
+ $mark{$keym} = 3;
+ } elsif ($mark{$keym} <= 2 && $lg =~ /^(.+,)?$lang_abk(,.+)?$/) {
+ # abrevation
+ $keymap{$keym} = $desc;
+ $mark{$keym} = 2;
+ } elsif ($mark{$keym} <= 1 && $lg =~ /^(.+,)?$lang_default(,.+)?$/) {
+ # default
+ $keymap{$keym} = $desc;
+ $mark{$keym} = 1;
+ } elsif ($mark{$keym} <= 0) {
+ # any
+ $keymap{$keym} = $desc;
+ $mark{$keym} = 0;
+ }
+ }
+ close I;
+
+ if ($show) {
+ @langlist = sort(@langlist);
+ print "Currently supported languages: @langlist\n";
+ exit(0);
+ }
+
+ # remove variables from list
+ local($ee);
+ foreach $e (@langsupport) {
+ ($ee = $e) =~ y/A-Z/a-z/;
+ eval "\$$ee = \"$keymap{$e}\"";
+ #warn "$e \$$ee = \"$keymap{$e}\"";
+ delete $keymap{$e};
+ }
+ #warn "$font $font_default $font_current\n";
+
+
+ # look for keymaps which are not in database
+ opendir(D, "$dir") || warn "$dir: $!\n";
+ foreach $e (readdir(D)) {
+ if ($e =~ /^[a-z].*(kbd|fnt)$/ && !$keymap{$e}) {
+ warn "$e not in database\n" if $verbose;
+ $keymap{$e} = $e;
+ $keymap{$e} =~ s/\.(kbd|fnt)$//;
+ }
+ }
+ closedir D;
+
+ # sort menu, font 8x8 is less than 8x14 and 8x16
+ foreach $e (sort(keys %keymap)) {
+ push(@a, "\"$keymap{$e}\" \"\"");
+ }
+ # side effects to @a
+ grep(s/8x8/8x08/, @a);
+ @a = sort @a;
+ grep(s/8x08/8x8/, @a);
+
+ if ($print) {
+ foreach (@a) {
+ s/"//g; #"
+ print "$_\n";
+ }
+ exit;
+ }
+
+ return @a;
+}
+
+sub dialog {
+ local(@argv) = @_;
+ local($tmp) = "/tmp/_kbd_lang$$";
+
+ $dialog = "/usr/bin/dialog \\
+--clear \\
+--title \"Keyboard Menu\" \\
+--menu \"$menu\" \\
+-1 -1 10";
+
+ ## *always* start right font, don't believe that your current font
+ ## is equal with default font in /etc/rc.conf
+ ## see also at end of this function
+ ## if ($font) {
+
+ # start right font, assume that current font is equal
+ # to default font in /etc/rc.conf
+ #
+ # $font is the font which require the language $lang; e.g.
+ # russian *need* a koi8 font
+ # $font_current is the current font from /etc/rc.conf
+ if ($font && $font ne $font_current) {
+ &vidcontrol($font);
+ }
+
+ # start dialog
+ system("$dialog @argv 2> $tmp");
+
+ if (!$?) {
+ $choise = `cat $tmp`;
+ foreach $e (keys %keymap) {
+ if ($keymap{$e} eq $choise) {
+ if ($program eq "kbdmap") {
+ system("kbdcontrol -l $dir/$e\n") unless $x11;
+ print "keymap=$e", "\n";
+ } else {
+ &vidcontrol("$dir/$e");
+ $_ = $e;
+ if (/^.*\-(.*)\.fnt/) {
+ $font=$1
+ } else { $font="unknown" }
+ print "font$font=$e", "\n";
+ }
+ last;
+ }
+ }
+ # } else {
+ } elsif ($font && $font ne $font_current) {
+ # cancel, restore old font
+ &vidcontrol($font_current);
+ }
+ unlink $tmp;
+ exit($?);
+}
+
+sub usage {
+ warn <<EOF;
+usage: $program\t[-K] [-V] [-d|-default] [-h|-help] [-l|-lang language]
+\t\t[-p|-print] [-r|-restore] [-s|-show] [-v|-verbose]
+EOF
+ exit 1;
+}
+
+# Argumente lesen
+sub parse {
+ local(@argv) = @_;
+
+ while($_ = $argv[0], /^-/) {
+ shift @argv;
+ last if /^--$/;
+ if (/^--?(h|help|\?)$/) { &usage; }
+ elsif (/^-(v|verbose)$/) { $verbose = 1; }
+ elsif (/^-(l|lang)$/) { $lang = &lang($argv[0]); shift @argv; }
+ elsif (/^-(d|default)$/) { $lang = $lang_default }
+ elsif (/^-(s|show)$/) { $show = 1 }
+ elsif (/^-(p|print)$/) { $print = 1 }
+ elsif (/^-(r|restore)$/) { &vidcontrol($font_current); exit(0) }
+ elsif (/^-K$/) { $dir = $keymapdir; }
+ elsif (/^-V$/) { $dir = $fontdir; }
+ else { &usage }
+ }
+}
+
+# main
+&variables_static; # read variables
+&parse(@ARGV); # parse arguments
+&dialog(&menu_read); # start dialog and kbdcontrol/vidcontrol
diff --git a/usr.sbin/kernbb/Makefile b/usr.sbin/kernbb/Makefile
new file mode 100644
index 0000000..92a416f
--- /dev/null
+++ b/usr.sbin/kernbb/Makefile
@@ -0,0 +1,9 @@
+# $Id$
+
+PROG= kernbb
+MAN8= kernbb.8
+DPADD= ${LIBKVM}
+LDADD= -lkvm
+
+.include <bsd.prog.mk>
+
diff --git a/usr.sbin/kernbb/kernbb.8 b/usr.sbin/kernbb/kernbb.8
new file mode 100644
index 0000000..b62c0a4
--- /dev/null
+++ b/usr.sbin/kernbb/kernbb.8
@@ -0,0 +1,70 @@
+.\" Copyright (c) 1983, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $Id: kernbb.8,v 1.6 1997/09/22 06:30:04 charnier Exp $
+.\"
+.Dd May 22, 1995
+.Dt KERNBB 8
+.Os FreeBSD 2.1
+.Sh NAME
+.Nm kernbb
+.Nd generate a dump of the kernels basic-block profile buffers
+.Sh SYNOPSIS
+.Nm kernbb
+.Sh DESCRIPTION
+.Nm Kernbb
+is a tool used to dump the basic-block profiling buffers of the running
+kernel.
+.Pp
+At least one source file in the running kernel must have been compiled
+with the
+.Fl a
+option.
+.Pp
+The output format is ascii, consisting of one line per record with the
+following fields: filename, linenumber, procedure, address, count
+of executions, length of the basic-block in bytes and the product of
+the previous two fields.
+.Sh FILES
+.Bl -tag -width /dev/kmemx -compact
+.It Pa /kernel
+the default system
+.It Pa /dev/kmem
+the default memory
+.El
+.Sh SEE ALSO
+.Xr cc 1
+.Sh AUTHORS
+The
+.Nm
+command was written by
+.An Poul-Henning Kamp ,
+along with the kernel-support.
diff --git a/usr.sbin/kernbb/kernbb.c b/usr.sbin/kernbb/kernbb.c
new file mode 100644
index 0000000..a67d5ba
--- /dev/null
+++ b/usr.sbin/kernbb/kernbb.c
@@ -0,0 +1,139 @@
+/*
+ * ----------------------------------------------------------------------------
+ * "THE BEER-WARE LICENSE" (Revision 42):
+ * <phk@FreeBSD.org> wrote this file. As long as you retain this notice you
+ * can do whatever you want with this stuff. If we meet some day, and you think
+ * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp
+ * ----------------------------------------------------------------------------
+ *
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id$";
+#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[] = {
+ { "bbset" },
+ { NULL }
+};
+
+u_long lineno[MAXBB];
+u_long counts[MAXBB];
+u_long addr[MAXBB];
+u_long func[MAXBB];
+u_long file[MAXBB];
+char *fn[MAXBB];
+char *pn[MAXBB];
+
+kvm_t *kv;
+
+int
+main()
+{
+ int i,j;
+ u_long l1,l2,l3,l4;
+ struct bb bb;
+ char buf[128];
+
+ kv = kvm_open(NULL,NULL,NULL,O_RDWR,"dnc");
+ if (!kv)
+ err(1,"kvm_open");
+ i = kvm_nlist(kv,namelist);
+ if (i)
+ err(1,"kvm_nlist");
+
+ l1 = namelist[0].n_value;
+ kvm_read(kv,l1,&l2,sizeof l2);
+ while(l2--) {
+ l1 += sizeof l1;
+ kvm_read(kv,l1,&l3,sizeof l3);
+ kvm_read(kv,l3,&bb,sizeof bb);
+ if (!bb.ncounts)
+ continue;
+ if (bb.ncounts > MAXBB)
+ errx(1, "found %lu counts above limit of %u",
+ bb.ncounts, MAXBB);
+ kvm_read(kv,bb.lineno,lineno, bb.ncounts * sizeof lineno[0]);
+ kvm_read(kv,bb.counts,counts, bb.ncounts * sizeof counts[0]);
+ kvm_read(kv,bb.addr, addr, bb.ncounts * sizeof addr[0]);
+ kvm_read(kv,bb.file, file, bb.ncounts * sizeof file[0]);
+ kvm_read(kv,bb.func, func, bb.ncounts * sizeof func[0]);
+ l4 = 0;
+ for (i=0; i < bb.ncounts; i++) {
+ if (counts[i])
+ l4++;
+ if (!func[i] && i+1 < bb.ncounts)
+ func[i] = func[i+1];
+ }
+ if (!l4)
+ continue;
+ for (i=0; i < bb.ncounts; i++) {
+
+ if (0 && !counts[i])
+ continue;
+
+ if (!pn[i] && func[i]) {
+ kvm_read(kv,func[i], buf, sizeof buf);
+ buf[sizeof buf -1] = 0;
+ pn[i] = strdup(buf);
+ for(j=i+1;j<bb.ncounts;j++)
+ if (func[j] == func[i]) {
+ pn[j] = pn[i];
+ func[j] = 0;
+ }
+ }
+ if (!pn[i])
+ pn[i] = "-";
+ if (!fn[i] && file[i]) {
+ kvm_read(kv,file[i], buf, sizeof buf);
+ buf[sizeof buf -1] = 0;
+ fn[i] = strdup(buf);
+ for(j=i+1;j<bb.ncounts;j++)
+ if (file[j] == file[i]) {
+ fn[j] = fn[i];
+ file[j] = 0;
+ }
+ }
+ if (!fn[i])
+ fn[i] = "-";
+ l4 = 0;
+ if (i+1 < bb.ncounts)
+ l4 = addr[i+1] - addr[i];
+ printf("%s %5lu %s %lu %lu %lu %lu\n",
+ fn[i], lineno[i], pn[i], addr[i], counts[i], l4, counts[i] * l4);
+ }
+ for(i=0;i<bb.ncounts;i++) {
+ if (func[i] && pn[i])
+ free(pn[i]);
+ if (file[i] && fn[i])
+ free(fn[i]);
+ pn[i] = 0;
+ fn[i] = 0;
+ }
+ }
+ return 0;
+}
diff --git a/usr.sbin/keyadmin/Makefile b/usr.sbin/keyadmin/Makefile
new file mode 100644
index 0000000..d0a422e
--- /dev/null
+++ b/usr.sbin/keyadmin/Makefile
@@ -0,0 +1,6 @@
+# $ANA: Makefile,v 1.2 1996/06/13 20:11:05 wollman Exp $
+
+PROG= keyadmin
+MAN8= keyadmin.8
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/keyadmin/keyadmin.8 b/usr.sbin/keyadmin/keyadmin.8
new file mode 100644
index 0000000..eedb8cb
--- /dev/null
+++ b/usr.sbin/keyadmin/keyadmin.8
@@ -0,0 +1,239 @@
+.\"# @(#)COPYRIGHT 1.1a (NRL) 17 August 1995
+.\"
+.\"COPYRIGHT NOTICE
+.\"
+.\"All of the documentation and software included in this software
+.\"distribution from the US Naval Research Laboratory (NRL) are
+.\"copyrighted by their respective developers.
+.\"
+.\"This software and documentation were developed at NRL by various
+.\"people. Those developers have each copyrighted the portions that they
+.\"developed at NRL and have assigned All Rights for those portions to
+.\"NRL. Outside the USA, NRL also has copyright on the software
+.\"developed at NRL. The affected files all contain specific copyright
+.\"notices and those notices must be retained in any derived work.
+.\"
+.\"NRL LICENSE
+.\"
+.\"NRL grants permission for redistribution and use in source and binary
+.\"forms, with or without modification, of the software and documentation
+.\"created at NRL provided that the following conditions are met:
+.\"
+.\"1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\"2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\"
+.\" This product includes software developed at the Information
+.\" Technology Division, US Naval Research Laboratory.
+.\"
+.\"4. Neither the name of the NRL nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\"THE SOFTWARE PROVIDED BY NRL IS PROVIDED BY NRL AND CONTRIBUTORS ``AS
+.\"IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+.\"TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+.\"PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL NRL OR
+.\"CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+.\"EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+.\"PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+.\"PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+.\"LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+.\"NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+.\"SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+.\"
+.\"The views and conclusions contained in the software and documentation
+.\"are those of the authors and should not be interpreted as representing
+.\"official policies, either expressed or implied, of the US Naval
+.\"Research Laboratory (NRL).
+.\"
+.\"----------------------------------------------------------------------*/
+.\"
+.\" $ANA: keyadmin.8,v 1.3 1996/06/13 20:15:57 wollman Exp $
+.\"
+.Dd June 13, 1996
+.Dt KEY 8
+.Os
+.Sh NAME
+.Nm keyadmin
+.Nd manually manipulate the kernel key management database
+.Sh SYNOPSIS
+.Nm keyadmin
+.Op Ar command Op Ar args
+.Sh DESCRIPTION
+The
+.Nm
+command is used to manually enter security associations into the kernel
+key/security association database. (See
+.Xr key 4 ).
+.Pp
+Almost any operation offered in the
+.Xr key 4
+API is available to privileged users running
+.Nm keyadmin .
+Until there is an implementation of an automated key management protocol,
+which will manipulate the key database in a manner similar to how
+.Xr routed 8
+or
+.Xr gated 8
+manipulates the routing tables,
+.Nm
+is the only way of establishing security associations.
+.Pp
+If
+.Nm
+is invoked without any arguments, it will enter an interactive mode, where
+the user can type in
+.Dq Ar command Op Ar args
+interactively, or use
+.Nm
+to enter a single
+.Dq Ar command Op Ar args .
+.Ar Command
+can be one of the following:
+.Bl -inset
+.It Nm del Ar type spi source destination
+.Pp
+Delete a security association between
+.Ar source
+and
+.Ar destination
+of the given
+.Ar type
+and
+.Ar spi .
+Example:
+.Bd -literal
+ delete esp 90125 anderson.yes.org rabin.yes.org
+.Ed
+.It Nm get Ar type spi source destination
+.Pp
+Retrieve (and print) a security association between
+.Ar source
+and
+.Ar destination
+of the given
+.Ar type
+and
+.Ar spi .
+Example:
+.Bd -literal
+ get ah 5150 eddie.vanhalen.com alex.vanhalen.com
+.Ed
+.It Nm dump
+.Pp
+Display the entire security association table. WARNING: This prints a lot
+of data.
+.It Nm load Ar filename
+.Pp
+Load security association information from a file formatted as documented in
+.Xr keys 5 . If
+.Dq -
+is specified for the
+.Ar filename ,
+load keys from the standard input.
+.It Nm save Ar filename
+.Pp
+Save security association information to a file formatted as documented in
+.Xr keys 5 . If
+.Dq -
+is specified for the
+.Ar filename ,
+place the key file out on the standard output. (This can be used as a sort
+of lightweight
+.Nm dump
+command.)
+NOTE: The save command must create a new file; it will not write into an
+existing file. This is to prevent writing into a world-readable file, or a
+named pipe or UNIX socket (see
+.Xr socket 2
+and
+.Xr mkfifo 1 ).
+.It Nm help Op command
+.Pp
+Offer brief help without an argument, or slightly more specific help on a
+particular command.
+.It Nm flush
+.Pp
+Erase all entries in the kernel security association table.
+.El
+
+.Pp
+The following values for
+.Ar command
+are only available by using
+.Nm
+in its interactive mode of operation:
+.Bl -inset
+.It Nm add Ar type spi source destination transform key
+.Op Ar iv
+.Pp
+Add a security association of a particular
+.Ar type
+and
+.Ar spi
+from a
+.Ar source
+to a
+.Ar destination ,
+using a particular
+.Ar transform
+and
+.Ar key .
+If a transform requires an initialization vector, the
+.Ar iv
+argument contains it. This command is available only in interactive mode
+because
+.Nm
+makes no attempt to destroy its argument vector after use. A malicous user
+of the
+.Xr ps 1
+command could determine security keys if
+.Nm add
+were allowed to be used straight from the command line. Example:
+.Bd -literal
+ add esp 2112 temples.syrinx.org priests.syrinx.org des-cbc \\
+ a652a476a652a476 87ac9876deac9876
+.Ed
+.It Nm exit
+.It Nm quit
+.Pp
+Exit interaction with
+.Nm keyadmin .
+An EOF will also end interaction with
+.Nm keyadmin .
+.El
+.Sh SEE ALSO
+.Xr ipsec 4 ,
+.Xr key 4 ,
+.Xr route 4 ,
+.Xr gated 8 ,
+.Xr routed 8
+
+.Sh HISTORY
+The
+.Nm
+command first appeared in NRL's
+.Bx 4.4
+IPv6 networking distribution.
+.Nm Keyadmin
+started its life as a pipe dream thought up by Dan McDonald, and came to
+life through the excruciating efforts of Ran Atkinson, Dan McDonald,
+Craig Metz, and Bao Phan.
+The NRL version of the program was originally called
+.Nm key ,
+but was renamed to
+.Nm keyadmin
+because of the conflict with
+.Xr key 1 .
+.Sh BUGS
+.Nm Keyadmin
+needs a -n flag like
+.Xr route 8
+to avoid name lookups.
+.Pp
+The dump and save commands currently display the first 30 or so entries.
diff --git a/usr.sbin/keyadmin/keyadmin.c b/usr.sbin/keyadmin/keyadmin.c
new file mode 100644
index 0000000..1bbd3b8
--- /dev/null
+++ b/usr.sbin/keyadmin/keyadmin.c
@@ -0,0 +1,1251 @@
+/*----------------------------------------------------------------------
+ key.c: Main routines for the key(8) tool for manually managing
+ cryptographic keys and security associations inside the
+ Key Engine of the operating system.
+
+ Future Enhancements should support:
+ multiple sensitivity levels
+ OSPFv2 keys
+ RIPv2 keys
+ Triple DES for ESP
+ DES+MD5 for ESP
+
+ Copyright 1995 by Bao Phan, Randall Atkinson, & Dan McDonald,
+ All Rights Reserved. All Rights have been assigned to the
+ US Naval Research Laboratory. The NRL Copyright Notice and
+ License govern distribution and use of this software.
+----------------------------------------------------------------------*/
+/*----------------------------------------------------------------------
+# @(#)COPYRIGHT 1.1a (NRL) 17 August 1995
+
+COPYRIGHT NOTICE
+
+All of the documentation and software included in this software
+distribution from the US Naval Research Laboratory (NRL) are
+copyrighted by their respective developers.
+
+This software and documentation were developed at NRL by various
+people. Those developers have each copyrighted the portions that they
+developed at NRL and have assigned All Rights for those portions to
+NRL. Outside the USA, NRL also has copyright on the software
+developed at NRL. The affected files all contain specific copyright
+notices and those notices must be retained in any derived work.
+
+NRL LICENSE
+
+NRL grants permission for redistribution and use in source and binary
+forms, with or without modification, of the software and documentation
+created at NRL provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+3. All advertising materials mentioning features or use of this software
+ must display the following acknowledgement:
+
+ This product includes software developed at the Information
+ Technology Division, US Naval Research Laboratory.
+
+4. Neither the name of the NRL nor the names of its contributors
+ may be used to endorse or promote products derived from this software
+ without specific prior written permission.
+
+THE SOFTWARE PROVIDED BY NRL IS PROVIDED BY NRL AND CONTRIBUTORS ``AS
+IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL NRL OR
+CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+The views and conclusions contained in the software and documentation
+are those of the authors and should not be interpreted as representing
+official policies, either expressed or implied, of the US Naval
+Research Laboratory (NRL).
+
+----------------------------------------------------------------------*/
+
+/*
+ * $ANA: keyadmin.c,v 1.2 1996/06/13 19:42:40 wollman Exp $
+ */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <netinet/in.h>
+
+#ifdef INET6
+#include <netinet6/in6.h>
+#else /* INET6 */
+#if 0
+#include <netinet6/in6_types.h>
+#endif
+#endif /* INET6 */
+
+#ifdef IPSEC
+#include <netsec/ipsec.h>
+#endif
+#include <netkey/key.h>
+
+#ifdef INET6
+#include <netinet6/support.h>
+#endif
+
+#ifndef INET6 /* XXX */
+#define hostname2addr(a, b, c) gethostbyname(a)
+#define addr2hostname(a, b, c, d) gethostbyaddr((a), (b), (c))
+#endif
+
+#include <arpa/inet.h>
+
+int parse7 __P((int, char **));
+int parse4 __P((int, char **));
+int docmd __P((int, char **));
+
+#define KEYCMD_ARG_MAX 10
+
+#define KEYCMD_LOAD 1
+#define KEYCMD_SAVE 2
+#define KEYCMD_ADD 3
+#define KEYCMD_DEL 4
+#define KEYCMD_FLUSH 5
+#define KEYCMD_GET 6
+#define KEYCMD_DUMP 7
+#define KEYCMD_HELP 8
+#define KEYCMD_EXIT 9
+#define KEYCMD_SHELL 10
+
+struct nametonum {
+ char *name;
+ int num;
+ int flags;
+};
+
+char parse7usage[] = "<type> <spi> <src> <dst> <transform> <key> [iv]";
+char parse4usage[] = "<type> <spi> <src> <dst>";
+
+struct keycmd {
+ char *name;
+ int num;
+ int (*parse) __P((int, char **));
+ char *usage;
+ char *help;
+} keycmds[] = {
+ { "add", KEYCMD_ADD, parse7, parse7usage,
+ "Adds a specific key entry to the kernel key table." },
+ { "del", KEYCMD_DEL, parse4, parse4usage,
+ "Removes a specific key entry from the kernel key table." },
+ { "get", KEYCMD_GET, parse4, parse4usage,
+ "Retrieves a key entry from the kernel key table." },
+ { "dump", KEYCMD_DUMP, NULL, " ",
+ "Retrieves all key entries from the kernel key table." },
+ { "load", KEYCMD_LOAD, NULL, "{ <filename> | - }",
+ "Loads keys from a file or stdin into the kernel key table." },
+ { "save", KEYCMD_SAVE, NULL, "{ <filename> | - }",
+ "Saves keys from the kernel key table to a file or stdout." },
+ { "help", KEYCMD_HELP, NULL, "[command]",
+ "Provides limited on-line help. Read the man page for more." },
+ { "flush", KEYCMD_FLUSH, NULL, NULL,
+ "Clears the kernel key table." },
+ { "!", KEYCMD_SHELL, NULL, "[command]",
+ "Executes a subshell." },
+ { "exit", KEYCMD_EXIT, NULL, NULL,
+ "Exits the program." },
+ { "quit", KEYCMD_EXIT, NULL, NULL,
+ "Exits the program." },
+ { NULL, 0, NULL, NULL, NULL }
+};
+
+/* flags: index into algorithmtabs */
+
+struct nametonum keytypes[] = {
+#ifdef IPSEC
+ { "ah", KEY_TYPE_AH, 0 },
+ { "esp", KEY_TYPE_ESP, 1 },
+#endif
+ { "rsvp", KEY_TYPE_RSVP, 2 },
+ { "ospf", KEY_TYPE_OSPF, 3 },
+ { "rip", KEY_TYPE_RIPV2, 4 },
+ { NULL, 0, 0 }
+};
+
+#ifndef IPSEC_ALGTYPE_AUTH_MD5 /* XXX */
+#define IPSEC_ALGTYPE_AUTH_MD5 1
+#endif
+
+/* flags: true = iv req. */
+struct nametonum authalgorithms[] = {
+ { "md5", IPSEC_ALGTYPE_AUTH_MD5, 0 },
+#ifdef DEBUG
+ /* These provide no security but are useful for debugging the
+ kernel's ESP and Key Engine and PF_KEY routines */
+ { "dummy", IPSEC_ALGTYPE_AUTH_DUMMY, 0 },
+ { "cksum", IPSEC_ALGTYPE_AUTH_CKSUM, 0 },
+#endif
+ { NULL, 0, 0 }
+};
+
+#ifndef IPSEC_ALGTYPE_ESP_DES_CBC /* XXX */
+#define IPSEC_ALGTYPE_ESP_DES_CBC 1
+#endif
+
+/* flags: true = iv req. */
+struct nametonum encralgorithms[] = {
+ { "des-cbc", IPSEC_ALGTYPE_ESP_DES_CBC, 1 },
+#ifdef DEBUG
+ /* These provide no security but are useful for debugging the
+ kernel's ESP and Key Engine and PF_KEY routines */
+ { "dummy", IPSEC_ALGTYPE_ESP_DUMMY, 0 },
+#endif
+ { NULL, 0, 0 }
+};
+
+/*
+ * These numbers should be defined in a header file somewhere
+ * and shared with the consuming programs, once someone has
+ * actually written the support in those programs (rspvd,
+ * gated, and routed). Probably <protocols/*>...?
+ */
+#define RSVP_AUTHTYPE_MD5 1 /* XXX */
+struct nametonum rsvpalgorithms[] = {
+ { "md5", RSVP_AUTHTYPE_MD5, 0 },
+ { NULL, 0, 0 }
+};
+
+#define OSPF_AUTHTYPE_MD5 1 /* XXX */
+struct nametonum ospfalgorithms[] = {
+ { "md5", OSPF_AUTHTYPE_MD5, 0 },
+ { NULL, 0, 0 }
+};
+
+#define RIPV2_AUTHTYPE_MD5 1 /* XXX */
+struct nametonum ripalgorithms[] = {
+ { "md5", RIPV2_AUTHTYPE_MD5, 0 },
+ { NULL, 0, 0 }
+};
+
+/* NB: It is the ordering here that determines the values for the
+ flags specified above that are used to index into algorithmtabs[] */
+struct nametonum *algorithmtabs[] = {
+ authalgorithms,
+ encralgorithms,
+ rsvpalgorithms,
+ ospfalgorithms,
+ ripalgorithms,
+ NULL
+};
+
+char buffer[1024] = "\0";
+
+#define MAXRCVBUFSIZE 8 * 1024
+
+char key_message[sizeof(struct key_msghdr) + MAX_SOCKADDR_SZ * 3
+ + MAX_KEY_SZ + MAX_IV_SZ];
+int key_messageptr;
+
+int keysock = -1;
+
+int keygetseqno = 1;
+int keydumpseqno = 1;
+pid_t mypid;
+
+
+/*----------------------------------------------------------------------
+ help: Print appropriate help message on stdout.
+
+----------------------------------------------------------------------*/
+int help(cmdname)
+ char *cmdname;
+{
+ int i;
+
+ if (cmdname) {
+ for (i = 0; keycmds[i].name; i++)
+ if (!strcasecmp(keycmds[i].name, cmdname))
+ break;
+
+ if (!keycmds[i].name) {
+ warnx("unknown command: %s", cmdname);
+ return 0;
+ }
+
+ printf("%s%s%s\n", keycmds[i].name, keycmds[i].usage ? " " : "",
+ keycmds[i].usage ? keycmds[i].usage : "");
+
+ if (keycmds[i].help)
+ puts(keycmds[i].help);
+ } else {
+ for (i = 0; keycmds[i].name; i++)
+ printf("\t%s%s%s\n", keycmds[i].name, keycmds[i].usage ? " " : "",
+ keycmds[i].usage ? keycmds[i].usage : "");
+ }
+
+ return 0;
+}
+
+/*----------------------------------------------------------------------
+ usage: print suitable usage message on stdout.
+
+----------------------------------------------------------------------*/
+static void
+usage()
+{
+
+ fprintf(stderr, "usage: keyadmin <command> <args>\n");
+}
+
+/*----------------------------------------------------------------------
+ parsekey: parse argument into a binary key and also record
+ the length of the resulting key.
+----------------------------------------------------------------------*/
+int parsekey(key, keylen, arg)
+ u_int8_t *key;
+ u_int8_t *keylen;
+ char *arg;
+{
+ int i, j, k, l;
+ u_int8_t thisbyte;
+
+ i = strlen(arg);
+
+ if ((i == 1) && (arg[0] == '0')) {
+ *keylen = 0;
+ return 0;
+ }
+
+ if ((i % 2)) {
+ printf("Invalid number \"%s\"\n", arg);
+ return -1;
+ }
+
+ j = 1;
+ k = l = thisbyte = 0;
+
+ while(arg[k]) {
+ if ((arg[k] >= '0') && (arg[k] <= '9'))
+ thisbyte |= arg[k] - '0';
+ else
+ if ((arg[k] >= 'a') && (arg[k] <= 'f'))
+ thisbyte |= arg[k] - 'a' + 10;
+ else
+ if ((arg[k] >= 'A') && (arg[k] <= 'F'))
+ thisbyte |= arg[k] - 'A' + 10;
+ else {
+ printf("Invalid hex number \"%s\"\n", arg);
+ return 1;
+ }
+
+ if (!(j % 2))
+ key[l++] = thisbyte;
+
+ thisbyte = (thisbyte << 4);
+ j++;
+ k++;
+ }
+
+ *keylen = l;
+
+ return 0;
+}
+
+/*----------------------------------------------------------------------
+ parsenametonum: converts command-line name into index number.
+
+----------------------------------------------------------------------*/
+int parsenametonum(tab, arg)
+ struct nametonum *tab;
+ char *arg;
+{
+ int i;
+
+ for (i = 0; tab[i].name; i++)
+ if (!strcasecmp(tab[i].name, arg))
+ return i;
+
+ if (!tab[i].name)
+ return -1;
+}
+
+/*----------------------------------------------------------------------
+ parsesockaddr: Convert hostname arg into an appropriate sockaddr.
+
+----------------------------------------------------------------------*/
+int parsesockaddr(sockaddr, arg)
+ struct sockaddr *sockaddr;
+ char *arg;
+{
+ struct hostent *hostent;
+ struct in_addr in_addr, *in_addrp;
+#ifdef INET6
+ struct in_addr6 in_addr6, *in_addr6p;
+#endif /* INET6 */
+
+ if ((hostent = hostname2addr(arg, AF_INET, 0)))
+ if ((hostent->h_addrtype == AF_INET) &&
+ (hostent->h_length == sizeof(struct in_addr))) {
+ in_addrp = ((struct in_addr *)hostent->h_addr_list[0]);
+ goto fillin4;
+ }
+
+ if (ascii2addr(AF_INET, arg, (char *)&in_addr) ==
+ sizeof(struct in_addr)) {
+ in_addrp = &in_addr;
+ goto fillin4;
+ }
+
+#ifdef INET6
+ if (hostent = hostname2addr(arg, AF_INET6))
+ if ((hostent->h_addrtype == AF_INET6) &&
+ (hostent->h_length == sizeof(struct in_addr6))) {
+ in_addr6p = ((struct in_addr6 *)hostent->h_addr_list[0]);
+ goto fillin6;
+ }
+
+ if (ascii2addr(AF_INET6, arg, (char *)&in_addr6)
+ == sizeof(struct in_addr6)) {
+ in_addr6p = &in_addr6;
+ goto fillin6;
+ }
+#endif /* INET6 */
+
+ warnx("unknown host \"%s\"", arg);
+ return 1;
+
+ fillin4:
+ bzero(sockaddr, sizeof(struct sockaddr_in));
+ sockaddr->sa_len = sizeof(struct sockaddr_in);
+ sockaddr->sa_family = AF_INET;
+ ((struct sockaddr_in *)sockaddr)->sin_addr = *in_addrp;
+ return 0;
+
+#ifdef INET6
+ fillin6:
+ bzero(sockaddr, sizeof(struct sockaddr_in6));
+ sockaddr->sa_len = sizeof(struct sockaddr_in6);
+ sockaddr->sa_family = AF_INET6;
+ ((struct sockaddr_in6 *)sockaddr)->sin6_addr = *in_addr6p;
+ return 0;
+#endif /* INET6 */
+}
+
+/*----------------------------------------------------------------------
+ dummyfromaddr: Creates a zeroed sockaddr of family af.
+
+----------------------------------------------------------------------*/
+void dummyfromaddr(sa, af)
+ struct sockaddr *sa;
+ int af;
+{
+ int size;
+#ifdef INET6
+ size = (af == AF_INET6) ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in));
+#else /* INET6 */
+ size = sizeof(struct sockaddr_in);
+#endif /* INET6 */
+ bzero((char *)sa, size);
+ sa->sa_family = af;
+ sa->sa_len = size;
+}
+
+/*
+ * Macros to ensure word alignment.
+ */
+#define ROUNDUP(a) \
+ ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
+#define ADVANCE(x, n) \
+ { x += ROUNDUP(n); }
+
+
+/*----------------------------------------------------------------------
+ parse4: parse keytype, spi, src addr, and dest addr from argv (command line)
+ and stick in structure pointed to by key_messageptr.
+----------------------------------------------------------------------*/
+int parse4(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int i;
+
+ if (argc < 4)
+ return 1;
+
+ if ((i = parsenametonum(keytypes, argv[0])) < 0)
+ return i;
+
+ ((struct key_msghdr *)key_message)->type = keytypes[i].num;
+
+ /* Should we zero check? */
+ ((struct key_msghdr *)key_message)->spi = atoi(argv[1]);
+
+ if (parsesockaddr(key_message + key_messageptr, argv[2]) != 0)
+ return 1;
+ ADVANCE(key_messageptr, ((struct sockaddr *)(key_message +
+ key_messageptr))->sa_len);
+
+ if (parsesockaddr(key_message + key_messageptr, argv[3]) != 0)
+ return 1;
+ ADVANCE(key_messageptr, ((struct sockaddr *)(key_message +
+ key_messageptr))->sa_len);
+
+ /*
+ * We need to put a dummy from address since the kernel expects
+ * this to be in the message.
+ */
+#ifdef INET6
+ dummyfromaddr(key_message + key_messageptr, AF_INET6);
+#else /* INET6 */
+ dummyfromaddr(key_message + key_messageptr, AF_INET);
+#endif /* INET6 */
+ ADVANCE(key_messageptr, ((struct sockaddr *)(key_message +
+ key_messageptr))->sa_len);
+
+ return 0;
+}
+
+/*----------------------------------------------------------------------
+ parse7: parse keytype, spi, src addr, dest addr, alg type, key, and iv
+ from argv (command line)
+ and stick in structure pointed to by key_messageptr.
+----------------------------------------------------------------------*/
+int parse7(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int i, j;
+
+ if (argc < 6)
+ return 1;
+
+ if ((i = parsenametonum(keytypes, argv[0])) < 0)
+ return i;
+
+ ((struct key_msghdr *)key_message)->type = keytypes[i].num;
+
+/* Should we zero check? */
+ ((struct key_msghdr *)key_message)->spi = atoi(argv[1]);
+
+ if (parsesockaddr(key_message + key_messageptr, argv[2]) != 0)
+ return 1;
+ ADVANCE(key_messageptr, ((struct sockaddr *)(key_message +
+ key_messageptr))->sa_len);
+
+ if (parsesockaddr(key_message + key_messageptr, argv[3]) != 0)
+ return 1;
+ ADVANCE(key_messageptr, ((struct sockaddr *)(key_message +
+ key_messageptr))->sa_len);
+
+ /*
+ * We need to put a dummy from address since the kernel expects
+ * this to be in the message.
+ */
+#ifdef INET6
+ dummyfromaddr(key_message + key_messageptr, AF_INET6);
+#else /* INET6 */
+ dummyfromaddr(key_message + key_messageptr, AF_INET);
+#endif /* INET6 */
+ ADVANCE(key_messageptr, ((struct sockaddr *)(key_message +
+ key_messageptr))->sa_len);
+
+ if ((j = parsenametonum(algorithmtabs[keytypes[i].flags], argv[4])) < 0)
+ return j;
+
+ ((struct key_msghdr *)key_message)->algorithm =
+ algorithmtabs[keytypes[i].flags][j].num;
+
+ if ((argc < 7) && algorithmtabs[keytypes[i].flags][j].flags)
+ return 1;
+
+ if (parsekey(key_message + key_messageptr,
+ &(((struct key_msghdr *)key_message)->keylen), argv[5]) != 0)
+ return 1;
+ ADVANCE(key_messageptr, ((struct key_msghdr *)key_message)->keylen);
+
+ if (argc >= 7) {
+ if (parsekey(key_message + key_messageptr,
+ &(((struct key_msghdr *)key_message)->ivlen), argv[6]) != 0)
+ return 1;
+ ADVANCE(key_messageptr, ((struct key_msghdr *)key_message)->ivlen);
+ }
+
+ return 0;
+}
+
+/*----------------------------------------------------------------------
+ parsecmdline:
+
+----------------------------------------------------------------------*/
+int parsecmdline(buffer, argv, argc)
+ char *buffer;
+ char **argv;
+ int *argc;
+{
+ int i = 0, iargc = 0;
+ char *head;
+
+ head = buffer;
+
+ while(buffer[i] && (iargc < KEYCMD_ARG_MAX)) {
+ if ((buffer[i] == '\n') || (buffer[i] == '#')) {
+ buffer[i] = 0;
+ if (*head)
+ argv[iargc++] = head;
+ break;
+ }
+ if ((buffer[i] == ' ') || (buffer[i] == '\t')) {
+ buffer[i] = 0;
+ if (*head)
+ argv[iargc++] = head;
+ head = &(buffer[++i]);
+ } else
+ i++;
+ };
+ argv[iargc] = NULL;
+ *argc = iargc;
+
+ return iargc ? 0 : 1;
+}
+
+
+/*----------------------------------------------------------------------
+ load: load keys from file filename into Key Engine.
+
+----------------------------------------------------------------------*/
+int load(filename)
+ char *filename;
+{
+ FILE *fh;
+ char buffer[1024], *buf, *largv[KEYCMD_ARG_MAX], *c;
+ int i, largc, left, line = 0;
+
+ if (strcmp(filename, "-")) {
+ if (!(fh = fopen(filename, "r")))
+ return -1;
+ } else
+ fh = stdin;
+
+ largv[0] = "add";
+
+ buf = buffer;
+ left = sizeof(buffer);
+
+ while(fgets(buf, left, fh)) {
+ line++;
+ if ((c = strchr(buffer, '\\'))) {
+ left = (sizeof(buffer) - 1) - (--c - buffer);
+ buf = c;
+ } else {
+ buffer[sizeof(buffer)-1] = 0;
+
+ if ((i = parsecmdline(buffer, &(largv[1]), &largc)) < 0)
+ return i;
+
+ if (!i) {
+ if ((i = docmd(++largc, largv))) {
+ if (i > 0) {
+ warnx("parse error on line %d of %s", line, filename);
+ return 0;
+ }
+ return i;
+ }
+ }
+
+ buf = buffer;
+ left = sizeof(buffer);
+ }
+ };
+
+ return 0;
+}
+
+/*----------------------------------------------------------------------
+ parsedata:
+
+----------------------------------------------------------------------*/
+int
+parsedata(km, kip)
+ struct key_msghdr *km;
+ struct key_msgdata *kip;
+{
+ char *cp, *cpmax;
+
+ if (!km)
+ return (-1);
+ if (!(km->key_msglen))
+ return (-1);
+
+ cp = (caddr_t)(km + 1);
+ cpmax = (caddr_t)km + km->key_msglen;
+
+#define NEXTDATA(x, n) \
+ { x += ROUNDUP(n); if (cp >= cpmax) { \
+ warnx("kernel returned a truncated message!"); return(-1); } }
+
+ /* Grab src addr */
+ kip->src = (struct sockaddr *)cp;
+ if (!kip->src->sa_len)
+ return(-1);
+
+ NEXTDATA(cp, kip->src->sa_len);
+
+ /* Grab dest addr */
+ kip->dst = (struct sockaddr *)cp;
+ if (!kip->dst->sa_len)
+ return(-1);
+
+ NEXTDATA(cp, kip->dst->sa_len);
+
+ /* Grab from addr */
+ kip->from = (struct sockaddr *)cp;
+ if (!kip->from->sa_len)
+ return(-1);
+
+ NEXTDATA(cp, kip->from->sa_len);
+
+ /* Grab key */
+ if (kip->keylen = km->keylen) {
+ kip->key = cp;
+ NEXTDATA(cp, km->keylen);
+ }
+
+ /* Grab iv */
+ if (kip->ivlen = km->ivlen)
+ kip->iv = cp;
+
+ cp += kip->ivlen;
+
+ printf("key: parsedata: difference=%d\n", cp - cpmax);
+ return (0);
+}
+
+
+/*----------------------------------------------------------------------
+ printkeyiv:
+
+----------------------------------------------------------------------*/
+void printkeyiv(fp, cp, len)
+ FILE *fp;
+ caddr_t cp;
+ int len;
+{
+ int i;
+ for (i=0; i<len; i++)
+ fprintf(fp, "%02x", (u_int8_t)*(cp+i));
+}
+
+/*----------------------------------------------------------------------
+ printsockaddr:
+
+----------------------------------------------------------------------*/
+void printsockaddr(fp, sa)
+ FILE *fp;
+ struct sockaddr *sa;
+{
+ struct hostent *hp;
+ char *addrp;
+ int len;
+
+#ifdef INET6
+ if (sa->sa_family == AF_INET6) {
+ len = sizeof(struct in_addr6);
+ addrp = (char *)&(((struct sockaddr_in6 *)sa)->sin6_addr);
+ } else {
+#endif /* INET6 */
+ len = sizeof(struct in_addr);
+ addrp = (char *)&(((struct sockaddr_in *)sa)->sin_addr);
+#ifdef INET6
+ }
+#endif /* INET6 */
+
+ if((hp = addr2hostname(addrp, len, sa->sa_family, 0)) != NULL)
+ fprintf(fp, "%s", hp->h_name);
+ else
+ fprintf(fp, "%s", addr2ascii(sa->sa_family, addrp, len, NULL));
+}
+
+/*----------------------------------------------------------------------
+ parsenumtoname:
+
+----------------------------------------------------------------------*/
+char *
+parsenumtoname(tab, num)
+ struct nametonum *tab;
+ int num;
+{
+ int i;
+ for (i = 0; tab[i].name; i++) {
+ if (num == tab[i].num)
+ return(tab[i].name);
+ }
+ return(0);
+}
+
+/*----------------------------------------------------------------------
+ parsenumtoflag:
+
+----------------------------------------------------------------------*/
+int
+parsenumtoflag(tab, num)
+ struct nametonum *tab;
+ int num;
+{
+ int i;
+ for (i = 0; tab[i].name; i++) {
+ if (num == tab[i].num)
+ return(tab[i].flags);
+ }
+ return(-1);
+}
+
+
+/*----------------------------------------------------------------------
+ printkeymsg:
+
+----------------------------------------------------------------------*/
+void printkeymsg(kmp, kdp)
+ struct key_msghdr *kmp;
+ struct key_msgdata *kdp;
+{
+
+ printf("type=%d(%s) ",kmp->type, parsenumtoname(keytypes, kmp->type));
+ printf("spi=%u ", kmp->spi);
+ printf("alogrithm=%u(%s) ", kmp->algorithm,
+ parsenumtoname(algorithmtabs[parsenumtoflag(keytypes, kmp->type)],
+ kmp->algorithm));
+ printf("state=0x%x ",kmp->state);
+
+ if (kmp->state != 0) {
+ if (kmp->state & K_USED)
+ printf("USED ");
+ if (kmp->state & K_UNIQUE)
+ printf("UNIQUE ");
+ if (kmp->state & K_LARVAL)
+ printf("LARVAL ");
+ if (kmp->state & K_ZOMBIE)
+ printf("ZOMBIE ");
+ if (kmp->state & K_DEAD)
+ printf("DEAD ");
+ if (kmp->state & K_INBOUND)
+ printf("INBOUND ");
+ if (kmp->state & K_OUTBOUND)
+ printf("OUTBOUND");
+ }
+ printf("\n");
+ printf("sensitivity_label=%d ",kmp->label);
+ printf("lifetype=%d ",kmp->lifetype);
+ printf("lifetime1=%d ",kmp->lifetime1);
+ printf("lifetime2=%d\n",kmp->lifetime2);
+ printf("key (len=%d):\t",kdp->keylen);
+ printkeyiv(stdout, kdp->key, kdp->keylen);
+ printf("\n");
+ printf("iv (len=%d):\t", kdp->ivlen);
+ printkeyiv(stdout, kdp->iv, kdp->ivlen);
+ printf("\n");
+ printf("src:\t");
+ printsockaddr(stdout, (struct sockaddr *)kdp->src);
+ printf("\n");
+ printf("dst:\t");
+ printsockaddr(stdout, (struct sockaddr *)kdp->dst);
+ printf("\n");
+/* printf("from:\t");
+ printsockaddr(stdout, (struct sockaddr *)kdp->from); */
+ printf("\n");
+}
+
+/*----------------------------------------------------------------------
+ docmd:
+
+----------------------------------------------------------------------*/
+int docmd(argc, argv)
+ int argc;
+ char **argv;
+{
+ int i, j, seqno;
+ int fd;
+ FILE *fp;
+
+ if (!argv[0] || !argc)
+ return -1;
+
+ if (!argv[0][0])
+ return -1;
+
+ bzero(&key_message, sizeof(key_message));
+ key_messageptr = sizeof(struct key_msghdr);
+
+ for (i = 0; keycmds[i].name; i++)
+ if (!strcasecmp(keycmds[i].name, argv[0]))
+ break;
+
+ if (!keycmds[i].name)
+ return -1;
+
+ if (keycmds[i].parse)
+ if ((j = keycmds[i].parse(argc - 1, &(argv[1]))))
+ return j;
+
+ ((struct key_msghdr *)key_message)->key_msglen = key_messageptr;
+ ((struct key_msghdr *)key_message)->key_msgvers = 1;
+ ((struct key_msghdr *)key_message)->key_seq = 1;
+
+ switch(keycmds[i].num) {
+
+ case KEYCMD_ADD:
+#ifndef NOSANITYCHK
+ /*
+ * For now, we do sanity check of security association
+ * information here until we find a better place.
+ */
+ {
+ struct key_msghdr *kmp = (struct key_msghdr *)key_message;
+
+ if ((kmp->type == KEY_TYPE_AH ||
+ kmp->type == KEY_TYPE_ESP) && (kmp->spi < 256)) {
+ warnx("add: spi must be greater than 255");
+ return(0);
+ }
+
+ if (kmp->type == KEY_TYPE_ESP &&
+ (kmp->algorithm == IPSEC_ALGTYPE_ESP_DES_CBC
+#ifdef IPSEC_ALGTYPE_ESP_3DES
+ || kmp->algorithm == IPSEC_ALGTYPE_ESP_3DES
+#endif
+ )) {
+ if (kmp->keylen != 8) {
+ warnx("add: key must be 8 bytes");
+ return (0);
+ }
+ if (kmp->ivlen != 4 && kmp->ivlen != 8) {
+ warnx("add: iv must be 4 or 8 bytes");
+ return (0);
+ }
+ }
+
+ if (kmp->type == KEY_TYPE_AH &&
+ kmp->algorithm == IPSEC_ALGTYPE_AUTH_MD5 && kmp->keylen == 0) {
+ warnx("add: no key specified");
+ return (0);
+ }
+ }
+#endif
+ ((struct key_msghdr *)key_message)->key_msgtype = KEY_ADD;
+ if (write(keysock, key_message,
+ ((struct key_msghdr *)key_message)->key_msglen) !=
+ ((struct key_msghdr *)key_message)->key_msglen) {
+ if (errno == EEXIST)
+ warnx("add: security association already exists");
+ else
+ warn("add");
+ return -1;
+ }
+ read(keysock, key_message, sizeof(key_message));
+ return (0);
+
+ case KEYCMD_DEL:
+ ((struct key_msghdr *)key_message)->key_msgtype = KEY_DELETE;
+ if (write(keysock, key_message,
+ ((struct key_msghdr *)key_message)->key_msglen) !=
+ ((struct key_msghdr *)key_message)->key_msglen) {
+ if (errno == ESRCH) {
+ warnx("delete: security association not found");
+ return 0;
+ } else {
+ warn("delete");
+ return -1;
+ }
+ }
+ read(keysock, key_message, sizeof(key_message));
+ return (0);
+
+ case KEYCMD_GET:
+ ((struct key_msghdr *)key_message)->key_msgtype = KEY_GET;
+ ((struct key_msghdr *)key_message)->key_seq = seqno = keygetseqno++;
+
+ if (write(keysock, key_message,
+ ((struct key_msghdr *)key_message)->key_msglen) !=
+ ((struct key_msghdr *)key_message)->key_msglen) {
+ if (errno == ESRCH) {
+ warnx("get: security association not found");
+ return 0;
+ } else {
+ warn("get");
+ return (-1);
+ } /* endif ESRCH */
+ } /* endif write() */
+
+ {
+ int len;
+ struct key_msgdata keymsgdata;
+
+ len = sizeof(struct key_msghdr) + MAX_SOCKADDR_SZ * 3
+ + MAX_KEY_SZ + MAX_IV_SZ;
+
+readmesg:
+ if (read(keysock, key_message, len) < 0) {
+ warn("read");
+ return -1;
+ }
+
+ if (!((((struct key_msghdr *)&key_message)->key_pid==mypid)
+ && (((struct key_msghdr *)&key_message)->key_msgtype==KEY_GET)
+ && (((struct key_msghdr *)&key_message)->key_seq==seqno))) {
+ fprintf(stderr, ".");
+ goto readmesg;
+ }
+
+ if (((struct key_msghdr *)&key_message)->key_errno != 0) {
+ printf("Error: kernel reporting errno=%d\n",
+ ((struct key_msghdr *)&key_message)->key_errno);
+ return 0;
+ }
+
+ if (parsedata((struct key_msghdr *)&key_message, &keymsgdata) < 0) {
+ printf("get: can't parse reply\n");
+ return -1;
+ }
+ printf("\n");
+ printkeymsg((struct key_msghdr *)&key_message, &keymsgdata);
+ }
+ return (0);
+
+ case KEYCMD_FLUSH:
+ ((struct key_msghdr *)key_message)->key_msgtype = KEY_FLUSH;
+ if (write(keysock, key_message,
+ ((struct key_msghdr *)key_message)->key_msglen) !=
+ ((struct key_msghdr *)key_message)->key_msglen) {
+ warn("write");
+ return -1;
+ }
+ read(keysock, key_message, sizeof(key_message));
+ return (0);
+
+ case KEYCMD_HELP:
+ return help((argc > 1) ? argv[1] : NULL);
+
+ case KEYCMD_SHELL:
+ if (argc > 1) {
+ char buffer[1024], *ap, *ep, *c;
+ int i;
+
+ ep = buffer + sizeof(buffer) - 1;
+ for (i = 1, ap = buffer; (i < argc) && (ap < ep); i++) {
+ c = argv[i];
+ while ((*(ap++) = *(c++)) && (ap < ep));
+ *(ap - 1) = ' ';
+ }
+ *(ap - 1) = 0;
+ system(buffer);
+ } else {
+ char *c = getenv("SHELL");
+ if (!c)
+ c = "/bin/sh";
+ system(c);
+ }
+ return 0;
+
+ case KEYCMD_EXIT:
+ exit(0);
+
+ case KEYCMD_LOAD:
+ if (argc != 2)
+ return 1;
+ return load(argv[1]);
+
+ case KEYCMD_SAVE:
+ if (argc != 2)
+ return 1;
+ if (!strcmp(argv[1], "-"))
+ fp = stdout;
+ else if ((fd = open(argv[1], O_CREAT | O_RDWR | O_EXCL,
+ S_IRUSR | S_IWUSR)) < 0) {
+ warn("open");
+ return 1;
+ } else if (!(fp = fdopen(fd, "w"))) {
+ warn("fdopen");
+ return 1;
+ }
+
+ case KEYCMD_DUMP:
+ ((struct key_msghdr *)key_message)->key_msgtype = KEY_DUMP;
+ if (write(keysock, key_message,
+ ((struct key_msghdr *)key_message)->key_msglen) !=
+ ((struct key_msghdr *)key_message)->key_msglen) {
+ warn("write");
+ return -1;
+ }
+
+ {
+ struct key_msgdata keymsgdata;
+
+readmesg2:
+ if (read(keysock, key_message, sizeof(key_message)) < 0) {
+ warn("read");
+ return -1;
+ }
+
+ if (!((((struct key_msghdr *)&key_message)->key_pid==mypid)
+ && (((struct key_msghdr *)&key_message)->key_msgtype==KEY_DUMP)))
+ goto readmesg2;
+
+ /*
+ * Kernel is done sending secassoc if key_seq == 0
+ */
+ if (((struct key_msghdr *)&key_message)->key_seq == 0) {
+ if ((keycmds[i].num == KEYCMD_SAVE) && (fp != stdout))
+ fclose(fp);
+ return 0;
+ }
+
+ if (parsedata((struct key_msghdr *)&key_message, &keymsgdata) < 0) {
+ printf("get: can't parse reply\n");
+ goto readmesg2;
+ }
+ if (keycmds[i].num == KEYCMD_SAVE) {
+ char *keytype, *algorithm;
+
+ keytype = parsenumtoname(keytypes,
+ ((struct key_msghdr *)&key_message)->type);
+
+ algorithm = parsenumtoname(algorithmtabs[parsenumtoflag(keytypes,
+ ((struct key_msghdr *)&key_message)->type)],
+ ((struct key_msghdr *)&key_message)->algorithm);
+
+ fprintf(fp, "%s %u ", keytype,
+ ((struct key_msghdr *)&key_message)->spi);
+ printsockaddr(fp, (struct sockaddr *)(keymsgdata.src));
+ fprintf(fp, " ");
+ printsockaddr(fp, (struct sockaddr *)(keymsgdata.dst));
+ fprintf(fp, " ");
+ fprintf(fp, "%s ", algorithm);
+ printkeyiv(fp, keymsgdata.key, keymsgdata.keylen);
+ fprintf(fp, " ");
+ printkeyiv(fp, keymsgdata.iv, keymsgdata.ivlen);
+ fprintf(fp, "\n");
+ } else
+ printkeymsg((struct key_msghdr *)&key_message, &keymsgdata);
+ goto readmesg2;
+ }
+ return (0);
+ }
+ return (-1);
+}
+
+/*----------------------------------------------------------------------
+ main:
+
+----------------------------------------------------------------------*/
+int main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int i, j;
+ u_long rcvsize;
+
+ if (getuid())
+ errx(1, "this program is intended for the superuser only");
+
+ if (!(keysock = socket(PF_KEY, SOCK_RAW, 0))) {
+ warn("socket");
+ return -1;
+ }
+
+ for (rcvsize = MAXRCVBUFSIZE; rcvsize; rcvsize -= 1024) {
+ if (setsockopt(keysock, SOL_SOCKET, SO_RCVBUF, &rcvsize,
+ sizeof(rcvsize)) <= 0)
+ break;
+ }
+
+ mypid = getpid();
+ if (mypid < 0) {
+ warn("getpid");
+ return -1;
+ }
+
+ if (argc > 1) {
+ /*
+ * Attempt to do a single command, based on command line arguments.
+ */
+ if (strcasecmp(argv[1], "add") == 0)
+ errx(1, "cannot add keys from the command line. RTFM for why");
+ if ((i = docmd(argc - 1, &(argv[1])))) {
+ if (i > 0) {
+ for (j = 0; keycmds[j].name; j++)
+ if (!strcasecmp(keycmds[j].name, argv[1]))
+ break;
+
+ if (keycmds[j].name) {
+ fprintf(stderr, "usage: %s%s%s\n", keycmds[j].name,
+ keycmds[j].usage ? " " : "",
+ keycmds[j].usage ? keycmds[j].usage : "");
+ exit(1);
+ }
+ }
+ usage();
+ }
+ return 0;
+ }
+
+ {
+ char buffer[1024];
+ char *iargv[KEYCMD_ARG_MAX];
+ int iargc;
+
+ while(1) {
+ printf("key> ");
+ if (!(fgets(buffer, sizeof(buffer), stdin)))
+ return -1;
+ buffer[sizeof(buffer)-1] = 0;
+ /*
+ * get command line, and parse into an argc/argv form.
+ */
+ if ((i = parsecmdline(buffer, iargv, &iargc)) < 0)
+ exit(1);
+ if (i > 0)
+ continue;
+ errno = 0;
+ /*
+ * given argc/argv, process argument as if it came from the command
+ * line.
+ */
+ if ((i = docmd(iargc, iargv))) {
+ if (i > 0) {
+ for (j = 0; keycmds[j].name; j++)
+ if (!strcasecmp(keycmds[j].name, iargv[0]))
+ break;
+
+ if (keycmds[j].name) {
+ fprintf(stderr, "usage: %s%s%s\n", keycmds[j].name,
+ keycmds[j].usage ? " " : "",
+ keycmds[j].usage ? keycmds[j].usage : "");
+ } else
+ i = -1;
+ }
+ if (i < 0) {
+ if (errno)
+ warn("system error");
+ else
+ warnx("unrecognized command");
+ warnx("type 'help' if you need help");
+ };
+ };
+ };
+ };
+}
+
+/* EOF */
diff --git a/usr.sbin/keyadmin/keys b/usr.sbin/keyadmin/keys
new file mode 100644
index 0000000..b1657bf
--- /dev/null
+++ b/usr.sbin/keyadmin/keys
@@ -0,0 +1,18 @@
+# This is an example key file.
+
+# The format of entries in this file is as follows:
+# <type> <spi> <src> <dst> <transform> <key> [iv]
+#
+# where:
+#
+# <type> is currently one of { ah | esp }
+# <spi> is a decimal number
+# <src> is an IP address for the source this association applies to
+# <dst> is an IP address for the destination this assoc. applies to
+# <transform> is currently one of { md5 } for ah, { des-cbc } for esp
+# <key> is a hexadecimal key value (key length is derived from hex len)
+# [iv] is a hexadecimal initial value (length is derived from hex len)
+# [this field is required for des-cbc, ignored for others]
+
+ah 1142 ::0 ::0 md5 0123456789abcdef0123456789abcdef
+esp 1984 ::0 ::0 des-cbc 0123456789abcdef 11223344
diff --git a/usr.sbin/keyserv/Makefile b/usr.sbin/keyserv/Makefile
new file mode 100644
index 0000000..564e8ac
--- /dev/null
+++ b/usr.sbin/keyserv/Makefile
@@ -0,0 +1,24 @@
+# $Id: Makefile,v 1.2 1996/11/22 03:00:17 wpaul Exp wpaul $
+
+PROG= keyserv
+SRCS= keyserv.c setkey.c keyserv_uid.c crypt_svc.c crypt_server.c
+
+MAN8= keyserv.8
+
+CFLAGS+= -DKEYSERV_RANDOM -DBROKEN_DES -I.
+
+LDADD+= -lmp -lrpcsvc
+
+RPCDIR= ${DESTDIR}/usr/include/rpcsvc
+
+CLEANFILES= crypt_svc.c crypt.h
+
+RPCGEN= rpcgen -C
+
+crypt_svc.c: ${RPCDIR}/crypt.x crypt.h
+ ${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..ff3f460
--- /dev/null
+++ b/usr.sbin/keyserv/crypt_server.c
@@ -0,0 +1,297 @@
+/*
+ * Copyright (c) 1996
+ * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <dirent.h>
+#include <dlfcn.h>
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <rpc/des_crypt.h>
+#include <rpc/des.h>
+#include "crypt.h"
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+/*
+ * The U.S. government stupidly believes that a) it can keep strong
+ * crypto code a secret and b) that doing so somehow protects national
+ * interests. It's wrong on both counts, but until it listens to reason
+ * we have to make certain compromises so it doesn't have an excuse to
+ * throw us in federal prison.
+ *
+ * Consequently, the core OS ships without DES support, and keyserv
+ * defaults to using ARCFOUR with only a 40 bit key, just like nutscrape.
+ * This breaks compatibility with Secure RPC on other systems, but it
+ * allows Secure RPC to work between FreeBSD systems that don't have the
+ * DES package installed without throwing security totally out the window.
+ *
+ * In order to avoid having to supply two versions of keyserv (one with
+ * DES and one without), we use dlopen() and friends to load libdes.so
+ * into our address space at runtime. We check for the presence of
+ * /usr/lib/libdes.so.3.0 at startup and load it if we find it. If we
+ * can't find it, or the __des_crypt symbol doesn't exist, we fall back
+ * to the ARCFOUR encryption code. The user can specify another path using
+ * the -p flag.
+ */
+
+ /* arcfour.h */
+typedef struct arcfour_key
+{
+ unsigned char state[256];
+ unsigned char x;
+ unsigned char y;
+} arcfour_key;
+
+static void prepare_key(unsigned char *key_data_ptr,int key_data_len,
+ arcfour_key *key);
+static void arcfour(unsigned char *buffer_ptr,int buffer_len,arcfour_key * key);
+static void swap_byte(unsigned char *a, unsigned char *b);
+
+static void prepare_key(unsigned char *key_data_ptr, int key_data_len,
+ arcfour_key *key)
+{
+ unsigned char index1;
+ unsigned char index2;
+ unsigned char* state;
+ short counter;
+
+ state = &key->state[0];
+ for(counter = 0; counter < 256; counter++)
+ state[counter] = counter;
+ key->x = 0;
+ key->y = 0;
+ index1 = 0;
+ index2 = 0;
+ for(counter = 0; counter < 256; counter++)
+ {
+ index2 = (key_data_ptr[index1] + state[counter] +
+ index2) % 256;
+ swap_byte(&state[counter], &state[index2]);
+
+ index1 = (index1 + 1) % key_data_len;
+ }
+}
+
+static void arcfour(unsigned char *buffer_ptr, int buffer_len, arcfour_key *key)
+{
+ unsigned char x;
+ unsigned char y;
+ unsigned char* state;
+ unsigned char xorIndex;
+ short counter;
+
+ x = key->x;
+ y = key->y;
+
+ state = &key->state[0];
+ for(counter = 0; counter < buffer_len; counter ++)
+ {
+ x = (x + 1) % 256;
+ y = (state[x] + y) % 256;
+ swap_byte(&state[x], &state[y]);
+
+ xorIndex = (state[x] + state[y]) % 256;
+
+ buffer_ptr[counter] ^= state[xorIndex];
+ }
+ key->x = x;
+ key->y = y;
+}
+
+static void swap_byte(unsigned char *a, unsigned char *b)
+{
+ unsigned char swapByte;
+
+ swapByte = *a;
+ *a = *b;
+ *b = swapByte;
+}
+
+/* Dummy _des_crypt function that uses ARCFOUR with a 40 bit key */
+int _arcfour_crypt(buf, len, desp)
+ char *buf;
+ int len;
+ struct desparams *desp;
+{
+ struct arcfour_key arcfourk;
+
+ /*
+ * U.S. government anti-crypto weasels take
+ * note: although we are supplied with a 64 bit
+ * key, we're only passing 40 bits to the ARCFOUR
+ * encryption code. So there.
+ */
+ prepare_key(desp->des_key, 5, &arcfourk);
+ arcfour(buf, len, &arcfourk);
+
+ return(DESERR_NOHWDEVICE);
+}
+
+int (*_my_crypt)__P((char *, int, struct desparams *)) = NULL;
+
+static void *dlhandle;
+
+#ifndef _PATH_USRLIB
+#define _PATH_USRLIB "/usr/lib"
+#endif
+
+#ifndef LIBDES
+#define LIBDES "libdes.so.3."
+#endif
+
+void load_des(warn, libpath)
+ int warn;
+ char *libpath;
+{
+ DIR *dird;
+ struct dirent *dirp;
+ char dlpath[MAXPATHLEN];
+ int minor = -1;
+ int len;
+
+ if (libpath == NULL) {
+ len = strlen(LIBDES);
+ if ((dird = opendir(_PATH_USRLIB)) == NULL)
+ err(1, "opendir(/usr/lib) failed");
+
+ while ((dirp = readdir(dird)) != NULL) {
+ /* must have a minor number */
+ if (strlen(dirp->d_name) <= len)
+ continue;
+ if (!strncmp(dirp->d_name, LIBDES, len)) {
+ if (atoi((dirp->d_name + len + 1)) > minor) {
+ minor = atoi((dirp->d_name + len + 1));
+ snprintf(dlpath,sizeof(dlpath),"%s/%s",
+ _PATH_USRLIB, dirp->d_name);
+ }
+ }
+ }
+
+ closedir(dird);
+ } else
+ snprintf(dlpath, sizeof(dlpath), "%s", libpath);
+
+ if (dlpath != NULL && (dlhandle = dlopen(dlpath, 0444)) != NULL)
+ _my_crypt = (int (*)())dlsym(dlhandle, "__des_crypt");
+
+ if (_my_crypt == NULL) {
+ if (dlhandle != NULL)
+ dlclose(dlhandle);
+ _my_crypt = &_arcfour_crypt;
+ if (warn) {
+ printf ("DES support disabled -- using ARCFOUR instead.\n");
+ printf ("Warning: ARCFOUR cipher is not compatible with ");
+ printf ("other Secure RPC implementations.\nInstall ");
+ printf ("the FreeBSD 'des' distribution to enable");
+ printf (" DES encryption.\n");
+ }
+ } else {
+ if (warn) {
+ printf ("DES support enabled\n");
+ printf ("Using %s shared object.\n", dlpath);
+ }
+ }
+
+ return;
+}
+
+desresp *
+des_crypt_1_svc(desargs *argp, struct svc_req *rqstp)
+{
+ static desresp result;
+ struct desparams dparm;
+
+ if (argp->desbuf.desbuf_len > DES_MAXDATA) {
+ result.stat = DESERR_BADPARAM;
+ return(&result);
+ }
+
+
+ bcopy(argp->des_key, dparm.des_key, 8);
+ bcopy(argp->des_ivec, dparm.des_ivec, 8);
+ dparm.des_mode = argp->des_mode;
+ dparm.des_dir = argp->des_dir;
+#ifdef BROKEN_DES
+ dparm.UDES.UDES_buf = argp->desbuf.desbuf_val;
+#endif
+
+ /*
+ * XXX This compensates for a bug in the libdes Secure RPC
+ * compat interface. (Actually, there are a couple.) The
+ * des_ecb_encrypt() routine in libdes only encrypts 8 bytes
+ * (64 bits) at a time. However, the Sun Secure RPC ecb_crypt()
+ * routine is supposed to be able to handle buffers up to 8Kbytes.
+ * The rpc_enc module in libdes ignores this fact and just drops
+ * the length parameter on the floor, encrypting only the
+ * first 64 bits of whatever buffer you feed it. We deal with
+ * this here: if we're using DES encryption, and we're using
+ * ECB mode, then we make a pass over the entire buffer
+ * ourselves. Note: the rpc_enc module incorrectly transposes
+ * the mode flags, so when you ask for CBC mode, you're really
+ * getting ECB mode.
+ */
+#ifdef BROKEN_DES
+ if (_my_crypt != &_arcfour_crypt && argp->des_mode == CBC) {
+#else
+ if (_my_crypt != &_arcfour_crypt && argp->des_mode == ECB) {
+#endif
+ int i;
+ char *dptr;
+
+ for (i = 0; i < argp->desbuf.desbuf_len / 8; i++) {
+ dptr = argp->desbuf.desbuf_val;
+ dptr += (i * 8);
+#ifdef BROKEN_DES
+ dparm.UDES.UDES_buf = dptr;
+#endif
+ result.stat = _my_crypt(dptr, 8, &dparm);
+ }
+ } else {
+ result.stat = _my_crypt(argp->desbuf.desbuf_val,
+ argp->desbuf.desbuf_len,
+ &dparm);
+ }
+
+ if (result.stat == DESERR_NONE || result.stat == DESERR_NOHWDEVICE) {
+ bcopy(dparm.des_ivec, result.des_ivec, 8);
+ result.desbuf.desbuf_len = argp->desbuf.desbuf_len;
+ result.desbuf.desbuf_val = argp->desbuf.desbuf_val;
+ }
+
+ return (&result);
+}
diff --git a/usr.sbin/keyserv/keyserv.8 b/usr.sbin/keyserv/keyserv.8
new file mode 100644
index 0000000..48eac9b
--- /dev/null
+++ b/usr.sbin/keyserv/keyserv.8
@@ -0,0 +1,79 @@
+.\" @(#)keyserv.1m 1.21 93/07/14 SMI; from SVr4
+.\"macro stdmacro
+.\" Copyright 1989 AT&T
+.\" @(#)keyserv.8c 1.8 89/03/29 SMI;
+.\".TH KEYSERV 8C "9 September 1987"
+.Dd September 14, 1992
+.Dt KEYSERV 8
+.Os
+.Sh NAME
+.Nm keyserv
+.Nd server for storing private encryption keys
+.Sh SYNOPSIS
+.Nm keyserv
+.Op Fl d
+.Op Fl D
+.Op Fl n
+.Op Fl p Ar path
+.Op Fl v
+.Sh AVAILABILITY
+SUNWcsu
+.Sh DESCRIPTION
+.Nm Keyserv
+is a daemon that is used for storing the
+private encryption keys of each
+user logged into the system.
+These encryption keys are used for accessing
+secure network services such as secure NFS.
+.Pp
+Normally, root's key is read from the file
+.Pa /etc/.rootkey
+when the daemon is started.
+This is useful during power-fail reboots
+when no one is around to type a password.
+.Pp
+If a client with no secret key calls
+.Nm keyserv ,
+then the key of user
+.Em nobody
+is used instead as the default key.
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl d
+Disable the use of default keys for
+.Em nobody .
+.It Fl D
+Run in debugging mode and log all requests to
+.Nm keyserv .
+.It Fl n
+Root's secret key is not read from
+.Pa /etc/.rootkey .
+Instead,
+.Nm
+prompts the user for the password to decrypt
+root's key stored in the
+.Pa /etc/publickey
+database and then stores the decrypted key in
+.Pa /etc/.rootkey
+for future use.
+This option is useful if the
+.Pa /etc/.rootkey
+file ever gets out of date or corrupted.
+.It Fl p Ar path
+Specify where to search for
+.Pa libdes.so.3 .
+Default is
+.Pa /usr/lib .
+.It Fl v
+Display status of DES support (enabled/disabled).
+.El
+.Sh FILES
+.Bl -tag -width /usr/lib/libdes.so.3. -compact
+.It Pa /etc/.rootkey
+.It Pa /usr/lib/libdes.so.3.
+.El
+.Sh "SEE ALSO"
+.Xr keylogin 1 ,
+.Xr keylogout 1 ,
+.Xr publickey 5
diff --git a/usr.sbin/keyserv/keyserv.c b/usr.sbin/keyserv/keyserv.c
new file mode 100644
index 0000000..e49dded
--- /dev/null
+++ b/usr.sbin/keyserv/keyserv.c
@@ -0,0 +1,803 @@
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)keyserv.c 1.15 94/04/25 SMI";
+#endif
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+/*
+ * Copyright (c) 1986 - 1991 by Sun Microsystems, Inc.
+ */
+
+/*
+ * Keyserver
+ * Store secret keys per uid. Do public key encryption and decryption
+ * operations. Generate "random" keys.
+ * Do not talk to anything but a local root
+ * process on the local transport only
+ */
+
+#include <err.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <rpc/rpc.h>
+#include <rpc/pmap_clnt.h>
+#include <sys/param.h>
+#include <sys/file.h>
+#include <rpc/des_crypt.h>
+#include <rpc/des.h>
+#include <rpc/key_prot.h>
+#include <rpcsvc/crypt.h>
+#include "keyserv.h"
+
+#ifndef NGROUPS
+#define NGROUPS 16
+#endif
+
+#ifndef KEYSERVSOCK
+#define KEYSERVSOCK "/var/run/keyservsock"
+#endif
+
+static void randomize __P(( des_block * ));
+static void usage __P(( void ));
+static int getrootkey __P(( des_block *, int ));
+static int root_auth __P(( SVCXPRT *, struct svc_req * ));
+
+#ifdef DEBUG
+static int debugging = 1;
+#else
+static int debugging = 0;
+#endif
+
+static void keyprogram();
+static des_block masterkey;
+char *getenv();
+static char ROOTKEY[] = "/etc/.rootkey";
+
+/*
+ * Hack to allow the keyserver to use AUTH_DES (for authenticated
+ * NIS+ calls, for example). The only functions that get called
+ * are key_encryptsession_pk, key_decryptsession_pk, and key_gendes.
+ *
+ * The approach is to have the keyserver fill in pointers to local
+ * implementations of these functions, and to call those in key_call().
+ */
+
+extern cryptkeyres *(*__key_encryptsession_pk_LOCAL)();
+extern cryptkeyres *(*__key_decryptsession_pk_LOCAL)();
+extern des_block *(*__key_gendes_LOCAL)();
+extern int (*__des_crypt_LOCAL)();
+
+cryptkeyres *key_encrypt_pk_2_svc_prog __P(( uid_t, cryptkeyarg2 * ));
+cryptkeyres *key_decrypt_pk_2_svc_prog __P(( uid_t, cryptkeyarg2 * ));
+des_block *key_gen_1_svc_prog __P(( void *, struct svc_req * ));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int nflag = 0;
+ int c;
+ register SVCXPRT *transp;
+ int sock = RPC_ANYSOCK;
+ int warn = 0;
+ char *path = NULL;
+
+ __key_encryptsession_pk_LOCAL = &key_encrypt_pk_2_svc_prog;
+ __key_decryptsession_pk_LOCAL = &key_decrypt_pk_2_svc_prog;
+ __key_gendes_LOCAL = &key_gen_1_svc_prog;
+
+ while ((c = getopt(argc, argv, "ndDvp:")) != -1)
+ switch (c) {
+ case 'n':
+ nflag++;
+ break;
+ case 'd':
+ pk_nodefaultkeys();
+ break;
+ case 'D':
+ debugging = 1;
+ break;
+ case 'v':
+ warn = 1;
+ break;
+ case 'p':
+ path = optarg;
+ break;
+ default:
+ usage();
+ }
+
+ load_des(warn, path);
+ __des_crypt_LOCAL = _my_crypt;
+ if (svc_auth_reg(AUTH_DES, _svcauth_des) == -1)
+ errx(1, "failed to register AUTH_DES authenticator");
+
+ if (optind != argc) {
+ usage();
+ }
+
+ /*
+ * Initialize
+ */
+ (void) umask(066); /* paranoia */
+ if (geteuid() != 0)
+ errx(1, "keyserv must be run as root");
+ setmodulus(HEXMODULUS);
+ getrootkey(&masterkey, nflag);
+
+
+ /* Create services. */
+
+ (void) pmap_unset(KEY_PROG, KEY_VERS);
+ (void) pmap_unset(KEY_PROG, KEY_VERS2);
+ unlink(KEYSERVSOCK);
+
+ transp = svcudp_create(RPC_ANYSOCK);
+ if (transp == NULL)
+ errx(1, "cannot create udp service");
+ if (!svc_register(transp, KEY_PROG, KEY_VERS, keyprogram, IPPROTO_UDP))
+ errx(1, "unable to register (KEY_PROG, KEY_VERS, udp)");
+ if (!svc_register(transp, KEY_PROG, KEY_VERS2, keyprogram, IPPROTO_UDP))
+ errx(1, "unable to register (KEY_PROG, KEY_VERS2, udp)");
+
+ transp = svctcp_create(RPC_ANYSOCK, 0, 0);
+ if (transp == NULL)
+ errx(1, "cannot create tcp service");
+ if (!svc_register(transp, KEY_PROG, KEY_VERS, keyprogram, IPPROTO_TCP))
+ errx(1, "unable to register (KEY_PROG, KEY_VERS, tcp)");
+ if (!svc_register(transp, KEY_PROG, KEY_VERS2, keyprogram, IPPROTO_TCP))
+ errx(1, "unable to register (KEY_PROG, KEY_VERS2, tcp)");
+
+ transp = svcunix_create(sock, 0, 0, KEYSERVSOCK);
+ chmod(KEYSERVSOCK, 0666);
+ if (transp == NULL)
+ errx(1, "cannot create AF_UNIX service");
+ if (!svc_register(transp, KEY_PROG, KEY_VERS, keyprogram, 0))
+ errx(1, "unable to register (KEY_PROG, KEY_VERS, unix)");
+ if (!svc_register(transp, KEY_PROG, KEY_VERS2, keyprogram, 0))
+ errx(1, "unable to register (KEY_PROG, KEY_VERS2, unix)");
+ if (!svc_register(transp, CRYPT_PROG, CRYPT_VERS, crypt_prog_1, 0))
+ errx(1, "unable to register (CRYPT_PROG, CRYPT_VERS, unix)");
+
+ if (!debugging) {
+ daemon(0,0);
+ }
+
+ signal(SIGPIPE, SIG_IGN);
+
+ svc_run();
+ abort();
+ /* NOTREACHED */
+}
+
+/*
+ * In the event that we don't get a root password, we try to
+ * randomize the master key the best we can
+ */
+static void
+randomize(master)
+ des_block *master;
+{
+ int i;
+ int seed;
+ struct timeval tv;
+ int shift;
+
+ seed = 0;
+ for (i = 0; i < 1024; i++) {
+ (void) gettimeofday(&tv, (struct timezone *) NULL);
+ shift = i % 8 * sizeof (int);
+ seed ^= (tv.tv_usec << shift) | (tv.tv_usec >> (32 - shift));
+ }
+#ifdef KEYSERV_RANDOM
+ srandom(seed);
+ master->key.low = random();
+ master->key.high = random();
+ srandom(seed);
+#else
+ /* use stupid dangerous bad rand() */
+ srand(seed);
+ master->key.low = rand();
+ master->key.high = rand();
+ srand(seed);
+#endif
+}
+
+/*
+ * Try to get root's secret key, by prompting if terminal is a tty, else trying
+ * from standard input.
+ * Returns 1 on success.
+ */
+static int
+getrootkey(master, prompt)
+ des_block *master;
+ int prompt;
+{
+ char *passwd;
+ char name[MAXNETNAMELEN + 1];
+ char secret[HEXKEYBYTES];
+ key_netstarg netstore;
+ int fd;
+
+ if (!prompt) {
+ /*
+ * Read secret key out of ROOTKEY
+ */
+ fd = open(ROOTKEY, O_RDONLY, 0);
+ if (fd < 0) {
+ randomize(master);
+ return (0);
+ }
+ if (read(fd, secret, HEXKEYBYTES) < HEXKEYBYTES) {
+ warnx("the key read from %s was too short", ROOTKEY);
+ (void) close(fd);
+ return (0);
+ }
+ (void) close(fd);
+ if (!getnetname(name)) {
+ warnx(
+ "failed to generate host's netname when establishing root's key");
+ return (0);
+ }
+ memcpy(netstore.st_priv_key, secret, HEXKEYBYTES);
+ memset(netstore.st_pub_key, 0, HEXKEYBYTES);
+ netstore.st_netname = name;
+ if (pk_netput(0, &netstore) != KEY_SUCCESS) {
+ warnx("could not set root's key and netname");
+ return (0);
+ }
+ return (1);
+ }
+ /*
+ * Decrypt yellow pages publickey entry to get secret key
+ */
+ passwd = getpass("root password:");
+ passwd2des(passwd, (char *)master);
+ getnetname(name);
+ if (!getsecretkey(name, secret, passwd)) {
+ warnx("can't find %s's secret key", name);
+ return (0);
+ }
+ if (secret[0] == 0) {
+ warnx("password does not decrypt secret key for %s", name);
+ return (0);
+ }
+ (void) pk_setkey(0, secret);
+ /*
+ * Store it for future use in $ROOTKEY, if possible
+ */
+ fd = open(ROOTKEY, O_WRONLY|O_TRUNC|O_CREAT, 0);
+ if (fd > 0) {
+ char newline = '\n';
+
+ write(fd, secret, strlen(secret));
+ write(fd, &newline, sizeof (newline));
+ close(fd);
+ }
+ return (1);
+}
+
+/*
+ * Procedures to implement RPC service
+ */
+char *
+strstatus(status)
+ keystatus status;
+{
+ switch (status) {
+ case KEY_SUCCESS:
+ return ("KEY_SUCCESS");
+ case KEY_NOSECRET:
+ return ("KEY_NOSECRET");
+ case KEY_UNKNOWN:
+ return ("KEY_UNKNOWN");
+ case KEY_SYSTEMERR:
+ return ("KEY_SYSTEMERR");
+ default:
+ return ("(bad result code)");
+ }
+}
+
+keystatus *
+key_set_1_svc_prog(uid, key)
+ uid_t uid;
+ keybuf key;
+{
+ static keystatus status;
+
+ if (debugging) {
+ (void) fprintf(stderr, "set(%ld, %.*s) = ", uid,
+ (int) sizeof (keybuf), key);
+ }
+ status = pk_setkey(uid, key);
+ if (debugging) {
+ (void) fprintf(stderr, "%s\n", strstatus(status));
+ (void) fflush(stderr);
+ }
+ return (&status);
+}
+
+cryptkeyres *
+key_encrypt_pk_2_svc_prog(uid, arg)
+ uid_t uid;
+ cryptkeyarg2 *arg;
+{
+ static cryptkeyres res;
+
+ if (debugging) {
+ (void) fprintf(stderr, "encrypt(%ld, %s, %08x%08x) = ", uid,
+ arg->remotename, arg->deskey.key.high,
+ arg->deskey.key.low);
+ }
+ res.cryptkeyres_u.deskey = arg->deskey;
+ res.status = pk_encrypt(uid, arg->remotename, &(arg->remotekey),
+ &res.cryptkeyres_u.deskey);
+ if (debugging) {
+ if (res.status == KEY_SUCCESS) {
+ (void) fprintf(stderr, "%08x%08x\n",
+ res.cryptkeyres_u.deskey.key.high,
+ res.cryptkeyres_u.deskey.key.low);
+ } else {
+ (void) fprintf(stderr, "%s\n", strstatus(res.status));
+ }
+ (void) fflush(stderr);
+ }
+ return (&res);
+}
+
+cryptkeyres *
+key_decrypt_pk_2_svc_prog(uid, arg)
+ uid_t uid;
+ cryptkeyarg2 *arg;
+{
+ static cryptkeyres res;
+
+ if (debugging) {
+ (void) fprintf(stderr, "decrypt(%ld, %s, %08x%08x) = ", uid,
+ arg->remotename, arg->deskey.key.high,
+ arg->deskey.key.low);
+ }
+ res.cryptkeyres_u.deskey = arg->deskey;
+ res.status = pk_decrypt(uid, arg->remotename, &(arg->remotekey),
+ &res.cryptkeyres_u.deskey);
+ if (debugging) {
+ if (res.status == KEY_SUCCESS) {
+ (void) fprintf(stderr, "%08x%08x\n",
+ res.cryptkeyres_u.deskey.key.high,
+ res.cryptkeyres_u.deskey.key.low);
+ } else {
+ (void) fprintf(stderr, "%s\n", strstatus(res.status));
+ }
+ (void) fflush(stderr);
+ }
+ return (&res);
+}
+
+keystatus *
+key_net_put_2_svc_prog(uid, arg)
+ uid_t uid;
+ key_netstarg *arg;
+{
+ static keystatus status;
+
+ if (debugging) {
+ (void) fprintf(stderr, "net_put(%s, %.*s, %.*s) = ",
+ arg->st_netname, (int)sizeof (arg->st_pub_key),
+ arg->st_pub_key, (int)sizeof (arg->st_priv_key),
+ arg->st_priv_key);
+ };
+
+ status = pk_netput(uid, arg);
+
+ if (debugging) {
+ (void) fprintf(stderr, "%s\n", strstatus(status));
+ (void) fflush(stderr);
+ }
+
+ return (&status);
+}
+
+key_netstres *
+key_net_get_2_svc_prog(uid, arg)
+ uid_t uid;
+ void *arg;
+{
+ static key_netstres keynetname;
+
+ if (debugging)
+ (void) fprintf(stderr, "net_get(%ld) = ", uid);
+
+ keynetname.status = pk_netget(uid, &keynetname.key_netstres_u.knet);
+ if (debugging) {
+ if (keynetname.status == KEY_SUCCESS) {
+ fprintf(stderr, "<%s, %.*s, %.*s>\n",
+ keynetname.key_netstres_u.knet.st_netname,
+ (int)sizeof (keynetname.key_netstres_u.knet.st_pub_key),
+ keynetname.key_netstres_u.knet.st_pub_key,
+ (int)sizeof (keynetname.key_netstres_u.knet.st_priv_key),
+ keynetname.key_netstres_u.knet.st_priv_key);
+ } else {
+ (void) fprintf(stderr, "NOT FOUND\n");
+ }
+ (void) fflush(stderr);
+ }
+
+ return (&keynetname);
+
+}
+
+cryptkeyres *
+key_get_conv_2_svc_prog(uid, arg)
+ uid_t uid;
+ keybuf arg;
+{
+ static cryptkeyres res;
+
+ if (debugging)
+ (void) fprintf(stderr, "get_conv(%ld, %.*s) = ", uid,
+ (int)sizeof (arg), arg);
+
+
+ res.status = pk_get_conv_key(uid, arg, &res);
+
+ if (debugging) {
+ if (res.status == KEY_SUCCESS) {
+ (void) fprintf(stderr, "%08x%08x\n",
+ res.cryptkeyres_u.deskey.key.high,
+ res.cryptkeyres_u.deskey.key.low);
+ } else {
+ (void) fprintf(stderr, "%s\n", strstatus(res.status));
+ }
+ (void) fflush(stderr);
+ }
+ return (&res);
+}
+
+
+cryptkeyres *
+key_encrypt_1_svc_prog(uid, arg)
+ uid_t uid;
+ cryptkeyarg *arg;
+{
+ static cryptkeyres res;
+
+ if (debugging) {
+ (void) fprintf(stderr, "encrypt(%ld, %s, %08x%08x) = ", uid,
+ arg->remotename, arg->deskey.key.high,
+ arg->deskey.key.low);
+ }
+ res.cryptkeyres_u.deskey = arg->deskey;
+ res.status = pk_encrypt(uid, arg->remotename, NULL,
+ &res.cryptkeyres_u.deskey);
+ if (debugging) {
+ if (res.status == KEY_SUCCESS) {
+ (void) fprintf(stderr, "%08x%08x\n",
+ res.cryptkeyres_u.deskey.key.high,
+ res.cryptkeyres_u.deskey.key.low);
+ } else {
+ (void) fprintf(stderr, "%s\n", strstatus(res.status));
+ }
+ (void) fflush(stderr);
+ }
+ return (&res);
+}
+
+cryptkeyres *
+key_decrypt_1_svc_prog(uid, arg)
+ uid_t uid;
+ cryptkeyarg *arg;
+{
+ static cryptkeyres res;
+
+ if (debugging) {
+ (void) fprintf(stderr, "decrypt(%ld, %s, %08x%08x) = ", uid,
+ arg->remotename, arg->deskey.key.high,
+ arg->deskey.key.low);
+ }
+ res.cryptkeyres_u.deskey = arg->deskey;
+ res.status = pk_decrypt(uid, arg->remotename, NULL,
+ &res.cryptkeyres_u.deskey);
+ if (debugging) {
+ if (res.status == KEY_SUCCESS) {
+ (void) fprintf(stderr, "%08x%08x\n",
+ res.cryptkeyres_u.deskey.key.high,
+ res.cryptkeyres_u.deskey.key.low);
+ } else {
+ (void) fprintf(stderr, "%s\n", strstatus(res.status));
+ }
+ (void) fflush(stderr);
+ }
+ return (&res);
+}
+
+/* ARGSUSED */
+des_block *
+key_gen_1_svc_prog(v, s)
+ void *v;
+ struct svc_req *s;
+{
+ struct timeval time;
+ static des_block keygen;
+ static des_block key;
+
+ (void) gettimeofday(&time, (struct timezone *) NULL);
+ keygen.key.high += (time.tv_sec ^ time.tv_usec);
+ keygen.key.low += (time.tv_sec ^ time.tv_usec);
+ ecb_crypt((char *)&masterkey, (char *)&keygen, sizeof (keygen),
+ DES_ENCRYPT | DES_HW);
+ key = keygen;
+ des_setparity((char *)&key);
+ if (debugging) {
+ (void) fprintf(stderr, "gen() = %08x%08x\n", key.key.high,
+ key.key.low);
+ (void) fflush(stderr);
+ }
+ return (&key);
+}
+
+getcredres *
+key_getcred_1_svc_prog(uid, name)
+ uid_t uid;
+ netnamestr *name;
+{
+ static getcredres res;
+ static u_int gids[NGROUPS];
+ struct unixcred *cred;
+
+ cred = &res.getcredres_u.cred;
+ cred->gids.gids_val = gids;
+ if (!netname2user(*name, (uid_t *) &cred->uid, (gid_t *) &cred->gid,
+ (int *)&cred->gids.gids_len, (gid_t *)gids)) {
+ res.status = KEY_UNKNOWN;
+ } else {
+ res.status = KEY_SUCCESS;
+ }
+ if (debugging) {
+ (void) fprintf(stderr, "getcred(%s) = ", *name);
+ if (res.status == KEY_SUCCESS) {
+ (void) fprintf(stderr, "uid=%d, gid=%d, grouplen=%d\n",
+ cred->uid, cred->gid, cred->gids.gids_len);
+ } else {
+ (void) fprintf(stderr, "%s\n", strstatus(res.status));
+ }
+ (void) fflush(stderr);
+ }
+ return (&res);
+}
+
+/*
+ * RPC boilerplate
+ */
+static void
+keyprogram(rqstp, transp)
+ struct svc_req *rqstp;
+ SVCXPRT *transp;
+{
+ union {
+ keybuf key_set_1_arg;
+ cryptkeyarg key_encrypt_1_arg;
+ cryptkeyarg key_decrypt_1_arg;
+ netnamestr key_getcred_1_arg;
+ cryptkeyarg key_encrypt_2_arg;
+ cryptkeyarg key_decrypt_2_arg;
+ netnamestr key_getcred_2_arg;
+ cryptkeyarg2 key_encrypt_pk_2_arg;
+ cryptkeyarg2 key_decrypt_pk_2_arg;
+ key_netstarg key_net_put_2_arg;
+ netobj key_get_conv_2_arg;
+ } argument;
+ char *result;
+ bool_t(*xdr_argument)(), (*xdr_result)();
+ char *(*local) ();
+ uid_t uid = -1;
+ int check_auth;
+
+ switch (rqstp->rq_proc) {
+ case NULLPROC:
+ svc_sendreply(transp, xdr_void, (char *)NULL);
+ return;
+
+ case KEY_SET:
+ xdr_argument = xdr_keybuf;
+ xdr_result = xdr_int;
+ local = (char *(*)()) key_set_1_svc_prog;
+ check_auth = 1;
+ break;
+
+ case KEY_ENCRYPT:
+ xdr_argument = xdr_cryptkeyarg;
+ xdr_result = xdr_cryptkeyres;
+ local = (char *(*)()) key_encrypt_1_svc_prog;
+ check_auth = 1;
+ break;
+
+ case KEY_DECRYPT:
+ xdr_argument = xdr_cryptkeyarg;
+ xdr_result = xdr_cryptkeyres;
+ local = (char *(*)()) key_decrypt_1_svc_prog;
+ check_auth = 1;
+ break;
+
+ case KEY_GEN:
+ xdr_argument = xdr_void;
+ xdr_result = xdr_des_block;
+ local = (char *(*)()) key_gen_1_svc_prog;
+ check_auth = 0;
+ break;
+
+ case KEY_GETCRED:
+ xdr_argument = xdr_netnamestr;
+ xdr_result = xdr_getcredres;
+ local = (char *(*)()) key_getcred_1_svc_prog;
+ check_auth = 0;
+ break;
+
+ case KEY_ENCRYPT_PK:
+ xdr_argument = xdr_cryptkeyarg2;
+ xdr_result = xdr_cryptkeyres;
+ local = (char *(*)()) key_encrypt_pk_2_svc_prog;
+ check_auth = 1;
+ break;
+
+ case KEY_DECRYPT_PK:
+ xdr_argument = xdr_cryptkeyarg2;
+ xdr_result = xdr_cryptkeyres;
+ local = (char *(*)()) key_decrypt_pk_2_svc_prog;
+ check_auth = 1;
+ break;
+
+
+ case KEY_NET_PUT:
+ xdr_argument = xdr_key_netstarg;
+ xdr_result = xdr_keystatus;
+ local = (char *(*)()) key_net_put_2_svc_prog;
+ check_auth = 1;
+ break;
+
+ case KEY_NET_GET:
+ xdr_argument = (xdrproc_t) xdr_void;
+ xdr_result = xdr_key_netstres;
+ local = (char *(*)()) key_net_get_2_svc_prog;
+ check_auth = 1;
+ break;
+
+ case KEY_GET_CONV:
+ xdr_argument = (xdrproc_t) xdr_keybuf;
+ xdr_result = xdr_cryptkeyres;
+ local = (char *(*)()) key_get_conv_2_svc_prog;
+ check_auth = 1;
+ break;
+
+ default:
+ svcerr_noproc(transp);
+ return;
+ }
+ if (check_auth) {
+ if (root_auth(transp, rqstp) == 0) {
+ if (debugging) {
+ (void) fprintf(stderr,
+ "not local privileged process\n");
+ }
+ svcerr_weakauth(transp);
+ return;
+ }
+ if (rqstp->rq_cred.oa_flavor != AUTH_SYS) {
+ if (debugging) {
+ (void) fprintf(stderr,
+ "not unix authentication\n");
+ }
+ svcerr_weakauth(transp);
+ return;
+ }
+ uid = ((struct authsys_parms *)rqstp->rq_clntcred)->aup_uid;
+ }
+
+ memset((char *) &argument, 0, sizeof (argument));
+ if (!svc_getargs(transp, xdr_argument, (caddr_t)&argument)) {
+ svcerr_decode(transp);
+ return;
+ }
+ result = (*local) (uid, &argument);
+ if (!svc_sendreply(transp, xdr_result, (char *) result)) {
+ if (debugging)
+ (void) fprintf(stderr, "unable to reply\n");
+ svcerr_systemerr(transp);
+ }
+ if (!svc_freeargs(transp, xdr_argument, (caddr_t)&argument)) {
+ if (debugging)
+ (void) fprintf(stderr,
+ "unable to free arguments\n");
+ exit(1);
+ }
+ return;
+}
+
+static int
+root_auth(trans, rqstp)
+ SVCXPRT *trans;
+ struct svc_req *rqstp;
+{
+ uid_t uid;
+ struct sockaddr_in *remote;
+
+ remote = svc_getcaller(trans);
+ if (remote->sin_family == AF_INET) {
+ if (debugging)
+ fprintf(stderr, "client didn't use AF_UNIX\n");
+ return (0);
+ }
+
+ if (__rpc_get_local_uid(&uid, trans) < 0) {
+ if (debugging)
+ fprintf(stderr, "__rpc_get_local_uid failed\n");
+ return (0);
+ }
+
+ if (debugging)
+ fprintf(stderr, "local_uid %ld\n", uid);
+ if (uid == 0)
+ return (1);
+ if (rqstp->rq_cred.oa_flavor == AUTH_SYS) {
+ if (((uid_t) ((struct authunix_parms *)
+ rqstp->rq_clntcred)->aup_uid)
+ == uid) {
+ return (1);
+ } else {
+ if (debugging)
+ fprintf(stderr,
+ "local_uid %ld mismatches auth %ld\n", uid,
+((uid_t) ((struct authunix_parms *)rqstp->rq_clntcred)->aup_uid));
+ return (0);
+ }
+ } else {
+ if (debugging)
+ fprintf(stderr, "Not auth sys\n");
+ return (0);
+ }
+}
+
+static void
+usage()
+{
+ (void) fprintf(stderr,
+ "usage: keyserv [-n] [-D] [-d] [-v] [-p path]\n");
+ (void) fprintf(stderr, "-d disables the use of default keys\n");
+ exit(1);
+}
diff --git a/usr.sbin/keyserv/keyserv.h b/usr.sbin/keyserv/keyserv.h
new file mode 100644
index 0000000..6e2402d
--- /dev/null
+++ b/usr.sbin/keyserv/keyserv.h
@@ -0,0 +1,19 @@
+
+extern void setmodulus __P((char *modx));
+
+extern keystatus pk_setkey __P(( uid_t, keybuf ));;
+extern keystatus pk_encrypt __P(( uid_t, char *, netobj *, des_block * ));
+extern keystatus pk_decrypt __P(( uid_t, char *, netobj *, des_block * ));
+extern keystatus pk_netput __P(( uid_t, key_netstarg * ));
+extern keystatus pk_netget __P(( uid_t, key_netstarg * ));
+extern keystatus pk_get_conv_key __P(( uid_t, keybuf, cryptkeyres * ));
+extern void pk_nodefaultkeys __P(( void ));
+
+extern int __rpc_get_local_uid __P(( uid_t * , SVCXPRT * ));
+extern void crypt_prog_1 __P(( struct svc_req *, register SVCXPRT * ));
+extern void load_des __P(( int, char * ));
+
+extern int (*_my_crypt)__P(( char *, int, struct desparams * ));
+
+extern char ROOTKEY[];
+
diff --git a/usr.sbin/keyserv/keyserv_uid.c b/usr.sbin/keyserv/keyserv_uid.c
new file mode 100644
index 0000000..add3d33
--- /dev/null
+++ b/usr.sbin/keyserv/keyserv_uid.c
@@ -0,0 +1,76 @@
+/*
+ * Copyright (c) 1996
+ * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <rpc/key_prot.h>
+#include <rpc/des.h>
+#include <sys/socket.h>
+#include <sys/param.h>
+
+#include "keyserv.h"
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id$";
+#endif
+
+/*
+ * XXX should be declared somewhere
+ */
+struct cmessage {
+ struct cmsghdr cmsg;
+ struct cmsgcred cmcred;
+};
+
+int
+__rpc_get_local_uid(uid, transp)
+ uid_t *uid;
+ SVCXPRT *transp;
+{
+ struct cmessage *cm;
+
+ if (transp->xp_verf.oa_length < sizeof(struct cmessage) ||
+ transp->xp_verf.oa_base == NULL ||
+ transp->xp_verf.oa_flavor != AUTH_UNIX)
+ return(-1);
+
+ cm = (struct cmessage *)transp->xp_verf.oa_base;
+ if (cm->cmsg.cmsg_type != SCM_CREDS)
+ return(-1);
+
+ *uid = cm->cmcred.cmcred_euid;
+ return(0);
+}
diff --git a/usr.sbin/keyserv/setkey.c b/usr.sbin/keyserv/setkey.c
new file mode 100644
index 0000000..8403913
--- /dev/null
+++ b/usr.sbin/keyserv/setkey.c
@@ -0,0 +1,550 @@
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)setkey.c 1.11 94/04/25 SMI";
+#endif
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+/*
+ * Copyright (c) 1986 - 1991 by Sun Microsystems, Inc.
+ */
+
+/*
+ * Do the real work of the keyserver.
+ * Store secret keys. Compute common keys,
+ * and use them to decrypt and encrypt DES keys.
+ * Cache the common keys, so the expensive computation is avoided.
+ */
+#include <mp.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <rpc/rpc.h>
+#include <rpc/key_prot.h>
+#include <rpc/des_crypt.h>
+#include <rpc/des.h>
+#include <sys/errno.h>
+#include "keyserv.h"
+
+static MINT *MODULUS;
+static char *fetchsecretkey __P(( uid_t ));
+static void writecache __P(( char *, char *, des_block * ));
+static int readcache __P(( char *, char *, des_block * ));
+static void extractdeskey __P (( MINT *, des_block * ));
+static int storesecretkey __P(( uid_t, keybuf ));
+static keystatus pk_crypt __P(( uid_t, char *, netobj *, des_block *, int));
+static int nodefaultkeys = 0;
+
+
+/*
+ * prohibit the nobody key on this machine k (the -d flag)
+ */
+void
+pk_nodefaultkeys()
+{
+ nodefaultkeys = 1;
+}
+
+/*
+ * Set the modulus for all our Diffie-Hellman operations
+ */
+void
+setmodulus(modx)
+ char *modx;
+{
+ MODULUS = xtom(modx);
+}
+
+/*
+ * Set the secretkey key for this uid
+ */
+keystatus
+pk_setkey(uid, skey)
+ uid_t uid;
+ keybuf skey;
+{
+ if (!storesecretkey(uid, skey)) {
+ return (KEY_SYSTEMERR);
+ }
+ return (KEY_SUCCESS);
+}
+
+/*
+ * Encrypt the key using the public key associated with remote_name and the
+ * secret key associated with uid.
+ */
+keystatus
+pk_encrypt(uid, remote_name, remote_key, key)
+ uid_t uid;
+ char *remote_name;
+ netobj *remote_key;
+ des_block *key;
+{
+ return (pk_crypt(uid, remote_name, remote_key, key, DES_ENCRYPT));
+}
+
+/*
+ * Decrypt the key using the public key associated with remote_name and the
+ * secret key associated with uid.
+ */
+keystatus
+pk_decrypt(uid, remote_name, remote_key, key)
+ uid_t uid;
+ char *remote_name;
+ netobj *remote_key;
+ des_block *key;
+{
+ return (pk_crypt(uid, remote_name, remote_key, key, DES_DECRYPT));
+}
+
+static int store_netname __P(( uid_t, key_netstarg * ));
+static int fetch_netname __P(( uid_t, key_netstarg * ));
+
+keystatus
+pk_netput(uid, netstore)
+ uid_t uid;
+ key_netstarg *netstore;
+{
+ if (!store_netname(uid, netstore)) {
+ return (KEY_SYSTEMERR);
+ }
+ return (KEY_SUCCESS);
+}
+
+keystatus
+pk_netget(uid, netstore)
+ uid_t uid;
+ key_netstarg *netstore;
+{
+ if (!fetch_netname(uid, netstore)) {
+ return (KEY_SYSTEMERR);
+ }
+ return (KEY_SUCCESS);
+}
+
+
+/*
+ * Do the work of pk_encrypt && pk_decrypt
+ */
+static keystatus
+pk_crypt(uid, remote_name, remote_key, key, mode)
+ uid_t uid;
+ char *remote_name;
+ netobj *remote_key;
+ des_block *key;
+ int mode;
+{
+ char *xsecret;
+ char xpublic[1024];
+ char xsecret_hold[1024];
+ des_block deskey;
+ int err;
+ MINT *public;
+ MINT *secret;
+ MINT *common;
+ char zero[8];
+
+ xsecret = fetchsecretkey(uid);
+ if (xsecret == NULL || xsecret[0] == 0) {
+ memset(zero, 0, sizeof (zero));
+ xsecret = xsecret_hold;
+ if (nodefaultkeys)
+ return (KEY_NOSECRET);
+
+ if (!getsecretkey("nobody", xsecret, zero) || xsecret[0] == 0) {
+ return (KEY_NOSECRET);
+ }
+ }
+ if (remote_key) {
+ memcpy(xpublic, remote_key->n_bytes, remote_key->n_len);
+ } else {
+ bzero((char *)&xpublic, sizeof(xpublic));
+ if (!getpublickey(remote_name, xpublic)) {
+ if (nodefaultkeys || !getpublickey("nobody", xpublic))
+ return (KEY_UNKNOWN);
+ }
+ }
+
+ if (!readcache(xpublic, xsecret, &deskey)) {
+ public = xtom(xpublic);
+ secret = xtom(xsecret);
+ /* Sanity Check on public and private keys */
+ if ((public == NULL) || (secret == NULL))
+ return (KEY_SYSTEMERR);
+
+ common = itom(0);
+ pow(public, secret, MODULUS, common);
+ extractdeskey(common, &deskey);
+ writecache(xpublic, xsecret, &deskey);
+ mfree(secret);
+ mfree(public);
+ mfree(common);
+ }
+ err = ecb_crypt((char *)&deskey, (char *)key, sizeof (des_block),
+ DES_HW | mode);
+ if (DES_FAILED(err)) {
+ return (KEY_SYSTEMERR);
+ }
+ return (KEY_SUCCESS);
+}
+
+keystatus
+pk_get_conv_key(uid, xpublic, result)
+ uid_t uid;
+ keybuf xpublic;
+ cryptkeyres *result;
+{
+ char *xsecret;
+ char xsecret_hold[1024];
+ MINT *public;
+ MINT *secret;
+ MINT *common;
+ char zero[8];
+
+
+ xsecret = fetchsecretkey(uid);
+
+ if (xsecret == NULL || xsecret[0] == 0) {
+ memset(zero, 0, sizeof (zero));
+ xsecret = xsecret_hold;
+ if (nodefaultkeys)
+ return (KEY_NOSECRET);
+
+ if (!getsecretkey("nobody", xsecret, zero) ||
+ xsecret[0] == 0)
+ return (KEY_NOSECRET);
+ }
+
+ if (!readcache(xpublic, xsecret, &result->cryptkeyres_u.deskey)) {
+ public = xtom(xpublic);
+ secret = xtom(xsecret);
+ /* Sanity Check on public and private keys */
+ if ((public == NULL) || (secret == NULL))
+ return (KEY_SYSTEMERR);
+
+ common = itom(0);
+ pow(public, secret, MODULUS, common);
+ extractdeskey(common, &result->cryptkeyres_u.deskey);
+ writecache(xpublic, xsecret, &result->cryptkeyres_u.deskey);
+ mfree(secret);
+ mfree(public);
+ mfree(common);
+ }
+
+ return (KEY_SUCCESS);
+}
+
+/*
+ * Choose middle 64 bits of the common key to use as our des key, possibly
+ * overwriting the lower order bits by setting parity.
+ */
+static void
+extractdeskey(ck, deskey)
+ MINT *ck;
+ des_block *deskey;
+{
+ MINT *a;
+ short r;
+ int i;
+ short base = (1 << 8);
+ char *k;
+
+ a = itom(0);
+#ifdef SOLARIS_MP
+ _mp_move(ck, a);
+#else
+ move(ck, a);
+#endif
+ for (i = 0; i < ((KEYSIZE - 64) / 2) / 8; i++) {
+ sdiv(a, base, a, &r);
+ }
+ k = deskey->c;
+ for (i = 0; i < 8; i++) {
+ sdiv(a, base, a, &r);
+ *k++ = r;
+ }
+ mfree(a);
+ des_setparity((char *)deskey);
+}
+
+/*
+ * Key storage management
+ */
+
+#define KEY_ONLY 0
+#define KEY_NAME 1
+struct secretkey_netname_list {
+ uid_t uid;
+ key_netstarg keynetdata;
+ u_char sc_flag;
+ struct secretkey_netname_list *next;
+};
+
+
+
+static struct secretkey_netname_list *g_secretkey_netname;
+
+/*
+ * Store the keys and netname for this uid
+ */
+static int
+store_netname(uid, netstore)
+ uid_t uid;
+ key_netstarg *netstore;
+{
+ struct secretkey_netname_list *new;
+ struct secretkey_netname_list **l;
+
+ for (l = &g_secretkey_netname; *l != NULL && (*l)->uid != uid;
+ l = &(*l)->next) {
+ }
+ if (*l == NULL) {
+ new = (struct secretkey_netname_list *)malloc(sizeof (*new));
+ if (new == NULL) {
+ return (0);
+ }
+ new->uid = uid;
+ new->next = NULL;
+ *l = new;
+ } else {
+ new = *l;
+ if (new->keynetdata.st_netname)
+ (void) free (new->keynetdata.st_netname);
+ }
+ memcpy(new->keynetdata.st_priv_key, netstore->st_priv_key,
+ HEXKEYBYTES);
+ memcpy(new->keynetdata.st_pub_key, netstore->st_pub_key, HEXKEYBYTES);
+
+ if (netstore->st_netname)
+ new->keynetdata.st_netname = strdup(netstore->st_netname);
+ else
+ new->keynetdata.st_netname = (char *)NULL;
+ new->sc_flag = KEY_NAME;
+ return (1);
+
+}
+
+/*
+ * Fetch the keys and netname for this uid
+ */
+
+static int
+fetch_netname(uid, key_netst)
+ uid_t uid;
+ struct key_netstarg *key_netst;
+{
+ struct secretkey_netname_list *l;
+
+ for (l = g_secretkey_netname; l != NULL; l = l->next) {
+ if ((l->uid == uid) && (l->sc_flag == KEY_NAME)){
+
+ memcpy(key_netst->st_priv_key,
+ l->keynetdata.st_priv_key, HEXKEYBYTES);
+
+ memcpy(key_netst->st_pub_key,
+ l->keynetdata.st_pub_key, HEXKEYBYTES);
+
+ if (l->keynetdata.st_netname)
+ key_netst->st_netname =
+ strdup(l->keynetdata.st_netname);
+ else
+ key_netst->st_netname = NULL;
+ return (1);
+ }
+ }
+
+ return (0);
+}
+
+static char *
+fetchsecretkey(uid)
+ uid_t uid;
+{
+ struct secretkey_netname_list *l;
+
+ for (l = g_secretkey_netname; l != NULL; l = l->next) {
+ if (l->uid == uid) {
+ return (l->keynetdata.st_priv_key);
+ }
+ }
+ return (NULL);
+}
+
+/*
+ * Store the secretkey for this uid
+ */
+static int
+storesecretkey(uid, key)
+ uid_t uid;
+ keybuf key;
+{
+ struct secretkey_netname_list *new;
+ struct secretkey_netname_list **l;
+
+ for (l = &g_secretkey_netname; *l != NULL && (*l)->uid != uid;
+ l = &(*l)->next) {
+ }
+ if (*l == NULL) {
+ new = (struct secretkey_netname_list *) malloc(sizeof (*new));
+ if (new == NULL) {
+ return (0);
+ }
+ new->uid = uid;
+ new->sc_flag = KEY_ONLY;
+ memset(new->keynetdata.st_pub_key, 0, HEXKEYBYTES);
+ new->keynetdata.st_netname = NULL;
+ new->next = NULL;
+ *l = new;
+ } else {
+ new = *l;
+ }
+
+ memcpy(new->keynetdata.st_priv_key, key,
+ HEXKEYBYTES);
+ return (1);
+}
+
+static int
+hexdigit(val)
+ int val;
+{
+ return ("0123456789abcdef"[val]);
+}
+
+void
+bin2hex(bin, hex, size)
+ unsigned char *bin;
+ unsigned char *hex;
+ int size;
+{
+ int i;
+
+ for (i = 0; i < size; i++) {
+ *hex++ = hexdigit(*bin >> 4);
+ *hex++ = hexdigit(*bin++ & 0xf);
+ }
+}
+
+static int
+hexval(dig)
+ char dig;
+{
+ if ('0' <= dig && dig <= '9') {
+ return (dig - '0');
+ } else if ('a' <= dig && dig <= 'f') {
+ return (dig - 'a' + 10);
+ } else if ('A' <= dig && dig <= 'F') {
+ return (dig - 'A' + 10);
+ } else {
+ return (-1);
+ }
+}
+
+void
+hex2bin(hex, bin, size)
+ unsigned char *hex;
+ unsigned char *bin;
+ int size;
+{
+ int i;
+
+ for (i = 0; i < size; i++) {
+ *bin = hexval(*hex++) << 4;
+ *bin++ |= hexval(*hex++);
+ }
+}
+
+/*
+ * Exponential caching management
+ */
+struct cachekey_list {
+ keybuf secret;
+ keybuf public;
+ des_block deskey;
+ struct cachekey_list *next;
+};
+static struct cachekey_list *g_cachedkeys;
+
+/*
+ * cache result of expensive multiple precision exponential operation
+ */
+static void
+writecache(pub, sec, deskey)
+ char *pub;
+ char *sec;
+ des_block *deskey;
+{
+ struct cachekey_list *new;
+
+ new = (struct cachekey_list *) malloc(sizeof (struct cachekey_list));
+ if (new == NULL) {
+ return;
+ }
+ memcpy(new->public, pub, sizeof (keybuf));
+ memcpy(new->secret, sec, sizeof (keybuf));
+ new->deskey = *deskey;
+ new->next = g_cachedkeys;
+ g_cachedkeys = new;
+}
+
+/*
+ * Try to find the common key in the cache
+ */
+static int
+readcache(pub, sec, deskey)
+ char *pub;
+ char *sec;
+ des_block *deskey;
+{
+ struct cachekey_list *found;
+ register struct cachekey_list **l;
+
+#define cachehit(pub, sec, list) \
+ (memcmp(pub, (list)->public, sizeof (keybuf)) == 0 && \
+ memcmp(sec, (list)->secret, sizeof (keybuf)) == 0)
+
+ for (l = &g_cachedkeys; (*l) != NULL && !cachehit(pub, sec, *l);
+ l = &(*l)->next)
+ ;
+ if ((*l) == NULL) {
+ return (0);
+ }
+ found = *l;
+ (*l) = (*l)->next;
+ found->next = g_cachedkeys;
+ g_cachedkeys = found;
+ *deskey = found->deskey;
+ return (1);
+}
diff --git a/usr.sbin/kgmon/Makefile b/usr.sbin/kgmon/Makefile
new file mode 100644
index 0000000..20fcf68
--- /dev/null
+++ b/usr.sbin/kgmon/Makefile
@@ -0,0 +1,14 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= kgmon
+MAN8= kgmon.8
+DPADD= ${LIBKVM}
+LDADD= -lkvm
+#
+# This program may safely be run setuid-root to allow non-root
+# users to start, stop, and reset profiling buffers.
+#
+#BINOWN=root
+#BINMODE=4555
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/kgmon/kgmon.8 b/usr.sbin/kgmon/kgmon.8
new file mode 100644
index 0000000..0ec962d
--- /dev/null
+++ b/usr.sbin/kgmon/kgmon.8
@@ -0,0 +1,129 @@
+.\" Copyright (c) 1983, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)kgmon.8 8.1 (Berkeley) 6/6/93
+.\"
+.Dd June 6, 1993
+.Dt KGMON 8
+.Os BSD 4.2
+.Sh NAME
+.Nm kgmon
+.Nd generate a dump of the operating system's profile buffers
+.Sh SYNOPSIS
+.Nm kgmon
+.Op Fl Bbhpr
+.Op Fl M core
+.Op Fl N system
+.Sh DESCRIPTION
+.Nm Kgmon
+is a tool used when profiling the operating system.
+When no arguments are supplied,
+.Nm
+indicates the state of operating system profiling as running,
+off, or not configured.
+(see
+.Xr config 8 )
+If the
+.Fl p
+flag is specified,
+.Nm
+extracts profile data from the operating system and produces a
+.Pa gmon.out
+file suitable for later analysis by
+.Xr gprof 1 .
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl B
+Resume the collection of high resolution profile data.
+.It Fl b
+Resume the collection of low resolution profile data.
+.It Fl h
+Stop the collection of profile data.
+.It Fl p
+Dump the contents of the profile buffers into a
+.Pa gmon.out
+file.
+.It Fl r
+Reset all the profile buffers.
+If the
+.Fl p
+flag is also specified, the
+.Pa gmon.out
+file is generated before the buffers are reset.
+.It Fl M
+Extract values associated with the name list from the specified core
+instead of the default ``/dev/kmem''.
+.It Fl N
+Extract the name list from the specified system instead of the
+default ``/kernel''.
+.El
+.Pp
+If neither
+.Fl B
+nor
+.Fl b
+nor
+.Fl h
+is specified, the state of profiling collection remains unchanged.
+For example, if the
+.Fl p
+flag is specified and profile data is being collected,
+profiling will be momentarily suspended,
+the operating system profile buffers will be dumped,
+and profiling will be immediately resumed.
+.Pp
+The profile buffers should be reset when the resolution
+of the profile data is changed.
+.Sh FILES
+.Bl -tag -width /dev/kmemx -compact
+.It Pa /kernel
+the default system
+.It Pa /dev/kmem
+the default memory
+.El
+.Sh SEE ALSO
+.Xr gprof 1 ,
+.Xr config 8
+.Sh DIAGNOSTICS
+Users with only read permission on
+.Pa /dev/kmem
+cannot change the state
+of profiling collection.
+They can get a
+.Pa gmon.out
+file with the warning that the data may be
+inconsistent if profiling is in progress.
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 4.2 .
diff --git a/usr.sbin/kgmon/kgmon.c b/usr.sbin/kgmon/kgmon.c
new file mode 100644
index 0000000..4d2bd4f
--- /dev/null
+++ b/usr.sbin/kgmon/kgmon.c
@@ -0,0 +1,505 @@
+/*
+ * Copyright (c) 1983, 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 1983, 1992, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)kgmon.c 8.1 (Berkeley) 6/6/93";
+#endif
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/file.h>
+#include <sys/time.h>
+#include <sys/sysctl.h>
+#include <sys/gmon.h>
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <kvm.h>
+#include <limits.h>
+#include <nlist.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+struct nlist nl[] = {
+#define N_GMONPARAM 0
+ { "__gmonparam" },
+#define N_PROFHZ 1
+ { "_profhz" },
+ { NULL },
+};
+
+struct kvmvars {
+ kvm_t *kd;
+ struct gmonparam gpm;
+};
+
+int Bflag, bflag, hflag, kflag, rflag, pflag;
+int debug = 0;
+int getprof __P((struct kvmvars *));
+int getprofhz __P((struct kvmvars *));
+void kern_readonly __P((int));
+int openfiles __P((char *, char *, struct kvmvars *));
+void setprof __P((struct kvmvars *kvp, int state));
+void dumpstate __P((struct kvmvars *kvp));
+void reset __P((struct kvmvars *kvp));
+static void usage __P((void));
+
+int
+main(int argc, char **argv)
+{
+ int ch, mode, disp, accessmode;
+ struct kvmvars kvmvars;
+ char *system, *kmemf;
+
+ seteuid(getuid());
+ kmemf = NULL;
+ system = NULL;
+ while ((ch = getopt(argc, argv, "M:N:Bbhpr")) != -1) {
+ switch((char)ch) {
+
+ case 'M':
+ kmemf = optarg;
+ kflag = 1;
+ break;
+
+ case 'N':
+ system = optarg;
+ break;
+
+ case 'B':
+ Bflag = 1;
+ break;
+
+ case 'b':
+ bflag = 1;
+ break;
+
+ case 'h':
+ hflag = 1;
+ break;
+
+ case 'p':
+ pflag = 1;
+ break;
+
+ case 'r':
+ rflag = 1;
+ break;
+
+ default:
+ usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+#define BACKWARD_COMPATIBILITY
+#ifdef BACKWARD_COMPATIBILITY
+ if (*argv) {
+ system = *argv;
+ if (*++argv) {
+ kmemf = *argv;
+ ++kflag;
+ }
+ }
+#endif
+ if (system == NULL)
+ system = (char *)getbootfile();
+ accessmode = openfiles(system, kmemf, &kvmvars);
+ mode = getprof(&kvmvars);
+ if (hflag)
+ disp = GMON_PROF_OFF;
+ else if (Bflag)
+ disp = GMON_PROF_HIRES;
+ else if (bflag)
+ disp = GMON_PROF_ON;
+ else
+ disp = mode;
+ if (pflag)
+ dumpstate(&kvmvars);
+ if (rflag)
+ reset(&kvmvars);
+ if (accessmode == O_RDWR)
+ setprof(&kvmvars, disp);
+ (void)fprintf(stdout, "kgmon: kernel profiling is %s.\n",
+ disp == GMON_PROF_OFF ? "off" :
+ disp == GMON_PROF_HIRES ? "running (high resolution)" :
+ disp == GMON_PROF_ON ? "running" :
+ disp == GMON_PROF_BUSY ? "busy" :
+ disp == GMON_PROF_ERROR ? "off (error)" :
+ "in an unknown state");
+ return (0);
+}
+
+static void
+usage()
+{
+ fprintf(stderr, "usage: kgmon [-Bbhrp] [-M core] [-N system]\n");
+ exit(1);
+}
+
+/*
+ * Check that profiling is enabled and open any ncessary files.
+ */
+int
+openfiles(system, kmemf, kvp)
+ char *system;
+ char *kmemf;
+ struct kvmvars *kvp;
+{
+ int mib[3], state, size, openmode;
+ char errbuf[_POSIX2_LINE_MAX];
+
+ if (!kflag) {
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_PROF;
+ mib[2] = GPROF_STATE;
+ size = sizeof state;
+ if (sysctl(mib, 3, &state, &size, NULL, 0) < 0)
+ errx(20, "profiling not defined in kernel");
+ if (!(Bflag || bflag || hflag || rflag ||
+ (pflag &&
+ (state == GMON_PROF_HIRES || state == GMON_PROF_ON))))
+ return (O_RDONLY);
+ (void)seteuid(0);
+ if (sysctl(mib, 3, NULL, NULL, &state, size) >= 0)
+ return (O_RDWR);
+ (void)seteuid(getuid());
+ kern_readonly(state);
+ return (O_RDONLY);
+ }
+ openmode = (Bflag || bflag || hflag || pflag || rflag)
+ ? O_RDWR : O_RDONLY;
+ kvp->kd = kvm_openfiles(system, kmemf, NULL, openmode, errbuf);
+ if (kvp->kd == NULL) {
+ if (openmode == O_RDWR) {
+ openmode = O_RDONLY;
+ kvp->kd = kvm_openfiles(system, kmemf, NULL, O_RDONLY,
+ errbuf);
+ }
+ if (kvp->kd == NULL)
+ errx(2, "kvm_openfiles: %s", errbuf);
+ kern_readonly(GMON_PROF_ON);
+ }
+ if (kvm_nlist(kvp->kd, nl) < 0)
+ errx(3, "%s: no namelist", system);
+ if (!nl[N_GMONPARAM].n_value)
+ errx(20, "profiling not defined in kernel");
+ return (openmode);
+}
+
+/*
+ * Suppress options that require a writable kernel.
+ */
+void
+kern_readonly(mode)
+ int mode;
+{
+
+ (void)fprintf(stderr, "kgmon: kernel read-only: ");
+ if (pflag && (mode == GMON_PROF_HIRES || mode == GMON_PROF_ON))
+ (void)fprintf(stderr, "data may be inconsistent\n");
+ if (rflag)
+ (void)fprintf(stderr, "-r supressed\n");
+ if (Bflag)
+ (void)fprintf(stderr, "-B supressed\n");
+ if (bflag)
+ (void)fprintf(stderr, "-b supressed\n");
+ if (hflag)
+ (void)fprintf(stderr, "-h supressed\n");
+ rflag = Bflag = bflag = hflag = 0;
+}
+
+/*
+ * Get the state of kernel profiling.
+ */
+int
+getprof(kvp)
+ struct kvmvars *kvp;
+{
+ int mib[3], size;
+
+ if (kflag) {
+ size = kvm_read(kvp->kd, nl[N_GMONPARAM].n_value, &kvp->gpm,
+ sizeof kvp->gpm);
+ } else {
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_PROF;
+ mib[2] = GPROF_GMONPARAM;
+ size = sizeof kvp->gpm;
+ if (sysctl(mib, 3, &kvp->gpm, &size, NULL, 0) < 0)
+ size = 0;
+ }
+ if (size != sizeof kvp->gpm)
+ errx(4, "cannot get gmonparam: %s",
+ kflag ? kvm_geterr(kvp->kd) : strerror(errno));
+ return (kvp->gpm.state);
+}
+
+/*
+ * Enable or disable kernel profiling according to the state variable.
+ */
+void
+setprof(kvp, state)
+ struct kvmvars *kvp;
+ int state;
+{
+ struct gmonparam *p = (struct gmonparam *)nl[N_GMONPARAM].n_value;
+ int mib[3], sz, oldstate;
+
+ sz = sizeof(state);
+ if (!kflag) {
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_PROF;
+ mib[2] = GPROF_STATE;
+ if (sysctl(mib, 3, &oldstate, &sz, NULL, 0) < 0)
+ goto bad;
+ if (oldstate == state)
+ return;
+ (void)seteuid(0);
+ if (sysctl(mib, 3, NULL, NULL, &state, sz) >= 0) {
+ (void)seteuid(getuid());
+ return;
+ }
+ (void)seteuid(getuid());
+ } else if (kvm_write(kvp->kd, (u_long)&p->state, (void *)&state, sz)
+ == sz)
+ return;
+bad:
+ warnx("warning: cannot turn profiling %s",
+ state == GMON_PROF_OFF ? "off" : "on");
+}
+
+/*
+ * Build the gmon.out file.
+ */
+void
+dumpstate(kvp)
+ struct kvmvars *kvp;
+{
+ register FILE *fp;
+ struct rawarc rawarc;
+ struct tostruct *tos;
+ u_long frompc;
+ u_short *froms, *tickbuf;
+ int mib[3], i;
+ struct gmonhdr h;
+ int fromindex, endfrom, toindex;
+
+ setprof(kvp, GMON_PROF_OFF);
+ fp = fopen("gmon.out", "w");
+ if (fp == 0) {
+ warn("gmon.out");
+ return;
+ }
+
+ /*
+ * Build the gmon header and write it to a file.
+ */
+ bzero(&h, sizeof(h));
+ h.lpc = kvp->gpm.lowpc;
+ h.hpc = kvp->gpm.highpc;
+ h.ncnt = kvp->gpm.kcountsize + sizeof(h);
+ h.version = GMONVERSION;
+ h.profrate = kvp->gpm.profrate;
+ if (h.profrate == 0)
+ h.profrate = getprofhz(kvp); /* ancient kernel */
+ fwrite((char *)&h, sizeof(h), 1, fp);
+
+ /*
+ * Write out the tick buffer.
+ */
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_PROF;
+ if ((tickbuf = (u_short *)malloc(kvp->gpm.kcountsize)) == NULL)
+ errx(5, "cannot allocate kcount space");
+ if (kflag) {
+ i = kvm_read(kvp->kd, (u_long)kvp->gpm.kcount, (void *)tickbuf,
+ kvp->gpm.kcountsize);
+ } else {
+ mib[2] = GPROF_COUNT;
+ i = kvp->gpm.kcountsize;
+ if (sysctl(mib, 3, tickbuf, &i, NULL, 0) < 0)
+ i = 0;
+ }
+ if (i != kvp->gpm.kcountsize)
+ errx(6, "read ticks: read %u, got %d: %s",
+ kvp->gpm.kcountsize, i,
+ kflag ? kvm_geterr(kvp->kd) : strerror(errno));
+ if ((fwrite(tickbuf, kvp->gpm.kcountsize, 1, fp)) != 1)
+ err(7, "writing tocks to gmon.out");
+ free(tickbuf);
+
+ /*
+ * Write out the arc info.
+ */
+ if ((froms = (u_short *)malloc(kvp->gpm.fromssize)) == NULL)
+ errx(8, "cannot allocate froms space");
+ if (kflag) {
+ i = kvm_read(kvp->kd, (u_long)kvp->gpm.froms, (void *)froms,
+ kvp->gpm.fromssize);
+ } else {
+ mib[2] = GPROF_FROMS;
+ i = kvp->gpm.fromssize;
+ if (sysctl(mib, 3, froms, &i, NULL, 0) < 0)
+ i = 0;
+ }
+ if (i != kvp->gpm.fromssize)
+ errx(9, "read froms: read %u, got %d: %s",
+ kvp->gpm.fromssize, i,
+ kflag ? kvm_geterr(kvp->kd) : strerror(errno));
+ if ((tos = (struct tostruct *)malloc(kvp->gpm.tossize)) == NULL)
+ errx(10, "cannot allocate tos space");
+ if (kflag) {
+ i = kvm_read(kvp->kd, (u_long)kvp->gpm.tos, (void *)tos,
+ kvp->gpm.tossize);
+ } else {
+ mib[2] = GPROF_TOS;
+ i = kvp->gpm.tossize;
+ if (sysctl(mib, 3, tos, &i, NULL, 0) < 0)
+ i = 0;
+ }
+ if (i != kvp->gpm.tossize)
+ errx(11, "read tos: read %u, got %d: %s",
+ kvp->gpm.tossize, i,
+ kflag ? kvm_geterr(kvp->kd) : strerror(errno));
+ if (debug)
+ warnx("lowpc 0x%x, textsize 0x%x",
+ kvp->gpm.lowpc, kvp->gpm.textsize);
+ endfrom = kvp->gpm.fromssize / sizeof(*froms);
+ for (fromindex = 0; fromindex < endfrom; ++fromindex) {
+ if (froms[fromindex] == 0)
+ continue;
+ frompc = (u_long)kvp->gpm.lowpc +
+ (fromindex * kvp->gpm.hashfraction * sizeof(*froms));
+ for (toindex = froms[fromindex]; toindex != 0;
+ toindex = tos[toindex].link) {
+ if (debug)
+ warnx("[mcleanup] frompc 0x%x selfpc 0x%x count %d",
+ frompc, tos[toindex].selfpc,
+ tos[toindex].count);
+ rawarc.raw_frompc = frompc;
+ rawarc.raw_selfpc = (u_long)tos[toindex].selfpc;
+ rawarc.raw_count = tos[toindex].count;
+ fwrite((char *)&rawarc, sizeof(rawarc), 1, fp);
+ }
+ }
+ fclose(fp);
+}
+
+/*
+ * Get the profiling rate.
+ */
+int
+getprofhz(kvp)
+ struct kvmvars *kvp;
+{
+ int mib[2], size, profrate;
+ struct clockinfo clockrate;
+
+ if (kflag) {
+ profrate = 1;
+ if (kvm_read(kvp->kd, nl[N_PROFHZ].n_value, &profrate,
+ sizeof profrate) != sizeof profrate)
+ warnx("get clockrate: %s", kvm_geterr(kvp->kd));
+ return (profrate);
+ }
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_CLOCKRATE;
+ clockrate.profhz = 1;
+ size = sizeof clockrate;
+ if (sysctl(mib, 2, &clockrate, &size, NULL, 0) < 0)
+ warn("get clockrate");
+ return (clockrate.profhz);
+}
+
+/*
+ * Reset the kernel profiling date structures.
+ */
+void
+reset(kvp)
+ struct kvmvars *kvp;
+{
+ char *zbuf;
+ u_long biggest;
+ int mib[3];
+
+ setprof(kvp, GMON_PROF_OFF);
+
+ biggest = kvp->gpm.kcountsize;
+ if (kvp->gpm.fromssize > biggest)
+ biggest = kvp->gpm.fromssize;
+ if (kvp->gpm.tossize > biggest)
+ biggest = kvp->gpm.tossize;
+ if ((zbuf = (char *)malloc(biggest)) == NULL)
+ errx(12, "cannot allocate zbuf space");
+ bzero(zbuf, biggest);
+ if (kflag) {
+ if (kvm_write(kvp->kd, (u_long)kvp->gpm.kcount, zbuf,
+ kvp->gpm.kcountsize) != kvp->gpm.kcountsize)
+ errx(13, "tickbuf zero: %s", kvm_geterr(kvp->kd));
+ if (kvm_write(kvp->kd, (u_long)kvp->gpm.froms, zbuf,
+ kvp->gpm.fromssize) != kvp->gpm.fromssize)
+ errx(14, "froms zero: %s", kvm_geterr(kvp->kd));
+ if (kvm_write(kvp->kd, (u_long)kvp->gpm.tos, zbuf,
+ kvp->gpm.tossize) != kvp->gpm.tossize)
+ errx(15, "tos zero: %s", kvm_geterr(kvp->kd));
+ return;
+ }
+ (void)seteuid(0);
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_PROF;
+ mib[2] = GPROF_COUNT;
+ if (sysctl(mib, 3, NULL, NULL, zbuf, kvp->gpm.kcountsize) < 0)
+ err(13, "tickbuf zero");
+ mib[2] = GPROF_FROMS;
+ if (sysctl(mib, 3, NULL, NULL, zbuf, kvp->gpm.fromssize) < 0)
+ err(14, "froms zero");
+ mib[2] = GPROF_TOS;
+ if (sysctl(mib, 3, NULL, NULL, zbuf, kvp->gpm.tossize) < 0)
+ err(15, "tos zero");
+ (void)seteuid(getuid());
+ free(zbuf);
+}
diff --git a/usr.sbin/kvm_mkdb/Makefile b/usr.sbin/kvm_mkdb/Makefile
new file mode 100644
index 0000000..d3a6c49
--- /dev/null
+++ b/usr.sbin/kvm_mkdb/Makefile
@@ -0,0 +1,11 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= kvm_mkdb
+SRCS= kvm_mkdb.c nlist.c testdb.c
+MAN8= kvm_mkdb.8
+CFLAGS+=-DDO_AOUT
+.if defined(BINFORMAT) && ${BINFORMAT} == elf
+CFLAGS+=-DDO_ELF
+.endif
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/kvm_mkdb/extern.h b/usr.sbin/kvm_mkdb/extern.h
new file mode 100644
index 0000000..6d72268
--- /dev/null
+++ b/usr.sbin/kvm_mkdb/extern.h
@@ -0,0 +1,38 @@
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)extern.h 8.1 (Berkeley) 6/6/93
+ */
+
+void create_knlist __P((char *, DB *));
+void error __P((char *));
+int testdb __P(());
diff --git a/usr.sbin/kvm_mkdb/kvm_mkdb.8 b/usr.sbin/kvm_mkdb/kvm_mkdb.8
new file mode 100644
index 0000000..0bfcbea
--- /dev/null
+++ b/usr.sbin/kvm_mkdb/kvm_mkdb.8
@@ -0,0 +1,70 @@
+.\" Copyright (c) 1989, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)kvm_mkdb.8 8.1 (Berkeley) 6/9/93
+.\"
+.Dd June 9, 1993
+.Dt KVM_MKDB 8
+.Os
+.Sh NAME
+.Nm kvm_mkdb
+.Nd create kernel database
+.Sh SYNOPSIS
+.Nm kvm_mkdb
+.Op file
+.Sh DESCRIPTION
+.Nm Kvm_mkdb
+creates a database in
+.Pa /var/db
+containing information about the specified file.
+If no file is specified,
+.Pa /kernel
+is used by default.
+The file is named ``kvm_filename.db'', where ``filename'' is the
+name of the file read.
+Various library routines consult this database.
+The only information currently stored is the kernel namelist, which is
+used by the
+.Xr kvm_nlist 3
+function, however, in the future the database may contain other static
+information about the current system.
+.Sh FILES
+.Bl -tag -width /var/db/kvm_kernel.db -compact
+.It Pa /kernel
+.It Pa /var/db/kvm_kernel.db
+.El
+.Sh SEE ALSO
+.Xr kvm_nlist 3
+.Sh HISTORY
+The
+.Nm
+utility first appeared in
+.Bx 4.4 .
diff --git a/usr.sbin/kvm_mkdb/kvm_mkdb.c b/usr.sbin/kvm_mkdb/kvm_mkdb.c
new file mode 100644
index 0000000..0284c55
--- /dev/null
+++ b/usr.sbin/kvm_mkdb/kvm_mkdb.c
@@ -0,0 +1,126 @@
+/*-
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 1990, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)kvm_mkdb.c 8.3 (Berkeley) 5/4/95";
+#endif
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include <db.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "extern.h"
+
+static void usage __P((void));
+
+HASHINFO openinfo = {
+ 4096, /* bsize */
+ 128, /* ffactor */
+ 1024, /* nelem */
+ 2048 * 1024, /* cachesize */
+ NULL, /* hash() */
+ 0 /* lorder */
+};
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ DB *db;
+ int ch;
+ char *p, *nlistpath, *nlistname, dbtemp[MAXPATHLEN], dbname[MAXPATHLEN];
+
+ while ((ch = getopt(argc, argv, "")) != -1)
+ switch (ch) {
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc > 1)
+ usage();
+
+ /* If the existing db file matches the currently running kernel, exit */
+ if (testdb())
+ exit(0);
+
+#define basename(cp) ((p = rindex((cp), '/')) != NULL ? p + 1 : (cp))
+ nlistpath = argc > 0 ? argv[0] : (char *)getbootfile();
+ nlistname = basename(nlistpath);
+
+ (void)snprintf(dbtemp, sizeof(dbtemp), "%skvm_%s.tmp",
+ _PATH_VARDB, nlistname);
+ (void)snprintf(dbname, sizeof(dbname), "%skvm_%s.db",
+ _PATH_VARDB, nlistname);
+ (void)umask(0);
+
+ db = dbopen(dbtemp, O_CREAT | O_EXLOCK | O_TRUNC | O_RDWR,
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH, DB_HASH, &openinfo);
+ if (db == NULL)
+ err(1, "%s", dbtemp);
+ create_knlist(nlistpath, db);
+ if (db->close(db))
+ err(1, "%s", dbtemp);
+ if (rename(dbtemp, dbname))
+ err(1, "rename %s to %s", dbtemp, dbname);
+ exit(0);
+}
+
+static void
+usage()
+{
+ (void)fprintf(stderr, "usage: kvm_mkdb [file]\n");
+ exit(1);
+}
diff --git a/usr.sbin/kvm_mkdb/nlist.c b/usr.sbin/kvm_mkdb/nlist.c
new file mode 100644
index 0000000..954742a
--- /dev/null
+++ b/usr.sbin/kvm_mkdb/nlist.c
@@ -0,0 +1,338 @@
+/*-
+ * 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[] = "@(#)from: nlist.c 8.1 (Berkeley) 6/6/93";
+#endif
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+
+#include <vm/vm.h>
+#include <vm/vm_param.h>
+
+#include <a.out.h>
+#include <db.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <kvm.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "extern.h"
+
+#ifdef DO_ELF
+#include <elf.h>
+#endif
+
+typedef struct nlist NLIST;
+#define _strx n_un.n_strx
+#define _name n_un.n_name
+
+#define badfmt(str) errx(1, "%s: %s: %s", kfile, str, strerror(EFTYPE))
+static char *kfile;
+
+#if defined(DO_AOUT)
+
+int
+__aout_knlist(name, db)
+ char *name;
+ DB *db;
+{
+ register int nsyms;
+ struct exec *ebuf;
+ NLIST *nbuf;
+ DBT data, key;
+ int fd;
+ char *strtab;
+ u_char *filep;
+ char *vp;
+ struct stat sst;
+ long cur_off, voff;
+
+ kfile = name;
+ if ((fd = open(name, O_RDONLY, 0)) < 0)
+ err(1, "%s", name);
+
+ fstat(fd,&sst);
+
+ filep = (u_char*)mmap(0, sst.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+
+ if (filep == (u_char*)MAP_FAILED)
+ err(1, "mmap failed");
+
+ /* Read in exec structure. */
+ ebuf = (struct exec *) filep;
+
+ /* Check magic number and symbol count. */
+ if (N_BADMAG(*ebuf))
+ badfmt("bad magic number");
+ if (!ebuf->a_syms)
+ badfmt("stripped");
+
+ strtab = filep + N_STROFF(*ebuf) + sizeof (int);
+
+ /* Seek to symbol table. */
+ cur_off = N_SYMOFF(*ebuf);
+
+ /* Read each symbol and enter it into the database. */
+ nsyms = ebuf->a_syms / sizeof(struct nlist);
+ while (nsyms--) {
+
+ nbuf = (NLIST *)(filep + cur_off);
+ cur_off += sizeof(NLIST);
+
+ if (!nbuf->_strx || nbuf->n_type&N_STAB)
+ continue;
+
+ key.data = (u_char *)strtab + nbuf->_strx - sizeof(long);
+ key.size = strlen((char *)key.data);
+ data.data = (u_char *)nbuf;
+ data.size = sizeof(NLIST);
+ if (db->put(db, &key, &data, 0))
+ err(1, "record enter");
+
+ if (1 && strcmp((char *)key.data, VRS_SYM) == 0) {
+#ifndef KERNTEXTOFF
+/*
+ * XXX
+ * The FreeBSD bootloader loads the kernel at the a_entry address, meaning
+ * that this is where the kernel starts. (not at KERNBASE)
+ *
+ * This may be introducing an i386 dependency.
+ */
+#if defined(__FreeBSD__)
+#define KERNTEXTOFF ebuf->a_entry
+#else
+#define KERNTEXTOFF KERNBASE
+#endif
+#endif
+ /*
+ * Calculate offset relative to a normal (non-kernel)
+ * a.out. KERNTEXTOFF is where the kernel is really
+ * loaded; N_TXTADDR is where a normal file is loaded.
+ * From there, locate file offset in text or data.
+ */
+ voff = nbuf->n_value - KERNTEXTOFF + N_TXTADDR(*ebuf);
+ if ((nbuf->n_type & N_TYPE) == N_TEXT)
+ voff += N_TXTOFF(*ebuf) - N_TXTADDR(*ebuf);
+ else
+ voff += N_DATOFF(*ebuf) - N_DATADDR(*ebuf);
+
+ vp = filep + voff;
+
+ key.data = (u_char *)VRS_KEY;
+ key.size = sizeof(VRS_KEY) - 1;
+ data.data = vp;
+ data.size = strchr(vp, '\n') - vp + 1;
+
+ if (db->put(db, &key, &data, 0))
+ err(1, "record enter");
+
+ /* Restore to original values. */
+ data.size = sizeof(NLIST);
+ }
+ }
+ return(0);
+}
+
+
+#endif /* DO_AOUT */
+
+#ifdef DO_ELF
+int
+__elf_knlist(name, db)
+ char *name;
+ DB *db;
+{
+ register caddr_t strtab;
+ register off_t symstroff, symoff;
+ register u_long symsize;
+ register u_long kernvma, kernoffs;
+ register int i;
+ Elf32_Sym *sbuf;
+ size_t symstrsize;
+ char *shstr, buf[1024];
+ Elf32_Ehdr *eh;
+ Elf32_Shdr *sh = NULL;
+ DBT data, key;
+ NLIST nbuf;
+ int fd;
+ u_char *filep;
+ struct stat sst;
+
+ kfile = name;
+ if ((fd = open(name, O_RDONLY, 0)) < 0)
+ err(1, "%s", name);
+
+ fstat(fd, &sst);
+
+ /* Check for files too large to mmap. */
+ /* XXX is this really possible? */
+ if (sst.st_size > SIZE_T_MAX) {
+ badfmt("corrupt file");
+ }
+ filep = (u_char*)mmap(0, sst.st_size, PROT_READ, MAP_PRIVATE, fd, 0);
+
+ if (filep == (u_char*)MAP_FAILED)
+ err(1, "mmap failed");
+
+ /* Read in exec structure. */
+ eh = (Elf32_Ehdr *) filep;
+
+ if (!IS_ELF(*eh))
+ return(-1);
+
+ sh = (Elf32_Shdr *)&filep[eh->e_shoff];
+
+ shstr = (char *)&filep[sh[eh->e_shstrndx].sh_offset];
+
+ for (i = 0; i < eh->e_shnum; i++) {
+ if (strcmp (shstr + sh[i].sh_name, ".strtab") == 0) {
+ symstroff = sh[i].sh_offset;
+ symstrsize = sh[i].sh_size;
+ }
+ else if (strcmp (shstr + sh[i].sh_name, ".symtab") == 0) {
+ symoff = sh[i].sh_offset;
+ symsize = sh[i].sh_size;
+ }
+ else if (strcmp (shstr + sh[i].sh_name, ".text") == 0) {
+ kernvma = sh[i].sh_addr;
+ kernoffs = sh[i].sh_offset;
+ }
+ }
+
+ strtab = (char *)&filep[symstroff];
+
+ data.data = (u_char *)&nbuf;
+ data.size = sizeof(NLIST);
+
+ /* Read each symbol and enter it into the database. */
+ for (i = 0; symsize > 0; i++, symsize -= sizeof(Elf32_Sym)) {
+
+ sbuf = (Elf32_Sym *)&filep[symoff + i * sizeof(*sbuf)];
+ if (!sbuf->st_name)
+ continue;
+
+ nbuf.n_value = sbuf->st_value;
+
+ /*XXX type conversion is pretty rude... */
+ switch (ELF32_ST_TYPE(sbuf->st_info)) {
+ case STT_NOTYPE:
+ nbuf.n_type = N_UNDF;
+ break;
+ case STT_FUNC:
+ nbuf.n_type = N_TEXT;
+ break;
+ case STT_OBJECT:
+ nbuf.n_type = N_DATA;
+ break;
+ }
+ if (ELF32_ST_BIND(sbuf->st_info) == STB_LOCAL)
+ nbuf.n_type = N_EXT;
+
+ key.data = (u_char *)(strtab + sbuf->st_name);
+ key.size = strlen((char *)key.data);
+ if (db->put(db, &key, &data, 0))
+ err(1, "record enter");
+
+ /* also put in name prefixed with _ */
+ *buf = '_';
+ strcpy(buf + 1, strtab + sbuf->st_name);
+ key.data = (u_char *)buf;
+ key.size = strlen((char *)key.data);
+ if (db->put(db, &key, &data, 0))
+ err(1, "record enter");
+
+ /* Special processing for "_version" (depends on above) */
+ if (strcmp((char *)key.data, VRS_SYM) == 0) {
+ char *vp;
+
+ key.data = (u_char *)VRS_KEY;
+ key.size = sizeof(VRS_KEY) - 1;
+ /* Find the version string, relative to its section */
+ data.data = strdup(&filep[nbuf.n_value -
+ sh[sbuf->st_shndx].sh_addr +
+ sh[sbuf->st_shndx].sh_offset]);
+ /* assumes newline terminates version. */
+ if ((vp = strchr(data.data, '\n')) != NULL)
+ *vp = '\0';
+ data.size = strlen((char *)data.data);
+
+ if (db->put(db, &key, &data, 0))
+ err(1, "record enter");
+
+ /* Restore to original values. */
+ data.data = (u_char *)&nbuf;
+ data.size = sizeof(NLIST);
+ }
+ }
+ munmap(filep, sst.st_size);
+ (void)close(fd);
+ return(0);
+}
+#endif /* DO_ELF */
+
+static struct knlist_handlers {
+ int (*fn) __P((char *name, DB *db));
+} nlist_fn[] = {
+#ifdef DO_ELF
+ { __elf_knlist },
+#endif
+#ifdef DO_AOUT
+ { __aout_knlist },
+#endif
+};
+
+void
+create_knlist(name, db)
+ char *name;
+ DB *db;
+{
+ int n, i;
+
+ for (i = 0; i < sizeof(nlist_fn)/sizeof(nlist_fn[0]); i++) {
+ n = (nlist_fn[i].fn)(name, db);
+ if (n != -1)
+ break;
+ }
+}
diff --git a/usr.sbin/kvm_mkdb/testdb.c b/usr.sbin/kvm_mkdb/testdb.c
new file mode 100644
index 0000000..8d769bd
--- /dev/null
+++ b/usr.sbin/kvm_mkdb/testdb.c
@@ -0,0 +1,117 @@
+/*-
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)testdb.c 8.1 (Berkeley) 6/6/93";
+#endif
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/file.h>
+#include <errno.h>
+#include <limits.h>
+#include <kvm.h>
+#include <db.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <paths.h>
+
+#include "extern.h"
+
+/* Return true if the db file is valid, else false */
+int
+testdb()
+{
+ register DB *db;
+ register int cc, kd, ret, dbversionlen;
+ register char *cp, *uf;
+ DBT rec;
+ struct nlist nitem;
+ char dbname[MAXPATHLEN], dbversion[_POSIX2_LINE_MAX];
+ char kversion[_POSIX2_LINE_MAX];
+
+ ret = 0;
+ db = NULL;
+
+ if ((kd = open(_PATH_KMEM, O_RDONLY, 0)) < 0)
+ goto close;
+
+ uf = (char *)getbootfile();
+ if ((cp = rindex(uf, '/')) != 0)
+ uf = cp + 1;
+ (void) snprintf(dbname, sizeof(dbname), "%skvm_%s.db", _PATH_VARDB, uf);
+ if ((db = dbopen(dbname, O_RDONLY, 0, DB_HASH, NULL)) == NULL)
+ goto close;
+
+ /* Read the version out of the database */
+ rec.data = VRS_KEY;
+ rec.size = sizeof(VRS_KEY) - 1;
+ if ((db->get)(db, &rec, &rec, 0))
+ goto close;
+ if (rec.data == 0 || rec.size > sizeof(dbversion))
+ goto close;
+ bcopy(rec.data, dbversion, rec.size);
+ dbversionlen = rec.size;
+
+ /* Read version string from kernel memory */
+ rec.data = VRS_SYM;
+ rec.size = sizeof(VRS_SYM) - 1;
+ if ((db->get)(db, &rec, &rec, 0))
+ goto close;
+ if (rec.data == 0 || rec.size != sizeof(struct nlist))
+ goto close;
+ bcopy(rec.data, &nitem, sizeof(nitem));
+ /*
+ * Theoretically possible for lseek to be seeking to -1. Not
+ * that it's something to lie awake nights about, however.
+ */
+ errno = 0;
+ if (lseek(kd, (off_t)nitem.n_value, SEEK_SET) == -1 && errno != 0)
+ goto close;
+ cc = read(kd, kversion, sizeof(kversion));
+ if (cc < 0 || cc != sizeof(kversion))
+ goto close;
+
+ /* If they match, we win */
+ ret = bcmp(dbversion, kversion, dbversionlen) == 0;
+
+close: if (kd >= 0)
+ (void)close(kd);
+ if (db)
+ (void)(db->close)(db);
+ return (ret);
+}
diff --git a/usr.sbin/lpr/Makefile b/usr.sbin/lpr/Makefile
new file mode 100644
index 0000000..4ea49c9
--- /dev/null
+++ b/usr.sbin/lpr/Makefile
@@ -0,0 +1,6 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+SUBDIR= lp lpc lpd lpq lpr lprm lptest pac filters
+
+.include <bsd.subdir.mk>
+
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..1701c86
--- /dev/null
+++ b/usr.sbin/lpr/SMM.doc/Makefile
@@ -0,0 +1,10 @@
+# @(#)Makefile 8.1 (Berkeley) 6/8/93
+
+DIR= smm/07.lpd
+SRCS= 0.t 1.t 2.t 3.t 4.t 5.t 6.t 7.t
+MACROS= -ms
+
+paper.ps: ${SRCS}
+ ${TBL} ${SRCS} | ${ROFF} > ${.TARGET}
+
+.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/common_source/common.c b/usr.sbin/lpr/common_source/common.c
new file mode 100644
index 0000000..7301e2b
--- /dev/null
+++ b/usr.sbin/lpr/common_source/common.c
@@ -0,0 +1,386 @@
+/*
+ * 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";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netdb.h>
+
+#include <dirent.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "lp.h"
+#include "pathnames.h"
+
+/*
+ * Routines and data common to all the line printer functions.
+ */
+
+char *AF; /* accounting file */
+long BR; /* baud rate if lp is a tty */
+char *CF; /* name of cifplot filter (per job) */
+long CT; /* TCP connection timeout */
+char *DF; /* name of tex filter (per job) */
+long DU; /* daeomon user-id */
+char *FF; /* form feed string */
+char *GF; /* name of graph(1G) filter (per job) */
+long HL; /* print header last */
+char *IF; /* name of input filter (created per job) */
+char *LF; /* log file for error messages */
+char *LO; /* lock file name */
+char *LP; /* line printer device name */
+long MC; /* maximum number of copies allowed */
+long MX; /* maximum number of blocks to copy */
+char *NF; /* name of ditroff filter (per job) */
+char *OF; /* name of output filter (created once) */
+char *PF; /* name of vrast filter (per job) */
+long PL; /* page length */
+long PW; /* page width */
+long PX; /* page width in pixels */
+long PY; /* page length in pixels */
+char *RF; /* name of fortran text filter (per job) */
+char *RG; /* resricted group */
+char *RM; /* remote machine name */
+char *RP; /* remote printer name */
+long RS; /* restricted to those with local accounts */
+long RW; /* open LP for reading and writing */
+long SB; /* short banner instead of normal header */
+long SC; /* suppress multiple copies */
+char *SD; /* spool directory */
+long SF; /* suppress FF on each print job */
+long SH; /* suppress header page */
+char *ST; /* status file name */
+char *TF; /* name of troff filter (per job) */
+char *TR; /* trailer string to be output when Q empties */
+char *MS; /* mode set, a la stty */
+char *VF; /* name of vplot filter (per job) */
+
+char line[BUFSIZ];
+char *bp; /* pointer into printcap buffer. */
+char *name; /* program name */
+char *printer; /* printer name */
+ /* host machine name */
+char host[MAXHOSTNAMELEN];
+char *from = host; /* client's machine name */
+int remote; /* true if sending files to a remote host */
+char *printcapdb[2] = { _PATH_PRINTCAP, 0 };
+
+extern uid_t uid, euid;
+
+static int compar __P((const void *, const void *));
+
+/*
+ * 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(rhost, rport)
+ char *rhost;
+ int rport;
+{
+ struct hostent *hp;
+ struct servent *sp;
+ struct sockaddr_in sin;
+ int s, timo = 1, lport = IPPORT_RESERVED - 1;
+ int err;
+
+ /*
+ * Get the host address and port number to connect to.
+ */
+ if (rhost == NULL)
+ fatal("no remote host to connect to");
+ bzero((char *)&sin, sizeof(sin));
+ sin.sin_addr.s_addr = inet_addr(rhost);
+ if (sin.sin_addr.s_addr != INADDR_NONE)
+ sin.sin_family = AF_INET;
+ else {
+ hp = gethostbyname(rhost);
+ if (hp == NULL)
+ fatal("unknown host %s", rhost);
+ bcopy(hp->h_addr, (caddr_t)&sin.sin_addr, hp->h_length);
+ sin.sin_family = hp->h_addrtype;
+ }
+ if (rport == 0) {
+ sp = getservbyname("printer", "tcp");
+ if (sp == NULL)
+ fatal("printer/tcp: unknown service");
+ sin.sin_port = sp->s_port;
+ } else
+ sin.sin_port = htons(rport);
+
+ /*
+ * Try connecting to the server.
+ */
+retry:
+ seteuid(euid);
+ s = rresvport(&lport);
+ seteuid(uid);
+ if (s < 0)
+ return(-1);
+ if (connect(s, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
+ err = errno;
+ (void) close(s);
+ errno = err;
+ if (errno == EADDRINUSE) {
+ lport--;
+ goto retry;
+ }
+ if (errno == ECONNREFUSED && timo <= 16) {
+ sleep(timo);
+ timo *= 2;
+ goto retry;
+ }
+ return(-1);
+ }
+ return(s);
+}
+
+/*
+ * Getline reads a line from the control file cfp, removes tabs, converts
+ * new-line to null and leaves it in line.
+ * Returns 0 at EOF or the number of characters read.
+ */
+int
+getline(cfp)
+ FILE *cfp;
+{
+ register int linel = 0;
+ register char *lp = line;
+ register c;
+
+ while ((c = getc(cfp)) != '\n' && linel+1 < sizeof(line)) {
+ if (c == EOF)
+ return(0);
+ if (c == '\t') {
+ do {
+ *lp++ = ' ';
+ linel++;
+ } while ((linel & 07) != 0 && linel+1 < sizeof(line));
+ continue;
+ }
+ *lp++ = c;
+ linel++;
+ }
+ *lp++ = '\0';
+ return(linel);
+}
+
+/*
+ * Scan the current directory and make a list of daemon files sorted by
+ * creation time.
+ * Return the number of entries and a pointer to the list.
+ */
+int
+getq(namelist)
+ struct queue *(*namelist[]);
+{
+ register struct dirent *d;
+ register struct queue *q, **queue;
+ register int nitems;
+ struct stat stbuf;
+ DIR *dirp;
+ int arraysz;
+
+ seteuid(euid);
+ if ((dirp = opendir(SD)) == NULL)
+ return(-1);
+ if (fstat(dirp->dd_fd, &stbuf) < 0)
+ goto errdone;
+ seteuid(uid);
+
+ /*
+ * Estimate the array size by taking the size of the directory file
+ * and dividing it by a multiple of the minimum size entry.
+ */
+ arraysz = (stbuf.st_size / 24);
+ queue = (struct queue **)malloc(arraysz * sizeof(struct queue *));
+ if (queue == NULL)
+ goto errdone;
+
+ nitems = 0;
+ while ((d = readdir(dirp)) != NULL) {
+ if (d->d_name[0] != 'c' || d->d_name[1] != 'f')
+ continue; /* daemon control files only */
+ seteuid(euid);
+ if (stat(d->d_name, &stbuf) < 0)
+ continue; /* Doesn't exist */
+ seteuid(uid);
+ q = (struct queue *)malloc(sizeof(time_t)+strlen(d->d_name)+1);
+ if (q == NULL)
+ goto errdone;
+ q->q_time = stbuf.st_mtime;
+ strcpy(q->q_name, d->d_name);
+ /*
+ * Check to make sure the array has space left and
+ * realloc the maximum size.
+ */
+ if (++nitems > arraysz) {
+ arraysz *= 2;
+ queue = (struct queue **)realloc((char *)queue,
+ arraysz * sizeof(struct queue *));
+ if (queue == NULL)
+ goto errdone;
+ }
+ queue[nitems-1] = q;
+ }
+ closedir(dirp);
+ if (nitems)
+ qsort(queue, nitems, sizeof(struct queue *), compar);
+ *namelist = queue;
+ return(nitems);
+
+errdone:
+ closedir(dirp);
+ return(-1);
+}
+
+/*
+ * Compare modification times.
+ */
+static int
+compar(p1, p2)
+ const void *p1, *p2;
+{
+ if ((*(struct queue **)p1)->q_time < (*(struct queue **)p2)->q_time)
+ return(-1);
+ if ((*(struct queue **)p1)->q_time > (*(struct queue **)p2)->q_time)
+ return(1);
+ return(0);
+}
+
+/*
+ * Figure out whether the local machine is the same
+ * as the remote machine (RM) entry (if it exists).
+ */
+char *
+checkremote()
+{
+ char name[MAXHOSTNAMELEN];
+ register struct hostent *hp;
+ static char errbuf[128];
+
+ remote = 0; /* assume printer is local */
+ if (RM != NULL) {
+ /* get the official name of the local host */
+ gethostname(name, sizeof(name));
+ name[sizeof(name) - 1] = '\0';
+ hp = gethostbyname(name);
+ if (hp == (struct hostent *) NULL) {
+ (void) snprintf(errbuf, sizeof(errbuf),
+ "unable to get official name for local machine %s",
+ name);
+ return errbuf;
+ } else {
+ (void) strncpy(name, hp->h_name, sizeof(name));
+ name[sizeof(name) - 1] = '\0';
+ }
+
+ /* get the official name of RM */
+ hp = gethostbyname(RM);
+ if (hp == (struct hostent *) NULL) {
+ (void) snprintf(errbuf, sizeof(errbuf),
+ "unable to get official name for remote machine %s",
+ RM);
+ return errbuf;
+ }
+
+ /*
+ * if the two hosts are not the same,
+ * then the printer must be remote.
+ */
+ if (strcasecmp(name, hp->h_name) != 0)
+ remote = 1;
+ }
+ return NULL;
+}
+
+/* sleep n milliseconds */
+void
+delay(n)
+{
+ struct timeval tdelay;
+
+ if (n <= 0 || n > 10000)
+ fatal("unreasonable delay period (%d)", n);
+ tdelay.tv_sec = n / 1000;
+ tdelay.tv_usec = n * 1000 % 1000000;
+ (void) select(0, (fd_set *)0, (fd_set *)0, (fd_set *)0, &tdelay);
+}
+
+#ifdef __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+void
+#ifdef __STDC__
+fatal(const char *msg, ...)
+#else
+fatal(msg, va_alist)
+ char *msg;
+ va_dcl
+#endif
+{
+ va_list ap;
+#ifdef __STDC__
+ va_start(ap, msg);
+#else
+ va_start(ap);
+#endif
+ if (from != host)
+ (void)printf("%s: ", host);
+ (void)printf("%s: ", name);
+ if (printer)
+ (void)printf("%s: ", printer);
+ (void)vprintf(msg, ap);
+ va_end(ap);
+ (void)putchar('\n');
+ exit(1);
+}
diff --git a/usr.sbin/lpr/common_source/displayq.c b/usr.sbin/lpr/common_source/displayq.c
new file mode 100644
index 0000000..01d91a6
--- /dev/null
+++ b/usr.sbin/lpr/common_source/displayq.c
@@ -0,0 +1,490 @@
+/*
+ * 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";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/file.h>
+
+#include <signal.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include "lp.h"
+#include "lp.local.h"
+#include "pathnames.h"
+
+/*
+ * Routines to display the state of the queue.
+ */
+#define JOBCOL 40 /* column for job # in -l format */
+#define OWNCOL 7 /* start of Owner column in normal */
+#define SIZCOL 62 /* start of Size column in normal */
+
+/*
+ * Stuff for handling job specifications
+ */
+extern uid_t uid, euid;
+
+static int col; /* column on screen */
+static char current[40]; /* current file being printed */
+static char file[132]; /* print file name */
+static int first; /* first file in ``files'' column? */
+static int garbage; /* # of garbage cf files */
+static int lflag; /* long output option */
+static int rank; /* order to be printed (-1=none, 0=active) */
+static long totsize; /* total print job size in bytes */
+
+static char *head0 = "Rank Owner Job Files";
+static char *head1 = "Total Size\n";
+
+static void alarmhandler __P((int));
+static void warn __P((void));
+
+/*
+ * Display the current state of the queue. Format = 1 if long format.
+ */
+void
+displayq(format)
+ int format;
+{
+ register struct queue *q;
+ register int i, nitems, fd, ret;
+ register char *cp;
+ struct queue **queue;
+ struct stat statb;
+ FILE *fp;
+ void (*savealrm)(int);
+
+ lflag = format;
+ totsize = 0;
+ rank = -1;
+ if ((i = cgetent(&bp, printcapdb, printer)) == -2)
+ fatal("can't open printer description file");
+ else if (i == -1)
+ fatal("unknown printer");
+ else if (i == -3)
+ fatal("potential reference loop detected in printcap file");
+ if (cgetstr(bp, "lp", &LP) < 0)
+ LP = _PATH_DEFDEVLP;
+ if (cgetstr(bp, "rp", &RP) < 0)
+ RP = DEFLP;
+ if (cgetstr(bp, "sd", &SD) < 0)
+ SD = _PATH_DEFSPOOL;
+ if (cgetstr(bp,"lo", &LO) < 0)
+ LO = DEFLOCK;
+ if (cgetstr(bp, "st", &ST) < 0)
+ ST = DEFSTAT;
+ if (cgetnum(bp, "ct", &CT) < 0)
+ CT = DEFTIMEOUT;
+ if (cgetstr(bp, "rm", &RM) < 0)
+ RM = NULL;
+ if ((cp = checkremote()))
+ printf("Warning: %s\n", cp);
+
+ /*
+ * Print out local queue
+ * Find all the control files in the spooling directory
+ */
+ seteuid(euid);
+ if (chdir(SD) < 0)
+ fatal("cannot chdir to spooling directory");
+ seteuid(uid);
+ if ((nitems = getq(&queue)) < 0)
+ fatal("cannot examine spooling area\n");
+ seteuid(euid);
+ ret = stat(LO, &statb);
+ seteuid(uid);
+ if (ret >= 0) {
+ if (statb.st_mode & 0100) {
+ if (remote)
+ printf("%s: ", host);
+ printf("Warning: %s is down: ", printer);
+ seteuid(euid);
+ fd = open(ST, O_RDONLY);
+ seteuid(uid);
+ if (fd >= 0) {
+ (void) flock(fd, LOCK_SH);
+ 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 & 010) {
+ if (remote)
+ printf("%s: ", host);
+ printf("Warning: %s queue is turned off\n", printer);
+ }
+ }
+
+ if (nitems) {
+ seteuid(euid);
+ fp = fopen(LO, "r");
+ seteuid(uid);
+ if (fp == NULL)
+ warn();
+ else {
+ /* get daemon pid */
+ cp = current;
+ while ((i = getc(fp)) != EOF && i != '\n')
+ *cp++ = i;
+ *cp = '\0';
+ i = atoi(current);
+ if (i <= 0) {
+ ret = -1;
+ } else {
+ seteuid(euid);
+ ret = kill(i, 0);
+ seteuid(uid);
+ }
+ if (ret < 0) {
+ warn();
+ } else {
+ /* read current file name */
+ cp = current;
+ while ((i = getc(fp)) != EOF && i != '\n')
+ *cp++ = i;
+ *cp = '\0';
+ /*
+ * Print the status file.
+ */
+ if (remote)
+ printf("%s: ", host);
+ seteuid(euid);
+ fd = open(ST, O_RDONLY);
+ seteuid(uid);
+ if (fd >= 0) {
+ (void) flock(fd, LOCK_SH);
+ while ((i = read(fd, line, sizeof(line))) > 0)
+ (void) fwrite(line, 1, i, stdout);
+ (void) 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(q->q_name);
+ free(q);
+ }
+ free(queue);
+ }
+ if (!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 + '\3', RP);
+ 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(CT);
+ fd = getport(RM, 0);
+ alarm(0);
+ (void)signal(SIGALRM, savealrm);
+ if (fd < 0) {
+ if (from != host)
+ printf("%s: ", host);
+ printf("connection to %s is down\n", RM);
+ }
+ else {
+ i = strlen(line);
+ if (write(fd, line, i) != i)
+ fatal("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()
+{
+ if (remote)
+ printf("%s: ", host);
+ puts("Warning: no daemon present");
+ current[0] = '\0';
+}
+
+/*
+ * Print the header for the short listing format
+ */
+void
+header()
+{
+ printf(head0);
+ col = strlen(head0)+1;
+ blankfill(SIZCOL);
+ printf(head1);
+}
+
+void
+inform(cf)
+ char *cf;
+{
+ register int j;
+ FILE *cfp;
+
+ /*
+ * There's a chance the control file has gone away
+ * in the meantime; if this is the case just keep going
+ */
+ seteuid(euid);
+ if ((cfp = fopen(cf, "r")) == NULL)
+ return;
+ seteuid(uid);
+
+ if (rank < 0)
+ rank = 0;
+ if (remote || garbage || strcmp(cf, current))
+ rank++;
+ j = 0;
+ while (getline(cfp)) {
+ switch (line[0]) {
+ case 'P': /* Was this file specified in the user's list? */
+ if (!inlist(line+1, cf)) {
+ fclose(cfp);
+ return;
+ }
+ if (lflag) {
+ printf("\n%s: ", line+1);
+ col = strlen(line+1) + 2;
+ prank(rank);
+ blankfill(JOBCOL);
+ printf(" [job %s]\n", cf+3);
+ } else {
+ col = 0;
+ prank(rank);
+ blankfill(OWNCOL);
+ printf("%-10s %-3d ", line+1, atoi(cf+3));
+ col += 16;
+ first = 1;
+ }
+ continue;
+ default: /* some format specifer and file name? */
+ if (line[0] < 'a' || line[0] > 'z')
+ continue;
+ if (j == 0 || strcmp(file, line+1) != 0) {
+ (void) strncpy(file, line+1, sizeof(file) - 1);
+ file[sizeof(file) - 1] = '\0';
+ }
+ j++;
+ continue;
+ case 'N':
+ show(line+1, file, j);
+ file[0] = '\0';
+ j = 0;
+ }
+ }
+ fclose(cfp);
+ if (!lflag) {
+ blankfill(SIZCOL);
+ printf("%ld bytes\n", totsize);
+ totsize = 0;
+ }
+}
+
+int
+inlist(name, file)
+ char *name, *file;
+{
+ register int *r, n;
+ register char **u, *cp;
+
+ if (users == 0 && requests == 0)
+ return(1);
+ /*
+ * Check to see if it's in the user list
+ */
+ for (u = user; u < &user[users]; u++)
+ if (!strcmp(*u, name))
+ return(1);
+ /*
+ * Check the request list
+ */
+ for (n = 0, cp = file+3; isdigit(*cp); )
+ n = n * 10 + (*cp++ - '0');
+ for (r = requ; r < &requ[requests]; r++)
+ if (*r == n && !strcmp(cp, from))
+ return(1);
+ return(0);
+}
+
+void
+show(nfile, file, copies)
+ register char *nfile, *file;
+ int copies;
+{
+ if (strcmp(nfile, " ") == 0)
+ nfile = "(standard input)";
+ if (lflag)
+ ldump(nfile, file, copies);
+ else
+ dump(nfile, file, copies);
+}
+
+/*
+ * Fill the line with blanks to the specified column
+ */
+void
+blankfill(n)
+ register int n;
+{
+ while (col++ < n)
+ putchar(' ');
+}
+
+/*
+ * Give the abbreviated dump of the file names
+ */
+void
+dump(nfile, file, copies)
+ char *nfile, *file;
+ int copies;
+{
+ register short n, fill;
+ struct stat lbuf;
+
+ /*
+ * Print as many files as will fit
+ * (leaving room for the total size)
+ */
+ fill = first ? 0 : 2; /* fill space for ``, '' */
+ if (((n = strlen(nfile)) + col + fill) >= SIZCOL-4) {
+ if (col < SIZCOL) {
+ printf(" ..."), col += 4;
+ blankfill(SIZCOL);
+ }
+ } else {
+ if (first)
+ first = 0;
+ else
+ printf(", ");
+ printf("%s", nfile);
+ col += n+fill;
+ }
+ seteuid(euid);
+ if (*file && !stat(file, &lbuf))
+ totsize += copies * lbuf.st_size;
+ seteuid(uid);
+}
+
+/*
+ * Print the long info about the file
+ */
+void
+ldump(nfile, file, copies)
+ char *nfile, *file;
+ int copies;
+{
+ struct stat lbuf;
+
+ putchar('\t');
+ if (copies > 1)
+ printf("%-2d copies of %-19s", copies, nfile);
+ else
+ printf("%-32s", nfile);
+ if (*file && !stat(file, &lbuf))
+ printf(" %qd bytes", lbuf.st_size);
+ else
+ printf(" ??? bytes");
+ putchar('\n');
+}
+
+/*
+ * Print the job's rank in the queue,
+ * update col for screen management
+ */
+void
+prank(n)
+ int n;
+{
+ char rline[100];
+ static char *r[] = {
+ "th", "st", "nd", "rd", "th", "th", "th", "th", "th", "th"
+ };
+
+ if (n == 0) {
+ printf("active");
+ col += 6;
+ return;
+ }
+ if ((n/10)%10 == 1)
+ (void)snprintf(rline, sizeof(rline), "%dth", n);
+ else
+ (void)snprintf(rline, sizeof(rline), "%d%s", n, r[n%10]);
+ col += strlen(rline);
+ printf("%s", rline);
+}
+
+void
+alarmhandler(signo)
+ int signo;
+{
+ /* ignored */
+}
diff --git a/usr.sbin/lpr/common_source/lp.h b/usr.sbin/lpr/common_source/lp.h
new file mode 100644
index 0000000..816e652
--- /dev/null
+++ b/usr.sbin/lpr/common_source/lp.h
@@ -0,0 +1,133 @@
+/*
+ * 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.h 8.2 (Berkeley) 4/28/95
+ */
+
+
+/*
+ * Global definitions for the line printer system.
+ */
+
+extern char *AF; /* accounting file */
+extern long BR; /* baud rate if lp is a tty */
+extern char *CF; /* name of cifplot filter (per job) */
+extern long CT; /* TCP connection timeout */
+extern char *DF; /* name of tex filter (per job) */
+extern long DU; /* daeomon user-id */
+extern char *FF; /* form feed string */
+extern char *GF; /* name of graph(1G) filter (per job) */
+extern long HL; /* print header last */
+extern char *IF; /* name of input filter (created per job) */
+extern char *LF; /* log file for error messages */
+extern char *LO; /* lock file name */
+extern char *LP; /* line printer device name */
+extern long MC; /* maximum number of copies allowed */
+extern long MX; /* maximum number of blocks to copy */
+extern char *NF; /* name of ditroff(1) filter (per job) */
+extern char *OF; /* name of output filter (created once) */
+extern long PL; /* page length */
+extern long PW; /* page width */
+extern long PX; /* page width in pixels */
+extern long PY; /* page length in pixels */
+extern char *RF; /* name of fortran text filter (per job) */
+extern char *RG; /* restricted group */
+extern char *RM; /* remote machine name */
+extern char *RP; /* remote printer name */
+extern long RS; /* restricted to those with local accounts */
+extern long RW; /* open LP for reading and writing */
+extern long SB; /* short banner instead of normal header */
+extern long SC; /* suppress multiple copies */
+extern char *SD; /* spool directory */
+extern long SF; /* suppress FF on each print job */
+extern long SH; /* suppress header page */
+extern char *ST; /* status file name */
+extern char *TF; /* name of troff(1) filter (per job) */
+extern char *TR; /* trailer string to be output when Q empties */
+extern char *MS; /* mode set, a la stty */
+extern char *VF; /* name of raster filter (per job) */
+
+extern char line[BUFSIZ];
+extern char *bp; /* pointer into printcap buffer */
+extern char *name; /* program name */
+extern char *printer; /* printer name */
+ /* host machine name */
+extern char host[MAXHOSTNAMELEN];
+extern char *from; /* client's machine name */
+extern int remote; /* true if sending files to a remote host */
+extern char *printcapdb[]; /* printcap database array */
+
+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 char *name;
+
+
+/*
+ * Structure used for building a sorted list of control files.
+ */
+struct queue {
+ time_t q_time; /* modification time */
+ char q_name[MAXNAMLEN+1]; /* control file name */
+};
+
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+struct dirent;
+
+void blankfill __P((int));
+char *checkremote __P((void));
+int chk __P((char *));
+void displayq __P((int));
+void dump __P((char *, char *, int));
+void fatal __P((const char *, ...));
+int getline __P((FILE *));
+int getport __P((char *, int));
+int getq __P((struct queue *(*[])));
+void header __P((void));
+void inform __P((char *));
+int inlist __P((char *, char *));
+int iscf __P((struct dirent *));
+int isowner __P((char *, char *));
+void ldump __P((char *, char *, int));
+int lockchk __P((char *));
+void prank __P((int));
+void process __P((char *));
+void rmjob __P((void));
+void rmremote __P((void));
+void show __P((char *, char *, int));
+int startdaemon __P((char *));
+void delay __P((int));
+__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/pathnames.h b/usr.sbin/lpr/common_source/pathnames.h
new file mode 100644
index 0000000..5c07cdb
--- /dev/null
+++ b/usr.sbin/lpr/common_source/pathnames.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.
+ *
+ * @(#)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"
diff --git a/usr.sbin/lpr/common_source/recvjob.c b/usr.sbin/lpr/common_source/recvjob.c
new file mode 100644
index 0000000..0a10e49
--- /dev/null
+++ b/usr.sbin/lpr/common_source/recvjob.c
@@ -0,0 +1,372 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 1983, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)recvjob.c 8.2 (Berkeley) 4/27/95";
+#endif
+static const char rcsid[] =
+ "$Id: recvjob.c,v 1.10 1997/09/24 06:47:55 charnier Exp $";
+#endif /* not lint */
+
+/*
+ * Receive printer jobs from the network, queue them and
+ * start the printer daemon.
+ */
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+
+#include <unistd.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <syslog.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "lp.h"
+#include "lp.local.h"
+#include "extern.h"
+#include "pathnames.h"
+
+#define ack() (void) write(1, sp, 1);
+
+static char dfname[NAME_MAX]; /* data files */
+static int minfree; /* keep at least minfree blocks available */
+static char *sp = "";
+static char tfname[NAME_MAX]; /* tmp copy of cf before linking */
+
+static int chksize __P((int));
+static void frecverr __P((const char *, ...));
+static int noresponse __P((void));
+static void rcleanup __P((int));
+static int read_number __P((char *));
+static int readfile __P((char *, int));
+static int readjob __P((void));
+
+
+void
+recvjob()
+{
+ struct stat stb;
+ int status;
+
+ /*
+ * Perform lookup for printer name or abbreviation
+ */
+ if ((status = cgetent(&bp, printcapdb, printer)) == -2)
+ frecverr("cannot open printer description file");
+ else if (status == -1)
+ frecverr("unknown printer %s", printer);
+ else if (status == -3)
+ fatal("potential reference loop detected in printcap file");
+
+ if (cgetstr(bp, "lf", &LF) == -1)
+ LF = _PATH_CONSOLE;
+ if (cgetstr(bp, "sd", &SD) == -1)
+ SD = _PATH_DEFSPOOL;
+ if (cgetstr(bp, "lo", &LO) == -1)
+ LO = DEFLOCK;
+
+ (void) close(2); /* set up log file */
+ if (open(LF, O_WRONLY|O_APPEND, 0664) < 0) {
+ syslog(LOG_ERR, "%s: %m", LF);
+ (void) open(_PATH_DEVNULL, O_WRONLY);
+ }
+
+ if (chdir(SD) < 0)
+ frecverr("%s: %s: %m", printer, SD);
+ if (stat(LO, &stb) == 0) {
+ if (stb.st_mode & 010) {
+ /* queue is disabled */
+ putchar('\1'); /* return error code */
+ exit(1);
+ }
+ } else if (stat(SD, &stb) < 0)
+ frecverr("%s: %s: %m", printer, SD);
+ minfree = 2 * read_number("minfree"); /* scale KB to 512 blocks */
+ signal(SIGTERM, rcleanup);
+ signal(SIGPIPE, rcleanup);
+
+ if (readjob())
+ printjob();
+}
+
+/*
+ * Read printer jobs sent by lpd and copy them to the spooling directory.
+ * Return the number of jobs successfully transfered.
+ */
+static int
+readjob()
+{
+ register int size, nfiles;
+ register char *cp;
+
+ ack();
+ nfiles = 0;
+ for (;;) {
+ /*
+ * Read a command to tell us what to do
+ */
+ cp = line;
+ do {
+ if ((size = read(1, cp, 1)) != 1) {
+ if (size < 0)
+ frecverr("%s: lost connection",
+ printer);
+ return(nfiles);
+ }
+ } while (*cp++ != '\n' && (cp - line + 1) < sizeof(line));
+ if (cp - line + 1 >= sizeof(line))
+ frecverr("readjob overflow");
+ *--cp = '\0';
+ cp = line;
+ switch (*cp++) {
+ case '\1': /* cleanup because data sent was bad */
+ rcleanup(0);
+ continue;
+
+ case '\2': /* read cf file */
+ size = 0;
+ while (*cp >= '0' && *cp <= '9')
+ size = size * 10 + (*cp++ - '0');
+ if (*cp++ != ' ')
+ break;
+ /*
+ * host name has been authenticated, we use our
+ * view of the host name since we may be passed
+ * something different than what gethostbyaddr()
+ * returns
+ */
+ strncpy(cp + 6, from, sizeof(line) + line - cp - 7);
+ line[sizeof(line) - 1 ] = '\0';
+ strncpy(tfname, cp, sizeof(tfname) - 1);
+ tfname[sizeof (tfname) - 1] = '\0';
+ tfname[0] = 't';
+ if (strchr(tfname, '/'))
+ frecverr("readjob: %s: illegal path name",
+ tfname);
+ if (!chksize(size)) {
+ (void) write(1, "\2", 1);
+ continue;
+ }
+ if (!readfile(tfname, size)) {
+ rcleanup(0);
+ continue;
+ }
+ if (link(tfname, cp) < 0)
+ frecverr("%s: %m", tfname);
+ (void) unlink(tfname);
+ tfname[0] = '\0';
+ nfiles++;
+ continue;
+
+ case '\3': /* read df file */
+ size = 0;
+ while (*cp >= '0' && *cp <= '9')
+ size = size * 10 + (*cp++ - '0');
+ if (*cp++ != ' ')
+ break;
+ if (!chksize(size)) {
+ (void) write(1, "\2", 1);
+ continue;
+ }
+ (void) strncpy(dfname, cp, sizeof(dfname) - 1);
+ dfname[sizeof(dfname) - 1] = '\0';
+ if (strchr(dfname, '/'))
+ frecverr("readjob: %s: illegal path name",
+ dfname);
+ (void) readfile(dfname, size);
+ continue;
+ }
+ frecverr("protocol screwup: %s", line);
+ }
+}
+
+/*
+ * Read files send by lpd and copy them to the spooling directory.
+ */
+static int
+readfile(file, size)
+ char *file;
+ int size;
+{
+ register char *cp;
+ char buf[BUFSIZ];
+ register int i, j, amt;
+ int fd, err;
+
+ fd = open(file, O_CREAT|O_EXCL|O_WRONLY, FILMOD);
+ if (fd < 0)
+ frecverr("readfile: %s: illegal path name: %m", file);
+ ack();
+ err = 0;
+ for (i = 0; i < size; i += BUFSIZ) {
+ amt = BUFSIZ;
+ cp = buf;
+ if (i + amt > size)
+ amt = size - i;
+ do {
+ j = read(1, cp, amt);
+ if (j <= 0)
+ frecverr("lost connection");
+ amt -= j;
+ cp += j;
+ } while (amt > 0);
+ amt = BUFSIZ;
+ if (i + amt > size)
+ amt = size - i;
+ if (write(fd, buf, amt) != amt) {
+ err++;
+ break;
+ }
+ }
+ (void) close(fd);
+ if (err)
+ frecverr("%s: write error", file);
+ if (noresponse()) { /* file sent had bad data in it */
+ if (strchr(file, '/') == NULL)
+ (void) unlink(file);
+ return(0);
+ }
+ ack();
+ return(1);
+}
+
+static int
+noresponse()
+{
+ char resp;
+
+ if (read(1, &resp, 1) != 1)
+ frecverr("lost connection");
+ if (resp == '\0')
+ return(0);
+ return(1);
+}
+
+/*
+ * Check to see if there is enough space on the disk for size bytes.
+ * 1 == OK, 0 == Not OK.
+ */
+static int
+chksize(size)
+ int size;
+{
+ int spacefree;
+ struct statfs sfb;
+
+ if (statfs(".", &sfb) < 0) {
+ syslog(LOG_ERR, "%s: %m", "statfs(\".\")");
+ return (1);
+ }
+ spacefree = sfb.f_bavail * (sfb.f_bsize / 512);
+ size = (size + 511) / 512;
+ if (minfree + size > spacefree)
+ return(0);
+ return(1);
+}
+
+static int
+read_number(fn)
+ char *fn;
+{
+ char lin[80];
+ register FILE *fp;
+
+ if ((fp = fopen(fn, "r")) == NULL)
+ return (0);
+ if (fgets(lin, 80, fp) == NULL) {
+ fclose(fp);
+ return (0);
+ }
+ fclose(fp);
+ return (atoi(lin));
+}
+
+/*
+ * Remove all the files associated with the current job being transfered.
+ */
+static void
+rcleanup(signo)
+ int signo;
+{
+ if (tfname[0] && strchr(tfname, '/') == NULL)
+ (void) unlink(tfname);
+ if (dfname[0] && strchr(dfname, '/') == NULL) {
+ do {
+ do
+ (void) unlink(dfname);
+ while (dfname[2]-- != 'A');
+ dfname[2] = 'z';
+ } while (dfname[0]-- != 'd');
+ }
+ dfname[0] = '\0';
+}
+
+#ifdef __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+static void
+#ifdef __STDC__
+frecverr(const char *msg, ...)
+#else
+frecverr(msg, va_alist)
+ char *msg;
+ va_dcl
+#endif
+{
+ va_list ap;
+#ifdef __STDC__
+ va_start(ap, msg);
+#else
+ va_start(ap);
+#endif
+ rcleanup(0);
+ syslog(LOG_ERR, "%s", fromb);
+ vsyslog(LOG_ERR, msg, ap);
+ va_end(ap);
+ putchar('\1'); /* return error code */
+ exit(1);
+}
diff --git a/usr.sbin/lpr/common_source/rmjob.c b/usr.sbin/lpr/common_source/rmjob.c
new file mode 100644
index 0000000..256da3f
--- /dev/null
+++ b/usr.sbin/lpr/common_source/rmjob.c
@@ -0,0 +1,378 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)rmjob.c 8.2 (Berkeley) 4/28/95";
+#endif
+static const char rcsid[] =
+ "$Id: rmjob.c,v 1.9 1997/09/24 06:47:31 charnier Exp $";
+#endif /* not lint */
+
+#include <sys/param.h>
+
+#include <signal.h>
+#include <errno.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include "lp.h"
+#include "lp.local.h"
+#include "pathnames.h"
+
+/*
+ * rmjob - remove the specified jobs from the queue.
+ */
+
+/*
+ * Stuff for handling lprm specifications
+ */
+static char root[] = "root";
+static int all = 0; /* eliminate all files (root only) */
+static int cur_daemon; /* daemon's pid */
+static char current[40]; /* active control file name */
+
+extern uid_t uid, euid; /* real and effective user id's */
+
+static void alarmhandler __P((int));
+static void do_unlink __P((char *));
+
+void
+rmjob()
+{
+ register int i, nitems;
+ int assasinated = 0;
+ struct dirent **files;
+ char *cp;
+
+ if ((i = cgetent(&bp, printcapdb, printer)) == -2)
+ fatal("can't open printer description file");
+ else if (i == -1)
+ fatal("unknown printer");
+ else if (i == -3)
+ fatal("potential reference loop detected in printcap file");
+ if (cgetstr(bp, "lp", &LP) < 0)
+ LP = _PATH_DEFDEVLP;
+ if (cgetstr(bp, "rp", &RP) < 0)
+ RP = DEFLP;
+ if (cgetstr(bp, "sd", &SD) < 0)
+ SD = _PATH_DEFSPOOL;
+ if (cgetstr(bp,"lo", &LO) < 0)
+ LO = DEFLOCK;
+ if (cgetnum(bp, "ct", &CT) < 0)
+ CT = DEFTIMEOUT;
+ cgetstr(bp, "rm", &RM);
+ if ((cp = checkremote()))
+ printf("Warning: %s\n", cp);
+
+ /*
+ * If the format was `lprm -' and the user isn't the super-user,
+ * then fake things to look like he said `lprm user'.
+ */
+ if (users < 0) {
+ if (getuid() == 0)
+ all = 1; /* all files in local queue */
+ else {
+ user[0] = person;
+ users = 1;
+ }
+ }
+ if (!strcmp(person, "-all")) {
+ if (from == host)
+ fatal("The login name \"-all\" is reserved");
+ all = 1; /* all those from 'from' */
+ person = root;
+ }
+
+ seteuid(euid);
+ if (chdir(SD) < 0)
+ fatal("cannot chdir to spool directory");
+ if ((nitems = scandir(".", &files, iscf, NULL)) < 0)
+ fatal("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(LO) && chk(current)) {
+ seteuid(euid);
+ assasinated = kill(cur_daemon, SIGINT) == 0;
+ seteuid(uid);
+ if (!assasinated)
+ fatal("cannot kill printer daemon");
+ }
+ /*
+ * process the files
+ */
+ for (i = 0; i < nitems; i++)
+ process(files[i]->d_name);
+ }
+ rmremote();
+ /*
+ * Restart the printer daemon if it was killed
+ */
+ if (assasinated && !startdaemon(printer))
+ fatal("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(s)
+ char *s;
+{
+ register FILE *fp;
+ register int i, n;
+
+ seteuid(euid);
+ if ((fp = fopen(s, "r")) == NULL) {
+ if (errno == EACCES)
+ fatal("can't access lock file");
+ 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(file)
+ char *file;
+{
+ FILE *cfp;
+
+ if (!chk(file))
+ return;
+ seteuid(euid);
+ if ((cfp = fopen(file, "r")) == NULL)
+ fatal("cannot open %s", file);
+ seteuid(uid);
+ while (getline(cfp)) {
+ switch (line[0]) {
+ case 'U': /* unlink associated files */
+ if (strchr(line+1, '/') || strncmp(line+1, "df", 2))
+ break;
+ if (from != host)
+ printf("%s: ", host);
+ do_unlink(line+1);
+ }
+ }
+ (void) fclose(cfp);
+ do_unlink(file);
+}
+
+static void
+do_unlink(file)
+ char *file;
+{
+ int ret;
+
+ if (from != host)
+ printf("%s: ", host);
+ seteuid(euid);
+ ret = unlink(file);
+ seteuid(uid);
+ printf(ret ? "cannot dequeue %s\n" : "%s dequeued\n", file);
+}
+
+/*
+ * Do the dirty work in checking
+ */
+int
+chk(file)
+ char *file;
+{
+ register int *r, n;
+ register char **u, *cp;
+ FILE *cfp;
+
+ /*
+ * Check for valid cf file name (mostly checking current).
+ */
+ if (strlen(file) < 7 || file[0] != 'c' || file[1] != 'f')
+ return(0);
+
+ if (all && (from == host || !strcmp(from, file+6)))
+ return(1);
+
+ /*
+ * get the owner's name from the control file.
+ */
+ seteuid(euid);
+ if ((cfp = fopen(file, "r")) == NULL)
+ return(0);
+ seteuid(uid);
+ while (getline(cfp)) {
+ if (line[0] == 'P')
+ break;
+ }
+ (void) fclose(cfp);
+ if (line[0] != 'P')
+ return(0);
+
+ if (users == 0 && requests == 0)
+ return(!strcmp(file, current) && isowner(line+1, file));
+ /*
+ * Check the request list
+ */
+ for (n = 0, cp = file+3; isdigit(*cp); )
+ n = n * 10 + (*cp++ - '0');
+ for (r = requ; r < &requ[requests]; r++)
+ if (*r == n && isowner(line+1, file))
+ return(1);
+ /*
+ * Check to see if it's in the user list
+ */
+ for (u = user; u < &user[users]; u++)
+ if (!strcmp(*u, line+1) && isowner(line+1, file))
+ return(1);
+ return(0);
+}
+
+/*
+ * If root is removing a file on the local machine, allow it.
+ * If root is removing a file from a remote machine, only allow
+ * files sent from the remote machine to be removed.
+ * Normal users can only remove the file from where it was sent.
+ */
+int
+isowner(owner, file)
+ char *owner, *file;
+{
+ if (!strcmp(person, root) && (from == host || !strcmp(from, file+6)))
+ return(1);
+ if (!strcmp(person, owner) && !strcmp(from, file+6))
+ return(1);
+ if (from != host)
+ printf("%s: ", host);
+ printf("%s: Permission denied\n", file);
+ return(0);
+}
+
+/*
+ * Check to see if we are sending files to a remote machine. If we are,
+ * then try removing files on the remote machine.
+ */
+void
+rmremote()
+{
+ register char *cp;
+ register int i, rem;
+ char buf[BUFSIZ];
+ void (*savealrm)(int);
+
+ if (!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);
+
+ (void)snprintf(buf, sizeof(buf), "\5%s %s", RP, all ? "-all" : person);
+ cp = buf;
+ for (i = 0; i < users && cp-buf+1+strlen(user[i]) < sizeof(buf); i++) {
+ cp += strlen(cp);
+ *cp++ = ' ';
+ strcpy(cp, user[i]);
+ }
+ for (i = 0; i < requests && cp-buf+10 < sizeof(buf) - 1; i++) {
+ cp += strlen(cp);
+ (void) sprintf(cp, " %d", requ[i]);
+ }
+ strcat(cp, "\n");
+ savealrm = signal(SIGALRM, alarmhandler);
+ alarm(CT);
+ rem = getport(RM, 0);
+ (void)signal(SIGALRM, savealrm);
+ if (rem < 0) {
+ if (from != host)
+ printf("%s: ", host);
+ printf("connection to %s is down\n", RM);
+ } else {
+ i = strlen(buf);
+ if (write(rem, buf, i) != i)
+ fatal("Lost connection");
+ while ((i = read(rem, buf, sizeof(buf))) > 0)
+ (void) fwrite(buf, 1, i, stdout);
+ (void) close(rem);
+ }
+}
+
+/*
+ * Return 1 if the filename begins with 'cf'
+ */
+int
+iscf(d)
+ struct dirent *d;
+{
+ return(d->d_name[0] == 'c' && d->d_name[1] == 'f');
+}
+
+void
+alarmhandler(signo)
+{
+ /* ignored */
+}
diff --git a/usr.sbin/lpr/common_source/startdaemon.c b/usr.sbin/lpr/common_source/startdaemon.c
new file mode 100644
index 0000000..7f990b4
--- /dev/null
+++ b/usr.sbin/lpr/common_source/startdaemon.c
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 1983, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)startdaemon.c 8.2 (Berkeley) 4/17/94";
+#endif
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+
+#include <sys/param.h>
+#include <sys/socket.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(printer)
+ char *printer;
+{
+ struct sockaddr_un un;
+ register int s, n;
+ char buf[BUFSIZ];
+
+ s = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (s < 0) {
+ warn("socket");
+ return(0);
+ }
+ 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
+ seteuid(euid);
+ if (connect(s, (struct sockaddr *)&un, SUN_LEN(&un)) < 0) {
+ seteuid(uid);
+ warn("connect");
+ (void) close(s);
+ return(0);
+ }
+ seteuid(uid);
+ if (snprintf(buf, sizeof(buf), "\1%s\n", printer) > sizeof(buf) - 1) {
+ close(s);
+ return (0);
+ }
+ n = strlen(buf);
+ if (write(s, buf, n) != n) {
+ warn("write");
+ (void) close(s);
+ return(0);
+ }
+ if (read(s, buf, 1) == 1) {
+ if (buf[0] == '\0') { /* everything is OK */
+ (void) close(s);
+ return(1);
+ }
+ putchar(buf[0]);
+ }
+ while ((n = read(s, buf, sizeof(buf))) > 0)
+ fwrite(buf, 1, n, stdout);
+ (void) close(s);
+ return(0);
+}
diff --git a/usr.sbin/lpr/filters/Makefile b/usr.sbin/lpr/filters/Makefile
new file mode 100644
index 0000000..c9d5c4f
--- /dev/null
+++ b/usr.sbin/lpr/filters/Makefile
@@ -0,0 +1,7 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= lpf
+NOMAN= noman
+BINDIR= /usr/libexec/lpr
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/lpr/filters/lpf.c b/usr.sbin/lpr/filters/lpf.c
new file mode 100644
index 0000000..59b3cbf
--- /dev/null
+++ b/usr.sbin/lpr/filters/lpf.c
@@ -0,0 +1,219 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1983, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "From: @(#)lpf.c 8.1 (Berkeley) 6/6/93";
+static char id[] = "$Id$";
+#endif /* not lint */
+
+/*
+ * filter which reads the output of nroff and converts lines
+ * with ^H's to overwritten lines. Thus this works like 'ul'
+ * but is much better: it can handle more than 2 overwrites
+ * and it is written with some style.
+ * modified by kls to use register references instead of arrays
+ * to try to gain a little speed.
+ */
+
+#include <signal.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#define MAXWIDTH 132
+#define MAXREP 10
+
+char buf[MAXREP][MAXWIDTH];
+int maxcol[MAXREP] = {-1};
+int lineno;
+int width = 132; /* default line length */
+int length = 66; /* page length */
+int indent; /* indentation length */
+int npages = 1;
+int literal; /* print control characters */
+char *name; /* user's login name */
+char *host; /* user's machine name */
+char *acctfile; /* accounting information file */
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ register FILE *p = stdin, *o = stdout;
+ register int i, col;
+ register char *cp;
+ int done, linedone, maxrep;
+ char *limit;
+ int ch;
+
+ while (--argc) {
+ if (*(cp = *++argv) == '-') {
+ switch (cp[1]) {
+ case 'n':
+ argc--;
+ name = *++argv;
+ break;
+
+ case 'h':
+ argc--;
+ host = *++argv;
+ break;
+
+ case 'w':
+ if ((i = atoi(&cp[2])) > 0 && i <= MAXWIDTH)
+ width = i;
+ break;
+
+ case 'l':
+ length = atoi(&cp[2]);
+ break;
+
+ case 'i':
+ indent = atoi(&cp[2]);
+ break;
+
+ case 'c': /* Print control chars */
+ literal++;
+ break;
+ }
+ } else
+ acctfile = cp;
+ }
+
+ for (cp = buf[0], limit = buf[MAXREP]; cp < limit; *cp++ = ' ');
+ done = 0;
+
+ while (!done) {
+ col = indent;
+ maxrep = -1;
+ linedone = 0;
+ while (!linedone) {
+ switch (ch = getc(p)) {
+ case EOF:
+ linedone = done = 1;
+ ch = '\n';
+ break;
+
+ case '\f':
+ lineno = length;
+ case '\n':
+ if (maxrep < 0)
+ maxrep = 0;
+ linedone = 1;
+ break;
+
+ case '\b':
+ if (--col < indent)
+ col = indent;
+ break;
+
+ case '\r':
+ col = indent;
+ break;
+
+ case '\t':
+ col = ((col - indent) | 07) + indent + 1;
+ break;
+
+ case '\031':
+ /*
+ * lpd needs to use a different filter to
+ * print data so stop what we are doing and
+ * wait for lpd to restart us.
+ */
+ if ((ch = getchar()) == '\1') {
+ fflush(stdout);
+ kill(getpid(), SIGSTOP);
+ break;
+ } else {
+ ungetc(ch, stdin);
+ ch = '\031';
+ }
+
+ default:
+ if (col >= width || !literal && ch < ' ') {
+ col++;
+ break;
+ }
+ cp = &buf[0][col];
+ for (i = 0; i < MAXREP; i++) {
+ if (i > maxrep)
+ maxrep = i;
+ if (*cp == ' ') {
+ *cp = ch;
+ if (col > maxcol[i])
+ maxcol[i] = col;
+ break;
+ }
+ cp += MAXWIDTH;
+ }
+ col++;
+ break;
+ }
+ }
+
+ /* print out lines */
+ for (i = 0; i <= maxrep; i++) {
+ for (cp = buf[i], limit = cp+maxcol[i]; cp <= limit;) {
+ putc(*cp, o);
+ *cp++ = ' ';
+ }
+ if (i < maxrep)
+ putc('\r', o);
+ else
+ putc(ch, o);
+ if (++lineno >= length) {
+ fflush(o);
+ npages++;
+ lineno = 0;
+ }
+ maxcol[i] = -1;
+ }
+ }
+ if (lineno) { /* be sure to end on a page boundary */
+ putchar('\f');
+ npages++;
+ }
+ if (name && acctfile && access(acctfile, 02) >= 0 &&
+ freopen(acctfile, "a", stdout) != NULL) {
+ printf("%7.2f\t%s:%s\n", (float)npages, host, name);
+ }
+ exit(0);
+}
diff --git a/usr.sbin/lpr/lp/Makefile b/usr.sbin/lpr/lp/Makefile
new file mode 100644
index 0000000..b23be95
--- /dev/null
+++ b/usr.sbin/lpr/lp/Makefile
@@ -0,0 +1,10 @@
+# @(#)Makefile 5.6 (Berkeley) 6/23/90
+
+BINDIR = /usr/bin
+MAN1 = lp.1
+
+beforeinstall:
+ ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \
+ ${.CURDIR}/lp.sh ${DESTDIR}${BINDIR}/lp
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/lpr/lp/lp.1 b/usr.sbin/lpr/lp/lp.1
new file mode 100644
index 0000000..1faeba9
--- /dev/null
+++ b/usr.sbin/lpr/lp/lp.1
@@ -0,0 +1,107 @@
+.\"
+.\" Copyright (c) 1995 Joerg Wunsch
+.\"
+.\" All rights reserved.
+.\"
+.\" This program is free software.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by Joerg Wunsch
+.\" 4. The name of the developer may not be used to endorse or promote
+.\" products derived from this software without specific prior written
+.\" permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+.\" IN NO EVENT SHALL THE DEVELOPERS BE LIABLE FOR ANY DIRECT, INDIRECT,
+.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+.\"
+.\" $Id: lp.1,v 1.5 1997/09/18 06:52:35 charnier Exp $
+.\"
+.Dd January 22, 1995
+.Dt LP 1
+.Os
+.Sh NAME
+.Nm lp
+.Nd front-end to the print spooler
+.Sh SYNOPSIS
+.Nm lp
+.Op Fl c
+.Op Fl d Ar printer
+.Op Fl n Ar num
+.Op Ar name ...
+.Sh DESCRIPTION
+.Nm Lp
+is a front-end to the print spooler as required by the
+.St -p1003.2
+specification. It effectively invokes
+.Xr lpr 1
+with the proper set of arguments.
+
+It generally prints the named files on the destination printer.
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl c
+Make the
+.Nm
+command exit only after further access to any of the input files is no
+longer required. The application can then safely delete or modify the
+files without affecting the output operation.
+.It Fl d Ar dest
+Specify a particular printer. If no
+.Fl d
+is provided on the command line, the contents of the environment
+variables
+.Ev LPDEST
+or
+.Ev PRINTER
+.Pq with this precedence
+are taken as the destination printer.
+.It Fl n Ar num
+Specify that
+.Ar num
+copies of each of the named files shall be printed.
+.El
+.Sh ENVIRONMENT
+As described above, the variables
+.Ev LPDEST
+and
+.Ev PRINTER
+are examined to select the destination printer.
+
+.Sh SEE ALSO
+.Xr lpr 1
+.Sh STANDARDS
+The
+.Nm
+command is expected to comply with the
+.St -p1003.2
+specification.
+.Sh AUTHOR
+This implementation of the
+.Nm
+command has been written by
+.if t J\(:org Wunsch.
+.if n Joerg Wunsch.
+.Sh BUGS
+The
+.St -p1003.2
+specification does not provide any means to print non-text files. It
+rather requires the files to be printed to be text files limited to
+reasonable line lengths and printable characters.
diff --git a/usr.sbin/lpr/lp/lp.sh b/usr.sbin/lpr/lp/lp.sh
new file mode 100644
index 0000000..0a9c8e9
--- /dev/null
+++ b/usr.sbin/lpr/lp/lp.sh
@@ -0,0 +1,71 @@
+#!/bin/sh
+#
+#
+# Copyright (c) 1995 Joerg Wunsch
+#
+# All rights reserved.
+#
+# This program is free software.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. All advertising materials mentioning features or use of this software
+# must display the following acknowledgement:
+# This product includes software developed by Joerg Wunsch
+# 4. The name of the developer may not be used to endorse or promote
+# products derived from this software without specific prior written
+# permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY EXPRESS OR
+# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+# OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+# IN NO EVENT SHALL THE DEVELOPERS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+# NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+# THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+#
+# Posix 1003.2 compliant print spooler interface.
+#
+# $Id$
+#
+
+ncopies=""
+symlink="-s"
+
+# Posix says LPDEST gets precedence over PRINTER
+dest=${LPDEST:-${PRINTER:-lp}}
+
+#
+# XXX We include the -o flag as a dummy. Posix 1003.2 does not require
+# it, but the rationale mentions it as a possible future extension.
+#
+while getopts "cd:n:o:" option
+do
+ case $option in
+
+ c) # copy files before printing
+ symlink="";;
+ d) # destination
+ dest="${OPTARG}";;
+ n) # number of copies
+ ncopies="-#${OPTARG}";;
+ o) # (printer option)
+ : ;;
+ *) # (error msg printed by getopts)
+ exit 2;;
+ esac
+done
+
+shift $(($OPTIND - 1))
+
+exec /usr/bin/lpr "-P${dest}" ${symlink} ${ncopies} "$@"
diff --git a/usr.sbin/lpr/lpc/Makefile b/usr.sbin/lpr/lpc/Makefile
new file mode 100644
index 0000000..4aeeab7
--- /dev/null
+++ b/usr.sbin/lpr/lpc/Makefile
@@ -0,0 +1,12 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= lpc
+CFLAGS+=-I${.CURDIR}/../common_source
+MAN8= lpc.8
+SRCS= lpc.c cmds.c cmdtab.c startdaemon.c common.c
+BINGRP= daemon
+BINMODE=2555
+.PATH: ${.CURDIR}/../common_source
+
+.include "../../Makefile.inc"
+.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..07ac45a
--- /dev/null
+++ b/usr.sbin/lpr/lpc/cmds.c
@@ -0,0 +1,1160 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1983, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)cmds.c 8.2 (Berkeley) 4/28/95";
+#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"
+
+extern uid_t uid, euid;
+
+static void abortpr __P((int));
+static void cleanpr __P((void));
+static void disablepr __P((void));
+static int doarg __P((char *));
+static int doselect __P((struct dirent *));
+static void enablepr __P((void));
+static void prstat __P((void));
+static void putmsg __P((int, char **));
+static int sortq __P((const void *, const void *));
+static void startpr __P((int));
+static void stoppr __P((void));
+static int touch __P((struct queue *));
+static void unlinkf __P((char *));
+static void upstat __P((char *));
+
+/*
+ * kill an existing daemon and disable printing.
+ */
+void
+doabort(argc, argv)
+ int argc;
+ char *argv[];
+{
+ register int c, status;
+ register char *cp1, *cp2;
+ char prbuf[100];
+
+ if (argc == 1) {
+ printf("Usage: abort {all | printer ...}\n");
+ return;
+ }
+ if (argc == 2 && !strcmp(argv[1], "all")) {
+ printer = prbuf;
+ while (cgetnext(&bp, printcapdb) > 0) {
+ cp1 = prbuf;
+ cp2 = bp;
+ while ((c = *cp2++) && c != '|' && c != ':' &&
+ (cp1 - prbuf) < sizeof(prbuf))
+ *cp1++ = c;
+ *cp1 = '\0';
+ abortpr(1);
+ }
+ return;
+ }
+ while (--argc) {
+ printer = *++argv;
+ if ((status = cgetent(&bp, printcapdb, printer)) == -2) {
+ printf("cannot open printer description file\n");
+ continue;
+ } else if (status == -1) {
+ printf("unknown printer %s\n", printer);
+ continue;
+ } else if (status == -3)
+ fatal("potential reference loop detected in printcap file");
+ abortpr(1);
+ }
+}
+
+static void
+abortpr(dis)
+ int dis;
+{
+ register FILE *fp;
+ struct stat stbuf;
+ int pid, fd;
+
+ if (cgetstr(bp, "sd", &SD) == -1)
+ SD = _PATH_DEFSPOOL;
+ if (cgetstr(bp, "lo", &LO) == -1)
+ LO = DEFLOCK;
+ (void) snprintf(line, sizeof(line), "%s/%s", SD, LO);
+ printf("%s:\n", printer);
+
+ /*
+ * Turn on the owner execute bit of the lock file to disable printing.
+ */
+ if (dis) {
+ seteuid(euid);
+ if (stat(line, &stbuf) >= 0) {
+ if (chmod(line, (stbuf.st_mode & 0777) | 0100) < 0)
+ printf("\tcannot disable printing\n");
+ else {
+ upstat("printing disabled\n");
+ printf("\tprinting disabled\n");
+ }
+ } else if (errno == ENOENT) {
+ if ((fd = open(line, O_WRONLY|O_CREAT, 0760)) < 0)
+ printf("\tcannot create lock file\n");
+ else {
+ (void) close(fd);
+ upstat("printing disabled\n");
+ printf("\tprinting disabled\n");
+ printf("\tno daemon to abort\n");
+ }
+ goto out;
+ } else {
+ printf("\tcannot stat lock file\n");
+ goto out;
+ }
+ }
+ /*
+ * Kill the current daemon to stop printing now.
+ */
+ if ((fp = fopen(line, "r")) == NULL) {
+ printf("\tcannot open lock file\n");
+ goto out;
+ }
+ if (!getline(fp) || flock(fileno(fp), LOCK_SH|LOCK_NB) == 0) {
+ (void) fclose(fp); /* unlocks as well */
+ printf("\tno daemon to abort\n");
+ goto out;
+ }
+ (void) fclose(fp);
+ if (kill(pid = atoi(line), SIGTERM) < 0) {
+ if (errno == ESRCH)
+ printf("\tno daemon to abort\n");
+ else
+ printf("\tWarning: daemon (pid %d) not killed\n", pid);
+ } else
+ printf("\tdaemon (pid %d) killed\n", pid);
+out:
+ seteuid(uid);
+}
+
+/*
+ * Write a message into the status file.
+ */
+static void
+upstat(msg)
+ char *msg;
+{
+ register int fd;
+ char statfile[MAXPATHLEN];
+
+ if (cgetstr(bp, "st", &ST) == -1)
+ ST = DEFSTAT;
+ (void) snprintf(statfile, sizeof(statfile), "%s/%s", SD, ST);
+ umask(0);
+ fd = open(statfile, O_WRONLY|O_CREAT, 0664);
+ if (fd < 0 || flock(fd, LOCK_EX) < 0) {
+ printf("\tcannot create status file\n");
+ return;
+ }
+ (void) ftruncate(fd, 0);
+ if (msg == (char *)NULL)
+ (void) write(fd, "\n", 1);
+ else
+ (void) write(fd, msg, strlen(msg));
+ (void) close(fd);
+}
+
+/*
+ * Remove all spool files and temporaries from the spooling area.
+ */
+void
+clean(argc, argv)
+ int argc;
+ char *argv[];
+{
+ register int c, status;
+ register char *cp1, *cp2;
+ char prbuf[100];
+
+ if (argc == 1) {
+ printf("Usage: clean {all | printer ...}\n");
+ return;
+ }
+ if (argc == 2 && !strcmp(argv[1], "all")) {
+ printer = prbuf;
+ while (cgetnext(&bp, printcapdb) > 0) {
+ cp1 = prbuf;
+ cp2 = bp;
+ while ((c = *cp2++) && c != '|' && c != ':' &&
+ (cp1 - prbuf) < sizeof(prbuf))
+ *cp1++ = c;
+ *cp1 = '\0';
+ cleanpr();
+ }
+ return;
+ }
+ while (--argc) {
+ printer = *++argv;
+ if ((status = cgetent(&bp, printcapdb, printer)) == -2) {
+ printf("cannot open printer description file\n");
+ continue;
+ } else if (status == -1) {
+ printf("unknown printer %s\n", printer);
+ continue;
+ } else if (status == -3)
+ fatal("potential reference loop detected in printcap file");
+
+ cleanpr();
+ }
+}
+
+static int
+doselect(d)
+ struct dirent *d;
+{
+ int c = d->d_name[0];
+
+ if ((c == 't' || c == 'c' || c == 'd') && d->d_name[1] == 'f')
+ return(1);
+ return(0);
+}
+
+/*
+ * Comparison routine for scandir. Sort by job number and machine, then
+ * by `cf', `tf', or `df', then by the sequence letter A-Z, a-z.
+ */
+static int
+sortq(a, b)
+ const void *a, *b;
+{
+ struct dirent **d1, **d2;
+ int c1, c2;
+
+ d1 = (struct dirent **)a;
+ d2 = (struct dirent **)b;
+ if ((c1 = strcmp((*d1)->d_name + 3, (*d2)->d_name + 3)))
+ return(c1);
+ c1 = (*d1)->d_name[0];
+ c2 = (*d2)->d_name[0];
+ if (c1 == c2)
+ return((*d1)->d_name[2] - (*d2)->d_name[2]);
+ if (c1 == 'c')
+ return(-1);
+ if (c1 == 'd' || c2 == 'c')
+ return(1);
+ return(-1);
+}
+
+/*
+ * Remove incomplete jobs from spooling area.
+ */
+static void
+cleanpr()
+{
+ register int i, n;
+ register char *cp, *cp1, *lp;
+ struct dirent **queue;
+ int nitems;
+
+ if (cgetstr(bp, "sd", &SD) == -1)
+ SD = _PATH_DEFSPOOL;
+ printf("%s:\n", printer);
+
+ for (lp = line, cp = SD; (lp - line) < sizeof(line) && (*lp++ = *cp++);)
+ ;
+ lp[-1] = '/';
+
+ seteuid(euid);
+ nitems = scandir(SD, &queue, doselect, sortq);
+ seteuid(uid);
+ if (nitems < 0) {
+ printf("\tcannot examine spool directory\n");
+ return;
+ }
+ if (nitems == 0)
+ return;
+ i = 0;
+ do {
+ cp = queue[i]->d_name;
+ if (*cp == 'c') {
+ n = 0;
+ while (i + 1 < nitems) {
+ cp1 = queue[i + 1]->d_name;
+ if (*cp1 != 'd' || strcmp(cp + 3, cp1 + 3))
+ break;
+ i++;
+ n++;
+ }
+ if (n == 0) {
+ strncpy(lp, cp, sizeof(line) - strlen(line) - 1);
+ line[sizeof(line) - 1] = '\0';
+ unlinkf(line);
+ }
+ } else {
+ /*
+ * Must be a df with no cf (otherwise, it would have
+ * been skipped above) or a tf file (which can always
+ * be removed).
+ */
+ strncpy(lp, cp, sizeof(line) - strlen(line) - 1);
+ line[sizeof(line) - 1] = '\0';
+ unlinkf(line);
+ }
+ } while (++i < nitems);
+}
+
+static void
+unlinkf(name)
+ char *name;
+{
+ seteuid(euid);
+ if (unlink(name) < 0)
+ printf("\tcannot remove %s\n", name);
+ else
+ printf("\tremoved %s\n", name);
+ seteuid(uid);
+}
+
+/*
+ * Enable queuing to the printer (allow lpr's).
+ */
+void
+enable(argc, argv)
+ int argc;
+ char *argv[];
+{
+ register int c, status;
+ register char *cp1, *cp2;
+ char prbuf[100];
+
+ if (argc == 1) {
+ printf("Usage: enable {all | printer ...}\n");
+ return;
+ }
+ if (argc == 2 && !strcmp(argv[1], "all")) {
+ printer = prbuf;
+ while (cgetnext(&bp, printcapdb) > 0) {
+ cp1 = prbuf;
+ cp2 = bp;
+ while ((c = *cp2++) && c != '|' && c != ':' &&
+ (cp1 - prbuf) < sizeof(prbuf))
+ *cp1++ = c;
+ *cp1 = '\0';
+ enablepr();
+ }
+ return;
+ }
+ while (--argc) {
+ printer = *++argv;
+ if ((status = cgetent(&bp, printcapdb, printer)) == -2) {
+ printf("cannot open printer description file\n");
+ continue;
+ } else if (status == -1) {
+ printf("unknown printer %s\n", printer);
+ continue;
+ } else if (status == -3)
+ fatal("potential reference loop detected in printcap file");
+
+ enablepr();
+ }
+}
+
+static void
+enablepr()
+{
+ struct stat stbuf;
+
+ if (cgetstr(bp, "sd", &SD) == -1)
+ SD = _PATH_DEFSPOOL;
+ if (cgetstr(bp, "lo", &LO) == -1)
+ LO = DEFLOCK;
+ (void) snprintf(line, sizeof(line), "%s/%s", SD, LO);
+ printf("%s:\n", printer);
+
+ /*
+ * Turn off the group execute bit of the lock file to enable queuing.
+ */
+ seteuid(euid);
+ if (stat(line, &stbuf) >= 0) {
+ if (chmod(line, stbuf.st_mode & 0767) < 0)
+ printf("\tcannot enable queuing\n");
+ else
+ printf("\tqueuing enabled\n");
+ }
+ seteuid(uid);
+}
+
+/*
+ * Disable queuing.
+ */
+void
+disable(argc, argv)
+ int argc;
+ char *argv[];
+{
+ register int c, status;
+ register char *cp1, *cp2;
+ char prbuf[100];
+
+ if (argc == 1) {
+ printf("Usage: disable {all | printer ...}\n");
+ return;
+ }
+ if (argc == 2 && !strcmp(argv[1], "all")) {
+ printer = prbuf;
+ while (cgetnext(&bp, printcapdb) > 0) {
+ cp1 = prbuf;
+ cp2 = bp;
+ while ((c = *cp2++) && c != '|' && c != ':' &&
+ (cp1 - prbuf) < sizeof(prbuf))
+ *cp1++ = c;
+ *cp1 = '\0';
+ disablepr();
+ }
+ return;
+ }
+ while (--argc) {
+ printer = *++argv;
+ if ((status = cgetent(&bp, printcapdb, printer)) == -2) {
+ printf("cannot open printer description file\n");
+ continue;
+ } else if (status == -1) {
+ printf("unknown printer %s\n", printer);
+ continue;
+ } else if (status == -3)
+ fatal("potential reference loop detected in printcap file");
+
+ disablepr();
+ }
+}
+
+static void
+disablepr()
+{
+ register int fd;
+ struct stat stbuf;
+
+ if (cgetstr(bp, "sd", &SD) == -1)
+ SD = _PATH_DEFSPOOL;
+ if (cgetstr(bp, "lo", &LO) == -1)
+ LO = DEFLOCK;
+ (void) snprintf(line, sizeof(line), "%s/%s", SD, LO);
+ printf("%s:\n", printer);
+ /*
+ * Turn on the group execute bit of the lock file to disable queuing.
+ */
+ seteuid(euid);
+ if (stat(line, &stbuf) >= 0) {
+ if (chmod(line, (stbuf.st_mode & 0777) | 010) < 0)
+ printf("\tcannot disable queuing\n");
+ else
+ printf("\tqueuing disabled\n");
+ } else if (errno == ENOENT) {
+ if ((fd = open(line, O_WRONLY|O_CREAT, 0670)) < 0)
+ printf("\tcannot create lock file\n");
+ else {
+ (void) close(fd);
+ printf("\tqueuing disabled\n");
+ }
+ } else
+ printf("\tcannot stat lock file\n");
+ seteuid(uid);
+}
+
+/*
+ * Disable queuing and printing and put a message into the status file
+ * (reason for being down).
+ */
+void
+down(argc, argv)
+ int argc;
+ char *argv[];
+{
+ register int c, status;
+ register char *cp1, *cp2;
+ char prbuf[100];
+
+ if (argc == 1) {
+ printf("Usage: down {all | printer} [message ...]\n");
+ return;
+ }
+ if (!strcmp(argv[1], "all")) {
+ printer = prbuf;
+ while (cgetnext(&bp, printcapdb) > 0) {
+ cp1 = prbuf;
+ cp2 = bp;
+ while ((c = *cp2++) && c != '|' && c != ':' &&
+ (cp1 - prbuf) < sizeof(prbuf))
+ *cp1++ = c;
+ *cp1 = '\0';
+ putmsg(argc - 2, argv + 2);
+ }
+ return;
+ }
+ printer = argv[1];
+ if ((status = cgetent(&bp, printcapdb, printer)) == -2) {
+ printf("cannot open printer description file\n");
+ return;
+ } else if (status == -1) {
+ printf("unknown printer %s\n", printer);
+ return;
+ } else if (status == -3)
+ fatal("potential reference loop detected in printcap file");
+
+ putmsg(argc - 2, argv + 2);
+}
+
+static void
+putmsg(argc, argv)
+ int argc;
+ char **argv;
+{
+ register int fd;
+ register char *cp1, *cp2;
+ char buf[1024];
+ struct stat stbuf;
+
+ if (cgetstr(bp, "sd", &SD) == -1)
+ SD = _PATH_DEFSPOOL;
+ if (cgetstr(bp, "lo", &LO) == -1)
+ LO = DEFLOCK;
+ if (cgetstr(bp, "st", &ST) == -1)
+ ST = DEFSTAT;
+ printf("%s:\n", printer);
+ /*
+ * Turn on the group execute bit of the lock file to disable queuing and
+ * turn on the owner execute bit of the lock file to disable printing.
+ */
+ (void) snprintf(line, sizeof(line), "%s/%s", SD, LO);
+ seteuid(euid);
+ if (stat(line, &stbuf) >= 0) {
+ if (chmod(line, (stbuf.st_mode & 0777) | 0110) < 0)
+ printf("\tcannot disable queuing\n");
+ else
+ printf("\tprinter and queuing disabled\n");
+ } else if (errno == ENOENT) {
+ if ((fd = open(line, O_WRONLY|O_CREAT, 0770)) < 0)
+ printf("\tcannot create lock file\n");
+ else {
+ (void) close(fd);
+ printf("\tprinter and queuing disabled\n");
+ }
+ seteuid(uid);
+ return;
+ } else
+ printf("\tcannot stat lock file\n");
+ /*
+ * Write the message into the status file.
+ */
+ (void) snprintf(line, sizeof(line), "%s/%s", SD, ST);
+ fd = open(line, O_WRONLY|O_CREAT, 0664);
+ if (fd < 0 || flock(fd, LOCK_EX) < 0) {
+ printf("\tcannot create status file\n");
+ seteuid(uid);
+ return;
+ }
+ seteuid(uid);
+ (void) ftruncate(fd, 0);
+ if (argc <= 0) {
+ (void) write(fd, "\n", 1);
+ (void) close(fd);
+ return;
+ }
+ cp1 = buf;
+ while (--argc >= 0) {
+ cp2 = *argv++;
+ while ((cp1 - buf) < sizeof(buf) && (*cp1++ = *cp2++))
+ ;
+ cp1[-1] = ' ';
+ }
+ cp1[-1] = '\n';
+ *cp1 = '\0';
+ (void) write(fd, buf, strlen(buf));
+ (void) close(fd);
+}
+
+/*
+ * Exit lpc
+ */
+void
+quit(argc, argv)
+ int argc;
+ char *argv[];
+{
+ exit(0);
+}
+
+/*
+ * Kill and restart the daemon.
+ */
+void
+restart(argc, argv)
+ int argc;
+ char *argv[];
+{
+ register int c, status;
+ register char *cp1, *cp2;
+ char prbuf[100];
+
+ if (argc == 1) {
+ printf("Usage: restart {all | printer ...}\n");
+ return;
+ }
+ if (argc == 2 && !strcmp(argv[1], "all")) {
+ printer = prbuf;
+ while (cgetnext(&bp, printcapdb) > 0) {
+ cp1 = prbuf;
+ cp2 = bp;
+ while ((c = *cp2++) && c != '|' && c != ':' &&
+ (cp1 - prbuf) < sizeof(prbuf))
+ *cp1++ = c;
+ *cp1 = '\0';
+ abortpr(0);
+ startpr(0);
+ }
+ return;
+ }
+ while (--argc) {
+ printer = *++argv;
+ if ((status = cgetent(&bp, printcapdb, printer)) == -2) {
+ printf("cannot open printer description file\n");
+ continue;
+ } else if (status == -1) {
+ printf("unknown printer %s\n", printer);
+ continue;
+ } else if (status == -3)
+ fatal("potential reference loop detected in printcap file");
+
+ abortpr(0);
+ startpr(0);
+ }
+}
+
+/*
+ * Enable printing on the specified printer and startup the daemon.
+ */
+void
+startcmd(argc, argv)
+ int argc;
+ char *argv[];
+{
+ register int c, status;
+ register char *cp1, *cp2;
+ char prbuf[100];
+
+ if (argc == 1) {
+ printf("Usage: start {all | printer ...}\n");
+ return;
+ }
+ if (argc == 2 && !strcmp(argv[1], "all")) {
+ printer = prbuf;
+ while (cgetnext(&bp, printcapdb) > 0) {
+ cp1 = prbuf;
+ cp2 = bp;
+ while ((c = *cp2++) && c != '|' && c != ':' &&
+ (cp1 - prbuf) < sizeof(prbuf))
+ *cp1++ = c;
+ *cp1 = '\0';
+ startpr(1);
+ }
+ return;
+ }
+ while (--argc) {
+ printer = *++argv;
+ if ((status = cgetent(&bp, printcapdb, printer)) == -2) {
+ printf("cannot open printer description file\n");
+ continue;
+ } else if (status == -1) {
+ printf("unknown printer %s\n", printer);
+ continue;
+ } else if (status == -3)
+ fatal("potential reference loop detected in printcap file");
+
+ startpr(1);
+ }
+}
+
+static void
+startpr(enable)
+ int enable;
+{
+ struct stat stbuf;
+
+ if (cgetstr(bp, "sd", &SD) == -1)
+ SD = _PATH_DEFSPOOL;
+ if (cgetstr(bp, "lo", &LO) == -1)
+ LO = DEFLOCK;
+ (void) snprintf(line, sizeof(line), "%s/%s", SD, LO);
+ printf("%s:\n", printer);
+
+ /*
+ * Turn off the owner execute bit of the lock file to enable printing.
+ */
+ seteuid(euid);
+ if (enable && stat(line, &stbuf) >= 0) {
+ if (chmod(line, stbuf.st_mode & (enable==2 ? 0666 : 0677)) < 0)
+ printf("\tcannot enable printing\n");
+ else
+ printf("\tprinting enabled\n");
+ }
+ if (!startdaemon(printer))
+ printf("\tcouldn't start daemon\n");
+ else
+ printf("\tdaemon started\n");
+ seteuid(uid);
+}
+
+/*
+ * Print the status of each queue listed or all the queues.
+ */
+void
+status(argc, argv)
+ int argc;
+ char *argv[];
+{
+ register int c, status;
+ register char *cp1, *cp2;
+ char prbuf[100];
+
+ if (argc == 1 || argc == 2 && !strcmp(argv[1], "all")) {
+ printer = prbuf;
+ while (cgetnext(&bp, printcapdb) > 0) {
+ cp1 = prbuf;
+ cp2 = bp;
+ while ((c = *cp2++) && c != '|' && c != ':' &&
+ (cp1 - prbuf) < sizeof(prbuf))
+ *cp1++ = c;
+ *cp1 = '\0';
+ prstat();
+ }
+ return;
+ }
+ while (--argc) {
+ printer = *++argv;
+ if ((status = cgetent(&bp, printcapdb, printer)) == -2) {
+ printf("cannot open printer description file\n");
+ continue;
+ } else if (status == -1) {
+ printf("unknown printer %s\n", printer);
+ continue;
+ } else if (status == -3)
+ fatal("potential reference loop detected in printcap file");
+
+ prstat();
+ }
+}
+
+/*
+ * Print the status of the printer queue.
+ */
+static void
+prstat()
+{
+ struct stat stbuf;
+ register int fd, i;
+ register struct dirent *dp;
+ DIR *dirp;
+
+ if (cgetstr(bp, "sd", &SD) == -1)
+ SD = _PATH_DEFSPOOL;
+ if (cgetstr(bp, "lo", &LO) == -1)
+ LO = DEFLOCK;
+ if (cgetstr(bp, "st", &ST) == -1)
+ ST = DEFSTAT;
+ printf("%s:\n", printer);
+ (void) snprintf(line, sizeof(line), "%s/%s", SD, LO);
+ if (stat(line, &stbuf) >= 0) {
+ printf("\tqueuing is %s\n",
+ (stbuf.st_mode & 010) ? "disabled" : "enabled");
+ printf("\tprinting is %s\n",
+ (stbuf.st_mode & 0100) ? "disabled" : "enabled");
+ } else {
+ printf("\tqueuing is enabled\n");
+ printf("\tprinting is enabled\n");
+ }
+ if ((dirp = opendir(SD)) == 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\n");
+ else if (i == 1)
+ printf("\t1 entry in spool area\n");
+ else
+ printf("\t%d entries in spool area\n", i);
+ fd = open(line, 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 */
+ (void) snprintf(line, sizeof(line), "%s/%s", SD, ST);
+ fd = open(line, O_RDONLY);
+ if (fd >= 0) {
+ (void) flock(fd, LOCK_SH);
+ (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(argc, argv)
+ int argc;
+ char *argv[];
+{
+ register int c, status;
+ register char *cp1, *cp2;
+ char prbuf[100];
+
+ if (argc == 1) {
+ printf("Usage: stop {all | printer ...}\n");
+ return;
+ }
+ if (argc == 2 && !strcmp(argv[1], "all")) {
+ printer = prbuf;
+ while (cgetnext(&bp, printcapdb) > 0) {
+ cp1 = prbuf;
+ cp2 = bp;
+ while ((c = *cp2++) && c != '|' && c != ':' &&
+ (cp1 - prbuf) < sizeof(prbuf))
+ *cp1++ = c;
+ *cp1 = '\0';
+ stoppr();
+ }
+ return;
+ }
+ while (--argc) {
+ printer = *++argv;
+ if ((status = cgetent(&bp, printcapdb, printer)) == -2) {
+ printf("cannot open printer description file\n");
+ continue;
+ } else if (status == -1) {
+ printf("unknown printer %s\n", printer);
+ continue;
+ } else if (status == -3)
+ fatal("potential reference loop detected in printcap file");
+
+ stoppr();
+ }
+}
+
+static void
+stoppr()
+{
+ register int fd;
+ struct stat stbuf;
+
+ if (cgetstr(bp, "sd", &SD) == -1)
+ SD = _PATH_DEFSPOOL;
+ if (cgetstr(bp, "lo", &LO) == -1)
+ LO = DEFLOCK;
+ (void) snprintf(line, sizeof(line), "%s/%s", SD, LO);
+ printf("%s:\n", printer);
+
+ /*
+ * Turn on the owner execute bit of the lock file to disable printing.
+ */
+ seteuid(euid);
+ if (stat(line, &stbuf) >= 0) {
+ if (chmod(line, (stbuf.st_mode & 0777) | 0100) < 0)
+ printf("\tcannot disable printing\n");
+ else {
+ upstat("printing disabled\n");
+ printf("\tprinting disabled\n");
+ }
+ } else if (errno == ENOENT) {
+ if ((fd = open(line, O_WRONLY|O_CREAT, 0760)) < 0)
+ printf("\tcannot create lock file\n");
+ else {
+ (void) close(fd);
+ upstat("printing disabled\n");
+ printf("\tprinting disabled\n");
+ }
+ } else
+ printf("\tcannot stat lock file\n");
+ seteuid(uid);
+}
+
+struct queue **queue;
+int nitems;
+time_t mtime;
+
+/*
+ * Put the specified jobs at the top of printer queue.
+ */
+void
+topq(argc, argv)
+ int argc;
+ char *argv[];
+{
+ register int i;
+ struct stat stbuf;
+ int status, changed;
+
+ if (argc < 3) {
+ printf("Usage: topq printer [jobnum ...] [user ...]\n");
+ return;
+ }
+
+ --argc;
+ printer = *++argv;
+ status = cgetent(&bp, printcapdb, printer);
+ if (status == -2) {
+ printf("cannot open printer description file\n");
+ return;
+ } else if (status == -1) {
+ printf("%s: unknown printer\n", printer);
+ return;
+ } else if (status == -3)
+ fatal("potential reference loop detected in printcap file");
+
+ if (cgetstr(bp, "sd", &SD) == -1)
+ SD = _PATH_DEFSPOOL;
+ if (cgetstr(bp, "lo", &LO) == -1)
+ LO = DEFLOCK;
+ printf("%s:\n", printer);
+
+ seteuid(euid);
+ if (chdir(SD) < 0) {
+ printf("\tcannot chdir to %s\n", SD);
+ goto out;
+ }
+ seteuid(uid);
+ nitems = getq(&queue);
+ if (nitems == 0)
+ return;
+ changed = 0;
+ mtime = queue[0]->q_time;
+ for (i = argc; --i; ) {
+ if (doarg(argv[i]) == 0) {
+ printf("\tjob %s is not in the queue\n", argv[i]);
+ continue;
+ } else
+ changed++;
+ }
+ for (i = 0; i < nitems; i++)
+ free(queue[i]);
+ free(queue);
+ if (!changed) {
+ printf("\tqueue order unchanged\n");
+ return;
+ }
+ /*
+ * Turn on the public execute bit of the lock file to
+ * get lpd to rebuild the queue after the current job.
+ */
+ seteuid(euid);
+ if (changed && stat(LO, &stbuf) >= 0)
+ (void) chmod(LO, (stbuf.st_mode & 0777) | 01);
+
+out:
+ seteuid(uid);
+}
+
+/*
+ * Reposition the job by changing the modification time of
+ * the control file.
+ */
+static int
+touch(q)
+ struct queue *q;
+{
+ struct timeval tvp[2];
+ int ret;
+
+ tvp[0].tv_sec = tvp[1].tv_sec = --mtime;
+ tvp[0].tv_usec = tvp[1].tv_usec = 0;
+ seteuid(euid);
+ ret = utimes(q->q_name, tvp);
+ seteuid(uid);
+ return (ret);
+}
+
+/*
+ * Checks if specified job name is in the printer's queue.
+ * Returns: negative (-1) if argument name is not in the queue.
+ */
+static int
+doarg(job)
+ char *job;
+{
+ register struct queue **qq;
+ register int jobnum, n;
+ register char *cp, *machine;
+ int cnt = 0;
+ FILE *fp;
+
+ /*
+ * Look for a job item consisting of system name, colon, number
+ * (example: ucbarpa:114)
+ */
+ if ((cp = strchr(job, ':')) != NULL) {
+ machine = job;
+ *cp++ = '\0';
+ job = cp;
+ } else
+ machine = NULL;
+
+ /*
+ * Check for job specified by number (example: 112 or 235ucbarpa).
+ */
+ if (isdigit(*job)) {
+ jobnum = 0;
+ do
+ jobnum = jobnum * 10 + (*job++ - '0');
+ while (isdigit(*job));
+ for (qq = queue + nitems; --qq >= queue; ) {
+ n = 0;
+ for (cp = (*qq)->q_name+3; isdigit(*cp); )
+ n = n * 10 + (*cp++ - '0');
+ if (jobnum != n)
+ continue;
+ if (*job && strcmp(job, cp) != 0)
+ continue;
+ if (machine != NULL && strcmp(machine, cp) != 0)
+ continue;
+ if (touch(*qq) == 0) {
+ printf("\tmoved %s\n", (*qq)->q_name);
+ cnt++;
+ }
+ }
+ return(cnt);
+ }
+ /*
+ * Process item consisting of owner's name (example: henry).
+ */
+ for (qq = queue + nitems; --qq >= queue; ) {
+ seteuid(euid);
+ fp = fopen((*qq)->q_name, "r");
+ seteuid(uid);
+ if (fp == NULL)
+ continue;
+ while (getline(fp) > 0)
+ if (line[0] == 'P')
+ break;
+ (void) fclose(fp);
+ if (line[0] != 'P' || strcmp(job, line+1) != 0)
+ continue;
+ if (touch(*qq) == 0) {
+ printf("\tmoved %s\n", (*qq)->q_name);
+ cnt++;
+ }
+ }
+ return(cnt);
+}
+
+/*
+ * Enable everything and start printer (undo `down').
+ */
+void
+up(argc, argv)
+ int argc;
+ char *argv[];
+{
+ register int c, status;
+ register char *cp1, *cp2;
+ char prbuf[100];
+
+ if (argc == 1) {
+ printf("Usage: up {all | printer ...}\n");
+ return;
+ }
+ if (argc == 2 && !strcmp(argv[1], "all")) {
+ printer = prbuf;
+ while (cgetnext(&bp, printcapdb) > 0) {
+ cp1 = prbuf;
+ cp2 = bp;
+ while ((c = *cp2++) && c != '|' && c != ':' &&
+ (cp1 - prbuf) < sizeof(prbuf))
+ *cp1++ = c;
+ *cp1 = '\0';
+ startpr(2);
+ }
+ return;
+ }
+ while (--argc) {
+ printer = *++argv;
+ if ((status = cgetent(&bp, printcapdb, printer)) == -2) {
+ printf("cannot open printer description file\n");
+ continue;
+ } else if (status == -1) {
+ printf("unknown printer %s\n", printer);
+ continue;
+ } else if (status == -3)
+ fatal("potential reference loop detected in printcap file");
+
+ startpr(2);
+ }
+}
diff --git a/usr.sbin/lpr/lpc/cmdtab.c b/usr.sbin/lpr/lpc/cmdtab.c
new file mode 100644
index 0000000..7619791
--- /dev/null
+++ b/usr.sbin/lpr/lpc/cmdtab.c
@@ -0,0 +1,79 @@
+/*
+ * 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";
+#endif /* not lint */
+
+#include <sys/cdefs.h>
+
+#include "lpc.h"
+#include "extern.h"
+
+/*
+ * lpc -- command tables
+ */
+char aborthelp[] = "terminate a spooling daemon immediately and disable printing";
+char cleanhelp[] = "remove cruft files from a queue";
+char enablehelp[] = "turn a spooling queue on";
+char disablehelp[] = "turn a spooling queue off";
+char downhelp[] = "do a 'stop' followed by 'disable' and put a message in status";
+char helphelp[] = "get help on commands";
+char quithelp[] = "exit lpc";
+char restarthelp[] = "kill (if possible) and restart a spooling daemon";
+char starthelp[] = "enable printing and start a spooling daemon";
+char statushelp[] = "show status of daemon and queue";
+char stophelp[] = "stop a spooling daemon after current job completes and disable printing";
+char topqhelp[] = "put job at top of printer queue";
+char uphelp[] = "enable everything and restart spooling daemon";
+
+struct cmd cmdtab[] = {
+ { "abort", aborthelp, doabort, 1 },
+ { "clean", cleanhelp, clean, 1 },
+ { "enable", enablehelp, enable, 1 },
+ { "exit", quithelp, quit, 0 },
+ { "disable", disablehelp, disable, 1 },
+ { "down", downhelp, down, 1 },
+ { "help", helphelp, help, 0 },
+ { "quit", quithelp, quit, 0 },
+ { "restart", restarthelp, restart, 0 },
+ { "start", starthelp, startcmd, 1 },
+ { "status", statushelp, status, 0 },
+ { "stop", stophelp, stop, 1 },
+ { "topq", topqhelp, topq, 1 },
+ { "up", uphelp, up, 1 },
+ { "?", helphelp, help, 0 },
+ { 0 },
+};
+
+int NCMDS = sizeof (cmdtab) / sizeof (cmdtab[0]);
diff --git a/usr.sbin/lpr/lpc/extern.h b/usr.sbin/lpr/lpc/extern.h
new file mode 100644
index 0000000..1b807b1
--- /dev/null
+++ b/usr.sbin/lpr/lpc/extern.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)extern.h 8.1 (Berkeley) 6/6/93
+ */
+
+
+#include <sys/cdefs.h>
+
+
+__BEGIN_DECLS
+void clean __P((int, char **));
+void disable __P((int, char **));
+void doabort __P((int, char **));
+void down __P((int, char **));
+void enable __P((int, char **));
+void help __P((int, char **));
+void quit __P((int, char **));
+void restart __P((int, char **));
+void startcmd __P((int, char **));
+void status __P((int, char **));
+void stop __P((int, char **));
+void topq __P((int, char **));
+void up __P((int, char **));
+__END_DECLS
+
+extern int NCMDS;
+extern struct cmd cmdtab[];
diff --git a/usr.sbin/lpr/lpc/lpc.8 b/usr.sbin/lpr/lpc/lpc.8
new file mode 100644
index 0000000..b3a0c52
--- /dev/null
+++ b/usr.sbin/lpr/lpc/lpc.8
@@ -0,0 +1,175 @@
+.\" Copyright (c) 1983, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)lpc.8 8.5 (Berkeley) 4/28/95
+.\"
+.Dd April 28, 1995
+.Dt LPC 8
+.Os BSD 4.2
+.Sh NAME
+.Nm lpc
+.Nd line printer control program
+.Sh SYNOPSIS
+.Nm lpc
+.Oo
+.Ar command
+.Op Ar argument ...
+.Oc
+.Sh DESCRIPTION
+.Nm Lpc
+is used by the system administrator to control the
+operation of the line printer system.
+For each line printer configured in
+.Pa /etc/printcap ,
+.Nm lpc
+may be used to:
+.Bl -bullet -offset indent
+.It
+disable or enable a printer,
+.It
+disable or enable a printer's spooling queue,
+.It
+rearrange the order of jobs in a spooling queue,
+.It
+find the status of printers, and their associated
+spooling queues and printer daemons.
+.El
+.Pp
+Without any arguments,
+.Nm
+will prompt for commands from the standard input.
+If arguments are supplied,
+.Nm
+interprets the first argument as a command and the remaining
+arguments as parameters to the command. The standard input
+may be redirected causing
+.Nm
+to read commands from file.
+Commands may be abbreviated;
+the following is the list of recognized commands.
+.Pp
+.Bl -tag -width Ds -compact
+.It Ic \&? No [ command ... ]
+.It Ic help No [ command ... ]
+Print a short description of each command specified in the argument list,
+or, if no argument is given, a list of the recognized commands.
+.Pp
+.It Ic abort No {\ all\ |\ printer\ }
+Terminate an active spooling daemon on the local host immediately and
+then disable printing (preventing new daemons from being started by
+.Xr lpr 1 )
+for the specified printers.
+.Pp
+.It Ic clean No {\ all\ |\ printer\ }
+Remove any temporary files, data files, and control files that cannot
+be printed (i.e., do not form a complete printer job)
+from the specified printer queue(s) on the local machine.
+.Pp
+.It Ic disable No {\ all\ |\ printer\ }
+Turn the specified printer queues off. This prevents new
+printer jobs from being entered into the queue by
+.Xr lpr 1 .
+.Pp
+.It Ic down No {\ all\ |\ printer\ } message ...
+Turn the specified printer queue off, disable printing and put
+.Em message
+in the printer status file. The message doesn't need to be quoted, the
+remaining arguments are treated like
+.Xr echo 1 .
+This is normally used to take a printer down and let others know why
+.Xr lpq 1
+will indicate the printer is down and print the status message).
+.Pp
+.It Ic enable No {\ all\ |\ printer\ }
+Enable spooling on the local queue for the listed printers.
+This will allow
+.Xr lpr 1
+to put new jobs in the spool queue.
+.Pp
+.It Ic exit
+.It Ic quit
+Exit from lpc.
+.\" ne 1i
+.Pp
+.It Ic restart No {\ all\ |\ printer\ }
+Attempt to start a new printer daemon.
+This is useful when some abnormal condition causes the daemon to
+die unexpectedly, leaving jobs in the queue.
+.Xr Lpq 1
+will report that there is no daemon present when this condition occurs.
+If the user is the super-user,
+try to abort the current daemon first (i.e., kill and restart a stuck daemon).
+.Pp
+.It Ic start No {\ all\ |\ printer\ }
+Enable printing and start a spooling daemon for the listed printers.
+.Pp
+.It Ic status No {\ all\ |\ printer\ }
+Display the status of daemons and queues on the local machine.
+.Pp
+.It Ic stop No {\ all\ |\ printer\ }
+Stop a spooling daemon after the current job completes and disable
+printing.
+.Pp
+.It Ic topq No printer\ [\ jobnum\ ...\ ]\ [\ user\ ...\ ]
+Place the jobs in the order listed at the top of the printer queue.
+.Pp
+.It Ic up No {\ all\ |\ printer\ }
+Enable everything and start a new printer daemon. Undoes the effects of
+.Ic down .
+.Sh FILES
+.Bl -tag -width /var/spool/*/lockx -compact
+.It Pa /etc/printcap
+printer description file
+.It Pa /var/spool/*
+spool directories
+.It Pa /var/spool/*/lock
+lock file for queue control
+.El
+.Sh SEE ALSO
+.Xr lpq 1 ,
+.Xr lpr 1 ,
+.Xr lprm 1 ,
+.Xr printcap 5 ,
+.Xr lpd 8
+.Sh DIAGNOSTICS
+.Bl -tag -width Ds
+.It Sy "?Ambiguous command"
+abbreviation matches more than one command
+.It Sy "?Invalid command"
+no match was found
+.It Sy "?Privileged command"
+you must be a member of group "operator" or root to execute this command
+.El
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 4.2 .
diff --git a/usr.sbin/lpr/lpc/lpc.c b/usr.sbin/lpr/lpc/lpc.c
new file mode 100644
index 0000000..bb07d46
--- /dev/null
+++ b/usr.sbin/lpr/lpc/lpc.c
@@ -0,0 +1,322 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 1983, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)lpc.c 8.3 (Berkeley) 4/28/95";
+#endif
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#include <sys/param.h>
+
+#include <ctype.h>
+#include <dirent.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 <sys/param.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
+int fromatty;
+
+char cmdline[MAX_CMDLINE];
+int margc;
+char *margv[MAX_MARGV];
+int top;
+uid_t uid, euid;
+
+jmp_buf toplevel;
+
+static void cmdscanner __P((int));
+static struct cmd *getcmd __P((char *));
+static void intr __P((int));
+static void makeargv __P((void));
+static int ingroup __P((char *));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ register struct cmd *c;
+
+ euid = geteuid();
+ uid = getuid();
+ seteuid(uid);
+ name = argv[0];
+ openlog("lpd", 0, LOG_LPR);
+
+ if (--argc > 0) {
+ c = getcmd(*++argv);
+ if (c == (struct cmd *)-1) {
+ printf("?Ambiguous command\n");
+ exit(1);
+ }
+ if (c == 0) {
+ printf("?Invalid command\n");
+ exit(1);
+ }
+ if (c->c_priv && getuid() && ingroup(LPR_OPER) == 0) {
+ printf("?Privileged command\n");
+ exit(1);
+ }
+ (*c->c_handler)(argc, argv);
+ exit(0);
+ }
+ fromatty = isatty(fileno(stdin));
+ top = setjmp(toplevel) == 0;
+ if (top)
+ signal(SIGINT, intr);
+ for (;;) {
+ cmdscanner(top);
+ top = 1;
+ }
+}
+
+static void
+intr(signo)
+ int signo;
+{
+ if (!fromatty)
+ exit(0);
+ longjmp(toplevel, 1);
+}
+
+/*
+ * Command parser.
+ */
+static void
+cmdscanner(top)
+ int top;
+{
+ register struct cmd *c;
+
+ if (!top)
+ putchar('\n');
+ for (;;) {
+ if (fromatty) {
+ printf("lpc> ");
+ fflush(stdout);
+ }
+ if (fgets(cmdline, MAX_CMDLINE, stdin) == 0)
+ quit(0, NULL);
+ if (cmdline[0] == 0 || cmdline[0] == '\n')
+ break;
+ makeargv();
+ c = getcmd(margv[0]);
+ if (c == (struct cmd *)-1) {
+ printf("?Ambiguous command\n");
+ continue;
+ }
+ if (c == 0) {
+ printf("?Invalid command\n");
+ continue;
+ }
+ if (c->c_priv && getuid() && ingroup(LPR_OPER) == 0) {
+ printf("?Privileged command\n");
+ continue;
+ }
+ (*c->c_handler)(margc, margv);
+ }
+ longjmp(toplevel, 0);
+}
+
+static struct cmd *
+getcmd(name)
+ register char *name;
+{
+ register char *p, *q;
+ register struct cmd *c, *found;
+ register int nmatches, longest;
+
+ longest = 0;
+ nmatches = 0;
+ found = 0;
+ for (c = cmdtab; (p = c->c_name); c++) {
+ for (q = name; *q == *p++; q++)
+ if (*q == 0) /* exact match? */
+ return(c);
+ if (!*q) { /* the name was a prefix */
+ if (q - name > longest) {
+ longest = q - name;
+ nmatches = 1;
+ found = c;
+ } else if (q - name == longest)
+ nmatches++;
+ }
+ }
+ if (nmatches > 1)
+ return((struct cmd *)-1);
+ return(found);
+}
+
+/*
+ * Slice a string up into argc/argv.
+ */
+static void
+makeargv()
+{
+ register char *cp;
+ register char **argp = margv;
+ register int n = 0;
+
+ margc = 0;
+ for (cp = cmdline; *cp && (cp - cmdline) < sizeof(cmdline) &&
+ n < MAX_MARGV; n++) {
+ while (isspace(*cp))
+ cp++;
+ if (*cp == '\0')
+ break;
+ *argp++ = cp;
+ margc += 1;
+ while (*cp != '\0' && !isspace(*cp))
+ cp++;
+ if (*cp == '\0')
+ break;
+ *cp++ = '\0';
+ }
+ *argp++ = 0;
+}
+
+#define HELPINDENT (sizeof ("directory"))
+
+/*
+ * Help command.
+ */
+void
+help(argc, argv)
+ int argc;
+ char *argv[];
+{
+ register struct cmd *c;
+
+ if (argc == 1) {
+ register int i, j, w;
+ int columns, width = 0, lines;
+
+ printf("Commands may be abbreviated. Commands are:\n\n");
+ for (c = cmdtab; c->c_name; c++) {
+ int len = strlen(c->c_name);
+
+ if (len > width)
+ width = len;
+ }
+ width = (width + 8) &~ 7;
+ columns = 80 / width;
+ if (columns == 0)
+ columns = 1;
+ lines = (NCMDS + columns - 1) / columns;
+ for (i = 0; i < lines; i++) {
+ for (j = 0; j < columns; j++) {
+ c = cmdtab + j * lines + i;
+ if (c->c_name)
+ printf("%s", c->c_name);
+ if (c + lines >= &cmdtab[NCMDS]) {
+ printf("\n");
+ break;
+ }
+ w = strlen(c->c_name);
+ while (w < width) {
+ w = (w + 8) &~ 7;
+ putchar('\t');
+ }
+ }
+ }
+ return;
+ }
+ while (--argc > 0) {
+ register char *arg;
+ arg = *++argv;
+ c = getcmd(arg);
+ if (c == (struct cmd *)-1)
+ printf("?Ambiguous help command %s\n", arg);
+ else if (c == (struct cmd *)0)
+ printf("?Invalid help command %s\n", arg);
+ else
+ printf("%-*s\t%s\n", HELPINDENT,
+ c->c_name, c->c_help);
+ }
+}
+
+/*
+ * return non-zero if the user is a member of the given group
+ */
+static int
+ingroup(grname)
+ char *grname;
+{
+ static struct group *gptr=NULL;
+ static gid_t groups[NGROUPS];
+ register gid_t gid;
+ register int i;
+
+ if (gptr == NULL) {
+ if ((gptr = getgrnam(grname)) == NULL) {
+ warnx("warning: unknown group '%s'", grname);
+ return(0);
+ }
+ if (getgroups(NGROUPS, groups) < 0)
+ err(1, "getgroups");
+ }
+ gid = gptr->gr_gid;
+ for (i = 0; i < NGROUPS; i++)
+ if (gid == groups[i])
+ return(1);
+ return(0);
+}
diff --git a/usr.sbin/lpr/lpc/lpc.h b/usr.sbin/lpr/lpc/lpc.h
new file mode 100644
index 0000000..5e71203
--- /dev/null
+++ b/usr.sbin/lpr/lpc/lpc.h
@@ -0,0 +1,45 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)lpc.h 8.1 (Berkeley) 6/6/93
+ */
+
+/*
+ * Line printer control program.
+ */
+struct cmd {
+ char *c_name; /* command name */
+ char *c_help; /* help message */
+ /* routine to do the work */
+ void (*c_handler) __P((int, char *[]));
+ int c_priv; /* privileged command */
+};
diff --git a/usr.sbin/lpr/lpd/Makefile b/usr.sbin/lpr/lpd/Makefile
new file mode 100644
index 0000000..b3c8081
--- /dev/null
+++ b/usr.sbin/lpr/lpd/Makefile
@@ -0,0 +1,11 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= lpd
+CFLAGS+=-I${.CURDIR}/../common_source
+MAN8= lpd.8
+SRCS= lpd.c printjob.c recvjob.c displayq.c rmjob.c startdaemon.c \
+ lpdchar.c common.c modes.c
+.PATH: ${.CURDIR}/../common_source
+
+.include "../../Makefile.inc"
+.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..718bef3
--- /dev/null
+++ b/usr.sbin/lpr/lpd/extern.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)extern.h 8.1 (Berkeley) 6/6/93
+ */
+
+#include <sys/cdefs.h>
+
+extern char scnkey[][HEIGHT]; /* in lpdchar.c */
+extern char fromb[];
+
+void printjob __P((void));
+void recvjob __P((void));
diff --git a/usr.sbin/lpr/lpd/lpd.8 b/usr.sbin/lpr/lpd/lpd.8
new file mode 100644
index 0000000..2323f40
--- /dev/null
+++ b/usr.sbin/lpr/lpd/lpd.8
@@ -0,0 +1,256 @@
+.\" Copyright (c) 1983, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)lpd.8 8.3 (Berkeley) 4/19/94
+.\"
+.Dd April 19, 1994
+.Dt LPD 8
+.Os BSD 4.2
+.Sh NAME
+.Nm lpd
+.Nd line printer spooler daemon
+.Sh SYNOPSIS
+.Nm lpd
+.Op Fl dl
+.Op Ar port#
+.Sh DESCRIPTION
+.Nm Lpd
+is the line printer daemon (spool area handler) and is normally invoked
+at boot time from the
+.Xr rc 8
+file. It makes a single pass through the
+.Xr printcap 5
+file to find out about the existing printers and
+prints any files left after a crash. It then uses the system calls
+.Xr listen 2
+and
+.Xr accept 2
+to receive requests to print files in the queue,
+transfer files to the spooling area, display the queue,
+or remove jobs from the queue. In each case, it forks a child to handle
+the request so the parent can continue to listen for more requests.
+.Pp
+Available options:
+.Bl -tag -width Ds
+.It Fl d
+Turn on
+.Dv SO_DEBUG
+on the Internet listening socket (see
+.Xr setsockopt 2 ) .
+.It Fl l
+The
+.Fl l
+flag causes
+.Nm
+to log valid requests received from the network. This can be useful
+for debugging purposes.
+.It Ar "port#"
+The Internet port number used to rendezvous
+with other processes is normally obtained with
+.Xr getservbyname 3
+but can be changed with the
+.Ar port#
+argument.
+.El
+.Pp
+Access control is provided by two means. First, all requests must come from
+one of the machines listed in the file
+.Pa /etc/hosts.equiv
+or
+.Pa /etc/hosts.lpd .
+Second, if the
+.Li rs
+capability is specified in the
+.Xr printcap
+entry for the printer being accessed,
+.Em lpr
+requests will only be honored for those users with accounts on the
+machine with the printer.
+.Pp
+The file
+.Em minfree
+in each spool directory contains the number of disk blocks to leave free
+so that the line printer queue won't completely fill the disk.
+The
+.Em minfree
+file can be edited with your favorite text editor.
+.Pp
+The daemon begins processing files
+after it has successfully set the lock for exclusive
+access (described a bit later),
+and scans the spool directory
+for files beginning with
+.Em cf .
+Lines in each
+.Em cf
+file specify files to be printed or non-printing actions to be
+performed. Each such line begins with a key character
+to specify what to do with the remainder of the line.
+.Bl -tag -width Ds
+.It J
+Job Name. String to be used for the job name on the burst page.
+.It C
+Classification. String to be used for the classification line
+on the burst page.
+.It L
+Literal. The line contains identification info from
+the password file and causes the banner page to be printed.
+.It T
+Title. String to be used as the title for
+.Xr pr 1 .
+.It H
+Host Name. Name of the machine where
+.Xr lpr
+was invoked.
+.It P
+Person. Login name of the person who invoked
+.Xr lpr .
+This is used to verify ownership by
+.Xr lprm .
+.It M
+Send mail to the specified user when the current print job completes.
+.It f
+Formatted File. Name of a file to print which is already formatted.
+.It l
+Like ``f'' but passes control characters and does not make page breaks.
+.It p
+Name of a file to print using
+.Xr pr 1
+as a filter.
+.It t
+Troff File. The file contains
+.Xr troff 1
+output (cat phototypesetter commands).
+.It n
+Ditroff File. The file contains device independent troff
+output.
+.It r
+DVI File. The file contains
+.Tn Tex l
+output
+DVI format from Standford.
+.It g
+Graph File. The file contains data produced by
+.Xr plot 3 .
+.It c
+Cifplot File. The file contains data produced by
+.Em cifplot .
+.It v
+The file contains a raster image.
+.It r
+The file contains text data with
+FORTRAN carriage control characters.
+.It \&1
+Troff Font R. Name of the font file to use instead of the default.
+.It \&2
+Troff Font I. Name of the font file to use instead of the default.
+.It \&3
+Troff Font B. Name of the font file to use instead of the default.
+.It \&4
+Troff Font S. Name of the font file to use instead of the default.
+.It W
+Width. Changes the page width (in characters) used by
+.Xr pr 1
+and the text filters.
+.It I
+Indent. The number of characters to indent the output by (in ascii).
+.It U
+Unlink. Name of file to remove upon completion of printing.
+.It N
+File name. The name of the file which is being printed, or a blank
+for the standard input (when
+.Xr lpr
+is invoked in a pipeline).
+.El
+.Pp
+If a file cannot be opened, a message will be logged via
+.Xr syslog 3
+using the
+.Em LOG_LPR
+facility.
+.Nm Lpd
+will try up to 20 times
+to reopen a file it expects to be there, after which it will
+skip the file to be printed.
+.Pp
+.Nm Lpd
+uses
+.Xr flock 2
+to provide exclusive access to the lock file and to prevent multiple
+daemons from becoming active simultaneously. If the daemon should be killed
+or die unexpectedly, the lock file need not be removed.
+The lock file is kept in a readable
+.Tn ASCII
+form
+and contains two lines.
+The first is the process id of the daemon and the second is the control
+file name of the current job being printed. The second line is updated to
+reflect the current status of
+.Nm
+for the programs
+.Xr lpq 1
+and
+.Xr lprm 1 .
+.Sh FILES
+.Bl -tag -width "/var/spool/*/minfree" -compact
+.It Pa /etc/printcap
+printer description file
+.It Pa /var/spool/*
+spool directories
+.It Pa /var/spool/*/minfree
+minimum free space to leave
+.It Pa /dev/lp*
+line printer devices
+.It Pa /var/run/printer
+socket for local requests
+.It Pa /etc/hosts.equiv
+lists machine names allowed printer access
+.It Pa /etc/hosts.lpd
+lists machine names allowed printer access,
+but not under same administrative control.
+.El
+.Sh SEE ALSO
+.Xr lpq 1 ,
+.Xr lpr 1 ,
+.Xr lprm 1 ,
+.Xr setsockopt 2 ,
+.Xr syslog 3 ,
+.Xr hosts.lpd 5 ,
+.Xr printcap 5 ,
+.Xr lpc 8 ,
+.Xr pac 8
+.Rs
+.%T "4.2 BSD Line Printer Spooler Manual"
+.Re
+.Sh HISTORY
+An
+.Nm
+daemon appeared in Version 6 AT&T UNIX.
diff --git a/usr.sbin/lpr/lpd/lpd.c b/usr.sbin/lpr/lpd/lpd.c
new file mode 100644
index 0000000..e62e6bf
--- /dev/null
+++ b/usr.sbin/lpr/lpd/lpd.c
@@ -0,0 +1,615 @@
+/*
+ * Copyright (c) 1983, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 1983, 1993, 1994\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)lpd.c 8.7 (Berkeley) 5/10/95";
+#endif
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+/*
+ * lpd -- line printer daemon.
+ *
+ * Listen for a connection and perform the requested operation.
+ * Operations are:
+ * \1printer\n
+ * check the queue for jobs and print any found.
+ * \2printer\n
+ * receive a job from another machine and queue it.
+ * \3printer [users ...] [jobs ...]\n
+ * return the current state of the queue (short form).
+ * \4printer [users ...] [jobs ...]\n
+ * return the current state of the queue (long form).
+ * \5printer person [users ...] [jobs ...]\n
+ * remove jobs from the queue.
+ *
+ * Strategy to maintain protected spooling area:
+ * 1. Spooling area is writable only by daemon and spooling group
+ * 2. lpr runs setuid root and setgrp spooling group; it uses
+ * root to access any file it wants (verifying things before
+ * with an access call) and group id to know how it should
+ * set up ownership of files in the spooling area.
+ * 3. Files in spooling area are owned by root, group spooling
+ * group, with mode 660.
+ * 4. lpd, lpq and lprm run setuid daemon and setgrp spooling group to
+ * access files and printer. Users can't get to anything
+ * w/o help of lpq and lprm programs.
+ */
+
+#include <sys/param.h>
+#include <sys/wait.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/stat.h>
+#include <sys/file.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <netdb.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <signal.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <ctype.h>
+#include "lp.h"
+#include "lp.local.h"
+#include "pathnames.h"
+#include "extern.h"
+
+int lflag; /* log requests flag */
+int from_remote; /* from remote socket */
+
+static void reapchild __P((int));
+static void mcleanup __P((int));
+static void doit __P((void));
+static void startup __P((void));
+static void chkhost __P((struct sockaddr_in *));
+static int ckqueue __P((char *));
+static void usage __P((void));
+
+uid_t uid, euid;
+
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ int f, funix, finet, options, fromlen, i, errs;
+ fd_set defreadfds;
+ struct sockaddr_un un, fromunix;
+ struct sockaddr_in sin, frominet;
+ int omask, lfd;
+ struct servent *sp, serv;
+
+ euid = geteuid(); /* these shouldn't be different */
+ uid = getuid();
+ options = 0;
+ gethostname(host, sizeof(host));
+
+ name = "lpd";
+
+ if (euid != 0)
+ errx(EX_NOPERM,"must run as root");
+
+ errs = 0;
+ while ((i = getopt(argc, argv, "dl")) != -1)
+ switch (i) {
+ case 'd':
+ options |= SO_DEBUG;
+ break;
+ case 'l':
+ lflag++;
+ break;
+ default:
+ errs++;
+ }
+ argc -= optind;
+ argv += optind;
+ if (errs)
+ usage();
+
+ if (argc == 1) {
+ if ((i = atoi(argv[0])) == 0)
+ usage();
+ if (i < 0 || i > USHRT_MAX)
+ errx(EX_USAGE, "port # %d is invalid", i);
+
+ serv.s_port = htons(i);
+ sp = &serv;
+ argc--;
+ } else {
+ sp = getservbyname("printer", "tcp");
+ if (sp == NULL)
+ errx(EX_OSFILE, "printer/tcp: unknown service");
+ }
+
+ if (argc != 0)
+ usage();
+
+#ifndef DEBUG
+ /*
+ * Set up standard environment by detaching from the parent.
+ */
+ daemon(0, 0);
+#endif
+
+ openlog("lpd", LOG_PID, LOG_LPR);
+ syslog(LOG_INFO, "restarted");
+ (void) umask(0);
+ lfd = open(_PATH_MASTERLOCK, O_WRONLY|O_CREAT, 0644);
+ if (lfd < 0) {
+ syslog(LOG_ERR, "%s: %m", _PATH_MASTERLOCK);
+ exit(1);
+ }
+ if (flock(lfd, LOCK_EX|LOCK_NB) < 0) {
+ if (errno == EWOULDBLOCK) /* active deamon present */
+ exit(0);
+ syslog(LOG_ERR, "%s: %m", _PATH_MASTERLOCK);
+ exit(1);
+ }
+ 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);
+ }
+#define mask(s) (1 << ((s) - 1))
+ omask = sigblock(mask(SIGHUP)|mask(SIGINT)|mask(SIGQUIT)|mask(SIGTERM));
+ (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);
+ sigsetmask(omask);
+ FD_ZERO(&defreadfds);
+ FD_SET(funix, &defreadfds);
+ listen(funix, 5);
+ finet = socket(AF_INET, SOCK_STREAM, 0);
+ if (finet >= 0) {
+ if (options & SO_DEBUG)
+ if (setsockopt(finet, SOL_SOCKET, SO_DEBUG, 0, 0) < 0) {
+ syslog(LOG_ERR, "setsockopt (SO_DEBUG): %m");
+ mcleanup(0);
+ }
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_family = AF_INET;
+ sin.sin_port = sp->s_port;
+ if (bind(finet, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
+ syslog(LOG_ERR, "bind: %m");
+ mcleanup(0);
+ }
+ FD_SET(finet, &defreadfds);
+ listen(finet, 5);
+ }
+ /*
+ * Main loop: accept, do a request, continue.
+ */
+ memset(&frominet, 0, sizeof(frominet));
+ memset(&fromunix, 0, sizeof(fromunix));
+ for (;;) {
+ int domain, nfds, s;
+ fd_set readfds;
+
+ FD_COPY(&defreadfds, &readfds);
+ nfds = select(20, &readfds, 0, 0, 0);
+ if (nfds <= 0) {
+ if (nfds < 0 && errno != EINTR)
+ syslog(LOG_WARNING, "select: %m");
+ continue;
+ }
+ if (FD_ISSET(funix, &readfds)) {
+ domain = AF_UNIX, fromlen = sizeof(fromunix);
+ s = accept(funix,
+ (struct sockaddr *)&fromunix, &fromlen);
+ } else /* if (FD_ISSET(finet, &readfds)) */ {
+ domain = AF_INET, fromlen = sizeof(frominet);
+ s = accept(finet,
+ (struct sockaddr *)&frominet, &fromlen);
+ if (frominet.sin_port == htons(20)) {
+ close(s);
+ continue;
+ }
+ }
+ if (s < 0) {
+ if (errno != EINTR)
+ syslog(LOG_WARNING, "accept: %m");
+ continue;
+ }
+ if (fork() == 0) {
+ signal(SIGCHLD, SIG_IGN);
+ signal(SIGHUP, SIG_IGN);
+ signal(SIGINT, SIG_IGN);
+ signal(SIGQUIT, SIG_IGN);
+ signal(SIGTERM, SIG_IGN);
+ (void) close(funix);
+ (void) close(finet);
+ dup2(s, 1);
+ (void) close(s);
+ if (domain == AF_INET) {
+ from_remote = 1;
+ chkhost(&frominet);
+ } else
+ from_remote = 0;
+ doit();
+ exit(0);
+ }
+ (void) close(s);
+ }
+}
+
+static void
+reapchild(signo)
+ int signo;
+{
+ union wait status;
+
+ while (wait3((int *)&status, WNOHANG, 0) > 0)
+ ;
+}
+
+static void
+mcleanup(signo)
+ int signo;
+{
+ if (lflag)
+ syslog(LOG_INFO, "exiting");
+ unlink(_PATH_SOCKETNAME);
+ exit(0);
+}
+
+/*
+ * Stuff for handling job specifications
+ */
+char *user[MAXUSERS]; /* users to process */
+int users; /* # of users in user array */
+int requ[MAXREQUESTS]; /* job number of spool entries */
+int requests; /* # of spool requests */
+char *person; /* name of person doing lprm */
+
+char fromb[MAXHOSTNAMELEN]; /* buffer for client's machine name */
+char cbuf[BUFSIZ]; /* command line buffer */
+char *cmdnames[] = {
+ "null",
+ "printjob",
+ "recvjob",
+ "displayq short",
+ "displayq long",
+ "rmjob"
+};
+
+static void
+doit()
+{
+ register char *cp;
+ register int n;
+
+ for (;;) {
+ cp = cbuf;
+ do {
+ if (cp >= &cbuf[sizeof(cbuf) - 1])
+ fatal("Command line too long");
+ if ((n = read(1, cp, 1)) != 1) {
+ if (n < 0)
+ fatal("Lost connection");
+ return;
+ }
+ } while (*cp++ != '\n');
+ *--cp = '\0';
+ cp = cbuf;
+ if (lflag) {
+ if (*cp >= '\1' && *cp <= '\5')
+ syslog(LOG_INFO, "%s requests %s %s",
+ from, cmdnames[*cp], cp+1);
+ else
+ syslog(LOG_INFO, "bad request (%d) from %s",
+ *cp, from);
+ }
+ switch (*cp++) {
+ case '\1': /* check the queue and print any jobs there */
+ printer = cp;
+ printjob();
+ break;
+ case '\2': /* receive files to be queued */
+ if (!from_remote) {
+ syslog(LOG_INFO, "illegal request (%d)", *cp);
+ exit(1);
+ }
+ printer = cp;
+ recvjob();
+ break;
+ case '\3': /* display the queue (short form) */
+ case '\4': /* display the queue (long form) */
+ 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("Too many requests");
+ requ[requests++] = atoi(cp);
+ } else {
+ if (users >= MAXUSERS)
+ fatal("Too many users");
+ user[users++] = cp;
+ }
+ }
+ displayq(cbuf[0] - '\3');
+ exit(0);
+ case '\5': /* 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("Too many requests");
+ requ[requests++] = atoi(cp);
+ } else {
+ if (users >= MAXUSERS)
+ fatal("Too many users");
+ user[users++] = cp;
+ }
+ }
+ rmjob();
+ break;
+ }
+ fatal("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()
+{
+ char *buf;
+ register char *cp;
+ int pid;
+ char *spooldirs[16]; /* Which spooldirs are active? */
+ int i; /* Printer index presently processed */
+ int j; /* Printer index of potential conflict */
+ char *spooldir; /* Spooldir of present printer */
+ int canfreespool; /* Is the spooldir malloc()ed? */
+
+ /*
+ * Restart the daemons and test for spooldir conflict.
+ */
+ i = 0;
+ while (cgetnext(&buf, printcapdb) > 0) {
+
+ /* Check for duplicate spooldirs */
+ canfreespool = 1;
+ if (cgetstr(buf, "sd", &spooldir) <= 0) {
+ spooldir = _PATH_DEFSPOOL;
+ canfreespool = 0;
+ }
+ if (i < sizeof(spooldirs)/sizeof(spooldirs[0]))
+ spooldirs[i] = spooldir;
+ for (j = 0;
+ j < MIN(i,sizeof(spooldirs)/sizeof(spooldirs[0]));
+ j++) {
+ if (strcmp(spooldir, spooldirs[j]) == 0) {
+ syslog(LOG_ERR,
+ "startup: duplicate spool directories: %s",
+ spooldir);
+ mcleanup(0);
+ }
+ }
+ if (canfreespool && i >= sizeof(spooldirs)/sizeof(spooldirs[0]))
+ free(spooldir);
+ i++;
+ /* Spooldir test done */
+
+ if (ckqueue(buf) <= 0) {
+ free(buf);
+ continue; /* no work to do for this printer */
+ }
+ for (cp = buf; *cp; cp++)
+ if (*cp == '|' || *cp == ':') {
+ *cp = '\0';
+ break;
+ }
+ if (lflag)
+ syslog(LOG_INFO, "work for %s", buf);
+ if ((pid = fork()) < 0) {
+ syslog(LOG_WARNING, "startup: cannot fork");
+ mcleanup(0);
+ }
+ if (!pid) {
+ printer = buf;
+ cgetclose();
+ printjob();
+ /* NOTREACHED */
+ }
+ else free(buf);
+ }
+}
+
+/*
+ * Make sure there's some work to do before forking off a child
+ */
+static int
+ckqueue(cap)
+ char *cap;
+{
+ register struct dirent *d;
+ DIR *dirp;
+ char *spooldir;
+
+ if (cgetstr(cap, "sd", &spooldir) == -1)
+ spooldir = _PATH_DEFSPOOL;
+ if ((dirp = opendir(spooldir)) == NULL)
+ return (-1);
+ while ((d = readdir(dirp)) != NULL) {
+ if (d->d_name[0] != 'c' || d->d_name[1] != 'f')
+ continue; /* daemon control files only */
+ closedir(dirp);
+ return (1); /* found something */
+ }
+ closedir(dirp);
+ return (0);
+}
+
+#define DUMMY ":nobody::"
+
+/*
+ * Check to see if the from host has access to the line printer.
+ */
+static void
+chkhost(f)
+ struct sockaddr_in *f;
+{
+ register struct hostent *hp;
+ register FILE *hostf;
+ int first = 1;
+ int good = 0;
+
+ /* Need real hostname for temporary filenames */
+ hp = gethostbyaddr((char *)&f->sin_addr,
+ sizeof(struct in_addr), f->sin_family);
+ if (hp == NULL)
+ fatal("Host name for your address (%s) unknown",
+ inet_ntoa(f->sin_addr));
+
+ (void) strncpy(fromb, hp->h_name, sizeof(fromb) - 1);
+ from[sizeof(fromb) - 1] = '\0';
+ from = fromb;
+
+ /* Check for spoof, ala rlogind */
+ hp = gethostbyname(fromb);
+ if (!hp)
+ fatal("hostname for your address (%s) unknown",
+ inet_ntoa(f->sin_addr));
+ for (; good == 0 && hp->h_addr_list[0] != NULL; hp->h_addr_list++) {
+ if (!bcmp(hp->h_addr_list[0], (caddr_t)&f->sin_addr,
+ sizeof(f->sin_addr)))
+ good = 1;
+ }
+ if (good == 0)
+ fatal("address for your hostname (%s) not matched",
+ inet_ntoa(f->sin_addr));
+
+ hostf = fopen(_PATH_HOSTSEQUIV, "r");
+again:
+ if (hostf) {
+ if (__ivaliduser(hostf, f->sin_addr.s_addr,
+ DUMMY, DUMMY) == 0) {
+ (void) fclose(hostf);
+ return;
+ }
+ (void) fclose(hostf);
+ }
+ if (first == 1) {
+ first = 0;
+ hostf = fopen(_PATH_HOSTSLPD, "r");
+ goto again;
+ }
+ fatal("Your host does not have line printer access");
+ /*NOTREACHED*/
+}
+
+static void
+usage()
+{
+ fprintf(stderr, "usage: lpd [-dl] [port#]\n");
+ exit(EX_USAGE);
+}
diff --git a/usr.sbin/lpr/lpd/lpdchar.c b/usr.sbin/lpr/lpd/lpdchar.c
new file mode 100644
index 0000000..4d24ffd
--- /dev/null
+++ b/usr.sbin/lpr/lpd/lpdchar.c
@@ -0,0 +1,1067 @@
+/*
+ * 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";
+#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..7e3dcd9
--- /dev/null
+++ b/usr.sbin/lpr/lpd/modes.c
@@ -0,0 +1,232 @@
+/*-
+ * 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.
+ *
+ * $Id$
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)modes.c 8.3 (Berkeley) 4/2/94";
+#endif /* not lint */
+
+#include <stddef.h>
+#include <string.h>
+#include <termios.h>
+
+struct modes {
+ char *name;
+ long set;
+ long unset;
+};
+
+/*
+ * The code in optlist() depends on minus options following regular
+ * options, i.e. "foo" must immediately precede "-foo".
+ */
+struct modes cmodes[] = {
+ { "cs5", CS5, CSIZE },
+ { "cs6", CS6, CSIZE },
+ { "cs7", CS7, CSIZE },
+ { "cs8", CS8, CSIZE },
+ { "cstopb", CSTOPB, 0 },
+ { "-cstopb", 0, CSTOPB },
+ { "cread", CREAD, 0 },
+ { "-cread", 0, CREAD },
+ { "parenb", PARENB, 0 },
+ { "-parenb", 0, PARENB },
+ { "parodd", PARODD, 0 },
+ { "-parodd", 0, PARODD },
+ { "parity", PARENB | CS7, PARODD | CSIZE },
+ { "-parity", CS8, PARODD | PARENB | CSIZE },
+ { "evenp", PARENB | CS7, PARODD | CSIZE },
+ { "-evenp", CS8, PARODD | PARENB | CSIZE },
+ { "oddp", PARENB | CS7 | PARODD, CSIZE },
+ { "-oddp", CS8, PARODD | PARENB | CSIZE },
+ { "pass8", CS8, PARODD | PARENB | CSIZE },
+ { "-pass8", PARENB | CS7, PARODD | CSIZE },
+ { "hupcl", HUPCL, 0 },
+ { "-hupcl", 0, HUPCL },
+ { "hup", HUPCL, 0 },
+ { "-hup", 0, HUPCL },
+ { "clocal", CLOCAL, 0 },
+ { "-clocal", 0, CLOCAL },
+ { "crtscts", CRTSCTS, 0 },
+ { "-crtscts", 0, CRTSCTS },
+ { "ctsflow", CCTS_OFLOW, 0 },
+ { "-ctsflow", 0, CCTS_OFLOW },
+ { "dsrflow", CDSR_OFLOW, 0 },
+ { "-dsrflow", 0, CDSR_OFLOW },
+ { "dtrflow", CDTR_IFLOW, 0 },
+ { "-dtrflow", 0, CDTR_IFLOW },
+ { "rtsflow", CRTS_IFLOW, 0 },
+ { "-rtsflow", 0, CRTS_IFLOW },
+ { "mdmbuf", MDMBUF, 0 },
+ { "-mdmbuf", 0, MDMBUF },
+ { NULL },
+};
+
+struct modes imodes[] = {
+ { "ignbrk", IGNBRK, 0 },
+ { "-ignbrk", 0, IGNBRK },
+ { "brkint", BRKINT, 0 },
+ { "-brkint", 0, BRKINT },
+ { "ignpar", IGNPAR, 0 },
+ { "-ignpar", 0, IGNPAR },
+ { "parmrk", PARMRK, 0 },
+ { "-parmrk", 0, PARMRK },
+ { "inpck", INPCK, 0 },
+ { "-inpck", 0, INPCK },
+ { "istrip", ISTRIP, 0 },
+ { "-istrip", 0, ISTRIP },
+ { "inlcr", INLCR, 0 },
+ { "-inlcr", 0, INLCR },
+ { "igncr", IGNCR, 0 },
+ { "-igncr", 0, IGNCR },
+ { "icrnl", ICRNL, 0 },
+ { "-icrnl", 0, ICRNL },
+ { "ixon", IXON, 0 },
+ { "-ixon", 0, IXON },
+ { "flow", IXON, 0 },
+ { "-flow", 0, IXON },
+ { "ixoff", IXOFF, 0 },
+ { "-ixoff", 0, IXOFF },
+ { "tandem", IXOFF, 0 },
+ { "-tandem", 0, IXOFF },
+ { "ixany", IXANY, 0 },
+ { "-ixany", 0, IXANY },
+ { "decctlq", 0, IXANY },
+ { "-decctlq", IXANY, 0 },
+ { "imaxbel", IMAXBEL, 0 },
+ { "-imaxbel", 0, IMAXBEL },
+ { NULL },
+};
+
+struct modes lmodes[] = {
+ { "echo", ECHO, 0 },
+ { "-echo", 0, ECHO },
+ { "echoe", ECHOE, 0 },
+ { "-echoe", 0, ECHOE },
+ { "crterase", ECHOE, 0 },
+ { "-crterase", 0, ECHOE },
+ { "crtbs", ECHOE, 0 }, /* crtbs not supported, close enough */
+ { "-crtbs", 0, ECHOE },
+ { "echok", ECHOK, 0 },
+ { "-echok", 0, ECHOK },
+ { "echoke", ECHOKE, 0 },
+ { "-echoke", 0, ECHOKE },
+ { "crtkill", ECHOKE, 0 },
+ { "-crtkill", 0, ECHOKE },
+ { "altwerase", ALTWERASE, 0 },
+ { "-altwerase", 0, ALTWERASE },
+ { "iexten", IEXTEN, 0 },
+ { "-iexten", 0, IEXTEN },
+ { "echonl", ECHONL, 0 },
+ { "-echonl", 0, ECHONL },
+ { "echoctl", ECHOCTL, 0 },
+ { "-echoctl", 0, ECHOCTL },
+ { "ctlecho", ECHOCTL, 0 },
+ { "-ctlecho", 0, ECHOCTL },
+ { "echoprt", ECHOPRT, 0 },
+ { "-echoprt", 0, ECHOPRT },
+ { "prterase", ECHOPRT, 0 },
+ { "-prterase", 0, ECHOPRT },
+ { "isig", ISIG, 0 },
+ { "-isig", 0, ISIG },
+ { "icanon", ICANON, 0 },
+ { "-icanon", 0, ICANON },
+ { "noflsh", NOFLSH, 0 },
+ { "-noflsh", 0, NOFLSH },
+ { "tostop", TOSTOP, 0 },
+ { "-tostop", 0, TOSTOP },
+ { "flusho", FLUSHO, 0 },
+ { "-flusho", 0, FLUSHO },
+ { "pendin", PENDIN, 0 },
+ { "-pendin", 0, PENDIN },
+ { "crt", ECHOE|ECHOKE|ECHOCTL, ECHOK|ECHOPRT },
+ { "-crt", ECHOK, ECHOE|ECHOKE|ECHOCTL },
+ { "newcrt", ECHOE|ECHOKE|ECHOCTL, ECHOK|ECHOPRT },
+ { "-newcrt", ECHOK, ECHOE|ECHOKE|ECHOCTL },
+ { "nokerninfo", NOKERNINFO, 0 },
+ { "-nokerninfo",0, NOKERNINFO },
+ { "kerninfo", 0, NOKERNINFO },
+ { "-kerninfo", NOKERNINFO, 0 },
+ { NULL },
+};
+
+struct modes omodes[] = {
+ { "opost", OPOST, 0 },
+ { "-opost", 0, OPOST },
+ { "litout", 0, OPOST },
+ { "-litout", OPOST, 0 },
+ { "onlcr", ONLCR, 0 },
+ { "-onlcr", 0, ONLCR },
+ { "tabs", 0, OXTABS }, /* "preserve" tabs */
+ { "-tabs", OXTABS, 0 },
+ { "oxtabs", OXTABS, 0 },
+ { "-oxtabs", 0, OXTABS },
+ { NULL },
+};
+
+#define CHK(name, s) (*name == s[0] && !strcmp(name, s))
+
+int
+msearch(str, ip)
+ char *str;
+ struct termios *ip;
+{
+ struct modes *mp;
+
+ for (mp = cmodes; mp->name; ++mp)
+ if (CHK(str, mp->name)) {
+ ip->c_cflag &= ~mp->unset;
+ ip->c_cflag |= mp->set;
+ return (1);
+ }
+ for (mp = imodes; mp->name; ++mp)
+ if (CHK(str, mp->name)) {
+ ip->c_iflag &= ~mp->unset;
+ ip->c_iflag |= mp->set;
+ return (1);
+ }
+ for (mp = lmodes; mp->name; ++mp)
+ if (CHK(str, mp->name)) {
+ ip->c_lflag &= ~mp->unset;
+ ip->c_lflag |= mp->set;
+ return (1);
+ }
+ for (mp = omodes; mp->name; ++mp)
+ if (CHK(str, mp->name)) {
+ ip->c_oflag &= ~mp->unset;
+ ip->c_oflag |= mp->set;
+ return (1);
+ }
+ return (0);
+}
diff --git a/usr.sbin/lpr/lpd/printjob.c b/usr.sbin/lpr/lpd/printjob.c
new file mode 100644
index 0000000..cff1d9f
--- /dev/null
+++ b/usr.sbin/lpr/lpd/printjob.c
@@ -0,0 +1,1676 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1983, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)printjob.c 8.7 (Berkeley) 5/10/95";
+#endif /* not lint */
+
+
+/*
+ * printjob -- print jobs in the queue.
+ *
+ * NOTE: the lock file is used to pass information to lpq and lprm.
+ * it does not need to be removed because file locks are dynamic.
+ */
+
+#include <sys/param.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <pwd.h>
+#include <unistd.h>
+#include <signal.h>
+#include <syslog.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/ioctl.h>
+#include <termios.h>
+#include <time.h>
+#include "lp.h"
+#include "lp.local.h"
+#include "pathnames.h"
+#include "extern.h"
+
+#define DORETURN 0 /* absorb fork error */
+#define DOABORT 1 /* abort if dofork fails */
+
+/*
+ * Error tokens
+ */
+#define REPRINT -2
+#define ERROR -1
+#define OK 0
+#define FATALERR 1
+#define NOACCT 2
+#define FILTERERR 3
+#define ACCESS 4
+
+static dev_t fdev; /* device of file pointed to by symlink */
+static ino_t fino; /* inode of file pointed to by symlink */
+static FILE *cfp; /* control file */
+static int child; /* id of any filters */
+static int lfd; /* lock file descriptor */
+static int ofd; /* output filter file descriptor */
+static int ofilter; /* id of output filter, if any */
+static int tfd = -1; /* output filter temp file output */
+static int pfd; /* prstatic inter file descriptor */
+static int pid; /* pid of lpd process */
+static int prchild; /* id of pr process */
+static char title[80]; /* ``pr'' title */
+static int tof; /* true if at top of form */
+
+static char class[32]; /* classification field */
+static char fromhost[32]; /* user's host machine */
+ /* indentation size in static characters */
+static char indent[10] = "-i0";
+static char jobname[100]; /* job or file name */
+static char length[10] = "-l"; /* page length in lines */
+static char logname[32]; /* user's login name */
+static char pxlength[10] = "-y"; /* page length in pixels */
+static char pxwidth[10] = "-x"; /* page width in pixels */
+static char tempfile[] = "errsXXXXXX"; /* file name for filter errors */
+static char width[10] = "-w"; /* page width in static characters */
+#define TFILENAME "fltXXXXXX"
+static char tfile[] = TFILENAME; /* file name for filter output */
+
+static void abortpr __P((int));
+static void alarmhandler __P((int));
+static void banner __P((char *, char *));
+static int dofork __P((int));
+static int dropit __P((int));
+static void init __P((void));
+static void openpr __P((void));
+static void opennet __P((char *));
+static void opentty __P((void));
+static void openrem __P((void));
+static int print __P((int, char *));
+static int printit __P((char *));
+static void pstatus __P((const char *, ...));
+static char response __P((void));
+static void scan_out __P((int, char *, int));
+static char *scnline __P((int, char *, int));
+static int sendfile __P((int, char *, char));
+static int sendit __P((char *));
+static void sendmail __P((char *, int));
+static void setty __P((void));
+
+void
+printjob()
+{
+ struct stat stb;
+ register struct queue *q, **qp;
+ struct queue **queue;
+ register int i, nitems;
+ off_t pidoff;
+ int errcnt, count = 0;
+
+ init(); /* set up capabilities */
+ (void) write(1, "", 1); /* ack that daemon is started */
+ (void) close(2); /* set up log file */
+ if (open(LF, O_WRONLY|O_APPEND, 0664) < 0) {
+ syslog(LOG_ERR, "%s: %m", LF);
+ (void) open(_PATH_DEVNULL, O_WRONLY);
+ }
+ setgid(getegid());
+ pid = getpid(); /* for use with lprm */
+ setpgrp(0, pid);
+ signal(SIGHUP, abortpr);
+ signal(SIGINT, abortpr);
+ signal(SIGQUIT, abortpr);
+ signal(SIGTERM, abortpr);
+
+ (void) mktemp(tempfile);
+
+ /*
+ * uses short form file names
+ */
+ if (chdir(SD) < 0) {
+ syslog(LOG_ERR, "%s: %m", SD);
+ exit(1);
+ }
+ if (stat(LO, &stb) == 0 && (stb.st_mode & 0100))
+ exit(0); /* printing disabled */
+ lfd = open(LO, O_WRONLY|O_CREAT, 0644);
+ if (lfd < 0) {
+ syslog(LOG_ERR, "%s: %s: %m", printer, LO);
+ exit(1);
+ }
+ if (flock(lfd, LOCK_EX|LOCK_NB) < 0) {
+ if (errno == EWOULDBLOCK) /* active deamon present */
+ exit(0);
+ syslog(LOG_ERR, "%s: %s: %m", printer, LO);
+ exit(1);
+ }
+ ftruncate(lfd, 0);
+ /*
+ * write process id for others to know
+ */
+ sprintf(line, "%u\n", pid);
+ pidoff = i = strlen(line);
+ if (write(lfd, line, i) != i) {
+ syslog(LOG_ERR, "%s: %s: %m", printer, LO);
+ exit(1);
+ }
+ /*
+ * search the spool directory for work and sort by queue order.
+ */
+ if ((nitems = getq(&queue)) < 0) {
+ syslog(LOG_ERR, "%s: can't scan %s", printer, SD);
+ exit(1);
+ }
+ if (nitems == 0) /* no work to do */
+ exit(0);
+ if (stb.st_mode & 01) { /* reset queue flag */
+ if (fchmod(lfd, stb.st_mode & 0776) < 0)
+ syslog(LOG_ERR, "%s: %s: %m", printer, LO);
+ }
+ openpr(); /* open printer or remote */
+again:
+ /*
+ * we found something to do now do it --
+ * write the name of the current control file into the lock file
+ * so the spool queue program can tell what we're working on
+ */
+ for (qp = queue; nitems--; free((char *) q)) {
+ q = *qp++;
+ if (stat(q->q_name, &stb) < 0)
+ continue;
+ errcnt = 0;
+ restart:
+ (void) lseek(lfd, pidoff, 0);
+ (void) snprintf(line, sizeof(line), "%s\n", q->q_name);
+ i = strlen(line);
+ if (write(lfd, line, i) != i)
+ syslog(LOG_ERR, "%s: %s: %m", printer, LO);
+ if (!remote)
+ i = printit(q->q_name);
+ else
+ i = sendit(q->q_name);
+ /*
+ * Check to see if we are supposed to stop printing or
+ * if we are to rebuild the queue.
+ */
+ if (fstat(lfd, &stb) == 0) {
+ /* stop printing before starting next job? */
+ if (stb.st_mode & 0100)
+ goto done;
+ /* rebuild queue (after lpc topq) */
+ if (stb.st_mode & 01) {
+ for (free((char *) q); nitems--; free((char *) q))
+ q = *qp++;
+ if (fchmod(lfd, stb.st_mode & 0776) < 0)
+ syslog(LOG_WARNING, "%s: %s: %m",
+ printer, LO);
+ break;
+ }
+ }
+ if (i == OK) /* file ok and printed */
+ count++;
+ else if (i == REPRINT && ++errcnt < 5) {
+ /* try reprinting the job */
+ syslog(LOG_INFO, "restarting %s", printer);
+ if (ofilter > 0) {
+ kill(ofilter, SIGCONT); /* to be sure */
+ (void) close(ofd);
+ while ((i = wait(NULL)) > 0 && i != ofilter)
+ ;
+ ofilter = 0;
+ }
+ (void) close(pfd); /* close printer */
+ if (ftruncate(lfd, pidoff) < 0)
+ syslog(LOG_WARNING, "%s: %s: %m", printer, LO);
+ openpr(); /* try to reopen printer */
+ goto restart;
+ } else {
+ syslog(LOG_WARNING, "%s: job could not be %s (%s)", printer,
+ remote ? "sent to remote host" : "printed", q->q_name);
+ if (i == REPRINT) {
+ /* ensure we don't attempt this job again */
+ (void) unlink(q->q_name);
+ q->q_name[0] = 'd';
+ (void) unlink(q->q_name);
+ if (logname[0])
+ sendmail(logname, FATALERR);
+ }
+ }
+ }
+ free((char *) queue);
+ /*
+ * search the spool directory for more work.
+ */
+ if ((nitems = getq(&queue)) < 0) {
+ syslog(LOG_ERR, "%s: can't scan %s", printer, SD);
+ exit(1);
+ }
+ if (nitems == 0) { /* no more work to do */
+ done:
+ if (count > 0) { /* Files actually printed */
+ if (!SF && !tof)
+ (void) write(ofd, FF, strlen(FF));
+ if (TR != NULL) /* output trailer */
+ (void) write(ofd, TR, strlen(TR));
+ }
+ (void) close(ofd);
+ (void) wait(NULL);
+ (void) unlink(tempfile);
+ exit(0);
+ }
+ goto again;
+}
+
+char fonts[4][50]; /* fonts for troff */
+
+char ifonts[4][40] = {
+ _PATH_VFONTR,
+ _PATH_VFONTI,
+ _PATH_VFONTB,
+ _PATH_VFONTS,
+};
+
+/*
+ * The remaining part is the reading of the control file (cf)
+ * and performing the various actions.
+ */
+static int
+printit(file)
+ char *file;
+{
+ register int i;
+ char *cp;
+ int bombed = OK;
+
+ /*
+ * open control file; ignore if no longer there.
+ */
+ if ((cfp = fopen(file, "r")) == NULL) {
+ syslog(LOG_INFO, "%s: %s: %m", printer, file);
+ return(OK);
+ }
+ /*
+ * Reset troff fonts.
+ */
+ for (i = 0; i < 4; i++)
+ strcpy(fonts[i], ifonts[i]);
+ sprintf(&width[2], "%ld", PW);
+ strcpy(indent+2, "0");
+
+ /*
+ * read the control file for work to do
+ *
+ * file format -- first character in the line is a command
+ * rest of the line is the argument.
+ * valid commands are:
+ *
+ * S -- "stat info" for symbolic link protection
+ * J -- "job name" on banner page
+ * C -- "class name" on banner page
+ * L -- "literal" user's name to print on banner
+ * T -- "title" for pr
+ * H -- "host name" of machine where lpr was done
+ * P -- "person" user's login name
+ * I -- "indent" amount to indent output
+ * R -- laser dpi "resolution"
+ * f -- "file name" name of text file to print
+ * l -- "file name" text file with control chars
+ * p -- "file name" text file to print with pr(1)
+ * t -- "file name" troff(1) file to print
+ * n -- "file name" ditroff(1) file to print
+ * d -- "file name" dvi file to print
+ * g -- "file name" plot(1G) file to print
+ * v -- "file name" plain raster file to print
+ * c -- "file name" cifplot file to print
+ * 1 -- "R font file" for troff
+ * 2 -- "I font file" for troff
+ * 3 -- "B font file" for troff
+ * 4 -- "S font file" for troff
+ * N -- "name" of file (used by lpq)
+ * U -- "unlink" name of file to remove
+ * (after we print it. (Pass 2 only)).
+ * M -- "mail" to user when done printing
+ *
+ * getline reads a line and expands tabs to blanks
+ */
+
+ /* pass 1 */
+
+ while (getline(cfp))
+ switch (line[0]) {
+ case 'H':
+ strncpy(fromhost, line+1, sizeof(fromhost) - 1);
+ fromhost[sizeof(fromhost) - 1] = '\0';
+ if (class[0] == '\0') {
+ strncpy(class, line+1, sizeof(class) - 1);
+ class[sizeof(class) - 1] = '\0';
+ }
+ continue;
+
+ case 'P':
+ strncpy(logname, line+1, sizeof(logname) - 1);
+ logname[sizeof(logname) - 1] = '\0';
+ if (RS) { /* restricted */
+ if (getpwnam(logname) == NULL) {
+ bombed = NOACCT;
+ sendmail(line+1, bombed);
+ goto pass2;
+ }
+ }
+ continue;
+
+ case 'S':
+ cp = line+1;
+ i = 0;
+ while (*cp >= '0' && *cp <= '9')
+ i = i * 10 + (*cp++ - '0');
+ fdev = i;
+ cp++;
+ i = 0;
+ while (*cp >= '0' && *cp <= '9')
+ i = i * 10 + (*cp++ - '0');
+ fino = i;
+ continue;
+
+ case 'J':
+ if (line[1] != '\0') {
+ strncpy(jobname, line+1, sizeof(jobname) - 1);
+ jobname[sizeof(jobname) - 1] = '\0';
+ } else
+ strcpy(jobname, " ");
+ continue;
+
+ case 'C':
+ if (line[1] != '\0')
+ strncpy(class, line+1, sizeof(class) - 1);
+ else if (class[0] == '\0')
+ gethostname(class, sizeof(class));
+ class[sizeof(class) - 1] = '\0';
+ continue;
+
+ case 'T': /* header title for pr */
+ strncpy(title, line+1, sizeof(title) - 1);
+ title[sizeof(title) - 1] = '\0';
+ continue;
+
+ case 'L': /* identification line */
+ if (!SH && !HL)
+ banner(line+1, jobname);
+ continue;
+
+ case '1': /* troff fonts */
+ case '2':
+ case '3':
+ case '4':
+ if (line[1] != '\0') {
+ strncpy(fonts[line[0]-'1'], line+1,
+ 50-1);
+ fonts[line[0]-'1'][50-1] = '\0';
+ }
+ continue;
+
+ case 'W': /* page width */
+ strncpy(width+2, line+1, sizeof(width) - 3);
+ width[2+sizeof(width) - 3] = '\0';
+ continue;
+
+ case 'I': /* indent amount */
+ strncpy(indent+2, line+1, sizeof(indent) - 3);
+ indent[2+sizeof(indent) - 3] = '\0';
+ continue;
+
+ default: /* some file to print */
+ switch (i = print(line[0], line+1)) {
+ case ERROR:
+ if (bombed == OK)
+ bombed = FATALERR;
+ break;
+ case REPRINT:
+ (void) fclose(cfp);
+ return(REPRINT);
+ case FILTERERR:
+ case ACCESS:
+ bombed = i;
+ sendmail(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 (!SH && HL)
+ banner(line+1, jobname);
+ continue;
+
+ case 'M':
+ if (bombed < NOACCT) /* already sent if >= NOACCT */
+ sendmail(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(format, file)
+ int format;
+ char *file;
+{
+ register int n;
+ register char *prog;
+ int dtablesize, fi, fo;
+ FILE *fp;
+ char *av[15], buf[BUFSIZ];
+ int pid, p[2], stopped = 0;
+ union wait status;
+ struct stat stb;
+
+ if (lstat(file, &stb) < 0 || (fi = open(file, O_RDONLY)) < 0)
+ return(ERROR);
+ /*
+ * Check to see if data file is a symbolic link. If so, it should
+ * still point to the same file or someone is trying to print
+ * something he shouldn't.
+ */
+ if ((stb.st_mode & S_IFMT) == S_IFLNK && fstat(fi, &stb) == 0 &&
+ (stb.st_dev != fdev || stb.st_ino != fino))
+ return(ACCESS);
+ if (!SF && !tof) { /* start on a fresh page */
+ (void) write(ofd, FF, strlen(FF));
+ tof = 1;
+ }
+ if (IF == NULL && (format == 'f' || format == 'l')) {
+ 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 (IF == NULL) { /* use output filter */
+ prog = _PATH_PR;
+ av[0] = "pr";
+ av[1] = width;
+ av[2] = length;
+ av[3] = "-h";
+ av[4] = *title ? title : " ";
+ av[5] = "-F";
+ av[6] = 0;
+ fo = ofd;
+ goto start;
+ }
+ pipe(p);
+ if ((prchild = dofork(DORETURN)) == 0) { /* child */
+ dup2(fi, 0); /* file is stdin */
+ dup2(p[1], 1); /* pipe is stdout */
+ closelog();
+ for (n = 3, dtablesize = getdtablesize();
+ n < dtablesize; n++)
+ (void) close(n);
+ execl(_PATH_PR, "pr", width, length,
+ "-h", *title ? title : " ", "-F", 0);
+ syslog(LOG_ERR, "cannot execl %s", _PATH_PR);
+ exit(2);
+ }
+ (void) close(p[1]); /* close output side */
+ (void) close(fi);
+ if (prchild < 0) {
+ prchild = 0;
+ (void) close(p[0]);
+ return(ERROR);
+ }
+ fi = p[0]; /* use pipe for input */
+ case 'f': /* print plain text file */
+ prog = IF;
+ av[1] = width;
+ av[2] = length;
+ av[3] = indent;
+ n = 4;
+ break;
+ case 'l': /* like 'f' but pass control characters */
+ prog = IF;
+ av[1] = "-c";
+ av[2] = width;
+ av[3] = length;
+ av[4] = indent;
+ n = 5;
+ break;
+ case 'r': /* print a fortran text file */
+ prog = RF;
+ 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", 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') ? TF : (format == 'n') ? NF : DF;
+ av[1] = pxwidth;
+ av[2] = pxlength;
+ n = 3;
+ break;
+ case 'c': /* print cifplot output */
+ prog = CF;
+ av[1] = pxwidth;
+ av[2] = pxlength;
+ n = 3;
+ break;
+ case 'g': /* print plot(1G) output */
+ prog = GF;
+ av[1] = pxwidth;
+ av[2] = pxlength;
+ n = 3;
+ break;
+ case 'v': /* print raster output */
+ prog = VF;
+ av[1] = pxwidth;
+ av[2] = pxlength;
+ n = 3;
+ break;
+ default:
+ (void) close(fi);
+ syslog(LOG_ERR, "%s: illegal format character '%c'",
+ printer, format);
+ return(ERROR);
+ }
+ if (prog == NULL) {
+ (void) close(fi);
+ syslog(LOG_ERR,
+ "%s: no filter found in printcap for format character '%c'",
+ printer, format);
+ return(ERROR);
+ }
+ if ((av[0] = strrchr(prog, '/')) != NULL)
+ av[0]++;
+ else
+ av[0] = prog;
+ av[n++] = "-n";
+ av[n++] = logname;
+ av[n++] = "-h";
+ av[n++] = fromhost;
+ av[n++] = AF;
+ av[n] = 0;
+ fo = pfd;
+ if (ofilter > 0) { /* stop output filter */
+ write(ofd, "\031\1", 2);
+ while ((pid =
+ wait3((int *)&status, WUNTRACED, 0)) > 0 && pid != ofilter)
+ ;
+ if (status.w_stopval != WSTOPPED) {
+ (void) close(fi);
+ syslog(LOG_WARNING,
+ "%s: output filter died (retcode=%d termsig=%d)",
+ printer, status.w_retcode, status.w_termsig);
+ return(REPRINT);
+ }
+ stopped++;
+ }
+start:
+ if ((child = dofork(DORETURN)) == 0) { /* child */
+ dup2(fi, 0);
+ dup2(fo, 1);
+ n = open(tempfile, O_WRONLY|O_CREAT|O_TRUNC, 0664);
+ if (n >= 0)
+ dup2(n, 2);
+ closelog();
+ for (n = 3, dtablesize = getdtablesize(); n < dtablesize; n++)
+ (void) close(n);
+ execv(prog, av);
+ syslog(LOG_ERR, "cannot execv %s", prog);
+ exit(2);
+ }
+ (void) close(fi);
+ if (child < 0)
+ status.w_retcode = 100;
+ else
+ while ((pid = wait((int *)&status)) > 0 && pid != child)
+ ;
+ child = 0;
+ prchild = 0;
+ if (stopped) { /* restart output filter */
+ if (kill(ofilter, SIGCONT) < 0) {
+ syslog(LOG_ERR, "cannot restart output filter");
+ exit(1);
+ }
+ }
+ tof = 0;
+
+ /* Copy filter output to "lf" logfile */
+ if ((fp = fopen(tempfile, "r"))) {
+ while (fgets(buf, sizeof(buf), fp))
+ fputs(buf, stderr);
+ fclose(fp);
+ }
+
+ if (!WIFEXITED(status)) {
+ syslog(LOG_WARNING, "%s: filter '%c' terminated (termsig=%d)",
+ printer, format, status.w_termsig);
+ return(ERROR);
+ }
+ switch (status.w_retcode) {
+ case 0:
+ tof = 1;
+ return(OK);
+ case 1:
+ return(REPRINT);
+ case 2:
+ return(ERROR);
+ default:
+ syslog(LOG_WARNING, "%s: filter '%c' exited (retcode=%d)",
+ printer, format, status.w_retcode);
+ return(FILTERERR);
+ }
+}
+
+/*
+ * Send the daemon control file (cf) and any data files.
+ * Return -1 if a non-recoverable error occured, 1 if a recoverable error and
+ * 0 if all is well.
+ */
+static int
+sendit(file)
+ char *file;
+{
+ register int i, err = OK;
+ char *cp, last[BUFSIZ];
+
+ /*
+ * open control file
+ */
+ if ((cfp = fopen(file, "r")) == NULL)
+ return(OK);
+ /*
+ * read the control file for work to do
+ *
+ * file format -- first character in the line is a command
+ * rest of the line is the argument.
+ * commands of interest are:
+ *
+ * a-z -- "file name" name of file to print
+ * U -- "unlink" name of file to remove
+ * (after we print it. (Pass 2 only)).
+ */
+
+ /*
+ * pass 1
+ */
+ while (getline(cfp)) {
+ again:
+ if (line[0] == 'S') {
+ cp = line+1;
+ i = 0;
+ while (*cp >= '0' && *cp <= '9')
+ i = i * 10 + (*cp++ - '0');
+ fdev = i;
+ cp++;
+ i = 0;
+ while (*cp >= '0' && *cp <= '9')
+ i = i * 10 + (*cp++ - '0');
+ fino = i;
+ } else if (line[0] == 'H') {
+ strcpy(fromhost, line+1);
+ if (class[0] == '\0')
+ strncpy(class, line+1, sizeof(class) - 1);
+ } else if (line[0] == 'P') {
+ strncpy(logname, line+1, sizeof(logname) - 1);
+ if (RS) { /* restricted */
+ if (getpwnam(logname) == NULL) {
+ sendmail(line+1, NOACCT);
+ err = ERROR;
+ break;
+ }
+ }
+ } else if (line[0] == 'I') {
+ strncpy(indent+2, line+1, sizeof(indent) - 3);
+ } else if (line[0] >= 'a' && line[0] <= 'z') {
+ strcpy(last, line);
+ while (i = getline(cfp))
+ if (strcmp(last, line))
+ break;
+ switch (sendfile('\3', last+1, *last)) {
+ case OK:
+ if (i)
+ goto again;
+ break;
+ case REPRINT:
+ (void) fclose(cfp);
+ return(REPRINT);
+ case ACCESS:
+ sendmail(logname, ACCESS);
+ case ERROR:
+ err = ERROR;
+ }
+ break;
+ }
+ }
+ if (err == OK && sendfile('\2', file, '\0') > 0) {
+ (void) fclose(cfp);
+ return(REPRINT);
+ }
+ /*
+ * pass 2
+ */
+ fseek(cfp, 0L, 0);
+ while (getline(cfp))
+ if (line[0] == 'U' && !strchr(line+1, '/'))
+ (void) unlink(line+1);
+ /*
+ * clean-up in case another control file exists
+ */
+ (void) fclose(cfp);
+ (void) unlink(file);
+ return(err);
+}
+
+/*
+ * Send a data file to the remote machine and spool it.
+ * Return positive if we should try resending.
+ */
+static int
+sendfile(type, file, format)
+ int type;
+ char *file;
+ char format;
+{
+ register int f, i, amt;
+ struct stat stb;
+ char buf[BUFSIZ];
+ int sizerr, resp, closedpr;
+
+ if (lstat(file, &stb) < 0 || (f = open(file, O_RDONLY)) < 0)
+ return(ERROR);
+ /*
+ * Check to see if data file is a symbolic link. If so, it should
+ * still point to the same file or someone is trying to print something
+ * he shouldn't.
+ */
+ if ((stb.st_mode & S_IFMT) == S_IFLNK && fstat(f, &stb) == 0 &&
+ (stb.st_dev != fdev || stb.st_ino != fino))
+ return(ACCESS);
+
+ sizerr = 0;
+ closedpr = 0;
+ if (type == '\3') {
+ if (IF) {
+ /*
+ * We're sending something with an ifilter, we have to
+ * run the ifilter and store the output as a
+ * temporary file (tfile)... the protocol requires us
+ * to send the file size
+ */
+ char *av[15];
+ int n;
+ int nfd;
+ int ifilter;
+ union wait status;
+
+ strcpy(tfile,TFILENAME);
+ if ((tfd = mkstemp(tfile)) == -1) {
+ syslog(LOG_ERR, "mkstemp: %m");
+ return(ERROR);
+ }
+ if ((av[0] = strrchr(IF, '/')) == NULL)
+ av[0] = IF;
+ else
+ av[0]++;
+ if (format == 'l')
+ av[n=1] = "-c";
+ else
+ n = 0;
+ av[++n] = width;
+ av[++n] = length;
+ av[++n] = indent;
+ av[++n] = "-n";
+ av[++n] = logname;
+ av[++n] = "-h";
+ av[++n] = fromhost;
+ av[++n] = AF;
+ av[++n] = 0;
+ if ((ifilter = dofork(DORETURN)) == 0) { /* child */
+ dup2(f, 0);
+ dup2(tfd, 1);
+ n = open(tempfile, O_WRONLY|O_CREAT|O_TRUNC, 0664);
+ if (n >= 0)
+ dup2(n, 2);
+ closelog();
+ for (n = 3, nfd = getdtablesize(); n < nfd; n++)
+ (void) close(n);
+ execv(IF, av);
+ syslog(LOG_ERR, "cannot execv %s", IF);
+ exit(2);
+ }
+ (void) close(f);
+ if (ifilter < 0)
+ status.w_retcode = 100;
+ else
+ while ((pid = wait((int *)&status)) > 0 &&
+ pid != ifilter)
+ ;
+ switch (status.w_retcode) {
+ case 0:
+ break;
+ case 1:
+ unlink(tfile);
+ return(REPRINT);
+ case 2:
+ unlink(tfile);
+ return(ERROR);
+ default:
+ syslog(LOG_WARNING, "%s: filter '%c' exited"
+ " (retcode=%d)",
+ printer, format, status.w_retcode);
+ unlink(tfile);
+ return(FILTERERR);
+ }
+ if (fstat(tfd, &stb) < 0) /* the size of tfile */
+ return(ERROR);
+ f = tfd;
+ lseek(f,0,SEEK_SET);
+ } else if (ofilter) {
+ /*
+ * We're sending something with an ofilter, we have to
+ * store the output as a temporary file (tfile)... the
+ * protocol requires us to send the file size
+ */
+ int i;
+ for (i = 0; i < stb.st_size; i += BUFSIZ) {
+ amt = BUFSIZ;
+ if (i + amt > stb.st_size)
+ amt = stb.st_size - i;
+ if (sizerr == 0 && read(f, buf, amt) != amt) {
+ sizerr = 1;
+ break;
+ }
+ if (write(ofd, buf, amt) != amt) {
+ (void) close(f);
+ return(REPRINT);
+ }
+ }
+ close(ofd);
+ close(f);
+ while ((i = wait(NULL)) > 0 && i != ofilter)
+ ;
+ ofilter = 0;
+ if (fstat(tfd, &stb) < 0) { /* the size of tfile */
+ openpr();
+ return(ERROR);
+ }
+ f = tfd;
+ lseek(f,0,SEEK_SET);
+ closedpr = 1;
+ }
+ }
+
+ (void) sprintf(buf, "%c%qd %s\n", type, stb.st_size, file);
+ amt = strlen(buf);
+ for (i = 0; ; i++) {
+ if (write(pfd, buf, amt) != amt ||
+ (resp = response()) < 0 || resp == '\1') {
+ (void) close(f);
+ if (tfd != -1 && type == '\3') {
+ tfd = -1;
+ unlink(tfile);
+ if (closedpr)
+ openpr();
+ }
+ return(REPRINT);
+ } else if (resp == '\0')
+ break;
+ if (i == 0)
+ pstatus("no space on remote; waiting for queue to drain");
+ if (i == 10)
+ syslog(LOG_ALERT, "%s: can't send to %s; queue full",
+ printer, RM);
+ sleep(5 * 60);
+ }
+ if (i)
+ pstatus("sending to %s", RM);
+ for (i = 0; i < stb.st_size; i += BUFSIZ) {
+ amt = BUFSIZ;
+ if (i + amt > stb.st_size)
+ amt = stb.st_size - i;
+ if (sizerr == 0 && read(f, buf, amt) != amt)
+ sizerr = 1;
+ if (write(pfd, buf, amt) != amt) {
+ (void) close(f);
+ if (tfd != -1 && type == '\3') {
+ tfd = -1;
+ unlink(tfile);
+ if (closedpr)
+ openpr();
+ }
+ return(REPRINT);
+ }
+ }
+
+ (void) close(f);
+ if (tfd != -1 && type == '\3') {
+ tfd = -1;
+ unlink(tfile);
+ }
+ if (sizerr) {
+ syslog(LOG_INFO, "%s: %s: changed size", printer, file);
+ /* tell recvjob to ignore this file */
+ (void) write(pfd, "\1", 1);
+ if (closedpr)
+ openpr();
+ return(ERROR);
+ }
+ if (write(pfd, "", 1) != 1 || response()) {
+ if (closedpr)
+ openpr();
+ return(REPRINT);
+ }
+ if (closedpr)
+ openpr();
+ return(OK);
+}
+
+/*
+ * Check to make sure there have been no errors and that both programs
+ * are in sync with eachother.
+ * Return non-zero if the connection was lost.
+ */
+static char
+response()
+{
+ char resp;
+
+ if (read(pfd, &resp, 1) != 1) {
+ syslog(LOG_INFO, "%s: lost connection", printer);
+ return(-1);
+ }
+ return(resp);
+}
+
+/*
+ * Banner printing stuff
+ */
+static void
+banner(name1, name2)
+ char *name1, *name2;
+{
+ time_t tvec;
+
+ time(&tvec);
+ if (!SF && !tof)
+ (void) write(ofd, FF, strlen(FF));
+ if (SB) { /* 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(ofd, name1, '\0');
+ (void) write(ofd, "\n\n", 2);
+ scan_out(ofd, name2, '\0');
+ if (class[0]) {
+ (void) write(ofd,"\n\n\n",3);
+ scan_out(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 (!SF)
+ (void) write(ofd, FF, strlen(FF));
+ tof = 1;
+}
+
+static char *
+scnline(key, p, c)
+ register int key;
+ register char *p;
+ int c;
+{
+ register scnwidth;
+
+ for (scnwidth = WIDTH; --scnwidth;) {
+ key <<= 1;
+ *p++ = key & 0200 ? c : BACKGND;
+ }
+ return (p);
+}
+
+#define TRC(q) (((q)-' ')&0177)
+
+static void
+scan_out(scfd, scsp, dlm)
+ int scfd, dlm;
+ char *scsp;
+{
+ register char *strp;
+ register nchrs, j;
+ char outbuf[LINELEN+1], *sp, c, cc;
+ int d, scnhgt;
+
+ for (scnhgt = 0; scnhgt++ < HEIGHT+DROP; ) {
+ strp = &outbuf[0];
+ sp = scsp;
+ for (nchrs = 0; ; ) {
+ d = dropit(c = TRC(cc = *sp++));
+ if ((!d && scnhgt > HEIGHT) || (scnhgt <= DROP && d))
+ for (j = WIDTH; --j;)
+ *strp++ = BACKGND;
+ else
+ strp = scnline(scnkey[c][scnhgt-1-d], strp, cc);
+ if (*sp == dlm || *sp == '\0' || nchrs++ >= PW/(WIDTH+1)-1)
+ break;
+ *strp++ = BACKGND;
+ *strp++ = BACKGND;
+ }
+ while (*--strp == BACKGND && strp >= outbuf)
+ ;
+ strp++;
+ *strp++ = '\n';
+ (void) write(scfd, outbuf, strp-outbuf);
+ }
+}
+
+static int
+dropit(c)
+ int c;
+{
+ switch(c) {
+
+ case TRC('_'):
+ case TRC(';'):
+ case TRC(','):
+ case TRC('g'):
+ case TRC('j'):
+ case TRC('p'):
+ case TRC('q'):
+ case TRC('y'):
+ return (DROP);
+
+ default:
+ return (0);
+ }
+}
+
+/*
+ * sendmail ---
+ * tell people about job completion
+ */
+static void
+sendmail(user, bombed)
+ char *user;
+ int bombed;
+{
+ register int i;
+ int dtablesize;
+ int p[2], s;
+ register char *cp;
+ struct stat stb;
+ FILE *fp;
+
+ pipe(p);
+ if ((s = dofork(DORETURN)) == 0) { /* child */
+ dup2(p[0], 0);
+ closelog();
+ for (i = 3, dtablesize = getdtablesize(); i < dtablesize; i++)
+ (void) close(i);
+ if ((cp = strrchr(_PATH_SENDMAIL, '/')) != NULL)
+ cp++;
+ else
+ cp = _PATH_SENDMAIL;
+ execl(_PATH_SENDMAIL, cp, "-t", 0);
+ exit(0);
+ } else if (s > 0) { /* parent */
+ dup2(p[1], 1);
+ printf("To: %s@%s\n", user, fromhost);
+ printf("Subject: %s printer job \"%s\"\n", printer,
+ *jobname ? jobname : "<unknown>");
+ printf("Reply-To: root@%s\n\n", host);
+ printf("Your printer job ");
+ if (*jobname)
+ printf("(%s) ", jobname);
+ switch (bombed) {
+ case OK:
+ printf("\ncompleted successfully\n");
+ cp = "OK";
+ break;
+ default:
+ case FATALERR:
+ printf("\ncould not be printed\n");
+ cp = "FATALERR";
+ break;
+ case NOACCT:
+ printf("\ncould not be printed without an account on %s\n", host);
+ cp = "NOACCT";
+ break;
+ case FILTERERR:
+ if (stat(tempfile, &stb) < 0 || stb.st_size == 0 ||
+ (fp = fopen(tempfile, "r")) == NULL) {
+ printf("\nhad some errors and may not have printed\n");
+ break;
+ }
+ printf("\nhad the following errors and may not have printed:\n");
+ while ((i = getc(fp)) != EOF)
+ putchar(i);
+ (void) fclose(fp);
+ cp = "FILTERERR";
+ break;
+ case ACCESS:
+ printf("\nwas not printed because it was not linked to the original file\n");
+ cp = "ACCESS";
+ }
+ fflush(stdout);
+ (void) close(1);
+ }
+ (void) close(p[0]);
+ (void) close(p[1]);
+ wait(NULL);
+ syslog(LOG_INFO, "mail sent to user %s about job %s on printer %s (%s)",
+ user, *jobname ? jobname : "<unknown>", printer, cp);
+}
+
+/*
+ * dofork - fork with retries on failure
+ */
+static int
+dofork(action)
+ int action;
+{
+ register int i, pid;
+
+ for (i = 0; i < 20; i++) {
+ if ((pid = fork()) < 0) {
+ sleep((unsigned)(i*i));
+ continue;
+ }
+ /*
+ * Child should run as daemon instead of root
+ */
+ if (pid == 0)
+ setuid(DU);
+ return(pid);
+ }
+ syslog(LOG_ERR, "can't fork");
+
+ switch (action) {
+ case DORETURN:
+ return (-1);
+ default:
+ syslog(LOG_ERR, "bad action (%d) to dofork", action);
+ /*FALL THRU*/
+ case DOABORT:
+ exit(1);
+ }
+ /*NOTREACHED*/
+}
+
+/*
+ * Kill child processes to abort current job.
+ */
+static void
+abortpr(signo)
+ int signo;
+{
+ (void) unlink(tempfile);
+ kill(0, SIGINT);
+ if (ofilter > 0)
+ kill(ofilter, SIGCONT);
+ while (wait(NULL) > 0)
+ ;
+ if (ofilter > 0 && tfd != -1)
+ unlink(tfile);
+ exit(0);
+}
+
+static void
+init()
+{
+ int status;
+ char *s;
+
+ if ((status = cgetent(&bp, printcapdb, printer)) == -2) {
+ syslog(LOG_ERR, "can't open printer description file");
+ exit(1);
+ } else if (status == -1) {
+ syslog(LOG_ERR, "unknown printer: %s", printer);
+ exit(1);
+ } else if (status == -3)
+ fatal("potential reference loop detected in printcap file");
+
+ if (cgetstr(bp, "lp", &LP) == -1)
+ LP = _PATH_DEFDEVLP;
+ if (cgetstr(bp, "rp", &RP) == -1)
+ RP = DEFLP;
+ if (cgetstr(bp, "lo", &LO) == -1)
+ LO = DEFLOCK;
+ if (cgetstr(bp, "st", &ST) == -1)
+ ST = DEFSTAT;
+ if (cgetstr(bp, "lf", &LF) == -1)
+ LF = _PATH_CONSOLE;
+ if (cgetstr(bp, "sd", &SD) == -1)
+ SD = _PATH_DEFSPOOL;
+ if (cgetnum(bp, "du", &DU) < 0)
+ DU = DEFUID;
+ if (cgetstr(bp,"ff", &FF) == -1)
+ FF = DEFFF;
+ if (cgetnum(bp, "pw", &PW) < 0)
+ PW = DEFWIDTH;
+ sprintf(&width[2], "%ld", PW);
+ if (cgetnum(bp, "pl", &PL) < 0)
+ PL = DEFLENGTH;
+ if (cgetnum(bp, "ct", &CT) < 0)
+ CT = DEFTIMEOUT;
+ sprintf(&length[2], "%ld", PL);
+ if (cgetnum(bp,"px", &PX) < 0)
+ PX = 0;
+ sprintf(&pxwidth[2], "%ld", PX);
+ if (cgetnum(bp, "py", &PY) < 0)
+ PY = 0;
+ sprintf(&pxlength[2], "%ld", PY);
+ cgetstr(bp, "rm", &RM);
+ if ((s = checkremote()))
+ syslog(LOG_WARNING, s);
+
+ cgetstr(bp, "af", &AF);
+ cgetstr(bp, "of", &OF);
+ cgetstr(bp, "if", &IF);
+ cgetstr(bp, "rf", &RF);
+ cgetstr(bp, "tf", &TF);
+ cgetstr(bp, "nf", &NF);
+ cgetstr(bp, "df", &DF);
+ cgetstr(bp, "gf", &GF);
+ cgetstr(bp, "vf", &VF);
+ cgetstr(bp, "cf", &CF);
+ cgetstr(bp, "tr", &TR);
+ cgetstr(bp, "ms", &MS);
+
+ RS = (cgetcap(bp, "rs", ':') != NULL);
+ SF = (cgetcap(bp, "sf", ':') != NULL);
+ SH = (cgetcap(bp, "sh", ':') != NULL);
+ SB = (cgetcap(bp, "sb", ':') != NULL);
+ HL = (cgetcap(bp, "hl", ':') != NULL);
+ RW = (cgetcap(bp, "rw", ':') != NULL);
+
+ cgetnum(bp, "br", &BR);
+
+ tof = (cgetcap(bp, "fo", ':') == NULL);
+}
+
+/*
+ * Acquire line printer or remote connection.
+ */
+static void
+openpr()
+{
+ register int i;
+ int dtablesize;
+ char *cp;
+
+ if (!remote && *LP) {
+ if (cp = strchr(LP, '@'))
+ opennet(cp);
+ else
+ opentty();
+ } else if (remote) {
+ openrem();
+ } else {
+ syslog(LOG_ERR, "%s: no line printer device or host name",
+ printer);
+ exit(1);
+ }
+
+ /*
+ * Start up an output filter, if needed.
+ */
+ if (OF && !IF && !ofilter) {
+ int p[2];
+
+ pipe(p);
+ if (remote) {
+ strcpy(tfile,TFILENAME);
+ tfd = mkstemp(tfile);
+ }
+ if ((ofilter = dofork(DOABORT)) == 0) { /* child */
+ dup2(p[0], 0); /* pipe is std in */
+ /* tfile/printer is stdout */
+ dup2(remote ? tfd : pfd, 1);
+ closelog();
+ for (i = 3, dtablesize = getdtablesize();
+ i < dtablesize; i++)
+ (void) close(i);
+ if ((cp = strrchr(OF, '/')) == NULL)
+ cp = OF;
+ else
+ cp++;
+ execl(OF, cp, width, length, 0);
+ syslog(LOG_ERR, "%s: %s: %m", printer, OF);
+ exit(1);
+ }
+ (void) close(p[0]); /* close input side */
+ ofd = p[1]; /* use pipe for output */
+ } else {
+ ofd = pfd;
+ ofilter = 0;
+ }
+}
+
+/*
+ * Printer connected directly to the network
+ * or to a terminal server on the net
+ */
+static void
+opennet(cp)
+ char *cp;
+{
+ register int i;
+ int resp, port;
+ char save_ch;
+ void (*savealrm)(int);
+
+ save_ch = *cp;
+ *cp = '\0';
+ port = atoi(LP);
+ if (port <= 0) {
+ syslog(LOG_ERR, "%s: bad port number: %s", printer, LP);
+ exit(1);
+ }
+ *cp++ = save_ch;
+
+ for (i = 1; ; i = i < 256 ? i << 1 : i) {
+ resp = -1;
+ savealrm = signal(SIGALRM, alarmhandler);
+ alarm(CT);
+ pfd = getport(cp, 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("waiting for %s to come up", LP);
+ else
+ pstatus("waiting for access to printer on %s", LP);
+ }
+ sleep(i);
+ }
+ pstatus("sending to %s port %d", cp, port);
+}
+
+/*
+ * Printer is connected to an RS232 port on this host
+ */
+static void
+opentty()
+{
+ register int i;
+ int resp, port;
+
+ for (i = 1; ; i = i < 32 ? i << 1 : i) {
+ pfd = open(LP, RW ? O_RDWR : O_WRONLY);
+ if (pfd >= 0) {
+ delay(500);
+ break;
+ }
+ if (errno == ENOENT) {
+ syslog(LOG_ERR, "%s: %m", LP);
+ exit(1);
+ }
+ if (i == 1)
+ pstatus("waiting for %s to become ready (offline ?)",
+ printer);
+ sleep(i);
+ }
+ if (isatty(pfd))
+ setty();
+ pstatus("%s is ready and printing", printer);
+}
+
+/*
+ * Printer is on a remote host
+ */
+static void
+openrem()
+{
+ register int i, n;
+ int resp;
+ void (*savealrm)(int);
+
+ for (i = 1; ; i = i < 256 ? i << 1 : i) {
+ resp = -1;
+ savealrm = signal(SIGALRM, alarmhandler);
+ alarm(CT);
+ pfd = getport(RM, 0);
+ alarm(0);
+ (void)signal(SIGALRM, savealrm);
+ if (pfd >= 0) {
+ (void) snprintf(line, sizeof(line), "\2%s\n", RP);
+ n = strlen(line);
+ if (write(pfd, line, n) == n &&
+ (resp = response()) == '\0')
+ break;
+ (void) close(pfd);
+ }
+ if (i == 1) {
+ if (resp < 0)
+ pstatus("waiting for %s to come up", RM);
+ else {
+ pstatus("waiting for queue to be enabled on %s",
+ RM);
+ i = 256;
+ }
+ }
+ sleep(i);
+ }
+ pstatus("sending to %s", RM);
+}
+
+struct bauds {
+ int baud;
+ int speed;
+} bauds[] = {
+ 50, B50,
+ 75, B75,
+ 110, B110,
+ 134, B134,
+ 150, B150,
+ 200, B200,
+ 300, B300,
+ 600, B600,
+ 1200, B1200,
+ 1800, B1800,
+ 2400, B2400,
+ 4800, B4800,
+ 9600, B9600,
+ 19200, EXTA,
+ 38400, EXTB,
+ 57600, B57600,
+ 115200, B115200,
+ 0, 0
+};
+
+/*
+ * setup tty lines.
+ */
+static void
+setty()
+{
+ struct termios ttybuf;
+ struct bauds *bp;
+
+ if (ioctl(pfd, TIOCEXCL, (char *)0) < 0) {
+ syslog(LOG_ERR, "%s: ioctl(TIOCEXCL): %m", printer);
+ exit(1);
+ }
+ if (tcgetattr(pfd, &ttybuf) < 0) {
+ syslog(LOG_ERR, "%s: tcgetattr: %m", printer);
+ exit(1);
+ }
+ if (BR > 0) {
+ for (bp = bauds; bp->baud; bp++)
+ if (BR == bp->baud)
+ break;
+ if (!bp->baud) {
+ syslog(LOG_ERR, "%s: illegal baud rate %d", printer, BR);
+ exit(1);
+ }
+ cfsetspeed(&ttybuf, bp->speed);
+ }
+ if (MS) {
+ char *s = strdup(MS), *tmp;
+
+ while (tmp = strsep (&s, ",")) {
+ msearch(tmp, &ttybuf);
+ }
+ }
+ if (MS || (BR > 0)) {
+ if (tcsetattr(pfd, TCSAFLUSH, &ttybuf) == -1) {
+ syslog(LOG_ERR, "%s: tcsetattr: %m", printer);
+ }
+ }
+}
+
+#ifdef __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+static void
+#ifdef __STDC__
+pstatus(const char *msg, ...)
+#else
+pstatus(msg, va_alist)
+ char *msg;
+ va_dcl
+#endif
+{
+ register int fd;
+ char buf[BUFSIZ];
+ va_list ap;
+#ifdef __STDC__
+ va_start(ap, msg);
+#else
+ va_start(ap);
+#endif
+
+ umask(0);
+ fd = open(ST, O_WRONLY|O_CREAT, 0664);
+ if (fd < 0 || flock(fd, LOCK_EX) < 0) {
+ syslog(LOG_ERR, "%s: %s: %m", printer, ST);
+ exit(1);
+ }
+ ftruncate(fd, 0);
+ (void)vsnprintf(buf, sizeof(buf) - 1, msg, ap);
+ va_end(ap);
+ strcat(buf, "\n");
+ (void) write(fd, buf, strlen(buf));
+ (void) close(fd);
+}
+
+void
+alarmhandler(signo)
+{
+ /* ignored */
+}
diff --git a/usr.sbin/lpr/lpd/recvjob.c b/usr.sbin/lpr/lpd/recvjob.c
new file mode 100644
index 0000000..0a10e49
--- /dev/null
+++ b/usr.sbin/lpr/lpd/recvjob.c
@@ -0,0 +1,372 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 1983, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)recvjob.c 8.2 (Berkeley) 4/27/95";
+#endif
+static const char rcsid[] =
+ "$Id: recvjob.c,v 1.10 1997/09/24 06:47:55 charnier Exp $";
+#endif /* not lint */
+
+/*
+ * Receive printer jobs from the network, queue them and
+ * start the printer daemon.
+ */
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+
+#include <unistd.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <syslog.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "lp.h"
+#include "lp.local.h"
+#include "extern.h"
+#include "pathnames.h"
+
+#define ack() (void) write(1, sp, 1);
+
+static char dfname[NAME_MAX]; /* data files */
+static int minfree; /* keep at least minfree blocks available */
+static char *sp = "";
+static char tfname[NAME_MAX]; /* tmp copy of cf before linking */
+
+static int chksize __P((int));
+static void frecverr __P((const char *, ...));
+static int noresponse __P((void));
+static void rcleanup __P((int));
+static int read_number __P((char *));
+static int readfile __P((char *, int));
+static int readjob __P((void));
+
+
+void
+recvjob()
+{
+ struct stat stb;
+ int status;
+
+ /*
+ * Perform lookup for printer name or abbreviation
+ */
+ if ((status = cgetent(&bp, printcapdb, printer)) == -2)
+ frecverr("cannot open printer description file");
+ else if (status == -1)
+ frecverr("unknown printer %s", printer);
+ else if (status == -3)
+ fatal("potential reference loop detected in printcap file");
+
+ if (cgetstr(bp, "lf", &LF) == -1)
+ LF = _PATH_CONSOLE;
+ if (cgetstr(bp, "sd", &SD) == -1)
+ SD = _PATH_DEFSPOOL;
+ if (cgetstr(bp, "lo", &LO) == -1)
+ LO = DEFLOCK;
+
+ (void) close(2); /* set up log file */
+ if (open(LF, O_WRONLY|O_APPEND, 0664) < 0) {
+ syslog(LOG_ERR, "%s: %m", LF);
+ (void) open(_PATH_DEVNULL, O_WRONLY);
+ }
+
+ if (chdir(SD) < 0)
+ frecverr("%s: %s: %m", printer, SD);
+ if (stat(LO, &stb) == 0) {
+ if (stb.st_mode & 010) {
+ /* queue is disabled */
+ putchar('\1'); /* return error code */
+ exit(1);
+ }
+ } else if (stat(SD, &stb) < 0)
+ frecverr("%s: %s: %m", printer, SD);
+ minfree = 2 * read_number("minfree"); /* scale KB to 512 blocks */
+ signal(SIGTERM, rcleanup);
+ signal(SIGPIPE, rcleanup);
+
+ if (readjob())
+ printjob();
+}
+
+/*
+ * Read printer jobs sent by lpd and copy them to the spooling directory.
+ * Return the number of jobs successfully transfered.
+ */
+static int
+readjob()
+{
+ register int size, nfiles;
+ register char *cp;
+
+ ack();
+ nfiles = 0;
+ for (;;) {
+ /*
+ * Read a command to tell us what to do
+ */
+ cp = line;
+ do {
+ if ((size = read(1, cp, 1)) != 1) {
+ if (size < 0)
+ frecverr("%s: lost connection",
+ printer);
+ return(nfiles);
+ }
+ } while (*cp++ != '\n' && (cp - line + 1) < sizeof(line));
+ if (cp - line + 1 >= sizeof(line))
+ frecverr("readjob overflow");
+ *--cp = '\0';
+ cp = line;
+ switch (*cp++) {
+ case '\1': /* cleanup because data sent was bad */
+ rcleanup(0);
+ continue;
+
+ case '\2': /* read cf file */
+ size = 0;
+ while (*cp >= '0' && *cp <= '9')
+ size = size * 10 + (*cp++ - '0');
+ if (*cp++ != ' ')
+ break;
+ /*
+ * host name has been authenticated, we use our
+ * view of the host name since we may be passed
+ * something different than what gethostbyaddr()
+ * returns
+ */
+ strncpy(cp + 6, from, sizeof(line) + line - cp - 7);
+ line[sizeof(line) - 1 ] = '\0';
+ strncpy(tfname, cp, sizeof(tfname) - 1);
+ tfname[sizeof (tfname) - 1] = '\0';
+ tfname[0] = 't';
+ if (strchr(tfname, '/'))
+ frecverr("readjob: %s: illegal path name",
+ tfname);
+ if (!chksize(size)) {
+ (void) write(1, "\2", 1);
+ continue;
+ }
+ if (!readfile(tfname, size)) {
+ rcleanup(0);
+ continue;
+ }
+ if (link(tfname, cp) < 0)
+ frecverr("%s: %m", tfname);
+ (void) unlink(tfname);
+ tfname[0] = '\0';
+ nfiles++;
+ continue;
+
+ case '\3': /* read df file */
+ size = 0;
+ while (*cp >= '0' && *cp <= '9')
+ size = size * 10 + (*cp++ - '0');
+ if (*cp++ != ' ')
+ break;
+ if (!chksize(size)) {
+ (void) write(1, "\2", 1);
+ continue;
+ }
+ (void) strncpy(dfname, cp, sizeof(dfname) - 1);
+ dfname[sizeof(dfname) - 1] = '\0';
+ if (strchr(dfname, '/'))
+ frecverr("readjob: %s: illegal path name",
+ dfname);
+ (void) readfile(dfname, size);
+ continue;
+ }
+ frecverr("protocol screwup: %s", line);
+ }
+}
+
+/*
+ * Read files send by lpd and copy them to the spooling directory.
+ */
+static int
+readfile(file, size)
+ char *file;
+ int size;
+{
+ register char *cp;
+ char buf[BUFSIZ];
+ register int i, j, amt;
+ int fd, err;
+
+ fd = open(file, O_CREAT|O_EXCL|O_WRONLY, FILMOD);
+ if (fd < 0)
+ frecverr("readfile: %s: illegal path name: %m", file);
+ ack();
+ err = 0;
+ for (i = 0; i < size; i += BUFSIZ) {
+ amt = BUFSIZ;
+ cp = buf;
+ if (i + amt > size)
+ amt = size - i;
+ do {
+ j = read(1, cp, amt);
+ if (j <= 0)
+ frecverr("lost connection");
+ amt -= j;
+ cp += j;
+ } while (amt > 0);
+ amt = BUFSIZ;
+ if (i + amt > size)
+ amt = size - i;
+ if (write(fd, buf, amt) != amt) {
+ err++;
+ break;
+ }
+ }
+ (void) close(fd);
+ if (err)
+ frecverr("%s: write error", file);
+ if (noresponse()) { /* file sent had bad data in it */
+ if (strchr(file, '/') == NULL)
+ (void) unlink(file);
+ return(0);
+ }
+ ack();
+ return(1);
+}
+
+static int
+noresponse()
+{
+ char resp;
+
+ if (read(1, &resp, 1) != 1)
+ frecverr("lost connection");
+ if (resp == '\0')
+ return(0);
+ return(1);
+}
+
+/*
+ * Check to see if there is enough space on the disk for size bytes.
+ * 1 == OK, 0 == Not OK.
+ */
+static int
+chksize(size)
+ int size;
+{
+ int spacefree;
+ struct statfs sfb;
+
+ if (statfs(".", &sfb) < 0) {
+ syslog(LOG_ERR, "%s: %m", "statfs(\".\")");
+ return (1);
+ }
+ spacefree = sfb.f_bavail * (sfb.f_bsize / 512);
+ size = (size + 511) / 512;
+ if (minfree + size > spacefree)
+ return(0);
+ return(1);
+}
+
+static int
+read_number(fn)
+ char *fn;
+{
+ char lin[80];
+ register FILE *fp;
+
+ if ((fp = fopen(fn, "r")) == NULL)
+ return (0);
+ if (fgets(lin, 80, fp) == NULL) {
+ fclose(fp);
+ return (0);
+ }
+ fclose(fp);
+ return (atoi(lin));
+}
+
+/*
+ * Remove all the files associated with the current job being transfered.
+ */
+static void
+rcleanup(signo)
+ int signo;
+{
+ if (tfname[0] && strchr(tfname, '/') == NULL)
+ (void) unlink(tfname);
+ if (dfname[0] && strchr(dfname, '/') == NULL) {
+ do {
+ do
+ (void) unlink(dfname);
+ while (dfname[2]-- != 'A');
+ dfname[2] = 'z';
+ } while (dfname[0]-- != 'd');
+ }
+ dfname[0] = '\0';
+}
+
+#ifdef __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+static void
+#ifdef __STDC__
+frecverr(const char *msg, ...)
+#else
+frecverr(msg, va_alist)
+ char *msg;
+ va_dcl
+#endif
+{
+ va_list ap;
+#ifdef __STDC__
+ va_start(ap, msg);
+#else
+ va_start(ap);
+#endif
+ rcleanup(0);
+ syslog(LOG_ERR, "%s", fromb);
+ vsyslog(LOG_ERR, msg, ap);
+ va_end(ap);
+ putchar('\1'); /* return error code */
+ exit(1);
+}
diff --git a/usr.sbin/lpr/lpq/Makefile b/usr.sbin/lpr/lpq/Makefile
new file mode 100644
index 0000000..0240b57
--- /dev/null
+++ b/usr.sbin/lpr/lpq/Makefile
@@ -0,0 +1,13 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= lpq
+CFLAGS+=-I${.CURDIR}/../common_source
+SRCS= lpq.c displayq.c common.c
+BINOWN= root
+BINGRP= daemon
+BINMODE=6555
+BINDIR= /usr/bin
+MAN1= lpq.1
+.PATH: ${.CURDIR}/../common_source
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/lpr/lpq/lpq.1 b/usr.sbin/lpr/lpq/lpq.1
new file mode 100644
index 0000000..04ecdad
--- /dev/null
+++ b/usr.sbin/lpr/lpq/lpq.1
@@ -0,0 +1,136 @@
+.\" Copyright (c) 1983, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)lpq.1 8.2 (Berkeley) 4/28/95
+.\"
+.Dd April 28, 1995
+.Dt LPQ 1
+.Os BSD 4.2
+.Sh NAME
+.Nm lpq
+.Nd spool queue examination program
+.Sh SYNOPSIS
+.Nm lpq
+.Op Fl a
+.Op Fl l
+.Op Fl P Ns Ar printer
+.Op job # ...
+.Op user ...
+.Sh DESCRIPTION
+.Nm Lpq
+examines the spooling area used by
+.Xr lpd 8
+for printing files on the line printer, and reports the status of the
+specified jobs or all jobs associated with a user.
+.Nm Lpq
+invoked
+without any arguments reports on any jobs currently in the queue.
+.Pp
+Options:
+.Pp
+.Bl -tag -width indent
+.It Fl P
+Specify a particular printer, otherwise the default
+line printer is used (or the value of the
+.Ev PRINTER
+variable in the
+environment). All other arguments supplied are interpreted as user
+names or job numbers to filter out only those jobs of interest.
+.It Fl l
+Information about each of the files comprising the job entry
+is printed.
+Normally, only as much information as will fit on one line is displayed.
+.It Fl a
+Report on the local queues for all printers,
+rather than just the specified printer.
+.El
+.Pp
+For each job submitted (i.e. invocation of
+.Xr lpr 1 )
+.Nm
+reports the user's name, current rank in the queue, the
+names of files comprising the job, the job identifier (a number which
+may be supplied to
+.Xr lprm 1
+for removing a specific job), and the total size in bytes.
+Job ordering is dependent on
+the algorithm used to scan the spooling directory and is supposed
+to be
+.Tn FIFO
+(First in First Out).
+File names comprising a job may be unavailable
+(when
+.Xr lpr 1
+is used as a sink in a pipeline) in which case the file
+is indicated as ``(standard input)''.
+.Pp
+If
+.Nm
+warns that there is no daemon present (i.e. due to some malfunction),
+the
+.Xr lpc 8
+command can be used to restart the printer daemon.
+.Sh ENVIRONMENT
+If the following environment variable exists, it is used by
+.Nm lpq :
+.Bl -tag -width PRINTER
+.It Ev PRINTER
+Specifies an alternate default printer.
+.El
+.Sh FILES
+.Bl -tag -width "/var/spool/*/lock" -compact
+.It Pa /etc/printcap
+To determine printer characteristics.
+.It Pa /var/spool/*
+The spooling directory, as determined from printcap.
+.It Pa /var/spool/*/cf*
+Control files specifying jobs.
+.It Pa /var/spool/*/lock
+The lock file to obtain the currently active job.
+.El
+.Sh SEE ALSO
+.Xr lpr 1 ,
+.Xr lprm 1 ,
+.Xr lpc 8 ,
+.Xr lpd 8
+.Sh HISTORY
+.Nm Lpq
+appeared in
+.Bx 3 .
+.Sh BUGS
+Due to the dynamic nature of the information in the spooling directory
+.Nm
+may report unreliably.
+Output formatting is sensitive to the line length of the terminal;
+this can results in widely spaced columns.
+.Sh DIAGNOSTICS
+Unable to open various files. The lock file being malformed. Garbage
+files when there is no daemon active, but files in the spooling directory.
diff --git a/usr.sbin/lpr/lpq/lpq.c b/usr.sbin/lpr/lpq/lpq.c
new file mode 100644
index 0000000..9dc9c8a
--- /dev/null
+++ b/usr.sbin/lpr/lpq/lpq.c
@@ -0,0 +1,177 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1983, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)lpq.c 8.3 (Berkeley) 5/10/95";
+#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 <syslog.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <ctype.h>
+#include "lp.h"
+#include "lp.local.h"
+#include "pathnames.h"
+
+int requ[MAXREQUESTS]; /* job number of spool entries */
+int requests; /* # of spool requests */
+char *user[MAXUSERS]; /* users to process */
+int users; /* # of users in user array */
+
+uid_t uid, euid;
+
+static int ckqueue __P((char *));
+static void usage __P((void));
+
+int
+main(argc, argv)
+ register int argc;
+ register char **argv;
+{
+ extern char *optarg;
+ extern int optind;
+ int ch, aflag, lflag;
+ char *buf, *cp;
+
+ euid = geteuid();
+ uid = getuid();
+ seteuid(uid);
+ name = *argv;
+ if (gethostname(host, sizeof(host)))
+ err(1, "gethostname");
+ openlog("lpd", 0, LOG_LPR);
+
+ aflag = lflag = 0;
+ while ((ch = getopt(argc, argv, "alP:")) != -1)
+ switch((char)ch) {
+ case 'a':
+ ++aflag;
+ break;
+ case 'l': /* long output */
+ ++lflag;
+ break;
+ case 'P': /* printer name */
+ printer = optarg;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+
+ if (!aflag && printer == NULL && (printer = getenv("PRINTER")) == NULL)
+ printer = DEFLP;
+
+ for (argc -= optind, argv += optind; argc; --argc, ++argv)
+ if (isdigit(argv[0][0])) {
+ if (requests >= MAXREQUESTS)
+ fatal("too many requests");
+ requ[requests++] = atoi(*argv);
+ }
+ else {
+ if (users >= MAXUSERS)
+ fatal("too many users");
+ user[users++] = *argv;
+ }
+
+ if (aflag) {
+ while (cgetnext(&buf, printcapdb) > 0) {
+ if (ckqueue(buf) <= 0) {
+ free(buf);
+ continue; /* no jobs */
+ }
+ for (cp = buf; *cp; cp++)
+ if (*cp == '|' || *cp == ':') {
+ *cp = '\0';
+ break;
+ }
+ printer = buf;
+ printf("%s:\n", printer);
+ displayq(lflag);
+ free(buf);
+ printf("\n");
+ }
+ } else
+ displayq(lflag);
+ exit(0);
+}
+
+static int
+ckqueue(cap)
+ char *cap;
+{
+ register struct dirent *d;
+ DIR *dirp;
+ char *spooldir;
+
+ if (cgetstr(cap, "sd", &spooldir) == -1)
+ spooldir = _PATH_DEFSPOOL;
+ if ((dirp = opendir(spooldir)) == NULL)
+ return (-1);
+ while ((d = readdir(dirp)) != NULL) {
+ if (d->d_name[0] != 'c' || d->d_name[1] != 'f')
+ continue; /* daemon control files only */
+ closedir(dirp);
+ return (1); /* found something */
+ }
+ closedir(dirp);
+ return (0);
+}
+
+static void
+usage()
+{
+ fprintf(stderr,
+ "usage: lpq [-a] [-l] [-Pprinter] [user ...] [job ...]\n");
+ exit(1);
+}
diff --git a/usr.sbin/lpr/lpr/Makefile b/usr.sbin/lpr/lpr/Makefile
new file mode 100644
index 0000000..eed68fd
--- /dev/null
+++ b/usr.sbin/lpr/lpr/Makefile
@@ -0,0 +1,13 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= lpr
+CFLAGS+=-I${.CURDIR}/../common_source
+SRCS= lpr.c startdaemon.c common.c
+BINOWN= root
+BINGRP= daemon
+BINMODE=6555
+BINDIR= /usr/bin
+MAN1= lpr.1
+.PATH: ${.CURDIR}/../common_source
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/lpr/lpr/lpr.1 b/usr.sbin/lpr/lpr/lpr.1
new file mode 100644
index 0000000..672edad
--- /dev/null
+++ b/usr.sbin/lpr/lpr/lpr.1
@@ -0,0 +1,253 @@
+.\" Copyright (c) 1980, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" From @(#)lpr.1 8.1 (Berkeley) 6/6/93
+.\" $Id: lpr.1,v 1.7 1997/02/22 16:06:19 peter Exp $
+.\" "
+.Dd June 6, 1993
+.Dt LPR 1
+.Os BSD 4
+.Sh NAME
+.Nm lpr
+.Nd off line print
+.Sh SYNOPSIS
+.Nm lpr
+.Op Fl P Ns Ar printer
+.Op Fl \&# Ns Ar num
+.Op Fl C Ar class
+.Op Fl J Ar job
+.Op Fl T Ar title
+.Op Fl U Ar user
+.Op Fl i Ar numcols
+.Op Fl 1234 Ar font
+.Op Fl w Ns Ar num
+.Op Fl cdfghlnmprstv
+.Op Ar name ...
+.Sh DESCRIPTION
+.Nm Lpr
+uses a spooling daemon to print the named files when facilities
+become available. If no names appear, the standard input is assumed.
+.Pp
+The following single letter options are used to notify the line printer
+spooler that the files are not standard text files. The spooling daemon will
+use the appropriate filters to print the data accordingly.
+.Bl -tag -width indent
+.It Fl c
+The files are assumed to contain data produced by
+.Xr cifplot 1
+.It Fl d
+The files are assumed to contain data from
+.Em tex
+.Pf ( Tn DVI
+format from Stanford).
+.It Fl f
+Use a filter which interprets the first character of each line as a
+standard
+.Tn FORTRAN
+carriage control character.
+.It Fl g
+The files are assumed to contain standard plot data as produced by the
+.Xr plot
+routines (see also
+.Xr plot
+for the filters used by the printer spooler).
+.It Fl l
+Use a filter which allows control characters to be printed and suppresses
+page breaks.
+.It Fl n
+The files are assumed to contain data from
+.Em ditroff
+(device independent troff).
+.It Fl p
+Use
+.Xr pr 1
+to format the files.
+.It Fl t
+The files are assumed to contain data from
+.Xr troff 1
+(cat phototypesetter commands).
+.It Fl v
+The files are assumed to contain a raster image for devices like the
+Benson Varian.
+.El
+.Pp
+These options apply to the handling of
+the print job:
+.Bl -tag -width indent
+.It Fl P
+Force output to a specific printer. Normally,
+the default printer is used (site dependent), or the value of the
+environment variable
+.Ev PRINTER
+is used.
+.It Fl h
+Suppress the printing of the burst page.
+.It Fl m
+Send mail upon completion.
+.It Fl r
+Remove the file upon completion of spooling or upon completion of
+printing (with the
+.Fl s
+option).
+.It Fl s
+Use symbolic links. Usually files are copied to the spool directory.
+The
+.Fl s
+option will use
+.Xr symlink 2
+to link data files rather than trying to copy them so large files can be
+printed. This means the files should
+not be modified or removed until they have been printed.
+.El
+.Pp
+The remaining options apply to copies, the page display, and headers:
+.Bl -tag -width indent
+.It Fl \&# Ns Ar num
+The quantity
+.Ar num
+is the number of copies desired of each file named. For example,
+.Bd -literal -offset indent
+lpr \-#3 foo.c bar.c more.c
+.Ed
+would result in 3 copies of the file foo.c, followed by 3 copies
+of the file bar.c, etc. On the other hand,
+.Bd -literal -offset indent
+cat foo.c bar.c more.c \&| lpr \-#3
+.Ed
+.Pp
+will give three copies of the concatenation of the files. Often
+a site will disable this feature to encourage use of a photocopier
+instead.
+.It Xo
+.Fl Ns Oo Cm 1234 Oc Ar font
+.Xc
+Specifies a
+.Ar font
+to be mounted on font position
+.Ar i .
+The daemon
+will construct a
+.Li .railmag
+file referencing
+the font pathname.
+.It Fl C Ar class
+Job classification
+to use on the burst page. For example,
+.Bd -literal -offset indent
+lpr \-C EECS foo.c
+.Ed
+.Pp
+causes the system name (the name returned by
+.Xr hostname 1 )
+to be replaced on the burst page by
+.Tn EECS ,
+and the file foo.c to be printed.
+.It Fl J Ar job
+Job name to print on the burst page.
+Normally, the first file's name is used.
+.It Fl T Ar title
+Title name for
+.Xr pr 1 ,
+instead of the file name.
+.It Fl U Ar user
+User name to print on the burst page,
+also for accounting purposes.
+This option is only honored if the real user-id is daemon
+(or that specified in the printcap file instead of daemon),
+and is intended for those instances where print filters wish to requeue jobs.
+.It Fl i numcols
+The output is indented by
+.Pq Ar numcols .
+.It Fl w Ns Ar num
+Uses
+.Ar num
+as the page width for
+.Xr pr 1 .
+.El
+.Sh ENVIRONMENT
+If the following environment variable exists, it is used by
+.Nm lpr :
+.Bl -tag -width PRINTER
+.It Ev PRINTER
+Specifies an alternate default printer.
+.El
+.Sh FILES
+.Bl -tag -width /var/spool/output/*/tf* -compact
+.It Pa /etc/passwd
+Personal identification.
+.It Pa /etc/printcap
+Printer capabilities data base.
+.It Pa /usr/sbin/lpd
+Line printer daemons.
+.It Pa /var/spool/output/*
+Directories used for spooling.
+.It Pa /var/spool/output/*/cf*
+Daemon control files.
+.It Pa /var/spool/output/*/df*
+Data files specified in "cf" files.
+.It Pa /var/spool/output/*/tf*
+Temporary copies of "cf" files.
+.El
+.Sh SEE ALSO
+.Xr lpq 1 ,
+.Xr lprm 1 ,
+.Xr pr 1 ,
+.Xr symlink 2 ,
+.Xr printcap 5 ,
+.Xr lpc 8 ,
+.Xr lpd 8
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 3 .
+.Sh DIAGNOSTICS
+If you try to spool too large a file, it will be truncated.
+.Nm Lpr
+will object to printing binary files.
+If a user other than root prints a file and spooling is disabled,
+.Nm
+will print a message saying so and will not put jobs in the queue.
+If a connection to
+.Xr lpd 8
+on the local machine cannot be made,
+.Nm
+will say that the daemon cannot be started.
+Diagnostics may be printed in the daemon's log file
+regarding missing spool files by
+.Xr lpd 8 .
+.Sh BUGS
+Fonts for
+.Xr troff 1
+and
+.Xr tex
+reside on the host with the printer. It is currently not possible to
+use local font libraries.
diff --git a/usr.sbin/lpr/lpr/lpr.c b/usr.sbin/lpr/lpr/lpr.c
new file mode 100644
index 0000000..a22ccc3
--- /dev/null
+++ b/usr.sbin/lpr/lpr/lpr.c
@@ -0,0 +1,792 @@
+/*
+ * Copyright (c) 1983, 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ * (c) UNIX System Laboratories, Inc.
+ * All or some portions of this file are derived from material licensed
+ * to the University of California by American Telephone and Telegraph
+ * Co. or Unix System Laboratories, Inc. and are reproduced herein with
+ * the permission of UNIX System Laboratories, Inc.
+ *
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 1983, 1989, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)from: lpr.c 8.4 (Berkeley) 4/28/95";
+#endif
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+/*
+ * lpr -- off line print
+ *
+ * Allows multiple printers and printers on remote machines by
+ * using information from a printer data base.
+ */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include <dirent.h>
+#include <fcntl.h>
+#include <a.out.h>
+#include <signal.h>
+#include <syslog.h>
+#include <pwd.h>
+#include <grp.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <string.h>
+#include "lp.h"
+#include "lp.local.h"
+#include "pathnames.h"
+
+static char *cfname; /* daemon control files, linked from tf's */
+static char *class = host; /* class title on header page */
+static char *dfname; /* data files */
+static char *fonts[4]; /* troff font names */
+static char format = 'f'; /* format char for printing files */
+static int hdr = 1; /* print header or not (default is yes) */
+static int iflag; /* indentation wanted */
+static int inchar; /* location to increment char in file names */
+static int indent; /* amount to indent */
+static char *jobname; /* job name on header page */
+static int mailflg; /* send mail */
+static int nact; /* number of jobs to act on */
+static int ncopies = 1; /* # of copies to make */
+static char *person; /* user name */
+static int qflag; /* q job, but don't exec daemon */
+static int rflag; /* remove files upon completion */
+static int sflag; /* symbolic link flag */
+static int tfd; /* control file descriptor */
+static char *tfname; /* tmp copy of cf before linking */
+static char *title; /* pr'ing title */
+static int userid; /* user id */
+static char *width; /* width for versatec printing */
+
+static struct stat statb;
+
+static void card __P((int, char *));
+static int checkwriteperm __P((char*, char *));
+static void chkprinter __P((char *));
+static void cleanup __P((int));
+static void copy __P((int, char []));
+static void fatal2 __P((const char *, ...));
+static char *itoa __P((int));
+static char *linked __P((char *));
+static char *lmktemp __P((char *, int, int));
+static void mktemps __P((void));
+static int nfile __P((char *));
+static int test __P((char *));
+static void usage __P((void));
+
+uid_t uid, euid;
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ struct passwd *pw;
+ struct group *gptr;
+ register char *arg, *cp;
+ char buf[BUFSIZ];
+ int c, i, f, errs;
+ struct stat stb;
+
+ euid = geteuid();
+ uid = getuid();
+ seteuid(uid);
+ if (signal(SIGHUP, SIG_IGN) != SIG_IGN)
+ signal(SIGHUP, cleanup);
+ if (signal(SIGINT, SIG_IGN) != SIG_IGN)
+ signal(SIGINT, cleanup);
+ if (signal(SIGQUIT, SIG_IGN) != SIG_IGN)
+ signal(SIGQUIT, cleanup);
+ if (signal(SIGTERM, SIG_IGN) != SIG_IGN)
+ signal(SIGTERM, cleanup);
+
+ name = argv[0];
+ gethostname(host, sizeof(host));
+ openlog("lpd", 0, LOG_LPR);
+
+ errs = 0;
+ while ((c = getopt(argc, argv,
+ ":#:1:2:3:4:C:J:P:T:U:cdfghi:lnmprstvw:")) != -1)
+ switch (c) {
+ case '#': /* n copies */
+ i = atoi(optarg);
+ if (i > 0)
+ ncopies = i;
+ break;
+
+ case '1': /* troff fonts */
+ case '2':
+ case '3':
+ case '4':
+ fonts[optopt - '1'] = optarg;
+ break;
+
+ case 'C': /* classification spec */
+ hdr++;
+ class = optarg;
+ break;
+
+ case 'J': /* job name */
+ hdr++;
+ jobname = optarg;
+ break;
+
+ case 'P': /* specifiy printer name */
+ printer = optarg;
+ break;
+
+ case 'T': /* pr's title line */
+ title = optarg;
+ break;
+
+ case 'U': /* user name */
+ hdr++;
+ person = 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': /* toggle want of header page */
+ hdr = !hdr;
+ break;
+
+ case 'i': /* indent output */
+ iflag++;
+ indent = atoi(optarg);
+ 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);
+ if (SC && ncopies > 1)
+ fatal2("multiple copies are not allowed");
+ if (MC > 0 && ncopies > MC)
+ fatal2("only %d copies are allowed", MC);
+ /*
+ * Get the identity of the person doing the lpr using the same
+ * algorithm as lprm.
+ */
+ userid = getuid();
+ if (userid != DU || person == 0) {
+ if ((pw = getpwuid(userid)) == NULL)
+ fatal2("Who are you?");
+ person = pw->pw_name;
+ }
+ /*
+ * Check for restricted group access.
+ */
+ if (RG != NULL && userid != DU) {
+ if ((gptr = getgrnam(RG)) == NULL)
+ fatal2("Restricted group specified incorrectly");
+ if (gptr->gr_gid != getgid()) {
+ while (*gptr->gr_mem != NULL) {
+ if ((strcmp(person, *gptr->gr_mem)) == 0)
+ break;
+ gptr->gr_mem++;
+ }
+ if (*gptr->gr_mem == NULL)
+ fatal2("Not a member of the restricted group");
+ }
+ }
+ /*
+ * Check to make sure queuing is enabled if userid is not root.
+ */
+ (void) snprintf(buf, sizeof(buf), "%s/%s", SD, LO);
+ if (userid && stat(buf, &stb) == 0 && (stb.st_mode & 010))
+ fatal2("Printer queue is disabled");
+ /*
+ * Initialize the control file.
+ */
+ mktemps();
+ tfd = nfile(tfname);
+ seteuid(euid);
+ (void) fchown(tfd, DU, -1); /* owned by daemon for protection */
+ seteuid(uid);
+ card('H', host);
+ card('P', person);
+ if (hdr) {
+ if (jobname == NULL) {
+ if (argc == 0)
+ jobname = "stdin";
+ else
+ jobname = (arg = strrchr(argv[0], '/')) ? arg+1 : argv[0];
+ }
+ card('J', jobname);
+ card('C', class);
+ card('L', person);
+ }
+ if (iflag)
+ card('I', itoa(indent));
+ if (mailflg)
+ card('M', person);
+ if (format == 't' || format == 'n' || format == 'd')
+ for (i = 0; i < 4; i++)
+ if (fonts[i] != NULL)
+ card('1'+i, fonts[i]);
+ if (width != NULL)
+ card('W', width);
+
+ /*
+ * Read the files and spool them.
+ */
+ if (argc == 0)
+ copy(0, " ");
+ else while (argc--) {
+ if (argv[0][0] == '-' && argv[0][1] == '\0') {
+ /* use stdin */
+ copy(0, " ");
+ argv++;
+ continue;
+ }
+ if ((f = test(arg = *argv++)) < 0)
+ continue; /* file unreasonable */
+
+ if (sflag && (cp = linked(arg)) != NULL) {
+ (void) snprintf(buf, sizeof(buf), "%d %d", statb.st_dev,
+ statb.st_ino);
+ card('S', buf);
+ if (format == 'p')
+ card('T', title ? title : arg);
+ for (i = 0; i < ncopies; i++)
+ card(format, &dfname[inchar-2]);
+ card('U', &dfname[inchar-2]);
+ if (f)
+ card('U', cp);
+ card('N', arg);
+ dfname[inchar]++;
+ nact++;
+ continue;
+ }
+ if (sflag)
+ printf("%s: %s: not linked, copying instead\n", name, arg);
+ if ((i = open(arg, O_RDONLY)) < 0) {
+ printf("%s: cannot open %s\n", name, arg);
+ } else {
+ copy(i, arg);
+ (void) close(i);
+ if (f && unlink(arg) < 0)
+ printf("%s: %s: not removed\n", name, arg);
+ }
+ }
+
+ if (nact) {
+ (void) close(tfd);
+ tfname[inchar]--;
+ /*
+ * Touch the control file to fix position in the queue.
+ */
+ seteuid(euid);
+ if ((tfd = open(tfname, O_RDWR)) >= 0) {
+ char c;
+
+ if (read(tfd, &c, 1) == 1 &&
+ lseek(tfd, (off_t)0, 0) == 0 &&
+ write(tfd, &c, 1) != 1) {
+ printf("%s: cannot touch %s\n", name, tfname);
+ tfname[inchar]++;
+ cleanup(0);
+ }
+ (void) close(tfd);
+ }
+ if (link(tfname, cfname) < 0) {
+ printf("%s: cannot rename %s\n", name, cfname);
+ tfname[inchar]++;
+ cleanup(0);
+ }
+ unlink(tfname);
+ seteuid(uid);
+ if (qflag) /* just q things up */
+ exit(0);
+ if (!startdaemon(printer))
+ 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(f, n)
+ int f;
+ char n[];
+{
+ register int fd, i, nr, nc;
+ char buf[BUFSIZ];
+
+ if (format == 'p')
+ card('T', title ? title : n);
+ for (i = 0; i < ncopies; i++)
+ card(format, &dfname[inchar-2]);
+ card('U', &dfname[inchar-2]);
+ card('N', n);
+ fd = nfile(dfname);
+ nr = nc = 0;
+ while ((i = read(f, buf, BUFSIZ)) > 0) {
+ if (write(fd, buf, i) != i) {
+ printf("%s: %s: temp file write error\n", name, n);
+ break;
+ }
+ nc += i;
+ if (nc >= BUFSIZ) {
+ nc -= BUFSIZ;
+ nr++;
+ if (MX > 0 && nr > MX) {
+ printf("%s: %s: copy file is too large\n", name, n);
+ break;
+ }
+ }
+ }
+ (void) close(fd);
+ if (nc==0 && nr==0)
+ printf("%s: %s: empty input file\n", name, f ? n : "stdin");
+ else
+ nact++;
+}
+
+/*
+ * Try and link the file to dfname. Return a pointer to the full
+ * path name if successful.
+ */
+static char *
+linked(file)
+ register char *file;
+{
+ register char *cp;
+ static char buf[MAXPATHLEN];
+ register int ret;
+
+ if (*file != '/') {
+ if (getcwd(buf, sizeof(buf)) == NULL)
+ return(NULL);
+ while (file[0] == '.') {
+ switch (file[1]) {
+ case '/':
+ file += 2;
+ continue;
+ case '.':
+ if (file[2] == '/') {
+ if ((cp = strrchr(buf, '/')) != NULL)
+ *cp = '\0';
+ file += 3;
+ continue;
+ }
+ }
+ break;
+ }
+ strncat(buf, "/", sizeof(buf) - strlen(buf) - 1);
+ strncat(buf, file, sizeof(buf) - strlen(buf) - 1);
+ file = buf;
+ }
+ seteuid(euid);
+ ret = symlink(file, dfname);
+ seteuid(uid);
+ return(ret ? NULL : file);
+}
+
+/*
+ * Put a line into the control file.
+ */
+static void
+card(c, p2)
+ register int c;
+ register char *p2;
+{
+ char buf[BUFSIZ];
+ register char *p1 = buf;
+ register int len = 2;
+
+ *p1++ = c;
+ while ((c = *p2++) != '\0' && len < sizeof(buf)) {
+ *p1++ = (c == '\n') ? ' ' : c;
+ len++;
+ }
+ *p1++ = '\n';
+ write(tfd, buf, len);
+}
+
+/*
+ * Create a new file in the spool directory.
+ */
+static int
+nfile(n)
+ char *n;
+{
+ register int f;
+ int oldumask = umask(0); /* should block signals */
+
+ seteuid(euid);
+ f = open(n, O_WRONLY | O_EXCL | O_CREAT, FILMOD);
+ (void) umask(oldumask);
+ if (f < 0) {
+ printf("%s: cannot create %s\n", name, n);
+ cleanup(0);
+ }
+ if (fchown(f, userid, -1) < 0) {
+ printf("%s: cannot chown %s\n", name, n);
+ cleanup(0); /* cleanup does exit */
+ }
+ seteuid(uid);
+ if (++n[inchar] > 'z') {
+ if (++n[inchar-2] == 't') {
+ printf("too many files - break up the job\n");
+ cleanup(0);
+ }
+ n[inchar] = 'A';
+ } else if (n[inchar] == '[')
+ n[inchar] = 'a';
+ return(f);
+}
+
+/*
+ * Cleanup after interrupts and errors.
+ */
+static void
+cleanup(signo)
+ int signo;
+{
+ register i;
+
+ signal(SIGHUP, SIG_IGN);
+ signal(SIGINT, SIG_IGN);
+ signal(SIGQUIT, SIG_IGN);
+ signal(SIGTERM, SIG_IGN);
+ i = inchar;
+ seteuid(euid);
+ if (tfname)
+ do
+ unlink(tfname);
+ while (tfname[i]-- != 'A');
+ if (cfname)
+ do
+ unlink(cfname);
+ while (cfname[i]-- != 'A');
+ if (dfname)
+ do {
+ do
+ unlink(dfname);
+ while (dfname[i]-- != 'A');
+ dfname[i] = 'z';
+ } while (dfname[i-2]-- != 'd');
+ exit(1);
+}
+
+/*
+ * Test to see if this is a printable file.
+ * Return -1 if it is not, 0 if its printable, and 1 if
+ * we should remove it after printing.
+ */
+static int
+test(file)
+ char *file;
+{
+ struct exec execb;
+ char *path;
+ register int fd;
+ register char *cp;
+
+ if (access(file, 4) < 0) {
+ printf("%s: cannot access %s\n", name, file);
+ return(-1);
+ }
+ if (stat(file, &statb) < 0) {
+ printf("%s: cannot stat %s\n", name, file);
+ return(-1);
+ }
+ if ((statb.st_mode & S_IFMT) == S_IFDIR) {
+ printf("%s: %s is a directory\n", name, file);
+ return(-1);
+ }
+ if (statb.st_size == 0) {
+ printf("%s: %s is an empty file\n", name, file);
+ return(-1);
+ }
+ if ((fd = open(file, O_RDONLY)) < 0) {
+ printf("%s: cannot open %s\n", name, file);
+ return(-1);
+ }
+ if (read(fd, &execb, sizeof(execb)) == sizeof(execb) &&
+ !N_BADMAG(execb)) {
+ printf("%s: %s is an executable program", name, file);
+ goto error1;
+ }
+ (void) close(fd);
+ if (rflag) {
+ if ((cp = strrchr(file, '/')) == NULL) {
+ if (checkwriteperm(file,".") == 0)
+ return(1);
+ } else {
+ if (cp == file) {
+ fd = checkwriteperm(file,"/");
+ } else {
+ path = alloca(strlen(file) + 1);
+ strcpy(path,file);
+ *cp = '\0';
+ fd = checkwriteperm(path,file);
+ *cp = '/';
+ }
+ if (fd == 0)
+ return(1);
+ }
+ printf("%s: %s: is not removable by you\n", name, file);
+ }
+ return(0);
+
+error1:
+ printf(" and is unprintable\n");
+ (void) close(fd);
+ return(-1);
+}
+
+static int
+checkwriteperm(file, directory)
+ char *file, *directory;
+{
+ struct stat stats;
+ if (access(directory, W_OK) == 0) {
+ stat(directory, &stats);
+ if (stats.st_mode & S_ISVTX) {
+ stat(file, &stats);
+ if(stats.st_uid == userid) {
+ return(0);
+ }
+ } else return(0);
+ }
+ return(-1);
+}
+
+/*
+ * itoa - integer to string conversion
+ */
+static char *
+itoa(i)
+ register int i;
+{
+ static char b[10] = "########";
+ register char *p;
+
+ p = &b[8];
+ do
+ *p-- = i%10 + '0';
+ while (i /= 10);
+ return(++p);
+}
+
+/*
+ * Perform lookup for printer name or abbreviation --
+ */
+static void
+chkprinter(s)
+ char *s;
+{
+ int status;
+
+ if ((status = cgetent(&bp, printcapdb, s)) == -2)
+ fatal2("cannot open printer description file");
+ else if (status == -1)
+ fatal2("%s: unknown printer", s);
+ if (cgetstr(bp, "sd", &SD) == -1)
+ SD = _PATH_DEFSPOOL;
+ if (cgetstr(bp, "lo", &LO) == -1)
+ LO = DEFLOCK;
+ cgetstr(bp, "rg", &RG);
+ if (cgetnum(bp, "mx", &MX) < 0)
+ MX = DEFMX;
+ if (cgetnum(bp,"mc", &MC) < 0)
+ MC = DEFMAXCOPIES;
+ if (cgetnum(bp, "du", &DU) < 0)
+ DU = DEFUID;
+ SC = (cgetcap(bp, "sc", ':') != NULL);
+}
+
+/*
+ * Tell the user what we wanna get.
+ */
+static void
+usage()
+{
+ fprintf(stderr, "%s\n%s\n",
+"usage: lpr [-Pprinter] [-#num] [-C class] [-J job] [-T title] [-U user]",
+"[-i[numcols]] [-1234 font] [-wnum] [-cdfghlnmprstv] [name ...]");
+ exit(1);
+}
+
+
+/*
+ * Make the temp files.
+ */
+static void
+mktemps()
+{
+ register int len, fd, n;
+ register char *cp;
+ char buf[BUFSIZ];
+ char *lmktemp();
+
+ (void) snprintf(buf, sizeof(buf), "%s/.seq", SD);
+ seteuid(euid);
+ if ((fd = open(buf, O_RDWR|O_CREAT, 0661)) < 0) {
+ printf("%s: cannot create %s\n", name, buf);
+ exit(1);
+ }
+ if (flock(fd, LOCK_EX)) {
+ printf("%s: cannot lock %s\n", name, buf);
+ exit(1);
+ }
+ seteuid(uid);
+ n = 0;
+ if ((len = read(fd, buf, sizeof(buf))) > 0) {
+ for (cp = buf; len--; ) {
+ if (*cp < '0' || *cp > '9')
+ break;
+ n = n * 10 + (*cp++ - '0');
+ }
+ }
+ len = strlen(SD) + strlen(host) + 8;
+ tfname = lmktemp("tf", n, len);
+ cfname = lmktemp("cf", n, len);
+ dfname = lmktemp("df", n, len);
+ inchar = strlen(SD) + 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(id, num, len)
+ char *id;
+ int num, len;
+{
+ register char *s;
+
+ if ((s = malloc(len)) == NULL)
+ fatal2("out of memory");
+ (void) snprintf(s, len, "%s/%sA%03d%s", SD, id, num, host);
+ return(s);
+}
+
+#ifdef __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+static void
+#ifdef __STDC__
+fatal2(const char *msg, ...)
+#else
+fatal2(msg, va_alist)
+ char *msg;
+ va_dcl
+#endif
+{
+ va_list ap;
+#ifdef __STDC__
+ va_start(ap, msg);
+#else
+ va_start(ap);
+#endif
+ printf("%s: ", name);
+ vprintf(msg, ap);
+ putchar('\n');
+ va_end(ap);
+ exit(1);
+}
diff --git a/usr.sbin/lpr/lpr/printcap.5 b/usr.sbin/lpr/lpr/printcap.5
new file mode 100644
index 0000000..ee2e768
--- /dev/null
+++ b/usr.sbin/lpr/lpr/printcap.5
@@ -0,0 +1,315 @@
+.\" Copyright (c) 1983, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)printcap.5 8.2 (Berkeley) 12/11/93
+.\" $Id: printcap.5,v 1.11 1997/05/08 15:31:39 joerg Exp $
+.\"
+.Dd December 11, 1993
+.Dt PRINTCAP 5
+.Os BSD 4.2
+.Sh NAME
+.Nm printcap
+.Nd printer capability data base
+.Sh SYNOPSIS
+.Nm printcap
+.Sh DESCRIPTION
+The
+.Nm Printcap
+function
+is a simplified version of the
+.Xr termcap 5
+data base
+used to describe line printers. The spooling system accesses the
+.Nm printcap
+file every time it is used, allowing dynamic
+addition and deletion of printers. Each entry in the data base
+is used to describe one printer. This data base may not be
+substituted for, as is possible for
+.Xr termcap 5 ,
+because it may allow accounting to be bypassed.
+.Pp
+The default printer is normally
+.Em lp ,
+though the environment variable
+.Ev PRINTER
+may be used to override this. Each spooling utility supports an option,
+.Fl P Ar printer ,
+to allow explicit naming of a destination printer.
+.Pp
+Refer to the
+.%T "4.3 BSD Line Printer Spooler Manual"
+for a complete discussion on how to setup the database for a given printer.
+.Sh CAPABILITIES
+Refer to
+.Xr termcap 5
+for a description of the file layout.
+.Bl -column Namexxx Typexx "/var/spool/lpdxxxxx"
+.Sy Name Type Default Description
+.It "af str" Ta Dv NULL Ta No "name of accounting file"
+.It "br num none if lp is a tty, set the baud rate"
+.Pf ( Xr ioctl 2
+call)
+.It "cf str" Ta Dv NULL Ta No "cifplot data filter"
+.It "ct num 120 TCP connection timeout in seconds"
+.It "df str" Ta Dv NULL Ta No "tex data filter"
+.Pf ( Tn DVI
+format)
+.It "ff str" Ta So Li \ef Sc Ta No "string to send for a form feed"
+.It "fo bool false print a form feed when device is opened"
+.It "gf str" Ta Dv NULL Ta No "graph data filter"
+.Pf ( Xr plot 3
+format
+.It "hl bool false print the burst header page last"
+.It "ic bool false driver supports (non standard) ioctl to indent printout"
+.It "if str" Ta Dv NULL Ta No "name of text filter which does accounting"
+.It "lf str" Ta Pa /dev/console Ta No "error logging file name"
+.It "lo str" Ta Pa lock Ta No "name of lock file"
+.It "lp str" Ta Pa /dev/lp Ta No "device name to open for output"
+.It "ms str" Ta Dv NULL Ta No "if lp is a tty, a comma-seperated, "
+.Xr stty 1
+-like list describing the tty modes
+.It "mx num 1000 maximum file size (in"
+.Dv BUFSIZ
+blocks), zero = unlimited
+.It "nd str" Ta Dv NULL Ta No "next directory for list of queues (unimplemented)"
+.It "nf str" Ta Dv NULL Ta No "ditroff data filter (device independent troff)"
+.It "of str" Ta Dv NULL Ta No "name of output filtering program"
+.It "pc num 200 price per foot or page in hundredths of cents"
+.It "pl num 66 page length (in lines)"
+.It "pw num 132 page width (in characters)"
+.It "px num 0 page width in pixels (horizontal)"
+.It "py num 0 page length in pixels (vertical)"
+.It "rf str" Ta Dv NULL Ta No "filter for printing"
+.Tn FORTRAN
+style text files
+.It "rg str" Ta Dv NULL Ta No "restricted group. Only members of group allowed access"
+.It "rm str" Ta Dv NULL Ta No "machine name for remote printer"
+.It "rp str ``lp'' remote printer name argument"
+.It "rs bool false restrict remote users to those with local accounts"
+.It "rw bool false open the printer device for reading and writing"
+.It "sb bool false short banner (one line only)"
+.It "sc bool false suppress multiple copies"
+.It "sd str" Ta Pa /var/spool/lpd Ta No "spool directory"
+.It "sf bool false suppress form feeds"
+.It "sh bool false suppress printing of burst page header"
+.It "st str" Ta Pa status Ta No "status file name"
+.It "tf str" Ta Dv NULL Ta No "troff data filter (cat phototypesetter)"
+.It "tr str" Ta Dv NULL Ta No "trailer string to print when queue empties"
+.It "vf str" Ta Dv NULL Ta No "raster image filter"
+.El
+.Pp
+If the local line printer driver supports indentation, the daemon
+must understand how to invoke it.
+.Sh FILTERS
+The
+.Xr lpd 8
+daemon creates a pipeline of
+.Em filters
+to process files for various printer types.
+The filters selected depend on the flags passed to
+.Xr lpr 1 .
+The pipeline set up is:
+.Bd -literal -offset indent
+p pr | if regular text + pr(1)
+none if regular text
+c cf cifplot
+d df DVI (tex)
+g gf plot(3)
+n nf ditroff
+f rf Fortran
+t tf troff
+v vf raster image
+.Ed
+.Pp
+The
+.Sy if
+filter is invoked with arguments:
+.Bd -filled -offset indent
+.Cm if
+.Op Fl c
+.Fl w Ns Ar width
+.Fl l Ns Ar length
+.Fl i Ns Ar indent
+.Fl n Ar login
+.Fl h Ar host acct-file
+.Ed
+.Pp
+The
+.Fl c
+flag is passed only if the
+.Fl l
+flag (pass control characters literally)
+is specified to
+.Xr lpr 1 .
+The
+.Ar Width
+function
+and
+.Ar length
+specify the page width and length
+(from
+.Cm pw
+and
+.Cm pl
+respectively) in characters.
+The
+.Fl n
+and
+.Fl h
+parameters specify the login name and host name of the owner
+of the job respectively.
+The
+.Ar Acct-file
+function
+is passed from the
+.Cm af
+.Nm printcap
+entry.
+.Pp
+If no
+.Cm if
+is specified,
+.Cm of
+is used instead,
+with the distinction that
+.Cm of
+is opened only once,
+while
+.Cm if
+is opened for every individual job.
+Thus,
+.Cm if
+is better suited to performing accounting.
+The
+.Cm of
+is only given the
+.Ar width
+and
+.Ar length
+flags.
+.Pp
+All other filters are called as:
+.Bd -filled -offset indent
+.Nm filter
+.Fl x Ns Ar width
+.Fl y Ns Ar length
+.Fl n Ar login
+.Fl h Ar host acct-file
+.Ed
+.Pp
+where
+.Ar width
+and
+.Ar length
+are represented in pixels,
+specified by the
+.Cm px
+and
+.Cm py
+entries respectively.
+.Pp
+All filters take
+.Em stdin
+as the file,
+.Em stdout
+as the printer,
+may log either to
+.Em stderr
+or using
+.Xr syslog 3 ,
+and must not ignore
+.Dv SIGINT .
+.Sh REMOTE PRINTING
+When printing to a remote printer using
+.Cm rm ,
+it is possible to use either
+.Cm if
+or
+.Cm of .
+If both are specified,
+.Cm of
+is ignored. Both filters behave the same except that they are passed
+different arguments as above. Specifically, the output filter is
+terminated and restarted for each file transmitted. This is necessary
+in order to pass the resulting size to the remote
+.Xr lpd 8 .
+.Pp
+If the
+.Fl p
+flag was passed to
+.Xr lpr 1 ,
+.Xr pr 1
+is not executed locally, but is requested of the remote
+.Xr lpd 8 .
+Any input filtering via
+.Cm if
+will therefore happen before
+.Xr pr 1
+is executed rather than afterwards.
+.Sh LOGGING
+Error messages generated by the line printer programs themselves
+(that is, the
+.Xr lpd 8
+and related programs)
+are logged by
+.Xr syslog 3
+using the
+.Dv LPR
+facility.
+Messages printed on
+.Em stderr
+of one of the filters
+are sent to the corresponding
+.Cm lf
+file.
+The filters may, of course, use
+.Xr syslog 8
+themselves.
+.Pp
+Error messages sent to the console have a carriage return and a line
+feed appended to them, rather than just a line feed.
+.Sh SEE ALSO
+.Xr lpq 1 ,
+.Xr lpr 1 ,
+.Xr lprm 1 ,
+.Xr hosts.lpd 5 ,
+.Xr termcap 5 ,
+.Xr lpc 8 ,
+.Xr lpd 8 ,
+.Xr pac 8
+.Rs
+.%T "4.3 BSD Line Printer Spooler Manual"
+.Re
+.Sh HISTORY
+The
+.Nm
+file format appeared in
+.Bx 4.2 .
diff --git a/usr.sbin/lpr/lprm/Makefile b/usr.sbin/lpr/lprm/Makefile
new file mode 100644
index 0000000..77c57e5
--- /dev/null
+++ b/usr.sbin/lpr/lprm/Makefile
@@ -0,0 +1,13 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= lprm
+CFLAGS+=-I${.CURDIR}/../common_source
+SRCS= lprm.c rmjob.c startdaemon.c common.c
+BINOWN= root
+BINGRP= daemon
+BINMODE=6555
+BINDIR= /usr/bin
+MAN1= lprm.1
+.PATH: ${.CURDIR}/../common_source
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/lpr/lprm/lprm.1 b/usr.sbin/lpr/lprm/lprm.1
new file mode 100644
index 0000000..02da034
--- /dev/null
+++ b/usr.sbin/lpr/lprm/lprm.1
@@ -0,0 +1,145 @@
+.\" Copyright (c) 1983, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)lprm.1 8.1 (Berkeley) 6/6/93
+.\"
+.Dd June 6, 1993
+.Dt LPRM 1
+.Os BSD 4.2
+.Sh NAME
+.Nm lprm
+.Nd remove jobs from the line printer spooling queue
+.Sh SYNOPSIS
+.Nm lprm
+.Op Fl P Ns Ar printer
+.Op Fl
+.Op job # ...
+.Op Ar user ...
+.Sh DESCRIPTION
+.Nm Lprm
+will remove a job, or jobs, from a printer's spool queue.
+Since the spooling directory is protected from users, using
+.Nm
+is normally the only method by which a user may remove a job.
+The owner of a job is determined by the user's login name
+and host name on the machine where the
+.Xr lpr 1
+command was invoked.
+.Pp
+Options and arguments:
+.Bl -tag -width indent
+.It Fl P Ns Ar printer
+Specify the queue associated with a specific
+.Ar printer
+(otherwise the default printer is used).
+.It Fl
+If a single
+.Sq Fl
+is given,
+.Nm
+will remove all jobs which a user
+owns. If the super-user employs this flag, the spool queue will
+be emptied entirely.
+.It Ar user
+Cause
+.Nm
+to attempt to remove any jobs queued belonging to that user
+(or users). This form of invoking
+.Nm
+is useful only to the super-user.
+.It Ar job\ \&#
+A user may dequeue an individual job by specifying its job number.
+This number may be obtained from the
+.Xr lpq 1
+program, e.g.
+.Pp
+.Bd -literal -offset indent
+\&% lpq \-l
+
+1st:ken [job #013ucbarpa]
+ (standard input) 100 bytes
+% lprm 13
+.Ed
+.El
+.Pp
+If neither arguments or options are given,
+.Nm
+will delete the currently active job if it is
+owned by the user who invoked
+.Nm lprm .
+.Pp
+.Nm Lprm
+announces the names of any files it removes and is silent if
+there are no jobs in the queue which match the request list.
+.Pp
+.Nm Lprm
+will kill off an active daemon, if necessary, before removing
+any spooling files. If a daemon is killed, a new one is
+automatically restarted upon completion of file removals.
+.Sh ENVIRONMENT
+If the following environment variable exists, it is utilized by
+.Nm lprm .
+.Bl -tag -width PRINTER
+.It Ev PRINTER
+If the environment variable
+.Ev PRINTER
+exists,
+and a printer has not been specified with the
+.Fl P
+option,
+the default printer is assumed from
+.Ev PRINTER .
+.El
+.Sh FILES
+.Bl -tag -width /var/spool/*/lock/ -compact
+.It Pa /etc/printcap
+Printer characteristics file.
+.It Pa /var/spool/*
+Spooling directories.
+.It Pa /var/spool/*/lock
+Lock file used to obtain the pid of the current
+daemon and the job number of the currently active job.
+.El
+.Sh SEE ALSO
+.Xr lpq 1 ,
+.Xr lpr 1 ,
+.Xr lpd 8
+.Sh DIAGNOSTICS
+``Permission denied" if the user tries to remove files other than his
+own.
+.Sh BUGS
+Since there are race conditions possible in the update of the lock file,
+the currently active job may be incorrectly identified.
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 3.0 .
diff --git a/usr.sbin/lpr/lprm/lprm.c b/usr.sbin/lpr/lprm/lprm.c
new file mode 100644
index 0000000..e124744
--- /dev/null
+++ b/usr.sbin/lpr/lprm/lprm.c
@@ -0,0 +1,152 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 1983, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)lprm.c 8.1 (Berkeley) 6/6/93";
+#endif
+static const char rcsid[] =
+ "$Id$";
+#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 */
+
+static void usage __P((void));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ register char *arg;
+ struct passwd *p;
+
+ uid = getuid();
+ euid = geteuid();
+ seteuid(uid); /* be safe */
+ name = argv[0];
+ gethostname(host, sizeof(host));
+ openlog("lpd", 0, LOG_LPR);
+ if ((p = getpwuid(getuid())) == NULL)
+ fatal("Who are you?");
+ if (strlen(p->pw_name) >= sizeof(luser))
+ fatal("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("Too many requests");
+ requ[requests++] = atoi(arg);
+ } else {
+ if (users >= MAXUSERS)
+ fatal("Too many users");
+ user[users++] = arg;
+ }
+ }
+ }
+ if (printer == NULL && (printer = getenv("PRINTER")) == NULL)
+ printer = DEFLP;
+
+ rmjob();
+ exit(0);
+}
+
+static void
+usage()
+{
+ fprintf(stderr, "usage: lprm [-] [-Pprinter] [[job #] [user] ...]\n");
+ exit(2);
+}
diff --git a/usr.sbin/lpr/lptest/Makefile b/usr.sbin/lpr/lptest/Makefile
new file mode 100644
index 0000000..8492b69
--- /dev/null
+++ b/usr.sbin/lpr/lptest/Makefile
@@ -0,0 +1,7 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= lptest
+MAN1= lptest.1
+
+.include "../../Makefile.inc"
+.include <bsd.prog.mk>
diff --git a/usr.sbin/lpr/lptest/lptest.1 b/usr.sbin/lpr/lptest/lptest.1
new file mode 100644
index 0000000..95b28f3
--- /dev/null
+++ b/usr.sbin/lpr/lptest/lptest.1
@@ -0,0 +1,74 @@
+.\" Copyright (c) 1985, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)lptest.1 8.2 (Berkeley) 12/30/93
+.\"
+.Dd December 30, 1993
+.Dt LPTEST 1
+.Os BSD 4.3
+.Sh NAME
+.Nm lptest
+.Nd generate lineprinter ripple pattern
+.Sh SYNOPSIS
+.Nm lptest
+.Op Ar length
+.Op Ar count
+.Sh DESCRIPTION
+.Nm Lptest
+writes the traditional "ripple test" pattern on standard output.
+In 96 lines,
+this pattern will print all 96 printable
+.Tn ASCII
+characters
+in each position.
+While originally created to test printers, it is quite
+useful for testing terminals,
+driving terminal ports for debugging purposes,
+or any other task where a quick supply of random data is needed.
+.Pp
+The
+.Ar length
+argument specifies the output line length if the default
+length of 79 is inappropriate.
+.Pp
+The
+.Ar count
+argument specifies the number of output lines to be generated if
+the default count of 200 is inappropriate.
+Note that if
+.Ar count
+is to be specified,
+.Ar length
+must be also be specified.
+.Sh HISTORY
+.Nm Lptest
+appeared in
+.Bx 4.3 .
diff --git a/usr.sbin/lpr/lptest/lptest.c b/usr.sbin/lpr/lptest/lptest.c
new file mode 100644
index 0000000..1cf2206
--- /dev/null
+++ b/usr.sbin/lpr/lptest/lptest.c
@@ -0,0 +1,83 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1983, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)lptest.c 8.1 (Berkeley) 6/6/93";
+#endif /* not lint */
+
+#include <stdlib.h>
+#include <stdio.h>
+
+/*
+ * lptest -- line printer test program (and other devices).
+ */
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ int len, count;
+ register i, j, fc, nc;
+ char outbuf[BUFSIZ];
+
+ setbuf(stdout, outbuf);
+ if (argc >= 2)
+ len = atoi(argv[1]);
+ else
+ len = 79;
+ if (argc >= 3)
+ count = atoi(argv[2]);
+ else
+ count = 200;
+ fc = ' ';
+ for (i = 0; i < count; i++) {
+ if (++fc == 0177)
+ fc = ' ';
+ nc = fc;
+ for (j = 0; j < len; j++) {
+ putchar(nc);
+ if (++nc == 0177)
+ nc = ' ';
+ }
+ putchar('\n');
+ }
+ (void) fflush(stdout);
+ exit(0);
+}
diff --git a/usr.sbin/lpr/pac/Makefile b/usr.sbin/lpr/pac/Makefile
new file mode 100644
index 0000000..1f49dea
--- /dev/null
+++ b/usr.sbin/lpr/pac/Makefile
@@ -0,0 +1,10 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= pac
+CFLAGS+=-I${.CURDIR}/../common_source
+MAN8= pac.8
+SRCS= pac.c common.c
+.PATH: ${.CURDIR}/../common_source
+
+.include "../../Makefile.inc"
+.include <bsd.prog.mk>
diff --git a/usr.sbin/lpr/pac/pac.8 b/usr.sbin/lpr/pac/pac.8
new file mode 100644
index 0000000..433cfe4
--- /dev/null
+++ b/usr.sbin/lpr/pac/pac.8
@@ -0,0 +1,106 @@
+.\" Copyright (c) 1983, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)pac.8 8.1 (Berkeley) 6/6/93
+.\"
+.Dd June 6, 1993
+.Dt PAC 8
+.Os BSD 4.2
+.Sh NAME
+.Nm pac
+.Nd printer/plotter accounting information
+.Sh SYNOPSIS
+.Nm pac
+.Op Fl P Ns Ar printer
+.Op Fl c
+.Op Fl m
+.Op Fl p Ns Ar price
+.Op Fl s
+.Op Fl r
+.Op Ar name ...
+.Sh DESCRIPTION
+.Nm Pac
+reads the printer/plotter accounting files, accumulating the number
+of pages (the usual case) or feet (for raster devices)
+of paper consumed by each user, and printing out
+how much each user consumed in pages or feet and dollars.
+.Pp
+Options and operands available:
+.Bl -tag -width PPprinter
+.It Fl P Ns Ar printer
+Accounting is done for the named printer.
+Normally, accounting is done for the default printer (site dependent) or
+the value of the environment variable
+.Ev PRINTER
+is used.
+.It Fl c
+Cause the output to be sorted by cost; usually the
+output is sorted alphabetically by name.
+.It Fl m
+Cause the host name to be ignored in the accounting file. This
+allows for a user on multiple machines to have all of his printing
+charges grouped together.
+.It Fl p Ns Ar price
+The value
+.Ar price
+is used for the cost in dollars instead of the default value of 0.02
+or the price specified in
+.Pa /etc/printcap .
+.It Fl r
+Reverse the sorting order.
+.It Fl s
+Accounting information is summarized on the
+summary accounting file; this summarization is necessary since on a
+busy system, the accounting file can grow by several lines per day.
+.It Ar names
+Statistics are only printed for user(s)
+.Ar name ;
+usually, statistics are printed for every user who has used any paper.
+.El
+.Sh FILES
+.Bl -tag -width /var/account/?_sum -compact
+.It Pa /var/account/?acct
+raw accounting files
+.It Pa /var/account/?_sum
+summary accounting files
+.It Pa /etc/printcap
+printer capability data base
+.El
+.Sh SEE ALSO
+.Xr printcap 5
+.Sh BUGS
+The relationship between the computed price and reality is
+as yet unknown.
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 4.0 .
diff --git a/usr.sbin/lpr/pac/pac.c b/usr.sbin/lpr/pac/pac.c
new file mode 100644
index 0000000..c89a526
--- /dev/null
+++ b/usr.sbin/lpr/pac/pac.c
@@ -0,0 +1,457 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 1983, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)pac.c 8.1 (Berkeley) 6/6/93";
+#endif
+static const char rcsid[] =
+ "$Id$";
+#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 <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include "lp.h"
+#include "lp.local.h"
+
+static char *acctfile; /* accounting file (input data) */
+static int allflag = 1; /* Get stats on everybody */
+static int errs;
+static int hcount; /* Count of hash entries */
+static int mflag = 0; /* disregard machine names */
+static int pflag = 0; /* 1 if -p on cmd line */
+static float price = 0.02; /* cost per page (or what ever) */
+static long price100; /* per-page cost in 100th of a cent */
+static int reverse; /* Reverse sort order */
+static int sort; /* Sort by cost */
+static char *sumfile; /* summary file */
+static int summarize; /* Compress accounting file */
+
+uid_t uid, euid;
+
+/*
+ * Grossness follows:
+ * Names to be accumulated are hashed into the following
+ * table.
+ */
+
+#define HSHSIZE 97 /* Number of hash buckets */
+
+struct hent {
+ struct hent *h_link; /* Forward hash link */
+ char *h_name; /* Name of this user */
+ float h_feetpages; /* Feet or pages of paper */
+ int h_count; /* Number of runs */
+};
+
+static struct hent *hashtab[HSHSIZE]; /* Hash table proper */
+
+static void account __P((FILE *));
+static int any __P((int, char []));
+static int chkprinter __P((char *));
+static void dumpit __P((void));
+static int hash __P((char []));
+static struct hent *enter __P((char []));
+static struct hent *lookup __P((char []));
+static int qucmp __P((const void *, const void *));
+static void rewrite __P((void));
+static void usage __P((void));
+
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ register FILE *acct;
+ register char *cp;
+
+ euid = geteuid(); /* these aren't used in pac(1) */
+ uid = getuid();
+ while (--argc) {
+ cp = *++argv;
+ if (*cp++ == '-') {
+ switch(*cp++) {
+ case 'P':
+ /*
+ * Printer name.
+ */
+ printer = cp;
+ continue;
+
+ case 'p':
+ /*
+ * get the price.
+ */
+ price = atof(cp);
+ pflag = 1;
+ continue;
+
+ case 's':
+ /*
+ * Summarize and compress accounting file.
+ */
+ summarize++;
+ continue;
+
+ case 'c':
+ /*
+ * Sort by cost.
+ */
+ sort++;
+ continue;
+
+ case 'm':
+ /*
+ * disregard machine names for each user
+ */
+ mflag = 1;
+ continue;
+
+ case 'r':
+ /*
+ * Reverse sorting order.
+ */
+ reverse++;
+ continue;
+
+ default:
+ usage();
+ }
+ }
+ (void) enter(--cp);
+ allflag = 0;
+ }
+ if (printer == NULL && (printer = getenv("PRINTER")) == NULL)
+ printer = DEFLP;
+ if (!chkprinter(printer)) {
+ printf("pac: unknown printer %s\n", printer);
+ exit(2);
+ }
+
+ if ((acct = fopen(acctfile, "r")) == NULL) {
+ perror(acctfile);
+ exit(1);
+ }
+ account(acct);
+ fclose(acct);
+ if ((acct = fopen(sumfile, "r")) != NULL) {
+ account(acct);
+ fclose(acct);
+ }
+ if (summarize)
+ rewrite();
+ else
+ dumpit();
+ exit(errs);
+}
+
+static void
+usage()
+{
+ fprintf(stderr,
+ "usage: pac [-Pprinter] [-pprice] [-s] [-c] [-r] [-m] [user ...]\n");
+ exit(1);
+}
+
+/*
+ * Read the entire accounting file, accumulating statistics
+ * for the users that we have in the hash table. If allflag
+ * is set, then just gather the facts on everyone.
+ * Note that we must accomodate both the active and summary file
+ * formats here.
+ * Host names are ignored if the -m flag is present.
+ */
+static void
+account(acct)
+ register FILE *acct;
+{
+ char linebuf[BUFSIZ];
+ double t;
+ register char *cp, *cp2;
+ register struct hent *hp;
+ register int ic;
+
+ while (fgets(linebuf, BUFSIZ, acct) != NULL) {
+ cp = linebuf;
+ while (any(*cp, " \t"))
+ cp++;
+ t = atof(cp);
+ while (any(*cp, ".0123456789"))
+ cp++;
+ while (any(*cp, " \t"))
+ cp++;
+ for (cp2 = cp; !any(*cp2, " \t\n"); cp2++)
+ ;
+ ic = atoi(cp2);
+ *cp2 = '\0';
+ if (mflag && strchr(cp, ':'))
+ cp = strchr(cp, ':') + 1;
+ hp = lookup(cp);
+ if (hp == NULL) {
+ if (!allflag)
+ continue;
+ hp = enter(cp);
+ }
+ hp->h_feetpages += t;
+ if (ic)
+ hp->h_count += ic;
+ else
+ hp->h_count++;
+ }
+}
+
+/*
+ * Sort the hashed entries by name or footage
+ * and print it all out.
+ */
+static void
+dumpit()
+{
+ struct hent **base;
+ register struct hent *hp, **ap;
+ register int hno, c, runs;
+ float feet;
+
+ hp = hashtab[0];
+ hno = 1;
+ base = (struct hent **) calloc(sizeof hp, hcount);
+ for (ap = base, c = hcount; c--; ap++) {
+ while (hp == NULL)
+ hp = hashtab[hno++];
+ *ap = hp;
+ hp = hp->h_link;
+ }
+ qsort(base, hcount, sizeof hp, qucmp);
+ printf(" Login pages/feet runs price\n");
+ feet = 0.0;
+ runs = 0;
+ for (ap = base, c = hcount; c--; ap++) {
+ hp = *ap;
+ runs += hp->h_count;
+ feet += hp->h_feetpages;
+ printf("%-24s %7.2f %4d $%6.2f\n", hp->h_name,
+ hp->h_feetpages, hp->h_count, hp->h_feetpages * price);
+ }
+ if (allflag) {
+ printf("\n");
+ printf("%-24s %7.2f %4d $%6.2f\n", "total", feet,
+ runs, feet * price);
+ }
+}
+
+/*
+ * Rewrite the summary file with the summary information we have accumulated.
+ */
+static void
+rewrite()
+{
+ register struct hent *hp;
+ register int i;
+ register FILE *acctf;
+
+ if ((acctf = fopen(sumfile, "w")) == NULL) {
+ warn("%s", sumfile);
+ errs++;
+ return;
+ }
+ for (i = 0; i < HSHSIZE; i++) {
+ hp = hashtab[i];
+ while (hp != NULL) {
+ fprintf(acctf, "%7.2f\t%s\t%d\n", hp->h_feetpages,
+ hp->h_name, hp->h_count);
+ hp = hp->h_link;
+ }
+ }
+ fflush(acctf);
+ if (ferror(acctf)) {
+ warn("%s", sumfile);
+ errs++;
+ }
+ fclose(acctf);
+ if ((acctf = fopen(acctfile, "w")) == NULL)
+ warn("%s", acctfile);
+ else
+ fclose(acctf);
+}
+
+/*
+ * Hashing routines.
+ */
+
+/*
+ * Enter the name into the hash table and return the pointer allocated.
+ */
+
+static struct hent *
+enter(name)
+ char name[];
+{
+ register struct hent *hp;
+ register int h;
+
+ if ((hp = lookup(name)) != NULL)
+ return(hp);
+ h = hash(name);
+ hcount++;
+ hp = (struct hent *) calloc(sizeof *hp, 1);
+ hp->h_name = (char *) calloc(sizeof(char), strlen(name)+1);
+ strcpy(hp->h_name, name);
+ hp->h_feetpages = 0.0;
+ hp->h_count = 0;
+ hp->h_link = hashtab[h];
+ hashtab[h] = hp;
+ return(hp);
+}
+
+/*
+ * Lookup a name in the hash table and return a pointer
+ * to it.
+ */
+
+static struct hent *
+lookup(name)
+ char name[];
+{
+ register int h;
+ register struct hent *hp;
+
+ h = hash(name);
+ for (hp = hashtab[h]; hp != NULL; hp = hp->h_link)
+ if (strcmp(hp->h_name, name) == 0)
+ return(hp);
+ return(NULL);
+}
+
+/*
+ * Hash the passed name and return the index in
+ * the hash table to begin the search.
+ */
+static int
+hash(name)
+ char name[];
+{
+ register int h;
+ register char *cp;
+
+ for (cp = name, h = 0; *cp; h = (h << 2) + *cp++)
+ ;
+ return((h & 0x7fffffff) % HSHSIZE);
+}
+
+/*
+ * Other stuff
+ */
+static int
+any(ch, str)
+ int ch;
+ char str[];
+{
+ register int c = ch;
+ register char *cp = str;
+
+ while (*cp)
+ if (*cp++ == c)
+ return(1);
+ return(0);
+}
+
+/*
+ * The qsort comparison routine.
+ * The comparison is ascii collating order
+ * or by feet of typesetter film, according to sort.
+ */
+static int
+qucmp(a, b)
+ const void *a, *b;
+{
+ register struct hent *h1, *h2;
+ register int r;
+
+ h1 = *(struct hent **)a;
+ h2 = *(struct hent **)b;
+ if (sort)
+ r = h1->h_feetpages < h2->h_feetpages ?
+ -1 : h1->h_feetpages > h2->h_feetpages;
+ else
+ r = strcmp(h1->h_name, h2->h_name);
+ return(reverse ? -r : r);
+}
+
+/*
+ * Perform lookup for printer name or abbreviation --
+ */
+static int
+chkprinter(s)
+ register char *s;
+{
+ int stat;
+
+ if ((stat = cgetent(&bp, printcapdb, s)) == -2) {
+ printf("pac: can't open printer description file\n");
+ exit(3);
+ } else if (stat == -1)
+ return(0);
+ else if (stat == -3)
+ fatal("potential reference loop detected in printcap file");
+
+ if (cgetstr(bp, "af", &acctfile) == -1) {
+ printf("accounting not enabled for printer %s\n", printer);
+ exit(2);
+ }
+ if (!pflag && (cgetnum(bp, "pc", &price100) == 0))
+ price = price100/10000.0;
+ sumfile = (char *) calloc(sizeof(char), strlen(acctfile)+5);
+ if (sumfile == NULL)
+ errx(1, "calloc failed");
+ strcpy(sumfile, acctfile);
+ strcat(sumfile, "_sum");
+ return(1);
+}
diff --git a/usr.sbin/lpr/runqueue/extern.h b/usr.sbin/lpr/runqueue/extern.h
new file mode 100644
index 0000000..718bef3
--- /dev/null
+++ b/usr.sbin/lpr/runqueue/extern.h
@@ -0,0 +1,42 @@
+/*
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)extern.h 8.1 (Berkeley) 6/6/93
+ */
+
+#include <sys/cdefs.h>
+
+extern char scnkey[][HEIGHT]; /* in lpdchar.c */
+extern char fromb[];
+
+void printjob __P((void));
+void recvjob __P((void));
diff --git a/usr.sbin/lpr/runqueue/lpdchar.c b/usr.sbin/lpr/runqueue/lpdchar.c
new file mode 100644
index 0000000..4d24ffd
--- /dev/null
+++ b/usr.sbin/lpr/runqueue/lpdchar.c
@@ -0,0 +1,1067 @@
+/*
+ * 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";
+#endif /* not lint */
+
+/*
+ * Character set for line printer daemon
+ */
+#include "lp.local.h"
+#include "extern.h"
+
+#define c_______ 0
+#define c______1 01
+#define c_____1_ 02
+#define c____1__ 04
+#define c____11_ 06
+#define c___1___ 010
+#define c___1__1 011
+#define c___1_1_ 012
+#define c___11__ 014
+#define c__1____ 020
+#define c__1__1_ 022
+#define c__1_1__ 024
+#define c__11___ 030
+#define c__111__ 034
+#define c__111_1 035
+#define c__1111_ 036
+#define c__11111 037
+#define c_1_____ 040
+#define c_1____1 041
+#define c_1___1_ 042
+#define c_1__1__ 044
+#define c_1_1___ 050
+#define c_1_1__1 051
+#define c_1_1_1_ 052
+#define c_11____ 060
+#define c_11_11_ 066
+#define c_111___ 070
+#define c_111__1 071
+#define c_111_1_ 072
+#define c_1111__ 074
+#define c_1111_1 075
+#define c_11111_ 076
+#define c_111111 077
+#define c1______ 0100
+#define c1_____1 0101
+#define c1____1_ 0102
+#define c1____11 0103
+#define c1___1__ 0104
+#define c1___1_1 0105
+#define c1___11_ 0106
+#define c1__1___ 0110
+#define c1__1__1 0111
+#define c1__11_1 0115
+#define c1__1111 0117
+#define c1_1____ 0120
+#define c1_1___1 0121
+#define c1_1_1_1 0125
+#define c1_1_11_ 0126
+#define c1_111__ 0134
+#define c1_1111_ 0136
+#define c11____1 0141
+#define c11___1_ 0142
+#define c11___11 0143
+#define c11_1___ 0150
+#define c11_1__1 0151
+#define c111_11_ 0166
+#define c1111___ 0170
+#define c11111__ 0174
+#define c111111_ 0176
+#define c1111111 0177
+
+char scnkey[][HEIGHT] = /* this is relatively easy to modify */
+ /* just look: */
+{
+ { c_______,
+ c_______,
+ c_______,
+ c_______,
+ c_______,
+ c_______,
+ c_______,
+ c_______,
+ c_______ }, /* */
+
+ { c__11___,
+ c__11___,
+ c__11___,
+ c__11___,
+ c__11___,
+ c_______,
+ c_______,
+ c__11___,
+ c__11___ }, /* ! */
+
+ { c_1__1__,
+ c_1__1__,
+ c_______,
+ c_______,
+ c_______,
+ c_______,
+ c_______,
+ c_______,
+ c_______ }, /* " */
+
+ { c_______,
+ c__1_1__,
+ c__1_1__,
+ c1111111,
+ c__1_1__,
+ c1111111,
+ c__1_1__,
+ c__1_1__,
+ c_______ }, /* # */
+
+ { c___1___,
+ c_11111_,
+ c1__1__1,
+ c1__1___,
+ c_11111_,
+ c___1__1,
+ c1__1__1,
+ c_11111_,
+ c___1___ }, /* $ */
+
+ { c_1_____,
+ c1_1___1,
+ c_1___1_,
+ c____1__,
+ c___1___,
+ c__1____,
+ c_1___1_,
+ c1___1_1,
+ c_____1_ }, /* % */
+
+ { c_11____,
+ c1__1___,
+ c1___1__,
+ c_1_1___,
+ c__1____,
+ c_1_1__1,
+ c1___11_,
+ c1___11_,
+ c_111__1 }, /* & */
+
+ { c___11__,
+ c___11__,
+ c___1___,
+ c__1____,
+ c_______,
+ c_______,
+ c_______,
+ c_______,
+ c_______ }, /* ' */
+
+ { c____1__,
+ c___1___,
+ c__1____,
+ c__1____,
+ c__1____,
+ c__1____,
+ c__1____,
+ c___1___,
+ c____1__ }, /* ( */
+
+ { c__1____,
+ c___1___,
+ c____1__,
+ c____1__,
+ c____1__,
+ c____1__,
+ c____1__,
+ c___1___,
+ c__1____ }, /* ) */
+
+ { c_______,
+ c___1___,
+ c1__1__1,
+ c_1_1_1_,
+ c__111__,
+ c_1_1_1_,
+ c1__1__1,
+ c___1___,
+ c_______ }, /* * */
+
+ { c_______,
+ c___1___,
+ c___1___,
+ c___1___,
+ c1111111,
+ c___1___,
+ c___1___,
+ c___1___,
+ c_______ }, /* + */
+
+ { c_______,
+ c_______,
+ c_______,
+ c_______,
+ c__11___,
+ c__11___,
+ c__1____,
+ c_1_____,
+ c_______ }, /* , */
+
+ { c_______,
+ c_______,
+ c_______,
+ c_______,
+ c1111111,
+ c_______,
+ c_______,
+ c_______,
+ c_______ }, /* - */
+
+ { c_______,
+ c_______,
+ c_______,
+ c_______,
+ c_______,
+ c_______,
+ c_______,
+ c__11___,
+ c__11___ }, /* . */
+
+ { c_______,
+ c______1,
+ c_____1_,
+ c____1__,
+ c___1___,
+ c__1____,
+ c_1_____,
+ c1______,
+ c_______ }, /* / */
+
+ { c_11111_,
+ c1_____1,
+ c1____11,
+ c1___1_1,
+ c1__1__1,
+ c1_1___1,
+ c11____1,
+ c1_____1,
+ c_11111_ }, /* 0 */
+
+ { c___1___,
+ c__11___,
+ c_1_1___,
+ c___1___,
+ c___1___,
+ c___1___,
+ c___1___,
+ c___1___,
+ c_11111_ }, /* 1 */
+
+ { c_11111_,
+ c1_____1,
+ c______1,
+ c_____1_,
+ c__111__,
+ c_1_____,
+ c1______,
+ c1______,
+ c1111111 }, /* 2 */
+
+ { c_11111_,
+ c1_____1,
+ c______1,
+ c______1,
+ c__1111_,
+ c______1,
+ c______1,
+ c1_____1,
+ c_11111_ }, /* 3 */
+
+ { c_____1_,
+ c____11_,
+ c___1_1_,
+ c__1__1_,
+ c_1___1_,
+ c1____1_,
+ c1111111,
+ c_____1_,
+ c_____1_ }, /* 4 */
+
+ { c1111111,
+ c1______,
+ c1______,
+ c11111__,
+ c_____1_,
+ c______1,
+ c______1,
+ c1____1_,
+ c_1111__ }, /* 5 */
+
+ { c__1111_,
+ c_1_____,
+ c1______,
+ c1______,
+ c1_1111_,
+ c11____1,
+ c1_____1,
+ c1_____1,
+ c_11111_ }, /* 6 */
+
+ { c1111111,
+ c1_____1,
+ c_____1_,
+ c____1__,
+ c___1___,
+ c__1____,
+ c__1____,
+ c__1____,
+ c__1____ }, /* 7 */
+
+ { c_11111_,
+ c1_____1,
+ c1_____1,
+ c1_____1,
+ c_11111_,
+ c1_____1,
+ c1_____1,
+ c1_____1,
+ c_11111_ }, /* 8 */
+
+ { c_11111_,
+ c1_____1,
+ c1_____1,
+ c1_____1,
+ c_111111,
+ c______1,
+ c______1,
+ c1_____1,
+ c_1111__ }, /* 9 */
+
+ { c_______,
+ c_______,
+ c_______,
+ c__11___,
+ c__11___,
+ c_______,
+ c_______,
+ c__11___,
+ c__11___ }, /* : */
+
+
+ { c__11___,
+ c__11___,
+ c_______,
+ c_______,
+ c__11___,
+ c__11___,
+ c__1____,
+ c_1_____,
+ c_______ }, /* ; */
+
+ { c____1__,
+ c___1___,
+ c__1____,
+ c_1_____,
+ c1______,
+ c_1_____,
+ c__1____,
+ c___1___,
+ c____1__ }, /* < */
+
+ { c_______,
+ c_______,
+ c_______,
+ c1111111,
+ c_______,
+ c1111111,
+ c_______,
+ c_______,
+ c_______ }, /* = */
+
+ { c__1____,
+ c___1___,
+ c____1__,
+ c_____1_,
+ c______1,
+ c_____1_,
+ c____1__,
+ c___1___,
+ c__1____ }, /* > */
+
+ { c__1111_,
+ c_1____1,
+ c_1____1,
+ c______1,
+ c____11_,
+ c___1___,
+ c___1___,
+ c_______,
+ c___1___ }, /* ? */
+
+ { c__1111_,
+ c_1____1,
+ c1__11_1,
+ c1_1_1_1,
+ c1_1_1_1,
+ c1_1111_,
+ c1______,
+ c_1____1,
+ c__1111_ }, /* @ */
+
+ { c__111__,
+ c_1___1_,
+ c1_____1,
+ c1_____1,
+ c1111111,
+ c1_____1,
+ c1_____1,
+ c1_____1,
+ c1_____1 }, /* A */
+
+ { c111111_,
+ c_1____1,
+ c_1____1,
+ c_1____1,
+ c_11111_,
+ c_1____1,
+ c_1____1,
+ c_1____1,
+ c111111_ }, /* B */
+
+ { c__1111_,
+ c_1____1,
+ c1______,
+ c1______,
+ c1______,
+ c1______,
+ c1______,
+ c_1____1,
+ c__1111_ }, /* C */
+
+ { c11111__,
+ c_1___1_,
+ c_1____1,
+ c_1____1,
+ c_1____1,
+ c_1____1,
+ c_1____1,
+ c_1___1_,
+ c11111__ }, /* D */
+
+ { c1111111,
+ c1______,
+ c1______,
+ c1______,
+ c111111_,
+ c1______,
+ c1______,
+ c1______,
+ c1111111 }, /* E */
+
+ { c1111111,
+ c1______,
+ c1______,
+ c1______,
+ c111111_,
+ c1______,
+ c1______,
+ c1______,
+ c1______ }, /* F */
+
+ { c__1111_,
+ c_1____1,
+ c1______,
+ c1______,
+ c1______,
+ c1__1111,
+ c1_____1,
+ c_1____1,
+ c__1111_ }, /* G */
+
+ { c1_____1,
+ c1_____1,
+ c1_____1,
+ c1_____1,
+ c1111111,
+ c1_____1,
+ c1_____1,
+ c1_____1,
+ c1_____1 }, /* H */
+
+ { c_11111_,
+ c___1___,
+ c___1___,
+ c___1___,
+ c___1___,
+ c___1___,
+ c___1___,
+ c___1___,
+ c_11111_ }, /* I */
+
+ { c__11111,
+ c____1__,
+ c____1__,
+ c____1__,
+ c____1__,
+ c____1__,
+ c____1__,
+ c1___1__,
+ c_111___ }, /* J */
+
+ { c1_____1,
+ c1____1_,
+ c1___1__,
+ c1__1___,
+ c1_1____,
+ c11_1___,
+ c1___1__,
+ c1____1_,
+ c1_____1 }, /* K */
+
+ { c1______,
+ c1______,
+ c1______,
+ c1______,
+ c1______,
+ c1______,
+ c1______,
+ c1______,
+ c1111111 }, /* L */
+
+ { c1_____1,
+ c11___11,
+ c1_1_1_1,
+ c1__1__1,
+ c1_____1,
+ c1_____1,
+ c1_____1,
+ c1_____1,
+ c1_____1 }, /* M */
+
+ { c1_____1,
+ c11____1,
+ c1_1___1,
+ c1__1__1,
+ c1___1_1,
+ c1____11,
+ c1_____1,
+ c1_____1,
+ c1_____1 }, /* N */
+
+ { c__111__,
+ c_1___1_,
+ c1_____1,
+ c1_____1,
+ c1_____1,
+ c1_____1,
+ c1_____1,
+ c_1___1_,
+ c__111__ }, /* O */
+
+ { c111111_,
+ c1_____1,
+ c1_____1,
+ c1_____1,
+ c111111_,
+ c1______,
+ c1______,
+ c1______,
+ c1______ }, /* P */
+
+ { c__111__,
+ c_1___1_,
+ c1_____1,
+ c1_____1,
+ c1_____1,
+ c1__1__1,
+ c1___1_1,
+ c_1___1_,
+ c__111_1 }, /* Q */
+
+ { c111111_,
+ c1_____1,
+ c1_____1,
+ c1_____1,
+ c111111_,
+ c1__1___,
+ c1___1__,
+ c1____1_,
+ c1_____1 }, /* R */
+
+ { c_11111_,
+ c1_____1,
+ c1______,
+ c1______,
+ c_11111_,
+ c______1,
+ c______1,
+ c1_____1,
+ c_11111_ }, /* S */
+
+ { c1111111,
+ c___1___,
+ c___1___,
+ c___1___,
+ c___1___,
+ c___1___,
+ c___1___,
+ c___1___,
+ c___1___ }, /* T */
+
+ { c1_____1,
+ c1_____1,
+ c1_____1,
+ c1_____1,
+ c1_____1,
+ c1_____1,
+ c1_____1,
+ c1_____1,
+ c_11111_ }, /* U */
+
+ { c1_____1,
+ c1_____1,
+ c1_____1,
+ c_1___1_,
+ c_1___1_,
+ c__1_1__,
+ c__1_1__,
+ c___1___,
+ c___1___ }, /* V */
+
+ { c1_____1,
+ c1_____1,
+ c1_____1,
+ c1_____1,
+ c1__1__1,
+ c1__1__1,
+ c1_1_1_1,
+ c11___11,
+ c1_____1 }, /* W */
+
+ { c1_____1,
+ c1_____1,
+ c_1___1_,
+ c__1_1__,
+ c___1___,
+ c__1_1__,
+ c_1___1_,
+ c1_____1,
+ c1_____1 }, /* X */
+
+ { c1_____1,
+ c1_____1,
+ c_1___1_,
+ c__1_1__,
+ c___1___,
+ c___1___,
+ c___1___,
+ c___1___,
+ c___1___ }, /* Y */
+
+ { c1111111,
+ c______1,
+ c_____1_,
+ c____1__,
+ c___1___,
+ c__1____,
+ c_1_____,
+ c1______,
+ c1111111 }, /* Z */
+
+ { c_1111__,
+ c_1_____,
+ c_1_____,
+ c_1_____,
+ c_1_____,
+ c_1_____,
+ c_1_____,
+ c_1_____,
+ c_1111__ }, /* [ */
+
+ { c_______,
+ c1______,
+ c_1_____,
+ c__1____,
+ c___1___,
+ c____1__,
+ c_____1_,
+ c______1,
+ c_______ }, /* \ */
+
+ { c__1111_,
+ c_____1_,
+ c_____1_,
+ c_____1_,
+ c_____1_,
+ c_____1_,
+ c_____1_,
+ c_____1_,
+ c__1111_ }, /* ] */
+
+ { c___1___,
+ c__1_1__,
+ c_1___1_,
+ c1_____1,
+ c_______,
+ c_______,
+ c_______,
+ c_______ }, /* ^ */
+
+ { c_______,
+ c_______,
+ c_______,
+ c_______,
+ c_______,
+ c_______,
+ c_______,
+ c1111111,
+ c_______ }, /* _ */
+
+ { c__11___,
+ c__11___,
+ c___1___,
+ c____1__,
+ c_______,
+ c_______,
+ c_______,
+ c_______,
+ c_______ }, /* ` */
+
+ { c_______,
+ c_______,
+ c_______,
+ c_1111__,
+ c_____1_,
+ c_11111_,
+ c1_____1,
+ c1____11,
+ c_1111_1 }, /* a */
+
+ { c1______,
+ c1______,
+ c1______,
+ c1_111__,
+ c11___1_,
+ c1_____1,
+ c1_____1,
+ c11___1_,
+ c1_111__ }, /* b */
+
+ { c_______,
+ c_______,
+ c_______,
+ c_1111__,
+ c1____1_,
+ c1______,
+ c1______,
+ c1____1_,
+ c_1111__ }, /* c */
+
+ { c_____1_,
+ c_____1_,
+ c_____1_,
+ c_111_1_,
+ c1___11_,
+ c1____1_,
+ c1____1_,
+ c1___11_,
+ c_111_1_ }, /* d */
+
+ { c_______,
+ c_______,
+ c_______,
+ c_1111__,
+ c1____1_,
+ c111111_,
+ c1______,
+ c1____1_,
+ c_1111__ }, /* e */
+
+ { c___11__,
+ c__1__1_,
+ c__1____,
+ c__1____,
+ c11111__,
+ c__1____,
+ c__1____,
+ c__1____,
+ c__1____ }, /* f */
+
+ { c_111_1_,
+ c1___11_,
+ c1____1_,
+ c1____1_,
+ c1___11_,
+ c_111_1_,
+ c_____1_,
+ c1____1_,
+ c_1111__ }, /* g */
+
+ { c1______,
+ c1______,
+ c1______,
+ c1_111__,
+ c11___1_,
+ c1____1_,
+ c1____1_,
+ c1____1_,
+ c1____1_ }, /* h */
+
+ { c_______,
+ c___1___,
+ c_______,
+ c__11___,
+ c___1___,
+ c___1___,
+ c___1___,
+ c___1___,
+ c__111__ }, /* i */
+
+ { c____11_,
+ c_____1_,
+ c_____1_,
+ c_____1_,
+ c_____1_,
+ c_____1_,
+ c_____1_,
+ c_1___1_,
+ c__111__ }, /* j */
+
+ { c1______,
+ c1______,
+ c1______,
+ c1___1__,
+ c1__1___,
+ c1_1____,
+ c11_1___,
+ c1___1__,
+ c1____1_ }, /* k */
+
+ { c__11___,
+ c___1___,
+ c___1___,
+ c___1___,
+ c___1___,
+ c___1___,
+ c___1___,
+ c___1___,
+ c__111__ }, /* l */
+
+ { c_______,
+ c_______,
+ c_______,
+ c1_1_11_,
+ c11_1__1,
+ c1__1__1,
+ c1__1__1,
+ c1__1__1,
+ c1__1__1 }, /* m */
+
+ { c_______,
+ c_______,
+ c_______,
+ c1_111__,
+ c11___1_,
+ c1____1_,
+ c1____1_,
+ c1____1_,
+ c1____1_ }, /* n */
+
+ { c_______,
+ c_______,
+ c_______,
+ c_1111__,
+ c1____1_,
+ c1____1_,
+ c1____1_,
+ c1____1_,
+ c_1111__ }, /* o */
+
+ { c1_111__,
+ c11___1_,
+ c1____1_,
+ c1____1_,
+ c11___1_,
+ c1_111__,
+ c1______,
+ c1______,
+ c1______ }, /* p */
+
+ { c_111_1_,
+ c1___11_,
+ c1____1_,
+ c1____1_,
+ c1___11_,
+ c_111_1_,
+ c_____1_,
+ c_____1_,
+ c_____1_ }, /* q */
+
+ { c_______,
+ c_______,
+ c_______,
+ c1_111__,
+ c11___1_,
+ c1______,
+ c1______,
+ c1______,
+ c1______ }, /* r */
+
+ { c_______,
+ c_______,
+ c_______,
+ c_1111__,
+ c1____1_,
+ c_11____,
+ c___11__,
+ c1____1_,
+ c_1111__ }, /* s */
+
+ { c_______,
+ c__1____,
+ c__1____,
+ c11111__,
+ c__1____,
+ c__1____,
+ c__1____,
+ c__1__1_,
+ c___11__ }, /* t */
+
+ { c_______,
+ c_______,
+ c_______,
+ c1____1_,
+ c1____1_,
+ c1____1_,
+ c1____1_,
+ c1___11_,
+ c_111_1_ }, /* u */
+
+ { c_______,
+ c_______,
+ c_______,
+ c1_____1,
+ c1_____1,
+ c1_____1,
+ c_1___1_,
+ c__1_1__,
+ c___1___ }, /* v */
+
+ { c_______,
+ c_______,
+ c_______,
+ c1_____1,
+ c1__1__1,
+ c1__1__1,
+ c1__1__1,
+ c1__1__1,
+ c_11_11_ }, /* w */
+
+ { c_______,
+ c_______,
+ c_______,
+ c1____1_,
+ c_1__1__,
+ c__11___,
+ c__11___,
+ c_1__1__,
+ c1____1_ }, /* x */
+
+ { c1____1_,
+ c1____1_,
+ c1____1_,
+ c1____1_,
+ c1___11_,
+ c_111_1_,
+ c_____1_,
+ c1____1_,
+ c_1111__ }, /* y */
+
+ { c_______,
+ c_______,
+ c_______,
+ c111111_,
+ c____1__,
+ c___1___,
+ c__1____,
+ c_1_____,
+ c111111_ }, /* z */
+
+ { c___11__,
+ c__1____,
+ c__1____,
+ c__1____,
+ c_1_____,
+ c__1____,
+ c__1____,
+ c__1____,
+ c___11__ }, /* } */
+
+ { c___1___,
+ c___1___,
+ c___1___,
+ c___1___,
+ c___1___,
+ c___1___,
+ c___1___,
+ c___1___,
+ c___1___ }, /* | */
+
+ { c__11___,
+ c____1__,
+ c____1__,
+ c____1__,
+ c_____1_,
+ c____1__,
+ c____1__,
+ c____1__,
+ c__11___ }, /* } */
+
+ { c_11____,
+ c1__1__1,
+ c____11_,
+ c_______,
+ c_______,
+ c_______,
+ c_______,
+ c_______,
+ c_______ }, /* ~ */
+
+ { c_1__1__,
+ c1__1__1,
+ c__1__1_,
+ c_1__1__,
+ c1__1__1,
+ c__1__1_,
+ c_1__1__,
+ c1__1__1,
+ c__1__1_ } /* rub-out */
+};
diff --git a/usr.sbin/lpr/runqueue/modes.c b/usr.sbin/lpr/runqueue/modes.c
new file mode 100644
index 0000000..7e3dcd9
--- /dev/null
+++ b/usr.sbin/lpr/runqueue/modes.c
@@ -0,0 +1,232 @@
+/*-
+ * 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.
+ *
+ * $Id$
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)modes.c 8.3 (Berkeley) 4/2/94";
+#endif /* not lint */
+
+#include <stddef.h>
+#include <string.h>
+#include <termios.h>
+
+struct modes {
+ char *name;
+ long set;
+ long unset;
+};
+
+/*
+ * The code in optlist() depends on minus options following regular
+ * options, i.e. "foo" must immediately precede "-foo".
+ */
+struct modes cmodes[] = {
+ { "cs5", CS5, CSIZE },
+ { "cs6", CS6, CSIZE },
+ { "cs7", CS7, CSIZE },
+ { "cs8", CS8, CSIZE },
+ { "cstopb", CSTOPB, 0 },
+ { "-cstopb", 0, CSTOPB },
+ { "cread", CREAD, 0 },
+ { "-cread", 0, CREAD },
+ { "parenb", PARENB, 0 },
+ { "-parenb", 0, PARENB },
+ { "parodd", PARODD, 0 },
+ { "-parodd", 0, PARODD },
+ { "parity", PARENB | CS7, PARODD | CSIZE },
+ { "-parity", CS8, PARODD | PARENB | CSIZE },
+ { "evenp", PARENB | CS7, PARODD | CSIZE },
+ { "-evenp", CS8, PARODD | PARENB | CSIZE },
+ { "oddp", PARENB | CS7 | PARODD, CSIZE },
+ { "-oddp", CS8, PARODD | PARENB | CSIZE },
+ { "pass8", CS8, PARODD | PARENB | CSIZE },
+ { "-pass8", PARENB | CS7, PARODD | CSIZE },
+ { "hupcl", HUPCL, 0 },
+ { "-hupcl", 0, HUPCL },
+ { "hup", HUPCL, 0 },
+ { "-hup", 0, HUPCL },
+ { "clocal", CLOCAL, 0 },
+ { "-clocal", 0, CLOCAL },
+ { "crtscts", CRTSCTS, 0 },
+ { "-crtscts", 0, CRTSCTS },
+ { "ctsflow", CCTS_OFLOW, 0 },
+ { "-ctsflow", 0, CCTS_OFLOW },
+ { "dsrflow", CDSR_OFLOW, 0 },
+ { "-dsrflow", 0, CDSR_OFLOW },
+ { "dtrflow", CDTR_IFLOW, 0 },
+ { "-dtrflow", 0, CDTR_IFLOW },
+ { "rtsflow", CRTS_IFLOW, 0 },
+ { "-rtsflow", 0, CRTS_IFLOW },
+ { "mdmbuf", MDMBUF, 0 },
+ { "-mdmbuf", 0, MDMBUF },
+ { NULL },
+};
+
+struct modes imodes[] = {
+ { "ignbrk", IGNBRK, 0 },
+ { "-ignbrk", 0, IGNBRK },
+ { "brkint", BRKINT, 0 },
+ { "-brkint", 0, BRKINT },
+ { "ignpar", IGNPAR, 0 },
+ { "-ignpar", 0, IGNPAR },
+ { "parmrk", PARMRK, 0 },
+ { "-parmrk", 0, PARMRK },
+ { "inpck", INPCK, 0 },
+ { "-inpck", 0, INPCK },
+ { "istrip", ISTRIP, 0 },
+ { "-istrip", 0, ISTRIP },
+ { "inlcr", INLCR, 0 },
+ { "-inlcr", 0, INLCR },
+ { "igncr", IGNCR, 0 },
+ { "-igncr", 0, IGNCR },
+ { "icrnl", ICRNL, 0 },
+ { "-icrnl", 0, ICRNL },
+ { "ixon", IXON, 0 },
+ { "-ixon", 0, IXON },
+ { "flow", IXON, 0 },
+ { "-flow", 0, IXON },
+ { "ixoff", IXOFF, 0 },
+ { "-ixoff", 0, IXOFF },
+ { "tandem", IXOFF, 0 },
+ { "-tandem", 0, IXOFF },
+ { "ixany", IXANY, 0 },
+ { "-ixany", 0, IXANY },
+ { "decctlq", 0, IXANY },
+ { "-decctlq", IXANY, 0 },
+ { "imaxbel", IMAXBEL, 0 },
+ { "-imaxbel", 0, IMAXBEL },
+ { NULL },
+};
+
+struct modes lmodes[] = {
+ { "echo", ECHO, 0 },
+ { "-echo", 0, ECHO },
+ { "echoe", ECHOE, 0 },
+ { "-echoe", 0, ECHOE },
+ { "crterase", ECHOE, 0 },
+ { "-crterase", 0, ECHOE },
+ { "crtbs", ECHOE, 0 }, /* crtbs not supported, close enough */
+ { "-crtbs", 0, ECHOE },
+ { "echok", ECHOK, 0 },
+ { "-echok", 0, ECHOK },
+ { "echoke", ECHOKE, 0 },
+ { "-echoke", 0, ECHOKE },
+ { "crtkill", ECHOKE, 0 },
+ { "-crtkill", 0, ECHOKE },
+ { "altwerase", ALTWERASE, 0 },
+ { "-altwerase", 0, ALTWERASE },
+ { "iexten", IEXTEN, 0 },
+ { "-iexten", 0, IEXTEN },
+ { "echonl", ECHONL, 0 },
+ { "-echonl", 0, ECHONL },
+ { "echoctl", ECHOCTL, 0 },
+ { "-echoctl", 0, ECHOCTL },
+ { "ctlecho", ECHOCTL, 0 },
+ { "-ctlecho", 0, ECHOCTL },
+ { "echoprt", ECHOPRT, 0 },
+ { "-echoprt", 0, ECHOPRT },
+ { "prterase", ECHOPRT, 0 },
+ { "-prterase", 0, ECHOPRT },
+ { "isig", ISIG, 0 },
+ { "-isig", 0, ISIG },
+ { "icanon", ICANON, 0 },
+ { "-icanon", 0, ICANON },
+ { "noflsh", NOFLSH, 0 },
+ { "-noflsh", 0, NOFLSH },
+ { "tostop", TOSTOP, 0 },
+ { "-tostop", 0, TOSTOP },
+ { "flusho", FLUSHO, 0 },
+ { "-flusho", 0, FLUSHO },
+ { "pendin", PENDIN, 0 },
+ { "-pendin", 0, PENDIN },
+ { "crt", ECHOE|ECHOKE|ECHOCTL, ECHOK|ECHOPRT },
+ { "-crt", ECHOK, ECHOE|ECHOKE|ECHOCTL },
+ { "newcrt", ECHOE|ECHOKE|ECHOCTL, ECHOK|ECHOPRT },
+ { "-newcrt", ECHOK, ECHOE|ECHOKE|ECHOCTL },
+ { "nokerninfo", NOKERNINFO, 0 },
+ { "-nokerninfo",0, NOKERNINFO },
+ { "kerninfo", 0, NOKERNINFO },
+ { "-kerninfo", NOKERNINFO, 0 },
+ { NULL },
+};
+
+struct modes omodes[] = {
+ { "opost", OPOST, 0 },
+ { "-opost", 0, OPOST },
+ { "litout", 0, OPOST },
+ { "-litout", OPOST, 0 },
+ { "onlcr", ONLCR, 0 },
+ { "-onlcr", 0, ONLCR },
+ { "tabs", 0, OXTABS }, /* "preserve" tabs */
+ { "-tabs", OXTABS, 0 },
+ { "oxtabs", OXTABS, 0 },
+ { "-oxtabs", 0, OXTABS },
+ { NULL },
+};
+
+#define CHK(name, s) (*name == s[0] && !strcmp(name, s))
+
+int
+msearch(str, ip)
+ char *str;
+ struct termios *ip;
+{
+ struct modes *mp;
+
+ for (mp = cmodes; mp->name; ++mp)
+ if (CHK(str, mp->name)) {
+ ip->c_cflag &= ~mp->unset;
+ ip->c_cflag |= mp->set;
+ return (1);
+ }
+ for (mp = imodes; mp->name; ++mp)
+ if (CHK(str, mp->name)) {
+ ip->c_iflag &= ~mp->unset;
+ ip->c_iflag |= mp->set;
+ return (1);
+ }
+ for (mp = lmodes; mp->name; ++mp)
+ if (CHK(str, mp->name)) {
+ ip->c_lflag &= ~mp->unset;
+ ip->c_lflag |= mp->set;
+ return (1);
+ }
+ for (mp = omodes; mp->name; ++mp)
+ if (CHK(str, mp->name)) {
+ ip->c_oflag &= ~mp->unset;
+ ip->c_oflag |= mp->set;
+ return (1);
+ }
+ return (0);
+}
diff --git a/usr.sbin/lpr/runqueue/printjob.c b/usr.sbin/lpr/runqueue/printjob.c
new file mode 100644
index 0000000..cff1d9f
--- /dev/null
+++ b/usr.sbin/lpr/runqueue/printjob.c
@@ -0,0 +1,1676 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1983, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)printjob.c 8.7 (Berkeley) 5/10/95";
+#endif /* not lint */
+
+
+/*
+ * printjob -- print jobs in the queue.
+ *
+ * NOTE: the lock file is used to pass information to lpq and lprm.
+ * it does not need to be removed because file locks are dynamic.
+ */
+
+#include <sys/param.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <pwd.h>
+#include <unistd.h>
+#include <signal.h>
+#include <syslog.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/ioctl.h>
+#include <termios.h>
+#include <time.h>
+#include "lp.h"
+#include "lp.local.h"
+#include "pathnames.h"
+#include "extern.h"
+
+#define DORETURN 0 /* absorb fork error */
+#define DOABORT 1 /* abort if dofork fails */
+
+/*
+ * Error tokens
+ */
+#define REPRINT -2
+#define ERROR -1
+#define OK 0
+#define FATALERR 1
+#define NOACCT 2
+#define FILTERERR 3
+#define ACCESS 4
+
+static dev_t fdev; /* device of file pointed to by symlink */
+static ino_t fino; /* inode of file pointed to by symlink */
+static FILE *cfp; /* control file */
+static int child; /* id of any filters */
+static int lfd; /* lock file descriptor */
+static int ofd; /* output filter file descriptor */
+static int ofilter; /* id of output filter, if any */
+static int tfd = -1; /* output filter temp file output */
+static int pfd; /* prstatic inter file descriptor */
+static int pid; /* pid of lpd process */
+static int prchild; /* id of pr process */
+static char title[80]; /* ``pr'' title */
+static int tof; /* true if at top of form */
+
+static char class[32]; /* classification field */
+static char fromhost[32]; /* user's host machine */
+ /* indentation size in static characters */
+static char indent[10] = "-i0";
+static char jobname[100]; /* job or file name */
+static char length[10] = "-l"; /* page length in lines */
+static char logname[32]; /* user's login name */
+static char pxlength[10] = "-y"; /* page length in pixels */
+static char pxwidth[10] = "-x"; /* page width in pixels */
+static char tempfile[] = "errsXXXXXX"; /* file name for filter errors */
+static char width[10] = "-w"; /* page width in static characters */
+#define TFILENAME "fltXXXXXX"
+static char tfile[] = TFILENAME; /* file name for filter output */
+
+static void abortpr __P((int));
+static void alarmhandler __P((int));
+static void banner __P((char *, char *));
+static int dofork __P((int));
+static int dropit __P((int));
+static void init __P((void));
+static void openpr __P((void));
+static void opennet __P((char *));
+static void opentty __P((void));
+static void openrem __P((void));
+static int print __P((int, char *));
+static int printit __P((char *));
+static void pstatus __P((const char *, ...));
+static char response __P((void));
+static void scan_out __P((int, char *, int));
+static char *scnline __P((int, char *, int));
+static int sendfile __P((int, char *, char));
+static int sendit __P((char *));
+static void sendmail __P((char *, int));
+static void setty __P((void));
+
+void
+printjob()
+{
+ struct stat stb;
+ register struct queue *q, **qp;
+ struct queue **queue;
+ register int i, nitems;
+ off_t pidoff;
+ int errcnt, count = 0;
+
+ init(); /* set up capabilities */
+ (void) write(1, "", 1); /* ack that daemon is started */
+ (void) close(2); /* set up log file */
+ if (open(LF, O_WRONLY|O_APPEND, 0664) < 0) {
+ syslog(LOG_ERR, "%s: %m", LF);
+ (void) open(_PATH_DEVNULL, O_WRONLY);
+ }
+ setgid(getegid());
+ pid = getpid(); /* for use with lprm */
+ setpgrp(0, pid);
+ signal(SIGHUP, abortpr);
+ signal(SIGINT, abortpr);
+ signal(SIGQUIT, abortpr);
+ signal(SIGTERM, abortpr);
+
+ (void) mktemp(tempfile);
+
+ /*
+ * uses short form file names
+ */
+ if (chdir(SD) < 0) {
+ syslog(LOG_ERR, "%s: %m", SD);
+ exit(1);
+ }
+ if (stat(LO, &stb) == 0 && (stb.st_mode & 0100))
+ exit(0); /* printing disabled */
+ lfd = open(LO, O_WRONLY|O_CREAT, 0644);
+ if (lfd < 0) {
+ syslog(LOG_ERR, "%s: %s: %m", printer, LO);
+ exit(1);
+ }
+ if (flock(lfd, LOCK_EX|LOCK_NB) < 0) {
+ if (errno == EWOULDBLOCK) /* active deamon present */
+ exit(0);
+ syslog(LOG_ERR, "%s: %s: %m", printer, LO);
+ exit(1);
+ }
+ ftruncate(lfd, 0);
+ /*
+ * write process id for others to know
+ */
+ sprintf(line, "%u\n", pid);
+ pidoff = i = strlen(line);
+ if (write(lfd, line, i) != i) {
+ syslog(LOG_ERR, "%s: %s: %m", printer, LO);
+ exit(1);
+ }
+ /*
+ * search the spool directory for work and sort by queue order.
+ */
+ if ((nitems = getq(&queue)) < 0) {
+ syslog(LOG_ERR, "%s: can't scan %s", printer, SD);
+ exit(1);
+ }
+ if (nitems == 0) /* no work to do */
+ exit(0);
+ if (stb.st_mode & 01) { /* reset queue flag */
+ if (fchmod(lfd, stb.st_mode & 0776) < 0)
+ syslog(LOG_ERR, "%s: %s: %m", printer, LO);
+ }
+ openpr(); /* open printer or remote */
+again:
+ /*
+ * we found something to do now do it --
+ * write the name of the current control file into the lock file
+ * so the spool queue program can tell what we're working on
+ */
+ for (qp = queue; nitems--; free((char *) q)) {
+ q = *qp++;
+ if (stat(q->q_name, &stb) < 0)
+ continue;
+ errcnt = 0;
+ restart:
+ (void) lseek(lfd, pidoff, 0);
+ (void) snprintf(line, sizeof(line), "%s\n", q->q_name);
+ i = strlen(line);
+ if (write(lfd, line, i) != i)
+ syslog(LOG_ERR, "%s: %s: %m", printer, LO);
+ if (!remote)
+ i = printit(q->q_name);
+ else
+ i = sendit(q->q_name);
+ /*
+ * Check to see if we are supposed to stop printing or
+ * if we are to rebuild the queue.
+ */
+ if (fstat(lfd, &stb) == 0) {
+ /* stop printing before starting next job? */
+ if (stb.st_mode & 0100)
+ goto done;
+ /* rebuild queue (after lpc topq) */
+ if (stb.st_mode & 01) {
+ for (free((char *) q); nitems--; free((char *) q))
+ q = *qp++;
+ if (fchmod(lfd, stb.st_mode & 0776) < 0)
+ syslog(LOG_WARNING, "%s: %s: %m",
+ printer, LO);
+ break;
+ }
+ }
+ if (i == OK) /* file ok and printed */
+ count++;
+ else if (i == REPRINT && ++errcnt < 5) {
+ /* try reprinting the job */
+ syslog(LOG_INFO, "restarting %s", printer);
+ if (ofilter > 0) {
+ kill(ofilter, SIGCONT); /* to be sure */
+ (void) close(ofd);
+ while ((i = wait(NULL)) > 0 && i != ofilter)
+ ;
+ ofilter = 0;
+ }
+ (void) close(pfd); /* close printer */
+ if (ftruncate(lfd, pidoff) < 0)
+ syslog(LOG_WARNING, "%s: %s: %m", printer, LO);
+ openpr(); /* try to reopen printer */
+ goto restart;
+ } else {
+ syslog(LOG_WARNING, "%s: job could not be %s (%s)", printer,
+ remote ? "sent to remote host" : "printed", q->q_name);
+ if (i == REPRINT) {
+ /* ensure we don't attempt this job again */
+ (void) unlink(q->q_name);
+ q->q_name[0] = 'd';
+ (void) unlink(q->q_name);
+ if (logname[0])
+ sendmail(logname, FATALERR);
+ }
+ }
+ }
+ free((char *) queue);
+ /*
+ * search the spool directory for more work.
+ */
+ if ((nitems = getq(&queue)) < 0) {
+ syslog(LOG_ERR, "%s: can't scan %s", printer, SD);
+ exit(1);
+ }
+ if (nitems == 0) { /* no more work to do */
+ done:
+ if (count > 0) { /* Files actually printed */
+ if (!SF && !tof)
+ (void) write(ofd, FF, strlen(FF));
+ if (TR != NULL) /* output trailer */
+ (void) write(ofd, TR, strlen(TR));
+ }
+ (void) close(ofd);
+ (void) wait(NULL);
+ (void) unlink(tempfile);
+ exit(0);
+ }
+ goto again;
+}
+
+char fonts[4][50]; /* fonts for troff */
+
+char ifonts[4][40] = {
+ _PATH_VFONTR,
+ _PATH_VFONTI,
+ _PATH_VFONTB,
+ _PATH_VFONTS,
+};
+
+/*
+ * The remaining part is the reading of the control file (cf)
+ * and performing the various actions.
+ */
+static int
+printit(file)
+ char *file;
+{
+ register int i;
+ char *cp;
+ int bombed = OK;
+
+ /*
+ * open control file; ignore if no longer there.
+ */
+ if ((cfp = fopen(file, "r")) == NULL) {
+ syslog(LOG_INFO, "%s: %s: %m", printer, file);
+ return(OK);
+ }
+ /*
+ * Reset troff fonts.
+ */
+ for (i = 0; i < 4; i++)
+ strcpy(fonts[i], ifonts[i]);
+ sprintf(&width[2], "%ld", PW);
+ strcpy(indent+2, "0");
+
+ /*
+ * read the control file for work to do
+ *
+ * file format -- first character in the line is a command
+ * rest of the line is the argument.
+ * valid commands are:
+ *
+ * S -- "stat info" for symbolic link protection
+ * J -- "job name" on banner page
+ * C -- "class name" on banner page
+ * L -- "literal" user's name to print on banner
+ * T -- "title" for pr
+ * H -- "host name" of machine where lpr was done
+ * P -- "person" user's login name
+ * I -- "indent" amount to indent output
+ * R -- laser dpi "resolution"
+ * f -- "file name" name of text file to print
+ * l -- "file name" text file with control chars
+ * p -- "file name" text file to print with pr(1)
+ * t -- "file name" troff(1) file to print
+ * n -- "file name" ditroff(1) file to print
+ * d -- "file name" dvi file to print
+ * g -- "file name" plot(1G) file to print
+ * v -- "file name" plain raster file to print
+ * c -- "file name" cifplot file to print
+ * 1 -- "R font file" for troff
+ * 2 -- "I font file" for troff
+ * 3 -- "B font file" for troff
+ * 4 -- "S font file" for troff
+ * N -- "name" of file (used by lpq)
+ * U -- "unlink" name of file to remove
+ * (after we print it. (Pass 2 only)).
+ * M -- "mail" to user when done printing
+ *
+ * getline reads a line and expands tabs to blanks
+ */
+
+ /* pass 1 */
+
+ while (getline(cfp))
+ switch (line[0]) {
+ case 'H':
+ strncpy(fromhost, line+1, sizeof(fromhost) - 1);
+ fromhost[sizeof(fromhost) - 1] = '\0';
+ if (class[0] == '\0') {
+ strncpy(class, line+1, sizeof(class) - 1);
+ class[sizeof(class) - 1] = '\0';
+ }
+ continue;
+
+ case 'P':
+ strncpy(logname, line+1, sizeof(logname) - 1);
+ logname[sizeof(logname) - 1] = '\0';
+ if (RS) { /* restricted */
+ if (getpwnam(logname) == NULL) {
+ bombed = NOACCT;
+ sendmail(line+1, bombed);
+ goto pass2;
+ }
+ }
+ continue;
+
+ case 'S':
+ cp = line+1;
+ i = 0;
+ while (*cp >= '0' && *cp <= '9')
+ i = i * 10 + (*cp++ - '0');
+ fdev = i;
+ cp++;
+ i = 0;
+ while (*cp >= '0' && *cp <= '9')
+ i = i * 10 + (*cp++ - '0');
+ fino = i;
+ continue;
+
+ case 'J':
+ if (line[1] != '\0') {
+ strncpy(jobname, line+1, sizeof(jobname) - 1);
+ jobname[sizeof(jobname) - 1] = '\0';
+ } else
+ strcpy(jobname, " ");
+ continue;
+
+ case 'C':
+ if (line[1] != '\0')
+ strncpy(class, line+1, sizeof(class) - 1);
+ else if (class[0] == '\0')
+ gethostname(class, sizeof(class));
+ class[sizeof(class) - 1] = '\0';
+ continue;
+
+ case 'T': /* header title for pr */
+ strncpy(title, line+1, sizeof(title) - 1);
+ title[sizeof(title) - 1] = '\0';
+ continue;
+
+ case 'L': /* identification line */
+ if (!SH && !HL)
+ banner(line+1, jobname);
+ continue;
+
+ case '1': /* troff fonts */
+ case '2':
+ case '3':
+ case '4':
+ if (line[1] != '\0') {
+ strncpy(fonts[line[0]-'1'], line+1,
+ 50-1);
+ fonts[line[0]-'1'][50-1] = '\0';
+ }
+ continue;
+
+ case 'W': /* page width */
+ strncpy(width+2, line+1, sizeof(width) - 3);
+ width[2+sizeof(width) - 3] = '\0';
+ continue;
+
+ case 'I': /* indent amount */
+ strncpy(indent+2, line+1, sizeof(indent) - 3);
+ indent[2+sizeof(indent) - 3] = '\0';
+ continue;
+
+ default: /* some file to print */
+ switch (i = print(line[0], line+1)) {
+ case ERROR:
+ if (bombed == OK)
+ bombed = FATALERR;
+ break;
+ case REPRINT:
+ (void) fclose(cfp);
+ return(REPRINT);
+ case FILTERERR:
+ case ACCESS:
+ bombed = i;
+ sendmail(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 (!SH && HL)
+ banner(line+1, jobname);
+ continue;
+
+ case 'M':
+ if (bombed < NOACCT) /* already sent if >= NOACCT */
+ sendmail(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(format, file)
+ int format;
+ char *file;
+{
+ register int n;
+ register char *prog;
+ int dtablesize, fi, fo;
+ FILE *fp;
+ char *av[15], buf[BUFSIZ];
+ int pid, p[2], stopped = 0;
+ union wait status;
+ struct stat stb;
+
+ if (lstat(file, &stb) < 0 || (fi = open(file, O_RDONLY)) < 0)
+ return(ERROR);
+ /*
+ * Check to see if data file is a symbolic link. If so, it should
+ * still point to the same file or someone is trying to print
+ * something he shouldn't.
+ */
+ if ((stb.st_mode & S_IFMT) == S_IFLNK && fstat(fi, &stb) == 0 &&
+ (stb.st_dev != fdev || stb.st_ino != fino))
+ return(ACCESS);
+ if (!SF && !tof) { /* start on a fresh page */
+ (void) write(ofd, FF, strlen(FF));
+ tof = 1;
+ }
+ if (IF == NULL && (format == 'f' || format == 'l')) {
+ 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 (IF == NULL) { /* use output filter */
+ prog = _PATH_PR;
+ av[0] = "pr";
+ av[1] = width;
+ av[2] = length;
+ av[3] = "-h";
+ av[4] = *title ? title : " ";
+ av[5] = "-F";
+ av[6] = 0;
+ fo = ofd;
+ goto start;
+ }
+ pipe(p);
+ if ((prchild = dofork(DORETURN)) == 0) { /* child */
+ dup2(fi, 0); /* file is stdin */
+ dup2(p[1], 1); /* pipe is stdout */
+ closelog();
+ for (n = 3, dtablesize = getdtablesize();
+ n < dtablesize; n++)
+ (void) close(n);
+ execl(_PATH_PR, "pr", width, length,
+ "-h", *title ? title : " ", "-F", 0);
+ syslog(LOG_ERR, "cannot execl %s", _PATH_PR);
+ exit(2);
+ }
+ (void) close(p[1]); /* close output side */
+ (void) close(fi);
+ if (prchild < 0) {
+ prchild = 0;
+ (void) close(p[0]);
+ return(ERROR);
+ }
+ fi = p[0]; /* use pipe for input */
+ case 'f': /* print plain text file */
+ prog = IF;
+ av[1] = width;
+ av[2] = length;
+ av[3] = indent;
+ n = 4;
+ break;
+ case 'l': /* like 'f' but pass control characters */
+ prog = IF;
+ av[1] = "-c";
+ av[2] = width;
+ av[3] = length;
+ av[4] = indent;
+ n = 5;
+ break;
+ case 'r': /* print a fortran text file */
+ prog = RF;
+ 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", 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') ? TF : (format == 'n') ? NF : DF;
+ av[1] = pxwidth;
+ av[2] = pxlength;
+ n = 3;
+ break;
+ case 'c': /* print cifplot output */
+ prog = CF;
+ av[1] = pxwidth;
+ av[2] = pxlength;
+ n = 3;
+ break;
+ case 'g': /* print plot(1G) output */
+ prog = GF;
+ av[1] = pxwidth;
+ av[2] = pxlength;
+ n = 3;
+ break;
+ case 'v': /* print raster output */
+ prog = VF;
+ av[1] = pxwidth;
+ av[2] = pxlength;
+ n = 3;
+ break;
+ default:
+ (void) close(fi);
+ syslog(LOG_ERR, "%s: illegal format character '%c'",
+ printer, format);
+ return(ERROR);
+ }
+ if (prog == NULL) {
+ (void) close(fi);
+ syslog(LOG_ERR,
+ "%s: no filter found in printcap for format character '%c'",
+ printer, format);
+ return(ERROR);
+ }
+ if ((av[0] = strrchr(prog, '/')) != NULL)
+ av[0]++;
+ else
+ av[0] = prog;
+ av[n++] = "-n";
+ av[n++] = logname;
+ av[n++] = "-h";
+ av[n++] = fromhost;
+ av[n++] = AF;
+ av[n] = 0;
+ fo = pfd;
+ if (ofilter > 0) { /* stop output filter */
+ write(ofd, "\031\1", 2);
+ while ((pid =
+ wait3((int *)&status, WUNTRACED, 0)) > 0 && pid != ofilter)
+ ;
+ if (status.w_stopval != WSTOPPED) {
+ (void) close(fi);
+ syslog(LOG_WARNING,
+ "%s: output filter died (retcode=%d termsig=%d)",
+ printer, status.w_retcode, status.w_termsig);
+ return(REPRINT);
+ }
+ stopped++;
+ }
+start:
+ if ((child = dofork(DORETURN)) == 0) { /* child */
+ dup2(fi, 0);
+ dup2(fo, 1);
+ n = open(tempfile, O_WRONLY|O_CREAT|O_TRUNC, 0664);
+ if (n >= 0)
+ dup2(n, 2);
+ closelog();
+ for (n = 3, dtablesize = getdtablesize(); n < dtablesize; n++)
+ (void) close(n);
+ execv(prog, av);
+ syslog(LOG_ERR, "cannot execv %s", prog);
+ exit(2);
+ }
+ (void) close(fi);
+ if (child < 0)
+ status.w_retcode = 100;
+ else
+ while ((pid = wait((int *)&status)) > 0 && pid != child)
+ ;
+ child = 0;
+ prchild = 0;
+ if (stopped) { /* restart output filter */
+ if (kill(ofilter, SIGCONT) < 0) {
+ syslog(LOG_ERR, "cannot restart output filter");
+ exit(1);
+ }
+ }
+ tof = 0;
+
+ /* Copy filter output to "lf" logfile */
+ if ((fp = fopen(tempfile, "r"))) {
+ while (fgets(buf, sizeof(buf), fp))
+ fputs(buf, stderr);
+ fclose(fp);
+ }
+
+ if (!WIFEXITED(status)) {
+ syslog(LOG_WARNING, "%s: filter '%c' terminated (termsig=%d)",
+ printer, format, status.w_termsig);
+ return(ERROR);
+ }
+ switch (status.w_retcode) {
+ case 0:
+ tof = 1;
+ return(OK);
+ case 1:
+ return(REPRINT);
+ case 2:
+ return(ERROR);
+ default:
+ syslog(LOG_WARNING, "%s: filter '%c' exited (retcode=%d)",
+ printer, format, status.w_retcode);
+ return(FILTERERR);
+ }
+}
+
+/*
+ * Send the daemon control file (cf) and any data files.
+ * Return -1 if a non-recoverable error occured, 1 if a recoverable error and
+ * 0 if all is well.
+ */
+static int
+sendit(file)
+ char *file;
+{
+ register int i, err = OK;
+ char *cp, last[BUFSIZ];
+
+ /*
+ * open control file
+ */
+ if ((cfp = fopen(file, "r")) == NULL)
+ return(OK);
+ /*
+ * read the control file for work to do
+ *
+ * file format -- first character in the line is a command
+ * rest of the line is the argument.
+ * commands of interest are:
+ *
+ * a-z -- "file name" name of file to print
+ * U -- "unlink" name of file to remove
+ * (after we print it. (Pass 2 only)).
+ */
+
+ /*
+ * pass 1
+ */
+ while (getline(cfp)) {
+ again:
+ if (line[0] == 'S') {
+ cp = line+1;
+ i = 0;
+ while (*cp >= '0' && *cp <= '9')
+ i = i * 10 + (*cp++ - '0');
+ fdev = i;
+ cp++;
+ i = 0;
+ while (*cp >= '0' && *cp <= '9')
+ i = i * 10 + (*cp++ - '0');
+ fino = i;
+ } else if (line[0] == 'H') {
+ strcpy(fromhost, line+1);
+ if (class[0] == '\0')
+ strncpy(class, line+1, sizeof(class) - 1);
+ } else if (line[0] == 'P') {
+ strncpy(logname, line+1, sizeof(logname) - 1);
+ if (RS) { /* restricted */
+ if (getpwnam(logname) == NULL) {
+ sendmail(line+1, NOACCT);
+ err = ERROR;
+ break;
+ }
+ }
+ } else if (line[0] == 'I') {
+ strncpy(indent+2, line+1, sizeof(indent) - 3);
+ } else if (line[0] >= 'a' && line[0] <= 'z') {
+ strcpy(last, line);
+ while (i = getline(cfp))
+ if (strcmp(last, line))
+ break;
+ switch (sendfile('\3', last+1, *last)) {
+ case OK:
+ if (i)
+ goto again;
+ break;
+ case REPRINT:
+ (void) fclose(cfp);
+ return(REPRINT);
+ case ACCESS:
+ sendmail(logname, ACCESS);
+ case ERROR:
+ err = ERROR;
+ }
+ break;
+ }
+ }
+ if (err == OK && sendfile('\2', file, '\0') > 0) {
+ (void) fclose(cfp);
+ return(REPRINT);
+ }
+ /*
+ * pass 2
+ */
+ fseek(cfp, 0L, 0);
+ while (getline(cfp))
+ if (line[0] == 'U' && !strchr(line+1, '/'))
+ (void) unlink(line+1);
+ /*
+ * clean-up in case another control file exists
+ */
+ (void) fclose(cfp);
+ (void) unlink(file);
+ return(err);
+}
+
+/*
+ * Send a data file to the remote machine and spool it.
+ * Return positive if we should try resending.
+ */
+static int
+sendfile(type, file, format)
+ int type;
+ char *file;
+ char format;
+{
+ register int f, i, amt;
+ struct stat stb;
+ char buf[BUFSIZ];
+ int sizerr, resp, closedpr;
+
+ if (lstat(file, &stb) < 0 || (f = open(file, O_RDONLY)) < 0)
+ return(ERROR);
+ /*
+ * Check to see if data file is a symbolic link. If so, it should
+ * still point to the same file or someone is trying to print something
+ * he shouldn't.
+ */
+ if ((stb.st_mode & S_IFMT) == S_IFLNK && fstat(f, &stb) == 0 &&
+ (stb.st_dev != fdev || stb.st_ino != fino))
+ return(ACCESS);
+
+ sizerr = 0;
+ closedpr = 0;
+ if (type == '\3') {
+ if (IF) {
+ /*
+ * We're sending something with an ifilter, we have to
+ * run the ifilter and store the output as a
+ * temporary file (tfile)... the protocol requires us
+ * to send the file size
+ */
+ char *av[15];
+ int n;
+ int nfd;
+ int ifilter;
+ union wait status;
+
+ strcpy(tfile,TFILENAME);
+ if ((tfd = mkstemp(tfile)) == -1) {
+ syslog(LOG_ERR, "mkstemp: %m");
+ return(ERROR);
+ }
+ if ((av[0] = strrchr(IF, '/')) == NULL)
+ av[0] = IF;
+ else
+ av[0]++;
+ if (format == 'l')
+ av[n=1] = "-c";
+ else
+ n = 0;
+ av[++n] = width;
+ av[++n] = length;
+ av[++n] = indent;
+ av[++n] = "-n";
+ av[++n] = logname;
+ av[++n] = "-h";
+ av[++n] = fromhost;
+ av[++n] = AF;
+ av[++n] = 0;
+ if ((ifilter = dofork(DORETURN)) == 0) { /* child */
+ dup2(f, 0);
+ dup2(tfd, 1);
+ n = open(tempfile, O_WRONLY|O_CREAT|O_TRUNC, 0664);
+ if (n >= 0)
+ dup2(n, 2);
+ closelog();
+ for (n = 3, nfd = getdtablesize(); n < nfd; n++)
+ (void) close(n);
+ execv(IF, av);
+ syslog(LOG_ERR, "cannot execv %s", IF);
+ exit(2);
+ }
+ (void) close(f);
+ if (ifilter < 0)
+ status.w_retcode = 100;
+ else
+ while ((pid = wait((int *)&status)) > 0 &&
+ pid != ifilter)
+ ;
+ switch (status.w_retcode) {
+ case 0:
+ break;
+ case 1:
+ unlink(tfile);
+ return(REPRINT);
+ case 2:
+ unlink(tfile);
+ return(ERROR);
+ default:
+ syslog(LOG_WARNING, "%s: filter '%c' exited"
+ " (retcode=%d)",
+ printer, format, status.w_retcode);
+ unlink(tfile);
+ return(FILTERERR);
+ }
+ if (fstat(tfd, &stb) < 0) /* the size of tfile */
+ return(ERROR);
+ f = tfd;
+ lseek(f,0,SEEK_SET);
+ } else if (ofilter) {
+ /*
+ * We're sending something with an ofilter, we have to
+ * store the output as a temporary file (tfile)... the
+ * protocol requires us to send the file size
+ */
+ int i;
+ for (i = 0; i < stb.st_size; i += BUFSIZ) {
+ amt = BUFSIZ;
+ if (i + amt > stb.st_size)
+ amt = stb.st_size - i;
+ if (sizerr == 0 && read(f, buf, amt) != amt) {
+ sizerr = 1;
+ break;
+ }
+ if (write(ofd, buf, amt) != amt) {
+ (void) close(f);
+ return(REPRINT);
+ }
+ }
+ close(ofd);
+ close(f);
+ while ((i = wait(NULL)) > 0 && i != ofilter)
+ ;
+ ofilter = 0;
+ if (fstat(tfd, &stb) < 0) { /* the size of tfile */
+ openpr();
+ return(ERROR);
+ }
+ f = tfd;
+ lseek(f,0,SEEK_SET);
+ closedpr = 1;
+ }
+ }
+
+ (void) sprintf(buf, "%c%qd %s\n", type, stb.st_size, file);
+ amt = strlen(buf);
+ for (i = 0; ; i++) {
+ if (write(pfd, buf, amt) != amt ||
+ (resp = response()) < 0 || resp == '\1') {
+ (void) close(f);
+ if (tfd != -1 && type == '\3') {
+ tfd = -1;
+ unlink(tfile);
+ if (closedpr)
+ openpr();
+ }
+ return(REPRINT);
+ } else if (resp == '\0')
+ break;
+ if (i == 0)
+ pstatus("no space on remote; waiting for queue to drain");
+ if (i == 10)
+ syslog(LOG_ALERT, "%s: can't send to %s; queue full",
+ printer, RM);
+ sleep(5 * 60);
+ }
+ if (i)
+ pstatus("sending to %s", RM);
+ for (i = 0; i < stb.st_size; i += BUFSIZ) {
+ amt = BUFSIZ;
+ if (i + amt > stb.st_size)
+ amt = stb.st_size - i;
+ if (sizerr == 0 && read(f, buf, amt) != amt)
+ sizerr = 1;
+ if (write(pfd, buf, amt) != amt) {
+ (void) close(f);
+ if (tfd != -1 && type == '\3') {
+ tfd = -1;
+ unlink(tfile);
+ if (closedpr)
+ openpr();
+ }
+ return(REPRINT);
+ }
+ }
+
+ (void) close(f);
+ if (tfd != -1 && type == '\3') {
+ tfd = -1;
+ unlink(tfile);
+ }
+ if (sizerr) {
+ syslog(LOG_INFO, "%s: %s: changed size", printer, file);
+ /* tell recvjob to ignore this file */
+ (void) write(pfd, "\1", 1);
+ if (closedpr)
+ openpr();
+ return(ERROR);
+ }
+ if (write(pfd, "", 1) != 1 || response()) {
+ if (closedpr)
+ openpr();
+ return(REPRINT);
+ }
+ if (closedpr)
+ openpr();
+ return(OK);
+}
+
+/*
+ * Check to make sure there have been no errors and that both programs
+ * are in sync with eachother.
+ * Return non-zero if the connection was lost.
+ */
+static char
+response()
+{
+ char resp;
+
+ if (read(pfd, &resp, 1) != 1) {
+ syslog(LOG_INFO, "%s: lost connection", printer);
+ return(-1);
+ }
+ return(resp);
+}
+
+/*
+ * Banner printing stuff
+ */
+static void
+banner(name1, name2)
+ char *name1, *name2;
+{
+ time_t tvec;
+
+ time(&tvec);
+ if (!SF && !tof)
+ (void) write(ofd, FF, strlen(FF));
+ if (SB) { /* 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(ofd, name1, '\0');
+ (void) write(ofd, "\n\n", 2);
+ scan_out(ofd, name2, '\0');
+ if (class[0]) {
+ (void) write(ofd,"\n\n\n",3);
+ scan_out(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 (!SF)
+ (void) write(ofd, FF, strlen(FF));
+ tof = 1;
+}
+
+static char *
+scnline(key, p, c)
+ register int key;
+ register char *p;
+ int c;
+{
+ register scnwidth;
+
+ for (scnwidth = WIDTH; --scnwidth;) {
+ key <<= 1;
+ *p++ = key & 0200 ? c : BACKGND;
+ }
+ return (p);
+}
+
+#define TRC(q) (((q)-' ')&0177)
+
+static void
+scan_out(scfd, scsp, dlm)
+ int scfd, dlm;
+ char *scsp;
+{
+ register char *strp;
+ register nchrs, j;
+ char outbuf[LINELEN+1], *sp, c, cc;
+ int d, scnhgt;
+
+ for (scnhgt = 0; scnhgt++ < HEIGHT+DROP; ) {
+ strp = &outbuf[0];
+ sp = scsp;
+ for (nchrs = 0; ; ) {
+ d = dropit(c = TRC(cc = *sp++));
+ if ((!d && scnhgt > HEIGHT) || (scnhgt <= DROP && d))
+ for (j = WIDTH; --j;)
+ *strp++ = BACKGND;
+ else
+ strp = scnline(scnkey[c][scnhgt-1-d], strp, cc);
+ if (*sp == dlm || *sp == '\0' || nchrs++ >= PW/(WIDTH+1)-1)
+ break;
+ *strp++ = BACKGND;
+ *strp++ = BACKGND;
+ }
+ while (*--strp == BACKGND && strp >= outbuf)
+ ;
+ strp++;
+ *strp++ = '\n';
+ (void) write(scfd, outbuf, strp-outbuf);
+ }
+}
+
+static int
+dropit(c)
+ int c;
+{
+ switch(c) {
+
+ case TRC('_'):
+ case TRC(';'):
+ case TRC(','):
+ case TRC('g'):
+ case TRC('j'):
+ case TRC('p'):
+ case TRC('q'):
+ case TRC('y'):
+ return (DROP);
+
+ default:
+ return (0);
+ }
+}
+
+/*
+ * sendmail ---
+ * tell people about job completion
+ */
+static void
+sendmail(user, bombed)
+ char *user;
+ int bombed;
+{
+ register int i;
+ int dtablesize;
+ int p[2], s;
+ register char *cp;
+ struct stat stb;
+ FILE *fp;
+
+ pipe(p);
+ if ((s = dofork(DORETURN)) == 0) { /* child */
+ dup2(p[0], 0);
+ closelog();
+ for (i = 3, dtablesize = getdtablesize(); i < dtablesize; i++)
+ (void) close(i);
+ if ((cp = strrchr(_PATH_SENDMAIL, '/')) != NULL)
+ cp++;
+ else
+ cp = _PATH_SENDMAIL;
+ execl(_PATH_SENDMAIL, cp, "-t", 0);
+ exit(0);
+ } else if (s > 0) { /* parent */
+ dup2(p[1], 1);
+ printf("To: %s@%s\n", user, fromhost);
+ printf("Subject: %s printer job \"%s\"\n", printer,
+ *jobname ? jobname : "<unknown>");
+ printf("Reply-To: root@%s\n\n", host);
+ printf("Your printer job ");
+ if (*jobname)
+ printf("(%s) ", jobname);
+ switch (bombed) {
+ case OK:
+ printf("\ncompleted successfully\n");
+ cp = "OK";
+ break;
+ default:
+ case FATALERR:
+ printf("\ncould not be printed\n");
+ cp = "FATALERR";
+ break;
+ case NOACCT:
+ printf("\ncould not be printed without an account on %s\n", host);
+ cp = "NOACCT";
+ break;
+ case FILTERERR:
+ if (stat(tempfile, &stb) < 0 || stb.st_size == 0 ||
+ (fp = fopen(tempfile, "r")) == NULL) {
+ printf("\nhad some errors and may not have printed\n");
+ break;
+ }
+ printf("\nhad the following errors and may not have printed:\n");
+ while ((i = getc(fp)) != EOF)
+ putchar(i);
+ (void) fclose(fp);
+ cp = "FILTERERR";
+ break;
+ case ACCESS:
+ printf("\nwas not printed because it was not linked to the original file\n");
+ cp = "ACCESS";
+ }
+ fflush(stdout);
+ (void) close(1);
+ }
+ (void) close(p[0]);
+ (void) close(p[1]);
+ wait(NULL);
+ syslog(LOG_INFO, "mail sent to user %s about job %s on printer %s (%s)",
+ user, *jobname ? jobname : "<unknown>", printer, cp);
+}
+
+/*
+ * dofork - fork with retries on failure
+ */
+static int
+dofork(action)
+ int action;
+{
+ register int i, pid;
+
+ for (i = 0; i < 20; i++) {
+ if ((pid = fork()) < 0) {
+ sleep((unsigned)(i*i));
+ continue;
+ }
+ /*
+ * Child should run as daemon instead of root
+ */
+ if (pid == 0)
+ setuid(DU);
+ return(pid);
+ }
+ syslog(LOG_ERR, "can't fork");
+
+ switch (action) {
+ case DORETURN:
+ return (-1);
+ default:
+ syslog(LOG_ERR, "bad action (%d) to dofork", action);
+ /*FALL THRU*/
+ case DOABORT:
+ exit(1);
+ }
+ /*NOTREACHED*/
+}
+
+/*
+ * Kill child processes to abort current job.
+ */
+static void
+abortpr(signo)
+ int signo;
+{
+ (void) unlink(tempfile);
+ kill(0, SIGINT);
+ if (ofilter > 0)
+ kill(ofilter, SIGCONT);
+ while (wait(NULL) > 0)
+ ;
+ if (ofilter > 0 && tfd != -1)
+ unlink(tfile);
+ exit(0);
+}
+
+static void
+init()
+{
+ int status;
+ char *s;
+
+ if ((status = cgetent(&bp, printcapdb, printer)) == -2) {
+ syslog(LOG_ERR, "can't open printer description file");
+ exit(1);
+ } else if (status == -1) {
+ syslog(LOG_ERR, "unknown printer: %s", printer);
+ exit(1);
+ } else if (status == -3)
+ fatal("potential reference loop detected in printcap file");
+
+ if (cgetstr(bp, "lp", &LP) == -1)
+ LP = _PATH_DEFDEVLP;
+ if (cgetstr(bp, "rp", &RP) == -1)
+ RP = DEFLP;
+ if (cgetstr(bp, "lo", &LO) == -1)
+ LO = DEFLOCK;
+ if (cgetstr(bp, "st", &ST) == -1)
+ ST = DEFSTAT;
+ if (cgetstr(bp, "lf", &LF) == -1)
+ LF = _PATH_CONSOLE;
+ if (cgetstr(bp, "sd", &SD) == -1)
+ SD = _PATH_DEFSPOOL;
+ if (cgetnum(bp, "du", &DU) < 0)
+ DU = DEFUID;
+ if (cgetstr(bp,"ff", &FF) == -1)
+ FF = DEFFF;
+ if (cgetnum(bp, "pw", &PW) < 0)
+ PW = DEFWIDTH;
+ sprintf(&width[2], "%ld", PW);
+ if (cgetnum(bp, "pl", &PL) < 0)
+ PL = DEFLENGTH;
+ if (cgetnum(bp, "ct", &CT) < 0)
+ CT = DEFTIMEOUT;
+ sprintf(&length[2], "%ld", PL);
+ if (cgetnum(bp,"px", &PX) < 0)
+ PX = 0;
+ sprintf(&pxwidth[2], "%ld", PX);
+ if (cgetnum(bp, "py", &PY) < 0)
+ PY = 0;
+ sprintf(&pxlength[2], "%ld", PY);
+ cgetstr(bp, "rm", &RM);
+ if ((s = checkremote()))
+ syslog(LOG_WARNING, s);
+
+ cgetstr(bp, "af", &AF);
+ cgetstr(bp, "of", &OF);
+ cgetstr(bp, "if", &IF);
+ cgetstr(bp, "rf", &RF);
+ cgetstr(bp, "tf", &TF);
+ cgetstr(bp, "nf", &NF);
+ cgetstr(bp, "df", &DF);
+ cgetstr(bp, "gf", &GF);
+ cgetstr(bp, "vf", &VF);
+ cgetstr(bp, "cf", &CF);
+ cgetstr(bp, "tr", &TR);
+ cgetstr(bp, "ms", &MS);
+
+ RS = (cgetcap(bp, "rs", ':') != NULL);
+ SF = (cgetcap(bp, "sf", ':') != NULL);
+ SH = (cgetcap(bp, "sh", ':') != NULL);
+ SB = (cgetcap(bp, "sb", ':') != NULL);
+ HL = (cgetcap(bp, "hl", ':') != NULL);
+ RW = (cgetcap(bp, "rw", ':') != NULL);
+
+ cgetnum(bp, "br", &BR);
+
+ tof = (cgetcap(bp, "fo", ':') == NULL);
+}
+
+/*
+ * Acquire line printer or remote connection.
+ */
+static void
+openpr()
+{
+ register int i;
+ int dtablesize;
+ char *cp;
+
+ if (!remote && *LP) {
+ if (cp = strchr(LP, '@'))
+ opennet(cp);
+ else
+ opentty();
+ } else if (remote) {
+ openrem();
+ } else {
+ syslog(LOG_ERR, "%s: no line printer device or host name",
+ printer);
+ exit(1);
+ }
+
+ /*
+ * Start up an output filter, if needed.
+ */
+ if (OF && !IF && !ofilter) {
+ int p[2];
+
+ pipe(p);
+ if (remote) {
+ strcpy(tfile,TFILENAME);
+ tfd = mkstemp(tfile);
+ }
+ if ((ofilter = dofork(DOABORT)) == 0) { /* child */
+ dup2(p[0], 0); /* pipe is std in */
+ /* tfile/printer is stdout */
+ dup2(remote ? tfd : pfd, 1);
+ closelog();
+ for (i = 3, dtablesize = getdtablesize();
+ i < dtablesize; i++)
+ (void) close(i);
+ if ((cp = strrchr(OF, '/')) == NULL)
+ cp = OF;
+ else
+ cp++;
+ execl(OF, cp, width, length, 0);
+ syslog(LOG_ERR, "%s: %s: %m", printer, OF);
+ exit(1);
+ }
+ (void) close(p[0]); /* close input side */
+ ofd = p[1]; /* use pipe for output */
+ } else {
+ ofd = pfd;
+ ofilter = 0;
+ }
+}
+
+/*
+ * Printer connected directly to the network
+ * or to a terminal server on the net
+ */
+static void
+opennet(cp)
+ char *cp;
+{
+ register int i;
+ int resp, port;
+ char save_ch;
+ void (*savealrm)(int);
+
+ save_ch = *cp;
+ *cp = '\0';
+ port = atoi(LP);
+ if (port <= 0) {
+ syslog(LOG_ERR, "%s: bad port number: %s", printer, LP);
+ exit(1);
+ }
+ *cp++ = save_ch;
+
+ for (i = 1; ; i = i < 256 ? i << 1 : i) {
+ resp = -1;
+ savealrm = signal(SIGALRM, alarmhandler);
+ alarm(CT);
+ pfd = getport(cp, 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("waiting for %s to come up", LP);
+ else
+ pstatus("waiting for access to printer on %s", LP);
+ }
+ sleep(i);
+ }
+ pstatus("sending to %s port %d", cp, port);
+}
+
+/*
+ * Printer is connected to an RS232 port on this host
+ */
+static void
+opentty()
+{
+ register int i;
+ int resp, port;
+
+ for (i = 1; ; i = i < 32 ? i << 1 : i) {
+ pfd = open(LP, RW ? O_RDWR : O_WRONLY);
+ if (pfd >= 0) {
+ delay(500);
+ break;
+ }
+ if (errno == ENOENT) {
+ syslog(LOG_ERR, "%s: %m", LP);
+ exit(1);
+ }
+ if (i == 1)
+ pstatus("waiting for %s to become ready (offline ?)",
+ printer);
+ sleep(i);
+ }
+ if (isatty(pfd))
+ setty();
+ pstatus("%s is ready and printing", printer);
+}
+
+/*
+ * Printer is on a remote host
+ */
+static void
+openrem()
+{
+ register int i, n;
+ int resp;
+ void (*savealrm)(int);
+
+ for (i = 1; ; i = i < 256 ? i << 1 : i) {
+ resp = -1;
+ savealrm = signal(SIGALRM, alarmhandler);
+ alarm(CT);
+ pfd = getport(RM, 0);
+ alarm(0);
+ (void)signal(SIGALRM, savealrm);
+ if (pfd >= 0) {
+ (void) snprintf(line, sizeof(line), "\2%s\n", RP);
+ n = strlen(line);
+ if (write(pfd, line, n) == n &&
+ (resp = response()) == '\0')
+ break;
+ (void) close(pfd);
+ }
+ if (i == 1) {
+ if (resp < 0)
+ pstatus("waiting for %s to come up", RM);
+ else {
+ pstatus("waiting for queue to be enabled on %s",
+ RM);
+ i = 256;
+ }
+ }
+ sleep(i);
+ }
+ pstatus("sending to %s", RM);
+}
+
+struct bauds {
+ int baud;
+ int speed;
+} bauds[] = {
+ 50, B50,
+ 75, B75,
+ 110, B110,
+ 134, B134,
+ 150, B150,
+ 200, B200,
+ 300, B300,
+ 600, B600,
+ 1200, B1200,
+ 1800, B1800,
+ 2400, B2400,
+ 4800, B4800,
+ 9600, B9600,
+ 19200, EXTA,
+ 38400, EXTB,
+ 57600, B57600,
+ 115200, B115200,
+ 0, 0
+};
+
+/*
+ * setup tty lines.
+ */
+static void
+setty()
+{
+ struct termios ttybuf;
+ struct bauds *bp;
+
+ if (ioctl(pfd, TIOCEXCL, (char *)0) < 0) {
+ syslog(LOG_ERR, "%s: ioctl(TIOCEXCL): %m", printer);
+ exit(1);
+ }
+ if (tcgetattr(pfd, &ttybuf) < 0) {
+ syslog(LOG_ERR, "%s: tcgetattr: %m", printer);
+ exit(1);
+ }
+ if (BR > 0) {
+ for (bp = bauds; bp->baud; bp++)
+ if (BR == bp->baud)
+ break;
+ if (!bp->baud) {
+ syslog(LOG_ERR, "%s: illegal baud rate %d", printer, BR);
+ exit(1);
+ }
+ cfsetspeed(&ttybuf, bp->speed);
+ }
+ if (MS) {
+ char *s = strdup(MS), *tmp;
+
+ while (tmp = strsep (&s, ",")) {
+ msearch(tmp, &ttybuf);
+ }
+ }
+ if (MS || (BR > 0)) {
+ if (tcsetattr(pfd, TCSAFLUSH, &ttybuf) == -1) {
+ syslog(LOG_ERR, "%s: tcsetattr: %m", printer);
+ }
+ }
+}
+
+#ifdef __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+static void
+#ifdef __STDC__
+pstatus(const char *msg, ...)
+#else
+pstatus(msg, va_alist)
+ char *msg;
+ va_dcl
+#endif
+{
+ register int fd;
+ char buf[BUFSIZ];
+ va_list ap;
+#ifdef __STDC__
+ va_start(ap, msg);
+#else
+ va_start(ap);
+#endif
+
+ umask(0);
+ fd = open(ST, O_WRONLY|O_CREAT, 0664);
+ if (fd < 0 || flock(fd, LOCK_EX) < 0) {
+ syslog(LOG_ERR, "%s: %s: %m", printer, ST);
+ exit(1);
+ }
+ ftruncate(fd, 0);
+ (void)vsnprintf(buf, sizeof(buf) - 1, msg, ap);
+ va_end(ap);
+ strcat(buf, "\n");
+ (void) write(fd, buf, strlen(buf));
+ (void) close(fd);
+}
+
+void
+alarmhandler(signo)
+{
+ /* ignored */
+}
diff --git a/usr.sbin/lptcontrol/Makefile b/usr.sbin/lptcontrol/Makefile
new file mode 100644
index 0000000..76c40e7
--- /dev/null
+++ b/usr.sbin/lptcontrol/Makefile
@@ -0,0 +1,5 @@
+PROG= lptcontrol
+CFLAGS+= -Wall
+MAN8= lptcontrol.8
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/lptcontrol/lptcontrol.8 b/usr.sbin/lptcontrol/lptcontrol.8
new file mode 100644
index 0000000..d174fe6
--- /dev/null
+++ b/usr.sbin/lptcontrol/lptcontrol.8
@@ -0,0 +1,79 @@
+.\"
+.\" lptcontrol - a utility for manipulating the lpt driver
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\"
+.\" $Id: lptcontrol.8,v 1.5 1997/02/22 16:06:24 peter Exp $
+.Dd September 3, 1994
+.Dt LPTCONTROL 8
+.Os FreeBSD 2
+.Sh NAME
+.Nm \&lptcontrol
+.Nd a utility for manipulating the lpt printer driver
+.Sh SYNOPSIS
+.Nm \&lptcontrol
+.Cm -i
+|
+.Cm -p
+.Op Fl u Ar unit no
+.Sh DESCRIPTION
+The
+.Nm
+command is used to set either the interrupt-driven or polling mode
+of individual
+.Xr lpt 4
+devices. When a printer is switched between
+interrupt-driven and polled mode, this change will only take effect
+the next time the device is opened.
+.Sh OPTIONS
+.TP
+The following command line options are supported:
+.Bl -tag -width indent
+.It Fl i
+Turn on interrupt-driven mode.
+.It Fl p
+Turn on polled mode.
+.It Fl u Ar n
+Set the mode of the printer device specified by
+.Em n .
+The default value for
+.Em n
+is
+.Em 0
+(ie.
+.Pa /dev/lpt0 )
+.El
+.Pp
+One of
+.Fl i
+or
+.Fl p
+must be specified.
+.Pp
+.Sh FILES
+.Bl -tag -width /sys/i386/conf/GENERIC -compact
+.It Pa /dev/lpt?
+Printer devices.
+.It Pa /dev/lpctl?
+Printer control devices.
+.It Pa /sys/i386/conf/GENERIC
+Kernel configuration file.
+.El
+.Sh BUGS
+Sure to be some.
+.Sh "SEE ALSO"
+.Xr lpt 4
+.Sh AUTHOR
+.An Geoffrey M. Rehmet
+.Sh HISTORY
+.Nm Lptcontrol
+first appeared in
+.Fx 1.1.5
diff --git a/usr.sbin/lptcontrol/lptcontrol.c b/usr.sbin/lptcontrol/lptcontrol.c
new file mode 100644
index 0000000..73ea05c
--- /dev/null
+++ b/usr.sbin/lptcontrol/lptcontrol.c
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 1994 Geoffrey M. Rehmet
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Geoffrey M. Rehmet
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software withough specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#include <ctype.h>
+#include <err.h>
+#include <limits.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <machine/lpt.h>
+#include <sys/file.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+
+
+#define PATH_LPCTL _PATH_DEV "lpctl"
+#define DEFAULT_UNIT "0"
+#define IRQ_INVALID -1
+#define DO_POLL 0
+#define USE_IRQ 1
+
+static void usage()
+{
+ fprintf(stderr, "usage: lptcontrol -i | -p [-u <unit no.>]\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);
+}
+
+static char * dev_file(char unit_no)
+{
+ static char devname[_POSIX_PATH_MAX+1];
+ int len;
+
+ strncpy(devname, PATH_LPCTL, _POSIX_PATH_MAX);
+ devname[len = strlen(devname)] = unit_no;
+ devname[++len] = '\0';
+
+ return(devname);
+}
+
+int main (int argc, char * argv[])
+{
+ int opt;
+ int irq_status = IRQ_INVALID;
+ char * unit = DEFAULT_UNIT;
+
+ while((opt = getopt(argc, argv, "ipu:")) != -1)
+ switch(opt) {
+ case 'i': irq_status = USE_IRQ; break;
+ case 'p': irq_status = DO_POLL; break;
+ case 'u': unit = optarg;
+ if(!isdigit(*unit))
+ usage();
+ break;
+ default : usage();
+ }
+ if(irq_status == IRQ_INVALID)
+ usage();
+
+ set_interrupt_status(irq_status, dev_file(*unit));
+
+ exit(0);
+}
diff --git a/usr.sbin/manctl/Makefile b/usr.sbin/manctl/Makefile
new file mode 100644
index 0000000..d812530
--- /dev/null
+++ b/usr.sbin/manctl/Makefile
@@ -0,0 +1,10 @@
+# Makefile
+# $Id$
+
+MAN8= manctl.8
+
+beforeinstall:
+ ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \
+ ${.CURDIR}/manctl.sh ${DESTDIR}${BINDIR}/manctl
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/manctl/manctl.8 b/usr.sbin/manctl/manctl.8
new file mode 100644
index 0000000..03540c3
--- /dev/null
+++ b/usr.sbin/manctl/manctl.8
@@ -0,0 +1,55 @@
+.\" Copyright (c) 1996 Wolfram Schneider <wosch@FreeBSD.org>. Berlin.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $Id: manctl.8,v 1.6 1997/02/22 16:06:28 peter Exp $
+.Dd January 1, 1996
+.Dt MANCTL 8
+.Os FreeBSD
+.Sh NAME
+.Nm manctl
+.Nd manipulating manual pages
+.Sh SYNOPSIS
+.Nm manctl
+.Op Fl compress
+.Op Fl uncompress
+.Op Fl purge
+.Op Fl help
+.Ar path ...
+.Sh DESCRIPTION
+.Nm Manctl
+compress or uncompress manual pages in directory path.
+If possible, .so's are replaced with hard links.
+.Sh OPTIONS
+.Bl -tag -width -indent
+.It Fl help
+Print options and exit.
+.It Fl compress
+Compress uncompressed man pages (eliminating .so's).
+.It Fl uncompress
+Uncompress compressed man pages.
+.It Fl purge
+Purge old formatted man pages (not implemented yet).
+.Sh SEE ALSO
+.Xr catman 1 ,
+.Xr man 1
diff --git a/usr.sbin/manctl/manctl.sh b/usr.sbin/manctl/manctl.sh
new file mode 100644
index 0000000..7f81369
--- /dev/null
+++ b/usr.sbin/manctl/manctl.sh
@@ -0,0 +1,376 @@
+#!/bin/sh
+#
+# Copyright (c) 1994 Geoffrey M. Rehmet, Rhodes University
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. All advertising materials mentioning features or use of this software
+# must display the following acknowledgement:
+# This product includes software developed by Geoffrey M. Rehmet
+# 4. Neither the name of Geoffrey M. Rehmet nor that of Rhodes University
+# may be used to endorse or promote products derived from this software
+# without specific prior written permission.
+#
+# THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+# WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+# IN NO EVENT SHALL GEOFFREY M. REHMET OR RHODES UNIVERSITY BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# $Id: manctl.sh,v 1.9 1997/02/22 16:06:29 peter Exp $
+#
+# manctl:
+# a utility for manipulating manual pages
+# functions:
+# compress uncompressed man pages (elliminating .so's)
+# this is now two-pass. If possible, .so's
+# are replaced with hard links
+# uncompress compressed man pages
+# purge old formatted man pages (not implemented yet)
+# Things to watch out for:
+# Hard links - careful with g(un)zipping!
+# .so's - throw everything through soelim before gzip!
+# symlinks - ignore these - eg: expn is its own man page:
+# don't want to compress this!
+#
+PATH=/bin:/sbin:/usr/bin:/usr/sbin; export PATH
+
+#
+# purge cat? directories
+#
+do_purge()
+{
+ echo "purge $@" 2>&1
+ echo "not implemented yet\n" 2>&1
+}
+
+
+#
+# Uncompress one page
+#
+uncompress_page()
+{
+ local pname
+ local fname
+ local sect
+ local ext
+
+ # break up file name
+ pname=$1
+ IFS='.' ; set $pname
+ # less than 3 fields - don't know what to do with this
+ if [ $# -lt 3 ] ; then
+ IFS=" " ; echo ignoring $pname 1>&2 ; return 0 ;
+ fi
+ # construct name and section
+ fname=$1 ; shift
+ while [ $# -gt 2 ] ; do
+ fname=$fname.$1
+ shift
+ done
+ sect=$1
+ ext=$2
+
+ IFS=" "
+ case "$ext" in
+ gz|Z) {
+ IFS=" " ; set `file $pname`
+ if [ $2 != "gzip" ] ; then
+ echo moving hard link $pname 1>&2
+ mv $pname $fname.$ext # link
+ else
+ if [ $2 != "symbolic" ] ; then
+ echo gunzipping page $pname 1>&2
+ gunzip -c $pname > /tmp/manager.$$
+ chmod u+w $pname
+ cp /tmp/manager.$$ $pname
+ chmod 444 $pname
+ mv $pname $fname.$sect
+ rm /tmp/manager.$$
+ else
+ # skip symlinks - this can be
+ # a program like expn, which is
+ # its own man page !
+ echo skipping symlink $pname 1>&2
+ fi
+ fi };;
+ *) {
+ IFS=" "
+ echo skipping file $pname 1>&2
+ } ;;
+ esac
+ # reset IFS - this is important!
+ IFS=" "
+}
+
+
+#
+# Uncompress manpages in paths
+#
+do_uncompress()
+{
+ local i
+ local dir
+ local workdir
+
+ workdir=`pwd`
+ while [ $# != 0 ] ; do
+ if [ -d $1 ] ; then
+ dir=$1
+ cd $dir
+ for i in * ; do
+ case $i in
+ *cat?) ;; # ignore cat directories
+ *) {
+ if [ -d $i ] ; then
+ do_uncompress $i
+ else
+ if [ -e $i ] ; then
+ uncompress_page $i
+ fi
+ fi } ;;
+ esac
+ done
+ cd $workdir
+ else
+ echo "directory $1 not found" 1>&2
+ fi
+ shift
+ done
+}
+
+#
+# Remove .so's from one file
+#
+so_purge_page()
+{
+ local so_entries
+ local lines
+ local fname
+
+ so_entries=`grep "^\.so" $1 | wc -l`
+ if [ $so_entries -eq 0 ] ; then return 0 ; fi
+
+ # we have a page with a .so in it
+ echo $1 contains a .so entry 2>&1
+
+ # now check how many lines in the file
+ lines=`wc -l < $1`
+
+ # if the file is only one line long, we can replace it
+ # with a hard link!
+ if [ $lines -eq 1 ] ; then
+ fname=$1;
+ echo replacing $fname with a hard link
+ set `cat $fname`;
+ rm -f $fname
+ ln ../$2 $fname
+ else
+ echo inlining page $fname 1>&2
+ cat $fname | \
+ (cd .. ; soelim ) > /tmp/manager.$$
+ chmod u+w $fname
+ cp /tmp/manager.$$ $fname
+ chmod 444 $fname
+ fi
+}
+
+#
+# Remove .so entries from man pages
+# If a page consists of just one line with a .so,
+# replace it with a hard link
+#
+remove_so()
+{
+ local pname
+ local fname
+ local sect
+
+ # break up file name
+ pname=$1
+ IFS='.' ; set $pname
+ if [ $# -lt 2 ] ; then
+ IFS=" " ; echo ignoring $pname 1>&2 ; return 0 ;
+ fi
+ # construct name and section
+ fname=$1 ; shift
+ while [ $# -gt 1 ] ; do
+ fname=$fname.$1
+ shift
+ done
+ sect=$1
+
+ IFS=" "
+ case "$sect" in
+ gz) { echo file $pname already gzipped 1>&2 ; } ;;
+ Z) { echo file $pname already compressed 1>&2 ; } ;;
+ [12345678ln]*){
+ IFS=" " ; set `file $pname`
+ if [ $2 = "gzip" ] ; then
+ echo moving hard link $pname 1>&2
+ mv $pname $pname.gz # link
+ else
+ if [ $2 != "symbolic" ] ; then
+ echo "removing .so's in page $pname" 1>&2
+ so_purge_page $pname
+ else
+ # skip symlink - this can be
+ # a program like expn, which is
+ # its own man page !
+ echo skipping symlink $pname 1>&2
+ fi
+ fi };;
+ *) {
+ IFS=" "
+ echo skipping file $pname 1>&2
+ } ;;
+ esac
+ # reset IFS - this is important!
+ IFS=" "
+}
+
+
+#
+# compress one page
+# We need to watch out for hard links here.
+#
+compress_page()
+{
+ local pname
+ local fname
+ local sect
+
+ # break up file name
+ pname=$1
+ IFS='.' ; set $pname
+ if [ $# -lt 2 ] ; then
+ IFS=" " ; echo ignoring $pname 1>&2 ; return 0 ;
+ fi
+ # construct name and section
+ fname=$1 ; shift
+ while [ $# -gt 1 ] ; do
+ fname=$fname.$1
+ shift
+ done
+ sect=$1
+
+ IFS=" "
+ case "$sect" in
+ gz) { echo file $pname already gzipped 1>&2 ; } ;;
+ Z) { echo file $pname already compressed 1>&2 ; } ;;
+ [12345678ln]*){
+ IFS=" " ; set `file $pname`
+ if [ $2 = "gzip" ] ; then
+ echo moving hard link $pname 1>&2
+ mv $pname $pname.gz # link
+ else
+ if [ $2 != "symbolic" ] ; then
+ echo gzipping page $pname 1>&2
+ cat $pname | \
+ (cd .. ; soelim )| gzip -c -- > /tmp/manager.$$
+ chmod u+w $pname
+ cp /tmp/manager.$$ $pname
+ chmod 444 $pname
+ mv $pname $pname.gz
+ rm /tmp/manager.$$
+ else
+ # skip symlink - this can be
+ # a program like expn, which is
+ # its own man page !
+ echo skipping symlink $pname 1>&2
+ fi
+ fi };;
+ *) {
+ IFS=" "
+ echo skipping file $pname 1>&2
+ } ;;
+ esac
+ # reset IFS - this is important!
+ IFS=" "
+}
+
+#
+# Compress man pages in paths
+#
+do_compress_so()
+{
+ local i
+ local dir
+ local workdir
+ local what
+
+ what=$1
+ shift
+ workdir=`pwd`
+ while [ $# != 0 ] ; do
+ if [ -d $1 ] ; then
+ dir=$1
+ cd $dir
+ for i in * ; do
+ case $i in
+ *cat?) ;; # ignore cat directories
+ *) {
+ if [ -d $i ] ; then
+ do_compress_so $what $i
+ else
+ if [ -e $i ] ; then
+ $what $i
+ fi
+ fi } ;;
+ esac
+ done
+ cd $workdir
+ else
+ echo "directory $1 not found" 1>&2
+ fi
+ shift
+ done
+}
+
+#
+# Display a usage message
+#
+ctl_usage()
+{
+ echo "usage: $1 -compress <path> ... " 1>&2
+ echo " $1 -uncompress <path> ... " 1>&2
+ echo " $1 -purge <days> <path> ... " 1>&2
+ echo " $1 -purge expire <path> ... " 1>&2
+ exit 1
+}
+
+#
+# remove .so's and do compress
+#
+do_compress()
+{
+ # First remove all so's from the pages to be compressed
+ do_compress_so remove_so "$@"
+ # now do ahead and compress the pages
+ do_compress_so compress_page "$@"
+}
+
+#
+# dispatch options
+#
+if [ $# -lt 2 ] ; then ctl_usage $0 ; fi ;
+
+case "$1" in
+ -compress) shift ; do_compress "$@" ;;
+ -uncompress) shift ; do_uncompress "$@" ;;
+ -purge) shift ; do_purge "$@" ;;
+ *) ctl_usage $0 ;;
+esac
diff --git a/usr.sbin/mixer/Makefile b/usr.sbin/mixer/Makefile
new file mode 100644
index 0000000..d11934f
--- /dev/null
+++ b/usr.sbin/mixer/Makefile
@@ -0,0 +1,6 @@
+# $Id: Makefile,v 1.4 1997/02/22 16:06:33 peter Exp $
+
+PROG = mixer
+MAN8= mixer.8
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/mixer/mixer.8 b/usr.sbin/mixer/mixer.8
new file mode 100644
index 0000000..ec031ee
--- /dev/null
+++ b/usr.sbin/mixer/mixer.8
@@ -0,0 +1,137 @@
+.\" Copyright (c) 1997
+.\" Mike Pritchard <mpp@FreeBSD.ORG>. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. Neither the name of the author nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY MIKE PRITCHARD AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.Dd January 9, 1997
+.Dt MIXER 8
+.Os
+.Sh NAME
+.Nm mixer
+.Nd set/display soundcard mixer values
+.Sh SYNOPSIS
+.Nm
+.Op Fl f Ar device
+.Oo
+.Oo Ns
+.Ar dev Op Ar lvol Ns Op Ar :rvol
+.Li | recsrc |
+.Ar {^|+|-|=}rec rdev
+.Oc
+.Ar ... \&
+.Oc
+.Sh DESCRIPTION
+The
+.Nm
+command is used to set and display soundcard mixer device levels. It may
+also be used to start and stop recording from the soundcard. The list
+of mixer devices that may be modified are:
+.Pp
+.Bd -ragged -offset indent
+vol, bass, treble, synth, pcm, speaker, mic, cd, mix,
+pcm2, rec, igain, ogain, line1, line2, and line3.
+.Ed
+.Pp
+Not all mixer devices are available.
+.Pp
+When
+.Nm
+is run without any arguments, all supported devices are displayed
+along with current values.
+If the
+.Ar dev
+argument is specified, the value for the
+.Ar dev
+device will be displayed.
+.Pp
+To modify the mixer value
+.Ar dev ,
+the optional left and right channel settings of
+.Ar lvol Ns Op Ar :rvol
+may be specified. The
+.Ar lvol
+and
+.Ar rvol
+arguments may be from 0 - 100.
+.Pp
+To change the recording device you use one of:
+.Bl -tag -width =rec -offset indent
+.It ^rec
+toggles
+.Ar rdev
+of possible recording devices
+.It +rec
+adds
+.Ar rdev
+to possible recording devices
+.It -rec
+removes
+.Ar rdev
+from possible recording devices
+.It =rec
+sets the recording device to
+.Ar rdev
+.El
+.Pp
+The above commands work on an internal mask. After all the options
+have been parsed, it will set then read the mask from the sound card.
+This will let you see EXACTLY what the soundcard is using for the
+recording device(s).
+.Pp
+The option recsrc will display the current recording devices.
+.Pp
+The option
+.Fl f
+.Ar device
+will open
+.Ar device
+as the mixer device.
+This will ONLY work if the
+.Fl f
+option is immediately after the
+.Nm
+command.
+.Sh FILES
+.Bl -tag -width /dev/mixer -compact
+.It Pa /dev/mixer
+The default mixer device.
+.Sh SEE ALSO
+.Xr cdcontrol 1 ,
+.Xr cdplay 1
+.Sh HISTORY
+The
+.Nm
+command first appeared in
+.Fx 2.0.5 .
+.Sh AUTHORS
+Original source by
+.An Craig Metz Aq cmetz@thor.tjhsst.edu
+and
+.An Hannu Savolainen .
+Mostly rewriten by
+.An John-Mark Gurney Aq jmg@freebsd.org .
+This
+manual page was written by
+.An Mike Pritchard Aq mpp@FreeBSD.ORG .
diff --git a/usr.sbin/mixer/mixer.c b/usr.sbin/mixer/mixer.c
new file mode 100644
index 0000000..5c0ffa6
--- /dev/null
+++ b/usr.sbin/mixer/mixer.c
@@ -0,0 +1,233 @@
+/*
+ * This is an example of a mixer program for Linux
+ *
+ * updated 1/1/93 to add stereo, level query, broken
+ * devmask kludge - cmetz@thor.tjhsst.edu
+ *
+ * (C) Craig Metz and Hannu Savolainen 1993.
+ *
+ * You may do anything you wish with this program.
+ *
+ * ditto for my modifications (John-Mark Gurney, 1997)
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id: mixer.c,v 1.7 1997/09/29 06:38:49 charnier Exp $";
+#endif /* not lint */
+
+#include <err.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#ifdef __FreeBSD__
+#include <machine/soundcard.h>
+#else
+#include <sys/soundcard.h>
+#endif
+
+char *names[SOUND_MIXER_NRDEVICES] = SOUND_DEVICE_NAMES;
+
+void usage(int devmask, int recmask);
+int res_name(const char *name, int mask);
+void print_recsrc(int recsrc);
+
+void
+usage(int devmask, int recmask)
+{
+ int i, n;
+
+ printf("usage: mixer [[dev [voll[:volr]] | recsrc | {^|+|-|=}rec recdev] ... ]\n");
+ printf(" devices: ");
+ for (i = 0, n = 0; i < SOUND_MIXER_NRDEVICES; i++)
+ if ((1 << i) & devmask) {
+ if (n)
+ printf(", ");
+ printf(names[i]);
+ n = 1;
+ }
+ printf("\n rec devices: ");
+ for (i = 0, n = 0; i < SOUND_MIXER_NRDEVICES; i++)
+ if ((1 << i) & recmask) {
+ if (n)
+ printf(", ");
+ printf(names[i]);
+ n = 1;
+ }
+ printf("\n");
+ exit(1);
+}
+
+int
+res_name(const char *name, int mask)
+{
+ int foo;
+
+ for (foo = 0; foo < SOUND_MIXER_NRDEVICES; foo++)
+ if ((1 << foo) & mask && !strcmp(names[foo], name))
+ break;
+
+ return foo == SOUND_MIXER_NRDEVICES ? -1 : foo;
+}
+
+void
+print_recsrc(int recsrc)
+{
+ int i, n = 0;
+ fprintf(stderr, "Recording source: ");
+
+ for (i = 0; i < SOUND_MIXER_NRDEVICES; i++)
+ if ((1 << i) & recsrc) {
+ if (n)
+ fprintf(stderr, ", ");
+ fprintf(stderr, names[i]);
+ n = 1;
+ }
+ fprintf(stderr, "\n");
+}
+
+int
+main(int argc, char *argv[])
+{
+ int foo, bar, baz, dev;
+ int devmask = 0, recmask = 0, recsrc = 0, orecsrc;
+ int dusage = 0, drecsrc = 0;
+ int l, r;
+
+ char *name;
+
+ name = strdup("/dev/mixer");
+
+ if (!strcmp(argv[0], "mixer2"))
+ name = strdup("/dev/mixer1");
+ else if (!strcmp(argv[0], "mixer3"))
+ name = strdup("/dev/mixer2");
+
+ if (argc > 1 && !strcmp(argv[1], "-f")) {
+ name = strdup(argv[2]);
+ argc -= 2; argv += 2;
+ }
+
+ if ((baz = open(name, O_RDWR)) < 0)
+ err(1, "%s", name);
+ free(name);
+ if (ioctl(baz, SOUND_MIXER_READ_DEVMASK, &devmask) == -1)
+ err(1, "SOUND_MIXER_READ_DEVMASK");
+ if (ioctl(baz, SOUND_MIXER_READ_RECMASK, &recmask) == -1)
+ err(1, "SOUND_MIXER_READ_RECMASK");
+ if (ioctl(baz, SOUND_MIXER_READ_RECSRC, &recsrc) == -1)
+ err(1, "SOUND_MIXER_READ_RECSRC");
+ orecsrc = recsrc;
+
+ if (argc == 1) {
+ for (foo = 0; foo < SOUND_MIXER_NRDEVICES; foo++) {
+ if (!((1 << foo) & devmask))
+ continue;
+ if (ioctl(baz, MIXER_READ(foo),&bar)== -1) {
+ warn("MIXER_READ");
+ continue;
+ }
+ printf("Mixer %-8s is currently set to %3d:%d\n", names[foo], bar & 0x7f, (bar >> 8) & 0x7f);
+ }
+ return(0);
+ }
+
+ argc--; argv++;
+
+ while (argc) {
+ if (!strcmp("recsrc", *argv)) {
+ drecsrc = 1;
+ argc--; argv++;
+ continue;
+ } else if (argc > 1 && !strcmp("rec", *argv + 1)) {
+ if (**argv != '+' && **argv != '-' &&
+ **argv != '=' && **argv != '^') {
+ dusage = 1;
+ argc -= 1; argv += 1;
+ continue;
+ }
+ if ((dev = res_name(argv[1], recmask)) == -1) {
+ dusage = 1;
+ argc -= 1; argv += 1;
+ continue;
+ }
+ switch(**argv) {
+ case '+':
+ recsrc |= (1 << dev);
+ break;
+ case '-':
+ recsrc &= ~(1 << dev);
+ break;
+ case '=':
+ recsrc = (1 << dev);
+ break;
+ case '^':
+ recsrc ^= (1 << dev);
+ break;
+ }
+ drecsrc = 1;
+ argc -= 2; argv += 2;
+ continue;
+ }
+
+ if ((dev = res_name(*argv, devmask)) == -1) {
+ dusage = 1;
+ argc--; argv++;
+ continue;
+ }
+
+ switch(argc > 1 ? sscanf(argv[1], "%d:%d", &l, &r) : 0) {
+ case 0:
+ if (ioctl(baz, MIXER_READ(dev),&bar)== -1) {
+ warn("MIXER_READ");
+ argc--; argv++;
+ continue;
+ }
+ printf("Mixer %-8s is currently set to %3d:%d\n",
+ names[dev], bar & 0x7f, (bar >> 8) & 0x7f);
+
+ argc--; argv++;
+ break;
+ case 1:
+ r = l;
+ case 2:
+ if (l < 0)
+ l = 0;
+ else if (l > 100)
+ l = 100;
+ if (r < 0)
+ r = 0;
+ else if (r > 100)
+ r = 100;
+
+ printf("Setting the mixer %s to %d:%d.\n", names[dev],
+ l, r);
+
+ l |= r << 8;
+ if (ioctl(baz, MIXER_WRITE(dev), &l) == -1)
+ warn("WRITE_MIXER");
+
+ argc -= 2; argv += 2;
+ break;
+ }
+ }
+
+ if (orecsrc != recsrc)
+ if (ioctl(baz, SOUND_MIXER_WRITE_RECSRC, &recsrc) == -1)
+ err(1, "SOUND_MIXER_WRITE_RECSRC");
+
+ if (drecsrc) {
+ if (ioctl(baz, SOUND_MIXER_READ_RECSRC, &recsrc) == -1)
+ err(1, "SOUND_MIXER_READ_RECSRC");
+ print_recsrc(recsrc);
+ }
+
+ close(baz);
+
+ if (dusage)
+ usage(devmask, recmask);
+
+ exit(0);
+}
diff --git a/usr.sbin/mkdosfs/Makefile b/usr.sbin/mkdosfs/Makefile
new file mode 100644
index 0000000..1d8ff5d
--- /dev/null
+++ b/usr.sbin/mkdosfs/Makefile
@@ -0,0 +1,59 @@
+# $Id$
+#
+PROG= mkdosfs
+
+CFLAGS+= -Wall
+
+###################################################################
+#
+# Everything below is solely intented for maintenance.
+# As you can see, it requires as86/ld86 from the ``bcc'' package.
+#
+# For this reason, the bootcode.h target puts the result into
+# ${.CURDIR}
+
+AS86= as86
+LD86= ld86
+AS86FLAGS= -0
+LD86FLAGS= -0 -s
+
+CLEANFILES+= *.obj *.bin *.com
+.SUFFIXES: .asm .obj .bin .com
+
+.asm.obj:
+ ${AS86} ${AS86FLAGS} -o ${.TARGET} ${.IMPSRC}
+
+.obj.bin:
+ ${LD86} ${LD86FLAGS} -T 0x7c00 -o ${.PREFIX}.tmp ${.IMPSRC}
+ dd bs=32 skip=1 of=${.TARGET} if=${.PREFIX}.tmp
+ rm -f ${.PREFIX}.tmp
+
+# .com file is just for testing
+.obj.com:
+ ${LD86} ${LD86FLAGS} -T 0x100 -o ${.PREFIX}.tmp ${.IMPSRC}
+ dd bs=32 skip=1 of=${.TARGET} if=${.PREFIX}.tmp
+ rm -f ${.PREFIX}.tmp
+
+## Do NOT depend this on bootcode.bin unless you've installed the
+## bcc package!
+bootcode.h: ## bootcode.bin
+ @echo converting bootcode.bin into bootcode.h...
+ @perl -e 'if(read(STDIN,$$buf,512)<512) { \
+ die "Read error on .bin file\n"; \
+ } \
+ @arr = unpack("C*",$$buf); \
+ print "#ifndef BOOTCODE_H\n"; \
+ print "#define BOOTCODE_H 1\n\n"; \
+ print "/*\n * This file has been generated\n"; \
+ print " * automatically. Do not edit.\n */\n\n"; \
+ print "static unsigned char bootcode[512] = {\n"; \
+ for($$i=0; $$i<512; $$i++) { \
+ printf "0x%02x, ",$$arr[$$i]; \
+ if($$i % 12 == 11) {print "\n";} \
+ } \
+ print "};\n\n"; \
+ print "#endif /* BOOTCODE_H */\n";' \
+ < bootcode.bin > ${.CURDIR}/bootcode.h
+
+.include <bsd.prog.mk>
+
diff --git a/usr.sbin/mkdosfs/bootcode.asm b/usr.sbin/mkdosfs/bootcode.asm
new file mode 100644
index 0000000..7e5d429
--- /dev/null
+++ b/usr.sbin/mkdosfs/bootcode.asm
@@ -0,0 +1,101 @@
+;;; Hello emacs, this looks like -*- asm -*- code, doesn't it?
+;;;
+;;; This forms a simple dummy boot program for use with a tool to
+;;; format DOS floppies. All it does is displaying a message, and
+;;; recover gracefully by re-initializing the CPU.
+;;;
+;;; Written by Joerg Wunsch, Dresden. Placed in the public domain.
+;;; This software is provided as is, neither kind of warranty applies.
+;;; Use at your own risk.
+;;;
+;;; (This is written in as86 syntax. as86 is part of Bruce Evans'
+;;; bcc package.)
+;;;
+;;; $Id: bootcode.asm,v 1.4 1997/11/07 00:12:54 joerg Exp $
+;;;
+;;; This code must be linked to address 0x7c00 in order to function
+;;; correctly (the BIOS boot address).
+;;;
+;;; It's 16-bit code, and we don't care for a data segment.
+ use16
+ .text
+
+ entry _begin
+_begin: jmp init ; jump to boot prog
+ nop ; historical baggage ;-)
+;;;
+;;; Reserve space for the "BIOS parameter block".
+;;; This will be overwritten by the actual formatting routine.
+;;;
+bpb: .ascii "BSD 4.4" ; "OEM" name
+ .word 512 ; sector size
+ .byte 2 ; cluster size
+ .word 1 ; reserved sectors (just the boot sector)
+ .byte 2 ; FAT count
+ .word 112 ; # of entries in root dir
+ .word 1440 ; total number of sectors, MSDOS 3.3 or below
+ .byte 0xf9 ; "media descriptor"
+ .word 3 ; FAT size (sectors)
+ .word 9 ; sectors per track
+ .word 2 ; heads per cylinder
+ .word 0 ; hidden sectors
+ ;; MSDOS 4.0++ -- only valid iff total number of sectors == 0
+ .word 0 ; unused
+ .long 0 ; total number of sectors
+ .short 0 ; physical drive (0, 1, ..., 0x80) %-)
+ .byte 0 ; "extented boot signature"
+ .long 0 ; volume serial number (i.e., garbage :)
+ .ascii " " ; label -- same as vol label in root dir
+ .ascii "FAT12 " ; file system ID
+;;;
+;;; Executable code starts here.
+;;;
+init:
+ ;; First, display our message.
+ mov si, *message
+lp1: seg cs
+ movb al, [si]
+ inc si
+ testb al, al
+ jz lp2 ; null-terminated string
+ movb bl, *7 ; display with regular attribute
+ movb ah, *0x0e ; int 0x10, fnc 0x0e -- emulate tty
+ int 0x10
+ jmp lp1
+lp2: xorb ah, ah ; int 0x16, fnc 0x00 -- wait for keystroke
+ int 0x16
+ mov ax, *0x40 ; write 0x1234 to address 0x472 --
+ push ax ; tell the BIOS that this is a warm boot
+ pop ds
+ mov 0x72, *0x1234
+ int 0x19 ; jump to CPU initialization code
+
+message:
+ .byte 7
+ .byte 0xc9
+ .byte 0xcd,0xcd,0xcd,0xcd,0xcd,0xcd,0xcd,0xcd,0xcd,0xcd
+ .byte 0xcd,0xcd,0xcd,0xcd,0xcd,0xcd,0xcd,0xcd,0xcd,0xcd
+ .byte 0xcd,0xcd,0xcd,0xcd,0xcd,0xcd,0xcd,0xcd,0xcd,0xcd
+ .byte 0xcd,0xcd,0xcd,0xcd,0xcd,0xcd,0xcd,0xcd,0xcd,0xcd
+ .byte 0xcd,0xcd,0xcd,0xcd,0xcd,0xcd
+ .byte 0xbb, 13, 10, 0xba
+ .ascii " Sorry, this disc does actually not contain "
+ .byte 0xba, 13, 10, 0xba
+ .ascii " a bootable system. "
+ .byte 0xba, 13, 10, 0xba
+ .ascii " Press any key to reboot. "
+ .byte 0xba, 13, 10, 0xc8
+ .byte 0xcd,0xcd,0xcd,0xcd,0xcd,0xcd,0xcd,0xcd,0xcd,0xcd
+ .byte 0xcd,0xcd,0xcd,0xcd,0xcd,0xcd,0xcd,0xcd,0xcd,0xcd
+ .byte 0xcd,0xcd,0xcd,0xcd,0xcd,0xcd,0xcd,0xcd,0xcd,0xcd
+ .byte 0xcd,0xcd,0xcd,0xcd,0xcd,0xcd,0xcd,0xcd,0xcd,0xcd
+ .byte 0xcd,0xcd,0xcd,0xcd,0xcd,0xcd
+ .byte 0xbc, 13,10
+ .byte 0
+
+ ;; Adjust the value below after changing the length of
+ ;; the code above!
+ .space 0x1fe-0x15e ; pad to 512 bytes
+
+ .byte 0x55, 0xaa ; yes, we are bootable (cheating :)
+ end
diff --git a/usr.sbin/mkdosfs/bootcode.h b/usr.sbin/mkdosfs/bootcode.h
new file mode 100644
index 0000000..70cd4b8
--- /dev/null
+++ b/usr.sbin/mkdosfs/bootcode.h
@@ -0,0 +1,54 @@
+#ifndef BOOTCODE_H
+#define BOOTCODE_H 1
+
+/*
+ * This file has been generated
+ * automatically. Do not edit.
+ */
+
+static unsigned char bootcode[512] = {
+0xeb, 0x3c, 0x90, 0x42, 0x53, 0x44, 0x20, 0x20, 0x34, 0x2e, 0x34, 0x00,
+0x02, 0x02, 0x01, 0x00, 0x02, 0x70, 0x00, 0xa0, 0x05, 0xf9, 0x03, 0x00,
+0x09, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x20, 0x20, 0x20, 0x20, 0x20,
+0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x46, 0x41, 0x54, 0x31, 0x32, 0x20,
+0x20, 0x20, 0xbe, 0x62, 0x7c, 0x2e, 0x8a, 0x04, 0x46, 0x84, 0xc0, 0x74,
+0x08, 0xb3, 0x07, 0xb4, 0x0e, 0xcd, 0x10, 0xeb, 0xf0, 0x30, 0xe4, 0xcd,
+0x16, 0xb8, 0x40, 0x00, 0x50, 0x1f, 0xc7, 0x06, 0x72, 0x00, 0x34, 0x12,
+0xcd, 0x19, 0x07, 0xc9, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
+0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
+0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
+0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
+0xcd, 0xcd, 0xbb, 0x0d, 0x0a, 0xba, 0x20, 0x20, 0x53, 0x6f, 0x72, 0x72,
+0x79, 0x2c, 0x20, 0x74, 0x68, 0x69, 0x73, 0x20, 0x64, 0x69, 0x73, 0x63,
+0x20, 0x64, 0x6f, 0x65, 0x73, 0x20, 0x61, 0x63, 0x74, 0x75, 0x61, 0x6c,
+0x6c, 0x79, 0x20, 0x6e, 0x6f, 0x74, 0x20, 0x63, 0x6f, 0x6e, 0x74, 0x61,
+0x69, 0x6e, 0x20, 0x20, 0xba, 0x0d, 0x0a, 0xba, 0x20, 0x20, 0x61, 0x20,
+0x62, 0x6f, 0x6f, 0x74, 0x61, 0x62, 0x6c, 0x65, 0x20, 0x73, 0x79, 0x73,
+0x74, 0x65, 0x6d, 0x2e, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xba, 0x0d, 0x0a, 0xba, 0x20, 0x20,
+0x50, 0x72, 0x65, 0x73, 0x73, 0x20, 0x61, 0x6e, 0x79, 0x20, 0x6b, 0x65,
+0x79, 0x20, 0x74, 0x6f, 0x20, 0x72, 0x65, 0x62, 0x6f, 0x6f, 0x74, 0x2e,
+0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20,
+0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0xba, 0x0d, 0x0a, 0xc8,
+0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
+0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
+0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd,
+0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xcd, 0xbc, 0x0d,
+0x0a, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 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, 0x55, 0xaa, };
+
+#endif /* BOOTCODE_H */
diff --git a/usr.sbin/mkdosfs/dosfs.h b/usr.sbin/mkdosfs/dosfs.h
new file mode 100644
index 0000000..08afd4c
--- /dev/null
+++ b/usr.sbin/mkdosfs/dosfs.h
@@ -0,0 +1,135 @@
+/*
+ * 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.
+ *
+ * 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.
+ */
+
+/*
+ * MS-DOS (FAT) file system structure definitions.
+ *
+ * $Id$
+ */
+
+#ifndef DOSFS_H
+#define DOSFS_H 1
+
+typedef u_char Long[4];
+typedef u_char Short[2];
+
+union bootsector
+{
+ unsigned char raw[512];
+ struct bsec
+ {
+ u_char jump_boot[3]; /* jump code to boot-up partition */
+ char oem_name[8]; /* OEM company name & version */
+ Short sectsiz; /* bytes per sector */
+ u_char clustsiz; /* sectors per cluster */
+ Short ressecs; /* reserved sectors [before 1st FAT] */
+ u_char fatcnt; /* # of FAT's */
+ Short rootsiz; /* number of root dir entries */
+ Short totsecs; /* total # of sectors */
+ u_char media; /* media descriptor */
+ Short fatsize; /* # of sectors per FAT */
+ Short trksecs; /* sectors per track (cylinder) */
+ Short headcnt; /* # of r/w heads */
+ Short hidnsec; /* hidden sectors */
+ union
+ {
+ /* case totsecs != 0: */
+ /* This is a partition of MS-DOS 3.3 format (< 32 MB) */
+ u_char bootprogram[480];
+
+ /* case totsecs == 0: */
+ /* partition of MS-DOS 4.0+ format, or > 32 MB */
+ struct
+ {
+ Short unused;
+ Long totsecs; /* total # of sectors, as a 32-bit */
+ Short physdrv; /* physical drive # [0x80...] */
+ u_char extboot; /* extended boot signature??? */
+ Long serial; /* volume serial number */
+ char label[11]; /* same as volume label in root dir */
+ char fsysid[8]; /* some like `FAT16' */
+ u_char bootprogram[448];
+ } extended;
+ } variable_part;
+ u_char signature[2]; /* always {0x55, 0xaa} */
+ } bsec;
+};
+
+struct fat
+{
+ u_char media; /* the media descriptor again */
+ u_char padded; /* alway 0xff */
+ u_char contents[1]; /* the `1' is a placeholder only */
+};
+
+/* DOS file attributes */
+#define FA_RONLY 1 /* read/only */
+#define FA_HIDDEN 2 /* hidden */
+#define FA_SYSTEM 4 /* system */
+#define FA_VOLLABEL 8 /* this is the volume label */
+#define FA_SUBDIR 0x10 /* sub-directory */
+#define FA_ARCH 0x20 /* archive - file hasn't been backed up */
+
+struct dosftime
+{
+ u_char time[2]; /* [0] & 0x1f - seconds div 2
+ * ([1] & 7) * 8 + ([0] >> 5) - minutes
+ * [1] >> 3 - hours
+ */
+ u_char date[2]; /* [0] & 0x1f - day
+ * ([1] & 1) * 8 + ([0] >> 5) - month
+ * [1] >> 1 - year - 1980
+ */
+};
+
+#define dosft_hour(dft) ((dft).time[1] >> 3)
+#define dosft_minute(dft) (((dft).time[1] & 7) * 8 + ((dft).time[0] >> 5))
+#define dosft_second(dft) (((dft).time[0] & 0x1f) * 2)
+#define dosft_year(dft) (((dft).date[1] >> 1) + 1980)
+#define dosft_month(dft) (((dft).date[1] & 1) * 8 + ((dft).date[0] >> 5))
+#define dosft_day(dft) ((dft).date[0] & 0x1f)
+
+
+struct direntry
+{
+ char name[8]; /* file name portion */
+ char ext[3]; /* file extension */
+ u_char attr; /* file attribute as above */
+ char reserved[10];
+ struct dosftime fdate; /* time created/last modified */
+ Short startclstr; /* starting cluster number */
+ Long filesiz; /* file size in bytes */
+};
+
+/* handle endiannes: */
+#define s_to_little_s(dst, src) dst[0]=(src)&0xff; dst[1]=((src)&0xff00)>>8
+#define l_to_little_l(dst, src) \
+dst[0]=(src)&0xff; dst[1]=((src)&0xff00)>>8; \
+dst[2]=((src)&0xff0000)>>16; dst[3]=((src)&0xff000000)>>24
+
+#endif /* DOSFS_H */
diff --git a/usr.sbin/mkdosfs/mkdosfs.1 b/usr.sbin/mkdosfs/mkdosfs.1
new file mode 100644
index 0000000..652f0e5
--- /dev/null
+++ b/usr.sbin/mkdosfs/mkdosfs.1
@@ -0,0 +1,135 @@
+.\"
+.\" Copyright (c) 1995, 1996 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.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+.\" IN NO EVENT SHALL THE DEVELOPERS BE LIABLE FOR ANY DIRECT, INDIRECT,
+.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+.\"
+.\" $Id$
+.\"
+.Dd November 5, 1995
+.Os
+.Dt MKDOSFS 1
+.Sh NAME
+.Nm mkdosfs
+.Nd create an MS-DOS (FAT) file system
+.Sh SYNOPSIS
+.Nm mkdosfs
+.Bq Fl f Ar capacity
+.Bq Fl L Ar vollabel
+.Ar device
+.Sh DESCRIPTION
+.Nm Mkdosfs
+establishes a file system structure on
+.Ar device
+that is understood by
+.Xr mount_msdos
+and some ancient program loader.
+.Ar Device
+will typically be the character device node for a floppy disk drive,
+.Pq e.\ g. Pa /dev/rfd0 ,
+although any existing writable file or device is acceptable. In case
+of a regular file it is treated as a dumped image of an MS-DOS file
+system; only the file system structure will be written to it, and it
+won't be truncated.
+.Pp
+The options are as follows:
+.Bl -tag -width 10n -offset indent
+.It Fl f Ar capacity
+Use defaults for a typical file system with
+.Ar capacity
+kilobytes. Currently, the values 360, 720, 1200, and 1440 are
+recognized.
+.Pp
+If this option is not specified,
+.Nm
+attempts to determine the size of the
+.Ar device .
+This is not reliably possible in all cases, but is believed to work
+for the more common situations.
+.It Fl L Ar vollabel
+Use
+.Ar vollabel
+to describe the file system, instead of the default
+.Ql 4.4BSD .
+.El
+.Pp
+The file system structure consists of three major areas:
+.Bl -tag -width 10n -offset indent
+.It Em The bootsector
+This is the very first (512-byte) sector. It contains executable
+code that normally would bootstrap an operating system when loaded.
+Since it's beyond the scope of
+.Nm
+to install an operating system on the medium, this boot code will only
+print a message that the disk does not contain a bootable system.
+Inside the
+.Em bootsector
+is the
+.Em BIOS parameter block (BPB) ,
+where several statistical parameters of the file system are being
+held.
+.It Em The file allocation table(s) (FAT)
+Sectors next to the
+.Em bootsector
+hold the FAT, which is used to register file system allocation,
+as well as keeping pointer chains for the chunks constituting
+one file. There are usually two identical copies of the FAT.
+.It Em The root directory
+The final structure is the root directory for this medium. It is
+merely a space reservation, padded with 0's, and unfortunately fixed
+in its size.
+.Nm mkdosfs
+initializes it to empty, and enters a volume label record into the
+very first directory slot.
+.Sh DIAGNOSTICS
+An exit status of 0 is returned upon successful operation. Exit status
+1 is returned on any errors during file system creation, 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 fdformat 1 ,
+.Xr mount_msdos 8 ,
+.Xr newfs 8 .
+.Sh BUGS
+There is currently no way to specify obscure file system parameters.
+Thus, only media with one of the supported capacity values can be
+formatted. For the same reason, it's not possible to handle hard disk
+partitions. More options should be added to allow this. More entries
+should be added to the table of known formats, too.
+.Pp
+No attempt is made to handle media defects. However, this is beyond
+the scope of
+.Nm mkdosfs
+and should better be handled by the (nonexistent)
+.Xr dosfsck 1
+utility.
+.Sh HISTORY
+.Nm Mkdosfs
+appeared in
+.Fx 2.2 .
+.Sh AUTHOR
+The program has been contributed by
+.if n Joerg Wunsch,
+.if t J\(:org Wunsch,
+Dresden.
diff --git a/usr.sbin/mkdosfs/mkdosfs.c b/usr.sbin/mkdosfs/mkdosfs.c
new file mode 100644
index 0000000..f52a29c
--- /dev/null
+++ b/usr.sbin/mkdosfs/mkdosfs.c
@@ -0,0 +1,312 @@
+/*
+ * Copyright (c) 1995, 1996 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.
+ *
+ * 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.
+ */
+
+/*
+ * Create an MS-DOS (FAT) file system.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <memory.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "bootcode.h"
+#include "dosfs.h"
+
+struct descrip
+{
+ /* our database key */
+ unsigned kilobytes;
+ /* MSDOS 3.3 BPB fields */
+ u_short sectsiz;
+ u_char clustsiz;
+ u_short ressecs;
+ u_char fatcnt;
+ u_short rootsiz;
+ u_short totsecs;
+ u_char media;
+ u_short fatsize;
+ u_short trksecs;
+ u_short headcnt;
+ u_short hidnsec;
+ /* MSDOS 4 BPB extensions */
+ u_long ext_totsecs;
+ u_short ext_physdrv;
+ u_char ext_extboot;
+ char ext_label[11];
+ char ext_fsysid[8];
+};
+
+static struct descrip
+table[] =
+{
+ /* NB: must be sorted, starting with the largest format! */
+ /*
+ * kilobytes
+ * sec cls res fat rot tot med fsz spt hds hid
+ * tot phs ebt label fsysid
+ */
+ {1440,
+ 512, 1, 1, 2, 224, 2880, 0xf0, 9, 18, 2, 0,
+ 0, 0, 0, "4.4BSD ", "FAT12 "},
+ {1200,
+ 512, 1, 1, 2, 224, 2400, 0xf9, 7, 15, 2, 0,
+ 0, 0, 0, "4.4BSD ", "FAT12 "},
+ { 720,
+ 512, 2, 1, 2, 112, 1440, 0xf9, 3, 9, 2, 0,
+ 0, 0, 0, "4.4BSD ", "FAT12 "},
+ { 360,
+ 512, 2, 1, 2, 112, 720, 0xfd, 2, 9, 2, 0,
+ 0, 0, 0, "4.4BSD ", "FAT12 "},
+};
+
+static void
+usage(void)
+{
+ fprintf(stderr, "usage: mkdosfs [-f kbytes] [-L label] device\n");
+ exit(2);
+}
+
+unsigned
+findformat(int fd)
+{
+ struct stat sb;
+
+ /*
+ * This is a bit tricky. If the argument is a regular file, we can
+ * lseek() to its end and get the size reported. If it's a device
+ * however, lseeking doesn't report us any useful number. Instead,
+ * we try to seek just to the end of the device and try reading a
+ * block there. In the case where we've hit exactly the device
+ * boundary, we get a zero read, and thus have found the size.
+ * Since our knowledge of distinct formats is limited anyway, this
+ * is not a big deal at all.
+ */
+
+ if(fstat(fd, &sb) == -1)
+ err(1, "huh? cannot fstat()"); /* Cannot happen */
+ if(S_ISREG(sb.st_mode))
+ {
+ off_t o;
+ if(lseek(fd, (off_t)0, SEEK_END) == -1 ||
+ (o = lseek(fd, (off_t)0, SEEK_CUR)) == -1)
+ /* Hmm, hmm. Hard luck. */
+ return 0;
+ return (int)(o / 1024);
+ }
+ else if(S_ISCHR(sb.st_mode) || S_ISBLK(sb.st_mode))
+ {
+ char b[512];
+ int i, rv;
+ struct descrip *dp;
+
+ for(i = 0, dp = table;
+ i < sizeof table / sizeof(struct descrip);
+ i++, dp++)
+ {
+ if(lseek(fd, (off_t)(dp->kilobytes * 1024), SEEK_SET) == 1)
+ /* Uh-oh, lseek() is not supposed to fail. */
+ return 0;
+ if((rv = read(fd, b, 512)) == 0)
+ break;
+ /* XXX The ENOSPC is for the bogus fd(4) driver return value. */
+ if(rv == -1 && errno != EINVAL && errno != ENOSPC)
+ return 0;
+ /* else: continue */
+ }
+ if(i == sizeof table / sizeof(struct descrip))
+ return 0;
+ (void)lseek(fd, (off_t)0, SEEK_SET);
+ return dp->kilobytes;
+ }
+ else
+ /* Outta luck. */
+ return 0;
+}
+
+
+void
+setup_boot_sector_from_template(union bootsector *bs, struct descrip *dp)
+{
+ memcpy((void *)bs->raw, (void *)bootcode, 512);
+
+ /* historical part of BPB */
+ s_to_little_s(bs->bsec.sectsiz, dp->sectsiz);
+ bs->bsec.clustsiz = dp->clustsiz;
+ s_to_little_s(bs->bsec.ressecs, dp->ressecs);
+ bs->bsec.fatcnt = dp->fatcnt;
+ s_to_little_s(bs->bsec.rootsiz, dp->rootsiz);
+ s_to_little_s(bs->bsec.totsecs, dp->totsecs);
+ bs->bsec.media = dp->media;
+ s_to_little_s(bs->bsec.fatsize, dp->fatsize);
+ s_to_little_s(bs->bsec.trksecs, dp->trksecs);
+ s_to_little_s(bs->bsec.headcnt, dp->headcnt);
+ s_to_little_s(bs->bsec.hidnsec, dp->hidnsec);
+
+ /* MSDOS 4 extensions */
+ l_to_little_l(bs->bsec.variable_part.extended.totsecs, dp->ext_totsecs);
+ s_to_little_s(bs->bsec.variable_part.extended.physdrv, dp->ext_physdrv);
+ bs->bsec.variable_part.extended.extboot = dp->ext_extboot;
+
+ /* assign a "serial number" :) */
+ srandom((unsigned)time((time_t)0));
+ l_to_little_l(bs->bsec.variable_part.extended.serial, random());
+
+ memcpy((void *)bs->bsec.variable_part.extended.label,
+ (void *)dp->ext_label, 11);
+ memcpy((void *)bs->bsec.variable_part.extended.fsysid,
+ (void *)dp->ext_fsysid, 8);
+}
+
+#define roundup(dst, limit) dst = (((dst) | ((limit) - 1)) & ~(limit)) + 1
+
+int
+main(int argc, char **argv)
+{
+ union bootsector bs;
+ struct descrip *dp;
+ struct fat *fat;
+ struct direntry *rootdir;
+ struct tm *tp;
+ time_t now;
+
+ int c, i, fd, format = 0, rootdirsize;
+ const char *label = 0;
+
+ while((c = getopt(argc, argv, "f:L:")) != -1)
+ switch(c)
+ {
+ case 'f':
+ format = atoi(optarg);
+ break;
+
+ case 'L':
+ label = optarg;
+ break;
+
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if(argc != 1)
+ usage();
+
+ if((fd = open(argv[0], O_RDWR|O_EXCL, 0)) == -1)
+ err(1, "open(%s)", argv[0]);
+
+ if(format == 0)
+ {
+ /*
+ * No format specified, try to figure it out.
+ */
+ if((format = findformat(fd)) == 0)
+ errx(1, "cannot determine size, must use -f format");
+ }
+
+ for(i = 0, dp = table; i < sizeof table / sizeof(struct descrip); i++, dp++)
+ if(dp->kilobytes == format)
+ break;
+ if(i == sizeof table / sizeof(struct descrip))
+ errx(1, "cannot find format description for %d KB", format);
+
+ /* prepare and write the boot sector */
+ setup_boot_sector_from_template(&bs, dp);
+
+ /* if we've got an explicit label, use it */
+ if(label)
+ strncpy(bs.bsec.variable_part.extended.label, label, 11);
+
+ if(write(fd, (char *)bs.raw, sizeof bs) != sizeof bs)
+ err(1, "boot sector write()");
+
+ /* now, go on with the FATs */
+ if((fat = (struct fat *)malloc(dp->sectsiz * dp->fatsize)) == 0)
+ abort();
+ memset((void *)fat, 0, dp->sectsiz * dp->fatsize);
+
+ fat->media = dp->media;
+ fat->padded = 0xff;
+ fat->contents[0] = 0xff;
+ if(dp->totsecs > 20740 || (dp->totsecs == 0 && dp->ext_totsecs > 20740))
+ /* 16-bit FAT */
+ fat->contents[1] = 0xff;
+
+ for(i = 0; i < dp->fatcnt; i++)
+ if(write(fd, (char *)fat, dp->sectsiz * dp->fatsize)
+ != dp->sectsiz * dp->fatsize)
+ err(1, "FAT write()");
+
+ free((void *)fat);
+
+ /* finally, build the root dir */
+ rootdirsize = dp->rootsiz * sizeof(struct direntry);
+ roundup(rootdirsize, dp->clustsiz * dp->sectsiz);
+
+ if((rootdir = (struct direntry *)malloc(rootdirsize)) == 0)
+ abort();
+ memset((void *)fat, 0, rootdirsize);
+
+ /* set up a volume label inside the root dir :) */
+ if(label)
+ strncpy(rootdir[0].name, label, 11);
+ else
+ memcpy(rootdir[0].name, dp->ext_label, 11);
+ rootdir[0].attr = FA_VOLLABEL;
+ now = time((time_t)0);
+ tp = localtime(&now);
+ rootdir[0].fdate.time[0] = tp->tm_sec / 2;
+ rootdir[0].fdate.time[0] |= (tp->tm_min & 7) << 5;
+ rootdir[0].fdate.time[1] = ((tp->tm_min >> 3) & 7);
+ rootdir[0].fdate.time[1] |= tp->tm_hour << 3;
+ rootdir[0].fdate.date[0] = tp->tm_mday;
+ rootdir[0].fdate.date[0] |= ((tp->tm_mon + 1) & 7) << 5;
+ rootdir[0].fdate.date[1] = ((tp->tm_mon + 1) >> 3) & 1;
+ rootdir[0].fdate.date[1] |= (tp->tm_year - 80) << 1;
+
+ if(write(fd, (char *)rootdir, rootdirsize) != rootdirsize)
+ err(1, "root dir write()");
+
+ (void)close(fd);
+
+ return 0;
+}
+
diff --git a/usr.sbin/mount_portalfs/Makefile b/usr.sbin/mount_portalfs/Makefile
new file mode 100644
index 0000000..fd0e416
--- /dev/null
+++ b/usr.sbin/mount_portalfs/Makefile
@@ -0,0 +1,14 @@
+# From: @(#)Makefile 8.3 (Berkeley) 3/27/94
+# $Id: Makefile,v 1.6 1997/02/22 14:32:52 peter Exp $
+
+PROG= mount_portal
+SRCS= mount_portal.c activate.c conf.c getmntopts.c pt_conf.c \
+ pt_exec.c pt_file.c pt_tcp.c
+MAN8= mount_portal.8
+
+MOUNT= ${.CURDIR}/../mount
+CFLAGS+= -D_NEW_VFSCONF
+CFLAGS+= -I${.CURDIR}/../../sys -I${MOUNT}
+.PATH: ${MOUNT}
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/mount_portalfs/activate.c b/usr.sbin/mount_portalfs/activate.c
new file mode 100644
index 0000000..f14a4a3
--- /dev/null
+++ b/usr.sbin/mount_portalfs/activate.c
@@ -0,0 +1,215 @@
+/*
+ * 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
+ *
+ * $Id: activate.c,v 1.3 1997/02/22 14:32:53 peter Exp $
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <signal.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/syslog.h>
+#include <sys/uio.h>
+
+#include "portald.h"
+
+/*
+ * Scan the providers list and call the
+ * appropriate function.
+ */
+static int activate_argv(pcr, key, v, so, fdp)
+struct portal_cred *pcr;
+char *key;
+char **v;
+int so;
+int *fdp;
+{
+ provider *pr;
+
+ for (pr = providers; pr->pr_match; pr++)
+ if (strcmp(v[0], pr->pr_match) == 0)
+ return ((*pr->pr_func)(pcr, key, v, so, fdp));
+
+ return (ENOENT);
+}
+
+static int get_request(so, pcr, key, klen)
+int so;
+struct portal_cred *pcr;
+char *key;
+int klen;
+{
+ struct iovec iov[2];
+ struct msghdr msg;
+ int n;
+
+ iov[0].iov_base = (caddr_t) pcr;
+ iov[0].iov_len = sizeof(*pcr);
+ iov[1].iov_base = key;
+ iov[1].iov_len = klen;
+
+ memset(&msg, 0, sizeof(msg));
+ msg.msg_iov = iov;
+ msg.msg_iovlen = 2;
+
+ n = recvmsg(so, &msg, 0);
+ if (n < 0)
+ return (errno);
+
+ if (n <= sizeof(*pcr))
+ return (EINVAL);
+
+ n -= sizeof(*pcr);
+ key[n] = '\0';
+
+ return (0);
+}
+
+static void send_reply(so, fd, error)
+int so;
+int fd;
+int error;
+{
+ int n;
+ struct iovec iov;
+ struct msghdr msg;
+ struct {
+ struct cmsghdr cmsg;
+ int fd;
+ } ctl;
+
+ /*
+ * Line up error code. Don't worry about byte ordering
+ * because we must be sending to the local machine.
+ */
+ iov.iov_base = (caddr_t) &error;
+ iov.iov_len = sizeof(error);
+
+ /*
+ * Build a msghdr
+ */
+ memset(&msg, 0, sizeof(msg));
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+
+ /*
+ * If there is a file descriptor to send then
+ * construct a suitable rights control message.
+ */
+ if (fd >= 0) {
+ ctl.fd = fd;
+ ctl.cmsg.cmsg_len = sizeof(ctl);
+ ctl.cmsg.cmsg_level = SOL_SOCKET;
+ ctl.cmsg.cmsg_type = SCM_RIGHTS;
+ msg.msg_control = (caddr_t) &ctl;
+ msg.msg_controllen = ctl.cmsg.cmsg_len;
+ }
+
+ /*
+ * Send to kernel...
+ */
+ if ((n = sendmsg(so, &msg, MSG_EOR)) < 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..b7f3e57
--- /dev/null
+++ b/usr.sbin/mount_portalfs/conf.c
@@ -0,0 +1,337 @@
+/*
+ * 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
+ *
+ * $Id$
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <limits.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:%s: 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..5c1f177
--- /dev/null
+++ b/usr.sbin/mount_portalfs/mount_portalfs.8
@@ -0,0 +1,137 @@
+.\"
+.\" 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
+.\"
+.\"
+.Dd March 27, 1994
+.Dt MOUNT_PORTAL 8
+.Os BSD 4.4
+.Sh NAME
+.Nm mount_portal
+.Nd mount the portal daemon
+.Sh SYNOPSIS
+.Nm mount_portal
+.Op Fl o Ar options
+.Ar /etc/portal.conf
+.Ar mount_point
+.Sh DESCRIPTION
+The
+.Nm mount_portal
+command attaches an instance of the portal daemon
+to the global filesystem namespace.
+The conventional mount point is
+.Pa /p .
+.PA /dev .
+This command is normally executed by
+.Xr mount 8
+at boot time.
+.Pp
+The options are as follows:
+.Bl -tag -width indent
+.It Fl o
+Options are specified with a
+.Fl o
+flag followed by a comma separated string of options.
+See the
+.Xr mount 8
+man page for possible options and their meanings.
+.El
+.Pp
+The portal daemon provides an
+.Em open
+service.
+Objects opened under the portal mount point are
+dynamically created by the portal daemon according
+to rules specified in the named configuration file.
+Using this mechanism allows descriptors such as sockets
+to be made available in the filesystem namespace.
+.Pp
+The portal daemon works by being passed the full pathname
+of the object being opened.
+The daemon creates an appropriate descriptor according
+to the rules in the configuration file, and then passes the descriptor back
+to the calling process as the result of the open system call.
+.Sh NAMESPACE
+By convention, the portal daemon divides the namespace into sub-namespaces,
+each of which handles objects of a particular type.
+.Pp
+Currently, two sub-namespaces are implemented:
+.Pa tcp
+and
+.Pa fs .
+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
+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 mount_portal
+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..4f489ed
--- /dev/null
+++ b/usr.sbin/mount_portalfs/mount_portalfs.c
@@ -0,0 +1,292 @@
+/*
+ * 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
+/*
+static char sccsid[] = "@(#)mount_portal.c 8.6 (Berkeley) 4/26/95";
+*/
+static const char rcsid[] =
+ "$Id: mount_portal.c,v 1.11 1997/03/24 05:53:12 imp Exp $";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/wait.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/syslog.h>
+#include <sys/mount.h>
+
+#include <err.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <unistd.h>
+
+#include "mntopts.h"
+#include "pathnames.h"
+#include "portald.h"
+
+struct mntopt mopts[] = {
+ MOPT_STDOPTS,
+ { NULL }
+};
+
+static void usage __P((void)) __dead2;
+
+static sig_atomic_t readcf; /* Set when SIGHUP received */
+
+static void sighup(sig)
+int sig;
+{
+ readcf ++;
+}
+
+static void sigchld(sig)
+int sig;
+{
+ pid_t pid;
+
+ while ((pid = waitpid((pid_t) -1, (int *) 0, WNOHANG)) > 0)
+ ;
+ /* wrtp - waitpid _doesn't_ return 0 when no children! */
+#ifdef notdef
+ if (pid < 0 && errno != ECHILD)
+ syslog(LOG_WARNING, "waitpid: %s", strerror(errno));
+#endif
+}
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ struct portal_args args;
+ struct sockaddr_un un;
+ char *conf;
+ char *mountpt;
+ int mntflags = 0;
+ char tag[32];
+ struct vfsconf vfc;
+ mode_t um;
+
+ qelem q;
+ int rc;
+ int so;
+ int error = 0;
+
+ /*
+ * Crack command line args
+ */
+ int ch;
+
+ while ((ch = getopt(argc, argv, "o:")) != -1) {
+ switch (ch) {
+ case 'o':
+ getmntopts(optarg, mopts, &mntflags, 0);
+ break;
+ default:
+ error = 1;
+ break;
+ }
+ }
+
+ if (optind != (argc - 2))
+ error = 1;
+
+ if (error)
+ usage();
+
+ /*
+ * Get config file and mount point
+ */
+ conf = argv[optind];
+ mountpt = argv[optind+1];
+
+ /*
+ * Construct the listening socket
+ */
+ un.sun_family = AF_UNIX;
+ if (sizeof(_PATH_TMPPORTAL) >= sizeof(un.sun_path)) {
+ errx(EX_SOFTWARE, "portal socket name too long");
+ }
+ strcpy(un.sun_path, _PATH_TMPPORTAL);
+ mktemp(un.sun_path);
+ un.sun_len = strlen(un.sun_path);
+
+ so = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (so < 0) {
+ err(EX_OSERR, "socket");
+ }
+ um = umask(077);
+ (void) unlink(un.sun_path);
+ if (bind(so, (struct sockaddr *) &un, sizeof(un)) < 0)
+ err(1, NULL);
+
+ (void) unlink(un.sun_path);
+ (void) umask(um);
+
+ (void) listen(so, 5);
+
+ args.pa_socket = so;
+ sprintf(tag, "portal:%d", getpid());
+ args.pa_config = tag;
+
+ error = getvfsbyname("portal", &vfc);
+ if (error && vfsisloadable("portal")) {
+ if (vfsload("portal"))
+ err(EX_OSERR, "vfsload(portal)");
+ endvfsent();
+ error = getvfsbyname("portal", &vfc);
+ }
+ if (error)
+ errx(EX_OSERR, "portal filesystem is not available");
+
+ rc = mount(vfc.vfc_name, mountpt, mntflags, &args);
+ if (rc < 0)
+ err(1, NULL);
+
+ /*
+ * Everything is ready to go - now is a good time to fork
+ */
+ daemon(0, 0);
+
+ /*
+ * Start logging (and change name)
+ */
+ openlog("portald", LOG_CONS|LOG_PID, LOG_DAEMON);
+
+ q.q_forw = q.q_back = &q;
+ readcf = 1;
+
+ signal(SIGCHLD, sigchld);
+ signal(SIGHUP, sighup);
+
+ /*
+ * Just loop waiting for new connections and activating them
+ */
+ for (;;) {
+ struct sockaddr_un un2;
+ int len2 = sizeof(un2);
+ int so2;
+ pid_t pid;
+ fd_set fdset;
+ int rc;
+
+ /*
+ * Check whether we need to re-read the configuration file
+ */
+ if (readcf) {
+#ifdef DEBUG
+ printf ("re-reading configuration file\n");
+#endif
+ readcf = 0;
+ conf_read(&q, conf);
+ continue;
+ }
+
+ /*
+ * Accept a new connection
+ * Will get EINTR if a signal has arrived, so just
+ * ignore that error code
+ */
+ FD_ZERO(&fdset);
+ FD_SET(so, &fdset);
+ rc = select(so+1, &fdset, (fd_set *) 0, (fd_set *) 0, (struct timeval *) 0);
+ if (rc < 0) {
+ if (errno == EINTR)
+ continue;
+ syslog(LOG_ERR, "select: %s", strerror(errno));
+ exit(EX_OSERR);
+ }
+ if (rc == 0)
+ break;
+ so2 = accept(so, (struct sockaddr *) &un2, &len2);
+ if (so2 < 0) {
+ /*
+ * The unmount function does a shutdown on the socket
+ * which will generated ECONNABORTED on the accept.
+ */
+ if (errno == ECONNABORTED)
+ break;
+ if (errno != EINTR) {
+ syslog(LOG_ERR, "accept: %s", strerror(errno));
+ exit(EX_OSERR);
+ }
+ continue;
+ }
+
+ /*
+ * Now fork a new child to deal with the connection
+ */
+ eagain:;
+ switch (pid = fork()) {
+ case -1:
+ if (errno == EAGAIN) {
+ sleep(1);
+ goto eagain;
+ }
+ syslog(LOG_ERR, "fork: %s", strerror(errno));
+ break;
+ case 0:
+ (void) close(so);
+ activate(&q, so2);
+ exit(0);
+ default:
+ (void) close(so2);
+ break;
+ }
+ }
+ syslog(LOG_INFO, "%s unmounted", mountpt);
+ exit(0);
+}
+
+static void
+usage()
+{
+ (void)fprintf(stderr,
+ "usage: mount_portal [-o options] config mount-point\n");
+ exit(EX_USAGE);
+}
diff --git a/usr.sbin/mount_portalfs/pathnames.h b/usr.sbin/mount_portalfs/pathnames.h
new file mode 100644
index 0000000..3203b00
--- /dev/null
+++ b/usr.sbin/mount_portalfs/pathnames.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ * All rights reserved.
+ *
+ * This code is derived from software donated to Berkeley by
+ * Jan-Simon Pendry.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)pathnames.h 8.1 (Berkeley) 6/5/93
+ *
+ * $Id$
+ */
+
+#include <paths.h>
+
+#define _PATH_TMPPORTAL "/tmp/portalXXXXXX" /* Scratch socket name */
diff --git a/usr.sbin/mount_portalfs/portal.conf b/usr.sbin/mount_portalfs/portal.conf
new file mode 100644
index 0000000..3a4ddf0a
--- /dev/null
+++ b/usr.sbin/mount_portalfs/portal.conf
@@ -0,0 +1,7 @@
+# @(#)portal.conf 8.1 (Berkeley) 6/5/93
+# $Id$
+tcplisten/ tcplisten tcplisten/
+tcp/ tcp tcp/
+fs/ file fs/
+pipe/ pipe
+foo/ exec ./bar bar baz
diff --git a/usr.sbin/mount_portalfs/portald.h b/usr.sbin/mount_portalfs/portald.h
new file mode 100644
index 0000000..23fce24
--- /dev/null
+++ b/usr.sbin/mount_portalfs/portald.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ * All rights reserved.
+ *
+ * This code is derived from software donated to Berkeley by
+ * Jan-Simon Pendry.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)portald.h 8.1 (Berkeley) 6/5/93
+ *
+ * $Id$
+ */
+
+#include <sys/cdefs.h>
+#include <miscfs/portal/portal.h>
+
+/*
+ * Meta-chars in an RE. Paths in the config file containing
+ * any of these characters will be matched using regexec, other
+ * paths will be prefix-matched.
+ */
+#define RE_CHARS ".|()[]*+?\\^$"
+
+typedef struct qelem qelem;
+
+struct qelem {
+ qelem *q_forw;
+ qelem *q_back;
+};
+
+typedef struct provider provider;
+struct provider {
+ char *pr_match;
+ int (*pr_func) __P((struct portal_cred *,
+ char *key, char **v, int so, int *fdp));
+};
+extern provider providers[];
+
+/*
+ * Portal providers
+ */
+extern int portal_exec __P((struct portal_cred *,
+ char *key, char **v, int so, int *fdp));
+extern int portal_file __P((struct portal_cred *,
+ char *key, char **v, int so, int *fdp));
+extern int portal_tcp __P((struct portal_cred *,
+ char *key, char **v, int so, int *fdp));
+
+/*
+ * Global functions
+ */
+extern void activate __P((qelem *q, int so));
+extern char **conf_match __P((qelem *q, char *key));
+extern void conf_read __P((qelem *q, char *conf));
diff --git a/usr.sbin/mount_portalfs/pt_conf.c b/usr.sbin/mount_portalfs/pt_conf.c
new file mode 100644
index 0000000..ae588ad
--- /dev/null
+++ b/usr.sbin/mount_portalfs/pt_conf.c
@@ -0,0 +1,51 @@
+/*
+ * 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
+ *
+ * $Id$
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include "portald.h"
+
+provider providers[] = {
+ { "exec", portal_exec },
+ { "file", portal_file },
+ { "tcp", portal_tcp },
+ { 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..6ee2bf5
--- /dev/null
+++ b/usr.sbin/mount_portalfs/pt_exec.c
@@ -0,0 +1,61 @@
+/*
+ * 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
+ *
+ * $Id$
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/syslog.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..9d71bc7
--- /dev/null
+++ b/usr.sbin/mount_portalfs/pt_file.c
@@ -0,0 +1,108 @@
+/*
+ * 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
+ *
+ * $Id: pt_file.c,v 1.5 1997/02/22 14:32:56 peter Exp $
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.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..ea7ac31
--- /dev/null
+++ b/usr.sbin/mount_portalfs/pt_tcp.c
@@ -0,0 +1,165 @@
+/*
+ * Copyright (c) 1992, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ * All rights reserved.
+ *
+ * This code is derived from software donated to Berkeley by
+ * Jan-Simon Pendry.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)pt_tcp.c 8.5 (Berkeley) 4/28/95
+ *
+ * $Id: pt_tcp.c,v 1.4 1997/02/22 14:32:56 peter 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 tcp/host/port[/"priv"]
+ * Create a TCP socket connected to the
+ * requested host and port.
+ * Some trailing suffix values have special meanings.
+ * An unrecognised 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, 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/mountd/Makefile b/usr.sbin/mountd/Makefile
new file mode 100644
index 0000000..c05730d
--- /dev/null
+++ b/usr.sbin/mountd/Makefile
@@ -0,0 +1,10 @@
+# From: @(#)Makefile 8.3 (Berkeley) 1/25/94
+# $Id: Makefile,v 1.6 1997/02/22 14:33:01 peter Exp $
+
+PROG= mountd
+CFLAGS+= -D_NEW_VFSCONF
+CFLAGS+=-DNFS -DMFS -DCD9660 -DMSDOSFS
+MAN5= exports.5 netgroup.5
+MAN8= mountd.8
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/mountd/exports.5 b/usr.sbin/mountd/exports.5
new file mode 100644
index 0000000..2838fdc
--- /dev/null
+++ b/usr.sbin/mountd/exports.5
@@ -0,0 +1,289 @@
+.\" Copyright (c) 1989, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)exports.5 8.3 (Berkeley) 3/29/95
+.\" $Id: exports.5,v 1.5 1997/03/12 15:08:06 mpp Exp $
+.\"
+.Dd March 29, 1995
+.Dt EXPORTS 5
+.Os
+.Sh NAME
+.Nm exports
+.Nd define remote mount points for
+.Tn NFS
+mount requests
+.Sh SYNOPSIS
+.Nm exports
+.Sh DESCRIPTION
+The
+.Nm exports
+file specifies remote mount points for the
+.Tn NFS
+mount protocol per the
+.Tn NFS
+server specification; see
+.%T "Network File System Protocol Specification \\*(tNRFC\\*(sP 1094, Appendix A"
+and
+.%T "NFS: Network File System Version 3 Specification, Appendix I" .
+.Pp
+Each line in the file
+(other than comment lines that begin with a #)
+specifies the mount point(s) and export flags within one local server
+filesystem for one or more hosts.
+A host may be specified only once for each local filesystem on the
+server and there may be only one default entry for each server
+filesystem that applies to all other hosts.
+The latter exports the filesystem to the ``world'' and should
+be used only when the filesystem contains public information.
+.Pp
+In a mount entry,
+the first field(s) specify the directory path(s) within a server filesystem
+that can be mounted on by the corresponding client(s).
+There are two forms of this specification.
+The first is to list all mount points as absolute
+directory paths separated by whitespace.
+The second is to specify the pathname of the root of the filesystem
+followed by the
+.Fl alldirs
+flag;
+this form allows the host(s) to mount at any point within the filesystem,
+including regular files if the
+.Fl r
+option is used on mountd.
+The pathnames must not have any symbolic links in them and should not have
+any "." or ".." components.
+Mount points for a filesystem may appear on multiple lines each with
+different sets of hosts and export options.
+.Pp
+The second component of a line specifies how the filesystem is to be
+exported to the host set.
+The option flags specify whether the filesystem
+is exported read-only or read-write and how the client uid is mapped to
+user credentials on the server.
+.Pp
+Export options are specified as follows:
+.Pp
+.Sm off
+.Fl maproot No = Sy user
+.Sm on
+The credential of the specified user is used for remote access by root.
+The credential includes all the groups to which the user is a member
+on the local machine (see
+.Xr id 1 ).
+The user may be specified by name or number.
+.Pp
+.Sm off
+.Fl maproot No = Sy user:group1:group2:...
+.Sm on
+The colon separated list is used to specify the precise credential
+to be used for remote access by root.
+The elements of the list may be either names or numbers.
+Note that user: should be used to distinguish a credential containing
+no groups from a complete credential for that user.
+.Pp
+.Sm off
+.Fl mapall No = Sy user
+.Sm on
+or
+.Sm off
+.Fl mapall No = Sy user:group1:group2:...
+.Sm on
+specifies a mapping for all client uids (including root)
+using the same semantics as
+.Fl maproot .
+.Pp
+The option
+.Fl r
+is a synonym for
+.Fl maproot
+in an effort to be backward compatible with older export file formats.
+.Pp
+In the absence of
+.Fl maproot
+and
+.Fl mapall
+options, remote accesses by root will result in using a credential of -2:-2.
+All other users will be mapped to their remote credential.
+If a
+.Fl maproot
+option is given,
+remote access by root will be mapped to that credential instead of -2:-2.
+If a
+.Fl mapall
+option is given,
+all users (including root) will be mapped to that credential in
+place of their own.
+.Pp
+The
+.Fl kerb
+option specifies that the Kerberos authentication server should be
+used to authenticate and map client credentials.
+This option requires that the kernel be built with the NFSKERB option.
+.Pp
+The
+.Fl ro
+option specifies that the filesystem should be exported read-only
+(default read/write).
+The option
+.Fl o
+is a synonym for
+.Fl ro
+in an effort to be backward compatible with older export file formats.
+.Pp
+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 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 off
+option can be used to specify a file whose handle will be returned if
+a directory is looked up using the public filehandle (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.
+.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..487dacb
--- /dev/null
+++ b/usr.sbin/mountd/mountd.8
@@ -0,0 +1,143 @@
+.\" Copyright (c) 1989, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)mountd.8 8.4 (Berkeley) 4/28/95
+.\" $Id: mountd.8,v 1.9 1997/04/09 20:25:43 guido Exp $
+.\"
+.Dd April 28, 1995
+.Dt MOUNTD 8
+.Os
+.Sh NAME
+.Nm mountd
+.Nd service remote
+.Tn NFS
+mount requests
+.Sh SYNOPSIS
+.Nm /sbin/mountd
+.Op Fl 2nr
+.Op Ar exportsfile
+.Sh DESCRIPTION
+.Xr Mountd
+is the server for
+.Tn NFS
+mount requests from other client machines.
+.Xr Mountd
+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
+Options and operands available for
+.Nm mountd :
+.Bl -tag -width Ds
+.It Fl 2
+The
+.Fl 2
+option allows the administrator to force clients to use only the
+version 2 NFS protocol to mount filesystems from this server.
+.It Fl n
+The
+.Fl n
+option allows non-root mount requests to be served.
+This should only be specified if there are clients such as PC's,
+that require it.
+It will automatically clear the vfs.nfs.nfs_privport sysctl flag, which
+controls if the kernel will accept nfs requests form reserved ports only.
+.It Fl r
+The
+.Fl r
+option allows mount RPCs requests for regular files to be served.
+Although this seems to violate the mount protocol specification,
+some diskless workstations do mount requests for
+their swapfiles and expect them to be regular files.
+Since a regular file cannot be specified in
+.Pa /etc/exports ,
+the entire file system in which the swapfiles resides
+will have to be exported with the
+.Fl alldirs
+flag.
+.It Ar exportsfile
+The
+.Ar exportsfile
+argument specifies an alternate location
+for the exports file.
+.El
+.Pp
+When mountd is started,
+it loads the export host addresses and options into the kernel
+using the mount(2) system call.
+After changing the exports file,
+a hangup signal should be sent to the mountd 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 mountd logged any parsing
+errors in the exports file.
+.Pp
+If
+.Nm mountd
+detects that the running kernel does not include
+.Tn NFS
+support, it will attempt to load a loadable kernel module containing
+.Tn NFS
+code, using
+.Xr modload 8
+by way of
+.Xr vfsload 3 .
+If this fails, or no
+.Tn NFS
+LKM was available,
+.Nm mountd
+exits with an error.
+.Sh FILES
+.Bl -tag -width /var/run/mountd.pid -compact
+.It Pa /etc/exports
+the list of exported filesystems
+.It Pa /var/run/mountd.pid
+the pid of the currently running mountd
+.It Pa /var/db/mountdtab
+the current list of remote mounted filesystems
+.El
+.Sh SEE ALSO
+.Xr nfsstat 1 ,
+.Xr exports 5 ,
+.Xr modload 8 ,
+.Xr nfsd 8 ,
+.Xr portmap 8 ,
+.Xr showmount 8
+.Sh HISTORY
+The
+.Nm mountd
+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..b82a0f3
--- /dev/null
+++ b/usr.sbin/mountd/mountd.c
@@ -0,0 +1,2159 @@
+/*
+ * 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 char copyright[] =
+"@(#) Copyright (c) 1989, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /*not lint*/
+
+#ifndef lint
+/*static char sccsid[] = "@(#)mountd.c 8.15 (Berkeley) 5/1/95"; */
+static const char rcsid[] =
+ "$Id: mountd.c,v 1.23 1997/08/29 19:22:28 guido Exp $";
+#endif /*not lint*/
+
+#include <sys/param.h>
+#include <sys/file.h>
+#include <sys/ioctl.h>
+#include <sys/mount.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/syslog.h>
+#include <sys/ucred.h>
+#include <sys/sysctl.h>
+
+#include <rpc/rpc.h>
+#include <rpc/pmap_clnt.h>
+#include <rpc/pmap_prot.h>
+#ifdef ISO
+#include <netiso/iso.h>
+#endif
+#include <nfs/rpcv2.h>
+#include <nfs/nfsproto.h>
+#include <nfs/nfs.h>
+#include <ufs/ufs/ufsmount.h>
+#include <msdosfs/msdosfsmount.h>
+#include <isofs/cd9660/cd9660_mount.h> /* XXX need isofs in include */
+
+#include <arpa/inet.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <grp.h>
+#include <netdb.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include "pathnames.h"
+
+#ifdef DEBUG
+#include <stdarg.h>
+#endif
+
+/*
+ * Structures for keeping the mount list and export list
+ */
+struct mountlist {
+ struct mountlist *ml_next;
+ char ml_host[RPCMNT_NAMELEN+1];
+ char ml_dirp[RPCMNT_PATHLEN+1];
+};
+
+struct dirlist {
+ struct dirlist *dp_left;
+ struct dirlist *dp_right;
+ int dp_flag;
+ struct hostlist *dp_hosts; /* List of hosts this dir exported to */
+ char dp_dirp[1]; /* Actually malloc'd to size of dir */
+};
+/* dp_flag bits */
+#define DP_DEFSET 0x1
+#define DP_HOSTSET 0x2
+#define DP_KERB 0x4
+
+struct exportlist {
+ struct exportlist *ex_next;
+ struct dirlist *ex_dirl;
+ struct dirlist *ex_defdir;
+ int ex_flag;
+ fsid_t ex_fs;
+ char *ex_fsdir;
+ char *ex_indexfile;
+};
+/* ex_flag bits */
+#define EX_LINKED 0x1
+
+struct netmsk {
+ u_long nt_net;
+ u_long nt_mask;
+ char *nt_name;
+};
+
+union grouptypes {
+ struct hostent *gt_hostent;
+ struct netmsk gt_net;
+#ifdef ISO
+ struct sockaddr_iso *gt_isoaddr;
+#endif
+};
+
+struct grouplist {
+ int gr_type;
+ union grouptypes gr_ptr;
+ struct grouplist *gr_next;
+};
+/* Group types */
+#define GT_NULL 0x0
+#define GT_HOST 0x1
+#define GT_NET 0x2
+#define GT_ISO 0x4
+#define GT_IGNORE 0x5
+
+struct hostlist {
+ int ht_flag; /* Uses DP_xx bits */
+ struct grouplist *ht_grp;
+ struct hostlist *ht_next;
+};
+
+struct fhreturn {
+ int fhr_flag;
+ int fhr_vers;
+ nfsfh_t fhr_fh;
+};
+
+/* Global defs */
+char *add_expdir __P((struct dirlist **, char *, int));
+void add_dlist __P((struct dirlist **, struct dirlist *,
+ struct grouplist *, int));
+void add_mlist __P((char *, char *));
+int check_dirpath __P((char *));
+int check_options __P((struct dirlist *));
+int chk_host __P((struct dirlist *, u_long, int *, int *));
+void del_mlist __P((char *, char *));
+struct dirlist *dirp_search __P((struct dirlist *, char *));
+int do_mount __P((struct exportlist *, struct grouplist *, int,
+ struct ucred *, char *, int, struct statfs *));
+int do_opt __P((char **, char **, struct exportlist *, struct grouplist *,
+ int *, int *, struct ucred *));
+struct exportlist *ex_search __P((fsid_t *));
+struct exportlist *get_exp __P((void));
+void free_dir __P((struct dirlist *));
+void free_exp __P((struct exportlist *));
+void free_grp __P((struct grouplist *));
+void free_host __P((struct hostlist *));
+void get_exportlist __P((void));
+int get_host __P((char *, struct grouplist *, struct grouplist *));
+int get_num __P((char *));
+struct hostlist *get_ht __P((void));
+int get_line __P((void));
+void get_mountlist __P((void));
+int get_net __P((char *, struct netmsk *, int));
+void getexp_err __P((struct exportlist *, struct grouplist *));
+struct grouplist *get_grp __P((void));
+void hang_dirp __P((struct dirlist *, struct grouplist *,
+ struct exportlist *, int));
+void mntsrv __P((struct svc_req *, SVCXPRT *));
+void nextfield __P((char **, char **));
+void out_of_mem __P((void));
+void parsecred __P((char *, struct ucred *));
+int put_exlist __P((struct dirlist *, XDR *, struct dirlist *, int *));
+int scan_tree __P((struct dirlist *, u_long));
+void send_umntall __P((void));
+int umntall_each __P((caddr_t, struct sockaddr_in *));
+int xdr_dir __P((XDR *, char *));
+int xdr_explist __P((XDR *, caddr_t));
+int xdr_fhs __P((XDR *, caddr_t));
+int xdr_mlist __P((XDR *, caddr_t));
+
+/* C library */
+int getnetgrent();
+void endnetgrent();
+void setnetgrent();
+
+#ifdef ISO
+struct iso_addr *iso_addr();
+#endif
+
+struct exportlist *exphead;
+struct mountlist *mlhead;
+struct grouplist *grphead;
+char exname[MAXPATHLEN];
+struct ucred def_anon = {
+ 1,
+ (uid_t) -2,
+ 1,
+ { (gid_t) -2 }
+};
+int force_v2 = 0;
+int resvport_only = 1;
+int dir_only = 1;
+int opt_flags;
+/* Bits for above */
+#define OP_MAPROOT 0x01
+#define OP_MAPALL 0x02
+#define OP_KERB 0x04
+#define OP_MASK 0x08
+#define OP_NET 0x10
+#define OP_ISO 0x20
+#define OP_ALLDIRS 0x40
+
+#ifdef DEBUG
+int debug = 1;
+void SYSLOG __P((int, const char *, ...));
+#define syslog SYSLOG
+#else
+int debug = 0;
+#endif
+
+/*
+ * Mountd server for NFS mount protocol as described in:
+ * NFS: Network File System Protocol Specification, RFC1094, Appendix A
+ * The optional arguments are the exports file name
+ * default: _PATH_EXPORTS
+ * and "-n" to allow nonroot mount.
+ */
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ SVCXPRT *udptransp, *tcptransp;
+ int c;
+ int mib[3];
+#ifdef __FreeBSD__
+ struct vfsconf vfc;
+ int error;
+
+ error = getvfsbyname("nfs", &vfc);
+ if (error && vfsisloadable("nfs")) {
+ if(vfsload("nfs"))
+ err(1, "vfsload(nfs)");
+ endvfsent(); /* flush cache */
+ error = getvfsbyname("nfs", &vfc);
+ }
+ if (error)
+ errx(1, "NFS support is not available in the running kernel");
+#endif /* __FreeBSD__ */
+
+ while ((c = getopt(argc, argv, "2dnr")) != -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;
+ default:
+ fprintf(stderr, "Usage: mountd [-d] [-r] [-n] [export_file]\n");
+ exit(1);
+ };
+ 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)
+ fprintf(stderr,"Getting export list.\n");
+ get_exportlist();
+ if (debug)
+ fprintf(stderr,"Getting mount list.\n");
+ get_mountlist();
+ if (debug)
+ fprintf(stderr,"Here we go.\n");
+ if (debug == 0) {
+ daemon(0, 0);
+ signal(SIGINT, SIG_IGN);
+ signal(SIGQUIT, SIG_IGN);
+ }
+ signal(SIGHUP, (void (*) __P((int))) get_exportlist);
+ signal(SIGTERM, (void (*) __P((int))) send_umntall);
+ { FILE *pidfile = fopen(_PATH_MOUNTDPID, "w");
+ if (pidfile != NULL) {
+ fprintf(pidfile, "%d\n", getpid());
+ fclose(pidfile);
+ }
+ }
+
+ if (!resvport_only) {
+ mib[0] = CTL_VFS;
+ mib[1] = MOUNT_NFS;
+ mib[2] = NFS_NFSPRIVPORT;
+ if (sysctl(mib, 3, NULL, NULL, &resvport_only,
+ sizeof(resvport_only)) != 0 && errno != ENOENT) {
+ syslog(LOG_ERR, "sysctl: %m");
+ exit(1);
+ }
+ }
+
+ if ((udptransp = svcudp_create(RPC_ANYSOCK)) == NULL ||
+ (tcptransp = svctcp_create(RPC_ANYSOCK, 0, 0)) == NULL) {
+ syslog(LOG_ERR, "Can't create socket");
+ exit(1);
+ }
+ pmap_unset(RPCPROG_MNT, 1);
+ pmap_unset(RPCPROG_MNT, 3);
+ if (!force_v2)
+ if (!svc_register(udptransp, RPCPROG_MNT, 3, mntsrv, IPPROTO_UDP) ||
+ !svc_register(tcptransp, RPCPROG_MNT, 3, mntsrv, IPPROTO_TCP)) {
+ syslog(LOG_ERR, "Can't register mount");
+ exit(1);
+ }
+ if (!svc_register(udptransp, RPCPROG_MNT, 1, mntsrv, IPPROTO_UDP) ||
+ !svc_register(tcptransp, RPCPROG_MNT, 1, mntsrv, IPPROTO_TCP)) {
+ syslog(LOG_ERR, "Can't register mount");
+ exit(1);
+ }
+ svc_run();
+ syslog(LOG_ERR, "Mountd died");
+ exit(1);
+}
+
+/*
+ * The mount rpc service
+ */
+void
+mntsrv(rqstp, transp)
+ struct svc_req *rqstp;
+ SVCXPRT *transp;
+{
+ struct exportlist *ep;
+ struct dirlist *dp;
+ struct fhreturn fhr;
+ struct stat stb;
+ struct statfs fsb;
+ struct hostent *hp;
+ u_long saddr;
+ u_short sport;
+ char rpcpath[RPCMNT_PATHLEN + 1], dirpath[MAXPATHLEN];
+ int bad = 0, defset, hostset;
+ sigset_t sighup_mask;
+
+ sigemptyset(&sighup_mask);
+ sigaddset(&sighup_mask, SIGHUP);
+ saddr = transp->xp_raddr.sin_addr.s_addr;
+ sport = ntohs(transp->xp_raddr.sin_port);
+ hp = (struct hostent *)NULL;
+ switch (rqstp->rq_proc) {
+ case NULLPROC:
+ if (!svc_sendreply(transp, xdr_void, (caddr_t)NULL))
+ syslog(LOG_ERR, "Can't send reply");
+ return;
+ case RPCMNT_MOUNT:
+ if (sport >= IPPORT_RESERVED && resvport_only) {
+ svcerr_weakauth(transp);
+ return;
+ }
+ if (!svc_getargs(transp, xdr_dir, rpcpath)) {
+ svcerr_decode(transp);
+ return;
+ }
+
+ /*
+ * Get the real pathname and make sure it is a directory
+ * or a regular file if the -r option was specified
+ * and it exists.
+ */
+ if (realpath(rpcpath, dirpath) == 0 ||
+ stat(dirpath, &stb) < 0 ||
+ (!S_ISDIR(stb.st_mode) &&
+ (dir_only || !S_ISREG(stb.st_mode))) ||
+ statfs(dirpath, &fsb) < 0) {
+ chdir("/"); /* Just in case realpath doesn't */
+ if (debug)
+ fprintf(stderr, "stat failed on %s\n", dirpath);
+ bad = ENOENT; /* We will send error reply later */
+ }
+
+ /* Check in the exports list */
+ sigprocmask(SIG_BLOCK, &sighup_mask, NULL);
+ ep = ex_search(&fsb.f_fsid);
+ hostset = defset = 0;
+ if (ep && (chk_host(ep->ex_defdir, saddr, &defset, &hostset) ||
+ ((dp = dirp_search(ep->ex_dirl, dirpath)) &&
+ chk_host(dp, saddr, &defset, &hostset)) ||
+ (defset && scan_tree(ep->ex_defdir, saddr) == 0 &&
+ scan_tree(ep->ex_dirl, saddr) == 0))) {
+ if (bad) {
+ if (!svc_sendreply(transp, xdr_long,
+ (caddr_t)&bad))
+ syslog(LOG_ERR, "Can't send reply");
+ sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
+ return;
+ }
+ if (hostset & DP_HOSTSET)
+ fhr.fhr_flag = hostset;
+ else
+ fhr.fhr_flag = defset;
+ fhr.fhr_vers = rqstp->rq_vers;
+ /* Get the file handle */
+ memset(&fhr.fhr_fh, 0, sizeof(nfsfh_t));
+ if (getfh(dirpath, (fhandle_t *)&fhr.fhr_fh) < 0) {
+ bad = errno;
+ syslog(LOG_ERR, "Can't get fh for %s", dirpath);
+ if (!svc_sendreply(transp, xdr_long,
+ (caddr_t)&bad))
+ syslog(LOG_ERR, "Can't send reply");
+ sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
+ return;
+ }
+ if (!svc_sendreply(transp, xdr_fhs, (caddr_t)&fhr))
+ syslog(LOG_ERR, "Can't send reply");
+ if (hp == NULL)
+ hp = gethostbyaddr((caddr_t)&saddr,
+ sizeof(saddr), AF_INET);
+ if (hp)
+ add_mlist(hp->h_name, dirpath);
+ else
+ add_mlist(inet_ntoa(transp->xp_raddr.sin_addr),
+ dirpath);
+ if (debug)
+ fprintf(stderr,"Mount successfull.\n");
+ } else
+ bad = EACCES;
+
+ 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");
+ return;
+ case RPCMNT_UMOUNT:
+ if (sport >= IPPORT_RESERVED && resvport_only) {
+ svcerr_weakauth(transp);
+ return;
+ }
+ if (!svc_getargs(transp, xdr_dir, dirpath)) {
+ svcerr_decode(transp);
+ return;
+ }
+ if (!svc_sendreply(transp, xdr_void, (caddr_t)NULL))
+ syslog(LOG_ERR, "Can't send reply");
+ hp = gethostbyaddr((caddr_t)&saddr, sizeof(saddr), AF_INET);
+ if (hp)
+ del_mlist(hp->h_name, dirpath);
+ del_mlist(inet_ntoa(transp->xp_raddr.sin_addr), dirpath);
+ return;
+ case RPCMNT_UMNTALL:
+ if (sport >= IPPORT_RESERVED && resvport_only) {
+ svcerr_weakauth(transp);
+ return;
+ }
+ if (!svc_sendreply(transp, xdr_void, (caddr_t)NULL))
+ syslog(LOG_ERR, "Can't send reply");
+ hp = gethostbyaddr((caddr_t)&saddr, sizeof(saddr), AF_INET);
+ if (hp)
+ del_mlist(hp->h_name, (char *)NULL);
+ del_mlist(inet_ntoa(transp->xp_raddr.sin_addr), (char *)NULL);
+ return;
+ case RPCMNT_EXPORT:
+ if (!svc_sendreply(transp, xdr_explist, (caddr_t)NULL))
+ syslog(LOG_ERR, "Can't send reply");
+ return;
+ default:
+ svcerr_noproc(transp);
+ return;
+ }
+}
+
+/*
+ * Xdr conversion for a dirpath string
+ */
+int
+xdr_dir(xdrsp, dirp)
+ XDR *xdrsp;
+ char *dirp;
+{
+ return (xdr_string(xdrsp, &dirp, RPCMNT_PATHLEN));
+}
+
+/*
+ * Xdr routine to generate file handle reply
+ */
+int
+xdr_fhs(xdrsp, cp)
+ XDR *xdrsp;
+ caddr_t cp;
+{
+ register struct fhreturn *fhrp = (struct fhreturn *)cp;
+ u_long ok = 0, len, auth;
+
+ if (!xdr_long(xdrsp, &ok))
+ return (0);
+ switch (fhrp->fhr_vers) {
+ case 1:
+ return (xdr_opaque(xdrsp, (caddr_t)&fhrp->fhr_fh, NFSX_V2FH));
+ case 3:
+ len = NFSX_V3FH;
+ if (!xdr_long(xdrsp, &len))
+ return (0);
+ if (!xdr_opaque(xdrsp, (caddr_t)&fhrp->fhr_fh, len))
+ return (0);
+ if (fhrp->fhr_flag & DP_KERB)
+ auth = RPCAUTH_KERB4;
+ else
+ auth = RPCAUTH_UNIX;
+ len = 1;
+ if (!xdr_long(xdrsp, &len))
+ return (0);
+ return (xdr_long(xdrsp, &auth));
+ };
+ return (0);
+}
+
+int
+xdr_mlist(xdrsp, cp)
+ XDR *xdrsp;
+ caddr_t cp;
+{
+ struct mountlist *mlp;
+ int true = 1;
+ int false = 0;
+ char *strp;
+
+ mlp = mlhead;
+ while (mlp) {
+ if (!xdr_bool(xdrsp, &true))
+ return (0);
+ strp = &mlp->ml_host[0];
+ if (!xdr_string(xdrsp, &strp, RPCMNT_NAMELEN))
+ return (0);
+ strp = &mlp->ml_dirp[0];
+ if (!xdr_string(xdrsp, &strp, RPCMNT_PATHLEN))
+ return (0);
+ mlp = mlp->ml_next;
+ }
+ if (!xdr_bool(xdrsp, &false))
+ return (0);
+ return (1);
+}
+
+/*
+ * Xdr conversion for export list
+ */
+int
+xdr_explist(xdrsp, cp)
+ XDR *xdrsp;
+ caddr_t cp;
+{
+ struct exportlist *ep;
+ int false = 0;
+ int putdef;
+ sigset_t sighup_mask;
+
+ sigemptyset(&sighup_mask);
+ sigaddset(&sighup_mask, SIGHUP);
+ sigprocmask(SIG_BLOCK, &sighup_mask, NULL);
+ ep = exphead;
+ while (ep) {
+ putdef = 0;
+ if (put_exlist(ep->ex_dirl, xdrsp, ep->ex_defdir, &putdef))
+ goto errout;
+ if (ep->ex_defdir && putdef == 0 &&
+ put_exlist(ep->ex_defdir, xdrsp, (struct dirlist *)NULL,
+ &putdef))
+ goto errout;
+ ep = ep->ex_next;
+ }
+ sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
+ if (!xdr_bool(xdrsp, &false))
+ return (0);
+ return (1);
+errout:
+ sigprocmask(SIG_UNBLOCK, &sighup_mask, NULL);
+ return (0);
+}
+
+/*
+ * Called from xdr_explist() to traverse the tree and export the
+ * directory paths.
+ */
+int
+put_exlist(dp, xdrsp, adp, putdefp)
+ struct dirlist *dp;
+ XDR *xdrsp;
+ struct dirlist *adp;
+ int *putdefp;
+{
+ struct grouplist *grp;
+ struct hostlist *hp;
+ int true = 1;
+ int false = 0;
+ int gotalldir = 0;
+ char *strp;
+
+ if (dp) {
+ if (put_exlist(dp->dp_left, xdrsp, adp, putdefp))
+ return (1);
+ if (!xdr_bool(xdrsp, &true))
+ return (1);
+ strp = dp->dp_dirp;
+ if (!xdr_string(xdrsp, &strp, RPCMNT_PATHLEN))
+ return (1);
+ if (adp && !strcmp(dp->dp_dirp, adp->dp_dirp)) {
+ gotalldir = 1;
+ *putdefp = 1;
+ }
+ if ((dp->dp_flag & DP_DEFSET) == 0 &&
+ (gotalldir == 0 || (adp->dp_flag & DP_DEFSET) == 0)) {
+ hp = dp->dp_hosts;
+ while (hp) {
+ grp = hp->ht_grp;
+ if (grp->gr_type == GT_HOST) {
+ if (!xdr_bool(xdrsp, &true))
+ return (1);
+ strp = grp->gr_ptr.gt_hostent->h_name;
+ if (!xdr_string(xdrsp, &strp,
+ RPCMNT_NAMELEN))
+ return (1);
+ } else if (grp->gr_type == GT_NET) {
+ if (!xdr_bool(xdrsp, &true))
+ return (1);
+ strp = grp->gr_ptr.gt_net.nt_name;
+ if (!xdr_string(xdrsp, &strp,
+ RPCMNT_NAMELEN))
+ return (1);
+ }
+ hp = hp->ht_next;
+ if (gotalldir && hp == (struct hostlist *)NULL) {
+ hp = adp->dp_hosts;
+ gotalldir = 0;
+ }
+ }
+ }
+ if (!xdr_bool(xdrsp, &false))
+ return (1);
+ if (put_exlist(dp->dp_right, xdrsp, adp, putdefp))
+ return (1);
+ }
+ return (0);
+}
+
+#define LINESIZ 10240
+char line[LINESIZ];
+FILE *exp_file;
+
+/*
+ * Get the export list
+ */
+void
+get_exportlist()
+{
+ struct exportlist *ep, *ep2;
+ struct grouplist *grp, *tgrp;
+ struct exportlist **epp;
+ struct dirlist *dirhead;
+ struct statfs fsb, *fsp;
+ struct hostent *hpe;
+ struct ucred anon;
+ char *cp, *endcp, *dirp, *hst, *usr, *dom, savedc;
+ int len, has_host, exflags, got_nondir, dirplen, num, i, netgrp;
+
+ /*
+ * First, get rid of the old list
+ */
+ ep = exphead;
+ while (ep) {
+ ep2 = ep;
+ ep = ep->ex_next;
+ free_exp(ep2);
+ }
+ exphead = (struct exportlist *)NULL;
+
+ grp = grphead;
+ while (grp) {
+ tgrp = grp;
+ grp = grp->gr_next;
+ free_grp(tgrp);
+ }
+ grphead = (struct grouplist *)NULL;
+
+ /*
+ * And delete exports that are in the kernel for all local
+ * file systems.
+ * XXX: Should know how to handle all local exportable file systems
+ * instead of just "ufs".
+ */
+ num = getmntinfo(&fsp, MNT_NOWAIT);
+ for (i = 0; i < num; i++) {
+ union {
+ struct ufs_args ua;
+ struct iso_args ia;
+ struct mfs_args ma;
+ struct msdosfs_args da;
+ } targs;
+
+ if (!strcmp(fsp->f_fstypename, "mfs") ||
+ !strcmp(fsp->f_fstypename, "ufs") ||
+ !strcmp(fsp->f_fstypename, "msdos") ||
+ !strcmp(fsp->f_fstypename, "cd9660")) {
+ targs.ua.fspec = NULL;
+ targs.ua.export.ex_flags = MNT_DELEXPORT;
+ if (mount(fsp->f_fstypename, fsp->f_mntonname,
+ fsp->f_flags | MNT_UPDATE,
+ (caddr_t)&targs) < 0)
+ syslog(LOG_ERR, "Can't delete exports for %s",
+ fsp->f_mntonname);
+ }
+ fsp++;
+ }
+
+ /*
+ * Read in the exports file and build the list, calling
+ * mount() as we go along to push the export rules into the kernel.
+ */
+ if ((exp_file = fopen(exname, "r")) == NULL) {
+ syslog(LOG_ERR, "Can't open %s", exname);
+ exit(2);
+ }
+ dirhead = (struct dirlist *)NULL;
+ while (get_line()) {
+ if (debug)
+ fprintf(stderr,"Got line %s\n",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)
+ fprintf(stderr, "doing opt %s\n", 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)
+ fprintf(stderr,
+ "Making new ep fs=0x%x,0x%x\n",
+ fsb.f_fsid.val[0],
+ fsb.f_fsid.val[1]);
+ } else if (debug)
+ fprintf(stderr,
+ "Found ep fs=0x%x,0x%x\n",
+ 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 (get_host(hst, grp, tgrp)) {
+ syslog(LOG_ERR, "Bad host %s in netgroup %s, skipping", hst, cp);
+ grp->gr_type = GT_IGNORE;
+ }
+ } else if (get_host(cp, grp, tgrp)) {
+ syslog(LOG_ERR, "Bad host %s, skipping", cp);
+ grp->gr_type = GT_IGNORE;
+ }
+ has_host = TRUE;
+ } while (netgrp && getnetgrent(&hst, &usr, &dom));
+ endnetgrent();
+ *endcp = savedc;
+ }
+ cp = endcp;
+ nextfield(&cp, &endcp);
+ len = endcp - cp;
+ }
+ if (check_options(dirhead)) {
+ getexp_err(ep, tgrp);
+ goto nextline;
+ }
+ if (!has_host) {
+ grp->gr_type = GT_HOST;
+ if (debug)
+ fprintf(stderr,"Adding a default entry\n");
+ /* add a default group and make the grp list NULL */
+ hpe = (struct hostent *)malloc(sizeof(struct hostent));
+ if (hpe == (struct hostent *)NULL)
+ out_of_mem();
+ hpe->h_name = strdup("Default");
+ hpe->h_addrtype = AF_INET;
+ hpe->h_length = sizeof (u_long);
+ hpe->h_addr_list = (char **)NULL;
+ grp->gr_ptr.gt_hostent = hpe;
+
+ /*
+ * Don't allow a network export coincide with a list of
+ * host(s) on the same line.
+ */
+ } else if ((opt_flags & OP_NET) && tgrp->gr_next) {
+ getexp_err(ep, tgrp);
+ goto nextline;
+
+ /*
+ * If an export list was specified on this line, make sure
+ * that we have at least one valid entry, otherwise skip it.
+ */
+ } else {
+ grp = tgrp;
+ while (grp && grp->gr_type == GT_IGNORE)
+ grp = grp->gr_next;
+ if (! grp) {
+ getexp_err(ep, tgrp);
+ goto nextline;
+ }
+ }
+
+ /*
+ * Loop through hosts, pushing the exports into the kernel.
+ * After loop, tgrp points to the start of the list and
+ * grp points to the last entry in the list.
+ */
+ grp = tgrp;
+ do {
+ if (do_mount(ep, grp, exflags, &anon, dirp,
+ dirplen, &fsb)) {
+ getexp_err(ep, tgrp);
+ goto nextline;
+ }
+ } while (grp->gr_next && (grp = grp->gr_next));
+
+ /*
+ * Success. Update the data structures.
+ */
+ if (has_host) {
+ hang_dirp(dirhead, tgrp, ep, opt_flags);
+ grp->gr_next = grphead;
+ grphead = tgrp;
+ } else {
+ hang_dirp(dirhead, (struct grouplist *)NULL, ep,
+ opt_flags);
+ free_grp(grp);
+ }
+ dirhead = (struct dirlist *)NULL;
+ if ((ep->ex_flag & EX_LINKED) == 0) {
+ ep2 = exphead;
+ epp = &exphead;
+
+ /*
+ * Insert in the list in alphabetical order.
+ */
+ while (ep2 && strcmp(ep2->ex_fsdir, ep->ex_fsdir) < 0) {
+ epp = &ep2->ex_next;
+ ep2 = ep2->ex_next;
+ }
+ if (ep2)
+ ep->ex_next = ep2;
+ *epp = ep;
+ ep->ex_flag |= EX_LINKED;
+ }
+nextline:
+ if (dirhead) {
+ free_dir(dirhead);
+ dirhead = (struct dirlist *)NULL;
+ }
+ }
+ fclose(exp_file);
+}
+
+/*
+ * Allocate an export list element
+ */
+struct exportlist *
+get_exp()
+{
+ struct exportlist *ep;
+
+ ep = (struct exportlist *)malloc(sizeof (struct exportlist));
+ if (ep == (struct exportlist *)NULL)
+ out_of_mem();
+ memset(ep, 0, sizeof(struct exportlist));
+ return (ep);
+}
+
+/*
+ * Allocate a group list element
+ */
+struct grouplist *
+get_grp()
+{
+ struct grouplist *gp;
+
+ gp = (struct grouplist *)malloc(sizeof (struct grouplist));
+ if (gp == (struct grouplist *)NULL)
+ out_of_mem();
+ memset(gp, 0, sizeof(struct grouplist));
+ return (gp);
+}
+
+/*
+ * Clean up upon an error in get_exportlist().
+ */
+void
+getexp_err(ep, grp)
+ struct exportlist *ep;
+ struct grouplist *grp;
+{
+ struct grouplist *tgrp;
+
+ syslog(LOG_ERR, "Bad exports list line %s", line);
+ if (ep && (ep->ex_flag & EX_LINKED) == 0)
+ free_exp(ep);
+ while (grp) {
+ tgrp = grp;
+ grp = grp->gr_next;
+ free_grp(tgrp);
+ }
+}
+
+/*
+ * Search the export list for a matching fs.
+ */
+struct exportlist *
+ex_search(fsid)
+ fsid_t *fsid;
+{
+ struct exportlist *ep;
+
+ ep = exphead;
+ while (ep) {
+ if (ep->ex_fs.val[0] == fsid->val[0] &&
+ ep->ex_fs.val[1] == fsid->val[1])
+ return (ep);
+ ep = ep->ex_next;
+ }
+ return (ep);
+}
+
+/*
+ * Add a directory path to the list.
+ */
+char *
+add_expdir(dpp, cp, len)
+ struct dirlist **dpp;
+ char *cp;
+ int len;
+{
+ struct dirlist *dp;
+
+ dp = (struct dirlist *)malloc(sizeof (struct dirlist) + len);
+ dp->dp_left = *dpp;
+ dp->dp_right = (struct dirlist *)NULL;
+ dp->dp_flag = 0;
+ dp->dp_hosts = (struct hostlist *)NULL;
+ strcpy(dp->dp_dirp, cp);
+ *dpp = dp;
+ return (dp->dp_dirp);
+}
+
+/*
+ * Hang the dir list element off the dirpath binary tree as required
+ * and update the entry for host.
+ */
+void
+hang_dirp(dp, grp, ep, flags)
+ struct dirlist *dp;
+ struct grouplist *grp;
+ struct exportlist *ep;
+ int flags;
+{
+ struct hostlist *hp;
+ struct dirlist *dp2;
+
+ if (flags & OP_ALLDIRS) {
+ if (ep->ex_defdir)
+ free((caddr_t)dp);
+ else
+ ep->ex_defdir = dp;
+ if (grp == (struct grouplist *)NULL) {
+ ep->ex_defdir->dp_flag |= DP_DEFSET;
+ if (flags & OP_KERB)
+ ep->ex_defdir->dp_flag |= DP_KERB;
+ } else while (grp) {
+ hp = get_ht();
+ if (flags & OP_KERB)
+ hp->ht_flag |= DP_KERB;
+ hp->ht_grp = grp;
+ hp->ht_next = ep->ex_defdir->dp_hosts;
+ ep->ex_defdir->dp_hosts = hp;
+ grp = grp->gr_next;
+ }
+ } else {
+
+ /*
+ * Loop throught the directories adding them to the tree.
+ */
+ while (dp) {
+ dp2 = dp->dp_left;
+ add_dlist(&ep->ex_dirl, dp, grp, flags);
+ dp = dp2;
+ }
+ }
+}
+
+/*
+ * Traverse the binary tree either updating a node that is already there
+ * for the new directory or adding the new node.
+ */
+void
+add_dlist(dpp, newdp, grp, flags)
+ struct dirlist **dpp;
+ struct dirlist *newdp;
+ struct grouplist *grp;
+ int flags;
+{
+ struct dirlist *dp;
+ struct hostlist *hp;
+ int cmp;
+
+ dp = *dpp;
+ if (dp) {
+ cmp = strcmp(dp->dp_dirp, newdp->dp_dirp);
+ if (cmp > 0) {
+ add_dlist(&dp->dp_left, newdp, grp, flags);
+ return;
+ } else if (cmp < 0) {
+ add_dlist(&dp->dp_right, newdp, grp, flags);
+ return;
+ } else
+ free((caddr_t)newdp);
+ } else {
+ dp = newdp;
+ dp->dp_left = (struct dirlist *)NULL;
+ *dpp = dp;
+ }
+ if (grp) {
+
+ /*
+ * Hang all of the host(s) off of the directory point.
+ */
+ do {
+ hp = get_ht();
+ if (flags & OP_KERB)
+ hp->ht_flag |= DP_KERB;
+ hp->ht_grp = grp;
+ hp->ht_next = dp->dp_hosts;
+ dp->dp_hosts = hp;
+ grp = grp->gr_next;
+ } while (grp);
+ } else {
+ dp->dp_flag |= DP_DEFSET;
+ if (flags & OP_KERB)
+ dp->dp_flag |= DP_KERB;
+ }
+}
+
+/*
+ * Search for a dirpath on the export point.
+ */
+struct dirlist *
+dirp_search(dp, dirpath)
+ struct dirlist *dp;
+ char *dirpath;
+{
+ int cmp;
+
+ if (dp) {
+ cmp = strcmp(dp->dp_dirp, dirpath);
+ if (cmp > 0)
+ return (dirp_search(dp->dp_left, dirpath));
+ else if (cmp < 0)
+ return (dirp_search(dp->dp_right, dirpath));
+ else
+ return (dp);
+ }
+ return (dp);
+}
+
+/*
+ * Scan for a host match in a directory tree.
+ */
+int
+chk_host(dp, saddr, defsetp, hostsetp)
+ struct dirlist *dp;
+ u_long saddr;
+ int *defsetp;
+ int *hostsetp;
+{
+ struct hostlist *hp;
+ struct grouplist *grp;
+ u_long **addrp;
+
+ if (dp) {
+ if (dp->dp_flag & DP_DEFSET)
+ *defsetp = dp->dp_flag;
+ hp = dp->dp_hosts;
+ while (hp) {
+ grp = hp->ht_grp;
+ switch (grp->gr_type) {
+ case GT_HOST:
+ addrp = (u_long **)
+ grp->gr_ptr.gt_hostent->h_addr_list;
+ while (*addrp) {
+ if (**addrp == saddr) {
+ *hostsetp = (hp->ht_flag | DP_HOSTSET);
+ return (1);
+ }
+ addrp++;
+ }
+ break;
+ case GT_NET:
+ if ((saddr & grp->gr_ptr.gt_net.nt_mask) ==
+ grp->gr_ptr.gt_net.nt_net) {
+ *hostsetp = (hp->ht_flag | DP_HOSTSET);
+ return (1);
+ }
+ break;
+ };
+ hp = hp->ht_next;
+ }
+ }
+ return (0);
+}
+
+/*
+ * Scan tree for a host that matches the address.
+ */
+int
+scan_tree(dp, saddr)
+ struct dirlist *dp;
+ u_long saddr;
+{
+ int defset, hostset;
+
+ if (dp) {
+ if (scan_tree(dp->dp_left, saddr))
+ return (1);
+ if (chk_host(dp, saddr, &defset, &hostset))
+ return (1);
+ if (scan_tree(dp->dp_right, saddr))
+ return (1);
+ }
+ return (0);
+}
+
+/*
+ * Traverse the dirlist tree and free it up.
+ */
+void
+free_dir(dp)
+ struct dirlist *dp;
+{
+
+ if (dp) {
+ free_dir(dp->dp_left);
+ free_dir(dp->dp_right);
+ free_host(dp->dp_hosts);
+ free((caddr_t)dp);
+ }
+}
+
+/*
+ * Parse the option string and update fields.
+ * Option arguments may either be -<option>=<value> or
+ * -<option> <value>
+ */
+int
+do_opt(cpp, endcpp, ep, grp, has_hostp, exflagsp, cr)
+ char **cpp, **endcpp;
+ struct exportlist *ep;
+ struct grouplist *grp;
+ int *has_hostp;
+ int *exflagsp;
+ struct ucred *cr;
+{
+ char *cpoptarg, *cpoptend;
+ char *cp, *endcp, *cpopt, savedc, savedc2;
+ int allflag, usedarg;
+
+ cpopt = *cpp;
+ cpopt++;
+ cp = *endcpp;
+ savedc = *cp;
+ *cp = '\0';
+ while (cpopt && *cpopt) {
+ allflag = 1;
+ usedarg = -2;
+ if (cpoptend = strchr(cpopt, ',')) {
+ *cpoptend++ = '\0';
+ if (cpoptarg = strchr(cpopt, '='))
+ *cpoptarg++ = '\0';
+ } else {
+ if (cpoptarg = strchr(cpopt, '='))
+ *cpoptarg++ = '\0';
+ else {
+ *cp = savedc;
+ nextfield(&cp, &endcp);
+ **endcpp = '\0';
+ if (endcp > cp && *cp != '-') {
+ cpoptarg = cp;
+ savedc2 = *endcp;
+ *endcp = '\0';
+ usedarg = 0;
+ }
+ }
+ }
+ if (!strcmp(cpopt, "ro") || !strcmp(cpopt, "o")) {
+ *exflagsp |= MNT_EXRDONLY;
+ } else if (cpoptarg && (!strcmp(cpopt, "maproot") ||
+ !(allflag = strcmp(cpopt, "mapall")) ||
+ !strcmp(cpopt, "root") || !strcmp(cpopt, "r"))) {
+ usedarg++;
+ parsecred(cpoptarg, cr);
+ if (allflag == 0) {
+ *exflagsp |= MNT_EXPORTANON;
+ opt_flags |= OP_MAPALL;
+ } else
+ opt_flags |= OP_MAPROOT;
+ } else if (!strcmp(cpopt, "kerb") || !strcmp(cpopt, "k")) {
+ *exflagsp |= MNT_EXKERB;
+ opt_flags |= OP_KERB;
+ } else if (cpoptarg && (!strcmp(cpopt, "mask") ||
+ !strcmp(cpopt, "m"))) {
+ if (get_net(cpoptarg, &grp->gr_ptr.gt_net, 1)) {
+ syslog(LOG_ERR, "Bad mask: %s", cpoptarg);
+ return (1);
+ }
+ usedarg++;
+ opt_flags |= OP_MASK;
+ } else if (cpoptarg && (!strcmp(cpopt, "network") ||
+ !strcmp(cpopt, "n"))) {
+ if (grp->gr_type != GT_NULL) {
+ syslog(LOG_ERR, "Network/host conflict");
+ return (1);
+ } else if (get_net(cpoptarg, &grp->gr_ptr.gt_net, 0)) {
+ syslog(LOG_ERR, "Bad net: %s", cpoptarg);
+ return (1);
+ }
+ grp->gr_type = GT_NET;
+ *has_hostp = 1;
+ usedarg++;
+ opt_flags |= OP_NET;
+ } else if (!strcmp(cpopt, "alldirs")) {
+ opt_flags |= OP_ALLDIRS;
+ } else if (!strcmp(cpopt, "public")) {
+ *exflagsp |= MNT_EXPUBLIC;
+ } else if (!strcmp(cpopt, "webnfs")) {
+ *exflagsp |= (MNT_EXPUBLIC|MNT_EXRDONLY|MNT_EXPORTANON);
+ opt_flags |= OP_MAPALL;
+ } else if (cpoptarg && !strcmp(cpopt, "index")) {
+ ep->ex_indexfile = strdup(cpoptarg);
+#ifdef ISO
+ } else if (cpoptarg && !strcmp(cpopt, "iso")) {
+ if (get_isoaddr(cpoptarg, grp)) {
+ syslog(LOG_ERR, "Bad iso addr: %s", cpoptarg);
+ return (1);
+ }
+ *has_hostp = 1;
+ usedarg++;
+ opt_flags |= OP_ISO;
+#endif /* ISO */
+ } else {
+ syslog(LOG_ERR, "Bad opt %s", cpopt);
+ return (1);
+ }
+ if (usedarg >= 0) {
+ *endcp = savedc2;
+ **endcpp = savedc;
+ if (usedarg > 0) {
+ *cpp = cp;
+ *endcpp = endcp;
+ }
+ return (0);
+ }
+ cpopt = cpoptend;
+ }
+ **endcpp = savedc;
+ return (0);
+}
+
+/*
+ * Translate a character string to the corresponding list of network
+ * addresses for a hostname.
+ */
+int
+get_host(cp, grp, tgrp)
+ char *cp;
+ struct grouplist *grp;
+ struct grouplist *tgrp;
+{
+ struct grouplist *checkgrp;
+ struct hostent *hp, *nhp;
+ char **addrp, **naddrp;
+ struct hostent t_host;
+ int i;
+ u_long saddr;
+ char *aptr[2];
+
+ if (grp->gr_type != GT_NULL)
+ return (1);
+ if ((hp = gethostbyname(cp)) == NULL) {
+ if (isdigit(*cp)) {
+ saddr = inet_addr(cp);
+ if (saddr == -1) {
+ syslog(LOG_ERR, "Inet_addr failed for %s", cp);
+ return (1);
+ }
+ if ((hp = gethostbyaddr((caddr_t)&saddr, sizeof (saddr),
+ AF_INET)) == NULL) {
+ hp = &t_host;
+ hp->h_name = cp;
+ hp->h_addrtype = AF_INET;
+ hp->h_length = sizeof (u_long);
+ hp->h_addr_list = aptr;
+ aptr[0] = (char *)&saddr;
+ aptr[1] = (char *)NULL;
+ }
+ } else {
+ syslog(LOG_ERR, "Gethostbyname failed for %s", cp);
+ return (1);
+ }
+ }
+ /*
+ * Sanity check: make sure we don't already have an entry
+ * for this host in the grouplist.
+ */
+ checkgrp = tgrp;
+ while (checkgrp) {
+ if (checkgrp->gr_type == GT_HOST &&
+ checkgrp->gr_ptr.gt_hostent != NULL &&
+ !strcmp(checkgrp->gr_ptr.gt_hostent->h_name, hp->h_name)) {
+ grp->gr_type = GT_IGNORE;
+ return(0);
+ }
+ checkgrp = checkgrp->gr_next;
+ }
+
+ grp->gr_type = GT_HOST;
+ nhp = grp->gr_ptr.gt_hostent = (struct hostent *)
+ malloc(sizeof(struct hostent));
+ if (nhp == (struct hostent *)NULL)
+ out_of_mem();
+ memmove(nhp, hp, sizeof(struct hostent));
+ i = strlen(hp->h_name)+1;
+ nhp->h_name = (char *)malloc(i);
+ if (nhp->h_name == (char *)NULL)
+ out_of_mem();
+ memmove(nhp->h_name, hp->h_name, i);
+ addrp = hp->h_addr_list;
+ i = 1;
+ while (*addrp++)
+ i++;
+ naddrp = nhp->h_addr_list = (char **)
+ malloc(i*sizeof(char *));
+ if (naddrp == (char **)NULL)
+ out_of_mem();
+ addrp = hp->h_addr_list;
+ while (*addrp) {
+ *naddrp = (char *)
+ malloc(hp->h_length);
+ if (*naddrp == (char *)NULL)
+ out_of_mem();
+ memmove(*naddrp, *addrp, hp->h_length);
+ addrp++;
+ naddrp++;
+ }
+ *naddrp = (char *)NULL;
+ if (debug)
+ fprintf(stderr, "got host %s\n", hp->h_name);
+ return (0);
+}
+
+/*
+ * Free up an exports list component
+ */
+void
+free_exp(ep)
+ struct exportlist *ep;
+{
+
+ if (ep->ex_defdir) {
+ free_host(ep->ex_defdir->dp_hosts);
+ free((caddr_t)ep->ex_defdir);
+ }
+ if (ep->ex_fsdir)
+ free(ep->ex_fsdir);
+ if (ep->ex_indexfile)
+ free(ep->ex_indexfile);
+ free_dir(ep->ex_dirl);
+ free((caddr_t)ep);
+}
+
+/*
+ * Free hosts.
+ */
+void
+free_host(hp)
+ struct hostlist *hp;
+{
+ struct hostlist *hp2;
+
+ while (hp) {
+ hp2 = hp;
+ hp = hp->ht_next;
+ free((caddr_t)hp2);
+ }
+}
+
+struct hostlist *
+get_ht()
+{
+ struct hostlist *hp;
+
+ hp = (struct hostlist *)malloc(sizeof (struct hostlist));
+ if (hp == (struct hostlist *)NULL)
+ out_of_mem();
+ hp->ht_next = (struct hostlist *)NULL;
+ hp->ht_flag = 0;
+ return (hp);
+}
+
+#ifdef ISO
+/*
+ * Translate an iso address.
+ */
+get_isoaddr(cp, grp)
+ char *cp;
+ struct grouplist *grp;
+{
+ struct iso_addr *isop;
+ struct sockaddr_iso *isoaddr;
+
+ if (grp->gr_type != GT_NULL)
+ return (1);
+ if ((isop = iso_addr(cp)) == NULL) {
+ syslog(LOG_ERR,
+ "iso_addr failed, ignored");
+ return (1);
+ }
+ isoaddr = (struct sockaddr_iso *)
+ malloc(sizeof (struct sockaddr_iso));
+ if (isoaddr == (struct sockaddr_iso *)NULL)
+ out_of_mem();
+ memset(isoaddr, 0, sizeof(struct sockaddr_iso));
+ memmove(&isoaddr->siso_addr, isop, sizeof(struct iso_addr));
+ isoaddr->siso_len = sizeof(struct sockaddr_iso);
+ isoaddr->siso_family = AF_ISO;
+ grp->gr_type = GT_ISO;
+ grp->gr_ptr.gt_isoaddr = isoaddr;
+ return (0);
+}
+#endif /* ISO */
+
+/*
+ * Out of memory, fatal
+ */
+void
+out_of_mem()
+{
+
+ syslog(LOG_ERR, "Out of memory");
+ exit(2);
+}
+
+/*
+ * Do the mount syscall with the update flag to push the export info into
+ * the kernel.
+ */
+int
+do_mount(ep, grp, exflags, anoncrp, dirp, dirplen, fsb)
+ struct exportlist *ep;
+ struct grouplist *grp;
+ int exflags;
+ struct ucred *anoncrp;
+ char *dirp;
+ int dirplen;
+ struct statfs *fsb;
+{
+ char *cp = (char *)NULL;
+ u_long **addrp;
+ int done;
+ char savedc = '\0';
+ struct sockaddr_in sin, imask;
+ union {
+ struct ufs_args ua;
+ struct iso_args ia;
+ struct mfs_args ma;
+#ifdef __NetBSD__
+ struct msdosfs_args da;
+#endif
+ } args;
+ u_long net;
+
+ args.ua.fspec = 0;
+ args.ua.export.ex_flags = exflags;
+ args.ua.export.ex_anon = *anoncrp;
+ args.ua.export.ex_indexfile = ep->ex_indexfile;
+ memset(&sin, 0, sizeof(sin));
+ memset(&imask, 0, sizeof(imask));
+ sin.sin_family = AF_INET;
+ sin.sin_len = sizeof(sin);
+ imask.sin_family = AF_INET;
+ imask.sin_len = sizeof(sin);
+ if (grp->gr_type == GT_HOST)
+ addrp = (u_long **)grp->gr_ptr.gt_hostent->h_addr_list;
+ else
+ addrp = (u_long **)NULL;
+ done = FALSE;
+ while (!done) {
+ switch (grp->gr_type) {
+ case GT_HOST:
+ if (addrp) {
+ sin.sin_addr.s_addr = **addrp;
+ args.ua.export.ex_addrlen = sizeof(sin);
+ } else
+ args.ua.export.ex_addrlen = 0;
+ args.ua.export.ex_addr = (struct sockaddr *)&sin;
+ args.ua.export.ex_masklen = 0;
+ break;
+ case GT_NET:
+ if (grp->gr_ptr.gt_net.nt_mask)
+ imask.sin_addr.s_addr = grp->gr_ptr.gt_net.nt_mask;
+ else {
+ net = ntohl(grp->gr_ptr.gt_net.nt_net);
+ if (IN_CLASSA(net))
+ imask.sin_addr.s_addr = inet_addr("255.0.0.0");
+ else if (IN_CLASSB(net))
+ imask.sin_addr.s_addr =
+ inet_addr("255.255.0.0");
+ else
+ imask.sin_addr.s_addr =
+ inet_addr("255.255.255.0");
+ grp->gr_ptr.gt_net.nt_mask = imask.sin_addr.s_addr;
+ }
+ sin.sin_addr.s_addr = grp->gr_ptr.gt_net.nt_net;
+ args.ua.export.ex_addr = (struct sockaddr *)&sin;
+ args.ua.export.ex_addrlen = sizeof (sin);
+ args.ua.export.ex_mask = (struct sockaddr *)&imask;
+ args.ua.export.ex_masklen = sizeof (imask);
+ break;
+#ifdef ISO
+ case GT_ISO:
+ args.ua.export.ex_addr =
+ (struct sockaddr *)grp->gr_ptr.gt_isoaddr;
+ args.ua.export.ex_addrlen =
+ sizeof(struct sockaddr_iso);
+ args.ua.export.ex_masklen = 0;
+ break;
+#endif /* ISO */
+ case GT_IGNORE:
+ return(0);
+ break;
+ default:
+ syslog(LOG_ERR, "Bad grouptype");
+ if (cp)
+ *cp = savedc;
+ return (1);
+ };
+
+ /*
+ * XXX:
+ * Maybe I should just use the fsb->f_mntonname path instead
+ * of looping back up the dirp to the mount point??
+ * Also, needs to know how to export all types of local
+ * exportable file systems and not just "ufs".
+ */
+ while (mount(fsb->f_fstypename, dirp,
+ fsb->f_flags | MNT_UPDATE, (caddr_t)&args) < 0) {
+ if (cp)
+ *cp-- = savedc;
+ else
+ cp = dirp + dirplen - 1;
+ if (errno == EPERM) {
+ syslog(LOG_ERR,
+ "Can't change attributes for %s.\n", 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)
+ fprintf(stderr,"mnt unsucc\n");
+ syslog(LOG_ERR, "Can't export %s", dirp);
+ return (1);
+ }
+ savedc = *cp;
+ *cp = '\0';
+ }
+ if (addrp) {
+ ++addrp;
+ if (*addrp == (u_long *)NULL)
+ done = TRUE;
+ } else
+ done = TRUE;
+ }
+ if (cp)
+ *cp = savedc;
+ return (0);
+}
+
+/*
+ * Translate a net address.
+ */
+int
+get_net(cp, net, maskflg)
+ char *cp;
+ struct netmsk *net;
+ int maskflg;
+{
+ struct netent *np;
+ long netaddr;
+ struct in_addr inetaddr, inetaddr2;
+ char *name;
+
+ if (isdigit(*cp) && ((netaddr = inet_network(cp)) != -1)) {
+ inetaddr = inet_makeaddr(netaddr, 0);
+ /*
+ * Due to arbritrary subnet masks, you don't know how many
+ * bits to shift the address to make it into a network,
+ * however you do know how to make a network address into
+ * a host with host == 0 and then compare them.
+ * (What a pest)
+ */
+ if (!maskflg) {
+ setnetent(0);
+ while (np = getnetent()) {
+ inetaddr2 = inet_makeaddr(np->n_net, 0);
+ if (inetaddr2.s_addr == inetaddr.s_addr)
+ break;
+ }
+ endnetent();
+ }
+ } else if ((np = getnetbyname(cp)) != NULL) {
+ inetaddr = inet_makeaddr(np->n_net, 0);
+ } else
+ return (1);
+
+ if (maskflg)
+ net->nt_mask = inetaddr.s_addr;
+ else {
+ if (np)
+ name = np->n_name;
+ else
+ name = inet_ntoa(inetaddr);
+ net->nt_name = (char *)malloc(strlen(name) + 1);
+ if (net->nt_name == (char *)NULL)
+ out_of_mem();
+ strcpy(net->nt_name, name);
+ net->nt_net = inetaddr.s_addr;
+ }
+ return (0);
+}
+
+/*
+ * Parse out the next white space separated field
+ */
+void
+nextfield(cp, endcp)
+ char **cp;
+ char **endcp;
+{
+ char *p;
+
+ p = *cp;
+ while (*p == ' ' || *p == '\t')
+ p++;
+ if (*p == '\n' || *p == '\0')
+ *cp = *endcp = p;
+ else {
+ *cp = p++;
+ while (*p != ' ' && *p != '\t' && *p != '\n' && *p != '\0')
+ p++;
+ *endcp = p;
+ }
+}
+
+/*
+ * Get an exports file line. Skip over blank lines and handle line
+ * continuations.
+ */
+int
+get_line()
+{
+ char *p, *cp;
+ int len;
+ int totlen, cont_line;
+
+ /*
+ * Loop around ignoring blank lines and getting all continuation lines.
+ */
+ p = line;
+ totlen = 0;
+ do {
+ if (fgets(p, LINESIZ - totlen, exp_file) == NULL)
+ return (0);
+ len = strlen(p);
+ cp = p + len - 1;
+ cont_line = 0;
+ while (cp >= p &&
+ (*cp == ' ' || *cp == '\t' || *cp == '\n' || *cp == '\\')) {
+ if (*cp == '\\')
+ cont_line = 1;
+ cp--;
+ len--;
+ }
+ *++cp = '\0';
+ if (len > 0) {
+ totlen += len;
+ if (totlen >= LINESIZ) {
+ syslog(LOG_ERR, "Exports line too long");
+ exit(2);
+ }
+ p = cp;
+ }
+ } while (totlen == 0 || cont_line);
+ return (1);
+}
+
+/*
+ * Parse a description of a credential.
+ */
+void
+parsecred(namelist, cr)
+ char *namelist;
+ struct ucred *cr;
+{
+ char *name;
+ int cnt;
+ char *names;
+ struct passwd *pw;
+ struct group *gr;
+ int ngroups, groups[NGROUPS + 1];
+
+ /*
+ * Set up the unpriviledged user.
+ */
+ cr->cr_ref = 1;
+ cr->cr_uid = -2;
+ cr->cr_groups[0] = -2;
+ cr->cr_ngroups = 1;
+ /*
+ * Get the user's password table entry.
+ */
+ names = strsep(&namelist, " \t\n");
+ name = strsep(&names, ":");
+ if (isdigit(*name) || *name == '-')
+ pw = getpwuid(atoi(name));
+ else
+ pw = getpwnam(name);
+ /*
+ * Credentials specified as those of a user.
+ */
+ if (names == NULL) {
+ if (pw == NULL) {
+ syslog(LOG_ERR, "Unknown user: %s", name);
+ return;
+ }
+ cr->cr_uid = pw->pw_uid;
+ ngroups = NGROUPS + 1;
+ if (getgrouplist(pw->pw_name, pw->pw_gid, groups, &ngroups))
+ syslog(LOG_ERR, "Too many groups");
+ /*
+ * Convert from int's to gid_t's and compress out duplicate
+ */
+ cr->cr_ngroups = ngroups - 1;
+ cr->cr_groups[0] = groups[0];
+ for (cnt = 2; cnt < ngroups; cnt++)
+ cr->cr_groups[cnt - 1] = groups[cnt];
+ return;
+ }
+ /*
+ * Explicit credential specified as a colon separated list:
+ * uid:gid:gid:...
+ */
+ if (pw != NULL)
+ cr->cr_uid = pw->pw_uid;
+ else if (isdigit(*name) || *name == '-')
+ cr->cr_uid = atoi(name);
+ else {
+ syslog(LOG_ERR, "Unknown user: %s", name);
+ return;
+ }
+ cr->cr_ngroups = 0;
+ while (names != NULL && *names != '\0' && cr->cr_ngroups < NGROUPS) {
+ name = strsep(&names, ":");
+ if (isdigit(*name) || *name == '-') {
+ cr->cr_groups[cr->cr_ngroups++] = atoi(name);
+ } else {
+ if ((gr = getgrnam(name)) == NULL) {
+ syslog(LOG_ERR, "Unknown group: %s", name);
+ continue;
+ }
+ cr->cr_groups[cr->cr_ngroups++] = gr->gr_gid;
+ }
+ }
+ if (names != NULL && *names != '\0' && cr->cr_ngroups == NGROUPS)
+ syslog(LOG_ERR, "Too many groups");
+}
+
+#define STRSIZ (RPCMNT_NAMELEN+RPCMNT_PATHLEN+50)
+/*
+ * Routines that maintain the remote mounttab
+ */
+void
+get_mountlist()
+{
+ struct mountlist *mlp, **mlpp;
+ char *host, *dirp, *cp;
+ int len;
+ char str[STRSIZ];
+ FILE *mlfile;
+
+ if ((mlfile = fopen(_PATH_RMOUNTLIST, "r")) == NULL) {
+ syslog(LOG_ERR, "Can't open %s", _PATH_RMOUNTLIST);
+ return;
+ }
+ mlpp = &mlhead;
+ while (fgets(str, STRSIZ, mlfile) != NULL) {
+ cp = str;
+ host = strsep(&cp, " \t\n");
+ dirp = strsep(&cp, " \t\n");
+ if (host == NULL || dirp == NULL)
+ continue;
+ mlp = (struct mountlist *)malloc(sizeof (*mlp));
+ strncpy(mlp->ml_host, host, RPCMNT_NAMELEN);
+ mlp->ml_host[RPCMNT_NAMELEN] = '\0';
+ strncpy(mlp->ml_dirp, dirp, RPCMNT_PATHLEN);
+ mlp->ml_dirp[RPCMNT_PATHLEN] = '\0';
+ mlp->ml_next = (struct mountlist *)NULL;
+ *mlpp = mlp;
+ mlpp = &mlp->ml_next;
+ }
+ fclose(mlfile);
+}
+
+void
+del_mlist(hostp, dirp)
+ char *hostp, *dirp;
+{
+ struct mountlist *mlp, **mlpp;
+ struct mountlist *mlp2;
+ FILE *mlfile;
+ int fnd = 0;
+
+ mlpp = &mlhead;
+ mlp = mlhead;
+ while (mlp) {
+ if (!strcmp(mlp->ml_host, hostp) &&
+ (!dirp || !strcmp(mlp->ml_dirp, dirp))) {
+ fnd = 1;
+ mlp2 = mlp;
+ *mlpp = mlp = mlp->ml_next;
+ free((caddr_t)mlp2);
+ } else {
+ mlpp = &mlp->ml_next;
+ mlp = mlp->ml_next;
+ }
+ }
+ if (fnd) {
+ if ((mlfile = fopen(_PATH_RMOUNTLIST, "w")) == NULL) {
+ syslog(LOG_ERR,"Can't update %s", _PATH_RMOUNTLIST);
+ return;
+ }
+ mlp = mlhead;
+ while (mlp) {
+ fprintf(mlfile, "%s %s\n", mlp->ml_host, mlp->ml_dirp);
+ mlp = mlp->ml_next;
+ }
+ fclose(mlfile);
+ }
+}
+
+void
+add_mlist(hostp, dirp)
+ char *hostp, *dirp;
+{
+ struct mountlist *mlp, **mlpp;
+ FILE *mlfile;
+
+ mlpp = &mlhead;
+ mlp = mlhead;
+ while (mlp) {
+ if (!strcmp(mlp->ml_host, hostp) && !strcmp(mlp->ml_dirp, dirp))
+ return;
+ mlpp = &mlp->ml_next;
+ mlp = mlp->ml_next;
+ }
+ mlp = (struct mountlist *)malloc(sizeof (*mlp));
+ strncpy(mlp->ml_host, hostp, RPCMNT_NAMELEN);
+ mlp->ml_host[RPCMNT_NAMELEN] = '\0';
+ strncpy(mlp->ml_dirp, dirp, RPCMNT_PATHLEN);
+ mlp->ml_dirp[RPCMNT_PATHLEN] = '\0';
+ mlp->ml_next = (struct mountlist *)NULL;
+ *mlpp = mlp;
+ if ((mlfile = fopen(_PATH_RMOUNTLIST, "a")) == NULL) {
+ syslog(LOG_ERR, "Can't update %s", _PATH_RMOUNTLIST);
+ return;
+ }
+ fprintf(mlfile, "%s %s\n", mlp->ml_host, mlp->ml_dirp);
+ fclose(mlfile);
+}
+
+/*
+ * This function is called via. SIGTERM when the system is going down.
+ * It sends a broadcast RPCMNT_UMNTALL.
+ */
+void
+send_umntall()
+{
+ (void) clnt_broadcast(RPCPROG_MNT, RPCMNT_VER1, RPCMNT_UMNTALL,
+ xdr_void, (caddr_t)0, xdr_void, (caddr_t)0, umntall_each);
+ exit(0);
+}
+
+int
+umntall_each(resultsp, raddr)
+ caddr_t resultsp;
+ struct sockaddr_in *raddr;
+{
+ return (1);
+}
+
+/*
+ * Free up a group list.
+ */
+void
+free_grp(grp)
+ struct grouplist *grp;
+{
+ char **addrp;
+
+ if (grp->gr_type == GT_HOST) {
+ if (grp->gr_ptr.gt_hostent->h_name) {
+ addrp = grp->gr_ptr.gt_hostent->h_addr_list;
+ while (addrp && *addrp)
+ free(*addrp++);
+ free((caddr_t)grp->gr_ptr.gt_hostent->h_addr_list);
+ free(grp->gr_ptr.gt_hostent->h_name);
+ }
+ free((caddr_t)grp->gr_ptr.gt_hostent);
+ } else if (grp->gr_type == GT_NET) {
+ if (grp->gr_ptr.gt_net.nt_name)
+ free(grp->gr_ptr.gt_net.nt_name);
+ }
+#ifdef ISO
+ else if (grp->gr_type == GT_ISO)
+ free((caddr_t)grp->gr_ptr.gt_isoaddr);
+#endif
+ free((caddr_t)grp);
+}
+
+#ifdef DEBUG
+void
+SYSLOG(int pri, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ vfprintf(stderr, fmt, ap);
+ va_end(ap);
+}
+#endif /* DEBUG */
+
+/*
+ * Check options for consistency.
+ */
+int
+check_options(dp)
+ struct dirlist *dp;
+{
+
+ if (dp == (struct dirlist *)NULL)
+ return (1);
+ if ((opt_flags & (OP_MAPROOT | OP_MAPALL)) == (OP_MAPROOT | OP_MAPALL) ||
+ (opt_flags & (OP_MAPROOT | OP_KERB)) == (OP_MAPROOT | OP_KERB) ||
+ (opt_flags & (OP_MAPALL | OP_KERB)) == (OP_MAPALL | OP_KERB)) {
+ syslog(LOG_ERR, "-mapall, -maproot and -kerb mutually exclusive");
+ return (1);
+ }
+ if ((opt_flags & OP_MASK) && (opt_flags & OP_NET) == 0) {
+ syslog(LOG_ERR, "-mask requires -net");
+ return (1);
+ }
+ if ((opt_flags & (OP_NET | OP_ISO)) == (OP_NET | OP_ISO)) {
+ syslog(LOG_ERR, "-net and -iso mutually exclusive");
+ return (1);
+ }
+ if ((opt_flags & OP_ALLDIRS) && dp->dp_left) {
+ syslog(LOG_ERR, "-alldir has multiple directories");
+ return (1);
+ }
+ return (0);
+}
+
+/*
+ * Check an absolute directory path for any symbolic links. Return true
+ * if no symbolic links are found.
+ */
+int
+check_dirpath(dirp)
+ char *dirp;
+{
+ char *cp;
+ int ret = 1;
+ struct stat sb;
+
+ cp = dirp + 1;
+ while (*cp && ret) {
+ if (*cp == '/') {
+ *cp = '\0';
+ if (lstat(dirp, &sb) < 0 || !S_ISDIR(sb.st_mode))
+ ret = 0;
+ *cp = '/';
+ }
+ cp++;
+ }
+ if (lstat(dirp, &sb) < 0 || !S_ISDIR(sb.st_mode))
+ ret = 0;
+ return (ret);
+}
+
+/*
+ * Just translate an ascii string to an integer.
+ */
+int
+get_num(cp)
+ register char *cp;
+{
+ register int res = 0;
+
+ while (*cp) {
+ if (*cp < '0' || *cp > '9')
+ return (-1);
+ res = res * 10 + (*cp++ - '0');
+ }
+ return (res);
+}
diff --git a/usr.sbin/mountd/netgroup.5 b/usr.sbin/mountd/netgroup.5
new file mode 100644
index 0000000..af7d448
--- /dev/null
+++ b/usr.sbin/mountd/netgroup.5
@@ -0,0 +1,160 @@
+.\" Copyright (c) 1992, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)netgroup.5 8.2 (Berkeley) 12/11/93
+.\"
+.Dd December 11, 1993
+.Dt NETGROUP 5
+.Os
+.Sh NAME
+.Nm netgroup
+.Nd defines network groups
+.Sh SYNOPSIS
+.Nm netgroup
+.Sh DESCRIPTION
+The
+.Nm netgroup
+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
+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.
+The functions specified in
+.Xr getnetgrent 3
+should normally be used to access the
+.Nm netgroup
+database.
+.Pp
+Lines that begin with a # are treated as comments.
+.Sh NIS/YP INTERACTION
+On most other platforms,
+.Nm netgroups
+are only used in conjunction with
+NIS and local
+.Pa /etc/netgroup
+files are ignored. With FreeBSD,
+.Nm netgroups
+can be used with either NIS or local files, but there are certain
+caveats to consider. The existing
+.Nm netgroup
+system is extremely inefficient where
+.Fn innetgr 3
+lookups are concerned since
+.Nm netgroup
+memberships are computed on the fly. By contrast, the NIS
+.Nm netgroup
+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 FreeBSD
+.Nm netgroup
+system can interact with the NIS
+.Nm netgroup
+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 '+', and NIS is running,
+.Nm netgroup
+lookups will be done exclusively through 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 netgroup
+information (with no NIS '+' token), then only the local
+.Nm netgroup
+information will be processed (and NIS will be ignored).
+.It
+If
+.Pa /etc/netgroup
+exists and contains both local netgroup data
+.Pa and
+the NIS '+' token, the local data and the NIS netgroup
+map will be processed as a single combined
+.Nm netgroup
+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 BSD environment.
+.Pp
+The
+.Nm netgroup
+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..fa1371f
--- /dev/null
+++ b/usr.sbin/moused/Makefile
@@ -0,0 +1,9 @@
+PROG= moused
+SRCS= moused.c
+MAN8= moused.8
+
+#BINOWN= root
+#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..1115bab
--- /dev/null
+++ b/usr.sbin/moused/moused.8
@@ -0,0 +1,123 @@
+.\" Copyright (c) 1996
+.\" Mike Pritchard <mpp@FreeBSD.org>. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by Mike Pritchard.
+.\" 4. Neither the name of the author nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $Id: moused.8,v 1.5 1997/07/27 23:10:33 wosch Exp $
+.\"
+.Dd December 16, 1996
+.Dt MOUSED 8
+.Os FreeBSD
+.Sh NAME
+.Nm moused
+.Nd pass mouse data to console driver
+.Sh SYNOPSIS
+.Nm
+.Op Fl DRcdfs
+.Op Fl r Ar samplerate
+.Op Fl S Ar baudrate
+.Fl p Ar port
+.Fl t Ar mousetype
+.Sh DESCRIPTION
+The mouse daemon listens to a serial port for mouse data,
+interprets the data and then passes ioctls to the console
+driver.
+.Pp
+The options are as follows:
+.Bl -tag -width indent
+.It Fl D
+Lower DTR on the serial port.
+.It Fl R
+Lower RTS on the serial port.
+.It Fl S Ar baudrate
+Select the baudrate for the serial port (1200 to 9600).
+.It Fl c
+Enable ChordMiddle option.
+.It Fl d
+Enable debugging messages.
+.It Fl f
+Do not become a daemon and instead run as a foreground process.
+.It Fl p Ar port
+Use
+.Ar port
+as the serial port to communicate with the mouse.
+.It Fl r
+Set the bit sample rate on devices that support it (in Dots Per Inch).
+.It Fl s
+Select a baudrate of 9600 for the serial line.
+.It Fl t Ar mousetype
+Specify the type of mouse attached to the
+serial port. Valid mouse types are:
+.Pp
+.Bl -tag -compact -width mousesystemsxxx
+.It microsoft
+Microsoft mouse
+.It mousesystems
+Mouse systems Corp mouse
+.It mmseries
+MM Series mouse
+.It logitech
+Logitech mouse
+.It busmouse
+A bus mouse
+.It mouseman
+Logitech MouseMan and TrackMan
+.It ps/2
+PS/2 mouse
+.It mmhittab
+MM HitTablet
+.El
+.El
+.Sh EXAMPLE
+.Pp
+.Dl moused -t microsoft -p /dev/mouse
+.Dl vidcontrol -m on
+.Pp
+Start the mouse daemon on the serial device
+.Pa /dev/mouse
+for a microsoft mouse and enable the mousepointer.
+.Sh SEE ALSO
+.Xr vidcontrol 1 ,
+.Xr keyboard 4 ,
+.Xr pcvt 4 ,
+.Xr screen 4 ,
+.Xr sysmouse 4
+.Sh AUTHORS
+The
+.Nm
+command was written by
+.An Michael Smith .
+
+This manual page
+was written by
+.An Mike Pritchard Aq mpp@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..5e4abda
--- /dev/null
+++ b/usr.sbin/moused/moused.c
@@ -0,0 +1,717 @@
+/**
+ ** 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 serial port for mouse data stream,
+ ** interprets same and passes ioctls off to the console driver.
+ **
+ ** The mouse interface functions are derived closely from the mouse
+ ** handler in the XFree86 X server. Many thanks to the XFree86 people
+ ** for their great work!
+ **
+ **/
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <machine/console.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <unistd.h>
+
+#define debug(fmt,args...) \
+ if (debug&&nodaemon) warnx(fmt, ##args)
+
+int debug = 0;
+int nodaemon = 0;
+
+void usage(void);
+
+#define R_UNKNOWN 0
+#define R_MICROSOFT 1
+#define R_MOUSESYS 2
+#define R_MMSERIES 3
+#define R_LOGITECH 4
+#define R_BUSMOUSE 5
+#define R_LOGIMAN 6
+#define R_PS_2 7
+#define R_MMHITAB 8
+
+char *rnames[] = {
+ "xxx",
+ "microsoft",
+ "mousesystems",
+ "mmseries",
+ "logitech",
+ "busmouse",
+ "mouseman",
+ "ps/2",
+ "mmhitab",
+ NULL
+};
+
+unsigned short rodentcflags[] =
+{
+ 0, /* nomouse */
+ (CS7 | CREAD | CLOCAL | HUPCL ), /* MicroSoft */
+ (CS8 | CSTOPB | CREAD | CLOCAL | HUPCL ), /* MouseSystems */
+ (CS8 | PARENB | PARODD | CREAD | CLOCAL | HUPCL ), /* MMSeries */
+ (CS8 | CSTOPB | CREAD | CLOCAL | HUPCL ), /* Logitech */
+ 0, /* BusMouse */
+ (CS7 | CREAD | CLOCAL | HUPCL ), /* MouseMan */
+ 0, /* PS/2 */
+ (CS8 | CREAD | CLOCAL | HUPCL ), /* MMHitTablet */
+};
+
+
+typedef struct
+{
+ int
+ dx,dy,
+ buttons;
+} ACTIVITY;
+
+
+struct rodentparam
+{
+ int
+ baudrate,
+ samplerate,
+ flags,
+ rtype,
+ lastbuttons,
+ buttons,
+ mfd,
+ cleardtr,
+ clearrts;
+
+ char
+ *portname;
+
+} rodent = { baudrate : 1200,
+ samplerate : 0,
+ flags : 0,
+ rtype : R_UNKNOWN,
+ lastbuttons : 0,
+ buttons : 0,
+ mfd : -1,
+ portname : NULL,
+ cleardtr : 0,
+ clearrts : 0};
+
+#define ChordMiddle 1
+
+void r_init(void);
+ACTIVITY *r_protocol(u_char b);
+void setmousespeed(int old, int new, unsigned cflag);
+
+void
+main(int argc, char *argv[])
+{
+ int c,i,cfd;
+ u_char b;
+ ACTIVITY *act;
+ struct mouse_info mouse;
+ fd_set fds;
+
+ while((c = getopt(argc,argv,"cdfr:sp:t:h?RDS:")) != -1)
+ switch(c)
+ {
+ case 'c':
+ rodent.flags |= ChordMiddle;
+ break;
+
+ case 'd':
+ debug = 1;
+ break;
+
+ case 'f':
+ nodaemon = 1;
+ break;
+
+ case 'p':
+ rodent.portname = optarg;
+ break;
+
+ case 'r':
+ rodent.samplerate = atoi(optarg);
+ break;
+
+ case 's':
+ rodent.baudrate = 9600;
+ break;
+
+ case 'R':
+ rodent.clearrts = 1;
+ break;
+
+ case 'D':
+ rodent.cleardtr = 1;
+ break;
+
+ case 'S':
+ rodent.baudrate = atoi(optarg);
+ debug("rodent baudrate %d", rodent.baudrate);
+ break;
+
+ case 't':
+ for (i = 0; rnames[i]; i++)
+ if (!strcmp(optarg,rnames[i]))
+ {
+ debug("rodent is %s",rnames[i]);
+ rodent.rtype = i;
+ break;
+ }
+ if (rnames[i])
+ break;
+ warnx("no such mouse type `%s'",optarg);
+ usage();
+
+ case 'h':
+ case '?':
+ default:
+ usage();
+ }
+
+ switch(rodent.rtype)
+ {
+ case R_BUSMOUSE:
+ if (!rodent.portname)
+ rodent.portname = "/dev/mse0";
+ break;
+ case R_PS_2:
+ if (!rodent.portname)
+ rodent.portname = "/dev/psm0";
+ break;
+ default:
+ if (rodent.portname)
+ break;
+ warnx("no port name specified");
+ usage();
+ }
+
+ if ((rodent.mfd = open(rodent.portname, O_RDWR, 0)) == -1)
+ {
+ warn("can't open %s",rodent.portname);
+ usage();
+ }
+ r_init(); /* call init function */
+
+ if ((cfd = open("/dev/consolectl", O_RDWR, 0)) == -1)
+ err(1, "open(/dev/consolectl)");
+
+ if (!nodaemon)
+ if (daemon(0,0))
+ {
+ err(1, "daemon() failed");
+ }
+
+ for(;;)
+ {
+ FD_ZERO(&fds);
+ FD_SET(rodent.mfd,&fds);
+ select(FD_SETSIZE,&fds,NULL,&fds,NULL);
+ i = read(rodent.mfd,&b,1); /* get a byte */
+ if (i != 1) /* read returned or error; goodbye */
+ {
+ debug("read returned %d : %s exiting",i,strerror(errno));
+ close(rodent.mfd);
+ exit(1);
+ }
+ act = r_protocol(b); /* pass byte to handler */
+ if (act) /* handler detected action */
+ {
+ mouse.operation = MOUSE_ACTION;
+ mouse.u.data.x = act->dx;
+ mouse.u.data.y = act->dy;
+ mouse.u.data.buttons = act->buttons;
+ ioctl(cfd, CONS_MOUSECTL, &mouse);
+ debug("activity : buttons 0x%02x dx %d dy %d",
+ act->buttons,act->dx,act->dy);
+ }
+ }
+}
+
+
+/**
+ ** usage
+ **
+ ** Complain, and free the CPU for more worthy tasks
+ **/
+void
+usage(void)
+{
+ fprintf(stderr, "%s\n%s\n",
+ "usage: moused [-DRcdfs] [-r samplerate] [-S baudrate]",
+ " -p <port> -t <mousetype>");
+ exit(1);
+}
+
+
+/**
+ ** Mouse interface code, courtesy of XFree86 3.1.2.
+ **
+ ** Note: Various bits have been trimmed, and in my shortsighted enthusiasm
+ ** to clean, reformat and rationalise naming, it's quite possible that
+ ** some things in here have been broken.
+ **
+ ** I hope not 8)
+ **
+ ** The following code is derived from a module marked :
+ **/
+
+/* $XConsortium: xf86_Mouse.c,v 1.2 94/10/12 20:33:21 kaleb Exp $ */
+/* $XFree86: xc/programs/Xserver/hw/xfree86/common/xf86_Mouse.c,v 3.2 1995/01/28
+ 17:03:40 dawes Exp $ */
+/*
+ *
+ * Copyright 1990,91 by Thomas Roell, Dinkelscherben, Germany.
+ * Copyright 1993 by David Dawes <dawes@physics.su.oz.au>
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the names of Thomas Roell and David Dawes not be
+ * used in advertising or publicity pertaining to distribution of the
+ * software without specific, written prior permission. Thomas Roell
+ * and David Dawes makes no representations about the suitability of this
+ * software for any purpose. It is provided "as is" without express or
+ * implied warranty.
+ *
+ * THOMAS ROELL AND DAVID DAWES DISCLAIM ALL WARRANTIES WITH REGARD TO THIS
+ * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
+ * FITNESS, IN NO EVENT SHALL THOMAS ROELL OR DAVID DAWES BE LIABLE FOR ANY
+ * SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
+ * RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
+ * CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+
+void
+r_init(void)
+{
+ /**
+ ** 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.)
+ */
+
+
+ if (rodent.rtype == R_LOGIMAN)
+ {
+ setmousespeed(1200, 1200, rodentcflags[R_LOGIMAN]);
+ write(rodent.mfd, "*X", 2);
+ setmousespeed(1200, rodent.baudrate, rodentcflags[R_LOGIMAN]);
+ } else {
+ if ((rodent.rtype != R_BUSMOUSE) && (rodent.rtype != R_PS_2))
+ {
+ /* 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]);
+
+ if (rodent.rtype == R_LOGITECH) {
+ write(rodent.mfd, "S", 1);
+ setmousespeed(rodent.baudrate, rodent.baudrate,
+ rodentcflags[R_MMSERIES]);
+ }
+
+ if (rodent.rtype == R_MMHITAB) {
+ char speedcmd;
+ /*
+ * 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);
+
+ /* These sample rates translate to 'lines per inch' on the
+ Hitachi tablet */
+ if (rodent.samplerate <= 40) speedcmd = 'g';
+ else if (rodent.samplerate <= 100) speedcmd = 'd';
+ else if (rodent.samplerate <= 200) speedcmd = 'e';
+ else if (rodent.samplerate <= 500) speedcmd = 'h';
+ else if (rodent.samplerate <= 1000) speedcmd = 'j';
+ else speedcmd = 'd';
+ write(rodent.mfd, &speedcmd, 1);
+ usleep(50000);
+
+ write(rodent.mfd, "\021", 1); /* Resume DATA output */
+ } else {
+ if (rodent.samplerate <= 0) write(rodent.mfd, "O", 1);
+ else if (rodent.samplerate <= 15) write(rodent.mfd, "J", 1);
+ else if (rodent.samplerate <= 27) write(rodent.mfd, "K", 1);
+ else if (rodent.samplerate <= 42) write(rodent.mfd, "L", 1);
+ else if (rodent.samplerate <= 60) write(rodent.mfd, "R", 1);
+ else if (rodent.samplerate <= 85) write(rodent.mfd, "M", 1);
+ else if (rodent.samplerate <= 125) write(rodent.mfd, "Q", 1);
+ else write(rodent.mfd, "N", 1);
+ }
+ }
+ }
+ if (rodent.rtype == R_MOUSESYS && (rodent.cleardtr))
+ {
+ int val = TIOCM_DTR;
+ ioctl(rodent.mfd, TIOCMBIC, &val);
+ }
+ if (rodent.rtype == R_MOUSESYS && (rodent.clearrts))
+ {
+ int val = TIOCM_RTS;
+ ioctl(rodent.mfd, TIOCMBIC, &val);
+ }
+}
+
+ACTIVITY *
+r_protocol(u_char rBuf)
+{
+ static int pBufP = 0;
+ static unsigned char pBuf[8];
+ static ACTIVITY act;
+
+ static unsigned char proto[10][5] = {
+ /* hd_mask hd_id dp_mask dp_id nobytes */
+ { 0, 0, 0, 0, 0 }, /* nomouse */
+ { 0x40, 0x40, 0x40, 0x00, 3 }, /* MicroSoft */
+ { 0xf8, 0x80, 0x00, 0x00, 5 }, /* MouseSystems */
+ { 0xe0, 0x80, 0x80, 0x00, 3 }, /* MMSeries */
+ { 0xe0, 0x80, 0x80, 0x00, 3 }, /* Logitech */
+ { 0xf8, 0x80, 0x00, 0x00, 5 }, /* BusMouse */
+ { 0x40, 0x40, 0x40, 0x00, 3 }, /* MouseMan */
+ { 0xc0, 0x00, 0x00, 0x00, 3 }, /* PS/2 mouse */
+ { 0xe0, 0x80, 0x80, 0x00, 3 }, /* MM_HitTablet */
+ };
+
+ debug("received char 0x%x",(int)rBuf);
+
+ /*
+ * 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 != R_PS_2 &&
+ ((rBuf & proto[rodent.rtype][2]) != proto[rodent.rtype][3]
+ || rBuf == 0x80))
+ {
+ pBufP = 0; /* skip package */
+ }
+
+ if (pBufP == 0 &&
+ (rBuf & proto[rodent.rtype][0]) != proto[rodent.rtype][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.
+ */
+ if ((rodent.rtype == R_MICROSOFT || rodent.rtype == R_LOGIMAN)
+ && (char)(rBuf & ~0x23) == 0)
+ {
+ act.buttons = ((int)(rBuf & 0x20) >> 4)
+ | (rodent.lastbuttons & 0x05);
+ rodent.lastbuttons = act.buttons; /* save new button state */
+ return(&act);
+ }
+
+ return(NULL); /* skip package */
+ }
+
+ pBuf[pBufP++] = rBuf;
+ if (pBufP != proto[rodent.rtype][4]) return(NULL);
+
+ /*
+ * assembly full package
+ */
+
+ debug("assembled full packet (len %d) %x,%x,%x,%x,%x",
+ proto[rodent.rtype][4], pBuf[0],pBuf[1],pBuf[2],pBuf[3],pBuf[4]);
+
+ switch(rodent.rtype)
+ {
+ case R_LOGIMAN: /* MouseMan / TrackMan */
+ case R_MICROSOFT: /* Microsoft */
+ if (rodent.flags & ChordMiddle)
+ act.buttons = (((int) pBuf[0] & 0x30) == 0x30) ? 2 :
+ ((int)(pBuf[0]&0x20)>>3) | ((int)(pBuf[0]&0x10)>>4);
+ else
+ act.buttons = (rodent.lastbuttons & 2)
+ | ((int)(pBuf[0] & 0x20) >> 3)
+ | ((int)(pBuf[0] & 0x10) >> 4);
+ act.dx = (char)(((pBuf[0] & 0x03) << 6) | (pBuf[1] & 0x3F));
+ act.dy = (char)(((pBuf[0] & 0x0C) << 4) | (pBuf[2] & 0x3F));
+ break;
+
+ case R_MOUSESYS: /* Mouse Systems Corp */
+ act.buttons = (~pBuf[0]) & 0x07;
+ act.dx = (char)(pBuf[1]) + (char)(pBuf[3]);
+ act.dy = - ((char)(pBuf[2]) + (char)(pBuf[4]));
+ break;
+
+ case R_MMHITAB: /* MM_HitTablet */
+ act.buttons = pBuf[0] & 0x07;
+ if (act.buttons != 0)
+ act.buttons = 1 << (act.buttons - 1);
+ act.dx = (pBuf[0] & 0x10) ? pBuf[1] : - pBuf[1];
+ act.dy = (pBuf[0] & 0x08) ? - pBuf[2] : pBuf[2];
+ break;
+
+ case R_MMSERIES: /* MM Series */
+ case R_LOGITECH: /* Logitech Mice */
+ act.buttons = pBuf[0] & 0x07;
+ act.dx = (pBuf[0] & 0x10) ? pBuf[1] : - pBuf[1];
+ act.dy = (pBuf[0] & 0x08) ? - pBuf[2] : pBuf[2];
+ break;
+
+ case R_BUSMOUSE: /* BusMouse */
+ act.buttons = (~pBuf[0]) & 0x07;
+ act.dx = (char)pBuf[1];
+ act.dy = - (char)pBuf[2];
+ break;
+
+ case R_PS_2: /* PS/2 mouse */
+ act.buttons = (pBuf[0] & 0x04) >> 1 | /* Middle */
+ (pBuf[0] & 0x02) >> 1 | /* Right */
+ (pBuf[0] & 0x01) << 2; /* Left */
+ act.dx = (pBuf[0] & 0x10) ? pBuf[1]-256 : pBuf[1];
+ act.dy = (pBuf[0] & 0x20) ? -(pBuf[2]-256) : -pBuf[2];
+ break;
+ }
+ pBufP = 0;
+ return(&act);
+}
+
+/* $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.
+ *
+ */
+
+
+void
+setmousespeed(old, new, cflag)
+int old;
+int new;
+unsigned cflag;
+{
+ struct termios tty;
+ char *c;
+
+ if (tcgetattr(rodent.mfd, &tty) < 0)
+ {
+ err(1, "warning: unable to get status of mouse fd");
+ }
+
+ 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)
+ {
+ err(1, "unable to set status of mouse fd");
+ }
+
+ 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 == R_LOGIMAN || rodent.rtype == R_LOGITECH)
+ {
+ if (write(rodent.mfd, c, 2) != 2)
+ {
+ err(1, "unable to write to mouse fd");
+ }
+ }
+ usleep(100000);
+
+ if (tcsetattr(rodent.mfd, TCSADRAIN, &tty) < 0)
+ {
+ err(1,"unable to set status of mouse fd");
+ }
+}
diff --git a/usr.sbin/mptable/Makefile b/usr.sbin/mptable/Makefile
new file mode 100644
index 0000000..039e78c
--- /dev/null
+++ b/usr.sbin/mptable/Makefile
@@ -0,0 +1,8 @@
+# $Id$
+
+PROG = mptable
+MAN1 = mptable.1
+
+BINMODE = 550
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/mptable/mptable.1 b/usr.sbin/mptable/mptable.1
new file mode 100644
index 0000000..19c6604
--- /dev/null
+++ b/usr.sbin/mptable/mptable.1
@@ -0,0 +1,65 @@
+.\" Copyright (c) 1996
+.\" Steve Passe <fsmp@FreeBSD.ORG>. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. The name of the developer may NOT be used to endorse or promote products
+.\" derived from this software without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.Dd April 28, 1997
+.Dt MPTABLE 1
+.Os
+.Sh NAME
+.Nm mptable
+.Nd display MP configuration table
+.Sh SYNOPSIS
+.Nm mptable
+.Op Fl dmesg
+.Op Fl verbose
+.Op Fl grope
+.Op Fl help
+.Sh DESCRIPTION
+The
+.Nm
+command finds and analyzes the MP configuration table on
+an Intel(tm) MP spec capable motherboard.
+It is useful both for determining kernel config options and debugging
+an SMP kernel that will not boot. It can be run with a UniProcessor kernel.
+.Pp
+It must be run as root.
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl dmesg
+Include a dmesg dump.
+.It Fl grope
+Look in areas it shouldn't NEED to, use ONLY as a last resort.
+.It Fl help
+Print a usage message and exits.
+.It Fl verbose
+Print extra info.
+.El
+.Sh SEE ALSO
+.Xr smp 4 ,
+.Xr dmesg 8
+.Sh HISTORY
+The
+.Nm
+command first appeared in
+.Fx 3.0 .
+.Sh AUTHORS
+.An Steve Passe Aq fsmp@FreeBSD.ORG
diff --git a/usr.sbin/mptable/mptable.c b/usr.sbin/mptable/mptable.c
new file mode 100644
index 0000000..1f2f519
--- /dev/null
+++ b/usr.sbin/mptable/mptable.c
@@ -0,0 +1,1133 @@
+/*
+ * Copyright (c) 1996, by Steve Passe
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. The name of the developer may NOT be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * mptable.c
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id$";
+#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 <err.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+
+#include <machine/types.h>
+
+
+#define SEP_LINE \
+"\n-------------------------------------------------------------------------------\n"
+
+#define SEP_LINE2 \
+"\n===============================================================================\n"
+
+/* EBDA is @ 40:0e in real-mode terms */
+#define EBDA_POINTER 0x040e /* location of EBDA pointer */
+
+/* CMOS 'top of mem' is @ 40:13 in real-mode terms */
+#define TOPOFMEM_POINTER 0x0413 /* BIOS: base memory size */
+
+#define DEFAULT_TOPOFMEM 0xa0000
+
+#define BIOS_BASE 0xf0000
+#define BIOS_BASE2 0xe0000
+#define BIOS_SIZE 0x10000
+#define ONE_KBYTE 1024
+
+#define GROPE_AREA1 0x80000
+#define GROPE_AREA2 0x90000
+#define GROPE_SIZE 0x10000
+
+#define PROCENTRY_FLAG_EN 0x01
+#define PROCENTRY_FLAG_BP 0x02
+#define IOAPICENTRY_FLAG_EN 0x01
+
+#define MAXPNSTR 132
+
+enum busTypes {
+ CBUS = 1,
+ CBUSII = 2,
+ EISA = 3,
+ ISA = 6,
+ PCI = 13,
+ XPRESS = 18,
+ MAX_BUSTYPE = 18,
+ UNKNOWN_BUSTYPE = 0xff
+};
+
+typedef struct BUSTYPENAME {
+ u_char type;
+ char name[ 7 ];
+} busTypeName;
+
+static busTypeName busTypeTable[] =
+{
+ { CBUS, "CBUS" },
+ { CBUSII, "CBUSII" },
+ { EISA, "EISA" },
+ { UNKNOWN_BUSTYPE, "---" },
+ { UNKNOWN_BUSTYPE, "---" },
+ { ISA, "ISA" },
+ { UNKNOWN_BUSTYPE, "---" },
+ { UNKNOWN_BUSTYPE, "---" },
+ { UNKNOWN_BUSTYPE, "---" },
+ { UNKNOWN_BUSTYPE, "---" },
+ { UNKNOWN_BUSTYPE, "---" },
+ { UNKNOWN_BUSTYPE, "---" },
+ { PCI, "PCI" },
+ { UNKNOWN_BUSTYPE, "---" },
+ { UNKNOWN_BUSTYPE, "---" },
+ { UNKNOWN_BUSTYPE, "---" },
+ { UNKNOWN_BUSTYPE, "---" },
+ { UNKNOWN_BUSTYPE, "---" },
+ { UNKNOWN_BUSTYPE, "---" }
+};
+
+char* whereStrings[] = {
+ "Extended BIOS Data Area",
+ "BIOS top of memory",
+ "Default top of memory",
+ "BIOS",
+ "Extended BIOS",
+ "GROPE AREA #1",
+ "GROPE AREA #2"
+};
+
+typedef struct TABLE_ENTRY {
+ u_char type;
+ u_char length;
+ char name[ 32 ];
+} tableEntry;
+
+tableEntry basetableEntryTypes[] =
+{
+ { 0, 20, "Processor" },
+ { 1, 8, "Bus" },
+ { 2, 8, "I/O APIC" },
+ { 3, 8, "I/O INT" },
+ { 4, 8, "Local INT" }
+};
+
+tableEntry extendedtableEntryTypes[] =
+{
+ { 128, 20, "System Address Space" },
+ { 129, 8, "Bus Heirarchy" },
+ { 130, 8, "Compatibility Bus Address" }
+};
+
+/* MP Floating Pointer Structure */
+typedef struct MPFPS {
+ char signature[ 4 ];
+ void* pap;
+ u_char length;
+ u_char spec_rev;
+ u_char checksum;
+ u_char mpfb1;
+ u_char mpfb2;
+ u_char mpfb3;
+ u_char mpfb4;
+ u_char mpfb5;
+} mpfps_t;
+
+/* MP Configuration Table Header */
+typedef struct MPCTH {
+ char signature[ 4 ];
+ u_short base_table_length;
+ u_char spec_rev;
+ u_char checksum;
+ u_char oem_id[ 8 ];
+ u_char product_id[ 12 ];
+ void* oem_table_pointer;
+ u_short oem_table_size;
+ u_short entry_count;
+ void* apic_address;
+ u_short extended_table_length;
+ u_char extended_table_checksum;
+ u_char reserved;
+} mpcth_t;
+
+
+typedef struct PROCENTRY {
+ u_char type;
+ u_char apicID;
+ u_char apicVersion;
+ u_char cpuFlags;
+ u_long cpuSignature;
+ u_long featureFlags;
+ u_long reserved1;
+ u_long reserved2;
+} ProcEntry;
+
+typedef struct BUSENTRY {
+ u_char type;
+ u_char busID;
+ char busType[ 6 ];
+} BusEntry;
+
+typedef struct IOAPICENTRY {
+ u_char type;
+ u_char apicID;
+ u_char apicVersion;
+ u_char apicFlags;
+ void* apicAddress;
+} IOApicEntry;
+
+typedef struct INTENTRY {
+ u_char type;
+ u_char intType;
+ u_short intFlags;
+ u_char srcBusID;
+ u_char srcBusIRQ;
+ u_char dstApicID;
+ u_char dstApicINT;
+} IntEntry;
+
+
+/*
+ * extended entry type structures
+ */
+
+typedef struct SASENTRY {
+ u_char type;
+ u_char length;
+ u_char busID;
+ u_char addressType;
+ u_int64_t addressBase;
+ u_int64_t addressLength;
+} SasEntry;
+
+
+typedef struct BHDENTRY {
+ u_char type;
+ u_char length;
+ u_char busID;
+ u_char busInfo;
+ u_char busParent;
+ u_char reserved[ 3 ];
+} BhdEntry;
+
+
+typedef struct CBASMENTRY {
+ u_char type;
+ u_char length;
+ u_char busID;
+ u_char addressMod;
+ u_int predefinedRange;
+} CbasmEntry;
+
+
+
+static void apic_probe( vm_offset_t* paddr, int* where );
+
+static void MPConfigDefault( int featureByte );
+
+static void MPFloatingPointer( vm_offset_t paddr, int where, mpfps_t* mpfps );
+static void MPConfigTableHeader( void* pap );
+
+static int readType( void );
+static void seekEntry( vm_offset_t addr );
+static void readEntry( void* entry, int size );
+
+static void processorEntry( void );
+static void busEntry( void );
+static void ioApicEntry( void );
+static void intEntry( void );
+
+static void sasEntry( void );
+static void bhdEntry( void );
+static void cbasmEntry( void );
+
+static void doOptionList( void );
+static void doDmesg( void );
+static void pnstr( char* s, int c );
+
+/* global data */
+int pfd; /* physical /dev/mem fd */
+
+int busses[ 16 ];
+int apics[ 16 ];
+
+int ncpu;
+int nbus;
+int napic;
+int nintr;
+
+int dmesg;
+int grope;
+int verbose;
+
+static void
+usage( void )
+{
+ fprintf( stderr, "usage: mptable [-dmesg] [-verbose] [-grope] [-help]\n" );
+ exit( 0 );
+}
+
+/*
+ *
+ */
+int
+main( int argc, char *argv[] )
+{
+ vm_offset_t paddr;
+ int where;
+ mpfps_t mpfps;
+ int defaultConfig;
+
+ extern int optreset;
+ int ch;
+
+ /* announce ourselves */
+ puts( SEP_LINE2 );
+
+ printf( "MPTable, version %d.%d.%d\n", VMAJOR, VMINOR, VDELTA );
+
+ while ((ch = getopt(argc, argv, "d:g:h:v:")) != EOF) {
+ switch(ch) {
+ case 'd':
+ if ( strcmp( optarg, "mesg") == 0 )
+ dmesg = 1;
+ else
+ dmesg = 0;
+ break;
+ case 'h':
+ if ( strcmp( optarg, "elp") == 0 )
+ usage();
+ break;
+ case 'g':
+ if ( strcmp( optarg, "rope") == 0 )
+ grope = 1;
+ break;
+ case 'v':
+ if ( strcmp( optarg, "erbose") == 0 )
+ verbose = 1;
+ break;
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+ optreset = 1;
+ optind = 0;
+ }
+
+ /* open physical memory for access to MP structures */
+ if ( (pfd = open( "/dev/mem", O_RDONLY )) < 0 )
+ err( 1, "mem open" );
+
+ /* probe for MP structures */
+ apic_probe( &paddr, &where );
+ if ( where <= 0 ) {
+ fprintf( stderr, "\n MP FPS NOT found,\n" );
+ fprintf( stderr, " suggest trying -grope option!!!\n\n" );
+ return 1;
+ }
+
+ if ( verbose )
+ printf( "\n MP FPS found in %s @ physical addr: 0x%08x\n",
+ whereStrings[ where - 1 ], paddr );
+
+ puts( SEP_LINE );
+
+ /* analyze the MP Floating Pointer Structure */
+ MPFloatingPointer( paddr, where, &mpfps );
+
+ puts( SEP_LINE );
+
+ /* check whether an MP config table exists */
+ if ( (defaultConfig = mpfps.mpfb1) )
+ MPConfigDefault( defaultConfig );
+ else
+ MPConfigTableHeader( mpfps.pap );
+
+ /* build "options" entries for the kernel config file */
+ doOptionList();
+
+ /* do a dmesg output */
+ if ( dmesg )
+ doDmesg();
+
+ puts( SEP_LINE2 );
+
+ return 0;
+}
+
+
+/*
+ * set PHYSICAL address of MP floating pointer structure
+ */
+#define NEXT(X) ((X) += 4)
+static void
+apic_probe( vm_offset_t* paddr, int* where )
+{
+ /*
+ * c rewrite of apic_probe() by Jack F. Vogel
+ */
+
+ int x;
+ u_short segment;
+ vm_offset_t target;
+ u_int buffer[ BIOS_SIZE / sizeof( int ) ];
+
+ if ( verbose )
+ printf( "\n" );
+
+ /* search Extended Bios Data Area, if present */
+ if ( verbose )
+ printf( " looking for EBDA pointer @ 0x%04x, ", EBDA_POINTER );
+ seekEntry( (vm_offset_t)EBDA_POINTER );
+ readEntry( &segment, 2 );
+ if ( segment ) { /* search EBDA */
+ target = (vm_offset_t)segment << 4;
+ if ( verbose )
+ printf( "found, searching EBDA @ 0x%08x\n", target );
+ seekEntry( target );
+ readEntry( buffer, ONE_KBYTE );
+
+ for ( x = 0; x < ONE_KBYTE / sizeof ( unsigned int ); NEXT(x) ) {
+ if ( buffer[ x ] == MP_SIG ) {
+ *where = 1;
+ *paddr = (x * sizeof( unsigned int )) + target;
+ return;
+ }
+ }
+ }
+ else {
+ if ( verbose )
+ printf( "NOT found\n" );
+ }
+
+ /* read CMOS for real top of mem */
+ seekEntry( (vm_offset_t)TOPOFMEM_POINTER );
+ readEntry( &segment, 2 );
+ --segment; /* less ONE_KBYTE */
+ target = segment * 1024;
+ if ( verbose )
+ printf( " searching CMOS 'top of mem' @ 0x%08x (%dK)\n",
+ target, segment );
+ seekEntry( target );
+ readEntry( buffer, ONE_KBYTE );
+
+ for ( x = 0; x < ONE_KBYTE / sizeof ( unsigned int ); NEXT(x) ) {
+ if ( buffer[ x ] == MP_SIG ) {
+ *where = 2;
+ *paddr = (x * sizeof( unsigned int )) + target;
+ return;
+ }
+ }
+
+ /* we don't necessarily believe CMOS, check base of the last 1K of 640K */
+ if ( target != (DEFAULT_TOPOFMEM - 1024)) {
+ target = (DEFAULT_TOPOFMEM - 1024);
+ if ( verbose )
+ printf( " searching default 'top of mem' @ 0x%08x (%dK)\n",
+ target, (target / 1024) );
+ seekEntry( target );
+ readEntry( buffer, ONE_KBYTE );
+
+ for ( x = 0; x < ONE_KBYTE / sizeof ( unsigned int ); NEXT(x) ) {
+ if ( buffer[ x ] == MP_SIG ) {
+ *where = 3;
+ *paddr = (x * sizeof( unsigned int )) + target;
+ return;
+ }
+ }
+ }
+
+ /* search the BIOS */
+ if ( verbose )
+ printf( " searching BIOS @ 0x%08x\n", BIOS_BASE );
+ seekEntry( BIOS_BASE );
+ readEntry( buffer, BIOS_SIZE );
+
+ for ( x = 0; x < BIOS_SIZE / sizeof( unsigned int ); NEXT(x) ) {
+ if ( buffer[ x ] == MP_SIG ) {
+ *where = 4;
+ *paddr = (x * sizeof( unsigned int )) + BIOS_BASE;
+ return;
+ }
+ }
+
+ /* search the extended BIOS */
+ if ( verbose )
+ printf( " searching extended BIOS @ 0x%08x\n", BIOS_BASE2 );
+ seekEntry( BIOS_BASE2 );
+ readEntry( buffer, BIOS_SIZE );
+
+ for ( x = 0; x < BIOS_SIZE / sizeof( unsigned int ); NEXT(x) ) {
+ if ( buffer[ x ] == MP_SIG ) {
+ *where = 5;
+ *paddr = (x * sizeof( unsigned int )) + BIOS_BASE2;
+ return;
+ }
+ }
+
+ if ( grope ) {
+ /* search additional memory */
+ target = GROPE_AREA1;
+ if ( verbose )
+ printf( " groping memory @ 0x%08x\n", target );
+ seekEntry( target );
+ readEntry( buffer, GROPE_SIZE );
+
+ for ( x = 0; x < GROPE_SIZE / sizeof( unsigned int ); NEXT(x) ) {
+ if ( buffer[ x ] == MP_SIG ) {
+ *where = 6;
+ *paddr = (x * sizeof( unsigned int )) + GROPE_AREA1;
+ return;
+ }
+ }
+
+ target = GROPE_AREA2;
+ if ( verbose )
+ printf( " groping memory @ 0x%08x\n", target );
+ seekEntry( target );
+ readEntry( buffer, GROPE_SIZE );
+
+ for ( x = 0; x < GROPE_SIZE / sizeof( unsigned int ); NEXT(x) ) {
+ if ( buffer[ x ] == MP_SIG ) {
+ *where = 7;
+ *paddr = (x * sizeof( unsigned int )) + GROPE_AREA2;
+ return;
+ }
+ }
+ }
+
+ *where = 0;
+ *paddr = (vm_offset_t)0;
+}
+
+
+/*
+ *
+ */
+static void
+MPFloatingPointer( vm_offset_t paddr, int where, mpfps_t* mpfps )
+{
+
+ /* read in mpfps structure*/
+ seekEntry( paddr );
+ readEntry( mpfps, sizeof( mpfps_t ) );
+
+ /* show its contents */
+ printf( "MP Floating Pointer Structure:\n\n" );
+
+ printf( " location:\t\t\t" );
+ switch ( where )
+ {
+ case 1:
+ printf( "EBDA\n" );
+ break;
+ case 2:
+ printf( "BIOS base memory\n" );
+ break;
+ case 3:
+ printf( "DEFAULT base memory (639K)\n" );
+ break;
+ case 4:
+ printf( "BIOS\n" );
+ break;
+ case 5:
+ printf( "Extended BIOS\n" );
+ break;
+
+ case 0:
+ printf( "NOT found!\n" );
+ exit( 1 );
+ default:
+ printf( "BOGUS!\n" );
+ exit( 1 );
+ }
+ printf( " physical address:\t\t0x%08x\n", paddr );
+
+ printf( " signature:\t\t\t'" );
+ pnstr( mpfps->signature, 4 );
+ printf( "'\n" );
+
+ printf( " length:\t\t\t%d bytes\n", mpfps->length * 16 );
+ printf( " version:\t\t\t1.%1d\n", mpfps->spec_rev );
+ printf( " checksum:\t\t\t0x%02x\n", mpfps->checksum );
+
+ /* bits 0:6 are RESERVED */
+ if ( mpfps->mpfb2 & 0x7f ) {
+ printf( " warning, MP feature byte 2: 0x%02x\n", mpfps->mpfb2 );
+ }
+
+ /* bit 7 is IMCRP */
+ printf( " mode:\t\t\t\t%s\n", (mpfps->mpfb2 & 0x80) ?
+ "PIC" : "Virtual Wire" );
+
+ /* MP feature bytes 3-5 are expected to be ZERO */
+ if ( mpfps->mpfb3 )
+ printf( " warning, MP feature byte 3 NONZERO!\n" );
+ if ( mpfps->mpfb4 )
+ printf( " warning, MP feature byte 4 NONZERO!\n" );
+ if ( mpfps->mpfb5 )
+ printf( " warning, MP feature byte 5 NONZERO!\n" );
+}
+
+
+/*
+ *
+ */
+static void
+MPConfigDefault( int featureByte )
+{
+ printf( " MP default config type: %d\n\n", featureByte );
+ switch ( featureByte ) {
+ case 1:
+ printf( " bus: ISA, APIC: 82489DX\n" );
+ break;
+ case 2:
+ printf( " bus: EISA, APIC: 82489DX\n" );
+ break;
+ case 3:
+ printf( " bus: EISA, APIC: 82489DX\n" );
+ break;
+ case 4:
+ printf( " bus: MCA, APIC: 82489DX\n" );
+ break;
+ case 5:
+ printf( " bus: ISA+PCI, APIC: Integrated\n" );
+ break;
+ case 6:
+ printf( " bus: EISA+PCI, APIC: Integrated\n" );
+ break;
+ case 7:
+ printf( " bus: MCA+PCI, APIC: Integrated\n" );
+ break;
+ default:
+ printf( " future type\n" );
+ break;
+ }
+
+ switch ( featureByte ) {
+ case 1:
+ case 2:
+ case 3:
+ case 4:
+ nbus = 1;
+ break;
+ case 5:
+ case 6:
+ case 7:
+ nbus = 2;
+ break;
+ default:
+ printf( " future type\n" );
+ break;
+ }
+
+ ncpu = 2;
+ napic = 1;
+ nintr = 16;
+}
+
+
+/*
+ *
+ */
+static void
+MPConfigTableHeader( void* pap )
+{
+ vm_offset_t paddr;
+ mpcth_t cth;
+ int x;
+ int totalSize, t;
+ int count, c;
+ int type;
+
+ if ( pap == 0 ) {
+ printf( "MP Configuration Table Header MISSING!\n" );
+ exit( 1 );
+ }
+
+ /* convert physical address to virtual address */
+ paddr = (vm_offset_t)pap;
+
+ /* read in cth structure */
+ seekEntry( paddr );
+ readEntry( &cth, sizeof( cth ) );
+
+ printf( "MP Config Table Header:\n\n" );
+
+ printf( " physical address:\t\t0x%08x\n", pap );
+
+ printf( " signature:\t\t\t'" );
+ pnstr( cth.signature, 4 );
+ printf( "'\n" );
+
+ printf( " base table length:\t\t%d\n", cth.base_table_length );
+
+ printf( " version:\t\t\t1.%1d\n", cth.spec_rev );
+ printf( " checksum:\t\t\t0x%02x\n", cth.checksum );
+
+ printf( " OEM ID:\t\t\t'" );
+ pnstr( cth.oem_id, 8 );
+ printf( "'\n" );
+
+ printf( " Product ID:\t\t\t'" );
+ pnstr( cth.product_id, 12 );
+ printf( "'\n" );
+
+ printf( " OEM table pointer:\t\t0x%08x\n", cth.oem_table_pointer );
+ printf( " OEM table size:\t\t%d\n", cth.oem_table_size );
+
+ printf( " entry count:\t\t\t%d\n", cth.entry_count );
+
+ printf( " local APIC address:\t\t0x%08x\n", cth.apic_address );
+
+ printf( " extended table length:\t%d\n", cth.extended_table_length );
+ printf( " extended table checksum:\t%d\n", cth.extended_table_checksum );
+
+ totalSize = cth.base_table_length - sizeof( struct MPCTH );
+ count = cth.entry_count;
+
+ puts( SEP_LINE );
+
+ printf( "MP Config Base Table Entries:\n\n" );
+
+ /* initialze tables */
+ for ( x = 0; x < 16; ++x ) {
+ busses[ x ] = apics[ x ] = 0xff;
+ }
+
+ ncpu = 0;
+ nbus = 0;
+ napic = 0;
+ nintr = 0;
+
+ /* process all the CPUs */
+ printf( "--\nProcessors:\tAPIC ID\tVersion\tState"
+ "\t\tFamily\tModel\tStep\tFlags\n" );
+ for ( t = totalSize, c = count; c; c-- ) {
+ if ( readType() == 0 )
+ processorEntry();
+ totalSize -= basetableEntryTypes[ 0 ].length;
+ }
+
+ /* process all the busses */
+ printf( "--\nBus:\t\tBus ID\tType\n" );
+ for ( t = totalSize, c = count; c; c-- ) {
+ if ( readType() == 1 )
+ busEntry();
+ totalSize -= basetableEntryTypes[ 1 ].length;
+ }
+
+ /* process all the apics */
+ printf( "--\nI/O APICs:\tAPIC ID\tVersion\tState\t\tAddress\n" );
+ for ( t = totalSize, c = count; c; c-- ) {
+ if ( readType() == 2 )
+ ioApicEntry();
+ totalSize -= basetableEntryTypes[ 2 ].length;
+ }
+
+ /* process all the I/O Ints */
+ printf( "--\nI/O Ints:\tType\tPolarity Trigger\tBus ID\t IRQ\tAPIC ID\tPIN#\n" );
+ for ( t = totalSize, c = count; c; c-- ) {
+ if ( readType() == 3 )
+ intEntry();
+ totalSize -= basetableEntryTypes[ 3 ].length;
+ }
+
+ /* process all the Local Ints */
+ printf( "--\nLocal Ints:\tType\tPolarity Trigger\tBus ID\t IRQ\tAPIC ID\tPIN#\n" );
+ for ( t = totalSize, c = count; c; c-- ) {
+ if ( readType() == 4 )
+ intEntry();
+ totalSize -= basetableEntryTypes[ 4 ].length;
+ }
+
+
+#if defined( EXTENDED_PROCESSING_READY )
+ /* process any extended data */
+ if ( (totalSize = cth.extended_table_length) ) {
+ puts( SEP_LINE );
+
+ printf( "MP Config Extended Table Entries:\n\n" );
+
+ while ( totalSize > 0 ) {
+ switch ( type = readType() ) {
+ case 128:
+ sasEntry();
+ break;
+ case 129:
+ bhdEntry();
+ break;
+ case 130:
+ cbasmEntry();
+ break;
+ default:
+ printf( "Extended Table HOSED!\n" );
+ exit( 1 );
+ }
+
+ totalSize -= extendedtableEntryTypes[ type-128 ].length;
+ }
+ }
+#endif /* EXTENDED_PROCESSING_READY */
+
+ /* process any OEM data */
+ if ( cth.oem_table_pointer && (cth.oem_table_size > 0) ) {
+#if defined( OEM_PROCESSING_READY )
+# error your on your own here!
+ /* convert OEM table pointer to virtual address */
+ poemtp = (vm_offset_t)cth.oem_table_pointer;
+
+ /* read in oem table structure */
+ if ( (oemdata = (void*)malloc( cth.oem_table_size )) == NULL )
+ err( 1, "oem malloc" );
+
+ seekEntry( poemtp );
+ readEntry( oemdata, cth.oem_table_size );
+
+ /** process it */
+
+ free( oemdata );
+#else
+ printf( "\nyou need to modify the source to handle OEM data!\n\n" );
+#endif /* OEM_PROCESSING_READY */
+ }
+
+ fflush( stdout );
+
+#if defined( RAW_DUMP )
+{
+ int ofd;
+ u_char dumpbuf[ 4096 ];
+
+ ofd = open( "/tmp/mpdump", O_CREAT | O_RDWR );
+ seekEntry( paddr );
+ readEntry( dumpbuf, 1024 );
+ write( ofd, dumpbuf, 1024 );
+ close( ofd );
+}
+#endif /* RAW_DUMP */
+}
+
+
+/*
+ *
+ */
+static int
+readType( void )
+{
+ u_char type;
+
+ if ( read( pfd, &type, sizeof( u_char ) ) != sizeof( u_char ) )
+ err( 1, "type read; pfd: %d", pfd );
+
+ if ( lseek( pfd, -1, SEEK_CUR ) < 0 )
+ err( 1, "type seek" );
+
+ return (int)type;
+}
+
+
+/*
+ *
+ */
+static void
+seekEntry( vm_offset_t addr )
+{
+ if ( lseek( pfd, (off_t)addr, SEEK_SET ) < 0 )
+ err( 1, "/dev/mem seek" );
+}
+
+
+/*
+ *
+ */
+static void
+readEntry( void* entry, int size )
+{
+ if ( read( pfd, entry, size ) != size )
+ err( 1, "readEntry" );
+}
+
+
+static void
+processorEntry( void )
+{
+ ProcEntry entry;
+
+ /* read it into local memory */
+ readEntry( &entry, sizeof( entry ) );
+
+ /* count it */
+ ++ncpu;
+
+ printf( "\t\t%2d", entry.apicID );
+ printf( "\t 0x%2x", entry.apicVersion );
+
+ printf( "\t %s, %s",
+ (entry.cpuFlags & PROCENTRY_FLAG_BP) ? "BSP" : "AP",
+ (entry.cpuFlags & PROCENTRY_FLAG_EN) ? "usable" : "unusable" );
+
+ printf( "\t %d\t %d\t %d",
+ (entry.cpuSignature >> 8) & 0x0f,
+ (entry.cpuSignature >> 4) & 0x0f,
+ entry.cpuSignature & 0x0f );
+
+ printf( "\t 0x%04x\n", entry.featureFlags );
+}
+
+
+/*
+ *
+ */
+static int
+lookupBusType( char* name )
+{
+ int x;
+
+ for ( x = 0; x < MAX_BUSTYPE; ++x )
+ if ( strcmp( busTypeTable[ x ].name, name ) == 0 )
+ return busTypeTable[ x ].type;
+
+ return UNKNOWN_BUSTYPE;
+}
+
+
+static void
+busEntry( void )
+{
+ int x;
+ char name[ 8 ];
+ char c;
+ BusEntry entry;
+
+ /* read it into local memory */
+ readEntry( &entry, sizeof( entry ) );
+
+ /* count it */
+ ++nbus;
+
+ printf( "\t\t%2d", entry.busID );
+ printf( "\t " ); pnstr( entry.busType, 6 ); printf( "\n" );
+
+ for ( x = 0; x < 6; ++x ) {
+ if ( (c = entry.busType[ x ]) == ' ' )
+ break;
+ name[ x ] = c;
+ }
+ name[ x ] = '\0';
+ busses[ entry.busID ] = lookupBusType( name );
+}
+
+
+static void
+ioApicEntry( void )
+{
+ IOApicEntry entry;
+
+ /* read it into local memory */
+ readEntry( &entry, sizeof( entry ) );
+
+ /* count it */
+ ++napic;
+
+ printf( "\t\t%2d", entry.apicID );
+ printf( "\t 0x%02x", entry.apicVersion );
+ printf( "\t %s",
+ (entry.apicFlags & IOAPICENTRY_FLAG_EN) ? "usable" : "unusable" );
+ printf( "\t\t 0x%x\n", entry.apicAddress );
+
+ apics[ entry.apicID ] = entry.apicID;
+}
+
+
+char* intTypes[] = {
+ "INT", "NMI", "SMI", "ExtINT"
+};
+
+char* polarityMode[] = {
+ "conforms", "active-hi", "reserved", "active-lo"
+};
+char* triggerMode[] = {
+ "conforms", "edge", "reserved", "level"
+};
+
+static void
+intEntry( void )
+{
+ IntEntry entry;
+
+ /* read it into local memory */
+ readEntry( &entry, sizeof( entry ) );
+
+ /* count it */
+ if ( (int)entry.type == 3 )
+ ++nintr;
+
+ printf( "\t\t%s", intTypes[ (int)entry.intType ] );
+
+ printf( "\t%9s", polarityMode[ (int)entry.intFlags & 0x03 ] );
+ printf( "%12s", triggerMode[ ((int)entry.intFlags >> 2) & 0x03 ] );
+
+ printf( "\t %5d", (int)entry.srcBusID );
+ if ( busses[ (int)entry.srcBusID ] == PCI )
+ printf( "\t%2d:%c",
+ ((int)entry.srcBusIRQ >> 2) & 0x1f,
+ ((int)entry.srcBusIRQ & 0x03) + 'A' );
+ else
+ printf( "\t %3d", (int)entry.srcBusIRQ );
+ printf( "\t %6d", (int)entry.dstApicID );
+ printf( "\t %3d\n", (int)entry.dstApicINT );
+}
+
+
+static void
+sasEntry( void )
+{
+ SasEntry entry;
+
+ /* read it into local memory */
+ readEntry( &entry, sizeof( entry ) );
+
+ printf( "--\n%s\n", extendedtableEntryTypes[ entry.type ].name );
+ printf( " bus ID: %d", entry.busID );
+ printf( " address type: " );
+ switch ( entry.addressType ) {
+ case 0:
+ printf( "I/O address\n" );
+ break;
+ case 1:
+ printf( "memory address\n" );
+ break;
+ case 2:
+ printf( "prefetch address\n" );
+ break;
+ default:
+ printf( "UNKNOWN type\n" );
+ break;
+ }
+
+ printf( " address base: 0x%qx\n", entry.addressBase );
+ printf( " address range: 0x%qx\n", entry.addressLength );
+}
+
+
+static void
+bhdEntry( void )
+{
+ BhdEntry entry;
+
+ /* read it into local memory */
+ readEntry( &entry, sizeof( entry ) );
+
+ printf( "--\n%s\n", extendedtableEntryTypes[ entry.type ].name );
+ printf( " bus ID: %d", entry.busID );
+ printf( " bus info: 0x%02x", entry.busInfo );
+ printf( " parent bus ID: %d", entry.busParent );
+}
+
+
+static void
+cbasmEntry( void )
+{
+ CbasmEntry entry;
+
+ /* read it into local memory */
+ readEntry( &entry, sizeof( entry ) );
+
+ printf( "--\n%s\n", extendedtableEntryTypes[ entry.type ].name );
+ printf( " bus ID: %d", entry.busID );
+ printf( " address modifier: %s\n", (entry.addressMod & 0x01) ?
+ "subtract" : "add" );
+ printf( " predefined range: 0x%08x", entry.predefinedRange );
+}
+
+
+/*
+ * do a dmesg output
+ */
+static void
+doDmesg( void )
+{
+ puts( SEP_LINE );
+
+ printf( "dmesg output:\n\n" );
+ fflush( stdout );
+ system( "dmesg" );
+}
+
+
+/*
+ * build "options" entries for the kernel config file
+ */
+static void
+doOptionList( void )
+{
+ puts( SEP_LINE );
+
+ printf( "# SMP kernel config file options:\n\n" );
+ printf( "\n# Required:\n" );
+ printf( "options SMP\t\t\t# Symmetric MultiProcessor Kernel\n" );
+ printf( "options APIC_IO\t\t\t# Symmetric (APIC) I/O\n" );
+
+ printf( "\n# Optional (built-in defaults will work in most cases):\n" );
+ printf( "#options NCPU=%d\t\t\t# number of CPUs\n", ncpu );
+ printf( "#options NBUS=%d\t\t\t# number of busses\n", nbus );
+ printf( "#options NAPIC=%d\t\t\t# number of IO APICs\n", napic );
+ printf( "#options NINTR=%d\t\t# number of INTs\n",
+ (nintr < 24) ? 24 : nintr );
+}
+
+
+/*
+ *
+ */
+static void
+pnstr( char* s, int c )
+{
+ char string[ MAXPNSTR + 1 ];
+
+ if ( c > MAXPNSTR )
+ c = MAXPNSTR;
+ strncpy( string, s, c );
+ string[ c ] = '\0';
+ printf( "%s", string );
+}
diff --git a/usr.sbin/mrouted/LICENSE b/usr.sbin/mrouted/LICENSE
new file mode 100644
index 0000000..ef7da47
--- /dev/null
+++ b/usr.sbin/mrouted/LICENSE
@@ -0,0 +1,48 @@
+
+The mrouted program is covered by the following license. Use of the
+mrouted program represents acceptance of these terms and conditions.
+
+1. STANFORD grants to LICENSEE a nonexclusive and nontransferable license
+to use, copy and modify the computer software ``mrouted'' (hereinafter
+called the ``Program''), upon the terms and conditions hereinafter set
+out and until Licensee discontinues use of the Licensed Program.
+
+2. LICENSEE acknowledges that the Program is a research tool still in
+the development state, that it is being supplied ``as is,'' without any
+accompanying services from STANFORD, and that this license is entered
+into in order to encourage scientific collaboration aimed at further
+development and application of the Program.
+
+3. LICENSEE may copy the Program and may sublicense others to use object
+code copies of the Program or any derivative version of the Program.
+All copies must contain all copyright and other proprietary notices found
+in the Program as provided by STANFORD. Title to copyright to the
+Program remains with STANFORD.
+
+4. LICENSEE may create derivative versions of the Program. LICENSEE
+hereby grants STANFORD a royalty-free license to use, copy, modify,
+distribute and sublicense any such derivative works. At the time
+LICENSEE provides a copy of a derivative version of the Program to a
+third party, LICENSEE shall provide STANFORD with one copy of the source
+code of the derivative version at no charge to STANFORD.
+
+5. STANFORD MAKES NO REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED.
+By way of example, but not limitation, STANFORD MAKES NO REPRESENTATION
+OR WARRANTIES OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR
+THAT THE USE OF THE LICENSED PROGRAM WILL NOT INFRINGE ANY PATENTS,
+COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS. STANFORD shall not be held liable
+for any liability nor for any direct, indirect or consequential damages
+with respect to any claim by LICENSEE or any third party on account of or
+arising from this Agreement or use of the Program.
+
+6. This agreement shall be construed, interpreted and applied in
+accordance with the State of California and any legal action arising
+out of this Agreement or use of the Program shall be filed in a court
+in the State of California.
+
+7. Nothing in this Agreement shall be construed as conferring rights to
+use in advertising, publicity or otherwise any trademark or the name
+of ``Stanford''.
+
+The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+Leland Stanford Junior University.
diff --git a/usr.sbin/mrouted/Makefile b/usr.sbin/mrouted/Makefile
new file mode 100644
index 0000000..862ba67
--- /dev/null
+++ b/usr.sbin/mrouted/Makefile
@@ -0,0 +1,5 @@
+# $Id$
+
+SUBDIR= common mrouted mrinfo map-mbone mtrace testrsrr
+
+.include <bsd.subdir.mk>
diff --git a/usr.sbin/mrouted/Makefile.inc b/usr.sbin/mrouted/Makefile.inc
new file mode 100644
index 0000000..dd437b1
--- /dev/null
+++ b/usr.sbin/mrouted/Makefile.inc
@@ -0,0 +1,2 @@
+.include "${.CURDIR}/../../Makefile.inc"
+CFLAGS+= -DRSRR
diff --git a/usr.sbin/mrouted/RELEASE b/usr.sbin/mrouted/RELEASE
new file mode 100644
index 0000000..7dff937
--- /dev/null
+++ b/usr.sbin/mrouted/RELEASE
@@ -0,0 +1,326 @@
+Id: README-3.8.mrouted,v 3.8 1995/11/29 22:23:02 fenner Rel
+
+ IP Multicast Extensions for BSD-Derived Unix Systems
+
+ Release 3.8
+ November 29, 1995
+
+ available from parcftp.xerox.com,
+ file pub/net-research/ipmulti/mrouted3.8.tar.Z
+ binaries pub/net-research/ipmulti/mrouted3.8-sparc-sunos41x.tar.Z
+ pub/net-research/ipmulti/mrouted3.8-sparc-solaris2.tar.Z
+ pub/net-research/ipmulti/mrouted3.8-i386-bsd.tar.Z
+ pub/net-research/ipmulti/mrouted3.8-alpha-osf1.tar.Z
+ pub/net-research/ipmulti/mrouted3.8-sgi-irix.tar.Z
+ pub/net-research/ipmulti/mrouted3.8-hp-hpux.tar.Z
+
+Note: The 3.8 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 or 3.7 .
+
+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/callout.c b/usr.sbin/mrouted/callout.c
new file mode 100644
index 0000000..395199a
--- /dev/null
+++ b/usr.sbin/mrouted/callout.c
@@ -0,0 +1,226 @@
+/*
+ * The mrouted program is covered by the license in the accompanying file
+ * named "LICENSE". Use of the mrouted program represents acceptance of
+ * the terms and conditions listed in that file.
+ *
+ * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+ * Leland Stanford Junior University.
+ *
+ *
+ * $Id$
+ */
+
+#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 */
+
+static int in_callout = 0;
+
+struct timeout_q {
+ struct timeout_q *next; /* next event */
+ int id;
+ cfunc_t func; /* function to call */
+ char *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;
+}
+
+
+/*
+ * signal handler for SIGALARM that is called once every second
+ */
+void
+age_callout_queue()
+{
+ struct timeout_q *ptr;
+
+ if (in_callout)
+ return;
+
+ in_callout = 1;
+ ptr = Q;
+
+ while (ptr) {
+ if (!ptr->time) {
+ /* timeout has happened */
+ Q = Q->next;
+
+ in_callout = 0;
+ if (ptr->func)
+ ptr->func(ptr->data);
+ in_callout = 1;
+
+ free(ptr);
+ ptr = Q;
+ }
+ else {
+ ptr->time --;
+#ifdef IGMP_DEBUG
+ log(LOG_DEBUG,0,"[callout, age_callout_queue] -- time (%d)", ptr->time);
+#endif /* IGMP_DEBUG */
+ in_callout = 0; return;
+ }
+ }
+ in_callout = 0;
+ return;
+}
+
+
+/*
+ * 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 */
+ char *data; /* what to call the timeout function with */
+{
+ struct timeout_q *ptr, *node, *prev;
+
+ if (in_callout)
+ return -1;
+
+ in_callout = 1;
+
+ /* 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");
+ in_callout = 0;
+ 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();
+ in_callout = 0;
+ return node->id;
+ } else {
+ /* keep moving */
+
+ delay -= ptr->time; node->time = delay;
+ prev = ptr;
+ ptr = ptr->next;
+ }
+ }
+ prev->next = node;
+ }
+ print_Q();
+ in_callout = 0;
+ return node->id;
+}
+
+
+/* clears the associated timer */
+void
+timer_clearTimer(timer_id)
+ int timer_id;
+{
+ struct timeout_q *ptr, *prev;
+
+ if (in_callout)
+ return;
+ if (!timer_id)
+ return;
+
+ in_callout = 1;
+
+ 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;
+
+ free(ptr->data);
+ free(ptr);
+ print_Q();
+ in_callout = 0;
+ return;
+ }
+ prev = ptr;
+ ptr = ptr->next;
+ }
+ print_Q();
+ in_callout = 0;
+}
+
+#ifdef IGMP_DEBUG
+/*
+ * debugging utility
+ */
+static void
+print_Q()
+{
+ struct timeout_q *ptr;
+
+ for(ptr = Q; ptr; ptr = ptr->next)
+ log(LOG_DEBUG,0,"(%d,%d) ", ptr->id, ptr->time);
+}
+#endif /* IGMP_DEBUG */
+int
+secs_remaining( timer_id)
+ int timer_id;
+{
+ struct timeout_q *ptr;
+ int left=0;
+
+ for (ptr = Q; ptr && ptr->id != timer_id; ptr = ptr->next)
+ left += ptr->time;
+
+ if (!ptr) /* not found */
+ return 0;
+
+ return left + ptr->time;
+}
diff --git a/usr.sbin/mrouted/cfparse.y b/usr.sbin/mrouted/cfparse.y
new file mode 100644
index 0000000..e42064e
--- /dev/null
+++ b/usr.sbin/mrouted/cfparse.y
@@ -0,0 +1,655 @@
+%{
+/*
+ * Configuration file parser for mrouted.
+ *
+ * Written by Bill Fenner, NRL, 1994
+ *
+ * $Id$
+ */
+#include <stdio.h>
+#ifdef __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+#include "defs.h"
+#include <netdb.h>
+
+/*
+ * Local function declarations
+ */
+static void fatal __P((char *fmt, ...));
+static void warn __P((char *fmt, ...));
+static void yyerror __P((char *s));
+static char * next_word __P((void));
+static int yylex __P((void));
+static u_int32 valid_if __P((char *s));
+static struct ifreq * ifconfaddr __P((struct ifconf *ifcp, u_int32 a));
+int yyparse __P((void));
+
+static FILE *f;
+
+extern int udp_socket;
+char *configfilename = _PATH_MROUTED_CONF;
+
+extern int cache_lifetime;
+extern int max_prune_lifetime;
+
+static int lineno;
+static struct ifreq ifbuf[32];
+static struct ifconf ifc;
+
+static struct uvif *v;
+
+static int order;
+
+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;
+};
+
+%token CACHE_LIFETIME PRUNING
+%token PHYINT TUNNEL NAME
+%token DISABLE IGMPV1 SRCRT
+%token METRIC THRESHOLD RATE_LIMIT BOUNDARY NETMASK ALTNET ADVERT_METRIC
+%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
+
+%start conf
+
+%%
+
+conf : stmts
+ ;
+
+stmts : /* Empty */
+ | stmts stmt
+ ;
+
+stmt : error
+ | PHYINT interface {
+
+ vifi_t vifi;
+
+ 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));
+
+ strncpy(ffr.ifr_name, ifr->ifr_name, IFNAMSIZ);
+ if (ioctl(udp_socket, SIOCGIFFLAGS, (char *)&ffr)<0)
+ fatal("ioctl SIOCGIFFLAGS on %s",ffr.ifr_name);
+ if (ffr.ifr_flags & IFF_LOOPBACK)
+ fatal("Tunnel local address %s is a loopback interface",
+ 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",
+ inet_fmt($3,s1));
+ }
+
+ if (numvifs == MAXVIFS)
+ fatal("too many vifs");
+
+ v = &uvifs[numvifs];
+ v->uv_flags = VIFF_TUNNEL;
+ v->uv_metric = DEFAULT_METRIC;
+ v->uv_admetric = 0;
+ v->uv_rate_limit= DEFAULT_TUN_RATE_LIMIT;
+ v->uv_threshold = DEFAULT_THRESHOLD;
+ v->uv_lcl_addr = $2;
+ v->uv_rmt_addr = $3;
+ v->uv_subnet = 0;
+ v->uv_subnetmask= 0;
+ v->uv_subnetbcast= 0;
+ strncpy(v->uv_name, ffr.ifr_name, IFNAMSIZ);
+ v->uv_groups = NULL;
+ v->uv_neighbors = NULL;
+ v->uv_acl = NULL;
+ v->uv_addrs = NULL;
+
+ if (!(ffr.ifr_flags & IFF_UP)) {
+ v->uv_flags |= VIFF_DOWN;
+ vifs_down = TRUE;
+ }
+ }
+ tunnelmods
+ {
+ 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;
+ }
+ | PRUNING BOOLEAN { pruning = $2; }
+ | CACHE_LIFETIME NUMBER { cache_lifetime = $2;
+ max_prune_lifetime = cache_lifetime * 2;
+ }
+ | 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
+ | 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");
+
+ }
+ ;
+
+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");
+
+ }
+ ;
+
+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 {
+
+ 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; }
+ ;
+%%
+#ifdef __STDC__
+static void
+fatal(char *fmt, ...)
+{
+ va_list ap;
+ char buf[200];
+
+ va_start(ap, fmt);
+#else
+/*VARARGS1*/
+static void
+fatal(fmt, va_alist)
+char *fmt;
+va_dcl
+{
+ va_list ap;
+ char buf[200];
+
+ va_start(ap);
+#endif
+ vsprintf(buf, fmt, ap);
+ va_end(ap);
+
+ log(LOG_ERR,0,"%s: %s near line %d", configfilename, buf, lineno);
+}
+
+#ifdef __STDC__
+static void
+warn(char *fmt, ...)
+{
+ va_list ap;
+ char buf[200];
+
+ va_start(ap, fmt);
+#else
+/*VARARGS1*/
+static void
+warn(fmt, va_alist)
+char *fmt;
+va_dcl
+{
+ va_list ap;
+ char buf[200];
+
+ va_start(ap);
+#endif
+ vsprintf(buf, fmt, ap);
+ va_end(ap);
+
+ log(LOG_WARNING,0,"%s: %s near line %d", configfilename, buf, lineno);
+}
+
+static void
+yyerror(s)
+char *s;
+{
+ log(LOG_ERR, 0, "%s: %s near line %d", configfilename, s, lineno);
+}
+
+static char *
+next_word()
+{
+ static char buf[1024];
+ static char *p=NULL;
+ extern FILE *f;
+ 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;
+ }
+}
+
+static int
+yylex()
+{
+ int n;
+ u_int32 addr;
+ char *q;
+
+ if ((q = next_word()) == NULL) {
+ return 0;
+ }
+
+ if (!strcmp(q,"cache_lifetime"))
+ return CACHE_LIFETIME;
+ if (!strcmp(q,"pruning"))
+ return PRUNING;
+ if (!strcmp(q,"phyint"))
+ return PHYINT;
+ if (!strcmp(q,"tunnel"))
+ return TUNNEL;
+ if (!strcmp(q,"disable"))
+ return DISABLE;
+ if (!strcmp(q,"metric"))
+ return METRIC;
+ if (!strcmp(q,"advert_metric"))
+ return ADVERT_METRIC;
+ if (!strcmp(q,"threshold"))
+ return THRESHOLD;
+ if (!strcmp(q,"rate_limit"))
+ return RATE_LIMIT;
+ if (!strcmp(q,"srcrt") || !strcmp(q,"sourceroute"))
+ return SRCRT;
+ if (!strcmp(q,"boundary"))
+ return BOUNDARY;
+ if (!strcmp(q,"netmask"))
+ return NETMASK;
+ if (!strcmp(q,"igmpv1"))
+ return IGMPV1;
+ if (!strcmp(q,"altnet"))
+ return ALTNET;
+ if (!strcmp(q,"name"))
+ return NAME;
+ 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 (!strcmp(q,"sysName"))
+ return SYSNAM;
+ if (!strcmp(q,"sysContact"))
+ return SYSCONTACT;
+ if (!strcmp(q,"sysVersion"))
+ return SYSVERSION;
+ if (!strcmp(q,"sysLocation"))
+ return SYSLOCATION;
+ 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()
+{
+ extern FILE *f;
+
+ order = 0;
+ numbounds = 0;
+ lineno = 0;
+
+ if ((f = fopen(configfilename, "r")) == NULL) {
+ if (errno != ENOENT)
+ log(LOG_ERR, errno, "can't open %s", configfilename);
+ return;
+ }
+
+ ifc.ifc_buf = (char *)ifbuf;
+ ifc.ifc_len = sizeof(ifbuf);
+ if (ioctl(udp_socket, SIOCGIFCONF, (char *)&ifc) < 0)
+ log(LOG_ERR, errno, "ioctl SIOCGIFCONF");
+
+ 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);
+#if (defined(BSD) && (BSD >= 199006))
+ n = ifrp->ifr_addr.sa_len + sizeof(ifrp->ifr_name);
+ if (n < sizeof(*ifrp))
+ ++ifrp;
+ else
+ ifrp = (struct ifreq *)((char *)ifrp + n);
+#else
+ ++ifrp;
+#endif
+ }
+ return (0);
+}
diff --git a/usr.sbin/mrouted/common/Makefile b/usr.sbin/mrouted/common/Makefile
new file mode 100644
index 0000000..801a207
--- /dev/null
+++ b/usr.sbin/mrouted/common/Makefile
@@ -0,0 +1,17 @@
+# $Id: Makefile,v 1.3 1997/02/22 16:07:20 peter Exp $
+
+LIB= mrouted
+NOPROFILE= yes
+NOPIC= yes
+
+S= ${.CURDIR}/..
+.PATH: $S
+CFLAGS+= -I$S
+
+SRCS= igmp.c inet.c kern.c
+NOMAN=
+
+# nothing to install
+install:
+
+.include <bsd.lib.mk>
diff --git a/usr.sbin/mrouted/config.c b/usr.sbin/mrouted/config.c
new file mode 100644
index 0000000..9b843b2
--- /dev/null
+++ b/usr.sbin/mrouted/config.c
@@ -0,0 +1,148 @@
+/*
+ * The mrouted program is covered by the license in the accompanying file
+ * named "LICENSE". Use of the mrouted program represents acceptance of
+ * the terms and conditions listed in that file.
+ *
+ * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+ * Leland Stanford Junior University.
+ *
+ *
+ * $Id$
+ */
+
+
+#include "defs.h"
+
+
+/*
+ * 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 ifbuf[32];
+ struct ifreq *ifrp, *ifend;
+ struct ifconf ifc;
+ register struct uvif *v;
+ register vifi_t vifi;
+ int n;
+ u_int32 addr, mask, subnet;
+ short flags;
+
+ ifc.ifc_buf = (char *)ifbuf;
+ ifc.ifc_len = sizeof(ifbuf);
+ if (ioctl(udp_socket, SIOCGIFCONF, (char *)&ifc) < 0)
+ log(LOG_ERR, errno, "ioctl SIOCGIFCONF");
+
+ ifrp = (struct ifreq *)ifbuf;
+ ifend = (struct ifreq *)((char *)ifbuf + ifc.ifc_len);
+ /*
+ * Loop through all of the interfaces.
+ */
+ for (; ifrp < ifend; ifrp = (struct ifreq *)((char *)ifrp + n)) {
+ struct ifreq ifr;
+#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;
+
+ 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 ((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];
+ v->uv_flags = 0;
+ v->uv_metric = DEFAULT_METRIC;
+ v->uv_admetric = 0;
+ v->uv_rate_limit = DEFAULT_PHY_RATE_LIMIT;
+ v->uv_threshold = DEFAULT_THRESHOLD;
+ v->uv_lcl_addr = addr;
+ v->uv_rmt_addr = 0;
+ v->uv_subnet = subnet;
+ v->uv_subnetmask = mask;
+ v->uv_subnetbcast = subnet | ~mask;
+ strncpy(v->uv_name, ifr.ifr_name, IFNAMSIZ);
+ v->uv_groups = NULL;
+ v->uv_neighbors = NULL;
+ v->uv_acl = NULL;
+ v->uv_addrs = NULL;
+
+ 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..6d11925
--- /dev/null
+++ b/usr.sbin/mrouted/defs.h
@@ -0,0 +1,330 @@
+/*
+ * The mrouted program is covered by the license in the accompanying file
+ * named "LICENSE". Use of the mrouted program represents acceptance of
+ * the terms and conditions listed in that file.
+ *
+ * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+ * Leland Stanford Junior University.
+ *
+ *
+ * $Id$
+ */
+
+
+#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
+#include <sys/time.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/igmp.h>
+#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 "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 VENDOR_CODE 1 /* Get a new vendor code if you make significant
+ * changes to mrouted. */
+
+#define PROTOCOL_VERSION 3 /* increment when packet format/content changes */
+
+#define MROUTED_VERSION 8 /* increment on local changes or bug fixes, */
+ /* reset to 0 whever PROTOCOL_VERSION increments */
+
+#define MROUTED_LEVEL ((MROUTED_VERSION << 8) | PROTOCOL_VERSION | \
+ ((NF_PRUNE | NF_GENID | NF_MTRACE) << 16) | \
+ (VENDOR_CODE << 24))
+ /* 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)
+#define signal(s,f) sigset(s,f)
+#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 DEFAULT_DEBUG 2 /* default if "-d" given without value */
+
+extern int debug;
+extern u_char pruning;
+
+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 IGMP_MEMBERSHIP_QUERY
+#define IGMP_MEMBERSHIP_QUERY IGMP_HOST_MEMBERSHIP_QUERY
+#define IGMP_V1_MEMBERSHIP_REPORT IGMP_HOST_MEMBERSHIP_REPORT
+#define IGMP_V2_MEMBERSHIP_REPORT IGMP_HOST_NEW_MEMBERSHIP_REPORT
+#define IGMP_V2_LEAVE_GROUP IGMP_HOST_LEAVE_MESSAGE
+#endif
+
+
+/* main.c */
+extern void log __P((int, int, char *, ...));
+extern int register_input_handler __P((int fd, ihfunc_t func));
+
+/* igmp.c */
+extern void init_igmp __P((void));
+extern void accept_igmp __P((int recvlen));
+extern void send_igmp __P((u_int32 src, u_int32 dst, int type,
+ int code, u_int32 group,
+ int datalen));
+
+/* callout.c */
+extern void callout_init __P((void));
+extern void age_callout_queue __P((void));
+extern int timer_setTimer __P((int delay, cfunc_t action,
+ char *data));
+extern void timer_clearTimer __P((int timer_id));
+
+/* route.c */
+extern void init_routes __P((void));
+extern void start_route_updates __P((void));
+extern void update_route __P((u_int32 origin, u_int32 mask,
+ u_int metric, u_int32 src,
+ vifi_t vifi));
+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 src, u_int32 dst,
+ char *p, int datalen,
+ u_int32 level));
+extern void accept_report __P((u_int32 src, u_int32 dst,
+ char *p, int datalen,
+ u_int32 level));
+extern struct rtentry * determine_route __P((u_int32 src));
+extern void report __P((int which_routes, vifi_t vifi,
+ u_int32 dst));
+extern void report_to_all_neighbors __P((int which_routes));
+extern int report_next_chunk __P((void));
+extern void add_vif_to_routes __P((vifi_t vifi));
+extern void delete_vif_from_routes __P((vifi_t vifi));
+extern void delete_neighbor_from_routes __P((u_int32 addr,
+ vifi_t vifi));
+extern void dump_routes __P((FILE *fp));
+extern void start_route_updates __P((void));
+
+/* vif.c */
+extern void init_vifs __P((void));
+extern void check_vif_state __P((void));
+extern vifi_t find_vif __P((u_int32 src, u_int32 dst));
+extern void age_vifs __P((void));
+extern void dump_vifs __P((FILE *fp));
+extern void stop_all_vifs __P((void));
+extern struct listaddr *neighbor_info __P((vifi_t vifi, u_int32 addr));
+extern void accept_group_report __P((u_int32 src, u_int32 dst,
+ u_int32 group, int r_type));
+extern void query_groups __P((void));
+extern void probe_for_neighbors __P((void));
+extern int update_neighbor __P((vifi_t vifi, u_int32 addr,
+ int msgtype, char *p, int datalen,
+ u_int32 level));
+extern void accept_neighbor_request __P((u_int32 src, u_int32 dst));
+extern void accept_neighbor_request2 __P((u_int32 src,
+ u_int32 dst));
+extern void accept_neighbors __P((u_int32 src, u_int32 dst,
+ u_char *p, int datalen, u_int32 level));
+extern void accept_neighbors2 __P((u_int32 src, u_int32 dst,
+ u_char *p, int datalen, u_int32 level));
+extern void accept_leave_message __P((u_int32 src, u_int32 dst,
+ u_int32 group));
+extern void accept_membership_query __P((u_int32 src, u_int32 dst,
+ u_int32 group, int tmo));
+
+/* 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 naddr));
+extern int inet_valid_subnet __P((u_int32 nsubnet, u_int32 nmask));
+extern char * inet_fmt __P((u_int32 addr, char *s));
+extern char * inet_fmts __P((u_int32 addr, u_int32 mask, char *s));
+extern u_int32 inet_parse __P((char *s, int n));
+extern int inet_cksum __P((u_short *addr, u_int len));
+
+/* prune.c */
+extern unsigned kroutes;
+extern void add_table_entry __P((u_int32 origin, u_int32 mcastgrp));
+extern void del_table_entry __P((struct rtentry *r,
+ u_int32 mcastgrp, u_int del_flag));
+extern void update_table_entry __P((struct rtentry *r));
+extern void init_ktable __P((void));
+extern void accept_prune __P((u_int32 src, u_int32 dst, char *p,
+ int datalen));
+extern void steal_sources __P((struct rtentry *rt));
+extern void reset_neighbor_state __P((vifi_t vifi, u_int32 addr));
+extern int grplst_mem __P((vifi_t vifi, u_int32 mcastgrp));
+extern int scoped_addr __P((vifi_t vifi, u_int32 addr));
+extern void free_all_prunes __P((void));
+extern void age_table_entry __P((void));
+extern void dump_cache __P((FILE *fp2));
+extern void update_lclgrp __P((vifi_t vifi, u_int32 mcastgrp));
+extern void delete_lclgrp __P((vifi_t vifi, u_int32 mcastgrp));
+extern void chkgrp_graft __P((vifi_t vifi, u_int32 mcastgrp));
+extern void accept_graft __P((u_int32 src, u_int32 dst, char *p,
+ int datalen));
+extern void accept_g_ack __P((u_int32 src, u_int32 dst, char *p,
+ int datalen));
+/* u_int is promoted u_char */
+extern void accept_mtrace __P((u_int32 src, u_int32 dst,
+ u_int32 group, char *data, u_int no,
+ int datalen));
+
+/* kern.c */
+extern void k_set_rcvbuf __P((int bufsize));
+extern void k_hdr_include __P((int bool));
+extern void k_set_ttl __P((int t));
+extern void k_set_loop __P((int l));
+extern void k_set_if __P((u_int32 ifa));
+extern void k_join __P((u_int32 grp, u_int32 ifa));
+extern void k_leave __P((u_int32 grp, u_int32 ifa));
+extern void k_init_dvmrp __P((void));
+extern void k_stop_dvmrp __P((void));
+extern void k_add_vif __P((vifi_t vifi, struct uvif *v));
+extern void k_del_vif __P((vifi_t vifi));
+extern void k_add_rg __P((u_int32 origin, struct gtable *g));
+extern int k_del_rg __P((u_int32 origin, struct gtable *g));
+extern int k_get_version __P((void));
+
+#ifdef SNMP
+/* prune.c */
+extern struct rtentry * snmp_find_route __P(());
+extern struct gtable * find_grp __P(());
+extern struct stable * find_grp_src __P(());
+#endif
+
+#ifdef RSRR
+/* prune.c */
+extern struct gtable *kernel_table;
+extern struct gtable *gtp;
+extern int find_src_grp __P((u_int32 src, u_int32 mask,
+ u_int32 grp));
+
+/* rsrr.c */
+extern void rsrr_init __P((void));
+extern void rsrr_read __P((int f, fd_set *rfd));
+extern void rsrr_clean __P((void));
+extern void rsrr_cache_send __P((struct gtable *gt, int notify));
+extern void rsrr_cache_clean __P((struct gtable *gt));
+#endif /* RSRR */
diff --git a/usr.sbin/mrouted/dvmrp.h b/usr.sbin/mrouted/dvmrp.h
new file mode 100644
index 0000000..53b8d2f
--- /dev/null
+++ b/usr.sbin/mrouted/dvmrp.h
@@ -0,0 +1,174 @@
+/*
+ * The mrouted program is covered by the license in the accompanying file
+ * named "LICENSE". Use of the mrouted program represents acceptance of
+ * the terms and conditions listed in that file.
+ *
+ * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+ * Leland Stanford Junior University.
+ *
+ *
+ * $Id$
+ */
+
+/*
+ * 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 140 /* time to consider neighbor gone */
+
+#define GROUP_QUERY_INTERVAL 125 /* periodic group query interval */
+#define GROUP_EXPIRE_TIME 270 /* time to consider group gone */
+#define LEAVE_EXPIRE_TIME 3 /* " " after receiving a leave */
+/* Note: LEAVE_EXPIRE_TIME should ideally be shorter, but the resolution of
+ * the timer in mrouted doesn't allow us to make it any shorter. */
+
+#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 500 /* default tunnel rate limit */
+
+#define DEFAULT_CACHE_LIFETIME 300 /* kernel route entry discard time */
+#define GRAFT_TIMEOUT_VAL 5 /* retransmission time for grafts */
+
+#define OLD_AGE_THRESHOLD 2
diff --git a/usr.sbin/mrouted/igmp.c b/usr.sbin/mrouted/igmp.c
new file mode 100644
index 0000000..d9e57f9
--- /dev/null
+++ b/usr.sbin/mrouted/igmp.c
@@ -0,0 +1,467 @@
+/*
+ * The mrouted program is covered by the license in the accompanying file
+ * named "LICENSE". Use of the mrouted program represents acceptance of
+ * the terms and conditions listed in that file.
+ *
+ * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+ * Leland Stanford Junior University.
+ *
+ *
+ * $Id$
+ */
+
+
+#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 */
+
+/*
+ * Private variables
+ */
+static char router_alert[4]; /* Router Alert IP Option */
+#ifndef IPOPT_RA
+#define IPOPT_RA 148
+#endif
+#ifdef SUNOS5
+static char no_op[4]; /* Null IP Option */
+static int ip_addlen = 0; /* Workaround for Option bug #2*/
+#endif
+#define SEND_RA(x) (((x) == IGMP_MEMBERSHIP_QUERY) || \
+ ((x) == IGMP_V1_MEMBERSHIP_REPORT) || \
+ ((x) == IGMP_V2_MEMBERSHIP_REPORT) || \
+ ((x) == IGMP_V2_LEAVE_GROUP) || \
+ ((x) == IGMP_MTRACE))
+
+/*
+ * Local function definitions.
+ */
+/* u_char promoted to u_int */
+static char * packet_kind __P((u_int type, u_int code));
+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;
+#ifdef SUNOS5
+ u_int32 localhost = htonl(0x7f000001);
+#endif
+
+ 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(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 */
+
+ allhosts_group = htonl(INADDR_ALLHOSTS_GROUP);
+ dvmrp_group = htonl(INADDR_DVMRP_GROUP);
+ 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
+ no_op[0] = IPOPT_NOP;
+ no_op[1] = IPOPT_NOP;
+ no_op[2] = IPOPT_NOP;
+ no_op[3] = IPOPT_NOP;
+
+ setsockopt(igmp_socket, IPPROTO_IP, IP_OPTIONS, no_op, sizeof(no_op));
+ /*
+ * Check if the kernel adds the options length to the packet
+ * length. Send myself an IGMP packet of type 0 (illegal),
+ * with 4 IPOPT_NOP 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;
+
+ 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
+}
+
+#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
+
+static char *
+packet_kind(type, code)
+ u_int type, code;
+{
+ switch (type) {
+ case IGMP_HOST_MEMBERSHIP_QUERY: return "membership query ";
+ case IGMP_HOST_MEMBERSHIP_REPORT: return "V1 member report ";
+ case IGMP_HOST_NEW_MEMBERSHIP_REPORT: return "V2 member report ";
+ case IGMP_HOST_LEAVE_MESSAGE: 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: return "unknown DVMRP msg ";
+ }
+ 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: return "unknown PIM msg ";
+ }
+ case IGMP_MTRACE: return "IGMP trace query ";
+ case IGMP_MTRACE_RESP: return "IGMP trace reply ";
+ default: return "unknown IGMP msg ";
+ }
+}
+
+/*
+ * 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;
+ ipdatalen = ip->ip_len;
+ 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;
+ }
+
+ log(LOG_DEBUG, 0, "RECV %s from %-15s to %s",
+ packet_kind(igmp->igmp_type, igmp->igmp_code),
+ inet_fmt(src, s1), inet_fmt(dst, s2));
+
+ switch (igmp->igmp_type) {
+
+ case IGMP_HOST_MEMBERSHIP_QUERY:
+ accept_membership_query(src, dst, group, igmp->igmp_code);
+ return;
+
+ case IGMP_HOST_MEMBERSHIP_REPORT:
+ case IGMP_HOST_NEW_MEMBERSHIP_REPORT:
+ accept_group_report(src, dst, group, igmp->igmp_type);
+ return;
+
+ case IGMP_HOST_LEAVE_MESSAGE:
+ 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'. 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
+
+ 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);
+ if (type != IGMP_DVMRP || dst == allhosts_group) {
+ setloop = 1;
+ k_set_loop(TRUE);
+ }
+ if (SEND_RA(type))
+ sendra = 1;
+ }
+
+ if (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 no-op's.
+ */
+ setsockopt(igmp_socket, IPPROTO_IP, IP_OPTIONS,
+ no_op, sizeof(no_op));
+#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) {
+ 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);
+
+ log(LOG_DEBUG, 0, "SENT %s from %-15s to %s",
+ packet_kind(type, code), src == INADDR_ANY ? "INADDR_ANY" :
+ inet_fmt(src, s1), inet_fmt(dst, s2));
+}
diff --git a/usr.sbin/mrouted/inet.c b/usr.sbin/mrouted/inet.c
new file mode 100644
index 0000000..e540572
--- /dev/null
+++ b/usr.sbin/mrouted/inet.c
@@ -0,0 +1,232 @@
+/*
+ * The mrouted program is covered by the license in the accompanying file
+ * named "LICENSE". Use of the mrouted program represents acceptance of
+ * the terms and conditions listed in that file.
+ *
+ * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+ * Leland Stanford Junior University.
+ *
+ *
+ * $Id$
+ */
+
+
+#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/kern.c b/usr.sbin/mrouted/kern.c
new file mode 100644
index 0000000..f37940f
--- /dev/null
+++ b/usr.sbin/mrouted/kern.c
@@ -0,0 +1,241 @@
+/*
+ * The mrouted program is covered by the license in the accompanying file
+ * named "LICENSE". Use of the mrouted program represents acceptance of
+ * the terms and conditions listed in that file.
+ *
+ * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+ * Leland Stanford Junior University.
+ *
+ *
+ * $Id$
+ */
+
+
+#include "defs.h"
+
+
+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));
+}
+
+
+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");
+}
+
+
+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");
+ }
+}
+
+
+/*
+ * 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");
+ }
+
+ 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
+}
diff --git a/usr.sbin/mrouted/main.c b/usr.sbin/mrouted/main.c
new file mode 100644
index 0000000..1b1efcf
--- /dev/null
+++ b/usr.sbin/mrouted/main.c
@@ -0,0 +1,759 @@
+/*
+ * 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.
+ *
+ */
+
+/*
+ * Written by Steve Deering, Stanford University, February 1989.
+ *
+ * (An earlier version of DVMRP was implemented by David Waitzman of
+ * BBN STC by extending Berkeley's routed program. Some of Waitzman's
+ * extensions have been incorporated into mrouted, but none of the
+ * original routed code has been adopted.)
+ */
+
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id$";
+#endif
+
+#include <err.h>
+#include "defs.h"
+#ifdef __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+#include <fcntl.h>
+
+#ifdef SNMP
+#include "snmp.h"
+#endif
+
+extern char *configfilename;
+char versionstring[100];
+
+static char pidfilename[] = _PATH_MROUTED_PID;
+static char dumpfilename[] = _PATH_MROUTED_DUMP;
+static char cachefilename[] = _PATH_MROUTED_CACHE;
+static char genidfilename[] = _PATH_MROUTED_GENID;
+
+int cache_lifetime = DEFAULT_CACHE_LIFETIME;
+int max_prune_lifetime = DEFAULT_CACHE_LIFETIME * 2;
+
+int debug = 0;
+u_char pruning = 1; /* Enable pruning by default */
+
+#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;
+
+/*
+ * Forward declarations.
+ */
+static void fasttimer __P((int));
+static void done __P((int));
+static void dump __P((int));
+static void fdump __P((int));
+static void cdump __P((int));
+static void restart __P((int));
+static void timer __P((void));
+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;
+ register int omask;
+ int dummy;
+ FILE *fp;
+ struct timeval tv;
+ u_int32 prev_genid;
+ int vers;
+ fd_set rfds, readers;
+ int nfds, n, i;
+#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");
+
+ argv++, argc--;
+ while (argc > 0 && *argv[0] == '-') {
+ if (strcmp(*argv, "-d") == 0) {
+ if (argc > 1 && isdigit(*(argv + 1)[0])) {
+ argv++, argc--;
+ debug = atoi(*argv);
+ } 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) {
+ pruning = 0;
+#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) {
+ /*
+ * Detach from the terminal
+ */
+ int t;
+
+ if (fork()) exit(0);
+ (void)close(0);
+ (void)close(1);
+ (void)close(2);
+ (void)open("/", 0);
+ (void)dup2(0, 1);
+ (void)dup2(0, 2);
+#ifdef SYSV
+ (void)setpgrp();
+#else
+#ifdef TIOCNOTTY
+ t = open("/dev/tty", 2);
+ if (t >= 0) {
+ (void)ioctl(t, TIOCNOTTY, (char *)0);
+ (void)close(t);
+ }
+#else
+ if (setsid() < 0)
+ warn("setsid");
+#endif
+#endif
+ }
+ else
+ fprintf(stderr, "debug level %u\n", debug);
+
+#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 %d.%da",
+ PROTOCOL_VERSION, MROUTED_VERSION);
+
+ log(LOG_NOTICE, 0, "%s", versionstring);
+
+#ifdef SYSV
+ srand48(time(NULL));
+#else
+ srandom(gethostid());
+#endif
+
+ /*
+ * Get generation id
+ */
+ gettimeofday(&tv, 0);
+ dvmrp_genid = tv.tv_sec;
+
+ fp = fopen(genidfilename, "r");
+ if (fp != NULL) {
+ fscanf(fp, "%d", &prev_genid);
+ if (prev_genid == dvmrp_genid)
+ dvmrp_genid++;
+ (void) fclose(fp);
+ }
+
+ fp = fopen(genidfilename, "w");
+ if (fp != NULL) {
+ fprintf(fp, "%d", dvmrp_genid);
+ (void) fclose(fp);
+ }
+
+ callout_init();
+ init_igmp();
+ init_routes();
+ init_ktable();
+ k_init_dvmrp(); /* enable DVMRP routing in kernel */
+
+#ifndef OLD_KERNEL
+ vers = k_get_version();
+ /*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 */
+
+#if defined(__STDC__) || defined(__GNUC__)
+ /*
+ * Allow cleanup if unexpected exit. Apparently some architectures
+ * have a kernel bug where closing the socket doesn't do an
+ * ip_mrouter_done(), so we attempt to do it on exit.
+ */
+ atexit(cleanup);
+#endif
+
+ if (debug)
+ fprintf(stderr, "pruning %s\n", pruning ? "on" : "off");
+
+ fp = fopen(pidfilename, "w");
+ if (fp != NULL) {
+ fprintf(fp, "%d\n", (int)getpid());
+ (void) fclose(fp);
+ }
+
+ (void)signal(SIGALRM, fasttimer);
+
+ (void)signal(SIGHUP, restart);
+ (void)signal(SIGTERM, done);
+ (void)signal(SIGINT, done);
+ (void)signal(SIGUSR1, fdump);
+ (void)signal(SIGUSR2, cdump);
+ if (debug != 0)
+ (void)signal(SIGQUIT, dump);
+
+ 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;
+ }
+
+ /*
+ * Install the vifs in the kernel as late as possible in the
+ * initialization sequence.
+ */
+ init_installvifs();
+
+ if (debug >= 2) dump(0);
+
+ /* Start up the log rate-limiter */
+ resetlogging(NULL);
+
+ (void)alarm(1); /* schedule first timer interrupt */
+
+ /*
+ * Main receive loop.
+ */
+ dummy = 0;
+ for(;;) {
+#ifdef SYSV
+ sigset_t block, oblock;
+#endif
+ bcopy((char *)&readers, (char *)&rfds, sizeof(rfds));
+#ifdef SNMP
+ gettimeofday(nvp, 0);
+ 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)
+#else
+ if ((n = select(nfds, &rfds, NULL, NULL, NULL)) < 0)
+#endif
+ {
+ if (errno != EINTR) /* SIGALRM is expected */
+ log(LOG_WARNING, errno, "select failed");
+ continue;
+ }
+
+ 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;
+ }
+#ifdef SYSV
+ (void)sigemptyset(&block);
+ (void)sigaddset(&block, SIGALRM);
+ if (sigprocmask(SIG_BLOCK, &block, &oblock) < 0)
+ log(LOG_ERR, errno, "sigprocmask");
+#else
+ omask = sigblock(sigmask(SIGALRM));
+#endif
+ accept_igmp(recvlen);
+#ifdef SYSV
+ (void)sigprocmask(SIG_SETMASK, &oblock, (sigset_t *)NULL);
+#else
+ (void)sigsetmask(omask);
+#endif
+ }
+
+ for (i = 0; i < nhandlers; i++) {
+ if (FD_ISSET(ihandlers[i].fd, &rfds)) {
+ (*ihandlers[i].func)(ihandlers[i].fd, &rfds);
+ }
+ }
+
+#ifdef SNMP
+ snmp_read(&rfds);
+ snmp_timeout(); /* poll */
+#endif
+ }
+}
+
+static void
+usage()
+{
+ fprintf(stderr,
+ "usage: mrouted [-p] [-c configfile] [-d [debug_level]]\n");
+ exit(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)
+ int 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;
+ }
+ if ((t % TIMER_INTERVAL) == 0)
+ timer();
+
+ age_callout_queue();/* Advance the timer for the callout queue
+ for groups */
+ alarm(1);
+}
+
+/*
+ * 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.
+ */
+
+static 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()
+{
+ 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 % GROUP_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;
+}
+
+
+/*
+ * On termination, let everyone know we're going away.
+ */
+static void
+done(i)
+ int i;
+{
+ log(LOG_NOTICE, 0, "%s exiting", versionstring);
+ cleanup();
+ _exit(1);
+}
+
+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);
+ k_stop_dvmrp();
+ }
+}
+
+
+/*
+ * Dump internal data structures to stderr.
+ */
+static void
+dump(i)
+ int i;
+{
+ dump_vifs(stderr);
+ dump_routes(stderr);
+}
+
+
+/*
+ * Dump internal data structures to a file.
+ */
+static void
+fdump(i)
+ int i;
+{
+ FILE *fp;
+
+ fp = fopen(dumpfilename, "w");
+ if (fp != NULL) {
+ dump_vifs(fp);
+ dump_routes(fp);
+ (void) fclose(fp);
+ }
+}
+
+
+/*
+ * Dump local cache contents to a file.
+ */
+static void
+cdump(i)
+ int i;
+{
+ FILE *fp;
+
+ fp = fopen(cachefilename, "w");
+ if (fp != NULL) {
+ dump_cache(fp);
+ (void) fclose(fp);
+ }
+}
+
+
+/*
+ * Restart mrouted
+ */
+static void
+restart(i)
+ int i;
+{
+ register int omask;
+#ifdef SYSV
+ sigset_t block, oblock;
+#endif
+
+ log(LOG_NOTICE, 0, "%s restart", versionstring);
+
+ /*
+ * reset all the entries
+ */
+#ifdef SYSV
+ (void)sigemptyset(&block);
+ (void)sigaddset(&block, SIGALRM);
+ if (sigprocmask(SIG_BLOCK, &block, &oblock) < 0)
+ log(LOG_ERR, errno, "sigprocmask");
+#else
+ omask = sigblock(sigmask(SIGALRM));
+#endif
+ free_all_prunes();
+ free_all_routes();
+ stop_all_vifs();
+ k_stop_dvmrp();
+ close(igmp_socket);
+ close(udp_socket);
+
+ /*
+ * start processing again
+ */
+ dvmrp_genid++;
+ pruning = 1;
+
+ init_igmp();
+ init_routes();
+ init_ktable();
+ init_vifs();
+ k_init_dvmrp(); /* enable DVMRP routing in kernel */
+ init_installvifs();
+
+#ifdef SYSV
+ (void)sigprocmask(SIG_SETMASK, &oblock, (sigset_t *)NULL);
+#else
+ (void)sigsetmask(omask);
+#endif
+}
+
+#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);
+}
+
+/*
+ * 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;
+ char tbuf[20];
+ struct timeval now;
+ struct tm *thyme;
+
+ 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[211] = "warning - ";
+ char *msg;
+ char tbuf[20];
+ struct timeval now;
+ struct tm *thyme;
+
+ va_start(ap);
+#endif
+ vsprintf(&fmt[10], format, ap);
+ va_end(ap);
+ msg = (severity == LOG_WARNING) ? fmt : &fmt[10];
+
+ switch (debug) {
+ case 0: break;
+ case 1: if (severity > LOG_NOTICE) break;
+ case 2: if (severity > LOG_INFO ) break;
+ default:
+ gettimeofday(&now,NULL);
+ thyme = localtime(&now.tv_sec);
+ strftime(tbuf, sizeof(tbuf), "%X.%%03d ", thyme);
+ fprintf(stderr, tbuf, now.tv_usec / 1000);
+ fprintf(stderr, "%s", 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);
+ }
+
+ if (severity <= LOG_NOTICE) {
+ if (log_nmsgs++ < LOG_MAX_MSGS) {
+ 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..8091467
--- /dev/null
+++ b/usr.sbin/mrouted/map-mbone.8
@@ -0,0 +1,80 @@
+.Dd May 8, 1995
+.Dt MAP-MBONE 8
+.UC 5
+.Sh NAME
+.Nm map-mbone
+.Nd multicast connection mapper
+.Sh SYNOPSIS
+.Nm map-mbone
+.Op Fl d Ar debug_level
+.Op Fl f
+.Op Fl g
+.Op Fl n
+.Op Fl r Ar retry_count
+.Op Fl t Ar timeout_count
+.Op Ar starting_router
+.Sh DESCRIPTION
+.Nm Map-mbone
+attempts to display all multicast routers that are reachable from the multicast
+.Ar starting_router .
+If not specified on the command line, the default multicast
+.Ar starting_router
+is the localhost.
+.Pp
+.Nm Map-mbone
+traverses neighboring multicast routers by sending the ASK_NEIGHBORS IGMP
+message to the multicast starting_router. If this multicast router responds,
+the version number and a list of their neighboring multicast router addresses is
+part of that response. If the responding router has recent multicast version
+number, then
+.Nm
+requests additional information such as metrics, thresholds, and flags from the
+multicast router. For each new occurrence of neighboring multicast router in
+the reply and provided the flooding option has been selected, then
+.Nm
+asks each of this multicast router for a list of neighbors. This search
+for unique routers will continue until no new neighboring multicast routers
+are reported.
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl d
+Set the debug level. When the debug level is greater than the
+default value of 0, addition debugging messages are printed. Regardless of
+the debug level, an error condition, will always write an error message and will
+cause
+.Nm
+to terminate.
+Non-zero debug levels have the following effects:
+.Bl -tag -width indent
+.It "level 1"
+packet warnings are printed to stderr.
+.It "level 2"
+all level 1 messages plus notifications down networks are printed to stderr.
+.It "level 3"
+all level 2 messages plus notifications of all packet
+timeouts are printed to stderr.
+.El
+.It Fl f
+Set flooding option. Flooding allows the recursive search
+of neighboring multicast routers and is enable by default when starting_router
+is not used.
+.It Fl g
+Set graphing in GraphEd format.
+.It Fl n
+Disable the DNS lookup for the multicast routers names.
+.It Fl r Ar retry_count
+Set the neighbor query retry limit. Default is 1 retry.
+.It Fl t Ar timeout_count
+Set the number of seconds to wait for a neighbor query
+reply before retrying. Default timeout is 2 seconds.
+.El
+.Sh IMPORTANT NOTE
+.Nm Map-mbone
+must be run as root.
+.Sh SEE ALSO
+.Xr mrouted 8 ,
+.Xr mrinfo 8 ,
+.Xr mtrace 8
+.Sh AUTHOR
+.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..9754fd7
--- /dev/null
+++ b/usr.sbin/mrouted/map-mbone/Makefile
@@ -0,0 +1,20 @@
+# $Id$
+
+PROG= map-mbone
+
+S= ${.CURDIR}/..
+.PATH: $S
+CFLAGS+= -I$S
+LDADD+= -lmrouted
+.if exists(${.OBJDIR}/../common)
+LDDESTDIR+= -L${.OBJDIR}/../common
+DPADD+= ${.OBJDIR}/../common/libmrouted.a
+.else
+LDDESTDIR+= -L$S/common
+DPADD+= $S/common/libmrouted.a
+.endif
+
+SRCS= mapper.c
+MAN8= ${.CURDIR}/../map-mbone.8
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/mrouted/mapper.c b/usr.sbin/mrouted/mapper.c
new file mode 100644
index 0000000..aeef027
--- /dev/null
+++ b/usr.sbin/mrouted/mapper.c
@@ -0,0 +1,1032 @@
+/* Mapper for connections between MRouteD multicast routers.
+ * Written by Pavel Curtis <Pavel@PARC.Xerox.Com>
+ *
+ * $Id: mapper.c,v 1.10 1997/02/22 16:06:57 peter 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.
+ */
+
+#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));
+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)
+ strcat(fmt, "warning - ");
+ strncat(fmt, format, 80);
+ 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)
+ u_int32 addr;
+ char *buf;
+{
+ char *name;
+
+ if (show_names && (name = inet_name(addr)))
+ strcpy(buf, name);
+ 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),
+ 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;
+#if (defined(BSD) && (BSD >= 199103))
+ 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..7ad6466
--- /dev/null
+++ b/usr.sbin/mrouted/mrinfo.8
@@ -0,0 +1,76 @@
+.Dd May 8, 1995
+.Dt MRINFO 8
+.UC 5
+.Sh NAME
+.Nm mrinfo
+.Nd display configuration info from a multicast router
+.Sh SYNOPSIS
+.Nm mrinfo
+.Op Fl d Ar debug_level
+.Op Fl r Ar retry_count
+.Op Fl t Ar timeout_count
+.Ar multicast_router
+.Sh DESCRIPTION
+.Nm Mrinfo
+attempts to display the configuration information from the multicast router
+.Ar multicast_router .
+.Pp
+.Nm Mrinfo
+uses the ASK_NEIGHBORS IGMP message to the specified multicast router. If this
+multicast router responds, the version number and a list of their neighboring
+multicast router addresses is part of that response. If the responding router
+has a recent multicast version number, then
+.Nm
+requests additional information such as metrics, thresholds, and flags from the
+multicast router. Once the specified multicast router responds, the
+configuration is displayed to the standard output.
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl d Ar debug_level
+Set the debug level. When the debug level is greater than the
+default value of 0, addition debugging messages are printed. Regardless of
+the debug level, an error condition, will always write an error message and will
+cause
+.Nm
+to terminate.
+Non-zero debug levels have the following effects:
+.Bl -tag -width indent
+.It "level 1"
+packet warnings are printed to stderr.
+.It "level 2"
+all level 1 messages plus notifications down networks are printed to stderr.
+.It "level 3"
+all level 2 messages plus notifications of all packet
+timeouts are printed to stderr.
+.El
+.It Fl r Ar retry_count
+Set the neighbor query retry limit. Default is 3 retries.
+.It Fl t Ar timeout_count
+Set the number of seconds to wait for a neighbor query
+reply. Default timeout is 4 seconds.
+.El
+.Sh SAMPLE OUTPUT
+.nf
+.Nm mrinfo mbone.phony.dom.net
+127.148.176.10 (mbone.phony.dom.net) [version 3.3]:
+ 127.148.176.10 -> 0.0.0.0 (?) [1/1/querier]
+ 127.148.176.10 -> 127.0.8.4 (mbone2.phony.dom.net) [1/45/tunnel]
+ 127.148.176.10 -> 105.1.41.9 (momoney.com) [1/32/tunnel/down]
+ 127.148.176.10 -> 143.192.152.119 (mbone.dipu.edu) [1/32/tunnel]
+.fi
+.Pp
+For each neighbor of the queried multicast router, the IP of the queried router
+is displayed, followed by the IP and name of the neighbor. In square brackets
+the metric (cost of connection), the treashold (multicast ttl) is displayed. If
+the queried multicast router has a newer version number, the type (tunnel,
+srcrt) and status (disabled, down) of the connection is displayed.
+.Sh IMPORTANT NOTE
+.Nm Mrinfo
+must be run as root.
+.Sh SEE ALSO
+.Xr mrouted 8 ,
+.Xr map-mbone 8 ,
+.Xr mtrace 8
+.Sh AUTHOR
+.An Van Jacobson
diff --git a/usr.sbin/mrouted/mrinfo.c b/usr.sbin/mrouted/mrinfo.c
new file mode 100644
index 0000000..2266275
--- /dev/null
+++ b/usr.sbin/mrouted/mrinfo.c
@@ -0,0 +1,628 @@
+/*
+ * This tool requests configuration info from a multicast router
+ * and prints the reply (if any). Invoke it as:
+ *
+ * mrinfo router-name-or-address
+ *
+ * Written Wed Mar 24 1993 by Van Jacobson (adapted from the
+ * multicast mapper written by Pavel Curtis).
+ *
+ * The lawyers insist we include the following UC copyright notice.
+ * The mapper from which this is derived contained a Xerox copyright
+ * notice which follows the UC one. Try not to get depressed noting
+ * that the legal gibberish is larger than the program.
+ *
+ * Copyright (c) 1993 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the Computer Systems
+ * Engineering Group at Lawrence Berkeley Laboratory.
+ * 4. Neither the name of the University nor of the Laboratory may be used
+ * to endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * ---------------------------------
+ * Copyright (c) Xerox Corporation 1992. All rights reserved.
+ *
+ * License is granted to copy, to use, and to make and to use derivative works
+ * for research and evaluation purposes, provided that Xerox is acknowledged
+ * in all documentation pertaining to any such copy or derivative work. Xerox
+ * grants no other licenses expressed or implied. The Xerox trade name should
+ * not be used in any advertising without its written permission.
+ *
+ * XEROX CORPORATION MAKES NO REPRESENTATIONS CONCERNING EITHER THE
+ * MERCHANTABILITY OF THIS SOFTWARE OR THE SUITABILITY OF THIS SOFTWARE FOR
+ * ANY PARTICULAR PURPOSE. The software is provided "as is" without express
+ * or implied warranty of any kind.
+ *
+ * These notices must be retained in any copies of any part of this software.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id$";
+/* 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)
+ strcat(fmt, "warning - ");
+ strncat(fmt, format, 80);
+ 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. */
+
+ printf("%s (%s) [version %d.%d", inet_fmt(src, s1), inet_name(src),
+ level & 0xff, (level >> 8) & 0xff);
+ if ((level >> 16) & NF_LEAF) { printf (",leaf"); }
+ if ((level >> 16) & NF_PRUNE) { printf (",prune"); }
+ if ((level >> 16) & NF_GENID) { printf (",genid"); }
+ if ((level >> 16) & NF_MTRACE) { printf (",mtrace"); }
+ 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;
+#if (defined(BSD) && (BSD >= 199103))
+ 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;
+ ipdatalen = ip->ip_len;
+ 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..96b2f05
--- /dev/null
+++ b/usr.sbin/mrouted/mrinfo/Makefile
@@ -0,0 +1,22 @@
+# $Id$
+
+PROG= mrinfo
+
+S= ${.CURDIR}/..
+.PATH: $S
+CFLAGS+= -I$S
+LDADD+= -lmrouted
+.if exists(${.OBJDIR}/../common)
+LDDESTDIR+= -L${.OBJDIR}/../common
+DPADD+= ${.OBJDIR}/../common/libmrouted.a
+.else
+LDDESTDIR+= -L$S/common
+DPADD+= $S/common/libmrouted.a
+.endif
+
+SRCS= mrinfo.c
+MAN8= ${.CURDIR}/../mrinfo.8
+BINOWN= root
+BINMODE=4555
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/mrouted/mrouted.8 b/usr.sbin/mrouted/mrouted.8
new file mode 100644
index 0000000..8a6298b
--- /dev/null
+++ b/usr.sbin/mrouted/mrouted.8
@@ -0,0 +1,420 @@
+.\"COPYRIGHT 1989 by The Board of Trustees of Leland Stanford Junior University.
+.\"$Id: mrouted.8,v 1.9 1997/02/22 16:06:59 peter Exp $
+.Dd May 8, 1995
+.Dt MROUTED 8
+.UC 5
+.Sh NAME
+.Nm mrouted
+.Nd IP multicast routing daemon
+.Sh SYNOPSIS
+.Nm mrouted
+.Op Fl c Ar config_file
+.Op Fl d Op Ar debug_level
+.Op Fl p
+.Sh DESCRIPTION
+.Nm Mrouted
+is an implementation of the Distance-Vector Multicast Routing
+Protocol (DVMRP), an earlier version of which is specified in RFC-1075.
+It maintains topological knowledge via a distance-vector routing protocol
+(like RIP, described in RFC-1058), upon which it implements a multicast
+datagram forwarding algorithm called Reverse Path Multicasting.
+.Pp
+.Nm Mrouted
+forwards a multicast datagram along a shortest (reverse) path tree
+rooted at the subnet on which the datagram originates. The multicast
+delivery tree may be thought of as a broadcast delivery tree that has
+been pruned back so that it does not extend beyond those subnetworks
+that have members of the destination group. Hence, datagrams
+are not forwarded along those branches which have no listeners of the
+multicast group. The IP time-to-live of a multicast datagram can be
+used to limit the range of multicast datagrams.
+.Pp
+In order to support multicasting among subnets that are separated by (unicast)
+routers that do not support IP multicasting,
+.Nm
+includes support for
+"tunnels", which are virtual point-to-point links between pairs of
+.Nm mrouted Ns s
+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.
+By default, the packets are encapsulated using the IP-in-IP protocol
+(IP protocol number 4).
+Older versions of
+.Nm
+tunnel using IP source routing, which puts a heavy load on some
+types of routers.
+This version does not support IP source route tunnelling.
+.Pp
+The tunnelling mechanism allows
+.Nm
+to establish a virtual internet, for
+the purpose of multicasting only, which is independent of the physical
+internet, and which may span multiple Autonomous Systems. This capability
+is intended for experimental support of internet multicasting only, pending
+widespread support for multicast routing by the regular (unicast) routers.
+.Nm Mrouted
+suffers from the well-known scaling problems of any distance-vector
+routing protocol, and does not (yet) support hierarchical multicast routing.
+.Pp
+.Nm Mrouted
+handles multicast routing only; there may or may not be unicast routing
+software running on the same machine as
+.Nm mrouted .
+With the use of tunnels, it
+is not necessary for
+.Nm
+to have access to more than one physical subnet
+in order to perform multicast forwarding.
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl c Ar config_file
+Specify an alternative file for configuration commands. Default is
+.Pa /etc/mrouted.conf .
+.It Fl d Op Ar debug_level
+If no
+.Fl d
+option is given, or if the debug level is specified as 0,
+.Nm
+detaches from the invoking terminal. Otherwise, it remains attached to the
+invoking terminal and responsive to signals from that terminal.
+If
+.Fl d
+is given with no argument, the debug level defaults to 2. Regardless of the
+debug level,
+.Nm
+always writes warning and error messages to the system
+log demon. Non-zero debug levels have the following effects:
+.Bl -tag -width indent
+.It "level 1"
+all syslog'ed messages are also printed to stderr.
+.It "level 2"
+all level 1 messages plus notifications of "significant"
+events are printed to stderr.
+.It "level 3"
+all level 2 messages plus notifications of all packet
+arrivals and departures are printed to stderr.
+.El
+.It Fl p
+Disable pruning.
+.El
+.Pp
+Upon startup,
+.Nm
+writes its pid to the file
+.Pa /var/run/mrouted.pid .
+.Sh CONFIGURATION
+.Nm Mrouted
+automatically configures itself to forward on all multicast-capable
+interfaces, i.e., interfaces that have the IFF_MULTICAST flag set (excluding
+the loopback "interface"), and it finds other
+.Nm mrouted Ns s
+directly reachable
+via those interfaces. To override the default configuration, or to add
+tunnel links to other
+.Nm mrouted Ns s,
+configuration commands may be placed in
+.Pa /etc/mrouted.conf
+(or an alternative file, specified by the
+.Fl c
+option).
+There are four types of configuration commands:
+.nf
+
+ phyint <local-addr> [disable] [metric <m>] [advert_metric <m>]
+ [threshold <t>] [rate_limit <b>]
+ [boundary (<boundary-name>|<scoped-addr>/<mask-len>)]
+ [altnet <network>/<mask-len>]
+
+ tunnel <local-addr> <remote-addr> [metric <m>] [advert_metric <m>]
+ [threshold <t>] [rate_limit <b>]
+ [boundary (<boundary-name>|<scoped-addr>/<mask-len>)]
+
+ cache_lifetime <ct>
+
+ name <boundary-name> <scoped-addr>/<mask-len>
+
+.fi
+.Pp
+The file format is free-form; whitespace (including newlines) is not
+significant.
+The
+.Em boundary
+and
+.Em altnet
+options may be specified as many times as necessary.
+.Pp
+The phyint command can be used to disable multicast routing on the physical
+interface identified by local IP address <local-addr>, or to associate a
+non-default metric or threshold with the specified physical interface.
+The local IP address <local-addr> may be replaced by the
+interface name (e.g le0).
+If a phyint is attached to multiple IP subnets, describe each additional subnet
+with the altnet keyword.
+Phyint commands must precede tunnel commands.
+.Pp
+The tunnel command can be used to establish a tunnel link between local
+IP address <local-addr> and remote IP address <remote-addr>, and to associate
+a non-default metric or threshold with that tunnel.
+The local IP address <local-addr> may be replaced by the
+interface name (e.g. le0). The remote IP address <remote-addr> may
+be replaced by a host name, if and only if the host name has a single
+IP address associated with it.
+The tunnel must be set
+up in the
+.Pa mrouted.conf
+files of both routers before it can be used.
+.Pp
+The cache_lifetime is a value that determines the amount of time that a
+cached multicast route stays in kernel before timing out. The value of this
+entry should lie between 300 (5 min) and 86400 (1 day). It defaults to 300.
+.Pp
+You may assign names to boundaries to make configuration easier with
+the name keyword. The boundary option on phyint or tunnel commands
+can accept either a name or a boundary.
+.Pp
+The 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 metric defaults to 1. Metrics should be kept as small as possible,
+because
+.Nm
+cannot route along paths with a sum of metrics greater
+than 31.
+.Pp
+The advert_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 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.
+.Pp
+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 1.) The default threshold is 1.
+.Pp
+In general, all
+.Nm mrouted Ns s
+connected to a particular subnet or tunnel should
+use the same metric and threshold for that subnet or tunnel.
+.Pp
+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 to 500Kbps on tunnels, and 0 (unlimited) on physical
+interfaces.
+.Pp
+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.
+.Pp
+.Nm Mrouted
+will not initiate execution if it has fewer than two enabled vifs,
+where a vif (virtual interface) is either a physical multicast-capable
+interface or a tunnel. It will log a warning if all of its vifs are
+tunnels; such an
+.Nm
+configuration would be better replaced by more
+direct tunnels (i.e., eliminate the middle man).
+.Sh "EXAMPLE CONFIGURATION"
+This is an example configuration for a mythical multicast router at a big
+school.
+.sp
+.nf
+#
+# mrouted.conf example
+#
+# Name our boundaries to make it easier
+name LOCAL 239.255.0.0/16
+name EE 239.254.0.0/16
+#
+# le1 is our gateway to compsci, don't forward our
+# local groups to them
+phyint le1 boundary EE
+#
+# le2 is our interface on the classroom net, it has four
+# different length subnets on it.
+# note that you can use either an ip address or an
+# interface name
+phyint 172.16.12.38 boundary EE altnet 172.16.15.0/26
+ altnet 172.16.15.128/26 altnet 172.16.48.0/24
+#
+# atm0 is our ATM interface, which doesn't properly
+# support multicasting.
+phyint atm0 disable
+#
+# This is an internal tunnel to another EE subnet
+# Remove the default tunnel rate limit, since this
+# tunnel is over ethernets
+tunnel 192.168.5.4 192.168.55.101 metric 1 threshold 1
+ rate_limit 0
+#
+# This is our tunnel to the outside world.
+# Careful with those boundaries, Eugene.
+tunnel 192.168.5.4 10.11.12.13 metric 1 threshold 32
+ boundary LOCAL boundary EE
+.fi
+.Sh SIGNALS
+.Nm Mrouted
+responds to the following signals:
+.Bl -tag -width indent
+.It HUP
+Restarts
+.Nm mrouted .
+The configuration file is reread every time this signal is evoked.
+.It INT
+Terminate execution gracefully (i.e., by sending
+good-bye messages to all neighboring routers).
+.It TERM
+Same as INT.
+.It USR1
+Dump the internal routing tables to
+.Pa /var/tmp/mrouted.dump .
+.It USR2
+Dump the internal cache tables to
+.Pa /var/tmp/mrouted.cache .
+.It QUIT
+Dump the internal routing tables to stderr (only if
+.Nm
+was invoked with a non-zero debug level).
+.El
+.Pp
+For convenience in sending signals,
+.Nm
+writes its pid to
+.Pa /var/run/mrouted.pid
+upon startup.
+.Sh EXAMPLE
+The routing tables look like this:
+.nf
+
+Virtual Interface Table
+ Vif Local-Address Metric Thresh Flags
+ 0 36.2.0.8 subnet: 36.2 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 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 (2.2)
+ boundaries: 239.0.1
+ : 239.1.2
+ pkts in: 34545433
+ pkts out: 234342
+
+ 3 36.2.0.8 tunnel: 36.6.8.23 3 16
+
+Multicast Routing Table (1136 entries)
+ Origin-Subnet From-Gateway Metric Tmr In-Vif Out-Vifs
+ 36.2 1 45 0 1* 2 3*
+ 36.8 36.8.0.77 4 15 2 0* 1* 3*
+ 36.11 1 20 1 0* 2 3*
+ .
+ .
+ .
+
+.fi
+In this example, there are four vifs connecting to two subnets and two
+tunnels. The vif 3 tunnel is not in use (no peer address). The vif 0 and
+vif 1 subnets have some groups present; tunnels never have any groups. This
+instance of
+.Nm
+is the one responsible for sending periodic group
+membership queries on the vif 0 and vif 1 subnets, as indicated by the
+"querier" flags. The list of boundaries indicate the scoped addresses on that
+interface. A count of the no. of incoming and outgoing packets is also
+shown at each interface.
+.Pp
+Associated with each subnet from which a multicast datagram can originate
+is the address of the previous hop router (unless the subnet is directly-
+connected), the metric of the path back to the origin, the amount of time
+since we last received an update for this subnet, the incoming vif for
+multicasts from that origin, and a list of outgoing vifs. "*" means that
+the outgoing vif is connected to a leaf of the broadcast tree rooted at the
+origin, and a multicast datagram from that origin will be forwarded on that
+outgoing vif only if there are members of the destination group on that leaf.
+.Pp
+.Nm Mrouted
+also maintains a copy of the kernel forwarding cache table. Entries
+are created and deleted by
+.Nm mrouted .
+.Pp
+The cache tables look like this:
+.nf
+
+Multicast Routing Cache Table (147 entries)
+ Origin Mcast-group CTmr Age Ptmr IVif Forwvifs
+ 13.2.116/22 224.2.127.255 3m 2m - 0 1
+>13.2.116.19
+>13.2.116.196
+ 138.96.48/21 224.2.127.255 5m 2m - 0 1
+>138.96.48.108
+ 128.9.160/20 224.2.127.255 3m 2m - 0 1
+>128.9.160.45
+ 198.106.194/24 224.2.135.190 9m 28s 9m 0P
+>198.106.194.22
+
+.fi
+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
+when the timer decrements to zero.
+.Pp
+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 are 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.
+.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 mrinfo 8 ,
+.Xr mtrace 8 ,
+.Xr map-mbone 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..1e69244
--- /dev/null
+++ b/usr.sbin/mrouted/mrouted.conf
@@ -0,0 +1,43 @@
+# $Id$
+#
+# 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..532ff93
--- /dev/null
+++ b/usr.sbin/mrouted/mrouted/Makefile
@@ -0,0 +1,21 @@
+# $Id$
+
+PROG= mrouted
+
+S= ${.CURDIR}/..
+.PATH: $S
+CFLAGS+= -I$S
+LDADD+= -lmrouted
+.if exists(${.OBJDIR}/../common)
+LDDESTDIR+= -L${.OBJDIR}/../common
+DPADD+= ${.OBJDIR}/../common/libmrouted.a
+.else
+LDDESTDIR+= -L$S/common
+DPADD+= $S/common/libmrouted.a
+.endif
+
+SRCS= config.c cfparse.y main.c route.c vif.c prune.c callout.c rsrr.c
+CLEANFILES+= y.tab.h
+MAN8= ${.CURDIR}/../mrouted.8
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/mrouted/mtrace.8 b/usr.sbin/mrouted/mtrace.8
new file mode 100644
index 0000000..3316ae5
--- /dev/null
+++ b/usr.sbin/mrouted/mtrace.8
@@ -0,0 +1,550 @@
+.\" Copyright (c) 1995 by the University of Southern California
+.\" All rights reserved.
+.\"
+.\" Permission to use, copy, modify, and distribute this software and its
+.\" documentation in source and binary forms for non-commercial purposes
+.\" and without fee is hereby granted, provided that the above copyright
+.\" notice appear in all copies and that both the copyright notice and
+.\" this permission notice appear in supporting documentation, and that
+.\" any documentation, advertising materials, and other materials related
+.\" to such distribution and use acknowledge that the software was
+.\" developed by the University of Southern California, Information
+.\" Sciences Institute. The name of the University may not be used to
+.\" endorse or promote products derived from this software without
+.\" specific prior written permission.
+.\"
+.\" THE UNIVERSITY OF SOUTHERN CALIFORNIA makes no representations about
+.\" the suitability of this software for any purpose. THIS SOFTWARE IS
+.\" PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES,
+.\" INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+.\"
+.\" Other copyrights might apply to parts of this software and are so
+.\" noted when applicable.
+.\"
+.\" This manual page (but not the software) was derived from the
+.\" manual page for the traceroute program which bears the following
+.\" copyright notice:
+.\"
+.\" Copyright (c) 1988 The Regents of the University of California.
+.\" All rights reserved.
+.\"
+.\" mtrace.8,v 5.1 1996/12/19 21:31:26 fenner Exp
+.\"
+.Dd May 8, 1995
+.Dt MTRACE 8
+.UC 6
+.Sh NAME
+.Nm mtrace
+.Nd print multicast path from a source to a receiver
+.Sh SYNOPSIS
+.Nm mtrace
+.Op Fl e Ar extrahops
+.Op Fl g Ar gateway
+.Op Fl i Ar if_addr
+.Op Fl l
+.Op Fl M
+.Op Fl m Ar max_hops
+.Op Fl n
+.Op Fl O
+.Op Fl p
+.Op Fl P
+.Op Fl q Ar nqueries
+.Op Fl r Ar resp_dest
+.Op Fl s
+.Op Fl S Ar stat_int
+.Op Fl t Ar ttl
+.Op Fl T
+.Op Fl U
+.Op Fl v
+.Op Fl w Ar waittime
+.Ar source
+.Op Ar receiver
+.Op Ar group
+.Sh DESCRIPTION
+Assessing problems in the distribution of IP multicast traffic
+can be difficult.
+.Nm Mtrace
+utilizes a tracing feature implemented in multicast routers that is
+accessed via an extension to the IGMP protocol. A trace query is
+passed hop-by-hop along the reverse path from the
+.Ar receiver
+to the
+.Ar source ,
+collecting hop addresses, packet counts, and routing error conditions
+along the path, and then the response is returned to the requestor.
+.Pp
+The only required parameter is the
+.Ar source
+host name or address. The default
+.Ar receiver
+is the host running mtrace, and the default
+.Ar group
+is "MBone Audio" (224.2.0.1), which is sufficient if packet loss
+statistics for a particular multicast group are not needed. These two
+optional parameters may be specified to test the path to some other
+receiver in a particular group, subject to some constraints as
+detailed below. The two parameters can be distinguished because the
+.Ar receiver
+is a unicast address and the
+.Ar group
+is a multicast address.
+If the
+.Fl g
+flag is specified, the source address defaults to the host running
+.Nm mtrace ,
+and the receiver defaults to the router being addressed with
+the
+.Fl g
+flag. In this case, there are no required parameters.
+.Pp
+NOTE: For Solaris 2.4/2.5, if the multicast interface is not the default
+interface, the
+.Fl i
+option must be used to set the local address.
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl e Ar extrahops
+Try tracing
+.Ar extrahops
+hops past a non-responding router.
+.It Fl g Ar gwy
+Send the trace query via unicast directly to the multicast router
+.Ar gwy
+rather than multicasting the query.
+This must be the last-hop router on the path from the intended
+.Ar source
+to the
+.Ar receiver .
+.Pp
+.Em CAUTION!!
+Versions 3.3 and 3.5 of
+.Nm mrouted
+will crash if a trace query is received via a
+unicast packet and
+.Nm mrouted
+has no route for the
+.Ar source
+address. Therefore, do not use the
+.Fl g
+option unless the target
+.Nm mrouted
+has been verified to be 3.4 or newer than 3.5.
+.It Fl i Ar addr
+Use
+.Ar addr
+as the local interface address (on a multi-homed host) for sending the
+trace query and as the default for the
+.Ar receiver
+and the response destination.
+.It Fl l
+Loop indefinitely printing packet rate and loss statistics for the
+multicast path every 10 seconds (see
+.Fl S Ar stat_int ).
+.It Fl M
+Always request the response using multicast rather than attempting
+unicast for the last half of the tries.
+.It Fl m Ar n
+Set to
+.Ar n
+the maximum number of hops that will be traced from the
+.Ar receiver
+back toward the
+.Ar source .
+The default is 32 hops (infinity for the DVMRP routing protocol).
+.It Fl n
+Print hop addresses numerically rather than symbolically and numerically
+(saves a nameserver address-to-name lookup for each router found on the
+path).
+.It Fl q Ar n
+Set the maximum number of query attempts for any hop to
+.Ar n .
+The default is 3.
+.It Fl O
+Do not use the Router-Alert IP option on those requests which need it.
+Some versions of Cisco's IOS cannot handle
+multicast traceroutes with IP options, so it may be necessary to use the
+.Fl O
+flag if the last-hop router is a Cisco.
+.It Fl p
+Listen passively for multicast responses from traces initiated by
+others. This works best when run on a multicast router.
+.It Fl P
+Loop indefinitely collecting the path every 10 seconds (see
+.Fl S Ar stat_int )
+and printing it when it changes. Do not print any statistics.
+.It Fl r Ar host
+Send the trace response to
+.Ar host
+rather than to the host on which
+.Nm
+is being run, or to a multicast address other than the one registered
+for this purpose (224.0.1.32).
+.It Fl s
+Print a short form output including only the multicast path and not
+the packet rate and loss statistics.
+.It Fl S Ar n
+Change the interval between statistics gathering traces to
+.Ar n
+seconds (default 10 seconds).
+.It Fl t Ar ttl
+Set the
+.Ar ttl
+(time-to-live, or number of hops) for multicast trace queries and
+responses. The default is 127, except for local queries to the "all
+routers" multicast group which use ttl 1.
+.It Fl T
+"Tunnel statistics" mode; show loss rates for overall traffic.
+These statistics can be extremely misleading.
+.It Fl U
+Always request the response using unicast rather than attempting
+multicast first.
+.It Fl v
+Verbose mode; show hop times on the initial trace and statistics display.
+Also show the route that was used to forward the initial trace.
+.It Fl w Ar n
+Set the time to wait for a trace response to
+.Ar n
+seconds (default 3 seconds).
+.El
+.Sh USAGE
+.SS How It Works
+The technique used by the
+.Nm traceroute
+tool to trace unicast network paths will not work for IP multicast
+because ICMP responses are specifically forbidden for multicast traffic.
+Instead, a tracing feature has been built into the multicast routers.
+This technique has the advantage that additional information about
+packet rates and losses can be accumulated while the number of packets
+sent is minimized.
+.Pp
+Since multicast uses
+reverse path forwarding, the trace is run backwards from the
+.Ar receiver
+to the
+.Ar source .
+A trace query packet is sent to the last
+hop multicast router (the leaf router for the desired
+.Ar receiver
+address). The last hop router builds a trace response packet, fills in
+a report for its hop, and forwards the trace packet using unicast to
+the router it believes is the previous hop for packets originating
+from the specified
+.Ar source .
+Each router along the path adds its report and forwards the packet.
+When the trace response packet reaches the first hop router (the router
+that is directly connected to the source's net), that router sends the
+completed response to the response destination address specified in
+the trace query.
+.Pp
+If some multicast router along the path does not implement the
+multicast traceroute feature or if there is some outage, then no
+response will be returned. To solve this problem, the trace query
+includes a maximum hop count field to limit the number of hops traced
+before the response is returned. That allows a partial path to be
+traced.
+.Pp
+The reports inserted by each router contain not only the address of
+the hop, but also the ttl required to forward and some flags to indicate
+routing errors, plus counts of the total number of packets on the
+incoming and outgoing interfaces and those forwarded for the specified
+.Ar group .
+Taking differences in these counts for two traces separated in time
+and comparing the output packet counts from one hop with the input
+packet counts of the next hop allows the calculation of packet rate
+and packet loss statistics for each hop to isolate congestion
+problems.
+.SS Finding the Last-Hop Router
+The trace query must be sent to the multicast router which is the
+last hop on the path from the
+.Ar source
+to the
+.Ar receiver .
+If the receiver is on the local subnet (as determined using the subnet
+mask), then the default method is to multicast the trace query to
+all-routers.mcast.net (224.0.0.2) with a ttl of 1. Otherwise, the
+trace query is multicast to the
+.Ar group
+address since the last hop router will be a member of that group if
+the receiver is. Therefore it is necessary to specify a group that
+the intended receiver has joined. This multicast is sent with a
+default ttl of 127, which may not be sufficient for all cases (changed
+with the
+.Fl t
+option).
+If the last hop router is known, it may also be addressed directly
+using the
+.Fl g
+option). Alternatively, if it is desired to trace a group that the
+receiver has not joined, but it is known that the last-hop router is a
+member of another group, the
+.Fl g
+option may also be used to specify a different multicast address for the
+trace query.
+.Pp
+When tracing from a multihomed host or router, the default receiver
+address may not be the desired interface for the path from the source.
+In that case, the desired interface should be specified explicitly as
+the
+.Ar receiver .
+.SS Directing the Response
+By default,
+.Nm
+first attempts to trace the full reverse path, unless the number of
+hops to trace is explicitly set with the
+.Fl m
+option. If there is no response within a 3 second timeout interval
+(changed with the
+.Fl w
+option), a "*" is printed and the probing switches to hop-by-hop mode.
+Trace queries are issued starting with a maximum hop count of one and
+increasing by one until the full path is traced or no response is
+received. At each hop, multiple probes are sent (default is three,
+changed with
+.Fl q
+option). The first half of the attempts (default is two) are made with
+the reply address set to standard multicast address, mtrace.mcast.net
+(224.0.1.32) with the ttl set to 32 more than what's needed to pass the
+thresholds seen so far along the path to the receiver. For each
+additional attempt, the ttl is increased by another 32 each time up to
+a maximum of 192. Since the desired router may not be able to send a
+multicast reply, the remainder of the attempts request that the
+response be sent via unicast to the host running
+.Nm mtrace .
+Alternatively, the multicast ttl may be set explicitly with the
+.Fl t
+option, the initial multicast attempts can be forced to use unicast
+instead with the
+.Fl U
+option, the final unicast attempts can be forced to use multicast
+isntead with the
+.Fl M
+option, or if you specify
+.Fl UM ,
+.Nm
+will first attempt using unicast and then multicast. For each attempt,
+if no response is received within the timeout, a "*" is printed. After
+the specified number of attempts have failed,
+.Nm
+will try to query the next hop router with a DVMRP_ASK_NEIGHBORS2
+request (as used by the
+.Nm mrinfo
+program) to see what kind of router it is.
+.Nm
+will try to query three (changed with the
+.Fl e
+option) hops past a non-responding router, in the hopes that even
+though it isn't capable of sending a response, it might be capable of
+forwarding the request on.
+.Sh EXAMPLES
+The output of
+.Nm
+is in two sections. The first section is a short listing of the hops
+in the order they are queried, that is, in the reverse of the order
+from the
+.Ar source
+to the
+.Ar receiver .
+For each hop, a line is printed showing the hop number (counted
+negatively to indicate that this is the reverse path); the multicast
+routing protocol (DVMRP, MOSPF, PIM, etc.); the threshold required to
+forward data (to the previous hop in the listing as indicated by the
+up-arrow character); and the cumulative delay for the query to reach
+that hop (valid only if the clocks are synchronized). This first
+section ends with a line showing the round-trip time which measures
+the interval from when the query is issued until the response is
+received, both derived from the local system clock, and the total
+ttl required for a packet to travel along this path. A sample use and
+output might be:
+.Pp
+.nf
+.ft C
+oak.isi.edu 80# mtrace -l caraway.lcs.mit.edu 224.2.0.3
+Mtrace from 18.26.0.170 to 128.9.160.100 via group 224.2.0.3
+Querying full reverse path...
+ 0 oak.isi.edu (128.9.160.100)
+ -1 cub.isi.edu (128.9.160.153) DVMRP thresh^ 1 3 ms
+ -2 la.dart.net (140.173.128.1) DVMRP thresh^ 1 14 ms
+ -3 dc.dart.net (140.173.64.1) DVMRP thresh^ 1 50 ms
+ -4 bbn.dart.net (140.173.32.1) DVMRP thresh^ 1 63 ms
+ -5 mit.dart.net (140.173.48.2) DVMRP thresh^ 1 71 ms
+ -6 caraway.lcs.mit.edu (18.26.0.170)
+Round trip time 124 ms; total ttl of 6 required.
+.fi
+.Pp
+If a hop reports that it is using the default route to forward packets,
+the word
+.Em [default]
+is printed after that hop. If the
+.Fl v
+flag is supplied, the route being used to forward packets is printed
+in the form
+.Em [18.26.0/24] .
+.Pp
+The second section provides a pictorial view of the path in the
+forward direction with data flow indicated by arrows pointing downward
+and the query path indicated by arrows pointing upward. For each hop,
+both the entry and exit addresses of the router are shown if
+different, along with the initial ttl required on the packet in order
+to be forwarded at this hop and the propagation delay across the hop
+assuming that the routers at both ends have synchronized clocks.
+The right half of this section is composed of two sets of statistics.
+The first column contains the average packet rate for all traffic at
+each hop.
+The remaining columns are the
+number of packets lost, the number of packets sent, the percentage
+lost, and the average packet rate at each hop. These statistics are
+calculated from differences between traces and from hop to hop as
+explained above. The first group shows the statistics for all traffic
+flowing out the interface at one hop and in the interface at the next
+hop. The second group shows the statistics only for traffic forwarded
+from the specified
+.Ar source
+to the specified
+.Ar group .
+The first group of statistics may be expanded to include loss rates
+using the
+.Fl T
+option. However, these numbers can be extremely misleading and require
+detailed knowledge of the routers involved to be interpreted properly.
+.Pp
+These statistics are shown on one or two lines for each hop. Without
+any options, this second section of the output is printed only once,
+approximately 10 seconds after the initial trace. One line is shown
+for each hop showing the statistics over that 10-second period. If
+the
+.Fl l
+option is given, the second section is repeated every 10 seconds and
+two lines are shown for each hop. The first line shows the statistics
+for the last 10 seconds, and the second line shows the cumulative
+statistics over the period since the initial trace, which is 101
+seconds in the example below. The second section of the output is
+omitted if the
+.Fl s
+option is set or if no multicast group is specified.
+.ie t \{\
+.ft C
+. ie \w'i'<>\w'm' \{\" looks like this is not proper Courier font
+(If this example is not properly columned with a fixed-width font, get
+.B groff
+and try again.)
+. \}
+.\}
+.Pp
+.ft C
+.nf
+Waiting to accumulate statistics... Results after 101 seconds:
+
+ Source Response Dest Overall Packet Statistics For Traffic From
+18.26.0.170 128.9.160.100 Packet 18.26.0.170 To 224.2.0.3
+ | __/ rtt 125 ms Rate Lost/Sent = Pct Rate
+ v / hop 65 ms ------- ---------------------
+18.26.0.144
+140.173.48.2 mit.dart.net
+ | ^ ttl 1 0 pps 0/2 = --% 0 pps
+ v | hop 8 ms 0 pps 0/18 = 0% 0 pps
+140.173.48.1
+140.173.32.1 bbn.dart.net
+ | ^ ttl 2 0 pps 0/2 = --% 0 pps
+ v | hop 12 ms 0 pps 0/18 = 0% 0 pps
+140.173.32.2
+140.173.64.1 dc.dart.net
+ | ^ ttl 3 27 pps 0/2 = --% 0 pps
+ v | hop 34 ms 26 pps 0/18 = 0% 0 pps
+140.173.64.2
+140.173.128.1 la.dart.net
+ | ^ ttl 4 83 pps 0/2 = --% 0 pps
+ v | hop 11 ms 79 pps 0/18 = 0% 0 pps
+140.173.128.2
+128.9.160.153 cub.isi.edu
+ | \\__ ttl 5 83 pps ?/2 0 pps
+ v \\ hop -8 ms 79 pps ?/18 0 pps
+128.9.160.100 128.9.160.100
+ Receiver Query Source
+.fi
+.Pp
+Because the packet counts may be changing as the trace query is
+propagating, there may be small errors (off by 1 or 2) in these
+statistics. However, those errors should not accumulate, so the
+cumulative statistics line should increase in accuracy as a new trace
+is run every 10 seconds. There are two sources of larger errors, both
+of which show up as negative losses:
+.Pp
+If the input to a node is from a multi-access network with more than
+one other node attached, then the input count will be (close to) the
+sum of the output counts from all the attached nodes, but the output
+count from the previous hop on the traced path will be only part of
+that. Hence the output count minus the input count will be negative.
+.Pp
+In release 3.3 of the DVMRP multicast forwarding software for SunOS
+and other systems, a multicast packet generated on a router will be
+counted as having come in an interface even though it did not. This
+creates the negative loss that can be seen in the example above.
+.Pp
+Note that these negative losses may mask positive losses.
+.Pp
+In the example, there is also one negative hop time. This simply
+indicates a lack of synchronization between the system clocks across
+that hop. This example also illustrates how the percentage loss is
+shown as two dashes when the number of packets sent is less than 10
+because the percentage would not be statistically valid.
+.Pp
+A second example shows a trace to a receiver that is not local; the
+query is sent to the last-hop router with the
+.Fl g
+option. In this example, the trace of the full reverse path resulted
+in no response because there was a node running an old version of
+.Nm mrouted
+that did not implement the multicast traceroute function, so
+.Nm
+switched to hop-by-hop mode. The \*(lqOutput pruned\*(rq error code
+indicates that traffic for group 224.2.143.24 would not be forwarded.
+.Pp
+.nf
+.ft C
+oak.isi.edu 108# mtrace -g 140.173.48.2 204.62.246.73 \\
+ butter.lcs.mit.edu 224.2.143.24
+Mtrace from 204.62.246.73 to 18.26.0.151 via group 224.2.143.24
+Querying full reverse path... * switching to hop-by-hop:
+ 0 butter.lcs.mit.edu (18.26.0.151)
+ -1 jam.lcs.mit.edu (18.26.0.144) DVMRP thresh^ 1 33 ms Output pruned
+ -2 bbn.dart.net (140.173.48.1) DVMRP thresh^ 1 36 ms
+ -3 dc.dart.net (140.173.32.2) DVMRP thresh^ 1 44 ms
+ -4 darpa.dart.net (140.173.240.2) DVMRP thresh^ 16 47 ms
+ -5 * * * noc.hpc.org (192.187.8.2) [mrouted 2.2] didn't respond
+Round trip time 95 ms
+.fi
+.Sh AUTHOR
+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 mrouted 8 ,
+.Xr mrinfo 8 ,
+.Xr map-mbone 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..40e84af
--- /dev/null
+++ b/usr.sbin/mrouted/mtrace.c
@@ -0,0 +1,2761 @@
+/*
+ * 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.
+ *
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id$";
+#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 32 /* Don't need more hops than max metric */
+#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][40];
+/*
+ * 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];
+};
+
+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;
+
+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; /* 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 no_op[4]; /* Null 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
+
+void init_igmp __P(());
+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 fixtime __P((u_long time, struct resp_buf *base));
+int send_recv __P((u_int32 dst, int type, int code,
+ int tries, struct resp_buf *save));
+char * print_host __P((u_int32 addr));
+char * print_host2 __P((u_int32 addr1, u_int32 addr2));
+void print_trace __P((int index, struct resp_buf *buf));
+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 print_stats __P((struct resp_buf *base,
+ struct resp_buf *prev,
+ struct resp_buf *new,
+ int *bugs));
+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;
+#ifdef SUNOS5
+ u_int32 localhost = htonl(0x7f000001);
+#endif
+
+ 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
+ if (!sendopts)
+ return;
+
+ no_op[0] = IPOPT_EOL;
+ no_op[1] = IPOPT_EOL;
+ no_op[2] = IPOPT_EOL;
+ no_op[3] = IPOPT_EOL;
+
+ setsockopt(igmp_socket, IPPROTO_IP, IP_OPTIONS, no_op, sizeof(no_op));
+ /*
+ * 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;
+
+ 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
+
+ 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 no-op's.
+ */
+ setsockopt(igmp_socket, IPPROTO_IP, IP_OPTIONS,
+ no_op, sizeof(no_op));
+#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);
+ 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 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 i;
+ char ifbuf[5000];
+ struct ifconf ifc;
+ struct ifreq *ifr;
+ u_int32 if_addr, if_mask;
+ u_int32 retval = 0xFFFFFFFF;
+ int found = FALSE;
+
+ ifc.ifc_buf = ifbuf;
+ ifc.ifc_len = sizeof(ifbuf);
+ if (ioctl(s, SIOCGIFCONF, (char *) &ifc) < 0) {
+ warn("ioctl (SIOCGIFCONF)");
+ return (retval);
+ }
+ i = ifc.ifc_len / sizeof(struct ifreq);
+ ifr = ifc.ifc_req;
+ for (; i > 0; i--, ifr++) {
+ if_addr = ((struct sockaddr_in *)&(ifr->ifr_addr))->sin_addr.s_addr;
+ if (ioctl(s, SIOCGIFNETMASK, (char *)ifr) >= 0) {
+ if_mask = ((struct sockaddr_in *)&(ifr->ifr_addr))->sin_addr.s_addr;
+ if ((dst & if_mask) == (if_addr & if_mask)) {
+ retval = if_mask;
+ if (lcl_addr == 0) lcl_addr = if_addr;
+ }
+ }
+ 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);
+}
+
+/*
+ * Fixup for incorrect time format in 3.3 mrouted.
+ * This is possible because (JAN_1970 mod 64K) is quite close to 32K,
+ * so correct and incorrect times will be far apart.
+ */
+u_long
+fixtime(time, base)
+ u_long time;
+ struct resp_buf *base;
+{
+ if (abs((int)(time-base->qtime)) > 0x3FFFFFFF)
+ time = ((time & 0xFFFF0000) + (JAN_1970 << 16)) +
+ ((time & 0xFFFF) << 14) / 15625;
+ return (time);
+}
+
+/*
+ * 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));
+}
+
+int
+send_recv(dst, type, code, tries, save)
+ u_int32 dst;
+ int type, code, tries;
+ struct resp_buf *save;
+{
+ 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;
+ query->tr_rttl = 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) {
+ if (tries == nqueries && raddr == 0) {
+ if (i == (nqueries >> 1)) {
+ if (multicast && unicast) {
+ query->tr_raddr = resp_cast;
+ if (!rttl)
+ query->tr_rttl = get_ttl(save);
+ } else if (!multicast) {
+ query->tr_raddr = lcl_addr;
+ query->tr_rttl = UNICAST_TTL;
+ }
+ }
+ if (i < tries && IN_MULTICAST(ntohl(query->tr_raddr)) &&
+ rttl == 0) {
+ query->tr_rttl += MULTICAST_TTL_INC;
+ if (query->tr_rttl > MULTICAST_TTL_MAX)
+ query->tr_rttl = MULTICAST_TTL_MAX;
+ }
+ }
+
+ /*
+ * Change the qid for each request sent to avoid being confused
+ * by duplicate responses
+ */
+#ifdef SYSV
+ query->tr_qid = ((u_int32)lrand48() >> 8);
+#else
+ query->tr_qid = ((u_int32)random() >> 8);
+#endif
+
+ /*
+ * Set timer to calculate delays, then send query
+ */
+ gettimeofday(&tq, 0);
+ send_igmp(local, dst, type, code, group, datalen);
+
+ /*
+ * Wait for response, discarding false alarms
+ */
+ while (TRUE) {
+ FD_ZERO(&fds);
+ FD_SET(igmp_socket, &fds);
+ gettimeofday(&tv, 0);
+ tv.tv_sec = tq.tv_sec + timeout - tv.tv_sec;
+ tv.tv_usec = tq.tv_usec - tv.tv_usec;
+ if (tv.tv_usec < 0) tv.tv_usec += 1000000L, --tv.tv_sec;
+ if (tv.tv_sec < 0) tv.tv_sec = tv.tv_usec = 0;
+
+ count = select(igmp_socket + 1, &fds, (fd_set *)0, (fd_set *)0,
+ &tv);
+
+ if (count < 0) {
+ if (errno != EINTR) warn("select");
+ continue;
+ } else if (count == 0) {
+ printf("* ");
+ fflush(stdout);
+ break;
+ }
+
+ 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;
+ ipdatalen = ip->ip_len;
+ 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_qid != query->tr_qid) continue;
+ if (rquery->tr_src != qsrc) continue;
+ if (rquery->tr_dst != qdst) 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 */
+ rquery->tr_rttl = query->tr_rttl; /* as we sent them */
+ break;
+
+ default:
+ 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;
+ 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;
+ u_int32 smask;
+ struct mtrace *remembered = NULL, *m, *n, **nn;
+ int pc = 0;
+
+ if (raddr) {
+ if (IN_MULTICAST(ntohl(raddr))) k_join(raddr, INADDR_ANY);
+ } else k_join(htonl(0xE0000120), INADDR_ANY);
+
+ 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;
+ ipdatalen = ip->ip_len;
+ 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;
+ if (tr.tv_sec - n->last.tv_sec > 500) { /* XXX don't hardcode */
+ *nn = n->next;
+ free(n);
+ } else {
+ nn = &n->next;
+ }
+ }
+
+ if (m)
+ bcopy(&tr, &m->last, sizeof(tr));
+
+ now = localtime(&tr.tv_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", base.qhdr.tr_rttl);
+ printf(", qid %06x\n", base.qhdr.tr_qid);
+ 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 exactly the same as the
+ * last one.
+ */
+ if (bcmp((char *)&base.igmp, (char *)&m->prev->igmp, ipdatalen) == 0)
+ continue;
+
+ ++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->prev = m->new;
+ m->new = &m->incr[(m->nresp & 1)];
+
+ continue;
+ }
+
+ if (m == NULL) {
+ m = (struct mtrace *)malloc(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);
+ 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(index, buf)
+ int index;
+ struct resp_buf *buf;
+{
+ struct tr_resp *r;
+ char *name;
+ int i;
+ int hop;
+ char *ms;
+
+ i = abs(index);
+ r = buf->resps + i - 1;
+
+ for (; i <= buf->len; ++i, ++r) {
+ if (index > 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(fixtime(ntohl(r->tr_qarr), &base), 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");
+ memcpy(names[i-1], name, sizeof(names[0]) - 1);
+ names[i-1][sizeof(names[0])-1] = '\0';
+ }
+}
+
+/*
+ * 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]);
+ 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;
+{
+ /* this may fail in passive statistics mode due to wrong "base". */
+ int timediff = (fixtime(ntohl(s->tr_qarr), &base) -
+ fixtime(ntohl(r->tr_qarr), &base)) >> 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;
+
+ if (timediff == 0) timediff = 1;
+ v_pps = v_out / timediff;
+ g_pps = g_out / timediff;
+
+#define STATS_MISSING(x) ((x) == 0xFFFFFFFF || (x) == 0)
+
+ if (v_out && !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;
+ }
+
+ /*
+ * 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;
+
+ 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 >> 1)) / v_out;
+ else v_pct = 0;
+ if (-100 < v_pct && v_pct < 101 && v_out > 10)
+ sprintf(v_str, "%3d", v_pct);
+ else memcpy(v_str, " --", 4);
+
+ if (tunstats)
+ printf("%6d/%-5d=%s%%", v_lost, v_out, v_str);
+ else
+ printf(" ");
+ printf("%4d pps", v_pps);
+
+ break;
+
+ case INS:
+ v_out = ntohl(s->tr_vifin) - ntohl(r->tr_vifin);
+ v_pps = v_out / timediff;
+ whochar = 'v';
+ /* Fall through */
+
+ case OUTS:
+ if (tunstats)
+ printf(" %c%-5d ", whochar, v_out);
+ else
+ printf(" %c", whochar);
+ printf("%4d pps", v_pps);
+
+ 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 >> 1))/ g_out;
+ else g_pct = 0;
+ if (-100 < g_pct && g_pct < 101 && g_out > 10)
+ sprintf(g_str, "%3d", g_pct);
+ else memcpy(g_str, " --", 4);
+
+ printf("%s%6d/%-5d=%s%%%4d pps\n",
+ tunstats ? "" : " ", g_lost, g_out, g_str, g_pps);
+ break;
+
+#if 0
+ case INS:
+ g_out = ntohl(s->tr_pktcnt) - ntohl(r->tr_pktcnt);
+ g_pps = g_out / timediff;
+ whochar = 'v';
+ /* Fall through */
+#endif
+
+ case OUTS:
+ printf("%s ?/%-5d %4d pps\n",
+ tunstats ? "" : " ", g_out, g_pps);
+ break;
+
+ case INS:
+ case NEITHER:
+ printf("\n");
+ break;
+ }
+
+
+ if (debug > 2) {
+ printf("\t\t\t\tv_in: %ld ", ntohl(s->tr_vifin));
+ printf("v_out: %ld ", ntohl(s->tr_vifout));
+ printf("pkts: %ld\n", ntohl(s->tr_pktcnt));
+ printf("\t\t\t\tv_in: %ld ", ntohl(r->tr_vifin));
+ printf("v_out: %ld ", ntohl(r->tr_vifout));
+ printf("pkts: %ld\n", ntohl(r->tr_pktcnt));
+ printf("\t\t\t\tv_in: %ld ",ntohl(s->tr_vifin)-ntohl(r->tr_vifin));
+ printf("v_out: %ld ", ntohl(s->tr_vifout) - ntohl(r->tr_vifout));
+ printf("pkts: %ld ", ntohl(s->tr_pktcnt) - ntohl(r->tr_pktcnt));
+ printf("time: %d\n", timediff);
+ }
+}
+
+/*
+ * A fixup to check if any pktcnt has been reset, and to fix the
+ * byteorder bugs in mrouted 3.6 on little-endian machines.
+ */
+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;
+
+ /* Check for byte-swappers */
+ while (--rno >= 0) {
+ --n; --p; --b;
+ if ((*r & BUG_SWAP) ||
+ abs(ntohl(n->tr_vifout) - ntohl(p->tr_vifout)) > 100000) {
+ /* 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);
+ }
+ /*
+ * 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;
+ res = ((ntohl(n->tr_pktcnt) < ntohl(b->tr_pktcnt)) ||
+ (ntohl(n->tr_pktcnt) < ntohl(p->tr_pktcnt)));
+ if (debug > 2)
+ printf("\t\tr=%d, res=%d\n", *r, res);
+ 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;
+ break;
+ }
+ } else
+ if (res)
+ *r |= BUG_RESET;
+ }
+
+ if (rno < 0) return;
+
+ rno = base->len;
+ b = base->resps + rno;
+ p = prev->resps + rno;
+
+ while (--rno >= 0) (--b)->tr_pktcnt = (--p)->tr_pktcnt;
+}
+
+/*
+ * Print responses with statistics for forward path (from src to dst)
+ */
+int
+print_stats(base, prev, new, bugs)
+ struct resp_buf *base, *prev, *new;
+ int *bugs;
+{
+ 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 = fixtime(ntohl(n->tr_qarr), base);
+ u_int ttl = 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 (debug > 2) {
+ printf("\t\t\t\tv_in: %ld ", ntohl(n->tr_vifin));
+ printf("v_out: %ld ", ntohl(n->tr_vifout));
+ printf("pkts: %ld\n", ntohl(n->tr_pktcnt));
+ printf("\t\t\t\tv_in: %ld ", ntohl(b->tr_vifin));
+ printf("v_out: %ld ", ntohl(b->tr_vifout));
+ printf("pkts: %ld\n", ntohl(b->tr_pktcnt));
+ printf("\t\t\t\tv_in: %ld ", ntohl(n->tr_vifin) - ntohl(b->tr_vifin));
+ printf("v_out: %ld ", ntohl(n->tr_vifout) - ntohl(b->tr_vifout));
+ printf("pkts: %ld\n", ntohl(n->tr_pktcnt) - ntohl(b->tr_pktcnt));
+ printf("\t\t\t\treset: %x\n", *r);
+ }
+
+ 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 = fixtime(ntohl((n-1)->tr_qarr), base);
+ 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, 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);
+ }
+ /* lcl_addr is 0 in passive mode, where we don't know the query source. */
+ printf("%-15s %s\n", inet_fmt(base->qhdr.tr_dst, s1),
+ lcl_addr ? 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;
+
+ if (geteuid() != 0)
+ errx(1, "must be root");
+
+ 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 'O': /* Don't use IP options */
+ sendopts = FALSE;
+ break;
+ case 'P': /* Just watch the path */
+ printstats = FALSE;
+ numstats = 3153600;
+ 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 */
+ {
+ char *p = strchr(rcsid, ',');
+
+ while (p && *(p+1) != 'v')
+ p = strchr(p + 1, ',');
+
+ if (p) {
+ char *q;
+
+ p += 3; /* , v sp */
+ q = strchr(p, ' ');
+ if (q)
+ *q = '\0';
+ fprintf(stderr, "mtrace version %s\n", p);
+ } 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 '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();
+ }
+
+ /*
+ * Set useful defaults for as many parameters as possible.
+ */
+
+ defgrp = htonl(0xE0020001); /* MBone Audio (224.2.0.1) */
+ 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 */
+
+ 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) {
+ warn("getting my hostname");
+ exit(-1);
+ }
+
+ hp = gethostbyname(myhostname);
+ if (hp == NULL || hp->h_addrtype != AF_INET ||
+ hp->h_length != sizeof(addr.sin_addr)) {
+ warn("finding IP address for my hostname");
+ exit(-1);
+ }
+
+ 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();
+ dst_netmask = get_netmask(udp, qdst);
+ close(udp);
+ if (lcl_addr == 0) lcl_addr = addr.sin_addr.s_addr;
+
+ /*
+ * Initialize the seed for random query identifiers.
+ */
+ gettimeofday(&tv, 0);
+ seed = tv.tv_usec ^ lcl_addr;
+#ifdef SYSV
+ srand48(seed);
+#else
+ srandom(seed);
+#endif
+
+ /*
+ * Protect against unicast queries to mrouted versions that might crash.
+ * Also use the obsolete "can mtrace" neighbor bit to warn about
+ * older implementations.
+ */
+ if (gwy && !IN_MULTICAST(ntohl(gwy)))
+ if (send_recv(gwy, IGMP_DVMRP, DVMRP_ASK_NEIGHBORS2, 1, &incr[0])) {
+ 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)) {
+ printf("Source & receiver are directly connected, no path to trace\n");
+ exit(0);
+ }
+
+ /*
+ * 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 && weak)
+ errx(1, "-W requires -g if destination is not local");
+
+ 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;
+ printf("Querying reverse path, maximum %d hops... ", qno);
+ fflush(stdout);
+ }
+ base.rtime = 0;
+ base.len = 0;
+
+ recvlen = send_recv(tdst, IGMP_MTRACE, hops, tries, &base);
+
+ /*
+ * 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) {
+ hopbyhop = FALSE;
+ printf("\n 0 ");
+ print_host(qdst);
+ printf("\n");
+ print_trace(1, &base);
+ 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)) {
+ printf("%3d ", -(base.len+1));
+ what_kind(&base, r->tr_rflags == TR_OLD_ROUTER ?
+ "doesn't support mtrace"
+ : "is the next hop");
+ } else {
+ 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 &&
+ (recvlen = send_recv(lastout, IGMP_MTRACE, hops, 1, &base)))
+ tdst = lastout;
+ else recvlen = send_recv(tdst, IGMP_MTRACE, hops, nqueries, &base);
+
+ if (recvlen == 0) {
+ if (hops == 1) break;
+ if (hops == nexthop) {
+ 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);
+ } else {
+ printf("\nResuming...\n");
+ print_trace(nexthop, &base);
+ }
+ } 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);
+ } else if (nexthop > base.len + 1) {
+ hops = base.len;
+ printf("\nRoute must have changed...\n");
+ print_trace(1, &base);
+ }
+ } else {
+ /*
+ * The last hop address is not the same as it was;
+ * the route probably changed underneath us.
+ */
+ hops = base.len;
+ printf("\nRoute must have changed...\n");
+ print_trace(1, &base);
+ }
+ }
+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, 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 = base.qhdr.tr_rttl;
+ 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 {
+ printf("%s", printstats ? "Waiting to accumulate statistics... "
+ : ".");
+ fflush(stdout);
+ sleep((unsigned)waittime);
+ }
+ rno = hopbyhop ? base.len : qno ? qno : MAXHOPS;
+ recvlen = send_recv(tdst, IGMP_MTRACE, rno, nqueries, new);
+
+ 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);
+ numstats++;
+ bcopy(new, &base, sizeof(base));
+ nexthop = hops = new->len;
+ printf("Continuing with hop-by-hop...\n");
+ goto continuehop;
+ }
+
+ if (printstats) {
+ printf("Results after %d seconds:\n\n",
+ (int)((new->qtime - base.qtime) >> 16));
+ fixup_stats(&base, prev, new, bugs);
+ if (print_stats(&base, prev, new, bugs)) {
+ 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 [-Mlnps] [-w wait] [-m max_hops] [-q nqueries]",
+ " [-g gateway] [-S statint] [-t ttl] [-r resp_dest]",
+ " [-i if_addr] 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) strcat(fmt, "warning - ");
+ strncat(fmt, format, 80);
+ 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);
+}
+
+/* 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 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_neighbors(src, dst, p, datalen, level)
+ u_int32 src, dst, level;
+ u_char *p;
+ int datalen;
+{
+}
+void accept_neighbors2(src, dst, p, datalen, level)
+ u_int32 src, dst, level;
+ u_char *p;
+ int datalen;
+{
+}
+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/mtrace.h b/usr.sbin/mrouted/mtrace.h
new file mode 100644
index 0000000..1d70491
--- /dev/null
+++ b/usr.sbin/mrouted/mtrace.h
@@ -0,0 +1,87 @@
+/*
+ * Multicast traceroute related definitions
+ *
+ * mtrace.h,v 5.1 1996/12/19 21:31:26 fenner Exp
+ */
+
+/*
+ * 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_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 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..1927f0d
--- /dev/null
+++ b/usr.sbin/mrouted/mtrace/Makefile
@@ -0,0 +1,14 @@
+# $Id$
+
+PROG= mtrace
+
+S= ${.CURDIR}/..
+.PATH: $S
+CFLAGS+= -I$S
+
+SRCS= mtrace.c
+MAN8= ${.CURDIR}/../mtrace.8
+BINOWN= root
+BINMODE=4555
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/mrouted/pathnames.h b/usr.sbin/mrouted/pathnames.h
new file mode 100644
index 0000000..b8fb6ba
--- /dev/null
+++ b/usr.sbin/mrouted/pathnames.h
@@ -0,0 +1,25 @@
+/*
+ * The mrouted program is covered by the license in the accompanying file
+ * named "LICENSE". Use of the mrouted program represents acceptance of
+ * the terms and conditions listed in that file.
+ *
+ * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+ * Leland Stanford Junior University.
+ *
+ *
+ * $Id$
+ */
+
+#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..d18137e
--- /dev/null
+++ b/usr.sbin/mrouted/prune.c
@@ -0,0 +1,2302 @@
+/*
+ * The mrouted program is covered by the license in the accompanying file
+ * named "LICENSE". Use of the mrouted program represents acceptance of
+ * the terms and conditions listed in that file.
+ *
+ * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+ * Leland Stanford Junior University.
+ *
+ *
+ * $Id: prune.c,v 1.13 1997/02/22 16:07:03 peter Exp $
+ */
+
+
+#include "defs.h"
+
+extern int cache_lifetime;
+extern int max_prune_lifetime;
+extern struct rtentry *routing_table;
+
+extern int phys_vif;
+
+/*
+ * dither cache lifetime to obtain a value between x and 2*x
+ */
+#ifdef SYSV
+#define CACHE_LIFETIME(x) ((x) + (lrand48() % (x)))
+#else
+#define CACHE_LIFETIME(x) ((x) + (random() % (x)))
+#endif
+
+#define CHK_GS(x, y) { \
+ switch(x) { \
+ case 2: \
+ case 4: \
+ case 8: \
+ case 16: \
+ case 32: \
+ case 64: \
+ case 128: \
+ case 256: y = 1; \
+ break; \
+ default: y = 0; \
+ } \
+ }
+
+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 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 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));
+static void update_kernel __P((struct gtable *g));
+static char * scaletime __P((u_long t));
+
+/*
+ * 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
+ */
+#define GET_SCOPE(gt) { \
+ register vifi_t _i; \
+ if ((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); \
+ }
+
+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 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;
+
+ if (n->al_flags & NF_PRUNE)
+ return 1;
+
+ /*
+ * Versions from 3.0 to 3.4 relied on the version number to identify
+ * that they could handle pruning.
+ */
+ vers = NBR_VERS(n);
+ return (vers >= 0x0300 && vers <= 0x0304);
+}
+
+/*
+ * 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 0;
+
+ if (n->al_flags & NF_MTRACE)
+ return 1;
+
+ /*
+ * Versions 3.3 and 3.4 relied on the version number to identify
+ * that they could handle traceroute.
+ */
+ vers = NBR_VERS(n);
+ return (vers >= 0x0303 && vers <= 0x0304);
+}
+
+/*
+ * 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;
+}
+
+/*
+ * 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 src;
+ u_int32 dst;
+ u_int32 tmp;
+
+ /* Don't process any prunes if router is not pruning */
+ if (pruning == 0)
+ return;
+
+ /* Can't process a prune if we don't have an associated route */
+ if (gt->gt_route == NULL)
+ 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;
+
+ /*
+ * sends a prune message to the router upstream.
+ */
+ src = uvifs[gt->gt_route->rt_parent].uv_lcl_addr;
+ dst = gt->gt_route->rt_gateway;
+
+ p = send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN;
+ datalen = 0;
+
+ /*
+ * determine prune lifetime
+ */
+ gt->gt_prsent_timer = gt->gt_timer;
+ 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;
+
+ /*
+ * 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_igmp(src, dst, IGMP_DVMRP, DVMRP_PRUNE,
+ htonl(MROUTED_LEVEL), datalen);
+
+ log(LOG_DEBUG, 0, "sent prune for (%s %s)/%d on vif %d to %s",
+ inet_fmts(gt->gt_route->rt_origin, gt->gt_route->rt_originmask, s1),
+ inet_fmt(gt->gt_mcastgrp, s2),
+ gt->gt_prsent_timer, gt->gt_route->rt_parent,
+ inet_fmt(gt->gt_route->rt_gateway, s3));
+}
+
+/*
+ * 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 src;
+ u_int32 dst;
+
+ /* Can't send a graft without an associated route */
+ if (gt->gt_route == NULL)
+ return;
+
+ src = uvifs[gt->gt_route->rt_parent].uv_lcl_addr;
+ dst = gt->gt_route->rt_gateway;
+
+ 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;
+
+ if (datalen != 0) {
+ send_igmp(src, dst, IGMP_DVMRP, DVMRP_GRAFT,
+ htonl(MROUTED_LEVEL), datalen);
+ }
+ log(LOG_DEBUG, 0, "sent graft for (%s %s) to %s on vif %d",
+ inet_fmts(gt->gt_route->rt_origin, gt->gt_route->rt_originmask, 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)
+ u_int32 src;
+ u_int32 dst;
+ u_int32 origin;
+ u_int32 grp;
+{
+ 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;
+
+ send_igmp(src, dst, IGMP_DVMRP, DVMRP_GRAFT_ACK,
+ htonl(MROUTED_LEVEL), datalen);
+
+ log(LOG_DEBUG, 0, "sent graft ack for (%s, %s) to %s",
+ inet_fmt(origin, s1), inet_fmt(grp, s2), inet_fmt(dst, s3));
+}
+
+/*
+ * 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)
+ 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_long 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_long 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_long grp;
+ u_long src;
+ u_long 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);
+ }
+}
+
+/*
+ * 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_long 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_long src;
+ u_long 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_long src;
+ u_long mask;
+ vifi_t *vifi; /* vif at which to start looking */
+{
+ struct rtentry *rt;
+
+ /* 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;
+}
+
+/*
+ * 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_long grp;
+ u_long src;
+ u_long mask;
+ vifi_t *vifi; /* vif at which to start looking */
+{
+ struct stable *st;
+
+ /* 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;
+ vifi_t i;
+
+#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_grpmems = 0;
+ gt->gt_scope = 0;
+ gt->gt_prsent_timer = 0;
+ gt->gt_grftsnt = 0;
+ gt->gt_srctbl = NULL;
+ gt->gt_pruntbl = NULL;
+ gt->gt_route = r;
+#ifdef RSRR
+ gt->gt_rsrr_cache = NULL;
+#endif
+
+ if (r != NULL) {
+ /* obtain the multicast group membership list */
+ for (i = 0; i < numvifs; i++) {
+ if (VIFM_ISSET(i, r->rt_children) &&
+ !(VIFM_ISSET(i, r->rt_leaves)))
+ VIFM_SET(i, gt->gt_grpmems);
+
+ if (VIFM_ISSET(i, r->rt_leaves) && grplst_mem(i, mcastgrp))
+ VIFM_SET(i, gt->gt_grpmems);
+ }
+ GET_SCOPE(gt);
+ if (VIFM_ISSET(r->rt_parent, gt->gt_scope))
+ gt->gt_scope = -1;
+ gt->gt_grpmems &= ~gt->gt_scope;
+ } else {
+ gt->gt_scope = -1;
+ gt->gt_grpmems = 0;
+ }
+
+ /* 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)",
+ inet_fmts(r->rt_origin, r->rt_originmask, 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_next = *stnp;
+ *stnp = st;
+ } else {
+#ifdef DEBUG_MFC
+ md_log(MD_DUPE, origin, mcastgrp);
+#endif
+ log(LOG_WARNING, 0, "kernel entry already exists for (%s %s)",
+ inet_fmt(origin, s1), inet_fmt(mcastgrp, s2));
+ /* XXX Doing this should cause no harm, and may ensure
+ * kernel<>mrouted synchronization */
+ k_add_rg(origin, gt);
+ return;
+ }
+
+ kroutes++;
+ k_add_rg(origin, gt);
+
+ 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 leaf vifs
+ * which have this group, then
+ * mark this src-grp as a prune candidate.
+ */
+ if (!gt->gt_prsent_timer && !gt->gt_grpmems && r && r->rt_gateway)
+ send_prune(gt);
+}
+
+/*
+ * An mrouter has gone down and come up on an interface
+ * Forward on that interface immediately
+ */
+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) {
+ log(LOG_DEBUG, 0, "reset_neighbor_state parent reset (%s %s)",
+ inet_fmts(r->rt_origin, r->rt_originmask, s1),
+ inet_fmt(g->gt_mcastgrp, s2));
+
+ g->gt_prsent_timer = 0;
+ g->gt_grftsnt = 0;
+ while ((st = g->gt_srctbl)) {
+ g->gt_srctbl = st->st_next;
+ k_del_rg(st->st_origin, g);
+ kroutes--;
+ free(st);
+ }
+ }
+ } else {
+ /*
+ * Neighbor was not the parent, send grafts to join the groups
+ */
+ if (g->gt_prsent_timer) {
+ g->gt_grftsnt = 1;
+ send_graft(g);
+ g->gt_prsent_timer = 0;
+ }
+
+ /*
+ * 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) {
+ *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)) {
+ if (VIFM_ISSET(vifi, r->rt_children) &&
+ !(VIFM_ISSET(vifi, r->rt_leaves)))
+ VIFM_SET(vifi, g->gt_grpmems);
+
+ if (VIFM_ISSET(vifi, r->rt_leaves) &&
+ grplst_mem(vifi, g->gt_mcastgrp))
+ VIFM_SET(vifi, g->gt_grpmems);
+
+ g->gt_grpmems &= ~g->gt_scope;
+ 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 */
+
+ log(LOG_DEBUG, 0, "reset member state (%s %s) gm:%x",
+ inet_fmts(r->rt_origin, r->rt_originmask, 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) {
+ log(LOG_DEBUG, 0, "del_table_entry deleting (%s %s)",
+ inet_fmts(r->rt_origin, r->rt_originmask, s1),
+ inet_fmt(g->gt_mcastgrp, s2));
+ st = g->gt_srctbl;
+ while (st) {
+ 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 */
+ 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) {
+ log(LOG_DEBUG, 0, "del_table_entry deleting (%s %s)",
+ inet_fmts(r->rt_origin, r->rt_originmask, s1),
+ inet_fmt(g->gt_mcastgrp, s2));
+ st = g->gt_srctbl;
+ while (st) {
+ 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;
+
+#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)
+ struct rtentry *r;
+{
+ struct gtable *g;
+ struct ptable *pt, *prev_pt;
+ vifi_t i;
+
+ for (g = r->rt_groups; g; g = g->gt_next) {
+ pt = g->gt_pruntbl;
+ while (pt) {
+ prev_pt = pt->pt_next;
+ free(pt);
+ pt = prev_pt;
+ }
+ g->gt_pruntbl = NULL;
+
+ g->gt_grpmems = 0;
+
+ /* obtain the multicast group membership list */
+ for (i = 0; i < numvifs; i++) {
+ if (VIFM_ISSET(i, r->rt_children) &&
+ !(VIFM_ISSET(i, r->rt_leaves)))
+ VIFM_SET(i, g->gt_grpmems);
+
+ if (VIFM_ISSET(i, r->rt_leaves) && grplst_mem(i, g->gt_mcastgrp))
+ VIFM_SET(i, g->gt_grpmems);
+ }
+ if (VIFM_ISSET(r->rt_parent, g->gt_scope))
+ g->gt_scope = -1;
+ g->gt_grpmems &= ~g->gt_scope;
+
+ log(LOG_DEBUG, 0, "updating cache entries (%s %s) gm:%x",
+ inet_fmts(r->rt_origin, r->rt_originmask, s1),
+ inet_fmt(g->gt_mcastgrp, s2),
+ g->gt_grpmems);
+
+ if (g->gt_grpmems && g->gt_prsent_timer) {
+ g->gt_grftsnt = 1;
+ send_graft(g);
+ g->gt_prsent_timer = 0;
+ }
+
+ /* 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 */
+
+ /* Check if we want to prune this group */
+ if (!g->gt_prsent_timer && g->gt_grpmems == 0 && r->rt_gateway) {
+ g->gt_timer = CACHE_LIFETIME(cache_lifetime);
+ send_prune(g);
+ }
+ }
+}
+
+/*
+ * 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;
+
+ 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);
+ g->gt_grpmems &= ~g->gt_scope;
+ if (g->gt_grpmems == 0)
+ continue;
+
+ prun_add_ttls(g);
+ log(LOG_DEBUG, 0, "update lclgrp (%s %s) gm:%x",
+ inet_fmts(r->rt_origin, r->rt_originmask, 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 rtentry *r;
+ struct gtable *g;
+
+ 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) {
+ int stop_sending = 1;
+
+ r = g->gt_route;
+ /*
+ * If this is not a leaf, then we have router neighbors on this
+ * vif. Only turn off forwarding if they have all pruned.
+ */
+ if (!VIFM_ISSET(vifi, r->rt_leaves)) {
+ struct listaddr *vr;
+
+ for (vr = uvifs[vifi].uv_neighbors; vr; vr = vr->al_next)
+ if (find_prune_entry(vr->al_addr, g->gt_pruntbl) == NULL) {
+ stop_sending = 0;
+ break;
+ }
+ }
+
+ if (stop_sending) {
+ VIFM_CLR(vifi, g->gt_grpmems);
+ log(LOG_DEBUG, 0, "delete lclgrp (%s %s) gm:%x",
+ inet_fmts(r->rt_origin, r->rt_originmask, 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 (!g->gt_prsent_timer && g->gt_grpmems == 0 && r->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;
+ int stop_sending;
+ struct rtentry *r;
+ struct gtable *g;
+ struct ptable *pt;
+ struct listaddr *vr;
+
+ /* Don't process any prunes if router is not pruning */
+ if (pruning == 0)
+ return;
+
+ 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);
+
+ 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 (!VIFM_ISSET(vifi, r->rt_children)) {
+ log(LOG_WARNING, 0, "prune received from non-child %s for (%s %s)",
+ inet_fmt(src, s1), inet_fmt(prun_src, s2),
+ inet_fmt(prun_grp, s3));
+ 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) {
+ 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 {
+ /* 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;
+ }
+
+ /* Refresh the group's lifetime */
+ g->gt_timer = CACHE_LIFETIME(cache_lifetime);
+ if (g->gt_timer < prun_tmr)
+ g->gt_timer = prun_tmr;
+
+ /*
+ * check if any more packets need to be sent on the
+ * vif which sent this message
+ */
+ stop_sending = 1;
+ for (vr = uvifs[vifi].uv_neighbors; vr; vr = vr->al_next)
+ if (find_prune_entry(vr->al_addr, g->gt_pruntbl) == NULL) {
+ stop_sending = 0;
+ break;
+ }
+
+ if (stop_sending && !grplst_mem(vifi, prun_grp)) {
+ VIFM_CLR(vifi, g->gt_grpmems);
+ log(LOG_DEBUG, 0, "prune (%s %s), stop sending on vif %d, gm:%x",
+ inet_fmts(r->rt_origin, r->rt_originmask, 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 (!g->gt_prsent_timer && g->gt_grpmems == 0 && 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.
+ */
+ 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
+ */
+ g->gt_grpmems &= ~g->gt_scope;
+ if (g->gt_grpmems == 0)
+ continue;
+
+ /* set the flag for graft retransmission */
+ g->gt_grftsnt = 1;
+
+ /* send graft upwards */
+ send_graft(g);
+
+ /* reset the prune timer and update cache timer*/
+ g->gt_prsent_timer = 0;
+ g->gt_timer = max_prune_lifetime;
+
+ log(LOG_DEBUG, 0, "chkgrp graft (%s %s) gm:%x",
+ inet_fmts(r->rt_origin, r->rt_originmask, 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 ((vifi = find_vif(src, dst)) == NO_VIF) {
+ log(LOG_INFO, 0,
+ "ignoring graft from non-neighbor %s",
+ inet_fmt(src, s1));
+ return;
+ }
+
+ 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++;
+
+ 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)) {
+ *ptnp = pt->pt_next;
+ free(pt);
+
+ VIFM_SET(vifi, g->gt_grpmems);
+ log(LOG_DEBUG, 0, "accept graft (%s %s) gm:%x",
+ inet_fmts(r->rt_origin, r->rt_originmask, 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;
+ }
+ }
+
+ /* send ack downstream */
+ send_graft_ack(dst, src, graft_src, graft_grp);
+ g->gt_timer = max_prune_lifetime;
+
+ if (g->gt_prsent_timer) {
+ /* set the flag for graft retransmission */
+ g->gt_grftsnt = 1;
+
+ /* send graft upwards */
+ send_graft(g);
+
+ /* reset the prune sent timer */
+ g->gt_prsent_timer = 0;
+ }
+ } else {
+ /*
+ * We have no state for the source and group in question.
+ * We can simply acknowledge the graft, since we know
+ * that we have no prune state, and grafts are requests
+ * to remove prune state.
+ */
+ send_graft_ack(dst, src, graft_src, graft_grp);
+ 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++;
+
+ 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));
+ 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;
+ 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;
+ 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 ((rt->rt_origin & rp->rt_originmask) == rp->rt_origin) {
+ log(LOG_DEBUG, 0, "Route for %s stealing sources from %s",
+ inet_fmts(rt->rt_origin, rt->rt_originmask, s1),
+ inet_fmts(rp->rt_origin, rp->rt_originmask, 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) {
+ log(LOG_DEBUG, 0, "%s stealing (%s %s) from %s",
+ inet_fmts(rt->rt_origin, rt->rt_originmask, s1),
+ inet_fmt(st->st_origin, s3),
+ inet_fmt(gt->gt_mcastgrp, s4),
+ inet_fmts(rp->rt_origin, rp->rt_originmask, s2));
+ 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));
+ }
+ *stnp = st->st_next;
+ kroutes--;
+ 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)) {
+ log(LOG_DEBUG, 0, "%s stealing (%s %s) from %s",
+ inet_fmts(rt->rt_origin, rt->rt_originmask, s1),
+ inet_fmt(gt->gt_srctbl->st_origin, s3),
+ inet_fmt(gt->gt_mcastgrp, s4),
+ "no_route table");
+ 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;
+ 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;
+
+ log(LOG_DEBUG, 0, "ageing entries");
+
+ gtnptr = &kernel_table;
+ while ((gt = *gtnptr) != NULL) {
+ r = gt->gt_route;
+
+ /* advance the timer for the kernel entry */
+ gt->gt_timer -= ROUTE_MAX_REPORT_DELAY;
+
+ /* decrement prune timer if need be */
+ if (gt->gt_prsent_timer > 0) {
+ gt->gt_prsent_timer -= ROUTE_MAX_REPORT_DELAY;
+ if (gt->gt_prsent_timer <= 0) {
+ log(LOG_DEBUG, 0, "upstream prune tmo (%s %s)",
+ inet_fmts(r->rt_origin, r->rt_originmask, s1),
+ inet_fmt(gt->gt_mcastgrp, s2));
+ gt->gt_prsent_timer = -1;
+ }
+ }
+
+ /* retransmit graft if graft sent flag is still set */
+ if (gt->gt_grftsnt) {
+ register int y;
+ CHK_GS(gt->gt_grftsnt++, y);
+ if (y)
+ 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 -= ROUTE_MAX_REPORT_DELAY) <= 0) {
+ log(LOG_DEBUG, 0, "expire prune (%s %s) from %s on vif %d",
+ inet_fmts(r->rt_origin, r->rt_originmask, s1),
+ inet_fmt(gt->gt_mcastgrp, s2),
+ inet_fmt(pt->pt_router, s3),
+ pt->pt_vifi);
+
+ 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) {
+ /* Check for traffic before deleting source entries */
+ sg_req.grp.s_addr = gt->gt_mcastgrp;
+ stnp = &gt->gt_srctbl;
+ while ((st = *stnp) != NULL) {
+ 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;
+ }
+ if (sg_req.pktcnt == st->st_pktcnt) {
+ *stnp = st->st_next;
+ log(LOG_DEBUG, 0, "age_table_entry deleting (%s %s)",
+ inet_fmt(st->st_origin, s1),
+ inet_fmt(gt->gt_mcastgrp, s2));
+ 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 is running.
+ */
+ if (gt->gt_pruntbl != NULL || gt->gt_srctbl != NULL ||
+ gt->gt_prsent_timer > 0) {
+ gt->gt_timer = CACHE_LIFETIME(cache_lifetime);
+ if (gt->gt_prsent_timer == -1)
+ if (gt->gt_grpmems == 0)
+ send_prune(gt);
+ else
+ gt->gt_prsent_timer = 0;
+ gtnptr = &gt->gt_gnext;
+ continue;
+ }
+
+ log(LOG_DEBUG, 0, "timeout cache entry (%s, %s)",
+ inet_fmts(r->rt_origin, r->rt_originmask, 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 */
+ free((char *)gt);
+ } else {
+ if (gt->gt_prsent_timer == -1)
+ if (gt->gt_grpmems == 0)
+ send_prune(gt);
+ else
+ gt->gt_prsent_timer = 0;
+ 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 -= ROUTE_MAX_REPORT_DELAY;
+
+ if (gt->gt_timer < 0) {
+ if (gt->gt_srctbl) {
+ 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));
+ }
+ free(gt->gt_srctbl);
+ }
+ *gtnptr = gt->gt_next;
+ if (gt->gt_next)
+ gt->gt_next->gt_prev = gt->gt_prev;
+
+ 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.
+ */
+ if (gt->gt_prsent_timer > 0) {
+ log(LOG_DEBUG, 0, "prune expired with %d left on %s",
+ gt->gt_prsent_timer, "prsent_timer");
+ gt->gt_prsent_timer = 0;
+ }
+
+ /* 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);
+ log(LOG_DEBUG, 0, "forw again (%s %s) gm:%x vif:%d",
+ inet_fmts(rt->rt_origin, rt->rt_originmask, 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 */
+ }
+}
+
+
+static char *
+scaletime(t)
+ u_long t;
+{
+ static char buf1[5];
+ static char buf2[5];
+ static char *buf=buf1;
+ char s;
+ char *p;
+
+ p = buf;
+ if (buf == buf1)
+ buf = buf2;
+ else
+ buf = buf1;
+
+ if (t < 120) {
+ s = 's';
+ } else if (t < 3600) {
+ t /= 60;
+ s = 'm';
+ } else if (t < 86400) {
+ t /= 3600;
+ s = 'h';
+ } else if (t < 864000) {
+ t /= 86400;
+ s = 'd';
+ } else {
+ t /= 604800;
+ s = 'w';
+ }
+ if (t > 999)
+ return "*** ";
+
+ sprintf(p,"%3d%c", (int)t, s);
+
+ return p;
+}
+
+/*
+ * 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 IVif Forwvifs\n");
+
+ for (gt = kernel_no_route; gt; gt = gt->gt_next) {
+ if (gt->gt_srctbl) {
+ fprintf(fp2, " %-18s %-15s %-4s %-4s - -1\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",
+ inet_fmts(r->rt_origin, r->rt_originmask, s1),
+ inet_fmt(gt->gt_mcastgrp, s2));
+
+ fprintf(fp2, " %-4s", scaletime(gt->gt_timer));
+
+ fprintf(fp2, " %-4s %-4s ", scaletime(thyme - gt->gt_ctime),
+ gt->gt_prsent_timer ? scaletime(gt->gt_prsent_timer) :
+ " -");
+
+ fprintf(fp2, "%2u%c%c ", r->rt_parent,
+ gt->gt_prsent_timer ? 'P' : ' ',
+ 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) &&
+ !VIFM_ISSET(i, r->rt_leaves))
+ fprintf(fp2, " %u%c", i,
+ VIFM_ISSET(i, gt->gt_scope) ? 'b' : '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", c, inet_fmt(pt->pt_router, s1),
+ pt->pt_vifi, pt->pt_timer);
+ c = ',';
+ }
+ fprintf(fp2, ")\n");
+ }
+ for (st = gt->gt_srctbl; st; st = st->st_next) {
+ fprintf(fp2, ">%s\n", inet_fmt(st->st_origin, s1));
+ }
+ }
+}
+
+/*
+ * 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;
+ 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;
+ 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))) {
+ 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) {
+ log(LOG_DEBUG, 0, "packet with all reports filled in");
+ return;
+ }
+
+ 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 (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",
+ inet_fmts(rt->rt_origin, rt->rt_originmask, 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.
+ */
+ log(LOG_DEBUG, 0, "ignoring duplicate traceroute packet");
+ return;
+ }
+
+ if (rt == NULL) {
+ 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. */
+ 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)) {
+ 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) {
+ 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;
+
+ 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;
+ if (errcode != TR_NO_ERR) {
+ resp->tr_rflags = errcode;
+ rt = NULL; /* hack to enforce send straight to requestor */
+ goto sendit;
+ }
+ resp->tr_outaddr = uvifs[vifi].uv_lcl_addr;
+ resp->tr_fttl = uvifs[vifi].uv_threshold;
+ resp->tr_rflags = TR_NO_ERR;
+
+ /*
+ * obtain # of packets out on interface
+ */
+ v_req.vifi = vifi;
+ if (ioctl(udp_socket, SIOCGETVIFCNT, (char *)&v_req) >= 0)
+ resp->tr_vifout = htonl(v_req.ocount);
+
+ /*
+ * 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) {
+ sg_req.src.s_addr = qry->tr_src;
+ sg_req.grp.s_addr = group;
+ if (ioctl(udp_socket, SIOCGETSGCNT, (char *)&sg_req) >= 0)
+ resp->tr_pktcnt = htonl(sg_req.pktcnt);
+
+ 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 (VIFM_ISSET(vifi, rt->rt_children) &&
+ !VIFM_ISSET(vifi, rt->rt_leaves))
+ resp->tr_rflags = TR_OPRUNED;
+ else
+ resp->tr_rflags = TR_NO_FWD;
+ } else {
+ if (scoped_addr(vifi, 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);
+
+ 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)) {
+ 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.
+ */
+ 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) {
+ 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 {
+ 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..c007ed3
--- /dev/null
+++ b/usr.sbin/mrouted/prune.h
@@ -0,0 +1,143 @@
+/*
+ * The mrouted program is covered by the license in the accompanying file
+ * named "LICENSE". Use of the mrouted program represents acceptance of
+ * the terms and conditions listed in that file.
+ *
+ * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+ * Leland Stanford Junior University.
+ *
+ *
+ * $Id$
+ */
+
+/*
+ * 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 */
+ struct stable *gt_srctbl; /* source table */
+ struct ptable *gt_pruntbl; /* prune table */
+ struct rtentry *gt_route; /* parent route */
+#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 */
+};
+
+/*
+ * 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_timer; /* timer for prune */
+};
+
+/*
+ * 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 = htonl(~((1 << (32 - (i))) - 1)); \
+ };
+
+#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..9ed2bb4
--- /dev/null
+++ b/usr.sbin/mrouted/route.c
@@ -0,0 +1,1172 @@
+/*
+ * The mrouted program is covered by the license in the accompanying file
+ * named "LICENSE". Use of the mrouted program represents acceptance of
+ * the terms and conditions listed in that file.
+ *
+ * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+ * Leland Stanford Junior University.
+ *
+ *
+ * $Id$
+ */
+
+
+#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));
+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((struct rtentry *start_rt, vifi_t vifi,
+ u_int32 dst));
+
+/*
+ * 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 and leaf bits for route 'r', along with the
+ * associated dominant, subordinate, and leaf timing data structures.
+ * Return TRUE if this changes the value of either the children or
+ * leaf bitmaps for 'r'.
+ */
+static int
+init_children_and_leaves(r, parent)
+ register struct rtentry *r;
+ register vifi_t parent;
+{
+ register vifi_t vifi;
+ register struct uvif *v;
+ vifbitmap_t old_children, old_leaves;
+
+ VIFM_COPY(r->rt_children, old_children);
+ VIFM_COPY(r->rt_leaves, old_leaves );
+
+ VIFM_CLRALL(r->rt_children);
+ VIFM_CLRALL(r->rt_leaves);
+ r->rt_flags &= ~RTF_LEAF_TIMING;
+
+ for (vifi = 0, v = uvifs; vifi < numvifs; ++vifi, ++v) {
+ r->rt_dominants [vifi] = 0;
+ r->rt_subordinates[vifi] = 0;
+
+ if (vifi != parent && !(v->uv_flags & (VIFF_DOWN|VIFF_DISABLED))) {
+ VIFM_SET(vifi, r->rt_children);
+ if (v->uv_neighbors == NULL) {
+ VIFM_SET(vifi, r->rt_leaves);
+ r->rt_leaf_timers[vifi] = 0;
+ }
+ else {
+ r->rt_leaf_timers[vifi] = LEAF_CONFIRMATION_TIME;
+ r->rt_flags |= RTF_LEAF_TIMING;
+ }
+ }
+ else {
+ r->rt_leaf_timers[vifi] = 0;
+ }
+ }
+
+ return (!VIFM_SAME(r->rt_children, old_children) ||
+ !VIFM_SAME(r->rt_leaves, old_leaves));
+}
+
+
+/*
+ * A new vif has come up -- update the children and leaf 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;
+ r->rt_subordinates[vifi] = 0;
+ if (v->uv_neighbors == NULL) {
+ VIFM_SET(vifi, r->rt_leaves);
+ r->rt_leaf_timers[vifi] = 0;
+ }
+ else {
+ VIFM_CLR(vifi, r->rt_leaves);
+ r->rt_leaf_timers[vifi] = LEAF_CONFIRMATION_TIME;
+ r->rt_flags |= RTF_LEAF_TIMING;
+ }
+ update_table_entry(r);
+ }
+ }
+}
+
+
+/*
+ * 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);
+ VIFM_CLR(vifi, r->rt_leaves);
+ r->rt_subordinates[vifi] = 0;
+ r->rt_leaf_timers [vifi] = 0;
+ update_table_entry(r);
+ }
+ else {
+ r->rt_dominants[vifi] = 0;
+ }
+ }
+ }
+}
+
+
+/*
+ * A neighbor has failed or become unreachable. If that neighbor was
+ * considered a dominant or subordinate router in any route entries,
+ * take appropriate action.
+ */
+void
+delete_neighbor_from_routes(addr, vifi)
+ register u_int32 addr;
+ 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) {
+ if (r->rt_dominants[vifi] == addr) {
+ VIFM_SET(vifi, r->rt_children);
+ r->rt_dominants [vifi] = 0;
+ r->rt_subordinates[vifi] = 0;
+ if (v->uv_neighbors == NULL) {
+ VIFM_SET(vifi, r->rt_leaves);
+ r->rt_leaf_timers[vifi] = 0;
+ }
+ else {
+ VIFM_CLR(vifi, r->rt_leaves);
+ r->rt_leaf_timers[vifi] = LEAF_CONFIRMATION_TIME;
+ r->rt_flags |= RTF_LEAF_TIMING;
+ }
+ update_table_entry(r);
+ }
+ else if (r->rt_subordinates[vifi] == addr) {
+ r->rt_subordinates[vifi] = 0;
+ if (v->uv_neighbors == NULL) {
+ VIFM_SET(vifi, r->rt_leaves);
+ update_table_entry(r);
+ }
+ else {
+ r->rt_leaf_timers[vifi] = LEAF_CONFIRMATION_TIME;
+ r->rt_flags |= RTF_LEAF_TIMING;
+ }
+ }
+ else if (v->uv_neighbors == NULL &&
+ r->rt_leaf_timers[vifi] != 0) {
+ VIFM_SET(vifi, r->rt_leaves);
+ r->rt_leaf_timers[vifi] = 0;
+ update_table_entry(r);
+ }
+ }
+ }
+}
+
+
+/*
+ * 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) +
+ (2 * numvifs * sizeof(u_int32)) +
+ (numvifs * sizeof(u_int)))) == 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);
+ r->rt_subordinates = (u_int32 *)(r->rt_dominants + numvifs);
+ r->rt_leaf_timers = (u_int *)(r->rt_subordinates + numvifs);
+ r->rt_groups = NULL;
+
+ 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;
+ 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)
+ u_int32 origin, mask;
+ u_int metric;
+ u_int32 src;
+ vifi_t vifi;
+{
+ 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;
+ }
+
+ /*
+ * OK, create the new routing entry. 'rtp' will be left pointing
+ * to the new entry.
+ */
+ create_route(origin, mask);
+
+ /*
+ * 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.
+ */
+ steal_sources(rtp);
+
+ 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;
+
+ r->rt_parent = vifi;
+ init_children_and_leaves(r, vifi);
+
+ r->rt_gateway = src;
+ r->rt_timer = 0;
+ r->rt_metric = adj_metric;
+ r->rt_flags |= RTF_CHANGED;
+ routes_changed = TRUE;
+ update_table_entry(r);
+ }
+ 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 (adj_metric == r->rt_metric)
+ return;
+
+ if (adj_metric == UNREACHABLE) {
+ del_table_entry(r, 0, DEL_ALL_ROUTES);
+ r->rt_timer = ROUTE_EXPIRE_TIME;
+ }
+ else if (adj_metric < r->rt_metric) {
+ if (init_children_and_leaves(r, vifi)) {
+ update_table_entry(r);
+ }
+ }
+ 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?
+ */
+ if (r->rt_parent != vifi || adj_metric < r->rt_metric) {
+ /*
+ * XXX Why do we do this if we are just changing the metric?
+ */
+ r->rt_parent = vifi;
+ if (init_children_and_leaves(r, vifi)) {
+ update_table_entry(r);
+ }
+ }
+ r->rt_gateway = src;
+ 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 and leaf info, if necessary.
+ */
+ 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);
+ VIFM_CLR(vifi, r->rt_leaves);
+ r->rt_dominants [vifi] = src;
+ r->rt_subordinates[vifi] = 0;
+ r->rt_leaf_timers [vifi] = 0;
+ update_table_entry(r);
+ }
+ else if (metric > UNREACHABLE) { /* "poisoned reverse" */
+ /*
+ * Neighbor considers this vif to be on path to route's
+ * origin; if no subordinate recorded, record this neighbor
+ * as subordinate and clear the leaf flag.
+ */
+ if (r->rt_subordinates[vifi] == 0) {
+ VIFM_CLR(vifi, r->rt_leaves);
+ r->rt_subordinates[vifi] = src;
+ r->rt_leaf_timers [vifi] = 0;
+ update_table_entry(r);
+ }
+ }
+ else if (src == r->rt_subordinates[vifi]) {
+ /*
+ * Current subordinate no longer considers this vif to be on
+ * path to route's origin; it is no longer a subordinate
+ * router, and we set the leaf confirmation timer to give
+ * us time to hear from other subordinates.
+ */
+ r->rt_subordinates[vifi] = 0;
+ if (uvifs[vifi].uv_neighbors == NULL ||
+ uvifs[vifi].uv_neighbors->al_next == NULL) {
+ VIFM_SET(vifi, r->rt_leaves);
+ update_table_entry(r);
+ }
+ else {
+ r->rt_leaf_timers [vifi] = LEAF_CONFIRMATION_TIME;
+ r->rt_flags |= RTF_LEAF_TIMING;
+ }
+ }
+
+ }
+ 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.
+ */
+ VIFM_SET(vifi, r->rt_children);
+ r->rt_dominants [vifi] = 0;
+ if (metric > UNREACHABLE) {
+ r->rt_subordinates[vifi] = src;
+ }
+ else if (uvifs[vifi].uv_neighbors == NULL ||
+ uvifs[vifi].uv_neighbors->al_next == NULL) {
+ VIFM_SET(vifi, r->rt_leaves);
+ }
+ else {
+ r->rt_leaf_timers[vifi] = LEAF_CONFIRMATION_TIME;
+ r->rt_flags |= RTF_LEAF_TIMING;
+ }
+ update_table_entry(r);
+ }
+ }
+}
+
+
+/*
+ * On every timer interrupt, advance the timer in each routing entry.
+ */
+void
+age_routes()
+{
+ register struct rtentry *r;
+ register struct rtentry *prev_r;
+ register vifi_t vifi;
+
+ for (prev_r = RT_ADDR, r = routing_table;
+ r != NULL;
+ prev_r = r, r = r->rt_next) {
+
+ if ((r->rt_timer += TIMER_INTERVAL) < ROUTE_EXPIRE_TIME) {
+ /*
+ * Route is still good; see if any leaf timers need to be
+ * advanced.
+ */
+ if (r->rt_flags & RTF_LEAF_TIMING) {
+ r->rt_flags &= ~RTF_LEAF_TIMING;
+ for (vifi = 0; vifi < numvifs; ++vifi) {
+ if (r->rt_leaf_timers[vifi] != 0) {
+ /*
+ * Unlike other timers, leaf timers decrement.
+ */
+ if ((r->rt_leaf_timers[vifi] -= TIMER_INTERVAL) == 0){
+#ifdef NOTYET
+ /* If the vif is a physical leaf but has neighbors,
+ * it is not a tree leaf. If I am a leaf, then no
+ * interface with neighbors is a tree leaf. */
+ if (!(((uvifs[vifi].uv_flags & VIFF_LEAF) ||
+ (vifs_with_neighbors == 1)) &&
+ (uvifs[vifi].uv_neighbors != NULL))) {
+#endif
+ VIFM_SET(vifi, r->rt_leaves);
+ update_table_entry(r);
+#ifdef NOTYET
+ }
+#endif
+ }
+ else {
+ r->rt_flags |= RTF_LEAF_TIMING;
+ }
+ }
+ }
+ }
+ }
+ else if (r->rt_timer >= 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_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;
+ }
+ }
+ }
+}
+
+
+/*
+ * 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;
+
+ if ((vifi = find_vif(src, dst)) == NO_VIF) {
+ log(LOG_INFO, 0,
+ "ignoring probe from non-neighbor %s", inet_fmt(src, s1));
+ 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);
+}
+
+/*
+ * Process an incoming route report message.
+ */
+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];
+
+ 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 (!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;
+
+ 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;
+ }
+ update_route(rt[i].origin, rt[i].mask, rt[i].metric,
+ src, vifi);
+ }
+
+ 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 char *p;
+ register int i;
+ int datalen = 0;
+ int width = 0;
+ u_int32 mask = 0;
+ u_int32 src;
+ u_int32 nflags;
+ int metric;
+ int admetric = uvifs[vifi].uv_admetric;
+
+ src = uvifs[vifi].uv_lcl_addr;
+
+ p = send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN;
+
+#ifdef NOTYET
+ /* If I'm not a leaf, but the neighbor is a leaf, only advertise default */
+ if ((vifs_with_neighbors != 1) && (uvifs[vifi].uv_flags & VIFF_LEAF)) {
+ *p++ = 0; /* 0xff000000 mask */
+ *p++ = 0;
+ *p++ = 0;
+ *p++ = 0; /* class A net 0.0.0.0 == default */
+ *p++ = 0x81; /*XXX metric 1, is this safe? */
+ datalen += 5;
+ send_igmp(src, dst, IGMP_DVMRP, DVMRP_REPORT,
+ htonl(MROUTED_LEVEL), datalen);
+ return;
+ }
+#endif
+
+ nflags = (uvifs[vifi].uv_flags & VIFF_LEAF) ? 0 : LEAF_FLAGS;
+
+ for (r = rt_end; r != RT_ADDR; r = r->rt_prev) {
+
+ if (which_routes == CHANGED_ROUTES && !(r->rt_flags & RTF_CHANGED))
+ continue;
+
+ /*
+ * If there is no room for this route in the current message,
+ * send the message and start a new one.
+ */
+ if (datalen + ((r->rt_originmask == mask) ?
+ (width + 1) :
+ (r->rt_originwidth + 4)) > MAX_DVMRP_DATA_LEN) {
+ *(p-1) |= 0x80;
+ send_igmp(src, dst, IGMP_DVMRP, DVMRP_REPORT,
+ htonl(MROUTED_LEVEL | nflags), datalen);
+
+ p = send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN;
+ datalen = 0;
+ mask = 0;
+ }
+
+ 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;
+ *p++ = (r->rt_parent == vifi && metric != UNREACHABLE) ?
+ (char)(metric + UNREACHABLE) : /* "poisoned reverse" */
+ (char)(metric);
+
+ datalen += width + 1;
+ }
+
+ if (datalen != 0) {
+ *(p-1) |= 0x80;
+ send_igmp(src, dst, IGMP_DVMRP, DVMRP_REPORT,
+ htonl(MROUTED_LEVEL | nflags), datalen);
+ }
+}
+
+
+/*
+ * 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 (v->uv_neighbors != NULL) {
+ report(which_routes, vifi,
+ (v->uv_flags & VIFF_TUNNEL) ? v->uv_rmt_addr
+ : dvmrp_group);
+ }
+ }
+
+ /*
+ * 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(start_rt, vifi, dst)
+ 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;
+ int datalen = 0;
+ int width = 0;
+ u_int32 mask = 0;
+ u_int32 src;
+ u_int32 nflags;
+ int admetric = uvifs[vifi].uv_admetric;
+ int metric;
+
+ src = uvifs[vifi].uv_lcl_addr;
+ p = send_buf + MIN_IP_HEADER_LEN + IGMP_MINLEN;
+
+ nflags = (uvifs[vifi].uv_flags & VIFF_LEAF) ? 0 : LEAF_FLAGS;
+
+ for (r = start_rt; r != RT_ADDR; r = r->rt_prev) {
+
+#ifdef NOTYET
+ /* Don't send poisoned routes back to parents if I am a leaf */
+ if ((vifs_with_neighbors == 1) && (r->rt_parent == vifi)
+ && (r->rt_metric > 1)) {
+ ++nrt;
+ continue;
+ }
+#endif
+
+ /*
+ * 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_igmp(src, dst, IGMP_DVMRP, DVMRP_REPORT,
+ htonl(MROUTED_LEVEL | nflags), 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;
+ *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_igmp(src, dst, IGMP_DVMRP, DVMRP_REPORT,
+ htonl(MROUTED_LEVEL | nflags), 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 ((v->uv_neighbors != NULL)
+#ifdef NOTYET
+ && !(v->uv_flags & VIFF_LEAF)
+#endif
+ ) {
+ n = report_chunk(sr, vifi,
+ (v->uv_flags & VIFF_TUNNEL) ? v->uv_rmt_addr
+ : dvmrp_group);
+ if (n < min)
+ min = n;
+ }
+ }
+ if (min == 20000)
+ min = 0; /* Neighborless router didn't send any routes */
+
+ n = min;
+ 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 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 %3u ", r->rt_timer, r->rt_parent);
+
+ for (i = 0; i < numvifs; ++i) {
+ if (VIFM_ISSET(i, r->rt_children)) {
+ fprintf(fp, " %u%c",
+ i, VIFM_ISSET(i, r->rt_leaves) ? '*' : ' ');
+ }
+ }
+ 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))
+ break;
+ }
+ return rt;
+}
diff --git a/usr.sbin/mrouted/route.h b/usr.sbin/mrouted/route.h
new file mode 100644
index 0000000..5b34e8a
--- /dev/null
+++ b/usr.sbin/mrouted/route.h
@@ -0,0 +1,51 @@
+/*
+ * The mrouted program is covered by the license in the accompanying file
+ * named "LICENSE". Use of the mrouted program represents acceptance of
+ * the terms and conditions listed in that file.
+ *
+ * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+ * Leland Stanford Junior University.
+ *
+ *
+ * $Id$
+ */
+
+/*
+ * 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 */
+ vifbitmap_t rt_leaves; /* subset of outgoing children vifs */
+ u_int32 *rt_dominants; /* per vif dominant gateways */
+ u_int32 *rt_subordinates; /* per vif subordinate gateways */
+ u_int *rt_leaf_timers; /* per vif leaf confirmation timers */
+ 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_LEAF_TIMING 0x02 /* some leaf timers are running */
+
+#define ALL_ROUTES 0 /* possible arguments to report() */
+#define CHANGED_ROUTES 1 /* and report_to_all_neighbors() */
diff --git a/usr.sbin/mrouted/rsrr.c b/usr.sbin/mrouted/rsrr.c
new file mode 100644
index 0000000..b447f1a
--- /dev/null
+++ b/usr.sbin/mrouted/rsrr.c
@@ -0,0 +1,514 @@
+/*
+ * 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 */
+
+#ifdef RSRR
+
+#include "defs.h"
+#include <sys/param.h>
+#if (defined(BSD) && (BSD >= 199103))
+#include <stddef.h>
+#endif
+
+/* Taken from prune.c */
+/*
+ * checks for scoped multicast addresses
+ */
+#define GET_SCOPE(gt) { \
+ register int _i; \
+ if (((gt)->gt_mcastgrp & 0xff000000) == 0xef000000) \
+ for (_i = 0; _i < numvifs; _i++) \
+ if (scoped_addr(_i, (gt)->gt_mcastgrp)) \
+ VIFM_SET(_i, (gt)->gt_scope); \
+ }
+
+/*
+ * 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 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);
+#if (defined(BSD) && (BSD >= 199103))
+ 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 */
+void
+rsrr_read(f, rfd)
+ int f;
+ fd_set *rfd;
+{
+ register int rsrr_recvlen;
+#ifdef SYSV
+ sigset_t block, oblock;
+#else
+ register int omask;
+#endif
+
+ 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;
+ }
+#ifdef SYSV
+ (void)sigemptyset(&block);
+ (void)sigaddset(&block, SIGALRM);
+ if (sigprocmask(SIG_BLOCK, &block, &oblock) < 0)
+ log(LOG_ERR, errno, "sigprocmask");
+#else
+ /* Use of omask taken from main() */
+ omask = sigblock(sigmask(SIGALRM));
+#endif
+ rsrr_accept(rsrr_recvlen);
+#ifdef SYSV
+ (void)sigprocmask(SIG_SETMASK, &oblock, (sigset_t *)NULL);
+#else
+ (void)sigsetmask(omask);
+#endif
+}
+
+/* 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 */
+ log(LOG_INFO, 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);
+ log(LOG_INFO, 0,
+ "Received Route Query for src %s grp %s notification %d",
+ inet_fmt(route_query->source_addr.s_addr, s1),
+ inet_fmt(route_query->dest_addr.s_addr,s2),
+ BIT_TST(rsrr->flags,RSRR_NOTIFICATION_BIT));
+ /* Send Route Reply to client */
+ rsrr_accept_rq(route_query,rsrr->flags,NULL);
+ break;
+ default:
+ log(LOG_WARNING, 0,
+ "Received RSRR packet type %d, which I don't handle",
+ rsrr->type);
+ break;
+ }
+ break;
+
+ default:
+ log(LOG_WARNING, 0,
+ "Received RSRR packet version %d, which I don't understand",
+ rsrr->version);
+ break;
+ }
+}
+
+/* Send an Initial Reply to the reservation protocol. */
+static void
+rsrr_accept_iq()
+{
+ struct rsrr_header *rsrr;
+ struct rsrr_vif *vif_list;
+ struct uvif *v;
+ int vifi, sendlen;
+
+ /* Check for space. There should be room for plenty of vifs,
+ * but we should check anyway.
+ */
+ if (numvifs > RSRR_MAX_VIFS) {
+ log(LOG_WARNING, 0,
+ "Can't send RSRR Route Reply because %d is too many vifs %d",
+ numvifs);
+ return;
+ }
+
+ /* Set up message */
+ rsrr = (struct rsrr_header *) rsrr_send_buf;
+ rsrr->version = 1;
+ rsrr->type = RSRR_INITIAL_REPLY;
+ rsrr->flags = 0;
+ rsrr->num = numvifs;
+
+ vif_list = (struct rsrr_vif *) (rsrr_send_buf + RSRR_HEADER_LEN);
+
+ /* Include the vif list. */
+ for (vifi=0, v = uvifs; vifi < numvifs; vifi++, v++) {
+ vif_list[vifi].id = vifi;
+ vif_list[vifi].status = 0;
+ if (v->uv_flags & VIFF_DISABLED)
+ BIT_SET(vif_list[vifi].status,RSRR_DISABLED_BIT);
+ vif_list[vifi].threshold = v->uv_threshold;
+ vif_list[vifi].local_addr.s_addr = v->uv_lcl_addr;
+ }
+
+ /* Get the size. */
+ sendlen = RSRR_HEADER_LEN + numvifs*RSRR_VIF_LEN;
+
+ /* Send it. */
+ log(LOG_INFO, 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,i;
+ 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;
+ route_reply->out_vif_bm = gt_notify->gt_grpmems;
+
+ } 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 */
+ for (i = 0; i < numvifs; i++) {
+ if (VIFM_ISSET(i, r->rt_children) &&
+ !(VIFM_ISSET(i, r->rt_leaves)))
+ VIFM_SET(i, gt->gt_grpmems);
+
+ if (VIFM_ISSET(i, r->rt_leaves) && grplst_mem(i, mcastgrp))
+ VIFM_SET(i, gt->gt_grpmems);
+ }
+
+ GET_SCOPE(gt);
+ gt->gt_grpmems &= ~gt->gt_scope;
+
+ /* 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 (gt_notify)
+ log(LOG_INFO, 0, "Route Change: Send RSRR Route Reply");
+
+ else
+ log(LOG_INFO, 0, "Send RSRR Route Reply");
+
+ log(LOG_INFO, 0, "for src %s dst %s in vif %d out vif %d\n",
+ 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;
+ 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;
+ 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) {
+ 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;
+
+ printf("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..8bc8c91
--- /dev/null
+++ b/usr.sbin/mrouted/rsrr.h
@@ -0,0 +1,138 @@
+/*
+ * 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 "/tmp/.rsrr_svr"
+/* Note this needs to be 14 chars for 4.3 BSD compatibility */
+#define RSRR_CLI_PATH "/tmp/.rsrr_cli"
+
+#define RSRR_MAX_LEN 2048
+#define RSRR_HEADER_LEN (sizeof(struct rsrr_header))
+#define RSRR_RQ_LEN (RSRR_HEADER_LEN + sizeof(struct rsrr_rq))
+#define RSRR_RR_LEN (RSRR_HEADER_LEN + sizeof(struct rsrr_rr))
+#define RSRR_VIF_LEN (sizeof(struct rsrr_vif))
+
+/* Current maximum number of vifs. */
+#define RSRR_MAX_VIFS 32
+
+/* Maximum acceptable version */
+#define RSRR_MAX_VERSION 1
+
+/* RSRR message types */
+#define RSRR_ALL_TYPES 0
+#define RSRR_INITIAL_QUERY 1
+#define RSRR_INITIAL_REPLY 2
+#define RSRR_ROUTE_QUERY 3
+#define RSRR_ROUTE_REPLY 4
+
+/* RSRR Initial Reply (Vif) Status bits
+ * Each definition represents the position of the bit from right to left.
+ *
+ * Right-most bit is the disabled bit, set if the vif is administratively
+ * disabled.
+ */
+#define RSRR_DISABLED_BIT 0
+/* All other bits are zeroes */
+
+/* RSRR Route Query/Reply flag bits
+ * Each definition represents the position of the bit from right to left.
+ *
+ * Right-most bit is the Route Change Notification bit, set if the
+ * reservation protocol wishes to receive notification of
+ * a route change for the source-destination pair listed in the query.
+ * Notification is in the form of an unsolicitied Route Reply.
+ */
+#define RSRR_NOTIFICATION_BIT 0
+/* Next bit indicates an error returning the Route Reply. */
+#define RSRR_ERROR_BIT 1
+/* All other bits are zeroes */
+
+/* Definition of an RSRR message header.
+ * An Initial Query uses only the header, and an Initial Reply uses
+ * the header and a list of vifs.
+ */
+struct rsrr_header {
+ u_char version; /* RSRR Version, currently 1 */
+ u_char type; /* type of message, as defined above */
+ u_char flags; /* flags; defined by type */
+ u_char num; /* number; defined by type */
+};
+
+/* Definition of a vif as seen by the reservation protocol.
+ *
+ * Routing gives the reservation protocol a list of vifs in the
+ * Initial Reply.
+ *
+ * We explicitly list the ID because we can't assume that all routing
+ * protocols will use the same numbering scheme.
+ *
+ * The status is a bitmask of status flags, as defined above. It is the
+ * responsibility of the reservation protocol to perform any status checks
+ * if it uses the MULTICAST_VIF socket option.
+ *
+ * The threshold indicates the ttl an outgoing packet needs in order to
+ * be forwarded. The reservation protocol must perform this check itself if
+ * it uses the MULTICAST_VIF socket option.
+ *
+ * The local address is the address of the physical interface over which
+ * packets are sent.
+ */
+struct rsrr_vif {
+ u_char id; /* vif id */
+ u_char threshold; /* vif threshold ttl */
+ u_short status; /* vif status bitmask */
+ struct in_addr local_addr; /* vif local address */
+};
+
+/* Definition of an RSRR Route Query.
+ *
+ * The query asks routing for the forwarding entry for a particular
+ * source and destination. The query ID uniquely identifies the query
+ * for the reservation protocol. Thus, the combination of the client's
+ * address and the query ID forms a unique identifier for routing.
+ * Flags are defined above.
+ */
+struct rsrr_rq {
+ struct in_addr dest_addr; /* destination */
+ struct in_addr source_addr; /* source */
+ u_long query_id; /* query ID */
+};
+
+/* Definition of an RSRR Route Reply.
+ *
+ * Routing uses the reply to give the reservation protocol the
+ * forwarding entry for a source-destination pair. Routing copies the
+ * query ID from the query and fills in the incoming vif and a bitmask
+ * of the outgoing vifs.
+ * Flags are defined above.
+ */
+struct rsrr_rr {
+ struct in_addr dest_addr; /* destination */
+ struct in_addr source_addr; /* source */
+ u_long query_id; /* query ID */
+ u_short in_vif; /* incoming vif */
+ u_short reserved; /* reserved */
+ u_long out_vif_bm; /* outgoing vif bitmask */
+};
diff --git a/usr.sbin/mrouted/rsrr_var.h b/usr.sbin/mrouted/rsrr_var.h
new file mode 100644
index 0000000..9b1c09c
--- /dev/null
+++ b/usr.sbin/mrouted/rsrr_var.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 1993 by the University of Southern California
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation in source and binary forms for non-commercial purposes
+ * and without fee is hereby granted, provided that the above copyright
+ * notice appear in all copies and that both the copyright notice and
+ * this permission notice appear in supporting documentation. and that
+ * any documentation, advertising materials, and other materials related
+ * to such distribution and use acknowledge that the software was
+ * developed by the University of Southern California, Information
+ * Sciences Institute. The name of the University may not be used to
+ * endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THE UNIVERSITY OF SOUTHERN CALIFORNIA makes no representations about
+ * the suitability of this software for any purpose. THIS SOFTWARE IS
+ * PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Other copyrights might apply to parts of this software and are so
+ * noted when applicable.
+ */
+
+/* RSRR things that are only needed by mrouted. */
+
+/* Cache of Route Query messages, distinguished by source,
+ * destination, and client addresses. Cache is flushed by RSRR client
+ * -- it sends notification when an unwanted Route Reply is received.
+ * Since this only happens during route changes, it is more likely
+ * that the cache will be flushed when the kernel table entry is
+ * deleted. */
+struct rsrr_cache {
+ struct rsrr_rq route_query; /* Cached Route Query */
+ struct sockaddr_un client_addr; /* Client address */
+ int client_length; /* Length of client */
+ struct rsrr_cache *next; /* next cache item */
+};
+
diff --git a/usr.sbin/mrouted/testrsrr/Makefile b/usr.sbin/mrouted/testrsrr/Makefile
new file mode 100644
index 0000000..2f242bf
--- /dev/null
+++ b/usr.sbin/mrouted/testrsrr/Makefile
@@ -0,0 +1,14 @@
+# $Id$
+
+PROG= testrsrr
+
+S= ${.CURDIR}/..
+.PATH: $S
+CFLAGS+= -I$S
+
+SRCS= testrsrr.c
+NOMAN=
+
+install:
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/mrouted/testrsrr/testrsrr.c b/usr.sbin/mrouted/testrsrr/testrsrr.c
new file mode 100644
index 0000000..b99b593
--- /dev/null
+++ b/usr.sbin/mrouted/testrsrr/testrsrr.c
@@ -0,0 +1,124 @@
+/*
+ * Copyright 1995 Massachusetts Institute of Technology
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that both the above copyright notice and this
+ * permission notice appear in all copies, that both the above
+ * copyright notice and this permission notice appear in all
+ * supporting documentation, and that the name of M.I.T. not be used
+ * in advertising or publicity pertaining to distribution of the
+ * software without specific, written prior permission. M.I.T. makes
+ * no representations about the suitability of this software for any
+ * purpose. It is provided "as is" without express or implied
+ * warranty.
+ *
+ * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS
+ * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
+ * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id$
+ */
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <err.h>
+#include <sysexits.h>
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include "rsrr.h"
+
+char sunpath[MAXPATHLEN];
+int s;
+
+void exitfn(void) {
+ close(s);
+ unlink(sunpath);
+}
+
+int main(void) {
+ struct sockaddr_un sun;
+ char buf[RSRR_MAX_LEN];
+ struct rsrr_header *rh;
+ struct rsrr_vif *rvp;
+ int i;
+
+ s = socket(PF_LOCAL, SOCK_DGRAM, 0);
+ if (s < 0) {
+ err(EX_OSERR, "socket(PF_LOCAL, SOCK_DGRAM, 0)");
+ }
+
+ sun.sun_family = AF_LOCAL;
+ snprintf(sunpath, sizeof sun.sun_path, "/tmp/testrsrr.%lu",
+ (unsigned long)getpid());
+ strcpy(sun.sun_path, sunpath);
+ sun.sun_len = (offsetof(struct sockaddr_un, sun_path)
+ + strlen(sunpath));
+
+ if (bind(s, (struct sockaddr *)&sun, sun.sun_len) < 0) {
+ err(EX_OSERR, "bind: %s", sunpath);
+ }
+
+ atexit(exitfn); /* clean up if we exit on error */
+
+ strcpy(sun.sun_path, RSRR_SERV_PATH);
+ sun.sun_len = (offsetof(struct sockaddr_un, sun_path)
+ + strlen(sunpath));
+
+ if (connect(s, (struct sockaddr *)&sun, sun.sun_len) < 0) {
+ err(EX_OSERR, "connect: %s", RSRR_SERV_PATH);
+ }
+
+ rh = (struct rsrr_header *)buf;
+ rh->version = RSRR_MAX_VERSION;
+ rh->type = RSRR_INITIAL_QUERY;
+ rh->flags = 0;
+ rh->num = 0;
+
+ if (write(s, rh, sizeof *rh) == (ssize_t)-1) {
+ err(EX_OSERR, "write(initial query)");
+ }
+
+ if (read(s, buf, sizeof buf) == (ssize_t)-1) {
+ err(EX_OSERR, "read(initial reply)");
+ }
+
+ if (rh->version != RSRR_MAX_VERSION) {
+ errx(EX_PROTOCOL, "bad remote version %d", rh->version);
+ }
+
+ if (rh->type != RSRR_INITIAL_REPLY) {
+ errx(EX_PROTOCOL, "remote returned unexpected message type %d",
+ rh->type);
+ }
+
+ if (rh->flags) {
+ printf("confusing flags: %d\n", rh->flags);
+ }
+
+ printf("There are %d vifs configured:\n", rh->num);
+
+ printf(" Vif Thresh Status Local address\n");
+ for(i = 0, rvp = (struct rsrr_vif *)(rh + 1); i < rh->num; i++,rvp++) {
+ printf(" %3d %6d %6d %s\n", rvp->id, rvp->threshold,
+ rvp->status, inet_ntoa(rvp->local_addr));
+ }
+ exit(0);
+}
diff --git a/usr.sbin/mrouted/vif.c b/usr.sbin/mrouted/vif.c
new file mode 100644
index 0000000..9212585
--- /dev/null
+++ b/usr.sbin/mrouted/vif.c
@@ -0,0 +1,1441 @@
+/*
+ * The mrouted program is covered by the license in the accompanying file
+ * named "LICENSE". Use of the mrouted program represents acceptance of
+ * the terms and conditions listed in that file.
+ *
+ * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+ * Leland Stanford Junior University.
+ *
+ *
+ * $Id$
+ */
+
+
+#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 */
+
+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 int info_version __P((char *p));
+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 ((udp_socket = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
+ log(LOG_ERR, errno, "UDP socket");
+ 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);
+ }
+ }
+}
+
+/*
+ * 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 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;
+
+ 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_igmp(v->uv_lcl_addr,
+ (v->uv_flags & VIFF_TUNNEL) ? v->uv_rmt_addr
+ : dvmrp_group,
+ IGMP_DVMRP, DVMRP_PROBE,
+ htonl(MROUTED_LEVEL |
+ ((v->uv_flags & VIFF_LEAF) ? 0 : LEAF_FLAGS)),
+ datalen);
+}
+
+/*
+ * 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);
+ for (p = v->uv_addrs; p; p = p->pa_next) {
+ start_route_updates();
+ update_route(p->pa_subnet, p->pa_subnetmask, 0, 0, vifi);
+ }
+
+ /*
+ * Until neighbors are discovered, assume responsibility for sending
+ * periodic group membership queries to the subnet. Send the first
+ * query.
+ */
+ v->uv_flags |= VIFF_QUERIER;
+ send_igmp(src, allhosts_group, IGMP_HOST_MEMBERSHIP_QUERY,
+ (v->uv_flags & VIFF_IGMPV1) ? 0 :
+ IGMP_MAX_HOST_REPORT_DELAY * IGMP_TIMER_SCALE, 0, 0);
+ age_old_hosts();
+ }
+
+ 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);
+ for (p = v->uv_addrs; p; p = p->pa_next) {
+ start_route_updates();
+ update_route(p->pa_subnet, p->pa_subnetmask, UNREACHABLE, 0, vifi);
+ }
+
+ /*
+ * 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);
+ }
+
+ 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 (v->uv_neighbors)
+ vifs_with_neighbors--;
+
+ while (v->uv_neighbors != NULL) {
+ a = v->uv_neighbors;
+ v->uv_neighbors = a->al_next;
+ free((char *)a);
+ }
+}
+
+
+/*
+ * 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;
+ 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)
+ 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 to all subnets for which I am querier.
+ */
+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_igmp(v->uv_lcl_addr, allhosts_group,
+ IGMP_HOST_MEMBERSHIP_QUERY,
+ (v->uv_flags & VIFF_IGMPV1) ? 0 :
+ IGMP_MAX_HOST_REPORT_DELAY * IGMP_TIMER_SCALE, 0, 0);
+ }
+ }
+ age_old_hosts();
+}
+
+/*
+ * Process an incoming host membership query
+ */
+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 we consider ourselves the querier for this vif, but hear a
+ * query from a router with a lower IP address, yield to them.
+ *
+ * This is done here as well as in the neighbor discovery in case
+ * there is a querier that doesn't speak DVMRP.
+ *
+ * XXX If this neighbor doesn't speak DVMRP, then we need to create
+ * some neighbor state for him so that we can time him out!
+ */
+ if ((v->uv_flags & VIFF_QUERIER) &&
+ (ntohl(src) < ntohl(v->uv_lcl_addr))) {
+ v->uv_flags &= ~VIFF_QUERIER;
+
+ }
+}
+
+/*
+ * 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_HOST_MEMBERSHIP_REPORT)
+ g->al_old = OLD_AGE_THRESHOLD;
+#ifdef SNMP
+ g->al_genid = src;
+#endif /* SNMP */
+
+ /** delete old timers, set a timer for expiration **/
+ g->al_timer = GROUP_EXPIRE_TIME;
+ 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_HOST_NEW_MEMBERSHIP_REPORT)
+ g->al_old = 0;
+ else
+ g->al_old = OLD_AGE_THRESHOLD;
+#ifdef SNMP
+ g->al_genid = src;
+#endif
+
+ /** set a timer for expiration **/
+ g->al_query = 0;
+ g->al_timer = GROUP_EXPIRE_TIME;
+ time(&g->al_ctime);
+ g->al_timerid = SetTimer(vifi, g);
+ g->al_next = v->uv_groups;
+ v->uv_groups = g;
+
+ update_lclgrp(vifi, group);
+ }
+
+ /*
+ * Check if a graft is necessary for this group
+ */
+ chkgrp_graft(vifi, group);
+}
+
+
+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) {
+ 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);
+
+ /** send a group specific querry **/
+ g->al_timer = LEAVE_EXPIRE_TIME;
+ send_igmp(v->uv_lcl_addr, g->al_addr,
+ IGMP_HOST_MEMBERSHIP_QUERY,
+ LEAVE_EXPIRE_TIME / 3 * IGMP_TIMER_SCALE,
+ g->al_addr, 0);
+ g->al_query = SetQueryTimer(g, vifi, g->al_timer / 3,
+ LEAVE_EXPIRE_TIME / 3 * 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) {
+ *(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;
+ }
+ *(u_int*)p = la->al_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);
+ 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)
+ char *p;
+{
+ int len;
+ extern char versionstring[];
+
+ *p++ = DVMRP_INFO_VERSION;
+ p++; /* skip over length */
+ *p++ = 0; /* zero out */
+ *p++ = 0; /* reserved fields */
+ strcpy(p, versionstring); /* XXX strncpy!!! */
+
+ 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;
+{
+ log(LOG_INFO, 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;
+{
+ log(LOG_INFO, 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 TRUE if 'addr' is a valid neighbor, FALSE otherwise.
+ */
+int
+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;
+ u_int32 genid = 0;
+ u_int32 router;
+ u_int32 send_tables = 0;
+ int do_reset = FALSE;
+ int nflags;
+
+ v = &uvifs[vifi];
+ nflags = (level >> 16) & 0xff;
+
+ /*
+ * 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 'the unknown host' or self: %s",
+ inet_fmt(addr, s1));
+ return (FALSE);
+ }
+
+ /*
+ * Look for addr in list of neighbors.
+ */
+ for (n = v->uv_neighbors; n != NULL; n = n->al_next) {
+ if (addr == n->al_addr) {
+ break;
+ }
+ }
+
+ /*
+ * Found it. Reset its timer, and check for a version change
+ */
+ if (n) {
+ n->al_timer = 0;
+
+ /*
+ * update the neighbors version and protocol number
+ * if changed => router went down and came up,
+ * so take action immediately.
+ */
+ if ((n->al_pv != (level & 0xff)) ||
+ (n->al_mv != ((level >> 8) & 0xff))) {
+
+ do_reset = TRUE;
+ log(LOG_DEBUG, 0,
+ "version change neighbor %s [old:%d.%d, new:%d.%d]",
+ inet_fmt(addr, s1),
+ n->al_pv, n->al_mv, level&0xff, (level >> 8) & 0xff);
+
+ n->al_pv = level & 0xff;
+ n->al_mv = (level >> 8) & 0xff;
+ }
+ } else {
+ /*
+ * If not found, add it to the list. If the neighbor has a lower
+ * IP address than me, yield querier duties to it.
+ */
+ log(LOG_DEBUG, 0, "New neighbor %s on vif %d v%d.%d nf 0x%02x",
+ inet_fmt(addr, s1), vifi, level & 0xff, (level >> 8) & 0xff,
+ (level >> 16) & 0xff);
+
+ 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 = level & 0xff;
+ n->al_mv = (level >> 8) & 0xff;
+ n->al_genid = 0;
+
+ time(&n->al_ctime);
+ n->al_timer = 0;
+ n->al_next = v->uv_neighbors;
+
+ /*
+ * 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 (v->uv_neighbors == NULL) {
+ send_tables = (v->uv_flags & VIFF_TUNNEL) ? addr : dvmrp_group;
+ vifs_with_neighbors++;
+ } else {
+ send_tables = addr;
+ }
+
+ v->uv_neighbors = n;
+
+ if (!(v->uv_flags & VIFF_TUNNEL) &&
+ ntohl(addr) < ntohl(v->uv_lcl_addr))
+ v->uv_flags &= ~VIFF_QUERIER;
+ }
+
+ /*
+ * Check if the router gen-ids are the same.
+ * Need to reset the prune state of the router if not.
+ * Also check for one-way interfaces by seeing if we are in our
+ * neighbor's list of known routers.
+ */
+ if (msgtype == DVMRP_PROBE) {
+
+ /* Check genid neighbor flag. Also check version number; 3.3 and
+ * 3.4 didn't set this flag. */
+ if ((((level >> 16) & 0xff) & NF_GENID) ||
+ (((level & 0xff) == 3) && (((level >> 8) & 0xff) > 2))) {
+
+ int i;
+
+ if (datalen < 4) {
+ log(LOG_WARNING, 0,
+ "received truncated probe message from %s (len %d)",
+ inet_fmt(addr, s1), datalen);
+ return (FALSE);
+ }
+
+ for (i = 0; i < 4; i++)
+ ((char *)&genid)[i] = *p++;
+ datalen -= 4;
+
+ if (n->al_genid == 0)
+ n->al_genid = genid;
+ else if (n->al_genid != genid) {
+ log(LOG_DEBUG, 0,
+ "new genid neigbor %s on vif %d [old:%x, new:%x]",
+ inet_fmt(addr, s1), vifi, n->al_genid, genid);
+
+ n->al_genid = genid;
+ do_reset = TRUE;
+ }
+
+ /*
+ * loop through router list and check for one-way ifs.
+ */
+
+ v->uv_flags |= VIFF_ONEWAY;
+
+ while (datalen > 0) {
+ if (datalen < 4) {
+ log(LOG_WARNING, 0,
+ "received truncated probe message from %s (len %d)",
+ inet_fmt(addr, s1), datalen);
+ return (FALSE);
+ }
+ for (i = 0; i < 4; i++)
+ ((char *)&router)[i] = *p++;
+ datalen -= 4;
+ if (router == v->uv_lcl_addr) {
+ v->uv_flags &= ~VIFF_ONEWAY;
+ break;
+ }
+ }
+ }
+ }
+ if (n->al_flags != nflags) {
+ n->al_flags = nflags;
+
+ if (n->al_flags & NF_LEAF) {
+ /*XXX If we have non-leaf neighbors then we know we shouldn't
+ * mark this vif as a leaf. For now we just count on other
+ * probes and/or reports resetting the timer. */
+ if (!v->uv_leaf_timer)
+ v->uv_leaf_timer = LEAF_CONFIRMATION_TIME;
+ } else {
+ /* If we get a leaf to non-leaf transition, we *must* update
+ * the routing table. */
+ if (v->uv_flags & VIFF_LEAF && send_tables == 0)
+ send_tables = addr;
+ v->uv_flags &= ~VIFF_LEAF;
+ v->uv_leaf_timer = 0;
+ }
+ }
+ if (do_reset) {
+ reset_neighbor_state(vifi, addr);
+ if (!send_tables)
+ send_tables = addr;
+ }
+ if (send_tables)
+ report(ALL_ROUTES, vifi, send_tables);
+
+ return (TRUE);
+}
+
+
+/*
+ * 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, *n;
+ 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) {
+
+ if ((a->al_timer += TIMER_INTERVAL) < NEIGHBOR_EXPIRE_TIME)
+ continue;
+
+ /*
+ * Neighbor has expired; delete it from the neighbor list,
+ * delete it from the 'dominants' and 'subordinates arrays of
+ * any route entries and assume querier duties unless there is
+ * another neighbor with a lower IP address than mine.
+ */
+ addr = a->al_addr;
+ prev_a->al_next = a->al_next;
+ free((char *)a);
+ a = prev_a;
+
+ delete_neighbor_from_routes(addr, vifi);
+
+ if (v->uv_neighbors == NULL)
+ vifs_with_neighbors--;
+
+ v->uv_leaf_timer = LEAF_CONFIRMATION_TIME;
+
+ if (!(v->uv_flags & VIFF_TUNNEL)) {
+ v->uv_flags |= VIFF_QUERIER;
+ for (n = v->uv_neighbors; n != NULL; n = n->al_next) {
+ if (ntohl(n->al_addr) < ntohl(v->uv_lcl_addr)) {
+ v->uv_flags &= ~VIFF_QUERIER;
+ }
+ if (!(n->al_flags & NF_LEAF)) {
+ v->uv_leaf_timer = 0;
+ }
+ }
+ }
+ }
+ }
+}
+
+/*
+ * 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;
+}
+
+/*
+ * 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;
+ struct sioc_vif_req v_req;
+
+ 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);
+
+ if (v->uv_flags & VIFF_ONEWAY) fprintf(fp, " one-way");
+ if (v->uv_flags & VIFF_DOWN) fprintf(fp, " down");
+ if (v->uv_flags & VIFF_DISABLED) fprintf(fp, " disabled");
+ if (v->uv_flags & VIFF_QUERIER) fprintf(fp, " querier");
+ if (v->uv_flags & VIFF_SRCRT) fprintf(fp, " src-rt");
+ if (v->uv_flags & VIFF_LEAF) fprintf(fp, " leaf");
+ if (v->uv_flags & VIFF_IGMPV1) fprintf(fp, " IGMPv1");
+ fprintf(fp, "\n");
+
+ if (v->uv_addrs != NULL) {
+ fprintf(fp, " alternate subnets: %s\n",
+ inet_fmts(v->uv_addrs->pa_subnet, v->uv_addrs->pa_subnetmask, s1));
+ for (p = v->uv_addrs->pa_next; p; p = p->pa_next) {
+ fprintf(fp, " %s\n",
+ inet_fmts(p->pa_subnet, p->pa_subnetmask, s1));
+ }
+ }
+
+ if (v->uv_neighbors != NULL) {
+ fprintf(fp, " peers: %s (%d.%d) (0x%x)\n",
+ inet_fmt(v->uv_neighbors->al_addr, s1),
+ v->uv_neighbors->al_pv, v->uv_neighbors->al_mv,
+ v->uv_neighbors->al_flags);
+ for (a = v->uv_neighbors->al_next; a != NULL; a = a->al_next) {
+ fprintf(fp, " %s (%d.%d) (0x%x)\n",
+ inet_fmt(a->al_addr, s1), a->al_pv, a->al_mv,
+ a->al_flags);
+ }
+ }
+
+ if (v->uv_groups != NULL) {
+ fprintf(fp, " groups: %-15s\n",
+ inet_fmt(v->uv_groups->al_addr, s1));
+ for (a = v->uv_groups->al_next; a != NULL; a = a->al_next) {
+ fprintf(fp, " %-15s\n",
+ inet_fmt(a->al_addr, s1));
+ }
+ }
+ if (v->uv_acl != NULL) {
+ struct vif_acl *acl;
+
+ fprintf(fp, " boundaries: %-18s\n",
+ inet_fmts(v->uv_acl->acl_addr, v->uv_acl->acl_mask, s1));
+ for (acl = v->uv_acl->acl_next; acl != NULL; acl = acl->acl_next) {
+ fprintf(fp, " : %-18s\n",
+ inet_fmts(acl->acl_addr, acl->acl_mask, s1));
+ }
+ }
+ v_req.vifi = vifi;
+ if (ioctl(udp_socket, SIOCGETVIFCNT, (char *)&v_req) < 0) {
+ log(LOG_WARNING, 0,
+ "SIOCGETVIFCNT fails");
+ }
+ else {
+ fprintf(fp, " pkts in : %ld\n",
+ v_req.icount);
+ fprintf(fp, " pkts out: %ld\n",
+ v_req.ocount);
+ }
+ 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, (cfunc_t)DelVif, (void *)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_HOST_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, (cfunc_t)SendQuery, (void *)cbk);
+}
diff --git a/usr.sbin/mrouted/vif.h b/usr.sbin/mrouted/vif.h
new file mode 100644
index 0000000..e0ed7ec
--- /dev/null
+++ b/usr.sbin/mrouted/vif.h
@@ -0,0 +1,79 @@
+/*
+ * The mrouted program is covered by the license in the accompanying file
+ * named "LICENSE". Use of the mrouted program represents acceptance of
+ * the terms and conditions listed in that file.
+ *
+ * The mrouted program is COPYRIGHT 1989 by The Board of Trustees of
+ * Leland Stanford Junior University.
+ *
+ *
+ * $Id$
+ */
+
+/*
+ * 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_short uv_flags; /* VIFF_ flags defined below */
+ u_char uv_metric; /* cost of this vif */
+ u_char uv_admetric; /* advertised cost of this vif */
+ u_int uv_rate_limit; /* rate limit on this vif */
+ u_char uv_threshold; /* min ttl required to forward on 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_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 */
+ 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 */
+};
+
+#define VIFF_KERNEL_FLAGS (VIFF_TUNNEL|VIFF_SRCRT)
+#define VIFF_DOWN 0x0100 /* kernel state of interface */
+#define VIFF_DISABLED 0x0200 /* administratively disabled */
+#define VIFF_QUERIER 0x0400 /* I am the subnet's querier */
+#define VIFF_ONEWAY 0x0800 /* Maybe one way interface */
+#define VIFF_LEAF 0x1000 /* all neighbors are leaves */
+#define VIFF_IGMPV1 0x2000 /* Act as an IGMPv1 Router */
+
+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 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; /* neighbor creation time */
+ u_int32 al_genid; /* generation id for neighbor */
+ u_char al_pv; /* router protocol version */
+ u_char al_mv; /* router mrouted version */
+ u_long al_timerid; /* returned by set timer */
+ u_long al_query; /* second query in case of leave */
+ u_short al_old; /* time since heard old report */
+ u_char al_flags; /* flags related to this neighbor */
+};
+
+#define NF_LEAF 0x01 /* This neighbor is a leaf */
+#define NF_PRUNE 0x02 /* This neighbor understands prunes */
+#define NF_GENID 0x04 /* I supply genid & rtrlist in probe*/
+#define NF_MTRACE 0x08 /* I can understand mtrace requests */
+
+#define NO_VIF ((vifi_t)MAXVIFS) /* An invalid vif index */
diff --git a/usr.sbin/mtest/Makefile b/usr.sbin/mtest/Makefile
new file mode 100644
index 0000000..75e2cf6
--- /dev/null
+++ b/usr.sbin/mtest/Makefile
@@ -0,0 +1,4 @@
+PROG= mtest
+MAN8= mtest.8
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/mtest/mtest.8 b/usr.sbin/mtest/mtest.8
new file mode 100644
index 0000000..bdb4aec
--- /dev/null
+++ b/usr.sbin/mtest/mtest.8
@@ -0,0 +1,52 @@
+.\" The following requests are required for all man pages.
+.Dd December 15, 1996
+.Os
+.Dt MTEST 8
+.Sh NAME
+.Nm mtest
+.Nd test multicast membership socket operations and ioctls
+.Sh SYNOPSIS
+.Nm
+.Sh DESCRIPTION
+.Nm
+is a small program for testing the multicast membership socket operations
+and ioctls. It accepts the following commands, interactively:
+.Bl -tag -width "a ifname e.e.e.e.e.e" -compact -offset indent
+.It Ic j Ar g.g.g.g Ar i.i.i.i
+Join the IP group address
+.Ar g.g.g.g
+on the interface with address
+.Ar i.i.i.i .
+.Ar i.i.i.i
+may be specified as 0.0.0.0 to use the default interface.
+.It Ic l Ar g.g.g.g Ar i.i.i.i
+Leave the IP group address
+.Ar g.g.g.g
+on the interface with address
+.Ar i.i.i.i .
+.It Ic a Ar ifname Ar e.e.e.e.e.e
+Join the Ethernet group address
+.Ar e.e.e.e.e.e
+on interface
+.Ar ifname .
+.It Ic d Ar ifname Ar e.e.e.e.e.e
+Leave the Ethernet group address
+.Ar e.e.e.e.e.e
+on interface
+.Ar ifname .
+.It Ic m Ar ifname Ar 1/0
+Set or reset ALLMULTI mode on interface
+.Ar ifname .
+.It Ic p Ar ifname Ar 1/0
+Set or reset promiscuous mode on interface
+.Ar ifname .
+.It Ic ?
+List legal commands.
+.It Ic q
+Quit the program.
+.El
+.\" .Sh SEE ALSO
+.Sh AUTHORS
+.An Steve Deering
+.Sh BUGS
+The command parser is not very flexible.
diff --git a/usr.sbin/mtest/mtest.c b/usr.sbin/mtest/mtest.c
new file mode 100644
index 0000000..a43dd2b
--- /dev/null
+++ b/usr.sbin/mtest/mtest.c
@@ -0,0 +1,205 @@
+/*
+ * Program to test new [sg]etsockopts and ioctls for manipulating IP and
+ * Ethernet multicast address filters.
+ *
+ * Written by Steve Deering, Stanford University, February 1989.
+ */
+
+#include <err.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <net/if.h>
+#include <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':
+ {
+ ++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;
+ }
+ ifr.ifr_addr.sa_family = AF_UNSPEC;
+ ifr.ifr_addr.sa_data[0] = e1;
+ ifr.ifr_addr.sa_data[1] = e2;
+ ifr.ifr_addr.sa_data[2] = e3;
+ ifr.ifr_addr.sa_data[3] = e4;
+ ifr.ifr_addr.sa_data[4] = e5;
+ ifr.ifr_addr.sa_data[5] = e6;
+ if( ioctl( so, SIOCADDMULTI, &ifr ) == -1 )
+ warn( "can't add ether address" );
+ else printf( "ether address added\n" );
+ break;
+ }
+
+ case 'd':
+ {
+ ++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;
+ }
+ ifr.ifr_addr.sa_family = AF_UNSPEC;
+ ifr.ifr_addr.sa_data[0] = e1;
+ ifr.ifr_addr.sa_data[1] = e2;
+ ifr.ifr_addr.sa_data[2] = e3;
+ ifr.ifr_addr.sa_data[3] = e4;
+ ifr.ifr_addr.sa_data[4] = e5;
+ ifr.ifr_addr.sa_data[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..9f78f8f
--- /dev/null
+++ b/usr.sbin/mtree/Makefile
@@ -0,0 +1,12 @@
+# From: @(#)Makefile 8.1 (Berkeley) 6/6/93
+# $Id$
+
+PROG= mtree
+SRCS= compare.c crc.c create.c misc.c mtree.c spec.c verify.c
+MAN8= mtree.8
+.PATH: ${.CURDIR}/../../usr.bin/cksum
+
+DPADD+= ${LIBMD}
+LDADD+= -lmd
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/mtree/compare.c b/usr.sbin/mtree/compare.c
new file mode 100644
index 0000000..72b00b9
--- /dev/null
+++ b/usr.sbin/mtree/compare.c
@@ -0,0 +1,295 @@
+/*-
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)compare.c 8.1 (Berkeley) 6/6/93";
+#endif
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <fts.h>
+#include <md5.h>
+#include <stdio.h>
+#include <time.h>
+#include <unistd.h>
+#include "mtree.h"
+#include "extern.h"
+
+extern int uflag;
+extern int lineno;
+
+static char *ftype __P((u_int));
+
+#define INDENTNAMELEN 8
+#define LABEL \
+ if (!label++) { \
+ len = printf("%s: ", RP(p)); \
+ if (len > INDENTNAMELEN) { \
+ tab = "\t"; \
+ (void)printf("\n"); \
+ } else { \
+ tab = ""; \
+ (void)printf("%*s", INDENTNAMELEN - (int)len, ""); \
+ } \
+ }
+
+int
+compare(name, s, p)
+ char *name;
+ register NODE *s;
+ register FTSENT *p;
+{
+ extern int uflag;
+ u_long len, val;
+ int fd, label;
+ char *cp, *tab = "";
+
+ label = 0;
+ switch(s->type) {
+ case F_BLOCK:
+ if (!S_ISBLK(p->fts_statp->st_mode))
+ goto typeerr;
+ break;
+ case F_CHAR:
+ if (!S_ISCHR(p->fts_statp->st_mode))
+ goto typeerr;
+ break;
+ case F_DIR:
+ if (!S_ISDIR(p->fts_statp->st_mode))
+ goto typeerr;
+ break;
+ case F_FIFO:
+ if (!S_ISFIFO(p->fts_statp->st_mode))
+ goto typeerr;
+ break;
+ case F_FILE:
+ if (!S_ISREG(p->fts_statp->st_mode))
+ goto typeerr;
+ break;
+ case F_LINK:
+ if (!S_ISLNK(p->fts_statp->st_mode))
+ goto typeerr;
+ break;
+ case F_SOCK:
+ if (!S_ISSOCK(p->fts_statp->st_mode)) {
+typeerr: LABEL;
+ (void)printf("\ttype (%s, %s)\n",
+ ftype(s->type), inotype(p->fts_statp->st_mode));
+ }
+ break;
+ }
+ /* Set the uid/gid first, then set the mode. */
+ if (s->flags & (F_UID | F_UNAME) && s->st_uid != p->fts_statp->st_uid) {
+ LABEL;
+ (void)printf("%suser (%lu, %lu",
+ tab, s->st_uid, p->fts_statp->st_uid);
+ if (uflag)
+ if (chown(p->fts_accpath, s->st_uid, -1))
+ (void)printf(", not modified: %s)\n",
+ strerror(errno));
+ else
+ (void)printf(", modified)\n");
+ else
+ (void)printf(")\n");
+ tab = "\t";
+ }
+ if (s->flags & (F_GID | F_GNAME) && s->st_gid != p->fts_statp->st_gid) {
+ LABEL;
+ (void)printf("%sgid (%lu, %lu",
+ tab, s->st_gid, p->fts_statp->st_gid);
+ if (uflag)
+ if (chown(p->fts_accpath, -1, s->st_gid))
+ (void)printf(", not modified: %s)\n",
+ strerror(errno));
+ else
+ (void)printf(", modified)\n");
+ else
+ (void)printf(")\n");
+ tab = "\t";
+ }
+ if (s->flags & F_MODE &&
+ s->st_mode != (p->fts_statp->st_mode & MBITS)) {
+ LABEL;
+ (void)printf("%spermissions (%#o, %#o",
+ tab, s->st_mode, p->fts_statp->st_mode & MBITS);
+ if (uflag)
+ if (chmod(p->fts_accpath, s->st_mode))
+ (void)printf(", not modified: %s)\n",
+ strerror(errno));
+ else
+ (void)printf(", modified)\n");
+ else
+ (void)printf(")\n");
+ tab = "\t";
+ }
+ if (s->flags & F_NLINK && s->type != F_DIR &&
+ s->st_nlink != p->fts_statp->st_nlink) {
+ LABEL;
+ (void)printf("%slink count (%u, %u)\n",
+ tab, s->st_nlink, p->fts_statp->st_nlink);
+ tab = "\t";
+ }
+ if (s->flags & F_SIZE && s->st_size != p->fts_statp->st_size) {
+ LABEL;
+ (void)printf("%ssize (%qd, %qd)\n",
+ tab, s->st_size, p->fts_statp->st_size);
+ tab = "\t";
+ }
+ /*
+ * XXX
+ * Catches nano-second differences, but doesn't display them.
+ */
+ if ((s->flags & F_TIME) &&
+ ((s->st_mtimespec.tv_sec != p->fts_statp->st_mtimespec.tv_sec) ||
+ (s->st_mtimespec.tv_nsec != p->fts_statp->st_mtimespec.tv_nsec))) {
+ LABEL;
+ (void)printf("%smodification time (%.24s, ",
+ tab, ctime(&s->st_mtimespec.tv_sec));
+ (void)printf("%.24s)\n",
+ ctime(&p->fts_statp->st_mtimespec.tv_sec));
+ tab = "\t";
+ }
+ if (s->flags & F_CKSUM)
+ if ((fd = open(p->fts_accpath, O_RDONLY, 0)) < 0) {
+ LABEL;
+ (void)printf("%scksum: %s: %s\n",
+ tab, p->fts_accpath, strerror(errno));
+ tab = "\t";
+ } else if (crc(fd, &val, &len)) {
+ (void)close(fd);
+ LABEL;
+ (void)printf("%scksum: %s: %s\n",
+ tab, p->fts_accpath, strerror(errno));
+ tab = "\t";
+ } else {
+ (void)close(fd);
+ if (s->cksum != val) {
+ LABEL;
+ (void)printf("%scksum (%lu, %lu)\n",
+ tab, s->cksum, val);
+ }
+ tab = "\t";
+ }
+ if (s->flags & F_MD5) {
+ char *new_digest, buf[33];
+
+ new_digest = MD5File(p->fts_accpath,buf);
+ if (!new_digest) {
+ LABEL;
+ printf("%sMD5File: %s: %s\n", tab, p->fts_accpath,
+ strerror(errno));
+ tab = "\t";
+ } else if (strcmp(new_digest, s->md5digest)) {
+ LABEL;
+ printf("%sMD5 (%s, %s)\n", tab, s->md5digest,
+ new_digest);
+ tab = "\t";
+ }
+ }
+
+ if (s->flags & F_SLINK && strcmp(cp = rlink(name), s->slink)) {
+ LABEL;
+ (void)printf("%slink ref (%s, %s)\n", tab, cp, s->slink);
+ }
+ return (label);
+}
+
+char *
+inotype(type)
+ u_int type;
+{
+ switch(type & S_IFMT) {
+ case S_IFBLK:
+ return ("block");
+ case S_IFCHR:
+ return ("char");
+ case S_IFDIR:
+ return ("dir");
+ case S_IFIFO:
+ return ("fifo");
+ case S_IFREG:
+ return ("file");
+ case S_IFLNK:
+ return ("link");
+ case S_IFSOCK:
+ return ("socket");
+ default:
+ return ("unknown");
+ }
+ /* NOTREACHED */
+}
+
+static char *
+ftype(type)
+ u_int type;
+{
+ switch(type) {
+ case F_BLOCK:
+ return ("block");
+ case F_CHAR:
+ return ("char");
+ case F_DIR:
+ return ("dir");
+ case F_FIFO:
+ return ("fifo");
+ case F_FILE:
+ return ("file");
+ case F_LINK:
+ return ("link");
+ case F_SOCK:
+ return ("socket");
+ default:
+ return ("unknown");
+ }
+ /* NOTREACHED */
+}
+
+char *
+rlink(name)
+ char *name;
+{
+ static char lbuf[MAXPATHLEN];
+ register int len;
+
+ if ((len = readlink(name, lbuf, sizeof(lbuf))) == -1)
+ 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..2638e2a
--- /dev/null
+++ b/usr.sbin/mtree/create.c
@@ -0,0 +1,354 @@
+/*-
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)create.c 8.1 (Berkeley) 6/6/93";
+#endif
+static const char rcsid[] =
+ "$Id$";
+#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>
+#include <md5.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <time.h>
+#include <unistd.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_short keys;
+extern char fullpath[MAXPATHLEN];
+extern int lineno;
+
+static gid_t gid;
+static uid_t uid;
+static mode_t mode;
+
+static int dsort __P((const FTSENT **, const FTSENT **));
+static void output __P((int, int *, const char *, ...));
+static int statd __P((FTS *, FTSENT *, uid_t *, gid_t *, mode_t *));
+static void statf __P((int, FTSENT *));
+
+void
+cwalk()
+{
+ register FTS *t;
+ register FTSENT *p;
+ time_t clock;
+ char *argv[2], host[MAXHOSTNAMELEN];
+ int indent = 0;
+
+ (void)time(&clock);
+ (void)gethostname(host, sizeof(host));
+ (void)printf(
+ "#\t user: %s\n#\tmachine: %s\n#\t tree: %s\n#\t date: %s",
+ getlogin(), host, fullpath, ctime(&clock));
+
+ argv[0] = ".";
+ argv[1] = NULL;
+ if ((t = fts_open(argv, ftsoptions, dsort)) == NULL)
+ err(1, "line %d: fts_open", lineno);
+ while ((p = fts_read(t))) {
+ if (iflag)
+ indent = p->fts_level * 4;
+ switch(p->fts_info) {
+ case FTS_D:
+ if (!dflag)
+ (void)printf("\n");
+ if (!nflag)
+ (void)printf("# %s\n", p->fts_path);
+ statd(t, p, &uid, &gid, &mode);
+ statf(indent, p);
+ break;
+ case FTS_DP:
+ if (!nflag && (p->fts_level > 0))
+ (void)printf("%*s# %s\n", indent, "", p->fts_path);
+ (void)printf("%*s..\n", indent, "");
+ if (!dflag)
+ (void)printf("\n");
+ break;
+ case FTS_DNR:
+ case FTS_ERR:
+ case FTS_NS:
+ warnx("%s: %s", p->fts_path, strerror(p->fts_errno));
+ break;
+ default:
+ if (!dflag)
+ statf(indent, p);
+ break;
+
+ }
+ }
+ (void)fts_close(t);
+ if (sflag && keys & F_CKSUM)
+ warnx("%s checksum: %lu", fullpath, crc_total);
+}
+
+static void
+statf(indent, p)
+ int indent;
+ FTSENT *p;
+{
+ struct group *gr;
+ struct passwd *pw;
+ u_long len, val;
+ int fd, offset;
+
+ if (iflag || S_ISDIR(p->fts_statp->st_mode))
+ offset = printf("%*s%s", indent, "", p->fts_name);
+ else
+ offset = printf("%*s %s", indent, "", p->fts_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);
+ }
+ if (keys & F_MD5 && S_ISREG(p->fts_statp->st_mode)) {
+ char *md5digest, buf[33];
+
+ md5digest = MD5File(p->fts_accpath,buf);
+ if (!md5digest) {
+ err(1, "line %d: %s", lineno, p->fts_accpath);
+ } else {
+ output(indent, &offset, "md5digest=%s", md5digest);
+ }
+ }
+ if (keys & F_SLINK &&
+ (p->fts_info == FTS_SL || p->fts_info == FTS_SLNONE))
+ output(indent, &offset, "link=%s", rlink(p->fts_accpath));
+ (void)putchar('\n');
+}
+
+#define MAXGID 5000
+#define MAXUID 5000
+#define MAXMODE MBITS + 1
+
+static int
+statd(t, parent, puid, pgid, pmode)
+ FTS *t;
+ FTSENT *parent;
+ uid_t *puid;
+ gid_t *pgid;
+ mode_t *pmode;
+{
+ register FTSENT *p;
+ register gid_t sgid;
+ register uid_t suid;
+ register mode_t smode;
+ struct group *gr;
+ struct passwd *pw;
+ gid_t savegid = *pgid;
+ uid_t saveuid = *puid;
+ mode_t savemode = *pmode;
+ u_short maxgid, maxuid, maxmode, g[MAXGID], u[MAXUID], m[MAXMODE];
+ static int first = 1;
+
+ if ((p = fts_children(t, 0)) == NULL) {
+ if (errno)
+ err(1, "line %d: %s", lineno, RP(parent));
+ return (1);
+ }
+
+ bzero(g, sizeof(g));
+ bzero(u, sizeof(u));
+ bzero(m, sizeof(m));
+
+ maxuid = maxgid = maxmode = 0;
+ for (; p; p = p->fts_link) {
+ if (!dflag || (dflag && S_ISDIR(p->fts_statp->st_mode))) {
+ smode = p->fts_statp->st_mode & MBITS;
+ if (smode < MAXMODE && ++m[smode] > maxmode) {
+ savemode = smode;
+ maxmode = m[smode];
+ }
+ sgid = p->fts_statp->st_gid;
+ if (sgid < MAXGID && ++g[sgid] > maxgid) {
+ savegid = sgid;
+ maxgid = g[sgid];
+ }
+ suid = p->fts_statp->st_uid;
+ if (suid < MAXUID && ++u[suid] > maxuid) {
+ saveuid = suid;
+ maxuid = u[suid];
+ }
+ }
+ }
+ /*
+ * If the /set record is the same as the last one we do not need to output
+ * a new one. So first we check to see if anything changed. Note that we
+ * always output a /set record for the first directory.
+ */
+ if ((((keys & F_UNAME) | (keys & F_UID)) && (*puid != saveuid)) ||
+ (((keys & F_GNAME) | (keys & F_GID)) && (*pgid != savegid)) ||
+ ((keys & F_MODE) && (*pmode != savemode)) || (first)) {
+ first = 0;
+ if (dflag)
+ (void)printf("/set type=dir");
+ else
+ (void)printf("/set type=file");
+ if (keys & F_UNAME)
+ if ((pw = getpwuid(saveuid)) != NULL)
+ (void)printf(" uname=%s", pw->pw_name);
+ else
+ errx(1,
+ "line %d: could not get uname for uid=%u",
+ lineno, saveuid);
+ if (keys & F_UID)
+ (void)printf(" uid=%lu", 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", savegid);
+ if (keys & F_MODE)
+ (void)printf(" mode=%#o", savemode);
+ if (keys & F_NLINK)
+ (void)printf(" nlink=1");
+ (void)printf("\n");
+ *puid = saveuid;
+ *pgid = savegid;
+ *pmode = savemode;
+ }
+ return (0);
+}
+
+static int
+dsort(a, b)
+ const FTSENT **a, **b;
+{
+ if (S_ISDIR((*a)->fts_statp->st_mode)) {
+ if (!S_ISDIR((*b)->fts_statp->st_mode))
+ return (1);
+ } else if (S_ISDIR((*b)->fts_statp->st_mode))
+ return (-1);
+ return (strcmp((*a)->fts_name, (*b)->fts_name));
+}
+
+#if __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+void
+#if __STDC__
+output(int indent, int *offset, const char *fmt, ...)
+#else
+output(indent, offset, fmt, va_alist)
+ int indent;
+ int *offset;
+ char *fmt;
+ va_dcl
+#endif
+{
+ va_list ap;
+ char buf[1024];
+#if __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+ (void)vsnprintf(buf, sizeof(buf), fmt, ap);
+ va_end(ap);
+
+ if (*offset + strlen(buf) > MAXLINELEN - 3) {
+ (void)printf(" \\\n%*s", INDENTNAMELEN + indent, "");
+ *offset = INDENTNAMELEN + indent;
+ }
+ *offset += printf(" %s", buf) + 1;
+}
diff --git a/usr.sbin/mtree/extern.h b/usr.sbin/mtree/extern.h
new file mode 100644
index 0000000..f3b6230
--- /dev/null
+++ b/usr.sbin/mtree/extern.h
@@ -0,0 +1,43 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)extern.h 8.1 (Berkeley) 6/6/93
+ */
+
+int compare __P((char *, NODE *, FTSENT *));
+int crc __P((int, u_long *, u_long *));
+void cwalk __P((void));
+char *inotype __P((u_int));
+u_int parsekey __P((char *, int *));
+char *rlink __P((char *));
+NODE *spec __P((void));
+int verify __P((void));
diff --git a/usr.sbin/mtree/misc.c b/usr.sbin/mtree/misc.c
new file mode 100644
index 0000000..093265c
--- /dev/null
+++ b/usr.sbin/mtree/misc.c
@@ -0,0 +1,101 @@
+/*-
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)misc.c 8.1 (Berkeley) 6/6/93";
+#endif
+static const char rcsid[] =
+ "$Id$";
+#endif /*not lint */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <err.h>
+#include <fts.h>
+#include <stdio.h>
+#include "mtree.h"
+#include "extern.h"
+
+extern int lineno;
+
+typedef struct _key {
+ char *name; /* key name */
+ u_int val; /* value */
+
+#define NEEDVALUE 0x01
+ u_int flags;
+} KEY;
+
+/* NB: the following table must be sorted lexically. */
+static KEY keylist[] = {
+ {"cksum", F_CKSUM, NEEDVALUE},
+ {"gid", F_GID, NEEDVALUE},
+ {"gname", F_GNAME, NEEDVALUE},
+ {"ignore", F_IGN, 0},
+ {"link", F_SLINK, NEEDVALUE},
+ {"md5digest", F_MD5, NEEDVALUE},
+ {"mode", F_MODE, NEEDVALUE},
+ {"nlink", F_NLINK, NEEDVALUE},
+ {"size", F_SIZE, NEEDVALUE},
+ {"time", F_TIME, NEEDVALUE},
+ {"type", F_TYPE, NEEDVALUE},
+ {"uid", F_UID, NEEDVALUE},
+ {"uname", F_UNAME, NEEDVALUE},
+};
+
+u_int
+parsekey(name, needvaluep)
+ char *name;
+ int *needvaluep;
+{
+ KEY *k, tmp;
+ int keycompare __P((const void *, const void *));
+
+ tmp.name = name;
+ k = (KEY *)bsearch(&tmp, keylist, sizeof(keylist) / sizeof(KEY),
+ sizeof(KEY), keycompare);
+ if (k == NULL)
+ errx(1, "line %d: unknown keyword %s", lineno, name);
+
+ if (needvaluep)
+ *needvaluep = k->flags & NEEDVALUE ? 1 : 0;
+ return (k->val);
+}
+
+int
+keycompare(a, b)
+ const void *a, *b;
+{
+ return (strcmp(((KEY *)a)->name, ((KEY *)b)->name));
+}
diff --git a/usr.sbin/mtree/mtree.8 b/usr.sbin/mtree/mtree.8
new file mode 100644
index 0000000..8fedd94
--- /dev/null
+++ b/usr.sbin/mtree/mtree.8
@@ -0,0 +1,290 @@
+.\" Copyright (c) 1989, 1990, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" From: @(#)mtree.8 8.2 (Berkeley) 12/11/93
+.\" $Id: mtree.8,v 1.10 1997/02/22 16:07:52 peter Exp $
+.\"
+.Dd February 9, 1995
+.Dt MTREE 8
+.Os
+.Sh NAME
+.Nm mtree
+.Nd map a directory hierarchy
+.Sh SYNOPSIS
+.Nm mtree
+.Op Fl cdeinrUux
+.Op Fl f Ar spec
+.Op Fl K Ar keywords
+.Op Fl k Ar keywords
+.Op Fl p Ar path
+.Op Fl s Ar seed
+.Sh DESCRIPTION
+The utility
+.Nm mtree
+compares the file hierarchy rooted in the current directory against a
+specification read from the standard input.
+Messages are written to the standard output for any files whose
+characteristics do not match the specifications, or which are
+missing from either the file hierarchy or the specification.
+.Pp
+The options are as follows:
+.Bl -tag -width flag
+.It Fl c
+Print a specification for the file hierarchy to the standard output.
+.It Fl d
+Ignore everything except directory type files.
+.It Fl e
+Don't complain about files that are in the file hierarchy, but not in the
+specification.
+.It Fl f Ar file
+Read the specification from
+.Ar file ,
+instead of from the standard input.
+.It Fl i
+Indent the output 4 spaces each time a directory level is descended when
+create a specification with the
+.Fl c
+option.
+This does not effect either the /set statements or the comment before each
+directory.
+It does however effect the comment before the close of each directory.
+.It Fl K Ar keywords
+Add the specified (whitespace or comma separated)
+.Ar keywords
+to the current set of keywords.
+.It Fl k Ar keywords
+Use the ``type'' keyword plus the specified (whitespace or comma separated)
+.Ar keywords
+instead of the current set of keywords.
+.It Fl n
+Do not emit pathname comments when creating a specification. Normally
+a comment is emitted before each directory and before the close of that
+directory when using the
+.Fl c
+option.
+.It Fl p Ar path
+Use the file hierarchy rooted in
+.Ar path ,
+instead of the current directory.
+.It Fl r
+Remove any files in the file hierarchy that are not described in the
+specification.
+.It Fl s Ar seed
+Display a single checksum to the standard error output that represents all
+of the files for which the keyword
+.Cm cksum
+was specified.
+The checksum is seeded with the specified value.
+.It Fl U
+Modify the owner, group, and permissions of existing files to match
+the specification and create any missing directories.
+User, group, and permissions must all be specified for missing directories
+to be created.
+Exit with a status of 0 on success, 1 if any error occurred,
+a mismatch is not considered an error if it was corrected.
+.It Fl u
+Same as
+.Fl U
+except a status of 2 is returned if the file hierarchy did not match
+the specification.
+.It Fl x
+Don't descend below mount points in the file hierarchy.
+.El
+.Pp
+Specifications are mostly composed of ``keywords'', i.e. strings that
+that specify values relating to files.
+No keywords have default values, and if a keyword has no value set, no
+checks based on it are performed.
+.Pp
+Currently supported keywords are as follows:
+.Bl -tag -width Cm
+.It Cm cksum
+The checksum of the file using the default algorithm specified by
+the
+.Xr cksum 1
+utility.
+.It Cm ignore
+Ignore any file hierarchy below this file.
+.It Cm gid
+The file group as a numeric value.
+.It Cm gname
+The file group as a symbolic name.
+.It Cm md5digest
+The MD5 message digest of the file.
+.It Cm 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 uid
+The file owner as a numeric value.
+.It Cm uname
+The file owner as a symbolic name.
+.It Cm size
+The size, in bytes, of the file.
+.It Cm link
+The file the symbolic link is expected to reference.
+.It Cm time
+The last modification time of the file.
+.It Cm type
+The type of the file; may be set to any one of the following:
+.sp
+.Bl -tag -width Cm -compact
+.It Cm block
+block special device
+.It Cm char
+character special device
+.It Cm dir
+directory
+.It Cm fifo
+fifo
+.It Cm file
+regular file
+.It Cm link
+symbolic link
+.It Cm socket
+socket
+.El
+.El
+.Pp
+The default set of keywords are
+.Cm gid ,
+.Cm mode ,
+.Cm nlink ,
+.Cm size ,
+.Cm link ,
+.Cm time ,
+and
+.Cm uid .
+.Pp
+There are four types of lines in a specification.
+.Pp
+The first type of line sets a global value for a keyword, and consists of
+the string ``/set'' followed by whitespace, followed by sets of keyword/value
+pairs, separated by whitespace.
+Keyword/value pairs consist of a keyword, followed by an equals sign
+(``=''), followed by a value, without whitespace characters.
+Once a keyword has been set, its value remains unchanged until either
+reset or unset.
+.Pp
+The second type of line unsets keywords and consists of the string
+``/unset'', followed by whitespace, followed by one or more keywords,
+separated by whitespace.
+.Pp
+The third type of line is a file specification and consists of a file
+name, followed by whitespace, followed by zero or more whitespace
+separated keyword/value pairs.
+The file name may be preceded by whitespace characters.
+The file name may contain any of the standard file name matching
+characters (``['', ``]'', ``?'' or ``*''), in which case files
+in the hierarchy will be associated with the first pattern that
+they match.
+.Pp
+Each of the keyword/value pairs consist of a keyword, followed by an
+equals sign (``=''), followed by the keyword's value, without
+whitespace characters.
+These values override, without changing, the global value of the
+corresponding keyword.
+.Pp
+All paths are relative.
+Specifying a directory will cause subsequent files to be searched
+for in that directory hierarchy.
+Which brings us to the last type of line in a specification: a line
+containing only the string
+.Dq Nm \&..
+causes the current directory
+path to ascend one level.
+.Pp
+Empty lines and lines whose first non-whitespace character is a hash
+mark (``#'') are ignored.
+.Pp
+The
+.Nm
+utility exits with a status of 0 on success, 1 if any error occurred,
+and 2 if the file hierarchy did not match the specification.
+A status of 2 is converted to a status of 0 if the
+.Fl U
+option is used.
+.Sh EXAMPLES
+To detect system binaries that have been ``trojan horsed'', it is recommended
+that
+.Nm
+.Fl K
+.Cm md5digest
+be run on the file systems, and a copy of the results stored on a different
+machine, or, at least, in encrypted form.
+The output file itself should be digested using the
+.Xr md5 1
+utility.
+Then, periodically,
+.Nm
+and
+.Xr md5 1
+should be run against the on-line specifications.
+While it is possible for the bad guys to change the on-line specifications
+to conform to their modified binaries, it is believed to be
+impractical for them to create a modified specification which has
+the same MD5 digest as the original.
+.Pp
+The
+.Fl d
+and
+.Fl u
+options can be used in combination to create directory hierarchies
+for distributions and other such things; the files in
+.Pa /etc/mtree
+were used to create almost all directories in this
+.Tn FreeBSD
+distribution.
+.Sh FILES
+.Bl -tag -width /etc/mtree -compact
+.It Pa /etc/mtree
+system specification directory
+.El
+.Sh SEE ALSO
+.Xr chgrp 1 ,
+.Xr chmod 1 ,
+.Xr cksum 1 ,
+.Xr md5 1 ,
+.Xr stat 2 ,
+.Xr fts 3 ,
+.Xr md5 3 ,
+.Xr chown 8
+.Sh HISTORY
+The
+.Nm
+utility appeared in
+.Bx 4.3 Reno .
+The MD5 digest capability was added in
+.Fx 2.1 ,
+in response to the widespread use of programs which can spoof
+.Xr cksum 1 .
+
diff --git a/usr.sbin/mtree/mtree.c b/usr.sbin/mtree/mtree.c
new file mode 100644
index 0000000..cf61a9b
--- /dev/null
+++ b/usr.sbin/mtree/mtree.c
@@ -0,0 +1,163 @@
+/*-
+ * Copyright (c) 1989, 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 1989, 1990, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)mtree.c 8.1 (Berkeley) 6/6/93";
+#endif
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <err.h>
+#include <errno.h>
+#include <fts.h>
+#include <stdio.h>
+#include <unistd.h>
+#include "mtree.h"
+#include "extern.h"
+
+extern long int crc_total;
+
+int ftsoptions = FTS_LOGICAL;
+int cflag, dflag, eflag, iflag, nflag, rflag, sflag, uflag, Uflag;
+u_short keys;
+char fullpath[MAXPATHLEN];
+
+static void usage __P((void));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int ch;
+ char *dir, *p;
+ int status;
+
+ dir = NULL;
+ keys = KEYDEFAULT;
+ while ((ch = getopt(argc, argv, "cdef:iK:k:np:rs:Uux")) != -1)
+ switch((char)ch) {
+ case 'c':
+ cflag = 1;
+ break;
+ case 'd':
+ dflag = 1;
+ break;
+ case 'e':
+ eflag = 1;
+ break;
+ case 'f':
+ if (!(freopen(optarg, "r", stdin)))
+ err(1, "%s", optarg);
+ break;
+ case 'i':
+ iflag = 1;
+ break;
+ case 'K':
+ while ((p = strsep(&optarg, " \t,")) != NULL)
+ if (*p != '\0')
+ keys |= parsekey(p, NULL);
+ break;
+ case 'k':
+ keys = F_TYPE;
+ while ((p = strsep(&optarg, " \t,")) != NULL)
+ if (*p != '\0')
+ keys |= parsekey(p, NULL);
+ break;
+ case 'n':
+ nflag = 1;
+ break;
+ case 'p':
+ dir = optarg;
+ break;
+ case 'r':
+ rflag = 1;
+ break;
+ case 's':
+ sflag = 1;
+ crc_total = ~strtol(optarg, &p, 0);
+ if (*p)
+ errx(1, "illegal seed value -- %s", optarg);
+ case 'U':
+ Uflag = 1;
+ uflag = 1;
+ break;
+ case 'u':
+ uflag = 1;
+ break;
+ case 'x':
+ ftsoptions |= FTS_XDEV;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc)
+ usage();
+
+ if (dir && chdir(dir))
+ err(1, "%s", dir);
+
+ if ((cflag || sflag) && !getwd(fullpath))
+ errx(1, "%s", fullpath);
+
+ if (cflag) {
+ cwalk();
+ exit(0);
+ }
+ status = verify();
+ if (Uflag & (status == MISMATCHEXIT))
+ status = 0;
+ exit(status);
+}
+
+static void
+usage()
+{
+ (void)fprintf(stderr,
+"usage: mtree [-cdeinrUux] [-f spec] [-K key] [-k key] [-p path] [-s seed]\n");
+ exit(1);
+}
diff --git a/usr.sbin/mtree/mtree.h b/usr.sbin/mtree/mtree.h
new file mode 100644
index 0000000..4102274
--- /dev/null
+++ b/usr.sbin/mtree/mtree.h
@@ -0,0 +1,90 @@
+/*-
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)mtree.h 8.1 (Berkeley) 6/6/93
+ */
+
+#include <string.h>
+#include <stdlib.h>
+
+#define KEYDEFAULT \
+ (F_GID | F_MODE | F_NLINK | F_SIZE | F_SLINK | F_TIME | F_UID)
+
+#define MISMATCHEXIT 2
+
+typedef struct _node {
+ struct _node *parent, *child; /* up, down */
+ struct _node *prev, *next; /* left, right */
+ off_t st_size; /* size */
+ struct timespec st_mtimespec; /* last modification time */
+ u_long cksum; /* check sum */
+ char *md5digest; /* MD5 digest */
+ char *slink; /* symbolic link reference */
+ uid_t st_uid; /* uid */
+ gid_t st_gid; /* gid */
+#define MBITS (S_ISUID|S_ISGID|S_ISTXT|S_IRWXU|S_IRWXG|S_IRWXO)
+ mode_t st_mode; /* mode */
+ nlink_t st_nlink; /* link count */
+
+#define F_CKSUM 0x0001 /* check sum */
+#define F_DONE 0x0002 /* directory done */
+#define F_GID 0x0004 /* gid */
+#define F_GNAME 0x0008 /* group name */
+#define F_IGN 0x0010 /* ignore */
+#define F_MAGIC 0x0020 /* name has magic chars */
+#define F_MODE 0x0040 /* mode */
+#define F_NLINK 0x0080 /* number of links */
+#define F_SIZE 0x0100 /* size */
+#define F_SLINK 0x0200 /* link count */
+#define F_TIME 0x0400 /* modification time */
+#define F_TYPE 0x0800 /* file type */
+#define F_UID 0x1000 /* uid */
+#define F_UNAME 0x2000 /* user name */
+#define F_VISIT 0x4000 /* file visited */
+#define F_MD5 0x8000 /* MD5 digest */
+ u_short 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..77f54b1
--- /dev/null
+++ b/usr.sbin/mtree/spec.c
@@ -0,0 +1,299 @@
+/*-
+ * Copyright (c) 1989, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)spec.c 8.1 (Berkeley) 6/6/93";
+#endif
+static const char rcsid[] =
+ "$Id$";
+#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 "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;
+ (void)strcpy(centry->name, p);
+#define MAGIC "?*["
+ if (strpbrk(p, MAGIC))
+ centry->flags |= F_MAGIC;
+ set(NULL, centry);
+
+ if (!root) {
+ last = root = centry;
+ root->parent = root;
+ } else if (last->type == F_DIR && !(last->flags & F_DONE)) {
+ centry->parent = last;
+ last = last->child = centry;
+ } else {
+ centry->parent = last->parent;
+ centry->prev = last;
+ last = last->next = centry;
+ }
+ }
+ return (root);
+}
+
+static void
+set(t, ip)
+ char *t;
+ register NODE *ip;
+{
+ register int type;
+ register char *kw, *val = NULL;
+ struct group *gr;
+ struct passwd *pw;
+ mode_t *m;
+ int value;
+ char *ep;
+
+ for (; (kw = strtok(t, "= \t\n")); t = NULL) {
+ ip->flags |= type = parsekey(kw, &value);
+ if (value && (val = strtok(NULL, " \t\n")) == NULL)
+ errx(1, "line %d: missing value", lineno);
+ switch(type) {
+ case F_CKSUM:
+ ip->cksum = strtoul(val, &ep, 10);
+ if (*ep)
+ errx(1, "line %d: invalid checksum %s",
+ lineno, val);
+ break;
+ case F_MD5:
+ ip->md5digest = strdup(val);
+ if(!ip->md5digest) {
+ errx(1, "strdup");
+ }
+ break;
+ case F_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);
+ 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..6610d3a
--- /dev/null
+++ b/usr.sbin/mtree/verify.c
@@ -0,0 +1,209 @@
+/*-
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)verify.c 8.1 (Berkeley) 6/6/93";
+#endif
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <err.h>
+#include <errno.h>
+#include <fts.h>
+#include <fnmatch.h>
+#include <stdio.h>
+#include <unistd.h>
+#include "mtree.h"
+#include "extern.h"
+
+extern long int crc_total;
+extern int ftsoptions;
+extern int dflag, eflag, rflag, sflag, uflag;
+extern char fullpath[MAXPATHLEN];
+extern int lineno;
+
+static NODE *root;
+static char path[MAXPATHLEN];
+
+static void miss __P((NODE *, char *));
+static int vwalk __P((void));
+
+int
+verify()
+{
+ int rval;
+
+ root = spec();
+ rval = vwalk();
+ miss(root, path);
+ return (rval);
+}
+
+static int
+vwalk()
+{
+ register FTS *t;
+ register FTSENT *p;
+ register NODE *ep, *level;
+ int specdepth, rval;
+ char *argv[2];
+
+ argv[0] = ".";
+ argv[1] = NULL;
+ if ((t = fts_open(argv, ftsoptions, NULL)) == NULL)
+ err(1, "line %d: fts_open", lineno);
+ level = root;
+ specdepth = rval = 0;
+ while ((p = fts_read(t))) {
+ switch(p->fts_info) {
+ case FTS_D:
+ break;
+ case FTS_DP:
+ if (specdepth > p->fts_level) {
+ for (level = level->parent; level->prev;
+ level = level->prev);
+ --specdepth;
+ }
+ continue;
+ case FTS_DNR:
+ case FTS_ERR:
+ case FTS_NS:
+ warnx("%s: %s", RP(p), strerror(p->fts_errno));
+ continue;
+ default:
+ if (dflag)
+ continue;
+ }
+
+ if (specdepth != p->fts_level)
+ goto extra;
+ for (ep = level; ep; ep = ep->next)
+ if ((ep->flags & F_MAGIC &&
+ !fnmatch(ep->name, p->fts_name, FNM_PATHNAME)) ||
+ !strcmp(ep->name, p->fts_name)) {
+ ep->flags |= F_VISIT;
+ if (compare(ep->name, ep, p))
+ rval = MISMATCHEXIT;
+ if (ep->flags & F_IGN)
+ (void)fts_set(t, p, FTS_SKIP);
+ else if (ep->child && ep->type == F_DIR &&
+ p->fts_info == FTS_D) {
+ level = ep->child;
+ ++specdepth;
+ }
+ break;
+ }
+
+ if (ep)
+ continue;
+extra:
+ if (!eflag) {
+ (void)printf("extra: %s", RP(p));
+ if (rflag) {
+ if ((S_ISDIR(p->fts_statp->st_mode)
+ ? rmdir : unlink)(p->fts_accpath)) {
+ (void)printf(", not removed: %s",
+ strerror(errno));
+ } else
+ (void)printf(", removed");
+ }
+ (void)putchar('\n');
+ }
+ (void)fts_set(t, p, FTS_SKIP);
+ }
+ (void)fts_close(t);
+ if (sflag)
+ warnx("%s checksum: %lu", fullpath, crc_total);
+ return (rval);
+}
+
+static void
+miss(p, tail)
+ register NODE *p;
+ register char *tail;
+{
+ register int create;
+ register char *tp;
+
+ for (; p; p = p->next) {
+ if (p->type != F_DIR && (dflag || p->flags & F_VISIT))
+ continue;
+ (void)strcpy(tail, p->name);
+ if (!(p->flags & F_VISIT))
+ (void)printf("missing: %s", path);
+ if (p->type != F_DIR) {
+ putchar('\n');
+ continue;
+ }
+
+ create = 0;
+ if (!(p->flags & F_VISIT) && uflag)
+ if (!(p->flags & (F_UID | F_UNAME)))
+ (void)printf(" (not created: user not specified)");
+ else if (!(p->flags & (F_GID | F_GNAME)))
+ (void)printf(" (not created: group not specified)");
+ else if (!(p->flags & F_MODE))
+ (void)printf(" (not created: mode not specified)");
+ else if (mkdir(path, S_IRWXU))
+ (void)printf(" (not created: %s)",
+ strerror(errno));
+ else {
+ create = 1;
+ (void)printf(" (created)");
+ }
+
+ if (!(p->flags & F_VISIT))
+ (void)putchar('\n');
+
+ for (tp = tail; *tp; ++tp);
+ *tp = '/';
+ miss(p->child, tp + 1);
+ *tp = '\0';
+
+ if (!create)
+ continue;
+ if (chown(path, p->st_uid, p->st_gid)) {
+ (void)printf("%s: user/group/mode not modified: %s\n",
+ path, strerror(errno));
+ continue;
+ }
+ if (chmod(path, p->st_mode))
+ (void)printf("%s: permissions not set: %s\n",
+ path, strerror(errno));
+ }
+}
diff --git a/usr.sbin/named.reload/Makefile b/usr.sbin/named.reload/Makefile
new file mode 100644
index 0000000..d5599dc
--- /dev/null
+++ b/usr.sbin/named.reload/Makefile
@@ -0,0 +1,22 @@
+# $Id$
+
+.include "${.CURDIR}/../named/Makefile.inc"
+
+.PATH: ${BIND_DIR}/named
+.PATH: ${BIND_DIR}/man
+
+MAN8= named.reload.8
+CLEANFILES+= named.reload
+
+all: named.reload
+
+realinstall:
+ ${INSTALL} ${COPY} -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \
+ named.reload ${DESTDIR}${BINDIR}
+
+named.reload: named.reload.sh ${BIND_DIR}/Makefile
+ sed -e "s|%INDOT%|${INDOT}|" \
+ -e "s|%DESTSBIN%|${DESTSBIN}|" \
+ < ${BIND_DIR}/named/named.reload.sh > named.reload
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/named.restart/Makefile b/usr.sbin/named.restart/Makefile
new file mode 100644
index 0000000..7055b22
--- /dev/null
+++ b/usr.sbin/named.restart/Makefile
@@ -0,0 +1,22 @@
+# $Id$
+
+.include "${.CURDIR}/../named/Makefile.inc"
+
+.PATH: ${BIND_DIR}/named
+.PATH: ${BIND_DIR}/man
+
+MAN8= named.restart.8
+CLEANFILES+= named.restart
+
+all: named.restart
+
+realinstall:
+ ${INSTALL} ${COPY} -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \
+ named.restart ${DESTDIR}${BINDIR}
+
+named.restart: named.restart.sh ${BIND_DIR}/Makefile
+ sed -e "s|%INDOT%|${INDOT}|" \
+ -e "s|%DESTSBIN%|${DESTSBIN}|" \
+ < ${BIND_DIR}/named/named.restart.sh > named.restart
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/named/Makefile b/usr.sbin/named/Makefile
new file mode 100644
index 0000000..4906e90
--- /dev/null
+++ b/usr.sbin/named/Makefile
@@ -0,0 +1,23 @@
+# $Id$
+
+.include "${.CURDIR}/Makefile.inc"
+
+.PATH: ${BIND_DIR}/named
+.PATH: ${BIND_DIR}/man
+
+PROG= named
+SRCS= version.c db_dump.c db_glue.c db_load.c db_lookup.c db_reload.c \
+ db_save.c db_secure.c db_update.c ns_forw.c \
+ ns_init.c ns_main.c ns_maint.c ns_ncache.c ns_req.c ns_resp.c \
+ ns_sort.c ns_stats.c ns_udp.c ns_validate.c storage.c tree.c
+MAN8= named.8
+
+CLEANFILES+= version.c
+
+version.c: Version.c ${BIND_DIR}/Makefile
+ (u=$${USER-root} d=`pwd` h=`hostname` t=`LC_TIME=C date`; \
+ sed -e "s|%WHEN%|$${t}|" -e "s|%VERSION%|"${VER}"|" \
+ -e "s|%WHOANDWHERE%|$${u}@$${h}:$${d}|" \
+ < ${BIND_DIR}/named/Version.c > version.c)
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/named/Makefile.inc b/usr.sbin/named/Makefile.inc
new file mode 100644
index 0000000..3b13a89
--- /dev/null
+++ b/usr.sbin/named/Makefile.inc
@@ -0,0 +1,24 @@
+# From: Id: Makefile.inc,v 8.4 1996/03/03 17:42:43 vixie Exp
+# $Id: Makefile.inc,v 1.4 1997/02/22 16:07:58 peter Exp $
+
+.ifndef (Mk.Inc)
+Mk.Inc?=defined
+
+BIND_DIR= ${.CURDIR}/../../contrib/bind
+
+VER!= awk -F' *= *' '$$1 == "VER" { print $$2 ; exit }' \
+ ${BIND_DIR}/Makefile
+
+PIDDIR= /var/run
+INDOT=
+XFER_INDOT= ${INDOT}
+PS= ps
+DESTSBIN= /usr/sbin
+IOT= ABRT
+
+CONFIG?= -DUSE_OPTIONS_H
+INCLUDE?= -I${BIND_DIR} -I${BIND_DIR}/include
+CFLAGS+= ${INCLUDE} ${CONFIG}
+
+.include "Makefile.maninc"
+.endif
diff --git a/usr.sbin/named/Makefile.maninc b/usr.sbin/named/Makefile.maninc
new file mode 100644
index 0000000..8551e63
--- /dev/null
+++ b/usr.sbin/named/Makefile.maninc
@@ -0,0 +1,56 @@
+# From: Id: Makefile.maninc,v 8.1 1994/12/15 06:23:43 vixie Exp
+# $Id$
+
+# (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"
+
+MANFILTER= ${EXT_SED_CMD}
diff --git a/usr.sbin/natd/HISTORY b/usr.sbin/natd/HISTORY
new file mode 100644
index 0000000..7d3175b
--- /dev/null
+++ b/usr.sbin/natd/HISTORY
@@ -0,0 +1,119 @@
+* Version 0.1
+
+ Initial version of natd.
+
+* Version 0.2
+
+ - Alias address can now be set by giving interface name with
+ new (-n) command-line option.
+
+ - New Makefile based on bsd.prog.mk.
+
+ - Error messages are written to syslog
+ after natd has become a daemon.
+
+* Version 1.0
+
+ - Support for using only single socket (-p option)
+
+* Version 1.1
+
+ - -a option now understands a hostname also.
+ - -a option no longer dumps core.
+ - Packet aliasing software upgraded to v. 1.9
+ - added long option names (like -address)
+
+* Version 1.2
+
+ - Fixed core dump with -port option.
+ - Added -Wall to CFLAGS and some headers added to natd.c
+ to get clean compile by Brian Somers [brian@awfulhak.org].
+
+* Version 1.3
+
+ - Aliasing address initialization is delayed until first
+ packet arrives. This allows natd to start up before
+ interface address is set.
+ - SIGTERM is now catched to allow kernel to close
+ existing connections when system is shutting down.
+ - SIGHUP is now catched to allow natd to refresh aliasing
+ address from interface, which might be useful to tun devices.
+
+* Version 1.4
+
+ - Changed command line options to be compatible with
+ command names used in ppp+packetAlias package (which is the
+ original application using aliasing routines).
+
+ The options which map directly to packet aliasing options are:
+
+ -unregistered_only [yes|no]
+ -log [yes|no]
+ -deny_incoming [yes|no]
+ -use_sockets [yes|no]
+ -same_ports [yes|no]
+
+ The short option names are the same as in previous
+ releases.
+
+ - Command line parser rewritten to provide more flexible
+ way to support new packet aliasing options.
+
+ - Support for natd.cf configuration file has been added.
+
+ - SIGHUP no longer causes problems when running without
+ interface name option.
+
+ - When using -interface command line option, routing socket
+ is optionally listened for interface address changes. This
+ mode is activated by -dynamic option.
+
+ - Directory tree reorganized, alias package is now a library.
+
+ - Manual page written by Brian Somers <brian@awfulhak.org> added.
+ - README file updated.
+
+* Version 1.5
+
+ - Support for sending ICMP 'need fragmentation' messages
+ when packet size exceeds mtu size of outgoing network interface.
+
+ - ipfw rule example in manual page fixed.
+
+* Version 1.6
+
+ - Upgrade to new packet aliasing engine (2.1)
+ - redirect_port and redirect_address configuration
+ parameters added.
+ - It is no longer necessary to quote complex parameter values
+ in command line.
+ - Manual page fixed (same_port -> same_ports).
+
+* Version 1.7
+
+ - A bug in command-line parsing fixed (it appeared due
+ to changes made in 1.6).
+
+* Version 1.8
+
+ - Fixed problems with -dynamic option.
+ - Added /var/run/natd.pid
+
+* Version 1.9
+
+ - Changes to manual page by
+ Brian Somers <brian@awfulhak.org> integrated.
+ - Checksum for incoming packets is always recalculated
+ for FreeBSD 2.2 and never recalculated for newer
+ versions. This should fix the problem with wrong
+ checksum of fragmented packets.
+ - Buffer space problem found by Sergio Lenzi <lenzi@bsi.com.br>
+ fixed. Natd now waits with select(2) for buffer space
+ to become available if write fails.
+ - Packet aliasing library upgraded to 2.2.
+
+* Version 1.10
+
+ - Ignored incoming packets are now dropped when
+ deny_incoming option is set to yes.
+ - Packet aliasing library upgraded to 2.4.
diff --git a/usr.sbin/natd/Makefile b/usr.sbin/natd/Makefile
new file mode 100644
index 0000000..015d645
--- /dev/null
+++ b/usr.sbin/natd/Makefile
@@ -0,0 +1,10 @@
+# $Id:$
+
+PROG= natd
+SRCS= natd.c icmp.c
+CFLAGS+=-Wall
+LDADD= -lalias
+DPADD= ${LIBALIAS}
+MAN8= natd.8
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/natd/README b/usr.sbin/natd/README
new file mode 100644
index 0000000..6c158d5
--- /dev/null
+++ b/usr.sbin/natd/README
@@ -0,0 +1,53 @@
+
+ A Network Address Translation Daemon for FreeBSD
+
+
+1. WHAT IS NATD ?
+
+ This is a simple daemon based on FreeBSD divert sockets
+ which performs network address translation (or masquerading)
+ for IP packets (see related RFCs 1631 and 1918).
+ It is based on packet aliasing package (see README.alias)
+ written by Charles Mott (cmott@srv.net).
+
+ This package works with any network interface (doesn't have
+ to be ppp). I run it on a computer having two ethernet cards,
+ one connected to internet and the other one to local network.
+
+2. GETTING IT RUNNING
+
+ 1) Get FreeBSD 2.2 - I think the divert sockets are
+ not available on earlier versions,
+
+ 2) Compile this software by executing "make".
+
+ 3) Install the software by executing "make install".
+
+ 4) See man natd for further instructions.
+
+3. FTP SITES FOR NATD
+
+ This package is available at ftp://ftp.suutari.iki.fi/pub/natd.
+
+4. AUTHORS
+
+ This program is the result of the efforts of many people
+ at different times:
+
+ Archie Cobbs <archie@whistle.com> Divert sockets
+ Charles Mott <cmott@srv.net> Packet aliasing engine
+ Eivind Eklund <eivind@dimaga.com> Packet aliasing engine
+ Ari Suutari <suutari@iki.fi> Natd
+ Brian Somers <brian@awfulhak.org> Manual page, glue and
+ bunch of good ideas.
+
+ The original package written by Charles Mott
+ is available at http://www.srv.net/~cmott.
+ It is described in README.alias.
+
+ Happy Networking - comments and fixes are welcome!
+
+ Ari S. (suutari@iki.fi)
+
+
+
diff --git a/usr.sbin/natd/icmp.c b/usr.sbin/natd/icmp.c
new file mode 100644
index 0000000..2018f66
--- /dev/null
+++ b/usr.sbin/natd/icmp.c
@@ -0,0 +1,113 @@
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <ctype.h>
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <errno.h>
+#include <signal.h>
+
+#include <netdb.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/ip_icmp.h>
+#include <machine/in_cksum.h>
+
+#include <alias.h>
+
+#include "natd.h"
+
+int SendNeedFragIcmp (int sock, struct ip* failedDgram, int mtu)
+{
+ char icmpBuf[IP_MAXPACKET];
+ struct ip* ip;
+ struct icmp* icmp;
+ int icmpLen;
+ int failBytes;
+ int failHdrLen;
+ struct sockaddr_in addr;
+ int wrote;
+ struct in_addr swap;
+/*
+ * Don't send error if packet is
+ * not the first fragment.
+ */
+ if (ntohs (failedDgram->ip_off) & ~(IP_MF | IP_DF))
+ return 0;
+/*
+ * Dont respond if failed datagram is ICMP.
+ */
+ if (failedDgram->ip_p == IPPROTO_ICMP)
+ return 0;
+/*
+ * Start building the message.
+ */
+ ip = (struct ip*) icmpBuf;
+ icmp = (struct icmp*) (icmpBuf + sizeof (struct ip));
+/*
+ * Complete ICMP part.
+ */
+ icmp->icmp_type = ICMP_UNREACH;
+ icmp->icmp_code = ICMP_UNREACH_NEEDFRAG;
+ icmp->icmp_cksum = 0;
+ icmp->icmp_void = 0;
+ icmp->icmp_nextmtu = htons (mtu);
+/*
+ * Copy header + 64 bits of original datagram.
+ */
+ failHdrLen = (failedDgram->ip_hl << 2);
+ failBytes = failedDgram->ip_len - failHdrLen;
+ if (failBytes > 8)
+ failBytes = 8;
+
+ failBytes += failHdrLen;
+ icmpLen = ICMP_MINLEN + failBytes;
+
+ memcpy (&icmp->icmp_ip, failedDgram, failBytes);
+/*
+ * Calculate checksum.
+ */
+ icmp->icmp_cksum = PacketAliasInternetChecksum ((u_short*) icmp,
+ icmpLen);
+/*
+ * Add IP header using old IP header as template.
+ */
+ memcpy (ip, failedDgram, sizeof (struct ip));
+
+ ip->ip_v = 4;
+ ip->ip_hl = 5;
+ ip->ip_len = htons (sizeof (struct ip) + icmpLen);
+ ip->ip_p = IPPROTO_ICMP;
+ ip->ip_tos = 0;
+
+ swap = ip->ip_dst;
+ ip->ip_dst = ip->ip_src;
+ ip->ip_src = swap;
+
+ PacketAliasIn ((char*) ip, IP_MAXPACKET);
+
+ addr.sin_family = AF_INET;
+ addr.sin_addr = ip->ip_dst;
+ addr.sin_port = 0;
+/*
+ * Put packet into processing queue.
+ */
+ wrote = sendto (sock,
+ icmp,
+ icmpLen,
+ 0,
+ (struct sockaddr*) &addr,
+ sizeof addr);
+
+ if (wrote != icmpLen)
+ Warn ("Cannot send ICMP message.");
+
+ return 1;
+}
+
+
diff --git a/usr.sbin/natd/natd.8 b/usr.sbin/natd/natd.8
new file mode 100644
index 0000000..8aeb312
--- /dev/null
+++ b/usr.sbin/natd/natd.8
@@ -0,0 +1,383 @@
+.\" manual page [] for natd 1.4
+.Dd 15 April 1997
+.Os FreeBSD
+.Dt NATD 8
+.Sh NAME
+.Nm natd
+.Nd
+Network Address Translation Daemon
+.Sh SYNOPSIS
+.Nm
+.Op Fl ldsmvu
+.Op Fl permanent_link
+.Op Fl dynamic
+.Op Fl i Ar inport
+.Op Fl o Ar outport
+.Op Fl p Ar port
+.Op Fl a Ar address
+.Op Fl i Ar interface
+.Op Fl f Ar configfile
+
+.Nm
+.Op Fl log
+.Op Fl deny_incoming
+.Op Fl use_sockets
+.Op Fl same_ports
+.Op Fl verbose
+.Op Fl unregistered_only
+.Op Fl permanent_link
+.Op Fl dynamic
+.Op Fl inport Ar inport
+.Op Fl outport Ar outport
+.Op Fl port Ar port
+.Op Fl alias_address Ar address
+.Op Fl interface Ar interface
+.Op Fl config Ar configfile
+.Op Fl redirect_port Ar linkspec
+.Op Fl redirect_address Ar localIP publicIP
+
+.Sh DESCRIPTION
+This program provides a Network Address Translation facility for use
+with
+.Xr divert 4
+sockets under FreeBSD. Most of the command line options are available
+in a single character short form or in a long form. Use of the long
+form is encouraged as it makes things clearer to the casual observer.
+
+.Pp
+.Nm Natd
+normally runs in the background as a daemon. It is passed raw IP packets
+as they travel into and out of the machine, and will possibly change these
+before re-injecting them back into the IP packet stream.
+
+.Pp
+.Nm Natd
+changes all packets destined for another host so that their source
+IP number is that of the current machine. For each packet changed
+in this manner, an internal table entry is created to record this
+fact. The source port number is also changed to indicate the
+table entry applying to the packet. Packets that are received with
+a target IP of the current host are checked against this internal
+table. If an entry is found, it is used to determine the correct
+target IP number and port to place in the packet.
+
+.Pp
+The following command line options are available.
+.Bl -tag -width Fl
+
+.It Fl log | l
+Log various aliasing statistics and information to the file
+.Pa /var/log/alias.log .
+This file is truncated each time natd is started.
+
+.It Fl deny_incoming | d
+Reject packets destined for the current IP number that have no entry
+in the internal translation table.
+
+.It Fl use_sockets | s
+Allocate a
+.Xr socket 2
+in order to establish an FTP data or IRC DCC send connection. This
+option uses more system resources, but guarantees successful connections
+when port numbers conflict.
+
+.It Fl same_ports | m
+Try to keep the same port number when altering outgoing packets.
+With this option, protocols such as RPC will have a better chance
+of working. If it is not possible to maintain the port number, it
+will be silently changed as per normal.
+
+.It Fl verbose | v
+Don't call
+.Xr fork 2
+or
+.Xr daemon 3
+on startup. Instead, stay attached to the controling terminal and
+display all packet alterations to the standard output. This option
+should only be used for debugging purposes.
+
+.It Fl unregistered_only | u
+Only alter outgoing packets with an unregistered source address.
+According to rfc 1918, unregistered source addresses are 10.0.0.0/8,
+172.16.0.0/12 and 192.168.0.0/16.
+
+.It Fl redirect_port Ar linkspec
+Redirect incoming connections arriving to given port to another host and port.
+Linkspec is of the form
+
+ proto targetIP:targetPORT [aliasIP:]aliasPORT [remoteIP[:remotePORT]]
+
+where proto is either tcp or udp, targetIP is the desired target IP
+number, targetPORT is the desired target PORT number, aliasPORT
+is the requested PORT number and aliasIP is the aliasing address.
+RemoteIP and remotePORT can be used to specify the connection
+more accurately if necessary.
+For example, the argument
+
+.Ar tcp inside1:telnet 6666
+
+means that tcp packets destined for port 6666 on this machine will
+be sent to the telnet port on the inside1 machine.
+
+.It Fl redirect_address Ar localIP publicIP
+Redirect traffic for public IP address to a machine on the local
+network. This function is known as "static NAT". Normally static NAT
+is useful if your ISP has allocated a small block of IP addresses to you,
+but it can even be used in the case of single address:
+
+ redirect_address 10.0.0.8 0.0.0.0
+
+The above command would redirect all incoming traffic
+to machine 10.0.0.8.
+
+If several address aliases specify the same public address
+as follows
+
+ redirect_address 192.168.0.2 public_addr
+ redirect_address 192.168.0.3 public_addr
+ redirect_address 192.168.0.4 public_addr
+
+the incoming traffic will be directed to the last
+translated local address (192.168.0.4), but outgoing
+traffic to the first two addresses will still be aliased
+to specified public address.
+
+.It Fl permanent_link Ar linkspec
+Create a permanent entry in the internal alias table. Linkspec is
+of the form
+
+ proto targetIP:targetPORT sourceIP:sourcePORT aliasPORT
+
+where proto is either tcp or udp, targetIP is the desired target IP
+number, targetPORT is the desired target PORT number, sourceIP and
+sourcePORT match the incoming packet, and aliasPORT is the requested
+PORT number. Values of zero are considered as wildcards. For example,
+the argument
+
+.Ar tcp inside1:telnet outside1:0 6666
+
+means that tcp packets destined for port 6666 on this machine from the
+outside1 machine (any port) will be sent to the telnet port on the
+inside1 machine.
+
+New installations are encouraged to use redirect_port instead.
+
+.It Fl dynamic
+If the
+.Fl n
+or
+.Fl interface
+option is used,
+.Nm
+will monitor the routing socket for alterations to the
+.Ar interface
+passed. If the interfaces IP number is changed,
+.Nm
+will dynamically alter its concept of the alias address.
+
+.It Fl i | inport Ar inport
+Read from and write to
+.Ar inport ,
+treating all packets as packets coming into the machine.
+
+.It Fl o | outport Ar outport
+Read from and write to
+.Ar outport ,
+treating all packets as packets going out of the machine.
+
+.It Fl p | port Ar port
+Read from and write to
+.Ar port ,
+distinguishing packets as incoming our outgoing using the rules specified in
+.Xr divert 4 .
+If
+.Ar port
+is not numeric, it is searched for in the
+.Pa /etc/services
+database using the
+.Xr getservbyname 3
+function. If this flag is not specified, the divert port named natd will
+be used as a default. An example entry in the
+.Pa /etc/services
+database would be:
+
+ natd 6668/divert # Network Address Translation socket
+
+Refer to
+.Xr services 5
+for further details.
+
+.It Fl a | alias_address Ar address
+Use
+.Ar address
+as the alias address. If this option is not specified, the
+.Fl n
+or
+.Fl interface
+option must be used.
+
+.It Fl n | interface Ar interface
+Use
+.Ar interface
+to determine the alias address. If there is a possibility that the
+IP number associated with
+.Ar interface
+may change, the
+.Fl dynamic
+flag should also be used. If this option is not specified, the
+.Fl a
+or
+.Fl alias_address
+flag must be used.
+
+.It Fl f | config Ar configfile
+Read configuration from
+.Ar configfile .
+.Ar Configfile
+contains a list of options, one per line in the same form as the
+long form of the above command line flags. For example, the line
+
+ alias_address 158.152.17.1
+
+would specify an alias address of 158.152.17.1. Options that don't
+take an argument are specified with an option of
+.Ar yes
+or
+.Ar no
+in the configuration file. For example, the line
+
+ log yes
+
+is synonomous with
+.Fl log .
+Empty lines and lines beginning with '#' are ignored.
+
+.El
+
+.Sh RUNNING NATD
+The following steps are necessary before attempting to run
+.Nm natd :
+
+.Bl -enum
+.It
+Get FreeBSD version 2.2 or higher. Versions before this do not support
+.Xr divert 4
+sockets.
+
+.It
+Build a custom kernel with the following options:
+
+ options IPFIREWALL
+ options IPDIVERT
+
+Refer to the handbook for detailed instructions on building a custom
+kernel.
+
+.It
+Ensure that your machine is acting as a gateway. This can be done by
+specifying the line
+
+ gateway_enable=YES
+
+in
+.Pa /etc/rc.conf ,
+or using the command
+
+ sysctl -w net.inet.ip.forwarding=1
+
+.It
+If you wish to use the
+.Fl n
+or
+.Fl interface
+flags, make sure that your interface is already configured. If, for
+example, you wish to specify tun0 as your
+.Ar interface ,
+and you're using
+.Xr ppp 8
+on that interface, you must make sure that you start
+.Nm ppp
+prior to starting
+.Nm natd .
+
+.It
+Create an entry in
+.Pa /etc/services :
+
+ natd 6668/divert # Network Address Translation socket
+
+This gives a default for the
+.Fl p
+or
+.Fl port
+flag.
+
+.El
+.Pp
+Running
+.Nm
+is fairly straight forward. The line
+
+ natd -interface ed0
+
+should suffice in most cases (substituting the correct interface name). Once
+.Nm
+is running, you must ensure that traffic is diverted to natd:
+
+.Bl -enum
+.It
+You will need to adjust the
+.Pa /etc/rc.firewall
+script to taste. If you're not interested in having a firewall, the
+following lines will do:
+
+ /sbin/ipfw -f flush
+ /sbin/ipfw add divert natd all from any to any via ed0
+ /sbin/ipfw add pass all from any to any
+
+The second line depends on your interface (change ed0 as appropriate)
+and assumes that you've updated
+.Pa /etc/services
+with the natd entry as above. If you specify real firewall rules, it's
+best to specify line 2 at the start of the script so that
+.Nm
+sees all packets before they are dropped by the firewall. The firewall
+rules will be run again on each packet after translation by
+.Nm natd ,
+minus any divert rules.
+
+.It
+Enable your firewall by setting
+
+ firewall_enable=YES
+
+in
+.Pa /etc/rc.conf .
+This tells the system startup scripts to run the
+.Pa /etc/rc.firewall
+script. If you don't wish to reboot now, just run this by hand from the
+console. NEVER run this from a virtual session unless you put it into
+the background. If you do, you'll lock yourself out after the flush
+takes place, and execution of
+.Pa /etc/rc.firewall
+will stop at this point - blocking all accesses permanently. Running
+the script in the background should be enough to prevent this disaster.
+
+.El
+
+.Sh SEE ALSO
+.Xr getservbyname 2 ,
+.Xr socket 2 ,
+.Xr divert 4 ,
+.Xr services 5 ,
+.Xr ipfw 8
+
+.Sh AUTHORS
+This program is the result of the efforts of many people at different
+times:
+
+ Divert sockets: Archie Cobbs <archie@whistle.com>
+ Packet aliasing: Charles Mott <cmott@srv.net>
+ IRC support & misc additions: Eivind Eklund <perhaps@yes.no>
+ Natd: Ari Suutari <suutari@iki.fi>
+ Glue: Brian Somers <brian@awfulhak.org>
diff --git a/usr.sbin/natd/natd.c b/usr.sbin/natd/natd.c
new file mode 100644
index 0000000..fb80d1d
--- /dev/null
+++ b/usr.sbin/natd/natd.c
@@ -0,0 +1,1369 @@
+/*
+ * natd - Network Address Translation Daemon for FreeBSD.
+ *
+ * This software ois provided free of charge, with no
+ * warranty of any kind, either expressed or implied.
+ * Use at your own risk.
+ *
+ * You may copy, modify and distribute this software (natd.c) freely.
+ *
+ * Ari Suutari <suutari@iki.fi>
+ *
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <machine/in_cksum.h>
+#include <netinet/tcp.h>
+#include <sys/ioctl.h>
+#include <net/if.h>
+#include <net/route.h>
+#include <arpa/inet.h>
+
+#include <alias.h>
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <netdb.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+#include "natd.h"
+
+/*
+ * Default values for input and output
+ * divert socket ports.
+ */
+
+#define DEFAULT_SERVICE "natd"
+
+/*
+ * Function prototypes.
+ */
+
+static void DoAliasing (int fd);
+static void DaemonMode ();
+static void HandleRoutingInfo (int fd);
+static void Usage ();
+static void PrintPacket (struct ip*);
+static void SetAliasAddressFromIfName (char* ifName);
+static void InitiateShutdown ();
+static void Shutdown ();
+static void RefreshAddr ();
+static void ParseOption (char* option, char* parms, int cmdLine);
+static void ReadConfigFile (char* fileName);
+static void SetupPermanentLink (char* parms);
+static void SetupPortRedirect (char* parms);
+static void SetupAddressRedirect (char* parms);
+static void StrToAddr (char* str, struct in_addr* addr);
+static int StrToPort (char* str, char* proto);
+static int StrToProto (char* str);
+static int StrToAddrAndPort (char* str, struct in_addr* addr, char* proto);
+static void ParseArgs (int argc, char** argv);
+static void FlushPacketBuffer (int fd);
+
+/*
+ * Globals.
+ */
+
+static int verbose;
+static int background;
+static int running;
+static int assignAliasAddr;
+static char* ifName;
+static int ifIndex;
+static int inPort;
+static int outPort;
+static int inOutPort;
+static struct in_addr aliasAddr;
+static int dynamicMode;
+static int ifMTU;
+static int aliasOverhead;
+static int icmpSock;
+static char packetBuf[IP_MAXPACKET];
+static int packetLen;
+static struct sockaddr_in packetAddr;
+static int packetSock;
+static int dropIgnoredIncoming;
+
+int main (int argc, char** argv)
+{
+ int divertIn;
+ int divertOut;
+ int divertInOut;
+ int routeSock;
+ struct sockaddr_in addr;
+ fd_set readMask;
+ fd_set writeMask;
+ int fdMax;
+/*
+ * Initialize packet aliasing software.
+ * Done already here to be able to alter option bits
+ * during command line and configuration file processing.
+ */
+ PacketAliasInit ();
+/*
+ * Parse options.
+ */
+ inPort = 0;
+ outPort = 0;
+ verbose = 0;
+ inOutPort = 0;
+ ifName = NULL;
+ ifMTU = -1;
+ background = 0;
+ running = 1;
+ assignAliasAddr = 0;
+ aliasAddr.s_addr = INADDR_NONE;
+ aliasOverhead = 12;
+ dynamicMode = 0;
+/*
+ * Mark packet buffer empty.
+ */
+ packetSock = -1;
+
+ ParseArgs (argc, argv);
+/*
+ * Check that valid aliasing address has been given.
+ */
+ if (aliasAddr.s_addr == INADDR_NONE && ifName == NULL)
+ errx(1, "aliasing address not given");
+
+ if (aliasAddr.s_addr != INADDR_NONE && ifName != NULL)
+ errx(1,
+ "both alias address and interface name are not allowed");
+/*
+ * Check that valid port number is known.
+ */
+ if (inPort != 0 || outPort != 0)
+ if (inPort == 0 || outPort == 0)
+ errx(1, "both input and output ports are required");
+
+ if (inPort == 0 && outPort == 0 && inOutPort == 0)
+ ParseOption ("port", DEFAULT_SERVICE, 0);
+
+/*
+ * Check if ignored packets should be dropped.
+ */
+ dropIgnoredIncoming = PacketAliasSetMode (0, 0);
+ dropIgnoredIncoming &= PKT_ALIAS_DENY_INCOMING;
+/*
+ * Create divert sockets. Use only one socket if -p was specified
+ * on command line. Otherwise, create separate sockets for
+ * outgoing and incoming connnections.
+ */
+ if (inOutPort) {
+
+ divertInOut = socket (PF_INET, SOCK_RAW, IPPROTO_DIVERT);
+ if (divertInOut == -1)
+ Quit ("Unable to create divert socket.");
+
+ divertIn = -1;
+ divertOut = -1;
+/*
+ * Bind socket.
+ */
+
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = INADDR_ANY;
+ addr.sin_port = inOutPort;
+
+ if (bind (divertInOut,
+ (struct sockaddr*) &addr,
+ sizeof addr) == -1)
+ Quit ("Unable to bind divert socket.");
+ }
+ else {
+
+ divertIn = socket (PF_INET, SOCK_RAW, IPPROTO_DIVERT);
+ if (divertIn == -1)
+ Quit ("Unable to create incoming divert socket.");
+
+ divertOut = socket (PF_INET, SOCK_RAW, IPPROTO_DIVERT);
+ if (divertOut == -1)
+ Quit ("Unable to create outgoing divert socket.");
+
+ divertInOut = -1;
+
+/*
+ * Bind divert sockets.
+ */
+
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = INADDR_ANY;
+ addr.sin_port = inPort;
+
+ if (bind (divertIn,
+ (struct sockaddr*) &addr,
+ sizeof addr) == -1)
+ Quit ("Unable to bind incoming divert socket.");
+
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = INADDR_ANY;
+ addr.sin_port = outPort;
+
+ if (bind (divertOut,
+ (struct sockaddr*) &addr,
+ sizeof addr) == -1)
+ Quit ("Unable to bind outgoing divert socket.");
+ }
+/*
+ * Create routing socket if interface name specified.
+ */
+ if (ifName && dynamicMode) {
+
+ routeSock = socket (PF_ROUTE, SOCK_RAW, 0);
+ if (routeSock == -1)
+ Quit ("Unable to create routing info socket.");
+ }
+ else
+ routeSock = -1;
+/*
+ * Create socket for sending ICMP messages.
+ */
+ icmpSock = socket (AF_INET, SOCK_RAW, IPPROTO_ICMP);
+ if (icmpSock == -1)
+ Quit ("Unable to create ICMP socket.");
+/*
+ * Become a daemon unless verbose mode was requested.
+ */
+ if (!verbose)
+ DaemonMode ();
+/*
+ * Catch signals to manage shutdown and
+ * refresh of interface address.
+ */
+ signal (SIGTERM, InitiateShutdown);
+ signal (SIGHUP, RefreshAddr);
+/*
+ * Set alias address if it has been given.
+ */
+ if (aliasAddr.s_addr != INADDR_NONE)
+ PacketAliasSetAddress (aliasAddr);
+/*
+ * We need largest descriptor number for select.
+ */
+
+ fdMax = -1;
+
+ if (divertIn > fdMax)
+ fdMax = divertIn;
+
+ if (divertOut > fdMax)
+ fdMax = divertOut;
+
+ if (divertInOut > fdMax)
+ fdMax = divertInOut;
+
+ if (routeSock > fdMax)
+ fdMax = routeSock;
+
+ while (running) {
+
+ if (divertInOut != -1 && !ifName && packetSock == -1) {
+/*
+ * When using only one socket, just call
+ * DoAliasing repeatedly to process packets.
+ */
+ DoAliasing (divertInOut);
+ continue;
+ }
+/*
+ * Build read mask from socket descriptors to select.
+ */
+ FD_ZERO (&readMask);
+ FD_ZERO (&writeMask);
+
+/*
+ * If there is unsent packet in buffer, use select
+ * to check when socket comes writable again.
+ */
+ if (packetSock != -1) {
+
+ FD_SET (packetSock, &writeMask);
+ }
+ else {
+/*
+ * No unsent packet exists - safe to check if
+ * new ones are available.
+ */
+ if (divertIn != -1)
+ FD_SET (divertIn, &readMask);
+
+ if (divertOut != -1)
+ FD_SET (divertOut, &readMask);
+
+ if (divertInOut != -1)
+ FD_SET (divertInOut, &readMask);
+ }
+/*
+ * Routing info is processed always.
+ */
+ if (routeSock != -1)
+ FD_SET (routeSock, &readMask);
+
+ if (select (fdMax + 1,
+ &readMask,
+ &writeMask,
+ NULL,
+ NULL) == -1) {
+
+ if (errno == EINTR)
+ continue;
+
+ Quit ("Select failed.");
+ }
+
+ if (packetSock != -1)
+ if (FD_ISSET (packetSock, &writeMask))
+ FlushPacketBuffer (packetSock);
+
+ if (divertIn != -1)
+ if (FD_ISSET (divertIn, &readMask))
+ DoAliasing (divertIn);
+
+ if (divertOut != -1)
+ if (FD_ISSET (divertOut, &readMask))
+ DoAliasing (divertOut);
+
+ if (divertInOut != -1)
+ if (FD_ISSET (divertInOut, &readMask))
+ DoAliasing (divertInOut);
+
+ if (routeSock != -1)
+ if (FD_ISSET (routeSock, &readMask))
+ HandleRoutingInfo (routeSock);
+ }
+
+ if (background)
+ unlink (PIDFILE);
+
+ return 0;
+}
+
+static void DaemonMode ()
+{
+ FILE* pidFile;
+
+ daemon (0, 0);
+ background = 1;
+
+ pidFile = fopen (PIDFILE, "w");
+ if (pidFile) {
+
+ fprintf (pidFile, "%d\n", getpid ());
+ fclose (pidFile);
+ }
+}
+
+static void ParseArgs (int argc, char** argv)
+{
+ int arg;
+ char* parm;
+ char* opt;
+ char parmBuf[256];
+
+ for (arg = 1; arg < argc; arg++) {
+
+ opt = argv[arg];
+ if (*opt != '-') {
+
+ warnx ("invalid option %s", opt);
+ Usage ();
+ }
+
+ parm = NULL;
+ parmBuf[0] = '\0';
+
+ while (arg < argc - 1) {
+
+ if (argv[arg + 1][0] == '-')
+ break;
+
+ if (parm)
+ strcat (parmBuf, " ");
+
+ ++arg;
+ parm = parmBuf;
+ strcat (parmBuf, argv[arg]);
+ }
+
+ ParseOption (opt + 1, parm, 1);
+ }
+}
+
+static void DoAliasing (int fd)
+{
+ int bytes;
+ int origBytes;
+ int status;
+ int addrSize;
+ struct ip* ip;
+
+ if (assignAliasAddr) {
+
+ SetAliasAddressFromIfName (ifName);
+ assignAliasAddr = 0;
+ }
+/*
+ * Get packet from socket.
+ */
+ addrSize = sizeof packetAddr;
+ origBytes = recvfrom (fd,
+ packetBuf,
+ sizeof packetBuf,
+ 0,
+ (struct sockaddr*) &packetAddr,
+ &addrSize);
+
+ if (origBytes == -1) {
+
+ if (errno != EINTR)
+ Warn ("read from divert socket failed");
+
+ return;
+ }
+/*
+ * This is a IP packet.
+ */
+ ip = (struct ip*) packetBuf;
+
+ if (verbose) {
+
+/*
+ * Print packet direction and protocol type.
+ */
+
+ if (packetAddr.sin_addr.s_addr == INADDR_ANY)
+ printf ("Out ");
+ else
+ printf ("In ");
+
+ switch (ip->ip_p) {
+ case IPPROTO_TCP:
+ printf ("[TCP] ");
+ break;
+
+ case IPPROTO_UDP:
+ printf ("[UDP] ");
+ break;
+
+ case IPPROTO_ICMP:
+ printf ("[ICMP] ");
+ break;
+
+ default:
+ printf ("[?] ");
+ break;
+ }
+/*
+ * Print addresses.
+ */
+ PrintPacket (ip);
+ }
+
+ if (packetAddr.sin_addr.s_addr == INADDR_ANY) {
+/*
+ * Outgoing packets. Do aliasing.
+ */
+ PacketAliasOut (packetBuf, IP_MAXPACKET);
+ }
+ else {
+/*
+ * Do aliasing.
+ */
+ status = PacketAliasIn (packetBuf, IP_MAXPACKET);
+ if (status == PKT_ALIAS_IGNORED &&
+ dropIgnoredIncoming) {
+
+ printf (" dropped.\n");
+ return;
+ }
+ }
+/*
+ * Length might have changed during aliasing.
+ */
+ bytes = ntohs (ip->ip_len);
+/*
+ * Update alias overhead size for outgoing packets.
+ */
+ if (packetAddr.sin_addr.s_addr == INADDR_ANY &&
+ bytes - origBytes > aliasOverhead)
+ aliasOverhead = bytes - origBytes;
+
+ if (verbose) {
+
+/*
+ * Print addresses after aliasing.
+ */
+ printf (" aliased to\n");
+ printf (" ");
+ PrintPacket (ip);
+ printf ("\n");
+ }
+
+ packetLen = bytes;
+ packetSock = fd;
+ FlushPacketBuffer (fd);
+}
+
+static void FlushPacketBuffer (int fd)
+{
+ int wrote;
+ char msgBuf[80];
+/*
+ * Put packet back for processing.
+ */
+ wrote = sendto (fd,
+ packetBuf,
+ packetLen,
+ 0,
+ (struct sockaddr*) &packetAddr,
+ sizeof packetAddr);
+
+ if (wrote != packetLen) {
+/*
+ * If buffer space is not available,
+ * just return. Main loop will take care of
+ * retrying send when space becomes available.
+ */
+ if (errno == ENOBUFS)
+ return;
+
+ if (errno == EMSGSIZE) {
+
+ if (packetAddr.sin_addr.s_addr == INADDR_ANY &&
+ ifMTU != -1)
+ SendNeedFragIcmp (icmpSock,
+ (struct ip*) packetBuf,
+ ifMTU - aliasOverhead);
+ }
+ else {
+
+ sprintf (msgBuf, "failed to write packet back");
+ Warn (msgBuf);
+ }
+ }
+
+ packetSock = -1;
+}
+
+static void HandleRoutingInfo (int fd)
+{
+ int bytes;
+ struct if_msghdr ifMsg;
+/*
+ * Get packet from socket.
+ */
+ bytes = read (fd, &ifMsg, sizeof ifMsg);
+ if (bytes == -1) {
+
+ Warn ("read from routing socket failed");
+ return;
+ }
+
+ if (ifMsg.ifm_version != RTM_VERSION) {
+
+ Warn ("unexpected packet read from routing socket");
+ return;
+ }
+
+ if (verbose)
+ printf ("Routing message %X received.\n", ifMsg.ifm_type);
+
+ if (ifMsg.ifm_type != RTM_NEWADDR)
+ return;
+
+ if (verbose && ifMsg.ifm_index == ifIndex)
+ printf ("Interface address has changed.\n");
+
+ if (ifMsg.ifm_index == ifIndex)
+ assignAliasAddr = 1;
+}
+
+static void PrintPacket (struct ip* ip)
+{
+ struct tcphdr* tcphdr;
+
+ if (ip->ip_p == IPPROTO_TCP)
+ tcphdr = (struct tcphdr*) ((char*) ip + (ip->ip_hl << 2));
+ else
+ tcphdr = NULL;
+
+ printf ("%s", inet_ntoa (ip->ip_src));
+ if (tcphdr)
+ printf (":%d", ntohs (tcphdr->th_sport));
+
+ printf (" -> ");
+ printf ("%s", inet_ntoa (ip->ip_dst));
+ if (tcphdr)
+ printf (":%d", ntohs (tcphdr->th_dport));
+}
+
+static void SetAliasAddressFromIfName (char* ifName)
+{
+ struct ifconf cf;
+ struct ifreq buf[32];
+ char msg[80];
+ struct ifreq* ifPtr;
+ int extra;
+ int helperSock;
+ int bytes;
+ struct sockaddr_in* addr;
+ int found;
+ struct ifreq req;
+ char last[10];
+/*
+ * Create a dummy socket to access interface information.
+ */
+ helperSock = socket (AF_INET, SOCK_DGRAM, 0);
+ if (helperSock == -1) {
+
+ Quit ("Failed to create helper socket.");
+ exit (1);
+ }
+
+ cf.ifc_len = sizeof (buf);
+ cf.ifc_req = buf;
+/*
+ * Get interface data.
+ */
+ if (ioctl (helperSock, SIOCGIFCONF, &cf) == -1) {
+
+ Quit ("Ioctl SIOCGIFCONF failed.");
+ exit (1);
+ }
+
+ ifIndex = 0;
+ ifPtr = buf;
+ bytes = cf.ifc_len;
+ found = 0;
+ last[0] = '\0';
+/*
+ * Loop through interfaces until one with
+ * given name is found. This is done to
+ * find correct interface index for routing
+ * message processing.
+ */
+ while (bytes) {
+
+ if (ifPtr->ifr_addr.sa_family == AF_INET &&
+ !strcmp (ifPtr->ifr_name, ifName)) {
+
+ found = 1;
+ break;
+ }
+
+ if (strcmp (last, ifPtr->ifr_name)) {
+
+ strcpy (last, ifPtr->ifr_name);
+ ++ifIndex;
+ }
+
+ extra = ifPtr->ifr_addr.sa_len - sizeof (struct sockaddr);
+
+ ifPtr++;
+ ifPtr = (struct ifreq*) ((char*) ifPtr + extra);
+ bytes -= sizeof (struct ifreq) + extra;
+ }
+
+ if (!found) {
+
+ close (helperSock);
+ sprintf (msg, "Unknown interface name %s.\n", ifName);
+ Quit (msg);
+ }
+/*
+ * Get MTU size.
+ */
+ strcpy (req.ifr_name, ifName);
+
+ if (ioctl (helperSock, SIOCGIFMTU, &req) == -1)
+ Quit ("Cannot get interface mtu size.");
+
+ ifMTU = req.ifr_mtu;
+/*
+ * Get interface address.
+ */
+ if (ioctl (helperSock, SIOCGIFADDR, &req) == -1)
+ Quit ("Cannot get interface address.");
+
+ addr = (struct sockaddr_in*) &req.ifr_addr;
+ SetPacketAliasAddress (addr->sin_addr);
+ syslog (LOG_INFO, "Aliasing to %s, mtu %d bytes",
+ inet_ntoa (addr->sin_addr),
+ ifMTU);
+
+ close (helperSock);
+}
+
+void Quit (char* msg)
+{
+ Warn (msg);
+ exit (1);
+}
+
+void Warn (char* msg)
+{
+ if (background)
+ syslog (LOG_ALERT, "%s (%m)", msg);
+ else
+ warn (msg);
+}
+
+static void RefreshAddr ()
+{
+ signal (SIGHUP, RefreshAddr);
+ if (ifName)
+ assignAliasAddr = 1;
+}
+
+static void InitiateShutdown ()
+{
+/*
+ * Start timer to allow kernel gracefully
+ * shutdown existing connections when system
+ * is shut down.
+ */
+ signal (SIGALRM, Shutdown);
+ alarm (10);
+}
+
+static void Shutdown ()
+{
+ running = 0;
+}
+
+/*
+ * Different options recognized by this program.
+ */
+
+enum Option {
+
+ PacketAliasOption,
+ Verbose,
+ InPort,
+ OutPort,
+ Port,
+ AliasAddress,
+ InterfaceName,
+ PermanentLink,
+ RedirectPort,
+ RedirectAddress,
+ ConfigFile,
+ DynamicMode
+};
+
+enum Param {
+
+ YesNo,
+ Numeric,
+ String,
+ None,
+ Address,
+ Service
+};
+
+/*
+ * Option information structure (used by ParseOption).
+ */
+
+struct OptionInfo {
+
+ enum Option type;
+ int packetAliasOpt;
+ enum Param parm;
+ char* parmDescription;
+ char* description;
+ char* name;
+ char* shortName;
+};
+
+/*
+ * Table of known options.
+ */
+
+static struct OptionInfo optionTable[] = {
+
+ { PacketAliasOption,
+ PKT_ALIAS_UNREGISTERED_ONLY,
+ YesNo,
+ "[yes|no]",
+ "alias only unregistered addresses",
+ "unregistered_only",
+ "u" },
+
+ { PacketAliasOption,
+ PKT_ALIAS_LOG,
+ YesNo,
+ "[yes|no]",
+ "enable logging",
+ "log",
+ "l" },
+
+ { PacketAliasOption,
+ PKT_ALIAS_DENY_INCOMING,
+ YesNo,
+ "[yes|no]",
+ "allow incoming connections",
+ "deny_incoming",
+ "d" },
+
+ { PacketAliasOption,
+ PKT_ALIAS_USE_SOCKETS,
+ YesNo,
+ "[yes|no]",
+ "use sockets to inhibit port conflict",
+ "use_sockets",
+ "s" },
+
+ { PacketAliasOption,
+ PKT_ALIAS_SAME_PORTS,
+ YesNo,
+ "[yes|no]",
+ "try to keep original port numbers for connections",
+ "same_ports",
+ "m" },
+
+ { Verbose,
+ 0,
+ YesNo,
+ "[yes|no]",
+ "verbose mode, dump packet information",
+ "verbose",
+ "v" },
+
+ { DynamicMode,
+ 0,
+ YesNo,
+ "[yes|no]",
+ "dynamic mode, automatically detect interface address changes",
+ "dynamic",
+ NULL },
+
+ { InPort,
+ 0,
+ Service,
+ "number|service_name",
+ "set port for incoming packets",
+ "in_port",
+ "i" },
+
+ { OutPort,
+ 0,
+ Service,
+ "number|service_name",
+ "set port for outgoing packets",
+ "out_port",
+ "o" },
+
+ { Port,
+ 0,
+ Service,
+ "number|service_name",
+ "set port (defaults to natd/divert)",
+ "port",
+ "p" },
+
+ { AliasAddress,
+ 0,
+ Address,
+ "x.x.x.x",
+ "address to use for aliasing",
+ "alias_address",
+ "a" },
+
+ { InterfaceName,
+ 0,
+ String,
+ "network_if_name",
+ "take aliasing address from interface",
+ "interface",
+ "n" },
+
+ { PermanentLink,
+ 0,
+ String,
+ "tcp|udp src:port dst:port alias",
+ "define permanent link for incoming connection",
+ "permanent_link",
+ NULL },
+
+ { RedirectPort,
+ 0,
+ String,
+ "tcp|udp local_addr:local_port [public_addr:]public_port"
+ " [remote_addr[:remote_port]]",
+ "redirect a port for incoming traffic",
+ "redirect_port",
+ NULL },
+
+ { RedirectAddress,
+ 0,
+ String,
+ "local_addr public_addr",
+ "define mapping between local and public addresses",
+ "redirect_address",
+ NULL },
+
+ { ConfigFile,
+ 0,
+ String,
+ "file_name",
+ "read options from configuration file",
+ "config",
+ "f" }
+};
+
+static void ParseOption (char* option, char* parms, int cmdLine)
+{
+ int i;
+ struct OptionInfo* info;
+ int yesNoValue;
+ int aliasValue;
+ int numValue;
+ char* strValue;
+ struct in_addr addrValue;
+ int max;
+ char* end;
+/*
+ * Find option from table.
+ */
+ max = sizeof (optionTable) / sizeof (struct OptionInfo);
+ for (i = 0, info = optionTable; i < max; i++, info++) {
+
+ if (!strcmp (info->name, option))
+ break;
+
+ if (info->shortName)
+ if (!strcmp (info->shortName, option))
+ break;
+ }
+
+ if (i >= max) {
+
+ warnx ("unknown option %s", option);
+ Usage ();
+ }
+
+ yesNoValue = 0;
+ numValue = 0;
+ strValue = NULL;
+/*
+ * Check parameters.
+ */
+ switch (info->parm) {
+ case YesNo:
+ if (!parms)
+ parms = "yes";
+
+ if (!strcmp (parms, "yes"))
+ yesNoValue = 1;
+ else
+ if (!strcmp (parms, "no"))
+ yesNoValue = 0;
+ else
+ errx(1, "%s needs yes/no parameter", option);
+ break;
+
+ case Service:
+ if (!parms)
+ errx(1,
+ "%s needs service name or port number parameter", option);
+
+ numValue = StrToPort (parms, "divert");
+ break;
+
+ case Numeric:
+ if (parms)
+ numValue = strtol (parms, &end, 10);
+ else
+ end = parms;
+
+ if (end == parms)
+ errx(1, "%s needs numeric parameter", option);
+ break;
+
+ case String:
+ strValue = parms;
+ if (!strValue)
+ errx(1, "%s needs parameter", option);
+ break;
+
+ case None:
+ if (parms)
+ errx(1, "%s does not take parameters", option);
+ break;
+
+ case Address:
+ if (!parms)
+ errx(1, "%s needs address/host parameter", option);
+
+ StrToAddr (parms, &addrValue);
+ break;
+ }
+
+ switch (info->type) {
+ case PacketAliasOption:
+
+ aliasValue = yesNoValue ? info->packetAliasOpt : 0;
+ PacketAliasSetMode (aliasValue, info->packetAliasOpt);
+ break;
+
+ case Verbose:
+ verbose = yesNoValue;
+ break;
+
+ case DynamicMode:
+ dynamicMode = yesNoValue;
+ break;
+
+ case InPort:
+ inPort = numValue;
+ break;
+
+ case OutPort:
+ outPort = numValue;
+ break;
+
+ case Port:
+ inOutPort = numValue;
+ break;
+
+ case AliasAddress:
+ memcpy (&aliasAddr, &addrValue, sizeof (struct in_addr));
+ break;
+
+ case PermanentLink:
+ SetupPermanentLink (strValue);
+ break;
+
+ case RedirectPort:
+ SetupPortRedirect (strValue);
+ break;
+
+ case RedirectAddress:
+ SetupAddressRedirect (strValue);
+ break;
+
+ case InterfaceName:
+ if (ifName)
+ free (ifName);
+
+ ifName = strdup (strValue);
+ assignAliasAddr = 1;
+ break;
+
+ case ConfigFile:
+ ReadConfigFile (strValue);
+ break;
+ }
+}
+
+void ReadConfigFile (char* fileName)
+{
+ FILE* file;
+ char buf[128];
+ char* ptr;
+ char* option;
+
+ file = fopen (fileName, "r");
+ if (!file) {
+
+ sprintf (buf, "Cannot open config file %s.\n", fileName);
+ Quit (buf);
+ }
+
+ while (fgets (buf, sizeof (buf), file)) {
+
+ ptr = strchr (buf, '\n');
+ if (!ptr)
+ errx(1, "config line too link: %s", buf);
+
+ *ptr = '\0';
+ if (buf[0] == '#')
+ continue;
+
+ ptr = buf;
+/*
+ * Skip white space at beginning of line.
+ */
+ while (*ptr && isspace (*ptr))
+ ++ptr;
+
+ if (*ptr == '\0')
+ continue;
+/*
+ * Extract option name.
+ */
+ option = ptr;
+ while (*ptr && !isspace (*ptr))
+ ++ptr;
+
+ if (*ptr != '\0') {
+
+ *ptr = '\0';
+ ++ptr;
+ }
+/*
+ * Skip white space between name and parms.
+ */
+ while (*ptr && isspace (*ptr))
+ ++ptr;
+
+ ParseOption (option, *ptr ? ptr : NULL, 0);
+ }
+
+ fclose (file);
+}
+
+static void Usage ()
+{
+ int i;
+ int max;
+ struct OptionInfo* info;
+
+ fprintf (stderr, "Recognized options:\n\n");
+
+ max = sizeof (optionTable) / sizeof (struct OptionInfo);
+ for (i = 0, info = optionTable; i < max; i++, info++) {
+
+ fprintf (stderr, "-%-20s %s\n", info->name,
+ info->parmDescription);
+
+ if (info->shortName)
+ fprintf (stderr, "-%-20s %s\n", info->shortName,
+ info->parmDescription);
+
+ fprintf (stderr, " %s\n\n", info->description);
+ }
+
+ exit (1);
+}
+
+void SetupPermanentLink (char* parms)
+{
+ char buf[128];
+ char* ptr;
+ struct in_addr srcAddr;
+ struct in_addr dstAddr;
+ int srcPort;
+ int dstPort;
+ int aliasPort;
+ int proto;
+ char* protoName;
+
+ strcpy (buf, parms);
+/*
+ * Extract protocol.
+ */
+ protoName = strtok (buf, " \t");
+ if (!protoName)
+ errx(1, "permanent_link: missing protocol");
+
+ proto = StrToProto (protoName);
+/*
+ * Extract source address.
+ */
+ ptr = strtok (NULL, " \t");
+ if (!ptr)
+ errx(1, "permanent_link: missing src address");
+
+ srcPort = StrToAddrAndPort (ptr, &srcAddr, protoName);
+/*
+ * Extract destination address.
+ */
+ ptr = strtok (NULL, " \t");
+ if (!ptr)
+ errx(1, "permanent_link: missing dst address");
+
+ dstPort = StrToAddrAndPort (ptr, &dstAddr, protoName);
+/*
+ * Export alias port.
+ */
+ ptr = strtok (NULL, " \t");
+ if (!ptr)
+ errx(1, "permanent_link: missing alias port");
+
+ aliasPort = StrToPort (ptr, protoName);
+
+ PacketAliasPermanentLink (srcAddr,
+ srcPort,
+ dstAddr,
+ dstPort,
+ aliasPort,
+ proto);
+}
+
+void SetupPortRedirect (char* parms)
+{
+ char buf[128];
+ char* ptr;
+ struct in_addr localAddr;
+ struct in_addr publicAddr;
+ struct in_addr remoteAddr;
+ int localPort;
+ int publicPort;
+ int remotePort;
+ int proto;
+ char* protoName;
+ char* separator;
+
+ strcpy (buf, parms);
+/*
+ * Extract protocol.
+ */
+ protoName = strtok (buf, " \t");
+ if (!protoName)
+ errx(1, "redirect_port: missing protocol");
+
+ proto = StrToProto (protoName);
+/*
+ * Extract local address.
+ */
+ ptr = strtok (NULL, " \t");
+ if (!ptr)
+ errx(1, "redirect_port: missing local address");
+
+ localPort = StrToAddrAndPort (ptr, &localAddr, protoName);
+/*
+ * Extract public port and optinally address.
+ */
+ ptr = strtok (NULL, " \t");
+ if (!ptr)
+ errx(1, "redirect_port: missing public port");
+
+ separator = strchr (ptr, ':');
+ if (separator)
+ publicPort = StrToAddrAndPort (ptr, &publicAddr, protoName);
+ else {
+
+ publicAddr.s_addr = INADDR_ANY;
+ publicPort = StrToPort (ptr, protoName);
+ }
+
+/*
+ * Extract remote address and optionally port.
+ */
+ ptr = strtok (NULL, " \t");
+ if (ptr) {
+
+
+ separator = strchr (ptr, ':');
+ if (separator)
+ remotePort = StrToAddrAndPort (ptr,
+ &remoteAddr,
+ protoName);
+ else {
+
+ remotePort = 0;
+ StrToAddr (ptr, &remoteAddr);
+ }
+ }
+ else {
+
+ remotePort = 0;
+ remoteAddr.s_addr = INADDR_ANY;
+ }
+
+ PacketAliasRedirectPort (localAddr,
+ localPort,
+ remoteAddr,
+ remotePort,
+ publicAddr,
+ publicPort,
+ proto);
+}
+
+void SetupAddressRedirect (char* parms)
+{
+ char buf[128];
+ char* ptr;
+ struct in_addr localAddr;
+ struct in_addr publicAddr;
+
+ strcpy (buf, parms);
+/*
+ * Extract local address.
+ */
+ ptr = strtok (buf, " \t");
+ if (!ptr)
+ errx(1, "redirect_address: missing local address");
+
+ StrToAddr (ptr, &localAddr);
+/*
+ * Extract public address.
+ */
+ ptr = strtok (NULL, " \t");
+ if (!ptr)
+ errx(1, "redirect_address: missing public address");
+
+ StrToAddr (ptr, &publicAddr);
+ PacketAliasRedirectAddr (localAddr, publicAddr);
+}
+
+void StrToAddr (char* str, struct in_addr* addr)
+{
+ struct hostent* hp;
+
+ if (inet_aton (str, addr))
+ return;
+
+ hp = gethostbyname (str);
+ if (!hp)
+ errx(1, "unknown host %s", str);
+
+ memcpy (addr, hp->h_addr, sizeof (struct in_addr));
+}
+
+int StrToPort (char* str, char* proto)
+{
+ int port;
+ struct servent* sp;
+ char* end;
+
+ port = strtol (str, &end, 10);
+ if (end != str)
+ return htons (port);
+
+ sp = getservbyname (str, proto);
+ if (!sp)
+ errx(1, "unknown service %s/%s", str, proto);
+
+ return sp->s_port;
+}
+
+int StrToProto (char* str)
+{
+ if (!strcmp (str, "tcp"))
+ return IPPROTO_TCP;
+
+ if (!strcmp (str, "udp"))
+ return IPPROTO_UDP;
+
+ errx(1, "unknown protocol %s. Expected tcp or udp", str);
+}
+
+int StrToAddrAndPort (char* str, struct in_addr* addr, char* proto)
+{
+ char* ptr;
+
+ ptr = strchr (str, ':');
+ if (!ptr)
+ errx(1, "%s is missing port number", str);
+
+ *ptr = '\0';
+ ++ptr;
+
+ StrToAddr (str, addr);
+ return StrToPort (ptr, proto);
+}
+
diff --git a/usr.sbin/natd/natd.h b/usr.sbin/natd/natd.h
new file mode 100644
index 0000000..d398241
--- /dev/null
+++ b/usr.sbin/natd/natd.h
@@ -0,0 +1,5 @@
+#define PIDFILE "/var/run/natd.pid"
+
+extern void Quit (char* msg);
+extern void Warn (char* msg);
+extern int SendNeedFragIcmp (int sock, struct ip* failedDgram, int mtu);
diff --git a/usr.sbin/natd/samples/natd.cf.sample b/usr.sbin/natd/samples/natd.cf.sample
new file mode 100644
index 0000000..46eee91
--- /dev/null
+++ b/usr.sbin/natd/samples/natd.cf.sample
@@ -0,0 +1,52 @@
+#
+# Configuration file for natd.
+#
+#
+# Logging to /var/log
+#
+log no
+#
+# Incoming connections.
+#
+deny_incoming no
+#
+# Use sockets to avoid port clashes.
+#
+use_sockets no
+#
+# Avoid port changes if possible. Makes rlogin work
+# in most cases.
+#
+same_port yes
+#
+# Verbose mode. Enables dumping of packets and disables
+# forking to background.
+#
+verbose no
+#
+# Divert port. Can be a name in /etc/services or numeric value.
+#
+port 32000
+#
+# Interface name or address being aliased. Either one,
+# not both is required.
+#
+# alias_address 192.168.0.1
+interface ep0
+#
+# Alias unregistered addresses or all addresses.
+#
+unregistered_only no
+#
+# Configure permanent links. If you use host names instead
+# of addresses here, be sure that name server works BEFORE
+# natd is up - this is usually not the case. So either use
+# numeric addresses or hosts that are in /etc/hosts.
+#
+# Map connections coming to port 30000 to telnet in my_private_host.
+# Remember to allow the connection /etc/rc.firewall also.
+#permanent_link tcp my_private_host:telnet 0.0.0.0:0 30000
+#
+# Map connections coming from host.xyz.com to port 30001 to
+# telnet in another_host.
+#permanent_link tcp another_host:telnet host.xyz.com:0 30001
diff --git a/usr.sbin/natd/samples/natd.test b/usr.sbin/natd/samples/natd.test
new file mode 100644
index 0000000..cfdbd15
--- /dev/null
+++ b/usr.sbin/natd/samples/natd.test
@@ -0,0 +1,14 @@
+#!/bin/sh
+
+ if [ $# != 1 ]
+ then
+ echo "usage: natd.test ifname"
+ exit 1
+ fi
+
+ ipfw flush
+ ipfw add divert 32000 ip from any to any via $1
+ ipfw add pass ip from any to any
+
+ ./natd -port 32000 -interface $1 -verbose
+
diff --git a/usr.sbin/ncrcontrol/Makefile b/usr.sbin/ncrcontrol/Makefile
new file mode 100644
index 0000000..487b2b0
--- /dev/null
+++ b/usr.sbin/ncrcontrol/Makefile
@@ -0,0 +1,11 @@
+PROG= ncrcontrol
+SRCS= ncrcontrol.c
+MAN8= ncrcontrol.8
+
+CFLAGS+= -I${.CURDIR}/../../sys
+LDADD+= -lkvm
+DPADD+= ${LIBKVM}
+BINGRP= kmem
+BINMODE= 2555
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/ncrcontrol/ncrcontrol.8 b/usr.sbin/ncrcontrol/ncrcontrol.8
new file mode 100644
index 0000000..e8a8484
--- /dev/null
+++ b/usr.sbin/ncrcontrol/ncrcontrol.8
@@ -0,0 +1,290 @@
+.\" Copyright (c) 1994 Wolfgang Stanglmeier. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. The name of the author may not be used to endorse or promote products
+.\" derived from this software without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+.\"
+.\" $Id: ncrcontrol.8,v 1.6 1997/02/22 16:08:14 peter Exp $
+.\"
+.Dd September 16, 1994
+.Dt NCRCONTROL 8
+.Os FreeBSD
+.Sh NAME
+.Nm ncrcontrol
+.Nd manually configure the ncr810/825 PCI/SCSI device driver
+.Sh SYNOPSIS
+.\"--------------------------------------------------------
+.\" ncrcontrol [-Mcore] [-Nsystem] [-u unit] [-v] [-v] -i
+.\"--------------------------------------------------------
+.Nm ncrcontrol
+.Op Fl M Ar core
+.Op Fl N Ar system
+.Op Fl u Ar unit
+.Op Fl v
+.Op Fl v
+.Fl i
+.\"--------------------------------------------------------
+.\" ncrcontrol [-N system] [-u unit] [-p wait]
+.\"--------------------------------------------------------
+.Nm ncrcontrol
+.Op Fl N Ar system
+.Op Fl u Ar unit
+.Op Fl p Ar wait
+.\"------------------------------------------------------------
+.\" ncrcontrol [-Nsystem] [-u unit] [-t target] -s name=value
+.\"------------------------------------------------------------
+.Nm ncrcontrol
+.Op Fl N Ar system
+.Op Fl u Ar unit
+.Op Fl t Ar target
+.Fl s
+.Ar name=value
+.\"------------------------------------------------------------
+.\" ncrcontrol [-Mcore] [-Nsystem] [-u unit] [-t target] -d pattern
+.\"------------------------------------------------------------
+.Nm ncrcontrol
+.Op Fl M Ar core
+.Op Fl N Ar system
+.Op Fl u Ar unit
+.Op Fl t Ar target
+.Fl d
+.Ar debug
+.\"------------------------------------------------------------
+.\" ncrcontrol [-Nsystem] [-u unit] -w -k torture
+.\"------------------------------------------------------------
+.Nm ncrcontrol
+.Op Fl N Ar system
+.Op Fl u Ar unit
+.Fl w
+.Fl k
+.Ar torture
+.\"
+.\"=====================================================================
+.\"
+.Sh DESCRIPTION
+The
+.Nm
+command symbolically displays the contents of the ncr810/ncr825
+pci/scsi driver control structure.
+There are a number of output formats,
+depending on the options for the information presented.
+It may be used to set some values, too.
+
+.\"--------------------------------------------------------
+.\" ncrcontrol [-Mcore] [-Nsystem] [-u unit] [-v] [-v] -i
+.\"--------------------------------------------------------
+The first form of the command displays an overview of the
+connected targets. The
+.Fl v
+requests a verbose output.
+
+.\"--------------------------------------------------------
+.\" ncrcontrol [-N system] [-u unit] [-p wait]
+.\"--------------------------------------------------------
+Using the second form, with a
+.Ar wait
+interval specified,
+.Nm
+will continuously display the information regarding data
+traffic on the selected scsi bus.
+
+.\"------------------------------------------------------------
+.\" ncrcontrol [-Nsystem] [-u unit] [-t target] -s name=value
+.\"------------------------------------------------------------
+The third form sets a configuration value for one or all
+targets of a selected scsi bus.
+It normally is not needed, if the device driver options are
+correctly set in kernel configuration.
+
+.\"------------------------------------------------------------
+.\" ncrcontrol [-M core] [-N system] [-u unit] [-t target] -w -d pattern
+.\"------------------------------------------------------------
+The forth form presents the content of the drivers control
+structure. By the
+.Ar pattern
+certain parts of the output can be enabled/disabled.
+It's only used to debug the driver.
+
+.\"------------------------------------------------------------
+.\" ncrcontrol [-N system] [-u unit] -w -k torture
+.\"------------------------------------------------------------
+With the last form certain driver problems can be forced.
+THIS MAY CRASH YOUR SYSTEM.
+It's only used to debug the driver.
+
+.\"
+.\"=====================================================================
+.\"
+.Pp
+The options have the following meaning:
+.Bl -tag -width flag
+.\"------------------------------------------------------------
+.\" -d pattern
+.\"------------------------------------------------------------
+.It Fl d
+Show the content of the drivers control structure.
+The
+.Ar pattern
+selects the fields, which are displayed. With
+.Fl d?
+you can get a list of valid flags.
+
+.\"------------------------------------------------------------
+.\" -i
+.\"------------------------------------------------------------
+.It Fl i
+Show the configuration state of the connected targets.
+With the
+.Fl v
+option you can request a more verbose information.
+
+.\"------------------------------------------------------------
+.\" -k torture
+.\"------------------------------------------------------------
+.It Fl k
+Force certain driver problems to check the error recovery
+functions. This option should only be used by the developers.
+With
+.Fl k?
+you can get a list of possible tortures.
+This option requires the
+.Fl w
+option to be set.
+
+.\"------------------------------------------------------------
+.\" -M core
+.\"------------------------------------------------------------
+.It Fl M
+Extract values associated with the name list from the specified core
+instead of the default
+.Pa /dev/kmem .
+.\"------------------------------------------------------------
+.\" -N system
+.\"------------------------------------------------------------
+.It Fl N
+Extract the name list from the specified system instead of the default
+.Pa /kernel .
+
+.\"------------------------------------------------------------
+.\" -p wait
+.\"------------------------------------------------------------
+When
+.Nm
+is invoked with a
+.Ar wait
+interval argument, it displays a running count of statistics related to
+the transfers on the selected scsi bus.
+
+This display consists of the number of transfers and number of transferred
+bytes for the controller and for each connected target.
+
+(NOT YET COMPLETE)
+
+.\"------------------------------------------------------------
+.\" -s field=value
+.\"------------------------------------------------------------
+.It Fl s
+Set a configuration value for one or more targets.
+One ore more targets can be selected by the
+.Fl t
+option. If no target is selected, then the field is set
+for all connect targets.
+
+(NOT YET COMPLETE)
+
+.\"------------------------------------------------------------
+.\" -t target
+.\"------------------------------------------------------------
+.It Fl t
+Select the target for the
+.Fl s
+option. This option may be given more than one time.
+
+.\"------------------------------------------------------------
+.\" -u unit
+.\"------------------------------------------------------------
+.It Fl u
+Select the controller unit.
+If this option is not given, then controller 0 is assumed.
+Most systems have only one controller.
+
+.\"------------------------------------------------------------
+.\" -v
+.\"------------------------------------------------------------
+.It Fl v
+Enable verbose output for the
+.Fl i
+option.
+
+.\"------------------------------------------------------------
+.\" -w
+.\"------------------------------------------------------------
+.It Fl w
+Wizard option.
+Enable the usage of the
+.Fl k
+option.
+
+.\"
+.\"=====================================================================
+.\"
+.Pp
+(NOT YET COMPLETE)
+
+.\"
+.\"=====================================================================
+.\"
+.Sh SEE ALSO
+.Xr scsi 4 ,
+.Xr scsi 8
+
+.\"
+.\"=====================================================================
+.\"
+.Sh HISTORY
+The
+.Nm
+command was hacked for
+.Bx 386
+and ported to version 1.1 of
+.Bx Free
+by
+.An Wolfgang Stanglmeier Aq wolf@dentaro.GUN.de .
+It was ported to
+.Bx Net
+by
+.An Charles M. Hannum Aq mycroft@gnu.ai.mit.edu
+and to version 2.0 of
+.Bx Free
+by
+.An Stefan Esser Aq se@zpr.Uni-Koeln.DE .
+
+.\"
+.\"=====================================================================
+.\"
+.Sh BUGS
+The
+.Nm
+was hacked as a debugging tool for the ncr810 driver.
+It should be deleted sometimes.
+There should be a general tool to change device driver options
+on the fly.
+The statistical data should be gathered by the generic scsi driver.
diff --git a/usr.sbin/ncrcontrol/ncrcontrol.c b/usr.sbin/ncrcontrol/ncrcontrol.c
new file mode 100644
index 0000000..e2ee461
--- /dev/null
+++ b/usr.sbin/ncrcontrol/ncrcontrol.c
@@ -0,0 +1,1552 @@
+/**************************************************************************
+**
+** $Id: ncrcontrol.c,v 1.18 1997/07/28 21:33:45 se Exp $
+**
+** Utility for NCR 53C810 device driver.
+**
+** 386bsd / FreeBSD / NetBSD
+**
+**-------------------------------------------------------------------------
+**
+** Written for 386bsd and FreeBSD by
+** wolf@dentaro.gun.de Wolfgang Stanglmeier
+** se@mi.Uni-Koeln.de Stefan Esser
+**
+** Ported to NetBSD by
+** mycroft@gnu.ai.mit.edu
+**
+**-------------------------------------------------------------------------
+**
+** Copyright (c) 1994 Wolfgang Stanglmeier. All rights reserved.
+**
+** Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions
+** are met:
+** 1. Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** 2. Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in the
+** documentation and/or other materials provided with the distribution.
+** 3. The name of the author may not be used to endorse or promote products
+** derived from this software without specific prior written permission.
+**
+** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+**
+**
+***************************************************************************
+*/
+
+#include <sys/file.h>
+#include <sys/types.h>
+#ifdef __NetBSD__
+#include <sys/device.h>
+#endif
+#include <err.h>
+#include <errno.h>
+#include <kvm.h>
+#include <limits.h>
+#include <nlist.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <pci/ncr.c>
+
+/*
+** used external functions
+*/
+
+#if defined(__NetBSD__) || (__FreeBSD__ >= 2)
+kvm_t *kvm;
+#define KVM_NLIST(n) (kvm_nlist(kvm, (n)) >= 0)
+#define KVM_READ(o, p, l) (kvm_read(kvm, (o), (void*)(p), (l)) == (l))
+#else
+#define KVM_NLIST(n) (kvm_nlist((n)) >= 0)
+#define KVM_READ(o, p, l) (kvm_read((void*)(o), (p), (l)) == (l))
+#endif
+
+
+/*===========================================================
+**
+** Global variables.
+**
+**===========================================================
+*/
+
+u_long verbose;
+u_long wizard;
+
+
+
+struct nlist nl[] = {
+#define N_NCR_VERSION 0
+ { "_ncr_version" },
+#ifdef __NetBSD__
+#define N_NCRCD 1
+ { "_ncrcd" },
+#else
+#define N_NCRP 1
+ { "_ncrp" },
+#define N_NNCR 2
+ { "_nncr" },
+#endif
+ { 0 }
+};
+
+
+const char *vmunix = NULL;
+char *kmemf = NULL;
+
+int kvm_isopen;
+
+u_long ncr_base;
+u_long lcb_base;
+u_long ccb_base;
+
+u_long ncr_unit;
+#ifdef __NetBSD__
+struct cfdriver ncrcd;
+#else
+u_long ncr_units;
+#endif
+
+struct ncb ncr;
+struct lcb lcb;
+struct ccb ccb;
+
+u_long target_mask;
+u_long global_lun_mask;
+u_long lun_mask;
+u_long interval;
+
+static void usage __P((void));
+
+
+/*===========================================================
+**
+** Accessing kernel memory via kvm library.
+**
+**===========================================================
+*/
+
+void read_ccb(u_long base)
+{
+ ccb_base = base;
+ if (!KVM_READ (
+ base,
+ &ccb,
+ sizeof (struct ccb)))
+ errx(1, "bad kvm read at %x", base);
+}
+
+void read_lcb(u_long base)
+{
+ lcb_base = base;
+ if (!KVM_READ (
+ base,
+ &lcb,
+ sizeof (struct lcb)))
+ errx(1, "bad kvm read at %x", base);
+}
+
+void read_ncr()
+{
+ if (!KVM_READ (
+ ncr_base,
+ &ncr,
+ sizeof (ncr)))
+ errx(1, "bad kvm read at %x", ncr_base);
+}
+
+void open_kvm(int flags)
+{
+ int i;
+ u_long kernel_version;
+#if defined(__NetBSD__) || (__FreeBSD__ >= 2)
+ char errbuf[_POSIX2_LINE_MAX];
+#endif
+
+ if (kvm_isopen) return;
+
+#if defined(__NetBSD__) || (__FreeBSD__ >= 2)
+ /*
+ * Discard setgid privileges if not the running kernel so that bad
+ * guys can't print interesting stuff from kernel memory.
+ */
+ if (vmunix != NULL || kmemf != NULL)
+ setgid(getgid());
+ else {
+#if (__FreeBSD__ >= 2)
+ vmunix = getbootfile();
+#else
+ vmunix = _PATH_UNIX;
+#endif
+ }
+
+ kvm = kvm_openfiles(vmunix, kmemf, NULL, flags, errbuf);
+ if (kvm == NULL)
+ errx(1, "kvm_openfiles: %s", errbuf);
+#else
+ if (vmunix != NULL) {
+#if (__FreeBSD__ >= 2)
+ vmunix = getbootfile();
+#else
+ vmunix = _PATH_UNIX;
+#endif
+ }
+ if (kvm_openfiles(vmunix, kmemf, NULL) == -1)
+ errx(1, "kvm_openfiles: %s", kvm_geterr());
+#endif
+
+ if (!KVM_NLIST(nl))
+ errx(2, "no symbols in \"%s\"", vmunix);
+
+ for (i=0; nl[i].n_name; i++)
+ if (nl[i].n_type == 0)
+ errx(1, "no symbol \"%s\" in \"%s\"",
+ nl[i].n_name, vmunix);
+
+ if (!KVM_READ (
+ nl[N_NCR_VERSION].n_value,
+ &kernel_version,
+ sizeof (kernel_version)))
+ errx(1, "bad kvm read");
+
+ if (kernel_version != ncr_version)
+ errx(1, "incompatible with kernel. Rebuild!");
+
+#ifdef __NetBSD__
+
+ if (!KVM_READ (
+ nl[N_NCRCD].n_value,
+ &ncrcd,
+ sizeof (ncrcd)))
+ errx(1, "bad kvm read");
+
+ if (ncr_unit >= ncrcd.cd_ndevs)
+ errx(1, "bad unit number (valid range: 0-%d)",
+ ncrcd.cd_ndevs-1);
+
+ if (!KVM_READ (
+ ncrcd.cd_devs+4*ncr_unit,
+ &ncr_base,
+ sizeof (ncr_base)))
+ errx(1, "bad kvm read");
+
+ if (!ncr_base)
+ errx(1,
+ "control structure not allocated (not found in autoconfig?)");
+
+#else /* !__NetBSD__ */
+
+ if (!KVM_READ (
+ nl[N_NNCR].n_value,
+ &ncr_units,
+ sizeof (ncr_units)))
+ errx(1, "bad kvm read");
+
+ if (ncr_unit >= ncr_units)
+ errx(1, "bad unit number (valid range: 0-%d)",
+ ncr_units-1);
+
+ if (!KVM_READ (
+ nl[N_NCRP].n_value+4*ncr_unit,
+ &ncr_base,
+ sizeof (ncr_base))) {
+ errx(1, "bad kvm read");
+ };
+
+ if (!ncr_base)
+ errx(1,
+ "control structure not allocated (not found in autoconfig?)");
+
+#endif /* !__NetBSD__ */
+
+ read_ncr();
+
+ if (!ncr.vaddr)
+ errx(1, "53c810 not mapped (not found in autoconfig?)");
+
+ kvm_isopen = 1;
+}
+
+
+
+
+void set_target_mask(void)
+{
+ int t;
+ if (target_mask) return;
+ for (t=0; t<MAX_TARGET; t++)
+ if (ncr.target[t].jump_tcb.l_cmd) target_mask |= (1<<t);
+}
+
+void set_lun_mask(struct tcb * tp)
+{
+ int l;
+ lun_mask = global_lun_mask;
+ if (lun_mask) return;
+ for (l=0; l<MAX_LUN; l++)
+ if (tp->lp[l]) lun_mask |= (1<<l);
+}
+
+void printc (u_char*p, int l)
+{
+ for (;l>0;l--) {
+ char c=*p++;
+ printf ("%c", c?c:'_');
+ }
+}
+
+/*================================================================
+**
+**
+** system info
+**
+**
+**================================================================
+*/
+
+static double syncmhz(int negoval)
+{
+ switch (negoval) {
+ case 0:
+ return 0.0;
+ case 10:
+ return 40.0;
+ case 11:
+ return 33.0;
+ case 12:
+ return 20.0;
+ }
+ return 250.0 / negoval;
+}
+
+void do_info(void)
+{
+ int t,l,i,d,f,fl;
+ struct tcb * tip;
+ open_kvm(O_RDONLY);
+
+ if (verbose>=3)
+ printf ("ncr unit=%d data@%x register@%x (pci@%x)\n\n",
+ ncr_unit, ncr_base, ncr.vaddr, ncr.paddr);
+
+ set_target_mask();
+
+ printf ("T:L Vendor Device Rev Speed Max Wide Tags\n");
+ for (t=0; t<MAX_TARGET;t++) {
+ if (!((target_mask>>t)&1)) continue;
+ tip = &ncr.target[t];
+
+ set_lun_mask(tip);
+ if (!lun_mask) lun_mask=1;
+ fl=1;
+
+ for (l=0; l<MAX_LUN; l++) {
+ if (!((lun_mask>>l)&1)) continue;
+
+ printf ("%d:%d ", t, l);
+
+ if (!tip->jump_tcb.l_cmd) break;
+
+ if (fl) {
+ fl=0;
+ printc (&tip->inqdata[ 8], 8);printf(" ");
+ printc (&tip->inqdata[16],16);printf(" ");
+ printc (&tip->inqdata[32], 4);printf(" ");
+
+ if (tip->period==0xffff) {
+ printf ("asyn");
+ } else if (tip->period) {
+ printf ("%4.1f", 10000.0 / tip->period);
+ } else {
+ printf (" ?");
+ }
+
+ printf (" ");
+
+ if (tip->minsync==255) {
+ printf ("asyn");
+ } else if (tip->minsync) {
+ printf ("%4.1f", syncmhz(tip->minsync));
+ } else {
+ printf (" ?");
+ }
+ } else printf ("%42s", "");
+
+ if (!tip->lp[l]) {
+ printf (" no\n");
+ continue;
+ };
+ read_lcb ((u_long) tip->lp[l]);
+
+ switch (tip->widedone) {
+ case 1:
+ printf (" 8");
+ break;
+ case 2:
+ printf (" 16");
+ break;
+ case 3:
+ printf (" 32");
+ break;
+ default:
+ printf (" ?");
+ };
+
+ if (lcb.usetags)
+ printf ("%5d", lcb.actlink);
+ else
+ printf (" -");
+
+ printf ("\n");
+
+ };
+
+ if (!tip->jump_tcb.l_cmd) {
+ printf (" --- no target.\n");
+ continue;
+ };
+
+ if (verbose<1) continue;
+
+ for (i=0; i<8; i++) {
+ char* (class[10])={
+ "disk","tape","printer","processor",
+ "worm", "cdrom", "scanner", "optical disk",
+ "media changer", "communication device"};
+ d = tip->inqdata[i];
+ printf ("[%02x]: ",d);
+
+ switch (i) {
+
+ case 0:
+ f = d & 0x1f;
+ if (f<10) printf (class[f]);
+ else printf ("unknown (%x)", f);
+ break;
+ case 1:
+ f = (d>>7) & 1;
+ if (f) printf ("removable media");
+ else printf ("fixed media");
+ break;
+
+ case 2:
+ f = d & 7;
+ switch (f) {
+ case 0: printf ("SCSI-1");
+ break;
+ case 1: printf ("SCSI-1 with CCS");
+ break;
+ case 2: printf ("SCSI-2");
+ break;
+ default:
+ printf ("unknown ansi version (%d)",
+ f);
+ }
+ break;
+
+ case 3:
+ if (d&0xc0) printf ("capabilities:");
+ if (d&0x80) printf (" AEN");
+ if (d&0x40) printf (" TERMINATE-I/O");
+ break;
+
+ case 7:
+ if (d&0xfb) printf ("capabilities:");
+ if (d&0x80) printf (" relative");
+ if (d&0x40) printf (" wide32");
+ if (d&0x20) printf (" wide");
+ if (d&0x10) printf (" synch");
+ if (d&0x08) printf (" link");
+ if (d&0x02) printf (" tags");
+ if (d&0x01) printf (" soft-reset");
+ };
+ printf ("\n");
+ };
+ printf ("\n");
+ };
+ printf ("\n");
+}
+
+/*================================================================
+**
+**
+** profiling
+**
+**
+**================================================================
+*/
+
+void do_profile(void)
+{
+#define old backup.profile
+#define new ncr.profile
+
+ struct ncb backup;
+ struct profile diff;
+ int tra,line,t;
+
+ open_kvm(O_RDONLY);
+
+ set_target_mask();
+
+ if (interval<1) interval=1;
+ for (;;) {
+ /*
+ ** Header Line 1
+ */
+ printf (" total ");
+
+ for (t=0; t<MAX_TARGET; t++) {
+ if (!((target_mask>>t)&1)) continue;
+ printf (" ");
+ printc (&ncr.target[t].inqdata[16],8);
+ };
+
+ printf (" transf. disconn interru");
+
+ if (verbose>=1) printf (" ---- ms/transfer ----");
+
+ printf ("\n");
+
+ /*
+ ** Header Line 2
+ */
+
+ printf ("t/s kb/s ");
+
+ for (t=0; t<MAX_TARGET; t++) {
+ if (!((target_mask>>t)&1)) continue;
+ printf (" t/s kb/s");
+ };
+
+ printf (" length exp une fly brk");
+
+ if (verbose>=1) printf (" total pre post disc");
+
+ printf ("\n");
+
+ /*
+ ** Data
+ */
+
+ for(line=0;line<20;line++) {
+ backup = ncr;
+ read_ncr();
+ diff.num_trans = new.num_trans - old.num_trans;
+ diff.num_bytes = new.num_bytes - old.num_bytes;
+ diff.num_fly = new.num_fly - old.num_fly ;
+ diff.num_int = new.num_int - old.num_int ;
+ diff.ms_setup = new.ms_setup - old.ms_setup;
+ diff.ms_data = new.ms_data - old.ms_data;
+ diff.ms_disc = new.ms_disc - old.ms_disc;
+ diff.ms_post = new.ms_post - old.ms_post;
+ diff.num_disc = new.num_disc - old.num_disc;
+ diff.num_break = new.num_break - old.num_break;
+
+ tra = diff.num_trans;
+ if (!tra) tra=1;
+
+ printf ("%3.0f %4.0f ",
+ (1.0 * diff.num_trans) / interval,
+ (1.0 * diff.num_bytes) / (1024*interval));
+
+
+ for (t=0; t<MAX_TARGET; t++) {
+ if (!((target_mask>>t)&1)) continue;
+ printf (" %3.0f %4.0f",
+ ((ncr.target[t].transfers-
+ backup.target[t].transfers)*1.0)
+ /interval,
+ ((ncr.target[t].bytes-
+ backup.target[t].bytes)*1.0)
+ /(1024*interval));
+ };
+
+ printf ("%7.0f ", (diff.num_bytes*1.0) / tra);
+
+ printf (" %4.0f", (1.0*(diff.num_disc-diff.num_break))
+ /interval);
+
+ printf ("%4.0f", (1.0*diff.num_break)/interval);
+
+ printf ("%4.0f", (1.0*diff.num_fly) / interval);
+
+ printf ("%4.0f", (1.0*diff.num_int) / interval);
+
+ if (verbose >= 1) {
+ printf ("%7.1f",
+ (diff.ms_disc+diff.ms_data+diff.ms_setup+diff.ms_post)
+ * 1.0 / tra);
+
+ printf ("%5.1f%5.1f%6.1f",
+ 1.0 * diff.ms_setup / tra,
+ 1.0 * diff.ms_post / tra,
+ 1.0 * diff.ms_disc / tra);
+ };
+
+ printf ("\n");
+ fflush (stdout);
+ sleep (interval);
+ };
+ };
+}
+
+/*================================================================
+**
+**
+** Port access
+**
+**
+**================================================================
+*/
+
+static int kernelwritefile;
+static char* kernelwritefilename = _PATH_KMEM;
+
+void openkernelwritefile(void)
+{
+ if (kernelwritefile) return;
+
+ kernelwritefile = open (kernelwritefilename, O_WRONLY);
+ if (kernelwritefile<3)
+ err(1, "%s", kernelwritefilename);
+}
+
+void out (u_char reg, u_char val)
+{
+ u_long addr = ncr.vaddr + reg;
+ openkernelwritefile();
+ if (lseek (kernelwritefile, addr, 0) != addr)
+ err(1, "%s", kernelwritefilename);
+ if (write (kernelwritefile, &val, 1) < 0)
+ err(1, "%s", kernelwritefilename);
+}
+
+u_char in (u_char reg)
+{
+ u_char res;
+ if (!KVM_READ (
+ (ncr.vaddr + reg),
+ &res,
+ 1))
+ errx(1, "bad kvm read");
+ return (res);
+}
+
+/*================================================================
+**
+**
+** Setting of driver parameters
+**
+**
+**================================================================
+*/
+
+void do_set (char * arg)
+{
+ struct usrcmd user;
+ u_long addr;
+ int i;
+
+ if (!strcmp(arg, "?")) { printf (
+"async: disable synchronous transfers.\n"
+"sync=value: set the maximal synchronous transfer rate (MHz).\n"
+"fast: set FAST SCSI-2.\n"
+"\n"
+"wide=value: set the bus width (0=8bit 1=16bit).\n"
+"\n"
+"tags=value: use this number of tags.\n"
+"orderedtag: use ordered tags only.\n"
+"simpletag: use simple tags only.\n"
+"orderedwrite: use simple tags for read, else ordered tags.\n"
+"\n"
+"debug=value: set debug mode.\n"
+"\n");
+ return;
+ };
+
+ open_kvm(O_RDWR);
+ addr = ncr_base + offsetof (struct ncb, user);
+
+ for (i=3; i; i--) {
+ if (!KVM_READ (
+ (addr),
+ &user,
+ sizeof (user)))
+ errx(1, "bad kvm read");
+ if (!user.cmd) break;
+ sleep (1);
+ }
+ if (user.cmd)
+ errx(1, "ncb.user busy");
+
+ set_target_mask();
+
+ user.target = target_mask;
+ user.lun = lun_mask;
+ user.data = 0;
+ user.cmd = 0;
+
+
+ if (!strcmp(arg, "async")) {
+ user.data = 255;
+ user.cmd = UC_SETSYNC;
+ };
+
+ if (!strcmp(arg, "fast")) {
+ user.data = 25;
+ user.cmd = UC_SETSYNC;
+ };
+
+ if (!strncmp(arg, "sync=", 5)) {
+ double f = strtod (arg+5, NULL);
+ if (f>=4.0 && f<=10.0) {
+ user.data = 250.0 / f;
+ user.cmd = UC_SETSYNC;
+ };
+ };
+
+ if (!strncmp(arg, "wide=", 5)) {
+ u_char t = strtoul (arg+5, (char**)0, 0);
+ if (t<=1) {
+ user.data = t;
+ user.cmd = UC_SETWIDE;
+ };
+ };
+
+ if (!strncmp(arg, "tags=", 5)) {
+ u_char t = strtoul (arg+5, (char**)0, 0);
+ if (t<=MAX_TAGS) {
+ user.data = t;
+ user.cmd = UC_SETTAGS;
+ };
+ };
+
+ if (!strncmp(arg, "flags=", 6)) {
+ u_char t = strtoul (arg+6, (char**)0, 0);
+ if (t<=0xff) {
+ user.data = t;
+ user.cmd = UC_SETFLAG;
+ };
+ };
+
+ if (!strncmp(arg, "debug=", 6)) {
+ user.data = strtoul (arg+6, (char**)0, 0);
+ user.cmd = UC_SETDEBUG;
+ };
+
+ if (!strcmp(arg, "orderedtag")) {
+ user.data = M_ORDERED_TAG;
+ user.cmd = UC_SETORDER;
+ };
+
+ if (!strcmp(arg, "simpletag")) {
+ user.data = M_SIMPLE_TAG;
+ user.cmd = UC_SETORDER;
+ };
+
+ if (!strcmp(arg, "orderedwrite")) {
+ user.data = 0;
+ user.cmd = UC_SETORDER;
+ };
+
+ if (user.cmd) {
+ openkernelwritefile();
+
+ if (lseek (kernelwritefile, addr, 0) != addr)
+ err(1, "%s", kernelwritefilename);
+ if (write (kernelwritefile, &user, sizeof (user)) < 0)
+ errx(1, "%s", kernelwritefilename);
+
+ return;
+ };
+
+ warnx ("do_set \"%s\" not (yet) implemented", arg);
+}
+
+/*================================================================
+**
+**
+** D O _ K I L L
+**
+**
+**================================================================
+*/
+
+void do_kill(char * arg)
+{
+ open_kvm(O_RDWR);
+
+ if (!strcmp(arg, "?")) { printf (
+"scsireset: force SCSI bus reset.\n"
+"scriptabort: send an abort cmd to the script processor.\n"
+"scriptstart: start script processind (set SIGP bit).\n"
+"evenparity: force even parity.\n"
+"oddparity: force odd parity.\n"
+"noreselect: disable reselect (force timeouts).\n"
+"doreselect: enable reselect.\n"
+"\n");
+ return;
+ };
+
+ if (!wizard)
+ errx(2, "you are NOT a wizard!");
+
+ if (!strcmp(arg, "scsireset")) {
+ out (0x01, 0x08);
+ out (0x01, 0x00);
+ return;
+ };
+ if (!strcmp(arg, "scriptabort")) {
+ out (0x14, 0x80);
+ out (0x14, 0x20);
+ return;
+ };
+ if (!strcmp(arg, "scriptstart")) {
+ out (0x14, 0x20);
+ return;
+ };
+ if (!strcmp(arg, "evenparity")) {
+ out (0x01, 0x04);
+ return;
+ };
+ if (!strcmp(arg, "oddparity")) {
+ out (0x01, 0x00);
+ return;
+ };
+ if (!strcmp(arg, "noreselect")) {
+ out (0x04, in (0x04) & ~RRE);
+ return;
+ };
+ if (!strcmp(arg, "doreselect")) {
+ out (0x04, in (0x04) | RRE);
+ return;
+ };
+ warnx ("do_kill \"%s\" not (yet) implemented", arg);
+}
+
+/*================================================================
+**
+**
+** Write debug info: utilities: write symbolname.
+**
+**
+**================================================================
+*/
+
+static const char * sn (u_long a)
+{
+ static char buffer[100];
+
+ const char * s="";
+ u_long d,m;
+
+ a -= ncr.p_script;
+ m = sizeof (struct script);
+
+ if ((d=a-offsetof(struct script, start))<m) m=d, s="<start>";
+ if ((d=a-offsetof(struct script, start1))<m) m=d, s="<start1>";
+ if ((d=a-offsetof(struct script, startpos))<m) m=d, s="<startpos>";
+ if ((d=a-offsetof(struct scripth, tryloop))<m) m=d, s="<tryloop>";
+ if ((d=a-offsetof(struct script, trysel))<m) m=d, s="<trysel>";
+ if ((d=a-offsetof(struct script, skip))<m) m=d, s="<skip>";
+ if ((d=a-offsetof(struct script, skip2))<m) m=d, s="<skip2>";
+ if ((d=a-offsetof(struct script, idle))<m) m=d, s="<idle>";
+ if ((d=a-offsetof(struct script, select))<m) m=d, s="<select>";
+ if ((d=a-offsetof(struct script, prepare))<m) m=d, s="<prepare>";
+ if ((d=a-offsetof(struct script, loadpos))<m) m=d, s="<loadpos>";
+ if ((d=a-offsetof(struct script, prepare2))<m) m=d, s="<prepare2>";
+ if ((d=a-offsetof(struct script, setmsg))<m) m=d, s="<setmsg>";
+ if ((d=a-offsetof(struct script, clrack))<m) m=d, s="<clrack>";
+ if ((d=a-offsetof(struct script, dispatch))<m) m=d, s="<dispatch>";
+ if ((d=a-offsetof(struct script, checkatn))<m) m=d, s="<checkatn>";
+ if ((d=a-offsetof(struct script, command))<m) m=d, s="<command>";
+ if ((d=a-offsetof(struct script, status))<m) m=d, s="<status>";
+ if ((d=a-offsetof(struct script, msg_in))<m) m=d, s="<msg_in>";
+ if ((d=a-offsetof(struct script, msg_bad))<m) m=d, s="<msg_bad>";
+ if ((d=a-offsetof(struct scripth, msg_parity))<m) m=d, s="<msg_parity>";
+ if ((d=a-offsetof(struct scripth, msg_reject))<m) m=d, s="<msg_reject>";
+ if ((d=a-offsetof(struct scripth, msg_extended))<m) m=d, s="<msg_extended>";
+ if ((d=a-offsetof(struct scripth, msg_sdtr))<m) m=d, s="<msg_sdtr>";
+ if ((d=a-offsetof(struct script, complete))<m) m=d, s="<complete>";
+ if ((d=a-offsetof(struct script, cleanup))<m) m=d, s="<cleanup>";
+ if ((d=a-offsetof(struct script, cleanup0))<m) m=d, s="<cleanup>";
+ if ((d=a-offsetof(struct script, signal))<m) m=d, s="<signal>";
+ if ((d=a-offsetof(struct script, save_dp))<m) m=d, s="<save_dp>";
+ if ((d=a-offsetof(struct script, restore_dp))<m) m=d, s="<restore_dp>";
+ if ((d=a-offsetof(struct script, disconnect))<m) m=d, s="<disconnect>";
+ if ((d=a-offsetof(struct script, msg_out))<m) m=d, s="<msg_out>";
+ if ((d=a-offsetof(struct script, msg_out_done))<m) m=d, s="<msg_out_done>";
+ if ((d=a-offsetof(struct scripth, msg_out_abort))<m) m=d, s="<msg_out_abort>";
+ if ((d=a-offsetof(struct scripth, getcc))<m) m=d, s="<getcc>";
+ if ((d=a-offsetof(struct scripth, getcc1))<m) m=d, s="<getcc1>";
+ if ((d=a-offsetof(struct scripth, getcc2))<m) m=d, s="<getcc2>";
+ if ((d=a-offsetof(struct script, badgetcc))<m) m=d, s="<badgetcc>";
+ if ((d=a-offsetof(struct script, reselect))<m) m=d, s="<reselect>";
+ if ((d=a-offsetof(struct script, reselect2))<m) m=d, s="<reselect2>";
+ if ((d=a-offsetof(struct script, resel_tmp))<m) m=d, s="<resel_tmp>";
+ if ((d=a-offsetof(struct script, resel_lun))<m) m=d, s="<resel_lun>";
+ if ((d=a-offsetof(struct script, resel_tag))<m) m=d, s="<resel_tag>";
+ if ((d=a-offsetof(struct script, data_in))<m) m=d, s="<data_in>";
+ if ((d=a-offsetof(struct script, data_out))<m) m=d, s="<data_out>";
+ if ((d=a-offsetof(struct script, no_data))<m) m=d, s="<no_data>";
+ if ((d=a-offsetof(struct scripth, aborttag))<m) m=d, s="<aborttag>";
+ if ((d=a-offsetof(struct scripth, abort))<m) m=d, s="<abort>";
+
+ if (!*s) return s;
+
+ sprintf (buffer, "%s:%d%c", s, m/4, 0);
+ return (buffer);
+}
+
+/*================================================================
+**
+**
+** Write debug info: utilities: write misc. fields.
+**
+**
+**================================================================
+*/
+
+static void printm (u_char * msg, int len)
+{
+ u_char l;
+ do {
+ if (*msg==M_EXTENDED)
+ l=msg[1]+2;
+ else if ((*msg & 0xf0)==0x20)
+ l=2;
+ else l=1;
+ len-=l;
+
+ printf (" %x",*msg++);
+ while (--l>0) printf ("-%x",*msg++);
+ } while (len>0);
+}
+
+void dump_table (const char * str, struct scr_tblmove * p, int l)
+{
+ int i;
+ for (i=0;l>0;i++,p++,l--) if (p->size) {
+ printf (" %s[%d]: %5d @ 0x%08x\n",
+ str, i, p->size, p->addr);
+ };
+}
+
+void dump_link (const char* name, struct link * link)
+{
+ printf ("%s: cmd=%08x pa=%08x %s\n",
+ name, link->l_cmd, link->l_paddr, sn(link->l_paddr));
+}
+
+/*================================================================
+**
+**
+** Write debug info: utilities: write time fields.
+**
+**
+**================================================================
+*/
+
+void dump_tstamp (const char* name, struct tstamp * p)
+#define P(id,fld)\
+ if (p->fld.tv_sec) \
+ printf ("%s: "id" at %s.%06d",\
+ name,ctime(&p->fld.tv_sec),p->fld.tv_usec);
+{
+ P ("started ", start);
+ P ("ended ", end );
+ P ("selected ", select);
+ P ("command ", command);
+ P ("data ", data);
+ P ("status ", status);
+ P ("disconnected", disconnect);
+ P ("reselected ", reselect);
+ printf ("\n");
+}
+
+
+
+
+void dump_profile (const char* name, struct profile * p)
+{
+ printf ("%s: %10d transfers.\n" ,name,p->num_trans);
+ printf ("%s: %10d bytes transferred.\n",name,p->num_bytes);
+ printf ("%s: %10d disconnects.\n" ,name,p->num_disc);
+ printf ("%s: %10d short transfers.\n" ,name,p->num_break);
+ printf ("%s: %10d interrupts.\n" ,name,p->num_int);
+ printf ("%s: %10d on the fly ints.\n" ,name,p->num_fly);
+ printf ("%s: %10d ms setup time.\n" ,name,p->ms_setup);
+ printf ("%s: %10d ms data transfer.\n" ,name,p->ms_data);
+ printf ("%s: %10d ms disconnected.\n" ,name,p->ms_disc);
+ printf ("%s: %10d ms postprocessing.\n",name,p->ms_post);
+ printf ("\n");
+}
+
+/*================================================================
+**
+**
+** Write debug info: utilities: write script registers.
+**
+**
+**================================================================
+*/
+
+static void dump_reg(struct ncr_reg * rp)
+{
+ u_char *reg = (u_char*) rp;
+#define l(i) (reg[i]+(reg[i+1]<<8ul)+(reg[i+2]<<16ul)+(reg[i+3]<<24ul))
+ int ad;
+
+ char*(phasename[8])={"DATA-OUT","DATA-IN","COMMAND","STATUS",
+ "ILG-OUT","ILG-IN","MESSAGE-OUT","MESSAGE-IN"};
+ for (ad=0x00;ad<0x80;ad++) {
+ switch (ad % 16) {
+
+ case 0:
+ printf (" %02x:\t",ad);
+ break;
+ case 8:
+ printf (" : ");
+ break;
+ default:
+ printf (" ");
+ };
+ printf ("%02x", reg[ad]);
+ if (ad % 16 == 15) printf ("\n");
+ };
+ printf ("\n");
+ printf (" DSP %08x %-20s CMD %08x DSPS %08x %s\n",
+ l(0x2c),sn(l(0x2c)),l(0x24),l(0x30), sn(l(0x30)));
+ printf (" TEMP %08x %-20s DSA %08x\n",
+ l(0x1c),sn(l(0x1c)),l(0x10));
+ printf ("\n");
+ printf (" Busstatus: ");
+ if ((reg[0x0b]>>7)&1) printf (" Req");
+ if ((reg[0x0b]>>6)&1) printf (" Ack");
+ if ((reg[0x0b]>>5)&1) printf (" Bsy");
+ if ((reg[0x0b]>>4)&1) printf (" Sel");
+ if ((reg[0x0b]>>3)&1) printf (" Atn");
+ printf (" %s\n", phasename[reg[0x0b]&7]);
+
+ printf (" Dmastatus: ");
+ if ((reg[0x0c]>>7)&1) printf (" FifoEmpty");
+ if ((reg[0x0c]>>6)&1) printf (" MasterParityError");
+ if ((reg[0x0c]>>5)&1) printf (" BusFault");
+ if ((reg[0x0c]>>4)&1) printf (" Aborted");
+ if ((reg[0x0c]>>3)&1) printf (" SingleStep");
+ if ((reg[0x0c]>>2)&1) printf (" Interrupt");
+ if ((reg[0x0c]>>0)&1) printf (" IllegalInstruction");
+ printf ("\n");
+ printf (" Intstatus: ");
+ if ((reg[0x14]>>7)&1) printf (" Abort");
+ if ((reg[0x14]>>6)&1) printf (" SoftwareReset");
+ if ((reg[0x14]>>5)&1) printf (" SignalProcess");
+ if ((reg[0x14]>>4)&1) printf (" Semaphore");
+ if ((reg[0x14]>>3)&1) printf (" Connected");
+ if ((reg[0x14]>>2)&1) printf (" IntOnTheFly");
+ if ((reg[0x14]>>1)&1) printf (" SCSI-Interrupt");
+ if ((reg[0x14]>>0)&1) printf (" DMA-Interrupt");
+ printf ("\n");
+ printf (" ScsiIstat: ");
+ if ((reg[0x42]>>7)&1) printf (" PhaseMismatch");
+ if ((reg[0x42]>>6)&1) printf (" Complete");
+ if ((reg[0x42]>>5)&1) printf (" Selected");
+ if ((reg[0x42]>>4)&1) printf (" Reselected");
+ if ((reg[0x42]>>3)&1) printf (" GrossError");
+ if ((reg[0x42]>>2)&1) printf (" UnexpectedDisconnect");
+ if ((reg[0x42]>>1)&1) printf (" ScsiReset");
+ if ((reg[0x42]>>0)&1) printf (" ParityError");
+ if ((reg[0x43]>>2)&1) printf (" SelectionTimeout");
+ if ((reg[0x43]>>1)&1) printf (" TimerExpired");
+ if ((reg[0x43]>>0)&1) printf (" HandshakeTimeout");
+ printf ("\n");
+ printf (" ID=%d DEST-ID=%d RESEL-ID=%d\n", reg[4]&7, reg[6]&7, reg[0xa]&7);
+ printf ("\n");
+}
+
+/*================================================================
+**
+**
+** Write debug info: utilities: write header.
+**
+**
+**================================================================
+*/
+
+char * debug_opt;
+
+void dump_head (struct head * hp)
+{
+ dump_link (" launch", & hp->launch);
+ printf (" savep: %08x %s\n",
+ hp->savep, sn((u_long) hp->savep));
+ printf (" cp: %08x %s\n",
+ hp->cp, sn((u_long)hp->cp));
+ if (strchr (debug_opt, 'y')) {
+ printf ("\n");
+ dump_tstamp (" timestamp", &hp->stamp);
+ };
+
+ printf (" status: %x %x %x %x %x %x %x %x\n",
+ hp->status[0], hp->status[1], hp->status[2], hp->status[3],
+ hp->status[4], hp->status[5], hp->status[6], hp->status[7]);
+
+ printf ("\n");
+}
+
+/*================================================================
+**
+**
+** Write debug info: utilities: write ccb.
+**
+**
+**================================================================
+*/
+
+void dump_ccb (struct ccb * cp, u_long base)
+{
+ printf ("----------------------\n");
+ printf ("struct ccb @ %08x:\n", base);
+ printf ("----------------------\n");
+
+ dump_link (" next", &cp->jump_ccb);
+ dump_link (" call", &cp->call_tmp);
+
+ dump_head (&cp->phys.header);
+
+ if (strchr (debug_opt, 's')) {
+ dump_table(" smsg", &cp->phys.smsg, 1);
+ dump_table("smsg2", &cp->phys.smsg2, 1);
+ dump_table(" cmd", &cp->phys.cmd, 1);
+ dump_table(" data", &cp->phys.data[0],MAX_SCATTER);
+ dump_table("sense", &cp->phys.sense, 1);
+ };
+
+ if (strchr (debug_opt, 'a')) {
+ int i;
+ for (i=0; i<8; i++)
+ printf (" patch[%d]: %08x\n", i, cp->patch[i]);
+ };
+
+ if (strchr (debug_opt, 'x')) {
+ printf (" xfer: -- dump not yet implemented.\n");
+ };
+
+ if (strchr (debug_opt, 'm')) {
+ printf (" smsg:");
+ printm (cp->scsi_smsg, cp->phys.smsg.size);
+ printf ("\n");
+ printf (" smsg2:");
+ printm (cp->scsi_smsg2, cp->phys.smsg2.size);
+ printf ("\n");
+ };
+
+ printf (" magic: %x\n", cp->magic);
+ if (cp->tlimit)
+ printf (" timeout at: %s", ctime((time_t*)&cp->tlimit));
+ printf (" link_ccb: %08x\n", (u_long) cp->link_ccb);
+ printf (" next_ccb: %08x\n", (u_long) cp->next_ccb);
+ printf (" tag: %d\n", cp->tag);
+ printf ("\n");
+}
+
+/*================================================================
+**
+**
+** Write debug info: struct lcb
+**
+**
+**================================================================
+*/
+
+static void dump_lcb (u_long base)
+{
+ struct lcb l;
+ struct ccb c;
+ u_long cp,cn;
+
+ printf ("----------------------\n");
+ printf ("struct lcb @ %08x:\n", base);
+ printf ("----------------------\n");
+
+ if (!KVM_READ (
+ base,
+ &l,
+ sizeof (struct lcb)))
+ errx(1, "bad kvm read");
+ printf (" reqccbs: %d\n", l.reqccbs);
+ printf (" actccbs: %d\n", l.actccbs);
+ printf (" reqlink: %d\n", l.reqlink);
+ printf (" actlink: %d\n", l.actlink);
+ printf (" usetags: %d\n", l.usetags);
+ dump_link (" jump_lcb", &l.jump_lcb);
+ dump_link (" call_tag", &l.call_tag);
+ dump_link (" jump_ccb", &l.jump_ccb);
+ printf ("\n");
+ cp = (u_long) l.next_ccb;
+ cn = 0;
+ while (cp) {
+ cn++;
+ printf ("ccb #%d:\n", cn);
+ if (!KVM_READ (
+ cp,
+ &c,
+ sizeof (struct ccb)))
+ errx(1, "bad kvm read");
+ dump_ccb (&c, cp);
+ cp= (u_long) c.next_ccb;
+ };
+}
+
+/*================================================================
+**
+**
+** Write debug info: struct tcb
+**
+**
+**================================================================
+*/
+
+static void dump_tip (struct tcb * tip)
+{
+ int i;
+ u_long lp;
+
+ printf ("----------------------\n");
+ printf ("struct tcb:\n");
+ printf ("----------------------\n");
+
+ printf (" transfers:%10d.\n", tip->transfers);
+ printf (" bytes:%10d.\n", tip->bytes );
+ printf (" user limits: usrsync=%d usrwide=%d usrtags=%d.\n",
+ tip->usrsync, tip->usrwide, tip->usrtags);
+ printf (" sync: minsync=%d, maxoffs=%d, period=%d ns, sval=%x.\n",
+ tip->minsync, tip->maxoffs, tip->period, tip->sval);
+ printf (" wide: widedone=%d, wval=%x.\n",
+ tip->widedone, tip->wval);
+
+ printf (" hold_cp: %x\n", tip->hold_cp);
+ dump_link (" jump_tcb", &tip->jump_tcb);
+ dump_link (" call_lun", &tip->call_lun);
+ dump_link (" jump_lcb", &tip->jump_lcb);
+ if (tip->hold_cp) printf (" hold_cp: @ %x\n", tip->hold_cp);
+ printf ("\n");
+
+ if (strchr (debug_opt, 'l')) {
+ for (i=0;i<MAX_LUN;i++) {
+ lp= (u_long) tip->lp[i];
+ printf ("logic unit #%d:\n", i);
+ if (lp) dump_lcb (lp);
+ };
+ }
+}
+
+/*================================================================
+**
+**
+** Write debug info: struct ncb
+**
+**
+**================================================================
+*/
+
+
+static void dump_ncr (void)
+{
+ int i;
+
+ printf ("----------------------\n");
+ printf ("struct ncb @ %x:\n", ncr_base);
+ printf ("----------------------\n");
+
+ dump_link (" jump_tcb", &ncr.jump_tcb);
+ printf (" register: @ %x (p=%x)\n", ncr.vaddr, ncr.paddr);
+
+ if (wizard && strchr (debug_opt, 'r')) {
+ struct ncr_reg reg;
+
+ if (!KVM_READ (
+ ncr.vaddr,
+ &reg,
+ sizeof (reg)))
+ errx(1, "bad kvm read");
+
+ printf ("\n");
+ dump_reg (&reg);
+ };
+
+ printf (" script: @ %x (p=%x)\n", ncr.script, ncr.p_script);
+
+ printf ("hostscsiaddr: %d\n", ncr.myaddr);
+ printf (" minsync: %d\n", ncr.minsync);
+ printf (" scntl3: 0x%02x\n", ncr.rv_scntl3);
+ printf ("\n");
+
+ /* sc_link not dumped */
+
+ if (strchr (debug_opt, 'u')) {
+ printf (" usercmd: cmd=%x data=%x target=%x lun=%x\n",
+ ncr.user.cmd,
+ ncr.user.data,
+ ncr.user.target,
+ ncr.user.lun);
+ };
+
+ printf (" actccbs: %d\n", ncr.actccbs);
+
+ if (strchr (debug_opt, 'q')) {
+
+ u_long startpos;
+
+ if (!KVM_READ (
+ ((u_long)ncr.script
+ +offsetof(struct script, startpos)),
+ &startpos,
+ sizeof (startpos)))
+ errx(1, "bad kvm read");
+
+ printf (" startpos: %x\n", startpos);
+ printf (" slot: %d\n", (startpos-
+ (ncr.p_script+offsetof(struct scripth, tryloop)))/20);
+ printf (" squeuput: %d\n", ncr.squeueput);
+ for (i=0; i<MAX_START; i++)
+ printf ("%12d: %08x %s\n", i,
+ ncr.squeue[i], sn(ncr.squeue[i]));
+
+ printf ("\n");
+ };
+
+ printf (" ticks: %d ms\n", ncr.ticks * 10);
+ printf (" heartbeat: %s", ctime ((time_t*)&ncr.heartbeat));
+ printf (" lasttime: %s", ctime ((time_t*)&ncr.lasttime));
+ printf ("\n");
+
+ if (wizard && strchr (debug_opt, 'd') && ncr.regtime.tv_sec) {
+ printf (" regdump: %s", ctime (&ncr.regtime.tv_sec));
+ dump_reg (&ncr.regdump);
+ };
+
+ if (strchr (debug_opt, 'p')) {
+ printf ("\n");
+ dump_profile (" profile", &ncr.profile);
+ };
+
+ if (strchr (debug_opt, 'h')) {
+ printf ("\n");
+ dump_head ( &ncr.header);
+ };
+
+ if (strchr (debug_opt, 'c')) {
+ dump_ccb (ncr.ccb, ncr_base + offsetof (struct ncb, ccb));
+ };
+
+ if (strchr (debug_opt, 'm')) {
+ printf (" msgout:"); printm (ncr.msgout,0); printf ("\n");
+ printf (" msg in:"); printm (ncr.msgin,0); printf ("\n");
+ printf ("\n");
+ };
+
+ if (strchr (debug_opt, 't')) {
+ struct tcb * tip;
+ for (i=0;i<MAX_TARGET;i++) {
+ tip = &ncr.target[i];
+ if (!tip->jump_tcb.l_cmd) continue;
+ printf ("target #%d:\n", i);
+ dump_tip (tip);
+ }
+ }
+}
+
+/*================================================================
+**
+**
+** D O _ D E B U G
+**
+**
+**================================================================
+*/
+
+
+void do_debug(char * arg)
+{
+ open_kvm(O_RDONLY);
+ debug_opt = arg;
+ if (strchr (debug_opt, '?')) printf (
+"'?': list debug options [sic].\n"
+"'a': show patchfields in ccbs (requires c).\n"
+"'c': show ccbs.\n"
+"'d': show register dump.\n"
+"'h': show header information.\n"
+"'m': show message buffers.\n"
+"'n': show ncr main control block.\n"
+"'p': show profiling information.\n"
+"'q': show start queue.\n"
+"'r': show registers (*DANGEROUS*).\n"
+"'s': show scatter/gather info.\n"
+"'t': show target control blocks.\n"
+"'u': show user cmd field.\n"
+"'x': show generic xfer structure.\n"
+"'y': show timestamps.\n"
+"\n"
+ );
+
+ if (strchr (debug_opt, 'n')) dump_ncr ();
+
+ if (!wizard)
+ errx(2, "you are NOT a wizard!");
+ if (strchr (debug_opt, 'r')) {
+ struct ncr_reg reg;
+ if (!KVM_READ (
+ ncr.vaddr,
+ &reg,
+ sizeof (reg)))
+ errx(1, "bad kvm read");
+ dump_reg (&reg);
+ };
+}
+
+
+/*================================================================
+**
+**
+** Main function
+**
+**
+**================================================================
+*/
+
+int main(argc, argv)
+ int argc;
+ char **argv;
+{
+ int usageflg=0;
+ char * charp;
+ int ch;
+ int i;
+
+ while ((ch = getopt(argc, argv, "M:N:u:f:t:l:p:s:k:d:vwhin:?")) != -1)
+ switch((char)ch) {
+ case 'M':
+ if (kvm_isopen)
+ errx(1, "-M: kernel file already open");
+ kmemf = optarg;
+ break;
+ case 'N':
+ if (kvm_isopen)
+ errx(1, "-N: symbol table already open");
+ vmunix = optarg;
+ break;
+#ifdef OPT_F
+ case 'f':
+ errx(1, "-f: option not yet implemented");
+#endif
+
+ case 'u':
+ i = strtoul (optarg, &charp, 0);
+ if (!*optarg || *charp || (i<0))
+ errx(1, "bad unit number \"%s\"", optarg);
+ ncr_unit = i;
+ break;
+ case 't':
+ i = strtoul (optarg, &charp, 0);
+ if (!*optarg || *charp || (i<0) || (i>=MAX_TARGET))
+ errx(1, "bad target number \"%s\" (valid range: 0-%d)",
+ optarg, MAX_TARGET-1);
+ target_mask |= 1ul << i;
+ break;
+ case 'n':
+ open_kvm(O_RDONLY);
+ i = strtoul (optarg, &charp, 0);
+ printf ("addr %d (0x%x) has label %s.\n",
+ i,i,sn(i));
+ break;
+ case 'l':
+ i = strtoul (optarg, &charp, 0);
+ if (!*optarg || *charp || (i<0) || (i>=MAX_LUN))
+ errx(1,
+ "bad logic unit number \"%s\" (valid range: 0-%d)",
+ optarg, MAX_LUN);
+ global_lun_mask |= 1ul << i;
+ break;
+ case 'p':
+ i = strtoul (optarg, &charp, 0);
+ if (!*optarg || *charp || (i<1) || (i>60))
+ errx(1, "bad interval \"%s\"", optarg);
+ interval = i;
+ do_profile();
+ break;
+
+ case 'w':
+ if(geteuid()==0)
+ wizard=1;
+ break;
+ case 'v':
+ verbose++;
+ break;
+ case 'i':
+ do_info();
+ break;
+
+ case 's':
+ do_set(optarg);
+ break;
+ case 'd':
+ do_debug(optarg);
+ break;
+ case 'k':
+ do_kill(optarg);
+ break;
+ case 'h':
+ case '?':
+ usageflg++;
+ break;
+ default:
+ warnx("illegal option \"%c\"", ch);
+ usageflg++;
+ }
+
+ argv += optind;
+ argc -= optind;
+
+ if (argc)
+ warnx("rest of line starting with \"%s\" ignored", *argv);
+
+ if (verbose&&!kvm_isopen) usageflg++;
+ if (usageflg) {
+ usage();
+ if (verbose) fprintf (stderr, ident);
+ exit (1);
+ }
+
+ if (!kvm_isopen) {
+ do_info();
+ do_profile();
+ };
+ exit (0);
+}
+
+
+static void
+usage (void)
+{
+ fprintf (stderr, "%s\n%s\n%s\n%s\n%s\n",
+"usage: ncrcontrol [-M core] [-N system] [-u unit] [-v] [-v] -i",
+" ncrcontrol [-N system] [-u unit] [-p wait]",
+" ncrcontrol [-N system] [-u unit] [-t target] -s name=value",
+" ncrcontrol [-M core] [-N system] [-u unit] [-t target] -d debug",
+" ncrcontrol [-N system] [-u unit] -w -k torture");
+}
diff --git a/usr.sbin/ndc/Makefile b/usr.sbin/ndc/Makefile
new file mode 100644
index 0000000..732ea65
--- /dev/null
+++ b/usr.sbin/ndc/Makefile
@@ -0,0 +1,25 @@
+# $Id$
+
+.include "${.CURDIR}/../named/Makefile.inc"
+
+.PATH: ${BIND_DIR}/named
+.PATH: ${BIND_DIR}/man
+
+MAN8= ndc.8
+CLEANFILES+= ndc
+
+all: ndc
+
+realinstall:
+ ${INSTALL} ${COPY} -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} ndc \
+ ${DESTDIR}${BINDIR}
+
+ndc: ndc.sh ndcedit.awk ${BIND_DIR}/Makefile
+ sed -e "s|%PIDDIR%|${PIDDIR}|" \
+ -e "s|%INDOT%|${INDOT}|" \
+ -e "s|%PS%|${PS}|" \
+ -e "s|%DESTSBIN%|${DESTSBIN}|" \
+ -e "s|%IOT%|${IOT}|" \
+ < ${BIND_DIR}/named/ndc.sh | awk -f ${.CURDIR}/ndcedit.awk > ndc
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/ndc/ndcedit.awk b/usr.sbin/ndc/ndcedit.awk
new file mode 100644
index 0000000..8074819
--- /dev/null
+++ b/usr.sbin/ndc/ndcedit.awk
@@ -0,0 +1,35 @@
+# $Id: ndcedit.awk,v 1.5 1997/05/27 07:19:57 jkh Exp $
+NR == 3 {
+ print "#"
+ print "# This file is generated automatically, do not edit it here!"
+ print "# Please change src/usr.sbin/ndc/ndcedit.awk instead"
+ print "#"
+ print ""
+
+ print "# If there is a global system configuration file, suck it in."
+ print "if [ -f /etc/rc.conf ]; then"
+ print "\t. /etc/rc.conf"
+ print "fi\n"
+}
+{
+ if ($1 == "named") {
+ printf "\t\t# $named_flags is imported from /etc/rc.conf\n"
+ printf "\t\tif [ \"X${named_enable}\" = X\"YES\" ]; then\n"
+ printf "\t\t\tnamed ${named_flags} && {\n"
+ getline
+ printf "\t%s\n", $0
+ getline
+ printf "\t%s\n", $0
+ getline
+ printf "\t%s\n", $0
+ printf "\t\tfi\n"
+ } else {
+ if (/PATH=/) {
+ gsub(":/usr/ucb:", ":", $0);
+ if (!/export/) {
+ $0=$0"\nexport PATH";
+ }
+ }
+ print;
+ }
+}
diff --git a/usr.sbin/newsyslog/Makefile b/usr.sbin/newsyslog/Makefile
new file mode 100644
index 0000000..a6654c6
--- /dev/null
+++ b/usr.sbin/newsyslog/Makefile
@@ -0,0 +1,16 @@
+# $Id$
+
+PROG= newsyslog
+
+CFLAGS+= -DOSF
+CFLAGS+= -DCONF=\"/etc/newsyslog.conf\"
+CFLAGS+= -DPIDFILE=\"/var/run/syslog.pid\"
+CFLAGS+= -DCOMPRESS_PATH=\"/usr/bin/gzip\"
+CFLAGS+= -DCOMPRESS_PROG=\"gzip\"
+CFLAGS+= -DCOMPRESS_POSTFIX=\".gz\"
+
+BINOWN= root
+
+MAN8= newsyslog.8
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/newsyslog/newsyslog.8 b/usr.sbin/newsyslog/newsyslog.8
new file mode 100644
index 0000000..c805197
--- /dev/null
+++ b/usr.sbin/newsyslog/newsyslog.8
@@ -0,0 +1,172 @@
+.\" This file contains changes from the Open Software Foundation.
+.\"
+.\" from: @(#)newsyslog.8
+.\" $Id: newsyslog.8,v 1.8 1997/06/23 04:03:49 steve Exp $
+.\"
+.\" Copyright 1988, 1989 by the Massachusetts Institute of Technology
+.\"
+.\" Permission to use, copy, modify, and distribute this software
+.\" and its documentation for any purpose and without fee is
+.\" hereby granted, provided that the above copyright notice
+.\" appear in all copies and that both that copyright notice and
+.\" this permission notice appear in supporting documentation,
+.\" and that the names of M.I.T. and the M.I.T. S.I.P.B. not be
+.\" used in advertising or publicity pertaining to distribution
+.\" of the software without specific, written prior permission.
+.\" M.I.T. and the M.I.T. S.I.P.B. make no representations about
+.\" the suitability of this software for any purpose. It is
+.\" provided "as is" without express or implied warranty.
+.\"
+.Dd January 12, 1989
+.Dt NEWSYSLOG 8
+.Os
+.Sh NAME
+.Nm newsyslog
+.Nd maintain system log files to manageable sizes
+.Sh SYNOPSIS
+.Nm newsyslog
+.Op Fl rnv
+.Op Fl f Ar config_file
+.Sh DESCRIPTION
+.Nm Newsyslog
+is a program that should be scheduled to run periodically by
+.Xr cron 8 .
+When it is executed it archives log files if necessary. If a log file
+is determined to require archiving,
+.Nm
+rearranges the files so that ``logfile'' is empty, ``logfile.0'' has
+the last period's logs in it, ``logfile.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 because of two reasons. The log file can have
+grown bigger than a preset size in kilobytes, or a preset number of
+hours may have elapsed since the last log archive. 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.
+.Pp
+When starting up,
+.Nm
+reads in a configuration file to determine which logs should be looked
+at. By default, this configuration file is
+.Pa /etc/newsyslog.conf .
+Each line of the file contains information about a particular log file
+that should be handled by
+.Nm newsyslog .
+Each line has five mandatory fields and three optional fields, with a
+whitespace separating each field. Blank lines or lines beginning with
+``#'' are ignored. The fields of the configuration file are as
+follows:
+.Pp
+.Bl -tag -width indent
+.It Ar logfile_name
+Name of the system log file to be archived.
+.It Ar owner.group
+Specify the owner and group for the archive file.
+The "." is essential, even if the
+.Ar owner
+or
+.Ar group
+field is left blank. The field may be numeric, or a name which is
+present in
+.Pa /etc/passwd
+or
+.Pa /etc/group .
+.It Ar mode
+Specify the mode of the log file and archives.
+.It Ar count
+Specify the number of archive files to be kept
+besides the log file itself.
+.It Ar size
+When the size of the log file reaches
+.Ar size ,
+the log file will be trimmed as described above. If this field
+is replaced by a
+.Ar * ,
+then the size of the log file is not taken into account
+when determining when to trim the log file.
+of archives
+.It Ar interval
+When
+.Ar interval
+hours have passed, the log file will be trimmed. If this field is
+replaced by a
+.Ar * ,
+then the number of hours since the last time the log was
+trimmed will not be taken into consideration.
+.It Ar flags
+This optional field specifies if the archive should have any
+special processing done to the archived log files.
+The
+.Ar Z
+flag will make the archive files compress to save space by
+using
+.Xr gzip 1 .
+The
+.Ar B
+flag means that the file is a binary file, and so the
+.Tn ASCII
+message which
+.Nm
+inserts to indicate the fact that the logs have been
+turned over should not be included. The
+.Ar -
+flag means nothing, but can be used as a placeholder when the
+.Ar path_to_pid_file
+field is specified.
+.It Ar path_to_pid_file
+This optional field specifies
+the file name to read to find the daemon process id. If this
+field is present, a SIGHUP is sent the process id contained in this
+file. This field must start with "/" in order to be recognized
+properly.
+.El
+.Sh OPTIONS
+The following options can be used with newsyslog:
+.Bl -tag -width indent
+.It Fl f Ar config_file
+Instruct newsyslog to use
+.Ar config_file
+instead of
+.Pa /etc/newsyslog.conf
+for its configuration file.
+.It Fl v
+Place
+.Nm
+in verbose mode. In this mode it will print out each log and its
+reasons for either trimming that log or skipping it.
+.It Fl n
+Cause
+.Nm
+not to trim the logs, but to print out what it would do if this option
+were not specified.
+.It Fl r
+Remove the restriction that
+.Nm
+must be running as root. Of course,
+.Nm
+will not be able to send a HUP signal to
+.Xr syslogd 8
+so this option should only be used in debugging.
+.El
+.Sh FILES
+.Bl -tag -width /etc/newsyslog.confxxxx -compact
+.It Pa /etc/newsyslog.conf
+.Nm
+configuration file.
+.El
+.Sh BUGS
+Doesn't yet automatically read the logs to find security breaches.
+.Sh AUTHOR
+.An Theodore Ts'o ,
+MIT Project Athena
+.Pp
+Copyright 1987, Massachusetts Institute of Technology
+.Sh "SEE ALSO"
+.Xr gzip 1 ,
+.Xr syslog 3 ,
+.Xr syslogd 8
diff --git a/usr.sbin/newsyslog/newsyslog.c b/usr.sbin/newsyslog/newsyslog.c
new file mode 100644
index 0000000..00763a2
--- /dev/null
+++ b/usr.sbin/newsyslog/newsyslog.c
@@ -0,0 +1,636 @@
+/*
+ * This file contains changes from the Open Software Foundation.
+ */
+
+/*
+
+Copyright 1988, 1989 by the Massachusetts Institute of Technology
+
+Permission to use, copy, modify, and distribute this software
+and its documentation for any purpose and without fee is
+hereby granted, provided that the above copyright notice
+appear in all copies and that both that copyright notice and
+this permission notice appear in supporting documentation,
+and that the names of M.I.T. and the M.I.T. S.I.P.B. not be
+used in advertising or publicity pertaining to distribution
+of the software without specific, written prior permission.
+M.I.T. and the M.I.T. S.I.P.B. make no representations about
+the suitability of this software for any purpose. It is
+provided "as is" without express or implied warranty.
+
+*/
+
+/*
+ * newsyslog - roll over selected logs at the appropriate time,
+ * keeping the a specified number of backup files around.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id: newsyslog.c,v 1.13 1997/05/06 23:11:06 brian Exp $";
+#endif /* not lint */
+
+#ifndef CONF
+#define CONF "/etc/athena/newsyslog.conf" /* Configuration file */
+#endif
+#ifndef PIDFILE
+#define PIDFILE "/etc/syslog.pid"
+#endif
+#ifndef COMPRESS_PATH
+#define COMPRESS_PATH "/usr/ucb/compress" /* File compression program */
+#endif
+#ifndef COMPRESS_PROG
+#define COMPRESS_PROG "compress"
+#endif
+#ifndef COMPRESS_POSTFIX
+#define COMPRESS_POSTFIX ".Z"
+#endif
+
+#include <ctype.h>
+#include <err.h>
+#include <fcntl.h>
+#include <grp.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <sys/wait.h>
+
+#define kbytes(size) (((size) + 1023) >> 10)
+#ifdef _IBMR2
+/* Calculates (db * DEV_BSIZE) */
+#define dbtob(db) ((unsigned)(db) << UBSHIFT)
+#endif
+
+#define CE_COMPACT 1 /* Compact the achived log files */
+#define CE_BINARY 2 /* Logfile is in binary, don't add */
+ /* status messages */
+#define 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 */
+ int permissions; /* File permissions on the log */
+ int flags; /* Flags (CE_COMPACT & CE_BINARY) */
+ struct conf_entry *next; /* Linked list pointer */
+};
+
+int verbose = 0; /* Print out what's going on */
+int needroot = 1; /* Root privs are necessary */
+int noaction = 0; /* Don't do anything, just show it */
+char *conf = CONF; /* Configuration file to use */
+time_t timenow;
+pid_t syslog_pid; /* read in from /etc/syslog.pid */
+#define MIN_PID 5
+#define MAX_PID 30000 /* was 65534, see /usr/include/sys/proc.h */
+char hostname[MAXHOSTNAMELEN+1]; /* hostname */
+char *daytime; /* timenow in human readable form */
+
+#ifndef OSF
+char *strdup(char *strp);
+#endif
+
+static struct conf_entry *parse_file();
+static char *sob(char *p);
+static char *son(char *p);
+static char *missing_field(char *p,char *errline);
+static void do_entry(struct conf_entry *ent);
+static void PRS(int argc,char **argv);
+static void usage();
+static void dotrim(char *log,char *pid_file,int numdays,int falgs,int perm, int owner_uid,int group_gid);
+static int log_trim(char *log);
+static void compress_log(char *log);
+static int sizefile(char *file);
+static int age_old_log(char *file);
+static pid_t get_pid(char *pid_file);
+
+int main(argc,argv)
+ int argc;
+ char **argv;
+{
+ struct conf_entry *p, *q;
+
+ PRS(argc,argv);
+ if (needroot && getuid() && geteuid())
+ errx(1, "must have root privs");
+ p = q = parse_file();
+
+ syslog_pid = needroot ? get_pid(PIDFILE) : 0;
+
+ while (p) {
+ do_entry(p);
+ p=p->next;
+ free((char *) q);
+ q=p;
+ }
+ return(0);
+}
+
+static void do_entry(ent)
+ struct conf_entry *ent;
+
+{
+ int size, modtime;
+
+ if (verbose) {
+ if (ent->flags & CE_COMPACT)
+ printf("%s <%dZ>: ",ent->log,ent->numlogs);
+ else
+ printf("%s <%d>: ",ent->log,ent->numlogs);
+ }
+ size = sizefile(ent->log);
+ modtime = age_old_log(ent->log);
+ if (size < 0) {
+ if (verbose)
+ printf("does not exist.\n");
+ } else {
+ if (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 (((ent->size > 0) && (size >= ent->size)) ||
+ ((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",
+ ent->log,ent->numlogs);
+ else
+ printf("%s <%d>: trimming",
+ ent->log,ent->numlogs);
+ }
+ dotrim(ent->log, ent->pid_file, ent->numlogs,
+ ent->flags, ent->permissions, ent->uid, ent->gid);
+ } else {
+ if (verbose)
+ printf("--> skipping\n");
+ }
+ }
+}
+
+static void PRS(argc,argv)
+ int argc;
+ char **argv;
+{
+ int c;
+ char *p;
+
+ timenow = time((time_t *) 0);
+ daytime = ctime(&timenow) + 4;
+ daytime[15] = '\0';
+
+ /* Let's get our hostname */
+ (void) gethostname(hostname, sizeof(hostname));
+
+ /* Truncate domain */
+ if ((p = strchr(hostname, '.'))) {
+ *p = '\0';
+ }
+
+ optind = 1; /* Start options parsing */
+ while ((c=getopt(argc,argv,"nrvf:t:")) != -1)
+ switch (c) {
+ case 'n':
+ noaction++; /* This implies needroot as off */
+ /* fall through */
+ case 'r':
+ needroot = 0;
+ break;
+ case 'v':
+ verbose++;
+ break;
+ case 'f':
+ conf = optarg;
+ break;
+ default:
+ usage();
+ }
+ }
+
+static void usage()
+{
+ fprintf(stderr, "usage: newsyslog [-nrv] [-f config-file]\n");
+ exit(1);
+}
+
+/* Parse a configuration file and return a linked list of all the logs
+ * to process
+ */
+static struct conf_entry *parse_file()
+{
+ FILE *f;
+ char line[BUFSIZ], *parse, *q;
+ char *errline, *group;
+ struct conf_entry *first = NULL;
+ struct conf_entry *working = NULL;
+ struct passwd *pass;
+ struct group *grp;
+ int eol;
+
+ if (strcmp(conf,"-"))
+ f = fopen(conf,"r");
+ else
+ f = stdin;
+ if (!f)
+ err(1, "%s", conf);
+ while (fgets(line,BUFSIZ,f)) {
+ if ((line[0]== '\n') || (line[0] == '#'))
+ continue;
+ errline = strdup(line);
+ if (!first) {
+ working = (struct conf_entry *) malloc(sizeof(struct conf_entry));
+ first = working;
+ } else {
+ working->next = (struct conf_entry *) malloc(sizeof(struct conf_entry));
+ working = working->next;
+ }
+
+ q = parse = missing_field(sob(line),errline);
+ parse = son(line);
+ if (!*parse)
+ errx(1, "malformed line (missing fields):\n%s", errline);
+ *parse = '\0';
+ working->log = strdup(q);
+
+ q = parse = missing_field(sob(++parse),errline);
+ parse = son(parse);
+ if (!*parse)
+ errx(1, "malformed line (missing fields):\n%s", errline);
+ *parse = '\0';
+ if ((group = strchr(q, '.')) != NULL) {
+ *group++ = '\0';
+ if (*q) {
+ if (!(isnumber(*q))) {
+ if ((pass = getpwnam(q)) == NULL)
+ errx(1,
+ "error in config file; unknown user:\n%s",
+ errline);
+ working->uid = pass->pw_uid;
+ } else
+ working->uid = atoi(q);
+ } else
+ working->uid = NONE;
+
+ q = group;
+ if (*q) {
+ if (!(isnumber(*q))) {
+ if ((grp = getgrnam(q)) == NULL)
+ errx(1,
+ "error in config file; unknown group:\n%s",
+ errline);
+ working->gid = grp->gr_gid;
+ } else
+ working->gid = atoi(q);
+ } else
+ working->gid = NONE;
+
+ q = parse = missing_field(sob(++parse),errline);
+ parse = son(parse);
+ if (!*parse)
+ errx(1, "malformed line (missing fields):\n%s", errline);
+ *parse = '\0';
+ }
+ else
+ working->uid = working->gid = NONE;
+
+ if (!sscanf(q,"%o",&working->permissions))
+ errx(1, "error in config file; bad permissions:\n%s",
+ errline);
+
+ q = parse = missing_field(sob(++parse),errline);
+ parse = son(parse);
+ if (!*parse)
+ errx(1, "malformed line (missing fields):\n%s", errline);
+ *parse = '\0';
+ if (!sscanf(q,"%d",&working->numlogs))
+ errx(1, "error in config file; bad number:\n%s",
+ errline);
+
+ q = parse = missing_field(sob(++parse),errline);
+ parse = son(parse);
+ if (!*parse)
+ errx(1, "malformed line (missing fields):\n%s", errline);
+ *parse = '\0';
+ if (isdigit(*q))
+ working->size = atoi(q);
+ else
+ working->size = -1;
+
+ q = parse = missing_field(sob(++parse),errline);
+ parse = son(parse);
+ eol = !*parse;
+ *parse = '\0';
+ if (isdigit(*q))
+ working->hours = atoi(q);
+ else
+ working->hours = -1;
+
+ if (eol)
+ q = NULL;
+ else {
+ q = parse = sob(++parse); /* Optional field */
+ parse = son(parse);
+ if (!*parse)
+ eol = 1;
+ *parse = '\0';
+ }
+
+ working->flags = 0;
+ while (q && *q && !isspace(*q)) {
+ if ((*q == 'Z') || (*q == 'z'))
+ working->flags |= CE_COMPACT;
+ else if ((*q == 'B') || (*q == 'b'))
+ working->flags |= CE_BINARY;
+ else if (*q != '-')
+ errx(1, "illegal flag in config file -- %c", *q);
+ q++;
+ }
+
+ if (eol)
+ q = NULL;
+ else {
+ q = parse = sob(++parse); /* Optional field */
+ *(parse = son(parse)) = '\0';
+ }
+
+ working->pid_file = NULL;
+ if (q && *q) {
+ if (*q == '/')
+ working->pid_file = strdup(q);
+ else
+ errx(1, "illegal pid file in config file:\n%s", q);
+ }
+
+ free(errline);
+ }
+ if (working)
+ working->next = (struct conf_entry *) NULL;
+ (void) fclose(f);
+ return(first);
+}
+
+static char *missing_field(p,errline)
+ char *p,*errline;
+{
+ if (!p || !*p)
+ errx(1, "missing field in config file:\n%s", errline);
+ return(p);
+}
+
+static void dotrim(log,pid_file,numdays,flags,perm,owner_uid,group_gid)
+ char *log;
+ char *pid_file;
+ int numdays;
+ int flags;
+ int perm;
+ int owner_uid;
+ int group_gid;
+{
+ char file1 [MAXPATHLEN+1], file2 [MAXPATHLEN+1];
+ char zfile1[MAXPATHLEN+1], zfile2[MAXPATHLEN+1];
+ int notified, need_notification, fd, _numdays;
+ struct stat st;
+ pid_t pid;
+
+#ifdef _IBMR2
+/* AIX 3.1 has a broken fchown- if the owner_uid is -1, it will actually */
+/* change it to be owned by uid -1, instead of leaving it as is, as it is */
+/* supposed to. */
+ if (owner_uid == -1)
+ owner_uid = geteuid();
+#endif
+
+ /* Remove oldest log */
+ (void) sprintf(file1,"%s.%d",log,numdays);
+ (void) strcpy(zfile1, file1);
+ (void) strcat(zfile1, COMPRESS_POSTFIX);
+
+ if (noaction) {
+ printf("rm -f %s\n", file1);
+ printf("rm -f %s\n", zfile1);
+ } else {
+ (void) unlink(file1);
+ (void) unlink(zfile1);
+ }
+
+ /* Move down log files */
+ _numdays = numdays; /* preserve */
+ while (numdays--) {
+ (void) strcpy(file2,file1);
+ (void) sprintf(file1,"%s.%d",log,numdays);
+ (void) strcpy(zfile1, file1);
+ (void) strcpy(zfile2, file2);
+ if (lstat(file1, &st)) {
+ (void) strcat(zfile1, COMPRESS_POSTFIX);
+ (void) strcat(zfile2, COMPRESS_POSTFIX);
+ if (lstat(zfile1, &st)) continue;
+ }
+ if (noaction) {
+ printf("mv %s %s\n",zfile1,zfile2);
+ printf("chmod %o %s\n", perm, zfile2);
+ printf("chown %d.%d %s\n",
+ owner_uid, group_gid, zfile2);
+ } else {
+ (void) rename(zfile1, zfile2);
+ (void) chmod(zfile2, perm);
+ (void) chown(zfile2, owner_uid, group_gid);
+ }
+ }
+ if (!noaction && !(flags & CE_BINARY))
+ (void) log_trim(log); /* Report the trimming to the old log */
+
+ if (!_numdays) {
+ if (noaction)
+ printf("rm %s\n",log);
+ else
+ (void)unlink(log);
+ }
+ else {
+ if (noaction)
+ printf("mv %s to %s\n",log,file1);
+ else
+ (void)rename(log, file1);
+ }
+
+ if (noaction)
+ printf("Start new log...");
+ else {
+ fd = creat(log,perm);
+ if (fd < 0)
+ err(1, "can't start new log");
+ if (fchown(fd, owner_uid, group_gid))
+ err(1, "can't chmod new log file");
+ (void) close(fd);
+ if (!(flags & CE_BINARY))
+ if (log_trim(log)) /* Add status message */
+ err(1, "can't add status message to log");
+ }
+ if (noaction)
+ printf("chmod %o %s...",perm,log);
+ else
+ (void) chmod(log,perm);
+
+ pid = 0;
+ need_notification = notified = 0;
+ if (pid_file != NULL) {
+ need_notification = 1;
+ pid = get_pid(pid_file);
+ } else if (needroot && !(flags & CE_BINARY)) {
+ need_notification = 1;
+ pid = syslog_pid;
+ }
+
+ if (pid) {
+ if (noaction) {
+ notified = 1;
+ printf("kill -HUP %d\n", (int)pid);
+ } else if (kill(pid,SIGHUP))
+ warn("can't notify daemon, pid %d", (int)pid);
+ else {
+ notified = 1;
+ if (verbose)
+ printf("daemon pid %d notified\n", (int)pid);
+ }
+ }
+
+ if ((flags & CE_COMPACT)) {
+ if (need_notification && !notified)
+ warnx("log not compressed because daemon not notified");
+ else if (noaction)
+ printf("Compress %s.0\n",log);
+ else {
+ if (notified) {
+ if (verbose)
+ printf("small pause to allow daemon to close log\n");
+ sleep(3);
+ }
+ compress_log(log);
+ }
+ }
+}
+
+/* Log the fact that the logs were turned over */
+static int log_trim(log)
+ char *log;
+{
+ FILE *f;
+ if ((f = fopen(log,"a")) == NULL)
+ return(-1);
+ fprintf(f,"%s %s newsyslog[%d]: logfile turned over\n",
+ daytime, hostname, (int)getpid());
+ if (fclose(f) == EOF)
+ err(1, "log_trim: fclose:");
+ return(0);
+}
+
+/* Fork of /usr/ucb/compress to compress the old log file */
+static void compress_log(log)
+ char *log;
+{
+ pid_t pid;
+ char tmp[MAXPATHLEN+1];
+
+ (void) sprintf(tmp,"%s.0",log);
+ pid = fork();
+ if (pid < 0)
+ err(1, "fork");
+ else if (!pid) {
+ (void) execl(COMPRESS_PATH,COMPRESS_PROG,"-f",tmp,0);
+ err(1, COMPRESS_PATH);
+ }
+}
+
+/* Return size in kilobytes of a file */
+static int sizefile(file)
+ char *file;
+{
+ struct stat sb;
+
+ if (stat(file,&sb) < 0)
+ return(-1);
+ return(kbytes(dbtob(sb.st_blocks)));
+}
+
+/* Return the age of old log file (file.0) */
+static int age_old_log(file)
+ char *file;
+{
+ struct stat sb;
+ char tmp[MAXPATHLEN+sizeof(".0")+sizeof(COMPRESS_POSTFIX)+1];
+
+ (void) strcpy(tmp,file);
+ if (stat(strcat(tmp,".0"),&sb) < 0)
+ if (stat(strcat(tmp,COMPRESS_POSTFIX), &sb) < 0)
+ return(-1);
+ return( (int) (timenow - sb.st_mtime + 1800) / 3600);
+}
+
+static pid_t get_pid(pid_file)
+ char *pid_file;
+{
+ FILE *f;
+ char line[BUFSIZ];
+ pid_t pid = 0;
+
+ if ((f = fopen(pid_file,"r")) == NULL)
+ warn("can't open %s pid file to restart a daemon",
+ pid_file);
+ else {
+ if (fgets(line,BUFSIZ,f)) {
+ pid = atol(line);
+ if (pid < MIN_PID || pid > MAX_PID) {
+ warnx("preposterous process number: %d", (int)pid);
+ pid = 0;
+ }
+ } else
+ warn("can't read %s pid file to restart a daemon",
+ pid_file);
+ (void)fclose(f);
+ }
+ return pid;
+}
+
+#ifndef OSF
+/* Duplicate a string using malloc */
+
+char *strdup(strp)
+register char *strp;
+{
+ register char *cp;
+
+ if ((cp = malloc((unsigned) strlen(strp) + 1)) == NULL)
+ abort();
+ return(strcpy (cp, strp));
+}
+#endif
+
+/* Skip Over Blanks */
+char *sob(p)
+ register char *p;
+{
+ while (p && *p && isspace(*p))
+ p++;
+ return(p);
+}
+
+/* Skip Over Non-Blanks */
+char *son(p)
+ register char *p;
+{
+ while (p && *p && !isspace(*p))
+ p++;
+ return(p);
+}
diff --git a/usr.sbin/nfsd/Makefile b/usr.sbin/nfsd/Makefile
new file mode 100644
index 0000000..6a144fa
--- /dev/null
+++ b/usr.sbin/nfsd/Makefile
@@ -0,0 +1,18 @@
+# @(#)Makefile 8.1 (Berkeley) 6/5/93
+
+PROG= nfsd
+CFLAGS+= -D_NEW_VFSCONF
+CFLAGS+=-DNFS
+MAN8= nfsd.8
+DPADD+= ${LIBUTIL}
+LDADD+= -lutil
+
+.if exists(${DESTDIR}/usr/lib/libkrb.a) && (defined(MAKE_KERBEROS) \
+ || defined(MAKE_EBONES))
+CFLAGS+=-DKERBEROS
+DPADD+= ${LIBKRB} ${LIBDES}
+LDADD+= -lkrb -ldes
+DISTRIBUTION= krb
+.endif
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/nfsd/nfsd.8 b/usr.sbin/nfsd/nfsd.8
new file mode 100644
index 0000000..8e13e72
--- /dev/null
+++ b/usr.sbin/nfsd/nfsd.8
@@ -0,0 +1,134 @@
+.\" Copyright (c) 1989, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)nfsd.8 8.4 (Berkeley) 3/29/95
+.\"
+.Dd March 29, 1995
+.Dt NFSD 8
+.Os
+.Sh NAME
+.Nm nfsd
+.Nd remote
+.Tn NFS
+server
+.Sh SYNOPSIS
+.Nm nfsd
+.Op Fl rut
+.Op Fl n Ar num_servers
+.Sh DESCRIPTION
+.Nm Nfsd
+runs on a server machine to service
+.Tn NFS
+requests from client machines.
+At least one
+.Nm nfsd
+must be running for a machine to operate as a server.
+.Pp
+Unless otherwise specified, four servers for
+.Tn UDP
+transport are started.
+.Pp
+The following options are available:
+.Bl -tag -width Ds
+.It Fl r
+Register the
+.Tn NFS
+service with
+.Xr portmap 8
+without creating any servers.
+This option can be used along with the
+.Fl u
+or
+.Fl t
+options to re-register NFS if the portmap server is restarted.
+.It Fl n
+Specifies how many servers to create.
+.It Fl t
+Serve
+.Tn TCP NFS
+clients.
+.It Fl u
+Serve
+.Tn UDP NFS
+clients.
+.El
+.Pp
+For example,
+.Dq Li "nfsd -u -t 6"
+serves
+.Tn UDP
+and
+.Tn TCP
+transports using six daemons.
+.Pp
+A server should run enough daemons to handle
+the maximum level of concurrency from its clients,
+typically four to six.
+.Pp
+.Nm Nfsd
+listens for service requests at the port indicated in the
+.Tn NFS
+server specification; see
+.%T "Network File System Protocol Specification" ,
+RFC1094 and
+.%T "NFS: Network File System Version 3 Protocol Specification" .
+.Pp
+If
+.Nm nfsd
+detects that
+.Tn NFS
+is not loaded in the running kernel, it will attempt
+to load a loadable kernel module containing
+.Tn NFS
+support using
+.Xr modload 8
+by way of
+.Xr vfsload 3 .
+If this fails, or no
+.Tn NFS
+LKM is available,
+.Nm nfsd
+will exit with an error.
+.Pp
+The
+.Nm nfsd
+utility exits 0 on success, and >0 if an error occurs.
+.Sh SEE ALSO
+.Xr nfsstat 1 ,
+.Xr nfssvc 2 ,
+.Xr modload 8 ,
+.Xr mountd 8 ,
+.Xr portmap 8
+.Sh HISTORY
+The
+.Nm nfsd
+utility first appeared in
+.Bx 4.4 .
diff --git a/usr.sbin/nfsd/nfsd.c b/usr.sbin/nfsd/nfsd.c
new file mode 100644
index 0000000..399b12e
--- /dev/null
+++ b/usr.sbin/nfsd/nfsd.c
@@ -0,0 +1,673 @@
+/*
+ * Copyright (c) 1989, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Rick Macklem at The University of Guelph.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1989, 1993, 1994\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif not lint
+
+#ifndef lint
+static char sccsid[] = "@(#)nfsd.c 8.9 (Berkeley) 3/29/95";
+#endif not lint
+
+#include <sys/param.h>
+#include <sys/syslog.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <sys/uio.h>
+#include <sys/ucred.h>
+#include <sys/mount.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+
+#include <rpc/rpc.h>
+#include <rpc/pmap_clnt.h>
+#include <rpc/pmap_prot.h>
+
+#ifdef ISO
+#include <netiso/iso.h>
+#endif
+#include <nfs/rpcv2.h>
+#include <nfs/nfsproto.h>
+#include <nfs/nfs.h>
+
+#ifdef NFSKERB
+#include <kerberosIV/des.h>
+#include <kerberosIV/krb.h>
+#endif
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <grp.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <unistd.h>
+#include <libutil.h>
+
+/* Global defs */
+#ifdef DEBUG
+#define syslog(e, s) fprintf(stderr,(s))
+int debug = 1;
+#else
+int debug = 0;
+#endif
+
+struct nfsd_srvargs nsd;
+#ifdef OLD_SETPROCTITLE
+char **Argv = NULL; /* pointer to argument vector */
+char *LastArg = NULL; /* end of argv */
+#endif
+
+#ifdef NFSKERB
+char lnam[ANAME_SZ];
+KTEXT_ST kt;
+AUTH_DAT kauth;
+char inst[INST_SZ];
+struct nfsrpc_fullblock kin, kout;
+struct nfsrpc_fullverf kverf;
+NFSKERBKEY_T kivec;
+struct timeval ktv;
+NFSKERBKEYSCHED_T kerb_keysched;
+#endif
+
+void nonfs __P((int));
+void reapchild __P((int));
+#ifdef OLD_SETPROCTITLE
+#ifdef __FreeBSD__
+void setproctitle __P((char *));
+#endif
+#endif
+void usage __P((void));
+
+/*
+ * Nfs server daemon mostly just a user context for nfssvc()
+ *
+ * 1 - do file descriptor and signal cleanup
+ * 2 - fork the nfsd(s)
+ * 3 - create server socket(s)
+ * 4 - register socket with portmap
+ *
+ * For connectionless protocols, just pass the socket into the kernel via.
+ * nfssvc().
+ * For connection based sockets, loop doing accepts. When you get a new
+ * socket from accept, pass the msgsock into the kernel via. nfssvc().
+ * The arguments are:
+ * -c - support iso cltp clients
+ * -r - reregister with portmapper
+ * -t - support tcp nfs clients
+ * -u - support udp nfs clients
+ * followed by "n" which is the number of nfsds' to fork off
+ */
+int
+main(argc, argv, envp)
+ int argc;
+ char *argv[], *envp[];
+{
+ extern int optind;
+ struct group *grp;
+ struct nfsd_args nfsdargs;
+ struct passwd *pwd;
+ struct ucred *cr;
+ struct sockaddr_in inetaddr, inetpeer;
+#ifdef ISO
+ struct sockaddr_iso isoaddr, isopeer;
+#endif
+ struct timeval ktv;
+ fd_set ready, sockbits;
+ int ch, cltpflag, connect_type_cnt, i, len, maxsock, msgsock;
+ int nfsdcnt, nfssvc_flag, on, reregister, sock, tcpflag, tcpsock;
+ int tp4cnt, tp4flag, tp4sock, tpipcnt, tpipflag, tpipsock, udpflag;
+ char *cp, **cpp;
+#ifdef __FreeBSD__
+ struct vfsconf vfc;
+ int error;
+
+ error = getvfsbyname("nfs", &vfc);
+ if (error && vfsisloadable("nfs")) {
+ if (vfsload("nfs"))
+ err(1, "vfsload(nfs)");
+ endvfsent(); /* flush cache */
+ error = getvfsbyname("nfs", &vfc);
+ }
+ if (error)
+ errx(1, "NFS is not available in the running kernel");
+#endif
+
+#ifdef OLD_SETPROCTITLE
+ /* Save start and extent of argv for setproctitle. */
+ Argv = argv;
+ if (envp == 0 || *envp == 0)
+ envp = argv;
+ while (*envp)
+ envp++;
+ LastArg = envp[-1] + strlen(envp[-1]);
+#endif
+
+#define MAXNFSDCNT 20
+#define DEFNFSDCNT 4
+ nfsdcnt = DEFNFSDCNT;
+ cltpflag = reregister = tcpflag = tp4cnt = tp4flag = tpipcnt = 0;
+ tpipflag = udpflag = 0;
+#ifdef ISO
+#define GETOPT "cn:rtu"
+#define USAGE "[-crtu] [-n num_servers]"
+#else
+#define GETOPT "n:rtu"
+#define USAGE "[-rtu] [-n num_servers]"
+#endif
+ while ((ch = getopt(argc, argv, GETOPT)) != -1)
+ switch (ch) {
+ case 'n':
+ nfsdcnt = atoi(optarg);
+ if (nfsdcnt < 1 || nfsdcnt > MAXNFSDCNT) {
+ warnx("nfsd count %d; reset to %d", DEFNFSDCNT);
+ nfsdcnt = DEFNFSDCNT;
+ }
+ break;
+ case 'r':
+ reregister = 1;
+ break;
+ case 't':
+ tcpflag = 1;
+ break;
+ case 'u':
+ udpflag = 1;
+ break;
+#ifdef ISO
+ case 'c':
+ cltpflag = 1;
+ break;
+#ifdef notyet
+ case 'i':
+ tp4cnt = 1;
+ break;
+ case 'p':
+ tpipcnt = 1;
+ break;
+#endif /* notyet */
+#endif /* ISO */
+ default:
+ case '?':
+ usage();
+ };
+ if (!tcpflag && !udpflag)
+ udpflag = 1;
+ argv += optind;
+ argc -= optind;
+
+ /*
+ * XXX
+ * Backward compatibility, trailing number is the count of daemons.
+ */
+ if (argc > 1)
+ usage();
+ if (argc == 1) {
+ nfsdcnt = atoi(argv[0]);
+ if (nfsdcnt < 1 || nfsdcnt > MAXNFSDCNT) {
+ warnx("nfsd count %d; reset to %d", DEFNFSDCNT);
+ nfsdcnt = DEFNFSDCNT;
+ }
+ }
+
+ if (debug == 0) {
+ daemon(0, 0);
+ (void)signal(SIGHUP, SIG_IGN);
+ (void)signal(SIGINT, SIG_IGN);
+ (void)signal(SIGQUIT, SIG_IGN);
+ (void)signal(SIGSYS, nonfs);
+ (void)signal(SIGTERM, SIG_IGN);
+ }
+ (void)signal(SIGCHLD, reapchild);
+
+ if (reregister) {
+ if (udpflag &&
+ (!pmap_set(RPCPROG_NFS, 2, IPPROTO_UDP, NFS_PORT) ||
+ !pmap_set(RPCPROG_NFS, 3, IPPROTO_UDP, NFS_PORT)))
+ err(1, "can't register with portmap for UDP.");
+ if (tcpflag &&
+ (!pmap_set(RPCPROG_NFS, 2, IPPROTO_TCP, NFS_PORT) ||
+ !pmap_set(RPCPROG_NFS, 3, IPPROTO_TCP, NFS_PORT)))
+ err(1, "can't register with portmap for TCP.");
+ exit(0);
+ }
+ openlog("nfsd:", LOG_PID, LOG_DAEMON);
+
+ for (i = 0; i < nfsdcnt; i++) {
+ switch (fork()) {
+ case -1:
+ syslog(LOG_ERR, "fork: %m");
+ exit (1);
+ case 0:
+ break;
+ default:
+ continue;
+ }
+
+ setproctitle("server");
+ nfssvc_flag = NFSSVC_NFSD;
+ nsd.nsd_nfsd = NULL;
+#ifdef NFSKERB
+ if (sizeof (struct nfsrpc_fullverf) != RPCX_FULLVERF ||
+ sizeof (struct nfsrpc_fullblock) != RPCX_FULLBLOCK)
+ syslog(LOG_ERR, "Yikes NFSKERB structs not packed!");
+ nsd.nsd_authstr = (u_char *)&kt;
+ nsd.nsd_authlen = sizeof (kt);
+ nsd.nsd_verfstr = (u_char *)&kverf;
+ nsd.nsd_verflen = sizeof (kverf);
+#endif
+ while (nfssvc(nfssvc_flag, &nsd) < 0) {
+ if (errno != ENEEDAUTH) {
+ syslog(LOG_ERR, "nfssvc: %m");
+ exit(1);
+ }
+ nfssvc_flag = NFSSVC_NFSD | NFSSVC_AUTHINFAIL;
+#ifdef NFSKERB
+ /*
+ * Get the Kerberos ticket out of the authenticator
+ * verify it and convert the principal name to a user
+ * name. The user name is then converted to a set of
+ * user credentials via the password and group file.
+ * Finally, decrypt the timestamp and validate it.
+ * For more info see the IETF Draft "Authentication
+ * in ONC RPC".
+ */
+ kt.length = ntohl(kt.length);
+ if (gettimeofday(&ktv, (struct timezone *)0) == 0 &&
+ kt.length > 0 && kt.length <=
+ (RPCAUTH_MAXSIZ - 3 * NFSX_UNSIGNED)) {
+ kin.w1 = NFS_KERBW1(kt);
+ kt.mbz = 0;
+ (void)strcpy(inst, "*");
+ if (krb_rd_req(&kt, NFS_KERBSRV,
+ inst, nsd.nsd_haddr, &kauth, "") == RD_AP_OK &&
+ krb_kntoln(&kauth, lnam) == KSUCCESS &&
+ (pwd = getpwnam(lnam)) != NULL) {
+ cr = &nsd.nsd_cr;
+ cr->cr_uid = pwd->pw_uid;
+ cr->cr_groups[0] = pwd->pw_gid;
+ cr->cr_ngroups = 1;
+ setgrent();
+ while ((grp = getgrent()) != NULL) {
+ if (grp->gr_gid == cr->cr_groups[0])
+ continue;
+ for (cpp = grp->gr_mem;
+ *cpp != NULL; ++cpp)
+ if (!strcmp(*cpp, lnam))
+ break;
+ if (*cpp == NULL)
+ continue;
+ cr->cr_groups[cr->cr_ngroups++]
+ = grp->gr_gid;
+ if (cr->cr_ngroups == NGROUPS)
+ break;
+ }
+ endgrent();
+
+ /*
+ * Get the timestamp verifier out of the
+ * authenticator and verifier strings.
+ */
+ kin.t1 = kverf.t1;
+ kin.t2 = kverf.t2;
+ kin.w2 = kverf.w2;
+ bzero((caddr_t)kivec, sizeof (kivec));
+ bcopy((caddr_t)kauth.session,
+ (caddr_t)nsd.nsd_key,sizeof(kauth.session));
+
+ /*
+ * Decrypt the timestamp verifier in CBC mode.
+ */
+ XXX
+
+ /*
+ * Validate the timestamp verifier, to
+ * check that the session key is ok.
+ */
+ nsd.nsd_timestamp.tv_sec = ntohl(kout.t1);
+ nsd.nsd_timestamp.tv_usec = ntohl(kout.t2);
+ nsd.nsd_ttl = ntohl(kout.w1);
+ if ((nsd.nsd_ttl - 1) == ntohl(kout.w2))
+ nfssvc_flag = NFSSVC_NFSD | NFSSVC_AUTHIN;
+ }
+#endif /* NFSKERB */
+ }
+ exit(0);
+ }
+
+ /* If we are serving udp, set up the socket. */
+ if (udpflag) {
+ if ((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+ syslog(LOG_ERR, "can't create udp socket");
+ exit(1);
+ }
+ inetaddr.sin_family = AF_INET;
+ inetaddr.sin_addr.s_addr = INADDR_ANY;
+ inetaddr.sin_port = htons(NFS_PORT);
+ inetaddr.sin_len = sizeof(inetaddr);
+ if (bind(sock,
+ (struct sockaddr *)&inetaddr, sizeof(inetaddr)) < 0) {
+ syslog(LOG_ERR, "can't bind udp addr");
+ exit(1);
+ }
+ if (!pmap_set(RPCPROG_NFS, 2, IPPROTO_UDP, NFS_PORT) ||
+ !pmap_set(RPCPROG_NFS, 3, IPPROTO_UDP, NFS_PORT)) {
+ syslog(LOG_ERR, "can't register with udp portmap");
+ exit(1);
+ }
+ nfsdargs.sock = sock;
+ nfsdargs.name = NULL;
+ nfsdargs.namelen = 0;
+ if (nfssvc(NFSSVC_ADDSOCK, &nfsdargs) < 0) {
+ syslog(LOG_ERR, "can't Add UDP socket");
+ exit(1);
+ }
+ (void)close(sock);
+ }
+
+#ifdef ISO
+ /* If we are serving cltp, set up the socket. */
+ if (cltpflag) {
+ if ((sock = socket(AF_ISO, SOCK_DGRAM, 0)) < 0) {
+ syslog(LOG_ERR, "can't create cltp socket");
+ exit(1);
+ }
+ memset(&isoaddr, 0, sizeof(isoaddr));
+ isoaddr.siso_family = AF_ISO;
+ isoaddr.siso_tlen = 2;
+ cp = TSEL(&isoaddr);
+ *cp++ = (NFS_PORT >> 8);
+ *cp = (NFS_PORT & 0xff);
+ isoaddr.siso_len = sizeof(isoaddr);
+ if (bind(sock,
+ (struct sockaddr *)&isoaddr, sizeof(isoaddr)) < 0) {
+ syslog(LOG_ERR, "can't bind cltp addr");
+ exit(1);
+ }
+#ifdef notyet
+ /*
+ * XXX
+ * Someday this should probably use "rpcbind", the son of
+ * portmap.
+ */
+ if (!pmap_set(RPCPROG_NFS, NFS_VER2, IPPROTO_UDP, NFS_PORT)) {
+ syslog(LOG_ERR, "can't register with udp portmap");
+ exit(1);
+ }
+#endif /* notyet */
+ nfsdargs.sock = sock;
+ nfsdargs.name = NULL;
+ nfsdargs.namelen = 0;
+ if (nfssvc(NFSSVC_ADDSOCK, &nfsdargs) < 0) {
+ syslog(LOG_ERR, "can't add UDP socket");
+ exit(1);
+ }
+ close(sock);
+ }
+#endif /* ISO */
+
+ /* Now set up the master server socket waiting for tcp connections. */
+ on = 1;
+ FD_ZERO(&sockbits);
+ connect_type_cnt = 0;
+ if (tcpflag) {
+ if ((tcpsock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
+ syslog(LOG_ERR, "can't create tcp socket");
+ exit(1);
+ }
+ if (setsockopt(tcpsock,
+ SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)) < 0)
+ syslog(LOG_ERR, "setsockopt SO_REUSEADDR: %m");
+ inetaddr.sin_family = AF_INET;
+ inetaddr.sin_addr.s_addr = INADDR_ANY;
+ inetaddr.sin_port = htons(NFS_PORT);
+ inetaddr.sin_len = sizeof(inetaddr);
+ if (bind(tcpsock,
+ (struct sockaddr *)&inetaddr, sizeof (inetaddr)) < 0) {
+ syslog(LOG_ERR, "can't bind tcp addr");
+ exit(1);
+ }
+ if (listen(tcpsock, 5) < 0) {
+ syslog(LOG_ERR, "listen failed");
+ exit(1);
+ }
+ if (!pmap_set(RPCPROG_NFS, 2, IPPROTO_TCP, NFS_PORT) ||
+ !pmap_set(RPCPROG_NFS, 3, IPPROTO_TCP, NFS_PORT)) {
+ syslog(LOG_ERR, "can't register tcp with portmap");
+ exit(1);
+ }
+ FD_SET(tcpsock, &sockbits);
+ maxsock = tcpsock;
+ connect_type_cnt++;
+ }
+
+#ifdef notyet
+ /* Now set up the master server socket waiting for tp4 connections. */
+ if (tp4flag) {
+ if ((tp4sock = socket(AF_ISO, SOCK_SEQPACKET, 0)) < 0) {
+ syslog(LOG_ERR, "can't create tp4 socket");
+ exit(1);
+ }
+ if (setsockopt(tp4sock,
+ SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)) < 0)
+ syslog(LOG_ERR, "setsockopt SO_REUSEADDR: %m");
+ memset(&isoaddr, 0, sizeof(isoaddr));
+ isoaddr.siso_family = AF_ISO;
+ isoaddr.siso_tlen = 2;
+ cp = TSEL(&isoaddr);
+ *cp++ = (NFS_PORT >> 8);
+ *cp = (NFS_PORT & 0xff);
+ isoaddr.siso_len = sizeof(isoaddr);
+ if (bind(tp4sock,
+ (struct sockaddr *)&isoaddr, sizeof (isoaddr)) < 0) {
+ syslog(LOG_ERR, "can't bind tp4 addr");
+ exit(1);
+ }
+ if (listen(tp4sock, 5) < 0) {
+ syslog(LOG_ERR, "listen failed");
+ exit(1);
+ }
+ /*
+ * XXX
+ * Someday this should probably use "rpcbind", the son of
+ * portmap.
+ */
+ if (!pmap_set(RPCPROG_NFS, NFS_VER2, IPPROTO_TCP, NFS_PORT)) {
+ syslog(LOG_ERR, "can't register tcp with portmap");
+ exit(1);
+ }
+ FD_SET(tp4sock, &sockbits);
+ maxsock = tp4sock;
+ connect_type_cnt++;
+ }
+
+ /* Now set up the master server socket waiting for tpip connections. */
+ if (tpipflag) {
+ if ((tpipsock = socket(AF_INET, SOCK_SEQPACKET, 0)) < 0) {
+ syslog(LOG_ERR, "can't create tpip socket");
+ exit(1);
+ }
+ if (setsockopt(tpipsock,
+ SOL_SOCKET, SO_REUSEADDR, (char *)&on, sizeof(on)) < 0)
+ syslog(LOG_ERR, "setsockopt SO_REUSEADDR: %m");
+ inetaddr.sin_family = AF_INET;
+ inetaddr.sin_addr.s_addr = INADDR_ANY;
+ inetaddr.sin_port = htons(NFS_PORT);
+ inetaddr.sin_len = sizeof(inetaddr);
+ if (bind(tpipsock,
+ (struct sockaddr *)&inetaddr, sizeof (inetaddr)) < 0) {
+ syslog(LOG_ERR, "can't bind tcp addr");
+ exit(1);
+ }
+ if (listen(tpipsock, 5) < 0) {
+ syslog(LOG_ERR, "listen failed");
+ exit(1);
+ }
+ /*
+ * XXX
+ * Someday this should probably use "rpcbind", the son of
+ * portmap.
+ */
+ if (!pmap_set(RPCPROG_NFS, NFS_VER2, IPPROTO_TCP, NFS_PORT)) {
+ syslog(LOG_ERR, "can't register tcp with portmap");
+ exit(1);
+ }
+ FD_SET(tpipsock, &sockbits);
+ maxsock = tpipsock;
+ connect_type_cnt++;
+ }
+#endif /* notyet */
+
+ if (connect_type_cnt == 0)
+ exit(0);
+
+ setproctitle("master");
+
+ /*
+ * Loop forever accepting connections and passing the sockets
+ * into the kernel for the mounts.
+ */
+ for (;;) {
+ ready = sockbits;
+ if (connect_type_cnt > 1) {
+ if (select(maxsock + 1,
+ &ready, NULL, NULL, NULL) < 1) {
+ syslog(LOG_ERR, "select failed: %m");
+ exit(1);
+ }
+ }
+ if (tcpflag && FD_ISSET(tcpsock, &ready)) {
+ len = sizeof(inetpeer);
+ if ((msgsock = accept(tcpsock,
+ (struct sockaddr *)&inetpeer, &len)) < 0) {
+ syslog(LOG_ERR, "accept failed: %m");
+ exit(1);
+ }
+ memset(inetpeer.sin_zero, 0, sizeof(inetpeer.sin_zero));
+ if (setsockopt(msgsock, SOL_SOCKET,
+ SO_KEEPALIVE, (char *)&on, sizeof(on)) < 0)
+ syslog(LOG_ERR,
+ "setsockopt SO_KEEPALIVE: %m");
+ nfsdargs.sock = msgsock;
+ nfsdargs.name = (caddr_t)&inetpeer;
+ nfsdargs.namelen = sizeof(inetpeer);
+ nfssvc(NFSSVC_ADDSOCK, &nfsdargs);
+ (void)close(msgsock);
+ }
+#ifdef notyet
+ if (tp4flag && FD_ISSET(tp4sock, &ready)) {
+ len = sizeof(isopeer);
+ if ((msgsock = accept(tp4sock,
+ (struct sockaddr *)&isopeer, &len)) < 0) {
+ syslog(LOG_ERR, "accept failed: %m");
+ exit(1);
+ }
+ if (setsockopt(msgsock, SOL_SOCKET,
+ SO_KEEPALIVE, (char *)&on, sizeof(on)) < 0)
+ syslog(LOG_ERR,
+ "setsockopt SO_KEEPALIVE: %m");
+ nfsdargs.sock = msgsock;
+ nfsdargs.name = (caddr_t)&isopeer;
+ nfsdargs.namelen = len;
+ nfssvc(NFSSVC_ADDSOCK, &nfsdargs);
+ (void)close(msgsock);
+ }
+ if (tpipflag && FD_ISSET(tpipsock, &ready)) {
+ len = sizeof(inetpeer);
+ if ((msgsock = accept(tpipsock,
+ (struct sockaddr *)&inetpeer, &len)) < 0) {
+ syslog(LOG_ERR, "Accept failed: %m");
+ exit(1);
+ }
+ if (setsockopt(msgsock, SOL_SOCKET,
+ SO_KEEPALIVE, (char *)&on, sizeof(on)) < 0)
+ syslog(LOG_ERR, "setsockopt SO_KEEPALIVE: %m");
+ nfsdargs.sock = msgsock;
+ nfsdargs.name = (caddr_t)&inetpeer;
+ nfsdargs.namelen = len;
+ nfssvc(NFSSVC_ADDSOCK, &nfsdargs);
+ (void)close(msgsock);
+ }
+#endif /* notyet */
+ }
+}
+
+void
+usage()
+{
+ (void)fprintf(stderr, "usage: nfsd %s\n", USAGE);
+ exit(1);
+}
+
+void
+nonfs(signo)
+ int signo;
+{
+ syslog(LOG_ERR, "missing system call: NFS not available.");
+}
+
+void
+reapchild(signo)
+ int signo;
+{
+
+ while (wait3(NULL, WNOHANG, NULL) > 0);
+}
+
+#ifdef OLD_SETPROCTITLE
+#ifdef __FreeBSD__
+void
+setproctitle(a)
+ char *a;
+{
+ register char *cp;
+ char buf[80];
+
+ cp = Argv[0];
+ (void)snprintf(buf, sizeof(buf), "nfsd-%s", a);
+ (void)strncpy(cp, buf, LastArg - cp);
+ cp += strlen(cp);
+ while (cp < LastArg)
+ *cp++ = '\0';
+ Argv[1] = NULL;
+}
+#endif /* __FreeBSD__ */
+#endif
diff --git a/usr.sbin/nologin/Makefile b/usr.sbin/nologin/Makefile
new file mode 100644
index 0000000..e30ea08
--- /dev/null
+++ b/usr.sbin/nologin/Makefile
@@ -0,0 +1,12 @@
+# @(#)Makefile 8.2 (Berkeley) 4/22/94
+
+MAN8= nologin.8
+MAN5= nologin.5
+
+nologin depend lint tags:
+
+beforeinstall:
+ ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \
+ ${.CURDIR}/nologin.sh ${DESTDIR}${BINDIR}/nologin
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/nologin/nologin.5 b/usr.sbin/nologin/nologin.5
new file mode 100644
index 0000000..70b7392
--- /dev/null
+++ b/usr.sbin/nologin/nologin.5
@@ -0,0 +1,64 @@
+.\" 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
+.\"
+.Dd June 19, 1993
+.Dt NOLOGIN 5
+.Os FreeBSD 2.1
+.Sh NAME
+.Nm nologin
+.Nd disallow logins
+.Sh DESCRIPTION
+.Nm Nologin
+disallows logins if the file
+.Pa /etc/nologin
+exists.
+Programs display the contents of
+.Pa /etc/nologin
+to the user and exit.
+.Sh SECURITY
+Ignored by
+.Xr login 1
+for user root.
+.Sh FILES
+.Bl -tag -width /etc/nologinxxx -compact
+.It Pa /etc/nologin
+The
+.Nm nologin
+file resides in
+.Pa /etc .
+.El
+.Sh SEE ALSO
+.Xr login 1 ,
+.Xr rlogin 1 ,
+.Xr telnet 1 ,
+.Xr shutdown 8
diff --git a/usr.sbin/nologin/nologin.8 b/usr.sbin/nologin/nologin.8
new file mode 100644
index 0000000..32a7e73
--- /dev/null
+++ b/usr.sbin/nologin/nologin.8
@@ -0,0 +1,54 @@
+.\" 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
+.\"
+.Dd June 19, 1993
+.Dt NOLOGIN 8
+.Os BSD 4.4
+.Sh NAME
+.Nm nologin
+.Nd politely refuse a login
+.Sh SYNOPSIS
+.Nm nologin
+.Sh DESCRIPTION
+.Nm Nologin
+displays a message that an account is not available and
+exits non-zero.
+It is intended as a replacement shell field for accounts that
+have been disabled.
+.Sh SEE ALSO
+.Xr login 1
+.Sh HISTORY
+The
+.Nm nologin
+command appeared in
+.Bx 4.4 .
diff --git a/usr.sbin/nologin/nologin.sh b/usr.sbin/nologin/nologin.sh
new file mode 100644
index 0000000..a396e9c
--- /dev/null
+++ b/usr.sbin/nologin/nologin.sh
@@ -0,0 +1,38 @@
+#!/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
+#
+
+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..d65edfd
--- /dev/null
+++ b/usr.sbin/nslookup/Makefile
@@ -0,0 +1,22 @@
+# $Id$
+
+.include "${.CURDIR}/../named/Makefile.inc"
+
+.PATH: ${BIND_DIR}/tools/nslookup
+.PATH: ${BIND_DIR}/man
+
+PROG= nslookup
+SRCS= main.c getinfo.c debug.c send.c skip.c list.c subr.c commands.c
+MAN8= nslookup.8
+
+LDADD= -ll
+DPADD= ${LIBL}
+
+CLEANFILES+= commands.c lex.yy.c lex.yy.o
+
+beforeinstall:
+ ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 444 \
+ ${BIND_DIR}/tools/nslookup/nslookup.help \
+ ${DESTDIR}/usr/share/misc/nslookup.help
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/pccard/Makefile b/usr.sbin/pccard/Makefile
new file mode 100644
index 0000000..ad64ec1
--- /dev/null
+++ b/usr.sbin/pccard/Makefile
@@ -0,0 +1,7 @@
+#
+# Makefile for xntpd.
+# $Id$
+#
+SUBDIR= pccardc pccardd
+
+.include <bsd.subdir.mk>
diff --git a/usr.sbin/pccard/Makefile.inc b/usr.sbin/pccard/Makefile.inc
new file mode 100644
index 0000000..3d73c9e
--- /dev/null
+++ b/usr.sbin/pccard/Makefile.inc
@@ -0,0 +1,2 @@
+CFLAGS+= -Wall -g -static
+BINDIR?= /usr/sbin
diff --git a/usr.sbin/pccard/pccardc/Makefile b/usr.sbin/pccard/pccardc/Makefile
new file mode 100644
index 0000000..28683df
--- /dev/null
+++ b/usr.sbin/pccard/pccardc/Makefile
@@ -0,0 +1,14 @@
+#
+# pccardc Makefile
+#
+# $Id$
+#
+PROG= pccardc
+NOMAN= noman
+SRCS= dumpcis.c enabler.c pccardc.c pccardmem.c printcis.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/dumpcis.c b/usr.sbin/pccard/pccardc/dumpcis.c
new file mode 100644
index 0000000..805199e
--- /dev/null
+++ b/usr.sbin/pccard/pccardc/dumpcis.c
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 1995 Andrew McRae. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id$";
+#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/card.h>
+#include <pccard/cis.h>
+#include "readcis.h"
+
+int nocards;
+
+void
+scan(slot)
+ int slot;
+{
+ int fd;
+ char name[64];
+ struct cis *cp;
+ struct slotstate st;
+
+ sprintf(name, CARD_DEVICE, slot);
+ fd = open(name, 0);
+ if (fd < 0)
+ return;
+ nocards++;
+ ioctl(fd, PIOCGSTATE, &st);
+ 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..ddd1dfd
--- /dev/null
+++ b/usr.sbin/pccard/pccardc/enabler.c
@@ -0,0 +1,153 @@
+/*
+ * Copyright (c) 1995 Andrew McRae. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id: enabler.c,v 1.8 1997/10/06 11:35:54 charnier Exp $";
+#endif /* not lint */
+
+#include <err.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+
+#include <pccard/card.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, 2);
+ 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;
+{
+ warnx("enabler: %s", msg);
+ fprintf(stderr,
+"usage: pccardc enabler slot driver [-m addr size] [-a iobase] [-i irq]\n");
+ fprintf(stderr,
+" -m card addr size : card address (hex), host address (hex) & size (Kb)\n");
+ fprintf(stderr,
+" -a iobase : I/O port address (hex)\n");
+ fprintf(stderr,
+" -i irq : interrupt request number (1-15)\n");
+ fprintf(stderr,
+" Example: enabler 0 ed0 -m 2000 d4000 16 -a 300 -i 3\n");
+ exit(1);
+}
diff --git a/usr.sbin/pccard/pccardc/pccardc.c b/usr.sbin/pccard/pccardc/pccardc.c
new file mode 100644
index 0000000..e18d66c
--- /dev/null
+++ b/usr.sbin/pccard/pccardc/pccardc.c
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 1995 Andrew McRae. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id$";
+#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(dumpcis_main);
+DECL(enabler_main);
+DECL(help_main);
+DECL(pccardmem_main);
+DECL(rdmap_main);
+DECL(rdreg_main);
+DECL(wrattr_main);
+DECL(wrreg_main);
+
+struct {
+ char *name;
+ main_t func;
+ char *help;
+} subcommands[] = {
+ { "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" },
+ { "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..059a571
--- /dev/null
+++ b/usr.sbin/pccard/pccardc/pccardmem.c
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 1995 Andrew McRae. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#include <err.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+
+#include <pccard/card.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, 0);
+ 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))
+ warn("ioctl");
+ else
+ printf("PCCARD Memory address set to 0x%x\n", addr);
+ exit(0);
+}
diff --git a/usr.sbin/pccard/pccardc/printcis.c b/usr.sbin/pccard/pccardc/printcis.c
new file mode 100644
index 0000000..961485c
--- /dev/null
+++ b/usr.sbin/pccard/pccardc/printcis.c
@@ -0,0 +1,703 @@
+/*
+ * Copyright (c) 1995 Andrew McRae. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+
+#include <pccard/card.h>
+#include <pccard/cis.h>
+
+#include "readcis.h"
+
+int dump_pwr_desc(unsigned char *);
+void print_ext_speed(unsigned char, int);
+void dump_device_desc(unsigned char *p, int len, char *type);
+void dump_info_v1(unsigned char *p, int len);
+void dump_config_map(struct tuple *tp);
+void dump_cis_config(struct tuple *tp);
+void dump_other_cond(unsigned char *p);
+void dump_func_ext(unsigned char *p, int len);
+
+void
+dumpcis(struct cis *cp)
+{
+ struct tuple *tp;
+ struct tuple_list *tl;
+ int count = 0, sz, ad, i;
+ unsigned char *p;
+
+ for (tl = cp->tlist; tl; tl = tl->next)
+ for (tp = tl->tuples; tp; tp = tp->next) {
+ printf("Tuple #%d, code = 0x%x (%s), length = %d\n",
+ ++count, tp->code, tuple_name(tp->code), tp->length);
+ p = tp->data;
+ sz = tp->length;
+ ad = 0;
+ while (sz > 0) {
+ printf(" %03x: ", ad);
+ for (i = 0; i < ((sz < 16) ? sz : 16); i++)
+ printf(" %02x", p[i]);
+ printf("\n");
+ sz -= 16;
+ p += 16;
+ ad += 16;
+ }
+ switch (tp->code) {
+ default:
+ break;
+ case CIS_MEM_COMMON: /* 0x01 */
+ dump_device_desc(tp->data, tp->length, "Common");
+ break;
+ case CIS_CHECKSUM: /* 0x10 */
+ if (tp->length == 5) {
+ printf("\tChecksum from offset %d, length %d, value is 0x%x\n",
+ (short)((tp->data[1] << 8) | tp->data[0]),
+ (tp->data[3] << 8) | tp->data[2],
+ tp->data[4]);
+ } else
+ printf("\tIllegal length for checksum!\n");
+ break;
+ case CIS_LONGLINK_A: /* 0x11 */
+ printf("\tLong link to attribute memory, address 0x%x\n",
+ (tp->data[3] << 24) |
+ (tp->data[2] << 16) |
+ (tp->data[1] << 8) |
+ tp->data[0]);
+ break;
+ case CIS_LONGLINK_C: /* 0x12 */
+ printf("\tLong link to common memory, address 0x%x\n",
+ (tp->data[3] << 24) |
+ (tp->data[2] << 16) |
+ (tp->data[1] << 8) |
+ tp->data[0]);
+ break;
+ break;
+ case CIS_INFO_V1: /* 0x15 */
+ dump_info_v1(tp->data, tp->length);
+ break;
+ case CIS_ALTSTR: /* 0x16 */
+ break;
+ case CIS_MEM_ATTR: /* 0x17 */
+ dump_device_desc(tp->data, tp->length, "Attribute");
+ break;
+ case CIS_JEDEC_C: /* 0x18 */
+ break;
+ case CIS_JEDEC_A: /* 0x19 */
+ break;
+ case CIS_CONF_MAP: /* 0x1A */
+ dump_config_map(tp);
+ break;
+ case CIS_CONFIG: /* 0x1B */
+ dump_cis_config(tp);
+ break;
+ case CIS_DEVICE_OC: /* 0x1C */
+ dump_other_cond(tp->data);
+ break;
+ case CIS_DEVICE_OA: /* 0x1D */
+ dump_other_cond(tp->data);
+ break;
+ case CIS_DEVICEGEO: /* 0x1E */
+ break;
+ case CIS_DEVICEGEO_A: /* 0x1F */
+ break;
+ case CIS_MANUF_ID: /* 0x20 */
+ printf("\tPCMCIA ID = 0x%x, OEM ID = 0x%x\n",
+ (tp->data[1] << 8) | tp->data[0],
+ (tp->data[3] << 8) | tp->data[2]);
+ break;
+ case CIS_FUNC_ID: /* 0x21 */
+ switch (tp->data[0]) {
+ default:
+ printf("\tUnknown function");
+ break;
+ case 0:
+ printf("\tMultifunction card");
+ break;
+ case 1:
+ printf("\tMemory card");
+ break;
+ case 2:
+ printf("\tSerial port/modem");
+ break;
+ case 3:
+ printf("\tParallel port");
+ break;
+ case 4:
+ printf("\tFixed disk card");
+ break;
+ case 5:
+ printf("\tVideo adapter");
+ break;
+ case 6:
+ printf("\tNetwork/LAN adapter");
+ break;
+ case 7:
+ printf("\tAIMS");
+ break;
+ }
+ printf("%s%s\n", (tp->data[1] & 1) ? " - POST initialize" : "",
+ (tp->data[1] & 2) ? " - Card has ROM" : "");
+ break;
+ case CIS_FUNC_EXT: /* 0x22 */
+ dump_func_ext(tp->data, tp->length);
+ break;
+ case CIS_VERS_2: /* 0x40 */
+ break;
+ }
+ }
+}
+
+/*
+ * Dump configuration map tuple.
+ */
+void
+dump_config_map(struct tuple *tp)
+{
+ unsigned char *p, x;
+ int rlen, mlen;
+ int i;
+ union {
+ unsigned long l;
+ unsigned char b[4];
+ } u;
+
+ rlen = (tp->data[0] & 3) + 1;
+ mlen = ((tp->data[0] >> 2) & 3) + 1;
+ u.l = 0;
+ p = tp->data + 2;
+ for (i = 0; i < rlen; i++)
+ u.b[i] = *p++;
+ printf("\tReg len = %d, config register addr = 0x%lx, last config = 0x%x\n",
+ rlen, u.l, tp->data[1]);
+ if (mlen)
+ printf("\tRegisters: ");
+ for (i = 0; i < mlen; i++, p++) {
+ for (x = 0x1; x; x <<= 1)
+ printf("%c", x & *p ? 'X' : '-');
+ printf(" ");
+ }
+ printf("\n");
+}
+
+/*
+ * Dump a config entry.
+ */
+void
+dump_cis_config(struct tuple *tp)
+{
+ unsigned char *p, feat;
+ int i, j;
+ char c;
+
+ p = tp->data;
+ printf("\tConfig index = 0x%x%s\n", *p & 0x3F,
+ *p & 0x40 ? "(default)" : "");
+ if (*p & 0x80) {
+ p++;
+ printf("\tInterface byte = 0x%x ", *p);
+ switch (*p & 0xF) {
+ default:
+ printf("(reserved)");
+ break;
+ case 0:
+ printf("(memory)");
+ break;
+ case 1:
+ printf("(I/O)");
+ break;
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ case 8:
+ printf("(custom)");
+ break;
+ }
+ c = ' ';
+ if (*p & 0x10) {
+ printf(" BVD1/2 active");
+ c = ',';
+ }
+ if (*p & 0x20) {
+ printf("%c card WP active", c); /* Write protect */
+ c = ',';
+ }
+ if (*p & 0x40) {
+ printf("%c +RDY/-BSY active", c);
+ c = ',';
+ }
+ if (*p & 0x80)
+ printf("%c wait signal supported", c);
+ printf("\n");
+ }
+ p++;
+ feat = *p++;
+ switch (CIS_FEAT_POWER(feat)) {
+ case 0:
+ break;
+ case 1:
+ printf("\tVcc pwr:\n");
+ p += dump_pwr_desc(p);
+ break;
+ case 2:
+ printf("\tVcc pwr:\n");
+ p += dump_pwr_desc(p);
+ printf("\tVpp pwr:\n");
+ p += dump_pwr_desc(p);
+ break;
+ case 3:
+ printf("\tVcc pwr:\n");
+ p += dump_pwr_desc(p);
+ printf("\tVpp1 pwr:\n");
+ p += dump_pwr_desc(p);
+ printf("\tVpp2 pwr:\n");
+ p += dump_pwr_desc(p);
+ break;
+ }
+ if (feat & CIS_FEAT_TIMING) {
+ i = CIS_WAIT_SCALE(*p);
+ j = CIS_READY_SCALE(*p);
+ p++;
+ if (i != 3) {
+ printf("\tWait scale ");
+ print_ext_speed(*p, i);
+ while (*p & 0x80)
+ p++;
+ printf("\n");
+ }
+ if (j != 7) {
+ printf("\tRDY/BSY scale ");
+ print_ext_speed(*p, j);
+ while (*p & 0x80)
+ p++;
+ printf("\n");
+ }
+ }
+ if (feat & CIS_FEAT_I_O) {
+ if (CIS_IO_ADDR(*p))
+ printf("\tCard decodes %d address lines",
+ CIS_IO_ADDR(*p));
+ else
+ printf("\tCard provides address decode");
+ switch (CIS_MEM_ADDRSZ(*p)) {
+ case 0:
+ break;
+ case 1:
+ printf(", 8 Bit I/O only");
+ break;
+ case 2:
+ printf(", limited 8/16 Bit I/O");
+ break;
+ case 3:
+ printf(", full 8/16 Bit I/O");
+ break;
+ }
+ printf("\n");
+ if (*p & CIS_IO_RANGE) {
+ p++;
+ c = *p++;
+ for (i = 0; i <= CIS_IO_BLKS(c); i++) {
+ printf("\t\tI/O address # %d: ", i + 1);
+ switch (CIS_IO_ADSZ(c)) {
+ case 0:
+ break;
+ case 1:
+ printf("block start = 0x%x", *p++);
+ break;
+ case 2:
+ printf("block start = 0x%x", (p[1] << 8) | *p);
+ p += 2;
+ break;
+ case 3:
+ printf("block start = 0x%x",
+ (p[3] << 24) | (p[2] << 16) |
+ (p[1] << 8) | *p);
+ p += 4;
+ break;
+ }
+ switch (CIS_IO_BLKSZ(c)) {
+ case 0:
+ break;
+ case 1:
+ printf(" block length = 0x%x", *p++ + 1);
+ break;
+ case 2:
+ printf(" block length = 0x%x", ((p[1] << 8) | *p) + 1);
+ p += 2;
+ break;
+ case 3:
+ printf(" block length = 0x%x",
+ ((p[3] << 24) | (p[2] << 16) |
+ (p[1] << 8) | *p) + 1);
+ p += 4;
+ break;
+ }
+ printf("\n");
+ }
+ }
+ }
+
+ /* IRQ descriptor */
+ if (feat & CIS_FEAT_IRQ) {
+ printf("\t\tIRQ modes:");
+ c = ' ';
+ if (*p & CIS_IRQ_LEVEL) {
+ printf(" Level");
+ c = ',';
+ }
+ if (*p & CIS_IRQ_PULSE) {
+ printf("%c Pulse", c);
+ c = ',';
+ }
+ if (*p & CIS_IRQ_SHARING)
+ printf("%c Shared", c);
+ printf("\n");
+ if (*p & CIS_IRQ_MASK) {
+ i = p[0] | (p[1] << 8);
+ printf("\t\tIRQs: ");
+ if (*p & 1)
+ printf(" NMI");
+ if (*p & 0x2)
+ printf(" IOCK");
+ if (*p & 0x4)
+ printf(" BERR");
+ if (*p & 0x8)
+ printf(" VEND");
+ for (j = 0; j < 16; j++)
+ if (i & (1 << j))
+ printf(" %d", j);
+ printf("\n");
+ p += 3;
+ } else {
+ printf("\t\tIRQ level = %d\n", CIS_IRQ_IRQN(*p));
+ p++;
+ }
+ }
+ switch (CIS_FEAT_MEMORY(feat)) {
+ case 0:
+ break;
+ case 1:
+ printf("\tMemory space length = 0x%x\n", (p[1] << 8) | p[0]);
+ p += 2;
+ break;
+ case 2:
+ printf("\tMemory space address = 0x%x, length = 0x%x\n",
+ (p[3] << 8) | p[2],
+ (p[1] << 8) | p[0]);
+ p += 4;
+ break;
+
+ /* Memory descriptors. */
+ case 3:
+ c = *p++;
+ for (i = 0; i <= (c & 7); i++) {
+ printf("\tMemory descriptor %d\n\t\t", i + 1);
+ switch (CIS_MEM_LENSZ(c)) {
+ case 0:
+ break;
+ case 1:
+ printf(" blk length = 0x%x00", *p++);
+ break;
+ case 2:
+ printf(" blk length = 0x%x00", (p[1] << 8) | *p);
+ p += 2;
+ break;
+ case 3:
+ printf(" blk length = 0x%x00",
+ (p[3] << 24) | (p[2] << 16) |
+ (p[1] << 8) | *p);
+ p += 4;
+ break;
+ }
+ switch (CIS_MEM_ADDRSZ(c)) {
+ case 0:
+ break;
+ case 1:
+ printf(" card addr = 0x%x00", *p++);
+ break;
+ case 2:
+ printf(" card addr = 0x%x00", (p[1] << 8) | *p);
+ p += 2;
+ break;
+ case 3:
+ printf(" card addr = 0x%x00",
+ (p[3] << 24) | (p[2] << 16) |
+ (p[1] << 8) | *p);
+ p += 4;
+ break;
+ }
+ if (c & CIS_MEM_HOST)
+ switch ((c >> 5) & 3) {
+ case 0:
+ break;
+ case 1:
+ printf(" host addr = 0x%x00", *p++);
+ break;
+ case 2:
+ printf(" host addr = 0x%x00", (p[1] << 8) | *p);
+ p += 2;
+ break;
+ case 3:
+ printf(" host addr = 0x%x00",
+ (p[3] << 24) | (p[2] << 16) |
+ (p[1] << 8) | *p);
+ p += 4;
+ break;
+ }
+ printf("\n");
+ }
+ break;
+ }
+ if (feat & CIS_FEAT_MISC) {
+ printf("\tMax twin cards = %d\n", *p & 7);
+ printf("\tMisc attr:");
+ if (*p & 0x8)
+ printf(" (Audio-BVD2)");
+ if (*p & 0x10)
+ printf(" (Read-only)");
+ if (*p & 0x20)
+ printf(" (Power down supported)");
+ if (*p & 0x80) {
+ printf(" (Ext byte = 0x%x)", p[1]);
+ p++;
+ }
+ printf("\n");
+ p++;
+ }
+}
+
+/*
+ * dump_other_cond - Dump other conditions.
+ */
+void
+dump_other_cond(unsigned char *p)
+{
+ if (p[0]) {
+ printf("\t");
+ if (p[0] & 1)
+ printf("(MWAIT)");
+ if (p[0] & 2)
+ printf(" (3V card)");
+ if (p[0] & 0x80)
+ printf(" (Extension bytes follow)");
+ printf("\n");
+ }
+}
+
+/*
+ * Dump power descriptor.
+ */
+int
+dump_pwr_desc(unsigned char *p)
+{
+ int len = 1, i;
+ unsigned char mask;
+ char **expp;
+ static char *pname[] =
+ {"Nominal operating supply voltage",
+ "Minimum operating supply voltage",
+ "Maximum operating supply voltage",
+ "Continuous supply current",
+ "Max current average over 1 second",
+ "Max current average over 10 ms",
+ "Power down supply current",
+ "Reserved"
+ };
+ static char *vexp[] =
+ {"10uV", "100uV", "1mV", "10mV", "100mV", "1V", "10V", "100V"};
+ static char *cexp[] =
+ {"10nA", "1uA", "10uA", "100uA", "1mA", "10mA", "100mA", "1A"};
+ static char *mant[] =
+ {"1", "1.2", "1.3", "1.5", "2", "2.5", "3", "3.5", "4", "4.5",
+ "5", "5.5", "6", "7", "8", "9"};
+
+ mask = *p++;
+ expp = vexp;
+ for (i = 0; i < 8; i++)
+ if (mask & (1 << i)) {
+ len++;
+ if (i >= 3)
+ expp = cexp;
+ printf("\t\t%s: ", pname[i]);
+ printf("%s x %s",
+ mant[(*p >> 3) & 0xF],
+ expp[*p & 7]);
+ while (*p & 0x80) {
+ len++;
+ p++;
+ printf(", ext = 0x%x", *p);
+ }
+ printf("\n");
+ p++;
+ }
+ return (len);
+}
+
+void
+dump_device_desc(unsigned char *p, int len, char *type)
+{
+ static char *un_name[] =
+ {"512b", "2Kb", "8Kb", "32Kb", "128Kb", "512Kb", "2Mb", "reserved"};
+ static char *speed[] =
+ {"No speed", "250nS", "200nS", "150nS",
+ "100nS", "Reserved", "Reserved"};
+ static char *dev[] =
+ {"No device", "Mask ROM", "OTPROM", "UV EPROM",
+ "EEPROM", "FLASH EEPROM", "SRAM", "DRAM",
+ "Reserved", "Reserved", "Reserved", "Reserved",
+ "Reserved", "Function specific", "Extended",
+ "Reserved"};
+ int count = 0;
+
+ while (*p != 0xFF && len > 0) {
+ unsigned char x;
+
+ x = *p++;
+ len -= 2;
+ if (count++ == 0)
+ printf("\t%s memory device information:\n", type);
+ printf("\t\tDevice number %d, type %s, WPS = %s\n",
+ count, dev[x >> 4], (x & 0x8) ? "ON" : "OFF");
+ if ((x & 7) == 7) {
+ len--;
+ if (*p) {
+ printf("\t\t");
+ print_ext_speed(*p, 0);
+ while (*p & 0x80) {
+ p++;
+ len--;
+ }
+ }
+ p++;
+ } else
+ printf("\t\tSpeed = %s", speed[x & 7]);
+ printf(", Memory block size = %s, %d units\n",
+ un_name[*p & 7], (*p >> 3) + 1);
+ p++;
+ }
+}
+
+/*
+ * Print version info
+ */
+void
+dump_info_v1(unsigned char *p, int len)
+{
+ printf("\tVersion = %d.%d", p[0], p[1]);
+ p += 2;
+ printf(", Manuf = [%s],", p);
+ while (*p++);
+ printf("card vers = [%s]\n", p);
+ while (*p++);
+ printf("\tAddit. info = [%s]", p);
+ while (*p++);
+ printf(",[%s]\n", p);
+}
+
+/*
+ * dump functional extension tuple.
+ */
+void
+dump_func_ext(unsigned char *p, int len)
+{
+ if (len == 0)
+ return;
+ switch (p[0]) {
+ case 0:
+ case 8:
+ case 10:
+ if (len != 4) {
+ printf("\tWrong length for serial extension\n");
+ return;
+ }
+ printf("\tSerial interface extension:\n");
+ switch (p[1] & 0x1F) {
+ default:
+ printf("\t\tUnkn device");
+ break;
+ case 0:
+ printf("\t\t8250 UART");
+ break;
+ case 1:
+ printf("\t\t16450 UART");
+ break;
+ case 2:
+ printf("\t\t16550 UART");
+ break;
+ }
+ printf(", Parity - %s%s%s%s",
+ (p[2] & 1) ? "Space," : "",
+ (p[2] & 2) ? "Mark," : "",
+ (p[2] & 4) ? "Odd," : "",
+ (p[2] & 8) ? "Even," : "");
+ printf("\n");
+ break;
+ case 1:
+ case 5:
+ case 6:
+ case 7:
+ printf("\tModem interface capabilities:\n");
+ break;
+ case 2:
+ printf("\tData modem services available:\n");
+ break;
+ case 9:
+ printf("\tFax/modem services available:\n");
+ break;
+ case 4:
+ printf("\tVoice services available:\n");
+ break;
+ }
+}
+
+/*
+ * print_ext_speed - Print extended speed.
+ */
+void
+print_ext_speed(unsigned char x, int scale)
+{
+ static char *mant[] =
+ {"Reserved", "1.0", "1.2", "1.3", "1.5", "2.0", "2.5", "3.0",
+ "3.5", "4.0", "4.5", "5.0", "5.5", "6.0", "7.0", "8.0"};
+ static char *exp[] =
+ {"1 ns", "10 ns", "100 ns", "1 us", "10 us", "100 us",
+ "1 ms", "10 ms"};
+ static char *scale_name[] =
+ {"None", "10", "100", "1,000", "10,000", "100,000",
+ "1,000,000", "10,000,000"};
+
+ printf("Speed = %s x %s", mant[(x >> 3) & 0xF], exp[x & 7]);
+ if (scale)
+ printf(", scaled by %s", scale_name[scale & 7]);
+}
diff --git a/usr.sbin/pccard/pccardc/rdmap.c b/usr.sbin/pccard/pccardc/rdmap.c
new file mode 100644
index 0000000..5515f22
--- /dev/null
+++ b/usr.sbin/pccard/pccardc/rdmap.c
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 1995 Andrew McRae. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+
+#include <pccard/card.h>
+#include <pccard/cis.h>
+
+void
+dump_io(fd, nio)
+ int fd, nio;
+{
+ struct io_desc io;
+ int i;
+
+ for (i = 0; i < nio; i++) {
+ io.window = i;
+ ioctl(fd, PIOCGIO, &io);
+ printf("I/O %d: flags 0x%03x port 0x%3x size %d bytes\n",
+ io.window, io.flags, io.start, io.size);
+ }
+}
+
+void
+dump_mem(fd, nmem)
+ int fd, nmem;
+{
+ struct mem_desc mem;
+ int i;
+
+ for (i = 0; i < nmem; i++) {
+ mem.window = i;
+ ioctl(fd, PIOCGMEM, &mem);
+ 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, 0);
+ if (fd < 0)
+ return;
+ ioctl(fd, PIOCGSTATE, &st);
+/*
+ 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..57ed072
--- /dev/null
+++ b/usr.sbin/pccard/pccardc/rdreg.c
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 1995 Andrew McRae. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id$";
+#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/card.h>
+void
+dumpslot(sl)
+ int sl;
+{
+ char name[64];
+ int fd;
+ struct pcic_reg r;
+
+ sprintf(name, CARD_DEVICE, sl);
+ fd = open(name, 2);
+ 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)) {
+ warn("ioctl");
+ 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..ec29bf8
--- /dev/null
+++ b/usr.sbin/pccard/pccardc/wrattr.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.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id$";
+#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/card.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, 2);
+ 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)
+ warn("%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..963a466
--- /dev/null
+++ b/usr.sbin/pccard/pccardc/wrreg.c
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 1995 Andrew McRae. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id$";
+#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/card.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, 2);
+ 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))
+ warn("ioctl");
+ return 0;
+}
diff --git a/usr.sbin/pccard/pccardd/Makefile b/usr.sbin/pccard/pccardd/Makefile
new file mode 100644
index 0000000..75ae2cd
--- /dev/null
+++ b/usr.sbin/pccard/pccardd/Makefile
@@ -0,0 +1,12 @@
+# Makefile for pccardd
+
+PROG= pccardd
+SRCS= cardd.c file.c util.c readcis.c printcis.c
+MAN8= pccardd.8
+MAN5= pccard.conf.5
+DPADD= ${LIBUTIL}
+LDADD= -lutil
+CFLAGS+= -I${.CURDIR} -I${.CURDIR}/../pccardc
+
+.include <bsd.prog.mk>
+.PATH: ${.CURDIR}/../pccardc
diff --git a/usr.sbin/pccard/pccardd/cardd.c b/usr.sbin/pccard/pccardd/cardd.c
new file mode 100644
index 0000000..64c31a0
--- /dev/null
+++ b/usr.sbin/pccard/pccardd/cardd.c
@@ -0,0 +1,666 @@
+/*
+ * Copyright (c) 1995 Andrew McRae. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id: cardd.c,v 1.19 1997/10/26 04:36:24 nate Exp $";
+#endif /* not lint */
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#define EXTERN
+#include "cardd.h"
+
+char *config_file = "/etc/pccard.conf";
+
+static struct card_config *assign_driver(struct card *);
+static int assign_io(struct slot *);
+static int setup_slot(struct slot *);
+static void card_inserted(struct slot *);
+static void card_removed(struct slot *);
+static void dump_config_file(void);
+static void pr_cmd(struct cmd *);
+static void read_ether(struct slot *);
+static void readslots(void);
+static void slot_change(struct slot *);
+
+/*
+ * mainline code for cardd
+ */
+int
+main(int argc, char *argv[])
+{
+ struct slot *sp;
+ int count, debug = 0;
+ int verbose = 0;
+
+ while ((count = getopt(argc, argv, ":dvf:")) != -1) {
+ switch (count) {
+ case 'd':
+ setbuf(stdout, 0);
+ setbuf(stderr, 0);
+ debug = 1;
+ break;
+ case 'v':
+ verbose = 1;
+ break;
+ case 'f':
+ config_file = optarg;
+ break;
+ case ':':
+ die("no config file argument");
+ break;
+ case '?':
+ die("illegal option");
+ break;
+ }
+ }
+#ifdef DEBUG
+ debug = 1;
+#endif
+ io_avail = bit_alloc(IOPORTS); /* Only supports ISA ports */
+
+ /* Mem allocation done in MEMUNIT units. */
+ mem_avail = bit_alloc(MEMBLKS);
+ readfile(config_file);
+ if (verbose)
+ dump_config_file();
+ log_setup();
+ if (!debug)
+ if (daemon(0, 0))
+ die("fork failed");
+ readslots();
+ if (slots == 0)
+ die("no PC-CARD slots");
+ log_1s("pccardd started", NULL);
+ for (;;) {
+ fd_set mask;
+ FD_ZERO(&mask);
+ for (sp = slots; sp; sp = sp->next)
+ FD_SET(sp->fd, &mask);
+ count = select(32, 0, 0, &mask, 0);
+ if (count == -1) {
+ logerr("select");
+ continue;
+ }
+ if (count)
+ for (sp = slots; sp; sp = sp->next)
+ if (FD_ISSET(sp->fd, &mask))
+ slot_change(sp);
+ }
+}
+
+/*
+ * Dump configuration file data.
+ */
+void
+dump_config_file(void)
+{
+ struct card *cp;
+ struct card_config *confp;
+
+ for (cp = cards; cp; cp = cp->next) {
+ printf("Card manuf %s, vers %s\n", cp->manuf, cp->version);
+ printf("Configuration entries:\n");
+ for (confp = cp->config; confp; confp = confp->next)
+ printf("\tIndex code = 0x%x, driver name = %s\n",
+ confp->index, confp->driver->name);
+ if (cp->insert) {
+ printf("Insert commands are:\n");
+ pr_cmd(cp->insert);
+ }
+ if (cp->remove) {
+ printf("Remove commands are:\n");
+ pr_cmd(cp->remove);
+ }
+ }
+}
+
+static void
+pr_cmd(struct cmd *cp)
+{
+ while (cp) {
+ printf("\t%s\n", cp->line);
+ cp = cp->next;
+ }
+}
+
+/*
+ * readslots - read all the PCMCIA slots, and build
+ * a list of the slots.
+ */
+void
+readslots(void)
+{
+ char name[128];
+ int i, fd;
+ struct slot *sp;
+
+ for (i = 0; i < MAXSLOT; i++) {
+ sprintf(name, CARD_DEVICE, i);
+ fd = open(name, 2);
+ if (fd < 0)
+ continue;
+ sp = xmalloc(sizeof(*sp));
+ sp->fd = fd;
+ sp->name = newstr(name);
+ sp->slot = i;
+ sp->state = empty;
+
+ /* 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
+ log_1s("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)");
+ }
+ }
+#ifdef DEBUG
+ printf("%p %p\n", sp, &sp->next);
+#endif
+ sp->next = slots;
+ slots = sp;
+ slot_change(sp);
+ }
+}
+
+/*
+ * slot_change - Card status has changed.
+ * read new state and process.
+ */
+void
+slot_change(struct slot *sp)
+{
+ struct slotstate state;
+
+ current_slot = sp;
+ if (ioctl(sp->fd, PIOCGSTATE, &state)) {
+ logerr("ioctl (PIOCGSTATE)");
+ return;
+ }
+ if (state.state == sp->state)
+ return;
+ sp->state = state.state;
+ switch (sp->state) {
+ case empty:
+ case noslot:
+ card_removed(sp);
+ break;
+ case filled:
+ card_inserted(sp);
+ break;
+ case suspend:
+ /* ignored */
+ break;
+ }
+}
+
+/*
+ * card_removed - card has been removed from slot.
+ * Execute the remove commands, and clear the slot's state.
+ * Execute the device commands, then the driver commands
+ * and then the card commands. This is the reverse
+ * order to the insertion commands
+ */
+void
+card_removed(struct slot *sp)
+{
+ struct card *cp;
+
+ if (sp->cis)
+ freecis(sp->cis);
+ if (sp->config) {
+ sp->config->inuse = 0;
+ sp->config->driver->inuse = 0;
+ }
+ if ((cp = sp->card) != 0)
+ execute(cp->remove);
+ sp->cis = 0;
+ sp->config = 0;
+ /* release io */
+ bit_nset(io_avail, sp->io.addr, sp->io.size);
+}
+
+/*
+ * card_inserted - Card has been inserted;
+ * - Read the CIS
+ * - match the card type.
+ * - Match the driver and allocate a driver instance.
+ * - Allocate I/O ports, memory and IRQ.
+ * - Set up the slot.
+ * - assign the driver (if failed, then terminate).
+ * - Run the card commands.
+ * - Run the driver commands
+ * - Run the device commands
+ */
+void
+card_inserted(struct slot *sp)
+{
+ struct card *cp;
+
+ sleep(5);
+ sp->cis = readcis(sp->fd);
+ if (sp->cis == 0) {
+ log_1s("Error reading CIS on %s\n", sp->name);
+ return;
+ }
+#if 0
+ dumpcis(sp->cis);
+#endif
+ for (cp = cards; cp; cp = cp->next)
+ if (strcmp(cp->manuf, sp->cis->manuf) == 0 &&
+ strcmp(cp->version, sp->cis->vers) == 0)
+ break;
+ sp->card = cp;
+#if 0
+ reset_slot(sp);
+#endif
+ if (cp == 0) {
+ log_1s("No card in database for \"%s\"(\"%s\")",
+ sp->cis->manuf, sp->cis->vers);
+ return;
+ }
+ if (cp->ether)
+ read_ether(sp);
+ sp->config = assign_driver(cp);
+ if (sp->config == 0) {
+ execute(cp->insert);
+ return;
+ }
+ if (assign_io(sp)) {
+ log_1s("Resource allocation failure for %s", sp->cis->manuf);
+ return;
+ }
+
+ /*
+ *
+ * Once assigned, set up the I/O & mem contexts, set up the
+ * windows, and then attach the driver.
+ */
+ if (setup_slot(sp))
+ execute(cp->insert);
+#if 0
+ else
+ reset_slot(sp);
+#endif
+}
+
+/*
+ * read_ether - read ethernet address from card. Offset is
+ * the offset into the attribute memory of the card.
+ */
+static void
+read_ether(struct slot *sp)
+{
+ unsigned char net_addr[12];
+
+ lseek(sp->fd, (off_t)sp->card->ether, SEEK_SET);
+ if (read(sp->fd, net_addr, sizeof(net_addr)) != sizeof(net_addr)) {
+ logerr("read err on net addr");
+ return;
+ }
+ sp->eaddr[0] = net_addr[0];
+ sp->eaddr[1] = net_addr[2];
+ sp->eaddr[2] = net_addr[4];
+ sp->eaddr[3] = net_addr[6];
+ sp->eaddr[4] = net_addr[8];
+ sp->eaddr[5] = net_addr[10];
+ log_1s("Ether=%02x:%02x:%02x:%02x:%02x:%02x\n",
+ sp->eaddr[0], sp->eaddr[1], sp->eaddr[2],
+ sp->eaddr[3], sp->eaddr[4], sp->eaddr[5]);
+}
+
+/*
+ * assign_driver - Assign driver to card.
+ * First, see if an existing driver is already setup.
+ */
+static struct card_config *
+assign_driver(struct card *cp)
+{
+ struct driver *drvp;
+ struct card_config *conf;
+
+ for (conf = cp->config; conf; conf = conf->next)
+ if (conf->inuse == 0 && conf->driver->card == cp &&
+ conf->driver->config == conf) {
+#ifdef DEBUG
+ log_1s("Found existing driver (%s) for %s\n",
+ conf->driver->name, cp->manuf);
+#endif
+ return (conf);
+ }
+ /*
+ * New driver must be allocated. Find one that matches the
+ * any configurations not in use.
+ */
+ for (conf = cp->config; conf; conf = conf->next)
+ if (conf->inuse == 0 && conf->driver->card == 0)
+ break;
+ if (conf == 0) {
+ log_1s("No free configuration for card %s", cp->manuf);
+ return (0);
+ }
+ /*
+ * 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) {
+ log_1s("Driver already being used for %s", cp->manuf);
+ return (0);
+ }
+ /* Allocate a free IRQ if none has been specified */
+ if (conf->irq == 0) {
+ int i;
+ for (i = 1; i < 16; i++)
+ if (pool_irq[i]) {
+ conf->irq = i;
+ pool_irq[i] = 0;
+ break;
+ }
+ if (conf->irq == 0) {
+ log_1s("Failed to allocate IRQ for %s\n", cp->manuf);
+ return (0);
+ }
+ }
+#if 0
+ /* Allocate I/O and memory resources. */
+ for (ap = drvp->io; ap; ap = ap->next) {
+ if (ap->addr == 0 && ap->size) {
+ int i = bit_fns(io_avail, IOPORTS, ap->size);
+
+ if (i < 0) {
+ log_1s("Failed to allocate I/O ports for %s\n",
+ cp->manuf);
+ return (0);
+ }
+ ap->addr = i;
+ bit_nclear(io_avail, i, ap->size);
+ }
+ }
+ for (ap = drvp->mem; ap; ap = ap->next) {
+ if (ap->addr == 0 && ap->size) {
+ ap->addr = alloc_memory(ap->size);
+ if (ap->addr == 0) {
+ log_1s("Failed to allocate memory for %s\n",
+ cp->manuf);
+ return (0);
+ }
+ }
+ }
+#endif /* 0 */
+ drvp->card = cp;
+ drvp->config = conf;
+ drvp->inuse = 1;
+ conf->inuse = 1;
+ return (conf);
+}
+
+/*
+ * assign_io - Allocate resources to slot matching the
+ * configuration index selected.
+ */
+static int
+assign_io(struct slot *sp)
+{
+ struct cis *cis;
+ struct cis_config *cisconf, *defconf;
+
+ cis = sp->cis;
+ defconf = cis->def_config;
+ for (cisconf = cis->conf; cisconf; cisconf = cisconf->next)
+ if (cisconf->id == sp->config->index)
+ break;
+ if (cisconf == 0)
+ return (-1);
+ sp->card_config = cisconf;
+
+ /*
+ * Found a matching configuration. Now look at the I/O, memory and IRQ
+ * to create the desired parameters. Look at memory first.
+ */
+ if (cisconf->memspace || (defconf && defconf->memspace)) {
+ struct cis_memblk *mp;
+
+ mp = cisconf->mem;
+ if (!cisconf->memspace)
+ mp = defconf->mem;
+ sp->mem.size = mp->length;
+ sp->mem.cardaddr = mp->address;
+
+ /* For now, we allocate our own memory from the pool. */
+ sp->mem.addr = sp->config->driver->mem;
+ /*
+ * Host memory address is required. Allocate one
+ * from our pool.
+ */
+ if (sp->mem.size && sp->mem.addr == 0) {
+ sp->mem.addr = alloc_memory(mp->length);
+ if (sp->mem.addr == 0)
+ return (-2);
+ sp->config->driver->mem = sp->mem.addr;
+ }
+ sp->mem.cardaddr = 0x4000;
+#ifdef DEBUG
+ log_1s("Using mem addr 0x%x, size %d, card addr 0x%x\n",
+ sp->mem.addr, sp->mem.size, sp->mem.cardaddr);
+#endif
+ }
+
+ /* Now look at I/O. */
+ bzero(&sp->io, sizeof(sp->io));
+ if (cisconf->iospace || (defconf && defconf->iospace)) {
+ struct cis_config *cp;
+
+ cp = cisconf;
+ if (!cisconf->iospace)
+ cp = defconf;
+ /*
+ * If # of I/O lines decoded == 10, then card does its
+ * own decoding.
+ *
+ * If an I/O block exists, then use it.
+ * If no address (but a length) is available, allocate
+ * from the pool.
+ */
+ if (cp->io) {
+ sp->io.addr = cp->io->addr;
+ sp->io.size = cp->io->size;
+ } else
+ /*
+ * No I/O block, assume the address lines
+ * decode gives the size.
+ */
+ sp->io.size = 1 << cp->io_addr;
+
+ if (sp->io.addr == 0) {
+ int i = bit_fns(io_avail, IOPORTS, sp->io.size);
+
+ if (i < 0)
+ return (-1);
+ sp->io.addr = i;
+ }
+ bit_nclear(io_avail, sp->io.addr, sp->io.size);
+
+ /* Set up the size to take into account the decode lines. */
+ sp->io.cardaddr = cp->io_addr;
+ switch (cp->io_bus) {
+ case 0:
+ break;
+ case 1:
+ sp->io.flags = IODF_WS;
+ break;
+ case 2:
+ sp->io.flags = IODF_WS | IODF_CS16;
+ break;
+ case 3:
+ sp->io.flags = IODF_WS | IODF_CS16 | IODF_16BIT;
+ break;
+ }
+#ifdef DEBUG
+ log_1s("Using I/O addr 0x%x, size %d\n",
+ sp->io.addr, sp->io.size);
+#endif
+ }
+ sp->irq = sp->config->irq;
+ return (0);
+}
+
+/*
+ * setup_slot - Allocate the I/O and memory contexts
+ * return true if completed OK.
+ */
+static int
+setup_slot(struct slot *sp)
+{
+ struct mem_desc mem;
+ struct io_desc io;
+ struct dev_desc drv;
+ struct driver *drvp = sp->config->driver;
+ char c;
+ off_t offs;
+ int rw_flags;
+
+ memset(&io, 0, sizeof io);
+ memset(&drv, 0, sizeof drv);
+ offs = sp->cis->reg_addr;
+ rw_flags = MDF_ATTR;
+ ioctl(sp->fd, PIOCRWFLAG, &rw_flags);
+ lseek(sp->fd, offs, SEEK_SET);
+ c = 0x80;
+ write(sp->fd, &c, sizeof(c));
+ usleep(sp->card->reset_time * 1000);
+ lseek(sp->fd, offs, SEEK_SET);
+ c = 0x00;
+ write(sp->fd, &c, sizeof(c));
+ usleep(sp->card->reset_time * 1000);
+ lseek(sp->fd, offs, SEEK_SET);
+ c = sp->config->index;
+ c |= 0x40;
+ write(sp->fd, &c, sizeof(c));
+#ifdef DEBUG
+ log_1s("Setting config reg at offs 0x%lx to 0x%x, Reset time = %d ms\n",
+ (unsigned long)offs, c, sp->card->reset_time);
+#endif
+ sleep(5);
+ usleep(sp->card->reset_time * 1000);
+
+ /* If other config registers exist, set them up. */
+ if (sp->cis->ccrs & 2) {
+ /* CCSR */
+ c = 0;
+ if (sp->cis->def_config && sp->cis->def_config->misc_valid &&
+ (sp->cis->def_config->misc & 0x8))
+ c |= 0x08;
+ if (sp->card_config->io_bus == 1)
+ c |= 0x20;
+ lseek(sp->fd, offs + 2, SEEK_SET);
+ write(sp->fd, &c, sizeof(c));
+ }
+ mem.window = 0;
+ if (sp->mem.addr) {
+ mem.window = 0;
+ mem.flags = sp->mem.flags | MDF_ACTIVE | MDF_16BITS;
+ mem.start = (caddr_t) sp->mem.addr;
+ mem.card = sp->mem.cardaddr;
+ mem.size = sp->mem.size;
+ if (ioctl(sp->fd, PIOCSMEM, &mem)) {
+ logerr("ioctl (PIOCSMEM)");
+ return (0);
+ }
+ }
+ io.window = 0;
+ if (sp->io.size) {
+ io.flags = sp->io.flags;
+ io.start = sp->io.addr;
+ io.size = sp->io.size;
+#if 0
+ io.start = sp->io.addr & ~((1 << sp->io.cardaddr) - 1);
+ io.size = 1 << sp->io.cardaddr;
+ if (io.start < 0x100) {
+ io.start = 0x100;
+ io.size = 0x300;
+ }
+#endif
+#ifdef DEBUG
+ log_1s("Assigning I/O window %d, start 0x%x, size 0x%x flags 0x%x\n",
+ io.window, io.start, io.size, io.flags);
+#endif
+ io.flags |= IODF_ACTIVE;
+ if (ioctl(sp->fd, PIOCSIO, &io)) {
+ logerr("ioctl (PIOCSIO)");
+ return (0);
+ }
+ }
+ strcpy(drv.name, drvp->kernel);
+ drv.unit = drvp->unit;
+ drv.irqmask = 1 << sp->irq;
+ drv.flags = 0x80;
+ if (sp->mem.size) {
+ drv.mem = sp->mem.addr;
+ drv.memsize = sp->mem.size;
+ } else {
+ drv.mem = 0;
+ drv.memsize = 0;
+ }
+ if (sp->io.size)
+ drv.iobase = sp->io.addr;
+ else
+ drv.iobase = 0;
+#ifdef DEBUG
+ log_1s("Assign %s%d, io 0x%x, mem 0x%lx, %d bytes, irq %d, flags %x\n",
+ drv.name, drv.unit, drv.iobase, drv.mem, drv.memsize, sp->irq, drv.flags);
+#endif
+
+ /*
+ * If the driver fails to be connected to the device,
+ * then it may mean that the driver did not recognise it.
+ */
+ memcpy(drv.misc, sp->eaddr, 6);
+ if (ioctl(sp->fd, PIOCSDRV, &drv)) {
+ log_1s("driver allocation failed for %s", sp->card->manuf);
+ return (0);
+ }
+ return (1);
+}
diff --git a/usr.sbin/pccard/pccardd/cardd.h b/usr.sbin/pccard/pccardd/cardd.h
new file mode 100644
index 0000000..f43a7a7
--- /dev/null
+++ b/usr.sbin/pccard/pccardd/cardd.h
@@ -0,0 +1,147 @@
+/*
+ * Copyright (c) 1995 Andrew McRae. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $Id$
+ *
+ * Common include file for PCMCIA daemon
+ */
+#include <bitstring.h>
+
+#include <pccard/card.h>
+#include <pccard/cis.h>
+
+#include "readcis.h"
+
+#ifndef EXTERN
+#define EXTERN extern
+#endif
+
+struct cmd {
+ struct cmd *next;
+ char *line; /* Command line */
+ int macro; /* Contains macros */
+};
+
+struct card_config {
+ struct card_config *next;
+ unsigned char index;
+ struct driver *driver;
+ int irq;
+ int flags;
+ char inuse;
+};
+
+struct card {
+ struct card *next;
+ char *manuf;
+ char *version;
+ int ether; /* For net cards, ether at offset */
+ int reset_time; /* Reset time */
+ struct card_config *config; /* List of configs */
+ struct cmd *insert; /* Insert commands */
+ struct cmd *remove; /* Remove commands */
+};
+
+struct driver {
+ struct driver *next;
+ char *name;
+ char *kernel; /* Kernel driver base name */
+ int unit; /* Unit of driver */
+ /*
+ * The rest of the structure is allocated dynamically.
+ * Once allocated, it stays allocated.
+ */
+ struct card *card; /* Current card, if any */
+ struct card_config *config; /* Config back ptr */
+ unsigned int mem; /* Allocated host address (if any) */
+ int inuse;
+};
+
+/*
+ * Defines one allocation block i.e a starting address
+ * and size. Used for either memory or I/O ports
+ */
+struct allocblk {
+ struct allocblk *next;
+ int addr; /* Address */
+ int size; /* Size */
+ int flags; /* Flags for block */
+ int cardaddr; /* Card address */
+};
+/*
+ * Slot structure - data held for each slot.
+ */
+struct slot {
+ struct slot *next;
+ int fd;
+ int mask;
+ int slot;
+ char *name;
+ enum cardstate state;
+ struct cis *cis;
+ struct card *card; /* Current card */
+ struct card_config *config; /* Current configuration */
+ struct cis_config *card_config;
+ char devname[16];
+ unsigned char eaddr[6]; /* If any */
+ struct allocblk io; /* I/O block spec */
+ struct allocblk mem; /* Memory block spec */
+ int irq; /* Irq value */
+};
+
+EXTERN struct slot *slots, *current_slot;
+
+EXTERN struct allocblk *pool_ioblks; /* I/O blocks in the pool */
+EXTERN struct allocblk *pool_mem; /* Memory in the pool */
+EXTERN int pool_irq[16]; /* IRQ allocations */
+EXTERN struct driver *drivers; /* List of drivers */
+EXTERN struct card *cards;
+EXTERN bitstr_t *mem_avail;
+EXTERN bitstr_t *io_avail;
+
+EXTERN int verbose;
+
+/* util.c functions */
+unsigned long alloc_memory(int);
+int bit_fns(bitstr_t *, int, int);
+void die(char *);
+void execute(struct cmd *);
+void log_1s(const char *, ...);
+void log_setup(void);
+void logerr(char *);
+char *newstr();
+void reset_slot(struct slot *);
+void *xmalloc(int);
+
+/* file.c */
+void readfile(char *);
+
+#define IOPORTS 0x400
+#define MEMUNIT 0x1000
+#define MEMSTART 0xA0000
+#define MEMEND 0x100000
+#define MEMBLKS ((MEMEND-MEMSTART)/MEMUNIT)
+#define MEM2BIT(x) (((x)-MEMSTART)/MEMUNIT)
+#define BIT2MEM(x) (((x)*MEMUNIT)+MEMSTART)
diff --git a/usr.sbin/pccard/pccardd/file.c b/usr.sbin/pccard/pccardd/file.c
new file mode 100644
index 0000000..3add3da
--- /dev/null
+++ b/usr.sbin/pccard/pccardd/file.c
@@ -0,0 +1,626 @@
+/*
+ * Copyright (c) 1995 Andrew McRae. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include "cardd.h"
+
+static FILE *in;
+static int pushc, pusht;
+static int lineno;
+static char *filename;
+
+static char *keys[] = {
+ "__EOF__", /* 1 */
+ "io", /* 2 */
+ "irq", /* 3 */
+ "memory", /* 4 */
+ "card", /* 5 */
+ "device", /* 6 */
+ "config", /* 7 */
+ "reset", /* 8 */
+ "ether", /* 9 */
+ "insert", /* 10 */
+ "remove", /* 11 */
+ "iosize", /* 12 */
+ "memsize", /* 13 */
+ 0
+};
+
+struct flags {
+ char *name;
+ int mask;
+};
+
+static void parsefile(void);
+static char *getline(void);
+static char *next_tok(void);
+static int num_tok(void);
+static void error(char *);
+static int keyword(char *);
+static int irq_tok(int);
+static struct allocblk *ioblk_tok(int);
+static struct allocblk *memblk_tok(int);
+static struct driver *new_driver(char *);
+
+static void addcmd(struct cmd **);
+static void parse_card(void);
+
+/*
+ * Read a file and parse the pcmcia configuration data.
+ * After parsing, verify the links.
+ */
+void
+readfile(char *name)
+{
+ struct card *cp;
+
+ in = fopen(name, "r");
+ if (in == 0) {
+ logerr(name);
+ die("readfile");
+ }
+ parsefile();
+ for (cp = cards; cp; cp = cp->next) {
+ if (cp->config == 0)
+ log_1s("warning: card %s(%s) has no valid configuration\n",
+ cp->manuf, cp->version);
+ }
+}
+
+static void
+parsefile(void)
+{
+ int i;
+ struct allocblk *bp;
+
+ pushc = 0;
+ lineno = 1;
+ for (;;)
+ switch (keyword(next_tok())) {
+ case 1:
+ /* EOF */
+ return;
+ case 2:
+ /* reserved I/O blocks */
+ while ((bp = ioblk_tok(0)) != 0) {
+ if (bp->size == 0 || bp->addr == 0) {
+ free(bp);
+ continue;
+ }
+ bit_nset(io_avail, bp->addr,
+ bp->addr + bp->size - 1);
+ bp->next = pool_ioblks;
+ pool_ioblks = bp;
+ }
+ pusht = 1;
+ break;
+ case 3:
+ /* reserved irqs */
+ while ((i = irq_tok(0)) > 0)
+ pool_irq[i] = 1;
+ pusht = 1;
+ break;
+ case 4:
+ /* reserved memory blocks. */
+ while ((bp = memblk_tok(0)) != 0) {
+ if (bp->size == 0 || bp->addr == 0) {
+ free(bp);
+ continue;
+ }
+ bit_nset(mem_avail, MEM2BIT(bp->addr),
+ MEM2BIT(bp->addr + bp->size) - 1);
+ bp->next = pool_mem;
+ pool_mem = bp;
+ }
+ pusht = 1;
+ break;
+ case 5:
+ /* Card definition. */
+ parse_card();
+ break;
+ default:
+ error("syntax error");
+ pusht = 0;
+ break;
+ }
+}
+
+/*
+ * Parse a card definition.
+ */
+static void
+parse_card(void)
+{
+ char *man, *vers;
+ struct card *cp;
+ int i;
+ struct card_config *confp, *lastp;
+
+ man = newstr(next_tok());
+ vers = newstr(next_tok());
+ cp = xmalloc(sizeof(*cp));
+ cp->manuf = man;
+ cp->version = vers;
+ cp->reset_time = 50;
+ cp->next = cards;
+ cards = cp;
+ for (;;) {
+ switch (keyword(next_tok())) {
+ case 7:
+ /* config */
+ i = num_tok();
+ if (i == -1) {
+ error("illegal card config index");
+ break;
+ }
+ confp = xmalloc(sizeof(*confp));
+ man = next_tok();
+ confp->driver = new_driver(man);
+ confp->irq = irq_tok(1);
+ confp->flags = num_tok();
+ if (confp->flags == -1) {
+ pusht = 1;
+ confp->flags = 0;
+ }
+ if (confp->irq < 0 || confp->irq > 15) {
+ error("illegal card IRQ value");
+ break;
+ }
+ confp->index = i & 0x3F;
+
+ /*
+ * If no valid driver for this config, then do not save
+ * this configuration entry.
+ */
+ if (confp->driver) {
+ if (cp->config == 0)
+ cp->config = confp;
+ else {
+ for (lastp = cp->config; lastp->next;
+ lastp = lastp->next);
+ lastp->next = confp;
+ }
+ } else
+ free(confp);
+ break;
+ case 8:
+ /* reset */
+ i = num_tok();
+ if (i == -1) {
+ error("illegal card reset time");
+ break;
+ }
+ cp->reset_time = i;
+ break;
+ case 9:
+ /* ether */
+ cp->ether = num_tok();
+ if (cp->ether == -1) {
+ error("illegal ether address offset");
+ cp->ether = 0;
+ }
+ break;
+ case 10:
+ /* insert */
+ addcmd(&cp->insert);
+ break;
+ case 11:
+ /* remove */
+ addcmd(&cp->remove);
+ break;
+ default:
+ pusht = 1;
+ return;
+ }
+ }
+}
+
+/*
+ * Generate a new driver structure. If one exists, use
+ * that one after confirming the correct class.
+ */
+static struct driver *
+new_driver(char *name)
+{
+ struct driver *drvp;
+ char *p;
+
+ for (drvp = drivers; drvp; drvp = drvp->next)
+ if (strcmp(drvp->name, name) == 0)
+ return (drvp);
+ drvp = xmalloc(sizeof(*drvp));
+ drvp->next = drivers;
+ drivers = drvp;
+ drvp->name = newstr(name);
+ drvp->kernel = newstr(name);
+ p = drvp->kernel;
+ while (*p++)
+ if (*p >= '0' && *p <= '9') {
+ drvp->unit = atoi(p);
+ *p = 0;
+ break;
+ }
+#ifdef DEBUG
+ if (verbose)
+ printf("Drv %s%d created\n", drvp->kernel, drvp->unit);
+#endif
+ return (drvp);
+}
+
+
+/*
+ * Parse one I/O block.
+ */
+static struct allocblk *
+ioblk_tok(int force)
+{
+ struct allocblk *io;
+ int i, j;
+
+ if ((i = num_tok()) >= 0) {
+ if (strcmp("-", next_tok()) || (j = num_tok()) < 0 || j < i) {
+ error("I/O block format error");
+ return (0);
+ }
+ io = xmalloc(sizeof(*io));
+ io->addr = i;
+ io->size = j - i + 1;
+ if (j > IOPORTS) {
+ error("I/O port out of range");
+ if (force) {
+ free(io);
+ io = 0;
+ } else
+ io->addr = io->size = 0;
+ }
+ return (io);
+ }
+ if (force)
+ error("illegal or missing I/O block spec");
+ return (0);
+}
+
+/*
+ * Parse a memory block.
+ */
+static struct allocblk *
+memblk_tok(int force)
+{
+ struct allocblk *mem;
+ int i, j;
+
+ if ((i = num_tok()) >= 0)
+ if ((j = num_tok()) < 0)
+ error("illegal memory block");
+ else {
+ mem = xmalloc(sizeof(*mem));
+ mem->addr = i & ~(MEMUNIT - 1);
+ mem->size = (j + MEMUNIT - 1) & ~(MEMUNIT - 1);
+ if (i < MEMSTART || (i + j) > MEMEND) {
+ error("memory address out of range");
+ if (force) {
+ free(mem);
+ mem = 0;
+ } else
+ mem->addr = mem->size = 0;
+ }
+ return (mem);
+ }
+ if (force)
+ error("illegal or missing memory block spec");
+ return (0);
+}
+
+/*
+ * IRQ token. Must be number > 0 && < 16.
+ * If force is set, IRQ must exist, and can also be '?'.
+ */
+static int
+irq_tok(int force)
+{
+ int i;
+
+ if (strcmp("?", next_tok()) == 0 && force)
+ return (0);
+ pusht = 1;
+ i = num_tok();
+ if (i > 0 && i < 16)
+ return (i);
+ if (force)
+ error("illegal IRQ value");
+ return (-1);
+}
+
+/*
+ * search the table for a match.
+ */
+static int
+keyword(char *str)
+{
+ char **s;
+ int i = 1;
+
+ for (s = keys; *s; s++, i++)
+ if (strcmp(*s, str) == 0)
+ return (i);
+ return (0);
+}
+
+/*
+ * addcmd - Append the command line to the list of
+ * commands.
+ */
+static void
+addcmd(struct cmd **cp)
+{
+ struct cmd *ncp;
+ char *s = getline();
+
+ if (*s) {
+ ncp = xmalloc(sizeof(*ncp));
+ ncp->line = s;
+ while (*cp)
+ cp = &(*cp)->next;
+ *cp = ncp;
+ }
+
+}
+
+static void
+error(char *msg)
+{
+ pusht = 1;
+ log_1s("%s: %s at line %d, near %s\n",
+ filename, msg, lineno, next_tok());
+ pusht = 1;
+}
+
+static int last_char;
+
+static int
+get(void)
+{
+ int c;
+
+ if (pushc)
+ c = pushc;
+ else
+ c = getc(in);
+ pushc = 0;
+ while (c == '\\') {
+ c = getc(in);
+ switch (c) {
+ case '#':
+ return (last_char = c);
+ case '\n':
+ lineno++;
+ c = getc(in);
+ continue;
+ }
+ pushc = c;
+ return ('\\');
+ }
+ if (c == '\n')
+ lineno++;
+ if (c == '#') {
+ while (get() != '\n');
+ return (last_char = '\n');
+ }
+ return (last_char = c);
+}
+
+/*
+ * num_tok - expecting a number token. If not a number,
+ * return -1.
+ * Handles octal (who uses octal anymore?)
+ * hex
+ * decimal
+ * Looks for a 'k' at the end of decimal numbers
+ * and multiplies by 1024.
+ */
+static int
+num_tok(void)
+{
+ char *s = next_tok(), c;
+ int val = 0, base;
+
+ base = 10;
+ c = *s++;
+ if (c == '0') {
+ base = 8;
+ c = *s++;
+ if (c == '\0') return 0;
+ else if (c == 'x' || c == 'X') {
+ c = *s++;
+ base = 16;
+ }
+ }
+ do {
+ switch (c) {
+ case 'k':
+ case 'K':
+ if (val && base == 10 && *s == 0)
+ return (val * 1024);
+ return (-1);
+ default:
+ return (-1);
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ val = val * base + c - '0';
+ break;
+
+ case '8':
+ case '9':
+ if (base == 8)
+ return (-1);
+ else
+ val = val * base + c - '0';
+ break;
+ case 'a':
+ case 'b':
+ case 'c':
+ case 'd':
+ case 'e':
+ case 'f':
+ if (base == 16)
+ val = val * base + c - 'a' + 10;
+ else
+ return (-1);
+ break;
+ case 'A':
+ case 'B':
+ case 'C':
+ case 'D':
+ case 'E':
+ case 'F':
+ if (base == 16)
+ val = val * base + c - 'A' + 10;
+ else
+ return (-1);
+ break;
+ }
+ } while ((c = *s++) != 0);
+ return (val);
+}
+
+static char *_next_tok(void);
+
+static char *
+next_tok(void)
+{
+ char *s = _next_tok();
+#if 0
+ printf("Tok = %s\n", s);
+#endif
+ return (s);
+}
+
+/*
+ * get one token. Handles string quoting etc.
+ */
+static char *
+_next_tok(void)
+{
+ static char buf[1024];
+ char *p = buf, instr = 0;
+ int c;
+
+ if (pusht) {
+ pusht = 0;
+ return (buf);
+ }
+ for (;;) {
+ c = get();
+ switch (c) {
+ default:
+ *p++ = c;
+ break;
+ case '"':
+ if (instr) {
+ *p++ = 0;
+ return (buf);
+ }
+ instr = 1;
+ break;
+ case '\n':
+ if (instr) {
+ error("unterminated string");
+ break;
+ }
+ case ' ':
+ case '\t':
+ /* Eat whitespace unless in a string. */
+ if (!instr) {
+ if (p != buf) {
+ *p++ = 0;
+ return (buf);
+ }
+ } else
+ *p++ = c;
+ break;
+ case '-':
+ case '?':
+ case '*':
+ /* Special characters that are tokens on their own. */
+ if (instr)
+ *p++ = c;
+ else {
+ if (p != buf)
+ pushc = c;
+ else
+ *p++ = c;
+ *p++ = 0;
+ return (buf);
+ }
+ break;
+ case EOF:
+ if (p != buf) {
+ *p++ = 0;
+ return (buf);
+ }
+ strcpy(buf, "__EOF__");
+ return (buf);
+ }
+ }
+}
+
+/*
+ * get the rest of the line. If the
+ * last character scanned was a newline, then
+ * return an empty line. If this isn't checked, then
+ * a getline may incorrectly return the next line.
+ */
+static char *
+getline(void)
+{
+ char buf[1024], *p = buf;
+ int c, i = 0;
+
+ if (last_char == '\n')
+ return (newstr(""));
+ do {
+ c = get();
+ } while (c == ' ' || c == '\t');
+ for (; c != '\n' && c != EOF; c = get())
+ if (i++ < sizeof(buf) - 10)
+ *p++ = c;
+ *p = 0;
+ return (newstr(buf));
+}
diff --git a/usr.sbin/pccard/pccardd/pccard.conf.5 b/usr.sbin/pccard/pccardd/pccard.conf.5
new file mode 100644
index 0000000..96ad0cf
--- /dev/null
+++ b/usr.sbin/pccard/pccardd/pccard.conf.5
@@ -0,0 +1,202 @@
+.\"
+.\" Copyright (c) 1994 Andrew McRae. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. The name of the author may not be used to endorse or promote products
+.\" derived from this software without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+.\"
+.Dd November 2, 1994
+.Dt PCCARD.CONF 5
+.Os FreeBSD
+.Sh NAME
+.Nm pccard.conf
+.Nd
+.Xr pccardd 8
+configuration file
+.Sh DESCRIPTION
+The
+.Nm
+file is the configuration file for the
+.Xr pccardd 8
+PC-CARD slot management daemon.
+It provides information to allow card
+identification, and the matching of drivers (along
+with driver resources) to the PC-CARD cards.
+.Pp
+There are four basic elements within the configuration file;
+An optional
+.Em "resource pool"
+preceding the other sections,
+and one or more
+.Em "card identifiers" ,
+and
+.Em "device instances" .
+The latter two may appear in any order, and may be
+interspersed as desired.
+.Pp
+Each PC-CARD card contains configuration tuples that provide
+the manufacturer and card version; these are used
+to identify the card specification in the configuration
+file, and from this find a driver that can be used to
+interface to the particular card. There is a many-to-one mapping
+between cards to drivers i.e a single driver may interface to
+multiple types of cards. To aid this, card parameters may be
+specified separately from the driver to initialise the card or
+extract (in the case of a network card) an Ethernet address.
+.Pp
+Once a driver is allocated to a card, it stays
+allocated to that particular card.
+However, multiple instances of the same type of driver can be
+configured, so that if two cards are plugged in that map to a
+similar type of driver, other driver instances of the same name
+can be configured.
+.Pp
+The
+.Em insert
+and
+.Em remove
+commands allow a shell command line to be executed.
+The command to be executed is the rest of the line after
+the keyword. The line can be continued using a backslash.
+A simple
+macro substitution allows the current kernel device name
+.Em ( $device )
+and
+network card Ethernet address
+.Em ( $ether )
+to be inserted into the command line.
+.Xr pccardd 8
+uses the
+.Xr system 3
+subroutine to execute the command line.
+.Pp
+Numeric values may be expressed as octal, hex or decimal.
+If a decimal number has
+.Em k
+or
+.Em K
+appended to it, the value is multiplied by 1024. Names may be
+quoted using double quotes if spaces are required.
+A hash character comments out the rest of the line.
+.Ss "Resource pool"
+The (optional) section specifies a pool of system resources
+such as ISA bus memory address space, Input/Output ports and
+interrupt request numbers. This resource pool is used
+to allocate address space and interrupt numbers dynamically
+according to the requirements specified in each driver
+description.
+.Pp
+The syntax of the resources is as follows:
+.Pp
+.Dl io Ar start - end ...
+.Dl memory Ar address size ...
+.Dl irq Ar irq-number ...
+.Pp
+Each of the statements define I/O, memory or IRQ
+blocks that can be used to allocate to drivers when
+they are initialised.
+.Pp
+Multiple lines of any of the above statements may be
+present to allow separate blocks of each resource to be
+defined.
+.Ss "Card Identifiers"
+The syntax for card identifiers is:
+.Pp
+.Dl card Ar manufacturer version
+.Dl config Ar index driver interrupt [ flags ]
+.Dl ether Ar offset
+.Dl insert Ar command
+.Dl remove Ar command
+.Pp
+The first line is mandatory;
+the latter statements are optional and can appear in
+any order. There may be multiple
+.Em config
+lines.
+The
+.Em card
+parameters are the Manufacturer name and card version that
+is used to match the values from the card's CIS memory. The
+.Em config
+parameters select the particular card's configuration index
+from the range available in the card's CIS, the driver that
+is to be associated with this configuration, and the interrupt
+level (if any) to be assigned. An optional set of flags may
+be assigned.
+.Pp
+The optional
+.Em ether
+keyword is used when network cards have their physical Ethernet address
+located within the attribute memory of the card. The parameter of this
+statement indicates the offset within the attribute memory of the
+Ethernet address. This value can be used within insert/remove
+commands using the
+.Em $ether
+macro.
+.Pp
+The
+.Em insert
+and
+.Em remove
+sections allow shell commands to be specified that are executed
+when the card is inserted or removed. Multiple
+.Em insert
+and
+.Em remove
+commands are allowed, and they are executed in the order they
+are listed.
+.Sh EXAMPLE
+A typical configuration file may appear thus:
+.Bd -literal
+#
+# Sample configuration file.
+#
+# Pool parameters.
+#
+io 0x280 - 0x2F0 0x300 - 0x360
+irq 5 6 8 9 10 15
+memory 0xd4000 96k
+memory 0xc4000 32k
+#
+# Card database.
+#
+card "RPTI LTD." "EP400" # NE2000 clone
+ ether 0x110
+ config 0x21 "ed0" 5
+ insert ifconfig $device physical $ether
+ insert ifconfig $device bean
+ remove ifconfig $device down
+
+card "XYZZY" "FAX/1.0"
+ config 0x30 "sio1" 11
+ insert echo start getty
+ remove echo stop getty
+
+.Ed
+.Sh FILES
+.Bl -tag -width /etc/pccard.conf -compact
+.It Pa /etc/pccard.conf
+The
+.Xr pccardd 8
+configuration file.
+.El
+.Sh SEE ALSO
+.Xr pccardd 8
diff --git a/usr.sbin/pccard/pccardd/pccardd.8 b/usr.sbin/pccard/pccardd/pccardd.8
new file mode 100644
index 0000000..a4ebb74
--- /dev/null
+++ b/usr.sbin/pccard/pccardd/pccardd.8
@@ -0,0 +1,158 @@
+.\"
+.\" Copyright (c) 1994 Andrew McRae. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. The name of the author may not be used to endorse or promote products
+.\" derived from this software without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+.\"
+.\" $Id: pccardd.8,v 1.7 1997/02/22 16:08:58 peter Exp $
+.\"
+.Dd November 1, 1994
+.Dt PCCARD 8
+.Os FreeBSD
+.Sh NAME
+.Nm pccardd
+.Nd PC-CARD (PCMCIA) management daemon
+.Sh SYNOPSIS
+.Nm pccardd
+.Op Fl d
+.Op Fl v
+.Op Fl f Ar configfile
+.Sh DESCRIPTION
+.Nm Pccardd
+is normally started at boot time, and manages the insertion
+and removal of PC-CARD cards.
+.Pp
+When started,
+.Nm
+will read the configuration file (default name
+.Pa /etc/pccard.conf )
+and scans the available PC-CARD slots for cards.
+.Nm Pccardd
+then waits for
+.Em "card events" ,
+such as the insertion of a new card or the removal
+of a card.
+.Pp
+When a card is inserted, the following
+actions are taken:
+.Bl -enum
+.It
+The kernel driver detects the card insertion and applies
+power to the card.
+.It
+.Nm Pccardd
+reads the
+.Em CIS
+data from the attribute memory of the card, and uses
+the manufacturer name and card version to match
+the card description in the configuration file.
+.It
+Once matched, a driver is allocated.
+.It
+Once a free driver and device instance is located,
+.Nm
+will (if required) allocate resources such as an ISA memory
+block and Input/Output ports from a common pool.
+.It
+The PC-CARD slot is configured with the I/O and memory
+contexts allocated, and the kernel driver is attached to
+this card.
+.It
+If the attach succeeds, then specific shell commands
+may be executed to configure the device, such as
+.Xr ifconfig 8
+to set up a network interface. Separate commands may be specified
+for each card, driver or device, and are executed in that order.
+.El
+.Pp
+When
+.Nm
+detects that a card has been removed, the following sequence occurs:
+.Bl -enum
+.It
+The shell commands associated with card removal are executed. These
+are intended to reset any device associated with the removed card.
+Separate commands may exist for card, driver and device instances.
+.It
+The PC-CARD slot resources are freed.
+.El
+.Pp
+Once a card/driver instance is configured, the resources
+bound to that instance are remembered, and if the card is removed
+and reinserted, the same driver is allocated. The primary reason
+is that once a driver is associated with a card, the
+driver's
+.Fn probe
+routine has been called, and this usually causes driver specific
+data areas to be initialised with the I/O ports or memory resources
+allocated to the card. Most drivers are not designed to be
+disassociated from the hardware and then reassociated with different
+parameters. This will change significantly when loadable kernel
+modules are supported.
+.Pp
+The start options understood by
+.Nm
+are:
+.Bl -tag -width Ds
+.It Fl d
+Do not run as a daemon, but run in the foreground and
+display error messages.
+.It Fl v
+After reading the configuration file, print out a summary
+of it.
+.It Fl f Ar configfile
+Specifies a different configuration file to be used
+in placed of the default file
+.Pa /etc/pccard.conf .
+The file format is detailed in
+.Xr card.conf 5 ,
+and lists the PC-CARD cards recognized by
+.Nm pccardd ,
+and the kernel drivers and devices that are used to
+interface to the card.
+.Pp
+.Sh FILES
+.Bl -tag -width /etc/pccard.conf -compact
+.It Pa /etc/pccard.conf
+.El
+.Sh SEE ALSO
+.Xr pccard.conf 5 ,
+.Xr ifconfig 8
+.Sh AUTHOR
+Developed by
+.An Andrew McRae Aq andrew@mega.com.au .
+.Sh BUGS
+.Nm Pccardd
+can set up card parameters, but cannot guarantee that
+particular drivers can work with the card.
+.Pp
+Since
+.Nm FreeBSD
+does not currently support loadable kernel modules, any
+.Em irq
+specifications in the configuration file must match the
+.Nm config
+entry for the kernel.
+.Pp
+Removing cards may cause problems if system resources
+have been associated with the card, such as network
+mounted filesystems.
diff --git a/usr.sbin/pccard/pccardd/readcis.c b/usr.sbin/pccard/pccardd/readcis.c
new file mode 100644
index 0000000..fd00db5
--- /dev/null
+++ b/usr.sbin/pccard/pccardd/readcis.c
@@ -0,0 +1,615 @@
+/*
+ * Copyright (c) 1995 Andrew McRae. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+
+#include <pccard/card.h>
+#include <pccard/cis.h>
+
+#include "readcis.h"
+
+static int read_attr(int, char *, int);
+static int ck_linktarget(int, off_t, int);
+static void cis_info(struct cis *, unsigned char *, int);
+static void device_desc(unsigned char *, int, struct dev_mem *);
+static void config_map(struct cis *, unsigned char *, int);
+static void cis_config(struct cis *, unsigned char *, int);
+static struct tuple_list *read_one_tuplelist(int, int, off_t);
+static struct tuple_list *read_tuples(int);
+static struct tuple *find_tuple_in_list(struct tuple_list *, unsigned char);
+static struct tuple_info *get_tuple_info(unsigned char);
+
+static struct tuple_info tuple_info[] = {
+ {"Null tuple", 0x00, 0},
+ {"Common memory descriptor", 0x01, 255},
+ {"Checksum", 0x10, 5},
+ {"Long link to attribute memory", 0x11, 4},
+ {"Long link to common memory", 0x12, 4},
+ {"Link target", 0x13, 3},
+ {"No link", 0x14, 0},
+ {"Version 1 info", 0x15, 255},
+ {"Alternate language string", 0x16, 255},
+ {"Attribute memory descriptor", 0x17, 255},
+ {"JEDEC descr for common memory", 0x18, 255},
+ {"JEDEC descr for attribute memory", 0x19, 255},
+ {"Configuration map", 0x1A, 255},
+ {"Configuration entry", 0x1B, 255},
+ {"Other conditions for common memory", 0x1C, 255},
+ {"Other conditions for attribute memory", 0x1D, 255},
+ {"Geometry info for common memory", 0x1E, 255},
+ {"Geometry info for attribute memory", 0x1F, 255},
+ {"Manufacturer ID", 0x20, 4},
+ {"Functional ID", 0x21, 255},
+ {"Functional EXT", 0x22, 255},
+ {"Software interleave", 0x23, 2},
+ {"Version 2 Info", 0x40, 255},
+ {"Data format", 0x41, 255},
+ {"Geometry", 0x42, 4},
+ {"Byte order", 0x43, 2},
+ {"Card init date", 0x44, 4},
+ {"Battery replacement", 0x45, 4},
+ {"Organisation", 0x46, 255},
+ {"Terminator", 0xFF, 255},
+ {0, 0, 0}
+};
+
+/*
+ * After reading the tuples, decode the relevant ones.
+ */
+struct cis *
+readcis(int fd)
+{
+ struct tuple_list *tl;
+ struct tuple *tp;
+ struct cis *cp;
+
+ cp = xmalloc(sizeof(*cp));
+ cp->tlist = read_tuples(fd);
+ if (cp->tlist == 0)
+ return (NULL);
+
+ for (tl = cp->tlist; tl; tl = tl->next)
+ for (tp = tl->tuples; tp; tp = tp->next) {
+#if 0
+ printf("tuple code = 0x%02x, data is\n", tp->code);
+ dump(tp->data, tp->length);
+#endif
+ switch (tp->code) {
+ case CIS_MEM_COMMON: /* 0x01 */
+ device_desc(tp->data, tp->length, &cp->common_mem);
+ break;
+ case CIS_INFO_V1: /* 0x15 */
+ cis_info(cp, tp->data, tp->length);
+ break;
+ case CIS_MEM_ATTR: /* 0x17 */
+ device_desc(tp->data, tp->length, &cp->attr_mem);
+ break;
+ case CIS_CONF_MAP: /* 0x1A */
+ config_map(cp, tp->data, tp->length);
+ break;
+ case CIS_CONFIG: /* 0x1B */
+ cis_config(cp, tp->data, tp->length);
+ break;
+ }
+ }
+ return (cp);
+}
+
+/*
+ * free_cis - delete cis entry.
+ */
+void
+freecis(struct cis *cp)
+{
+ struct cis_ioblk *io;
+ struct cis_memblk *mem;
+ struct cis_config *conf;
+ struct tuple *tp;
+ struct tuple_list *tl;
+
+ while ((tl = cp->tlist) != 0) {
+ cp->tlist = tl->next;
+ while ((tp = tl->tuples) != 0) {
+ tl->tuples = tp->next;
+ if (tp->data)
+ free(tp->data);
+ }
+ }
+
+ while ((conf = cp->conf) != 0) {
+ cp->conf = conf->next;
+ while ((io = conf->io) != 0) {
+ conf->io = io->next;
+ free(io);
+ }
+ while ((mem = conf->mem) != 0) {
+ conf->mem = mem->next;
+ free(mem);
+ }
+ free(conf);
+ }
+ free(cp);
+}
+
+/*
+ * Fills in CIS version data.
+ */
+static void
+cis_info(struct cis *cp, unsigned char *p, int len)
+{
+ cp->maj_v = *p++;
+ cp->min_v = *p++;
+ strncpy(cp->manuf, p, CIS_MAXSTR - 1);
+ while (*p++);
+ strncpy(cp->vers, p, CIS_MAXSTR - 1);
+ while (*p++);
+ strncpy(cp->add_info1, p, CIS_MAXSTR - 1);
+ while (*p++);
+ strncpy(cp->add_info2, p, CIS_MAXSTR - 1);
+}
+
+/*
+ * device_desc - decode device descriptor.
+ */
+static void
+device_desc(unsigned char *p, int len, struct dev_mem *dp)
+{
+ while (len > 0 && *p != 0xFF) {
+ dp->valid = 1;
+ dp->type = (*p & 0xF0) >> 4;
+ dp->wps = !!(*p & 0x8);
+ dp->speed = *p & 7;
+ p++;
+ if (*p != 0xFF) {
+ dp->addr = *p >> 3;
+ dp->units = *p & 7;
+ }
+ p++;
+ len -= 2;
+ }
+}
+
+/*
+ * configuration map of card control register.
+ */
+static void
+config_map(struct cis *cp, unsigned char *p, int len)
+{
+ unsigned char *p1;
+ int i;
+ union {
+ unsigned long l;
+ unsigned char b[4];
+ } u;
+
+ p1 = p + 1;
+ cp->last_config = *p1++ & 0x3F;
+ u.l = 0;
+ for (i = 0; i <= (*p & 3); i++)
+ u.b[i] = *p1++;
+ cp->reg_addr = u.l;
+ cp->ccrs = *p1;
+}
+
+/*
+ * CIS config entry - Decode and build configuration entry.
+ */
+static void
+cis_config(struct cis *cp, unsigned char *p, int len)
+{
+ int x;
+ int i, j;
+ union {
+ unsigned long l;
+ unsigned char b[4];
+ } u;
+ struct cis_config *conf, *last;
+ struct cis_memblk *mem;
+ unsigned char feat;
+ struct cis_memblk *lastmem = 0;
+
+ conf = xmalloc(sizeof(*conf));
+ if ((last = cp->conf) != 0) {
+ while (last->next)
+ last = last->next;
+ last->next = conf;
+ } else
+ cp->conf = conf;
+ conf->id = *p & 0x3F;
+ if (*p & 0x40)
+ cp->def_config = conf;
+ if (*p++ & 0x80)
+ p++;
+ feat = *p++;
+ for (i = 0; i < CIS_FEAT_POWER(feat); i++) {
+ unsigned char parms = *p++;
+
+ conf->pwr = 1;
+ for (j = 0; j < 8; j++)
+ if (parms & (1 << j))
+ while (*p++ & 0x80);
+ }
+ if (feat & CIS_FEAT_TIMING) {
+ conf->timing = 1;
+ i = *p++;
+ if (CIS_WAIT_SCALE(i) != 3)
+ p++;
+ if (CIS_READY_SCALE(i) != 7)
+ p++;
+ if (CIS_RESERVED_SCALE(i) != 7)
+ p++;
+ }
+ if (feat & CIS_FEAT_I_O) {
+ conf->iospace = 1;
+ if (CIS_IO_RANGE & *p)
+ conf->io_blks = CIS_IO_BLKS(p[1]) + 1;
+ conf->io_addr = CIS_IO_ADDR(*p);
+ conf->io_bus = (*p >> 5) & 3;
+ if (*p++ & CIS_IO_RANGE) {
+ struct cis_ioblk *io, *last = 0;
+ i = CIS_IO_ADSZ(*p);
+ j = CIS_IO_BLKSZ(*p++);
+ for (x = 0; x < conf->io_blks; x++) {
+ io = xmalloc(sizeof(*io));
+ if (last)
+ last->next = io;
+ else
+ conf->io = io;
+ last = io;
+ u.l = 0;
+ switch (i) {
+ case 0:
+ break;
+ case 1:
+ u.b[0] = *p++;
+ break;
+ case 2:
+ u.b[0] = *p++;
+ u.b[1] = *p++;
+ break;
+ case 3:
+ u.b[0] = *p++;
+ u.b[1] = *p++;
+ u.b[2] = *p++;
+ u.b[3] = *p++;
+ break;
+ }
+ io->addr = u.l;
+ u.l = 0;
+ switch (j) {
+ case 0:
+ break;
+ case 1:
+ u.b[0] = *p++;
+ u.l++;
+ break;
+ case 2:
+ u.b[0] = *p++;
+ u.b[1] = *p++;
+ u.l++;
+ break;
+ case 3:
+ u.b[0] = *p++;
+ u.b[1] = *p++;
+ u.b[2] = *p++;
+ u.b[3] = *p++;
+ u.l++;
+ break;
+ }
+ io->size = u.l;
+ }
+ }
+ }
+ if (feat & CIS_FEAT_IRQ) {
+ conf->irq = 1;
+ conf->irqlevel = *p & 0xF;
+ conf->irq_flags = *p & 0xF0;
+ if (*p++ & CIS_IRQ_MASK) {
+ conf->irq_mask = (p[1] << 8) | p[0];
+ p += 2;
+ }
+ }
+ switch (CIS_FEAT_MEMORY(feat)) {
+ case 0:
+ break;
+ case 1:
+ conf->memspace = 1;
+ conf->mem = xmalloc(sizeof(*conf->mem));
+ conf->mem->length = ((p[1] << 8) | p[0]) << 8;
+ break;
+ case 2:
+ conf->memspace = 1;
+ conf->mem = xmalloc(sizeof(*conf->mem));
+ conf->mem->length = ((p[1] << 8) | p[0]) << 8;
+ conf->mem->address = ((p[3] << 8) | p[2]) << 8;
+ break;
+ case 3:
+ conf->memspace = 1;
+ x = *p++;
+ conf->memwins = CIS_MEM_WINS(x);
+ for (i = 0; i < conf->memwins; i++) {
+ mem = xmalloc(sizeof(*mem));
+ if (i == 0)
+ conf->mem = mem;
+ else
+ lastmem->next = mem;
+ lastmem = mem;
+ u.l = 0;
+ for (j = 0; j < CIS_MEM_LENSZ(x); j++)
+ u.b[j] = *p++;
+ mem->length = u.l << 8;
+ u.l = 0;
+ for (j = 0; j < CIS_MEM_ADDRSZ(x); j++)
+ u.b[j] = *p++;
+ mem->address = u.l << 8;
+ if (x & CIS_MEM_HOST) {
+ u.l = 0;
+ for (j = 0; j < CIS_MEM_ADDRSZ(x); j++)
+ u.b[j] = *p++;
+ mem->host_address = u.l << 8;
+ }
+ }
+ break;
+ }
+ if (feat & 0x80) {
+ conf->misc_valid = 1;
+ conf->misc = *p++;
+ }
+}
+
+/*
+ * Read the tuples from the card.
+ * The processing of tuples is as follows:
+ * - Read tuples at attribute memory, offset 0.
+ * - If a CIS_END is the first tuple, look for
+ * a tuple list at common memory offset 0; this list
+ * must start with a LINKTARGET.
+ * - If a long link tuple was encountered, execute the long
+ * link.
+ * - If a no-link tuple was seen, terminate processing.
+ * - If no no-link tuple exists, and no long link tuple
+ * exists while processing the primary tuple list,
+ * then look for a LINKTARGET tuple in common memory.
+ * - If a long link tuple is found in any list, then process
+ * it. Only one link is allowed per list.
+ */
+static struct tuple_list *tlist;
+
+static struct tuple_list *
+read_tuples(int fd)
+{
+ struct tuple_list *tl = 0, *last_tl;
+ struct tuple *tp;
+ int flag;
+ off_t offs;
+
+ tlist = 0;
+ last_tl = tlist = read_one_tuplelist(fd, MDF_ATTR, (off_t) 0);
+
+ /* Now start processing the links (if any). */
+ do {
+ flag = MDF_ATTR;
+ tp = find_tuple_in_list(last_tl, CIS_LONGLINK_A);
+ if (tp == 0) {
+ flag = 0;
+ tp = find_tuple_in_list(last_tl, CIS_LONGLINK_C);
+ }
+ if (tp && tp->length == 4) {
+ offs = tp->data[0] |
+ (tp->data[1] << 8) |
+ (tp->data[2] << 16) |
+ (tp->data[3] << 24);
+#ifdef DEBUG
+ printf("Checking long link at %ld (%s memory)\n",
+ offs, flag ? "Attribute" : "Common");
+#endif
+ /* If a link was found, read the tuple list from it. */
+ if (ck_linktarget(fd, offs, flag)) {
+ tl = read_one_tuplelist(fd, flag, offs);
+ last_tl->next = tl;
+ last_tl = tl;
+ }
+ }
+ } while (tl);
+
+ /*
+ * If the primary list had no NOLINK tuple, and no LINKTARGET,
+ * then try to read a tuple list at common memory (offset 0).
+ */
+ if (find_tuple_in_list(tlist, CIS_NOLINK) == 0 && tlist->next == 0 &&
+ ck_linktarget(fd, (off_t) 0, 0)) {
+#ifdef DEBUG
+ printf("Reading long link at %ld (%s memory)\n",
+ offs, flag ? "Attribute" : "Common");
+#endif
+ tlist->next = read_one_tuplelist(fd, 0, (off_t) 0);
+ }
+ return (tlist);
+}
+
+/*
+ * Read one tuple list from the card.
+ */
+static struct tuple_list *
+read_one_tuplelist(int fd, int flags, off_t offs)
+{
+ struct tuple *tp, *last_tp = 0;
+ struct tuple_list *tl;
+ struct tuple_info *tinfo;
+ int total = 0;
+ unsigned char code, length;
+
+ /* Check to see if this memory has already been scanned. */
+ for (tl = tlist; tl; tl = tl->next)
+ if (tl->offs == offs && tl->flags == (flags & MDF_ATTR))
+ return (0);
+ tl = xmalloc(sizeof(*tl));
+ tl->offs = offs;
+ tl->flags = flags & MDF_ATTR;
+ ioctl(fd, PIOCRWFLAG, &flags);
+ lseek(fd, offs, SEEK_SET);
+ do {
+ if (read_attr(fd, &code, 1) != 1) {
+ warn("CIS code read");
+ break;
+ }
+ total++;
+ if (code == CIS_NULL)
+ continue;
+ tp = xmalloc(sizeof(*tp));
+ tp->code = code;
+ if (read_attr(fd, &length, 1) != 1) {
+ warn("CIS len read");
+ break;
+ }
+ total++;
+ tp->length = length;
+#ifdef DEBUG
+ printf("Tuple code = 0x%x, len = %d\n", code, length);
+#endif
+ if (length == 0xFF) {
+ length = tp->length = 0;
+ code = CIS_END;
+ }
+ if (length != 0) {
+ total += length;
+ tp->data = xmalloc(length);
+ if (read_attr(fd, tp->data, length) != length) {
+ warn("CIS read");
+ break;
+ }
+ }
+
+ /*
+ * Check the tuple, and ignore it if it isn't in the table
+ * or the length is illegal.
+ */
+ tinfo = get_tuple_info(code);
+ if (tinfo == 0 || (tinfo->length != 255 && tinfo->length != length)) {
+ printf("code %s ignored\n", tuple_name(code));
+ tp->code = CIS_NULL;
+ }
+ if (tl->tuples == 0)
+ tl->tuples = tp;
+ else
+ last_tp->next = tp;
+ last_tp = tp;
+ } while (code != CIS_END && total < 1024);
+ return (tl);
+}
+
+/*
+ * return true if the offset points to a LINKTARGET tuple.
+ */
+static int
+ck_linktarget(int fd, off_t offs, int flag)
+{
+ char blk[5];
+
+ ioctl(fd, PIOCRWFLAG, &flag);
+ lseek(fd, offs, SEEK_SET);
+ if (read_attr(fd, blk, 5) != 5)
+ return (0);
+ if (blk[0] == 0x13 &&
+ blk[1] == 0x3 &&
+ blk[2] == 'C' &&
+ blk[3] == 'I' &&
+ blk[4] == 'S')
+ return (1);
+ return (0);
+}
+
+/*
+ * find_tuple_in_list - find a tuple within a
+ * single tuple list.
+ */
+static struct tuple *
+find_tuple_in_list(struct tuple_list *tl, unsigned char code)
+{
+ struct tuple *tp;
+
+ for (tp = tl->tuples; tp; tp = tp->next)
+ if (tp->code == code)
+ break;
+ return (tp);
+}
+
+static int
+read_attr(int fd, char *bp, int len)
+{
+ char blk[1024], *p = blk;
+ int i, l;
+
+ if (len > sizeof(blk) / 2)
+ len = sizeof(blk) / 2;
+ l = i = read(fd, blk, len * 2);
+ if (i <= 0) {
+ printf("Read return %d bytes (expected %d)\n", i, len * 2);
+ return (i);
+ }
+ while (i > 0) {
+ *bp++ = *p++;
+ p++;
+ i -= 2;
+ }
+ return (l / 2);
+}
+
+/*
+ * return table entry for code.
+ */
+static struct tuple_info *
+get_tuple_info(unsigned char code)
+{
+ struct tuple_info *tp;
+
+ for (tp = tuple_info; tp->name; tp++)
+ if (tp->code == code)
+ return (tp);
+ printf("Code %d not found\n", code);
+ return (0);
+}
+
+char *
+tuple_name(unsigned char code)
+{
+ struct tuple_info *tp;
+
+ tp = get_tuple_info(code);
+ if (tp)
+ return (tp->name);
+ return ("Unknown");
+}
diff --git a/usr.sbin/pccard/pccardd/readcis.h b/usr.sbin/pccard/pccardd/readcis.h
new file mode 100644
index 0000000..024b9dc
--- /dev/null
+++ b/usr.sbin/pccard/pccardd/readcis.h
@@ -0,0 +1,133 @@
+/*
+ * Copyright (c) 1995 Andrew McRae. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $Id: readcis.h,v 1.8 1997/02/22 16:09:00 peter Exp $
+ */
+
+#define CIS_MAXSTR 30
+struct tuple {
+ struct tuple *next;
+ unsigned char code;
+ int length;
+ unsigned char *data;
+};
+
+struct tuple_list {
+ struct tuple_list *next;
+ struct tuple *tuples;
+ off_t offs;
+ int flags;
+};
+
+struct tuple_info {
+ char *name;
+ unsigned char code;
+ unsigned char length; /* 255 means variable length */
+};
+
+/*
+ * Memory device descriptor.
+ */
+struct dev_mem {
+ unsigned char valid;
+ unsigned char type;
+ unsigned char speed;
+ unsigned char wps;
+ unsigned char addr;
+ unsigned char units;
+};
+
+/*
+ * One I/O structure describing a possible I/O map
+ * of the card.
+ */
+struct cis_ioblk {
+ struct cis_ioblk *next;
+ unsigned int addr;
+ unsigned int size;
+};
+
+/*
+ * A structure storing a memory map for the card.
+ */
+struct cis_memblk {
+ struct cis_memblk *next;
+ unsigned int address;
+ unsigned int length;
+ unsigned int host_address;
+};
+
+/*
+ * One configuration entry for the card.
+ */
+struct cis_config {
+ struct cis_config *next;
+ unsigned int pwr:1; /* Which values are defined. */
+ unsigned int timing:1;
+ unsigned int iospace:1;
+ unsigned int irq:1;
+ unsigned int memspace:1;
+ unsigned int misc_valid:1;
+ unsigned char id;
+ unsigned char io_blks;
+ unsigned char io_addr;
+ unsigned char io_bus;
+ struct cis_ioblk *io;
+ unsigned char irqlevel;
+ unsigned char irq_flags;
+ unsigned irq_mask;
+ unsigned char memwins;
+ struct cis_memblk *mem;
+ unsigned char misc;
+};
+
+/*
+ * Structure holding all data retrieved from the
+ * CIS block on the card.
+ * The default configuration contains interface defaults
+ * not listed in each separate configuration.
+ */
+struct cis {
+ struct tuple_list *tlist;
+ char manuf[CIS_MAXSTR];
+ char vers[CIS_MAXSTR];
+ char add_info1[CIS_MAXSTR];
+ char add_info2[CIS_MAXSTR];
+ unsigned char maj_v, min_v;
+ unsigned char last_config;
+ unsigned char ccrs;
+ unsigned long reg_addr;
+ struct dev_mem attr_mem;
+ struct dev_mem common_mem;
+ struct cis_config *def_config;
+ struct cis_config *conf;
+};
+
+void *xmalloc(int);
+void dumpcis(struct cis *);
+void freecis(struct cis *);
+struct cis *readcis(int);
+
+char *tuple_name(unsigned char);
diff --git a/usr.sbin/pccard/pccardd/util.c b/usr.sbin/pccard/pccardd/util.c
new file mode 100644
index 0000000..f963608
--- /dev/null
+++ b/usr.sbin/pccard/pccardd/util.c
@@ -0,0 +1,272 @@
+/*
+ * Copyright (c) 1995 Andrew McRae. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/*
+ * Code cleanup, bug-fix and extension
+ * by:
+ * Tatsumi Hosokawa <hosokawa@jp.FreeBSD.org>
+ * Nate Williams <nate@FreeBSD.org>
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id$";
+#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
+log_1s(const char *fmt, ...)
+{
+ va_list ap;
+ char s[256];
+
+ va_start(ap, fmt);
+ vsprintf(s, fmt, ap);
+
+ if (do_log)
+ syslog(LOG_ERR, s);
+ else {
+#ifdef SYSINSTALL
+ dialog_clear();
+ msgConfirm(s);
+#else
+ warnx("%s", s);
+#endif
+ }
+}
+
+void
+logerr(char *msg)
+{
+ if (do_log)
+ syslog(LOG_ERR, "%s: %m", msg);
+ else {
+#ifdef SYSINSTALL
+ dialog_clear();
+ msgConfirm(msg);
+#else
+ warn("%s", msg);
+#endif
+ }
+}
+
+/*
+ * Deliver last will and testament, and die.
+ */
+void
+die(char *msg)
+{
+ if (do_log)
+ syslog(LOG_CRIT, "fatal error: %s", msg);
+ else {
+#ifdef SYSINSTALL
+ char s[256];
+
+ sprintf(s, "cardd fatal error: %s\n", msg);
+ dialog_clear();
+ msgConfirm(s);
+#else
+ warnx("fatal error: %s", msg);
+#endif
+ }
+ closelog();
+ exit(1);
+}
+
+void *
+xmalloc(int sz)
+{
+ void *p;
+
+ p = malloc(sz);
+ if (p)
+ bzero(p, sz);
+ else
+ die("malloc failed");
+ return (p);
+}
+
+char *
+newstr(char *p)
+{
+ char *s;
+
+ s = strdup(p);
+ if (s == 0)
+ die("strdup failed");
+ return (s);
+}
+
+/*
+ * Find contiguous bit string (all set) of at
+ * least count number.
+ */
+int
+bit_fns(bitstr_t *nm, int nbits, int count)
+{
+ int i;
+ int found = 0;
+
+ for (i = 0; i < nbits; i++)
+ if (bit_test(nm, i)) {
+ if (++found == count)
+ return (i - count + 1);
+ } else
+ found = 0;
+ return (-1);
+}
+
+/*
+ * Allocate a block of memory and return the address.
+ */
+unsigned long
+alloc_memory(int size)
+{
+ int i;
+
+ i = bit_fns(mem_avail, MEMBLKS, size / MEMUNIT);
+ if (i < 0)
+ return (0);
+ bit_nclear(mem_avail, i, size / MEMUNIT);
+ return (BIT2MEM(i));
+}
+
+/*
+ * reset_slot - Power has been applied to the card.
+ * Now reset the card.
+ */
+void
+reset_slot(struct slot *sp)
+{
+ char c;
+ off_t offs;
+ struct mem_desc mem;
+ struct io_desc io;
+ int rw_flags;
+
+ rw_flags = MDF_ATTR;
+ ioctl(sp->fd, PIOCRWFLAG, &rw_flags);
+#ifdef DEBUG
+ printf("Resetting card, writing 0x80 to offs 0x%x\n",
+ sp->cis->reg_addr);
+#endif
+ offs = sp->cis->reg_addr;
+ lseek(sp->fd, offs, SEEK_SET);
+ c = 0x80;
+ write(sp->fd, &c, sizeof(c));
+ usleep(10 * 1000);
+ c = 0;
+ lseek(sp->fd, offs, SEEK_SET);
+ write(sp->fd, &c, sizeof(c));
+
+ /* Reset all the memory and I/O windows. */
+ bzero((caddr_t) & mem, sizeof(mem));
+ bzero((caddr_t) & io, sizeof(io));
+ for (mem.window = 0; mem.window < NUM_MEM_WINDOWS; mem.window++)
+ ioctl(sp->fd, PIOCSMEM, &mem);
+ for (io.window = 0; io.window < NUM_IO_WINDOWS; io.window++)
+ ioctl(sp->fd, PIOCSIO, &io);
+}
+
+/*
+ * execute - Execute the command strings.
+ * For the current slot (if any) perform macro
+ * substitutions.
+ */
+void
+execute(struct cmd *cmdp)
+{
+ 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",
+ current_slot->eaddr[0],
+ current_slot->eaddr[1],
+ current_slot->eaddr[2],
+ current_slot->eaddr[3],
+ current_slot->eaddr[4],
+ current_slot->eaddr[5]);
+ while (*++cp)
+ continue;
+ lp += 6;
+ } else
+ /* replace device name */
+ if (strncmp(p, "$device", 7) == 0) {
+ sprintf(cp, "%s%d",
+ current_slot->config->driver->kernel,
+ current_slot->config->driver->unit);
+ while (*cp)
+ cp++;
+ lp += 7;
+ } else
+ /* Copy the `$' and rescan. */
+ *cp++ = *lp++;
+ }
+ /* No more replacements. Copy rest of string. */
+ while ((*cp++ = *lp++) != 0)
+ continue;
+#ifdef DEBUG
+ fprintf(stderr, "Executing [%s]\n", cmd);
+#endif
+ system(cmd);
+ }
+}
diff --git a/usr.sbin/pciconf/Makefile b/usr.sbin/pciconf/Makefile
new file mode 100644
index 0000000..b608668
--- /dev/null
+++ b/usr.sbin/pciconf/Makefile
@@ -0,0 +1,7 @@
+# $ANA: Makefile,v 1.1.1.1 1996/09/25 21:12:57 wollman Exp $
+
+PROG= pciconf
+MAN8= pciconf.8
+CFLAGS+= -I${.CURDIR}/../../sys
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/pciconf/pathnames.h b/usr.sbin/pciconf/pathnames.h
new file mode 100644
index 0000000..34fc83a
--- /dev/null
+++ b/usr.sbin/pciconf/pathnames.h
@@ -0,0 +1 @@
+#define _PATH_DEVPCI "/dev/pci"
diff --git a/usr.sbin/pciconf/pciconf.8 b/usr.sbin/pciconf/pciconf.8
new file mode 100644
index 0000000..a7d6ac4
--- /dev/null
+++ b/usr.sbin/pciconf/pciconf.8
@@ -0,0 +1,185 @@
+.\" $Id: pciconf.8,v 1.2 1997/02/22 16:09:06 peter Exp $
+.\" Copyright (c) 1997
+.\" Stefan Esser <se@freebsd.org>. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\"
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.Dd February 7, 1997
+.Dt PCICONF 8
+.Os FreeBSD
+.Sh NAME
+.Nm pciconf
+.Nd diagnostic utility for the PCI bus
+.Sh SYNOPSIS
+.Nm pciconf Fl l
+.Nm pciconf Fl a Ar selector
+.Nm pciconf Fl r Ar selector
+.Op Fl b | Fl h
+.Ar reg
+.Nm pciconf Fl w Ar selector
+.Op Fl b | Fl h
+.Ar reg value
+.Sh DESCRIPTION
+The
+.Nm
+command provides a command line interface to the functionality provided by
+.Pa /dev/pci Ns 's
+.Xr ioctl 2
+interface.
+With the
+.Fl l
+option, it lists all devices found by the boot probe in the following format:
+.Bd -literal
+pci0:4:0: class=0x010000 card=0x00000000 chip=0x000f1000 rev=0x01 hdr=0x00
+pci0:5:0: class=0x000100 card=0x00000000 chip=0x88c15333 rev=0x00 hdr=0x00
+pci0:6:0: class=0x020000 card=0x00000000 chip=0x802910ec rev=0x00 hdr=0x00
+.Ed
+.Pp
+The first column gives the
+.Ar selector
+in a form which may directly be used for the other forms of the command.
+The second column is the class code, with the class byte printed as two
+hex digits, followed by the sub-class and the interface bytes.
+The third column gives the contents of the subvendorid register, introduced
+in revision 2.1 of the
+.Tn PCI
+standard. It is 0 for most current (2.0)
+.Tn PCI
+cards, but is supposed to be loaded with a unique card identification code
+in newly developed
+.Tn PCI
+cards. The field consists of the card ID in the upper
+half and the card vendor ID in the lower half of the value.
+.Pp
+The fourth column contains the chip device ID, which identifies the chip
+this card is based on. It consists of two fields, identifying the chip and
+its vendor, as above.
+The fifth column prints the chip's revision.
+The sixth column describes the header type.
+Currently assigned header types are 0 for all devices except
+.Tn PCI
+to
+.Tn PCI
+bridges, and 1 for such bridge chips. If the most significant bit
+of the header type register is set for
+function 0 of a
+.Tn PCI
+device, it is a
+.Em multi-function
+device, which contains several (similar or independent) functions on
+one chip.
+.Pp
+The
+.Fl l
+option is the only one available to non-root users.
+All other invocations of
+.Nm
+require a
+.Ar selector
+of the form
+.Li pci Ns Va bus Ns \&: Ns Va device
+(optionally followed by
+.Li \&: Ns Va function ) .
+A final colon may be appended and
+will be ignored; this is so that the first column in the output of
+.Nm
+.Fl l
+can be used without modification. All numbers are base 10.
+.Pp
+With the
+.Fl a
+flag,
+.Nm
+determines whether any driver has been assigned to the device
+identified by
+.Ar selector .
+An exit status of zero indicates that the device has a driver;
+non-zero indicates that it does not.
+.Pp
+The
+.Fl r
+option reads a configuration space register at byte offset
+.Ar reg
+of device
+.Ar selector
+and prints out its value in hexadecimal.
+The
+.Fl w
+option writes the
+.Ar value
+into a configuration space register at byte offset
+.Ar reg
+of device
+.Ar selector .
+For both operations, the flags
+.Fl b
+and
+.Fl h
+select the width of the operation;
+.Fl b
+indicates a byte operation, and
+.Fl h
+indicates a halfword (two-byte) operation. The default is to read or
+write a longword (four bytes).
+.Sh SEE ALSO
+.Xr ioctl 2 ,
+.\" .Xr pci 4 ,
+.Xr modload 8
+.Sh HISTORY
+The
+.Nm
+command appeared first in
+.Fx 2.2 .
+The
+.Fl a
+option was added for
+.Tn PCI
+LKM support in
+.Fx 3.0 .
+.Sh AUTHORS
+The
+.Nm
+facility was written by
+.An Stefan Esser
+and
+.An Garrett Wollman .
+.Sh BUGS
+The
+.Fl b
+and
+.Fl h
+options are implemented in
+.Nm pciconf ,
+but not in the underlying
+.Fn ioctl .
+.Pp
+It might be useful to give non-root users access to the
+.Fl a
+and
+.Fl r
+options. But only root will be able to execute a
+.Nm modload
+to provide the device with a driver LKM, 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..85a09e8
--- /dev/null
+++ b/usr.sbin/pciconf/pciconf.c
@@ -0,0 +1,243 @@
+/*
+ * Copyright 1996 Massachusetts Institute of Technology
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that both the above copyright notice and this
+ * permission notice appear in all copies, that both the above
+ * copyright notice and this permission notice appear in all
+ * supporting documentation, and that the name of M.I.T. not be used
+ * in advertising or publicity pertaining to distribution of the
+ * software without specific, written prior permission. M.I.T. makes
+ * no representations about the suitability of this software for any
+ * purpose. It is provided "as is" without express or implied
+ * warranty.
+ *
+ * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS
+ * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
+ * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/fcntl.h>
+
+#include <err.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <pci/pcivar.h>
+#include <pci/pci_ioctl.h>
+
+#include "pathnames.h"
+
+static void list_devs(void);
+static void readit(const char *, const char *, int);
+static void writeit(const char *, const char *, const char *, int);
+static void chkattached(const char *, int);
+
+static exitstatus = 0;
+
+static void
+usage()
+{
+ fprintf(stderr, "%s\n%s\n%s\n%s\n",
+ "usage: pciconf -l",
+ " pciconf -a sel",
+ " pciconf -r [-b | -h] sel addr",
+ " pciconf -w [-b | -h] sel addr [value]");
+ exit (1);
+}
+
+int
+main(int argc, char **argv)
+{
+ int c;
+ int listmode, readmode, writemode, attachedmode;
+ int byte, isshort;
+
+ listmode = readmode = writemode = attachedmode = byte = isshort = 0;
+
+ while ((c = getopt(argc, argv, "alrwbh")) != -1) {
+ switch(c) {
+ case 'a':
+ attachedmode = 1;
+ break;
+
+ case 'l':
+ listmode = 1;
+ break;
+
+ case 'r':
+ readmode = 1;
+ break;
+
+ case 'w':
+ writemode = 1;
+ break;
+
+ case 'b':
+ byte = 1;
+ break;
+
+ case 'h':
+ isshort = 1;
+ break;
+
+ default:
+ usage();
+ }
+ }
+
+ if ((listmode && optind != argc)
+ || (writemode && optind + 3 != argc)
+ || (readmode && optind + 2 != argc)
+ || (attachedmode && optind + 1 != argc))
+ usage();
+
+ if (listmode) {
+ list_devs();
+ } else if(attachedmode) {
+ chkattached(argv[optind],
+ byte ? 1 : isshort ? 2 : 4);
+ } else if(readmode) {
+ readit(argv[optind], argv[optind + 1],
+ byte ? 1 : isshort ? 2 : 4);
+ } else if(writemode) {
+ writeit(argv[optind], argv[optind + 1], argv[optind + 2],
+ byte ? 1 : isshort ? 2 : 4);
+ } else {
+ usage();
+ }
+
+ return exitstatus;
+}
+
+static void
+list_devs(void)
+{
+ int fd;
+ struct pci_conf_io pc;
+ struct pci_conf conf[255], *p;
+
+ fd = open(_PATH_DEVPCI, O_RDONLY, 0);
+ if (fd < 0)
+ err(1, "%s", _PATH_DEVPCI);
+
+ pc.pci_len = sizeof(conf);
+ pc.pci_buf = conf;
+
+ if (ioctl(fd, PCIOCGETCONF, &pc) < 0)
+ err(1, "ioctl(PCIOCGETCONF)");
+
+ close(fd);
+
+ for (p = conf; p < &conf[pc.pci_len / sizeof conf[0]]; p++) {
+ printf("pci%d:%d:%d:\tclass=0x%06x card=0x%08lx chip=0x%08lx rev=0x%02x hdr=0x%02x\n",
+ p->pc_sel.pc_bus, p->pc_sel.pc_dev, p->pc_sel.pc_func,
+ p->pc_class >> 8, p->pc_subid,
+ p->pc_devid, p->pc_class & 0xff, p->pc_hdr);
+ }
+}
+
+static struct pcisel
+getsel(const char *str)
+{
+ char *ep = (char*) str;
+ struct pcisel sel;
+
+ if (strncmp(ep, "pci", 3) == 0) {
+ ep += 3;
+ sel.pc_bus = strtoul(ep, &ep, 0);
+ if (!ep || *ep++ != ':')
+ errx(1, "cannot parse selector %s", str);
+ sel.pc_dev = strtoul(ep, &ep, 0);
+ if (!ep || *ep != ':') {
+ sel.pc_func = 0;
+ } else {
+ ep++;
+ sel.pc_func = strtoul(ep, &ep, 0);
+ }
+ }
+ if (*ep == ':')
+ ep++;
+ if (*ep || ep == str)
+ errx(1, "cannot parse selector %s", str);
+ return sel;
+}
+
+static void
+readit(const char *name, const char *reg, int width)
+{
+ int fd;
+ struct pci_io pi;
+
+ pi.pi_sel = getsel(name);
+ pi.pi_reg = strtoul(reg, (char **)0, 0); /* XXX error check */
+ pi.pi_width = width;
+
+ fd = open(_PATH_DEVPCI, O_RDWR, 0);
+ if (fd < 0)
+ err(1, "%s", _PATH_DEVPCI);
+
+ if (ioctl(fd, PCIOCREAD, &pi) < 0)
+ err(1, "ioctl(PCIOCREAD)");
+
+ printf("0x%08x\n", pi.pi_data);
+}
+
+static void
+writeit(const char *name, const char *reg, const char *data, int width)
+{
+ int fd;
+ struct pci_io pi;
+
+ pi.pi_sel = getsel(name);
+ pi.pi_reg = strtoul(reg, (char **)0, 0); /* XXX error check */
+ pi.pi_width = width;
+ pi.pi_data = strtoul(data, (char **)0, 0); /* XXX error check */
+
+ fd = open(_PATH_DEVPCI, O_RDWR, 0);
+ if (fd < 0)
+ err(1, "%s", _PATH_DEVPCI);
+
+ if (ioctl(fd, PCIOCWRITE, &pi) < 0)
+ err(1, "ioctl(PCIOCWRITE)");
+}
+
+static void
+chkattached (const char *name, int width)
+{
+ int fd;
+ struct pci_io pi;
+
+ pi.pi_sel = getsel(name);
+ pi.pi_reg = 0;
+ pi.pi_width = width;
+ pi.pi_data = 0;
+
+ fd = open(_PATH_DEVPCI, O_RDWR, 0);
+ if (fd < 0)
+ err(1, "%s", _PATH_DEVPCI);
+
+ if (ioctl(fd, PCIOCATTACHED, &pi) < 0)
+ err(1, "ioctl(PCIOCATTACHED)");
+
+ exitstatus = pi.pi_data ? 0 : 2; /* exit(2), if NOT attached */
+ printf("%s: %s%s\n", name, pi.pi_data == 0 ? "not " : "", "attached");
+}
diff --git a/usr.sbin/pcvt/Makefile b/usr.sbin/pcvt/Makefile
new file mode 100644
index 0000000..1a3be34
--- /dev/null
+++ b/usr.sbin/pcvt/Makefile
@@ -0,0 +1,7 @@
+SUBDIR= keycap cursor fontedit fonts kcon loadfont scon \
+ userkeys vttest ispcvt mcon
+SUBDIR+= vgaio kbdio set2061
+SUBDIR+= demo
+SUBDIR+= Misc
+
+.include <bsd.subdir.mk>
diff --git a/usr.sbin/pcvt/Makefile.inc b/usr.sbin/pcvt/Makefile.inc
new file mode 100644
index 0000000..1329c97
--- /dev/null
+++ b/usr.sbin/pcvt/Makefile.inc
@@ -0,0 +1,8 @@
+BINDIR = /usr/sbin
+FONTDIR = /usr/share/misc/pcvtfonts
+LIBDIR = /usr/lib
+LIBMODE = 644
+
+MAN3EXT= 3
+MAN5EXT= 5
+MAN8EXT= 8
diff --git a/usr.sbin/pcvt/Misc/Doc/Acknowledgements b/usr.sbin/pcvt/Misc/Doc/Acknowledgements
new file mode 100644
index 0000000..327f23d
--- /dev/null
+++ b/usr.sbin/pcvt/Misc/Doc/Acknowledgements
@@ -0,0 +1,111 @@
+Thank You, (in the order of appearance)
+
+
+ Lynne and Bill Jolitz
+
+ for your work on 386BSD and making all this possible !
+
+
+ Holger Veit (veit@du9ds3.uni-duisburg.de)
+
+ for the permission to use a modified version of his keyboard
+ driver and utilities for keyboard remapping / multinational
+ keyboard support.
+
+
+ Per Lindberg
+
+ for the extremely helpful vt100 terminal testprogram found
+ in the directory vttest.
+
+
+ John Birchfield
+
+ for the ncsa telnet package, which contains a vt100 emulation
+ and which was very helpful in studying some concepts.
+
+
+ Ralf Friedl (friedl@informatik.uni-kl.de)
+
+ for making his implementation of multi-sceens for the net-2
+ distribution available. i looked at this and took some ideas
+ and lines from his code.
+
+
+ Bruce Evans (bde@runx.oz.au)
+
+ for contributing some bugfixes and a complete termcap entry
+
+
+ Brian H. Dunford-Shore (brian@morpheus.wustl.edu)
+
+ for contributing most of the EGA/VGA screen switching code
+ and being a fast, reliable and responsive co-author. This
+ driver would not be what it is without Brian, Thank You !
+
+
+ Frank da Cruz (fdc@columbia.edu)
+
+ for my famous datacomm program and for giving the permission
+ to redistribute files from the msdos kermit distribution
+ located in the directory support/demo.
+
+
+ Joerg Wunsch (joerg_wunsch@uriah.sax.de)
+
+ for contributing precise bugreports and -fixes, the 8x10
+ EGA/VGA fonts and the color palette ioctls and for being
+ a very responsive contributor of various ideas.
+ Joerg wrote pcvt's interface to XFree86 1.2 and 1.3 and
+ the SYSV/syscons - like interface to XFree86 2.0.
+ There would be no support for X without Joerg's work!
+ Thank you very much Joerg, i enjoy it !!! :-)
+
+
+ Scott Turner (scotty@gagetalker.com)
+
+ for contributing code to change the winsize structure, many
+ discussions on the keyboard code and fine-tuning the driver
+
+
+ Gordon L. Burditt (gordon@sneaky.lonestar.org)
+
+ for the nicest and most complete bugreports i ever got
+
+
+ Theo Deraadt (deraadt@fsa.ca)
+
+ for pushing me forward. There would be no 3.00 release
+ if Theo didn't asked for features ... ;-)
+
+
+ Onno van der Linden (c/o frank@fwi.uva.nl)
+
+ for writing the 132 column support for the Cirrus
+ chipsets although he had no time to do it ... :-)
+
+
+ Wolfram Solfrank, Ingo Koenig
+
+ for putting some data onto tape (and handling and shipping
+ in the case of Wolfgang) to provide me with some latest
+ sources because i still have no ip-connectivity ....
+
+
+ Michael Havemester (tik@abqhh.hanse.de)
+
+ for giving me a chance to stay up to date with NetBSD-
+ current, for programming the initial version of the fast
+ scrolling code and for the keyboard fifo code!
+
+
+ Charles Hannum (mycroft@gnu.ai.mit.edu)
+
+ for getting bored by a slow-scrolling video driver and
+ for leaving me some bugs to fix ;-)
+
+
+ The NetBSD and FreeBSD teams
+
+ for giving me something to play, work and learn with !
+ There would be nothing to write a driver for without you !!!
diff --git a/usr.sbin/pcvt/Misc/Doc/Bibliography b/usr.sbin/pcvt/Misc/Doc/Bibliography
new file mode 100644
index 0000000..2cce039
--- /dev/null
+++ b/usr.sbin/pcvt/Misc/Doc/Bibliography
@@ -0,0 +1,189 @@
+
+VGA BOOKS
+---------
+
+ Richard Wilton, "Programmers Guide to PC & PS/2 Video Systems",
+ Microsoft Press 1987
+
+
+ Richard F. Ferraro, "Programmers Guide to the EGA and VGA Cards",
+ Second Edition, Addison-Wesley 1990
+
+
+ Richard F. Ferraro, "Programmers Guide to the EGA and VGA Cards",
+ Third Edition, Addison-Wesley 1994
+
+
+ Matthias Uphoff, "Die Programmierung der EGA/VGA Grafikkarte",
+ Addison Wesley 1992
+
+
+ Bradley Dyck Kliewer, "EGA/VGA A Programmers Reference Guide",
+ McGraw Hill, 2nd Edition 1990
+
+
+UNIX AND UNIX DEVICE DRIVERS
+----------------------------
+
+ Bell Telephone Laboratories, Inc. "UNIX Programmer's Manual, Seventh
+ Edition, Volume 2". Revised and Expanded Version.
+ Holt, Rinehart and Winston 1983
+
+
+ George Pajari, "Writing Unix Device Drivers"
+ Addison Wesley 1992
+
+
+ Janet I. Egan and Thomas J. Teixeira, "Writing a UNIX Device Driver"
+ John Wiley & Sons 1988
+
+
+ Janet I. Egan and Thomas J. Teixeira, "Writing a UNIX Device Driver"
+ Second Edition. John Wiley & Sons 1992
+
+
+ Leffler, McKusick, Karels, Quarterman, "The Design and Implementation
+ of the 4.3BSD UNIX Operating System"
+ Addison Wesley 1988, corrected Reprint 1989
+
+
+ Leffler, McKusick, "The Design and Implementation of the 4.3BSD UNIX
+ Operating System, Answer Book"
+ Addison Wesley 1991
+
+
+ Maurice J. Bach, "The Design of the UNIX Operating System"
+ Prentice-Hall 1986
+
+
+ Sun Microsystems Inc., "Writing Device Drivers"
+ Part No. 800-3851-10, Revision A of 27 March 1990
+
+
+ Hewlett-Packard Company, "HP-UX Driver Development Guide",
+ Part No. 98577-90013, First Edition 07/91
+
+
+ W. Richard Stevens, "Advanced Programming in the UNIX Environment",
+ Addison Wesley 1992
+
+
+ Phillip M. Adams, Clovis L. Tondo, "Writing Unix Device Drivers in C",
+ Prentice Hall 1993
+
+
+ Berny Goodheart, James Cox, "The Magic Garden Explained",
+ Prentice Hall 1994
+
+
+ Peter Kettle, Steve Statler, "Writing Device Drivers for SCO Unix"
+ Addison Wesley 1993
+
+
+TERMINAL MANUALS
+----------------
+
+ Digital Equipment, VT100 Users Manual, 2nd ed. 1979
+ Digital Equipment, VT132 Users Manual
+ Digital Equipment, VT220 Programmers Reference Manual, 2nd ed. 1984
+ Digital Equipment Corporation
+
+
+ Hewlett Packard, 700/92 Technical Reference Manual (ANSI Operation)
+ Hewlett Packard, 2392a Users Manual (ANSI Operation)
+ Hewlett-Packard Company
+
+
+ Walker Richer & Quinn, Inc., "Reflection 2 and Reflection 4 Technical
+ Reference Manual", Version 4.2
+ Walker, Richer & Quinn, Seattle, August 1992
+
+
+IBM PC HARDWARE/FIRMWARE
+------------------------
+
+ Frank van Gilluwe, "The Undocumented PC",
+ Addison Wesley, First Edition May 1994
+
+
+ IBM Corporation, "Technical Reference Personal Computer AT",
+ Part No. 6280070, Form No. S229-9611-00, First Edition 1985
+
+
+ Phoenix Technologies Ltd., "System BIOS for IBM PC/XT/AT Computers
+ and Compatibles", Addison Wesley, Fourth Printing June 1990
+
+
+ Phoenix Technologies Ltd., "System BIOS for IBM PCs, Compatibles,
+ and EISA Computers", Second Edition
+ Addison Wesley, First Printing May 1991
+
+
+ American Megatrends, Inc., "Hi-Flex ISA and EISA AMIBIOS Technical
+ Reference", American Megatrends, Inc. 1992 (9/25/92)
+
+
+ Thom Hogan, "The Programmers PC Sourcebook", 2nd Edition
+ Microsoft Press, 1991
+
+
+TERMCAP/TERMINFO
+----------------
+
+ John Strang, Linda Mui and Tim O'Reilly, "Termcap and Terminfo",
+ O'Reilly & Associates, Inc. , April 1991
+
+
+ Richard M. Stallman, "Termcap - The Termcap Library and Data Base",
+ Free Software Foundation, Second Edition November 1988
+
+
+
+DATABOOKS/DATASHEETS
+--------------------
+
+ Intel Corporation, "Microsystem Components Handbook Volume II",
+ Intel Corporation, 1984
+
+
+ Intel Corporation, "Peripheral Design Handbook",
+ Intel Corporation, 1980
+
+
+ Tseng Labs, Inc. "ET4000 Graphics Controller Data Book", 1990
+
+
+ Western Digital Corporation, "WD90C11, WD90C11A (PVGA1C) Enhanced
+ VGA Controller", Western Digital 9/18/91
+
+
+ Western Digital Corporation, "WD90C00 Enhanced VGA Controller",
+ Western Digital 1/14/91
+
+
+ Western Digital Corporation, "WD90C00 Interface Guide",
+ Western Digital 1/10/91
+
+
+ Western Digital Corporation, "VGA Register Based Programmers Manual",
+ Western Digital 1/30/91
+
+
+ Western Digital Corporation, "VGA BIOS Programmers Manual",
+ Western Digital 1/10/91
+
+
+ Cirrus Logic, "True Color VGA Family - CL-GD542X", Technical Ref.
+ Manual, Cirrus Logic, Inc. April 1993
+
+
+ S3 Inc, "86C801/86C805 GUI Accelerators"
+ S3 Incorporated, September 1992
+
+
+ S3 Inc, "86C928 GUI Accelerator"
+ S3 Incorporated, July 1993
+
+
+ Trident Microsystems Inc., "TVGA9000i Technical Reference Manual",
+ Trident Microsystems, Inc. (c) 1992, March 1993, Rev 1
diff --git a/usr.sbin/pcvt/Misc/Doc/BugList b/usr.sbin/pcvt/Misc/Doc/BugList
new file mode 100644
index 0000000..9c8feef
--- /dev/null
+++ b/usr.sbin/pcvt/Misc/Doc/BugList
@@ -0,0 +1,62 @@
+
+List of known Bugs Last Edit-Date: [Sun Mar 5 13:03:51 1995]
+================================================================================
+
+
+Description: Fixed/done by/date
+------------------------------------------- --------------------------------
+Util/keycap/man5/keycap.5 does STILL not
+build correctly when doing a make obj.
+Will there ever be a solution ... :-)
+------------------------------------------- --------------------------------
+NetBSD PR #404: Meta-Control-Space broken
+with PCVT_META_ESC and PCVT_NULLCHARS set
+Meta-Control-Space should (theoretically)
+send ESC NUL if PCVT_NULLCHARS and
+PCVT_META_ESC are defined; in reality, it
+just sends ESC. Because of the grody
+encoding hack used by sgets() to implement
+PCVT_NULLCHARS (if the first character is
+a null, send it), there isn't a trivial
+one-line fix.
+How-To-Repeat: in a kernel with
+PCVT_NULLCHARS and PCVT_META_ESC, type a
+M-C-SPC to emacs.
+------------------------------------------- --------------------------------
+when auto switching to vt0 is enabled by
+PCVT_SW0CNOUTP and the screen is switched
+from an X-vt to screen 0, the video mem
+is irrecoverably destroyed - no data loss
+------------------------------------------- --------------------------------
+Altgr+Shift+key is not separately They way the keyboard mapping
+mappable. is done is subject to a sub-
+ stantial rewrite in a future
+ release
+------------------------------------------- --------------------------------
+132 column mode not working with #9 GXE
+(S3-based) (sorry, i don't have one -hm)
+(see discussion of 132 column mode in the
+ NotesAndHints file!)
+------------------------------------------- --------------------------------
+WD90Cxx chip detection fails to detect
+chips other than C00,C10 and C11. C30
+chips are detected as C11s ....
+------------------------------------------- --------------------------------
+Xfree86 2.0 locks the console when started
+under NetBSD 0.9. SuperProbe and X both
+show process status "DE+" in ps. Work-
+around is to disable PCVT_USL_VT_COMPAT.
+------------------------------------------- --------------------------------
+On one keyboard, if a "Lock" key is pressed
+the leds do not get updated and the key-
+board hangs.
+------------------------------------------- --------------------------------
+HP function key labels code needs to set
+the user defined fkey string somehow!
+------------------------------------------- --------------------------------
+Video 7 1024i not fully supported yet, has
+sometimes problems with some chars to displ
+------------------------------------------- --------------------------------
+The HP-Escape sequences need to be FULLY 28.12.93, -hm did a bit to supp-
+implemented port elm. Needs MORE work !
+------------------------------------------- --------------------------------
diff --git a/usr.sbin/pcvt/Misc/Doc/ChangeLog b/usr.sbin/pcvt/Misc/Doc/ChangeLog
new file mode 100644
index 0000000..2e8547e
--- /dev/null
+++ b/usr.sbin/pcvt/Misc/Doc/ChangeLog
@@ -0,0 +1,899 @@
+ChangeLog last edit-date: [Thu Apr 6 10:52:50 1995]
+================================================================================
+
+Changes 3.10 -> 3.20 April 1995
+--------------------------------------------------------------------------------
+
+- bugfix from Thomas Eberhardt: the force 24 lines enable function key
+ label was not updated when changing force 24 lines mode with
+ 'scon -f on|off', this is now fixed.
+
+- bugfix from John Kohl fixing divide by zero problem in pcvt_ext.c when
+ ringing the bell and pitch is 0: this avoids an integer divide
+ trap in supervisor mode.
+
+- changed the default behaviour of keyboard controller delay from using
+ delay()/DELAY() to using dummy reads to port 0x84 because i got
+ keyboard hangs in X.
+
+- bugfix: in pcvt_ext.c, MDA state restore when leaving X was lost, should
+ function now again.
+
+- renamed PCVT_XSERVER to XSERVER in ispcvt.c
+
+- bugfix in pcvt_vtf.c: renamed PCVT_USL_COMPAT to PCVT_USL_VT_COMPAT
+
+- added '-d <device>' option to ispcvt and loadfont
+
+- removed PCVT_NEEDPG
+
+- almost completely removed support for 386BSD 0.1 with patchkit 0.2.4
+
+- applied a patch from Joerg providing missing FreeBSD 2.1 functionality and
+ a some minor bugfixes and checks.
+
+- provide a single place PCVT_KBD_DELAY in pcvt_hdr.h. made it configurable
+ via PCVT_PORTIO_DELAY to use either 6 dummy reads from port 0x84
+ or the delay/DELAY function of the operating system. Changed ispcvt
+ to report status of compile time switch.
+
+- release beta 23 --------------------------------------------------------------
+
+- removed some files for 386BSD as there is virtually noone outside
+ using it anymore
+
+- make pcvt_ioctl.h accept "KERNEL" or "_KERNEL" for NetBSD-current
+
+- configuration fix: patch from Rafal Boni for pcvt_vtf.c, he writes:
+ This patch lets one compile pcvt on a system that is lacking XSERVER
+ and UCONSOLE [or either?] in the config file. I found this while
+ trying to build a minimal floppy-based test kernel. The problem is
+ that roll_up and roll_down assume that PCVT_USL_COMPAT is
+ unconditionally set, and hence muck with parts of the video structure
+ that are only there if PCVT_USL_COMPAT is set.
+ (generalized the patch a bit so that fastscroll functionality is
+ not lost in case of PCVT_USL_COMPAT not being defined. -hm)
+
+- bugfix: pcvt_ext.c switch_screen() - when switching to a 25 line charset
+ pure VT emulation screen which has force 24 lines set, then we have
+ to clear the last line on screen (bugreport from Joerg).
+
+- added define PCVT_NONRESP_KEYB_TRY for how many times to try to detect
+ a non-connected keyboard. This was previously set to 100 and
+ Joerg reports a 5+ minutes delay for a 386 booting without keyboard.
+ Set to 25 now in the definition. Also made shure the messages in
+ the corresponding loop display only once. [doreset() in pcvt_kbd.c]
+
+- updated kbdio utility to include the 7 us delay and added a 'what' command
+ detect type of MCA motherboard keyboard controller according to
+ Frank van Gilluwe, "The Undocumented PC", Addison Wesley, pp 273
+
+- "Gateway 2000" problem: after some discussion on the NetBSD port-i386
+ mailing list about hanging keyboards, Martin Husemann sent in a
+ patch where each inb(CONTROLLER_DATA) is now preceeded by a
+ delay(6). This fixed his keyboard problem, the same was reported
+ from Roland McGrath for his Gateway 2000 keyboard/machine.
+
+- patch from John Kohl for usl_vt_ioctl() in file pcvt_ext.c, he writes:
+ This is probably a long standing bug. tsleep() returns ERESTART
+ if it's interrupted. ERESTART is -1, which means that the
+ VT_WAITACTIVE call returns from usl_vt_ioctl() with -1,
+ meaning "not one of my ioctl's".
+
+- patch from Matthieu Herrb for NetBSD-current (post 1.0) support
+
+- patch from Joerg for FreeBSD pre-2.1 support
+
+- added file ToDo in directory Doc
+
+- fix for Util/Makefile.inc.NetBSD-c from Onno
+
+- removing unnecessary Trident support code from pcvt_out.c
+
+- fix from Onno for IST_EDGE in NetBSD-current
+
+- patch from Thomas Eberhardt to fix the bell frequency and duration setting
+ for NetBSD
+
+- some fixes from Onno van der Linden for NetBSD
+
+- added patch for NetBSD-current from Rafal Boni and Lon Willett
+
+- added another mega patch from Lon Willett fixing several bugs:
+
+ 1 -- Misc porting changes to deal with NetBSD-current, including
+ a new Util/Makefile.inc.NetBSD-current.
+
+ 2 -- The Control_R scancodes were being mapped to the Control_L
+ pcvt keynum.
+
+ 3 -- FASTSCROLL/graphics-mode problem: there was a problem where
+ switching from a graphics screen to a text screen would corrupt
+ the text screen's content if it had been scrolling using the
+ FASTSCROLL code. (The problem is the bcopy() from kernel to
+ video memory was using the vs[i].Memory pointer as the source,
+ instead of the vs[i].Crtat pointer). This is fixed in the beta
+ 21 release by just disabling the fast scroll code for screens
+ which aren't active.
+
+ The patch I'm sending you re-enables the fast scroll code on
+ inactive (i.e. in kernel memory) screens, saving a few cpu
+ cycles.
+
+ 4 -- This is a big one: running multiple X servers was not
+ really working. The USL handshaking code was a mess (not very
+ well designed to begin with, but there's not much that can be
+ done about that). I did manage to get it fairly functional, but
+ there's a lot of changes.
+
+- changing example rc.local to take care of good old EGA's
+
+- fixing support for EGA boards in vt_coldinit()
+
+- removing vt100 font files from Util/fonts, updated Manifest
+
+- fixing an obviously longstanding bug in roll_up() / roll_down() which was
+ triggered by Joergs "Crtat" cleanup mega patch.
+
+- removing PCVT_FAKE_SYSCONS10
+
+- removing duplicate names in termcap entries
+
+- fixing vt_coldinit: setting variable "color" for mda/hercules and cga
+ initializing cursor position
+
+- fixing roll_up() and roll_down() for mda/hercules: mda's have ALWAYS
+ just one page of memory, original hercules boards too so they
+ can NEVER use the fastscroll option. SOME hercules are somehow
+ enhanced in that they support 2 pages. pcvt now just supports
+ one page hercules/mda!
+
+- made cleanups to 8x14 high and 8x8 high fonts: adjust the 5 scanline
+ characters to adjust with the corner characters from the low fonts
+
+- applying patch from Joerg which fixes some bugs:
+
+ writing to a tty containing an X server resulted in some stange
+ behaviour depending on the opsys and opsys version used
+
+ the init code preserving the screen contents and cursor shape
+ can only be done on boards allowing the reading of
+ several crtc registers
+
+- removed bcopyb declaration from pcvt_hdr.h for FreeBSD 2.1
+
+- another patch from Lon Willett (willett@math.utah.edu), he writes:
+
+ 1 -- Very minor: One of my previous changes didn't get merged
+ into "pcvt_out.c"; the caclulation of ws_row doesn't account for
+ "force24" at one spot. It's easier to just set it from
+ screen_rows anyway, unless there's something that I didn't
+ understand happening here.
+
+ 2 -- Debugging stuff: I don't know if you care to add it, but I
+ found it useful, so I left it in. I rearranged the
+ PCVT_SHOWKEYS code a little, and added a few lines to show
+ keyboard commands and responses, with special delimiters.
+ [file: pcvt_kbd.c]
+
+ 3 -- "kcon" utility enhancement: I added a "-R" switch to kcon
+ to do a KBDRESET ioctl. [file: Util/kcon/kcon.c]
+
+ 4 -- KBDRESET-ioctl bug: the code was trying to read kbd
+ responses without an spltty(), so naturally it would fail, since
+ pcrint() was grabbing the ACKs. [file: pcvt_kbd.c]
+
+ 5 -- update_led() makes kbd hang: the problem here is that
+ update_led() makes the keyboard generate two KEYB_R_ACK
+ responses, and one of the interrupts sometimes gets lost when
+ there is a lot of other I/O happening. See the comment in the
+ code. My fix seems to work for me, but you may want to handle
+ it some other way. It would probably be quite reasonable to
+ check for lost interrupts periodically anyway, whether
+ update_led() has been called or not. [file: pcvt_kbd.c]
+
+ (NOTE: item 5 has been disabled because it causes older
+ FreeBSD's to panic because of the timer queue not being
+ initialized at the time called. -hm)
+
+- partly rewrote doreset() in pcvt_kbd.c to enable boot procedure to proceed
+ if no (PC) keyboard is found. The current behaviour and implementation
+ is subject to change.
+
+- patch from Thomas Gellekum to Util/keycap/keycap.src
+
+- minor modification to main.c in Util/set2061
+
+- patch from Onno and John Kohl to make pcvt beta 16 work on NetBSD current:
+ 1. pcvt_conf.h
+ _real_ 1.0 ===> NetBSD1_0 == 1
+ current 1.0 (1.0A) ===> NetBSD1_0 == 2
+ 2. pcvt_hdr.h
+ Put pcvt_conf.h before _all_ the #ifdef PCVT_*. This way
+ the #ifdef NetBSDx_y stuff can do its work.
+
+- size of PCVT_BURST was still reported as 1 for FreeBSD 2.0, fixed
+
+- made some adjustments for FreeBSD 2.0 in Util/vgaio
+
+- Another patch from Joerg for FreeBSD 2.0
+
+- included vgaio, kbdio and set2061 into the outer Makefile, this programs
+ are build and cleaned but don't install anything.
+ All program's in the Util dir should now cleanly make <anything> ...
+
+- enable 132 column support for Trident TVGA8900CL, some NetBSDisms
+
+- addend patch from Joerg for pcvt-320b7 to fix FreeBSD 2.0's ttymalloc's
+ encapsulation into #if's. Also new keycap.src entry.
+
+- fixed keyboard status/LED not updated on soft reset emulator
+
+- on 23rd of December i had some spare time, so i added some demos to the
+ Util/demo directory as well a a time-killer program (playvt) to view
+ some of the VT animations and a christmas animation i got from Joerg.
+
+- added a new utility set2061 to program the clock generator on my S3 based
+ ELSA Winner VGA board. This eventually lets me use 132 columns on this
+ board, because i'm now able to program the clock to generate 40MHz
+ on clock output #2.
+
+- debugged winsize patch on FreeBSD 1.1, line discipline open init's the
+ queues, winsize initialization must happen after line disc. open!
+
+- got a bunch of NetBSD PR's from J.T. Conklin:
+
+ PR #214: PCVT treats ctrl-shift exactly like shift for most characters.
+ (fixed by Lon Willet's Mega Patch described below -hm)
+
+ PR #399: If you define PCVT_META_ESC when builting PCVT, Meta-Return
+ sends 0x8d instead of ESC RET.
+ (fixed by applying the patch from Bill Sommerfeld. -hm)
+
+ PR #400: pcvt sometimes gets confused about window size
+ (fixed by applying the patch from Bill Sommerfeld. -hm)
+
+ PR #404: Meta-Control-Space broken with PCVT_META_ESC and PCVT_NULLCHARS
+ (put into "BugList" file, no solution yet .. -hm)
+
+ PR #488: pcvt can loose keyboard control if you start an xserver from
+ an xterm
+ (already fixed by a patch from John Kohl, see below -hm)
+
+ PR #580: NetBSD i386/pcvt bugs/enhancements; fixes included
+ (already integrated the patch from Lon Willet, see below -hm)
+
+- on coldinit, if FAT_CURSOR is defined, the old large blockcursor is used
+
+- enhanced the vgaio output in an attempt to make 132 column mode work
+ for my S3 board ....
+
+- applied patch from Thomas Gellekum: install instructions for FreeBSD 2.0,
+ patch to fontedit to compile under FreeBSD 2.0, an addition to
+ keycap.src and a fix for kbdio
+
+- changed installation instructions to reflect recent changes, removed
+ instructions for FreeBSD 1.0 and added instructions for FreeBSD 2.0
+
+- upgraded all utilities version strings to 3.20
+
+- new example of rc.local
+
+- changing timeout()/untimeout() function parameter type to TIMEOUT_FUNC_T
+ definition in pcvt_hdr.h
+
+- changed install instructions to reference Etc dir instead of Doc dir for
+ several files which were moved from Doc to Etc.
+
+- new parameter -d for the cursor utility, updated cursor manpage
+
+- applying a patch from Lon Willett, willett@math.utah.edu which fixes
+ several bugs and provides some enhancements. Lon writes:
+
+ 1 -- The displayed cursor is not updated during kernel I/O,
+ because async_update() doesn't get called. This is merely ugly
+ while the system startup messages are being displayed, but it is
+ positively annoying when using the kernel debugger.
+
+ 2 -- CONTROL-SHIFT-<KEY> is taken to be the same as SHIFT-<KEY>.
+ It should be interpretted as CONTROL-<KEY>, or even have its own
+ binding. One of the lines below fixes it in the former way.
+ This is especially bad (i.e. clearly buggy) when I type
+ CONTROL-SHIFT-<6/^> to get a "Control-^", and instead get a "^".
+
+ 3 -- The "special" region at the bottom of the screen, i.e. the
+ function-key-labels/status-line in HPVT mode, and the blank line
+ in FORCE24LINES mode, do not get properly cleared/updated.
+
+ 4 -- When switching screen sizes/modes, the FORCE24LINES setting
+ is not always handled.
+
+ 5 -- The PCVT_VT220KEYB functions are missing some features.
+ The control key settings are bug fixes; they apply only if
+ system function key labels are on, and match the labels. The
+ shift key definitions are enhancements.
+
+ The new keys (previously undefined) are:
+
+ Control-F1 -- toggle 80/132 columns
+ Control-F2 -- soft reset emulator
+ Control-F3 -- toggle force 24 lines mode
+ Control-F4 -- toggle keyboard debugging
+
+ Shift-F1 -- select screen 4
+ Shift-F2 -- select screen 5
+ Shift-F3 -- select screen 6
+ Shift-F4 -- select screen 7
+ Shift-F5 -- select (current screen - 1)
+
+ 6 -- [Enhancement] The initial startup screen is cleared, and
+ the screen gets cleared when changing modes/sizes. I don't like
+ this. I want a chance to see bootstrap loader messages, and I
+ also want the command "scon -d/dev/ttyv0 -H -s28" in my
+ rc.local, but then I lose various messages from daemons, etc
+ (which aren't always logged to syslog). So I have fixed the
+ code so that it tries to preserve the contents of the screen
+ when starting up, and when changing modes/sizes.
+
+ 7 -- [Enhancement?] I added code to preserve the initial cursor
+ shape at startup. This isn't really necessary, since the
+ "cursor" command can be used to set it to your preference; but
+ it seems like this is the preferable policy to use for the
+ default.
+
+- applying patches from Onno van der Linden and Martin Husemann: adding
+ support for post 1.0 NetBSD-current and adding PCVT_NO_LED_UPDATE
+
+- patch from Joerg Wunsch: support for FreeBSD 2.0 and better support
+ for serial console coexistence
+
+- bugreport from Michael Havemester regarding PCVT_NOFASTSCROLL: it was
+ not enabled in pcvt_vtf.c, fixed.
+
+- renamed pcvt_disable_intr and pcvt_enable_intr to PCVT_DISABLE_INTR and
+ PCVT_ENABLE_INTR (Joerg)
+
+- attempt to automatically compile time configure in pcvt_conf.h, how
+ does FreeBSD do this ? should work for NetBSD (from Onno v.d. Linden)
+
+- split off pcvt_kbd.h from pcvt_kbd.c, file > 100k
+
+- split off pcvt_config.h from pcvt_hdr.h. All compile time configurations
+ should be put into pcvt_config.h now.
+
+- moved version definitions to begin of file in pcvt_ioctl.h and pcvt_hdr.h
+
+- fixing NetBSD #ifdef in pcvt_drv.c
+
+- fixed bug in scon. when doing "scon -c <num>" the destination screen was
+ cleared, this is fixed now by properly setting "force_24lines"
+
+- added kbdio utility from Joerg into Util directory
+
+- changed font dir from /usr/share/misc/vgafonts to /usr/share/misc/pcvtfonts
+
+- Fix for Gateway 2000 keyboard problems from Brian Moore
+
+- Starting to implement more keymoard mapping layers in pcvt_kbd.c. This
+ section is a building site, enclosed in NOT_YET_DEF ifdef's
+
+- VT_SETMODE ioctl patch from John Kohl <jtk@kolvir.blrc.ma.us>, see text
+ from John in the source file pcvt_ext.c
+
+- patch from Joerg for kbd_emulate_pc(), intro of timeout
+
+- fixed bug in soft/hard reset and real system fkey labels
+
+- added patch from Joerg for FreeBSD 1.1.5.1R
+
+- fixed both SR and SF entries in termcap database file
+
+- removed SR entry in termcap database file (bugreport from John Perry)
+
+- bugfix in cirrus 132 column switching got from Onno/Charles
+
+- french keycap database entry from Matthieu Herrb
+
+- cleanup patch from Joerg for Util/demo/Makefile und Util/fonts/Makefile
+
+
+Changes 3.00 -> 3.10 June 1994
+--------------------------------------------------------------------------------
+
+- patch from Joerg correcting my assumed timeout/untimeout FreeBSD casts
+
+- adjusted some #if's while validating NetBSD 0.9 and NetBSD-current
+
+- rolled in Joerg's changes for a pre-1.1.5 (?) FreeBSD-current
+
+- Util/fonts: install only the necessary fontfiles, vt100 not used anymore
+
+- updated and commented Etc/rc.local and updated to more recent /etc/ttys files
+
+- updated all installation instructions and changed their names
+
+- documenting more PCVT_ options in pcvt_hdr.h
+
+- adding CONF_ options to driver config ioctl and upgrading ispcvt
+
+- commenting the source, housekeeping, step up to 3.10 beta 2
+
+- casting timeout()/untimeout() for FreeBSD 1.1R :-(
+
+- unprotecting vgapage() routine with #if !PCVT_KBD_FIFO
+
+- adding Michael Havemester's keyboard fifo diffs to source tree
+
+- pcvt_drv.c: made declaration of Crtat global for this file for NetBSD
+
+- changing Util/fonts/Makefile and Util/demo/Makefile to run in all
+ supported environments
+
+- moving inline from pcvt_vtf.h into pcvt_hdr.h, i still feel that this is
+ not the best solution, but i got annoyed by the 10 line pcvt_vtf.h
+
+- bugfix, in hpmode, clear last 4 lines when 28 column mode and force
+ 24 lines are true
+
+- applying patch from Thomas Gellekum <thomas@ghpc8.ihf.rwth-aachen.de>:
+ - discard escape sequences ESC space F and ESC space G
+ - discard escape sequences DECELR = enable locator report
+ and DECSLE = select type of locator event
+ - disable scrolling when writing outside the scrolling region
+ at an absolute position.
+
+ [ pcvt now seems to be "VAX resistant" :-) ]
+
+- optional switch to screen 0 can be done on kernel/console output
+
+- fixed kernel output cursor positioning
+
+- removed PCVT_FORCE8BIT
+
+- pcvt_ext.c, changed screen switching bcopyb's to bcopy's
+
+- new patch from Onno to support all BIOS versions of the Cirrus chipset.
+
+- pcvt_x_hook() has to care about fkey labels now
+
+- updated Doc/NotesAndHints and Doc/BugList
+
+- applied patch from Onno van der Linden for updated Cirrus chipset support
+
+- updated Doc/Bibliography
+
+- removed paranoid delay()/DELAY() from vga_test()
+
+- added prototype ttrstrt() into pcvt_drv.c for NetBSD 0.9
+
+- made INSTALLATION.NetBSD and INSTALLATION.NetBSD.bundled from mycrofts base
+
+- Charles Hannum took Michael Havemesters speedup modifications, made some
+ further enhancements and after adding support for pcvt, put it into
+ the NetBSD-current tree. THANK YOU, Charles !
+ The NetBSD version of May 20th '94 was taken as the base for 3.10.
+
+
+Changes 2.20 -> 3.00 March 1994
+--------------------------------------------------------------------------------
+
+- Release 3.00
+
+- last minute patch from Joerg (pcvt_hdr.h, BugList, NotesAndHints)
+
+- included speedup patch from Michael Havemester as Etc/LAST-MINUTE
+
+- updated Doc/pcvt.4
+
+- removed bug in Util/ispcvt/Makefile which caused ispcvt to be installed
+ into /usr/sbin and /usr/local/bin
+
+- split ioctl VGAPCVTID into two: intro of ioctl VGAPCVTINFO for compile
+ time options only.
+ VGAPCVTID is now frozen for identification purposes (XFree86 3.0)
+ updated Util/ispcvt for the above mentioned changes
+
+- issued patch to upgrade beta14 to beta16
+
+- bugfix: when scrolling up (bcopy) is interrupted by a keystroke requesting
+ a change of the current screen, the "new" screen is scrolled up. Fixed
+ in pcvt_kbd.c and pcvt_out.c (check_scroll)
+
+- fixed bug "Jumping through vt's with ALT-F12 does jump over vt0"
+
+- PCVT_PCBURST intro, update of ioctl and Util/ispcvt
+
+- NetBSD speedup patch from Michael Havemester (factor 6-10)
+
+- issued patch to upgrade beta14 to beta15 (never officially announced)
+
+- added patch for ttioctl parameters NetBSD-current 12 Feb 94 from Michael
+ Havemester in pcvt_drv.c
+
+- INSTALLATION.xxx(x)BSD upgraded to reflect changes in Util and NetBSD-current
+
+- struct pcvtid changed to hold the value of PCVT_xxx(x)BSD, ispcvt upgraded
+
+- applied patch from Szabolcs Szigeti for 132 column operation for Trident
+ TVGA 8900B and TVGA8900C based boards
+
+- PCVT_NETBSD can/must now be 1 or 09 for Release 0.9 and > 09 for current
+
+- applied patches from John Brezak and Szabolcs Szigeti for recent
+ NetBSD-current changes (syscframe -> trapframe)
+
+- in Util, removed Makefile.inc. Make Makefile.inc.FreeBSD and
+ Makefile.inc.NetBSD and added a check to the toplevel and every
+ other Makefile in this part of the tree. sigh ...
+
+- patch from Joerg for pcvt.4 Makefile and Debugger in FreeBSD
+
+- issued pcvt-beta14, code freeze for 3.00 release, just bugfixes now
+
+- INSTALLATION.FreeBSD and a small cleanup patch from Joerg
+
+- large patch from Joerg to get pcvt FreeBSD-current compliant
+
+- permission/owner cleanup, files:664, dirs:775, user:root, group:wheel
+
+- got rid of the verbose error message when installing in Util/fonts
+
+- new make-method in Util/kcon to workaround make portability problems
+
+- NetBSD-current detection workaroundaroundaround for NEW_AVERRUNNABLE in
+ pcvt_header.h
+
+- MONO_BUF and COLOR_BUF now ifndef'ed
+
+- fixed typo in pcvt_kbd.h in cfkey11() and cfkey12()
+
+- machine/pio.h must be included in pcvt_hdr.h for recent NetBSD-current
+
+- protected every tsleep call with an "if(curproc)" otherwise there is
+ chance to panic the system (Joerg has an idea why ...)
+
+- more fixes from Joerg: keyboard scansets fixed, X server is now aware
+ of redefined keys (ioctl implemented)
+
+- patch from Heiko Rupp, configuration with XSERVER not defined didn't compile
+
+- new version of vgaio from Joerg installed
+
+- large keyboard cleanup patch from Joerg merged in
+
+- bugfixes from Joerg: fix crash on not-open vt, remove pcxint, add option
+ PCVT_INHIBIT_NUMLOCK (for notebook owners :-), support for EGA/VGA
+ fonts with up to 32 scanlines.
+
+- included vgaio, a program to read/write vga register values from Joerg.
+
+- included mcon, the keyboard mouse emulator control program from Joerg.
+
+- applied averrunnable patch to satisfy the most recent NetBSD-current.
+
+- fixing cursor not updated bug if usl/vt server is running on vt0
+
+- polished some chars in Util/fonts/vt220l.810, fixed all permissions in
+ the uuencoded fontfiles.
+
+- debugged the EGA/VGA curses based font editor 'fed' in Util/fed. It seems
+ it's working ok now now.
+
+- updated copyright header files
+
+- synchronize asynchronous cursor position update with having a valid
+ (new) cursor (row) position from sputc(). (Otherwise a cursor
+ would appear temporarily in the first position of the first
+ function key label in the HP mode)
+
+- updated screeninfo ioctl and scon to report the monitor type
+
+- added file Doc/Notes for random notes and hints for pcvt-users.
+
+- enhanced the pcvtid-ioctl and the ispcvt(8) utility to print out the
+ values of all "PCVT_XXXXXX" compile time options.
+
+- included work from Joerg to convert all ifdef's to if's, to be able
+ to compile various configurations of pcvt without changing
+ options in the header file.
+
+- included (currently untested !) patch for a keyboard mouse emulator
+ from Joerg. (he got problems after he bought a notebook with
+ just one serial port, which he wanted to use for slip ...)
+
+- screensaver reset is now also done asynchronously to get more speed. the
+ function average() was renamed to async_update().
+
+- cursor position update and cursor position display in HP mode is now done
+ asynchronously in function average() in pcvt_sup.c. the function
+ update_cursor does no longer exist. this gave about 10..30% increase
+ in speed depending on the data cat'ed (termcap, kernel, 1Mb nulls)
+
+- HP function key emulation processing debugged, this has to be rewritten
+ to use a stack and a new parser. elm -K now works a bit more, the
+ display is not garbled anymore, but fkey strings do not work.
+
+- pcvt_vtf.c split off from pcvt_out.c, file got > 100k. Checked all
+ forward declarations in header and source files
+
+- 132 column support for Cirrus Logic CL-GD542X chipsets written by
+ Onno van der Linden, c/o vdlinden@fwi.uva.nl
+
+- keyboard scancode display (#define PCVT_SHOWKEYS)
+
+- printscreen keycode fix form Onno van der Linden
+
+- Util/Makefile.inc added .depend dependency for make depend
+
+- pcvt_ioctl.h is now installed into /usr/include/machine.
+
+- keyboard scancode 1 is now used by default, perhaps it cures some problems
+
+- support for keyboard scancodes sets 1 and 2 (compile time selectable via
+ PCVT_SCANSET), patch from Onno van der Linden, c/o vdlinden@fwi.uva.nl
+
+- Util/fontedit.c updated to "#if defined (__386BSD__) || defined (__NetBSD__)"
+ (suggested by Mark Weaver, Mark_Weaver@brown.edu)
+
+- 132 column support for S3 86c928 chipsets
+
+- split off pcvt_ext.c from pcvt_sup.c, it was more than 100k ....
+
+- intro of Doc/Manifest and Doc/TestedHardware, removed README.X-PATCH because
+ it was now really outdated
+
+- superprobe compatibility patches from Joerg
+
+- intro of file Doc/BugList
+
+- another powerpatch from Joerg:
+ - some vgaioctl's are now available if in X mode (i.e. to scon to
+ another screen from within an xterm)
+ - removed bug in keyboardhandling, numlock'ed numkeys did send an
+ additional null (0x00) char, this has been fixed now.
+ - SysRq key made functional
+
+- made force 24 lines (see below) the default configuration to have a well
+ behaving vt220 emulator at startup.
+
+- every vt now has a separate caps-lock, num-lock and scroll-lock flag and
+ a separate handling of these lock-keys.
+
+- made sleeping in case of scroll lock working from an earlier patch from Joerg.
+
+- large patch from Joerg:
+ - Doc/pcvt.4 updated to reflect recent changes
+ - struct winsize set ok when switching between HP/VT
+ - struct winsize pixels reflect real values now
+ - ioctl for switching between 80 and 132 cols
+ - 132 columns for generic VGA's
+ - updated scon to provide access to ioctl 80/132 col switching
+
+- removed pcconcoftc and kbdsoftc structures from header files, removed
+ pcconsintr variable and introduced kbd polling synchronization
+ variable kbd_polling.
+
+- merging Joerg's patch to support 132 columns on Tseng Labs ET3000
+
+- moved Util/uemacs/* --> Etc/uemacs.tar.Z.uu
+
+- added patches from Joerg for new Makefiles in Util
+
+- added keyboard security define's to the new X server code
+
+- adding bugfixes from Joerg Wunsch for "old" (= non vt switching) X server
+
+- Control-Alt-Functionkey(1...12) switches now virtual screens/terminals to
+ behave consistently with xfree 2.0, also the pages are now checked
+ against the real no. of terminals available ALL the time ...
+
+- pcvt_hdr.h: changed "int pcstart();" to "void pcstart();" to avoid warning
+ message when compiling under NetBSD-current
+
+- Doc dir split into Doc and Etc, Support renamed to Util
+
+- adding NetBSD-current support for new X server support (syscframe changed
+ to trapframe in NetBSD-current as of 11/11/93)
+
+- adding Joergs changes for XFree86 2.0 multiple X server and/or terminal
+ session support
+
+- adding entries from patchkit 0.2.4 codrv keymap to Support/keycap/keycap.src
+
+- Keyboard security introduced into the XSERVER dependent part in pcvt_drv.c
+
+- renamed device files from /dev/ttycXX to /dev/ttyvXX
+
+- added file Doc/pcvt.el from Joerg Wunsch to distribution
+
+- added HP-mode function key map from Gordon L. Burditt to description
+ in Doc/Keyboard.HP
+
+- fixed bug in scon which prevents it from showing the correct status of
+ 132 column support of chipset
+
+- 132 column mode for Trident TVGA9000 works now, after 2 1/5 months of
+ calling everybody i eventually got a tech ref manual from Trident ....
+
+- it is now possible to "force" pcvt into a 24 line mode when operating
+ in pure VT mode with 25 lines or in HP mode with 28 lines. This
+ is sometimes necessary when running software which assumes it runs
+ on a "real" VT220 which has just 24 lines.
+
+- updated scon to support the 24 lines force mode (scon -f [ on | off ])
+
+- soft reset fkey now positions cursor into left upper corner, update_cursor()
+ made global function
+
+- updated scon (-l) to print out additional info about the vga chipset,
+ family and 132 column support if VGA detected.
+
+- added additional fields to screeninfo structure in pcvt_ioctl.h to be
+ able to return information about current vga chipset.
+
+- moved VGA type/family definitions from pcvt_hdr.h to pcvt_ioctl.h
+
+- Terminfo and Termcap updated to support 132 columns
+
+- fixed bug in kcon which outputs garbage for remapped keys in kcon -l.
+ (reported by Gordon L. Burditt, gordon@sneaky.lonestar.org)
+
+- modified Makefile for Support/ispcvt to copy it for installation instead
+ of moving it ..
+
+- renamed /usr/share/misc/keycap -> /usr/share/misc/keycap.pcvt to
+ avoid nameclash with same file for codrv (Gordon Burditt)
+
+- security bit no longer ignored when initializing keyboard (see #define
+ PCVT_USEKBDSEC in pcvt_hdr.h, suggested by Terry Lambert)
+
+- cpufunc.h include made only for NetBSD (Gordon Burditt)
+
+- switch statement in pcvt_drv.c removed for 386BSD (Gordon Burditt)
+
+- Trident cursor size bug removed
+
+- keyboard initialization for ddb
+
+- added support for cursor on/of switching, screensaver and DECTCEM
+
+- removed explicit Hercules support, MDA = Hercules in this context now
+
+- patchkit from Joerg Wunsch (kbd-overlay malloc, scroll_sleep, clip fix)
+
+- ispcvt now installed into /usr/sbin
+
+- removed PCVT_NETBSD08 and PCVT_NETBSDCU, intro of PCVT_NETBSD
+
+- implemented 132 column operation for wd90c11 chipsets
+
+- PCVT_PREPATCH022 renamed to PCVT_NEEDPG
+
+- INSTALLATION.NetBSD written
+
+- DEVICE in kcon makefile changed from /dev/console to /dev/ttyc0
+
+- new keyboard code in pcvt_kbd.c from NetBSD current
+
+- new /etc/rc.local script in INSTALLATION
+
+- implemented 132 column operation for et4000 chipsets
+
+- detection of super vga chipsets as a prerequisite for 132 col mode
+
+- switched to memory mapped virtual screen operation, configurable no. of
+ virtual screens, virtual screens now also on MDA and Hercules boards
+
+
+Changes 2.10 -> 2.20 June 1993
+--------------------------------------------------------------------------------
+
+- added new option -a to scon to get the video adaptor in scripts
+
+- support for NetBSD-current, define PCVT_NETBSDCU to enable it
+
+- Support for NetBSD 0.8, define PCVT_NETBSD08 in pcvt_hdr.h to enable it.
+
+- Change Support/Makefile to use <bsd.subdir.mk> instead of <bsd.prog.mk>
+
+- Font editor for the EGA/VGA font-files added to support the design of new
+ fonts. One will need Zeyd M. Ben-Halim's ncurses library to compile it, see
+ file README.FIRST for information where to get it
+
+- Doc/INSTALLATION upgraded
+
+- Terminfo entry added to support Zeyd M. Ben-Halim's ncurses port
+
+- ispcvt is now installed in /sbin to have it at boottime if /usr is not
+ yet mounted.
+
+- applied a patch which prevents CAPS LOCK, SHIFT LOCK, and SCROLL LOCK
+ from being repeated (causing i.e a flashing CAPS LOCK led while
+ holding CAPS LOCK key down).
+ (diff from Gordon L. Burditt, gordon@sneaky.lonestar.org)
+
+- merging patches to support the pccons-model of X11 server support
+ (diff from Joerg Wunsch, joerg_wunsch@uriah.sax.de)
+
+- file pcvt_drv.c routine pg() enclosed in "#ifdef PCVT_PREPATCH022" to solve
+ multiple defined symbols beginning with patchkit 0.2.2
+ (reported by Marko Karppinen, dreamer@purkki.apu.fi)
+
+- added british keycap entry into keycap source file
+ (from Andy Duplain, duplain@rtf.bt.co.uk)
+
+
+Changes 2.00 -> 2.10 March 1993
+--------------------------------------------------------------------------------
+
+- detecting the presence of video boards has been changed to ask the
+ BIOS "equipment byte" in the RTC-CMOS ram what's installed. this caused
+ many discussions but solved also many problems ....
+
+- driver name changed from "pc" to "vt" for multiple driver coexistence
+ (diff from Joerg Wunsch, joerg_wunsch@uriah.sax.de)
+
+- new devicenames recommended for showup in utils like "ps"
+ (many people suggested that ..)
+
+- new location for manual pcvt.0
+ (diff from Joerg Wunsch, joerg_wunsch@uriah.sax.de)
+
+- new demo file "sgr.vt" to show available graphic renditions
+
+- intro of Doc/ChangeLog (this file)
+
+- fixed bug in Support/keycap/Makefile
+ (diff from Gordon L. Burditt, gordon@sneaky.lonestar.org)
+
+- make vttest (main.c) compile after applying patchkit-beta2
+
+- screensaver fixed by Joerg Wunsch
+
+- screensaver now compiled in by default
+
+- made recognition of CONTROL-ALT-DELETE an optional #ifdef'ed feature
+
+- added sgr-conversion table for MDA adaptors
+
+- fixed sgr-conversion table for VGA monochrome environments
+
+- added support for using the kernel debugger
+ (diff from Bruce Evans, bde@runx.oz.au)
+
+- fixed several bugs regarding monochrome environments in pcvt_sup.c
+
+- fixed bug in scon preventing one from piping output though more
+ (reported by Gordon L. Burditt, gordon@sneaky.lonestar.org)
+
+- display current screen number in HP-mode in the bottom right of screen
+
+- changed names of all #define-able compile time options to start with
+ "PCVT_" for easy identification and installation into the kernel config file
+
+- applied another pcvt_kbd.c patchkit from Bruce Evans, bde@runx.oz.au. he
+ writes:
+
+ These fixes are mainly related to ddb. sgetc has a weird interface that
+ has caused some bugs, and it was too easy for ddb to reenter itself.
+
+ 1. Don't use char for keypad2num, char might be unsigned. Space is not
+ important since the array is small.
+
+ 2. Don't use u_short for n.
+
+ 3. Change some 0's to NULLs.
+
+ 4. sgetc must not return NULL for the !noblock case. Only callers with
+ noblock set check for the null pointer. When the kernel follows a
+ null pointer, I think page 0 is sometimes mapped in so nothing bad
+ happens. The kernel panics if the page is not mapped in.
+
+ 5. Reentrancy fix. The debugger really ought to check for reentrancy
+ itself, but the driver still needs to return early after the
+ debugger returns, so that it doesn't return a junk ESC from
+ ctrl-alt-ESC.
+
+ 6. xlatkey2ascii may return NULL too.
+
+--------------------------------------------------------------------------------
diff --git a/usr.sbin/pcvt/Misc/Doc/CharGen b/usr.sbin/pcvt/Misc/Doc/CharGen
new file mode 100644
index 0000000..c047bfa
--- /dev/null
+++ b/usr.sbin/pcvt/Misc/Doc/CharGen
@@ -0,0 +1,149 @@
+Character Generator description (before it gets lost ..)
+--------------------------------------------------------------------------------
+
+The lower character generator is the default IBM character set II.
+
+The description of the higher character generator follows below. The
+character names are taken from the "Postscript Language Reference
+Manual", 2nd Edition, Fourth printing July 1991, pp 596.
+
+NOTE: The hex values here are NOT the values the character is identified
+ by in the emulator. The "physical" (vt220 character) to "logical"
+ (this table, character generator) conversion is done in the character
+ output routine by using the tables from file pcvt_tbl.h.
+
+The order of the characters is not regular and was largely influenced by
+the status of my brain while pixel-placing characters ....
+
+HEX What
+--- -------------------------------------------------
+00 Control code display for
+. control characters in the
+1f range 0x00 - 0x1f
+
+20 Control code display for
+. control characters in the
+3f range 0x80 - 0x9f
+
+HEX What
+--- -------------------------------------------------
+40 rho
+41 psi
+42 partialdiff
+43 lambda
+44 iota
+45 eta
+46 epsilon
+47 chi
+48 logicaland
+49 logicalor
+4a union
+4b propersuperset
+4c propersubset
+4d gamma
+4e Xi
+4f Psi
+
+HEX What
+--- -------------------------------------------------
+50 Pi
+51 arrowdblright
+52 arrowdblboth
+53 Lambda
+54 Theta
+55 congruent
+56 gradient
+57 Delta
+58 proportional
+59 therefore
+5a integral
+5b fraction
+5c (inverted fraction ????)
+5d angle
+5e (inverted angle ????)
+5f braceleftmid
+
+HEX What
+--- -------------------------------------------------
+60 bracerightmid
+61 bracelefttp
+62 braceleftbt
+63 bracerighttp
+64 bracerightbt
+65 radical
+66 omega
+67 (Yen ??)
+68 xi
+69 yacute
+6a thorn
+6b eth
+6c Thorn
+6d Yacute
+6e multiply
+6f Eth
+
+HEX What
+--- -------------------------------------------------
+70 threequarters
+71 Cedillasmall
+72 Acutesmall
+73 emdash
+74 registered
+75 endash
+76 logicalnot
+77 dieresis
+78 notequal
+79 scan 9
+7a scan 7
+7b scan 5
+7c scan 3
+7d scan 1
+7e upsilon
+7f emptyset
+
+HEX What
+--- -------------------------------------------------
+80 oe
+81 Otilde
+82 atilde
+83 Ydieresis
+84 Ucircumflex
+85 Uacute
+86 Ugrave
+87 Oslash
+88 OE
+89 Otilde
+8a Ocircumflex
+8b Oacute
+8c Ograve
+8d Idieresis
+8e Icircumflex
+8f Iacute
+
+HEX What
+--- -------------------------------------------------
+90 Igrave
+91 Edieresis
+92 Ecircumflex
+93 Egrave
+94 Atilde
+95 Acircumflex
+96 Aacute
+97 Agrave
+98 onesuperior
+99 (small black rectangle)
+9a zeta
+9b threesuperior
+9c copyright
+9d currency
+9e kappa
+9f (inverted question mark)
+
+HEX What
+--- -------------------------------------------------
+a0 the remaining positions
+. are used for the vt220
+ff downloadable characterset
+
+-------------------------------------------------------------------------------
+ (phoooo ..)
diff --git a/usr.sbin/pcvt/Misc/Doc/Charsets b/usr.sbin/pcvt/Misc/Doc/Charsets
new file mode 100644
index 0000000..b30dd8a
--- /dev/null
+++ b/usr.sbin/pcvt/Misc/Doc/Charsets
@@ -0,0 +1,99 @@
+
+I. Character Set Selection on VT220 Terminals
+==================================================
+
+
+name C0 GL C1 GR
+ +---+ +-------------------+ +---+ +-------------------+
+ |00h| | | |80h| | |
+range | | | | 20h .. 7fh | | | | | a0h .. ffh |
+ |1fh| | | |9fh| | |
+ +---+ +-------------------+ +---+ +-------------------+
+length 32 96 32 96
+
+ /\
+SECOND /||\ "lock"- or "single"-shift one set of G0, G1, G2 or
+STEP || G3 into one of the "displayable" charactersets GL
+ || or GR. (escape) sequences are: SI, SO, ESC ~, ESC n,
+ || ESC }, ESC o, ESC |, ESC N and ESC O.
+
+ +----+ +----+ +----+ +----+
+name | G0 | | G1 | | G2 | | G3 |
+length |(96)| |(96)| |(96)| |(96)|
+ +----+ +----+ +----+ +----+
+
+ /\
+ /||\ designate a hard or a soft character set as
+ || one of G0, G1, G2 or G3, used escape sequences
+FIRST || are, ESC ( X, ESC ) X, ESC * X and ESC + X - where X is
+STEP || B for ascii, < for supplemental, 0 for special, A for
+ || british, 4 for dutch, C and 5 for finnish etc. etc. ...
+ ||
+
+ +-----+ +------------+ +--------+ +-----------+ +------------+
+name |ascii| |supplemental| |special | | national | |downloadable|
+ | | | graphics | |graphics| |replacement| |characterset|
+length | (96)| | (96)| | (96)| | (96)| | (96)|
+ +-----+ +------------+ +--------+ +-----------+ +------------+
+
+ \-------\ /----------/ norway/danish
+ | dutch
+ together, this is also finnish
+ referred to as the french
+ multinational character french canada
+ set (power on default german
+ on a dec vt220) italian
+ spanish
+ swedish
+ swiss
+
+
+II. Emulating Character Set Selection
+=========================================
+
+MDA/HCG/CGA:
+
+ just a partial emulation is done, because these boards don't allow
+ downloadable charactersets. some characters simply don't display
+ because they are not in the characterset roms on the video board.
+
+ if you want to change the mapping, have a look at the default tables
+ in pcvt_tbl.h.
+
+EGA/VGA:
+
+ these cards have provisions for downloadable charactersets and so
+ many vt220/vt320 charactersets are fully supported:
+
+ - US Ascii
+ - DEC Supplemental
+ - DEC Special Graphic
+ - DEC Technical
+ - ISO Latin-1
+ - Downloadable
+
+ when the emulator is started, it behaves initially similar to
+ the MDA/CGA/HCG method described above.
+
+ when a second (special) characterset for a given screen resolution
+ is loaded via the "loadfont" utility, the emulator detects this fact
+ and uses from then on extended tables to access the second charset
+ as an upper half 512 characterset in terms of ega/vga speak.
+
+ from this time on, vt220 downloadable fonts are usable.
+
+ the organization of this extended characterset is as follows:
+
+ 0x00 ... 0x1f C0 display function fonts
+ 0x20 ... 0x3f C1 display function fonts
+ 0x40 ... 0x9f fonts for DEC Supplemental, DEC Special
+ Graphic, DEC Technical and ISO Latin-1
+ 0xa0 ... 0xff VT220 Downloadable Font
+
+ the mapping between vt220 charactersets and the charactersets inside
+ the ega/vga is done by tables found in the file pcvt_tbl.h.
+
+ there is a MSDOS fonteditor called "EVAFONT" available on the
+ SIMTEL-20 archive, which i used to edit the character sets in the
+ distribution.
+
diff --git a/usr.sbin/pcvt/Misc/Doc/EscapeSequences b/usr.sbin/pcvt/Misc/Doc/EscapeSequences
new file mode 100644
index 0000000..8e0e2c7
--- /dev/null
+++ b/usr.sbin/pcvt/Misc/Doc/EscapeSequences
@@ -0,0 +1,268 @@
+ Control Codes and Escape Sequences supported by pcvt
+===============================================================================
+
+CONTROL CODES
+-------------------------------------------------------------------------------
+
+ NUL (0x00) ignored
+ SOH (0x01) ignored
+ STX (0x02) ignored
+ ETX (0x03) ignored
+ EOT (0x04) ignored
+ ENQ (0x05) ignored
+ ACK (0x06) ignored
+
+ BEL (0x07) beep
+
+ BS (0x08) move one character position to the left
+ until at left margin
+
+ HT (0x09) move to next tab stop
+
+ LF (0x0a) move to next line, same column
+ VT (0x0b) move to next line, same column
+ FF (0x0c) move to next line, same column
+
+ CR (0x0d) move to left margin on current column
+
+ SO (0x0e) invoke character set G1 into GL
+ SI (0x0f) invoke character set G0 into GL
+
+ DLE (0x10) ignored
+ DC1 (0x11) ignored
+ DC2 (0x12) ignored
+ DC3 (0x13) ignored
+ DC4 (0x14) ignored
+ NAK (0x15) ignored
+ SYN (0x16) ignored
+ ETB (0x17) ignored
+
+ CAN (0x18) abort current escape sequence
+
+ EM (0x19) ignored
+
+ SUB (0x1a) abort current escape sequence
+
+ ESC (0x1b) start of escape sequence
+
+ FS (0x1c) ignored
+ GS (0x1d) ignored
+ RS (0x1e) ignored
+ US (0x1f) ignored
+
+ VT220 control codes in the range 0x80 .. 0x9f are completely ignored,
+ but displayed as C1 display controls.
+
+
+ESCAPE SEQUENCES DIGITAL EQUIPMENT
+-------------------------------------------------------------------------------
+
+(ni) = not implemented yet, all hooks available inside emulator!
+<p> = numeric parameter
+
+ ESC space F select 7-bit c1 control transmission (ni)
+ ESC space G select 8-bit c1 control transmission (ni)
+
+ ESC # 3 double height top half (ni)
+ ESC # 4 double height bottom half (ni)
+ ESC # 5 single width single height (ni)
+ ESC # 6 double width single height (ni)
+
+ ESC # 8 fill screen with 'E's
+
+ ESC 7 save cursor
+
+ ESC 8 restore cursor
+
+ ESC = keypad application mode
+
+ ESC > keypad numeric mode
+
+ ESC D index
+
+ ESC E next line
+
+ ESC H set tab at cur col
+
+ ESC M reverse index
+
+ ESC N single shift G2
+
+ ESC O single shift G3
+
+ ESC Z who are you
+
+ ESC d Only available if PCVT_SETCOLOR was defined when
+ compiling the kernel, allows to set custom color table
+ for more info, see pcvt_out.c ...
+
+ ESC c power up reset
+
+ ESC n Lock Shift G2 -> GL
+
+ ESC o Lock Shift G3 -> GL
+
+ ESC } Lock Shift G2 -> GR
+
+ ESC | Lock Shift G3 -> GR
+
+ ESC ~ Lock Shift G1 -> GR
+
+ ESC [ ? <p> h set dec private modes
+ ESC [ ? <p> l reset dec private modes
+ 1 CKM - cursor key mode
+ 6 OM - origin mode
+ 7 AWM - auto wrap mode
+
+ ESC [ <p> ' z DECELR - Enable Locator Report (ni)
+
+ ESC [ <p> ' { DECSLE - Select type of locator events (ni)
+
+ ESC [ ? n Terminal Reports
+
+ ESC [ ? K selective erase in line
+
+ ESC [ ? J selective erase in display
+
+ ESC [ <p> @ insert char(s)
+
+ ESC [ <p> A cursor up
+
+ ESC [ <p> B cursor down
+
+ ESC [ <p> C cursor forward
+
+ ESC [ <p> D cursor backward
+
+ ESC [ <p> H cursor direct cursor addressing
+
+ ESC [ <p> J erase screen
+
+ ESC [ <p> K erase line
+
+ ESC [ <p> L insert line
+
+ ESC [ <p> M delete line
+
+ ESC [ <p> P delete char
+
+ ESC [ <p> S scroll up
+
+ ESC [ <p> T scroll down
+
+ ESC [ <p> X erase character
+
+ ESC [ <p> c device attributes
+
+ ESC [ <p> f direct cursor addressing
+
+ ESC [ <p> g clear tabs
+
+ ESC [ <p> h set mode
+ ESC [ <p> l reset mode
+ 4 IRM - insert replacement mode
+ 20 LNM - line feed / newline mode
+
+ ESC [ <p> i media copy (ni)
+
+ ESC [ <p> m select graphic rendition
+ 0 reset to normal attributes
+ 1 bold
+ 4 underline
+ 5 blinking
+ 7 reverse
+ 22 bold off
+ 24 underline off
+ 25 blinking off
+ 27 reverse off
+ 30-37 foreground colors (on color display)
+ 40-47 background colors (on color display)
+
+ ESC [ <p> n device status report
+
+ ESC [ <p> r set scrolling region
+
+ ESC [ <p> x request / report terminal parameters
+
+ ESC [ <p> y invoke selftests (ni)
+
+
+ ESC [ x request/report parameters
+ ESC [ y invoke seftest(s)
+
+ ESC [ " q SCA
+ ESC [ ! p SCA
+
+ ESC ( <p> designate G0
+ ESC ) <p> designate G1
+ ESC * <p> designate G2
+ ESC + <p> designate G3
+ ESC - <p> designate G1 (96)
+ ESC . <p> designate G2 (96)
+ ESC / <p> designate G3 (96)
+ A British or ISO-Latin-1
+ B USASCII
+ C Finnish
+ 5 Finnish
+ E Norwegian/Danish
+ 6 Norwegian/Danish
+ H Swedish
+ 7 Swedish
+ K German
+ Q French Canadien
+ R French
+ Y Italian
+ Z Spanish
+ 0 special graphics
+ 1 alternate ROM
+ 2 alt ROM, spec graphics
+ 3 HP Roman 8, upper 128 chars*/
+ 4 Dutch
+ < DEC Supplemental
+ = Swiss
+ > DEC Technical
+
+
+ESCAPE SEQUENCES HEWLETT-PACKARD
+-------------------------------------------------------------------------------
+
+ESC&f<attr>a<key>k<llen>d<slen>L<label><string> set function key label
+
+ attr = attribute, 0 - normal (not implemented)
+ 1 - local only (not implemented)
+ 2 - transmit only (not implemented)
+
+ key = function key number, range 1 .. 8
+
+ llen = label string length
+
+ slen = string string length
+
+ label = label data (up to 16 chars per label)
+
+ string = string to send data (up to 80 chars per label)
+ (not implemented)
+
+EXAMPLE:
+========
+
+ ESC&f0a1k16d1LFUNCTION KEY 0
+
+ sets function key label 1 to "FUNCTION KEY ".
+ should program fkey 1 to emit "0" on keypress
+
+
+ESC&j<parm> function key operations
+
+ parm = '@' remove the function key labels from screen
+
+ parm = 'A' display the modes set of function key labels
+
+ parm = 'B' enable & display user function key labels
+
+ parm = 'C' clear message & restore the current key labels
+
+ parm = 'R' enable usr/sys and menu and label modes
+
+ parm = 'S' disable usr/sys and menu and label modes
+
diff --git a/usr.sbin/pcvt/Misc/Doc/Keyboard.HP b/usr.sbin/pcvt/Misc/Doc/Keyboard.HP
new file mode 100644
index 0000000..06ad5b7
--- /dev/null
+++ b/usr.sbin/pcvt/Misc/Doc/Keyboard.HP
@@ -0,0 +1,286 @@
+================================================================================
+# #
+# Function key mapping for the "more HP" - like layout #
+# #
+================================================================================
+
+
+I. Function Key Map
+========================
+
+
+PC | F1 | F2 | F3 | F4 | F5 | F6 | F7 | F8 | F9 | F10 | F11 | F12 |
+Key
+ _________________________________________________________________________
+UNSHF| 132 | Soft| | | | 7/8 | Dspl| Auto| SCRN| SCRN| SCRN| SCRN|
+SYS | Cols|Reset| | | Beep| Bit | Func| Wrap| 0 | 1 | 2 | 3 |
+LABEL|_____|_____|_____|_____|_____|_____|_____|_____|_____|_____|_____|_____|
+UNSHF| | | | | | | | | SCRN| SCRN| SCRN| SCRN|
+USER | F6 | F7 | F8 | F9 | F10 | F11 | F12 | F13 | 0 | 1 | 2 | 3 |
+LABEL|_____|_____|_____|_____|_____|_____|_____|_____|_____|_____|_____|_____|
+ | | | | | | | | | Fkey| U/S |VT/HP| Next|
+ALT | F14 | HELP| DO | F17 | F18 | F19 | F20 | F21 |Label|Label| Mode| SCRN|
+ |_____|_____|_____|_____|_____|_____|_____|_____|_____|_____|_____|_____|
+ | User| User| User| User| User| User| User| User| | | | |
+SHIFT| F6 | F7 | F8 | F9 | F10 | F11 | F12 | F13 | | | | |
+ |_____|_____|_____|_____|_____|_____|_____|_____|_____|_____|_____|_____|
+ALT- | User| User| User| User| User| User| User| User| | | | |
+SHIFT| F14 | F15 | F16 | F17 | F18 | F19 | F20 | F21 | | | | |
+ |_____|_____|_____|_____|_____|_____|_____|_____|_____|_____|_____|_____|
+CTRL-| SCRN| SCRN| SCRN| SCRN| SCRN| SCRN| SCRN| SCRN| SCRN| SCRN| SCRN| SCRN|
+ALT | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
+ |_____|_____|_____|_____|_____|_____|_____|_____|_____|_____|_____|_____|
+
+PC | F1 | F2 | F3 | F4 | F5 | F6 | F7 | F8 | F9 | F10 | F11 | F12 |
+Key
+
+
+
+II. Special Keys Used To Change The General Runtime Functionality
+=====================================================================
+
+
+ To be consistent with XFfree86 2.0 Virtual Screen switching, it is
+ now also possible to switch screens by using:
+
+ CTRL - ALT - Fx , where x can be 1 ... No of screens compiled,
+ see the definition of PCVT_NSCREENS !
+
+
+ Virtual Screen/Terminal switching
+ ---------------------------------
+
+ F9 or CTRL-ALT-F1 switches to screen 0
+ F10 or CTRL-ALT-F2 switches to screen 1
+ F11 or CTRL-ALT-F3 switches to screen 2
+ F12 or CTRL-ALT-F4 switches to screen 3
+ CTRL-ALT-F5 switches to screen 4
+ CTRL-ALT-F6 switches to screen 5
+ CTRL-ALT-F7 switches to screen 6
+ CTRL-ALT-F8 switches to screen 7
+ CTRL-ALT-F9 switches to screen 8
+ CTRL-ALT-F10 switches to screen 9
+ CTRL-ALT-F11 switches to screen 10
+ CTRL-ALT-F12 switches to screen 11
+
+ (see also ALT-F12 below)
+
+
+ ALT-F9 Function key labels ON / OFF
+ ------------------------------------
+
+ this key is only operational, when in HP/VT mode, see F11
+
+ ALT-F9 toggles between function key labels displayed or not.
+
+ ON: screen has either 25-3 = 22, 28-3 = 25 or 50-3 = 47 lines
+ 2 lines fkey labels displayed
+ row/col display enabled
+ 1 line status/load avg line
+
+ OFF: screen has either 25-3 = 22, 28-3 = 25 or 50-3 = 47 lines
+ no function key labels displayed
+ no row/col display
+ no status/load avg line
+
+ applicable escape sequences:
+
+ switch OFF:
+ ESC & j @ remove labels from screen
+
+ switch ON:
+ ESC & j A display system fkey labels
+ ESC & j B display user fkey labels
+ ESC & j C display current fkey labels
+
+
+ ALT-F10 User / System Function key labels
+ -----------------------------------------
+
+ this key is only operational, when in HP/VT mode, see ALT-F11
+
+ ALT-F10 toggles between the display of user or system
+ function key labels
+
+ this key is only active if labels are toggled on via
+ the ALT-F9 function key
+
+ screen has either 25-3 = 22, 28-3 = 25 or 50-3 = 47 lines
+ 2 lines fkey labels displayed
+ row/col display enabled
+ 1 line status/load avg line
+
+
+ applicable escape sequences: (see above)
+
+ ESC & j A display system fkey labels
+ ESC & j B display user fkey labels
+ ESC & j C display current fkey labels
+
+
+ ALT-F11 Toggle between pure VT and HP-VT Emulation
+ --------------------------------------------------
+
+ This key switches between pure vt100/vt220 mode and
+ vt100/vt220 with hp-fkey-labels mode of operation.
+ this is not dependent of any screen resolution the
+ virtual consoles are in.
+
+ initially, after power on, all virtual screens are in
+ the pure vt-mode:
+
+ - the emulator does not execute any hp escape sequences
+ - the function key label lines are switched off
+ - no load average or status line is displayed
+ - no row / column counter is displayed
+ - no window number is displayed
+ - the full screen 25x80, 28x80 or 50x80 is usable
+ - ALT-F9 and ALT-F10 have no function
+
+ when toggled to the vt/hp mix of operation, the following
+ changes take place:
+
+ - the emulator executes the supported hp-esc sequences
+ - the function key labels are displayable depending
+ on the state of ALT-F9/F10
+ - the load average is displayed
+ - the row / column counters are displayed
+ - the window number is displayed in the lower right corner
+ - in any screen resolution, the last 3 lines are lost,
+ so one has a 22x80, 25x80 and 47x80 resolution
+ - F9 and F10 are operational.
+
+ when switching modes by means of ALT-F11, the following
+ changes to the current environment, the emulator may
+ be in, apply:
+
+ - the screen is cleared
+ - the cursor moves to the home position
+ - the scrolling region is reset to default
+
+ (this functionality is available via an ioctl)
+
+ ALT-F12 Cycle current screen
+ ----------------------------
+
+ This key cycles through the display of the video
+ screens. on startup, screen 0 is displayed; with every
+ keypress of F12, the next screen is displayed, wrapping
+ from the maximum screen number back to screen 0.
+
+ (this functionality is available via an ioctl)
+
+
+III. Special Keys used to change the Runtime Functionality of a Page
+=======================================================================
+
+ when in mixed HP/VT mode, one has two types of function key labels
+ on screen, user function keys and system function keys.
+
+ this functionality is NOT available in pure VT220 mode !
+
+ the user function keys emit the below mentioned VT220 function-
+ key sequences. the labels can be reprogrammed by use of escape
+ sequences.
+
+ in system function key mode, several pre-programmed functions inside
+ the emulator can be toggled, currently implemented are:
+
+ - F1, if a chipset is detected for which 132 operation is supported,
+ F1 toggles between 80 columns and 132 columns.
+ - F2, does a soft reset of the emulator code
+ - F3, -/-
+ - F4, -/-
+ - F5, toggle the audible beep generation
+ - F6, toggle 7/8 bit char width
+ - F7, toggle display functions. this means that control codes
+ in the range 0x00 to 0x1f are not EXECUTED by the emulator
+ any longer, but displayed on the screen
+ - F8, toggle automatic cursor wraparound at end of line
+
+ these functions operate just for the selected screen
+
+
+IV. Keyboard VT220 compatibility
+====================================
+
+ The following keys behave different as probably expected.
+
+ They were mapped to provide more VT220 compatibility.
+
+ To have F1-F8 emit something in HP-mode, you have to switch
+ to user function key labels by using ALT-F10 (see ALT-F10 above) !
+
+ PC Keyboard VT220 Keyboard
+ ------------- --------------------------------------------------
+ F1 F6 (ESC [ 17 ~)
+ F2 F7 (ESC [ 18 ~)
+ F3 F8 (ESC [ 19 ~)
+ F4 F9 (ESC [ 20 ~)
+ F5 F10 (ESC [ 21 ~)
+ F6 F11 (ESC [ 23 ~)
+ F7 F12 (ESC [ 24 ~)
+ F8 F13 (ESC [ 25 ~)
+ ALT-F1 F14 (ESC [ 26 ~)
+ ALT-F2 HELP (ESC [ 28 ~)
+ ALT-F3 DO (ESC [ 29 ~)
+ ALT-F4 F17 (ESC [ 31 ~)
+ ALT-F5 F18 (ESC [ 32 ~)
+ ALT-F6 F19 (ESC [ 33 ~)
+ ALT-F7 F20 (ESC [ 34 ~)
+ ALT-F8 F21 (ESC [ 35 ~) (i know !!!!)
+
+ SHIFT-F1 ... SHIFT-F8
+ User Defined Keys for F6 - F13
+
+ ALT-SHIFT-F1 ... ALT-SHIFT-F8
+ User Defined Keys for F14 - F20
+
+ Insert Insert Here (ESC [ 2 ~)
+
+ Delete Remove (ESC [ 3 ~)
+
+ Home Find (ESC [ 1 ~)
+
+ End Select (ESC [ 4 ~)
+
+ PgUp Prev Screen (ESC [ 5 ~)
+
+ PgDn Next Screen (ESC [ 6 ~)
+
+ PrtSc (ignored)
+
+ Pause (ignored)
+
+ Break (ignored)
+
+ Cursor Keys dependent upon state of cursor key mode either
+ ESC [ A ... ESC [ D or ESC O A ... ESC O D
+
+ NumLock toggles Numeric Keypad for Keypad Numeric Mode
+ and Keypad Application Mode
+
+ ALT-Keypad-0 can be used to generate any keycode in the
+ ALT-Keypad-1 rage 0-255.
+ ALT-Keypad-2 this was modeled with the behaviour of a
+ ALT-Keypad-3 popular boot loader in mind
+ ALT-Keypad-4
+ ALT-Keypad-5
+ ALT-Keypad-6
+ ALT-Keypad-7
+ ALT-Keypad-8
+ ALT-Keypad-9
+
+ Numeric Keypad dependent upon state of keypad numeric/application
+ mode either 0 ... 9 or ESC O p ... ESC O y
+
+ ALT-NumLock emits PF1 sequence (ESC O P)
+
+ ALT-Keypad-/ emits PF2 sequence (ESC O Q)
+
+ ALT-Keypad-* emits PF3 sequence (ESC O R)
+
+ ALT-Keypad-- emits PF4 sequence (ESC O S)
+
+
+/* EOF */
diff --git a/usr.sbin/pcvt/Misc/Doc/Keyboard.VT b/usr.sbin/pcvt/Misc/Doc/Keyboard.VT
new file mode 100644
index 0000000..4353ec7
--- /dev/null
+++ b/usr.sbin/pcvt/Misc/Doc/Keyboard.VT
@@ -0,0 +1,231 @@
+================================================================================
+# #
+# Function key mapping for the "more VT220" - like layout #
+# #
+================================================================================
+
+
+I. Special Keys Used To Change The General Runtime Functionality
+=====================================================================
+
+
+ CTRL-F9 Function key labels ON / OFF
+ ------------------------------------
+
+ this key is only operational, when in HP/VT mode, see CTRL-F11
+
+ CTRL-F9 toggles between function key labels displayed or not.
+
+ ON: screen has either 25-3 = 22, 28-3 = 25 or 50-3 = 47 lines
+ 2 lines fkey labels displayed
+ row/col display enabled
+ 1 line status/load avg line
+
+ OFF: screen has either 25-3 = 22, 28-3 = 25 or 50-3 = 47 lines
+ no function key labels displayed
+ no row/col display
+ no status/load avg line
+
+ applicable escape sequences:
+
+ switch OFF:
+ ESC & j @ remove labels from screen
+
+ switch ON:
+ ESC & j A display system fkey labels
+ ESC & j B display user fkey labels
+ ESC & j C display current fkey labels
+
+
+ CTRL-F10 User / System Function key labels
+ -------------------------------------------------
+
+ this key is only operational, when in HP/VT mode, see CTRL-F11
+
+ CTRL-F10 toggles between the display of user or system
+ function key labels
+
+ this key is only active if labels are toggled on via
+ the CTRL-F9 function key
+
+ screen has either 25-3 = 22, 28-3 = 25 or 50-3 = 47 lines
+ 2 lines fkey labels displayed
+ row/col display enabled
+ 1 line status/load avg line
+
+
+ applicable escape sequences: (see above)
+
+ ESC & j A display system fkey labels
+ ESC & j B display user fkey labels
+ ESC & j C display current fkey labels
+
+
+ CTRL-F11 Toggle between pure VT and HP-VT Emulation
+ ----------------------------------------------------------
+
+ This key switches between pure vt100/vt220 mode and
+ vt100/vt220 with hp-fkey-labels mode of operation.
+ this is not dependent of any screen resolution the
+ virtual consoles are in.
+
+ initially, after power on, all virtual screens are in
+ the pure vt-mode:
+
+ - the emulator does not execute any hp escape sequences
+ - the function key label lines are switched off
+ - no load average or status line is displayed
+ - no row / column counter is displayed
+ - the full screen 25x80, 28x80 or 50x80 is usable
+ - CTRL-F9 and CTRL-F10 have no function
+
+ when toggled to the vt/hp mix of operation, the following
+ changes take place:
+
+ - the emulator executes the supported hp-esc sequences
+ - the function key labels are displayable depending
+ on the state of CTRL-F9/CTRL-F10
+ - the load average is displayed
+ - the row / column counters are displayed
+ - in any screen resolution, the last 3 lines are lost,
+ so one has a 22x80, 25x80 and 47x80 resolution
+ - CTRL-F9 and CTRL-F10 are operational.
+
+ when switching modes by means of CTRL-F11, the following
+ changes to the current environment, the emulator may
+ be in, apply:
+
+ - the screen is cleared
+ - the cursor moves to the home position
+ - the scrolling region is reset to default
+
+ (this functionality is available via an ioctl)
+
+ F5 Cycle current page
+ --------------------------
+
+ This key cycles through the display of the video
+ pages. on startup, page 0 is displayed; with every
+ keypress of F5, the next page is displayed, wrapping
+ from the maximum page number back to page 0.
+
+ F1 switches to page 0
+ F2 switches to page 1
+ F3 switches to page 2
+ F4 switches to page 3
+
+ Shift-F1 -- select screen 4
+ Shift-F2 -- select screen 5
+ Shift-F3 -- select screen 6
+ Shift-F4 -- select screen 7
+ Shift-F5 -- select (current screen - 1)
+
+ (this functionality is available via an ioctl)
+
+
+II. Special Keys used to change the Runtime Functionality of a Page
+=======================================================================
+
+ when in mixed HP/VT mode, one has two types of function key labels
+ on screen, user function keys and system function keys. they are
+ accessed by using the CTRL key while pressing a function key.
+
+ the function key labels are NOT available in pure VT220 mode--although
+ the function keys are still active
+
+ the user function keys and their labels can be reprogrammed by use of
+ escape sequences.
+
+ in system function key mode, several pre-programmed functions inside
+ the emulator can be toggled, currently implemented are:
+
+ - CTRL-F1, toggle 80/132 columns
+ - CTRL-F2, soft reset emulator
+ - CTRL-F3, toggle force 24 lines mode
+ - CTRL-F4, toggle keyboard debugging (if compiled in)
+ - CTRL-F5, toggle the audible beep generation
+ - CTRL-F6, toggle 7/8 bit char width
+ - CTRL-F7, toggle display functions. this means that control codes
+ in the range 0x00 to 0x1f are not EXECUTED by the emulator
+ any longer, but displayed on the screen
+ - CTRL-F8, toggle automatic cursor wraparound at end of line
+
+ these functions operate just for the selected page
+
+
+III. Keyboard VT220 compatibility
+====================================
+
+ The following keys behave different as probably expected.
+
+ They were mapped to provide more VT220 compatibility.
+
+
+ PC Keyboard VT220 Keyboard
+ ------------- --------------------------------------------------
+ F6 - F12 emit the sequences for VT220 F6 - F12 keys
+ (ESC [ 17 ~ .... ESC [ 24 ~)
+
+ ALT F1 - F10 emit the sequences for VT220 F11 - F14, HELP, DO,
+ F17 - F20 keys
+ (ESC [ 23 ~ .... ESC [ 34 ~)
+
+ SHIFT F6 - F12 emit the User Definable Key sequences for VT220
+ F6 - F12 keys or
+ (ESC [ 17 ~ .... ESC [ 24 ~) for cleared sequences
+
+ ALTSHIFT F1-F10 emit the User Definable Key sequences for VT220
+ F11 - F14, HELP, DO, F17 - F20 keys or
+ (ESC [ 23 ~ .... ESC [ 34 ~) for cleared sequences
+
+ Insert Insert Here (ESC [ 2 ~)
+
+ Delete Remove (ESC [ 3 ~)
+
+ Home Find (ESC [ 1 ~)
+
+ End Select (ESC [ 4 ~)
+
+ PgUp Prev Screen (ESC [ 5 ~)
+
+ PgDn Next Screen (ESC [ 6 ~)
+
+ PrtSc (ignored)
+
+ Pause (ignored)
+
+ Break (ignored)
+
+ Cursor Keys dependent upon state of cursor key mode either
+ ESC [ A ... ESC [ D or ESC O A ... ESC O D
+
+ NumLock toggles Numeric Keypad for Keypad Numeric Mode
+ and Keypad Application Mode
+
+ ALT-Keypad-0 can be used to generate any keycode in the
+ ALT-Keypad-1 rage 0-255.
+ ALT-Keypad-2 this was modeled with the behaviour of a
+ ALT-Keypad-3 popular boot loader in mind
+ ALT-Keypad-4
+ ALT-Keypad-5
+ ALT-Keypad-6
+ ALT-Keypad-7
+ ALT-Keypad-8
+ ALT-Keypad-9
+
+ Numeric Keypad dependent upon state of keypad numeric/application
+ mode either 0 ... 9 or ESC O p ... ESC O y
+
+ ALT-NumLock emits PF1 sequence (ESC O P)
+
+ ALT-Keypad-/ emits PF2 sequence (ESC O Q)
+
+ ALT-Keypad-* emits PF3 sequence (ESC O R)
+
+ ALT-Keypad-- emits PF4 sequence (ESC O S)
+
+ ALT-F11 emits PF1 sequence (ESC O P)
+
+ ALT-F12 emits PF2 sequence (ESC O Q)
+
+/* EOF */
diff --git a/usr.sbin/pcvt/Misc/Doc/Makefile b/usr.sbin/pcvt/Misc/Doc/Makefile
new file mode 100644
index 0000000..8999896
--- /dev/null
+++ b/usr.sbin/pcvt/Misc/Doc/Makefile
@@ -0,0 +1,15 @@
+FILES= Acknowledgements Bibliography BugList ChangeLog CharGen \
+ Charsets EscapeSequences Keyboard.HP Keyboard.VT \
+ Manifest NotesAndHints TestedHardware ToDo
+
+beforeinstall:
+ for file in ${FILES}; \
+ do \
+ ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \
+ ${.CURDIR}/$$file ${DESTDIR}${BINDIR}/Doc/$$file ; \
+ done
+
+afterdistribute: beforeinstall
+
+.include "../Makefile.inc"
+.include <bsd.subdir.mk>
diff --git a/usr.sbin/pcvt/Misc/Doc/Manifest b/usr.sbin/pcvt/Misc/Doc/Manifest
new file mode 100644
index 0000000..cd75e15
--- /dev/null
+++ b/usr.sbin/pcvt/Misc/Doc/Manifest
@@ -0,0 +1,162 @@
+
+FILES AND DIRECTORIES - Base Directory [pcvt Release 3.20]
+--------------------------------------------------------------------------------
+
+Doc Documentation, see below
+Etc Additional things, see below
+README.FIRST guess what
+Util Additional utilities and support, see below
+
+Driversource Description
+------------ --------------------------------------------------------
+pcvt_conf.h Driver, compile-time configuration file
+pcvt_drv.c Driver, os-interface
+pcvt_ext.c Driver, USL-VT Xserver ioctl's and 132 column support
+pcvt_hdr.h Driver, global include file
+pcvt_ioctl.h Driver, ioctl's available for the driver
+pcvt_kbd.c Driver, keyboard handling
+pcvt_kbd.h Driver, keyboard handling header file
+pcvt_out.c Driver, VT220 emulator state machine and misc
+pcvt_sup.c Driver, support code for ega's/vga's
+pcvt_tbl.h Driver, character set to vga charset mapping tables
+pcvt_vtf.c Driver, VT220 emulator support functions
+
+FILES AND DIRECTORIES - Doc
+--------------------------------------------------------------------------------
+
+Acknowledgements Everyone and everything who/what helped
+Bibliography Useful Books and documentation
+BugList Things to do
+ChangeLog Driver development history
+CharGen Description of the character set files
+Charsets VT220 and mda/cga/hcs/ega/vga charactersets
+EscapeSequences List of supported control codes & escape sequences
+INSTALL.FreeBSD-1.1 Install instructions for FreeBSD Release 1.1 or 1.1.5.1
+INSTALL.FreeBSD-2.0 Install instructions for FreeBSD Release 2.0
+INSTALL.NetBSD-0.9 Install instructions for NetBSD Release 0.9
+INSTALL.NetBSD-1.0 Install instructions for NetBSD Release 1.0
+Keyboard.HP Special keys and emulated VT220 keys, one style
+Keyboard.VT Special keys and emulated VT220 keys, other style
+Makefile Makefile for the pcvt.4 manualpage
+Manifest This file
+NotesAndHints Random notes and hints
+TestedHardware A list of tested Hardware, this is just what we got ...
+ToDo A list of things to do
+pcvt.4 Manual page for the driver and ioctl's
+
+
+FILES AND DIRECTORIES - Etc
+--------------------------------------------------------------------------------
+
+MAKEDEV.pcvt A shell script to mknod the device files
+Terminfo A terminfo entry for the emulator, this IS in
+ fact a VT220 terminfo entry !
+Termcap A termcap entry for the emulator, this IS in fact
+ a series of VT220 termcap entries which
+ are extended by 24/25/40/50 lines, 80/132
+ columns and HP-function key labels
+pcvt.el GNU emacs configuration
+rc.local sample script for driver initialization
+ttys.pcvt.netbsd sample /etc/ttys.pcvt for NetBSD-current
+ttys.pcvt.freebsd sample /etc/ttys.pcvt for FreeBSD 1.1R
+ttys.pccons.netbsd sample /etc/ttys.pccons for NetBSD-current
+ttys.pccons.freebsd sample /etc/ttys.pccons for FreeBSD 1.1R
+uemacs.tar.Z.uu an example of how to use the function key labels.
+ It consists of some files from/for MicroEmacs 3.11a:
+ - dot-emacsrc, to be moved to /usr/local/.emacsrc,
+ startup file for micro emacs, contains setup of
+ the function key labels
+ - emacs.hlp, interactive uemacs help system
+ - unix.c-3.11a the source for the terminal handling
+xmodmap-german X-server german keyboard mapping sample
+
+
+FILES AND DIRECTORIES - Util
+--------------------------------------------------------------------------------
+
+directory description
+--------- ---------------------------------------------------------------
+
+loadfont Program to download charactersets into EGA/VGA character
+ generator memory.
+
+cursor Very simple program to set the cursor shape.
+
+scon Program to control various aspects of terminal emulation,
+ such as: emulation mode, screen switching etc.
+
+kcon Program to control various aspects of the keyboard such as
+ key remapping for national keyboards etc.
+
+keycap A library for accessing the keycap database
+
+ispcvt A short program usable in shell scripts to verify that the
+ current running kernel has pcvt compiled in and that the
+ the utility version and the driver version are the same.
+
+fontedit A program to edit VT220 downloadable character sets.
+
+userkeys A program to edit the VT220 programmable function keys.
+
+fonts Contains uuencoded binary fontfiles following the naming rule:
+
+ vt220<X>.<YYY>.uu
+
+ <X> can be 'l' or 'h', where 'l' stands for a standard
+ IBM II charset and is to be loaded first for the
+ base characterset to support a desired resolution,
+ and 'h' is the extended characterset needed for proper
+ VT220 emulation and is to be loaded as the second set
+ in addition to the above mentioned base characterset.
+
+ <YYY> is the identifier for the character cell size, currently
+ we have:
+ 808 = 8x8 - 50 lines on VGA, 43 lines on EGA
+ 810 = 8x10 - 40 lines on VGA
+ 814 = 8x14 - 28 lines on VGA, 25 lines on EGA
+ 816 = 8x16 - 25 lines on VGA
+
+ Files distributed:
+
+ vt220l.808.uu, vt220h.808.uu
+ vt220l.810.uu, vt220h.810.uu
+ vt220l.814.uu, vt220h.814.uu
+ vt220l.816.uu, vt220h.816.uu
+
+vttest A VT100 compatibility tester. This is a test tool for
+ VT100 emulation writers and terminal buyers.
+
+demo - chardemo.vt and colors.vt: These two files are from the
+ MSDOS-Kermit distribution from the Columbia University.
+ They can be "cat"-ed to the terminal screen and display
+ all supported VT220 charactersets and all possible colors
+ respectively.
+ - sgr.vt: A demonstration of the various display enhancement
+ combinations for the DECSGR escape sequence.
+ - some other VT animations i collected over the time and a
+ program to play them on pcvt with adjustable delay.
+
+fed - a simple, System V curses based font-editor for the EGA/VGA
+ fonts in the above mentioned font - directory.
+ Fed was developed using Zeyd M. Ben-Halim's ncurses library,
+ which is available by ftp from netcom.com:pub/zmbenhal/.
+
+mcon - a program to control the mouse emulation via the keyboard
+
+kbdio - keyboard controller debugging utility
+
+vgaio - a program to read and write VGA registers.
+ ---------------------- CAUTION ------------------------------
+ Because you are able to change the timings without check
+ or warning, this program may permanently damage your monitor.
+ If you don't know what you are doing, DO NOT USE IT !!!!!!!!!
+ -------------------------------------------------------------
+
+set2061 - a program to set the programmable clock generator on my S3-
+ based ELSA Winner 1000 VGA board.
+ ---------------------- CAUTION ------------------------------
+ Because you are able to change the timings without check
+ or warning, this program may permanently damage your monitor.
+ If you don't know what you are doing, DO NOT USE IT !!!!!!!!!
+ -------------------------------------------------------------
+
diff --git a/usr.sbin/pcvt/Misc/Doc/NotesAndHints b/usr.sbin/pcvt/Misc/Doc/NotesAndHints
new file mode 100644
index 0000000..725831a
--- /dev/null
+++ b/usr.sbin/pcvt/Misc/Doc/NotesAndHints
@@ -0,0 +1,321 @@
+Random Notes and Hints Last Edit-Date: [Sun Apr 2 18:28:09 1995]
+--------------------------------------------------------------------------------
+
+
+First of all, please read the file BugList in this directory !
+
+
+Can't get pcvt working on a ThinkPad
+===============================================================================
+
+Anyway, back to the keyboard. The problem is that by default the
+ThinkPad uses PS/2 scan code mode.
+
+You can fix this by using an option and building a kernel, as shown
+below.
+
+] Just for the record, in case someone else is asking for this: Al's
+] confirmation that pcvt w/ PCVT_SCANSET=2 works for the ThinkPad:
+]
+] As Al Elia wrote:
+] | Date: Mon, 28 Nov 1994 18:24:42 GMT
+] | From: Al Elia <aelia%aelia.student.harvard.edu@sax.sax.de>
+] | Message-Id: <199411281824.SAA01554@aelia.student.harvard.edu>
+] | To: joerg_wunsch@bonnie.tcd-dresden.de
+] | Subject: Re: Anyone got FreeBSD 1.1.5.1 running on a ThinkPad?
+] |
+] | PCVT_SCANSET=2 worked...I had put in PCVT_SCAN_SET=2 (Doh!)
+] |
+] | --Al Elia
+] | <aelia@aelia.student.harvard,edu>
+
+ (Terry Lambert quoting Joerg Wunsch quoting Al Elia)
+
+
+If one of the "lock" keys is pressed, LEDs do not get updated, keyboard hangs
+===============================================================================
+
+This entry used to be a long time in the BugList file, and i could never
+reproduce the problem. Today i got an explanation in german from someone
+owning such a keyboard, i'll try to translate:
+
+"This are old keyboards manufactured (~1985/1986) which manage their LED
+ setting only internally.
+ It is not possible to set the LEDs from the (main-) processor, if you
+ try, the keyboard processor hangs and the PC has to be reset by switching
+ power on and off, hard- and/or softreset does not work in this case.
+ Workaround: recompile pcvt with the LED update removed"
+
+In other words, define PCVT_NO_LED_UPDATE if you have such a beast!
+
+
+Cursor not visible anymore in 40 and 50 lines mode
+===============================================================================
+
+You have programmed an underline cursor in i.e. 28 line mode by doing
+"cursor -s 10 -e 12". Then you switch to 40 line mode using "scon -s 40".
+At this point the cursor is no longer visible because the 40 line font
+is only 10 pixels high and the cursor size is programmed with a value
+expressing its size from the top down and NOT from the bottom up!
+If anyone has a good idea how to solve this problem, please tell me!
+The only solution i see so far is having some sort of "generic" cursor
+sizes/descriptions (i.e. underline, rectangle, block) which are
+recalculated in case of a switch to another line size.
+
+
+386BSD port
+===============================================================================
+
+I don't have access to a 386BSD 0.1 machine anymore so the 386BSD pcvt is
+considered unsupported and will disappear in the future.
+
+386BSD support was dropped with release 3.20.
+
+
+Keyboard hangs after first update of keyboard LED's
+===============================================================================
+
+Define PCVT_NO_LED_UPDATE and recompile pcvt. (Or, get yourself a better
+keyboard. Some keyboards just don't work the documented way, this fact is
+"normally" masked by the manufacturers BIOS but unhides when one accesses
+the hardware directly.)
+
+
+Garbled screen when running vi
+===============================================================================
+
+When the terminal speed in the tty structure is set to low speeds (i.e. 1200
+Baud), pcvt shows a strange behaviour in some environments due to the changed
+screen update sequences from vi.
+
+Please check your shell startup files, /etc/ttys and /etc/gettytab and change
+the baudrate (i.e. by using stty(1)) to a higher value, i.e. 19200 Baud.
+
+Since i'm not a vi specialist, i never managed to find out wheter to blame
+vi or pcvt.
+
+
+Stty influences on the driver
+===============================================================================
+
+There used to be an entry in the BugList:
+
+ (printf with 9 x tab) printf "\n\t\t\t\t\t\t\t\t\tGotcha" works ok,
+ while one tab more: printf "\n\t\t\t\t\t\t\t\t\t\tGotcha" doesn't
+ work (it doesn't print Gotcha at column 80, but at column 131).
+
+This was solved some time ago:
+
+ On another note: if I use stty xtabs, the 'printf "\t\t\t\t\t\t\t"
+ bug goes away. With stty xtabs the tab handling is done in the kernel.
+
+(See also below: "Vttest shows strange results")
+
+
+After running some graphics application, the cursor is stuck on the
+bottom line, though everything else appears well
+===============================================================================
+
+Though this might initially appear to be a driver problem, it's rather
+an application program's bogosity. The cursor update is done asynchron-
+ously (to gain output speed), but this cursor update is inhibited while
+an application has put a virtual terminal into ``graphics mode'' (i.e.,
+the application program tells the driver that it's now responsible for
+anything and all on this vty). This is notably the case while X11 is
+running.
+
+If the application fails to properly shut down itself, the terminal
+might be left in an undefined state. The driver stand no chance there,
+even if it could detect this bad status, since it doesn't know enough
+about each piece of hardware to deal with. One possibility is that
+the X server has been shot up and didn't get it to do its cleanups.
+Another case (which i've often noticed on my slow notebook) is, killing
+the Xserver is too slow for the (unfortunately hard-coded) 10-second
+timeout from xinit, so it's being aborted ridiculously. (``X server
+slow to shut down, sending KILL signal.'') This way, the state of
+damage might range from ``almost okay, but cursor is stuck'' up to
+a totally unusable machine (moon bitmap from xphoon still displayed,
+no keyboard responses, only network is working and can be used to
+shut down cleanly).
+If the state of damage is only minimal, you might try to run the pure
+X server on that vty again, and exit it with Ctrl-Alt-BkSpc. This might
+be a workaround.
+
+
+Vttest shows strange results
+===============================================================================
+
+Verify your stty "oxtabs" settings, it has to be "oxtabs", NOT "-oxtabs".
+Get yourself an original DEC terminal to verify vttest's output, i have
+until now not seen any (!) VTxxx clone, which does it right !!!
+
+
+VT220-like Keyboard Layout
+===============================================================================
+
+I have to say, i don't use it and i don't like it, so it's mostly unsupported
+and untested. Patches welcome!
+
+
+132-column mode
+===============================================================================
+
+There are known difficulties running pcvt in 132 column mode in conjunction
+with X. Switching to 132 column mode does not only depend on a given chipset,
+but on the board/manufacturers method of clock generation also. Even if your
+chipset is detected, there may be still a problem with your board and it's
+method of generating clocks. You may run in severe difficulties if your
+board has a programmable clock generator and you run X and you switch from
+132 col mode into X and back.
+
+I have currently no idea how to solve this, other than having a similar
+scheme as XFree86 applied to pcvt: Letting the user probe his board by using
+SuperProbe and recompiling pcvt according to the result.
+
+I stumbled a bit deeper into this with my ELSA Winner 1000, which is equipped
+with a ICD2061 clock synthesizer chip. For 132 column mode to work properly,
+clock generator 2 must deliver 40 MHz to the S3 VGA chip, but this value has
+to be programmed or initialized. If this VGA board has ever been switched
+into 132 colums, i.e. in my case from a DOS program, it will continue to do
+so until X runs or the machine is power cycled. If that occurs, the clock
+generator 2 does contain nothing or garbage (in case of power cycling) or it
+does contain the value for the current resolution in X in case of having been
+in the X Server screen recently.
+
+The X Server reprograms the clock generator each time the server is entered,
+so the only thing to do is to reprogram the clock generator too when pcvt is
+entered. Until now i found no way of identifying the clock oscillator chip
+used, so an automatic clock switching seems to be a problem.
+
+
+NetBSD 0.9 and Xfree86 2.0
+===============================================================================
+
+To get the X server up and running on 0.9, you have to compile pcvt with
+PCVT_USL_VT_COMPAT disabled, otherwise X (and SuperProbe) will hang the
+video driver (not the whole machine !). This bug is reproducible but not
+found yet ...
+This does not apply to NetBSD-current, 386BSD and FreeBSD.
+
+
+X server ioctl compatibility:
+===============================================================================
+
+The compatibility X-Mode ioctl commands CONSOLE_X_MODE_ON and
+CONSOLE_X_MODE_OFF should not be used intermixed with the USL VT style
+commands on another virtual terminal. NB, that this situation could happen
+if you run an XFree86 2.0 server on one virtual terminal and attempt to
+run SuperProbe version 1.0 (as delivered with the XFree86 2.0 release)
+on another vty. SuperProbe is still using the old commands in order to
+gain IO privileges.
+Since the old commands cannot care for things like terminal switching,
+serious corruption could result from this, which need not to be detected
+immediately (i.e., apparently SuperProbe ran well). Known problems are
+font corruptions after the X server has been shut down later, or palette
+flickers in 1-second intervals due to an erroneously re-enabled screen
+saver.
+
+Once that SuperProbe has been fixed in its release to use the USL VT style
+commands, any support for the old CONSOLE_X_MODE_XXX commands will be
+eliminated.
+
+(Recent comment: SuperProbe 1.3 has been fixed. It will be delivered with
+XFree86 2.1.)
+
+
+How to set the foreground intensity to high on VGA mono screens:
+===============================================================================
+
+try to issue the command: "scon -p8,60,60,60", EXPERIMENT !!!
+
+
+How to change the color palette on VGA cards:
+===============================================================================
+
+try out the following commands:
+
+ /usr/local/bin/scon -d/dev/ttyv0 -pblack:0,0,0 -pblue:20,20,40
+ /usr/local/bin/scon -d/dev/ttyv0 -pbrown:55,55,15 -plightgray:0,42,0
+ /usr/local/bin/scon -d/dev/ttyv1 -pblack:42,42,42 -pblue:60,60,60
+ /usr/local/bin/scon -d/dev/ttyv1 -pbrown:60,60,30 -plightgray:30,10,0
+ /usr/local/bin/scon -d/dev/ttyv2 -pblack:42,42,42 -pblue:63,63,63
+ /usr/local/bin/scon -d/dev/ttyv2 -pbrown:60,60,20 -plightgray:0,22,0
+ /usr/local/bin/scon -d/dev/ttyv3 -pblack:38,38,38 -pblue:63,63,63
+ /usr/local/bin/scon -d/dev/ttyv3 -pbrown:60,40,0 -plightgray:0,0,20
+
+ ("scon -p default" resets the colors ...)
+
+
+I have the screensaver compiled in, but can't see any effect
+===============================================================================
+
+Don't forget to turn it on with the scon utility. E.g.,
+
+ scon -t 120
+
+sets the timeout to 2 minutes.
+
+
+Your Notebook uses the NumLock state to switch half of the keyboard into a
+numeric keypad
+===============================================================================
+
+Sigh, each time you leave "vi", your NumLock LED is on again and you
+get a "6" instead of "o"? Try
+
+ options "PCVT_INHIBIT_NUMLOCK"
+
+this prevents applications from turning NumLock on/off (except the
+Xserver - but you want this).
+
+
+Your notebook significantly loses contrast when using pcvt
+===============================================================================
+
+Pcvt turns off the "high intensity" attribute bit internally (to enable
+the use of a 512-characters charset). Some notebooks hard-code the out-
+put intensity versus the character attribute though (i know it for a
+Cirrus Logic CL-GD610/620 chipset).
+
+As a quick & dirty workaround, you can reverse what pcvt did to the
+Attribute Controller. Do not hack pcvt_sup.c, instead patch your
+VGA registers during rc.local with the help of the vgaio utility:
+
+ echo "ar12=0f" | vgaio > /dev/null
+
+For the CL-GD610/620, i'm remapping some attribute registers and
+get a simple gray scale emulation with this (i.e., i DO NOT use
+the hack above):
+
+ eagle_id=`echo 'cr1f?' | vgaio | cut -dx -f2`
+ echo "sr 6 = $eagle_id" | vgaio > /dev/null # enable extended regs
+ echo "sr d5 = 40" | vgaio > /dev/null # not inverse, enable
+ # color emulation
+ echo "ar0=0;ar1=9;ar2=12;ar3=1b;ar4=24;ar5=2d;ar6=36;ar7=3f"|vgaio>/dev/null
+ echo "ar8=0;ar9=9;ara=12;arb=1b;arc=24;ard=2d;are=36;arf=3f"|vgaio>/dev/null
+
+NOTE THAT THIS IS ONLY FROM EXPERIMENTS! There's no warranty that something
+like this wouldn't damage your screen/VGA!
+
+(If you have chipset documentation, you're lucky...)
+
+
+How to set the "LINES"-Environment variable for sh/csh:
+===============================================================================
+
+(Note: this is mostly obsoleted now since the driver properly generates
+SIGWINCH'es to notify applications about a changed screen size.)
+
+ first for the csh:
+
+ alias linesw scon -s \!^ \; setenv LINES \!^
+
+ now for the bash/ash/sh/bash users:
+
+ linesw()
+ {
+ scon -s $1
+ LINES=$1; export LINES
+ }
+
+/* EOF */
diff --git a/usr.sbin/pcvt/Misc/Doc/TestedHardware b/usr.sbin/pcvt/Misc/Doc/TestedHardware
new file mode 100644
index 0000000..5d37681
--- /dev/null
+++ b/usr.sbin/pcvt/Misc/Doc/TestedHardware
@@ -0,0 +1,79 @@
+Tested Hardware List last edit-date: [Mon Feb 20 19:36:20 1995]
+
+This is by no means a complete list of hardware pcvt runs on, it is just
+compiled from reports people sent in and from hardware owned/loaned !
+
+
+TESTED VIDEO BOARDS (80 column operation)
+--------------------------------------------------------------------------------
+
+Manufacturer Chipset Monitor Notes
+------------------------------ -------------- ---------------------- --------
+2theMax (?) ET4000 VGA Color
+Video7 Inc. VEGA VGA VGA Color/Mono (2)
+Diamond Stealth VRAM S3 NEC 3FGx
+Trident TVGA 8800CS NEC 3D
+Trident TVGA 9000B VGA Color/Mono (2)
+Data General C&T P82C604 VGA Color
+NoName Hercules W86855AF Mono
+Tandon Monochrome (Hercules) TD3088A Tandon Mono
+Kyocera ML III25 (Mainboard) WD90C00 JVC VGA Color (1)
+Kyocera ML III25 (Mainboard) WD90C00 Nokia CED1 VGA Mono (1)
+Kyocera ML IIII33 (Mainboard) WD90C11 Nokia CED1 VGA Mono (1)
+NoName VGA TVGA9000B JVC VGA Color (1,2)
+Tseng Labs ET3000AX JVC VGA Color (2)
+Video7 Inc. VEGA VGA VGA Mono (2)
+Video7 Inc. 1024i VGA Mono (2)
+ELSA GmbH S3 928 VGA Mono/Color
+IBM EGA 6845 Monochrome (2,3)
+IBM EGA 6845 Tandon EGA Color (2,3)
+Trident TVGA 8900CL VGA Mono (2)
+
+Notes:
+(1) - slight display distortion when switching between screens
+(2) - remarkable display distortion and/or loss of sync while loading fonts
+(3) - snow while scrolling with HP function key labels turned on
+
+TESTED VIDEO BOARDS (132 column operation)
+--------------------------------------------------------------------------------
+
+Manufacturer Chipset Monitor Notes
+------------------------------ -------------- ---------------------- --------
+2theMax (?) ET4000 VGA Color
+2theMax (?) ET4000 Tandon VGA Mono
+Kyocera ML III33 (Mainboard) WD90C11 Tandon VGA Mono (1,2)
+Kyocera ML IIII33 (Mainboard) WD90C11 Tandon VGA Mono (1,2)
+Kyocera ML IIII33 (Mainboard) WD90C11 VGA Color (1,2)
+Trident (?) TVGA9000B VGA Mono (1,3)
+Tseng Labs (?) ET3000 NEC 3D
+ELSA GmbH S3 928 VGA Mono/Color
+Trident TVGA 8900CL VGA Mono
+
+Notes:
+(1) - slight display distortion when switching between screens
+(2) - all fonts must be loaded in ascending order prior to switching to 132 cols
+(3) - remarkable display distortion and/or loss of sync while loading fonts
+
+
+TESTED KEYBOARDS
+--------------------------------------------------------------------------------
+
+Manufacturer Type Layout
+------------------------------ ---------------------- ------------------------
+Cherry MF II US
+Cherry/Tandon MF II German
+Hewlett-Packard MF II US
+Hewlett-Packard MF II German
+Tatung AT German
+Kyocera MF II German
+
+There is absolutely NO support for the ancient PC-keyboards (they had 83 keys).
+
+There is only limited support for AT-keyboards (they have 84 keys, and a
+separate numeric keypad, they don't have F9-F12 keys) because the emulator
+needs F9-F12 for control functions, and due to the current design of the
+keyboard driver there is no (full) support for national keyboards because
+of the lack of a ALTGR key.
+
+MF-keyboards are fully supported, 101- and 102-key versions.
+
diff --git a/usr.sbin/pcvt/Misc/Doc/ToDo b/usr.sbin/pcvt/Misc/Doc/ToDo
new file mode 100644
index 0000000..463e005
--- /dev/null
+++ b/usr.sbin/pcvt/Misc/Doc/ToDo
@@ -0,0 +1,13 @@
+
+Things to do Last Edit-Date: [Sun Apr 2 18:29:16 1995]
+================================================================================
+
+- implement secondary DA request: ESC [ > c and/or ESC [ > 0 c
+
+- implement user settable primary DA response and secondary DA response
+
+- retrying for a non-connected keyboard in doreset() must be made cpu and
+ speed independent. in case of a not connected keyboard, a fast machine
+ runs doreset() fast and a slow machine runs doreset() slow - bad !!
+
+- remove single screen X server support and make PCVT_USL_VT the default
diff --git a/usr.sbin/pcvt/Misc/Etc/Makefile b/usr.sbin/pcvt/Misc/Etc/Makefile
new file mode 100644
index 0000000..46117a4
--- /dev/null
+++ b/usr.sbin/pcvt/Misc/Etc/Makefile
@@ -0,0 +1,14 @@
+
+FILES= Termcap Terminfo pcvt.el rc.local uemacs.tar.Z.uu xmodmap-german
+
+beforeinstall:
+ for file in ${FILES}; \
+ do \
+ ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \
+ ${.CURDIR}/$$file ${DESTDIR}${BINDIR}/Etc/$$file ; \
+ done
+
+afterdistribute: beforeinstall
+
+.include "../Makefile.inc"
+.include <bsd.subdir.mk>
diff --git a/usr.sbin/pcvt/Misc/Etc/Termcap b/usr.sbin/pcvt/Misc/Etc/Termcap
new file mode 100644
index 0000000..a4eb030
--- /dev/null
+++ b/usr.sbin/pcvt/Misc/Etc/Termcap
@@ -0,0 +1,284 @@
+#---------------------------------------------------------------------------
+#
+# pcvt termcap database entry (release 3.20)
+#
+# last edit-date: [Sun Apr 2 18:31:04 1995]
+#
+# -hm new entries for SR and SF
+# -hm removing duplicate entries
+#
+#---------------------------------------------------------------------------
+
+#---------------------------------------------------------------------------
+# NetBSD/FreeBSD vt220 terminal emulator console (pc keyboard & monitor)
+# termcap entries for pure VT220-Emulation and 25, 28, 35, 40, 43 and
+# 50 lines entries
+# 80 columns
+#---------------------------------------------------------------------------
+pcvt25|dec vt220 with 25 lines:\
+ :li#25:\
+ :co#80:\
+ :is=\E>\E[?3l\E[?4l\E[?5l\E[?7h\E[?8h\E[1;25r\E[25;1H:\
+ :tc=pcvtXX:
+
+pcvt28|dec vt220 with 28 lines:\
+ :li#28:\
+ :co#80:\
+ :is=\E>\E[?3l\E[?4l\E[?5l\E[?7h\E[?8h\E[1;28r\E[28;1H:\
+ :tc=pcvtXX:
+
+pcvt35|dec vt220 with 35 lines:\
+ :li#35:\
+ :co#80:\
+ :is=\E>\E[?3l\E[?4l\E[?5l\E[?7h\E[?8h\E[1;35r\E[35;1H:\
+ :tc=pcvtXX:
+
+pcvt40|dec vt220 with 40 lines:\
+ :li#40:\
+ :co#80:\
+ :is=\E>\E[?3l\E[?4l\E[?5l\E[?7h\E[?8h\E[1;40r\E[40;1H:\
+ :tc=pcvtXX:
+
+pcvt43|dec vt220 with 43 lines:\
+ :li#43:\
+ :co#80:\
+ :is=\E>\E[?3l\E[?4l\E[?5l\E[?7h\E[?8h\E[1;43r\E[43;1H:\
+ :tc=pcvtXX:
+
+pcvt50|dec vt220 with 50 lines:\
+ :li#50:\
+ :co#80:\
+ :is=\E>\E[?3l\E[?4l\E[?5l\E[?7h\E[?8h\E[1;50r\E[50;1H:\
+ :tc=pcvtXX:
+
+#---------------------------------------------------------------------------
+# NetBSD/FreeBSD vt220 terminal emulator console (pc keyboard & monitor)
+# termcap entries for pure VT220-Emulation and 25, 28, 35, 40, 43 and
+# 50 lines entries
+# 132 columns
+#---------------------------------------------------------------------------
+pcvt25w|dec vt220 with 25 lines and 132 cols:\
+ :li#25:\
+ :co#132:\
+ :is=\E>\E[?3l\E[?4l\E[?5l\E[?7h\E[?8h\E[1;25r\E[25;1H:\
+ :tc=pcvtXX:
+
+pcvt28w|dec vt220 with 28 lines and 132 cols:\
+ :li#28:\
+ :co#132:\
+ :is=\E>\E[?3l\E[?4l\E[?5l\E[?7h\E[?8h\E[1;28r\E[28;1H:\
+ :tc=pcvtXX:
+
+pcvt35w|dec vt220 with 35 lines and 132 cols:\
+ :li#35:\
+ :co#132:\
+ :is=\E>\E[?3l\E[?4l\E[?5l\E[?7h\E[?8h\E[1;35r\E[35;1H:\
+ :tc=pcvtXX:
+
+pcvt40w|dec vt220 with 40 lines and 132 cols:\
+ :li#40:\
+ :co#132:\
+ :is=\E>\E[?3l\E[?4l\E[?5l\E[?7h\E[?8h\E[1;40r\E[40;1H:\
+ :tc=pcvtXX:
+
+pcvt43w|dec vt220 with 43 lines and 132 cols:\
+ :li#43:\
+ :co#132:\
+ :is=\E>\E[?3l\E[?4l\E[?5l\E[?7h\E[?8h\E[1;43r\E[43;1H:\
+ :tc=pcvtXX:
+
+pcvt50w|dec vt220 with 50 lines and 132 cols:\
+ :li#50:\
+ :co#132:\
+ :is=\E>\E[?3l\E[?4l\E[?5l\E[?7h\E[?8h\E[1;50r\E[50;1H:\
+ :tc=pcvtXX:
+
+#---------------------------------------------------------------------------
+# NetBSD/FreeBSD vt220 terminal emulator console (pc keyboard & monitor)
+# termcap entries for HP-Emulation and 25, 28, 35, 40, 43 and 50
+# lines entries. note that the HP-Emulation uses the bottom 3 lines
+# for status and function key labels, so we get always 3 lines less.
+# "Xs" is a nonstandard, private flag indicating HP-like fkey labels
+# 80 column entries
+#---------------------------------------------------------------------------
+pcvt22h|dec vt220 with HP-fkey labels and 22 lines:\
+ :li#22:\
+ :co#80:\
+ :Xs:\
+ :is=\E>\E[?3l\E[?4l\E[?5l\E[?7h\E[?8h\E[1;22r\E[22;1H:\
+ :tc=pcvtXX:
+
+pcvt25h|dec vt220 with HP-fkey labels and 25 lines:\
+ :li#25:\
+ :co#80:\
+ :Xs:\
+ :is=\E>\E[?3l\E[?4l\E[?5l\E[?7h\E[?8h\E[1;25r\E[25;1H:\
+ :tc=pcvtXX:
+
+pcvt32h|dec vt220 with HP-fkey labels and 32 lines:\
+ :li#32:\
+ :co#80:\
+ :Xs:\
+ :is=\E>\E[?3l\E[?4l\E[?5l\E[?7h\E[?8h\E[1;32r\E[32;1H:\
+ :tc=pcvtXX:
+
+pcvt37h|dec vt220 with HP-fkey labels and 37 lines:\
+ :li#37:\
+ :co#80:\
+ :Xs:\
+ :is=\E>\E[?3l\E[?4l\E[?5l\E[?7h\E[?8h\E[1;37r\E[37;1H:\
+ :tc=pcvtXX:
+
+pcvt40h|dec vt220 with HP-fkey labels and 40 lines:\
+ :li#40:\
+ :co#80:\
+ :Xs:\
+ :is=\E>\E[?3l\E[?4l\E[?5l\E[?7h\E[?8h\E[1;40r\E[40;1H:\
+ :tc=pcvtXX:
+
+pcvt47h|dec vt220 with HP-fkey labels and 47 lines:\
+ :li#47:\
+ :co#80:\
+ :Xs:\
+ :is=\E>\E[?3l\E[?4l\E[?5l\E[?7h\E[?8h\E[1;47r\E[47;1H:\
+ :tc=pcvtXX:
+
+#---------------------------------------------------------------------------
+# NetBSD/FreeBSD vt220 terminal emulator console (pc keyboard & monitor)
+# termcap entries for HP-Emulation and 25, 28, 35, 40, 43 and 50
+# lines entries. note that the HP-Emulation uses the bottom 3 lines
+# for status and function key labels, so we get always 3 lines less.
+# "Xs" is a nonstandard, private flag indicating HP-like fkey labels
+# 132 column entries
+#---------------------------------------------------------------------------
+pcvt22hw|dec vt220 with HP-fkey labels, 22 lines and 132 cols:\
+ :li#22:\
+ :co#132:\
+ :Xs:\
+ :is=\E>\E[?3l\E[?4l\E[?5l\E[?7h\E[?8h\E[1;22r\E[22;1H:\
+ :tc=pcvtXX:
+
+pcvt25hw|dec vt220 with HP-fkey labels, 25 lines and 132 cols:\
+ :li#25:\
+ :co#132:\
+ :Xs:\
+ :is=\E>\E[?3l\E[?4l\E[?5l\E[?7h\E[?8h\E[1;25r\E[25;1H:\
+ :tc=pcvtXX:
+
+pcvt32hw|dec vt220 with HP-fkey labels, 32 lines and 132 cols:\
+ :li#32:\
+ :co#132:\
+ :Xs:\
+ :is=\E>\E[?3l\E[?4l\E[?5l\E[?7h\E[?8h\E[1;32r\E[32;1H:\
+ :tc=pcvtXX:
+
+pcvt37hw|dec vt220 with HP-fkey labels, 37 lines and 132 cols:\
+ :li#37:\
+ :co#132:\
+ :Xs:\
+ :is=\E>\E[?3l\E[?4l\E[?5l\E[?7h\E[?8h\E[1;37r\E[37;1H:\
+ :tc=pcvtXX:
+
+pcvt40hw|dec vt220 with HP-fkey labels, 40 lines and 132 cols:\
+ :li#40:\
+ :co#132:\
+ :Xs:\
+ :is=\E>\E[?3l\E[?4l\E[?5l\E[?7h\E[?8h\E[1;40r\E[40;1H:\
+ :tc=pcvtXX:
+
+pcvt47hw|dec vt220 with HP-fkey labels, 47 lines and 132 cols:\
+ :li#47:\
+ :co#132:\
+ :Xs:\
+ :is=\E>\E[?3l\E[?4l\E[?5l\E[?7h\E[?8h\E[1;47r\E[47;1H:\
+ :tc=pcvtXX:
+
+#---------------------------------------------------------------------------
+# main entry, without "is" and "li" capabilities
+#
+# NOTE: because the 386BSD "vi"/"elvis" seems to have a bug if
+# both "ic" and "im" are specified (an original VT220
+# shows the same buggy behaviour!), "ic" has been taken
+# out of this entry. for reference, it should be "ic=\E[@".
+#
+#---------------------------------------------------------------------------
+pcvtXX|pcvt vt200 emulator (DEC VT220):\
+ :AL=\E[%dL:\
+ :DC=\E[%dP:\
+ :DL=\E[%dM:\
+ :DO=\E[%dB:\
+ :IC=\E[%d@:\
+ :LE=\E[%dD:\
+ :RI=\E[%dC:\
+ :SF=\E[%dS:\
+ :SR=\E[%dT:\
+ :UP=\E[%dA:\
+ :ae=^O:\
+ :al=\E[L:\
+ :am:\
+ :as=^N:\
+ :bl=^G:\
+ :bs:\
+ :cb=\E[1K:\
+ :cd=\E[J:\
+ :ce=\E[K:\
+ :cl=\E[H\E[J:\
+ :cm=\E[%i%d;%dH:\
+ :cr=^M:\
+ :cs=\E[%i%d;%dr:\
+ :ct=\E[3g:\
+ :dc=\E[P:\
+ :dl=\E[M:\
+ :do=^J:\
+ :ei=\E[4l:\
+ :ho=\E[H:\
+ :im=\E[4h:\
+ :it#8:\
+ :k1=\E[17~:\
+ :k2=\E[18~:\
+ :k3=\E[19~:\
+ :k4=\E[20~:\
+ :k5=\E[21~:\
+ :k6=\E[23~:\
+ :k7=\E[24~:\
+ :k8=\E[25~:\
+ :kD=\E[3~:\
+ :kH=\E[4~:\
+ :kI=\E[2~:\
+ :kN=\E[6~:\
+ :kP=\E[5~:\
+ :kb=\177:\
+ :kd=\EOB:\
+ :ke=\E[?1l\E>:\
+ :kh=\E[1~:\
+ :kl=\EOD:\
+ :km:\
+ :kr=\EOC:\
+ :ks=\E[?1h\E=:\
+ :ku=\EOA:\
+ :le=^H:\
+ :mb=\E[5m:\
+ :md=\E[1m:\
+ :me=\E[m:\
+ :mi:\
+ :mr=\E[7m:\
+ :ms:\
+ :nd=\E[C:\
+ :pb#16000000:\
+ :pt:\
+ :rc=\E8:\
+ :rf=/usr/share/tabset/vt100:\
+ :rs=\Ec\E>\E[?3l\E[?4l\E[?5l\E[?7h\E[?8h:\
+ :sc=\E7:\
+ :se=\E[27m:\
+ :sf=\ED:\
+ :so=\E[7m:\
+ :sr=\EM:\
+ :st=\EH:\
+ :ue=\E[24m:\
+ :up=\E[A:\
+ :us=\E[4m:\
+ :vt#3:\
+ :xn:
+
+#---------------------------- E O F -------------------------------------------
diff --git a/usr.sbin/pcvt/Misc/Etc/Terminfo b/usr.sbin/pcvt/Misc/Etc/Terminfo
new file mode 100644
index 0000000..072f885
--- /dev/null
+++ b/usr.sbin/pcvt/Misc/Etc/Terminfo
@@ -0,0 +1,41 @@
+pcvt25h|pcvt vt220 emulator video driver 80 cols,
+ lines#25, cols#80,
+ am, bel=^G, blink=^[[5m, bold=^[[1m, clear=^[[H^[[J,
+ cr=^M, csr=^[[%i%p1%d;%p2%dr, cub1=^H, cub=^[[%p1%dD, cud1=^J,
+ cud=^[[%p1%dB, cuf1=^[[C, cuf=^[[%p1%dC, cup=^[[%i%p1%d;%p2%dH,
+ cuu1=^[[A, cuu=^[[%p1%dA, dch1=^[[P, dch=^[[%p1%dP, dl1=^[[M,
+ dl=^[[%p1%dM, ed=^[[J, el=^[[K, home=^[[H, ht=^I, hts=^[H,
+ ich1=^[[@, ich=^[[%p1%d@, il1=^[[L, il=^[[%p1%dL, ind=^[D,
+ indn=^[%p1%dD, is1=^[>^[[?3l^[[?4l^[[?5l^[[?7h^[[?8h^[[1;25r^[[25;1H,
+ it#8, kbs=^?, kcub1=^[OD, kcud1=^[OB, kcuf1=^[OC, kcuu1=^[OA,
+ kdch1=^[[3~, kf1=^[[17~, kf2=^[[18~, kf3=^[[19~, kf4=^[[20~,
+ kf5=^[[21~, kf6=^[[23~, kf7=^[[24~, kf8=^[[25~, khome=^[[1~,
+ kich1=^[[2~, kll=^[[4~, km, knp=^[[6~, kpp=^[[5~,
+ mir, msgr, nel=^M^J, pb#16000000, rc=^[8, rev=^[[7m,
+ rf=/usr/share/tabset/vt100, ri=^[M, rin=^[%p1%dM, rmacs=^O,
+ rmir=^[[4l, rmkx=^[[?1l^[>, rmso=^[[27m, rmul=^[[24m,
+ rs1=^[c^[>^[[?3l^[[?4l^[[?5l^[[?7h^[[?8h, sc=^[7, sgr0=^[[m,
+ smacs=^N, smir=^[[4h, smkx=^[[?1h^[=, smso=^[[7m, smul=^[[4m,
+ tbc=^[[3g, vt#3, xenl, colors#8, pairs#64, setf=\E[%p1%{30}%+%dm,
+ setb=\E[%p1%{40}%+%dm,
+
+pcvt25hw|pcvt vt220 emulator video driver 132 cols,
+ lines#25, cols#132,
+ am, bel=^G, blink=^[[5m, bold=^[[1m, clear=^[[H^[[J,
+ cr=^M, csr=^[[%i%p1%d;%p2%dr, cub1=^H, cub=^[[%p1%dD, cud1=^J,
+ cud=^[[%p1%dB, cuf1=^[[C, cuf=^[[%p1%dC, cup=^[[%i%p1%d;%p2%dH,
+ cuu1=^[[A, cuu=^[[%p1%dA, dch1=^[[P, dch=^[[%p1%dP, dl1=^[[M,
+ dl=^[[%p1%dM, ed=^[[J, el=^[[K, home=^[[H, ht=^I, hts=^[H,
+ ich1=^[[@, ich=^[[%p1%d@, il1=^[[L, il=^[[%p1%dL, ind=^[D,
+ indn=^[%p1%dD, is1=^[>^[[?3l^[[?4l^[[?5l^[[?7h^[[?8h^[[1;25r^[[25;1H,
+ it#8, kbs=^?, kcub1=^[OD, kcud1=^[OB, kcuf1=^[OC, kcuu1=^[OA,
+ kdch1=^[[3~, kf1=^[[17~, kf2=^[[18~, kf3=^[[19~, kf4=^[[20~,
+ kf5=^[[21~, kf6=^[[23~, kf7=^[[24~, kf8=^[[25~, khome=^[[1~,
+ kich1=^[[2~, kll=^[[4~, km, knp=^[[6~, kpp=^[[5~,
+ mir, msgr, nel=^M^J, pb#16000000, rc=^[8, rev=^[[7m,
+ rf=/usr/share/tabset/vt100, ri=^[M, rin=^[%p1%dM, rmacs=^O,
+ rmir=^[[4l, rmkx=^[[?1l^[>, rmso=^[[27m, rmul=^[[24m,
+ rs1=^[c^[>^[[?3l^[[?4l^[[?5l^[[?7h^[[?8h, sc=^[7, sgr0=^[[m,
+ smacs=^N, smir=^[[4h, smkx=^[[?1h^[=, smso=^[[7m, smul=^[[4m,
+ tbc=^[[3g, vt#3, xenl, colors#8, pairs#64, setf=\E[%p1%{30}%+%dm,
+ setb=\E[%p1%{40}%+%dm,
diff --git a/usr.sbin/pcvt/Misc/Etc/pcvt.el b/usr.sbin/pcvt/Misc/Etc/pcvt.el
new file mode 100644
index 0000000..bd6484f
--- /dev/null
+++ b/usr.sbin/pcvt/Misc/Etc/pcvt.el
@@ -0,0 +1,19 @@
+;; pcvt25.el, by J"org Wunsch <joerg_wunsch@uriah.sax.de>
+;;
+;; pcvt is a good VT emulator
+
+(load "term/vt220")
+
+;; ...but i don't like `find' and `select' on `home' and `end' keys
+(global-set-key [find] 'beginning-of-line)
+(global-set-key [select] 'end-of-line)
+
+;; and the f1 thru f8 keys are designated as f6 thru f13
+(define-key function-key-map "\e[17~" [f1])
+(define-key function-key-map "\e[18~" [f2])
+(define-key function-key-map "\e[19~" [f3])
+(define-key function-key-map "\e[20~" [f4])
+(define-key function-key-map "\e[21~" [f5])
+(define-key function-key-map "\e[23~" [f6])
+(define-key function-key-map "\e[24~" [f7])
+(define-key function-key-map "\e[25~" [f8])
diff --git a/usr.sbin/pcvt/Misc/Etc/rc.local b/usr.sbin/pcvt/Misc/Etc/rc.local
new file mode 100644
index 0000000..32d89c2
--- /dev/null
+++ b/usr.sbin/pcvt/Misc/Etc/rc.local
@@ -0,0 +1,264 @@
+#---------------------------------------------------------------------------
+#
+# sample rc.local
+# ---------------
+#
+# last edit-date: [Sun Feb 19 19:20:42 1995]
+#
+#---------------------------------------------------------------------------
+#
+# NOTE:
+# assumptions: - 8 screens configured
+# - /dev/ttyv0 ... /dev/ttyv7 exist
+#
+#---------------------------------------------------------------------------
+
+# start xdm on screen 8
+#xdm_start=YES
+xdm_start=NO
+
+# setup german keyboard layout
+#set_keybd=YES
+set_keybd=NO
+
+# setup keyboard delay & rate
+set_keydr=YES
+# very fast settings
+set_keydr_rate=0
+set_keydr_delay=0
+#set_keydr=NO
+
+# constuct a new /etc/motd file
+construct_motd=NO
+#construct_motd=YES
+
+# setup cursor start and end scanline
+set_cursor=YES
+#set_cursor=NO
+# block cursor
+set_cur_start=0
+set_cur_end=15
+
+#-----------------------------------------------------
+# construct /etc/motd file
+#-----------------------------------------------------
+
+if [ X${construct_motd} = X"YES" ]
+then
+ if [ -f /netbsd ]
+ then
+ if [ ! -f /etc/motd ]; then
+ install -c -o root -g wheel -m 664 /dev/null /etc/motd
+ fi
+ T=/tmp/_motd
+ rm -f $T
+ sysctl -n kern.version | sed 1q > $T
+ echo "" >> $T
+ sed '1,/^$/d' < /etc/motd >> $T
+ cmp -s $T /etc/motd || cp $T /etc/motd
+ rm -f $T
+ echo 'runtime link editor directory cache'
+ ldconfig
+ else
+ T=/tmp/_motd
+ rm -f $T
+ uname -a > $T
+ echo "" >> $T
+ sed '1,/^$/d' < /etc/motd >> $T
+ cp $T /etc/motd
+ chmod 644 /etc/motd
+ rm -f $T
+
+ fi
+fi
+
+#-----------------------------------------------------
+# local daemons
+#-----------------------------------------------------
+
+echo -n 'starting local daemons:'
+
+# Kerberos runs ONLY on the Kerberos server machine
+if [ X${kerberos_server} = X"YES" ]; then
+ echo -n ' kerberos'; kerberos >> /var/log/kerberos.log &
+fi
+
+echo '.'
+
+#-----------------------------------------------------
+# check for correct driver and driver version matching
+#-----------------------------------------------------
+
+if [ -x /usr/sbin/ispcvt ]
+then
+ if /usr/sbin/ispcvt
+ then
+
+#--------------------------------------------------
+# loading fonts into vga
+#--------------------------------------------------
+ echo 'console driver type: pcvt'
+ if [ -x /usr/sbin/loadfont -a -x /usr/sbin/scon ]
+ then
+ adaptor=`/usr/sbin/scon -a`
+ if [ $adaptor = VGA ]
+ then
+ echo 'loading 25 lines base font into character set 0'
+ /usr/sbin/loadfont -c0 -f/usr/share/misc/pcvtfonts/vt220l.816
+ echo 'loading 25 lines extension font into character set 1'
+ /usr/sbin/loadfont -c1 -f/usr/share/misc/pcvtfonts/vt220h.816
+ echo 'loading 28 lines base font into character set 2'
+ /usr/sbin/loadfont -c2 -f/usr/share/misc/pcvtfonts/vt220l.814
+ echo 'loading 28 lines extension font into character set 3'
+ /usr/sbin/loadfont -c3 -f/usr/share/misc/pcvtfonts/vt220h.814
+ echo 'loading 40 lines base font into character set 4'
+ /usr/sbin/loadfont -c4 -f/usr/share/misc/pcvtfonts/vt220l.810
+ echo 'loading 40 lines extension font into character set 5'
+ /usr/sbin/loadfont -c5 -f/usr/share/misc/pcvtfonts/vt220h.810
+ echo 'loading 50 lines base font into character set 6'
+ /usr/sbin/loadfont -c6 -f/usr/share/misc/pcvtfonts/vt220l.808
+ echo 'loading 50 lines extension font into character set 7'
+ /usr/sbin/loadfont -c7 -f/usr/share/misc/pcvtfonts/vt220h.808
+ elif [ $adaptor = EGA ]
+ then
+ echo 'loading 25 lines base font into character set 0'
+ /usr/sbin/loadfont -c0 -f/usr/share/misc/pcvtfonts/vt220l.814
+ echo 'loading 25 lines extension font into character set 1'
+ /usr/sbin/loadfont -c1 -f/usr/share/misc/pcvtfonts/vt220h.814
+ echo 'loading 35 lines base font into character set 2'
+ /usr/sbin/loadfont -c2 -f/usr/share/misc/pcvtfonts/vt220l.810
+ echo 'loading 35 lines extension font into character set 3'
+ /usr/sbin/loadfont -c3 -f/usr/share/misc/pcvtfonts/vt220h.810
+
+# echo 'loading 43 lines base font into character set 2'
+# /usr/sbin/loadfont -c2 -f/usr/share/misc/pcvtfonts/vt220l.808
+# echo 'loading 43 lines extension font into character set 3'
+# /usr/sbin/loadfont -c3 -f/usr/share/misc/pcvtfonts/vt220h.808
+
+ fi
+ fi
+
+#--------------------------------------------------
+# setting screen sizes and emulation
+#--------------------------------------------------
+ if [ -x /usr/sbin/scon ]
+ then
+ if [ $adaptor = VGA ]
+ then
+ size=-s28
+ echo 'switching to 28 lines and VT/HP-emulation'
+ elif [ $adaptor = EGA ]
+ then
+ size=-s25
+ echo 'switching to 25 lines and VT/HP-emulation'
+ else
+ size=
+ echo 'switching to VT/HP-emulation'
+ fi
+
+# get monitor type (mono/color)
+
+ monitor=`/usr/sbin/scon -m`
+
+# for all screens do
+
+ for device in /dev/ttyv*
+ do
+
+# setup HP mode
+
+ /usr/sbin/scon -d$device $size -H
+
+# setup cursor size
+
+ if [ X${set_cursor} = X"YES" -a -x /usr/sbin/cursor ]
+ then
+ /usr/sbin/cursor -d$device -s$set_cur_start -e$set_cur_end
+ fi
+
+# if monochrome monitor, set color palette to use a higher intensity
+
+ if [ $monitor = MONO ]
+ then
+ if [ $adaptor = VGA ]
+ then
+ /usr/sbin/scon -d$device -p8,60,60,60
+ fi
+ fi
+ done
+
+# switch to screen 0
+
+ /usr/sbin/scon -c0
+
+# set screensaver timeout to one minute
+
+ /usr/sbin/scon -t360
+ fi
+
+#------------------------------------------------------
+# if desired, setup keyboard for german keyboard layout
+#------------------------------------------------------
+
+ if [ X${set_keybd} = X"YES" -a -x /usr/sbin/kcon ]
+ then
+ echo 'switching to german keyboard layout'
+ /usr/sbin/kcon -m de
+ fi
+
+#------------------------------------------------------
+# if desired, setup rate and delay keyboard values
+#------------------------------------------------------
+
+ if [ X${set_keydr} = X"YES" -a -x /usr/sbin/kcon ]
+ then
+ echo setting keyboard typematic rate = $set_keydr_rate and delay = $set_keydr_delay
+ /usr/sbin/kcon -r $set_keydr_rate -d $set_keydr_delay
+ fi
+
+#--------------------------------------------------
+# if desired, start xdm on screen 8
+#--------------------------------------------------
+
+ if [ X${xdm_start} = X"YES" -a -x /usr/X386/bin/xdm ]
+ then
+ /usr/sbin/scon -c 7
+ /usr/X386/bin/xdm
+ sleep 5
+ /usr/sbin/scon -c 0
+ fi
+
+#--------------------------------------------------
+# cp /etc/ttys corresponding to console driver
+#--------------------------------------------------
+
+ if [ -f /etc/ttys.pcvt ]
+ then
+ echo 'copying /etc/ttys.pcvt -> /etc/ttys'
+ cp /etc/ttys.pcvt /etc/ttys
+ fi
+
+ else
+ echo 'console driver type: not pcvt or pcvt utility/driver mismatch:'
+ echo '--------------------------------------------------------------'
+ /usr/sbin/ispcvt -v
+ echo '--------------------------------------------------------------'
+ if [ -f /etc/ttys.pccons ]
+ then
+ echo 'copying /etc/ttys.pccons -> /etc/ttys'
+ cp /etc/ttys.pccons /etc/ttys
+ fi
+ fi
+else
+ echo 'console driver type: not pcvt'
+ if [ -f /etc/ttys.pccons ]
+ then
+ echo 'copying /etc/ttys.pccons -> /etc/ttys'
+ cp /etc/ttys.pccons /etc/ttys
+ fi
+fi
+
+echo
+
+# EOF ----------------------------------------------------------------------
+
diff --git a/usr.sbin/pcvt/Misc/Etc/uemacs.tar.Z.uu b/usr.sbin/pcvt/Misc/Etc/uemacs.tar.Z.uu
new file mode 100644
index 0000000..25fba07
--- /dev/null
+++ b/usr.sbin/pcvt/Misc/Etc/uemacs.tar.Z.uu
@@ -0,0 +1,594 @@
+begin 666 uemacs.tar.Z
+M'YV09-[0:5&F39@Q<^2,`<"PH<.'$"-*G$BQHL6*(#+:V`@"0,:/,#I^S!C2
+MXT@0,F3,N%$C8XT:,&+`H#$CA@T:&6/$F%&#AD<8%X,*'4JT:,0Z<^B$D9,1
+M@)PW`HU.O(.F3!DV4K-JW<JUJ]>O6G>T&$NVK-FS:-.J7<M6K8(=;Q-,42J'
+M3ATX(,RD85,F[QNF3=*,>5JD29`A4T#,<(%##`O%+F0^7JPS3-RVF#.??1OW
+MCIPT=.B4<0-"3!X02*ZR:5.'#AH0@<>@"7,US1P0,7+@R,$B=PX8O7/DB!$\
+MAXRX"4`4.7.FC)RD9<C022K'!4H<R%'"V(XZ3!LQ=>2<`0$S`0HT;8"@08@&
+MC8OU<UR0*9."\XX$=\F$$4U&!VX<+<`@0PN[@=`">LB9\1<(;\S10@XOV(##
+M&B#<`=IK2$#1`A7.M9&&&V&P<9L)(+`!U1HAIK%&&7,@)QIT9%1X(0AHP"&#
+M#3+0\$*-,<"THXTS&/?C#=OE@,<+8]3!&AM(O:#':**-@4.0,H`05W[[1><?
+M#`#&,&"!![:1X((9ME`%%B`4=-!ME,'@(HO\R>@:"$DNB10(HLGA(8@B@H`"
+M"#WT0"->1,+P0@XXU7<?EOSY)T,.`<;P8$AA(L=&&$D11`9H+>@G&@@&#?;&
+ME7!XJB4(,-S0@DP/QF`@@O?AIR9"D.GTV'S@,??A&8_!\08;MJ&Q*P@NN&!?
+M`M%Q:JI_6TQ1!VE!U#&>3;C5H$,--EP;DF\R=!%7$9L.9(5S<S`;PPW>PC5'
+M&72`0,*F<XS11HQ&!,'$%$4HX!EH9;30!HMSA-$<""(TRRX=P]X%@A'/CH'P
+M&VZTL$09IS4Q6AT@S)$'=&T06ZP+78A@7Z`D"XI$$4Q``<(464Q!16&`EBSS
+MS#3/'!>4SM%X%5X:<ZR`JTG]U6^H3RF0P+KMOFN;O/3:BZ_18GQ(1@MTO-'"
+MBJ>5@4<928KF[T%/M5"E$4[$8+2KEXIQ50MF-/QPQ%@3G%$16"1!Q4@GIYR1
+MR`E$[<;455]-<9I;=STTV%;/L+`3QR509=IKM^V&PVE`+/AI(IRD^4E\^PVX
+MU7%KS75KAXO:@N)DSV"TXI"SP;;;E<,]>.:;U]ZYU%2#/KCHAG]M.NI.T&`T
+M3JV_/OG;E\M=^^:W_YU[\KR3[GO8P-=@=$O%2TZYY7'3OCSG4.,>>.B%2T]T
+MXHO;8+0-)8:AMNO:(]_]]YHW_SGTY7M]?@LXD7V#T3=HW_N,MSW982XC<)!#
+M&>PPDGC)@30$"Y_SQK>[_)4N;"TA&W82@`,!1@YVW)M=1MR@M79]Q($0Y)NK
+M2(B'@5CH;V^X@]&JP@8X&*V#"IQ#&I[4@A<&1(8)"`,9IM:&-\R'8`HD0^?*
+M<(8/N6%7+7B#&=BVES(8K4H*DL,=EC(UV2SE('DRVACXLA1_`4Q@_0(6"8V&
+M-'?!BVD@H((4JI"O$(R&#&*Z&1Y`H[,:*J!*05/@]$:%+`L.L@4V2(#1YL,7
+MK_DPAF(DHQS,.(>`-:<%:K2B'?^6Q_N<3S&`RDA`W'""=H7(1'?`4U761:<W
+MM,$@?[L-R4!PA"(XH0A2L!<(H%`%*4#A"?B*60\4,(.C54V0YS/:OO1WQH$5
+M#`1+*$(64!.$Q#CA"0NK@A.&0(4D/,$)(`A!"$`0,@5L$H\*B$L"%UBY.]$0
+M+W!`HP)P$L@+$E)M373#$]UPABA.,9-L+,-29-,"!=J!7'T100]\H$(0)%$.
+M8;A#IVP#ATOEX8I^T2(76^!%B#K,.>:\8R<3P,)VO1,$\6R.`EI2ST%"C8E.
+MA*(4,?DA*SHNHUN40Q=GX]$P'DV@"D$#V_Z2TQ@IE*%G<VAT("I1>%4T#!<]
+MIYAV4+-9&B8)X&R"+:N0S6UV\YO0E&9B2-2$)$QA",*465S::)`/Y06$I(E;
+MZUK$OI8F\Z=)>^.\%N:T?/5-?+K+FB'WYRJRF2T!(<CB&/J"-O=]\'BQ2Q[M
+M\J:RCQ@A"4PHPMXD>#_RC4Y_B&M!`,G6N,3^9;$H\2#\X"K9C%SA"5(@PD>:
+M\`0KO(`(*+/?\SS;N_UU,'5&,ZU"^L(ZQZX6LB$\(&IXB::/3`$*14!K!/\Z
+MP<`2[K/V?-#BA(=8Q?:%>,8EH/Q$>`6L$N$)5_C($)I`A,1,UW.[K2!V#RF3
+MQ5FONZ?M"_;"&[_(S@]?09#"$)"0$2D4`0I,.$P1=$M!P<Z7L(5U@OKP.UP0
+ML"][K)T?;,PJW8RLM[V;I6YGY=O;T'II<?^C,&H#B&'D&E!N!CZ"-\'I8?:Z
+ME\'6U1JG9G4;#0;7NR#H8(L+V%KEU.UN'RG,81+#MS$"E9*63&--`YK7I>U5
+MCG0,*2??4E40O#:V7>7FC!-3U3*3+"[@V4N,[O"7J6'M-O]R0QT4$$"[(H[*
+M;K1RT^[E5_@V&*4&;:>#V*S3Q1VVL0/L;W*5]V79@NH-!P4!#\:"X^25M(=M
+M7ESC'L??#(O0R[!U=!$C/1:DBCB^IV$DN_JU3CL(&M.%!FX"BIMH3RNWT:*\
+M2D8FW8)*QTW57KLTH>D5O.&I5KS^_32N0<#(C)3:UX,;`Z;Z-6R.A@$.H$G1
+MD^Q[O6,K^L6T6[:TL:T4-N@!VJ>1]KI@/354YHQL$[YPIUU<Y&6C4B'31C>=
+MILWN%MP%#N]V0HI9/&\B:WC9_\;WNO0=/=":CGT^YJ"W;:T\NMDM(U3`)FR"
+M@-4F2Q+*:*3I&O&:YWA=>8YU%"F7N_P$#;$L,8<90A&F,%80#.$)33",$V1+
+MM^BFM:H*Z*"=185GI9E\ST\[]9_G0$/7C<&5L"3VH2=.;PUGZ$P9D=??,JGO
+M-+2`Z:K1-$:'/%[E7KVY&0$[&]CP7L`F3XA$-&)?9$WKQQK\TT$@@J-G^P3<
+MAMC/U@7VT.2^76.3/=D'Q&UF7\9WO[>]NOA[L(G[YX3[[K?651>A888@!8UG
+M!`I!J.7?W<Y;Z8'H7UV$>AC^EK[U4?WNRNUYA^G4!M8_?L0.[IU>^()B`+Z^
+M[!7'@L\_LON^W![5URWQPQ>W02$7'/BTLSB2,YZ1JSK!XT_^5R5##E"2&QV.
+M6$[YEJG:Y?+N'+T;=T+H"V/+NYGY_2530`XR=DQ[%EVO2.\SZ0<G;*G%T-!)
+M=7B+1CNWA`5()B,PE$KZUFJO]DBI1%IC]WR(ISQ08&!6\!$.>'Q_)G@]Y'\/
+MZ`2J,VN_-X&THW@?D21,X4/ZQH$"4163Y(`]5FP)`%Z8!WO*8X(9T8((J$2<
+MA7QS4%&<@H(*Y`8NY('<E@"79W?0EQ'/Q007MX/_IV],]QENL`8=F("MEP#R
+M5H-+.`5((`58M009D8'Z=@9/(5$9Z#^^)X#@IAQ.D&!2('IDV(-_UG#9!7%.
+MT'PC.(!S<V08IW'6AWUEI'U1)G(VU4;?=W)9)E4K5S,K4P0!-F`O8&`(IF`_
+M!W]F]C,A,71%XWWXQU=\1H?6M2X#)5195%0`F`"(IH0D^(B12&`XQ452"%0$
+M95`()78WQ8:M!6`"1F!B<!!K4%3ZID!/M5A?1P=4.!YTMX=MR(L#!@(F0(R7
+MLECZ%@=UX!QY4%!E4(S]0AW#0C;<18.L.(!3T(N^Q`1H=0<O8(W8J&]H\"P#
+M<8I<=(1)>%PVF#F4"(E(EA!CD!=WP(-*9UWO2(0M\(MC$(SS"&^NIXOSDX]!
+M<#?\6!K`R'"#%5K`,W#,6&3T\Q$4*7G+%W'.QX6M:&1/2'T;UW&1E'W-)&4C
+MAXB?&'Y:AD[DYXAE=58V9V.7B(DZ^3-`4W\NY8EZ!HI)!WC)<P8"834&00>R
+M`45F,!JH95@!*(%\2$M/8))#P#9.F5"B6)1':8BXR&DB.95'4)6>5R(U-7J0
+M%S=MM(P,^6GX<H!VL!1IX#Y\H6_@809-.4F^HD-O4W@SF)$:YH7HAU(-`AJQ
+M8Y<P%3&?1#:6!Y@BY#(!=H"?I($YYCR+*6$+*95MZ(9[!RJ(@Y:XEWSF\YEJ
+MF``$%Y:;*7L?,9D=J7QA@X=ZV):QYX=Q!(@<=WTI.8@K:8CW%Y0PR8@S23,@
+M$&,S=I,@II/(&7\GQHF$Y))!62^A&)#)@S1?(P<4`I6JZ)C*]98G81#6"9K(
+MIS5>Q$^L]@8?,A"K1T1+<9V,$X&H6618,&!'@$!$:`+>N0;ZM@9[X3H*U$00
+MLS@A6'?VN(30A%E,\!']&3N4F3Q/!P?9F*#_"8Z&IYE%=G-0,$T9`:&DH6_J
+MUB\:BDDQ%'"-*9L$PP3H)6#5I%D:NJ!QTZ':Z)\1\V\!%V_:23!5``45.`0I
+MJE0PRJ(D-IJ_TWNF6:/>LY$^FGM`^IK,=T-$VH<E:9LHF0!.IIO;=TG=YYQ'
+M%T<H%Y.=E)Q>FIQQ,07L`@(*$P:M\08WV7?Y$IQ?VJ9`1P,P8$Q"\Y-M5`(*
+M00=M@!<FH$.D00)C8`:GEU`NP#<AD`9F`(UE8(T@8*<.DZ>H8C2(I4!V\4#!
+M=4>&BF>,BJ=ZZB$QXJ>`ZAU]80)PMZAWZJBN<E^%>J@FL"![FJBDVJAX(0)C
+MD#FM&@>OJJD$@P9\$T1#Y"^$)ZM%-!^$:JEFP*4*@(@*)$1H<"*BZ7!A`Z?I
+MQ*9NVJ9QP01O($0@$`8@0$*IE%)6)*W3ZJ4_0P-RBDQWAEB&"HWK8JLFH!>L
+MQZW>Z@)P)`*[NDQ#LY($LP5#L'K;*A!Y(34@<$M7`'JUY`(?5DZ(=17K`JEV
+M2$6\!Z]H)*_S4JE_<ZF,V(CA&JX8RYD@\`1&\(A4<*./`9E2<#=*AA@?DY,U
+M@[$9ZZ8*T+#[HSYP`7>^>D1T<QA4H``T>P8F\HNN$ZQ]<;/<Q!E8"GY;"A<[
+MX`-*JQQ$8#=$\)!%8+#L92#92@9J@!3MXAI]D2SM8BK9RGI<NZU*HC9R<"Q4
+M4!5I$B[,EB4@8!NJU!=>ZQK[\;;_RGM;=!N7DA1I"QIQ`C'6<;;?BBQJ*V??
+MD3-N>[5ZRZ\?,A@%`27#0KADJTISVQS341I*`G!K5A4D=%!E>Q]:6[=]X;9<
+MRQ_&@K0)8`1_D92W(45T&[9>FYY[VRZ0FS-Z<14QLA1EH`/'D@#@PBE$D"7,
+M<@71`0+1,AXQ@1LPH`,P8"T[@1O"D2X)P+OAT@+C\AS,`@/I$A=9\`88PW3<
+MRP:W*R)H"@>M0;=TP&;9*@:0UA>9=!MS^[E5@Q>LFP?<V[G($BKQH1"@FS%H
+M*K?M0A6CL4`Y0[\8$[9\I+6G<0=KQW:42RJF0@8A<"R,PB*Z&RLMD#%A$&E"
+M""6$R9>Q@QP7'#`:'!Y#V"ZD&%3TEXP@K%1MI#5@]&A'1!55E#&T*"S\E%$$
+MT[L#\;NBP2SE!!<I4:[VEP#S1YWWV1F?P4Q5FE#Z6A4'.2SI>I=YV;:WX596
+MD`1%D%Y`B["IBJC6&"+0^(NLYZ=`BQOLXR;16Z>TA5M3$`16H%ED+'>06J<)
+MYC+E^(PD<,*R,<=C&L='9`)BW*GR0GB`[`;^ND$)8*\@YTQ;8*UOH)\WG$7*
+M,;T\G+L@L`5=@+!]DYC[U$\S57R0*ERHI<>F2%3S:`+2UBXBH,,M4,G^D3D%
+MLZM=7*ONDA3[<2<P&;T)L(GL4IUK`*D_58I#I5&%)@*:G)T&B9`ZQ5$\!48@
+MI<OZN78O^L&Z_"'K4A?'F(SN@C#_$LK$"JF*3(AHE*^.#,GC(<FL7+WE<LF9
+M+,M`1LK#C(JH/+>K/+WJ_,KY*LOI2LMYK!1V<1NY'+V\/!!'K,OP+(_%?,RN
+MDLQ%Q<Q?]%%E"\W[.<T0`\S6[!P#X8TW+*I#Y"[1S':'E;`56ZS1BT\QQ4_^
+MY+`V):4?)\Y6.F5IW,>#?$0EL,8SY\9^Y7VDO*AU3`5WC`04NRG%.G_B.1OD
+MV0*^<IXMD)Z^;*SJTL?VNJR/W*S9E1(*`!98G=5:O=5<W=5>'1$\]AXU]-5D
+M710?L1'L8Q(@(1(C41*:(P,"\A(N`1,R01,VT1*X(1,R$`,_4=9^O158NQ1-
+M\111\14`?!5_G=B*O=B,O5#1&QN$L7F)01FNDAHUE#&,.QIS$+TH``,S\`*Y
+M\0*[41]&TP07#"H"Y0:W435DRDJ?RP,S-P0^``)QDT"5PQ2LC13#LGHMF#-8
+M8S1>$`09\2^KM]IHBA1]\;E/1X1/P79Q\[YH&S#_@B=I,-V80K?"_=L*X`47
+MN"!;``7C001NT`7)L3*BLG;,%D-N\-@MP`,,PA0\@`0X5P0^D`!"D)C#PKJ@
+M[`5:\-Z7#-X@4`5P0-Y,>-YL=Q?L/=L+`ML[5]]%P'KZ747II!D47N$6;A8*
+MX-CF$0,I`!NUA57S204G8W.]-`6PE4Y>\+$+8\J%UE'.+`<)8-HJGA&H2\QK
+MUF;1.S%Y$$^W*P=GV")>(`09(03`B(HN#M$QW@)"_A%$?I"H.&S1B[O92CD'
+M%<'!3>-/080,<JB9E.3S^1%'<)39:I9KY`6:I1P0SN4U%;W;36-.4$)D7@;L
+M36-8#C'MPKKQ!%%F>&UHL-V5!06!QKUXN^:F75ESD^8H]45[#@=]?N&._NAL
+MD>'U;1XRT.&*5P3=Y`3S22)8A2\E"^+IQ&O12P17L6J`QDZ"3B?-#-';+5L+
+M?NDO4]^DWDA]45*J_M!AY`5HM>"=CDM44-])H-H8/>8_>!!69-JBG@"S;NH,
+MF.K#I@"F302C7NJ?8NO/[@5+$+U#8"*LA`*"U^&L?4=;'N<*`.GF?NYC(>F<
+M+0-AT.&T96`@`.L@#HT@T.N?KNFA;F`A*P5.,-L9$>S7;$K;6@8*O.8)P`-4
+M$`1"X.]!0`9Q.3G)C::VKA1B0']O8$->H`3E7>_"7A=CSJV9]+4Q(C50DN1>
+MD%X9L>R?DN<"`U&,OMU/L/%/`'"D@0(771<=#E`GO_$JWQ<KJC;G:Q6D<9]^
+MTD:Z?</(7NR+]8MRX`,=#KLH.`=_`>TM<`4;/P07?QHKRMH?;;EX"5)>@`5>
+M$/,?T?.E<2E5&.?N^Q3/$B-1/_7H'O>.KN[F,0,=[HQ(`.K;/05R4<.Q6&AF
+M\!0=\_9,L9>&Z;?;+04)(`4"_!Q]L=.![TIT$AY27_B%^3;&$O9\7^.HN+@*
+M%&?E1L.EN-U8H/B,S[FLY/F-2X1@3,H*P`/V\NLKT_>EB,.?:^MO,`8HF)7C
+M_KEY:\+(."PHD`1"4.B[[@9LD`?U`?M,(/N*+Z:U+\F^CREWKOOAP?NL._UZ
+MJ]'C,?S%WP)0</S)7Q]R7_X43O<H0`,=3HD)-@1Z;]I2\!&,SXW9BM[6K!00
+MO[J'JA?/D;4['ATMP*UP/SGQ&M;%<B,#1B,CT('_-S4$8/#C)]8!S<4(@2(;
+M4MBPL!!S(K89"]/F!>+?<-I&TZ@O8,#7P([D0!Z(@$%`M=V!G#$"=5<"\`)?
+M3MI`/#80O3Y"!"0<!PQ-00D32)C.4W!!4"#0V-&M'$('HA>&RE!!$+60B.6&
+M,.2,3?D!8&Y,:2M@H;=8U\5[&YN-SADRI<*-0!^]8X(?XAJ5._-'!B.=AD,!
+M-:##Z2@H8#?L11+0`O..1,B1(.`$G@LP>7]F(@'<*.B"HH+)LS-M0R`!["MR
+MHVU$8*;9>"#`"U`!1$@%((IJVTM]X<CEB19AVIA``G`W"L<0ZA32YP4L(2;L
+M4#SJ@X4]+U`%\`,<`#B9,!16-"\0!1)`%*@#`J'6P;D=F`<>@]237.V""3:W
+M5C(?X`Q4*0U;BP@YA^%UGOI7"1F#93`9;H8S:`/67Q&0,=^DYH@XS;($#!0(
+M$`)5P`@8`5R23I)>/%EZ2Z&^M1'#(`7$T-S:8%K.\+T-3H@%HE<9P`+C:6`0
+M/:A'^::>`A!NQ`FL6`CTIK5(@]IH)40H#`JZY-<IRH"[&E[7[7/A+FT%]%;@
+M:*!;\_!O*`#E1L(X&.'K8(?/#41`P%6@F,"!PH;:$)=0,?,%YV18!9P-MT%M
+MC`8%4$3TE@)9+$0H^6&P@Q(C%H3@(0/&0AGJQ+*`_FZ`&FPY66#>T4'90ENN
+M&+[;;BC/[*&`*Y`&`$Z'TU#LS>HU**T'4_X3:ZN&'M'KY:7MAJ&RP.I9`\EL
+M)O8%*98SSF$]+%M'((!!E+53"^E6`LE]T>'ZX;"IF-^8PJ@9%J*AA5`QW342
+M.%R,6T]C+A^"DZ,W'KRAL6-ZLVUNF31]LMM8ST6<6W?$6(R$2J?LJ%WH:A<H
+M8`0FPBO0X1;$5&Q;F%$SFC;.V+:(4/]"6UCQ0)'%ZJ``1H+=BW'KBVY=1-;V
+MN7IA&D@B)2+W[0<%!;ORP%<$C1+I(/D)S>@%LD`*R(D[<2>B/QS@[M148O!8
+M'8L//J090_J:0`)H>#&BC+F5U<C>FL!(P(X@@&>]`9\%PZS(*+2.9D\[^L,Z
+M\/5@G,GSCBG/,H;'G@7&@)8"J"VX)'K-OQ!X&U#`^M(B28Q%/,7]X((D%VFX
+M>95+$I(+!7`%<@D4B%Y4(#RH-@9!&H9-A7!Y?L),58VD)!@&7L$C(7.@/ERQ
+M+!:]@L#:B2&WP=40O&%1?`@@]VH7")(WLAY@\\'6BYHJD6>*0_8CDA<0(]F"
+M0"MO<<]U#%JH`(06%7@!AD'&!,(A8-2:PVU85JEDI\$N:20$G\XK24^W@=!0
+M"!2P+F2A7O16Y"\YZD3TEP/N'B3J1<1KYPRG`]/^-(M-FP+IQ$CJP5.("E\`
+M)MPWJ>\V&#(8>08,&6X4@#6,14"[T),$`B'C.P-UX%(4OOV0)X;>?EA*-TQ!
+M/H=LI4"F',(X**W1!OY%I;1*OM9I@!B1<-6%D9&@`C:E;&`1GC)5SBZFD/W0
+MUCI9+)LB4HI*D#(2O(!]XV3Y3<V1D.2P!<B)>IF53&&,8(K;X)2XW?7+E!F!
+M!/`N1-?ED@,7.`FO,!8./+TH*9&AF"R#Z$\F=+AOT@+.BH&Q)0L#MA@&*I#I
+MC@"*PP)&('J)J7:Q>]C.TV$2M0?:10P>0/'JV[K$$^XC8WQ#O"CQ;MU+G(3`
+M,.@]1(IW&X(&'*"$+:`5GJ[4-;=8WJ(K@*?Q?<$Y8($J&Z+0\TSBP9J1OAX0
+MO:#`Y5-0Q.@OM(NQP&PHBD49="1$;!4N.?`8.DHK@4<L`)AMO!:I0[8-U`.6
+M9.IOY`S"1_5TG;;C7EINV,P!.UE3AF9'J6*.TCH-+PUE+:^E^<N6'`XV5('F
+MEP00C&8Q/^?E"L!):+=Z3@/61"^)P2"<AG\(1M)`I)E;$`.U6,7HEMDT8D8P
+MD6P'!GFFT^"]9(A0RTL<+,]5KE;Y^$"55G0.CV%!4,#7(#9+Y@^R*&/N;@Y#
+M+0<J,Q\6D`%R`0AE+;2%#O^7$7(KY^L-O$`L$/.8Y.H9&*S-VGD@TA>G!,_D
+M\W$<S`%%K[#W(#TG><(3XRO0W0D'1/IB0`(PG:>$06BM%.2!-AL'M`()``J,
+MLQ]"&D1G`I*9)D\+!,]QIC!Z&^\\GDXS&6;+2F=SOHD<>0(><=Y]3:V9)F7+
+M-%P9G*<(V))S:2L?G*(<&)43"MV!Y(#LW,!L"WLH[P/63!%HA%@;#W"?:H\3
+M*L\I@`:H$(5(G[1S%#H!V!AI,A#QY(0/L@G$Q@QT%WJF)9Q_'_,_!=`':A!S
+MR&OX7`Y$Z#7-Z!GWLJ7=DYI4TVI>PVRX#:4`G!1N(+&$CD1M%:AZG$`!B$KA
+M0^RV]%8G.)C:&!9<*SI$P";0-?_F<V")OY!L4KDL<;ON',1SG$)`+F#`"LC:
+M>-ON]*$1D`=PGMDF*)`;"$!<`Z'9W8G52/K:X118HA=4XL&YU5@:?:A92@H]
+M<X"ZSH$1J,9=^N2B82_;F3T6:CF<BD497JO1<08Y96<R@RA[G&*;XE_^A=/@
+M5K05[>2@9#!;JK\UF7?FG1'H/-ZQ:4V!)4`^TZ41`%C%9P<H%6Q5##\>P3.C
+MC(N(Y@7!![H"E;$P#V?+;;FMSX44Z@`8VR*G@;4MQM@U0[D5Z'(!]6$4FCZ!
+M$B-:9"=]HW\4<*8)B,*GQ@-HN`U:]#8P02@1'XR&$Q"CJ82,[D.V\P]#*>GB
+MA$D@`0"\8>=+3>/I+&%FU"P^A[]P`O"6;L1\G!!X7E+6HZU:)&LS""OBFW(P
+M*X;%M)@<0Z1/\PSVB+OGQN9="L4E,$=-"L;$0'TD*27EA'QO"F2P2%@1M1P9
+M96WPXI>-0JMW!0*D/'6HP12WH:F(2OK4:)/L"RVRC;(N8-H>J9[RK*C\@D&4
+MK]P9#_%H1G5?8&N/M`O(]@9.%IP,>SS@+\;3QI<'7$-^:PTZY(A01-2IY5;4
+MAW`U`4,,\`725]\87STIC4@U#2C5Y'8,\6GYRY;-D'@-`9E#<^;=]VPYN&0Z
+M:KJ5T3)>1A,XER&@[T&XE&DEHP[LI%L7SSGLQAO6,T1#QYB2[$)"DCX@$#R;
+M8E\X+XFAK>+5.9`H*Y<LS:BD;P2<KKV0)\RH:V![9^`U`-:ZU5B#)$01$V$O
+M$,Z%I2#P;*E@93UO<;%4D@71]G(&3;6II$_:.8L?).Y.JV0KC;01;24S,P0T
+M8P0*T"9)`$T(@2E`!&B`"Z@2$&/\<<)`6`1D*FP0#)%MR6Q0J_KHLJ5/#"O3
+M1`B8%Q`74&7+S<DY0W%KFK9L)P0`EK:*&TVTE5Q)VZ,Y3)MPBW<L8C!(U;Z@
+MK81E)7&CJB>6],QL5P7<@.<8<W$C\MU5\1IU2-\/J(P.9+WR5]BEOMK>6_VN
+M%(/JM</BBET^'JBRK;!K+^D0I7H:GD6!_1L=[K,BP#U`Z@*LVBA(4F-7S($^
+M8#2(0!\]#5/0-MRY0\52XRL9@*DQ(FYX#A%[8'WH<F6N%RY;,L?JB5W5I.RI
+M`E_%"2@`K#=>7^R^\8>/#^",`4.5!A"B,G6Q:J^,?JXLT@9<(`^X@K$#C!$C
+M@9(+@28=F&U[0,.V@$#5!R0-EH488&PI($K0-P=\@`+8`PRV=XC93-('"DF#
+MU58+$'.540U+[N"L(5F-==8./=DBBS)192&UL7YVOA2?.FMG#<>@=:OM:Y1*
+MOG8JX<)L2]M-=):E`975AK9<&OLZ2V0@/.#0_/'!9DL+\`.Z;$I-RL\5K)CL
+M\#(#E^*<+0B&B%AQ++K+EF5RXW`>;/)C@^RY1`%R@2ZTBY?89H8%UE!?\RB9
+MA+T4$&POGE)Y.CK%V%(,9%MHE*WPX5T6A+9-VVM5;>],^P2SS*G.;E:/1VRC
+M[0UKH9Z)Z&@.J:(NX]>S+;;G]L&FVZ*Q!V!6:'$#=3;.DHYYVR\SHPNJ=2-1
+MO^*&MPK7PJ2MA73HK^"N#.CB_G1)-&D9*.X(:+O5LUC<94/MLD56Y"F5244:
+M:*.SY0L'A>W@N:<@6HMIYN,J?R%ZU5>S22ZT[#;BLCLTB(+47^%N8H1I^%JP
+ME8GD#&]G$,T4&V@7@D+]43T>L"F:R)<-(JQGR^X'3NLE,:Y;]5^>R0W$TNK&
+M(C9@"VB'V/;.;BMYBP(B;(.8L#+1PM;6#.MBTTDZ86QH-^VJW;7+=MLN17@6
+M:0`/R*O3$1EB0!APNV3MK'$$M482V-I:X[MYC0;HM;D6$V9"3>`(>4TG``54
+M@7?7;F!C"AV!L-$!L'#8L$+CO;R85^V^`!5P<.6>"FB-GQ<$E$HF0/TFF>\"
+M7I?,64`+:6'&S)CR2EXRH(-P"V\A>D%O`B@3'$)/?`@PA@32DQH9#\M$-/C#
+MBR)ZHY=E6PVMX37$AME0&^;`8^B]WR$\\`I:TB&ZINT]''O!/Z`']<`>W`-\
+MD`]4E0WXAQ5``R`%G&H!+^$WS``W47N+;Y@PA9Z"19)2'E,K0@)KHVP!TS(4
+MWP00PO!?0"A?>W*83AN;^RDYE[TRG$_E-!@U,O![;2__?2LN1MLJ8-^[*QSP
+M!?LV$EBU#(L%W(#W[P4[MMRV)M*\+\CM?-Z++)H6&`(7D`S<.KZ@G6.FP7$-
+M*(QZ`GIM[_ME%/PR3>`O$(#Z%!1E>P&43?^6RO?;:LU`'L"+'6+<X,NI:H/1
+M0P(P`U@#DQB7VR#)9@`.L`%B8`[$"%=3%UPIVREN7D<E&`<\T2%V+QNHP:*W
+M\\8]%?`"TLGF_;P)X,VEDJ9TRW*([CH;?XOJOL@L&08,TPW[3^T4KF3@V1!I
+M5")I:*F]5/"M#IC`'5@;(]X.(<%#B`H#"#%>K+&X(M8!.V9$,(8U#,(IQ*',
+MK<DJ4W[U#PW"$1&XRJWL7L+/Z4H'QLVU#81UAI;3/A9/&LFGP`.X^`1L1+1%
+M9=$F2</%>&"%6L`;=MT4Q(F\`^4"4HF`AVL85,:Q#;$W++U>FVZ4J*X1Q!,!
+MIY0*H*E1)0)@VUGQ`3Q@"X#C(,!0LI7[DL`S]@\?*K+A!!X#++Y&+C`!+&-I
+MTHP7!SCQ`EM@"P0!;.Q,I274_5S2&.#XRQ<W3NH=C#S'R6H"QHMI/)!9'=(=
+M>72@G)ZZT)!J#)5>2!(_]ZWB#-:X.JP#UC,1DU+*$=93*#)U:$:HK_.!*7`<
+M-)&*07*6W$_`$*4D2KZ``!-`(<V1NW&,G$`:MR"<9YIP`UP88K#9QT#`CFR:
+MN!!E<8@8#?B5A*_-3PY^+.(Q#`'[BZ:(P`V(@$\`MU'.7_$7?.'8A%L&L:;4
+M9,(H&Z$LVD.4X^P55Y(Z((L142T^&'T!&)\`,>)B=_',U1.^&`0`8V$\`(LQ
+MS461\5@$#`$F(`7J\0A($PL9X'R=:IR5^'$"X(BK4AA'9H%+E:O%8P!@E;(R
+M!Y!56A6,1A9A(FS/,7+EY_`88%<'86T]PAS'8-IJ8-]E5\[&V[BC?6;6+)I!
+M,JK`86"8#.M>/N%\J1AAS5W*>#`7YB"@,D("/MX"Z[<-.&:C<0(\9EUX7+$P
+M&1N-)$`C%BJ>^`S#B[59T^0F+*CP#+N%4)>/>*]$>>.LD]&X;BL0O5TW"2M5
+M[=:%>)&WCCRM+M)`P!9E7;C$MS=$<2YW?*BDWK_PJ3<L(*!*HW0A(<;T3<AT
+MRQBCD@J<`-Y)VV`[-FPZW&%59!V>`&F8`MJD!0`3&O`8=F?H.L=O<:IV#&1T
+MA&\8:UM<3.*(J"_7L)315I[P$&_@/6C<RN$PV(",+C[QX=_)"*:S,IY%A?A>
+M,8)RO:W;>)X=!NRD7"XH`A:!@])QEY6T>`U%64B;B+UYJ))(6"8AV7$E.5^C
+M(9Z=-/@B);G03+&2'AJB^4(;>,ES8LJ:808A!M0`UZ`#^%D&6(=@9[[^WU*K
+M"MAJ/D2)OO1TYD.'1ELAUM6$6C"&6[$`&]D8=O4H/U&4..8NRS<Q,'G'OVDK
+M1QU;!HP8HM%TP%V>DE.*!.+J@TY^1+E^90Q$71!:&ZH$E;VO*MQ5UN-34R)8
+MWM/1@2ZCSAJ=P-[S2I6Z.5<.,&ETVI&)E_C*C8\,/DNR0/JF!VEUWCJ[PBZ#
+M3VR"!49#W!W5/L,F,]T*409N8R]L%TG@!$2:+ND&'H,"L4:W<8:RUB4+QDP$
+MM@)EYJ%+`D-4<AR9D"OI"Z'"AD7"W/FY<DAA'7?:*B$'4A2084_)>288U*$.
+M&&EB[3`RQX(0`0%[8-]&*(&-_]WD2-&H$D=32IN";A5G"9.)\X'FQ<KQP#<[
+MU@N8`A$P"1RJ^@RM$[6`G@-@>H989WX55RN6'.#72*($[^L4\`+T]6WDUV9)
+M#$`4'BAMULZ?7HD)H"B/9_!EJMUB'5"J'3(@M!72T!KV`FC`'"Z;'QL-;2Q>
+ML0U?B,=$UFJS5_0FL?VD06"+L)G,K8%X+*;X@I%&U1U;0<>0_(8*Y^IXJ*L%
+M(1GKLK/"K)TU'HC'T<N*B@!,\J+'3>:`77N;#>`!$0`7YK8V`26PBP;@;?S`
+M2@)WW[XVA!N8$3_X1P4>P^1N`2OY,3RDNU&H-??/;1`!S@GD5LLM#'L*RUW<
+M>AN31#T6P8]5`!MVPZZ;XXV1.G"*J\AM<-UM>`2@:-K=%WA`4M@4,1H-3+H$
+ML'E7AO^=1TG@!6"3^>"N,N)F>]VZ^V'S;H(!)^2`P(;3NDJ7%>\BL(.9MPS%
+M@M'K=2N`-QSO)%X+&A9LKPGV!=RM`,X39MN3-NP,\.LW:S0DE83L;"D`+I#8
+M\1V[+;1,M)):&X<5ZG0KKV\W]$Y7NU6V\`$^$."FP'Q:X"N#MB0&"'YV0``$
+MQ]#@!((+']&-)B!X/Z6._!N'66$;D,!1A0N``;84!82)#G?`#U4"'^$*8`1X
+M[Y19!?#%%\">1.`+D,LL$,._&?]6`;(;8H,N`YZ[=_<1$0';Z"E4C?<@`K0W
+M$&<8$7B&.^_P7<2E]Q&/#@:1B3MQ6D(?V4Y<^@QT"2PW;_!]P*UX0KD*GU.+
+M1Z_B37KY22L6X]\[=FRV!""^R?<2@,GK9(8/+_8]`DKKH>(F4L`2;G%`-Q[-
+M-)I^#8!<D-?Q_LVY<J<1!ZFV6_26\=E]1'SWQG@!>;;J"N\M/@5(]8C^?\SF
+M2D_Q22[$+?G0M&78>W@7[TO*>U#YG9#B9+R*4_+>W3->`*:VT9M\C0/QY(U-
+M=.&O".5C7([3\3;\PXG7$-G$;.>1#W%)7L1=.&^-X<K<E&-RJOL>5#D0IP)4
+MERG`<F$^S'OX2`,!Q;N$M_!KR%LM.`/'X.9\N6`=#W[%J&/TGN621H?TR1!1
+MS3EY&ICG;&>;0XPY#KV)%3@'XB7\@A]N"LY<TOD'!ROCO(8_\',NP=-Y!1?H
+M&9R!;_#<"LW-N*1YT96CGNOR.%*&^42]4][`/([O\VX^`OQY\5;HZ7P*-'2"
+MOL[/^4"7Z+8DMXK>9DZ\VODWJ>CPG%Z6X0:AT8GW->_H8(R7A_0G$LN]N5#[
+MYS4]"12G<3X$2MS,P>FE?'7'AUSNTVT.Y4.5&?0AODCR501)NA_W,N8%O>BR
+MJOX$7H,A2ZM4W*BGJ^+=U$WHS)GIL!N(9^U&HJ"4N<1FW^3;Q`I2'G@02NZN
+M\`^-$,6R'KV`*"MEJU:I<+I8M*V1S;V.[#CAXPC\F;_S4E[+`VE/+]YZO5CS
+M0%HXU$=Y[M;C\2X)&!@G($>RP.U<V`Y#K1^J<%[.Q_E$[^`,W`H\)*@^O:6Y
+MR[[L0#RS.PQCS=GUN6HCZ:"]:8WVTG[:_Z5J1^JO':[K[D-U6QTX-"(1(4"V
+M4X$.=\%7.@-_Z"X]HB=U=Q[-B341PNWQ#K4?DZS!W%*-*"_J,OQ*_U71WGZD
+M@&GW[G3@N)]TYU[=@8E#+^@0W:#;='"RR(&X4ZT_ITZ/QPC?3L1C^%=/Y&.]
+M>/]WH6%SY(@@!^[JO<$'<E2!!PJOFRCFPO.RKII)R<=!NQ-XN.TG>JW?.%75
+MG<#8RAFLJQ,[Y1THBB'\00Q86&7`!)CH=1,V>K#CZF9T?J;U]/[BKPF0E?&*
+M3P9@BXW^!%H#CB>C.IZDIZN749C78`SW\&L0)$8OO4:NJGKN;0-*.)",LR3?
+MS[]Y\6;R36`-*G?03@YES`#E(!N=AV9,TI#D7?Q9\I93X`W2^"I?O*>`@<-A
+MWBF?['@/#WIJ>!&8\B2^>!]F;H8J'RO&F&!O-9Y8T?Y>/>M@PB/MB:'#1_AN
+MHE6>`)"-7D*^!FST9]J0&VL&Z\-I@R]T\V).!4`Y@6?FQPH976\)S,'V0(('
+MXCK.,WWB:%S>T[HQL=Y&&FMP,"`NW=B`'6`#L8*MX\S_)-W8EP"C@:_[UJ]Z
+M7:_E@+@;:"&^'MAO=,_94U!\\9R8Q[Z;XP?5=L^QM+]4=<%>K@-+79;LW7>?
+M+@.QHJH[U<(J;?/`N4_K?0`N+/<X@DO"/'%.];@^3,.!^^KJI_VU<1].>P&6
+M!F@\'FBAMG>9SAY4I?LOWY3Q0J`B]VU82O$4T6OQ%,C"!^)SX2^,LR\^EZ8J
+MO%<7JC[73UN`U>^KNHY+QP6_UA_\B9_P_\7%Y^AZ0@D_?%OOON,2DT#W6[R[
+M5JP;9O.OT<=7``@_AXPI08$".HH*Z'!.8&HR@7#/PL;4`%P#:J`?=?FC_N7K
+MO9AG]-:J9_-@N13&"_R\3^"I/B>GL%4_!\X`1:YX4<_L+T"F/P0N;GGDYVT8
+ME7?(A&W"U'X>J'B_XL7:?::?E>_YF@9:<#_L(XQ^1/?QQ-%D$."+4RZ%.<#V
+MW;[B?PYI7>X3_I#?+G[NX<_[;.!HW@>V[O8M/T\)_)+_VRO5K[@%;$`7`!0Y
+M'^U1"/@L*6E8$4SV>\!`$`?9_QA61>V?_;9?]H.`>"_?`7IKI^DHO;Y/\.N.
+MW[-[.G_M@G_N4W[=#*-/YXMN_$2U70#^R._/E'^^9_Z5`_%+AP[!]S]#/JF/
+M<B?PEW0O#\2#/T.W[RP=MJ^,EPX"DOLX/^A"-O07?HP.NE'0\Y_V;G_Z=W/Y
+MO_SI_^I*_-P?D>?]_7WAWXYGT@%Q\)]RM^19??=>Z.<R*277QEVR!6P3Q!E(
+MU`4P?5>>$M;Z:7G-@9N7,JU!7\!@)M!,>Y*$1H;H>"M[7H0'`JX74Q[^-T[9
+M10)![)`"OG@KX)_G.)"`0(4)6!/I2G*.!]@7@(#X@HBWT6U6,$3Y<D>(?Z`=
+M$!CSS"!#H/\%GZ$`MHCCPX.E`?/!&\!L06])(''V!9@5T8NF5]4%.X9)(839
+M*!"CP0QXEH"`2P#?HX5L=#H.CU,>T1^<%1*H`FJ!T43T<@.P@12#&UC&'(%F
+MX`>H!5H!212:5]59`;9!>S86J080'S3WQV%/)PZ/=P9J@5-9])(#;'0U#FA6
+M6[42MMD(0.)A@7,@%!`"VDZJ2*#WQ`D-M)G;,YJ!`",`*,@)TH"0(.2DBH""
+MJQPI&)J9@IF@*[@*/H*>X!!03*@BKF`LV)_,@IC@@C`"[(*WH!^8"U9Y.P$E
+M*`M>@JX94S`"5'G%X`\("7J!,0"=-PK^@LS@*3@">('1H,T!"282JH@7Z`M:
+M@JV9-@@.=H,KX!VHBH"#XV`I&`PZ@^H@.J@%$@$DWKFPT34Y:X`[V`RB@IO@
+M9]<)?@%$`"@8`^``]R`PH@]J@ZJ@/\@*>H)$@"N8&Q2$!\E!6`OV@=(@0[@+
+MPC4083X(#.Z#PR!%Z`TRA%7>7I,12H3"(#2H$.*"`*$7&,14=?@@2>@,<H,G
+MH3$($(*#*L%(N!&:@QXA"&B587K6X#5D$-Z$F6`\F+L=@-63B1+_O&[\W_4W
+M;MQ72Z'4@,F$$JX>6_?O10W`@L"'\IU1KU_$%_O)*FQ`YM`'L`!;W+:3`X8[
+M)R`:8>MMA?*"5P@6NH"5SZ/Q-IR%!`,ULONMA56=6"C8D(4[8)P#%R)L98!:
+MR,DY@49@++$72CU^81.8GCR!42`K(:B5`5:@MK<5V@:%X1=(U,TEP,*VD=6M
+M-XXAP;`&S`&18?'6!F(K98PM4Q<$?@G`5K@B=(:P7AX(&A(>?&!F*`+8`6(`
+M:@@"$()@UR&X=B2"\][-810:#6@A#"`;5H(N(3^X%XX!,<!ON`R6@YE@0L@;
+MQH4R@'&(#2*'PJ`MJ!7&A3.`<T@.UF;"(#$X'<HJ-(!U&!P^@\-A#>`=`H7"
+M($RX',HJ-L!XF`UF@N?@=C@&W`#J(70(#^Z%9(!O.!?F?#_A>B@,]H/G(1E0
+M'-Z'-J%^Z`PJAZ4AP4`&-(<`(DN8'\J'J*!TV!]6APEB.+<@8H?.H';8'W:'
+M$2)`-R'2@B4A?2@>9H@^841('KZ$]&%Z""*VA",B*M@>]H?PX>Z''XJ(`B(J
+M*!3V?D4A;`'7\7ZY(2,H!21_.Y*6\Z?L@X+"J@#]@5-9A$NX_U5_/6(N)`8`
+MB;+?D&A#;8CO($E'%.J&-B)2B"2.?2+?W0>P/&-_PU,H*$2%J:&6N/.-!U10
+M5P?[$0QB`!T@`HPL2(`10`7P`8D<'W`"I`$GP'TH(1XD5$.^A#4D@@6B"+`&
+M%(<C"\,E)\8`=>)72`FZ"GOB7K@&-(>!8G0A)\H`A>):N,I5"8FB:[@&5(>-
+MXA`@)\X`D2(EJ#A4BF>BG]@=9HIR(@W0*59U1@!.`"INAVN`>$@JG@`UP*FX
+MRK4$J^)YN`:DAZ^B#2`K/G'L0ZW8)ZX!\.&K>`/LB@M#`.$KFH8XP)JX<#F*
+M)P`.0"P:`1W$L:@9Y@#*XG/!+.8`S^+\(2WZB;[AJP@#/(LAP8)@!-07B>+K
+M!@+\BCM`M2@HG@#?(H"XRH6+3,&X&!+LB2@`U#<&7(&LXCXFE3EX<N(30"QN
+M4QY/R%,NLHJQX;X8R,F)2`"QB`\J/7W!P&@K#@'*8IQX`C`!Q*)=R!1<AA*8
+MHD@$1(S\X@E`!!"+9D_KYS#^BDJ$P<@$R(E.`,BHWE!*9TC&:"DN&":CG+@$
+M5(PE(%XX[@B,%(.E:`1PC$P`%L`'P``57AE@*%9U=EYSPW8@-#%6SG@>M`'X
+MHJV(!"B+6D7"(R?Z`,1BIS;=$$\NH[G8)P(!P2)L@.D$`5-CU?BNI3=W0%RA
+M--J+3>.OJ*L\!E$CV'@"\`!BXW2S+:X!20#'>#">`$/`O]CQY$(XT\AH&C(!
+M=N/)>`+,C.YB;B<?X8QY@*+8%<:,)X`00"PR`0:1*>'C_!]^HV9X?3".5@"Q
+M^.9\2>-,Y>@G0@&!HYRH!1"+@`ZJ<R=X*UDCJR@%\(P^(]`H$PR-==Z=Q]#,
+M(_0B>I`VFH9R0.AX`A@!Q*(4<,^A`9)CR^@YRF"Z(Q10*&YT`@[+2#GFC#_?
+M^$?U77,,H,I@)8I][MN'(`&*;ML$$A!@7(`VGAMPY&54B6#HY[X!<=8C>_0:
+M<$P;76J`K;`N'\+WV![1?/85>P3R48_-7FETEP!=6QP5`.J-.^UC^<(=:7OE
+MHQD@[RV`35X#>"4^"_*<M^<RO4C7HZ5'!0!YW&/Q5N31`>YC7D+]B7T(9+<W
+MO"!\B-W]&$J0=4:>_YA1X4M[@:W'0,J/-"*8=_7%?0<DM]<G;9`3WYN1J&P!
+M0I[IM_D!<:6C7A",!62/#V,&\2QAHM[T./AAB>T"LU=$4@R>V`0(XI%V$628
+M*.N%CU=BX6=$4GS/'AT0-Z"/Q=OFR"Z1@1F8P:?MJ7APP/PX1`IWHE<@!0<@
+M(Z%$TN<1.8E:#L$VWJ4P`D4;<"2*?2Y3/.$:3("716;A!'"/59WP-"=$.+P'
+M:^.ND`$7Y!#I,LD!UV,>:4OPD0I>7O5`["__HQ#9(2%\@<H9*0>\?%``&AE/
+MZ"WB#IY#()51B"1[-.J1;U?>FN;;G3VY#X7`OKEO\4++QJ_90KI/"?9*X@ET
+M`.4R!LR2H0%7=TNF`!T:'0!K(05H`+\&%["2@\Q!,4O&"U;!KW!,+A:,(1RP
+M3*I$SB0O.08N$,)D^Z;EQ`MK@"LI36*38T2#4$WFB+LA,6D&O$O/)#F9O]&(
+M5.)1V(:!><P?5+@^``>O'FP0!NP1K$''$)FQ+C^.M@=/AH5NWSU9I:%(VMY.
+M(`/0A54=#T5/*@DJ4^0R4OT*2L)OE^SMDRX@..5/8H(-Y?,6\95Y6`5!6;RI
+M>?D$#E-"6"HW3/NB[<%Y;Y!&B?'=>?.31]E"@)1G@#[Y&$"459V,PA3P!69`
+ME_5`Y`S,F\]EPK1-<%T<UN?A"R8E*DC=:!^JA/5&II0*;`MKH^BM"]I>*TGS
+M!)4SS\`$U+$=Z=N:D^S%"]WDND`7LG4E&/;GT565N])5>9"T;%LE$!=5FHUW
+M7PCF4)Q@867$QTUR.V6ES=%5@F#S"%@IYR1[H8$M&52F17PCKI,SV)7:7B[9
+M&HP!02444+ZT?H`E7NE+,@E,1UQI!#"6K\%65[X`EDUE,5D&!)4*U`CS`JZ5
+M3=NN5%DFDVQ`4%D$$%,1ST^6%X8\B:5;R4Q>/*(E::D#CCNH8VK)1JR6<$!0
+M:3'2+;W0&A"_V'KQ`C095-XWV\@X05F*E09%4'DO)89]P6*(ILB61\,8`+_M
+M"G'E<<DB,)1]B6P)3IXXS>4X^2L8EV-*D9@B,I>]Y;L43]Y+L]5WR5;>E4,A
+M^5<CJI,*`.]G39H49V0`N-'!6@)#:0!1D#S82HU`-4R5'H-U\+JY;S4"%$8Q
+MO'STY7B`T7ETC)I^"2S$4P)F-C)7P'7%W%GI58(Q\X&K@5JLDEI.:*!-TF]?
+MWS-7U?&5M.1I`/CUE.F*S9/[9&J=S6/P50P!M005``4\!JL*^%+V4634'1\`
+MJ=QR*68<X4VPF)C.$`!CYGV/7XADSMV8)R8;D&.NF$=`\^-C0B.8W]&4`M0'
+MT8O]]D"H<!P.7&`TU'G6&6OS%_A]'ET9P_%U?3UEVD>1A1)YWXRY]J431..8
+M8DN-F,F>F+D`N0!EWQ=08-X&?("@,`0(`9#:$E"XN9DG4)PY9T(C@H(?@`)$
+M%_(-G%B8]1WGY)6)\8TI8R:;&?&5F#@F<*!C/@&(`:;S8D(C?&:-2636:$?F
+MCOF6+)FC7Q6B:48OD::*Z6DJF8]!J+D&/)F0BI3I!E"9YZ3SN-;Y?K)%0D??
+MG7_#GSJG_J%S&EQ,9Z@I@A$<,*%H*@<KI<X4(R`-3=`9P/@%&;^!#9`;M`%H
+MP&T0!"@.5R6[\`7D.6U`BFG[K0JQ)E&HTMEWXMN'.:8XFEP:<U=JQA$\9L+S
+M8P*`>L*3"0+L`9`**O04O&R"V>H1@#V0+>8^=DY&F8[DJ\EM%F[[&]&89:(I
+M6^;WQW9XF5S?5!5FA@<O&IG);K8!5F::*;M\4N3FYB6E,)P=@KSR!1@JL1:@
+M"0((FE@%.B(%\`&%$K]S,B8!1\`V(07D;TC=%N0%)`:P"RO4P=2&X42;B7'J
+M"1HGQUE?F@"!YO#SAG`>)"?G\8:0G"DGT)E;?1,D)^>D#>V;]E_&.09\`6Q`
+M@>EQ@IPZRC7A!)"<<=X1P`<0FD\`TWESM@$:I^ZS!5@!9<4>&4K$`'OFUQEV
+MC@%C)Z57!)Q^@@(,8&7.>]\FG.33N6XJ@(AC5M0[,,<UA'+2$G0$S=&Q9!73
+M!.A1LJ0LJ=@1@8@E0+`3FL(:5$`6TT/4@^US>%YI^0\15H9==R8CH#=:$M5"
+M!#0!!TJX6;S!3?_'DH`P5!1]@;?CA74XZT(E(<<M=O5/0D;>A7$Q@LU9%SB=
+M4*?4*2@`F;AGU-EQ<CI99\1YTK$2>=_*<`4\D`2&RY2ND!`EUU)P&I1[S%W3
+MB7,^G;YG?1D"Q)U0YL5Y>TZ?7X#8:05,`<?G@`%W:G\OVMHY=H*?R.<%B&:R
+M=5G/;-A-N'[)ID4I1JB=3Z?W205(5U3`^,E[<I_V)_ZI?@Z;Q97P-1\<FP>#
+MB*5)[08P@+.9`T";TB:UZ59:F]BFMDD<_)LT8MT)84J<(>8;"'WZ">?F`_F6
+M!`$PIO3)-'8X\&;T(F_^!2A`O:E/^"L>Z-<H`NR;"8"K"6L"G#2B^1=LYIK8
+MW?JGW;E_-!W\-VR"F.2F3<;<*264"Z/TLDF:,>;VUVZ2H/&F<S!OIJ#[R@J:
+MM8P!16AHD#O"H#(H!9H`!)Q89J2A90Z`72;A\64JG+9GP[E[/IS"YZ(Y<:82
+M0:@(FG-*G3SGQ^ESBIQ!I\E)=*J<7N?V"792GW!HSUDHT4%(IUF!<FZ=`T;7
+MF7;JH>8GV8E5C)]HY_R)B-:?;*<5X':.GW)G.C'O^:`8:,CS1C1%(8\DP\-H
+M*VD1";$6+9R/*-1YEL2=\AY1F`"*GFIH!EK&;*`H@%*"-!BA1R9B`(A>`2'H
+MU^ENEJ`)P`E*;T:A`9@LRBX8H2]HX1:#]ILSJ-%`8LF:JQT"N-]=H'6>P%9R
+M]90R*`QP3NYOY-NVTR!05;L9A<E.7)C_98:IE,B2]4$)VD@^507GFG9@@G^]
+M$(G)W`E][<+UZ2<8?2F`&LD$9)_V"@HP^\&CMA`RPA>\FO#HDSEW1G9$0+G)
+M@1:9G2:E^99<FDFHF:G\#)FD)D(J::Z8G^:Z*1TXF1"I32:1FIH**:K)9((O
+MF=_GQVKRFQQ7,LJ,(G7B'#$W[]F@%FCZE]*Q?\@?W2G!`:#%Y@`*?QJ@S&8"
+M^FQ&F\2+`TIE7)M+0;8)'&R;568%VM#!HAUHI:EN?J1*Z`C::B*C%.A).M\M
+M=#?H?=?2Z:#'GZ\)85ZB^]VP>2\UFH0'+"J,^C/(2"W:QEP3N"A3^J*-I,=H
+M21J5JJ+/*"O:H^D^``,UBHQ:H_I;7+<P0):(7069,T!4)Q:JY[Z%!K\DTS&_
+MP9OSG@IY[]ED6LX_FH;:'*X$5V?L\1-_Y*%R@"5[_RB9V1IXD!?,"2E`&J0H
+M0&8:**`*V6<UFFBJ>RS"F$)&J4,*2K+GF0)='20*.6Q"`<X!E74AI52L#<:8
+M[,F@^>CLYYD^!O]HAR./!J>IJ'JIF$J/*FFZTM;A"W:G`A'XL`C!9+>I7BZG
+M;YWXMMSI3(?*<;K1.4\6`BNQ`H5'4A#`YL^L/K*+OY*LG$I*A,=@EY:D>*EJ
+MUU[JI89E7UGML4K>E!WU"V&860L%.5@6?6A`?>`RR0;I7EV(,P&FAQ-4D=8A
+MI@2DO0<%#)M,$M=`(4@R9-2GYH[Z":]IS,!D;J;LT18P/TD1_A&&:@:D`%U`
+M]DF80I;5Y+`)'AU6[5%/J0*\IBO`"A!*`*@I9/2XE2JG3IW=6;S%IYV>M3=-
+M/D3)'MPA&_"GTNGS2.*X=9$>,=>6WF_M*38:NX%'&Z`/B5IP/13#N?>4\4#L
+MFQU@GMPN0P36P"4E*H\!H%(?9)`OI-O#\M$P<<#+1^V].&2J-;98I'7N&Z`2
+MH**FA-7/U>Z]>]U<"=JFWD9)0>$F15(,N][I]!@X>RT$"O*83@'ZB?Q2/,UD
+M:6I6<KL]FLQ="+"NE*;]YJ!:J&8,NP+OT:2R(_G/A,HE^:.CP98:!W0X[DVE
+M\Y1R7(^IR-:O2&"W@8#W&!!H;J1,=*+MC1IHQ,?_Z"V"0B,JA/H)563<((^"
+MD4PHFV,8>CSOB[.5X@T.GEA/J7V&$F"D,0JIL'%0`5X`C^P%G@FGA"II#2A1
+MNY#L)3+"`N^!`J@`C^J["<P`,Y@=FO*=)B454/K4^EVKM^JVNJY8J"A("^`#
+MR`:]JBXC,XV>#@_%55K:.B"7KH&N_A1Q0(MJC$8O*,B+&AZXJU5DM/>O/F%X
+M*FQ:B>HR99;"TA?PHKYJ_L@#V3JM'['G-^6K;,>^6D4&K()"N^H#]'J_WL$Z
+MKV)\[M;RZ7@R>/MJB;FQA@?99ZH5'H02*^LE*3,YK,B3SB90K`'_*HD5O>BL
+M.BN#ZL0\J`L"8=,QN!5@I*9*'?RCGFJ',]M\>-)$^T&UH`"YZN#P@2&1UP9;
+MZFHJJU_@_I.PYJA,`:"$J:(6JM3>&!V49AV-4NF</FL4#^\!B\ZJ=$#V*4/&
+M`5L`MYJH,*)8:_%FJDI*:X^Q5U,R"/M9:13PO"U#JS=5)O:4D@P*P+:>G9J4
+MW%JF_BO]3RAAC7JK$>M&&0;$4[849W>M1JW9R+MZ/HI>ZXJ_"JE@KNZJE"HH
+MN*F0RK*ZR_V3`L%C\%R::!L5FG*MJJPMA*XJ*/"JD&O$BH$>KKFI=Q#M!4Z[
+MDW&SK4![OYZQFJZRK2VK]NFN@JQLAZ"`N?ZK-.M8Y[$6K,&K\AJ[#IB02L!9
+MU34\#\^3*D;I17'#M<JQ_JY6),7@N6ZA[VDQ=P1`%!5/_RB?OCB]F6:T1?`1
+M[!N8JD!.?'N.&/"^T@&'*;`)]FU["20,*=C`J+-I;9JZ;%*U)Z/J)R1D.28J
+M@P8\!E&3/!HU\:)<W1R0@L9NG9J/8P@2L,/4O,D@6#^XD>M6C#(L,I4^NF_J
+MK#(HO%J<"JDI*;#)DEIWNN9+NH-JI>R<4G?3\:\:I)CZOZ(!C^D:!:_)'?S9
+MP--\4JD&;-%'?W:?D>@D&J^ZH9#HV%EVTJV0"A)+Q+:=28!608D.D(?*"HO^
+M$7]7*:\)TW%PFR:*.9$JI2`HIJF+GK"S9@-'E;:D6.RN"9-JI>]?5QJ]C*6T
+MZ!=[EJ(7N:@>&J26L:OH"^"K<J$XI`"K)V"PL"@"BX3*!@RL<%JKQJL1[`0+
+MQ%6PGX'"B:`YH3T9![O'J0`?K#L4PA:EY.NPR?AP7!X2U[J>WF\F;`UZ:U:E
+M+FE^UVMR</J=#,O?M6%**A!WON9+ZBO7VIMY+Q[/_8KJR:_^*U-0OQHR]VO^
+MVF$6I,:$V#?_45TVWU`)GWV)J,)C\(AM!P<*[\>8=EDPDT,QJ)8Z1EHLNYLB
+MKG09/!)*K`MF&_XZ^T4CCX$]&LXJ?3`F-_,B[9LEYM.AS8ZFUNBH>K^YG0XD
+M.AM]>EF2!FE:NR:R(@`%^Q<TLJT<-=LN0+*LDB1;>U*R,*B.X<V.L.MG'RL'
+MV*8(VFZ*C-:OO:Q`2B.FL"OI*(O&MK"F[!8KT\6PU-$S^S(1(84;+FO#!DLX
+M;$)K<WQ4Y5$/RWR>$4`L:.K$VI]1[-MIH3ZNO.A+&XDNL8ZK,6K30K%2;"A1
+M`U"Q9FQ*Q\+FH%IL^^=K=K%&IAS[-=:Q:RD9VXP&M<*?5:K&OK"H;!NKRD(J
+M<*P5:I;>HDQMAW#'/K5Y[!YKTM*F"^T`V]!&?.JLEB,H"+(AZ`*+&\BS\R@]
+MZ][0M/$F4*7(H@;[[`7[R$Z.`>WN,]!6LH4$:"#"TJ#I:EH+=&&?[NR4"<\"
+M6:=I(\G)>E8XDT-;DH:R4FGY9]$.M<4?5GK*;K0,7!Y;OI)OKU`9<(V41CC>
+MN?JZ6:E4(`@0!WP(0.I[">Z-=0*J7ZE152V$9)WJLSJH.,Q_!&O]'TAI_+BW
+MCJ8F0`"IH>X0C*$98//XMB!JO&JZ$B\K:(5US1BKO:55$$T:HU>K,FHTF*@=
+MS;D:\:D``20=T**^J"5M*PN93C6Q+-Z*ZJ&VIN`CD[_2L&'JMP>C*GM98I\J
+MJ%ZWXB:[M+6V?L\JZA%F;JX0;1^&O^Z;J^VK&<HJ$D3CI)JN?"YQ@VT`IA$[
+M2A9E6"+P4CA48M9#?JWK6Q#[MFX!L@'<B=B2I!+2H"IL92O#%E2P8DE@Q:KM
+MB:Q2K6&D2>O(.*L!D7.#XA(.U>JBJDQDJZDG"L*$+K<FUIH!R;%3T2JD-!YH
+MMS=F]#FP:J[L:G_JK8*KNQRGQ9D%0P6L12/D3A(^`/2:O`H*]NB-&T_N1T(0
+M2X4S:4:VU+JBIE:X,E-W&TH$D`=K@"O$0KGG'@S*X3X0.6N:("(\K".K,9>]
+MWJO87L@EO!ZK'>N0*^7^JW$NB$FQ`DL=&M75Y#JO_"WZRLOZM]7D6)?.GH^"
+MPF([W2%/U6V<&]J.MD#N6&?F#K@[:_7JYBXL$NMI\.?*MA02!C/=8*S"Z_?J
+ML2*O-*AXN\E*2*W$(Y/+LJ^"J3AJ2ZZWYEY).VRNN#D3PN`NX4P*@H&5[,DP
+MVNH#2]_6MZ1M^2)PK48][*>W%X1QQBJDZ=N^!O)H=QNO2GRA!'?[[(ZOI*:Y
+M^>Q:J-)NF+OM"@IB+KCKV_ZK-NOD:HPVK&\NFDD)PF2Q+!F%KCX=ZBU"2^`Z
+MMJSNISN??K)39BB[OR6F60!T$00`-8/ICA,&X&ED`*WK(WI9TVSH<JB<)\Z!
+M1T=&N2V&3,10$)R1SV<0&T!"N^+N_0BIEJ1%Z;#Y3*42IY)PI!G!;1U#"\>\
+M+0PS!J3F:!1O5\"C%%FV!@59*O5DD6<#WZ=`P#QV$5_QYN>>>*S2H:*Q#0OF
+M+5*ZD:*\CQHD0@2$H%Z6U7J7[ILE;-*;BKZY+:>_XJ@Y`2JO`F@&G+S27?VV
+M]#Z]K$3Q%H!)=SWEZ#D'4`@\!1F04]QE'ZVUBM;2L^#NT$NI$0&6VDL)UQ9]
+M<BUIFMB^FNWID9KO.KVJW2#XD"AW1.'46_6^;LLH44@%]+N0"(%AG2:H3A[Y
+MAJ/"9UYN5G*9S0@\SL9VWEZI,AD%*40DK4#?F+JN!*C"'J@;^4(\!^IM*QQ)
+M,G(&>@.+.JJ)RL;;X9JT^PIZ8Q4U?&_5NL!ZC+X79L2GGTX'GBH#VTOJDM=H
+MC%I`(J=Z*1B(,(B!&.,WA"(,#/@I9K-A(JC9:?0H1HRI8T!M:?A)@%2>^NG1
+MNG*,'QNQ_#6%K`<0-P:(`7KJ\L<EUIZT38H[_0HV0!SE,AK8`;,D$%=+L@O4
+M08EZG6*]LYTCF#)1`2VFRR`%=%TL@!C`;,V_TT%9VG5)DRB``%P?E'3IKO[;
+M%_"_F([_"P`+P`D``5S_'L!_:@+,:C*CEFBZDH-FO55=F8!UG&P+;I<THJ$I
+MA!AWEII*62X:[0LLZ&S.YWXKV(0_?VJ``P7HJ<N*Q],@`!S1`=?;%WB]_DH%
+ME^Q]E(^DRT0#4WPW2N%&!+-Y.?"=PUKUP#2B$&RDZKR,)G^9R869>(&@X/X"
+M92DHF%?)EI@A0/6+R-JUJPQ0=AL)91S,&,I[<`$B`)C'!O<K78MZ%P(`M@;M
+M8&O=#IN!W5MEKL4(CQM><%7Z,W>"H$``0PFQZ/C+'K5,<,#<R_U:J-SF/4L&
+M`Z/^2LO&_"EAQ0<=?,G*NR7F(LS.(K)4B"^9@G)ZG)WT8P6#<FQP"3`'L,%<
+M@!N@+(K!QF@=C,GRL=27"4/<2EHC&NV+5V:<=,`7X`:TC((PY4*XI*#`P@M*
+MM=BJY6<N/#G\<KTPN_`+RRIOP!<\A.+"NG#+Z-XH#A!<,:P+OTOU[`<%P9+!
+M1>/%E%*N4E!!QO"UL0%S<$%["=.@7NF'6[_Z#08F[:MAR;A22A9L^$6`LBFQ
+M6Z^EIL"/-N/0MA&-;O\K1Z2@D&$(F@@_IB#FY^(&5KHEI@5<EHH`<,"L(A"S
+MI>%/*`$![GL9*O9"$(\I"D/(,VX`?%5AQ6LTW"BA!`3LT_S#(L!=H"RB,@.Q
+M20MB^F0;,55HF*!*;6;%T[%&@%)#X0;LUK@5CWLSS^Q[3N&&:MR"OV0`S>83
+M;ZM`<7);NXZ_QRN`UP]'P/\P4ASE@BH/L4XK!HROL'"#>OHN"'M"=8-0KKY`
+ML;X""3(!70"<">"9`"0""A`&T\1=X@K8!(3%3Q4$%P+<*+MH7<O[ML&:YSJ:
+MS[T!+,*"FP+_9-Q+9$68K$"SK?4V]"!7-17^4@ZWPN?P'8R!(I=38!6H<<E5
+MKHT.Q"R=);!H5ZP$LL5C\1*$%I,!7K$GB"]@QD(#I'I0[!&TJI9"1Q1N8+!E
+M#`F^G6)Q:,RP_`HZ1FD,G>`+%_$1*29N;/#P&B`/G[\V,8WK)^C&]:P)</X.
+MMT\"AWK^"L7$+8>J`AS'(6KM"G=HJ2*Q!*P;$ZQ0,::9"#\&T[$/T//1!\:H
+M;DP5HYDU@AF*)U`N!68*B@=PAOE;5:<EY3>D`>[%7_X`\/&K4/)&?`'F%3D>
+MLPOEL0B`!:#'-^2@0%,PF%'8@RF72IAV<2AG88*YZ:J&2?/P:V/P7`P)WSDC
+MF%UL"0NVK[!)>R\]P;W4@E#Y#@NX+TQ,T^VO.[!5$"/DN<^!W0=GG@%?0(@,
+M!5>VY%RM"?Q=ME<L1FO\;;:&6@*@(H_(SA_N^:?\GC:'$!`$5`%$@%.+U%FQ
+M.&AF6]3RH,`F)GHCOZAF`.5R(Z,`J,P82R-ZMO/NH@GAE"]QG+:Q&Q%[3JJ'
+M;#1P=3Q.T;<9=\9;X!0`&BL0YZ3%"29OOJJQ)Y@&GLG<<490O(4)/24VJ4V^
+M?'3RU%?&'J<SZA]7HPZ;QB]E.`OKIH\F4>=`E*@8:$K)NA#*2QDTO`O_'X*"
+M$W)+)`87##'L*&/#=B;V-`4\I@0R?UEA"@8(<D(<OW2J<;$)^@C;FY%PA#Q5
+M3L@'+0W*(E.G1:J%+(W6I9(MDAK\%G/:*"N1_`(CXTSSBU62HQPFX[N8?GFL
+MKA1QJ&`->F!8VH:IR48>FSPFHX&L<3V1'L_)Z$&=?)!DE<+D%I<GY[QR75=9
+M('_*N>]2-HYNH]^DX2NC+KY^,I&*!Q?+>,GIU'KVE'?$"X$H<Y7;Z(29SWVC
+M"')HD%66`>7R=%JCAKT8WZS\7M7*4V:2"KLQ"",875EH;,NNK9V\()>CBN#)
+M>YQ^R<URP=LF?P%O<FN,)A=N(RHPF>C""U\`>[0&],M"S6<K_LZ5V]8\PC`3
+MDVL`M@PQ+W<2L_3;BU;,8C)/G!;3@=%R_;%O=LR&Z;X),L\!4!C)?*F(MYDE
+M0[59^LJ6)1080[1,OT)]X+Z=(:"O?_=_K$J)H+GW*T3-D"F\=*BN3*6OQ,?^
+MUI)'R3<I+"^H=6'N-/O&?$Z9=I:L?`JCQH3[Y?:4:W+!"S97#36SYT`FKQ=P
+M,M,<6JZ5=X!$RR+SR>CRD,J<&@UML])\!^C-7RU2!RM#HP$S73HP1WRF:5Y:
+MS(V6T\9KR;J$/$@S:.G+CLU>J;@#&;O-O$^R%S?7S4YA#0@G)\Y5W_#;)PO.
+M3QU+4Q?@Q<HDZBS7_<MRJ;/@.,/#D;/)W"WG@+II.+I3)I/19+`<_0Z_P&8Z
+M.5MJET;F&8`\_P7[9GBI3)X!2^)H)CNOEP#SRB#NJ%J)Z@\I.M/,%_-@=CH+
+MR:FS@KHZP\I&P^<L-#^UB[/XYI4*S+CSUJL[6\_#5N.C&%*!C"$<^"D@S0;%
+MIIHE.,W7Y'B*-<MA]G,/:-LFIM+O=;DC>K0U99Z2:FHJ3)]=Y2OL;*+87#2:
+MR;BOLH[(.,//2@7JDUS2SV@**H<@C\X>-`@`!5W,)@Z<#`+X!R1TSAP:#Z2+
+MX&Z8KH;0H^EL7`3$JPFT//PCCF;&:)KHJ'2LT3-(IM,NSTQ!D&BK2BD]]((@
+M1!NC\0+R;+AJ*C"H\VQDZM`I,?E:01N%@*]Q>BXGI^GRX/Q"CZ?9IRU3!/ZW
+M2G"ZF\;X7W?$^:PXT\Y3<.,\C1+,>.^M3+X!EWXP4CC^2K>;\_"LH`*;(<`U
+M85;P<&GFE=PN1%4*Y_NP-Z<K?$1L7+E4QL_R'R@$G,YR<9A\,0."WW/YW``3
+MA0&8'LTR&*M?'MI2?'K19,#-]9\M*X-N!;S[WI(G`!<P+$[/@#,6S3K;G3YJ
+M,%E*G]),WV6!)NB=4\`X`4WB;+`+?<FC64FHDL+@QXEE5;20FCZGT7/I&@TY
+MN\_[KG*J(Q9S]])LEB(BS4IT,\@_=UD^]+1W"N:F[$(B"/W2>\1S@=L4P9XA
+M@NQ9ELUJU/2"4!F?@J/I#0V2N;X%<V.+\1FXA\H^J."6$D/='A?$GL4V,V>\
+M`F(O&C-]</?B;].L'_T.9L_A\LRL^5K,B70N&!*XJ'2O;48B^$21=.><"[E]
+M^V";"42_J#BT\"L^!\[D<U4'!>G)Z#,:K?6RIVYT[$9>0HD5M>^,V?30#37S
+M[-HV@V;J-=U!9].E+P'=3==Y\'3LR4O%4EL9.3U0G].V63I=1,L!['0;+:E^
+MT^F*/(VRT=,"GJ:*3]O-^S0CO>8:U19R)WE8GH(=,MS,/2?4`.%"'=<ZU"``
+M1-U/N],8M+EZ"IJ+F\/XRR3"U!KU"MD!9]&M<_'V4:>7PK1(K?=2U87O=&I!
+M,WKWTEL6&JQO*G6Q8V0R#?XI]3LO8,W"DRT6";E87;/5[*>E-\GJ^LL4B+_J
+MKX"*VTHR8"2V$2E!6;`H=3`YY"E%W[SP&,QC64`]IBP^5YLPI!(0%*VU:AB]
+ML-C4=[0!#9IZUO*",\DTC-;"F6G]&*#6&^[MY.KF`<4J*BI)O]93XEY-5^_)
+M5_0_G??"$2N`H'`#/*;E#`[#,!*N"T+19T,?UJ)7]1M."`JE]+>H256_+6KV
+M66*J`-KU:%HGG@#3+GD-!WRWW#47X%T#,^4NSEJ]@J;JM87:7:/7_W3'VT?Z
+ML1U#7`51]"6PZ&J-XEJY=2'$P+ED+6@*UB"E>J^$AZ`0-!PDH750''%>KV5K
+M_\)%7JO/,<5@73/-\P%""UL[>;(UL@#UPIO++=8#E`T[VMDI&)DQV$>$H+`?
+MF"<1-@R:SA(><VTZ)Z7.-OL4,".#ZM<%96RT#TJIVME4&/#1392?7.2]<M53
+M&0C@HIY[)K2@,`[G/INJ'("TBL%*]EMKC*:S2+97S62/U<`,=5#]Y@&D\\VL
+M4%?96W8]@0A[V,4U%0U2(W6I]$AM*TO.OZP+*]6FLM11-*I`R$1%1.;)KV!1
+M+7+)%K>E-ZJ$V\+HD;6VJ;9B,%&J^%RLVRZ(`;IE@]!T&*ZJ*\0,IS*:+7`W
+M^H6Q'@1:;,6^E%Z8<\1G!X0&5""8O6\.FT\`BM`6&4PI2[+WT:``E`NV4?!*
+MDUGG2`P%D-7"2\;@I<4(O&Q,%A;]6]E*3?E7L@A*PK`@ES(^DC`&Q5/B560`
+M4V$MW\F.Z!T`![BK=X"<V7$*"E>`$:`]QA9ZZAFP%(@!ATJCBW(,JNPSOHM7
+M6Z)$84`W(Z>Q=#9"1\R1;P4QU37NZ`?/Y^O&01)Z2,-Z:R+<,.[;7:))U7FW
+M=DQ&Z'T!:J]D3?'](#5P]1MQ<I"4P[\P+[NR3I\G-U3FRU07E'R7D-7"TWM%
+M\C[<;]5R<V#G#%>E#4WH2=SL$5E=!,PM(,]94ND6KN6UOI2X&MQTPGJ]`HC7
+MS%U];5ZGPNAU[4K'I=SW-;F;K,C7F>Y=S26EQ,ET%0LC%\E7*?)'OC$^;12E
+MI;:JU$,(J&*X5A&S7_$AJG*0Q0?:&4\^`41;2`6JI'54=Q4A`_S/GY36_2]T
+MS84?*N?UV*IEMS_C]7S=)BVG9Y08$;"GU!,>H!8M$@E,3QO21JL_\W3S!=_L
+M7<+A%-7)J.D+M.(V:M:8TEGKW5(WC'F74$^;\)$[!%H%L*<O-:T5>\TN<^=W
+MB\5?0(5IH2[>F+?F31;#U^Q1#(!Y?PAHRFC*>2<%&Z<A0]IL<<_4Q/G#QE++
+MY6C@-[5(Z&KNG,D2NY]!7D@7"&G)WK.@1HS,4C=9S01\"!1"YK3,50U(&L<=
+MQ/[>>W<9$'5[W4JO6ZI?GW3(#_']WG"98`SM?4R7I+XW\=U\_]UY*8M,),_9
+M-#*$F8V>$O!9;NIP*Y7\]8`5_JK4/9N)T!K$V)TO^TM[D`%8,Y'U=.E`%D1C
+MW3_?,MTO@]I58HUB9>%L#3_*AS-IRC$OEAYS\WPM<SO9,K4\'XO+^_+"7;ZY
+MWX.N)NQG9YN$]4S[F/YWG-4&#M?5#F7?MMUMF\9%@)XZ5C[,_3'#G""/RJ]F
+MJ6Q02[#Y+!`W6FJP"D3+AH19V@8RJ"S)L<IV\.T=C=[.(_CF((-ROR6U7+<V
+M/SY'F]J1L0;6\439.$:D`9#VMC)-YP5OZBH7:\UQW9S[Y@9DX0B@>*`D6$1>
+M5M?,03(=!_>8N@7#OV0U$0`QT-/H=RIQV/%9Z0H1]!E\%(-NB4D$%9B`-Q'D
+MA#+(M;&M'7;8D:_!5<G?KAI<L`C@A:`,@./<ZZCVI_IK.0?1S3&;JPCP`O@-
+M2`+3$;FQR.8V5FIKGK&8;=$-PW:V;NS14(E?XA_""Z")@\^6+2@>(^>@R1U'
+MJVZ;M&ZX(*[Z;M^@+/H]?S,=$JUX.P5$X:1!>\590^$1Q:MI!D3:KJT`"=MJ
+MX0(#%ZY2?^'+>!B^9I'A\(@9/O%E$A.@I/QVHL;7Y&/JAB^X<7B@_6C!+G5X
+M]288Q`F!+)S`A__3?KB/`XBKQ&,*G]5ILQ&A1!O`!D@C8+8(,`<#IS5%.*N-
+M.^(ATC\=(CVFA/8`FXIE7%?EBZJ+XZ^9!%F=KGRG4@[#:[H=%+V"F-87\-J5
+M,5]0.%BH,G2O6CO8X_EHP?#@D`$A`PS*,T>GQFA>R2[LDG!![4!BU0Y'>!).
+M;#98EI5WH&@G'PF$_':,6^').!B^ES;C5;.6`XW'DT&`&.X%&;9=,Y9IW11,
+MP+BX_7,7XU6X&]"+PZ=^55]:OHQ<;X"0]%:YNRHUMJ%DS0M1^16NC,>3CR50
+MSJ8*Y3YY43Z-'[S5N&WK-<^4-44VCE5LXUQ:-VXT_%-'86F0XB8`VQ/%YVR7
+MOW3DQ+=[G!Y>8ESH8D5NP^8W#H<?8^(X!@Y[VN'GN#T-FN[AL58?#B?\X1+M
+M#SJF>.7]@EO!9R7D3O8]#@+EXT"`LIA)^.-S.4`.>`_D\/BPE=IV;'R69+EH
+MCY"$]#]+&A_2$9\8(`]SVU+#WGUZ/`8RM-R+&EO>B7#M^N7Q0._4Y,J$5TH0
+M>:5D+.^HI,&UZFR'$@",$+&78ZLS3&94G$.L)::S#6U_`7=)%BPHZ.;3KHV,
+M_.0!/@0*$)Q;V3)3?)VS`C/3N:#@G?L`T7;92`9<YSIKNAH"Z`$"P5V2`"?"
+M+:OM#0N#1[*YOL0U,%FI3&OJ5B(CJ4QHVH\3#`Q%\\R@[P<..@G1I>X%IT<;
+MCJ9\YFZE0GXBS=\-^7#N)SBJJSE9_8M7A;*1VY>!).?QE-!J^VQE&5>!OOH>
+M3J!!>KZ>_]W*-5G],>!5V`H&91>TRV[%ZDL>5Q&^N7<`G/<5T?>;;=+R4/'4
+M89>!T%-93$I[5+XNZ$T9$VG9GO-YM!V>NZMBP!=0QM29L`$1,")=`3:Q/%R=
+MW^>]<7;N;,>K6;JT75^"Z=6V36.,QN<5PK--GW\!]OE>#@OOZ)CVF))[IVI4
+M59ZB\?%`VK?1T'U7(44ZTUBX[=@R.2O76#U4_5-?_!I\YBKU[I$GA.4]>33^
+MDRN;0;GLDI8;Y=0X$6*-"S;8^!LREW<!'5J>0I@[`8KD'LF-FS"%&UY.\>GF
+M@+DA.?&)`8%*#%"8BP"P5A,$!T1NP:RM+M@,YMZ!KCY^\NIRJO<(K#N`@CF&
+M[AW(`+MZK_XB(>95G6)N2C#F<SB458Y[(WBXIDJ9"PR6.73@CF?FU?H;?JVC
+M$I25<Y`KS8;UU!OH[@:Q*(ANSJ5[Z81,F#ZF]^&C1/+SCFOFDQ,D]W/E#$DW
+MZW%,44K*9IU<CXOF%44^/@*8Y@^ZJ7Y+J.8"N8F>5^$%!O%3@`KM+X$*/*R;
+M"ZR(Y%[N93LHL:BJ;J3KY?/ZI_(O3,L87Y:I28.A8$P;]0<K!""[O.X#=.DF
+M^V59&N3J*#L&G0.I5$R!+86JR:4HU:?`1[Q(+=//RB()3GPM/`P&VRLO4J(^
+M^XD`=\`+&J^"Y`%2"JJOH,K_BT5EJ=,N541)CD0SZ#&[G4ZS!RJI.AR0H5.W
+M[K,0KH8J#+$L["*C+^CX!D/>CV<./$"OWCZ"`#Z`M-X:?+`!;%G[QW+H]/A,
+M_*'+[20$66VJ$E:W\X/FK?%2952A_D+'J[($`<O:$NMMP$`IE,S05S9S%Y!O
+M<6H>A4!&73>(F/`JL]/L'2>8+@08`?()8<OF:++X,,;N-[WL7_9_7K*3[7B"
+MR*ZHF[2PBY#VIW=L#O2@?AHX[H<Z\.VLMP&8;/">J%<Z@RJ49#_?"3-Z]DFU
+M\PM6N[[5EY@!H%YTX+6[V5,F:R[>@IB':_'11@E<=F2CA.HA?.1Q\6&XCFN%
+M-?MK!HQK6#,K)WM/-^,:@KXEKS>OFYVJY9`\>$"O8)X0(8]!"5%@/J:75..:
+MY[@VO-@,0XAO.8>*FM;**5EU,M@.9A/B72KZSHW/!\&8@\VIOIJ$>(=3*:?I
+MVBK^CK?'G<^=GT"(;P'X^^DGCYX`+T"=^'F;H`02":\SX0$F?'O-!:CP),((
+M7\)OUR#`":`#H-?9YUB`OQ=N-OP+OP+$`.-GSFUV6=97,5/`KWR4W-Y_(CKO
+M[[#IT=JIKN]*E@:/&RC!+83N>;ES\*EG4J/E^`#89^U:8E+Q7,,6X,73`1IN
+M#N\"U-P0J_\^;:/@.O?->N[&FU#\6(`.DW41,MKB1AIK9[SR#9J:D<C($2NS
+MW6R`/%%MC!;R::32=\<C\KOGS_NRH0`C//9B7[O7-3<4A(V]`)F#?Y#!1YQ@
+M<"$/>%>Y$>?'#F:'DCOY)F\T5))HI*`0C4B`4CQ`.G'_GU4=G^[VY$[23P'?
+MN#KN,J@O;+Y/Y>:KF@GG>.^S-RD5OC>\XSO+I\L3Z9)V]?C"E[_7GW#7WDT3
+M0!R:[OU>?V;W^N[//*:R_+[!M2T(M\\O/\,D>P%![7J?AQ(VV\OFR6?N(_I]
+M'J`CHZ"\NBOLX;8L^G(N`CT^DVKNM`)9:U-J!"V>;"-=BWBW97K)8S3,#F:C
+M\BP3LV&GDP&Z,*ARFI99-W'1HW>3\C"F-;\?=#@KO'EPOOLSF'<90R),`5N@
+M$=`$3'<A/`K`T2<!C]0SE-)U],*=N[D)/Z8'KN;)V;DMVLH>/[%6QP$#W>2Z
+M!K$7_7Z0T=?K([U'#]*/ICU]2C_'3.CX*T0?VKEU4(#_2U:ONH]D-Z\7B=UE
+M`,Z+O;OR(3><G:X8`0F&$(`R\&]J&+KS>2%+'P$'I@*K8'W)FZ%Q86!RQ106
+MZKK'EG:+H#ET]9E!.O$]'%^LP9RP?-$&5)#SLAL4!\"!;W!8S`W,`;G`'Q3`
+MU<%UP'C"-=P!]`4>B`?D`1J3$:1%>)G4I<M@7^(7C9`>P`?MP7N`$(!?C"?H
+M!=>;.^8BCY@S]:\C;5Z0-@026P`.T`5L`3`!(PG$M6`D`A[@(2S:P,@,!N!)
+MDOV(RT1?9F%5@QF0VH^?>P!P(-RS`,-]<1_O577MTJO5<8IO;>K#9ER$Y1M=
+M"Q:(16#=:QO6IC)]:M88?DW^&Q13C!`?/^-,'UK/?_"\N,''T$'\)_JE72SC
+MPIOGHLO$!BA5-*1%7-6U8`/0N]X:O>5ZV6S/VC>1UUEQ,]O'"0/0GQ#;Y/!P
+M@"Y6+)P`=T"=R,*U8>W]K9ZAX@#R??'&M(N0*:K66#TR?<V!*`K&D"]R`"14
+M&E'P(_CV"P@S?E6=J[F\R[A6PKEHJ)C%X7&'$,*'`/4QQ4"SB;\_*^S9G"_6
+M^L2;EIUM8Z1!>L]?:HT^=T?>DBL2*CYSUVT+"AUYBE\:WO<)P/,N&J2@)<";
+M\VH)8G'#JO3!GHM!/I(^Y+_D]WWQ1HY+?UA+3#8%,1&<V'B_,HW@][W*6L^Z
+M"A`<:3#;[%CW/7L?O3CY_+((4`)`<2M8E4_>PV?XY.?4%YZ36+X,*N3?]US^
+MN5B\"6D^NC:#38>_#/Y]_]ZS`5N`)-]>>]?J,=QBKO<K<CC:TH+E`88W@T_D
+M"Z$H@/[\SQSL^'@*^N`T5J37^V"\)2.O#'"J5#&P99(ABX+7!X$^,HJ$M^2.
+M/GQO$Q3QE#RN7^2'^FG`-GO!AZ9*E;N)8S=#3'ZP7ZO:`+Y^`B#;@P:1/B6*
+M!\0`Y.]]_^S3`5L`$1]*G`!O0)WHHG[AUKY^WRUL^[U^:W3?_R"=L'&[VZ/V
+M$0,1SP+8^6-`NP(#A`$E`!FP!M3[!R(38`J;PFLB?[H4I`"NHV/0K:4!+,"C
+MSP)<^X!^(E.U$P?K_C&Q!;3[J'HK;[B>]@X_Q"_1GHL-OQ3Q\*\*3.RY>.[;
+M-D2(<7N7O/LE0+RO!@@!_7Y'`?!/^P+PD-^;!L"'L,0_<:?':*8,&GY<HU9"
+M"&>?.K>T"Y4_.`C(+*38!S*+S`8OC)33"MS8KPU)WZ[X,'Y\^<>2Q>%$C*_\
+MW+U6YKD8$,3YGSZHG_'[]FE`B,KD*_S0.\-/\?\%)'S$/^QG_')`V7_QDZ_G
+MXDUL\WRW[@US-.2C^^>)R'\(P_LFP,F?\O,4*S^UG_"__"._S!]RT_Q<?O&V
+M\XO@XOUI`/2+]C]ST`PQ-_B"3=*O?GK\OW[3WW!"_4U_@TGK3]5S/\CO"8_\
+M>+\:H"_Z^T1UP/_W+_PP_S$N^'^HA#^-B-4K/"@#A`EL)G=0?QY):5)2[EL;
+ML`;L:[0[T)*^3]:!2G\L4ATJ,CWL32<LX7!=[B]W],=*`J8P>!__7):"XF\)
+M!J_!=XJT51*?&B'5)PD-'\."3[_A]Q(@A`_+B_()\"%,,.C^@13!(*''[>7_
+M3AZHK/\-^L@O-\PV+T"%^0*DOFR'#.`#F`#%(=-KZWMR[3]>WM-L7G"S>?_5
+MWX!_"I^JSO"O9++7,?Z93NIW2+\'GT4,S43^`_WI"=)_E2PO&X5._O?^8^;%
+M_\Q_F0/ZG_T/_X<2V/_U_Q9U_S^.00`P^,4BH_VAO$P40P`QA'6JW":)8^",
+MWTI9YS:C%BI+%K?*NFID7GZ`0,`@H!!P"$@$+`(:`8^`2,`DH!)P"<@$;`(Z
+C`9^`4,`HH!1P"D@%K`):`:^`6,`LH!9P"\@%[`)Z`;^`8`$D
+`
+end
diff --git a/usr.sbin/pcvt/Misc/Etc/xmodmap-german b/usr.sbin/pcvt/Misc/Etc/xmodmap-german
new file mode 100644
index 0000000..61bd100
--- /dev/null
+++ b/usr.sbin/pcvt/Misc/Etc/xmodmap-german
@@ -0,0 +1,117 @@
+! German keyboard with a programmer-like mapping,
+! J"org Wunsch <joerg_wunsch@uriah.sax.de>
+!
+! (``programmer-like'' stands for having brackets and braces on diaeresis
+! keys, whereas the ModeShifted keys result in umlauts)
+!
+! Note: the modifier keys are commented out, since they are remapped
+! within Xconfig to match internationalization requirements
+!
+keycode 8 =
+keycode 9 = Escape
+keycode 10 = 1 exclam
+keycode 11 = 2 quotedbl twosuperior
+keycode 12 = 3 paragraph threesuperior
+keycode 13 = 4 dollar
+keycode 14 = 5 percent
+keycode 15 = 6 ampersand
+keycode 16 = 7 slash braceleft
+keycode 17 = 8 parenleft bracketleft
+keycode 18 = 9 parenright bracketright
+keycode 19 = 0 equal braceright
+keycode 20 = ssharp question backslash
+keycode 21 = apostrophe grave
+keycode 22 = Delete
+keycode 23 = Tab
+keycode 24 = q Q at
+keycode 25 = W
+keycode 26 = E
+keycode 27 = R
+keycode 28 = T
+keycode 29 = Z
+keycode 30 = U
+keycode 31 = I
+keycode 32 = O
+keycode 33 = P
+keycode 34 = bracketright braceright udiaeresis Udiaeresis
+keycode 35 = plus asterisk asciitilde
+keycode 36 = Return
+! keycode 37 = Control_L
+keycode 38 = A
+keycode 39 = S
+keycode 40 = D
+keycode 41 = F
+keycode 42 = G
+keycode 43 = H
+keycode 44 = J
+keycode 45 = K
+keycode 46 = L
+keycode 47 = backslash bar odiaeresis Odiaeresis
+keycode 48 = bracketleft braceleft adiaeresis Adiaeresis
+keycode 49 = asciicircum degree
+! keycode 50 = Shift_L
+keycode 51 = numbersign apostrophe
+keycode 52 = Y
+keycode 53 = X
+keycode 54 = C
+keycode 55 = V
+keycode 56 = B
+keycode 57 = N
+keycode 58 = m M mu
+keycode 59 = comma semicolon
+keycode 60 = period colon
+keycode 61 = minus underscore
+! keycode 62 = Shift_R
+keycode 63 = KP_Multiply
+! keycode 64 = Alt_L Meta_L
+keycode 65 = space
+! keycode 66 = Caps_Lock
+keycode 67 = F1
+keycode 68 = F2
+keycode 69 = F3
+keycode 70 = F4
+keycode 71 = F5
+keycode 72 = F6
+keycode 73 = F7
+keycode 74 = F8
+keycode 75 = F9
+keycode 76 = F10
+! keycode 77 = Num_Lock
+keycode 78 = Multi_key
+keycode 79 = KP_7
+keycode 80 = KP_8
+keycode 81 = KP_9
+keycode 82 = KP_Subtract
+keycode 83 = KP_4
+keycode 84 = KP_5
+keycode 85 = KP_6
+keycode 86 = KP_Add
+keycode 87 = KP_1
+keycode 88 = KP_2
+keycode 89 = KP_3
+keycode 90 = KP_0
+keycode 91 = KP_Decimal
+keycode 92 = X386Sys_Req
+keycode 93 =
+keycode 94 = less greater bar
+keycode 95 = F11
+keycode 96 = F12
+keycode 97 = Home
+keycode 98 = Up
+keycode 99 = Prior
+keycode 100 = Left
+keycode 101 = Begin
+keycode 102 = Right
+keycode 103 = End
+keycode 104 = Down
+keycode 105 = Next
+keycode 106 = Insert
+keycode 107 = Delete
+keycode 108 = KP_Enter
+! keycode 109 = Control_R
+keycode 110 = Pause
+keycode 111 = Print
+keycode 112 = KP_Divide
+! keycode 113 = Alt_R Meta_R
+keycode 114 = Break
+
diff --git a/usr.sbin/pcvt/Misc/Makefile b/usr.sbin/pcvt/Misc/Makefile
new file mode 100644
index 0000000..dcbc0b5
--- /dev/null
+++ b/usr.sbin/pcvt/Misc/Makefile
@@ -0,0 +1,15 @@
+SUBDIR= Doc Etc
+
+FILES= README.FIRST
+
+beforeinstall:
+ for file in ${FILES}; \
+ do \
+ ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \
+ ${.CURDIR}/$$file ${DESTDIR}${BINDIR}/$$file ; \
+ done
+
+afterdistribute: beforeinstall
+
+.include "Makefile.inc"
+.include <bsd.subdir.mk>
diff --git a/usr.sbin/pcvt/Misc/Makefile.inc b/usr.sbin/pcvt/Misc/Makefile.inc
new file mode 100644
index 0000000..6c5322a
--- /dev/null
+++ b/usr.sbin/pcvt/Misc/Makefile.inc
@@ -0,0 +1,3 @@
+DISTRIBUTION= doc
+BINDIR= /usr/share/pcvt
+BINMODE= 0444
diff --git a/usr.sbin/pcvt/Misc/README.FIRST b/usr.sbin/pcvt/Misc/README.FIRST
new file mode 100644
index 0000000..7194a72
--- /dev/null
+++ b/usr.sbin/pcvt/Misc/README.FIRST
@@ -0,0 +1,291 @@
+================================================================================
+| |
+| P C V T - VT220 Terminal Emulation Driver |
+| ------------------------------------------- |
+| |
+| NetBSD 0.9, 1.0 |
+| FreeBSD 1.0, 1.1, 1.1.5.1, 2.0 |
+| |
+| Release 3.20 April 1995 |
+| |
+| ---------------------------- |
+| ========>> BETA 24 <<======= |
+| ---------------------------- |
+| |
+| (c) Copyright 1992,1993,1994,1995 by |
+| |
+| Hellmuth Michaelis |
+| Eggerstedtstrasse 28 |
+| 22765 Hamburg |
+| Europe |
+| |
+| For the contributors copyrights which apply to parts of the source |
+| see the header sections of the respective source files. |
+| |
+================================================================================
+
+Written by: Hellmuth Michaelis (hm@hcs.de)
+
+The major contributors to pcvt are Brian and Joerg, pcvt would not be what it
+is without the help, the support and the code from Joerg:
+
+ Brian Dunford-Shore (brian@athe.wustl.edu)
+
+ wrote parts of the EGA/VGA hardware handling and
+ some of the more ugly aspects of the VT220.
+
+ Joerg Wunsch (joerg_wunsch@uriah.heep.sax.de)
+
+ added ALL support for XFree86, the screensaver sub-
+ system and support for FreeBSD (and much more ...).
+
+
+I have to thank the following people for their help, for beta-testing, bugfixes,
+code, keymaps, suggestions, hints, patience and too much more to mention:
+
+ Scott Turner (scotty@gagetalker.com)
+ Peter Galbavy (peter@wonderland.org)
+ Michael Havemester (tik@abqhh.hanse.de)
+ Gordon L. Burditt (gordon@sneaky.lonestar.org)
+ Bruce Evans (bde@runx.oz.au)
+ Heiko W. Rupp (hwr@pilhuhn.ka.sub.org)
+ Carsten Lutz (clu@malihh.hanse.de)
+ Christian Rohrmueller (internal@doitcr.doit.sub.org)
+ Andy Duplain (duplain@rtf.bt.co.uk)
+ Marko Karppinen (dreamer@purkki.apu.fi)
+ Onno van der Linden (c/o frank@fwi.uva.nl)
+ Dave Nelson (dcn@ignatz.acs.depaul.edu)
+ Mark Weaver (Mark_Weaver@brown.edu)
+ John Brezak (brezak@apollo.hp.com)
+ Jan-Oliver Neumann (jan@encap.hanse.de)
+ Kim Andersen (kim@dde.dk)
+ Michael Graff (explorer@iastate.edu)
+ Randy Terbush (randyt@cse.unl.edu)
+ Benjamin Lewis (blewis@vet.vet.purdue.edu)
+ Daniel Harris (daniel@reubio.apana.org.au)
+ Alistair G. Crooks (agc@uts.amdahl.com)
+ Szabolcs Szigeti (pink@bagira.fsz.bme.hu)
+ Charles Hannum (mycroft@gnu.ai.mit.edu)
+ Thomas Gellekum (thomas@ghpc8.ihf.rwth-aachen.de)
+ Matthieu Herrb (matthieu@laas.fr)
+ John A. Perry (perry@jpunix.com)
+ John Kohl (jtk@kolvir.blrc.ma.us)
+ Brian Moore (ziff@eecs.umich.edu)
+ Martin Husemann (martin@euterpe.owl.de)
+ Lon Willett (willett@math.utah.edu)
+ Mark Willey (mwilley@mipos2.intel.com)
+ Bill Sommerfeld (sommerfeld@orchard.medford.ma.us)
+ Rafal Boni (r-boni@uiuc.edu)
+ Thomas Eberhardt (thomas@mathematik.uni-bremen.de)
+
+
+History (see also Doc/ChangeLog)
+--------------------------------------------------------------------------------
+
+Release Changes/Features
+------------- ----------------------------------------------------------------
+1.00 (08/92) Released as "pccons_vga" to alt.sources, VT100 emulation
+
+2.00 (01/93) VT220 emulation, many bugfixes and enhancements
+
+2.10 (03/93) Fixed bugs, monochrome environments, configuration changes
+
+2.20 (10/93) never released (experimental internal release)
+
+3.00 (03/94) Support for XFree86 >= 1.2, support for XFree86 2.0's
+ syscons/USL model for multiple X servers and/or terminal
+ sessions from Joerg Wunsch (Thank You, Joerg !!!), fixed
+ bugs, (n)curses-based EGA/VGA font editor, memory mapped
+ screens, full MDA/CGA virtual screen support, 132 columns
+ on some super VGA chipsets, support for NetBSD >= 0.9,
+ support for FreeBSD >= 1.0 and much more ....
+ (posted to comp.sources.misc, Volume 41, Issue 140-152)
+
+3.10 (08/94) never released (experimental internal release)
+
+3.20 Fast forward/backward scrolling from Michael Havemester,
+ further optimization by Charles Hannum. Keyboard queueing
+ for silo overflow minimization also from Michael.
+ Many bugfixes, cleanups and enhancements.
+ Support for NetBSD 1.0 and FreeBSD 2.0.
+
+
+Features
+--------------------------------------------------------------------------------
+
+The 'pcvt' VT220 emulator driver has:
+
+ - Almost full DEC VT220 (VT100/VT102) Terminal functionality
+ - support for XFree86 >= 1.2 using the pccons model
+ - full multiple virtual screen / multiple X-server support
+ for XFree86 >= 2.0 using the USL-VT/syscons model
+ - Full Support for MDA, CGA, EGA and VGA display adaptors
+ - configurable number of virtual screens on any video board
+ - completely independent virtual terminals for any video board
+ - (24), 25, 28, 40, or 50 lines for each virtual screen on VGA's
+ - (24), 25, 35, or 43 lines for each virtual screen on EGA's
+ - Fully remappable keyboard to support national keyboards
+ - All VT220 character sets plus ISO Latin-1 and DEC Technical supported
+ - VT220 downloadable character set supported when run on EGA/VGA
+ - VT220 user defined keys for each virtual terminal
+ - Optional function key label support a 'la Hewlett-Packard
+ - Display function codes (0x00-0x1f/0x90-0xaf) functionality
+ - Optional screen-saving feature
+ - 132 column operation on several VGA chipsets:
+ o Tseng Labs ET3000 and ET4000
+ o Western Digital WD90C11
+ o Trident TVGA9000, TVGA8900B, TVGA8900C, TVGA8900CL
+ o Video 7 1024i
+ o S3 80C928 (board dependent)
+ o Cirrus Logic GD542x (board dependent)
+
+What it cannot:
+
+ - No double wide/high characters
+ - No softscroll
+ - No inverse background
+ - No VT220 printer output support
+ - No VT52 support at all
+ - No 8-bit controls
+ - Only limited AT-keyboard (84 keys) support
+ - Help you to make money ....
+
+
+The entire pcvt package consists of:
+
+ - the VT220 emulating driver itself
+ - complete documentation for installation and operation
+ - termcap/terminfo, pcvt.el, rc.local, /etc/ttys, xmodmap examples
+ - cursor: utility to set the cursor size and shape
+ - fed: curses-based EGA/VGA character set editor
+ - fontedit: utility to edit the vt220 downloadable character set
+ - ispcvt: utility to display the drivers compile time configuration
+ - kcon: utility to setup national keyboard layouts and remap keys
+ - keycap: keyboard mapping database library similar to termcap
+ - loadfont: utility to load up to 4/8 fonts into an EGA/VGA board
+ - mcon: utility to control/configure a keyboard based mouse emulator
+ - scon: utility to runtime configure the video part of pcvt
+ - userkeys: utility to set the VT220 user programmable function keys
+ - vttest: VT100 compatibility torture test program
+ - some color- characterset- and attribute demos
+ - vga and keyboard register-level debugging utilities
+
+
+Tested Operating Systems
+--------------------------------------------------------------------------------
+
+ NetBSD 0.9 pcvt release 3.20-b2 tested
+
+ NetBSD 1.0 pcvt release 3.20-b24 tested
+
+ NetBSD-current (post 1.0) reported to run (end of March '95)
+
+
+ FreeBSD 1.1R pcvt release 3.20-b7 tested
+
+ FreeBSD 1.1.5.1R pcvt release 3.20-b24 tested
+
+ FreeBSD 2.0 pcvt release 3.20-b24 tested
+
+ FreeBSD-current (post 2.0) reported to run (end of March '95)
+
+
+
+Installation / Upgrade
+--------------------------------------------------------------------------------
+
+ R E A D (!!!) THE INSTRUCTIONS IN THE Doc/INSTALL.xxxBSD FILES CAREFULLY !
+ ==========================================================================
+
+ Again: PLEASE R E A D T H E M !!!!! (Thank You! ;-)
+ ========================================
+
+ If you read them, you should have NO problems installing pcvt on your
+ system, if you don't read them, you'll probably run into problems ...
+
+ If you run into any difficulties, please read Doc/NotesAndHints !
+
+NOTE 1:
+-------
+ It is highly recommended in order to configure the driver into the system,
+ that you remove (and/or backup) your previous kernel compile directory and
+ do a fresh "config" with the new pcvt configuration. This has to be done
+ because the chain "config/make depend/make" obviously does not resolve ALL
+ dependencies!
+
+NOTE 2:
+-------
+ You MUST copy or link the Util/Makefile.inc.X for your flavour of xxx(x)BSD
+ to Util/Makefile.inc . This is because FreeBSD handles manual pages
+ in a differnet way than NetBSD. Also you have to edit Doc/Makefile
+ to make this changes for the pcvt(4) manpage.
+
+NOTE 3:
+-------
+ The driver now (from 2.10 on) DEPENDS on the BIOS video display setting
+ stored in the RTC CMOS Ram - verify your configuration setting!
+
+NOTE 4:
+-------
+ If you are using the pcvt termcap entry from Etc/Termcap, please reinstall
+ this into /usr/share/misc/termcap if you are upgrading, the entry had bugs
+ in release 3.10 and all earlier releases.
+
+ After doing that, you may need to generate a new termcap database for newer
+ FreeBSD (2.0 and up) and NetBSD (1.0 and up) Releases:
+ cd to /usr/share/misc and execute 'cap_mkdb termcap'.
+
+NOTE 5:
+-------
+ The default keyboard layout is documented in Doc/Keyboard.HP unless you
+ compiled with PCVT_VT220KEY which is described in Doc/Keyboard.VT. Please
+ note that PCVT_VT220KEYB is not much supported, because i don't use it.
+
+NOTE 6:
+-------
+ When upgrading from a previous version of the driver, you can remove now
+ the directory /usr/share/misc/vgafonts. It was moved in release 3.20 to
+ /usr/share/misc/pcvtfonts.
+
+NOTE 7:
+-------
+ In case you don't like pcvt's white on red kernel messages, have a look at
+ the end of pcvt_conf.h, this is the place to change them.
+
+
+WYSIWYG - What You Share Is What You Get
+--------------------------------------------------------------------------------
+
+PLEASE, if you fix bugs, add features, hack this driver to work on your
+hardware or simply don't get it up and running, get in contact with me!
+
+ Help us to avoid reinventing the wheel over and over again!
+ -----------------------------------------------------------
+
+The code is far from being perfect, YOU are very welcome to enhance it !
+Please mail bug reports/fixes, suggestions, enhancements & diffs to
+
+ hm@hcs.de or
+ hm@altona.hamburg.com
+
+I will support this driver as my time permits it, feel free to contact me!
+
+Have fun!
+
+Hellmuth
+
+
+@home
+-----
+e-mail: hm@altona.hamburg.com
+ tel: +49 / 40 / 384298
+s-mail: Eggerstedtstr. 28, 22765 Hamburg, Europe
+
+
+@work
+-----
+e-mail: hm@hcs.de
+ tel: +49 / 40 / 55903-170
+ fax: +49 / 40 / 5591486
+s-mail: GFKT HCS Computertechnik GmbH, Oldesloer Str. 97-99,
+ 22457 Hamburg, Europe
diff --git a/usr.sbin/pcvt/cursor/Makefile b/usr.sbin/pcvt/cursor/Makefile
new file mode 100644
index 0000000..b5afd6c
--- /dev/null
+++ b/usr.sbin/pcvt/cursor/Makefile
@@ -0,0 +1,3 @@
+PROG= cursor
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/pcvt/cursor/cursor.1 b/usr.sbin/pcvt/cursor/cursor.1
new file mode 100644
index 0000000..7333108
--- /dev/null
+++ b/usr.sbin/pcvt/cursor/cursor.1
@@ -0,0 +1,76 @@
+.\" Copyright (c) 1992,1993,1994 Hellmuth Michaelis
+.\"
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by Hellmuth Michaelis
+.\" 4. The name authors may not be used to endorse or promote products
+.\" derived from this software without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+.\" IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+.\"
+.\" @(#)cursor.1, 3.20, Last Edit-Date: [Mon Dec 19 20:30:40 1994]
+.\"
+.Dd December 19, 1994
+.Dt CURSOR 1
+.Sh NAME
+.Nm cursor
+.Nd set cursor shape for the pcvt VT220 video driver
+.Sh SYNOPSIS
+.Nm cursor
+.Op Fl d Ar device
+.Op Fl n Ar screenno
+.Op Fl s Ar lineno
+.Op Fl e Ar lineno
+.Sh DESCRIPTION
+The
+.Nm cursor
+utility allows the user to set the cursor shape in a given virtual screen
+of the above mentioned driver.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl d
+Specifies a device for which the cursor shape is set.
+.It Fl n
+Sets the virtual screen number to apply the following parameters to. Not
+specifying this parameter implies the current virtual screen or the screen
+referenced by the -d parameter.
+.It Fl s
+Specifies the starting (top) scanline the cursor should have.
+.It Fl e
+Specifies the last (bottom) scanline the cursor should have.
+.El
+.Pp
+Be aware of the fact that the parameters need to be adjusted for the current
+size of the characterfont in use, on EGA and VGA boards sizes of 8, 14 and
+16 scanlines are currently supported.
+.Sh EXAMPLES
+The command
+.Dq Li cursor -s3 -e10
+sets the cursor on the current virtual screen to a rectangular shape on a
+14 line VGA screen.
+.Sh BUGS
+No known bugs
+.Sh SEE ALSO
+.Xr loadfont 1 ,
+.Xr scon 1 ,
+.Xr pcvt 4
diff --git a/usr.sbin/pcvt/cursor/cursor.c b/usr.sbin/pcvt/cursor/cursor.c
new file mode 100644
index 0000000..1c7c140
--- /dev/null
+++ b/usr.sbin/pcvt/cursor/cursor.c
@@ -0,0 +1,157 @@
+/*
+ * Copyright (c) 1992, 1995 Hellmuth Michaelis
+ *
+ * Copyright (c) 1992, 1994 Brian Dunford-Shore
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by
+ * Hellmuth Michaelis and Brian Dunford-Shore
+ * 4. The name authors may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+static char *id =
+ "@(#)cursor.c, 3.20, Last Edit-Date: [Tue Apr 4 12:27:54 1995]";
+
+/*---------------------------------------------------------------------------*
+ *
+ * history:
+ *
+ * -hm adding option -d <device>
+ *
+ *---------------------------------------------------------------------------*/
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <machine/pcvt_ioctl.h>
+
+#define DEFAULTFD 0
+
+main(argc,argv)
+int argc;
+char *argv[];
+{
+ extern int optind;
+ extern int opterr;
+ extern char *optarg;
+
+ struct cursorshape cursorshape;
+ int fd;
+ int c;
+ int screen = -1;
+ int start = -1;
+ int end = -1;
+ int dflag = -1;
+ char *device;
+
+ while( (c = getopt(argc, argv, "d:n:s:e:")) != -1)
+ {
+ switch(c)
+ {
+ case 'd':
+ device = optarg;
+ dflag = 1;
+ break;
+
+ case 'n':
+ screen = atoi(optarg);
+ break;
+
+ case 's':
+ start = atoi(optarg);
+ break;
+
+ case 'e':
+ end = atoi(optarg);
+ break;
+
+ case '?':
+ default:
+ usage();
+ break;
+ }
+ }
+
+ if(start == -1 || end == -1)
+ usage();
+
+ if(dflag == -1)
+ {
+ fd = DEFAULTFD;
+ }
+ else
+ {
+ if((fd = open(device, O_RDWR)) == -1)
+ {
+ char buffer[80];
+ strcpy(buffer,"ERROR opening ");
+ strcat(buffer,device);
+ perror(buffer);
+ exit(1);
+ }
+ }
+
+ if(screen == -1)
+ {
+ struct stat stat;
+
+ if((fstat(fd, &stat)) == -1)
+ {
+ char buffer[80];
+ strcpy(buffer,"ERROR opening ");
+ strcat(buffer,device);
+ perror(buffer);
+ exit(1);
+ }
+
+ screen = minor(stat.st_rdev);
+ }
+
+ cursorshape.start = start;
+ cursorshape.end = end;
+ cursorshape.screen_no = screen;
+
+ if(ioctl(fd, VGACURSOR, &cursorshape) == -1)
+ {
+ perror("cursor - ioctl VGACURSOR failed, error");
+ exit(1);
+ }
+ else
+ exit(0);
+}
+
+usage()
+{
+ fprintf(stderr,"\ncursor - set cursor shape for pcvt video driver\n");
+ fprintf(stderr,"usage: cursor -d [device] -n [no] -s [line] -e [line]\n");
+ fprintf(stderr," -d <device> device to use (/dev/ttyvX), default current\n");
+ fprintf(stderr," -n <no> screen no if specified, else current screen\n");
+ fprintf(stderr," -s <line> start scan line (topmost scan line)\n");
+ fprintf(stderr," -e <line> ending scan line (bottom scan line)\n\n");
+ exit(1);
+}
+
diff --git a/usr.sbin/pcvt/demo/Makefile b/usr.sbin/pcvt/demo/Makefile
new file mode 100644
index 0000000..79f7397
--- /dev/null
+++ b/usr.sbin/pcvt/demo/Makefile
@@ -0,0 +1,54 @@
+PROG= playvt
+SRCS= playvt.c
+DEMOS= chardemo.vt colors.vt sgr.vt
+DEMOS+= outerlimit.vt twzone.vt cowscene.vt xmas.vt
+NOMAN=
+
+all: $(DEMOS) $(PROG)
+
+install: ${DEMOS}
+ @${ECHO} "to look at the demos, execute:"
+ @${ECHO} " \"cat <filename>.vt\""
+ @${ECHO} "if it is an animation an it runs too fast, try out:"
+ @${ECHO} " \"playvt -f <filename>.vt -d<some-delay-val>\""
+
+.include <bsd.prog.mk>
+
+CLEANFILES+= ${DEMOS}
+
+# this seems to be the lowest common denominator
+
+chardemo.vt: ${.CURDIR}/$@.gz.uu
+ uudecode ${.CURDIR}/$@.gz.uu
+ gunzip -f $@.gz
+ rm -f $@.gz
+
+colors.vt: ${.CURDIR}/$@.gz.uu
+ uudecode ${.CURDIR}/$@.gz.uu
+ gunzip -f $@.gz
+ rm -f $@.gz
+
+sgr.vt: ${.CURDIR}/$@.gz.uu
+ uudecode ${.CURDIR}/$@.gz.uu
+ gunzip -f $@.gz
+ rm -f $@.gz
+
+cowscene.vt: ${.CURDIR}/$@.gz.uu
+ uudecode ${.CURDIR}/$@.gz.uu
+ gunzip -f $@.gz
+ rm -f $@.gz
+
+xmas.vt: ${.CURDIR}/$@.gz.uu
+ uudecode ${.CURDIR}/$@.gz.uu
+ gunzip -f $@.gz
+ rm -f $@.gz
+
+outerlimit.vt: ${.CURDIR}/$@.gz.uu
+ uudecode ${.CURDIR}/$@.gz.uu
+ gunzip -f $@.gz
+ rm -f $@.gz
+
+twzone.vt: ${.CURDIR}/$@.gz.uu
+ uudecode ${.CURDIR}/$@.gz.uu
+ gunzip -f $@.gz
+ rm -f $@.gz
diff --git a/usr.sbin/pcvt/demo/README b/usr.sbin/pcvt/demo/README
new file mode 100644
index 0000000..5e33f54
--- /dev/null
+++ b/usr.sbin/pcvt/demo/README
@@ -0,0 +1,20 @@
+- The files "chardemo.vt" and "colors.vt" are taken from the MSDOS Kermit
+ distribution and are redistributed with permission from Frank da Cruz.
+
+- cat chardemo.vt - displays all available character sets
+
+- cat colors.vt - displays all available back/foreground color combinations
+
+- cat sgr.vt - displays all possible graphic renditions
+
+- all other files are some VT100 animations collected over the time from
+ unkown sources, play them with playvt.
+
+- Playvt is a program to play an animation file on pcvt: because pcvt is
+ _much_ faster than an original VT100/VT220, it adds a programmable delay
+ for each character so one can enjoy smooth-running animations! :-)
+ There is no manpage available for playvt, just try to run playvt -? which
+ should give sufficient information. You have to empirically find out which
+ delay value fit's your needs!
+
+have fun!
diff --git a/usr.sbin/pcvt/demo/chardemo.vt.gz.uu b/usr.sbin/pcvt/demo/chardemo.vt.gz.uu
new file mode 100644
index 0000000..efcd4c7
--- /dev/null
+++ b/usr.sbin/pcvt/demo/chardemo.vt.gz.uu
@@ -0,0 +1,53 @@
+begin 664 chardemo.vt.gz
+M'XL("):V^BX"`V-H87)D96UO+G9T`*7829,=5Q5%X3D1X!]0DZ(1($`F[VLR
+M\UI"IF3`1V",<=DT%IT0!MF6)6%D^N:OLW*OO!',J<E>\09U[N2;Y,6CBP>'
+MVZT^^YE+_MY\^.*#9T\?/KF\O+I^[?[][:<W^.GIK79Y>?WI\\O7/^&7Z^>/
+MC,MWW[J^WO;RG?<?/=[VZLF+6V\_^]A_]K]_]T9<C;AQWF,:O]P9<7=$V__1
+MX7@ZS\O_'Q</YMN'NIQ>H9:MVE;K5H>M^E;'K=JTY2G9MCPG#UO.R>.62_*T
+MY9H\;]F3VZF64VV[U7*K;<=:CK7M6LNUPW:MY=IAN]9R[=73X?'%\KGMT7/E
+MQ7-]?GON7%_8WCK7%_/0N;Z45\YU(T^<Z\MYWUQ?R>/F^FI>-M?-/&NNK^5-
+M<WT]#YKK&WG-7+?RE+E>SCOF^N9V>ZEIN[U4VVXO==AN+W7,[:5.N;W4.;>7
+MFG-[J26WEUIS>ZF>VTN]DMM+W<[MI>[D]E+?RNVE[N;V4J]NM]?Z]G9[K:OM
+M]EKWMMMKO9;;:WTGM]?Z;FZO];W<7NOUW%ZK<GNM^[F]UO=S>ZT?Y/9:;^3V
+M6C_,[;7>S.VU?K3=[O76=KO7C[?;O=[>;O>ZSNU>[^1VKW=SN]=/<KO73W.[
+MU\]RN]?/<[O7>[G=ZT%N]_I%;O?Z96[W^E5N]_KU=KM-]9OM./MPN\[^=CO/
+M/LI]XG=Y`/%^7D#\/D\@_I`W$(_S".*#O(+X,,\@/LH[B"=Y"/%Q7D(\S5.(
+M9WE+J^=Y2ZL_YBVM/LE;6OW)M[1ZX5M:?>I;6OW9M[3ZBV]I]5??TNIOOJ75
+MWWU+JW_XEE;_]"VM_N5;6OW;M[3ZSTL7:TP\N;AY;V?1SKI@`X.-#%8:A#8(
+M<1#J(.1!Z(,0"*$00B*$1@B1$"HAPJ3-.F$#A8T45BJ$5@BQ$&HAY$+HA1`,
+MH1A",H1F"-$0JB'"IBVZ80.'C1Q6.H1V"/$0ZB'D0^B'$!"A($)"A(8($1$J
+M(L*HK3IB`XF-)%9*A)8(,1%J(N1$Z(D0%*$H0E*$I@A1$:HB9-5W5GUGU7=6
+M?;#J@U4?K/I@U0>K/ECUP:H/5GVPZH-5'ZSZ8-5E=9ADQ885&U:LK`A9$;(B
+M9$7(BI`5(2M"5H2L"%D1LB)D1835K:L=U.$D*#:@V(!B!44(BA`4(2A"4(2@
+M"$$1@B($10B*$!0A*"*@#F=!L0'%!A0K*$)0A*`(01&"(@1%"(H0%"$H0E"$
+MH`A!$0%UF`7%!A0;4*R@"$$1@B($10B*$!0A*$)0A*`(01&"(@1%!-1A$10;
+M4&Q`L8(B!$4(BA`4(2A"4(2@"$$1@B($10B*$!014(=54&Q`L0'%"HH0%"$H
+M0E"$H`A!$8(B!$4(BA`4(2A"4(2@^@ZJ[Z#Z#JH/4'V`Z@-4'Z#Z`-4'J#Y`
+M]0&J#U!]@.H#5!^@NJ!NWCCOHHY'1;$1Q484JRA"482B"$41BB(412B*4!2A
+M*$)1A*((11$1=3PIBHTH-J)811&*(A1%*(I0%*$H0E&$H@A%$8HB%$4HBHBH
+MXUE1;$2Q$<4JBE`4H2A"482B"$41BB(412B*4!2A*$)11$0=9T6Q$<5&%*LH
+M0E&$H@A%$8HB%$4HBE`4H2A"482B"$41$75<%,5&%!M1K*((11&*(A1%*(I0
+M%*$H0E&$H@A%$8HB%$5$U'%5%!M1;$2QBB(412B*4!2A*$)1A*((11&*(A1%
+M*(I0%*&H:0=U.@B*#2@VH%A!$8(B!$4(BA`4(2A"4(2@"$$1@B($10B*"*C3
+M45!L0+$!Q0J*$!0A*$)0A*`(01&"(@1%"(H0%"$H0E!$0)U.@F(#B@TH5E"$
+MH`A!$8(B!$4(BA`4(2A"4(2@"$$1@B("ZG06%!M0;$"Q@B($10B*$!0A*$)0
+MA*`(01&"(@1%"(H0%!%0IUE0;$"Q`<4*BA`4(2A"4(2@"$$1@B($10B*$!0A
+M*$)01$"=%D&Q`<4&%"LH0E"$H`A!$8(B!$4(BA`4(2A"4(2@"$$1@KJS@SHW
+M0;$!Q084*RA"4(2@"$$1@B($10B*$!0A*$)0A*`(01$!=3X(B@TH-J!801&"
+M(@1%"(H0%"$H0E"$H`A!$8(B!$4(B@BH\U%0;$"Q`<4*BA`4(2A"4(2@"$$1
+M@B($10B*$!0A*$)01$"=3X)B`XH-*%90A*`(01&"(@1%"(H0%"$H0E"$H`A!
+M$8(B`NI\%A0;4&Q`L8(B!$4(BA`4(2A"4(2@"$$1@B($10B*$!014.=94&Q`
+ML0'%"HH0%"$H0E"$H`A!$8(B!$4(BA`4(2A"4(2@[HZ/==/^M6[:/]=-^_>Z
+M:7RPF\87NVE\LIO&-[MI?+2;QE>[:7RVF\9WNVE\N)O&E[MI?+J;QK>[25!S
+M$Q0;4&Q`L8(B!$4(BA`4(2A"4(2@"$$1@B($10B*$!014/-!4&Q`L0'%"HH0
+M%"$H0E"$H`A!$8(B!$4(BA`4(2A"4$1`S4=!L0'%!A0K*$)0A*`(01&"(@1%
+M"(H0%"$H0E"$H`A!$0$UGP3%!A0;4*R@"$$1@B($10B*$!0A*$)0A*`(01&"
+M(@1%!-1\%A0;4&Q`L8(B!$4(BA`4(2A"4(2@"$$1@B($10B*$!0AJ#9`]1U4
+MWT'U'50?H/H`U0>H/D#U`:H/4'V`Z@-4'Z#Z`-4'J#Y`]?UK^+1_#I_V[^'3
+M_D%\&E_$I_%)?!K?Q*?Q47P:7\6G\5E\&M_%I_%A?!I?QJ?Q:7P:W\:G\7%\
+M$M32!,4&%!M0K*`(01&"(@1%"(H0%"$H0E"$H`A!$8(B!$4$U'(0%!M0;$"Q
+M@B($10B*$!0A*$)0A*`(01&"(@1%"(H0%!%0RU%0;$"Q`<4*BA`4(2A"4(2@
+M"$$1@B($10B*$!0A*$)01$`M)T&Q`<4&%"LH0E"$H`A!$8(B!$4(BA`4(2A"
+64(2@"$$1?CT_'&^W^B\8)0@*\AL``,4&
+`
+end
diff --git a/usr.sbin/pcvt/demo/colors.vt.gz.uu b/usr.sbin/pcvt/demo/colors.vt.gz.uu
new file mode 100644
index 0000000..1d5d0da
--- /dev/null
+++ b/usr.sbin/pcvt/demo/colors.vt.gz.uu
@@ -0,0 +1,15 @@
+begin 664 colors.vt.gz
+M'XL("):V^BX"`V-O;&]R<RYV=`#MU+UNG$`4!>!^I7T"FGD`Q^+^S)TB2N-8
+M;B(GA=UMM;*1C;(8":\E/W[,G(.SX0E2+,WE(#%\@N$T._.O7H;MYO;NR_6O
+MN_2CFX;^F.RR3??=^S'=CH]=^CX>QNGU(ME[^I9NQJE[FL:WE\>+Y/.%J_W#
+M;US8;K:;A*/963LO_'&+MVF^TP5#,0S#,3)&8)24MIMEA63MLEP[I#EYBRB,
+M@JB,BFB,ANB,CI@9,V(PQN=3:RRG#*D,(4/`$#($#"%#P!`R!`PA0\`0,@0,
+M(4/`$#*DOH_T<YR&_>'4H]6C]"@\2H_"H_0H/$J/PJ/T*#Q*C\*C]"@\2H_"
+M\W<;G)JLFHPF@\EH,IB,)H/):#*8C":#R6@RF(PF@\EHLM6G\LIP,AP,)\/!
+M<#(<#"?#P7`R'`PGP\%P,AP,)\-7C%P9F8P,1B8C@Y')R&!D,C(8F8P,1B8C
+M@Y')R&!D,O**$9419`0804:`$60$&$%&@!%D!!A!1H`19`0804:L&*4R"AD%
+MC$)&`:.04<`H9!0P"AD%C$)&`:.04<`H9)1_&/?/_6M:_BZ<UHV]3\MV&MX.
+MQW[YJ`]S\Z7EW1[ZEVY9Z++YK,YF)\.YKU9]=37U3\_'<U^=^^K<5_]57S6[
+-]J.M_@#AP6KE^0D``&Y9
+`
+end
diff --git a/usr.sbin/pcvt/demo/cowscene.vt.gz.uu b/usr.sbin/pcvt/demo/cowscene.vt.gz.uu
new file mode 100644
index 0000000..7a5a4dbb
--- /dev/null
+++ b/usr.sbin/pcvt/demo/cowscene.vt.gz.uu
@@ -0,0 +1,90 @@
+begin 644 cowscene.vt.gz
+M'XL(".BA?2H"`V-O=W-C96YE+G9T`.U=P8[CQA$]&MA?F`N!R6%6B"QU=9.4
+ML`?;"1((1GP*XLL(&!C916QDXPT<(-Z#H&\/V5U-=C<?BV4@<)RD%XA":JA^
+M[W635:\DLOSP3%\^/']&[?N'Y_X-N1]>/3R;XQMS>3UNF+A!PT8S;MAQPV^Y
+M::N-?^R&C=WVOX>GXU]_?/_P])OM0R.;EXG.AXG/`1&*?*8/)I^</WJ=/WN;
+M/WR;Y=R;.,!3,L)3,L0^'2,9Y'!-1IF':=)QFFR@=*0F'6I_2Q@UZ6#Y:,5P
+MV7CY@/N479,/68Q9#IJ/6@R;,RT'+D=>#%V,G0U^W>>L[\OA%^,O`?;YBN<0
+MMUNAX(Y`EB@9S&&!TRR`FD.IIL%0`"L#:Y9H!P#7+)0U:X`(,8<$F`T`W35`
+M90[;)Q#*?R5;2+?@BP@CQLT.3M3,F=$)H1-")X1.")T\>C%AA-`M0K<(W2)T
+MB]`MTFX1ND/H#J$[A.X0ND/:'4)O$7J+T%N$WB+T%FEO$7J'T#N$WB'T#J%W
+M2'N'T'N$WB/T'J'W"+U'VGN$?D+H)X1^0N@GA'Y"VD\(_8S0SPC]C-#/"/V,
+MM)\1NCG"<'.$\>8(`\X11IPCF@`>M^2`0QZ.>3CHX:B'PQZ,>P8&/@,CGX&A
+MS\#89V#P,S#Z&1C^#(Q_!@9``R.@@2'0P!AH8!`T,`H:&`8-C(,&!D(#(Z&!
+MH=#`6&A@,#0P&AH8#@V,AP8&1`,CHH$AT<"8:&!0-#`J&A@6#8R+!@9&`R.C
+M@:'1P-AH8'`T,#H:&!X-C(\&!D@#(Z2!(=+`&&E@D#0P2AH8)@V,DP3C),$X
+M23!.$HR3=$0<:!DG3T/=>OFB^>V''Q^>SV^HN]P"K^[R]?B9P<V-K^1?K7]U
+M_K7UKYU_[?WKR;^>_>L0D/W_A4\/82F,%08P8003AC!A#!,&,6$4$X:A,`PQ
+MB<""[*4RJ\Q^)F;^^O`7(E\?3;P^FCJCE5F]/FK^J,PJLYH_*K/*K.:/RJPR
+MJ_FC,JO,*K/*K#+[.9@-=-SE$'[H;L>-\QMK+U]]^/#IIW5**[/*S%\@3;Q`
+M&KY`IGMCZI169O4"J1FD,JO,:@:IS"JSFD$JL\JL9I#*K#*KS"JSRNS_FYE[
+M8_O+U^%9J-[_0D]C]OJB>?_-]V__]MWW[ZK)J\PJLW"=-/$Z:>)U4CR+66>V
+M,JO72<TGE5EE5O-)95:9U7Q2F55FOY1\<AP_C)Y*AIT3"'9.(-@Y@0QLV8([
+MQN"6,;AG#&X:@[O&$'PR&G9.(-@Y@6#G!(*=$PAV3B`+YP%V3B#8.8%@YP2"
+MG1,(=DX@!^<!=DX@V#F!8.<$@IT3"'9.H!;.`^R<0+!S`L'."00[)Q#LG$`=
+MG`?8.8%@YP2"G1,(=DX@V#F!>C@/R\X)OKW&PZ/]W<>_O__P]KOO_S(^.#.\
+MWX7W7?G^R!EV6B#8:8%@IP6"G1;H!.<M[[3PZI/0%JF[7&^'\6F&(1K]X>'Y
+MJX?GS]IO.2*UE^LP]H%;+;G+-<WC]HTSEP,W5QJ.W.UVAU<Q;EW#1LSXCL*1
+MSH^R&]B%8X<@3/Y8$X\=`K*]E'S>O_(_RIWF=G-'?WSS]/GGL674J([GA<:]
+M89:8]\!F%[:ZR^X6-MWQ,K$]7@XA\-(Y;/D9W(4>#,6LG%(63.GCQT@IYT`!
+MEV9<&J=L4F["W\W\=S-.5/R[&SM"Q+6_-4U<\%M"D6?_Z%N[#8R'_]WO/-'#
+MXAR:G3]?=E<PH7TJY91*.6=*CBF3?F;23TQZ9F)#QHISW?#F\/$KFLN!P#DE
+M<$X)G-.I')\]B[#=!-L%V%+7R?.=ELCO3$ODI:1+=-C')1KF*2[1_N#IN="3
+M(\;.B4([46B7%+X-J"8]5_T><S!^CSF<PD[$<#.&FS#<O,Y39ID6V7(?P/T5
+M+?$(36F/1K_'1/Q5&8F<PTXD,IR:_&!32-97WO'Y,=#Q)[!_T\R+'1!B:S=_
+M6K^*%_5$@\(>T[!^+SG9;,9CHM#$YBG#Z3P##.%I6LJ\M5XX"X=K^Y!,4,1F
+M)C9CXC(F)C*9@'G;]XM)2/27^3)\?`Q@CX_\L8Q`$Z^2XR4CRJN6,&)^+N/7
+M9OQHXA=:E(ZS[1N+YHGHW=L&)J+Y_6K7*[-:?-4O\RJSRNS?<9UP_FG+=-S!
+M][WW2/M:S]UOK].5QXUOKR\O+[]NLI;*8]T3&]Z^A'^WJ=?M4_,ZCO^K"'";
+M$9X!Q`K&`B1!26'N,TX"U$"D-:@E5@J6HC4SW$N&MP*XB@@@,\P4].7>)).9
+MPMX`[@T"WP#R;04ZPWY))#<Y>H/@&XS?(`(OJPQR"HG^_Q2%;!H*$F\QBS4:
+MF(=`I&"234?)Y<,:F54V*W0D/B6A?'(6E%8XO9-(K;$2:2UX%5-5,OO]&K4_
+MR]Q6R<GLEO3*B2L)-JL,O]NBN,YQ@R1@N9C&DN<_UHG^<YNI0!5R?2V374[J
+M@J[`]P<-88DQGMZ"<TD9S'!)^AN)]3L=;9$W)/ZTR1Q-=\G]*Y'\'[7L9?KX
+M7%D(X.\1"A%P`4H9>UE'HQ>RH63E'%IJV0$E>$%*+7_:$O-3U&S)63NU%H*@
+MGI6U603+;4D_2=.F*+Q*I:P]"E&+A;H)PFZ;RFX*:;=-;;=M<:6Z?:JN>5E?
+M-DE?LRVPT2ALMB4V&HV%R"%@3")I(7):0Y)$TK9(THBD;9&T+9(6(L>@.(FT
+MZRMI)9%V6Z35B+3;(NVV2%N*'#7.(MWZ2CI)I-L6Z30BW;9(MRW2%2*]QEED
+MN[Z2K22RW1;9:D2VVR+;;9%M+C)HG+Q(M[Z0G:2QV];8:31VVQJ[;8U=II$E
+M3AK[=8V]I+'?UMAK-/;;&OMMC7VJ,4H<WTA_P[F//[>LB)Y.WO]2T9-F+WJ/
+M1)_65_HDB3YMBSYI1)^V19^V19]FT;/F8+Z!YO/Z0I\ES>=MS6>-YO.VYO.V
+MYG.B>=<L-.>2Q_]&SZH9/(IVZ:CP2T>583HJ'--189F.L_)9^&-47@@W@G#9
+M)VJ,HLXI:JRBQBL"L]@\1N&%;A+LL6@=C<([&I5Y-`KW:!3VT2S]XR!['W07
+MLJVPW**9-`HW:51VTBC\I%$82K-PE*/J(+M0[83%%MVE4=A+H_*71F$PC<)A
+MFM)B>M%>=2&Z%99:=)M&83>-RF\:A>$T"L?I_W-5^T1T<"C>JF2:.V&A1?=I
+M%/;3J/RG41A0HW"@XS'[1#.;LE%S)EGPHD;T949AS(S*F1F%-3,*;S8<$XK[
+M(#G:4+^7*#X)BRR:,J-P949ERXS"EQF%,1N."96^UQ@%>\6)X+.PQ*(C,PI+
+M9E2>S"A,F5&XLN&8W12^)KUCCK[/-^+1<7V%271CI'!CI')CI'!CI'!CE+BQ
+M6>^C7^^HUW:7>_-+ONW!4VSNOWB*=1;K+-99_)^8Q4]\(A"J4Q*K4U)4IZ2J
+M3DE1G9*B.B54G8;OAJ>\)Q2E)/^>H?E!0_>+AN8G#<UO&K0F=U(KU*(DUJ*D
+MJ$5)58N2HA8E12U*%JF]#WO!PI%0@9)8@9*B`B55!4J*"I04%2@YH/4^?3=.
+M0MU)8MU)BKJ35'4G*>I.4M2=U"ZEWN>?`4@H-TDL-TE1;I*JW"1%N4F*<I.Z
+MA=31D4]2A3*3Q#*3%&4FJ<I,4I29I"@SJ2^E^MICDBK4ER36EZ2H+TE57Y*B
+MOB1%?4FG0FJHLB:I0F5)8F5)BLJ25)4E*2I+4E26=,ZE!J635"O4E%:L*:VB
+MIK2JFM(J:DJKJ"GM,9/*2F>I@FNRHFNR"M=D5:[)*ER35;@FF[FFJ'26*C@F
+M*SHFJW!,5N68K,(Q685CLJECFI3.4@6[9.7[0#0W@NCN!-'<"J*Y%R2Q2[/2
+M6:K@EJSHEJS"+5F56[(*MV05;LEF;BDJG:4*;LF*;LDJW))5N26K<$M6X99L
+MZI8FI;-4P2U9T2U9A5NR*K=D%6[)*MR23=S2K'26*K@E*[HEJW!+5N66K,(M
+M685;LHE;.BQO/;:"6[*B6[(*MV15;LDJW))5N"5[6JM9IR?R5U=5=$M6X9:L
+MRBU9A5NR"K=DSZ)4)[@E)[HEIW!+3N66G,(M.85;\CT,[NM2!;?D1+?D%&[)
+MJ=R24[@EIW!+XS%W854%M^1$M^04;LFIW))3N"6G<$O#,7?I!!;<DA/=DE.X
+M):=R2T[AEIS"+86>!^M2!;?DY'MG-3?/ZNZ>=9>G3:GC,5M2!S1?B:](%=R2
+M$]V24[@EIW)+PU$OFU+;R\=-J>TE5.(K4@6WY$2WY!1NR:G<TG#4MM1.(;6[
+M'*1K57!+3G1+3N&6G,HM#4>]WI0Z'K,EM9?SJN"6G.B6G,(M.<DM-9-4R2VQ
+M4LDLL5#9*SG!*SG1*SF%5W*"5V*1@D]BC8)+8HFR1VH%C]2*'JE5>*1VW2--
+M1UF$4ZYENVZ1^';^HRQ3\$>MZ(]:A3]J5_T1"S1;Z]BN^B*69V1Y@B=J14_4
+M*CQ1N^:)$@D6X2Q6<<T2L<BU7]N"1L$,M:(9:E?,4)-J7#-#+,YNKN"*!V)I
+M%DL+R@3OTV+OP\*<0IA3+9[;7CS\/5%L2]@ZJ#`(%!Q/"QT/ZX-^AW5!G\-J
+MX'=!+`-^!\0:6J@AJA#,3`O,S$M4@:P,B^A4B]-M+P[ZNH=%=<+"(-/"BH!G
+M83W]NIY>OHKZS:L(?)?#,GI!!C(D+&/I$UC%THJPAJ7_8/9X*!?^D)-FSB>!
+M\]);,.-%TF?""TL1)WWA(^)T+T:*L[QP#?$B7IB%\#3<PB3PHW!EXN:'X$IG
+MP`^^E6:`'W5#@[CP=@G9^K?7IK0KDCR?`EV1?9EDGM?CPWE%)F>*8`07WEU,
+M85=DZL"LR,],C/#T92F9>64IF%D1GK@LR3(I$F8MRZO,+$UWS"I-HDPJS9G,
+MJ?R@"V\5=/)L&&BD29!9.#P_2=9C(DF.8QX.STV2PYB*D+:Z)&TQHSFQ,)<Y
+M0S&5.2TQD_PC+KR149BS3H"=\PRCQOC/F%,^8<@IB3!B>K@+NRG<E`\"VI0"
+M&*S/P6*P9ZP8X!FJSZ%B]&:D&+(#4`S2C'/*<3@<,PS_D5'"'F-PL&4(CK`!
+M@4,J`YQS@!`^>?SS-*+-#W1A=QH_!,3PZ&B(@ORPZ'$:P?`.CQUV^&G/:=N%
+M;1ZV]U$LC.J#%H>J<8?SD]^,3Y;&M^V\Z>;-UF_.P\5NI\=I;QJQF7JRIGLV
+>.?"2C#BW34UWQQ.;K+_C\C-JOWWU+THZ&OFDJ0``
+`
+end
diff --git a/usr.sbin/pcvt/demo/outerlimit.vt.gz.uu b/usr.sbin/pcvt/demo/outerlimit.vt.gz.uu
new file mode 100644
index 0000000..a2b6d1a
--- /dev/null
+++ b/usr.sbin/pcvt/demo/outerlimit.vt.gz.uu
@@ -0,0 +1,193 @@
+begin 644 outerlimit.vt.gz
+M'XL(",2<V"H"`V]U=&5R;&EM:70N=G0`[9U=C]S(=88O#0R"Y`?H9@#?V#<!
+MZYM<76FU8_>LM1IG-+*\WJL-D@M[K37LK&(;0?Y[2-;[G"*[1^.6+4ZV%RT(
+MF&)WL5AUZCWO^:ABUS]=//G)IT]^VET\^:I[^^0K__F3K\KXU_FGL=M=UH*;
+M"V%1F+_JCJPW%MSN8I-V_4;MAHW:C1NUFS9J-V_4;MFHW7ZC=H>-VG7=5@UO
+MI7%N*Y5S6^F<VTKIW%9:Y[92.[>5WKFM%,]MI7E^UCP_:>#=[NKVZFSVMC9[
+M<=+$ZU=G06\MZ#SI^<N;N]WURY^?I7WVYM;>7/_4Y]V;VYLS-L[8V,>&BV/[
+M;Z[O=F=H;`T-EY\&O_ORYO7M6=:;R[J??/6[JQ=7O[I^=7WS\BSQ,_&MB<]W
+MTP>OKN[.T-@:&G/(-=[V"T4$M3C[K+4X.RBU.-LC%6>^5+F?;OR?IM#_RPQ.
+MWU^<Y_`Q")69\T_]."\_#I_=//DJU'*<RN<YV#J<#K/<QT#OR5>I7L3YXBSY
+M;27?SPSVX_!LE/50R_'9"<K=O9W',J/H[NJ+7];AS#C2Y=3B&4T;<^ELX\89
+MN+F9KM)\%>O5R2&*L;Q9C>7-28_E1DY%FYH3]3*<$==8-N*:7:=*`I^-92<&
+M^*R9^1^'S^L-4SE^/OMB+D^?OQ[+I9;CZRHSUT]?O)K%-9?CJ_D&/S_Y;KYA
+M+L>[LZ/V",[V+.J+4?2SR["\RJNK?G7ENO7E^DZWOM6M[_7K>[V7'NVNQHL@
+M'=J=Z(++.!H_#^>7U\_O7M_6(<V?1/OD;#7/"8:#!,-Y7\T9$_O9]NYIZ'9O
+MSDO/9V3L(\-//M*S4]R5,+L*/]??EQ3FE>C:S/3HN5TJO[BV:B\6Y1LNYF<>
+MW'5[LZQ\M[Y\N;@.]]Y^\W+OEN?MDPN&M7?7U(S[Q?.]6_T]GU9YU;O"O7?%
+M^^]RW)7NO2O?\VE8W%7NO:N__R[KX7#O77.6=O7QV5\_$]4^41$AWMT^>_GJ
+MB^M7=7G-@L7EQV?X;)]+&J=EM!LG:#7\T^QV_M0Z_J-SA[>'QHAIO_OB-#L>
+M=L]/L^-Q=X)+>.<@^^RAW!-*.7\.LA_#^9A3HV^N7[PXR_JLA0=:&'IB[.5C
+M7G+[W<./>;C>(3P^6KM^HW;#1NVBBLGO]A993D/44R3@=[N;V^O?C&AY]H,%
+MBY1BF/RLJ9+?W=;O/``9K%(_>9%3I3#7#E-!E7JK5%#%N'M1*T4J%:N4J93H
+M"H7I*U5*5,I4HC!]I4J12H5*%*:O5"E0J:<2A>DK5?)4&JA$8?I*E9PJ):,K
+M4R1GE6"CA*"M$$SB?J"2U[0D).Y-XKZ?93A6"KM=K83$O4G<%RK%2=&F2DC<
+MF\1]WEW72@C:"MXD/IG-6@E!6\&;Q#T23PC:"MXD[I%X0M!6\"9QC\03@K:"
+M-XE[))Z-R`WE)G&/Q#."MH(WB3LD/NDU\9R]W4>EGDI@/(?V;B&5P/@8GMP2
+MI]B;C50"XSG-H@]3P=ZKI!(8SWGWFUHIM[<ZJ83$<Z%/I;U32J4P3_Y8J9_U
+M/4P%>Z/ULGF@M=(@T.6AO4]+)21>YA3.]);TXFU>*B'QXJ3FQ;5WB54)@1?D
+M;`63-^(N2-D*)FV$79!Q*U`%41<D;`63-((NR-<*)F?$7)"N%1J34`796J&A
+MFBI(U@I-PJK2V^OBS2!<7AC3_ZUJQSWLB"X?,?`CQ'?$)!PQE4<`X@A8+>%Y
+M^3YX+H%^^5Z@(V+3E/M4!B&;SMVG?`LUOGRO&B\(X?*]A+"@ELOW4LN"I"[?
+M2U(+NKM\+]TMB//R?<1Y%`4?1^9'F86C#,Q1I@J)/VCTD/B#YA.)F_V]SQ`C
+M<;/D]YGTA7-P^3[GX"@WXSB'Y2C7YR@GZBAW["C'[B@7<>%L7K[7V43BYJW>
+MY[8B<?-[[W&`_T:8>5Y8.N<;]O(-<;H^)_T>XUV(44W/.;^S#IYS?O_?.;\\
+MJ>))YOQ\-[D-O[JZ/<FNCQ_<79]FS_WN^0\[P^JXT_%=7!14J:-2QW=I4:B5
+M!M49^"9/A:M:Z';5%(Z%Z_EOS<T4ZA9S4]6*?=,O"K5*5I7,-\.B4*LD5:&7
+M?N[WM8'1_)_Y[]Q+S_B]HQ6$BM2\7Q1J%<C>O@F+0F<I$VWK=+]85)W?#`Y[
+M%ZK%PW7+JI;?NVBWQ,4M<5G+[5VT6]+BEK2LU>U=M%ORXI:\J&73P$6[I2QN
+M*<M:_=Y%NZ5?W+*J5?8NVBW#XI957_+>1;O%=8M[W'+,+NU=+&YRRYN6LG5Q
+M[V)QTQ(";CF'+NQ=+.1V]A?/_N(J9NM/,F3+4X;P^;,3_%69?DKL/=\]>_GS
+M<Z1\5L=[WDYRNY-\4;-&GC^[>?YZ]6N3S[GY]<,/>;C>8><_6KM^HW;#1NW&
+MC=I-&[7+;]S%?G=W<WK(&+V[E->[^T^DX_VTO/7JYF=W9W7<6AW'<*:XW:<O
+M7M]>G(5]YK[W_9+_1VNW;-1NOU&[PT;M[O^2_\=K>"N-FW[)_Y_GQ(S;??V1
+M_XT/J$SA_0:M;\ALT^_?G%1_N]/J[_1FZ$GUMS^Q_I83ZV\^L?ZF$^MO/+'^
+MAL?O[]?TX^N'^[NL9_W=W+Y]33^^?KB_#]>S_KH3ZV]W6OT=3JN[_6EUMYQ6
+M=_-I=3>=5G?C:74WG%9W3\VNG1B1G4Z[?J-VPT;MQHW:31NUFTF3C'[P^*^F
+M(,;RS6W=VJ//SZ#9'#13;J*?A7U99V(*_L</7NV>W?[RZF7=R;6L<9Z3[><D
+MSR_5U.E(<_GZKF[?"V?%.+/IO6PZ!HT)T`QS\>YFWFF73A<R\WMVC9KR?/W\
+M]LM7=\]>S-L;TYF8'I68WDX12%Y-2GW]]/F+9[?7=U_.FV'S>5+.1/5^HMJ@
+MW;)1N_U&[0Y;A4/=5@UO%L#9H3SS#P'\\8]__-U$(7VW^\L4WXY_OYD#73]]
+M]1'_U>#Y]S7D_XNMW(;555Q=I=557EV5U56_NAI65W-N=W'IUI?K[KAU?]RZ
+M0V[=([?NDEOWR:T[Y=:]\NM>^76O_+I7\^+YVWH(Q$>>EM_5A\R/G#!0^T*I
+MHS3VGE)OI6*E;*5DI6BE8"5[AK-G.'N&/<*>8`^P]JUY:WV2U'<?5R:7JW_O
+MA-+-'Q(?XR'I,1Z2'^,AY3$>TC_&0X;'>,A,@UL^Y1WDNOE#_&,\)#S&0^)C
+M/"0]QD/R8SRD/,9#^L=XR/`(#_&/H?'^,33>/X;&5R?KOS_JO]^M/;KOOOVH
+M_]ZMW<?M6N^V;+WJPF:M]YNV7C9M/6_:>MJT];AIZV'3UC?55;>IKKI-=753
+M5=U44S=5U$WU=%,UW51+-U72;75T2E7]^:/^^V8A\Y!VWS8GX=OY8/6T>W?9
+MN4_&_]Y??C=A:JK5[O^V_M)$V/E.!3=0Z"D4"IE"HA`I!!IRGH*C0-,=37<T
+MW=%T1]-=HJ&.MKM`@:8[FNXZ_5!&UWV2!NY,-)]*!6,GP<PR&:_>7?[LYK:*
+M8_IN+8E$5Q*C3/0DT9-$3Q(]20PR6C<BW8B,,M)TI.E(TY&F(TU'5SN?=ZWK
+M>7JG61W/NU6W(QT(2#GP_,#S`\\//#_P_&`3&.A`8&R!ICU->YKV-.UIVJ>Y
+MV[[;-9&/%R^O?GU7.SY>[$&//GADX.F"=\?`\^(8?!X#SXMC\'D,/*L(TE($
+M:;>[>2W0C1=K$50<AU)Q?#^4CX'GQ3'X/`:>%\?@\QCD71P#O6.0=W$,](Y!
+MU(74_MT<H_U^-N+3U:OK&:#]/93PO2+'6?4),6OW\^[?7E]?W;WX4@.HU+!&
+MV(>P:>K?RZ;?/XKL5Y,YU*MG+S^;9.&Z>V;S>\64M8?OU'OGZM6;F>3K;U)_
+M^_TERZGS:7?Y5BPW]7Z\G'Y1D^Z+Z!8X_`C4N@?6\@_QY3@&[W8+KVD<Q?B!
+M?OI1XQ@_.-"H,\<^R+$5O*:7=55[5$QA(W[?:?;O8,W\7M;4[Z\O9#%Y<L_N
+M$$7>?8]]4/T&N_5]O/CRYK6Z+LWXGI)K7&-0SO^KJTJO]WG_/TQG=`^HZ1]C
+MS(7[]%W-5BY-[GT.U)DK'^;*A0_S]MN:O)ZN=U?/QHCA1VRE__:'%;.G>$)>
+MYO>/W#X:7<V)Z?HCE^7MR.S:^/7V`#AA'R4-'.6`D<(>_XSS'9;S70XF-^Q/
+M9:.&LB2"^?"9^7C[9[=7>YUML^<.IJH<3$S8GX59K>-T:D25QY[\TX&P35^&
+MNFY3CZ,_4,*'1%CWQ97E$Q\4)]+TDS3M`)PJCT]O7M_M2:1I43C0$!-KO."T
+MF$7WFPC[`\GYI<!\G`XM64HL'PBJ.Y!)U(]GYY7(W`'=I#63C"-WBY$[06'Z
+M3;2_;]QI;[J/'_9\G,JB\T</NW^:ET\\:M#=<M"E#OKJU[^\NKV^>OE\7PV:
+M4KF#,1OHA[ILE%<3T,;J#N`?EF"?CVI:#B(=C*:LP=N-"FV#2-/!,XOG'M_C
+M^3B<.N>[_7$?W?O1PGKWP=WOZ7Z9SB#ZNWK?34=!+9Y[?(_G(W>JFK_9'[?9
+M],;(D4ZOPN-8$TC]2L^'?1O8.#.N`.^F7Q]<ZGD\L"SW=*0ZXCFT9XZ]6L5*
+M\8&>V,D^=>@O/]N?\GA@.._IU;RTXU<D=8^HTOV=JAY:*BNA/20KZTC=`%+\
+M"FG#$9,6%]H^_^ST./8OOGQU=W7[Y;ZJAP/$N7ULS;,W3)NTEVP5#KC)K>EG
+M[$>P?N3IQ*;%,(Y^;B6[#WOLTK9%:?N;W?7SW=\[>/BF^Z!N-$,CNG`?/'KY
+M!!_VW"77)QFXVZMGSW=7K_8%X/<=V6:UW!*&HNM[?)-NG^;"R-+Y8H5^J6^S
+MD?Y@L/V!2Q07L'&'K@UA0!A9U1QC<\Q1_"S.^]GMS1?[FN\.9)K63QD;+R9*
+M:7`#L#M@Z+1/P6[)V?<X*2:RD<Y:C%J68\!+:(Q_3+>3==M-)Z/4A\\_0__V
+MT.HUQ\8=."IA.1MIS]LRQ(6XG/*R#Z4E?S0D=P?BP%D)83FE2Q8\]#/,:3)$
+MR@L:FUG20.A74KA^^7(Z<F7M_`_[G&Q4;*0[^]NR@!W6*+@%\3_8RL(*NCU#
+M,C;3_8UF%E[VH3FP9OQP?S,7'#^WE,,7UP<6T1\D`12ICTWW2YS&A>_>)N8@
+MD'0'D;LOG\0Z,3CP#=T'4:O+^VCSH[;$.J\U\&E6]2#UXO<R$N/=2]V(W5(:
+M\XF2=OWC<'?#69+MPSA]N(?S@[C.$67YN.?R-0H\YFXSGXHIW0?=[1<#S?F#
+M'V[62Y[/ASW<;-#1M[@5;N^YX6)_*MT(QW*0VC."=/T^<MQ(Y_YBJ1'KP+SU
+M)2];#@>*G/;#:9<^B:6-U_#7=`AR<W&J>42]L&[1K9\]5O!+>84#O;&NN872
+M+@;M#Z33?1+[P^?A5';#4B;=P;18.UV_&&&3&?WIRH+=;7H=MJO+:^1T^YIL
+M$NI&F8>#3%;KQBAH,TK>.F1&L@O+;I:#K_W#7[O5]/@#ENI&:<:+?;1/G?>?
+M+PWS@W]/JN*=3@.O)N;!OQ>G5/'F]=W5[<)X/OCWXESQJ(HOKK^XOGO%^;E_
+MZ^_%:534F]&Y'3!>7S[526B#CCNK)X5/BS*<2U9/_/:<ZNT]QW-[CN?V','M
+M.69[NJVG(9K6D=B%8Z]3.]J:,ZH[G5'=ZQCJK).FHPZ3MO.B.1*:4Y\YV)D#
+MFCF?.>H(YJQ3EH=VD')`!`$9U$.//>?.Z02S.M*(%.IAR=-WD>/?$J?)Y=4A
+MSX-.A,XZ/KJ>-3V[ACJ96L=8<_AUT@'9P^)8[2SA)>1:S_7V]3CPZK-G!)O=
+MZF3RS.GE''6N<]$3AZC;V>O#_(L8=:0ZSWW@]/=.9\5SP/SR+/JHONAH^_2T
+MU(:G%^4UH87^%LU8WZDN`%-?U54[HURWU<.MI^,3=&!@7V6IUIQ.Y7-N<0I]
+MQ1T3I3/DLQT&WPFED?/>P:@@&H30`D"S8#FVZNF7SCGWG',^#:27Y`3.U,XD
+MK_#L=;1X$CKM@'`[Z!MX"IV<UIV$S7)P).)\,/==/6(N<&1A1&:1WD;TLT+3
+M<VRYYY#S"5FH?N+LP\3PD_33CG!W@N@@A!8!-`F?'%)O1]N#S1F:\TBS1%"Q
+M&`3%-"-QQNY015:08>$$<N$OS?BKB2@!L`>`PXS`>5@"H!,`ZVO[(BOF4G^#
+M:3LB03MH7?+H=50EIZ0[9L5IGIW7J9%.-.1JDX!1A.F$1*-+(1!J[(T9@Y#G
+MH,2)4`N@9"X]0PP,,:!E.M1^L-/I,^`+G$#?"7U9Z`M0(\?!@SZ!KQX*7X]:
+M#!SG&3F&$]D)>IV`-J$):56@)7!F,!/*(CSHA+*)]9C-BJ_I%UNZ6C=+].*\
+M828]$6325Q*Z6*\(:U-!`RSH0.$X4:%N!MT\HY7T)C"@7,)<%.:B(`?B(I0G
+MP$T$0<^BD"QEDBH($VI%C3@==.HX)U48FXRV41TTY4"J&,Z+XH8&,)&Z*,Z`
+M)JISPEL0W)+0UL-R9H(C+%<PPH-980?1P713`;%5L'GCN@ZX]>`M+\YU<;MK
+MY46\C&N0<156HOHKO(GIG(C.`3^,L!?J@HQO%.BR0-=C>SM1F\/V.LAMG+?<
+M,0<9NY.9C,R(,^#-D&\VHXBG43@RMMBLBN8*B$NP7`!Q3H@;A+@BQ"4A;GJ?
+M2%D2Z;%$(#60.:]596AD9YQ!5OU)0MFT)PK/1^Z?6[A_#,QA5>3U#7A]>4:<
+M*"T(DY*\*$Y0PZ`&`2P+7P-<%HW+>N#E%O#"M1&JL*$<&6PG#@M1;D94C8HQ
+M<A&S$L%JI+$*)\@LPF%)</+S\H:(-@F:2:HD0'7"41:'3<#0"#.><0-1,J3)
+MXHK%!*8`AT%BDPW$P,ES<\)4Y:ZZ=`((-/WRTCHX*S\%F%ZB,+*K/>U%BW)Q
+MA1M82NZ8$WB"L),%G0$[6`,'X0P6</CC'O%XL]B8;(^T/;0L\YAG%-45GHJC
+M03B*PD\0.3DAQ`L@<>\<YX*9FV@(1S?R%-%,#S(&'"HO\^9A&N\7V$BX&@ED
+M)5@PT9!8IN!#99RHN,!'T'S)VO4R=EDPB8))@'.:J=,,F5<?97R2M*`(9Y5C
+MG'GP9L^*8<.&!=[T>0]FX<M&*P*ZX2/)/Q(.G;P_SPW>X=2V*8=^/(%7G>@>
+MT^1P<7I<G*RYKB;FHAV,[3I"O$Z6Q0([YAJ3$C$E0<H?Y@F>999P/I+%,5(P
+MZ;QFUR:W1_<=<YKF.:U3J2G)$+;:JE.:<5]L3@OVQ&*TP7QDI_D,YIW(A\.<
+M`C'U!B('S*ZSH!\K[_##Y=22!ACD?A3-9D"!L0&8@(PST>%#H)MU<57\G4`9
+M5B;0N3#LG8SN%^>I!Z8S<K`\XHJ07))/E:3H%H\3CLD':+,GY=&D22&#YBY)
+M'8MF;C#69NX<^NAL]GK-WF3[;?:P9@4O6A:_6&1M7B9N9B^U)+!^*OV7VT9E
+MHGMG^N1PLIU@12SB9)T\U"^.+3+0`X''K&]S'"`/L&@:/7D88HQ`@-MCGWOL
+M,W99YYG+V4/7(X^.2F%(*:63F<G-"_I%L9(S0ZRH2CR,EC:7KZ#8(*SE5DAZ
+MB&ACBR9D^HJ\E")J*R"YX"\5"*N`9,VFQP![,[S]4_.7[,A$\93Z*L35D?2R
+MYTX&WD&)#H?%P1&>!W@S[;@'C5I[''@/.`->1<`(!3C.<FM2^R"UERZ:_606
+MR4MHTI*FJD@/!YPI#*;#.7?5*Z^X30`H&W?A'V=<]ZP>93THR\+7F2CS1%2O
+MHZ"-!<^U!S^65[&S(*MZJ-..=(GR2\&,F`?07N/R6!+S;%&M0-0@H7:DA`**
+M4E/3M0Y3&=5NE$,3I6Q1>(Z03BR6$C+O@BRLA-U)V%FR#M(*8NXD\NLMZ('^
+M)LF37<BP;@9W+7_CI!^*MT1R.!RN)0\EG%[NFNZ3[EJF`6L),>ZTG0E%8AZ=
+MI7LL-4B2R)$(<+"'P\?QG:6D92&]N+VR8)+[$36IP:*6H&1N9;K*;SJXFWS1
+MR'3*V3'_+:V,M4LP3R+:D3)89)%EGB*^(]-`QL,92?4MTUMP,PJ2E(M`%BTT
+M1\\2@32+I0#83F-UBC_=(-=%-@-IQ1;;H9J>#GF+^LD+!5G54#N&"^"0Z<1&
+MYG9W@KJ7;R9JCQ)-E/V+/9K>[`*TE["R"3")6R+..)8_"?*1A+DS1SH@V4$0
+MS4]M$M"`"B2UY-228\G"(2HY$Q8>(_[FY[)VX4E@>>QZZ-#T@((O#VB_FA59
+MJ;($7!*>7*-9@Z'EN2U`R79XK62N(%6H&W!'>RP??DR$;P<@UC=SYV4[\(91
+M2%LN@*:<](]T?D:-![27K`/$2Z;!68K!MX4CUH=L<2"0.8Y8O`CS1\`;95>4
+M11>FTB)GU$-'&91EC&O&N\M05?/?F;,BTBF*YG`4D-=3B5[1C".(<5A.A^5T
+M6$=;K!.U><OU&<0*$"/=-^FI6%\I98DGRLAG`JD)9[9&9BMB$$F2;Y2TV)-P
+M@A,SEC6K\IA[#`@+`=-2$@`L2N08=F11!$/SLHC:43B28I*M`Q0.FZN,@^"2
+MD$^PU1Y;:`P"3C2G)^(-1408L2[FFR86#K1XY\S![%K^FE"">%Q]*;+IA58M
+M7$5!L>P.[FT!&N-P9/J=C).MA+$"YN1U>'E(#0<D/SW)V6#B-X\0MIRV7%0=
+M,G0D'(*LK'_+F-HRBZ4UE9@I8+<(;(6H7<*R8)&F4)\Z*I*96+J6N<R6@Q0B
+MM<)GZ6Z-"7/N6$%V0TM<1RQSS*R#R+],6`GYB"U?@R7+MCYEJ(;+^\XZAT&4
+M#9/RJ&VYH6(")^_-*3YP%GHZO$!G"T:6+*,['M%[P;E.=\3_QZ[8JI%;R`-K
+M%.6I15NUEG%&',V#Z;%29D-8#B1:;ZB0V<SDN$%;4;;!$Q$;Y;$H3'*)Q3<"
+M7-N2@+6QQ;7`BE8`!W*MHJ0>];GY%?ATB6XEU"^99C:H`\#,`S.>1B854B0T
+M\Y<EH<(*C<)(T6"Q`-$6?S#K3GUV1!`M@(=E&YWA$P<8>31_NVKLR,1$S4#B
+M20FJE9?@\!)"H[I,%K1%T**6K-7&MNA&I+78/J`Q]E(DV,ARQ+8:18@E/\*S
+MPNX4K@<I43#C!'U%XN!H,T=7;95;6B'<LF!HFR0BSE]IF;?>HC\41CZ"N$U,
+MAK@=7.9HP.$GV/PY9MP65>3E-NVD&P%E4/I,$UP$:!QG-K1,)"9ZB19)69J$
+M-7Z8WW++(H="K%*P005_-+%$CQ-,<HEH"I[U&"O;8A'PG&S/SCA!MS-LK:\L
+M^H$Z>1%)$$NXD9G)SN0'L[D'!6Q9P@9QPT6VBFKHPK7W>K#',`6VLV"2;)5]
+M%/2K*FB3C44B<F`RLY<MRY'I3,%T%S&0V41];D$;0;FSM`[I:4FT+7"0_+4-
+M#[8@S:J!=#3S%.(>UIL\5IHM%TZYY/)4%L39?@49QQ;$#2`M$I1:^(8A[0A1
+MB)2342A?95D^DJV^^4B%[!5Y2<<6J>;S"2J.'6;R'90"2^IVD;MC^2_?ME1$
+M7(26:B9DB_*SI%1AL79`'Q-10%,ON99%3VR;$30;\A!*HWQB(@L,"0B\K;(3
+MM042*=)`9J%;,"+(T7:AME!)LQEMS9B`3-ZU6&:YH"4%(]G;1AD!4+,LL&D0
+MBGD6^[5PH#T9"0]@`IY54+0<A+Z`P99"1MOW$C%UT=*]NC<!BBS)9Z17>$PA
+M#6OKXMA41UW'/'KY(1X;[XGS`B8OL,(<+!,96&^;]O?.(Y+P;3-$,@Y#'!G'
+M*D,.G<Q>401<+-;'"N-M2ABF*9"2;:E;I,((IW&6;>.0):G)0-H2.D-.+&(F
+MV[65\6\M7=4,B4!:B+(TW3*[CM1@@HT<VU.TC<RS<<[V(5F^DW$ED7FRI[/P
+M)H';4S1LGM%2Z3!`<R0);#V9L`!S1GR6MOQD_@8>D2WT$K%8PMJKN:+5!G:_
+M8&_QAPAG:*!9*EA7/7-Y@;1A]WIF*1QXQ:"P,0DC=@O(^EHR!PVPU5TM<EH\
+MC8X$G-S(3E?A)3LPEH$2?K_E*?G"X2;:_A./[VWI'07L62H7Q-I1RD12C!2Z
+M6ZXP(<M$0$:T'J1PC:$SKKCXS-BAX-\76L-<]YI(BT4]:NT):E'-%FH1G;4U
+M8@U#N`@\QRTV;K#$+D)T6C'RA(-MNQ;A8#0+&FVBE/ZQ#`[1.Y$M:I61GX4V
+M!"RL9V*_7,(GR(B?C%`O2T/4YWQ;)A6E#=@.F$:Z:1;>=HW0,TO("Y7D)-CR
+MZN)BRRMN&UNQ;4&CL*9M&3;(S].QB`/4<AVLN;.%VHP^$&;:`L3!QH1NL91M
+M"62Y5*SXLKO4MB<'`A=VQ9&'9%ERDB.D56QH%FB@`F@<^9=`YCE89!8L><FD
+MLA8@:F+75E+@9<;5^#85]H?8AKJ"@U'DT13QN4)/BY9LT<W4R&-6/!&-)R7G
+M!7U3J!:L$X-B%@4NCV?J+=UFN2U+IV.4(N)O`"#(3$Q=PAX8A8AC+<?5-L9;
+M,I=-4[9LB&_#BHUMKDU,*DM!;/12X[;L3V*<ARD^=I:U[5J*P_8?6:[*(G5[
+MCP0D@H^A[M,D)>Y8S/6L+[K>=JRUC8UTT[;9VYJG;04(BE78.F\>AGJG/D53
+MU$+"LCF='9DB-H_"-J8!MFAHJ5/EC#RZD0A82>^((D)CP$+$X[2OQ#9!8'S8
+MBT62))KU4W1,:LNB8TOR=+LOJF,U+^:$MG<%TX-_Y4%A:NM:&:%9=I.=W#A>
+MT1A$HN&E%5O6B0LE50A1A$MP"G",*PFJ-)UX8^C?9%E)<UNL@ZPLE"(D!=K-
+MO+"9D%#.V`I;&*"+EE,0E49U/>$P:RF][8IA_8-5<=*1%GZ:$\XV3W+4MK_"
+M]!<)1M2/I0Q+?M@"',99GE%N(2ST:3E\<:,7ER%;L^&-\<WYL->\;`,6H2N)
+MSY9X,`\)@&FFQ$%M<0&4V>YS3U@2L!$M*<9NXL#>FX0.)$C-MA<49%U$FI:9
+ML<0!6W@*`U**TZ6VI-[V#6M.6^!NV4H;#%O#>.$@6X!F6HO<21`(.K8^"A-%
+M/**D&<,JL]8;;$,(KXP5,D[!7&=10M1?>]E+?I%+S4&V5P3(_;%!1SUN&V5L
+MW+9DZ^B:Y9NDXZ2EB9J88UO3(M)R]D*=[>EM\K7E+MPTND+(8*Z3GF.AK=:.
+MM0.Y;QE?>0@!D`1;;K,7#7E["J>RA;21%PUY]<LPPKA--?%GQ!QMZ;*'B"PW
+MP,J]O5%HKP9JP99=N;9BG4@[V99VSP*K;C$B;/ABUP54Q?.#+7@9T!Q!9Y;X
+M;,81N&TWMGTA;%7%-$3;>FCK<K8U`>_(-L@'WN0D@K=F9,BSV2F+HG6+A3OD
+M38HHQS$Y60(V2QL-:H$0T';D>U;&;)N`O?_UY">?/OGII_\R_<Q(_2F7\:8W
+M?_KM=]_]Y[>7__[7R]MW__'7R[M_O?ST#W_ZY@]__J]O?JO;+B[_#PQUTQ:@
+#^0``
+`
+end
diff --git a/usr.sbin/pcvt/demo/playvt.c b/usr.sbin/pcvt/demo/playvt.c
new file mode 100644
index 0000000..b34d798
--- /dev/null
+++ b/usr.sbin/pcvt/demo/playvt.c
@@ -0,0 +1,111 @@
+/*
+ * Copyright (c) 1995 Hellmuth Michaelis
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Hellmuth Michaelis
+ * 4. The name authors may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+static char *id =
+ "@(#)playvt.c, 1.00, Last Edit-Date: [Sun Jan 1 18:32:22 1995]";
+
+/*---------------------------------------------------------------------------*
+ *
+ * history:
+ *
+ * -hm want to see my xmas greeting ... :-)
+ *
+ *---------------------------------------------------------------------------*/
+
+#include <stdio.h>
+#include <unistd.h>
+
+main(argc,argv)
+int argc;
+char *argv[];
+{
+ extern int optind;
+ extern int opterr;
+ extern char *optarg;
+
+ int c;
+ FILE *fp = stdin;
+ volatile int i;
+ int delay = 0;
+ int fflag = -1;
+ char *filename;
+
+ while( (c = getopt(argc, argv, "d:f:")) != -1)
+ {
+ switch(c)
+ {
+ case 'd':
+ delay = atoi(optarg);
+ break;
+
+ case 'f':
+ filename = optarg;
+ fflag = 1;
+ break;
+
+ case '?':
+ default:
+ usage();
+ break;
+ }
+ }
+
+ if(fflag == 1)
+ {
+ if((fp = fopen(filename, "r")) == NULL)
+ {
+ char buffer[80];
+ strcpy(buffer,"ERROR opening file ");
+ strcat(buffer,filename);
+ perror(buffer);
+ exit(1);
+ }
+ }
+
+ while((c = getc(fp)) != EOF)
+ {
+ putchar(c);
+ for(i = delay; i > 0; i--)
+ ;
+ }
+}
+
+
+usage()
+{
+ fprintf(stderr,"\nplayvt - play a VT animation with programmable delay\n");
+ fprintf(stderr,"usage: playvt -f [filename] -d [delay]\n");
+ fprintf(stderr," -f <filename> file containing the animation\n");
+ fprintf(stderr," -d <delay> delay between characters\n");
+ exit(1);
+}
+
diff --git a/usr.sbin/pcvt/demo/sgr.vt.gz.uu b/usr.sbin/pcvt/demo/sgr.vt.gz.uu
new file mode 100644
index 0000000..099c3a4
--- /dev/null
+++ b/usr.sbin/pcvt/demo/sgr.vt.gz.uu
@@ -0,0 +1,11 @@
+begin 664 sgr.vt.gz
+M'XL("):V^BX"`W-G<BYV=`"%44U/@T`0/2]_H9<Y:AH;MMT5#?&`L*D8^Q&H
+MIYYJNU0BA0:HO]]AV18L&"=D=LE[,^\]&*Q-VXP&ZU=CL#8/)!1OPEW!-'"6
+M+[X+@9A[_LI?S,$3L\4\7`6.>KOQA!M.`W@"$;JP+&Q\1B/L<+@U#"**[>8H
+M"0ED<4K*.-W#/M\</^,MY#+=Q66<I0:Y4T7TV5NU)Y3`0UV)DR3@E&4>?YQ*
+M64`610V'(H<BAP(\9\FN`1@"#($QP'NZDWD2I[)!.:(<T0F.(?*%=AO00M!"
+MD`$$\EOF16N0VM7BJA->2PY[UE.;*Q9*W&M65X;:EB*AE*5)'3FF%E6=/+2"
+M]&QC:EO5R6.;V5G)%;'JA)J7],.^I#J%DJ?T*FUO(J8S*1]T?#W2(\+U1&UH
+F\OMC=0<L[<K2KMCY%_UKC%^LU5+\KSQGT6K:P/H!L`MLDRX#``#2
+`
+end
diff --git a/usr.sbin/pcvt/demo/twzone.vt.gz.uu b/usr.sbin/pcvt/demo/twzone.vt.gz.uu
new file mode 100644
index 0000000..df05d50
--- /dev/null
+++ b/usr.sbin/pcvt/demo/twzone.vt.gz.uu
@@ -0,0 +1,350 @@
+begin 644 twzone.vt.gz
+M'XL("`PIFB8"`W1W>F]N92YV=`#M?5MS'<>1YN-&G-@`?H!>VK$7CV-WI*Y[
+MMV%Z!Y8@'U@2Y1$XJ_!X7R2*MD00D"%1XO'^^NW.S"_KTET@'V;?K`@%#[JK
+MJ[*R\EY96?_Y\-X__>Z]7XV']_YLS(5/Q^&]/UMS8?WZ(UP8M_YKPH4-]&.Z
+MX`?V(DS<@AJZBS`>AZ6/Z<+2^Z4':FCMA9_IB;\(^-30-V:^,./Z8[YPHW0>
+M(_6R_+)1&GL>>)2.XT6DQOXB"62.81[I8^[64B_+0(#52I,`Z!B69:*&H$L"
+M0I)I+="B$WLQR:N8Y)6=I3N>FKN(CO^=91K>\[PL?3G>K?W\84'RV?+_?QK^
+M8_\[_$?W]P\`_P'@/P#\!X#_GP%<1?A\7,3EW=4J(DE.Q_GX0#\6D?M`+29J
+M\86V"%%:+`*<6P1J\4Q;I"`M'/J(U.*8^\`H(4F+U,!AC;2PZ&/1!FN3&VWB
+M+9J@$V.HR77N9<(X$YIX:G*I39P#L&B1FDX6;2?CS&C"./E8FQ@T"=)B@;J>
+ML0?6G'82FTX<@%T4KS296\RB24*31<?7T"ZZF)MH"Y[Q9QDI7EHL.E*:\#B?
+MY_7!"EJ=4&JPGS!E9]%D:D`Q6&5GT"0TRYP`K=%>>)D_RK``W(A>'"/W:4:N
+M$BTZB4T+"UHQC)9U:(-?7G\EI\]&_`KY[:S/K+;3E_@1)_PR07]ILZ#]6ATK
+MZ5NOWT9]ZW(O>0R=A<_M\K/<+O_2,4R&+V-`Q]`I)IVB_HCZR^11M=_(?:P(
+M7QE^78`5BR=A[RNV;O%@7A_,^>]P?+;^HW]/QR_(.-0'"X$LQ&=2\<0<%UJS
+MKGCBCY=DJ.8GT_%CM@?Q9.',(QO%VB:M_9@"F(5(/F;K4I_,*WRF`'"AJ(]`
+M*?>@_Q6>HLE\_'RU2?7!0K@+2<:B15P?%'-:&&1%5/'-PJV?D9&=G[AUG/)!
+M6E&C<S0RP#DOQ+>$HP66#P31KPDF_)V.+]BI\/)@I@=FY8H/9"E^H.&2_+VL
+MQ(_K/!=2_@`+\1U-*^*!/W[%ZV#Q)*U-R"S_``OS%UH&I[W$]<D"O--NYA76
+M!>F+9/P`B_<M$<'"%!\`I?>T5`Y#+2A\02ATF.*"GQ\)R_AHP>!W1&W:KS_>
+M$3DZ/(AKMPM-.'UBC]^L)!V`EF6YOX/W\P'6^WLB6H<VL11WQ.CO$[*G]5^W
+M,M3[3&?\@B!ZGY"P"'AN:B=YM7Y<:KE!A)M;L?P^+3)_M/Q8]-'[!#Y_;4D!
+MOD^PQ4G&M%&&6K#[?F$+0&C&=1G7I@+FBG4&,\BW*Q.M_R9,+/'?;I5/[Q?&
+M`Q3+:CMPCX`/'7I`)6-%0#ZNB.>VBT4B"!@K%>%H!M26$>"HZ3J>C&,RYGGZ
+MAEQ5QI4#7EDW_4YZ32M!O@\?64#A[A9N`E8C'HR"WH257+CD_<*R^9/0@%MI
+MC7`@*^+0";G&[Q/58*V<E1\&O4;%0:R((`BT"Y#XF!'HI=?5^<<BR;J"<%*!
+MV=J4`RCK#RP8?H!0A29`<RNPDR`]`VNK;I-,;`9('@0["4'(,!X+""2MI`V\
+MUI;<2MG49`(/34!L7(4$@Z9<X"(H&908M%]7FFY!\+>.3!^'5972O[+41@:,
+MJ\H4GE6&1*=BU6"YIE5X,@#<VR3_6@L,3\")EU<K3XP\<@#'BIGY(7<;\XQY
+M95>R`.\JR^GJ6^T?C;V":RKS=5P-3UHP:2H@1="'DQ5=&4QH2E?!`U@QK+^$
+M,$B"1_XBK!:E\($NEX=4P')EAH:$$5O\J&3@519YACD!!>!:D#UWMF`"7*QX
+M%</[6KDK"L%&95HAAJ3<-?(Z!L@B@P6UNEJ^M*'76)A0.__K!<M61DE@W2@T
+MD;*HT.G7]G]`BP1=,%Z`%@4O8S'M")V`M4.GH:36E8Z%PH-RRRB"7G#L%<=X
+M(-(G0KK:6A5XD%^"%K0RRJITA2<F0&JQ8A"(#J*E\054\GL0)JNJ&4SDA5(C
+M6D:9_:JKI,M*OUH+]6+DQX*H!,),8.,LR70<89&@&&"1_8E2JJI3[0W:.N)-
+M%+X+&!FKJ4I;'+.G*EF4T:U@406*8EQLBB0#3D+%+O.J>(3@U5F$WZK@+,A(
+M=+VL&D1!U,5*@%75B_,59D>H"B\P6BL(!62K72<\DK0%-!"HU=7:9<*B./EV
+MU87@"LA'-3,"NE4BQ'(Y5Q%LPA*K%@'1*Q\[\/P(VR$*I<1L$;EZO4*6QTY5
+M'*3=)%RF3*UH\I`8BMI8:4,+V6DOP.1J1X%5$\P:KV]&,1D4V%#%#@)W%K`&
+M7E"APD7E(6Q1`WLG0KBR?QB.OV/@H#:<=)0@I=0\<BIS8C:`!S*G,;4$ZR&I
+M)H-N`7\ZJ+9(#I9JFQG*.G<BO!9T<J$P/#]BF:J$I30!RQ=<$"#,5*E,JS=H
+M0I;,(D7`H!Z]`2LF*^1Y]1-#EMAL&\R0D!8T,T+PI3QN.OZ)[0FP5`#2)[4`
+M0&#:QL=L9EW2\@&U`4,ZR!7U53PZSE\[\K9=]C:BF.B*;B5ES"T;#'9UNJ,T
+MQ:).JGR41!+X+]IL'M-VD*(VP:8`>B90?\`395%#P8C53(4245$6P>!J^4,,
+M9F,W';_D-Q!N3F1:4.,%+)C='I\M#PHB)&BZ*.A1?C;Z1EEMS.;@ASQ)C*3V
+M61+<J1@5T4N"%B8J[^Y9]4?$H(:!IK:_*KM06&%']AE!_T;L(2%93!7&C;49
+MWXNUR1]#WGE11VH?`9/JR[C"_KEF)QB.':\T)"^LVTDT"JM46#FR/:H\JT:L
+MR*RDLP'/JONQ\.H-P2:<*&P!=P$^[DK/8K3[;+(\)32JO:.R**G]+IU@^=0[
+M<08;G["50)K*EU8-3[5P?%;K7[*X55,@\DP]L*2^(=2\S>;+)\Q*F.&$3HR@
+M7`,%22A&)>=BI3S%QK"(7=AE(%6U+=5SR(M,HE/%U`0CRZF-+^QH@%-_8;/-
+M07O5F<T5Z[`!G,Q=G:9L73J2V7,.T!C1@)/:&&J.H#N5!$[DEX?_%J`V`H2/
+M4UM<M%`H[(2GO`#B#'C(:+62H=NLNDF%-7#)8@IM#)2:A2<G'K:'U9'I,U#$
+M,5O=RNX60D]M9XE8J-$T4]@,S+8J4UCN7K71WYGP5,>IW+>J*+]G`:I8PNJJ
+MR(AKC,^!85UV<[63A2:_886H/A-B0,K#X?@UJW.1A=F*81DQ9P\L@=9=H1B_
+M8AH$_$J#8]9^WS(EJQ,&8[G0-:^)=<"T"7C)NLQ2&#0@(*`AB1&JTL1,S`$Z
+M/;N(X_$Y<Y@&/<#L8Q;?WY)F!0*<<&<JM-H;0AD(5LT,'@8ZT@"XPN\Q@@4#
+M9:P&9UZM>0VFIAP#4#M2NC$N,X"&X91/%P[XD<#58*G&4:>L*+[CP2';H0E=
+M(>%N264)BZA9D<78O&)A-6>$*354E[&=B`5"X7:I<66S//N>T0Q=:!"T*!U;
+M[B>II(>&$W]J74C8V-I/(7M>LUC2P*_&YVR6;@Q&1'@!-@'W8JUX4$":8=\$
+M(N8K'@"KH1'5[)0P+K)=&,$BV24,1_97+*)`2EACX2]0WD\A6CEMR>;EG427
+MYS">.`DZ4D3>4>9^RBGR678-+(H+]J4<HE2P/"="Z3<F(/-IJBS/U34HHI]L
+M6!6!,'Y@BGC;P&9:8XVYBX*+>:0<K)6\J<Q$D\S`^,P7`U'TF+E`)EE;068N
+MC(0D;5QC=!2XX]RP.1OZ=I(YJ?4N&66^B'\!P[YPW0F^K/4\4LH*-WP@FK29
+MJ#@7K*!$SODJ-1@GQO$WJ]G!C=7H5#C7A8(,M1Z_!#L.2V\X^LOM%5A9B96E
+M(@82]6!I%W_]E;2/"0.L9B1_.@&C)H*F[(BI&B<48[$V9LZSED96;4-KP3(F
+M02&;I`,8=!)TZ@Y3#YC3JFLG/(,.%,",SZ0GB+7J/ABB6/DEJ#)&/U7]OW(*
+MFNF<!%B+YHA1$\-]1FLM?\YL!RY4?+.V=V+]B825KB%Q$4C!_I"!`H+8U;]E
+M]P9\4[A`#BW$B'?0SA;V,38[)-XY'6\(QT;\+2@*B1\;D;16#"=+OH#':T%^
+M]IZ<>$+XVXJWX<2L<F*.PL447DN*`NPVJ0GEQ*DS,B.!>^G@DCJ.F>]NF&RX
+M'<(XB"KB7P=$.-&:0N2ZE0,4&MDK-!)_-9@"5+X\=R!ZB_`:(JE&HI56D`+4
+M6VRG"DX7X_=`(Z)G(^%K^#KH%XOI,)+@1$2W,;`K1XFBR=\D]Z]H7<3[%WIU
+MR'.`0!C587"(%,DT$7:R<+)%T?+>OHU95]P0"0-HH`_8PAZMS-U2!@3H(`KV
+MX>.0@/TC+9NPA)`G-N2L.,G"S`B!K`KXFG8*9?I$+5;6#AD(I/R>$N8A/P5_
+M%UA!D+]$=+`PX!;=.C20548`$S@E/@434)UK,8!TFN'XC-$/AA%)881T8&TX
+M`0`36=#U(7]7Z+X;`D36"98QUD^(3#D8`ZD,=`#58O)@22%G`R2&X_\6-7Y-
+M'AKWD#TE!/V,]BBSD<@Q8`!E81$P,,2A@2&)D1%\=;(LZ+:P5Y]1!\+"L`H-
+MY\!D@8K`.`">P`4J-<!ZD&."/6S*6,@]B2A,6:-S9@]<-0>!+/0-&8`.,%=!
+M0D!F`E`$B:^H``6*:P0Z\U@$R$\KDM;(O^!+Z""5$!R8,K!DC&)7!`*9`*Q-
+MD6>_;CR-GV1K=365:#/J$WUN):Y9-(4UV38-;%L5+9V8K_(H&\ZQZG`6ZZEX
+MQ/;VI,]*PS9M1F;;VTS5B]+0M2548NJ6C\22';<]2URW_'R4<Q+%YVSEUC.8
+MQ?XOGWFQ:S?#B(U9=<"F?H4\*Z<,RG9L4/OZ&<YP;!$R\4AEXQE>1OE0/*#R
+M$=N]53,GQSE*(!U.7%0-#8*?S=3%WG8MJ,8W3<V\]^W-'R\_O$(+LNA^A3S%
+M_XET.0=J?PVP?LL_'#4FL_%#I`O^^P$[(;]`UN^O]=$K]/1_\-V?T>@%AOT.
+M@WR$03Y`WQG(OP&VVP->6O3T=[Q[C6%_C;[O\/TK#/('`/(;S2W]5XP;-;7T
+M&=[]6K,BT?=/&/82/?T[!GF.0?YR0)>_`"2_48-[1`=O\.X%AOT"P_Y"(3GB
+M,X//;M'HO^'[;S35]"]H]#<T^A=\;]'COVKKO^*=:T`:*:I*W_]WA=OCT7]%
+MH]_CL[^BQV^U=4"CJW*=ZPGDOB,:O<2[7Z'U2=?IG_'H%3Y+^.SV@'?_%^\F
+M?/8_T.B5]C2@T=P0BCU^I]BYWRR8(O67VM,3M/Z^1"$OV*%$3[D&[O@W??=-
+MLP8YP=D>?\:C'W0-%+H?&T3;X]?Z[C4>_:3X/6S83M\E_>[G#<"3?O=R@[(W
+M6XJ^P[N3]OGW%ATN+Y'31U]M6WV];?6\:L48/53-FF=-NVE-#K6LBQ/U1C+\
+MN1B:5A*>GW.+>7U`Z=U6;#A+ALHB1*]8W(:U*05)K7@03Q#!Y-UXSTUIR^0)
+M8J3V@)V1Y^(@7(EL?B(&_7-DC4IWB8`FK_Z*80O')^P]A+4->1&4P.J<S"92
+M)_/Z[179:XDZHZB=%4/\3D7I$S:$9H'$RA:\C\`%NB=V#CDH&+@7LO]X-H0U
+MSBJ[$QOU"5*^N0]B&G)T.'-E0?3=05P>SCSRA`J.EUR)37^%_(HG2(BW\&JO
+M:.!E00^0OW?86[G#+NYSR>*VW"_!X%9TW5'_LZ!KF8^E`,?R1++59@R=CG>(
+M`UTAQ^HYSY4^YVVC)SRT86C""N@=#D(*"3C!#!&`I7,ZSY$4\80&IT6SE,M\
+M=T#^K14'UF)GX0IFP!713>",?<>DMG;/+9?N[4%L0\M3X23V:9W3$^BUYP03
+M.J/)4OCB"0*+S]E3<#,>>5F"94(66057*C">77]V]>G5S8V:SI<W*G:OGWY\
+M_?3ZV9_`V)=HM;BM3S\:#B#']0^L.[7AM:.?1(7%((P3^8(8G'LEA!?MB+XQ
+MRHJE#`RO('U&_%Z`2>M?3HE6BYJ2S."QB%-*F&B+$7]K#)"!)/(K&I,@R4,2
+MN>8_B<?KKD/QFL223)Y6!*^4"DO@22YP:Y(90*C,B)BS:$]"BIN3H"R!=*F>
+M(!$Z8X,$5EZ0P(^)J'@QB;L4:*>K1Q*TPHQ+6#+F%ZR1E:&(Q]%IJ`B,!$(Y
+M^3$3(Y$L#TI"!F1!+,+/O>*'>4VP1KQ9+P?6E06GHB"5P)"T*_'%BD!INV8;
+MDJ+U.@2=NGQ%HEWHT$V9R8@D$2&ZQ,3'E@RF3+F^@-\5\Q0<D^3663GIGM4@
+M8`)Z6"9>"LPICS$7R""Q>RFL&US)AR2[2S218JD;9#$0L_PH68+EFR!I6;GB
+M#<G3_#>)U8)A2/S7"Q%,#7FU+JS7*VD1QKI_P1%K#?UI*S8=%2$LD<$N($6V
+M"03OE*S`Q_%ASX91_$TO'F*`ESR)QZN!C)FBX"N<CN,&R\@WLO4P@$Y5J`KQ
+M!(E$8`-MK0,`BVB0\TW,,;.X]Q:;?D%"("%*BX0M(<?5#(),9]V),-@"0RI-
+ME,U$=I2C],]"_D:K%Y`\9R#731>G$0*!`:4)!$U6P)VP/S6B%V(992/BK(#(
+MC'P\"BZ=S)$3*08DC3_EL%%"+P[3EWTO&1&(LK*&O+<YX%3C@'VP0:)3`_(E
+M!N1VLMJ097:Z,"CWL*#[V4$X$P*S+,O`A,UKEX"5>/R4%V26]4Y'/HHLTP"2
+MC>"6M,6E''@;D/\`A<)KNDZ<`)%/';RP!+Q93%W)-.C$O$9GA%IL\3EH@2<]
+MR]K-F0B#QIK`3M`C*(;!RV*5@Q*P,0D-&BR5+K-1DHOX*@*INF8!A(]B&GP6
+M9<!IGP%;>])11!!/&=C+VLN_'@5#@NQ\>P`LY#*AB7#8)/0Y`6$C(/$";<Q2
+M`;RHJX0E#[)ABU"5P[A@E!&L/M)Z"$,GP.9!:]BXEE<.2S`*PB>,S$+L('IU
+M$!&*HYB#G$0=1!1?2I[4`"'*\PK"02)L1LB"<1W_3\0.4IQ$"`S,["%3$VJV
+M<-K^(-GZ`CL60^045LD5-()(H2S/++!X&8GV,SY"YJ3J!XT&2G=&)B2P)*`M
+MRJL$R@N%<#82)$Y"_BJ#K(`2-&D`BF0$"%K`1G,R%',2#G4S9/^,MLA_"!Q8
+MCP6O(]RKA(!B-EZH92Q*X(0CC&$U(AEEVAW4V00$!:4Y!$>#%*2!TG*HS9.9
+M7J.X3E.1Q2>P`BY6(JA2">!951`)V1DF)[B(J$,RBG(Z`VP1PK69OR'@=-4B
+MECKC!0(ZRQ19-X^]B`"QXU$]"+(%:EQ+`:$XD0<+8[:0!U'(>5W0Q:HZR![U
+M()X(]B0&G)`44E))/6N&AV;UB%\G#)2*'Y")7M$Z@_\@B+'HS@EZG===D`%'
+M5`9D]LOLE?%'S<09X'->ZXG^&S8RH"(C$*2EBG2-Q1I0^2S+Q#DG:M.IJ0;S
+M7'V=`0;_(/N+/(,9O40H8VSZP(A)N3C3QSS'62@'8MU-8KMDX$2XN4GW.X0%
+M\$((WREYS^``9%X+4Q>4JFN&M"2U0B:99QQ%P'CL)7G*$IFQ0E:C4@&2$WL_
+MJJUF2'\(0:_:1"U4H`^@I5P.BZ)4L!:TK!9D'OOIES1W_8AR@5V!ZI%W>0.F
+M$V#I94*#%H:)@:0KV`HC$.MAS,$PFK`62OI)9H.$M074I_"O!N01#0=X[X,<
+M]QOD6/P`/U_ZU>51LWK,65@@6H';04A.NGDYB&\_X!C!@+,E3)BR/F/I2`UB
+M6J;-D[AY$HHG!QRQ+QN)1WJCK]WFM2M?V\UK6[XVF]>F?#UN7I.7IGZ".GOR
+M0>&-R@?LN@TH(;/Y8-I\,!4?3-L/TN:#5'Q01%T&I&"W[6/1/E;M)[R4L<+F
+MVU!\&]IO0_GM9MDX,C&@^DCS;;FF;K.F'&L94/BG^;9<<+=9<`X&#:AYTWR+
+MEQK^0S\;RN`PQX#".$T_)O=CJGXV).0*$G)CV\^8^QG+?NR&LFQ!679N^M&7
+MM"U=]K,A.%L0G)W:?J;<SU3ULZ%#6]"A36T_*?>3JGXV]&D+^K0M?>I+3A<K
+M^MG0JBUHU;:TJB]IW[_L9T.WMJ!;V]*MOJ2<@K*?#0W;@H9M2\/ZDG(.RGXV
+M]&P+>K8M/=M,S[:B9[NA9UO0LVWIV69ZMA4]VPT]VX*>;4O/-M.SK>C9;.C9
+M%/1L6GHVF9Y-1<]F0\^FH&?3TK/)]&PJ>C8;>C8%/9N6GDVF9U/1L]G0LRGH
+MV;3T;#(]FXJ>S8:>34'/IJ5GD^G95/1L-O1L"GHV+3V;3,^FHF>SH6=3T+-I
+MZ=ED>C85/9L-/9N"GDU+SR;3LZGHV6SHV13T;%IZ-IF>347/9D//IJ!GT]*S
+MR?1L*GK>D'-!S2TQ9UJN2'E#R04AMW2<R;BBX@T1%S3<DG"FX(J`-_1;D&]+
+MO9EX*]K=D&Y!N2WA9KJMR'9K]!5V5]-%)MF*8K=&1*%W6[6;M6XE[#:RKA!U
+MK:3+@JZBKPUY%=35$E>FK;J+MH?<0?.]?EY^W7RLW]:?XLL*\NH[^:S^BC\J
+M0-W[9.\+'61OB'[SG=;]QCMM>TTW+3<-SY`[>8]<R0=)=#Y)QO0IQ^,]2NU9
+M>4'R]$'RYT\B/Q[P`1(5'U@H/$A":M$AY=^?#B(33Z(P'LH1)RW)^2`ITS5`
+M28MZ/B`CO6X@!0:QI6#R2#-#EBL:R@L[\@O?3$6J@&ZQPD@AI2C%]^HY2+U'
+MC]E2\O.)ODAERRC8HOSNTZ'J@@N#A@P]QJ0BG%/NT3/PAFO&%=#S=&(%_4$.
+MIO"G<P.,$YP+,&DS<2G(R0<+3I(R7?1A<]],82:7]E9L1YF)H#=_PG_C@0)L
+MYOQMJI9(>R_&'9D^;5$7]`$ZN^PU7E2S-QDM*(V).64*DA*L47"[ZK8'G,L^
+M5<AW-5HL*(%+I=$P20`-#=4Y4WWJ]-.8(8S\;:P^)3OUOJ(BJ8ZJD\R$&&1T
+M9J=):&&21:$4]9.<E6AFYG:7(%X(2J3H[00JFI1BA2M&Z9-HIR995W,@GYP[
+ME8,X&60&V$GZ=B72`L"FA/F:L:2@J2U(N.8"*1L\"4=-LMB^`A=^0M,W2-LP
+MF+R4=DO1KL*KG!PXB4OW(*;P0]UWJJDRD[PL\B2B@(M;%9U/0B\*L$>QW\P+
+MSK5$5PND282#0=\A0YQ:2.?J04255A(!]^*/EZP7,V!^[%`7ZM"F/*Y0K#$-
+M`WG3ZR,*',*=TP919H1\6];A'H<-&U1U9-"LD/&R3ZAF:];.3CB(\H#S&@4&
+M9FJ!;>@.\!.`K^AUNH!\`S6.(B6)ENX1.)#Q]WOW*,9,D;ZRA34Z*^FTX?^$
+MSETQBC+ZU"Y-Z`D/1CR7#:F6A&7")`6ES2@2*F)V.76`"<+X`IFQ6;N&-*:+
+M4./2T=]<G5"0*;029'KXMQRWE;H^-0S@:S$\7<B<4#0Y]\6DXS&YF)%JYH;-
+MI%IS;$EFJBB4_0)!'9..:Z>0BE&F=A2H/.]WEVX263*A4//8R!(SBI!6,ISR
+M`H6Q7:#0&458"K)E+D".#1U`B\@H>2ZHI9W7!0*A%3NC&&8<DJN7.U?$7B;*
+M,B+8'EFG=N$9!%O(!!=;=/FRO#67FG`5$`8/BOKGH:N7(3C\/I2VD`]".:.6
+MV,[*3LP27PP=I+'@H4"T;Q$]MWC(-#EE0_Z>>2_4RC(40XYLG33XT/+C'K7C
+M,T6%L4-1(O(XIT)`:,6#<\*&&8**MW)9_F+R,`*"Z8V<K>BQ67L4?D\R8MJ9
+MJA;J;WDUM'(GP#)H!"%GB_"<:WEOICQ5VTZ5\10+NI\Z-">7,]#N\JZR'$&-
+MHSA\!HM:K+;%K04=J\#330[UP#`U_+29L9,!]T'B'9N3F+CW6IH?"K2F\#BV
+M`P<,/&\&AM*//>>%S0(+MG89!1[L'7M&38`,ZQ`ZYS8R``%*MB8J6XR'^RC@
+M!)$YU<S=@L!3JTW-9NZM<,N7#>!B!0NQ5,/$2#@@G:[D`37*U%ZD1(E:V$?7
+MKM#4R*"0-9;@QW?=2Z]74I3W-MS70#E(R%D\;P&*ZM!V5@^.3$=\4V&"!^2X
+M8.38RB"Q0`PPE;8H<_!5O6_$1@P-IB(\H.![0$%Z>+VQ0:_;:)'H,_T)RB9`
+MYUMCFHJLU?([]J1H"*WQ%,96:7G(RC8J!-MP!BAS`5-L$92$X*-M"7XC5UL6
+ML)5`*"_2X(W[AJX32$C<0&$^*H7>X,7U\))`UAVS1.X!0=CD_H""P0\XC5Q%
+M[!0O'J&Y.'>H.4+Z=A0#)V@6]Y/H%2<-P<Q-]"2O#8GFN!5*:<0:A7:-YBXL
+MOKPAI490;;&'`@(K+H(^";@C*)EVC6)GC:+ZF2%T(UJNONF($S>K.*/PD\<5
+M-YRN>X\*/0TPK;2.&VG=DK,5HRS?@Y.#B!DAH]!L2VZQ)7U*PCPAU>:$',^:
+MV1*"JW'J(4_E=J.`\M5!!18-D*?N(8M:6Q'Z`=FC#TCAU+6]KR(5OC6Z$CSG
+MABNF#8>.&[:PK0%HH<X+F1`TJB-?"]P1.F,&N#F4J>CU;7`FM<(^C8U:C&H7
+MMYQC@7#;6HUJ=V]#M%$D+2>=G7"XXP%RJ(X`IM@1+7)?TM3R-U>4;\`L[]VJ
+MP123TF](':XGY\F?JC67N?&;&INMQ9U:3S]JW")N-`.0&;;8K$UOKP;H")3%
+M:@;(*'M`7J/<>]2SSY-KP4RM,LW7+;76!H27;C$4M-JH600GYF+MD\C/+3(1
+M:DB]4$.<-E"J.@G%/5U[`E6W5'QA=`&7WC:XU!O;II*XSE%>Y/KU<,:@]N(5
+M<=Z`ZEOK).):,`^$1LA2:YLH6^9HTTBDC0^48K.X2;5-[&J;6%]$9W3WJ^MZ
+M\)&.!_&Q[Y%8^'#`:8C[&BKL-"33T\?Y7K'8ZF.K4(VB\N)&VT13[P$HRA`T
+MHH3K$S)3'ZK8;7X#][UAGJG5J"7@>M&9:7DE`'`#]=.HA1T=(U/3F"]GO#8$
+MFL249N'!QSY.#/C<H<F&Z[FZ;W&G6RV#(O2.F7KKS]9[H6Z$'?F45BE$0X%@
+M+THVPF1K>8RV18?O?B0>FWKB8`5_WK!44GS#E&YI/F)FL0F]J>`TZH[YC.>:
+MNOC('>]OI8U&2AN-U#%FC.[;>J5^-6$C(FR51CK@\..IVET-,$L5SQ'!GXY.
+M7>%49[9CTAL/L]'KCE1MUB(IMEF'C#8'DPH*/'5#0HWRE&L>#[@*Z@$WG#3+
+MJ4&ZQL(UIB"Z$?#XEJLW@:+D>D(RJ3%I0!\MXB+V9[8&[)BQ(H;QM/564M=;
+MR?<TMH+(5P2'Q(/[`Q)^!9RZYWE+0QY;M['5('0=Z[GL$P^OOWU!K%D"%S9+
+MIXK$0)&DKB(1'T*MT0FH*H!+`$[=YVAA%*6-X](3&IJQXI7<(@"+\!&*:"@;
+MKX7F:%>52J^?JC@BT[R%R<>7)<AV4MJX+!T3PB"%1ETBKK/]@'3G)LRMI%5K
+M"K5$^)J'6H0)Y"0K[G$864R%M/%5TD9%)*B(KH807M)$`[YQ@U)>-N27-/;-
+M<%G8*[9V15"Q_M20X$89--:YD:0BL_&"#%(J^,H<`52H8JHVV"3.?E]'C@,V
+M82.ZB4W^RZ@\Q-O`P]UWWWSSJN$COF*W`1D>BZJ'!/50[H`><`,-*^0-:PNM
+MAXQ;IA@[;L2FW0@A7Y$MZ\4-`J%4&_/39M;1\)\0M!T;)X8K63_`OKL_Z%W0
+MI7G`NU[#7W_X_J?[;QB!5>X62K`W`,H4<GY/O>V'F[58X2:$GC?,'4"2K?'A
+M@3;DAYG:AD3:NXSMH`(Z_&_'?,FQLF=03\$V\Y4+LTUM_B%%'F-.[7PC4A24
+M+)CJ^:1K0P2F60<^IOSUB]=O7KRXYX6HC3GD=MP+=4*$-U%?+CK]`+?S'H<!
+M&\5J-L(Y06&YV.BV/!W7$%@3<.)4>^"GE9V-3+%POPN)"7E1Y_8=<*OM`ZXZ
+MZJRQA_G.9)!4L,4VH\_H=F01T[.EEN0#:P^XB[2=9M!IUJ@-6R+GE>+C;IA=
+M1Z%."#Y:[/;X+5A;;C3*A&YLB:%68784DG8E;FR-FPR-[,?:3`(2%QDWJQ#;
+M#=^R&T"<NW$])6?4Z[4;PNYLNE-J/3Z*FX_,_NSBA9)J(_OYF%]/"=?(YXPV
+M/>K7.D==C*C0;K+'3+P(+>5;V]UD39L0D<[)M(APL6LQ;N8TMDK9;754^Y%1
+MV\CY[NJ*VM8*&'>?7O_^2/?]U=I[:G>9["8UPDQZC7K19@L6[\K8L4H&9229
+MKKIP2H8=7N4#>.U8NJR-36FF'I?QP;FV'Y4"MDM"9FSGCKCF=#%W05;ZF%KR
+M:-/9IO[>?E:\L=M$]607Q5F7=3%L-AAV[:3;S4CD3MEQ1X>T2I>+/'P%RZ?H
+M-;8,T.206DTMXI-0O0E8$)_9\*/9R.KNLFU8(;MMO4\T,]=OY,!FY3>0U6C2
+MD@?I[N9X^='G7PJ[GNOYO[,WYP@(##`&!UBG`QR[`?:*:H4!V6=P$.%FN:I4
+M$N<NJTI2\W*`#Z1%8[XZ@`]59``V=)EP\%TALEGW#LC4AY&`(@:V$(V#,)`6
+MQJY*E5`,Y>SG<U@'@RHF30]4Q3$(MPY0WAJS1`=5>:$#/!6\-0!"KK,AUY5?
+MI@Q8U#Z*:A<'"!KU:33O5LW9`4*R*HBB90UR498)55,2"I10W&.`H37`H1J@
+MF]0D'&#393-4U8F*-5[#.5>#26.NE`YDA)"5J_+[`%-B@*.@!RA4A2BK*AF'
+MHJ:(6I]Z=I,&9*>/OF!YP1^3-06(DHX?LM`&J4]%D058]+F2$+(H<NTSG-U0
+M1V`9Y8"H]3?PK0?X)FIA`W=Y8<:JH!<;&"A=-.=J[<"&5HK02FRH$V%S:8I[
+MZ%HI1J/\&B=-1%A@>3A'N:Y[Y$.=W9\CE69Y?<#MY2^0FZ3?T$5Q7-KKM>;"
+MZ%M#98`IL4*?A>.+`_)3]*&GNLN4'L+/*-E(AHX`+6*\B*YI3_#-`1MQKP'H
+MV:!SXI^T`_;B@-'TO<L_EUF?%,;EJ6Z:OL!4Y6$"/*D``SB)@@!IR#!%F0WN
+M37^#6>49FBPB>(X)0\=B%6+&6M(930`C%4*`OR59=7\H2[Y,&&`"="R5#F7E
+MM)0%N(";M!Y+RD7S"@CX/C12,E\#IA>UM%R+3J/+%X#D_M!(KX`PGT,C*Y\M
+M<D*[UL%D_$4G?0C^>0KVO`)3?PA`;J`4KB`JKP]@^-_BQ]G#;W5+ZK?GBR%R
+M@*0]J_Y.S=]3\_=<_QW'YF_3_&V;OUWSMV_^#LW?#7RQ@2\V\,4&OM3`EQKX
+M4@-?:N!+#7RI@2_%S=^_7?[XC;8^IS_/'FX.6BU0#NMIE8E!A]+&_W8HBI^-
+MHH38D%$X<^,_ZDU;@YQ6U`S^'&$LFE]IA&[(-2$I0CDHELKF7QQD-P'E!W(A
+M/L5RU?Y&0R$/R$=".9`L_NLOGAUR%4V/$H<6]3QYI9LOKC6=H:S@Q^I-J:7]
+MAFM/:KW-(N5_4(K;?'-]D)SN(5?U"V4=OK#SS>=%'5+<3L+[%X/2_LY73P\(
+MI*IB?9"L\$%Y:.\[Y`65)>NBUJ$C5MS][""!L2'7Q\K*E#BZ]QDEPPPXO*3E
+M-U4R]+Y#M3:M#QL+2RK,_?&\5D2S`8=D!A54CWVF[":'JJ.R74B/SB_E*'%5
+M-I%EYR-#>B'E9/1L(PRR161_>'WU],.KAT=&CE7UQ*EP%98G;_O>*\MYEZMD
+M'J",WOXY^T5:MXZ">8/N!+W]>RVJ&W)MR0-4X-L^YZVWHL!B*CTW^R[8$P\K
+MP'1D!E3#XFT]!`AH+Q3`MQ#I_L-;(1AQIZ<3/X?VG`?=1'A;!U$Y*R#/*KN*
+MT]O'!X?,N1"RPN_2.RR@\_F,CB;39U_U[?!'HX<]<*`I>W!O^UP=-BL_4"^!
+M':1W0']$$4(SYHI_.2#_=@"T)NTD6B4#\`X4*)=N<16"LGP@>_]OQW\2!A)_
+MVQ<<X,9WH%\M0:DU1:W.W\[OQ$)R:Y;5TV'X?'H']!E9]C1IR81!H^UOIQZU
+M9>3SY(OPQCN,;JJRTY0\-&@(_JWRQ^F%7*A$&G-XY=W(KRX<[4OLOYWZ4&/4
+M*O5D_6C?2?X9+=$*^1?S`IAWF8&5PN8V)T+B^_$=%C#G=AD]*H]8R?P."(@8
+MU]BZTC#%HMY!@=E\TF^0<Z7X/KT+_R:G9]-R85PE@?-AN+S_9L#VZ2+<5O?U
+M;J!-AF$-6Y[AK#!J&YGCP_DFM*FE<0\:UA#8D)B<A<[06'NSV@1K()6"LHMQ
+M<J5"XOR[,ZTIKR&'/3#YB/F@Z3F[<-HRF!=*,#D'+,O6%DZJT'I=PLEUJ54:
+MGK\^PT7W5QH0V`5TG<'3@Z;K[.+3%X[,*CT+0$.IQ/P&T$F)7`"U!=\O"W!.
+M_8^*"`J+[*][T!#WFKZS!V<H*=*58'+IDB+<N0'35%#&PA-9T'_^ZDPJS`QZ
+M2?0N,@T*%TO*SAZ4OK`;5J`*9%JM",UDOO%%_(8ZRR`G4V>VBRF<U*'.E+DH
+M[L,92_EB*CA+VW?=$FC@S)H5<-(62D;_^8LS.14$..T^G!Y4+=DY>W!.A4.V
+M6B893E=::-.6B6)9QN=XT)1C78#S'\_DR-F@X;<.G-)&$G=VN:@4V'/#[<87
+MENP.$]7DR6E?>0'`106@8X?;%[K^]*!I._OTF0/Z4\5$Y.1_H1;[%DPW5V!2
+MSFS&__G79UIN_H!\]#TH>=]`ML#6W)]=X4DEKS_';F<!)U\RJH[)9ME'O0)!
+MX2S]"").2^7LGQZ0?=[AHJ"KGDP/F]G.B@TVUQO8U`/;`;-A=EMTM:ZOB/BU
+MZ.P!6>A[<'HPGZ07[3-1%IVA1J9-A9^Y@9)2DVMDIK'`_OF;,YSLQJ+'+@\E
+M!=-UP2RV&RO)F?6E]WO(K)=\@J!FY.N:F^-G!V2<[T+)5VXH,K=`/B`A%!15
+MPCD5/IO?JB&3K6)51"86Z!?!B0KNE'#>0:9)!6GN@AD+G6@;>13F(C2RM3X:
+M*ZFT#U;5>'^F5_!H9OH>F)24/2B?[R/3%G:V:3G(%1&@K1J*H0$SFX6KF<%:
+MSILB87W?]/#9])B[2Q[+G4O\QQM_%=1EV&JKE-+&`O7EQF-DMJ>TL4'SVCN4
+M6EA,TS[87KV@N2:!4`;'=C52*YNR!EM-XF_/Y"6`'+MT&@L%W\.MUQQSURI.
+M)=0P[K+]U`+J"^P3/_&I#VS0SQU*%5IB@ZE'J4;AW(BG'%8U.Y1:0\F%TC+Z
+MB5)YIUJ2$&W/.XIC87_N@SFJR3Y?M%Z'(B?8/7[RC?FY%JA7[)__3132?/SX
+M@%SW?8649>@:L=\73DZ!K/1FB9K@]HR0C00M,`\'SJ@-8F,'1MD=8+]H%\8)
+M\UA@3"TB,Y!^Q\T,XT:W3P7JB<F-ORCS1CKK'0HOL[O>2I93`Z;+6G]''7FX
+M"84A[U*!_7-6R@4V?0=.:PJOO2.+,IASXVX4R(F[EM*&R[-]O.#__/LSLGWM
+M\:,#,M\[7)[-^+&[Z),691P;)S-[C2'MH=.UV"S$PK+"?SF3XI7`INV`&=3+
+M#'-WU46WKG"V[EL.'89=ORB7SB!`RS2O=8MF.$-=)`#:]8N<(C1T=!!7ULOU
+M36MSR1>;6#L+'UNYF2V"=1?J[@S'>P#HV(LNJ)\9>FHH*)W/37@A%CL1<=P1
+MG,FU_MNB)#_.VVQ?G96W`7**_RY"U?'C(R-=\S,OO:\]XHR?:/;"(&%C?-AR
+M&_'^#`>4`>C4M>95$X6.)K)T#=,S`;1513DX&'>\HU5[?]Y(T&JC])?$\HLZ
+M/^JAA(YSE.'LZ")#%VEH'=9JX3.!QJTRLFI<%H*I7``),`#K9.UW/&*CJ;'!
+M]2WE3*"I8:1BBWP#)EVS]<=ZW;/;%ZV:R@IFZ"#39NJT/<7N,Y!3#60.%\2P
+M$_URKC4_/+MKO('_ES-4%1KTI$:'-KUJH]#51D9UNYD;7+HBLV$+9J.+7)G5
+MX-GD3(60-_NZR&ITF#,6NB:GFDEV;"*S66BO<%;_L5T5]W24S:6M4-NNR,GX
+MZ@QGX@<]@-*)T^<#&7-7E6;@31UU4.3O4D*(C3>?MZ)7Z_R',Q2ZR<=;]L-A
+M=BQNN.H`:;*+U&JG;*+NQAS2)@Q:B(L$[G=9ZH\]J9\)UJ<>P3KE*^L:?>\5
+M3K>WY$C5R8QEUQ2X6$9MQ^,RU"<XDIR?DJGJLVB8>]ZH5TO`]Q37RN'_)E-H
+MMQE"*HX9[\QAWD2<0[D?(@ZI"MJN_^2*PU<=@L@"S(::(%)&M-D#TC0.5"CT
+ML9,PE-P%)V<8.P9@#M][WQ4--G-7;`E"I[!CKT1-*BYD6"KW0U9U,!<R+'87
+M7468=WWW)#-8:@W563W5><>N"JF1`SD):8V,_7BFEX[B>%8'G:DXIM8#<U(K
+MU39ZR^J5?7OFM-4@2[8"\I;%&FC\Z0REMA&^[XBKQ;7^7$_&]<15!K/16\I!
+M82]89ESC-V<+8`W9WIW)<5)$=+H[2\4YO)[9MQ#M+R6>,S:&=$;E[@;8O`GG
+MS.5&R-T9+M04>[]'F#DD[CK:B4_'H"2K:?U[M;##GH*RF_B8*Y.-$(9(V?#K
+MFBHJ-EU70WE=66=K=-H,IM]U2,>-AK+E7@C%(4SIX!\?-H!R_?!\HV'/*W'*
+MS*Y64.LB?GG02XFW8(8-.N-8;H4,9RC?K-2Y!Z;1E#$^4MD3G%G7.-]N?&8F
+MVE5#QK0.5/9C5IY=(Q&NP*??!306^YZN%\<S!1&Z=M-&H32[`JFQ44V=#<BF
+M="@"^&$?GV.QW^U\5W(&U3:N<9^BBO[]\&VPC8"OLA[/92/0V.+0[CZ<(1-H
+MWWW*BLC5BBC[S?->.)QN1/Y];9#Z<D-$K(^8LE[?9Z.43_KU%-%*4%<"Y51C
+M,\.XKX8V08CY>)FW0[X[DTJN:B+MXM+":N=3N;TUSY+'S74((G/7SJ:--6WR
+M@*^+T+!2S\FC='Y\G]<+.,=NE#D#,S8B/JJXVMD#\^UF-P>7'O(6"/;D0V'=
+M[P(:"NO>SCU`8]Y:,(V,5Z/3[\9PW48DY3-CJ]=S>R:WB!2U`'9E4LA@=O>3
+M4L:GK;>]"@[:"4'D;;%"M9=;4!QZLD6.@]G71:;TZFQ'&]D*'E<+SU"_V4#J
+MVV2A(CHZ2K#99E?#N`X^D\I.^T@HK\):*3IK3&]WOAI\ZFWHO!'RYDP*8^9@
+M24<D:4C']G>4YHH$\ZJ/&DJ>]W(QO";!9RA'#8Q:3KRRY7:-Z6HBHX:2]3U#
+M*4P52U<[(7,E"#9;7XT>*G8D9%O>F$(/F:XBRHK=]K>5?"4A2_LC[]?N)`K%
+MUB%B=?>0-T*^.9,R)T,NU;(#IX<!RP4#.B+)SI6^J?Q+-4!V$J_61")SO*ED
+M?+D/=?[7,RG7EX/,'1&O$MZ:OC57J^[2[+0<'9[WLMBF-GPSE;LU$;D89B[+
+M\NSB,D?";4</I6)K(8R-<YDJXVFCU5V;V1)P-&PL$KS7X`V*/LOYI+G8%-E%
+M;]1;6FB?O2>A@JF,S<I]=Y6!NH5];B1I(0Y<L;FT+YFLLI*9^JL_5P9[H9#"
+M6%GY&U:J0,L9=SDBRE6L=D!33X0+6?0TN@)6YZI:E2/S;NZO;6-=0<,LI6=A
+M]]5.*JP-$WMJ/.,FU!Y0Q$8$G,VMWMFDK8FFFHK]+>M[')T%CPE]DDN5]UVR
+M=#Q^4KCL&\&SL/QE;;4YO8''IV*[?0^\N0C)FW[BPE2%,"J3,B_LKKN3TB90
+MI-@LMH5C1]#D6('I.3ETUJR(`E4J,%:1HXW=TR0M1-TZ,T42E4T=:\=FX+H:
+MQ=6!M&)=%_GQ61%\V[#$M(ECZ)-LX]BI`YO/[&KZX0M7Q2*K9'U313`;Z#)7
+M9O`FO7LS)QYUI%PLBLYT#(:IBN96\8I8Q8!;%;<)IT'LYC1.+DJZ3V]SKG[3
+MW9^J(N*E92C*!6'TC=^R#:%-^>K-G/S6DR-*;E/7:LW1A]CL[.3<";N[7[J>
+M^*R5[HPBUF4ZX:YR<"LQ7^.&[]YF3K5C4T9VM/*.VPOBQG8G9^1@P5@X>,YU
+M`3/'(VH3]8).Q49,F^26X^#6[C%INQL^:IS4%CN*SO<"#WG7HY=14-A1<7.B
+MQE>;C0UTH3U<$96K0V'8N]#AA1Q@ZF_(F,RGL47=6&WCUL"%UJZ7DMU[B[AF
+M.N^//Y,B+D:5G>V-%M(A4F\(VQ]B:H>8'AUBZ@UA^D/,[1#IT2'F_BSV#VK,
+M)/OJ(>)C0_BQOQ;=(4P[1'AT"-,;PC\Z1+'A/^V,Z1\=T_;&[!QMDB&*,=/.
+MF.[1,5UOS,XQI76(S6K91X?PO2%2=X@MS9E'APB](:;^$!O.&1\=HLO_<W^(
+MEO\?97_?97\S]H>(S1"/LK_OL[_I#Q&:(1YE?]]E?V/[0_AFB#[[KUKOAT(T
+M_R__K61P3\?W_HM[=KR^T0NI>H"X/B"N`:0O)*SE(3V&#%3B=T^3SASMVQLR
+M-J:0G+3I#SFOL^0!^>:KO0&Y6F?GK&YM&*YEZ-XZH.<!'=5FWALP<01N_S!K
+M8R33@>RWS?#944K\!-\+EIC4/?C7.`SAPKQ]BC*BH?I2^_L&IB=-;.L_^?)0
+M"H2S>PL8<9WX1]>?73V]D2(Z7*=\W^`V/;EC6D_3[@%CW@$87P)##NPN+(XC
+M6GNPC%N?_!T6__./]3*_?</4=J15&Y^8WX6V:;1$E?X[VQO=0WQUL.9MK!N(
+M=3^[_/WUTTLID\17&>U'KFU'3*4F@)7>(J0",7`>ELYTL+=B_0_\USK*T/]O
+MX8UEA,\^NWXV?/[QL,B[X9.GGW_YZ=5'O[_J?[3"JS=I=J;HNU.T]10?40CN
+M[?#S#/[(X']V^?27-\/'5Y=?W#SZ"2L;]X,.0=,)7%Y@+UAE0^]D=VBV$-.C
+MLV$1_PQ5C+IABLZA_#J@'1ZQ"#"8EYIBT7="F#9US];/M;L\O\/,;K@N0>@'
+MKG8'<\VVAWO,8LM3NZ%%LW.O)D2]Y6,?LS1U"I=/L5'>$WLKO2@,U-QQ\*37
+M7+HG(^;RBZM+PI&S[_:%ER\V/7UYO/[PN//"[[V@J7UYM?/4;YY2[Q]>?OKI
+M7N?T//\GDF;]=\7*'PY<0'I9DEOD:_^T\L],EP&XU5CB'];+]?"+-GK%_>,*
+M$J_W[VEERD5>OI23QJ?BY@"N7_4`1^6$5$VZ\GLA[#N2_EQLW^3KR>7B)LID
+M.;$@]<<[K?2/W*L3+J9Z*3=&C^ND+*YT7IPEFF18R?TD1QLTD?PGGE,2Z*Q<
+M_D'1B9^*BPU691U6#/!1D),4Q[@M[^H*%TEN8);JV[QV>I[S):43,D96?/(=
+M[Q/UNH9%7\I9OI-4NCEIXN2)JNH!$,&N0P5_S\NFMYCK/99)=RZL+*2ES,P3
+M=C5><9`[\#V-O.AZ%3G5M+A%PB#K$<=E!'GMDGPQ"4:XKMTMS8HF15D,KQ@C
+M"?>/Y[M8PSICN6\!9M]KGN%$ZTQ+)41AU^7@@YP_'9!N<XL#KR?^?EKKH7*B
+MFUXQ^U*2WM_01A"7"E]6Y<VAN$PI275S!^R8U1:YA4<AV`[Y,M8[*;URTLRE
+M$XJ9,$,)X$3$KYDL9]!16F%:W25/<\JWD-".QZOR5DH*;_XL%3].3%&XZE/N
+MYG/YJA&Y%GOMVO&T&/VT;W5+($_R\=+_'6S8GY$$Q^2PM/F92U5:`(,KNOAV
+MCK!B_)X7BJB6<Y-^+F]JH=2)6RWY\AJY)8Q*G@R?USX5]Z9;6O%;;.;=ZAV$
+M;`C,PG>0)1[B*GC!ML$:);G9FP>8]4X4XDB2._ROP<WULUQ\,HF86%TD%GA\
+MGP4?ZOCI@`UH)DL;92(B6SS(960(9ESD,^86"R?^K.F[MT2=5E;2$/EP>M^=
+M5`N]1:[&2>H7/.@MR@=D$3V4MRN2)W1'RAER(8":A%%GN<>#C/63;J,^2/&+
+M4WD/J0.C.=$.>ML-;AZ<44(FR>T)#I.E+(/7+&2`9:[GGP"VP^UN$9=8S/D2
+M-2$8BG>^DC(G+,46#K^CIB,4#*F>I3N]ODDN$)EED=(ZUULR$F4:*HGB*%0<
+M2+_POM_/ZH4QVT8H(;GHQ:S<P/UZXA,C!)JO0*'S."\/<H)6`"?NE[M69J&D
+M&1>.X?9WV1-`4OEM>7D/KERA^,K/I!EFJ@,]BTB08_-,44'X7/9\D][L8`FE
+M?(+NA)1GZBZ!JT#*WHOFFW&5W3+B'<OU&4(H02P%H-VP1INQAA"`"93K82[(
+MTGDH`DM"4R^BLD;T/&UQ_,3R.4*A\#TIEJH@WS$IXW)L-T-#X\8M9BC5@E[O
+MKXSX"I=61[21NVE(3]V3CBCNH[RK+I:B*E%W.%,F^,"EQU8X@(65H7UIOA%=
+M5H#*XYP.2+K6T\3,\'.^EOR$79,''/9YPQNK<@L;*>N5"[&R_(.S[DZH8RD+
+MZ(1X(2E%EQD`,\NRT*FP.R28W)<7K5H(.Q^@,;`F2R>B3&@IK!'='//5D%;M
+M`#(Q9]R*1OD,),>$BV9H>BO(2&)-6@-)84&T!F@&BUG:JWNI1UY.4@KD-5)6
+M7B*Q\*1Y+BAT^!KY][=ETFMD:X(+K8D`()*WT"T)HC/*_341RTE!-;Y"5G57
+M)DDPKW,01U[DK%"B@Q@T5GJQO"0.`Y!C_XJY*HJ&#V(4Q"AFG:$F:RF<UT(P
+M(RZ\@FWA27[@NF]KY<JN5?4YXL2$&X<H$_0-U]U$$Q@0,J.4S2OQ*SPT!04,
+M3QQF$%8*)&-B5M2>E2$1XLOR_K`$P6=%R'"NZTL]RO.*6!ZN@EZ+RIIGEG_#
+MBM9[*5=S0E#MC@@O0=$E^":PE=D&F>7R0LZ_/<EQUY>2DW;2&VD?)%'^)Y(K
+M4.YV$L*'`K=@=A&F2>N@C.*SD>2]8X8"R3IPHYJCANR_`'>)$@-AAN-^TM7_
+M*B^O!5/C4OE9J,TX`<M2BHY`(T,Y<0F66<Y8X8!;RT&1#@3.=S=-4-54:>:N
+M*/8>A$XLY<8R"[#&(8/NEK%IP*E&-%LDZ1/Y(BB5Y["3B<EO4;CGE:8%H)SL
+MF[QY@M3#DY0$Y`6.JQP@@7E+0HDM&X?^R7!F@1DAB8W8RT)0Y$2+67%\[Y]^
+0]]ZO?G=^^'_[0&'EA?,``'E+
+`
+end
diff --git a/usr.sbin/pcvt/demo/xmas.vt.gz.uu b/usr.sbin/pcvt/demo/xmas.vt.gz.uu
new file mode 100644
index 0000000..ca2ba3a
--- /dev/null
+++ b/usr.sbin/pcvt/demo/xmas.vt.gz.uu
@@ -0,0 +1,110 @@
+begin 644 xmas.vt.gz
+M'XL(".RV^BX"`WAM87,N=G0`[5U;;R.YL7X<0`A:/T`O#/9EL]A8S6NWH#B7
+M'2!HY"#8Q_-@XV`-'"N>&6EDR7/Q`H/][>&UR>IFR3.>5LXFA[W><9MB?:PB
+M/Q:+U90T^\WB#XNK;G'UM\6W?UG\KEY<_4G<Z7_D=G&UF\T65\V:L>Y_%E?M
+MFM%NN;B2N_WBJMY=+ZY6:U9W2T*(OJ?UFJXTBM@MS:NZ8/?++^9.OVQ*KWTI
+M7=-6R^B&*@.UL<7?_C`GJ83!8VO:6'`+T+]VK36B?$U5;,M6T6AT[?'FJ<C:
+MMVWDQ)I*8T*B6[BN*Z_H]G#X\^'P1E>2:\Z[=X?#V\/AO?Y3F3^-U.YP^'`X
+MO)[;*E18)7.8UU:(\JXZN.OC86/_-__IUQK3?8^ZXJ/^H^V[3\,?7H?NTIVN
+M.JV256AE_K`OSVSGT^[/KJ-67?63[18FNMK:R9INOJ^<AK(SVFT\HEPSWGTW
+MGUD%:%=]NKP<_CC=9)=YZ7-^%E=,D^&YTMC/W'62[DU-2=W+C[;3&U-BARAT
+MMG]%L\!UDA70@V-[2C/Y%\>?UM[4IL=,Y0O7,\*6"M.U04B7MMU%P"*NF^ON
+MPHY'8\;,4"R07A>UH:BZ<86Z-W2]C6&YM<$*T;58:S.B6!/%$KFVVWR*@BT4
+MG`6;2-`^F*JM=Z@J04U@FVYSF>`V$)=46R<M4^E$7&GQR\0@-9)W=&W$"(`X
+M!*GMNMSX0;7M#.3)+(P3L5W/0H]S#%(8FS:7'E)D(!T`PP"XM2H`<!2`8@`,
+M&,40HWK.$3=\JJM^\:-58\@4V$8QU=0*`ZB!;34*T"(`&CFQ3:T0VRP)W1RA
+MEE2[*I)1-1AXFYJG6E0[A0$TJ7FJ00$D!J"`>0HQCQJOZ'BH,&KK-E)C)*H+
+M1F0E@#$HD15&9(V<&L,18ZR7<YY#CY6W"N.V;BRUBJ%*8116%%B%4EAB%-;(
+MJ54USD#K,DR@L1KY0]EV+_+PNMW$0+E"]<-(+-O40(F26&(DULB)@;(YY3Z"
+MQY`2M4<!>Q2J#L9C*8$]*(\EQF.-G-HC4!^O5U/B@K.5IZ'$J"TYL`KUT1+C
+ML63`*I3'$N.Q1DZMHHA5UJ^3L(0YJP1&;5D#JU#O+##O+%:I50(EK\#(*]K4
+M*M&>\G]NK/2"/)Q<`F.V:%+[!.J>!>:>A0+VH606&)D%B#?$B7BC\?&&CI&3
+MD$]#8S07(.X0J+L6&*<%B#L$RFF!<5J`N$,PU#,ZX\PHTD!)C.8"1!P"==<<
+MX[0`$8=`.<TQ3G,0<?#5*7?HX]\827&,Z!R$&ASUTASC,@>A!D>YS#$N<Q!J
+M<(6O8\Z!U'8+$^S"",Y!U,%1;\TQ&G,0=7"4QARC,0=1!\>C#NWE_7A9IIJM
+ME]\#<8SA'$0?'/7:'*,S!]$'1^G,,#IS$'WP^HGX-]EWSGOS&$9U!F(/-J!Z
+M]:;7#F,U`[$'&["Z.O0`&*L9B#U8DP%(]V/&SK!6LS'17SM,$($P-<8,6HU)
+M?7`((`1A`U)7V^V;D!;A&`*(01ADM:Y?V3R+QIGY328)@;$WCF'`(!!A?`3\
+MV@,[F#&QW4Z:@7"$L1',P<&X_3C#Z,U`4,(H!M,GXGS\'P-)BA&?@=B$U:>@
+M7_=4IQC5*8A4Z&H$MXMPAPB'$9^"N(6V([CE`&X6R#M(Y%!L9E`0N]#AS"`C
+M53&_3T$,0^%TT+\J$E7=13C,X5,0T5`YA"-5A%LF0]XGB^HX]MB:0$%H0\5(
+MY4?71LQB46QUH"#(H1RW/DV*46Q!H"#DH0RW'J;*W(8W67(H-J4H"(`HS8U[
+MLWOL@;#Y0T$<1.L\3C09FS@@&EKE\FV)I318*D)TM%IC4P@$1.U8/;^OQQ)+
+M(*^$2:-I%["EQR,\'O,4-J>4+*KH]A5LJ3#%T*`>!*^8-!H*@5CAJ?2+,"M;
+M/Q<Q-PR\,*90D!YR$%#P<X13V62>=6F@4W<C.2`6&ZS&34#E^HI]/1)>K#Y=
+MAK8)F3BC?S)YKJRV\<&5LMUN=+_NGZ3042+?/E+9I/*Z6Z[GB03)BGP:RD`A
+MUS%1;!Z?WFSFGVN)906$W6*XAC!!EN2%R4@Z&G,9[&%Y:89)LZZZW`2C1NE_
+MM>9Y/([A<8/7:R/RT@*3%L`6F9>6F+1,;<&R_FJM\K`*@U7`I"8OW6#2#3"I
+M3:3]RADQ6@RC30T[D?)7ZU7$7UR]U#\1?>70W4.\E;/)53"DJWO!N1.T/X%[
+M=>>6O*2T,:65=SK*+-@>(!%/6J>6O^$%(TV-"KXQD.M7)KX@*8R3D9ER`\2Z
+MR[1$N6@G(STL=@\S!V7*Q5VQK.KCS?X!]!AF(.)&:ISP5RYP!,@IM,Q`IP*]
+MCU#=`.)E@J(R*+V`?;#K#37T`1`I2I-!<0*;3\FPG<K_*^-/S(;%=N#+41-M
+MIHDVH46OZ,JB`%X9^14B_VD^'M0\A-U?#2`&98-G`,JM;`-NC<N:3)ER.TZ$
+M6.EK/0;+8/`!QB;!R'"\+ZN"EQ\^`%#.5V)Z90COROI5E,D3*F5([<IZ.IMX
+M[#":LE")#*F-3[?K9OX!@'(5+)`[`1+6/-5=QO66-9ZBIHXY0J)=KUOC[.0-
+M,DV7KK)M!$["$YZ--G3EZ+U/9?25V2<'76X@;G;M7J5V\#KH=#.0=9O2=\ZT
+M9*FN4Y-T/=?TS5`\;KD'\K0#H4,^DZ],^K`B&62&(S-@F(XI=AEYWGFSPK;[
+M[0B'`PM%WD+1]5S#]!%=NOX.T_K*Y'^ST/)I:`E,U83]D,%QAZ_>1ISW(QP%
+M3&WR^C2I/KGHK>D&$=0@TZ],"C\+W3X%W0)#-=%?9U#<D:SW.,HJ-5/4:3`6
+MP\NZ(_GPL@;C.$[X*_/D)0M),4B:FB587IIAT@R8P_/2')/FB3EXGE^9AV19
+M8($!"V"4S$M+3%H"HU1>6F'2JAO'NIGTOC+/-;/(#8;<`*O:O'2+2;?`JE5>
+M>H5)K[KQ-BMF]95Y")T#E!B599T:(_.LE1AK)4V-D7G62HRUDD5CQDE\98X)
+M9/$P'DL.;,F356)DU2^DMN3)*C&R2@GF4!+FR3QQ)49<":(*F2>GQ,@I07PA
+M\^24&#EE"R?-(-VNS/F:+"#&5PE""Y4GI\+(J4!DH?+D5!@Y%85#,LJ@*W,>
+M*@N),5:!@$+E^:DP?BH01J@\/Q7&3Y4&#^.4N#)'UK*`&&45"!E4GJ8*HZD"
+M@8+*TU1A-%4-C&4'66]E#A-F`3'F*A`6J#Q-%493!<*!)D_3!J-I4\-H!\UZ
+M*W,$-`N-<;@!84&3IVN#T;4!84&3IVN#T;6!8<$XZZW,:=TL),;A!@0$C819
+M+"_<\[4Q]\8`7UVE2:E06[GHWU2V*_X<9+F5.2,=I**0H:63,3ST\&W,/(5Z
+M;GEMS(W/3S4A./(WC;FI/I4SZ^7,>CFS7LZLES/KY<QZ.;->SJR7,^OES'HY
+MLU[.K/];G%F?<N-BHW:^UD.@`_8K=U3V;F:N/WSWQ]GL]'O6PV7?%[X]A.OC
+MQX_]_1LRN!Y.71;IT5B_^_OM\?@S>7EWU(::XW_4_+-SK^UN'LAW1->PKPU;
+MN";XM?0M..A7#^^J!-IAZI>J$2:Y/R*(Q_M$:8>IU<LH#0W*(.T/QX=A9QT/
+M>P_O<+6*J<:)&8AZ^_W]O7E#O!Z1^_N]`WO7C\[K`+7KB]Z34]<C>7001K"7
+M.?P4;S789T)$C@0MME^HQ3/&,0_SU6.7`WWFB.5T@X2'ND&]44!-I<-KHL?L
+MZ&99'+T/'SY$0N1$39:%'`_W^Y17]X>C?L'D)]:T/FI/8E,L6_/Q%?JW+K>Y
+MET?SL1;FMZO0=MN#K=%VYI&A3>(\$EO'W+A*C:[TT=9J=*T/MEJC7W58]LY5
+M-!_8\-'AF8]K^.`0E:G@,-VMJRQC91DKRUA9)I5%K"QB91$KBZ0RCY5YK,QC
+M99Y49K$RBY59K,R2RC16IK$RC95I4KF.E>M8N8Z5ZU#9[M&7ILS\M@N**[#[
+MZZ5?.EV1W_93D\0RJY"M0H(P,6UX$!TZA*V_K>Q`[&>0.!028"!*A%^ZX-BA
+M!`S:[_EW2Y+`DJ77>X`Z!/6QF[/&H#N-`JYF=E0V!0.&!U!35Z7U/#CH(M=(
+M8JWOA]";H:/)4-.T,WEH+BJ:Z$7ZL?(8RZ`8Z85[<X>*D.&HDMAUH6;C*YXV
+MMN_0H.9#:".Q:17T:U/&^"R#WJ6ER_](=W*"`<O``!([%.B_S!D@S,;NP=XH
+MT<4F29X=2]A[J[[WR'(X!DGS<1RMB<=X8819A4Y<Q4XD"!M'TS"T)+K>D8.N
+M3`9KU6'D3V?J<!+%CCOV+1P?^IF5)7WL0@+)!Z;K<,A\2U2W%,8H:>A<4]BV
+M>#S:#[<1)H.ES3N>;6X+DS&Y=]=7S_6@NM.<=ZGBYW$!KD4=3H;.TA'F<5K?
+MX)NI;5]H(NQT:SIZM9>^.X3B?Y'#J,,9IUVOA=,D%)_5B\QF#EZ3QOO),!NU
+M#GV8OW>%37=!YON*;&;G\32N%=5=;$RS03/CQ5USK`OJF4^(<K5BQ:G\46S!
+M7L%8T=H>,G[6N2A]Q?ZJN]'V:G8.%Q:FR(5IXL*U=!$T;+I4P43)(&5[;.,$
+M-U&_Z!2F=("VQ=`;&]-FU-/K>!\NY_-[[GGKQESSX=\4SC*T9?H$$$ZKYU5+
+M)N-]JAY)R'<1]7MXAI<-'GM#`-V866_[T*57RZF4[K^]6MS4OS#]Y90*L)&.
+M:3?"T9[,?P<]@BT;G*S,KO</?>22F)@:Z/>"P42NH4]2N85;A&>L$:$9S3W?
+M?H53F9F,L!NBX]&U9^(Q.&CALP^Y/<WC_U*Q.9.CMJ5SL`6N^E',3`<6[,D[
+MD2?6HJ3E?EA<^X$_&V1R,)/!]J-V`)>QVL8)W#X3"-9XP(O0"<CT8:D9SJBG
+M5C5=0,Q&U.>%_(F<P\=08/>DQ&[L5;QUZGE]Y@-S,[-0F^L-O0=7V#?W*O@M
+M=:^!WT'W"M"H@'GH8BP/XWW1<VPT7]G`.Y]:6/UHF_,_7BF?$P@Z^0Q`KQ(#
+M*OGIY!E.3DU=W2F^._;@\ED*W[Q/7_C6?:ZB;YPGC?MI;1?XZ+QR4YP%^_.+
+M$_%)%:>!3[8X!7QBI6]?)(1HK)5V=J7S/OJO[,S74;8S>Y#.LCD@XG(D_@"3
+M39W8+%#??K@U2(;*?KHD/N=YGH&Z!T$Z@CUU!?<H>L:YSA4=L$-9.WPZC-C$
+MF4U\]5:HQ`KS6:4D,<+=YQS-:3_CDW-D=_`).[(]^.2<33:I_LZU&AQ)<&&P
+M"[_,%_GT(=GYA*(Y&&;3A]Y>=^,Z[L(/3*!-11)-+K[$U?BT)O%I3N+3FL2G
+M.4EH<4-27]V3P;!R\R6>91[>C^3>1_4-)R_O;F^/#[\UG>_>=_6-Z,O\!P@'
+MMEP`KEZ$?MWX->YS/4D8O4T8N,J^N.E[:O,EOB%R(6EX$[G\C%D^[=2<>HJ<
+M@_K3TWI:VA82%A(6$A82%A(6$A82%A(6$A82%A(6$OY'DM!]+-E_+;Z1=D^>
+M_/'KIV>][@N7,$.XOYZ>OK&UI3L!&DZ"FM.:T_,[-O=(R+<FT_;PX\/B:O<[
+M,OSS\5S3`:C@KI_ZHWWGF2_)D%[KG_[)U-*<>IUZ/L'V!D=NEV=Q^6E[B7G:
+MP.EG'&C+GZ&U#XW./QVS[9UE+F*FG6$>XKUXIEF7;_!\,PYI[DS3#31VAKG6
+M+?[^E/.:;K*EC7VVLYYP\O7MXVO3=-.O;VR\[)+))Z!N[$PS+2)/.Z52W.GF
+M3HIZKA4IOP*>:TG*+W]E32IKTI/+Q*33ZBG'>9Y5Z93OG'!5.MMR4R90F4#D
+M\P@]]00J\^2I1?0_<*(LKH[_'BDZ8?#F+U[\[=7;:NH)X.#;;OZ/[6TUZ328
+MGO^3$G]29MM>U`7S%S_<;K</WU=G(//D7)V8JF=@9B'C\\FX&GB,B:EHW^&4
+M.HW"RV?R4IC/SLHYCO\/1"U.\M=%1CD.-::EHAP&&X67S^6E=!''S79;E:7\
+MJVDIC1-^=U=X.0$O5:W[\N/-S]\78I:E^_^2BLHM9S_>_;8P\6N9:+^48?[B
+MO^]NWA4?^?7$E(:8F_=O"S$G(*;=[+Z:GI?VJQ7FKQZJ<MIPDM2^_78''6;M
+MJW+\]:O3]O;;)>8OCJ_^]_;[0M")SA,53GX5)QN?/_CQ[?2,M%\Z-;^I"CFG
+M(F?C0KH7^[>WOZ\F)ZO]+JKYW?[X<%N5MQ,4BOX*_&?C4A,O]O=G8J3];JWY
+M[=NJL+.P\U>PNC<NX'[8WK[ZQ]U%865A97D;8.%@X6#A8.%@X6"&@P1\9&*9
+M<V7.E3E7.%@X6#A8.%@X6#A8.%@X6#A8./BOW8.!\7V2A&1(PLP0)R0D@(1)
+M7ZZ@+"$G*(-0,*)YRIQ$F\\&G^T-5`_OA#.?)W5E)?OO'W3?]!?.K^L*X>R?
+LK:O,4Y_?D[\>]SORP^WQS>WV]N?PK-K59<Q^3M6?Q'9Q/?LG-?=D49*R``"?
+`
+end
diff --git a/usr.sbin/pcvt/fed/Makefile b/usr.sbin/pcvt/fed/Makefile
new file mode 100644
index 0000000..7c8a3c3
--- /dev/null
+++ b/usr.sbin/pcvt/fed/Makefile
@@ -0,0 +1,29 @@
+
+.if !exists(${.CURDIR}/../Makefile.inc)
+error:
+ @echo
+ @echo " You MUST link/copy"
+ @echo
+ @echo " ../Makefile.inc.NetBSD or ../Makefile.inc.FreeBSD"
+ @echo
+ @echo " to ../Makefile.inc before you can proceed !"
+ @echo
+.else
+#CFLAGS = -g -Wall -pipe
+CFLAGS = -O -Wall -pipe -I/usr/local/include
+OBJS = fed.o select.o edit.o misc.o
+LIB = ncurses
+DEST = /usr/local/bin
+
+fed: $(OBJS)
+ $(CC) -o fed $(OBJS) -l$(LIB) -L/usr/local/lib
+
+$(OBJS): fed.h
+
+clean:
+ rm -f *.o fed *core* trace*
+
+install: fed
+ ${INSTALL} -c -s -o bin -g bin fed $(DEST)
+
+.endif
diff --git a/usr.sbin/pcvt/fed/edit.c b/usr.sbin/pcvt/fed/edit.c
new file mode 100644
index 0000000..67d616b
--- /dev/null
+++ b/usr.sbin/pcvt/fed/edit.c
@@ -0,0 +1,340 @@
+/*
+ * Copyright (c) 1992, 1993, 1994 by Hellmuth Michaelis
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Hellmuth Michaelis.
+ * 4. The name of the developer may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE DEVELOPER ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE DEVELOPERS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * edit.c, 3.00, last edit-date: [Sun Jan 2 20:08:27 1994]
+ */
+
+/*---------------------------------------------------------------------------
+ *
+ * edit.c font editor edit character
+ * ------------------------------------------
+ *
+ * written by Hellmuth Michaelis, hm@hcshh.hcs.de
+ *
+ * -hm first public release
+ * -hm some debugging & cleanup
+ *
+ *---------------------------------------------------------------------------*/
+
+#include "fed.h"
+
+#define UP 0
+#define DOWN 1
+
+static int pen;
+
+/*---------------------------------------------------------------------------*
+ * fill edit mode command window
+ *---------------------------------------------------------------------------*/
+void edit_mode(void)
+{
+ mvwprintw(cmd_win,1,1,"(W)hite ");
+ mvwprintw(cmd_win,2,1,"(Black ");
+ mvwprintw(cmd_win,3,1,"(I)nvert ");
+ mvwprintw(cmd_win,4,1,"(R)ow BLACK ");
+ mvwprintw(cmd_win,5,1,"(r)ow WHITE ");
+ mvwprintw(cmd_win,6,1,"(C)ol BLACK ");
+ mvwprintw(cmd_win,7,1,"(c)ol WHITE ");
+ mvwprintw(cmd_win,8,1,"(Q)uit/Save ");
+
+ mvwprintw(cmd_win,9 ,1,"e(X)it/undo ");
+ mvwprintw(cmd_win,10,1,"Pen (U)p ");
+ mvwprintw(cmd_win,11,1,"Pen (D)own ");
+ mvwprintw(cmd_win,12,1," ");
+ mvwprintw(cmd_win,13,1,"(^P)rev Line");
+ mvwprintw(cmd_win,14,1,"(^N)ext Line");
+ mvwprintw(cmd_win,15,1,"(^F)orwd Col");
+ mvwprintw(cmd_win,16,1,"(^B)ack Col");
+ wrefresh(cmd_win);
+}
+
+/*---------------------------------------------------------------------------*
+ * edit mode command loop
+ *---------------------------------------------------------------------------*/
+int edit(void)
+{
+ int c, r;
+ char l;
+ unsigned int k_ch;
+
+ c = r = 0;
+
+ pen = UP;
+
+ for(;;)
+ {
+ if(pen == DOWN)
+ dis_cmd(" Edit Mode, the Pen is DOWN");
+ else
+ dis_cmd(" Edit Mode, the Pen is UP");
+
+ l = ((mvwinch(ch_win,(r+1),(c+1))) & A_CHARTEXT);
+ wattron(ch_win,A_REVERSE);
+ mvwprintw(ch_win,(r+1),(c+1),"%c",l);
+ wattroff(ch_win,A_REVERSE);
+ wmove(ch_win,(r+1),(c+1));
+ wrefresh(ch_win);
+
+ k_ch = wgetch(ch_win);
+
+ switch(k_ch)
+ {
+ case K_LEFT:
+ case KEY_LEFT:
+ if(c > 0)
+ {
+ normal_ch(r,c);
+ c--;
+ }
+ break;
+
+ case K_DOWN:
+ case KEY_DOWN:
+ if(r < (ch_height-1))
+ {
+ normal_ch(r,c);
+ r++;
+ }
+ break;
+
+ case K_UP:
+ case KEY_UP:
+ if(r > 0)
+ {
+ normal_ch(r,c);
+ r--;
+ }
+ break;
+
+ case K_RIGHT:
+ case KEY_RIGHT:
+ if(c < (ch_width-1))
+ {
+ normal_ch(r,c);
+ c++;
+ }
+ break;
+
+ case KEY_HOME:
+ normal_ch(r,c);
+ c = r = 0;
+ break;
+
+ case KEY_LL:
+ normal_ch(r,c);
+ c = ch_width-1;
+ r = ch_height-1;
+ break;
+
+ case 0x0c:
+ wrefresh(curscr);
+ break;
+
+ case '\n':
+ case '\r':
+ case ' ' :
+ chg_pt(r,c);
+ break;
+
+ case 'q':
+ pen = UP;
+ normal_ch(r,c);
+ wrefresh(ch_win);
+ return(1);
+ break;
+
+ case 'x':
+ pen = UP;
+ normal_ch(r,c);
+ wrefresh(ch_win);
+ return(0);
+ break;
+
+ case 'w':
+ case 'W':
+ setchr(WHITE);
+ break;
+
+ case 'b':
+ case 'B':
+ setchr(BLACK);
+ break;
+
+ case 'i':
+ case 'I':
+ invert();
+ break;
+
+ case 'r':
+ setrow(WHITE);
+ break;
+
+ case 'R':
+ setrow(BLACK);
+ break;
+
+ case 'c':
+ setcol(WHITE);
+ break;
+
+ case 'C':
+ setcol(BLACK);
+ break;
+
+ case 'u':
+ case 'U':
+ pen = UP;
+ break;
+
+ case 'd':
+ case 'D':
+ pen = DOWN;
+ break;
+
+ default:
+ beep();
+ break;
+
+ }
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ *
+ *---------------------------------------------------------------------------*/
+void normal_ch(int r, int c)
+{
+ char l = ((mvwinch(ch_win,(r+1),(c+1))) & A_CHARTEXT);
+ wattroff(ch_win,A_REVERSE);
+ if(pen == DOWN)
+ mvwprintw(ch_win,(r+1),(c+1),"*");
+ else
+ mvwprintw(ch_win,(r+1),(c+1),"%c",l);
+ wmove(ch_win,(r+1),(c+1));
+}
+
+/*---------------------------------------------------------------------------*
+ *
+ *---------------------------------------------------------------------------*/
+void chg_pt(int r, int c)
+{
+ char l;
+ l = ((mvwinch(ch_win,(r+1),(c+1))) & A_CHARTEXT);
+ if(l == WHITE)
+ l = BLACK;
+ else
+ l = WHITE;
+ mvwprintw(ch_win,(r+1),(c+1),"%c",l);
+ wmove(ch_win,(r+1),(c+1));
+}
+
+/*---------------------------------------------------------------------------*
+ * invert current character
+ *---------------------------------------------------------------------------*/
+void invert(void)
+{
+ int r,c;
+
+ r = 1;
+
+ while(r <= ch_height)
+ {
+ c = 1;
+ while(c <= ch_width)
+ {
+ if(WHITE == mvwinch(ch_win, r, c))
+ mvwaddch(ch_win, r, c, BLACK);
+ else
+ mvwaddch(ch_win, r, c, WHITE);
+ c++;
+ }
+ r++;
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * fill current character black/white
+ *---------------------------------------------------------------------------*/
+void setchr(char type)
+{
+ int r,c;
+
+ r = 1;
+
+ while(r <= ch_height)
+ {
+ c = 1;
+ while(c <= ch_width)
+ {
+ mvwaddch(ch_win, r, c, type);
+ c++;
+ }
+ r++;
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * set current row to black/white
+ *---------------------------------------------------------------------------*/
+void setrow(char type)
+{
+ int r,c;
+
+ getyx(ch_win,r,c);
+
+ c = 1;
+
+ while(c <= ch_width)
+ {
+ mvwaddch(ch_win, r, c, type);
+ c++;
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * set current column to black/white
+ *---------------------------------------------------------------------------*/
+void setcol(char type)
+{
+ int r,c;
+
+ getyx(ch_win,r,c);
+
+ r = 1;
+
+ while(r <= ch_height)
+ {
+ mvwaddch(ch_win, r, c, type);
+ r++;
+ }
+}
+
+/*---------------------------------- E O F ----------------------------------*/
diff --git a/usr.sbin/pcvt/fed/fed.c b/usr.sbin/pcvt/fed/fed.c
new file mode 100644
index 0000000..d2fa4f3
--- /dev/null
+++ b/usr.sbin/pcvt/fed/fed.c
@@ -0,0 +1,168 @@
+/*
+ * Copyright (c) 1992, 1993, 1994 by Hellmuth Michaelis
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Hellmuth Michaelis.
+ * 4. The name of the developer may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE DEVELOPER ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE DEVELOPERS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * fed.c, 3.00, last edit-date: [Sun Jan 2 20:08:45 1994]
+ */
+
+/*---------------------------------------------------------------------------
+ *
+ * fed.c font editor main file
+ * -------------------------------------
+ *
+ * written by Hellmuth Michaelis, hm@hcshh.hcs.de
+ *
+ * -hm first public release
+ * -hm activating font save
+ *
+ *---------------------------------------------------------------------------*/
+
+#define FED
+
+#include "fed.h"
+
+void main(int argc, char *argv[])
+{
+ int i;
+ int row, col;
+ int ret;
+
+ if(argc != 2)
+ {
+ fprintf(stderr,"EGA/VGA Fonteditor, Rel 1.00\n");
+ fprintf(stderr,"usage: %s <fontfilename>\n",argv[0]);
+ exit(1);
+ }
+
+ readfont(argv[1]); /* read fontfile into memory */
+
+ initscr();
+ cbreak();
+ noecho();
+ nonl();
+ keypad(stdscr,TRUE);
+ idlok(stdscr, TRUE);
+
+ move(0,0);
+ standout();
+ addstr(" Interactive EGA/VGA Fonteditor - (c) 1993, 1994 Hellmuth Michaelis ");
+ standend();
+
+/* character horizontal ruler */
+
+ move(WINROW-1, CHCOL + ((WIDTH16 - ch_width)/2) + 1);
+ if(ch_width == WIDTH16)
+ addstr("1234567890123456");
+ else
+ addstr("12345678");
+
+/* charcater vertical ruler */
+
+ for(i=1; i < ch_height+1; i++)
+ mvprintw((WINROW+i), (CHCOL + ((WIDTH16 - ch_width)/2) - 2), "%2d", i);
+
+
+/* select horizontal ruler */
+
+ move(WINROW-1,SETCOL+2);
+ addstr("0 1 2 3 4 5 6 7 8 9 A B C D E F ");
+
+/* select vertical ruler */
+
+ for(i=0; i<10; i++)
+ mvaddch((WINROW+i+1),(SETCOL-1),(i+'0'));
+ for(i=0; i<6; i++)
+ mvaddch((WINROW+10+i+1),(SETCOL-1),(i+'A'));
+
+/* label available commands window */
+
+ move(WINROW-1,CMDCOL+1);
+ addstr("Commands");
+
+ refresh();
+
+/* command window */
+
+ cmd_win = newwin(((WSIZE)+(2*WBORDER)),(CMDSIZE+(2*WBORDER)),
+ WINROW,CMDCOL);
+ keypad(cmd_win,TRUE);
+ idlok(cmd_win, TRUE);
+ box(cmd_win,'|','-');
+
+ sel_mode();
+
+/* character font window */
+
+ ch_win = newwin((ch_height+(2*WBORDER)),(ch_width+(2*WBORDER)),
+ WINROW, CHCOL+((WIDTH16 - ch_width)/2));
+ keypad(ch_win,TRUE);
+ idlok(ch_win, TRUE);
+
+ box(ch_win,'|','-');
+ wrefresh(ch_win);
+
+/* character select window */
+
+ set_win = newwin((WSIZE+(2*WBORDER)),((WSIZE*2)+(2*WBORDER)),
+ WINROW,SETCOL); /* whole character set */
+ keypad(set_win,TRUE);
+ idlok(set_win, TRUE);
+
+ box(set_win,'|','-');
+
+ row = 0;
+ col = 0;
+
+ for(i=0; i<256; i++)
+ {
+ mvwprintw(set_win,row+1,col+1,"%02.2X",i);
+ if(++row > 15)
+ {
+ row = 0;
+ col += 2;
+ }
+ }
+ wmove(set_win,1,1);
+ wrefresh(set_win);
+
+/* start */
+
+ clr_cmd();
+
+ curchar = 0;
+
+ if((ret = selectc()) == 1)
+ {
+ writefont();
+ }
+ endwin();
+}
+
+/*---------------------------------- E O F ----------------------------------*/
diff --git a/usr.sbin/pcvt/fed/fed.h b/usr.sbin/pcvt/fed/fed.h
new file mode 100644
index 0000000..0911d37
--- /dev/null
+++ b/usr.sbin/pcvt/fed/fed.h
@@ -0,0 +1,127 @@
+/*
+ * Copyright (c) 1992, 1993, 1994 by Hellmuth Michaelis
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Hellmuth Michaelis.
+ * 4. The name of the developer may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE DEVELOPER ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE DEVELOPERS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * fed.h, 3.00, last edit-date: [Sun Jan 2 20:10:31 1994]
+ */
+
+#include <ncurses.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#ifdef FED
+
+int ch_height;
+int ch_width;
+
+int curchar;
+
+WINDOW *ch_win;
+WINDOW *set_win;
+WINDOW *cmd_win;
+
+#else
+
+extern int ch_height; /* current fontfile character dimensions */
+extern int ch_width;
+
+extern int curchar; /* character being edited */
+
+extern WINDOW *ch_win; /* windows */
+extern WINDOW *set_win;
+extern WINDOW *cmd_win;
+
+#endif
+
+#define FONTCHARS 256 /* no of chars in a fontfile */
+
+#define WHITE ('.')
+#define BLACK ('*')
+
+#define K_UP 0x10 /* ^P */
+#define K_DOWN 0x0e /* ^N */
+#define K_RIGHT 0x06 /* ^F */
+#define K_LEFT 0x02 /* ^B */
+
+#define WINROW 3
+#define CMDCOL 3
+#define CHCOL 20
+#define SETCOL 41
+#define WSIZE 16
+#define CMDSIZE 12
+#define WBORDER 1
+
+/* fonts */
+
+#define WIDTH8 8 /* 8 bits wide font */
+#define WIDTH16 16 /* 16 bits wide font */
+
+#define FONT8X8 2048 /* filesize for 8x8 font */
+#define HEIGHT8X8 8 /* 8 scan lines char cell height */
+
+#define FONT8X10 2560 /* filesize for 8x10 font */
+#define HEIGHT8X10 10 /* 10 scan lines char cell height */
+
+#define FONT8X14 3584 /* filesize for 8x14 font */
+#define HEIGHT8X14 14 /* 14 scan lines char cell height */
+#define WIDTH8X14 8 /* 8 bits wide font */
+
+#define FONT8X16 4096 /* filesize for 8x16 font */
+#define HEIGHT8X16 16 /* 16 scan lines char cell height */
+
+#define FONT16X16 8192 /* filesize for 16x16 font */
+#define HEIGHT16X16 16 /* 16 scan lines char cell height */
+
+
+void edit_mode ( void );
+int edit ( void );
+void normal_ch ( int r, int c );
+void chg_pt ( int r, int c );
+void invert ( void );
+void setchr ( char type );
+void setrow ( char type );
+void setcol ( char type );
+void main ( int argc, char *argv[] );
+void readfont ( char *filename );
+void dis_cmd ( char *strg );
+void clr_cmd ( void );
+void save_ch ( void );
+void move_ch ( int src, int dest );
+void xchg_ch ( int src, int dest );
+void display ( int no );
+void sel_mode ( void );
+int selectc ( void );
+void normal_set ( int r, int c );
+int sel_dest ( void );
+void normal_uset ( int r, int c );
+void writefont( void );
+
+/* ------------------------------ EOF ----------------------------------- */
diff --git a/usr.sbin/pcvt/fed/misc.c b/usr.sbin/pcvt/fed/misc.c
new file mode 100644
index 0000000..c6707ba
--- /dev/null
+++ b/usr.sbin/pcvt/fed/misc.c
@@ -0,0 +1,353 @@
+/*
+ * Copyright (c) 1992, 1993, 1994 by Hellmuth Michaelis
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Hellmuth Michaelis.
+ * 4. The name of the developer may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE DEVELOPER ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE DEVELOPERS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * misc.c, 3.00, last edit-date: [Sun Jan 2 20:09:21 1994]
+ */
+
+/*---------------------------------------------------------------------------
+ *
+ * misc.c font editor misc routines
+ * -----------------------------------------
+ *
+ * written by Hellmuth Michaelis, hm@hcshh.hcs.de
+ *
+ * -hm first public release
+ * -hm writefont routine
+ *
+ *---------------------------------------------------------------------------*/
+
+#include "fed.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+static unsigned char *fonttab; /* ptr to font in core memory */
+
+static char *bitmask[] = {
+ "....", /* 0 */
+ "...*", /* 1 */
+ "..*.", /* 2 */
+ "..**", /* 3 */
+ ".*..", /* 4 */
+ ".*.*", /* 5 */
+ ".**.", /* 6 */
+ ".***", /* 7 */
+ "*...", /* 8 */
+ "*..*", /* 9 */
+ "*.*.", /* A */
+ "*.**", /* B */
+ "**..", /* C */
+ "**.*", /* D */
+ "***.", /* E */
+ "****", /* F */
+ NULL };
+
+static char lfilename[1024]; /* current filename */
+static unsigned int lfilesize; /* current filename's size */
+
+/*---------------------------------------------------------------------------*
+ * read fontfile into memory
+ *---------------------------------------------------------------------------*/
+void readfont(char *filename)
+{
+ FILE *in;
+ struct stat sbuf, *sbp;
+ int ret;
+ char buffer[1024];
+
+ sbp = &sbuf;
+
+ if((in = fopen(filename, "r")) == NULL)
+ {
+ sprintf(buffer, "cannot open file %s for reading", filename);
+ perror(buffer);
+ exit(1);
+ }
+
+ if((fstat(fileno(in), sbp)) != 0)
+ {
+ sprintf(buffer, "cannot fstat file %s", filename);
+ perror(buffer);
+ exit(1);
+ }
+
+ switch(sbp->st_size)
+ {
+ case FONT8X8:
+ ch_height = HEIGHT8X8;
+ ch_width = WIDTH8;
+ break;
+
+ case FONT8X10:
+ ch_height = HEIGHT8X10;
+ ch_width = WIDTH8;
+ break;
+
+ case FONT8X14:
+ ch_height = HEIGHT8X14;
+ ch_width = WIDTH8;
+ break;
+
+ case FONT8X16:
+ ch_height = HEIGHT8X16;
+ ch_width = WIDTH8;
+ break;
+
+ case FONT16X16:
+ ch_height = HEIGHT16X16;
+ ch_width = WIDTH16;
+ break;
+
+ default:
+ fprintf(stderr,"error, file %s is no valid font file, size=%d\n",filename,sbp->st_size);
+ exit(1);
+ }
+
+ if((fonttab = (unsigned char *)malloc((size_t)sbp->st_size)) == NULL)
+ {
+ fprintf(stderr,"error, malloc failed\n");
+ exit(1);
+ }
+
+ strcpy(lfilename, filename); /* save for write */
+ lfilesize = sbp->st_size; /* save for write */
+
+ if((ret = fread(fonttab, sizeof(*fonttab), sbp->st_size, in)) != sbp->st_size)
+ {
+ sprintf(buffer,"error reading file %s, size = %d, ret = %d\n",filename,sbp->st_size, ret);
+ perror(buffer);
+ exit(1);
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * write fontfile to disk
+ *---------------------------------------------------------------------------*/
+void writefont()
+{
+ FILE *in, *out;
+ int ret;
+ char buffer[1024];
+
+ if((in = fopen(lfilename, "r")) != NULL)
+ {
+ int c;
+ char wfn[1024];
+
+ strcpy(wfn, lfilename);
+ strcat(wfn, ".BAK");
+ if((out = fopen(wfn, "w")) == NULL)
+ {
+ sprintf(buffer, "cannot open file %s for writing", wfn);
+ perror(buffer);
+ exit(1);
+ }
+
+ while(( c = fgetc(in) ) != EOF )
+ fputc(c, out);
+
+ fclose(out);
+ fclose(in);
+ }
+
+ if((out = fopen(lfilename, "w")) == NULL)
+ {
+ sprintf(buffer, "cannot open file %s for writing", lfilename);
+ perror(buffer);
+ exit(1);
+ }
+
+ if((ret = fwrite(fonttab, sizeof(*fonttab), lfilesize, out)) != lfilesize)
+ {
+ sprintf(buffer,"error writing file %s, size=%d, ret=%d\n",lfilename,lfilesize, ret);
+ perror(buffer);
+ exit(1);
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * display a string
+ *---------------------------------------------------------------------------*/
+void dis_cmd(char *strg)
+{
+ move(22,0);
+ clrtoeol();
+ mvaddstr(22,0,strg);
+ refresh();
+}
+
+/*---------------------------------------------------------------------------*
+ * clear a command string
+ *---------------------------------------------------------------------------*/
+void clr_cmd(void)
+{
+ move(22,0);
+ clrtoeol();
+ refresh();
+}
+
+/*---------------------------------------------------------------------------*
+ * move char from src to dest
+ *---------------------------------------------------------------------------*/
+void move_ch(int src, int dst)
+{
+ unsigned char *s, *d;
+ int offset = 0;
+
+ if(ch_width == WIDTH16)
+ offset = 2;
+ else
+ offset = 1;
+
+ s = &(fonttab[ch_height * offset * src]);
+ d = &(fonttab[ch_height * offset * dst]);
+
+ bcopy(s, d, (ch_height*offset)); /* src -> dst */
+}
+
+/*---------------------------------------------------------------------------*
+ * exchange char's src and dest
+ *---------------------------------------------------------------------------*/
+void xchg_ch(int src, int dst)
+{
+ unsigned char *s, *d;
+ unsigned char buf[32];
+ int offset = 0;
+
+ if(ch_width == WIDTH16)
+ offset = 2;
+ else
+ offset = 1;
+
+ s = &(fonttab[ch_height * offset * src]);
+ d = &(fonttab[ch_height * offset * dst]);
+
+ bcopy(s, buf, (ch_height*offset)); /* src -> tmp */
+ bcopy(d, s, (ch_height*offset)); /* dst -> src */
+ bcopy(buf, d, (ch_height*offset)); /* tmp -> dst */
+}
+
+/*---------------------------------------------------------------------------*
+ * display the current selected character
+ *---------------------------------------------------------------------------*/
+void display(int no)
+{
+ unsigned char *fontchar;
+ char line[32];
+ int ln_no;
+ unsigned char hibyte;
+ unsigned char lobyte;
+ int offset;
+ int r;
+
+ offset = 0;
+ r = 1;
+ lobyte = 0;
+
+ if(ch_width == WIDTH16)
+ fontchar = &(fonttab[ch_height * 2 * no]);
+ else
+ fontchar = &(fonttab[ch_height * no]);
+
+ for (ln_no = 0; ln_no < ch_height; ln_no++)
+ {
+ hibyte = *(fontchar + (offset++));
+
+ if(ch_width == WIDTH16)
+ {
+ lobyte = *(fontchar + offset++);
+ }
+
+ strcpy(line,bitmask[(int)((hibyte >> 4) & 0x0f)]);
+ strcat(line,bitmask[(int)(hibyte & 0x0f)]);
+
+ if(ch_width == WIDTH16)
+ {
+ strcat(line,bitmask[(int)((lobyte >> 4) & 0x0f)]);
+ strcat(line,bitmask[(int)(lobyte & 0x0f)]);
+ mvwprintw(ch_win, r, 1, "%16.16s", line);
+ }
+ else
+ {
+ mvwprintw(ch_win, r, 1, "%8.8s", line);
+ }
+ r++;
+ }
+ wmove(ch_win, 1, 1);
+ wrefresh(ch_win);
+}
+
+/*---------------------------------------------------------------------------*
+ * save character
+ *---------------------------------------------------------------------------*/
+void save_ch(void)
+{
+ unsigned char *s;
+ int offset = 0;
+ int r, c;
+ unsigned short byte;
+ unsigned short shift;
+
+ if(ch_width == WIDTH16)
+ offset = 2;
+ else
+ offset = 1;
+
+ s = &(fonttab[ch_height * offset * curchar]);
+
+ r = 1;
+
+ while(r <= ch_height)
+ {
+ c = 1;
+ byte = 0;
+ if(offset == 2)
+ shift = 0x8000;
+ else
+ shift = 0x80;
+
+ while(c <= ch_width)
+ {
+ if(mvwinch(ch_win, r, c) == BLACK)
+ byte |= shift;
+ shift = (shift >> 1);
+ c++;
+ }
+ *s++ = byte;
+ r++;
+ }
+}
+
+/*---------------------------------- E O F ----------------------------------*/
+
+
diff --git a/usr.sbin/pcvt/fed/select.c b/usr.sbin/pcvt/fed/select.c
new file mode 100644
index 0000000..b6993bb
--- /dev/null
+++ b/usr.sbin/pcvt/fed/select.c
@@ -0,0 +1,334 @@
+/*
+ * Copyright (c) 1992, 1993, 1994 by Hellmuth Michaelis
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Hellmuth Michaelis.
+ * 4. The name of the developer may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE DEVELOPER ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE DEVELOPERS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * select.c, 3.00, last edit-date: [Sun Jan 2 20:09:36 1994]
+ */
+
+/*---------------------------------------------------------------------------
+ *
+ * select.c font editor select character
+ * ----------------------------------------------------
+ *
+ * written by Hellmuth Michaelis, hm@hcshh.hcs.de
+ *
+ * -hm first public release
+ * -hm debugging
+ *
+ *---------------------------------------------------------------------------*/
+
+#include "fed.h"
+
+int sc, sr, scurchar;
+
+int edit();
+
+void sel_mode(void)
+{
+ mvwprintw(cmd_win,1,1,"(E)dit ");
+ mvwprintw(cmd_win,2,1,"(M)ove ");
+ mvwprintw(cmd_win,3,1,"exchan(G)e ");
+ mvwprintw(cmd_win,4,1,"(Q)uit/Save ");
+ mvwprintw(cmd_win,5,1,"e(X)it/Undo ");
+ mvwprintw(cmd_win,6,1," ");
+ mvwprintw(cmd_win,7,1," ");
+ mvwprintw(cmd_win,8,1," ");
+
+ mvwprintw(cmd_win,9 ,1," ");
+ mvwprintw(cmd_win,10,1," ");
+ mvwprintw(cmd_win,11,1," ");
+ mvwprintw(cmd_win,12,1," ");
+ mvwprintw(cmd_win,13,1,"(^P)rev Line");
+ mvwprintw(cmd_win,14,1,"(^N)ext Line");
+ mvwprintw(cmd_win,15,1,"(^F)orwd Col");
+ mvwprintw(cmd_win,16,1,"(^B)ack Col");
+ wrefresh(cmd_win);
+}
+
+int selectc()
+{
+ int c, r;
+ int ret;
+ char h, l;
+ unsigned int k_ch;
+
+ c = (curchar / 16);
+ r = (curchar % 16);
+
+ for(;;)
+ {
+ dis_cmd(" Select Character");
+
+ sel_mode();
+
+ curchar = r + (c*16);
+
+ display(curchar);
+
+ h = ((mvwinch(set_win,(r+1),((c*2)+1))) & A_CHARTEXT);
+ l = ((mvwinch(set_win,(r+1),((c*2)+2))) & A_CHARTEXT);
+ wattron(set_win,A_REVERSE);
+ mvwprintw(set_win,(r+1),((c*2)+1),"%c%c",h,l);
+ wattroff(set_win,A_REVERSE);
+ wmove(set_win,(r+1),((c*2)+1));
+ wrefresh(set_win);
+
+ k_ch = wgetch(set_win);
+
+ switch(k_ch)
+ {
+ case K_LEFT:
+ case KEY_LEFT:
+ if(c > 0)
+ {
+ normal_set(r,c);
+ c--;
+ }
+ break;
+
+ case K_DOWN:
+ case KEY_DOWN:
+ if(r < 15)
+ {
+ normal_set(r,c);
+ r++;
+ }
+ break;
+
+ case K_UP:
+ case KEY_UP:
+ if(r > 0)
+ {
+ normal_set(r,c);
+ r--;
+ }
+ break;
+
+ case K_RIGHT:
+ case KEY_RIGHT:
+ if(c < 15)
+ {
+ normal_set(r,c);
+ c++;
+ }
+ break;
+
+ case 'e':
+ case 'E':
+ edit_mode();
+ dis_cmd(" Edit Character");
+ display(curchar);
+ ret = edit();
+ if(ret == 1)
+ save_ch();
+ break;
+
+ case 'g':
+ case 'G':
+ dis_cmd(" Exchange: select Destination, then press RETURN or any other Key to ABORT");
+ sr = r;
+ sc = c;
+ scurchar = curchar;
+ if((curchar = sel_dest()) == -1)
+ { /* failsafe */
+ r = sr;
+ c = sc;
+ curchar = scurchar;
+ }
+ else
+ { /* valid return */
+ normal_set(r,c);
+ c = (curchar / 16);
+ r = (curchar % 16);
+ xchg_ch(scurchar,curchar);
+ }
+ break;
+
+ case 'm':
+ case 'M':
+ dis_cmd(" Move: select Destination, then press RETURN or any other Key to ABORT");
+ sr = r;
+ sc = c;
+ scurchar = curchar;
+ if((curchar = sel_dest()) == -1)
+ { /* failsafe */
+ r = sr;
+ c = sc;
+ curchar = scurchar;
+ }
+ else
+ { /* valid return */
+ normal_set(r,c);
+ c = (curchar / 16);
+ r = (curchar % 16);
+ move_ch(scurchar,curchar);
+ }
+ break;
+
+ case 'q':
+ case 'Q':
+ normal_set(r,c);
+ wrefresh(set_win);
+ return(1);
+ break;
+
+ case 'x':
+ case 'X':
+ normal_set(r,c);
+ wrefresh(set_win);
+ return(0);
+ break;
+
+ case 0x0c:
+ wrefresh(curscr);
+ break;
+
+ default:
+ beep();
+ break;
+
+ }
+ }
+}
+
+void normal_set(int r, int c)
+{
+ char h, l;
+
+ h = ((mvwinch(set_win,(r+1),((c*2)+1))) & A_CHARTEXT);
+ l = ((mvwinch(set_win,(r+1),((c*2)+2))) & A_CHARTEXT);
+ wattroff(set_win,A_REVERSE);
+ mvwprintw(set_win,(r+1),((c*2)+1),"%c%c",h,l);
+ wmove(set_win,(r+1),((c*2)+1));
+}
+
+int sel_dest(void)
+{
+ int c, r;
+ char h, l;
+ unsigned int k_ch;
+
+ c = (curchar / 16);
+ r = (curchar % 16);
+
+ for(;;)
+ {
+
+ curchar = r + (c*16);
+
+ display(curchar);
+
+ h = ((mvwinch(set_win,(r+1),((c*2)+1))) & A_CHARTEXT);
+ l = ((mvwinch(set_win,(r+1),((c*2)+2))) & A_CHARTEXT);
+ wattron(set_win,A_UNDERLINE);
+ mvwprintw(set_win,(r+1),((c*2)+1),"%c%c",h,l);
+ wattroff(set_win,A_UNDERLINE);
+ wmove(set_win,(r+1),((c*2)+1));
+ wrefresh(set_win);
+
+ k_ch = wgetch(set_win);
+
+ switch(k_ch)
+ {
+ case K_LEFT:
+ case KEY_LEFT:
+ if(c > 0)
+ {
+ normal_uset(r,c);
+ c--;
+ }
+ break;
+
+ case K_DOWN:
+ case KEY_DOWN:
+ if(r < 15)
+ {
+ normal_uset(r,c);
+ r++;
+ }
+ break;
+
+ case K_UP:
+ case KEY_UP:
+ if(r > 0)
+ {
+ normal_uset(r,c);
+ r--;
+ }
+ break;
+
+ case K_RIGHT:
+ case KEY_RIGHT:
+ if(c < 15)
+ {
+ normal_uset(r,c);
+ c++;
+ }
+ break;
+
+ case '\r':
+ case '\n':
+ normal_uset(r,c);
+ return(r + (c*16));
+
+ case 0x0c:
+ wrefresh(curscr);
+ break;
+
+ default:
+ normal_uset(r,c);
+ return(-1);
+ }
+ }
+}
+
+void normal_uset(int r, int c)
+{
+ char h, l;
+
+ h = ((mvwinch(set_win,(r+1),((c*2)+1))) & A_CHARTEXT);
+ l = ((mvwinch(set_win,(r+1),((c*2)+2))) & A_CHARTEXT);
+
+ wattroff(set_win,A_UNDERLINE);
+ mvwprintw(set_win,(r+1),((c*2)+1),"%c%c",h,l);
+ wmove(set_win,(r+1),((c*2)+1));
+
+ if((r==sr) && (c==sc))
+ {
+ wattron(set_win,A_REVERSE);
+ mvwprintw(set_win,(r+1),((c*2)+1),"%c%c",h,l);
+ wattroff(set_win,A_REVERSE);
+ wmove(set_win,(r+1),((c*2)+1));
+ }
+}
+
+
+
+/*---------------------------------- E O F ----------------------------------*/
diff --git a/usr.sbin/pcvt/fontedit/Makefile b/usr.sbin/pcvt/fontedit/Makefile
new file mode 100644
index 0000000..94463aa
--- /dev/null
+++ b/usr.sbin/pcvt/fontedit/Makefile
@@ -0,0 +1,20 @@
+.if !exists(${.CURDIR}/../Makefile.inc)
+error:
+ @echo
+ @echo " You MUST link/copy"
+ @echo
+ @echo " ../Makefile.inc.NetBSD or ../Makefile.inc.FreeBSD"
+ @echo
+ @echo " to ../Makefile.inc before you can proceed !"
+ @echo
+.else
+
+PROG= fontedit
+CLEANFILES+= core.fontedit fontedit.core
+
+.include <bsd.prog.mk>
+
+.endif
+
+
+
diff --git a/usr.sbin/pcvt/fontedit/README b/usr.sbin/pcvt/fontedit/README
new file mode 100644
index 0000000..1854129
--- /dev/null
+++ b/usr.sbin/pcvt/fontedit/README
@@ -0,0 +1,36 @@
+When I first saw this posted to rn, I tried to compile this on a machine
+running BSD UNIX. Much to my dissapointment, It said "unable to find
+/usr/include/termio.h" and thus it sat for a couple months. I was able to
+compile it on a 3b5, but I didn't have a vt220 hooked up to it. I was doing
+some unrelated work with ioctl calls and finally realized that it would not be
+too hard to convert it from System V to BSD. It also looked kind of strange
+with the cursor on, so I turned this off. To implement this, compile with
+the "-DCURFIX" flag in the Makefile.
+I am working on a new version that uses curses and that would enable you to
+change the file that your are working on without leaving the program.
+I thought I'd post it as it is now, since it has a lot of uses right away.
+Imagine changing your favorite game to have objects that look like what they
+are instead of the regular characters. Also, I think people should post their
+own character sets, if they come up with some neat stuff.
+ Please send any comments or suggestions to:
+
+ UUCP : ..!harvard!bu-cs!bucsb!eap
+ ARPANET: eap@bucsb.bu.edu
+ CSNET : eap%bucsb@bu-cs
+
+ Have fun,
+
+ - Eric Pearce
+ Boston University
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/usr.sbin/pcvt/fontedit/fontedit.1 b/usr.sbin/pcvt/fontedit/fontedit.1
new file mode 100644
index 0000000..8b55edb
--- /dev/null
+++ b/usr.sbin/pcvt/fontedit/fontedit.1
@@ -0,0 +1,58 @@
+.TH FONTEDIT 1 LOCAL
+.SH NAME
+fontedit \- Edit fonts.
+.SH SYNOPSIS
+.B fontedit file
+.SH DESCRIPTION
+.I Fontedit
+is used to edit the down line reloadable character set (DRCS) of a VT220
+terminal. The editor has two display areas, one for displaying the
+entry currently being manipulated, and one for displaying the complete
+DRCS. Commands to the editor take the form of function keys.
+.PP
+.I Fontedit
+takes one command line parameter, a file name. This file is
+used to save the character set. If the file exists when \fIfontedit\fP
+is invoked, it is read in to initialize the DRCS. The file is written
+to when \fIfontedit\fP exits.
+.PP
+Commands to fontedit take the form of function keys. The current
+definitions are:
+.IP \fBHELP\fP
+Display a help screen.
+.IP \fBF6\fP
+Turn the pixel under the cursor on.
+.IP \fBF7\fP
+Turn the pixel under the cursor off.
+.IP \fBF13\fP
+Clear the display area.
+.IP \fBFind\fP
+Save the current font in the font table. Update the DRCS display.
+.IP \fBSelect\fP
+Extract the entry selected by the cursor in the DRCS display.
+.IP \fBPrev\fP
+Move the cursor to the previous entry in the DRCS display.
+.IP \fBNext\fP
+Move the cursor to the next entry in the DRCS display.
+.IP \fBInsert\fP
+Insert a blank line at the current cursor position. The bottom row is lost.
+.IP \fBRemove\fP
+Remove the row at the current cursor position. All rows below the
+current one are shifted up.
+.IP \fBCursors\fP
+Move the cursor in the main display area.
+.PP
+If the screen gets garbled, press <control-L>.
+.PP
+To exit \fIfontedit\fP, press <control-D>. The DRCS will be saved in
+\fIfile\fP. To exit without saving the DRCS, hit interrupt (usually
+DEL).
+.SH DIAGNOSTICS
+.I Fontedit
+will issue a warning when the entry being worked on is not saved, and
+some potentially destructive command, like \fBSelect\fP is used. To
+override the warning message, immediately reissue the command.
+.SH AUTHOR
+Greg Franks.
+
+
diff --git a/usr.sbin/pcvt/fontedit/fontedit.c b/usr.sbin/pcvt/fontedit/fontedit.c
new file mode 100644
index 0000000..d9ed357c
--- /dev/null
+++ b/usr.sbin/pcvt/fontedit/fontedit.c
@@ -0,0 +1,925 @@
+/*
+ * fontedit
+ * Fonteditor for VT220
+ *
+ * BUGS:
+ * o Cursor motion is less than optimal (but who cares at 9600),
+ *
+ * COMPILE:
+ * cc -O fontedit.c -o fontedit
+ * (use Makefile)
+ *
+ * Copyright (c) 1987 by Greg Franks.
+ *
+ * Permission is granted to do anything you want with this program
+ * except claim that you wrote it.
+ *
+ *
+ * REVISION HISTORY:
+ *
+ * Nov 21, 1987 - Fixed man page to say "Fontedit" instead of "Top"
+ * Nov 22, 1987 - Added BSD Compatible ioctl, turned cursor on/off
+ * - eap@bucsf.bu.edu
+ */
+
+void clear_screen();
+#include <stdio.h>
+#ifdef SYSV
+#include <sys/termio.h>
+#endif SYSV
+#ifdef BSD
+#include <sys/ioctl.h>
+#endif BSD
+#if defined (__NetBSD__) || defined (__FreeBSD__)
+#include <sys/termios.h>
+#include <sys/ioctl.h>
+#endif /* __NetBSD__ || __FreeBSD__ */
+#include <signal.h>
+
+#ifdef CURFIX
+#define CURSORON "\033[?25h"
+#define CURSOROFF "\033[?25l"
+#endif CURFIX
+
+#define MAX_ROWS 10
+#define MAX_COLS 8
+
+typedef enum { false, true } bool;
+
+#define KEY_FIND 0x0100
+#define KEY_INSERT 0x0101
+#define KEY_REMOVE 0x0102
+#define KEY_SELECT 0x0103
+#define KEY_PREV 0x0104
+#define KEY_NEXT 0x0105
+#define KEY_F6 0X0106
+#define KEY_F7 0x0107
+#define KEY_F8 0x0108
+#define KEY_F9 0x0109
+#define KEY_F10 0x010a
+#define KEY_F11 0x010b
+#define KEY_F12 0x010c
+#define KEY_F13 0x010d
+#define KEY_F14 0x010e
+#define KEY_HELP 0x010f
+#define KEY_DO 0x0110
+#define KEY_F17 0x0111
+#define KEY_F18 0x0112
+#define KEY_F19 0x0113
+#define KEY_F20 0x0114
+#define KEY_UP 0x0115
+#define KEY_DOWN 0x0116
+#define KEY_RIGHT 0x0117
+#define KEY_LEFT 0x0118
+
+/*
+ * Position of main drawing screen.
+ */
+
+#define ROW_OFFSET 3
+#define COL_OFFSET 10
+
+/*
+ * Position of the DRCS table.
+ */
+
+#define TABLE_ROW 4
+#define TABLE_COL 50
+
+/*
+ *
+ */
+
+#define ERROR_ROW 20
+#define ERROR_COL 40
+
+bool display_table[MAX_ROWS][MAX_COLS];
+
+#define TOTAL_ENTRIES (128 - 32)
+#define SIXELS_PER_CHAR 16
+
+char font_table[TOTAL_ENTRIES][SIXELS_PER_CHAR];
+unsigned int current_entry;
+
+#ifdef SYSV
+struct termio old_stty, new_stty;
+#endif SYSV
+#ifdef BSD
+struct sgttyb old_stty, new_stty;
+#endif BSD
+#if defined (__NetBSD__) || defined (__FreeBSD__)
+struct termios old_stty, new_stty;
+#endif /* __NetBSD__ || __FreeBSD__ */
+FILE * font_file = (FILE *)0;
+
+
+/*
+ * Interrupt
+ * Exit gracefully.
+ */
+
+interrupt()
+{
+ void clear_screen();
+#ifdef CURFIX
+ printf("%s\n",CURSORON);
+#endif CURFIX
+#ifdef SYSV
+ ioctl( 0, TCSETA, &old_stty );
+#endif SYSV
+#ifdef BSD
+ ioctl( 0, TIOCSETP, &old_stty );
+#endif BSD
+#if defined (__NetBSD__) || defined (__FreeBSD__)
+ ioctl( 0, TIOCSETA, &old_stty );
+#endif /* __NetBSD__ || __FreeBSD__ */
+ clear_screen();
+ exit( 0 );
+}
+
+
+/*
+ * Main
+ * Grab input/output file and call main command processor.
+ */
+
+main( argc, argv )
+int argc;
+char *argv[];
+{
+ void command(), init_restore(), clear_screen();
+ void save_table(), get_table(), extract_entry();
+
+ if ( argc != 2 ) {
+ fprintf( stderr, "usage: fontedit filename\n" );
+ exit( 1 );
+ }
+
+ printf( "Press HELP for help\n" );
+ printf( "\033P1;1;2{ @\033\\" ); /* Clear font buffer */
+ fflush( stdout );
+ sleep( 1 ); /* Let terminal catch up */
+ /* otherwise we get frogs */
+
+ if ( ( font_file = fopen( argv[1], "r" ) ) == (FILE *)0 ) {
+ if ( ( font_file = fopen( argv[1], "w" ) ) == (FILE *)0 ) {
+ fprintf( stderr, "Cannot create file %s \n", argv[1] );
+ exit( 1 );
+ }
+ }
+ fclose( font_file );
+
+ if ( ( font_file = fopen( argv[1], "r" ) ) != (FILE *)0 ) {
+ get_table( font_file );
+ fclose( font_file );
+ }
+
+ if ( ( font_file = fopen( argv[1], "r+" ) ) == (FILE *)0 ) {
+ fprintf( stderr, "Cannot open %s for writing\n", argv[1] );
+ exit( 1 );
+ }
+#ifdef CURFIX
+ printf("%s\n",CURSOROFF);
+#endif CURFIX
+#ifdef SYSV
+ ioctl( 0, TCGETA, &old_stty );
+#endif SYSV
+#ifdef BSD
+ ioctl( 0, TIOCGETP, &old_stty );
+#endif BSD
+#if defined (__NetBSD__) || defined (__FreeBSD__)
+ ioctl( 0, TIOCGETA, &old_stty );
+#endif /* __NetBSD__ || __FreeBSD__ */
+ signal( SIGINT, (void *) interrupt );
+ new_stty = old_stty;
+#ifdef SYSV
+ new_stty.c_lflag &= ~ICANON;
+ new_stty.c_cc[VMIN] = 1;
+ ioctl( 0, TCSETA, &new_stty );
+#endif SYSV
+#if defined (__NetBSD__) || defined (__FreeBSD__)
+ new_stty.c_lflag &= ~ICANON;
+ new_stty.c_lflag &= ~ECHO;
+ new_stty.c_cc[VMIN] = 1;
+ ioctl( 0, TIOCSETA, &new_stty );
+#endif /* __NetBSD__ || __FreeBSD__ */
+#ifdef BSD
+ new_stty.sg_flags |= CBREAK;
+ new_stty.sg_flags &= ~ECHO;
+ ioctl( 0, TIOCSETP, &new_stty );
+#endif BSD
+ current_entry = 1;
+ extract_entry( current_entry );
+ init_restore();
+ command();
+#ifdef SYSV
+ ioctl( 0, TCSETA, &old_stty );
+#endif SYSV
+#ifdef BSD
+ ioctl( 0, TIOCSETP, &old_stty );
+#endif BSD
+#if defined (__NetBSD__) || defined (__FreeBSD__)
+ ioctl( 0, TIOCSETA, &old_stty );
+#endif /* __NetBSD__ || __FreeBSD__ */
+ clear_screen();
+
+ /* Overwrite the old file. */
+
+ fseek( font_file, 0L, 0 );
+ save_table( font_file );
+ fclose( font_file );
+#ifdef CURFIX
+ printf("%s\n",CURSORON);
+#endif CURFIX
+}
+
+
+
+/*
+ * Command
+ * Process a function key.
+ *
+ * The user cannot fill in slots 0 or 95 (space and del respecitively).
+ */
+
+void
+command()
+{
+ register int c;
+ register int row, col;
+ register int i, j;
+ bool change, error, override;
+
+ void build_entry(), extract_entry(), send_entry(), print_entry();
+ void highlight(), draw_current(), init_restore(), help();
+ void warning();
+
+ change = false;
+ error = false;
+ override = false;
+ row = 0; col = 0;
+ highlight( row, col, true );
+
+ for ( ;; ) {
+ c = get_key();
+ highlight( row, col, false ); /* turn cursor off */
+
+ if ( error ) {
+ move ( ERROR_ROW, ERROR_COL );
+ printf( "\033[K" ); /* Clear error message */
+ move ( ERROR_ROW+1, ERROR_COL );
+ printf( "\033[K" ); /* Clear error message */
+ error = false;
+ } else {
+ override = false;
+ }
+
+ switch ( c ) {
+
+ case KEY_FIND: /* update DRCS */
+ if ( !change && !override ) {
+ warning( "No changes to save" );
+ override = true;
+ error = true;
+ } else {
+ build_entry( current_entry );
+ send_entry( current_entry );
+ print_entry( current_entry, true );
+ change = false;
+ }
+ break;
+
+ case KEY_F6: /* Turn on pixel */
+ change = true;
+ display_table[row][col] = true;
+ highlight( row, col, false );
+ col = ( col + 1 ) % MAX_COLS;
+ if ( col == 0 )
+ row = ( row + 1 ) % MAX_ROWS;
+ break;
+
+ case KEY_F7: /* Turn off pixel */
+ change = true;
+ display_table[row][col] = false;
+ highlight( row, col, false );
+ col = ( col + 1 ) % MAX_COLS;
+ if ( col == 0 )
+ row = ( row + 1 ) % MAX_ROWS;
+ break;
+
+ case KEY_INSERT: /* Insert a blank row */
+ change = true;
+ for ( j = 0; j < MAX_COLS; ++j ) {
+ for ( i = MAX_ROWS - 1; i > row; --i ) {
+ display_table[i][j] = display_table[i-1][j];
+ }
+ display_table[row][j] = false;
+ }
+ draw_current();
+ break;
+
+ case KEY_REMOVE: /* Remove a row */
+ change = true;
+ for ( j = 0; j < MAX_COLS; ++j ) {
+ for ( i = row; i < MAX_ROWS - 1; ++i ) {
+ display_table[i][j] = display_table[i+1][j];
+ }
+ display_table[MAX_ROWS-1][j] = false;
+ }
+ draw_current();
+ break;
+
+ case KEY_F13: /* Clear buffer */
+ if ( change && !override ) {
+ warning( "Changes not saved" );
+ error = true;
+ override = true;
+ } else {
+ for ( j = 0; j < MAX_COLS; ++j ) {
+ for ( i = 0; i < MAX_ROWS; ++i ) {
+ display_table[i][j] = false;
+ }
+ }
+ draw_current();
+ }
+ break;
+
+ case KEY_SELECT: /* Select font from DRCS */
+ if ( change && !override ) {
+ warning( "Changes not saved" );
+ error = true;
+ override = true;
+ } else {
+ extract_entry( current_entry );
+ draw_current();
+ }
+ break;
+
+ case KEY_PREV: /* Move to prev entry in DRCS */
+ if ( change && !override ) {
+ warning( "Changes not saved" );
+ override = true;
+ error = true;
+ } else {
+ print_entry( current_entry, false );
+ current_entry = current_entry - 1;
+ if ( current_entry == 0 )
+ current_entry = TOTAL_ENTRIES - 2;
+ print_entry( current_entry, true );
+ }
+ break;
+
+ case KEY_NEXT: /* Move to next entry in DRCS */
+ if ( change && !override ) {
+ warning( "Changes not saved" );
+ override = true;
+ error = true;
+ } else {
+ print_entry( current_entry, false );
+ current_entry = current_entry + 1;
+ if ( current_entry == TOTAL_ENTRIES - 1 )
+ current_entry = 1;
+ print_entry( current_entry, true );
+ }
+ break;
+
+ case KEY_UP: /* UP one row. */
+ if ( row == 0 )
+ row = MAX_ROWS;
+ row = row - 1;
+ break;
+
+ case KEY_DOWN: /* Guess. */
+ row = ( row + 1 ) % MAX_ROWS;
+ break;
+
+ case KEY_RIGHT:
+ col = ( col + 1 ) % MAX_COLS;
+ break;
+
+ case KEY_LEFT:
+ if ( col == 0 )
+ col = MAX_COLS;
+ col = col - 1;
+ break;
+
+ case KEY_HELP: /* Display helpful info */
+ clear_screen();
+ help();
+ c = getchar();
+ init_restore();
+ break;
+
+ case '\004': /* All done! */
+ return;
+
+ case '\f': /* Redraw display */
+ init_restore();
+ break;
+
+ default: /* user is a klutzy typist */
+ move ( ERROR_ROW, ERROR_COL );
+ printf( "Unknown key: " );
+ if ( c < 0x20 ) {
+ printf( "^%c", c );
+ } else if ( c < 0x0100 ) {
+ printf( "%c", c );
+ } else {
+ printf( "0x%04x", c );
+ }
+ fflush( stdout );
+ error = true;
+ }
+
+ highlight( row, col, true ); /* turn cursor on */
+ }
+}
+
+
+
+char *key_table[] = {
+ "\033[1~", /* Find */
+ "\033[2~", /* Insert */
+ "\033[3~", /* Remove */
+ "\033[4~", /* Select */
+ "\033[5~", /* Prev */
+ "\033[6~", /* Next */
+ "\033[17~",
+ "\033[18~",
+ "\033[19~",
+ "\033[20~",
+ "\033[21~",
+ "\033[23~",
+ "\033[24~",
+ "\033[25~",
+ "\033[26~",
+ "\033[28~",
+ "\033[29~",
+ "\033[31~",
+ "\033[32~",
+ "\033[33~",
+ "\033[34~",
+ "\033[A",
+ "\033[B",
+ "\033[C",
+ "\033[D",
+ (char *)0 };
+
+/*
+ * get_key
+ * Convert VT220 escape sequence into something more reasonable.
+ */
+
+int
+get_key()
+{
+ register char *p;
+ char s[10];
+ register int i, j;
+
+ p = s;
+ for ( i = 0; i < 10; ++i ) {
+ *p = getchar();
+ if ( i == 0 && *p != '\033' )
+ return( (int)*p ); /* Not an escape sequence */
+ if ( *p != '\033' && *p < 0x0020 )
+ return( (int)*p ); /* Control character */
+ *++p = '\0'; /* Null terminate */
+ for ( j = 0; key_table[j]; ++j ) {
+ if ( strcmp( s, key_table[j] ) == 0 ) {
+ return( j | 0x0100 );
+ }
+ }
+ }
+ return( -1 );
+}
+
+
+
+/*
+ * pad
+ * Emit nulls so that the terminal can catch up.
+ */
+
+pad()
+{
+ int i;
+
+ for ( i = 0; i < 20; ++i )
+ putchar( '\000' );
+ fflush( stdout );
+}
+
+
+
+/*
+ * init_restore
+ * refresh the main display table.
+ */
+
+void
+init_restore()
+{
+ register int row, col;
+ register int i;
+
+ void draw_current(), clear_screen(), print_entry();
+
+ clear_screen();
+
+ for ( col = 0; col < MAX_COLS; ++col ) {
+ move( ROW_OFFSET - 2, col * 3 + COL_OFFSET + 1 );
+ printf( "%d", col );
+ }
+ move( ROW_OFFSET - 1, COL_OFFSET );
+ printf( "+--+--+--+--+--+--+--+--+" );
+ move( ROW_OFFSET + MAX_ROWS * 2, COL_OFFSET );
+ printf( "+--+--+--+--+--+--+--+--+" );
+
+ for ( row = 0; row < MAX_ROWS; ++row ) {
+ if ( row != 0 && row != 7 ) {
+ move( row * 2 + ROW_OFFSET, COL_OFFSET - 2 );
+ printf( "%d|", row );
+ move( row * 2 + ROW_OFFSET + 1, COL_OFFSET - 1 );
+ printf( "|" );
+ move( row * 2 + ROW_OFFSET, COL_OFFSET + MAX_COLS * 3 );
+ printf( "|" );
+ move( row * 2 + ROW_OFFSET + 1, COL_OFFSET + MAX_COLS * 3 );
+ printf( "|" );
+ } else {
+ move( row * 2 + ROW_OFFSET, COL_OFFSET - 2 );
+ printf( "%d*", row );
+ move( row * 2 + ROW_OFFSET + 1, COL_OFFSET - 1 );
+ printf( "*" );
+ move( row * 2 + ROW_OFFSET, COL_OFFSET + MAX_COLS * 3 );
+ printf( "*" );
+ move( row * 2 + ROW_OFFSET + 1, COL_OFFSET + MAX_COLS * 3 );
+ printf( "*" );
+ }
+ }
+ draw_current();
+
+ move( TABLE_ROW - 1, TABLE_COL - 1 );
+ printf( "+-+-+-+-+-+-+-+-+-+-+-+-+" );
+ move( TABLE_ROW + 8 * 2 - 1, TABLE_COL - 1 );
+ printf( "+-+-+-+-+-+-+-+-+-+-+-+-+" );
+ for ( i = 0; i < 8; ++i ) {
+ move ( TABLE_ROW + i * 2, TABLE_COL - 1 );
+ printf( "|" );
+ move ( TABLE_ROW + i * 2 + 1, TABLE_COL - 1 );
+ printf( "+" );
+ move ( TABLE_ROW + i * 2, TABLE_COL + 12 * 2 - 1);
+ printf( "|" );
+ move ( TABLE_ROW + i * 2 + 1, TABLE_COL +12 * 2 - 1);
+ printf( "+" );
+ }
+ for ( i = 0; i < TOTAL_ENTRIES; ++i )
+ print_entry( i, (i == current_entry) ? true : false );
+}
+
+
+
+/*
+ * draw_current
+ * Draw the complete current entry.
+ */
+
+void
+draw_current()
+{
+ register int row, col;
+
+ printf( "\033)0" ); /* Special graphics in G1 */
+ printf( "\016" ); /* Lock in G1 (SO) */
+
+ for ( row = 0; row < MAX_ROWS; ++row ) {
+ for ( col = 0; col < MAX_COLS; ++col ) {
+ if ( display_table[row][col] ) {
+ move( row * 2 + ROW_OFFSET, col * 3 + COL_OFFSET );
+ printf( "\141\141\141" );
+ move( row * 2 + ROW_OFFSET + 1, col * 3 + COL_OFFSET );
+ printf( "\141\141\141" );
+ } else {
+ move( row * 2 + ROW_OFFSET, col * 3 + COL_OFFSET );
+ printf( " " ); /* erase splat */
+ move( row * 2 + ROW_OFFSET + 1, col * 3 + COL_OFFSET );
+ printf( " " ); /* erase splat */
+ }
+ }
+ pad();
+ }
+ printf( "\017" ); /* Lock in G0 (SI) */
+ fflush( stdout );
+}
+
+
+
+/*
+ * highlight
+ * Draw the cursor in the main display area.
+ */
+
+void
+highlight( row, col, on )
+unsigned int row, col;
+bool on;
+{
+
+ printf( "\033)0" ); /* Special graphics in G1 */
+ printf( "\016" ); /* Lock in G1 (SO) */
+ if ( on ) {
+ printf( "\033[7m" ); /* Reverse video cursor */
+ }
+
+ if ( display_table[row][col] ) {
+ move( row * 2 + ROW_OFFSET, col * 3 + COL_OFFSET );
+ printf( "\141\141\141" );
+ move( row * 2 + ROW_OFFSET + 1, col * 3 + COL_OFFSET );
+ printf( "\141\141\141" );
+ } else {
+ move( row * 2 + ROW_OFFSET, col * 3 + COL_OFFSET );
+ printf( " " ); /* erase splat */
+ move( row * 2 + ROW_OFFSET + 1, col * 3 + COL_OFFSET );
+ printf( " " ); /* erase splat */
+ }
+ pad();
+ printf( "\017" ); /* Lock in G0 (SI) */
+ printf( "\033[0m" ); /* normal video */
+ printf( "\b" ); /* Back up one spot */
+ fflush( stdout );
+}
+
+
+
+/*
+ * Clear_screen
+ */
+
+void
+clear_screen()
+{
+ printf( "\033[H\033[J" ); /* Clear screen. */
+ fflush( stdout );
+}
+
+
+
+/*
+ * move
+ */
+
+move( y, x )
+int y, x;
+{
+ printf( "\033[%d;%df", y, x );
+}
+
+
+
+/*
+ * Build_entry
+ * Convert the bit pattern used in the main display area into something
+ * that the vt220 can digest - namely sixels...
+ */
+
+void
+build_entry( entry_no )
+unsigned int entry_no;
+{
+ register int row, col;
+ register unsigned int mask;
+
+ for ( col = 0; col < 8; ++col ) {
+
+ /* Top set of sixels */
+
+ mask = 0;
+ for ( row = 5; row >= 0; --row ) {
+ mask = mask << 1;
+ if ( display_table[row][col] )
+ mask |= 1;
+ }
+ font_table[entry_no][col] = mask + 077;
+
+ /* Bottom set of sixels */
+
+ mask = 0;
+ for ( row = 9; row >= 6; --row ) {
+ mask = mask << 1;
+ if ( display_table[row][col] )
+ mask |= 1;
+ }
+ font_table[entry_no][col+8] = mask + 077;
+ }
+
+}
+
+
+
+/*
+ * Extract_engry
+ * convert sixel representation into an array of bits.
+ */
+
+void
+extract_entry( entry_no )
+unsigned int entry_no;
+{
+ register int row, col;
+ register unsigned int mask;
+
+ for ( col = 0; col < 8; ++col ) {
+
+ /* Top set of sixels */
+
+ mask = font_table[entry_no][col];
+ if ( mask >= 077 )
+ mask -= 077;
+ else
+ mask = 0; /* Bogus entry */
+
+ for ( row = 0; row <= 5; ++row ) {
+ display_table[row][col] = (bool)(mask & 0x0001);
+ mask = mask >> 1;
+ }
+
+ /* Bottom set of sixels */
+
+ mask = font_table[entry_no][col+8];
+ if ( mask >= 077 )
+ mask -= 077;
+ else
+ mask = 0;
+
+ for ( row = 6; row <= 9; ++row ) {
+ display_table[row][col] = (bool)(mask & 0x0001);
+ mask = mask >> 1;
+ }
+ }
+
+}
+
+
+
+/*
+ * Send_entry
+ * Emit the stuff used by the VT220 to load a character into the
+ * DRCS. We could, of course, send more than one entry at a time...
+ */
+
+void
+send_entry( entry_no )
+int entry_no;
+{
+ register char *fp = font_table[entry_no];
+
+ printf( "\033P1;%d;1;0;0;0{ @%c%c%c%c%c%c%c%c/%c%c%c%c%c%c%c%c\033\\",
+ entry_no,
+ fp[ 0], fp[ 1], fp[ 2], fp[ 3], fp[ 4], fp[ 5], fp[ 6], fp[ 7],
+ fp[ 8], fp[ 9], fp[10], fp[11], fp[12], fp[13], fp[14], fp[15] );
+}
+
+
+
+/*
+ * Print_entry
+ * The terminal normally has G0 in GL. We don't want to change
+ * this, nor do we want to use GR. Sooooo send out the necessary
+ * magic for shifting in G2 temporarily for the character that we
+ * want to display.
+ */
+
+void
+print_entry( entry_no, highlight )
+register unsigned int entry_no;
+bool highlight;
+{
+
+ register int y, x;
+
+ y = entry_no & 0x07;
+ x = entry_no >> 3 & 0x1f;
+ entry_no += 32; /* Map up to G set */
+
+ move( y * 2 + TABLE_ROW, x * 2 + TABLE_COL );
+
+ if ( highlight )
+ printf( "\033[7m" );
+
+ printf( "\033* @" ); /* select DRCS into G2 */
+ printf( "\033N" ); /* select single shift */
+ printf( "%c", entry_no ); /* Draw the character */
+
+ if ( highlight )
+ printf( "\033[0m" );
+}
+
+
+
+/*
+ * Save_table
+ * Save a font table
+ */
+
+void
+save_table( font_file )
+FILE *font_file;
+{
+ register char *fp;
+ register int i;
+
+ for ( i = 0; i < TOTAL_ENTRIES; ++i ) {
+ fp = font_table[i];
+ fprintf( font_file, "\033P1;%d;1;0;0;0{ @%c%c%c%c%c%c%c%c/%c%c%c%c%c%c%c%c\033\\\n",
+ i,
+ fp[ 0], fp[ 1], fp[ 2], fp[ 3], fp[ 4], fp[ 5], fp[ 6], fp[ 7],
+ fp[ 8], fp[ 9], fp[10], fp[11], fp[12], fp[13], fp[14], fp[15] );
+ }
+}
+
+
+
+/*
+ * Get_table
+ * Extract font table entries from a file
+ */
+
+void
+get_table( font_file )
+FILE *font_file;
+{
+ char s[256];
+ register char *p;
+ char *fp;
+ int i;
+ register int j;
+
+ while( fgets( s, 255, font_file ) ) {
+ if ( strncmp( s, "\033P1;", 4 ) != 0 )
+ continue; /* Bogus line */
+ p = &s[4];
+ if ( sscanf( p, "%d", &i ) != 1 )
+ continue; /* Illegal entry number */
+
+ if ( i <= 0 || TOTAL_ENTRIES <= i )
+ continue; /* Bogues entry */
+
+ fp = font_table[i];
+
+ while ( *p && *p != '@' )
+ ++p; /* Skip to font definition */
+ if ( ! *p++ )
+ continue; /* Skip @ */
+
+ for ( j = 0; *p && *p != '\033' && j < 16; ++j, ++p ) {
+ if ( *p == '/' ) {
+ j = 8;
+ ++p;
+ }
+ fp[j] = *p;
+ }
+ send_entry( i );
+ }
+}
+
+
+
+/*
+ * Help
+ * Print out help information.
+ */
+
+void
+help()
+{
+ printf( "Font editor\n\n" );
+ printf( "F6 - Pixel on\n" );
+ printf( "F7 - Pixel off\n" );
+ printf( "F13 - Clear display area\n" );
+ printf( "HELP - This screen\n" );
+ printf( "FIND - Update font table\n" );
+ printf( "INSERT - Insert a blank row\n" );
+ printf( "REMOVE - Remove a row\n" );
+ printf( "SELECT - Select current font table entry\n" );
+ printf( "PREV - Move to previous font table entry\n" );
+ printf( "NEXT - Move to next font table entry\n" );
+ printf( "^D - Exit\n" );
+ printf( "\n\n\n\nPress any key to continue\n" );
+}
+
+
+
+/*
+ * Warning
+ * Issue a warning to the regarding the current status.
+ */
+
+void
+warning( s )
+char *s;
+{
+ move( ERROR_ROW, ERROR_COL );
+ printf( "Warning: %s!\n", s );
+ move( ERROR_ROW+1, ERROR_COL );
+ printf( " Reissue command to override\n" );
+}
diff --git a/usr.sbin/pcvt/fonts/COPYRIGHT b/usr.sbin/pcvt/fonts/COPYRIGHT
new file mode 100644
index 0000000..175f7b8
--- /dev/null
+++ b/usr.sbin/pcvt/fonts/COPYRIGHT
@@ -0,0 +1,38 @@
+The font files:
+
+ vt100pc.814.uu, vt100sg.814.uu, vt220h.808.uu, vt220h.810.uu,
+ vt220h.814.uu, vt220h.816.uu, vt220l.808.uu, vt220l.810.uu,
+ vt220l.814.uu and vt220l.816.uu
+
+in this directory are
+
+ Copyright (c) 1992, 1993, 1994 Hellmuth Michaelis and Joerg Wunsch
+
+ All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer in the
+ documentation and/or other materials provided with the distribution.
+ 3. All advertising materials mentioning features or use of this software
+ must display the following acknowledgement:
+ This product includes software developed by
+ Hellmuth Michaelis and Joerg Wunsch
+ 4. The name authors may not be used to endorse or promote products
+ derived from this software without specific prior written permission.
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
+ IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
diff --git a/usr.sbin/pcvt/fonts/Makefile b/usr.sbin/pcvt/fonts/Makefile
new file mode 100644
index 0000000..9d9bf50
--- /dev/null
+++ b/usr.sbin/pcvt/fonts/Makefile
@@ -0,0 +1,70 @@
+.if !exists(${.CURDIR}/../Makefile.inc)
+error:
+ @echo
+ @echo " You MUST link/copy"
+ @echo
+ @echo " ../Makefile.inc.NetBSD or ../Makefile.inc.FreeBSD"
+ @echo
+ @echo " to ../Makefile.inc before you can proceed !"
+ @echo
+.else
+
+#FONTS = vt100pc.814 vt100sg.814 vt220l.814 vt220h.814 vt220l.808\
+# vt220h.808 vt220l.816 vt220h.816 vt220l.810 vt220h.810
+
+FONTS = vt220l.814 vt220h.814 vt220l.808 vt220h.808 \
+ vt220l.816 vt220h.816 vt220l.810 vt220h.810
+
+LIBMODE = 644
+LIBOWN = bin
+LIBGRP = bin
+CLEANFILES= ${FONTS}
+
+all: $(FONTS)
+
+install: ${FONTS}
+ @if [ ! -d ${DESTDIR}${FONTDIR} ]; then mkdir ${DESTDIR}${FONTDIR};fi
+ @for i in ${FONTS}; do \
+ ${ECHO} "installing font $$i into ${DESTDIR}${FONTDIR}"; \
+ ${INSTALL} ${COPY} -m ${LIBMODE} -o ${LIBOWN} -g ${LIBGRP} \
+ $$i ${DESTDIR}${FONTDIR}; \
+ done
+
+clean:
+ rm -f ${CLEANFILES}
+
+.include <bsd.prog.mk>
+
+# this seems to be the lowest common denominator
+
+vt100pc.814: ${.CURDIR}/vt100pc.814.uu
+ uudecode ${.CURDIR}/$@.uu
+
+vt100sg.814: ${.CURDIR}/vt100sg.814.uu
+ uudecode ${.CURDIR}/$@.uu
+
+vt220l.814: ${.CURDIR}/vt220l.814.uu
+ uudecode ${.CURDIR}/$@.uu
+
+vt220h.814: ${.CURDIR}/vt220h.814.uu
+ uudecode ${.CURDIR}/$@.uu
+
+vt220l.808: ${.CURDIR}/vt220l.808.uu
+ uudecode ${.CURDIR}/$@.uu
+
+vt220h.808: ${.CURDIR}/vt220h.808.uu
+ uudecode ${.CURDIR}/$@.uu
+
+vt220l.816: ${.CURDIR}/vt220l.816.uu
+ uudecode ${.CURDIR}/$@.uu
+
+vt220h.816: ${.CURDIR}/vt220h.816.uu
+ uudecode ${.CURDIR}/$@.uu
+
+vt220l.810: ${.CURDIR}/vt220l.810.uu
+ uudecode ${.CURDIR}/$@.uu
+
+vt220h.810: ${.CURDIR}/vt220h.810.uu
+ uudecode ${.CURDIR}/$@.uu
+
+.endif
diff --git a/usr.sbin/pcvt/fonts/vt100pc.814.uu b/usr.sbin/pcvt/fonts/vt100pc.814.uu
new file mode 100644
index 0000000..470f9c9
--- /dev/null
+++ b/usr.sbin/pcvt/fonts/vt100pc.814.uu
@@ -0,0 +1,83 @@
+begin 644 vt100pc.814
+M`$1D5$Q$`"`@("`^````/$`X!'@`(B(^(B(````\0#@$>``B%`@4(@```'Q`
+M>$!\`"(4"!0B````?$!X0'P`/@@("`@```!\0'A`?``<(B(J'`(``#A$?$1$
+M`"0H,"@D````>$1X1'@`("`@(#X```!X1'A$>``>(!P"/````$1$?$1$`#X(
+M"`@(````0$!`0'P`/B`\("````!$1$0H$``^"`@("````'Q`>$!``#X@/"`@
+M````/$!`0#P`/"(\)"(````\0#@$>``<(B(B'````#Q`.`1X`!P("`@<````
+M>$1$1'@`("`@(#X```!X1$1$>``$#`0$#@```'A$1$1X`!PB#!`^````>$1$
+M1'@`'`(,`AP```!X1$1$>``$"!(^`@```$1D5$Q$`"(D."0B````/$`X!'@`
+M(A0("`@```!\0'A`?``\(CPB/````#Q`0$`\`"(R*B8B````?$!X0'P`(C8J
+M(B(````\0#@$>``B(B(B'````'Q`>$!\`!X@("`>````?$!X0$``'B`<`CP`
+M```\0$Q$/``>(!P"/````'A$>$A$`!X@'`(\````1$1$1#@`'B`<`CP`````
+M```````````````````8/#P\&!@`&!@`````8V-C(@``````````````-C9_
+M-C8V?S8V````#`P^8V%@/@-#8SX,#```````86,&#!@S8P``````'#8V'#MN
+M9F8[`````#`P,&````````````````P8,#`P,#`8#```````&`P&!@8&!@P8
+M`````````&8\_SQF```````````8&!C_&!@8````````````````&!@8,```
+M````````_P`````````````````````8&````````0,&#!@P8$`````````^
+M8V=O>W-C8SX```````P</`P,#`P,/P``````/F,#!@P8,&-_```````^8P,#
+M'@,#8SX```````8.'C9F?P8&#P``````?V!@8'X#`V,^```````<,&!@?F-C
+M8SX``````']C`P8,&!@8&```````/F-C8SYC8V,^```````^8V-C/P,#!CP`
+M```````8&````!@8`````````!@8````&!@P```````&#!@P8#`8#`8`````
+M`````'X``'X`````````8#`8#`8,&#!@```````^8V,&#`P`#`P``````#YC
+M8V]O;VY@/@``````"!PV8V-_8V-C``````!^,S,S/C,S,WX``````!XS86!@
+M8&$S'@``````?#8S,S,S,S9\``````!_,3$T/#0Q,7\``````'\Q,30\-#`P
+M>```````'C-A8&!O8S,=``````!C8V-C?V-C8V,``````#P8&!@8&!@8/```
+M````#P8&!@8&9F8\``````!S,S8V/#8V,W,``````'@P,#`P,#$Q?P``````
+MP^?_V\/#P\/#``````!C<WM_;V=C8V,``````!PV8V-C8V,V'```````?C,S
+M,SXP,#!X```````^8V-C8VMO/@8'`````'XS,S,^-C,S<P``````/F-C,!P&
+M8V,^``````#_F9D8&!@8&#P``````&-C8V-C8V-C/@``````P\/#P\/#9CP8
+M``````##P\/#V]O_9F8``````,/#9CP8/&;#PP``````P\/#9CP8&!@\````
+M``#_@X8,&#!AP?\``````#PP,#`P,#`P/```````0&!P.!P.!P,!```````\
+M#`P,#`P,##P````('#9C`````````````````````````````/\`&!@,````
+M`````````````````#P&/F9F.P``````<#`P/#8S,S-N```````````^8V!@
+M8SX```````X&!AXV9F9F.P``````````/F-_8&,^```````<-C(P?#`P,'@`
+M`````````#MF9F8^!F8\````<#`P-CLS,S-S```````,#``<#`P,#!X`````
+M``8&``X&!@8&9F8\````<#`P,S8\-C-S```````<#`P,#`P,#!X`````````
+M`.;_V]O;VP``````````;C,S,S,S```````````^8V-C8SX``````````&XS
+M,S,^,#!X````````.V9F9CX&!@\```````!N.S,P,'@``````````#YC.`YC
+M/@``````"!@8?A@8&!L.``````````!F9F9F9CL``````````,/#PV8\&```
+M````````P\/;V_]F``````````!C-AP<-F,``````````&-C8V,_`P8\````
+M````?V8,&#-_```````.&!@8<!@8&`X``````!@8&!@`&!@8&```````<!@8
+M&`X8&!AP```````[;@``````````````J@"J`*H`J@"J`*H`J@``/&;"P,#"
+M9CP,!GP```#,S`#,S,S,S'8`````#!@P`'S&_L#&?``````0.&P`>`Q\S,QV
+M``````#,S`!X#'S,S'8`````8#`8`'@,?,S,=@`````X;#@`>`Q\S,QV````
+M`````'C,P,QX&`QX````$#AL`'S&_L#&?```````S,P`?,;^P,9\`````&`P
+M&`!\QO[`QGP``````,S,`'`P,#`P>``````P>,P`<#`P,#!X`````,!@,`!P
+M,#`P,'@`````QL80.&S&QO[&Q@```#AL.``X;,;&_L;&````&#!@`/YB8'Q@
+M8OX`````````;+H2?I"8?@``````/FS,S/[,S,S.`````!`X;`!\QL;&QGP`
+M`````,;&`'S&QL;&?`````!@,!@`?,;&QL9\`````#!XS`#,S,S,S'8`````
+M8#`8`,S,S,S,=@``````QL8`QL;&QGX&#'@``,;&.&S&QL;&;#@`````QL8`
+MQL;&QL;&?``````("'[(R,C(?@@(`````#AL9&#P8&!@YOP``````,9L.!#^
+M$/X0$`````#XS,SXQ,S>S,SF`````!PT,#`P_#`P,#"PX```&#!@`'@,?,S,
+M=@`````,&#``.!@8&!@\`````!@P8`!\QL;&QGP`````&#!@`,S,S,S,=@``
+M````=MP`W&9F9F9F````=MP`QN;V_M[.QL8`````/&QL/@!^```````````X
+M;&PX`'P````````````P,``P,&#&QGP```````````!_8&!@````````````
+M`'\!`0$``````,#`QLS8,&#<A@P8/@``P,#&S-@P9LZ:/@8&````&!@`&!@\
+M/#P8`````````#9LV&PV````````````V&PV;-@``````"*((H@BB"*((H@B
+MB"*(JE2J5*I4JE2J5*I4JE2Z[KKNNNZZ[KKNNNZZ[A@8&!@8&!@8&!@8&!@8
+M&!@8&!@8&/@8&!@8&!@8&!@8&/@8^!@8&!@8&#8V-C8V-C;V-C8V-C8V````
+M`````/XV-C8V-C8``````/@8^!@8&!@8&#8V-C8V]@;V-C8V-C8V-C8V-C8V
+M-C8V-C8V-C8``````/X&]C8V-C8V-C8V-C8V]@;^````````-C8V-C8V-OX`
+M```````8&!@8&/@8^`````````````````#X&!@8&!@8&!@8&!@8&!\`````
+M```8&!@8&!@8_P````````````````#_&!@8&!@8&!@8&!@8&!\8&!@8&!@`
+M````````_P```````!@8&!@8&!C_&!@8&!@8&!@8&!@?&!\8&!@8&!@V-C8V
+M-C8V-S8V-C8V-C8V-C8V-S`_```````````````_,#<V-C8V-C8V-C8V-O<`
+M_P``````````````_P#W-C8V-C8V-C8V-C8W,#<V-C8V-C8``````/\`_P``
+M`````#8V-C8V]P#W-C8V-C8V&!@8&!C_`/\````````V-C8V-C8V_P``````
+M````````_P#_&!@8&!@8`````````/\V-C8V-C8V-C8V-C8V/P```````!@8
+M&!@8'Q@?```````````````?&!\8&!@8&!@`````````/S8V-C8V-C8V-C8V
+M-C;_-C8V-C8V&!@8&!C_&/\8&!@8&!@8&!@8&!@8^``````````````````?
+M&!@8&!@8__________________\`````````__________#P\/#P\/#P\/#P
+M\/#P#P\/#P\/#P\/#P\/#P__________````````````````=MS8V-QV````
+M`````'S&_,;&_,#`0````/[&QL#`P,#`P`````````#^;&QL;&QL``````#^
+MQF`P&#!@QOX``````````'[8V-C8<`````````!F9F9F?&!@P````````';<
+M&!@8&!@``````/PP>,S,S'@P_```````.&S&QO[&QFPX```````X;,;&QFQL
+M;.X``````#Q@,!A\S,S,>```````````?-;6?``````````&#'S.UN9\8,``
+M`````#A@P,#XP,!@.````````'S&QL;&QL;&````````_@``_@``_@``````
+M`#`P,/PP,#``_```````8#`8#!@P8`#\```````8,&#`8#`8`/P```````X:
+M&A@8&!@8&!@8&!@8&!@8&!@86%AP```````P,```_```,#``````````=MP`
+M=MP```````!PV-AP````````````````````&!@`````````````````&```
+M```````>&!@8&!C8V'@X`````-AL;&QL;```````````<-@P8,CX````````
+=```````^/CX^/CX`````````````````````````
+`
+end
diff --git a/usr.sbin/pcvt/fonts/vt100sg.814.uu b/usr.sbin/pcvt/fonts/vt100sg.814.uu
new file mode 100644
index 0000000..24ea4c7
--- /dev/null
+++ b/usr.sbin/pcvt/fonts/vt100sg.814.uu
@@ -0,0 +1,83 @@
+begin 644 vt100sg.814
+M`$1D5$Q$`"`@("`^````/$`X!'@`(B(^(B(````\0#@$>``B%`@4(@```'Q`
+M>$!\`"(4"!0B````?$!X0'P`/@@("`@```!\0'A`?``<(B(J'`(``#A$?$1$
+M`"0H,"@D````>$1X1'@`("`@(#X```!X1'A$>``>(!P"/````$1$?$1$`#X(
+M"`@(````0$!`0'P`/B`\("````!$1$0H$``^"`@("````'Q`>$!``#X@/"`@
+M````/$!`0#P`/"(\)"(````\0#@$>``<(B(B'````#Q`.`1X`!P("`@<````
+M>$1$1'@`("`@(#X```!X1$1$>``$#`0$#@```'A$1$1X`!PB#!`^````>$1$
+M1'@`'`(,`AP```!X1$1$>``$"!(^`@```$1D5$Q$`"(D."0B````/$`X!'@`
+M(A0("`@```!\0'A`?``\(CPB/````#Q`0$`\`"(R*B8B````?$!X0'P`(C8J
+M(B(````\0#@$>``B(B(B'````'Q`>$!\`!X@("`>````?$!X0$``'B`<`CP`
+M```\0$Q$/``>(!P"/````'A$>$A$`!X@'`(\````1$1$1#@`'B`<`CP`````
+M```````````````````8/#P\&!@`&!@`````8V-C(@``````````````-C9_
+M-C8V?S8V````#`P^8V%@/@-#8SX,#```````86,&#!@S8P``````'#8V'#MN
+M9F8[`````#`P,&````````````````P8,#`P,#`8#```````&`P&!@8&!@P8
+M`````````&8\_SQF```````````8&!C_&!@8````````````````&!@8,```
+M````````_P`````````````````````8&````````0,&#!@P8$`````````^
+M8V=O>W-C8SX```````P</`P,#`P,/P``````/F,#!@P8,&-_```````^8P,#
+M'@,#8SX```````8.'C9F?P8&#P``````?V!@8'X#`V,^```````<,&!@?F-C
+M8SX``````']C`P8,&!@8&```````/F-C8SYC8V,^```````^8V-C/P,#!CP`
+M```````8&````!@8`````````!@8````&!@P```````&#!@P8#`8#`8`````
+M`````'X``'X`````````8#`8#`8,&#!@```````^8V,&#`P`#`P``````#YC
+M8V]O;VY@/@``````"!PV8V-_8V-C``````!^,S,S/C,S,WX``````!XS86!@
+M8&$S'@``````?#8S,S,S,S9\``````!_,3$T/#0Q,7\``````'\Q,30\-#`P
+M>```````'C-A8&!O8S,=``````!C8V-C?V-C8V,``````#P8&!@8&!@8/```
+M````#P8&!@8&9F8\``````!S,S8V/#8V,W,``````'@P,#`P,#$Q?P``````
+MP^?_V\/#P\/#``````!C<WM_;V=C8V,``````!PV8V-C8V,V'```````?C,S
+M,SXP,#!X```````^8V-C8VMO/@8'`````'XS,S,^-C,S<P``````/F-C,!P&
+M8V,^``````#_F9D8&!@8&#P``````&-C8V-C8V-C/@``````P\/#P\/#9CP8
+M``````##P\/#V]O_9F8``````,/#9CP8/&;#PP``````P\/#9CP8&!@\````
+M``#_@X8,&#!AP?\``````#PP,#`P,#`P/```````0&!P.!P.!P,!```````\
+M#`P,#`P,##P````('#9C```````````````````````````````````0.'S^
+M_GPX$```````_H*"_H*"_H*"_@````!$1'Q$1``^"`@("````'Q`>$!\`#X@
+M."`@````/$!`0#P`/"(\)"(```!`0$!`?``^(#P@(````'#8V'``````````
+M`````#`P,/PP,#``_`````!$9%1,1``@("`@/@```$1$1"@0`#X("`@(```8
+M&!@8&!@8^`````````````````#X&!@8&!@8`````````!\8&!@8&!@8&!@8
+M&!@8'P```````!@8&!@8&!C_&!@8&!@8``#_````````````````````_P``
+M`````````````````/\```````````````````#_````````````````````
+M_P```!@8&!@8&!@?&!@8&!@8&!@8&!@8&/@8&!@8&!@8&!@8&!@8_P``````
+M``````````#_&!@8&!@8&!@8&!@8&!@8&!@8&!@``!@P8,!@,!@`_```````
+M8#`8#!@P8`#\`````````/YL;&QL;&P````````"!'X($'X@0``````X;&1@
+M\&!@8.;\`````````````!@`````````J@"J`*H`J@"J`*H`J@``````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````&!@`&!@\/#P8``````@(?LC(R,A^"`@`````.&QD8/!@8&#F_```
+M``````````````````````#&;#@0$/X0$!````````````````````````!\
+MQF`X;,;&;#@,QGP```"!F3QF9F8\F8$``````'R"FJ*BHIJ"?``````\;&P^
+M`'X``````````````#9LV&PV````````````````````````````````````
+M```````````````````````````````````````````````````\)"0\````
+M````````&!A^&!@`?@```````'S&!@0,&##&_@``````?,8&!CP&!L9\````
+M````````````````````````9F9F9GQ@8,``````?O3T]'04%!04````````
+M```8&```````````````````````````````"!@X&!@8&!A^`````#AL;#@`
+M?```````````````V&PV;-@```````#`P,;,V#!FSIH^!@8``,#`QLS8,&#<
+MA@P8/@``````-FS8;#8`````````,#``,#!@QL9\````,!@`$#ALQL;^QL8`
+M```,&``0.&S&QO[&Q@```!!LQA`X;,;&_L;&````=MP`$#ALQL;^QL8`````
+MQL80.&S&QO[&Q@```#AL.``X;,;&_L;&```````^;,S,_LS,S,X``````#QF
+MPL#`PF8\#`9\`#`8#`#^8F!\8&+^````&#!@`/YB8'Q@8OX````0;,8`_F)@
+M?&!B_@````#&Q@#^8F!\8&+^````,!@`/!@8&!@8&#P````,&``\&!@8&!@8
+M/````#QF`#P8&!@8&!@\````9F8`/!@8&!@8&#P`````````````````````
+M`';<`,;F]O[>SL;&````8#``.&S&QL;&;#@````,&``X;,;&QL9L.````!!L
+M@CALQL;&QFPX````=MP`.&S&QL;&;#@`````QL8X;,;&QL9L.```````?LS,
+MS,[,S,Q^```````".FS.UM;F;+B```!@,!@`QL;&QL;&?`````8,&`#&QL;&
+MQL9\`````';<`,;&QL;&QGP`````QL8`QL;&QL;&?````,;&`,;&;#@0$!`X
+M`````````````````````````'S&QMS&QOS`P,````!@,!@`>`Q\S,QV````
+M`!@P8`!X#'S,S'8`````$#AL`'@,?,S,=@``````=MP`>`Q\S,QV``````#,
+MS`!X#'S,S'8`````.&PX`'@,?,S,=@````````!LNA)^D)A^`````````'C,
+MP,QX&`QX````8#`8`'S&_L#&?``````,&#``?,;^P,9\`````!`X;`!\QO[`
+MQGP``````,S,`'S&_L#&?`````#`8#``<#`P,#!X``````P8,``X&!@8&#P`
+M````,'C,`'`P,#`P>```````S,P`<#`P,#!X````````````````````````
+M`';<`-QF9F9F9@````!@,!@`?,;&QL9\`````!@P8`!\QL;&QGP`````.&S&
+M`'S&QL;&?```````=MP`?,;&QL9\``````#&Q@!\QL;&QGP``````````'R2
+MDIR0?@````````)\SM;6YGR``````&`P&`#,S,S,S'8`````&#!@`,S,S,S,
+M=@`````P>,P`S,S,S,QV``````#,S`#,S,S,S'8``````,;&`,;&QL9^!@QX
+=````````````````````````````````````````
+`
+end
diff --git a/usr.sbin/pcvt/fonts/vt220h.808.uu b/usr.sbin/pcvt/fonts/vt220h.808.uu
new file mode 100644
index 0000000..c9585dc
--- /dev/null
+++ b/usr.sbin/pcvt/fonts/vt220h.808.uu
@@ -0,0 +1,49 @@
+begin 644 vt220h.808
+M9%1,1"`@(#X\0#@$>"(^(CQ`.`1X-@@V?$!X0'PV"#9\0'A`?#X("'Q`>'P<
+M(BH<.$1\1"PP*"1X1'A$>"`@/GA$>$1^$`P\1$1\1#X("`A`0$!\/B`\($1$
+M*!`^"`@(?$!X0#X@/"`\0$`\/"(\)CQ`.`1X'"(</$`X!'@<"!QX1$1X("`@
+M/GA$1'@$#`0.>$1X'"(,$#YX1'@<`@P"''A$1'@,$CX"9%1,(B0X)"(\0#@$
+M>"(<"'Q`>'P\(CP^/$`\(C(J)B)\0'A`?"(V*CQ`.`1X(B(<?$!X0'P>(!Y\
+M0'A`'B`>/#Q`3#P>(!X\>$1X3!X@'CQ$1#@>(!P"/'"(<(AP#!(,<(APB'`,
+M!`YPB'"(?"0(/'"(<(QB#@(<<(APC'@2/@)PB'"><!P"''"(<(YP/"(<<(AP
+MCG($"`APB'"<<APB''"(<)QR#@(<<(APB'P2'A)PB'"<<AP2''"(<(AP#A`.
+M<(APB'`<$AQPB'"><!P0'G"(<)YP'!`0<(AX"'`,$@QPB'@(<`P$#G"(>`A\
+M)`@\<(AX#&(.`AQPB'@,>!(^`G"(>!YP'`(<<(AX#G`\(AQPB'@><@0("'"(
+M>!QR'"(<<(AX''(.`AQPB'@(?!(>$G"(>!QR'!(<<(AX"'`.$`YPB'@(<!P2
+M''"(>!YP'!`><(AX'G`<$!```'S&QOS`P````,945'P8`'S&!G[&?```8+`8
+M+$V&`````!@X&!H,````_+8V-@0``'SD<.1\````PJ08*L8``$1$*"@0$```
+M$!`H*$1$``#&QL;&QGP```#\!@8&_````'[`P,!^````PD(D&"08`/Z"*#BJ
+M_@``QE14?!@\`````/YL;.X````$_@'^!````"1^@7XD````$!`H1.X`.$2"
+MJKJJ1#@``-QV`/X``/Z"Q,1H:#``&"PL1$:&_@`````VS,PV```8&`!F9@``
+M`!PT,#`PL.`!`@0($"!`@(!`(!`(!`(!`0($"!`@0/\``#\0"`0"`1@8,&`P
+M&!@8&!@,!@P8&!@```X8&!@8&!@8&!@8&`X```!P&!@8&!@8&!@8&!AP```"
+M`@(B$@H&``#NQM;6?```9F8\&'X8`%@D&"0X8`QX`!@`QL9^#'@`P,#XS/C`
+MP%`@4`P\9CP`\&!X;'A@\``.`&9F/!@\````1"@0*$0`_&9F]F9F_`#P$'(6
+M_CINP@``````&`QX&#!@````````````_@```'R"NJJRJH)\`````'@`````
+M``!X"`@``,;&``````````($?AA^($````````#_````````_P```````/\`
+M``````#_````````_P````````#N9F8L&````GS.UN9\@```?-;<T'P``/X`
+M?,;&?`#^`'@,?,QV`,P`S$AX,'@`.&P`QL;&?``<`,;&QL9\`'``QL;&QGP`
+M`CILUM9LN(``?LS.S,Q^`/XX;,;&;#@`.,8X;,9L.``.`#ALQFPX`'``.&S&
+M;#@`9@`\&!@8/``8)``\&!@\``X`/!@8&#P`<``\&!@8/`#&`/YB>&+^`#A$
+M_F)X8OX`<`#^8GAB_@#^`#ALQO[&`#AL$&S&_L8`#@`X;,;^Q@!P`#ALQO[&
+M```<#!X`````````&!@````('CIB>#P$&'@,.`QX````?(*:HJ*:@GP`P]L\
+M9CS;PP``S%1@4$P``'S&8#``,```?,9@,``P``!\QF`P`#```'S&8#``,```
+M?,9@,``P``!\QF`P`#```'S&8#``,```?,9@,``P``!\QF`P`#```'S&8#``
+M,```?,9@,``P``!\QF`P`#```'S&8#``,```?,9@,``P``!\QF`P`#```'S&
+M8#``,```?,9@,``P``!\QF`P`#```'S&8#``,```?,9@,``P``!\QF`P`#``
+M`'S&8#``,```?,9@,``P``!\QF`P`#```'S&8#``,```?,9@,``P``!\QF`P
+M`#```'S&8#``,```?,9@,``P``!\QF`P`#```'S&8#``,```?,9@,``P``!\
+MQF`P`#```'S&8#``,```?,9@,``P``!\QF`P`#```'S&8#``,```?,9@,``P
+M``!\QF`P`#```'S&8#``,```?,9@,``P``!\QF`P`#```'S&8#``,```?,9@
+M,``P``!\QF`P`#```'S&8#``,```?,9@,``P``!\QF`P`#```'S&8#``,```
+M?,9@,``P``!\QF`P`#```'S&8#``,```?,9@,``P``!\QF`P`#```'S&8#``
+M,```?,9@,``P``!\QF`P`#```'S&8#``,```?,9@,``P``!\QF`P`#```'S&
+M8#``,```?,9@,``P``!\QF`P`#```'S&8#``,```?,9@,``P``!\QF`P`#``
+M`'S&8#``,```?,9@,``P``!\QF`P`#```'S&8#``,```?,9@,``P``!\QF`P
+M`#```'S&8#``,```?,9@,``P``!\QF`P`#```'S&8#``,```?,9@,``P``!\
+MQF`P`#```'S&8#``,```?,9@,``P``!\QF`P`#```'S&8#``,```?,9@,``P
+M``!\QF`P`#```'S&8#``,```?,9@,``P``!\QF`P`#```'S&8#``,```?,9@
+M,``P``!\QF`P`#```'S&8#``,```?,9@,``P``!\QF`P`#```'S&8#``,```
+7?,9@,``P``!\QF`P`#```'S&8#``,`!@
+`
+end
diff --git a/usr.sbin/pcvt/fonts/vt220h.810.uu b/usr.sbin/pcvt/fonts/vt220h.810.uu
new file mode 100644
index 0000000..04689b1
--- /dev/null
+++ b/usr.sbin/pcvt/fonts/vt220h.810.uu
@@ -0,0 +1,60 @@
+begin 644 vt220h.810
+M1&143$0@("`@/CQ`.`1X(B(^(B(\0#@$>"(4"!0B?$!X0'PB%`@4(GQ`>$!\
+M/@@("`A\0'A`?!PB(BH<.$1\1$0D*#`H)'A$>$1X("`@(#YX1'A$>!X@'`(\
+M1$1\1$0^"`@("$!`0$!\/B`\("!$1$0H$#X("`@(?$!X0$`^(#P@(#Q`0$`\
+M/"(\)"(\0#@$>!PB(B(</$`X!'@<"`@(''A$1$1X("`@(#YX1$1$>`0,!`0.
+M>$1$1'@<(@P0/GA$1$1X'`(,`AQX1$1$>`0($CX"1&143$0B)#@D(CQ`.`1X
+M(A0("`A\0'A`?#PB/"(\/$!`0#PB,BHF(GQ`>$!\(C8J(B(\0#@$>"(B(B(<
+M?$!X0'P>("`@'GQ`>$!`'B`<`CP\0$Q$/!X@'`(\>$1X2$0>(!P"/$1$1$0X
+M'B`<`CQPB'"(<`P2$A(,<(APB'`$#`0$#G"(<(AP'"(,$#YPB'"(<!P"#`(<
+M<(APB'`$"!(^`G"(<(AP'A`<`AQPB'"(<!P@/"(<<(APB'`^`@0("'"(<(AP
+M'"(<(AQPB'"(<!PB'@(<<(APB'`,$AX2$G"(<(AP'!(<$AQPB'"(<`X0$!`.
+M<(APB'`<$A(2''"(<(AP'A`<$!YPB'"(<!X0'!`0<(AX"'`,$A(2#'"(>`AP
+M!`P$!`YPB'@(<!PB#!`^<(AX"'`<`@P"''"(>`AP!`@2/@)PB'@(<!X0'`(<
+M<(AX"'`<(!PB''"(>`AP/@($"`APB'@(<!PB'"(<<(AX"'`<(AX"''"(>`AP
+M#!(>$A)PB'@(<!P2'!(<<(AX"'`.$!`0#G"(>`AP'!(2$AQPB'@(<!X0'!`>
+M<(AX"'`>$!P0$```?,;&QOS`P,````#&5%14?!@8`'S&!@9^QL9\``!@L#`8
+M&"Q-A@`````8.!@8&!H,````_+8V-C8&!`````!\Y'#D?`````#"I!@8*L8`
+M``""@D1$*"@0$```$!`H*$1$@H(``,;&QL;&QGP`````_`8&!@;\`````'[`
+MP,#`?@```,)")!@8)!@```#^@B@X*(+^``#&5%14?#@0.````/YL;&QL;.X`
+M````!/X!_@0``````"1^@7XD`````!`0*"A$1.X``#A$@JJZJH)$.````-QV
+M`/X``````/Z"Q,1H:#```!@8+"Q$1H;^```````V2$@V`````!@8``!F9@``
+M`!PT,#`P,+#@```!`@0($"!`@```@$`@$`@$`@$```$"!`@0($#_`````#\0
+M"`0"`0`8&!@P8#`8&!@8&!@8#`8,&!@8&```#A@8&!@8&!@8&!@8&!@8&`X`
+M``!P&!@8&!@8&!@8&!@8&!@8<`````("`B(2"@8``````.[&UM9\``!F9CP8
+M&'X8&`!8)!@8)#A@>`1X`!@P`,;&QGX,>`#`P/C,S/C`P```4"!0&`P\9F8\
+M`/!@>&QX8&#P``8,`&9F/!@8/`````!$*!`H1````/QF9O9F9F;\`/`0<!;N
+M&C9JSH(`````````&`QX&#!@````````````````_@``````?(*ZJKJRJH)\
+M``````!X``````````!X"`@```#&Q@````````````($?@@0?B!`````````
+M`````/\`````````_P````````#_`````````/\`````````_P``````````
+M````[FYF9BP8```"?,[6UN9\@`````!\UM;<T'P``';<`'S&QL9\`';<`'@,
+M?,S,=@#,`,S,2'@P,'@`.&P`QL;&QL9\``P8`,;&QL;&?``P&`#&QL;&QGP`
+M`CILSM;F;+B```!^S,S.S,S,?@!VS#ALQL;&;#@`.,8X;,;&QFPX``X`.&S&
+MQL9L.`!P`#ALQL;&;#@`9@`\&!@8&!@\`!@D`#P8&!@8/``,&``\&!@8&#P`
+M,!@`/!@8&!@\`,8`_F9B>&!B_@`X;`#^8GA@8OX`,!@`_F)X8&+^`';<`!`X
+M;,;^Q@`X;``0.&S&_L8`#!@`$#ALQO[&`&`P`!`X;,;^Q@``#!P,#!X`````
+M`````!@8```````('CID8'@\!!@`>`PX#'@``````'R"FJ*BHIJ"?`##VSQF
+M9CS;PP``````S%1@4$P``'S&QF`P,``P``!\QL9@,#``,```?,;&8#`P`#``
+M`'S&QF`P,``P``!\QL9@,#``,```?,;&8#`P`#```'S&QF`P,``P``!\QL9@
+M,#``,```?,;&8#`P`#```'S&QF`P,``P``!\QL9@,#``,```?,;&8#`P`#``
+M`'S&QF`P,``P``!\QL9@,#``,```?,;&8#`P`#```'S&QF`P,``P``!\QL9@
+M,#``,```?,;&8#`P`#```'S&QF`P,``P``!\QL9@,#``,```?,;&8#`P`#``
+M`'S&QF`P,``P``!\QL9@,#``,```?,;&8#`P`#```'S&QF`P,``P``!\QL9@
+M,#``,```?,;&8#`P`#```'S&QF`P,``P``!\QL9@,#``,```?,;&8#`P`#``
+M`'S&QF`P,``P``!\QL9@,#``,```?,;&8#`P`#```'S&QF`P,``P``!\QL9@
+M,#``,```?,;&8#`P`#```'S&QF`P,``P``!\QL9@,#``,```?,;&8#`P`#``
+M`'S&QF`P,``P``!\QL9@,#``,```?,;&8#`P`#```'S&QF`P,``P``!\QL9@
+M,#``,```?,;&8#`P`#```'S&QF`P,``P``!\QL9@,#``,```?,;&8#`P`#``
+M`'S&QF`P,``P``!\QL9@,#``,```?,;&8#`P`#```'S&QF`P,``P``!\QL9@
+M,#``,```?,;&8#`P`#```'S&QF`P,``P``!\QL9@,#``,```?,;&8#`P`#``
+M`'S&QF`P,``P``!\QL9@,#``,```?,;&8#`P`#```'S&QF`P,``P``!\QL9@
+M,#``,```?,;&8#`P`#```'S&QF`P,``P``!\QL9@,#``,```?,;&8#`P`#``
+M`'S&QF`P,``P``!\QL9@,#``,```?,;&8#`P`#```'S&QF`P,``P``!\QL9@
+M,#``,```?,;&8#`P`#```'S&QF`P,``P``!\QL9@,#``,```?,;&8#`P`#``
+M`'S&QF`P,``P``!\QL9@,#``,```?,;&8#`P`#```'S&QF`P,``P``!\QL9@
+M,#``,```?,;&8#`P`#```'S&QF`P,``P``!\QL9@,#``,```?,;&8#`P`#``
+M`'S&QF`P,``P``!\QL9@,#``,```?,;&8#`P`#```'S&QF`P,``P``!\QL9@
+M,#``,```?,;&8#`P`#```'S&QF`P,``P``!\QL9@,#``,```?,;&8#`P`#``
+H`'S&QF`P,``P``!\QL9@,#``,```?,;&8#`P`#```'S&QF`P,``P`#`P
+`
+end
diff --git a/usr.sbin/pcvt/fonts/vt220h.814.uu b/usr.sbin/pcvt/fonts/vt220h.814.uu
new file mode 100644
index 0000000..9d49552
--- /dev/null
+++ b/usr.sbin/pcvt/fonts/vt220h.814.uu
@@ -0,0 +1,83 @@
+begin 644 vt220h.814
+M`$1D5$Q$`"`@("`^````/$`X!'@`(B(^(B(````\0#@$>``B%`@4(@```'Q`
+M>$!\`"(4"!0B````?$!X0'P`/@@("`@```!\0'A`?``<(B(J'`(``#A$?$1$
+M`"0H,"@D````>$1X1'@`("`@(#X```!X1'A$>``>(!P"/````$1$?$1$`#X(
+M"`@(````0$!`0'P`/B`\("````!$1$0H$``^"`@("````'Q`>$!``#X@/"`@
+M````/$!`0#P`/"(\)"(````\0#@$>``<(B(B'````#Q`.`1X`!P("`@<````
+M>$1$1'@`("`@(#X```!X1$1$>``$#`0$#@```'A$1$1X`!PB#!`^````>$1$
+M1'@`'`(,`AP```!X1$1$>``$"!(^`@```$1D5$Q$`"(D."0B````/$`X!'@`
+M(A0("`@```!\0'A`?``\(CPB/````#Q`0$`\`"(R*B8B````?$!X0'P`(C8J
+M(B(````\0#@$>``B(B(B'````'Q`>$!\`!X@("`>````?$!X0$``'B`<`CP`
+M```\0$Q$/``>(!P"/````'A$>$A$`!X@'`(\````1$1$1#@`'B`<`CP```!P
+MB'"(<``,$A(2#````'"(<(AP``0,!`0.````<(APB'``'"(,$#X```!PB'"(
+M<``<`@P"'````'"(<(AP``0($CX"````<(APB'``'A`<`AP```!PB'"(<``<
+M(#PB'````'"(<(AP`#X"!`@(````<(APB'``'"(<(AP```!PB'"(<``<(AX"
+M'````'"(<(AP``P2'A(2````<(APB'``'!(<$AP```!PB'"(<``.$!`0#@``
+M`'"(<(AP`!P2$A(<````<(APB'``'A`<$!X```!PB'"(<``>$!P0$````'"(
+M>`AP``P2$A(,````<(AX"'``!`P$!`X```!PB'@(<``<(@P0/@```'"(>`AP
+M`!P"#`(<````<(AX"'``!`@2/@(```!PB'@(<``>$!P"'````'"(>`AP`!P@
+M'"(<````<(AX"'``/@($"`@```!PB'@(<``<(APB'````'"(>`AP`!PB'@(<
+M````<(AX"'``#!(>$A(```!PB'@(<``<$AP2'````'"(>`AP``X0$!`.````
+M<(AX"'``'!(2$AP```!PB'@(<``>$!P0'@```'"(>`AP`!X0'!`0````````
+M`'S&QL;&_,#`P``````8V]O;V]M^&!@8````?,8&!G[&QGP``````,#`8#`8
+M'#9C00`````````P<#`P,#08``````````#<9F9F9F8&!@8``````'S&\,#&
+M?`````````#BMAP8.&S&````````@L;&;&PX.!`````````0.#AL;,;&@@``
+M`````,;&QL;&QL9\`````````/P&!@8&_```````````?L#`P,!^````````
+M``#&9CP8/&8\``````#^Q@!L?&P`QOX``````#P8V]O;VWX8/```````_FQL
+M;&QL;&SN````````"`S^`_X,"``````````D9G[#?F8D```````````0$"@H
+M1$3N```````\9L/G_^?#9CP``````````-QV`/X``````````/[&QLQL:#@P
+M````````&#@L;$3&QOX``````````&[8V&X````````````8&```9F8`````
+M```<-#`P,#`PL.`````````!`@0($"!`@````````(!`(!`(!`(!````````
+M``$"!`@0('\````````````_$`@$`@$``!@8&!@8,&`P&!@8&!@8&!@8&!@,
+M!@P8&!@8&!@```X8&!@8&!@8&!@8&!@8&!@8&!@8&!@.``````!P&!@8&!@8
+M&!@8&!@8&!@8&!@8&!@8<````````@("`B(2"@8"``````````!LQM;6UFP`
+M`````,S,>#`P_#`P,```````X'S@P'C`P&`X#'@```P8,`#&QL;&?@8,>```
+M`,#`^,S,^,#`P`````#8<.`P&`P^9F8\``````#P8'AL>&!@8/`````,&`#,
+MS,QX,#`P>`````````!$*!`H1`````````#\9F9F]F9F9OP```#P$'`6_!@P
+M9LZ:/@8&```````````````8#'@`&#!@``````````````````````#^````
+M````````?(*ZJKJRJH)\````````````>````````````````'@("```````
+M`,;&`````````````````````@9^"!!^8$```````````````````/\`````
+M`````````/\``````````````/\``````````````/\``````````````/\`
+M``````````````````#N;F9F;#@````````&?,[>]N9\P```````````?-;6
+MW-!^``````!VW`!\QL;&QGP``````';<`'@,?,S,=@```,S,`,S,2'@P,#!X
+M````.&S&`,;&QL;&QGP````&#!@`QL;&QL;&?````&`P&`#&QL;&QL9\````
+M```&/FS.UM;F;/C``````'[,S,S.S,S,?@```';<`#ALQL;&QFPX````.&S&
+M.&S&QL;&;#@````,&``X;,;&QL9L.````&`P`#ALQL;&QFPX````9F8`/!@8
+M&!@8&#P````\9@`\&!@8&!@8/`````P8`#P8&!@8&!@\````,!@`/!@8&!@8
+M&#P```#&Q@#^9F)X8&)F_@```#ALQ@#^9F!X8&;^````,!@,`/YF8'A@9OX`
+M``!VW``0.&S&QO[&Q@```#ALQA`X;,;&_L;&````#!@`$#ALQL;^QL8````P
+M&``0.&S&QO[&Q@`````,'`P,'@`````````````````8&````````````.`\
+M8,#`P,!@.`QX```\!AP&/`````````````!\@IJBHJ*:@GP```````##VSQF
+M9CS;PP``````````YFQX;&9F``````!\QL9@,#``,#```````'S&QF`P,``P
+M,```````?,;&8#`P`#`P``````!\QL9@,#``,#```````'S&QF`P,``P,```
+M````?,;&8#`P`#`P``````!\QL9@,#``,#```````'S&QF`P,``P,```````
+M?,;&8#`P`#`P``````!\QL9@,#``,#```````'S&QF`P,``P,```````?,;&
+M8#`P`#`P``````!\QL9@,#``,#```````'S&QF`P,``P,```````?,;&8#`P
+M`#`P``````!\QL9@,#``,#```````'S&QF`P,``P,```````?,;&8#`P`#`P
+M``````!\QL9@,#``,#```````'S&QF`P,``P,```````?,;&8#`P`#`P````
+M``!\QL9@,#``,#```````'S&QF`P,``P,```````?,;&8#`P`#`P``````!\
+MQL9@,#``,#```````'S&QF`P,``P,```````?,;&8#`P`#`P``````!\QL9@
+M,#``,#```````'S&QF`P,``P,```````?,;&8#`P`#`P``````!\QL9@,#``
+M,#```````'S&QF`P,``P,```````?,;&8#`P`#`P``````!\QL9@,#``,#``
+M`````'S&QF`P,``P,```````?,;&8#`P`#`P``````!\QL9@,#``,#``````
+M`'S&QF`P,``P,```````?,;&8#`P`#`P``````!\QL9@,#``,#```````'S&
+MQF`P,``P,```````?,;&8#`P`#`P``````!\QL9@,#``,#```````'S&QF`P
+M,``P,```````?,;&8#`P`#`P``````!\QL9@,#``,#```````'S&QF`P,``P
+M,```````?,;&8#`P`#`P``````!\QL9@,#``,#```````'S&QF`P,``P,```
+M````?,;&8#`P`#`P``````!\QL9@,#``,#```````'S&QF`P,``P,```````
+M?,;&8#`P`#`P``````!\QL9@,#``,#```````'S&QF`P,``P,```````?,;&
+M8#`P`#`P``````!\QL9@,#``,#```````'S&QF`P,``P,```````?,;&8#`P
+M`#`P``````!\QL9@,#``,#```````'S&QF`P,``P,```````?,;&8#`P`#`P
+M``````!\QL9@,#``,#```````'S&QF`P,``P,```````?,;&8#`P`#`P````
+M``!\QL9@,#``,#```````'S&QF`P,``P,```````?,;&8#`P`#`P``````!\
+MQL9@,#``,#```````'S&QF`P,``P,```````?,;&8#`P`#`P``````!\QL9@
+M,#``,#```````'S&QF`P,``P,```````?,;&8#`P`#`P``````!\QL9@,#``
+M,#```````'S&QF`P,``P,```````?,;&8#`P`#`P``````!\QL9@,#``,#``
+M`````'S&QF`P,``P,```````?,;&8#`P`#`P``````!\QL9@,#``,#``````
+M`'S&QF`P,``P,```````?,;&8#`P`#`P``````!\QL9@,#``,#```````'S&
+MQF`P,``P,```````?,;&8#`P`#`P``````!\QL9@,#``,#```````'S&QF`P
+M,``P,```````?,;&8#`P`#`P``````!\QL9@,#``,#```````'S&QF`P,``P
+M,```````?,;&8#`P`#`P``````!\QL9@,#``,#```````'S&QF`P,``P,```
+=````?,;&8#`P`#`P``````!\QL9@,#``,#``````
+`
+end
diff --git a/usr.sbin/pcvt/fonts/vt220h.816.uu b/usr.sbin/pcvt/fonts/vt220h.816.uu
new file mode 100644
index 0000000..401db37
--- /dev/null
+++ b/usr.sbin/pcvt/fonts/vt220h.816.uu
@@ -0,0 +1,95 @@
+begin 644 vt220h.816
+M``!$9%1,1``@("`@/@``````/$`X!'@`(B(^(B(``````#Q`.`1X`"(4"!0B
+M``````!\0'A`?``B%`@4(@``````?$!X0'P`/@@("`@``````'Q`>$!\`!PB
+M(BH<`@`````X1'Q$1``D*#`H)```````>$1X1'@`("`@(#X``````'A$>$1X
+M`!X@'`(\``````!$1'Q$1``^"`@("```````0$!`0'P`/B`\("```````$1$
+M1"@0`#X("`@(``````!\0'A`0``^(#P@(```````/$!`0#P`/"(\)"(`````
+M`#Q`.`1X`!PB(B(<```````\0#@$>``<"`@('```````>$1$1'@`("`@(#X`
+M`````'A$1$1X``0,!`0.``````!X1$1$>``<(@P0/@``````>$1$1'@`'`(,
+M`AP``````'A$1$1X``0($CX"``````!$9%1,1``B)#@D(@``````/$`X!'@`
+M(A0("`@``````'Q`>$!\`#PB/"(\```````\0$!`/``B,BHF(@``````?$!X
+M0'P`(C8J(B(``````#Q`.`1X`"(B(B(<``````!\0'A`?``>("`@'@``````
+M?$!X0$``'B`<`CP``````#Q`3$0\`!X@'`(\``````!X1'A(1``>(!P"/```
+M````1$1$1#@`'B`<`CP`````<(APB'``#!(2$@P``````'"(<(AP``0,!`0.
+M``````!PB'"(<``<(@P0/@``````<(APB'``'`(,`AP``````'"(<(AP``0(
+M$CX"``````!PB'"(<``>$!P"'```````<(APB'``'"`\(AP``````'"(<(AP
+M`#X"!`@(``````!PB'"(<``<(APB'```````<(APB'``'"(>`AP``````'"(
+M<(AP``P2'A(2``````!PB'"(<``<$AP2'```````<(APB'``#A`0$`X`````
+M`'"(<(AP`!P2$A(<``````!PB'"(<``>$!P0'@``````<(APB'``'A`<$!``
+M`````'"(>`AP``P2$A(,``````!PB'@(<``$#`0$#@``````<(AX"'``'"(,
+M$#X``````'"(>`AP`!P"#`(<``````!PB'@(<``$"!(^`@``````<(AX"'``
+M'A`<`AP``````'"(>`AP`!P@/"(<``````!PB'@(<``^`@0("```````<(AX
+M"'``'"(<(AP``````'"(>`AP`!PB'@(<``````!PB'@(<``,$AX2$@``````
+M<(AX"'``'!(<$AP``````'"(>`AP``X0$!`.``````!PB'@(<``<$A(2'```
+M````<(AX"'``'A`<$!X``````'"(>`AP`!X0'!`0`````````````#YC8V-C
+M?F!@8&```````!C;V]O;VWX8&!@8`````'S&!@9^QL9\`````````,#`8#`8
+M'#9C00```````````#!P,#`P-!@`````````````W&9F9F9F!@8&!@``````
+M``!\Y'#D?````````````.:V'!@X;,8```````````""QD1L*#@0````````
+M````$#@H;$3&@@````````#&QL;&QL;&QGP``````````/P&!@8&_```````
+M``````!^P,#`P'X``````````````,9F/!@\9CP```````#^Q@!L?'QL`,;^
+M```````\&-O;V]O;?A@8/````````/YL;&QL;&QL;.X```````````@,_@/^
+M#`@````````````D9G[#?F8D````````````$!`X*&Q$1.X````````X1(*J
+MNKJJ@D0X````````````W'8`_@``````````````_H+$Q&AH,#``````````
+M`!@8+"Q$1H;^````````````=HB(=@``````````````&!@``&9F````````
+M```,'A@8&!@8&'@P```````````!`@0($"!`@```````````@$`@$`@$`@$`
+M```````````!`@0($"!_```````````````_$`@$`@$````8&!@8&!@P8#`8
+M&!@8&!@8&!@8&!@8#`8,&!@8&!@8&```#A@8&!@8&!@8&!@8&!@8&!@8&!@8
+M&!@8&`X```````!P&!@8&!@8&!@8&!@8&!@8&!@8&!@8&!@8<``````````"
+M`@("(A(*!@(``````````````&S&UM9L````````9F9F/!@8?A@8&```````
+M<#YP8#Q@8#`<!CP`````````#!@P`,;&QL9^!@QX````P,#XS,S,^,#`P```
+M`````+!@X+`8##YF9CP```````#P8'QF9GQ@8&#P``````P8`,S,S'@P,#`P
+M>```````````1"@0*$0```````````#\9F9F]F9F9F;\`````.`0<!#F#!@P
+M9LJ2/@(``````````````````!@,>```&#!@````````````````````````
+M``#^`````````````'R"NJJJLJJJ@GP``````````````'@`````````````
+M`````'@("`````````#&Q@```````````````````````@9^"!!^8$``````
+M````````````````_P````````````````#_`````````````````/\`````
+M````````````_P````````````````#_````````````````````````YF9B
+M8C08```````````&?,[>]N9\P`````````````!\UM;<T'X`````````=MP`
+M?,;&QL9\`````````';<`'@,?,S,=@````#,S`#,S,QX,#`P,'@`````.&S&
+M`,;&QL;&QL9\``````8,&`#&QL;&QL;&?`````!@,!@`QL;&QL;&QGP`````
+M```&/FS.WO;F;/C`````````?MC8V-[>V-C8?@````!VW``X;,;&QL;&;#@`
+M````.&S&.&S&QL;&QFPX``````P8`#ALQL;&QL9L.`````!@,``X;,;&QL;&
+M;#@`````9F8`/!@8&!@8&!@\`````!@\9@`\&!@8&!@8/``````&#!@`/!@8
+M&!@8&#P`````8#`8`#P8&!@8&!@\`````,;&`/YB8&!\8&!B_@`````X;,8`
+M_F)@?&!@8OX`````8#`8`/YB8'Q@8&+^`````';<`!`X;,;&_L;&Q@`````X
+M;,80.&S&QO[&QL8`````&#!@$#ALQL;^QL;&`````#`8#!`X;,;&_L;&Q@``
+M````"!@("!P`````````````````````&!@`````````````X#Q@P,#`8#@,
+M>```````.`08!#@```````````````!\@IJBHJ*BFH)\``````````##VSQF
+M9CS;PP``````````````YFYX;&8```````!\QL9@,#`P`#`P````````````
+M`````````````````'S&QF`P,#``,#````````!\QL9@,#`P`#`P````````
+M?,;&8#`P,``P,````````'S&QF`P,#``,#````````!\QL9@,#`P`#`P````
+M````?,;&8#`P,``P,````````'S&QF`P,#``,#````````!\QL9@,#`P`#`P
+M````````?,;&8#`P,``P,````````'S&QF`P,#``,#````````!\QL9@,#`P
+M`#`P````````?,;&8#`P,``P,````````'S&QF`P,#``,#````````!\QL9@
+M,#`P`#`P````````?,;&8#`P,``P,````````'S&QF`P,#``,#````````!\
+MQL9@,#`P`#`P````````?,;&8#`P,``P,````````'S&QF`P,#``,#``````
+M``!\QL9@,#`P`#`P````````?,;&8#`P,``P,````````'S&QF`P,#``,#``
+M``````!\QL9@,#`P`#`P````````?,;&8#`P,``P,````````'S&QF`P,#``
+M,#````````!\QL9@,#`P`#`P````````?,;&8#`P,``P,````````'S&QF`P
+M,#``,#````````!\QL9@,#`P`#`P````````?,;&8#`P,``P,````````'S&
+MQF`P,#``,#````````!\QL9@,#`P`#`P````````?,;&8#`P,``P,```````
+M`'S&QF`P,#``,#````````!\QL9@,#`P`#`P````````?,;&8#`P,``P,```
+M`````'S&QF`P,#``,#````````!\QL9@,#`P`#`P````````?,;&8#`P,``P
+M,````````'S&QF`P,#``,#````````!\QL9@,#`P`#`P````````?,;&8#`P
+M,``P,````````'S&QF`P,#``,#````````!\QL9@,#`P`#`P````````?,;&
+M8#`P,``P,````````'S&QF`P,#``,#````````!\QL9@,#`P`#`P````````
+M?,;&8#`P,``P,````````'S&QF`P,#``,#````````!\QL9@,#`P`#`P````
+M````?,;&8#`P,``P,````````'S&QF`P,#``,#````````!\QL9@,#`P`#`P
+M````````?,;&8#`P,``P,````````'S&QF`P,#``,#````````!\QL9@,#`P
+M`#`P````````?,;&8#`P,``P,````````'S&QF`P,#``,#````````!\QL9@
+M,#`P`#`P````````?,;&8#`P,``P,````````'S&QF`P,#``,#````````!\
+MQL9@,#`P`#`P````````?,;&8#`P,``P,````````'S&QF`P,#``,#``````
+M``!\QL9@,#`P`#`P````````?,;&8#`P,``P,````````'S&QF`P,#``,#``
+M``````!\QL9@,#`P`#`P````````?,;&8#`P,``P,````````'S&QF`P,#``
+M,#````````!\QL9@,#`P`#`P````````?,;&8#`P,``P,````````'S&QF`P
+M,#``,#````````!\QL9@,#`P`#`P````````?,;&8#`P,``P,````````'S&
+MQF`P,#``,#````````!\QL9@,#`P`#`P````````?,;&8#`P,``P,```````
+M`'S&QF`P,#``,#````````!\QL9@,#`P`#`P````````?,;&8#`P,``P,```
+M`````'S&QF`P,#``,#````````!\QL9@,#`P`#`P````````?,;&8#`P,``P
+M,````````'S&QF`P,#``,#````````!\QL9@,#`P`#`P````````?,;&8#`P
+M,``P,````````'S&QF`P,#``,#````````!\QL9@,#`P`#`P````````?,;&
+M8#`P,``P,````````'S&QF`P,#``,#````````!\QL9@,#`P`#`P````````
+M?,;&8#`P,``P,````````'S&QF`P,#``,#``````````````````````````
+!`,;&
+`
+end
diff --git a/usr.sbin/pcvt/fonts/vt220l.808.uu b/usr.sbin/pcvt/fonts/vt220l.808.uu
new file mode 100644
index 0000000..02ad944
--- /dev/null
+++ b/usr.sbin/pcvt/fonts/vt220l.808.uu
@@ -0,0 +1,49 @@
+begin 644 vt220l.808
+M``````````!^@:6!O9F!?G[_V__#Y_]^;/[^_GPX$``0.'S^?#@0`#A\./[^
+MUA`X$#A\_OY\$#@``!@\/!@``/__Y\/#Y___/&;#P\/#9CS_PYF]O9G#_Q\'
+M#7G-S,QX/&9F9CP8?A@X/#8R-'#PX']C?V-C9^;`&-L\Y^<\VQB`X/C^^."`
+M``(./OX^#@(`&#Q^&!A^/!AF9F9F9@!F`'_;VWL;&QL`?L/\9F8_PWX``'Y^
+M?@```!@\?AA^/!C_&#Q^&!@8&``8&!@8?CP8```8#/X,&````#!@_F`P````
+M`&!@8'X````D9O]F)````!`X?/[^````_OY\.!``````````````&#P\&!@`
+M&`!L;&P``````&QL_FS^;&P`,'S`?`;\,```QLP8,&;&`#AL.';<SGL`8&#`
+M```````8,&!@8#`8`#`8#`P,&#```&PX_CAL````,##\,#``````````,#!@
+M````_````````````!@8``8,&#!@P(``?,[>]N;&?``P<+`P,##\`'C,##A@
+MP/P`>,P,.`S,>``</&S,_@P>`/S`^`P,S'@`>,S`^,S,>`#\S`P8,#`P`'C,
+MS'C,S'@`>,S,?`S,>```,#```#`P```P,```,#!@&#!@P&`P&````'X``'X`
+M`#`8#`8,&#``/&8&#!@`&`!\QM[>WL!^`#!XS,S\S,P`_&9F?&9F_``\9L#`
+MP&8\`/AL9F9F;/@`_F)H>&AB_@#^8FAX:&#P`#QFP,#.9CX`S,S,_,S,S`!X
+M,#`P,#!X`!X,#`S,S'@`YFQX<'ALY@#P8&!@8F;^`,;N_M;&QL8`QN;VWL[&
+MQ@`X;,;&QFPX`/QF9GQ@8/``>,S,S-QX'`#\9F9\;&;F`'C,P'@,S'@`_+0P
+M,#`P>`#,S,S,S,QX`,S,S,S,>#``QL;&UO[^Q@#&QFPX.&S&`,S,S'@P,'@`
+M_,R8,&3,_`!X8&!@8&!X`,!@,!@,!@(`>!@8&!@8>``0.&S&````````````
+M``#_,#`8`````````'@,?,QV`.!@8'QF9OP```!XS,#,>``<#`Q\S,Q^````
+M>,S\P'@`.&Q@^&!@\````';,S'P,^.!@;'9F9N8`,`!P,#`P>``,`!P,#,S,
+M>.!@9FQX;.8`<#`P,#`P>````,S^_M;&````^,S,S,P```!XS,S,>````-QF
+M9GQ@\```=LS,?`P>``#<=F!@\````'S`?`;\`!`P?#`P-AP```#,S,S,=@``
+M`,;&QGPX````QM;^_FP```#&;#ALQ@```,S,S'P,^```_)@P9/P`'#`PX#`P
+M'``8&!@`&!@8`.`P,!PP,.``=MP````````0.&QLQL;^`'C,P,QX&`QX`,P`
+MS,S,?@`<`'C,_,!X`'[#/`8^9C\`S`!X#'S,?@#@`'@,?,Q^`#`P>`Q\S'X`
+M``!XP,!X##A^PSQF?F`\`,P`>,S\P'@`X`!XS/S`>`#,`'`P,#!X`'S&.!@8
+M&#P`X`!P,#`P>`#&.&S&_L;&`#`P`'C,_,P`'`#\8'A@_````'\,?\Q_`#YL
+MS/[,S,X`>,P`>,S,>```S`!XS,QX``#@`'C,S'@`>,P`S,S,?@``X`#,S,Q^
+M``#,`,S,?`SXPQ@\9F8\&`#,`,S,S,QX`!@8?L#`?A@8.&QD\&#F_`#,S'C\
+M,/PP,/C,S/K&S\;'#AL8/!@8V'`<`'@,?,Q^`#@`<#`P,'@``!P`>,S,>```
+M'`#,S,Q^``#X`/C,S,P`_`#,[/S<S``\;&P^`'X``#AL;#@`?```,``P8,#,
+M>`````#\P,```````/P,#```P\;,WC-FS`_#QLS;-V_/`Q@8`!@8&!@``#-F
+MS&8S````S&8S9LP``"*((H@BB"*(5:I5JE6J5:K;=]ONVW?;[A@8&!@8&!@8
+M&!@8&/@8&!@8&/@8^!@8&#8V-C;V-C8V`````/XV-C8``/@8^!@8&#8V]@;V
+M-C8V-C8V-C8V-C8``/X&]C8V-C8V]@;^````-C8V-OX````8&/@8^```````
+M``#X&!@8&!@8&!\````8&!@8_P````````#_&!@8&!@8&!\8&!@`````_P``
+M`!@8&!C_&!@8&!@?&!\8&!@V-C8V-S8V-C8V-S`_```````_,#<V-C8V-O<`
+M_P``````_P#W-C8V-C8W,#<V-C8``/\`_P```#8V]P#W-C8V&!C_`/\````V
+M-C8V_P``````_P#_&!@8`````/\V-C8V-C8V/P```!@8'Q@?```````?&!\8
+M&!@`````/S8V-C8V-C;_-C8V&!C_&/\8&!@8&!@8^``````````?&!@8____
+M______\`````______#P\/#P\/#P#P\/#P\/#P______````````=MS(W'8`
+M`'C,^,SXP,``_,S`P,#```#^;&QL;&P`_,Q@,&#,_````'[8V-AP``!F9F9F
+M?&#``';<&!@8&`#\,'C,S'@P_#ALQO[&;#@`.&S&QFQL[@`<,!A\S,QX````
+M?MO;?@``!@Q^V]M^8,`\8,#\P&`\`'C,S,S,S,P``/P`_`#\```P,/PP,`#\
+M`&`P&#!@`/P`&#!@,!@`_``.&QL8&!@8&!@8&!@8V-AP,#``_``P,```=MP`
+M=MP``#AL;#@`````````&!@`````````&`````\,#`SL;#P<>&QL;&P```!P
+7&#!@>```````/#P\/```````````````
+`
+end
diff --git a/usr.sbin/pcvt/fonts/vt220l.810.uu b/usr.sbin/pcvt/fonts/vt220l.810.uu
new file mode 100644
index 0000000..7cba2cb
--- /dev/null
+++ b/usr.sbin/pcvt/fonts/vt220l.810.uu
@@ -0,0 +1,60 @@
+begin 644 vt220l.810
+M`````````````'Z!I:6!@;V9@7Y^_]O;___#Y_]^;/[^?'PX.!`0````$#A\
+M_GPX$``X?'PX?/[^?#A\$!`X.'S^_GPX?````!@\/!@```#____GP\/G____
+M```\9D)"9CP``/__PYF]O9G#__\`#P</?<S,S'@``#QF9F8\&'X8`#\S/S`P
+M,'#PX`!_8W]C8V-GYL```-M^/.?G/'[;````@.#X_OC@@`````(./OX^#@(`
+M&!@\?A@8?CP8&&9F9F9F9@!F9@!_V]O;>QL;&QL`/&8P;,9L&,QX````````
+M?GY^?@`8&#Q^&!A^/!C_&!@\/'X8&!@8&!@8&!@8?CP\&!@`````&`S^#!@`
+M`````#!@_F`P``````#`P,#`_@``````)&;_9B0``!@8/#Q^?O__``#__WY^
+M/#P8&```````````````,'AX>#`P`#`P`&QL;"@```````!L;/YL;&S^;&P`
+M&'[8V'PV-OPP`,;,#!@X,&!FQ@`X;&PX,';<S'8`X.!@8,````````P8,#`P
+M,#`8#`!@,!@8&!@8,&````#&;#C^.&S&`````#`P_#`P````````<'`P,&``
+M``````#^```````````````P,```#`P8&#`P8&``?,;.WM;VYL9\`#!P,#`P
+M,#`P_`!\QL8&##A@QOX`?,;&!CP&QL9\``P,'#QLS/X,'@#^P,#\!@8&QGP`
+M'#!@P/S&QL9\`/[&!@P,&!@8&`!\QL;&?,;&QGP`?,;&QGX&#!AP`````#`P
+M```P,````#`P```P,&`````8,&#`8#`8``````#\``#\`````&`P&`P8,&``
+M?,;&!@P8&``8`'S&SMK6WL#&?``X?,;&QO[&QL8`_&9F9GQF9F;\`#QFP,#`
+MP,!F/`#X;&9F9F9F;/@`_F)@:'AH8&+^`/YB8&AX:&!@\``\9L#`P,[&9CP`
+MQL;&QO[&QL;&`#P8&!@8&!@8/``>#`P,#`S,S'@`YF9L;'AL;&;F`/!@8&!@
+M8&)F_@#&[O[^UL;&QL8`QN;F]M[.SL;&`#ALQL;&QL9L.`#\9F9F?&!@8/``
+M?,;&QL;&SGP.`/QF9F9X;&QFY@!\QL!@.`P&QGP`?EH8&!@8&!@\`,;&QL;&
+MQL;&?`!F9F9F9F9F/!@`QL;&QL;6_N[&`,;&;#@0.&S&Q@#,S,S,>#`P,'@`
+M_L:,&#!@PL;^`!X8&!@8&!@8'@``8&`P,!@8#`P`\#`P,#`P,##P`!`X;,8`
+M``````````````````#_,#`8`````````````'@,?,S,=@#@8&!\9F9F9MP`
+M````?,;`P,9\`!P,#'S,S,S,=@````!\QO[`P'P`.&Q@\&!@8&#P`````';,
+MS'P,S'C@8&!L=F9F9N8`,#``<#`P,#!X``P,``P,#`S,S'C@8&!F;'AX;.8`
+M<#`P,#`P,#!X`````,S^_M;6U@````#<YL;&QL8`````?,;&QL9\`````-QF
+M9F9\8/````!VS,S,?`P>````W'9F8&#P`````'S&<!S&?``@8&#\8&!@;#@`
+M````S,S,S,QV`````,;&QL9L.`````#&UM;^_FP`````QFPX.&S&`````,;&
+MQGX&!OP```#^C!@P8OX`#A@8&'`8&!@.`!@8&!@`&!@8&`#@,#`P'#`P,.``
+M=MP``````````````!`X;,;&_@!\QL#`P,9\#`8\`,P`S,S,S,QV```.`'S&
+M_L#`?``\9@`\!CYF9C\``,P`>`Q\S,QV``!P`'@,?,S,=@`P,`!X#'S,S'8`
+M````?L#`P'X&/#QF`#QF?F!@/`#,``!\QO[`P'P``'``?,;^P,!\``#,`'`P
+M,#`P>``X;``X&!@8&#P``.``<#`P,#!X`,8`.&S&_L;&Q@`P,`!XS/S,S,P`
+M'`#\8&!X8&#\`````'X;?MC8?P`^;,S,_LS,S,X`.&P`?,;&QL9\``#&`'S&
+MQL;&?```<`!\QL;&QGP`>,P`S,S,S,QV``!P`,S,S,S,=@``Q@#&QL9^!@;\
+MQCALQL;&QFPX`,8`QL;&QL;&?``8&'[`P,#`?A@8.&QD8/A@8.;\`,S,>##\
+M,/PP,`#XS,S,^L;/QL<`#AL8&#P8&!C8<``<`'@,?,S,=@``.`!P,#`P,'@`
+M`!P`?,;&QL9\```<`,S,S,S,=@!P_AP`W.;&QL8`_@#&YO;^WL[&`#QL;&P^
+M`'X````X;&QL.`!\````,``P,&#`QL9\``````#^P,``````````_@8&````
+MP\;,V#Y[PP8,#\/&S-DS9\T/`P,8&``8&#P\/!@`````,V;,9C,``````,QF
+M,V;,```BB"*((H@BB"*(5:I5JE6J5:I5JMMWV^[;=]ONVW<8&!@8&!@8&!@8
+M&!@8&!CX&!@8&!@8&/@8^!@8&!@V-C8V-O8V-C8V``````#^-C8V-@```/@8
+M^!@8&!@V-C;V!O8V-C8V-C8V-C8V-C8V-@```/X&]C8V-C8V-C;V!OX`````
+M-C8V-C;^`````!@8&/@8^````````````/@8&!@8&!@8&!@?`````!@8&!@8
+M_P```````````/\8&!@8&!@8&!@?&!@8&```````_P`````8&!@8&/\8&!@8
+M&!@8'Q@?&!@8&#8V-C8V-S8V-C8V-C8W,#\`````````/S`W-C8V-C8V-O<`
+M_P````````#_`/<V-C8V-C8V-S`W-C8V-@```/\`_P`````V-C;W`/<V-C8V
+M&!@8_P#_`````#8V-C8V_P````````#_`/\8&!@8``````#_-C8V-C8V-C8V
+M/P`````8&!@?&!\`````````'Q@?&!@8&```````/S8V-C8V-C8V-O\V-C8V
+M&!@8_QC_&!@8&!@8&!@8^````````````!\8&!@8_____________P``````
+M_______P\/#P\/#P\/#P#P\/#P\/#P\/#_______``````````!VW-C,W'8`
+M`'S&QMS&QMS`P/YF9&!@8&!@\`#^;&QL;&QL;.X`_L9@,!@P8,;^`````'_,
+MS,S,>`````!F9F9V;&#`````=MP8&!@8`#P8?MO;VWX8/``X;,;&_L;&;#@`
+M.&S&QL9L;&SN`!PR,!A\S,S,>`````!VV]O;;@```P9\S]O;\SY@P```'#!@
+M?&`P'````'S&QL;&QL8`````_@#^`/X`````,##\,#``_`!@,!@,&#!@`/X`
+M#!@P8#`8#`#^``X;&Q@8&!@8&!@8&!@8&!@8V-AP```P,`#\`#`P`````';<
+M`';<```X;&QL.````````````!@8````````````&``````/#`P,[&QL-#P<
+H>&QL;&QL`````'#8&'#`^``````````\/#P\`````````````````&QL
+`
+end
diff --git a/usr.sbin/pcvt/fonts/vt220l.814.uu b/usr.sbin/pcvt/fonts/vt220l.814.uu
new file mode 100644
index 0000000..7d918bb
--- /dev/null
+++ b/usr.sbin/pcvt/fonts/vt220l.814.uu
@@ -0,0 +1,83 @@
+begin 644 vt220l.814
+M`````````````````````'Z!I8&!O9F!?@``````?O_;___#Y_]^````````
+M;/[^_OY\.!`````````0.'S^?#@0````````&#P\Y^?G&!@\```````8/'[_
+M_WX8&#P``````````!@\/!@``````/______Y\/#Y_______`````#QF0D)F
+M/`````#_____PYF]O9G#_____P``'@X:,GC,S,QX```````\9F9F/!A^&!@`
+M`````#\S/S`P,'#PX```````?V-_8V-C9^?FP``````8&-L\YSS;&!@`````
+M`(#`X/C^^.#`@````````@8./OX^#@8"```````8/'X8&!A^/!@``````&9F
+M9F9F9@!F9@``````?]O;VWL;&QL;`````'S&8#ALQL9L.`S&?```````````
+M`/[^_@``````&#Q^&!@8?CP8?@`````8/'X8&!@8&!@``````!@8&!@8&'X\
+M&``````````8#/X,&````````````#!@_F`P`````````````,#`P/X`````
+M```````H;/YL*```````````$#@X?'S^_@````````#^_GQ\.#@0````````
+M```````````````````8/#P\&!@`&!@`````9F9F)```````````````;&S^
+M;&QL_FQL````&!A\QL+`?`:&QGP8&```````PL8,&#!FQ@``````.&QL.';<
+MS,QV`````#`P,&````````````````P8,#`P,#`8#```````,!@,#`P,#!@P
+M`````````&8\_SQF````````````&!A^&!@`````````````````&!@8,```
+M````````_@`````````````````````8&````````@8,&#!@P(````````!\
+MQL[>]N;&QGP``````!@X>!@8&!@8?@``````?,8&#!@P8,;^``````!\Q@8&
+M/`8&QGP```````P</&S,_@P,'@``````_L#`P/P&!L9\```````X8,#`_,;&
+MQGP``````/[&!@P8,#`P,```````?,;&QGS&QL9\``````!\QL;&?@8&#'@`
+M```````8&````!@8`````````!@8````&!@P```````&#!@P8#`8#`8`````
+M`````'X``'X`````````8#`8#`8,&#!@``````!\QL8,&!@`&!@``````'S&
+MQM[>WMS`?```````$#ALQL;^QL;&``````#\9F9F?&9F9OP``````#QFPL#`
+MP,)F/```````^&QF9F9F9FSX``````#^9F)H>&AB9OX``````/YF8FAX:&!@
+M\```````/&;"P,#>QF8Z``````#&QL;&_L;&QL8``````#P8&!@8&!@8/```
+M````'@P,#`P,S,QX``````#F9FQL>&QL9N8``````/!@8&!@8&)F_@``````
+MQN[^_M;&QL;&``````#&YO;^WL[&QL8``````#ALQL;&QL9L.```````_&9F
+M9GQ@8&#P``````!\QL;&QM;>?`P.`````/QF9F9\;&9FY@``````?,;&8#@,
+MQL9\``````!^?EH8&!@8&#P``````,;&QL;&QL;&?```````QL;&QL;&;#@0
+M``````#&QL;&UM;^?&P``````,;&;#@X.&S&Q@``````9F9F9CP8&!@\````
+M``#^QHP8,&#"QOX``````#PP,#`P,#`P/```````@,#@<#@<#@8"```````\
+M#`P,#`P,##P````0.&S&`````````````````````````````/\`,#`8````
+M`````````````````'@,?,S,=@``````X&!@>&QF9F9\``````````!\QL#`
+MQGP``````!P,##QLS,S,=@``````````?,;^P,9\```````X;&1@\&!@8/``
+M`````````';,S,Q\#,QX````X&!@;'9F9F;F```````8&``X&!@8&#P`````
+M``8&``X&!@8&9F8\````X&!@9FQX;&;F```````X&!@8&!@8&#P`````````
+M`.S^UM;6Q@``````````W&9F9F9F``````````!\QL;&QGP``````````-QF
+M9F9\8&#P````````=LS,S'P,#!X```````#<=F9@8/```````````'S&<!S&
+M?```````$#`P_#`P,#8<``````````#,S,S,S'8``````````,;&QFPX$```
+M````````QL;6UOYL``````````#&;#@X;,8``````````,;&QL9^!@SX````
+M````_LP8,&;^```````.&!@8<!@8&`X``````!@8&!@`&!@8&```````<!@8
+M&`X8&!AP``````!VW```````````````````$#ALQL;^````````/&;"P,#"
+M9CP,!GP```#,S`#,S,S,S'8`````#!@P`'S&_L#&?``````0.&P`>`Q\S,QV
+M``````#,S`!X#'S,S'8`````8#`8`'@,?,S,=@`````X;#@`>`Q\S,QV````
+M`````#QF8&8\#`8\````$#AL`'S&_L#&?```````S,P`?,;^P,9\`````&`P
+M&`!\QO[`QGP``````&9F`#@8&!@8/``````8/&8`.!@8&!@\`````&`P&``X
+M&!@8&#P`````QL80.&S&QO[&Q@```#AL.``X;,;&_L;&````&#!@`/YF8'Q@
+M9OX`````````S'8V?MC8;@``````/FS,S/[,S,S.`````!`X;`!\QL;&QGP`
+M`````,;&`'S&QL;&?`````!@,!@`?,;&QL9\`````#!XS`#,S,S,S'8`````
+M8#`8`,S,S,S,=@``````QL8`QL;&QGX&#'@`QL8`.&S&QL;&;#@```#&Q@#&
+MQL;&QL;&?``````8&#QF8&!F/!@8`````#AL9&#P8&!@YOP``````&9F/!A^
+M&'X8&`````#XS,SXQ,S>S,S&``````X;&!@8?A@8&!C8<```&#!@`'@,?,S,
+M=@`````,&#``.!@8&!@\`````!@P8`!\QL;&QGP`````&#!@`,S,S,S,=@``
+M````=MP`W&9F9F9F````=MP`QN;V_M[.QL8`````/&QL/@!^```````````X
+M;&PX`'P````````````P,``P,&#&QGP```````````#^P,#`````````````
+M`/X&!@8``````,#`QLS8,&#<A@P8/@``P,#&S-@P9LZ>/@8&````&!@`&!@\
+M/#P8`````````#9LV&PV````````````V&PV;-@``````!%$$4011!%$$401
+M1!%$5:I5JE6J5:I5JE6J5:K==]UWW7?==]UWW7?==Q@8&!@8&!@8&!@8&!@8
+M&!@8&!@8&/@8&!@8&!@8&!@8&/@8^!@8&!@8&#8V-C8V-C;V-C8V-C8V````
+M`````/XV-C8V-C8``````/@8^!@8&!@8&#8V-C8V]@;V-C8V-C8V-C8V-C8V
+M-C8V-C8V-C8``````/X&]C8V-C8V-C8V-C8V]@;^````````-C8V-C8V-OX`
+M```````8&!@8&/@8^`````````````````#X&!@8&!@8&!@8&!@8&!\`````
+M```8&!@8&!@8_P````````````````#_&!@8&!@8&!@8&!@8&!\8&!@8&!@`
+M````````_P```````!@8&!@8&!C_&!@8&!@8&!@8&!@?&!\8&!@8&!@V-C8V
+M-C8V-S8V-C8V-C8V-C8V-S`_```````````````_,#<V-C8V-C8V-C8V-O<`
+M_P``````````````_P#W-C8V-C8V-C8V-C8W,#<V-C8V-C8``````/\`_P``
+M`````#8V-C8V]P#W-C8V-C8V&!@8&!C_`/\````````V-C8V-C8V_P``````
+M````````_P#_&!@8&!@8`````````/\V-C8V-C8V-C8V-C8V/P```````!@8
+M&!@8'Q@?```````````````?&!\8&!@8&!@`````````/S8V-C8V-C8V-C8V
+M-C;_-C8V-C8V&!@8&!C_&/\8&!@8&!@8&!@8&!@8^``````````````````?
+M&!@8&!@8__________________\`````````__________#P\/#P\/#P\/#P
+M\/#P#P\/#P\/#P\/#P\/#P__________````````````````=MS8V-QV````
+M`````'S&_,;&_,#`0````/[&QL#`P,#`P`````````#^;&QL;&QL``````#^
+MQF`P&#!@QOX``````````'[8V-C8<`````````!F9F9F?&!@P````````';<
+M&!@8&!@``````'X8/&9F9CP8?@``````.&S&QO[&QFPX```````X;,;&QFQL
+M;.X``````!XP&`P^9F9F/```````````?MO;?@`````````#!G[;V_-^8,``
+M`````!XP8&!^8&`P'@```````'S&QL;&QL;&````````_@``_@``_@``````
+M```8&'X8&```_P``````,!@,!@P8,`!^```````,&#!@,!@,`'X```````X;
+M&Q@8&!@8&!@8&!@8&!@8&!@8V-AP````````&!@`?@`8&```````````=MP`
+M=MP````````X;&PX````````````````````&!@`````````````````&```
+M```````/#`P,#`SL;#P<`````-AL;&QL;```````````<-@P8,CX````````
+=``````!\?'Q\?'P`````````````````````````
+`
+end
diff --git a/usr.sbin/pcvt/fonts/vt220l.816.uu b/usr.sbin/pcvt/fonts/vt220l.816.uu
new file mode 100644
index 0000000..62393e1
--- /dev/null
+++ b/usr.sbin/pcvt/fonts/vt220l.816.uu
@@ -0,0 +1,95 @@
+begin 644 vt220l.816
+M````````````````````````?H&E@8&]F8&!?@```````'[_V___P^?__WX`
+M`````````&S^_O[^?#@0```````````0.'S^?#@0```````````8/#SGY^<8
+M&#P`````````&#Q^__]^&!@\`````````````!@\/!@```````#________G
+MP\/G________```````\9D)"9CP``````/______PYF]O9G#______\``!X.
+M&C)XS,S,S'@````````\9F9F9CP8?A@8````````/S,_,#`P,'#PX```````
+M`']C?V-C8V-GY^;`````````&!C;/.<\VQ@8``````"`P.#P^/[X\.#`@```
+M`````@8.'C[^/AX.!@(````````8/'X8&!A^/!@`````````9F9F9F9F9@!F
+M9@```````'_;V]M[&QL;&QL``````'S&8#ALQL9L.`S&?```````````````
+M_O[^_@```````!@\?A@8&'X\&'X````````8/'X8&!@8&!@8````````&!@8
+M&!@8&'X\&````````````!@,_@P8```````````````P8/Y@,```````````
+M`````,#`P/X``````````````"AL_FPH`````````````!`X.'Q\_OX`````
+M``````#^_GQ\.#@0```````````````````````````````8/#P\&!@8`!@8
+M``````!F9F8D``````````````````!L;/YL;&S^;&P``````!@8?,;"P'P&
+MAL9\&!@```````#"Q@P8,&#&A@```````#AL;#AVW,S,S'8``````#`P,&``
+M````````````````#!@P,#`P,#`8#````````#`8#`P,#`P,&#``````````
+M``!F//\\9@``````````````&!A^&!@````````````````````8&!@P````
+M`````````'X````````````````````````8&````````````@8,&#!@P(``
+M``````!\QL;.WO;FQL9\````````&#AX&!@8&!@8?@```````'S&!@P8,&#`
+MQOX```````!\Q@8&/`8&!L9\````````#!P\;,S^#`P,'@```````/[`P,#\
+M!@8&QGP````````X8,#`_,;&QL9\````````_L8&!@P8,#`P,````````'S&
+MQL9\QL;&QGP```````!\QL;&?@8&!@QX```````````8&````!@8````````
+M````&!@````8&#``````````!@P8,&`P&`P&````````````?@``?@``````
+M``````!@,!@,!@P8,&````````!\QL8,&!@8`!@8````````?,;&QM[>WMS`
+M?````````!`X;,;&_L;&QL8```````#\9F9F?&9F9F;\````````/&;"P,#`
+MP,)F/````````/AL9F9F9F9F;/@```````#^9F)H>&A@8F;^````````_F9B
+M:'AH8&!@\````````#QFPL#`WL;&9CH```````#&QL;&_L;&QL;&````````
+M/!@8&!@8&!@8/````````!X,#`P,#,S,S'@```````#F9F9L>'AL9F;F````
+M````\&!@8&!@8&)F_@```````,;N_O[6QL;&QL8```````#&YO;^WL[&QL;&
+M````````?,;&QL;&QL;&?````````/QF9F9\8&!@8/````````!\QL;&QL;&
+MUMY\#`X`````_&9F9GQL9F9FY@```````'S&QF`X#`;&QGP```````!^?EH8
+M&!@8&!@\````````QL;&QL;&QL;&?````````,;&QL;&QL9L.!````````#&
+MQL;&UM;6_NYL````````QL9L?#@X?&S&Q@```````&9F9F8\&!@8&#P`````
+M``#^QH8,&#!@PL;^````````/#`P,#`P,#`P/`````````"`P.!P.!P.!@(`
+M```````\#`P,#`P,#`P\`````!`X;,8`````````````````````````````
+M````_P``,#`8````````````````````````>`Q\S,S,=@```````.!@8'AL
+M9F9F9GP```````````!\QL#`P,9\````````'`P,/&S,S,S,=@``````````
+M`'S&_L#`QGP````````X;&1@\&!@8&#P````````````=LS,S,S,?`S,>```
+M`.!@8&QV9F9F9N8````````8&``X&!@8&!@\````````!@8`#@8&!@8&!F9F
+M/````.!@8&9L>'AL9N8````````X&!@8&!@8&!@\````````````[/[6UM;6
+MQ@```````````-QF9F9F9F8```````````!\QL;&QL9\````````````W&9F
+M9F9F?&!@\````````';,S,S,S'P,#!X```````#<=F9@8&#P````````````
+M?,9@.`S&?````````!`P,/PP,#`P-AP```````````#,S,S,S,QV````````
+M````QL;&QFPX$````````````,;&UM;6_FP```````````#&;#@X.&S&````
+M````````QL;&QL;&?@8,^````````/[,&#!@QOX````````.&!@8<!@8&!@.
+M````````&!@8&``8&!@8&````````'`8&!@.&!@8&'````````!VW```````
+M```````````````0.&S&QL;^`````````#QFPL#`P,)F/`P&?`````#,``#,
+MS,S,S,QV```````,&#``?,;^P,#&?```````$#AL`'@,?,S,S'8```````#,
+M``!X#'S,S,QV``````!@,!@`>`Q\S,S,=@``````.&PX`'@,?,S,S'8`````
+M`````#QF8&!F/`P&/``````0.&P`?,;^P,#&?````````,8``'S&_L#`QGP`
+M`````&`P&`!\QO[`P,9\````````9@``.!@8&!@8/```````&#QF`#@8&!@8
+M&#P``````&`P&``X&!@8&!@\``````#&`!`X;,;&_L;&Q@`````X;#@`.&S&
+MQO[&QL8`````&#!@`/YF8'Q@8&;^``````````!L_K(R?MC8;@```````#YL
+MS,S^S,S,S,X``````!`X;`!\QL;&QL9\````````Q@``?,;&QL;&?```````
+M8#`8`'S&QL;&QGP``````#!XS`#,S,S,S,QV``````!@,!@`S,S,S,S,=@``
+M````QL8``,;&QL;&QGX&#'@`QL8`?,;&QL;&QL9\`````,;&`,;&QL;&QL;&
+M?```````&!@\9F!@8&8\&!@``````#AL9&#P8&!@8.;\````````9F8\&'X8
+M?A@8&```````^,S,^,3,WLS,S,8```````X;&!@8?A@8&!@8V'`````8,&``
+M>`Q\S,S,=@``````#!@P`#@8&!@8&#P``````!@P8`!\QL;&QL9\```````8
+M,&``S,S,S,S,=@```````';<`-QF9F9F9F8`````=MP`QN;V_M[.QL;&````
+M```\;&P^`'X`````````````.&QL.`!\```````````````P,``P,&#`QL9\
+M`````````````/[`P,#```````````````#^!@8&!@```````,#`PL;,&#!@
+MW(8,&#X```#`P,+&S!@P9LZ>/@8&`````!@8`!@8&#P\/!@````````````V
+M;-AL-@``````````````V&PV;-@````````11!%$$4011!%$$4011!%$5:I5
+MJE6J5:I5JE6J5:I5JMUWW7?==]UWW7?==]UWW7<8&!@8&!@8&!@8&!@8&!@8
+M&!@8&!@8&/@8&!@8&!@8&!@8&!@8^!CX&!@8&!@8&!@V-C8V-C8V]C8V-C8V
+M-C8V`````````/XV-C8V-C8V-@``````^!CX&!@8&!@8&!@V-C8V-O8&]C8V
+M-C8V-C8V-C8V-C8V-C8V-C8V-C8V-@``````_@;V-C8V-C8V-C8V-C8V-O8&
+M_@``````````-C8V-C8V-OX``````````!@8&!@8^!CX````````````````
+M````^!@8&!@8&!@8&!@8&!@8&!\``````````!@8&!@8&!C_````````````
+M````````_Q@8&!@8&!@8&!@8&!@8&!\8&!@8&!@8&`````````#_````````
+M```8&!@8&!@8_Q@8&!@8&!@8&!@8&!@?&!\8&!@8&!@8&#8V-C8V-C8W-C8V
+M-C8V-C8V-C8V-C<P/P`````````````````_,#<V-C8V-C8V-C8V-C8V]P#_
+M`````````````````/\`]S8V-C8V-C8V-C8V-C8W,#<V-C8V-C8V-@``````
+M_P#_```````````V-C8V-O<`]S8V-C8V-C8V&!@8&!C_`/\``````````#8V
+M-C8V-C;_`````````````````/\`_Q@8&!@8&!@8`````````/\V-C8V-C8V
+M-C8V-C8V-C8_```````````8&!@8&!\8'P`````````````````?&!\8&!@8
+M&!@8&``````````_-C8V-C8V-C8V-C8V-C8V_S8V-C8V-C8V&!@8&!C_&/\8
+M&!@8&!@8&!@8&!@8&!CX````````````````````'Q@8&!@8&!@8________
+M_____________P````````#____________P\/#P\/#P\/#P\/#P\/#P#P\/
+M#P\/#P\/#P\/#P\/#_________\``````````````````';<V-C8W'8`````
+M``!XS,S,V,S&QL;,````````_L;&P,#`P,#`P```````````_FQL;&QL;&P`
+M````````_L9@,!@P8,;^````````````?MC8V-C8<```````````9F9F9F9\
+M8&#``````````';<&!@8&!@8`````````'X8/&9F9CP8?@`````````X;,;&
+M_L;&;#@````````X;,;&QFQL;&SN````````'C`8##YF9F9F/```````````
+M`'[;V]M^`````````````P9^V]OS?F#`````````'C!@8'Y@8&`P'@``````
+M``!\QL;&QL;&QL8``````````/X``/X``/X````````````8&'X8&```_P``
+M```````P&`P&#!@P`'X`````````#!@P8#`8#`!^````````#AL;&Q@8&!@8
+M&!@8&!@8&!@8&!@8&-C8V'```````````!@8`'X`&!@`````````````=MP`
+M=MP`````````.&QL.````````````````````````!@8````````````````
+M````&```````````#PP,#`P,[&QL/!P``````-AL;&QL;`````````````!P
+MV#!@R/@`````````````````?'Q\?'Q\?```````````````````````````
+!`#!@
+`
+end
diff --git a/usr.sbin/pcvt/ispcvt/Makefile b/usr.sbin/pcvt/ispcvt/Makefile
new file mode 100644
index 0000000..a5a5cef
--- /dev/null
+++ b/usr.sbin/pcvt/ispcvt/Makefile
@@ -0,0 +1,4 @@
+PROG= ispcvt
+MAN8= ispcvt.${MAN8EXT}
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/pcvt/ispcvt/ispcvt.8 b/usr.sbin/pcvt/ispcvt/ispcvt.8
new file mode 100644
index 0000000..7edde7e
--- /dev/null
+++ b/usr.sbin/pcvt/ispcvt/ispcvt.8
@@ -0,0 +1,91 @@
+.\" Copyright (c) 1992, 1995 Hellmuth Michaelis
+.\"
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by Hellmuth Michaelis
+.\" 4. The name authors may not be used to endorse or promote products
+.\" derived from this software without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+.\" IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+.\"
+.\" @(#)ispcvt.1, 3.20, Last Edit-Date: [Tue Apr 4 12:35:54 1995]
+.\"
+.Dd April 4, 1995
+.Dt ISPCVT 1
+.Sh NAME
+.Nm ispcvt
+.Nd verify if current video driver is pcvt driver
+.Sh SYNOPSIS
+.Nm ispcvt
+.Op Fl c
+.Op Fl d Ar device
+.Op Fl v
+.Sh DESCRIPTION
+The
+.Nm ispcvt
+utility allows the user to check whether the current video driver compiled
+into the kernel is a pcvt driver. The major and minor release numbers of
+the driver are also checked.
+Furthermore
+.Nm ispcvt
+is also able to print out the values of all the
+.Dq Ar PCVT_XXXXXX
+compile time options, the driver in the current running kernel was
+compiled with.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl d
+Specifies a device for which the check is done.
+.It Fl v
+Specifies being verbose. On success the name and revision is reported, on
+failure which comparison failed.
+.It Fl c
+This options prints out the values of all
+.Dq Ar PCVT_XXXXXX
+#defines which were given to the compiler at the time the currently running
+kernel was compiled. Specifying
+.Fl v
+with the
+.Fl c
+option gives a verbose listing of the compile-time options.
+.Sh RETURN VALUE
+.Bl -tag -width Ds
+.Pp
+.It Sy 0
+driver is pcvt and major and minor numbers match
+.It Sy 1
+open or ioctl system call failed
+.It Sy 2
+driver name mismatch
+.It Sy 3
+name matched, release major number mismatch
+.It Sy 4
+name & major number matched, release minor number mismatch
+.It Sy 5
+usage error
+.El
+.Pp
+.Sh BUGS
+No known bugs
+.Sh SEE ALSO
+.Xr pcvt 4
diff --git a/usr.sbin/pcvt/ispcvt/ispcvt.c b/usr.sbin/pcvt/ispcvt/ispcvt.c
new file mode 100644
index 0000000..813e9dd
--- /dev/null
+++ b/usr.sbin/pcvt/ispcvt/ispcvt.c
@@ -0,0 +1,308 @@
+/*
+ * Copyright (c) 1992, 1995 Hellmuth Michaelis
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Hellmuth Michaelis
+ * 4. The name authors may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+static char *id =
+ "@(#)ispcvt.c, 3.20, Last Edit-Date: [Wed Apr 5 17:53:28 1995]";
+
+/*---------------------------------------------------------------------------*
+ *
+ * history:
+ *
+ * -hm upgraded to report pcvt compile time configuration
+ * -hm PCVT_INHIBIT_NUMLOCK patch from Joerg
+ * -hm PCVT_META_ESC patch from Joerg
+ * -hm PCVT_PCBURST
+ * -hm new ioctl VGAPCVTINFO
+ * -hm new CONF_ values for 3.10
+ * -hm new CONF_ values for 3.20
+ * -hm removed PCVT_FAKE_SYSCONS10
+ * -hm added PCVT_PORTIO_DELAY
+ * -hm removed PCVT_386BSD
+ * -hm add -d option to specify a device
+ * -hm PCVT_XSERVER -> XSERVER
+ *
+ *---------------------------------------------------------------------------*/
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <machine/pcvt_ioctl.h>
+
+#define DEFAULTFD 0
+
+main(argc,argv)
+int argc;
+char *argv[];
+{
+ extern int optind;
+ extern int opterr;
+ extern char *optarg;
+
+ struct pcvtid pcvtid;
+ struct pcvtinfo pcvtinfo;
+ int c;
+ char *p;
+ int verbose = 0;
+ int config = 0;
+ int dflag = 0;
+ int fd;
+ char *device;
+
+ while( (c = getopt(argc, argv, "vcd:")) != -1)
+ {
+ switch(c)
+ {
+ case 'v':
+ verbose = 1;
+ break;
+
+ case 'c':
+ config = 1;
+ break;
+
+ case 'd':
+ device = optarg;
+ dflag = 1;
+ break;
+
+ case '?':
+ default:
+ usage();
+ break;
+ }
+ }
+
+ if(dflag)
+ {
+ if((fd = open(device, O_RDWR)) == -1)
+ {
+ if(verbose)
+ {
+ char buffer[80];
+ strcpy(buffer,"ERROR opening ");
+ strcat(buffer,device);
+ perror(buffer);
+ }
+ exit(1);
+ }
+ }
+ else
+ {
+ fd = DEFAULTFD;
+ }
+
+ if(ioctl(fd, VGAPCVTID, &pcvtid) == -1)
+ {
+ if(verbose)
+ perror("ispcvt - ioctl VGAPCVTID failed, error");
+ exit(1);
+ }
+
+ if(!strcmp(pcvtid.name, PCVTIDNAME))
+ {
+ if(pcvtid.rmajor == PCVTIDMAJOR)
+ {
+ if(pcvtid.rminor != PCVTIDMINOR)
+ {
+ if(verbose)
+ fprintf(stderr,"ispcvt - minor revision: expected %d, got %d\n", PCVTIDMINOR, pcvtid.rminor);
+ exit(4); /* minor revision mismatch */
+ }
+ }
+ else
+ {
+ if(verbose)
+ fprintf(stderr,"ispcvt - major revision: expected %d, got %d\n", PCVTIDMAJOR, pcvtid.rmajor);
+ exit(3); /* major revision mismatch */
+ }
+ }
+ else
+ {
+ if(verbose)
+ fprintf(stderr,"ispcvt - name check: expected %s, got %s\n", PCVTIDNAME, pcvtid.name);
+ exit(2); /* name mismatch */
+ }
+
+ if(verbose)
+ {
+ fprintf(stderr,"\nispcvt: kernel and utils match, driver name [%s], release [%1.1d.%02.2d]\n\n",pcvtid.name,pcvtid.rmajor,pcvtid.rminor);
+ }
+
+ if(config == 0)
+ exit(0);
+
+ if(ioctl(fd, VGAPCVTINFO, &pcvtinfo) == -1)
+ {
+ if(verbose)
+ perror("ispcvt - ioctl VGAPCVTINFO failed, error");
+ exit(1);
+ }
+
+ if(verbose)
+ {
+ switch(pcvtinfo.opsys)
+ {
+ case CONF_NETBSD:
+ p = "PCVT_NETBSD";
+ break;
+
+ case CONF_FREEBSD:
+ p = "PCVT_FREEBSD";
+ break;
+
+ default:
+ case CONF_UNKNOWNOPSYS:
+ p = "UNKNOWN";
+ break;
+
+ }
+ fprintf(stderr,"Operating System = %s\t", p);
+ fprintf(stderr,"OS Release Id = %u\n", pcvtinfo.opsysrel);
+ fprintf(stderr,"PCVT_NSCREENS = %u\t\t", pcvtinfo.nscreens);
+ fprintf(stderr,"PCVT_UPDATEFAST = %u\n", pcvtinfo.updatefast);
+ fprintf(stderr,"PCVT_UPDATESLOW = %u\t\t", pcvtinfo.updateslow);
+ fprintf(stderr,"PCVT_SYSBEEPF = %u\n", pcvtinfo.sysbeepf);
+ fprintf(stderr,"PCVT_PCBURST = %u\t\t", pcvtinfo.pcburst);
+ fprintf(stderr,"PCVT_KBD_FIFO_SZ = %u\n\n", pcvtinfo.kbd_fifo_sz);
+
+ /* config booleans */
+
+ fprintf(stderr,"PCVT_132GENERIC = %s",
+ (pcvtinfo.compile_opts & CONF_132GENERIC) ? "ON" : "OFF");
+ next();
+ fprintf(stderr,"PCVT_24LINESDEF = %s",
+ (pcvtinfo.compile_opts & CONF_24LINESDEF) ? "ON" : "OFF");
+ next();
+ fprintf(stderr,"PCVT_BACKUP_FONTS = %s",
+ (pcvtinfo.compile_opts & CONF_BACKUP_FONTS) ? "ON" : "OFF");
+ next();
+ fprintf(stderr,"PCVT_CTRL_ALT_DEL = %s",
+ (pcvtinfo.compile_opts & CONF_CTRL_ALT_DEL) ? "ON" : "OFF");
+ next();
+ fprintf(stderr,"PCVT_EMU_MOUSE = %s",
+ (pcvtinfo.compile_opts & CONF_EMU_MOUSE) ? "ON" : "OFF");
+ next();
+ fprintf(stderr,"PCVT_INHIBIT_NUMLOCK = %s",
+ (pcvtinfo.compile_opts & CONF_INHIBIT_NUMLOCK) ? "ON" : "OFF");
+ next();
+ fprintf(stderr,"PCVT_KEYBDID = %s",
+ (pcvtinfo.compile_opts & CONF_KEYBDID) ? "ON" : "OFF");
+ next();
+ fprintf(stderr,"PCVT_KBD_FIFO = %s",
+ (pcvtinfo.compile_opts & CONF_KBD_FIFO) ? "ON" : "OFF");
+ next();
+ fprintf(stderr,"PCVT_META_ESC = %s",
+ (pcvtinfo.compile_opts & CONF_META_ESC) ? "ON" : "OFF");
+ next();
+ fprintf(stderr,"PCVT_NOFASTSCROLL = %s",
+ (pcvtinfo.compile_opts & CONF_NOFASTSCROLL) ? "ON" : "OFF");
+ next();
+ fprintf(stderr,"PCVT_NO_LED_UPDATE = %s",
+ (pcvtinfo.compile_opts & CONF_NO_LED_UPDATE) ? "ON" : "OFF");
+ next();
+ fprintf(stderr,"PCVT_NULLCHARS = %s",
+ (pcvtinfo.compile_opts & CONF_NULLCHARS) ? "ON" : "OFF");
+ next();
+ fprintf(stderr,"PCVT_PALFLICKER = %s",
+ (pcvtinfo.compile_opts & CONF_PALFLICKER) ? "ON" : "OFF");
+ next();
+ fprintf(stderr,"PCVT_PORTIO_DELAY = %s",
+ (pcvtinfo.compile_opts & CONF_PORTIO_DELAY) ? "ON" : "OFF");
+ next();
+ fprintf(stderr,"PCVT_PRETTYSCRNS = %s",
+ (pcvtinfo.compile_opts & CONF_PRETTYSCRNS) ? "ON" : "OFF");
+ next();
+ fprintf(stderr,"PCVT_SCREENSAVER = %s",
+ (pcvtinfo.compile_opts & CONF_SCREENSAVER) ? "ON" : "OFF");
+ next();
+ fprintf(stderr,"PCVT_SETCOLOR = %s",
+ (pcvtinfo.compile_opts & CONF_SETCOLOR) ? "ON" : "OFF");
+ next();
+ fprintf(stderr,"PCVT_SHOWKEYS = %s",
+ (pcvtinfo.compile_opts & CONF_SHOWKEYS) ? "ON" : "OFF");
+ next();
+ fprintf(stderr,"PCVT_SIGWINCH = %s",
+ (pcvtinfo.compile_opts & CONF_SIGWINCH) ? "ON" : "OFF");
+ next();
+ fprintf(stderr,"PCVT_SLOW_INTERRUPT = %s",
+ (pcvtinfo.compile_opts & CONF_SLOW_INTERRUPT) ? "ON" : "OFF");
+ next();
+ fprintf(stderr,"PCVT_SW0CNOUTP = %s",
+ (pcvtinfo.compile_opts & CONF_SW0CNOUTP) ? "ON" : "OFF");
+ next();
+ fprintf(stderr,"PCVT_USEKBDSEC = %s",
+ (pcvtinfo.compile_opts & CONF_USEKBDSEC) ? "ON" : "OFF");
+ next();
+ fprintf(stderr,"PCVT_USL_VT_COMPAT = %s",
+ (pcvtinfo.compile_opts & CONF_USL_VT_COMPAT) ? "ON" : "OFF");
+ next();
+ fprintf(stderr,"PCVT_VT220KEYB = %s",
+ ((u_int)pcvtinfo.compile_opts & (u_int)CONF_VT220KEYB) ? "ON" : "OFF");
+ next();
+ fprintf(stderr,"PCVT_WAITRETRACE = %s",
+ (pcvtinfo.compile_opts & CONF_WAITRETRACE) ? "ON" : "OFF");
+ next();
+ fprintf(stderr,"XSERVER = %s",
+ (pcvtinfo.compile_opts & CONF_XSERVER) ? "ON" : "OFF");
+
+ fprintf(stderr,"\n\n");
+ }
+ else /* !verbose */
+ {
+ fprintf(stderr,"BSD Version = %u\n", pcvtinfo.opsys);
+ fprintf(stderr,"PCVT_NSCREENS = %u\n", pcvtinfo.nscreens);
+ fprintf(stderr,"PCVT_UPDATEFAST = %u\n", pcvtinfo.updatefast);
+ fprintf(stderr,"PCVT_UPDATESLOW = %u\n", pcvtinfo.updateslow);
+ fprintf(stderr,"PCVT_SYSBEEPF = %u\n", pcvtinfo.sysbeepf);
+ fprintf(stderr,"Compile options = 0x%08X\n", pcvtinfo.compile_opts);
+ }
+}
+
+usage()
+{
+ fprintf(stderr,"\nispcvt - verify current video driver is the pcvt-driver\n");
+ fprintf(stderr," usage: ispcvt [-v] [-c] [-d device]\n");
+ fprintf(stderr,"options: -v be verbose\n");
+ fprintf(stderr," -c print compile time configuration\n");
+ fprintf(stderr," -d <name> use devicefile <name> for verification\n\n");
+ exit(5);
+}
+
+next()
+{
+ static int i = 0;
+
+ fprintf(stderr, "%s", (i == 0) ? "\t\t" : "\n");
+
+ i = ~i;
+}
+
+/* EOF */
diff --git a/usr.sbin/pcvt/kbdio/Makefile b/usr.sbin/pcvt/kbdio/Makefile
new file mode 100644
index 0000000..2b320b4
--- /dev/null
+++ b/usr.sbin/pcvt/kbdio/Makefile
@@ -0,0 +1,37 @@
+.if !exists(${.CURDIR}/../Makefile.inc)
+error:
+ @echo
+ @echo " You MUST link/copy"
+ @echo
+ @echo " ../Makefile.inc.NetBSD or ../Makefile.inc.FreeBSD"
+ @echo
+ @echo " to ../Makefile.inc before you can proceed !"
+ @echo
+.else
+
+PROG= kbdio
+SRCS= kbdio.c lex.c
+
+#YACC= bison
+#YFLAGS+= -yd # Bison only
+
+YFLAGS+= -v # verbose
+
+LFLAGS+= -I
+
+DPADD= ${LIBM} ${LIBY} ${LIBL}
+LDADD= -lm -ly -ll
+
+NOMAN=
+
+CFLAGS+= -I${.OBJDIR} -I${.CURDIR} #-g
+
+CLEANFILES+= kbdio.c lex.c lex.yy.c y.tab.[ch]
+CLEANFILES+= y.output # comment file from bison
+
+install:
+ @${ECHO} "kbdio is not installed automatically ...."
+
+.include <bsd.prog.mk>
+
+.endif
diff --git a/usr.sbin/pcvt/kbdio/kbdio.y b/usr.sbin/pcvt/kbdio/kbdio.y
new file mode 100644
index 0000000..5a81157
--- /dev/null
+++ b/usr.sbin/pcvt/kbdio/kbdio.y
@@ -0,0 +1,333 @@
+/* Hello emacs, this should be edited in -*- Fundamental -*- mode */
+%{
+/*
+ * Copyright (c) 1994 Joerg Wunsch
+ *
+ * All rights reserved.
+ *
+ * This program is free software.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Joerg Wunsch
+ * 4. The name of the developer may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE DEVELOPERS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ident "$Header: /home/ncvs/src/usr.sbin/pcvt/kbdio/kbdio.y,v 1.1.1.1 1995/02/05 13:49:24 jkh Exp $"
+
+/*
+ * $Log: kbdio.y,v $
+ * Revision 1.1.1.1 1995/02/05 13:49:24 jkh
+ * PCVT userland utilities.
+ * Submitted by: hm
+ *
+ * Revision 1.2 1994/09/18 19:49:22 j
+ * Refined expr handling; can now set/clear bits.
+ *
+ * Revision 1.1 1994/09/18 12:57:13 j
+ * Initial revision
+ *
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <sys/fcntl.h>
+#include <machine/cpufunc.h>
+#include <machine/pcvt_ioctl.h>
+
+#ifdef __NetBSD__
+#include <machine/pio.h>
+#endif
+
+#define KBD_DELAY \
+ { u_char x = inb(0x84); } \
+ { u_char x = inb(0x84); } \
+ { u_char x = inb(0x84); } \
+ { u_char x = inb(0x84); } \
+ { u_char x = inb(0x84); } \
+ { u_char x = inb(0x84); }
+
+#define YYDEBUG 1
+
+void yyerror(const char *msg);
+
+static void help(int), status(void), data(int), kbd(int), cmdbyte(int),
+ kbc(int), whatMCA(void);
+static int kbget(void);
+%}
+
+%union {
+ int num;
+}
+
+%token NEWLINE
+%token ALL CMD DATA DEFAULTS ECHOC ENABLE EXPR HELP ID LED
+%token MAKE ONLY RELEASE RESEND RESET SCAN STATUS TYPEMATIC
+%token WHAT
+%token <num> NUM
+
+%type <num> expr opr
+
+%%
+
+interpret: lines ;
+
+lines: line
+ | lines line
+ ;
+
+line: statements NEWLINE
+ | NEWLINE
+ | error NEWLINE { fprintf(stderr, "bing!\n"); }
+ ;
+
+statements: statement
+ | statements ';' statement
+ ;
+
+statement: '?' { help(0); }
+ | HELP { help(0); }
+ | HELP EXPR { help(1); }
+ | STATUS '?' { status(); }
+ | WHAT '?' { whatMCA(); }
+ | DATA '?' { data(kbget()); }
+ | LED '=' NUM { kbd(0xed); kbd($3); }
+ | ECHOC { kbd(0xee); kbget(); }
+ | SCAN '=' NUM { kbd(0xf0); kbd($3);
+ if($3 == 0) data(kbget()); }
+ | SCAN '?' { kbd(0xf0); kbd(0); data(kbget()); }
+ | ID '?' { kbd(0xf2); data(kbget());
+ data(kbget()); }
+ | TYPEMATIC '=' NUM ',' NUM
+ { kbd(0xf3);
+ if($3 > 1000) $3 = 1000;
+ if($5 > 30) $5 = 30;
+ if($5 < 2) $5 = 2;
+ kbd(
+ (int)
+ (8.0 * log(30.0 / (double)$5)
+ / log(2))
+ | ((($3 / 250) - 1) * 32)
+ );
+ }
+ | ENABLE { kbd(0xf4); }
+ | DEFAULTS { kbd(0xf6); }
+ | ALL TYPEMATIC { kbd(0xf7); }
+ | ALL MAKE RELEASE { kbd(0xf8); }
+ | ALL MAKE ONLY { kbd(0xf9); }
+ | ALL TYPEMATIC MAKE RELEASE
+ { kbd(0xfa); }
+ | NUM TYPEMATIC { kbd(0xfb); kbd($1); }
+ | NUM MAKE RELEASE { kbd(0xfc); kbd($1); }
+ | NUM MAKE ONLY { kbd(0xfd); kbd($1); }
+ | RESEND { kbd(0xfe); }
+ | RESET { kbd(0xff); }
+ | CMD '?' { kbc(0x20); cmdbyte(kbget()); }
+ | CMD '=' expr { kbc(0x60); kbd($3); }
+ | /* lambda */
+ ;
+
+expr: opr { $$ = $1; }
+ | expr '+' opr { $$ = $1 | $3; }
+ | expr '-' opr { $$ = $1 & ~($3); }
+ ;
+
+opr: NUM { $$ = $1; }
+ | CMD { kbc(0x20); $$ = kbget(); }
+ ;
+
+%%
+
+static void
+help(int topic) {
+ switch(topic) {
+ case 0:
+ printf(
+ "Input consists of lines, containing one or more semicolon-separated\n"
+ "statements. Numbers are implicitly hexadecimal, append a dot for\n"
+ "decimal numbers. Valid statements include:\n"
+ "help [expr]; give help [to expression syntax]\n"
+ "status ? interpret kbd ctrl status byte\n"
+ "what ? check for MCA type 1 or 2 motherboard controller\n"
+ "data ? get one byte of data\n"
+ "led = NUM set kbd LEDs\n"
+ "echo = NUM echo byte to kbd\n"
+ "scan = NUM; scan ? set scan code set; return current set\n"
+ "id ? get two id bytes\n"
+ "typematic=delay,rate set typematic delay(ms)&rate(1/s)\n"
+ "enable; defaults enable kbd; back to defaults\n"
+ "all typematic make all keys typematic\n"
+ "all make release make all keys sending make/release\n"
+ "all make only make all keys sending make only\n"
+ "all typematic make release make all keys typematic & make/release\n"
+ "NUM typematic make specific key typematic\n"
+ "NUM make release make specific key sending make/release\n"
+ "NUM make only make specific key sending make only\n"
+ "resend; reset resend last byte from kbd; reset kbd\n"
+ "cmd ? fetch kbd ctrl command byte\n"
+ "cmd = expr set kbd ctrl command byte\n"
+ "\n");
+ break;
+
+ case 1:
+ printf(
+ "Expressions can either consist of a number, possibly followed\n"
+ "by a + or - sign and bit values in numeric or symbolic form.\n"
+ "Symbolic bit values are:\n"
+ "SCCONV IGNPAR CLKLOW OVRINH TEST IRQ\n"
+ "\n");
+ break;
+ }
+}
+
+static void
+status(void) {
+ int c = inb(0x64);
+ if(c&0x80) printf("parity error | ");
+ if(c&0x40) printf("rx timeout | ");
+ if(c&0x20) printf("tx timeout | ");
+ if(c&0x10) printf("kbd released ");
+ else printf("kbd locked ");
+ if(c&0x08) printf("| cmd last sent ");
+ else printf("| data last sent ");
+ if(c&0x04) printf("| power-on ");
+ else printf("| test ok ");
+ if(c&0x02) printf("| ctrl write busy ");
+ else printf("| ctrl write ok ");
+ if(c&0x01) printf("| ctrl read ok\n");
+ else printf("| ctrl read empty\n");
+}
+
+/* see: Frank van Gilluwe, "The Undocumented PC", Addison Wesley 1994, pp 273 */
+
+static void
+whatMCA(void) {
+ int new, sav;
+ kbc(0x20); /* get command byte */
+ sav = kbget(); /* sav = command byte */
+ kbc(0x60); /* set command byte */
+ kbd(sav | 0x40); /* set keyboard xlate bit */
+ kbc(0x20); /* get keyboard command */
+ new = kbget(); /* new = command byte */
+ kbc(0x60); /* set command byte */
+ kbd(sav); /* restore command byte */
+ if(new & 0xbf)
+ printf("Hmm - looks like MCA type 1 motherboard controller\n");
+ else
+ printf("Hmm - looks like MCA type 2 motherboard controller\n");
+}
+
+static void
+kbd(int d) {
+ int i = 100000;
+ while(i && (inb(0x64) & 2)) i--;
+ if(i == 0) { printf("kbd write: timed out\n"); return; }
+ outb(0x60, d);
+}
+
+static void
+kbc(int d) {
+ int i = 100000;
+ while(i && (inb(0x64) & 2)) i--;
+ if(i == 0) { printf("ctrl write: timed out\n"); return; }
+ outb(0x64, d);
+}
+
+static int
+kbget(void) {
+ int i, c;
+ for(;;) {
+ i = 10000;
+ while(i && (inb(0x64) & 1) == 0) i--;
+ if(i == 0) { printf("data read: timed out\n"); return -1; }
+ KBD_DELAY
+ c = (unsigned char)inb(0x60);
+ switch(c) {
+ case 0: case 0xff:
+ printf("got kbd overrun\n"); break;
+ case 0xaa:
+ printf("got self-test OK\n"); break;
+ case 0xee:
+ printf("got ECHO byte\n"); break;
+ case 0xfa:
+ printf("got ACK\n"); break;
+ case 0xfc:
+ printf("got self-test FAIL\n"); break;
+ case 0xfd:
+ printf("got internal failure\n"); break;
+ case 0xfe:
+ printf("got RESEND request\n"); break;
+ default:
+ goto done;
+ }
+ }
+done:
+ return c;
+}
+
+static void
+cmdbyte(int d) {
+ if(d&0x40) printf("scan conv ");
+ else printf("pass thru ");
+ if(d&0x20) printf("| ign parity ");
+ else printf("| check parity ");
+ if(d&0x10) printf("| kbd clk low ");
+ else printf("| enable kbd ");
+ if(d&0x08) printf("| override kbd inh ");
+ if(d&0x04) printf("| test ok ");
+ else printf("| power-on ");
+ if(d&0x01) printf("| irq 1 enable\n");
+ else printf("| no irq\n");
+}
+
+static void
+data(int d) {
+ if(d < 0) return;
+ printf("data: 0x%02x\n", d);
+}
+
+void yyerror(const char *msg) {
+ fprintf(stderr, "yyerror: %s\n", msg);
+}
+
+int main(int argc, char **argv) {
+ int fd;
+
+ if(argc > 1) yydebug = 1;
+
+ if((fd = open("/dev/console", O_RDONLY)) < 0)
+ fd = 0;
+
+ if(ioctl(fd, KDENABIO, 0) < 0) {
+ perror("ioctl(KDENABIO)");
+ return 1;
+ }
+ yyparse();
+
+ (void)ioctl(fd, KDDISABIO, 0);
+ return 0;
+}
+
diff --git a/usr.sbin/pcvt/kbdio/lex.l b/usr.sbin/pcvt/kbdio/lex.l
new file mode 100644
index 0000000..a829dd2
--- /dev/null
+++ b/usr.sbin/pcvt/kbdio/lex.l
@@ -0,0 +1,102 @@
+%{
+/*
+ * Copyright (c) 1994 Joerg Wunsch
+ *
+ * All rights reserved.
+ *
+ * This program is free software.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Joerg Wunsch
+ * 4. The name of the developer may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE DEVELOPERS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ident "$Header: /home/ncvs/src/usr.sbin/pcvt/kbdio/lex.l,v 1.1.1.1 1995/02/05 13:49:24 jkh Exp $"
+
+/*
+ * $Log: lex.l,v $
+ * Revision 1.1.1.1 1995/02/05 13:49:24 jkh
+ * PCVT userland utilities.
+ * Submitted by: hm
+ *
+ * Revision 1.2 1994/09/18 19:48:45 j
+ * Added the symbolic values for kbd cmd byte.
+ *
+ * Revision 1.1 1994/09/18 12:57:13 j
+ * Initial revision
+ *
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "y.tab.h"
+
+extern YYSTYPE yylval;
+
+%}
+
+D [0-9a-fA-F]
+
+%%
+
+all { return ALL; }
+cmd { return CMD; }
+data { return DATA; }
+defaults { return DEFAULTS; }
+echo { return ECHOC; }
+enable { return ENABLE; }
+expr { return EXPR; }
+help { return HELP; }
+id { return ID; }
+led { return LED; }
+make { return MAKE; }
+only { return ONLY; }
+release { return RELEASE; }
+resend { return RESEND; }
+reset { return RESET; }
+scan { return SCAN; }
+status { return STATUS; }
+typematic { return TYPEMATIC; }
+what { return WHAT; }
+
+ /* numeric values */
+clklow { yylval.num = 0x10; return NUM; }
+ignpar { yylval.num = 0x20; return NUM; }
+irq { yylval.num = 0x01; return NUM; }
+ovrinh { yylval.num = 0x08; return NUM; }
+scconv { yylval.num = 0x40; return NUM; }
+test { yylval.num = 0x04; return NUM; }
+
+{D}({D}*)\. { sscanf(yytext, "%d", &yylval.num); return NUM; }
+
+{D}({D}*) { sscanf(yytext, "%x", &yylval.num); return NUM; }
+
+[ \t] { /* ignore */ }
+
+\n { return NEWLINE; }
+
+. { return yytext[0]; }
diff --git a/usr.sbin/pcvt/kcon/Makefile b/usr.sbin/pcvt/kcon/Makefile
new file mode 100644
index 0000000..5b58298
--- /dev/null
+++ b/usr.sbin/pcvt/kcon/Makefile
@@ -0,0 +1,17 @@
+PROG= kcon
+DEVICE= /dev/ttyv0
+CFLAGS+= -I${.CURDIR}/../keycap -DKEYB_DEVICE=\"${DEVICE}\"
+
+.if exists(${.OBJDIR}/../keycap)
+LIBDESTDIR= ${.OBJDIR}/../keycap
+.else
+LIBDESTDIR= ${.CURDIR}/../keycap
+.endif
+
+# the -Lfoo could be omitted if libkeycap.a were installed before
+# making those programs here
+
+DPADD = ${LIBDESTDIR}/libkeycap.a
+LDADD = -L${LIBDESTDIR} -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..b430340
--- /dev/null
+++ b/usr.sbin/pcvt/kcon/kcon.1
@@ -0,0 +1,122 @@
+.\" Copyright (c) 1992,1993,1994 Hellmuth Michaelis
+.\"
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by Hellmuth Michaelis
+.\" 4. The name authors may not be used to endorse or promote products
+.\" derived from this software without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+.\" IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+.\"
+.\" @(#)kcon.1, 3.20, Last Edit-Date: [Wed Jan 25 16:34:56 1995]
+.\"
+.Dd January 25, 1995
+.Dt KCON 1
+.Sh NAME
+.Nm kcon
+.Nd keyboard control and remapping
+.Sh SYNOPSIS
+.Nm kcon
+.Op Fl d Ar delay
+.Op Fl l
+.Op Fl m Ar map
+.Op Fl o
+.Op Fl p
+.Op Fl R
+.Op Fl r Ar rate
+.Op Fl s
+.Op Fl t Ns Ar +/-
+.Op Fl x
+.Sh DESCRIPTION
+.Nm kcon
+is used for controlling all aspects of keyboard configuration for the 'pcvt'
+video driver.
+.Pp
+The available options are:
+.Bl -tag -width flag
+.It Fl d Ar delay
+Specifies the delay after which the last key entered will be repeated by the
+Keyboard. Valid values are 0..3 corresponding to delays of 250, 500, 750 and
+1000 milliseconds.
+.It Fl l
+Displays the current keyboard map in use by the driver.
+.It Fl m Ar map
+Specifies the map entry to be searched in the keyboard capabilities database
+.Nm keycap.
+The database is searched for the entry and if found, the mapping
+is loaded and is used in the driver from then on.
+.It Fl o
+Switches display of control codes to octal in the listing of the current map.
+To be used in conjunction with the
+.Fl l
+option.
+.It Fl p
+Uses 'pure' output when listing - the Escape character is displayed in either
+octal or hexadecimal and not as 'ESC'. To be used in conjunction with the
+.Fl l
+option.
+.It Fl r Ar rate
+Specifies the character repetition rate. Valid argument values are 0...31
+corresponding to rates of 30 characters/second ... 2 characters/second.
+.It Fl R
+Reset the Keyboard.
+.It Fl s
+Displays the current settings of the rate and delay values.
+.It Fl t Ar +/-
+Specify this option to enable (
+.Ar +
+) or disable (
+.Ar -
+) the repetition of keys.
+.It Fl x
+Switches display of control codes to hexadecimal in the listing of the current map.
+To be used in conjunction with the
+.Fl l
+option. This is the default behaviour.
+.Sh FILES
+.Bl -tag -width /usr/share/misc/keycap.pcvt -compact
+.It Pa /usr/share/misc/keycap.pcvt
+Keyboard capabilities data base file if nothing else was chosen during installation.
+.It Pa /dev/console
+Keyboard raw device.
+.Sh SEE ALSO
+.Xr keycap 3 ,
+.Xr keycap 5
+.Sh BUGS
+.Nm kcon
+detects several inconsistencies in the keycap database. In case of errors
+.Nm kcon
+exits with an error message. If this happens, the keyboard may remain in
+an undefined state. To recover from such situation, execute
+.Nm kcon -m default
+.Sh EXAMPLES
+The command
+.Dq Li kcon -m gb
+loads the entry 'gb' from the keycap file into the keyboard to switch to
+a british keyboard behaviour.
+
+The command
+.Dq Li kcon -r 0 -d 0
+switches the keyboard to emit characters at a rate of 30 characters per second
+after a key has been held down for 250 milliseconds.
+
+
diff --git a/usr.sbin/pcvt/kcon/kcon.c b/usr.sbin/pcvt/kcon/kcon.c
new file mode 100644
index 0000000..1f9cded
--- /dev/null
+++ b/usr.sbin/pcvt/kcon/kcon.c
@@ -0,0 +1,753 @@
+/*
+ * Copyright (c) 1992,1993,1994 Hellmuth Michaelis
+ *
+ * Copyright (c) 1992,1993 Holger Veit.
+ *
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to 386BSD by
+ * Holger Veit
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by
+ * Hellmuth Michaelis and Holger Veit
+ * 4. The name authors may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+static char *id =
+ "@(#)kcon.c, 3.20, Last Edit-Date: [Wed Jan 25 16:33:08 1995]";
+
+/*---------------------------------------------------------------------------*
+ *
+ * kcon.c Keyboard control and remapping
+ * ----------------------------------------------
+ *
+ * based on "keymap" which was written by
+ * Holger Veit (veit@du9ds3.uni-duisburg.de)
+ *
+ * -hm a first rewrite
+ * -hm rewrite for pcvt 2.0 distribution
+ * -hm adding show current typematic values
+ * -hm hex/octal/esc output choices
+ * -hm remapping debugging
+ * -hm garbage output for remapped keys bugfix
+ * -hm patch from Lon Willet, adding -R
+ *
+ *---------------------------------------------------------------------------*/
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <machine/pcvt_ioctl.h>
+
+#include "keycap.h"
+
+int Rf = 0;
+int df = 0;
+int lf = 0;
+int mf = 0;
+int of = 0;
+int pf = 0;
+int rf = 0;
+int tf = 0;
+int xf = 0;
+int sf = 0;
+
+/*---------------------------------------------------------------------------*
+ * main entry
+ *---------------------------------------------------------------------------*/
+main(argc, argv)
+int argc;
+char *argv[];
+{
+ extern char *optarg;
+ extern int optind;
+
+ int c = 0;
+
+ int errf = 0;
+
+ int rate = -1;
+ int delay = -1;
+ char *map;
+ int kbfd;
+
+ while((c = getopt(argc, argv, "Rd:lm:opr:st:x")) != -1)
+ {
+ switch(c)
+ {
+ case 'R':
+ Rf = 1;
+ break;
+
+ case 'd':
+ df = 1;
+ delay = atoi(optarg);
+ break;
+
+ case 'l':
+ lf = 1;
+ break;
+
+ case 'm':
+ mf = 1;
+ map = optarg;
+ break;
+
+ case 'o':
+ if(xf)
+ errf = 1;
+ else
+ of = 1;
+ break;
+
+ case 'p':
+ pf = 1;
+ break;
+
+ case 'r':
+ rf = 1;
+ rate = atoi(optarg);
+ break;
+
+ case 's':
+ sf = 1;
+ break;
+
+ case 't':
+ if(*optarg == '+')
+ tf = 1;
+ else if(*optarg == '-')
+ tf = -1;
+ else
+ errf = 1;
+ break;
+
+ case 'x':
+ if(of)
+ errf = 1;
+ else
+ xf = 1;
+ break;
+
+ default:
+ usage();
+ }
+ }
+
+ if((Rf == 0 && df == 0 && lf == 0 && tf == 0 && sf == 0 &&
+ rf == 0 && mf == 0 ) || errf)
+ {
+ usage();
+ }
+
+ if((kbfd = open(KEYB_DEVICE, 0)) < 0)
+ {
+ perror("kcon: keyboard open failiure");
+ exit(1);
+ }
+
+ if(sf)
+ {
+ showtypeamatic(kbfd);
+ exit(0);
+ }
+
+ if(lf)
+ {
+ listcurrent(kbfd);
+ exit(0);
+ }
+
+ if (Rf)
+ {
+ if (ioctl(kbfd, KBDRESET, 0) < 0) {
+ perror ("kcon: ioctl KBDRESET failed");
+ exit (1);
+ }
+ }
+
+ if(tf)
+ {
+ setrepeat(kbfd, tf);
+ }
+
+ if(df || rf)
+ {
+ if(delay > 3)
+ {
+ fprintf(stderr,"Delay value (%d) out of range, possible values are 0..3!\n",delay);
+ exit(1);
+ }
+ if(rate > 31)
+ {
+ fprintf(stderr,"Rate value (%d) out of range, possible values are 0..31!\n",rate);
+ exit(1);
+ }
+ settypeam(kbfd, delay, rate);
+ }
+
+ if(mf)
+ {
+ remapkeys(kbfd, map);
+ }
+
+ close(kbfd);
+ exit(0);
+}
+
+/*---------------------------------------------------------------------------*
+ * display usage info & exit
+ *---------------------------------------------------------------------------*/
+usage()
+{
+ fprintf(stderr, "\nkcon: keyboard control and remapping utility for pcvt video driver\n");
+ fprintf(stderr, "usage: [-R] [-d delay] [-l] [-m map] [-o] [-p] [-r rate] [-t +/-] [-x]\n");
+ fprintf(stderr, " -R full reset of keyboard\n");
+ fprintf(stderr, " -d delay until a key is repeated (range: 0...3 => 250...1000ms)\n");
+ fprintf(stderr, " -l produce listing of current keyboard mapping\n");
+ fprintf(stderr, " -m set keyboard remapping from a keycap entry\n");
+ fprintf(stderr, " -o set octal output for listing\n");
+ fprintf(stderr, " -p pure, don't display escape as 'ESC' for listing\n");
+ fprintf(stderr, " -r chars/second repeat value (range: 0...31 => 30...2 chars/sec)\n");
+ fprintf(stderr, " -s show, display the current keyboard typematic values\n");
+ fprintf(stderr, " -t switch repeat on(+) or off(-)\n");
+ fprintf(stderr, " -x set hexadecimal output for listing\n\n");
+ exit(1);
+}
+
+/*---------------------------------------------------------------------------*
+ * convert control char in string to printable values
+ *---------------------------------------------------------------------------*/
+char *showcntrl(s)
+u_char *s;
+{
+ static char res_str[80];
+ static char conv_buf[80];
+ int i;
+
+ res_str[0] = '\0';
+
+ for(i = 0; s[i]; i++)
+ {
+ if(((s[i] > 0x20) && (s[i] <= 0x7e)) || ((s[i] >= 0xa0) && (s[i] <= 0xff)))
+ {
+ conv_buf[0] = s[i];
+ conv_buf[1] = '\0';
+ }
+ else if((s[i] == 0x1b) && (pf == 0))
+ {
+ strcpy(conv_buf,"ESC ");
+ }
+ else if(of)
+ {
+ sprintf(conv_buf,"\\%03.3o ", s[i]);
+ }
+ else
+ {
+ sprintf(conv_buf,"0x%02.2X ", s[i]);
+ }
+ strcat(res_str, conv_buf);
+ }
+ return(res_str);
+}
+
+/*---------------------------------------------------------------------------*
+ * list the current keyboard mapping
+ *---------------------------------------------------------------------------*/
+listcurrent(kbfd)
+int kbfd;
+{
+ static char *keytypetab[] = {
+ "NONE ",
+ "SHIFT ",
+ "ALT/META ",
+ "NUMLOCK ",
+ "CONTROL ",
+ "CAPSLOCK ",
+ "ASCII ",
+ "SCROLL ",
+ "FUNCTION ",
+ "KEYPAD ",
+ "BREAK ",
+ "ALTGR ",
+ "SHIFTLOCK",
+ "CURSOR ",
+ "RETURN "
+ };
+
+ struct kbd_ovlkey keyboardmap[KBDMAXKEYS];
+ struct kbd_ovlkey *kbmapp;
+ int keytype;
+ int altgr_defined;
+ int i;
+
+ altgr_defined = 0;
+ kbmapp = keyboardmap;
+
+ for (i = 0; i < KBDMAXKEYS; i++)
+ {
+ kbmapp->keynum = i;
+
+ if(ioctl(kbfd, KBDGCKEY, kbmapp) < 0)
+ {
+ perror("kcon: ioctl KBDGCKEY failed");
+ exit(1);
+ }
+
+ if((kbmapp->type & KBD_MASK) == KBD_ALTGR)
+ altgr_defined = i;
+
+ kbmapp++;
+ }
+
+ if(altgr_defined)
+ {
+ printf("S Key KeyType Normal Shift Control Altgr \n");
+ printf("- --- --------- --------------- --------------- --------------- ---------------\n");
+ }
+ else
+ {
+ printf("S Key KeyType Normal Shift Control \n");
+ printf("- --- --------- --------------- --------------- ---------------\n");
+ }
+
+ kbmapp = &keyboardmap[1];
+
+ for(i = 1; i < KBDMAXKEYS; i++)
+ {
+ keytype = kbmapp->type;
+
+ if(keytype)
+ {
+ if(keytype & KBD_OVERLOAD)
+ printf("! %3.3d %9.9s ", i, keytypetab[keytype & KBD_MASK]);
+ else
+ printf("- %3.3d %9.9s ", i, keytypetab[keytype & KBD_MASK]);
+
+ switch(keytype & KBD_MASK)
+ {
+
+ case KBD_NUM:
+ case KBD_ASCII:
+ case KBD_FUNC:
+ case KBD_KP:
+ case KBD_CURSOR:
+ case KBD_RETURN: /* ??? */
+
+ if(kbmapp->subu == KBD_SUBT_STR)
+ printf("%-15s ",showcntrl(kbmapp->unshift));
+ else
+ printf("Function() ");
+
+ if(kbmapp->subs == KBD_SUBT_STR)
+ printf("%-15s ",showcntrl(kbmapp->shift));
+ else
+ printf("Function() ");
+
+ if(kbmapp->subc == KBD_SUBT_STR)
+ printf("%-15s ",showcntrl(kbmapp->ctrl));
+ else
+ printf("Function() ");
+
+ if(altgr_defined)
+ {
+ if(kbmapp->suba == KBD_SUBT_STR)
+ printf("%-15s ",showcntrl(kbmapp->altgr));
+ else
+ printf("Function() ");
+ }
+ break;
+ }
+ putchar('\n');
+ }
+ kbmapp++;
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * show delay and rate values for keyboard
+ *---------------------------------------------------------------------------*/
+showtypeamatic(kbfd)
+int kbfd;
+{
+ static char *delaytab[] = {
+ "250",
+ "500",
+ "750",
+ "1000"
+ };
+
+ static char *ratetab[] = {
+ "30.0",
+ "26.7",
+ "24.0",
+ "21.8",
+ "20.0",
+ "18.5",
+ "17.1",
+ "16.0",
+ "15.0",
+ "13.3",
+ "12.0",
+ "10.9",
+ "10.0",
+ "9.2",
+ "8.6",
+ "8.0",
+ "7.5",
+ "6.7",
+ "6.0",
+ "5.5",
+ "5.0",
+ "4.6",
+ "4.3",
+ "4.0",
+ "3.7",
+ "3.3",
+ "3.0",
+ "2.7",
+ "2.5",
+ "2.3",
+ "2.1",
+ "2.0"
+ };
+
+ int cur_typemat_val;
+ int delay, rate;
+
+ if((ioctl(kbfd, KBDGTPMAT, &cur_typemat_val)) < 0)
+ {
+ perror("kcon: ioctl KBDGTPMAT failed");
+ exit(1);
+ }
+
+ delay = ((cur_typemat_val & 0x60) >> 5);
+ rate = cur_typemat_val & 0x1f;
+
+ printf("\nDisplaying the current keyboard typematic values:\n\n");
+ printf("The delay-until-repeat time is [ %s ] milliseconds\n",delaytab[delay]);
+ printf("The repeat-rate is [ %s ] characters per second\n\n",ratetab[rate]);
+}
+
+/*---------------------------------------------------------------------------*
+ * set repeat feature on/off
+ *---------------------------------------------------------------------------*/
+setrepeat(kbfd, tf)
+int kbfd;
+int tf;
+{
+ int srepsw_val;
+
+ if(tf == 1)
+ srepsw_val = KBD_REPEATON;
+ else
+ srepsw_val = KBD_REPEATOFF;
+
+ if(ioctl(kbfd, KBDSREPSW, &srepsw_val) < 0)
+ {
+ perror("kcon: ioctl KBDREPSW failed");
+ exit(1);
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * set delay and rate values for keyboard
+ *---------------------------------------------------------------------------*/
+settypeam(kbfd, delay, rate)
+int kbfd;
+int delay;
+int rate;
+{
+ int cur_typemat_val;
+ int new_typemat_val;
+
+ if((ioctl(kbfd, KBDGTPMAT, &cur_typemat_val)) < 0)
+ {
+ perror("kcon: ioctl KBDGTPMAT failed");
+ exit(1);
+ }
+
+ if(delay == -1)
+ delay = (cur_typemat_val & 0x60);
+ else
+ delay = ((delay << 5) & 0x60);
+
+ if(rate == -1)
+ rate = (cur_typemat_val & 0x1f);
+ else
+ rate &= 0x1f;
+
+ new_typemat_val = delay | rate;
+
+ if((ioctl(kbfd, KBDSTPMAT, &new_typemat_val)) < 0)
+ {
+ perror("kcon: ioctl KBDSTPMAT failed");
+ exit(1);
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * remap keyboard from keycap entry
+ *---------------------------------------------------------------------------*/
+remapkeys(kbfd, map)
+int kbfd;
+char *map;
+{
+ char cap_entry[1024];
+ int ret;
+ char keyflag[128];
+ int i;
+
+ /* try to find the entry */
+
+ ret = kgetent(cap_entry, map);
+
+ if(ret == -1)
+ {
+ fprintf(stderr, "kcon: keycap database not found or not accessible!\n");
+ exit(1);
+ }
+ else if(ret == 0)
+ {
+ fprintf(stderr, "kcon: keycap entry [%s] not found in database!\n", map);
+ exit(1);
+ }
+
+ /* set default mapping */
+
+ if((ioctl(kbfd, KBDDEFAULT)) < 0)
+ {
+ perror("kcon: ioctl KBDDEFAULT failed");
+ exit(1);
+ }
+
+ /* DE flag present? */
+
+ if(kgetflag("de"))
+ return;
+
+ for(i = 0; i < KBDMAXKEYS; i++)
+ keyflag[i] = 0;
+
+ set_lock(keyflag, kbfd);
+
+ set_shift(keyflag, kbfd);
+
+ set_char(keyflag, kbfd);
+}
+
+/*---------------------------------------------------------------------------*
+ * care for lock keys
+ *---------------------------------------------------------------------------*/
+set_lock(keyflag, kbfd)
+char keyflag[];
+int kbfd;
+{
+ int i, j;
+ char cap[16];
+ struct kbd_ovlkey entry;
+
+ struct {
+ char *ch;
+ u_short typ;
+ } lock[] =
+ {
+ "ca", KBD_CAPS,
+ "sh", KBD_SHFTLOCK,
+ "nl", KBD_NUMLOCK,
+ "sc", KBD_SCROLL
+ };
+
+
+ for(i = 0; i < 4; i++)
+ {
+ int n;
+
+ sprintf(cap, "%s", lock[i].ch);
+
+ n = kgetnum(cap);
+
+ if(n > 0)
+ {
+ if (keyflag[n])
+ {
+ fprintf(stderr,"kcon: duplicate key definition for key [%d]!\n",n);
+ exit(1);
+ }
+ keyflag[n] = 1;
+
+ entry.keynum = n;
+ entry.type = lock[i].typ;
+
+ if((ioctl(kbfd, KBDSCKEY, &entry)) < 0)
+ {
+ perror("kcon: ioctl KBDSCKEY failed");
+ exit(1);
+ }
+ }
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * care for shifting keys
+ *---------------------------------------------------------------------------*/
+set_shift(keyflag, kbfd)
+char keyflag[];
+int kbfd;
+{
+ int i, j;
+ char cap[16];
+ struct kbd_ovlkey entry;
+
+ struct {
+ char ch;
+ u_short typ;
+ } shift[] =
+ {
+ 'm', KBD_META,
+ 'l', KBD_ALTGR,
+ 'h', KBD_SHIFT,
+ 't', KBD_CTL
+ };
+
+ for(i = 0; i < 4; i++)
+ {
+ for(j = 1; j < 10; j++)
+ {
+ int n;
+
+ sprintf(cap, "%c%d", shift[i].ch,j);
+
+ n = kgetnum(cap);
+
+ if (n >= 0)
+ {
+ if (keyflag[n])
+ {
+ fprintf(stderr,"kcon: duplicate key definition for key [%d]!\n",n);
+ exit(1);
+ }
+ keyflag[n] = 1;
+
+ entry.keynum = n;
+ entry.type = shift[i].typ;
+ if((ioctl(kbfd, KBDSCKEY, &entry)) < 0)
+ {
+ perror("kcon: ioctl KBDSCKEY failed");
+ exit(1);
+ }
+ }
+ }
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * care for normal keys
+ *---------------------------------------------------------------------------*/
+set_char(keyflag, kbfd)
+char keyflag[];
+int kbfd;
+{
+ int i, j;
+ char cap[16];
+ int setflag;
+ char *addr_str;
+ char *new_str;
+ struct kbd_ovlkey entry;
+
+ struct {
+ char *addr;
+ char ch;
+ } standard[] = {
+ 0, 'D',
+ &entry.unshift[0], 'K',
+ &entry.shift[0], 'S',
+ &entry.ctrl[0], 'C',
+ &entry.altgr[0], 'A'
+ };
+
+ for(i = 1; i < KBDMAXKEYS; i++)
+ {
+ setflag = 0;
+
+ entry.keynum = i;
+
+ if((ioctl(kbfd, KBDGOKEY, &entry)) < 0)
+ {
+ perror("kcon: ioctl KBDGOKEY failed");
+ exit(1);
+ }
+
+ entry.type = KBD_ASCII;
+
+ for(j = 0; j < 5; j++)
+ {
+ sprintf(cap, "%c%d", standard[j].ch,i);
+
+ if((j == 0) && (kgetflag(cap)))
+ {
+ /* delete a key */
+
+ entry.type = KBD_NONE;
+ setflag = 1;
+ goto setit;
+
+ }
+ else
+ {
+ addr_str = standard[j].addr;
+ if(new_str = kgetstr(cap, &addr_str))
+ {
+ if(strlen(new_str) > KBDMAXOVLKEYSIZE)
+ {
+ fprintf(stderr, "kcon: database entry string [%s] longer than max [%d]!\n",new_str,KBDMAXOVLKEYSIZE);
+ exit(1);
+ }
+ setflag = 1;
+ }
+ }
+ }
+
+setit: if (setflag)
+ {
+ if (keyflag[i])
+ {
+ fprintf(stderr,"kcon: duplicate key definition for key [%d]!\n",i);
+ exit(1);
+ }
+ keyflag[i] = 1;
+
+ if((ioctl(kbfd, KBDSCKEY, &entry)) < 0)
+ {
+ perror("kcon: ioctl KBDSCKEY failed");
+ exit(1);
+ }
+ }
+ }
+}
+
+/*------------------- EOF ------------------------------------------------*/
diff --git a/usr.sbin/pcvt/keycap/Makefile b/usr.sbin/pcvt/keycap/Makefile
new file mode 100644
index 0000000..a39ee67
--- /dev/null
+++ b/usr.sbin/pcvt/keycap/Makefile
@@ -0,0 +1,33 @@
+
+.if !exists(${.CURDIR}/../Makefile.inc)
+error:
+ @echo
+ @echo " You MUST link/copy"
+ @echo
+ @echo " ../Makefile.inc.NetBSD or ../Makefile.inc.FreeBSD"
+ @echo
+ @echo " to ../Makefile.inc before you can proceed !"
+ @echo
+.else
+
+LIB = keycap
+CAPDIR = /usr/share/misc
+CAPPATH = $(CAPDIR)/keycap.pcvt
+KEYCAPSRC= keycap.src
+CFLAGS += -DKEYCAP_PATH=\"$(CAPPATH)\"
+SRCS = keycap.c
+MAN3 = keycap.${MAN3EXT}
+MLINKS+= keycap.${MAN3EXT} kgetent.${MAN3EXT} \
+ keycap.${MAN3EXT} kgetnum.${MAN3EXT} \
+ keycap.${MAN3EXT} kgetflag.${MAN3EXT} \
+ keycap.${MAN3EXT} kgetstr.${MAN3EXT}
+MAN5 = man5/keycap.${MAN5EXT}
+#CLEANFILES+= keycap.0 man5/keycap.0
+
+beforeinstall:
+ ${INSTALL} -c -m ${LIBMODE} -o ${LIBOWN} -g ${LIBGRP} \
+ ${.CURDIR}/${KEYCAPSRC} ${DESTDIR}${CAPPATH}
+
+.include <bsd.lib.mk>
+
+.endif
diff --git a/usr.sbin/pcvt/keycap/keycap.3 b/usr.sbin/pcvt/keycap/keycap.3
new file mode 100644
index 0000000..b62b427
--- /dev/null
+++ b/usr.sbin/pcvt/keycap/keycap.3
@@ -0,0 +1,124 @@
+.\"
+.\" Copyright (c) 1992,1993,1994 Hellmuth Michaelis
+.\"
+.\" Copyright (c) 1990 The Regents of the University of California.
+.\"
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)keycap.3, 3.00, Last Edit-Date: [Sun Jan 2 13:46:43 1994]
+.\"
+.Dd January 3, 1993
+.Dt KEYCAP 3
+.Sh NAME
+.Nm kgetent ,
+.Nm kgetnum ,
+.Nm kgetflag ,
+.Nm kgetstr
+.Nd routines for accessing the keycap database
+.Sh SYNOPSIS
+.Ft int
+.Fn kgetent "char *bp" "char *name"
+.Ft int
+.Fn kgetnum "char *id"
+.Ft int
+.Fn kgetflag "char *id"
+.Ft char *
+.Fn kgetstr "char *id" "char *area"
+.Sh DESCRIPTION
+These functions extract and use capabilities from a keyboard capability data
+base, usually
+.Pa /usr/share/misc/keycap.pcvt ,
+the format of which is described in
+.Xr keycap 5 .
+.Pp
+The
+.Fn kgetent
+function
+extracts the entry for keyboard mapping
+.Fa name
+into the buffer at
+.Fa bp .
+The
+.Fa bp
+argument
+should be a character buffer of size
+1024 and must be retained through all subsequent calls to
+.Fn kgetnum ,
+.Fn kgetflag ,
+and
+.Fn kgetstr .
+The
+.Fn kgetent
+function
+returns \-1 if none of the
+.Nm keycap
+data base files could be opened,
+0 if the map name given does not have an entry,
+and 1 if all goes well.
+.Pp
+The
+.Fn kgetnum
+function
+gets the numeric value of capability
+.Fa id ,
+returning \-1 if is not given for the map.
+.Pp
+The
+.Fn kgetflag
+function
+returns 1 if the specified capability is present in
+the map's entry, 0 if it is not.
+.Pp
+The
+.Fn kgetstr
+function
+returns the string value of the capability
+.Fa id ,
+places it in the buffer at
+.Fa area ,
+and advances the
+.Fa area
+pointer.
+The
+.Fn kgetstr
+function
+returns
+.Dv NULL
+if the capability was not found.
+.Pp
+.Sh FILES
+.Bl -tag -width /usr/share/misc/keycap.pcvt -compact
+.It Pa /usr/share/misc/keycap.pcvt
+Keyboard capabilities database (if nothing else chosen during installation).
+.El
+.Sh SEE ALSO
+.Xr kcon 1 ,
+.Xr keycap 5
diff --git a/usr.sbin/pcvt/keycap/keycap.c b/usr.sbin/pcvt/keycap/keycap.c
new file mode 100644
index 0000000..9bbb7e5
--- /dev/null
+++ b/usr.sbin/pcvt/keycap/keycap.c
@@ -0,0 +1,377 @@
+/*-
+ * Copyright (c) 1992, 1993 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Holger Veit
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+static char *id =
+ "@(#)keycap.c, 3.20, Last Edit-Date: [Tue Dec 20 14:51:50 1994]";
+
+/*---------------------------------------------------------------------------*
+ *
+ * keycap.c Keyboard capabilities database handling
+ * -------------------------------------------------------
+ *
+ * converted from printcap by Holger Veit (veit@du9ds3.uni-duisburg.de)
+ *
+ * BUG: Should use a "last" pointer in tbuf, so that searching
+ * for capabilities alphabetically would not be a n**2/2
+ * process when large numbers of capabilities are given.
+ *
+ * Note: If we add a last pointer now we will screw up the
+ * tc capability. We really should compile termcap.
+ *
+ * modified by Hellmuth Michaelis (hm@hcshh.hcs.de) to fit into the
+ * vt220 driver pcvt 2.0 distribution
+ *
+ * -hm header conversion & cosmetic changes for pcvt 2.0 distribution
+ * -hm debugging remapping
+ * -hm cleaning up from termcap ....
+ * -hm split off header file keycap.h
+ *
+ *---------------------------------------------------------------------------*/
+
+#include <stdio.h>
+#include <ctype.h>
+
+#include "keycap.h"
+
+#define KEYCAP_BUFSIZ 1024
+
+#define MAXHOP 32 /* max number of tc= indirections */
+
+char *getenv();
+
+static FILE *pfp = NULL; /* keycap data base file pointer */
+static char *tbuf;
+static int hopcount; /* detect infinite loops in keycap, init 0 */
+
+static int knchktc();
+static int knamatch();
+static char *kdecode();
+
+/*---------------------------------------------------------------------------*
+ * match a name
+ *---------------------------------------------------------------------------*/
+static char *nmatch(id,cstr)
+char *id,*cstr;
+{
+ register n = strlen(id);
+ register char *c = cstr+n;
+
+ if (strncmp(id,cstr,n)==0 &&
+ (*c==':' || *c=='|' || *c=='=' || *c=='#') || *c=='@')
+ return c;
+ return 0;
+}
+
+/*---------------------------------------------------------------------------*
+ * Get an entry for keyboard name in buffer bp from the keycap file.
+ * Parse is very rudimentary, we just notice escaped newlines.
+ *---------------------------------------------------------------------------*/
+kgetent(bp, name)
+char *bp, *name;
+{
+ register char *cp;
+ register int c;
+ register int i = 0, cnt = 0;
+ char ibuf[KEYCAP_BUFSIZ];
+ char *cp2;
+ int tf;
+
+ tbuf = bp;
+ tf = 0;
+
+ tf = open(KEYCAP_PATH, 0);
+
+ if (tf < 0)
+ return (-1);
+ for (;;) {
+ cp = bp;
+ for (;;) {
+ if (i == cnt) {
+ cnt = read(tf, ibuf, KEYCAP_BUFSIZ);
+ if (cnt <= 0) {
+ close(tf);
+ return (0);
+ }
+ i = 0;
+ }
+ c = ibuf[i++];
+ if (c == '\n') {
+ if (cp > bp && cp[-1] == '\\'){
+ cp--;
+ continue;
+ }
+ break;
+ }
+ if (cp >= bp+KEYCAP_BUFSIZ) {
+ write(2,"Keycap entry too long\n", 23);
+ break;
+ } else
+ *cp++ = c;
+ }
+ *cp = 0;
+
+ /*
+ * The real work for the match.
+ */
+ if (knamatch(name)) {
+ close(tf);
+ return(knchktc());
+ }
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * knchktc: check the last entry, see if it's tc=xxx. If so, recursively
+ * find xxx and append that entry (minus the names) to take the place of
+ * the tc=xxx entry. Note that this works because of the left to right scan.
+ *---------------------------------------------------------------------------*/
+static int knchktc()
+{
+ register char *p, *q;
+ char tcname[16]; /* name of similar keyboard */
+ char tcbuf[KEYCAP_BUFSIZ];
+ char *holdtbuf = tbuf;
+ int l;
+
+ p = tbuf + strlen(tbuf) - 2; /* before the last colon */
+ while (*--p != ':')
+ if (p<tbuf) {
+ write(2, "Bad keycap entry\n", 18);
+ return (0);
+ }
+ p++;
+ /* p now points to beginning of last field */
+ if (p[0] != 't' || p[1] != 'c')
+ return(1);
+ strcpy(tcname,p+3);
+ q = tcname;
+ while (q && *q != ':')
+ q++;
+ *q = 0;
+ if (++hopcount > MAXHOP) {
+ write(2, "Infinite tc= loop\n", 18);
+ return (0);
+ }
+ if (kgetent(tcbuf, tcname) != 1)
+ return(0);
+ for (q=tcbuf; *q != ':'; q++)
+ ;
+ l = p - holdtbuf + strlen(q);
+ if (l > KEYCAP_BUFSIZ) {
+ write(2, "Keycap entry too long\n", 23);
+ q[KEYCAP_BUFSIZ - (p-tbuf)] = 0;
+ }
+ strcpy(p, q+1);
+ tbuf = holdtbuf;
+ return(1);
+}
+
+/*---------------------------------------------------------------------------*
+ * knamatch deals with name matching. The first field of the keycap entry
+ * is a sequence of names separated by |'s, so we compare against each such
+ * name. The normal : terminator after the last name (before the first field)
+ * stops us.
+ *---------------------------------------------------------------------------*/
+static int knamatch(np)
+char *np;
+{
+ register char *Np, *Bp;
+
+ Bp = tbuf;
+ if (*Bp == '#' || *Bp == 0)
+ return(0);
+ for (;;) {
+ for (Np = np; *Np && *Bp == *Np; Bp++, Np++)
+ continue;
+ if (*Np == 0 && (*Bp == '|' || *Bp == ':' || *Bp == 0))
+ return (1);
+ while (*Bp && *Bp != ':' && *Bp != '|')
+ Bp++;
+ if (*Bp == 0 || *Bp == ':')
+ return (0);
+ Bp++;
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * Skip to the next field. Notice that this is very dumb, not knowing about
+ * \: escapes or any such. If necessary, :'s can be put into the keycap file
+ * in octal.
+ *---------------------------------------------------------------------------*/
+static char *kskip(bp)
+char *bp;
+{
+ while (*bp && *bp != ':')
+ bp++;
+ if (*bp == ':')
+ bp++;
+ return (bp);
+}
+
+/*---------------------------------------------------------------------------*
+ * Return the (numeric) option id. Numeric options look like 'li#80' i.e.
+ * the option string is separated from the numeric value by a # character.
+ * If the option is not found we return -1. Note that we handle octal
+ * numbers beginning with 0.
+ *---------------------------------------------------------------------------*/
+int kgetnum(id)
+char *id;
+{
+ register int i, base;
+ register char *bp = tbuf,*xp;
+
+ for (;;) {
+ bp = kskip(bp);
+ if (*bp == 0)
+ return (-1);
+ if ((xp=nmatch(id,bp)) == 0)
+ continue;
+ bp = xp; /* we have an entry */
+ if (*bp == '@')
+ return(-1);
+ if (*bp != '#')
+ continue;
+ bp++;
+ base = 10;
+ if (*bp == '0')
+ base = 8;
+ i = 0;
+ while (isdigit(*bp))
+ i *= base, i += *bp++ - '0';
+ return (i);
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * Handle a flag option. Flag options are given "naked", i.e. followed by
+ * a : or the end of the buffer. Return 1 if we find the option, or 0 if
+ * it is not given.
+ *---------------------------------------------------------------------------*/
+int kgetflag(id)
+char *id;
+{
+ register char *bp = tbuf,*xp;
+
+ for (;;) {
+ bp = kskip(bp);
+ if (!*bp)
+ return (0);
+ if ((xp=nmatch(id,bp)) != 0) {
+ bp = xp;
+ if (!*bp || *bp == ':')
+ return (1);
+ else if (*bp == '@')
+ return(0);
+ }
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * Get a string valued option. These are given as 'cl=^Z'. Much decoding
+ * is done on the strings, and the strings are placed in area, which is a
+ * ref parameter which is updated. No checking on area overflow.
+ *---------------------------------------------------------------------------*/
+char *kgetstr(id, area)
+char *id;
+char **area;
+{
+ register char *bp = tbuf,*xp;
+
+ for (;;) {
+ bp = kskip(bp);
+ if (!*bp)
+ return (0);
+ if ((xp = nmatch(id,bp)) == 0)
+ continue;
+ bp = xp;
+ if (*bp == '@')
+ return(0);
+ if (*bp != '=')
+ continue;
+ bp++;
+ return (kdecode(bp, area));
+ }
+}
+
+/*---------------------------------------------------------------------------*
+ * kdecode does the grung work to decode the string capability escapes.
+ *---------------------------------------------------------------------------*/
+static char *kdecode(str, area)
+char *str;
+char **area;
+{
+ register char *cp;
+ register int c;
+ register char *dp;
+ int i;
+
+ cp = *area;
+ while ((c = *str++) && c != ':') {
+ switch (c) {
+
+ case '^':
+ c = *str++ & 037;
+ break;
+
+ case '\\':
+ dp = "E\033^^\\\\::n\nr\rt\tb\bf\f";
+ c = *str++;
+nextc:
+ if (*dp++ == c) {
+ c = *dp++;
+ break;
+ }
+ dp++;
+ if (*dp)
+ goto nextc;
+ if (isdigit(c)) {
+ c -= '0', i = 2;
+ do
+ c <<= 3, c |= *str++ - '0';
+ while (--i && isdigit(*str));
+ }
+ break;
+ }
+ *cp++ = c;
+ }
+ *cp++ = 0;
+ str = *area;
+ *area = cp;
+ return (str);
+}
+
+/*-------------------------------- EOF --------------------------------------*/
diff --git a/usr.sbin/pcvt/keycap/keycap.h b/usr.sbin/pcvt/keycap/keycap.h
new file mode 100644
index 0000000..1dc4c3e
--- /dev/null
+++ b/usr.sbin/pcvt/keycap/keycap.h
@@ -0,0 +1,49 @@
+/*-
+ * Copyright (c) 1992, 1993 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Holger Veit
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)keycap.h, 3.20, Last Edit-Date: [Tue Dec 20 14:52:11 1994]
+ */
+
+#ifndef _KEYCAP_H_
+#define _KEYCAP_H_
+
+int kgetent( char*, char* );
+int kgetnum( char* );
+int kgetflag( char* );
+char *kgetstr( char*, char** );
+
+#endif /* _KEYCAP_H_ */
+
+/*-------------------------------- EOF -------------------------------------*/
diff --git a/usr.sbin/pcvt/keycap/keycap.src b/usr.sbin/pcvt/keycap/keycap.src
new file mode 100644
index 0000000..af50d72
--- /dev/null
+++ b/usr.sbin/pcvt/keycap/keycap.src
@@ -0,0 +1,613 @@
+# Copyright (c) 1992, 1993, 1994 Hellmuth Michaelis, Joerg Wunsch and
+# Holger Veit.
+#
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# 3. All advertising materials mentioning features or use of this software
+# must display the following acknowledgement:
+# This product includes software developed by Hellmuth Michaelis,
+# Joerg Wunsch and Holger Veit.
+# 4. The names of the Authors may not be used to endorse or promote
+# products derived from this software without specific prior written
+# permission.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# @(#)keycap.src, 3.20, Last Edit-Date: [Wed Mar 8 20:53:01 1995]
+#
+#---------------------------------------------------------------------------
+#
+# keyboard mappings for vt220 emulator pcvt 3.00
+# ----------------------------------------------
+#
+# DEC MCS and/or ISO-Latin-1 Characterset used
+#
+# MF II Keyboards fully supported
+# AT Keyboards lack a ALTGR Key, so they cannot be handled by
+# the current driver implementation .... sorry
+#
+# If you design a new entry for national keyboards, please
+# send it to hm@hcshh.hcs.de, thank you !
+#
+# Many entries are taken from the 386BSD patchkit 0.2.4 codrv
+#
+#---------------------------------------------------------------------------
+#
+# -hm patch from Thomas Gellekum
+# -hm renamed finnish "f8" entry to "f1"
+#
+#---------------------------------------------------------------------------
+
+df|default|default entry:\
+ :de:
+
+tt|test|Test entry which swaps y and z:\
+ :K22=z:S22=Z:C22=\032:\
+ :K46=y:S46=Y:C46=\031:
+
+# from codrv, untested
+be|belgium|Belgian mapping:\
+ :K1=\262:S1=\263:\
+ :K2=&:S2=1:A2=|:\
+ :K3=\351:S3=2:A3=@:\
+ :K4=":S4=3:A4=#:\
+ :K5=':S5=4:\
+ :K6=(:S6=5:\
+ :K7=\247:S7=6:A7=\136:\
+ :K8=\350:S8=7:\
+ :K9=!:S9=8:\
+ :K10=\347:S10=9:A10={:\
+ :K11=\340:S11=0:A11=}:\
+ :K12=):S12=\260:\
+ :K13=-:S13=_:\
+ :K17=a:S17=A:C17=^A:\
+ :K18=z:S18=Z:C18=^z:\
+ :K27=\136:S27=\250:A27=[:p1#27:\
+ :K28=$:S28=*:A28=]:\
+ :K31=q:S31=Q:C31=^q:\
+ :K40=m:S40=M:C40=^m:\
+ :K41=\371:S41=%:A41=':p2#41:\
+ :K42=\265:S42=\243:A42=\264:p3#42:\
+ :K45=<:S45=>:A45=\\:\
+ :K46=w:S46=W:C46=^w:\
+ :K52=,:S52=\077:\
+ :K53=;:S53=.:\
+ :K54=\072:S54=/:\
+ :K55==:S55=+:A55=~:\
+ :l1#62:a0:
+
+# from codrv, untested
+ca|canadafr|Canadian French mapping:\
+ :K1=#:S1=|:A1=\\:\
+ :A2=\261:S3=":A3=@:S4=/:A4=\243:A5=\242:\
+ :A6=\244:S7=\077:A7=\254:S8=&:A8=\246:S9=*:A9=\262:\
+ :S10=(:A10=\263:S11=):A11=\274:\
+ :K12=-:S12=_:A12=\275:\
+ :K13==:S13=+:A13=\276:\
+ :A25=\247:A26=\266:\
+ :K27=\136:S27=\136:A27=[:\
+ :K28=\270:S28=\250:A28=]:p1#28:\
+ :A40=~:K41=`:S41=`:A41={:\
+ :K42=<:S42=>:A42=}:\
+ :K45=\253:S45=\273:A45=\260:\
+ :A49=\253:A50=\273:A51=\260:\
+ :A52=\265:K53=,:S53=,:A53=-:\
+ :K54=\351:S54=\311:A54=':\
+ :l1#62:a0:
+
+# from codrv, untested
+c1|swissde|Swiss German mapping:\
+ :K1=\247:S1=\260:p1#1:\
+ :S2=+:A2=|:S3=":A3=@:S4=*:A4=#:S5=\347:S7=&:A7=\254:\
+ :S8=/:A8=\246:S9=(:A9=\242:S10=):S11==:\
+ :K12=`:S12=\077:A12=':p2#12:\
+ :K13=^:S13=\\:A13=~:p3#13:\
+ :K22=z:S22=Z:C22=\032:\
+ :S27=\350:K27=\374:A27=[:\
+ :K28=\250:S28=!:A28=]:\
+ :S40=\351:K40=\366:\
+ :S41=\340:K41=\344:A41={:\
+ :K42=$:S42=\243:A42=}:\
+ :K45=<:S45=>:A45=\\:\
+ :K46=y:S46=Y:C46=\031:
+ :S53=;:S54=\072:K55=-:S55=_:\
+ :l1#62:a0:
+
+# from codrv, untested
+c2|swissfr|Swiss French mapping:\
+ :K27=\350:S27=\374:A27=[:\
+ :K40=\351:S40=\366:\
+ :K41=\340:S41=\344:A41={:\
+ :tc=swissde:
+
+# more programmer-like than an original German kbd, you needn't
+# have gum-fingers to get `{}' and the like:-)
+# maps: ae -> [, oe -> \, ue -> ], Ae -> {, Oe -> |, Ue -> }
+# umlaute are available as AltGr- and Control-Mappings
+# also maps Pause -> ^Z
+#
+# (from Joerg Wunsch)
+#
+# l1/m1 bindings: left Alt is AltGr
+# Emacs functions:
+# C79/C89: ctrl-{leftarrow,rightarrow} {backward,forward} word
+# A79/A89: {backward,forward} sexp
+# C83/C84: ctrl-{uparrow,downarrow} {backward,forward} window
+#
+de-prog|germany-prog|programmer's mapping for german keyboard:\
+ :K27=]:S27=}:A27=\374:C27=\334:\
+ :K40=\\:S40=|:A40=\366:C40=\326:\
+ :K41=[:S41={:A41=\344:C41=\304:\
+ :K126=\032:C126=\003:\
+ :tc=de:
+# :l1#60:l2#62:\
+# :C79=^[B:K79=^[[D:S79=^[OD:A79=^[^B:\
+# :C89=^[F:K89=^[[C:S89=^[OC:A89=^[^F:\
+# :C83=^U-1^XO:K83=^[[A:S84=^[OA:\
+# :C84=^XO:K84=^[[B:S84=^[OB:\
+
+de|germany|German mapping for MF II-Keyboard:\
+ :K1=\136:S1=\260:\
+ :S3=\042:S4=\247:S7=&:S8=/:S9=(:S10=):S11=\075:\
+ :A3=\262:A4=\263:A8={:A9=[:A10=]:A11=}:A12=\134:\
+ :K12=\337:S12=?:C12=\034:\
+ :K13=':S13=`:\
+ :A17=\100:\
+ :K22=z:S22=Z:C22=\032:\
+ :K27=\374:S27=\334:\
+ :K28=+:S28=*:A28=\176:\
+ :K29=\043:S29=':A29=\174:\
+ :K40=\366:S40=\326:\
+ :K41=\344:S41=\304:\
+ :A45=\174:\
+ :K46=y:S46=Y:C46=\031:\
+ :A52=\265:\
+ :S53=;:S54=\072:\
+ :K55=-:S55=_:\
+ :l1#62:
+
+de-at|germany-at|German mapping for AT-Keyboard:\
+ :K1=<:S1=>:\
+ :S3=\042:\
+ :S4=\247:\
+ :S7=&:\
+ :S8=/:\
+ :S9=(:\
+ :S10=):\
+ :S11=\075:\
+ :K12=\337:S12=?:C12=\034:\
+ :K13=':S13=`:\
+ :K14=#:S14=\136:\
+ :K22=z:S22=Z:C22=\032:\
+ :K27=\374:S27=\334:\
+ :K28=+:S28=*:\
+ :K29=\043:S29=':\
+ :K40=\366:S40=\326:\
+ :K41=\344:S41=\304:\
+ :K46=y:S46=Y:C46=\031:\
+ :S53=;:S54=\072:\
+ :K55=-:S55=_:\
+
+# from codrv, untested
+# Includes improvements by Thomas Hiller (hiller@fzi.de)
+# and Andreas Israel (ai@hrz.tu-chemnitz.de)
+de-hi|germany-hiller|yet another German mapping:\
+ :K1=\136:S1=\260:C1=|:\
+ :S3=\042:S4=#:S7=&:S8=/:S9=(:S10=):S11=\075:\
+ :A8={:A9=[:A10=]:A11=}:A17=@:A28=~:\
+ :K12=\337:S12=\077:C12=\036:A12=\\:\
+ :K13=':S13=`:C13=\134:p1#13:\
+ :K22=z:S22=Z:C22=\032:\
+ :K27=\374:S27=\334:C27=\035:\
+ :K28=+:S28=*:C28=\000:\
+ :K29=<:S29=>:C29=\134:\
+ :K40=\366:S40=\326:C40=\034:\
+ :K41=\344:S41=\304:C41=\033:\
+ :K46=y:S46=Y:C46=\031:\
+ :S53=;:S54=\072:\
+ :K55=-:S55=_:\
+ :l1#62:a0:
+
+# from codrv, untested
+# Contribution by Thomas Hiller (hiller@fzi.de)
+# "K42 may not work on 102 keys kbds, K29 seems to work"
+d1|de-102|german with mf2:\
+ :K29=#:\
+ :K42=#:\
+ :K45=<:S45=>:A45=|:\
+ :tc=germany:
+
+# from codrv, untested
+hv|holgi|Holgi's special MF1 keyboard mapping:\
+ :K1=<:S1=>:C1=|:\
+ :K29=#:S29=\136:A29=\\:C29=~:\
+ :tc=germany:
+
+# from codrv, untested
+# Contributed by Andreas Israel (ai@hrz.tu-chemnitz.de)
+de-ai|nixmf2|ct22|nix|nix7|German Nixdorf MF2:\
+ :A28=~:\
+ :K29=#:S29=':\
+ :K45=<:S45=>:A45=|:\
+ :tc=germany:
+
+# from codrv, untested
+dk|denmark|Danish mapping:\
+ :K1=\275:S1=\247:\
+ :S3=":A3=@:\
+ :A4=\243:\
+ :S5=\244:A5=$:\
+ :S7=&:S8=/:A8={:S9=(:A9=[:S10=):A10=]:S11==:A11=}:\
+ :K12=+:S12=\077:\
+ :K13=':S13=`:A13=|:p1#13:\
+ :K27=\345:S27=\305:\
+ :K28=\250:S28=\136:A28=~:p2#28:\
+ :K40=\346:S40=\306:\
+ :K41=\370:S41=\330:\
+ :K42=:S42=*:\
+ :K45=<:S45=>:A45=\\:\
+ :S53=;:S54=\072:\
+ :K55=-:S55=_:\
+ :l1#62:a0:
+
+# from codrv, untested
+# Finnish keyboard map with 7-bit versions of the national
+# chars. The Latin1 chars are available with Alt-7, Alt-8, etc
+# (where normally you would have the 7-bit ones).
+# Makes C/C++ programming more comfortable, since the 7-bit chars
+# (|\{[}]) are needed much more often than the Latin1 chars.
+# -- Petri.Wessman@hut.fi
+fi|finland|finland7b|finland-ascii|Finnish ASCII mapping:\
+ :l1#60:l2#62:\
+ :A8=\344:A9=\304:A10=\305:A11=\345:A12=\326:A13=\366:\
+ :K40=|:S40=\\:K41={:S41=[:K27=}:S27=]:\
+ :S1=\275:K1=\247:\
+ :S3=":A3=@:\
+ :A4=\243:\
+ :S5=$:A5=$:\
+ :S7=&:S8=/:S9=(:S10=):S11==:\
+ :K12=+:S12=\077:\
+ :K13=':S13=`:\
+ :K28=~:S28=\136:A28=~:\
+ :K29=':S29=*:\
+ :K45=<:S45=>:A45=|:\
+ :S53=;:S54=\072:\
+ :K55=-:S55=_:\
+ :a0:
+
+# from codrv, untested
+# Finnish keyboard map with the Latin1 national chars in
+# their "right" place. --Petri.Wessman@hut.fi
+f1|finland8b|finland-latin1|Finnish Latin1 mapping:\
+ :A8={:A9=[:A10=]:A11=}:A12=\\:\
+ :K40=\366:S40=\326:K41=\344:S41=\304:K27=\345:S27=\305:\
+ :tc=finland:
+
+
+# French keyboard mapping
+# From Matthieu Herrb <matthieu@laas.fr>
+# For 102 keys keyboards, produces 8 bits characters
+# with ISO Latin-1 encoding
+f8|france-iso-8859-1|French ISO 8859-1 102 keys keyboard:\
+ :l1#62:\
+ :K1=\262:S1=:\
+ :K2=&:S2=1:\
+ :K3=\351:S3=2:C3=\211:A3=~:\
+ :K4=":S4=3:A4=#:\
+ :K5=':S5=4:A5={:\
+ :K6=(:S6=5:A6=[:\
+ :K7=-:S7=6:C7=\036:A7=|:\
+ :K8=\350:S8=7:C8=\210:A8=`:\
+ :K9=_:S9=8:C9=\037:A9=\\:\
+ :K10=\347:S10=9:C10=\207:A10=\136:\
+ :K11=\340:S11=0:C11=\340:A11=@:\
+ :K12=):S12=\260:A12=]:\
+ :A13=}:\
+ :K17=a:S17=A:C17=\001:\
+ :K18=z:S18=Z:C18=\032:\
+ :D27:\
+ :K28=$:S28=\243:\
+ :K29=*:S29=\265:\
+ :K31=q:S31=Q:C31=\021:\
+ :K40=m:S40=M:C40=\015;\
+ :K41=\371:C41=\231:S41=%:\
+ :K42=*:S42=\265:\
+ :K46=w:S46=W:C46=\027:\
+ :K52=,:S52=?:\
+ :K53=;:S53=.:\
+ :K54=\072:S54=/:C54=\037\
+ :K55=!:S55=\266:
+
+# fr|france|French mapping:\
+# :de:
+#
+# from codrv, untested
+# f1|france120|French 120 mapping:\
+# :tc=belgium:
+#
+#
+# from codrv, untested
+#f2|france189|French 189 mapping:\
+# :S1=:A1=':p3#1:\
+# :A2=:A3=~:A5={:A6=[:A7=|:A8=`:\
+# :S9=_:A9=\\:A10=\136:A11=@:A12=]:A13=}:\
+# :A27=:A28=\244:A41=:A42=:\
+# :A45=:!:S45=\247:\
+# :tc=belgium:
+
+# From: Andy Duplain, duplain@rtf.bt.co.uk
+gb|greatbritain|British mapping for MF-2 keyboard:\
+ :S1=|:S3=":C3=2:C12=-:S41=@:K42=#:S42=~:C42=#:K45=\\:S45=|:C45=\\:
+
+# from codrv, untested
+# This entry has been corrected by Mike Trim (mtrim@crucible.demon.co.uk)
+# (hv's comment: For the keys # and ~ you might also check the following
+# line
+# :K42=#:S42=~:\
+# Also I think I was wrong with the ALTGR key. If you need one, add this:
+# :l1#62:
+g1|greatbritain166|British 166 mapping:\
+ :K1=`:S1=\254:A1=|:\
+ :S3=":S4=\243:\
+ :K41=':S41=@:\
+ :K29=#:S29=~:\
+ :K45=\\:S45=|:
+
+# from codrv, untested
+g2|greatbritain168|British 168 mapping:\
+ :K1=\\:S1=|:\
+ :S3=":S4=\243:S7=&:S8=:S9=(:S10=):A10=\261:\
+ :S11=#:A11=\260:S12=:K13=\254:S13=-:\
+ :K27=@:S27=`:\
+ :K28=[:S28={:A28=~:\
+ :K40=;:S40=+:\
+ :K41=\072:S41=*:A41=\136:\
+ :K42=]:S42=}:\
+ :K45=|:S45=_:\
+ :A52=\265:\
+ :l1#62:a0:
+
+# from codrv, untested
+is:iceland:Island mapping:\
+ :K1=\260:S1=\250:p1#1:\
+ :S2=":S7=&:S8=/:A8={:S9=(:A9=[:S10=):A10=]:S11==:A11=}:\
+ :K12=\\:S12=\326:\
+ :K13=':S13=`:A13=|:\
+ :A17=@:K27=\360:S27=\320:\
+ :K28=':S28=\077:A28=~:\
+ :K40=\346:S40=\306:\
+ :K41=':S41=':A41=\136:p2#41:\
+ :K42=+:S42=*:A42=`:p3#42:\
+ :K45=<:S45=>:A45=|:\
+ :S53=;:S54=\072:\
+ :K54=\376:S54=\336:\
+ :K104=,:104=,:\
+ :l1#62:a0:
+
+# from codrv, untested
+i1|italy141|Italian 141 mapping:\
+ :K1=\\:S1=|:\
+ :S3=":S4=\243:S7=&:S8=/:S9=(:S10=):S11==:\
+ :K12=':S12=\077:\
+ :K13=\354:S13=\136:\
+ :K27=\350:S27=\351:\A27=[:\
+ :K28=+:S28=*:A28=]:\
+ :K40=\362:S40=\347:A40=@:\
+ :K41=\340:S41=\260:A41=#:\
+ :K42=\371:S42=\247:\
+ :K45=<:S45=>:\
+ :S53=;:S54=\072:\
+ :K55=-:S55=_:\
+ :l1#62:a0:
+
+# from codrv, untested
+i2|italy142|Italian 142 mapping:\
+ :A8={:A9=[:A10=]:A11=}:\
+ :A17=@:A27=:A28=~:A40=:A41=:\
+ :tc=italy141:a0:
+
+# from codrv, untested
+nl|netherlands|Dutch mapping:\
+ :K1=@:S1=\247:A1=\254:\
+ :A2=\271:S3=":A3=\262:S4=#:A4=\263:A5=\274:A6=\275:S7=&:\
+ :A7=\276:S8=_:A8=\243:S9=(:A9={:S10=):A10=}:S11=':\
+ :K12=/:S12=\077:A12=\\:\
+ :K13=\260:S13=~:A13=\270:p1#13:\
+ :K20=\266:K27=\250:S27=^:p2#27:\
+ :K28=*:S28=|:\
+ :K40=+:S40=\261:\
+ :K41=':S41=`:\
+ :K42=<:S42=>:\
+ :K45=[:S45=]:A45=|:\
+ :S53=;:S54=\072:K55=-:S55=_:\
+ :K104=,:S104=,:\
+ :l1#62:a0:
+
+# from codrv, untested
+no|norway|Norwegian mapping:\
+ :K1=|:\
+ :K13=\\:S13=`:A13=':p1#13:\
+ :K41=\346:S41=\306:\
+ :K40=\370:S40=\330:\
+ :A45=:\
+ :tc=denmark:
+
+# from codrv, untested
+pt|portugal|Portugesian mapping:\
+ :K1=\\:S1=|:\
+ :S3=":A3=@:A4=\243:A5=\247:S7=&:S8=/:A8={:S9=(:A9=[:\
+ :S10=):A10=]:S11=}:A11==:\
+ :K12=':S12=\077:\
+ :K13=\253:S13=\273:\
+ :K40=\347:S40=\307:\
+ :K41=\272:S41=\252:\
+ :K42=~:S42=^:p1#42:\
+ :K45=<:S45=>:\
+ :S53=;:S54=\072:K55=-:S55=_:\
+ :l1#62:a0:
+
+# from codrv, untested
+es|spain|Spainish mapping:\
+ :K1=\272:S1=\252:A1=\\:\
+ :A2=|:S3=":A3=@:S4=:A4=#:S7=&:A7=\254:S8=/:S9=(:S10=):S11==:\
+ :K12=':S12=\077:p1#12:\
+ :K13=\277:S13=\241:\
+ :K27=`:S27=^:A27=[:p2#27:\
+ :K28=+:S28=*:A28=]:\
+ :K40=\361:S41=\321:\
+ :K41=/:S41=\250:A41={:p3#41:\
+ :K42=\347:S42=\307:A42=}:\
+ :K45=<:S45=>:\
+ :S53=;:S54=\072:\
+ :K55=-:S55=_:\
+ :K104=,:S104=,:\
+ :l1#62:a0:
+
+# from codrv, untested
+# Contributed by Mats O Jansson, moj@stacken.kth.se
+# "Here is my map, rather large, but i want ALTGR normaly to be dead.
+# Only seven bit national characters have been placed where national characters
+# is on the keyboard."
+# We can help this man, just use the 'a0' capability -hv-
+s1|sweden1|Swedish mapping:\
+ :l1#62:a0:\
+ :D1:\
+ :K12=+:K13=':K29=':\
+ :S2=!:S3=":S4=#:S5=$:S6=%:S7=&:S8=/:\
+ :S9=(:S10=):S11=\075:S12=\077:S13=`:S29=*:\
+ :C2=:C3=\000:C4=\033:C5=\034:C6=\035:C7=:C8=:C9=:\
+ :C10=:C11=:C12=:C13=:C29=:\
+ :A3=\100:A5=$:A8={:\
+ :A9=[:A10=]:A11=}:A12=\134:\
+ :K27=]:K28=:\
+ :S27=}:S28=\136:\
+ :C27=\035:C28=:\
+ :A28=\176:\
+ :K40=\134:K41=[:\
+ :S40=|:S41={:\
+ :C40=\034:C41=\033:\
+ :K45=<:K53=,:K54=.:K55=-:\
+ :S45=>:S53=;:S54=\072:S55=_:\
+ :C45=\034:C53=:C54=:C55=:\
+ :A45=|:\
+ :C61=\000:
+
+# from codrv, untested
+# Contributed by Mats O Jansson, moj@stacken.kth.se
+# "Here is my map, rather large, but i want ALTGR normaly to be dead.
+# Only seven bit national characters have been placed where national
+# characters is on the keyboard."
+# We can help this man, just use the 'a0' capability -hv-
+# Corrected by Paul Pries, 5322@msg.abc.se (Some national shifts were
+# wrong).
+sa|sweden1a|Swedish 7bit mapping ISO 646:\
+ :l1#62:a0:\
+ :D1:\
+ :K12=+:K13=':K29=':\
+ :S2=!:S3=":S4=#:S5=$:S6=%:S7=&:S8=/:\
+ :S9=(:S10=):S11=\075:S12=\077:S13=`:S29=*:\
+ :C2=:C3=\000:C4=\033:C5=\034:C6=\035:C7=:C8=:C9=:\
+ :C10=:C11=:C12=:C13=:C29=:\
+ :A3=\100:A5=$:A8={:\
+ :A9=[:A10=]:A11=}:A12=\134:\
+ :K27=}:K28=:\
+ :S27=]:S28=\136:\
+ :C27=\035:C28=:\
+ :A28=\176:\
+ :S40=\134:S41=[:\
+ :K40=|:K41={:\
+ :C40=\034:C41=\033:\
+ :K45=<:K53=,:K54=.:K55=-:\
+ :S45=>:S53=;:S54=\072:S55=_:\
+ :C45=\034:C53=:C54=:C55=:\
+ :A45=|:\
+ :C61=\000:
+
+# from codrv, untested
+# Swedish keyboard map with national characters.
+# Paul Pries, 5322@msg.abc.se
+s2|sweden2|Swedish 8bit mapping ISO 8859-1:\
+ :l1#62:\
+ :K1=\247:S1=:\
+ :K12=+:K13=':K29=':\
+ :S2=!:S3=":S4=#:S5=$:S6=%:S7=&:S8=/:\
+ :S9=(:S10=):S11=\075:S12=\077:S13=`:S29=*:\
+ :C2=:C3=\000:C4=\033:C5=\034:C6=\035:C7=:C8=:C9=:\
+ :C10=:C11=:C12=:C13=:C29=:\
+ :A3=\100:A4=\234:A5=$:A8={:\
+ :A9=[:A10=]:A11=}:A12=\134:\
+ :K27=\206:K28=:\
+ :S27=\217:S28=\136:\
+ :C27=\035:C28=:\
+ :A28=\176:\
+ :S40=\231:S41=\216:\
+ :K40=\224:K41=\204:\
+ :C40=\034:C41=\033:\
+ :K45=<:K53=,:K54=.:K55=-:\
+ :S45=>:S53=;:S54=\072:S55=_:\
+ :C45=\034:C53=:C54=:C55=:\
+ :A45=|:\
+ :C61=\000:
+
+#
+# tg: my idiosyncratic mappings (thomas@ghpc8.ihf.rwth-aachen.de)
+#
+# the six function keys above the cursor keys are arranged
+# identical to a real VT220:
+#
+# find insert remove
+# select up down
+#
+# since i don't have a use for the numbers on the keypad,
+# i map NumLock, /, *, - to PF1-PF4;
+# + is mapped to SS3 l, shifted + is mapped to SS3 m
+#
+# they convinced me finally to add some support for german umlauts.
+# so, i stole the mapping from jörg wunsch's de-prog entry.
+#
+# tg
+#
+
+tg:\
+ :l1#62:\
+ :A12=\337:\
+ :A27=\374:C27=\334:\
+ :A40=\366:C40=\326:\
+ :A41=\344:C41=\304:\
+ :K126=\032:C126=\003:\
+ :K75=[1~:S75=[1~:C75=[1~:\
+ :K76=[4~:S76=[4~:C76=[4~:\
+ :K80=[2~:S80=[2~:C80=[2~:\
+ :K81=[5~:S81=[5~:C81=[5~:\
+ :K85=[3~:S85=[3~:C85=[3~:\
+ :K86=[6~:S86=[6~:C86=[6~:\
+ :K90=OP:S90=OP:C90=OP:\
+ :K95=OQ:S95=OQ:C95=OQ:\
+ :K100=OR:S100=OR:C100=OR:\
+ :K104=On:S104=On:C104=On:\
+ :K105=OS:S105=OS:C105=OS:\
+ :K106=Ol:S106=Om:\
+ :K108=OM:S108=OM:C108=OM:
+
+us|usa|United States mapping:\
+ :de:
+
+# EOF
diff --git a/usr.sbin/pcvt/keycap/man5/keycap.5 b/usr.sbin/pcvt/keycap/man5/keycap.5
new file mode 100644
index 0000000..b397720
--- /dev/null
+++ b/usr.sbin/pcvt/keycap/man5/keycap.5
@@ -0,0 +1,130 @@
+.\"
+.\" Copyright (c) 1992,1993,1994 Hellmuth Michaelis
+.\"
+.\" Copyright (c) 1990 The Regents of the University of California.
+.\"
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)keycap.5, 3.00, Last Edit-Date: [Sun Jan 2 13:45:59 1994]
+.\" $Id$
+.\"
+.Dd January 3, 1993
+.Dt KEYCAP 5
+.Sh NAME
+.Nm keycap
+.Nd keyboard mapping data base
+.Sh SYNOPSIS
+.Nm keycap
+.Sh DESCRIPTION
+The
+.Nm keycap
+file
+is a data base describing keyboard mappings, used by
+.Xr kcon 1 .
+.Pp
+Entries in
+.Nm keycap
+consist of a number of `:'-separated fields.
+The first entry for each mapping gives the names that are known for the
+mapping, separated by `|' characters.
+All names but the first and last
+should be in lower case and contain no blanks;
+the last name may well contain
+upper case and blanks for readability.
+.Sh CAPABILITIES
+.Pp
+.Bl -column indent indent
+.Sy Name Type Description
+.It "de bool Resets Keyboard mapping to compiled-in default"
+.It "D<n> bool Disables key <n> completely"
+
+.It "m<n> num specify key numbers for ALT keys
+.It "l<n> num specify key numbers for ALTGR keys
+.It "h<n> num specify key numbers for SHIFT keys
+.It "t<n> num specify key numbers for CONTROL keys
+.It "ca<n> num specify key number for the CAPS LOCK key
+.It "sh<n> num specify key number for the SHIFT LOCK key
+.It "nl<n> num specify key number for the NUM LOCK key
+.It "sc<n> num specify key number for the SCROLL LOCK key
+
+.It "K<n> str bind a string to a unshifted (normal) key
+.It "S<n> str bind a string to a shifted key
+.It "C<n> str bind a string to a control key
+.It "A<n> str bind a string to a altgr key
+
+.It "tc str Entry of similar map \- must be last."
+.El
+
+Parameter <n> describing the key number can have values from 1 to 128.
+
+A string parameter may have up to 15 characters.
+
+.Pp
+.Ss A Sample Entry
+The following entry, which describes a test entry, is among the very
+easy entries in the
+.Nm keycap
+file as of this writing.
+.Pp
+.Bd -literal
+tt\||test\||Test entry which swaps y and z:\e
+ :K22=z:S22=Z:C22=\e032:\e
+ :K46=y:S46=Y:C46=\e031:
+
+.Ed
+.Pp
+Entries may continue onto multiple lines by giving a \e as the last
+character of a line. Comments may be included on lines beginning with
+.Dq # .
+.Sh FILES
+.Bl -tag -width /usr/share/misc/keycap.pcvt -compact
+.It Pa /usr/share/misc/keycap.pcvt
+File containing keyboard mapping descriptions.
+.El
+.Sh SEE ALSO
+.Xr kcon 1 ,
+.Xr keycap 3
+.Sh EXAMPLES
+The entry
+.Dq Li l1#60
+sets the keynumber for the ALTGR key to 60.
+
+The entry
+.Dq Li K100=hugo
+binds the string 'hugo' to the key number 100.
+
+The entry
+.Dq Li K100=^D
+binds the control character EOT (0x04) to the key number 100.
+
+The entry
+.Dq Li K100=\e000
+binds the control character NUL (0x00) to the key number 100.
diff --git a/usr.sbin/pcvt/loadfont/Makefile b/usr.sbin/pcvt/loadfont/Makefile
new file mode 100644
index 0000000..c0f454f
--- /dev/null
+++ b/usr.sbin/pcvt/loadfont/Makefile
@@ -0,0 +1,3 @@
+PROG= loadfont
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/pcvt/loadfont/loadfont.1 b/usr.sbin/pcvt/loadfont/loadfont.1
new file mode 100644
index 0000000..64fd3de
--- /dev/null
+++ b/usr.sbin/pcvt/loadfont/loadfont.1
@@ -0,0 +1,90 @@
+.\" Copyright (c) 1992, 1995 Hellmuth Michaelis
+.\"
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by Hellmuth Michaelis
+.\" 4. The name authors may not be used to endorse or promote products
+.\" derived from this software without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+.\" IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+.\"
+.\" @(#)loadfont.1, 3.20, Last Edit-Date: [Tue Apr 4 13:06:00 1995]
+.\"
+.Dd April 4, 1995
+.Dt LOADFONT 1
+.Sh NAME
+.Nm loadfont
+.Nd is used to load fonts into EGA or VGA boards for use by the 'pcvt' video
+driver.
+.Sh SYNOPSIS
+.Nm loadfont
+.Op Fl c Ar charsetno
+.Op Fl d Ar devicefile
+.Op Fl f Ar fontfilename
+.Op Fl i
+.Sh DESCRIPTION
+The
+.Nm loadfont
+utility is used to load fonts needed for proper operation of the pcvt
+VT220 driver on EGA and VGA boards into the font ram of this boards.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl c
+Specifies the slot, the font is to load into. EGA boards have four
+slots and VGA boards have eight slots available for downloading fonts.
+.It Fl d
+Specifies the devicefile to use.
+.It Fl f
+Specifies the file which contains the font to be downloaded.
+.It Fl i
+Gives information what type(s) of font do currently reside in which slot.
+This is also the default behaviour if no options are specified on the commandline.
+.El
+.Pp
+This utility is used only on EGA and VGA boards, as MDA, HCG and CGA boards
+do not have downloadable charactersets available.
+.Sh FILES
+The following fontfiles are available in the pcvt distribution:
+
+.nf
+/usr/share/misc/pcvtfonts/vt220l.808: 8x8 IBM II font
+/usr/share/misc/pcvtfonts/vt220h.808: 8x8 Extension font
+/usr/share/misc/pcvtfonts/vt220l.810: 8x10 IBM II font
+/usr/share/misc/pcvtfonts/vt220h.810: 8x10 Extension font
+/usr/share/misc/pcvtfonts/vt220l.814: 8x14 IBM II font
+/usr/share/misc/pcvtfonts/vt220h.814: 8x14 Extension font
+/usr/share/misc/pcvtfonts/vt220l.816: 8x16 IBM II font
+/usr/share/misc/pcvtfonts/vt220h.816: 8x16 Extension font
+.fi
+.Sh EXAMPLES
+The command
+.Dq Li loadfont -c0 -f/usr/share/misc/pcvtfonts/vt220l.816
+loads a 8x16 font containing the standard IBM characterset II into font slot
+0 on a VGA or EGA board.
+.Sh BUGS
+No known bugs
+.Sh SEE ALSO
+.Xr cursor 1 ,
+.Xr scon 1 ,
+.Xr pcvt 4 ,
+.Xr ispcvt 8
diff --git a/usr.sbin/pcvt/loadfont/loadfont.c b/usr.sbin/pcvt/loadfont/loadfont.c
new file mode 100644
index 0000000..ce4abc3
--- /dev/null
+++ b/usr.sbin/pcvt/loadfont/loadfont.c
@@ -0,0 +1,345 @@
+/*
+ * Copyright (c) 1992, 1995 Hellmuth Michaelis
+ *
+ * Copyright (c) 1992, 1994 Brian Dunford-Shore
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by
+ * Hellmuth Michaelis and Brian Dunford-Shore
+ * 4. The name authors may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+static char *id =
+ "@(#)loadfont.c, 3.20, Last Edit-Date: [Fri Apr 7 10:13:16 1995]";
+
+/*---------------------------------------------------------------------------*
+ *
+ * load a font into vga character font memory
+ *
+ * -hm removing explicit HGC support (same as MDA ..)
+ * -hm new pcvt_ioctl.h SIZ_xxROWS
+ * -hm add -d option
+ *
+ *---------------------------------------------------------------------------*/
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <machine/pcvt_ioctl.h>
+
+#define FONT8X8 2048 /* filesize for 8x8 font */
+#define HEIGHT8X8 8 /* 8 scan lines char cell height */
+#define SSCAN8X8 143 /* 400 scan lines on screen - 256 - 1 */
+
+#define FONT8X10 2560 /* filesize for 8x10 font */
+#define HEIGHT8X10 10 /* 10 scan lines char cell height */
+#define SSCAN8X10 143 /* 400 scan lines on screen - 256 - 1 */
+
+#define FONT8X14 3584 /* filesize for 8x14 font */
+#define HEIGHT8X14 14 /* 14 scan lines char cell height */
+#define SSCAN8X14 135 /* 392 scan lines on screen - 256 - 1 */
+
+#define FONT8X16 4096 /* filesize for 8x16 font */
+#define HEIGHT8X16 16 /* 16 scan lines char cell height */
+#define SSCAN8X16 143 /* 400 scan lines on screen - 256 - 1 */
+
+struct screeninfo screeninfo;
+
+#define DEFAULTFD 0
+int fd;
+
+
+main(argc,argv)
+int argc;
+char *argv[];
+{
+ extern int optind;
+ extern int opterr;
+ extern char *optarg;
+
+ FILE *in;
+ struct stat sbuf, *sbp;
+ unsigned char *fonttab;
+ int ret;
+ int chr_height;
+ int scr_scan;
+ int scr_rows;
+ int c;
+ int chr_set = -1;
+ char *filename;
+ int fflag = -1;
+ int info = -1;
+ int dflag = 0;
+ char *device;
+
+ while( (c = getopt(argc, argv, "c:d:f:i")) != -1)
+ {
+ switch(c)
+ {
+ case 'c':
+ chr_set = atoi(optarg);
+ break;
+
+ case 'd':
+ device = optarg;
+ dflag = 1;
+ break;
+
+ case 'f':
+ filename = optarg;
+ fflag = 1;
+ break;
+
+ case 'i':
+ info = 1;
+ break;
+
+ case '?':
+ default:
+ usage();
+ break;
+ }
+ }
+
+ if(chr_set == -1 || fflag == -1)
+ info = 1;
+
+ if(dflag)
+ {
+ if((fd = open(device, O_RDWR)) == -1)
+ {
+ char buffer[80];
+ strcpy(buffer,"ERROR opening ");
+ strcat(buffer,device);
+ perror(buffer);
+ exit(1);
+ }
+ }
+ else
+ {
+ fd = DEFAULTFD;
+ }
+
+ if(info == 1)
+ {
+ int i;
+
+ if(ioctl(fd, VGAGETSCREEN, &screeninfo) == -1)
+ {
+ perror("ioctl VGAGETSCREEN failed");
+ exit(1);
+ }
+
+ switch(screeninfo.adaptor_type)
+ {
+ case UNKNOWN_ADAPTOR:
+ case MDA_ADAPTOR:
+ case CGA_ADAPTOR:
+ printf("Adaptor does not support Downloadable Fonts!\n");
+ break;
+ case EGA_ADAPTOR:
+ printheader();
+ for(i = 0;i < 4;i++)
+ {
+ printvgafontattr(i);
+ }
+ break;
+ case VGA_ADAPTOR:
+ printheader();
+ for(i = 0;i < 8;i++)
+ {
+ printvgafontattr(i);
+ }
+ }
+ printf("\n");
+ exit(0);
+ }
+
+ if(chr_set < 0 || chr_set > 7)
+ usage();
+
+ sbp = &sbuf;
+
+ if((in = fopen(filename, "r")) == NULL)
+ {
+ char buffer[80];
+ sprintf(buffer, "cannot open file %s for reading", filename);
+ perror(buffer);
+ exit(1);
+ }
+
+ if((fstat(fileno(in), sbp)) != 0)
+ {
+ char buffer[80];
+ sprintf(buffer, "cannot fstat file %s", filename);
+ perror(buffer);
+ exit(1);
+ }
+
+ switch(sbp->st_size)
+ {
+ case FONT8X8:
+ chr_height = HEIGHT8X8;
+ scr_scan = SSCAN8X8;
+ scr_rows = SIZ_50ROWS;
+ break;
+
+ case FONT8X10:
+ chr_height = HEIGHT8X10;
+ scr_scan = SSCAN8X10;
+ scr_rows = SIZ_40ROWS;
+ break;
+
+ case FONT8X14:
+ chr_height = HEIGHT8X14;
+ scr_scan = SSCAN8X14;
+ scr_rows = SIZ_28ROWS;
+ break;
+
+ case FONT8X16:
+ chr_height = HEIGHT8X16;
+ scr_scan = SSCAN8X16;
+ scr_rows = SIZ_25ROWS;
+ break;
+
+ default:
+ fprintf(stderr,"error, file %s is no valid font file, size=%d\n",argv[1],sbp->st_size);
+ exit(1);
+ }
+
+ if((fonttab = (unsigned char *)malloc((size_t)sbp->st_size)) == NULL)
+ {
+ fprintf(stderr,"error, malloc failed\n");
+ exit(1);
+ }
+
+ if((ret = fread(fonttab, sizeof(*fonttab), sbp->st_size, in)) != sbp->st_size)
+ {
+ fprintf(stderr,"error reading file %s, size = %d, read = is no valid font file, size=%d\n",argv[1],sbp->st_size, ret);
+ exit(1);
+ }
+
+ loadfont(chr_set, chr_height, fonttab);
+ setfont(chr_set, 1, chr_height - 1, scr_scan, scr_rows);
+
+ exit(0);
+}
+
+setfont(charset, fontloaded, charscan, scrscan, scrrow)
+int charset, fontloaded, charscan, scrscan, scrrow;
+{
+ struct vgafontattr vfattr;
+
+ vfattr.character_set = charset;
+ vfattr.font_loaded = fontloaded;
+ vfattr.character_scanlines = charscan;
+ vfattr.screen_scanlines = scrscan;
+ vfattr.screen_size = scrrow;
+
+ if(ioctl(fd, VGASETFONTATTR, &vfattr) == -1)
+ {
+ perror("loadfont - ioctl VGASETFONTATTR failed, error");
+ exit(1);
+ }
+}
+
+loadfont(fontset,charscanlines,font_table)
+int fontset;
+int charscanlines;
+unsigned char *font_table;
+{
+ int i, j;
+ struct vgaloadchar vlc;
+
+ vlc.character_set = fontset;
+ vlc.character_scanlines = charscanlines;
+
+ for(i = 0; i < 256; i++)
+ {
+ vlc.character = i;
+ for (j = 0; j < charscanlines; j++)
+ {
+ vlc.char_table[j] = font_table[j];
+ }
+ font_table += charscanlines;
+ if(ioctl(fd, VGALOADCHAR, &vlc) == -1)
+ {
+ perror("loadfont - ioctl VGALOADCHAR failed, error");
+ exit(1);
+ }
+ }
+}
+
+printvgafontattr(charset)
+int charset;
+{
+ struct vgafontattr vfattr;
+ static int sizetab[] = { 25, 28, 35, 40, 43, 50 };
+
+ vfattr.character_set = charset;
+
+ if(ioctl(fd, VGAGETFONTATTR, &vfattr) == -1)
+ {
+ perror("loadfont - ioctl VGAGETFONTATTR failed, error");
+ exit(1);
+ }
+ printf(" %d ",charset);
+ if(vfattr.font_loaded)
+ {
+
+ printf("Loaded ");
+ printf(" %2.2d ", sizetab[vfattr.screen_size]);
+ printf(" %2.2d ",
+ (((int)vfattr.character_scanlines) & 0x1f) + 1);
+ printf(" %3.3d",
+ ((int)vfattr.screen_scanlines+0x101));
+ }
+ else
+ {
+ printf("Empty");
+ }
+ printf("\n");
+}
+
+printheader()
+{
+ printf("\nEGA/VGA Charactersets Status Info:\n\n");
+ printf("Set Status Lines CharScanLines ScreenScanLines\n");
+ printf("--- ------ ----- ------------- ---------------\n");
+}
+
+usage()
+{
+ fprintf(stderr,"\nloadfont - load font into ega/vga font ram for pcvt video driver\n");
+ fprintf(stderr,"usage: loadfont -c <cset> -d <dev> -f <name> -i\n");
+ fprintf(stderr," -c <cset> characterset to load (ega 0..3, vga 0..7)\n");
+ fprintf(stderr," -d <dev> specify device\n");
+ fprintf(stderr," -f <name> filename containing binary font data\n");
+ fprintf(stderr," -i print status and types of loaded fonts (default)\n");
+ exit(1);
+}
diff --git a/usr.sbin/pcvt/mcon/Makefile b/usr.sbin/pcvt/mcon/Makefile
new file mode 100644
index 0000000..1a3b934
--- /dev/null
+++ b/usr.sbin/pcvt/mcon/Makefile
@@ -0,0 +1,3 @@
+PROG= mcon
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/pcvt/mcon/mcon.1 b/usr.sbin/pcvt/mcon/mcon.1
new file mode 100644
index 0000000..800d5ea
--- /dev/null
+++ b/usr.sbin/pcvt/mcon/mcon.1
@@ -0,0 +1,166 @@
+.\" Copyright (c) 1994 Joerg Wunsch
+.\"
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by Joerg Wunsch
+.\" 4. The name authors may not be used to endorse or promote products
+.\" derived from this software without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+.\" IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+.\"
+.\" @(#)mcon.1, 3.00, Last Edit-Date: [Mon Jan 10 21:28:22 1994]
+.\"
+.Dd January 3, 1994
+.Dt MCON 1
+.Sh NAME
+.Nm mcon
+.Nd controls pcvt mouse emulator
+.Sh SYNOPSIS
+.Nm mcon
+.Op Fl l Ar left-button-key
+.Op Fl m Ar mid-button-key
+.Op Fl r Ar right-button-key
+.Op Fl a Ar accel-time
+.Op Fl s Ar 0 | false | \&no
+.Op Fl s Ar 1 | true | yes
+.Ar device
+.Sh DESCRIPTION
+The
+.Nm mcon
+utility controls the configurable parameters for the mouse emulator of
+.Xr pcvt 4 .
+.br
+.Em NB :
+The mouse emulator is not configured in by default; the system's config
+file needs to specify an option line
+
+.Em options Dq PCVT_EMU_MOUSE
+
+in order to get its functionality.
+.Pp
+Either way, the
+.Nm
+program must be called with an argument
+.Ar device
+that specifies the device node used for the mouse emulation. This is
+usually the first device node of the
+.Xr pcvt 4
+driver not being used as a virtual terminal device. E.\ g., if you
+have configured eight virtual terminals
+.Pq the default value ,
+named
+.Pa /dev/ttyv0
+through
+.Pa /dev/ttyv7 ,
+the mouse emulator would allocate
+.Pa /dev/ttyv8 .
+
+If
+.Nm
+is called without any option, it will print the actual values of the
+configurable parameters.
+
+If called with an option, the program attempts to set up the new value.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl l Ar left-button-key
+.It Fl m Ar mid-button-key
+.It Fl r Ar right-button-key
+Maps the named
+.Ar button key
+to emulate either the left, middle, or right mouse button.
+.Ar Button key
+is the usual name for that key. Normal ASCII keys are denoted by the
+character they're labeled with, function keys are named
+.Em f1
+through
+.Em f10 .
+Note that the AT function keys
+.Em f11
+and
+.Em f12
+are
+.Em extended
+keys that cannot be mapped to be used with the mouse emulator since
+it only allows basic PC-scancode keys to be used.
+
+.It Fl a Ar accel-time
+Set the time limit for the internal accelerator to
+.Ar accel-time
+milliseconds. Key events occurring after a longer time than this limit
+will move the mouse cursor in single steps. Key events arriving more
+frequently will move the cursor accelerated by a factor of 6. Note that
+despite of
+.Em milliseconds
+being the unit of choice here, the time resolution is restricted by the
+timer tick distance of the underlying operating system, usually to a
+granularity of 10 milliseconds.
+
+.It Fl s Ar 0 | false | \&no
+.It Fl s Ar 1 | true | yes
+The first form disables, the second form enables the
+.Em sticky
+behaviour of the mouse buttons. Sticky mouse keys behave much like
+toggle-buttons: on first press, they become active, on second press,
+they're deactivated. Pressing another button will deactivate any
+other sticky button anyway.
+
+Sticky buttons might be more convenient since you don't need 20 fingers
+at all; on the other hand, they make it virtually impossible to initiate
+double or triple mouse clicks.
+.El
+.Sh EXAMPLES
+The following example would install the default behaviour of the
+mouse emulator:
+
+.Nm mcon
+.Fl l Ar f1
+.Fl m Ar f2
+.Fl r Ar f3
+.Fl a Ar 250
+.Fl s Ar \&no
+.Pa /dev/ttyv8
+.Sh BUGS
+The key names used to map the button-emulating keys to scan codes
+.Pq and vica verse
+are based on the American keyboard layout. This would usually not
+cause any trouble since the
+.Dq button-of-choice
+is certainly some function key that should be equal for any national
+keyboard layout.
+.Pp
+The mouse emulator is a rude hack at all; its only purpose is to provide
+a device to move the pointer within an X-windowing environment.
+.Sh SEE ALSO
+.Xr X 1 ,
+.Xr pcvt 4
+.Sh HISTORY
+The
+.Nm
+utility appeared in
+.Xr pcvt 4 ,
+release 3.00.
+.Sh AUTHOR
+The mouse emulator has been contributed by
+.if n Joerg Wunsch.
+.if t J\(:org Wunsch.
diff --git a/usr.sbin/pcvt/mcon/mcon.c b/usr.sbin/pcvt/mcon/mcon.c
new file mode 100644
index 0000000..db39944
--- /dev/null
+++ b/usr.sbin/pcvt/mcon/mcon.c
@@ -0,0 +1,193 @@
+/*
+ * Copyright (c) 1994 Joerg Wunsch
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Joerg Wunsch
+ * 4. The name authors may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *
+ * @(#)mcon.c, 3.20, Last Edit-Date: [Tue Dec 20 14:53:15 1994]
+ */
+
+/*---------------------------------------------------------------------------*
+ *
+ * history:
+ *
+ * -jw initial version; includes a basic mapping between PeeCee
+ * scan codes and key names
+ * -hm changed sys/pcvt_ioctl.h -> machine/pcvt_ioctl.h
+ *
+ *---------------------------------------------------------------------------*/
+
+/*
+ * Utility program to wire the mouse emulator control ioctl to the
+ * user level. Allows setting of any configurable parameter, or
+ * display the current configuration.
+ */
+
+#include <machine/pcvt_ioctl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <sys/fcntl.h>
+
+static const char *keynames[] = {
+ "", "esc", "1", "2", "3", "4", "5", "6", "7", "8", "9", "0",
+ "-", "+", "bksp", "tab", "q", "w", "e", "r", "t", "y", "u",
+ "i", "o", "p", "[", "]", "enter", "ctrl", "a", "s", "d", "f",
+ "g", "h", "j", "k", "l", ";", "\"", "`", "lshift", "\\",
+ "z", "x", "c", "v", "b", "n", "m", ",", ".", "/", "rshift",
+ "prtscr", "alt", "space", "caps", "f1", "f2", "f3", "f4",
+ "f5", "f6", "f7", "f8", "f9", "f10", "numlock", "scrolllock",
+ "kp7", "kp8", "kp9", "kp-", "kp4", "kp5", "kp6", "kp+",
+ "kp1", "kp2", "kp3", "kp0", "kp."
+};
+
+
+const char *scantoname(int scan) {
+ if(scan >= sizeof keynames / sizeof(const char *))
+ return "???";
+ else
+ return keynames[scan];
+}
+
+int nametoscan(const char *name) {
+ int i;
+ for(i = 0; i < sizeof keynames / sizeof(const char *); i++)
+ if(strcmp(keynames[i], name) == 0)
+ return i;
+ return -1;
+}
+
+
+int main(int argc, char **argv) {
+ int c, errs = 0, fd, do_set = 0;
+ int left = 0, mid = 0, right = 0, accel = 0, sticky = -1;
+ struct mousedefs mdef;
+
+ while((c = getopt(argc, argv, "l:m:r:a:s:")) != -1)
+ switch(c) {
+ case 'l':
+ left = nametoscan(optarg);
+ do_set = 1;
+ if(left == -1) goto keynameerr;
+ break;
+
+ case 'm':
+ mid = nametoscan(optarg);
+ do_set = 1;
+ if(mid == -1) goto keynameerr;
+ break;
+
+ case 'r':
+ right = nametoscan(optarg);
+ do_set = 1;
+ if(right == -1) goto keynameerr;
+ break;
+
+ keynameerr:
+ {
+ fprintf(stderr, "unknown key name: %s\n",
+ optarg);
+ errs++;
+ }
+ break;
+
+ case 'a':
+ accel = 1000 * strtol(optarg, 0, 10);
+ do_set = 1;
+ break;
+
+ case 's':
+ if(strcmp(optarg, "0") == 0
+ || strcmp(optarg, "false") == 0
+ || strcmp(optarg, "no") == 0)
+ sticky = 0;
+ else if(strcmp(optarg, "1") == 0
+ || strcmp(optarg, "true") == 0
+ || strcmp(optarg, "yes") == 0)
+ sticky = 1;
+ else {
+ fprintf(stderr, "invalid argument to -s: %s\n",
+ optarg);
+ errs++;
+ }
+ do_set = 1;
+ break;
+
+ default:
+ errs++;
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if(errs || argc != 1) {
+ fprintf(stderr, "usage: "
+ "mouse [-l key][-m key][-r key][-a acctime][-s 0|1] "
+ "mousedev\n");
+ return 2;
+ }
+
+ if((fd = open(argv[0], O_RDONLY)) < 0) {
+ perror("open(mousedev)");
+ return 2;
+ }
+ if(ioctl(fd, KBDMOUSEGET, &mdef) < 0) {
+ perror("ioctl(KBDMOUSEGET)");
+ return 1;
+ }
+
+ if(!do_set) {
+ printf("Current mouse emulator definitions:\n"
+ "left button: %s\n"
+ "middle button: %s\n"
+ "right button: %s\n"
+ "acceleration limit: %d msec\n"
+ "sticky buttons: %s\n",
+ scantoname(mdef.leftbutton),
+ scantoname(mdef.middlebutton),
+ scantoname(mdef.rightbutton),
+ mdef.acceltime / 1000,
+ mdef.stickybuttons? "yes": "no");
+ return 0;
+ }
+
+ if(left) mdef.leftbutton = left & 0x7f;
+ if(mid) mdef.middlebutton = mid & 0x7f;
+ if(right) mdef.rightbutton = right & 0x7f;
+
+ if(accel) mdef.acceltime = accel;
+ if(sticky != -1) mdef.stickybuttons = sticky;
+
+ if(ioctl(fd, KBDMOUSESET, &mdef) < 0) {
+ perror("ioctl(KBDMOUSESET)");
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/usr.sbin/pcvt/scon/Makefile b/usr.sbin/pcvt/scon/Makefile
new file mode 100644
index 0000000..6fdb94e
--- /dev/null
+++ b/usr.sbin/pcvt/scon/Makefile
@@ -0,0 +1,3 @@
+PROG= scon
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/pcvt/scon/scon.1 b/usr.sbin/pcvt/scon/scon.1
new file mode 100644
index 0000000..7787fda
--- /dev/null
+++ b/usr.sbin/pcvt/scon/scon.1
@@ -0,0 +1,214 @@
+.\" Copyright (c) 1992,1993,1994 Hellmuth Michaelis and Joerg Wunsch
+.\"
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by
+.\" Hellmuth Michaelis and Joerg Wunsch
+.\" 4. The name authors may not be used to endorse or promote products
+.\" derived from this software without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+.\" IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+.\"
+.\" @(#)scon.1, 3.00, Last Edit-Date: [Mon Jan 10 21:30:48 1994]
+.\"
+.Dd December 31, 1993
+.Dt SCON 1
+.Sh NAME
+.Nm scon
+.Nd controls screen modes for pcvt video driver
+.Sh SYNOPSIS
+.Nm scon
+.Op Fl a
+.Op Fl c Ar screenno
+.Op Fl d Ar device
+.Op Fl f Ar on|off
+.Op Fl h
+.Op Fl H
+.Op Fl l
+.Op Fl m
+.Op Fl v
+.Op Fl V
+.Op Fl s Ar lines
+.br
+.Nm scon
+.Op Fl v
+.Op Fl d Ar device
+.Fl p Ar entry,red,green,blue
+.br
+.Nm scon
+.Op Fl v
+.Op Fl d Ar device
+.Fl p Ar default
+.br
+.Nm scon
+.Op Fl v
+.Op Fl d Ar device
+.Fl p Ar list
+.Nm scon
+.Op Fl v
+.Fl t Ar timeout
+.Nm scon
+.Op Fl v
+.Fl 1 | Fl 8
+.Sh DESCRIPTION
+The
+.Nm scon
+utility controls several aspects of the runtime behaviour of the pcvt vt220
+driver.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl a
+Returns a string describing the video adaptor found by pcvt, the string
+returned could be MDA, HGC, CGA, EGA, VGA or UNKNOWN.
+.It Fl c
+Specify the screen number the current (displayed) screen should be switched
+to.
+.It Fl d
+Specify the device filename (i.e. /dev/ttyv2) further operations specified on
+the command line should be applied to.
+.It Fl f
+Some programs which silently assume 24 lines when they run on a VT220 show
+incorrect behaviour when the terminal has really 25 lines. To support full
+VT220 behaviour, it is possible to force pcvt to select only 24 lines when
+it is running in 25-lines pure VT mode and/or in 28-lines HP-mode. The
+.Fl f
+option requires one additional parameter, the string 'on' or 'off' to switch
+this mode for a virtual screen on or off respectively. This mode has no effect
+if any other vertical resolutions are selected than the two above mentioned.
+.It Fl h
+Prints a usage/help text.
+.It Fl l
+Lists the current configuration of runtime changeable options and fixed
+parameters (such as the type of the adaptor, and in case of a VGA adaptor,
+the Manufacturer, Chipset and 132 column support) of the output portion
+of the pcvt driver.
+.It Fl m
+Returns a string describing the connected display monitor type found by pcvt,
+the string returned can be MONO, COLOR or UNKNOWN.
+.It Fl v
+Specify verbose operation of the program.
+.It Fl V
+Switch the specified/current screen into a pure VT220 mode without recognizing
+any HP escape sequences and without displaying function key labels.
+.It Fl H
+Switch the specified/current screen into a mixed HP/VT220 mode. That is, that
+in addition to the full VT220 emulation, the HP function key labels and the
+escape sequences for handling the labels are available to the user.
+.It Fl s
+Specify the number of character lines on the screen. Possible parameters are
+25, 28, 35, 40, 43 or 50. To use all this screen sizes, the fonts required
+for proper operation of a desired size have to be downloaded to the EGA/VGA
+font ram. This option is available only for EGA and VGA boards.
+.It Fl p
+Modify VGA palette
+.Pq DAC .
+The
+.Fl p
+is mutually exclusive with
+.Fl s ,
+.Fl H ,
+and
+.Fl V .
+Naturally, option
+.Fl p
+is available only for VGA boards. Three flavors are available.
+
+If used with argument
+.Dq Ar default ,
+this flag will restore the default palette
+.Po
+as installed by VGA ROM BIOS after hardware reset
+.Pc .
+
+If used with argument
+.Dq Ar list ,
+the current VGA DAC palette entries are listed. Each entry contains
+the table index, values for red, green, and blue, and if there's a
+known name for this entry, the color name. Trailing empty table
+slots (RGB values all zero) are omitted.
+
+Otherwise, four comma-separated arguments are expected. The first
+denotes the number of palette entry to be modified. This may be either
+a number between 0 and 255, or the usual name of an associated color
+.Pq case-insensitive .
+The following values for red, green and blue are restricted to 0 through 63
+due to VGA DAC conventions.
+Note that the first delimiter within such an argument may be a colon
+.Dq \&:
+instead of a comma
+.Dq \&,
+for better readability, but this violates common command argument
+conventions.
+Multiple
+.Fl p
+options may be specified if unambiguous.
+.It Fl t
+Specifying
+.Fl t
+will activate the screen saver. The behaviour depends on
+.Ar timeout :
+if
+.Ar timeout
+is given as 0, the screen saver is turned off. Otherwise,
+.Ar timeout
+is taken as a number of seconds to wait until activating the
+screen saver.
+NOTE: the
+.Fl t
+option is only available if screen saver support has been compiled into
+the driver !
+.It Fl 1
+Sets 132 columns mode
+.Pq only available on VGA adaptors .
+.It Fl 8
+Sets 80 columns mode.
+.El
+.Pp
+When switching between HP and VT mode, when switching the force 24 lines
+mode on and off, or when switching between 80 and 132 columns operation,
+the screen is cleared, the scrolling
+region is reset and the cursor is placed in the home position.
+.Sh EXAMPLES
+The command
+.Dq Li scon Fl H s Ar 28
+places the current screen into HP mode and sets the screen size to 28x80.
+
+Invoking
+.Do
+.Li scon Fl p
+.Ar lightgray,0,15,0
+.Fl p
+.Ar 0:45,45,45
+.Dc
+will result in green on gray output for normal text.
+Note that normal text color is light gray, and not white as one might expect.
+.Sh BUGS
+the
+.Fl c
+and
+.Fl d
+options collide somehow, this will change in a future release.
+.Sh SEE ALSO
+.Xr cursor 1 ,
+.Xr loadfont 1 ,
+.Xr pcvt 4
diff --git a/usr.sbin/pcvt/scon/scon.c b/usr.sbin/pcvt/scon/scon.c
new file mode 100644
index 0000000..d42d5c4
--- /dev/null
+++ b/usr.sbin/pcvt/scon/scon.c
@@ -0,0 +1,856 @@
+/*
+ * Copyright (c) 1992,1993,1994 Hellmuth Michaelis and Joerg Wunsch
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by
+ * Hellmuth Michaelis and Joerg Wunsch
+ * 4. The name authors may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+static char *id =
+ "@(#)scon.c, 3.20, Last Edit-Date: [Sun Sep 25 12:33:21 1994]";
+
+/*---------------------------------------------------------------------------*
+ *
+ * history:
+ *
+ * -hm moving fd for default device from 1 -> 0 for such things
+ * as "scon -p list | more" to be possible
+ * (reported by Gordon L. Burditt, gordon@sneaky.lonestar.org)
+ * -hm adding option "a" for just returning the type of video adaptor
+ * -hm removing explicit HGC support, same as MDA ...
+ * -hm vga type/family/132col support info on -l
+ * -hm force 24 lines in DEC 25 lines mode and HP 28 lines mode
+ * -hm fixed bug with 132 column mode display status display
+ * -jw added 132/80 col mode switching
+ * -hm removed -h flag, use -? now ... ;-)
+ * -hm S3 chipsets ..
+ * -hm Cirrus chipsets support from Onno van der Linden
+ * -hm -m option, display monitor type
+ * -hm bugfix, scon -c <screen-num> cleared dest screen, fixed
+ *
+ *---------------------------------------------------------------------------*/
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <machine/pcvt_ioctl.h>
+
+#define DEFAULTFD 0
+
+int aflag = -1;
+int lflag = -1;
+int mflag = -1;
+int current = -1;
+int pflag = -1;
+int hflag = -1;
+int res = -1;
+char *device;
+int dflag = -1;
+int vflag = 0;
+int Pflag = 0;
+int tflag = 0;
+int fflag = -1;
+int colms = 0;
+char *onoff;
+
+unsigned timeout;
+struct screeninfo screeninfo;
+
+#define NVGAPEL 256
+
+struct rgb {
+ unsigned r, g, b;
+ int dothis;
+};
+
+static struct rgb palette[NVGAPEL] = {
+ { 0x00, 0x00, 0x00, 0}, /* 0 - black */
+ { 0x00, 0x00, 0x2a, 0}, /* 1 - blue */
+ { 0x00, 0x2a, 0x00, 0}, /* 2 - green */
+ { 0x00, 0x2a, 0x2a, 0}, /* 3 - cyan */
+ { 0x2a, 0x00, 0x00, 0}, /* 4 - red */
+ { 0x2a, 0x00, 0x2a, 0}, /* 5 - magenta */
+ { 0x2a, 0x2a, 0x00, 0}, /* 6 */
+ { 0x2a, 0x2a, 0x2a, 0}, /* 7 - lightgray */
+ { 0x00, 0x00, 0x15, 0}, /* 8 */
+ { 0x00, 0x00, 0x3f, 0}, /* 9 */
+ { 0x00, 0x2a, 0x15, 0}, /* 10 */
+ { 0x00, 0x2a, 0x3f, 0}, /* 11 */
+ { 0x2a, 0x00, 0x15, 0}, /* 12 */
+ { 0x2a, 0x00, 0x3f, 0}, /* 13 */
+ { 0x2a, 0x2a, 0x15, 0}, /* 14 */
+ { 0x2a, 0x2a, 0x3f, 0}, /* 15 */
+ { 0x00, 0x15, 0x00, 0}, /* 16 */
+ { 0x00, 0x15, 0x2a, 0}, /* 17 */
+ { 0x00, 0x3f, 0x00, 0}, /* 18 */
+ { 0x00, 0x3f, 0x2a, 0}, /* 19 */
+ { 0x2a, 0x15, 0x00, 0}, /* 20 - brown */
+ { 0x2a, 0x15, 0x2a, 0}, /* 21 */
+ { 0x2a, 0x3f, 0x00, 0}, /* 22 */
+ { 0x2a, 0x3f, 0x2a, 0}, /* 23 */
+ { 0x00, 0x15, 0x15, 0}, /* 24 */
+ { 0x00, 0x15, 0x3f, 0}, /* 25 */
+ { 0x00, 0x3f, 0x15, 0}, /* 26 */
+ { 0x00, 0x3f, 0x3f, 0}, /* 27 */
+ { 0x2a, 0x15, 0x15, 0}, /* 28 */
+ { 0x2a, 0x15, 0x3f, 0}, /* 29 */
+ { 0x2a, 0x3f, 0x15, 0}, /* 30 */
+ { 0x2a, 0x3f, 0x3f, 0}, /* 31 */
+ { 0x15, 0x00, 0x00, 0}, /* 32 */
+ { 0x15, 0x00, 0x2a, 0}, /* 33 */
+ { 0x15, 0x2a, 0x00, 0}, /* 34 */
+ { 0x15, 0x2a, 0x2a, 0}, /* 35 */
+ { 0x3f, 0x00, 0x00, 0}, /* 36 */
+ { 0x3f, 0x00, 0x2a, 0}, /* 37 */
+ { 0x3f, 0x2a, 0x00, 0}, /* 38 */
+ { 0x3f, 0x2a, 0x2a, 0}, /* 39 */
+ { 0x15, 0x00, 0x15, 0}, /* 40 */
+ { 0x15, 0x00, 0x3f, 0}, /* 41 */
+ { 0x15, 0x2a, 0x15, 0}, /* 42 */
+ { 0x15, 0x2a, 0x3f, 0}, /* 43 */
+ { 0x3f, 0x00, 0x15, 0}, /* 44 */
+ { 0x3f, 0x00, 0x3f, 0}, /* 45 */
+ { 0x3f, 0x2a, 0x15, 0}, /* 46 */
+ { 0x3f, 0x2a, 0x3f, 0}, /* 47 */
+ { 0x15, 0x15, 0x00, 0}, /* 48 */
+ { 0x15, 0x15, 0x2a, 0}, /* 49 */
+ { 0x15, 0x3f, 0x00, 0}, /* 50 */
+ { 0x15, 0x3f, 0x2a, 0}, /* 51 */
+ { 0x3f, 0x15, 0x00, 0}, /* 52 */
+ { 0x3f, 0x15, 0x2a, 0}, /* 53 */
+ { 0x3f, 0x3f, 0x00, 0}, /* 54 */
+ { 0x3f, 0x3f, 0x2a, 0}, /* 55 */
+ { 0x15, 0x15, 0x15, 0}, /* 56 - darkgray */
+ { 0x15, 0x15, 0x3f, 0}, /* 57 - lightblue */
+ { 0x15, 0x3f, 0x15, 0}, /* 58 - lightgreen */
+ { 0x15, 0x3f, 0x3f, 0}, /* 59 - lightcyan */
+ { 0x3f, 0x15, 0x15, 0}, /* 60 - lightred */
+ { 0x3f, 0x15, 0x3f, 0}, /* 61 - lightmagenta */
+ { 0x3f, 0x3f, 0x15, 0}, /* 62 - yellow */
+ { 0x3f, 0x3f, 0x3f, 0}, /* 63 - white */
+ { 0x00, 0x00, 0x00, 0} /* 64 ... - empty */
+};
+
+static struct colname {
+ const char *name;
+ unsigned idx;
+} colnames[] = {
+ {"black", 0},
+ {"blue", 1},
+ {"green", 2},
+ {"cyan", 3},
+ {"red", 4},
+ {"magenta", 5},
+ {"brown", 20},
+ {"lightgray", 7},
+ {"lightgrey", 7},
+ {"darkgray", 56},
+ {"darkgrey", 56},
+ {"lightblue", 57},
+ {"lightgreen", 58},
+ {"lightcyan", 59},
+ {"lightred", 60},
+ {"lightmagenta", 61},
+ {"yellow", 62},
+ {"white", 63},
+ /* must be terminator: */ {(const char *)NULL, 0}
+};
+
+
+static void parsepopt(char *arg, unsigned *idx,
+ unsigned *r, unsigned *g, unsigned *b);
+static void printpalette(int fd);
+
+main(argc,argv)
+int argc;
+char *argv[];
+{
+ extern int optind;
+ extern int opterr;
+ extern char *optarg;
+
+ int c;
+ int fd;
+
+ while( (c = getopt(argc, argv, "ac:d:f:HVlms:t:vp:18")) != -1)
+ {
+ switch(c)
+ {
+ case 'a':
+ aflag = 1;
+ break;
+
+ case 'l':
+ lflag = 1;
+ break;
+
+ case 'm':
+ mflag = 1;
+ break;
+
+ case 'c':
+ current = atoi(optarg);
+ break;
+
+ case 'd':
+ device = optarg;
+ dflag = 1;
+ break;
+
+ case 'f':
+ onoff = optarg;
+ fflag = 1;
+ break;
+
+ case 'V':
+ pflag = 1;
+ break;
+
+ case 'H':
+ hflag = 1;
+ break;
+
+ case 's':
+ if (!strncmp(optarg, "25", 2))
+ res = SIZ_25ROWS;
+ else if(!strncmp(optarg, "28", 2))
+ res = SIZ_28ROWS;
+ else if(!strncmp(optarg, "35", 2))
+ res = SIZ_35ROWS;
+ else if(!strncmp(optarg, "40", 2))
+ res = SIZ_40ROWS;
+ else if(!strncmp(optarg, "43", 2))
+ res = SIZ_43ROWS;
+ else if(!strncmp(optarg, "50", 2))
+ res = SIZ_50ROWS;
+ break;
+
+ case 'v':
+ vflag++;
+ break;
+
+ case 'p':
+ if(!strcmp(optarg, "list"))
+ {
+ if(Pflag)
+ {
+ fprintf(stderr,
+ "-p list is mutual exclusive "
+ "with other -p options\n");
+ return 2;
+ }
+ Pflag = 3;
+ }
+ else if(!strcmp(optarg, "default"))
+ {
+ if(Pflag)
+ {
+ fprintf(stderr,
+ "multiple -p default not "
+ "allowed\n");
+ return 2;
+ }
+ Pflag = 2;
+ } else {
+ unsigned idx, r, g, b;
+
+ if(Pflag > 1)
+ {
+ fprintf(stderr,
+ "-p default and -p i,r,g,b "
+ "ambiguous\n");
+ return 2;
+ }
+ Pflag = 1;
+ parsepopt(optarg, &idx, &r, &g, &b);
+ if(idx >= NVGAPEL)
+ {
+ fprintf(stderr,
+ "index %u in -p option "
+ "out of range\n", idx);
+ return 2;
+ }
+ palette[idx].r = r;
+ palette[idx].g = g;
+ palette[idx].b = b;
+ palette[idx].dothis = 1;
+ }
+ break;
+
+ case 't':
+ tflag++;
+ timeout = atoi(optarg);
+ break;
+
+ case '1':
+ colms = 132;
+ break;
+
+ case '8':
+ colms = 80;
+ break;
+
+ case '?':
+ default:
+ usage();
+ break;
+ }
+ }
+
+ if((pflag == 1) && (hflag == 1))
+ usage();
+
+ if(dflag == -1 && lflag == -1 && current == -1 && pflag == -1 &&
+ hflag == -1 && res == -1 && Pflag == 0 && tflag == 0 && fflag == -1
+ && colms == 0 && mflag == -1)
+ {
+ lflag = 1;
+ }
+
+ if(dflag == -1)
+ {
+ if(vflag)
+ printf("using current device\n");
+ fd = DEFAULTFD; /* -hm, Feb 12 1993 */
+ }
+ else
+ {
+ if((fd = open(device, O_RDWR)) == -1)
+ {
+ char buffer[80];
+ strcpy(buffer,"ERROR opening ");
+ strcat(buffer,device);
+ perror(buffer);
+ exit(1);
+ }
+ if(vflag)
+ printf("using device %s\n",device);
+ }
+
+ if(aflag == 1) /* return adaptor type */
+ {
+ printadaptor(fd);
+ exit(0);
+ }
+
+ if(mflag == 1) /* return monitor type */
+ {
+ printmonitor(fd);
+ exit(0);
+ }
+
+ if(lflag == 1) /* list information */
+ {
+ if(vflag)
+ printf("processing option -l, listing screen info\n");
+ printinfo(fd);
+ exit(0);
+ }
+
+ if(tflag) /* set screen saver timeout */
+ {
+ if(vflag)
+ {
+ printf(
+ "processing option -t, setting screen saver timeout: "
+ );
+ if(timeout)
+ printf("new timeout = %d s\n", timeout);
+ else
+ printf("turned off\n");
+ }
+
+ if(ioctl(fd, VGASCREENSAVER, &timeout) < 0)
+ {
+ perror("ioctl(VGASCREENSAVER)");
+ fprintf(stderr, "Check the driver, the screensaver is probably not compiled in!\n");
+ exit(2);
+ }
+ goto success;
+ }
+
+ if(colms)
+ {
+ if(vflag)
+ printf("Setting number of columns to %d\n", colms);
+ if(ioctl(fd, VGASETCOLMS, &colms) < 0)
+ {
+ perror("ioctl(VGASETCOLMS)");
+ exit(2);
+ }
+ goto success;
+ }
+
+ if(Pflag == 3)
+ {
+ /* listing VGA palette */
+ if(vflag)
+ printf("processing option -p list, "
+ "listing VGA palette\n");
+
+ printpalette(fd);
+ goto success;
+ }
+
+ if(Pflag)
+ {
+ unsigned int idx;
+
+ /* setting VGA palette */
+ if(vflag)
+ printf("processing option -p, setting VGA palette%s\n",
+ Pflag == 2? " to default": "");
+
+ for(idx = 0; idx < NVGAPEL; idx++)
+ if(Pflag == 2 || palette[idx].dothis)
+ {
+ struct vgapel p;
+ p.idx = idx;
+ p.r = palette[idx].r;
+ p.g = palette[idx].g;
+ p.b = palette[idx].b;
+ if(ioctl(fd, VGAWRITEPEL, (caddr_t)&p) < 0)
+ {
+ perror("ioctl(fd, VGAWRITEPEL)");
+ return 2;
+ }
+ }
+ goto success;
+ }
+
+ screeninfo.screen_no = -1; /* We are using fd */
+ screeninfo.current_screen = current;
+ screeninfo.pure_vt_mode = -1;
+ screeninfo.screen_size = res;
+ screeninfo.force_24lines = -1;
+
+ if(current != -1) /* set current screen */
+ {
+ if(vflag)
+ printf("processing option -c, setting current screen to %d\n",current);
+
+ if(ioctl(1, VGASETSCREEN, &screeninfo) == -1)
+ {
+ perror("ioctl VGASETSCREEN failed");
+ exit(1);
+ }
+ exit(0);
+ }
+
+ if(pflag == 1)
+ {
+ if(vflag)
+ printf("processing option -V, setting emulation to pure VT220\n");
+ screeninfo.pure_vt_mode = M_PUREVT;
+ }
+ else if(hflag == 1)
+ {
+ if(vflag)
+ printf("processing option -H, setting emulation to VT220 + HP Labels\n");
+ screeninfo.pure_vt_mode = M_HPVT;
+ }
+ else
+ {
+ if(vflag)
+ printf("no change in terminal emulation\n");
+ }
+
+ if(vflag)
+ {
+ if(res == -1)
+ printf("no change in screen resolution\n");
+ else if(res == SIZ_25ROWS)
+ printf("change screen resolution to 25 lines\n");
+ else if(res == SIZ_28ROWS)
+ printf("change screen resolution to 28 lines\n");
+ else if(res == SIZ_35ROWS)
+ printf("change screen resolution to 35 lines\n");
+ else if(res == SIZ_40ROWS)
+ printf("change screen resolution to 40 lines\n");
+ else if(res == SIZ_43ROWS)
+ printf("change screen resolution to 43 lines\n");
+ else if(res == SIZ_50ROWS)
+ printf("change screen resolution to 50 lines\n");
+ }
+
+ if(fflag == 1) /* force 24 lines on/off */
+ {
+ if(!strcmp(onoff, "on"))
+ {
+ fflag = 1;
+ }
+ else if(!strcmp(onoff, "off"))
+ {
+ fflag = 0;
+ }
+ else
+ {
+ fprintf(stderr,"you must specify 'on' or 'off' with -f option!\n");
+ exit(1);
+ }
+ }
+ screeninfo.force_24lines = fflag;
+
+ if(ioctl(fd, VGASETSCREEN, &screeninfo) == -1)
+ {
+ perror("ioctl VGASETSCREEN failed");
+ exit(1);
+ }
+success:
+ if(vflag)
+ printf("successful execution of ioctl VGASETSCREEN!\n");
+ exit(0);
+}
+
+usage()
+{
+ fprintf(stderr,"\nscon - screen control utility for the pcvt video driver\n");
+ fprintf(stderr,"usage: scon -a -l -m -v -c [n] -d [dev] -f [on|off] -V -H -s [n]\n");
+ fprintf(stderr,"usage: scon -p [default | list | i,r,g,b] | -t [sec] | -1 | -8\n");
+ fprintf(stderr," -a list video adaptor type (MDA,CGA,EGA or VGA)\n");
+ fprintf(stderr," -c <screen no> switch current virtual screen to <screen no>\n");
+ fprintf(stderr," -d <device> set parameters(-V|-H|-s) for virtual device\n");
+ fprintf(stderr," -f <on|off> force 24 lines in VT 25 lines and HP 28 lines mode\n");
+ fprintf(stderr," -H set VT220/HP emulation mode for a virtual screen\n");
+ fprintf(stderr," -l list current parameters for a virtual screen\n");
+ fprintf(stderr," -m report monitor type (MONO/COLOR)\n");
+ fprintf(stderr," -p default set default VGA palette\n");
+ fprintf(stderr," -p list list current VGA palette\n");
+ fprintf(stderr," -p <i,r,g,b> set VGA palette entry i to r/g/b\n");
+ fprintf(stderr," -p <name,r,g,b> set VGA palette entry for color name to r/g/b\n");
+ fprintf(stderr," -s <lines> set 25, 28, 35, 40, 43 or 50 lines for a virtual screen\n");
+ fprintf(stderr," -t <timeout> set screen saver timeout [seconds]\n");
+ fprintf(stderr," -1 set 132 columns mode\n");
+ fprintf(stderr," -8 set 80 columns mode\n");
+ fprintf(stderr," -v verbose mode\n");
+ fprintf(stderr," -V set pure VT220 emulation for a virtual screen\n");
+ fprintf(stderr," -? display help (this message)\n\n");
+ exit(1);
+}
+
+printadaptor(fd)
+int fd;
+{
+ if(ioctl(fd, VGAGETSCREEN, &screeninfo) == -1)
+ {
+ perror("ioctl VGAGETSCREEN failed");
+ exit(1);
+ }
+ switch(screeninfo.adaptor_type)
+ {
+ default:
+ case UNKNOWN_ADAPTOR:
+ printf("UNKNOWN\n");
+ break;
+
+ case MDA_ADAPTOR:
+ printf("MDA\n");
+ break;
+
+ case CGA_ADAPTOR:
+ printf("CGA\n");
+ break;
+
+ case EGA_ADAPTOR:
+ printf("EGA\n");
+ break;
+
+ case VGA_ADAPTOR:
+ printf("VGA\n");
+ break;
+ }
+}
+
+printmonitor(fd)
+int fd;
+{
+ if(ioctl(fd, VGAGETSCREEN, &screeninfo) == -1)
+ {
+ perror("ioctl VGAGETSCREEN failed");
+ exit(1);
+ }
+ switch(screeninfo.monitor_type)
+ {
+ default:
+ printf("UNKNOWN\n");
+ break;
+
+ case MONITOR_MONO:
+ printf("MONO\n");
+ break;
+
+ case MONITOR_COLOR:
+ printf("COLOR\n");
+ break;
+ }
+}
+
+char *vga_type(int number)
+{
+ static char *vga_tab[] = {
+ "Generic VGA",
+ "ET4000",
+ "ET3000",
+ "PVGA1A",
+ "WD90C00",
+ "WD90C10",
+ "WD90C11",
+ "VIDEO 7 VEGA",
+ "VIDEO 7 FAST",
+ "VIDEO 7 VER5",
+ "VIDEO 7 1024I",
+ "Unknown VIDEO 7",
+ "TVGA 8800BR",
+ "TVGA 8800CS",
+ "TVGA 8900B",
+ "TVGA 8900C",
+ "TVGA 8900CL",
+ "TVGA 9000",
+ "TVGA 9100",
+ "TVGA 9200",
+ "Unknown TRIDENT",
+ "S3 80C911",
+ "S3 80C924",
+ "S3 80C801/80C805",
+ "S3 80C928",
+ "Unknown S3",
+ "CL-GD5402",
+ "CL-GD5402r1",
+ "CL-GD5420",
+ "CL-GD5420r1",
+ "CL-GD5422",
+ "CL-GD5424",
+ "CL-GD5426",
+ "CL-GD5428",
+
+ };
+ return(vga_tab[number]);
+}
+
+char *vga_family(int number)
+{
+ static char *vga_tab[] = {
+ "Generic VGA",
+ "Tseng Labs",
+ "Western Digital",
+ "Video Seven",
+ "Trident",
+ "S3 Incorporated",
+ "Cirrus Logic",
+ };
+ return(vga_tab[number]);
+}
+
+printinfo(fd)
+int fd;
+{
+ if(ioctl(fd, VGAGETSCREEN, &screeninfo) == -1)
+ {
+ perror("ioctl VGAGETSCREEN failed");
+ exit(1);
+ }
+
+ printf( "\nVideo Adaptor Type = ");
+
+ switch(screeninfo.adaptor_type)
+ {
+ default:
+ case UNKNOWN_ADAPTOR:
+ printf("UNKNOWN Video Adaptor\n");
+ break;
+
+ case MDA_ADAPTOR:
+ printf("MDA - Monochrome Display Adaptor\n");
+ break;
+
+ case CGA_ADAPTOR:
+ printf("CGA - Color Graphics Adaptor\n");
+ break;
+
+ case EGA_ADAPTOR:
+ printf("EGA - Enhanced Graphics Adaptor\n");
+ break;
+
+ case VGA_ADAPTOR:
+ printf("VGA - Video Graphics Adaptor/Array\n");
+ printf(" VGA Chipset Manufacturer = %s\n",
+ vga_family(screeninfo.vga_family));
+ printf(" VGA Chipset Type = %s\n",
+ vga_type(screeninfo.vga_type));
+ printf(" Support for 132 Column Mode = %s\n",
+ screeninfo.vga_132 ? "Yes" : "No");
+ break;
+ }
+
+ printf( "Display Monitor Type = ");
+
+ switch(screeninfo.monitor_type)
+ {
+ default:
+ printf("UNKNOWN Monitor Type\n");
+ break;
+
+ case MONITOR_MONO:
+ printf("Monochrome Monitor\n");
+ break;
+
+ case MONITOR_COLOR:
+ printf("Color Monitor\n");
+ break;
+ }
+
+ printf( "Number of Downloadable Fonts = %d\n",screeninfo.totalfonts);
+ printf( "Number of Virtual Screens = %d\n",screeninfo.totalscreens);
+ printf( "Info Request Screen Number = %d\n",screeninfo.screen_no);
+ printf( "Current Displayed Screen = %d\n",screeninfo.current_screen);
+
+ if(screeninfo.pure_vt_mode == M_PUREVT)
+ printf( "Terminal Emulation Mode = VT220\n");
+ else
+ printf( "Terminal Emulation Mode = VT220 with HP Features\n");
+
+ printf( "Lines = ");
+
+ switch(screeninfo.screen_size)
+ {
+ case SIZ_25ROWS:
+ printf( "25\n");
+ break;
+
+ case SIZ_28ROWS:
+ printf( "28\n");
+ break;
+
+ case SIZ_35ROWS:
+ printf( "35\n");
+ break;
+
+ case SIZ_40ROWS:
+ printf( "40\n");
+ break;
+
+ case SIZ_43ROWS:
+ printf( "43\n");
+ break;
+
+ case SIZ_50ROWS:
+ printf( "50\n");
+ break;
+
+ default:
+ printf( "UNKNOWN\n");
+ break;
+ }
+ printf( "Force 24 Lines = %s",
+ screeninfo.force_24lines ? "Yes" : "No");
+
+ printf("\n\n");
+}
+
+static const char *findname(unsigned idx)
+{
+ /* try to find a name for palette entry idx */
+ /* if multiple names exist, returns first matching */
+ register struct colname *cnp;
+
+ for(cnp = colnames; cnp->name; cnp++)
+ if(cnp->idx == idx)
+ return cnp->name;
+
+ /* not found */
+ return (const char *)NULL;
+}
+
+static void printpalette(int fd)
+{
+ register unsigned idx, last;
+
+ for(idx = 0; idx < NVGAPEL; idx++)
+ {
+ struct vgapel p;
+ p.idx = idx;
+ if(ioctl(fd, VGAREADPEL, &p) < 0)
+ {
+ perror("ioctl(VGAREADPEL)");
+ exit(2);
+ }
+ palette[idx].r = p.r;
+ palette[idx].g = p.g;
+ palette[idx].b = p.b;
+ }
+
+ /* find last non-empty entry */
+ for(last = NVGAPEL - 1; last; last--)
+ if(palette[last].r || palette[last].g || palette[last].b)
+ break;
+
+ if(last != NVGAPEL - 1)
+ last++;
+
+ /* now, everything's collected. print out table */
+ printf("VGA palette status\n");
+ printf("index red green blue name\n");
+ for(idx = 0; idx < last; idx++)
+ {
+ const char *cp;
+ printf("%5d %5d %5d %5d",
+ idx, palette[idx].r, palette[idx].g, palette[idx].b);
+ if(cp = findname(idx))
+ printf(" %s\n", cp);
+ else
+ putchar('\n');
+ }
+ putchar('\n');
+}
+
+
+static void parsepopt(char *arg, unsigned *idx,
+ unsigned *r, unsigned *g, unsigned *b)
+{
+ char firstarg[21];
+ register unsigned i;
+
+ if(sscanf(arg, "%20[a-zA-Z0-9]%*[,:]%u,%u,%u", firstarg, r, g, b) < 4
+ || strlen(firstarg) == 0) {
+ fprintf(stderr, "too few args in -p i,r,g,b\n");
+ exit(2);
+ }
+
+ if(firstarg[0] >= '0' && firstarg[0] <= '9') {
+ *idx = strtoul(firstarg, NULL, 10);
+ return;
+ }
+
+ for(i = 0; colnames[i].name; i++)
+ if(strcasecmp(colnames[i].name, firstarg) == 0) {
+ *idx = colnames[i].idx;
+ return;
+ }
+ fprintf(stderr, "arg ``%s'' in -p option not recognized\n",
+ firstarg);
+ exit(2);
+}
diff --git a/usr.sbin/pcvt/set2061/CAUTION b/usr.sbin/pcvt/set2061/CAUTION
new file mode 100644
index 0000000..e1eba06
--- /dev/null
+++ b/usr.sbin/pcvt/set2061/CAUTION
@@ -0,0 +1,28 @@
+CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION
+CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION
+CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION
+
+ THE USE OF THIS PROGRAM MAY DESTROY YOUR MONITOR !!!
+ ====================================================
+
+ IF YOU DON'T KNOW WHAT YOU ARE DOING, STAY AWAY FROM IT !!!
+ ===========================================================
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION
+CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION
+CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION
+
diff --git a/usr.sbin/pcvt/set2061/ICD2061Aalt.c b/usr.sbin/pcvt/set2061/ICD2061Aalt.c
new file mode 100644
index 0000000..a74ec4e
--- /dev/null
+++ b/usr.sbin/pcvt/set2061/ICD2061Aalt.c
@@ -0,0 +1,297 @@
+/*
+ * This code is derived from code available from the STB bulletin board
+ */
+
+/* $XFree86: mit/server/ddx/x386/common_hw/ICD2061Aalt.c,v 2.6 1994/04/15 05:10:30 dawes Exp $ */
+
+#ifndef KERNEL
+#include "compiler.h"
+#else
+#define GCCUSESGAS
+#define PCVT_STANDALONE 1
+#endif
+
+#define SEQREG 0x03C4
+#define MISCREG 0x03C2
+#define MISCREAD 0x03CC
+
+double fref = 14.31818 * 2.0;
+char ascclk[] = "VIDEO CLOCK ?";
+
+unsigned short clknum;
+unsigned short vlbus_flag;
+unsigned short card;
+unsigned short crtcaddr;
+unsigned short clockreg;
+
+static double range[15] = {50.0, 51.0, 53.2, 58.5, 60.7, 64.4, 66.8, 73.5,
+ 75.6, 80.9, 83.2, 91.5, 100.0, 120.0, 120.0};
+
+#ifdef __STDC__
+static double genratio(unsigned int *p, unsigned int *q, double tgt);
+static double f(unsigned int p, unsigned int q, double basefreq);
+#if 0
+static void prtbinary(unsigned int size, unsigned int val);
+#endif
+static void wait_vb();
+static void wrt_clk_bit(unsigned int value);
+static void init_clock(unsigned long setup, unsigned short crtcport);
+#else
+static double genratio();
+static double f();
+#if 0
+static void prtbinary();
+#endif
+static void wait_vb();
+static void wrt_clk_bit();
+static void init_clock();
+#endif
+
+void AltICD2061SetClock(frequency, select)
+register long frequency; /* in Hz */
+int select;
+{
+ unsigned int m, mval, ival;
+ int i;
+ long dwv;
+ double realval;
+ double freq, fvco;
+ double dev, devx;
+ double delta, deltax;
+ unsigned int p, q;
+ unsigned int bestp, bestq;
+ unsigned char tmp;
+
+ crtcaddr=(inb(0x3CC) & 0x01) ? 0x3D4 : 0x3B4;
+
+
+ outb(crtcaddr, 0x11); /* Unlock CRTC registers */
+ tmp = inb(crtcaddr + 1);
+ outb(crtcaddr + 1, tmp & ~0x80);
+
+ outw(crtcaddr, 0x4838); /* Unlock S3 register set */
+ outw(crtcaddr, 0xA039);
+
+ clknum = select;
+
+ freq = ((double)frequency)/1000000.0;
+ if (freq > range[14])
+ freq =range[14];
+ else if (freq <= 6.99)
+ freq = 7.0;
+
+/*
+ * Calculate values to load into ICD 2061A clock chip to set frequency
+ */
+ delta = 999.0;
+ dev = 999.0;
+ ival = 99;
+ mval = 99;
+
+ fvco = freq / 2;
+ for (m = 0; m < 8; m++) {
+ fvco *= 2.0;
+ for (i = 14; i >= 0; i--)
+ if (fvco >= range[i])
+ break;
+ if (i < 0)
+ continue;
+ if (i == 14)
+ break;
+ devx = (fvco - (range[i] + range[i+1])/2)/fvco;
+ if (devx < 0)
+ devx = -devx;
+ deltax = genratio(&p, &q, fvco);
+ if (delta < deltax)
+ continue;
+ if (deltax < delta || devx < dev) {
+ bestp = p;
+ bestq = q;
+ delta = deltax;
+ dev = devx;
+ ival = i;
+ mval = m;
+ }
+ }
+ fvco = fref;
+ for (m=0; m<mval; m++)
+ fvco /= 2.0;
+ realval = f(bestp, bestq, fvco);
+ dwv = ((((((long)ival << 7) | bestp) << 3) | mval) << 7) | bestq;
+
+/*
+ * Write ICD 2061A clock chip
+ */
+ init_clock(((unsigned long)dwv) | (((long)clknum) << 21), crtcaddr);
+
+ wait_vb();
+ wait_vb();
+ wait_vb();
+ wait_vb();
+ wait_vb();
+ wait_vb();
+ wait_vb(); /* 0.10 second delay... */
+}
+
+static double f(p, q, base)
+ unsigned int p;
+ unsigned int q;
+ double base;
+ {
+ return(base * (p + 3)/(q + 2));
+ }
+
+static double genratio(p, q, tgt)
+ unsigned int *p;
+ unsigned int *q;
+ double tgt;
+ {
+ int k, m;
+ double test, mindiff;
+ unsigned int mmax;
+
+ mindiff = 999999999.0;
+ for (k = 13; k < 69; k++) { /* q={15..71}:Constraint 2 on page 14 */
+ m = 50.0*k/fref - 3;
+ if (m < 0)
+ m = 0;
+ mmax = 120*k/fref - 3; /* m..mmax is constraint 3 on page 14 */
+ if (mmax > 128)
+ mmax = 128;
+ while (m < mmax) {
+ test = f(m, k, fref) - tgt;
+ if (test < 0) test = -test;
+ if (mindiff > test) {
+ mindiff = test;
+ *p = m;
+ *q = k;
+ }
+ m++;
+ }
+ }
+ return (mindiff);
+ }
+
+#if 0
+static void prtbinary(size, val)
+ unsigned int size;
+ unsigned int val;
+ {
+ unsigned int mask;
+ int k;
+
+ mask = 1;
+
+ for (k=size; --k > 0 || mask <= val/2;)
+ mask <<= 1;
+
+ while (mask) {
+ fputc((mask&val)? '1': '0' , stderr);
+ mask >>= 1;
+ }
+ }
+#endif
+
+static void wait_vb()
+ {
+ while ((inb(crtcaddr+6) & 0x08) == 0)
+ ;
+ while (inb(crtcaddr+6) & 0x08)
+ ;
+ }
+
+
+#ifdef __STDC__
+static void init_clock(unsigned long setup, unsigned short crtcport)
+#else
+static void init_clock(setup, crtcport)
+ unsigned long setup;
+ unsigned short crtcport;
+#endif
+ {
+ unsigned char nclk[2], clk[2];
+ unsigned short restore42;
+ unsigned short oldclk;
+ unsigned short bitval;
+ int i;
+ unsigned char c;
+
+#ifndef PCVT_STANDALONE
+ (void)xf86DisableInterrupts();
+#endif
+
+ oldclk = inb(0x3CC);
+
+ outb(crtcport, 0x42);
+ restore42 = inb(crtcport+1);
+
+ outw(0x3C4, 0x0100);
+
+ outb(0x3C4, 1);
+ c = inb(0x3C5);
+ outb(0x3C5, 0x20 | c);
+
+ outb(crtcport, 0x42);
+ outb(crtcport+1, 0x03);
+
+ outw(0x3C4, 0x0300);
+
+ nclk[0] = oldclk & 0xF3;
+ nclk[1] = nclk[0] | 0x08;
+ clk[0] = nclk[0] | 0x04;
+ clk[1] = nclk[0] | 0x0C;
+
+ outb(crtcport, 0x42);
+ i = inw(crtcport);
+
+ outw(0x3C4, 0x0100);
+
+ wrt_clk_bit(oldclk | 0x08);
+ wrt_clk_bit(oldclk | 0x0C);
+ for (i=0; i<5; i++) {
+ wrt_clk_bit(nclk[1]);
+ wrt_clk_bit(clk[1]);
+ }
+ wrt_clk_bit(nclk[1]);
+ wrt_clk_bit(nclk[0]);
+ wrt_clk_bit(clk[0]);
+ wrt_clk_bit(nclk[0]);
+ wrt_clk_bit(clk[0]);
+ for (i=0; i<24; i++) {
+ bitval = setup & 0x01;
+ setup >>= 1;
+ wrt_clk_bit(clk[1-bitval]);
+ wrt_clk_bit(nclk[1-bitval]);
+ wrt_clk_bit(nclk[bitval]);
+ wrt_clk_bit(clk[bitval]);
+ }
+ wrt_clk_bit(clk[1]);
+ wrt_clk_bit(nclk[1]);
+ wrt_clk_bit(clk[1]);
+
+ outb(0x3C4, 1);
+ c = inb(0x3C5);
+ outb(0x3C5, 0xDF & c);
+
+ outb(crtcport, 0x42);
+ outb(crtcport+1, restore42);
+
+ outb(0x3C2, oldclk);
+
+ outw(0x3C4, 0x0300);
+
+#ifndef PCVT_STANDALONE
+ xf86EnableInterrupts();
+#endif
+
+ }
+
+static void wrt_clk_bit(value)
+ unsigned int value;
+ {
+ int j;
+
+ outb(0x3C2, value);
+ for (j=2; --j; )
+ inb(0x200);
+ }
diff --git a/usr.sbin/pcvt/set2061/Makefile b/usr.sbin/pcvt/set2061/Makefile
new file mode 100644
index 0000000..0cbeff7
--- /dev/null
+++ b/usr.sbin/pcvt/set2061/Makefile
@@ -0,0 +1,13 @@
+PROG= set2061
+SRCS= main.c ICD2061Aalt.c
+CFLAGS+= -DGCCUSESGAS -DPCVT_STANDALONE
+NOMAN=
+
+all: $(PROG)
+
+install: ${DEMOS}
+ @${ECHO} "set2061 is not installed automatically ...."
+
+.include <bsd.prog.mk>
+
+$(PROG): compiler.h
diff --git a/usr.sbin/pcvt/set2061/README b/usr.sbin/pcvt/set2061/README
new file mode 100644
index 0000000..76109f9
--- /dev/null
+++ b/usr.sbin/pcvt/set2061/README
@@ -0,0 +1,22 @@
+CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION
+
+ THE USE OF THIS PROGRAM MAY DESTROY YOUR MONITOR !!!
+ ====================================================
+
+ IF YOU DON'T KNOW WHAT YOU ARE DOING, STAY AWAY FROM IT !!!
+ ===========================================================
+
+Read the file "CAUTION" before proceeding !!!
+
+The files:
+
+ ICD2061Aalt.c and
+ compiler.h
+
+come from the Xfree86 2.1 distribution and have been slightly modified to
+fit in a non-XFree environment.
+
+I use it to program the clock generator ICD2061a on my S3 928 based ELSA
+Winner VGA board to 40MHz for clock generator #2: set2061 -n2 -f40000000.
+
+This enables me to use 132 columns mode on this VGA board.
diff --git a/usr.sbin/pcvt/set2061/compiler.h b/usr.sbin/pcvt/set2061/compiler.h
new file mode 100644
index 0000000..9fbdb56
--- /dev/null
+++ b/usr.sbin/pcvt/set2061/compiler.h
@@ -0,0 +1,341 @@
+/* $XFree86: mit/server/ddx/x386/common/compiler.h,v 2.3 1993/10/03 14:55:28 dawes Exp $ */
+/*
+ * Copyright 1990,91 by Thomas Roell, Dinkelscherben, Germany.
+ *
+ * Permission to use, copy, modify, distribute, and sell this software and its
+ * documentation for any purpose is hereby granted without fee, provided that
+ * the above copyright notice appear in all copies and that both that
+ * copyright notice and this permission notice appear in supporting
+ * documentation, and that the name of Thomas Roell not be used in
+ * advertising or publicity pertaining to distribution of the software without
+ * specific, written prior permission. Thomas Roell makes no representations
+ * about the suitability of this software for any purpose. It is provided
+ * "as is" without express or implied warranty.
+ *
+ * THOMAS ROELL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO
+ * EVENT SHALL THOMAS ROELL BE LIABLE FOR ANY SPECIAL, INDIRECT OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE,
+ * DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
+ * TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+ * PERFORMANCE OF THIS SOFTWARE.
+ *
+ * $Header: /home/ncvs/src/usr.sbin/pcvt/set2061/compiler.h,v 1.1.1.1 1995/02/05 13:49:25 jkh Exp $
+ */
+
+
+#ifndef _COMPILER_H
+#define _COMPILER_H
+
+#ifndef __STDC__
+# ifdef signed
+# undef signed
+# endif
+# ifdef volatile
+# undef volatile
+# endif
+# ifdef const
+# undef const
+# endif
+# define signed /**/
+# ifdef __GNUC__
+# define volatile __volatile__
+# define const __const__
+# else
+# define const /**/
+# endif /* __GNUC__ */
+#endif /* !__STDC__ */
+
+#ifdef NO_INLINE
+
+extern void outb();
+extern void outw();
+extern unsigned int inb();
+extern unsigned int inw();
+#if NeedFunctionPrototypes
+extern unsigned char rdinx(unsigned short, unsigned char);
+extern void wrinx(unsigned short, unsigned char, unsigned char);
+extern void modinx(unsigned short, unsigned char, unsigned char, unsigned char);
+extern int testrg(unsigned short, unsigned char);
+extern int textinx2(unsigned short, unsigned char, unsigned char);
+extern int textinx(unsigned short, unsigned char);
+#else /* NeedFunctionProtoypes */
+extern unsigned char rdinx();
+extern void wrinx();
+extern void modinx();
+extern int testrg();
+extern int textinx2();
+extern int textinx();
+#endif /* NeedFunctionProtoypes */
+
+#else /* NO_INLINE */
+
+#ifdef __GNUC__
+
+#ifndef FAKEIT
+#ifdef GCCUSESGAS
+
+/*
+ * If gcc uses gas rather than the native assembler, the syntax of these
+ * inlines has to be different. DHD
+ */
+
+static __inline__ void
+outb(port, val)
+short port;
+char val;
+{
+ __asm__ __volatile__("outb %0,%1" : :"a" (val), "d" (port));
+}
+
+
+static __inline__ void
+outw(port, val)
+short port;
+short val;
+{
+ __asm__ __volatile__("outw %0,%1" : :"a" (val), "d" (port));
+}
+
+static __inline__ unsigned int
+inb(port)
+short port;
+{
+ unsigned char ret;
+ __asm__ __volatile__("inb %1,%0" :
+ "=a" (ret) :
+ "d" (port));
+ return ret;
+}
+
+static __inline__ unsigned int
+inw(port)
+short port;
+{
+ unsigned short ret;
+ __asm__ __volatile__("inw %1,%0" :
+ "=a" (ret) :
+ "d" (port));
+ return ret;
+}
+
+#else /* GCCUSESGAS */
+
+static __inline__ void
+outb(port, val)
+ short port;
+ char val;
+{
+ __asm__ __volatile__("out%B0 (%1)" : :"a" (val), "d" (port));
+}
+
+static __inline__ void
+outw(port, val)
+ short port;
+ short val;
+{
+ __asm__ __volatile__("out%W0 (%1)" : :"a" (val), "d" (port));
+}
+
+static __inline__ unsigned int
+inb(port)
+ short port;
+{
+ unsigned char ret;
+ __asm__ __volatile__("in%B0 (%1)" :
+ "=a" (ret) :
+ "d" (port));
+ return ret;
+}
+
+static __inline__ unsigned int
+inw(port)
+ short port;
+{
+ unsigned short ret;
+ __asm__ __volatile__("in%W0 (%1)" :
+ "=a" (ret) :
+ "d" (port));
+ return ret;
+}
+
+#endif /* GCCUSESGAS */
+
+#else /* FAKEIT */
+
+static __inline__ void
+outb(port, val)
+ short port;
+ char val;
+{
+}
+
+static __inline__ void
+outw(port, val)
+ short port;
+ short val;
+{
+}
+
+static __inline__ unsigned int
+inb(port)
+ short port;
+{
+ return 0;
+}
+
+static __inline__ unsigned int
+inw(port)
+ short port;
+{
+ return 0;
+}
+
+#endif /* FAKEIT */
+
+#else /* __GNUC__ */
+#if !defined(AMOEBA) && !defined(_MINIX)
+# if defined(__STDC__) && (__STDC__ == 1)
+# define asm __asm
+# endif
+# ifdef SVR4
+# include <sys/types.h>
+# ifndef __USLC__
+# define __USLC__
+# endif
+# endif
+# include <sys/inline.h>
+#endif
+#endif
+
+/*
+ *-----------------------------------------------------------------------
+ * Port manipulation convenience functions
+ *-----------------------------------------------------------------------
+ */
+
+#ifndef __GNUC__
+#define __inline__ /**/
+#endif
+
+/*
+ * rdinx - read the indexed byte port 'port', index 'ind', and return its value
+ */
+static __inline__ unsigned char
+#ifdef __STDC__
+rdinx(unsigned short port, unsigned char ind)
+#else
+rdinx(port, ind)
+unsigned short port;
+unsigned char ind;
+#endif
+{
+ if (port == 0x3C0) /* reset attribute flip-flop */
+ (void) inb(0x3DA);
+ outb(port, ind);
+ return(inb(port+1));
+}
+
+/*
+ * wrinx - write 'val' to port 'port', index 'ind'
+ */
+static __inline__ void
+#ifdef __STDC__
+wrinx(unsigned short port, unsigned char ind, unsigned char val)
+#else
+wrinx(port, ind, val)
+unsigned short port;
+unsigned char ind, val;
+#endif
+{
+ outb(port, ind);
+ outb(port+1, val);
+}
+
+/*
+ * modinx - in register 'port', index 'ind', set the bits in 'mask' as in 'new';
+ * the other bits are unchanged.
+ */
+static __inline__ void
+#ifdef __STDC__
+modinx(unsigned short port, unsigned char ind,
+ unsigned char mask, unsigned char new)
+#else
+modinx(port, ind, mask, new)
+unsigned short port;
+unsigned char ind, mask, new;
+#endif
+{
+ unsigned char tmp;
+
+ tmp = (rdinx(port, ind) & ~mask) | (new & mask);
+ wrinx(port, ind, tmp);
+}
+
+/*
+ * tstrg - returns true iff the bits in 'mask' of register 'port' are
+ * readable & writable.
+ */
+
+static __inline__ int
+#ifdef __STDC__
+testrg(unsigned short port, unsigned char mask)
+#else
+tstrg(port, mask)
+unsigned short port;
+unsigned char mask;
+#endif
+{
+ unsigned char old, new1, new2;
+
+ old = inb(port);
+ outb(port, old & ~mask);
+ new1 = inb(port) & mask;
+ outb(port, old | mask);
+ new2 = inb(port) & mask;
+ outb(port, old);
+ return((new1 == 0) && (new2 == mask));
+}
+
+/*
+ * testinx2 - returns true iff the bits in 'mask' of register 'port', index
+ * 'ind' are readable & writable.
+ */
+static __inline__ int
+#ifdef __STDC__
+testinx2(unsigned short port, unsigned char ind, unsigned char mask)
+#else
+testinx2(port, ind, mask)
+unsigned short port;
+unsigned char ind, mask;
+#endif
+{
+ unsigned char old, new1, new2;
+
+ old = rdinx(port, ind);
+ wrinx(port, ind, old & ~mask);
+ new1 = rdinx(port, ind) & mask;
+ wrinx(port, ind, old | mask);
+ new2 = rdinx(port, ind) & mask;
+ wrinx(port, ind, old);
+ return((new1 == 0) && (new2 == mask));
+}
+
+/*
+ * testinx - returns true iff all bits of register 'port', index 'ind' are
+ * readable & writable.
+ */
+static __inline__ int
+#ifdef __STDC__
+testinx(unsigned short port, unsigned char ind)
+#else
+testinx(port, ind, mask)
+unsigned short port;
+unsigned char ind;
+#endif
+{
+ return(testinx2(port, ind, 0xFF));
+}
+
+#endif /* NO_INLINE */
+#endif /* _COMPILER_H */
diff --git a/usr.sbin/pcvt/set2061/main.c b/usr.sbin/pcvt/set2061/main.c
new file mode 100644
index 0000000..49862fa
--- /dev/null
+++ b/usr.sbin/pcvt/set2061/main.c
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 1994 Hellmuth Michaelis
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by
+ * Hellmuth Michaelis
+ * 4. The name authors may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+static char *id =
+ "@(#)set2061.c, 1.00, Last Edit-Date: [Sun Jan 15 19:52:05 1995]";
+
+/*---------------------------------------------------------------------------*
+ *
+ * history:
+ *
+ * -hm start using 132 columns on my Elsa Winner
+ *
+ *---------------------------------------------------------------------------*/
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <machine/pcvt_ioctl.h>
+
+#define DEFAULTFD 0
+
+void AltICD2061SetClock(long frequency, int select);
+
+main(argc,argv)
+int argc;
+char *argv[];
+{
+ extern int optind;
+ extern int opterr;
+ extern char *optarg;
+
+ int fd;
+ int c;
+ long freq = -1;
+ int no = -1;
+
+ while( (c = getopt(argc, argv, "f:n:")) != -1)
+ {
+ switch(c)
+ {
+ case 'f':
+ freq = atoi(optarg);
+ break;
+
+ case 'n':
+ no = atoi(optarg);
+ break;
+
+ case '?':
+ default:
+ usage();
+ break;
+ }
+ }
+
+ if(freq == -1 || no == -1)
+ usage();
+
+ if((fd = open("/dev/console", O_RDONLY)) < 0)
+ fd = DEFAULTFD;
+
+ if(ioctl(fd, KDENABIO, 0) < 0)
+ {
+ perror("ioctl(KDENABIO)");
+ return 1;
+ }
+
+ AltICD2061SetClock(freq, no);
+
+ (void)ioctl(fd, KDDISABIO, 0);
+
+ exit(0);
+}
+
+usage()
+{
+ fprintf(stderr,"\nset2061 - program the ICD2061 video clock chip\n");
+ fprintf(stderr,"usage: set2061 -f <freq> -n <no>\n");
+ fprintf(stderr," -f <freq> frequency in Hz\n");
+ fprintf(stderr," -n <no> clock generator number\n");
+ exit(1);
+}
+
diff --git a/usr.sbin/pcvt/userkeys/Makefile b/usr.sbin/pcvt/userkeys/Makefile
new file mode 100644
index 0000000..b405546
--- /dev/null
+++ b/usr.sbin/pcvt/userkeys/Makefile
@@ -0,0 +1,18 @@
+
+.if !exists(${.CURDIR}/../Makefile.inc)
+error:
+ @echo
+ @echo " You MUST link/copy"
+ @echo
+ @echo " ../Makefile.inc.NetBSD or ../Makefile.inc.FreeBSD"
+ @echo
+ @echo " to ../Makefile.inc before you can proceed !"
+ @echo
+.else
+
+PROG= vt220keys
+CLEANFILES+= core.vt220keys vt220keys.core
+
+.include <bsd.prog.mk>
+
+.endif
diff --git a/usr.sbin/pcvt/userkeys/vt220keys.1 b/usr.sbin/pcvt/userkeys/vt220keys.1
new file mode 100644
index 0000000..282adec
--- /dev/null
+++ b/usr.sbin/pcvt/userkeys/vt220keys.1
@@ -0,0 +1,131 @@
+.TH VT220KEYS 1
+.UC 4
+.SH NAME
+vt220keys \- define SHIFTED function keys on VT220 terminal
+.SH SYNTAX
+.B vt220keys
+[
+.B \-cil
+]
+[ keyname keystring ] ...
+.SH DESCRIPTION
+.I Vt220keys
+sets up a "vt220 terminal" in vt200 mode to allow user
+definition of the SHIFTED function keys. Each
+\f2keyname\f1 specified on the command line will be loaded with
+the corresponding \f2keystring\f1.
+A \f2keyname\f1 is one of the following "words":
+F6 F7 F8 F9 F10 F11 ESC F12 BS F13 LF F14 HELP DO F17 F18 F19 F20.
+\f2Keystrings\f1
+must be quoted if spaces, tabs, or shell metacharacters are included.
+.PP
+.B Vt220keys
+expects to receive some combination of option flags and/or
+argument pair(s), otherwised an usage message
+is printed.
+.PP
+The options are:
+.TP
+.B \-c
+Clears all SHIFTED function key definitions before setting them to user
+defined strings.
+.TP
+.B \-i
+Read the initialization file
+.I $HOME/.vt220rc
+for SHIFTED function key definitions. This is done before any
+argument pair specified on the command line is processed.
+Each line in the file must consist of two fields (separated by spaces
+or tabs) where the first field is the
+\f2keyname\f1 and the second field is the \f2keystring\f1.
+The second field extends to the end of the line, thus a
+\f2keystring\f1
+may include spaces or tabs. A newline (return) may be specified
+within the string by using the C Language notation for newline (\\n).
+.TP
+.B \-l
+Locks the function keys from further definition.
+Locking occurs after processing the initialization file (if the "i"
+option is specified) and any argument
+pairs.
+The only way
+to unlock is by turning the power off.
+.SH EXAMPLES
+vt220keys -ci
+.br
+vt220keys F6 'nroff -ms '
+.br
+vt220keys -i F20 'cc -O -c '
+.br
+vt220keys -l HELP man
+.SH "OTHER FEATURES"
+Pressing the function keys without using the shift key, generates
+a string of characters. With
+\f2csh\f1(1) this string can be aliased to some command. For example:
+.br
+ alias ^[[17~ "ls -CR | more"
+.br
+where ^[[17~ is what is generated by pressing the F6 key. Therefore
+F6 can perform two commands, depending if pressed with/without the SHIFT
+key.
+.PP
+.B Vt220keys
+can be called from your .login or .profile file. Typically an user
+will create a initialization file and include a line like
+.br
+ vt220keys -ci
+.br
+OR
+.br
+ vt220keys -cil
+.br
+in the above mentioned files. This way the SHIFTED function keys
+will be set to your favorite commands when logging in.
+.SH CAVEATS
+If the SHIFTED function keys are unlocked, redefinition of a SHIFTED
+function key will rewrite the old string.
+.PP
+There are 256 bytes available for the SHIFTED function keys. Space is
+supplied on a first-come/first-serve basis. After the 256 bytes are
+used, you can't define any more keys unless space is cleared. This
+can be done by redefining a key to contain a string of fewer bytes.
+.PP
+All key definitions are stored in volatile RAM, and are lost when
+terminal power is lost.
+.PP
+The ESC key (unshifted) no longer generates the proper escape character. This
+is of particular importance since many editors require use of the
+ESC key. Here are some available alternatives:
+.sp
+.in +.5i
+The escape character can be generated by typing ^[ (control-[).
+.sp
+Use
+.B vt220keys
+as follows (note ^[ is control-[)
+.br
+.in +.5i
+vt220keys ESC '^['
+.in
+.br
+This will require you
+to press the SHIFT key and ESC to generate the escape sequence.
+.sp
+Some editors, allow other character(s) to be substituted for the
+escape character. For example with
+.B emacs
+include this line in your .emacs_pro:
+.br
+ (bind-to-key "ESC-prefix" "\\033[23~")
+.br
+Thus when the ESC key is pressed, emacs will allow the characters
+generated (^[[23~) to perform the same function as the escape
+character.
+.in
+.SH FILES
+$HOME/.vt220rc \- initialization file
+.SH "SEE ALSO"
+VT220 Programmer Reference Manual
+.br
+VT220 Programmer Pocket Guide
+
diff --git a/usr.sbin/pcvt/userkeys/vt220keys.c b/usr.sbin/pcvt/userkeys/vt220keys.c
new file mode 100644
index 0000000..55664c5
--- /dev/null
+++ b/usr.sbin/pcvt/userkeys/vt220keys.c
@@ -0,0 +1,297 @@
+/*
+ * Trivial program to load VT220 Function keys with strings,
+ * note that the values only get sent when the key is shifted
+ * (shoulda been an option to flip the shift set like the Z19!)
+ *
+ * Typing no args gives help, basically pairs of keyname/value
+ * strings.
+ *
+ * Author, Author: Barry Shein, Boston University
+ *
+ * HISTORY
+ {1} 30-Oct-85 Kenneth J. Lester (ken) at ektools
+
+ Added the necessary code to read an initialization file. This
+ should make it easier to used this program. Also added code
+ that will set-up the terminal in vt200 (this saves the user the
+ trouble of checking if the set-up is in vt200).
+
+ Restructed the main function to use getopt, for argument
+ processing.
+
+ Alterated usage function to include new "i" option (init file)
+
+
+ -hm minor modifications for pcvt 2.0 release
+
+*/
+
+#include <stdio.h>
+#include <ctype.h>
+
+/*
+ * The default toupper() macro is stupid, will toupper anything
+ */
+
+#ifdef toupper
+#undef toupper
+#endif
+#define toupper(c) (islower(c) ? ((c)-' ') : c)
+
+#define VT200_7BIT 1
+#define ESC 033
+#define INITFILE ".vt220rc"
+
+struct keynames {
+ char *name ;
+ char *string ;
+} keys[] = {
+ "F6", "17",
+ "F7", "18",
+ "F8", "19",
+ "F9", "20",
+ "F10", "21",
+ "F11", "23",
+ "ESC", "23",
+ "F12", "24",
+ "BS", "24",
+ "F13", "25",
+ "LF", "25",
+ "F14", "26",
+ "HELP", "28",
+ "DO", "29",
+ "F17", "31",
+ "F18", "32",
+ "F19", "33",
+ "F20", "34",
+ NULL, NULL
+};
+
+char prog[BUFSIZ];
+
+main(argc,argv)
+ int argc;
+ char *argv[];
+{
+ /* these are defined in the getopt routine */
+ extern char *optarg; /* argument give to an option */
+ extern int optind; /* argv index after option processing */
+
+ int option; /* option character returned by getopt */
+ int initf = 0; /* read initialization file */
+ int lockf = 0; /* lock keys after loading strings */
+ int clearf = 0; /* clear all keys before loading strings */
+ char *strcpy();
+
+ (void) strcpy(prog, *argv); /* store program name */
+
+ if(argc == 1) usage(); /* program requires options */
+
+ /* get options */
+ while ((option = getopt(argc, argv, "cli")) != -1)
+ switch(option)
+ {
+ case 'c' :
+ clearf++;
+ break;
+ case 'l' :
+ lockf++;
+ break;
+ case 'i' :
+ initf++;
+ break;
+ case '?' :
+ usage();
+ }
+
+ if (VT200_7BIT)
+ printf("\033[62;1\"p"); /* vt200 7 bits */
+ else
+ printf("\033[62;2\"p"); /* vt200 8 bits */
+
+ if(clearf) clearkeys();
+
+ if (initf) getinit();
+
+ /* process {key, key string} pairs. Note optind is index to argv
+ for first pair. By adding 1 to optind insures that a pair exists
+ i.e. the last key has a key string. */
+
+ while(optind + 1 < argc)
+ {
+ dokey(argv[optind], argv[optind+1]);
+ optind += 2;
+ }
+
+ if(lockf) lockkeys();
+
+ exit(0);
+}
+
+/****************************************************************************/
+
+/*
+ * Load the VT220 SHIFT-FNKEY value, the basic pattern is
+ * "\EP1;1|"+KEYNAME+"/"+VAL_AS_HEX+"\E\\"
+ * that is, literally what is in quotes (w/o quotes) then the
+ * name of the key from the keytable above (a numeric string)
+ * then a slash, then the string value as hex pairs then ESC-BACKSLASH
+ *
+ * Note: you can gang together key defns with semicolons but that
+ * would complicate things, especially error handling, so do it all
+ * for each pair, who cares, really.
+ */
+
+dokey(nm,val) char *nm, *val;
+{
+ register char *scr;
+ register struct keynames *kp;
+
+ for(scr = nm; *scr = toupper(*scr); scr++)
+ ;
+ for(kp = keys; kp->name != NULL; kp++)
+ if(strcmp(nm,kp->name) == 0) {
+ printf("%cP1;1|%s/",ESC,kp->string);
+ while(*val) printf("%02x",*val++);
+ printf("%c\\",ESC);
+ fflush(stdout);
+ return;
+ }
+ fprintf(stderr,"Bad key name: %s\n",nm);
+ usage(); /* bad key name, give up */
+}
+
+/****************************************************************************/
+
+clearkeys()
+{
+ printf("%cP0;1|%c\\",ESC,ESC);
+ fflush(stdout);
+}
+
+/****************************************************************************/
+
+lockkeys()
+{
+ printf("%cP1;0|%c\\",ESC,ESC);
+ fflush(stdout);
+}
+
+/****************************************************************************/
+
+usage()
+{
+ int i;
+
+ fprintf(stderr,"Usage: %s [-cil] [keyname string keyname string...]\n\n",prog);
+ fprintf(stderr,"The following options are available\n");
+ fprintf(stderr,"\t-c\tclears keys first\n");
+ fprintf(stderr,"\t-l\t[sets then] locks further setting\n");
+ fprintf(stderr,"\t-i\tfirst read initialization file $HOME/%s\n",INITFILE);
+ fprintf(stderr,"(note that the only way to unlock is via Set-Up)\n\n");
+ fprintf(stderr,"Keyname is one of:\n\t");
+ for(i=0; keys[i].name != NULL; i++)
+ fprintf(stderr,"%s ",keys[i].name);
+ fprintf(stderr,"\nKeyname is SHIFTED function key that sends the string\n\n");
+ fprintf(stderr,"Strings may need quoting to protect from shell\n");
+ fprintf(stderr,"You must specify an option or key,string pairs\n\n");
+ exit(1);
+}
+
+/****************************************************************************/
+
+/* This routine process the INITFILE. This file expects lines in the format
+
+ <ws> keyname ws string
+
+ Where ws is white space (spaces or tabs) and <ws> is optional white space.
+ The string may include spaces or tabs and need not be quoted. If the
+ string has the sequence of "\n" then a newline character is included in
+ the string.
+
+ examples:
+
+ F6 ls -lg\n
+ F7 uulog -s
+
+*/
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+getinit()
+{
+ char *home; /* user's home directory */
+ char path[BUFSIZ]; /* full path name of init file */
+ char buf[BUFSIZ]; /* buffer to hold 1 line from init file */
+ char key[BUFSIZ]; /* buffer, to hold specified fcn key */
+ char keystr[BUFSIZ]; /* string associated with fcn key */
+ char *ptr; /* pointer to transverse buf */
+ int i, j; /* array indices */
+ int statflag; /* whether init file is regular & readable */
+ struct stat statbuf; /* stat of the init file */
+ FILE *fp; /* file pointer to init file */
+
+ /* system calls and subroutines */
+ FILE *fopen();
+ char *strcpy();
+ char *strcat();
+ char *fgets();
+ char *getenv();
+
+ /* construct full path name for init file */
+ home = getenv("HOME");
+ (void) strcpy(path, home);
+ (void) strcat(path,"/");
+ (void) strcat(path,INITFILE);
+
+ /* check status if init file */
+ if (stat(path, &statbuf) != -1)
+ {
+ statflag = statbuf.st_mode & S_IFREG && statbuf.st_mode & S_IREAD;
+ if (!statflag || (fp = fopen(path, "r")) == NULL)
+ {
+ fprintf(stderr, "couldn't open initalization file: %s\n", path);
+ exit(1);
+ }
+
+ /* process lines from init file */
+ while (fgets(buf, BUFSIZ, fp) != NULL)
+ {
+ /* variable initializations */
+ i = 0; j = 0;
+ key[0] = '\0'; keystr[0] = '\0';
+ ptr = buf;
+
+ while (*ptr == ' ' || *ptr == '\t') ptr++; /*skip whitespace*/
+
+ if (*ptr == '\n') break; /* we hit an emtpy line */
+
+ while (!isspace(*ptr) && *ptr != '\0') /* get keyname */
+ key[i++] = *ptr++;
+ key[i] = '\0'; /* place EOS in buffer */
+
+ while (*ptr == ' ' || *ptr == '\t') ptr++; /*skip whitespace*/
+
+ while (*ptr != '\n' && *ptr != '\0') /* get string */
+ {
+ /* check if string is to include newline i.e. \n */
+ if (*ptr == '\\' && *(ptr+1) == 'n')
+ {
+ keystr[j] = '\012';
+ ptr++;
+ }
+ else
+ keystr[j] = *ptr;
+ j++; ptr++;
+ }
+ keystr[j] = '\0'; /* place EOS in buffer */
+ dokey(key, keystr); /* load key with string */
+ }
+ }
+ else
+ {
+ fprintf(stderr, "init file %s not found\n\n", path);
+ usage();
+ }
+}
diff --git a/usr.sbin/pcvt/vgaio/CAUTION b/usr.sbin/pcvt/vgaio/CAUTION
new file mode 100644
index 0000000..e1eba06
--- /dev/null
+++ b/usr.sbin/pcvt/vgaio/CAUTION
@@ -0,0 +1,28 @@
+CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION
+CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION
+CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION
+
+ THE USE OF THIS PROGRAM MAY DESTROY YOUR MONITOR !!!
+ ====================================================
+
+ IF YOU DON'T KNOW WHAT YOU ARE DOING, STAY AWAY FROM IT !!!
+ ===========================================================
+
+ THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION
+CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION
+CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION CAUTION
+
diff --git a/usr.sbin/pcvt/vgaio/Makefile b/usr.sbin/pcvt/vgaio/Makefile
new file mode 100644
index 0000000..53083e0
--- /dev/null
+++ b/usr.sbin/pcvt/vgaio/Makefile
@@ -0,0 +1,37 @@
+.if !exists(${.CURDIR}/../Makefile.inc)
+error:
+ @echo
+ @echo " You MUST link/copy"
+ @echo
+ @echo " ../Makefile.inc.NetBSD or ../Makefile.inc.FreeBSD"
+ @echo
+ @echo " to ../Makefile.inc before you can proceed !"
+ @echo
+.else
+
+PROG= vgaio
+MAN8= vgaio.${MAN8EXT}
+
+SRCS= vgaio.c lex.c
+YACC= yacc
+
+#YFLAGS+= -yd # Bison
+#YFLAGS+= -v # verbose
+
+LFLAGS+= -I
+
+DPADD= ${LIBM} ${LIBY} ${LIBL}
+LDADD= -lm -ly -ll
+
+CFLAGS= -O2 # due to a gcc bug, it compiles only with -O2!
+CFLAGS+= -I${.OBJDIR} -I${.CURDIR}
+
+CLEANFILES+= lex.c lex.yy.c vgaio.c y.tab.[ch]
+CLEANFILES+= y.output # comment file from bison
+
+install:
+ @${ECHO} "vgaio is not installed automatically ...."
+
+.include <bsd.prog.mk>
+
+.endif
diff --git a/usr.sbin/pcvt/vgaio/lex.l b/usr.sbin/pcvt/vgaio/lex.l
new file mode 100644
index 0000000..5ff8622
--- /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 "$Header: /b/source/CVS/src/sys/arch/i386/isa/pcvt/Util/vgaio/lex.l,v 1.1 1994/03/29 02:47:20 mycroft Exp $"
+
+/*
+ * $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..aa0c93c
--- /dev/null
+++ b/usr.sbin/pcvt/vgaio/vgaio.8
@@ -0,0 +1,144 @@
+.\"
+.\" 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: /b/source/CVS/src/sys/arch/i386/isa/pcvt/Util/vgaio/vgaio.8,v 1.1 1994/03/29 02:47:23 mycroft Exp $
+.\" -hm updated 31.12.94
+.\"
+.Dd December 31, 1994
+.Dt VGAIO 8
+.Sh NAME
+.Nm vgaio
+.Nd perform input/output on a Video Graphics Array
+.Sh SYNOPSIS
+.Nm vgaio
+.Op Fl d
+.Sh DESCRIPTION
+.Ss Purpose
+.Nm Vgaio
+is used to perform register-level input/output on a Video Graphics Array.
+Since some of the sequences required to access those registers are very
+silly,
+.Nm vgaio
+cares of all the things necessary and allows the user to access the
+registers of several register groups with their symbolic names.
+
+.Ss Options
+.Bl -tag -width 10n -offset indent
+.It Fl d
+Turn on the grammar parser debugger.
+
+.El
+.Ss Command language
+The command language of
+.Nm
+constitutes of some very simple tokens and rules. Commands are executed
+line by line as they are entered. Each line may contain any number of
+semicolon-separated input/output commands.
+
+Symbolic register names look like:
+
+.D1 Ao Em reggroup Ac Ao Em regnumber Ac
+
+with
+.Aq Em regnumber
+being any hexadecimal number
+.Pq without a leading Em 0x ,
+and
+.Aq Em reggroup
+one of the strings
+.Dq Em ar ,
+.Dq Em cr ,
+.Dq Em gr ,
+.Dq Em mi ,
+or
+.Dq Em sr ,
+standing for the
+.Em Attribute controller ,
+.Em CRT controller ,
+.Em Graphics controller ,
+.Em Miscellaneous Output Register ,
+or
+.Em Timing sequencer ,
+respectively.
+
+An input instruction has the form
+
+.D1 Ao Em regname Ac ?
+
+and will cause
+.Nm
+to output a line like
+
+.Bd -ragged -offset indent
+.Ao Em regname Ac \& = 0x Ns
+.Aq Em number
+.Ed
+
+An output instruction looks like
+
+.Bd -ragged -offset indent
+.Ao Em regname Ac =
+.Aq Em number
+.Ed
+
+Spaces or Tabs between the
+.Aq Em reggroup ,
+the
+.Aq Em regnumber ,
+or any of the other tokens are ignored. They are not required anyway.
+
+The
+.Dq Em mi
+needs a single unused argument to satisfy the syntax :-) (-hm).
+
+
+.Ss Access control
+The caller must have uid 0 in order to gain the required access to
+the IO registers.
+
+.Sh HISTORY
+This program is considered
+.Dq hackware .
+It has been developed in order to simplify the process of developing other
+software that needs to program the Video Graphics Array.
+
+Remember, to use this program, your kernel has to be compiled with XSERVER
+being defined !
+
+.Sh AUTHOR
+The program has been contributed by
+.if n Joerg Wunsch,
+.if t J\(:org Wunsch,
+Dresden
+.Aq joerg_wunsch@uriah.sax.de .
+
diff --git a/usr.sbin/pcvt/vgaio/vgaio.h b/usr.sbin/pcvt/vgaio/vgaio.h
new file mode 100644
index 0000000..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..0e7dd79
--- /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 "$Header: /b/source/CVS/src/sys/arch/i386/isa/pcvt/Util/vgaio/vgaio.y,v 1.1 1994/03/29 02:47:27 mycroft Exp $"
+
+/*
+ * $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..3c6f089
--- /dev/null
+++ b/usr.sbin/pcvt/vttest/Makefile
@@ -0,0 +1,6 @@
+
+PROG= vttest
+CFLAGS+= -traditional -DUSEMYSTTY
+SRCS= main.c esc.c
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/pcvt/vttest/README b/usr.sbin/pcvt/vttest/README
new file mode 100644
index 0000000..589d08f
--- /dev/null
+++ b/usr.sbin/pcvt/vttest/README
@@ -0,0 +1,57 @@
+NOTES FROM THE MOD.SOURCES MODERATOR:
+I split the source up into the three separate pieces it now is.
+In doing this, I put lines like "int reading;" in a header file
+that both C modules include. If your Unix requires one of these
+to be "extern int reading;" then you will have some editing to do.
+Also note that this program uses FIONREAD, which must be implemented
+differently in SystemV, etc., and check out the setjmp() call...
+ /Rich $alz
+Oh, yeah: I also wrote the Makefile and manpage, such as they are.
+-------------------------------------------------------------------
+
+This is a program to test the compatibility (or to demonstrate the
+non-compatibility) of so-called "VT100-compatible" terminals. In
+conformance of the good old hacker traditions, the only documentation
+of this program is the source code itself. To understand it, you also
+need a copy of the original VT100 manual from DEC.
+
+Comments and bug reports: Since this is a release (via USENET) to the
+whole world, a lot of people are going to have opinions and fresh
+ideas about it. (What -- bugs in MY program? Aww...) I can't deal
+with everyone sending me a hacked version, but if you have found a
+serious bug, or ported it to VMS, do tell me. I can't promise any new
+version release, though. From this version on (1.7b) VTTEST will have
+to live its own life without its father holding its hand.
+
+My adress is:
+
+Network-mail adress: (mcvax,seismo)!enea!suadb!lindberg
+
+Real-world-mail address: Per Lindberg
+ QZ, Stockholm University Computing Center
+ Box 27322
+ S - 102 54 Stockholm
+ SWEDEN
+
+The original version of this program is written for the Sargasso C
+compiler for the DECsystem-10. Many thanks to all sales persons with
+quote VT100-compatible unquote terminals, who prompted me to write
+this program, and also to:
+
+-- Bo Kleve, LIDAC, Linkoping University, Sweden
+ for the portation to DECSYSTEM-20 with the Sargasso C compiler
+
+-- Johan Widen, TTDS, Royal Institute of Technology, Stockholm, Sweden
+ for the portation to various UNIX systems (incl. System III and Xenix)
+
+-- Russ Herman, AES Data Inc., Missisauga, Ont. Canada
+ for fixes and code for the VT102 test
+
+Thanx also to JMR "Gremlin" at KTH, and Goran Wallberg at QZ
+for suggestions, bug fixes, etc.
+
+This program does not have support for all the different variations
+of VT100, like VT125, VT131 nor the new VT200 series. Feel free to
+add that yourself. Happy Hacking!
+
+ /TMP
diff --git a/usr.sbin/pcvt/vttest/esc.c b/usr.sbin/pcvt/vttest/esc.c
new file mode 100644
index 0000000..b2bce92
--- /dev/null
+++ b/usr.sbin/pcvt/vttest/esc.c
@@ -0,0 +1,398 @@
+#include "header.h"
+
+println(s) char *s; {
+ printf("%s\n", s);
+}
+
+esc(s) char *s; {
+ printf("%c%s", 27, s);
+}
+
+esc2(s1, s2) char s1, s2; {
+ printf("%c%s%s", 27, s1, s2);
+}
+
+brcstr(ps, c) char *ps, c; {
+ printf("%c[%s%c", 27, ps, c);
+}
+
+brc(pn,c) int pn; char c; {
+ printf("%c[%d%c", 27, pn, c);
+}
+
+brc2(pn1, pn2 ,c) int pn1, pn2; char c; {
+ printf("%c[%d;%d%c", 27, pn1, pn2, c);
+}
+
+cub(pn) int pn; { /* Cursor Backward */
+ brc(pn,'D');
+}
+cud(pn) int pn; { /* Cursor Down */
+ brc(pn,'B');
+}
+cuf(pn) int pn; { /* Cursor Forward */
+ brc(pn,'C');
+}
+cup(pn1, pn2) int pn1, pn2; { /* Cursor Position */
+ brc2(pn1, pn2, 'H');
+}
+cuu(pn) int pn; { /* Cursor Up */
+ brc(pn,'A');
+}
+da() { /* Device Attributes */
+ brc(0,'c');
+}
+decaln() { /* Screen Alignment Display */
+ esc("#8");
+}
+decdhl(lower) int lower; { /* Double Height Line (also double width) */
+ if (lower) esc("#4");
+ else esc("#3");
+}
+decdwl() { /* Double Wide Line */
+ esc("#6");
+}
+deckpam() { /* Keypad Application Mode */
+ esc("=");
+}
+deckpnm() { /* Keypad Numeric Mode */
+ esc(">");
+}
+decll(ps) char *ps; { /* Load LEDs */
+ brcstr(ps, 'q');
+}
+decrc() { /* Restore Cursor */
+ esc("8");
+}
+decreqtparm(pn) int pn; { /* Request Terminal Parameters */
+ brc(pn,'x');
+}
+decsc() { /* Save Cursor */
+ esc("7");
+}
+decstbm(pn1, pn2) int pn1, pn2; { /* Set Top and Bottom Margins */
+ if (pn1 || pn2) brc2(pn1, pn2, 'r');
+ else esc("[r");
+ /* Good for >24-line terminals */
+}
+decswl() { /* Single With Line */
+ esc("#5");
+}
+dectst(pn) int pn; { /* Invoke Confidence Test */
+ brc2(2, pn, 'y');
+}
+dsr(pn) int pn; { /* Device Status Report */
+ brc(pn, 'n');
+}
+ed(pn) int pn; { /* Erase in Display */
+ brc(pn, 'J');
+}
+el(pn) int pn; { /* Erase in Line */
+ brc(pn,'K');
+}
+hts() { /* Horizontal Tabulation Set */
+ esc("H");
+}
+hvp(pn1, pn2) int pn1, pn2; { /* Horizontal and Vertical Position */
+ brc2(pn1, pn2, 'f');
+}
+ind() { /* Index */
+ esc("D");
+}
+nel() { /* Next Line */
+ esc("E");
+}
+ri() { /* Reverse Index */
+ esc("M");
+}
+ris() { /* Reset to Initial State */
+ esc("c");
+}
+rm(ps) char *ps; { /* Reset Mode */
+ brcstr(ps, 'l');
+}
+scs(g,c) int g; char c; { /* Select character Set */
+ printf("%c%c%c%c%c%c%c", 27, g ? ')' : '(', c,
+ 27, g ? '(' : ')', 'B',
+ g ? 14 : 15);
+}
+sgr(ps) char *ps; { /* Select Graphic Rendition */
+ brcstr(ps, 'm');
+}
+sm(ps) char *ps; { /* Set Mode */
+ brcstr(ps, 'h');
+}
+tbc(pn) int pn; { /* Tabulation Clear */
+ brc(pn, 'g');
+}
+
+vt52cup(l,c) int l,c; {
+ printf("%cY%c%c", 27, l + 31, c + 31);
+}
+
+char inchar() {
+
+ /*
+ * Wait until a character is typed on the terminal
+ * then read it, without waiting for CR.
+ */
+
+#ifdef UNIX
+ int lval, waittime, getpid(); static int val; char ch;
+
+ fflush(stdout);
+ lval = val;
+ brkrd = 0;
+ reading = 1;
+ read(0,&ch,1);
+ reading = 0;
+ if (brkrd)
+ val = 0177;
+ else
+ val = ch;
+ if ((val==0177) && (val==lval))
+ kill(getpid(), (int) SIGTERM);
+#endif
+#ifdef SARG10
+ int val, waittime;
+
+ waittime = 0;
+ while(!uuo(051,2,&val)) { /* TTCALL 2, (INCHRS) */
+ zleep(100); /* Wait 0.1 seconds */
+ if ((waittime += ttymode) > 600) /* Time-out, in case */
+ return('\177'); /* of hung in ttybin(1) */
+ }
+#endif
+#ifdef SARG20 /* try to fix a time-out function */
+ int val, waittime;
+
+ waittime = 0;
+ while(jsys(SIBE,2,_PRIIN) == 0) { /* Is input empty? */
+ zleep(100);
+ if ((waittime += ttymode) > 600)
+ return('\177');
+ }
+ ejsys(BIN,_PRIIN);
+ val = jsac[2];
+#endif
+ return(val);
+}
+
+char *instr() {
+
+ /*
+ * Get an unfinished string from the terminal:
+ * wait until a character is typed on the terminal,
+ * then read it, and all other available characters.
+ * Return a pointer to that string.
+ */
+
+
+ int i, val, crflag; long l1; char ch;
+ static char result[80];
+
+ i = 0;
+ result[i++] = inchar();
+/* Wait 0.1 seconds (1 second in vanilla UNIX) */
+#ifdef SARG10
+ if (trmop(01031,0) < 5) zleep(500); /* wait longer if low speed */
+ else zleep(100);
+#else
+ zleep(100);
+#endif
+#ifdef UNIX
+ fflush(stdout);
+#ifdef XENIX
+ while(rdchk(0)) {
+ read(0,result+i,1);
+ if (i++ == 78) break;
+ }
+#else
+#ifdef SIII
+ while(read(2,result+i,1) == 1)
+ if (i++ == 78) break;
+#else
+ while(ioctl(0,FIONREAD,&l1), l1 > 0L) {
+ while(l1-- > 0L) {
+ read(0,result+i,1);
+ if (i++ == 78) goto out1;
+ }
+ }
+out1:
+#endif
+#endif
+#endif
+#ifdef SARG10
+ while(uuo(051,2,&val)) { /* TTCALL 2, (INCHRS) */
+ if (!(val == '\012' && crflag)) /* TOPS-10 adds LF to CR */
+ result[i++] = val;
+ crflag = val == '\015';
+ if (i == 79) break;
+ zleep(50); /* Wait 0.05 seconds */
+ }
+#endif
+#ifdef SARG20
+ while(jsys(SIBE,2,_PRIIN) != 0) { /* read input until buffer is empty */
+ ejsys(BIN,_PRIIN);
+ result[i++] = jsac[2];
+ if (i == 79) break;
+ zleep(50); /* Wait 0.05 seconds */
+ }
+#endif
+ result[i] = '\0';
+ return(result);
+}
+
+ttybin(bin) int bin; {
+#ifdef SARG10
+ #define OPEN 050
+ #define IO_MOD 0000017
+ #define _IOPIM 2
+ #define _IOASC 0
+ #define _TOPAG 01021
+ #define _TOSET 01000
+
+ int v;
+ static int arglst[] = {
+ _IOPIM,
+ `TTY`,
+ 0
+ };
+ arglst[0] = bin ? _IOPIM : _IOASC;
+ v = uuo(OPEN, 1, &arglst[0]);
+ if (!v) { printf("OPEN failed"); exit(); }
+ trmop(_TOPAG + _TOSET, bin ? 0 : 1);
+ ttymode = bin;
+#endif
+#ifdef SARG20
+ /* TTYBIN will set the line in BINARY/ASCII mode
+ * BINARY mode is needed to send control characters
+ * Bit 28 must be 0 (we don't flip it).
+ * Bit 29 is used for the mode change.
+ */
+
+ #define _TTASC 0000100
+ #define _MOXOF 0000043
+
+ int v;
+
+ ejsys(RFMOD,_CTTRM);
+ v = ejsys(SFMOD,_CTTRM, bin ? (~_TTASC & jsac[2]) : (_TTASC | jsac[2]));
+ if (v) { printf("SFMOD failed"); exit(); }
+ v = ejsys(MTOPR,_CTTRM,_MOXOF,0);
+ if (v) { printf("MTOPR failed"); exit(); }
+#endif
+}
+
+#ifdef SARG20
+/*
+ * SUPERBIN turns off/on all input character interrupts
+ * This affects ^C, ^O, ^T
+ * Beware where and how you use it !!!!!!!
+ */
+
+superbin(bin) int bin; {
+ int v;
+
+ v = ejsys(STIW,(0//-5), bin ? 0 : -1);
+ if (v) { printf("STIW superbinary setting failed"); exit(); }
+ ttymode = bin;
+}
+
+/*
+ * PAGE affects the ^S/^Q handshake.
+ * Set bit 34 to turn it on. Clear it for off.
+ */
+
+page(bin) int bin; {
+ int v;
+
+ #define TT_PGM 0000002
+
+ ejsys(RFMOD,_CTTRM); /* Get the current terminal status */
+ v = ejsys(STPAR,_CTTRM, bin ? (TT_PGM | jsac[2]) : (~TT_PGM & jsac[2]));
+ if (v) { printf("STPAR failed"); exit(); }
+}
+#endif
+
+trmop(fc,arg) int fc, arg; {
+#ifdef SARG10
+ int retvalp;
+ int arglst[3];
+
+ /* TRMOP is a TOPS-10 monitor call that does things to the terminal. */
+
+ /* Find out TTY nbr (PA1050 barfs if TRMOP get -1 instead of udx) */
+ /* A TRMNO monitor call returns the udx (Universal Device Index) */
+
+ arglst[0] = fc; /* function code */
+ arglst[1] = calli(0115, -1); /* udx, TRMNO. UUO */
+ arglst[2] = arg; /* Optional argument */
+
+ if (calli(0116, 3 // &arglst[0], &retvalp)) /* TRMOP. UUO */
+ return (retvalp);
+ else {
+ printf("?Error return in TRMOP.");
+ exit();
+ }
+#endif
+}
+
+inputline(s) char *s; {
+ scanf("%s",s);
+#ifdef SARG10
+ readnl();
+#endif
+#ifdef SARG20
+ readnl();
+#endif
+}
+
+inflush() {
+
+ /*
+ * Flush input buffer, make sure no pending input character
+ */
+
+ int val;
+
+#ifdef UNIX
+#ifdef XENIX
+ while(rdchk(0)) read(0,&val,1);
+#else
+#ifdef SIII
+ while(read(2,&val,1));
+#else
+ long l1;
+ ioctl (0, FIONREAD, &l1);
+ while(l1-- > 0L) read(0,&val,1);
+#endif
+#endif
+#endif
+#ifdef SARG10
+ while(uuo(051,2,&val)) /* TTCALL 2, (INCHRS) */
+ ;
+#endif
+#ifdef SARG20
+ ejsys(CFIBF,_PRIIN); /* Clear input buffer */
+#endif
+}
+
+zleep(t) int t; {
+
+/*
+ * Sleep and do nothing (don't waste CPU) for t milliseconds
+ */
+
+#ifdef SARG10
+ calli(072,t); /* (HIBER) t milliseconds */
+#endif
+#ifdef SARG20
+ ejsys(DISMS,t); /* DISMISS for t milliseconds */
+#endif
+#ifdef UNIX
+ t = t / 1000;
+ if (t == 0) t = 1;
+ sleep(t); /* UNIX can only sleep whole seconds */
+#endif
+}
diff --git a/usr.sbin/pcvt/vttest/header.h b/usr.sbin/pcvt/vttest/header.h
new file mode 100644
index 0000000..300564a
--- /dev/null
+++ b/usr.sbin/pcvt/vttest/header.h
@@ -0,0 +1,43 @@
+#define VERSION "1.7b 1985-04-19"
+
+/* Choose one of these */
+
+/* #define XENIX */ /* XENIX implies UNIX */
+/* #define SIII */ /* SIII implies UNIX, (NDELAY a la System III) */
+#define UNIX /* UNIX */
+/* #define VMS */ /* VMS not done yet -- send me your version!!!! */
+/* #define SARG20 */ /* Sargasso C for TOPS-20 */
+/* #define SARG10 */ /* Sargasso C for TOPS-10 */
+
+/* These #ifdef:s are implementation dependent stuff for the Sargasso C */
+/* Unix C barfs on directives like "#strings", so we keep them */
+/* indented. Then unix c can't find them, but Sargasso C *can*. */
+/* Admittedly kludgey, but it works...) */
+#ifdef SARG10
+ #define _UNIXCON /* Make UNIX-flavored I/O on TOPS */
+ #strings low /* put strings in lowseg mem so we can modify them. */
+#endif
+#ifdef SARG20
+ #define _UNIXCON /* Make UNIX-flavored I/O on TOPS */
+ #strings low /* put strings in lowseg mem so we can modify them. */
+ #include <TOPS20.HDR>
+#endif
+
+#include <stdio.h>
+
+
+#ifdef UNIX
+#include <ctype.h>
+#include <sgtty.h>
+#include <signal.h>
+#include <setjmp.h>
+jmp_buf intrenv;
+struct sgttyb sgttyOrg, sgttyNew;
+char stdioBuf[BUFSIZ];
+int brkrd, reading;
+extern onterm(), onbrk();
+#ifdef SIII
+#include <fcntl.h>
+#endif
+#endif
+int ttymode;
diff --git a/usr.sbin/pcvt/vttest/main.c b/usr.sbin/pcvt/vttest/main.c
new file mode 100644
index 0000000..02cbc62
--- /dev/null
+++ b/usr.sbin/pcvt/vttest/main.c
@@ -0,0 +1,2016 @@
+/*
+ VTTEST.C
+
+ Written Novemeber 1983 - July 1984 by Per Lindberg,
+ Stockholm University Computer Center (QZ), Sweden.
+
+ THE MAD PROGRAMMER STRIKES AGAIN!
+
+ This software is (c) 1984 by QZ
+ Non-commercial use and copying allowed.
+
+If you are developing a commercial product, and use this program to do
+it, and that product is successful, please send a sum of money of your
+choice to the address below.
+
+*/
+
+#include "header.h"
+
+char inchar(), *instr(), *lookup();
+
+struct table {
+ int key;
+ char *msg;
+} paritytable[] = {
+ { 1, "NONE" },
+ { 4, "ODD" },
+ { 5, "EVEN" },
+ { -1, "" }
+},nbitstable[] = {
+ { 1, "8" },
+ { 2, "7" },
+ { -1,"" }
+},speedtable[] = {
+ { 0, "50" },
+ { 8, "75" },
+ { 16, "110" },
+ { 24, "132.5"},
+ { 32, "150" },
+ { 40, "200" },
+ { 48, "300" },
+ { 56, "600" },
+ { 64, "1200" },
+ { 72, "1800" },
+ { 80, "2000" },
+ { 88, "2400" },
+ { 96, "3600" },
+ { 104, "4800" },
+ { 112, "9600" },
+ { 120, "19200" },
+ { -1, "" }
+};
+
+#ifdef USEMYSTTY
+#ifndef stty
+int stty(fd,ptr)
+int fd;
+struct sgttyb *ptr;
+{
+ return(ioctl(fd, TIOCSETP, ptr));
+}
+#endif
+#ifndef gtty
+int gtty(fd,ptr)
+int fd;
+struct sgttyb *ptr;
+{
+ return(ioctl(fd, TIOCGETP, ptr));
+}
+#endif
+#endif
+
+main() {
+
+ int menuchoice;
+
+ static char *mainmenu[] = {
+ "Exit",
+ "Test of cursor movements",
+ "Test of screen features",
+ "Test of character sets",
+ "Test of double-sized characters",
+ "Test of keyboard",
+ "Test of terminal reports",
+ "Test of VT52 mode",
+ "Test of VT102 features (Insert/Delete Char/Line)",
+ "Test of known bugs",
+ "Test of reset and self-test",
+ ""
+ };
+
+#ifdef UNIX
+ initterminal(setjmp(intrenv));
+ signal(SIGINT, onbrk);
+ signal(SIGTERM, onterm);
+ reading = 0;
+#else
+ initterminal(0);
+#endif
+ do {
+#ifdef SARG20
+ ttybin(1); /* set line to binary mode again. It's reset somehow!! */
+#endif
+ ed(2);
+ cup(5,10); printf("VT100 test program, version %s", VERSION);
+ cup(7,10); println("Choose test type:");
+ menuchoice = menu(mainmenu);
+ switch (menuchoice) {
+ case 1: tst_movements(); break;
+ case 2: tst_screen(); break;
+ case 3: tst_characters(); break;
+ case 4: tst_doublesize(); break;
+ case 5: tst_keyboard(); break;
+ case 6: tst_reports(); break;
+ case 7: tst_vt52(); break;
+ case 8: tst_insdel(); break;
+ case 9: tst_bugs(); break;
+ case 10: tst_rst(); break;
+ }
+ } while (menuchoice);
+ bye();
+}
+
+tst_movements() {
+
+ /* Test of:
+ CUF (Cursor Forward)
+ CUB (Cursor Backward)
+ CUD (Cursor Down) IND (Index) NEL (Next Line)
+ CUU (Cursor Up) RI (Reverse Index)
+ CUP (Cursor Position) HVP (Horizontal and Vertical Position)
+ ED (Erase in Display)
+ EL (Erase in Line)
+ DECALN (Screen Alignment Display)
+ <CR> <BS>
+ Cursor control characters inside CSI sequences
+ */
+
+ int i, row, col, pass, width, hlfxtra;
+ char c, *ctext = "This is a correct sentence";
+
+ for (pass = 0; pass <= 1; pass++) {
+ if (pass == 0) { rm("?3"); width = 80; hlfxtra = 0; }
+ else { sm("?3"); width = 132; hlfxtra = 26; }
+
+ decaln();
+ cup( 9,10+hlfxtra); ed(1);
+ cup(18,60+hlfxtra); ed(0); el(1);
+ cup( 9,71+hlfxtra); el(0);
+ for (row = 10; row <= 16; row++) {
+ cup(row, 10+hlfxtra); el(1);
+ cup(row, 71+hlfxtra); el(0);
+ }
+ cup(17,30); el(2);
+ for (col = 1; col <= width; col++) {
+ hvp(24, col); printf("*");
+ hvp( 1, col); printf("*");
+ }
+ cup(2,2);
+ for (row = 2; row <= 23; row++) {
+ printf("+");
+ cub(1);
+ ind();
+ }
+ cup(23,width-1);
+ for (row = 23; row >=2; row--) {
+ printf("+");
+ cub(1); ri();
+ }
+ cup(2,1);
+ for (row = 2; row <= 23; row++) {
+ printf("*");
+ cup(row, width);
+ printf("*");
+ cub(10);
+ if(row < 10)
+ nel();
+ else
+ printf("\n");
+ }
+ cup(2,10);
+ cub(42+hlfxtra); cuf(2);
+ for (col = 3; col <= width-2; col++) {
+ printf("+");
+ cuf(0); cub(2); cuf(1);
+ }
+ cup(23,70+hlfxtra);
+ cuf(42+hlfxtra); cub(2);
+ for (col = width-2; col >= 3; col--) {
+ printf("+");
+ cub(1); cuf(1); cub(0); printf("%c", 8);
+ }
+ cup( 1, 1); cuu(10); cuu(1); cuu(0);
+ cup(24,width); cud(10); cud(1); cud(0);
+
+ cup(10,12+hlfxtra);
+ for (row = 10; row <= 15; row++) {
+ for (col = 12+hlfxtra; col <= 69+hlfxtra; col++) printf(" ");
+ cud(1); cub(58);
+ }
+ cuu(5); cuf(1);
+ printf("The screen should be cleared, and have an unbroken bor-");
+ cup(12,13+hlfxtra);
+ printf("der of *'s and +'s around the edge, and exactly in the");
+ cup(13,13+hlfxtra);
+ printf("middle there should be a frame of E's around this text");
+ cup(14,13+hlfxtra);
+ printf("with one (1) free position around it. ");
+ holdit();
+ }
+ rm("?3");
+
+ ed(2);
+ cup(1,1);
+ println("Test of cursor-control characters inside ESC sequences.");
+ println("Below should be two identical lines:");
+ println("");
+ println("A B C D E F G H I J K L M N O P Q R S");
+ for (i = 1; i < 20; i++) {
+ printf("%c", 64 + i);
+ brcstr("2\010", 'C'); /* Two forward, one backspace */
+ }
+ println("");
+ println("");
+ holdit();
+
+ ed(2);
+ cup(1,1);
+ println("Test of leading zeros in ESC sequences.");
+ printf("Two lines below you should see the sentence \"%s\".",ctext);
+ for (col = 1; *ctext; col++)
+ printf("\033[00000000004;00000000%dH%c",col,*ctext++);
+ cup(20,1);
+ holdit();
+}
+
+tst_screen() {
+
+ /* Test of:
+ - DECSTBM (Set Top and Bottom Margins)
+ - TBC (Tabulation Clear)
+ - HTS (Horizontal Tabulation Set)
+ - SM RM (Set/Reset mode): - 80/132 chars
+ - Origin: Realtive/absolute
+ - Scroll: Smooth/jump
+ - Wraparound
+ - SGR (Select Graphic Rendition)
+ - SM RM (Set/Reset Mode) - Inverse
+ - DECSC (Save Cursor)
+ - DECRC (Restore Cursor)
+ */
+
+ int i, j, cset, row, col, down, soft, background;
+
+ static char *tststr = "*qx`";
+ static char *attr[5] = { ";0", ";1", ";4", ";5", ";7" };
+
+ cup(1,1);
+ sm("?7"); /* Wrap Around ON */
+ for (col = 1; col <= 160; col++) printf("*");
+ rm("?7"); /* Wrap Around OFF */
+ cup(3,1);
+ for (col = 1; col <= 160; col++) printf("*");
+ sm("?7"); /* Wrap Around ON */
+ cup(5,1);
+ println("This should be three identical lines of *'s completely filling");
+ println("the top of the screen without any empty lines between.");
+ println("(Test of WRAP AROUND mode setting.)");
+ holdit();
+
+ ed(2);
+ tbc(3);
+ cup(1,1);
+ for (col = 1; col <= 78; col += 3) {
+ cuf(3); hts();
+ }
+ cup(1,4);
+ for (col = 4; col <= 78; col += 6) {
+ tbc(0); cuf(6);
+ }
+ cup(1,7); tbc(1); tbc(2); /* no-op */
+ cup(1,1); for (col = 1; col <= 78; col += 6) printf("\t*");
+ cup(2,2); for (col = 2; col <= 78; col += 6) printf(" *");
+ cup(4,1);
+ println("Test of TAB setting/resetting. These two lines");
+ printf("should look the same. ");
+ holdit();
+ for (background = 0; background <= 1; background++) {
+ if (background) rm("?5");
+ else sm("?5");
+ sm("?3"); /* 132 cols */
+ ed(2); /* VT100 clears screen on SM3/RM3, but not obviously, so... */
+ cup(1,1); tbc(3);
+ for (col = 1; col <= 132; col += 8) {
+ cuf(8); hts();
+ }
+ cup(1,1); for (col = 1; col <= 130; col += 10) printf("1234567890");
+ printf("12");
+ for (row = 3; row <= 20; row++) {
+ cup(row,row);
+ printf("This is 132 column mode, %s background.",
+ background ? "dark" : "light");
+ }
+ holdit();
+ rm("?3"); /* 80 cols */
+ ed(2); /* VT100 clears screen on SM3/RM3, but not obviously, so... */
+ cup(1,1); for (col = 1; col <= 80; col += 10) printf("1234567890");
+ for (row = 3; row <= 20; row++) {
+ cup(row,row);
+ printf("This is 80 column mode, %s background.",
+ background ? "dark" : "light");
+ }
+ holdit();
+ }
+
+ ed(2);
+ sm("?6"); /* Origin mode (relative) */
+ for (soft = -1; soft <= 0; soft++) {
+ if (soft) sm("?4");
+ else rm("?4");
+ for (row = 12; row >= 1; row -= 11) {
+ decstbm(row, 24-row+1);
+ ed(2);
+ for (down = 0; down >= -1; down--) {
+ if (down) cuu(24);
+ else cud(24);
+ for (i = 1; i <= 30; i++) {
+ printf("%s scroll %s region %d Line %d\n",
+ soft ? "Soft" : "Jump",
+ down ? "down" : "up",
+ 2*(13-row), i);
+ if (down) { ri(); ri(); }
+ }
+ }
+ holdit();
+ }
+ }
+ ed(2);
+ decstbm(23,24);
+ printf(
+ "\nOrigin mode test. This line should be at the bottom of the screen.");
+ cup(1,1);
+ printf("%s",
+ "This line should be the one above the bottom of the screeen. ");
+ holdit();
+ ed(2);
+ rm("?6"); /* Origin mode (absolute) */
+ cup(24,1);
+ printf(
+ "Origin mode test. This line should be at the bottom of the screen.");
+ cup(1,1);
+ printf("%s", "This line should be at the top if the screen. ");
+ holdit();
+ decstbm(1,24);
+
+ ed(2);
+ cup( 1,20); printf("Graphic rendition test pattern:");
+ cup( 4, 1); sgr("0"); printf("vanilla");
+ cup( 4,40); sgr("0;1"); printf("bold");
+ cup( 6, 6); sgr(";4"); printf("underline");
+ cup( 6,45);sgr(";1");sgr("4");printf("bold underline");
+ cup( 8, 1); sgr("0;5"); printf("blink");
+ cup( 8,40); sgr("0;5;1"); printf("bold blink");
+ cup(10, 6); sgr("0;4;5"); printf("underline blink");
+ cup(10,45); sgr("0;1;4;5"); printf("bold underline blink");
+ cup(12, 1); sgr("1;4;5;0;7"); printf("negative");
+ cup(12,40); sgr("0;1;7"); printf("bold negative");
+ cup(14, 6); sgr("0;4;7"); printf("underline negative");
+ cup(14,45); sgr("0;1;4;7"); printf("bold underline negative");
+ cup(16, 1); sgr("1;4;;5;7"); printf("blink negative");
+ cup(16,40); sgr("0;1;5;7"); printf("bold blink negative");
+ cup(18, 6); sgr("0;4;5;7"); printf("underline blink negative");
+ cup(18,45); sgr("0;1;4;5;7"); printf("bold underline blink negative");
+ sgr("");
+
+ rm("?5"); /* Inverse video off */
+ cup(23,1); el(0); printf("Dark background. "); holdit();
+ sm("?5"); /* Inverse video */
+ cup(23,1); el(0); printf("Light background. "); holdit();
+ rm("?5");
+ ed(2);
+ cup(8,12); printf("normal");
+ cup(8,24); printf("bold");
+ cup(8,36); printf("underscored");
+ cup(8,48); printf("blinking");
+ cup(8,60); printf("reversed");
+ cup(10,1); printf("stars:");
+ cup(12,1); printf("line:");
+ cup(14,1); printf("x'es:");
+ cup(16,1); printf("diamonds:");
+ for (cset = 0; cset <= 3; cset++) {
+ for (i = 0; i <= 4; i++) {
+ cup(10 + 2 * cset, 12 + 12 * i);
+ sgr(attr[i]);
+ if (cset == 0 || cset == 2) scs(0,'B');
+ else scs(0,'0');
+ for (j = 0; j <= 4; j++) {
+ printf("%c", tststr[cset]);
+ }
+ decsc();
+ cup(cset + 1, i + 1); sgr(""); scs(0,'B'); printf("A");
+ decrc();
+ for (j = 0; j <= 4; j++) {
+ printf("%c", tststr[cset]);
+ }
+ }
+ }
+ sgr("0"); scs(0,'B'); cup(21,1);
+ println("Test of the SAVE/RESTORE CURSOR feature. There should");
+ println("be ten characters of each flavour, and a rectangle");
+ println("of 5 x 4 A's filling the top left of the screen.");
+ holdit();
+}
+
+tst_characters() {
+ /* Test of:
+ SCS (Select character Set)
+ */
+
+ int i, j, g, cset;
+ char chcode[5], *setmsg[5];
+
+ chcode[0] = 'A';
+ chcode[1] = 'B';
+ chcode[2] = '0';
+ chcode[3] = '1';
+ chcode[4] = '2';
+ setmsg[0] = "UK / national";
+ setmsg[1] = "US ASCII";
+ setmsg[2] = "Special graphics and line drawing";
+ setmsg[3] = "Alternate character ROM standard characters";
+ setmsg[4] = "Alternate character ROM special graphics";
+
+ cup(1,10); printf("Selected as G0 (with SI)");
+ cup(1,48); printf("Selected as G1 (with SO)");
+ for (cset = 0; cset <= 4; cset++) {
+ scs(1,'B');
+ cup(3 + 4 * cset, 1);
+ sgr("1");
+ printf("Character set %c (%s)",chcode[cset], setmsg[cset]);
+ sgr("0");
+ for (g = 0; g <= 1; g++) {
+ scs(g,chcode[cset]);
+ for (i = 1; i <= 3; i++) {
+ cup(3 + 4 * cset + i, 10 + 38 * g);
+ for (j = 0; j <= 31; j++) {
+ printf("%c", i * 32 + j);
+ }
+ }
+ }
+ }
+ scs(1,'B');
+ cup(24,1); printf("These are the installed character sets. ");
+ holdit();
+}
+
+tst_doublesize() {
+ /* Test of:
+ DECSWL (Single Width Line)
+ DECDWL (Double Width Line)
+ DECDHL (Double Height Line) (also implicit double width)
+ */
+
+ int col, i, w, w1;
+
+ /* Print the test pattern in both 80 and 132 character width */
+
+ for(w = 0; w <= 1; w++) {
+ w1 = 13 * w;
+
+ ed(2);
+ cup(1, 1);
+ if (w) { sm("?3"); printf("132 column mode"); }
+ else { rm("?3"); printf(" 80 column mode"); }
+
+ cup( 5, 3 + 2 * w1);
+ printf("v------- left margin");
+
+ cup( 7, 3 + 2 * w1);
+ printf("This is a normal-sized line");
+ decdhl(0); decdhl(1); decdwl(); decswl();
+
+ cup( 9, 2 + w1);
+ printf("This is a Double-width line");
+ decswl(); decdhl(0); decdhl(1); decdwl();
+
+ cup(11, 2 + w1);
+ decdwl(); decswl(); decdhl(1); decdhl(0);
+ printf("This is a Double-width-and-height line");
+ cup(12, 2 + w1);
+ decdwl(); decswl(); decdhl(0); decdhl(1);
+ printf("This is a Double-width-and-height line");
+
+ cup(14, 2 + w1);
+ decdwl(); decswl(); decdhl(1); decdhl(0); el(2);
+ printf("This is another such line");
+ cup(15, 2 + w1);
+ decdwl(); decswl(); decdhl(0); decdhl(1);
+ printf("This is another such line");
+
+ cup(17, 3 + 2 * w1);
+ printf("^------- left margin");
+
+ cup(21, 1);
+ printf("This is not a double-width line");
+ for (i = 0; i <= 1; i++) {
+ cup(21,6);
+ if (i) { printf("**is**"); decdwl(); }
+ else { printf("is not"); decswl(); }
+ cup(23,1); holdit();
+ }
+ }
+ /* Set vanilla tabs for next test */
+ cup(1,1); tbc(3); for (col = 1; col <= 132; col += 8) { cuf(8); hts(); }
+ rm("?3");
+ ed(2);
+ scs(0,'0');
+
+ cup( 8,1); decdhl(0); printf("lqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqk");
+ cup( 9,1); decdhl(1); printf("lqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqk");
+ cup(10,1); decdhl(0); printf("x%c%c%c%c%cx",9,9,9,9,9);
+ cup(11,1); decdhl(1); printf("x%c%c%c%c%cx",9,9,9,9,9);
+ cup(12,1); decdhl(0); printf("x%c%c%c%c%cx",9,9,9,9,9);
+ cup(13,1); decdhl(1); printf("x%c%c%c%c%cx",9,9,9,9,9);
+ cup(14,1); decdhl(0); printf("x x");
+ cup(15,1); decdhl(1); printf("x x");
+ cup(16,1); decdhl(0); printf("mqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqj");
+ cup(17,1); decdhl(1); printf("mqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqqj");
+ scs(0,'B'); sgr("1;5");
+ cup(12,3);
+ printf("* The mad programmer strikes again * ");
+ cup(13,3); printf("%c",9); cub(6);
+ printf("* The mad programmer strikes again *");
+ sgr("0");
+ cup(22,1);
+ println("Another test pattern... a frame with blinking bold text,");
+ printf("all in double-height double-width size. ");
+ holdit();
+
+ decstbm(8,24); /* Absolute origin mode, so cursor is set at (1,1) */
+ cup(8,1);
+ for (i = 1; i <= 12; i++)
+ ri();
+ decstbm(0,0); /* No scroll region */
+ cup(1,1);
+ printf("%s", "Exactly half of the box should remain. ");
+ holdit();
+}
+
+tst_keyboard() {
+
+/* Test of:
+ - DECLL (Load LEDs)
+ - Keyboard return messages
+ - SM RM (Set/Reset Mode) - Cursor Keys
+ - Auto repeat
+ - DECKPAM (Keypad Application Mode)
+ - DECKPNM (Keypad Numeric Mode)
+
+The standard VT100 keayboard layout:
+
+ UP DN LE RI
+
+ESC 1! 2@ 3# 4$ 5% 6^ 7& 8* 9( 0) -_ =+ `~ BS
+
+TAB* qQ wW eE rR tT yY uU iI oO pP [{ ]} DEL
+
+** ** aA sS dD fF gG hH jJ kK lL ;: ," RETN \|
+
+** **** zZ xX cC vV bB nN mM ,< .> /? **** LF
+
+ ****************SPACE BAR****************
+
+ PF1 PF2 PF3 PF4
+
+ *7* *8* *9* *-*
+
+ *4* *5* *6* *,*
+
+ *1* *2* *3*
+
+ ***0*** *.* ENT
+*/
+
+ char *ledmsg[6], *ledseq[6];
+
+ int i, j, okflag;
+ int kblayout;
+ int ckeymode;
+ int fkeymode;
+ char kbdc;
+ char *kbds = " ";
+ char *curkeystr, *fnkeystr, *abmstr;
+ char arptstring[500];
+
+ static struct key {
+ char c;
+ int row;
+ int col;
+ char *symbol;
+ } keytab [] = {
+ { 27, 1, 0, "ESC" },
+ { '1', 1, 6, "1" }, { '!', 1, 7, "!" },
+ { '2', 1, 11, "2" }, { '@', 1, 12, "@" },
+ { '3', 1, 16, "3" }, { '#', 1, 17, "#" },
+ { '4', 1, 21, "4" }, { '$', 1, 22, "$" },
+ { '5', 1, 26, "5" }, { '%', 1, 27, "%" },
+ { '6', 1, 31, "6" }, { '^', 1, 32, "^" },
+ { '7', 1, 36, "7" }, { '&', 1, 37, "&" },
+ { '8', 1, 41, "8" }, { '*', 1, 42, "*" },
+ { '9', 1, 46, "9" }, { '(', 1, 47, "(" },
+ { '0', 1, 51, "0" }, { ')', 1, 52, ")" },
+ { '-', 1, 56, "-" }, { '_', 1, 57, "_" },
+ { '=', 1, 61, "=" }, { '+', 1, 62, "+" },
+ { '`', 1, 66, "`" }, { '~', 1, 67, "~" },
+ { 8, 1, 70, "BS" },
+ { 9, 2, 0, " TAB " },
+ { 'q', 2, 8, "q" }, { 'Q', 2, 9, "Q" },
+ { 'w', 2, 13, "w" }, { 'W', 2, 14, "W" },
+ { 'e', 2, 18, "e" }, { 'E', 2, 19, "E" },
+ { 'r', 2, 23, "r" }, { 'R', 2, 24, "R" },
+ { 't', 2, 28, "t" }, { 'T', 2, 29, "T" },
+ { 'y', 2, 33, "y" }, { 'Y', 2, 34, "Y" },
+ { 'u', 2, 38, "u" }, { 'U', 2, 39, "U" },
+ { 'i', 2, 43, "i" }, { 'I', 2, 44, "I" },
+ { 'o', 2, 48, "o" }, { 'O', 2, 49, "O" },
+ { 'p', 2, 53, "p" }, { 'P', 2, 54, "P" },
+ { '[', 2, 58, "[" }, { '{', 2, 59, "{" },
+ { ']', 2, 63, "]" }, { '}', 2, 64, "}" },
+ { 127, 2, 71, "DEL" },
+ { 'a', 3, 10, "a" }, { 'A', 3, 11, "A" },
+ { 's', 3, 15, "s" }, { 'S', 3, 16, "S" },
+ { 'd', 3, 20, "d" }, { 'D', 3, 21, "D" },
+ { 'f', 3, 25, "f" }, { 'F', 3, 26, "F" },
+ { 'g', 3, 30, "g" }, { 'G', 3, 31, "G" },
+ { 'h', 3, 35, "h" }, { 'H', 3, 36, "H" },
+ { 'j', 3, 40, "j" }, { 'J', 3, 41, "J" },
+ { 'k', 3, 45, "k" }, { 'K', 3, 46, "K" },
+ { 'l', 3, 50, "l" }, { 'L', 3, 51, "L" },
+ { ';', 3, 55, ";" }, { ':', 3, 56, ":" },
+ {'\'', 3, 60, "'" }, { '"', 3, 61,"\"" },
+ { 13, 3, 65, "RETN"},
+ {'\\', 3, 71,"\\" }, { '|', 3, 72, "|" },
+ { 'z', 4, 12, "z" }, { 'Z', 4, 13, "Z" },
+ { 'x', 4, 17, "x" }, { 'X', 4, 18, "X" },
+ { 'c', 4, 22, "c" }, { 'C', 4, 23, "C" },
+ { 'v', 4, 27, "v" }, { 'V', 4, 28, "V" },
+ { 'b', 4, 32, "b" }, { 'B', 4, 33, "B" },
+ { 'n', 4, 37, "n" }, { 'N', 4, 38, "N" },
+ { 'm', 4, 42, "m" }, { 'M', 4, 43, "M" },
+ { ',', 4, 47, "," }, { '<', 4, 48, "<" },
+ { '.', 4, 52, "." }, { '>', 4, 53, ">" },
+ { '/', 4, 57, "/" }, { '?', 4, 58, "?" },
+ { 10, 4, 69, "LF" },
+ { ' ', 5, 13, " SPACE BAR "},
+ {'\0', 0, 0, "" }
+ };
+
+ static struct natkey {
+ char natc;
+ int natrow;
+ int natcol;
+ char *natsymbol;
+ } natkeytab [][29] = {
+ {
+ { '"', 1, 12, "\""},
+ { '&', 1, 32, "&" },
+ { '/', 1, 37, "/" },
+ { '(', 1, 42, "(" },
+ { ')', 1, 47, ")" },
+ { '=', 1, 52, "=" },
+ { '+', 1, 56, "+" }, { '?', 1, 57, "?" },
+ { '`', 1, 61, "`" }, { '@', 1, 62, "@" },
+ { '<', 1, 66, "<" }, { '>', 1, 67, ">" },
+ { '}', 2, 58, "}" }, { ']', 2, 59, "]" },
+ { '^', 2, 63, "^" }, { '~', 2, 64, "~" },
+ { '|', 3, 55, "|" }, {'\\', 3, 56,"\\" },
+ { '{', 3, 60, "{" }, { '[', 3, 61, "[" },
+ {'\'', 3, 71, "'" }, { '*', 3, 72, "*" },
+ { ',', 4, 47, "," }, { ';', 4, 48, ";" },
+ { '.', 4, 52, "." }, { ':', 4, 53, ":" },
+ { '-', 4, 57, "-" }, { '_', 4, 58, "_" },
+ {'\0', 0, 0, "" }
+ },
+ {
+ { '"', 1, 12, "\""},
+ { '&', 1, 32, "&" },
+ { '/', 1, 37, "/" },
+ { '(', 1, 42, "(" },
+ { ')', 1, 47, ")" },
+ { '=', 1, 52, "=" },
+ { '+', 1, 56, "+" }, { '?', 1, 57, "?" },
+ { '`', 1, 61, "`" }, { '@', 1, 62, "@" },
+ { '<', 1, 66, "<" }, { '>', 1, 67, ">" },
+ { '}', 2, 58, "}" }, { ']', 2, 59, "]" },
+ { '~', 2, 63, "~" }, { '^', 2, 64, "^" },
+ { '|', 3, 55, "|" }, {'\\', 3, 56,"\\" },
+ { '{', 3, 60, "{" }, { '[', 3, 61, "[" },
+ {'\'', 3, 71, "'" }, { '*', 3, 72, "*" },
+ { ',', 4, 47, "," }, { ';', 4, 48, ";" },
+ { '.', 4, 52, "." }, { ':', 4, 53, ":" },
+ { '-', 4, 57, "-" }, { '_', 4, 58, "_" },
+ {'\0', 0, 0, "" }
+ }
+ };
+
+ static struct curkey {
+ char *curkeymsg[3];
+ int curkeyrow;
+ int curkeycol;
+ char *curkeysymbol;
+ char *curkeyname;
+ } curkeytab [] = {
+
+ /* A Reset, A Set, VT52 */
+
+ {{"\033[A","\033OA","\033A"}, 0, 56, "UP", "Up arrow" },
+ {{"\033[B","\033OB","\033B"}, 0, 61, "DN", "Down arrow" },
+ {{"\033[D","\033OD","\033D"}, 0, 66, "LT", "Left arrow" },
+ {{"\033[C","\033OC","\033C"}, 0, 71, "RT", "Right arrow"},
+ {{"", "", "" }, 0, 0, "", "" }
+ };
+
+ static struct fnkey {
+ char *fnkeymsg[4];
+ int fnkeyrow;
+ int fnkeycol;
+ char *fnkeysymbol;
+ char *fnkeyname;
+ } fnkeytab [] = {
+
+ /* ANSI-num,ANSI-app,VT52-nu,VT52-ap, r, c, symb name */
+
+ {{"\033OP","\033OP","\033P","\033P" }, 6, 59, "PF1", "PF1" },
+ {{"\033OQ","\033OQ","\033Q","\033Q" }, 6, 63, "PF2", "PF2" },
+ {{"\033OR","\033OR","\033R","\033R" }, 6, 67, "PF3", "PF3" },
+ {{"\033OS","\033OS","\033S","\033S" }, 6, 71, "PF4", "PF4" },
+ {{"7", "\033Ow","7", "\033?w"}, 7, 59, " 7 ", "Numeric 7" },
+ {{"8", "\033Ox","8", "\033?x"}, 7, 63, " 8 ", "Numeric 8" },
+ {{"9", "\033Oy","9", "\033?y"}, 7, 67, " 9 ", "Numeric 9" },
+ {{"-", "\033Om","-", "\033?m"}, 7, 71, " - ", "Minus" },
+ {{"4", "\033Ot","4", "\033?t"}, 8, 59, " 4 ", "Numeric 4" },
+ {{"5", "\033Ou","5", "\033?u"}, 8, 63, " 5 ", "Numeric 5" },
+ {{"6", "\033Ov","6", "\033?v"}, 8, 67, " 6 ", "Numeric 6" },
+ {{",", "\033Ol",",", "\033?l"}, 8, 71, " , ", "Comma" },
+ {{"1", "\033Oq","1", "\033?q"}, 9, 59, " 1 ", "Numeric 1" },
+ {{"2", "\033Or","2", "\033?r"}, 9, 63, " 2 ", "Numeric 2" },
+ {{"3", "\033Os","3", "\033?s"}, 9, 67, " 3 ", "Numeric 3" },
+ {{"0", "\033Op","0", "\033?p"},10, 59," O ","Numeric 0"},
+ {{".", "\033On",".", "\033?n"},10, 67, " . ", "Point" },
+ {{"\015", "\033OM","\015", "\033?M"},10, 71, "ENT", "ENTER" },
+ {{"","","",""}, 0, 0, "", "" }
+ };
+
+ static struct ckey {
+ int ccount;
+ char *csymbol;
+ } ckeytab [] = {
+ { 0, "NUL (CTRL-@ or CTRL-Space)" },
+ { 0, "SOH (CTRL-A)" },
+ { 0, "STX (CTRL-B)" },
+ { 0, "ETX (CTRL-C)" },
+ { 0, "EOT (CTRL-D)" },
+ { 0, "ENQ (CTRL-E)" },
+ { 0, "ACK (CTRL-F)" },
+ { 0, "BEL (CTRL-G)" },
+ { 0, "BS (CTRL-H) (BACK SPACE)" },
+ { 0, "HT (CTRL-I) (TAB)" },
+ { 0, "LF (CTRL-J) (LINE FEED)" },
+ { 0, "VT (CTRL-K)" },
+ { 0, "FF (CTRL-L)" },
+ { 0, "CR (CTRL-M) (RETURN)" },
+ { 0, "SO (CTRL-N)" },
+ { 0, "SI (CTRL-O)" },
+ { 0, "DLE (CTRL-P)" },
+ { 0, "DC1 (CTRL-Q) (X-On)" },
+ { 0, "DC2 (CTRL-R)" },
+ { 0, "DC3 (CTRL-S) (X-Off)" },
+ { 0, "DC4 (CTRL-T)" },
+ { 0, "NAK (CTRL-U)" },
+ { 0, "SYN (CTRL-V)" },
+ { 0, "ETB (CTRL-W)" },
+ { 0, "CAN (CTRL-X)" },
+ { 0, "EM (CTRL-Y)" },
+ { 0, "SUB (CTRL-Z)" },
+ { 0, "ESC (CTRL-[) (ESCAPE)" },
+ { 0, "FS (CTRL-\\ or CTRL-? or CTRL-_)" },
+ { 0, "GS (CTRL-])" },
+ { 0, "RS (CTRL-^ or CTRL-~ or CTRL-`)" },
+ { 0, "US (CTRL-_ or CTRL-?)" }
+ };
+
+ static char *keyboardmenu[] = {
+ "Standard American ASCII layout",
+ "Swedish national layout D47",
+ "Swedish national layout E47",
+ /* add new keyboard layouts here */
+ ""
+ };
+
+ static char *curkeymodes[3] = {
+ "ANSI / Cursor key mode RESET",
+ "ANSI / Cursor key mode SET",
+ "VT52 Mode"
+ };
+
+ static char *fnkeymodes[4] = {
+ "ANSI Numeric mode",
+ "ANSI Application mode",
+ "VT52 Numeric mode",
+ "VT52 Application mode"
+ };
+
+ ledmsg[0] = "L1 L2 L3 L4"; ledseq[0] = "1;2;3;4";
+ ledmsg[1] = " L2 L3 L4"; ledseq[1] = "1;0;4;3;2";
+ ledmsg[2] = " L2 L3"; ledseq[2] = "1;4;;2;3";
+ ledmsg[3] = "L1 L2"; ledseq[3] = ";;2;1";
+ ledmsg[4] = "L1"; ledseq[4] = "1";
+ ledmsg[5] = ""; ledseq[5] = "";
+
+#ifdef UNIX
+ fflush(stdout);
+#endif
+ ed(2);
+ cup(10,1);
+ println("These LEDs (\"lamps\") on the keyboard should be on:");
+ for (i = 0; i <= 5; i++) {
+ cup(10,52); el(0); printf("%s", ledmsg[i]);
+ decll("0");
+ decll(ledseq[i]);
+ cup(12,1); holdit();
+ }
+
+ ed(2);
+ cup(10,1);
+ println("Test of the AUTO REPEAT feature");
+ println("");
+ println("Hold down an alphanumeric key for a while, then push RETURN.");
+ printf("%s", "Auto Repeat OFF: ");
+ rm("?8");
+ inputline(arptstring);
+ if (strlen(arptstring) == 0) println("No characters read!??");
+ else if (strlen(arptstring) == 1) println("OK.");
+ else println("Too many characters read.");
+ println("");
+ println("Hold down an alphanumeric key for a while, then push RETURN.");
+ printf("%s", "Auto Repeat ON: ");
+ sm("?8");
+ inputline(arptstring);
+ if (strlen(arptstring) == 0) println("No characters read!??");
+ else if (strlen(arptstring) == 1) println("Not enough characters read.");
+ else println("OK.");
+ println("");
+ holdit();
+
+ ed(2);
+ cup(5,10);
+ println("Choose keyboard layout:");
+ kblayout = menu(keyboardmenu);
+ if (kblayout) {
+ kblayout--;
+ for (j = 0; natkeytab[kblayout][j].natc != '\0'; j++) {
+ for (i = 0; keytab[i].c != '\0'; i++) {
+ if (keytab[i].row == natkeytab[kblayout][j].natrow &&
+ keytab[i].col == natkeytab[kblayout][j].natcol) {
+ keytab[i].c = natkeytab[kblayout][j].natc;
+ keytab[i].symbol = natkeytab[kblayout][j].natsymbol;
+ break;
+ }
+ }
+ }
+ }
+
+ ed(2);
+ for (i = 0; keytab[i].c != '\0'; i++) {
+ cup(1 + 2 * keytab[i].row, 1 + keytab[i].col);
+ sgr("7");
+ printf("%s", keytab[i].symbol);
+ sgr("");
+ }
+ cup(22,1);
+#ifdef UNIX
+ sgttyNew.sg_flags &= ~CRMOD;
+ sgttyNew.sg_flags &= ~ECHO;
+ stty(0, &sgttyNew);
+#endif
+ inflush();
+ printf("Press each key, both shifted and unshifted. Finish with RETURN:");
+ do { /* while (kbdc != 13) */
+ cup(23,1); kbdc = inchar();
+ cup(23,1); el(0);
+ sprintf(kbds, "%c", kbdc);
+ chrprint(kbds);
+ for (i = 0; keytab[i].c != '\0'; i++) {
+ if (keytab[i].c == kbdc) {
+ cup(1 + 2 * keytab[i].row, 1 + keytab[i].col);
+ printf("%s", keytab[i].symbol);
+ break;
+ }
+ }
+ } while (kbdc != 13);
+#ifdef SARG10
+ inchar(); /* Local hack: Read LF that TOPS-10 adds to CR */
+#endif
+ cup(23,1); el(0);
+
+ for (ckeymode = 0; ckeymode <= 2; ckeymode++) {
+ if (ckeymode) sm("?1");
+ else rm("?1");
+ for (i = 0; curkeytab[i].curkeysymbol[0] != '\0'; i++) {
+ cup(1 + 2 * curkeytab[i].curkeyrow, 1 + curkeytab[i].curkeycol);
+ sgr("7");
+ printf("%s", curkeytab[i].curkeysymbol);
+ sgr("");
+ }
+ cup(20,1); printf("<%s>%20s", curkeymodes[ckeymode], "");
+ cup(22,1); el(0);
+ cup(22,1); printf("%s", "Press each cursor key. Finish with TAB.");
+ for(;;) {
+ cup(23,1);
+ if (ckeymode == 2) rm("?2"); /* VT52 mode */
+ curkeystr = instr();
+ esc("<"); /* ANSI mode */
+ cup(23,1); el(0);
+ cup(23,1); chrprint(curkeystr);
+ if (!strcmp(curkeystr,"\t")) break;
+ for (i = 0; curkeytab[i].curkeysymbol[0] != '\0'; i++) {
+ if (!strcmp(curkeystr,curkeytab[i].curkeymsg[ckeymode])) {
+ sgr("7");
+ printf(" (%s key) ", curkeytab[i].curkeyname);
+ sgr("");
+ cup(1 + 2 * curkeytab[i].curkeyrow,
+ 1 + curkeytab[i].curkeycol);
+ printf("%s", curkeytab[i].curkeysymbol);
+ break;
+ }
+ }
+ if (i == sizeof(curkeytab) / sizeof(struct curkey) - 1) {
+ sgr("7");
+ printf("%s", " (Unknown cursor key) ");
+ sgr("");
+ }
+ }
+ }
+
+ for (fkeymode = 0; fkeymode <= 3; fkeymode++) {
+ for (i = 0; fnkeytab[i].fnkeysymbol[0] != '\0'; i++) {
+ cup(1 + 2 * fnkeytab[i].fnkeyrow, 1 + fnkeytab[i].fnkeycol);
+ sgr("7");
+ printf("%s", fnkeytab[i].fnkeysymbol);
+ sgr("");
+ }
+ cup(20,1); printf("<%s>%20s", fnkeymodes[fkeymode], "");
+ cup(22,1); el(0);
+ cup(22,1); printf("%s", "Press each function key. Finish with TAB.");
+ for(;;) {
+ cup(23,1);
+ if (fkeymode >= 2) rm("?2"); /* VT52 mode */
+ if (fkeymode % 2) deckpam(); /* Application mode */
+ else deckpnm(); /* Numeric mode */
+ fnkeystr = instr();
+ esc("<"); /* ANSI mode */
+ cup(23,1); el(0);
+ cup(23,1); chrprint(fnkeystr);
+ if (!strcmp(fnkeystr,"\t")) break;
+ for (i = 0; fnkeytab[i].fnkeysymbol[0] != '\0'; i++) {
+ if (!strcmp(fnkeystr,fnkeytab[i].fnkeymsg[fkeymode])) {
+ sgr("7");
+ printf(" (%s key) ", fnkeytab[i].fnkeyname);
+ sgr("");
+ cup(1 + 2 * fnkeytab[i].fnkeyrow, 1 + fnkeytab[i].fnkeycol);
+ printf("%s", fnkeytab[i].fnkeysymbol);
+ break;
+ }
+ }
+ if (i == sizeof(fnkeytab) / sizeof(struct fnkey) - 1) {
+ sgr("7");
+ printf("%s", " (Unknown function key) ");
+ sgr("");
+ }
+ }
+ }
+
+#ifdef UNIX
+ sgttyNew.sg_flags |= CRMOD;
+ stty(0, &sgttyNew);
+#endif
+ ed(2);
+ cup(5,1);
+ println("Finally, a check of the ANSWERBACK MESSAGE, which can be sent");
+ println("by pressing CTRL-BREAK. The answerback message can be loaded");
+ println("in SET-UP B by pressing SHIFT-A and typing e.g.");
+ println("");
+ println(" \" H e l l o , w o r l d Return \"");
+ println("");
+ println("(the double-quote characters included). Do that, and then try");
+ println("to send an answerback message with CTRL-BREAK. If it works,");
+ println("the answerback message should be displayed in reverse mode.");
+ println("Finish with a single RETURN.");
+
+#ifdef UNIX
+ sgttyNew.sg_flags &= ~CRMOD;
+ stty(0, &sgttyNew);
+#endif
+ do {
+ cup(17,1);
+ inflush();
+ abmstr = instr();
+ cup(17,1);
+ el(0);
+ chrprint(abmstr);
+ } while (strcmp(abmstr,"\r"));
+
+ ed(2);
+ for (i = 0; i < 32; i++) {
+ cup(1 + (i % 16), 1 + 40 * (i / 16));
+ sgr("7");
+ printf("%s", ckeytab[i].csymbol);
+ sgr("0");
+ }
+ cup(19,1);
+#ifdef UNIX
+ sgttyNew.sg_flags |= CRMOD;
+ stty(0, &sgttyNew);
+#endif
+ println(
+ "Push each CTRL-key TWICE. Note that you should be able to send *all*");
+ println(
+ "CTRL-codes twice, including CTRL-S (X-Off) and CTRL-Q (X-Off)!");
+ println(
+ "Finish with DEL (also called DELETE or RUB OUT), or wait 1 minute.");
+#ifdef UNIX
+#ifdef SIII
+ sgttyNew.sg_flags &= ~CBREAK;
+ stty(0, &sgttyNew);
+#endif
+ sgttyNew.sg_flags |= RAW;
+ stty(0, &sgttyNew);
+#endif
+ ttybin(1);
+#ifdef SARG20
+ page(0); /* Turn off all character processing at input */
+ superbin(1); /* Turn off ^C (among others). Keep your fingers crossed!! */
+#endif
+ do {
+ cup(23,1); kbdc = inchar();
+ cup(23,1); el(0);
+ if (kbdc < 32) printf(" %s", ckeytab[kbdc].csymbol);
+ else {
+ sprintf(kbds, "%c", kbdc);
+ chrprint(kbds);
+ printf("%s", " -- not a CTRL key");
+ }
+ if (kbdc < 32) ckeytab[kbdc].ccount++;
+ if (ckeytab[kbdc].ccount == 2) {
+ cup(1 + (kbdc % 16), 1 + 40 * (kbdc / 16));
+ printf("%s", ckeytab[kbdc].csymbol);
+ }
+ } while (kbdc != '\177');
+#ifdef UNIX
+ sgttyNew.sg_flags &= ~RAW;
+ sgttyNew.sg_flags |= ECHO;
+ stty(0, &sgttyNew);
+#ifdef SIII
+ sgttyNew.sg_flags |= CBREAK;
+ stty(0, &sgttyNew);
+#endif
+#endif
+ ttybin(0);
+#ifdef SARG20
+ superbin(0); /* Puuuh! We made it!? */
+ page(1); /* Back to normal input processing */
+ ttybin(1); /* This must be the mode for DEC20 */
+#endif
+ cup(24,1);
+ okflag = 1;
+ for (i = 0; i < 32; i++) if (ckeytab[i].ccount < 2) okflag = 0;
+ if (okflag) printf("%s", "OK. ");
+ else printf("%s", "You have not been able to send all CTRL keys! ");
+ holdit();
+}
+
+tst_reports() {
+ /* Test of:
+ <ENQ> (AnswerBack Message)
+ SM RM (Set/Reset Mode) - LineFeed / Newline
+ DSR (Device Status Report)
+ DA (Device Attributes)
+ DECREQTPARM (Request Terminal Parameters)
+ */
+
+ int parity, nbits, xspeed, rspeed, clkmul, flags;
+ int i, reportpos;
+ char *report, *report2;
+ static char *attributes[][2] = {
+ { "\033[?1;0c", "No options (vanilla VT100)" },
+ { "\033[?1;1c", "VT100 with STP" },
+ { "\033[?1;2c", "VT100 with AVO (could be a VT102)" },
+ { "\033[?1;3c", "VT100 with STP and AVO" },
+ { "\033[?1;4c", "VT100 with GPO" },
+ { "\033[?1;5c", "VT100 with STP and GPO" },
+ { "\033[?1;6c", "VT100 with AVO and GPO" },
+ { "\033[?1;7c", "VT100 with STP, AVO and GPO" },
+ { "\033[?1;11c", "VT100 with PP and AVO" },
+ { "\033[?1;15c", "VT100 with PP, GPO and AVO" },
+ { "\033[?4;2c", "VT132 with AVO" },
+ { "\033[?4;3c", "VT132 with AVO and STP" },
+ { "\033[?4;6c", "VT132 with GPO and AVO" },
+ { "\033[?4;7c", "VT132 with GPO, AVO, and STP" },
+ { "\033[?4;11c", "VT132 with PP and AVO" },
+ { "\033[?4;15c", "VT132 with PP, GPO and AVO" },
+ { "\033[?7c", "VT131" },
+ { "\033[?12;5c", "VT125" }, /* VT125 also has ROM version */
+ { "\033[?12;7c", "VT125 with AVO" }, /* number, so this won't work */
+ { "\033[?5;0c", "VK100 (GIGI)" },
+ { "\033[?5c", "VK100 (GIGI)" },
+ { "", "" }
+ };
+
+#ifdef UNIX
+ sgttyNew.sg_flags &= ~ECHO;
+ stty(0, &sgttyNew);
+#endif
+ cup(5,1);
+ println("This is a test of the ANSWERBACK MESSAGE. (To load the A.B.M.");
+ println("see the TEST KEYBOARD part of this program). Below here, the");
+ println("current answerback message in your terminal should be");
+ println("displayed. Finish this test with RETURN.");
+ cup(10,1);
+ inflush();
+ printf("%c", 5); /* ENQ */
+ report = instr();
+ cup(10,1);
+ chrprint(report);
+ cup(12,1);
+ holdit();
+
+ ed(2);
+ cup(1,1);
+ println("Test of LineFeed/NewLine mode.");
+ cup(3,1);
+ sm("20");
+#ifdef UNIX
+ sgttyNew.sg_flags &= ~CRMOD;
+ stty(0, &sgttyNew);
+#endif
+ printf("NewLine mode set. Push the RETURN key: ");
+ report = instr();
+ cup(4,1);
+ el(0);
+ chrprint(report);
+ if (!strcmp(report, "\015\012")) printf(" -- OK");
+ else printf(" -- Not expected");
+ cup(6,1);
+ rm("20");
+ printf("NewLine mode reset. Push the RETURN key: ");
+ report = instr();
+ cup(7,1);
+ el(0);
+ chrprint(report);
+ if (!strcmp(report, "\015")) printf(" -- OK");
+ else printf(" -- Not expected");
+ cup(9,1);
+#ifdef UNIX
+ sgttyNew.sg_flags |= CRMOD;
+ stty(0, &sgttyNew);
+#endif
+ holdit();
+
+ ed(2);
+ cup(1,1);
+ printf("Test of Device Status Report 5 (report terminal status).");
+ cup(2,1);
+ dsr(5);
+ report = instr();
+ cup(2,1);
+ el(0);
+ printf("Report is: ");
+ chrprint(report);
+ if (!strcmp(report,"\033[0n")) printf(" -- means \"TERMINAL OK\"");
+ else if (!strcmp(report,"\033[3n")) printf(" -- means \"TERMINAL OK\"");
+ else printf(" -- Unknown response!");
+
+ cup(4,1);
+ println("Test of Device Status Report 6 (report cursor position).");
+ cup(5,1);
+ dsr(6);
+ report = instr();
+ cup(5,1);
+ el(0);
+ printf("Report is: ");
+ chrprint(report);
+ if (!strcmp(report,"\033[5;1R")) printf(" -- OK");
+ else printf(" -- Unknown response!");
+
+ cup(7,1);
+ println("Test of Device Attributes report (what are you)");
+ cup(8,1);
+ da(0);
+ report = instr();
+ cup(8,1);
+ el(0);
+ printf("Report is: ");
+ chrprint(report);
+ for (i = 0; *attributes[i][0] != '\0'; i++) {
+ if (!strcmp(report,attributes[i][0])) break;
+ }
+ if (*attributes[i][0] == '\0')
+ printf(" -- Unknown response, refer to the manual");
+ else {
+ printf(" -- means %s", attributes[i][1]);
+ if (i) {
+ cup(9,1);
+ println("Legend: STP = Processor Option");
+ println(" AVO = Advanced Video Option");
+ println(" GPO = Graphics Processor Option");
+ println(" PP = Printer Port");
+ }
+ }
+
+ cup(14,1);
+ println("Test of the \"Request Terminal Parameters\" feature, argument 0.");
+ cup(15,1);
+ decreqtparm(0);
+ report = instr();
+ cup(15,1);
+ el(0);
+ printf("Report is: ");
+ chrprint(report);
+ if (strlen(report) < 16
+ || report[0] != '\033'
+ || report[1] != '['
+ || report[2] != '2'
+ || report[3] != ';')
+ println(" -- Bad format");
+ else {
+ reportpos = 4;
+ parity = scanto(report, &reportpos, ';');
+ nbits = scanto(report, &reportpos, ';');
+ xspeed = scanto(report, &reportpos, ';');
+ rspeed = scanto(report, &reportpos, ';');
+ clkmul = scanto(report, &reportpos, ';');
+ flags = scanto(report, &reportpos, 'x');
+ if (parity == 0 || nbits == 0 || clkmul == 0) println(" -- Bad format");
+ else println(" -- OK");
+ printf(
+ "This means: Parity %s, %s bits, xmitspeed %s, recvspeed %s.\n",
+ lookup(paritytable, parity),
+ lookup(nbitstable, nbits),
+ lookup(speedtable, xspeed),
+ lookup(speedtable, rspeed));
+ printf("(CLoCk MULtiplier = %d, STP option flags = %d)\n", clkmul, flags);
+ }
+
+ cup(19,1);
+ println("Test of the \"Request Terminal Parameters\" feature, argument 1.");
+ cup(20,1);
+ decreqtparm(1); /* Does the same as decreqtparm(0), reports "3" */
+ report2 = instr();
+ cup(20,1);
+ el(0);
+ printf("Report is: ");
+ chrprint(report2);
+ if (strlen(report2) < 3
+ || report2[2] != '3')
+ println(" -- Bad format");
+ else {
+ report2[2] = '2';
+ if (!strcmp(report,report2)) println(" -- OK");
+ else println(" -- Bad format");
+ }
+ cup(24,1);
+ holdit();
+#ifdef UNIX
+ sgttyNew.sg_flags |= ECHO;
+ stty(0, &sgttyNew);
+#endif
+}
+
+tst_vt52() {
+
+ static struct rtabl {
+ char *rcode;
+ char *rmsg;
+ } resptable[] = {
+ { "\033/K", " -- OK (means Standard VT52)" },
+ { "\033/Z", " -- OK (means VT100 emulating VT52)" },
+ { "", " -- Unknown response"}
+ };
+
+ int i,j;
+ char *response;
+
+ rm("?2"); /* Reset ANSI (VT100) mode, Set VT52 mode */
+ esc("H"); /* Cursor home */
+ esc("J"); /* Erase to end of screen */
+ esc("H"); /* Cursor home */
+ for (i = 0; i <= 23; i++) {
+ for (j = 0; j <= 9; j++)
+ printf("%s", "FooBar ");
+ println("Bletch");
+ }
+ esc("H"); /* Cursor home */
+ esc("J"); /* Erase to end of screen */
+
+ vt52cup(7,47);
+ printf("nothing more.");
+ for (i = 1; i <= 10; i++) printf("THIS SHOULD GO AWAY! ");
+ for (i = 1; i <= 5; i++) {
+ vt52cup(1,1);
+ printf("%s", "Back scroll (this should go away)");
+ esc("I"); /* Reverse LineFeed (with backscroll!) */
+ }
+ vt52cup(12,60);
+ esc("J"); /* Erase to end of screen */
+ for (i = 2; i <= 6; i++) {
+ vt52cup(i,1);
+ esc("K"); /* Erase to end of line */
+ }
+
+ for (i = 2; i <= 23; i++) {
+ vt52cup(i,70); printf("%s", "**Foobar");
+ }
+ vt52cup(23,10);
+ for (i = 23; i >= 2; i--) {
+ printf("%s", "*");
+ printf("%c", 8); /* BS */
+ esc("I"); /* Reverse LineFeed (LineStarve) */
+ }
+ vt52cup(1,70);
+ for (i = 70; i >= 10; i--) {
+ printf("%s", "*");
+ esc("D"); esc("D"); /* Cursor Left */
+ }
+ vt52cup(24,10);
+ for (i = 10; i <= 70; i++) {
+ printf("%s", "*");
+ printf("%c", 8); /* BS */
+ esc("C"); /* Cursor Right */
+ }
+ vt52cup(2,11);
+ for (i = 2; i <= 23; i++) {
+ printf("%s", "!");
+ printf("%c", 8); /* BS */
+ esc("B"); /* Cursor Down */
+ }
+ vt52cup(23,69);
+ for (i = 23; i >= 2; i--) {
+ printf("%s", "!");
+ printf("%c", 8); /* BS */
+ esc("A"); /* Cursor Up */
+ }
+ for (i = 2; i <= 23; i++) {
+ vt52cup(i,71);
+ esc("K"); /* Erase to end of line */
+ }
+
+ vt52cup(10,16);
+ printf("%s", "The screen should be cleared, and have a centered");
+ vt52cup(11,16);
+ printf("%s", "rectangle of \"*\"s with \"!\"s on the inside to the");
+ vt52cup(12,16);
+ printf("%s", "left and right. Only this, and");
+ vt52cup(13,16);
+ holdit();
+
+ esc("H"); /* Cursor home */
+ esc("J"); /* Erase to end of screen */
+ printf("%s", "This is the normal character set:");
+ for (j = 0; j <= 1; j++) {
+ vt52cup(3 + j, 16);
+ for (i = 0; i <= 47; i++)
+ printf("%c", 32 + i + 48 * j);
+ }
+ vt52cup(6,1);
+ printf("%s", "This is the special graphics character set:");
+ esc("F"); /* Select Special Graphics character set */
+ for (j = 0; j <= 1; j++) {
+ vt52cup(8 + j, 16);
+ for (i = 0; i <= 47; i++)
+ printf("%c", 32 + i + 48 * j);
+ }
+ esc("G"); /* Select ASCII character set */
+ vt52cup(12,1);
+ holdit();
+
+ esc("H"); /* Cursor home */
+ esc("J"); /* Erase to end of screen */
+ println("Test of terminal response to IDENTIFY command");
+ esc("Z"); /* Identify */
+ response = instr();
+ println("");
+ printf("Response was");
+ esc("<"); /* Enter ANSI mode (VT100 mode) */
+ chrprint(response);
+ for(i = 0; resptable[i].rcode[0] != '\0'; i++)
+ if (!strcmp(response, resptable[i].rcode))
+ break;
+ printf("%s", resptable[i].rmsg);
+ println("");
+ println("");
+ holdit();
+}
+
+tst_insdel() {
+
+ /* Test of:
+ SM/RM(4) (= IRM (Insertion/replacement mode))
+ ICH (Insert Character)
+ DCH (Delete character)
+ IL (Insert line)
+ DL (Delete line)
+ */
+
+ int i, row, col, sw, dblchr, scr132;
+
+ for(scr132 = 0; scr132 <= 1; scr132++) {
+ if (scr132) { sm("?3"); sw = 132; }
+ else { rm("?3"); sw = 80; }
+ ed(2);
+ cup(1,1);
+ for (row=1; row<=24; row++) {
+ cup(row,1);
+ for (col=1; col<=sw; col++)
+ printf("%c", 'A'-1+row);
+ }
+ cup(4,1);
+ printf("Screen accordion test (Insert & Delete Line). "); holdit();
+ ri(); el(2);
+ decstbm( 2,23);
+ sm("?6");
+ cup(1,1);
+ for (row=1; row<=24; row++) {
+ il(row);
+ dl(row);
+ }
+ rm("?6");
+ decstbm( 0, 0);
+ cup(2,1);
+ printf(
+ "Top line: A's, bottom line: X's, this line, nothing more. ");
+ holdit();
+ cup(2,1); ed(0);
+ cup(1,2);
+ printf("B");
+ cub(1);
+ sm("4");
+ for (col=2; col<=sw-1; col++)
+ printf("*");
+ rm("4");
+ cup(4,1);
+ printf("Test of 'Insert Mode'. The top line should be 'A*** ... ***B'. ");
+ holdit(); ri(); el(2);
+ cup(1,2);
+ dch(sw-2);
+ cup(4,1);
+ printf("Test of 'Delete Character'. The top line should be 'AB'. ");
+ holdit();
+
+ for(dblchr = 1; dblchr <= 2; dblchr++) {
+ ed(2);
+ for (row=1; row<=24; row++) {
+ cup(row,1);
+ if (dblchr == 2) decdwl();
+ for (col=1; col<=sw/dblchr; col++)
+ printf("%c", 'A'-1+row);
+ cup(row,sw/dblchr-row);
+ dch(row);
+ }
+ cup(4,1);
+ println("The right column should be staggered ");
+ printf("by one. ");
+ holdit();
+ }
+ ed(2);
+ cup(1,1);
+ println("If your terminal has the ANSI 'Insert Character' function");
+ println("(the VT102 does not), then you should see a line like this");
+ println(" A B C D E F G H I J K L M N O P Q R S T U V W X Y Z");
+ println("below:");
+ println("");
+ for (i = 'Z'; i >= 'A'; i--) {
+ printf("%c\010",i);
+ ich(2);
+ }
+ cup(10,1);
+ holdit();
+
+ if (sw == 132) rm("?3");
+ }
+}
+
+dch(pn) int pn; { brc(pn, 'P'); } /* Delete character */
+ich(pn) int pn; { brc(pn, '@'); } /* Insert character -- not in VT102 */
+dl(pn) int pn; { brc(pn, 'M'); } /* Delete line */
+il(pn) int pn; { brc(pn, 'L'); } /* Insert line */
+
+/* Test of some known VT100 bugs and misfeatures */
+
+tst_bugs() {
+
+ int i, menuchoice;
+
+ static char *menutable[] = {
+ "Exit to main menu",
+ "Bug A: Smooth scroll to jump scroll",
+ "Bug B: Scrolling region",
+ "Bug C: Wide to narrow screen",
+ "Bug D: Narrow to wide screen",
+ "Bug E: Cursor move from double- to single-wide line",
+ "Bug F: Column mode escape sequence",
+ "Wrap around with cursor addressing",
+ "Erase right half of double width lines",
+ "Funny scroll regions",
+ /* Add more here */
+ ""
+ };
+
+ static char *hmsg[] = {
+ "Test of known bugs in the DEC VT100 series. The numbering of some of",
+ "the bugs (A-F) refers to the article 'VT100 MAGIC' by Sami Tabih in",
+ "the 'Proceedings of the DEC Users Society' at St. Louis, Missouri, May",
+ "1983. To understand some of the tests, you have to look at the source",
+ "code or the article. Of course, a good VT100-compatible terminal",
+ "should not have these bugs (or have some means of disabling them)! If",
+ "a bug appears, you might want to RESET the terminal before continuing",
+ "the test. There is a test of the RESET function in the main menu.",
+ "" };
+
+ do {
+ ed(2); cup(1,1);
+ for (i = 0; *hmsg[i]; i++) println(hmsg[i]);
+ println("");
+ println(" Choose bug test number:");
+ menuchoice = menu(menutable);
+ switch (menuchoice) {
+ case 1: bug_a(); break;
+ case 2: bug_b(); break;
+ case 3: bug_c(); break;
+ case 4: bug_d(); break;
+ case 5: bug_e(); break;
+ case 6: bug_f(); break;
+ case 7: bug_w(); break;
+ case 8: bug_l(); break;
+ case 9: bug_s(); break;
+ }
+ } while (menuchoice);
+}
+
+/* Bug A: Smooth scroll to jump scroll */
+
+bug_a() {
+ int i;
+
+ cup (10, 1);
+ println("This is a test of the VT100 'Scroll while toggle softscroll'");
+ println("bug. The cursor may disappear, or move UP the screen, or");
+ println("multiple copies of some lines may appear.");
+ holdit();
+
+ /* Invoke the bug */
+
+ esc ("[24H"); /* Simplified cursor movement */
+ rm("?4"); for (i = 1; i <= 20; i++) printf("\n");
+ sm("?4"); for (i = 1; i <= 10; i++) printf("\n");
+ rm("?4"); for (i = 1; i <= 5; i++) printf("\n");
+
+ /* That should be enough to show the bug. But we'll try another way: */
+ sm ("?4"); /* Set soft scroll */
+ nel (); /* "NextLine", move down */
+ rm ("?4"); /* Reset soft scroll */
+ nel (); /* "NextLine", move down */
+ for (i = 1; i <= 10; i++) { /* Show the bug */
+ printf ("Softscroll bug test, line %d. ", i);
+ holdit();
+ }
+ println("That should have been enough to show the bug, if present.");
+ holdit();
+}
+
+/* Bug B: Scrolling region */
+
+bug_b() {
+ char c;
+
+ decaln();
+ cup( 1,1); el(0);
+ printf("Line 11 should be double-wide, line 12 should be cleared.");
+ cup( 2,1); el(0);
+ printf("Then, the letters A-P should be written at the beginning");
+ cup( 3,1); el(0);
+ printf("of lines 12-24, and the empty line and A-E are scrolled away.");
+ cup( 4,1); el(0);
+ printf("If the bug is present, some lines are confused, look at K-P.");
+ cup(11,1); decdwl();
+ decstbm(12,24);
+ cup(12,1); el(0); printf("Here we go... "); holdit();
+ cup(12,1); ri(); /* Bug comes here */
+ for (c = 'A'; c <= 'P'; c++) printf("%c\n",c); /* Bug shows here */
+ holdit();
+ decstbm(0,0); /* No scr. region */
+}
+
+/* Bug C: Wide to narrow screen */
+
+bug_c() {
+ sm("?3"); /* 132 column mode */
+ cup(1,81);
+ rm("?3"); /* 80 column mode */
+ cup(12,5);
+ printf("Except for this line, the screen should be blank. ");
+ holdit();
+}
+
+/* Bug D: Narrow to wide screen */
+
+bug_d() {
+ int i;
+ char result;
+ /* Make the bug appear */
+ do {
+ cup(14,1);
+
+ /* The original code in the article says
+ * PRINT ESC$; "[13;1H"; CHR$(10%);
+ * but I guess a cup(14,1); would do.
+ * (To output a pure LF might be tricky).
+ */
+
+ sm("?3"); /* Make the bug visible */
+ cup(1,9); decdwl();
+ println("You should see blinking text at the bottom line.");
+ cup(3,9); decdwl();
+ println("Enter 0 to exit, 1 to try to invoke the bug again.");
+ cup(24,9); decdwl(); sgr("1;5;7");
+ printf("If you can see this then the bug did not appear."); sgr("");
+ cup(4,9); decdwl();
+ result = inchar(); readnl();
+ rm("?3");
+ } while (result == '1');
+ sm("?4"); /* Syrup scroll */
+ cup(23,1);
+ for (i = 1; i <= 5; i++)
+ println("If the bug is present, this should make things much worse!");
+ holdit();
+ rm("?4"); /* Jump scroll */
+}
+
+/* Bug E: Cursor move from double- to single-wide line */
+
+bug_e() {
+ int i;
+ static char *rend[2] = { "\033[m", "\033[7m" };
+ sm("?3");
+ cup(1,1); decdwl();
+ println("This test should put an 'X' at line 3 column 100.");
+ for (i = 1; i <= 12; i++) printf("1234567890%s",rend[i & 1]);
+ cup(1,1); /* The bug appears when we jump from a dobule-wide line */
+ cup(3,100); /* to a single-wide line, column > 66. */
+ printf("X");
+ cup(4, 66); printf("! !");
+ cup(5,1);
+ printf("--------------------------- The 'X' should NOT be above here -");
+ printf("---+------------ but above here -----+");
+ cup(10,1); decdwl(); holdit();
+ rm("?3");
+}
+
+/* Bug F: Column mode escape sequence */
+
+bug_f() {
+ int i, row, col;
+
+ /*
+ * VT100 "toggle origin mode, forget rest" bug. If you try to set
+ * (or clear) parameters and one of them is the "origin mode"
+ * ("?6") parameter, parameters that appear after the "?6"
+ * remain unaffected. This is also true on CIT-101 terminals.
+ */
+ sm ("?5"); /* Set reverse mode */
+ sm ("?3"); /* Set 132 column mode */
+ println("Test VT100 'Toggle origin mode, forget rest' bug, part 1.");
+ println("The screen should be in reverse, 132 column mode.");
+ holdit();
+ ed (2);
+ rm ("?6;5;3"); /* Reset (origin, reverse, 132 col) */
+ println("Test VT100 'Toggle origin mode, forget rest' bug, part 2.\n");
+ println("The screen should be in non-reverse, 80 column mode.");
+ holdit();
+}
+
+ /* Bug W:
+ * The dreaded "wraparound" bug! You CUP to col 80, write a char,
+ * CUP to another line in col 80, write a char. And the brain-damaged
+ * terminal thinks that "Hokay, so he's written a char in col 80, so
+ * I stay in col 80 and wait for next character. Let's see now, here
+ * comes another character, and I'm still in col 80, so I must make
+ * a NewLine first." -- It doesn't clear that "still in col 80" flag
+ * on a CUP. Argh!
+ */
+
+bug_w() {
+ int row, col;
+
+ cup (16,1);
+ println(" This illustrates the \"wrap around bug\" which exists on a");
+ println(" standard VT100. At the top of the screen there should be");
+ println(" a row of +'s, and the rightmost column should be filled");
+ println(" with *'s. But if the bug is present, some of the *'s may");
+ println(" be placed in other places, e.g. in the leftmost column,");
+ println(" and the top line of +'s may be scrolled away.");
+
+ cup(1,1);
+ for (col = 1; col <= 79; col++)
+ printf ("+");
+ for (row = 1; row <= 24; row++) {
+ hvp (row, 80);
+ printf ("*");
+ }
+ cup(24,1);
+ holdit();
+}
+
+ /* Bug L:
+ * Check if the right half of double-width lines comes back
+ * when a line is first set to single-width, filled with stuff,
+ * set to double-width, and finally reset to single-width.
+ *
+ * A VT100 has this misfeature, and many others. Foo!
+ */
+
+bug_l() {
+ cup(15, 1);
+ printf("This-is-a-long-line-This-is-a-long-line-");
+ printf("This-is-a-long-line-This-is-a-long-line-");
+ cup(1, 1);
+ printf("This is a test of what happens to the right half of double-width");
+ println(" lines.");
+ printf("A common misfeature is that the right half does not come back");
+ println(" when a long");
+ printf("single-width line is set to double-width and then reset to");
+ println(" single-width.");
+
+ cup(5, 1);
+ println("Now the line below should contain 80 characters in single width.");
+ holdit();
+ cup(15, 1); decdwl();
+ cup(8, 1);
+ println("Now the line below should contain 40 characters in double width.");
+ holdit();
+ cup(15, 1); decswl();
+ cup(11, 1);
+ println("Now the line below should contain 80 characters in single width.");
+ holdit();
+
+ /* ...and in 132 column mode */
+
+ sm("?3");
+ ed(2);
+ cup(15, 1);
+ printf("This-is-a-long-line-This-is-a-long-line-");
+ printf("This-is-a-long-line-This-is-a-long-line-");
+ printf("This-is-a-long-line-This-is-a-long-line-");
+ printf("ending-here-");
+
+ cup(1, 1);
+ printf("This is the same test in 132 column mode.");
+
+ cup(5, 1);
+ println("Now the line below should contain 132 characters in single width.");
+ holdit();
+ cup(15, 1); decdwl();
+ cup(8, 1);
+ println("Now the line below should contain 66 characters in double width.");
+ holdit();
+ cup(15, 1); decswl();
+ cup(11, 1);
+ println("Now the line below should contain 132 characters in single width.");
+ holdit();
+ rm("?3");
+}
+
+bug_s() {
+ int i;
+ decstbm(20,10); /* 20-10=-10, < 2, so no scroll region. */
+ cup(1,1);
+ for (i=1; i<=20; i++)
+ printf("This is 20 lines of text (line %d), no scroll region.\n", i);
+ holdit();
+ ed(2);
+ decstbm(0,1); /* Should be interpreted as decstbm(1,1) = none */
+ cup(1,1);
+ for (i=1; i<=20; i++)
+ printf("This is 20 lines of text (line %d), no scroll region.\n", i);
+ holdit();
+ decstbm(0,0); /* No scroll region (just in case...) */
+}
+
+tst_rst() {
+
+ /*
+ * Test of
+ * - RIS (Reset to Initial State)
+ * - DECTST (invoke terminal test)
+ */
+
+ cup(10,1);
+ printf ("The terminal will now be RESET. ");
+ holdit();
+ ris();
+#ifdef UNIX
+ fflush(stdout);
+#endif
+ zleep(5000); /* Wait 5.0 seconds */
+ cup(10,1);
+ println("The terminal is now RESET. Next, the built-in confidence test");
+ printf("%s", "will be invoked. ");
+ holdit();
+ ed(2);
+ dectst(1);
+#ifdef UNIX
+ fflush(stdout);
+#endif
+ zleep(5000); /* Wait 5.0 seconds */
+ cup(10,1);
+ println("If the built-in confidence test found any errors, a code");
+ printf("%s", "is visible above. ");
+ holdit();
+}
+
+initterminal(pn) int pn; {
+
+#ifdef UNIX
+ if (pn==0) {
+ fflush(stdout);
+ gtty(0,&sgttyOrg);
+ gtty(0,&sgttyNew);
+ sgttyNew.sg_flags |= CBREAK;
+ }
+ else {
+ fflush(stdout);
+ inflush();
+ sleep(2);
+ sgttyNew.sg_flags = sgttyOrg.sg_flags | CBREAK;
+ }
+ stty(0,&sgttyNew);
+#ifdef SIII
+ close(2);
+ open("/dev/tty",O_RDWR|O_NDELAY);
+#endif
+#endif
+#ifdef SARG10
+ /* Set up neccesary TOPS-10 terminal parameters */
+
+ trmop(02041, `VT100`); /* tty type vt100 */
+ trmop(02002, 0); /* tty no tape */
+ trmop(02003, 0); /* tty lc */
+ trmop(02005, 1); /* tty tab */
+ trmop(02010, 1); /* tty no crlf */
+ trmop(02020, 0); /* tty no tape */
+ trmop(02021, 1); /* tty page */
+ trmop(02025, 0); /* tty blanks */
+ trmop(02026, 1); /* tty no alt */
+ trmop(02040, 1); /* tty defer */
+#endif
+#ifdef SARG20
+ ttybin(1); /* set line to binary mode */
+#endif
+ /* Set up my personal prejudices */
+
+ esc("<"); /* Enter ANSI mode (if in VT52 mode) */
+ rm("?1"); /* cursor keys normal */
+ rm("?3"); /* 80 col mode */
+ rm("?4"); /* Jump scroll */
+ rm("?5"); /* Normal screen */
+ rm("?6"); /* Absolute origin mode */
+ sm("?7"); /* Wrap around on */
+ rm("?8"); /* Auto repeat off */
+ decstbm(0,0); /* No scroll region */
+ sgr("0"); /* Normal character attributes */
+
+}
+
+bye () {
+ /* Force my personal prejudices upon the poor luser */
+
+ esc("<"); /* Enter ANSI mode (if in VT52 mode) */
+ rm("?1"); /* cursor keys normal */
+ rm("?3"); /* 80 col mode */
+ rm("?5"); /* Normal screen */
+ rm("?6"); /* Absolute origin mode */
+ sm("?7"); /* Wrap around on */
+ sm("?8"); /* Auto repeat on */
+ decstbm(0,0); /* No scroll region */
+ sgr("0"); /* Normal character attributes */
+
+ /* Say goodbye */
+
+ ed(2);
+ cup(12,30);
+ printf("That's all, folks!\n");
+ printf("\n\n\n");
+ inflush();
+#ifdef SARG20
+ ttybin(0); /* reset line to normal mode */
+#endif
+#ifdef UNIX
+ stty(0,&sgttyOrg);
+#endif
+ exit();
+}
+
+#ifdef UNIX
+onbrk() {
+ signal(SIGINT, onbrk);
+ if (reading)
+ brkrd = 1;
+ else
+ longjmp(intrenv, 1);
+}
+
+onterm() {
+ signal(SIGTERM, onterm);
+ longjmp(intrenv, 1);
+}
+#endif
+
+holdit() {
+ inflush();
+ printf("Push <RETURN>");
+ readnl();
+}
+
+readnl() {
+#ifdef UNIX
+ char ch;
+ fflush(stdout);
+ brkrd = 0;
+ reading = 1;
+ do { read(0,&ch,1); } while(ch != '\n' && !brkrd);
+ if (brkrd)
+ kill(getpid(), SIGTERM);
+ reading = 0;
+#endif
+#ifdef SARG10
+ while (getchar() != '\n')
+ ;
+#endif
+#ifdef SARG20
+ while (getchar() != '\n')
+ ;
+#endif
+}
+
+scanto(str, pos, toc) char *str; int *pos; char toc; {
+ char c;
+ int result = 0;
+
+ while (toc != (c = str[(*pos)++])) {
+ if (isdigit(c)) result = result * 10 + c - '0';
+ else break;
+ }
+ if (c == toc) return(result);
+ else return(0);
+}
+
+char *lookup(t, k) struct table t[]; int k; {
+
+ int i;
+ for (i = 0; t[i].key != -1; i++) {
+ if (t[i].key == k) return(t[i].msg);
+ }
+ return("BAD VALUE");
+}
+
+menu(table) char *table[]; {
+
+ int i, tablesize, choice;
+ char c;
+ char storage[80];
+ char *s = storage;
+ println("");
+ tablesize = 0;
+ for (i = 0; *table[i] != '\0'; i++) {
+ printf(" %d. %s\n", i, table[i]);
+ tablesize++;
+ }
+ tablesize--;
+
+ printf("\n Enter choice number (0 - %d): ", tablesize);
+ for(;;) {
+ inputline(s);
+ choice = 0;
+ while (c = *s++) choice = 10 * choice + c - '0';
+ if (choice >= 0 && choice <= tablesize) {
+ ed(2);
+ return (choice);
+ }
+ printf(" Bad choice, try again: ");
+ }
+}
+
+chrprint (s) char *s; {
+
+ int i;
+
+ printf(" ");
+ sgr("7");
+ printf(" ");
+ for (i = 0; s[i] != '\0'; i++) {
+ if (s[i] <= ' ' || s[i] == '\177')
+ printf("<%d> ", s[i]);
+ else printf("%c ", s[i]);
+ }
+ sgr("");
+}
diff --git a/usr.sbin/pcvt/vttest/vttest.1 b/usr.sbin/pcvt/vttest/vttest.1
new file mode 100644
index 0000000..c410492
--- /dev/null
+++ b/usr.sbin/pcvt/vttest/vttest.1
@@ -0,0 +1,13 @@
+.TH VTTEST 1 "LOCAL"
+.SH NAME
+vttest \- test VT100-type terminal
+.SH SYNOPSIS
+.B vttest
+.SH DESCRIPTION
+.I Vttest
+is a program designed to test the functionality of a VT100 terminal
+(or emulator thereof).
+It tests both display (escape sequence handling) and keyboard.
+.PP
+The program is menu\-driven and contains full on\-line operating
+instructions.
diff --git a/usr.sbin/periodic/Makefile b/usr.sbin/periodic/Makefile
new file mode 100644
index 0000000..d629790
--- /dev/null
+++ b/usr.sbin/periodic/Makefile
@@ -0,0 +1,11 @@
+# Makefile for periodic(8)
+#
+# $Id$
+
+MAN8= periodic.8
+
+beforeinstall:
+ ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \
+ ${.CURDIR}/periodic.sh ${DESTDIR}${BINDIR}/periodic
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/periodic/periodic.8 b/usr.sbin/periodic/periodic.8
new file mode 100644
index 0000000..3ac852c
--- /dev/null
+++ b/usr.sbin/periodic/periodic.8
@@ -0,0 +1,137 @@
+.\" Copyright (c) 1997 FreeBSD, Inc.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $Id: periodic.8,v 1.5 1997/08/19 16:49:35 pst Exp $
+.\"
+.Dd 13 August 1997
+.Os FreeBSD 3.0
+.Dt PERIODIC 8
+.Sh NAME
+.Nm periodic
+.Nd
+run periodic system functions
+.Sh SYNOPSIS
+.Nm periodic
+.Ao
+.Cm daily | weekly | monthly |
+.Ar path Op path ...
+.Ac
+.Sh DESCRIPTION
+The
+.Nm
+program is intended to be called by cron(8) to execute shell scripts
+located in the specified directory.
+.Pp
+One, and only one, of the following arguments should be specified:
+.Bl -tag -width Fl
+.It Cm daily
+Perform the standard daily periodic executable run.
+This usually occurs early in the morning (local time).
+.It Cm weekly
+Perform the standard weekly periodic executable run.
+This usually occurs on Sunday mornings.
+.It Cm monthly
+Perform the standard monthly periodic executable run.
+This usually occurs on the first day of the month.
+.It Ar path
+An absolute path to a directory containing a set of executables to be run.
+.El
+.Pp
+The
+.Nm
+program will run each executable file in the directory or directories
+specified. If a file does not have the executable bit set, it will be
+ignored silently.
+.Sh ENVIRONMENT
+The
+.Nm
+command sets the
+.Ev PATH
+environment to include all standard system directories, but no additional
+directories, such as
+.Pa /usr/local/bin .
+If executables are added which depend upon other path components, each
+executable must be responsible for configuring its own appropriate environment.
+.Sh FILES
+.Bl -tag -width /etc/periodic
+.It Pa /etc/crontab
+The
+.Nm
+program is typically called via entries in the system default cron table.
+.It Pa /etc/periodic
+The top level directory containing
+.Pa daily ,
+.Pa weekly ,
+and
+.Pa monthly
+subdirectories which contain standard system periodic executables.
+.It Pa /etc/rc.conf
+The
+.Pa rc.conf
+system registry contains a variable
+.Va local_periodic
+which may be configured to specify additional top level standard
+periodic directories, such as
+.Pa /usr/local/etc/periodic
+and
+.Pa /usr/X11R6/etc/periodic .
+.El
+.Sh EXAMPLES
+The system crontab should have entries for
+.Nm
+similar to the following example:
+.Pp
+.Dl # do daily/weekly/monthly maintenance
+.Dl 0 2 * * * root periodic daily 2>&1
+.Dl 0 3 * * 6 root periodic weekly 2>&1
+.Dl 0 5 1 * * root periodic monthly 2>&1
+.Pp
+Additionally, the system registry will typically have a
+.Va local_cron
+variable reading:
+.Dl local_cron="/usr/local/etc/periodic /usr/X11R6/etc/periodic" # cron script dirs.
+.Sh SEE ALSO
+.Xr sh 1 ,
+.Xr crontab 5 ,
+.Xr rc.conf 5 ,
+.Xr cron 8
+.Rs
+.Sh DIAGNOSTICS
+Exit status is 0 on success, and 1 if the command
+fails for one of the following reasons:
+.Bl -diag
+.It usage: periodic <directory of files to execute>
+No directory path argument was passed to
+.Nm
+to specify where the script fragments reside.
+.It <directory> not found
+Self explanatory.
+.El
+.Sh HISTORY
+The
+.Nm
+program first appeared in
+.Fx 3.0 .
+.Sh AUTHOR
+.An Paul Traina Aq pst@FreeBSD.ORG .
diff --git a/usr.sbin/periodic/periodic.sh b/usr.sbin/periodic/periodic.sh
new file mode 100644
index 0000000..593d6c5
--- /dev/null
+++ b/usr.sbin/periodic/periodic.sh
@@ -0,0 +1,64 @@
+#!/bin/sh -
+#
+# $Id: periodic.sh,v 1.4 1997/08/16 17:08:35 pst Exp $
+#
+# Run nightly periodic scripts
+#
+# usage: periodic { daily | weekly | monthly } - run standard periodic scripts
+# periodic /absolute/path/to/directory - run periodic scripts in dir
+#
+
+usage () {
+ echo "usage: $0 <directory of files to execute>" 1>&2
+ echo "or $0 { daily | weekly | monthly }" 1>&2
+ exit 1
+}
+
+if [ $# -lt 1 ] ; then
+ usage
+fi
+
+# If possible, check /etc/rc.conf to see if there are additional dirs to check
+if [ -r /etc/rc.conf ] ; then
+ . /etc/rc.conf
+fi
+
+dir=$1
+run=`basename $dir`
+
+# If a full path was not specified, check the standard cron areas
+
+if [ "$dir" = "$run" ] ; then
+ dirlist=""
+ for top in /etc/periodic ${local_periodic} ; do
+ if [ -d $top/$dir ] ; then
+ dirlist="${dirlist} $top/$dir"
+ fi
+ done
+
+# User wants us to run stuff in a particular directory
+else
+ for dir in $* ; do
+ if [ ! -d $dir ] ; then
+ echo "$0: $dir not found" 1>&2
+ exit 1
+ fi
+ done
+
+ dirlist="$*"
+fi
+
+host=`hostname -s`
+echo "Subject: $host $run run output"
+
+# Execute each executable file in the directory list. If the x bit is not
+# set, assume the user didn't really want us to muck with it (it's a
+# README file or has been disabled).
+
+for dir in $dirlist ; do
+ for file in $dir/* ; do
+ if [ -x $file ] ; then
+ $file
+ fi
+ done
+done
diff --git a/usr.sbin/pkg_install/Makefile b/usr.sbin/pkg_install/Makefile
new file mode 100644
index 0000000..37a1f05
--- /dev/null
+++ b/usr.sbin/pkg_install/Makefile
@@ -0,0 +1,3 @@
+SUBDIR=lib add create delete info
+
+.include <bsd.subdir.mk>
diff --git a/usr.sbin/pkg_install/Makefile.inc b/usr.sbin/pkg_install/Makefile.inc
new file mode 100644
index 0000000..50332c0
--- /dev/null
+++ b/usr.sbin/pkg_install/Makefile.inc
@@ -0,0 +1,2 @@
+# Inherit BINDIR from one level up.
+.include "../Makefile.inc"
diff --git a/usr.sbin/pkg_install/README b/usr.sbin/pkg_install/README
new file mode 100644
index 0000000..a5a517d
--- /dev/null
+++ b/usr.sbin/pkg_install/README
@@ -0,0 +1,8 @@
+This is the pkg_install suite of tools for doing maintainance of
+software "packages". More documentation is available in the man pages
+for each individual command.
+
+This code was written by Jordan Hubbard for FreeBSD, snatched and
+mildly reshaped by John Kohl in NetBSD and the changes taken back into
+FreeBSD again by Jordan, who then proceeded to add another couple
+of dozen features on top. Whee! :-)
diff --git a/usr.sbin/pkg_install/add/Makefile b/usr.sbin/pkg_install/add/Makefile
new file mode 100644
index 0000000..83a022e
--- /dev/null
+++ b/usr.sbin/pkg_install/add/Makefile
@@ -0,0 +1,18 @@
+PROG= pkg_add
+
+CFLAGS+= ${DEBUG} -I${.CURDIR}/../lib
+
+.if exists(${.OBJDIR}/../lib)
+LDADD+= -L${.OBJDIR}/../lib -linstall
+DPADD+= ${.OBJDIR}/../lib/libinstall.a
+.else
+LDADD+= -L${.CURDIR}/../lib -linstall
+DPADD+= ${.CURDIR}/../lib/libinstall.a
+.endif
+
+LDADD+= -lftpio -lmd
+DPADD+= ${LIBFTPIO} ${LIBMD}
+
+SRCS= main.c perform.c futil.c extract.c
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/pkg_install/add/add.h b/usr.sbin/pkg_install/add/add.h
new file mode 100644
index 0000000..a216944
--- /dev/null
+++ b/usr.sbin/pkg_install/add/add.h
@@ -0,0 +1,44 @@
+/* $Id$ */
+
+/*
+ * FreeBSD install - a package for the installation and maintainance
+ * of non-core utilities.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * Jordan K. Hubbard
+ * 18 July 1993
+ *
+ * Include and define various things wanted by the add command.
+ *
+ */
+
+#ifndef _INST_ADD_H_INCLUDE
+#define _INST_ADD_H_INCLUDE
+
+typedef enum { NORMAL, MASTER, SLAVE } add_mode_t;
+
+extern char *Prefix;
+extern Boolean NoInstall;
+extern Boolean NoRecord;
+extern Boolean Force;
+extern char *Mode;
+extern char *Owner;
+extern char *Group;
+extern char *Directory;
+extern char *PkgName;
+extern char FirstPen[];
+extern add_mode_t AddMode;
+
+int make_hierarchy(char *);
+void extract_plist(char *, Package *);
+void apply_perms(char *, char *);
+
+#endif /* _INST_ADD_H_INCLUDE */
diff --git a/usr.sbin/pkg_install/add/extract.c b/usr.sbin/pkg_install/add/extract.c
new file mode 100644
index 0000000..794e377
--- /dev/null
+++ b/usr.sbin/pkg_install/add/extract.c
@@ -0,0 +1,232 @@
+#ifndef lint
+static const char rcsid[] =
+ "$Id: extract.c,v 1.17 1997/10/08 07:45:35 charnier Exp $";
+#endif
+
+/*
+ * FreeBSD install - a package for the installation and maintainance
+ * of non-core utilities.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * Jordan K. Hubbard
+ * 18 July 1993
+ *
+ * This is the package extraction code for the add module.
+ *
+ */
+
+#include <err.h>
+#include "lib.h"
+#include "add.h"
+
+
+#define STARTSTRING "tar cf - "
+#define TOOBIG(str) ((strlen(str) + 22 + strlen(home) + where_count > maxargs) \
+ || (strlen(str) + 6 + strlen(home) + perm_count > maxargs))
+
+#define PUSHOUT(todir) /* push out string */ \
+ if (where_count > sizeof(STARTSTRING)-1) { \
+ strcat(where_args, "|tar xf - -C "); \
+ strcat(where_args, todir); \
+ if (system(where_args)) \
+ cleanup(0), errx(2, "can not invoke %d byte tar pipeline: %s", \
+ strlen(where_args), where_args); \
+ strcpy(where_args, STARTSTRING); \
+ where_count = sizeof(STARTSTRING)-1; \
+ } \
+ if (perm_count) { \
+ apply_perms(todir, perm_args); \
+ perm_args[0] = 0;\
+ perm_count = 0; \
+ }
+
+static void
+rollback(char *name, char *home, PackingList start, PackingList stop)
+{
+ PackingList q;
+ char try[FILENAME_MAX], bup[FILENAME_MAX], *dir;
+
+ dir = home;
+ for (q = start; q != stop; q = q->next) {
+ if (q->type == PLIST_FILE) {
+ snprintf(try, FILENAME_MAX, "%s/%s", dir, q->name);
+ if (make_preserve_name(bup, FILENAME_MAX, name, try) && fexists(bup)) {
+ (void)chflags(try, 0);
+ (void)unlink(try);
+ if (rename(bup, try))
+ warnx("rollback: unable to rename %s back to %s", bup, try);
+ }
+ }
+ else if (q->type == PLIST_CWD) {
+ if (strcmp(q->name, "."))
+ dir = q->name;
+ else
+ dir = home;
+ }
+ }
+}
+
+void
+extract_plist(char *home, Package *pkg)
+{
+ PackingList p = pkg->head;
+ char *last_file;
+ char *where_args, *perm_args, *last_chdir;
+ int maxargs, where_count = 0, perm_count = 0, add_count;
+ Boolean preserve;
+
+ maxargs = sysconf(_SC_ARG_MAX) / 2; /* Just use half the argument space */
+ where_args = alloca(maxargs);
+ if (!where_args)
+ cleanup(0), errx(2, "can't get argument list space");
+ perm_args = alloca(maxargs);
+ if (!perm_args)
+ cleanup(0), errx(2, "can't get argument list space");
+
+ strcpy(where_args, STARTSTRING);
+ where_count = sizeof(STARTSTRING)-1;
+ perm_args[0] = 0;
+
+ last_chdir = 0;
+ preserve = find_plist_option(pkg, "preserve") ? TRUE : FALSE;
+
+ /* Reset the world */
+ Owner = NULL;
+ Group = NULL;
+ Mode = NULL;
+ last_file = NULL;
+ Directory = home;
+
+ /* Do it */
+ while (p) {
+ char cmd[FILENAME_MAX];
+
+ switch(p->type) {
+ case PLIST_NAME:
+ PkgName = p->name;
+ if (Verbose)
+ printf("extract: Package name is %s\n", p->name);
+ break;
+
+ case PLIST_FILE:
+ last_file = p->name;
+ if (Verbose)
+ printf("extract: %s/%s\n", Directory, p->name);
+ if (!Fake) {
+ char try[FILENAME_MAX];
+
+ /* first try to rename it into place */
+ snprintf(try, FILENAME_MAX, "%s/%s", Directory, p->name);
+ if (fexists(try)) {
+ (void)chflags(try, 0); /* XXX hack - if truly immutable, rename fails */
+ if (preserve && PkgName) {
+ char pf[FILENAME_MAX];
+
+ if (make_preserve_name(pf, FILENAME_MAX, PkgName, try)) {
+ if (rename(try, pf)) {
+ warnx(
+ "unable to back up %s to %s, aborting pkg_add",
+ try, pf);
+ rollback(PkgName, home, pkg->head, p);
+ return;
+ }
+ }
+ }
+ }
+ if (rename(p->name, try) == 0) {
+ /* try to add to list of perms to be changed and run in bulk. */
+ if (p->name[0] == '/' || TOOBIG(p->name)) {
+ PUSHOUT(Directory);
+ }
+ add_count = snprintf(&perm_args[perm_count], maxargs - perm_count, "%s ", p->name);
+ if (add_count > maxargs - perm_count)
+ cleanup(0), errx(2, "oops, miscounted strings!");
+ perm_count += add_count;
+ }
+ else {
+ /* rename failed, try copying with a big tar command */
+ if (last_chdir != Directory) {
+ PUSHOUT(last_chdir);
+ last_chdir = Directory;
+ }
+ else if (p->name[0] == '/' || TOOBIG(p->name)) {
+ PUSHOUT(Directory);
+ }
+ add_count = snprintf(&where_args[where_count], maxargs - where_count, " %s", p->name);
+ if (add_count > maxargs - where_count)
+ cleanup(0), errx(2, "oops, miscounted strings!");
+ where_count += add_count;
+ add_count = snprintf(&perm_args[perm_count],
+ maxargs - perm_count,
+ "%s ", p->name);
+ if (add_count > maxargs - perm_count)
+ cleanup(0), errx(2, "oops, miscounted strings!");
+ perm_count += add_count;
+ }
+ }
+ break;
+
+ case PLIST_CWD:
+ if (Verbose)
+ printf("extract: CWD to %s\n", p->name);
+ PUSHOUT(Directory);
+ if (strcmp(p->name, ".")) {
+ if (!Fake && make_hierarchy(p->name) == FAIL)
+ cleanup(0), errx(2, "unable to make directory '%s'",
+ p->name);
+ Directory = p->name;
+ }
+ else
+ Directory = home;
+ break;
+
+ case PLIST_CMD:
+ if ((strstr(p->name, "%B") || strstr(p->name, "%F") ||
+ strstr(p->name, "%f")) && last_file == NULL)
+ cleanup(0), errx(2, "no last file specified for '%s' command",
+ p->name);
+ if (strstr(p->name, "%D") && Directory == NULL)
+ cleanup(0), errx(2, "no directory specified for '%s' command",
+ p->name);
+ format_cmd(cmd, p->name, Directory, last_file);
+ PUSHOUT(Directory);
+ if (Verbose)
+ printf("extract: execute '%s'\n", cmd);
+ if (!Fake && system(cmd))
+ warnx("command '%s' failed", cmd);
+ break;
+
+ case PLIST_CHMOD:
+ PUSHOUT(Directory);
+ Mode = p->name;
+ break;
+
+ case PLIST_CHOWN:
+ PUSHOUT(Directory);
+ Owner = p->name;
+ break;
+
+ case PLIST_CHGRP:
+ PUSHOUT(Directory);
+ Group = p->name;
+ break;
+
+ case PLIST_COMMENT:
+ break;
+
+ case PLIST_IGNORE:
+ p = p->next;
+ break;
+ }
+ p = p->next;
+ }
+ PUSHOUT(Directory);
+}
diff --git a/usr.sbin/pkg_install/add/futil.c b/usr.sbin/pkg_install/add/futil.c
new file mode 100644
index 0000000..1385ed5
--- /dev/null
+++ b/usr.sbin/pkg_install/add/futil.c
@@ -0,0 +1,93 @@
+#ifndef lint
+static const char rcsid[] =
+ "$Id: futil.c,v 1.6 1997/02/22 16:09:17 peter Exp $";
+#endif
+
+/*
+ * FreeBSD install - a package for the installation and maintainance
+ * of non-core utilities.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * Jordan K. Hubbard
+ * 18 July 1993
+ *
+ * Miscellaneous file access utilities.
+ *
+ */
+
+#include <err.h>
+#include "lib.h"
+#include "add.h"
+
+/*
+ * Assuming dir is a desired directory name, make it and all intervening
+ * directories necessary.
+ */
+
+int
+make_hierarchy(char *dir)
+{
+ char *cp1, *cp2;
+
+ if (dir[0] == '/')
+ cp1 = cp2 = dir + 1;
+ else
+ cp1 = cp2 = dir;
+ while (cp2) {
+ if ((cp2 = index(cp1, '/')) !=NULL )
+ *cp2 = '\0';
+ if (fexists(dir)) {
+ if (!isdir(dir))
+ return FAIL;
+ }
+ else {
+ if (vsystem("mkdir %s", dir))
+ return FAIL;
+ apply_perms(NULL, dir);
+ }
+ /* Put it back */
+ if (cp2) {
+ *cp2 = '/';
+ cp1 = cp2 + 1;
+ }
+ }
+ return SUCCESS;
+}
+
+/* Using permission defaults, apply them as necessary */
+void
+apply_perms(char *dir, char *arg)
+{
+ char *cd_to;
+
+ if (!dir || *arg == '/') /* absolute path? */
+ cd_to = "/";
+ else
+ cd_to = dir;
+
+ if (Mode)
+ if (vsystem("cd %s && chmod -R %s %s", cd_to, Mode, arg))
+ warnx("couldn't change modes of '%s' to '%s'", arg, Mode);
+ if (Owner && Group) {
+ if (vsystem("cd %s && chown -R %s.%s %s", cd_to, Owner, Group, arg))
+ warnx("couldn't change owner/group of '%s' to '%s.%s'",
+ arg, Owner, Group);
+ return;
+ }
+ if (Owner) {
+ if (vsystem("cd %s && chown -R %s %s", cd_to, Owner, arg))
+ warnx("couldn't change owner of '%s' to '%s'", arg, Owner);
+ return;
+ } else if (Group)
+ if (vsystem("cd %s && chgrp -R %s %s", cd_to, Group, arg))
+ warnx("couldn't change group of '%s' to '%s'", arg, Group);
+}
+
diff --git a/usr.sbin/pkg_install/add/main.c b/usr.sbin/pkg_install/add/main.c
new file mode 100644
index 0000000..da154c1
--- /dev/null
+++ b/usr.sbin/pkg_install/add/main.c
@@ -0,0 +1,157 @@
+#ifndef lint
+static const char rcsid[] =
+ "$Id: main.c,v 1.15 1997/09/18 14:08:27 phk Exp $";
+#endif
+
+/*
+ *
+ * FreeBSD install - a package for the installation and maintainance
+ * of non-core utilities.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * Jordan K. Hubbard
+ * 18 July 1993
+ *
+ * This is the add module.
+ *
+ */
+
+#include <err.h>
+#include <sys/param.h>
+#include "lib.h"
+#include "add.h"
+
+static char Options[] = "hvIRfnp:SMt:";
+
+char *Prefix = NULL;
+Boolean NoInstall = FALSE;
+Boolean NoRecord = FALSE;
+
+char *Mode = NULL;
+char *Owner = NULL;
+char *Group = NULL;
+char *PkgName = NULL;
+char *Directory = NULL;
+char FirstPen[FILENAME_MAX];
+add_mode_t AddMode = NORMAL;
+
+#define MAX_PKGS 20
+char pkgnames[MAX_PKGS][MAXPATHLEN];
+char *pkgs[MAX_PKGS];
+
+static void usage __P((void));
+
+int
+main(int argc, char **argv)
+{
+ int ch, err;
+ char **start;
+ char *cp;
+
+ 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 't':
+ strcpy(FirstPen, optarg);
+ break;
+
+ case 'S':
+ AddMode = SLAVE;
+ break;
+
+ case 'M':
+ AddMode = MASTER;
+ break;
+
+ case 'h':
+ case '?':
+ default:
+ usage();
+ break;
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc > MAX_PKGS) {
+ warnx("too many packages (max %d)", MAX_PKGS);
+ return(1);
+ }
+
+ if (AddMode != SLAVE) {
+ for (ch = 0; ch < MAX_PKGS; pkgs[ch++] = NULL) ;
+
+ /* Get all the remaining package names, if any */
+ for (ch = 0; *argv; ch++, argv++) {
+ if (!strcmp(*argv, "-")) /* stdin? */
+ pkgs[ch] = "-";
+ else if (isURL(*argv)) /* preserve URLs */
+ pkgs[ch] = strcpy(pkgnames[ch], *argv);
+ else { /* expand all pathnames to fullnames */
+ if (fexists(*argv)) /* refers to a file directly */
+ pkgs[ch] = realpath(*argv, pkgnames[ch]);
+ else { /* look for the file in the expected places */
+ if (!(cp = fileFindByPath(NULL, *argv)))
+ warnx("can't find package '%s'", *argv);
+ else
+ pkgs[ch] = strcpy(pkgnames[ch], cp);
+ }
+ }
+ }
+ }
+ /* If no packages, yelp */
+ else if (!ch)
+ warnx("missing package name(s)"), usage();
+ else if (ch > 1 && AddMode == MASTER)
+ warnx("only one package name may be specified with master mode"),
+ usage();
+ if ((err = pkg_perform(pkgs)) != 0) {
+ if (Verbose)
+ warnx("%d package addition(s) failed", err);
+ return err;
+ }
+ else
+ return 0;
+}
+
+static void
+usage()
+{
+ fprintf(stderr, "%s\n%s\n",
+ "usage: pkg_add [-vInfRMS] [-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..28cd30f
--- /dev/null
+++ b/usr.sbin/pkg_install/add/perform.c
@@ -0,0 +1,479 @@
+#ifndef lint
+static const char rcsid[] =
+ "$Id: perform.c,v 1.44 1997/10/13 15:03:46 jkh Exp $";
+#endif
+
+/*
+ * FreeBSD install - a package for the installation and maintainance
+ * of non-core utilities.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * Jordan K. Hubbard
+ * 18 July 1993
+ *
+ * This is the main body of the add module.
+ *
+ */
+
+#include <err.h>
+#include "lib.h"
+#include "add.h"
+
+#include <signal.h>
+#include <sys/wait.h>
+
+static int pkg_do(char *);
+static int sanity_check(char *);
+static char LogDir[FILENAME_MAX];
+
+int
+pkg_perform(char **pkgs)
+{
+ int i, err_cnt = 0;
+
+ signal(SIGINT, cleanup);
+ signal(SIGHUP, cleanup);
+
+ if (AddMode == SLAVE)
+ err_cnt = pkg_do(NULL);
+ else {
+ for (i = 0; pkgs[i]; i++)
+ err_cnt += pkg_do(pkgs[i]);
+ }
+ return err_cnt;
+}
+
+static Package Plist;
+static char *Home;
+
+/*
+ * This is seriously ugly code following. Written very fast!
+ * [And subsequently made even worse.. Sigh! This code was just born
+ * to be hacked, I guess.. :) -jkh]
+ */
+static int
+pkg_do(char *pkg)
+{
+ char pkg_fullname[FILENAME_MAX];
+ char playpen[FILENAME_MAX];
+ char extract_contents[FILENAME_MAX];
+ char *where_to, *tmp, *extract;
+ FILE *cfile;
+ int code;
+ PackingList p;
+ struct stat sb;
+ int inPlace;
+
+ code = 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)
+ warnx("unable to make playpen for %d bytes", sb.st_size * 4);
+ where_to = Home;
+ if (unpack(pkg_fullname, extract)) {
+ warnx(
+ "unable to extract table of contents file from `%s' - not a package?",
+ pkg_fullname);
+ goto bomb;
+ }
+ cfile = fopen(CONTENTS_FNAME, "r");
+ if (!cfile) {
+ warnx(
+ "unable to open table of contents file `%s' - not a package?",
+ CONTENTS_FNAME);
+ goto bomb;
+ }
+ read_plist(&Plist, cfile);
+ fclose(cfile);
+
+ /* Extract directly rather than moving? Oh goodie! */
+ if (find_plist_option(&Plist, "extract-in-place")) {
+ if (Verbose)
+ printf("Doing in-place extraction for %s\n", pkg_fullname);
+ p = find_plist(&Plist, PLIST_CWD);
+ if (p) {
+ if (!isdir(p->name) && !Fake) {
+ if (Verbose)
+ printf("Desired prefix of %s does not exist, creating..\n", p->name);
+ vsystem("mkdir -p %s", p->name);
+ if (chdir(p->name) == -1) {
+ warn("unable to change directory to `%s'", p->name);
+ goto bomb;
+ }
+ }
+ where_to = p->name;
+ inPlace = 1;
+ }
+ else {
+ warnx(
+ "no prefix specified in `%s' - this is a bad package!",
+ pkg_fullname);
+ goto bomb;
+ }
+ }
+
+ /*
+ * Apply a crude heuristic to see how much space the package will
+ * take up once it's unpacked. I've noticed that most packages
+ * compress an average of 75%, so multiply by 4 for good measure.
+ */
+
+ if (!inPlace && min_free(playpen) < sb.st_size * 4) {
+ warnx("projected size of %d exceeds available free space.\n"
+"Please set your PKG_TMPDIR variable to point to a location with more\n"
+ "free space and try again", sb.st_size * 4);
+ warnx("not extracting %s\ninto %s, sorry!",
+ pkg_fullname, where_to);
+ goto bomb;
+ }
+
+ /* If this is a direct extract and we didn't want it, stop now */
+ if (inPlace && Fake)
+ goto success;
+
+ /* Finally unpack the whole mess */
+ if (unpack(pkg_fullname, NULL)) {
+ warnx("unable to extract `%s'!", pkg_fullname);
+ goto bomb;
+ }
+ }
+
+ /* Check for sanity and dependencies */
+ if (sanity_check(pkg))
+ goto bomb;
+
+ /* If we're running in MASTER mode, just output the plist and return */
+ if (AddMode == MASTER) {
+ printf("%s\n", where_playpen());
+ write_plist(&Plist, stdout);
+ return 0;
+ }
+ }
+
+ /*
+ * If we have a prefix, delete the first one we see and add this
+ * one in place of it.
+ */
+ if (Prefix) {
+ delete_plist(&Plist, FALSE, PLIST_CWD, NULL);
+ add_plist_top(&Plist, PLIST_CWD, Prefix);
+ }
+
+ setenv(PKG_PREFIX_VNAME, (p = find_plist(&Plist, PLIST_CWD)) ? p->name : ".", 1);
+ /* Protect against old packages with bogus @name fields */
+ PkgName = (p = find_plist(&Plist, PLIST_NAME)) ? p->name : "anonymous";
+
+ /* See if we're already registered */
+ sprintf(LogDir, "%s/%s", (tmp = getenv(PKG_DBDIR)) ? tmp : DEF_LOG_DIR, PkgName);
+ if (isdir(LogDir) && !Force) {
+ warnx("package `%s' already recorded as installed", PkgName);
+ code = 1;
+ goto success; /* close enough for government work */
+ }
+
+ /* Now check the packing list for dependencies */
+ for (p = Plist.head; p ; p = p->next) {
+ if (p->type != PLIST_PKGDEP)
+ continue;
+ if (Verbose)
+ printf("Package `%s' depends on `%s'.\n", PkgName, p->name);
+ if (vsystem("pkg_info -e %s", p->name)) {
+ char path[FILENAME_MAX], *cp = NULL;
+
+ if (!Fake) {
+ if (!isURL(pkg) && !getenv("PKG_ADD_BASE")) {
+ snprintf(path, FILENAME_MAX, "%s/%s.tgz", Home, 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 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(cp);
+ }
+ }
+ else {
+ if (Verbose)
+ printf("and was not found%s.\n", Force ? " (proceeding anyway)" : "");
+ else
+ printf("Package dependency %s for %s not found%s\n", p->name, pkg,
+ Force ? " (proceeding anyway)" : "!");
+ if (!Force)
+ ++code;
+ }
+ }
+ else if (Verbose)
+ printf(" - already installed.\n");
+ }
+
+ if (code != 0)
+ goto bomb;
+
+ /* Look for the requirements file */
+ if (fexists(REQUIRE_FNAME)) {
+ vsystem("chmod +x %s", REQUIRE_FNAME); /* be sure */
+ if (Verbose)
+ printf("Running requirements file first for %s..\n", PkgName);
+ if (!Fake && vsystem("./%s %s INSTALL", REQUIRE_FNAME, PkgName)) {
+ warnx("package %s fails requirements %s", pkg_fullname,
+ Force ? "installing anyway" : "- not installed");
+ if (!Force) {
+ code = 1;
+ goto success; /* close enough for government work */
+ }
+ }
+ }
+
+ /* If we're really installing, and have an installation file, run it */
+ if (!NoInstall && fexists(INSTALL_FNAME)) {
+ vsystem("chmod +x %s", INSTALL_FNAME); /* make sure */
+ if (Verbose)
+ printf("Running install with PRE-INSTALL for %s..\n", PkgName);
+ if (!Fake && vsystem("./%s %s PRE-INSTALL", INSTALL_FNAME, PkgName)) {
+ warnx("install script returned error status");
+ unlink(INSTALL_FNAME);
+ 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", PkgName);
+ p = find_plist(&Plist, PLIST_CWD);
+ if (Verbose)
+ printf("mtree -U -f %s -d -e -p %s\n", MTREE_FNAME, p ? p->name : "/");
+ if (!Fake) {
+ if (vsystem("/usr/sbin/mtree -U -f %s -d -e -p %s", MTREE_FNAME, p ? p->name : "/"))
+ warnx("mtree returned a non-zero status - continuing");
+ }
+ unlink(MTREE_FNAME);
+ }
+
+ /* Run the installation script one last time? */
+ if (!NoInstall && fexists(INSTALL_FNAME)) {
+ if (Verbose)
+ printf("Running install with POST-INSTALL for %s..\n", PkgName);
+ if (!Fake && vsystem("./%s %s POST-INSTALL", INSTALL_FNAME, PkgName)) {
+ warnx("install script returned error status");
+ unlink(INSTALL_FNAME);
+ code = 1;
+ goto fail;
+ }
+ unlink(INSTALL_FNAME);
+ }
+
+ /* Time to record the deed? */
+ if (!NoRecord && !Fake) {
+ char contents[FILENAME_MAX];
+ FILE *cfile;
+
+ umask(022);
+ if (getuid() != 0)
+ warnx("not running as root - trying to record install anyway");
+ if (!PkgName) {
+ warnx("no package name! can't record package, sorry");
+ code = 1;
+ goto success; /* well, partial anyway */
+ }
+ sprintf(LogDir, "%s/%s", (tmp = getenv(PKG_DBDIR)) ? tmp : DEF_LOG_DIR, PkgName);
+ if (Verbose)
+ printf("Attempting to record package into %s..\n", LogDir);
+ if (make_hierarchy(LogDir)) {
+ warnx("can't record package into '%s', you're on your own!",
+ LogDir);
+ bzero(LogDir, FILENAME_MAX);
+ code = 1;
+ goto success; /* close enough for government work */
+ }
+ /* Make sure pkg_info can read the entry */
+ vsystem("chmod a+rx %s", LogDir);
+ if (fexists(DEINSTALL_FNAME))
+ move_file(".", DEINSTALL_FNAME, LogDir);
+ if (fexists(REQUIRE_FNAME))
+ move_file(".", REQUIRE_FNAME, LogDir);
+ sprintf(contents, "%s/%s", LogDir, CONTENTS_FNAME);
+ cfile = fopen(contents, "w");
+ if (!cfile) {
+ warnx("can't open new contents file '%s'! can't register pkg",
+ contents);
+ goto success; /* can't log, but still keep pkg */
+ }
+ write_plist(&Plist, cfile);
+ fclose(cfile);
+ move_file(".", DESC_FNAME, LogDir);
+ move_file(".", COMMENT_FNAME, LogDir);
+ if (fexists(DISPLAY_FNAME))
+ move_file(".", DISPLAY_FNAME, LogDir);
+ for (p = Plist.head; p ; p = p->next) {
+ if (p->type != PLIST_PKGDEP)
+ continue;
+ if (Verbose)
+ printf("Attempting to record dependency on package `%s'\n", p->name);
+ sprintf(contents, "%s/%s/%s", (tmp = getenv(PKG_DBDIR)) ? tmp : DEF_LOG_DIR,
+ basename_of(p->name), REQUIRED_BY_FNAME);
+ cfile = fopen(contents, "a");
+ if (!cfile)
+ warnx("can't open dependency file '%s'!\n"
+ "dependency registration is incomplete", contents);
+ else {
+ fprintf(cfile, "%s\n", PkgName);
+ if (fclose(cfile) == EOF)
+ warnx("cannot properly close file %s", contents);
+ }
+ }
+ if (Verbose)
+ printf("Package %s registered in %s\n", PkgName, LogDir);
+ }
+
+ if ((p = find_plist(&Plist, PLIST_DISPLAY)) != NULL) {
+ FILE *fp;
+ char buf[BUFSIZ];
+
+ snprintf(buf, sizeof buf, "%s/%s", LogDir, p->name);
+ fp = fopen(buf, "r");
+ if (fp) {
+ putc('\n', stdout);
+ while (fgets(buf, sizeof(buf), fp))
+ fputs(buf, stdout);
+ putc('\n', stdout);
+ (void) fclose(fp);
+ } else
+ warnx("cannot open %s as display file", buf);
+ }
+
+ goto success;
+
+ bomb:
+ code = 1;
+ goto success;
+
+ fail:
+ /* Nuke the whole (installed) show, XXX but don't clean directories */
+ if (!Fake)
+ delete_package(FALSE, FALSE, &Plist);
+
+ success:
+ /* delete the packing list contents */
+ free_plist(&Plist);
+ leave_playpen(Home);
+ 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 signo)
+{
+ if (signo)
+ printf("Signal %d received, cleaning up..\n", signo);
+ if (!Fake && LogDir[0])
+ vsystem("%s -rf %s", REMOVE_CMD, LogDir);
+ leave_playpen(Home);
+ 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..32431ef
--- /dev/null
+++ b/usr.sbin/pkg_install/add/pkg_add.1
@@ -0,0 +1,365 @@
+.\"
+.\" 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
+.\"
+.Dd November 25, 1994
+.Dt pkg_add 1
+.Os FreeBSD
+.Sh NAME
+.Nm pkg_add
+.Nd a utility for installing software package distributions
+.Sh SYNOPSIS
+.Nm
+.Op Fl vInfRMS
+.Op Fl t Ar template
+.Op Fl p Ar prefix
+.Ar pkg-name [pkg-name ...]
+.Sh DESCRIPTION
+The
+.Nm
+command is used to extract packages that have been previously created
+with the
+.Xr pkg_create 1
+command.
+
+.Sh WARNING
+.Bf -emphasis
+Since the
+.Nm
+command may execute scripts or programs contained within a package file,
+your system may be susceptible to ``trojan horses'' or other subtle
+attacks from miscreants who create dangerous package files.
+.Pp
+You are advised to verify the competence and identity of those who
+provide installable package files. For extra protection, use the
+.Fl M
+flag to extract the package file, and inspect its contents and scripts
+to insure it poses no danger to your system's integrity. Pay particular
+attention to any +INSTALL, +DEINSTALL, +REQUIRE or +MTREE_DIRS files,
+and inspect the +CONTENTS file for
+.Cm @cwd ,
+.Cm @mode
+(check for setuid),
+.Cm @dirrm ,
+.Cm @exec ,
+and
+.Cm @unexec
+directives, and/or use the
+.Xr pkg_info 1
+command to examine the package file.
+.Ef
+
+.Sh OPTIONS
+The following command line arguments are supported:
+.Bl -tag -width indent
+.It Ar pkg-name [... pkg-name]
+The named packages are installed. A package name of - will cause
+.Nm
+to read from stdin. If the packages are not found in the current
+working directory,
+.Nm
+will search them in each directory named by
+.Ev PKG_PATH .
+.It Fl v
+Turn on verbose output.
+.It Fl I
+If an installation script exists for a given package, do not execute it.
+.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 f
+Force installation to proceed even if prerequisite packages are not
+installed or the requirements script fails. Although
+.Nm
+will still try to find and auto-install missing prerequisite packages,
+a failure to find one will not be fatal.
+.It Fl p Ar prefix
+Set
+.Ar prefix
+as the directory in which to extract files from a package.
+If a package has set its default directory, it will be overridden
+by this flag. Note that only the first
+.Cm @cwd
+directive will be replaced, since
+.Nm
+has no way of knowing which directory settings are relative and
+which are absolute. It is rare in any case to see more than one
+directory transition made, but when such does happen and you wish
+to have control over *all* directory transitions, then you
+may then wish to look into the use of
+.Cm MASTER
+and
+.Cm SLAVE
+modes (see the
+.Fl M
+and
+.Fl S
+options).
+.It Fl t Ar template
+Use
+.Ar template
+as the input to
+.Xr mktemp 3
+when creating a ``staging area.''
+By default, this is the string
+.Pa /var/tmp/instmp.XXXXXX ,
+but it may be necessary to override it in the situation where
+space in your
+.Pa /var/tmp
+directory is limited. Be sure to leave some number of `X' characters
+for
+.Xr mktemp 3
+to fill in with a unique ID.
+.Pp
+You can get a performance boost by setting the staging area
+.Ar template
+to reside on the same disk partition as target directories for package
+file installation; often this is
+.Pa /usr .
+.It Fl M
+Run in
+.Cm MASTER
+mode. This is a very specialized mode for running
+.Nm
+and is meant to be run in conjunction with
+.Cm SLAVE
+mode. When run in this mode,
+.Nm
+does no work beyond extracting the package into a temporary staging
+area (see the
+.Fl t
+option), reading in the packing list, and then dumping it (prefaced by
+the current staging area) to stdout where it may be filtered by a
+program such as
+.Xr sed 1 .
+When used in conjunction with
+.Cm SLAVE
+mode, it allows you to make radical changes to the package structure
+before acting on its contents.
+.It Fl S
+Run in
+.Cm SLAVE
+mode. This is a very specialized mode for running
+.Nm
+and is meant to be run in conjunction with
+.Cm MASTER
+mode. When run in this mode,
+.Nm
+expects the release contents to be already extracted and waiting
+in the staging area, the location of which is read as a string
+from stdin. The complete packing list is also read from stdin,
+and the contents then acted on as normal.
+.El
+.Pp
+One or more
+.Ar pkg-name
+arguments may be specified, each being either a file containing the
+package (these usually ending with the ``.tgz'' suffix) or a
+URL pointing at a file available on an ftp site. Thus you may
+extract files directly from their anonymous ftp locations (e.g.
+.Nm
+ftp://ftp.freebsd.org/pub/FreeBSD/packages/shells/bash-1.14.4.tgz).
+Note: If you wish to use
+.Bf -emphasis
+passive mode
+.Ef
+ftp in such transfers, set
+the variable
+.Bf -emphasis
+FTP_PASSIVE_MODE
+.Ef
+to some value in your environment. Otherwise, the more standard
+ACTIVE mode may be used. If
+.Nm
+consistently fails to fetch a package from a site known to work,
+it may be because you have a firewall that demands the usage of
+.Bf -emphasis
+passive mode
+.Ef
+ftp.
+.Sh TECHNICAL DETAILS
+.Nm Pkg_add
+is fairly simple. It extracts each package's "packing list"
+into a special staging directory in /tmp (or $PKG_TMPDIR if set), parses it,
+and then runs through the following sequence to fully extract the contents:
+.Bl -enum -indent indent
+.It
+Check if the package is already recorded as installed. If so,
+terminate installation.
+.It
+Scan all the package dependencies (from
+.Cm @pkgdep
+directives, see
+.Xr pkg_create 1 )
+and make sure each one is met. If not, try and find the missing
+dependencies' packages and auto-install them; if they can't be found
+the installation is terminated.
+.It
+Search for any
+.Cm @option
+directives which control how the package is added to the system.
+At the time of this writing, the only currently implemented option is
+.Cm @option extract-in-place
+which will cause the package to be extracted directly into its
+prefix directory without moving through a staging area in
+.Pa /tmp .
+.It
+If
+.Cm @option extract-in-place
+is enabled, the package is now extracted directly into its
+final location, otherwise it is extracted into the staging area.
+.It
+If the package contains a
+.Ar require
+file (see
+.Xr pkg_create 1 ),
+then execute it with the following arguments:
+.Bd -filled -offset indent -compact
+.Ar pkg-name
+.Ar INSTALL
+.Ed
+where
+.Ar pkg-name
+is the name of the package in question and the
+.Ar INSTALL
+keyword denotes this as an installation requirements check (useful if
+you want to have one script serving multiple functions).
+.It
+If an
+.Ar install
+script exists for the package, it is then executed with the following arguments:
+.Bd -filled -offset indent -compact
+.Ar pkg-name
+.Ar PRE-INSTALL
+.Ed
+where
+.Ar pkg-name
+is the name of the package in question and
+.Ar PRE-INSTALL
+is a keyword denoting this as the preinstallation phase.
+.It
+If
+.Cm @option extract-in-place
+is not used, then the packing list (this is the
+.Pa +CONTENTS
+file) is now used as a guide for moving (or copying, as necessary) files from
+the staging area into their final locations.
+.It
+If the package contains an
+.Ar mtreefile
+file (see
+.Xr pkg_create 1 ),
+then mtree is invoked as:
+.Bd -filled -offset indent -compact
+.Cm mtree
+.Fl u
+.Fl f
+.Ar mtreefile
+.Fl d
+.Fl e
+.Fl p
+.Pa prefix
+.Ed
+where
+.Pa prefix
+is either the prefix specified with the
+.Fl p
+flag or, if no
+.Fl p
+flag was specified, the name of the first directory named by a
+.Cm @cwd
+directive within this package.
+.It
+If an
+.Ar install
+script exists for the package, it is then executed as
+.Bd -filled -offset indent -compact
+.Cm script
+.Ar pkg-name
+.Ar POST-INSTALL
+.Ed
+This all allows you to write a single
+.Ar install
+script that does both ``before and after'' actions.
+.It
+After installation is complete, a copy of the packing list,
+.Ar deinstall
+script, description, and display files are copied into
+.Pa /var/db/pkg/<pkg-name>
+for subsequent possible use by
+.Xr pkg_delete 1 .
+Any package dependencies are recorded in the other packages'
+.Pa /var/db/pkg/<other-pkg>/+REQUIRED_BY
+file
+(if the environment variable PKG_DBDIR is set, this overrides the
+.Pa /var/db/pkg/
+path shown above).
+.It
+Finally, the staging area is deleted and the program terminates.
+.El
+.Pp
+All the scripts are called with the environment variable
+.Ev PKG_PREFIX
+set to the installation prefix (see the
+.Fl p
+option above). This allows a package author to write a script
+that reliably performs some action on the directory where the package
+is installed, even if the user might change it with the
+.Fl p
+flag to
+.Cm pkg_add .
+.Sh ENVIRONMENT
+The value of the
+.Ev PKG_PATH
+is used if a given package can't be found. The environment variable
+should be a series of entries seperated by colons. Each entry
+consists of a directory name. The current directory may be indicated
+implicitly by an empty directory name, or explicitly by a single
+period.
+.Sh SEE ALSO
+.Xr pkg_create 1 ,
+.Xr pkg_delete 1 ,
+.Xr pkg_info 1 ,
+.Xr mktemp 3 ,
+.Xr sysconf 3 ,
+.Xr mtree 8
+.Sh AUTHORS
+.Bl -tag -width indent -compact
+.It "Jordan Hubbard"
+Initial work and ongoing development.
+.It "John Kohl"
+NetBSD refinements.
+.El
+.Sh BUGS
+Hard links between files in a distribution are only preserved if either
+(1) the staging area is on the same file system as the target directory of
+all the links to the file, or (2) all the links to the file are bracketed by
+.Cm @cwd
+directives in the contents file,
+.Em and
+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..01048ac
--- /dev/null
+++ b/usr.sbin/pkg_install/create/Makefile
@@ -0,0 +1,18 @@
+PROG= pkg_create
+
+CFLAGS+= ${DEBUG} -I${.CURDIR}/../lib
+
+.if exists(${.OBJDIR}/../lib)
+LDADD+= -L${.OBJDIR}/../lib -linstall
+DPADD+= ${.OBJDIR}/../lib/libinstall.a
+.else
+LDADD+= -L${.CURDIR}/../lib -linstall
+DPADD+= ${.CURDIR}/../lib/libinstall.a
+.endif
+
+LDADD+= -lftpio -lmd
+DPADD+= ${LIBFTPIO} ${LIBMD}
+
+SRCS= main.c perform.c pl.c
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/pkg_install/create/create.h b/usr.sbin/pkg_install/create/create.h
new file mode 100644
index 0000000..eab822a
--- /dev/null
+++ b/usr.sbin/pkg_install/create/create.h
@@ -0,0 +1,46 @@
+/* $Id: create.h,v 1.12 1997/06/06 12:19:11 jkh Exp $ */
+
+/*
+ * FreeBSD install - a package for the installation and maintainance
+ * of non-core utilities.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * Jordan K. Hubbard
+ * 18 July 1993
+ *
+ * Include and define various things wanted by the 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 *DeInstall;
+extern char *Contents;
+extern char *Require;
+extern char *SrcDir;
+extern char *ExcludeFrom;
+extern char *Mtree;
+extern char *Pkgdeps;
+extern char PlayPen[];
+extern int Dereference;
+extern int PlistOnly;
+
+void check_list(char *, Package *);
+int pkg_perform(char **);
+void copy_plist(char *, Package *);
+
+#endif /* _INST_CREATE_H_INCLUDE */
diff --git a/usr.sbin/pkg_install/create/main.c b/usr.sbin/pkg_install/create/main.c
new file mode 100644
index 0000000..38fff8a
--- /dev/null
+++ b/usr.sbin/pkg_install/create/main.c
@@ -0,0 +1,160 @@
+#ifndef lint
+static const char rcsid[] =
+ "$Id: main.c,v 1.16 1997/06/06 12:19:11 jkh Exp $";
+#endif
+
+/*
+ * FreeBSD install - a package for the installation and maintainance
+ * of non-core utilities.
+ *
+ * Jordan K. Hubbard
+ * 18 July 1993
+ *
+ * This is the create module.
+ *
+ */
+
+#include <err.h>
+#include "lib.h"
+#include "create.h"
+
+static char Options[] = "YNOhvf:p:P:c:d:i:k:r:t:X:D:m:s:";
+
+char *Prefix = NULL;
+char *Comment = NULL;
+char *Desc = NULL;
+char *SrcDir = NULL;
+char *Display = NULL;
+char *Install = NULL;
+char *DeInstall = NULL;
+char *Contents = NULL;
+char *Require = NULL;
+char *ExcludeFrom = NULL;
+char *Mtree = NULL;
+char *Pkgdeps = NULL;
+char PlayPen[FILENAME_MAX];
+int Dereference = 0;
+int PlistOnly = 0;
+
+static void usage __P((void));
+
+int
+main(int argc, char **argv)
+{
+ int ch;
+ char **pkgs, **start;
+
+ pkgs = start = argv;
+ while ((ch = getopt(argc, argv, Options)) != -1)
+ switch(ch) {
+ case 'v':
+ Verbose = TRUE;
+ break;
+
+ case 'N':
+ AutoAnswer = NO;
+ break;
+
+ case 'Y':
+ AutoAnswer = YES;
+ break;
+
+ case 'O':
+ PlistOnly = YES;
+ break;
+
+ case 'p':
+ Prefix = optarg;
+ break;
+
+ case 's':
+ SrcDir = optarg;
+ break;
+
+ case 'f':
+ Contents = optarg;
+ break;
+
+ case 'c':
+ Comment = optarg;
+ break;
+
+ case 'd':
+ Desc = optarg;
+ break;
+
+ case 'i':
+ Install = optarg;
+ break;
+
+ case 'k':
+ DeInstall = optarg;
+ break;
+
+ case 'r':
+ Require = optarg;
+ break;
+
+ case 't':
+ strcpy(PlayPen, optarg);
+ break;
+
+ case 'X':
+ ExcludeFrom = optarg;
+ break;
+
+ case 'h':
+ Dereference = 1;
+ break;
+
+ case 'D':
+ Display = optarg;
+ break;
+
+ case 'm':
+ Mtree = optarg;
+ break;
+
+ case 'P':
+ Pkgdeps = optarg;
+ break;
+
+ case '?':
+ default:
+ usage();
+ break;
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ /* Get all the remaining package names, if any */
+ while (*argv)
+ *pkgs++ = *argv++;
+
+ /* If no packages, yelp */
+ if (pkgs == start)
+ warnx("missing package name"), usage();
+ *pkgs = NULL;
+ if (start[1])
+ warnx("only one package name allowed ('%s' extraneous)", start[1]),
+ usage();
+ if (!pkg_perform(start)) {
+ if (Verbose)
+ warnx("package creation failed");
+ return 1;
+ }
+ else
+ return 0;
+}
+
+static void
+usage()
+{
+ fprintf(stderr, "%s\n%s\n%s\n%s\n",
+"usage: pkg_create [-YNOhv] [-P pkgs] [-p prefix] [-f contents] [-i iscript]",
+" [-k dscript] [-r rscript] [-t template] [-X excludefile]",
+" [-D displayfile] [-m mtreefile] -c comment -d description",
+" -f packlist pkg-name");
+ exit(1);
+}
diff --git a/usr.sbin/pkg_install/create/perform.c b/usr.sbin/pkg_install/create/perform.c
new file mode 100644
index 0000000..830a2b2
--- /dev/null
+++ b/usr.sbin/pkg_install/create/perform.c
@@ -0,0 +1,297 @@
+#ifndef lint
+static const char rcsid[] =
+ "$Id: perform.c,v 1.37 1997/10/08 07:46:27 charnier Exp $";
+#endif
+
+/*
+ * FreeBSD install - a package for the installation and maintainance
+ * of non-core utilities.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * Jordan K. Hubbard
+ * 18 July 1993
+ *
+ * This is the main body of the create module.
+ *
+ */
+
+#include "lib.h"
+#include "create.h"
+
+#include <err.h>
+#include <signal.h>
+#include <sys/syslimits.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+static void sanity_check(void);
+static void make_dist(char *, char *, char *, Package *);
+
+static char *home;
+
+int
+pkg_perform(char **pkgs)
+{
+ char *pkg = *pkgs; /* Only one arg to create */
+ char *cp;
+ FILE *pkg_in, *fp;
+ Package plist;
+ char *suffix; /* What we tack on to the end of the finished package */
+
+ /* Preliminary setup */
+ sanity_check();
+ if (Verbose && !PlistOnly)
+ printf("Creating package %s\n", pkg);
+ get_dash_string(&Comment);
+ get_dash_string(&Desc);
+ if (!strcmp(Contents, "-"))
+ pkg_in = stdin;
+ else {
+ pkg_in = fopen(Contents, "r");
+ if (!pkg_in)
+ cleanup(0), errx(2, "unable to open contents file '%s' for input",
+ Contents);
+ }
+ plist.head = plist.tail = NULL;
+
+ /* Break the package name into base and desired suffix (if any) */
+ if ((cp = rindex(pkg, '.')) != NULL) {
+ suffix = cp + 1;
+ *cp = '\0';
+ }
+ else
+ suffix = "tgz";
+
+ /* Stick the dependencies, if any, at the top */
+ if (Pkgdeps) {
+ if (Verbose && !PlistOnly)
+ printf("Registering depends:");
+ while (Pkgdeps) {
+ cp = strsep(&Pkgdeps, " \t\n");
+ if (*cp) {
+ add_plist(&plist, PLIST_PKGDEP, cp);
+ if (Verbose && !PlistOnly)
+ printf(" %s", cp);
+ }
+ }
+ if (Verbose && !PlistOnly)
+ printf(".\n");
+ }
+
+ /* If a SrcDir override is set, add it now */
+ if (SrcDir) {
+ if (Verbose && !PlistOnly)
+ printf("Using SrcDir value of %s\n", SrcDir);
+ add_plist(&plist, PLIST_SRC, SrcDir);
+ }
+
+ /* Slurp in the packing list */
+ read_plist(&plist, pkg_in);
+
+ /* Prefix should override the packing list */
+ if (Prefix) {
+ delete_plist(&plist, FALSE, PLIST_CWD, NULL);
+ add_plist_top(&plist, PLIST_CWD, Prefix);
+ }
+ /*
+ * Run down the list and see if we've named it, if not stick in a name
+ * at the top.
+ */
+ if (find_plist(&plist, PLIST_NAME) == NULL)
+ add_plist_top(&plist, PLIST_NAME, basename_of(pkg));
+
+ /*
+ * We're just here for to dump out a revised plist for the FreeBSD ports
+ * hack. It's not a real create in progress.
+ */
+ if (PlistOnly) {
+ check_list(home, &plist);
+ write_plist(&plist, stdout);
+ exit(0);
+ }
+
+ /* Make a directory to stomp around in */
+ home = make_playpen(PlayPen, 0);
+ signal(SIGINT, cleanup);
+ signal(SIGHUP, cleanup);
+
+ /* Make first "real contents" pass over it */
+ check_list(home, &plist);
+ (void) umask(022); /* make sure gen'ed directories, files don't have
+ group or other write bits. */
+ /* copy_plist(home, &plist); */
+ /* mark_plist(&plist); */
+
+ /* Now put the release specific items in */
+ add_plist(&plist, PLIST_CWD, ".");
+ write_file(COMMENT_FNAME, Comment);
+ add_plist(&plist, PLIST_IGNORE, NULL);
+ add_plist(&plist, PLIST_FILE, COMMENT_FNAME);
+ write_file(DESC_FNAME, Desc);
+ add_plist(&plist, PLIST_IGNORE, NULL);
+ add_plist(&plist, PLIST_FILE, DESC_FNAME);
+
+ if (Install) {
+ copy_file(home, Install, INSTALL_FNAME);
+ add_plist(&plist, PLIST_IGNORE, NULL);
+ add_plist(&plist, PLIST_FILE, INSTALL_FNAME);
+ }
+ if (DeInstall) {
+ copy_file(home, DeInstall, DEINSTALL_FNAME);
+ add_plist(&plist, PLIST_IGNORE, NULL);
+ add_plist(&plist, PLIST_FILE, DEINSTALL_FNAME);
+ }
+ if (Require) {
+ copy_file(home, Require, REQUIRE_FNAME);
+ add_plist(&plist, PLIST_IGNORE, NULL);
+ add_plist(&plist, PLIST_FILE, REQUIRE_FNAME);
+ }
+ if (Display) {
+ copy_file(home, Display, DISPLAY_FNAME);
+ add_plist(&plist, PLIST_IGNORE, NULL);
+ add_plist(&plist, PLIST_FILE, DISPLAY_FNAME);
+ add_plist(&plist, PLIST_DISPLAY, DISPLAY_FNAME);
+ }
+ if (Mtree) {
+ copy_file(home, Mtree, MTREE_FNAME);
+ add_plist(&plist, PLIST_IGNORE, NULL);
+ add_plist(&plist, PLIST_FILE, MTREE_FNAME);
+ add_plist(&plist, PLIST_MTREE, MTREE_FNAME);
+ }
+
+ /* Finally, write out the packing list */
+ fp = fopen(CONTENTS_FNAME, "w");
+ if (!fp)
+ cleanup(0), errx(2, "can't open file %s for writing", CONTENTS_FNAME);
+ write_plist(&plist, fp);
+ if (fclose(fp))
+ cleanup(0), errx(2, "error while closing %s", CONTENTS_FNAME);
+
+ /* And stick it into a tar ball */
+ make_dist(home, pkg, suffix, &plist);
+
+ /* Cleanup */
+ free(Comment);
+ free(Desc);
+ free_plist(&plist);
+ leave_playpen(home);
+ return TRUE; /* Success */
+}
+
+static void
+make_dist(char *home, char *pkg, char *suffix, Package *plist)
+{
+ char tball[FILENAME_MAX];
+ PackingList p;
+ int ret;
+ char *args[50]; /* Much more than enough. */
+ int nargs = 0;
+ int pipefds[2];
+ FILE *totar;
+ pid_t pid;
+
+ args[nargs++] = "tar"; /* argv[0] */
+
+ if (*pkg == '/')
+ snprintf(tball, FILENAME_MAX, "%s.%s", pkg, suffix);
+ else
+ snprintf(tball, FILENAME_MAX, "%s/%s.%s", home, pkg, suffix);
+
+ args[nargs++] = "-c";
+ args[nargs++] = "-f";
+ args[nargs++] = tball;
+ if (index(suffix, 'z')) /* Compress/gzip? */
+ args[nargs++] = "-z";
+ if (Dereference)
+ args[nargs++] = "-h";
+ if (ExcludeFrom) {
+ args[nargs++] = "-X";
+ args[nargs++] = ExcludeFrom;
+ }
+ args[nargs++] = "-T"; /* Take filenames from file instead of args. */
+ args[nargs++] = "-"; /* Use stdin for the file. */
+ args[nargs] = NULL;
+
+ if (Verbose)
+ printf("Creating gzip'd tar ball in '%s'\n", tball);
+
+ /* Set up a pipe for passing the filenames, and fork off a tar process. */
+ if (pipe(pipefds) == -1)
+ cleanup(0), errx(2, "cannot create pipe");
+ if ((pid = fork()) == -1)
+ cleanup(0), errx(2, "cannot fork process for tar");
+ if (pid == 0) { /* The child */
+ dup2(pipefds[0], 0);
+ close(pipefds[0]);
+ close(pipefds[1]);
+ execv("/usr/bin/tar", args);
+ cleanup(0);
+ errx(2, "failed to execute tar command");
+ }
+
+ /* Meanwhile, back in the parent process ... */
+ close(pipefds[0]);
+ if ((totar = fdopen(pipefds[1], "w")) == NULL)
+ cleanup(0), errx(2, "fdopen failed");
+
+ fprintf(totar, "%s\n", CONTENTS_FNAME);
+ fprintf(totar, "%s\n", COMMENT_FNAME);
+ fprintf(totar, "%s\n", DESC_FNAME);
+
+ if (Install)
+ fprintf(totar, "%s\n", INSTALL_FNAME);
+ if (DeInstall)
+ fprintf(totar, "%s\n", DEINSTALL_FNAME);
+ if (Require)
+ fprintf(totar, "%s\n", REQUIRE_FNAME);
+ if (Display)
+ fprintf(totar, "%s\n", DISPLAY_FNAME);
+ if (Mtree)
+ fprintf(totar, "%s\n", MTREE_FNAME);
+
+ for (p = plist->head; p; p = p->next) {
+ if (p->type == PLIST_FILE)
+ fprintf(totar, "%s\n", p->name);
+ else if (p->type == PLIST_CWD || p->type == PLIST_SRC)
+ fprintf(totar, "-C\n%s\n", p->name);
+ else if (p->type == PLIST_IGNORE)
+ p = p->next;
+ }
+
+ fclose(totar);
+ wait(&ret);
+ /* assume either signal or bad exit is enough for us */
+ if (ret)
+ cleanup(0), errx(2, "tar command failed with code %d", ret);
+}
+
+static void
+sanity_check()
+{
+ if (!Comment)
+ cleanup(0), errx(2,
+ "required package comment string is missing (-c comment)");
+ if (!Desc)
+ cleanup(0), errx(2,
+ "required package description string is missing (-d desc)");
+ if (!Contents)
+ cleanup(0), errx(2,
+ "required package contents list is missing (-f [-]file)");
+}
+
+
+/* Clean up those things that would otherwise hang around */
+void
+cleanup(int sig)
+{
+ leave_playpen(home);
+ exit(1);
+}
diff --git a/usr.sbin/pkg_install/create/pkg_create.1 b/usr.sbin/pkg_install/create/pkg_create.1
new file mode 100644
index 0000000..8d6a068
--- /dev/null
+++ b/usr.sbin/pkg_install/create/pkg_create.1
@@ -0,0 +1,382 @@
+.\"
+.\" FreeBSD install - a package for the installation and maintainance
+.\" of non-core utilities.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" Jordan K. Hubbard
+.\"
+.\"
+.\" @(#)pkg_create.1
+.\" $Id: pkg_create.1,v 1.22 1997/09/27 13:41:33 hoek Exp $
+.\"
+.\" hacked up by John Kohl for NetBSD--fixed a few bugs, extended keywords,
+.\" added dependency tracking, etc.
+.\"
+.\" [jkh] Took John's changes back and made some additional extensions for
+.\" better integration with FreeBSD's new ports collection.
+.\"
+.Dd April 21, 1995
+.Dt pkg_create 1
+.Os FreeBSD
+.Sh NAME
+.Nm pkg_create
+.Nd a utility for creating software package distributions
+.Sh SYNOPSIS
+.Nm
+.Op Fl YNOhv
+.Op Fl P Ar pkgs
+.Op Fl p Ar prefix
+.Op Fl f Ar contents
+.Op Fl i Ar iscript
+.Op Fl k Ar dscript
+.Op Fl r Ar rscript
+.Op Fl t Ar template
+.Op Fl X Ar excludefile
+.Op Fl D Ar displayfile
+.Op Fl m Ar mtreefile
+.Fl c Ar comment
+.Fl d Ar description
+.Fl f Ar packlist
+.Ar pkg-name
+.Sh DESCRIPTION
+The
+.Nm
+command is used to create packages that will subsequently be fed to
+one of the package extraction/info utilities. The input description
+and command line arguments for the creation of a package are not
+really meant to be human-generated, though it is easy enough to
+do so. It is more expected that you will use a front-end tool for
+the job rather than muddling through it yourself. Nonetheless, a short
+description of the input syntax is included in this document.
+.Sh OPTIONS
+The following command line options are supported:
+.Bl -tag -width indent
+.It Fl f Ar packinglist
+Fetch ``packing list'' for package from the file
+.Ar packinglist
+or
+.Cm stdin
+if
+.Ar packinglist
+is a
+.Cm -
+(dash).
+.It Fl c Ar [-]desc
+Fetch package ``one line description'' from file
+.Ar desc
+or, if preceded by
+.Cm - ,
+the argument itself. This string should also
+give some idea of which version of the product (if any) the package
+represents.
+.It Fl d Ar [-]desc
+Fetch long description for package from file
+.Ar desc
+or, if preceded by
+.Cm - ,
+the argument itself.
+.It Fl Y
+Assume a default answer of `Yes' for any questions asked.
+.It Fl N
+Assume a default answer of `No' for any questions asked.
+.It Fl O
+Go into a `packing list Only' mode. This is a custom hack for the
+.Em "FreeBSD Ports Collection"
+and is used to do `fake pkg_add' operations when a port is installed.
+In such cases, it is necessary to know what the final, adjusted packing
+list will look like.
+.It Fl v
+Turn on verbose output.
+.It Fl h
+Force tar to follow symbolic links, so that the files they point to
+are dumped, rather than the links themselves.
+.It Fl i Ar iscript
+Set
+.Ar iscript
+to be the 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 Fl P Ar pkgs
+Set the initial package dependency list to
+.Ar pkgs .
+This is assumed to be a whitespace separated list of package names
+and is meant as a convenient shorthand for specifying multiple
+.Cm @pkgdep
+directives in the packing list (see PACKING LIST DETAILS section below).
+.It Fl p Ar prefix
+Set
+.Ar prefix
+as the initial directory ``base'' to start from in selecting files for
+the package.
+.It Fl k Ar dscript
+Set
+.Ar dscript
+to be the de-install procedure for the package. This can be any
+executable program (or shell script). It will be invoked automatically
+when the package is later (if ever) de-installed.
+.It Fl r Ar rscript
+Set
+.Ar rscript
+to be the ``requirements'' procedure for the package. This can be any
+executable program (or shell script). It will be invoked automatically
+at installation/deinstallation time to determine whether or not
+installation/deinstallation should proceed.
+.It Fl t Ar template
+Use
+.Ar template
+as the input to
+.Xr mktemp 3 .
+By default, this is the string
+.Pa /tmp/instmp.XXXXXX ,
+but it may be necessary to override it in the situation where
+space in your
+.Pa /tmp
+directory is limited. Be sure to leave some number of `X' characters
+for
+.Xr mktemp 3
+ to fill in with a unique ID.
+.It Fl X Ar excludefile
+Pass
+.Ar excludefile
+as a
+.Fl exclude-from
+argument to
+.Cm tar
+when creating final package. See
+.Cm tar
+man page (or run
+.Cm tar
+with
+.Fl -help
+flag) for further information on using this flag.
+.It Fl D Ar displayfile
+Display the file (using
+.Xr more 1 )
+after installing the package. Useful for things like
+legal notices on almost-free software, etc.
+.It Fl m Ar mtreefile
+Run
+.Xr mtree 8
+with input from mtreefile before the package is installed.
+Mtree is invoked as
+.Cm mtree
+.Fl u
+.Fl f
+.Ar mtreefile
+.Fl d
+.Fl e
+.Fl p
+.Pa prefix ,
+where
+.Pa prefix
+is the name of the first directory named by a
+.Cm @cwd
+directive.
+.El
+.Pp
+.Sh PACKING LIST DETAILS
+The ``packing list'' format (see
+.Fl f )
+is fairly simple, being
+nothing more than a single column of filenames to include in the
+package. However, since absolute pathnames are generally a bad idea
+for a package that could be installed potentially anywhere, there is
+another method of specifying where things are supposed to go
+and, optionally, what ownership and mode information they should be
+installed with. This is done by imbeding specialized command sequences
+in the packing list. Briefly described, these sequences are:
+.Bl -tag -width indent -compact
+.It Cm @cwd Ar directory
+Set the internal directory pointer to point to
+.Ar directory .
+All subsequent filenames will be assumed relative to this directory.
+Note:
+.Cm @cd
+is also an alias for this command.
+.It Cm @srcdir Ar directory
+Set the internal directory pointer for _creation only_ to
+.Ar directory .
+That is to say that it overrides
+.Cm @cwd
+for package creation but not extraction.
+.It Cm @exec Ar command
+Execute
+.Ar command
+as part of the unpacking process. If
+.Ar command
+contains any of the following sequences somewhere in it, they will
+be expanded inline. For the following examples, assume that
+.Cm @cwd
+is set to
+.Pa /usr/local
+and the last extracted file was
+.Pa bin/emacs .
+.Bl -tag -width indent -compact
+.It Cm "%F"
+Expands to the last filename extracted (as specified), in the example case
+.Pa bin/emacs
+.It Cm "%D"
+Expand to the current directory prefix, as set with
+.Cm @cwd ,
+in the example case
+.Pa /usr/local .
+.It Cm "%B"
+Expand to the ``basename'' of the fully qualified filename, that
+is the current directory prefix, plus the last filespec, minus
+the trailing filename. In the example case, that would be
+.Pa /usr/local/bin .
+.It Cm "%f"
+Expand to the ``filename'' part of the fully qualified name, or
+the converse of
+.Cm %B ,
+being in the example case,
+.Pa emacs .
+.El
+.It Cm @unexec Ar command
+Execute
+.Ar command
+as part of the deinstallation process. Expansion of special
+.Cm %
+sequences is the same as for
+.Cm @exec .
+This command is not executed during the package add, as
+.Cm @exec
+is, but rather when the package is deleted. This is useful
+for deleting links and other ancillary files that were created
+as a result of adding the package, but not directly known to
+the package's table of contents (and hence not automatically
+removable). The advantage of using
+.Cm @unexec
+over a deinstallation script is that you can use the ``special
+sequence expansion'' to get at files regardless of where they've
+been potentially redirected (see
+.Fl p ) .
+.It Cm @mode Ar mode
+Set default permission for all subsequently extracted files to
+.Ar mode .
+Format is the same as that used by the
+.Cm chmod
+command (well, considering that it's later handed off to it, that's
+no surprise). Use without an arg to set back to default (extraction)
+permissions.
+.It Cm @option Ar option
+Set internal package options, the only two currently supported ones
+being
+.Ar extract-in-place ,
+which tells the pkg_add command not to extract the package's tarball
+into a staging area but rather directly into the target
+hierarchy (this is typically meant to be used only by distributions
+or other special package types), and
+.Ar preserve ,
+which tells pkg_add to move any existing files out of the way,
+preserving the previous contents (which are also resurrected on
+pkg_delete, so caveat emptor).
+.It Cm @owner Ar user
+Set default ownership for all subsequently extracted files to
+.Ar user .
+Use without an arg to set back to default (extraction)
+ownership.
+.It Cm @group Ar group
+Set default group ownership for all subsequently extracted files to
+.Ar group .
+Use without an arg to set back to default (extraction)
+group ownership.
+.It Cm @comment Ar string
+Imbed a comment in the packing list. Useful in
+trying to document some particularly hairy sequence that
+may trip someone up later.
+.It Cm @ignore
+Used internally to tell extraction to ignore the next file (don't
+copy it anywhere), as it's used for some special purpose.
+.It Cm @ignore_inst
+Similar to
+.Cm @ignore ,
+but the ignoring of the next file is delayed one evaluation cycle. This
+makes it possible to use this directive in the
+.Ar packinglist
+file, so you can pack a
+specialized datafile in with a distribution for your install script (or
+something) yet have the installer ignore it.
+.It Cm @name Ar name
+Set the name of the package. This is mandatory and is usually
+put at the top. This name is potentially different than the name of
+the file it came in, and is used when keeping track of the package
+for later deinstallation. Note that
+.Nm
+will derive this field from the package name and add it automatically
+if none is given.
+.It Cm @dirrm Ar name
+Declare directory
+.Pa name
+to be deleted at deinstall time. By default, directories created by a
+package installation are not deleted when the package is deinstalled;
+this provides an explicit directory cleanup method. This directive
+should appear at the end of the package list. If more than one
+.Cm @dirrm
+directives are used, the directories are removed in the order specified.
+The
+.Pa name
+directory will not be removed unless it is empty.
+.It Cm @mtree Ar name
+Declare
+.Pa name
+as an
+.Xr mtree 8
+input file to be used at install time (see
+.Fl m
+above). Only the first
+.Cm @mtree
+directive is honored.
+.It Cm @display Ar name
+Declare
+.Pa name
+as the file to be displayed at install time (see
+.Fl D
+above).
+.It Cm @pkgdep Ar pkgname
+Declare a dependency on the
+.Ar pkgname
+package. The
+.Ar pkgname
+package must be installed before this package may be
+installed, and this package must be deinstalled before the
+.Ar pkgname
+package is deinstalled. Multiple
+.Cm @pkgdep
+directives may be used if the package depends on multiple other packages.
+.El
+.Sh SEE ALSO
+.Xr pkg_add 1 ,
+.Xr pkg_delete 1 ,
+.Xr pkg_info 1 ,
+.Xr sysconf 3 .
+.Sh HISTORY
+The
+.Nm
+command first appeared in FreeBSD.
+.Sh AUTHORS
+.Bl -tag -width indent -compact
+.It "Jordan Hubbard"
+most of the work
+.It "John Kohl"
+refined it for NetBSD
+.El
+.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..a10e383
--- /dev/null
+++ b/usr.sbin/pkg_install/create/pl.c
@@ -0,0 +1,220 @@
+#ifndef lint
+static const char rcsid[] =
+ "$Id: pl.c,v 1.10 1997/02/22 16:09:30 peter Exp $";
+#endif
+
+/*
+ * FreeBSD install - a package for the installation and maintainance
+ * of non-core utilities.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * Jordan K. Hubbard
+ * 18 July 1993
+ *
+ * Routines for dealing with the packing list.
+ *
+ */
+
+#include "lib.h"
+#include "create.h"
+#include <errno.h>
+#include <err.h>
+#include <md5.h>
+
+/* Check a list for files that require preconversion */
+void
+check_list(char *home, Package *pkg)
+{
+ char *where = home;
+ char *there = NULL;
+ PackingList p = pkg->head;
+
+ while (p) {
+ if (p->type == PLIST_CWD)
+ where = p->name;
+ else if (p->type == PLIST_IGNORE)
+ p = p->next;
+ else if (p->type == PLIST_SRC) {
+ there = p->name;
+ }
+ else if (p->type == PLIST_FILE) {
+ char *cp, name[FILENAME_MAX], buf[33];
+
+ sprintf(name, "%s/%s", there ? there : where, p->name);
+ if ((cp = MD5File(name, buf)) != NULL) {
+ PackingList tmp = new_plist_entry();
+
+ tmp->name = copy_string(strconcat("MD5:", cp));
+ tmp->type = PLIST_COMMENT;
+ tmp->next = p->next;
+ tmp->prev = p;
+ p->next = tmp;
+ p = tmp;
+ }
+ }
+ p = p->next;
+ }
+}
+
+static int
+trylink(const char *from, const char *to)
+{
+ if (link(from, to) == 0)
+ return 0;
+ if (errno == ENOENT) {
+ /* try making the container directory */
+ char *cp = strrchr(to, '/');
+ if (cp)
+ vsystem("mkdir -p %.*s", cp - to,
+ to);
+ return link(from, to);
+ }
+ return -1;
+}
+
+#define STARTSTRING "tar cf -"
+#define TOOBIG(str) strlen(str) + 6 + strlen(home) + where_count > maxargs
+#define PUSHOUT() /* push out string */ \
+ if (where_count > sizeof(STARTSTRING)-1) { \
+ strcat(where_args, "|tar xpf -"); \
+ if (system(where_args)) \
+ cleanup(0), errx(2, "can't invoke tar pipeline"); \
+ memset(where_args, 0, maxargs); \
+ last_chdir = NULL; \
+ strcpy(where_args, STARTSTRING); \
+ where_count = sizeof(STARTSTRING)-1; \
+ }
+
+/*
+ * Copy unmarked files in packing list to playpen - marked files
+ * have already been copied in an earlier pass through the list.
+ */
+void
+copy_plist(char *home, Package *plist)
+{
+ PackingList p = plist->head;
+ char *where = home;
+ char *there = NULL, *mythere;
+ char *where_args, *last_chdir, *root = "/";
+ int maxargs, where_count = 0, add_count;
+ struct stat stb;
+ dev_t curdir;
+
+ maxargs = sysconf(_SC_ARG_MAX);
+ maxargs -= 64; /* some slop for the tar cmd text,
+ and sh -c */
+ where_args = malloc(maxargs);
+ if (!where_args)
+ cleanup(0), errx(2, "can't get argument list space");
+
+ memset(where_args, 0, maxargs);
+ strcpy(where_args, STARTSTRING);
+ where_count = sizeof(STARTSTRING)-1;
+ last_chdir = 0;
+
+ if (stat(".", &stb) == 0)
+ curdir = stb.st_dev;
+ else
+ curdir = (dev_t) -1; /* It's ok if this is a valid dev_t;
+ this is just a hint for an
+ optimization. */
+
+ while (p) {
+ if (p->type == PLIST_CWD)
+ where = p->name;
+ else if (p->type == PLIST_SRC)
+ there = p->name;
+ else if (p->type == PLIST_IGNORE)
+ p = p->next;
+ else if (p->type == PLIST_FILE && !p->marked) {
+ char fn[FILENAME_MAX];
+
+
+ /* First, look for it in the "home" dir */
+ sprintf(fn, "%s/%s", home, p->name);
+ if (fexists(fn)) {
+ if (lstat(fn, &stb) == 0 && stb.st_dev == curdir &&
+ S_ISREG(stb.st_mode)) {
+ /* if we can link it to the playpen, that avoids a copy
+ and saves time. */
+ if (p->name[0] != '/') {
+ /* don't link abspn stuff--it doesn't come from
+ local dir! */
+ if (trylink(fn, p->name) == 0) {
+ p = p->next;
+ continue;
+ }
+ }
+ }
+ if (TOOBIG(fn)) {
+ PUSHOUT();
+ }
+ if (p->name[0] == '/') {
+ add_count = snprintf(&where_args[where_count],
+ maxargs - where_count,
+ " %s %s",
+ last_chdir == root ? "" : "-C /",
+ p->name);
+ last_chdir = root;
+ } else {
+ add_count = snprintf(&where_args[where_count],
+ maxargs - where_count,
+ " %s%s %s",
+ last_chdir == home ? "" : "-C ",
+ last_chdir == home ? "" : home,
+ p->name);
+ last_chdir = home;
+ }
+ if (add_count > maxargs - where_count)
+ cleanup(0), errx(2, "oops, miscounted strings!");
+ where_count += add_count;
+ }
+ /*
+ * Otherwise, try along the actual extraction path..
+ */
+ else {
+ if (p->name[0] == '/')
+ mythere = root;
+ else mythere = there;
+ sprintf(fn, "%s/%s", mythere ? mythere : where, p->name);
+ if (lstat(fn, &stb) == 0 && stb.st_dev == curdir &&
+ S_ISREG(stb.st_mode)) {
+ /* if we can link it to the playpen, that avoids a copy
+ and saves time. */
+ if (trylink(fn, p->name) == 0) {
+ p = p->next;
+ continue;
+ }
+ }
+ if (TOOBIG(p->name)) {
+ PUSHOUT();
+ }
+ if (last_chdir == (mythere ? mythere : where))
+ add_count = snprintf(&where_args[where_count],
+ maxargs - where_count,
+ " %s", p->name);
+ else
+ add_count = snprintf(&where_args[where_count],
+ maxargs - where_count,
+ " -C %s %s",
+ mythere ? mythere : where,
+ p->name);
+ if (add_count > maxargs - where_count)
+ cleanup(0), errx(2, "oops, miscounted strings!");
+ where_count += add_count;
+ last_chdir = (mythere ? mythere : where);
+ }
+ }
+ p = p->next;
+ }
+ PUSHOUT();
+ free(where_args);
+}
diff --git a/usr.sbin/pkg_install/delete/Makefile b/usr.sbin/pkg_install/delete/Makefile
new file mode 100644
index 0000000..20a5717
--- /dev/null
+++ b/usr.sbin/pkg_install/delete/Makefile
@@ -0,0 +1,17 @@
+PROG= pkg_delete
+CFLAGS+= ${DEBUG} -I${.CURDIR}/../lib
+
+.if exists(${.OBJDIR}/../lib)
+LDADD+= -L${.OBJDIR}/../lib -linstall
+DPADD+= ${.OBJDIR}/../lib/libinstall.a
+.else
+LDADD+= -L${.CURDIR}/../lib -linstall
+DPADD+= ${.CURDIR}/../lib/libinstall.a
+.endif
+
+LDADD+= -lftpio -lmd
+DPADD+= ${LIBFTPIO} ${LIBMD}
+
+SRCS= main.c perform.c
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/pkg_install/delete/delete.h b/usr.sbin/pkg_install/delete/delete.h
new file mode 100644
index 0000000..c26345c
--- /dev/null
+++ b/usr.sbin/pkg_install/delete/delete.h
@@ -0,0 +1,33 @@
+/* $Id$ */
+
+/*
+ * FreeBSD install - a package for the installation and maintainance
+ * of non-core utilities.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * Jordan K. Hubbard
+ * 18 July 1993
+ *
+ * Include and define various things wanted by the delete command.
+ *
+ */
+
+#ifndef _INST_DELETE_H_INCLUDE
+#define _INST_DELETE_H_INCLUDE
+
+extern char *Prefix;
+extern Boolean NoDeInstall;
+extern Boolean CleanDirs;
+extern Boolean Force;
+extern char *Directory;
+extern char *PkgName;
+
+#endif /* _INST_DELETE_H_INCLUDE */
diff --git a/usr.sbin/pkg_install/delete/main.c b/usr.sbin/pkg_install/delete/main.c
new file mode 100644
index 0000000..863dc4a
--- /dev/null
+++ b/usr.sbin/pkg_install/delete/main.c
@@ -0,0 +1,108 @@
+#ifndef lint
+static const char rcsid[] =
+ "$Id: main.c,v 1.10 1997/09/18 14:08:40 phk Exp $";
+#endif
+
+/*
+ *
+ * FreeBSD install - a package for the installation and maintainance
+ * of non-core utilities.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * Jordan K. Hubbard
+ * 18 July 1993
+ *
+ * This is the delete module.
+ *
+ */
+
+#include <err.h>
+#include "lib.h"
+#include "delete.h"
+
+static char Options[] = "hvDdnfp:";
+
+char *Prefix = NULL;
+Boolean NoDeInstall = FALSE;
+Boolean CleanDirs = FALSE;
+
+static void usage __P((void));
+
+int
+main(int argc, char **argv)
+{
+ int ch, error;
+ char **pkgs, **start;
+
+ pkgs = start = argv;
+ while ((ch = getopt(argc, argv, Options)) != -1)
+ switch(ch) {
+ case 'v':
+ Verbose = TRUE;
+ break;
+
+ case 'f':
+ Force = TRUE;
+ break;
+
+ case 'p':
+ Prefix = optarg;
+ break;
+
+ case 'D':
+ NoDeInstall = TRUE;
+ break;
+
+ case 'd':
+ CleanDirs = TRUE;
+ break;
+
+ case 'n':
+ Fake = TRUE;
+ Verbose = TRUE;
+ break;
+
+ case 'h':
+ case '?':
+ default:
+ usage();
+ break;
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ /* Get all the remaining package names, if any */
+ /* Get all the remaining package names, if any */
+ while (*argv)
+ *pkgs++ = *argv++;
+
+ /* If no packages, yelp */
+ if (pkgs == start)
+ warnx("missing package name(s)"), usage();
+ *pkgs = NULL;
+ if (!Fake && getuid() != 0)
+ errx(1, "you must be root to delete packages");
+ if ((error = pkg_perform(start)) != 0) {
+ if (Verbose)
+ warnx("%d package deletion(s) failed", error);
+ return error;
+ }
+ else
+ return 0;
+}
+
+static void
+usage()
+{
+ fprintf(stderr, "usage: pkg_delete [-vDdnf] [-p prefix] pkg-name ...\n");
+ exit(1);
+}
diff --git a/usr.sbin/pkg_install/delete/perform.c b/usr.sbin/pkg_install/delete/perform.c
new file mode 100644
index 0000000..92ca3cb
--- /dev/null
+++ b/usr.sbin/pkg_install/delete/perform.c
@@ -0,0 +1,222 @@
+#ifndef lint
+static const char rcsid[] =
+ "$Id: perform.c,v 1.14 1997/10/08 07:46:52 charnier Exp $";
+#endif
+
+/*
+ * FreeBSD install - a package for the installation and maintainance
+ * of non-core utilities.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * Jordan K. Hubbard
+ * 18 July 1993
+ *
+ * This is the main body of the delete module.
+ *
+ */
+
+#include <err.h>
+#include "lib.h"
+#include "delete.h"
+
+static int pkg_do(char *);
+static void sanity_check(char *);
+static void undepend(PackingList, char *);
+static char LogDir[FILENAME_MAX];
+
+
+int
+pkg_perform(char **pkgs)
+{
+ int i, err_cnt = 0;
+
+ for (i = 0; pkgs[i]; i++)
+ err_cnt += pkg_do(pkgs[i]);
+ return err_cnt;
+}
+
+static Package Plist;
+
+/* This is seriously ugly code following. Written very fast! */
+static int
+pkg_do(char *pkg)
+{
+ FILE *cfile;
+ char home[FILENAME_MAX];
+ PackingList p;
+ char *tmp;
+
+ /* Reset some state */
+ if (Plist.head)
+ free_plist(&Plist);
+
+ sprintf(LogDir, "%s/%s", (tmp = getenv(PKG_DBDIR)) ? tmp : DEF_LOG_DIR,
+ pkg);
+ if (!fexists(LogDir)) {
+ warnx("no such package '%s' installed", pkg);
+ return 1;
+ }
+ if (!getcwd(home, FILENAME_MAX))
+ cleanup(0), errx(2, "unable to get current working directory!");
+ if (chdir(LogDir) == FAIL) {
+ warnx("unable to change directory to %s! deinstall failed", LogDir);
+ return 1;
+ }
+ if (!isemptyfile(REQUIRED_BY_FNAME)) {
+ char buf[512];
+ warnx("package `%s' is required by these other packages\n"
+ "and may not be deinstalled%s:",
+ pkg, Force ? " (but I'll delete it anyway)" : "" );
+ cfile = fopen(REQUIRED_BY_FNAME, "r");
+ if (cfile) {
+ while (fgets(buf, sizeof(buf), cfile))
+ fprintf(stderr, "%s", buf);
+ fclose(cfile);
+ } else
+ warnx("cannot open requirements file `%s'", REQUIRED_BY_FNAME);
+ if (!Force)
+ return 1;
+ }
+ sanity_check(LogDir);
+ cfile = fopen(CONTENTS_FNAME, "r");
+ if (!cfile) {
+ warnx("unable to open '%s' file", CONTENTS_FNAME);
+ return 1;
+ }
+ /* If we have a prefix, add it now */
+ if (Prefix)
+ add_plist(&Plist, PLIST_CWD, Prefix);
+ read_plist(&Plist, cfile);
+ fclose(cfile);
+ p = find_plist(&Plist, PLIST_CWD);
+ if (!p) {
+ warnx("package '%s' doesn't have a prefix", pkg);
+ return 1;
+ }
+ setenv(PKG_PREFIX_VNAME, p->name, 1);
+ if (fexists(REQUIRE_FNAME)) {
+ if (Verbose)
+ printf("Executing 'require' script.\n");
+ vsystem("chmod +x %s", REQUIRE_FNAME); /* be sure */
+ if (vsystem("./%s %s DEINSTALL", REQUIRE_FNAME, pkg)) {
+ warnx("package %s fails requirements %s", pkg,
+ Force ? "" : "- not deleted");
+ if (!Force)
+ return 1;
+ }
+ }
+ if (!NoDeInstall && fexists(DEINSTALL_FNAME)) {
+ if (Fake)
+ printf("Would execute de-install script at this point.\n");
+ else {
+ vsystem("chmod +x %s", DEINSTALL_FNAME); /* make sure */
+ if (vsystem("./%s %s DEINSTALL", DEINSTALL_FNAME, pkg)) {
+ warnx("deinstall script returned error status");
+ if (!Force)
+ return 1;
+ }
+ }
+ }
+ if (chdir(home) == FAIL)
+ cleanup(0), errx(2, "Toto! This doesn't look like Kansas anymore!");
+ if (!Fake) {
+ /* Some packages aren't packed right, so we need to just ignore delete_package()'s status. Ugh! :-( */
+ if (delete_package(FALSE, CleanDirs, &Plist) == FAIL)
+ warnx(
+ "couldn't entirely delete package (perhaps the packing list is\n"
+ "incorrectly specified?)");
+ if (vsystem("%s -r %s", REMOVE_CMD, LogDir)) {
+ warnx("couldn't remove log entry in %s, deinstall failed", LogDir);
+ if (!Force)
+ return 1;
+ }
+ }
+ for (p = Plist.head; p ; p = p->next) {
+ if (p->type != PLIST_PKGDEP)
+ continue;
+ if (Verbose)
+ printf("Attempting to remove dependency on package `%s'\n", p->name);
+ if (!Fake)
+ undepend(p, pkg);
+ }
+ return 0;
+}
+
+static void
+sanity_check(char *pkg)
+{
+ if (!fexists(CONTENTS_FNAME))
+ cleanup(0), errx(2, "installed package %s has no %s file!",
+ pkg, CONTENTS_FNAME);
+}
+
+void
+cleanup(int sig)
+{
+ /* Nothing to do */
+ exit(1);
+}
+
+static void
+undepend(PackingList p, char *pkgname)
+{
+ char fname[FILENAME_MAX], ftmp[FILENAME_MAX];
+ char fbuf[FILENAME_MAX];
+ FILE *fp, *fpwr;
+ char *tmp;
+ int s;
+
+ sprintf(fname, "%s/%s/%s",
+ (tmp = getenv(PKG_DBDIR)) ? tmp : DEF_LOG_DIR,
+ p->name, REQUIRED_BY_FNAME);
+ fp = fopen(fname, "r");
+ if (fp == NULL) {
+ warnx("couldn't open dependency file `%s'", fname);
+ return;
+ }
+ sprintf(ftmp, "%s.XXXXXX", fname);
+ s = mkstemp(ftmp);
+ if (s == -1) {
+ fclose(fp);
+ warnx("couldn't open temp file `%s'", ftmp);
+ return;
+ }
+ fpwr = fdopen(s, "w");
+ if (fpwr == NULL) {
+ close(s);
+ fclose(fp);
+ warnx("couldn't fdopen temp file `%s'", ftmp);
+ remove(ftmp);
+ return;
+ }
+ while (fgets(fbuf, sizeof(fbuf), fp) != NULL) {
+ if (fbuf[strlen(fbuf)-1] == '\n')
+ fbuf[strlen(fbuf)-1] = '\0';
+ if (strcmp(fbuf, pkgname)) /* no match */
+ fputs(fbuf, fpwr), putc('\n', fpwr);
+ }
+ (void) fclose(fp);
+ if (fchmod(s, 0644) == FAIL) {
+ warnx("error changing permission of temp file `%s'", ftmp);
+ fclose(fpwr);
+ remove(ftmp);
+ return;
+ }
+ if (fclose(fpwr) == EOF) {
+ warnx("error closing temp file `%s'", ftmp);
+ remove(ftmp);
+ return;
+ }
+ if (rename(ftmp, fname) == -1)
+ warnx("error renaming `%s' to `%s'", ftmp, fname);
+ remove(ftmp); /* just in case */
+ return;
+}
diff --git a/usr.sbin/pkg_install/delete/pkg_delete.1 b/usr.sbin/pkg_install/delete/pkg_delete.1
new file mode 100644
index 0000000..9a85d5c
--- /dev/null
+++ b/usr.sbin/pkg_install/delete/pkg_delete.1
@@ -0,0 +1,178 @@
+.\"
+.\" FreeBSD install - a package for the installation and maintainance
+.\" of non-core utilities.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" Jordan K. Hubbard
+.\"
+.\"
+.\" @(#)pkg_delete.1
+.\"
+.Dd November 25, 1994
+.Dt pkg_delete 1
+.Os FreeBSD 2.0
+.Sh NAME
+.Nm pkg_delete
+.Nd a utility for deleting previously installed software package distributions
+.Sh SYNOPSIS
+.Nm
+.Op Fl vDdnf
+.Op Fl p Ar prefix
+.Ar pkg-name ...
+.Sh DESCRIPTION
+The
+.Nm
+command is used to delete packages that have been previously installed
+with the
+.Xr pkg_add 1
+command.
+
+.Sh WARNING
+.Bf -emphasis
+Since the
+.Nm
+command may execute scripts or programs provided by a package file,
+your system may be susceptible to ``trojan horses'' or other subtle
+attacks from miscreants who create dangerous package files.
+.Pp
+You are advised to verify the competence and identity of those who
+provide installable package files. For extra protection, examine all
+the package control files in the package record directory (
+.Pa /var/db/pkg/<pkg-name>/ ).
+Pay particular
+attention to any +INSTALL, +DEINSTALL, +REQUIRE or +MTREE_DIRS files,
+and inspect the +CONTENTS file for
+.Cm @cwd ,
+.Cm @mode
+(check for setuid),
+.Cm @dirrm ,
+.Cm @exec ,
+and
+.Cm @unexec
+directives, and/or use the
+.Xr pkg_info 1
+command to examine the installed package control files.
+.Ef
+
+.Sh OPTIONS
+The following command line options are supported:
+.Bl -tag -width indent
+.It Ar pkg-name ...
+The named packages are deinstalled.
+.It Fl v
+Turn on verbose output.
+.It Fl D
+If a deinstallation script exists for a given package, do not execute it.
+.It Fl n
+Don't actually deinstall a package, just report the steps that
+would be taken if it were.
+.It Fl p Ar prefix
+Set
+.Ar prefix
+as the directory in which to delete files from any installed packages
+which do not explicitly set theirs. For most packages, the prefix will
+be set automatically to the installed location by
+.Xr pkg_add 1 .
+.It Fl d
+Remove empty directories created by file cleanup. By default, only
+files/directories explicitly listed in a package's contents (either as
+normal files/directories or with the
+.Cm @dirrm
+directive) will be removed at deinstallation time. This option tells
+.Nm
+to also remove any directories that were emptied as a result of removing
+the package.
+.It Fl f
+Force removal of the package, even if a dependency is recorded or the
+deinstall or require script fails.
+.El
+
+.Pp
+.Sh TECHNICAL DETAILS
+.Nm Pkg_delete
+does pretty much what it says. It examines installed package records in
+.Pa /var/db/pkg/<pkg-name> ,
+deletes the package contents, and finally removes the package records.
+.Pp
+If a package is required by other installed packages,
+.Nm
+will list those dependent packages and refuse to delete the package
+(unless the
+.Fl f
+option is given).
+.Pp
+If the package contains a
+.Ar require
+file (see
+.Xr pkg_create 1 ),
+then this is executed first as
+.Bd -filled -offset indent -compact
+.Cm require
+.Ar <pkg-name>
+.Ar DEINSTALL
+.Ed
+(where
+.Ar pkg-name
+is the name of the package in question and
+.Ar DEINSTALL
+is a keyword denoting that this is a deinstallation)
+to see whether or not deinstallation should continue. A non-zero exit
+status means no, unless the
+.Fl f
+option is specified.
+.Pp
+If a
+.Cm deinstall
+script exists for the package, it is executed before any files are removed.
+It is this script's responsibility to clean up any additional messy details
+around the package's installation, since all
+.Nm
+knows how to do is delete the files created in the original distribution.
+The
+.Nm deinstall
+script is called as:
+.Bd -filled -offset indent -compact
+.Cm deinstall
+.Ar <pkg-name>
+.Ar DEINSTALL
+.Ed
+Passing the keyword
+.Ar DEINSTALL
+lets you potentially write only one program/script that handles all
+aspects of installation and deletion.
+.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 SEE ALSO
+.Xr pkg_add 1 ,
+.Xr pkg_create 1 ,
+.Xr pkg_info 1 ,
+.Xr mktemp 3 ,
+.Xr mtree 8 .
+.Sh AUTHORS
+.Bl -tag -width indent -compact
+.It "Jordan Hubbard"
+most of the work
+.It "John Kohl"
+refined it for NetBSD
+.El
+.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..29f98d2
--- /dev/null
+++ b/usr.sbin/pkg_install/info/Makefile
@@ -0,0 +1,17 @@
+PROG= pkg_info
+CFLAGS+= ${DEBUG} -I${.CURDIR}/../lib
+
+.if exists(${.OBJDIR}/../lib)
+LDADD+= -L${.OBJDIR}/../lib -linstall
+DPADD+= ${.OBJDIR}/../lib/libinstall.a
+.else
+LDADD+= -L${.CURDIR}/../lib -linstall
+DPADD+= ${.CURDIR}/../lib/libinstall.a
+.endif
+
+LDADD+= -lftpio -lmd
+DPADD+= ${LIBFTPIO} ${LIBMD}
+
+SRCS= main.c perform.c show.c
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/pkg_install/info/info.h b/usr.sbin/pkg_install/info/info.h
new file mode 100644
index 0000000..fed1164
--- /dev/null
+++ b/usr.sbin/pkg_install/info/info.h
@@ -0,0 +1,59 @@
+/* $Id$ */
+
+/*
+ * FreeBSD install - a package for the installation and maintainance
+ * of non-core utilities.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * Jordan K. Hubbard
+ * 23 August 1993
+ *
+ * Include and define various things wanted by the info command.
+ *
+ */
+
+#ifndef _INST_INFO_H_INCLUDE
+#define _INST_INFO_H_INCLUDE
+
+#ifndef MAXINDEXSIZE
+#define MAXINDEXSIZE 60
+#endif
+
+#ifndef MAXNAMESIZE
+#define MAXNAMESIZE 20
+#endif
+
+#define SHOW_COMMENT 0x0001
+#define SHOW_DESC 0x0002
+#define SHOW_PLIST 0x0004
+#define SHOW_INSTALL 0x0008
+#define SHOW_DEINSTALL 0x0010
+#define SHOW_REQUIRE 0x0020
+#define SHOW_PREFIX 0x0040
+#define SHOW_INDEX 0x0080
+#define SHOW_FILES 0x0100
+#define SHOW_DISPLAY 0x0200
+#define SHOW_REQBY 0x0400
+#define SHOW_MTREE 0x0800
+
+extern int Flags;
+extern Boolean AllInstalled;
+extern Boolean Quiet;
+extern char *InfoPrefix;
+extern char PlayPen[];
+extern char *CheckPkg;
+
+extern void show_file(char *, char *);
+extern void show_plist(char *, Package *, plist_t);
+extern void show_files(char *, Package *);
+extern void show_index(char *, char *);
+
+#endif /* _INST_INFO_H_INCLUDE */
diff --git a/usr.sbin/pkg_install/info/main.c b/usr.sbin/pkg_install/info/main.c
new file mode 100644
index 0000000..10c12bc
--- /dev/null
+++ b/usr.sbin/pkg_install/info/main.c
@@ -0,0 +1,159 @@
+#ifndef lint
+static const char rcsid[] =
+ "$Id: main.c,v 1.13 1997/03/31 05:10:50 imp Exp $";
+#endif
+
+/*
+ *
+ * FreeBSD install - a package for the installation and maintainance
+ * of non-core utilities.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * Jordan K. Hubbard
+ * 18 July 1993
+ *
+ * This is the add module.
+ *
+ */
+
+#include <err.h>
+#include "lib.h"
+#include "info.h"
+
+static char Options[] = "acdDe:fikrRpLqImvhl:";
+
+int Flags = 0;
+Boolean AllInstalled = FALSE;
+Boolean Quiet = FALSE;
+char *InfoPrefix = "";
+char PlayPen[FILENAME_MAX];
+char *CheckPkg = NULL;
+
+static void usage __P((void));
+
+int
+main(int argc, char **argv)
+{
+ int ch;
+ char **pkgs, **start;
+
+ pkgs = start = argv;
+ while ((ch = getopt(argc, argv, Options)) != -1)
+ switch(ch) {
+ case 'a':
+ AllInstalled = TRUE;
+ break;
+
+ case 'v':
+ Verbose = TRUE;
+ /* Reasonable definition of 'everything' */
+ Flags = SHOW_COMMENT | SHOW_DESC | SHOW_PLIST | SHOW_INSTALL |
+ SHOW_DEINSTALL | SHOW_REQUIRE | SHOW_DISPLAY | SHOW_MTREE;
+ break;
+
+ case 'I':
+ Flags |= SHOW_INDEX;
+ break;
+
+ case 'p':
+ Flags |= SHOW_PREFIX;
+ break;
+
+ case 'c':
+ Flags |= SHOW_COMMENT;
+ break;
+
+ case 'd':
+ Flags |= SHOW_DESC;
+ break;
+
+ case 'D':
+ Flags |= SHOW_DISPLAY;
+ break;
+
+ case 'f':
+ Flags |= SHOW_PLIST;
+ break;
+
+ case 'i':
+ Flags |= SHOW_INSTALL;
+ break;
+
+ case 'k':
+ Flags |= SHOW_DEINSTALL;
+ break;
+
+ case 'r':
+ Flags |= SHOW_REQUIRE;
+ break;
+
+ case 'R':
+ Flags |= SHOW_REQBY;
+ break;
+
+ case 'L':
+ Flags |= SHOW_FILES;
+ break;
+
+ case 'm':
+ Flags |= SHOW_MTREE;
+ break;
+
+ case 'l':
+ InfoPrefix = optarg;
+ break;
+
+ case 'q':
+ Quiet = TRUE;
+ break;
+
+ case 't':
+ strcpy(PlayPen, optarg);
+ break;
+
+ case 'e':
+ CheckPkg = optarg;
+ break;
+
+ case 'h':
+ case '?':
+ default:
+ usage();
+ break;
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ /* Set some reasonable defaults */
+ if (!Flags)
+ Flags = SHOW_COMMENT | SHOW_DESC | SHOW_REQBY;
+
+ /* Get all the remaining package names, if any */
+ while (*argv)
+ *pkgs++ = *argv++;
+
+ /* If no packages, yelp */
+ if (pkgs == start && !AllInstalled && !CheckPkg)
+ warnx("missing package name(s)"), usage();
+ *pkgs = NULL;
+ return pkg_perform(start);
+}
+
+static void
+usage()
+{
+ fprintf(stderr, "%s\n%s\n%s\n",
+ "usage: pkg_info [-cdDikrRpLqImv] [-e package] [-l prefix]",
+ " pkg-name [pkg-name ...]",
+ " pkg_info -a [flags]");
+ exit(1);
+}
diff --git a/usr.sbin/pkg_install/info/perform.c b/usr.sbin/pkg_install/info/perform.c
new file mode 100644
index 0000000..5625f62
--- /dev/null
+++ b/usr.sbin/pkg_install/info/perform.c
@@ -0,0 +1,206 @@
+#ifndef lint
+static const char rcsid[] =
+ "$Id: perform.c,v 1.22 1997/10/08 07:47:29 charnier Exp $";
+#endif
+
+/*
+ * FreeBSD install - a package for the installation and maintainance
+ * of non-core utilities.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * Jordan K. Hubbard
+ * 23 Aug 1993
+ *
+ * This is the main body of the info module.
+ *
+ */
+
+#include "lib.h"
+#include "info.h"
+
+#include <signal.h>
+
+static int pkg_do(char *);
+
+int
+pkg_perform(char **pkgs)
+{
+ int i, err_cnt = 0;
+ char *tmp;
+
+ signal(SIGINT, cleanup);
+
+ tmp = getenv(PKG_DBDIR);
+ if (!tmp)
+ tmp = DEF_LOG_DIR;
+ /* Overriding action? */
+ if (CheckPkg) {
+ char buf[FILENAME_MAX];
+
+ snprintf(buf, FILENAME_MAX, "%s/%s", tmp, CheckPkg);
+ return abs(access(buf, R_OK));
+ }
+ else if (AllInstalled) {
+ DIR *dirp;
+ struct dirent *dp;
+
+ if (!isdir(tmp))
+ return 1;
+ dirp = opendir(tmp);
+ if (dirp) {
+ for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp))
+ if (strcmp(dp->d_name, ".") && strcmp(dp->d_name, ".."))
+ err_cnt += pkg_do(dp->d_name);
+ (void)closedir(dirp);
+ }
+ }
+ else
+ for (i = 0; pkgs[i]; i++)
+ err_cnt += pkg_do(pkgs[i]);
+ return err_cnt;
+}
+
+static char *Home;
+
+static int
+pkg_do(char *pkg)
+{
+ Boolean installed = FALSE, isTMP = FALSE;
+ char log_dir[FILENAME_MAX];
+ char fname[FILENAME_MAX];
+ Package plist;
+ FILE *fp;
+ struct stat sb;
+ char *cp = NULL;
+ int code = 0;
+
+ if (isURL(pkg)) {
+ if ((cp = fileGetURL(NULL, pkg)) != NULL) {
+ strcpy(fname, cp);
+ isTMP = TRUE;
+ }
+ }
+ else if (fexists(pkg) && isfile(pkg)) {
+ int len;
+
+ if (*pkg != '/') {
+ if (!getcwd(fname, FILENAME_MAX))
+ upchuck("getcwd");
+ len = strlen(fname);
+ snprintf(&fname[len], FILENAME_MAX - len, "/%s", pkg);
+ }
+ else
+ strcpy(fname, pkg);
+ cp = fname;
+ }
+ else {
+ if ((cp = fileFindByPath(NULL, pkg)) != NULL)
+ strncpy(fname, cp, FILENAME_MAX);
+ }
+ if (cp) {
+ /*
+ * Apply a crude heuristic to see how much space the package will
+ * take up once it's unpacked. I've noticed that most packages
+ * compress an average of 75%, but we're only unpacking the + files so
+ * be very optimistic.
+ */
+ if (stat(fname, &sb) == FAIL) {
+ warnx("can't stat package file '%s'", fname);
+ code = 1;
+ goto bail;
+ }
+ Home = make_playpen(PlayPen, sb.st_size / 2);
+ if (unpack(fname, "+*")) {
+ warnx("error during unpacking, no info for '%s' available", pkg);
+ code = 1;
+ goto bail;
+ }
+ }
+ /* It's not an ininstalled package, try and find it among the installed */
+ else {
+ char *tmp;
+
+ sprintf(log_dir, "%s/%s", (tmp = getenv(PKG_DBDIR)) ? tmp : DEF_LOG_DIR,
+ pkg);
+ if (!fexists(log_dir)) {
+ warnx("can't find package `%s' installed or in a file!", pkg);
+ return 1;
+ }
+ if (chdir(log_dir) == FAIL) {
+ warnx("can't change directory to '%s'!", log_dir);
+ return 1;
+ }
+ installed = TRUE;
+ }
+
+ /* Suck in the contents list */
+ plist.head = plist.tail = NULL;
+ fp = fopen(CONTENTS_FNAME, "r");
+ if (!fp) {
+ warnx("unable to open %s file", CONTENTS_FNAME);
+ code = 1;
+ goto bail;
+ }
+ /* If we have a prefix, add it now */
+ read_plist(&plist, fp);
+ fclose(fp);
+
+ /*
+ * Index is special info type that has to override all others to make
+ * any sense.
+ */
+ if (Flags & SHOW_INDEX) {
+ char tmp[FILENAME_MAX];
+
+ snprintf(tmp, FILENAME_MAX, "%-19s ", pkg);
+ show_index(tmp, COMMENT_FNAME);
+ }
+ else {
+ /* Start showing the package contents */
+ if (!Quiet)
+ printf("%sInformation for %s:\n\n", InfoPrefix, pkg);
+ if (Flags & SHOW_COMMENT)
+ show_file("Comment:\n", COMMENT_FNAME);
+ if ((Flags & SHOW_REQBY) && !isemptyfile(REQUIRED_BY_FNAME))
+ show_file("Required by:\n", REQUIRED_BY_FNAME);
+ if (Flags & SHOW_DESC)
+ show_file("Description:\n", DESC_FNAME);
+ if ((Flags & SHOW_DISPLAY) && fexists(DISPLAY_FNAME))
+ show_file("Install notice:\n", DISPLAY_FNAME);
+ if (Flags & SHOW_PLIST)
+ show_plist("Packing list:\n", &plist, (plist_t)-1);
+ if ((Flags & SHOW_INSTALL) && fexists(INSTALL_FNAME))
+ show_file("Install script:\n", INSTALL_FNAME);
+ if ((Flags & SHOW_DEINSTALL) && fexists(DEINSTALL_FNAME))
+ show_file("De-Install script:\n", DEINSTALL_FNAME);
+ if ((Flags & SHOW_MTREE) && fexists(MTREE_FNAME))
+ show_file("mtree file:\n", MTREE_FNAME);
+ if (Flags & SHOW_PREFIX)
+ show_plist("Prefix(s):\n", &plist, PLIST_CWD);
+ if (Flags & SHOW_FILES)
+ show_files("Files:\n", &plist);
+ if (!Quiet)
+ puts(InfoPrefix);
+ }
+ free_plist(&plist);
+ bail:
+ leave_playpen(Home);
+ if (isTMP)
+ unlink(fname);
+ return code;
+}
+
+void
+cleanup(int sig)
+{
+ leave_playpen(Home);
+ exit(1);
+}
diff --git a/usr.sbin/pkg_install/info/pkg_info.1 b/usr.sbin/pkg_install/info/pkg_info.1
new file mode 100644
index 0000000..ea8df62
--- /dev/null
+++ b/usr.sbin/pkg_install/info/pkg_info.1
@@ -0,0 +1,135 @@
+.\"
+.\" FreeBSD install - a package for the installation and maintainance
+.\" of non-core utilities.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" Jordan K. Hubbard
+.\"
+.\"
+.\" @(#)pkg_info.1
+.\"
+.Dd November 25, 1994
+.Dt pkg_info 1
+.Os FreeBSD
+.Sh NAME
+.Nm pkg_info
+.Nd a utility for displaying information on software packages
+.Sh SYNOPSIS
+.Nm pkg_info
+.Op Fl cdDikrRpLqImv
+.Op Fl e Ar package
+.Op Fl l Ar prefix
+.Ar pkg-name [pkg-name ...]
+.Nm pkg_info
+.Fl a
+.Op Ar flags
+.Sh DESCRIPTION
+The
+.Nm
+command is used to dump out information for packages, either packed up in
+files or already installed on the system
+with the
+.Xr pkg_create 1
+command.
+.Sh OPTIONS
+The following command line options are supported:
+.Bl -tag -width indent
+.It Ar pkg-name ...
+The named packages are described. A package name may either be the name of
+an installed package, the pathname to a package distribution file or a
+URL to an ftp available package.
+.It Fl a
+Show all currently installed packages.
+.It Fl v
+Turn on verbose output.
+.It Fl p
+Show the installation prefix for each package.
+.It Fl q
+Be ``quiet'' in emitting report headers and such, just dump the
+raw info (basically, assume a non-human reading).
+.It Fl c
+Show the comment (one liner) field for each package.
+.It Fl d
+Show the long description field for each package.
+.It Fl D
+Show the install-message file for each package.
+.It Fl f
+Show the packing list instructions for each package.
+.It Fl i
+Show the install script (if any) for each package.
+.It Fl 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 m
+Show the mtree file (if any) for each package.
+.It Fl L
+Show the files within each package. This is different from just
+viewing the packing list, since full pathnames for everything
+are generated.
+.It Fl e Ar pkg-name
+If the package identified by
+.Ar pkg-name
+is currently installed, return 0, otherwise return 1. This option
+allows you to easily test for the presence of another (perhaps
+prerequisite) package from a script.
+.It Fl l Ar str
+Prefix each information category header (see
+.Fl q )
+shown with
+.Ar str .
+This is primarily of use to front-end programs who want to request a
+lot of different information fields at once for a package, but don't
+necessary want the output intermingled in such a way that they can't
+organize it. This lets you add a special token to the start of
+each field.
+.It Fl t Ar template
+Use
+.Ar template
+as the input to
+.Xr mktemp 3
+when creating a ``staging area.''
+By default, this is the string
+.Pa /tmp/instmp.XXXXXX ,
+but it may be necessary to override it in the situation where
+space in your
+.Pa /tmp
+directory is limited. Be sure to leave some number of `X' characters
+for
+.Xr mktemp 3
+to fill in with a unique ID.
+.Bd -filled -offset indent -compact
+Note: This should really not be necessary with pkg_info,
+since very little information is extracted from each package
+and one would have to have a very small
+.Pa /tmp
+indeed to overflow it.
+.Ed
+.Sh TECHNICAL DETAILS
+Package info is either extracted from package files named on the
+command line, or from already installed package information
+in
+.Pa /var/db/pkg/<pkg-name> .
+.Sh SEE ALSO
+.Xr pkg_add 1 ,
+.Xr pkg_create 1 ,
+.Xr pkg_delete 1 ,
+.Xr mktemp 3 ,
+.Xr mtree 8 .
+.Sh AUTHORS
+.Bl -tag -width indent -compact
+.It "Jordan Hubbard"
+most of the work
+.It "John Kohl"
+refined it for NetBSD
+.El
+.Sh BUGS
+Sure to be some.
diff --git a/usr.sbin/pkg_install/info/show.c b/usr.sbin/pkg_install/info/show.c
new file mode 100644
index 0000000..9c00a7e
--- /dev/null
+++ b/usr.sbin/pkg_install/info/show.c
@@ -0,0 +1,199 @@
+#ifndef lint
+static const char rcsid[] =
+ "$Id: show.c,v 1.11 1997/10/08 07:47:38 charnier Exp $";
+#endif
+
+/*
+ * FreeBSD install - a package for the installation and maintainance
+ * of non-core utilities.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * Jordan K. Hubbard
+ * 23 Aug 1993
+ *
+ * Various display routines for the info module.
+ *
+ */
+
+#include "lib.h"
+#include "info.h"
+
+void
+show_file(char *title, char *fname)
+{
+ FILE *fp;
+ char line[1024];
+ int n;
+
+ if (!Quiet)
+ printf("%s%s", InfoPrefix, title);
+ fp = fopen(fname, "r");
+ if (!fp)
+ printf("ERROR: show_file: Can't open '%s' for reading!\n", fname);
+ else {
+ while ((n = fread(line, 1, 1024, fp)) != 0)
+ fwrite(line, 1, n, stdout);
+ fclose(fp);
+ }
+ printf("\n"); /* just in case */
+}
+
+void
+show_index(char *title, char *fname)
+{
+ FILE *fp;
+ char line[MAXINDEXSIZE+2];
+
+ if (!Quiet)
+ printf("%s%s", InfoPrefix, title);
+ fp = fopen(fname, "r");
+ if (!fp) {
+ warnx("show_file: can't open '%s' for reading", fname);
+ return;
+ }
+ if(fgets(line, MAXINDEXSIZE+1, fp)) {
+ if(line[MAXINDEXSIZE-1] != '\n')
+ line[MAXINDEXSIZE] = '\n';
+ line[MAXINDEXSIZE+1] = 0;
+ fputs(line, stdout);
+ }
+ fclose(fp);
+}
+
+/* Show a packing list item type. If type is -1, show all */
+void
+show_plist(char *title, Package *plist, plist_t type)
+{
+ PackingList p;
+ Boolean ign = FALSE;
+
+ if (!Quiet)
+ printf("%s%s", InfoPrefix, title);
+ p = plist->head;
+ while (p) {
+ if (p->type != type && type != -1) {
+ p = p->next;
+ continue;
+ }
+ switch(p->type) {
+ case PLIST_FILE:
+ if (ign) {
+ printf(Quiet ? "%s\n" : "File: %s (ignored)\n", p->name);
+ ign = FALSE;
+ }
+ else
+ printf(Quiet ? "%s\n" : "File: %s\n", p->name);
+ break;
+
+ case PLIST_CWD:
+ printf(Quiet ? "@cwd %s\n" : "\tCWD to %s\n", p->name);
+ break;
+
+ case PLIST_SRC:
+ printf(Quiet ? "@srcdir %s\n" : "\tSRCDIR to %s\n", p->name);
+ break;
+
+ case PLIST_CMD:
+ printf(Quiet ? "@exec %s\n" : "\tEXEC '%s'\n", p->name);
+ break;
+
+ case PLIST_UNEXEC:
+ printf(Quiet ? "@unexec %s\n" : "\tUNEXEC '%s'\n", p->name);
+ break;
+
+ case PLIST_CHMOD:
+ printf(Quiet ? "@chmod %s\n" : "\tCHMOD to %s\n",
+ p->name ? p->name : "(clear default)");
+ break;
+
+ case PLIST_CHOWN:
+ printf(Quiet ? "@chown %s\n" : "\tCHOWN to %s\n",
+ p->name ? p->name : "(clear default)");
+ break;
+
+ case PLIST_CHGRP:
+ printf(Quiet ? "@chgrp %s\n" : "\tCHGRP to %s\n",
+ p->name ? p->name : "(clear default)");
+ break;
+
+ case PLIST_COMMENT:
+ printf(Quiet ? "@comment %s\n" : "\tComment: %s\n", p->name);
+ break;
+
+ case PLIST_IGNORE:
+ ign = TRUE;
+ break;
+
+ case PLIST_IGNORE_INST:
+ printf(Quiet ? "@ignore_inst ??? doesn't belong here.\n" :
+ "\tIgnore next file installation directive (doesn't belong)\n");
+ ign = TRUE;
+ break;
+
+ case PLIST_NAME:
+ printf(Quiet ? "@name %s\n" : "\tPackage name: %s\n", p->name);
+ break;
+
+ case PLIST_DISPLAY:
+ printf(Quiet ? "@display %s\n" : "\tInstall message file: %s\n", p->name);
+ break;
+
+ case PLIST_PKGDEP:
+ printf(Quiet ? "@pkgdep %s\n" : "\tPackage depends on: %s\n", p->name);
+ break;
+
+ case PLIST_MTREE:
+ printf(Quiet ? "@mtree %s\n" : "\tPackage mtree file: %s\n", p->name);
+ break;
+
+ case PLIST_DIR_RM:
+ printf(Quiet ? "@dirrm %s\n" : "\tDeinstall directory remove: %s\n", p->name);
+ break;
+
+ default:
+ cleanup(0);
+ errx(2, "unknown command type %d (%s)", p->type, p->name);
+ break;
+ }
+ p = p->next;
+ }
+}
+
+/* Show all files in the packing list (except ignored ones) */
+void
+show_files(char *title, Package *plist)
+{
+ PackingList p;
+ Boolean ign = FALSE;
+ char *dir = ".";
+
+ if (!Quiet)
+ printf("%s%s", InfoPrefix, title);
+ p = plist->head;
+ while (p) {
+ switch(p->type) {
+ case PLIST_FILE:
+ if (!ign)
+ printf("%s/%s\n", dir, p->name);
+ ign = FALSE;
+ break;
+
+ case PLIST_CWD:
+ dir = p->name;
+ break;
+
+ case PLIST_IGNORE:
+ ign = TRUE;
+ break;
+ }
+ p = p->next;
+ }
+}
diff --git a/usr.sbin/pkg_install/lib/Makefile b/usr.sbin/pkg_install/lib/Makefile
new file mode 100644
index 0000000..3876b10
--- /dev/null
+++ b/usr.sbin/pkg_install/lib/Makefile
@@ -0,0 +1,10 @@
+LIB= install
+SRCS= file.c msg.c plist.c str.c exec.c global.c pen.c
+CFLAGS+= ${DEBUG}
+NOPROFILE= yes
+NOPIC= yes
+
+install:
+ @echo -n
+
+.include <bsd.lib.mk>
diff --git a/usr.sbin/pkg_install/lib/exec.c b/usr.sbin/pkg_install/lib/exec.c
new file mode 100644
index 0000000..0087c18
--- /dev/null
+++ b/usr.sbin/pkg_install/lib/exec.c
@@ -0,0 +1,62 @@
+#ifndef lint
+static const char rcsid[] =
+ "$Id: exec.c,v 1.5 1997/02/22 16:09:46 peter Exp $";
+#endif
+
+/*
+ * FreeBSD install - a package for the installation and maintainance
+ * of non-core utilities.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * Jordan K. Hubbard
+ * 18 July 1993
+ *
+ * Miscellaneous system routines.
+ *
+ */
+
+#include <err.h>
+#include "lib.h"
+
+/*
+ * Unusual system() substitute. Accepts format string and args,
+ * builds and executes command. Returns exit code.
+ */
+
+int
+vsystem(const char *fmt, ...)
+{
+ va_list args;
+ char *cmd;
+ int ret, maxargs;
+
+ maxargs = sysconf(_SC_ARG_MAX);
+ maxargs -= 32; /* some slop for the sh -c */
+ cmd = malloc(maxargs);
+ if (!cmd) {
+ warnx("vsystem can't alloc arg space");
+ return 1;
+ }
+
+ va_start(args, fmt);
+ if (vsnprintf(cmd, maxargs, fmt, args) > maxargs) {
+ warnx("vsystem args are too long");
+ return 1;
+ }
+#ifdef DEBUG
+printf("Executing %s\n", cmd);
+#endif
+ ret = system(cmd);
+ va_end(args);
+ free(cmd);
+ return ret;
+}
+
diff --git a/usr.sbin/pkg_install/lib/file.c b/usr.sbin/pkg_install/lib/file.c
new file mode 100644
index 0000000..ef52dca
--- /dev/null
+++ b/usr.sbin/pkg_install/lib/file.c
@@ -0,0 +1,539 @@
+#ifndef lint
+static const char rcsid[] =
+ "$Id: file.c,v 1.29 1997/10/08 07:47:54 charnier Exp $";
+#endif
+
+/*
+ * FreeBSD install - a package for the installation and maintainance
+ * of non-core utilities.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * Jordan K. Hubbard
+ * 18 July 1993
+ *
+ * Miscellaneous file access utilities.
+ *
+ */
+
+#include "lib.h"
+#include <err.h>
+#include <ftpio.h>
+#include <netdb.h>
+#include <pwd.h>
+#include <time.h>
+#include <sys/wait.h>
+
+/* Quick check to see if a file exists */
+Boolean
+fexists(char *fname)
+{
+ struct stat dummy;
+ if (!lstat(fname, &dummy))
+ return TRUE;
+ return FALSE;
+}
+
+/* Quick check to see if something is a directory */
+Boolean
+isdir(char *fname)
+{
+ struct stat sb;
+
+ if (stat(fname, &sb) != FAIL && S_ISDIR(sb.st_mode))
+ return TRUE;
+ else
+ return FALSE;
+}
+
+/* Check to see if file is a dir, and is empty */
+Boolean
+isemptydir(char *fname)
+{
+ if (isdir(fname)) {
+ DIR *dirp;
+ struct dirent *dp;
+
+ dirp = opendir(fname);
+ if (!dirp)
+ return FALSE; /* no perms, leave it alone */
+ for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) {
+ if (strcmp(dp->d_name, ".") && strcmp(dp->d_name, "..")) {
+ closedir(dirp);
+ return FALSE;
+ }
+ }
+ (void)closedir(dirp);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+Boolean
+isfile(char *fname)
+{
+ struct stat sb;
+ if (stat(fname, &sb) != FAIL && S_ISREG(sb.st_mode))
+ return TRUE;
+ return FALSE;
+}
+
+/* Check to see if file is a file and is empty. If nonexistent or not
+ a file, say "it's empty", otherwise return TRUE if zero sized. */
+Boolean
+isemptyfile(char *fname)
+{
+ struct stat sb;
+ if (stat(fname, &sb) != FAIL && S_ISREG(sb.st_mode)) {
+ if (sb.st_size != 0)
+ return FALSE;
+ }
+ return TRUE;
+}
+
+/* Returns TRUE if file is a URL specification */
+Boolean
+isURL(char *fname)
+{
+ /*
+ * I'm sure there are other types of URL specifications that I could
+ * also be looking for here, but for now I'll just be happy to get ftp
+ * working.
+ */
+ if (!fname)
+ return FALSE;
+ while (isspace(*fname))
+ ++fname;
+ if (!strncmp(fname, "ftp://", 6))
+ return TRUE;
+ return FALSE;
+}
+
+/* Returns the host part of a URL */
+char *
+fileURLHost(char *fname, char *where, int max)
+{
+ char *ret;
+
+ while (isspace(*fname))
+ ++fname;
+ /* Don't ever call this on a bad URL! */
+ fname += strlen("ftp://");
+ /* Do we have a place to stick our work? */
+ if ((ret = where) != NULL) {
+ while (*fname && *fname != '/' && max--)
+ *where++ = *fname++;
+ *where = '\0';
+ return ret;
+ }
+ /* If not, they must really want us to stomp the original string */
+ ret = fname;
+ while (*fname && *fname != '/')
+ ++fname;
+ *fname = '\0';
+ return ret;
+}
+
+/* Returns the filename part of a URL */
+char *
+fileURLFilename(char *fname, char *where, int max)
+{
+ char *ret;
+
+ while (isspace(*fname))
+ ++fname;
+ /* Don't ever call this on a bad URL! */
+ fname += strlen("ftp://");
+ /* Do we have a place to stick our work? */
+ if ((ret = where) != NULL) {
+ while (*fname && *fname != '/')
+ ++fname;
+ if (*fname == '/') {
+ while (*fname && max--)
+ *where++ = *fname++;
+ }
+ *where = '\0';
+ return ret;
+ }
+ /* If not, they must really want us to stomp the original string */
+ while (*fname && *fname != '/')
+ ++fname;
+ return fname;
+}
+
+#define HOSTNAME_MAX 64
+/*
+ * Try and fetch a file by URL, returning the directory name for where
+ * it's unpacked, if successful.
+ */
+char *
+fileGetURL(char *base, char *spec)
+{
+ char host[HOSTNAME_MAX], file[FILENAME_MAX];
+ char pword[HOSTNAME_MAX + 40], *uname, *cp, *rp;
+ char fname[FILENAME_MAX];
+ char pen[FILENAME_MAX];
+ struct passwd *pw;
+ FILE *ftp;
+ pid_t tpid;
+ int i, status;
+ char *hint;
+
+ rp = NULL;
+ /* Special tip that sysinstall left for us */
+ hint = getenv("PKG_ADD_BASE");
+ if (!isURL(spec)) {
+ if (!base && !hint)
+ return NULL;
+ /* We've been given an existing URL (that's known-good) and now we need
+ to construct a composite one out of that and the basename we were
+ handed as a dependency. */
+ if (base) {
+ strcpy(fname, base);
+ /* Advance back two slashes to get to the root of the package hierarchy */
+ cp = strrchr(fname, '/');
+ if (cp) {
+ *cp = '\0'; /* chop name */
+ cp = strrchr(fname, '/');
+ }
+ if (cp) {
+ *(cp + 1) = '\0';
+ strcat(cp, "All/");
+ strcat(cp, spec);
+ strcat(cp, ".tgz");
+ }
+ else
+ return NULL;
+ }
+ else {
+ /* Otherwise, we've been given an environment variable hinting at the right location from sysinstall */
+ strcpy(fname, hint);
+ strcat(fname, spec);
+ }
+ }
+ else
+ strcpy(fname, spec);
+ cp = fileURLHost(fname, host, HOSTNAME_MAX);
+ if (!*cp) {
+ warnx("URL `%s' has bad host part!", fname);
+ return NULL;
+ }
+
+ cp = fileURLFilename(fname, file, FILENAME_MAX);
+ if (!*cp) {
+ warnx("URL `%s' has bad filename part!", fname);
+ return NULL;
+ }
+
+ /* Maybe change to ftp if this doesn't work */
+ uname = "anonymous";
+
+ /* Make up a convincing "password" */
+ pw = getpwuid(getuid());
+ if (!pw) {
+ warnx("can't get user name for ID %d", getuid());
+ strcpy(pword, "joe@");
+ }
+ else {
+ char me[HOSTNAME_MAX];
+
+ gethostname(me, HOSTNAME_MAX);
+ snprintf(pword, HOSTNAME_MAX + 40, "%s@%s", pw->pw_name, me);
+ }
+ if (Verbose)
+ printf("Trying to fetch %s.\n", fname);
+ ftp = ftpGetURL(fname, uname, pword, &status);
+ if (ftp) {
+ pen[0] = '\0';
+ if ((rp = make_playpen(pen, 0)) != NULL) {
+ if (Verbose)
+ printf("Extracting from FTP connection into %s\n", pen);
+ tpid = fork();
+ if (!tpid) {
+ dup2(fileno(ftp), 0);
+ i = execl("/usr/bin/tar", "tar", Verbose ? "-xzvf" : "-xzf", "-", 0);
+ exit(i);
+ }
+ else {
+ int pstat;
+
+ fclose(ftp);
+ tpid = waitpid(tpid, &pstat, 0);
+ if (Verbose)
+ printf("tar command returns %d status\n", WEXITSTATUS(pstat));
+ }
+ }
+ else
+ printf("Error: Unable to construct a new playpen for FTP!\n");
+ fclose(ftp);
+ }
+ else
+ printf("Error: FTP Unable to get %s: %s\n",
+ fname,
+ status ? ftpErrString(status) : hstrerror(h_errno));
+ return rp;
+}
+
+char *
+fileFindByPath(char *base, char *fname)
+{
+ static char tmp[FILENAME_MAX];
+ char *cp;
+
+ if (fexists(fname) && isfile(fname)) {
+ strcpy(tmp, fname);
+ return tmp;
+ }
+ if (base) {
+ strcpy(tmp, base);
+
+ cp = strrchr(tmp, '/');
+ if (cp) {
+ *cp = '\0'; /* chop name */
+ cp = strrchr(tmp, '/');
+ }
+ if (cp) {
+ *(cp + 1) = '\0';
+ strcat(cp, "All/");
+ strcat(cp, fname);
+ strcat(cp, ".tgz");
+ if (fexists(tmp))
+ return tmp;
+ }
+ }
+
+ cp = getenv("PKG_PATH");
+ while (cp) {
+ char *cp2 = strsep(&cp, ":");
+
+ snprintf(tmp, FILENAME_MAX, "%s/%s.tgz", cp2 ? cp2 : cp, fname);
+ if (fexists(tmp) && isfile(tmp))
+ return tmp;
+ }
+ return NULL;
+}
+
+char *
+fileGetContents(char *fname)
+{
+ char *contents;
+ struct stat sb;
+ int fd;
+
+ if (stat(fname, &sb) == FAIL)
+ cleanup(0), errx(2, "can't stat '%s'", fname);
+
+ contents = (char *)malloc(sb.st_size + 1);
+ fd = open(fname, O_RDONLY, 0);
+ if (fd == FAIL)
+ cleanup(0), errx(2, "unable to open '%s' for reading", fname);
+ if (read(fd, contents, sb.st_size) != sb.st_size)
+ cleanup(0), errx(2, "short read on '%s' - did not get %qd bytes",
+ fname, sb.st_size);
+ close(fd);
+ contents[sb.st_size] = '\0';
+ return contents;
+}
+
+/* Takes a filename and package name, returning (in "try") the canonical "preserve"
+ * name for it.
+ */
+Boolean
+make_preserve_name(char *try, int max, char *name, char *file)
+{
+ int len, i;
+
+ if ((len = strlen(file)) == 0)
+ return FALSE;
+ else
+ i = len - 1;
+ strncpy(try, file, max);
+ if (try[i] == '/') /* Catch trailing slash early and save checking in the loop */
+ --i;
+ for (; i; i--) {
+ if (try[i] == '/') {
+ try[i + 1]= '.';
+ strncpy(&try[i + 2], &file[i + 1], max - i - 2);
+ break;
+ }
+ }
+ if (!i) {
+ try[0] = '.';
+ strncpy(try + 1, file, max - 1);
+ }
+ /* I should probably be called rude names for these inline assignments */
+ strncat(try, ".", max -= strlen(try));
+ strncat(try, name, max -= strlen(name));
+ strncat(try, ".", max--);
+ strncat(try, "backup", max -= 6);
+ return TRUE;
+}
+
+/* Write the contents of "str" to a file */
+void
+write_file(char *name, char *str)
+{
+ FILE *fp;
+ int len;
+
+ fp = fopen(name, "w");
+ if (!fp)
+ cleanup(0), errx(2, "cannot fopen '%s' for writing", name);
+ len = strlen(str);
+ if (fwrite(str, 1, len, fp) != len)
+ cleanup(0), errx(2, "short fwrite on '%s', tried to write %d bytes",
+ name, len);
+ if (fclose(fp))
+ cleanup(0), errx(2, "failure to fclose '%s'", name);
+}
+
+void
+copy_file(char *dir, char *fname, char *to)
+{
+ char cmd[FILENAME_MAX];
+
+ if (fname[0] == '/')
+ snprintf(cmd, FILENAME_MAX, "cp -p -r %s %s", fname, to);
+ else
+ snprintf(cmd, FILENAME_MAX, "cp -p -r %s/%s %s", dir, fname, to);
+ if (vsystem(cmd))
+ cleanup(0), errx(2, "could not perform '%s'", cmd);
+}
+
+void
+move_file(char *dir, char *fname, char *to)
+{
+ char cmd[FILENAME_MAX];
+
+ if (fname[0] == '/')
+ snprintf(cmd, FILENAME_MAX, "mv %s %s", fname, to);
+ else
+ snprintf(cmd, FILENAME_MAX, "mv %s/%s %s", dir, fname, to);
+ if (vsystem(cmd))
+ cleanup(0), errx(2, "could not perform '%s'", cmd);
+}
+
+/*
+ * Copy a hierarchy (possibly from dir) to the current directory, or
+ * if "to" is TRUE, from the current directory to a location someplace
+ * else.
+ *
+ * Though slower, using tar to copy preserves symlinks and everything
+ * without me having to write some big hairy routine to do it.
+ */
+void
+copy_hierarchy(char *dir, char *fname, Boolean to)
+{
+ char cmd[FILENAME_MAX * 3];
+
+ if (!to) {
+ /* If absolute path, use it */
+ if (*fname == '/')
+ dir = "/";
+ snprintf(cmd, FILENAME_MAX * 3, "tar cf - -C %s %s | tar xpf -",
+ dir, fname);
+ }
+ else
+ snprintf(cmd, FILENAME_MAX * 3, "tar cf - %s | tar xpf - -C %s",
+ fname, dir);
+#ifdef DEBUG
+ printf("Using '%s' to copy trees.\n", cmd);
+#endif
+ if (system(cmd))
+ cleanup(0), errx(2, "copy_file: could not perform '%s'", cmd);
+}
+
+/* Unpack a tar file */
+int
+unpack(char *pkg, char *flist)
+{
+ char args[10], suffix[80], *cp;
+
+ args[0] = '\0';
+ /*
+ * Figure out by a crude heuristic whether this or not this is probably
+ * compressed.
+ */
+ if (strcmp(pkg, "-")) {
+ cp = rindex(pkg, '.');
+ if (cp) {
+ strcpy(suffix, cp + 1);
+ if (index(suffix, 'z') || index(suffix, 'Z'))
+ strcpy(args, "-z");
+ }
+ }
+ else
+ strcpy(args, "z");
+ strcat(args, "xpf");
+ if (vsystem("tar %s %s %s", args, pkg, flist ? flist : "")) {
+ warnx("tar extract of %s failed!", pkg);
+ return 1;
+ }
+ return 0;
+}
+
+/* Using fmt, replace all instances of:
+ *
+ * %F With the parameter "name"
+ * %D With the parameter "dir"
+ * %B Return the directory part ("base") of %D/%F
+ * %f Return the filename part of %D/%F
+ *
+ * Does not check for overflow - caution!
+ *
+ */
+void
+format_cmd(char *buf, char *fmt, char *dir, char *name)
+{
+ char *cp, scratch[FILENAME_MAX * 2];
+
+ while (*fmt) {
+ if (*fmt == '%') {
+ switch (*++fmt) {
+ case 'F':
+ strcpy(buf, name);
+ buf += strlen(name);
+ break;
+
+ case 'D':
+ strcpy(buf, dir);
+ buf += strlen(dir);
+ break;
+
+ case 'B':
+ sprintf(scratch, "%s/%s", dir, name);
+ cp = &scratch[strlen(scratch) - 1];
+ while (cp != scratch && *cp != '/')
+ --cp;
+ *cp = '\0';
+ strcpy(buf, scratch);
+ buf += strlen(scratch);
+ break;
+
+ case 'f':
+ sprintf(scratch, "%s/%s", dir, name);
+ cp = &scratch[strlen(scratch) - 1];
+ while (cp != scratch && *(cp - 1) != '/')
+ --cp;
+ strcpy(buf, cp);
+ buf += strlen(cp);
+ break;
+
+ default:
+ *buf++ = *fmt;
+ break;
+ }
+ ++fmt;
+ }
+ else
+ *buf++ = *fmt++;
+ }
+ *buf = '\0';
+}
diff --git a/usr.sbin/pkg_install/lib/global.c b/usr.sbin/pkg_install/lib/global.c
new file mode 100644
index 0000000..75947b4
--- /dev/null
+++ b/usr.sbin/pkg_install/lib/global.c
@@ -0,0 +1,35 @@
+#ifndef lint
+static const char rcsid[] =
+ "$Id: global.c,v 1.5 1997/02/22 16:09:48 peter Exp $";
+#endif
+
+/*
+ * FreeBSD install - a package for the installation and maintainance
+ * of non-core utilities.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * Jordan K. Hubbard
+
+ * 18 July 1993
+ *
+ * Semi-convenient place to stick some needed globals.
+ *
+ */
+
+#include "lib.h"
+
+/* These are global for all utils */
+Boolean Verbose = FALSE;
+Boolean Fake = FALSE;
+Boolean Force = FALSE;
+int AutoAnswer = FALSE;
+
+
diff --git a/usr.sbin/pkg_install/lib/lib.h b/usr.sbin/pkg_install/lib/lib.h
new file mode 100644
index 0000000..5d16f56
--- /dev/null
+++ b/usr.sbin/pkg_install/lib/lib.h
@@ -0,0 +1,176 @@
+/* $Id: lib.h,v 1.24 1997/02/22 16:09:49 peter Exp $ */
+
+/*
+ * FreeBSD install - a package for the installation and maintainance
+ * of non-core utilities.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * Jordan K. Hubbard
+ * 18 July 1993
+ *
+ * Include and define various things wanted by the library routines.
+ *
+ */
+
+#ifndef _INST_LIB_LIB_H_
+#define _INST_LIB_LIB_H_
+
+/* Includes */
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <dirent.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/file.h>
+
+/* Macros */
+#define SUCCESS (0)
+#define FAIL (-1)
+
+#ifndef TRUE
+#define TRUE (1)
+#endif
+
+#ifndef FALSE
+#define FALSE (0)
+#endif
+
+#define YES 2
+#define NO 1
+
+/* Usually "rm", but often "echo" during debugging! */
+#define REMOVE_CMD "rm"
+
+/* Usually "rm", but often "echo" during debugging! */
+#define RMDIR_CMD "rmdir"
+
+/* Where we put logging information by default, else ${PKG_DBDIR} if set */
+#define DEF_LOG_DIR "/var/db/pkg"
+/* just in case we change the environment variable name */
+#define PKG_DBDIR "PKG_DBDIR"
+
+/* The names of our "special" files */
+#define CONTENTS_FNAME "+CONTENTS"
+#define COMMENT_FNAME "+COMMENT"
+#define DESC_FNAME "+DESC"
+#define INSTALL_FNAME "+INSTALL"
+#define DEINSTALL_FNAME "+DEINSTALL"
+#define REQUIRE_FNAME "+REQUIRE"
+#define REQUIRED_BY_FNAME "+REQUIRED_BY"
+#define DISPLAY_FNAME "+DISPLAY"
+#define MTREE_FNAME "+MTREE_DIRS"
+
+#define CMD_CHAR '@' /* prefix for extended PLIST cmd */
+
+/* The name of the "prefix" environment variable given to scripts */
+#define PKG_PREFIX_VNAME "PKG_PREFIX"
+
+enum _plist_t {
+ PLIST_FILE, PLIST_CWD, PLIST_CMD, PLIST_CHMOD,
+ PLIST_CHOWN, PLIST_CHGRP, PLIST_COMMENT, PLIST_IGNORE,
+ PLIST_NAME, PLIST_UNEXEC, PLIST_SRC, PLIST_DISPLAY,
+ PLIST_PKGDEP, PLIST_MTREE, PLIST_DIR_RM, PLIST_IGNORE_INST,
+ PLIST_OPTION
+};
+typedef enum _plist_t plist_t;
+
+/* Types */
+typedef unsigned int Boolean;
+
+struct _plist {
+ struct _plist *prev, *next;
+ char *name;
+ Boolean marked;
+ plist_t type;
+};
+typedef struct _plist *PackingList;
+
+struct _pack {
+ struct _plist *head, *tail;
+};
+typedef struct _pack Package;
+
+/* Prototypes */
+/* Misc */
+int vsystem(const char *, ...);
+void cleanup(int);
+char *make_playpen(char *, size_t);
+char *where_playpen(void);
+void leave_playpen(char *);
+off_t min_free(char *);
+
+/* String */
+char *get_dash_string(char **);
+char *copy_string(char *);
+Boolean suffix(char *, char *);
+void nuke_suffix(char *);
+void str_lowercase(char *);
+char *basename_of(char *);
+char *strconcat(char *, char *);
+
+/* File */
+Boolean fexists(char *);
+Boolean isdir(char *);
+Boolean isemptydir(char *fname);
+Boolean isemptyfile(char *fname);
+Boolean isfile(char *);
+Boolean isempty(char *);
+Boolean isURL(char *);
+char *fileGetURL(char *, char *);
+char *fileURLFilename(char *, char *, int);
+char *fileURLHost(char *, char *, int);
+char *fileFindByPath(char *, char *);
+char *fileGetContents(char *);
+void write_file(char *, char *);
+void copy_file(char *, char *, char *);
+void move_file(char *, char *, char *);
+void copy_hierarchy(char *, char *, Boolean);
+int delete_hierarchy(char *, Boolean, Boolean);
+int unpack(char *, char *);
+void format_cmd(char *, char *, char *, char *);
+
+/* Msg */
+void upchuck(const char *);
+void barf(const char *, ...);
+void whinge(const char *, ...);
+Boolean y_or_n(Boolean, const char *, ...);
+
+/* Packing list */
+PackingList new_plist_entry(void);
+PackingList last_plist(Package *);
+PackingList find_plist(Package *, plist_t);
+char *find_plist_option(Package *, char *name);
+void plist_delete(Package *, Boolean, plist_t, char *);
+void free_plist(Package *);
+void mark_plist(Package *);
+void csum_plist_entry(char *, PackingList);
+void add_plist(Package *, plist_t, char *);
+void add_plist_top(Package *, plist_t, char *);
+void delete_plist(Package *pkg, Boolean all, plist_t type, char *name);
+void write_plist(Package *, FILE *);
+void read_plist(Package *, FILE *);
+int plist_cmd(char *, char **);
+int delete_package(Boolean, Boolean, Package *);
+
+/* For all */
+int pkg_perform(char **);
+
+/* Externs */
+extern Boolean Verbose;
+extern Boolean Fake;
+extern Boolean Force;
+extern int AutoAnswer;
+
+#endif /* _INST_LIB_LIB_H_ */
diff --git a/usr.sbin/pkg_install/lib/msg.c b/usr.sbin/pkg_install/lib/msg.c
new file mode 100644
index 0000000..3c2d199
--- /dev/null
+++ b/usr.sbin/pkg_install/lib/msg.c
@@ -0,0 +1,77 @@
+#ifndef lint
+static const char rcsid[] =
+ "$Id: msg.c,v 1.9 1997/10/08 07:48:09 charnier Exp $";
+#endif
+
+/*
+ * FreeBSD install - a package for the installation and maintainance
+ * of non-core utilities.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * Jordan K. Hubbard
+
+ * 18 July 1993
+ *
+ * Miscellaneous message routines.
+ *
+ */
+
+#include <err.h>
+#include "lib.h"
+
+/* Die a relatively simple death */
+void
+upchuck(const char *err)
+{
+ warn("fatal error during execution: %s", err);
+ cleanup(0);
+}
+
+/*
+ * As a yes/no question, prompting from the varargs string and using
+ * default if user just hits return.
+ */
+Boolean
+y_or_n(Boolean def, const char *msg, ...)
+{
+ va_list args;
+ int ch = 0;
+ FILE *tty;
+
+ va_start(args, msg);
+ /*
+ * Need to open /dev/tty because file collection may have been
+ * collected on stdin
+ */
+ tty = fopen("/dev/tty", "r");
+ if (!tty) {
+ warnx("can't open /dev/tty!");
+ cleanup(0);
+ }
+ 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..36ece65
--- /dev/null
+++ b/usr.sbin/pkg_install/lib/pen.c
@@ -0,0 +1,145 @@
+#ifndef lint
+static const char rcsid[] =
+ "$Id: pen.c,v 1.24 1997/02/22 16:09:50 peter Exp $";
+#endif
+
+/*
+ * FreeBSD install - a package for the installation and maintainance
+ * of non-core utilities.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * Jordan K. Hubbard
+ * 18 July 1993
+ *
+ * Routines for managing the "play pen".
+ *
+ */
+
+#include <err.h>
+#include "lib.h"
+#include <sys/signal.h>
+#include <sys/param.h>
+#include <sys/mount.h>
+
+/* For keeping track of where we are */
+static char Current[FILENAME_MAX];
+static char Previous[FILENAME_MAX];
+
+char *
+where_playpen(void)
+{
+ return Current;
+}
+
+/* Find a good place to play. */
+static char *
+find_play_pen(char *pen, size_t sz)
+{
+ char *cp;
+ struct stat sb;
+
+ if (pen[0] && stat(pen, &sb) != FAIL && (min_free(pen) >= sz))
+ return pen;
+ else if ((cp = getenv("PKG_TMPDIR")) != NULL && stat(cp, &sb) != FAIL && (min_free(cp) >= sz))
+ sprintf(pen, "%s/instmp.XXXXXX", cp);
+ else if ((cp = getenv("TMPDIR")) != NULL && stat(cp, &sb) != FAIL && (min_free(cp) >= sz))
+ sprintf(pen, "%s/instmp.XXXXXX", cp);
+ else if (stat("/var/tmp", &sb) != FAIL && min_free("/var/tmp") >= sz)
+ strcpy(pen, "/var/tmp/instmp.XXXXXX");
+ else if (stat("/tmp", &sb) != FAIL && min_free("/tmp") >= sz)
+ strcpy(pen, "/tmp/instmp.XXXXXX");
+ else if ((stat("/usr/tmp", &sb) == SUCCESS || mkdir("/usr/tmp", 01777) == SUCCESS) && min_free("/usr/tmp") >= sz)
+ strcpy(pen, "/usr/tmp/instmp.XXXXXX");
+ else {
+ cleanup(0);
+ errx(2,
+"can't find enough temporary space to extract the files, please set your\n"
+"PKG_TMPDIR environment variable to a location with at least %d bytes\n"
+"free", sz);
+ return NULL;
+ }
+ return pen;
+}
+
+/*
+ * Make a temporary directory to play in and chdir() to it, returning
+ * pathname of previous working directory.
+ */
+char *
+make_playpen(char *pen, size_t sz)
+{
+ if (!find_play_pen(pen, sz))
+ return NULL;
+
+ if (!mktemp(pen)) {
+ cleanup(0);
+ errx(2, "can't mktemp '%s'", pen);
+ }
+ if (mkdir(pen, 0755) == FAIL) {
+ cleanup(0);
+ errx(2, "can't mkdir '%s'", pen);
+ }
+ if (Verbose) {
+ if (sz)
+ fprintf(stderr, "Requested space: %d bytes, free space: %qd bytes in %s\n", (int)sz, min_free(pen), pen);
+ }
+ if (min_free(pen) < sz) {
+ rmdir(pen);
+ cleanup(0);
+ errx(2, "not enough free space to create '%s'.\n"
+ "Please set your PKG_TMPDIR environment variable to a location\n"
+ "with more space and\ntry the command again", pen);
+ }
+ if (Current[0])
+ strcpy(Previous, Current);
+ else if (!getcwd(Previous, FILENAME_MAX)) {
+ upchuck("getcwd");
+ return NULL;
+ }
+ if (chdir(pen) == FAIL)
+ cleanup(0), errx(2, "can't chdir to '%s'", pen);
+ strcpy(Current, pen);
+ return Previous;
+}
+
+/* Convenience routine for getting out of playpen */
+void
+leave_playpen(char *save)
+{
+ void (*oldsig)(int);
+
+ /* Don't interrupt while we're cleaning up */
+ oldsig = signal(SIGINT, SIG_IGN);
+ if (Previous[0] && chdir(Previous) == FAIL)
+ cleanup(0), errx(2, "can't chdir back to '%s'", Previous);
+ else if (Current[0] && strcmp(Current, Previous)) {
+ if (vsystem("rm -rf %s", Current))
+ warnx("couldn't remove temporary dir '%s'", Current);
+ strcpy(Current, Previous);
+ }
+ if (save)
+ strcpy(Previous, save);
+ else
+ Previous[0] = '\0';
+ signal(SIGINT, oldsig);
+}
+
+off_t
+min_free(char *tmpdir)
+{
+ struct statfs buf;
+
+ if (statfs(tmpdir, &buf) != 0) {
+ warn("statfs");
+ return -1;
+ }
+ return (off_t)buf.f_bavail * (off_t)buf.f_bsize;
+}
diff --git a/usr.sbin/pkg_install/lib/plist.c b/usr.sbin/pkg_install/lib/plist.c
new file mode 100644
index 0000000..2caf31a
--- /dev/null
+++ b/usr.sbin/pkg_install/lib/plist.c
@@ -0,0 +1,505 @@
+#ifndef lint
+static const char rcsid[] =
+ "$Id: plist.c,v 1.23 1997/09/02 08:48:47 jkh Exp $";
+#endif
+
+/*
+ * FreeBSD install - a package for the installation and maintainance
+ * of non-core utilities.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * Jordan K. Hubbard
+ * 18 July 1993
+ *
+ * General packing list routines.
+ *
+ */
+
+#include "lib.h"
+#include <err.h>
+#include <md5.h>
+
+/* Add an item to a packing list */
+void
+add_plist(Package *p, plist_t type, char *arg)
+{
+ PackingList tmp;
+
+ tmp = new_plist_entry();
+ tmp->name = copy_string(arg);
+ tmp->type = type;
+
+ if (!p->head)
+ p->head = p->tail = tmp;
+ else {
+ tmp->prev = p->tail;
+ p->tail->next = tmp;
+ p->tail = tmp;
+ }
+}
+
+void
+add_plist_top(Package *p, plist_t type, char *arg)
+{
+ PackingList tmp;
+
+ tmp = new_plist_entry();
+ tmp->name = copy_string(arg);
+ tmp->type = type;
+
+ if (!p->head)
+ p->head = p->tail = tmp;
+ else {
+ tmp->next = p->head;
+ p->head->prev = tmp;
+ p->head = tmp;
+ }
+}
+
+/* Return the last (most recent) entry in a packing list */
+PackingList
+last_plist(Package *p)
+{
+ return p->tail;
+}
+
+/* Mark all items in a packing list to prevent iteration over them */
+void
+mark_plist(Package *pkg)
+{
+ PackingList p = pkg->head;
+
+ while (p) {
+ p->marked = TRUE;
+ p = p->next;
+ }
+}
+
+/* Find a given item in a packing list and, if so, return it (else NULL) */
+PackingList
+find_plist(Package *pkg, plist_t type)
+{
+ PackingList p = pkg->head;
+
+ while (p) {
+ if (p->type == type)
+ return p;
+ p = p->next;
+ }
+ return NULL;
+}
+
+/* Look for a specific boolean option argument in the list */
+char *
+find_plist_option(Package *pkg, char *name)
+{
+ PackingList p = pkg->head;
+
+ while (p) {
+ if (p->type == PLIST_OPTION && !strcmp(p->name, name))
+ return p->name;
+ p = p->next;
+ }
+ return NULL;
+}
+
+/*
+ * Delete plist item 'type' in the list (if 'name' is non-null, match it
+ * too.) If 'all' is set, delete all items, not just the first occurance.
+ */
+void
+delete_plist(Package *pkg, Boolean all, plist_t type, char *name)
+{
+ PackingList p = pkg->head;
+
+ while (p) {
+ PackingList pnext = p->next;
+
+ if (p->type == type && (!name || !strcmp(name, p->name))) {
+ free(p->name);
+ if (p->prev)
+ p->prev->next = pnext;
+ else
+ pkg->head = pnext;
+ if (pnext)
+ pnext->prev = p->prev;
+ else
+ pkg->tail = p->prev;
+ free(p);
+ if (!all)
+ return;
+ p = pnext;
+ }
+ else
+ p = p->next;
+ }
+}
+
+/* Allocate a new packing list entry */
+PackingList
+new_plist_entry(void)
+{
+ PackingList ret;
+
+ ret = (PackingList)malloc(sizeof(struct _plist));
+ bzero(ret, sizeof(struct _plist));
+ return ret;
+}
+
+/* Free an entire packing list */
+void
+free_plist(Package *pkg)
+{
+ PackingList p = pkg->head;
+
+ while (p) {
+ PackingList p1 = p->next;
+
+ free(p->name);
+ free(p);
+ p = p1;
+ }
+ pkg->head = pkg->tail = NULL;
+}
+
+/*
+ * For an ascii string denoting a plist command, return its code and
+ * optionally its argument(s)
+ */
+int
+plist_cmd(char *s, char **arg)
+{
+ char cmd[FILENAME_MAX + 20]; /* 20 == fudge for max cmd len */
+ char *cp, *sp;
+
+ strcpy(cmd, s);
+ str_lowercase(cmd);
+ cp = cmd;
+ sp = s;
+ while (*cp) {
+ if (isspace(*cp)) {
+ *cp = '\0';
+ while (isspace(*sp)) /* Never sure if macro, increment later */
+ ++sp;
+ break;
+ }
+ ++cp, ++sp;
+ }
+ if (arg)
+ *arg = sp;
+ if (!strcmp(cmd, "cwd"))
+ return PLIST_CWD;
+ else if (!strcmp(cmd, "srcdir"))
+ return PLIST_SRC;
+ else if (!strcmp(cmd, "cd"))
+ return PLIST_CWD;
+ else if (!strcmp(cmd, "exec"))
+ return PLIST_CMD;
+ else if (!strcmp(cmd, "unexec"))
+ return PLIST_UNEXEC;
+ else if (!strcmp(cmd, "mode"))
+ return PLIST_CHMOD;
+ else if (!strcmp(cmd, "owner"))
+ return PLIST_CHOWN;
+ else if (!strcmp(cmd, "group"))
+ return PLIST_CHGRP;
+ else if (!strcmp(cmd, "comment"))
+ return PLIST_COMMENT;
+ else if (!strcmp(cmd, "ignore"))
+ return PLIST_IGNORE;
+ else if (!strcmp(cmd, "ignore_inst"))
+ return PLIST_IGNORE_INST;
+ else if (!strcmp(cmd, "name"))
+ return PLIST_NAME;
+ else if (!strcmp(cmd, "display"))
+ return PLIST_DISPLAY;
+ else if (!strcmp(cmd, "pkgdep"))
+ return PLIST_PKGDEP;
+ else if (!strcmp(cmd, "mtree"))
+ return PLIST_MTREE;
+ else if (!strcmp(cmd, "dirrm"))
+ return PLIST_DIR_RM;
+ else if (!strcmp(cmd, "option"))
+ return PLIST_OPTION;
+ else
+ return FAIL;
+}
+
+/* Read a packing list from a file */
+void
+read_plist(Package *pkg, FILE *fp)
+{
+ char *cp, pline[FILENAME_MAX];
+ int cmd;
+
+ while (fgets(pline, FILENAME_MAX, fp)) {
+ int len = strlen(pline);
+
+ while (len && isspace(pline[len - 1]))
+ pline[--len] = '\0';
+ if (!len)
+ continue;
+ cp = pline;
+ if (pline[0] == CMD_CHAR) {
+ cmd = plist_cmd(pline + 1, &cp);
+ if (cmd == FAIL)
+ cleanup(0), errx(2, "bad command '%s'", pline);
+ if (*cp == '\0')
+ cp = NULL;
+ }
+ else
+ cmd = PLIST_FILE;
+ add_plist(pkg, cmd, cp);
+ }
+}
+
+/* Write a packing list to a file, converting commands to ascii equivs */
+void
+write_plist(Package *pkg, FILE *fp)
+{
+ PackingList plist = pkg->head;
+
+ while (plist) {
+ switch(plist->type) {
+ case PLIST_FILE:
+ fprintf(fp, "%s\n", plist->name);
+ break;
+
+ case PLIST_CWD:
+ fprintf(fp, "%ccwd %s\n", CMD_CHAR, plist->name);
+ break;
+
+ case PLIST_SRC:
+ fprintf(fp, "%csrcdir %s\n", CMD_CHAR, plist->name);
+ break;
+
+ case PLIST_CMD:
+ fprintf(fp, "%cexec %s\n", CMD_CHAR, plist->name);
+ break;
+
+ case PLIST_UNEXEC:
+ fprintf(fp, "%cunexec %s\n", CMD_CHAR, plist->name);
+ break;
+
+ case PLIST_CHMOD:
+ fprintf(fp, "%cmode %s\n", CMD_CHAR, plist->name ? plist->name : "");
+ break;
+
+ case PLIST_CHOWN:
+ fprintf(fp, "%cowner %s\n", CMD_CHAR, plist->name ? plist->name : "");
+ break;
+
+ case PLIST_CHGRP:
+ fprintf(fp, "%cgroup %s\n", CMD_CHAR, plist->name ? plist->name : "");
+ break;
+
+ case PLIST_COMMENT:
+ fprintf(fp, "%ccomment %s\n", CMD_CHAR, plist->name);
+ break;
+
+ case PLIST_IGNORE:
+ case PLIST_IGNORE_INST: /* a one-time non-ignored file */
+ fprintf(fp, "%cignore\n", CMD_CHAR);
+ break;
+
+ case PLIST_NAME:
+ fprintf(fp, "%cname %s\n", CMD_CHAR, plist->name);
+ break;
+
+ case PLIST_DISPLAY:
+ fprintf(fp, "%cdisplay %s\n", CMD_CHAR, plist->name);
+ break;
+
+ case PLIST_PKGDEP:
+ fprintf(fp, "%cpkgdep %s\n", CMD_CHAR, plist->name);
+ break;
+
+ case PLIST_MTREE:
+ fprintf(fp, "%cmtree %s\n", CMD_CHAR, plist->name);
+ break;
+
+ case PLIST_DIR_RM:
+ fprintf(fp, "%cdirrm %s\n", CMD_CHAR, plist->name);
+ break;
+
+ case PLIST_OPTION:
+ fprintf(fp, "%coption %s\n", CMD_CHAR, plist->name);
+ break;
+
+ default:
+ cleanup(0);
+ errx(2, "unknown command type %d (%s)", plist->type, plist->name);
+ break;
+ }
+ plist = plist->next;
+ }
+}
+
+/*
+ * Delete the results of a package installation.
+ *
+ * This is here rather than in the pkg_delete code because pkg_add needs to
+ * run it too in cases of failure.
+ */
+int
+delete_package(Boolean ign_err, Boolean nukedirs, Package *pkg)
+{
+ PackingList p;
+ char *Where = ".", *last_file = "";
+ Boolean fail = SUCCESS;
+ Boolean preserve;
+ char tmp[FILENAME_MAX], *name = NULL;
+
+ preserve = find_plist_option(pkg, "preserve") ? TRUE : FALSE;
+ for (p = pkg->head; p; p = p->next) {
+ switch (p->type) {
+ case PLIST_NAME:
+ name = p->name;
+ break;
+
+ case PLIST_IGNORE:
+ p = p->next;
+ break;
+
+ case PLIST_CWD:
+ Where = p->name;
+ if (Verbose)
+ printf("Change working directory to %s\n", Where);
+ break;
+
+ case PLIST_UNEXEC:
+ format_cmd(tmp, p->name, Where, last_file);
+ if (Verbose)
+ printf("Execute `%s'\n", tmp);
+ if (!Fake && system(tmp)) {
+ warnx("unexec command for `%s' failed", tmp);
+ fail = FAIL;
+ }
+ break;
+
+ case PLIST_FILE:
+ last_file = p->name;
+ sprintf(tmp, "%s/%s", Where, p->name);
+ if (isdir(tmp)) {
+ warnx("attempting to delete directory `%s' as a file\n"
+ "this packing list is incorrect - ignoring delete request", tmp);
+ }
+ else {
+ if (p->next && p->next->type == PLIST_COMMENT && !strncmp(p->next->name, "MD5:", 4)) {
+ char *cp, buf[33];
+
+ if ((cp = MD5File(tmp, buf)) != NULL) {
+ /* Mismatch? */
+ if (strcmp(cp, p->next->name + 4)) {
+ if (Verbose)
+ printf("%s fails original MD5 checksum - %s\n",
+ tmp, Force ? "deleted anyway." : "not deleted.");
+ if (!Force) {
+ fail = FAIL;
+ continue;
+ }
+ }
+ }
+ }
+ if (Verbose)
+ printf("Delete file %s\n", tmp);
+ if (!Fake) {
+ if (delete_hierarchy(tmp, ign_err, nukedirs))
+ fail = FAIL;
+ if (preserve && name) {
+ char tmp2[FILENAME_MAX];
+
+ if (make_preserve_name(tmp2, FILENAME_MAX, name, tmp)) {
+ if (fexists(tmp2)) {
+ if (rename(tmp2, tmp))
+ warn("preserve: unable to restore %s as %s",
+ tmp2, tmp);
+ }
+ }
+ }
+ }
+ }
+ break;
+
+ case PLIST_DIR_RM:
+ sprintf(tmp, "%s/%s", Where, p->name);
+ if (!isdir(tmp)) {
+ warnx("attempting to delete file `%s' as a directory\n"
+ "this packing list is incorrect - ignoring delete request", tmp);
+ }
+ else {
+ if (Verbose)
+ printf("Delete directory %s\n", tmp);
+ if (!Fake && delete_hierarchy(tmp, ign_err, FALSE)) {
+ warnx("unable to completely remove directory '%s'", tmp);
+ fail = FAIL;
+ }
+ }
+ last_file = p->name;
+ break;
+ }
+ }
+ return fail;
+}
+
+#ifdef DEBUG
+#define RMDIR(dir) vsystem("%s %s", RMDIR_CMD, dir)
+#define REMOVE(dir,ie) vsystem("%s %s%s", REMOVE_CMD, (ie ? "-f " : ""), dir)
+#else
+#define RMDIR rmdir
+#define REMOVE(file,ie) (remove(file) && !(ie))
+#endif
+
+/* Selectively delete a hierarchy */
+int
+delete_hierarchy(char *dir, Boolean ign_err, Boolean nukedirs)
+{
+ char *cp1, *cp2;
+
+ cp1 = cp2 = dir;
+ if (!fexists(dir)) {
+ if (!ign_err)
+ warnx("%s `%s' doesn't really exist",
+ isdir(dir) ? "directory" : "file", dir);
+ return !ign_err;
+ }
+ else if (nukedirs) {
+ if (vsystem("%s -r%s %s", REMOVE_CMD, (ign_err ? "f" : ""), dir))
+ return 1;
+ }
+ else if (isdir(dir)) {
+ if (RMDIR(dir) && !ign_err)
+ return 1;
+ }
+ else {
+ if (REMOVE(dir, ign_err))
+ return 1;
+ }
+
+ if (!nukedirs)
+ return 0;
+ while (cp2) {
+ if ((cp2 = rindex(cp1, '/')) != NULL)
+ *cp2 = '\0';
+ if (!isemptydir(dir))
+ return 0;
+ if (RMDIR(dir) && !ign_err)
+ if (!fexists(dir))
+ warnx("directory `%s' doesn't really exist", dir);
+ else
+ return 1;
+ /* back up the pathname one component */
+ if (cp2) {
+ cp1 = dir;
+ }
+ }
+ return 0;
+}
diff --git a/usr.sbin/pkg_install/lib/str.c b/usr.sbin/pkg_install/lib/str.c
new file mode 100644
index 0000000..cb4fb41
--- /dev/null
+++ b/usr.sbin/pkg_install/lib/str.c
@@ -0,0 +1,111 @@
+#ifndef lint
+static const char rcsid[] =
+ "$Id$";
+#endif
+
+/*
+ * FreeBSD install - a package for the installation and maintainance
+ * of non-core utilities.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * Jordan K. Hubbard
+ * 18 July 1993
+ *
+ * Miscellaneous string utilities.
+ *
+ */
+
+#include "lib.h"
+
+/* Return the filename portion of a path */
+char *
+basename_of(char *str)
+{
+ char *basename = str + strlen(str) - 1;
+
+ while (basename != str && basename[-1] != '/')
+ --basename;
+ return basename;
+}
+
+char *
+strconcat(char *s1, char *s2)
+{
+ static char tmp[FILENAME_MAX];
+
+ tmp[0] = '\0';
+ strncpy(tmp, s1 ? s1 : s2, FILENAME_MAX);
+ if (s1 && s2)
+ strncat(tmp, s2, FILENAME_MAX - strlen(tmp));
+ return tmp;
+}
+
+/* Get a string parameter as a file spec or as a "contents follow -" spec */
+char *
+get_dash_string(char **str)
+{
+ char *s = *str;
+
+ if (*s == '-')
+ *str = copy_string(s + 1);
+ else
+ *str = fileGetContents(s);
+ return *str;
+}
+
+/* Rather Obvious */
+char *
+copy_string(char *str)
+{
+ char *ret;
+
+ if (!str)
+ ret = NULL;
+ else {
+ ret = (char *)malloc(strlen(str) + 1);
+ strcpy(ret, str);
+ }
+ return ret;
+}
+
+/* Return TRUE if 'str' ends in suffix 'suff' */
+Boolean
+suffix(char *str, char *suff)
+{
+ char *idx;
+ Boolean ret = FALSE;
+
+ idx = rindex(str, '.');
+ if (idx && !strcmp(idx + 1, suff))
+ ret = TRUE;
+ return ret;
+}
+
+/* Assuming str has a suffix, brutally murder it! */
+void
+nuke_suffix(char *str)
+{
+ char *idx;
+
+ idx = rindex(str, '.');
+ if (idx)
+ *idx = '\0'; /* Yow! Don't try this on a const! */
+}
+
+/* Lowercase a whole string */
+void
+str_lowercase(char *str)
+{
+ while (*str) {
+ *str = tolower(*str);
+ ++str;
+ }
+}
diff --git a/usr.sbin/pkg_install/tkpkg b/usr.sbin/pkg_install/tkpkg
new file mode 100755
index 0000000..7730cd7
--- /dev/null
+++ b/usr.sbin/pkg_install/tkpkg
@@ -0,0 +1,186 @@
+#!/usr/local/bin/wish -f
+#$Id$
+#
+#$Log: tkpkg,v $
+#Revision 1.3 1997/01/14 07:14:23 jkh
+#Make the long-awaited change from $Id$ to $Id$
+#
+#This will make a number of things easier in the future, as well as (finally!)
+#avoiding the Id-smashing problem which has plagued developers for so long.
+#
+#Boy, I'm glad we're not using sup anymore. This update would have been
+#insane otherwise.
+#
+#Revision 1.2 1994/12/06 00:51:21 jkh
+#Many of John T. Kohl's patches from NetBSD. Thanks, John!
+#Submitted by: jkohl
+#
+# Revision 1.1 1994/01/06 08:16:20 jkh
+# Cleaning house.
+#
+# Revision 1.1 1993/09/04 17:06:09 jkh
+# Added Rich's wish front-end.
+#
+# Revision 1.6 1993/09/03 23:37:22 rich
+# warn user if no tar archives are found in the current directory.
+# removed the revision string from the lower text frame.
+#
+# Revision 1.5 1993/09/03 15:48:04 rich
+# glob for .tar.gz, .tar.z and .tar.Z looking for archives
+#
+# Revision 1.4 1993/08/28 15:53:59 rich
+# added version and date info to lower text window.
+#
+# Revision 1.3 1993/08/28 15:47:12 rich
+# filtered out ^Ls in pkg_* output.
+#
+#
+set pkgname ""
+wm title . "Package Installation"
+#--------------------------------------------------------------
+# The top level main window, consisting of a bar of buttons and a list
+# of packages and a description of the current package.
+#--------------------------------------------------------------
+frame .menu -relief raised -borderwidth 1
+frame .frame -borderwidth 4
+
+scrollbar .frame.scroll -relief sunken -command ".frame.list yview"
+listbox .frame.list -yscroll ".frame.scroll set" -relief sunken -setgrid 1
+pack append .frame .frame.scroll {right filly} \
+ .frame.list {left expand fill}
+
+# build the lower window shoing the complete description of a pacage
+frame .f -borderwidth 4
+text .f.t -width 80 -height 20 -yscrollcommand ".f.s set" -relief sunken
+
+# Initially display instructions in this window. Erase the
+# instructions and show the package description when the user clicks
+# on a package.
+#
+.f.t insert end "Double click on a package above to see its
+complete description here."
+scrollbar .f.s -relief sunken -command ".f.t yview"
+pack append .f .f.s {right filly} .f.t {left expand fill}
+
+bind .frame.list <Double-Button-1> \
+ {foreach i [selection get] {do_description $i}}
+pack append . .menu {top fill} \
+ .f {bottom expand fill} \
+ .frame {bottom expand fill}
+
+#----------------------------------------------------------------
+# Make menu bar:
+#----------------------------------------------------------------
+button .menu.inst -text "Install" \
+ -command "apply_to_pkg \"pkg_add -v\""
+button .menu.dein -text "Deinstall" \
+ -command "apply_to_pkg \"pkg_delete -v\""
+button .menu.installed -text "What is Installed?" \
+ -command "list_pkgs \"pkg_info -I -a |tr ' ' ' '\""
+button .menu.available -text "What can I install?" \
+ -command "list_pkgs \"pkg_info -I -c [glob -nocomplain *.{tgz,tar.z,tar.gz,tar.Z}] |tr ' ' ' '\""
+button .menu.cont -text "Contents?" \
+ -command "apply_to_pkg \"pkg_info -d -v\""
+button .menu.quit -text "Quit" -command "destroy ."
+button .menu.help -text "Help" -command "do_help"
+
+pack append .menu \
+ .menu.inst left \
+ .menu.dein left \
+ .menu.installed left \
+ .menu.available left \
+ .menu.cont left \
+ .menu.quit left \
+ .menu.help right
+#-------------------------------------------------------
+# Display the package description.
+#-------------------------------------------------------
+proc list_pkgs {s} {
+ set line ""
+ set f [eval "open {| sh -c \"$s\" } r"]
+ .frame.list delete 0 end
+ while {[gets $f line] > 0} {
+ .frame.list insert end $line
+ }
+ close $f
+}
+
+# display the list of available packages
+set archives [glob -nocomplain *.{tgz,tar.z,tar.gz,tar.Z}]
+if {$archives == ""} {
+ .frame.list delete 0 end
+ .frame.list insert end "Warning: no compressed tar archives files found."
+} else {
+ list_pkgs "pkg_info -I -c $archives |tr ' ' ' '"
+}
+
+#-------------------------------------------------------
+# Display the package description.
+#-------------------------------------------------------
+proc do_description {s} {
+ global pkgname
+ regexp {[^ ]*} $s filename
+ set pkgname $filename
+ .f.t delete 0.0 end
+ set cmd "pkg_info -d $filename |tr -d ' '"
+ set f [eval "open {| csh -c \"$cmd\" } r"]
+ while {![eof $f]} {
+ .f.t insert end [read $f]
+ }
+}
+#-------------------------------------------------------
+# package install window.
+#-------------------------------------------------------
+proc do_help {{w .help}} {
+ catch {destroy $w}
+ toplevel $w
+ wm title $w "Help"
+ wm iconname $w "Help"
+ button $w.ok -text OK -command "destroy $w"
+ message $w.t -relief raised -bd 2 \
+ -text "You can install, deinstall and list info on the available packages. To select a package and see its complete description, press mouse button 1 over the package name. To install a selected package, press the Install button. To exit, press the \"Quit\" button."
+ pack append $w $w.ok {bottom fillx} $w.t {expand fill}
+}
+#-------------------------------------------------------
+# Apply a command to a package.
+#-------------------------------------------------------
+proc apply_to_pkg {s} {
+ apply_to_pkg_err $s ""
+}
+#-------------------------------------------------------
+# Apply a command to a package, with error stream redirection instructions.
+#-------------------------------------------------------
+proc apply_to_pkg_err {s errredir} {
+ global pkgname
+ .f.t delete 0.0 end
+ if {$pkgname == ""} {
+ .f.t insert end "You must double click on a package name first!"
+ } else {
+ apply_to_pkg_int "$s $pkgname" "2>&1"
+ }
+}
+proc apply_to_pkg_int {s errredir} {
+ .f.t delete 0.0 end
+ .f.t insert end "Running: $s\n"
+ set f [eval "open {| sh -c \"$s $errredir\" } r"]
+ while {![eof $f]} {
+ .f.t insert end [read $f 64]
+ }
+}
+#-------------------------------------------------------
+# Invoke an arbitrary command.
+#-------------------------------------------------------
+proc do_command {s} {
+ .f.t delete 0.0 end
+ .f.t insert end "Running: $s\n"
+ set f [eval "open {| $s} r"]
+ while {![eof $f]} {
+ .f.t insert end [read $f 64]
+ }
+}
+# local variables:
+# mode: csh
+# compile-command: ""
+# comment-start: "# "
+# comment-start-skip: "# "
+# end:
diff --git a/usr.sbin/pnpinfo/Makefile b/usr.sbin/pnpinfo/Makefile
new file mode 100644
index 0000000..ca50830
--- /dev/null
+++ b/usr.sbin/pnpinfo/Makefile
@@ -0,0 +1,11 @@
+# $Id$
+
+PROG = pnpinfo
+
+SRCS = pnpinfo.c
+CFLAGS = -I${.CURDIR}/../../sys
+MAN8 = pnpinfo.8
+
+.PATH: ${.CURDIR}/../../contrib/pnpinfo
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/portmap/Makefile b/usr.sbin/portmap/Makefile
new file mode 100644
index 0000000..1fe7220
--- /dev/null
+++ b/usr.sbin/portmap/Makefile
@@ -0,0 +1,11 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= portmap
+MAN8= portmap.8
+SRCS= portmap.c from_local.c pmap_check.c
+SUBDIR= pmap_set pmap_dump
+
+# -DHOSTS_ACCESS (requires tcpwrapper libraries)
+CFLAGS+=-DCHECK_PORT
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/portmap/from_local.c b/usr.sbin/portmap/from_local.c
new file mode 100644
index 0000000..a1bfa72
--- /dev/null
+++ b/usr.sbin/portmap/from_local.c
@@ -0,0 +1,157 @@
+ /*
+ * Check if an address belongs to the local system. Adapted from:
+ *
+ * @(#)pmap_svc.c 1.32 91/03/11 Copyright 1984,1990 Sun Microsystems, Inc.
+ * @(#)get_myaddress.c 2.1 88/07/29 4.0 RPCSRC.
+ */
+
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user or with the express written consent of
+ * Sun Microsystems, Inc.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#) from_local.c 1.2 93/11/16 21:50:02";
+#endif
+static const char rcsid[] =
+ "$Id$";
+#endif
+
+#ifdef TEST
+#undef perror
+#endif
+
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+
+#include <netdb.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include <net/if.h>
+#include <netinet/in.h>
+
+#ifndef TRUE
+#define TRUE 1
+#define FALSE 0
+#endif
+
+/* How many interfaces could there be on a computer? */
+
+#define MAX_LOCAL 256 /* overkill */
+static int num_local = -1;
+static struct in_addr addrs[MAX_LOCAL];
+
+/* find_local - find all IP addresses for this host */
+
+int
+find_local()
+{
+ struct ifconf ifc;
+ struct ifreq ifreq;
+ struct ifreq *ifr;
+ struct ifreq *the_end;
+ int sock;
+ char buf[MAX_LOCAL * sizeof(struct ifreq)];
+
+ /* Get list of network interfaces. */
+
+ if ((sock = socket(PF_INET, SOCK_DGRAM, 0)) < 0) {
+ perror("socket");
+ return (0);
+ }
+ ifc.ifc_len = sizeof(buf);
+ ifc.ifc_buf = buf;
+ if (ioctl(sock, SIOCGIFCONF, (char *) &ifc) < 0) {
+ perror("SIOCGIFCONF");
+ (void) close(sock);
+ return (0);
+ }
+ /* Get IP address of each active IP network interface. */
+
+ the_end = (struct ifreq *) (ifc.ifc_buf + ifc.ifc_len);
+ num_local = 0;
+ for (ifr = ifc.ifc_req; ifr < the_end; ifr++) {
+ if (ifr->ifr_addr.sa_family == AF_INET) { /* IP net interface */
+ ifreq = *ifr;
+ if (ioctl(sock, SIOCGIFFLAGS, (char *) &ifreq) < 0) {
+ perror("SIOCGIFFLAGS");
+ } else if (ifreq.ifr_flags & IFF_UP) { /* active interface */
+ if (ioctl(sock, SIOCGIFADDR, (char *) &ifreq) < 0) {
+ perror("SIOCGIFADDR");
+ } else {
+ addrs[num_local++] = ((struct sockaddr_in *)
+ & ifreq.ifr_addr)->sin_addr;
+ }
+ }
+ }
+ if (num_local >= MAX_LOCAL)
+ break;
+ /* Support for variable-length addresses. */
+ ifr = (struct ifreq *) ((caddr_t) ifr
+ + ifr->ifr_addr.sa_len - sizeof(struct sockaddr));
+ }
+ (void) close(sock);
+ return (num_local);
+}
+
+/* from_local - determine whether request comes from the local system */
+
+int
+from_local(addr)
+struct sockaddr_in *addr;
+{
+ int i;
+
+ if (num_local == -1 && find_local() == 0)
+ syslog(LOG_ERR, "cannot find any active local network interfaces");
+
+ for (i = 0; i < num_local; i++) {
+ if (memcmp((char *) &(addr->sin_addr), (char *) &(addrs[i]),
+ sizeof(struct in_addr)) == 0)
+ return (TRUE);
+ }
+ return (FALSE);
+}
+
+#ifdef TEST
+
+main()
+{
+ char *inet_ntoa();
+ int i;
+
+ find_local();
+ for (i = 0; i < num_local; i++)
+ printf("%s\n", inet_ntoa(addrs[i]));
+}
+
+#endif
diff --git a/usr.sbin/portmap/pmap_check.c b/usr.sbin/portmap/pmap_check.c
new file mode 100644
index 0000000..c5aef7c
--- /dev/null
+++ b/usr.sbin/portmap/pmap_check.c
@@ -0,0 +1,264 @@
+ /*
+ * pmap_check - additional portmap security.
+ *
+ * Always reject non-local requests to update the portmapper tables.
+ *
+ * Refuse to forward mount requests to the nfs mount daemon. Otherwise, the
+ * requests would appear to come from the local system, and nfs export
+ * restrictions could be bypassed.
+ *
+ * Refuse to forward requests to the nfsd process.
+ *
+ * Refuse to forward requests to NIS (YP) daemons; The only exception is the
+ * YPPROC_DOMAIN_NONACK broadcast rpc call that is used to establish initial
+ * contact with the NIS server.
+ *
+ * Always allocate an unprivileged port when forwarding a request.
+ *
+ * If compiled with -DCHECK_PORT, require that requests to register or
+ * unregister a privileged port come from a privileged port. This makes it
+ * more difficult to replace a critical service by a trojan.
+ *
+ * If compiled with -DHOSTS_ACCESS, reject requests from hosts that are not
+ * authorized by the /etc/hosts.{allow,deny} files. The local system is
+ * always treated as an authorized host. The access control tables are never
+ * consulted for requests from the local system, and are always consulted
+ * for requests from other hosts. Access control is based on IP addresses
+ * only; attempts to map an address to a host name might cause the
+ * portmapper to hang.
+ *
+ * Author: Wietse Venema (wietse@wzv.win.tue.nl), dept. of Mathematics and
+ * Computing Science, Eindhoven University of Technology, The Netherlands.
+ */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#) pmap_check.c 1.6 93/11/21 20:58:59";
+#endif
+static const char rcsid[] =
+ "$Id$";
+#endif
+
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <rpc/rpc.h>
+#include <rpc/pmap_prot.h>
+#include <syslog.h>
+#include <netdb.h>
+#include <sys/signal.h>
+
+#include "pmap_check.h"
+
+/* Explicit #defines in case the include files are not available. */
+
+#define NFSPROG ((u_long) 100003)
+#define MOUNTPROG ((u_long) 100005)
+#define YPXPROG ((u_long) 100069)
+#define YPPROG ((u_long) 100004)
+#define YPPROC_DOMAIN_NONACK ((u_long) 2)
+#define MOUNTPROC_MNT ((u_long) 1)
+
+static void logit();
+static void toggle_verboselog();
+int verboselog = 0;
+int allow_severity = LOG_INFO;
+int deny_severity = LOG_WARNING;
+
+/* A handful of macros for "readability". */
+
+#define good_client(a) hosts_ctl("portmap", "", inet_ntoa(a->sin_addr), "")
+
+#define legal_port(a,p) \
+ (ntohs((a)->sin_port) < IPPORT_RESERVED || (p) >= IPPORT_RESERVED)
+
+#define log_bad_port(addr, proc, prog) \
+ logit(deny_severity, addr, proc, prog, ": request from unprivileged port")
+
+#define log_bad_host(addr, proc, prog) \
+ logit(deny_severity, addr, proc, prog, ": request from unauthorized host")
+
+#define log_bad_owner(addr, proc, prog) \
+ logit(deny_severity, addr, proc, prog, ": request from non-local host")
+
+#define log_no_forward(addr, proc, prog) \
+ logit(deny_severity, addr, proc, prog, ": request not forwarded")
+
+#define log_client(addr, proc, prog) \
+ logit(allow_severity, addr, proc, prog, "")
+
+/* check_startup - additional startup code */
+
+void check_startup()
+{
+
+ /*
+ * Give up root privileges so that we can never allocate a privileged
+ * port when forwarding an rpc request.
+ */
+ if (setuid(1) == -1) {
+ syslog(LOG_ERR, "setuid(1) failed: %m");
+ exit(1);
+ }
+ (void) signal(SIGINT, toggle_verboselog);
+}
+
+/* check_default - additional checks for NULL, DUMP, GETPORT and unknown */
+
+int
+check_default(addr, proc, prog)
+struct sockaddr_in *addr;
+u_long proc;
+u_long prog;
+{
+#ifdef HOSTS_ACCESS
+ if (!(from_local(addr) || good_client(addr))) {
+ log_bad_host(addr, proc, prog);
+ return (FALSE);
+ }
+#endif
+ if (verboselog)
+ log_client(addr, proc, prog);
+ return (TRUE);
+}
+
+/* check_privileged_port - additional checks for privileged-port updates */
+
+int
+check_privileged_port(addr, proc, prog, port)
+struct sockaddr_in *addr;
+u_long proc;
+u_long prog;
+u_long port;
+{
+#ifdef CHECK_PORT
+ if (!legal_port(addr, port)) {
+ log_bad_port(addr, proc, prog);
+ return (FALSE);
+ }
+#endif
+ return (TRUE);
+}
+
+/* check_setunset - additional checks for update requests */
+
+int
+check_setunset(addr, proc, prog, port)
+struct sockaddr_in *addr;
+u_long proc;
+u_long prog;
+u_long port;
+{
+ if (!from_local(addr)) {
+#ifdef HOSTS_ACCESS
+ (void) good_client(addr); /* because of side effects */
+#endif
+ log_bad_owner(addr, proc, prog);
+ return (FALSE);
+ }
+ if (port && !check_privileged_port(addr, proc, prog, port))
+ return (FALSE);
+ if (verboselog)
+ log_client(addr, proc, prog);
+ return (TRUE);
+}
+
+/* check_callit - additional checks for forwarded requests */
+
+int
+check_callit(addr, proc, prog, aproc)
+struct sockaddr_in *addr;
+u_long proc;
+u_long prog;
+u_long aproc;
+{
+#ifdef HOSTS_ACCESS
+ if (!(from_local(addr) || good_client(addr))) {
+ log_bad_host(addr, proc, prog);
+ return (FALSE);
+ }
+#endif
+ if (prog == PMAPPROG || prog == NFSPROG || prog == YPXPROG ||
+ (prog == MOUNTPROG && aproc == MOUNTPROC_MNT) ||
+ (prog == YPPROG && aproc != YPPROC_DOMAIN_NONACK)) {
+ log_no_forward(addr, proc, prog);
+ return (FALSE);
+ }
+ if (verboselog)
+ log_client(addr, proc, prog);
+ return (TRUE);
+}
+
+/* toggle_verboselog - toggle verbose logging flag */
+
+static void toggle_verboselog(sig)
+int sig;
+{
+ (void) signal(sig, toggle_verboselog);
+ verboselog = !verboselog;
+}
+
+/* logit - report events of interest via the syslog daemon */
+
+static void logit(severity, addr, procnum, prognum, text)
+int severity;
+struct sockaddr_in *addr;
+u_long procnum;
+u_long prognum;
+char *text;
+{
+ char *procname;
+ char procbuf[4 * sizeof(u_long)];
+ char *progname;
+ char progbuf[4 * sizeof(u_long)];
+ struct rpcent *rpc;
+ struct proc_map {
+ u_long code;
+ char *proc;
+ };
+ struct proc_map *procp;
+ static struct proc_map procmap[] = {
+ {PMAPPROC_CALLIT, "callit"},
+ {PMAPPROC_DUMP, "dump"},
+ {PMAPPROC_GETPORT, "getport"},
+ {PMAPPROC_NULL, "null"},
+ {PMAPPROC_SET, "set"},
+ {PMAPPROC_UNSET, "unset"},
+ {0, 0},
+ };
+
+ /*
+ * Fork off a process or the portmap daemon might hang while
+ * getrpcbynumber() or syslog() does its thing.
+ */
+
+ if (fork() == 0) {
+
+ /* Try to map program number to name. */
+
+ if (prognum == 0) {
+ progname = "";
+ } else if ((rpc = getrpcbynumber((int) prognum))) {
+ progname = rpc->r_name;
+ } else {
+ sprintf(progname = progbuf, "%lu", prognum);
+ }
+
+ /* Try to map procedure number to name. */
+
+ for (procp = procmap; procp->proc && procp->code != procnum; procp++)
+ /* void */ ;
+ if ((procname = procp->proc) == 0)
+ sprintf(procname = procbuf, "%lu", (u_long) procnum);
+
+ /* Write syslog record. */
+
+ syslog(severity, "connect from %s to %s(%s)%s",
+ inet_ntoa(addr->sin_addr), procname, progname, text);
+ exit(0);
+ }
+}
diff --git a/usr.sbin/portmap/pmap_check.h b/usr.sbin/portmap/pmap_check.h
new file mode 100644
index 0000000..2c240df
--- /dev/null
+++ b/usr.sbin/portmap/pmap_check.h
@@ -0,0 +1,11 @@
+/* @(#) pmap_check.h 1.3 93/11/21 16:18:53 */
+
+extern int from_local();
+extern void check_startup();
+extern int check_default();
+extern int check_setunset();
+extern int check_privileged_port();
+extern int check_callit();
+extern int verboselog;
+extern int allow_severity;
+extern int deny_severity;
diff --git a/usr.sbin/portmap/pmap_dump/Makefile b/usr.sbin/portmap/pmap_dump/Makefile
new file mode 100644
index 0000000..0064f28
--- /dev/null
+++ b/usr.sbin/portmap/pmap_dump/Makefile
@@ -0,0 +1,7 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= pmap_dump
+NOMAN= noman
+
+.include "${.CURDIR}/../../Makefile.inc"
+.include <bsd.prog.mk>
diff --git a/usr.sbin/portmap/pmap_dump/pmap_dump.c b/usr.sbin/portmap/pmap_dump/pmap_dump.c
new file mode 100644
index 0000000..409bf8b
--- /dev/null
+++ b/usr.sbin/portmap/pmap_dump/pmap_dump.c
@@ -0,0 +1,68 @@
+ /*
+ * pmap_dump - dump portmapper table in format readable by pmap_set
+ *
+ * Author: Wietse Venema (wietse@wzv.win.tue.nl), dept. of Mathematics and
+ * Computing Science, Eindhoven University of Technology, The Netherlands.
+ */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#) pmap_dump.c 1.1 92/06/11 22:53:15";
+#endif
+static const char rcsid[] =
+ "$Id$";
+#endif
+
+#include <stdio.h>
+#include <sys/types.h>
+#ifdef SYSV40
+#include <netinet/in.h>
+#include <rpc/rpcent.h>
+#else
+#include <netdb.h>
+#endif
+#include <rpc/rpc.h>
+#include <rpc/pmap_clnt.h>
+#include <rpc/pmap_prot.h>
+
+static char *protoname();
+
+int
+main(argc, argv)
+int argc;
+char **argv;
+{
+ struct sockaddr_in addr;
+ register struct pmaplist *list;
+ register struct rpcent *rpc;
+
+ get_myaddress(&addr);
+
+ for (list = pmap_getmaps(&addr); list; list = list->pml_next) {
+ rpc = getrpcbynumber((int) list->pml_map.pm_prog);
+ printf("%10lu %4lu %5s %6lu %s\n",
+ list->pml_map.pm_prog,
+ list->pml_map.pm_vers,
+ protoname(list->pml_map.pm_prot),
+ list->pml_map.pm_port,
+ rpc ? rpc->r_name : "");
+ }
+#undef perror
+ return (fclose(stdout) ? (perror(argv[0]), 1) : 0);
+}
+
+static char *protoname(proto)
+u_long proto;
+{
+ static char buf[BUFSIZ];
+
+ switch (proto) {
+ case IPPROTO_UDP:
+ return ("udp");
+ case IPPROTO_TCP:
+ return ("tcp");
+ default:
+ sprintf(buf, "%lu", proto);
+ return (buf);
+ }
+}
diff --git a/usr.sbin/portmap/pmap_set/Makefile b/usr.sbin/portmap/pmap_set/Makefile
new file mode 100644
index 0000000..987e320
--- /dev/null
+++ b/usr.sbin/portmap/pmap_set/Makefile
@@ -0,0 +1,7 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= pmap_set
+NOMAN= noman
+
+.include "${.CURDIR}/../../Makefile.inc"
+.include <bsd.prog.mk>
diff --git a/usr.sbin/portmap/pmap_set/pmap_set.c b/usr.sbin/portmap/pmap_set/pmap_set.c
new file mode 100644
index 0000000..3ba94fc
--- /dev/null
+++ b/usr.sbin/portmap/pmap_set/pmap_set.c
@@ -0,0 +1,79 @@
+ /*
+ * pmap_set - set portmapper table from data produced by pmap_dump
+ *
+ * Author: Wietse Venema (wietse@wzv.win.tue.nl), dept. of Mathematics and
+ * Computing Science, Eindhoven University of Technology, The Netherlands.
+ */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#) pmap_set.c 1.1 92/06/11 22:53:16";
+#endif
+static const char rcsid[] =
+ "$Id$";
+#endif
+
+#include <err.h>
+#include <stdio.h>
+#include <sys/types.h>
+#ifdef SYSV40
+#include <netinet/in.h>
+#endif
+#include <rpc/rpc.h>
+#include <rpc/pmap_clnt.h>
+
+int parse_line __P((char *, u_long *, u_long *, int *, unsigned *));
+
+int
+main(argc, argv)
+int argc;
+char **argv;
+{
+ struct sockaddr_in addr;
+ char buf[BUFSIZ];
+ u_long prog;
+ u_long vers;
+ int prot;
+ unsigned port;
+
+ get_myaddress(&addr);
+
+ while (fgets(buf, sizeof(buf), stdin)) {
+ if (parse_line(buf, &prog, &vers, &prot, &port) == 0) {
+ warnx("malformed line: %s", buf);
+ return (1);
+ }
+ if (pmap_set(prog, vers, prot, (unsigned short) port) == 0)
+ warnx("not registered: %s", buf);
+ }
+ return (0);
+}
+
+/* parse_line - convert line to numbers */
+
+int
+parse_line(buf, prog, vers, prot, port)
+char *buf;
+u_long *prog;
+u_long *vers;
+int *prot;
+unsigned *port;
+{
+ char proto_name[BUFSIZ];
+
+ if (sscanf(buf, "%lu %lu %s %u", prog, vers, proto_name, port) != 4) {
+ return (0);
+ }
+ if (strcmp(proto_name, "tcp") == 0) {
+ *prot = IPPROTO_TCP;
+ return (1);
+ }
+ if (strcmp(proto_name, "udp") == 0) {
+ *prot = IPPROTO_UDP;
+ return (1);
+ }
+ if (sscanf(proto_name, "%d", prot) == 1) {
+ return (1);
+ }
+ return (0);
+}
diff --git a/usr.sbin/portmap/portmap.8 b/usr.sbin/portmap/portmap.8
new file mode 100644
index 0000000..5635356
--- /dev/null
+++ b/usr.sbin/portmap/portmap.8
@@ -0,0 +1,114 @@
+.\" Copyright (c) 1987 Sun Microsystems
+.\" Copyright (c) 1990, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)portmap.8 8.1 (Berkeley) 6/6/93
+.\"
+.Dd June 6, 1993
+.Dt PORTMAP 8
+.Os BSD 4.3
+.Sh NAME
+.Nm portmap
+.Nd
+.Tn RPC
+program,version
+to
+.Tn DARPA
+port mapper
+.Sh SYNOPSIS
+.Nm portmap
+.Op Fl d
+.Op Fl v
+.Sh DESCRIPTION
+.Nm Portmap
+is a server that converts
+.Tn RPC
+program numbers into
+.Tn DARPA
+protocol port numbers.
+It must be running in order to make
+.Tn RPC
+calls.
+.Pp
+When an
+.Tn RPC
+server is started, it will tell
+.Nm
+what port number it is listening to, and what
+.Tn RPC
+program numbers it is prepared to serve.
+When a client wishes to make an
+.Tn RPC
+call to a given program number,
+it will first contact
+.Nm
+on the server machine to determine
+the port number where
+.Tn RPC
+packets should be sent.
+.Pp
+.Nm Portmap
+must be started before any
+.Tn RPC
+servers are invoked.
+.Pp
+Normally
+.Nm
+forks and dissociates itself from the terminal
+like any other daemon.
+.Nm Portmap
+then logs errors using
+.Xr syslog 3 .
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl d
+Prevent
+.Nm
+from running as a daemon,
+and causes errors and debugging information
+to be printed to the standard error output.
+.It Fl v
+Enable verbose logging access control checks.
+.El
+.Sh SEE ALSO
+.Xr inetd.conf 5 ,
+.Xr inetd 8 ,
+.Xr rpcinfo 8
+.Sh BUGS
+If
+.Nm
+crashes, all servers must be restarted.
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 4.3
diff --git a/usr.sbin/portmap/portmap.c b/usr.sbin/portmap/portmap.c
new file mode 100644
index 0000000..56c1262
--- /dev/null
+++ b/usr.sbin/portmap/portmap.c
@@ -0,0 +1,608 @@
+/*-
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 1990, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)portmap.c 8.1 (Berkeley) 6/6/93";
+#endif
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+/*
+@(#)portmap.c 2.3 88/08/11 4.0 RPCSRC
+static char sccsid[] = "@(#)portmap.c 1.32 87/08/06 Copyr 1984 Sun Micro";
+*/
+
+/*
+ * portmap.c, Implements the program,version to port number mapping for
+ * rpc.
+ */
+
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+
+#include <err.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <rpc/rpc.h>
+#include <rpc/pmap_prot.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <sys/wait.h>
+#include <sys/signal.h>
+#include <sys/resource.h>
+
+#include "pmap_check.h"
+
+void reg_service();
+void reap();
+static void callit();
+static void usage __P((void));
+
+struct pmaplist *pmaplist;
+int debugging = 0;
+
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ SVCXPRT *xprt;
+ int sock, c;
+ struct sockaddr_in addr;
+ int len = sizeof(struct sockaddr_in);
+ register struct pmaplist *pml;
+
+ while ((c = getopt(argc, argv, "dv")) != -1) {
+ switch (c) {
+
+ case 'd':
+ debugging = 1;
+ break;
+
+ case 'v':
+ verboselog = 1;
+ break;
+
+ default:
+ usage();
+ }
+ }
+
+ if (!debugging && daemon(0, 0))
+ err(1, "fork");
+
+ openlog("portmap", debugging ? LOG_PID | LOG_PERROR : LOG_PID,
+ LOG_DAEMON);
+
+ if ((sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP)) < 0) {
+ syslog(LOG_ERR, "cannot create udp socket: %m");
+ exit(1);
+ }
+
+ addr.sin_addr.s_addr = 0;
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(PMAPPORT);
+ if (bind(sock, (struct sockaddr *)&addr, len) != 0) {
+ syslog(LOG_ERR, "cannot bind udp: %m");
+ exit(1);
+ }
+
+ if ((xprt = svcudp_create(sock)) == (SVCXPRT *)NULL) {
+ syslog(LOG_ERR, "couldn't do udp_create");
+ exit(1);
+ }
+ /* make an entry for ourself */
+ pml = (struct pmaplist *)malloc((u_int)sizeof(struct pmaplist));
+ pml->pml_next = 0;
+ pml->pml_map.pm_prog = PMAPPROG;
+ pml->pml_map.pm_vers = PMAPVERS;
+ pml->pml_map.pm_prot = IPPROTO_UDP;
+ pml->pml_map.pm_port = PMAPPORT;
+ pmaplist = pml;
+
+ if ((sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP)) < 0) {
+ syslog(LOG_ERR, "cannot create tcp socket: %m");
+ exit(1);
+ }
+ if (bind(sock, (struct sockaddr *)&addr, len) != 0) {
+ syslog(LOG_ERR, "cannot bind tcp: %m");
+ exit(1);
+ }
+ if ((xprt = svctcp_create(sock, RPCSMALLMSGSIZE, RPCSMALLMSGSIZE))
+ == (SVCXPRT *)NULL) {
+ syslog(LOG_ERR, "couldn't do tcp_create");
+ exit(1);
+ }
+ /* make an entry for ourself */
+ pml = (struct pmaplist *)malloc((u_int)sizeof(struct pmaplist));
+ pml->pml_map.pm_prog = PMAPPROG;
+ pml->pml_map.pm_vers = PMAPVERS;
+ pml->pml_map.pm_prot = IPPROTO_TCP;
+ pml->pml_map.pm_port = PMAPPORT;
+ pml->pml_next = pmaplist;
+ pmaplist = pml;
+
+ (void)svc_register(xprt, PMAPPROG, PMAPVERS, reg_service, FALSE);
+
+ /* additional initializations */
+ check_startup();
+ (void)signal(SIGCHLD, reap);
+ svc_run();
+ syslog(LOG_ERR, "svc_run returned unexpectedly");
+ abort();
+}
+
+static void
+usage()
+{
+ fprintf(stderr, "usage: portmap [-dv]\n");
+ exit(1);
+}
+
+#ifndef lint
+/* need to override perror calls in rpc library */
+void
+perror(what)
+ const char *what;
+{
+
+ syslog(LOG_ERR, "%s: %m", what);
+}
+#endif
+
+static struct pmaplist *
+find_service(prog, vers, prot)
+ u_long prog, vers, prot;
+{
+ register struct pmaplist *hit = NULL;
+ register struct pmaplist *pml;
+
+ for (pml = pmaplist; pml != NULL; pml = pml->pml_next) {
+ if ((pml->pml_map.pm_prog != prog) ||
+ (pml->pml_map.pm_prot != prot))
+ continue;
+ hit = pml;
+ if (pml->pml_map.pm_vers == vers)
+ break;
+ }
+ return (hit);
+}
+
+/*
+ * 1 OK, 0 not
+ */
+void
+reg_service(rqstp, xprt)
+ struct svc_req *rqstp;
+ SVCXPRT *xprt;
+{
+ struct pmap reg;
+ struct pmaplist *pml, *prevpml, *fnd;
+ int ans, port;
+ caddr_t t;
+
+ /*
+ * Later wrappers change the logging severity on the fly. Reset to
+ * defaults before handling the next request.
+ */
+ allow_severity = LOG_INFO;
+ deny_severity = LOG_WARNING;
+
+ if (debugging)
+ (void) fprintf(stderr, "server: about to do a switch\n");
+ switch (rqstp->rq_proc) {
+
+ case PMAPPROC_NULL:
+ /*
+ * Null proc call
+ */
+ /* remote host authorization check */
+ check_default(svc_getcaller(xprt), rqstp->rq_proc, (u_long) 0);
+ if (!svc_sendreply(xprt, xdr_void, (caddr_t)0) && debugging) {
+ abort();
+ }
+ break;
+
+ case PMAPPROC_SET:
+ /*
+ * Set a program,version to port mapping
+ */
+ if (!svc_getargs(xprt, xdr_pmap, (caddr_t)&reg))
+ svcerr_decode(xprt);
+ else {
+ /* reject non-local requests, protect priv. ports */
+ if (!check_setunset(svc_getcaller(xprt),
+ rqstp->rq_proc, reg.pm_prog, reg.pm_port)) {
+ ans = 0;
+ goto done;
+ }
+ /*
+ * check to see if already used
+ * find_service returns a hit even if
+ * the versions don't match, so check for it
+ */
+ fnd = find_service(reg.pm_prog, reg.pm_vers, reg.pm_prot);
+ if (fnd && fnd->pml_map.pm_vers == reg.pm_vers) {
+ if (fnd->pml_map.pm_port == reg.pm_port) {
+ ans = 1;
+ goto done;
+ }
+ else {
+ ans = 0;
+ goto done;
+ }
+ } else {
+ /*
+ * add to END of list
+ */
+ pml = (struct pmaplist *)
+ malloc((u_int)sizeof(struct pmaplist));
+ pml->pml_map = reg;
+ pml->pml_next = 0;
+ if (pmaplist == 0) {
+ pmaplist = pml;
+ } else {
+ for (fnd= pmaplist; fnd->pml_next != 0;
+ fnd = fnd->pml_next);
+ fnd->pml_next = pml;
+ }
+ ans = 1;
+ }
+ done:
+ if ((!svc_sendreply(xprt, xdr_long, (caddr_t)&ans)) &&
+ debugging) {
+ (void) fprintf(stderr, "svc_sendreply\n");
+ abort();
+ }
+ }
+ break;
+
+ case PMAPPROC_UNSET:
+ /*
+ * Remove a program,version to port mapping.
+ */
+ if (!svc_getargs(xprt, xdr_pmap, (caddr_t)&reg))
+ svcerr_decode(xprt);
+ else {
+ ans = 0;
+ /* reject non-local requests */
+ if (!check_setunset(svc_getcaller(xprt),
+ rqstp->rq_proc, reg.pm_prog, (u_long) 0))
+ goto done;
+ for (prevpml = NULL, pml = pmaplist; pml != NULL; ) {
+ if ((pml->pml_map.pm_prog != reg.pm_prog) ||
+ (pml->pml_map.pm_vers != reg.pm_vers)) {
+ /* both pml & prevpml move forwards */
+ prevpml = pml;
+ pml = pml->pml_next;
+ continue;
+ }
+ /* found it; pml moves forward, prevpml stays */
+ /* privileged port check */
+ if (!check_privileged_port(svc_getcaller(xprt),
+ rqstp->rq_proc,
+ reg.pm_prog,
+ pml->pml_map.pm_port)) {
+ ans = 0;
+ break;
+ }
+ ans = 1;
+ t = (caddr_t)pml;
+ pml = pml->pml_next;
+ if (prevpml == NULL)
+ pmaplist = pml;
+ else
+ prevpml->pml_next = pml;
+ free(t);
+ }
+ if ((!svc_sendreply(xprt, xdr_long, (caddr_t)&ans)) &&
+ debugging) {
+ (void) fprintf(stderr, "svc_sendreply\n");
+ abort();
+ }
+ }
+ break;
+
+ case PMAPPROC_GETPORT:
+ /*
+ * Lookup the mapping for a program,version and return its port
+ */
+ if (!svc_getargs(xprt, xdr_pmap, (caddr_t)&reg))
+ svcerr_decode(xprt);
+ else {
+ /* remote host authorization check */
+ if (!check_default(svc_getcaller(xprt),
+ rqstp->rq_proc,
+ reg.pm_prog)) {
+ ans = 0;
+ goto done;
+ }
+ fnd = find_service(reg.pm_prog, reg.pm_vers, reg.pm_prot);
+ if (fnd)
+ port = fnd->pml_map.pm_port;
+ else
+ port = 0;
+ if ((!svc_sendreply(xprt, xdr_long, (caddr_t)&port)) &&
+ debugging) {
+ (void) fprintf(stderr, "svc_sendreply\n");
+ abort();
+ }
+ }
+ break;
+
+ case PMAPPROC_DUMP:
+ /*
+ * Return the current set of mapped program,version
+ */
+ if (!svc_getargs(xprt, xdr_void, NULL))
+ svcerr_decode(xprt);
+ else {
+ /* remote host authorization check */
+ struct pmaplist *p;
+ if (!check_default(svc_getcaller(xprt),
+ rqstp->rq_proc, (u_long) 0)) {
+ p = 0; /* send empty list */
+ } else {
+ p = pmaplist;
+ }
+ if ((!svc_sendreply(xprt, xdr_pmaplist,
+ (caddr_t)&p)) && debugging) {
+ (void) fprintf(stderr, "svc_sendreply\n");
+ abort();
+ }
+ }
+ break;
+
+ case PMAPPROC_CALLIT:
+ /*
+ * Calls a procedure on the local machine. If the requested
+ * procedure is not registered this procedure does not return
+ * error information!!
+ * This procedure is only supported on rpc/udp and calls via
+ * rpc/udp. It passes null authentication parameters.
+ */
+ callit(rqstp, xprt);
+ break;
+
+ default:
+ /* remote host authorization check */
+ check_default(svc_getcaller(xprt), rqstp->rq_proc, (u_long) 0);
+ svcerr_noproc(xprt);
+ break;
+ }
+}
+
+
+/*
+ * Stuff for the rmtcall service
+ */
+#define ARGSIZE 9000
+
+struct encap_parms {
+ u_int arglen;
+ char *args;
+};
+
+static bool_t
+xdr_encap_parms(xdrs, epp)
+ XDR *xdrs;
+ struct encap_parms *epp;
+{
+
+ return (xdr_bytes(xdrs, &(epp->args), &(epp->arglen), ARGSIZE));
+}
+
+struct rmtcallargs {
+ u_long rmt_prog;
+ u_long rmt_vers;
+ u_long rmt_port;
+ u_long rmt_proc;
+ struct encap_parms rmt_args;
+};
+
+static bool_t
+xdr_rmtcall_args(xdrs, cap)
+ register XDR *xdrs;
+ register struct rmtcallargs *cap;
+{
+
+ /* does not get a port number */
+ if (xdr_u_long(xdrs, &(cap->rmt_prog)) &&
+ xdr_u_long(xdrs, &(cap->rmt_vers)) &&
+ xdr_u_long(xdrs, &(cap->rmt_proc))) {
+ return (xdr_encap_parms(xdrs, &(cap->rmt_args)));
+ }
+ return (FALSE);
+}
+
+static bool_t
+xdr_rmtcall_result(xdrs, cap)
+ register XDR *xdrs;
+ register struct rmtcallargs *cap;
+{
+ if (xdr_u_long(xdrs, &(cap->rmt_port)))
+ return (xdr_encap_parms(xdrs, &(cap->rmt_args)));
+ return (FALSE);
+}
+
+/*
+ * only worries about the struct encap_parms part of struct rmtcallargs.
+ * The arglen must already be set!!
+ */
+static bool_t
+xdr_opaque_parms(xdrs, cap)
+ XDR *xdrs;
+ struct rmtcallargs *cap;
+{
+
+ return (xdr_opaque(xdrs, cap->rmt_args.args, cap->rmt_args.arglen));
+}
+
+/*
+ * This routine finds and sets the length of incoming opaque paraters
+ * and then calls xdr_opaque_parms.
+ */
+static bool_t
+xdr_len_opaque_parms(xdrs, cap)
+ register XDR *xdrs;
+ struct rmtcallargs *cap;
+{
+ register u_int beginpos, lowpos, highpos, currpos, pos;
+
+ beginpos = lowpos = pos = xdr_getpos(xdrs);
+ highpos = lowpos + ARGSIZE;
+ while ((int)(highpos - lowpos) >= 0) {
+ currpos = (lowpos + highpos) / 2;
+ if (xdr_setpos(xdrs, currpos)) {
+ pos = currpos;
+ lowpos = currpos + 1;
+ } else {
+ highpos = currpos - 1;
+ }
+ }
+ xdr_setpos(xdrs, beginpos);
+ cap->rmt_args.arglen = pos - beginpos;
+ return (xdr_opaque_parms(xdrs, cap));
+}
+
+/*
+ * Call a remote procedure service
+ * This procedure is very quiet when things go wrong.
+ * The proc is written to support broadcast rpc. In the broadcast case,
+ * a machine should shut-up instead of complain, less the requestor be
+ * overrun with complaints at the expense of not hearing a valid reply ...
+ *
+ * This now forks so that the program & process that it calls can call
+ * back to the portmapper.
+ */
+static void
+callit(rqstp, xprt)
+ struct svc_req *rqstp;
+ SVCXPRT *xprt;
+{
+ struct rmtcallargs a;
+ struct pmaplist *pml;
+ u_short port;
+ struct sockaddr_in me;
+ int pid, so = -1;
+ CLIENT *client;
+ struct authunix_parms *au = (struct authunix_parms *)rqstp->rq_clntcred;
+ struct timeval timeout;
+ char buf[ARGSIZE];
+
+ timeout.tv_sec = 5;
+ timeout.tv_usec = 0;
+ a.rmt_args.args = buf;
+ if (!svc_getargs(xprt, xdr_rmtcall_args, (caddr_t)&a))
+ return;
+ /* host and service access control */
+ if (!check_callit(svc_getcaller(xprt),
+ rqstp->rq_proc, a.rmt_prog, a.rmt_proc))
+ return;
+ if ((pml = find_service(a.rmt_prog, a.rmt_vers,
+ (u_long)IPPROTO_UDP)) == NULL)
+ return;
+ /*
+ * fork a child to do the work. Parent immediately returns.
+ * Child exits upon completion.
+ */
+ if ((pid = fork()) != 0) {
+ if (pid < 0)
+ syslog(LOG_ERR, "CALLIT (prog %lu): fork: %m",
+ a.rmt_prog);
+ return;
+ }
+ port = pml->pml_map.pm_port;
+ get_myaddress(&me);
+ me.sin_port = htons(port);
+ client = clntudp_create(&me, a.rmt_prog, a.rmt_vers, timeout, &so);
+ if (client != (CLIENT *)NULL) {
+ if (rqstp->rq_cred.oa_flavor == AUTH_UNIX) {
+ client->cl_auth = authunix_create(au->aup_machname,
+ au->aup_uid, au->aup_gid, au->aup_len, au->aup_gids);
+ }
+ a.rmt_port = (u_long)port;
+ if (clnt_call(client, a.rmt_proc, xdr_opaque_parms, &a,
+ xdr_len_opaque_parms, &a, timeout) == RPC_SUCCESS) {
+ svc_sendreply(xprt, xdr_rmtcall_result, (caddr_t)&a);
+ }
+ AUTH_DESTROY(client->cl_auth);
+ clnt_destroy(client);
+ }
+ (void)close(so);
+ exit(0);
+}
+
+void
+reap()
+{
+ while (wait3((int *)NULL, WNOHANG, (struct rusage *)NULL) > 0);
+}
diff --git a/usr.sbin/ppp/Makefile b/usr.sbin/ppp/Makefile
new file mode 100644
index 0000000..35a35b0
--- /dev/null
+++ b/usr.sbin/ppp/Makefile
@@ -0,0 +1,23 @@
+# $Id: Makefile,v 1.28 1997/10/26 01:01:58 brian Exp $
+
+PROG= ppp
+SRCS= alias_cmd.c arp.c async.c auth.c ccp.c chap.c chat.c command.c \
+ defs.c filter.c fsm.c hdlc.c id.c ip.c ipcp.c lcp.c loadalias.c log.c \
+ lqr.c main.c mbuf.c modem.c os.c pap.c phase.c pred.c route.c \
+ server.c sig.c slcompress.c systems.c timer.c vars.c vjcomp.c
+CFLAGS+=-Wall -Wmissing-prototypes
+LDADD+= -lmd -lcrypt -lutil
+DPADD+= ${LIBMD} ${LIBCRYPT} ${LIBUTIL}
+MAN8= ppp.8
+BINMODE=4550
+BINOWN= root
+BINGRP= network
+
+.if exists(${.CURDIR}/../../secure) && !defined(NOCRYPT) && !defined(NOSECURE)
+CFLAGS+=-DHAVE_DES
+SRCS+= chap_ms.c
+LDADD+= -ldes
+DPADD+= ${LIBDES}
+.endif
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/ppp/README.alias b/usr.sbin/ppp/README.alias
new file mode 100644
index 0000000..de5b3c9
--- /dev/null
+++ b/usr.sbin/ppp/README.alias
@@ -0,0 +1,352 @@
+User PPP Packet Aliasing
+
+
+
+0. Contents
+ 1. Background
+ 2. Setup
+ 3. New commands in ppp
+ 4. Future Work
+ 5. Authors / Acknowledgments
+ 6. Revision History for Aliasing Code
+
+
+
+1. Background
+
+User mode ppp has embedded packet aliasing (IP masquerading) code.
+Enabling this, either by the "-alias" command line option or the
+"alias enable yes" command in a ppp.conf file, makes the ppp host
+automatically alias IP packets forwarded from a local network, making
+them appear to come from the ppp host machine. Incoming packets
+from the outside world are then appropriately de-aliased.
+
+The process of aliasing involves both the IP address and the TCP or UDP
+port numbers. ICMP echo and timestamp packets are aliased by their id
+numbers. ICMP error messages can be properly directed by examining the
+fragment of the offending packet which is contained in the body of the
+message.
+
+This software was specifically meant to support users who have
+unregistered, private address IP networks (e.g. 192.168.0.x or 10.0.0.x
+addresses). The ppp host can act as a gateway for these networks, and
+computers on the local area net will have some degree of Internet access
+without the need for a registered IP address. Additionally, there will
+be no need for an Internet service provider to maintain routing tables
+for the local area network.
+
+A disadvantage of packet aliasing is that machines on the local network,
+behind the ppp host, are not visible from the outside world. They can
+establish TCP connections and make UDP inquiries (such as domain name
+service requests) but the connections seem to come from the ppp host
+itself. There is, in effect, a partial firewall. Of course, if this is
+what you want, the disadvantage becomes an advantage.
+
+A second disadvantage is that "IP encoding" protocols, which send IP
+address or port information within the data stream, are not supported
+for the cases where exception code exists. This implementation has
+workarounds for FTP and IRC DCC, the most well known of the IP encoding
+protocols. This frees users from depending on using the ftp passive
+mode and avoiding IRC DCC sends, as is sometimes the case with other
+masquerading solutions.
+
+The implementation supports all standard, non-encoding TCP and UDP protocols.
+Examples of these protocols are http, gopher and telnet. The standard UDP
+mode of RealAudio is not presently supported, but the TCP mode does work
+correctly.
+
+The packet aliasing code also handle many ICMP messages. In particular,
+ping and traceroute are supported.
+
+
+
+2. Packet Aliasing Setup
+
+It is recommended that users first verify correct ppp operation without
+packet aliasing enabled. This will confirm that the ppp.conf file is
+properly set up and that there are no ppp problems. Then start ppp with
+the "-alias" option on the command line. The user should verify that
+the ppp host can correctly connect to the Internet in packet aliasing
+mode. Finally, check that machines on the private network can access
+the Internet.
+
+The masquerading software aliases all packets, whether they come from
+the host or another computer on the local area network. Thus, a correctly
+operating ppp host indicates that the software should work properly for
+other computers on the private network.
+
+If the ppp host can access the Internet, but other computers on the local
+network cannot, check that IP forwarding is enabled on the ppp host. Also,
+verify that the other computers use this machine as a gateway. Of course,
+you should also verify that machines within the local area network
+communicate properly. A common error is inconsistent subnet addresses
+and masks.
+
+
+
+3. New commands in ppp
+
+In order to control aliasing behavior in a simple manner (no need for
+recompilation), a new command has been added to iij-ppp: alias. This
+is in addition to the -alias command line option. System managers and
+more experienced users may prefer to use the iij-ppp command syntax
+within the ppp.conf file. The alias command also allows packet aliasing
+behavior to be more precisely specified.
+
+The decision to add a command instead of extending 'set' or 'option' was
+to make obvious that these options only work when aliasing is enabled.
+
+The syntax for 'alias' is
+
+ ppp> alias option [yes|no]
+
+where option is given by one of the following templates.
+
+
+ - alias enable [yes|no] (default no)
+
+Enable packet aliasing functionality. If disabled, no other alias
+options will have any effect. You should usually enable aliasing
+before routing any packets over the link; good points are in the
+initial script or right before adding a route. If you do not always
+want aliasing, consider using the -alias option to ppp instead of this
+command.
+
+
+ - alias deny_incoming [yes|no] (default yes)
+
+Set to "yes" to disable all incoming connections. This just drops
+connections to, for example, ftp, telnet or web servers. The aliasing
+mechanism prevents these connections. Technically, this option denies
+all incoming TCP and UDP requests, making the aliasing software a
+fairly efficient one-way firewall. The default is no, which will
+all incoming connections to telnetd, ftpd, etc.
+
+
+ - alias log [yes|no]
+
+Controls logging of alias link creation to "/var/log/alias.log" - this
+is usually only useful if debugging a setup, to see if the bug is in
+the PPP aliasing. The debugging information is fairly limited, listing
+the number of aliasing links open for different prototocols.
+
+
+ - alias same_ports [yes|no] (default yes)
+
+When a connection is being established going through the aliasing
+routines, it will normally have its port number changed to allow the
+aliasing code to track it. If same_ports is enabled, the alias
+software attempts to keep the connection's source port unchanged.
+This will allow rsh, RPC and other specialized protocols to work
+_most of the time_, at least on the host machine. Please, do not
+report this being unstable as a bug - it is a result of the way
+aliasing has to work. TCP/IP was intended to have one IP address
+per machine.
+
+
+ - alias use_sockets [yes|no] (default yes)
+
+This is a fairly obscure option. For the most part, the packet aliasing
+software does not have to allocate system sockets when it chooses an
+aliasing port number. Under very specific circumstances, FTP data
+connections (which don't know the remote port nubmer, 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 interferience only exists
+until the TCP connection has been acknowledged on both sides. The safe
+option is yes, though fewer system resources are consumed by specifying
+no.
+
+
+ - alias unregistered_only [yes|no] (default no)
+
+Packet aliasing normally remaps all packets coming from the local area
+network to the ppp host machine address. Set this option to only map
+addresses from the following standard ranges for private, unregistered
+addresses:
+
+ 10.0.0.0 -> 10.255.255.255
+ 172.16.0.0 -> 172.31.255.255
+ 192.168.0.0 -> 192.168.255.255 */
+
+In the instance that there is a subnet of public addresses and another
+subnet of private addresses being routed by the ppp host, then only the
+packets on the private subnet will be aliased.
+
+
+- alias port <proto> <local addr>:<port> <alias port>
+
+This command allows incoming traffic to <alias port> on the host
+machine to be redirected to a specific machine and port on the
+local area network. One example of this would be:
+
+ alias port tcp 192.168.0.4:telnet 8066
+
+All traffic to port 8066 fthe ppp host would then be sent to
+the telnet port (23) of machine 192.168.0.4. Port numbers
+can either be designated numerically or by symbolic names
+listed in /etc/services. Similarly, addresses can be either
+in dotted quad notation or in /etc/hosts.
+
+
+- alias addr <local addr> <public addr>
+
+This command allows traffic for a public IP address to be
+redirected to a machine on the local network. This function
+is known as "static NAT". An address assignment of 0 refers
+to the default address of the ppp host. Normally static
+NAT is useful if your ISP has allocated a small block of
+IP addresses to the user, but it can even be used in the
+case of a single, dynamically allocated IP address:
+
+ alias addr 10.0.0.8 0
+
+The above command would redirect all incoming traffic to
+machine 10.0.0.8.
+
+If several address aliases specifiy the same public addres
+as follows
+
+ alias addr 192.168.0.2 public_addr
+ alias addr 192.168.0.3 public_addr
+ alias addr 192.168.0.4 public_addr
+
+then incoming traffice will be directed to the last
+translated local address (192.168.0.4), but outgoing
+traffic to the first two addresses will still be aliased
+to the specified public address.
+
+
+
+4. Future Work
+
+What is called packet aliasing here has been variously called masquerading,
+network address translation (NAT) and transparent proxying by others. It
+is an extremely useful function to many users, but it is also necessarily
+imperfect. The occasional IP-encoding protocols always need workarounds
+(hacks). Users who are interested in supporting new IP-encoding protocols
+can follow the examples of alias_ftp.c and alias_irc.c.
+
+ICMP error messages are currently handled only in the incoming direction.
+A handler needs to be added to correctly alias outgoing error messages.
+
+IRC and FTP exception handling make reasonable, though not strictly correct
+assumptions, about how IP encoded messages will appear in the control
+stream. Programmers may wish to consider how to make this process more
+robust.
+
+The packet aliasing engine (alias.c, alias_db.c, alias_ftp.c, alias_irc.c
+and alias_util.c) runs in user space, and is intended to be both portable
+and reusable for interfaces other than ppp. To access the basic engine
+only requires four simple function calls (initialization, communication of
+host address, outgoing aliasing and incoming de-aliasing).
+
+
+
+5. Authors / Acknowledgments
+
+Charles Mott (cmott@srv.net) <versions 1.0 - 1.8, 2.0, 2.1>
+Eivind Eklund (perhaps@yes.no) <versions 1.8b - 1.9, new ppp commands>
+
+Listed below, in chronological order, are individuals who have provided
+valuable comments and/or debugging assistance.
+
+ Gary Roberts
+ Tom Torrance
+ Reto Burkhalter
+ Martin Renters
+ Brian Somers
+ Paul Traina
+ Ari Suutari
+ J. Fortes
+ Andrzej Bialeki
+
+
+
+6. Revision History for Aliasing Code
+
+Version 1.0: August 11, 1996 (cjm)
+
+Version 1.1: August 20, 1996 (cjm)
+ PPP host accepts incoming connections for ports 0 to 1023.
+
+Version 1.2: September 7, 1996 (cjm)
+ Fragment handling error in alias_db.c corrected.
+
+Version 1.3: September 15, 1996 (cjm)
+ - Generalized 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.
+
+Verstion 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.
+
+Verstion 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 sourcecode 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 recognized 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 allocatioa).
+ - 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 messges/
+ - PPP commands to support address and port redirection.
+
diff --git a/usr.sbin/ppp/README.nat b/usr.sbin/ppp/README.nat
new file mode 100644
index 0000000..de5b3c9
--- /dev/null
+++ b/usr.sbin/ppp/README.nat
@@ -0,0 +1,352 @@
+User PPP Packet Aliasing
+
+
+
+0. Contents
+ 1. Background
+ 2. Setup
+ 3. New commands in ppp
+ 4. Future Work
+ 5. Authors / Acknowledgments
+ 6. Revision History for Aliasing Code
+
+
+
+1. Background
+
+User mode ppp has embedded packet aliasing (IP masquerading) code.
+Enabling this, either by the "-alias" command line option or the
+"alias enable yes" command in a ppp.conf file, makes the ppp host
+automatically alias IP packets forwarded from a local network, making
+them appear to come from the ppp host machine. Incoming packets
+from the outside world are then appropriately de-aliased.
+
+The process of aliasing involves both the IP address and the TCP or UDP
+port numbers. ICMP echo and timestamp packets are aliased by their id
+numbers. ICMP error messages can be properly directed by examining the
+fragment of the offending packet which is contained in the body of the
+message.
+
+This software was specifically meant to support users who have
+unregistered, private address IP networks (e.g. 192.168.0.x or 10.0.0.x
+addresses). The ppp host can act as a gateway for these networks, and
+computers on the local area net will have some degree of Internet access
+without the need for a registered IP address. Additionally, there will
+be no need for an Internet service provider to maintain routing tables
+for the local area network.
+
+A disadvantage of packet aliasing is that machines on the local network,
+behind the ppp host, are not visible from the outside world. They can
+establish TCP connections and make UDP inquiries (such as domain name
+service requests) but the connections seem to come from the ppp host
+itself. There is, in effect, a partial firewall. Of course, if this is
+what you want, the disadvantage becomes an advantage.
+
+A second disadvantage is that "IP encoding" protocols, which send IP
+address or port information within the data stream, are not supported
+for the cases where exception code exists. This implementation has
+workarounds for FTP and IRC DCC, the most well known of the IP encoding
+protocols. This frees users from depending on using the ftp passive
+mode and avoiding IRC DCC sends, as is sometimes the case with other
+masquerading solutions.
+
+The implementation supports all standard, non-encoding TCP and UDP protocols.
+Examples of these protocols are http, gopher and telnet. The standard UDP
+mode of RealAudio is not presently supported, but the TCP mode does work
+correctly.
+
+The packet aliasing code also handle many ICMP messages. In particular,
+ping and traceroute are supported.
+
+
+
+2. Packet Aliasing Setup
+
+It is recommended that users first verify correct ppp operation without
+packet aliasing enabled. This will confirm that the ppp.conf file is
+properly set up and that there are no ppp problems. Then start ppp with
+the "-alias" option on the command line. The user should verify that
+the ppp host can correctly connect to the Internet in packet aliasing
+mode. Finally, check that machines on the private network can access
+the Internet.
+
+The masquerading software aliases all packets, whether they come from
+the host or another computer on the local area network. Thus, a correctly
+operating ppp host indicates that the software should work properly for
+other computers on the private network.
+
+If the ppp host can access the Internet, but other computers on the local
+network cannot, check that IP forwarding is enabled on the ppp host. Also,
+verify that the other computers use this machine as a gateway. Of course,
+you should also verify that machines within the local area network
+communicate properly. A common error is inconsistent subnet addresses
+and masks.
+
+
+
+3. New commands in ppp
+
+In order to control aliasing behavior in a simple manner (no need for
+recompilation), a new command has been added to iij-ppp: alias. This
+is in addition to the -alias command line option. System managers and
+more experienced users may prefer to use the iij-ppp command syntax
+within the ppp.conf file. The alias command also allows packet aliasing
+behavior to be more precisely specified.
+
+The decision to add a command instead of extending 'set' or 'option' was
+to make obvious that these options only work when aliasing is enabled.
+
+The syntax for 'alias' is
+
+ ppp> alias option [yes|no]
+
+where option is given by one of the following templates.
+
+
+ - alias enable [yes|no] (default no)
+
+Enable packet aliasing functionality. If disabled, no other alias
+options will have any effect. You should usually enable aliasing
+before routing any packets over the link; good points are in the
+initial script or right before adding a route. If you do not always
+want aliasing, consider using the -alias option to ppp instead of this
+command.
+
+
+ - alias deny_incoming [yes|no] (default yes)
+
+Set to "yes" to disable all incoming connections. This just drops
+connections to, for example, ftp, telnet or web servers. The aliasing
+mechanism prevents these connections. Technically, this option denies
+all incoming TCP and UDP requests, making the aliasing software a
+fairly efficient one-way firewall. The default is no, which will
+all incoming connections to telnetd, ftpd, etc.
+
+
+ - alias log [yes|no]
+
+Controls logging of alias link creation to "/var/log/alias.log" - this
+is usually only useful if debugging a setup, to see if the bug is in
+the PPP aliasing. The debugging information is fairly limited, listing
+the number of aliasing links open for different prototocols.
+
+
+ - alias same_ports [yes|no] (default yes)
+
+When a connection is being established going through the aliasing
+routines, it will normally have its port number changed to allow the
+aliasing code to track it. If same_ports is enabled, the alias
+software attempts to keep the connection's source port unchanged.
+This will allow rsh, RPC and other specialized protocols to work
+_most of the time_, at least on the host machine. Please, do not
+report this being unstable as a bug - it is a result of the way
+aliasing has to work. TCP/IP was intended to have one IP address
+per machine.
+
+
+ - alias use_sockets [yes|no] (default yes)
+
+This is a fairly obscure option. For the most part, the packet aliasing
+software does not have to allocate system sockets when it chooses an
+aliasing port number. Under very specific circumstances, FTP data
+connections (which don't know the remote port nubmer, 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 interferience only exists
+until the TCP connection has been acknowledged on both sides. The safe
+option is yes, though fewer system resources are consumed by specifying
+no.
+
+
+ - alias unregistered_only [yes|no] (default no)
+
+Packet aliasing normally remaps all packets coming from the local area
+network to the ppp host machine address. Set this option to only map
+addresses from the following standard ranges for private, unregistered
+addresses:
+
+ 10.0.0.0 -> 10.255.255.255
+ 172.16.0.0 -> 172.31.255.255
+ 192.168.0.0 -> 192.168.255.255 */
+
+In the instance that there is a subnet of public addresses and another
+subnet of private addresses being routed by the ppp host, then only the
+packets on the private subnet will be aliased.
+
+
+- alias port <proto> <local addr>:<port> <alias port>
+
+This command allows incoming traffic to <alias port> on the host
+machine to be redirected to a specific machine and port on the
+local area network. One example of this would be:
+
+ alias port tcp 192.168.0.4:telnet 8066
+
+All traffic to port 8066 fthe ppp host would then be sent to
+the telnet port (23) of machine 192.168.0.4. Port numbers
+can either be designated numerically or by symbolic names
+listed in /etc/services. Similarly, addresses can be either
+in dotted quad notation or in /etc/hosts.
+
+
+- alias addr <local addr> <public addr>
+
+This command allows traffic for a public IP address to be
+redirected to a machine on the local network. This function
+is known as "static NAT". An address assignment of 0 refers
+to the default address of the ppp host. Normally static
+NAT is useful if your ISP has allocated a small block of
+IP addresses to the user, but it can even be used in the
+case of a single, dynamically allocated IP address:
+
+ alias addr 10.0.0.8 0
+
+The above command would redirect all incoming traffic to
+machine 10.0.0.8.
+
+If several address aliases specifiy the same public addres
+as follows
+
+ alias addr 192.168.0.2 public_addr
+ alias addr 192.168.0.3 public_addr
+ alias addr 192.168.0.4 public_addr
+
+then incoming traffice will be directed to the last
+translated local address (192.168.0.4), but outgoing
+traffic to the first two addresses will still be aliased
+to the specified public address.
+
+
+
+4. Future Work
+
+What is called packet aliasing here has been variously called masquerading,
+network address translation (NAT) and transparent proxying by others. It
+is an extremely useful function to many users, but it is also necessarily
+imperfect. The occasional IP-encoding protocols always need workarounds
+(hacks). Users who are interested in supporting new IP-encoding protocols
+can follow the examples of alias_ftp.c and alias_irc.c.
+
+ICMP error messages are currently handled only in the incoming direction.
+A handler needs to be added to correctly alias outgoing error messages.
+
+IRC and FTP exception handling make reasonable, though not strictly correct
+assumptions, about how IP encoded messages will appear in the control
+stream. Programmers may wish to consider how to make this process more
+robust.
+
+The packet aliasing engine (alias.c, alias_db.c, alias_ftp.c, alias_irc.c
+and alias_util.c) runs in user space, and is intended to be both portable
+and reusable for interfaces other than ppp. To access the basic engine
+only requires four simple function calls (initialization, communication of
+host address, outgoing aliasing and incoming de-aliasing).
+
+
+
+5. Authors / Acknowledgments
+
+Charles Mott (cmott@srv.net) <versions 1.0 - 1.8, 2.0, 2.1>
+Eivind Eklund (perhaps@yes.no) <versions 1.8b - 1.9, new ppp commands>
+
+Listed below, in chronological order, are individuals who have provided
+valuable comments and/or debugging assistance.
+
+ Gary Roberts
+ Tom Torrance
+ Reto Burkhalter
+ Martin Renters
+ Brian Somers
+ Paul Traina
+ Ari Suutari
+ J. Fortes
+ Andrzej Bialeki
+
+
+
+6. Revision History for Aliasing Code
+
+Version 1.0: August 11, 1996 (cjm)
+
+Version 1.1: August 20, 1996 (cjm)
+ PPP host accepts incoming connections for ports 0 to 1023.
+
+Version 1.2: September 7, 1996 (cjm)
+ Fragment handling error in alias_db.c corrected.
+
+Version 1.3: September 15, 1996 (cjm)
+ - Generalized 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.
+
+Verstion 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.
+
+Verstion 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 sourcecode 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 recognized 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 allocatioa).
+ - 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 messges/
+ - PPP commands to support address and port redirection.
+
diff --git a/usr.sbin/ppp/alias_cmd.c b/usr.sbin/ppp/alias_cmd.c
new file mode 100644
index 0000000..4b91898
--- /dev/null
+++ b/usr.sbin/ppp/alias_cmd.c
@@ -0,0 +1,190 @@
+/*
+ * $Id: $
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "mbuf.h"
+#include "log.h"
+#include "defs.h"
+#include "command.h"
+#include "loadalias.h"
+#include "vars.h"
+#include "alias_cmd.h"
+
+
+static int StrToAddr(char *, struct in_addr *);
+static int StrToPort(char *, u_short *, char *);
+static int StrToAddrAndPort(char *, struct in_addr *, u_short *, char *);
+
+
+int
+AliasRedirectPort(struct cmdtab *list, int argc, char **argv, void *param)
+{
+ if (!(mode & MODE_ALIAS)) {
+ if (VarTerm)
+ fprintf(VarTerm, "Alias not enabled\n");
+ } else if (argc == 3) {
+ char proto_constant;
+ char *proto;
+ u_short local_port;
+ u_short alias_port;
+ int error;
+ struct in_addr local_addr;
+ struct in_addr null_addr;
+ struct alias_link *link;
+
+ proto = argv[0];
+ if (strcmp(proto, "tcp") == 0) {
+ proto_constant = IPPROTO_TCP;
+ } else if (strcmp(proto, "udp") == 0) {
+ proto_constant = IPPROTO_UDP;
+ } else {
+ if (VarTerm) {
+ fprintf(VarTerm, "port redirect: protocol must be tcp or udp\n");
+ fprintf(VarTerm, "Usage: alias %s %s\n", list->name,
+ list->syntax);
+ }
+ return 1;
+ }
+
+ error = StrToAddrAndPort(argv[1], &local_addr, &local_port, proto);
+ if (error) {
+ if (VarTerm) {
+ fprintf(VarTerm, "port redirect: error reading local addr:port\n");
+ fprintf(VarTerm, "Usage: alias %s %s\n", list->name, list->syntax);
+ }
+ return 1;
+ }
+ error = StrToPort(argv[2], &alias_port, proto);
+ if (error) {
+ if (VarTerm) {
+ fprintf(VarTerm, "port redirect: error reading alias port\n");
+ fprintf(VarTerm, "Usage: alias %s %s\n", list->name, list->syntax);
+ }
+ return 1;
+ }
+ null_addr.s_addr = 0;
+
+ link = VarPacketAliasRedirectPort(local_addr, local_port,
+ null_addr, 0,
+ null_addr, alias_port,
+ proto_constant);
+
+ if (link == NULL && VarTerm)
+ fprintf(VarTerm, "port redirect: error returned by packed"
+ " aliasing engine (code=%d)\n", error);
+ } else if (VarTerm)
+ fprintf(VarTerm, "Usage: alias %s %s\n", list->name, list->syntax);
+
+ return 1;
+}
+
+
+int
+AliasRedirectAddr(struct cmdtab *list, int argc, char **argv, void *param)
+{
+ if (!(mode & MODE_ALIAS)) {
+ if (VarTerm)
+ fprintf(VarTerm, "alias not enabled\n");
+ } else if (argc == 2) {
+ int error;
+ struct in_addr local_addr;
+ struct in_addr alias_addr;
+ struct alias_link *link;
+
+ error = StrToAddr(argv[0], &local_addr);
+ if (error) {
+ if (VarTerm)
+ fprintf(VarTerm, "address redirect: invalid local address\n");
+ return 1;
+ }
+ error = StrToAddr(argv[1], &alias_addr);
+ if (error) {
+ if (VarTerm) {
+ fprintf(VarTerm, "address redirect: invalid alias address\n");
+ fprintf(VarTerm, "Usage: alias %s %s\n", list->name, list->syntax);
+ }
+ return 1;
+ }
+ link = VarPacketAliasRedirectAddr(local_addr, alias_addr);
+ if (link == NULL && VarTerm) {
+ fprintf(VarTerm, "address redirect: packet aliasing engine error\n");
+ fprintf(VarTerm, "Usage: alias %s %s\n", list->name, list->syntax);
+ }
+ } else if (VarTerm)
+ fprintf(VarTerm, "Usage: alias %s %s\n", list->name, list->syntax);
+
+ return 1;
+}
+
+
+static int
+StrToAddr(char *str, struct in_addr *addr)
+{
+ struct hostent *hp;
+
+ if (inet_aton(str, addr))
+ return 0;
+
+ hp = gethostbyname(str);
+ if (!hp) {
+ LogPrintf(LogWARN, "StrToAddr: Unknown host %s.\n", str);
+ return -1;
+ }
+ *addr = *((struct in_addr *) hp->h_addr);
+ return 0;
+}
+
+
+static int
+StrToPort(char *str, u_short *port, char *proto)
+{
+ int iport;
+ struct servent *sp;
+ char *end;
+
+ iport = strtol(str, &end, 10);
+ if (end != str) {
+ *port = htons(iport);
+ return 0;
+ }
+ sp = getservbyname(str, proto);
+ if (!sp) {
+ LogPrintf(LogWARN, "StrToAddr: Unknown port or service %s/%s.\n",
+ str, proto);
+ return -1;
+ }
+ *port = sp->s_port;
+ return 0;
+}
+
+
+int
+StrToAddrAndPort(char *str, struct in_addr *addr, u_short *port, char *proto)
+{
+ char *ptr;
+
+ ptr = strchr(str, ':');
+ if (!ptr) {
+ LogPrintf(LogWARN, "StrToAddrAndPort: %s is missing port number.\n",
+ str);
+ return -1;
+ }
+ *ptr = '\0';
+ ++ptr;
+
+ if (StrToAddr(str, addr) != 0)
+ return -1;
+
+ return StrToPort(ptr, port, proto);
+}
diff --git a/usr.sbin/ppp/alias_cmd.h b/usr.sbin/ppp/alias_cmd.h
new file mode 100644
index 0000000..49b1532
--- /dev/null
+++ b/usr.sbin/ppp/alias_cmd.h
@@ -0,0 +1,6 @@
+/*
+ * $Id: $
+ */
+
+extern int AliasRedirectPort(struct cmdtab *, int, char **, void *);
+extern int AliasRedirectAddr(struct cmdtab *, int, char **, void *);
diff --git a/usr.sbin/ppp/arp.c b/usr.sbin/ppp/arp.c
new file mode 100644
index 0000000..9450511
--- /dev/null
+++ b/usr.sbin/ppp/arp.c
@@ -0,0 +1,311 @@
+/*
+ * sys-bsd.c - System-dependent procedures for setting up
+ * PPP interfaces on bsd-4.4-ish systems (including 386BSD, NetBSD, etc.)
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: arp.c,v 1.16 1997/10/26 01:02:03 brian Exp $
+ *
+ */
+
+/*
+ * TODO:
+ */
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <net/if_var.h>
+#include <net/route.h>
+#include <net/if_dl.h>
+#include <netinet/in.h>
+#include <net/if_types.h>
+#include <netinet/in_var.h>
+#include <netinet/if_ether.h>
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/errno.h>
+#include <sys/ioctl.h>
+#include <sys/uio.h>
+#include <unistd.h>
+
+#include "mbuf.h"
+#include "log.h"
+#include "id.h"
+#include "arp.h"
+
+static int rtm_seq;
+
+static int get_ether_addr(int, u_long, struct sockaddr_dl *);
+
+/*
+ * 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
+
+/*
+ * 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(int unit, u_long 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(unit, hisaddr, &arpmsg.hwa)) {
+ LogPrintf(LogERROR, "Cannot determine ethernet address for proxy ARP\n");
+ return 0;
+ }
+ routes = ID0socket(PF_ROUTE, SOCK_RAW, AF_INET);
+ if (routes < 0) {
+ LogPrintf(LogERROR, "sifproxyarp: opening routing socket: %s\n",
+ strerror(errno));
+ 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) {
+ LogPrintf(LogERROR, "Add proxy arp entry: %s\n", strerror(errno));
+ close(routes);
+ return 0;
+ }
+ close(routes);
+ arpmsg_valid = 1;
+ return 1;
+}
+
+/*
+ * cifproxyarp - Delete the proxy ARP entry for the peer.
+ */
+int
+cifproxyarp(int unit, u_long hisaddr)
+{
+ int routes;
+
+ if (!arpmsg_valid)
+ return 0;
+ arpmsg_valid = 0;
+
+ arpmsg.hdr.rtm_type = RTM_DELETE;
+ arpmsg.hdr.rtm_seq = ++rtm_seq;
+
+ routes = ID0socket(PF_ROUTE, SOCK_RAW, AF_INET);
+ if (routes < 0) {
+ LogPrintf(LogERROR, "sifproxyarp: opening routing socket: %s\n",
+ strerror(errno));
+ return 0;
+ }
+ if (write(routes, &arpmsg, arpmsg.hdr.rtm_msglen) < 0) {
+ LogPrintf(LogERROR, "Delete proxy arp entry: %s\n", strerror(errno));
+ close(routes);
+ return 0;
+ }
+ close(routes);
+ return 1;
+}
+
+#else /* RTM_VERSION */
+
+/*
+ * sifproxyarp - Make a proxy ARP entry for the peer.
+ */
+int
+sifproxyarp(int unit, u_long hisaddr)
+{
+ struct arpreq arpreq;
+ struct {
+ struct sockaddr_dl sdl;
+ char space[128];
+ } dls;
+
+ memset(&arpreq, '\0', sizeof(arpreq));
+
+ /*
+ * Get the hardware address of an interface on the same subnet as our local
+ * address.
+ */
+ if (!get_ether_addr(unit, hisaddr, &dls.sdl)) {
+ LogPrintf(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 = hisaddr;
+ arpreq.arp_flags = ATF_PERM | ATF_PUBL;
+ if (ID0ioctl(unit, SIOCSARP, (caddr_t) & arpreq) < 0) {
+ LogPrintf(LogERROR, "sifproxyarp: ioctl(SIOCSARP): %s\n", strerror(errno));
+ return 0;
+ }
+ return 1;
+}
+
+/*
+ * cifproxyarp - Delete the proxy ARP entry for the peer.
+ */
+int
+cifproxyarp(int unit, u_long hisaddr)
+{
+ 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 = hisaddr;
+ if (ID0ioctl(unit, SIOCDARP, (caddr_t) & arpreq) < 0) {
+ LogPrintf(LogERROR, "cifproxyarp: ioctl(SIOCDARP): %s\n", strerror(errno));
+ return 0;
+ }
+ return 1;
+}
+
+#endif /* RTM_VERSION */
+
+
+/*
+ * get_ether_addr - get the hardware address of an interface on the
+ * the same subnet as ipaddr.
+ */
+#define MAX_IFS 32
+
+static int
+get_ether_addr(int s, u_long ipaddr, struct sockaddr_dl *hwaddr)
+{
+ struct ifreq *ifr, *ifend, *ifp;
+ u_long ina, mask;
+ struct sockaddr_dl *dla;
+ struct ifreq ifreq;
+ struct ifconf ifc;
+ struct ifreq ifs[MAX_IFS];
+
+ ifc.ifc_len = sizeof(ifs);
+ ifc.ifc_req = ifs;
+ if (ioctl(s, SIOCGIFCONF, &ifc) < 0) {
+ LogPrintf(LogERROR, "get_ether_addr: ioctl(SIOCGIFCONF): %s\n",
+ strerror(errno));
+ 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));
+ ifreq.ifr_name[sizeof(ifreq.ifr_name) - 1] = '\0';
+
+ /*
+ * Check that the interface is up, and not point-to-point or loopback.
+ */
+ if (ioctl(s, SIOCGIFFLAGS, &ifreq) < 0)
+ continue;
+ if ((ifreq.ifr_flags &
+ (IFF_UP | IFF_BROADCAST | IFF_POINTOPOINT | IFF_LOOPBACK | IFF_NOARP))
+ != (IFF_UP | IFF_BROADCAST))
+ goto nextif;
+
+ /*
+ * Get its netmask and check that it's on the right subnet.
+ */
+ if (ioctl(s, SIOCGIFNETMASK, &ifreq) < 0)
+ continue;
+ mask = ((struct sockaddr_in *) & ifreq.ifr_addr)->sin_addr.s_addr;
+ if ((ipaddr & mask) != (ina & mask))
+ goto nextif;
+
+ break;
+ }
+nextif:
+ ifr = (struct ifreq *) ((char *) &ifr->ifr_addr + ifr->ifr_addr.sa_len);
+ }
+
+ if (ifr >= ifend)
+ return 0;
+ LogPrintf(LogPHASE, "Found interface %s for proxy arp\n", 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;
+ memcpy(hwaddr, dla, dla->sdl_len);
+ return 1;
+ }
+ ifr = (struct ifreq *) ((char *) &ifr->ifr_addr + ifr->ifr_addr.sa_len);
+ }
+
+ return 0;
+}
+
+
+#ifdef DEBUG
+int
+main()
+{
+ u_long ipaddr;
+ int s;
+
+ s = socket(AF_INET, SOCK_DGRAM, 0);
+ ipaddr = inet_addr("192.168.1.32");
+ sifproxyarp(s, ipaddr);
+ close(s);
+}
+#endif
diff --git a/usr.sbin/ppp/arp.h b/usr.sbin/ppp/arp.h
new file mode 100644
index 0000000..8c54720
--- /dev/null
+++ b/usr.sbin/ppp/arp.h
@@ -0,0 +1,25 @@
+/*
+ * User Process PPP
+ *
+ * Written by Toshiharu OHNO (tony-o@iij.ad.jp)
+ *
+ * Copyright (C) 1993, Internet Initiative Japan, Inc. All rights reserverd.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the Internet Initiative Japan, Inc. The name of the
+ * IIJ may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: arp.h,v 1.5 1997/08/25 00:29:04 brian Exp $
+ *
+ */
+
+extern int cifproxyarp(int, u_long);
+extern int sifproxyarp(int, u_long);
diff --git a/usr.sbin/ppp/async.c b/usr.sbin/ppp/async.c
new file mode 100644
index 0000000..aa93865
--- /dev/null
+++ b/usr.sbin/ppp/async.c
@@ -0,0 +1,198 @@
+/*
+ * PPP Async HDLC Module
+ *
+ * Written by Toshiharu OHNO (tony-o@iij.ad.jp)
+ *
+ * Copyright (C) 1993, Internet Initiative Japan, Inc. All rights reserverd.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the Internet Initiative Japan, Inc. The name of the
+ * IIJ may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: async.c,v 1.12 1997/10/26 01:02:05 brian Exp $
+ *
+ */
+#include <sys/param.h>
+#include <netinet/in.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <termios.h>
+
+#include "mbuf.h"
+#include "log.h"
+#include "defs.h"
+#include "timer.h"
+#include "fsm.h"
+#include "hdlc.h"
+#include "lcp.h"
+#include "lcpproto.h"
+#include "modem.h"
+#include "loadalias.h"
+#include "command.h"
+#include "vars.h"
+#include "os.h"
+#include "async.h"
+
+#define HDLCSIZE (MAX_MRU*2+6)
+
+static struct async_state {
+ int mode;
+ int length;
+ u_char hbuff[HDLCSIZE]; /* recv buffer */
+ u_char xbuff[HDLCSIZE]; /* xmit buffer */
+ u_long my_accmap;
+ u_long his_accmap;
+} AsyncState;
+
+#define MODE_HUNT 0x01
+#define MODE_ESC 0x02
+
+void
+AsyncInit()
+{
+ struct async_state *stp = &AsyncState;
+
+ stp->mode = MODE_HUNT;
+ stp->length = 0;
+ stp->my_accmap = stp->his_accmap = 0xffffffff;
+}
+
+void
+SetLinkParams(struct lcpstate *lcp)
+{
+ struct async_state *stp = &AsyncState;
+
+ stp->my_accmap = lcp->want_accmap;
+ stp->his_accmap = lcp->his_accmap;
+}
+
+/*
+ * Encode into async HDLC byte code if necessary
+ */
+static void
+HdlcPutByte(u_char **cp, u_char c, int proto)
+{
+ u_char *wp;
+
+ wp = *cp;
+ if ((c < 0x20 && (proto == PROTO_LCP || (AsyncState.his_accmap & (1 << c))))
+ || (c == HDLC_ESC) || (c == HDLC_SYN)) {
+ *wp++ = HDLC_ESC;
+ c ^= HDLC_XOR;
+ }
+ if (EscMap[32] && EscMap[c >> 3] & (1 << (c & 7))) {
+ *wp++ = HDLC_ESC;
+ c ^= HDLC_XOR;
+ }
+ *wp++ = c;
+ *cp = wp;
+}
+
+void
+AsyncOutput(int pri, struct mbuf *bp, int proto)
+{
+ struct async_state *hs = &AsyncState;
+ u_char *cp, *sp, *ep;
+ struct mbuf *wp;
+ int cnt;
+
+ if (plength(bp) > HDLCSIZE) {
+ pfree(bp);
+ return;
+ }
+ cp = hs->xbuff;
+ ep = cp + HDLCSIZE - 10;
+ wp = bp;
+ *cp++ = HDLC_SYN;
+ while (wp) {
+ sp = MBUF_CTOP(wp);
+ for (cnt = wp->cnt; cnt > 0; cnt--) {
+ HdlcPutByte(&cp, *sp++, proto);
+ if (cp >= ep) {
+ pfree(bp);
+ return;
+ }
+ }
+ wp = wp->next;
+ }
+ *cp++ = HDLC_SYN;
+
+ cnt = cp - hs->xbuff;
+ LogDumpBuff(LogASYNC, "WriteModem", hs->xbuff, cnt);
+ WriteModem(pri, (char *) hs->xbuff, cnt);
+ ModemAddOutOctets(cnt);
+ pfree(bp);
+}
+
+static struct mbuf *
+AsyncDecode(u_char c)
+{
+ struct async_state *hs = &AsyncState;
+ struct mbuf *bp;
+
+ if ((hs->mode & MODE_HUNT) && c != HDLC_SYN)
+ return (NULLBUFF);
+
+ switch (c) {
+ case HDLC_SYN:
+ hs->mode &= ~MODE_HUNT;
+ if (hs->length) { /* packet is ready. */
+ bp = mballoc(hs->length, MB_ASYNC);
+ mbwrite(bp, hs->hbuff, hs->length);
+ hs->length = 0;
+ return (bp);
+ }
+ break;
+ case HDLC_ESC:
+ if (!(hs->mode & MODE_ESC)) {
+ hs->mode |= MODE_ESC;
+ break;
+ }
+ /* Fall into ... */
+ default:
+ if (hs->length >= HDLCSIZE) {
+ /* packet is too large, discard it */
+ LogPrintf(LogERROR, "Packet too large (%d), discarding.\n", hs->length);
+ hs->length = 0;
+ hs->mode = MODE_HUNT;
+ break;
+ }
+ if (hs->mode & MODE_ESC) {
+ c ^= HDLC_XOR;
+ hs->mode &= ~MODE_ESC;
+ }
+ hs->hbuff[hs->length++] = c;
+ break;
+ }
+ return NULLBUFF;
+}
+
+void
+AsyncInput(u_char *buff, int cnt)
+{
+ struct mbuf *bp;
+
+ ModemAddInOctets(cnt);
+ if (DEV_IS_SYNC) {
+ bp = mballoc(cnt, MB_ASYNC);
+ memcpy(MBUF_CTOP(bp), buff, cnt);
+ bp->cnt = cnt;
+ HdlcInput(bp);
+ } else {
+ while (cnt > 0) {
+ bp = AsyncDecode(*buff++);
+ if (bp)
+ HdlcInput(bp);
+ cnt--;
+ }
+ }
+}
diff --git a/usr.sbin/ppp/async.h b/usr.sbin/ppp/async.h
new file mode 100644
index 0000000..10b8e8c
--- /dev/null
+++ b/usr.sbin/ppp/async.h
@@ -0,0 +1,8 @@
+/*
+ * $Id: $
+ */
+
+extern void AsyncInit(void);
+extern void SetLinkParams(struct lcpstate *);
+extern void AsyncOutput(int, struct mbuf *, int);
+extern void AsyncInput(u_char *, int);
diff --git a/usr.sbin/ppp/auth.c b/usr.sbin/ppp/auth.c
new file mode 100644
index 0000000..89e4894
--- /dev/null
+++ b/usr.sbin/ppp/auth.c
@@ -0,0 +1,235 @@
+/*
+ * PPP Secret Key Module
+ *
+ * Written by Toshiharu OHNO (tony-o@iij.ad.jp)
+ *
+ * Copyright (C) 1994, Internet Initiative Japan, Inc. All rights reserverd.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the Internet Initiative Japan, Inc. The name of the
+ * IIJ may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: auth.c,v 1.20 1997/11/09 18:51:21 brian Exp $
+ *
+ * TODO:
+ * o Implement check against with registered IP addresses.
+ */
+#include <sys/param.h>
+#include <netinet/in.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "mbuf.h"
+#include "log.h"
+#include "defs.h"
+#include "timer.h"
+#include "fsm.h"
+#include "lcpproto.h"
+#include "ipcp.h"
+#include "loadalias.h"
+#include "command.h"
+#include "vars.h"
+#include "filter.h"
+#include "auth.h"
+#include "chat.h"
+#include "systems.h"
+
+void
+LocalAuthInit()
+{
+ if (*VarShortHost == '\0') {
+ char *p;
+
+ if (gethostname(VarShortHost, sizeof(VarShortHost))) {
+ VarLocalAuth = LOCAL_DENY;
+ return;
+ }
+
+ p = strchr(VarShortHost, '.');
+ if (p)
+ *p = '\0';
+ }
+
+ if (!(mode&(MODE_AUTO|MODE_DEDICATED|MODE_DIRECT)))
+ /* We're allowed in interactive and direct */
+ VarLocalAuth = LOCAL_AUTH;
+ else if (VarHaveLocalAuthKey)
+ VarLocalAuth = *VarLocalAuthKey == '\0' ? LOCAL_AUTH : LOCAL_NO_AUTH;
+ else
+ switch (LocalAuthValidate(SECRETFILE, VarShortHost, "")) {
+ case NOT_FOUND:
+ VarLocalAuth = LOCAL_DENY;
+ break;
+ case VALID:
+ VarLocalAuth = LOCAL_AUTH;
+ break;
+ case INVALID:
+ VarLocalAuth = LOCAL_NO_AUTH;
+ break;
+ }
+}
+
+LOCAL_AUTH_VALID
+LocalAuthValidate(char *fname, char *system, char *key)
+{
+ FILE *fp;
+ int n;
+ char *vector[3];
+ char buff[LINE_LEN];
+ LOCAL_AUTH_VALID rc;
+
+ rc = NOT_FOUND; /* No system entry */
+ fp = OpenSecret(fname);
+ if (fp == NULL)
+ return (rc);
+ while (fgets(buff, sizeof(buff), fp)) {
+ if (buff[0] == '#')
+ continue;
+ buff[strlen(buff) - 1] = 0;
+ memset(vector, '\0', sizeof(vector));
+ n = MakeArgs(buff, vector, VECSIZE(vector));
+ if (n < 1)
+ continue;
+ if (strcmp(vector[0], system) == 0) {
+ if ((vector[1] == (char *) NULL && (key == NULL || *key == '\0')) ||
+ (vector[1] != (char *) NULL && strcmp(vector[1], key) == 0)) {
+ rc = VALID; /* Valid */
+ } else {
+ rc = INVALID; /* Invalid */
+ }
+ break;
+ }
+ }
+ CloseSecret(fp);
+ return (rc);
+}
+
+int
+AuthValidate(char *fname, char *system, char *key)
+{
+ FILE *fp;
+ int n;
+ char *vector[4];
+ char buff[LINE_LEN];
+ char passwd[100];
+
+ fp = OpenSecret(fname);
+ if (fp == NULL)
+ return (0);
+ while (fgets(buff, sizeof(buff), fp)) {
+ if (buff[0] == '#')
+ continue;
+ buff[strlen(buff) - 1] = 0;
+ memset(vector, '\0', sizeof(vector));
+ n = MakeArgs(buff, vector, VECSIZE(vector));
+ if (n < 2)
+ continue;
+ if (strcmp(vector[0], system) == 0) {
+ ExpandString(vector[1], passwd, sizeof(passwd), 0);
+ if (strcmp(passwd, key) == 0) {
+ CloseSecret(fp);
+ memset(&DefHisAddress, '\0', sizeof(DefHisAddress));
+ n -= 2;
+ if (n > 0) {
+ if (ParseAddr(n--, &vector[2],
+ &DefHisAddress.ipaddr,
+ &DefHisAddress.mask,
+ &DefHisAddress.width) == 0) {
+ return (0); /* Invalid */
+ }
+ }
+ IpcpInit();
+ return (1); /* Valid */
+ }
+ }
+ }
+ CloseSecret(fp);
+ return (0); /* Invalid */
+}
+
+char *
+AuthGetSecret(char *fname, char *system, int len, int setaddr)
+{
+ FILE *fp;
+ int n;
+ char *vector[4];
+ char buff[LINE_LEN];
+ static char passwd[100];
+
+ fp = OpenSecret(fname);
+ if (fp == NULL)
+ return (NULL);
+ while (fgets(buff, sizeof(buff), fp)) {
+ if (buff[0] == '#')
+ continue;
+ buff[strlen(buff) - 1] = 0;
+ memset(vector, '\0', sizeof(vector));
+ n = MakeArgs(buff, vector, VECSIZE(vector));
+ if (n < 2)
+ continue;
+ if (strlen(vector[0]) == len && strncmp(vector[0], system, len) == 0) {
+ ExpandString(vector[1], passwd, sizeof(passwd), 0);
+ if (setaddr) {
+ memset(&DefHisAddress, '\0', sizeof(DefHisAddress));
+ }
+ n -= 2;
+ if (n > 0 && setaddr) {
+ LogPrintf(LogDEBUG, "AuthGetSecret: n = %d, %s\n", n, vector[2]);
+ if (ParseAddr(n--, &vector[2],
+ &DefHisAddress.ipaddr,
+ &DefHisAddress.mask,
+ &DefHisAddress.width) != 0)
+ IpcpInit();
+ }
+ return (passwd);
+ }
+ }
+ CloseSecret(fp);
+ return (NULL); /* Invalid */
+}
+
+static void
+AuthTimeout(struct authinfo *authp)
+{
+ struct pppTimer *tp;
+
+ tp = &authp->authtimer;
+ StopTimer(tp);
+ if (--authp->retry > 0) {
+ StartTimer(tp);
+ (authp->ChallengeFunc) (++authp->id);
+ }
+}
+
+void
+StartAuthChallenge(struct authinfo *authp)
+{
+ struct pppTimer *tp;
+
+ tp = &authp->authtimer;
+ StopTimer(tp);
+ tp->func = AuthTimeout;
+ tp->load = VarRetryTimeout * SECTICKS;
+ tp->state = TIMER_STOPPED;
+ tp->arg = (void *) authp;
+ StartTimer(tp);
+ authp->retry = 3;
+ authp->id = 1;
+ (authp->ChallengeFunc) (authp->id);
+}
+
+void
+StopAuthTimer(struct authinfo *authp)
+{
+ StopTimer(&authp->authtimer);
+}
diff --git a/usr.sbin/ppp/auth.h b/usr.sbin/ppp/auth.h
new file mode 100644
index 0000000..8a81cbc
--- /dev/null
+++ b/usr.sbin/ppp/auth.h
@@ -0,0 +1,41 @@
+/*
+ * Written by Toshiharu OHNO (tony-o@iij.ad.jp)
+ *
+ * Copyright (C) 1993, Internet Initiative Japan, Inc. All rights reserverd.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the Internet Initiative Japan. The name of the
+ * IIJ may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: auth.h,v 1.8 1997/09/04 00:38:18 brian Exp $
+ *
+ * TODO:
+ */
+
+typedef enum {
+ VALID,
+ INVALID,
+ NOT_FOUND
+} LOCAL_AUTH_VALID;
+
+struct authinfo {
+ void (*ChallengeFunc) ();
+ struct pppTimer authtimer;
+ int retry;
+ int id;
+};
+
+extern LOCAL_AUTH_VALID LocalAuthValidate(char *, char *, char *);
+extern void StopAuthTimer(struct authinfo *);
+extern void StartAuthChallenge(struct authinfo *);
+extern void LocalAuthInit(void);
+extern int AuthValidate(char *, char *, char *);
+extern char *AuthGetSecret(char *, char *, int, int);
diff --git a/usr.sbin/ppp/ccp.c b/usr.sbin/ppp/ccp.c
new file mode 100644
index 0000000..1ce6583
--- /dev/null
+++ b/usr.sbin/ppp/ccp.c
@@ -0,0 +1,275 @@
+/*
+ * PPP Compression Control Protocol (CCP) Module
+ *
+ * Written by Toshiharu OHNO (tony-o@iij.ad.jp)
+ *
+ * Copyright (C) 1994, Internet Initiative Japan, Inc. All rights reserverd.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the Internet Initiative Japan, Inc. The name of the
+ * IIJ may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: ccp.c,v 1.17 1997/10/26 01:02:10 brian Exp $
+ *
+ * TODO:
+ * o Support other compression protocols
+ */
+#include <sys/param.h>
+#include <netinet/in.h>
+
+#include <stdio.h>
+#include <string.h>
+
+#include "mbuf.h"
+#include "log.h"
+#include "defs.h"
+#include "timer.h"
+#include "fsm.h"
+#include "lcpproto.h"
+#include "lcp.h"
+#include "ccp.h"
+#include "phase.h"
+#include "loadalias.h"
+#include "command.h"
+#include "vars.h"
+#include "pred.h"
+
+struct ccpstate CcpInfo;
+
+static void CcpSendConfigReq(struct fsm *);
+static void CcpSendTerminateReq(struct fsm *);
+static void CcpSendTerminateAck(struct fsm *);
+static void CcpDecodeConfig(u_char *, int, int);
+static void CcpLayerStart(struct fsm *);
+static void CcpLayerFinish(struct fsm *);
+static void CcpLayerUp(struct fsm *);
+static void CcpLayerDown(struct fsm *);
+static void CcpInitRestartCounter(struct fsm *);
+
+#define REJECTED(p, x) (p->his_reject & (1<<x))
+
+struct fsm CcpFsm = {
+ "CCP",
+ PROTO_CCP,
+ CCP_MAXCODE,
+ OPEN_ACTIVE,
+ ST_INITIAL,
+ 0, 0, 0,
+ 0,
+ {0, 0, 0, NULL, NULL, NULL},
+ {0, 0, 0, NULL, NULL, NULL},
+ LogCCP,
+
+ CcpLayerUp,
+ CcpLayerDown,
+ CcpLayerStart,
+ CcpLayerFinish,
+ CcpInitRestartCounter,
+ CcpSendConfigReq,
+ CcpSendTerminateReq,
+ CcpSendTerminateAck,
+ CcpDecodeConfig,
+};
+
+static char const *cftypes[] = {
+ /* 0 */ "OUI", "PRED1", "PRED2", "PUDDLE",
+ /* 4 */ "???", "???", "???", "???",
+ /* 8 */ "???", "???", "???", "???",
+ /* 12 */ "???", "???", "???", "???",
+ /* 16 */ "HWPPC", "STAC", "MSPPC", "GAND",
+ /* 20 */ "V42BIS", "BSD",
+};
+
+int
+ReportCcpStatus()
+{
+ struct ccpstate *icp = &CcpInfo;
+ struct fsm *fp = &CcpFsm;
+
+ if (VarTerm) {
+ fprintf(VarTerm, "%s [%s]\n", fp->name, StateNames[fp->state]);
+ fprintf(VarTerm, "myproto = %s, hisproto = %s\n",
+ cftypes[icp->want_proto], cftypes[icp->his_proto]);
+ fprintf(VarTerm, "Input: %ld --> %ld, Output: %ld --> %ld\n",
+ icp->orgin, icp->compin, icp->orgout, icp->compout);
+ }
+ return 0;
+}
+
+void
+CcpInit()
+{
+ struct ccpstate *icp = &CcpInfo;
+
+ FsmInit(&CcpFsm);
+ memset(icp, '\0', sizeof(struct ccpstate));
+ if (Enabled(ConfPred1))
+ icp->want_proto = TY_PRED1;
+ CcpFsm.maxconfig = 10;
+}
+
+static void
+CcpInitRestartCounter(struct fsm *fp)
+{
+ fp->FsmTimer.load = VarRetryTimeout * SECTICKS;
+ fp->restart = 5;
+}
+
+static void
+CcpSendConfigReq(struct fsm *fp)
+{
+ u_char *cp;
+ struct ccpstate *icp = &CcpInfo;
+
+ cp = ReqBuff;
+ LogPrintf(LogCCP, "CcpSendConfigReq\n");
+ if (icp->want_proto && !REJECTED(icp, TY_PRED1)) {
+ *cp++ = TY_PRED1;
+ *cp++ = 2;
+ }
+ FsmOutput(fp, CODE_CONFIGREQ, fp->reqid++, ReqBuff, cp - ReqBuff);
+}
+
+void
+CcpSendResetReq(struct fsm *fp)
+{
+ LogPrintf(LogCCP, "CcpSendResetReq\n");
+ FsmOutput(fp, CODE_RESETREQ, fp->reqid, NULL, 0);
+}
+
+static void
+CcpSendTerminateReq(struct fsm *fp)
+{
+ /* XXX: No code yet */
+}
+
+static void
+CcpSendTerminateAck(struct fsm *fp)
+{
+ LogPrintf(LogCCP, "CcpSendTerminateAck\n");
+ FsmOutput(fp, CODE_TERMACK, fp->reqid++, NULL, 0);
+}
+
+void
+CcpRecvResetReq(struct fsm *fp)
+{
+ Pred1Init(2); /* Initialize Output part */
+}
+
+static void
+CcpLayerStart(struct fsm *fp)
+{
+ LogPrintf(LogCCP, "CcpLayerStart.\n");
+}
+
+static void
+CcpLayerFinish(struct fsm *fp)
+{
+ LogPrintf(LogCCP, "CcpLayerFinish.\n");
+}
+
+static void
+CcpLayerDown(struct fsm *fp)
+{
+ LogPrintf(LogCCP, "CcpLayerDown.\n");
+}
+
+/*
+ * Called when CCP has reached to OPEN state
+ */
+static void
+CcpLayerUp(struct fsm *fp)
+{
+ LogPrintf(LogCCP, "CcpLayerUp(%d).\n", fp->state);
+ LogPrintf(LogCCP, "myproto = %d, hisproto = %d\n",
+ CcpInfo.want_proto, CcpInfo.his_proto);
+ Pred1Init(3); /* Initialize Input and Output */
+}
+
+void
+CcpUp()
+{
+ FsmUp(&CcpFsm);
+ LogPrintf(LogCCP, "CCP Up event!!\n");
+}
+
+void
+CcpOpen()
+{
+ if (Enabled(ConfPred1))
+ FsmOpen(&CcpFsm);
+}
+
+static void
+CcpDecodeConfig(u_char *cp, int plen, int mode_type)
+{
+ int type, length;
+ char tbuff[100];
+
+ ackp = AckBuff;
+ nakp = NakBuff;
+ rejp = RejBuff;
+
+ while (plen >= sizeof(struct fsmconfig)) {
+ if (plen < 0)
+ break;
+ type = *cp;
+ length = cp[1];
+ if (type <= TY_BSD)
+ snprintf(tbuff, sizeof(tbuff), " %s[%d] ", cftypes[type], length);
+ else
+ snprintf(tbuff, sizeof(tbuff), " ");
+
+ LogPrintf(LogCCP, "%s\n", tbuff);
+
+ switch (type) {
+ case TY_PRED1:
+ switch (mode_type) {
+ case MODE_REQ:
+ if (Acceptable(ConfPred1)) {
+ memcpy(ackp, cp, length);
+ ackp += length;
+ CcpInfo.his_proto = type;
+ } else {
+ memcpy(rejp, cp, length);
+ rejp += length;
+ }
+ break;
+ case MODE_NAK:
+ case MODE_REJ:
+ CcpInfo.his_reject |= (1 << type);
+ CcpInfo.want_proto = 0;
+ break;
+ }
+ break;
+ case TY_BSD:
+ default:
+ CcpInfo.my_reject |= (1 << type);
+ memcpy(rejp, cp, length);
+ rejp += length;
+ break;
+ }
+ plen -= length;
+ cp += length;
+ }
+}
+
+void
+CcpInput(struct mbuf *bp)
+{
+ if (phase == PHASE_NETWORK)
+ FsmInput(&CcpFsm, bp);
+ else {
+ if (phase > PHASE_NETWORK)
+ LogPrintf(LogERROR, "Unexpected CCP in phase %d\n", phase);
+ pfree(bp);
+ }
+}
diff --git a/usr.sbin/ppp/ccp.h b/usr.sbin/ppp/ccp.h
new file mode 100644
index 0000000..dd8db01
--- /dev/null
+++ b/usr.sbin/ppp/ccp.h
@@ -0,0 +1,56 @@
+/*
+ * Written by Toshiharu OHNO (tony-o@iij.ad.jp)
+ *
+ * Copyright (C) 1993, Internet Initiative Japan, Inc. All rights reserverd.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the Internet Initiative Japan. The name of the
+ * IIJ may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: ccp.h,v 1.7 1997/08/25 00:29:06 brian Exp $
+ *
+ * TODO:
+ */
+
+#define CCP_MAXCODE CODE_RESETACK
+
+#define TY_OUI 0 /* OUI */
+#define TY_PRED1 1 /* Predictor type 1 */
+#define TY_PRED2 2 /* Predictor type 2 */
+#define TY_PUDDLE 3 /* Puddle Jumper */
+#define TY_HWPPC 16 /* Hewlett-Packard PPC */
+#define TY_STAC 17 /* Stac Electronics LZS */
+#define TY_MSPPC 18 /* Microsoft PPC */
+#define TY_GAND 19 /* Gandalf FZA */
+#define TY_V42BIS 20 /* V.42bis compression */
+#define TY_BSD 21 /* BSD LZW Compress */
+
+struct ccpstate {
+ u_long his_proto; /* peer's compression protocol */
+ u_long want_proto; /* my compression protocol */
+
+ u_long his_reject; /* Request codes rejected by peer */
+ u_long my_reject; /* Request codes I have rejected */
+
+ u_long orgout, compout;
+ u_long orgin, compin;
+};
+
+extern struct ccpstate CcpInfo;
+extern struct fsm CcpFsm;
+
+extern void CcpRecvResetReq(struct fsm *);
+extern void CcpSendResetReq(struct fsm *);
+extern void CcpInput(struct mbuf *);
+extern void CcpUp(void);
+extern void CcpOpen(void);
+extern void CcpInit(void);
+extern int ReportCcpStatus(void);
diff --git a/usr.sbin/ppp/chap.c b/usr.sbin/ppp/chap.c
new file mode 100644
index 0000000..972e071
--- /dev/null
+++ b/usr.sbin/ppp/chap.c
@@ -0,0 +1,311 @@
+/*
+ * PPP CHAP Module
+ *
+ * Written by Toshiharu OHNO (tony-o@iij.ad.jp)
+ *
+ * Copyright (C) 1993, Internet Initiative Japan, Inc. All rights reserverd.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the Internet Initiative Japan, Inc. The name of the
+ * IIJ may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: chap.c,v 1.24 1997/10/26 01:02:16 brian Exp $
+ *
+ * TODO:
+ */
+#include <sys/param.h>
+#include <netinet/in.h>
+
+#include <ctype.h>
+#ifdef HAVE_DES
+#include <md4.h>
+#endif
+#include <md5.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#ifdef __OpenBSD__
+#include <util.h>
+#else
+#include <libutil.h>
+#endif
+#include <utmp.h>
+
+#include "mbuf.h"
+#include "log.h"
+#include "defs.h"
+#include "timer.h"
+#include "fsm.h"
+#include "chap.h"
+#include "chap_ms.h"
+#include "lcpproto.h"
+#include "lcp.h"
+#include "hdlc.h"
+#include "phase.h"
+#include "loadalias.h"
+#include "command.h"
+#include "vars.h"
+#include "auth.h"
+
+static char *chapcodes[] = {
+ "???", "CHALLENGE", "RESPONSE", "SUCCESS", "FAILURE"
+};
+
+static void
+ChapOutput(u_int code, u_int id, u_char * ptr, int count)
+{
+ int plen;
+ struct fsmheader lh;
+ struct mbuf *bp;
+
+ plen = sizeof(struct fsmheader) + count;
+ lh.code = code;
+ lh.id = id;
+ lh.length = htons(plen);
+ bp = mballoc(plen, MB_FSM);
+ memcpy(MBUF_CTOP(bp), &lh, sizeof(struct fsmheader));
+ if (count)
+ memcpy(MBUF_CTOP(bp) + sizeof(struct fsmheader), ptr, count);
+ LogDumpBp(LogDEBUG, "ChapOutput", bp);
+ LogPrintf(LogLCP, "ChapOutput: %s\n", chapcodes[code]);
+ HdlcOutput(PRI_LINK, PROTO_CHAP, bp);
+}
+
+
+static char challenge_data[80];
+static int challenge_len;
+
+static void
+SendChapChallenge(int chapid)
+{
+ int len, i;
+ char *cp;
+
+ randinit();
+ cp = challenge_data;
+ *cp++ = challenge_len = random() % 32 + 16;
+ for (i = 0; i < challenge_len; i++)
+ *cp++ = random() & 0xff;
+ len = strlen(VarAuthName);
+ memcpy(cp, VarAuthName, len);
+ cp += len;
+ ChapOutput(CHAP_CHALLENGE, chapid, challenge_data, cp - challenge_data);
+}
+
+struct authinfo AuthChapInfo = {
+ SendChapChallenge,
+};
+
+static void
+RecvChapTalk(struct fsmheader *chp, struct mbuf *bp)
+{
+ int valsize, len;
+ int arglen, keylen, namelen;
+ char *cp, *argp, *ap, *name, *digest;
+ char *keyp;
+ MD5_CTX MD5context; /* context for MD5 */
+ char answer[100];
+ char cdigest[16];
+#ifdef HAVE_DES
+ int ix;
+ MD4_CTX MD4context; /* context for MD4 */
+#endif
+
+ len = ntohs(chp->length);
+ LogPrintf(LogDEBUG, "RecvChapTalk: length: %d\n", len);
+ arglen = len - sizeof(struct fsmheader);
+ cp = (char *) MBUF_CTOP(bp);
+ valsize = *cp++ & 255;
+ name = cp + valsize;
+ namelen = arglen - valsize - 1;
+ name[namelen] = 0;
+ LogPrintf(LogPHASE, " Valsize = %d, Name = %s\n", valsize, name);
+
+ switch (chp->code) {
+ case CHAP_CHALLENGE:
+ keyp = VarAuthKey;
+ keylen = strlen(VarAuthKey);
+ name = VarAuthName;
+ namelen = strlen(VarAuthName);
+
+#ifdef HAVE_DES
+ if (VarMSChap)
+ argp = malloc(1 + namelen + MS_CHAP_RESPONSE_LEN);
+ else
+#endif
+ argp = malloc(1 + valsize + namelen + 16);
+
+ if (argp == NULL) {
+ ChapOutput(CHAP_FAILURE, chp->id, "Out of memory!", 14);
+ return;
+ }
+#ifdef HAVE_DES
+ if (VarMSChap) {
+ digest = argp; /* this is the response */
+ *digest++ = MS_CHAP_RESPONSE_LEN; /* 49 */
+ memset(digest, '\0', 24);
+ digest += 24;
+
+ ap = answer; /* this is the challenge */
+ memcpy(ap, keyp, keylen);
+ ap += 2 * keylen;
+ memcpy(ap, cp, valsize);
+ LogDumpBuff(LogDEBUG, "recv", ap, valsize);
+ ap += valsize;
+ for (ix = keylen; ix > 0 ; ix--) {
+ answer[2*ix-2] = answer[ix-1];
+ answer[2*ix-1] = 0;
+ }
+ MD4Init(&MD4context);
+ MD4Update(&MD4context, answer, 2 * keylen);
+ MD4Final(digest, &MD4context);
+ memcpy(digest + 25, name, namelen);
+ ap += 2 * keylen;
+ ChapMS(digest, answer + 2 * keylen, valsize);
+ LogDumpBuff(LogDEBUG, "answer", digest, 24);
+ ChapOutput(CHAP_RESPONSE, chp->id, argp, namelen + MS_CHAP_RESPONSE_LEN + 1);
+ } else {
+#endif
+ digest = argp;
+ *digest++ = 16; /* value size */
+ ap = answer;
+ *ap++ = chp->id;
+ memcpy(ap, keyp, keylen);
+ ap += keylen;
+ memcpy(ap, cp, valsize);
+ LogDumpBuff(LogDEBUG, "recv", ap, valsize);
+ ap += valsize;
+ MD5Init(&MD5context);
+ MD5Update(&MD5context, answer, ap - answer);
+ MD5Final(digest, &MD5context);
+ LogDumpBuff(LogDEBUG, "answer", digest, 16);
+ memcpy(digest + 16, name, namelen);
+ ap += namelen;
+ /* Send answer to the peer */
+ ChapOutput(CHAP_RESPONSE, chp->id, argp, namelen + 17);
+#ifdef HAVE_DES
+ }
+#endif
+ free(argp);
+ break;
+ case CHAP_RESPONSE:
+ /*
+ * Get a secret key corresponds to the peer
+ */
+ keyp = AuthGetSecret(SECRETFILE, name, namelen, chp->code == CHAP_RESPONSE);
+ if (keyp) {
+ /*
+ * Compute correct digest value
+ */
+ keylen = strlen(keyp);
+ ap = answer;
+ *ap++ = chp->id;
+ memcpy(ap, keyp, keylen);
+ ap += keylen;
+ MD5Init(&MD5context);
+ MD5Update(&MD5context, answer, ap - answer);
+ MD5Update(&MD5context, challenge_data + 1, challenge_len);
+ MD5Final(cdigest, &MD5context);
+ LogDumpBuff(LogDEBUG, "got", cp, 16);
+ LogDumpBuff(LogDEBUG, "expect", cdigest, 16);
+
+ /*
+ * Compare with the response
+ */
+ if (memcmp(cp, cdigest, 16) == 0) {
+ ChapOutput(CHAP_SUCCESS, chp->id, "Welcome!!", 10);
+ if ((mode & MODE_DIRECT) && isatty(modem) && Enabled(ConfUtmp))
+ if (Utmp)
+ LogPrintf(LogERROR, "Oops, already logged in on %s\n",
+ VarBaseDevice);
+ else {
+ struct utmp ut;
+ memset(&ut, 0, sizeof(ut));
+ time(&ut.ut_time);
+ strncpy(ut.ut_name, name, sizeof(ut.ut_name)-1);
+ strncpy(ut.ut_line, VarBaseDevice, sizeof(ut.ut_line)-1);
+ if (logout(ut.ut_line))
+ logwtmp(ut.ut_line, "", "");
+ login(&ut);
+ Utmp = 1;
+ }
+ NewPhase(PHASE_NETWORK);
+ break;
+ }
+ }
+
+ /*
+ * Peer is not registerd, or response digest is wrong.
+ */
+ ChapOutput(CHAP_FAILURE, chp->id, "Invalid!!", 9);
+ reconnect(RECON_FALSE);
+ LcpClose();
+ break;
+ }
+}
+
+static void
+RecvChapResult(struct fsmheader *chp, struct mbuf *bp)
+{
+ int len;
+ struct lcpstate *lcp = &LcpInfo;
+
+ len = ntohs(chp->length);
+ LogPrintf(LogDEBUG, "RecvChapResult: length: %d\n", len);
+ if (chp->code == CHAP_SUCCESS) {
+ if (lcp->auth_iwait == PROTO_CHAP) {
+ lcp->auth_iwait = 0;
+ if (lcp->auth_ineed == 0)
+ NewPhase(PHASE_NETWORK);
+ }
+ } else {
+
+ /*
+ * Maybe, we shoud close LCP. Of cause, peer may take close action, too.
+ */
+ ;
+ }
+}
+
+void
+ChapInput(struct mbuf *bp)
+{
+ int len = plength(bp);
+ struct fsmheader *chp;
+
+ if (len >= sizeof(struct fsmheader)) {
+ chp = (struct fsmheader *) MBUF_CTOP(bp);
+ if (len >= ntohs(chp->length)) {
+ if (chp->code < 1 || chp->code > 4)
+ chp->code = 0;
+ LogPrintf(LogLCP, "ChapInput: %s\n", chapcodes[chp->code]);
+
+ bp->offset += sizeof(struct fsmheader);
+ bp->cnt -= sizeof(struct fsmheader);
+
+ switch (chp->code) {
+ case CHAP_RESPONSE:
+ StopAuthTimer(&AuthChapInfo);
+ /* Fall into.. */
+ case CHAP_CHALLENGE:
+ RecvChapTalk(chp, bp);
+ break;
+ case CHAP_SUCCESS:
+ case CHAP_FAILURE:
+ RecvChapResult(chp, bp);
+ break;
+ }
+ }
+ }
+ pfree(bp);
+}
diff --git a/usr.sbin/ppp/chap.h b/usr.sbin/ppp/chap.h
new file mode 100644
index 0000000..ba178b6
--- /dev/null
+++ b/usr.sbin/ppp/chap.h
@@ -0,0 +1,30 @@
+/*
+ * Written by Toshiharu OHNO (tony-o@iij.ad.jp)
+ *
+ * Copyright (C) 1993, Internet Initiative Japan, Inc. All rights reserverd.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the Internet Initiative Japan. The name of the
+ * IIJ may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: chap.h,v 1.8 1997/10/26 01:02:19 brian Exp $
+ *
+ * TODO:
+ */
+
+#define CHAP_CHALLENGE 1
+#define CHAP_RESPONSE 2
+#define CHAP_SUCCESS 3
+#define CHAP_FAILURE 4
+
+extern struct authinfo AuthChapInfo;
+
+extern void ChapInput(struct mbuf *);
diff --git a/usr.sbin/ppp/chap_ms.c b/usr.sbin/ppp/chap_ms.c
new file mode 100644
index 0000000..90cf2da
--- /dev/null
+++ b/usr.sbin/ppp/chap_ms.c
@@ -0,0 +1,114 @@
+/*
+ * 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.
+ *
+ * $Id: $
+ *
+ */
+
+#include <sys/types.h>
+
+#include <des.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/time.h>
+#include <syslog.h>
+
+#include "mbuf.h"
+#include "timer.h"
+#include "chap.h"
+#include "chap_ms.h"
+
+/* unused, for documentation only */
+/* only NTResp is filled in for FreeBSD */
+typedef struct {
+ u_char LANManResp[24];
+ u_char NTResp[24];
+ u_char UseNT; /* If 1, ignore the LANMan response field */
+} MS_ChapResponse;
+
+static void DesEncrypt(u_char *, u_char *, u_char *);
+static void MakeKey(u_char *, u_char *);
+
+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);
+}
+
+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 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);
+}
+
+/* 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
+ChapMS(char *passwordHash, char *challenge, int challenge_len)
+{
+ u_char response[24];
+
+ ChallengeResponse(challenge, passwordHash, response);
+ memcpy(passwordHash, response, 24);
+ passwordHash += 24;
+ *passwordHash = 1;
+}
diff --git a/usr.sbin/ppp/chap_ms.h b/usr.sbin/ppp/chap_ms.h
new file mode 100644
index 0000000..0f6c318
--- /dev/null
+++ b/usr.sbin/ppp/chap_ms.h
@@ -0,0 +1,31 @@
+/*
+ * chap.h - Cryptographic Handshake Authentication Protocol definitions.
+ *
+ * Copyright (c) 1995 Eric Rosenquist, Strata Software Limited.
+ * http://www.strataware.com/
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Eric Rosenquist. The name of the author may not be used to
+ * endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: chap_ms.h,v 1.1 1997/09/25 00:58:20 brian Exp $
+ */
+
+/* Max # of (Unicode) chars in an NT password */
+#define MAX_NT_PASSWORD 256
+
+/* Don't rely on sizeof(MS_ChapResponse) in case of struct padding */
+#define MS_CHAP_RESPONSE_LEN 49
+
+extern void ChapMS(char *, char *, int);
diff --git a/usr.sbin/ppp/chat.c b/usr.sbin/ppp/chat.c
new file mode 100644
index 0000000..6870af1
--- /dev/null
+++ b/usr.sbin/ppp/chat.c
@@ -0,0 +1,640 @@
+/*
+ * Written by Toshiharu OHNO (tony-o@iij.ad.jp)
+ *
+ * Copyright (C) 1993, Internet Initiative Japan, Inc. All rights reserverd.
+ *
+ * Most of codes are derived from chat.c by Karl Fox (karl@MorningStar.Com).
+ *
+ * Chat -- a program for automatic session establishment (i.e. dial
+ * the phone and log in).
+ *
+ * This software is in the public domain.
+ *
+ * Please send all bug reports, requests for information, etc. to:
+ *
+ * Karl Fox <karl@MorningStar.Com>
+ * Morning Star Technologies, Inc.
+ * 1760 Zollinger Road
+ * Columbus, OH 43221
+ * (614)451-1883
+ *
+ * $Id: chat.c,v 1.37 1997/11/09 06:22:39 brian Exp $
+ *
+ * TODO:
+ * o Support more UUCP compatible control sequences.
+ * o Dialing shoud not block monitor process.
+ * o Reading modem by select should be unified into main.c
+ */
+#include <sys/param.h>
+#include <netinet/in.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <setjmp.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/uio.h>
+#include <sys/wait.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "mbuf.h"
+#include "log.h"
+#include "defs.h"
+#include "timer.h"
+#include "loadalias.h"
+#include "command.h"
+#include "vars.h"
+#include "chat.h"
+#include "sig.h"
+#include "chat.h"
+#include "modem.h"
+
+#ifndef isblank
+#define isblank(c) ((c) == '\t' || (c) == ' ')
+#endif
+
+
+#define IBSIZE LINE_LEN
+
+static int TimeoutSec;
+static int abort_next, timeout_next;
+static int numaborts;
+static char *AbortStrings[50];
+static char inbuff[IBSIZE * 2 + 1];
+
+#define MATCH 1
+#define NOMATCH 0
+#define ABORT -1
+
+static char *
+findblank(char *p, int instring)
+{
+ if (instring) {
+ while (*p) {
+ if (*p == '\\') {
+ strcpy(p, p + 1);
+ if (!*p)
+ break;
+ } else if (*p == '"')
+ return (p);
+ p++;
+ }
+ } else {
+ while (*p) {
+ if (isblank(*p))
+ return (p);
+ p++;
+ }
+ }
+ return p;
+}
+
+int
+MakeArgs(char *script, char **pvect, int maxargs)
+{
+ int nargs, nb;
+ int instring;
+
+ nargs = 0;
+ while (*script) {
+ nb = strspn(script, " \t");
+ script += nb;
+ if (*script) {
+ if (*script == '"') {
+ instring = 1;
+ script++;
+ if (*script == '\0')
+ break; /* Shouldn't return here. Need to null
+ * terminate below */
+ } else
+ instring = 0;
+ if (nargs >= maxargs - 1)
+ break;
+ *pvect++ = script;
+ nargs++;
+ script = findblank(script, instring);
+ if (*script)
+ *script++ = '\0';
+ }
+ }
+ *pvect = NULL;
+ return nargs;
+}
+
+/*
+ * \c don't add a cr
+ * \d Sleep a little (delay 2 seconds
+ * \n Line feed character
+ * \P Auth Key password
+ * \p pause 0.25 sec
+ * \r Carrige return character
+ * \s Space character
+ * \T Telephone number(s) (defined via `set phone')
+ * \t Tab character
+ * \U Auth User
+ */
+char *
+ExpandString(char *str, char *result, int reslen, int sendmode)
+{
+ int addcr = 0;
+ char *phone;
+
+ result[--reslen] = '\0';
+ if (sendmode)
+ addcr = 1;
+ while (*str && reslen > 0) {
+ switch (*str) {
+ case '\\':
+ str++;
+ switch (*str) {
+ case 'c':
+ if (sendmode)
+ addcr = 0;
+ break;
+ case 'd': /* Delay 2 seconds */
+ nointr_sleep(2);
+ break;
+ case 'p':
+ nointr_usleep(250000);
+ break; /* Pause 0.25 sec */
+ case 'n':
+ *result++ = '\n';
+ reslen--;
+ break;
+ case 'r':
+ *result++ = '\r';
+ reslen--;
+ break;
+ case 's':
+ *result++ = ' ';
+ reslen--;
+ break;
+ case 't':
+ *result++ = '\t';
+ reslen--;
+ break;
+ case 'P':
+ strncpy(result, VarAuthKey, reslen);
+ reslen -= strlen(result);
+ result += strlen(result);
+ break;
+ case 'T':
+ if (VarAltPhone == NULL) {
+ if (VarNextPhone == NULL) {
+ strcpy(VarPhoneCopy, VarPhoneList);
+ VarNextPhone = VarPhoneCopy;
+ }
+ VarAltPhone = strsep(&VarNextPhone, ":");
+ }
+ phone = strsep(&VarAltPhone, "|");
+ strncpy(result, phone, reslen);
+ reslen -= strlen(result);
+ result += strlen(result);
+ if (VarTerm)
+ fprintf(VarTerm, "Phone: %s\n", phone);
+ LogPrintf(LogPHASE, "Phone: %s\n", phone);
+ break;
+ case 'U':
+ strncpy(result, VarAuthName, reslen);
+ reslen -= strlen(result);
+ result += strlen(result);
+ break;
+ default:
+ reslen--;
+ *result++ = *str;
+ break;
+ }
+ if (*str)
+ str++;
+ break;
+ case '^':
+ str++;
+ if (*str) {
+ *result++ = *str++ & 0x1f;
+ reslen--;
+ }
+ break;
+ default:
+ *result++ = *str++;
+ reslen--;
+ break;
+ }
+ }
+ if (--reslen > 0) {
+ if (addcr)
+ *result++ = '\r';
+ }
+ if (--reslen > 0)
+ *result++ = '\0';
+ return (result);
+}
+
+#define MAXLOGBUFF LINE_LEN
+static char logbuff[MAXLOGBUFF];
+static int loglen = 0;
+
+static void
+clear_log()
+{
+ memset(logbuff, 0, MAXLOGBUFF);
+ loglen = 0;
+}
+
+static void
+flush_log()
+{
+ if (LogIsKept(LogCONNECT))
+ LogPrintf(LogCONNECT, "%s\n", logbuff);
+ else if (LogIsKept(LogCARRIER) && strstr(logbuff, "CARRIER"))
+ LogPrintf(LogCARRIER, "%s\n", logbuff);
+
+ clear_log();
+}
+
+static void
+connect_log(char *str, int single_p)
+{
+ int space = MAXLOGBUFF - loglen - 1;
+
+ while (space--) {
+ if (*str == '\n') {
+ flush_log();
+ } else {
+ logbuff[loglen++] = *str;
+ }
+ if (single_p || !*++str)
+ break;
+ }
+ if (!space)
+ flush_log();
+}
+
+static int
+WaitforString(char *estr)
+{
+ struct timeval timeout;
+ char *s, *str, ch;
+ char *inp;
+ fd_set rfds;
+ int i, nfds, nb;
+ char buff[IBSIZE];
+
+
+#ifdef SIGALRM
+ int omask;
+
+ omask = sigblock(sigmask(SIGALRM));
+#endif
+ clear_log();
+ (void) ExpandString(estr, buff, sizeof(buff), 0);
+ LogPrintf(LogCHAT, "Wait for (%d): %s --> %s\n", TimeoutSec, estr, buff);
+ str = buff;
+ inp = inbuff;
+
+ if (strlen(str) >= IBSIZE) {
+ str[IBSIZE - 1] = 0;
+ LogPrintf(LogCHAT, "Truncating String to %d character: %s\n", IBSIZE, str);
+ }
+ nfds = modem + 1;
+ s = str;
+ for (;;) {
+ FD_ZERO(&rfds);
+ FD_SET(modem, &rfds);
+
+ /*
+ * Because it is not clear whether select() modifies timeout value, it is
+ * better to initialize timeout values everytime.
+ */
+ timeout.tv_sec = TimeoutSec;
+ timeout.tv_usec = 0;
+ i = select(nfds, &rfds, NULL, NULL, &timeout);
+#ifdef notdef
+ TimerService();
+#endif
+ if (i < 0) {
+#ifdef SIGALRM
+ if (errno == EINTR)
+ continue;
+ sigsetmask(omask);
+#endif
+ LogPrintf(LogERROR, "WaitForString: select(): %s\n", strerror(errno));
+ *inp = 0;
+ return (NOMATCH);
+ } else if (i == 0) { /* Timeout reached! */
+ *inp = 0;
+ if (inp != inbuff)
+ LogPrintf(LogCHAT, "Got: %s\n", inbuff);
+ LogPrintf(LogCHAT, "Can't get (%d).\n", timeout.tv_sec);
+#ifdef SIGALRM
+ sigsetmask(omask);
+#endif
+ return (NOMATCH);
+ }
+ if (FD_ISSET(modem, &rfds)) { /* got something */
+ if (DEV_IS_SYNC) {
+ int length;
+
+ if ((length = strlen(inbuff)) > IBSIZE) {
+ /* shuffle down next part */
+ memcpy(inbuff, &(inbuff[IBSIZE]), IBSIZE + 1);
+ length = strlen(inbuff);
+ }
+ nb = read(modem, &(inbuff[length]), IBSIZE);
+ inbuff[nb + length] = 0;
+ connect_log(inbuff, 0);
+ if (strstr(inbuff, str)) {
+#ifdef SIGALRM
+ sigsetmask(omask);
+#endif
+ flush_log();
+ return (MATCH);
+ }
+ for (i = 0; i < numaborts; i++) {
+ if (strstr(inbuff, AbortStrings[i])) {
+ LogPrintf(LogCHAT, "Abort: %s\n", AbortStrings[i]);
+#ifdef SIGALRM
+ sigsetmask(omask);
+#endif
+ flush_log();
+ return (ABORT);
+ }
+ }
+ } else {
+ if (read(modem, &ch, 1) < 0) {
+ LogPrintf(LogERROR, "read error: %s\n", strerror(errno));
+ *inp = '\0';
+ return (NOMATCH);
+ }
+ connect_log(&ch, 1);
+ *inp++ = ch;
+ if (ch == *s) {
+ s++;
+ if (*s == '\0') {
+#ifdef SIGALRM
+ sigsetmask(omask);
+#endif
+ *inp = 0;
+ flush_log();
+ return (MATCH);
+ }
+ } else
+ s = str;
+ if (inp == inbuff + IBSIZE) {
+ memcpy(inbuff, inp - 100, 100);
+ inp = inbuff + 100;
+ }
+ if (s == str) {
+ for (i = 0; i < numaborts; i++) { /* Look for Abort strings */
+ int len;
+ char *s1;
+
+ s1 = AbortStrings[i];
+ len = strlen(s1);
+ if ((len <= inp - inbuff) && (strncmp(inp - len, s1, len) == 0)) {
+ LogPrintf(LogCHAT, "Abort: %s\n", s1);
+ *inp = 0;
+#ifdef SIGALRM
+ sigsetmask(omask);
+#endif
+ flush_log();
+ return (ABORT);
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+static void
+ExecStr(char *command, char *out)
+{
+ int pid;
+ int fids[2];
+ char *vector[20];
+ int stat, nb;
+ char *cp;
+ char tmp[300];
+
+ cp = inbuff + strlen(inbuff) - 1;
+ while (cp > inbuff) {
+ if (*cp < ' ' && *cp != '\t') {
+ cp++;
+ break;
+ }
+ cp--;
+ }
+ if (snprintf(tmp, sizeof tmp, "%s %s", command, cp) >= sizeof tmp) {
+ LogPrintf(LogCHAT, "Too long string to ExecStr: \"%s\"\n", command);
+ return;
+ }
+ (void) MakeArgs(tmp, vector, VECSIZE(vector));
+
+ if (pipe(fids) < 0) {
+ LogPrintf(LogCHAT, "Unable to create pipe in ExecStr: %s\n",
+ strerror(errno));
+ return;
+ }
+ pid = fork();
+ if (pid == 0) {
+ TermTimerService();
+ signal(SIGINT, SIG_DFL);
+ signal(SIGQUIT, SIG_DFL);
+ signal(SIGTERM, SIG_DFL);
+ signal(SIGHUP, SIG_DFL);
+ signal(SIGALRM, SIG_DFL);
+ close(fids[0]);
+ if (dup2(fids[1], 1) < 0) {
+ LogPrintf(LogCHAT, "dup2(fids[1], 1) in ExecStr: %s\n", strerror(errno));
+ return;
+ }
+ close(fids[1]);
+ nb = open("/dev/tty", O_RDWR);
+ if (dup2(nb, 0) < 0) {
+ LogPrintf(LogCHAT, "dup2(nb, 0) in ExecStr: %s\n", strerror(errno));
+ return;
+ }
+ setuid(geteuid());
+ LogPrintf(LogCHAT, "exec: %s\n", command);
+ pid = execvp(command, vector);
+ LogPrintf(LogCHAT, "execvp failed for (%d/%d): %s\n", pid, errno, command);
+ exit(127);
+ } else {
+ close(fids[1]);
+ for (;;) {
+ nb = read(fids[0], out, 1);
+ if (nb <= 0)
+ break;
+ out++;
+ }
+ *out = '\0';
+ close(fids[0]);
+ close(fids[1]);
+ waitpid(pid, &stat, WNOHANG);
+ }
+}
+
+static void
+SendString(char *str)
+{
+ char *cp;
+ int on;
+ char buff[LINE_LEN];
+
+ if (abort_next) {
+ abort_next = 0;
+ ExpandString(str, buff, sizeof(buff), 0);
+ AbortStrings[numaborts++] = strdup(buff);
+ } else if (timeout_next) {
+ timeout_next = 0;
+ TimeoutSec = atoi(str);
+ if (TimeoutSec <= 0)
+ TimeoutSec = 30;
+ } else {
+ if (*str == '!') {
+ (void) ExpandString(str + 1, buff + 2, sizeof(buff) - 2, 0);
+ ExecStr(buff + 2, buff + 2);
+ } else {
+ (void) ExpandString(str, buff + 2, sizeof(buff) - 2, 1);
+ }
+ if (strstr(str, "\\P")) /* Do not log the password itself. */
+ LogPrintf(LogCHAT, "sending: %s\n", str);
+ else
+ LogPrintf(LogCHAT, "sending: %s\n", buff + 2);
+ cp = buff;
+ if (DEV_IS_SYNC)
+ memcpy(buff, "\377\003", 2); /* Prepend HDLC header */
+ else
+ cp += 2;
+ on = strlen(cp);
+ write(modem, cp, on);
+ }
+}
+
+static int
+ExpectString(char *str)
+{
+ char *minus;
+ int state;
+
+ if (strcmp(str, "ABORT") == 0) {
+ ++abort_next;
+ return (MATCH);
+ }
+ if (strcmp(str, "TIMEOUT") == 0) {
+ ++timeout_next;
+ return (MATCH);
+ }
+ LogPrintf(LogCHAT, "Expecting %s\n", str);
+ while (*str) {
+
+ /*
+ * Check whether if string contains sub-send-expect.
+ */
+ for (minus = str; *minus; minus++) {
+ if (*minus == '-') {
+ if (minus == str || minus[-1] != '\\')
+ break;
+ }
+ }
+ if (*minus == '-') { /* We have sub-send-expect. */
+ *minus++ = '\0';
+ state = WaitforString(str);
+ if (state != NOMATCH)
+ return (state);
+
+ /*
+ * Can't get expect string. Sendout send part.
+ */
+ str = minus;
+ for (minus = str; *minus; minus++) {
+ if (*minus == '-') {
+ if (minus == str || minus[-1] != '\\')
+ break;
+ }
+ }
+ if (*minus == '-') {
+ *minus++ = '\0';
+ SendString(str);
+ str = minus;
+ } else {
+ SendString(str);
+ return (MATCH);
+ }
+ } else {
+
+ /*
+ * Simple case. Wait for string.
+ */
+ return (WaitforString(str));
+ }
+ }
+ return (MATCH);
+}
+
+static jmp_buf ChatEnv;
+static void (*oint) (int);
+
+static void
+StopDial(int sig)
+{
+ LogPrintf(LogPHASE, "DoChat: Caught signal %d, abort connect\n", sig);
+ longjmp(ChatEnv, 1);
+}
+
+int
+DoChat(char *script)
+{
+ char *vector[40];
+ char **argv;
+ int argc, n, state;
+
+ if (!script || !*script)
+ return MATCH;
+
+ /* While we're chatting, we want an INT to fail us */
+ if (setjmp(ChatEnv)) {
+ signal(SIGINT, oint);
+ return (-1);
+ }
+ oint = signal(SIGINT, StopDial);
+
+ timeout_next = abort_next = 0;
+ for (n = 0; AbortStrings[n]; n++) {
+ free(AbortStrings[n]);
+ AbortStrings[n] = NULL;
+ }
+ numaborts = 0;
+
+ memset(vector, '\0', sizeof(vector));
+ n = MakeArgs(script, vector, VECSIZE(vector));
+ argc = n;
+ argv = vector;
+ TimeoutSec = 30;
+ while (*argv) {
+ if (strcmp(*argv, "P_ZERO") == 0 ||
+ strcmp(*argv, "P_ODD") == 0 || strcmp(*argv, "P_EVEN") == 0) {
+ ChangeParity(*argv++);
+ continue;
+ }
+ state = ExpectString(*argv++);
+ switch (state) {
+ case MATCH:
+ if (*argv)
+ SendString(*argv++);
+ break;
+ case ABORT:
+ case NOMATCH:
+ signal(SIGINT, oint);
+ return (NOMATCH);
+ }
+ }
+ signal(SIGINT, oint);
+ return (MATCH);
+}
diff --git a/usr.sbin/ppp/chat.h b/usr.sbin/ppp/chat.h
new file mode 100644
index 0000000..371a468
--- /dev/null
+++ b/usr.sbin/ppp/chat.h
@@ -0,0 +1,29 @@
+/*
+ * Written by Toshiharu OHNO (tony-o@iij.ad.jp)
+ *
+ * Copyright (C) 1993, Internet Initiative Japan, Inc. All rights reserverd.
+ *
+ * Most of codes are derived from chat.c by Karl Fox (karl@MorningStar.Com).
+ *
+ * Chat -- a program for automatic session establishment (i.e. dial
+ * the phone and log in).
+ *
+ * This software is in the public domain.
+ *
+ * Please send all bug reports, requests for information, etc. to:
+ *
+ * Karl Fox <karl@MorningStar.Com>
+ * Morning Star Technologies, Inc.
+ * 1760 Zollinger Road
+ * Columbus, OH 43221
+ * (614)451-1883
+ *
+ * $Id: chat.h,v 1.7 1997/08/25 00:29:07 brian Exp $
+ *
+ */
+
+#define VECSIZE(v) (sizeof(v) / sizeof(v[0]))
+
+extern char *ExpandString(char *, char *, int, int);
+extern int MakeArgs(char *, char **, int);
+extern int DoChat(char *);
diff --git a/usr.sbin/ppp/command.c b/usr.sbin/ppp/command.c
new file mode 100644
index 0000000..1c527da
--- /dev/null
+++ b/usr.sbin/ppp/command.c
@@ -0,0 +1,1550 @@
+/*
+ * PPP User command processing module
+ *
+ * Written by Toshiharu OHNO (tony-o@iij.ad.jp)
+ *
+ * Copyright (C) 1993, Internet Initiative Japan, Inc. All rights reserverd.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the Internet Initiative Japan, Inc. The name of the
+ * IIJ may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: command.c,v 1.96 1997/11/09 22:56:15 brian Exp $
+ *
+ */
+#include <sys/param.h>
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <arpa/inet.h>
+#include <sys/socket.h>
+#include <net/route.h>
+#include <netdb.h>
+
+#include <alias.h>
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+#include <termios.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "mbuf.h"
+#include "log.h"
+#include "defs.h"
+#include "timer.h"
+#include "fsm.h"
+#include "phase.h"
+#include "lcp.h"
+#include "ipcp.h"
+#include "modem.h"
+#include "command.h"
+#include "filter.h"
+#include "alias_cmd.h"
+#include "hdlc.h"
+#include "loadalias.h"
+#include "vars.h"
+#include "systems.h"
+#include "chat.h"
+#include "os.h"
+#include "server.h"
+#include "main.h"
+#include "route.h"
+#include "ccp.h"
+#include "ip.h"
+#include "slcompress.h"
+#include "auth.h"
+
+struct in_addr ifnetmask;
+
+static int ShowCommand(struct cmdtab const *, int, char **);
+static int TerminalCommand(struct cmdtab const *, int, char **);
+static int QuitCommand(struct cmdtab const *, int, char **);
+static int CloseCommand(struct cmdtab const *, int, char **);
+static int DialCommand(struct cmdtab const *, int, char **);
+static int DownCommand(struct cmdtab const *, int, char **);
+static int SetCommand(struct cmdtab const *, int, char **);
+static int AddCommand(struct cmdtab const *, int, char **);
+static int DeleteCommand(struct cmdtab const *, int, char **);
+static int BgShellCommand(struct cmdtab const *, int, char **);
+static int FgShellCommand(struct cmdtab const *, int, char **);
+static int ShellCommand(struct cmdtab const *, int, char **, int);
+static int AliasCommand(struct cmdtab const *, int, char **);
+static int AliasEnable(struct cmdtab const *, int, char **);
+static int AliasOption(struct cmdtab const *, int, char **, void *);
+
+static int
+HelpCommand(struct cmdtab const * list,
+ int argc,
+ char **argv,
+ struct cmdtab const * plist)
+{
+ struct cmdtab const *cmd;
+ int n;
+
+ if (!VarTerm)
+ return 0;
+
+ if (argc > 0) {
+ for (cmd = plist; cmd->name; cmd++)
+ if (strcasecmp(cmd->name, *argv) == 0 && (cmd->lauth & VarLocalAuth)) {
+ fprintf(VarTerm, "%s\n", cmd->syntax);
+ return 0;
+ }
+ return -1;
+ }
+ n = 0;
+ for (cmd = plist; cmd->func; cmd++)
+ if (cmd->name && (cmd->lauth & VarLocalAuth)) {
+ fprintf(VarTerm, " %-9s: %-20s\n", cmd->name, cmd->helpmes);
+ n++;
+ }
+ if (n & 1)
+ fprintf(VarTerm, "\n");
+
+ return 0;
+}
+
+int
+IsInteractive(int Display)
+{
+ char *mes = NULL;
+
+ if (mode & MODE_DDIAL)
+ mes = "Working in dedicated dial mode.";
+ else if (mode & MODE_BACKGROUND)
+ mes = "Working in background mode.";
+ else if (mode & MODE_AUTO)
+ mes = "Working in auto mode.";
+ else if (mode & MODE_DIRECT)
+ mes = "Working in direct mode.";
+ else if (mode & MODE_DEDICATED)
+ mes = "Working in dedicated mode.";
+ if (mes) {
+ if (Display && VarTerm)
+ fprintf(VarTerm, "%s\n", mes);
+ return 0;
+ }
+ return 1;
+}
+
+static int
+DialCommand(struct cmdtab const * cmdlist, int argc, char **argv)
+{
+ int tries;
+ int res;
+
+ if (LcpFsm.state > ST_CLOSED) {
+ if (VarTerm)
+ fprintf(VarTerm, "LCP state is [%s]\n", StateNames[LcpFsm.state]);
+ return 0;
+ }
+
+ if (argc > 0) {
+ if (SelectSystem(*argv, CONFFILE) < 0) {
+ if (VarTerm)
+ fprintf(VarTerm, "%s: not found.\n", *argv);
+ return -1;
+ }
+ }
+ tries = 0;
+ do {
+ if (VarTerm)
+ fprintf(VarTerm, "Dial attempt %u of %d\n", ++tries, VarDialTries);
+ if (OpenModem() < 0) {
+ if (VarTerm)
+ fprintf(VarTerm, "Failed to open modem.\n");
+ break;
+ }
+ if ((res = DialModem()) == EX_DONE) {
+ nointr_sleep(1);
+ ModemTimeout();
+ PacketMode();
+ break;
+ } else if (res == EX_SIG)
+ return 1;
+ } while (VarDialTries == 0 || tries < VarDialTries);
+
+ return 0;
+}
+
+static int
+SetLoopback(struct cmdtab const * cmdlist, int argc, char **argv)
+{
+ if (argc == 1)
+ if (!strcasecmp(*argv, "on"))
+ VarLoopback = 1;
+ else if (!strcasecmp(*argv, "off"))
+ VarLoopback = 0;
+ return -1;
+}
+
+static int
+BgShellCommand(struct cmdtab const * cmdlist, int argc, char **argv)
+{
+ if (argc == 0)
+ return -1;
+ return ShellCommand(cmdlist, argc, argv, 1);
+}
+
+static int
+FgShellCommand(struct cmdtab const * cmdlist, int argc, char **argv)
+{
+ return ShellCommand(cmdlist, argc, argv, 0);
+}
+
+static int
+ShellCommand(struct cmdtab const * cmdlist, int argc, char **argv, int bg)
+{
+ const char *shell;
+ pid_t shpid;
+ FILE *oVarTerm;
+
+#ifdef SHELL_ONLY_INTERACTIVELY
+ /* we're only allowed to shell when we run ppp interactively */
+ if (mode != MODE_INTER) {
+ LogPrintf(LogWARN, "Can only start a shell in interactive mode\n");
+ return 1;
+ }
+#endif
+#ifdef NO_SHELL_IN_AUTO_INTERACTIVE
+
+ /*
+ * we want to stop shell commands when we've got a telnet connection to an
+ * auto mode ppp
+ */
+ if ((mode & (MODE_AUTO | MODE_INTER)) == (MODE_AUTO | MODE_INTER)) {
+ LogPrintf(LogWARN, "Shell is not allowed interactively in auto mode\n");
+ return 1;
+ }
+#endif
+
+ if (argc == 0)
+ if (!(mode & MODE_INTER)) {
+ LogPrintf(LogWARN, "Can only start an interactive shell in"
+ " interactive mode\n");
+ return 1;
+ } else if (bg) {
+ LogPrintf(LogWARN, "Can only start an interactive shell in"
+ " the foreground mode\n");
+ return 1;
+ } else if (mode&(MODE_AUTO|MODE_DEDICATED|MODE_DIRECT)) {
+ LogPrintf(LogWARN, "Can't start an interactive shell from"
+ " a telnet session\n");
+ return 1;
+ }
+ if ((shell = getenv("SHELL")) == 0)
+ shell = _PATH_BSHELL;
+
+ if ((shpid = fork()) == 0) {
+ int dtablesize, i, fd;
+
+ if (VarTerm)
+ fd = fileno(VarTerm);
+ else if ((fd = open("/dev/null", O_RDWR)) == -1) {
+ LogPrintf(LogALERT, "Failed to open /dev/null: %s\n", strerror(errno));
+ exit(1);
+ }
+ for (i = 0; i < 3; i++)
+ dup2(fd, i);
+
+ if (fd > 2)
+ if (VarTerm) {
+ oVarTerm = VarTerm;
+ VarTerm = 0;
+ if (oVarTerm && oVarTerm != stdout)
+ fclose(oVarTerm);
+ } else
+ close(fd);
+
+ for (dtablesize = getdtablesize(), i = 3; i < dtablesize; i++)
+ (void) close(i);
+
+ TtyOldMode();
+ setuid(geteuid());
+ if (argc > 0) {
+ /* substitute pseudo args */
+ for (i = 1; i < argc; i++)
+ if (strcasecmp(argv[i], "HISADDR") == 0)
+ argv[i] = strdup(inet_ntoa(IpcpInfo.his_ipaddr));
+ else if (strcasecmp(argv[i], "INTERFACE") == 0)
+ argv[i] = strdup(IfDevName);
+ else if (strcasecmp(argv[i], "MYADDR") == 0)
+ argv[i] = strdup(inet_ntoa(IpcpInfo.want_ipaddr));
+ if (bg) {
+ pid_t p;
+
+ p = getpid();
+ if (daemon(1, 1) == -1) {
+ LogPrintf(LogERROR, "%d: daemon: %s\n", p, strerror(errno));
+ exit(1);
+ }
+ }
+ if (VarTerm)
+ fprintf(VarTerm, "ppp: Pausing until %s finishes\n", argv[0]);
+ (void) execvp(argv[0], argv);
+ } else {
+ if (VarTerm)
+ fprintf(VarTerm, "ppp: Pausing until %s finishes\n", shell);
+ (void) execl(shell, shell, NULL);
+ }
+
+ LogPrintf(LogWARN, "exec() of %s failed\n", argc > 0 ? argv[0] : shell);
+ exit(255);
+ }
+ if (shpid == (pid_t) - 1) {
+ LogPrintf(LogERROR, "Fork failed: %s\n", strerror(errno));
+ } else {
+ int status;
+
+ (void) waitpid(shpid, &status, 0);
+ }
+
+ TtyCommandMode(1);
+
+ return (0);
+}
+
+static struct cmdtab const Commands[] = {
+ {"accept", NULL, AcceptCommand, LOCAL_AUTH,
+ "accept option request", "accept option .."},
+ {"add", NULL, AddCommand, LOCAL_AUTH,
+ "add route", "add dest mask gateway"},
+ {"bg", "!bg", BgShellCommand, LOCAL_AUTH,
+ "Run a command in the background", "[!]bg command"},
+ {"close", NULL, CloseCommand, LOCAL_AUTH,
+ "Close connection", "close"},
+ {"delete", NULL, DeleteCommand, LOCAL_AUTH,
+ "delete route", "delete ALL | dest [gateway [mask]]"},
+ {"deny", NULL, DenyCommand, LOCAL_AUTH,
+ "Deny option request", "deny option .."},
+ {"dial", "call", DialCommand, LOCAL_AUTH,
+ "Dial and login", "dial|call [remote]"},
+ {"disable", NULL, DisableCommand, LOCAL_AUTH,
+ "Disable option", "disable option .."},
+ {"display", NULL, DisplayCommand, LOCAL_AUTH,
+ "Display option configs", "display"},
+ {"enable", NULL, EnableCommand, LOCAL_AUTH,
+ "Enable option", "enable option .."},
+ {"passwd", NULL, LocalAuthCommand, LOCAL_NO_AUTH,
+ "Password for manipulation", "passwd LocalPassword"},
+ {"load", NULL, LoadCommand, LOCAL_AUTH,
+ "Load settings", "load [remote]"},
+ {"save", NULL, SaveCommand, LOCAL_AUTH,
+ "Save settings", "save"},
+ {"set", "setup", SetCommand, LOCAL_AUTH,
+ "Set parameters", "set[up] var value"},
+ {"shell", "!", FgShellCommand, LOCAL_AUTH,
+ "Run a subshell", "shell|! [sh command]"},
+ {"show", NULL, ShowCommand, LOCAL_AUTH,
+ "Show status and statistics", "show var"},
+ {"term", NULL, TerminalCommand, LOCAL_AUTH,
+ "Enter to terminal mode", "term"},
+ {"alias", NULL, AliasCommand, LOCAL_AUTH,
+ "alias control", "alias option [yes|no]"},
+ {"quit", "bye", QuitCommand, LOCAL_AUTH | LOCAL_NO_AUTH,
+ "Quit PPP program", "quit|bye [all]"},
+ {"help", "?", HelpCommand, LOCAL_AUTH | LOCAL_NO_AUTH,
+ "Display this message", "help|? [command]", (void *) Commands},
+ {NULL, "down", DownCommand, LOCAL_AUTH,
+ "Generate down event", "down"},
+ {NULL, NULL, NULL},
+};
+
+static int
+ShowLoopback()
+{
+ if (VarTerm)
+ fprintf(VarTerm, "Local loopback is %s\n", VarLoopback ? "on" : "off");
+
+ return 0;
+}
+
+static int
+ShowLogLevel()
+{
+ int i;
+
+ if (!VarTerm)
+ return 0;
+
+ fprintf(VarTerm, "Log: ");
+ for (i = LogMIN; i <= LogMAX; i++)
+ if (LogIsKept(i) & LOG_KEPT_SYSLOG)
+ fprintf(VarTerm, " %s", LogName(i));
+
+ fprintf(VarTerm, "\nLocal:");
+ for (i = LogMIN; i <= LogMAX; i++)
+ if (LogIsKept(i) & LOG_KEPT_LOCAL)
+ fprintf(VarTerm, " %s", LogName(i));
+
+ fprintf(VarTerm, "\n");
+
+ return 0;
+}
+
+static int
+ShowEscape()
+{
+ int code, bit;
+
+ if (!VarTerm)
+ return 0;
+ if (EscMap[32]) {
+ for (code = 0; code < 32; code++)
+ if (EscMap[code])
+ for (bit = 0; bit < 8; bit++)
+ if (EscMap[code] & (1 << bit))
+ fprintf(VarTerm, " 0x%02x", (code << 3) + bit);
+ fprintf(VarTerm, "\n");
+ }
+ return 0;
+}
+
+static int
+ShowTimeout()
+{
+ if (VarTerm)
+ fprintf(VarTerm, " Idle Timer: %d secs LQR Timer: %d secs"
+ " Retry Timer: %d secs\n", VarIdleTimeout, VarLqrTimeout,
+ VarRetryTimeout);
+ return 0;
+}
+
+static int
+ShowStopped()
+{
+ if (!VarTerm)
+ return 0;
+
+ fprintf(VarTerm, " Stopped Timer: LCP: ");
+ if (!LcpFsm.StoppedTimer.load)
+ fprintf(VarTerm, "Disabled");
+ else
+ fprintf(VarTerm, "%ld secs", LcpFsm.StoppedTimer.load / SECTICKS);
+
+ fprintf(VarTerm, ", IPCP: ");
+ if (!IpcpFsm.StoppedTimer.load)
+ fprintf(VarTerm, "Disabled");
+ else
+ fprintf(VarTerm, "%ld secs", IpcpFsm.StoppedTimer.load / SECTICKS);
+
+ fprintf(VarTerm, ", CCP: ");
+ if (!CcpFsm.StoppedTimer.load)
+ fprintf(VarTerm, "Disabled");
+ else
+ fprintf(VarTerm, "%ld secs", CcpFsm.StoppedTimer.load / SECTICKS);
+
+ fprintf(VarTerm, "\n");
+
+ return 0;
+}
+
+static int
+ShowAuthKey()
+{
+ if (!VarTerm)
+ return 0;
+ fprintf(VarTerm, "AuthName = %s\n", VarAuthName);
+ fprintf(VarTerm, "AuthKey = %s\n", VarAuthKey);
+#ifdef HAVE_DES
+ fprintf(VarTerm, "Encrypt = %s\n", VarMSChap ? "MSChap" : "MD5" );
+#endif
+ return 0;
+}
+
+static int
+ShowVersion()
+{
+ if (VarTerm)
+ fprintf(VarTerm, "%s - %s \n", VarVersion, VarLocalVersion);
+ return 0;
+}
+
+static int
+ShowInitialMRU()
+{
+ if (VarTerm)
+ fprintf(VarTerm, " Initial MRU: %ld\n", VarMRU);
+ return 0;
+}
+
+static int
+ShowPreferredMTU()
+{
+ if (VarTerm)
+ if (VarPrefMTU)
+ fprintf(VarTerm, " Preferred MTU: %ld\n", VarPrefMTU);
+ else
+ fprintf(VarTerm, " Preferred MTU: unspecified\n");
+ return 0;
+}
+
+static int
+ShowReconnect()
+{
+ if (VarTerm)
+ fprintf(VarTerm, " Reconnect Timer: %d, %d tries\n",
+ VarReconnectTimer, VarReconnectTries);
+ return 0;
+}
+
+static int
+ShowRedial()
+{
+ if (!VarTerm)
+ return 0;
+ fprintf(VarTerm, " Redial Timer: ");
+
+ if (VarRedialTimeout >= 0) {
+ fprintf(VarTerm, " %d seconds, ", VarRedialTimeout);
+ } else {
+ fprintf(VarTerm, " Random 0 - %d seconds, ", REDIAL_PERIOD);
+ }
+
+ fprintf(VarTerm, " Redial Next Timer: ");
+
+ if (VarRedialNextTimeout >= 0) {
+ fprintf(VarTerm, " %d seconds, ", VarRedialNextTimeout);
+ } else {
+ fprintf(VarTerm, " Random 0 - %d seconds, ", REDIAL_PERIOD);
+ }
+
+ if (VarDialTries)
+ fprintf(VarTerm, "%d dial tries", VarDialTries);
+
+ fprintf(VarTerm, "\n");
+
+ return 0;
+}
+
+#ifndef NOMSEXT
+static int
+ShowMSExt()
+{
+ if (VarTerm) {
+ fprintf(VarTerm, " MS PPP extention values \n");
+ fprintf(VarTerm, " Primary NS : %s\n", inet_ntoa(ns_entries[0]));
+ fprintf(VarTerm, " Secondary NS : %s\n", inet_ntoa(ns_entries[1]));
+ fprintf(VarTerm, " Primary NBNS : %s\n", inet_ntoa(nbns_entries[0]));
+ fprintf(VarTerm, " Secondary NBNS : %s\n", inet_ntoa(nbns_entries[1]));
+ }
+ return 0;
+}
+
+#endif
+
+static struct cmdtab const ShowCommands[] = {
+ {"afilter", NULL, ShowAfilter, LOCAL_AUTH,
+ "Show keep Alive filters", "show afilter option .."},
+ {"auth", NULL, ShowAuthKey, LOCAL_AUTH,
+ "Show auth name, key and algorithm", "show auth"},
+ {"ccp", NULL, ReportCcpStatus, LOCAL_AUTH,
+ "Show CCP status", "show cpp"},
+ {"compress", NULL, ReportCompress, LOCAL_AUTH,
+ "Show compression statistics", "show compress"},
+ {"dfilter", NULL, ShowDfilter, LOCAL_AUTH,
+ "Show Demand filters", "show dfilteroption .."},
+ {"escape", NULL, ShowEscape, LOCAL_AUTH,
+ "Show escape characters", "show escape"},
+ {"hdlc", NULL, ReportHdlcStatus, LOCAL_AUTH,
+ "Show HDLC error summary", "show hdlc"},
+ {"ifilter", NULL, ShowIfilter, LOCAL_AUTH,
+ "Show Input filters", "show ifilter option .."},
+ {"ipcp", NULL, ReportIpcpStatus, LOCAL_AUTH,
+ "Show IPCP status", "show ipcp"},
+ {"lcp", NULL, ReportLcpStatus, LOCAL_AUTH,
+ "Show LCP status", "show lcp"},
+ {"loopback", NULL, ShowLoopback, LOCAL_AUTH,
+ "Show current loopback setting", "show loopback"},
+ {"log", NULL, ShowLogLevel, LOCAL_AUTH,
+ "Show current log level", "show log"},
+ {"mem", NULL, ShowMemMap, LOCAL_AUTH,
+ "Show memory map", "show mem"},
+ {"modem", NULL, ShowModemStatus, LOCAL_AUTH,
+ "Show modem setups", "show modem"},
+ {"mru", NULL, ShowInitialMRU, LOCAL_AUTH,
+ "Show Initial MRU", "show mru"},
+ {"mtu", NULL, ShowPreferredMTU, LOCAL_AUTH,
+ "Show Preferred MTU", "show mtu"},
+ {"ofilter", NULL, ShowOfilter, LOCAL_AUTH,
+ "Show Output filters", "show ofilter option .."},
+ {"proto", NULL, ReportProtStatus, LOCAL_AUTH,
+ "Show protocol summary", "show proto"},
+ {"reconnect", NULL, ShowReconnect, LOCAL_AUTH,
+ "Show Reconnect timer,tries", "show reconnect"},
+ {"redial", NULL, ShowRedial, LOCAL_AUTH,
+ "Show Redial timeout value", "show redial"},
+ {"route", NULL, ShowRoute, LOCAL_AUTH,
+ "Show routing table", "show route"},
+ {"timeout", NULL, ShowTimeout, LOCAL_AUTH,
+ "Show Idle timeout value", "show timeout"},
+ {"stopped", NULL, ShowStopped, LOCAL_AUTH,
+ "Show STOPPED timeout value", "show stopped"},
+#ifndef NOMSEXT
+ {"msext", NULL, ShowMSExt, LOCAL_AUTH,
+ "Show MS PPP extentions", "show msext"},
+#endif
+ {"version", NULL, ShowVersion, LOCAL_NO_AUTH | LOCAL_AUTH,
+ "Show version string", "show version"},
+ {"help", "?", HelpCommand, LOCAL_NO_AUTH | LOCAL_AUTH,
+ "Display this message", "show help|? [command]", (void *) ShowCommands},
+ {NULL, NULL, NULL},
+};
+
+static struct cmdtab const *
+FindCommand(struct cmdtab const * cmds, 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 int
+FindExec(struct cmdtab const * cmdlist, int argc, char **argv)
+{
+ struct cmdtab const *cmd;
+ int val = 1;
+ int nmatch;
+
+ cmd = FindCommand(cmdlist, *argv, &nmatch);
+ if (nmatch > 1)
+ LogPrintf(LogWARN, "%s: Ambiguous command\n", *argv);
+ else if (cmd && (cmd->lauth & VarLocalAuth))
+ val = (cmd->func) (cmd, argc-1, argv+1, cmd->args);
+ else
+ LogPrintf(LogWARN, "%s: Invalid command\n", *argv);
+
+ if (val == -1)
+ LogPrintf(LogWARN, "Usage: %s\n", cmd->syntax);
+ else if (val)
+ LogPrintf(LogCOMMAND, "%s: Failed %d\n", *argv, val);
+
+ return val;
+}
+
+int aft_cmd = 1;
+
+void
+Prompt()
+{
+ char *pconnect, *pauth;
+
+ if (!(mode & MODE_INTER) || !VarTerm || TermMode)
+ return;
+
+ if (!aft_cmd)
+ fprintf(VarTerm, "\n");
+ else
+ aft_cmd = 0;
+
+ if (VarLocalAuth == LOCAL_AUTH)
+ pauth = " ON ";
+ else
+ pauth = " on ";
+ if (IpcpFsm.state == ST_OPENED && phase == PHASE_NETWORK)
+ pconnect = "PPP";
+ else
+ pconnect = "ppp";
+ fprintf(VarTerm, "%s%s%s> ", pconnect, pauth, VarShortHost);
+ fflush(VarTerm);
+}
+
+void
+DecodeCommand(char *buff, int nb, int prompt)
+{
+ char *vector[20];
+ char **argv;
+ int argc;
+ char *cp;
+
+ if (nb > 0) {
+ cp = buff + strcspn(buff, "\r\n");
+ if (cp)
+ *cp = '\0';
+ argc = MakeArgs(buff, vector, VECSIZE(vector));
+ argv = vector;
+
+ if (argc > 0)
+ FindExec(Commands, argc, argv);
+ }
+ if (prompt)
+ Prompt();
+}
+
+static int
+ShowCommand(struct cmdtab const * list, int argc, char **argv)
+{
+ if (argc > 0)
+ FindExec(ShowCommands, argc, argv);
+ else if (VarTerm)
+ fprintf(VarTerm, "Use ``show ?'' to get a list.\n");
+ else
+ LogPrintf(LogWARN, "show command must have arguments\n");
+
+ return 0;
+}
+
+static int
+TerminalCommand(struct cmdtab const * list, int argc, char **argv)
+{
+ if (LcpFsm.state > ST_CLOSED) {
+ if (VarTerm)
+ fprintf(VarTerm, "LCP state is [%s]\n", StateNames[LcpFsm.state]);
+ return 1;
+ }
+ if (!IsInteractive(1))
+ return (1);
+ if (OpenModem() < 0) {
+ if (VarTerm)
+ fprintf(VarTerm, "Failed to open modem.\n");
+ return (1);
+ }
+ if (VarTerm) {
+ fprintf(VarTerm, "Enter to terminal mode.\n");
+ fprintf(VarTerm, "Type `~?' for help.\n");
+ }
+ TtyTermMode();
+ return (0);
+}
+
+static int
+QuitCommand(struct cmdtab const * list, int argc, char **argv)
+{
+ FILE *oVarTerm;
+
+ if (mode & (MODE_DIRECT | MODE_DEDICATED | MODE_AUTO)) {
+ if (argc > 0 && !strcasecmp(*argv, "all") && (VarLocalAuth & LOCAL_AUTH)) {
+ mode &= ~MODE_INTER;
+ oVarTerm = VarTerm;
+ VarTerm = 0;
+ if (oVarTerm && oVarTerm != stdout)
+ fclose(oVarTerm);
+ Cleanup(EX_NORMAL);
+ } else if (VarTerm) {
+ LogPrintf(LogPHASE, "Client connection closed.\n");
+ mode &= ~MODE_INTER;
+ oVarTerm = VarTerm;
+ VarTerm = 0;
+ if (oVarTerm && oVarTerm != stdout)
+ fclose(oVarTerm);
+ close(netfd);
+ netfd = -1;
+ }
+ } else
+ Cleanup(EX_NORMAL);
+
+ return 0;
+}
+
+static int
+CloseCommand(struct cmdtab const * list, int argc, char **argv)
+{
+ reconnect(RECON_FALSE);
+ LcpClose();
+ return 0;
+}
+
+static int
+DownCommand(struct cmdtab const * list, int argc, char **argv)
+{
+ LcpDown();
+ return 0;
+}
+
+static int
+SetModemSpeed(struct cmdtab const * list, int argc, char **argv)
+{
+ int speed;
+
+ if (argc > 0) {
+ if (strcasecmp(*argv, "sync") == 0) {
+ VarSpeed = 0;
+ return 0;
+ }
+ speed = atoi(*argv);
+ if (IntToSpeed(speed) != B0) {
+ VarSpeed = speed;
+ return 0;
+ }
+ LogPrintf(LogWARN, "%s: Invalid speed\n", *argv);
+ }
+ return -1;
+}
+
+static int
+SetReconnect(struct cmdtab const * list, int argc, char **argv)
+{
+ if (argc == 2) {
+ VarReconnectTimer = atoi(argv[0]);
+ VarReconnectTries = atoi(argv[1]);
+ return 0;
+ }
+ return -1;
+}
+
+static int
+SetRedialTimeout(struct cmdtab const * list, int argc, char **argv)
+{
+ int timeout;
+ int tries;
+ char *dot;
+
+ if (argc == 1 || argc == 2) {
+ if (strncasecmp(argv[0], "random", 6) == 0 &&
+ (argv[0][6] == '\0' || argv[0][6] == '.')) {
+ VarRedialTimeout = -1;
+ randinit();
+ } else {
+ timeout = atoi(argv[0]);
+
+ if (timeout >= 0)
+ VarRedialTimeout = timeout;
+ else {
+ LogPrintf(LogWARN, "Invalid redial timeout\n");
+ return -1;
+ }
+ }
+
+ dot = strchr(argv[0], '.');
+ if (dot) {
+ if (strcasecmp(++dot, "random") == 0) {
+ VarRedialNextTimeout = -1;
+ randinit();
+ } else {
+ timeout = atoi(dot);
+ if (timeout >= 0)
+ VarRedialNextTimeout = timeout;
+ else {
+ LogPrintf(LogWARN, "Invalid next redial timeout\n");
+ return -1;
+ }
+ }
+ } else
+ VarRedialNextTimeout = NEXT_REDIAL_PERIOD; /* Default next timeout */
+
+ if (argc == 2) {
+ tries = atoi(argv[1]);
+
+ if (tries >= 0) {
+ VarDialTries = tries;
+ } else {
+ LogPrintf(LogWARN, "Invalid retry value\n");
+ return 1;
+ }
+ }
+ return 0;
+ }
+ return -1;
+}
+
+static int
+SetStoppedTimeout(struct cmdtab const * list, int argc, char **argv)
+{
+ LcpFsm.StoppedTimer.load = 0;
+ IpcpFsm.StoppedTimer.load = 0;
+ CcpFsm.StoppedTimer.load = 0;
+ if (argc <= 3) {
+ if (argc > 0) {
+ LcpFsm.StoppedTimer.load = atoi(argv[0]) * SECTICKS;
+ if (argc > 1) {
+ IpcpFsm.StoppedTimer.load = atoi(argv[1]) * SECTICKS;
+ if (argc > 2)
+ CcpFsm.StoppedTimer.load = atoi(argv[2]) * SECTICKS;
+ }
+ }
+ return 0;
+ }
+ return -1;
+}
+
+#define ismask(x) \
+ (*x == '0' && strlen(x) == 4 && strspn(x+1, "0123456789.") == 3)
+
+static int
+SetServer(struct cmdtab const * list, int argc, char **argv)
+{
+ int res = -1;
+
+ if (argc > 0 && argc < 4) {
+ const char *port, *passwd, *mask;
+
+ /* What's what ? */
+ port = argv[0];
+ if (argc == 2)
+ if (ismask(argv[1])) {
+ passwd = NULL;
+ mask = argv[1];
+ } else {
+ passwd = argv[1];
+ mask = NULL;
+ }
+ else if (argc == 3) {
+ passwd = argv[1];
+ mask = argv[2];
+ if (!ismask(mask))
+ return -1;
+ } else
+ passwd = mask = NULL;
+
+ if (passwd == NULL)
+ VarHaveLocalAuthKey = 0;
+ else {
+ strncpy(VarLocalAuthKey, passwd, sizeof VarLocalAuthKey);
+ VarLocalAuthKey[sizeof VarLocalAuthKey - 1] = '\0';
+ VarHaveLocalAuthKey = 1;
+ }
+ LocalAuthInit();
+
+ if (strcasecmp(port, "none") == 0) {
+ int oserver;
+
+ if (mask != NULL || passwd != NULL)
+ return -1;
+ oserver = server;
+ ServerClose();
+ if (oserver != -1)
+ LogPrintf(LogPHASE, "Disabling server port.\n");
+ res = 0;
+ } else if (*port == '/') {
+ mode_t imask;
+
+ if (mask != NULL) {
+ unsigned m;
+
+ if (sscanf(mask, "%o", &m) == 1)
+ imask = m;
+ else
+ return -1;
+ } else
+ imask = (mode_t)-1;
+ res = ServerLocalOpen(port, imask);
+ } else {
+ int iport;
+
+ if (mask != NULL)
+ return -1;
+
+ if (strspn(port, "0123456789") != strlen(port)) {
+ struct servent *s;
+
+ if ((s = getservbyname(port, "tcp")) == NULL) {
+ iport = 0;
+ LogPrintf(LogWARN, "%s: Invalid port or service\n", port);
+ } else
+ iport = ntohs(s->s_port);
+ } else
+ iport = atoi(port);
+ res = iport ? ServerTcpOpen(iport) : -1;
+ }
+ }
+
+ return res;
+}
+
+static int
+SetModemParity(struct cmdtab const * list, int argc, char **argv)
+{
+ return argc > 0 ? ChangeParity(*argv) : -1;
+}
+
+static int
+SetLogLevel(struct cmdtab const * list, int argc, char **argv)
+{
+ int i;
+ int res;
+ char *arg;
+ void (*Discard)(int), (*Keep)(int);
+ void (*DiscardAll)(void);
+
+ res = 0;
+ if (strcasecmp(argv[0], "local")) {
+ Discard = LogDiscard;
+ Keep = LogKeep;
+ DiscardAll = LogDiscardAll;
+ } else {
+ argc--;
+ argv++;
+ Discard = LogDiscardLocal;
+ Keep = LogKeepLocal;
+ DiscardAll = LogDiscardAllLocal;
+ }
+
+ if (argc == 0 || (argv[0][0] != '+' && argv[0][0] != '-'))
+ (*DiscardAll)();
+ while (argc--) {
+ arg = **argv == '+' || **argv == '-' ? *argv + 1 : *argv;
+ for (i = LogMIN; i <= LogMAX; i++)
+ if (strcasecmp(arg, LogName(i)) == 0) {
+ if (**argv == '-')
+ (*Discard)(i);
+ else
+ (*Keep)(i);
+ break;
+ }
+ if (i > LogMAX) {
+ LogPrintf(LogWARN, "%s: Invalid log value\n", arg);
+ res = -1;
+ }
+ argv++;
+ }
+ return res;
+}
+
+static int
+SetEscape(struct cmdtab const * list, int argc, char **argv)
+{
+ int code;
+
+ for (code = 0; code < 33; code++)
+ EscMap[code] = 0;
+ while (argc-- > 0) {
+ sscanf(*argv++, "%x", &code);
+ code &= 0xff;
+ EscMap[code >> 3] |= (1 << (code & 7));
+ EscMap[32] = 1;
+ }
+ return 0;
+}
+
+static int
+SetInitialMRU(struct cmdtab const * list, int argc, char **argv)
+{
+ long mru;
+ char *err;
+
+ if (argc > 0) {
+ mru = atol(*argv);
+ if (mru < MIN_MRU)
+ err = "Given MRU value (%ld) is too small.\n";
+ else if (mru > MAX_MRU)
+ err = "Given MRU value (%ld) is too big.\n";
+ else {
+ VarMRU = mru;
+ return 0;
+ }
+ LogPrintf(LogWARN, err, mru);
+ }
+ return -1;
+}
+
+static int
+SetPreferredMTU(struct cmdtab const * list, int argc, char **argv)
+{
+ long mtu;
+ char *err;
+
+ if (argc > 0) {
+ mtu = atol(*argv);
+ if (mtu == 0) {
+ VarPrefMTU = 0;
+ return 0;
+ } else if (mtu < MIN_MTU)
+ err = "Given MTU value (%ld) is too small.\n";
+ else if (mtu > MAX_MTU)
+ err = "Given MTU value (%ld) is too big.\n";
+ else {
+ VarPrefMTU = mtu;
+ return 0;
+ }
+ LogPrintf(LogWARN, err, mtu);
+ }
+ return -1;
+}
+
+static int
+SetIdleTimeout(struct cmdtab const * list, int argc, char **argv)
+{
+ if (argc-- > 0) {
+ VarIdleTimeout = atoi(*argv++);
+ UpdateIdleTimer(); /* If we're connected, restart the idle timer */
+ if (argc-- > 0) {
+ VarLqrTimeout = atoi(*argv++);
+ if (VarLqrTimeout < 1)
+ VarLqrTimeout = 30;
+ if (argc > 0) {
+ VarRetryTimeout = atoi(*argv);
+ if (VarRetryTimeout < 1 || VarRetryTimeout > 10)
+ VarRetryTimeout = 3;
+ }
+ }
+ return 0;
+ }
+ return -1;
+}
+
+static struct in_addr
+GetIpAddr(char *cp)
+{
+ struct hostent *hp;
+ struct in_addr ipaddr;
+
+ hp = gethostbyname(cp);
+ if (hp && hp->h_addrtype == AF_INET)
+ memcpy(&ipaddr, hp->h_addr, hp->h_length);
+ else if (inet_aton(cp, &ipaddr) == 0)
+ ipaddr.s_addr = 0;
+ return (ipaddr);
+}
+
+static int
+SetInterfaceAddr(struct cmdtab const * list, int argc, char **argv)
+{
+ DefMyAddress.ipaddr.s_addr = DefHisAddress.ipaddr.s_addr = 0L;
+
+ if (argc > 4)
+ return -1;
+
+ HaveTriggerAddress = 0;
+ ifnetmask.s_addr = 0;
+
+ if (argc > 0) {
+ if (ParseAddr(argc, argv++,
+ &DefMyAddress.ipaddr,
+ &DefMyAddress.mask,
+ &DefMyAddress.width) == 0)
+ return 1;
+ if (--argc > 0) {
+ if (ParseAddr(argc, argv++,
+ &DefHisAddress.ipaddr,
+ &DefHisAddress.mask,
+ &DefHisAddress.width) == 0)
+ return 2;
+ if (--argc > 0) {
+ ifnetmask = GetIpAddr(*argv);
+ if (--argc > 0) {
+ TriggerAddress = GetIpAddr(*argv);
+ HaveTriggerAddress = 1;
+ }
+ }
+ }
+ }
+
+ /*
+ * For backwards compatibility, 0.0.0.0 means any address.
+ */
+ if (DefMyAddress.ipaddr.s_addr == 0) {
+ DefMyAddress.mask.s_addr = 0;
+ DefMyAddress.width = 0;
+ }
+ if (DefHisAddress.ipaddr.s_addr == 0) {
+ DefHisAddress.mask.s_addr = 0;
+ DefHisAddress.width = 0;
+ }
+ IpcpInfo.want_ipaddr.s_addr = DefMyAddress.ipaddr.s_addr;
+ IpcpInfo.his_ipaddr.s_addr = DefHisAddress.ipaddr.s_addr;
+
+ if ((mode & MODE_AUTO) ||
+ ((mode & MODE_DEDICATED) && dstsystem)) {
+ if (OsSetIpaddress(DefMyAddress.ipaddr, DefHisAddress.ipaddr, ifnetmask) < 0)
+ return 4;
+ }
+ return 0;
+}
+
+#ifndef NOMSEXT
+
+static void
+SetMSEXT(struct in_addr * pri_addr,
+ struct in_addr * sec_addr,
+ int argc,
+ char **argv)
+{
+ int dummyint;
+ struct in_addr dummyaddr;
+
+ pri_addr->s_addr = sec_addr->s_addr = 0L;
+
+ if (argc > 0) {
+ ParseAddr(argc, argv++, pri_addr, &dummyaddr, &dummyint);
+ if (--argc > 0)
+ ParseAddr(argc, argv++, sec_addr, &dummyaddr, &dummyint);
+ else
+ sec_addr->s_addr = pri_addr->s_addr;
+ }
+
+ /*
+ * if the primary/secondary ns entries are 0.0.0.0 we should set them to
+ * either the localhost's ip, or the values in /etc/resolv.conf ??
+ *
+ * up to you if you want to implement this...
+ */
+
+}
+
+static int
+SetNS(struct cmdtab const * list, int argc, char **argv)
+{
+ SetMSEXT(&ns_entries[0], &ns_entries[1], argc, argv);
+ return 0;
+}
+
+static int
+SetNBNS(struct cmdtab const * list, int argc, char **argv)
+{
+ SetMSEXT(&nbns_entries[0], &nbns_entries[1], argc, argv);
+ return 0;
+}
+
+#endif /* MS_EXT */
+
+int
+SetVariable(struct cmdtab const * list, int argc, char **argv, int param)
+{
+ u_long map;
+ char *arg;
+
+ if (argc > 0)
+ arg = *argv;
+ else
+ arg = "";
+
+ switch (param) {
+ case VAR_AUTHKEY:
+ strncpy(VarAuthKey, arg, sizeof(VarAuthKey) - 1);
+ VarAuthKey[sizeof(VarAuthKey) - 1] = '\0';
+ break;
+ case VAR_AUTHNAME:
+ strncpy(VarAuthName, arg, sizeof(VarAuthName) - 1);
+ VarAuthName[sizeof(VarAuthName) - 1] = '\0';
+ break;
+ case VAR_DIAL:
+ strncpy(VarDialScript, arg, sizeof(VarDialScript) - 1);
+ VarDialScript[sizeof(VarDialScript) - 1] = '\0';
+ break;
+ case VAR_LOGIN:
+ strncpy(VarLoginScript, arg, sizeof(VarLoginScript) - 1);
+ VarLoginScript[sizeof(VarLoginScript) - 1] = '\0';
+ break;
+ case VAR_DEVICE:
+ if (modem != -1)
+ LogPrintf(LogWARN, "Cannot change device to \"%s\" when \"%s\" is open\n",
+ arg, VarDevice);
+ else {
+ strncpy(VarDevice, arg, sizeof(VarDevice) - 1);
+ VarDevice[sizeof(VarDevice) - 1] = '\0';
+ VarBaseDevice = strrchr(VarDevice, '/');
+ VarBaseDevice = VarBaseDevice ? VarBaseDevice + 1 : "";
+ }
+ break;
+ case VAR_ACCMAP:
+ sscanf(arg, "%lx", &map);
+ VarAccmap = map;
+ break;
+ case VAR_PHONE:
+ strncpy(VarPhoneList, arg, sizeof(VarPhoneList) - 1);
+ VarPhoneList[sizeof(VarPhoneList) - 1] = '\0';
+ strcpy(VarPhoneCopy, VarPhoneList);
+ VarNextPhone = VarPhoneCopy;
+ VarAltPhone = NULL;
+ break;
+ case VAR_HANGUP:
+ strncpy(VarHangupScript, arg, sizeof(VarHangupScript) - 1);
+ VarHangupScript[sizeof(VarHangupScript) - 1] = '\0';
+ break;
+#ifdef HAVE_DES
+ case VAR_ENC:
+ VarMSChap = !strcasecmp(arg, "mschap");
+ break;
+#endif
+ }
+ return 0;
+}
+
+static int
+SetCtsRts(struct cmdtab const * list, int argc, char **argv)
+{
+ if (argc > 0) {
+ if (strcmp(*argv, "on") == 0)
+ VarCtsRts = 1;
+ else if (strcmp(*argv, "off") == 0)
+ VarCtsRts = 0;
+ else
+ return -1;
+ return 0;
+ }
+ return -1;
+}
+
+
+static int
+SetOpenMode(struct cmdtab const * list, int argc, char **argv)
+{
+ if (argc > 0) {
+ if (strcmp(*argv, "active") == 0)
+ VarOpenMode = OPEN_ACTIVE;
+ else if (strcmp(*argv, "passive") == 0)
+ VarOpenMode = OPEN_PASSIVE;
+ else
+ return -1;
+ return 0;
+ }
+ return -1;
+}
+
+static struct cmdtab const SetCommands[] = {
+ {"accmap", NULL, SetVariable, LOCAL_AUTH,
+ "Set accmap value", "set accmap hex-value", (void *) VAR_ACCMAP},
+ {"afilter", NULL, SetAfilter, LOCAL_AUTH,
+ "Set keep Alive filter", "set afilter ..."},
+ {"authkey", "key", SetVariable, LOCAL_AUTH,
+ "Set authentication key", "set authkey|key key", (void *) VAR_AUTHKEY},
+ {"authname", NULL, SetVariable, LOCAL_AUTH,
+ "Set authentication name", "set authname name", (void *) VAR_AUTHNAME},
+ {"ctsrts", NULL, SetCtsRts, LOCAL_AUTH,
+ "Use CTS/RTS modem signalling", "set ctsrts [on|off]"},
+ {"device", "line", SetVariable, LOCAL_AUTH,
+ "Set modem device name", "set device|line device-name", (void *) VAR_DEVICE},
+ {"dfilter", NULL, SetDfilter, LOCAL_AUTH,
+ "Set demand filter", "set dfilter ..."},
+ {"dial", NULL, SetVariable, LOCAL_AUTH,
+ "Set dialing script", "set dial chat-script", (void *) VAR_DIAL},
+#ifdef HAVE_DES
+ {"encrypt", NULL, SetVariable, LOCAL_AUTH,
+ "Set CHAP encryption algorithm", "set encrypt MSChap|MD5", (void *) VAR_ENC},
+#endif
+ {"escape", NULL, SetEscape, LOCAL_AUTH,
+ "Set escape characters", "set escape hex-digit ..."},
+ {"hangup", NULL, SetVariable, LOCAL_AUTH,
+ "Set hangup script", "set hangup chat-script", (void *) VAR_HANGUP},
+ {"ifaddr", NULL, SetInterfaceAddr, LOCAL_AUTH,
+ "Set destination address", "set ifaddr [src-addr [dst-addr [netmask [trg-addr]]]]"},
+ {"ifilter", NULL, SetIfilter, LOCAL_AUTH,
+ "Set input filter", "set ifilter ..."},
+ {"loopback", NULL, SetLoopback, LOCAL_AUTH,
+ "Set loopback facility", "set loopback on|off"},
+ {"log", NULL, SetLogLevel, LOCAL_AUTH,
+ "Set log level", "set log [local] [+|-]value..."},
+ {"login", NULL, SetVariable, LOCAL_AUTH,
+ "Set login script", "set login chat-script", (void *) VAR_LOGIN},
+ {"mru", NULL, SetInitialMRU, LOCAL_AUTH,
+ "Set Initial MRU value", "set mru value"},
+ {"mtu", NULL, SetPreferredMTU, LOCAL_AUTH,
+ "Set Preferred MTU value", "set mtu value"},
+ {"ofilter", NULL, SetOfilter, LOCAL_AUTH,
+ "Set output filter", "set ofilter ..."},
+ {"openmode", NULL, SetOpenMode, LOCAL_AUTH,
+ "Set open mode", "set openmode [active|passive]"},
+ {"parity", NULL, SetModemParity, LOCAL_AUTH,
+ "Set modem parity", "set parity [odd|even|none]"},
+ {"phone", NULL, SetVariable, LOCAL_AUTH,
+ "Set telephone number(s)", "set phone phone1[:phone2[...]]", (void *) VAR_PHONE},
+ {"reconnect", NULL, SetReconnect, LOCAL_AUTH,
+ "Set Reconnect timeout", "set reconnect value ntries"},
+ {"redial", NULL, SetRedialTimeout, LOCAL_AUTH,
+ "Set Redial timeout", "set redial value|random[.value|random] [dial_attempts]"},
+ {"stopped", NULL, SetStoppedTimeout, LOCAL_AUTH,
+ "Set STOPPED timeouts", "set stopped [LCPseconds [IPCPseconds [CCPseconds]]]"},
+ {"server", "socket", SetServer, LOCAL_AUTH,
+ "Set server port", "set server|socket TcpPort|LocalName|none [mask]"},
+ {"speed", NULL, SetModemSpeed, LOCAL_AUTH,
+ "Set modem speed", "set speed value"},
+ {"timeout", NULL, SetIdleTimeout, LOCAL_AUTH,
+ "Set Idle timeout", "set timeout value"},
+#ifndef NOMSEXT
+ {"ns", NULL, SetNS, LOCAL_AUTH,
+ "Set NameServer", "set ns pri-addr [sec-addr]"},
+ {"nbns", NULL, SetNBNS, LOCAL_AUTH,
+ "Set NetBIOS NameServer", "set nbns pri-addr [sec-addr]"},
+#endif
+ {"help", "?", HelpCommand, LOCAL_AUTH | LOCAL_NO_AUTH,
+ "Display this message", "set help|? [command]", (void *) SetCommands},
+ {NULL, NULL, NULL},
+};
+
+static int
+SetCommand(struct cmdtab const * list, int argc, char **argv)
+{
+ if (argc > 0)
+ FindExec(SetCommands, argc, argv);
+ else if (VarTerm)
+ fprintf(VarTerm, "Use `set ?' to get a list or `set ? <var>' for"
+ " syntax help.\n");
+ else
+ LogPrintf(LogWARN, "set command must have arguments\n");
+
+ return 0;
+}
+
+
+static int
+AddCommand(struct cmdtab const * list, int argc, char **argv)
+{
+ struct in_addr dest, gateway, netmask;
+
+ if (argc == 3) {
+ if (strcasecmp(argv[0], "MYADDR") == 0)
+ dest = IpcpInfo.want_ipaddr;
+ else
+ dest = GetIpAddr(argv[0]);
+ netmask = GetIpAddr(argv[1]);
+ if (strcasecmp(argv[2], "HISADDR") == 0)
+ gateway = IpcpInfo.his_ipaddr;
+ else
+ gateway = GetIpAddr(argv[2]);
+ OsSetRoute(RTM_ADD, dest, gateway, netmask);
+ return 0;
+ }
+ return -1;
+}
+
+static int
+DeleteCommand(struct cmdtab const * list, int argc, char **argv)
+{
+ struct in_addr dest, gateway, netmask;
+
+ if (argc == 1 && strcasecmp(argv[0], "all") == 0)
+ DeleteIfRoutes(0);
+ else if (argc > 0 && argc < 4) {
+ if (strcasecmp(argv[0], "MYADDR") == 0)
+ dest = IpcpInfo.want_ipaddr;
+ else
+ dest = GetIpAddr(argv[0]);
+ netmask.s_addr = INADDR_ANY;
+ if (argc > 1) {
+ if (strcasecmp(argv[1], "HISADDR") == 0)
+ gateway = IpcpInfo.his_ipaddr;
+ else
+ gateway = GetIpAddr(argv[1]);
+ if (argc == 3) {
+ if (inet_aton(argv[2], &netmask) == 0) {
+ LogPrintf(LogWARN, "Bad netmask value.\n");
+ return -1;
+ }
+ }
+ } else
+ gateway.s_addr = INADDR_ANY;
+ OsSetRoute(RTM_DELETE, dest, gateway, netmask);
+ } else
+ return -1;
+
+ return 0;
+}
+
+static struct cmdtab const AliasCommands[] =
+{
+ {"enable", NULL, AliasEnable, LOCAL_AUTH,
+ "enable IP aliasing", "alias enable [yes|no]"},
+ {"port", NULL, AliasRedirectPort, LOCAL_AUTH,
+ "port redirection", "alias port [proto addr_local:port_local port_alias]"},
+ {"addr", NULL, AliasRedirectAddr, LOCAL_AUTH,
+ "static address translation", "alias addr [addr_local addr_alias]"},
+ {"deny_incoming", NULL, AliasOption, LOCAL_AUTH,
+ "stop incoming connections", "alias deny_incoming [yes|no]",
+ (void *) PKT_ALIAS_DENY_INCOMING},
+ {"log", NULL, AliasOption, LOCAL_AUTH,
+ "log aliasing link creation", "alias log [yes|no]",
+ (void *) PKT_ALIAS_LOG},
+ {"same_ports", NULL, AliasOption, LOCAL_AUTH,
+ "try to leave port numbers unchanged", "alias same_ports [yes|no]",
+ (void *) PKT_ALIAS_SAME_PORTS},
+ {"use_sockets", NULL, AliasOption, LOCAL_AUTH,
+ "allocate host sockets", "alias use_sockets [yes|no]",
+ (void *) PKT_ALIAS_USE_SOCKETS},
+ {"unregistered_only", NULL, AliasOption, LOCAL_AUTH,
+ "alias unregistered (private) IP address space only",
+ "alias unregistered_only [yes|no]",
+ (void *) PKT_ALIAS_UNREGISTERED_ONLY},
+ {"help", "?", HelpCommand, LOCAL_AUTH | LOCAL_NO_AUTH,
+ "Display this message", "alias help|? [command]",
+ (void *) AliasCommands},
+ {NULL, NULL, NULL},
+};
+
+
+static int
+AliasCommand(struct cmdtab const * list, int argc, char **argv)
+{
+ if (argc > 0)
+ FindExec(AliasCommands, argc, argv);
+ else if (VarTerm)
+ fprintf(VarTerm, "Use `alias help' to get a list or `alias help <option>'"
+ " for syntax help.\n");
+ else
+ LogPrintf(LogWARN, "alias command must have arguments\n");
+
+ return 0;
+}
+
+static int
+AliasEnable(struct cmdtab const * list, int argc, char **argv)
+{
+ if (argc == 1)
+ if (strcasecmp(argv[0], "yes") == 0) {
+ if (!(mode & MODE_ALIAS)) {
+ if (loadAliasHandlers(&VarAliasHandlers) == 0) {
+ mode |= MODE_ALIAS;
+ return 0;
+ }
+ LogPrintf(LogWARN, "Cannot load alias library\n");
+ return 1;
+ }
+ return 0;
+ } else if (strcasecmp(argv[0], "no") == 0) {
+ if (mode & MODE_ALIAS) {
+ unloadAliasHandlers();
+ mode &= ~MODE_ALIAS;
+ }
+ return 0;
+ }
+ return -1;
+}
+
+
+static int
+AliasOption(struct cmdtab const * list, int argc, char **argv, void *param)
+{
+ if (argc == 1)
+ if (strcasecmp(argv[0], "yes") == 0) {
+ if (mode & MODE_ALIAS) {
+ VarPacketAliasSetMode((unsigned) param, (unsigned) param);
+ return 0;
+ }
+ LogPrintf(LogWARN, "alias not enabled\n");
+ } else if (strcmp(argv[0], "no") == 0) {
+ if (mode & MODE_ALIAS) {
+ VarPacketAliasSetMode(0, (unsigned) param);
+ return 0;
+ }
+ LogPrintf(LogWARN, "alias not enabled\n");
+ }
+ return -1;
+}
diff --git a/usr.sbin/ppp/command.h b/usr.sbin/ppp/command.h
new file mode 100644
index 0000000..f4282d0
--- /dev/null
+++ b/usr.sbin/ppp/command.h
@@ -0,0 +1,51 @@
+/*
+ * Written by Toshiharu OHNO (tony-o@iij.ad.jp)
+ *
+ * Copyright (C) 1993, Internet Initiative Japan, Inc. All rights reserverd.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the Internet Initiative Japan. The name of the
+ * IIJ may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: command.h,v 1.8 1997/10/26 01:02:28 brian Exp $
+ *
+ * TODO:
+ */
+
+struct cmdtab {
+ char *name;
+ char *alias;
+ int (*func) ();
+ u_char lauth;
+ char *helpmes;
+ char *syntax;
+ void *args;
+};
+
+#define VAR_AUTHKEY 0
+#define VAR_DIAL 1
+#define VAR_LOGIN 2
+#define VAR_AUTHNAME 3
+#define VAR_DEVICE 4
+#define VAR_ACCMAP 5
+#define VAR_PHONE 6
+#define VAR_HANGUP 7
+#ifdef HAVE_DES
+#define VAR_ENC 8
+#endif
+
+extern struct in_addr ifnetmask;
+extern int aft_cmd;
+
+extern int SetVariable(struct cmdtab const *, int, char **, int);
+extern void Prompt(void);
+extern int IsInteractive(int);
+extern void DecodeCommand(char *, int, int);
diff --git a/usr.sbin/ppp/defs.c b/usr.sbin/ppp/defs.c
new file mode 100644
index 0000000..be9134a
--- /dev/null
+++ b/usr.sbin/ppp/defs.c
@@ -0,0 +1,26 @@
+/*
+ * $Id: $
+ */
+
+#include <stdlib.h>
+
+#include "defs.h"
+
+int mode = MODE_INTER;
+int BGFiledes[2] = { -1, -1 };
+int modem = -1;
+int tun_in = -1;
+int tun_out = -1;
+int netfd = -1;
+char *dstsystem = NULL;
+
+void
+randinit()
+{
+ static int initdone;
+
+ if (!initdone) {
+ initdone = 1;
+ srandomdev();
+ }
+}
diff --git a/usr.sbin/ppp/defs.h b/usr.sbin/ppp/defs.h
new file mode 100644
index 0000000..ca20d45
--- /dev/null
+++ b/usr.sbin/ppp/defs.h
@@ -0,0 +1,88 @@
+/*
+ * Written by Toshiharu OHNO (tony-o@iij.ad.jp)
+ *
+ * Copyright (C) 1993, Internet Initiative Japan, Inc. All rights reserverd.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the Internet Initiative Japan. The name of the
+ * IIJ may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: defs.h,v 1.23 1997/10/26 12:42:10 brian Exp $
+ *
+ * TODO:
+ */
+
+/*
+ * Check following definitions for your machine environment
+ */
+#ifdef __FreeBSD__
+# define MODEM_DEV "/dev/cuaa1" /* name of tty device */
+# define BASE_MODEM_DEV "cuaa1" /* name of base tty device */
+#else
+# ifdef __OpenBSD__
+# define MODEM_DEV "/dev/cua01" /* name of tty device */
+# define BASE_MODEM_DEV "cua01" /* name of base tty device */
+# else
+# define MODEM_DEV "/dev/tty01" /* name of tty device */
+# define BASE_MODEM_DEV "tty01" /* name of base tty device */
+# endif
+#endif
+
+#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_TIMER 3 /* Default timer for carrier loss */
+#define RECONNECT_TRIES 0 /* Default retries on carrier loss */
+#define REDIAL_PERIOD 30 /* Default Hold time to redial */
+#define NEXT_REDIAL_PERIOD 3 /* Default Hold time to next number redial */
+#define SCRIPT_LEN 512 /* Size of login scripts */
+#define LINE_LEN SCRIPT_LEN /* Size of login scripts */
+
+#define CONFFILE "ppp.conf"
+#define LINKUPFILE "ppp.linkup"
+#define LINKDOWNFILE "ppp.linkdown"
+#define SECRETFILE "ppp.secret"
+
+/*
+ * Definition of working mode
+ */
+#define MODE_INTER 1 /* Interactive mode */
+#define MODE_AUTO 2 /* Auto calling mode */
+#define MODE_DIRECT 4 /* Direct connection mode */
+#define MODE_DEDICATED 8 /* Dedicated line mode */
+#define MODE_DDIAL 16 /* Dedicated dialing line mode */
+#define MODE_ALIAS 32 /* Packet aliasing (masquerading) */
+#define MODE_BACKGROUND 64 /* Background mode. */
+
+#define EX_SIG -1
+#define EX_NORMAL 0
+#define EX_START 1
+#define EX_SOCK 2
+#define EX_MODEM 3
+#define EX_DIAL 4
+#define EX_DEAD 5
+#define EX_DONE 6
+#define EX_REBOOT 7
+#define EX_ERRDEAD 8
+#define EX_HANGUP 10
+#define EX_TERM 11
+#define EX_NODIAL 12
+#define EX_NOLOGIN 13
+
+extern int mode;
+extern int BGFiledes[2];
+extern int modem;
+extern int tun_in;
+extern int tun_out;
+extern int netfd;
+extern char *dstsystem;
+
+extern void randinit(void);
diff --git a/usr.sbin/ppp/filter.c b/usr.sbin/ppp/filter.c
new file mode 100644
index 0000000..121ae04
--- /dev/null
+++ b/usr.sbin/ppp/filter.c
@@ -0,0 +1,484 @@
+/*
+ * PPP Filter command Interface
+ *
+ * Written by Toshiharu OHNO (tony-o@iij.ad.jp)
+ *
+ * Copyright (C) 1993, Internet Initiative Japan, Inc. All rights reserverd.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the Internet Initiative Japan. The name of the
+ * IIJ may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: filter.c,v 1.16 1997/10/26 01:02:34 brian Exp $
+ *
+ * TODO: Shoud send ICMP error message when we discard packets.
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <strings.h>
+
+#include "command.h"
+#include "mbuf.h"
+#include "log.h"
+#include "loadalias.h"
+#include "defs.h"
+#include "vars.h"
+#include "ipcp.h"
+#include "filter.h"
+
+struct filterent ifilters[MAXFILTERS]; /* incoming packet filter */
+struct filterent ofilters[MAXFILTERS]; /* outgoing packet filter */
+struct filterent dfilters[MAXFILTERS]; /* dial-out packet filter */
+struct filterent afilters[MAXFILTERS]; /* keep-alive packet filter */
+
+static struct filterent filterdata;
+
+static u_long netmasks[33] = {
+ 0x00000000,
+ 0x80000000, 0xC0000000, 0xE0000000, 0xF0000000,
+ 0xF8000000, 0xFC000000, 0xFE000000, 0xFF000000,
+ 0xFF800000, 0xFFC00000, 0xFFE00000, 0xFFF00000,
+ 0xFFF80000, 0xFFFC0000, 0xFFFE0000, 0xFFFF0000,
+ 0xFFFF8000, 0xFFFFC000, 0xFFFFE000, 0xFFFFF000,
+ 0xFFFFF800, 0xFFFFFC00, 0xFFFFFE00, 0xFFFFFF00,
+ 0xFFFFFF80, 0xFFFFFFC0, 0xFFFFFFE0, 0xFFFFFFF0,
+ 0xFFFFFFF8, 0xFFFFFFFC, 0xFFFFFFFE, 0xFFFFFFFF,
+};
+
+int
+ParseAddr(int argc,
+ char **argv,
+ struct in_addr * paddr,
+ struct in_addr * pmask,
+ int *pwidth)
+{
+ int bits;
+ char *cp, *wp;
+
+ if (argc < 1) {
+ LogPrintf(LogWARN, "ParseAddr: address/mask is expected.\n");
+ return (0);
+ }
+ pmask->s_addr = 0xffffffff; /* Assume 255.255.255.255 as default */
+ cp = strchr(*argv, '/');
+ if (cp)
+ *cp++ = '\0';
+ if (strcasecmp(*argv, "HISADDR") == 0)
+ *paddr = IpcpInfo.his_ipaddr;
+ else if (strcasecmp(*argv, "MYADDR") == 0)
+ *paddr = IpcpInfo.want_ipaddr;
+ else if (inet_aton(*argv, paddr) == 0) {
+ LogPrintf(LogWARN, "ParseAddr: %s: Bad address\n", *argv);
+ return (0);
+ }
+ if (cp && *cp) {
+ bits = strtol(cp, &wp, 0);
+ if (cp == wp || bits < 0 || bits > 32) {
+ LogPrintf(LogWARN, "ParseAddr: bad mask width.\n");
+ return (0);
+ }
+ } else {
+ /* if width is not given, assume whole 32 bits are meaningfull */
+ bits = 32;
+ }
+
+ *pwidth = bits;
+ pmask->s_addr = htonl(netmasks[bits]);
+
+ return (1);
+}
+
+static int
+ParseProto(int argc, char **argv)
+{
+ int proto;
+
+ if (argc < 1)
+ return (P_NONE);
+
+ if (!strcmp(*argv, "tcp"))
+ proto = P_TCP;
+ else if (!strcmp(*argv, "udp"))
+ proto = P_UDP;
+ else if (!strcmp(*argv, "icmp"))
+ proto = P_ICMP;
+ else
+ proto = P_NONE;
+ return (proto);
+}
+
+static int
+ParsePort(char *service, int proto)
+{
+ char *protocol_name, *cp;
+ struct servent *servent;
+ int port;
+
+ switch (proto) {
+ case P_UDP:
+ protocol_name = "udp";
+ break;
+ case P_TCP:
+ protocol_name = "tcp";
+ break;
+ default:
+ protocol_name = 0;
+ }
+
+ servent = getservbyname(service, protocol_name);
+ if (servent != 0)
+ return (ntohs(servent->s_port));
+
+ port = strtol(service, &cp, 0);
+ if (cp == service) {
+ LogPrintf(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 **argv)
+{
+ int type;
+ char *cp;
+
+ switch (argc) {
+ case 0:
+ /* permit/deny all ICMP types */
+ filterdata.opt.srcop = OP_NONE;
+ break;
+ default:
+ LogPrintf(LogWARN, "ParseIcmp: bad icmp syntax.\n");
+ return (0);
+ case 3:
+ if (!strcmp(*argv, "src") && !strcmp(argv[1], "eq")) {
+ type = strtol(argv[2], &cp, 0);
+ if (cp == argv[2]) {
+ LogPrintf(LogWARN, "ParseIcmp: type is expected.\n");
+ return (0);
+ }
+ filterdata.opt.srcop = OP_EQ;
+ filterdata.opt.srcport = type;
+ }
+ break;
+ }
+ return (1);
+}
+
+static int
+ParseOp(char *cp)
+{
+ int op = OP_NONE;
+
+ if (!strcmp(cp, "eq"))
+ op = OP_EQ;
+ else if (!strcmp(cp, "gt"))
+ op = OP_GT;
+ else if (!strcmp(cp, "lt"))
+ op = OP_LT;
+ return (op);
+}
+
+/*
+ * UDP Syntax: [src op port] [dst op port]
+ */
+static int
+ParseUdpOrTcp(int argc, char **argv, int proto)
+{
+ filterdata.opt.srcop = filterdata.opt.dstop = OP_NONE;
+ filterdata.opt.estab = 0;
+
+ if (argc == 0) {
+ /* permit/deny all tcp traffic */
+ return (1);
+ }
+
+ if (argc >= 3 && !strcmp(*argv, "src")) {
+ filterdata.opt.srcop = ParseOp(argv[1]);
+ if (filterdata.opt.srcop == OP_NONE) {
+ LogPrintf(LogWARN, "ParseUdpOrTcp: bad operation\n");
+ return (0);
+ }
+ filterdata.opt.srcport = ParsePort(argv[2], proto);
+ if (filterdata.opt.srcport == 0)
+ return (0);
+ argc -= 3;
+ argv += 3;
+ if (argc == 0)
+ return (1);
+ }
+ if (argc >= 3 && !strcmp(argv[0], "dst")) {
+ filterdata.opt.dstop = ParseOp(argv[1]);
+ if (filterdata.opt.dstop == OP_NONE) {
+ LogPrintf(LogWARN, "ParseUdpOrTcp: bad operation\n");
+ return (0);
+ }
+ filterdata.opt.dstport = ParsePort(argv[2], proto);
+ if (filterdata.opt.dstport == 0)
+ return (0);
+ argc -= 3;
+ argv += 3;
+ if (argc == 0)
+ return (1);
+ }
+ if (argc == 1 && proto == P_TCP) {
+ if (!strcmp(*argv, "estab")) {
+ filterdata.opt.estab = 1;
+ return (1);
+ }
+ LogPrintf(LogWARN, "ParseUdpOrTcp: estab is expected: %s\n", *argv);
+ return (0);
+ }
+ if (argc > 0)
+ LogPrintf(LogWARN, "ParseUdpOrTcp: bad src/dst port syntax: %s\n", *argv);
+ return (0);
+}
+
+char *opname[] = {"none", "eq", "gt", NULL, "lt"};
+
+static int
+Parse(int argc, char **argv, struct filterent * ofp)
+{
+ int action, proto;
+ int val;
+ char *wp;
+ struct filterent *fp = &filterdata;
+
+ val = strtol(*argv, &wp, 0);
+ if (*argv == wp || val > MAXFILTERS) {
+ LogPrintf(LogWARN, "Parse: invalid filter number.\n");
+ return (0);
+ }
+ if (val < 0) {
+ for (val = 0; val < MAXFILTERS; val++) {
+ ofp->action = A_NONE;
+ ofp++;
+ }
+ LogPrintf(LogWARN, "Parse: filter cleared.\n");
+ return (1);
+ }
+ ofp += val;
+
+ if (--argc == 0) {
+ LogPrintf(LogWARN, "Parse: missing action.\n");
+ return (0);
+ }
+ argv++;
+
+ proto = P_NONE;
+ memset(&filterdata, '\0', sizeof(filterdata));
+
+ if (!strcmp(*argv, "permit")) {
+ action = A_PERMIT;
+ } else if (!strcmp(*argv, "deny")) {
+ action = A_DENY;
+ } else if (!strcmp(*argv, "clear")) {
+ ofp->action = A_NONE;
+ return (1);
+ } else {
+ LogPrintf(LogWARN, "Parse: bad action: %s\n", *argv);
+ return (0);
+ }
+ fp->action = action;
+
+ argc--;
+ argv++;
+
+ if (fp->action == A_DENY) {
+ if (!strcmp(*argv, "host")) {
+ fp->action |= A_UHOST;
+ argc--;
+ argv++;
+ } else if (!strcmp(*argv, "port")) {
+ fp->action |= A_UPORT;
+ argc--;
+ argv++;
+ }
+ }
+ proto = ParseProto(argc, argv);
+ if (proto == P_NONE) {
+ if (ParseAddr(argc, argv, &fp->saddr, &fp->smask, &fp->swidth)) {
+ argc--;
+ argv++;
+ proto = ParseProto(argc, argv);
+ if (proto == P_NONE) {
+ if (ParseAddr(argc, argv, &fp->daddr, &fp->dmask, &fp->dwidth)) {
+ argc--;
+ argv++;
+ }
+ proto = ParseProto(argc, argv);
+ if (proto != P_NONE) {
+ argc--;
+ argv++;
+ }
+ } else {
+ argc--;
+ argv++;
+ }
+ } else {
+ LogPrintf(LogWARN, "Parse: Address/protocol expected.\n");
+ return (0);
+ }
+ } else {
+ argc--;
+ argv++;
+ }
+
+ val = 1;
+ fp->proto = proto;
+
+ switch (proto) {
+ case P_TCP:
+ val = ParseUdpOrTcp(argc, argv, P_TCP);
+ break;
+ case P_UDP:
+ val = ParseUdpOrTcp(argc, argv, P_UDP);
+ break;
+ case P_ICMP:
+ val = ParseIcmp(argc, argv);
+ break;
+ }
+
+ LogPrintf(LogDEBUG, "Parse: Src: %s\n", inet_ntoa(fp->saddr));
+ LogPrintf(LogDEBUG, "Parse: Src mask: %s\n", inet_ntoa(fp->smask));
+ LogPrintf(LogDEBUG, "Parse: Dst: %s\n", inet_ntoa(fp->daddr));
+ LogPrintf(LogDEBUG, "Parse: Dst mask: %s\n", inet_ntoa(fp->dmask));
+ LogPrintf(LogDEBUG, "Parse: Proto = %d\n", proto);
+
+ LogPrintf(LogDEBUG, "Parse: src: %s (%d)\n", opname[fp->opt.srcop],
+ fp->opt.srcport);
+ LogPrintf(LogDEBUG, "Parse: dst: %s (%d)\n", opname[fp->opt.dstop],
+ fp->opt.dstport);
+ LogPrintf(LogDEBUG, "Parse: estab: %d\n", fp->opt.estab);
+
+ if (val)
+ *ofp = *fp;
+ return (val);
+}
+
+int
+SetIfilter(struct cmdtab *list, int argc, char **argv)
+{
+ if (argc > 0) {
+ (void) Parse(argc, argv, ifilters);
+ return 0;
+ }
+ return -1;
+}
+
+int
+SetOfilter(struct cmdtab *list, int argc, char **argv)
+{
+ if (argc > 0) {
+ (void) Parse(argc, argv, ofilters);
+ return 0;
+ }
+ return -1;
+}
+
+int
+SetDfilter(struct cmdtab *list, int argc, char **argv)
+{
+ if (argc > 0) {
+ (void) Parse(argc, argv, dfilters);
+ return 0;
+ }
+ return -1;
+}
+
+int
+SetAfilter(struct cmdtab *list, int argc, char **argv)
+{
+ if (argc > 0) {
+ (void) Parse(argc, argv, afilters);
+ return 0;
+ }
+ return -1;
+}
+
+static char *protoname[] = {
+ "none", "tcp", "udp", "icmp",
+};
+
+static char *actname[] = {
+ "none ", "permit ", "deny ",
+};
+
+static void
+ShowFilter(struct filterent * fp)
+{
+ int n;
+
+ if (!VarTerm)
+ return;
+
+ for (n = 0; n < MAXFILTERS; n++, fp++) {
+ if (fp->action != A_NONE) {
+ fprintf(VarTerm, "%2d %s", n, actname[fp->action]);
+ fprintf(VarTerm, "%s/%d ", inet_ntoa(fp->saddr), fp->swidth);
+ fprintf(VarTerm, "%s/%d ", inet_ntoa(fp->daddr), fp->dwidth);
+ if (fp->proto) {
+ fprintf(VarTerm, "%s", protoname[fp->proto]);
+
+ if (fp->opt.srcop)
+ fprintf(VarTerm, " src %s %d", opname[fp->opt.srcop],
+ fp->opt.srcport);
+ if (fp->opt.dstop)
+ fprintf(VarTerm, " dst %s %d", opname[fp->opt.dstop],
+ fp->opt.dstport);
+ if (fp->opt.estab)
+ fprintf(VarTerm, " estab");
+
+ }
+ fprintf(VarTerm, "\n");
+ }
+ }
+}
+
+int
+ShowIfilter(struct cmdtab * list, int argc, char **argv)
+{
+ ShowFilter(ifilters);
+ return 0;
+}
+
+int
+ShowOfilter(struct cmdtab * list, int argc, char **argv)
+{
+ ShowFilter(ofilters);
+ return 0;
+}
+
+int
+ShowDfilter(struct cmdtab * list, int argc, char **argv)
+{
+ ShowFilter(dfilters);
+ return 0;
+}
+
+int
+ShowAfilter(struct cmdtab * list, int argc, char **argv)
+{
+ ShowFilter(afilters);
+ return 0;
+}
diff --git a/usr.sbin/ppp/filter.h b/usr.sbin/ppp/filter.h
new file mode 100644
index 0000000..f4ea051
--- /dev/null
+++ b/usr.sbin/ppp/filter.h
@@ -0,0 +1,87 @@
+/*
+ * Written by Toshiharu OHNO (tony-o@iij.ad.jp)
+ *
+ * Copyright (C) 1993, Internet Initiative Japan, Inc. All rights reserverd.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the Internet Initiative Japan. The name of the
+ * IIJ may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: filter.h,v 1.9 1997/08/25 00:29:11 brian Exp $
+ *
+ * TODO:
+ */
+
+/*
+ * Actions
+ */
+#define A_NONE 0
+#define A_PERMIT 1
+#define A_DENY 2
+#define A_MASK 3
+#define A_UHOST 4
+#define A_UPORT 8
+
+/*
+ * Known protocols
+ */
+#define P_NONE 0
+#define P_TCP 1
+#define P_UDP 2
+#define P_ICMP 3
+
+/*
+ * Operations
+ */
+#define OP_NONE 0
+#define OP_EQ 1
+#define OP_GT 2
+#define OP_LT 4
+
+struct filterent {
+ int action; /* Filtering action */
+ int swidth; /* Effective source address width */
+ struct in_addr saddr; /* Source address */
+ struct in_addr smask; /* Source address mask */
+ int dwidth; /* Effective destination address width */
+ struct in_addr daddr; /* Destination address */
+ struct in_addr dmask; /* Destination address mask */
+ int proto; /* Protocol */
+ struct {
+ short srcop;
+ u_short srcport;
+ short dstop;
+ u_short dstport;
+ int estab;
+ } opt;
+};
+
+#define MAXFILTERS 20
+
+#define FL_IN 0
+#define FL_OUT 1
+#define FL_DIAL 2
+#define FL_KEEP 3
+
+extern struct filterent ifilters[MAXFILTERS]; /* incoming packet filter */
+extern struct filterent ofilters[MAXFILTERS]; /* outgoing packet filter */
+extern struct filterent dfilters[MAXFILTERS]; /* dial-out packet filter */
+extern struct filterent afilters[MAXFILTERS]; /* keep-alive packet filter */
+
+extern int ParseAddr(int, char **, struct in_addr *, struct in_addr *, int *);
+extern int ShowIfilter(struct cmdtab *, int, char **);
+extern int ShowOfilter(struct cmdtab *, int, char **);
+extern int ShowDfilter(struct cmdtab *, int, char **);
+extern int ShowAfilter(struct cmdtab *, int, char **);
+extern int SetIfilter(struct cmdtab *, int, char **);
+extern int SetOfilter(struct cmdtab *, int, char **);
+extern int SetDfilter(struct cmdtab *, int, char **);
+extern int SetAfilter(struct cmdtab *, int, char **);
diff --git a/usr.sbin/ppp/fsm.c b/usr.sbin/ppp/fsm.c
new file mode 100644
index 0000000..4a11d4b
--- /dev/null
+++ b/usr.sbin/ppp/fsm.c
@@ -0,0 +1,785 @@
+/*
+ * PPP Finite State Machine for LCP/IPCP
+ *
+ * Written by Toshiharu OHNO (tony-o@iij.ad.jp)
+ *
+ * Copyright (C) 1993, Internet Initiative Japan, Inc. All rights reserverd.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the Internet Initiative Japan, Inc. The name of the
+ * IIJ may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: fsm.c,v 1.19 1997/09/10 23:55:35 brian Exp $
+ *
+ * TODO:
+ * o Refer loglevel for log output
+ * o Better option log display
+ */
+#include <sys/param.h>
+#include <netinet/in.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <termios.h>
+
+#include "mbuf.h"
+#include "log.h"
+#include "defs.h"
+#include "timer.h"
+#include "fsm.h"
+#include "hdlc.h"
+#include "lqr.h"
+#include "lcpproto.h"
+#include "lcp.h"
+#include "ccp.h"
+#include "modem.h"
+#include "loadalias.h"
+#include "command.h"
+#include "vars.h"
+#include "pred.h"
+
+u_char AckBuff[200];
+u_char NakBuff[200];
+u_char RejBuff[100];
+u_char ReqBuff[200];
+u_char *ackp = NULL;
+u_char *nakp = NULL;
+u_char *rejp = NULL;
+
+static void FsmSendConfigReq(struct fsm *);
+static void FsmSendTerminateReq(struct fsm *);
+static void FsmInitRestartCounter(struct fsm *);
+
+char const *StateNames[] = {
+ "Initial", "Starting", "Closed", "Stopped", "Closing", "Stopping",
+ "Req-Sent", "Ack-Rcvd", "Ack-Sent", "Opened",
+};
+
+static void
+StoppedTimeout(struct fsm * fp)
+{
+ LogPrintf(fp->LogLevel, "Stopped timer expired\n");
+ if (modem != -1)
+ DownConnection();
+ else
+ FsmDown(fp);
+}
+
+void
+FsmInit(struct fsm * fp)
+{
+ LogPrintf(LogDEBUG, "FsmInit\n");
+ fp->state = ST_INITIAL;
+ fp->reqid = 1;
+ fp->restart = 1;
+ fp->maxconfig = 3;
+}
+
+static void
+NewState(struct fsm * fp, int new)
+{
+ LogPrintf(fp->LogLevel, "State change %s --> %s\n",
+ StateNames[fp->state], StateNames[new]);
+ if (fp->state == ST_STOPPED && fp->StoppedTimer.state == TIMER_RUNNING)
+ StopTimer(&fp->StoppedTimer);
+ fp->state = new;
+ if ((new >= ST_INITIAL && new <= ST_STOPPED) || (new == ST_OPENED)) {
+ StopTimer(&fp->FsmTimer);
+ if (new == ST_STOPPED && fp->StoppedTimer.load) {
+ fp->StoppedTimer.state = TIMER_STOPPED;
+ fp->StoppedTimer.func = StoppedTimeout;
+ fp->StoppedTimer.arg = (void *) fp;
+ StartTimer(&fp->StoppedTimer);
+ }
+ }
+}
+
+void
+FsmOutput(struct fsm * fp, u_int code, u_int id, u_char * ptr, int count)
+{
+ int plen;
+ struct fsmheader lh;
+ struct mbuf *bp;
+
+ plen = sizeof(struct fsmheader) + count;
+ lh.code = code;
+ lh.id = id;
+ lh.length = htons(plen);
+ bp = mballoc(plen, MB_FSM);
+ memcpy(MBUF_CTOP(bp), &lh, sizeof(struct fsmheader));
+ if (count)
+ memcpy(MBUF_CTOP(bp) + sizeof(struct fsmheader), ptr, count);
+ LogDumpBp(LogDEBUG, "FsmOutput", bp);
+ HdlcOutput(PRI_LINK, fp->proto, bp);
+}
+
+void
+FsmOpen(struct fsm * fp)
+{
+ switch (fp->state) {
+ case ST_INITIAL:
+ (fp->LayerStart) (fp);
+ NewState(fp, ST_STARTING);
+ break;
+ case ST_STARTING:
+ break;
+ case ST_CLOSED:
+ if (fp->open_mode == OPEN_PASSIVE) {
+ NewState(fp, ST_STOPPED);
+ } else {
+ FsmInitRestartCounter(fp);
+ FsmSendConfigReq(fp);
+ NewState(fp, ST_REQSENT);
+ }
+ 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
+FsmUp(struct fsm * fp)
+{
+ switch (fp->state) {
+ case ST_INITIAL:
+ NewState(fp, ST_CLOSED);
+ break;
+ case ST_STARTING:
+ FsmInitRestartCounter(fp);
+ FsmSendConfigReq(fp);
+ NewState(fp, ST_REQSENT);
+ break;
+ default:
+ LogPrintf(fp->LogLevel, "Oops, Up at %s\n", StateNames[fp->state]);
+ break;
+ }
+}
+
+void
+FsmDown(struct fsm * fp)
+{
+ switch (fp->state) {
+ case ST_CLOSED:
+ case ST_CLOSING:
+ NewState(fp, ST_INITIAL);
+ break;
+ case ST_STOPPED:
+ (fp->LayerStart) (fp);
+ /* Fall into.. */
+ case ST_STOPPING:
+ case ST_REQSENT:
+ case ST_ACKRCVD:
+ case ST_ACKSENT:
+ NewState(fp, ST_STARTING);
+ break;
+ case ST_OPENED:
+ (fp->LayerDown) (fp);
+ NewState(fp, ST_STARTING);
+ break;
+ }
+}
+
+void
+FsmClose(struct fsm * fp)
+{
+ switch (fp->state) {
+ case ST_STARTING:
+ NewState(fp, ST_INITIAL);
+ break;
+ case ST_STOPPED:
+ NewState(fp, ST_CLOSED);
+ break;
+ case ST_STOPPING:
+ NewState(fp, ST_CLOSING);
+ break;
+ case ST_OPENED:
+ (fp->LayerDown) (fp);
+ /* Fall down */
+ case ST_REQSENT:
+ case ST_ACKRCVD:
+ case ST_ACKSENT:
+ FsmInitRestartCounter(fp);
+ FsmSendTerminateReq(fp);
+ NewState(fp, ST_CLOSING);
+ break;
+ }
+}
+
+/*
+ * Send functions
+ */
+static void
+FsmSendConfigReq(struct fsm * fp)
+{
+ if (--fp->maxconfig > 0) {
+ (fp->SendConfigReq) (fp);
+ StartTimer(&fp->FsmTimer); /* Start restart timer */
+ fp->restart--; /* Decrement restart counter */
+ } else {
+ FsmClose(fp);
+ }
+}
+
+static void
+FsmSendTerminateReq(struct fsm * fp)
+{
+ LogPrintf(fp->LogLevel, "SendTerminateReq.\n");
+ FsmOutput(fp, CODE_TERMREQ, fp->reqid++, NULL, 0);
+ (fp->SendTerminateReq) (fp);
+ StartTimer(&fp->FsmTimer); /* Start restart timer */
+ fp->restart--; /* Decrement restart counter */
+}
+
+static void
+FsmSendConfigAck(struct fsm * fp,
+ struct fsmheader * lhp,
+ u_char * option,
+ int count)
+{
+ LogPrintf(fp->LogLevel, "SendConfigAck(%s)\n", StateNames[fp->state]);
+ (fp->DecodeConfig) (option, count, MODE_NOP);
+ FsmOutput(fp, CODE_CONFIGACK, lhp->id, option, count);
+}
+
+static void
+FsmSendConfigRej(struct fsm * fp,
+ struct fsmheader * lhp,
+ u_char * option,
+ int count)
+{
+ LogPrintf(fp->LogLevel, "SendConfigRej(%s)\n", StateNames[fp->state]);
+ (fp->DecodeConfig) (option, count, MODE_NOP);
+ FsmOutput(fp, CODE_CONFIGREJ, lhp->id, option, count);
+}
+
+static void
+FsmSendConfigNak(struct fsm * fp,
+ struct fsmheader * lhp,
+ u_char * option,
+ int count)
+{
+ LogPrintf(fp->LogLevel, "SendConfigNak(%s)\n", StateNames[fp->state]);
+ (fp->DecodeConfig) (option, count, MODE_NOP);
+ FsmOutput(fp, CODE_CONFIGNAK, lhp->id, option, count);
+}
+
+/*
+ * Timeout actions
+ */
+static void
+FsmTimeout(struct fsm * fp)
+{
+ 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;
+ }
+ StartTimer(&fp->FsmTimer);
+ } else {
+ switch (fp->state) {
+ case ST_CLOSING:
+ NewState(fp, ST_CLOSED);
+ (fp->LayerFinish) (fp);
+ break;
+ case ST_STOPPING:
+ NewState(fp, ST_STOPPED);
+ (fp->LayerFinish) (fp);
+ break;
+ case ST_REQSENT: /* XXX: 3p */
+ case ST_ACKSENT:
+ case ST_ACKRCVD:
+ NewState(fp, ST_STOPPED);
+ (fp->LayerFinish) (fp);
+ break;
+ }
+ }
+}
+
+static void
+FsmInitRestartCounter(struct fsm * fp)
+{
+ StopTimer(&fp->FsmTimer);
+ fp->FsmTimer.state = TIMER_STOPPED;
+ fp->FsmTimer.func = FsmTimeout;
+ fp->FsmTimer.arg = (void *) fp;
+ (fp->InitRestartCounter) (fp);
+}
+
+/*
+ * Actions when receive packets
+ */
+static void
+FsmRecvConfigReq(struct fsm * fp, struct fsmheader * lhp, struct mbuf * bp)
+/* RCR */
+{
+ int plen, flen;
+ int ackaction = 0;
+
+ plen = plength(bp);
+ flen = ntohs(lhp->length) - sizeof(*lhp);
+ if (plen < flen) {
+ LogPrintf(LogERROR, "FsmRecvConfigReq: plen (%d) < flen (%d)\n",
+ plen, flen);
+ pfree(bp);
+ return;
+ }
+
+ /*
+ * Check and process easy case
+ */
+ switch (fp->state) {
+ case ST_INITIAL:
+ case ST_STARTING:
+ LogPrintf(fp->LogLevel, "Oops, RCR in %s.\n", StateNames[fp->state]);
+ pfree(bp);
+ return;
+ case ST_CLOSED:
+ (fp->SendTerminateAck) (fp);
+ pfree(bp);
+ return;
+ case ST_CLOSING:
+ case ST_STOPPING:
+ LogPrintf(LogERROR, "Got ConfigReq while state = %d\n", fp->state);
+ pfree(bp);
+ return;
+ }
+
+ (fp->DecodeConfig) (MBUF_CTOP(bp), flen, MODE_REQ);
+
+ if (nakp == NakBuff && rejp == RejBuff)
+ ackaction = 1;
+
+ switch (fp->state) {
+ case ST_OPENED:
+ (fp->LayerDown) (fp);
+ FsmSendConfigReq(fp);
+ break;
+ case ST_STOPPED:
+ FsmInitRestartCounter(fp);
+ FsmSendConfigReq(fp);
+ break;
+ }
+
+ if (rejp != RejBuff)
+ FsmSendConfigRej(fp, lhp, RejBuff, rejp - RejBuff);
+ if (nakp != NakBuff)
+ FsmSendConfigNak(fp, lhp, NakBuff, nakp - NakBuff);
+ if (ackaction)
+ FsmSendConfigAck(fp, lhp, AckBuff, ackp - AckBuff);
+
+ switch (fp->state) {
+ case ST_STOPPED:
+ case ST_OPENED:
+ if (ackaction)
+ NewState(fp, ST_ACKSENT);
+ else
+ NewState(fp, ST_REQSENT);
+ break;
+ case ST_REQSENT:
+ if (ackaction)
+ NewState(fp, ST_ACKSENT);
+ break;
+ case ST_ACKRCVD:
+ if (ackaction) {
+ NewState(fp, ST_OPENED);
+ (fp->LayerUp) (fp);
+ }
+ break;
+ case ST_ACKSENT:
+ if (!ackaction)
+ NewState(fp, ST_REQSENT);
+ break;
+ }
+ pfree(bp);
+}
+
+static void
+FsmRecvConfigAck(struct fsm * fp, struct fsmheader * lhp, struct mbuf * bp)
+/* RCA */
+{
+ switch (fp->state) {
+ case ST_CLOSED:
+ case ST_STOPPED:
+ (fp->SendTerminateAck) (fp);
+ break;
+ case ST_CLOSING:
+ case ST_STOPPING:
+ break;
+ case ST_REQSENT:
+ FsmInitRestartCounter(fp);
+ NewState(fp, ST_ACKRCVD);
+ break;
+ case ST_ACKRCVD:
+ FsmSendConfigReq(fp);
+ NewState(fp, ST_REQSENT);
+ break;
+ case ST_ACKSENT:
+ FsmInitRestartCounter(fp);
+ NewState(fp, ST_OPENED);
+ (fp->LayerUp) (fp);
+ break;
+ case ST_OPENED:
+ (fp->LayerDown) (fp);
+ FsmSendConfigReq(fp);
+ NewState(fp, ST_REQSENT);
+ break;
+ }
+ pfree(bp);
+}
+
+static void
+FsmRecvConfigNak(struct fsm * fp, struct fsmheader * lhp, struct mbuf * bp)
+/* RCN */
+{
+ int plen, flen;
+
+ plen = plength(bp);
+ flen = ntohs(lhp->length) - sizeof(*lhp);
+ if (plen < flen) {
+ pfree(bp);
+ return;
+ }
+
+ /*
+ * Check and process easy case
+ */
+ switch (fp->state) {
+ case ST_INITIAL:
+ case ST_STARTING:
+ LogPrintf(fp->LogLevel, "Oops, RCN in %s.\n", StateNames[fp->state]);
+ pfree(bp);
+ return;
+ case ST_CLOSED:
+ case ST_STOPPED:
+ (fp->SendTerminateAck) (fp);
+ pfree(bp);
+ return;
+ case ST_CLOSING:
+ case ST_STOPPING:
+ pfree(bp);
+ return;
+ }
+
+ (fp->DecodeConfig) (MBUF_CTOP(bp), flen, MODE_NAK);
+
+ switch (fp->state) {
+ case ST_REQSENT:
+ case ST_ACKSENT:
+ FsmInitRestartCounter(fp);
+ FsmSendConfigReq(fp);
+ break;
+ case ST_OPENED:
+ (fp->LayerDown) (fp);
+ /* Fall down */
+ case ST_ACKRCVD:
+ FsmSendConfigReq(fp);
+ NewState(fp, ST_REQSENT);
+ break;
+ }
+
+ pfree(bp);
+}
+
+static void
+FsmRecvTermReq(struct fsm * fp, struct fsmheader * lhp, struct mbuf * bp)
+/* RTR */
+{
+ switch (fp->state) {
+ case ST_INITIAL:
+ case ST_STARTING:
+ LogPrintf(fp->LogLevel, "Oops, RTR in %s\n", StateNames[fp->state]);
+ break;
+ case ST_CLOSED:
+ case ST_STOPPED:
+ case ST_CLOSING:
+ case ST_STOPPING:
+ case ST_REQSENT:
+ (fp->SendTerminateAck) (fp);
+ break;
+ case ST_ACKRCVD:
+ case ST_ACKSENT:
+ (fp->SendTerminateAck) (fp);
+ NewState(fp, ST_REQSENT);
+ break;
+ case ST_OPENED:
+ (fp->LayerDown) (fp);
+ (fp->SendTerminateAck) (fp);
+ StartTimer(&fp->FsmTimer); /* Start restart timer */
+ fp->restart = 0;
+ NewState(fp, ST_STOPPING);
+ break;
+ }
+ pfree(bp);
+}
+
+static void
+FsmRecvTermAck(struct fsm * fp, struct fsmheader * lhp, struct mbuf * bp)
+/* RTA */
+{
+ switch (fp->state) {
+ case ST_CLOSING:
+ NewState(fp, ST_CLOSED);
+ (fp->LayerFinish) (fp);
+ break;
+ case ST_STOPPING:
+ NewState(fp, ST_STOPPED);
+ (fp->LayerFinish) (fp);
+ break;
+ case ST_ACKRCVD:
+ NewState(fp, ST_REQSENT);
+ break;
+ case ST_OPENED:
+ (fp->LayerDown) (fp);
+ FsmSendConfigReq(fp);
+ NewState(fp, ST_REQSENT);
+ break;
+ }
+ pfree(bp);
+}
+
+static void
+FsmRecvConfigRej(struct fsm * fp, struct fsmheader * lhp, struct mbuf * bp)
+/* RCJ */
+{
+ int plen, flen;
+
+ plen = plength(bp);
+ flen = ntohs(lhp->length) - sizeof(*lhp);
+ if (plen < flen) {
+ pfree(bp);
+ return;
+ }
+ LogPrintf(fp->LogLevel, "RecvConfigRej.\n");
+
+ /*
+ * Check and process easy case
+ */
+ switch (fp->state) {
+ case ST_INITIAL:
+ case ST_STARTING:
+ LogPrintf(fp->LogLevel, "Oops, RCJ in %s.\n", StateNames[fp->state]);
+ pfree(bp);
+ return;
+ case ST_CLOSED:
+ case ST_STOPPED:
+ (fp->SendTerminateAck) (fp);
+ pfree(bp);
+ return;
+ case ST_CLOSING:
+ case ST_STOPPING:
+ pfree(bp);
+ return;
+ }
+
+ (fp->DecodeConfig) (MBUF_CTOP(bp), flen, MODE_REJ);
+
+ switch (fp->state) {
+ case ST_REQSENT:
+ case ST_ACKSENT:
+ FsmInitRestartCounter(fp);
+ FsmSendConfigReq(fp);
+ break;
+ case ST_OPENED:
+ (fp->LayerDown) (fp);
+ /* Fall down */
+ case ST_ACKRCVD:
+ FsmSendConfigReq(fp);
+ NewState(fp, ST_REQSENT);
+ break;
+ }
+ pfree(bp);
+}
+
+static void
+FsmRecvCodeRej(struct fsm * fp, struct fsmheader * lhp, struct mbuf * bp)
+{
+ LogPrintf(fp->LogLevel, "RecvCodeRej\n");
+ pfree(bp);
+}
+
+static void
+FsmRecvProtoRej(struct fsm * fp, struct fsmheader * lhp, struct mbuf * bp)
+{
+ u_short *sp, proto;
+
+ sp = (u_short *) MBUF_CTOP(bp);
+ proto = ntohs(*sp);
+ LogPrintf(fp->LogLevel, "-- Protocol (%04x) was rejected.\n", proto);
+
+ switch (proto) {
+ case PROTO_LQR:
+ StopLqr(LQM_LQR);
+ break;
+ case PROTO_CCP:
+ fp = &CcpFsm;
+ (fp->LayerFinish) (fp);
+ switch (fp->state) {
+ case ST_CLOSED:
+ case ST_CLOSING:
+ NewState(fp, ST_CLOSED);
+ default:
+ NewState(fp, ST_STOPPED);
+ break;
+ }
+ break;
+ }
+ pfree(bp);
+}
+
+static void
+FsmRecvEchoReq(struct fsm * fp, struct fsmheader * lhp, struct mbuf * bp)
+{
+ u_char *cp;
+ u_long *lp, magic;
+
+ cp = MBUF_CTOP(bp);
+ lp = (u_long *) cp;
+ magic = ntohl(*lp);
+ if (magic != LcpInfo.his_magic) {
+ LogPrintf(LogERROR, "RecvEchoReq: his magic is bad!!\n");
+ /* XXX: We should send terminate request */
+ }
+ if (fp->state == ST_OPENED) {
+ *lp = htonl(LcpInfo.want_magic); /* Insert local magic number */
+ LogPrintf(fp->LogLevel, "SendEchoRep(%s)\n", StateNames[fp->state]);
+ FsmOutput(fp, CODE_ECHOREP, lhp->id, cp, plength(bp));
+ }
+ pfree(bp);
+}
+
+static void
+FsmRecvEchoRep(struct fsm * fp, struct fsmheader * lhp, struct mbuf * bp)
+{
+ u_long *lp, magic;
+
+ lp = (u_long *) MBUF_CTOP(bp);
+ magic = ntohl(*lp);
+/*
+ * Tolerate echo replies with either magic number
+ */
+ if (magic != 0 && magic != LcpInfo.his_magic && magic != LcpInfo.want_magic) {
+ LogPrintf(LogERROR, "RecvEchoRep: his magic is wrong! expect: %x got: %x\n",
+ LcpInfo.his_magic, magic);
+
+ /*
+ * XXX: We should send terminate request. But poor implementation may die
+ * as a result.
+ */
+ }
+ RecvEchoLqr(bp);
+ pfree(bp);
+}
+
+static void
+FsmRecvDiscReq(struct fsm * fp, struct fsmheader * lhp, struct mbuf * bp)
+{
+ LogPrintf(fp->LogLevel, "RecvDiscReq\n");
+ pfree(bp);
+}
+
+static void
+FsmRecvIdent(struct fsm * fp, struct fsmheader * lhp, struct mbuf * bp)
+{
+ LogPrintf(fp->LogLevel, "RecvIdent\n");
+ pfree(bp);
+}
+
+static void
+FsmRecvTimeRemain(struct fsm * fp, struct fsmheader * lhp, struct mbuf * bp)
+{
+ LogPrintf(fp->LogLevel, "RecvTimeRemain\n");
+ pfree(bp);
+}
+
+static void
+FsmRecvResetReq(struct fsm * fp, struct fsmheader * lhp, struct mbuf * bp)
+{
+ LogPrintf(fp->LogLevel, "RecvResetReq\n");
+ CcpRecvResetReq(fp);
+ LogPrintf(fp->LogLevel, "SendResetAck\n");
+ FsmOutput(fp, CODE_RESETACK, fp->reqid, NULL, 0);
+ pfree(bp);
+}
+
+static void
+FsmRecvResetAck(struct fsm * fp, struct fsmheader * lhp, struct mbuf * bp)
+{
+ LogPrintf(fp->LogLevel, "RecvResetAck\n");
+ Pred1Init(1); /* Initialize Input part */
+ fp->reqid++;
+ pfree(bp);
+}
+
+struct fsmcodedesc FsmCodes[] = {
+ {FsmRecvConfigReq, "Configure Request",},
+ {FsmRecvConfigAck, "Configure Ack",},
+ {FsmRecvConfigNak, "Configure Nak",},
+ {FsmRecvConfigRej, "Configure Reject",},
+ {FsmRecvTermReq, "Terminate Request",},
+ {FsmRecvTermAck, "Terminate Ack",},
+ {FsmRecvCodeRej, "Code Reject",},
+ {FsmRecvProtoRej, "Protocol Reject",},
+ {FsmRecvEchoReq, "Echo Request",},
+ {FsmRecvEchoRep, "Echo Reply",},
+ {FsmRecvDiscReq, "Discard Request",},
+ {FsmRecvIdent, "Ident",},
+ {FsmRecvTimeRemain, "Time Remain",},
+ {FsmRecvResetReq, "Reset Request",},
+ {FsmRecvResetAck, "Reset Ack",},
+};
+
+void
+FsmInput(struct fsm * fp, struct mbuf * bp)
+{
+ int len;
+ struct fsmheader *lhp;
+ struct fsmcodedesc *codep;
+
+ len = plength(bp);
+ if (len < sizeof(struct fsmheader)) {
+ pfree(bp);
+ return;
+ }
+ lhp = (struct fsmheader *) MBUF_CTOP(bp);
+ if (lhp->code == 0 || lhp->code > fp->max_code) {
+ pfree(bp); /* XXX: Should send code reject */
+ return;
+ }
+ bp->offset += sizeof(struct fsmheader);
+ bp->cnt -= sizeof(struct fsmheader);
+
+ codep = FsmCodes + lhp->code - 1;
+ LogPrintf(fp->LogLevel, "Received %s (%d) state = %s (%d)\n",
+ codep->name, lhp->id, StateNames[fp->state], fp->state);
+ if (LogIsKept(LogDEBUG))
+ LogMemory();
+ (codep->action) (fp, lhp, bp);
+ if (LogIsKept(LogDEBUG))
+ LogMemory();
+}
diff --git a/usr.sbin/ppp/fsm.h b/usr.sbin/ppp/fsm.h
new file mode 100644
index 0000000..901b962
--- /dev/null
+++ b/usr.sbin/ppp/fsm.h
@@ -0,0 +1,132 @@
+/*
+ * Written by Toshiharu OHNO (tony-o@iij.ad.jp)
+ *
+ * Copyright (C) 1993, Internet Initiative Japan, Inc. All rights reserverd.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the Internet Initiative Japan. The name of the
+ * IIJ may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: fsm.h,v 1.10 1997/08/25 00:29:12 brian Exp $
+ *
+ * TODO:
+ */
+
+/*
+ * State of machine
+ */
+#define ST_INITIAL 0
+#define ST_STARTING 1
+#define ST_CLOSED 2
+#define ST_STOPPED 3
+#define ST_CLOSING 4
+#define ST_STOPPING 5
+#define ST_REQSENT 6
+#define ST_ACKRCVD 7
+#define ST_ACKSENT 8
+#define ST_OPENED 9
+
+#define ST_MAX 10
+#define ST_UNDEF -1
+
+#define MODE_REQ 0
+#define MODE_NAK 1
+#define MODE_REJ 2
+#define MODE_NOP 3
+
+#define OPEN_ACTIVE 0
+#define OPEN_PASSIVE 1
+
+struct fsm {
+ char *name; /* Name of protocol */
+ u_short proto; /* Protocol number */
+ u_short max_code;
+ int open_mode;
+ int state; /* State of the machine */
+ int reqid; /* Next request id */
+ int restart; /* Restart counter value */
+ int maxconfig;
+
+ int reqcode; /* Request code sent */
+ struct pppTimer FsmTimer; /* Restart Timer */
+
+ /*
+ * 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;
+
+ void (*LayerUp) (struct fsm *);
+ void (*LayerDown) (struct fsm *);
+ void (*LayerStart) (struct fsm *);
+ void (*LayerFinish) (struct fsm *);
+ void (*InitRestartCounter) (struct fsm *);
+ void (*SendConfigReq) (struct fsm *);
+ void (*SendTerminateReq) (struct fsm *);
+ void (*SendTerminateAck) (struct fsm *);
+ void (*DecodeConfig) (u_char *, int, int);
+};
+
+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 fsmcodedesc {
+ void (*action) (struct fsm *, struct fsmheader *, struct mbuf *);
+ char *name;
+};
+
+struct fsmconfig {
+ u_char type;
+ u_char length;
+};
+
+extern u_char AckBuff[200];
+extern u_char NakBuff[200];
+extern u_char RejBuff[100];
+extern u_char ReqBuff[200];
+extern u_char *ackp;
+extern u_char *nakp;
+extern u_char *rejp;
+
+extern char const *StateNames[];
+
+extern void FsmInit(struct fsm *);
+extern void FsmOutput(struct fsm *, u_int, u_int, u_char *, int);
+extern void FsmOpen(struct fsm *);
+extern void FsmUp(struct fsm *);
+extern void FsmDown(struct fsm *);
+extern void FsmInput(struct fsm *, struct mbuf *);
+extern void FsmClose(struct fsm *);
diff --git a/usr.sbin/ppp/hdlc.c b/usr.sbin/ppp/hdlc.c
new file mode 100644
index 0000000..ed33038
--- /dev/null
+++ b/usr.sbin/ppp/hdlc.c
@@ -0,0 +1,444 @@
+/*
+ * PPP High Level Link Control (HDLC) Module
+ *
+ * Written by Toshiharu OHNO (tony-o@iij.ad.jp)
+ *
+ * Copyright (C) 1993, Internet Initiative Japan, Inc. All rights reserverd.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the Internet Initiative Japan, Inc. The name of the
+ * IIJ may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: hdlc.c,v 1.20 1997/10/26 01:02:43 brian Exp $
+ *
+ * TODO:
+ */
+#include <sys/param.h>
+#include <netinet/in.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <termios.h>
+
+#include "mbuf.h"
+#include "log.h"
+#include "defs.h"
+#include "timer.h"
+#include "fsm.h"
+#include "hdlc.h"
+#include "lcpproto.h"
+#include "ipcp.h"
+#include "ip.h"
+#include "vjcomp.h"
+#include "pap.h"
+#include "chap.h"
+#include "lcp.h"
+#include "lqr.h"
+#include "loadalias.h"
+#include "command.h"
+#include "vars.h"
+#include "pred.h"
+#include "modem.h"
+#include "ccp.h"
+
+struct hdlcstat {
+ int badfcs;
+ int badaddr;
+ int badcommand;
+ int unknownproto;
+} HdlcStat;
+
+static int ifOutPackets;
+static int ifOutOctets;
+static int ifOutLQRs;
+static int ifInPackets;
+static int ifInOctets;
+
+struct protostat {
+ u_short number;
+ char *name;
+ u_long in_count;
+ u_long out_count;
+} ProtocolStat[] = {
+
+ {
+ PROTO_IP, "IP"
+ },
+ {
+ PROTO_VJUNCOMP, "VJ_UNCOMP"
+ },
+ {
+ PROTO_VJCOMP, "VJ_COMP"
+ },
+ {
+ PROTO_COMPD, "COMPD"
+ },
+ {
+ PROTO_LCP, "LCP"
+ },
+ {
+ PROTO_IPCP, "IPCP"
+ },
+ {
+ PROTO_CCP, "CCP"
+ },
+ {
+ PROTO_PAP, "PAP"
+ },
+ {
+ PROTO_LQR, "LQR"
+ },
+ {
+ PROTO_CHAP, "CHAP"
+ },
+ {
+ 0, "Others"
+ },
+};
+
+static u_short 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
+};
+
+u_char EscMap[33];
+
+void
+HdlcInit()
+{
+ ifInOctets = ifOutOctets = 0;
+ ifInPackets = ifOutPackets = 0;
+ ifOutLQRs = 0;
+}
+
+/*
+ * HDLC FCS computation. Read RFC 1171 Appendix B and CCITT X.25 section
+ * 2.27 for further details.
+ */
+inline u_short
+HdlcFcs(u_short fcs, u_char * cp, int len)
+{
+ while (len--)
+ fcs = (fcs >> 8) ^ fcstab[(fcs ^ *cp++) & 0xff];
+ return (fcs);
+}
+
+void
+HdlcOutput(int pri, u_short proto, struct mbuf * bp)
+{
+ struct mbuf *mhp, *mfcs;
+ struct protostat *statp;
+ struct lqrdata *lqr;
+ u_char *cp;
+ u_short fcs;
+
+ if ((proto & 0xfff1) == 0x21) { /* Network Layer protocol */
+ if (CcpFsm.state == ST_OPENED) {
+ if (CcpInfo.want_proto == TY_PRED1) {
+ Pred1Output(pri, proto, bp);
+ return;
+ }
+ }
+ }
+ if (DEV_IS_SYNC)
+ mfcs = NULLBUFF;
+ else
+ mfcs = mballoc(2, MB_HDLCOUT);
+ mhp = mballoc(4, MB_HDLCOUT);
+ mhp->cnt = 0;
+ cp = MBUF_CTOP(mhp);
+ if (proto == PROTO_LCP || LcpInfo.his_acfcomp == 0) {
+ *cp++ = HDLC_ADDR;
+ *cp++ = HDLC_UI;
+ mhp->cnt += 2;
+ }
+
+ /*
+ * If possible, compress protocol field.
+ */
+ if (LcpInfo.his_protocomp && (proto & 0xff00) == 0) {
+ *cp++ = proto;
+ mhp->cnt++;
+ } else {
+ *cp++ = proto >> 8;
+ *cp = proto & 0377;
+ mhp->cnt += 2;
+ }
+ mhp->next = bp;
+ bp->next = mfcs;
+
+ lqr = &MyLqrData;
+ lqr->PeerOutPackets = ifOutPackets++;
+ ifOutOctets += plength(mhp) + 1;
+ lqr->PeerOutOctets = ifOutOctets;
+
+ if (proto == PROTO_LQR) {
+ lqr->MagicNumber = LcpInfo.want_magic;
+ lqr->LastOutLQRs = HisLqrData.PeerOutLQRs;
+ lqr->LastOutPackets = HisLqrData.PeerOutPackets;
+ lqr->LastOutOctets = HisLqrData.PeerOutOctets;
+ lqr->PeerInLQRs = HisLqrSave.SaveInLQRs;
+ lqr->PeerInPackets = HisLqrSave.SaveInPackets;
+ lqr->PeerInDiscards = HisLqrSave.SaveInDiscards;
+ lqr->PeerInErrors = HisLqrSave.SaveInErrors;
+ lqr->PeerInOctets = HisLqrSave.SaveInOctets;
+ lqr->PeerOutLQRs = ++ifOutLQRs;
+ LqrDump("LqrOutput", lqr);
+ LqrChangeOrder(lqr, (struct lqrdata *) (MBUF_CTOP(bp)));
+ }
+ if (!DEV_IS_SYNC) {
+ fcs = HdlcFcs(INITFCS, MBUF_CTOP(mhp), mhp->cnt);
+ fcs = HdlcFcs(fcs, MBUF_CTOP(bp), bp->cnt);
+ fcs = ~fcs;
+ cp = MBUF_CTOP(mfcs);
+ *cp++ = fcs & 0377; /* Low byte first!! */
+ *cp++ = fcs >> 8;
+ }
+ LogDumpBp(LogHDLC, "HdlcOutput", mhp);
+ for (statp = ProtocolStat; statp->number; statp++)
+ if (statp->number == proto)
+ break;
+ statp->out_count++;
+ if (DEV_IS_SYNC)
+ ModemOutput(pri, mhp);
+ else
+ AsyncOutput(pri, mhp, proto);
+}
+
+void
+DecodePacket(u_short proto, struct mbuf * bp)
+{
+ u_char *cp;
+
+ LogPrintf(LogDEBUG, "DecodePacket: proto = %04x\n", proto);
+
+ switch (proto) {
+ case PROTO_LCP:
+ LcpInput(bp);
+ break;
+ case PROTO_PAP:
+ PapInput(bp);
+ break;
+ case PROTO_LQR:
+ HisLqrSave.SaveInLQRs++;
+ LqrInput(bp);
+ break;
+ case PROTO_CHAP:
+ ChapInput(bp);
+ break;
+ case PROTO_VJUNCOMP:
+ case PROTO_VJCOMP:
+ bp = VjCompInput(bp, proto);
+ if (bp == NULLBUFF) {
+ break;
+ }
+ /* fall down */
+ case PROTO_IP:
+ IpInput(bp);
+ break;
+ case PROTO_IPCP:
+ IpcpInput(bp);
+ break;
+ case PROTO_CCP:
+ CcpInput(bp);
+ break;
+ case PROTO_COMPD:
+ Pred1Input(bp);
+ break;
+ default:
+ LogPrintf(LogPHASE, "Unknown protocol 0x%04x\n", proto);
+ bp->offset -= 2;
+ bp->cnt += 2;
+ cp = MBUF_CTOP(bp);
+ LcpSendProtoRej(cp, bp->cnt);
+ HisLqrSave.SaveInDiscards++;
+ HdlcStat.unknownproto++;
+ pfree(bp);
+ break;
+ }
+}
+
+int
+ReportProtStatus()
+{
+ struct protostat *statp;
+ int cnt;
+
+ statp = ProtocolStat;
+ statp--;
+ cnt = 0;
+ fprintf(VarTerm, " Protocol in out Protocol in out\n");
+ do {
+ statp++;
+ fprintf(VarTerm, " %-9s: %8lu, %8lu",
+ statp->name, statp->in_count, statp->out_count);
+ if (++cnt == 2) {
+ fprintf(VarTerm, "\n");
+ cnt = 0;
+ }
+ } while (statp->number);
+ if (cnt)
+ fprintf(VarTerm, "\n");
+ return (1);
+}
+
+int
+ReportHdlcStatus()
+{
+ struct hdlcstat *hp = &HdlcStat;
+
+ if (VarTerm) {
+ fprintf(VarTerm, "HDLC level errors\n\n");
+ fprintf(VarTerm, "FCS: %u ADDR: %u COMMAND: %u PROTO: %u\n",
+ hp->badfcs, hp->badaddr, hp->badcommand, hp->unknownproto);
+ }
+ return 0;
+}
+
+static struct hdlcstat laststat;
+
+void
+HdlcErrorCheck()
+{
+ struct hdlcstat *hp = &HdlcStat;
+ struct hdlcstat *op = &laststat;
+
+ if (memcmp(hp, op, sizeof(laststat))) {
+ LogPrintf(LogPHASE, "HDLC errors -> FCS: %u ADDR: %u COMD: %u PROTO: %u\n",
+ hp->badfcs - op->badfcs, hp->badaddr - op->badaddr,
+ hp->badcommand - op->badcommand, hp->unknownproto - op->unknownproto);
+ }
+ laststat = HdlcStat;
+}
+
+void
+HdlcInput(struct mbuf * bp)
+{
+ u_short fcs, proto;
+ u_char *cp, addr, ctrl;
+ struct protostat *statp;
+
+ LogDumpBp(LogHDLC, "HdlcInput:", bp);
+ if (DEV_IS_SYNC)
+ fcs = GOODFCS;
+ else
+ fcs = HdlcFcs(INITFCS, MBUF_CTOP(bp), bp->cnt);
+ HisLqrSave.SaveInOctets += bp->cnt + 1;
+
+ LogPrintf(LogDEBUG, "HdlcInput: fcs = %04x (%s)\n",
+ fcs, (fcs == GOODFCS) ? "good" : "bad");
+ if (fcs != GOODFCS) {
+ HisLqrSave.SaveInErrors++;
+ LogPrintf(LogDEBUG, "HdlcInput: Bad FCS\n");
+ HdlcStat.badfcs++;
+ pfree(bp);
+ return;
+ }
+ if (!DEV_IS_SYNC)
+ bp->cnt -= 2; /* discard FCS part */
+
+ if (bp->cnt < 2) { /* XXX: raise this bar ? */
+ pfree(bp);
+ return;
+ }
+ cp = MBUF_CTOP(bp);
+
+ ifInPackets++;
+ ifInOctets += bp->cnt;
+
+ if (!LcpInfo.want_acfcomp) {
+
+ /*
+ * We expect that packet is not compressed.
+ */
+ addr = *cp++;
+ if (addr != HDLC_ADDR) {
+ HisLqrSave.SaveInErrors++;
+ HdlcStat.badaddr++;
+ LogPrintf(LogDEBUG, "HdlcInput: addr %02x\n", *cp);
+ pfree(bp);
+ return;
+ }
+ ctrl = *cp++;
+ if (ctrl != HDLC_UI) {
+ HisLqrSave.SaveInErrors++;
+ HdlcStat.badcommand++;
+ LogPrintf(LogDEBUG, "HdlcInput: %02x\n", *cp);
+ pfree(bp);
+ return;
+ }
+ bp->offset += 2;
+ bp->cnt -= 2;
+ } else if (cp[0] == HDLC_ADDR && cp[1] == HDLC_UI) {
+
+ /*
+ * We can receive compressed packet, but peer still send uncompressed
+ * packet to me.
+ */
+ cp += 2;
+ bp->offset += 2;
+ bp->cnt -= 2;
+ }
+ if (LcpInfo.want_protocomp) {
+ proto = 0;
+ cp--;
+ do {
+ cp++;
+ bp->offset++;
+ bp->cnt--;
+ proto = proto << 8;
+ proto += *cp;
+ } while (!(proto & 1));
+ } else {
+ proto = *cp++ << 8;
+ proto |= *cp++;
+ bp->offset += 2;
+ bp->cnt -= 2;
+ }
+
+ for (statp = ProtocolStat; statp->number; statp++)
+ if (statp->number == proto)
+ break;
+ statp->in_count++;
+ HisLqrSave.SaveInPackets++;
+
+ DecodePacket(proto, bp);
+}
diff --git a/usr.sbin/ppp/hdlc.h b/usr.sbin/ppp/hdlc.h
new file mode 100644
index 0000000..362d05c
--- /dev/null
+++ b/usr.sbin/ppp/hdlc.h
@@ -0,0 +1,68 @@
+/*
+ * Written by Toshiharu OHNO (tony-o@iij.ad.jp)
+ *
+ * Copyright (C) 1993, Internet Initiative Japan, Inc. All rights reserverd.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the Internet Initiative Japan. The name of the
+ * IIJ may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: hdlc.h,v 1.10 1997/08/25 00:29:13 brian Exp $
+ *
+ * TODO:
+ */
+
+/*
+ * Definition for Async HDLC
+ */
+#define HDLC_SYN 0x7e /* SYNC character */
+#define HDLC_ESC 0x7d /* Escape character */
+#define HDLC_XOR 0x20 /* Modifier value */
+
+#define HDLC_ADDR 0xff
+#define HDLC_UI 0x03
+/*
+ * Definition for HDLC Frame Check Sequence
+ */
+#define INITFCS 0xffff /* Initial value for FCS computation */
+#define GOODFCS 0xf0b8 /* Good FCS value */
+
+#define DEF_MRU 1500
+#define MAX_MRU 2048
+#define MIN_MRU 296
+
+#define DEF_MTU 0 /* whatever peer says */
+#define MAX_MTU 2048
+#define MIN_MTU 296
+
+/*
+ * Output priority
+ */
+/* PRI_NORMAL and PRI_FAST have meaning only on the IP queue.
+ * All IP frames have the same priority once they are compressed.
+ * IP frames stay on the IP queue till they can be sent on the
+ * link. They are compressed at that time.
+*/
+#define PRI_NORMAL 0 /* Normal priority */
+#define PRI_FAST 1 /* Fast (interractive) */
+#define PRI_LINK 1 /* Urgent (LQR packets) */
+
+extern u_char EscMap[33];
+
+extern void HdlcInit(void);
+extern void HdlcErrorCheck(void);
+extern void HdlcInput(struct mbuf *);
+extern void HdlcOutput(int, u_short, struct mbuf *bp);
+extern void AsyncOutput(int, struct mbuf *, int);
+extern u_short HdlcFcs(u_short, u_char *, int);
+extern void DecodePacket(u_short, struct mbuf *);
+extern int ReportHdlcStatus(void);
+extern int ReportProtStatus(void);
diff --git a/usr.sbin/ppp/id.c b/usr.sbin/ppp/id.c
new file mode 100644
index 0000000..c37e5fe
--- /dev/null
+++ b/usr.sbin/ppp/id.c
@@ -0,0 +1,145 @@
+/*
+ * $Id: defs.c,v 1.1 1997/10/26 01:02:30 brian Exp $
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <sys/ioctl.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sysexits.h>
+#include <unistd.h>
+
+#include "mbuf.h"
+#include "log.h"
+#include "main.h"
+#ifdef __OpenBSD__
+#include <util.h>
+#else
+#include <libutil.h>
+#endif
+#include "id.h"
+
+static int uid;
+static int gid;
+static int euid;
+static int egid;
+
+void
+ID0init()
+{
+ uid = getuid();
+ gid = getgid();
+ euid = geteuid();
+ egid = getegid();
+}
+
+static void
+ID0setuser()
+{
+ if (setreuid(euid, uid) == -1) {
+ LogPrintf(LogERROR, "ID0setuser: Unable to setreuid!\n");
+ Cleanup(EX_NOPERM);
+ }
+}
+
+uid_t
+ID0realuid()
+{
+ return uid;
+}
+
+static void
+ID0set0()
+{
+ if (setreuid(uid, euid) == -1) {
+ LogPrintf(LogERROR, "ID0set0: Unable to setreuid!\n");
+ Cleanup(EX_NOPERM);
+ }
+}
+
+int
+ID0ioctl(int fd, unsigned long req, void *arg)
+{
+ int ret;
+
+ ID0set0();
+ ret = ioctl(fd, req, arg);
+ LogPrintf(LogID0, "%d = ioctl(%d, %d, %p)\n", ret, fd, req, arg);
+ ID0setuser();
+ return ret;
+}
+
+int
+ID0unlink(const char *name)
+{
+ int ret;
+
+ ID0set0();
+ ret = unlink(name);
+ LogPrintf(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);
+ LogPrintf(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);
+ LogPrintf(LogID0, "%p = fopen(\"%s\", \"%s\")\n", ret, path, mode);
+ ID0setuser();
+ return ret;
+}
+
+int
+ID0open(const char *path, int flags)
+{
+ int ret;
+
+ ID0set0();
+ ret = open(path, flags);
+ LogPrintf(LogID0, "%d = open(\"%s\", %d)\n", ret, path, flags);
+ ID0setuser();
+ return ret;
+}
+
+int
+ID0uu_lock(const char *ttyname)
+{
+ int ret;
+
+ ID0set0();
+ ret = uu_lock(ttyname);
+ LogPrintf(LogID0, "%d = uu_lock(\"%s\")\n", ret, ttyname);
+ ID0setuser();
+ return ret;
+}
+
+int
+ID0uu_unlock(const char *ttyname)
+{
+ int ret;
+
+ ID0set0();
+ ret = uu_unlock(ttyname);
+ LogPrintf(LogID0, "%d = uu_unlock(\"%s\")\n", ret, ttyname);
+ ID0setuser();
+ return ret;
+}
diff --git a/usr.sbin/ppp/id.h b/usr.sbin/ppp/id.h
new file mode 100644
index 0000000..ff569c3
--- /dev/null
+++ b/usr.sbin/ppp/id.h
@@ -0,0 +1,13 @@
+/*
+ * $Id$
+ */
+
+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 ID0uu_lock(const char *);
+extern int ID0uu_unlock(const char *);
diff --git a/usr.sbin/ppp/ip.c b/usr.sbin/ppp/ip.c
new file mode 100644
index 0000000..d7174a9
--- /dev/null
+++ b/usr.sbin/ppp/ip.c
@@ -0,0 +1,499 @@
+/*
+ * PPP IP Protocol Interface
+ *
+ * Written by Toshiharu OHNO (tony-o@iij.ad.jp)
+ *
+ * Copyright (C) 1993, Internet Initiative Japan, Inc. All rights reserverd.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the Internet Initiative Japan. The name of the
+ * IIJ may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: ip.c,v 1.26 1997/10/26 01:02:52 brian Exp $
+ *
+ * TODO:
+ * o Return ICMP message for filterd packet
+ * and optionaly record it into log.
+ */
+#include <sys/param.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/ip_icmp.h>
+#include <netinet/udp.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+
+#include <alias.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "mbuf.h"
+#include "log.h"
+#include "defs.h"
+#include "timer.h"
+#include "fsm.h"
+#include "lcpproto.h"
+#include "hdlc.h"
+#include "loadalias.h"
+#include "command.h"
+#include "vars.h"
+#include "filter.h"
+#include "log.h"
+#include "os.h"
+#include "ipcp.h"
+#include "vjcomp.h"
+#include "lcp.h"
+#include "modem.h"
+#include "ip.h"
+
+static struct pppTimer IdleTimer;
+
+static void
+IdleTimeout()
+{
+ LogPrintf(LogPHASE, "Idle timer expired.\n");
+ reconnect(RECON_FALSE);
+ LcpClose();
+}
+
+/*
+ * Start Idle timer. If timeout is reached, we call LcpClose() to
+ * close LCP and link.
+ */
+void
+StartIdleTimer()
+{
+ if (!(mode & (MODE_DEDICATED | MODE_DDIAL))) {
+ StopTimer(&IdleTimer);
+ IdleTimer.func = IdleTimeout;
+ IdleTimer.load = VarIdleTimeout * SECTICKS;
+ IdleTimer.state = TIMER_STOPPED;
+ StartTimer(&IdleTimer);
+ }
+}
+
+void
+UpdateIdleTimer()
+{
+ if (OsLinkIsUp())
+ StartIdleTimer();
+}
+
+void
+StopIdleTimer()
+{
+ StopTimer(&IdleTimer);
+}
+
+/*
+ * If any IP layer traffic is detected, refresh IdleTimer.
+ */
+static void
+RestartIdleTimer()
+{
+ if (!(mode & (MODE_DEDICATED | MODE_DDIAL)) && ipKeepAlive) {
+ StartTimer(&IdleTimer);
+ ipIdleSecs = 0;
+ }
+}
+
+static u_short interactive_ports[32] = {
+ 544, 513, 514, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 21, 22, 23, 0, 0, 0, 0, 0, 0, 0, 543,
+};
+
+#define INTERACTIVE(p) (interactive_ports[(p) & 0x1F] == (p))
+
+static char *TcpFlags[] = {
+ "FIN", "SYN", "RST", "PSH", "ACK", "URG",
+};
+
+static char *Direction[] = {"INP", "OUT", "OUT", "IN/OUT"};
+static struct filterent *Filters[] = {ifilters, ofilters, dfilters, afilters};
+
+static int
+PortMatch(int op, u_short pport, u_short rport)
+{
+ switch (op) {
+ case OP_EQ:
+ return (pport == rport);
+ case OP_GT:
+ return (pport > rport);
+ case OP_LT:
+ return (pport < rport);
+ default:
+ return (0);
+ }
+}
+
+/*
+ * Check a packet against with defined filters
+ */
+static int
+FilterCheck(struct ip * pip, int direction)
+{
+ struct filterent *fp = Filters[direction];
+ int gotinfo, cproto, estab, n;
+ struct tcphdr *th;
+ struct udphdr *uh;
+ struct icmp *ih;
+ char *ptop;
+ u_short sport, dport;
+
+ if (fp->action) {
+ cproto = gotinfo = estab = 0;
+ sport = dport = 0;
+ for (n = 0; n < MAXFILTERS; n++) {
+ if (fp->action) {
+ /* permit fragments on in and out filter */
+ if ((direction == FL_IN || direction == FL_OUT) &&
+ (ntohs(pip->ip_off) & IP_OFFMASK) != 0) {
+ return (A_PERMIT);
+ }
+ LogPrintf(LogDEBUG, "rule = %d\n", n);
+ if ((pip->ip_src.s_addr & fp->smask.s_addr) == fp->saddr.s_addr
+ && (pip->ip_dst.s_addr & fp->dmask.s_addr) == fp->daddr.s_addr) {
+ if (fp->proto) {
+ if (!gotinfo) {
+ ptop = (char *) pip + (pip->ip_hl << 2);
+
+ switch (pip->ip_p) {
+ case IPPROTO_ICMP:
+ cproto = P_ICMP;
+ ih = (struct icmp *) ptop;
+ sport = ih->icmp_type;
+ estab = 1;
+ break;
+ case IPPROTO_UDP:
+ cproto = P_UDP;
+ uh = (struct udphdr *) ptop;
+ sport = ntohs(uh->uh_sport);
+ dport = ntohs(uh->uh_dport);
+ estab = 1;
+ break;
+ case IPPROTO_TCP:
+ cproto = P_TCP;
+ th = (struct tcphdr *) ptop;
+ sport = ntohs(th->th_sport);
+ dport = ntohs(th->th_dport);
+ estab = (th->th_flags & TH_ACK);
+ if (estab == 0)
+ LogPrintf(LogDEBUG, "flag = %02x, sport = %d, dport = %d\n",
+ th->th_flags, sport, dport);
+ break;
+ default:
+ return (A_DENY);/* We'll block unknown type of packet */
+ }
+ gotinfo = 1;
+ LogPrintf(LogDEBUG, "dir = %d, proto = %d, srcop = %d,"
+ " dstop = %d, estab = %d\n", direction, cproto,
+ fp->opt.srcop, fp->opt.dstop, estab);
+ }
+ LogPrintf(LogDEBUG, "check0: rule = %d, proto = %d, sport = %d,"
+ " dport = %d\n", n, cproto, sport, dport);
+ LogPrintf(LogDEBUG, "check0: action = %d\n", fp->action);
+
+ if (cproto == fp->proto) {
+ if ((fp->opt.srcop == OP_NONE ||
+ PortMatch(fp->opt.srcop, sport, fp->opt.srcport))
+ &&
+ (fp->opt.dstop == OP_NONE ||
+ PortMatch(fp->opt.dstop, dport, fp->opt.dstport))
+ &&
+ (fp->opt.estab == 0 || estab)) {
+ return (fp->action);
+ }
+ }
+ } else {
+ /* Address is mached. Make a decision. */
+ LogPrintf(LogDEBUG, "check1: action = %d\n", fp->action);
+ return (fp->action);
+ }
+ }
+ }
+ fp++;
+ }
+ return (A_DENY); /* No rule is mached. Deny this packet */
+ }
+ return (A_PERMIT); /* No rule is given. Permit this packet */
+}
+
+static void
+IcmpError(struct ip * pip, int code)
+{
+#ifdef notdef
+ struct mbuf *bp;
+
+ if (pip->ip_p != IPPROTO_ICMP) {
+ bp = mballoc(cnt, MB_IPIN);
+ memcpy(MBUF_CTOP(bp), ptr, cnt);
+ SendPppFrame(bp);
+ RestartIdleTimer();
+ ipOutOctets += cnt;
+ }
+#endif
+}
+
+/*
+ * For debugging aid.
+ */
+int
+PacketCheck(char *cp, int nb, int direction)
+{
+ struct ip *pip;
+ struct tcphdr *th;
+ struct udphdr *uh;
+ struct icmp *icmph;
+ char *ptop;
+ int mask, len, n;
+ int pri = PRI_NORMAL;
+ int logit, loglen;
+ static char logbuf[200];
+
+ logit = LogIsKept(LogTCPIP);
+ loglen = 0;
+
+ pip = (struct ip *) cp;
+
+ if (logit && loglen < sizeof logbuf) {
+ snprintf(logbuf + loglen, sizeof logbuf - loglen, "%s ",
+ Direction[direction]);
+ loglen += strlen(logbuf + loglen);
+ }
+ ptop = (cp + (pip->ip_hl << 2));
+
+ switch (pip->ip_p) {
+ case IPPROTO_ICMP:
+ if (logit && loglen < sizeof logbuf) {
+ icmph = (struct icmp *) ptop;
+ snprintf(logbuf + loglen, sizeof logbuf - loglen,
+ "ICMP: %s:%d ---> ", inet_ntoa(pip->ip_src), icmph->icmp_type);
+ loglen += strlen(logbuf + loglen);
+ snprintf(logbuf + loglen, sizeof logbuf - loglen,
+ "%s:%d", inet_ntoa(pip->ip_dst), icmph->icmp_type);
+ loglen += strlen(logbuf + loglen);
+ }
+ break;
+ case IPPROTO_UDP:
+ if (logit && loglen < sizeof logbuf) {
+ uh = (struct udphdr *) ptop;
+ snprintf(logbuf + loglen, sizeof logbuf - loglen,
+ "UDP: %s:%d ---> ", inet_ntoa(pip->ip_src), ntohs(uh->uh_sport));
+ loglen += strlen(logbuf + loglen);
+ snprintf(logbuf + loglen, sizeof logbuf - loglen,
+ "%s:%d", inet_ntoa(pip->ip_dst), ntohs(uh->uh_dport));
+ loglen += strlen(logbuf + loglen);
+ }
+ break;
+ case IPPROTO_TCP:
+ th = (struct tcphdr *) ptop;
+ if (pip->ip_tos == IPTOS_LOWDELAY)
+ pri = PRI_FAST;
+ else if ((ntohs(pip->ip_off) & IP_OFFMASK) == 0) {
+ if (INTERACTIVE(ntohs(th->th_sport)) || INTERACTIVE(ntohs(th->th_dport)))
+ pri = PRI_FAST;
+ }
+ if (logit && loglen < sizeof logbuf) {
+ len = ntohs(pip->ip_len) - (pip->ip_hl << 2) - (th->th_off << 2);
+ snprintf(logbuf + loglen, sizeof logbuf - loglen,
+ "TCP: %s:%d ---> ", inet_ntoa(pip->ip_src), ntohs(th->th_sport));
+ loglen += strlen(logbuf + loglen);
+ snprintf(logbuf + loglen, sizeof logbuf - loglen,
+ "%s:%d", inet_ntoa(pip->ip_dst), ntohs(th->th_dport));
+ loglen += strlen(logbuf + loglen);
+ n = 0;
+ for (mask = TH_FIN; mask != 0x40; mask <<= 1) {
+ if (th->th_flags & mask) {
+ snprintf(logbuf + loglen, sizeof logbuf - loglen, " %s", TcpFlags[n]);
+ loglen += strlen(logbuf + loglen);
+ }
+ n++;
+ }
+ snprintf(logbuf + loglen, sizeof logbuf - loglen,
+ " seq:%x ack:%x (%d/%d)",
+ ntohl(th->th_seq), ntohl(th->th_ack), len, nb);
+ loglen += strlen(logbuf + loglen);
+ if ((th->th_flags & TH_SYN) && nb > 40) {
+ u_short *sp;
+
+ ptop += 20;
+ sp = (u_short *) ptop;
+ if (ntohs(sp[0]) == 0x0204) {
+ snprintf(logbuf + loglen, sizeof logbuf - loglen,
+ " MSS = %d", ntohs(sp[1]));
+ loglen += strlen(logbuf + loglen);
+ }
+ }
+ }
+ break;
+ }
+
+ if (logit)
+ LogPrintf(LogTCPIP, "%s\n", logbuf);
+
+ if ((FilterCheck(pip, direction) & A_DENY)) {
+ LogPrintf(LogDEBUG, "blocked.\n");
+ if (direction == 0)
+ IcmpError(pip, pri);
+ return (-1);
+ } else {
+ if (FilterCheck(pip, FL_KEEP) & A_DENY) { /* Check Keep Alive filter */
+ ipKeepAlive = 0;
+ } else {
+ ipKeepAlive = 1;
+ }
+ return (pri);
+ }
+}
+
+void
+IpInput(struct mbuf * bp)
+{ /* IN: Pointer to IP pakcet */
+ u_char *cp;
+ struct mbuf *wp;
+ int nb, nw;
+ u_char tunbuff[MAX_MRU];
+
+ cp = tunbuff;
+ nb = 0;
+ for (wp = bp; wp; wp = wp->next) { /* Copy to contiguous region */
+ memcpy(cp, MBUF_CTOP(wp), wp->cnt);
+ cp += wp->cnt;
+ nb += wp->cnt;
+ }
+
+ if (mode & MODE_ALIAS) {
+ int iresult;
+ char *fptr;
+
+ iresult = VarPacketAliasIn(tunbuff, sizeof tunbuff);
+ nb = ntohs(((struct ip *) tunbuff)->ip_len);
+
+ if (nb > MAX_MRU) {
+ LogPrintf(LogERROR, "IpInput: Problem with IP header length\n");
+ pfree(bp);
+ return;
+ }
+ if (iresult == PKT_ALIAS_OK
+ || iresult == PKT_ALIAS_FOUND_HEADER_FRAGMENT) {
+ if (PacketCheck(tunbuff, nb, FL_IN) < 0) {
+ pfree(bp);
+ return;
+ }
+ ipInOctets += nb;
+
+ nb = ntohs(((struct ip *) tunbuff)->ip_len);
+ nw = write(tun_out, tunbuff, nb);
+ if (nw != nb)
+ if (nw == -1)
+ LogPrintf(LogERROR, "IpInput: wrote %d, got %s\n", nb,
+ strerror(errno));
+ else
+ LogPrintf(LogERROR, "IpInput: wrote %d, got %d\n", nb, nw);
+
+ if (iresult == PKT_ALIAS_FOUND_HEADER_FRAGMENT) {
+ while ((fptr = VarPacketAliasGetFragment(tunbuff)) != NULL) {
+ VarPacketAliasFragmentIn(tunbuff, fptr);
+ nb = ntohs(((struct ip *) fptr)->ip_len);
+ nw = write(tun_out, fptr, nb);
+ if (nw != nb)
+ if (nw == -1)
+ LogPrintf(LogERROR, "IpInput: wrote %d, got %s\n", nb,
+ strerror(errno));
+ else
+ LogPrintf(LogERROR, "IpInput: wrote %d, got %d\n", nb, nw);
+ free(fptr);
+ }
+ }
+ } else if (iresult == PKT_ALIAS_UNRESOLVED_FRAGMENT) {
+ nb = ntohs(((struct ip *) tunbuff)->ip_len);
+ fptr = malloc(nb);
+ if (fptr == NULL)
+ LogPrintf(LogALERT, "IpInput: Cannot allocate memory for fragment\n");
+ else {
+ memcpy(fptr, tunbuff, nb);
+ VarPacketAliasSaveFragment(fptr);
+ }
+ }
+ } else { /* no aliasing */
+ if (PacketCheck(tunbuff, nb, FL_IN) < 0) {
+ pfree(bp);
+ return;
+ }
+ ipInOctets += nb;
+ nw = write(tun_out, tunbuff, nb);
+ if (nw != nb)
+ if (nw == -1)
+ LogPrintf(LogERROR, "IpInput: wrote %d, got %s\n", nb, strerror(errno));
+ else
+ LogPrintf(LogERROR, "IpInput: wrote %d, got %d\n", nb, nw);
+ }
+ pfree(bp);
+
+ RestartIdleTimer();
+}
+
+static struct mqueue IpOutputQueues[PRI_FAST + 1];
+
+void
+IpEnqueue(int pri, char *ptr, int count)
+{
+ struct mbuf *bp;
+
+ bp = mballoc(count, MB_IPQ);
+ memcpy(MBUF_CTOP(bp), ptr, count);
+ Enqueue(&IpOutputQueues[pri], bp);
+}
+
+#if 0
+int
+IsIpEnqueued()
+{
+ struct mqueue *queue;
+ int exist = 0;
+
+ for (queue = &IpOutputQueues[PRI_FAST]; queue >= IpOutputQueues; queue--) {
+ if (queue->qlen > 0) {
+ exist = 1;
+ break;
+ }
+ }
+ return (exist);
+}
+#endif
+
+void
+IpStartOutput()
+{
+ struct mqueue *queue;
+ struct mbuf *bp;
+ int cnt;
+
+ if (IpcpFsm.state != ST_OPENED)
+ return;
+ for (queue = &IpOutputQueues[PRI_FAST]; queue >= IpOutputQueues; queue--) {
+ if (queue->top) {
+ bp = Dequeue(queue);
+ if (bp) {
+ cnt = plength(bp);
+ SendPppFrame(bp);
+ RestartIdleTimer();
+ ipOutOctets += cnt;
+ break;
+ }
+ }
+ }
+}
diff --git a/usr.sbin/ppp/ip.h b/usr.sbin/ppp/ip.h
new file mode 100644
index 0000000..056d655
--- /dev/null
+++ b/usr.sbin/ppp/ip.h
@@ -0,0 +1,30 @@
+/*
+ * User Process PPP
+ *
+ * Written by Toshiharu OHNO (tony-o@iij.ad.jp)
+ *
+ * Copyright (C) 1993, Internet Initiative Japan, Inc. All rights reserverd.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the Internet Initiative Japan, Inc. The name of the
+ * IIJ may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: ip.h,v 1.6 1997/10/26 01:02:53 brian Exp $
+ *
+ */
+
+extern void IpStartOutput(void);
+extern int PacketCheck(char *, int, int);
+extern void IpEnqueue(int, char *, int);
+extern void IpInput(struct mbuf *);
+extern void StartIdleTimer(void);
+extern void StopIdleTimer(void);
+extern void UpdateIdleTimer(void);
diff --git a/usr.sbin/ppp/ipcp.c b/usr.sbin/ppp/ipcp.c
new file mode 100644
index 0000000..c362184
--- /dev/null
+++ b/usr.sbin/ppp/ipcp.c
@@ -0,0 +1,618 @@
+/*
+ * PPP IP Control Protocol (IPCP) Module
+ *
+ * Written by Toshiharu OHNO (tony-o@iij.ad.jp)
+ *
+ * Copyright (C) 1993, Internet Initiative Japan, Inc. All rights reserverd.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the Internet Initiative Japan, Inc. The name of the
+ * IIJ may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: ipcp.c,v 1.33 1997/10/29 01:19:40 brian Exp $
+ *
+ * TODO:
+ * o More RFC1772 backwoard compatibility
+ */
+#include <sys/param.h>
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <arpa/inet.h>
+#include <sys/socket.h>
+#include <netdb.h>
+
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "mbuf.h"
+#include "log.h"
+#include "defs.h"
+#include "timer.h"
+#include "fsm.h"
+#include "lcpproto.h"
+#include "lcp.h"
+#include "ipcp.h"
+#include "slcompress.h"
+#include "os.h"
+#include "phase.h"
+#include "loadalias.h"
+#include "command.h"
+#include "vars.h"
+#include "vjcomp.h"
+#include "ip.h"
+
+#ifndef NOMSEXT
+struct in_addr ns_entries[2];
+struct in_addr nbns_entries[2];
+#endif
+
+struct ipcpstate IpcpInfo;
+struct in_range DefMyAddress;
+struct in_range DefHisAddress;
+struct in_addr TriggerAddress;
+int HaveTriggerAddress;
+struct pppTimer IpcpReportTimer;
+
+static void IpcpSendConfigReq(struct fsm *);
+static void IpcpSendTerminateAck(struct fsm *);
+static void IpcpSendTerminateReq(struct fsm *);
+static void IpcpDecodeConfig(u_char *, int, int);
+static void IpcpLayerStart(struct fsm *);
+static void IpcpLayerFinish(struct fsm *);
+static void IpcpLayerUp(struct fsm *);
+static void IpcpLayerDown(struct fsm *);
+static void IpcpInitRestartCounter(struct fsm *);
+static int IpcpOctetsIn(void);
+static int IpcpOctetsOut(void);
+
+static int lastInOctets, lastOutOctets;
+static int StartingIpIn, StartingIpOut;
+
+#define REJECTED(p, x) (p->his_reject & (1<<x))
+
+struct fsm IpcpFsm = {
+ "IPCP",
+ PROTO_IPCP,
+ IPCP_MAXCODE,
+ OPEN_ACTIVE,
+ ST_INITIAL,
+ 0, 0, 0,
+
+ 0,
+ {0, 0, 0, NULL, NULL, NULL},
+ {0, 0, 0, NULL, NULL, NULL},
+ LogIPCP,
+
+ IpcpLayerUp,
+ IpcpLayerDown,
+ IpcpLayerStart,
+ IpcpLayerFinish,
+ IpcpInitRestartCounter,
+ IpcpSendConfigReq,
+ IpcpSendTerminateReq,
+ IpcpSendTerminateAck,
+ IpcpDecodeConfig,
+};
+
+static char *cftypes[] = {
+ "???", "IPADDRS", "COMPPROTO", "IPADDR",
+};
+
+/*
+ * Function called every second. Updates connection period and idle period,
+ * also update LQR information.
+ */
+static void
+IpcpReportFunc()
+{
+ ipConnectSecs++;
+ if (lastInOctets == ipInOctets && lastOutOctets == ipOutOctets)
+ ipIdleSecs++;
+ lastInOctets = ipInOctets;
+ lastOutOctets = ipOutOctets;
+ StopTimer(&IpcpReportTimer);
+ IpcpReportTimer.state = TIMER_STOPPED;
+ StartTimer(&IpcpReportTimer);
+}
+
+static void
+IpcpStartReport()
+{
+ ipIdleSecs = ipConnectSecs = 0;
+ StopTimer(&IpcpReportTimer);
+ IpcpReportTimer.state = TIMER_STOPPED;
+ IpcpReportTimer.load = SECTICKS;
+ IpcpReportTimer.func = IpcpReportFunc;
+ StartTimer(&IpcpReportTimer);
+}
+
+int
+ReportIpcpStatus()
+{
+ struct ipcpstate *icp = &IpcpInfo;
+ struct fsm *fp = &IpcpFsm;
+
+ if (!VarTerm)
+ return 1;
+ fprintf(VarTerm, "%s [%s]\n", fp->name, StateNames[fp->state]);
+ fprintf(VarTerm, " his side: %s, %lx\n",
+ inet_ntoa(icp->his_ipaddr), icp->his_compproto);
+ fprintf(VarTerm, " my side: %s, %lx\n",
+ inet_ntoa(icp->want_ipaddr), icp->want_compproto);
+ fprintf(VarTerm, "Connected: %d secs, idle: %d secs\n\n",
+ ipConnectSecs, ipIdleSecs);
+ fprintf(VarTerm, " %d octets in, %d octets out\n",
+ IpcpOctetsIn(), IpcpOctetsOut());
+
+ fprintf(VarTerm, "Defaults:\n");
+ fprintf(VarTerm, " My Address: %s/%d\n",
+ inet_ntoa(DefMyAddress.ipaddr), DefMyAddress.width);
+ fprintf(VarTerm, " His Address: %s/%d\n",
+ inet_ntoa(DefHisAddress.ipaddr), DefHisAddress.width);
+ if (HaveTriggerAddress)
+ fprintf(VarTerm, " Negotiation(trigger): %s\n", inet_ntoa(TriggerAddress));
+ else
+ fprintf(VarTerm, " Negotiation(trigger): MYADDR\n");
+
+ return 0;
+}
+
+void
+IpcpDefAddress()
+{
+ struct hostent *hp;
+ char name[200];
+
+ memset(&DefMyAddress, '\0', sizeof(DefMyAddress));
+ memset(&DefHisAddress, '\0', sizeof(DefHisAddress));
+ TriggerAddress.s_addr = 0;
+ HaveTriggerAddress = 0;
+ if (gethostname(name, sizeof(name)) == 0) {
+ hp = gethostbyname(name);
+ if (hp && hp->h_addrtype == AF_INET) {
+ memcpy(&DefMyAddress.ipaddr.s_addr, hp->h_addr, hp->h_length);
+ }
+ }
+}
+
+void
+IpcpInit()
+{
+ struct ipcpstate *icp = &IpcpInfo;
+
+ FsmInit(&IpcpFsm);
+ memset(icp, '\0', sizeof(struct ipcpstate));
+ if ((mode & MODE_DEDICATED) && !dstsystem) {
+ icp->want_ipaddr.s_addr = icp->his_ipaddr.s_addr = 0;
+ } else {
+ icp->want_ipaddr.s_addr = DefMyAddress.ipaddr.s_addr;
+ icp->his_ipaddr.s_addr = DefHisAddress.ipaddr.s_addr;
+ }
+
+ /*
+ * 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").
+ */
+ if (HaveTriggerAddress) {
+ icp->want_ipaddr.s_addr = TriggerAddress.s_addr;
+ LogPrintf(LogIPCP, "Using trigger address %s\n", inet_ntoa(TriggerAddress));
+ }
+ if (Enabled(ConfVjcomp))
+ icp->want_compproto = (PROTO_VJCOMP << 16) | ((MAX_STATES - 1) << 8) | 1;
+ else
+ icp->want_compproto = 0;
+ icp->heis1172 = 0;
+ IpcpFsm.maxconfig = 10;
+ StartingIpIn = ipInOctets;
+ StartingIpOut = ipOutOctets;
+}
+
+static void
+IpcpInitRestartCounter(struct fsm * fp)
+{
+ fp->FsmTimer.load = VarRetryTimeout * SECTICKS;
+ fp->restart = 5;
+}
+
+static void
+IpcpSendConfigReq(struct fsm * fp)
+{
+ u_char *cp;
+ struct ipcpstate *icp = &IpcpInfo;
+
+ cp = ReqBuff;
+ LogPrintf(LogIPCP, "IpcpSendConfigReq\n");
+ if (!DEV_IS_SYNC || !REJECTED(icp, TY_IPADDR))
+ PutConfValue(&cp, cftypes, TY_IPADDR, 6, ntohl(icp->want_ipaddr.s_addr));
+ if (icp->want_compproto && !REJECTED(icp, TY_COMPPROTO)) {
+ if (icp->heis1172)
+ PutConfValue(&cp, cftypes, TY_COMPPROTO, 4, icp->want_compproto >> 16);
+ else
+ PutConfValue(&cp, cftypes, TY_COMPPROTO, 6, icp->want_compproto);
+ }
+ FsmOutput(fp, CODE_CONFIGREQ, fp->reqid++, ReqBuff, cp - ReqBuff);
+}
+
+static void
+IpcpSendTerminateReq(struct fsm * fp)
+{
+ /* XXX: No code yet */
+}
+
+static void
+IpcpSendTerminateAck(struct fsm * fp)
+{
+ LogPrintf(LogIPCP, "IpcpSendTerminateAck\n");
+ FsmOutput(fp, CODE_TERMACK, fp->reqid++, NULL, 0);
+}
+
+static void
+IpcpLayerStart(struct fsm * fp)
+{
+ LogPrintf(LogIPCP, "IpcpLayerStart.\n");
+}
+
+static void
+IpcpLayerFinish(struct fsm * fp)
+{
+ LogPrintf(LogIPCP, "IpcpLayerFinish.\n");
+ reconnect(RECON_FALSE);
+ LcpClose();
+ NewPhase(PHASE_TERMINATE);
+}
+
+static int
+IpcpOctetsIn()
+{
+ return ipInOctets < StartingIpIn ?
+ INT_MAX - StartingIpIn + ipInOctets - INT_MIN + 1 :
+ ipInOctets - StartingIpIn;
+}
+
+static int
+IpcpOctetsOut()
+{
+ return ipOutOctets < StartingIpOut ?
+ INT_MAX - StartingIpOut + ipOutOctets - INT_MIN + 1 :
+ ipOutOctets - StartingIpOut;
+}
+
+static void
+IpcpLayerDown(struct fsm * fp)
+{
+ LogPrintf(LogIPCP, "IpcpLayerDown.\n");
+ LogPrintf(LogIPCP, "%d octets in, %d octets out\n",
+ IpcpOctetsIn(), IpcpOctetsOut());
+ StopTimer(&IpcpReportTimer);
+ Prompt();
+}
+
+/*
+ * Called when IPCP has reached to OPEN state
+ */
+static void
+IpcpLayerUp(struct fsm * fp)
+{
+ char tbuff[100];
+
+ Prompt();
+ LogPrintf(LogIPCP, "IpcpLayerUp(%d).\n", fp->state);
+ snprintf(tbuff, sizeof(tbuff), "myaddr = %s ",
+ inet_ntoa(IpcpInfo.want_ipaddr));
+
+ if (IpcpInfo.his_compproto >> 16 == PROTO_VJCOMP)
+ VjInit((IpcpInfo.his_compproto >> 8) & 255);
+
+ LogPrintf(LogIsKept(LogIPCP) ? LogIPCP : LogLINK, " %s hisaddr = %s\n",
+ tbuff, inet_ntoa(IpcpInfo.his_ipaddr));
+ if (OsSetIpaddress(IpcpInfo.want_ipaddr, IpcpInfo.his_ipaddr, ifnetmask) < 0) {
+ if (VarTerm)
+ LogPrintf(LogERROR, "IpcpLayerUp: unable to set ip address\n");
+ return;
+ }
+ if (mode & MODE_ALIAS)
+ VarPacketAliasSetAddress(IpcpInfo.want_ipaddr);
+ OsLinkup();
+ StartingIpIn = ipInOctets;
+ StartingIpOut = ipOutOctets;
+ IpcpStartReport();
+ StartIdleTimer();
+}
+
+void
+IpcpUp()
+{
+ FsmUp(&IpcpFsm);
+ LogPrintf(LogIPCP, "IPCP Up event!!\n");
+}
+
+void
+IpcpOpen()
+{
+ FsmOpen(&IpcpFsm);
+}
+
+static int
+AcceptableAddr(struct in_range * prange, struct in_addr ipaddr)
+{
+ LogPrintf(LogDEBUG, "requested = %x\n", htonl(ipaddr.s_addr));
+ LogPrintf(LogDEBUG, "range = %x\n", htonl(prange->ipaddr.s_addr));
+ LogPrintf(LogDEBUG, "/%x\n", htonl(prange->mask.s_addr));
+ LogPrintf(LogDEBUG, "%x, %x\n", htonl(prange->ipaddr.s_addr & prange->
+ mask.s_addr), htonl(ipaddr.s_addr & prange->mask.s_addr));
+ return (prange->ipaddr.s_addr & prange->mask.s_addr) ==
+ (ipaddr.s_addr & prange->mask.s_addr) && ipaddr.s_addr;
+}
+
+static void
+IpcpDecodeConfig(u_char * cp, int plen, int mode_type)
+{
+ int type, length;
+ u_long *lp, compproto;
+ struct compreq *pcomp;
+ struct in_addr ipaddr, dstipaddr, dnsstuff, ms_info_req;
+ char tbuff[100];
+ char tbuff2[100];
+
+ ackp = AckBuff;
+ nakp = NakBuff;
+ rejp = RejBuff;
+
+ while (plen >= sizeof(struct fsmconfig)) {
+ type = *cp;
+ length = cp[1];
+ if (type <= TY_IPADDR)
+ snprintf(tbuff, sizeof(tbuff), " %s[%d] ", cftypes[type], length);
+ else
+ snprintf(tbuff, sizeof(tbuff), " ");
+
+ switch (type) {
+ case TY_IPADDR: /* RFC1332 */
+ lp = (u_long *) (cp + 2);
+ ipaddr.s_addr = *lp;
+ LogPrintf(LogIPCP, "%s %s\n", tbuff, inet_ntoa(ipaddr));
+
+ switch (mode_type) {
+ case MODE_REQ:
+ if (!AcceptableAddr(&DefHisAddress, ipaddr)) {
+ /*
+ * If destination address is not acceptable, insist to use what we
+ * want to use.
+ */
+ memcpy(nakp, cp, 2);
+ memcpy(nakp+2, &IpcpInfo.his_ipaddr.s_addr, length);
+ nakp += length;
+ break;
+ }
+ IpcpInfo.his_ipaddr = ipaddr;
+ memcpy(ackp, cp, length);
+ ackp += length;
+ break;
+ case MODE_NAK:
+ if (AcceptableAddr(&DefMyAddress, ipaddr)) {
+
+ /*
+ * Use address suggested by peer.
+ */
+ snprintf(tbuff2, sizeof(tbuff2), "%s changing address: %s ", tbuff,
+ inet_ntoa(IpcpInfo.want_ipaddr));
+ LogPrintf(LogIPCP, "%s --> %s\n", tbuff2, inet_ntoa(ipaddr));
+ IpcpInfo.want_ipaddr = ipaddr;
+ }
+ break;
+ case MODE_REJ:
+ IpcpInfo.his_reject |= (1 << type);
+ break;
+ }
+ break;
+ case TY_COMPPROTO:
+ lp = (u_long *) (cp + 2);
+ compproto = htonl(*lp);
+ LogPrintf(LogIPCP, "%s %08x\n", tbuff, compproto);
+
+ switch (mode_type) {
+ case MODE_REQ:
+ if (!Acceptable(ConfVjcomp)) {
+ memcpy(rejp, cp, length);
+ rejp += length;
+ } else {
+ pcomp = (struct compreq *) (cp + 2);
+ switch (length) {
+ case 4: /* RFC1172 */
+ if (ntohs(pcomp->proto) == PROTO_VJCOMP) {
+ LogPrintf(LogWARN, "Peer is speaking RFC1172 compression protocol !\n");
+ IpcpInfo.heis1172 = 1;
+ IpcpInfo.his_compproto = compproto;
+ memcpy(ackp, cp, length);
+ ackp += length;
+ } else {
+ memcpy(nakp, cp, 2);
+ pcomp->proto = htons(PROTO_VJCOMP);
+ memcpy(nakp+2, &pcomp, 2);
+ nakp += length;
+ }
+ break;
+ case 6: /* RFC1332 */
+ if (ntohs(pcomp->proto) == PROTO_VJCOMP
+ && pcomp->slots < MAX_STATES && pcomp->slots > 2) {
+ IpcpInfo.his_compproto = compproto;
+ IpcpInfo.heis1172 = 0;
+ memcpy(ackp, cp, length);
+ ackp += length;
+ } else {
+ memcpy(nakp, cp, 2);
+ pcomp->proto = htons(PROTO_VJCOMP);
+ pcomp->slots = MAX_STATES - 1;
+ pcomp->compcid = 0;
+ memcpy(nakp+2, &pcomp, sizeof(pcomp));
+ nakp += length;
+ }
+ break;
+ default:
+ memcpy(rejp, cp, length);
+ rejp += length;
+ break;
+ }
+ }
+ break;
+ case MODE_NAK:
+ LogPrintf(LogIPCP, "%s changing compproto: %08x --> %08x\n",
+ tbuff, IpcpInfo.want_compproto, compproto);
+ IpcpInfo.want_compproto = compproto;
+ break;
+ case MODE_REJ:
+ IpcpInfo.his_reject |= (1 << type);
+ break;
+ }
+ break;
+ case TY_IPADDRS: /* RFC1172 */
+ lp = (u_long *) (cp + 2);
+ ipaddr.s_addr = *lp;
+ lp = (u_long *) (cp + 6);
+ dstipaddr.s_addr = *lp;
+ snprintf(tbuff2, sizeof(tbuff2), "%s %s,", tbuff, inet_ntoa(ipaddr));
+ LogPrintf(LogIPCP, "%s %s\n", tbuff2, inet_ntoa(dstipaddr));
+
+ switch (mode_type) {
+ case MODE_REQ:
+ IpcpInfo.his_ipaddr = ipaddr;
+ IpcpInfo.want_ipaddr = dstipaddr;
+ memcpy(ackp, cp, length);
+ ackp += length;
+ break;
+ case MODE_NAK:
+ snprintf(tbuff2, sizeof(tbuff2), "%s changing address: %s", tbuff,
+ inet_ntoa(IpcpInfo.want_ipaddr));
+ LogPrintf(LogIPCP, "%s --> %s\n", tbuff2, inet_ntoa(ipaddr));
+ IpcpInfo.want_ipaddr = ipaddr;
+ IpcpInfo.his_ipaddr = dstipaddr;
+ break;
+ case MODE_REJ:
+ IpcpInfo.his_reject |= (1 << type);
+ break;
+ }
+ break;
+
+ /*
+ * MS extensions for MS's PPP
+ */
+
+#ifndef NOMSEXT
+ case TY_PRIMARY_DNS: /* MS PPP DNS negotiation hack */
+ case TY_SECONDARY_DNS:
+ if (!Enabled(ConfMSExt)) {
+ LogPrintf(LogIPCP, "MS NS req - rejected - msext disabled\n");
+ IpcpInfo.my_reject |= (1 << type);
+ memcpy(rejp, cp, length);
+ rejp += length;
+ break;
+ }
+ switch (mode_type) {
+ case MODE_REQ:
+ lp = (u_long *) (cp + 2);
+ dnsstuff.s_addr = *lp;
+ ms_info_req.s_addr = ns_entries[((type - TY_PRIMARY_DNS) ? 1 : 0)].s_addr;
+ if (dnsstuff.s_addr != ms_info_req.s_addr) {
+
+ /*
+ * So the client has got the DNS stuff wrong (first request) so
+ * we'll tell 'em how it is
+ */
+ memcpy(nakp, cp, 2); /* copy first two (type/length) */
+ LogPrintf(LogIPCP, "MS NS req %d:%s->%s - nak\n",
+ type,
+ inet_ntoa(dnsstuff),
+ inet_ntoa(ms_info_req));
+ memcpy(nakp+2, &ms_info_req, length);
+ nakp += length;
+ break;
+ }
+
+ /*
+ * Otherwise they have it right (this time) so we send a ack packet
+ * back confirming it... end of story
+ */
+ LogPrintf(LogIPCP, "MS NS req %d:%s ok - ack\n",
+ type,
+ inet_ntoa(ms_info_req));
+ memcpy(ackp, cp, length);
+ ackp += length;
+ break;
+ case MODE_NAK: /* what does this mean?? */
+ LogPrintf(LogIPCP, "MS NS req %d - NAK??\n", type);
+ break;
+ case MODE_REJ: /* confused?? me to :) */
+ LogPrintf(LogIPCP, "MS NS req %d - REJ??\n", type);
+ break;
+ }
+ break;
+
+ case TY_PRIMARY_NBNS: /* MS PPP NetBIOS nameserver hack */
+ case TY_SECONDARY_NBNS:
+ if (!Enabled(ConfMSExt)) {
+ LogPrintf(LogIPCP, "MS NBNS req - rejected - msext disabled\n");
+ IpcpInfo.my_reject |= (1 << type);
+ memcpy(rejp, cp, length);
+ rejp += length;
+ break;
+ }
+ switch (mode_type) {
+ case MODE_REQ:
+ lp = (u_long *) (cp + 2);
+ dnsstuff.s_addr = *lp;
+ ms_info_req.s_addr = nbns_entries[((type - TY_PRIMARY_NBNS) ? 1 : 0)].s_addr;
+ if (dnsstuff.s_addr != ms_info_req.s_addr) {
+ memcpy(nakp, cp, 2);
+ memcpy(nakp+2, &ms_info_req.s_addr, length);
+ LogPrintf(LogIPCP, "MS NBNS req %d:%s->%s - nak\n",
+ type,
+ inet_ntoa(dnsstuff),
+ inet_ntoa(ms_info_req));
+ nakp += length;
+ break;
+ }
+ LogPrintf(LogIPCP, "MS NBNS req %d:%s ok - ack\n",
+ type,
+ inet_ntoa(ms_info_req));
+ memcpy(ackp, cp, length);
+ ackp += length;
+ break;
+ case MODE_NAK:
+ LogPrintf(LogIPCP, "MS NBNS req %d - NAK??\n", type);
+ break;
+ case MODE_REJ:
+ LogPrintf(LogIPCP, "MS NBNS req %d - REJ??\n", type);
+ break;
+ }
+ break;
+
+#endif
+
+ default:
+ IpcpInfo.my_reject |= (1 << type);
+ memcpy(rejp, cp, length);
+ rejp += length;
+ break;
+ }
+ plen -= length;
+ cp += length;
+ }
+}
+
+void
+IpcpInput(struct mbuf * bp)
+{
+ FsmInput(&IpcpFsm, bp);
+}
diff --git a/usr.sbin/ppp/ipcp.h b/usr.sbin/ppp/ipcp.h
new file mode 100644
index 0000000..1b83bfc
--- /dev/null
+++ b/usr.sbin/ppp/ipcp.h
@@ -0,0 +1,78 @@
+/*
+ * Written by Toshiharu OHNO (tony-o@iij.ad.jp)
+ *
+ * Copyright (C) 1993, Internet Initiative Japan, Inc. All rights reserverd.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the Internet Initiative Japan. The name of the
+ * IIJ may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: ipcp.h,v 1.11 1997/10/26 01:02:55 brian Exp $
+ *
+ * TODO:
+ */
+
+#define IPCP_MAXCODE CODE_CODEREJ
+
+#define TY_IPADDRS 1
+#define TY_COMPPROTO 2
+#define TY_IPADDR 3
+
+/* MS PPP NameServer and NetBIOS NameServer stuff */
+
+#ifndef NOMSEXT
+#define TY_PRIMARY_DNS 129
+#define TY_PRIMARY_NBNS 130
+#define TY_SECONDARY_DNS 131
+#define TY_SECONDARY_NBNS 132
+
+extern struct in_addr ns_entries[2];
+extern struct in_addr nbns_entries[2];
+#endif
+
+struct ipcpstate {
+ struct in_addr his_ipaddr; /* IP address he is willing to use */
+ u_long his_compproto;
+
+ struct in_addr want_ipaddr; /* IP address I'm willing to use */
+ u_long want_compproto;
+
+ u_long his_reject; /* Request codes rejected by peer */
+ u_long my_reject; /* Request codes I have rejected */
+ int heis1172; /* True if he is speaking rfc1172 */
+};
+
+struct compreq {
+ u_short proto;
+ u_char slots;
+ u_char compcid;
+};
+
+struct in_range {
+ struct in_addr ipaddr;
+ struct in_addr mask;
+ int width;
+};
+
+extern struct ipcpstate IpcpInfo;
+extern struct in_range DefMyAddress;
+extern struct in_range DefHisAddress;
+extern struct in_addr TriggerAddress;
+extern int HaveTriggerAddress;
+extern struct fsm IpcpFsm;
+extern struct pppTimer IpcpReportTimer;
+
+extern void IpcpInit(void);
+extern void IpcpDefAddress(void);
+extern void IpcpUp(void);
+extern void IpcpOpen(void);
+extern int ReportIpcpStatus(void);
+extern void IpcpInput(struct mbuf *);
diff --git a/usr.sbin/ppp/lcp.c b/usr.sbin/ppp/lcp.c
new file mode 100644
index 0000000..ee69d29
--- /dev/null
+++ b/usr.sbin/ppp/lcp.c
@@ -0,0 +1,683 @@
+/*
+ * PPP Link Control Protocol (LCP) Module
+ *
+ * Written by Toshiharu OHNO (tony-o@iij.ad.jp)
+ *
+ * Copyright (C) 1993, Internet Initiative Japan, Inc. All rights reserverd.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the Internet Initiative Japan, Inc. The name of the
+ * IIJ may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: lcp.c,v 1.42 1997/10/29 01:19:41 brian Exp $
+ *
+ * TODO:
+ * o Validate magic number received from peer.
+ * o Limit data field length by MRU
+ */
+#include <sys/param.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "mbuf.h"
+#include "log.h"
+#include "defs.h"
+#include "timer.h"
+#include "fsm.h"
+#include "lcp.h"
+#include "ipcp.h"
+#include "lcpproto.h"
+#include "os.h"
+#include "hdlc.h"
+#include "ccp.h"
+#include "lqr.h"
+#include "phase.h"
+#include "loadalias.h"
+#include "command.h"
+#include "vars.h"
+#include "auth.h"
+#include "pap.h"
+#include "chap.h"
+#include "async.h"
+#include "main.h"
+#include "ip.h"
+#include "modem.h"
+
+struct lcpstate LcpInfo;
+
+static void LcpSendConfigReq(struct fsm *);
+static void LcpSendTerminateReq(struct fsm *);
+static void LcpSendTerminateAck(struct fsm *);
+static void LcpDecodeConfig(u_char *, int, int);
+static void LcpInitRestartCounter(struct fsm *);
+static void LcpLayerUp(struct fsm *);
+static void LcpLayerDown(struct fsm *);
+static void LcpLayerStart(struct fsm *);
+static void LcpLayerFinish(struct fsm *);
+
+#define REJECTED(p, x) (p->his_reject & (1<<x))
+
+static char *cftypes[] = {
+ "???", "MRU", "ACCMAP", "AUTHPROTO", "QUALPROTO", "MAGICNUM",
+ "RESERVED", "PROTOCOMP", "ACFCOMP", "FCSALT", "SDP",
+};
+
+struct fsm LcpFsm = {
+ "LCP", /* Name of protocol */
+ PROTO_LCP, /* Protocol Number */
+ LCP_MAXCODE,
+ OPEN_ACTIVE,
+ ST_INITIAL, /* State of machine */
+ 0, 0, 0,
+ 0,
+ {0, 0, 0, NULL, NULL, NULL},
+ {0, 0, 0, NULL, NULL, NULL},
+ LogLCP,
+
+ LcpLayerUp,
+ LcpLayerDown,
+ LcpLayerStart,
+ LcpLayerFinish,
+ LcpInitRestartCounter,
+ LcpSendConfigReq,
+ LcpSendTerminateReq,
+ LcpSendTerminateAck,
+ LcpDecodeConfig,
+};
+
+static struct pppTimer LcpReportTimer;
+static int LcpFailedMagic;
+
+static void
+LcpReportTime()
+{
+ if (LogIsKept(LogDEBUG)) {
+ time_t t;
+
+ time(&t);
+ LogPrintf(LogDEBUG, "LcpReportTime: %s\n", ctime(&t));
+ }
+ StopTimer(&LcpReportTimer);
+ LcpReportTimer.state = TIMER_STOPPED;
+ StartTimer(&LcpReportTimer);
+ HdlcErrorCheck();
+}
+
+int
+ReportLcpStatus()
+{
+ struct lcpstate *lcp = &LcpInfo;
+ struct fsm *fp = &LcpFsm;
+
+ if (!VarTerm)
+ return 1;
+
+ fprintf(VarTerm, "%s [%s]\n", fp->name, StateNames[fp->state]);
+ fprintf(VarTerm,
+ " his side: MRU %ld, ACCMAP %08lx, PROTOCOMP %d, ACFCOMP %d, MAGIC %08lx,\n"
+ " REJECT %04lx\n",
+ lcp->his_mru, lcp->his_accmap, lcp->his_protocomp, lcp->his_acfcomp,
+ lcp->his_magic, lcp->his_reject);
+ fprintf(VarTerm,
+ " my side: MRU %ld, ACCMAP %08lx, PROTOCOMP %d, ACFCOMP %d, MAGIC %08lx,\n"
+ " REJECT %04lx\n",
+ lcp->want_mru, lcp->want_accmap, lcp->want_protocomp, lcp->want_acfcomp,
+ lcp->want_magic, lcp->my_reject);
+ fprintf(VarTerm, "\nDefaults: MRU = %ld, ACCMAP = %08x\t", VarMRU, VarAccmap);
+ fprintf(VarTerm, "Open Mode: %s\n", (VarOpenMode == OPEN_ACTIVE) ? "active" : "passive");
+ return 0;
+}
+
+/*
+ * Generate random number which will be used as magic number.
+ */
+static u_long
+GenerateMagic()
+{
+ randinit();
+ return (random());
+}
+
+void
+LcpInit()
+{
+ struct lcpstate *lcp = &LcpInfo;
+
+ FsmInit(&LcpFsm);
+ HdlcInit();
+
+ memset(lcp, '\0', sizeof(struct lcpstate));
+ lcp->want_mru = VarMRU;
+ lcp->his_mru = DEF_MRU;
+ lcp->his_accmap = 0xffffffff;
+ lcp->want_accmap = VarAccmap;
+ lcp->want_magic = GenerateMagic();
+ lcp->want_auth = lcp->his_auth = 0;
+ if (Enabled(ConfChap))
+ lcp->want_auth = PROTO_CHAP;
+ else if (Enabled(ConfPap))
+ lcp->want_auth = PROTO_PAP;
+ if (Enabled(ConfLqr))
+ lcp->want_lqrperiod = VarLqrTimeout * 100;
+ if (Enabled(ConfAcfcomp))
+ lcp->want_acfcomp = 1;
+ if (Enabled(ConfProtocomp))
+ lcp->want_protocomp = 1;
+ LcpFsm.maxconfig = 10;
+}
+
+static void
+LcpInitRestartCounter(struct fsm * fp)
+{
+ fp->FsmTimer.load = VarRetryTimeout * SECTICKS;
+ fp->restart = 5;
+}
+
+void
+PutConfValue(u_char ** cpp, char **types, u_char type, int len, u_long val)
+{
+ u_char *cp;
+ struct in_addr ina;
+
+ cp = *cpp;
+ *cp++ = type;
+ *cp++ = len;
+ if (len == 6) {
+ if (type == TY_IPADDR) {
+ ina.s_addr = htonl(val);
+ LogPrintf(LogLCP, " %s [%d] %s\n",
+ types[type], len, inet_ntoa(ina));
+ } else {
+ LogPrintf(LogLCP, " %s [%d] %08x\n", types[type], len, val);
+ }
+ *cp++ = (val >> 24) & 0377;
+ *cp++ = (val >> 16) & 0377;
+ } else
+ LogPrintf(LogLCP, " %s [%d] %d\n", types[type], len, val);
+ *cp++ = (val >> 8) & 0377;
+ *cp++ = val & 0377;
+ *cpp = cp;
+}
+
+static void
+LcpSendConfigReq(struct fsm * fp)
+{
+ u_char *cp;
+ struct lcpstate *lcp = &LcpInfo;
+ struct lqrreq *req;
+
+ LogPrintf(LogLCP, "LcpSendConfigReq\n");
+ cp = ReqBuff;
+ if (!DEV_IS_SYNC) {
+ if (lcp->want_acfcomp && !REJECTED(lcp, TY_ACFCOMP)) {
+ *cp++ = TY_ACFCOMP;
+ *cp++ = 2;
+ LogPrintf(LogLCP, " %s\n", cftypes[TY_ACFCOMP]);
+ }
+ if (lcp->want_protocomp && !REJECTED(lcp, TY_PROTOCOMP)) {
+ *cp++ = TY_PROTOCOMP;
+ *cp++ = 2;
+ LogPrintf(LogLCP, " %s\n", cftypes[TY_PROTOCOMP]);
+ }
+ if (!REJECTED(lcp, TY_ACCMAP))
+ PutConfValue(&cp, cftypes, TY_ACCMAP, 6, lcp->want_accmap);
+ }
+ if (!REJECTED(lcp, TY_MRU))
+ PutConfValue(&cp, cftypes, TY_MRU, 4, lcp->want_mru);
+ if (lcp->want_magic && !REJECTED(lcp, TY_MAGICNUM))
+ PutConfValue(&cp, cftypes, TY_MAGICNUM, 6, lcp->want_magic);
+ if (lcp->want_lqrperiod && !REJECTED(lcp, TY_QUALPROTO)) {
+ req = (struct lqrreq *) cp;
+ req->type = TY_QUALPROTO;
+ req->length = sizeof(struct lqrreq);
+ req->proto = htons(PROTO_LQR);
+ req->period = htonl(lcp->want_lqrperiod);
+ cp += sizeof(struct lqrreq);
+ LogPrintf(LogLCP, " %s (%d)\n", cftypes[TY_QUALPROTO], lcp->want_lqrperiod);
+ }
+ switch (lcp->want_auth) {
+ case PROTO_PAP:
+ PutConfValue(&cp, cftypes, TY_AUTHPROTO, 4, lcp->want_auth);
+ break;
+ case PROTO_CHAP:
+ PutConfValue(&cp, cftypes, TY_AUTHPROTO, 5, lcp->want_auth);
+#ifdef HAVE_DES
+ *cp++ = VarMSChap ? 0x80 : 0x05; /* Use MSChap vs. RFC 1994 (MD5) */
+#else
+ *cp++ = 0x05; /* Use MD5 */
+#endif
+ break;
+ }
+ FsmOutput(fp, CODE_CONFIGREQ, fp->reqid++, ReqBuff, cp - ReqBuff);
+}
+
+void
+LcpSendProtoRej(u_char * option, int count)
+{
+ struct fsm *fp = &LcpFsm;
+
+ LogPrintf(LogLCP, "LcpSendProtoRej\n");
+ FsmOutput(fp, CODE_PROTOREJ, fp->reqid, option, count);
+}
+
+static void
+LcpSendTerminateReq(struct fsm * fp)
+{
+ /* Most thins are done in fsm layer. Nothing to to. */
+}
+
+static void
+LcpSendTerminateAck(struct fsm * fp)
+{
+ LogPrintf(LogLCP, "LcpSendTerminateAck.\n");
+ FsmOutput(fp, CODE_TERMACK, fp->reqid++, NULL, 0);
+}
+
+static void
+LcpLayerStart(struct fsm * fp)
+{
+ LogPrintf(LogLCP, "LcpLayerStart\n");
+ NewPhase(PHASE_ESTABLISH);
+}
+
+static void
+StopAllTimers()
+{
+ StopTimer(&LcpReportTimer);
+ StopTimer(&IpcpReportTimer);
+ StopIdleTimer();
+ StopTimer(&AuthPapInfo.authtimer);
+ StopTimer(&AuthChapInfo.authtimer);
+ StopLqrTimer();
+}
+
+static void
+LcpLayerFinish(struct fsm * fp)
+{
+ LogPrintf(LogLCP, "LcpLayerFinish\n");
+ HangupModem(0);
+ StopAllTimers();
+ /* We're down at last. Lets tell background and direct mode to get out */
+ NewPhase(PHASE_DEAD);
+ LcpInit();
+ IpcpInit();
+ CcpInit();
+ Prompt();
+}
+
+static void
+LcpLayerUp(struct fsm * fp)
+{
+ LogPrintf(LogLCP, "LcpLayerUp\n");
+ OsSetInterfaceParams(23, LcpInfo.his_mru, ModemSpeed());
+ SetLinkParams(&LcpInfo);
+
+ NewPhase(PHASE_AUTHENTICATE);
+
+ StartLqm();
+ StopTimer(&LcpReportTimer);
+ LcpReportTimer.state = TIMER_STOPPED;
+ LcpReportTimer.load = 60 * SECTICKS;
+ LcpReportTimer.func = LcpReportTime;
+ StartTimer(&LcpReportTimer);
+}
+
+static void
+LcpLayerDown(struct fsm * fp)
+{
+ StopAllTimers();
+ OsLinkdown();
+ LogPrintf(LogLCP, "LcpLayerDown\n");
+ /*
+ * OsLinkdown() brings CCP & IPCP down, then waits 'till we go from
+ * STOPPING to STOPPED. At this point, the FSM gives us a LayerFinish
+ */
+}
+
+void
+LcpUp()
+{
+ FsmUp(&LcpFsm);
+ LcpFailedMagic = 0;
+}
+
+void
+LcpDown()
+{ /* Sudden death */
+ LcpFailedMagic = 0;
+ FsmDown(&LcpFsm);
+ /* FsmDown() results in a LcpLayerDown() if we're currently open. */
+ LcpLayerFinish(&LcpFsm);
+}
+
+void
+LcpOpen(int open_mode)
+{
+ LcpFsm.open_mode = open_mode;
+ LcpFailedMagic = 0;
+ FsmOpen(&LcpFsm);
+}
+
+void
+LcpClose()
+{
+ NewPhase(PHASE_TERMINATE);
+ OsInterfaceDown(0);
+ FsmClose(&LcpFsm);
+ LcpFailedMagic = 0;
+}
+
+/*
+ * XXX: Should validate option length
+ */
+static void
+LcpDecodeConfig(u_char * cp, int plen, int mode_type)
+{
+ char *request;
+ int type, length, mru, mtu;
+ u_long *lp, magic, accmap;
+ u_short *sp, proto;
+ struct lqrreq *req;
+
+ ackp = AckBuff;
+ nakp = NakBuff;
+ rejp = RejBuff;
+
+ while (plen >= sizeof(struct fsmconfig)) {
+ type = *cp;
+ length = cp[1];
+ if (type <= TY_ACFCOMP)
+ request = cftypes[type];
+ else
+ request = "???";
+
+ switch (type) {
+ case TY_MRU:
+ sp = (u_short *) (cp + 2);
+ mru = htons(*sp);
+ LogPrintf(LogLCP, " %s %d\n", request, mru);
+
+ switch (mode_type) {
+ case MODE_REQ:
+ mtu = VarPrefMTU;
+ if (mtu == 0)
+ mtu = MAX_MTU;
+ if (mru > mtu) {
+ *sp = htons(mtu);
+ memcpy(nakp, cp, 4);
+ nakp += 4;
+ } else if (mru < MIN_MRU) {
+ *sp = htons(MIN_MRU);
+ memcpy(nakp, cp, 4);
+ nakp += 4;
+ } else {
+ LcpInfo.his_mru = mru;
+ memcpy(ackp, cp, 4);
+ ackp += 4;
+ }
+ break;
+ case MODE_NAK:
+ if (mru >= MIN_MRU || mru <= MAX_MRU)
+ LcpInfo.want_mru = mru;
+ break;
+ case MODE_REJ:
+ LcpInfo.his_reject |= (1 << type);
+ break;
+ }
+ break;
+ case TY_ACCMAP:
+ lp = (u_long *) (cp + 2);
+ accmap = htonl(*lp);
+ LogPrintf(LogLCP, " %s %08x\n", request, accmap);
+
+ switch (mode_type) {
+ case MODE_REQ:
+ LcpInfo.his_accmap = accmap;
+ memcpy(ackp, cp, 6);
+ ackp += 6;
+ break;
+ case MODE_NAK:
+ LcpInfo.want_accmap = accmap;
+ break;
+ case MODE_REJ:
+ LcpInfo.his_reject |= (1 << type);
+ break;
+ }
+ break;
+ case TY_AUTHPROTO:
+ sp = (u_short *) (cp + 2);
+ proto = ntohs(*sp);
+ LogPrintf(LogLCP, " %s proto = %04x\n", request, proto);
+
+ switch (mode_type) {
+ case MODE_REQ:
+ switch (proto) {
+ case PROTO_PAP:
+ if (length != 4) {
+ LogPrintf(LogLCP, " %s bad length (%d)\n", request, length);
+ goto reqreject;
+ }
+ if (Acceptable(ConfPap)) {
+ LcpInfo.his_auth = proto;
+ memcpy(ackp, cp, length);
+ ackp += length;
+ } else if (Acceptable(ConfChap)) {
+ *nakp++ = *cp;
+ *nakp++ = 5;
+ *nakp++ = (unsigned char) (PROTO_CHAP >> 8);
+ *nakp++ = (unsigned char) PROTO_CHAP;
+ *nakp++ = 5;
+ } else
+ goto reqreject;
+ break;
+ case PROTO_CHAP:
+ if (length < 5) {
+ LogPrintf(LogLCP, " %s bad length (%d)\n", request, length);
+ goto reqreject;
+ }
+#ifdef HAVE_DES
+ if (Acceptable(ConfChap) && (cp[4] == 5 || cp[4] == 0x80))
+#else
+ if (Acceptable(ConfChap) && cp[4] == 5)
+#endif
+ {
+ LcpInfo.his_auth = proto;
+ memcpy(ackp, cp, length);
+ ackp += length;
+#ifdef HAVE_DES
+ VarMSChap = cp[4] = 0x80;
+#endif
+ } else if (Acceptable(ConfPap)) {
+ *nakp++ = *cp;
+ *nakp++ = 4;
+ *nakp++ = (unsigned char) (PROTO_PAP >> 8);
+ *nakp++ = (unsigned char) PROTO_PAP;
+ } else
+ goto reqreject;
+ break;
+ default:
+ LogPrintf(LogLCP, " %s not implemented, NAK.\n", request);
+ memcpy(nakp, cp, length);
+ nakp += length;
+ break;
+ }
+ break;
+ case MODE_NAK:
+ break;
+ case MODE_REJ:
+ LcpInfo.his_reject |= (1 << type);
+ break;
+ }
+ break;
+ case TY_QUALPROTO:
+ req = (struct lqrreq *) cp;
+ LogPrintf(LogLCP, " %s proto: %x, interval: %dms\n",
+ request, ntohs(req->proto), ntohl(req->period) * 10);
+ switch (mode_type) {
+ case MODE_REQ:
+ if (ntohs(req->proto) != PROTO_LQR || !Acceptable(ConfLqr))
+ goto reqreject;
+ else {
+ LcpInfo.his_lqrperiod = ntohl(req->period);
+ if (LcpInfo.his_lqrperiod < 500)
+ LcpInfo.his_lqrperiod = 500;
+ req->period = htonl(LcpInfo.his_lqrperiod);
+ memcpy(ackp, cp, length);
+ ackp += length;
+ }
+ break;
+ case MODE_NAK:
+ break;
+ case MODE_REJ:
+ LcpInfo.his_reject |= (1 << type);
+ break;
+ }
+ break;
+ case TY_MAGICNUM:
+ lp = (u_long *) (cp + 2);
+ magic = ntohl(*lp);
+ LogPrintf(LogLCP, " %s %08x\n", request, magic);
+
+ switch (mode_type) {
+ case MODE_REQ:
+ if (LcpInfo.want_magic) {
+ /* Validate magic number */
+ if (magic == LcpInfo.want_magic) {
+ LogPrintf(LogLCP, "Magic is same (%08x) - %d times\n",
+ magic, ++LcpFailedMagic);
+ LcpInfo.want_magic = GenerateMagic();
+ memcpy(nakp, cp, 6);
+ nakp += 6;
+ ualarm(TICKUNIT * (4 + 4 * LcpFailedMagic), 0);
+ sigpause(0);
+ } else {
+ LcpInfo.his_magic = magic;
+ memcpy(ackp, cp, length);
+ ackp += length;
+ LcpFailedMagic = 0;
+ }
+ } else {
+ LcpInfo.my_reject |= (1 << type);
+ goto reqreject;
+ }
+ break;
+ case MODE_NAK:
+ LogPrintf(LogLCP, " %s magic %08x has NAKed\n", request, magic);
+ LcpInfo.want_magic = GenerateMagic();
+ break;
+ case MODE_REJ:
+ LogPrintf(LogLCP, " %s magic has REJected\n", request);
+ LcpInfo.want_magic = 0;
+ LcpInfo.his_reject |= (1 << type);
+ break;
+ }
+ break;
+ case TY_PROTOCOMP:
+ LogPrintf(LogLCP, " %s\n", request);
+
+ switch (mode_type) {
+ case MODE_REQ:
+ if (Acceptable(ConfProtocomp)) {
+ LcpInfo.his_protocomp = 1;
+ memcpy(ackp, cp, 2);
+ ackp += 2;
+ } else {
+#ifdef OLDMST
+ /*
+ * MorningStar before v1.3 needs NAK
+ */
+ memcpy(nakp, cp, 2);
+ nakp += 2;
+#else
+ memcpy(rejp, cp, 2);
+ rejp += 2;
+ LcpInfo.my_reject |= (1 << type);
+#endif
+ }
+ break;
+ case MODE_NAK:
+ case MODE_REJ:
+ LcpInfo.want_protocomp = 0;
+ LcpInfo.his_reject |= (1 << type);
+ break;
+ }
+ break;
+ case TY_ACFCOMP:
+ LogPrintf(LogLCP, " %s\n", request);
+ switch (mode_type) {
+ case MODE_REQ:
+ if (Acceptable(ConfAcfcomp)) {
+ LcpInfo.his_acfcomp = 1;
+ memcpy(ackp, cp, 2);
+ ackp += 2;
+ } else {
+#ifdef OLDMST
+ /*
+ * MorningStar before v1.3 needs NAK
+ */
+ memcpy(nakp, cp, 2);
+ nakp += 2;
+#else
+ memcpy(rejp, cp, 2);
+ rejp += 2;
+ LcpInfo.my_reject |= (1 << type);
+#endif
+ }
+ break;
+ case MODE_NAK:
+ case MODE_REJ:
+ LcpInfo.want_acfcomp = 0;
+ LcpInfo.his_reject |= (1 << type);
+ break;
+ }
+ break;
+ case TY_SDP:
+ LogPrintf(LogLCP, " %s\n", request);
+ switch (mode_type) {
+ case MODE_REQ:
+ case MODE_NAK:
+ case MODE_REJ:
+ break;
+ }
+ break;
+ default:
+ LogPrintf(LogLCP, " ???[%02x]\n", type);
+ if (mode_type == MODE_REQ) {
+ reqreject:
+ memcpy(rejp, cp, length);
+ rejp += length;
+ LcpInfo.my_reject |= (1 << type);
+ }
+ break;
+ }
+ /* to avoid inf. loop */
+ if (length == 0) {
+ LogPrintf(LogLCP, "LCP size zero\n");
+ break;
+ }
+ plen -= length;
+ cp += length;
+ }
+}
+
+void
+LcpInput(struct mbuf * bp)
+{
+ FsmInput(&LcpFsm, bp);
+}
diff --git a/usr.sbin/ppp/lcp.h b/usr.sbin/ppp/lcp.h
new file mode 100644
index 0000000..5b2803f
--- /dev/null
+++ b/usr.sbin/ppp/lcp.h
@@ -0,0 +1,83 @@
+/*
+ * Written by Toshiharu OHNO (tony-o@iij.ad.jp)
+ *
+ * Copyright (C) 1993, Internet Initiative Japan, Inc. All rights reserverd.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the Internet Initiative Japan. The name of the
+ * IIJ may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: lcp.h,v 1.10 1997/10/26 12:42:12 brian Exp $
+ *
+ * TODO:
+ */
+
+struct lcpstate {
+ u_long his_mru;
+ u_long his_accmap;
+ u_long his_magic;
+ u_long his_lqrperiod;
+ u_char his_protocomp;
+ u_char his_acfcomp;
+ u_short his_auth;
+
+ u_long want_mru;
+ u_long want_accmap;
+ u_long want_magic;
+ u_long want_lqrperiod;
+ u_char want_protocomp;
+ u_char want_acfcomp;
+ u_short want_auth;
+
+ u_long his_reject; /* Request codes rejected by peer */
+ u_long my_reject; /* Request codes I have rejected */
+
+ u_short auth_iwait;
+ u_short auth_ineed;
+};
+
+#define LCP_MAXCODE CODE_DISCREQ
+
+#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-Dscribing-Padding */
+#define TY_NUMMODE 11 /* Numbered-Mode */
+#define TY_XXXXXX 12
+#define TY_CALLBACK 13 /* Callback */
+#define TY_YYYYYY 14
+#define TY_COMPFRAME 15 /* Compound-Frames */
+
+struct lqrreq {
+ u_char type;
+ u_char length;
+ u_short proto; /* Quality protocol */
+ u_long period; /* Reporting interval */
+};
+
+extern struct lcpstate LcpInfo;
+extern struct fsm LcpFsm;
+
+extern void LcpInit(void);
+extern void LcpUp(void);
+extern void LcpSendProtoRej(u_char *, int);
+extern void LcpOpen(int);
+extern void LcpClose(void);
+extern void LcpDown(void);
+extern void PutConfValue(u_char **, char **, u_char, int, u_long);
+extern int ReportLcpStatus(void);
+extern void LcpInput(struct mbuf *);
diff --git a/usr.sbin/ppp/lcpproto.h b/usr.sbin/ppp/lcpproto.h
new file mode 100644
index 0000000..74c5d4d
--- /dev/null
+++ b/usr.sbin/ppp/lcpproto.h
@@ -0,0 +1,39 @@
+/*
+ * Written by Toshiharu OHNO (tony-o@iij.ad.jp)
+ *
+ * Copyright (C) 1993, Internet Initiative Japan, Inc. All rights reserverd.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the Internet Initiative Japan. The name of the
+ * IIJ may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: lcpproto.h,v 1.9 1997/10/26 01:02:59 brian Exp $
+ *
+ * TODO:
+ */
+
+/*
+ * Definition of protocol numbers
+ */
+#define PROTO_IP 0x0021 /* IP */
+#define PROTO_VJUNCOMP 0x002f /* VJ Uncompressed */
+#define PROTO_VJCOMP 0x002d /* VJ Compressed */
+#define PROTO_ICOMPD 0x00fb /* Individual link compressed */
+#define PROTO_COMPD 0x00fd /* Compressed datagram */
+
+#define PROTO_IPCP 0x8021
+#define PROTO_ICCP 0x80fb
+#define PROTO_CCP 0x80fd
+
+#define PROTO_LCP 0xc021
+#define PROTO_PAP 0xc023
+#define PROTO_LQR 0xc025
+#define PROTO_CHAP 0xc223
diff --git a/usr.sbin/ppp/loadalias.c b/usr.sbin/ppp/loadalias.c
new file mode 100644
index 0000000..6435f72
--- /dev/null
+++ b/usr.sbin/ppp/loadalias.c
@@ -0,0 +1,92 @@
+/*
+ * $Id: loadalias.c,v 1.9 1997/11/09 06:22:41 brian Exp $
+ */
+
+#include <sys/param.h>
+#include <netinet/in.h>
+
+#include <alias.h>
+#include <dlfcn.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <unistd.h>
+
+#include "command.h"
+#include "systems.h"
+#include "mbuf.h"
+#include "log.h"
+#include "id.h"
+#include "loadalias.h"
+#include "defs.h"
+#include "vars.h"
+
+#define _PATH_ALIAS "/usr/lib/libalias.so." ## __libalias_version
+
+#define off(item) ((int)&(((struct aliasHandlers *)0)->item))
+#define entry(a) { off(a), "_" #a }
+
+static struct {
+ int offset;
+ char *name;
+} map[] = {
+ entry(PacketAliasGetFragment),
+ entry(PacketAliasInit),
+ entry(PacketAliasIn),
+ entry(PacketAliasOut),
+ entry(PacketAliasRedirectAddr),
+ entry(PacketAliasRedirectPort),
+ entry(PacketAliasSaveFragment),
+ entry(PacketAliasSetAddress),
+ entry(PacketAliasSetMode),
+ entry(PacketAliasFragmentIn),
+ { 0, 0 }
+};
+
+static void *dl;
+
+int
+loadAliasHandlers(struct aliasHandlers * h)
+{
+ char *path;
+ char *env;
+ int i;
+
+ path = _PATH_ALIAS;
+ env = getenv("_PATH_ALIAS");
+ if (env)
+ if (ID0realuid() == 0)
+ path = env;
+ else
+ LogPrintf(LogALERT, "Ignoring environment _PATH_ALIAS value (%s)\n", env);
+
+ dl = dlopen(path, RTLD_LAZY);
+ if (dl == (void *) 0) {
+ LogPrintf(LogWARN, "_PATH_ALIAS (%s): Invalid lib: %s\n",
+ path, dlerror());
+ return -1;
+ }
+ for (i = 0; map[i].name; i++) {
+ *(void **) ((char *) h + map[i].offset) = dlsym(dl, map[i].name);
+ if (*(void **) ((char *) h + map[i].offset) == (void *) 0) {
+ LogPrintf(LogWARN, "_PATH_ALIAS (%s): %s: %s\n", path,
+ map[i].name, dlerror());
+ (void) dlclose(dl);
+ dl = (void *) 0;
+ return -1;
+ }
+ }
+
+ VarPacketAliasInit();
+
+ return 0;
+}
+
+void
+unloadAliasHandlers()
+{
+ if (dl) {
+ dlclose(dl);
+ dl = (void *) 0;
+ }
+}
diff --git a/usr.sbin/ppp/loadalias.h b/usr.sbin/ppp/loadalias.h
new file mode 100644
index 0000000..7dc5595
--- /dev/null
+++ b/usr.sbin/ppp/loadalias.h
@@ -0,0 +1,21 @@
+/*
+ * $Id: $
+ */
+
+struct aliasHandlers {
+ char *(*PacketAliasGetFragment)(char *);
+ void (*PacketAliasInit)(void);
+ int (*PacketAliasIn)(char *, int);
+ int (*PacketAliasOut)(char *, int);
+ struct alias_link *(*PacketAliasRedirectAddr)(struct in_addr, struct in_addr);
+ struct alias_link *(*PacketAliasRedirectPort)
+ (struct in_addr, u_short, struct in_addr, u_short,
+ struct in_addr, u_short, u_char);
+ int (*PacketAliasSaveFragment)(char *);
+ void (*PacketAliasSetAddress)(struct in_addr);
+ unsigned (*PacketAliasSetMode)(unsigned, unsigned);
+ void (*PacketAliasFragmentIn)(char *, char *);
+};
+
+extern int loadAliasHandlers(struct aliasHandlers *);
+extern void unloadAliasHandlers(void);
diff --git a/usr.sbin/ppp/log.c b/usr.sbin/ppp/log.c
new file mode 100644
index 0000000..b88483b
--- /dev/null
+++ b/usr.sbin/ppp/log.c
@@ -0,0 +1,221 @@
+/*
+ * $Id: log.c,v 1.19 1997/11/09 06:22:42 brian Exp $
+ */
+
+#include <sys/param.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+
+#include <stdarg.h>
+#include <stdio.h>
+#include <syslog.h>
+
+#include "mbuf.h"
+#include "log.h"
+#include "loadalias.h"
+#include "command.h"
+#include "defs.h"
+#include "vars.h"
+
+static char *LogNames[] = {
+ "Async",
+ "Carrier",
+ "CCP",
+ "Chat",
+ "Command",
+ "Connect",
+ "Debug",
+ "HDLC",
+ "ID0",
+ "IPCP",
+ "LCP",
+ "Link",
+ "LQM",
+ "Phase",
+ "TCP/IP",
+ "Tun",
+ "Warning",
+ "Error",
+ "Alert"
+};
+
+#define MSK(n) (1<<((n)-1))
+
+static u_long LogMask = MSK(LogLINK) | MSK(LogCARRIER) | MSK(LogPHASE);
+static u_long LogMaskLocal = MSK(LogERROR) | MSK(LogALERT) | MSK(LogWARN);
+static int LogTunno = -1;
+
+static int
+syslogLevel(int lev)
+{
+ switch (lev) {
+ case LogDEBUG: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 *
+LogName(int id)
+{
+ return id < LogMIN || id > LogMAX ? "Unknown" : LogNames[id - 1];
+}
+
+void
+LogKeep(int id)
+{
+ if (id >= LogMIN && id <= LogMAXCONF)
+ LogMask |= MSK(id);
+}
+
+void
+LogKeepLocal(int id)
+{
+ if (id >= LogMIN && id <= LogMAXCONF)
+ LogMaskLocal |= MSK(id);
+}
+
+void
+LogDiscard(int id)
+{
+ if (id >= LogMIN && id <= LogMAXCONF)
+ LogMask &= ~MSK(id);
+}
+
+void
+LogDiscardLocal(int id)
+{
+ if (id >= LogMIN && id <= LogMAXCONF)
+ LogMaskLocal &= ~MSK(id);
+}
+
+void
+LogDiscardAll()
+{
+ LogMask = 0;
+}
+
+void
+LogDiscardAllLocal()
+{
+ LogMaskLocal = 0;
+}
+
+int
+LogIsKept(int id)
+{
+ if (id < LogMIN || id > LogMAX)
+ return 0;
+ if (id > LogMAXCONF)
+ return LOG_KEPT_LOCAL | LOG_KEPT_SYSLOG;
+
+ return ((LogMaskLocal & MSK(id)) ? LOG_KEPT_LOCAL : 0) |
+ ((LogMask & MSK(id)) ? LOG_KEPT_SYSLOG : 0);
+}
+
+void
+LogOpen(const char *Name)
+{
+ openlog(Name, LOG_PID, LOG_DAEMON);
+}
+
+void
+LogSetTun(int tunno)
+{
+ LogTunno = tunno;
+}
+
+void
+LogClose()
+{
+ closelog();
+ LogTunno = -1;
+}
+
+void
+LogPrintf(int lev, char *fmt,...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ if (LogIsKept(lev)) {
+ static char nfmt[200];
+
+ if ((LogIsKept(lev) & LOG_KEPT_LOCAL) && VarTerm) {
+ if ((LogIsKept(LogTUN) & LOG_KEPT_LOCAL) && LogTunno != -1)
+ snprintf(nfmt, sizeof nfmt, "tun%d: %s: %s",
+ LogTunno, LogName(lev), fmt);
+ else
+ snprintf(nfmt, sizeof nfmt, "%s: %s", LogName(lev), fmt);
+ vfprintf(VarTerm, nfmt, ap);
+ }
+
+ if ((LogIsKept(lev) & LOG_KEPT_SYSLOG) && (lev != LogWARN || !VarTerm)) {
+ if ((LogIsKept(LogTUN) & LOG_KEPT_SYSLOG) && LogTunno != -1)
+ snprintf(nfmt, sizeof nfmt, "tun%d: %s: %s",
+ LogTunno, LogName(lev), fmt);
+ else
+ snprintf(nfmt, sizeof nfmt, "%s: %s", LogName(lev), fmt);
+ vsyslog(syslogLevel(lev), nfmt, ap);
+ }
+ }
+ va_end(ap);
+}
+
+void
+LogDumpBp(int lev, char *hdr, struct mbuf * bp)
+{
+ if (LogIsKept(lev)) {
+ char buf[50];
+ char *b;
+ u_char *ptr;
+ int f;
+
+ if (hdr && *hdr)
+ LogPrintf(lev, "%s\n", hdr);
+
+ b = buf;
+ do {
+ f = bp->cnt;
+ ptr = MBUF_CTOP(bp);
+ while (f--) {
+ sprintf(b, " %02x", (int) *ptr++);
+ b += 3;
+ if (b == buf + sizeof buf - 2) {
+ strcpy(b, "\n");
+ LogPrintf(lev, buf);
+ b = buf;
+ }
+ }
+ } while ((bp = bp->next) != NULL);
+
+ if (b > buf) {
+ strcpy(b, "\n");
+ LogPrintf(lev, buf);
+ }
+ }
+}
+
+void
+LogDumpBuff(int lev, char *hdr, u_char * ptr, int n)
+{
+ if (LogIsKept(lev)) {
+ char buf[50];
+ char *b;
+
+ if (hdr && *hdr)
+ LogPrintf(lev, "%s\n", hdr);
+ while (n > 0) {
+ b = buf;
+ for (b = buf; b != buf + sizeof(buf) - 2 && n--; b += 3)
+ sprintf(b, " %02x", (int) *ptr++);
+ strcpy(b, "\n");
+ LogPrintf(lev, buf);
+ }
+ }
+}
diff --git a/usr.sbin/ppp/log.h b/usr.sbin/ppp/log.h
new file mode 100644
index 0000000..d7fefa0
--- /dev/null
+++ b/usr.sbin/ppp/log.h
@@ -0,0 +1,44 @@
+/*
+ * $Id: log.h,v 1.15 1997/11/04 01:17:01 brian Exp $
+ */
+
+#define LogMIN (1)
+#define LogASYNC (1) /* syslog(LOG_INFO, ....) */
+#define LogCARRIER (2)
+#define LogCCP (3)
+#define LogCHAT (4)
+#define LogCOMMAND (5)
+#define LogCONNECT (6)
+#define LogDEBUG (7) /* syslog(LOG_DEBUG, ....) */
+#define LogHDLC (8)
+#define LogID0 (9)
+#define LogIPCP (10)
+#define LogLCP (11)
+#define LogLINK (12)
+#define LogLQM (13)
+#define LogPHASE (14)
+#define LogTCPIP (15)
+#define LogTUN (16) /* If set, tun%d is output with each message */
+#define LogMAXCONF (16)
+#define LogWARN (17) /* Sent to VarTerm else syslog(LOG_WARNING, ) */
+#define LogERROR (18) /* syslog(LOG_ERR, ....), + sent to VarTerm */
+#define LogALERT (19) /* syslog(LOG_ALERT, ....) */
+#define LogMAX (19)
+
+/* The first int arg for all of the following is one of the above values */
+extern const char *LogName(int);
+extern void LogKeep(int);
+extern void LogKeepLocal(int);
+extern void LogDiscard(int);
+extern void LogDiscardLocal(int);
+extern void LogDiscardAll(void);
+extern void LogDiscardAllLocal(void);
+#define LOG_KEPT_SYSLOG (1) /* Results of LogIsKept() */
+#define LOG_KEPT_LOCAL (2) /* Results of LogIsKept() */
+extern int LogIsKept(int);
+extern void LogOpen(const char *);
+extern void LogSetTun(int);
+extern void LogClose(void);
+extern void LogPrintf(int, char *,...);
+extern void LogDumpBp(int, char *, struct mbuf *);
+extern void LogDumpBuff(int, char *, u_char *, int);
diff --git a/usr.sbin/ppp/lqr.c b/usr.sbin/ppp/lqr.c
new file mode 100644
index 0000000..fa60c5d
--- /dev/null
+++ b/usr.sbin/ppp/lqr.c
@@ -0,0 +1,270 @@
+/*
+ * PPP Line Quality Monitoring (LQM) Module
+ *
+ * Written by Toshiharu OHNO (tony-o@iij.ad.jp)
+ *
+ * Copyright (C) 1993, Internet Initiative Japan, Inc. All rights reserverd.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the Internet Initiative Japan, Inc. The name of the
+ * IIJ may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: lqr.c,v 1.18 1997/08/31 22:59:35 brian Exp $
+ *
+ * o LQR based on RFC1333
+ *
+ * TODO:
+ * o LQM policy
+ * o Allow user to configure LQM method and interval.
+ */
+#include <sys/param.h>
+#include <netinet/in.h>
+
+#include <stdio.h>
+
+#include "mbuf.h"
+#include "log.h"
+#include "defs.h"
+#include "timer.h"
+#include "fsm.h"
+#include "lcpproto.h"
+#include "lqr.h"
+#include "hdlc.h"
+#include "lcp.h"
+#include "loadalias.h"
+#include "command.h"
+#include "vars.h"
+#include "main.h"
+
+struct pppTimer LqrTimer;
+
+static u_long lastpeerin = (u_long) - 1;
+
+static int lqmmethod;
+static int echoseq;
+static int gotseq;
+static int lqrsendcnt;
+
+struct echolqr {
+ u_long magic;
+ u_long signature;
+ u_long sequence;
+};
+
+#define SIGNATURE 0x594e4f54
+
+static void
+SendEchoReq()
+{
+ struct fsm *fp = &LcpFsm;
+ struct echolqr *lqr, lqrdata;
+
+ if (fp->state == ST_OPENED) {
+ lqr = &lqrdata;
+ lqr->magic = htonl(LcpInfo.want_magic);
+ lqr->signature = htonl(SIGNATURE);
+ LogPrintf(LogLQM, "Send echo LQR [%d]\n", echoseq);
+ lqr->sequence = htonl(echoseq++);
+ FsmOutput(fp, CODE_ECHOREQ, fp->reqid++,
+ (u_char *) lqr, sizeof(struct echolqr));
+ }
+}
+
+void
+RecvEchoLqr(struct mbuf * bp)
+{
+ struct echolqr *lqr;
+ u_long seq;
+
+ if (plength(bp) == sizeof(struct echolqr)) {
+ lqr = (struct echolqr *) MBUF_CTOP(bp);
+ if (htonl(lqr->signature) == SIGNATURE) {
+ seq = ntohl(lqr->sequence);
+ LogPrintf(LogLQM, "Got echo LQR [%d]\n", ntohl(lqr->sequence));
+ gotseq = seq;
+ }
+ }
+}
+
+void
+LqrChangeOrder(struct lqrdata * src, struct lqrdata * dst)
+{
+ u_long *sp, *dp;
+ int n;
+
+ sp = (u_long *) src;
+ dp = (u_long *) dst;
+ for (n = 0; n < sizeof(struct lqrdata) / sizeof(u_long); n++)
+ *dp++ = ntohl(*sp++);
+}
+
+static void
+SendLqrReport()
+{
+ struct mbuf *bp;
+
+ StopTimer(&LqrTimer);
+
+ if (lqmmethod & LQM_LQR) {
+ if (lqrsendcnt > 5) {
+
+ /*
+ * XXX: Should implement LQM strategy
+ */
+ LogPrintf(LogPHASE, "** 1 Too many ECHO packets are lost. **\n");
+ lqmmethod = 0; /* Prevent rcursion via LcpClose() */
+ reconnect(RECON_TRUE);
+ LcpClose();
+ } else {
+ bp = mballoc(sizeof(struct lqrdata), MB_LQR);
+ HdlcOutput(PRI_LINK, PROTO_LQR, bp);
+ lqrsendcnt++;
+ }
+ } else if (lqmmethod & LQM_ECHO) {
+ if (echoseq - gotseq > 5) {
+ LogPrintf(LogPHASE, "** 2 Too many ECHO packets are lost. **\n");
+ lqmmethod = 0; /* Prevent rcursion via LcpClose() */
+ reconnect(RECON_TRUE);
+ LcpClose();
+ } else
+ SendEchoReq();
+ }
+ if (lqmmethod && Enabled(ConfLqr))
+ StartTimer(&LqrTimer);
+}
+
+void
+LqrInput(struct mbuf * bp)
+{
+ int len;
+ u_char *cp;
+ struct lqrdata *lqr;
+
+ len = plength(bp);
+ if (len != sizeof(struct lqrdata)) {
+ pfree(bp);
+ return;
+ }
+ if (!Acceptable(ConfLqr)) {
+ bp->offset -= 2;
+ bp->cnt += 2;
+
+ cp = MBUF_CTOP(bp);
+ LcpSendProtoRej(cp, bp->cnt);
+ } else {
+ cp = MBUF_CTOP(bp);
+ lqr = (struct lqrdata *) cp;
+ if (ntohl(lqr->MagicNumber) != LcpInfo.his_magic) {
+ LogPrintf(LogERROR, "LqrInput: magic %x != expecting %x\n",
+ ntohl(lqr->MagicNumber), LcpInfo.his_magic);
+ pfree(bp);
+ return;
+ }
+
+ /*
+ * Convert byte order and save into our strage
+ */
+ LqrChangeOrder(lqr, &HisLqrData);
+ LqrDump("LqrInput", &HisLqrData);
+ lqrsendcnt = 0; /* we have received LQR from peer */
+
+ /*
+ * Generate LQR responce to peer, if i) We are not running LQR timer. ii)
+ * Two successive LQR's PeerInLQRs are same.
+ */
+ if (LqrTimer.load == 0 || lastpeerin == HisLqrData.PeerInLQRs) {
+ lqmmethod |= LQM_LQR;
+ SendLqrReport();
+ }
+ lastpeerin = HisLqrData.PeerInLQRs;
+ }
+ pfree(bp);
+}
+
+/*
+ * When LCP is reached to opened state, We'll start LQM activity.
+ */
+void
+StartLqm()
+{
+ struct lcpstate *lcp = &LcpInfo;
+ int period;
+
+ lqrsendcnt = 0; /* start waiting all over for ECHOs */
+ echoseq = 0;
+ gotseq = 0;
+
+ lqmmethod = LQM_ECHO;
+ if (Enabled(ConfLqr))
+ lqmmethod |= LQM_LQR;
+ StopTimer(&LqrTimer);
+ LogPrintf(LogLQM, "LQM method = %d\n", lqmmethod);
+
+ if (lcp->his_lqrperiod || lcp->want_lqrperiod) {
+
+ /*
+ * We need to run timer. Let's figure out period.
+ */
+ period = lcp->his_lqrperiod ? lcp->his_lqrperiod : lcp->want_lqrperiod;
+ StopTimer(&LqrTimer);
+ LqrTimer.state = TIMER_STOPPED;
+ LqrTimer.load = period * SECTICKS / 100;
+ LqrTimer.func = SendLqrReport;
+ SendLqrReport();
+ StartTimer(&LqrTimer);
+ LogPrintf(LogLQM, "Will send LQR every %d.%d secs\n",
+ period / 100, period % 100);
+ } else {
+ LogPrintf(LogLQM, "LQR is not activated.\n");
+ }
+}
+
+void
+StopLqrTimer()
+{
+ StopTimer(&LqrTimer);
+}
+
+void
+StopLqr(int method)
+{
+ LogPrintf(LogLQM, "StopLqr method = %x\n", method);
+
+ if (method == LQM_LQR)
+ LogPrintf(LogLQM, "Stop sending LQR, Use LCP ECHO instead.\n");
+ if (method == LQM_ECHO)
+ LogPrintf(LogLQM, "Stop sending LCP ECHO.\n");
+ lqmmethod &= ~method;
+ if (lqmmethod)
+ SendLqrReport();
+ else
+ StopTimer(&LqrTimer);
+}
+
+void
+LqrDump(char *message, struct lqrdata * lqr)
+{
+ if (LogIsKept(LogLQM)) {
+ LogPrintf(LogLQM, "%s:\n", message);
+ LogPrintf(LogLQM, " Magic: %08x LastOutLQRs: %08x\n",
+ lqr->MagicNumber, lqr->LastOutLQRs);
+ LogPrintf(LogLQM, " LastOutPackets: %08x LastOutOctets: %08x\n",
+ lqr->LastOutPackets, lqr->LastOutOctets);
+ LogPrintf(LogLQM, " PeerInLQRs: %08x PeerInPackets: %08x\n",
+ lqr->PeerInLQRs, lqr->PeerInPackets);
+ LogPrintf(LogLQM, " PeerInDiscards: %08x PeerInErrors: %08x\n",
+ lqr->PeerInDiscards, lqr->PeerInErrors);
+ LogPrintf(LogLQM, " PeerInOctets: %08x PeerOutLQRs: %08x\n",
+ lqr->PeerInOctets, lqr->PeerOutLQRs);
+ LogPrintf(LogLQM, " PeerOutPackets: %08x PeerOutOctets: %08x\n",
+ lqr->PeerOutPackets, lqr->PeerOutOctets);
+ }
+}
diff --git a/usr.sbin/ppp/lqr.h b/usr.sbin/ppp/lqr.h
new file mode 100644
index 0000000..fa784e5
--- /dev/null
+++ b/usr.sbin/ppp/lqr.h
@@ -0,0 +1,64 @@
+/*
+ * Written by Toshiharu OHNO (tony-o@iij.ad.jp)
+ *
+ * Copyright (C) 1993, Internet Initiative Japan, Inc. All rights reserverd.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the Internet Initiative Japan. The name of the
+ * IIJ may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: lqr.h,v 1.8 1997/10/26 01:03:10 brian Exp $
+ *
+ * TODO:
+ */
+
+/*
+ * Structure of LQR packet defined in RFC1333
+ */
+struct lqrdata {
+ u_long MagicNumber;
+ u_long LastOutLQRs;
+ u_long LastOutPackets;
+ u_long LastOutOctets;
+ u_long PeerInLQRs;
+ u_long PeerInPackets;
+ u_long PeerInDiscards;
+ u_long PeerInErrors;
+ u_long PeerInOctets;
+ u_long PeerOutLQRs;
+ u_long PeerOutPackets;
+ u_long PeerOutOctets;
+};
+
+struct lqrsave {
+ u_long SaveInLQRs;
+ u_long SaveInPackets;
+ u_long SaveInDiscards;
+ u_long SaveInErrors;
+ u_long SaveInOctets;
+};
+
+struct lqrdata MyLqrData, HisLqrData;
+struct lqrsave HisLqrSave;
+
+/*
+ * We support LQR and ECHO as LQM method
+ */
+#define LQM_LQR 1
+#define LQM_ECHO 2
+
+extern void LqrDump(char *, struct lqrdata *);
+extern void LqrChangeOrder(struct lqrdata *, struct lqrdata *);
+extern void StartLqm(void);
+extern void StopLqr(int);
+extern void StopLqrTimer(void);
+extern void RecvEchoLqr(struct mbuf *);
+extern void LqrInput(struct mbuf *);
diff --git a/usr.sbin/ppp/main.c b/usr.sbin/ppp/main.c
new file mode 100644
index 0000000..7224eb0
--- /dev/null
+++ b/usr.sbin/ppp/main.c
@@ -0,0 +1,1040 @@
+/*
+ * User Process PPP
+ *
+ * Written by Toshiharu OHNO (tony-o@iij.ad.jp)
+ *
+ * Copyright (C) 1993, Internet Initiative Japan, Inc. All rights reserverd.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the Internet Initiative Japan, Inc. The name of the
+ * IIJ may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: main.c,v 1.91 1997/11/09 18:51:23 brian Exp $
+ *
+ * TODO:
+ * o Add commands for traffic summary, version display, etc.
+ * o Add signal handler for misc controls.
+ */
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <arpa/inet.h>
+#include <netdb.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 <sys/wait.h>
+#include <sysexits.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "mbuf.h"
+#include "log.h"
+#include "defs.h"
+#include "id.h"
+#include "timer.h"
+#include "fsm.h"
+#include "modem.h"
+#include "os.h"
+#include "hdlc.h"
+#include "ccp.h"
+#include "lcp.h"
+#include "ipcp.h"
+#include "loadalias.h"
+#include "command.h"
+#include "vars.h"
+#include "auth.h"
+#include "filter.h"
+#include "systems.h"
+#include "ip.h"
+#include "sig.h"
+#include "server.h"
+#include "lcpproto.h"
+#include "main.h"
+#include "vjcomp.h"
+#include "async.h"
+
+#ifndef O_NONBLOCK
+#ifdef O_NDELAY
+#define O_NONBLOCK O_NDELAY
+#endif
+#endif
+
+int TermMode = 0;
+int tunno = 0;
+
+static struct termios oldtio; /* Original tty mode */
+static struct termios comtio; /* Command level tty mode */
+static pid_t BGPid = 0;
+static char pid_filename[MAXPATHLEN];
+static int dial_up;
+
+static void DoLoop(void);
+static void TerminalStop(int);
+static char *ex_desc(int);
+
+static void
+TtyInit(int DontWantInt)
+{
+ struct termios newtio;
+ int stat;
+
+ stat = fcntl(0, F_GETFL, 0);
+ if (stat > 0) {
+ stat |= O_NONBLOCK;
+ (void) fcntl(0, F_SETFL, stat);
+ }
+ newtio = oldtio;
+ newtio.c_lflag &= ~(ECHO | ISIG | ICANON);
+ newtio.c_iflag = 0;
+ newtio.c_oflag &= ~OPOST;
+ newtio.c_cc[VEOF] = _POSIX_VDISABLE;
+ if (DontWantInt)
+ newtio.c_cc[VINTR] = _POSIX_VDISABLE;
+ newtio.c_cc[VMIN] = 1;
+ newtio.c_cc[VTIME] = 0;
+ newtio.c_cflag |= CS8;
+ tcsetattr(0, TCSADRAIN, &newtio);
+ comtio = newtio;
+}
+
+/*
+ * Set tty into command mode. We allow canonical input and echo processing.
+ */
+void
+TtyCommandMode(int prompt)
+{
+ struct termios newtio;
+ int stat;
+
+ if (!(mode & MODE_INTER))
+ return;
+ tcgetattr(0, &newtio);
+ newtio.c_lflag |= (ECHO | ISIG | ICANON);
+ newtio.c_iflag = oldtio.c_iflag;
+ newtio.c_oflag |= OPOST;
+ tcsetattr(0, TCSADRAIN, &newtio);
+ stat = fcntl(0, F_GETFL, 0);
+ if (stat > 0) {
+ stat |= O_NONBLOCK;
+ (void) fcntl(0, F_SETFL, stat);
+ }
+ TermMode = 0;
+ if (prompt)
+ Prompt();
+}
+
+/*
+ * Set tty into terminal mode which is used while we invoke term command.
+ */
+void
+TtyTermMode()
+{
+ int stat;
+
+ tcsetattr(0, TCSADRAIN, &comtio);
+ stat = fcntl(0, F_GETFL, 0);
+ if (stat > 0) {
+ stat &= ~O_NONBLOCK;
+ (void) fcntl(0, F_SETFL, stat);
+ }
+ TermMode = 1;
+}
+
+void
+TtyOldMode()
+{
+ int stat;
+
+ stat = fcntl(0, F_GETFL, 0);
+ if (stat > 0) {
+ stat &= ~O_NONBLOCK;
+ (void) fcntl(0, F_SETFL, stat);
+ }
+ tcsetattr(0, TCSANOW, &oldtio);
+}
+
+void
+Cleanup(int excode)
+{
+ ServerClose();
+ OsInterfaceDown(1);
+ HangupModem(1);
+ nointr_sleep(1);
+ if (mode & MODE_AUTO)
+ DeleteIfRoutes(1);
+ ID0unlink(pid_filename);
+ if (mode & MODE_BACKGROUND && BGFiledes[1] != -1) {
+ char c = EX_ERRDEAD;
+
+ if (write(BGFiledes[1], &c, 1) == 1)
+ LogPrintf(LogPHASE, "Parent notified of failure.\n");
+ else
+ LogPrintf(LogPHASE, "Failed to notify parent of failure.\n");
+ close(BGFiledes[1]);
+ }
+ LogPrintf(LogPHASE, "PPP Terminated (%s).\n", ex_desc(excode));
+ TtyOldMode();
+ LogClose();
+
+ exit(excode);
+}
+
+static void
+CloseConnection(int signo)
+{
+ /* NOTE, these are manual, we've done a setsid() */
+ LogPrintf(LogPHASE, "Caught signal %d, abort connection\n", signo);
+ reconnectState = RECON_FALSE;
+ reconnectCount = 0;
+ DownConnection();
+ dial_up = 0;
+}
+
+static void
+CloseSession(int signo)
+{
+ if (BGPid) {
+ kill(BGPid, SIGINT);
+ exit(EX_TERM);
+ }
+ LogPrintf(LogPHASE, "Signal %d, terminate.\n", signo);
+ reconnect(RECON_FALSE);
+ LcpClose();
+ Cleanup(EX_TERM);
+}
+
+static void
+TerminalCont()
+{
+ pending_signal(SIGCONT, SIG_DFL);
+ pending_signal(SIGTSTP, TerminalStop);
+ TtyCommandMode(getpgrp() == tcgetpgrp(0));
+}
+
+static void
+TerminalStop(int signo)
+{
+ pending_signal(SIGCONT, TerminalCont);
+ TtyOldMode();
+ pending_signal(SIGTSTP, SIG_DFL);
+ kill(getpid(), signo);
+}
+
+static void
+SetUpServer(int signo)
+{
+ int res;
+
+ VarHaveLocalAuthKey = 0;
+ LocalAuthInit();
+ if ((res = ServerTcpOpen(SERVER_PORT + tunno)) != 0)
+ LogPrintf(LogERROR, "SIGUSR1: Failed %d to open port %d\n",
+ res, SERVER_PORT + tunno);
+}
+
+static void
+BringDownServer(int signo)
+{
+ VarHaveLocalAuthKey = 0;
+ LocalAuthInit();
+ ServerClose();
+}
+
+static char *
+ex_desc(int ex)
+{
+ static char num[12];
+ static char *desc[] = {"normal", "start", "sock",
+ "modem", "dial", "dead", "done", "reboot", "errdead",
+ "hangup", "term", "nodial", "nologin"};
+
+ if (ex >= 0 && ex < sizeof(desc) / sizeof(*desc))
+ return desc[ex];
+ snprintf(num, sizeof num, "%d", ex);
+ return num;
+}
+
+static void
+Usage()
+{
+ fprintf(stderr,
+ "Usage: ppp [-auto | -background | -direct | -dedicated | -ddial ] [ -alias ] [system]\n");
+ exit(EX_START);
+}
+
+static void
+ProcessArgs(int argc, char **argv)
+{
+ int optc;
+ char *cp;
+
+ optc = 0;
+ while (argc > 0 && **argv == '-') {
+ cp = *argv + 1;
+ if (strcmp(cp, "auto") == 0)
+ mode |= MODE_AUTO;
+ else if (strcmp(cp, "background") == 0)
+ mode |= MODE_BACKGROUND | MODE_AUTO;
+ else if (strcmp(cp, "direct") == 0)
+ mode |= MODE_DIRECT;
+ else if (strcmp(cp, "dedicated") == 0)
+ mode |= MODE_DEDICATED;
+ else if (strcmp(cp, "ddial") == 0)
+ mode |= MODE_DDIAL | MODE_AUTO;
+ else if (strcmp(cp, "alias") == 0) {
+ if (loadAliasHandlers(&VarAliasHandlers) == 0)
+ mode |= MODE_ALIAS;
+ else
+ LogPrintf(LogWARN, "Cannot load alias library\n");
+ optc--; /* this option isn't exclusive */
+ } else
+ Usage();
+ optc++;
+ argv++;
+ argc--;
+ }
+ if (argc > 1) {
+ fprintf(stderr, "specify only one system label.\n");
+ exit(EX_START);
+ }
+ if (argc == 1)
+ dstsystem = *argv;
+
+ if (optc > 1) {
+ fprintf(stderr, "specify only one mode.\n");
+ exit(EX_START);
+ }
+}
+
+static void
+Greetings()
+{
+ if (VarTerm) {
+ fprintf(VarTerm, "User Process PPP. Written by Toshiharu OHNO.\n");
+ fflush(VarTerm);
+ }
+}
+
+int
+main(int argc, char **argv)
+{
+ FILE *lockfile;
+ char *name;
+
+ VarTerm = 0;
+ name = strrchr(argv[0], '/');
+ LogOpen(name ? name + 1 : argv[0]);
+
+ argc--;
+ argv++;
+ ProcessArgs(argc, argv);
+ if (!(mode & MODE_DIRECT)) {
+ if (getuid() != 0) {
+ fprintf(stderr, "You may only run ppp in client mode as user id 0\n");
+ LogClose();
+ return EX_NOPERM;
+ }
+ VarTerm = stdout;
+ }
+ ID0init();
+ Greetings();
+ IpcpDefAddress();
+ LocalAuthInit();
+
+ if (SelectSystem("default", CONFFILE) < 0 && VarTerm)
+ fprintf(VarTerm, "Warning: No default entry is given in config file.\n");
+
+ if (OpenTunnel(&tunno) < 0) {
+ LogPrintf(LogWARN, "open_tun: %s\n", strerror(errno));
+ return EX_START;
+ }
+ if (mode & (MODE_AUTO | MODE_DIRECT | MODE_DEDICATED))
+ mode &= ~MODE_INTER;
+ if (mode & MODE_INTER) {
+ fprintf(VarTerm, "Interactive mode\n");
+ netfd = STDOUT_FILENO;
+ } else if (mode & MODE_AUTO) {
+ fprintf(VarTerm, "Automatic Dialer mode\n");
+ if (dstsystem == NULL) {
+ if (VarTerm)
+ fprintf(VarTerm, "Destination system must be specified in"
+ " auto, background or ddial mode.\n");
+ return EX_START;
+ }
+ }
+ tcgetattr(0, &oldtio); /* Save original tty mode */
+
+ pending_signal(SIGHUP, CloseSession);
+ pending_signal(SIGTERM, CloseSession);
+ pending_signal(SIGINT, CloseConnection);
+ pending_signal(SIGQUIT, CloseSession);
+#ifdef SIGPIPE
+ signal(SIGPIPE, SIG_IGN);
+#endif
+#ifdef SIGALRM
+ pending_signal(SIGALRM, SIG_IGN);
+#endif
+ if (mode & MODE_INTER) {
+#ifdef SIGTSTP
+ pending_signal(SIGTSTP, TerminalStop);
+#endif
+#ifdef SIGTTIN
+ pending_signal(SIGTTIN, TerminalStop);
+#endif
+#ifdef SIGTTOU
+ pending_signal(SIGTTOU, SIG_IGN);
+#endif
+ }
+#ifdef SIGUSR1
+ if (mode != MODE_INTER)
+ pending_signal(SIGUSR1, SetUpServer);
+#endif
+#ifdef SIGUSR2
+ if (mode != MODE_INTER)
+ pending_signal(SIGUSR2, BringDownServer);
+#endif
+
+ if (dstsystem) {
+ if (SelectSystem(dstsystem, CONFFILE) < 0) {
+ LogPrintf(LogWARN, "Destination system not found in conf file.\n");
+ Cleanup(EX_START);
+ }
+ if ((mode & MODE_AUTO) && DefHisAddress.ipaddr.s_addr == INADDR_ANY) {
+ LogPrintf(LogWARN, "Must specify dstaddr with"
+ " auto, background or ddial mode.\n");
+ Cleanup(EX_START);
+ }
+ }
+
+ if (!(mode & MODE_INTER)) {
+ if (mode & MODE_BACKGROUND) {
+ if (pipe(BGFiledes)) {
+ LogPrintf(LogERROR, "pipe: %s\n", strerror(errno));
+ Cleanup(EX_SOCK);
+ }
+ }
+
+ if (!(mode & MODE_DIRECT)) {
+ pid_t bgpid;
+
+ bgpid = fork();
+ if (bgpid == -1) {
+ LogPrintf(LogERROR, "fork: %s\n", strerror(errno));
+ Cleanup(EX_SOCK);
+ }
+ if (bgpid) {
+ char c = EX_NORMAL;
+
+ if (mode & MODE_BACKGROUND) {
+ /* Wait for our child to close its pipe before we exit. */
+ BGPid = bgpid;
+ close(BGFiledes[1]);
+ if (read(BGFiledes[0], &c, 1) != 1) {
+ fprintf(VarTerm, "Child exit, no status.\n");
+ LogPrintf(LogPHASE, "Parent: Child exit, no status.\n");
+ } else if (c == EX_NORMAL) {
+ fprintf(VarTerm, "PPP enabled.\n");
+ LogPrintf(LogPHASE, "Parent: PPP enabled.\n");
+ } else {
+ fprintf(VarTerm, "Child failed (%s).\n", ex_desc((int) c));
+ LogPrintf(LogPHASE, "Parent: Child failed (%s).\n",
+ ex_desc((int) c));
+ }
+ close(BGFiledes[0]);
+ }
+ return c;
+ } else if (mode & MODE_BACKGROUND)
+ close(BGFiledes[0]);
+ }
+
+ VarTerm = 0; /* We know it's currently stdout */
+ close(1);
+ close(2);
+
+#ifdef DOTTYINIT
+ if (mode & (MODE_DIRECT | MODE_DEDICATED))
+#else
+ if (mode & MODE_DIRECT)
+#endif
+ TtyInit(1);
+ else {
+ setsid();
+ close(0);
+ }
+ } else {
+ TtyInit(0);
+ TtyCommandMode(1);
+ }
+
+ snprintf(pid_filename, sizeof(pid_filename), "%stun%d.pid",
+ _PATH_VARRUN, tunno);
+ lockfile = ID0fopen(pid_filename, "w");
+ if (lockfile != NULL) {
+ fprintf(lockfile, "%d\n", (int) getpid());
+ fclose(lockfile);
+ } else
+ LogPrintf(LogALERT, "Warning: Can't create %s: %s\n",
+ pid_filename, strerror(errno));
+
+ LogPrintf(LogPHASE, "PPP Started.\n");
+
+
+ do
+ DoLoop();
+ while (mode & MODE_DEDICATED);
+
+ Cleanup(EX_DONE);
+ return 0;
+}
+
+/*
+ * Turn into packet mode, where we speak PPP.
+ */
+void
+PacketMode()
+{
+ if (RawModem() < 0) {
+ LogPrintf(LogWARN, "PacketMode: Not connected.\n");
+ return;
+ }
+ AsyncInit();
+ VjInit(15);
+ LcpInit();
+ IpcpInit();
+ CcpInit();
+ LcpUp();
+
+ LcpOpen(VarOpenMode);
+ if ((mode & (MODE_INTER | MODE_AUTO)) == MODE_INTER) {
+ TtyCommandMode(1);
+ if (VarTerm) {
+ fprintf(VarTerm, "Packet mode.\n");
+ aft_cmd = 1;
+ }
+ }
+}
+
+static void
+ShowHelp()
+{
+ fprintf(stderr, "The following commands are available:\r\n");
+ fprintf(stderr, " ~p\tEnter Packet mode\r\n");
+ fprintf(stderr, " ~-\tDecrease log level\r\n");
+ fprintf(stderr, " ~+\tIncrease log level\r\n");
+ fprintf(stderr, " ~t\tShow timers (only in \"log debug\" mode)\r\n");
+ fprintf(stderr, " ~m\tShow memory map (only in \"log debug\" mode)\r\n");
+ fprintf(stderr, " ~.\tTerminate program\r\n");
+ fprintf(stderr, " ~?\tThis help\r\n");
+}
+
+static void
+ReadTty()
+{
+ int n;
+ char ch;
+ static int ttystate;
+ FILE *oVarTerm;
+ char linebuff[LINE_LEN];
+
+ LogPrintf(LogDEBUG, "termode = %d, netfd = %d, mode = %d\n",
+ TermMode, netfd, mode);
+ if (!TermMode) {
+ n = read(netfd, linebuff, sizeof(linebuff) - 1);
+ if (n > 0) {
+ aft_cmd = 1;
+ if (linebuff[n-1] == '\n')
+ linebuff[--n] = '\0';
+ if (n) {
+ if (IsInteractive(0))
+ LogPrintf(LogCOMMAND, "%s\n", linebuff);
+ else
+ LogPrintf(LogCOMMAND, "Client: %s\n", linebuff);
+ DecodeCommand(linebuff, n, 1);
+ } else
+ Prompt();
+ } else {
+ LogPrintf(LogPHASE, "client connection closed.\n");
+ mode &= ~MODE_INTER;
+ oVarTerm = VarTerm;
+ VarTerm = 0;
+ if (oVarTerm && oVarTerm != stdout)
+ fclose(oVarTerm);
+ close(netfd);
+ netfd = -1;
+ }
+ return;
+ }
+
+ /*
+ * We are in terminal mode, decode special sequences
+ */
+ n = read(fileno(VarTerm), &ch, 1);
+ LogPrintf(LogDEBUG, "Got %d bytes (reading from the terminal)\n", n);
+
+ if (n > 0) {
+ switch (ttystate) {
+ case 0:
+ if (ch == '~')
+ ttystate++;
+ else
+ write(modem, &ch, n);
+ break;
+ case 1:
+ switch (ch) {
+ case '?':
+ ShowHelp();
+ break;
+ case 'p':
+
+ /*
+ * XXX: Should check carrier.
+ */
+ if (LcpFsm.state <= ST_CLOSED) {
+ VarOpenMode = OPEN_ACTIVE;
+ PacketMode();
+ }
+ break;
+ case '.':
+ TermMode = 1;
+ aft_cmd = 1;
+ TtyCommandMode(1);
+ break;
+ case 't':
+ if (LogIsKept(LogDEBUG)) {
+ ShowTimers();
+ break;
+ }
+ case 'm':
+ if (LogIsKept(LogDEBUG)) {
+ ShowMemMap();
+ break;
+ }
+ default:
+ if (write(modem, &ch, n) < 0)
+ LogPrintf(LogERROR, "error writing to modem.\n");
+ break;
+ }
+ ttystate = 0;
+ break;
+ }
+ }
+}
+
+
+/*
+ * Here, we'll try to detect HDLC frame
+ */
+
+static char *FrameHeaders[] = {
+ "\176\377\003\300\041",
+ "\176\377\175\043\300\041",
+ "\176\177\175\043\100\041",
+ "\176\175\337\175\043\300\041",
+ "\176\175\137\175\043\100\041",
+ NULL,
+};
+
+static u_char *
+HdlcDetect(u_char * cp, int n)
+{
+ char *ptr, *fp, **hp;
+
+ cp[n] = '\0'; /* be sure to null terminated */
+ ptr = NULL;
+ for (hp = FrameHeaders; *hp; hp++) {
+ fp = *hp;
+ if (DEV_IS_SYNC)
+ fp++;
+ ptr = strstr((char *) cp, fp);
+ if (ptr)
+ break;
+ }
+ return ((u_char *) ptr);
+}
+
+static struct pppTimer RedialTimer;
+
+static void
+RedialTimeout()
+{
+ StopTimer(&RedialTimer);
+ LogPrintf(LogPHASE, "Redialing timer expired.\n");
+}
+
+static void
+StartRedialTimer(int Timeout)
+{
+ StopTimer(&RedialTimer);
+
+ if (Timeout) {
+ RedialTimer.state = TIMER_STOPPED;
+
+ if (Timeout > 0)
+ RedialTimer.load = Timeout * SECTICKS;
+ else
+ RedialTimer.load = (random() % REDIAL_PERIOD) * SECTICKS;
+
+ LogPrintf(LogPHASE, "Enter pause (%d) for redialing.\n",
+ RedialTimer.load / SECTICKS);
+
+ RedialTimer.func = RedialTimeout;
+ StartTimer(&RedialTimer);
+ }
+}
+
+
+static void
+DoLoop()
+{
+ fd_set rfds, wfds, efds;
+ int pri, i, n, wfd, nfds;
+ struct sockaddr_in hisaddr;
+ struct timeval timeout, *tp;
+ int ssize = sizeof(hisaddr);
+ u_char *cp;
+ u_char rbuff[MAX_MRU];
+ int tries;
+ int qlen;
+ int res;
+ pid_t pgroup;
+
+ pgroup = getpgrp();
+
+ if (mode & MODE_DIRECT) {
+ LogPrintf(LogDEBUG, "Opening modem\n");
+ if (OpenModem() < 0)
+ return;
+ LogPrintf(LogPHASE, "Packet mode enabled\n");
+ PacketMode();
+ } else if (mode & MODE_DEDICATED) {
+ if (modem < 0)
+ while (OpenModem() < 0)
+ nointr_sleep(VarReconnectTimer);
+ }
+ fflush(VarTerm);
+
+ timeout.tv_sec = 0;
+ timeout.tv_usec = 0;
+ reconnectState = RECON_UNKNOWN;
+
+ if (mode & MODE_BACKGROUND)
+ dial_up = 1; /* Bring the line up */
+ else
+ dial_up = 0; /* XXXX */
+ tries = 0;
+ for (;;) {
+ nfds = 0;
+ FD_ZERO(&rfds);
+ FD_ZERO(&wfds);
+ FD_ZERO(&efds);
+
+ /*
+ * If the link is down and we're in DDIAL mode, bring it back up.
+ */
+ if (mode & MODE_DDIAL && LcpFsm.state <= ST_CLOSED)
+ dial_up = 1;
+
+ /*
+ * If we lost carrier and want to re-establish the connection due to the
+ * "set reconnect" value, we'd better bring the line back up.
+ */
+ if (LcpFsm.state <= ST_CLOSED) {
+ if (!dial_up && reconnectState == RECON_TRUE) {
+ if (++reconnectCount <= VarReconnectTries) {
+ LogPrintf(LogPHASE, "Connection lost, re-establish (%d/%d)\n",
+ reconnectCount, VarReconnectTries);
+ StartRedialTimer(VarReconnectTimer);
+ dial_up = 1;
+ } else {
+ if (VarReconnectTries)
+ LogPrintf(LogPHASE, "Connection lost, maximum (%d) times\n",
+ VarReconnectTries);
+ reconnectCount = 0;
+ if (mode & MODE_BACKGROUND)
+ Cleanup(EX_DEAD);
+ }
+ reconnectState = RECON_ENVOKED;
+ }
+ }
+
+ /*
+ * If Ip packet for output is enqueued and require dial up, Just do it!
+ */
+ if (dial_up && RedialTimer.state != TIMER_RUNNING) {
+ LogPrintf(LogDEBUG, "going to dial: modem = %d\n", modem);
+ if (OpenModem() < 0) {
+ tries++;
+ if (!(mode & MODE_DDIAL) && VarDialTries)
+ LogPrintf(LogCHAT, "Failed to open modem (attempt %u of %d)\n",
+ tries, VarDialTries);
+ else
+ LogPrintf(LogCHAT, "Failed to open modem (attempt %u)\n", tries);
+
+ if (!(mode & MODE_DDIAL) && VarDialTries && tries >= VarDialTries) {
+ if (mode & MODE_BACKGROUND)
+ Cleanup(EX_DIAL); /* Can't get the modem */
+ dial_up = 0;
+ reconnectState = RECON_UNKNOWN;
+ reconnectCount = 0;
+ tries = 0;
+ } else
+ StartRedialTimer(VarRedialTimeout);
+ } else {
+ tries++; /* Tries are per number, not per list of
+ * numbers. */
+ if (!(mode & MODE_DDIAL) && VarDialTries)
+ LogPrintf(LogCHAT, "Dial attempt %u of %d\n", tries, VarDialTries);
+ else
+ LogPrintf(LogCHAT, "Dial attempt %u\n", tries);
+
+ if ((res = DialModem()) == EX_DONE) {
+ nointr_sleep(1); /* little pause to allow peer starts */
+ ModemTimeout();
+ PacketMode();
+ dial_up = 0;
+ reconnectState = RECON_UNKNOWN;
+ tries = 0;
+ } else {
+ if (mode & MODE_BACKGROUND) {
+ if (VarNextPhone == NULL || res == EX_SIG)
+ Cleanup(EX_DIAL); /* Tried all numbers - no luck */
+ else
+ /* Try all numbers in background mode */
+ StartRedialTimer(VarRedialNextTimeout);
+ } else if (!(mode & MODE_DDIAL) &&
+ ((VarDialTries && tries >= VarDialTries) ||
+ res == EX_SIG)) {
+ /* I give up ! Can't get through :( */
+ StartRedialTimer(VarRedialTimeout);
+ dial_up = 0;
+ reconnectState = RECON_UNKNOWN;
+ reconnectCount = 0;
+ tries = 0;
+ } else if (VarNextPhone == NULL)
+ /* Dial failed. Keep quite during redial wait period. */
+ StartRedialTimer(VarRedialTimeout);
+ else
+ StartRedialTimer(VarRedialNextTimeout);
+ }
+ }
+ }
+ qlen = ModemQlen();
+
+ if (qlen == 0) {
+ IpStartOutput();
+ qlen = ModemQlen();
+ }
+ if (modem >= 0) {
+ if (modem + 1 > nfds)
+ nfds = modem + 1;
+ FD_SET(modem, &rfds);
+ FD_SET(modem, &efds);
+ if (qlen > 0) {
+ FD_SET(modem, &wfds);
+ }
+ }
+ if (server >= 0) {
+ if (server + 1 > nfds)
+ nfds = server + 1;
+ FD_SET(server, &rfds);
+ }
+
+ /*
+ * *** IMPORTANT ***
+ *
+ * CPU is serviced every TICKUNIT micro seconds. This value must be chosen
+ * with great care. If this values is too big, it results loss of
+ * characters from modem and poor responce. If this values is too small,
+ * ppp process eats many CPU time.
+ */
+#ifndef SIGALRM
+ nointr_usleep(TICKUNIT);
+ TimerService();
+#else
+ handle_signals();
+#endif
+
+ /* If there are aren't many packets queued, look for some more. */
+ if (qlen < 20 && tun_in >= 0) {
+ if (tun_in + 1 > nfds)
+ nfds = tun_in + 1;
+ FD_SET(tun_in, &rfds);
+ }
+ if (netfd >= 0) {
+ if (netfd + 1 > nfds)
+ nfds = netfd + 1;
+ FD_SET(netfd, &rfds);
+ FD_SET(netfd, &efds);
+ }
+#ifndef SIGALRM
+
+ /*
+ * Normally, select() will not block because modem is writable. In AUTO
+ * mode, select will block until we find packet from tun
+ */
+ tp = (RedialTimer.state == TIMER_RUNNING) ? &timeout : NULL;
+ i = select(nfds, &rfds, &wfds, &efds, tp);
+#else
+
+ /*
+ * When SIGALRM timer is running, a select function will be return -1 and
+ * EINTR after a Time Service signal hundler is done. If the redial
+ * timer is not running and we are trying to dial, poll with a 0 value
+ * timer.
+ */
+ tp = (dial_up && RedialTimer.state != TIMER_RUNNING) ? &timeout : NULL;
+ i = select(nfds, &rfds, &wfds, &efds, tp);
+#endif
+
+ if (i == 0) {
+ continue;
+ }
+ if (i < 0) {
+ if (errno == EINTR) {
+ handle_signals();
+ continue;
+ }
+ LogPrintf(LogERROR, "DoLoop: select(): %s\n", strerror(errno));
+ break;
+ }
+ if ((netfd >= 0 && FD_ISSET(netfd, &efds)) || (modem >= 0 && FD_ISSET(modem, &efds))) {
+ LogPrintf(LogALERT, "Exception detected.\n");
+ break;
+ }
+ if (server >= 0 && FD_ISSET(server, &rfds)) {
+ LogPrintf(LogPHASE, "connected to client.\n");
+ wfd = accept(server, (struct sockaddr *) & hisaddr, &ssize);
+ if (wfd < 0) {
+ LogPrintf(LogERROR, "DoLoop: accept(): %s\n", strerror(errno));
+ continue;
+ }
+ if (netfd >= 0) {
+ write(wfd, "already in use.\n", 16);
+ close(wfd);
+ continue;
+ } else
+ netfd = wfd;
+ VarTerm = fdopen(netfd, "a+");
+ LocalAuthInit();
+ mode |= MODE_INTER;
+ Greetings();
+ IsInteractive(1);
+ Prompt();
+ }
+ if ((mode & MODE_INTER) && (netfd >= 0 && FD_ISSET(netfd, &rfds)) &&
+ ((mode & MODE_AUTO) || pgroup == tcgetpgrp(0))) {
+ /* something to read from tty */
+ ReadTty();
+ }
+ if (modem >= 0) {
+ if (FD_ISSET(modem, &wfds)) { /* ready to write into modem */
+ ModemStartOutput(modem);
+ }
+ if (FD_ISSET(modem, &rfds)) { /* something to read from modem */
+ if (LcpFsm.state <= ST_CLOSED)
+ nointr_usleep(10000);
+ n = read(modem, rbuff, sizeof(rbuff));
+ if ((mode & MODE_DIRECT) && n <= 0) {
+ DownConnection();
+ } else
+ LogDumpBuff(LogASYNC, "ReadFromModem", rbuff, n);
+
+ if (LcpFsm.state <= ST_CLOSED) {
+
+ /*
+ * In dedicated mode, we just discard input until LCP is started.
+ */
+ if (!(mode & MODE_DEDICATED)) {
+ cp = HdlcDetect(rbuff, n);
+ if (cp) {
+
+ /*
+ * LCP packet is detected. Turn ourselves into packet mode.
+ */
+ if (cp != rbuff) {
+ write(modem, rbuff, cp - rbuff);
+ write(modem, "\r\n", 2);
+ }
+ PacketMode();
+ } else
+ write(fileno(VarTerm), rbuff, n);
+ }
+ } else {
+ if (n > 0)
+ AsyncInput(rbuff, n);
+ }
+ }
+ }
+ if (tun_in >= 0 && FD_ISSET(tun_in, &rfds)) { /* something to read
+ * from tun */
+ n = read(tun_in, rbuff, sizeof(rbuff));
+ if (n < 0) {
+ LogPrintf(LogERROR, "read from tun: %s\n", strerror(errno));
+ continue;
+ }
+ if (((struct ip *) rbuff)->ip_dst.s_addr == IpcpInfo.want_ipaddr.s_addr) {
+ /* we've been asked to send something addressed *to* us :( */
+ if (VarLoopback) {
+ pri = PacketCheck(rbuff, n, FL_IN);
+ if (pri >= 0) {
+ struct mbuf *bp;
+
+ if (mode & MODE_ALIAS) {
+ VarPacketAliasIn(rbuff, sizeof rbuff);
+ n = ntohs(((struct ip *) rbuff)->ip_len);
+ }
+ bp = mballoc(n, MB_IPIN);
+ memcpy(MBUF_CTOP(bp), rbuff, n);
+ IpInput(bp);
+ LogPrintf(LogDEBUG, "Looped back packet addressed to myself\n");
+ }
+ continue;
+ } else
+ LogPrintf(LogDEBUG, "Oops - forwarding packet addressed to myself\n");
+ }
+
+ /*
+ * Process on-demand dialup. Output packets are queued within tunnel
+ * device until IPCP is opened.
+ */
+ if (LcpFsm.state <= ST_CLOSED && (mode & MODE_AUTO)) {
+ pri = PacketCheck(rbuff, n, FL_DIAL);
+ if (pri >= 0) {
+ if (mode & MODE_ALIAS) {
+ VarPacketAliasOut(rbuff, sizeof rbuff);
+ n = ntohs(((struct ip *) rbuff)->ip_len);
+ }
+ IpEnqueue(pri, rbuff, n);
+ dial_up = 1; /* XXX */
+ }
+ continue;
+ }
+ pri = PacketCheck(rbuff, n, FL_OUT);
+ if (pri >= 0) {
+ if (mode & MODE_ALIAS) {
+ VarPacketAliasOut(rbuff, sizeof rbuff);
+ n = ntohs(((struct ip *) rbuff)->ip_len);
+ }
+ IpEnqueue(pri, rbuff, n);
+ }
+ }
+ }
+ LogPrintf(LogDEBUG, "Job (DoLoop) done.\n");
+}
diff --git a/usr.sbin/ppp/main.h b/usr.sbin/ppp/main.h
new file mode 100644
index 0000000..37820de
--- /dev/null
+++ b/usr.sbin/ppp/main.h
@@ -0,0 +1,32 @@
+/*
+ * User Process PPP
+ *
+ * Written by Toshiharu OHNO (tony-o@iij.ad.jp)
+ *
+ * Copyright (C) 1993, Internet Initiative Japan, Inc. All rights reserverd.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the Internet Initiative Japan, Inc. The name of the
+ * IIJ may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: main.h,v 1.6 1997/09/25 00:52:35 brian Exp $
+ *
+ */
+
+extern int TermMode;
+extern int tunno;
+
+extern void Cleanup(int);
+extern void TtyTermMode(void);
+extern void PacketMode(void);
+extern void TtyOldMode(void);
+extern void TtyTermMode(void);
+extern void TtyCommandMode(int);
diff --git a/usr.sbin/ppp/mbuf.c b/usr.sbin/ppp/mbuf.c
new file mode 100644
index 0000000..8a86e90
--- /dev/null
+++ b/usr.sbin/ppp/mbuf.c
@@ -0,0 +1,175 @@
+/*
+ * PPP Memory handling module
+ *
+ * Written by Toshiharu OHNO (tony-o@iij.ad.jp)
+ *
+ * Copyright (C) 1993, Internet Initiative Japan, Inc. All rights reserverd.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the Internet Initiative Japan, Inc. The name of the
+ * IIJ may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: mbuf.c,v 1.9 1997/08/25 00:29:20 brian Exp $
+ *
+ */
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "mbuf.h"
+#include "log.h"
+#include "defs.h"
+#include "loadalias.h"
+#include "command.h"
+#include "vars.h"
+#include "server.h"
+
+struct memmap {
+ struct mbuf *queue;
+ int count;
+} MemMap[MB_MAX + 2];
+
+static int totalalloced;
+
+int
+plength(struct mbuf * bp)
+{
+ int len;
+
+ for (len = 0; bp; bp = bp->next)
+ len += bp->cnt;
+ return (len);
+}
+
+struct mbuf *
+mballoc(int cnt, int type)
+{
+ u_char *p;
+ struct mbuf *bp;
+
+ if (type > MB_MAX)
+ LogPrintf(LogERROR, "Bad mbuf type %d\n", type);
+ bp = (struct mbuf *) malloc(sizeof(struct mbuf));
+ if (bp == NULL) {
+ LogPrintf(LogALERT, "failed to allocate memory: %u\n", sizeof(struct mbuf));
+ ServerClose();
+ exit(1);
+ }
+ memset(bp, '\0', sizeof(struct mbuf));
+ p = (u_char *) malloc(cnt);
+ if (p == NULL) {
+ LogPrintf(LogALERT, "failed to allocate memory: %d\n", cnt);
+ ServerClose();
+ exit(1);
+ }
+ MemMap[type].count += cnt;
+ totalalloced += cnt;
+ bp->base = p;
+ bp->size = bp->cnt = cnt;
+ bp->type = type;
+ return (bp);
+}
+
+struct mbuf *
+mbfree(struct mbuf * bp)
+{
+ struct mbuf *nbp;
+
+ if (bp) {
+ nbp = bp->next;
+ MemMap[bp->type].count -= bp->size;
+ totalalloced -= bp->size;
+ free(bp->base);
+ free(bp);
+ return (nbp);
+ }
+ return (bp);
+}
+
+void
+pfree(struct mbuf * bp)
+{
+ while (bp)
+ bp = mbfree(bp);
+}
+
+struct mbuf *
+mbread(struct mbuf * bp, u_char * ptr, int len)
+{
+ int nb;
+
+ while (bp && len > 0) {
+ if (len > bp->cnt)
+ nb = bp->cnt;
+ else
+ nb = len;
+ memcpy(ptr, MBUF_CTOP(bp), nb);
+ ptr += nb;
+ bp->cnt -= nb;
+ len -= nb;
+ bp->offset += nb;
+ if (bp->cnt == 0) {
+#ifdef notdef
+ bp = bp->next;
+#else
+ bp = mbfree(bp);
+#endif
+ }
+ }
+ return (bp);
+}
+
+void
+mbwrite(struct mbuf * bp, u_char * ptr, int cnt)
+{
+ int plen;
+ int nb;
+
+ plen = plength(bp);
+ if (plen < cnt)
+ cnt = plen;
+
+ while (cnt > 0) {
+ nb = (cnt < bp->cnt) ? cnt : bp->cnt;
+ memcpy(MBUF_CTOP(bp), ptr, nb);
+ cnt -= bp->cnt;
+ bp = bp->next;
+ }
+}
+
+int
+ShowMemMap()
+{
+ int i;
+
+ if (!VarTerm)
+ return 1;
+
+ for (i = 0; i <= MB_MAX; i += 2)
+ fprintf(VarTerm, "%d: %d %d: %d\n",
+ i, MemMap[i].count, i + 1, MemMap[i + 1].count);
+
+ return 0;
+}
+
+void
+LogMemory()
+{
+ LogPrintf(LogDEBUG, "LogMemory: mem alloced: %d\n", totalalloced);
+ LogPrintf(LogDEBUG, "LogMemory: 1: %d 2: %d 3: %d 4: %d\n",
+ MemMap[1].count, MemMap[2].count, MemMap[3].count, MemMap[4].count);
+ LogPrintf(LogDEBUG, "LogMemory: 5: %d 6: %d 7: %d 8: %d\n",
+ MemMap[5].count, MemMap[6].count, MemMap[7].count, MemMap[8].count);
+}
diff --git a/usr.sbin/ppp/mbuf.h b/usr.sbin/ppp/mbuf.h
new file mode 100644
index 0000000..b7a246d
--- /dev/null
+++ b/usr.sbin/ppp/mbuf.h
@@ -0,0 +1,63 @@
+/*
+ * Written by Toshiharu OHNO (tony-o@iij.ad.jp)
+ *
+ * Copyright (C) 1993, Internet Initiative Japan, Inc. All rights reserverd.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the Internet Initiative Japan. The name of the
+ * IIJ may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: mbuf.h,v 1.7 1997/10/26 01:03:18 brian Exp $
+ *
+ * TODO:
+ */
+
+struct mbuf {
+ u_char *base; /* pointer to top of buffer space */
+ short size; /* size allocated from base */
+ short offset; /* offset to start position */
+ short cnt; /* available byte count in buffer */
+ short type;
+ struct mbuf *next; /* link to next mbuf */
+ struct mbuf *pnext; /* link to next packet */
+};
+
+struct mqueue {
+ struct mbuf *top;
+ struct mbuf *last;
+ int qlen;
+};
+
+#define NULLBUFF ((struct mbuf *)0)
+
+#define MBUF_CTOP(bp) (bp->base + bp->offset)
+
+#define MB_ASYNC 1
+#define MB_FSM 2
+#define MB_HDLCOUT 3
+#define MB_IPIN 4
+#define MB_ECHO 5
+#define MB_LQR 6
+#define MB_MODEM 7
+#define MB_VJCOMP 8
+#define MB_LOG 9
+#define MB_IPQ 10
+#define MB_MAX MB_IPQ
+
+extern int plength(struct mbuf *);
+extern struct mbuf *mballoc(int, int);
+extern struct mbuf *mbfree(struct mbuf *);
+extern void pfree(struct mbuf *);
+extern void mbwrite(struct mbuf *, u_char *, int);
+extern struct mbuf *mbread(struct mbuf *, u_char *, int);
+extern void DumpBp(struct mbuf *);
+extern void LogMemory(void);
+extern int ShowMemMap(void);
diff --git a/usr.sbin/ppp/modem.c b/usr.sbin/ppp/modem.c
new file mode 100644
index 0000000..2a5ca62
--- /dev/null
+++ b/usr.sbin/ppp/modem.c
@@ -0,0 +1,962 @@
+/*
+ * PPP Modem handling module
+ *
+ * Written by Toshiharu OHNO (tony-o@iij.ad.jp)
+ *
+ * Copyright (C) 1993, Internet Initiative Japan, Inc. All rights reserverd.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the Internet Initiative Japan, Inc. The name of the
+ * IIJ may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: modem.c,v 1.63 1997/11/09 06:22:44 brian Exp $
+ *
+ * TODO:
+ */
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/tty.h>
+#include <termios.h>
+#include <time.h>
+#include <unistd.h>
+#include <utmp.h>
+
+#include "mbuf.h"
+#include "log.h"
+#include "defs.h"
+#include "id.h"
+#include "timer.h"
+#include "fsm.h"
+#include "hdlc.h"
+#include "lcp.h"
+#include "ip.h"
+#include "modem.h"
+#include "loadalias.h"
+#include "command.h"
+#include "vars.h"
+#include "main.h"
+#include "chat.h"
+#ifdef __OpenBSD__
+#include <util.h>
+#else
+#include <libutil.h>
+#endif
+
+#ifndef O_NONBLOCK
+#ifdef O_NDELAY
+#define O_NONBLOCK O_NDELAY
+#endif
+#endif
+
+static int mbits; /* Current DCD status */
+static int connect_count;
+static struct pppTimer ModemTimer;
+
+#define Online (mbits & TIOCM_CD)
+
+static struct mbuf *modemout;
+static struct mqueue OutputQueues[PRI_LINK + 1];
+static int dev_is_modem;
+
+static void CloseLogicalModem(void);
+
+void
+Enqueue(struct mqueue * queue, struct mbuf * bp)
+{
+ if (queue->last) {
+ queue->last->pnext = bp;
+ queue->last = bp;
+ } else
+ queue->last = queue->top = bp;
+ queue->qlen++;
+ LogPrintf(LogDEBUG, "Enqueue: len = %d\n", queue->qlen);
+}
+
+struct mbuf *
+Dequeue(struct mqueue * queue)
+{
+ struct mbuf *bp;
+
+ LogPrintf(LogDEBUG, "Dequeue: len = %d\n", queue->qlen);
+ bp = queue->top;
+ if (bp) {
+ queue->top = queue->top->pnext;
+ queue->qlen--;
+ if (queue->top == NULL) {
+ queue->last = queue->top;
+ if (queue->qlen)
+ LogPrintf(LogDEBUG, "Dequeue: Not zero (%d)!!!\n", queue->qlen);
+ }
+ }
+ return (bp);
+}
+
+static struct speeds {
+ int nspeed;
+ speed_t speed;
+} speeds[] = {
+
+#ifdef B50
+ {
+ 50, B50,
+ },
+#endif
+#ifdef B75
+ {
+ 75, B75,
+ },
+#endif
+#ifdef B110
+ {
+ 110, B110,
+ },
+#endif
+#ifdef B134
+ {
+ 134, B134,
+ },
+#endif
+#ifdef B150
+ {
+ 150, B150,
+ },
+#endif
+#ifdef B200
+ {
+ 200, B200,
+ },
+#endif
+#ifdef B300
+ {
+ 300, B300,
+ },
+#endif
+#ifdef B600
+ {
+ 600, B600,
+ },
+#endif
+#ifdef B1200
+ {
+ 1200, B1200,
+ },
+#endif
+#ifdef B1800
+ {
+ 1800, B1800,
+ },
+#endif
+#ifdef B2400
+ {
+ 2400, B2400,
+ },
+#endif
+#ifdef B4800
+ {
+ 4800, B4800,
+ },
+#endif
+#ifdef B9600
+ {
+ 9600, B9600,
+ },
+#endif
+#ifdef B19200
+ {
+ 19200, B19200,
+ },
+#endif
+#ifdef B38400
+ {
+ 38400, B38400,
+ },
+#endif
+#ifndef _POSIX_SOURCE
+#ifdef B7200
+ {
+ 7200, B7200,
+ },
+#endif
+#ifdef B14400
+ {
+ 14400, B14400,
+ },
+#endif
+#ifdef B28800
+ {
+ 28800, B28800,
+ },
+#endif
+#ifdef B57600
+ {
+ 57600, B57600,
+ },
+#endif
+#ifdef B76800
+ {
+ 76800, B76800,
+ },
+#endif
+#ifdef B115200
+ {
+ 115200, B115200,
+ },
+#endif
+#ifdef B230400
+ {
+ 230400, B230400,
+ },
+#endif
+#ifdef EXTA
+ {
+ 19200, EXTA,
+ },
+#endif
+#ifdef EXTB
+ {
+ 38400, EXTB,
+ },
+#endif
+#endif /* _POSIX_SOURCE */
+ {
+ 0, 0
+ }
+};
+
+static int
+SpeedToInt(speed_t speed)
+{
+ struct speeds *sp;
+
+ for (sp = speeds; sp->nspeed; sp++) {
+ if (sp->speed == speed) {
+ return (sp->nspeed);
+ }
+ }
+ return 0;
+}
+
+speed_t
+IntToSpeed(int nspeed)
+{
+ struct speeds *sp;
+
+ for (sp = speeds; sp->nspeed; sp++) {
+ if (sp->nspeed == nspeed) {
+ return (sp->speed);
+ }
+ }
+ return B0;
+}
+
+static time_t uptime;
+u_long OctetsIn, OctetsOut;
+
+void
+DownConnection()
+{
+ LogPrintf(LogPHASE, "Disconnected!\n");
+ LcpDown();
+}
+
+/*
+ * ModemTimeout() watches DCD signal and notifies if it's status is changed.
+ *
+ */
+void
+ModemTimeout()
+{
+ int ombits = mbits;
+ int change;
+
+ StopTimer(&ModemTimer);
+ StartTimer(&ModemTimer);
+
+ if (dev_is_modem) {
+ if (modem >= 0) {
+ if (ioctl(modem, TIOCMGET, &mbits) < 0) {
+ LogPrintf(LogPHASE, "ioctl error (%s)!\n", strerror(errno));
+ DownConnection();
+ return;
+ }
+ } else
+ mbits = 0;
+ change = ombits ^ mbits;
+ if (change & TIOCM_CD) {
+ if (Online) {
+ LogPrintf(LogDEBUG, "ModemTimeout: offline -> online\n");
+ /*
+ * In dedicated mode, start packet mode immediate after we detected
+ * carrier.
+ */
+ if (mode & MODE_DEDICATED)
+ PacketMode();
+ } else {
+ LogPrintf(LogDEBUG, "ModemTimeout: online -> offline\n");
+ reconnect(RECON_TRUE);
+ DownConnection();
+ }
+ }
+ else
+ LogPrintf(LogDEBUG, "ModemTimeout: Still %sline\n",
+ Online ? "on" : "off");
+ } else if (!Online) {
+ /* mbits was set to zero in OpenModem() */
+ mbits = TIOCM_CD;
+ }
+}
+
+static void
+StartModemTimer()
+{
+ StopTimer(&ModemTimer);
+ ModemTimer.state = TIMER_STOPPED;
+ ModemTimer.load = SECTICKS;
+ ModemTimer.func = ModemTimeout;
+ LogPrintf(LogDEBUG, "ModemTimer using ModemTimeout() - %p\n", ModemTimeout);
+ StartTimer(&ModemTimer);
+}
+
+struct parity {
+ char *name;
+ 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(char *str)
+{
+ struct parity *pp;
+
+ for (pp = validparity; pp->name; pp++) {
+ if (strcasecmp(pp->name, str) == 0 ||
+ strcasecmp(pp->name1, str) == 0) {
+ return VarParity = pp->set;
+ }
+ }
+ return (-1);
+}
+
+int
+ChangeParity(char *str)
+{
+ struct termios rstio;
+ int val;
+
+ val = GetParityValue(str);
+ if (val > 0) {
+ VarParity = val;
+ tcgetattr(modem, &rstio);
+ rstio.c_cflag &= ~(CSIZE | PARODD | PARENB);
+ rstio.c_cflag |= val;
+ tcsetattr(modem, TCSADRAIN, &rstio);
+ return 0;
+ }
+ LogPrintf(LogWARN, "ChangeParity: %s: Invalid parity\n", str);
+ return -1;
+}
+
+static int
+OpenConnection(char *host, char *port)
+{
+ struct sockaddr_in dest;
+ int sock;
+ struct hostent *hp;
+ struct servent *sp;
+
+ dest.sin_family = AF_INET;
+ dest.sin_addr.s_addr = inet_addr(host);
+ if (dest.sin_addr.s_addr == INADDR_NONE) {
+ hp = gethostbyname(host);
+ if (hp) {
+ memcpy(&dest.sin_addr.s_addr, hp->h_addr_list[0], 4);
+ } else {
+ LogPrintf(LogWARN, "OpenConnection: unknown host: %s\n", host);
+ return (-1);
+ }
+ }
+ dest.sin_port = htons(atoi(port));
+ if (dest.sin_port == 0) {
+ sp = getservbyname(port, "tcp");
+ if (sp) {
+ dest.sin_port = sp->s_port;
+ } else {
+ LogPrintf(LogWARN, "OpenConnection: unknown service: %s\n", port);
+ return (-1);
+ }
+ }
+ LogPrintf(LogPHASE, "Connecting to %s:%s\n", host, port);
+
+ sock = socket(PF_INET, SOCK_STREAM, 0);
+ if (sock < 0) {
+ return (sock);
+ }
+ if (connect(sock, (struct sockaddr *) & dest, sizeof(dest)) < 0) {
+ LogPrintf(LogWARN, "OpenConnection: connection failed.\n");
+ return (-1);
+ }
+ LogPrintf(LogDEBUG, "OpenConnection: modem fd is %d.\n", sock);
+ return (sock);
+}
+
+static char fn[MAXPATHLEN];
+
+static int
+LockModem()
+{
+ int res;
+ FILE *lockfile;
+
+ if (*VarDevice != '/')
+ return 0;
+
+ if (!(mode & MODE_DIRECT) && (res = ID0uu_lock(VarBaseDevice)) != UU_LOCK_OK) {
+ if (res == UU_LOCK_INUSE)
+ LogPrintf(LogPHASE, "Modem %s is in use\n", VarDevice);
+ else
+ LogPrintf(LogPHASE, "Modem %s is in use: uu_lock: %s\n",
+ VarDevice, uu_lockerr(res));
+ return (-1);
+ }
+
+ snprintf(fn, sizeof fn, "%s%s.if", _PATH_VARRUN, VarBaseDevice);
+ lockfile = ID0fopen(fn, "w");
+ if (lockfile != NULL) {
+ fprintf(lockfile, "tun%d\n", tunno);
+ fclose(lockfile);
+ } else
+ LogPrintf(LogALERT, "Warning: Can't create %s: %s\n", fn, strerror(errno));
+
+ return 0;
+}
+
+static void
+UnlockModem()
+{
+ if (*VarDevice != '/')
+ return;
+
+ snprintf(fn, sizeof fn, "%s%s.if", _PATH_VARRUN, VarBaseDevice);
+ if (ID0unlink(fn) == -1)
+ LogPrintf(LogALERT, "Warning: Can't remove %s: %s\n", fn, strerror(errno));
+
+ if (!(mode & MODE_DIRECT) && ID0uu_unlock(VarBaseDevice) == -1)
+ LogPrintf(LogALERT, "Warning: Can't uu_unlock %s\n", fn);
+}
+
+static void
+HaveModem()
+{
+ time(&uptime);
+ OctetsIn = OctetsOut = 0;
+ connect_count++;
+ LogPrintf(LogPHASE, "Connected!\n");
+}
+
+static struct termios modemios;
+
+int
+OpenModem()
+{
+ struct termios rstio;
+ int oldflag;
+ char *host, *cp, *port;
+
+ if (modem >= 0)
+ LogPrintf(LogDEBUG, "OpenModem: Modem is already open!\n");
+ /* We're going back into "term" mode */
+ else if (mode & MODE_DIRECT) {
+ HaveModem();
+ if (isatty(0)) {
+ LogPrintf(LogDEBUG, "OpenModem(direct): Modem is a tty\n");
+ cp = ttyname(0);
+ SetVariable(0, 1, &cp, VAR_DEVICE);
+ if (LockModem() == -1) {
+ close(0);
+ return -1;
+ }
+ modem = 0;
+ } else {
+ LogPrintf(LogDEBUG, "OpenModem(direct): Modem is not a tty\n");
+ SetVariable(0, 0, 0, VAR_DEVICE);
+ /* We don't call ModemTimeout() with this type of connection */
+ return modem = 0;
+ }
+ } else {
+ if (strncmp(VarDevice, "/dev/", 5) == 0) {
+ if (LockModem() == -1)
+ return (-1);
+ modem = ID0open(VarDevice, O_RDWR | O_NONBLOCK);
+ if (modem < 0) {
+ LogPrintf(LogERROR, "OpenModem failed: %s: %s\n", VarDevice,
+ strerror(errno));
+ UnlockModem();
+ return (-1);
+ }
+ HaveModem();
+ LogPrintf(LogDEBUG, "OpenModem: Modem is %s\n", VarDevice);
+ } else {
+ /* PPP over TCP */
+ cp = strchr(VarDevice, ':');
+ if (cp) {
+ *cp = 0;
+ host = VarDevice;
+ port = cp + 1;
+ if (*host && *port) {
+ modem = OpenConnection(host, port);
+ *cp = ':'; /* Don't destroy VarDevice */
+ if (modem < 0)
+ return (-1);
+ HaveModem();
+ LogPrintf(LogDEBUG, "OpenModem: Modem is socket %s\n", VarDevice);
+ } else {
+ *cp = ':'; /* Don't destroy VarDevice */
+ LogPrintf(LogERROR, "Invalid host:port: \"%s\"\n", VarDevice);
+ return (-1);
+ }
+ } else {
+ LogPrintf(LogERROR,
+ "Device (%s) must be in /dev or be a host:port pair\n",
+ VarDevice);
+ return (-1);
+ }
+ }
+ }
+
+ /*
+ * If we are working on tty device, change it's mode into the one desired
+ * for further operation. In this implementation, we assume that modem is
+ * configuted to use CTS/RTS flow control.
+ */
+ mbits = 0;
+ dev_is_modem = isatty(modem) || DEV_IS_SYNC;
+ if (DEV_IS_SYNC)
+ nointr_sleep(1);
+ if (dev_is_modem && !DEV_IS_SYNC) {
+ tcgetattr(modem, &rstio);
+ modemios = rstio;
+ LogPrintf(LogDEBUG, "OpenModem: modem = %d\n", modem);
+ LogPrintf(LogDEBUG, "OpenModem: modem (get): iflag = %x, oflag = %x,"
+ " cflag = %x\n", rstio.c_iflag, rstio.c_oflag, rstio.c_cflag);
+ cfmakeraw(&rstio);
+ if (VarCtsRts)
+ rstio.c_cflag |= CLOCAL | CCTS_OFLOW | CRTS_IFLOW;
+ else {
+ rstio.c_cflag |= CLOCAL;
+ rstio.c_iflag |= IXOFF;
+ }
+ rstio.c_iflag |= IXON;
+ if (!(mode & MODE_DEDICATED))
+ rstio.c_cflag |= HUPCL;
+ if ((mode & MODE_DIRECT) == 0) {
+
+ /*
+ * If we are working as direct mode, don't change tty speed.
+ */
+ rstio.c_cflag &= ~(CSIZE | PARODD | PARENB);
+ rstio.c_cflag |= VarParity;
+ if (cfsetspeed(&rstio, IntToSpeed(VarSpeed)) == -1) {
+ LogPrintf(LogWARN, "Unable to set modem speed (modem %d to %d)\n",
+ modem, VarSpeed);
+ }
+ }
+ tcsetattr(modem, TCSADRAIN, &rstio);
+ LogPrintf(LogDEBUG, "modem (put): iflag = %x, oflag = %x, cflag = %x\n",
+ rstio.c_iflag, rstio.c_oflag, rstio.c_cflag);
+
+ if (!(mode & MODE_DIRECT))
+ if (ioctl(modem, TIOCMGET, &mbits)) {
+ LogPrintf(LogERROR, "OpenModem: Cannot get modem status: %s\n",
+ strerror(errno));
+ uptime = 0;
+ CloseLogicalModem();
+ return (-1);
+ }
+ LogPrintf(LogDEBUG, "OpenModem: modem control = %o\n", mbits);
+
+ oldflag = fcntl(modem, F_GETFL, 0);
+ if (oldflag < 0) {
+ LogPrintf(LogERROR, "OpenModem: Cannot get modem flags: %s\n",
+ strerror(errno));
+ uptime = 0;
+ CloseLogicalModem();
+ return (-1);
+ }
+ (void) fcntl(modem, F_SETFL, oldflag & ~O_NONBLOCK);
+ }
+ StartModemTimer();
+
+ return (modem);
+}
+
+int
+ModemSpeed()
+{
+ struct termios rstio;
+
+ tcgetattr(modem, &rstio);
+ return (SpeedToInt(cfgetispeed(&rstio)));
+}
+
+/*
+ * Put modem tty line into raw mode which is necessary in packet mode operation
+ */
+int
+RawModem()
+{
+ struct termios rstio;
+ int oldflag;
+
+ if (!isatty(modem) || DEV_IS_SYNC)
+ return (0);
+ if (!(mode & MODE_DIRECT) && modem >= 0 && !Online) {
+ LogPrintf(LogDEBUG, "RawModem: mode = %d, modem = %d, mbits = %x\n", mode, modem, mbits);
+ }
+ tcgetattr(modem, &rstio);
+ cfmakeraw(&rstio);
+ if (VarCtsRts)
+ rstio.c_cflag |= CLOCAL | CCTS_OFLOW | CRTS_IFLOW;
+ else
+ rstio.c_cflag |= CLOCAL;
+
+ if (!(mode & MODE_DEDICATED))
+ rstio.c_cflag |= HUPCL;
+ tcsetattr(modem, TCSADRAIN, &rstio);
+ oldflag = fcntl(modem, F_GETFL, 0);
+ if (oldflag < 0)
+ return (-1);
+ (void) fcntl(modem, F_SETFL, oldflag | O_NONBLOCK);
+ return (0);
+}
+
+static void
+UnrawModem()
+{
+ int oldflag;
+
+ if (isatty(modem) && !DEV_IS_SYNC) {
+ tcsetattr(modem, TCSAFLUSH, &modemios);
+ oldflag = fcntl(modem, F_GETFL, 0);
+ if (oldflag < 0)
+ return;
+ (void) fcntl(modem, F_SETFL, oldflag & ~O_NONBLOCK);
+ }
+}
+
+void ModemAddInOctets(int n)
+{
+ OctetsIn += n;
+}
+
+void ModemAddOutOctets(int n)
+{
+ OctetsOut += n;
+}
+
+static void
+ClosePhysicalModem()
+{
+ close(modem);
+ if (uptime) {
+ LogPrintf(LogPHASE, "Connect time: %d secs\n", time(NULL) - uptime);
+ LogPrintf(LogPHASE, "Modem: %d octets in, %d octets out\n",
+ OctetsIn, OctetsOut);
+ OctetsIn = OctetsOut = 0;
+ uptime = 0;
+ }
+ modem = -1; /* Mark modem as closed */
+}
+
+void
+HangupModem(int flag)
+{
+ struct termios tio;
+
+ LogPrintf(LogDEBUG, "Hangup modem (%s), uptime %ld\n",
+ modem >= 0 ? "open" : "closed", (long)uptime);
+ StopTimer(&ModemTimer);
+
+ if (modem < 0)
+ return;
+
+ if (TermMode) {
+ LogPrintf(LogDEBUG, "HangupModem: Not in 'term' mode\n");
+ return;
+ }
+
+ if (!isatty(modem)) {
+ mbits &= ~TIOCM_DTR;
+ ClosePhysicalModem();
+ return;
+ }
+
+ if (modem >= 0 && Online) {
+ mbits &= ~TIOCM_DTR;
+ tcgetattr(modem, &tio);
+ if (cfsetspeed(&tio, B0) == -1) {
+ LogPrintf(LogWARN, "Unable to set modem to speed 0\n");
+ }
+ tcsetattr(modem, TCSANOW, &tio);
+ nointr_sleep(1);
+ }
+
+ if (modem >= 0) {
+ char ScriptBuffer[SCRIPT_LEN];
+
+ strcpy(ScriptBuffer, VarHangupScript); /* arrays are the same size */
+ if (flag || !(mode & MODE_DEDICATED)) {
+ DoChat(ScriptBuffer);
+ tcflush(modem, TCIOFLUSH);
+ UnrawModem();
+ CloseLogicalModem();
+ } else {
+ /*
+ * If we are working as dedicated mode, never close it until we are
+ * directed to quit program.
+ */
+ mbits |= TIOCM_DTR;
+ ioctl(modem, TIOCMSET, &mbits);
+ DoChat(ScriptBuffer);
+ }
+ }
+}
+
+static void
+CloseLogicalModem()
+{
+ if (modem >= 0) {
+ ClosePhysicalModem();
+ if (Utmp) {
+ struct utmp ut;
+ strncpy(ut.ut_line, VarBaseDevice, sizeof(ut.ut_line)-1);
+ ut.ut_line[sizeof(ut.ut_line)-1] = '\0';
+ if (logout(ut.ut_line))
+ logwtmp(ut.ut_line, "", "");
+ else
+ LogPrintf(LogERROR, "CloseLogicalModem: No longer logged in on %s\n",
+ ut.ut_line);
+ Utmp = 0;
+ }
+ UnlockModem();
+ }
+}
+
+/*
+ * Write to modem. Actualy, requested packets are queued, and goes out
+ * to the line when ModemStartOutput() is called.
+ */
+void
+WriteModem(int pri, char *ptr, int count)
+{
+ struct mbuf *bp;
+
+ bp = mballoc(count, MB_MODEM);
+ memcpy(MBUF_CTOP(bp), ptr, count);
+
+ /*
+ * Should be NORMAL and LINK only. All IP frames get here marked NORMAL.
+ */
+ Enqueue(&OutputQueues[pri], bp);
+}
+
+void
+ModemOutput(int pri, struct mbuf * bp)
+{
+ struct mbuf *wp;
+ int len;
+
+ len = plength(bp);
+ wp = mballoc(len, MB_MODEM);
+ mbread(bp, MBUF_CTOP(wp), len);
+ Enqueue(&OutputQueues[pri], wp);
+ ModemStartOutput(modem);
+}
+
+int
+ModemQlen()
+{
+ struct mqueue *queue;
+ int len = 0;
+ int i;
+
+ for (i = PRI_NORMAL; i <= PRI_LINK; i++) {
+ queue = &OutputQueues[i];
+ len += queue->qlen;
+ }
+ return (len);
+
+}
+
+void
+ModemStartOutput(int fd)
+{
+ struct mqueue *queue;
+ int nb, nw;
+ int i;
+
+ if (modemout == NULL && ModemQlen() == 0)
+ IpStartOutput();
+ if (modemout == NULL) {
+ i = PRI_LINK;
+ for (queue = &OutputQueues[PRI_LINK]; queue >= OutputQueues; queue--) {
+ if (queue->top) {
+ modemout = Dequeue(queue);
+ if (LogIsKept(LogDEBUG)) {
+ if (i > PRI_NORMAL) {
+ struct mqueue *q;
+
+ q = &OutputQueues[0];
+ LogPrintf(LogDEBUG, "ModemStartOutput: Output from queue %d,"
+ " normal has %d\n", i, q->qlen);
+ }
+ LogPrintf(LogDEBUG, "ModemStartOutput: Dequeued %d\n", i);
+ }
+ break;
+ }
+ i--;
+ }
+ }
+ if (modemout) {
+ nb = modemout->cnt;
+ if (nb > 1600)
+ nb = 1600;
+ nw = write(fd, MBUF_CTOP(modemout), nb);
+ LogPrintf(LogDEBUG, "ModemStartOutput: wrote: %d(%d)\n", nw, nb);
+ LogDumpBuff(LogDEBUG, "ModemStartOutput: modem write",
+ MBUF_CTOP(modemout), nb);
+ if (nw > 0) {
+ modemout->cnt -= nw;
+ modemout->offset += nw;
+ if (modemout->cnt == 0) {
+ modemout = mbfree(modemout);
+ LogPrintf(LogDEBUG, "ModemStartOutput: mbfree\n");
+ }
+ } else if (nw < 0) {
+ if (errno != EAGAIN) {
+ LogPrintf(LogERROR, "modem write (%d): %s\n", modem, strerror(errno));
+ reconnect(RECON_TRUE);
+ DownConnection();
+ }
+ }
+ }
+}
+
+int
+DialModem()
+{
+ char ScriptBuffer[SCRIPT_LEN];
+ int excode;
+
+ strncpy(ScriptBuffer, VarDialScript, sizeof(ScriptBuffer) - 1);
+ ScriptBuffer[sizeof(ScriptBuffer) - 1] = '\0';
+ if ((excode = DoChat(ScriptBuffer)) > 0) {
+ if (VarTerm)
+ fprintf(VarTerm, "dial OK!\n");
+ strncpy(ScriptBuffer, VarLoginScript, sizeof(ScriptBuffer) - 1);
+ if ((excode = DoChat(ScriptBuffer)) > 0) {
+ VarAltPhone = NULL;
+ if (VarTerm)
+ fprintf(VarTerm, "login OK!\n");
+ return EX_DONE;
+ } else if (excode == -1)
+ excode = EX_SIG;
+ else {
+ LogPrintf(LogWARN, "DialModem: login failed.\n");
+ excode = EX_NOLOGIN;
+ }
+ ModemTimeout(); /* Dummy call to check modem status */
+ } else if (excode == -1)
+ excode = EX_SIG;
+ else {
+ LogPrintf(LogWARN, "DialModem: dial failed.\n");
+ excode = EX_NODIAL;
+ }
+ HangupModem(0);
+ return (excode);
+}
+
+int
+ShowModemStatus()
+{
+ char *dev;
+#ifdef TIOCOUTQ
+ int nb;
+#endif
+
+ if (!VarTerm)
+ return 1;
+
+ dev = *VarDevice ? VarDevice : "network";
+
+ fprintf(VarTerm, "device: %s speed: ", dev);
+ if (DEV_IS_SYNC)
+ fprintf(VarTerm, "sync\n");
+ else
+ fprintf(VarTerm, "%d\n", VarSpeed);
+
+ switch (VarParity & CSIZE) {
+ case CS7:
+ fprintf(VarTerm, "cs7, ");
+ break;
+ case CS8:
+ fprintf(VarTerm, "cs8, ");
+ break;
+ }
+ if (VarParity & PARENB) {
+ if (VarParity & PARODD)
+ fprintf(VarTerm, "odd parity, ");
+ else
+ fprintf(VarTerm, "even parity, ");
+ } else
+ fprintf(VarTerm, "no parity, ");
+
+ fprintf(VarTerm, "CTS/RTS %s.\n", (VarCtsRts ? "on" : "off"));
+
+ if (LogIsKept(LogDEBUG))
+ fprintf(VarTerm, "fd = %d, modem control = %o\n", modem, mbits);
+ fprintf(VarTerm, "connect count: %d\n", connect_count);
+#ifdef TIOCOUTQ
+ if (modem >= 0)
+ if (ioctl(modem, TIOCOUTQ, &nb) > 0)
+ fprintf(VarTerm, "outq: %d\n", nb);
+ else
+ fprintf(VarTerm, "outq: ioctl probe failed: %s\n", strerror(errno));
+#endif
+ fprintf(VarTerm, "outqlen: %d\n", ModemQlen());
+ fprintf(VarTerm, "DialScript = %s\n", VarDialScript);
+ fprintf(VarTerm, "LoginScript = %s\n", VarLoginScript);
+ fprintf(VarTerm, "PhoneNumber(s) = %s\n", VarPhoneList);
+
+ return 0;
+}
diff --git a/usr.sbin/ppp/modem.h b/usr.sbin/ppp/modem.h
new file mode 100644
index 0000000..43541fa
--- /dev/null
+++ b/usr.sbin/ppp/modem.h
@@ -0,0 +1,42 @@
+/*
+ * Written by Toshiharu OHNO (tony-o@iij.ad.jp)
+ *
+ * Copyright (C) 1993, Internet Initiative Japan, Inc. All rights reserverd.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the Internet Initiative Japan. The name of the
+ * IIJ may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: modem.h,v 1.12 1997/10/29 01:19:45 brian Exp $
+ *
+ * TODO:
+ */
+
+extern int RawModem(void);
+extern void UpModem(int);
+extern void DownModem(int);
+extern void WriteModem(int, char *, int);
+extern void ModemStartOutput(int);
+extern int OpenModem(void);
+extern int ModemSpeed(void);
+extern int ModemQlen(void);
+extern int DialModem(void);
+extern speed_t IntToSpeed(int);
+extern void ModemTimeout(void);
+extern void DownConnection(void);
+extern void ModemOutput(int, struct mbuf *);
+extern int ChangeParity(char *);
+extern void HangupModem(int);
+extern int ShowModemStatus(void);
+extern void Enqueue(struct mqueue *, struct mbuf *);
+extern struct mbuf *Dequeue(struct mqueue *);
+extern void ModemAddInOctets(int);
+extern void ModemAddOutOctets(int);
diff --git a/usr.sbin/ppp/nat_cmd.c b/usr.sbin/ppp/nat_cmd.c
new file mode 100644
index 0000000..4b91898
--- /dev/null
+++ b/usr.sbin/ppp/nat_cmd.c
@@ -0,0 +1,190 @@
+/*
+ * $Id: $
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "mbuf.h"
+#include "log.h"
+#include "defs.h"
+#include "command.h"
+#include "loadalias.h"
+#include "vars.h"
+#include "alias_cmd.h"
+
+
+static int StrToAddr(char *, struct in_addr *);
+static int StrToPort(char *, u_short *, char *);
+static int StrToAddrAndPort(char *, struct in_addr *, u_short *, char *);
+
+
+int
+AliasRedirectPort(struct cmdtab *list, int argc, char **argv, void *param)
+{
+ if (!(mode & MODE_ALIAS)) {
+ if (VarTerm)
+ fprintf(VarTerm, "Alias not enabled\n");
+ } else if (argc == 3) {
+ char proto_constant;
+ char *proto;
+ u_short local_port;
+ u_short alias_port;
+ int error;
+ struct in_addr local_addr;
+ struct in_addr null_addr;
+ struct alias_link *link;
+
+ proto = argv[0];
+ if (strcmp(proto, "tcp") == 0) {
+ proto_constant = IPPROTO_TCP;
+ } else if (strcmp(proto, "udp") == 0) {
+ proto_constant = IPPROTO_UDP;
+ } else {
+ if (VarTerm) {
+ fprintf(VarTerm, "port redirect: protocol must be tcp or udp\n");
+ fprintf(VarTerm, "Usage: alias %s %s\n", list->name,
+ list->syntax);
+ }
+ return 1;
+ }
+
+ error = StrToAddrAndPort(argv[1], &local_addr, &local_port, proto);
+ if (error) {
+ if (VarTerm) {
+ fprintf(VarTerm, "port redirect: error reading local addr:port\n");
+ fprintf(VarTerm, "Usage: alias %s %s\n", list->name, list->syntax);
+ }
+ return 1;
+ }
+ error = StrToPort(argv[2], &alias_port, proto);
+ if (error) {
+ if (VarTerm) {
+ fprintf(VarTerm, "port redirect: error reading alias port\n");
+ fprintf(VarTerm, "Usage: alias %s %s\n", list->name, list->syntax);
+ }
+ return 1;
+ }
+ null_addr.s_addr = 0;
+
+ link = VarPacketAliasRedirectPort(local_addr, local_port,
+ null_addr, 0,
+ null_addr, alias_port,
+ proto_constant);
+
+ if (link == NULL && VarTerm)
+ fprintf(VarTerm, "port redirect: error returned by packed"
+ " aliasing engine (code=%d)\n", error);
+ } else if (VarTerm)
+ fprintf(VarTerm, "Usage: alias %s %s\n", list->name, list->syntax);
+
+ return 1;
+}
+
+
+int
+AliasRedirectAddr(struct cmdtab *list, int argc, char **argv, void *param)
+{
+ if (!(mode & MODE_ALIAS)) {
+ if (VarTerm)
+ fprintf(VarTerm, "alias not enabled\n");
+ } else if (argc == 2) {
+ int error;
+ struct in_addr local_addr;
+ struct in_addr alias_addr;
+ struct alias_link *link;
+
+ error = StrToAddr(argv[0], &local_addr);
+ if (error) {
+ if (VarTerm)
+ fprintf(VarTerm, "address redirect: invalid local address\n");
+ return 1;
+ }
+ error = StrToAddr(argv[1], &alias_addr);
+ if (error) {
+ if (VarTerm) {
+ fprintf(VarTerm, "address redirect: invalid alias address\n");
+ fprintf(VarTerm, "Usage: alias %s %s\n", list->name, list->syntax);
+ }
+ return 1;
+ }
+ link = VarPacketAliasRedirectAddr(local_addr, alias_addr);
+ if (link == NULL && VarTerm) {
+ fprintf(VarTerm, "address redirect: packet aliasing engine error\n");
+ fprintf(VarTerm, "Usage: alias %s %s\n", list->name, list->syntax);
+ }
+ } else if (VarTerm)
+ fprintf(VarTerm, "Usage: alias %s %s\n", list->name, list->syntax);
+
+ return 1;
+}
+
+
+static int
+StrToAddr(char *str, struct in_addr *addr)
+{
+ struct hostent *hp;
+
+ if (inet_aton(str, addr))
+ return 0;
+
+ hp = gethostbyname(str);
+ if (!hp) {
+ LogPrintf(LogWARN, "StrToAddr: Unknown host %s.\n", str);
+ return -1;
+ }
+ *addr = *((struct in_addr *) hp->h_addr);
+ return 0;
+}
+
+
+static int
+StrToPort(char *str, u_short *port, char *proto)
+{
+ int iport;
+ struct servent *sp;
+ char *end;
+
+ iport = strtol(str, &end, 10);
+ if (end != str) {
+ *port = htons(iport);
+ return 0;
+ }
+ sp = getservbyname(str, proto);
+ if (!sp) {
+ LogPrintf(LogWARN, "StrToAddr: Unknown port or service %s/%s.\n",
+ str, proto);
+ return -1;
+ }
+ *port = sp->s_port;
+ return 0;
+}
+
+
+int
+StrToAddrAndPort(char *str, struct in_addr *addr, u_short *port, char *proto)
+{
+ char *ptr;
+
+ ptr = strchr(str, ':');
+ if (!ptr) {
+ LogPrintf(LogWARN, "StrToAddrAndPort: %s is missing port number.\n",
+ str);
+ return -1;
+ }
+ *ptr = '\0';
+ ++ptr;
+
+ if (StrToAddr(str, addr) != 0)
+ return -1;
+
+ return StrToPort(ptr, port, proto);
+}
diff --git a/usr.sbin/ppp/nat_cmd.h b/usr.sbin/ppp/nat_cmd.h
new file mode 100644
index 0000000..49b1532
--- /dev/null
+++ b/usr.sbin/ppp/nat_cmd.h
@@ -0,0 +1,6 @@
+/*
+ * $Id: $
+ */
+
+extern int AliasRedirectPort(struct cmdtab *, int, char **, void *);
+extern int AliasRedirectAddr(struct cmdtab *, int, char **, void *);
diff --git a/usr.sbin/ppp/os.c b/usr.sbin/ppp/os.c
new file mode 100644
index 0000000..91d7435
--- /dev/null
+++ b/usr.sbin/ppp/os.c
@@ -0,0 +1,395 @@
+/*
+ * PPP OS Layer Interface Module
+ *
+ * Written by Toshiharu OHNO (tony-o@iij.ad.jp)
+ *
+ * Copyright (C) 1993, Internet Initiative Japan, Inc. All rights reserverd.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the Internet Initiative Japan, Inc. The name of the
+ * IIJ may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: os.c,v 1.30 1997/11/08 00:28:10 brian Exp $
+ *
+ */
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/select.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <net/if.h>
+#include <net/if_var.h>
+#include <net/if_tun.h>
+#include <net/route.h>
+#include <arpa/inet.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "mbuf.h"
+#include "log.h"
+#include "id.h"
+#include "defs.h"
+#include "timer.h"
+#include "fsm.h"
+#include "ipcp.h"
+#include "os.h"
+#include "loadalias.h"
+#include "command.h"
+#include "vars.h"
+#include "arp.h"
+#include "systems.h"
+#include "route.h"
+#include "ccp.h"
+#include "modem.h"
+
+char *IfDevName;
+
+static struct ifaliasreq ifra;
+static struct ifreq ifrq;
+static struct in_addr oldmine, oldhis;
+static int linkup;
+
+static int
+SetIpDevice(struct in_addr myaddr,
+ struct in_addr hisaddr,
+ struct in_addr netmask,
+ int updown)
+{
+ struct sockaddr_in *sock_in;
+ int s;
+ int changeaddr = 0;
+ u_long mask, addr;
+
+ s = ID0socket(AF_INET, SOCK_DGRAM, 0);
+ if (s < 0) {
+ LogPrintf(LogERROR, "SetIpDevice: socket(): %s\n", strerror(errno));
+ return (-1);
+ }
+ if (updown == 0) {
+ if (Enabled(ConfProxy))
+ cifproxyarp(s, oldhis.s_addr);
+ if (oldmine.s_addr == 0 && oldhis.s_addr == 0) {
+ close(s);
+ return (0);
+ }
+ memset(&ifra.ifra_addr, '\0', sizeof(ifra.ifra_addr));
+ memset(&ifra.ifra_broadaddr, '\0', sizeof(ifra.ifra_addr));
+ memset(&ifra.ifra_mask, '\0', sizeof(ifra.ifra_addr));
+ if (ID0ioctl(s, SIOCDIFADDR, &ifra) < 0) {
+ LogPrintf(LogERROR, "SetIpDevice: ioctl(SIOCDIFADDR): %s\n",
+ strerror(errno));
+ close(s);
+ return (-1);
+ }
+ oldmine.s_addr = oldhis.s_addr = 0;
+ } else {
+
+ /*
+ * If given addresses are alreay set, then ignore this request.
+ */
+ if (oldmine.s_addr == myaddr.s_addr && oldhis.s_addr == hisaddr.s_addr) {
+ close(s);
+ return (0);
+ }
+
+ /*
+ * If different address has been set, then delete it first.
+ */
+ if (oldmine.s_addr || oldhis.s_addr) {
+ changeaddr = 1;
+ }
+
+ /*
+ * Set interface address
+ */
+ sock_in = (struct sockaddr_in *) & (ifra.ifra_addr);
+ sock_in->sin_family = AF_INET;
+ sock_in->sin_addr = oldmine = myaddr;
+ sock_in->sin_len = sizeof(*sock_in);
+
+ /*
+ * Set destination address
+ */
+ sock_in = (struct sockaddr_in *) & (ifra.ifra_broadaddr);
+ sock_in->sin_family = AF_INET;
+ sock_in->sin_addr = oldhis = hisaddr;
+ sock_in->sin_len = sizeof(*sock_in);
+
+ /*
+ * */
+ addr = ntohl(myaddr.s_addr);
+ if (IN_CLASSA(addr))
+ mask = IN_CLASSA_NET;
+ else if (IN_CLASSB(addr))
+ mask = IN_CLASSB_NET;
+ else
+ mask = IN_CLASSC_NET;
+
+ /*
+ * if subnet mask is given, use it instead of class mask.
+ */
+ if (netmask.s_addr && (ntohl(netmask.s_addr) & mask) == mask)
+ mask = ntohl(netmask.s_addr);
+
+ sock_in = (struct sockaddr_in *) & (ifra.ifra_mask);
+ sock_in->sin_family = AF_INET;
+ sock_in->sin_addr.s_addr = htonl(mask);
+ sock_in->sin_len = sizeof(*sock_in);
+
+ if (changeaddr) {
+
+ /*
+ * Interface already exists. Just change the address.
+ */
+ memcpy(&ifrq.ifr_addr, &ifra.ifra_addr, sizeof(struct sockaddr));
+ if (ID0ioctl(s, SIOCSIFADDR, &ifra) < 0)
+ LogPrintf(LogERROR, "SetIpDevice: ioctl(SIFADDR): %s\n",
+ strerror(errno));
+ memcpy(&ifrq.ifr_dstaddr, &ifra.ifra_broadaddr, sizeof(struct sockaddr));
+ if (ID0ioctl(s, SIOCSIFDSTADDR, &ifrq) < 0)
+ LogPrintf(LogERROR, "SetIpDevice: ioctl(SIFDSTADDR): %s\n",
+ strerror(errno));
+#ifdef notdef
+ memcpy(&ifrq.ifr_broadaddr, &ifra.ifra_mask, sizeof(struct sockaddr));
+ if (ID0ioctl(s, SIOCSIFBRDADDR, &ifrq) < 0)
+ LogPrintf(LogERROR, "SetIpDevice: ioctl(SIFBRDADDR): %s\n",
+ strerror(errno));
+#endif
+ } else if (ID0ioctl(s, SIOCAIFADDR, &ifra) < 0) {
+ LogPrintf(LogERROR, "SetIpDevice: ioctl(SIOCAIFADDR): %s\n",
+ strerror(errno));
+ close(s);
+ return (-1);
+ }
+ if (Enabled(ConfProxy))
+ sifproxyarp(s, hisaddr.s_addr);
+ }
+ close(s);
+ return (0);
+}
+
+int
+OsSetIpaddress(struct in_addr myaddr,
+ struct in_addr hisaddr,
+ struct in_addr netmask)
+{
+ return (SetIpDevice(myaddr, hisaddr, netmask, 1));
+}
+
+static struct in_addr peer_addr;
+struct in_addr defaddr;
+
+void
+OsLinkup()
+{
+ char *s;
+
+ if (linkup == 0) {
+ reconnectState = RECON_UNKNOWN;
+ if (mode & MODE_BACKGROUND && BGFiledes[1] != -1) {
+ char c = EX_NORMAL;
+
+ if (write(BGFiledes[1], &c, 1) == 1)
+ LogPrintf(LogPHASE, "Parent notified of success.\n");
+ else
+ LogPrintf(LogPHASE, "Failed to notify parent of success.\n");
+ close(BGFiledes[1]);
+ BGFiledes[1] = -1;
+ }
+ peer_addr = IpcpInfo.his_ipaddr;
+ s = (char *) inet_ntoa(peer_addr);
+ if (LogIsKept(LogLINK))
+ LogPrintf(LogLINK, "OsLinkup: %s\n", s);
+ else
+ LogPrintf(LogLCP, "OsLinkup: %s\n", s);
+
+ if (SelectSystem(inet_ntoa(IpcpInfo.want_ipaddr), LINKUPFILE) < 0) {
+ if (dstsystem) {
+ if (SelectSystem(dstsystem, LINKUPFILE) < 0)
+ SelectSystem("MYADDR", LINKUPFILE);
+ } else
+ SelectSystem("MYADDR", LINKUPFILE);
+ }
+ linkup = 1;
+ }
+}
+
+int
+OsLinkIsUp()
+{
+ return linkup;
+}
+
+void
+OsLinkdown()
+{
+ char *s;
+ int Level;
+
+ if (linkup) {
+ s = (char *) inet_ntoa(peer_addr);
+ Level = LogIsKept(LogLINK) ? LogLINK : LogIPCP;
+ LogPrintf(Level, "OsLinkdown: %s\n", s);
+
+ FsmDown(&IpcpFsm); /* IPCP must come down */
+ FsmDown(&CcpFsm); /* CCP must come down */
+
+ if (!(mode & MODE_AUTO))
+ DeleteIfRoutes(0);
+ linkup = 0;
+ if (SelectSystem(s, LINKDOWNFILE) < 0) {
+ if (dstsystem) {
+ if (SelectSystem(dstsystem, LINKDOWNFILE) < 0)
+ SelectSystem("MYADDR", LINKDOWNFILE);
+ } else
+ SelectSystem("MYADDR", LINKDOWNFILE);
+ }
+ }
+}
+
+int
+OsInterfaceDown(int final)
+{
+ struct in_addr zeroaddr;
+ int s;
+
+ OsLinkdown();
+ if (!final && (mode & MODE_AUTO)) /* We still want interface alive */
+ return (0);
+ s = socket(AF_INET, SOCK_DGRAM, 0);
+ if (s < 0) {
+ LogPrintf(LogERROR, "OsInterfaceDown: socket: %s\n", strerror(errno));
+ return (-1);
+ }
+ ifrq.ifr_flags &= ~IFF_UP;
+ if (ID0ioctl(s, SIOCSIFFLAGS, &ifrq) < 0) {
+ LogPrintf(LogERROR, "OsInterfaceDown: ioctl(SIOCSIFFLAGS): %s\n",
+ strerror(errno));
+ close(s);
+ return (-1);
+ }
+ zeroaddr.s_addr = 0;
+ SetIpDevice(zeroaddr, zeroaddr, zeroaddr, 0);
+
+ close(s);
+ return (0);
+}
+
+void
+OsSetInterfaceParams(int type, int mtu, int speed)
+{
+ struct tuninfo info;
+
+ info.type = type;
+ info.mtu = mtu;
+ if (VarPrefMTU != 0 && VarPrefMTU < mtu)
+ info.mtu = VarPrefMTU;
+ info.baudrate = speed;
+ if (ioctl(tun_out, TUNSIFINFO, &info) < 0)
+ LogPrintf(LogERROR, "OsSetInterfaceParams: ioctl(TUNSIFINFO): %s\n",
+ strerror(errno));
+}
+
+/*
+ * Open tunnel device and returns its descriptor
+ */
+
+#define MAX_TUN 256
+/* MAX_TUN is set at an arbitrarily large value *
+ * as the loop aborts when it reaches the first *
+ * 'Device not configured' (ENXIO), or the third *
+ * 'No such file or directory' (ENOENT) error. */
+int
+OpenTunnel(int *ptun)
+{
+ int s;
+ char ifname[IFNAMSIZ];
+ static char devname[14]; /* sufficient room for "/dev/tun65535" */
+ unsigned unit, enoentcount = 0;
+ int err;
+
+ err = ENOENT;
+ for (unit = 0; unit <= MAX_TUN; unit++) {
+ snprintf(devname, sizeof(devname), "/dev/tun%d", unit);
+ tun_out = ID0open(devname, O_RDWR);
+ if (tun_out >= 0)
+ break;
+ if (errno == ENXIO) {
+ unit = MAX_TUN;
+ err = errno;
+ } else if (errno == ENOENT) {
+ enoentcount++;
+ if (enoentcount > 2)
+ unit = MAX_TUN;
+ } else
+ err = errno;
+ }
+ if (unit > MAX_TUN) {
+ if (VarTerm)
+ fprintf(VarTerm, "No tunnel device is available (%s).\n", strerror(err));
+ return -1;
+ }
+ *ptun = unit;
+
+ LogSetTun(unit);
+
+ /*
+ * At first, name the interface.
+ */
+ strncpy(ifname, devname + 5, IFNAMSIZ - 1);
+
+ memset(&ifra, '\0', sizeof(ifra));
+ memset(&ifrq, '\0', sizeof(ifrq));
+
+ strncpy(ifrq.ifr_name, ifname, IFNAMSIZ - 1);
+ strncpy(ifra.ifra_name, ifname, IFNAMSIZ - 1);
+
+ s = socket(AF_INET, SOCK_DGRAM, 0);
+ if (s < 0) {
+ LogPrintf(LogERROR, "OpenTunnel: socket(): %s\n", strerror(errno));
+ return (-1);
+ }
+
+ /*
+ * Now, bring up the interface.
+ */
+ if (ioctl(s, SIOCGIFFLAGS, &ifrq) < 0) {
+ LogPrintf(LogERROR, "OpenTunnel: ioctl(SIOCGIFFLAGS): %s\n",
+ strerror(errno));
+ close(s);
+ return (-1);
+ }
+ ifrq.ifr_flags |= IFF_UP;
+ if (ID0ioctl(s, SIOCSIFFLAGS, &ifrq) < 0) {
+ LogPrintf(LogERROR, "OpenTunnel: ioctl(SIOCSIFFLAGS): %s\n",
+ strerror(errno));
+ close(s);
+ return (-1);
+ }
+ tun_in = tun_out;
+ IfDevName = devname + 5;
+ if (GetIfIndex(IfDevName) < 0) {
+ LogPrintf(LogERROR, "OpenTunnel: Can't find ifindex.\n");
+ close(s);
+ return (-1);
+ }
+ if (VarTerm)
+ fprintf(VarTerm, "Using interface: %s\n", IfDevName);
+ LogPrintf(LogPHASE, "Using interface: %s\n", IfDevName);
+ close(s);
+ return (0);
+}
diff --git a/usr.sbin/ppp/os.h b/usr.sbin/ppp/os.h
new file mode 100644
index 0000000..d95ce0d
--- /dev/null
+++ b/usr.sbin/ppp/os.h
@@ -0,0 +1,33 @@
+/*
+ * Written by Toshiharu OHNO (tony-o@iij.ad.jp)
+ *
+ * Copyright (C) 1993, Internet Initiative Japan, Inc. All rights reserverd.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the Internet Initiative Japan. The name of the
+ * IIJ may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: os.h,v 1.9 1997/10/26 01:03:27 brian Exp $
+ *
+ * TODO:
+ */
+
+extern char *IfDevName;
+
+int OsSetIpaddress(struct in_addr, struct in_addr, struct in_addr);
+int OsInterfaceDown(int);
+void OsSetInterfaceParams(int, int, int);
+int OpenTunnel(int *);
+void OsLinkup(void);
+int OsLinkIsUp(void);
+void OsLinkdown(void);
+void OsSetRoute(int, struct in_addr, struct in_addr, struct in_addr);
+void DeleteIfRoutes(int);
diff --git a/usr.sbin/ppp/pap.c b/usr.sbin/ppp/pap.c
new file mode 100644
index 0000000..7b24293
--- /dev/null
+++ b/usr.sbin/ppp/pap.c
@@ -0,0 +1,218 @@
+/*
+ * PPP PAP Module
+ *
+ * Written by Toshiharu OHNO (tony-o@iij.ad.jp)
+ *
+ * Copyright (C) 1993-94, Internet Initiative Japan, Inc.
+ * All rights reserverd.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the Internet Initiative Japan, Inc. The name of the
+ * IIJ may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: pap.c,v 1.17 1997/10/16 23:55:19 brian Exp $
+ *
+ * TODO:
+ */
+#include <sys/param.h>
+#include <netinet/in.h>
+
+#include <pwd.h>
+#include <stdio.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#ifdef __OpenBSD__
+#include <util.h>
+#else
+#include <libutil.h>
+#endif
+#include <utmp.h>
+
+#include "mbuf.h"
+#include "log.h"
+#include "defs.h"
+#include "timer.h"
+#include "fsm.h"
+#include "lcp.h"
+#include "pap.h"
+#include "loadalias.h"
+#include "command.h"
+#include "vars.h"
+#include "hdlc.h"
+#include "lcpproto.h"
+#include "phase.h"
+#include "auth.h"
+
+static char *papcodes[] = {
+ "???", "REQUEST", "ACK", "NAK"
+};
+
+static void
+SendPapChallenge(int papid)
+{
+ struct fsmheader lh;
+ struct mbuf *bp;
+ u_char *cp;
+ int namelen, keylen, plen;
+
+ namelen = strlen(VarAuthName);
+ keylen = strlen(VarAuthKey);
+ plen = namelen + keylen + 2;
+ LogPrintf(LogDEBUG, "SendPapChallenge: namelen = %d, keylen = %d\n",
+ namelen, keylen);
+ if (LogIsKept(LogDEBUG))
+ LogPrintf(LogPHASE, "PAP: %s (%s)\n", VarAuthName, VarAuthKey);
+ else
+ LogPrintf(LogPHASE, "PAP: %s\n", VarAuthName);
+ lh.code = PAP_REQUEST;
+ lh.id = papid;
+ lh.length = htons(plen + sizeof(struct fsmheader));
+ bp = mballoc(plen + sizeof(struct fsmheader), MB_FSM);
+ memcpy(MBUF_CTOP(bp), &lh, sizeof(struct fsmheader));
+ cp = MBUF_CTOP(bp) + sizeof(struct fsmheader);
+ *cp++ = namelen;
+ memcpy(cp, VarAuthName, namelen);
+ cp += namelen;
+ *cp++ = keylen;
+ memcpy(cp, VarAuthKey, keylen);
+
+ HdlcOutput(PRI_LINK, PROTO_PAP, bp);
+}
+
+struct authinfo AuthPapInfo = {
+ SendPapChallenge,
+};
+
+static void
+SendPapCode(int id, int code, char *message)
+{
+ struct fsmheader lh;
+ struct mbuf *bp;
+ u_char *cp;
+ int plen, mlen;
+
+ lh.code = code;
+ lh.id = id;
+ mlen = strlen(message);
+ plen = mlen + 1;
+ lh.length = htons(plen + sizeof(struct fsmheader));
+ bp = mballoc(plen + sizeof(struct fsmheader), MB_FSM);
+ memcpy(MBUF_CTOP(bp), &lh, sizeof(struct fsmheader));
+ cp = MBUF_CTOP(bp) + sizeof(struct fsmheader);
+ *cp++ = mlen;
+ memcpy(cp, message, mlen);
+ LogPrintf(LogPHASE, "PapOutput: %s\n", papcodes[code]);
+ HdlcOutput(PRI_LINK, PROTO_PAP, bp);
+}
+
+/*
+ * Validate given username and passwrd against with secret table
+ */
+static int
+PapValidate(u_char * name, u_char * key)
+{
+ int nlen, klen;
+
+ nlen = *name++;
+ klen = *key;
+ *key++ = 0;
+ key[klen] = 0;
+ LogPrintf(LogDEBUG, "PapValidate: name %s (%d), key %s (%d)\n",
+ name, nlen, key, klen);
+
+#ifndef NOPASSWDAUTH
+ if (Enabled(ConfPasswdAuth)) {
+ struct passwd *pwd;
+ int result;
+
+ LogPrintf(LogLCP, "Using PasswdAuth\n");
+ result = (pwd = getpwnam(name)) &&
+ !strcmp(crypt(key, pwd->pw_passwd), pwd->pw_passwd);
+ endpwent();
+ return result;
+ }
+#endif
+
+ return (AuthValidate(SECRETFILE, name, key));
+}
+
+void
+PapInput(struct mbuf * bp)
+{
+ int len = plength(bp);
+ struct fsmheader *php;
+ struct lcpstate *lcp = &LcpInfo;
+ u_char *cp;
+
+ if (len >= sizeof(struct fsmheader)) {
+ php = (struct fsmheader *) MBUF_CTOP(bp);
+ if (len >= ntohs(php->length)) {
+ if (php->code < PAP_REQUEST || php->code > PAP_NAK)
+ php->code = 0;
+ LogPrintf(LogPHASE, "PapInput: %s\n", papcodes[php->code]);
+
+ switch (php->code) {
+ case PAP_REQUEST:
+ cp = (u_char *) (php + 1);
+ if (PapValidate(cp, cp + *cp + 1)) {
+ SendPapCode(php->id, PAP_ACK, "Greetings!!");
+ lcp->auth_ineed = 0;
+ if (lcp->auth_iwait == 0) {
+ if ((mode & MODE_DIRECT) && isatty(modem) && Enabled(ConfUtmp))
+ if (Utmp)
+ LogPrintf(LogERROR, "Oops, already logged in on %s\n",
+ VarBaseDevice);
+ else {
+ struct utmp ut;
+ memset(&ut, 0, sizeof(ut));
+ time(&ut.ut_time);
+ strncpy(ut.ut_name, cp+1, sizeof(ut.ut_name)-1);
+ strncpy(ut.ut_line, VarBaseDevice, sizeof(ut.ut_line)-1);
+ if (logout(ut.ut_line))
+ logwtmp(ut.ut_line, "", "");
+ login(&ut);
+ Utmp = 1;
+ }
+ NewPhase(PHASE_NETWORK);
+ }
+ } else {
+ SendPapCode(php->id, PAP_NAK, "Login incorrect");
+ reconnect(RECON_FALSE);
+ LcpClose();
+ }
+ break;
+ case PAP_ACK:
+ StopAuthTimer(&AuthPapInfo);
+ cp = (u_char *) (php + 1);
+ len = *cp++;
+ cp[len] = 0;
+ LogPrintf(LogPHASE, "Received PAP_ACK (%s)\n", cp);
+ if (lcp->auth_iwait == PROTO_PAP) {
+ lcp->auth_iwait = 0;
+ if (lcp->auth_ineed == 0)
+ NewPhase(PHASE_NETWORK);
+ }
+ break;
+ case PAP_NAK:
+ StopAuthTimer(&AuthPapInfo);
+ cp = (u_char *) (php + 1);
+ len = *cp++;
+ cp[len] = 0;
+ LogPrintf(LogPHASE, "Received PAP_NAK (%s)\n", cp);
+ reconnect(RECON_FALSE);
+ LcpClose();
+ break;
+ }
+ }
+ }
+ pfree(bp);
+}
diff --git a/usr.sbin/ppp/pap.h b/usr.sbin/ppp/pap.h
new file mode 100644
index 0000000..6ef0792
--- /dev/null
+++ b/usr.sbin/ppp/pap.h
@@ -0,0 +1,29 @@
+/*
+ * Written by Toshiharu OHNO (tony-o@iij.ad.jp)
+ *
+ * Copyright (C) 1993, Internet Initiative Japan, Inc. All rights reserverd.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the Internet Initiative Japan. The name of the
+ * IIJ may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: pap.h,v 1.4 1997/10/26 01:03:29 brian Exp $
+ *
+ * TODO:
+ */
+
+#define PAP_REQUEST 1
+#define PAP_ACK 2
+#define PAP_NAK 3
+
+extern struct authinfo AuthPapInfo;
+
+extern void PapInput(struct mbuf *);
diff --git a/usr.sbin/ppp/pathnames.h b/usr.sbin/ppp/pathnames.h
new file mode 100644
index 0000000..a88aaa8
--- /dev/null
+++ b/usr.sbin/ppp/pathnames.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (c) 1989 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ *
+ * $Id: pathnames.h,v 1.7 1997/09/10 02:20:33 brian Exp $
+ *
+ * @(#)pathnames.h 5.2 (Berkeley) 6/1/90
+ */
+
+#if defined(__FreeBSD__) || defined(__OpenBSD__)
+#define _PATH_PPP "/etc/ppp"
+#else
+#define _PATH_PPP "/etc"
+#endif
diff --git a/usr.sbin/ppp/phase.c b/usr.sbin/ppp/phase.c
new file mode 100644
index 0000000..61d5698
--- /dev/null
+++ b/usr.sbin/ppp/phase.c
@@ -0,0 +1,66 @@
+/*
+ * $Id: phase.c,v 1.1 1997/10/26 01:03:31 brian Exp $
+ */
+
+#include <sys/param.h>
+#include <netinet/in.h>
+
+#include <stdio.h>
+
+#include "mbuf.h"
+#include "log.h"
+#include "lcp.h"
+#include "lcpproto.h"
+#include "timer.h"
+#include "auth.h"
+#include "pap.h"
+#include "chap.h"
+#include "ipcp.h"
+#include "ccp.h"
+#include "defs.h"
+#include "main.h"
+#include "loadalias.h"
+#include "command.h"
+#include "vars.h"
+#include "phase.h"
+
+int phase = 0; /* Curent phase */
+
+static char *PhaseNames[] = {
+ "Dead", "Establish", "Authenticate", "Network", "Terminate"
+};
+
+void
+NewPhase(int new)
+{
+ struct lcpstate *lcp = &LcpInfo;
+
+ phase = new;
+ LogPrintf(LogPHASE, "NewPhase: %s\n", PhaseNames[phase]);
+ switch (phase) {
+ case PHASE_AUTHENTICATE:
+ lcp->auth_ineed = lcp->want_auth;
+ lcp->auth_iwait = lcp->his_auth;
+ if (lcp->his_auth || lcp->want_auth) {
+ LogPrintf(LogPHASE, " his = %x, mine = %x\n", lcp->his_auth, lcp->want_auth);
+ if (lcp->his_auth == PROTO_PAP)
+ StartAuthChallenge(&AuthPapInfo);
+ if (lcp->want_auth == PROTO_CHAP)
+ StartAuthChallenge(&AuthChapInfo);
+ } else
+ NewPhase(PHASE_NETWORK);
+ break;
+ case PHASE_NETWORK:
+ IpcpUp();
+ IpcpOpen();
+ CcpUp();
+ CcpOpen();
+ break;
+ case PHASE_DEAD:
+ if (mode & MODE_DIRECT)
+ Cleanup(EX_DEAD);
+ if (mode & MODE_BACKGROUND && reconnectState != RECON_TRUE)
+ Cleanup(EX_DEAD);
+ break;
+ }
+}
diff --git a/usr.sbin/ppp/phase.h b/usr.sbin/ppp/phase.h
new file mode 100644
index 0000000..e3f3b1c
--- /dev/null
+++ b/usr.sbin/ppp/phase.h
@@ -0,0 +1,32 @@
+/*
+ * Written by Toshiharu OHNO (tony-o@iij.ad.jp)
+ *
+ * Copyright (C) 1993, Internet Initiative Japan, Inc. All rights reserverd.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the Internet Initiative Japan. The name of the
+ * IIJ may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: phase.h,v 1.7 1997/08/25 00:29:25 brian Exp $
+ *
+ * TODO:
+ */
+
+#define PHASE_DEAD 0 /* Link is dead */
+#define PHASE_ESTABLISH 1 /* Establishing link */
+#define PHASE_AUTHENTICATE 2 /* Being authenticated */
+#define PHASE_NETWORK 3
+#define PHASE_TERMINATE 4 /* Terminating link */
+#define PHASE_OSLINKED 5 /* The OS is linked up */
+
+extern int phase; /* Curent phase */
+
+extern void NewPhase(int);
diff --git a/usr.sbin/ppp/ppp.8 b/usr.sbin/ppp/ppp.8
new file mode 100644
index 0000000..6eb42cd
--- /dev/null
+++ b/usr.sbin/ppp/ppp.8
@@ -0,0 +1,2351 @@
+.\" $Id: ppp.8,v 1.76 1997/11/09 17:51:26 brian Exp $
+.Dd 20 September 1995
+.Os FreeBSD
+.Dt PPP 8
+.Sh NAME
+.Nm ppp
+.Nd
+Point to Point Protocol (a.k.a. iijppp)
+.Sh SYNOPSIS
+.Nm
+.Op Fl auto | background | ddial | direct | dedicated
+.Op Fl alias
+.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).
+
+.Sh Major Features
+
+.Bl -diag
+.It Provides interactive user interface.
+Using its command mode, the user can
+easily enter commands to establish the connection with the remote end, check
+the status of connection and close the connection. All functions can
+also be optionally password protected for security.
+
+.It Supports both manual and automatic dialing.
+Interactive mode has a
+.Dq term
+command which enables you to talk to your modem directly. When your
+modem is connected to the remote peer and it starts to talk
+.Em PPP ,
+.Nm
+detects it and switches to packet mode automatically. Once you have
+determined the proper sequence for connecting with the remote host, you
+can write a chat script to define the necessary dialing and login
+procedure for later convenience.
+
+.It Supports on-demand dialup capability.
+By using 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 ddial mode (dedicated or daemon dialing)
+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 who worry less about line charges
+and more about being connected full time.
+
+.It Supports packet aliasing.
+Packet aliasing (a.k.a. IP masquerading) allows computers on a
+private, unregistered network to access the Internet. The
+.Em PPP
+host acts as a masquerading gateway. IP addresses as well as TCP and
+UDP port numbers are aliased for outgoing packets and de-aliased for
+returning packets.
+
+.It Supports background PPP connections.
+In background mode, if
+.Nm
+successfully establishes the connection, it will become a daemon.
+Otherwise, it will exit with an error. This allows the setup of
+scripts that wish to execute certain commands only if the connection
+is successfully established.
+
+.It Supports server-side PPP connections.
+In direct mode,
+.nm
+acts as server which accepts incoming
+.Em PPP
+connections on stdin/stdout.
+
+.It Supports PAP and CHAP authentication.
+With PAP or CHAP, it is possible to skip the Unix style
+.Xr login 1
+proceedure, and use the
+.Em PPP
+protocol for authentication instead.
+
+.It Supports Proxy Arp.
+When
+.Em PPP
+is set up as server, you can also configure it to do proxy arp for your
+connection.
+
+.It Supports packet filtering.
+User can define four kinds of filters:
+.Em ifilter
+for incoming packets,
+.Em ofilter
+for outgoing packets,
+.Em dfilter
+to define a dialing trigger packet and
+.Em afilter
+for keeping a connection alive with the trigger packet.
+
+.It Tunnel driver supports bpf.
+The user can use
+.Xr tcpdump 1
+to check the packet flow over the
+.Em PPP
+link.
+
+.It Supports PPP over TCP capability.
+
+
+.It Supports IETF draft Predictor-1 compression.
+.Nm
+supports not only VJ-compression but also Predictor-1 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
+compression pre-compresses
+.Em all
+data flowing through the link, thus reducing overhead to a minimum.
+
+.It Supports Microsoft's IPCP extensions.
+Name Server Addresses and NetBIOS Name Server Addresses can be negotiated
+with clients using the Microsoft
+.Em PPP
+stack (ie. Win95, WinNT)
+
+.Sh PERMISSIONS
+.Nm Ppp
+is installed as user
+.Dv root
+and group
+.Dv network ,
+with permissions
+.Dv 4550 .
+.Nm Ppp
+will not execute in client mode if the invoking user id is not zero.
+.Nm Ppp
+will run in
+.Fl direct
+mode as a normal user, but due to its execution permissions, this user
+must be a member of group
+.Dv network .
+When running as a normal user,
+.Nm
+switches to user id 0 in order to alter the system routing table. All
+external commands (executed via the "shell" or "!bg" commands) are executed
+as the user id that invoked
+.Nm ppp .
+
+.Sh GETTING STARTED
+
+When you first run
+.Nm
+you may need to deal with some initial configuration details. First,
+your kernel should 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:
+
+.Dl pseudo-device tun N
+
+where
+.Ar N
+is the maximum number of
+.Em PPP
+connections you wish to support.
+
+Second, 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 .
+
+Last of all, create a log file.
+.Nm Ppp
+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:
+
+.Dl !ppp
+.Dl *.*<TAB>/var/log/ppp.log
+
+Make sure you use actual TABs here. If you use spaces, the line will be
+silently ignored.
+
+It is possible to have more than one
+.Em PPP
+log file by creating a link to the
+.Nm
+executable:
+
+.Dl # cd /usr/sbin
+.Dl # ln ppp ppp0
+
+and using
+
+.Dl !ppp0
+.Dl *.* /var/log/ppp0.log
+
+in
+.Pa /etc/syslog.conf .
+Don't forget to send a
+.Dv HUP
+signal to
+.Xr syslogd 8
+after altering
+.Pa /etc/syslog.conf .
+
+.Sh MANUAL DIALING
+
+In the following examples, we assume that your machine name is
+.Dv awfulhak .
+
+If you set your host name and password in
+.Pa /etc/ppp/ppp.secret ,
+you can't do anything except run the help, passwd and quit commands.
+
+.Bd -literal -offset indent
+ppp on "your host name"> help
+ help : Display this message
+ passwd : Password for security
+ quit : Quit the PPP program
+ppp on awfulhak> pass <password>
+.Ed
+
+The "on" part of your prompt will change to "ON" if you specify the
+correct password.
+
+.Bd -literal -offset indent
+ppp ON awfulhak>
+.Ed
+
+You can now specify the device name, speed and parity for your modem,
+and whether CTS/RTS signalling should be used (CTS/RTS is used by
+default). If your hardware does not provide CTS/RTS lines (as
+may happen when you are connected directly to certain PPP-capable
+terminal servers),
+.Nm
+will never send any output through the port; it waits for a signal
+which never comes. Thus, if you have a direct line and can't seem
+to make a connection, try turning CTS/RTS off:
+
+
+.Bd -literal -offset indent
+ppp ON awfulhak> set line /dev/cuaa0
+ppp ON awfulhak> set speed 38400
+ppp ON awfulhak> set parity even
+ppp ON awfulhak> set ctsrts on
+ppp ON awfulhak> show modem
+
+* Modem related information is shown here *
+
+ppp ON awfulhak>
+.Ed
+
+The term command can now be used to talk directly with your modem:
+
+.Bd -literal -offset indent
+ppp ON awfulhak> term
+at
+OK
+atdt123456
+CONNECT
+login: ppp
+Password:
+Protocol: ppp
+.Ed
+
+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>
+PPP ON awfulhak>
+.Ed
+
+You are now connected! Note that
+.Sq PPP
+in the prompt has changed to capital letters to indicate that you have
+a peer connection. The show command can be used to see how things are
+going:
+
+.Bd -literal -offset indent
+PPP ON awfulhak> show lcp
+
+* LCP related information is shown here *
+
+PPP ON awfulhak> show ipcp
+
+* IPCP related information is shown here *
+.Ed
+
+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 0 0 HISADDR
+.Ed
+
+The string
+.Sq HISADDR
+represents the IP address of the connected peer. This variable is only
+available once a connection has been established. A common error
+is to specify the above command in your
+.Pa /etc/ppp/ppp.conf
+file. This won't work as the remote IP address hasn't been
+established when this file is read.
+
+You can now use your network applications (ping, telnet, ftp etc.)
+in other windows on your machine.
+
+Refer to the
+.Em 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 /etc/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 -compact
+.It
+A line starting with a
+.Pq Dq #
+character is treated as a comment line.
+
+.It
+An inclusion is a line beginning with the word
+.Sq !include .
+It must have one argument - the file to include. You may wish to
+.Dq !include ~/.ppp.conf
+for compatibility with older versions of
+.Nm ppp .
+
+.It
+A label name starts in the first column and is followed by
+a colon
+.Pq Dq \&: .
+
+.It
+A command line must contain a space or tab in the first column.
+.El
+
+.Pp
+The
+.Pa /etc/ppp/ppp.conf
+file should consist of at least a
+.Dq default
+section. This section is always executed. It should also contain
+one or more sections, named according to their purpose, for example,
+.Dq MyISP
+would represent your ISP, and
+.Dq ppp-in
+would represent an incoming
+.Nm
+configuration.
+
+You can now specify the destination label name when you invoke
+.Nm ppp .
+Commands associated with the
+.Dq default
+label are executed, followed by those associated with the destination
+label provided. When
+.Nm
+is started with no arguments, the
+.Dq default
+section is still executed. The load command can be used to manually
+load a section from the
+.Pa /etc/ppp/ppp.conf
+file:
+
+.Bd -literal -offset indent
+PPP ON awfulhak> load MyISP
+.Ed
+
+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
+dial OK!
+login OK!
+PPP ON awfulhak>
+.Ed
+
+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 /etc/ppp/ppp.conf.sample
+which adds a default route. The strings
+.Dv HISADDR ,
+.Dv MYADDR
+and
+.Dv INTERFACE
+are available as the relevent IP addresses and interface name.
+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 .
+
+.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. You must also specify the destination label in
+.Pa /etc/ppp/ppp.conf
+to use. This label must contain the
+.Dq set ifaddr
+command to define the remote peers IP address. (refer to
+.Pa /etc/ppp/ppp.conf.sample )
+
+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 /etc/ppp/ppp.conf.sample )
+
+.Bd -literal -offset indent
+# ppp -auto pmdemand
+...
+#
+.Ed
+
+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 diagnostic port as follows (this
+can be done in
+.Fl background
+and
+.Fl direct
+mode too):
+
+.Bd -literal -offset indent
+# pppctl -v 3000 show ipcp
+Password:
+IPCP [Opened]
+ his side: xxxx
+ ....
+.Ed
+
+Currently,
+.Xr telnet 1
+may also be used to talk interactively.
+
+.Pp
+In order to achieve this, you must use the
+.Dq set server
+command as described below. It is possible to retrospectively make a running
+.Nm
+program listen on a diagnostic port by configuring
+.Pa /etc/ppp/ppp.secret ,
+and sending it a
+.Dv USR1
+signal.
+
+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 with
+.Bd -literal -offset indent
+set redial seconds|random[.nseconds|random] [dial_attempts]
+.Ed
+.Pp
+.Sq Seconds
+is the number of seconds to wait before attempting
+to connect again. If the argument is
+.Sq random ,
+the delay period is a random value between 0 and 30 seconds.
+.Sq Nseconds
+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
+.Sq random ,
+the delay period is a random value between 0 and 30 seconds.
+.Sq dial_attempts
+is the number of times to try to connect for each outgoing packet
+that is received. The previous value is unchanged if this parameter
+is omitted. If a value of zero is specified for
+.Sq dial_attempts ,
+.Nm
+will keep trying until a connection is made.
+.Bd -literal -offset indent
+set redial 10.3 4
+.Ed
+.Pp
+will attempt to connect 4 times for each outgoing packet that is
+detected 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).
+
+Modifying the dial delay is very useful when running
+.Nm
+in demand
+dial 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
+
+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
+
+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
+
+ PPP ON awfulhak> close
+ ppp ON awfulhak> quit all
+
+.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
+
+.It
+Edit
+.Pa /etc/ttys
+to enable a
+.Xr getty 8
+on the port where the modem is attached.
+
+For example:
+
+.Dl ttyd1 "/usr/libexec/getty std.38400" dialup on secure
+
+Don't forget to send a
+.Dv HUP
+signal to the
+.Xr init 8
+process to start the
+.Xr getty 8 .
+
+.Dl # kill -HUP 1
+
+.It
+Prepare an account for the incoming user.
+.Bd -literal
+ppp:xxxx:66:66:PPP Login User:/home/ppp:/usr/local/bin/ppplogin
+.Ed
+
+.It
+Create a
+.Pa /usr/local/bin/ppplogin
+file with the following contents:
+.Bd -literal -offset indent
+#!/bin/sh -p
+exec /usr/sbin/ppp -direct
+.Ed
+
+(You can specify a label name for further control.)
+
+.Pp
+Direct mode
+.Pq Fl direct
+lets
+.Nm
+work with stdin and stdout. You can also use
+.Xr pppctl 8
+or
+.Xr telnet 1
+to connect to a configured diagnostic port, in the same manner as with
+client-side
+.Nm ppp .
+
+.It
+Optional support for Microsoft's IPCP Name Server and NetBIOS
+Name Server negotiation can be enabled use
+.Dq enable msext
+and
+.Dq set ns pri-addr [sec-addr]
+along with
+.Dq set nbns pri-addr [sec-addr]
+in your
+.Pa /etc/ppp/ppp.conf
+file.
+
+.El
+
+.Sh RECEIVING INCOMING PPP CONNECTIONS (Method 2)
+
+This method differs in that it recommends the use of
+.Em mgetty+sendfax
+to handle the modem connections. The latest versions (0.99 and higher)
+can be compiled with the
+.Dq AUTO_PPP
+option to allow detection of clients speaking
+.Em PPP
+to the login prompt.
+
+Follow these steps:
+
+.Bl -enum
+
+.It
+Get, configure, and install mgetty+sendfax v0.99 or later making
+sure you have used the AUTO_PPP option.
+
+.It
+Edit
+.Pa /etc/ttys
+to enable a mgetty on the port where the modem is attached. For
+example:
+
+.Dl cuaa1 "/usr/local/sbin/mgetty -s 57600" dialup on
+
+.It
+Prepare an account for the incoming user.
+.Bd -literal
+Pfred:xxxx:66:66:Fred's PPP:/home/ppp:/etc/ppp/ppp-dialup
+.Ed
+
+.It
+Examine the files
+.Pa /etc/ppp/sample.ppp-dialup ,
+.Pa /etc/ppp/sample.ppp-pap-dialup
+and
+.Pa /etc/ppp/ppp.conf.sample
+for ideas.
+.Pa /etc/ppp/ppp-pap-dialup
+is supposed to be called from
+.Pa /usr/local/etc/mgetty+sendfax/login.conf
+from a line like
+
+.Dl /AutoPPP/ - - /etc/ppp/ppp-pap-dialup
+.El
+
+.Sh PPP OVER TCP (a.k.a Tunneling)
+
+Instead of running
+.Nm
+over a serial link, it is possible to
+use a TCP connection instead by specifying a host and port as the
+device:
+
+.Dl set device ui-gate:6669
+
+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:
+
+.Dl ppp-in 6669/tcp # Incoming PPP connections over TCP
+
+and updating
+.Pa /etc/inetd.conf
+to tell
+.Xr inetd 8
+how to deal with incoming connections on that port:
+
+.Dl ppp-in stream tcp nowait root /usr/sbin/ppp ppp -direct ppp-in
+
+Don't forget to send a
+.Dv HUP
+signal to
+.Xr inetd 8
+after you've updated
+.Pa /etc/inetd.conf .
+
+Here, we use a label named
+.Dq ppp-in .
+The entry in
+.Pa /etc/ppp/ppp.conf
+on ui-gate (the receiver) should contain the following:
+
+.Bd -literal -offset indent
+ppp-in:
+ set timeout 0
+ set ifaddr 10.0.4.1 10.0.4.2
+ add 10.0.1.0 255.255.255.0 10.0.4.1
+.Ed
+
+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
+The entry in
+.Pa /etc/ppp/ppp.conf
+on awfulhak (the initiator) should contain the following:
+
+.Bd -literal -offset indent
+ui-gate:
+ set escape 0xff
+ set device ui-gate:ppp-in
+ set dial
+ set timeout 30 5 4
+ set log Phase Chat Connect Carrier hdlc LCP IPCP CCP tun
+ set ifaddr 10.0.4.2 10.0.4.1
+ add 10.0.2.0 255.255.255.0 10.0.4.2
+.Ed
+.Pp
+Again, if you're enabling PAP, you'll also need:
+.Bd -literal -offset indent
+ set authname MyAuthName
+ set authkey MyAuthKey
+.Ed
+
+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
+
+.Dl awfulhak # ppp -background ui-gate
+
+The result will be an additional "route" on awfulhak to the
+10.0.2.0/24 network via the TCP connection, and an additional
+"route" on ui-gate to the 10.0.1.0/24 network.
+
+The networks are effectively bridged - the underlying TCP
+connection may be across a public network (such as the
+Internet), and the
+.Em PPP
+traffic is conceptually encapsulated
+(although not packet by packet) inside the TCP stream between
+the two gateways.
+
+The major disadvantage of this mechanism is that there are two
+"guaranteed delivery" mechanisms in place - the underlying TCP
+stream and whatever protocol is used over the
+.Em PPP
+link - probably TCP again. If packets are lost, both levels will
+get in each others way trying to negotiate sending of the missing
+packet.
+
+.Sh PACKET ALIASING
+
+The
+.Fl alias
+command line option enables packet aliasing. This allows the
+.Nm
+host to act as a masquerading gateway for other computers over
+a local area network. Outgoing IP packets are aliased so that
+they appear to come from the
+.Nm
+host, and incoming packets are de-aliased so that they are routed
+to the correct machine on the local area network.
+
+Packet aliasing allows computers on private, unregistered
+subnets to have Internet access, although they are invisible
+from the outside world.
+
+In general, correct
+.Nm
+operation should first be verified with packet aliasing disabled.
+Then, the
+.Fl alias
+option should be switched on, and network applications (web browser,
+.Xr telnet 1 ,
+.Xr ftp 1 ,
+.Xr ping 8 ,
+.Xr traceroute 8 )
+should be checked on the
+.Nm
+host. Finally, the same or similar applications should be checked on other
+computers in the LAN.
+
+If network applications work correctly on the
+.Nm
+host, but not on other machines in the LAN, then the masquerading
+software is working properly, but the host is either not forwarding
+or possibly receiving IP packets. Check that IP forwarding is enabled in
+.Pa /etc/rc.conf
+and that other machines have designated the
+.Nm
+host as the gateway for the LAN.
+
+.Sh PACKET FILTERING
+
+This implementation supports packet filtering. There are four kinds of
+filters; ifilter, ofilter, dfilter and afilter. Here are the basics:
+
+.Bl -bullet -compact
+.It
+A filter definition has the following syntax:
+
+set filter-name rule-no action [src_addr/src_width] [dst_addr/dst_width]
+[proto [src [lt|eq|gt] port ]] [dst [lt|eq|gt] port] [estab]
+.Bl -enum
+.It
+.Sq filter-name
+should be one of ifilter, ofilter, dfilter or afilter.
+.It
+There are two actions:
+.Sq permit
+and
+.Sq deny .
+If a given packet
+matches the rule, the associated action is taken immediately.
+.It
+.Sq src_width
+and
+.Sq dst_width
+work like a netmask to represent an address range.
+.It
+.Sq proto
+must be one of icmp, udp or tcp.
+.It
+.Sq port number
+can be specified by number and service name from
+.Pa /etc/services .
+
+.El
+
+.It
+Each filter can hold up to 20 rules, starting from rule 0.
+The entire rule set is not effective until rule 0 is defined,
+ie. the default is to allow everything through.
+
+.It
+If no rule is matched to a packet, that packet will be discarded
+(blocked).
+
+.It
+Use
+.Dq set filter-name -1
+to flush all rules.
+
+.El
+
+See
+.Pa /etc/ppp/ppp.conf.filter.example .
+
+
+.Sh SETTING IDLE, LINE QUALITY REQUEST, RETRY TIMER
+
+To check/set idle timer, use the
+.Dq show timeout
+and
+.Dq set timeout [lqrtimer [retrytimer]]
+commands:
+
+.Bd -literal -offset indent
+ppp ON awfulhak> set timeout 600
+.Ed
+
+The timeout period is measured in seconds, the default values for which
+are timeout = 180 or 3 min, lqrtimer = 30sec and retrytimer = 3sec.
+To disable the idle timer function, use the command
+
+.Bd -literal -offset indent
+ppp ON awfulhak> set timeout 0
+.Ed
+
+In
+.Fl auto
+mode, an idle timeout causes the
+.Em PPP
+session to be
+closed, though the
+.Nm
+program itself remains running. Another trigger packet will cause it to
+attempt to reestablish the link.
+
+.Sh PREDICTOR-1 COMPRESSION
+
+This version supports CCP and Predictor type 1 compression based on
+the current IETF-draft specs. As a default behaviour,
+.Nm
+will attempt to use (or be willing to accept) this capability when the
+peer agrees (or requests it).
+
+To disable CCP/predictor functionality completely, use the
+.Dq disable pred1
+and
+.Dq deny pred1
+commands.
+
+.Sh CONTROLLING IP ADDRESS
+
+.Nm
+uses IPCP to negotiate IP addresses. Each side of the connection
+specifies the IP address that it's willing to use, and if the requested
+IP address is acceptable then
+.Nm
+returns ACK to the requester. Otherwise,
+.Nm
+returns NAK to suggest that the peer use a different IP address. When
+both sides of the connection agree to accept the received request (and
+send ACK), IPCP is set to the open state and a network level connection
+is established.
+
+To control this IPCP behaviour, this implementation has the
+.Dq set ifaddr
+command for defining the local and remote IP address:
+
+.Bd -literal -offset indent
+set ifaddr [src_addr [dst_addr [netmask [trigger_addr]]]]
+.Ed
+
+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
+and
+.Sq dst_addr
+default 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.
+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
+
+The above specification means:
+.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
+
+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 obey the direction from it.
+
+In order to allow more flexible behaviour, `ifaddr' variable allows the
+user to specify IP address more loosely:
+
+.Dl set ifaddr 192.244.177.38/24 192.244.177.2/20
+
+A number followed by a slash (/) represent the number of bits significant in
+the IP address. The above example signifies that:
+
+.Bl -bullet -compact
+.It
+I'd like to use 192.244.177.38 as my address if it is possible, but I'll
+also accept any IP address between 192.244.177.0 and 192.244.177.255.
+
+.It
+I'd like to make him use 192.244.177.2 as his own address, but I'll also
+permit him to use any IP address between 192.244.176.0 and
+192.244.191.255.
+
+.It
+As you may have already noticed, 192.244.177.2 is equivalent to saying
+192.244.177.2/32.
+
+.It
+As an exception, 0 is equivalent to 0.0.0.0/0, meaning that I have no
+preferred IP address and will obey the remote peers selection. When
+using zero, no routing table entries will be made until a connection
+is established.
+
+.It
+192.244.177.2/0 means that I'll accept/permit any IP address but I'll
+try to insist that 192.244.177.2 be used first.
+.El
+
+.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 (|) or a colon (:)
+.Bd -literal -offset indent
+set phone "111[|222]...[:333[|444]...]...
+.Ed
+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 \\"\\" 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.
+.It
+Expect nothing.
+.It
+Send ATZ.
+.It
+Expect OK. If that's not received, 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
+
+Once the connection is established, the login script is executed. This
+script is written in the same style as the dial script:
+.Bd -literal -offset indent
+set login "TIMEOUT 15 login:-\\\\r-login: awfulhak word: xxx 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 "xxx".
+.It
+Expect "ocol:" (the tail end of a "Protocol:" prompt).
+.It
+Send "PPP".
+.It
+Expect "HELLO".
+.El
+.Pp
+Login scripts vary greatly between ISPs.
+
+.It
+Use
+.Dq set line
+and
+.Dq set speed
+to specify your serial line and speed, for example:
+.Bd -literal -offset indent
+set line /dev/cuaa0
+set speed 115200
+.Ed
+.Pp
+Cuaa0 is the first serial port on FreeBSD. If you're running
+.Nm
+on OpenBSD, cua00 is the first. A speed of 115200 should be specified
+if you have a modem capable of bit rates of 28800 or more. In general,
+the serial speed should be about four times the modem speed.
+
+.It
+Use the
+.Dq set ifaddr
+command to define the IP address.
+.Bl -bullet
+.It
+If you know what IP address your provider uses, then use it as the remote
+address (dst_addr), otherwise choose something like 10.0.0.2/0 (see below).
+.It
+If your provider has assigned a particular IP address to you, then use
+it as your address (src_addr).
+.It
+If your provider assigns your address dynamically, choose a suitably
+unobtrusive and unspecific IP number as your address. 10.0.0.1/0 would
+be appropriate. The bit after the / specifies how many bits of the
+address you consider to be important, so if you wanted to insist on
+something in the class C network 1.2.3.0, you could specify 1.2.3.1/24.
+.It
+If you find that your ISP accepts the first IP number that you suggest,
+specify third and forth arguments of
+.Dq 0.0.0.0 .
+This will force your ISP to assign a number. (The third argument will
+be ignored as it is less restrictive than the default mask for your
+.Sq src_addr .
+.El
+.Pp
+An example for a connection where you don't know your IP number or your
+ISPs IP number would be:
+.Bd -literal -offset indent
+set ifaddr 10.10.10.10/0 10.10.11.11/0 0.0.0.0 0.0.0.0
+.Ed
+
+.It
+In most cases, your ISP will also be your default router. If this is
+the case, add the lines
+
+.Bd -literal -offset indent
+delete ALL
+add 0 0 HISADDR
+.Ed
+
+.Pp
+to
+.Pa /etc/ppp/ppp.conf .
+.Pp
+This tells
+.Nm
+to delete all non-direct routing entries for the tun interface that
+.Nm
+is running on, then to add a default route to 10.10.11.11.
+.Pp
+If you're using dynamic IP numbers, you must also put these two lines
+in the
+.Pa /etc/ppp/ppp.linkup
+file:
+
+.Bd -literal -offset indent
+delete ALL
+add 0 0 HISADDR
+.Ed
+
+HISADDR is a macro meaning the "other side"s IP number, and is
+available once an IP number has been agreed (using IPCP).
+Now, once a connection is established,
+.Nm
+will delete all non-direct interface routes, and add a default route
+pointing at the peers IP number. You should use the same label as the
+one used in
+.Pa /etc/ppp/ppp.conf .
+.Pp
+If commands are being typed interactively, the only requirement is
+to type
+.Bd -literal -offset indent
+add 0 0 HISADDR
+.Ed
+.Pp
+after a successful dial.
+
+.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.
+.El
+
+Please refer to
+.Pa /etc/ppp/ppp.conf.sample
+and
+.Pa /etc/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:
+
+.Bl -column SMMMMMM -offset indent
+.It Li Async Dump async level packet in hex
+.It Li Carrier Log Chat lines with 'CARRIER'
+.It Li CCP Generate a CCP packet trace
+.It Li Chat Generate Chat script trace log
+.It Li Command Log commands executed
+.It Li Connect Generate complete Chat log
+.It Li Debug Log (very verbose) debug information
+.It Li HDLC Dump HDLC packet in hex
+.It Li ID0 Log all function calls specifically made as user id 0.
+.It Li IPCP Generate an IPCP packet trace
+.It Li LCP Generate an LCP packet trace
+.It Li Link Log address assignments and link up/down events
+.It Li LQM Generate LQR report
+.It Li Phase Phase transition log output
+.It Li TCP/IP Dump all TCP/IP packets
+.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 LOG_WARNING.
+.It Li Error Output to both the terminal device and the log file using
+LOG_ERROR.
+.It Li Alert Output to the log file using 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 Carrier Link Phase .
+
+.Pp
+It is also possible to log directly to the screen. The syntax is
+the same except that the word
+.Dq local
+should immediately follow
+.Dq set log .
+The default is
+.Dq set log local
+(ie. no direct screen logging).
+
+.Pp
+If The first argument to
+.Dq set log Op local
+begins with a '+' or a '-' character, the current log levels are
+not cleared, for example:
+
+.Bd -literal -offset indent
+PPP ON awfulhak> set log carrier link phase
+PPP ON awfulhak> show log
+Log: Carrier Link Phase Warning Error Alert
+Local: Warning Error Alert
+PPP ON awfulhak> set log -link +tcp/ip -warning
+PPP ON awfulhak> set log local +command
+PPP ON awfulhak> show log
+Log: Carrier 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 Ppp
+deals with the following signals:
+
+.Bl -tag -width 20
+.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, when not in interactive mode, tells
+.Nm
+to close any existing server socket and open an Internet socket using
+port 3000 plus the current tunnel device number. This can only be
+achieved if a suitable local password is specified in
+.Pa /etc/ppp/ppp.secret .
+
+.It USR2
+This signal, tells
+.Nm
+to close any existing server socket.
+
+.El
+
+.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 20
+.It accept|deny|enable|disable 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 20
+.It vjcomp
+Default: Enabled and Accepted. This option decides if Van Jacobson
+header compression will be used.
+
+.It lqr
+Default: Disabled and Accepted. This option decides if Link Quality
+Requests will be sent. LQR is a protocol that allows
+.Nm
+to determine that the link is down without relying on the modems
+carrier detect.
+
+.It chap
+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. Refer to the description of the
+.Dq set encrypt
+command for further details.
+
+.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
+option 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 acfcomp
+Default: Enabled and Accepted. ACFComp stands for Address and Control
+Field Compression. Non LCP packets usually have very similar address
+and control fields - making them easily compressible.
+
+.It 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 pred1
+Default: Enabled and Accepted. This option decides if Predictor 1
+compression will be used.
+
+.It msext
+Default: Disabled. This option allows the use of Microsoft's
+.Em PPP
+extensions, supporting the negotiation of the DNS and the NetBIOS NS.
+Enabling this allows us to pass back the values given in "set ns"
+and "set nbns".
+
+.El
+The following options are not actually negotiated with the peer.
+Therefore, accepting or denying them makes no sense.
+
+.Bl -tag -width 20
+.It proxy
+Default: Disabled. Enabling this option will tell
+.Nm
+to proxy ARP for the peer.
+
+.It passwdauth
+Default: Disabled. Enabling this option will tell the PAP authentication
+code to use the password file (see
+.Xr passwd 5 )
+to authenticate the caller rather than the
+.Pa /etc/ppp/ppp.secret
+file.
+
+.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
+
+.It add dest mask gateway
+.Dq Dest
+is the destination IP address and
+.Dq mask
+is its mask.
+.Dq 0 0
+refers to the default route.
+.Dq Gateway
+is the next hop gateway to get to the given
+.Dq dest
+machine/network.
+
+.It [!]bg command
+The given command is executed in the background.
+Any of the pseudo arguments
+.Dv HISADDR ,
+.Dv INTERFACE
+and
+.Dv MYADDR
+will be replaced with the appropriate values. If you wish to pause
+.Nm
+while the command executes, use the
+.Dv shell
+command instead.
+
+.It close
+Close the current connection (but don't quit).
+
+.It delete ALL | dest [gateway [mask]]
+If
+.Dq ALL
+is specified, all non-direct entries in the routing for the interface
+that
+.Nm
+is using are deleted. This means all entries for tunN, except the entry
+representing the actual link. When
+.Dq ALL
+is not used, any existing route with the given
+.Dq dest ,
+destination network
+.Dq mask
+and
+.Dq gateway
+is deleted. The default
+.Dq mask
+value is 0.0.0.0.
+
+.It dial|call [remote]
+If
+.Dq remote
+is specified, a connection is established using the
+.Dq dial
+and
+.Dq login
+scripts for the given
+.Dq remote
+system. Otherwise, the current settings are used to establish
+the connection.
+
+.It display
+Displays the current status of the negotiable protocol
+values as specified under
+.Dq accept|deny|enable|disable option....
+above.
+
+.It passwd pass
+Specify the password required for access to the full
+.Nm
+command set.
+
+.It load [remote]
+Load the given
+.Dq remote
+label. If
+.Dq remote
+is not given, the
+.Dq default
+label is assumed.
+
+.It save
+This option is not (yet) implemented.
+
+.It set[up] var value
+This option allows the setting of any of the following variables:
+
+.Bl -tag -width 20
+.It set accmap hex-value
+ACCMap stands for Asyncronous Control Character Map. This is always
+negotiated with the peer, and defaults to a value of 0x00000000.
+This protocol is required to defeat hardware that depends on passing
+certain characters from end to end (such as XON/XOFF etc).
+
+.It set filter-name rule-no action [src_addr/src_width]
+[dst_addr/dst_width] [proto [src [lt|eq|gt] port ]]
+[dst [lt|eq|gt] port] [estab]
+.Pp
+.Nm Ppp
+supports four filter sets. The afilter specifies packets that keep
+the connection alive - reseting the idle timer. The dfilter specifies
+packets that cause
+.Nm
+to dial when in
+.Fl auto
+mode. The ifilter specifies packets that are allowed to travel
+into the machine and the ofilter specifies packets that are allowed
+out of the machine. By default all filter sets allow all packets
+to pass.
+
+Rules are processed in order according to
+.Dq n .
+Up to 20 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
+ifilters and ofilters, this means that the packet is dropped. In
+the case of afilters it means that the packet will not reset the
+idle timer and in the case of dfilters it means that the packet will
+not trigger a dial.
+
+Refer to the section on PACKET FILTERING above for further details.
+
+.It set authkey|key value
+This sets the authentication key (or password) used in client mode
+PAP or CHAP negotiation to the given value. It can also be used to
+specify the password to be used in the dial or login scripts, preventing
+the actual password from being logged.
+
+.It set authname id
+This sets the authentication id used in client mode PAP or CHAP negotiation.
+
+.It set ctsrts
+This sets hardware flow control and is the default.
+
+.It set device|line value
+This sets the device to which
+.Nm
+will talk to the given
+.Dq value .
+All serial device names are expected to begin with
+.Pa /dev/ .
+If
+.Dq value
+does not begin with
+.Pa /dev/ ,
+it must be of the format
+.Dq host:port .
+If this is the case,
+.Nm
+will attempt to connect to the given
+.Dq host
+on the given
+.Dq port .
+Refer to the section on
+.Em PPP OVER TCP
+above for further details.
+
+.It set dial chat-script
+This specifies the chat script that will be used to dial the other
+side. See also the
+.Dv set login
+command below. Refer to
+.Xr chat 8
+and to the example configuration files for details of the chat script
+format. The string \\\\T will be replaced with the current phone number
+(see
+.Dq set phone
+below) and the string \\\\P will be replaced with the password (see
+.Dq set key
+above).
+
+.It set hangup chat-script
+This specifies the chat script that will be used to reset the modem
+before it is closed. It should not normally be necessary, but can
+be used for devices that fail to reset themselves properly on close.
+
+.It set encrypt MSChap|MD5
+This specifies the encryption algorithm to request and use when issuing
+the CHAP challenge, and defaults to MD5. If this is set to MSChap,
+.Nm
+will behave like a Microsoft RAS when sending the CHAP challenge (assuming
+CHAP is enabled). When responding to a challenge,
+.Nm
+determines how to encrypt the response based on the challenge, so this
+setting is ignored.
+
+.Bl -tag -width NOTE:
+.It NOTE:
+Because the Microsoft encryption algorithm uses a combination of MD4 and DES,
+if you have not installed DES encryption software on your machine
+before building
+.Nm ppp ,
+this option will not be available - only MD5 will be used.
+.El
+
+.It set escape 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 `escaped' as they travel across the link.
+
+.It set ifaddr [myaddr [hisaddr [netmask [triggeraddr]]]]
+This command specifies the IP addresses that will be used during
+IPCP negotiation. Addresses are specified using the format
+
+.Dl a.b.c.d/n
+
+Where a.b.c.d is the preferred IP, but n specifies how many bits
+of the address we will insist on. If the /n bit is omitted, it
+defaults to /32 unless the IP address is 0.0.0.0 in which case
+the mask defaults to /0.
+
+If
+.Dq triggeraddr
+is specified, it is used in place of
+.Dq myaddr
+in the initial IPCP negotiation. However, only an address in the
+.Dq myaddr
+range will be accepted.
+
+.It set loopback on|off
+When set to
+.Dq on
+(the default),
+.Nm
+will automatically loop back packets being sent
+out with a destination address equal to that of the
+.Em PPP
+interface. If set to
+.Dq off ,
+.Nm
+will send the packet, probably resulting in an ICMP redirect from
+the other end.
+
+.It set log [local] [+|-]value...
+This command allows the adjustment of the current log level. Refer
+to the Logging Facility section for further details.
+
+.It set login chat-script
+This 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 mru value
+The default MRU is 1500. If it is increased, the other side *may*
+increase its mtu. There is no use decreasing the MRU to below the
+default as the
+.Em PPP
+protocol *must* be able to accept packets of at
+least 1500 octets.
+
+.It set mtu value
+The default MTU is 1500. This may be increased by the MRU specified
+by the peer. It may only be subsequently decreased by this option.
+Increasing it is not valid as the peer is not necessarily able to
+receive the increased packet size.
+
+.It set openmode active|passive
+By default, openmode is always active. That is,
+.Nm
+will always initiate LCP/IPCP/CCP negotiation. If you want to wait for the
+peer to initiate negotiations, you may use the value
+.Dq passive .
+
+.It set parity odd|even|none|mark
+This allows the line parity to be set. The default value is none.
+
+.It set phone telno[|telno]...[:telno[|telno]...]...
+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 by a pipe (|) or
+a colon (:). Numbers after the pipe are only dialed if the dial or login
+script for the previous number failed. Numbers separated by a colon are
+tried sequentially, irrespective of the reason the line was dropped.
+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 reconnect timeout ntries
+Should the line drop unexpectedly (due to loss of CD or LQR
+failure), a connection will be re-established after the given
+.Dq timeout .
+The line will be re-connected at most
+.Dq ntries
+times.
+.Dq Ntries
+defaults to zero. A value of
+.Dq random
+for
+.Dq timeout
+will result in a variable pause, somewhere between 0 and 30 seconds.
+
+.It set redial seconds[.nseconds] [attempts]
+.Nm Ppp
+can be instructed to attempt to redial
+.Dq attempts
+times. If more than one number is specified (see
+.Dq set phone
+above), a pause of
+.Dq nseconds
+is taken before dialing each number. A pause of
+.Dq seconds
+is taken before starting at the first number again. A value of
+.Dq random
+may be used here too.
+
+.It set stopped [LCPseconds [IPCPseconds [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 you see
+.Nm
+failing to respond in the stopped state. Use
+.Dq set log +lcp +ipcp +ccp
+to make
+.Nm
+log all state transitions.
+.Pp
+The default value is zero, where
+.Nm
+doesn't time out in the stopped state.
+
+.It set server|socket TcpPort|LocalName|none [password] [mask]
+This command tells
+.Nm
+to listen on the given socket or
+.Sq diagnostic port
+for incoming command connections. This is not possible if
+.Nm
+is in interactive mode. The word
+.Ar none
+instructs
+.Nm
+to close any existing socket. If you wish to specify a unix 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 that
+should be used with unix domain sockets as a four character octal number
+beginning with
+.Sq 0 .
+Refer to
+.Xr umask 2
+for umask details. Refer to
+.Xr services 5
+for details of how to translate TCP port names.
+
+.Pp
+You may also specify the password that must be used by the client when
+connecting to this socket. If the password is not specified here,
+.Pa /etc/ppp/ppp.secret
+is searched for a machine name that's the same as your local host name
+without any domain suffix. Refer to
+.Xr hostname 1
+for further details. If a password is specified as the empty string,
+no password is required.
+
+.Pp
+When using
+.Nm
+with a server socket, the
+.Xr pppctl 8
+command is the preferred mechanism of communications. Currently,
+.Xr telnet 1
+can also be used, but link encryption may be implemented in the future, so
+.Xr telnet 1
+should not be relied upon.
+
+.It set speed value
+This sets the speed of the serial device.
+
+.It set timeout Idle [ lqr [ retry ] ]
+This command allows the setting of the idle timer, the LQR timer (if
+enabled) and the retry timer.
+
+.It set ns x.x.x.x y.y.y.y
+This option allows the setting of the Microsoft DNS servers that
+will be negotiated.
+
+.It set nbns x.x.x.x y.y.y.y
+This option allows the setting of the Microsoft NetBIOS DNS servers that
+will be negotiated.
+
+.It set help|?
+This command gives a summary of available set commands.
+.El
+
+.It shell|! [command]
+If
+.Dq command
+is not specified a shell is invoked according to the
+.Dv SHELL
+environment variable. Otherwise, the given command is executed.
+Any of the pseudo arguments
+.Dv HISADDR ,
+.Dv INTERFACE
+and
+.Dv MYADDR
+will be replaced with the appropriate values. Use of the ! character
+requires a following space as with any 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 var
+This command allows the user to examine the following:
+
+.Bl -tag -width 20
+.It show [adio]filter
+List the current rules for the given filter.
+
+.It show auth
+Show the current authname and authkey.
+
+.It show ccp
+Show the current CCP statistics.
+
+.It show compress
+Show the current compress statistics.
+
+.It show escape
+Show the current escape characters.
+
+.It show hdlc
+Show the current HDLC statistics.
+
+.It show ipcp
+Show the current IPCP statistics.
+
+.It show lcp
+Show the current LCP statistics.
+
+.It show loopback
+Show the current loopback status.
+
+.It show log
+Show the current log values.
+
+.It show mem
+Show current memory statistics.
+
+.It show modem
+Show current modem statistics.
+
+.It show mru
+Show the current MRU.
+
+.It show mtu
+Show the current MTU.
+
+.It show proto
+Show current protocol totals.
+
+.It show reconnect
+Show the current reconnect values.
+
+.It show redial
+Show the current redial values.
+
+.It show stopped
+Show the current stopped timeouts.
+
+.It show route
+Show the current routing tables.
+
+.It show timeout
+Show the current timeout values.
+
+.It show msext
+Show the current Microsoft extension values.
+
+.It show version
+Show the current version number of
+.Nm ppp .
+
+.It show help|?
+Give a summary of available show commands.
+.El
+
+.It term
+Go into terminal mode. Characters typed at the keyboard are sent to
+the modem. Characters read from the modem are displayed on the
+screen. When a
+.Nm
+peer is detected on the other side of the modem,
+.Nm
+automatically enables Packet Mode and goes back into command mode.
+
+.It alias .....
+This command allows the control of the aliasing (or masquerading)
+facilities that are built into
+.Nm ppp .
+Until this code is required, it is not loaded by
+.Nm ppp ,
+and it is quite possible that the alias library is not installed
+on your system (some administrators consider it a security risk).
+
+If aliasing is enabled on your system, the following commands are
+possible:
+
+.Bl -tag -width 20
+.It alias enable [yes|no]
+This command either switches aliasing on or turns it off.
+The
+.Fl alias
+command line flag is synonymous with
+.Dq alias enable yes .
+
+.It alias port [proto targetIP:targetPORT [aliasIP:]aliasPORT]
+This command allows us to redirect connections arriving at
+.Dq aliasPORT
+for machine [aliasIP] to
+.Dq targetPORT
+on
+.Dq targetIP .
+If proto is specified, only connections of the given protocol
+are matched. This option is useful if you wish to run things like
+Internet phone on the machines behind your gateway.
+
+.It alias addr [addr_local addr_alias]
+This command allows data for
+.Dq addr_alias
+to be redirected to
+.Dq addr_local .
+It is useful if you own a small number of real IP numbers that
+you wish to map to specific machines behind your gateway.
+
+.It alias deny_incoming [yes|no]
+If set to yes, this command will refuse all incoming connections
+by dropping the packets in much the same way as a firewall would.
+
+.It alias log [yes|no]
+This option causes various aliasing statistics and information to
+be logged to the file
+.Pa /var/log/alias.log .
+
+.It alias same_ports [yes|no]
+When enabled, this command will tell the alias library attempt to
+avoid changing the port number on outgoing packets. This is useful
+if you want to support protocols such as RPC and LPD which require
+connections to come from a well known port.
+
+.It alias use_sockets [yes|no]
+When enabled, this option tells the alias library to create a
+socket so that it can guarantee a correct incoming ftp data or
+IRC connection.
+
+.It alias unregistered_only [yes|no]
+Only alter outgoing packets with an unregistered source ad-
+dress. According to RFC 1918, unregistered source addresses
+are 10.0.0.0/8, 172.16.0.0/12 and 192.168.0.0/16.
+
+.It alias help|?
+This command gives a summary of available alias commands.
+
+.El
+
+.It quit|bye [all]
+Exit
+.Nm ppp .
+If
+.Nm
+is in interactive mode or if the
+.Dq all
+argument is given,
+.Nm
+will exit, closing the connection. A simple
+.Dq quit
+issued from a
+.Xr pppctl 8
+or
+.Xr telnet 1
+session will not close the current connection.
+
+.It help|? [command]
+Show a list of available commands. If
+.Dq command
+is specified, show the usage string for that command.
+
+.It down
+Bring the link down ungracefully. It's not considered polite to
+use this command.
+
+.El
+
+.Sh MORE DETAILS
+
+.Bl -bullet -compact
+
+.It
+Read the example configuration files. They are a good source of information.
+
+.It
+Use
+.Dq help ,
+.Dq show ? ,
+.Dq alias ? ,
+.Dq set ?
+and
+.Dq set ? <var>
+commands.
+.El
+
+.Sh FILES
+.Nm Ppp
+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 flag
+.It Pa /etc/ppp/ppp.conf
+System default configuration file.
+
+.It Pa /etc/ppp/ppp.secret
+An authorisation file for each system.
+
+.It Pa /etc/ppp/ppp.linkup
+A file to check when
+.Nm
+establishes a network level connection.
+
+.It Pa /etc/ppp/ppp.linkdown
+A file to check when
+.Nm
+closes a network level connection.
+
+.It Pa /var/log/ppp.log
+Logging and debugging information file. Note, this name is specified in
+.Pa /etc/syslogd.conf .
+See
+.Xr syslog.conf 5
+for further details.
+
+.It Pa /var/spool/lock/LCK..*
+tty port locking file. Refer to
+.Xr uucplock 8
+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. This file is only created in
+.Fl background ,
+.Fl auto
+and
+.Fl ddial
+modes.
+
+.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.
+.El
+
+.Sh SEE ALSO
+
+.Xr at 1 ,
+.Xr chat 8 ,
+.Xr crontab 5 ,
+.Xr ftp 1 ,
+.Xr getty 8 ,
+.Xr hostname 1 ,
+.Xr inetd 8 ,
+.Xr init 8 ,
+.Xr login 1 ,
+.Xr passwd 5 ,
+.Xr ping 8 ,
+.Xr pppctl 8 ,
+.Xr pppd 8 ,
+.Xr syslog 3 ,
+.Xr syslog.conf 5 ,
+.Xr syslogd 8 ,
+.Xr tcpdump 1 ,
+.Xr telnet 1 ,
+.Xr traceroute 8 ,
+.Xr uucplock 3 ,
+.Xr uucplock 8
+
+.Sh HISTORY
+
+This program was originally written by Toshiharu OHNO (tony-o@iij.ad.jp),
+and was submitted to FreeBSD-2.0.5 by Atsushi Murai (amurai@spec.co.jp).
+It has since had an enormous face lift and looks substantially different.
diff --git a/usr.sbin/ppp/ppp.8.m4 b/usr.sbin/ppp/ppp.8.m4
new file mode 100644
index 0000000..6eb42cd
--- /dev/null
+++ b/usr.sbin/ppp/ppp.8.m4
@@ -0,0 +1,2351 @@
+.\" $Id: ppp.8,v 1.76 1997/11/09 17:51:26 brian Exp $
+.Dd 20 September 1995
+.Os FreeBSD
+.Dt PPP 8
+.Sh NAME
+.Nm ppp
+.Nd
+Point to Point Protocol (a.k.a. iijppp)
+.Sh SYNOPSIS
+.Nm
+.Op Fl auto | background | ddial | direct | dedicated
+.Op Fl alias
+.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).
+
+.Sh Major Features
+
+.Bl -diag
+.It Provides interactive user interface.
+Using its command mode, the user can
+easily enter commands to establish the connection with the remote end, check
+the status of connection and close the connection. All functions can
+also be optionally password protected for security.
+
+.It Supports both manual and automatic dialing.
+Interactive mode has a
+.Dq term
+command which enables you to talk to your modem directly. When your
+modem is connected to the remote peer and it starts to talk
+.Em PPP ,
+.Nm
+detects it and switches to packet mode automatically. Once you have
+determined the proper sequence for connecting with the remote host, you
+can write a chat script to define the necessary dialing and login
+procedure for later convenience.
+
+.It Supports on-demand dialup capability.
+By using 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 ddial mode (dedicated or daemon dialing)
+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 who worry less about line charges
+and more about being connected full time.
+
+.It Supports packet aliasing.
+Packet aliasing (a.k.a. IP masquerading) allows computers on a
+private, unregistered network to access the Internet. The
+.Em PPP
+host acts as a masquerading gateway. IP addresses as well as TCP and
+UDP port numbers are aliased for outgoing packets and de-aliased for
+returning packets.
+
+.It Supports background PPP connections.
+In background mode, if
+.Nm
+successfully establishes the connection, it will become a daemon.
+Otherwise, it will exit with an error. This allows the setup of
+scripts that wish to execute certain commands only if the connection
+is successfully established.
+
+.It Supports server-side PPP connections.
+In direct mode,
+.nm
+acts as server which accepts incoming
+.Em PPP
+connections on stdin/stdout.
+
+.It Supports PAP and CHAP authentication.
+With PAP or CHAP, it is possible to skip the Unix style
+.Xr login 1
+proceedure, and use the
+.Em PPP
+protocol for authentication instead.
+
+.It Supports Proxy Arp.
+When
+.Em PPP
+is set up as server, you can also configure it to do proxy arp for your
+connection.
+
+.It Supports packet filtering.
+User can define four kinds of filters:
+.Em ifilter
+for incoming packets,
+.Em ofilter
+for outgoing packets,
+.Em dfilter
+to define a dialing trigger packet and
+.Em afilter
+for keeping a connection alive with the trigger packet.
+
+.It Tunnel driver supports bpf.
+The user can use
+.Xr tcpdump 1
+to check the packet flow over the
+.Em PPP
+link.
+
+.It Supports PPP over TCP capability.
+
+
+.It Supports IETF draft Predictor-1 compression.
+.Nm
+supports not only VJ-compression but also Predictor-1 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
+compression pre-compresses
+.Em all
+data flowing through the link, thus reducing overhead to a minimum.
+
+.It Supports Microsoft's IPCP extensions.
+Name Server Addresses and NetBIOS Name Server Addresses can be negotiated
+with clients using the Microsoft
+.Em PPP
+stack (ie. Win95, WinNT)
+
+.Sh PERMISSIONS
+.Nm Ppp
+is installed as user
+.Dv root
+and group
+.Dv network ,
+with permissions
+.Dv 4550 .
+.Nm Ppp
+will not execute in client mode if the invoking user id is not zero.
+.Nm Ppp
+will run in
+.Fl direct
+mode as a normal user, but due to its execution permissions, this user
+must be a member of group
+.Dv network .
+When running as a normal user,
+.Nm
+switches to user id 0 in order to alter the system routing table. All
+external commands (executed via the "shell" or "!bg" commands) are executed
+as the user id that invoked
+.Nm ppp .
+
+.Sh GETTING STARTED
+
+When you first run
+.Nm
+you may need to deal with some initial configuration details. First,
+your kernel should 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:
+
+.Dl pseudo-device tun N
+
+where
+.Ar N
+is the maximum number of
+.Em PPP
+connections you wish to support.
+
+Second, 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 .
+
+Last of all, create a log file.
+.Nm Ppp
+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:
+
+.Dl !ppp
+.Dl *.*<TAB>/var/log/ppp.log
+
+Make sure you use actual TABs here. If you use spaces, the line will be
+silently ignored.
+
+It is possible to have more than one
+.Em PPP
+log file by creating a link to the
+.Nm
+executable:
+
+.Dl # cd /usr/sbin
+.Dl # ln ppp ppp0
+
+and using
+
+.Dl !ppp0
+.Dl *.* /var/log/ppp0.log
+
+in
+.Pa /etc/syslog.conf .
+Don't forget to send a
+.Dv HUP
+signal to
+.Xr syslogd 8
+after altering
+.Pa /etc/syslog.conf .
+
+.Sh MANUAL DIALING
+
+In the following examples, we assume that your machine name is
+.Dv awfulhak .
+
+If you set your host name and password in
+.Pa /etc/ppp/ppp.secret ,
+you can't do anything except run the help, passwd and quit commands.
+
+.Bd -literal -offset indent
+ppp on "your host name"> help
+ help : Display this message
+ passwd : Password for security
+ quit : Quit the PPP program
+ppp on awfulhak> pass <password>
+.Ed
+
+The "on" part of your prompt will change to "ON" if you specify the
+correct password.
+
+.Bd -literal -offset indent
+ppp ON awfulhak>
+.Ed
+
+You can now specify the device name, speed and parity for your modem,
+and whether CTS/RTS signalling should be used (CTS/RTS is used by
+default). If your hardware does not provide CTS/RTS lines (as
+may happen when you are connected directly to certain PPP-capable
+terminal servers),
+.Nm
+will never send any output through the port; it waits for a signal
+which never comes. Thus, if you have a direct line and can't seem
+to make a connection, try turning CTS/RTS off:
+
+
+.Bd -literal -offset indent
+ppp ON awfulhak> set line /dev/cuaa0
+ppp ON awfulhak> set speed 38400
+ppp ON awfulhak> set parity even
+ppp ON awfulhak> set ctsrts on
+ppp ON awfulhak> show modem
+
+* Modem related information is shown here *
+
+ppp ON awfulhak>
+.Ed
+
+The term command can now be used to talk directly with your modem:
+
+.Bd -literal -offset indent
+ppp ON awfulhak> term
+at
+OK
+atdt123456
+CONNECT
+login: ppp
+Password:
+Protocol: ppp
+.Ed
+
+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>
+PPP ON awfulhak>
+.Ed
+
+You are now connected! Note that
+.Sq PPP
+in the prompt has changed to capital letters to indicate that you have
+a peer connection. The show command can be used to see how things are
+going:
+
+.Bd -literal -offset indent
+PPP ON awfulhak> show lcp
+
+* LCP related information is shown here *
+
+PPP ON awfulhak> show ipcp
+
+* IPCP related information is shown here *
+.Ed
+
+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 0 0 HISADDR
+.Ed
+
+The string
+.Sq HISADDR
+represents the IP address of the connected peer. This variable is only
+available once a connection has been established. A common error
+is to specify the above command in your
+.Pa /etc/ppp/ppp.conf
+file. This won't work as the remote IP address hasn't been
+established when this file is read.
+
+You can now use your network applications (ping, telnet, ftp etc.)
+in other windows on your machine.
+
+Refer to the
+.Em 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 /etc/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 -compact
+.It
+A line starting with a
+.Pq Dq #
+character is treated as a comment line.
+
+.It
+An inclusion is a line beginning with the word
+.Sq !include .
+It must have one argument - the file to include. You may wish to
+.Dq !include ~/.ppp.conf
+for compatibility with older versions of
+.Nm ppp .
+
+.It
+A label name starts in the first column and is followed by
+a colon
+.Pq Dq \&: .
+
+.It
+A command line must contain a space or tab in the first column.
+.El
+
+.Pp
+The
+.Pa /etc/ppp/ppp.conf
+file should consist of at least a
+.Dq default
+section. This section is always executed. It should also contain
+one or more sections, named according to their purpose, for example,
+.Dq MyISP
+would represent your ISP, and
+.Dq ppp-in
+would represent an incoming
+.Nm
+configuration.
+
+You can now specify the destination label name when you invoke
+.Nm ppp .
+Commands associated with the
+.Dq default
+label are executed, followed by those associated with the destination
+label provided. When
+.Nm
+is started with no arguments, the
+.Dq default
+section is still executed. The load command can be used to manually
+load a section from the
+.Pa /etc/ppp/ppp.conf
+file:
+
+.Bd -literal -offset indent
+PPP ON awfulhak> load MyISP
+.Ed
+
+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
+dial OK!
+login OK!
+PPP ON awfulhak>
+.Ed
+
+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 /etc/ppp/ppp.conf.sample
+which adds a default route. The strings
+.Dv HISADDR ,
+.Dv MYADDR
+and
+.Dv INTERFACE
+are available as the relevent IP addresses and interface name.
+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 .
+
+.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. You must also specify the destination label in
+.Pa /etc/ppp/ppp.conf
+to use. This label must contain the
+.Dq set ifaddr
+command to define the remote peers IP address. (refer to
+.Pa /etc/ppp/ppp.conf.sample )
+
+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 /etc/ppp/ppp.conf.sample )
+
+.Bd -literal -offset indent
+# ppp -auto pmdemand
+...
+#
+.Ed
+
+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 diagnostic port as follows (this
+can be done in
+.Fl background
+and
+.Fl direct
+mode too):
+
+.Bd -literal -offset indent
+# pppctl -v 3000 show ipcp
+Password:
+IPCP [Opened]
+ his side: xxxx
+ ....
+.Ed
+
+Currently,
+.Xr telnet 1
+may also be used to talk interactively.
+
+.Pp
+In order to achieve this, you must use the
+.Dq set server
+command as described below. It is possible to retrospectively make a running
+.Nm
+program listen on a diagnostic port by configuring
+.Pa /etc/ppp/ppp.secret ,
+and sending it a
+.Dv USR1
+signal.
+
+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 with
+.Bd -literal -offset indent
+set redial seconds|random[.nseconds|random] [dial_attempts]
+.Ed
+.Pp
+.Sq Seconds
+is the number of seconds to wait before attempting
+to connect again. If the argument is
+.Sq random ,
+the delay period is a random value between 0 and 30 seconds.
+.Sq Nseconds
+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
+.Sq random ,
+the delay period is a random value between 0 and 30 seconds.
+.Sq dial_attempts
+is the number of times to try to connect for each outgoing packet
+that is received. The previous value is unchanged if this parameter
+is omitted. If a value of zero is specified for
+.Sq dial_attempts ,
+.Nm
+will keep trying until a connection is made.
+.Bd -literal -offset indent
+set redial 10.3 4
+.Ed
+.Pp
+will attempt to connect 4 times for each outgoing packet that is
+detected 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).
+
+Modifying the dial delay is very useful when running
+.Nm
+in demand
+dial 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
+
+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
+
+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
+
+ PPP ON awfulhak> close
+ ppp ON awfulhak> quit all
+
+.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
+
+.It
+Edit
+.Pa /etc/ttys
+to enable a
+.Xr getty 8
+on the port where the modem is attached.
+
+For example:
+
+.Dl ttyd1 "/usr/libexec/getty std.38400" dialup on secure
+
+Don't forget to send a
+.Dv HUP
+signal to the
+.Xr init 8
+process to start the
+.Xr getty 8 .
+
+.Dl # kill -HUP 1
+
+.It
+Prepare an account for the incoming user.
+.Bd -literal
+ppp:xxxx:66:66:PPP Login User:/home/ppp:/usr/local/bin/ppplogin
+.Ed
+
+.It
+Create a
+.Pa /usr/local/bin/ppplogin
+file with the following contents:
+.Bd -literal -offset indent
+#!/bin/sh -p
+exec /usr/sbin/ppp -direct
+.Ed
+
+(You can specify a label name for further control.)
+
+.Pp
+Direct mode
+.Pq Fl direct
+lets
+.Nm
+work with stdin and stdout. You can also use
+.Xr pppctl 8
+or
+.Xr telnet 1
+to connect to a configured diagnostic port, in the same manner as with
+client-side
+.Nm ppp .
+
+.It
+Optional support for Microsoft's IPCP Name Server and NetBIOS
+Name Server negotiation can be enabled use
+.Dq enable msext
+and
+.Dq set ns pri-addr [sec-addr]
+along with
+.Dq set nbns pri-addr [sec-addr]
+in your
+.Pa /etc/ppp/ppp.conf
+file.
+
+.El
+
+.Sh RECEIVING INCOMING PPP CONNECTIONS (Method 2)
+
+This method differs in that it recommends the use of
+.Em mgetty+sendfax
+to handle the modem connections. The latest versions (0.99 and higher)
+can be compiled with the
+.Dq AUTO_PPP
+option to allow detection of clients speaking
+.Em PPP
+to the login prompt.
+
+Follow these steps:
+
+.Bl -enum
+
+.It
+Get, configure, and install mgetty+sendfax v0.99 or later making
+sure you have used the AUTO_PPP option.
+
+.It
+Edit
+.Pa /etc/ttys
+to enable a mgetty on the port where the modem is attached. For
+example:
+
+.Dl cuaa1 "/usr/local/sbin/mgetty -s 57600" dialup on
+
+.It
+Prepare an account for the incoming user.
+.Bd -literal
+Pfred:xxxx:66:66:Fred's PPP:/home/ppp:/etc/ppp/ppp-dialup
+.Ed
+
+.It
+Examine the files
+.Pa /etc/ppp/sample.ppp-dialup ,
+.Pa /etc/ppp/sample.ppp-pap-dialup
+and
+.Pa /etc/ppp/ppp.conf.sample
+for ideas.
+.Pa /etc/ppp/ppp-pap-dialup
+is supposed to be called from
+.Pa /usr/local/etc/mgetty+sendfax/login.conf
+from a line like
+
+.Dl /AutoPPP/ - - /etc/ppp/ppp-pap-dialup
+.El
+
+.Sh PPP OVER TCP (a.k.a Tunneling)
+
+Instead of running
+.Nm
+over a serial link, it is possible to
+use a TCP connection instead by specifying a host and port as the
+device:
+
+.Dl set device ui-gate:6669
+
+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:
+
+.Dl ppp-in 6669/tcp # Incoming PPP connections over TCP
+
+and updating
+.Pa /etc/inetd.conf
+to tell
+.Xr inetd 8
+how to deal with incoming connections on that port:
+
+.Dl ppp-in stream tcp nowait root /usr/sbin/ppp ppp -direct ppp-in
+
+Don't forget to send a
+.Dv HUP
+signal to
+.Xr inetd 8
+after you've updated
+.Pa /etc/inetd.conf .
+
+Here, we use a label named
+.Dq ppp-in .
+The entry in
+.Pa /etc/ppp/ppp.conf
+on ui-gate (the receiver) should contain the following:
+
+.Bd -literal -offset indent
+ppp-in:
+ set timeout 0
+ set ifaddr 10.0.4.1 10.0.4.2
+ add 10.0.1.0 255.255.255.0 10.0.4.1
+.Ed
+
+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
+The entry in
+.Pa /etc/ppp/ppp.conf
+on awfulhak (the initiator) should contain the following:
+
+.Bd -literal -offset indent
+ui-gate:
+ set escape 0xff
+ set device ui-gate:ppp-in
+ set dial
+ set timeout 30 5 4
+ set log Phase Chat Connect Carrier hdlc LCP IPCP CCP tun
+ set ifaddr 10.0.4.2 10.0.4.1
+ add 10.0.2.0 255.255.255.0 10.0.4.2
+.Ed
+.Pp
+Again, if you're enabling PAP, you'll also need:
+.Bd -literal -offset indent
+ set authname MyAuthName
+ set authkey MyAuthKey
+.Ed
+
+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
+
+.Dl awfulhak # ppp -background ui-gate
+
+The result will be an additional "route" on awfulhak to the
+10.0.2.0/24 network via the TCP connection, and an additional
+"route" on ui-gate to the 10.0.1.0/24 network.
+
+The networks are effectively bridged - the underlying TCP
+connection may be across a public network (such as the
+Internet), and the
+.Em PPP
+traffic is conceptually encapsulated
+(although not packet by packet) inside the TCP stream between
+the two gateways.
+
+The major disadvantage of this mechanism is that there are two
+"guaranteed delivery" mechanisms in place - the underlying TCP
+stream and whatever protocol is used over the
+.Em PPP
+link - probably TCP again. If packets are lost, both levels will
+get in each others way trying to negotiate sending of the missing
+packet.
+
+.Sh PACKET ALIASING
+
+The
+.Fl alias
+command line option enables packet aliasing. This allows the
+.Nm
+host to act as a masquerading gateway for other computers over
+a local area network. Outgoing IP packets are aliased so that
+they appear to come from the
+.Nm
+host, and incoming packets are de-aliased so that they are routed
+to the correct machine on the local area network.
+
+Packet aliasing allows computers on private, unregistered
+subnets to have Internet access, although they are invisible
+from the outside world.
+
+In general, correct
+.Nm
+operation should first be verified with packet aliasing disabled.
+Then, the
+.Fl alias
+option should be switched on, and network applications (web browser,
+.Xr telnet 1 ,
+.Xr ftp 1 ,
+.Xr ping 8 ,
+.Xr traceroute 8 )
+should be checked on the
+.Nm
+host. Finally, the same or similar applications should be checked on other
+computers in the LAN.
+
+If network applications work correctly on the
+.Nm
+host, but not on other machines in the LAN, then the masquerading
+software is working properly, but the host is either not forwarding
+or possibly receiving IP packets. Check that IP forwarding is enabled in
+.Pa /etc/rc.conf
+and that other machines have designated the
+.Nm
+host as the gateway for the LAN.
+
+.Sh PACKET FILTERING
+
+This implementation supports packet filtering. There are four kinds of
+filters; ifilter, ofilter, dfilter and afilter. Here are the basics:
+
+.Bl -bullet -compact
+.It
+A filter definition has the following syntax:
+
+set filter-name rule-no action [src_addr/src_width] [dst_addr/dst_width]
+[proto [src [lt|eq|gt] port ]] [dst [lt|eq|gt] port] [estab]
+.Bl -enum
+.It
+.Sq filter-name
+should be one of ifilter, ofilter, dfilter or afilter.
+.It
+There are two actions:
+.Sq permit
+and
+.Sq deny .
+If a given packet
+matches the rule, the associated action is taken immediately.
+.It
+.Sq src_width
+and
+.Sq dst_width
+work like a netmask to represent an address range.
+.It
+.Sq proto
+must be one of icmp, udp or tcp.
+.It
+.Sq port number
+can be specified by number and service name from
+.Pa /etc/services .
+
+.El
+
+.It
+Each filter can hold up to 20 rules, starting from rule 0.
+The entire rule set is not effective until rule 0 is defined,
+ie. the default is to allow everything through.
+
+.It
+If no rule is matched to a packet, that packet will be discarded
+(blocked).
+
+.It
+Use
+.Dq set filter-name -1
+to flush all rules.
+
+.El
+
+See
+.Pa /etc/ppp/ppp.conf.filter.example .
+
+
+.Sh SETTING IDLE, LINE QUALITY REQUEST, RETRY TIMER
+
+To check/set idle timer, use the
+.Dq show timeout
+and
+.Dq set timeout [lqrtimer [retrytimer]]
+commands:
+
+.Bd -literal -offset indent
+ppp ON awfulhak> set timeout 600
+.Ed
+
+The timeout period is measured in seconds, the default values for which
+are timeout = 180 or 3 min, lqrtimer = 30sec and retrytimer = 3sec.
+To disable the idle timer function, use the command
+
+.Bd -literal -offset indent
+ppp ON awfulhak> set timeout 0
+.Ed
+
+In
+.Fl auto
+mode, an idle timeout causes the
+.Em PPP
+session to be
+closed, though the
+.Nm
+program itself remains running. Another trigger packet will cause it to
+attempt to reestablish the link.
+
+.Sh PREDICTOR-1 COMPRESSION
+
+This version supports CCP and Predictor type 1 compression based on
+the current IETF-draft specs. As a default behaviour,
+.Nm
+will attempt to use (or be willing to accept) this capability when the
+peer agrees (or requests it).
+
+To disable CCP/predictor functionality completely, use the
+.Dq disable pred1
+and
+.Dq deny pred1
+commands.
+
+.Sh CONTROLLING IP ADDRESS
+
+.Nm
+uses IPCP to negotiate IP addresses. Each side of the connection
+specifies the IP address that it's willing to use, and if the requested
+IP address is acceptable then
+.Nm
+returns ACK to the requester. Otherwise,
+.Nm
+returns NAK to suggest that the peer use a different IP address. When
+both sides of the connection agree to accept the received request (and
+send ACK), IPCP is set to the open state and a network level connection
+is established.
+
+To control this IPCP behaviour, this implementation has the
+.Dq set ifaddr
+command for defining the local and remote IP address:
+
+.Bd -literal -offset indent
+set ifaddr [src_addr [dst_addr [netmask [trigger_addr]]]]
+.Ed
+
+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
+and
+.Sq dst_addr
+default 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.
+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
+
+The above specification means:
+.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
+
+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 obey the direction from it.
+
+In order to allow more flexible behaviour, `ifaddr' variable allows the
+user to specify IP address more loosely:
+
+.Dl set ifaddr 192.244.177.38/24 192.244.177.2/20
+
+A number followed by a slash (/) represent the number of bits significant in
+the IP address. The above example signifies that:
+
+.Bl -bullet -compact
+.It
+I'd like to use 192.244.177.38 as my address if it is possible, but I'll
+also accept any IP address between 192.244.177.0 and 192.244.177.255.
+
+.It
+I'd like to make him use 192.244.177.2 as his own address, but I'll also
+permit him to use any IP address between 192.244.176.0 and
+192.244.191.255.
+
+.It
+As you may have already noticed, 192.244.177.2 is equivalent to saying
+192.244.177.2/32.
+
+.It
+As an exception, 0 is equivalent to 0.0.0.0/0, meaning that I have no
+preferred IP address and will obey the remote peers selection. When
+using zero, no routing table entries will be made until a connection
+is established.
+
+.It
+192.244.177.2/0 means that I'll accept/permit any IP address but I'll
+try to insist that 192.244.177.2 be used first.
+.El
+
+.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 (|) or a colon (:)
+.Bd -literal -offset indent
+set phone "111[|222]...[:333[|444]...]...
+.Ed
+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 \\"\\" 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.
+.It
+Expect nothing.
+.It
+Send ATZ.
+.It
+Expect OK. If that's not received, 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
+
+Once the connection is established, the login script is executed. This
+script is written in the same style as the dial script:
+.Bd -literal -offset indent
+set login "TIMEOUT 15 login:-\\\\r-login: awfulhak word: xxx 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 "xxx".
+.It
+Expect "ocol:" (the tail end of a "Protocol:" prompt).
+.It
+Send "PPP".
+.It
+Expect "HELLO".
+.El
+.Pp
+Login scripts vary greatly between ISPs.
+
+.It
+Use
+.Dq set line
+and
+.Dq set speed
+to specify your serial line and speed, for example:
+.Bd -literal -offset indent
+set line /dev/cuaa0
+set speed 115200
+.Ed
+.Pp
+Cuaa0 is the first serial port on FreeBSD. If you're running
+.Nm
+on OpenBSD, cua00 is the first. A speed of 115200 should be specified
+if you have a modem capable of bit rates of 28800 or more. In general,
+the serial speed should be about four times the modem speed.
+
+.It
+Use the
+.Dq set ifaddr
+command to define the IP address.
+.Bl -bullet
+.It
+If you know what IP address your provider uses, then use it as the remote
+address (dst_addr), otherwise choose something like 10.0.0.2/0 (see below).
+.It
+If your provider has assigned a particular IP address to you, then use
+it as your address (src_addr).
+.It
+If your provider assigns your address dynamically, choose a suitably
+unobtrusive and unspecific IP number as your address. 10.0.0.1/0 would
+be appropriate. The bit after the / specifies how many bits of the
+address you consider to be important, so if you wanted to insist on
+something in the class C network 1.2.3.0, you could specify 1.2.3.1/24.
+.It
+If you find that your ISP accepts the first IP number that you suggest,
+specify third and forth arguments of
+.Dq 0.0.0.0 .
+This will force your ISP to assign a number. (The third argument will
+be ignored as it is less restrictive than the default mask for your
+.Sq src_addr .
+.El
+.Pp
+An example for a connection where you don't know your IP number or your
+ISPs IP number would be:
+.Bd -literal -offset indent
+set ifaddr 10.10.10.10/0 10.10.11.11/0 0.0.0.0 0.0.0.0
+.Ed
+
+.It
+In most cases, your ISP will also be your default router. If this is
+the case, add the lines
+
+.Bd -literal -offset indent
+delete ALL
+add 0 0 HISADDR
+.Ed
+
+.Pp
+to
+.Pa /etc/ppp/ppp.conf .
+.Pp
+This tells
+.Nm
+to delete all non-direct routing entries for the tun interface that
+.Nm
+is running on, then to add a default route to 10.10.11.11.
+.Pp
+If you're using dynamic IP numbers, you must also put these two lines
+in the
+.Pa /etc/ppp/ppp.linkup
+file:
+
+.Bd -literal -offset indent
+delete ALL
+add 0 0 HISADDR
+.Ed
+
+HISADDR is a macro meaning the "other side"s IP number, and is
+available once an IP number has been agreed (using IPCP).
+Now, once a connection is established,
+.Nm
+will delete all non-direct interface routes, and add a default route
+pointing at the peers IP number. You should use the same label as the
+one used in
+.Pa /etc/ppp/ppp.conf .
+.Pp
+If commands are being typed interactively, the only requirement is
+to type
+.Bd -literal -offset indent
+add 0 0 HISADDR
+.Ed
+.Pp
+after a successful dial.
+
+.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.
+.El
+
+Please refer to
+.Pa /etc/ppp/ppp.conf.sample
+and
+.Pa /etc/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:
+
+.Bl -column SMMMMMM -offset indent
+.It Li Async Dump async level packet in hex
+.It Li Carrier Log Chat lines with 'CARRIER'
+.It Li CCP Generate a CCP packet trace
+.It Li Chat Generate Chat script trace log
+.It Li Command Log commands executed
+.It Li Connect Generate complete Chat log
+.It Li Debug Log (very verbose) debug information
+.It Li HDLC Dump HDLC packet in hex
+.It Li ID0 Log all function calls specifically made as user id 0.
+.It Li IPCP Generate an IPCP packet trace
+.It Li LCP Generate an LCP packet trace
+.It Li Link Log address assignments and link up/down events
+.It Li LQM Generate LQR report
+.It Li Phase Phase transition log output
+.It Li TCP/IP Dump all TCP/IP packets
+.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 LOG_WARNING.
+.It Li Error Output to both the terminal device and the log file using
+LOG_ERROR.
+.It Li Alert Output to the log file using 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 Carrier Link Phase .
+
+.Pp
+It is also possible to log directly to the screen. The syntax is
+the same except that the word
+.Dq local
+should immediately follow
+.Dq set log .
+The default is
+.Dq set log local
+(ie. no direct screen logging).
+
+.Pp
+If The first argument to
+.Dq set log Op local
+begins with a '+' or a '-' character, the current log levels are
+not cleared, for example:
+
+.Bd -literal -offset indent
+PPP ON awfulhak> set log carrier link phase
+PPP ON awfulhak> show log
+Log: Carrier Link Phase Warning Error Alert
+Local: Warning Error Alert
+PPP ON awfulhak> set log -link +tcp/ip -warning
+PPP ON awfulhak> set log local +command
+PPP ON awfulhak> show log
+Log: Carrier 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 Ppp
+deals with the following signals:
+
+.Bl -tag -width 20
+.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, when not in interactive mode, tells
+.Nm
+to close any existing server socket and open an Internet socket using
+port 3000 plus the current tunnel device number. This can only be
+achieved if a suitable local password is specified in
+.Pa /etc/ppp/ppp.secret .
+
+.It USR2
+This signal, tells
+.Nm
+to close any existing server socket.
+
+.El
+
+.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 20
+.It accept|deny|enable|disable 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 20
+.It vjcomp
+Default: Enabled and Accepted. This option decides if Van Jacobson
+header compression will be used.
+
+.It lqr
+Default: Disabled and Accepted. This option decides if Link Quality
+Requests will be sent. LQR is a protocol that allows
+.Nm
+to determine that the link is down without relying on the modems
+carrier detect.
+
+.It chap
+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. Refer to the description of the
+.Dq set encrypt
+command for further details.
+
+.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
+option 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 acfcomp
+Default: Enabled and Accepted. ACFComp stands for Address and Control
+Field Compression. Non LCP packets usually have very similar address
+and control fields - making them easily compressible.
+
+.It 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 pred1
+Default: Enabled and Accepted. This option decides if Predictor 1
+compression will be used.
+
+.It msext
+Default: Disabled. This option allows the use of Microsoft's
+.Em PPP
+extensions, supporting the negotiation of the DNS and the NetBIOS NS.
+Enabling this allows us to pass back the values given in "set ns"
+and "set nbns".
+
+.El
+The following options are not actually negotiated with the peer.
+Therefore, accepting or denying them makes no sense.
+
+.Bl -tag -width 20
+.It proxy
+Default: Disabled. Enabling this option will tell
+.Nm
+to proxy ARP for the peer.
+
+.It passwdauth
+Default: Disabled. Enabling this option will tell the PAP authentication
+code to use the password file (see
+.Xr passwd 5 )
+to authenticate the caller rather than the
+.Pa /etc/ppp/ppp.secret
+file.
+
+.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
+
+.It add dest mask gateway
+.Dq Dest
+is the destination IP address and
+.Dq mask
+is its mask.
+.Dq 0 0
+refers to the default route.
+.Dq Gateway
+is the next hop gateway to get to the given
+.Dq dest
+machine/network.
+
+.It [!]bg command
+The given command is executed in the background.
+Any of the pseudo arguments
+.Dv HISADDR ,
+.Dv INTERFACE
+and
+.Dv MYADDR
+will be replaced with the appropriate values. If you wish to pause
+.Nm
+while the command executes, use the
+.Dv shell
+command instead.
+
+.It close
+Close the current connection (but don't quit).
+
+.It delete ALL | dest [gateway [mask]]
+If
+.Dq ALL
+is specified, all non-direct entries in the routing for the interface
+that
+.Nm
+is using are deleted. This means all entries for tunN, except the entry
+representing the actual link. When
+.Dq ALL
+is not used, any existing route with the given
+.Dq dest ,
+destination network
+.Dq mask
+and
+.Dq gateway
+is deleted. The default
+.Dq mask
+value is 0.0.0.0.
+
+.It dial|call [remote]
+If
+.Dq remote
+is specified, a connection is established using the
+.Dq dial
+and
+.Dq login
+scripts for the given
+.Dq remote
+system. Otherwise, the current settings are used to establish
+the connection.
+
+.It display
+Displays the current status of the negotiable protocol
+values as specified under
+.Dq accept|deny|enable|disable option....
+above.
+
+.It passwd pass
+Specify the password required for access to the full
+.Nm
+command set.
+
+.It load [remote]
+Load the given
+.Dq remote
+label. If
+.Dq remote
+is not given, the
+.Dq default
+label is assumed.
+
+.It save
+This option is not (yet) implemented.
+
+.It set[up] var value
+This option allows the setting of any of the following variables:
+
+.Bl -tag -width 20
+.It set accmap hex-value
+ACCMap stands for Asyncronous Control Character Map. This is always
+negotiated with the peer, and defaults to a value of 0x00000000.
+This protocol is required to defeat hardware that depends on passing
+certain characters from end to end (such as XON/XOFF etc).
+
+.It set filter-name rule-no action [src_addr/src_width]
+[dst_addr/dst_width] [proto [src [lt|eq|gt] port ]]
+[dst [lt|eq|gt] port] [estab]
+.Pp
+.Nm Ppp
+supports four filter sets. The afilter specifies packets that keep
+the connection alive - reseting the idle timer. The dfilter specifies
+packets that cause
+.Nm
+to dial when in
+.Fl auto
+mode. The ifilter specifies packets that are allowed to travel
+into the machine and the ofilter specifies packets that are allowed
+out of the machine. By default all filter sets allow all packets
+to pass.
+
+Rules are processed in order according to
+.Dq n .
+Up to 20 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
+ifilters and ofilters, this means that the packet is dropped. In
+the case of afilters it means that the packet will not reset the
+idle timer and in the case of dfilters it means that the packet will
+not trigger a dial.
+
+Refer to the section on PACKET FILTERING above for further details.
+
+.It set authkey|key value
+This sets the authentication key (or password) used in client mode
+PAP or CHAP negotiation to the given value. It can also be used to
+specify the password to be used in the dial or login scripts, preventing
+the actual password from being logged.
+
+.It set authname id
+This sets the authentication id used in client mode PAP or CHAP negotiation.
+
+.It set ctsrts
+This sets hardware flow control and is the default.
+
+.It set device|line value
+This sets the device to which
+.Nm
+will talk to the given
+.Dq value .
+All serial device names are expected to begin with
+.Pa /dev/ .
+If
+.Dq value
+does not begin with
+.Pa /dev/ ,
+it must be of the format
+.Dq host:port .
+If this is the case,
+.Nm
+will attempt to connect to the given
+.Dq host
+on the given
+.Dq port .
+Refer to the section on
+.Em PPP OVER TCP
+above for further details.
+
+.It set dial chat-script
+This specifies the chat script that will be used to dial the other
+side. See also the
+.Dv set login
+command below. Refer to
+.Xr chat 8
+and to the example configuration files for details of the chat script
+format. The string \\\\T will be replaced with the current phone number
+(see
+.Dq set phone
+below) and the string \\\\P will be replaced with the password (see
+.Dq set key
+above).
+
+.It set hangup chat-script
+This specifies the chat script that will be used to reset the modem
+before it is closed. It should not normally be necessary, but can
+be used for devices that fail to reset themselves properly on close.
+
+.It set encrypt MSChap|MD5
+This specifies the encryption algorithm to request and use when issuing
+the CHAP challenge, and defaults to MD5. If this is set to MSChap,
+.Nm
+will behave like a Microsoft RAS when sending the CHAP challenge (assuming
+CHAP is enabled). When responding to a challenge,
+.Nm
+determines how to encrypt the response based on the challenge, so this
+setting is ignored.
+
+.Bl -tag -width NOTE:
+.It NOTE:
+Because the Microsoft encryption algorithm uses a combination of MD4 and DES,
+if you have not installed DES encryption software on your machine
+before building
+.Nm ppp ,
+this option will not be available - only MD5 will be used.
+.El
+
+.It set escape 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 `escaped' as they travel across the link.
+
+.It set ifaddr [myaddr [hisaddr [netmask [triggeraddr]]]]
+This command specifies the IP addresses that will be used during
+IPCP negotiation. Addresses are specified using the format
+
+.Dl a.b.c.d/n
+
+Where a.b.c.d is the preferred IP, but n specifies how many bits
+of the address we will insist on. If the /n bit is omitted, it
+defaults to /32 unless the IP address is 0.0.0.0 in which case
+the mask defaults to /0.
+
+If
+.Dq triggeraddr
+is specified, it is used in place of
+.Dq myaddr
+in the initial IPCP negotiation. However, only an address in the
+.Dq myaddr
+range will be accepted.
+
+.It set loopback on|off
+When set to
+.Dq on
+(the default),
+.Nm
+will automatically loop back packets being sent
+out with a destination address equal to that of the
+.Em PPP
+interface. If set to
+.Dq off ,
+.Nm
+will send the packet, probably resulting in an ICMP redirect from
+the other end.
+
+.It set log [local] [+|-]value...
+This command allows the adjustment of the current log level. Refer
+to the Logging Facility section for further details.
+
+.It set login chat-script
+This 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 mru value
+The default MRU is 1500. If it is increased, the other side *may*
+increase its mtu. There is no use decreasing the MRU to below the
+default as the
+.Em PPP
+protocol *must* be able to accept packets of at
+least 1500 octets.
+
+.It set mtu value
+The default MTU is 1500. This may be increased by the MRU specified
+by the peer. It may only be subsequently decreased by this option.
+Increasing it is not valid as the peer is not necessarily able to
+receive the increased packet size.
+
+.It set openmode active|passive
+By default, openmode is always active. That is,
+.Nm
+will always initiate LCP/IPCP/CCP negotiation. If you want to wait for the
+peer to initiate negotiations, you may use the value
+.Dq passive .
+
+.It set parity odd|even|none|mark
+This allows the line parity to be set. The default value is none.
+
+.It set phone telno[|telno]...[:telno[|telno]...]...
+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 by a pipe (|) or
+a colon (:). Numbers after the pipe are only dialed if the dial or login
+script for the previous number failed. Numbers separated by a colon are
+tried sequentially, irrespective of the reason the line was dropped.
+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 reconnect timeout ntries
+Should the line drop unexpectedly (due to loss of CD or LQR
+failure), a connection will be re-established after the given
+.Dq timeout .
+The line will be re-connected at most
+.Dq ntries
+times.
+.Dq Ntries
+defaults to zero. A value of
+.Dq random
+for
+.Dq timeout
+will result in a variable pause, somewhere between 0 and 30 seconds.
+
+.It set redial seconds[.nseconds] [attempts]
+.Nm Ppp
+can be instructed to attempt to redial
+.Dq attempts
+times. If more than one number is specified (see
+.Dq set phone
+above), a pause of
+.Dq nseconds
+is taken before dialing each number. A pause of
+.Dq seconds
+is taken before starting at the first number again. A value of
+.Dq random
+may be used here too.
+
+.It set stopped [LCPseconds [IPCPseconds [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 you see
+.Nm
+failing to respond in the stopped state. Use
+.Dq set log +lcp +ipcp +ccp
+to make
+.Nm
+log all state transitions.
+.Pp
+The default value is zero, where
+.Nm
+doesn't time out in the stopped state.
+
+.It set server|socket TcpPort|LocalName|none [password] [mask]
+This command tells
+.Nm
+to listen on the given socket or
+.Sq diagnostic port
+for incoming command connections. This is not possible if
+.Nm
+is in interactive mode. The word
+.Ar none
+instructs
+.Nm
+to close any existing socket. If you wish to specify a unix 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 that
+should be used with unix domain sockets as a four character octal number
+beginning with
+.Sq 0 .
+Refer to
+.Xr umask 2
+for umask details. Refer to
+.Xr services 5
+for details of how to translate TCP port names.
+
+.Pp
+You may also specify the password that must be used by the client when
+connecting to this socket. If the password is not specified here,
+.Pa /etc/ppp/ppp.secret
+is searched for a machine name that's the same as your local host name
+without any domain suffix. Refer to
+.Xr hostname 1
+for further details. If a password is specified as the empty string,
+no password is required.
+
+.Pp
+When using
+.Nm
+with a server socket, the
+.Xr pppctl 8
+command is the preferred mechanism of communications. Currently,
+.Xr telnet 1
+can also be used, but link encryption may be implemented in the future, so
+.Xr telnet 1
+should not be relied upon.
+
+.It set speed value
+This sets the speed of the serial device.
+
+.It set timeout Idle [ lqr [ retry ] ]
+This command allows the setting of the idle timer, the LQR timer (if
+enabled) and the retry timer.
+
+.It set ns x.x.x.x y.y.y.y
+This option allows the setting of the Microsoft DNS servers that
+will be negotiated.
+
+.It set nbns x.x.x.x y.y.y.y
+This option allows the setting of the Microsoft NetBIOS DNS servers that
+will be negotiated.
+
+.It set help|?
+This command gives a summary of available set commands.
+.El
+
+.It shell|! [command]
+If
+.Dq command
+is not specified a shell is invoked according to the
+.Dv SHELL
+environment variable. Otherwise, the given command is executed.
+Any of the pseudo arguments
+.Dv HISADDR ,
+.Dv INTERFACE
+and
+.Dv MYADDR
+will be replaced with the appropriate values. Use of the ! character
+requires a following space as with any 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 var
+This command allows the user to examine the following:
+
+.Bl -tag -width 20
+.It show [adio]filter
+List the current rules for the given filter.
+
+.It show auth
+Show the current authname and authkey.
+
+.It show ccp
+Show the current CCP statistics.
+
+.It show compress
+Show the current compress statistics.
+
+.It show escape
+Show the current escape characters.
+
+.It show hdlc
+Show the current HDLC statistics.
+
+.It show ipcp
+Show the current IPCP statistics.
+
+.It show lcp
+Show the current LCP statistics.
+
+.It show loopback
+Show the current loopback status.
+
+.It show log
+Show the current log values.
+
+.It show mem
+Show current memory statistics.
+
+.It show modem
+Show current modem statistics.
+
+.It show mru
+Show the current MRU.
+
+.It show mtu
+Show the current MTU.
+
+.It show proto
+Show current protocol totals.
+
+.It show reconnect
+Show the current reconnect values.
+
+.It show redial
+Show the current redial values.
+
+.It show stopped
+Show the current stopped timeouts.
+
+.It show route
+Show the current routing tables.
+
+.It show timeout
+Show the current timeout values.
+
+.It show msext
+Show the current Microsoft extension values.
+
+.It show version
+Show the current version number of
+.Nm ppp .
+
+.It show help|?
+Give a summary of available show commands.
+.El
+
+.It term
+Go into terminal mode. Characters typed at the keyboard are sent to
+the modem. Characters read from the modem are displayed on the
+screen. When a
+.Nm
+peer is detected on the other side of the modem,
+.Nm
+automatically enables Packet Mode and goes back into command mode.
+
+.It alias .....
+This command allows the control of the aliasing (or masquerading)
+facilities that are built into
+.Nm ppp .
+Until this code is required, it is not loaded by
+.Nm ppp ,
+and it is quite possible that the alias library is not installed
+on your system (some administrators consider it a security risk).
+
+If aliasing is enabled on your system, the following commands are
+possible:
+
+.Bl -tag -width 20
+.It alias enable [yes|no]
+This command either switches aliasing on or turns it off.
+The
+.Fl alias
+command line flag is synonymous with
+.Dq alias enable yes .
+
+.It alias port [proto targetIP:targetPORT [aliasIP:]aliasPORT]
+This command allows us to redirect connections arriving at
+.Dq aliasPORT
+for machine [aliasIP] to
+.Dq targetPORT
+on
+.Dq targetIP .
+If proto is specified, only connections of the given protocol
+are matched. This option is useful if you wish to run things like
+Internet phone on the machines behind your gateway.
+
+.It alias addr [addr_local addr_alias]
+This command allows data for
+.Dq addr_alias
+to be redirected to
+.Dq addr_local .
+It is useful if you own a small number of real IP numbers that
+you wish to map to specific machines behind your gateway.
+
+.It alias deny_incoming [yes|no]
+If set to yes, this command will refuse all incoming connections
+by dropping the packets in much the same way as a firewall would.
+
+.It alias log [yes|no]
+This option causes various aliasing statistics and information to
+be logged to the file
+.Pa /var/log/alias.log .
+
+.It alias same_ports [yes|no]
+When enabled, this command will tell the alias library attempt to
+avoid changing the port number on outgoing packets. This is useful
+if you want to support protocols such as RPC and LPD which require
+connections to come from a well known port.
+
+.It alias use_sockets [yes|no]
+When enabled, this option tells the alias library to create a
+socket so that it can guarantee a correct incoming ftp data or
+IRC connection.
+
+.It alias unregistered_only [yes|no]
+Only alter outgoing packets with an unregistered source ad-
+dress. According to RFC 1918, unregistered source addresses
+are 10.0.0.0/8, 172.16.0.0/12 and 192.168.0.0/16.
+
+.It alias help|?
+This command gives a summary of available alias commands.
+
+.El
+
+.It quit|bye [all]
+Exit
+.Nm ppp .
+If
+.Nm
+is in interactive mode or if the
+.Dq all
+argument is given,
+.Nm
+will exit, closing the connection. A simple
+.Dq quit
+issued from a
+.Xr pppctl 8
+or
+.Xr telnet 1
+session will not close the current connection.
+
+.It help|? [command]
+Show a list of available commands. If
+.Dq command
+is specified, show the usage string for that command.
+
+.It down
+Bring the link down ungracefully. It's not considered polite to
+use this command.
+
+.El
+
+.Sh MORE DETAILS
+
+.Bl -bullet -compact
+
+.It
+Read the example configuration files. They are a good source of information.
+
+.It
+Use
+.Dq help ,
+.Dq show ? ,
+.Dq alias ? ,
+.Dq set ?
+and
+.Dq set ? <var>
+commands.
+.El
+
+.Sh FILES
+.Nm Ppp
+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 flag
+.It Pa /etc/ppp/ppp.conf
+System default configuration file.
+
+.It Pa /etc/ppp/ppp.secret
+An authorisation file for each system.
+
+.It Pa /etc/ppp/ppp.linkup
+A file to check when
+.Nm
+establishes a network level connection.
+
+.It Pa /etc/ppp/ppp.linkdown
+A file to check when
+.Nm
+closes a network level connection.
+
+.It Pa /var/log/ppp.log
+Logging and debugging information file. Note, this name is specified in
+.Pa /etc/syslogd.conf .
+See
+.Xr syslog.conf 5
+for further details.
+
+.It Pa /var/spool/lock/LCK..*
+tty port locking file. Refer to
+.Xr uucplock 8
+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. This file is only created in
+.Fl background ,
+.Fl auto
+and
+.Fl ddial
+modes.
+
+.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.
+.El
+
+.Sh SEE ALSO
+
+.Xr at 1 ,
+.Xr chat 8 ,
+.Xr crontab 5 ,
+.Xr ftp 1 ,
+.Xr getty 8 ,
+.Xr hostname 1 ,
+.Xr inetd 8 ,
+.Xr init 8 ,
+.Xr login 1 ,
+.Xr passwd 5 ,
+.Xr ping 8 ,
+.Xr pppctl 8 ,
+.Xr pppd 8 ,
+.Xr syslog 3 ,
+.Xr syslog.conf 5 ,
+.Xr syslogd 8 ,
+.Xr tcpdump 1 ,
+.Xr telnet 1 ,
+.Xr traceroute 8 ,
+.Xr uucplock 3 ,
+.Xr uucplock 8
+
+.Sh HISTORY
+
+This program was originally written by Toshiharu OHNO (tony-o@iij.ad.jp),
+and was submitted to FreeBSD-2.0.5 by Atsushi Murai (amurai@spec.co.jp).
+It has since had an enormous face lift and looks substantially different.
diff --git a/usr.sbin/ppp/pred.c b/usr.sbin/ppp/pred.c
new file mode 100644
index 0000000..025120f
--- /dev/null
+++ b/usr.sbin/ppp/pred.c
@@ -0,0 +1,221 @@
+/*
+ * pred.c -- Test program for Dave Rand's rendition of the
+ * predictor algorithm
+ * Updated by: iand@labtam.labtam.oz.au (Ian Donaldson)
+ * Updated by: Carsten Bormann <cabo@cs.tu-berlin.de>
+ * Original : Dave Rand <dlr@bungi.com>/<dave_rand@novell.com>
+ *
+ * $Id: pred.c,v 1.15 1997/10/26 01:03:34 brian Exp $
+ *
+ */
+
+#include <sys/types.h>
+#include <netinet/in.h>
+
+#include <stdio.h>
+#include <string.h>
+
+#include "mbuf.h"
+#include "log.h"
+#include "defs.h"
+#include "timer.h"
+#include "fsm.h"
+#include "hdlc.h"
+#include "lcpproto.h"
+#include "ccp.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 IHASH(x) do {iHash = (iHash << 4) ^ (x);} while(0)
+#define OHASH(x) do {oHash = (oHash << 4) ^ (x);} while(0)
+
+static unsigned short int iHash, oHash;
+static unsigned char InputGuessTable[65536];
+static unsigned char OutputGuessTable[65536];
+
+static int
+compress(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 (OutputGuessTable[oHash] == *source) {
+ flags |= bitmask; /* Guess was right - don't output */
+ } else {
+ OutputGuessTable[oHash] = *source;
+ *dest++ = *source; /* Guess wrong, output char */
+ }
+ OHASH(*source++);
+ len--;
+ }
+ *flagdest = flags;
+ }
+ return (dest - orgdest);
+}
+
+static void
+SyncTable(u_char * source, u_char * dest, int len)
+{
+
+ while (len--) {
+ if (InputGuessTable[iHash] != *source) {
+ InputGuessTable[iHash] = *source;
+ }
+ IHASH(*dest++ = *source++);
+ }
+}
+
+static int
+decompress(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 = InputGuessTable[iHash]; /* Guess correct */
+ } else {
+ if (!len)
+ break; /* we seem to be really done -- cabo */
+ InputGuessTable[iHash] = *source; /* Guess wrong */
+ *dest = *source++; /* Read from source */
+ len--;
+ }
+ IHASH(*dest++);
+ }
+ }
+ return (dest - orgdest);
+}
+
+void
+Pred1Init(int direction)
+{
+ if (direction & 1) { /* Input part */
+ iHash = 0;
+ memset(InputGuessTable, '\0', sizeof(InputGuessTable));
+ }
+ if (direction & 2) { /* Output part */
+ oHash = 0;
+ memset(OutputGuessTable, '\0', sizeof(OutputGuessTable));
+ }
+}
+
+void
+Pred1Output(int pri, u_short proto, struct mbuf * bp)
+{
+ struct mbuf *mwp;
+ u_char *cp, *wp, *hp;
+ int orglen, len;
+ u_char bufp[MAX_MTU + 2];
+ u_short fcs;
+
+ orglen = plength(bp) + 2; /* add count of proto */
+ mwp = mballoc((orglen + 2) / 8 * 9 + 12, MB_HDLCOUT);
+ hp = wp = MBUF_CTOP(mwp);
+ cp = bufp;
+ *wp++ = *cp++ = orglen >> 8;
+ *wp++ = *cp++ = orglen & 0377;
+ *cp++ = proto >> 8;
+ *cp++ = proto & 0377;
+ mbread(bp, cp, orglen - 2);
+ fcs = HdlcFcs(INITFCS, bufp, 2 + orglen);
+ fcs = ~fcs;
+
+ len = compress(bufp + 2, wp, orglen);
+ LogPrintf(LogDEBUG, "Pred1Output: orglen (%d) --> len (%d)\n", orglen, len);
+ CcpInfo.orgout += orglen;
+ if (len < orglen) {
+ *hp |= 0x80;
+ wp += len;
+ CcpInfo.compout += len;
+ } else {
+ memcpy(wp, bufp + 2, orglen);
+ wp += orglen;
+ CcpInfo.compout += orglen;
+ }
+
+ *wp++ = fcs & 0377;
+ *wp++ = fcs >> 8;
+ mwp->cnt = wp - MBUF_CTOP(mwp);
+ HdlcOutput(PRI_NORMAL, PROTO_COMPD, mwp);
+}
+
+void
+Pred1Input(struct mbuf * bp)
+{
+ u_char *cp, *pp;
+ int len, olen, len1;
+ struct mbuf *wp;
+ u_char *bufp;
+ u_short fcs, proto;
+
+ wp = mballoc(MAX_MTU + 2, MB_IPIN);
+ cp = MBUF_CTOP(bp);
+ olen = plength(bp);
+ pp = bufp = MBUF_CTOP(wp);
+ *pp++ = *cp & 0177;
+ len = *cp++ << 8;
+ *pp++ = *cp;
+ len += *cp++;
+ CcpInfo.orgin += len & 0x7fff;
+ if (len & 0x8000) {
+ len1 = decompress(cp, pp, olen - 4);
+ CcpInfo.compin += olen;
+ len &= 0x7fff;
+ if (len != len1) { /* Error is detected. Send reset request */
+ LogPrintf(LogLCP, "%s: Length Error\n", CcpFsm.name);
+ CcpSendResetReq(&CcpFsm);
+ pfree(bp);
+ pfree(wp);
+ return;
+ }
+ cp += olen - 4;
+ pp += len1;
+ } else {
+ CcpInfo.compin += len;
+ SyncTable(cp, pp, len);
+ cp += len;
+ pp += len;
+ }
+ *pp++ = *cp++; /* CRC */
+ *pp++ = *cp++;
+ fcs = HdlcFcs(INITFCS, bufp, wp->cnt = pp - bufp);
+ if (fcs != GOODFCS)
+ LogPrintf(LogDEBUG, "Pred1Input: fcs = 0x%04x (%s), len = 0x%x,"
+ " olen = 0x%x\n", fcs, (fcs == GOODFCS) ? "good" : "bad",
+ len, olen);
+ if (fcs == GOODFCS) {
+ wp->offset += 2; /* skip length */
+ wp->cnt -= 4; /* skip length & CRC */
+ pp = MBUF_CTOP(wp);
+ proto = *pp++;
+ if (proto & 1) {
+ wp->offset++;
+ wp->cnt--;
+ } else {
+ wp->offset += 2;
+ wp->cnt -= 2;
+ proto = (proto << 8) | *pp++;
+ }
+ DecodePacket(proto, wp);
+ } else {
+ LogDumpBp(LogHDLC, "Bad FCS", wp);
+ CcpSendResetReq(&CcpFsm);
+ pfree(wp);
+ }
+ pfree(bp);
+}
diff --git a/usr.sbin/ppp/pred.h b/usr.sbin/ppp/pred.h
new file mode 100644
index 0000000..9bcc21b
--- /dev/null
+++ b/usr.sbin/ppp/pred.h
@@ -0,0 +1,25 @@
+/*
+ * Written by Toshiharu OHNO (tony-o@iij.ad.jp)
+ *
+ * Copyright (C) 1993, Internet Initiative Japan, Inc. All rights reserverd.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the Internet Initiative Japan. The name of the
+ * IIJ may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: pred.h,v 1.5 1997/08/25 00:29:26 brian Exp $
+ *
+ * TODO:
+ */
+
+extern void Pred1Output(int, u_short, struct mbuf *);
+extern void Pred1Input(struct mbuf *);
+extern void Pred1Init(int);
diff --git a/usr.sbin/ppp/route.c b/usr.sbin/ppp/route.c
new file mode 100644
index 0000000..8ecb5c8
--- /dev/null
+++ b/usr.sbin/ppp/route.c
@@ -0,0 +1,433 @@
+/*
+ * PPP Routing related Module
+ *
+ * Written by Toshiharu OHNO (tony-o@iij.ad.jp)
+ *
+ * Copyright (C) 1994, Internet Initiative Japan, Inc. All rights reserverd.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the Internet Initiative Japan, Inc. The name of the
+ * IIJ may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: route.c,v 1.23 1997/11/09 06:22:47 brian Exp $
+ *
+ */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <net/route.h>
+#include <net/if.h>
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <errno.h>
+#include <machine/endian.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/sysctl.h>
+#include <unistd.h>
+
+#include "mbuf.h"
+#include "log.h"
+#include "loadalias.h"
+#include "command.h"
+#include "defs.h"
+#include "vars.h"
+#include "id.h"
+#include "route.h"
+
+static int IfIndex;
+
+struct rtmsg {
+ struct rt_msghdr m_rtm;
+ char m_space[64];
+};
+
+static int seqno;
+
+void
+OsSetRoute(int cmd,
+ struct in_addr dst,
+ struct in_addr gateway,
+ struct in_addr mask)
+{
+ struct rtmsg rtmes;
+ int s, nb, wb;
+ char *cp, *cmdstr;
+ u_long *lp;
+ struct sockaddr_in rtdata;
+
+ cmdstr = (cmd == RTM_ADD ? "Add" : "Delete");
+ s = ID0socket(PF_ROUTE, SOCK_RAW, 0);
+ if (s < 0) {
+ LogPrintf(LogERROR, "OsSetRoute: socket(): %s\n", strerror(errno));
+ return;
+ }
+ 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 | RTA_NETMASK;
+ rtmes.m_rtm.rtm_seq = ++seqno;
+ rtmes.m_rtm.rtm_pid = getpid();
+ rtmes.m_rtm.rtm_flags = RTF_UP | RTF_GATEWAY | RTF_STATIC;
+
+ memset(&rtdata, '\0', sizeof(rtdata));
+ rtdata.sin_len = 16;
+ rtdata.sin_family = AF_INET;
+ rtdata.sin_port = 0;
+ rtdata.sin_addr = dst;
+
+ cp = rtmes.m_space;
+ memcpy(cp, &rtdata, 16);
+ cp += 16;
+ if (gateway.s_addr) {
+ rtdata.sin_addr = gateway;
+ memcpy(cp, &rtdata, 16);
+ cp += 16;
+ rtmes.m_rtm.rtm_addrs |= RTA_GATEWAY;
+ }
+ if (dst.s_addr == INADDR_ANY)
+ mask.s_addr = INADDR_ANY;
+
+ lp = (u_long *) cp;
+
+ if (mask.s_addr) {
+ *lp++ = 8;
+ cp += sizeof(int);
+ *lp = mask.s_addr;
+ } else
+ *lp = 0;
+ cp += sizeof(u_long);
+
+ nb = cp - (char *) &rtmes;
+ rtmes.m_rtm.rtm_msglen = nb;
+ wb = write(s, &rtmes, nb);
+ if (wb < 0) {
+ LogPrintf(LogTCPIP, "OsSetRoute: Dst = %s\n", inet_ntoa(dst));
+ LogPrintf(LogTCPIP, "OsSetRoute: Gateway = %s\n", inet_ntoa(gateway));
+ LogPrintf(LogTCPIP, "OsSetRoute: Mask = %s\n", inet_ntoa(mask));
+ switch (rtmes.m_rtm.rtm_errno) {
+ case EEXIST:
+ LogPrintf(LogTCPIP, "Add route failed: Already exists\n");
+ break;
+ case ESRCH:
+ LogPrintf(LogTCPIP, "Del route failed: Non-existent\n");
+ break;
+ case 0:
+ LogPrintf(LogTCPIP, "%s route failed: %s\n", cmdstr, strerror(errno));
+ break;
+ case ENOBUFS:
+ default:
+ LogPrintf(LogTCPIP, "%s route failed: %s\n",
+ cmdstr, strerror(rtmes.m_rtm.rtm_errno));
+ break;
+ }
+ }
+ LogPrintf(LogDEBUG, "wrote %d: cmd = %s, dst = %x, gateway = %x\n",
+ wb, cmdstr, dst.s_addr, gateway.s_addr);
+ close(s);
+}
+
+static void
+p_sockaddr(struct sockaddr * sa, int width)
+{
+ if (VarTerm) {
+ register char *cp;
+ register struct sockaddr_in *sock_in = (struct sockaddr_in *) sa;
+
+ cp = (sock_in->sin_addr.s_addr == 0) ? "default" :
+ inet_ntoa(sock_in->sin_addr);
+ fprintf(VarTerm, "%-*.*s ", width, width, cp);
+ }
+}
+
+struct bits {
+ short b_mask;
+ char b_val;
+} bits[] = {
+
+ {
+ RTF_UP, 'U'
+ },
+ {
+ RTF_GATEWAY, 'G'
+ },
+ {
+ RTF_HOST, 'H'
+ },
+ {
+ RTF_DYNAMIC, 'D'
+ },
+ {
+ RTF_MODIFIED, 'M'
+ },
+ {
+ RTF_CLONING, 'C'
+ },
+ {
+ RTF_XRESOLVE, 'X'
+ },
+ {
+ RTF_LLINFO, 'L'
+ },
+ {
+ RTF_REJECT, 'R'
+ },
+ {
+ 0
+ }
+};
+
+static void
+p_flags(int f, char *format)
+{
+ if (VarTerm) {
+ char name[33], *flags;
+ register struct bits *p = bits;
+
+ for (flags = name; p->b_mask; p++)
+ if (p->b_mask & f)
+ *flags++ = p->b_val;
+ *flags = '\0';
+ fprintf(VarTerm, format, name);
+ }
+}
+
+int
+ShowRoute()
+{
+ struct rt_msghdr *rtm;
+ struct sockaddr *sa;
+ char *sp, *ep, *cp;
+ u_char *wp;
+ int *lp;
+ int needed, nb;
+ u_long mask;
+ int mib[6];
+
+ if (!VarTerm)
+ return 1;
+
+ 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) {
+ LogPrintf(LogERROR, "ShowRoute: sysctl: estimate: %s\n", strerror(errno));
+ return (1);
+ }
+ if (needed < 0)
+ return (1);
+ sp = malloc(needed);
+ if (sp == NULL)
+ return (1);
+ if (sysctl(mib, 6, sp, &needed, NULL, 0) < 0) {
+ LogPrintf(LogERROR, "ShowRoute: sysctl: getroute: %s\n", strerror(errno));
+ free(sp);
+ return (1);
+ }
+ ep = sp + needed;
+
+ for (cp = sp; cp < ep; cp += rtm->rtm_msglen) {
+ rtm = (struct rt_msghdr *) cp;
+ sa = (struct sockaddr *) (rtm + 1);
+ mask = 0xffffffff;
+ if (rtm->rtm_addrs == RTA_DST)
+ p_sockaddr(sa, 36);
+ else {
+ wp = (u_char *) cp + rtm->rtm_msglen;
+ p_sockaddr(sa, 16);
+ if (sa->sa_len == 0)
+ sa->sa_len = sizeof(long);
+ sa = (struct sockaddr *) (sa->sa_len + (char *) sa);
+ p_sockaddr(sa, 18);
+ lp = (int *) (sa->sa_len + (char *) sa);
+ if ((char *) lp < (char *) wp && *lp) {
+ LogPrintf(LogDEBUG, " flag = %x, rest = %d\n", rtm->rtm_flags, *lp);
+ wp = (u_char *) (lp + 1);
+ mask = 0;
+ for (nb = *(char *) lp; nb > 4; nb--) {
+ mask <<= 8;
+ mask |= *wp++;
+ }
+ for (nb = 8 - *(char *) lp; nb > 0; nb--)
+ mask <<= 8;
+ }
+ }
+ fprintf(VarTerm, "%08lx ", mask);
+ p_flags(rtm->rtm_flags & (RTF_UP | RTF_GATEWAY | RTF_HOST), "%-6.6s ");
+ fprintf(VarTerm, "(%d)\n", rtm->rtm_index);
+ }
+ free(sp);
+ return 0;
+}
+
+/*
+ * Delete routes associated with our interface
+ */
+void
+DeleteIfRoutes(int all)
+{
+ struct rt_msghdr *rtm;
+ struct sockaddr *sa;
+ struct in_addr dstnet, gateway, maddr;
+ int needed;
+ char *sp, *cp, *ep;
+ u_long mask;
+ int *lp, nb;
+ u_char *wp;
+ int mib[6];
+
+ LogPrintf(LogDEBUG, "DeleteIfRoutes (%d)\n", IfIndex);
+
+ 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) {
+ LogPrintf(LogERROR, "DeleteIfRoutes: sysctl: estimate: %s\n",
+ strerror(errno));
+ return;
+ }
+ if (needed < 0)
+ return;
+
+ sp = malloc(needed);
+ if (sp == NULL)
+ return;
+
+ if (sysctl(mib, 6, sp, &needed, NULL, 0) < 0) {
+ LogPrintf(LogERROR, "DeleteIfRoutes: 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;
+ sa = (struct sockaddr *) (rtm + 1);
+ LogPrintf(LogDEBUG, "DeleteIfRoutes: addrs: %x, index: %d, flags: %x,"
+ " dstnet: %s\n",
+ rtm->rtm_addrs, rtm->rtm_index, rtm->rtm_flags,
+ inet_ntoa(((struct sockaddr_in *) sa)->sin_addr));
+ if (rtm->rtm_addrs != RTA_DST &&
+ (rtm->rtm_index == IfIndex) &&
+ (all || (rtm->rtm_flags & RTF_GATEWAY))) {
+ LogPrintf(LogDEBUG, "DeleteIfRoutes: Remove it\n");
+ dstnet = ((struct sockaddr_in *) sa)->sin_addr;
+ wp = (u_char *) cp + rtm->rtm_msglen;
+ if (sa->sa_len == 0)
+ sa->sa_len = sizeof(long);
+ sa = (struct sockaddr *) (sa->sa_len + (char *) sa);
+ gateway = ((struct sockaddr_in *) sa)->sin_addr;
+ lp = (int *) (sa->sa_len + (char *) sa);
+ mask = 0;
+ if ((char *) lp < (char *) wp && *lp) {
+ LogPrintf(LogDEBUG, "DeleteIfRoutes: flag = %x, rest = %d\n",
+ rtm->rtm_flags, *lp);
+ wp = (u_char *) (lp + 1);
+ for (nb = *lp; nb > 4; nb--) {
+ mask <<= 8;
+ mask |= *wp++;
+ }
+ for (nb = 8 - *lp; nb > 0; nb--)
+ mask <<= 8;
+ }
+ LogPrintf(LogDEBUG, "DeleteIfRoutes: Dst: %s\n", inet_ntoa(dstnet));
+ LogPrintf(LogDEBUG, "DeleteIfRoutes: Gw: %s\n", inet_ntoa(gateway));
+ LogPrintf(LogDEBUG, "DeleteIfRoutes: Index: %d\n", rtm->rtm_index);
+ if (dstnet.s_addr == INADDR_ANY)
+ mask = INADDR_ANY;
+ maddr.s_addr = htonl(mask);
+ OsSetRoute(RTM_DELETE, dstnet, gateway, maddr);
+ }
+ }
+ free(sp);
+}
+
+ /*
+ * 960603 - Modified to use dynamic buffer allocator as in ifconfig
+ */
+
+int
+GetIfIndex(char *name)
+{
+ char *buffer;
+ struct ifreq *ifrp;
+ int s, len, elen, newIfIndex;
+ struct ifconf ifconfs;
+
+ /* struct ifreq reqbuf[256]; -- obsoleted :) */
+ int oldbufsize, bufsize = sizeof(struct ifreq);
+
+ s = socket(AF_INET, SOCK_DGRAM, 0);
+ if (s < 0) {
+ LogPrintf(LogERROR, "GetIfIndex: socket(): %s\n", strerror(errno));
+ return (-1);
+ }
+ buffer = malloc(bufsize); /* allocate first buffer */
+ ifconfs.ifc_len = bufsize; /* Initial setting */
+
+ /*
+ * Iterate through here until we don't get many more data
+ */
+
+ do {
+ oldbufsize = ifconfs.ifc_len;
+ bufsize += 1 + sizeof(struct ifreq);
+ buffer = realloc((void *) buffer, bufsize); /* Make it bigger */
+ LogPrintf(LogDEBUG, "GetIfIndex: Growing buffer to %d\n", bufsize);
+ ifconfs.ifc_len = bufsize;
+ ifconfs.ifc_buf = buffer;
+ if (ioctl(s, SIOCGIFCONF, &ifconfs) < 0) {
+ LogPrintf(LogERROR, "GetIfIndex: ioctl(SIOCGIFCONF): %s\n",
+ strerror(errno));
+ close(s);
+ free(buffer);
+ return (-1);
+ }
+ } while (ifconfs.ifc_len > oldbufsize);
+
+ ifrp = ifconfs.ifc_req;
+
+ newIfIndex = 1;
+ for (len = ifconfs.ifc_len; len > 0; len -= sizeof(struct ifreq)) {
+ elen = ifrp->ifr_addr.sa_len - sizeof(struct sockaddr);
+ if (ifrp->ifr_addr.sa_family == AF_LINK) {
+ LogPrintf(LogDEBUG, "GetIfIndex: %d: %-*.*s, %d, %d\n",
+ newIfIndex, IFNAMSIZ, IFNAMSIZ, ifrp->ifr_name,
+ ifrp->ifr_addr.sa_family, elen);
+ if (strcmp(ifrp->ifr_name, name) == 0) {
+ IfIndex = newIfIndex;
+ close(s);
+ free(buffer);
+ return (newIfIndex);
+ }
+ newIfIndex++;
+ }
+ len -= elen;
+ ifrp = (struct ifreq *) ((char *) ifrp + elen);
+ ifrp++;
+ }
+
+ close(s);
+ free(buffer);
+ return (-1);
+}
diff --git a/usr.sbin/ppp/route.h b/usr.sbin/ppp/route.h
new file mode 100644
index 0000000..ba78987
--- /dev/null
+++ b/usr.sbin/ppp/route.h
@@ -0,0 +1,27 @@
+/*
+ * User Process PPP
+ *
+ * Written by Toshiharu OHNO (tony-o@iij.ad.jp)
+ *
+ * Copyright (C) 1993, Internet Initiative Japan, Inc. All rights reserverd.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the Internet Initiative Japan, Inc. The name of the
+ * IIJ may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: route.h,v 1.5 1997/08/25 00:29:27 brian Exp $
+ *
+ */
+
+extern int GetIfIndex(char *);
+extern int ShowRoute(void);
+extern void OsSetRoute(int, struct in_addr, struct in_addr, struct in_addr);
+extern void DeleteIfRoutes(int);
diff --git a/usr.sbin/ppp/server.c b/usr.sbin/ppp/server.c
new file mode 100644
index 0000000..b6869b8
--- /dev/null
+++ b/usr.sbin/ppp/server.c
@@ -0,0 +1,146 @@
+/*
+ * $Id: server.c,v 1.8 1997/11/09 14:18:51 brian Exp $
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/un.h>
+#include <unistd.h>
+
+#include "mbuf.h"
+#include "log.h"
+#include "loadalias.h"
+#include "command.h"
+#include "defs.h"
+#include "vars.h"
+#include "server.h"
+#include "log.h"
+#include "id.h"
+
+int server = -1;
+
+static struct sockaddr_un ifsun;
+static char *rm;
+
+int
+ServerLocalOpen(const char *name, mode_t mask)
+{
+ int s;
+
+ if (VarLocalAuth == LOCAL_DENY) {
+ LogPrintf(LogERROR, "Local: Can't open socket %s: No password "
+ "in ppp.secret\n", name);
+ return 1;
+ }
+
+ if (!(mode&(MODE_AUTO|MODE_DEDICATED|MODE_DIRECT))) {
+ LogPrintf(LogERROR, "Local: Can't open socket in interactive mode\n");
+ return 1;
+ }
+
+ ifsun.sun_len = strlen(name);
+ if (ifsun.sun_len > sizeof ifsun.sun_path - 1) {
+ LogPrintf(LogERROR, "Local: %s: Path too long\n", name);
+ return 2;
+ }
+ ifsun.sun_family = AF_LOCAL;
+ strcpy(ifsun.sun_path, name);
+
+ s = ID0socket(PF_LOCAL, SOCK_STREAM, 0);
+ if (s < 0) {
+ LogPrintf(LogERROR, "Local: socket: %s\n", strerror(errno));
+ return 3;
+ }
+ setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &s, sizeof s);
+ if (mask != (mode_t)-1)
+ mask = umask(mask);
+ if (bind(s, (struct sockaddr *) & ifsun, sizeof(ifsun)) < 0) {
+ if (mask != (mode_t)-1)
+ umask(mask);
+ LogPrintf(LogERROR, "Local: bind: %s\n", strerror(errno));
+ if (errno == EADDRINUSE && VarTerm)
+ fprintf(VarTerm, "Wait for a while, then try again.\n");
+ close(s);
+ ID0unlink(name);
+ return 4;
+ }
+ if (mask != (mode_t)-1)
+ umask(mask);
+ if (listen(s, 5) != 0) {
+ LogPrintf(LogERROR, "Local: Unable to listen to socket - OS overload?\n");
+ close(s);
+ ID0unlink(name);
+ return 5;
+ }
+ ServerClose();
+ server = s;
+ rm = ifsun.sun_path;
+ LogPrintf(LogPHASE, "Listening at local socket %s.\n", name);
+ return 0;
+}
+
+int
+ServerTcpOpen(int port)
+{
+ struct sockaddr_in ifsin;
+ int s;
+
+ if (VarLocalAuth == LOCAL_DENY) {
+ LogPrintf(LogERROR, "Tcp: Can't open socket %d: No password "
+ "in ppp.secret\n", port);
+ return 6;
+ }
+
+ if (!(mode&(MODE_AUTO|MODE_DEDICATED|MODE_DIRECT))) {
+ LogPrintf(LogERROR, "Tcp: Can't open socket in interactive mode\n");
+ return 6;
+ }
+
+ s = ID0socket(PF_INET, SOCK_STREAM, 0);
+ if (s < 0) {
+ LogPrintf(LogERROR, "Tcp: socket: %s\n", strerror(errno));
+ return 7;
+ }
+ ifsin.sin_family = AF_INET;
+ ifsin.sin_addr.s_addr = INADDR_ANY;
+ ifsin.sin_port = htons(port);
+ setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &s, sizeof s);
+ if (bind(s, (struct sockaddr *) & ifsin, sizeof(ifsin)) < 0) {
+ LogPrintf(LogERROR, "Tcp: bind: %s\n", strerror(errno));
+ if (errno == EADDRINUSE && VarTerm)
+ fprintf(VarTerm, "Wait for a while, then try again.\n");
+ close(s);
+ return 8;
+ }
+ if (listen(s, 5) != 0) {
+ LogPrintf(LogERROR, "Tcp: Unable to listen to socket - OS overload?\n");
+ close(s);
+ return 9;
+ }
+ ServerClose();
+ server = s;
+ LogPrintf(LogPHASE, "Listening at port %d.\n", port);
+ return 0;
+}
+
+void
+ServerClose()
+{
+ if (server >= 0) {
+ close(server);
+ if (rm) {
+ ID0unlink(rm);
+ rm = 0;
+ }
+ }
+ server = -1;
+}
diff --git a/usr.sbin/ppp/server.h b/usr.sbin/ppp/server.h
new file mode 100644
index 0000000..02707b8
--- /dev/null
+++ b/usr.sbin/ppp/server.h
@@ -0,0 +1,9 @@
+/*
+ * $Id: $
+ */
+
+extern int server;
+
+extern int ServerLocalOpen(const char *, mode_t);
+extern int ServerTcpOpen(int);
+extern void ServerClose(void);
diff --git a/usr.sbin/ppp/sig.c b/usr.sbin/ppp/sig.c
new file mode 100644
index 0000000..8ca77fa
--- /dev/null
+++ b/usr.sbin/ppp/sig.c
@@ -0,0 +1,72 @@
+/*
+ * $Id: $
+ */
+
+#include <sys/types.h>
+
+#include <signal.h>
+
+#include "sig.h"
+#include "mbuf.h"
+#include "log.h"
+
+static caused[NSIG]; /* An array of pending signals */
+static sig_type handler[NSIG]; /* all start at SIG_DFL */
+
+
+/* Record a signal in the "caused" array */
+
+static void
+signal_recorder(int sig)
+{
+ caused[sig - 1]++;
+}
+
+
+/*
+ * Set up signal_recorder, and record handler as the function to ultimately
+ * call in handle_signal()
+*/
+
+sig_type
+pending_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 ?) */
+ LogPrintf(LogALERT, "Eeek! %s:%s: 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 */
+
+void
+handle_signals()
+{
+ int sig;
+ int got;
+
+ do {
+ got = 0;
+ for (sig = 0; sig < NSIG; sig++)
+ if (caused[sig]) {
+ caused[sig]--;
+ got++;
+ (*handler[sig]) (sig + 1);
+ }
+ } while (got);
+}
diff --git a/usr.sbin/ppp/sig.h b/usr.sbin/ppp/sig.h
new file mode 100644
index 0000000..889a2b5
--- /dev/null
+++ b/usr.sbin/ppp/sig.h
@@ -0,0 +1,11 @@
+/*
+ * $Id: $
+ */
+
+typedef void (*sig_type)(int);
+
+/* Call this instead of signal() */
+extern sig_type pending_signal(int, sig_type);
+
+/* Call this when you want things to *actually* happen */
+extern void handle_signals(void);
diff --git a/usr.sbin/ppp/slcompress.c b/usr.sbin/ppp/slcompress.c
new file mode 100644
index 0000000..c16a22f
--- /dev/null
+++ b/usr.sbin/ppp/slcompress.c
@@ -0,0 +1,587 @@
+/*
+ * Routines to compress and uncompess tcp packets (for transmission
+ * over low speed serial lines.
+ *
+ * Copyright (c) 1989 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: slcompress.c,v 1.12 1997/10/12 21:43:55 brian Exp $
+ *
+ * Van Jacobson (van@helios.ee.lbl.gov), Dec 31, 1989:
+ * - Initial distribution.
+ */
+
+#include <sys/param.h>
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <netinet/ip.h>
+
+#include <stdio.h>
+#include <string.h>
+
+#include "mbuf.h"
+#include "log.h"
+#include "defs.h"
+#include "slcompress.h"
+#include "loadalias.h"
+#include "command.h"
+#include "vars.h"
+
+static 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 */
+} slstat;
+
+#define INCR(counter) slstat.counter++;
+
+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 long, 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_long)*cp++); \
+ } \
+}
+
+#define DECODES(f) { \
+ if (*cp == 0) {\
+ (f) = htons(ntohs(f) + ((cp[1] << 8) | cp[2])); \
+ cp += 3; \
+ } else { \
+ (f) = htons(ntohs(f) + (u_long)*cp++); \
+ } \
+}
+
+#define DECODEU(f) { \
+ if (*cp == 0) {\
+ (f) = htons((cp[1] << 8) | cp[2]); \
+ cp += 3; \
+ } else { \
+ (f) = htons((u_long)*cp++); \
+ } \
+}
+
+
+u_char
+sl_compress_tcp(struct mbuf * m,
+ struct ip * ip,
+ struct slcompress * comp,
+ int compress_cid)
+{
+ register struct cstate *cs = comp->last_cs->cs_next;
+ register u_int hlen = ip->ip_hl;
+ register struct tcphdr *oth;
+ register struct tcphdr *th;
+ register u_int deltaS, deltaA;
+ register u_int changes = 0;
+ u_char new_seq[16];
+ register u_char *cp = new_seq;
+
+ /*
+ * Bail if this is an IP fragment or if the TCP packet isn't `compressible'
+ * (i.e., ACK isn't set or some other control bit is set). (We assume that
+ * the caller has already made sure the packet is IP proto TCP).
+ */
+ if ((ip->ip_off & htons(0x3fff)) || m->cnt < 40) {
+ LogPrintf(LogDEBUG, "??? 1 ip_off = %x, cnt = %d\n",
+ ip->ip_off, m->cnt);
+ LogDumpBp(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) {
+ LogPrintf(LogDEBUG, "??? 2 th_flags = %x\n", th->th_flags);
+ LogDumpBp(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.
+ */
+ INCR(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;
+ INCR(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.
+ */
+ INCR(sls_misses)
+ comp->last_cs = lcs;
+#define THOFFSET(th) (th->th_off)
+ hlen += th->th_off;
+ hlen <<= 2;
+ if (hlen > m->cnt)
+ return (TYPE_IP);
+ goto uncompressed;
+
+found:
+
+ /*
+ * Found it -- move to the front on the connection list.
+ */
+ if (cs == lastcs)
+ comp->last_cs = lcs;
+ else {
+ lcs->cs_next = cs->cs_next;
+ cs->cs_next = lastcs->cs_next;
+ lastcs->cs_next = cs;
+ }
+ }
+
+ /*
+ * Make sure that only what we expect to change changed. The first line of
+ * the `if' checks the IP protocol version, header length & type of
+ * service. The 2nd line checks the "Don't fragment" bit. The 3rd line
+ * checks the time-to-live and protocol (the protocol check is unnecessary
+ * but costless). The 4th line checks the TCP header length. The 5th line
+ * checks IP options, if any. The 6th line checks TCP options, if any. If
+ * any of these things are different between the previous & current
+ * datagram, we send the current datagram `uncompressed'.
+ */
+ oth = (struct tcphdr *) & ((int *) &cs->cs_ip)[hlen];
+ deltaS = hlen;
+ hlen += th->th_off;
+ hlen <<= 2;
+ if (hlen > m->cnt)
+ return (TYPE_IP);
+
+ if (((u_short *) ip)[0] != ((u_short *) & cs->cs_ip)[0] ||
+ ((u_short *) ip)[3] != ((u_short *) & cs->cs_ip)[3] ||
+ ((u_short *) ip)[4] != ((u_short *) & cs->cs_ip)[4] ||
+ THOFFSET(th) != THOFFSET(oth) ||
+ (deltaS > 5 &&
+ memcmp(ip + 1, &cs->cs_ip + 1, (deltaS - 5) << 2)) ||
+ (THOFFSET(th) > 5 &&
+ memcmp(th + 1, oth + 1, (THOFFSET(th) - 5) << 2))) {
+ goto uncompressed;
+ }
+
+ /*
+ * Figure out which of the changing fields changed. The receiver expects
+ * changes in the order: urgent, window, ack, seq (the order minimizes the
+ * number of temporaries needed in this section of code).
+ */
+ if (th->th_flags & TH_URG) {
+ deltaS = ntohs(th->th_urp);
+ ENCODEZ(deltaS);
+ changes |= NEW_U;
+ } else if (th->th_urp != oth->th_urp) {
+
+ /*
+ * argh! URG not set but urp changed -- a sensible implementation should
+ * never do this but RFC793 doesn't prohibit the change so we have to
+ * deal with it.
+ */
+ goto uncompressed;
+ }
+ deltaS = (u_short) (ntohs(th->th_win) - ntohs(oth->th_win));
+ if (deltaS) {
+ ENCODE(deltaS);
+ changes |= NEW_W;
+ }
+ deltaA = ntohl(th->th_ack) - ntohl(oth->th_ack);
+ if (deltaA) {
+ if (deltaA > 0xffff) {
+ goto uncompressed;
+ }
+ ENCODE(deltaA);
+ changes |= NEW_A;
+ }
+ deltaS = ntohl(th->th_seq) - ntohl(oth->th_seq);
+ if (deltaS) {
+ if (deltaS > 0xffff) {
+ goto uncompressed;
+ }
+ ENCODE(deltaS);
+ changes |= NEW_S;
+ }
+ switch (changes) {
+
+ case 0:
+
+ /*
+ * Nothing changed. If this packet contains data and the last one didn't,
+ * this is probably a data packet following an ack (normal on an
+ * interactive connection) and we send it compressed. Otherwise it's
+ * probably a retransmit, retransmitted ack or window probe. Send it
+ * uncompressed in case the other side missed the compressed version.
+ */
+ if (ip->ip_len != cs->cs_ip.ip_len &&
+ ntohs(cs->cs_ip.ip_len) == hlen)
+ break;
+
+ /* (fall through) */
+
+ case SPECIAL_I:
+ case SPECIAL_D:
+
+ /*
+ * actual changes match one of our special case encodings -- send packet
+ * uncompressed.
+ */
+ goto uncompressed;
+
+ case NEW_S | NEW_A:
+ if (deltaS == deltaA &&
+ deltaS == ntohs(cs->cs_ip.ip_len) - hlen) {
+ /* special case for echoed terminal traffic */
+ changes = SPECIAL_I;
+ cp = new_seq;
+ }
+ break;
+
+ case NEW_S:
+ if (deltaS == ntohs(cs->cs_ip.ip_len) - hlen) {
+ /* special case for data xfer */
+ changes = SPECIAL_D;
+ cp = new_seq;
+ }
+ break;
+ }
+
+ deltaS = ntohs(ip->ip_id) - ntohs(cs->cs_ip.ip_id);
+ if (deltaS != 1) {
+ ENCODEZ(deltaS);
+ changes |= NEW_I;
+ }
+ if (th->th_flags & TH_PUSH)
+ changes |= TCP_PUSH_BIT;
+
+ /*
+ * Grab the cksum before we overwrite it below. Then update our state with
+ * this packet's header.
+ */
+ deltaA = ntohs(th->th_sum);
+ memcpy(&cs->cs_ip, ip, hlen);
+
+ /*
+ * We want to use the original packet as our compressed packet. (cp -
+ * new_seq) is the number of bytes we need for compressed sequence numbers.
+ * In addition we need one byte for the change mask, one for the connection
+ * id and two for the tcp checksum. So, (cp - new_seq) + 4 bytes of header
+ * are needed. hlen is how many bytes of the original packet to toss so
+ * subtract the two to get the new packet size.
+ */
+ deltaS = cp - new_seq;
+ cp = (u_char *) ip;
+
+ /*
+ * Since fastq traffic can jump ahead of the background traffic, we don't
+ * know what order packets will go on the line. In this case, we always
+ * send a "new" connection id so the receiver state stays synchronized.
+ */
+ if (comp->last_xmit == cs->cs_id && compress_cid) {
+ hlen -= deltaS + 3;
+ cp += hlen;
+ *cp++ = changes;
+ } else {
+ comp->last_xmit = cs->cs_id;
+ hlen -= deltaS + 4;
+ cp += hlen;
+ *cp++ = changes | NEW_C;
+ *cp++ = cs->cs_id;
+ }
+ m->cnt -= hlen;
+ m->offset += hlen;
+ *cp++ = deltaA >> 8;
+ *cp++ = deltaA;
+ memcpy(cp, new_seq, deltaS);
+ INCR(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)
+{
+ register u_char *cp;
+ register u_int hlen, changes;
+ register struct tcphdr *th;
+ register struct cstate *cs;
+ register struct ip *ip;
+
+ switch (type) {
+
+ case TYPE_UNCOMPRESSED_TCP:
+ ip = (struct ip *) * bufp;
+ if (ip->ip_p >= MAX_STATES)
+ goto bad;
+ cs = &comp->rstate[comp->last_recv = ip->ip_p];
+ comp->flags &= ~SLF_TOSS;
+ ip->ip_p = IPPROTO_TCP;
+
+ /*
+ * Calculate the size of the TCP/IP header and make sure that we don't
+ * overflow the space we have available for it.
+ */
+ hlen = ip->ip_hl << 2;
+ if (hlen + sizeof(struct tcphdr) > len)
+ goto bad;
+ th = (struct tcphdr *) & ((char *) ip)[hlen];
+ hlen += THOFFSET(th) << 2;
+ if (hlen > MAX_HDR)
+ goto bad;
+ memcpy(&cs->cs_ip, ip, hlen);
+ cs->cs_ip.ip_sum = 0;
+ cs->cs_hlen = hlen;
+ INCR(sls_uncompressedin)
+ return (len);
+
+ default:
+ goto bad;
+
+ case TYPE_COMPRESSED_TCP:
+ break;
+ }
+ /* We've got a compressed packet. */
+ INCR(sls_compressedin)
+ cp = *bufp;
+ changes = *cp++;
+ LogPrintf(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_STATES || 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) {
+ INCR(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) {
+ LogPrintf(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);
+
+ LogPrintf(LogDEBUG, "Uncompress: id = %04x, seq = %08x\n",
+ cs->cs_ip.ip_id, ntohl(th->th_seq));
+
+ /*
+ * At this point, cp points to the first byte of data in the packet. If
+ * we're not aligned on a 4-byte boundary, copy the data down so the ip &
+ * tcp headers will be aligned. Then 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;
+
+#ifdef notdef
+ if ((int) cp & 3) {
+ if (len > 0)
+ (void) bcopy(cp, (caddr_t) ((int) cp & ~3), len);
+ cp = (u_char *) ((int) cp & ~3);
+ }
+#endif
+
+ cp -= cs->cs_hlen;
+ len += cs->cs_hlen;
+ cs->cs_ip.ip_len = htons(len);
+ memcpy(cp, &cs->cs_ip, cs->cs_hlen);
+ *bufp = cp;
+
+ /* recompute the ip header checksum */
+ {
+ register u_short *bp = (u_short *) cp;
+
+ for (changes = 0; hlen > 0; hlen -= 2)
+ changes += *bp++;
+ changes = (changes & 0xffff) + (changes >> 16);
+ changes = (changes & 0xffff) + (changes >> 16);
+ ((struct ip *) cp)->ip_sum = ~changes;
+ }
+ return (len);
+bad:
+ comp->flags |= SLF_TOSS;
+ INCR(sls_errorin)
+ return (0);
+}
+
+int
+ReportCompress()
+{
+ if (!VarTerm)
+ return 1;
+
+ fprintf(VarTerm, "Out: %d (compress) / %d (total)",
+ slstat.sls_compressed, slstat.sls_packets);
+ fprintf(VarTerm, " %d (miss) / %d (search)\n",
+ slstat.sls_misses, slstat.sls_searches);
+ fprintf(VarTerm, "In: %d (compress), %d (uncompress)",
+ slstat.sls_compressedin, slstat.sls_uncompressedin);
+ fprintf(VarTerm, " %d (error), %d (tossed)\n",
+ slstat.sls_errorin, 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..dc5173c
--- /dev/null
+++ b/usr.sbin/ppp/slcompress.h
@@ -0,0 +1,134 @@
+/*
+ * Definitions for tcp compression routines.
+ *
+ * $Header: /home/ncvs/src/usr.sbin/ppp/slcompress.h,v 1.8 1997/10/07 00:56:58 brian Exp $
+ *
+ * Copyright (c) 1989 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: slcompress.h,v 1.8 1997/10/07 00:56:58 brian Exp $
+ *
+ * Van Jacobson (van@helios.ee.lbl.gov), Dec 31, 1989:
+ * - Initial distribution.
+ */
+
+#define MAX_STATES 16 /* must be > 2 and < 256 */
+#define MAX_HDR 128 /* XXX 4bsd-ism: should really be 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_STATES]; /* xmit connection states */
+ struct cstate rstate[MAX_STATES]; /* receive connection states */
+};
+
+/* flag values */
+#define SLF_TOSS 1 /* tossing rcvd frames because of input err */
+
+extern void sl_compress_init(struct slcompress *, int);
+extern u_char sl_compress_tcp
+ (struct mbuf *, struct ip *, struct slcompress *, int);
+extern int sl_uncompress_tcp(u_char **, int, u_int, struct slcompress *);
+extern int ReportCompress(void);
diff --git a/usr.sbin/ppp/systems.c b/usr.sbin/ppp/systems.c
new file mode 100644
index 0000000..715cb11
--- /dev/null
+++ b/usr.sbin/ppp/systems.c
@@ -0,0 +1,279 @@
+/*
+ * System configuration routines
+ *
+ * Written by Toshiharu OHNO (tony-o@iij.ad.jp)
+ *
+ * Copyright (C) 1993, Internet Initiative Japan, Inc. All rights reserverd.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the Internet Initiative Japan, Inc. The name of the
+ * IIJ may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: systems.c,v 1.21 1997/11/09 14:18:53 brian Exp $
+ *
+ * TODO:
+ */
+#include <sys/param.h>
+#include <netinet/in.h>
+
+#include <ctype.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "mbuf.h"
+#include "log.h"
+#include "id.h"
+#include "defs.h"
+#include "timer.h"
+#include "fsm.h"
+#include "loadalias.h"
+#include "command.h"
+#include "ipcp.h"
+#include "pathnames.h"
+#include "vars.h"
+#include "server.h"
+#include "systems.h"
+
+#define issep(ch) ((ch) == ' ' || (ch) == '\t')
+
+FILE *
+OpenSecret(char *file)
+{
+ FILE *fp;
+ char line[100];
+
+ snprintf(line, sizeof line, "%s/%s", _PATH_PPP, file);
+ fp = ID0fopen(line, "r");
+ if (fp == NULL)
+ LogPrintf(LogWARN, "OpenSecret: Can't open %s.\n", line);
+ return (fp);
+}
+
+void
+CloseSecret(FILE * fp)
+{
+ fclose(fp);
+}
+
+/* Move string from ``from'' to ``to'', interpreting ``~'' and $.... */
+static void
+InterpretArg(char *from, char *to)
+{
+ const char *env;
+ char *ptr, *startto, *endto;
+ int len;
+
+ startto = to;
+ endto = to + LINE_LEN - 1;
+
+ while(issep(*from))
+ from++;
+ if (*from == '~') {
+ ptr = strchr(++from, '/');
+ len = ptr ? ptr - from : strlen(from);
+ if (len == 0) {
+ if ((env = getenv("HOME")) == NULL)
+ env = _PATH_PPP;
+ strncpy(to, env, endto - to);
+ } else {
+ struct passwd *pwd;
+
+ strncpy(to, from, len);
+ to[len] = '\0';
+ pwd = getpwnam(to);
+ if (pwd)
+ strncpy(to, pwd->pw_dir, endto-to);
+ else
+ strncpy(to, _PATH_PPP, endto - to);
+ endpwent();
+ }
+ *endto = '\0';
+ to += strlen(to);
+ from += len;
+ }
+
+ while (to < endto && *from != '\0') {
+ if (*from == '$') {
+ if (from[1] == '$') {
+ *to = '\0'; /* For an empty var name below */
+ from += 2;
+ } else if (from[1] == '{') {
+ ptr = strchr(from+2, '}');
+ if (ptr) {
+ len = ptr - from - 2;
+ if (endto - to < len )
+ len = endto - to;
+ if (len) {
+ strncpy(to, from+2, len);
+ to[len] = '\0';
+ from = ptr+1;
+ } else {
+ *to++ = *from++;
+ continue;
+ }
+ } else {
+ *to++ = *from++;
+ continue;
+ }
+ } else {
+ ptr = to;
+ for (from++; (isalnum(*from) || *from == '_') && ptr < endto; from++)
+ *ptr++ = *from;
+ *ptr = '\0';
+ }
+ if (*to == '\0')
+ *to++ = '$';
+ else if ((env = getenv(to)) != NULL) {
+ strncpy(to, env, endto - to);
+ *endto = '\0';
+ to += strlen(to);
+ }
+ } else
+ *to++ = *from++;
+ }
+ while (to > startto) {
+ to--;
+ if (!issep(*to)) {
+ to++;
+ break;
+ }
+ }
+ *to = '\0';
+}
+
+#define CTRL_UNKNOWN (0)
+#define CTRL_INCLUDE (1)
+
+static int
+DecodeCtrlCommand(char *line, char *arg)
+{
+ if (!strncasecmp(line, "include", 7) && issep(line[7])) {
+ InterpretArg(line+8, arg);
+ return CTRL_INCLUDE;
+ }
+ return CTRL_UNKNOWN;
+}
+
+int
+SelectSystem(char *name, char *file)
+{
+ FILE *fp;
+ char *cp, *wp;
+ int n, len;
+ u_char olauth;
+ char line[LINE_LEN];
+ char filename[200];
+ int linenum;
+
+ if (*file == '/')
+ snprintf(filename, sizeof filename, "%s", file);
+ else
+ snprintf(filename, sizeof filename, "%s/%s", _PATH_PPP, file);
+ fp = ID0fopen(filename, "r");
+ if (fp == NULL) {
+ LogPrintf(LogDEBUG, "SelectSystem: Can't open %s.\n", filename);
+ return (-1);
+ }
+ LogPrintf(LogDEBUG, "SelectSystem: Checking %s (%s).\n", name, filename);
+
+ linenum = 0;
+ while (fgets(line, sizeof(line), fp)) {
+ linenum++;
+ cp = line;
+ switch (*cp) {
+ case '#': /* comment */
+ break;
+ case ' ':
+ case '\t':
+ break;
+ default:
+ wp = strpbrk(cp, ":\n");
+ if (wp == NULL) {
+ LogPrintf(LogWARN, "Bad rule in %s (line %d) - missing colon.\n",
+ filename, linenum);
+ ServerClose();
+ exit(1);
+ }
+ *wp = '\0';
+ if (*cp == '!') {
+ char arg[LINE_LEN];
+ switch (DecodeCtrlCommand(cp+1, arg)) {
+ case CTRL_INCLUDE:
+ LogPrintf(LogCOMMAND, "%s: Including \"%s\"\n", filename, arg);
+ n = SelectSystem(name, arg);
+ LogPrintf(LogCOMMAND, "%s: Done include of \"%s\"\n", filename, arg);
+ if (!n)
+ return 0; /* got it */
+ break;
+ default:
+ LogPrintf(LogCOMMAND, "%s: %s: Invalid command\n", name, cp);
+ break;
+ }
+ } else if (strcmp(cp, name) == 0) {
+ while (fgets(line, sizeof(line), fp)) {
+ cp = line;
+ if (issep(*cp)) {
+ n = strspn(cp, " \t");
+ cp += n;
+ len = strlen(cp);
+ if (!len)
+ continue;
+ if (cp[len-1] == '\n')
+ cp[--len] = '\0';
+ if (!len)
+ continue;
+ LogPrintf(LogCOMMAND, "%s: %s\n", name, cp);
+ olauth = VarLocalAuth;
+ if (VarLocalAuth == LOCAL_NO_AUTH)
+ VarLocalAuth = LOCAL_AUTH;
+ DecodeCommand(cp, len, 0);
+ VarLocalAuth = olauth;
+ } else if (*cp == '#') {
+ continue;
+ } else
+ break;
+ }
+ fclose(fp);
+ return (0);
+ }
+ break;
+ }
+ }
+ fclose(fp);
+ return -1;
+}
+
+int
+LoadCommand(struct cmdtab const * list, int argc, char **argv)
+{
+ char *name;
+
+ if (argc > 0)
+ name = *argv;
+ else
+ name = "default";
+
+ if (SelectSystem(name, CONFFILE) < 0) {
+ LogPrintf(LogWARN, "%s: not found.\n", name);
+ return -1;
+ }
+ return 0;
+}
+
+int
+SaveCommand(struct cmdtab const *list, int argc, char **argv)
+{
+ LogPrintf(LogWARN, "save command is not implemented (yet).\n");
+ return 1;
+}
diff --git a/usr.sbin/ppp/systems.h b/usr.sbin/ppp/systems.h
new file mode 100644
index 0000000..00f1748
--- /dev/null
+++ b/usr.sbin/ppp/systems.h
@@ -0,0 +1,28 @@
+/*
+ * User Process PPP
+ *
+ * Written by Toshiharu OHNO (tony-o@iij.ad.jp)
+ *
+ * Copyright (C) 1993, Internet Initiative Japan, Inc. All rights reserverd.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the Internet Initiative Japan, Inc. The name of the
+ * IIJ may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: systems.h,v 1.7 1997/10/26 01:03:49 brian Exp $
+ *
+ */
+
+extern int SelectSystem(char *, char *);
+extern FILE *OpenSecret(char *);
+extern void CloseSecret(FILE *);
+extern int LoadCommand(struct cmdtab const *, int, char **);
+extern int SaveCommand(struct cmdtab const *, int, char **);
diff --git a/usr.sbin/ppp/timer.c b/usr.sbin/ppp/timer.c
new file mode 100644
index 0000000..83ebb6d
--- /dev/null
+++ b/usr.sbin/ppp/timer.c
@@ -0,0 +1,295 @@
+/*
+ * PPP Timer Processing Module
+ *
+ * Written by Toshiharu OHNO (tony-o@iij.ad.jp)
+ *
+ * Copyright (C) 1993, Internet Initiative Japan, Inc. All rights reserverd.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the Internet Initiative Japan, Inc. The name of the
+ * IIJ may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: timer.c,v 1.22 1997/10/26 01:03:52 brian Exp $
+ *
+ * TODO:
+ */
+
+#include <signal.h>
+#ifdef SIGALRM
+#include <errno.h>
+#endif
+#include <sys/time.h>
+#include <stdio.h>
+#include <unistd.h>
+
+#include "mbuf.h"
+#include "log.h"
+#include "defs.h"
+#include "sig.h"
+#include "timer.h"
+
+struct pppTimer *TimerList = NULL;
+
+static void StopTimerNoBlock(struct pppTimer *);
+static void InitTimerService(void);
+
+void
+StopTimer(struct pppTimer * tp)
+{
+#ifdef SIGALRM
+ int omask;
+
+ omask = sigblock(sigmask(SIGALRM));
+#endif
+ StopTimerNoBlock(tp);
+#ifdef SIGALRM
+ sigsetmask(omask);
+#endif
+}
+
+void
+StartTimer(struct pppTimer * tp)
+{
+ struct pppTimer *t, *pt;
+ u_long ticks = 0;
+
+#ifdef SIGALRM
+ int omask;
+
+ omask = sigblock(sigmask(SIGALRM));
+#endif
+
+ if (tp->state != TIMER_STOPPED) {
+ StopTimerNoBlock(tp);
+ }
+ if (tp->load == 0) {
+ LogPrintf(LogDEBUG, "timer %x has 0 load!\n", tp);
+ sigsetmask(omask);
+ return;
+ }
+ pt = NULL;
+ for (t = TimerList; t; t = t->next) {
+ LogPrintf(LogDEBUG, "StartTimer: %x(%d): ticks: %d, rest: %d\n",
+ t, t->state, ticks, t->rest);
+ if (ticks + t->rest >= tp->load)
+ break;
+ ticks += t->rest;
+ pt = t;
+ }
+
+ tp->state = TIMER_RUNNING;
+ tp->rest = tp->load - ticks;
+ LogPrintf(LogDEBUG, "StartTimer: Inserting %x before %x, rest = %d\n",
+ tp, t, tp->rest);
+ /* Insert given *tp just before *t */
+ tp->next = t;
+ if (pt) {
+ pt->next = tp;
+ } else {
+ InitTimerService();
+ TimerList = tp;
+ }
+ if (t)
+ t->rest -= tp->rest;
+
+#ifdef SIGALRM
+ sigsetmask(omask);
+#endif
+}
+
+static void
+StopTimerNoBlock(struct pppTimer * tp)
+{
+ struct pppTimer *t, *pt;
+
+ /*
+ * A Running Timer should be removing TimerList, But STOPPED/EXPIRED is
+ * already removing TimerList. So just marked as TIMER_STOPPED. Do not
+ * change tp->enext!! (Might be Called by expired proc)
+ */
+ LogPrintf(LogDEBUG, "StopTimer: %x, next = %x state=%x\n",
+ tp, tp->next, tp->state);
+ if (tp->state != TIMER_RUNNING) {
+ tp->next = NULL;
+ tp->state = TIMER_STOPPED;
+ return;
+ }
+ pt = NULL;
+ for (t = TimerList; t != tp && t != NULL; t = t->next)
+ pt = t;
+ if (t) {
+ if (pt) {
+ pt->next = t->next;
+ } else {
+ TimerList = t->next;
+ if (TimerList == NULL) /* Last one ? */
+ TermTimerService(); /* Terminate Timer Service */
+ }
+ if (t->next)
+ t->next->rest += tp->rest;
+ } else
+ LogPrintf(LogERROR, "Oops, timer not found!!\n");
+
+ tp->next = NULL;
+ tp->state = TIMER_STOPPED;
+}
+
+void
+TimerService()
+{
+ struct pppTimer *tp, *exp, *wt;
+
+ if (LogIsKept(LogDEBUG))
+ ShowTimers();
+ tp = TimerList;
+ if (tp) {
+ tp->rest--;
+ if (tp->rest == 0) {
+
+ /*
+ * Multiple timers may expires at once. Create list of expired timers.
+ */
+ exp = NULL;
+ do {
+ tp->state = TIMER_EXPIRED;
+ wt = tp->next;
+ tp->enext = exp;
+ exp = tp;
+ LogPrintf(LogDEBUG, "TimerService: Add %x to exp\n", tp);
+ tp = wt;
+ } while (tp && (tp->rest == 0));
+
+ TimerList = tp;
+ if (TimerList == NULL) /* No timers ? */
+ TermTimerService(); /* Terminate Timer Service */
+ LogPrintf(LogDEBUG, "TimerService: next is %x(%d)\n",
+ TimerList, TimerList ? TimerList->rest : 0);
+
+ /*
+ * Process all expired timers.
+ */
+ while (exp) {
+#ifdef notdef
+ StopTimer(exp);
+#endif
+ if (exp->func)
+ (*exp->func) (exp->arg);
+
+ /*
+ * Just Removing each item from expired list And exp->enext will be
+ * intialized at next expire in this funtion.
+ */
+ exp = exp->enext;
+ }
+ }
+ }
+}
+
+void
+ShowTimers()
+{
+ struct pppTimer *pt;
+
+ LogPrintf(LogDEBUG, "---- Begin of Timer Service List---\n");
+ for (pt = TimerList; pt; pt = pt->next)
+ LogPrintf(LogDEBUG, "%x: load = %d, rest = %d, state =%x\n",
+ pt, pt->load, pt->rest, pt->state);
+ LogPrintf(LogDEBUG, "---- End of Timer Service List ---\n");
+}
+
+#ifdef SIGALRM
+u_int
+nointr_sleep(u_int sec)
+{
+ struct timeval to, st, et;
+ long sld, nwd, std;
+
+ gettimeofday(&st, NULL);
+ to.tv_sec = sec;
+ to.tv_usec = 0;
+ std = st.tv_sec * 1000000 + st.tv_usec;
+ for (;;) {
+ if (select(0, NULL, NULL, NULL, &to) == 0 ||
+ errno != EINTR) {
+ break;
+ } else {
+ gettimeofday(&et, NULL);
+ sld = to.tv_sec * 1000000 + to.tv_sec;
+ nwd = et.tv_sec * 1000000 + et.tv_usec - std;
+ if (sld > nwd)
+ sld -= nwd;
+ else
+ sld = 1; /* Avoid both tv_sec/usec is 0 */
+
+ /* Calculate timeout value for select */
+ to.tv_sec = sld / 1000000;
+ to.tv_usec = sld % 1000000;
+ }
+ }
+ return (0L);
+}
+
+void
+nointr_usleep(u_int usec)
+{
+ struct timeval to, st, et;
+ long sld, nwd, std;
+
+ gettimeofday(&st, NULL);
+ to.tv_sec = 0;
+ to.tv_usec = usec;
+ std = st.tv_sec * 1000000 + st.tv_usec;
+ for (;;) {
+ if (select(0, NULL, NULL, NULL, &to) == 0 ||
+ errno != EINTR) {
+ break;
+ } else {
+ gettimeofday(&et, NULL);
+ sld = to.tv_sec * 1000000 + to.tv_sec;
+ nwd = et.tv_sec * 1000000 + et.tv_usec - std;
+ if (sld > nwd)
+ sld -= nwd;
+ else
+ sld = 1; /* Avoid both tv_sec/usec is 0 */
+
+ /* Calculate timeout value for select */
+ to.tv_sec = sld / 1000000;
+ to.tv_usec = sld % 1000000;
+
+ }
+ }
+}
+
+static void
+InitTimerService()
+{
+ struct itimerval itimer;
+
+ pending_signal(SIGALRM, (void (*) (int)) TimerService);
+ itimer.it_interval.tv_sec = itimer.it_value.tv_sec = 0;
+ itimer.it_interval.tv_usec = itimer.it_value.tv_usec = TICKUNIT;
+ if (setitimer(ITIMER_REAL, &itimer, NULL) == -1)
+ LogPrintf(LogERROR, "Unable to set itimer.\n");
+}
+
+void
+TermTimerService(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)
+ LogPrintf(LogERROR, "Unable to set itimer.\n");
+ pending_signal(SIGALRM, SIG_IGN);
+}
+
+#endif
diff --git a/usr.sbin/ppp/timer.h b/usr.sbin/ppp/timer.h
new file mode 100644
index 0000000..89439f5
--- /dev/null
+++ b/usr.sbin/ppp/timer.h
@@ -0,0 +1,51 @@
+/*
+ * Written by Toshiharu OHNO (tony-o@iij.ad.jp)
+ *
+ * Copyright (C) 1993, Internet Initiative Japan, Inc. All rights reserverd.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the Internet Initiative Japan. The name of the
+ * IIJ may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: timer.h,v 1.1 1997/10/26 01:03:53 brian Exp $
+ *
+ * TODO:
+ */
+
+#define TICKUNIT 100000 /* Unit in usec */
+#define SECTICKS (1000000/TICKUNIT)
+
+struct pppTimer {
+ int state;
+ u_long rest; /* Ticks to expire */
+ u_long load; /* Initial load value */
+ void (*func)(); /* 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
+
+extern struct pppTimer *TimerList;
+
+extern void StartTimer(struct pppTimer *);
+extern void StopTimer(struct pppTimer *);
+extern void TimerService(void);
+extern void TermTimerService(void);
+extern void ShowTimers(void);
+
+#ifdef SIGALRM
+extern u_int nointr_sleep(u_int);
+extern void nointr_usleep(u_int);
+#endif
diff --git a/usr.sbin/ppp/vars.c b/usr.sbin/ppp/vars.c
new file mode 100644
index 0000000..1a47b37
--- /dev/null
+++ b/usr.sbin/ppp/vars.c
@@ -0,0 +1,195 @@
+/*
+ * PPP configuration variables
+ *
+ * Written by Toshiharu OHNO (tony-o@iij.ad.jp)
+ *
+ * Copyright (C) 1993, Internet Initiative Japan, Inc. All rights reserverd.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the Internet Initiative Japan, Inc. The name of the
+ * IIJ may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: vars.c,v 1.32 1997/10/29 01:19:51 brian Exp $
+ *
+ */
+#include <sys/param.h>
+#include <netinet/in.h>
+
+#include <stdio.h>
+#include <string.h>
+
+#include "mbuf.h"
+#include "log.h"
+#include "defs.h"
+#include "timer.h"
+#include "fsm.h"
+#include "command.h"
+#include "hdlc.h"
+#include "termios.h"
+#include "loadalias.h"
+#include "vars.h"
+#include "auth.h"
+#include "defs.h"
+
+char VarVersion[] = "PPP Version 1.3";
+char VarLocalVersion[] = "$Date: 1997/10/29 01:19:51 $";
+int Utmp = 0;
+int ipInOctets = 0;
+int ipOutOctets = 0;
+int ipKeepAlive = 0;
+int ipConnectSecs = 0;
+int ipIdleSecs = 0;
+int reconnectState = RECON_UNKNOWN;
+int reconnectCount = 0;
+
+/*
+ * Order of conf option is important. See vars.h.
+ */
+struct confdesc pppConfs[] = {
+ {"vjcomp", CONF_ENABLE, CONF_ACCEPT},
+ {"lqr", CONF_DISABLE, CONF_ACCEPT},
+ {"chap", CONF_DISABLE, CONF_ACCEPT},
+ {"pap", CONF_DISABLE, CONF_ACCEPT},
+ {"acfcomp", CONF_ENABLE, CONF_ACCEPT},
+ {"protocomp", CONF_ENABLE, CONF_ACCEPT},
+ {"pred1", CONF_ENABLE, CONF_ACCEPT},
+ {"proxy", CONF_DISABLE, CONF_NONE},
+ {"msext", CONF_DISABLE, CONF_NONE},
+ {"passwdauth", CONF_DISABLE, CONF_NONE},
+ {"utmp", CONF_ENABLE, CONF_NONE},
+ {NULL},
+};
+
+struct pppvars pppVars = {
+ DEF_MRU, DEF_MTU, 0, MODEM_SPEED, CS8, MODEM_CTSRTS, 180, 30, 3,
+ RECONNECT_TIMER, RECONNECT_TRIES, REDIAL_PERIOD,
+ NEXT_REDIAL_PERIOD, 1, 1, MODEM_DEV, BASE_MODEM_DEV,
+ OPEN_ACTIVE, LOCAL_NO_AUTH, 0
+};
+
+int
+DisplayCommand()
+{
+ struct confdesc *vp;
+
+ if (!VarTerm)
+ return 1;
+
+ fprintf(VarTerm, "Current configuration option settings..\n\n");
+ fprintf(VarTerm, "Name\t\tMy Side\t\tHis Side\n");
+ fprintf(VarTerm, "----------------------------------------\n");
+ for (vp = pppConfs; vp->name; vp++)
+ fprintf(VarTerm, "%-10s\t%s\t\t%s\n", vp->name,
+ (vp->myside == CONF_ENABLE) ? "enable" :
+ (vp->myside == CONF_DISABLE ? "disable" : "N/A"),
+ (vp->hisside == CONF_ACCEPT) ? "accept" :
+ (vp->hisside == CONF_DENY ? "deny" : "N/A"));
+
+ return 0;
+}
+
+static int
+ConfigCommand(struct cmdtab * list, int argc, char **argv, int mine, int val)
+{
+ struct confdesc *vp;
+ int err;
+
+ if (argc < 1)
+ return -1;
+
+ err = 0;
+ do {
+ for (vp = pppConfs; vp->name; vp++)
+ if (strcasecmp(vp->name, *argv) == 0) {
+ if (mine) {
+ if (vp->myside == CONF_NONE) {
+ LogPrintf(LogWARN, "Config: %s cannot be enabled or disabled\n",
+ vp->name);
+ err++;
+ } else
+ vp->myside = val;
+ } else {
+ if (vp->hisside == CONF_NONE) {
+ LogPrintf(LogWARN, "Config: %s cannot be accepted or denied\n",
+ vp->name);
+ err++;
+ } else
+ vp->hisside = val;
+ }
+ break;
+ }
+ if (!vp->name) {
+ LogPrintf(LogWARN, "Config: %s: No such key word\n", *argv);
+ err++;
+ }
+ argc--;
+ argv++;
+ } while (argc > 0);
+
+ return err;
+}
+
+int
+EnableCommand(struct cmdtab * list, int argc, char **argv)
+{
+ return ConfigCommand(list, argc, argv, 1, CONF_ENABLE);
+}
+
+int
+DisableCommand(struct cmdtab * list, int argc, char **argv)
+{
+ return ConfigCommand(list, argc, argv, 1, CONF_DISABLE);
+}
+
+int
+AcceptCommand(struct cmdtab * list, int argc, char **argv)
+{
+ return ConfigCommand(list, argc, argv, 0, CONF_ACCEPT);
+}
+
+int
+DenyCommand(struct cmdtab * list, int argc, char **argv)
+{
+ return ConfigCommand(list, argc, argv, 0, CONF_DENY);
+}
+
+int
+LocalAuthCommand(struct cmdtab * list, int argc, char **argv)
+{
+ char *pass;
+ if (argc == 0)
+ pass = "";
+ else if (argc > 1)
+ return -1;
+ else
+ pass = *argv;
+
+ if (VarHaveLocalAuthKey)
+ VarLocalAuth = strcmp(VarLocalAuthKey, pass) ? LOCAL_NO_AUTH : LOCAL_AUTH;
+ else
+ switch (LocalAuthValidate(SECRETFILE, VarShortHost, pass)) {
+ case INVALID:
+ VarLocalAuth = LOCAL_NO_AUTH;
+ break;
+ case VALID:
+ VarLocalAuth = LOCAL_AUTH;
+ break;
+ case NOT_FOUND:
+ VarLocalAuth = LOCAL_AUTH;
+ LogPrintf(LogWARN, "WARNING: No Entry for this system\n");
+ break;
+ default:
+ VarLocalAuth = LOCAL_NO_AUTH;
+ LogPrintf(LogERROR, "LocalAuthCommand: Ooops?\n");
+ return 1;
+ }
+ return 0;
+}
diff --git a/usr.sbin/ppp/vars.h b/usr.sbin/ppp/vars.h
new file mode 100644
index 0000000..3e205da
--- /dev/null
+++ b/usr.sbin/ppp/vars.h
@@ -0,0 +1,203 @@
+/*
+ * Written by Toshiharu OHNO (tony-o@iij.ad.jp)
+ *
+ * Copyright (C) 1993, Internet Initiative Japan, Inc. All rights reserverd.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the Internet Initiative Japan. The name of the
+ * IIJ may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: vars.h,v 1.32 1997/11/09 14:18:55 brian Exp $
+ *
+ * TODO:
+ */
+
+struct confdesc {
+ char *name;
+ int myside, hisside;
+};
+
+#define CONF_NONE -1
+#define CONF_DISABLE 0
+#define CONF_ENABLE 1
+
+#define CONF_DENY 0
+#define CONF_ACCEPT 1
+
+#define ConfVjcomp 0
+#define ConfLqr 1
+#define ConfChap 2
+#define ConfPap 3
+#define ConfAcfcomp 4
+#define ConfProtocomp 5
+#define ConfPred1 6
+#define ConfProxy 7
+#define ConfMSExt 8
+#define ConfPasswdAuth 9
+#define ConfUtmp 10
+#define MAXCONFS 11
+
+#define Enabled(x) (pppConfs[x].myside & CONF_ENABLE)
+#define Acceptable(x) (pppConfs[x].hisside & CONF_ACCEPT)
+
+extern struct confdesc pppConfs[MAXCONFS + 1];
+
+struct pppvars {
+ u_long var_mru; /* Initial MRU value */
+ u_long pref_mtu; /* Preferred MTU value */
+ int var_accmap; /* Initial ACCMAP value */
+ int modem_speed; /* Current modem speed */
+ int modem_parity; /* Parity setting */
+ int modem_ctsrts; /* Use CTS/RTS on modem port? (boolean) */
+ int idle_timeout; /* Idle timeout value */
+ int lqr_timeout; /* LQR timeout value */
+ int retry_timeout; /* Retry timeout value */
+ int reconnect_timer; /* Timeout before reconnect on carrier loss */
+ int reconnect_tries; /* Attempt reconnect on carrier loss */
+ int redial_timeout; /* Redial timeout value */
+ int redial_next_timeout; /* Redial next timeout value */
+ int dial_tries; /* Dial attempts before giving up, 0 == inf */
+ int loopback; /* Turn around packets addressed to me */
+ char modem_dev[40]; /* Name of device / host:port */
+ char *base_modem_dev; /* Pointer to base of modem_dev */
+ int open_mode; /* LCP open mode */
+#define LOCAL_AUTH 0x01
+#define LOCAL_NO_AUTH 0x02
+#define LOCAL_DENY 0x03
+ u_char lauth; /* Local Authorized status */
+ FILE *termfp; /* The terminal */
+#define DIALUP_REQ 0x01
+#define DIALUP_DONE 0x02
+ char dial_script[SCRIPT_LEN]; /* Dial script */
+ char login_script[SCRIPT_LEN]; /* Login script */
+ char auth_key[50]; /* PAP/CHAP key */
+ char auth_name[50]; /* PAP/CHAP system name */
+ char local_auth_key[50]; /* Local auth passwd */
+ int have_local_auth_key; /* Local auth passwd specified ? */
+#ifdef HAVE_DES
+ int use_MSChap; /* Use MSCHAP encryption */
+#endif
+ char phone_numbers[200]; /* Telephone Numbers */
+ char phone_copy[200]; /* copy for strsep() */
+ char *next_phone; /* Next phone from the list */
+ char *alt_phone; /* Next phone from the list */
+ char shostname[MAXHOSTNAMELEN]; /* Local short Host Name */
+ char hangup_script[SCRIPT_LEN]; /* Hangup script before modem is closed */
+ struct aliasHandlers handler; /* Alias function pointers */
+};
+
+#define VarAccmap pppVars.var_accmap
+#define VarMRU pppVars.var_mru
+#define VarPrefMTU pppVars.pref_mtu
+#define VarDevice pppVars.modem_dev
+#define VarBaseDevice pppVars.base_modem_dev
+#define VarSpeed pppVars.modem_speed
+#define VarParity pppVars.modem_parity
+#define VarCtsRts pppVars.modem_ctsrts
+#define VarOpenMode pppVars.open_mode
+#define VarLocalAuth pppVars.lauth
+#define VarDialScript pppVars.dial_script
+#define VarHangupScript pppVars.hangup_script
+#define VarLoginScript pppVars.login_script
+#define VarIdleTimeout pppVars.idle_timeout
+#define VarLqrTimeout pppVars.lqr_timeout
+#define VarRetryTimeout pppVars.retry_timeout
+#define VarAuthKey pppVars.auth_key
+#define VarAuthName pppVars.auth_name
+#define VarLocalAuthKey pppVars.local_auth_key
+#define VarHaveLocalAuthKey pppVars.have_local_auth_key
+#ifdef HAVE_DES
+#define VarMSChap pppVars.use_MSChap
+#endif
+#define VarPhoneList pppVars.phone_numbers
+#define VarPhoneCopy pppVars.phone_copy
+#define VarNextPhone pppVars.next_phone
+#define VarAltPhone pppVars.alt_phone
+#define VarShortHost pppVars.shostname
+#define VarReconnectTimer pppVars.reconnect_timer
+#define VarReconnectTries pppVars.reconnect_tries
+#define VarRedialTimeout pppVars.redial_timeout
+#define VarRedialNextTimeout pppVars.redial_next_timeout
+#define VarDialTries pppVars.dial_tries
+#define VarLoopback pppVars.loopback
+#define VarTerm pppVars.termfp
+
+#define VarAliasHandlers pppVars.handler
+#define VarPacketAliasGetFragment (*pppVars.handler.PacketAliasGetFragment)
+#define VarPacketAliasGetFragment (*pppVars.handler.PacketAliasGetFragment)
+#define VarPacketAliasInit (*pppVars.handler.PacketAliasInit)
+#define VarPacketAliasIn (*pppVars.handler.PacketAliasIn)
+#define VarPacketAliasOut (*pppVars.handler.PacketAliasOut)
+#define VarPacketAliasRedirectAddr (*pppVars.handler.PacketAliasRedirectAddr)
+#define VarPacketAliasRedirectPort (*pppVars.handler.PacketAliasRedirectPort)
+#define VarPacketAliasSaveFragment (*pppVars.handler.PacketAliasSaveFragment)
+#define VarPacketAliasSetAddress (*pppVars.handler.PacketAliasSetAddress)
+#define VarPacketAliasSetMode (*pppVars.handler.PacketAliasSetMode)
+#define VarPacketAliasFragmentIn (*pppVars.handler.PacketAliasFragmentIn)
+
+#define DEV_IS_SYNC (VarSpeed == 0)
+
+extern struct pppvars pppVars;
+extern char VarVersion[];
+extern char VarLocalVersion[];
+
+extern int Utmp; /* Are we in /etc/utmp ? */
+extern int ipInOctets;
+extern int ipOutOctets;
+extern int ipKeepAlive;
+extern int ipConnectSecs;
+extern int ipIdleSecs;
+extern int reconnectState;
+extern int reconnectCount;
+
+#define RECON_TRUE (1)
+#define RECON_FALSE (2)
+#define RECON_UNKNOWN (3)
+#define RECON_ENVOKED (4)
+#define reconnect(x) \
+ do \
+ if (reconnectState == RECON_UNKNOWN) { \
+ reconnectState = x; \
+ if (x == RECON_FALSE) \
+ reconnectCount = 0; \
+ } \
+ while(0)
+
+
+/*
+ * This is the logic behind the reconnect variables:
+ * We have four reconnect "states". We start off not requiring anything
+ * from the reconnect code (reconnectState == RECON_UNKNOWN). If the
+ * line is brought down (via LcpClose() or LcpDown()), we have to decide
+ * whether to set to RECON_TRUE or RECON_FALSE. It's only here that we
+ * know the correct action. Once we've decided, we don't want that
+ * decision to be overridden (hence the above reconnect() macro) - If we
+ * call LcpClose, the ModemTimeout() still gets to "notice" that the line
+ * is down. When it "notice"s, it should only set RECON_TRUE if a decision
+ * hasn't already been made.
+ *
+ * In main.c, when we notice we have RECON_TRUE, we must only action
+ * it once. The fourth "state" is where we're bringing the line up,
+ * but if we call LcpClose for any reason (failed PAP/CHAP etc) we
+ * don't want to set to RECON_{TRUE,FALSE}.
+ *
+ * If we get a connection or give up dialing, we go back to RECON_UNKNOWN.
+ * If we get give up dialing or reconnecting or if we chose to down the
+ * connection, we set reconnectCount back to zero.
+ *
+ */
+
+extern int EnableCommand(struct cmdtab *, int, char **);
+extern int DisableCommand(struct cmdtab *, int, char **);
+extern int AcceptCommand(struct cmdtab *, int, char **);
+extern int DenyCommand(struct cmdtab *, int, char **);
+extern int LocalAuthCommand(struct cmdtab *, int, char **);
+extern int DisplayCommand(void);
diff --git a/usr.sbin/ppp/vjcomp.c b/usr.sbin/ppp/vjcomp.c
new file mode 100644
index 0000000..e614e4b
--- /dev/null
+++ b/usr.sbin/ppp/vjcomp.c
@@ -0,0 +1,154 @@
+/*
+ * Input/Output VJ Compressed packets
+ *
+ * Written by Toshiharu OHNO (tony-o@iij.ad.jp)
+ *
+ * Copyright (C) 1993, Internet Initiative Japan, Inc. All rights reserverd.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the Internet Initiative Japan, Inc. The name of the
+ * IIJ may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: vjcomp.c,v 1.11 1997/10/26 01:04:01 brian Exp $
+ *
+ * TODO:
+ */
+#include <sys/types.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+
+#include <stdio.h>
+#include <string.h>
+
+#include "mbuf.h"
+#include "log.h"
+#include "defs.h"
+#include "timer.h"
+#include "fsm.h"
+#include "lcpproto.h"
+#include "slcompress.h"
+#include "hdlc.h"
+#include "ipcp.h"
+#include "vjcomp.h"
+
+#define MAX_VJHEADER 16 /* Maximum size of compressed header */
+
+struct slcompress cslc;
+
+void
+VjInit(int max_state)
+{
+ sl_compress_init(&cslc, max_state);
+}
+
+void
+SendPppFrame(struct mbuf * bp)
+{
+ int type;
+ int proto;
+ int cproto = IpcpInfo.his_compproto >> 16;
+
+ LogPrintf(LogDEBUG, "SendPppFrame: proto = %x\n", IpcpInfo.his_compproto);
+ if (((struct ip *) MBUF_CTOP(bp))->ip_p == IPPROTO_TCP
+ && cproto == PROTO_VJCOMP) {
+ type = sl_compress_tcp(bp, (struct ip *) MBUF_CTOP(bp), &cslc, IpcpInfo.his_compproto & 0xff);
+
+ LogPrintf(LogDEBUG, "SendPppFrame: type = %x\n", type);
+ switch (type) {
+ case TYPE_IP:
+ proto = PROTO_IP;
+ break;
+ case TYPE_UNCOMPRESSED_TCP:
+ proto = PROTO_VJUNCOMP;
+ break;
+ case TYPE_COMPRESSED_TCP:
+ proto = PROTO_VJCOMP;
+ break;
+ default:
+ LogPrintf(LogERROR, "Unknown frame type %x\n", type);
+ pfree(bp);
+ return;
+ }
+ } else
+ proto = PROTO_IP;
+ HdlcOutput(PRI_NORMAL, proto, bp);
+}
+
+static struct mbuf *
+VjUncompressTcp(struct mbuf * bp, u_char type)
+{
+ u_char *bufp;
+ int len, olen, rlen;
+ struct mbuf *nbp;
+ u_char work[MAX_HDR + MAX_VJHEADER]; /* enough to hold TCP/IP header */
+
+ olen = len = plength(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, &cslc);
+ if (len <= 0) {
+ pfree(bp);
+ bp = NULLBUFF;
+ }
+ return (bp);
+ }
+
+ /*
+ * Handle compressed packet. 1) Read upto MAX_VJHEADER bytes into work
+ * space. 2) Try to uncompress it. 3) Compute amount of necesary space. 4)
+ * Copy unread data info there.
+ */
+ if (len > MAX_VJHEADER)
+ len = MAX_VJHEADER;
+ rlen = len;
+ bufp = work + MAX_HDR;
+ bp = mbread(bp, bufp, rlen);
+ len = sl_uncompress_tcp(&bufp, olen, type, &cslc);
+ if (len <= 0) {
+ pfree(bp);
+ return NULLBUFF;
+ }
+ len -= olen;
+ len += rlen;
+ nbp = mballoc(len, MB_VJCOMP);
+ memcpy(MBUF_CTOP(nbp), bufp, len);
+ nbp->next = bp;
+ return (nbp);
+}
+
+struct mbuf *
+VjCompInput(struct mbuf * bp, int proto)
+{
+ u_char type;
+
+ LogPrintf(LogDEBUG, "VjCompInput: proto %02x\n", proto);
+ LogDumpBp(LogDEBUG, "Raw packet info:", bp);
+
+ switch (proto) {
+ case PROTO_VJCOMP:
+ type = TYPE_COMPRESSED_TCP;
+ break;
+ case PROTO_VJUNCOMP:
+ type = TYPE_UNCOMPRESSED_TCP;
+ break;
+ default:
+ LogPrintf(LogERROR, "VjCompInput...???\n");
+ return (bp);
+ }
+ bp = VjUncompressTcp(bp, type);
+ return (bp);
+}
diff --git a/usr.sbin/ppp/vjcomp.h b/usr.sbin/ppp/vjcomp.h
new file mode 100644
index 0000000..221b591
--- /dev/null
+++ b/usr.sbin/ppp/vjcomp.h
@@ -0,0 +1,7 @@
+/*
+ * $Id: vjcomp.h,v 1.1 1997/10/26 01:04:02 brian Exp $
+ */
+
+extern void VjInit(int);
+extern void SendPppFrame(struct mbuf *);
+extern struct mbuf *VjCompInput(struct mbuf *, int);
diff --git a/usr.sbin/pppctl/Makefile b/usr.sbin/pppctl/Makefile
new file mode 100644
index 0000000..7e50241
--- /dev/null
+++ b/usr.sbin/pppctl/Makefile
@@ -0,0 +1,10 @@
+# $Id: Makefile,v 1.1 1997/06/28 01:04:49 brian Exp $
+
+PROG= pppctl
+SRCS= pppctl.c
+CFLAGS+=-Wall -Wmissing-prototypes
+LDADD+= -ledit -ltermcap
+DPADD+= ${LIBEDIT} ${LIBTERMCAP}
+MAN8= pppctl.8
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/pppctl/pppctl.8 b/usr.sbin/pppctl/pppctl.8
new file mode 100644
index 0000000..6c778d6
--- /dev/null
+++ b/usr.sbin/pppctl/pppctl.8
@@ -0,0 +1,203 @@
+.\" $Id: pppctl.8,v 1.5 1997/11/07 02:54:46 brian Exp $
+.Dd 26 June 1997
+.Os FreeBSD
+.Dt PPPCTL 8
+.Sh NAME
+.Nm pppctl
+.Nd
+PPP control program
+.Sh SYNOPSIS
+.Nm
+.Op Fl v
+.Op Fl t Ar n
+.Op Fl p Ar passwd
+.Ar [host:]Port | LocalSocket
+.Op command[;command]...
+.Sh DESCRIPTION
+This program provides command line control of the
+.Xr ppp 8
+daemon. Its primary use is to facilitate simple scripts that
+control a running daemon.
+
+.Nm Pppctl
+is passed at least one argument, specifying the socket on which
+.Nm ppp
+is listening. Refer to the
+.Sq set server
+command of
+.Nm ppp
+for details. If the socket contains a leading '/', it
+is taken as an
+.Dv AF_LOCAL
+socket. If it contains a colon, it is treated as a
+.Ar host:port
+pair, otherwise it is treated as a TCP port specification on the
+local machine (127.0.0.1). Both the
+.Ar host
+and
+.Ar port
+may be specified numerically if you wish to avoid a DNS lookup
+or don't have an entry for the given port in
+.Pa /etc/services .
+
+.Pp
+All remaining arguments are concatenated to form the
+.Ar command(s)
+that will be sent to the
+.Nm ppp
+daemon. If any semi-colon characters are found, they are treated as
+.Ar command
+delimiters, allowing more than one
+.Ar command
+in a given "session". For example:
+
+ pppctl 3000 set timeout 300\\; show timeout
+
+Don't forget to escape or quote the ';' as it is a special character
+for most shells.
+
+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 .
+
+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 pppctl
+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. 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
+Assuming you want to run
+.Nm ppp
+in
+.Fl auto
+mode,
+.Nm
+can be used to automate many frequent tasks. Use of the
+.Fl p
+option is discouraged (even in scripts that aren't readably by others)
+as a
+.Xr ps 1
+listing may reveal your secret.
+.Pp
+In order to have
+.Nm ppp
+create a socket for use with
+.Nm pppctl ,
+you will need to define a password for your local system:
+.Bd -literal -offset indent
+# touch /etc/ppp/ppp.secret
+# chown root.wheel /etc/ppp/ppp.secret
+# chmod 400 /etc/ppp/ppp.secret
+# echo "`hostname -s` MyPassword" >>/etc/ppp/ppp.secret
+.Ed
+
+.Pp
+The most secure way to allow easy, secure
+.Nm
+access, and to make sure you can distinguish between multiple invocations
+of
+.Nm ppp
+is to create a local server socket in
+.Pa /etc/ppp/ppp.conf
+(in the correct section):
+
+.Bd -literal -offset indent
+set server /var/run/internet 0666
+.Ed
+
+This will instruct
+.Nm ppp
+to create a local domain socket rather than the tcp socket that's created
+by default. 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 -t 60 /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 '' | 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
+
+.Sh ENVIRONMENT VARIABLES
+The following environment variables are understood by
+.Nm pppctl
+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 editline 3 ,
+.Xr editrc 5 ,
+.Xr ppp 8 ,
+.Xr services 5
+
+.Sh HISTORY
+The
+.Nm
+command first appeared in FreeBSD 2.2.5.
diff --git a/usr.sbin/pppctl/pppctl.c b/usr.sbin/pppctl/pppctl.c
new file mode 100644
index 0000000..ea5b115
--- /dev/null
+++ b/usr.sbin/pppctl/pppctl.c
@@ -0,0 +1,358 @@
+#include <sys/types.h>
+
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <sys/un.h>
+#include <netdb.h>
+
+#include <histedit.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define LINELEN 2048
+static char Buffer[LINELEN], Command[LINELEN];
+
+static int
+Usage()
+{
+ fprintf(stderr, "Usage: pppctl [-v] [ -t n ] [ -p passwd ] "
+ "Port|LocalSock [command[;command]...]\n");
+ fprintf(stderr, " -v tells pppctl to output all"
+ " conversation\n");
+ fprintf(stderr, " -t n specifies a timeout of n"
+ " seconds (default 2)\n");
+ fprintf(stderr, " -p passwd specifies your password\n");
+ return 1;
+}
+
+static int TimedOut = 0;
+static void
+Timeout(int Sig)
+{
+ TimedOut = 1;
+}
+
+#define REC_PASSWD (1)
+#define REC_SHOW (2)
+#define REC_VERBOSE (4)
+
+static char *passwd;
+static char *prompt;
+
+static char *
+GetPrompt(EditLine *e)
+{
+ if (prompt == NULL)
+ prompt = "";
+ return prompt;
+}
+
+static int
+Receive(int fd, unsigned TimeoutVal, int display)
+{
+ int Result;
+ struct sigaction act, oact;
+ int len;
+ char *last;
+
+ TimedOut = 0;
+ if (TimeoutVal) {
+ act.sa_handler = Timeout;
+ sigemptyset(&act.sa_mask);
+ act.sa_flags = 0;
+ sigaction(SIGALRM, &act, &oact);
+ alarm(TimeoutVal);
+ }
+
+ prompt = Buffer;
+ len = 0;
+ while (Result = read(fd, Buffer+len, sizeof(Buffer)-len-1), Result != -1) {
+ len += Result;
+ Buffer[len] = '\0';
+ if (TimedOut) {
+ if (display & REC_VERBOSE)
+ write(1,Buffer,len);
+ Result = -1;
+ break;
+ } else if (len > 2 && !strcmp(Buffer+len-2, "> ")) {
+ prompt = strrchr(Buffer, '\n');
+ if (display & (REC_SHOW|REC_VERBOSE)) {
+ if (display & REC_VERBOSE)
+ last = Buffer+len-1;
+ else
+ last = prompt;
+ if (last) {
+ last++;
+ write(1, Buffer, last-Buffer);
+ }
+ }
+ prompt = prompt == NULL ? Buffer : prompt+1;
+ for (last = Buffer+len-2; last > Buffer && *last != ' '; last--)
+ ;
+ if (last > Buffer+3 && !strncmp(last-3, " on", 3)) {
+ /* a password is required ! */
+ if (display & REC_PASSWD) {
+ if (TimeoutVal) {
+ alarm(0);
+ sigaction(SIGALRM, &oact, 0);
+ }
+ /* password time */
+ if (!passwd)
+ passwd = getpass("Password: ");
+ sprintf(Buffer, "passwd %s\n", passwd);
+ memset(passwd, '\0', strlen(passwd));
+ if (display & REC_VERBOSE)
+ write(1, Buffer, strlen(Buffer));
+ write(fd, Buffer, strlen(Buffer));
+ memset(Buffer, '\0', strlen(Buffer));
+ return Receive(fd, TimeoutVal, display & ~REC_PASSWD);
+ }
+ Result = 1;
+ } else
+ Result = 0;
+ break;
+ }
+ }
+
+ if (TimedOut)
+ Result = -1;
+
+ if (TimeoutVal) {
+ alarm(0);
+ sigaction(SIGALRM, &oact, 0);
+ }
+ return Result;
+}
+
+int
+main(int argc, char **argv)
+{
+ struct servent *s;
+ struct hostent *h;
+ struct sockaddr *sock;
+ struct sockaddr_in ifsin;
+ struct sockaddr_un ifsun;
+ int socksz, arg, fd, len, verbose;
+ unsigned TimeoutVal;
+ char *DoneWord = "x", *next, *start;
+ struct sigaction act, oact;
+
+ verbose = 0;
+ TimeoutVal = 2;
+
+ for (arg = 1; arg < argc; arg++)
+ if (*argv[arg] == '-') {
+ for (start = argv[arg] + 1; *start; start++)
+ switch (*start) {
+ case 't':
+ TimeoutVal = (unsigned)atoi
+ (start[1] ? start + 1 : argv[++arg]);
+ start = DoneWord;
+ break;
+
+ case 'v':
+ verbose = REC_VERBOSE;
+ break;
+
+ case 'p':
+ passwd = (start[1] ? start + 1 : argv[++arg]);
+ start = DoneWord;
+ break;
+
+ default:
+ return Usage();
+ }
+ }
+ else
+ break;
+
+
+ if (argc < arg + 1)
+ return Usage();
+
+ if (*argv[arg] == '/') {
+ sock = (struct sockaddr *)&ifsun;
+ socksz = sizeof ifsun;
+
+ ifsun.sun_len = strlen(argv[arg]);
+ if (ifsun.sun_len > sizeof ifsun.sun_path - 1) {
+ fprintf(stderr, "%s: Path too long\n", argv[arg]);
+ return 1;
+ }
+ ifsun.sun_family = AF_LOCAL;
+ strcpy(ifsun.sun_path, argv[arg]);
+
+ if (fd = socket(AF_LOCAL, SOCK_STREAM, 0), fd < 0) {
+ fprintf(stderr, "Cannot create local domain socket\n");
+ return 2;
+ }
+ } else {
+ char *port, *host, *colon;
+ int hlen;
+
+ colon = strchr(argv[arg], ':');
+ if (colon) {
+ port = colon + 1;
+ *colon = '\0';
+ host = argv[arg];
+ } else {
+ port = argv[arg];
+ host = "127.0.0.1";
+ }
+ sock = (struct sockaddr *)&ifsin;
+ socksz = sizeof ifsin;
+ hlen = strlen(host);
+
+ if (strspn(host, "0123456789.") == hlen) {
+ if (!inet_aton(host, (struct in_addr *)&ifsin.sin_addr.s_addr)) {
+ fprintf(stderr, "Cannot translate %s\n", host);
+ return 1;
+ }
+ } else if ((h = gethostbyname(host)) == 0) {
+ fprintf(stderr, "Cannot resolve %s\n", host);
+ return 1;
+ }
+ else
+ ifsin.sin_addr.s_addr = *(u_long *)h->h_addr_list[0];
+
+ if (colon)
+ *colon = ':';
+
+ if (strspn(port, "0123456789") == strlen(port))
+ ifsin.sin_port = htons(atoi(port));
+ else if (s = getservbyname(port, "tcp"), !s) {
+ fprintf(stderr, "%s isn't a valid port or service!\n", port);
+ return Usage();
+ }
+ else
+ ifsin.sin_port = s->s_port;
+
+ ifsin.sin_len = sizeof(ifsin);
+ ifsin.sin_family = AF_INET;
+
+ if (fd = socket(AF_INET, SOCK_STREAM, 0), fd < 0) {
+ fprintf(stderr, "Cannot create internet socket\n");
+ return 2;
+ }
+ }
+
+ TimedOut = 0;
+ if (TimeoutVal) {
+ act.sa_handler = Timeout;
+ sigemptyset(&act.sa_mask);
+ act.sa_flags = 0;
+ sigaction(SIGALRM, &act, &oact);
+ alarm(TimeoutVal);
+ }
+
+ if (connect(fd, sock, socksz) < 0) {
+ if (TimeoutVal) {
+ alarm(0);
+ sigaction(SIGALRM, &oact, 0);
+ }
+ if (TimedOut)
+ fputs("Timeout: ", stderr);
+ fprintf(stderr, "Cannot connect to socket %s\n", 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, TimeoutVal, verbose | REC_PASSWD))
+ {
+ case 1:
+ fprintf(stderr, "Password incorrect\n");
+ break;
+
+ case 0:
+ if (len == 0) {
+ EditLine *edit;
+ History *hist;
+ const char *l, *env;
+ int size;
+
+ hist = history_init();
+ if ((env = getenv("EL_SIZE"))) {
+ size = atoi(env);
+ if (size < 0)
+ size = 20;
+ } else
+ size = 20;
+ history(hist, H_EVENT, size);
+
+ edit = el_init("pppctl", stdin, stdout);
+ el_source(edit, NULL);
+ el_set(edit, EL_PROMPT, GetPrompt);
+ if ((env = getenv("EL_EDITOR")))
+ if (!strcmp(env, "vi"))
+ el_set(edit, EL_EDITOR, "vi");
+ else if (!strcmp(env, "emacs"))
+ el_set(edit, EL_EDITOR, "emacs");
+ el_set(edit, EL_SIGNAL, 1);
+ el_set(edit, EL_HIST, history, (const char *)hist);
+ while ((l = el_gets(edit, &len))) {
+ if (len > 1)
+ history(hist, H_ENTER, l);
+ write(fd, l, len);
+ if (!strcasecmp(l, "quit\n") ||
+ !strcasecmp(l, "bye\n")) /* ok, we're cheating */
+ break;
+ if (Receive(fd, TimeoutVal, REC_SHOW) != 0) {
+ fprintf(stderr, "Connection closed\n");
+ break;
+ }
+ }
+ el_end(edit);
+ history_end(hist);
+ } else {
+ start = Command;
+ do {
+ next = strchr(start, ';');
+ while (*start == ' ' || *start == '\t')
+ start++;
+ if (next)
+ *next = '\0';
+ strcpy(Buffer, start);
+ Buffer[sizeof(Buffer)-2] = '\0';
+ strcat(Buffer, "\n");
+ if (verbose)
+ write(1, Buffer, strlen(Buffer));
+ write(fd, Buffer, strlen(Buffer));
+ if (Receive(fd, TimeoutVal, verbose | REC_SHOW) != 0) {
+ fprintf(stderr, "No reply from ppp\n");
+ break;
+ }
+ if (next)
+ start = ++next;
+ } while (next && *next);
+ if (verbose)
+ puts("");
+ }
+ break;
+
+ default:
+ fprintf(stderr, "ppp is not responding\n");
+ break;
+ }
+
+ close(fd);
+
+ return 0;
+}
diff --git a/usr.sbin/pppd/Makefile b/usr.sbin/pppd/Makefile
new file mode 100644
index 0000000..7d58b9f
--- /dev/null
+++ b/usr.sbin/pppd/Makefile
@@ -0,0 +1,31 @@
+# $Id: Makefile,v 1.9 1997/08/22 15:57:37 peter Exp $
+
+CFLAGS+= -DHAVE_PATHS_H
+
+PROG= pppd
+SRCS= main.c magic.c fsm.c lcp.c ipcp.c ipxcp.c upap.c chap.c ccp.c \
+ demand.c auth.c options.c sys-bsd.c
+MAN8= pppd.8
+BINMODE=4555
+BINOWN= root
+
+# as per handbook policies section
+MAINTAINER= peter@freebsd.org
+
+LDADD= -lcrypt -lutil -lmd
+DPADD= ${LIBCRYPT} ${LIBUTIL} ${LIBMD}
+
+# Support SPX/IPX - not quite ready
+#CFLAGS+=-DIPX_CHANGE
+#SRCS+= ipxcp.c
+
+# Callback Control Protocol
+CFLAGS+=-DCBCP_SUPPORT
+SRCS+= cbcp.c
+
+# Filter support
+CFLAGS+=-DPPP_FILTER
+LDADD+= -lpcap
+DPADD+= ${LIBPCAP}
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/pppd/RELNOTES b/usr.sbin/pppd/RELNOTES
new file mode 100644
index 0000000..fdb989b
--- /dev/null
+++ b/usr.sbin/pppd/RELNOTES
@@ -0,0 +1,641 @@
+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.)
+
+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.
+
+
+What's 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.
+[[ ***************************
+[[ NOTE: This change has NOT been incorporated into the FreeBSD version
+[[ due to tty handling differences! (This causes EIO during chat(8))
+
+* 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's 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/args.h b/usr.sbin/pppd/args.h
new file mode 100644
index 0000000..81bd1e0
--- /dev/null
+++ b/usr.sbin/pppd/args.h
@@ -0,0 +1,12 @@
+/*
+ * neat macro from ka9q to "do the right thing" with ansi prototypes
+ * $Id$
+ */
+
+#ifndef __P
+#ifdef __STDC__
+#define __P(x) x
+#else
+#define __P(x) ()
+#endif
+#endif
diff --git a/usr.sbin/pppd/auth.c b/usr.sbin/pppd/auth.c
new file mode 100644
index 0000000..4e9745c
--- /dev/null
+++ b/usr.sbin/pppd/auth.c
@@ -0,0 +1,1531 @@
+/*
+ * auth.c - PPP authentication and phase control.
+ *
+ * Copyright (c) 1993 The Australian National University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the Australian National University. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef lint
+static char rcsid[] = "$Id: auth.c,v 1.19 1997/10/10 06:02:54 peter Exp $";
+#endif
+
+#include <stdio.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <syslog.h>
+#include <pwd.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+
+#include <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>
+#include <security/pam_modules.h>
+#endif
+
+#ifdef HAS_SHADOW
+#include <shadow.h>
+#ifndef SVR4
+#include <shadow/pwauth.h>
+#endif
+#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 login() */
+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 ppplogin __P((char *, char *, char **, int *));
+static void ppplogout __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 *));
+#ifdef CBCP_SUPPORT
+static void callback_phase __P((int));
+#endif
+
+/*
+ * 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)
+ ppplogout();
+ 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);
+
+ /*
+ * 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);
+ }
+ ++num_np_up;
+}
+
+/*
+ * np_down - a network protocol has gone down.
+ */
+void
+np_down(unit, proto)
+ int unit, proto;
+{
+ if (--num_np_up == 0 && idle_time_limit > 0) {
+ UNTIMEOUT(check_idle, NULL);
+ }
+}
+
+/*
+ * np_finished - a network protocol has finished using the link.
+ */
+void
+np_finished(unit, proto)
+ int unit, proto;
+{
+ if (--num_np_open <= 0) {
+ /* no further use for the link: shut up shop. */
+ lcp_close(0, "No network protocols running");
+ }
+}
+
+/*
+ * check_idle - check whether the link has been idle for long
+ * enough that we can shut it down.
+ */
+static void
+check_idle(arg)
+ void *arg;
+{
+ struct ppp_idle idle;
+ time_t itime;
+
+ if (!get_idle_time(0, &idle))
+ return;
+ itime = MIN(idle.xmit_idle, idle.recv_idle);
+ if (itime >= idle_time_limit) {
+ /* link is idle: shut it down. */
+ syslog(LOG_INFO, "Terminating connection due to lack of activity.");
+ lcp_close(0, "Link inactive");
+ } else {
+ TIMEOUT(check_idle, NULL, idle_time_limit - itime);
+ }
+}
+
+/*
+ * connect_time_expired - log a message and close the connection.
+ */
+static void
+connect_time_expired(arg)
+ void *arg;
+{
+ syslog(LOG_INFO, "Connect time expired");
+ lcp_close(0, "Connect time expired"); /* Close connection */
+}
+
+/*
+ * auth_check_options - called to check authentication options.
+ */
+void
+auth_check_options()
+{
+ lcp_options *wo = &lcp_wantoptions[0];
+ int can_auth;
+ ipcp_options *ipwo = &ipcp_wantoptions[0];
+ u_int32_t remote;
+
+ /* Default our_name to hostname, and user to our_name */
+ if (our_name[0] == 0 || usehostname)
+ strcpy(our_name, hostname);
+ if (user[0] == 0)
+ strcpy(user, our_name);
+
+ /* If authentication is required, ask peer for CHAP or PAP. */
+ if (auth_required && !wo->neg_chap && !wo->neg_upap) {
+ wo->neg_chap = 1;
+ wo->neg_upap = 1;
+ }
+
+ /*
+ * Check whether we have appropriate secrets to use
+ * to authenticate the peer.
+ */
+ can_auth = wo->neg_upap && (uselogin || have_pap_secret());
+ if (!can_auth && wo->neg_chap) {
+ remote = ipwo->accept_remote? 0: ipwo->hisaddr;
+ can_auth = have_chap_secret(remote_name, our_name, remote);
+ }
+
+ if (auth_required && !can_auth) {
+ option_error("peer authentication required but no suitable secret(s) found\n");
+ if (remote_name[0] == 0)
+ option_error("for authenticating any peer to us (%s)\n", our_name);
+ else
+ option_error("for authenticating peer %s to us (%s)\n",
+ remote_name, our_name);
+ exit(1);
+ }
+
+ /*
+ * Check whether the user tried to override certain values
+ * set by root.
+ */
+ if (!auth_required && auth_req_info.priv > 0) {
+ if (!default_device && devnam_info.priv == 0) {
+ option_error("can't override device name when noauth option used");
+ exit(1);
+ }
+ if ((connector != NULL && connector_info.priv == 0)
+ || (disconnector != NULL && disconnector_info.priv == 0)
+ || (welcomer != NULL && welcomer_info.priv == 0)) {
+ option_error("can't override connect, disconnect or welcome");
+ option_error("option values when noauth option used");
+ exit(1);
+ }
+ }
+}
+
+/*
+ * auth_reset - called when LCP is starting negotiations to recheck
+ * authentication options, i.e. whether we have appropriate secrets
+ * to use for authenticating ourselves and/or the peer.
+ */
+void
+auth_reset(unit)
+ int unit;
+{
+ lcp_options *go = &lcp_gotoptions[unit];
+ lcp_options *ao = &lcp_allowoptions[0];
+ ipcp_options *ipwo = &ipcp_wantoptions[0];
+ u_int32_t remote;
+
+ ao->neg_upap = !refuse_pap && (passwd[0] != 0 || get_pap_passwd(NULL));
+ ao->neg_chap = !refuse_chap
+ && have_chap_secret(user, remote_name, (u_int32_t)0);
+
+ if (go->neg_upap && !uselogin && !have_pap_secret())
+ go->neg_upap = 0;
+ if (go->neg_chap) {
+ remote = ipwo->accept_remote? 0: ipwo->hisaddr;
+ if (!have_chap_secret(remote_name, our_name, remote))
+ go->neg_chap = 0;
+ }
+
+}
+
+
+/*
+ * check_passwd - Check the user name and passwd against the PAP secrets
+ * file. If requested, also check against the system password database,
+ * and login the user if OK.
+ *
+ * returns:
+ * UPAP_AUTHNAK: Authentication failed.
+ * UPAP_AUTHACK: Authentication succeeded.
+ * In either case, msg points to an appropriate message.
+ */
+int
+check_passwd(unit, auser, userlen, apasswd, passwdlen, msg, msglen)
+ int unit;
+ char *auser;
+ int userlen;
+ char *apasswd;
+ int passwdlen;
+ char **msg;
+ int *msglen;
+{
+ int ret;
+ char *filename;
+ FILE *f;
+ struct wordlist *addrs;
+ u_int32_t remote;
+ ipcp_options *ipwo = &ipcp_wantoptions[unit];
+ char passwd[256], user[256];
+ char secret[MAXWORDLEN];
+ static int attempts = 0;
+ int len;
+
+ /*
+ * Make copies of apasswd and auser, then null-terminate them.
+ */
+ len = MIN(passwdlen, sizeof(passwd) - 1);
+ BCOPY(apasswd, passwd, len);
+ passwd[len] = '\0';
+ len = MIN(userlen, sizeof(user) - 1);
+ BCOPY(auser, user, len);
+ user[len] = '\0';
+ *msg = (char *) 0;
+
+ /*
+ * Open the file of pap secrets and scan for a suitable secret
+ * for authenticating this user.
+ */
+ filename = _PATH_UPAPFILE;
+ addrs = NULL;
+ ret = UPAP_AUTHACK;
+ f = fopen(filename, "r");
+ if (f == NULL) {
+ syslog(LOG_ERR, "Can't open PAP password file %s: %m", filename);
+ ret = UPAP_AUTHNAK;
+
+ } else {
+ check_access(f, filename);
+ remote = ipwo->accept_remote? 0: ipwo->hisaddr;
+ if (scan_authfile(f, user, our_name, remote,
+ secret, &addrs, filename) < 0
+ || (secret[0] != 0 && (cryptpap || strcmp(passwd, secret) != 0)
+ && strcmp(crypt(passwd, secret), secret) != 0)) {
+ syslog(LOG_WARNING, "PAP authentication failure for %s", user);
+ ret = UPAP_AUTHNAK;
+ }
+ fclose(f);
+ }
+
+ if (uselogin && ret == UPAP_AUTHACK) {
+ ret = ppplogin(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. However, it should not be called.
+ * If it is, return the error code.
+ */
+
+#ifdef USE_PAM
+static int pam_conv(int num_msg, const struct pam_message **msg,
+ struct pam_response **resp, void *appdata_ptr)
+{
+ return PAM_CONV_ERR;
+}
+#endif
+
+/*
+ * ppplogin - 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
+ppplogin(user, passwd, msg, msglen)
+ char *user;
+ char *passwd;
+ char **msg;
+ int *msglen;
+{
+ char *tty;
+
+#ifdef USE_PAM
+ struct pam_conv pam_conversation;
+ pam_handle_t *pamh;
+ int pam_error;
+ char *pass;
+ char *dev;
+/*
+ * 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 = (char *) pam_strerror (pam_error);
+ return UPAP_AUTHNAK;
+ }
+/*
+ * Define the fields for the credintial validation
+ */
+ (void) pam_set_item (pamh, PAM_AUTHTOK, passwd);
+ (void) pam_set_item (pamh, PAM_TTY, devnam);
+/*
+ * Validate the user
+ */
+ pam_error = pam_authenticate (pamh, PAM_SILENT);
+ if (pam_error == PAM_SUCCESS)
+ pam_error = pam_acct_mgmt (pamh, PAM_SILENT);
+
+ *msg = (char *) pam_strerror (pam_error);
+/*
+ * 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 *epasswd;
+
+#ifdef HAS_SHADOW
+ struct spwd *spwd;
+ struct spwd *getspnam();
+ extern int isexpired (struct passwd *, struct spwd *); /* in libshadow.a */
+#endif
+
+ pw = getpwnam(user);
+ 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 */
+ if (isexpired(pw, spwd)) {
+ syslog(LOG_WARNING,"Expired password for %s",user);
+ return (UPAP_AUTHNAK);
+ }
+ pw->pw_passwd = spwd->sp_pwdp;
+ }
+#endif
+
+ /*
+ * If no passwd, don't let them login.
+ */
+ if (pw->pw_passwd[0] != '\0') {
+
+#ifdef HAS_SHADOW
+ if ((pw->pw_passwd && pw->pw_passwd[0] == '@'
+ && pw_auth (pw->pw_passwd+1, pw->pw_name, PW_PPP, NULL))
+ || !valid (passwd, pw)) {
+ return (UPAP_AUTHNAK);
+ }
+#else
+ epasswd = crypt(passwd, pw->pw_passwd);
+ if (strcmp(epasswd, pw->pw_passwd)) {
+ return (UPAP_AUTHNAK);
+ }
+#endif
+
+ 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);
+ }
+ }
+ } /* if password */
+#endif /* #ifdef USE_PAM */
+
+ syslog(LOG_INFO, "user %s logged in", user);
+
+ /* Log in wtmp and utmp using login() */
+
+ tty = devnam;
+ if (strncmp(tty, "/dev/", 5) == 0)
+ tty += 5;
+
+ if (logout(tty)) /* Already entered (by login?) */
+ logwtmp(tty, "", "");
+
+ logged_in = TRUE;
+
+ 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 */
+
+ return (UPAP_AUTHACK);
+}
+
+/*
+ * ppplogout - Logout the user.
+ */
+static void
+ppplogout()
+{
+ char *tty;
+
+ tty = devnam;
+ if (strncmp(tty, "/dev/", 5) == 0)
+ tty += 5;
+ logwtmp(tty, "", ""); /* Wipe out wtmp logout entry */
+ logged_in = FALSE;
+
+ logout(tty); /* Wipe out utmp */
+}
+
+
+/*
+ * 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 (wo->hisaddr == 0 && *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 != '\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/callout.h b/usr.sbin/pppd/callout.h
new file mode 100644
index 0000000..662100b
--- /dev/null
+++ b/usr.sbin/pppd/callout.h
@@ -0,0 +1,18 @@
+/* Note: This is a copy of /usr/include/sys/callout.h with the c_func */
+/* member of struct callout changed from a pointer to a function of type int*/
+/* to a pointer to a function of type void (generic pointer) as per */
+/* ANSI C */
+
+/* $Id$ */
+
+#ifndef _ppp_callout_h
+#define _ppp_callout_h
+
+struct callout {
+ int c_time; /* incremental time */
+ caddr_t c_arg; /* argument to routine */
+ void (*c_func)(); /* routine (changed to void (*)() */
+ struct callout *c_next;
+};
+
+#endif /*!_ppp_callout_h*/
diff --git a/usr.sbin/pppd/cbcp.c b/usr.sbin/pppd/cbcp.c
new file mode 100644
index 0000000..db939ba
--- /dev/null
+++ b/usr.sbin/pppd/cbcp.c
@@ -0,0 +1,430 @@
+/*
+ * cbcp - Call Back Configuration Protocol.
+ *
+ * Copyright (c) 1995 Pedro Roque Marques
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Pedro Roque Marques. The name of the author may not be used to
+ * endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef lint
+static char rcsid[] = "$Id$";
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <syslog.h>
+
+#include "pppd.h"
+#include "cbcp.h"
+#include "fsm.h"
+#include "lcp.h"
+#include "ipcp.h"
+
+/*
+ * Protocol entry points.
+ */
+static void cbcp_init __P((int unit));
+static void cbcp_open __P((int unit));
+static void cbcp_lowerup __P((int unit));
+static void cbcp_input __P((int unit, u_char *pkt, int len));
+static void cbcp_protrej __P((int unit));
+static int cbcp_printpkt __P((u_char *pkt, int len,
+ void (*printer) __P((void *, char *, ...)),
+ void *arg));
+
+struct protent cbcp_protent = {
+ PPP_CBCP,
+ cbcp_init,
+ cbcp_input,
+ cbcp_protrej,
+ cbcp_lowerup,
+ NULL,
+ cbcp_open,
+ NULL,
+ cbcp_printpkt,
+ NULL,
+ 0,
+ "CBCP",
+ NULL,
+ NULL,
+ NULL
+};
+
+cbcp_state cbcp[NUM_PPP];
+
+/* internal prototypes */
+
+static void cbcp_recvreq __P((cbcp_state *us, char *pckt, int len));
+static void cbcp_resp __P((cbcp_state *us));
+static void cbcp_up __P((cbcp_state *us));
+static void cbcp_recvack __P((cbcp_state *us, char *pckt, int len));
+static void cbcp_send __P((cbcp_state *us, u_char code, u_char *buf, int len));
+
+/* init state */
+static void
+cbcp_init(iface)
+ int iface;
+{
+ cbcp_state *us;
+
+ us = &cbcp[iface];
+ memset(us, 0, sizeof(cbcp_state));
+ us->us_unit = iface;
+ us->us_type |= (1 << CB_CONF_NO);
+}
+
+/* lower layer is up */
+static void
+cbcp_lowerup(iface)
+ int iface;
+{
+ cbcp_state *us = &cbcp[iface];
+
+ syslog(LOG_DEBUG, "cbcp_lowerup");
+ syslog(LOG_DEBUG, "want: %d", us->us_type);
+
+ if (us->us_type == CB_CONF_USER)
+ syslog(LOG_DEBUG, "phone no: %s", us->us_number);
+}
+
+static void
+cbcp_open(unit)
+ int unit;
+{
+ syslog(LOG_DEBUG, "cbcp_open");
+}
+
+/* process an incomming packet */
+static void
+cbcp_input(unit, inpacket, pktlen)
+ int unit;
+ u_char *inpacket;
+ int pktlen;
+{
+ u_char *inp;
+ u_char code, id;
+ u_short len;
+
+ cbcp_state *us = &cbcp[unit];
+
+ inp = inpacket;
+
+ if (pktlen < CBCP_MINLEN) {
+ syslog(LOG_ERR, "CBCP packet is too small");
+ return;
+ }
+
+ GETCHAR(code, inp);
+ GETCHAR(id, inp);
+ GETSHORT(len, inp);
+
+#if 0
+ if (len > pktlen) {
+ syslog(LOG_ERR, "CBCP packet: invalid length");
+ return;
+ }
+#endif
+
+ len -= CBCP_MINLEN;
+
+ switch(code) {
+ case CBCP_REQ:
+ us->us_id = id;
+ cbcp_recvreq(us, inp, len);
+ break;
+
+ case CBCP_RESP:
+ syslog(LOG_DEBUG, "CBCP_RESP received");
+ break;
+
+ case CBCP_ACK:
+ if (id != us->us_id)
+ syslog(LOG_DEBUG, "id doesn't match: expected %d recv %d",
+ us->us_id, id);
+
+ cbcp_recvack(us, inp, len);
+ break;
+
+ default:
+ break;
+ }
+}
+
+/* protocol was rejected by foe */
+void cbcp_protrej(int iface)
+{
+}
+
+char *cbcp_codenames[] = {
+ "Request", "Response", "Ack"
+};
+
+char *cbcp_optionnames[] = {
+ "NoCallback",
+ "UserDefined",
+ "AdminDefined",
+ "List"
+};
+
+/* pretty print a packet */
+static int
+cbcp_printpkt(p, plen, printer, arg)
+ u_char *p;
+ int plen;
+ void (*printer) __P((void *, char *, ...));
+ void *arg;
+{
+ int code, opt, id, len, olen, delay;
+ u_char *pstart;
+
+ if (plen < HEADERLEN)
+ return 0;
+ pstart = p;
+ GETCHAR(code, p);
+ GETCHAR(id, p);
+ GETSHORT(len, p);
+ if (len < HEADERLEN || len > plen)
+ return 0;
+
+ if (code >= 1 && code <= sizeof(cbcp_codenames) / sizeof(char *))
+ printer(arg, " %s", cbcp_codenames[code-1]);
+ else
+ printer(arg, " code=0x%x", code);
+
+ printer(arg, " id=0x%x", id);
+ len -= HEADERLEN;
+
+ switch (code) {
+ case CBCP_REQ:
+ case CBCP_RESP:
+ case CBCP_ACK:
+ while(len >= 2) {
+ GETCHAR(opt, p);
+ GETCHAR(olen, p);
+
+ if (olen < 2 || olen > len) {
+ break;
+ }
+
+ printer(arg, " <");
+ len -= olen;
+
+ if (opt >= 1 && opt <= sizeof(cbcp_optionnames) / sizeof(char *))
+ printer(arg, " %s", cbcp_optionnames[opt-1]);
+ else
+ printer(arg, " option=0x%x", opt);
+
+ if (olen > 2) {
+ GETCHAR(delay, p);
+ printer(arg, " delay = %d", delay);
+ }
+
+ if (olen > 3) {
+ int addrt;
+ char str[256];
+
+ GETCHAR(addrt, p);
+ memcpy(str, p, olen - 4);
+ str[olen - 4] = 0;
+ printer(arg, " number = %s", str);
+ }
+ printer(arg, ">");
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ for (; len > 0; --len) {
+ GETCHAR(code, p);
+ printer(arg, " %.2x", code);
+ }
+
+ return p - pstart;
+}
+
+/* received CBCP request */
+static void
+cbcp_recvreq(us, pckt, pcktlen)
+ cbcp_state *us;
+ char *pckt;
+ int pcktlen;
+{
+ u_char type, opt_len, delay, addr_type;
+ char address[256];
+ int len = pcktlen;
+
+ address[0] = 0;
+
+ while (len) {
+ syslog(LOG_DEBUG, "length: %d", len);
+
+ GETCHAR(type, pckt);
+ GETCHAR(opt_len, pckt);
+
+ if (opt_len > 2)
+ GETCHAR(delay, pckt);
+
+ us->us_allowed |= (1 << type);
+
+ switch(type) {
+ case CB_CONF_NO:
+ syslog(LOG_DEBUG, "no callback allowed");
+ break;
+
+ case CB_CONF_USER:
+ syslog(LOG_DEBUG, "user callback allowed");
+ if (opt_len > 4) {
+ GETCHAR(addr_type, pckt);
+ memcpy(address, pckt, opt_len - 4);
+ address[opt_len - 4] = 0;
+ if (address[0])
+ syslog(LOG_DEBUG, "address: %s", address);
+ }
+ break;
+
+ case CB_CONF_ADMIN:
+ syslog(LOG_DEBUG, "user admin defined allowed");
+ break;
+
+ case CB_CONF_LIST:
+ break;
+ }
+ len -= opt_len;
+ }
+
+ cbcp_resp(us);
+}
+
+static void
+cbcp_resp(us)
+ cbcp_state *us;
+{
+ u_char cb_type;
+ u_char buf[256];
+ u_char *bufp = buf;
+ int len = 0;
+
+ cb_type = us->us_allowed & us->us_type;
+ syslog(LOG_DEBUG, "cbcp_resp cb_type=%d", cb_type);
+
+#if 0
+ if (!cb_type)
+ lcp_down(us->us_unit);
+#endif
+
+ if (cb_type & ( 1 << CB_CONF_USER ) ) {
+ syslog(LOG_DEBUG, "cbcp_resp CONF_USER");
+ PUTCHAR(CB_CONF_USER, bufp);
+ len = 3 + 1 + strlen(us->us_number) + 1;
+ PUTCHAR(len , bufp);
+ PUTCHAR(5, bufp); /* delay */
+ PUTCHAR(1, bufp);
+ BCOPY(us->us_number, bufp, strlen(us->us_number) + 1);
+ cbcp_send(us, CBCP_RESP, buf, len);
+ return;
+ }
+
+ if (cb_type & ( 1 << CB_CONF_ADMIN ) ) {
+ syslog(LOG_DEBUG, "cbcp_resp CONF_ADMIN");
+ PUTCHAR(CB_CONF_ADMIN, bufp);
+ len = 3 + 1;
+ PUTCHAR(len , bufp);
+ PUTCHAR(5, bufp); /* delay */
+ PUTCHAR(0, bufp);
+ cbcp_send(us, CBCP_RESP, buf, len);
+ return;
+ }
+
+ if (cb_type & ( 1 << CB_CONF_NO ) ) {
+ syslog(LOG_DEBUG, "cbcp_resp CONF_NO");
+ PUTCHAR(CB_CONF_NO, bufp);
+ len = 3;
+ PUTCHAR(len , bufp);
+ PUTCHAR(0, bufp);
+ cbcp_send(us, CBCP_RESP, buf, len);
+ (*ipcp_protent.open)(us->us_unit);
+ return;
+ }
+}
+
+static void
+cbcp_send(us, code, buf, len)
+ cbcp_state *us;
+ u_char code;
+ u_char *buf;
+ int len;
+{
+ u_char *outp;
+ int outlen;
+
+ outp = outpacket_buf;
+
+ outlen = 4 + len;
+
+ MAKEHEADER(outp, PPP_CBCP);
+
+ PUTCHAR(code, outp);
+ PUTCHAR(us->us_id, outp);
+ PUTSHORT(outlen, outp);
+
+ if (len)
+ BCOPY(buf, outp, len);
+
+ output(us->us_unit, outpacket_buf, outlen + PPP_HDRLEN);
+}
+
+static void
+cbcp_recvack(us, pckt, len)
+ cbcp_state *us;
+ char *pckt;
+ int len;
+{
+ u_char type, delay, addr_type;
+ int opt_len;
+ char address[256];
+
+ if (len) {
+ GETCHAR(type, pckt);
+ GETCHAR(opt_len, pckt);
+
+ if (opt_len > 2)
+ GETCHAR(delay, pckt);
+
+ if (opt_len > 4) {
+ GETCHAR(addr_type, pckt);
+ memcpy(address, pckt, opt_len - 4);
+ address[opt_len - 4] = 0;
+ if (address[0])
+ syslog(LOG_DEBUG, "peer will call: %s", address);
+ }
+ }
+
+ cbcp_up(us);
+}
+
+extern int persist;
+
+/* ok peer will do callback */
+static void
+cbcp_up(us)
+ cbcp_state *us;
+{
+ persist = 0;
+ lcp_close(0, "Call me back, please");
+}
diff --git a/usr.sbin/pppd/cbcp.h b/usr.sbin/pppd/cbcp.h
new file mode 100644
index 0000000..c2ab3f6
--- /dev/null
+++ b/usr.sbin/pppd/cbcp.h
@@ -0,0 +1,26 @@
+#ifndef CBCP_H
+#define CBCP_H
+
+typedef struct cbcp_state {
+ int us_unit; /* Interface unit number */
+ u_char us_id; /* Current id */
+ u_char us_allowed;
+ int us_type;
+ char *us_number; /* Telefone Number */
+} cbcp_state;
+
+extern cbcp_state cbcp[];
+
+extern struct protent cbcp_protent;
+
+#define CBCP_MINLEN 4
+
+#define CBCP_REQ 1
+#define CBCP_RESP 2
+#define CBCP_ACK 3
+
+#define CB_CONF_NO 1
+#define CB_CONF_USER 2
+#define CB_CONF_ADMIN 3
+#define CB_CONF_LIST 4
+#endif
diff --git a/usr.sbin/pppd/ccp.c b/usr.sbin/pppd/ccp.c
new file mode 100644
index 0000000..000d916
--- /dev/null
+++ b/usr.sbin/pppd/ccp.c
@@ -0,0 +1,1047 @@
+/*
+ * ccp.c - PPP Compression Control Protocol.
+ *
+ * Copyright (c) 1994 The Australian National University.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation is hereby granted, provided that the above copyright
+ * notice appears in all copies. This software is provided without any
+ * warranty, express or implied. The Australian National University
+ * makes no representations about the suitability of this software for
+ * any purpose.
+ *
+ * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY
+ * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
+ * THE AUSTRALIAN NATIONAL UNIVERSITY HAVE BEEN ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * THE AUSTRALIAN NATIONAL UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE AUSTRALIAN NATIONAL UNIVERSITY HAS NO
+ * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS,
+ * OR MODIFICATIONS.
+ */
+
+#ifndef lint
+static char rcsid[] = "$Id$";
+#endif
+
+#include <string.h>
+#include <syslog.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <net/ppp_defs.h>
+#include <net/ppp_comp.h>
+
+#include "pppd.h"
+#include "fsm.h"
+#include "ccp.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_allowoptions[0].deflate = 1;
+ ccp_allowoptions[0].deflate_size = DEFLATE_MAX_SIZE;
+
+ 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) {
+ 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 = 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] = CI_DEFLATE;
+ 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 (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] != CI_DEFLATE || 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->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] == CI_DEFLATE && 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->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] == CI_DEFLATE && 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 = 0;
+ p += CILEN_DEFLATE;
+ len -= CILEN_DEFLATE;
+ }
+ 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:
+ if (!ao->deflate || clen != CILEN_DEFLATE) {
+ 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:
+ if (opt2 != NULL && opt2->deflate_size != opt->deflate_size)
+ sprintf(result, "Deflate (%d/%d)", opt->deflate_size,
+ opt2->deflate_size);
+ else
+ sprintf(result, "Deflate (%d)", 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:
+ if (optlen >= CILEN_DEFLATE) {
+ printer(arg, "deflate %d", 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..32f7b05
--- /dev/null
+++ b/usr.sbin/pppd/ccp.h
@@ -0,0 +1,46 @@
+/*
+ * ccp.h - Definitions for PPP Compression Control Protocol.
+ *
+ * Copyright (c) 1994 The Australian National University.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation is hereby granted, provided that the above copyright
+ * notice appears in all copies. This software is provided without any
+ * warranty, express or implied. The Australian National University
+ * makes no representations about the suitability of this software for
+ * any purpose.
+ *
+ * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY
+ * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
+ * THE AUSTRALIAN NATIONAL UNIVERSITY HAVE BEEN ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * THE AUSTRALIAN NATIONAL UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE AUSTRALIAN NATIONAL UNIVERSITY HAS NO
+ * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS,
+ * OR MODIFICATIONS.
+ *
+ * $Id$
+ */
+
+typedef struct ccp_options {
+ u_int bsd_compress: 1; /* do BSD Compress? */
+ u_int deflate: 1; /* do Deflate? */
+ u_int predictor_1: 1; /* do Predictor-1? */
+ u_int predictor_2: 1; /* do Predictor-2? */
+ u_short bsd_bits; /* # bits/code for BSD Compress */
+ u_short deflate_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..584b18b
--- /dev/null
+++ b/usr.sbin/pppd/chap.c
@@ -0,0 +1,867 @@
+/*
+ * chap.c - Challenge Handshake Authentication Protocol.
+ *
+ * Copyright (c) 1993 The Australian National University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the Australian National University. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Copyright (c) 1991 Gregory M. Christy.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Gregory M. Christy. The name of the author may not be used to
+ * endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef lint
+static char rcsid[] = "$Id$";
+#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);
+
+ } else {
+ syslog(LOG_ERR, "CHAP peer authentication failed");
+ cstate->serverstate = CHAPSS_BADAUTH;
+ auth_peer_fail(cstate->unit, PPP_CHAP);
+ }
+}
+
+/*
+ * ChapReceiveSuccess - Receive Success
+ */
+static void
+ChapReceiveSuccess(cstate, inp, id, len)
+ chap_state *cstate;
+ u_char *inp;
+ u_char id;
+ int len;
+{
+
+ CHAPDEBUG((LOG_INFO, "ChapReceiveSuccess: Rcvd id %d.", id));
+
+ if (cstate->clientstate == CHAPCS_OPEN)
+ /* presumably an answer to a duplicate response */
+ return;
+
+ if (cstate->clientstate != CHAPCS_RESPONSE) {
+ /* don't know what this is */
+ CHAPDEBUG((LOG_INFO, "ChapReceiveSuccess: in state %d\n",
+ cstate->clientstate));
+ return;
+ }
+
+ UNTIMEOUT(ChapResponseTimeout, cstate);
+
+ /*
+ * Print message.
+ */
+ if (len > 0)
+ PRINTMSG(inp, len);
+
+ cstate->clientstate = CHAPCS_OPEN;
+
+ auth_withpeer_success(cstate->unit, PPP_CHAP);
+}
+
+
+/*
+ * ChapReceiveFailure - Receive failure.
+ */
+static void
+ChapReceiveFailure(cstate, inp, id, len)
+ chap_state *cstate;
+ u_char *inp;
+ u_char id;
+ int len;
+{
+ CHAPDEBUG((LOG_INFO, "ChapReceiveFailure: Rcvd id %d.", id));
+
+ if (cstate->clientstate != CHAPCS_RESPONSE) {
+ /* don't know what this is */
+ CHAPDEBUG((LOG_INFO, "ChapReceiveFailure: in state %d\n",
+ cstate->clientstate));
+ return;
+ }
+
+ UNTIMEOUT(ChapResponseTimeout, cstate);
+
+ /*
+ * Print message.
+ */
+ if (len > 0)
+ PRINTMSG(inp, len);
+
+ syslog(LOG_ERR, "CHAP authentication failed");
+ auth_withpeer_fail(cstate->unit, PPP_CHAP);
+}
+
+
+/*
+ * ChapSendChallenge - Send an Authenticate challenge.
+ */
+static void
+ChapSendChallenge(cstate)
+ chap_state *cstate;
+{
+ u_char *outp;
+ int chal_len, name_len;
+ int outlen;
+
+ chal_len = cstate->chal_len;
+ name_len = strlen(cstate->chal_name);
+ outlen = CHAP_HEADERLEN + sizeof (u_char) + chal_len + name_len;
+ outp = outpacket_buf;
+
+ MAKEHEADER(outp, PPP_CHAP); /* paste in a CHAP header */
+
+ PUTCHAR(CHAP_CHALLENGE, outp);
+ PUTCHAR(cstate->chal_id, outp);
+ PUTSHORT(outlen, outp);
+
+ PUTCHAR(chal_len, outp); /* put length of challenge */
+ BCOPY(cstate->challenge, outp, chal_len);
+ INCPTR(chal_len, outp);
+
+ BCOPY(cstate->chal_name, outp, name_len); /* append hostname */
+
+ output(cstate->unit, outpacket_buf, outlen + PPP_HDRLEN);
+
+ CHAPDEBUG((LOG_INFO, "ChapSendChallenge: Sent id %d.", cstate->chal_id));
+
+ TIMEOUT(ChapChallengeTimeout, cstate, cstate->timeouttime);
+ ++cstate->chal_transmits;
+}
+
+
+/*
+ * ChapSendStatus - Send a status response (ack or nak).
+ */
+static void
+ChapSendStatus(cstate, code)
+ chap_state *cstate;
+ int code;
+{
+ u_char *outp;
+ int outlen, msglen;
+ char msg[256];
+
+ if (code == CHAP_SUCCESS)
+ sprintf(msg, "Welcome to %s.", hostname);
+ else
+ sprintf(msg, "I don't like you. Go 'way.");
+ msglen = strlen(msg);
+
+ outlen = CHAP_HEADERLEN + msglen;
+ outp = outpacket_buf;
+
+ MAKEHEADER(outp, PPP_CHAP); /* paste in a header */
+
+ PUTCHAR(code, outp);
+ PUTCHAR(cstate->chal_id, outp);
+ PUTSHORT(outlen, outp);
+ BCOPY(msg, outp, msglen);
+ output(cstate->unit, outpacket_buf, outlen + PPP_HDRLEN);
+
+ CHAPDEBUG((LOG_INFO, "ChapSendStatus: Sent code %d, id %d.", code,
+ cstate->chal_id));
+}
+
+/*
+ * ChapGenChallenge is used to generate a pseudo-random challenge string of
+ * a pseudo-random length between min_len and max_len. The challenge
+ * string and its length are stored in *cstate, and various other fields of
+ * *cstate are initialized.
+ */
+
+static void
+ChapGenChallenge(cstate)
+ chap_state *cstate;
+{
+ int chal_len;
+ u_char *ptr = cstate->challenge;
+ unsigned int i;
+
+ /* pick a random challenge length between MIN_CHALLENGE_LENGTH and
+ MAX_CHALLENGE_LENGTH */
+ chal_len = (unsigned) ((drand48() *
+ (MAX_CHALLENGE_LENGTH - MIN_CHALLENGE_LENGTH)) +
+ MIN_CHALLENGE_LENGTH);
+ cstate->chal_len = chal_len;
+ cstate->chal_id = ++cstate->id;
+ cstate->chal_transmits = 0;
+
+ /* generate a random string */
+ for (i = 0; i < chal_len; i++ )
+ *ptr++ = (char) (drand48() * 0xff);
+}
+
+/*
+ * ChapSendResponse - send a response packet with values as specified
+ * in *cstate.
+ */
+/* ARGSUSED */
+static void
+ChapSendResponse(cstate)
+ chap_state *cstate;
+{
+ u_char *outp;
+ int outlen, md_len, name_len;
+
+ md_len = cstate->resp_length;
+ name_len = strlen(cstate->resp_name);
+ outlen = CHAP_HEADERLEN + sizeof (u_char) + md_len + name_len;
+ outp = outpacket_buf;
+
+ MAKEHEADER(outp, PPP_CHAP);
+
+ PUTCHAR(CHAP_RESPONSE, outp); /* we are a response */
+ PUTCHAR(cstate->resp_id, outp); /* copy id from challenge packet */
+ PUTSHORT(outlen, outp); /* packet length */
+
+ PUTCHAR(md_len, outp); /* length of MD */
+ BCOPY(cstate->response, outp, md_len); /* copy MD to buffer */
+ INCPTR(md_len, outp);
+
+ BCOPY(cstate->resp_name, outp, name_len); /* append our name */
+
+ /* send the packet */
+ output(cstate->unit, outpacket_buf, outlen + PPP_HDRLEN);
+
+ cstate->clientstate = CHAPCS_RESPONSE;
+ TIMEOUT(ChapResponseTimeout, cstate, cstate->timeouttime);
+ ++cstate->resp_transmits;
+}
+
+/*
+ * ChapPrintPkt - print the contents of a CHAP packet.
+ */
+static char *ChapCodenames[] = {
+ "Challenge", "Response", "Success", "Failure"
+};
+
+static int
+ChapPrintPkt(p, plen, printer, arg)
+ u_char *p;
+ int plen;
+ void (*printer) __P((void *, char *, ...));
+ void *arg;
+{
+ int code, id, len;
+ int clen, nlen;
+ u_char x;
+
+ if (plen < CHAP_HEADERLEN)
+ return 0;
+ GETCHAR(code, p);
+ GETCHAR(id, p);
+ GETSHORT(len, p);
+ if (len < CHAP_HEADERLEN || len > plen)
+ return 0;
+
+ if (code >= 1 && code <= sizeof(ChapCodenames) / sizeof(char *))
+ printer(arg, " %s", ChapCodenames[code-1]);
+ else
+ printer(arg, " code=0x%x", code);
+ printer(arg, " id=0x%x", id);
+ len -= CHAP_HEADERLEN;
+ switch (code) {
+ case CHAP_CHALLENGE:
+ case CHAP_RESPONSE:
+ if (len < 1)
+ break;
+ clen = p[0];
+ if (len < clen + 1)
+ break;
+ ++p;
+ nlen = len - clen - 1;
+ printer(arg, " <");
+ for (; clen > 0; --clen) {
+ GETCHAR(x, p);
+ printer(arg, "%.2x", x);
+ }
+ printer(arg, ">, name = ");
+ print_string((char *)p, nlen, printer, arg);
+ break;
+ case CHAP_FAILURE:
+ case CHAP_SUCCESS:
+ printer(arg, " ");
+ print_string((char *)p, len, printer, arg);
+ break;
+ default:
+ for (clen = len; clen > 0; --clen) {
+ GETCHAR(x, p);
+ printer(arg, " %.2x", x);
+ }
+ }
+
+ return len + CHAP_HEADERLEN;
+}
diff --git a/usr.sbin/pppd/chap.h b/usr.sbin/pppd/chap.h
new file mode 100644
index 0000000..6e2cc45
--- /dev/null
+++ b/usr.sbin/pppd/chap.h
@@ -0,0 +1,124 @@
+/*
+ * chap.h - Challenge Handshake Authentication Protocol definitions.
+ *
+ * Copyright (c) 1993 The Australian National University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the Australian National University. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Copyright (c) 1991 Gregory M. Christy
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the author.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id$
+ */
+
+#ifndef __CHAP_INCLUDE__
+
+/* Code + ID + length */
+#define CHAP_HEADERLEN 4
+
+/*
+ * CHAP codes.
+ */
+
+#define CHAP_DIGEST_MD5 5 /* use MD5 algorithm */
+#define MD5_SIGNATURE_SIZE 16 /* 16 bytes in a MD5 message digest */
+#define CHAP_MICROSOFT 0x80 /* use Microsoft-compatible alg. */
+#define MS_CHAP_RESPONSE_LEN 49 /* Response length for MS-CHAP */
+
+#define CHAP_CHALLENGE 1
+#define CHAP_RESPONSE 2
+#define CHAP_SUCCESS 3
+#define CHAP_FAILURE 4
+
+/*
+ * Challenge lengths (for challenges we send) and other limits.
+ */
+#define MIN_CHALLENGE_LENGTH 32
+#define MAX_CHALLENGE_LENGTH 64
+#define MAX_RESPONSE_LENGTH 64 /* sufficient for MD5 or MS-CHAP */
+
+/*
+ * Each interface is described by a chap structure.
+ */
+
+typedef struct chap_state {
+ int unit; /* Interface unit number */
+ int clientstate; /* Client state */
+ int serverstate; /* Server state */
+ u_char challenge[MAX_CHALLENGE_LENGTH]; /* last challenge string sent */
+ u_char chal_len; /* challenge length */
+ u_char chal_id; /* ID of last challenge */
+ u_char chal_type; /* hash algorithm for challenges */
+ u_char id; /* Current id */
+ char *chal_name; /* Our name to use with challenge */
+ int chal_interval; /* Time until we challenge peer again */
+ int timeouttime; /* Timeout time in seconds */
+ int max_transmits; /* Maximum # of challenge transmissions */
+ int chal_transmits; /* Number of transmissions of challenge */
+ int resp_transmits; /* Number of transmissions of response */
+ u_char response[MAX_RESPONSE_LENGTH]; /* Response to send */
+ u_char resp_length; /* length of response */
+ u_char resp_id; /* ID for response messages */
+ u_char resp_type; /* hash algorithm for responses */
+ char *resp_name; /* Our name to send with response */
+} chap_state;
+
+
+/*
+ * Client (peer) states.
+ */
+#define CHAPCS_INITIAL 0 /* Lower layer down, not opened */
+#define CHAPCS_CLOSED 1 /* Lower layer up, not opened */
+#define CHAPCS_PENDING 2 /* Auth us to peer when lower up */
+#define CHAPCS_LISTEN 3 /* Listening for a challenge */
+#define CHAPCS_RESPONSE 4 /* Sent response, waiting for status */
+#define CHAPCS_OPEN 5 /* We've received Success */
+
+/*
+ * Server (authenticator) states.
+ */
+#define CHAPSS_INITIAL 0 /* Lower layer down, not opened */
+#define CHAPSS_CLOSED 1 /* Lower layer up, not opened */
+#define CHAPSS_PENDING 2 /* Auth peer when lower up */
+#define CHAPSS_INITIAL_CHAL 3 /* We've sent the first challenge */
+#define CHAPSS_OPEN 4 /* We've sent a Success msg */
+#define CHAPSS_RECHALLENGE 5 /* We've sent another challenge */
+#define CHAPSS_BADAUTH 6 /* We've sent a Failure msg */
+
+/*
+ * Timeouts.
+ */
+#define CHAP_DEFTIMEOUT 3 /* Timeout time in seconds */
+#define CHAP_DEFTRANSMITS 10 /* max # times to send challenge */
+
+extern chap_state chap[];
+
+void ChapAuthWithPeer __P((int, char *, int));
+void ChapAuthPeer __P((int, char *, int));
+
+extern struct protent chap_protent;
+
+#define __CHAP_INCLUDE__
+#endif /* __CHAP_INCLUDE__ */
diff --git a/usr.sbin/pppd/chap_ms.c b/usr.sbin/pppd/chap_ms.c
new file mode 100644
index 0000000..a19c87d
--- /dev/null
+++ b/usr.sbin/pppd/chap_ms.c
@@ -0,0 +1,327 @@
+/*
+ * chap_ms.c - Microsoft MS-CHAP compatible implementation.
+ *
+ * Copyright (c) 1995 Eric Rosenquist, Strata Software Limited.
+ * http://www.strataware.com/
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Eric Rosenquist. The name of the author may not be used to
+ * endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/*
+ * Modifications by Lauri Pesonen / lpesonen@clinet.fi, april 1997
+ *
+ * Implemented LANManager type password response to MS-CHAP challenges.
+ * Now pppd provides both NT style and LANMan style blocks, and the
+ * prefered is set by option "ms-lanman". Default is to use NT.
+ * The hash text (StdText) was taken from Win95 RASAPI32.DLL.
+ *
+ * You should also use DOMAIN\\USERNAME as described in README.MSCHAP80
+ */
+
+#ifndef lint
+static char rcsid[] = "$Id$";
+#endif
+
+#ifdef CHAPMS
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <syslog.h>
+
+#include "pppd.h"
+#include "chap.h"
+#include "chap_ms.h"
+#include "md4.h"
+
+#ifndef USE_CRYPT
+#include <des.h>
+#endif
+
+typedef struct {
+ u_char LANManResp[24];
+ u_char NTResp[24];
+ u_char UseNT; /* If 1, ignore the LANMan response field */
+} MS_ChapResponse;
+/* We use MS_CHAP_RESPONSE_LEN, rather than sizeof(MS_ChapResponse),
+ in case this struct gets padded. */
+
+
+static void DesEncrypt __P((u_char *, u_char *, u_char *));
+static void MakeKey __P((u_char *, u_char *));
+
+#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, 16);
+
+#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;
+ MDstruct md4Context;
+ u_char unicodePassword[MAX_NT_PASSWORD * 2];
+ static int low_byte_first = -1;
+
+ /* 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];
+
+ MDbegin(&md4Context);
+ MDupdate(&md4Context, unicodePassword, secret_len * 2 * 8); /* Unicode is 2 bytes/char, *8 for bit count */
+
+ if (low_byte_first == -1)
+ low_byte_first = (htons((unsigned short int)1) != 1);
+ if (low_byte_first == 0)
+ MDreverse(&md4Context); /* sfb 961105 */
+
+ MDupdate(&md4Context, NULL, 0); /* Tell MD4 we're done */
+
+ ChallengeResponse(rchallenge, (char *)md4Context.buffer, response->NTResp);
+}
+
+#ifdef MSLANMAN
+static u_char *StdText = (u_char *)"KGS!@#$%"; /* key from rasapi32.dll */
+
+static 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[16];
+
+ /* 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..f22607d
--- /dev/null
+++ b/usr.sbin/pppd/chap_ms.h
@@ -0,0 +1,32 @@
+/*
+ * chap.h - Challenge Handshake Authentication Protocol definitions.
+ *
+ * Copyright (c) 1995 Eric Rosenquist, Strata Software Limited.
+ * http://www.strataware.com/
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Eric Rosenquist. The name of the author may not be used to
+ * endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id$
+ */
+
+#ifndef __CHAPMS_INCLUDE__
+
+#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..f44510f
--- /dev/null
+++ b/usr.sbin/pppd/demand.c
@@ -0,0 +1,348 @@
+/*
+ * demand.c - Support routines for demand-dialling.
+ *
+ * Copyright (c) 1993 The Australian National University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the Australian National University. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef lint
+static char rcsid[] = "$Id$";
+#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>
+#include <net/if.h>
+#ifdef PPP_FILTER
+#include <net/bpf.h>
+#include <pcap.h>
+#endif
+
+#include "pppd.h"
+#include "fsm.h"
+#include "ipcp.h"
+#include "lcp.h"
+
+char *frame;
+int framelen;
+int framemax;
+int escape_flag;
+int flush_flag;
+int fcs;
+
+struct packet {
+ int length;
+ struct packet *next;
+ unsigned char data[1];
+};
+
+struct packet *pend_q;
+struct packet *pend_qtail;
+
+static int active_packet __P((unsigned char *, int));
+
+/*
+ * demand_conf - configure the interface for doing dial-on-demand.
+ */
+void
+demand_conf()
+{
+ int i;
+ struct protent *protp;
+
+/* framemax = lcp_allowoptions[0].mru;
+ if (framemax < PPP_MRU) */
+ framemax = PPP_MRU;
+ framemax += PPP_HDRLEN + PPP_FCSLEN;
+ frame = malloc(framemax);
+ if (frame == NULL)
+ novm("demand frame");
+ framelen = 0;
+ pend_q = NULL;
+ escape_flag = 0;
+ flush_flag = 0;
+ fcs = PPP_INITFCS;
+
+ ppp_send_config(0, PPP_MRU, (u_int32_t) 0, 0, 0);
+ ppp_recv_config(0, PPP_MRU, (u_int32_t) 0, 0, 0);
+
+#ifdef PPP_FILTER
+ set_filters(&pass_filter, &active_filter);
+#endif
+
+ /*
+ * Call the demand_conf procedure for each protocol that's got one.
+ */
+ for (i = 0; (protp = protocols[i]) != NULL; ++i)
+ if (protp->enabled_flag && protp->demand_conf != NULL)
+ if (!((*protp->demand_conf)(0)))
+ die(1);
+}
+
+
+/*
+ * demand_block - set each network protocol to block further packets.
+ */
+void
+demand_block()
+{
+ int i;
+ struct protent *protp;
+
+ for (i = 0; (protp = protocols[i]) != NULL; ++i)
+ if (protp->enabled_flag && protp->demand_conf != NULL)
+ sifnpmode(0, protp->protocol & ~0x8000, NPMODE_QUEUE);
+ get_loop_output();
+}
+
+/*
+ * demand_discard - set each network protocol to discard packets
+ * with an error.
+ */
+void
+demand_discard()
+{
+ struct packet *pkt, *nextpkt;
+ int i;
+ struct protent *protp;
+
+ for (i = 0; (protp = protocols[i]) != NULL; ++i)
+ if (protp->enabled_flag && protp->demand_conf != NULL)
+ sifnpmode(0, protp->protocol & ~0x8000, NPMODE_ERROR);
+ get_loop_output();
+
+ /* discard all saved packets */
+ for (pkt = pend_q; pkt != NULL; pkt = nextpkt) {
+ nextpkt = pkt->next;
+ free(pkt);
+ }
+ pend_q = NULL;
+ framelen = 0;
+ flush_flag = 0;
+ escape_flag = 0;
+ fcs = PPP_INITFCS;
+}
+
+/*
+ * demand_unblock - set each enabled network protocol to pass packets.
+ */
+void
+demand_unblock()
+{
+ int i;
+ struct protent *protp;
+
+ for (i = 0; (protp = protocols[i]) != NULL; ++i)
+ if (protp->enabled_flag && protp->demand_conf != NULL)
+ sifnpmode(0, protp->protocol & ~0x8000, NPMODE_PASS);
+}
+
+/*
+ * FCS lookup table as calculated by genfcstab.
+ */
+static u_short fcstab[256] = {
+ 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
+ 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
+ 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
+ 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
+ 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
+ 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
+ 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
+ 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
+ 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
+ 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
+ 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
+ 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
+ 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
+ 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
+ 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
+ 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
+ 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
+ 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
+ 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
+ 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
+ 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
+ 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
+ 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
+ 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
+ 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
+ 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
+ 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
+ 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
+ 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
+ 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
+ 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
+ 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
+};
+
+/*
+ * loop_chars - process characters received from the loopback.
+ * Calls loop_frame when a complete frame has been accumulated.
+ * Return value is 1 if we need to bring up the link, 0 otherwise.
+ */
+int
+loop_chars(p, n)
+ unsigned char *p;
+ int n;
+{
+ int c, rv;
+
+ rv = 0;
+ for (; n > 0; --n) {
+ c = *p++;
+ if (c == PPP_FLAG) {
+ if (!escape_flag && !flush_flag
+ && framelen > 2 && fcs == PPP_GOODFCS) {
+ framelen -= 2;
+ if (loop_frame(frame, framelen))
+ rv = 1;
+ }
+ framelen = 0;
+ flush_flag = 0;
+ escape_flag = 0;
+ fcs = PPP_INITFCS;
+ continue;
+ }
+ if (flush_flag)
+ continue;
+ if (escape_flag) {
+ c ^= PPP_TRANS;
+ escape_flag = 0;
+ } else if (c == PPP_ESCAPE) {
+ escape_flag = 1;
+ continue;
+ }
+ if (framelen >= framemax) {
+ flush_flag = 1;
+ continue;
+ }
+ frame[framelen++] = c;
+ fcs = PPP_FCS(fcs, c);
+ }
+ return rv;
+}
+
+/*
+ * loop_frame - given a frame obtained from the loopback,
+ * decide whether to bring up the link or not, and, if we want
+ * to transmit this frame later, put it on the pending queue.
+ * Return value is 1 if we need to bring up the link, 0 otherwise.
+ * We assume that the kernel driver has already applied the
+ * pass_filter, so we won't get packets it rejected.
+ * We apply the active_filter to see if we want this packet to
+ * bring up the link.
+ */
+int
+loop_frame(frame, len)
+ unsigned char *frame;
+ int len;
+{
+ struct packet *pkt;
+
+ /* log_packet(frame, len, "from loop: ", LOG_DEBUG); */
+ if (len < PPP_HDRLEN)
+ return 0;
+ if ((PPP_PROTOCOL(frame) & 0x8000) != 0)
+ return 0; /* shouldn't get any of these anyway */
+ if (!active_packet(frame, len))
+ return 0;
+
+ pkt = (struct packet *) malloc(sizeof(struct packet) + len);
+ if (pkt != NULL) {
+ pkt->length = len;
+ pkt->next = NULL;
+ memcpy(pkt->data, frame, len);
+ if (pend_q == NULL)
+ pend_q = pkt;
+ else
+ pend_qtail->next = pkt;
+ pend_qtail = pkt;
+ }
+ return 1;
+}
+
+/*
+ * demand_rexmit - Resend all those frames which we got via the
+ * loopback, now that the real serial link is up.
+ */
+void
+demand_rexmit(proto)
+ int proto;
+{
+ struct packet *pkt, *prev, *nextpkt;
+
+ prev = NULL;
+ pkt = pend_q;
+ pend_q = NULL;
+ for (; pkt != NULL; pkt = nextpkt) {
+ nextpkt = pkt->next;
+ if (PPP_PROTOCOL(pkt->data) == proto) {
+ output(0, pkt->data, pkt->length);
+ free(pkt);
+ } else {
+ if (prev == NULL)
+ pend_q = pkt;
+ else
+ prev->next = pkt;
+ prev = pkt;
+ }
+ }
+ pend_qtail = prev;
+ if (prev != NULL)
+ prev->next = NULL;
+}
+
+/*
+ * Scan a packet to decide whether it is an "active" packet,
+ * that is, whether it is worth bringing up the link for.
+ */
+static int
+active_packet(p, len)
+ unsigned char *p;
+ int len;
+{
+ int proto, i;
+ struct protent *protp;
+
+ if (len < PPP_HDRLEN)
+ return 0;
+ proto = PPP_PROTOCOL(p);
+#ifdef PPP_FILTER
+ if (active_filter.bf_len != 0
+ && bpf_filter(active_filter.bf_insns, frame, len, len) == 0)
+ return 0;
+#endif
+ for (i = 0; (protp = protocols[i]) != NULL; ++i) {
+ if (protp->protocol < 0xC000 && (protp->protocol & ~0x8000) == proto) {
+ if (!protp->enabled_flag)
+ return 0;
+ if (protp->active_pkt == NULL)
+ return 1;
+ return (*protp->active_pkt)(p, len);
+ }
+ }
+ return 0; /* not a supported protocol !!?? */
+}
diff --git a/usr.sbin/pppd/fsm.c b/usr.sbin/pppd/fsm.c
new file mode 100644
index 0000000..624799f
--- /dev/null
+++ b/usr.sbin/pppd/fsm.c
@@ -0,0 +1,798 @@
+/*
+ * fsm.c - {Link, IP} Control Protocol Finite State Machine.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef lint
+static char rcsid[] = "$Id$";
+#endif
+
+/*
+ * TODO:
+ * Randomize fsm id on link/init.
+ * Deal with variable outgoing MTU.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <syslog.h>
+
+#include "pppd.h"
+#include "fsm.h"
+
+static void fsm_timeout __P((void *));
+static void fsm_rconfreq __P((fsm *, int, u_char *, int));
+static void fsm_rconfack __P((fsm *, int, u_char *, int));
+static void fsm_rconfnakrej __P((fsm *, int, int, u_char *, int));
+static void fsm_rtermreq __P((fsm *, int, u_char *, int));
+static void fsm_rtermack __P((fsm *));
+static void fsm_rcoderej __P((fsm *, u_char *, int));
+static void fsm_sconfreq __P((fsm *, int));
+
+#define PROTO_NAME(f) ((f)->callbacks->proto_name)
+
+int peer_mru[NUM_PPP];
+
+
+/*
+ * fsm_init - Initialize fsm.
+ *
+ * Initialize fsm state.
+ */
+void
+fsm_init(f)
+ fsm *f;
+{
+ f->state = INITIAL;
+ f->flags = 0;
+ f->id = 0; /* XXX Start with random id? */
+ f->timeouttime = DEFTIMEOUT;
+ f->maxconfreqtransmits = DEFMAXCONFREQS;
+ f->maxtermtransmits = DEFMAXTERMREQS;
+ f->maxnakloops = DEFMAXNAKLOOPS;
+ f->term_reason_len = 0;
+}
+
+
+/*
+ * fsm_lowerup - The lower layer is up.
+ */
+void
+fsm_lowerup(f)
+ fsm *f;
+{
+ switch( f->state ){
+ case INITIAL:
+ f->state = CLOSED;
+ break;
+
+ case STARTING:
+ if( f->flags & OPT_SILENT )
+ f->state = STOPPED;
+ else {
+ /* Send an initial configure-request */
+ fsm_sconfreq(f, 0);
+ f->state = REQSENT;
+ }
+ break;
+
+ default:
+ FSMDEBUG((LOG_INFO, "%s: Up event in state %d!",
+ PROTO_NAME(f), f->state));
+ }
+}
+
+
+/*
+ * fsm_lowerdown - The lower layer is down.
+ *
+ * Cancel all timeouts and inform upper layers.
+ */
+void
+fsm_lowerdown(f)
+ fsm *f;
+{
+ switch( f->state ){
+ case CLOSED:
+ f->state = INITIAL;
+ break;
+
+ case STOPPED:
+ f->state = STARTING;
+ if( f->callbacks->starting )
+ (*f->callbacks->starting)(f);
+ break;
+
+ case CLOSING:
+ f->state = INITIAL;
+ UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
+ break;
+
+ case STOPPING:
+ case REQSENT:
+ case ACKRCVD:
+ case ACKSENT:
+ f->state = STARTING;
+ UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
+ break;
+
+ case OPENED:
+ if( f->callbacks->down )
+ (*f->callbacks->down)(f);
+ f->state = STARTING;
+ break;
+
+ default:
+ FSMDEBUG((LOG_INFO, "%s: Down event in state %d!",
+ PROTO_NAME(f), f->state));
+ }
+}
+
+
+/*
+ * fsm_open - Link is allowed to come up.
+ */
+void
+fsm_open(f)
+ fsm *f;
+{
+ switch( f->state ){
+ case INITIAL:
+ f->state = STARTING;
+ if( f->callbacks->starting )
+ (*f->callbacks->starting)(f);
+ break;
+
+ case CLOSED:
+ if( f->flags & OPT_SILENT )
+ f->state = STOPPED;
+ else {
+ /* Send an initial configure-request */
+ fsm_sconfreq(f, 0);
+ f->state = REQSENT;
+ }
+ break;
+
+ case CLOSING:
+ f->state = STOPPING;
+ /* fall through */
+ case STOPPED:
+ case OPENED:
+ if( f->flags & OPT_RESTART ){
+ fsm_lowerdown(f);
+ fsm_lowerup(f);
+ }
+ break;
+ }
+}
+
+
+/*
+ * fsm_close - Start closing connection.
+ *
+ * Cancel timeouts and either initiate close or possibly go directly to
+ * the CLOSED state.
+ */
+void
+fsm_close(f, reason)
+ fsm *f;
+ char *reason;
+{
+ f->term_reason = reason;
+ f->term_reason_len = (reason == NULL? 0: strlen(reason));
+ switch( f->state ){
+ case STARTING:
+ f->state = INITIAL;
+ break;
+ case STOPPED:
+ f->state = CLOSED;
+ break;
+ case STOPPING:
+ f->state = CLOSING;
+ break;
+
+ case REQSENT:
+ case ACKRCVD:
+ case ACKSENT:
+ case OPENED:
+ if( f->state != OPENED )
+ UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
+ else if( f->callbacks->down )
+ (*f->callbacks->down)(f); /* Inform upper layers we're down */
+
+ /* Init restart counter, send Terminate-Request */
+ f->retransmits = f->maxtermtransmits;
+ fsm_sdata(f, TERMREQ, f->reqid = ++f->id,
+ (u_char *) f->term_reason, f->term_reason_len);
+ TIMEOUT(fsm_timeout, f, f->timeouttime);
+ --f->retransmits;
+
+ f->state = CLOSING;
+ break;
+ }
+}
+
+
+/*
+ * fsm_timeout - Timeout expired.
+ */
+static void
+fsm_timeout(arg)
+ void *arg;
+{
+ fsm *f = (fsm *) arg;
+
+ switch (f->state) {
+ case CLOSING:
+ case STOPPING:
+ if( f->retransmits <= 0 ){
+ /*
+ * We've waited for an ack long enough. Peer probably heard us.
+ */
+ f->state = (f->state == CLOSING)? CLOSED: STOPPED;
+ if( f->callbacks->finished )
+ (*f->callbacks->finished)(f);
+ } else {
+ /* Send Terminate-Request */
+ fsm_sdata(f, TERMREQ, f->reqid = ++f->id,
+ (u_char *) f->term_reason, f->term_reason_len);
+ TIMEOUT(fsm_timeout, f, f->timeouttime);
+ --f->retransmits;
+ }
+ break;
+
+ case REQSENT:
+ case ACKRCVD:
+ case ACKSENT:
+ if (f->retransmits <= 0) {
+ syslog(LOG_WARNING, "%s: timeout sending Config-Requests",
+ PROTO_NAME(f));
+ f->state = STOPPED;
+ if( (f->flags & OPT_PASSIVE) == 0 && f->callbacks->finished )
+ (*f->callbacks->finished)(f);
+
+ } else {
+ /* Retransmit the configure-request */
+ if (f->callbacks->retransmit)
+ (*f->callbacks->retransmit)(f);
+ fsm_sconfreq(f, 1); /* Re-send Configure-Request */
+ if( f->state == ACKRCVD )
+ f->state = REQSENT;
+ }
+ break;
+
+ default:
+ FSMDEBUG((LOG_INFO, "%s: Timeout event in state %d!",
+ PROTO_NAME(f), f->state));
+ }
+}
+
+
+/*
+ * fsm_input - Input packet.
+ */
+void
+fsm_input(f, inpacket, l)
+ fsm *f;
+ u_char *inpacket;
+ int l;
+{
+ u_char *inp;
+ u_char code, id;
+ int len;
+
+ /*
+ * Parse header (code, id and length).
+ * If packet too short, drop it.
+ */
+ inp = inpacket;
+ if (l < HEADERLEN) {
+ FSMDEBUG((LOG_WARNING, "fsm_input(%x): Rcvd short header.",
+ f->protocol));
+ return;
+ }
+ GETCHAR(code, inp);
+ GETCHAR(id, inp);
+ GETSHORT(len, inp);
+ if (len < HEADERLEN) {
+ FSMDEBUG((LOG_INFO, "fsm_input(%x): Rcvd illegal length.",
+ f->protocol));
+ return;
+ }
+ if (len > l) {
+ FSMDEBUG((LOG_INFO, "fsm_input(%x): Rcvd short packet.",
+ f->protocol));
+ return;
+ }
+ len -= HEADERLEN; /* subtract header length */
+
+ if( f->state == INITIAL || f->state == STARTING ){
+ FSMDEBUG((LOG_INFO, "fsm_input(%x): Rcvd packet in state %d.",
+ f->protocol, f->state));
+ return;
+ }
+
+ /*
+ * Action depends on code.
+ */
+ switch (code) {
+ case CONFREQ:
+ fsm_rconfreq(f, id, inp, len);
+ break;
+
+ case CONFACK:
+ fsm_rconfack(f, id, inp, len);
+ break;
+
+ case CONFNAK:
+ case CONFREJ:
+ fsm_rconfnakrej(f, code, id, inp, len);
+ break;
+
+ case TERMREQ:
+ fsm_rtermreq(f, id, inp, len);
+ break;
+
+ case TERMACK:
+ fsm_rtermack(f);
+ break;
+
+ case CODEREJ:
+ fsm_rcoderej(f, inp, len);
+ break;
+
+ default:
+ if( !f->callbacks->extcode
+ || !(*f->callbacks->extcode)(f, code, id, inp, len) )
+ fsm_sdata(f, CODEREJ, ++f->id, inpacket, len + HEADERLEN);
+ break;
+ }
+}
+
+
+/*
+ * fsm_rconfreq - Receive Configure-Request.
+ */
+static void
+fsm_rconfreq(f, id, inp, len)
+ fsm *f;
+ u_char id;
+ u_char *inp;
+ int len;
+{
+ int code, reject_if_disagree;
+
+ FSMDEBUG((LOG_INFO, "fsm_rconfreq(%s): Rcvd id %d.", PROTO_NAME(f), id));
+ switch( f->state ){
+ case CLOSED:
+ /* Go away, we're closed */
+ fsm_sdata(f, TERMACK, id, NULL, 0);
+ return;
+ case CLOSING:
+ case STOPPING:
+ return;
+
+ case OPENED:
+ /* Go down and restart negotiation */
+ if( f->callbacks->down )
+ (*f->callbacks->down)(f); /* Inform upper layers */
+ fsm_sconfreq(f, 0); /* Send initial Configure-Request */
+ break;
+
+ case STOPPED:
+ /* Negotiation started by our peer */
+ fsm_sconfreq(f, 0); /* Send initial Configure-Request */
+ f->state = REQSENT;
+ break;
+ }
+
+ /*
+ * Pass the requested configuration options
+ * to protocol-specific code for checking.
+ */
+ if (f->callbacks->reqci){ /* Check CI */
+ reject_if_disagree = (f->nakloops >= f->maxnakloops);
+ code = (*f->callbacks->reqci)(f, inp, &len, reject_if_disagree);
+ } else if (len)
+ code = CONFREJ; /* Reject all CI */
+ else
+ code = CONFACK;
+
+ /* send the Ack, Nak or Rej to the peer */
+ fsm_sdata(f, code, id, inp, len);
+
+ if (code == CONFACK) {
+ if (f->state == ACKRCVD) {
+ UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
+ f->state = OPENED;
+ if (f->callbacks->up)
+ (*f->callbacks->up)(f); /* Inform upper layers */
+ } else
+ f->state = ACKSENT;
+ f->nakloops = 0;
+
+ } else {
+ /* we sent CONFACK or CONFREJ */
+ if (f->state != ACKRCVD)
+ f->state = REQSENT;
+ if( code == CONFNAK )
+ ++f->nakloops;
+ }
+}
+
+
+/*
+ * fsm_rconfack - Receive Configure-Ack.
+ */
+static void
+fsm_rconfack(f, id, inp, len)
+ fsm *f;
+ int id;
+ u_char *inp;
+ int len;
+{
+ FSMDEBUG((LOG_INFO, "fsm_rconfack(%s): Rcvd id %d.",
+ PROTO_NAME(f), id));
+
+ if (id != f->reqid || f->seen_ack) /* Expected id? */
+ return; /* Nope, toss... */
+ if( !(f->callbacks->ackci? (*f->callbacks->ackci)(f, inp, len):
+ (len == 0)) ){
+ /* Ack is bad - ignore it */
+ log_packet(inp, len, "Received bad configure-ack: ", LOG_ERR);
+ FSMDEBUG((LOG_INFO, "%s: received bad Ack (length %d)",
+ PROTO_NAME(f), len));
+ return;
+ }
+ f->seen_ack = 1;
+
+ switch (f->state) {
+ case CLOSED:
+ case STOPPED:
+ fsm_sdata(f, TERMACK, id, NULL, 0);
+ break;
+
+ case REQSENT:
+ f->state = ACKRCVD;
+ f->retransmits = f->maxconfreqtransmits;
+ break;
+
+ case ACKRCVD:
+ /* Huh? an extra valid Ack? oh well... */
+ UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
+ fsm_sconfreq(f, 0);
+ f->state = REQSENT;
+ break;
+
+ case ACKSENT:
+ UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
+ f->state = OPENED;
+ f->retransmits = f->maxconfreqtransmits;
+ if (f->callbacks->up)
+ (*f->callbacks->up)(f); /* Inform upper layers */
+ break;
+
+ case OPENED:
+ /* Go down and restart negotiation */
+ if (f->callbacks->down)
+ (*f->callbacks->down)(f); /* Inform upper layers */
+ fsm_sconfreq(f, 0); /* Send initial Configure-Request */
+ f->state = REQSENT;
+ break;
+ }
+}
+
+
+/*
+ * fsm_rconfnakrej - Receive Configure-Nak or Configure-Reject.
+ */
+static void
+fsm_rconfnakrej(f, code, id, inp, len)
+ fsm *f;
+ int code, id;
+ u_char *inp;
+ int len;
+{
+ int (*proc) __P((fsm *, u_char *, int));
+ int ret;
+
+ FSMDEBUG((LOG_INFO, "fsm_rconfnakrej(%s): Rcvd id %d.",
+ PROTO_NAME(f), id));
+
+ if (id != f->reqid || f->seen_ack) /* Expected id? */
+ return; /* Nope, toss... */
+ proc = (code == CONFNAK)? f->callbacks->nakci: f->callbacks->rejci;
+ if (!proc || !(ret = proc(f, inp, len))) {
+ /* Nak/reject is bad - ignore it */
+ log_packet(inp, len, "Received bad configure-nak/rej: ", LOG_ERR);
+ FSMDEBUG((LOG_INFO, "%s: received bad %s (length %d)",
+ PROTO_NAME(f), (code==CONFNAK? "Nak": "reject"), len));
+ return;
+ }
+ f->seen_ack = 1;
+
+ switch (f->state) {
+ case CLOSED:
+ case STOPPED:
+ fsm_sdata(f, TERMACK, id, NULL, 0);
+ break;
+
+ case REQSENT:
+ case ACKSENT:
+ /* They didn't agree to what we wanted - try another request */
+ UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
+ if (ret < 0)
+ f->state = STOPPED; /* kludge for stopping CCP */
+ else
+ fsm_sconfreq(f, 0); /* Send Configure-Request */
+ break;
+
+ case ACKRCVD:
+ /* Got a Nak/reject when we had already had an Ack?? oh well... */
+ UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
+ fsm_sconfreq(f, 0);
+ f->state = REQSENT;
+ break;
+
+ case OPENED:
+ /* Go down and restart negotiation */
+ if (f->callbacks->down)
+ (*f->callbacks->down)(f); /* Inform upper layers */
+ fsm_sconfreq(f, 0); /* Send initial Configure-Request */
+ f->state = REQSENT;
+ break;
+ }
+}
+
+
+/*
+ * fsm_rtermreq - Receive Terminate-Req.
+ */
+static void
+fsm_rtermreq(f, id, p, len)
+ fsm *f;
+ int id;
+ u_char *p;
+ int len;
+{
+ char str[80];
+
+ FSMDEBUG((LOG_INFO, "fsm_rtermreq(%s): Rcvd id %d.",
+ PROTO_NAME(f), id));
+
+ switch (f->state) {
+ case ACKRCVD:
+ case ACKSENT:
+ f->state = REQSENT; /* Start over but keep trying */
+ break;
+
+ case OPENED:
+ if (len > 0) {
+ fmtmsg(str, sizeof(str), "%0.*v", len, p);
+ syslog(LOG_INFO, "%s terminated by peer (%s)", PROTO_NAME(f), str);
+ } else
+ syslog(LOG_INFO, "%s terminated by peer", PROTO_NAME(f));
+ if (f->callbacks->down)
+ (*f->callbacks->down)(f); /* Inform upper layers */
+ f->retransmits = 0;
+ f->state = STOPPING;
+ TIMEOUT(fsm_timeout, f, f->timeouttime);
+ break;
+ }
+
+ fsm_sdata(f, TERMACK, id, NULL, 0);
+}
+
+
+/*
+ * fsm_rtermack - Receive Terminate-Ack.
+ */
+static void
+fsm_rtermack(f)
+ fsm *f;
+{
+ FSMDEBUG((LOG_INFO, "fsm_rtermack(%s).", PROTO_NAME(f)));
+
+ switch (f->state) {
+ case CLOSING:
+ UNTIMEOUT(fsm_timeout, f);
+ f->state = CLOSED;
+ if( f->callbacks->finished )
+ (*f->callbacks->finished)(f);
+ break;
+ case STOPPING:
+ UNTIMEOUT(fsm_timeout, f);
+ f->state = STOPPED;
+ if( f->callbacks->finished )
+ (*f->callbacks->finished)(f);
+ break;
+
+ case ACKRCVD:
+ f->state = REQSENT;
+ break;
+
+ case OPENED:
+ if (f->callbacks->down)
+ (*f->callbacks->down)(f); /* Inform upper layers */
+ fsm_sconfreq(f, 0);
+ break;
+ }
+}
+
+
+/*
+ * fsm_rcoderej - Receive an Code-Reject.
+ */
+static void
+fsm_rcoderej(f, inp, len)
+ fsm *f;
+ u_char *inp;
+ int len;
+{
+ u_char code, id;
+
+ FSMDEBUG((LOG_INFO, "fsm_rcoderej(%s).", PROTO_NAME(f)));
+
+ if (len < HEADERLEN) {
+ FSMDEBUG((LOG_INFO, "fsm_rcoderej: Rcvd short Code-Reject packet!"));
+ return;
+ }
+ GETCHAR(code, inp);
+ GETCHAR(id, inp);
+ syslog(LOG_WARNING, "%s: Rcvd Code-Reject for code %d, id %d",
+ PROTO_NAME(f), code, id);
+
+ if( f->state == ACKRCVD )
+ f->state = REQSENT;
+}
+
+
+/*
+ * fsm_protreject - Peer doesn't speak this protocol.
+ *
+ * Treat this as a catastrophic error (RXJ-).
+ */
+void
+fsm_protreject(f)
+ fsm *f;
+{
+ switch( f->state ){
+ case CLOSING:
+ UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
+ /* fall through */
+ case CLOSED:
+ f->state = CLOSED;
+ if( f->callbacks->finished )
+ (*f->callbacks->finished)(f);
+ break;
+
+ case STOPPING:
+ case REQSENT:
+ case ACKRCVD:
+ case ACKSENT:
+ UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
+ /* fall through */
+ case STOPPED:
+ f->state = STOPPED;
+ if( f->callbacks->finished )
+ (*f->callbacks->finished)(f);
+ break;
+
+ case OPENED:
+ if( f->callbacks->down )
+ (*f->callbacks->down)(f);
+
+ /* Init restart counter, send Terminate-Request */
+ f->retransmits = f->maxtermtransmits;
+ fsm_sdata(f, TERMREQ, f->reqid = ++f->id,
+ (u_char *) f->term_reason, f->term_reason_len);
+ TIMEOUT(fsm_timeout, f, f->timeouttime);
+ --f->retransmits;
+
+ f->state = STOPPING;
+ break;
+
+ default:
+ FSMDEBUG((LOG_INFO, "%s: Protocol-reject event in state %d!",
+ PROTO_NAME(f), f->state));
+ }
+}
+
+
+/*
+ * fsm_sconfreq - Send a Configure-Request.
+ */
+static void
+fsm_sconfreq(f, retransmit)
+ fsm *f;
+ int retransmit;
+{
+ u_char *outp;
+ int cilen;
+
+ if( f->state != REQSENT && f->state != ACKRCVD && f->state != ACKSENT ){
+ /* Not currently negotiating - reset options */
+ if( f->callbacks->resetci )
+ (*f->callbacks->resetci)(f);
+ f->nakloops = 0;
+ }
+
+ if( !retransmit ){
+ /* New request - reset retransmission counter, use new ID */
+ f->retransmits = f->maxconfreqtransmits;
+ f->reqid = ++f->id;
+ }
+
+ f->seen_ack = 0;
+
+ /*
+ * Make up the request packet
+ */
+ outp = outpacket_buf + PPP_HDRLEN + HEADERLEN;
+ if( f->callbacks->cilen && f->callbacks->addci ){
+ cilen = (*f->callbacks->cilen)(f);
+ if( cilen > peer_mru[f->unit] - HEADERLEN )
+ cilen = peer_mru[f->unit] - HEADERLEN;
+ if (f->callbacks->addci)
+ (*f->callbacks->addci)(f, outp, &cilen);
+ } else
+ cilen = 0;
+
+ /* send the request to our peer */
+ fsm_sdata(f, CONFREQ, f->reqid, outp, cilen);
+
+ /* start the retransmit timer */
+ --f->retransmits;
+ TIMEOUT(fsm_timeout, f, f->timeouttime);
+
+ FSMDEBUG((LOG_INFO, "%s: sending Configure-Request, id %d",
+ PROTO_NAME(f), f->reqid));
+}
+
+
+/*
+ * fsm_sdata - Send some data.
+ *
+ * Used for all packets sent to our peer by this module.
+ */
+void
+fsm_sdata(f, code, id, data, datalen)
+ fsm *f;
+ u_char code, id;
+ u_char *data;
+ int datalen;
+{
+ u_char *outp;
+ int outlen;
+
+ /* Adjust length to be smaller than MTU */
+ outp = outpacket_buf;
+ if (datalen > peer_mru[f->unit] - HEADERLEN)
+ datalen = peer_mru[f->unit] - HEADERLEN;
+ if (datalen && data != outp + PPP_HDRLEN + HEADERLEN)
+ BCOPY(data, outp + PPP_HDRLEN + HEADERLEN, datalen);
+ outlen = datalen + HEADERLEN;
+ MAKEHEADER(outp, f->protocol);
+ PUTCHAR(code, outp);
+ PUTCHAR(id, outp);
+ PUTSHORT(outlen, outp);
+ output(f->unit, outpacket_buf, outlen + PPP_HDRLEN);
+
+ FSMDEBUG((LOG_INFO, "fsm_sdata(%s): Sent code %d, id %d.",
+ PROTO_NAME(f), code, id));
+}
diff --git a/usr.sbin/pppd/fsm.h b/usr.sbin/pppd/fsm.h
new file mode 100644
index 0000000..f289429
--- /dev/null
+++ b/usr.sbin/pppd/fsm.h
@@ -0,0 +1,144 @@
+/*
+ * fsm.h - {Link, IP} Control Protocol Finite State Machine definitions.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id$
+ */
+
+/*
+ * Packet header = Code, id, length.
+ */
+#define HEADERLEN (sizeof (u_char) + sizeof (u_char) + sizeof (u_short))
+
+
+/*
+ * CP (LCP, IPCP, etc.) codes.
+ */
+#define CONFREQ 1 /* Configuration Request */
+#define CONFACK 2 /* Configuration Ack */
+#define CONFNAK 3 /* Configuration Nak */
+#define CONFREJ 4 /* Configuration Reject */
+#define TERMREQ 5 /* Termination Request */
+#define TERMACK 6 /* Termination Ack */
+#define CODEREJ 7 /* Code Reject */
+
+
+/*
+ * Each FSM is described by an fsm structure and fsm callbacks.
+ */
+typedef struct fsm {
+ int unit; /* Interface unit number */
+ int protocol; /* Data Link Layer Protocol field value */
+ int state; /* State */
+ int flags; /* Contains option bits */
+ u_char id; /* Current id */
+ u_char reqid; /* Current request id */
+ u_char seen_ack; /* Have received valid Ack/Nak/Rej to Req */
+ int timeouttime; /* Timeout time in milliseconds */
+ int maxconfreqtransmits; /* Maximum Configure-Request transmissions */
+ int retransmits; /* Number of retransmissions left */
+ int maxtermtransmits; /* Maximum Terminate-Request transmissions */
+ int nakloops; /* Number of nak loops since last ack */
+ int maxnakloops; /* Maximum number of nak loops tolerated */
+ struct fsm_callbacks *callbacks; /* Callback routines */
+ char *term_reason; /* Reason for closing protocol */
+ int term_reason_len; /* Length of term_reason */
+} fsm;
+
+
+typedef struct fsm_callbacks {
+ void (*resetci) /* Reset our Configuration Information */
+ __P((fsm *));
+ int (*cilen) /* Length of our Configuration Information */
+ __P((fsm *));
+ void (*addci) /* Add our Configuration Information */
+ __P((fsm *, u_char *, int *));
+ int (*ackci) /* ACK our Configuration Information */
+ __P((fsm *, u_char *, int));
+ int (*nakci) /* NAK our Configuration Information */
+ __P((fsm *, u_char *, int));
+ int (*rejci) /* Reject our Configuration Information */
+ __P((fsm *, u_char *, int));
+ int (*reqci) /* Request peer's Configuration Information */
+ __P((fsm *, u_char *, int *, int));
+ void (*up) /* Called when fsm reaches OPENED state */
+ __P((fsm *));
+ void (*down) /* Called when fsm leaves OPENED state */
+ __P((fsm *));
+ void (*starting) /* Called when we want the lower layer */
+ __P((fsm *));
+ void (*finished) /* Called when we don't want the lower layer */
+ __P((fsm *));
+ void (*protreject) /* Called when Protocol-Reject received */
+ __P((int));
+ void (*retransmit) /* Retransmission is necessary */
+ __P((fsm *));
+ int (*extcode) /* Called when unknown code received */
+ __P((fsm *, int, int, u_char *, int));
+ char *proto_name; /* String name for protocol (for messages) */
+} fsm_callbacks;
+
+
+/*
+ * Link states.
+ */
+#define INITIAL 0 /* Down, hasn't been opened */
+#define STARTING 1 /* Down, been opened */
+#define CLOSED 2 /* Up, hasn't been opened */
+#define STOPPED 3 /* Open, waiting for down event */
+#define CLOSING 4 /* Terminating the connection, not open */
+#define STOPPING 5 /* Terminating, but open */
+#define REQSENT 6 /* We've sent a Config Request */
+#define ACKRCVD 7 /* We've received a Config Ack */
+#define ACKSENT 8 /* We've sent a Config Ack */
+#define OPENED 9 /* Connection available */
+
+
+/*
+ * Flags - indicate options controlling FSM operation
+ */
+#define OPT_PASSIVE 1 /* Don't die if we don't get a response */
+#define OPT_RESTART 2 /* Treat 2nd OPEN as DOWN, UP */
+#define OPT_SILENT 4 /* Wait for peer to speak first */
+
+
+/*
+ * Timeouts.
+ */
+#define DEFTIMEOUT 3 /* Timeout time in seconds */
+#define DEFMAXTERMREQS 2 /* Maximum Terminate-Request transmissions */
+#define DEFMAXCONFREQS 10 /* Maximum Configure-Request transmissions */
+#define DEFMAXNAKLOOPS 5 /* Maximum number of nak loops */
+
+
+/*
+ * Prototypes
+ */
+void fsm_init __P((fsm *));
+void fsm_lowerup __P((fsm *));
+void fsm_lowerdown __P((fsm *));
+void fsm_open __P((fsm *));
+void fsm_close __P((fsm *, char *));
+void fsm_input __P((fsm *, u_char *, int));
+void fsm_protreject __P((fsm *));
+void fsm_sdata __P((fsm *, int, int, u_char *, int));
+
+
+/*
+ * Variables
+ */
+extern int peer_mru[]; /* currently negotiated peer MRU (per unit) */
diff --git a/usr.sbin/pppd/ipcp.c b/usr.sbin/pppd/ipcp.c
new file mode 100644
index 0000000..b3973cf
--- /dev/null
+++ b/usr.sbin/pppd/ipcp.c
@@ -0,0 +1,1528 @@
+/*
+ * ipcp.c - PPP IP Control Protocol.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef lint
+static char rcsid[] = "$Id: ipcp.c,v 1.9 1997/08/19 17:52:38 peter Exp $";
+#endif
+
+/*
+ * TODO:
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <syslog.h>
+#include <netdb.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+#include "pppd.h"
+#include "fsm.h"
+#include "ipcp.h"
+#include "pathnames.h"
+
+/* global vars */
+ipcp_options ipcp_wantoptions[NUM_PPP]; /* Options that we want to request */
+ipcp_options ipcp_gotoptions[NUM_PPP]; /* Options that peer ack'd */
+ipcp_options ipcp_allowoptions[NUM_PPP]; /* Options we allow peer to request */
+ipcp_options ipcp_hisoptions[NUM_PPP]; /* Options that we ack'd */
+
+/* local vars */
+static int cis_received[NUM_PPP]; /* # Conf-Reqs received */
+static int default_route_set[NUM_PPP]; /* Have set up a default route */
+static int proxy_arp_set[NUM_PPP]; /* Have created proxy arp entry */
+
+/*
+ * Callbacks for fsm code. (CI = Configuration Information)
+ */
+static void ipcp_resetci __P((fsm *)); /* Reset our CI */
+static int ipcp_cilen __P((fsm *)); /* Return length of our CI */
+static void ipcp_addci __P((fsm *, u_char *, int *)); /* Add our CI */
+static int ipcp_ackci __P((fsm *, u_char *, int)); /* Peer ack'd our CI */
+static int ipcp_nakci __P((fsm *, u_char *, int)); /* Peer nak'd our CI */
+static int ipcp_rejci __P((fsm *, u_char *, int)); /* Peer rej'd our CI */
+static int ipcp_reqci __P((fsm *, u_char *, int *, int)); /* Rcv CI */
+static void ipcp_up __P((fsm *)); /* We're UP */
+static void ipcp_down __P((fsm *)); /* We're DOWN */
+static void ipcp_script __P((fsm *, char *)); /* Run an up/down script */
+static void ipcp_finished __P((fsm *)); /* Don't need lower layer */
+
+fsm ipcp_fsm[NUM_PPP]; /* IPCP fsm structure */
+
+static fsm_callbacks ipcp_callbacks = { /* IPCP callback routines */
+ ipcp_resetci, /* Reset our Configuration Information */
+ ipcp_cilen, /* Length of our Configuration Information */
+ ipcp_addci, /* Add our Configuration Information */
+ ipcp_ackci, /* ACK our Configuration Information */
+ ipcp_nakci, /* NAK our Configuration Information */
+ ipcp_rejci, /* Reject our Configuration Information */
+ ipcp_reqci, /* Request peer's Configuration Information */
+ ipcp_up, /* Called when fsm reaches OPENED state */
+ ipcp_down, /* Called when fsm leaves OPENED state */
+ NULL, /* Called when we want the lower layer up */
+ ipcp_finished, /* Called when we want the lower layer down */
+ NULL, /* Called when Protocol-Reject received */
+ NULL, /* Retransmission is necessary */
+ NULL, /* Called to handle protocol-specific codes */
+ "IPCP" /* String name of protocol */
+};
+
+/*
+ * Protocol entry points from main code.
+ */
+static void ipcp_init __P((int));
+static void ipcp_open __P((int));
+static void ipcp_close __P((int, char *));
+static void ipcp_lowerup __P((int));
+static void ipcp_lowerdown __P((int));
+static void ipcp_input __P((int, u_char *, int));
+static void ipcp_protrej __P((int));
+static int ipcp_printpkt __P((u_char *, int,
+ void (*) __P((void *, char *, ...)), void *));
+static void ip_check_options __P((void));
+static int ip_demand_conf __P((int));
+static int ip_active_pkt __P((u_char *, int));
+
+struct protent ipcp_protent = {
+ PPP_IPCP,
+ ipcp_init,
+ ipcp_input,
+ ipcp_protrej,
+ ipcp_lowerup,
+ ipcp_lowerdown,
+ ipcp_open,
+ ipcp_close,
+ ipcp_printpkt,
+ NULL,
+ 1,
+ "IPCP",
+ ip_check_options,
+ ip_demand_conf,
+ ip_active_pkt
+};
+
+static void ipcp_clear_addrs __P((int));
+
+/*
+ * Lengths of configuration options.
+ */
+#define CILEN_VOID 2
+#define CILEN_COMPRESS 4 /* min length for compression protocol opt. */
+#define CILEN_VJ 6 /* length for RFC1332 Van-Jacobson opt. */
+#define CILEN_ADDR 6 /* new-style single address option */
+#define CILEN_ADDRS 10 /* old-style dual address option */
+
+
+#define CODENAME(x) ((x) == CONFACK ? "ACK" : \
+ (x) == CONFNAK ? "NAK" : "REJ")
+
+
+/*
+ * Make a string representation of a network IP address.
+ */
+char *
+ip_ntoa(ipaddr)
+u_int32_t ipaddr;
+{
+ static char b[64];
+
+ ipaddr = ntohl(ipaddr);
+
+ sprintf(b, "%d.%d.%d.%d",
+ (u_char)(ipaddr >> 24),
+ (u_char)(ipaddr >> 16),
+ (u_char)(ipaddr >> 8),
+ (u_char)(ipaddr));
+ return b;
+}
+
+
+/*
+ * ipcp_init - Initialize IPCP.
+ */
+static void
+ipcp_init(unit)
+ int unit;
+{
+ fsm *f = &ipcp_fsm[unit];
+ ipcp_options *wo = &ipcp_wantoptions[unit];
+ ipcp_options *ao = &ipcp_allowoptions[unit];
+
+ f->unit = unit;
+ f->protocol = PPP_IPCP;
+ f->callbacks = &ipcp_callbacks;
+ fsm_init(&ipcp_fsm[unit]);
+
+ memset(wo, 0, sizeof(*wo));
+ memset(ao, 0, sizeof(*ao));
+
+ wo->neg_addr = 1;
+ wo->neg_vj = 1;
+ wo->vj_protocol = IPCP_VJ_COMP;
+ wo->maxslotindex = MAX_STATES - 1; /* really max index */
+ wo->cflag = 1;
+
+ /* max slots and slot-id compression are currently hardwired in */
+ /* ppp_if.c to 16 and 1, this needs to be changed (among other */
+ /* things) gmc */
+
+ ao->neg_addr = 1;
+ ao->neg_vj = 1;
+ ao->maxslotindex = MAX_STATES - 1;
+ ao->cflag = 1;
+
+ /*
+ * XXX These control whether the user may use the proxyarp
+ * and defaultroute options.
+ */
+ ao->proxy_arp = 1;
+ ao->default_route = 1;
+}
+
+
+/*
+ * ipcp_open - IPCP is allowed to come up.
+ */
+static void
+ipcp_open(unit)
+ int unit;
+{
+ fsm_open(&ipcp_fsm[unit]);
+}
+
+
+/*
+ * ipcp_close - Take IPCP down.
+ */
+static void
+ipcp_close(unit, reason)
+ int unit;
+ char *reason;
+{
+ fsm_close(&ipcp_fsm[unit], reason);
+}
+
+
+/*
+ * ipcp_lowerup - The lower layer is up.
+ */
+static void
+ipcp_lowerup(unit)
+ int unit;
+{
+ fsm_lowerup(&ipcp_fsm[unit]);
+}
+
+
+/*
+ * ipcp_lowerdown - The lower layer is down.
+ */
+static void
+ipcp_lowerdown(unit)
+ int unit;
+{
+ fsm_lowerdown(&ipcp_fsm[unit]);
+}
+
+
+/*
+ * ipcp_input - Input IPCP packet.
+ */
+static void
+ipcp_input(unit, p, len)
+ int unit;
+ u_char *p;
+ int len;
+{
+ fsm_input(&ipcp_fsm[unit], p, len);
+}
+
+
+/*
+ * ipcp_protrej - A Protocol-Reject was received for IPCP.
+ *
+ * Pretend the lower layer went down, so we shut up.
+ */
+static void
+ipcp_protrej(unit)
+ int unit;
+{
+ fsm_lowerdown(&ipcp_fsm[unit]);
+}
+
+
+/*
+ * ipcp_resetci - Reset our CI.
+ */
+static void
+ipcp_resetci(f)
+ fsm *f;
+{
+ ipcp_options *wo = &ipcp_wantoptions[f->unit];
+
+ wo->req_addr = wo->neg_addr && ipcp_allowoptions[f->unit].neg_addr;
+ if (wo->ouraddr == 0)
+ wo->accept_local = 1;
+ if (wo->hisaddr == 0)
+ wo->accept_remote = 1;
+ ipcp_gotoptions[f->unit] = *wo;
+ cis_received[f->unit] = 0;
+}
+
+
+/*
+ * ipcp_cilen - Return length of our CI.
+ */
+static int
+ipcp_cilen(f)
+ fsm *f;
+{
+ ipcp_options *go = &ipcp_gotoptions[f->unit];
+ ipcp_options *wo = &ipcp_wantoptions[f->unit];
+ ipcp_options *ho = &ipcp_hisoptions[f->unit];
+
+#define LENCIVJ(neg, old) (neg ? (old? CILEN_COMPRESS : CILEN_VJ) : 0)
+#define LENCIADDR(neg, old) (neg ? (old? CILEN_ADDRS : CILEN_ADDR) : 0)
+
+ /*
+ * First see if we want to change our options to the old
+ * forms because we have received old forms from the peer.
+ */
+ if (wo->neg_addr && !go->neg_addr && !go->old_addrs) {
+ /* use the old style of address negotiation */
+ go->neg_addr = 1;
+ go->old_addrs = 1;
+ }
+ if (wo->neg_vj && !go->neg_vj && !go->old_vj) {
+ /* try an older style of VJ negotiation */
+ if (cis_received[f->unit] == 0) {
+ /* keep trying the new style until we see some CI from the peer */
+ go->neg_vj = 1;
+ } else {
+ /* use the old style only if the peer did */
+ if (ho->neg_vj && ho->old_vj) {
+ go->neg_vj = 1;
+ go->old_vj = 1;
+ go->vj_protocol = ho->vj_protocol;
+ }
+ }
+ }
+
+ return (LENCIADDR(go->neg_addr, go->old_addrs) +
+ LENCIVJ(go->neg_vj, go->old_vj));
+}
+
+
+/*
+ * ipcp_addci - Add our desired CIs to a packet.
+ */
+static void
+ipcp_addci(f, ucp, lenp)
+ fsm *f;
+ u_char *ucp;
+ int *lenp;
+{
+ ipcp_options *go = &ipcp_gotoptions[f->unit];
+ int len = *lenp;
+
+#define ADDCIVJ(opt, neg, val, old, maxslotindex, cflag) \
+ if (neg) { \
+ int vjlen = old? CILEN_COMPRESS : CILEN_VJ; \
+ if (len >= vjlen) { \
+ PUTCHAR(opt, ucp); \
+ PUTCHAR(vjlen, ucp); \
+ PUTSHORT(val, ucp); \
+ if (!old) { \
+ PUTCHAR(maxslotindex, ucp); \
+ PUTCHAR(cflag, ucp); \
+ } \
+ len -= vjlen; \
+ } else \
+ neg = 0; \
+ }
+
+#define ADDCIADDR(opt, neg, old, val1, val2) \
+ if (neg) { \
+ int addrlen = (old? CILEN_ADDRS: CILEN_ADDR); \
+ if (len >= addrlen) { \
+ u_int32_t l; \
+ PUTCHAR(opt, ucp); \
+ PUTCHAR(addrlen, ucp); \
+ l = ntohl(val1); \
+ PUTLONG(l, ucp); \
+ if (old) { \
+ l = ntohl(val2); \
+ PUTLONG(l, ucp); \
+ } \
+ len -= addrlen; \
+ } else \
+ neg = 0; \
+ }
+
+ ADDCIADDR((go->old_addrs? CI_ADDRS: CI_ADDR), go->neg_addr,
+ go->old_addrs, go->ouraddr, go->hisaddr);
+
+ ADDCIVJ(CI_COMPRESSTYPE, go->neg_vj, go->vj_protocol, go->old_vj,
+ go->maxslotindex, go->cflag);
+
+ *lenp -= len;
+}
+
+
+/*
+ * ipcp_ackci - Ack our CIs.
+ *
+ * Returns:
+ * 0 - Ack was bad.
+ * 1 - Ack was good.
+ */
+static int
+ipcp_ackci(f, p, len)
+ fsm *f;
+ u_char *p;
+ int len;
+{
+ ipcp_options *go = &ipcp_gotoptions[f->unit];
+ u_short cilen, citype, cishort;
+ u_int32_t cilong;
+ u_char cimaxslotindex, cicflag;
+
+ /*
+ * CIs must be in exactly the same order that we sent...
+ * Check packet length and CI length at each step.
+ * If we find any deviations, then this packet is bad.
+ */
+
+#define ACKCIVJ(opt, neg, val, old, maxslotindex, cflag) \
+ if (neg) { \
+ int vjlen = old? CILEN_COMPRESS : CILEN_VJ; \
+ if ((len -= vjlen) < 0) \
+ goto bad; \
+ GETCHAR(citype, p); \
+ GETCHAR(cilen, p); \
+ if (cilen != vjlen || \
+ citype != opt) \
+ goto bad; \
+ GETSHORT(cishort, p); \
+ if (cishort != val) \
+ goto bad; \
+ if (!old) { \
+ GETCHAR(cimaxslotindex, p); \
+ if (cimaxslotindex != maxslotindex) \
+ goto bad; \
+ GETCHAR(cicflag, p); \
+ if (cicflag != cflag) \
+ goto bad; \
+ } \
+ }
+
+#define ACKCIADDR(opt, neg, old, val1, val2) \
+ if (neg) { \
+ int addrlen = (old? CILEN_ADDRS: CILEN_ADDR); \
+ u_int32_t l; \
+ if ((len -= addrlen) < 0) \
+ goto bad; \
+ GETCHAR(citype, p); \
+ GETCHAR(cilen, p); \
+ if (cilen != addrlen || \
+ citype != opt) \
+ goto bad; \
+ GETLONG(l, p); \
+ cilong = htonl(l); \
+ if (val1 != cilong) \
+ goto bad; \
+ if (old) { \
+ GETLONG(l, p); \
+ cilong = htonl(l); \
+ if (val2 != cilong) \
+ goto bad; \
+ } \
+ }
+
+ ACKCIADDR((go->old_addrs? CI_ADDRS: CI_ADDR), go->neg_addr,
+ go->old_addrs, go->ouraddr, go->hisaddr);
+
+ ACKCIVJ(CI_COMPRESSTYPE, go->neg_vj, go->vj_protocol, go->old_vj,
+ go->maxslotindex, go->cflag);
+
+ /*
+ * If there are any remaining CIs, then this packet is bad.
+ */
+ if (len != 0)
+ goto bad;
+ return (1);
+
+bad:
+ IPCPDEBUG((LOG_INFO, "ipcp_ackci: received bad Ack!"));
+ return (0);
+}
+
+/*
+ * ipcp_nakci - Peer has sent a NAK for some of our CIs.
+ * This should not modify any state if the Nak is bad
+ * or if IPCP is in the OPENED state.
+ *
+ * Returns:
+ * 0 - Nak was bad.
+ * 1 - Nak was good.
+ */
+static int
+ipcp_nakci(f, p, len)
+ fsm *f;
+ u_char *p;
+ int len;
+{
+ ipcp_options *go = &ipcp_gotoptions[f->unit];
+ u_char cimaxslotindex, cicflag;
+ u_char citype, cilen, *next;
+ u_short cishort;
+ u_int32_t ciaddr1, ciaddr2, l;
+ ipcp_options no; /* options we've seen Naks for */
+ ipcp_options try; /* options to request next time */
+
+ BZERO(&no, sizeof(no));
+ try = *go;
+
+ /*
+ * Any Nak'd CIs must be in exactly the same order that we sent.
+ * Check packet length and CI length at each step.
+ * If we find any deviations, then this packet is bad.
+ */
+#define NAKCIADDR(opt, neg, old, code) \
+ if (go->neg && \
+ len >= (cilen = (old? CILEN_ADDRS: CILEN_ADDR)) && \
+ p[1] == cilen && \
+ p[0] == opt) { \
+ len -= cilen; \
+ INCPTR(2, p); \
+ GETLONG(l, p); \
+ ciaddr1 = htonl(l); \
+ if (old) { \
+ GETLONG(l, p); \
+ ciaddr2 = htonl(l); \
+ no.old_addrs = 1; \
+ } else \
+ ciaddr2 = 0; \
+ no.neg = 1; \
+ code \
+ }
+
+#define NAKCIVJ(opt, neg, code) \
+ if (go->neg && \
+ ((cilen = p[1]) == CILEN_COMPRESS || cilen == CILEN_VJ) && \
+ len >= cilen && \
+ p[0] == opt) { \
+ len -= cilen; \
+ INCPTR(2, p); \
+ GETSHORT(cishort, p); \
+ no.neg = 1; \
+ code \
+ }
+
+ /*
+ * Accept the peer's idea of {our,his} address, if different
+ * from our idea, only if the accept_{local,remote} flag is set.
+ */
+ NAKCIADDR((go->old_addrs? CI_ADDRS: CI_ADDR), neg_addr, go->old_addrs,
+ if (go->accept_local && ciaddr1) { /* Do we know our address? */
+ try.ouraddr = ciaddr1;
+ IPCPDEBUG((LOG_INFO, "local IP address %s",
+ ip_ntoa(ciaddr1)));
+ }
+ if (go->accept_remote && ciaddr2) { /* Does he know his? */
+ try.hisaddr = ciaddr2;
+ IPCPDEBUG((LOG_INFO, "remote IP address %s",
+ ip_ntoa(ciaddr2)));
+ }
+ );
+
+ /*
+ * Accept the peer's value of maxslotindex provided that it
+ * is less than what we asked for. Turn off slot-ID compression
+ * if the peer wants. Send old-style compress-type option if
+ * the peer wants.
+ */
+ NAKCIVJ(CI_COMPRESSTYPE, neg_vj,
+ if (cilen == CILEN_VJ) {
+ GETCHAR(cimaxslotindex, p);
+ GETCHAR(cicflag, p);
+ if (cishort == IPCP_VJ_COMP) {
+ try.old_vj = 0;
+ if (cimaxslotindex < go->maxslotindex)
+ try.maxslotindex = cimaxslotindex;
+ if (!cicflag)
+ try.cflag = 0;
+ } else {
+ try.neg_vj = 0;
+ }
+ } else {
+ if (cishort == IPCP_VJ_COMP || cishort == IPCP_VJ_COMP_OLD) {
+ try.old_vj = 1;
+ try.vj_protocol = cishort;
+ } else {
+ try.neg_vj = 0;
+ }
+ }
+ );
+
+ /*
+ * There may be remaining CIs, if the peer is requesting negotiation
+ * on an option that we didn't include in our request packet.
+ * If they want to negotiate about IP addresses, we comply.
+ * If they want us to ask for compression, we refuse.
+ */
+ while (len > CILEN_VOID) {
+ GETCHAR(citype, p);
+ GETCHAR(cilen, p);
+ if( (len -= cilen) < 0 )
+ goto bad;
+ next = p + cilen - 2;
+
+ switch (citype) {
+ case CI_COMPRESSTYPE:
+ if (go->neg_vj || no.neg_vj ||
+ (cilen != CILEN_VJ && cilen != CILEN_COMPRESS))
+ goto bad;
+ no.neg_vj = 1;
+ break;
+ case CI_ADDRS:
+ if ((go->neg_addr && go->old_addrs) || no.old_addrs
+ || cilen != CILEN_ADDRS)
+ goto bad;
+ try.neg_addr = 1;
+ try.old_addrs = 1;
+ GETLONG(l, p);
+ ciaddr1 = htonl(l);
+ if (ciaddr1 && go->accept_local)
+ try.ouraddr = ciaddr1;
+ GETLONG(l, p);
+ ciaddr2 = htonl(l);
+ if (ciaddr2 && go->accept_remote)
+ try.hisaddr = ciaddr2;
+ no.old_addrs = 1;
+ break;
+ case CI_ADDR:
+ if (go->neg_addr || no.neg_addr || cilen != CILEN_ADDR)
+ goto bad;
+ try.old_addrs = 0;
+ GETLONG(l, p);
+ ciaddr1 = htonl(l);
+ if (ciaddr1 && go->accept_local)
+ try.ouraddr = ciaddr1;
+ if (try.ouraddr != 0)
+ try.neg_addr = 1;
+ no.neg_addr = 1;
+ break;
+ }
+ p = next;
+ }
+
+ /* If there is still anything left, this packet is bad. */
+ if (len != 0)
+ goto bad;
+
+ /*
+ * OK, the Nak is good. Now we can update state.
+ */
+ if (f->state != OPENED)
+ *go = try;
+
+ return 1;
+
+bad:
+ IPCPDEBUG((LOG_INFO, "ipcp_nakci: received bad Nak!"));
+ return 0;
+}
+
+
+/*
+ * ipcp_rejci - Reject some of our CIs.
+ */
+static int
+ipcp_rejci(f, p, len)
+ fsm *f;
+ u_char *p;
+ int len;
+{
+ ipcp_options *go = &ipcp_gotoptions[f->unit];
+ u_char cimaxslotindex, ciflag, cilen;
+ u_short cishort;
+ u_int32_t cilong;
+ ipcp_options try; /* options to request next time */
+
+ try = *go;
+ /*
+ * Any Rejected CIs must be in exactly the same order that we sent.
+ * Check packet length and CI length at each step.
+ * If we find any deviations, then this packet is bad.
+ */
+#define REJCIADDR(opt, neg, old, val1, val2) \
+ if (go->neg && \
+ len >= (cilen = old? CILEN_ADDRS: CILEN_ADDR) && \
+ p[1] == cilen && \
+ p[0] == opt) { \
+ u_int32_t l; \
+ len -= cilen; \
+ INCPTR(2, p); \
+ GETLONG(l, p); \
+ cilong = htonl(l); \
+ /* Check rejected value. */ \
+ if (cilong != val1) \
+ goto bad; \
+ if (old) { \
+ GETLONG(l, p); \
+ cilong = htonl(l); \
+ /* Check rejected value. */ \
+ if (cilong != val2) \
+ goto bad; \
+ } \
+ try.neg = 0; \
+ }
+
+#define REJCIVJ(opt, neg, val, old, maxslot, cflag) \
+ if (go->neg && \
+ p[1] == (old? CILEN_COMPRESS : CILEN_VJ) && \
+ len >= p[1] && \
+ p[0] == opt) { \
+ len -= p[1]; \
+ INCPTR(2, p); \
+ GETSHORT(cishort, p); \
+ /* Check rejected value. */ \
+ if (cishort != val) \
+ goto bad; \
+ if (!old) { \
+ GETCHAR(cimaxslotindex, p); \
+ if (cimaxslotindex != maxslot) \
+ goto bad; \
+ GETCHAR(ciflag, p); \
+ if (ciflag != cflag) \
+ goto bad; \
+ } \
+ try.neg = 0; \
+ }
+
+ REJCIADDR((go->old_addrs? CI_ADDRS: CI_ADDR), neg_addr,
+ go->old_addrs, go->ouraddr, go->hisaddr);
+
+ REJCIVJ(CI_COMPRESSTYPE, neg_vj, go->vj_protocol, go->old_vj,
+ go->maxslotindex, go->cflag);
+
+ /*
+ * If there are any remaining CIs, then this packet is bad.
+ */
+ if (len != 0)
+ goto bad;
+ /*
+ * Now we can update state.
+ */
+ if (f->state != OPENED)
+ *go = try;
+ return 1;
+
+bad:
+ IPCPDEBUG((LOG_INFO, "ipcp_rejci: received bad Reject!"));
+ return 0;
+}
+
+
+/*
+ * ipcp_reqci - Check the peer's requested CIs and send appropriate response.
+ *
+ * Returns: CONFACK, CONFNAK or CONFREJ and input packet modified
+ * appropriately. If reject_if_disagree is non-zero, doesn't return
+ * CONFNAK; returns CONFREJ if it can't return CONFACK.
+ */
+static int
+ipcp_reqci(f, inp, len, reject_if_disagree)
+ fsm *f;
+ u_char *inp; /* Requested CIs */
+ int *len; /* Length of requested CIs */
+ int reject_if_disagree;
+{
+ ipcp_options *wo = &ipcp_wantoptions[f->unit];
+ ipcp_options *ho = &ipcp_hisoptions[f->unit];
+ ipcp_options *ao = &ipcp_allowoptions[f->unit];
+ ipcp_options *go = &ipcp_gotoptions[f->unit];
+ u_char *cip, *next; /* Pointer to current and next CIs */
+ u_short cilen, citype; /* Parsed len, type */
+ u_short cishort; /* Parsed short value */
+ u_int32_t tl, ciaddr1, ciaddr2;/* Parsed address values */
+ int rc = CONFACK; /* Final packet return code */
+ int orc; /* Individual option return code */
+ u_char *p; /* Pointer to next char to parse */
+ u_char *ucp = inp; /* Pointer to current output char */
+ int l = *len; /* Length left */
+ u_char maxslotindex, cflag;
+ int d;
+
+ cis_received[f->unit] = 1;
+
+ /*
+ * Reset all his options.
+ */
+ BZERO(ho, sizeof(*ho));
+
+ /*
+ * Process all his options.
+ */
+ next = inp;
+ while (l) {
+ orc = CONFACK; /* Assume success */
+ cip = p = next; /* Remember begining of CI */
+ if (l < 2 || /* Not enough data for CI header or */
+ p[1] < 2 || /* CI length too small or */
+ p[1] > l) { /* CI length too big? */
+ IPCPDEBUG((LOG_INFO, "ipcp_reqci: bad CI length!"));
+ orc = CONFREJ; /* Reject bad CI */
+ cilen = l; /* Reject till end of packet */
+ l = 0; /* Don't loop again */
+ goto endswitch;
+ }
+ GETCHAR(citype, p); /* Parse CI type */
+ GETCHAR(cilen, p); /* Parse CI length */
+ l -= cilen; /* Adjust remaining length */
+ next += cilen; /* Step to next CI */
+
+ switch (citype) { /* Check CI type */
+ case CI_ADDRS:
+ IPCPDEBUG((LOG_INFO, "ipcp: received ADDRS "));
+ if (!ao->neg_addr ||
+ cilen != CILEN_ADDRS) { /* Check CI length */
+ orc = CONFREJ; /* Reject CI */
+ break;
+ }
+
+ /*
+ * If he has no address, or if we both have his address but
+ * disagree about it, then NAK it with our idea.
+ * In particular, if we don't know his address, but he does,
+ * then accept it.
+ */
+ GETLONG(tl, p); /* Parse source address (his) */
+ ciaddr1 = htonl(tl);
+ IPCPDEBUG((LOG_INFO, "(%s:", ip_ntoa(ciaddr1)));
+ if (ciaddr1 != wo->hisaddr
+ && (ciaddr1 == 0 || !wo->accept_remote)) {
+ orc = CONFNAK;
+ if (!reject_if_disagree) {
+ DECPTR(sizeof(u_int32_t), p);
+ tl = ntohl(wo->hisaddr);
+ PUTLONG(tl, p);
+ }
+ } else if (ciaddr1 == 0 && wo->hisaddr == 0) {
+ /*
+ * If neither we nor he knows his address, reject the option.
+ */
+ orc = CONFREJ;
+ wo->req_addr = 0; /* don't NAK with 0.0.0.0 later */
+ break;
+ }
+
+ /*
+ * If he doesn't know our address, or if we both have our address
+ * but disagree about it, then NAK it with our idea.
+ */
+ GETLONG(tl, p); /* Parse desination address (ours) */
+ ciaddr2 = htonl(tl);
+ IPCPDEBUG((LOG_INFO, "%s)", ip_ntoa(ciaddr2)));
+ if (ciaddr2 != wo->ouraddr) {
+ if (ciaddr2 == 0 || !wo->accept_local) {
+ orc = CONFNAK;
+ if (!reject_if_disagree) {
+ DECPTR(sizeof(u_int32_t), p);
+ tl = ntohl(wo->ouraddr);
+ PUTLONG(tl, p);
+ }
+ } else {
+ go->ouraddr = ciaddr2; /* accept peer's idea */
+ }
+ }
+
+ ho->neg_addr = 1;
+ ho->old_addrs = 1;
+ ho->hisaddr = ciaddr1;
+ ho->ouraddr = ciaddr2;
+ break;
+
+ case CI_ADDR:
+ IPCPDEBUG((LOG_INFO, "ipcp: received ADDR "));
+
+ if (!ao->neg_addr ||
+ cilen != CILEN_ADDR) { /* Check CI length */
+ orc = CONFREJ; /* Reject CI */
+ break;
+ }
+
+ /*
+ * If he has no address, or if we both have his address but
+ * disagree about it, then NAK it with our idea.
+ * In particular, if we don't know his address, but he does,
+ * then accept it.
+ */
+ GETLONG(tl, p); /* Parse source address (his) */
+ ciaddr1 = htonl(tl);
+ IPCPDEBUG((LOG_INFO, "(%s)", ip_ntoa(ciaddr1)));
+ if (ciaddr1 != wo->hisaddr
+ && (ciaddr1 == 0 || !wo->accept_remote)) {
+ orc = CONFNAK;
+ if (!reject_if_disagree) {
+ DECPTR(sizeof(u_int32_t), p);
+ tl = ntohl(wo->hisaddr);
+ PUTLONG(tl, p);
+ }
+ } else if (ciaddr1 == 0 && wo->hisaddr == 0) {
+ /*
+ * Don't ACK an address of 0.0.0.0 - reject it instead.
+ */
+ orc = CONFREJ;
+ wo->req_addr = 0; /* don't NAK with 0.0.0.0 later */
+ break;
+ }
+
+ ho->neg_addr = 1;
+ ho->hisaddr = ciaddr1;
+ break;
+
+ case CI_MS_DNS1:
+ case CI_MS_DNS2:
+ /* Microsoft primary or secondary DNS request */
+ d = citype == CI_MS_DNS2;
+ IPCPDEBUG((LOG_INFO, "ipcp: received DNS%d Request ", d+1));
+
+ /* If we do not have a DNS address then we cannot send it */
+ if (ao->dnsaddr[d] == 0 ||
+ cilen != CILEN_ADDR) { /* Check CI length */
+ orc = CONFREJ; /* Reject CI */
+ break;
+ }
+ GETLONG(tl, p);
+ if (htonl(tl) != ao->dnsaddr[d]) {
+ DECPTR(sizeof(u_int32_t), p);
+ tl = ntohl(ao->dnsaddr[d]);
+ PUTLONG(tl, p);
+ orc = CONFNAK;
+ }
+ break;
+
+ case CI_MS_WINS1:
+ case CI_MS_WINS2:
+ /* Microsoft primary or secondary WINS request */
+ d = citype == CI_MS_WINS2;
+ IPCPDEBUG((LOG_INFO, "ipcp: received WINS%d Request ", d+1));
+
+ /* If we do not have a DNS address then we cannot send it */
+ if (ao->winsaddr[d] == 0 ||
+ cilen != CILEN_ADDR) { /* Check CI length */
+ orc = CONFREJ; /* Reject CI */
+ break;
+ }
+ GETLONG(tl, p);
+ if (htonl(tl) != ao->winsaddr[d]) {
+ DECPTR(sizeof(u_int32_t), p);
+ tl = ntohl(ao->winsaddr[d]);
+ PUTLONG(tl, p);
+ orc = CONFNAK;
+ }
+ break;
+
+ case CI_COMPRESSTYPE:
+ IPCPDEBUG((LOG_INFO, "ipcp: received COMPRESSTYPE "));
+ if (!ao->neg_vj ||
+ (cilen != CILEN_VJ && cilen != CILEN_COMPRESS)) {
+ orc = CONFREJ;
+ break;
+ }
+ GETSHORT(cishort, p);
+ IPCPDEBUG((LOG_INFO, "(%d)", cishort));
+
+ if (!(cishort == IPCP_VJ_COMP ||
+ (cishort == IPCP_VJ_COMP_OLD && cilen == CILEN_COMPRESS))) {
+ orc = CONFREJ;
+ break;
+ }
+
+ ho->neg_vj = 1;
+ ho->vj_protocol = cishort;
+ if (cilen == CILEN_VJ) {
+ GETCHAR(maxslotindex, p);
+ if (maxslotindex > ao->maxslotindex) {
+ orc = CONFNAK;
+ if (!reject_if_disagree){
+ DECPTR(1, p);
+ PUTCHAR(ao->maxslotindex, p);
+ }
+ }
+ GETCHAR(cflag, p);
+ if (cflag && !ao->cflag) {
+ orc = CONFNAK;
+ if (!reject_if_disagree){
+ DECPTR(1, p);
+ PUTCHAR(wo->cflag, p);
+ }
+ }
+ ho->maxslotindex = maxslotindex;
+ ho->cflag = cflag;
+ } else {
+ ho->old_vj = 1;
+ ho->maxslotindex = MAX_STATES - 1;
+ ho->cflag = 1;
+ }
+ break;
+
+ default:
+ orc = CONFREJ;
+ break;
+ }
+
+endswitch:
+ IPCPDEBUG((LOG_INFO, " (%s)\n", CODENAME(orc)));
+
+ if (orc == CONFACK && /* Good CI */
+ rc != CONFACK) /* but prior CI wasnt? */
+ continue; /* Don't send this one */
+
+ if (orc == CONFNAK) { /* Nak this CI? */
+ if (reject_if_disagree) /* Getting fed up with sending NAKs? */
+ orc = CONFREJ; /* Get tough if so */
+ else {
+ if (rc == CONFREJ) /* Rejecting prior CI? */
+ continue; /* Don't send this one */
+ if (rc == CONFACK) { /* Ack'd all prior CIs? */
+ rc = CONFNAK; /* Not anymore... */
+ ucp = inp; /* Backup */
+ }
+ }
+ }
+
+ if (orc == CONFREJ && /* Reject this CI */
+ rc != CONFREJ) { /* but no prior ones? */
+ rc = CONFREJ;
+ ucp = inp; /* Backup */
+ }
+
+ /* Need to move CI? */
+ if (ucp != cip)
+ BCOPY(cip, ucp, cilen); /* Move it */
+
+ /* Update output pointer */
+ INCPTR(cilen, ucp);
+ }
+
+ /*
+ * If we aren't rejecting this packet, and we want to negotiate
+ * their address, and they didn't send their address, then we
+ * send a NAK with a CI_ADDR option appended. We assume the
+ * input buffer is long enough that we can append the extra
+ * option safely.
+ */
+ if (rc != CONFREJ && !ho->neg_addr &&
+ wo->req_addr && !reject_if_disagree) {
+ if (rc == CONFACK) {
+ rc = CONFNAK;
+ ucp = inp; /* reset pointer */
+ wo->req_addr = 0; /* don't ask again */
+ }
+ PUTCHAR(CI_ADDR, ucp);
+ PUTCHAR(CILEN_ADDR, ucp);
+ tl = ntohl(wo->hisaddr);
+ PUTLONG(tl, ucp);
+ }
+
+ *len = ucp - inp; /* Compute output length */
+ IPCPDEBUG((LOG_INFO, "ipcp: returning Configure-%s", CODENAME(rc)));
+ return (rc); /* Return final code */
+}
+
+
+/*
+ * ip_check_options - check that any IP-related options are OK,
+ * and assign appropriate defaults.
+ */
+static void
+ip_check_options()
+{
+ struct hostent *hp;
+ u_int32_t local;
+ ipcp_options *wo = &ipcp_wantoptions[0];
+
+ /*
+ * Default our local IP address based on our hostname.
+ * If local IP address already given, don't bother.
+ */
+ if (wo->ouraddr == 0 && !disable_defaultip) {
+ /*
+ * Look up our hostname (possibly with domain name appended)
+ * and take the first IP address as our local IP address.
+ * If there isn't an IP address for our hostname, too bad.
+ */
+ wo->accept_local = 1; /* don't insist on this default value */
+ if ((hp = gethostbyname(hostname)) != NULL) {
+ local = *(u_int32_t *)hp->h_addr;
+ if (local != 0 && !bad_ip_adrs(local))
+ wo->ouraddr = local;
+ }
+ }
+
+ if (demand && wo->hisaddr == 0) {
+ option_error("remote IP address required for demand-dialling\n");
+ exit(1);
+ }
+#if 0
+ if (demand && wo->accept_remote) {
+ option_error("ipcp-accept-remote is incompatible with demand\n");
+ exit(1);
+ }
+#endif
+}
+
+
+/*
+ * ip_demand_conf - configure the interface as though
+ * IPCP were up, for use with dial-on-demand.
+ */
+static int
+ip_demand_conf(u)
+ int u;
+{
+ ipcp_options *wo = &ipcp_wantoptions[u];
+
+ if (!sifaddr(u, wo->ouraddr, wo->hisaddr, GetMask(wo->ouraddr)))
+ return 0;
+ if (!sifup(u))
+ return 0;
+ if (!sifnpmode(u, PPP_IP, NPMODE_QUEUE))
+ return 0;
+ if (wo->default_route)
+ if (sifdefaultroute(u, wo->ouraddr, wo->hisaddr))
+ default_route_set[u] = 1;
+ if (wo->proxy_arp)
+ if (sifproxyarp(u, wo->hisaddr))
+ proxy_arp_set[u] = 1;
+
+ syslog(LOG_NOTICE, "local IP address %s", ip_ntoa(wo->ouraddr));
+ syslog(LOG_NOTICE, "remote IP address %s", ip_ntoa(wo->hisaddr));
+
+ return 1;
+}
+
+
+/*
+ * ipcp_up - IPCP has come UP.
+ *
+ * Configure the IP network interface appropriately and bring it up.
+ */
+static void
+ipcp_up(f)
+ fsm *f;
+{
+ u_int32_t mask;
+ ipcp_options *ho = &ipcp_hisoptions[f->unit];
+ ipcp_options *go = &ipcp_gotoptions[f->unit];
+ ipcp_options *wo = &ipcp_wantoptions[f->unit];
+
+ np_up(f->unit, PPP_IP);
+ IPCPDEBUG((LOG_INFO, "ipcp: up"));
+
+ /*
+ * We must have a non-zero IP address for both ends of the link.
+ */
+ if (!ho->neg_addr)
+ ho->hisaddr = wo->hisaddr;
+
+ if (ho->hisaddr == 0) {
+ syslog(LOG_ERR, "Could not determine remote IP address");
+ ipcp_close(f->unit, "Could not determine remote IP address");
+ return;
+ }
+ if (go->ouraddr == 0) {
+ syslog(LOG_ERR, "Could not determine local IP address");
+ ipcp_close(f->unit, "Could not determine local IP address");
+ return;
+ }
+
+ /*
+ * 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 %s", ip_ntoa(htonl(cilong)));
+ GETLONG(cilong, p);
+ printer(arg, " %s", ip_ntoa(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 %s", ip_ntoa(htonl(cilong)));
+ }
+ break;
+ case CI_MS_DNS1:
+ case CI_MS_DNS2:
+ p += 2;
+ GETLONG(cilong, p);
+ printer(arg, "dns-addr %s", ip_ntoa(htonl(cilong)));
+ break;
+ case CI_MS_WINS1:
+ case CI_MS_WINS2:
+ p += 2;
+ GETLONG(cilong, p);
+ printer(arg, "wins-addr %s", ip_ntoa(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..90a625a
--- /dev/null
+++ b/usr.sbin/pppd/ipcp.h
@@ -0,0 +1,70 @@
+/*
+ * ipcp.h - IP Control Protocol definitions.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id$
+ */
+
+/*
+ * Options.
+ */
+#define CI_ADDRS 1 /* IP Addresses */
+#define CI_COMPRESSTYPE 2 /* Compression Type */
+#define CI_ADDR 3
+
+#define CI_MS_WINS1 128 /* Primary WINS value */
+#define CI_MS_DNS1 129 /* Primary DNS value */
+#define CI_MS_WINS2 130 /* Secondary WINS value */
+#define CI_MS_DNS2 131 /* Secondary DNS 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..9390643
--- /dev/null
+++ b/usr.sbin/pppd/ipxcp.c
@@ -0,0 +1,1414 @@
+/*
+ * ipxcp.c - PPP IPX Control Protocol.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifdef IPX_CHANGE
+#ifndef lint
+static char rcsid[] = "$Id$";
+#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, "%lx", 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;
+{
+ u_int32_t network;
+ int unit = f->unit;
+
+ 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 unit = f->unit;
+ 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;
+{
+ int len = *lenp;
+ int unit = f->unit;
+/*
+ * 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;
+{
+ int unit = f->unit;
+ 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;
+{
+ int unit = f->unit;
+ 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;
+{
+ int unit = f->unit;
+ 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;
+{
+ int unit = f->unit;
+ u_char *cip, *next; /* Pointer to current and next CIs */
+ u_short cilen, citype; /* Parsed len, type */
+ u_short cishort, ts; /* Parsed short value */
+ u_int32_t tl, cinetwork, outnet;/* 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;
+
+ /*
+ * 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) {
+ u_char *ps;
+ 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;
+{
+ u_int32_t ournn, network;
+
+ 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;
+{
+ int unit = f->unit;
+ char strspeed[32], strlocal[32], strremote[32];
+ char strnetwork[32], strpid[32];
+ char *argv[14], strproto_lcl[32], strproto_rmt[32];
+
+ sprintf (strpid, "%d", getpid());
+ sprintf (strspeed, "%d", baud_rate);
+
+ strproto_lcl[0] = '\0';
+ if (go->neg_router && ((go->router & BIT(IPX_NONE)) == 0)) {
+ if (go->router & BIT(RIP_SAP))
+ strcpy (strproto_lcl, "RIP ");
+ if (go->router & BIT(NLSP))
+ strcat (strproto_lcl, "NLSP ");
+ }
+
+ if (strproto_lcl[0] == '\0')
+ strcpy (strproto_lcl, "NONE ");
+
+ strproto_lcl[strlen (strproto_lcl)-1] = '\0';
+
+ strproto_rmt[0] = '\0';
+ if (ho->neg_router && ((ho->router & BIT(IPX_NONE)) == 0)) {
+ if (ho->router & BIT(RIP_SAP))
+ strcpy (strproto_rmt, "RIP ");
+ if (ho->router & BIT(NLSP))
+ strcat (strproto_rmt, "NLSP ");
+ }
+
+ if (strproto_rmt[0] == '\0')
+ strcpy (strproto_rmt, "NONE ");
+
+ strproto_rmt[strlen (strproto_rmt)-1] = '\0';
+
+ strcpy (strnetwork, ipx_ntoa (go->network));
+
+ sprintf (strlocal,
+ "%02X%02X%02X%02X%02X%02X",
+ NODE(go->our_node));
+
+ sprintf (strremote,
+ "%02X%02X%02X%02X%02X%02X",
+ NODE(ho->his_node));
+
+ argv[0] = script;
+ argv[1] = ifname;
+ argv[2] = devnam;
+ argv[3] = strspeed;
+ argv[4] = strnetwork;
+ argv[5] = strlocal;
+ argv[6] = strremote;
+ argv[7] = strproto_lcl;
+ argv[8] = strproto_rmt;
+ argv[9] = go->name;
+ argv[10] = ho->name;
+ argv[11] = ipparam;
+ argv[12] = strpid;
+ argv[13] = NULL;
+ run_program(script, argv, 0);
+}
+
+/*
+ * ipxcp_printpkt - print the contents of an IPXCP packet.
+ */
+static char *ipxcp_codenames[] = {
+ "ConfReq", "ConfAck", "ConfNak", "ConfRej",
+ "TermReq", "TermAck", "CodeRej"
+};
+
+static int
+ipxcp_printpkt(p, plen, printer, arg)
+ u_char *p;
+ int plen;
+ void (*printer) __P((void *, char *, ...));
+ void *arg;
+{
+ int code, id, len, olen;
+ u_char *pstart, *optend;
+ u_short cishort;
+ u_int32_t cilong;
+
+ if (plen < HEADERLEN)
+ return 0;
+ pstart = p;
+ GETCHAR(code, p);
+ GETCHAR(id, p);
+ GETSHORT(len, p);
+ if (len < HEADERLEN || len > plen)
+ return 0;
+
+ if (code >= 1 && code <= sizeof(ipxcp_codenames) / sizeof(char *))
+ printer(arg, " %s", ipxcp_codenames[code-1]);
+ else
+ printer(arg, " code=0x%x", code);
+ printer(arg, " id=0x%x", id);
+ len -= HEADERLEN;
+ switch (code) {
+ case CONFREQ:
+ case CONFACK:
+ case CONFNAK:
+ case CONFREJ:
+ /* print option list */
+ while (len >= 2) {
+ GETCHAR(code, p);
+ GETCHAR(olen, p);
+ p -= 2;
+ if (olen < CILEN_VOID || olen > len) {
+ break;
+ }
+ printer(arg, " <");
+ len -= olen;
+ optend = p + olen;
+ switch (code) {
+ case IPX_NETWORK_NUMBER:
+ if (olen == CILEN_NETN) {
+ p += 2;
+ GETLONG(cilong, p);
+ printer (arg, "network %s", ipx_ntoa (cilong));
+ }
+ break;
+ case IPX_NODE_NUMBER:
+ if (olen == CILEN_NODEN) {
+ p += 2;
+ printer (arg, "node ");
+ while (p < optend) {
+ GETCHAR(code, p);
+ printer(arg, "%.2x", (int) (unsigned int) (unsigned char) code);
+ }
+ }
+ break;
+ case IPX_COMPRESSION_PROTOCOL:
+ if (olen == CILEN_COMPRESS) {
+ p += 2;
+ GETSHORT (cishort, p);
+ printer (arg, "compression %d", (int) cishort);
+ }
+ break;
+ case IPX_ROUTER_PROTOCOL:
+ if (olen == CILEN_PROTOCOL) {
+ p += 2;
+ GETSHORT (cishort, p);
+ printer (arg, "router proto %d", (int) cishort);
+ }
+ break;
+ case IPX_ROUTER_NAME:
+ if (olen >= CILEN_NAME) {
+ p += 2;
+ printer (arg, "router name \"");
+ while (p < optend) {
+ GETCHAR(code, p);
+ if (code >= 0x20 && code <= 0x7E)
+ printer (arg, "%c", (int) (unsigned int) (unsigned char) code);
+ else
+ printer (arg, " \\%.2x", (int) (unsigned int) (unsigned char) code);
+ }
+ printer (arg, "\"");
+ }
+ break;
+ case IPX_COMPLETE:
+ if (olen == CILEN_COMPLETE) {
+ p += 2;
+ printer (arg, "complete");
+ }
+ break;
+ default:
+ break;
+ }
+
+ while (p < optend) {
+ GETCHAR(code, p);
+ printer(arg, " %.2x", (int) (unsigned int) (unsigned char) code);
+ }
+ printer(arg, ">");
+ }
+ break;
+
+ case TERMACK:
+ case TERMREQ:
+ if (len > 0 && *p >= ' ' && *p < 0x7f) {
+ printer(arg, " ");
+ print_string(p, len, printer, arg);
+ p += len;
+ len = 0;
+ }
+ break;
+ }
+
+ /* print the rest of the bytes in the packet */
+ for (; len > 0; --len) {
+ GETCHAR(code, p);
+ printer(arg, " %.2x", (int) (unsigned int) (unsigned char) code);
+ }
+
+ return p - pstart;
+}
+#endif /* ifdef IPX_CHANGE */
diff --git a/usr.sbin/pppd/ipxcp.h b/usr.sbin/pppd/ipxcp.h
new file mode 100644
index 0000000..139a726
--- /dev/null
+++ b/usr.sbin/pppd/ipxcp.h
@@ -0,0 +1,71 @@
+/*
+ * ipxcp.h - IPX Control Protocol definitions.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id$
+ */
+
+/*
+ * Options.
+ */
+#define IPX_NETWORK_NUMBER 1 /* IPX Network Number */
+#define IPX_NODE_NUMBER 2
+#define IPX_COMPRESSION_PROTOCOL 3
+#define IPX_ROUTER_PROTOCOL 4
+#define IPX_ROUTER_NAME 5
+#define IPX_COMPLETE 6
+
+/* Values for the router protocol */
+#define IPX_NONE 0
+#define RIP_SAP 2
+#define NLSP 4
+
+typedef struct ipxcp_options {
+ int neg_node : 1; /* Negotiate IPX node number? */
+ int req_node : 1; /* Ask peer to send IPX node number? */
+
+ int neg_nn : 1; /* Negotiate IPX network number? */
+ int req_nn : 1; /* Ask peer to send IPX network number */
+
+ int neg_name : 1; /* Negotiate IPX router name */
+ int neg_complete : 1; /* Negotiate completion */
+ int neg_router : 1; /* Negotiate IPX router number */
+
+ int accept_local : 1; /* accept peer's value for ournode */
+ int accept_remote : 1; /* accept peer's value for hisnode */
+ int accept_network : 1; /* accept network number */
+
+ int tried_nlsp : 1; /* I have suggested NLSP already */
+ int tried_rip : 1; /* I have suggested RIP/SAP already */
+
+ u_int32_t his_network; /* base network number */
+ u_int32_t our_network; /* our value for network number */
+ u_int32_t network; /* the final network number */
+
+ u_char his_node[6]; /* peer's node number */
+ u_char our_node[6]; /* our node number */
+ u_char name [48]; /* name of the router */
+ int router; /* routing protocol */
+} ipxcp_options;
+
+extern fsm ipxcp_fsm[];
+extern ipxcp_options ipxcp_wantoptions[];
+extern ipxcp_options ipxcp_gotoptions[];
+extern ipxcp_options ipxcp_allowoptions[];
+extern ipxcp_options ipxcp_hisoptions[];
+
+extern struct protent ipxcp_protent;
diff --git a/usr.sbin/pppd/lcp.c b/usr.sbin/pppd/lcp.c
new file mode 100644
index 0000000..325806b
--- /dev/null
+++ b/usr.sbin/pppd/lcp.c
@@ -0,0 +1,1861 @@
+/*
+ * lcp.c - PPP Link Control Protocol.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef lint
+static char rcsid[] = "$Id$";
+#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, 0x00000000,
+ 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);
+ /*
+ * If the asyncmap hasn't been negotiated, we really should
+ * set the receive asyncmap to ffffffff, but we set it to 0
+ * for backwards contemptibility.
+ */
+ ppp_recv_config(f->unit, (go->neg_mru? MAX(wo->mru, go->mru): PPP_MRU),
+ (go->neg_asyncmap? go->asyncmap: 0x00000000),
+ 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: 0x00000000),
+ 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_echo_lowerup - Start the timer for the LCP frame
+ */
+
+static void
+lcp_echo_lowerup (unit)
+ int unit;
+{
+ fsm *f = &lcp_fsm[unit];
+
+ /* Clear the parameters for generating echo frames */
+ lcp_echos_pending = 0;
+ lcp_echo_number = 0;
+ lcp_echo_timer_running = 0;
+
+ /* If a timeout interval is specified then start the timer */
+ if (lcp_echo_interval != 0)
+ LcpEchoCheck (f);
+}
+
+/*
+ * lcp_echo_lowerdown - Stop the timer for the LCP frame
+ */
+
+static void
+lcp_echo_lowerdown (unit)
+ int unit;
+{
+ fsm *f = &lcp_fsm[unit];
+
+ if (lcp_echo_timer_running != 0) {
+ UNTIMEOUT (LcpEchoTimeout, f);
+ lcp_echo_timer_running = 0;
+ }
+}
diff --git a/usr.sbin/pppd/lcp.h b/usr.sbin/pppd/lcp.h
new file mode 100644
index 0000000..42260f3
--- /dev/null
+++ b/usr.sbin/pppd/lcp.h
@@ -0,0 +1,88 @@
+/*
+ * lcp.h - Link Control Protocol definitions.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id$
+ */
+
+/*
+ * Options.
+ */
+#define CI_MRU 1 /* Maximum Receive Unit */
+#define CI_ASYNCMAP 2 /* Async Control Character Map */
+#define CI_AUTHTYPE 3 /* Authentication Type */
+#define CI_QUALITY 4 /* Quality Protocol */
+#define CI_MAGICNUMBER 5 /* Magic Number */
+#define CI_PCOMPRESSION 7 /* Protocol Field Compression */
+#define CI_ACCOMPRESSION 8 /* Address/Control Field Compression */
+#define CI_CALLBACK 13 /* callback */
+
+/*
+ * LCP-specific packet types.
+ */
+#define PROTREJ 8 /* Protocol Reject */
+#define ECHOREQ 9 /* Echo Request */
+#define ECHOREP 10 /* Echo Reply */
+#define DISCREQ 11 /* Discard Request */
+#define CBCP_OPT 6 /* Use callback control protocol */
+
+/*
+ * The state of options is described by an lcp_options structure.
+ */
+typedef struct lcp_options {
+ int passive : 1; /* Don't die if we don't get a response */
+ int silent : 1; /* Wait for the other end to start first */
+ int restart : 1; /* Restart vs. exit after close */
+ int neg_mru : 1; /* Negotiate the MRU? */
+ int neg_asyncmap : 1; /* Negotiate the async map? */
+ int neg_upap : 1; /* Ask for UPAP authentication? */
+ int neg_chap : 1; /* Ask for CHAP authentication? */
+ int neg_magicnumber : 1; /* Ask for magic number? */
+ int neg_pcompression : 1; /* HDLC Protocol Field Compression? */
+ int neg_accompression : 1; /* HDLC Address/Control Field Compression? */
+ int neg_lqr : 1; /* Negotiate use of Link Quality Reports */
+ int neg_cbcp : 1; /* Negotiate use of CBCP */
+ u_short mru; /* Value of MRU */
+ u_char chap_mdtype; /* which MD type (hashing algorithm) */
+ u_int32_t asyncmap; /* Value of async map */
+ u_int32_t magicnumber;
+ int numloops; /* Number of loops during magic number neg. */
+ u_int32_t lqr_period; /* Reporting period for LQR 1/100ths second */
+} lcp_options;
+
+extern fsm lcp_fsm[];
+extern lcp_options lcp_wantoptions[];
+extern lcp_options lcp_gotoptions[];
+extern lcp_options lcp_allowoptions[];
+extern lcp_options lcp_hisoptions[];
+extern u_int32_t xmit_accm[][8];
+
+#define DEFMRU 1500 /* Try for this */
+#define MINMRU 128 /* No MRUs below this */
+#define MAXMRU 16384 /* Normally limit MRU to this */
+
+void lcp_open __P((int));
+void lcp_close __P((int, char *));
+void lcp_lowerup __P((int));
+void lcp_lowerdown __P((int));
+void lcp_sprotrej __P((int, u_char *, int)); /* send protocol reject */
+
+extern struct protent lcp_protent;
+
+/* Default number of times we receive our magic number from the peer
+ before deciding the link is looped-back. */
+#define DEFLOOPBACKFAIL 10
diff --git a/usr.sbin/pppd/lock.c b/usr.sbin/pppd/lock.c
new file mode 100644
index 0000000..87c2984
--- /dev/null
+++ b/usr.sbin/pppd/lock.c
@@ -0,0 +1,122 @@
+/*
+ * lock.c - lock/unlock the serial device.
+ *
+ * This code is derived from chat.c.
+ */
+
+static char rcsid[] = "$Id$";
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <syslog.h>
+
+#ifdef sun
+# if defined(SUNOS) && SUNOS >= 41
+# ifndef HDB
+# define HDB
+# endif
+# endif
+#endif
+
+#ifndef LOCK_DIR
+# if defined(__NetBSD__) || defined(__FreeBSD__)
+# define PIDSTRING
+# define LOCK_PREFIX "/var/spool/lock/LCK.."
+# else
+# ifdef HDB
+# define PIDSTRING
+# define LOCK_PREFIX "/usr/spool/locks/LCK.."
+# else /* HDB */
+# define LOCK_PREFIX "/usr/spool/uucp/LCK.."
+# endif /* HDB */
+# endif
+#endif /* LOCK_DIR */
+
+static char *lock_file;
+
+/*
+ * Create a lock file for the named lock device
+ */
+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 */
+#ifdef PIDSTRING
+ n = read(fd, hdb_lock_buffer, 11);
+ if (n > 0) {
+ hdb_lock_buffer[n] = 0;
+ pid = atoi(hdb_lock_buffer);
+ }
+#else
+ n = read(fd, &pid, sizeof(pid));
+#endif
+ if (n <= 0) {
+ syslog(LOG_ERR, "Can't read pid from lock file %s", lock_file);
+ close(fd);
+ } else {
+ 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;
+ }
+
+# ifdef PIDSTRING
+ sprintf(hdb_lock_buffer, "%10d\n", getpid());
+ write(fd, hdb_lock_buffer, 11);
+# else
+ pid = getpid();
+ write(fd, &pid, sizeof pid);
+# endif
+
+ close(fd);
+ return 0;
+}
+
+/*
+ * Remove our lockfile
+ */
+unlock()
+{
+ if (lock_file) {
+ unlink(lock_file);
+ free(lock_file);
+ lock_file = NULL;
+ }
+}
+
diff --git a/usr.sbin/pppd/magic.c b/usr.sbin/pppd/magic.c
new file mode 100644
index 0000000..be3b503
--- /dev/null
+++ b/usr.sbin/pppd/magic.c
@@ -0,0 +1,87 @@
+/*
+ * magic.c - PPP Magic Number routines.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef lint
+static char rcsid[] = "$Id$";
+#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 = gethostid() ^ t.tv_sec ^ t.tv_usec ^ getpid();
+ srand48(seed);
+}
+
+/*
+ * magic - Returns the next magic number.
+ */
+u_int32_t
+magic()
+{
+ return (u_int32_t) mrand48();
+}
+
+#ifdef NO_DRAND48
+/*
+ * Substitute procedures for those systems which don't have
+ * drand48 et al.
+ */
+
+double
+drand48()
+{
+ return (double)random() / (double)0x7fffffffL; /* 2**31-1 */
+}
+
+long
+mrand48()
+{
+ return random();
+}
+
+void
+srand48(seedval)
+long seedval;
+{
+ srandom((int)seedval);
+}
+
+#endif
diff --git a/usr.sbin/pppd/magic.h b/usr.sbin/pppd/magic.h
new file mode 100644
index 0000000..1344626
--- /dev/null
+++ b/usr.sbin/pppd/magic.h
@@ -0,0 +1,23 @@
+/*
+ * magic.h - PPP Magic Number definitions.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id$
+ */
+
+void magic_init __P((void)); /* Initialize the magic number generator */
+u_int32_t magic __P((void)); /* Returns the next magic number */
diff --git a/usr.sbin/pppd/main.c b/usr.sbin/pppd/main.c
new file mode 100644
index 0000000..a54c22e
--- /dev/null
+++ b/usr.sbin/pppd/main.c
@@ -0,0 +1,1598 @@
+/*
+ * main.c - Point-to-Point Protocol main module
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef lint
+static char rcsid[] = "$Id: main.c,v 1.14 1997/08/22 12:03:55 peter Exp $";
+#endif
+
+#include <stdio.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <signal.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <syslog.h>
+#include <netdb.h>
+#include <utmp.h>
+#include <pwd.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <net/if.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[IFNAMSIZ]; /* 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 phase; /* where the link is at */
+int kill_link;
+int open_ccp_flag;
+int redirect_stderr; /* Connector's stderr should go to file */
+
+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 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, nonblock, fdflags;
+ struct sigaction sa;
+ FILE *pidfile;
+ FILE *iffile;
+ char *p;
+ struct passwd *pw;
+ struct timeval timo;
+ sigset_t mask;
+ struct protent *protp;
+ struct stat statbuf;
+ int connect_attempts = 0;
+
+ phase = PHASE_INITIALIZE;
+ p = ttyname(0);
+ if (p)
+ strcpy(devnam, p);
+ strcpy(default_devnam, devnam);
+
+ /* 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;
+
+ /*
+ * 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);
+ }
+
+ /*
+ * 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;
+ redirect_stderr = !nodetach || default_device;
+
+ /*
+ * 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 (!default_device && !nodetach && daemon(0, 0) < 0) {
+ perror("Couldn't detach from controlling terminal");
+ exit(1);
+ }
+ 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);
+
+ /* write pid to file */
+ (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;
+ }
+
+ /*
+ * 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.; set CLOCAL for now */
+ 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);
+
+ /* write pid to file */
+ (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 */
+ 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;
+ }
+ }
+
+ /*
+ * Start opening the connection and wait for
+ * incoming events (reply, timeout, etc.).
+ */
+ syslog(LOG_NOTICE, "Connect: %s <--> %s", ifname, devnam);
+ lcp_lowerup(0);
+ lcp_open(0); /* Start protocol */
+ for (phase = PHASE_ESTABLISH; phase != PHASE_DEAD; ) {
+ wait_input(timeleft(&timo));
+ calltimeout();
+ get_input();
+ if (kill_link) {
+ lcp_close(0, "User request");
+ kill_link = 0;
+ }
+ if (open_ccp_flag) {
+ if (phase == PHASE_NETWORK) {
+ ccp_fsm[0].flags = OPT_RESTART; /* clears OPT_SILENT */
+ (*ccp_protent.open)(0);
+ }
+ open_ccp_flag = 0;
+ }
+ reap_kids(); /* Don't leave dead kids lying around */
+ }
+
+ /*
+ * If we may want to bring the link up again, transfer
+ * the ppp unit back to the loopback. Set the
+ * real serial device back to its normal mode of operation.
+ */
+ clean_check();
+ if (demand)
+ restore_loop();
+ disestablish_ppp(ttyfd);
+
+ /*
+ * Run disconnector script, if requested.
+ * XXX we may not be able to do this if the line has hung up!
+ */
+ if (disconnector && !hungup) {
+ set_up_tty(ttyfd, 1);
+ if (device_script(disconnector, ttyfd, ttyfd) < 0) {
+ syslog(LOG_WARNING, "disconnect script failed");
+ } else {
+ syslog(LOG_INFO, "Serial link disconnected.");
+ }
+ }
+
+ fail:
+ if (ttyfd >= 0)
+ close_tty();
+ if (locked) {
+ unlock();
+ locked = 0;
+ }
+
+ if (!demand) {
+ if (pidfilename[0] != 0
+ && unlink(pidfilename) < 0 && errno != ENOENT)
+ syslog(LOG_WARNING, "unable to delete pid file: %m");
+ pidfilename[0] = 0;
+
+ if (iffile)
+ if (unlink(iffilename) < 0 && errno != ENOENT)
+ syslog(LOG_WARNING, "unable to delete if file: %m");
+ iffilename[0] = 0;
+ }
+
+ /* limit to retries? */
+ if (max_con_attempts)
+ if (connect_attempts >= max_con_attempts)
+ break;
+
+ if (!persist)
+ break;
+
+ 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;
+}
+
+/*
+ * 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;
+{
+ 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 (redirect_stderr) {
+ close(2);
+ errfd = open(_PATH_CONNERRS, O_WRONLY | O_APPEND | O_CREAT, 0644);
+ 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;
+ char *nullenv[1];
+
+ 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 */
+ nullenv[0] = NULL;
+ execve(prog, args, nullenv);
+ 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
+
+ vsprintf(buf, fmt, pvar);
+ va_end(pvar);
+
+ n = strlen(buf);
+ 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;
+}
diff --git a/usr.sbin/pppd/options.c b/usr.sbin/pppd/options.c
new file mode 100644
index 0000000..0fc7ab0
--- /dev/null
+++ b/usr.sbin/pppd/options.c
@@ -0,0 +1,2535 @@
+/*
+ * options.c - handles option processing for PPP.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef lint
+static char rcsid[] = "$Id: options.c,v 1.15 1997/10/10 06:02:56 peter Exp $";
+#endif
+
+#include <ctype.h>
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <limits.h>
+#include <stdlib.h>
+#include <termios.h>
+#include <syslog.h>
+#include <string.h>
+#include <netdb.h>
+#include <pwd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#ifdef PPP_FILTER
+#include <pcap.h>
+#include <pcap-int.h> /* XXX: To get struct pcap */
+#endif
+
+#include "pppd.h"
+#include "pathnames.h"
+#include "patchlevel.h"
+#include "fsm.h"
+#include "lcp.h"
+#include "ipcp.h"
+#include "upap.h"
+#include "chap.h"
+#include "ccp.h"
+#ifdef CBCP_SUPPORT
+#include "cbcp.h"
+#endif
+
+#ifdef IPX_CHANGE
+#include "ipxcp.h"
+#endif /* IPX_CHANGE */
+
+#include <net/ppp_comp.h>
+
+#define FALSE 0
+#define TRUE 1
+
+#if defined(ultrix) || defined(NeXT)
+char *strdup __P((char *));
+#endif
+
+#ifndef GIDSET_TYPE
+#define GIDSET_TYPE gid_t
+#endif
+
+/*
+ * Option variables and default values.
+ */
+#ifdef PPP_FILTER
+int dflag = 0; /* Tell libpcap we want debugging */
+#endif
+int debug = 0; /* Debug flag */
+int kdebugflag = 0; /* Tell kernel to print debug messages */
+int default_device = 1; /* Using /dev/tty or equivalent */
+char devnam[MAXPATHLEN] = "/dev/tty"; /* Device name */
+int crtscts = 0; /* Use hardware flow control */
+int modem = 1; /* Use modem control lines */
+int inspeed = 0; /* Input/Output speed requested */
+u_int32_t netmask = 0; /* IP netmask to set on interface */
+int lockflag = 0; /* Create lock file to lock the serial dev */
+int nodetach = 0; /* Don't detach from controlling tty */
+char *connector = NULL; /* Script to establish physical link */
+char *disconnector = NULL; /* Script to disestablish physical link */
+char *welcomer = NULL; /* Script to run after phys link estab. */
+int max_con_attempts = 0; /* Maximum connect tries in non-demand mode */
+int maxconnect = 0; /* Maximum connect time */
+char user[MAXNAMELEN]; /* Username for PAP */
+char passwd[MAXSECRETLEN]; /* Password for PAP */
+int auth_required = 0; /* Peer is required to authenticate */
+int defaultroute = 0; /* assign default route through interface */
+int proxyarp = 0; /* Set up proxy ARP entry for peer */
+int persist = 0; /* Reopen link after it goes down */
+int uselogin = 0; /* Use /etc/passwd for checking PAP */
+int lcp_echo_interval = 0; /* Interval between LCP echo-requests */
+int lcp_echo_fails = 0; /* Tolerance to unanswered echo-requests */
+char our_name[MAXNAMELEN]; /* Our name for authentication purposes */
+char remote_name[MAXNAMELEN]; /* Peer's name for authentication */
+int explicit_remote = 0; /* User specified explicit remote name */
+int usehostname = 0; /* Use hostname for our_name */
+int disable_defaultip = 0; /* Don't use hostname for default IP adrs */
+int demand = 0; /* do dial-on-demand */
+char *ipparam = NULL; /* Extra parameter for ip up/down scripts */
+int cryptpap; /* Passwords in pap-secrets are encrypted */
+int idle_time_limit = 0; /* Disconnect if idle for this many seconds */
+int holdoff = 30; /* # seconds to pause before reconnecting */
+int refuse_pap = 0; /* Set to say we won't do PAP */
+int refuse_chap = 0; /* Set to say we won't do CHAP */
+
+#ifdef MSLANMAN
+int ms_lanman = 0; /* Nonzero if use LanMan password instead of NT */
+ /* Has meaning only with MS-CHAP challenges */
+#endif
+
+struct option_info auth_req_info;
+struct option_info connector_info;
+struct option_info disconnector_info;
+struct option_info welcomer_info;
+struct option_info devnam_info;
+#ifdef PPP_FILTER
+struct bpf_program pass_filter;/* Filter program for packets to pass */
+struct bpf_program active_filter; /* Filter program for link-active pkts */
+pcap_t pc; /* Fake struct pcap so we can compile expr */
+#endif
+
+/*
+ * Prototypes
+ */
+static int setdevname __P((char *, int));
+static int setspeed __P((char *));
+static int setdebug __P((char **));
+static int setkdebug __P((char **));
+static int setpassive __P((char **));
+static int setsilent __P((char **));
+static int noopt __P((char **));
+static int setnovj __P((char **));
+static int setnovjccomp __P((char **));
+static int setvjslots __P((char **));
+static int reqpap __P((char **));
+static int nopap __P((char **));
+#ifdef OLD_OPTIONS
+static int setupapfile __P((char **));
+#endif
+static int nochap __P((char **));
+static int reqchap __P((char **));
+static int noaccomp __P((char **));
+static int noasyncmap __P((char **));
+static int noip __P((char **));
+static int nomagicnumber __P((char **));
+static int setasyncmap __P((char **));
+static int setescape __P((char **));
+static int setmru __P((char **));
+static int setmtu __P((char **));
+#ifdef CBCP_SUPPORT
+static int setcbcp __P((char **));
+#endif
+static int nomru __P((char **));
+static int nopcomp __P((char **));
+static int setconnector __P((char **));
+static int setdisconnector __P((char **));
+static int setwelcomer __P((char **));
+static int setmaxcon __P((char **));
+static int setmaxconnect __P((char **));
+static int setdomain __P((char **));
+static int setnetmask __P((char **));
+static int setcrtscts __P((char **));
+static int setnocrtscts __P((char **));
+static int setxonxoff __P((char **));
+static int setnodetach __P((char **));
+static int 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 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((void));
+#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 */
+ {"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 */
+ {"predictor1", 0, setpred1comp}, /* request Predictor-1 */
+ {"nopredictor1", 0, setnopred1comp},/* don't allow Predictor-1 */
+ {"-predictor1", 0, setnopred1comp}, /* don't allow Predictor-1 */
+ {"ipparam", 1, setipparam}, /* set ip script parameter */
+ {"papcrypt", 0, setpapcrypt}, /* PAP passwords encrypted */
+ {"idle", 1, setidle}, /* idle time limit (seconds) */
+ {"holdoff", 1, setholdoff}, /* set holdoff time (seconds) */
+/* backwards compat hack */
+ {"dns1", 1, setdnsaddr}, /* DNS address for the peer's use */
+ {"dns2", 1, setdnsaddr}, /* DNS address for the peer's use */
+/* end compat hack */
+ {"ms-dns", 1, setdnsaddr}, /* DNS address for the peer's use */
+ {"ms-wins", 1, setwinsaddr}, /* Nameserver for SMB over TCP/IP for peer */
+ {"noipx", 0, resetipxproto}, /* Disable IPXCP (and IPX) */
+ {"-ipx", 0, resetipxproto}, /* Disable IPXCP (and IPX) */
+ {"--version", 0, showversion}, /* Show version number */
+ {"--help", 0, showhelp}, /* Show brief listing of options */
+ {"-h", 0, showhelp}, /* ditto */
+
+#ifdef PPP_FILTER
+ {"pdebug", 1, setpdebug}, /* libpcap debugging */
+ {"pass-filter", 1, setpassfilter}, /* set filter for packets to pass */
+ {"active-filter", 1, setactivefilter}, /* set filter for active pkts */
+#endif
+
+#ifdef IPX_CHANGE
+ {"ipx-network", 1, setipxnetwork}, /* IPX network number */
+ {"ipxcp-accept-network", 0, setipxanet}, /* Accept peer netowrk */
+ {"ipx-node", 1, setipxnode}, /* IPX node number */
+ {"ipxcp-accept-local", 0, setipxalcl}, /* Accept our address */
+ {"ipxcp-accept-remote", 0, setipxarmt}, /* Accept peer's address */
+ {"ipx-routing", 1, setipxrouter}, /* IPX routing proto number */
+ {"ipx-router-name", 1, setipxname}, /* IPX router name */
+ {"ipxcp-restart", 1, setipxcptimeout}, /* Set timeout for IPXCP */
+ {"ipxcp-max-terminate", 1, setipxcpterm}, /* max #xmits for term-reqs */
+ {"ipxcp-max-configure", 1, setipxcpconf}, /* max #xmits for conf-reqs */
+ {"ipxcp-max-failure", 1, setipxcpfails}, /* max #conf-naks for IPXCP */
+#if 0
+ {"ipx-compression", 1, setipxcompression}, /* IPX compression number */
+#endif
+ {"ipx", 0, setipxproto}, /* Enable IPXCP (and IPX) */
+ {"+ipx", 0, setipxproto}, /* Enable IPXCP (and IPX) */
+#endif /* IPX_CHANGE */
+
+#ifdef MSLANMAN
+ {"ms-lanman", 0, setmslanman}, /* Use LanMan psswd when using MS-CHAP */
+#endif
+
+ {NULL, 0, NULL}
+};
+
+
+#ifndef IMPLEMENTATION
+#define IMPLEMENTATION ""
+#endif
+
+static char *usage_string = "\
+pppd version %s patch level %d%s\n\
+Usage: %s [ options ], where options are:\n\
+ <device> Communicate over the named device\n\
+ <speed> Set the baud rate to <speed>\n\
+ <loc>:<rem> Set the local and/or remote interface IP\n\
+ addresses. Either one may be omitted.\n\
+ asyncmap <n> Set the desired async map to hex <n>\n\
+ auth Require authentication from peer\n\
+ connect <p> Invoke shell command <p> to set up the serial line\n\
+ crtscts Use hardware RTS/CTS flow control\n\
+ defaultroute Add default route through interface\n\
+ file <f> Take options from file <f>\n\
+ modem Use modem control lines\n\
+ mru <n> Set MRU value to <n> for negotiation\n\
+See pppd(8) for more options.\n\
+";
+
+static char *current_option; /* the name of the option being parsed */
+static int privileged_option; /* set iff the current option came from root */
+static char *option_source; /* string saying where the option came from */
+
+/*
+ * parse_args - parse a string of arguments from the command line.
+ */
+int
+parse_args(argc, argv)
+ int argc;
+ char **argv;
+{
+ char *arg;
+ struct cmd *cmdp;
+ int ret;
+
+ privileged_option = privileged;
+ option_source = "command line";
+ while (argc > 0) {
+ arg = *argv++;
+ --argc;
+
+ /*
+ * First see if it's a command.
+ */
+ for (cmdp = cmds; cmdp->cmd_name; cmdp++)
+ if (!strcmp(arg, cmdp->cmd_name))
+ break;
+
+ if (cmdp->cmd_name != NULL) {
+ if (argc < cmdp->num_args) {
+ option_error("too few parameters for option %s", arg);
+ return 0;
+ }
+ current_option = arg;
+ if (!(*cmdp->cmd_func)(argv))
+ return 0;
+ argc -= cmdp->num_args;
+ argv += cmdp->num_args;
+
+ } else {
+ /*
+ * Maybe a tty name, speed or IP address?
+ */
+ if ((ret = setdevname(arg, 0)) == 0
+ && (ret = setspeed(arg)) == 0
+ && (ret = setipaddr(arg)) == 0) {
+ option_error("unrecognized option '%s'", arg);
+ usage();
+ return 0;
+ }
+ if (ret < 0) /* error */
+ return 0;
+ }
+ }
+ return 1;
+}
+
+/*
+ * scan_args - scan the command line arguments to get the tty name,
+ * if specified.
+ */
+void
+scan_args(argc, argv)
+ int argc;
+ char **argv;
+{
+ char *arg;
+ struct cmd *cmdp;
+
+ while (argc > 0) {
+ arg = *argv++;
+ --argc;
+
+ /* Skip options and their arguments */
+ for (cmdp = cmds; cmdp->cmd_name; cmdp++)
+ if (!strcmp(arg, cmdp->cmd_name))
+ break;
+
+ if (cmdp->cmd_name != NULL) {
+ argc -= cmdp->num_args;
+ argv += cmdp->num_args;
+ continue;
+ }
+
+ /* Check if it's a tty name and copy it if so */
+ (void) setdevname(arg, 1);
+ }
+}
+
+/*
+ * usage - print out a message telling how to use the program.
+ */
+void
+usage()
+{
+ if (phase == PHASE_INITIALIZE)
+ fprintf(stderr, usage_string, VERSION, PATCHLEVEL, IMPLEMENTATION,
+ progname);
+}
+
+/*
+ * showhelp - print out usage message and exit.
+ */
+static int
+showhelp(argv)
+ char **argv;
+{
+ if (phase == PHASE_INITIALIZE) {
+ usage();
+ exit(0);
+ }
+ return 0;
+}
+
+/*
+ * showversion - print out the version number and exit.
+ */
+static int
+showversion(argv)
+ char **argv;
+{
+ if (phase == PHASE_INITIALIZE) {
+ fprintf(stderr, "pppd version %s patch level %d%s\n",
+ VERSION, PATCHLEVEL, IMPLEMENTATION);
+ exit(0);
+ }
+ return 0;
+}
+
+/*
+ * options_from_file - Read a string of options from a file,
+ * and interpret them.
+ */
+int
+options_from_file(filename, must_exist, check_prot, priv)
+ char *filename;
+ int must_exist;
+ int check_prot;
+ int priv;
+{
+ FILE *f;
+ int i, newline, ret;
+ struct cmd *cmdp;
+ int oldpriv;
+ char *argv[MAXARGS];
+ char args[MAXARGS][MAXWORDLEN];
+ char cmd[MAXWORDLEN];
+
+ if ((f = fopen(filename, "r")) == NULL) {
+ if (!must_exist && errno == ENOENT)
+ return 1;
+ option_error("Can't open options file %s: %m", filename);
+ return 0;
+ }
+ if (check_prot && !readable(fileno(f))) {
+ option_error("Can't open options file %s: access denied", filename);
+ fclose(f);
+ return 0;
+ }
+
+ oldpriv = privileged_option;
+ privileged_option = priv;
+ ret = 0;
+ while (getword(f, cmd, &newline, filename)) {
+ /*
+ * First see if it's a command.
+ */
+ for (cmdp = cmds; cmdp->cmd_name; cmdp++)
+ if (!strcmp(cmd, cmdp->cmd_name))
+ break;
+
+ if (cmdp->cmd_name != NULL) {
+ for (i = 0; i < cmdp->num_args; ++i) {
+ if (!getword(f, args[i], &newline, filename)) {
+ option_error(
+ "In file %s: too few parameters for option '%s'",
+ filename, cmd);
+ goto err;
+ }
+ argv[i] = args[i];
+ }
+ current_option = cmd;
+ if (!(*cmdp->cmd_func)(argv))
+ goto err;
+
+ } else {
+ /*
+ * Maybe a tty name, speed or IP address?
+ */
+ if ((i = setdevname(cmd, 0)) == 0
+ && (i = setspeed(cmd)) == 0
+ && (i = setipaddr(cmd)) == 0) {
+ option_error("In file %s: unrecognized option '%s'",
+ filename, cmd);
+ goto err;
+ }
+ if (i < 0) /* error */
+ goto err;
+ }
+ }
+ ret = 1;
+
+err:
+ fclose(f);
+ privileged_option = oldpriv;
+ return ret;
+}
+
+/*
+ * options_from_user - See if the use has a ~/.ppprc file,
+ * and if so, interpret options from it.
+ */
+int
+options_from_user()
+{
+ char *user, *path, *file;
+ int ret;
+ struct passwd *pw;
+
+ pw = getpwuid(getuid());
+ if (pw == NULL || (user = pw->pw_dir) == NULL || user[0] == 0)
+ return 1;
+ file = _PATH_USEROPT;
+ path = malloc(strlen(user) + strlen(file) + 2);
+ if (path == NULL)
+ novm("init file name");
+ strcpy(path, user);
+ strcat(path, "/");
+ strcat(path, file);
+ ret = options_from_file(path, 0, 1, privileged);
+ free(path);
+ return ret;
+}
+
+/*
+ * options_for_tty - See if an options file exists for the serial
+ * device, and if so, interpret options from it.
+ */
+int
+options_for_tty()
+{
+ char *dev, *path, *p;
+ int ret;
+
+ dev = devnam;
+ if (strncmp(dev, "/dev/", 5) == 0)
+ dev += 5;
+ if (strcmp(dev, "tty") == 0)
+ return 1; /* don't look for /etc/ppp/options.tty */
+ path = malloc(strlen(_PATH_TTYOPT) + strlen(dev) + 1);
+ if (path == NULL)
+ novm("tty init file name");
+ strcpy(path, _PATH_TTYOPT);
+ /* Turn slashes into dots, for Solaris case (e.g. /dev/term/a) */
+ for (p = path + strlen(path); *dev != 0; ++dev)
+ *p++ = (*dev == '/'? '.': *dev);
+ *p = 0;
+ ret = options_from_file(path, 0, 0, 1);
+ free(path);
+ return ret;
+}
+
+/*
+ * option_error - print a message about an error in an option.
+ * The message is logged, and also sent to
+ * stderr if phase == PHASE_INITIALIZE.
+ */
+void
+option_error __V((char *fmt, ...))
+{
+ va_list args;
+ char buf[256];
+
+#if __STDC__
+ va_start(args, fmt);
+#else
+ char *fmt;
+ va_start(args);
+ fmt = va_arg(args, char *);
+#endif
+ vfmtmsg(buf, sizeof(buf), fmt, args);
+ va_end(args);
+ if (phase == PHASE_INITIALIZE)
+ fprintf(stderr, "%s: %s\n", progname, buf);
+ syslog(LOG_ERR, "%s", buf);
+}
+
+/*
+ * readable - check if a file is readable by the real user.
+ */
+static int
+readable(fd)
+ int fd;
+{
+ uid_t uid;
+ int ngroups, i;
+ struct stat sbuf;
+ GIDSET_TYPE groups[NGROUPS_MAX];
+
+ uid = getuid();
+ if (uid == 0)
+ return 1;
+ if (fstat(fd, &sbuf) != 0)
+ return 0;
+ if (sbuf.st_uid == uid)
+ return sbuf.st_mode & S_IRUSR;
+ if (sbuf.st_gid == getgid())
+ return sbuf.st_mode & S_IRGRP;
+ ngroups = getgroups(NGROUPS_MAX, groups);
+ for (i = 0; i < ngroups; ++i)
+ if (sbuf.st_gid == groups[i])
+ return sbuf.st_mode & S_IRGRP;
+ return sbuf.st_mode & S_IROTH;
+}
+
+/*
+ * Read a word from a file.
+ * Words are delimited by white-space or by quotes (" or ').
+ * Quotes, white-space and \ may be escaped with \.
+ * \<newline> is ignored.
+ */
+int
+getword(f, word, newlinep, filename)
+ FILE *f;
+ char *word;
+ int *newlinep;
+ char *filename;
+{
+ int c, len, escape;
+ int quoted, comment;
+ int value, digit, got, n;
+
+#define isoctal(c) ((c) >= '0' && (c) < '8')
+
+ *newlinep = 0;
+ len = 0;
+ escape = 0;
+ comment = 0;
+
+ /*
+ * First skip white-space and comments.
+ */
+ for (;;) {
+ c = getc(f);
+ if (c == EOF)
+ break;
+
+ /*
+ * A newline means the end of a comment; backslash-newline
+ * is ignored. Note that we cannot have escape && comment.
+ */
+ if (c == '\n') {
+ if (!escape) {
+ *newlinep = 1;
+ comment = 0;
+ } else
+ escape = 0;
+ continue;
+ }
+
+ /*
+ * Ignore characters other than newline in a comment.
+ */
+ if (comment)
+ continue;
+
+ /*
+ * If this character is escaped, we have a word start.
+ */
+ if (escape)
+ break;
+
+ /*
+ * If this is the escape character, look at the next character.
+ */
+ if (c == '\\') {
+ escape = 1;
+ continue;
+ }
+
+ /*
+ * If this is the start of a comment, ignore the rest of the line.
+ */
+ if (c == '#') {
+ comment = 1;
+ continue;
+ }
+
+ /*
+ * A non-whitespace character is the start of a word.
+ */
+ if (!isspace(c))
+ break;
+ }
+
+ /*
+ * Save the delimiter for quoted strings.
+ */
+ if (!escape && (c == '"' || c == '\'')) {
+ quoted = c;
+ c = getc(f);
+ } else
+ quoted = 0;
+
+ /*
+ * Process characters until the end of the word.
+ */
+ while (c != EOF) {
+ if (escape) {
+ /*
+ * This character is escaped: backslash-newline is ignored,
+ * various other characters indicate particular values
+ * as for C backslash-escapes.
+ */
+ escape = 0;
+ if (c == '\n') {
+ c = getc(f);
+ continue;
+ }
+
+ got = 0;
+ switch (c) {
+ case 'a':
+ value = '\a';
+ break;
+ case 'b':
+ value = '\b';
+ break;
+ case 'f':
+ value = '\f';
+ break;
+ case 'n':
+ value = '\n';
+ break;
+ case 'r':
+ value = '\r';
+ break;
+ case 's':
+ value = ' ';
+ break;
+ case 't':
+ value = '\t';
+ break;
+
+ default:
+ if (isoctal(c)) {
+ /*
+ * \ddd octal sequence
+ */
+ value = 0;
+ for (n = 0; n < 3 && isoctal(c); ++n) {
+ value = (value << 3) + (c & 07);
+ c = getc(f);
+ }
+ got = 1;
+ break;
+ }
+
+ if (c == 'x') {
+ /*
+ * \x<hex_string> sequence
+ */
+ value = 0;
+ c = getc(f);
+ for (n = 0; n < 2 && isxdigit(c); ++n) {
+ digit = toupper(c) - '0';
+ if (digit > 10)
+ digit += '0' + 10 - 'A';
+ value = (value << 4) + digit;
+ c = getc (f);
+ }
+ got = 1;
+ break;
+ }
+
+ /*
+ * Otherwise the character stands for itself.
+ */
+ value = c;
+ break;
+ }
+
+ /*
+ * Store the resulting character for the escape sequence.
+ */
+ if (len < MAXWORDLEN-1)
+ word[len] = value;
+ ++len;
+
+ if (!got)
+ c = getc(f);
+ continue;
+
+ }
+
+ /*
+ * Not escaped: see if we've reached the end of the word.
+ */
+ if (quoted) {
+ if (c == quoted)
+ break;
+ } else {
+ if (isspace(c) || c == '#') {
+ ungetc (c, f);
+ break;
+ }
+ }
+
+ /*
+ * Backslash starts an escape sequence.
+ */
+ if (c == '\\') {
+ escape = 1;
+ c = getc(f);
+ continue;
+ }
+
+ /*
+ * An ordinary character: store it in the word and get another.
+ */
+ if (len < MAXWORDLEN-1)
+ word[len] = c;
+ ++len;
+
+ c = getc(f);
+ }
+
+ /*
+ * End of the word: check for errors.
+ */
+ if (c == EOF) {
+ if (ferror(f)) {
+ if (errno == 0)
+ errno = EIO;
+ option_error("Error reading %s: %m", filename);
+ die(1);
+ }
+ /*
+ * If len is zero, then we didn't find a word before the
+ * end of the file.
+ */
+ if (len == 0)
+ return 0;
+ }
+
+ /*
+ * Warn if the word was too long, and append a terminating null.
+ */
+ if (len >= MAXWORDLEN) {
+ option_error("warning: word in file %s too long (%.20s...)",
+ filename, word);
+ len = MAXWORDLEN - 1;
+ }
+ word[len] = 0;
+
+ return 1;
+
+#undef isoctal
+
+}
+
+/*
+ * number_option - parse an unsigned numeric parameter for an option.
+ */
+static int
+number_option(str, valp, base)
+ char *str;
+ u_int32_t *valp;
+ int base;
+{
+ char *ptr;
+
+ *valp = strtoul(str, &ptr, base);
+ if (ptr == str) {
+ option_error("invalid numeric parameter '%s' for %s option",
+ str, current_option);
+ return 0;
+ }
+ return 1;
+}
+
+
+/*
+ * int_option - like number_option, but valp is int *,
+ * the base is assumed to be 0, and *valp is not changed
+ * if there is an error.
+ */
+static int
+int_option(str, valp)
+ char *str;
+ int *valp;
+{
+ u_int32_t v;
+
+ if (!number_option(str, &v, 0))
+ return 0;
+ *valp = (int) v;
+ return 1;
+}
+
+
+/*
+ * The following procedures parse options.
+ */
+
+/*
+ * readfile - take commands from a file.
+ */
+static int
+readfile(argv)
+ char **argv;
+{
+ return options_from_file(*argv, 1, 1, privileged_option);
+}
+
+/*
+ * callfile - take commands from /etc/ppp/peers/<name>.
+ * Name may not contain /../, start with / or ../, or end in /..
+ */
+static int
+callfile(argv)
+ char **argv;
+{
+ char *fname, *arg, *p;
+ int l, ok;
+
+ arg = *argv;
+ ok = 1;
+ if (arg[0] == '/' || arg[0] == 0)
+ ok = 0;
+ else {
+ for (p = arg; *p != 0; ) {
+ if (p[0] == '.' && p[1] == '.' && (p[2] == '/' || p[2] == 0)) {
+ ok = 0;
+ break;
+ }
+ while (*p != '/' && *p != 0)
+ ++p;
+ if (*p == '/')
+ ++p;
+ }
+ }
+ if (!ok) {
+ option_error("call option value may not contain .. or start with /");
+ return 0;
+ }
+
+ l = strlen(arg) + strlen(_PATH_PEERFILES) + 1;
+ if ((fname = (char *) malloc(l)) == NULL)
+ novm("call file name");
+ strcpy(fname, _PATH_PEERFILES);
+ strcat(fname, arg);
+
+ ok = options_from_file(fname, 1, 1, 1);
+
+ free(fname);
+ return ok;
+}
+
+
+/*
+ * setdebug - Set debug (command line argument).
+ */
+static int
+setdebug(argv)
+ char **argv;
+{
+ debug++;
+ return (1);
+}
+
+/*
+ * setkdebug - Set kernel debugging level.
+ */
+static int
+setkdebug(argv)
+ char **argv;
+{
+ return int_option(*argv, &kdebugflag);
+}
+
+#ifdef PPP_FILTER
+/*
+ * setpdebug - Set libpcap debugging level.
+ */
+static int
+setpdebug(argv)
+ char **argv;
+{
+ return int_option(*argv, &dflag);
+}
+
+/*
+ * setpassfilter - Set the pass filter for packets
+ */
+static int
+setpassfilter(argv)
+ char **argv;
+{
+ pc.linktype = DLT_PPP;
+ pc.snapshot = PPP_HDRLEN;
+
+ if (pcap_compile(&pc, &pass_filter, *argv, 1, netmask) == 0)
+ return 1;
+ option_error("error in pass-filter expression: %s\n", pcap_geterr(&pc));
+ return 0;
+}
+
+/*
+ * setactivefilter - Set the active filter for packets
+ */
+static int
+setactivefilter(argv)
+ char **argv;
+{
+ pc.linktype = DLT_PPP;
+ pc.snapshot = PPP_HDRLEN;
+
+ if (pcap_compile(&pc, &active_filter, *argv, 1, netmask) == 0)
+ return 1;
+ option_error("error in active-filter expression: %s\n", pcap_geterr(&pc));
+ return 0;
+}
+#endif
+
+/*
+ * noopt - Disable all options.
+ */
+static int
+noopt(argv)
+ char **argv;
+{
+ BZERO((char *) &lcp_wantoptions[0], sizeof (struct lcp_options));
+ BZERO((char *) &lcp_allowoptions[0], sizeof (struct lcp_options));
+ BZERO((char *) &ipcp_wantoptions[0], sizeof (struct ipcp_options));
+ BZERO((char *) &ipcp_allowoptions[0], sizeof (struct ipcp_options));
+
+#ifdef IPX_CHANGE
+ BZERO((char *) &ipxcp_wantoptions[0], sizeof (struct ipxcp_options));
+ BZERO((char *) &ipxcp_allowoptions[0], sizeof (struct ipxcp_options));
+#endif /* IPX_CHANGE */
+
+ return (1);
+}
+
+/*
+ * noaccomp - Disable Address/Control field compression negotiation.
+ */
+static int
+noaccomp(argv)
+ char **argv;
+{
+ lcp_wantoptions[0].neg_accompression = 0;
+ lcp_allowoptions[0].neg_accompression = 0;
+ return (1);
+}
+
+
+/*
+ * noasyncmap - Disable async map negotiation.
+ */
+static int
+noasyncmap(argv)
+ char **argv;
+{
+ lcp_wantoptions[0].neg_asyncmap = 0;
+ lcp_allowoptions[0].neg_asyncmap = 0;
+ return (1);
+}
+
+
+/*
+ * noip - Disable IP and IPCP.
+ */
+static int
+noip(argv)
+ char **argv;
+{
+ ipcp_protent.enabled_flag = 0;
+ return (1);
+}
+
+
+/*
+ * nomagicnumber - Disable magic number negotiation.
+ */
+static int
+nomagicnumber(argv)
+ char **argv;
+{
+ lcp_wantoptions[0].neg_magicnumber = 0;
+ lcp_allowoptions[0].neg_magicnumber = 0;
+ return (1);
+}
+
+
+/*
+ * nomru - Disable mru negotiation.
+ */
+static int
+nomru(argv)
+ char **argv;
+{
+ lcp_wantoptions[0].neg_mru = 0;
+ lcp_allowoptions[0].neg_mru = 0;
+ return (1);
+}
+
+
+/*
+ * setmru - Set MRU for negotiation.
+ */
+static int
+setmru(argv)
+ char **argv;
+{
+ u_int32_t mru;
+
+ if (!number_option(*argv, &mru, 0))
+ return 0;
+ lcp_wantoptions[0].mru = mru;
+ lcp_wantoptions[0].neg_mru = 1;
+ return (1);
+}
+
+
+/*
+ * setmru - Set the largest MTU we'll use.
+ */
+static int
+setmtu(argv)
+ char **argv;
+{
+ u_int32_t mtu;
+
+ if (!number_option(*argv, &mtu, 0))
+ return 0;
+ if (mtu < MINMRU || mtu > MAXMRU) {
+ option_error("mtu option value of %u is too %s", mtu,
+ (mtu < MINMRU? "small": "large"));
+ return 0;
+ }
+ lcp_allowoptions[0].mru = mtu;
+ return (1);
+}
+
+#ifdef CBCP_SUPPORT
+static int
+setcbcp(argv)
+ char **argv;
+{
+ lcp_wantoptions[0].neg_cbcp = 1;
+ cbcp_protent.enabled_flag = 1;
+ cbcp[0].us_number = strdup(*argv);
+ if (cbcp[0].us_number == 0)
+ novm("callback number");
+ cbcp[0].us_type |= (1 << CB_CONF_USER);
+ cbcp[0].us_type |= (1 << CB_CONF_ADMIN);
+ return (1);
+}
+#endif
+
+/*
+ * nopcomp - Disable Protocol field compression negotiation.
+ */
+static int
+nopcomp(argv)
+ char **argv;
+{
+ lcp_wantoptions[0].neg_pcompression = 0;
+ lcp_allowoptions[0].neg_pcompression = 0;
+ return (1);
+}
+
+
+/*
+ * setpassive - Set passive mode (don't give up if we time out sending
+ * LCP configure-requests).
+ */
+static int
+setpassive(argv)
+ char **argv;
+{
+ lcp_wantoptions[0].passive = 1;
+ return (1);
+}
+
+
+/*
+ * setsilent - Set silent mode (don't start sending LCP configure-requests
+ * until we get one from the peer).
+ */
+static int
+setsilent(argv)
+ char **argv;
+{
+ lcp_wantoptions[0].silent = 1;
+ return 1;
+}
+
+
+/*
+ * nopap - Disable PAP authentication with peer.
+ */
+static int
+nopap(argv)
+ char **argv;
+{
+ refuse_pap = 1;
+ return (1);
+}
+
+
+/*
+ * reqpap - Require PAP authentication from peer.
+ */
+static int
+reqpap(argv)
+ char **argv;
+{
+ lcp_wantoptions[0].neg_upap = 1;
+ setauth(NULL);
+ return 1;
+}
+
+#if OLD_OPTIONS
+/*
+ * setupapfile - specifies UPAP info for authenticating with peer.
+ */
+static int
+setupapfile(argv)
+ char **argv;
+{
+ FILE * ufile;
+ int l;
+
+ lcp_allowoptions[0].neg_upap = 1;
+
+ /* open user info file */
+ if ((ufile = fopen(*argv, "r")) == NULL) {
+ option_error("unable to open user login data file %s", *argv);
+ return 0;
+ }
+ if (!readable(fileno(ufile))) {
+ option_error("%s: access denied", *argv);
+ return 0;
+ }
+ check_access(ufile, *argv);
+
+ /* get username */
+ if (fgets(user, MAXNAMELEN - 1, ufile) == NULL
+ || fgets(passwd, MAXSECRETLEN - 1, ufile) == NULL){
+ option_error("unable to read user login data file %s", *argv);
+ return 0;
+ }
+ fclose(ufile);
+
+ /* get rid of newlines */
+ l = strlen(user);
+ if (l > 0 && user[l-1] == '\n')
+ user[l-1] = 0;
+ l = strlen(passwd);
+ if (l > 0 && passwd[l-1] == '\n')
+ passwd[l-1] = 0;
+
+ return (1);
+}
+#endif
+
+/*
+ * nochap - Disable CHAP authentication with peer.
+ */
+static int
+nochap(argv)
+ char **argv;
+{
+ refuse_chap = 1;
+ return (1);
+}
+
+
+/*
+ * reqchap - Require CHAP authentication from peer.
+ */
+static int
+reqchap(argv)
+ char **argv;
+{
+ lcp_wantoptions[0].neg_chap = 1;
+ setauth(NULL);
+ return (1);
+}
+
+
+/*
+ * setnovj - disable vj compression
+ */
+static int
+setnovj(argv)
+ char **argv;
+{
+ ipcp_wantoptions[0].neg_vj = 0;
+ ipcp_allowoptions[0].neg_vj = 0;
+ return (1);
+}
+
+
+/*
+ * setnovjccomp - disable VJ connection-ID compression
+ */
+static int
+setnovjccomp(argv)
+ char **argv;
+{
+ ipcp_wantoptions[0].cflag = 0;
+ ipcp_allowoptions[0].cflag = 0;
+ return 1;
+}
+
+
+/*
+ * setvjslots - set maximum number of connection slots for VJ compression
+ */
+static int
+setvjslots(argv)
+ char **argv;
+{
+ int value;
+
+ if (!int_option(*argv, &value))
+ return 0;
+ if (value < 2 || value > 16) {
+ option_error("vj-max-slots value must be between 2 and 16");
+ return 0;
+ }
+ ipcp_wantoptions [0].maxslotindex =
+ ipcp_allowoptions[0].maxslotindex = value - 1;
+ return 1;
+}
+
+
+/*
+ * setconnector - Set a program to connect to a serial line
+ */
+static int
+setconnector(argv)
+ char **argv;
+{
+ connector = strdup(*argv);
+ if (connector == NULL)
+ novm("connect script");
+ connector_info.priv = privileged_option;
+ connector_info.source = option_source;
+
+ return (1);
+}
+
+/*
+ * setdisconnector - Set a program to disconnect from the serial line
+ */
+static int
+setdisconnector(argv)
+ char **argv;
+{
+ disconnector = strdup(*argv);
+ if (disconnector == NULL)
+ novm("disconnect script");
+ disconnector_info.priv = privileged_option;
+ disconnector_info.source = option_source;
+
+ return (1);
+}
+
+/*
+ * setwelcomer - Set a program to welcome a client after connection
+ */
+static int
+setwelcomer(argv)
+ char **argv;
+{
+ welcomer = strdup(*argv);
+ if (welcomer == NULL)
+ novm("welcome script");
+ welcomer_info.priv = privileged_option;
+ welcomer_info.source = option_source;
+
+ return (1);
+}
+
+static int
+setmaxcon(argv)
+ char **argv;
+{
+ return int_option(*argv, &max_con_attempts);
+}
+
+/*
+ * setmaxconnect - Set the maximum connect time
+ */
+static int
+setmaxconnect(argv)
+ char **argv;
+{
+ int value;
+
+ if (!int_option(*argv, &value))
+ return 0;
+ if (value < 0) {
+ option_error("maxconnect time must be positive");
+ return 0;
+ }
+ if (maxconnect > 0 && (value == 0 || value > maxconnect)) {
+ option_error("maxconnect time cannot be increased");
+ return 0;
+ }
+ maxconnect = value;
+ return 1;
+}
+
+/*
+ * setdomain - Set domain name to append to hostname
+ */
+static int
+setdomain(argv)
+ char **argv;
+{
+ if (!privileged_option) {
+ option_error("using the domain option requires root privilege");
+ return 0;
+ }
+ gethostname(hostname, MAXNAMELEN);
+ if (**argv != 0) {
+ if (**argv != '.')
+ strncat(hostname, ".", MAXNAMELEN - strlen(hostname));
+ strncat(hostname, *argv, MAXNAMELEN - strlen(hostname));
+ }
+ hostname[MAXNAMELEN-1] = 0;
+ return (1);
+}
+
+
+/*
+ * setasyncmap - add bits to asyncmap (what we request peer to escape).
+ */
+static int
+setasyncmap(argv)
+ char **argv;
+{
+ u_int32_t asyncmap;
+
+ if (!number_option(*argv, &asyncmap, 16))
+ return 0;
+ lcp_wantoptions[0].asyncmap |= asyncmap;
+ lcp_wantoptions[0].neg_asyncmap = 1;
+ return(1);
+}
+
+
+/*
+ * setescape - add chars to the set we escape on transmission.
+ */
+static int
+setescape(argv)
+ char **argv;
+{
+ int n, ret;
+ char *p, *endp;
+
+ p = *argv;
+ ret = 1;
+ while (*p) {
+ n = strtol(p, &endp, 16);
+ if (p == endp) {
+ option_error("escape parameter contains invalid hex number '%s'",
+ p);
+ return 0;
+ }
+ p = endp;
+ if (n < 0 || (0x20 <= n && n <= 0x3F) || n == 0x5E || n > 0xFF) {
+ option_error("can't escape character 0x%x", n);
+ ret = 0;
+ } else
+ xmit_accm[0][n >> 5] |= 1 << (n & 0x1F);
+ while (*p == ',' || *p == ' ')
+ ++p;
+ }
+ return ret;
+}
+
+
+/*
+ * setspeed - Set the speed.
+ */
+static int
+setspeed(arg)
+ char *arg;
+{
+ char *ptr;
+ int spd;
+
+ spd = strtol(arg, &ptr, 0);
+ if (ptr == arg || *ptr != 0 || spd == 0)
+ return 0;
+ inspeed = spd;
+ return 1;
+}
+
+
+/*
+ * setdevname - Set the device name.
+ */
+static int
+setdevname(cp, quiet)
+ char *cp;
+ int quiet;
+{
+ struct stat statbuf;
+ char dev[MAXPATHLEN];
+
+ if (*cp == 0)
+ return 0;
+
+ if (strncmp("/dev/", cp, 5) != 0) {
+ strcpy(dev, "/dev/");
+ strncat(dev, cp, MAXPATHLEN - 5);
+ dev[MAXPATHLEN-1] = 0;
+ cp = dev;
+ }
+
+ /*
+ * Check if there is a device by this name.
+ */
+ if (stat(cp, &statbuf) < 0) {
+ if (errno == ENOENT || quiet)
+ return 0;
+ option_error("Couldn't stat %s: %m", cp);
+ return -1;
+ }
+
+ (void) strncpy(devnam, cp, MAXPATHLEN);
+ devnam[MAXPATHLEN-1] = 0;
+ default_device = FALSE;
+ devnam_info.priv = privileged_option;
+ devnam_info.source = option_source;
+
+ return 1;
+}
+
+
+/*
+ * setipaddr - Set the IP address
+ */
+int
+setipaddr(arg)
+ char *arg;
+{
+ struct hostent *hp;
+ char *colon;
+ u_int32_t local, remote;
+ ipcp_options *wo = &ipcp_wantoptions[0];
+
+ /*
+ * IP address pair separated by ":".
+ */
+ if ((colon = strchr(arg, ':')) == NULL)
+ return 0;
+
+ /*
+ * If colon first character, then no local addr.
+ */
+ if (colon != arg) {
+ *colon = '\0';
+ if ((local = inet_addr(arg)) == -1) {
+ if ((hp = gethostbyname(arg)) == NULL) {
+ option_error("unknown host: %s", arg);
+ return -1;
+ } else {
+ local = *(u_int32_t *)hp->h_addr;
+ }
+ }
+ if (bad_ip_adrs(local)) {
+ option_error("bad local IP address %s", ip_ntoa(local));
+ return -1;
+ }
+ if (local != 0)
+ wo->ouraddr = local;
+ *colon = ':';
+ }
+
+ /*
+ * If colon last character, then no remote addr.
+ */
+ if (*++colon != '\0') {
+ if ((remote = inet_addr(colon)) == -1) {
+ if ((hp = gethostbyname(colon)) == NULL) {
+ option_error("unknown host: %s", colon);
+ return -1;
+ } else {
+ remote = *(u_int32_t *)hp->h_addr;
+ if (remote_name[0] == 0) {
+ strncpy(remote_name, colon, MAXNAMELEN);
+ remote_name[MAXNAMELEN-1] = 0;
+ }
+ }
+ }
+ if (bad_ip_adrs(remote)) {
+ option_error("bad remote IP address %s", ip_ntoa(remote));
+ return -1;
+ }
+ if (remote != 0)
+ wo->hisaddr = remote;
+ }
+
+ return 1;
+}
+
+
+/*
+ * setnoipdflt - disable setipdefault()
+ */
+static int
+setnoipdflt(argv)
+ char **argv;
+{
+ disable_defaultip = 1;
+ return 1;
+}
+
+
+/*
+ * setipcpaccl - accept peer's idea of our address
+ */
+static int
+setipcpaccl(argv)
+ char **argv;
+{
+ ipcp_wantoptions[0].accept_local = 1;
+ return 1;
+}
+
+
+/*
+ * setipcpaccr - accept peer's idea of its address
+ */
+static int
+setipcpaccr(argv)
+ char **argv;
+{
+ ipcp_wantoptions[0].accept_remote = 1;
+ return 1;
+}
+
+
+/*
+ * setnetmask - set the netmask to be used on the interface.
+ */
+static int
+setnetmask(argv)
+ char **argv;
+{
+ struct in_addr mask;
+
+ if ((inet_aton(*argv, &mask)) == -1 || (netmask & ~mask.s_addr)) {
+ fprintf(stderr, "Invalid netmask %s\n", *argv);
+ return (0);
+ }
+
+ netmask = mask.s_addr;
+ return (1);
+}
+
+static int
+setcrtscts(argv)
+ char **argv;
+{
+ crtscts = 1;
+ return (1);
+}
+
+static int
+setnocrtscts(argv)
+ char **argv;
+{
+ crtscts = -1;
+ return (1);
+}
+
+static int
+setxonxoff(argv)
+ char **argv;
+{
+ lcp_wantoptions[0].asyncmap |= 0x000A0000; /* escape ^S and ^Q */
+ lcp_wantoptions[0].neg_asyncmap = 1;
+
+ crtscts = -2;
+ return (1);
+}
+
+static int
+setnodetach(argv)
+ char **argv;
+{
+ nodetach = 1;
+ return (1);
+}
+
+static int
+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;
+ return 1;
+}
+
+static int
+setnodeflate(argv)
+ char **argv;
+{
+ ccp_wantoptions[0].deflate = 0;
+ ccp_allowoptions[0].deflate = 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 (ipcp_allowoptions[0].dnsaddr[0] == 0) {
+ ipcp_allowoptions[0].dnsaddr[0] = dns;
+ } else {
+ 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 (ipcp_allowoptions[0].winsaddr[0] == 0) {
+ ipcp_allowoptions[0].winsaddr[0] = wins;
+ } else {
+ 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;
+}
+
+static int
+setipxalcl(argv)
+ char **argv;
+{
+ ipxcp_wantoptions[0].accept_local = 1;
+ ipxcp_allowoptions[0].accept_local = 1;
+}
+
+static int
+setipxarmt(argv)
+ char **argv;
+{
+ ipxcp_wantoptions[0].accept_remote = 1;
+ ipxcp_allowoptions[0].accept_remote = 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()
+{
+ 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..d0e64b8
--- /dev/null
+++ b/usr.sbin/pppd/patchlevel.h
@@ -0,0 +1,6 @@
+/* $Id: patchlevel.h,v 1.6 1997/08/19 17:52:44 peter Exp $ */
+#define PATCHLEVEL 1
+
+#define VERSION "2.3"
+#define IMPLEMENTATION ""
+#define DATE "27 June 97"
diff --git a/usr.sbin/pppd/pathnames.h b/usr.sbin/pppd/pathnames.h
new file mode 100644
index 0000000..896fed6
--- /dev/null
+++ b/usr.sbin/pppd/pathnames.h
@@ -0,0 +1,32 @@
+/*
+ * define path names
+ *
+ * $Id$
+ */
+
+#ifdef HAVE_PATHS_H
+#include <paths.h>
+
+#else
+#define _PATH_VARRUN "/etc/ppp/"
+#define _PATH_DEVNULL "/dev/null"
+#endif
+
+#define _PATH_UPAPFILE "/etc/ppp/pap-secrets"
+#define _PATH_CHAPFILE "/etc/ppp/chap-secrets"
+#define _PATH_SYSOPTIONS "/etc/ppp/options"
+#define _PATH_IPUP "/etc/ppp/ip-up"
+#define _PATH_IPDOWN "/etc/ppp/ip-down"
+#define _PATH_AUTHUP "/etc/ppp/auth-up"
+#define _PATH_AUTHDOWN "/etc/ppp/auth-down"
+#define _PATH_TTYOPT "/etc/ppp/options."
+#define _PATH_CONNERRS "/etc/ppp/connect-errors"
+#define _PATH_USEROPT ".ppprc"
+#define _PATH_PEERFILES "/etc/ppp/peers/"
+#define _PATH_PPPDENY "/etc/ppp/ppp.deny"
+#define _PATH_PPPSHELLS "/etc/ppp/ppp.shells"
+
+#ifdef IPX_CHANGE
+#define _PATH_IPXUP "/etc/ppp/ipx-up"
+#define _PATH_IPXDOWN "/etc/ppp/ipx-down"
+#endif /* IPX_CHANGE */
diff --git a/usr.sbin/pppd/ppp.h b/usr.sbin/pppd/ppp.h
new file mode 100644
index 0000000..4cb1e70
--- /dev/null
+++ b/usr.sbin/pppd/ppp.h
@@ -0,0 +1,41 @@
+/*
+ * ppp.h - PPP global declarations.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id$
+ */
+
+#ifndef __PPP_H__
+#define __PPP_H__
+
+#define NPPP 1 /* One PPP interface supported (per process) */
+
+/*
+ * Data Link Layer header = Address, Control, Protocol.
+ */
+#define ALLSTATIONS 0xff /* All-Stations Address */
+#define UI 0x03 /* Unnumbered Information */
+#define LCP 0xc021 /* Link Control Protocol */
+#define IPCP 0x8021 /* IP Control Protocol */
+#define UPAP 0xc023 /* User/Password Authentication Protocol */
+#define CHAP 0xc223 /* Crytpographic Handshake Protocol */
+#define LQR 0xc025 /* Link Quality Report protocol */
+#define IP_VJ_COMP 0x002d /* VJ TCP compressed IP packet */
+#define DLLHEADERLEN (sizeof (u_char) + sizeof (u_char) + sizeof (u_short))
+#define MTU 1500 /* Default MTU */
+
+#endif /* __PPP_H__ */
diff --git a/usr.sbin/pppd/pppd.8 b/usr.sbin/pppd/pppd.8
new file mode 100644
index 0000000..57bffe6
--- /dev/null
+++ b/usr.sbin/pppd/pppd.8
@@ -0,0 +1,1158 @@
+.\" manual page [] for pppd 2.3
+.\" $Id: pppd.8,v 1.16 1997/10/10 09:28:38 peter Exp $
+.\" SH section heading
+.\" SS subsection heading
+.\" LP paragraph
+.\" IP indented paragraph
+.\" TP hanging label
+.TH PPPD 8
+.SH NAME
+pppd \- Point to Point Protocol daemon
+.SH SYNOPSIS
+.B pppd
+[
+.I tty_name
+] [
+.I speed
+] [
+.I options
+]
+.SH DESCRIPTION
+.LP
+The Point-to-Point Protocol (PPP) provides a method for transmitting
+datagrams over serial point-to-point links. PPP
+is composed of three parts: a method for encapsulating datagrams over
+serial links, an extensible Link Control Protocol (LCP), and
+a family of Network Control Protocols (NCP) for establishing
+and configuring different network-layer protocols.
+.LP
+The encapsulation scheme is provided by driver code in the kernel.
+Pppd provides the basic LCP, authentication support, and an NCP for
+establishing and configuring the Internet Protocol (IP) (called the IP
+Control Protocol, IPCP).
+.SH FREQUENTLY USED OPTIONS
+.TP
+.I <tty_name>
+Communicate over the named device. The string "/dev/" is prepended if
+necessary. If no device name is given, or if the name of the terminal
+connected to the standard input is given, pppd
+will use that terminal, and will not fork to put itself in the
+background. This option is privileged if the \fInoauth\fR option is
+used.
+.TP
+.I <speed>
+Set the baud rate to <speed> (a decimal number). On systems such as
+4.4BSD and NetBSD, any speed can be specified, providing that it is
+supported by the serial device driver. Other systems
+(e.g. SunOS, Linux) allow only a limited set of speeds.
+.TP
+.B active-filter \fIfilter-expression
+Specifies a packet filter to be applied to data packets to determine
+which packets are to be regarded as link activity, and therefore reset
+the idle timer, or cause the link to be brought up in demand-dialling
+mode. This option is useful in conjunction with the
+\fBidle\fR option if there are packets being sent or received
+regularly over the link (for example, routing information packets)
+which would otherwise prevent the link from ever appearing to be idle.
+The \fIfilter-expression\fR syntax is as described for tcpdump(1),
+except that qualifiers which are inappropriate for a PPP link, such as
+\fBether\fR and \fBarp\fR, are not permitted. Generally the filter
+expression should be enclosed in single-quotes to prevent whitespace
+in the expression from being interpreted by the shell. This option
+only available
+if both the kernel and pppd were compiled with PPP_FILTER defined.
+.TP
+.B asyncmap \fI<map>
+Set the async character map to <map>. This map describes which
+control characters cannot be successfully received over the serial
+line. Pppd will ask the peer to send these characters as a 2-byte
+escape sequence. The argument is a 32 bit hex number with each bit
+representing a character to escape. Bit 0 (00000001) represents the
+character 0x00; bit 31 (80000000) represents the character 0x1f or ^_.
+If multiple \fIasyncmap\fR options are given, the values are ORed
+together. If no \fIasyncmap\fR option is given, no async character
+map will be negotiated for the receive direction; the peer should then
+escape \fIall\fR control characters. To escape transmitted
+characters, use the \fIescape\fR option.
+.TP
+.B auth
+Require the peer to authenticate itself before allowing network
+packets to be sent or received.
+.TP
+.B call \fIname
+Read options from the file /etc/ppp/peers/\fIname\fR. This file may
+contain privileged options, such as \fInoauth\fR, even if pppd
+is not being run by root. The \fIname\fR string may not begin with /
+or include .. as a pathname component. The format of the options file
+is described below.
+.TP
+.B connect \fIscript
+Use the executable or shell command specified by \fIscript\fR to set
+up the serial line. This script would typically use the chat(8)
+program to dial the modem and start the remote ppp session. This
+option is privileged if the \fInoauth\fR option is used.
+.TP
+.B connect-max-attempts \fI<n>
+Attempt dial-out connection to remote system no more than specified number
+of times (default = 1). If the connection is not made, pppd will exit.
+Requires that \fBpersist\fR has been specified.
+.TP
+.B crtscts
+Use hardware flow control (i.e. RTS/CTS) to control the flow of data
+on the serial port. If neither the \fIcrtscts\fR nor the
+\fInocrtscts\fR option is given, the hardware flow control setting
+for the serial port is left unchanged.
+.TP
+.B defaultroute
+Add a default route to the system routing tables, using the peer as
+the gateway, when IPCP negotiation is successfully completed.
+This entry is removed when the PPP connection is broken. This option
+is privileged if the \fInodefaultroute\fR option has been specified.
+.TP
+.B disconnect \fIscript
+Run the executable or shell command specified by \fIscript\fR after
+pppd has terminated the link. This script could, for example, issue
+commands to the modem to cause it to hang up if hardware modem control
+signals were not available. The disconnect script is not run if the
+modem has already hung up. This option is privileged if the
+\fInoauth\fR option is used.
+.TP
+.B escape \fIxx,yy,...
+Specifies that certain characters should be escaped on transmission
+(regardless of whether the peer requests them to be escaped with its
+async control character map). The characters to be escaped are
+specified as a list of hex numbers separated by commas. Note that
+almost any character can be specified for the \fIescape\fR option,
+unlike the \fIasyncmap\fR option which only allows control characters
+to be specified. The characters which may not be escaped are those
+with hex values 0x20 - 0x3f or 0x5e.
+.TP
+.B file \fIname
+Read options from file \fIname\fR (the format is described below).
+The file must be readable by the user who has invoked pppd.
+.TP
+.B lock
+Specifies that pppd should create a UUCP-style lock file for the
+serial device to ensure exclusive access to the device.
+.TP
+.B mru \fIn
+Set the MRU [Maximum Receive Unit] value to \fIn\fR. Pppd
+will ask the peer to send packets of no more than \fIn\fR bytes. The
+minimum MRU value is 128. The default MRU value is 1500. A value of
+296 is recommended for slow links (40 bytes for TCP/IP header + 256
+bytes of data).
+.TP
+.B mtu \fIn
+Set the MTU [Maximum Transmit Unit] value to \fIn\fR. Unless the
+peer requests a smaller value via MRU negotiation, pppd will
+request that the kernel networking code send data packets of no more
+than \fIn\fR bytes through the PPP network interface.
+.TP
+.B passive
+Enables the "passive" option in the LCP. With this option, pppd will
+attempt to initiate a connection; if no reply is received from the
+peer, pppd will then just wait passively for a valid LCP packet from
+the peer, instead of exiting, as it would without this option.
+.SH OPTIONS
+.TP
+.I <local_IP_address>\fB:\fI<remote_IP_address>
+Set the local and/or remote interface IP addresses. Either one may be
+omitted. The IP addresses can be specified with a host name or in
+decimal dot notation (e.g. 150.234.56.78). The default local
+address is the (first) IP address of the system (unless the
+\fInoipdefault\fR
+option is given). The remote address will be obtained from the peer
+if not specified in any option. Thus, in simple cases, this option is
+not required. If a local and/or remote IP address is specified with
+this option, pppd
+will not accept a different value from the peer in the IPCP
+negotiation, unless the \fIipcp-accept-local\fR and/or
+\fIipcp-accept-remote\fR options are given, respectively.
+.TP
+.B bsdcomp \fInr,nt
+Request that the peer compress packets that it sends, using the
+BSD-Compress scheme, with a maximum code size of \fInr\fR bits, and
+agree to compress packets sent to the peer with a maximum code size of
+\fInt\fR bits. If \fInt\fR is not specified, it defaults to the value
+given for \fInr\fR. Values in the range 9 to 15 may be used for
+\fInr\fR and \fInt\fR; larger values give better compression but
+consume more kernel memory for compression dictionaries.
+Alternatively, a value of 0 for \fInr\fR or \fInt\fR disables
+compression in the corresponding direction. Use \fInobsdcomp\fR or
+\fIbsdcomp 0\fR to disable BSD-Compress compression entirely.
+.TP
+.B chap-interval \fIn
+If this option is given, pppd will rechallenge the peer every \fIn\fR
+seconds.
+.TP
+.B chap-max-challenge \fIn
+Set the maximum number of CHAP challenge transmissions to \fIn\fR
+(default 10).
+.TP
+.B chap-restart \fIn
+Set the CHAP restart interval (retransmission timeout for challenges)
+to \fIn\fR seconds (default 3).
+.TP
+.B debug
+Enables connection debugging facilities.
+If this option is given, pppd will log the contents of all
+control packets sent or received in a readable form. The packets are
+logged through syslog with facility \fIdaemon\fR and level
+\fIdebug\fR. This information can be directed to a file by setting up
+/etc/syslog.conf appropriately (see syslog.conf(5)).
+.TP
+.B default-asyncmap
+Disable asyncmap negotiation, forcing all control characters to be
+escaped for both the transmit and the receive direction.
+.TP
+.B default-mru
+Disable MRU [Maximum Receive Unit] negotiation. With this option,
+pppd will use the default MRU value of 1500 bytes for both the
+transmit and receive direction.
+.TP
+.B deflate \fInr,nt
+Request that the peer compress packets that it sends, using the
+Deflate scheme, with a maximum window size of \fI2**nr\fR bytes, and
+agree to compress packets sent to the peer with a maximum window size
+of \fI2**nt\fR bytes. If \fInt\fR is not specified, it defaults to
+the value given for \fInr\fR. Values in the range 8 to 15 may be used
+for \fInr\fR and \fInt\fR; larger values give better compression but
+consume more kernel memory for compression dictionaries.
+Alternatively, a value of 0 for \fInr\fR or \fInt\fR disables
+compression in the corresponding direction. Use \fInodeflate\fR or
+\fIdeflate 0\fR to disable Deflate compression entirely. (Note: pppd
+requests Deflate compression in preference to BSD-Compress if the peer
+can do either.)
+.TP
+.B demand
+Initiate the link only on demand, i.e. when data traffic is present.
+With this option, the remote IP address must be specified by the user
+on the command line or in an options file. Pppd will initially
+configure the interface and enable it for IP traffic without
+connecting to the peer. When traffic is available, pppd will
+connect to the peer and perform negotiation, authentication, etc.
+When this is completed, pppd will commence passing data packets
+(i.e., IP packets) across the link.
+
+The \fIdemand\fR option implies the \fIpersist\fR option. If this
+behaviour is not desired, use the \fInopersist\fR option after the
+\fIdemand\fR option. The \fIidle\fR and \fIholdoff\fR
+options are also useful in conjuction with the \fIdemand\fR option.
+.TP
+.B domain \fId
+Append the domain name \fId\fR to the local host name for authentication
+purposes. For example, if gethostname() returns the name porsche, but
+the fully qualified domain name is porsche.Quotron.COM, you could
+specify \fIdomain Quotron.COM\fR. Pppd would then use the name
+\fIporsche.Quotron.COM\fR for looking up secrets in the secrets file,
+and as the default name to send to the peer when authenticating itself
+to the peer. This option is privileged.
+.TP
+.B holdoff \fIn
+Specifies how many seconds to wait before re-initiating the link after
+it terminates. This option only has any effect if the \fIpersist\fR
+or \fIdemand\fR option is used. The holdoff period is not applied if
+the link was terminated because it was idle.
+.TP
+.B idle \fIn
+Specifies that pppd should disconnect if the link is idle for \fIn\fR
+seconds. The link is idle when no data packets (i.e. IP packets) are
+being sent or received. Note: it is not advisable to use this option
+with the \fIpersist\fR option without the \fIdemand\fR option.
+If the \fBactive-filter\fR
+option is given, data packets which are rejected by the specified
+activity filter also count as the link being idle.
+.TP
+.B ipcp-accept-local
+With this option, pppd will accept the peer's idea of our local IP
+address, even if the local IP address was specified in an option.
+.TP
+.B ipcp-accept-remote
+With this option, pppd will accept the peer's idea of its (remote) IP
+address, even if the remote IP address was specified in an option.
+.TP
+.B ipcp-max-configure \fIn
+Set the maximum number of IPCP configure-request transmissions to
+\fIn\fR (default 10).
+.TP
+.B ipcp-max-failure \fIn
+Set the maximum number of IPCP configure-NAKs returned before starting
+to send configure-Rejects instead to \fIn\fR (default 10).
+.TP
+.B ipcp-max-terminate \fIn
+Set the maximum number of IPCP terminate-request transmissions to
+\fIn\fR (default 3).
+.TP
+.B ipcp-restart \fIn
+Set the IPCP restart interval (retransmission timeout) to \fIn\fR
+seconds (default 3).
+.TP
+.B ipparam \fIstring
+Provides an extra parameter to the ip-up and ip-down scripts. If this
+option is given, the \fIstring\fR supplied is given as the 6th
+parameter to those scripts.
+.TP
+.B ipx
+Enable the IPXCP and IPX protocols. This option is presently only
+supported under Linux, and only if your kernel has been configured to
+include IPX support.
+.TP
+.B ipx-network \fIn
+Set the IPX network number in the IPXCP configure request frame to
+\fIn\fR, a hexadecimal number (without a leading 0x). There is no
+valid default. If this option is not specified, the network number is
+obtained from the peer. If the peer does not have the network number,
+the IPX protocol will not be started.
+.TP
+.B ipx-node \fIn\fB:\fIm
+Set the IPX node numbers. The two node numbers are separated from each
+other with a colon character. The first number \fIn\fR is the local
+node number. The second number \fIm\fR is the peer's node number. Each
+node number is a hexadecimal number, at most 10 digits long. The node
+numbers on the ipx-network must be unique. There is no valid
+default. If this option is not specified then the node numbers are
+obtained from the peer.
+.TP
+.B ipx-router-name \fI<string>
+Set the name of the router. This is a string and is sent to the peer
+as information data.
+.TP
+.B ipx-routing \fIn
+Set the routing protocol to be received by this option. More than one
+instance of \fIipx-routing\fR may be specified. The '\fInone\fR'
+option (0) may be specified as the only instance of ipx-routing. The
+values may be \fI0\fR for \fINONE\fR, \fI2\fR for \fIRIP/SAP\fR, and
+\fI4\fR for \fINLSP\fR.
+.TP
+.B ipxcp-accept-local
+Accept the peer's NAK for the node number specified in the ipx-node
+option. If a node number was specified, and non-zero, the default is
+to insist that the value be used. If you include this option then you
+will permit the peer to override the entry of the node number.
+.TP
+.B ipxcp-accept-network
+Accept the peer's NAK for the network number specified in the
+ipx-network option. If a network number was specified, and non-zero, the
+default is to insist that the value be used. If you include this
+option then you will permit the peer to override the entry of the node
+number.
+.TP
+.B ipxcp-accept-remote
+Use the peer's network number specified in the configure request
+frame. If a node number was specified for the peer and this option was
+not specified, the peer will be forced to use the value which you have
+specified.
+.TP
+.B ipxcp-max-configure \fIn
+Set the maximum number of IPXCP configure request frames which the
+system will send to \fIn\fR. The default is 10.
+.TP
+.B ipxcp-max-failure \fIn
+Set the maximum number of IPXCP NAK frames which the local system will
+send before it rejects the options. The default value is 3.
+.TP
+.B ipxcp-max-terminate \fIn
+Set the maximum nuber of IPXCP terminate request frames before the
+local system considers that the peer is not listening to them. The
+default value is 3.
+.TP
+.B kdebug \fIn
+Enable debugging code in the kernel-level PPP driver. The argument
+\fIn\fR is a number which is the sum of the following values: 1 to
+enable general debug messages, 2 to request that the contents of
+received packets be printed, and 4 to request that the contents of
+transmitted packets be printed. On most systems, messages printed by
+the kernel are logged by syslog(1) to a file as directed in the
+/etc/syslog.conf configuration file.
+.TP
+.B lcp-echo-failure \fIn
+If this option is given, pppd will presume the peer to be dead
+if \fIn\fR LCP echo-requests are sent without receiving a valid LCP
+echo-reply. If this happens, pppd will terminate the
+connection. Use of this option requires a non-zero value for the
+\fIlcp-echo-interval\fR parameter. This option can be used to enable
+pppd to terminate after the physical connection has been broken
+(e.g., the modem has hung up) in situations where no hardware modem
+control lines are available.
+.TP
+.B lcp-echo-interval \fIn
+If this option is given, pppd will send an LCP echo-request frame to
+the peer every \fIn\fR seconds. Normally the peer should respond to
+the echo-request by sending an echo-reply. This option can be used
+with the \fIlcp-echo-failure\fR option to detect that the peer is no
+longer connected.
+.TP
+.B lcp-max-configure \fIn
+Set the maximum number of LCP configure-request transmissions to
+\fIn\fR (default 10).
+.TP
+.B lcp-max-failure \fIn
+Set the maximum number of LCP configure-NAKs returned before starting
+to send configure-Rejects instead to \fIn\fR (default 10).
+.TP
+.B lcp-max-terminate \fIn
+Set the maximum number of LCP terminate-request transmissions to
+\fIn\fR (default 3).
+.TP
+.B lcp-restart \fIn
+Set the LCP restart interval (retransmission timeout) to \fIn\fR
+seconds (default 3).
+.TP
+.B local
+Don't use the modem control lines. With this option, pppd will ignore
+the state of the CD (Carrier Detect) signal from the modem and will
+not change the state of the DTR (Data Terminal Ready) signal.
+.TP
+.B login
+Use the system password database for authenticating the peer using
+PAP, and record the user in the system wtmp file. Note that the peer
+must have an entry in the /etc/ppp/pap-secrets file as well as the
+system password database to be allowed access.
+.TP
+.B maxconnect \fIn
+Terminate the connection when it has been available for network
+traffic for \fIn\fR seconds (i.e. \fIn\fR seconds after the first
+network control protocol comes up).
+.TP
+.B modem
+Use the modem control lines. This option is the default. With this
+option, pppd will wait for the CD (Carrier Detect) signal from the
+modem to be asserted when opening the serial device (unless a connect
+script is specified), and it will drop the DTR (Data Terminal Ready)
+signal briefly when the connection is terminated and before executing
+the connect script. On Ultrix, this option implies hardware flow
+control, as for the \fIcrtscts\fR option.
+.TP
+.B ms-dns \fI<addr>
+If pppd is acting as a server for Microsoft Windows clients, this
+option allows pppd to supply one or two DNS (Domain Name Server)
+addresses to the clients. The first instance of this option specifies
+the primary DNS address; the second instance (if given) specifies the
+secondary DNS address. (This option was present in some older
+versions of pppd under the name \fBdns-addr\fR.)
+.TP
+.B ms-wins \fI<addr>
+If pppd is acting as a server for Microsoft Windows or "Samba"
+clients, this option allows pppd to supply one or two WINS (Windows
+Internet Name Services) server addresses to the clients. The first
+instance of this option specifies the primary WINS address; the second
+instance (if given) specifies the secondary WINS address.
+.TP
+.B name \fIname
+Set the name of the local system for authentication purposes to
+\fIname\fR. This is a privileged option. With this option, pppd will
+use lines in the secrets files which have \fIname\fR as the second
+field when looking for a secret to use in authenticating the peer. In
+addition, unless overridden with the \fIuser\fR option, \fIname\fR
+will be used as the name to send to the peer when authenticating the
+local system to the peer. (Note that pppd does not append the domain
+name to \fIname\fR.)
+.TP
+.B netmask \fIn
+Set the interface netmask to \fIn\fR, a 32 bit netmask in "decimal dot"
+notation (e.g. 255.255.255.0). If this option is given, the value
+specified is ORed with the default netmask. The default netmask is
+chosen based on the negotiated remote IP address; it is the
+appropriate network mask for the class of the remote IP address, ORed
+with the netmasks for any non point-to-point network interfaces in the
+system which are on the same network.
+.TP
+.B noaccomp
+Disable Address/Control compression in both directions (send and
+receive).
+.TP
+.B noauth
+Do not require the peer to authenticate itself. This option is
+privileged if the \fIauth\fR option is specified in /etc/ppp/options.
+.TP
+.B nobsdcomp
+Disables BSD-Compress compression; \fBpppd\fR will not request or
+agree to compress packets using the BSD-Compress scheme.
+.TP
+.B noccp
+Disable CCP (Compression Control Protocol) negotiation. This option
+should only be required if the peer is buggy and gets confused by
+requests from pppd for CCP negotiation.
+.TP
+.B nocrtscts
+Disable hardware flow control (i.e. RTS/CTS) on the serial port. If
+neither the \fIcrtscts\fR nor the \fInocrtscts\fR option is given,
+the hardware flow control setting for the serial port is left
+unchanged.
+.TP
+.B nodefaultroute
+Disable the \fIdefaultroute\fR option. The system administrator who
+wishes to prevent users from creating default routes with pppd
+can do so by placing this option in the /etc/ppp/options file.
+.TP
+.B nodeflate
+Disables Deflate compression; pppd will not request or agree to
+compress packets using the Deflate scheme.
+.TP
+.B nodetach
+Don't detach from the controlling terminal. Without this option, if a
+serial device other than the terminal on the standard input is
+specified, pppd will fork to become a background process.
+.TP
+.B noip
+Disable IPCP negotiation and IP communication. This option should
+only be required if the peer is buggy and gets confused by requests
+from pppd for IPCP negotiation.
+.TP
+.B noipdefault
+Disables the default behaviour when no local IP address is specified,
+which is to determine (if possible) the local IP address from the
+hostname. With this option, the peer will have to supply the local IP
+address during IPCP negotiation (unless it specified explicitly on the
+command line or in an options file).
+.TP
+.B noipx
+Disable the IPXCP and IPX protocols. This option should only be
+required if the peer is buggy and gets confused by requests from pppd
+for IPXCP negotiation.
+.TP
+.B nomagic
+Disable magic number negotiation. With this option, pppd cannot
+detect a looped-back line. This option should only be needed if the
+peer is buggy.
+.TP
+.B nopcomp
+Disable protocol field compression negotiation in both the receive and
+the transmit direction.
+.TP
+.B nopersist
+Exit once a connection has been made and terminated. This is the
+default unless the \fIpersist\fR or \fIdemand\fR option has been
+specified.
+.TP
+.B nopredictor1
+Do not accept or agree to Predictor-1 comprssion.
+.TP
+.B noproxyarp
+Disable the \fIproxyarp\fR option. The system administrator who
+wishes to prevent users from creating proxy ARP entries with pppd can
+do so by placing this option in the /etc/ppp/options file.
+.TP
+.B novj
+Disable Van Jacobson style TCP/IP header compression in both the
+transmit and the receive direction.
+.TP
+.B novjccomp
+Disable the connection-ID compression option in Van Jacobson style
+TCP/IP header compression. With this option, pppd will not omit the
+connection-ID byte from Van Jacobson compressed TCP/IP headers, nor
+ask the peer to do so.
+.TP
+.B papcrypt
+Indicates that all secrets in the /etc/ppp/pap-secrets file which are
+used for checking the identity of the peer are encrypted, and thus
+pppd should not accept a password which, before encryption, is
+identical to the secret from the /etc/ppp/pap-secrets file.
+.TP
+.B pap-max-authreq \fIn
+Set the maximum number of PAP authenticate-request transmissions to
+\fIn\fR (default 10).
+.TP
+.B pap-restart \fIn
+Set the PAP restart interval (retransmission timeout) to \fIn\fR
+seconds (default 3).
+.TP
+.B pap-timeout \fIn
+Set the maximum time that pppd will wait for the peer to authenticate
+itself with PAP to \fIn\fR seconds (0 means no limit).
+.TP
+.B pass-filter \fIfilter-expression
+Specifies a packet filter to applied to data packets being sent or
+received to determine which packets should be allowed to pass.
+Packets which are rejected by the filter are silently discarded. This
+option can be used to prevent specific network daemons (such as
+routed) using up link bandwidth, or to provide a basic firewall
+capability.
+The \fIfilter-expression\fR syntax is as described for tcpdump(1),
+except that qualifiers which are inappropriate for a PPP link, such as
+\fBether\fR and \fBarp\fR, are not permitted. Generally the filter
+expression should be enclosed in single-quotes to prevent whitespace
+in the expression from being interpreted by the shell. Note that it
+is possible to apply different constraints to incoming and outgoing
+packets using the \fBinbound\fR and \fBoutbound\fR qualifiers. This
+option is currently only available under NetBSD, and then only if both
+the kernel and pppd were compiled with PPP_FILTER defined.
+.TP
+.B persist
+Do not exit after a connection is terminated; instead try to reopen
+the connection.
+.TP
+.B predictor1
+Request that the peer compress frames that it sends using Predictor-1
+compression, and agree to compress transmitted frames with Predictor-1
+if requested. This option has no effect unless the kernel driver
+supports Predictor-1 compression.
+.TP
+.B proxyarp
+Add an entry to this system's ARP [Address Resolution Protocol] table
+with the IP address of the peer and the Ethernet address of this
+system. This will have the effect of making the peer appear to other
+systems to be on the local ethernet.
+.TP
+.B remotename \fIname
+Set the assumed name of the remote system for authentication purposes
+to \fIname\fR.
+.TP
+.B refuse-chap
+With this option, pppd will not agree to authenticate itself to the
+peer using CHAP.
+.TP
+.B refuse-pap
+With this option, pppd will not agree to authenticate itself to the
+peer using PAP.
+.TP
+.B require-chap
+Require the peer to authenticate itself using CHAP [Challenge
+Handshake Authentication Protocol] authentication.
+.TP
+.B require-pap
+Require the peer to authenticate itself using PAP [Password
+Authentication Protocol] authentication.
+.TP
+.B silent
+With this option, pppd will not transmit LCP packets to initiate a
+connection until a valid LCP packet is received from the peer (as for
+the `passive' option with ancient versions of pppd).
+.TP
+.B usehostname
+Enforce the use of the hostname (with domain name appended, if given)
+as the name of the local system for authentication purposes (overrides
+the \fIname\fR option).
+.TP
+.B user \fIname
+Sets the name used for authenticating the local system to the peer to
+\fIname\fR.
+.TP
+.B vj-max-slots \fIn
+Sets the number of connection slots to be used by the Van Jacobson
+TCP/IP header compression and decompression code to \fIn\fR, which
+must be between 2 and 16 (inclusive).
+.TP
+.B welcome \fIscript
+Run the executable or shell command specified by \fIscript\fR before
+initiating PPP negotiation, after the connect script (if any) has
+completed. This option is privileged if the \fInoauth\fR option is
+used.
+.TP
+.B xonxoff
+Use software flow control (i.e. XON/XOFF) to control the flow of data on
+the serial port.
+.SH OPTIONS FILES
+Options can be taken from files as well as the command line. Pppd
+reads options from the files /etc/ppp/options, ~/.ppprc and
+/etc/ppp/options.\fIttyname\fR (in that order) before processing the
+options on the command line. (In fact, the command-line options are
+scanned to find the terminal name before the options.\fIttyname\fR
+file is read.) In forming the name of the options.\fIttyname\fR file,
+the initial /dev/ is removed from the terminal name, and any remaining
+/ characters are replaced with dots.
+.PP
+An options file is parsed into a series of words, delimited by
+whitespace. Whitespace can be included in a word by enclosing the
+word in 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 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/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
+and with its standard input, output and error redirected to
+/dev/null. This program or script is executed with the real and
+effective user-IDs set to root, and with an empty environment. (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
+.IP
+and with its standard input,
+output and error streams redirected to /dev/null.
+.IP
+This program or script is executed with the real and effective
+user-IDs set to root. This is so that it can be used to manipulate
+routes, run privileged daemons (e.g. \fIsendmail\fR), etc. Be
+careful that the contents of the /etc/ppp/ip-up and /etc/ppp/ip-down
+scripts do not compromise your system's security.
+.IP
+This program or script is executed with an empty environment, so you
+must either specify a PATH or use full pathnames.
+.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, and the same security considerations apply.
+.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
+and with its standard input,
+output and error streams redirected to /dev/null.
+.br
+.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
+.br
+.IP
+This program or script is executed with the real and effective
+user-IDs set to root, and with an empty environment. This is so
+that it can be used to manipulate routes, run privileged daemons (e.g.
+\fIripd\fR), etc. Be careful that the contents of the /etc/ppp/ipx-up
+and /etc/ppp/ipx-down scripts do not compromise your system's
+security.
+.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, and the same security considerations apply.
+.TP
+.B /etc/ppp/pap-secrets
+Usernames, passwords and IP addresses for PAP authentication. This
+file should be owned by root and not readable or writable by any other
+user. Pppd will log a warning if this is not the case.
+.TP
+.B /etc/ppp/chap-secrets
+Names, secrets and IP addresses for CHAP authentication. As for
+/etc/ppp/pap-secrets, this file should be owned by root and not
+readable or writable by any other user. Pppd will log a warning if
+this is not the case.
+.TP
+.B /etc/ppp/options
+System default options for pppd, read before user default options or
+command-line options.
+.TP
+.B ~/.ppprc
+User default options, read before /etc/ppp/options.\fIttyname\fR.
+.TP
+.B /etc/ppp/options.\fIttyname
+System default options for the serial port being used, read after
+~/.ppprc. In forming the \fIttyname\fR part of this
+filename, an initial /dev/ is stripped from the port name (if
+present), and any slashes in the remaining part are converted to
+dots.
+.TP
+.B /etc/ppp/peers
+A directory containing options files which may contain privileged
+options, even if pppd was invoked by a user other than root. The
+system administrator can create options files in this directory to
+permit non-privileged users to dial out without requiring the peer to
+authenticate, but only to certain trusted peers.
+.TP
+.B /etc/ppp/ppp.deny
+Lists users who may not use the system password PAP authentication.
+.TP
+.B /etc/ppp/ppp.shells
+Lists user shells which are approved for system password PAP authentication
+logins.
+.SH SEE ALSO
+.IR chat(8),
+.IR ppp(8)
+.TP
+.B RFC1144
+Jacobson, V.
+\fICompressing TCP/IP headers for low-speed serial links.\fR
+February 1990.
+.TP
+.B RFC1321
+Rivest, R.
+.I The MD5 Message-Digest Algorithm.
+April 1992.
+.TP
+.B RFC1332
+McGregor, G.
+.I PPP Internet Protocol Control Protocol (IPCP).
+May 1992.
+.TP
+.B RFC1334
+Lloyd, B.; Simpson, W.A.
+.I PPP authentication protocols.
+October 1992.
+.TP
+.B RFC1661
+Simpson, W.A.
+.I The Point\-to\-Point Protocol (PPP).
+July 1994.
+.TP
+.B RFC1662
+Simpson, W.A.
+.I PPP in HDLC-like Framing.
+July 1994.
+.SH NOTES
+The following signals have the specified effect when sent to pppd.
+.TP
+.B SIGINT, SIGTERM
+These signals cause pppd to terminate the link (by closing LCP),
+restore the serial device settings, and exit.
+.TP
+.B SIGHUP
+This signal causes pppd to terminate the link, restore the serial
+device settings, and close the serial device. If the \fIpersist\fR or
+\fIdemand\fR option has been specified, pppd will try to reopen the
+serial device and start another connection (after the holdoff period).
+Otherwise pppd will exit. If this signal is received during the
+holdoff period, it causes pppd to end the holdoff period immediately.
+.TP
+.B SIGUSR1
+This signal toggles the state of the \fIdebug\fR option.
+.TP
+.B SIGUSR2
+This signal causes pppd to renegotiate compression. This can be
+useful to re-enable compression after it has been disabled as a result
+of a fatal decompression error. (Fatal decompression errors generally
+indicate a bug in one or other implementation.)
+
+.SH AUTHORS
+Paul Mackerras (Paul.Mackerras@cs.anu.edu.au), based on earlier work by
+Drew Perkins,
+Brad Clements,
+Karl Fox,
+Greg Christy,
+and
+Brad Parker.
diff --git a/usr.sbin/pppd/pppd.h b/usr.sbin/pppd/pppd.h
new file mode 100644
index 0000000..e1a63b7
--- /dev/null
+++ b/usr.sbin/pppd/pppd.h
@@ -0,0 +1,487 @@
+/*
+ * pppd.h - PPP daemon global declarations.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: pppd.h,v 1.10 1997/10/10 06:02:57 peter Exp $
+ */
+
+/*
+ * TODO:
+ */
+
+#ifndef __PPPD_H__
+#define __PPPD_H__
+
+#include <stdio.h> /* for FILE */
+#include <sys/param.h> /* for MAXPATHLEN and BSD4_4, if defined */
+#include <sys/types.h> /* for u_int32_t, if defined */
+#include <sys/time.h> /* for struct timeval */
+#include <net/ppp_defs.h>
+
+#if __STDC__
+#include <stdarg.h>
+#define __V(x) x
+#else
+#include <varargs.h>
+#define __V(x) (va_alist) va_dcl
+#define const
+#endif
+
+/*
+ * Limits.
+ */
+
+#define NUM_PPP 1 /* One PPP interface supported (per process) */
+#define MAXWORDLEN 1024 /* max length of word in file (incl null) */
+#define MAXARGS 1 /* max # args to a command */
+#define MAXNAMELEN 256 /* max length of hostname or name for auth */
+#define MAXSECRETLEN 256 /* max length of password or secret */
+
+/*
+ * Global variables.
+ */
+
+extern int hungup; /* Physical layer has disconnected */
+extern int ifunit; /* Interface unit number */
+extern char ifname[]; /* Interface name */
+extern int ttyfd; /* Serial device file descriptor */
+extern char hostname[]; /* Our hostname */
+extern u_char outpacket_buf[]; /* Buffer for outgoing packets */
+extern int phase; /* Current state of link - see values below */
+extern int baud_rate; /* Current link speed in bits/sec */
+extern char *progname; /* Name of this program */
+extern int redirect_stderr;/* Connector's stderr should go to file */
+extern char peer_authname[];/* Authenticated name of peer */
+extern int privileged; /* We were run by real-uid root */
+extern int need_holdoff; /* Need holdoff period after link terminates */
+
+/*
+ * 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 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++ */
+
+/* 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 */
+#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..a749a79
--- /dev/null
+++ b/usr.sbin/pppd/sys-bsd.c
@@ -0,0 +1,1556 @@
+/*
+ * sys-bsd.c - System-dependent procedures for setting up
+ * PPP interfaces on bsd-4.4-ish systems (including 386BSD, NetBSD, etc.)
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * Copyright (c) 1995 The Australian National University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University and The Australian National University.
+ * The names of the Universities may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef lint
+static char rcsid[] = "$Id$";
+#endif
+
+/*
+ * 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/if_var.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 <netinet/if_ether.h>
+#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;
+
+ 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));
+ if (ioctl(sockfd, SIOCAIFADDR, (caddr_t) &ifra) < 0) {
+ if (errno != EEXIST) {
+ syslog(LOG_ERR, "Couldn't set interface address: %m");
+ return 0;
+ }
+ syslog(LOG_WARNING,
+ "Couldn't set interface address: Address %s already exists",
+ ip_ntoa(o));
+ }
+ ifaddrs[0] = o;
+ ifaddrs[1] = h;
+ return 1;
+}
+
+/*
+ * cifaddr - Clear the interface IP addresses, and delete routes
+ * through the interface if possible.
+ */
+int
+cifaddr(u, o, h)
+ int u;
+ u_int32_t o, h;
+{
+ struct ifaliasreq ifra;
+
+ ifaddrs[0] = 0;
+ strncpy(ifra.ifra_name, ifname, sizeof(ifra.ifra_name));
+ SET_SA_FAMILY(ifra.ifra_addr, AF_INET);
+ ((struct sockaddr_in *) &ifra.ifra_addr)->sin_addr.s_addr = o;
+ SET_SA_FAMILY(ifra.ifra_broadaddr, AF_INET);
+ ((struct sockaddr_in *) &ifra.ifra_broadaddr)->sin_addr.s_addr = h;
+ BZERO(&ifra.ifra_mask, sizeof(ifra.ifra_mask));
+ if (ioctl(sockfd, SIOCDIFADDR, (caddr_t) &ifra) < 0) {
+ if (errno != EADDRNOTAVAIL)
+ syslog(LOG_WARNING, "Couldn't delete interface address: %m");
+ return 0;
+ }
+ return 1;
+}
+
+/*
+ * sifdefaultroute - assign a default route through the address given.
+ */
+int
+sifdefaultroute(u, l, g)
+ int u;
+ u_int32_t l, g;
+{
+ return dodefaultroute(g, 's');
+}
+
+/*
+ * cifdefaultroute - delete a default route through the address given.
+ */
+int
+cifdefaultroute(u, l, g)
+ int u;
+ u_int32_t l, g;
+{
+ return dodefaultroute(g, 'c');
+}
+
+/*
+ * dodefaultroute - talk to a routing socket to add/delete a default route.
+ */
+static int
+dodefaultroute(g, cmd)
+ u_int32_t g;
+ int cmd;
+{
+ int routes;
+ struct {
+ struct rt_msghdr hdr;
+ struct sockaddr_in dst;
+ struct sockaddr_in gway;
+ struct sockaddr_in mask;
+ } rtmsg;
+
+ if ((routes = socket(PF_ROUTE, SOCK_RAW, AF_INET)) < 0) {
+ syslog(LOG_ERR, "Couldn't %s default route: socket: %m",
+ cmd=='s'? "add": "delete");
+ return 0;
+ }
+
+ memset(&rtmsg, 0, sizeof(rtmsg));
+ rtmsg.hdr.rtm_type = cmd == 's'? RTM_ADD: RTM_DELETE;
+ rtmsg.hdr.rtm_flags = RTF_UP | RTF_GATEWAY | RTF_STATIC;
+ rtmsg.hdr.rtm_version = RTM_VERSION;
+ rtmsg.hdr.rtm_seq = ++rtm_seq;
+ rtmsg.hdr.rtm_addrs = RTA_DST | RTA_GATEWAY | RTA_NETMASK;
+ rtmsg.dst.sin_len = sizeof(rtmsg.dst);
+ rtmsg.dst.sin_family = AF_INET;
+ rtmsg.gway.sin_len = sizeof(rtmsg.gway);
+ rtmsg.gway.sin_family = AF_INET;
+ rtmsg.gway.sin_addr.s_addr = g;
+ rtmsg.mask.sin_len = sizeof(rtmsg.dst);
+ rtmsg.mask.sin_family = AF_INET;
+
+ rtmsg.hdr.rtm_msglen = sizeof(rtmsg);
+ if (write(routes, &rtmsg, sizeof(rtmsg)) < 0) {
+ syslog(LOG_ERR, "Couldn't %s default route: %m",
+ cmd=='s'? "add": "delete");
+ close(routes);
+ return 0;
+ }
+
+ close(routes);
+ default_route_gateway = (cmd == 's')? g: 0;
+ return 1;
+}
+
+#if RTM_VERSION >= 3
+
+/*
+ * sifproxyarp - Make a proxy ARP entry for the peer.
+ */
+static struct {
+ struct rt_msghdr hdr;
+ struct sockaddr_inarp dst;
+ struct sockaddr_dl hwa;
+ char extra[128];
+} arpmsg;
+
+static int arpmsg_valid;
+
+int
+sifproxyarp(unit, hisaddr)
+ int unit;
+ u_int32_t hisaddr;
+{
+ int routes;
+
+ /*
+ * Get the hardware address of an interface on the same subnet
+ * as our local address.
+ */
+ memset(&arpmsg, 0, sizeof(arpmsg));
+ if (!get_ether_addr(hisaddr, &arpmsg.hwa)) {
+ syslog(LOG_ERR, "Cannot determine ethernet address for proxy ARP");
+ return 0;
+ }
+
+ if ((routes = socket(PF_ROUTE, SOCK_RAW, AF_INET)) < 0) {
+ syslog(LOG_ERR, "Couldn't add proxy arp entry: socket: %m");
+ return 0;
+ }
+
+ arpmsg.hdr.rtm_type = RTM_ADD;
+ arpmsg.hdr.rtm_flags = RTF_ANNOUNCE | RTF_HOST | RTF_STATIC;
+ arpmsg.hdr.rtm_version = RTM_VERSION;
+ arpmsg.hdr.rtm_seq = ++rtm_seq;
+ arpmsg.hdr.rtm_addrs = RTA_DST | RTA_GATEWAY;
+ arpmsg.hdr.rtm_inits = RTV_EXPIRE;
+ arpmsg.dst.sin_len = sizeof(struct sockaddr_inarp);
+ arpmsg.dst.sin_family = AF_INET;
+ arpmsg.dst.sin_addr.s_addr = hisaddr;
+ arpmsg.dst.sin_other = SIN_PROXY;
+
+ arpmsg.hdr.rtm_msglen = (char *) &arpmsg.hwa - (char *) &arpmsg
+ + arpmsg.hwa.sdl_len;
+ if (write(routes, &arpmsg, arpmsg.hdr.rtm_msglen) < 0) {
+ syslog(LOG_ERR, "Couldn't add proxy arp entry: %m");
+ close(routes);
+ return 0;
+ }
+
+ close(routes);
+ arpmsg_valid = 1;
+ proxy_arp_addr = hisaddr;
+ return 1;
+}
+
+/*
+ * cifproxyarp - Delete the proxy ARP entry for the peer.
+ */
+int
+cifproxyarp(unit, hisaddr)
+ int unit;
+ u_int32_t hisaddr;
+{
+ int routes;
+
+ if (!arpmsg_valid)
+ return 0;
+ arpmsg_valid = 0;
+
+ arpmsg.hdr.rtm_type = RTM_DELETE;
+ arpmsg.hdr.rtm_seq = ++rtm_seq;
+
+ if ((routes = socket(PF_ROUTE, SOCK_RAW, AF_INET)) < 0) {
+ syslog(LOG_ERR, "Couldn't delete proxy arp entry: socket: %m");
+ return 0;
+ }
+
+ if (write(routes, &arpmsg, arpmsg.hdr.rtm_msglen) < 0) {
+ syslog(LOG_ERR, "Couldn't delete proxy arp entry: %m");
+ close(routes);
+ return 0;
+ }
+
+ close(routes);
+ proxy_arp_addr = 0;
+ return 1;
+}
+
+#else /* RTM_VERSION */
+
+/*
+ * sifproxyarp - Make a proxy ARP entry for the peer.
+ */
+int
+sifproxyarp(unit, hisaddr)
+ int unit;
+ u_int32_t hisaddr;
+{
+ struct arpreq arpreq;
+ struct {
+ struct sockaddr_dl sdl;
+ char space[128];
+ } dls;
+
+ BZERO(&arpreq, sizeof(arpreq));
+
+ /*
+ * Get the hardware address of an interface on the same subnet
+ * as our local address.
+ */
+ if (!get_ether_addr(hisaddr, &dls.sdl)) {
+ syslog(LOG_ERR, "Cannot determine ethernet address for proxy ARP");
+ return 0;
+ }
+
+ arpreq.arp_ha.sa_len = sizeof(struct sockaddr);
+ arpreq.arp_ha.sa_family = AF_UNSPEC;
+ BCOPY(LLADDR(&dls.sdl), arpreq.arp_ha.sa_data, dls.sdl.sdl_alen);
+ SET_SA_FAMILY(arpreq.arp_pa, AF_INET);
+ ((struct sockaddr_in *) &arpreq.arp_pa)->sin_addr.s_addr = hisaddr;
+ arpreq.arp_flags = ATF_PERM | ATF_PUBL;
+ if (ioctl(sockfd, SIOCSARP, (caddr_t)&arpreq) < 0) {
+ syslog(LOG_ERR, "Couldn't add proxy arp entry: %m");
+ return 0;
+ }
+
+ proxy_arp_addr = hisaddr;
+ return 1;
+}
+
+/*
+ * cifproxyarp - Delete the proxy ARP entry for the peer.
+ */
+int
+cifproxyarp(unit, hisaddr)
+ int unit;
+ u_int32_t hisaddr;
+{
+ struct arpreq arpreq;
+
+ BZERO(&arpreq, sizeof(arpreq));
+ SET_SA_FAMILY(arpreq.arp_pa, AF_INET);
+ ((struct sockaddr_in *) &arpreq.arp_pa)->sin_addr.s_addr = hisaddr;
+ if (ioctl(sockfd, SIOCDARP, (caddr_t)&arpreq) < 0) {
+ syslog(LOG_WARNING, "Couldn't delete proxy arp entry: %m");
+ return 0;
+ }
+ proxy_arp_addr = 0;
+ return 1;
+}
+#endif /* RTM_VERSION */
+
+#ifdef IPX_CHANGE
+/********************************************************************
+ *
+ * sipxfaddr - Config the interface IPX networknumber
+ */
+
+int
+sipxfaddr (int unit, unsigned long int network, unsigned char * node )
+ {
+ int result = 1;
+
+ int skfd;
+ struct sockaddr_ipx ipx_addr;
+ struct ifreq ifr;
+ struct sockaddr_ipx *sipx = (struct sockaddr_ipx *) &ifr.ifr_addr;
+ union ipx_net_u net;
+
+ skfd = socket (AF_IPX, SOCK_DGRAM, 0);
+ if (skfd < 0)
+ {
+ syslog (LOG_DEBUG, "socket(AF_IPX): %m(%d)", errno);
+ result = 0;
+ }
+ else
+ {
+ memset (&ifr, '\0', sizeof (ifr));
+ strcpy (ifr.ifr_name, ifname);
+
+ memcpy (sipx->sipx_addr.x_host.c_host, node, 6);
+ sipx->sipx_len = sizeof(sipx);
+ sipx->sipx_family = AF_IPX;
+ sipx->sipx_port = 0;
+ memset(&net, 0, sizeof(net));
+ net.long_e = htonl (network);
+ sipx->sipx_addr.x_net = net.net_e;
+/*
+ * Set the IPX device
+ */
+ if (ioctl(skfd, SIOCSIFADDR, (caddr_t) &ifr) < 0)
+ {
+ result = 0;
+ if (errno != EEXIST)
+ {
+ syslog (LOG_DEBUG,
+ "ioctl(SIOCAIFADDR, CRTITF): %m(%d)", errno);
+ }
+ else
+ {
+ syslog (LOG_WARNING,
+ "ioctl(SIOCAIFADDR, CRTITF): Address already exists");
+ }
+ }
+ close (skfd);
+ }
+ return result;
+ }
+
+/********************************************************************
+ *
+ * cipxfaddr - Clear the information for the IPX network. The IPX routes
+ * are removed and the device is no longer able to pass IPX
+ * frames.
+ */
+
+int cipxfaddr (int unit)
+ {
+ int result = 1;
+
+ int skfd;
+ struct sockaddr_ipx ipx_addr;
+ struct ifreq ifr;
+ struct sockaddr_ipx *sipx = (struct sockaddr_ipx *) &ifr.ifr_addr;
+
+ skfd = socket (AF_IPX, SOCK_DGRAM, 0);
+ if (skfd < 0)
+ {
+ syslog (LOG_DEBUG, "socket(AF_IPX): %m(%d)", errno);
+ result = 0;
+ }
+ else
+ {
+ memset (&ifr, '\0', sizeof (ifr));
+ strcpy (ifr.ifr_name, ifname);
+
+ sipx->sipx_len = sizeof(sipx);
+ sipx->sipx_family = AF_IPX;
+/*
+ * Set the IPX device
+ */
+ if (ioctl(skfd, SIOCSIFADDR, (caddr_t) &ifr) < 0)
+ {
+ syslog (LOG_INFO,
+ "ioctl(SIOCAIFADDR, IPX_DLTITF): %m(%d)", errno);
+ result = 0;
+ }
+ close (skfd);
+ }
+ return result;
+ }
+#endif
+
+/*
+ * get_ether_addr - get the hardware address of an interface on the
+ * the same subnet as ipaddr.
+ */
+#define MAX_IFS 32
+
+static int
+get_ether_addr(ipaddr, hwaddr)
+ u_int32_t ipaddr;
+ struct sockaddr_dl *hwaddr;
+{
+ struct ifreq *ifr, *ifend, *ifp;
+ u_int32_t ina, mask;
+ struct sockaddr_dl *dla;
+ struct ifreq ifreq;
+ struct ifconf ifc;
+ struct ifreq ifs[MAX_IFS];
+
+ ifc.ifc_len = sizeof(ifs);
+ ifc.ifc_req = ifs;
+ if (ioctl(sockfd, SIOCGIFCONF, &ifc) < 0) {
+ syslog(LOG_ERR, "ioctl(SIOCGIFCONF): %m");
+ return 0;
+ }
+
+ /*
+ * Scan through looking for an interface with an Internet
+ * address on the same subnet as `ipaddr'.
+ */
+ ifend = (struct ifreq *) (ifc.ifc_buf + ifc.ifc_len);
+ for (ifr = ifc.ifc_req; ifr < ifend; ifr = (struct ifreq *)
+ ((char *)&ifr->ifr_addr + ifr->ifr_addr.sa_len)) {
+ if (ifr->ifr_addr.sa_family == AF_INET) {
+ ina = ((struct sockaddr_in *) &ifr->ifr_addr)->sin_addr.s_addr;
+ strncpy(ifreq.ifr_name, ifr->ifr_name, sizeof(ifreq.ifr_name));
+ /*
+ * Check that the interface is up, and not point-to-point
+ * or loopback.
+ */
+ if (ioctl(sockfd, SIOCGIFFLAGS, &ifreq) < 0)
+ continue;
+ if ((ifreq.ifr_flags &
+ (IFF_UP|IFF_BROADCAST|IFF_POINTOPOINT|IFF_LOOPBACK|IFF_NOARP))
+ != (IFF_UP|IFF_BROADCAST))
+ continue;
+ /*
+ * Get its netmask and check that it's on the right subnet.
+ */
+ if (ioctl(sockfd, SIOCGIFNETMASK, &ifreq) < 0)
+ continue;
+ mask = ((struct sockaddr_in *) &ifreq.ifr_addr)->sin_addr.s_addr;
+ if ((ipaddr & mask) != (ina & mask))
+ continue;
+
+ break;
+ }
+ }
+
+ if (ifr >= ifend)
+ return 0;
+ syslog(LOG_INFO, "found interface %s for proxy arp", ifr->ifr_name);
+
+ /*
+ * Now scan through again looking for a link-level address
+ * for this interface.
+ */
+ ifp = ifr;
+ for (ifr = ifc.ifc_req; ifr < ifend; ) {
+ if (strcmp(ifp->ifr_name, ifr->ifr_name) == 0
+ && ifr->ifr_addr.sa_family == AF_LINK) {
+ /*
+ * Found the link-level address - copy it out
+ */
+ dla = (struct sockaddr_dl *) &ifr->ifr_addr;
+ BCOPY(dla, hwaddr, dla->sdl_len);
+ return 1;
+ }
+ ifr = (struct ifreq *) ((char *)&ifr->ifr_addr + ifr->ifr_addr.sa_len);
+ }
+
+ return 0;
+}
+
+/*
+ * Return user specified netmask, modified by any mask we might determine
+ * for address `addr' (in network byte order).
+ * Here we scan through the system's list of interfaces, looking for
+ * any non-point-to-point interfaces which might appear to be on the same
+ * network as `addr'. If we find any, we OR in their netmask to the
+ * user-specified netmask.
+ */
+u_int32_t
+GetMask(addr)
+ u_int32_t addr;
+{
+ u_int32_t mask, nmask, ina;
+ struct ifreq *ifr, *ifend, ifreq;
+ struct ifconf ifc;
+ struct ifreq ifs[MAX_IFS];
+
+ addr = ntohl(addr);
+ if (IN_CLASSA(addr)) /* determine network mask for address class */
+ nmask = IN_CLASSA_NET;
+ else if (IN_CLASSB(addr))
+ nmask = IN_CLASSB_NET;
+ else
+ nmask = IN_CLASSC_NET;
+ /* class D nets are disallowed by bad_ip_adrs */
+ mask = netmask | htonl(nmask);
+
+ /*
+ * Scan through the system's network interfaces.
+ */
+ ifc.ifc_len = sizeof(ifs);
+ ifc.ifc_req = ifs;
+ if (ioctl(sockfd, SIOCGIFCONF, &ifc) < 0) {
+ syslog(LOG_WARNING, "ioctl(SIOCGIFCONF): %m");
+ return mask;
+ }
+ ifend = (struct ifreq *) (ifc.ifc_buf + ifc.ifc_len);
+ for (ifr = ifc.ifc_req; ifr < ifend; ifr = (struct ifreq *)
+ ((char *)&ifr->ifr_addr + ifr->ifr_addr.sa_len)) {
+ /*
+ * Check the interface's internet address.
+ */
+ if (ifr->ifr_addr.sa_family != AF_INET)
+ continue;
+ ina = ((struct sockaddr_in *) &ifr->ifr_addr)->sin_addr.s_addr;
+ if ((ntohl(ina) & nmask) != (addr & nmask))
+ continue;
+ /*
+ * Check that the interface is up, and not point-to-point or loopback.
+ */
+ strncpy(ifreq.ifr_name, ifr->ifr_name, sizeof(ifreq.ifr_name));
+ if (ioctl(sockfd, SIOCGIFFLAGS, &ifreq) < 0)
+ continue;
+ if ((ifreq.ifr_flags & (IFF_UP|IFF_POINTOPOINT|IFF_LOOPBACK))
+ != IFF_UP)
+ continue;
+ /*
+ * Get its netmask and OR it into our mask.
+ */
+ if (ioctl(sockfd, SIOCGIFNETMASK, &ifreq) < 0)
+ continue;
+ mask |= ((struct sockaddr_in *)&ifreq.ifr_addr)->sin_addr.s_addr;
+ }
+
+ return mask;
+}
+
+/*
+ * lock - create a lock file for the named lock device
+ */
+#define LOCK_PREFIX "/var/spool/lock/LCK.."
+
+int
+lock(dev)
+ char *dev;
+{
+ char hdb_lock_buffer[12];
+ int fd, pid, n;
+ char *p;
+
+ if ((p = strrchr(dev, '/')) != NULL)
+ dev = p + 1;
+ lock_file = malloc(strlen(LOCK_PREFIX) + strlen(dev) + 1);
+ if (lock_file == NULL)
+ novm("lock file name");
+ strcat(strcpy(lock_file, LOCK_PREFIX), dev);
+
+ while ((fd = open(lock_file, O_EXCL | O_CREAT | O_RDWR, 0644)) < 0) {
+ if (errno == EEXIST
+ && (fd = open(lock_file, O_RDONLY, 0)) >= 0) {
+ /* Read the lock file to find out who has the device locked */
+ n = read(fd, hdb_lock_buffer, 11);
+ if (n <= 0) {
+ syslog(LOG_ERR, "Can't read pid from lock file %s", lock_file);
+ close(fd);
+ } else {
+ hdb_lock_buffer[n] = 0;
+ pid = atoi(hdb_lock_buffer);
+ if (kill(pid, 0) == -1 && errno == ESRCH) {
+ /* pid no longer exists - remove the lock file */
+ if (unlink(lock_file) == 0) {
+ close(fd);
+ syslog(LOG_NOTICE, "Removed stale lock on %s (pid %d)",
+ dev, pid);
+ continue;
+ } else
+ syslog(LOG_WARNING, "Couldn't remove stale lock on %s",
+ dev);
+ } else
+ syslog(LOG_NOTICE, "Device %s is locked by pid %d",
+ dev, pid);
+ }
+ close(fd);
+ } else
+ syslog(LOG_ERR, "Can't create lock file %s: %m", lock_file);
+ free(lock_file);
+ lock_file = NULL;
+ return -1;
+ }
+
+ sprintf(hdb_lock_buffer, "%10d\n", getpid());
+ write(fd, hdb_lock_buffer, 11);
+
+ close(fd);
+ return 0;
+}
+
+/*
+ * unlock - remove our lockfile
+ */
+void
+unlock()
+{
+ if (lock_file) {
+ unlink(lock_file);
+ free(lock_file);
+ lock_file = NULL;
+ }
+}
diff --git a/usr.sbin/pppd/upap.c b/usr.sbin/pppd/upap.c
new file mode 100644
index 0000000..469c5cb
--- /dev/null
+++ b/usr.sbin/pppd/upap.c
@@ -0,0 +1,618 @@
+/*
+ * upap.c - User/Password Authentication Protocol.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef lint
+static char rcsid[] = "$Id$";
+#endif
+
+/*
+ * TODO:
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <syslog.h>
+
+#include "pppd.h"
+#include "upap.h"
+
+/*
+ * Protocol entry points.
+ */
+static void upap_init __P((int));
+static void upap_lowerup __P((int));
+static void upap_lowerdown __P((int));
+static void upap_input __P((int, u_char *, int));
+static void upap_protrej __P((int));
+static int upap_printpkt __P((u_char *, int,
+ void (*) __P((void *, char *, ...)), void *));
+
+struct protent pap_protent = {
+ PPP_PAP,
+ upap_init,
+ upap_input,
+ upap_protrej,
+ upap_lowerup,
+ upap_lowerdown,
+ NULL,
+ NULL,
+ upap_printpkt,
+ NULL,
+ 1,
+ "PAP",
+ NULL,
+ NULL,
+ NULL
+};
+
+upap_state upap[NUM_PPP]; /* UPAP state; one for each unit */
+
+static void upap_timeout __P((void *));
+static void upap_reqtimeout __P((void *));
+static void upap_rauthreq __P((upap_state *, u_char *, int, int));
+static void upap_rauthack __P((upap_state *, u_char *, int, int));
+static void upap_rauthnak __P((upap_state *, u_char *, int, int));
+static void upap_sauthreq __P((upap_state *));
+static void upap_sresp __P((upap_state *, int, int, char *, int));
+
+
+/*
+ * upap_init - Initialize a UPAP unit.
+ */
+static void
+upap_init(unit)
+ int unit;
+{
+ upap_state *u = &upap[unit];
+
+ u->us_unit = unit;
+ u->us_user = NULL;
+ u->us_userlen = 0;
+ u->us_passwd = NULL;
+ u->us_passwdlen = 0;
+ u->us_clientstate = UPAPCS_INITIAL;
+ u->us_serverstate = UPAPSS_INITIAL;
+ u->us_id = 0;
+ u->us_timeouttime = UPAP_DEFTIMEOUT;
+ u->us_maxtransmits = 10;
+ u->us_reqtimeout = UPAP_DEFREQTIME;
+}
+
+
+/*
+ * upap_authwithpeer - Authenticate us with our peer (start client).
+ *
+ * Set new state and send authenticate's.
+ */
+void
+upap_authwithpeer(unit, user, password)
+ int unit;
+ char *user, *password;
+{
+ upap_state *u = &upap[unit];
+
+ /* Save the username and password we're given */
+ u->us_user = user;
+ u->us_userlen = strlen(user);
+ u->us_passwd = password;
+ u->us_passwdlen = strlen(password);
+ u->us_transmits = 0;
+
+ /* Lower layer up yet? */
+ if (u->us_clientstate == UPAPCS_INITIAL ||
+ u->us_clientstate == UPAPCS_PENDING) {
+ u->us_clientstate = UPAPCS_PENDING;
+ return;
+ }
+
+ upap_sauthreq(u); /* Start protocol */
+}
+
+
+/*
+ * upap_authpeer - Authenticate our peer (start server).
+ *
+ * Set new state.
+ */
+void
+upap_authpeer(unit)
+ int unit;
+{
+ upap_state *u = &upap[unit];
+
+ /* Lower layer up yet? */
+ if (u->us_serverstate == UPAPSS_INITIAL ||
+ u->us_serverstate == UPAPSS_PENDING) {
+ u->us_serverstate = UPAPSS_PENDING;
+ return;
+ }
+
+ u->us_serverstate = UPAPSS_LISTEN;
+ if (u->us_reqtimeout > 0)
+ TIMEOUT(upap_reqtimeout, u, u->us_reqtimeout);
+}
+
+
+/*
+ * upap_timeout - Retransmission timer for sending auth-reqs expired.
+ */
+static void
+upap_timeout(arg)
+ void *arg;
+{
+ upap_state *u = (upap_state *) arg;
+
+ if (u->us_clientstate != UPAPCS_AUTHREQ)
+ return;
+
+ if (u->us_transmits >= u->us_maxtransmits) {
+ /* give up in disgust */
+ syslog(LOG_ERR, "No response to PAP authenticate-requests");
+ u->us_clientstate = UPAPCS_BADAUTH;
+ auth_withpeer_fail(u->us_unit, PPP_PAP);
+ return;
+ }
+
+ upap_sauthreq(u); /* Send Authenticate-Request */
+}
+
+
+/*
+ * upap_reqtimeout - Give up waiting for the peer to send an auth-req.
+ */
+static void
+upap_reqtimeout(arg)
+ void *arg;
+{
+ upap_state *u = (upap_state *) arg;
+
+ if (u->us_serverstate != UPAPSS_LISTEN)
+ return; /* huh?? */
+
+ auth_peer_fail(u->us_unit, PPP_PAP);
+ u->us_serverstate = UPAPSS_BADAUTH;
+}
+
+
+/*
+ * upap_lowerup - The lower layer is up.
+ *
+ * Start authenticating if pending.
+ */
+static void
+upap_lowerup(unit)
+ int unit;
+{
+ upap_state *u = &upap[unit];
+
+ if (u->us_clientstate == UPAPCS_INITIAL)
+ u->us_clientstate = UPAPCS_CLOSED;
+ else if (u->us_clientstate == UPAPCS_PENDING) {
+ upap_sauthreq(u); /* send an auth-request */
+ }
+
+ if (u->us_serverstate == UPAPSS_INITIAL)
+ u->us_serverstate = UPAPSS_CLOSED;
+ else if (u->us_serverstate == UPAPSS_PENDING) {
+ u->us_serverstate = UPAPSS_LISTEN;
+ if (u->us_reqtimeout > 0)
+ TIMEOUT(upap_reqtimeout, u, u->us_reqtimeout);
+ }
+}
+
+
+/*
+ * upap_lowerdown - The lower layer is down.
+ *
+ * Cancel all timeouts.
+ */
+static void
+upap_lowerdown(unit)
+ int unit;
+{
+ upap_state *u = &upap[unit];
+
+ if (u->us_clientstate == UPAPCS_AUTHREQ) /* Timeout pending? */
+ UNTIMEOUT(upap_timeout, u); /* Cancel timeout */
+ if (u->us_serverstate == UPAPSS_LISTEN && u->us_reqtimeout > 0)
+ UNTIMEOUT(upap_reqtimeout, u);
+
+ u->us_clientstate = UPAPCS_INITIAL;
+ u->us_serverstate = UPAPSS_INITIAL;
+}
+
+
+/*
+ * upap_protrej - Peer doesn't speak this protocol.
+ *
+ * This shouldn't happen. In any case, pretend lower layer went down.
+ */
+static void
+upap_protrej(unit)
+ int unit;
+{
+ upap_state *u = &upap[unit];
+
+ if (u->us_clientstate == UPAPCS_AUTHREQ) {
+ syslog(LOG_ERR, "PAP authentication failed due to protocol-reject");
+ auth_withpeer_fail(unit, PPP_PAP);
+ }
+ if (u->us_serverstate == UPAPSS_LISTEN) {
+ syslog(LOG_ERR, "PAP authentication of peer failed (protocol-reject)");
+ auth_peer_fail(unit, PPP_PAP);
+ }
+ upap_lowerdown(unit);
+}
+
+
+/*
+ * upap_input - Input UPAP packet.
+ */
+static void
+upap_input(unit, inpacket, l)
+ int unit;
+ u_char *inpacket;
+ int l;
+{
+ upap_state *u = &upap[unit];
+ u_char *inp;
+ u_char code, id;
+ int len;
+
+ /*
+ * Parse header (code, id and length).
+ * If packet too short, drop it.
+ */
+ inp = inpacket;
+ if (l < UPAP_HEADERLEN) {
+ UPAPDEBUG((LOG_INFO, "pap_input: rcvd short header."));
+ return;
+ }
+ GETCHAR(code, inp);
+ GETCHAR(id, inp);
+ GETSHORT(len, inp);
+ if (len < UPAP_HEADERLEN) {
+ UPAPDEBUG((LOG_INFO, "pap_input: rcvd illegal length."));
+ return;
+ }
+ if (len > l) {
+ UPAPDEBUG((LOG_INFO, "pap_input: rcvd short packet."));
+ return;
+ }
+ len -= UPAP_HEADERLEN;
+
+ /*
+ * Action depends on code.
+ */
+ switch (code) {
+ case UPAP_AUTHREQ:
+ upap_rauthreq(u, inp, id, len);
+ break;
+
+ case UPAP_AUTHACK:
+ upap_rauthack(u, inp, id, len);
+ break;
+
+ case UPAP_AUTHNAK:
+ upap_rauthnak(u, inp, id, len);
+ break;
+
+ default: /* XXX Need code reject */
+ break;
+ }
+}
+
+
+/*
+ * upap_rauth - Receive Authenticate.
+ */
+static void
+upap_rauthreq(u, inp, id, len)
+ upap_state *u;
+ u_char *inp;
+ int id;
+ int len;
+{
+ u_char ruserlen, rpasswdlen;
+ char *ruser, *rpasswd;
+ int retcode;
+ char *msg;
+ int msglen;
+
+ UPAPDEBUG((LOG_INFO, "pap_rauth: Rcvd id %d.", id));
+
+ if (u->us_serverstate < UPAPSS_LISTEN)
+ return;
+
+ /*
+ * If we receive a duplicate authenticate-request, we are
+ * supposed to return the same status as for the first request.
+ */
+ if (u->us_serverstate == UPAPSS_OPEN) {
+ upap_sresp(u, UPAP_AUTHACK, id, "", 0); /* return auth-ack */
+ return;
+ }
+ if (u->us_serverstate == UPAPSS_BADAUTH) {
+ upap_sresp(u, UPAP_AUTHNAK, id, "", 0); /* return auth-nak */
+ return;
+ }
+
+ /*
+ * Parse user/passwd.
+ */
+ if (len < sizeof (u_char)) {
+ UPAPDEBUG((LOG_INFO, "pap_rauth: rcvd short packet."));
+ return;
+ }
+ GETCHAR(ruserlen, inp);
+ len -= sizeof (u_char) + ruserlen + sizeof (u_char);
+ if (len < 0) {
+ UPAPDEBUG((LOG_INFO, "pap_rauth: rcvd short packet."));
+ return;
+ }
+ ruser = (char *) inp;
+ INCPTR(ruserlen, inp);
+ GETCHAR(rpasswdlen, inp);
+ if (len < rpasswdlen) {
+ UPAPDEBUG((LOG_INFO, "pap_rauth: rcvd short packet."));
+ return;
+ }
+ rpasswd = (char *) inp;
+
+ /*
+ * Check the username and password given.
+ */
+ retcode = check_passwd(u->us_unit, ruser, ruserlen, rpasswd,
+ rpasswdlen, &msg, &msglen);
+ BZERO(rpasswd, rpasswdlen);
+
+ upap_sresp(u, retcode, id, msg, msglen);
+
+ if (retcode == UPAP_AUTHACK) {
+ u->us_serverstate = UPAPSS_OPEN;
+ auth_peer_success(u->us_unit, PPP_PAP, ruser, ruserlen);
+ } else {
+ u->us_serverstate = UPAPSS_BADAUTH;
+ auth_peer_fail(u->us_unit, PPP_PAP);
+ }
+
+ if (u->us_reqtimeout > 0)
+ UNTIMEOUT(upap_reqtimeout, u);
+}
+
+
+/*
+ * upap_rauthack - Receive Authenticate-Ack.
+ */
+static void
+upap_rauthack(u, inp, id, len)
+ upap_state *u;
+ u_char *inp;
+ int id;
+ int len;
+{
+ u_char msglen;
+ char *msg;
+
+ UPAPDEBUG((LOG_INFO, "pap_rauthack: Rcvd id %d.", id));
+ if (u->us_clientstate != UPAPCS_AUTHREQ) /* XXX */
+ return;
+
+ /*
+ * Parse message.
+ */
+ if (len < sizeof (u_char)) {
+ UPAPDEBUG((LOG_INFO, "pap_rauthack: rcvd short packet."));
+ return;
+ }
+ GETCHAR(msglen, inp);
+ len -= sizeof (u_char);
+ if (len < msglen) {
+ UPAPDEBUG((LOG_INFO, "pap_rauthack: rcvd short packet."));
+ return;
+ }
+ msg = (char *) inp;
+ PRINTMSG(msg, msglen);
+
+ u->us_clientstate = UPAPCS_OPEN;
+
+ auth_withpeer_success(u->us_unit, PPP_PAP);
+}
+
+
+/*
+ * upap_rauthnak - Receive Authenticate-Nakk.
+ */
+static void
+upap_rauthnak(u, inp, id, len)
+ upap_state *u;
+ u_char *inp;
+ int id;
+ int len;
+{
+ u_char msglen;
+ char *msg;
+
+ UPAPDEBUG((LOG_INFO, "pap_rauthnak: Rcvd id %d.", id));
+ if (u->us_clientstate != UPAPCS_AUTHREQ) /* XXX */
+ return;
+
+ /*
+ * Parse message.
+ */
+ if (len < sizeof (u_char)) {
+ UPAPDEBUG((LOG_INFO, "pap_rauthnak: rcvd short packet."));
+ return;
+ }
+ GETCHAR(msglen, inp);
+ len -= sizeof (u_char);
+ if (len < msglen) {
+ UPAPDEBUG((LOG_INFO, "pap_rauthnak: rcvd short packet."));
+ return;
+ }
+ msg = (char *) inp;
+ PRINTMSG(msg, msglen);
+
+ u->us_clientstate = UPAPCS_BADAUTH;
+
+ syslog(LOG_ERR, "PAP authentication failed");
+ auth_withpeer_fail(u->us_unit, PPP_PAP);
+}
+
+
+/*
+ * upap_sauthreq - Send an Authenticate-Request.
+ */
+static void
+upap_sauthreq(u)
+ upap_state *u;
+{
+ u_char *outp;
+ int outlen;
+
+ outlen = UPAP_HEADERLEN + 2 * sizeof (u_char) +
+ u->us_userlen + u->us_passwdlen;
+ outp = outpacket_buf;
+
+ MAKEHEADER(outp, PPP_PAP);
+
+ PUTCHAR(UPAP_AUTHREQ, outp);
+ PUTCHAR(++u->us_id, outp);
+ PUTSHORT(outlen, outp);
+ PUTCHAR(u->us_userlen, outp);
+ BCOPY(u->us_user, outp, u->us_userlen);
+ INCPTR(u->us_userlen, outp);
+ PUTCHAR(u->us_passwdlen, outp);
+ BCOPY(u->us_passwd, outp, u->us_passwdlen);
+
+ output(u->us_unit, outpacket_buf, outlen + PPP_HDRLEN);
+
+ UPAPDEBUG((LOG_INFO, "pap_sauth: Sent id %d.", u->us_id));
+
+ TIMEOUT(upap_timeout, u, u->us_timeouttime);
+ ++u->us_transmits;
+ u->us_clientstate = UPAPCS_AUTHREQ;
+}
+
+
+/*
+ * upap_sresp - Send a response (ack or nak).
+ */
+static void
+upap_sresp(u, code, id, msg, msglen)
+ upap_state *u;
+ u_char code, id;
+ char *msg;
+ int msglen;
+{
+ u_char *outp;
+ int outlen;
+
+ outlen = UPAP_HEADERLEN + sizeof (u_char) + msglen;
+ outp = outpacket_buf;
+ MAKEHEADER(outp, PPP_PAP);
+
+ PUTCHAR(code, outp);
+ PUTCHAR(id, outp);
+ PUTSHORT(outlen, outp);
+ PUTCHAR(msglen, outp);
+ BCOPY(msg, outp, msglen);
+ output(u->us_unit, outpacket_buf, outlen + PPP_HDRLEN);
+
+ UPAPDEBUG((LOG_INFO, "pap_sresp: Sent code %d, id %d.", code, id));
+}
+
+/*
+ * upap_printpkt - print the contents of a PAP packet.
+ */
+static char *upap_codenames[] = {
+ "AuthReq", "AuthAck", "AuthNak"
+};
+
+static int
+upap_printpkt(p, plen, printer, arg)
+ u_char *p;
+ int plen;
+ void (*printer) __P((void *, char *, ...));
+ void *arg;
+{
+ int code, id, len;
+ int mlen, ulen, wlen;
+ char *user, *pwd, *msg;
+ u_char *pstart;
+
+ if (plen < UPAP_HEADERLEN)
+ return 0;
+ pstart = p;
+ GETCHAR(code, p);
+ GETCHAR(id, p);
+ GETSHORT(len, p);
+ if (len < UPAP_HEADERLEN || len > plen)
+ return 0;
+
+ if (code >= 1 && code <= sizeof(upap_codenames) / sizeof(char *))
+ printer(arg, " %s", upap_codenames[code-1]);
+ else
+ printer(arg, " code=0x%x", code);
+ printer(arg, " id=0x%x", id);
+ len -= UPAP_HEADERLEN;
+ switch (code) {
+ case UPAP_AUTHREQ:
+ if (len < 1)
+ break;
+ ulen = p[0];
+ if (len < ulen + 2)
+ break;
+ wlen = p[ulen + 1];
+ if (len < ulen + wlen + 2)
+ break;
+ user = (char *) (p + 1);
+ pwd = (char *) (p + ulen + 2);
+ p += ulen + wlen + 2;
+ len -= ulen + wlen + 2;
+ printer(arg, " user=");
+ print_string(user, ulen, printer, arg);
+ printer(arg, " password=");
+ print_string(pwd, wlen, printer, arg);
+ break;
+ case UPAP_AUTHACK:
+ case UPAP_AUTHNAK:
+ if (len < 1)
+ break;
+ mlen = p[0];
+ if (len < mlen + 1)
+ break;
+ msg = (char *) (p + 1);
+ p += mlen + 1;
+ len -= mlen + 1;
+ printer(arg, " ");
+ print_string(msg, mlen, printer, arg);
+ break;
+ }
+
+ /* print the rest of the bytes in the packet */
+ for (; len > 0; --len) {
+ GETCHAR(code, p);
+ printer(arg, " %.2x", code);
+ }
+
+ return p - pstart;
+}
diff --git a/usr.sbin/pppd/upap.h b/usr.sbin/pppd/upap.h
new file mode 100644
index 0000000..510efa3
--- /dev/null
+++ b/usr.sbin/pppd/upap.h
@@ -0,0 +1,87 @@
+/*
+ * upap.h - User/Password Authentication Protocol definitions.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id$
+ */
+
+/*
+ * Packet header = Code, id, length.
+ */
+#define UPAP_HEADERLEN (sizeof (u_char) + sizeof (u_char) + sizeof (u_short))
+
+
+/*
+ * UPAP codes.
+ */
+#define UPAP_AUTHREQ 1 /* Authenticate-Request */
+#define UPAP_AUTHACK 2 /* Authenticate-Ack */
+#define UPAP_AUTHNAK 3 /* Authenticate-Nak */
+
+
+/*
+ * Each interface is described by upap structure.
+ */
+typedef struct upap_state {
+ int us_unit; /* Interface unit number */
+ char *us_user; /* User */
+ int us_userlen; /* User length */
+ char *us_passwd; /* Password */
+ int us_passwdlen; /* Password length */
+ int us_clientstate; /* Client state */
+ int us_serverstate; /* Server state */
+ u_char us_id; /* Current id */
+ int us_timeouttime; /* Timeout (seconds) for auth-req retrans. */
+ int us_transmits; /* Number of auth-reqs sent */
+ int us_maxtransmits; /* Maximum number of auth-reqs to send */
+ int us_reqtimeout; /* Time to wait for auth-req from peer */
+} upap_state;
+
+
+/*
+ * Client states.
+ */
+#define UPAPCS_INITIAL 0 /* Connection down */
+#define UPAPCS_CLOSED 1 /* Connection up, haven't requested auth */
+#define UPAPCS_PENDING 2 /* Connection down, have requested auth */
+#define UPAPCS_AUTHREQ 3 /* We've sent an Authenticate-Request */
+#define UPAPCS_OPEN 4 /* We've received an Ack */
+#define UPAPCS_BADAUTH 5 /* We've received a Nak */
+
+/*
+ * Server states.
+ */
+#define UPAPSS_INITIAL 0 /* Connection down */
+#define UPAPSS_CLOSED 1 /* Connection up, haven't requested auth */
+#define UPAPSS_PENDING 2 /* Connection down, have requested auth */
+#define UPAPSS_LISTEN 3 /* Listening for an Authenticate */
+#define UPAPSS_OPEN 4 /* We've sent an Ack */
+#define UPAPSS_BADAUTH 5 /* We've sent a Nak */
+
+
+/*
+ * Timeouts.
+ */
+#define UPAP_DEFTIMEOUT 3 /* Timeout (seconds) for retransmitting req */
+#define UPAP_DEFREQTIME 30 /* Time to wait for auth-req from peer */
+
+extern upap_state upap[];
+
+void upap_authwithpeer __P((int, char *, char *));
+void upap_authpeer __P((int));
+
+extern struct protent pap_protent;
diff --git a/usr.sbin/pppstats/Makefile b/usr.sbin/pppstats/Makefile
new file mode 100644
index 0000000..899c6db
--- /dev/null
+++ b/usr.sbin/pppstats/Makefile
@@ -0,0 +1,10 @@
+# $Id: Makefile,v 1.5 1997/02/22 16:12:11 peter Exp $
+
+PROG= pppstats
+SRCS= pppstats.c
+MAN8= pppstats.8
+
+#as per policies in handbook
+MAINTAINER= peter@freebsd.org
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/pppstats/pppstats.8 b/usr.sbin/pppstats/pppstats.8
new file mode 100644
index 0000000..d7f89f7
--- /dev/null
+++ b/usr.sbin/pppstats/pppstats.8
@@ -0,0 +1,217 @@
+.\" @(#) $Id: pppstats.8,v 1.3 1996/07/01 01:22:35 paulus Exp $
+.TH PPPSTATS 8 "26 June 1995"
+.SH NAME
+pppstats \- print PPP statistics
+.SH SYNOPSIS
+.B pppstats
+[
+.B -a
+] [
+.B -v
+] [
+.B -r
+] [
+.B -z
+] [
+.B -c
+.I <count>
+] [
+.B -w
+.I <secs>
+] [
+.I interface
+]
+.ti 12
+.SH DESCRIPTION
+The
+.B pppstats
+utility reports PPP-related statistics at regular intervals for the
+specified PPP interface. If the interface is unspecified, it will
+default to ppp0.
+The display is split horizontally
+into input and output sections containing columns of statistics
+describing the properties and volume of packets received and
+transmitted by the interface.
+.PP
+The options are as follows:
+.TP
+.B -a
+Display absolute values rather than deltas. With this option, all
+reports show statistics for the time since the link was initiated.
+Without this option, the second and subsequent reports show statistics
+for the time since the last report.
+.TP
+.B -c \fIcount
+Repeat the display
+.I count
+times. If this option is not specified, the default repeat count is 1
+if the
+.B -w
+option is not specified, otherwise infinity.
+.TP
+.B -r
+Display additional statistics summarizing the compression ratio
+achieved by the packet compression algorithm in use.
+.TP
+.B -v
+Display additional statistics relating to the performance of the Van
+Jacobson TCP header compression algorithm.
+.TP
+.B -w \fIwait
+Pause
+.I wait
+seconds between each display. If this option is not specified, the
+default interval is 5 seconds.
+.TP
+.B -z
+Instead of the standard display, show statistics indicating the
+performance of the packet compression algorithm in use.
+.PP
+The following fields are printed on the input side when the
+.B -z
+option is not used:
+.TP
+.B IN
+The total number of bytes received by this interface.
+.TP
+.B PACK
+The total number of packets received by this interface.
+.TP
+.B VJCOMP
+The number of header-compressed TCP packets received by this interface.
+.TP
+.B VJUNC
+The number of header-uncompressed TCP packets received by this
+interface. Not reported when the
+.B -r
+option is specified.
+.TP
+.B VJERR
+The number of corrupted or bogus header-compressed TCP packets
+received by this interface. Not reported when the
+.B -r
+option is specified.
+.TP
+.B VJTOSS
+The number of VJ header-compressed TCP packets dropped on reception by
+this interface because of preceding errors. Only reported when the
+.B -v
+option is specified.
+.TP
+.B NON-VJ
+The total number of non-TCP packets received by this interface. Only
+reported when the
+.B -v
+option is specified.
+.TP
+.B RATIO
+The compression ratio achieved for received packets by the
+packet compression scheme in use, defined as the uncompressed size
+divided by the compressed size.
+Only reported when the
+.B -r
+option is specified.
+.TP
+.B UBYTE
+The total number of bytes received, after decompression of compressed
+packets. Only reported when the
+.B -r
+option is specified.
+.PP
+The following fields are printed on the output side:
+.TP
+.B OUT
+The total number of bytes transmitted from this interface.
+.TP
+.B PACK
+The total number of packets transmitted from this interface.
+.TP
+.B VJCOMP
+The number of TCP packets transmitted from this interface with
+VJ-compressed TCP headers.
+.TP
+.B VJUNC
+The number of TCP packets transmitted from this interface with
+VJ-uncompressed TCP headers.
+Not reported when the
+.B -r
+option is specified.
+.TP
+.B NON-VJ
+The total number of non-TCP packets transmitted from this interface.
+Not reported when the
+.B -r
+option is specified.
+.TP
+.B VJSRCH
+The number of searches for the cached header entry for a VJ header
+compressed TCP packet. Only reported when the
+.B -v
+option is specified.
+.TP
+.B VJMISS
+The number of failed searches for the cached header entry for a
+VJ header compressed TCP packet. Only reported when the
+.B -v
+option is specified.
+.TP
+.B RATIO
+The compression ratio achieved for transmitted packets by the
+packet compression scheme in use, defined as the size
+before compression divided by the compressed size.
+Only reported when the
+.B -r
+option is specified.
+.TP
+.B UBYTE
+The total number of bytes to be transmitted, before packet compression
+is applied. Only reported when the
+.B -r
+option is specified.
+.PP
+When the
+.B -z
+option is specified,
+.Nm 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..4baab01
--- /dev/null
+++ b/usr.sbin/pppstats/pppstats.c
@@ -0,0 +1,523 @@
+/*
+ * print PPP statistics:
+ * pppstats [-a|-d] [-v|-r|-z] [-c count] [-w wait] [interface]
+ *
+ * -a Show absolute values rather than deltas
+ * -d Show data rate (kB/s) rather than bytes
+ * -v Show more stats for VJ TCP header compression
+ * -r Show compression ratio
+ * -z Show compression statistics instead of default display
+ *
+ * History:
+ * perkins@cps.msu.edu: Added compression statistics and alternate
+ * display. 11/94
+ * Brad Parker (brad@cayman.com) 6/92
+ *
+ * from the original "slstats" by Van Jacobson
+ *
+ * Copyright (c) 1989 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef lint
+static char rcsid[] = "$Id: pppstats.c,v 1.10 1997/08/22 15:39:04 peter Exp $";
+#endif
+
+#include <stdio.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <net/ppp_defs.h>
+
+#ifndef STREAMS
+#include <sys/socket.h> /* *BSD, Linux, NeXT, Ultrix etc. */
+#include <net/if.h>
+#ifndef _linux_
+#include <net/if_ppp.h>
+#else
+#include <net/if_ppp.h>
+#endif
+
+#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/pstat/Makefile b/usr.sbin/pstat/Makefile
new file mode 100644
index 0000000..5954d13
--- /dev/null
+++ b/usr.sbin/pstat/Makefile
@@ -0,0 +1,13 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= pstat
+CFLAGS+=-I${.CURDIR}/../../sys
+BINGRP= kmem
+BINMODE=2555
+DPADD= ${LIBKVM}
+LDADD= -lkvm
+MAN8= pstat.8
+LINKS= ${BINDIR}/pstat ${BINDIR}/swapinfo
+MLINKS= pstat.8 swapinfo.8
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/pstat/pstat.8 b/usr.sbin/pstat/pstat.8
new file mode 100644
index 0000000..011593c
--- /dev/null
+++ b/usr.sbin/pstat/pstat.8
@@ -0,0 +1,386 @@
+.\" Copyright (c) 1980, 1991, 1993, 1994
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)pstat.8 8.5 (Berkeley) 5/13/94
+.\" $Id: pstat.8,v 1.14 1997/08/30 20:18:48 peter Exp $
+.\"
+.Dd May 13, 1994
+.Dt PSTAT 8
+.Os BSD 4
+.Sh NAME
+.Nm pstat ,
+.Nm swapinfo
+.Nd display system data structures
+.Sh SYNOPSIS
+.Nm pstat
+.Op Fl Tfiknstv
+.Op Fl M Ar core
+.Op Fl N Ar system
+.Pp
+.Nm swapinfo
+.Op Fl k
+.Op Fl M Ar core
+.Op Fl N Ar system
+.Sh DESCRIPTION
+.Nm Pstat
+displays open file entry, swap space utilization,
+terminal state, and vnode data structures.
+If
+.Ar corefile
+is given, the information is sought there, otherwise
+in
+.Pa /dev/mem .
+The required namelist is taken from
+.Pa /kernel
+unless
+.Ar system
+is specified.
+.Pp
+If invoked as
+.Nm swapinfo
+the
+.Fl s
+option is implied, and only the
+.Fl k
+option is legal.
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl n
+Print devices out by major/minor instead of name.
+.It Fl k
+Print sizes in kilobytes, regardless of the setting of the BLOCKSIZE
+environment variable.
+.It Fl T
+Print the number of used and free slots in the several system tables
+and is useful for checking to see how large system tables have become
+if the system is under heavy load.
+.It Fl f
+Print the open file table with these headings:
+.Bl -tag -width indent
+.It LOC
+The core location of this table entry.
+.It TYPE
+The type of object the file table entry points to.
+.It FLG
+Miscellaneous state variables encoded thus:
+.Bl -tag -width indent
+.It R
+open for reading
+.It W
+open for writing
+.It A
+open for appending
+.It S
+shared lock present
+.It X
+exclusive lock present
+.It I
+signal pgrp when data ready
+.El
+.It CNT
+Number of processes that know this open file.
+.It MSG
+Number of messages outstanding for this file.
+.It DATA
+The location of the vnode table entry or socket structure for this file.
+.It OFFSET
+The file offset (see
+.Xr lseek 2 ) .
+.El
+.It Fl s
+Print information about swap space usage on all the
+swap areas compiled into the kernel.
+The first column is the device name of the partition. The next column is
+the total space available in the partition. The
+.Ar Used
+column indicates the total blocks used so far; the
+.Ar Available
+column indicates how much space is remaining on each partition.
+The
+.Ar Capacity
+reports the percentage of space used.
+.Pp
+If more than one partition is configured into the system, totals for all
+of the statistics will be reported in the final line of the report.
+.It Fl t
+Print table for terminals
+with these headings:
+.Bl -tag -width indent
+.It RAW
+Number of characters in raw input queue.
+.It CAN
+Number of characters in canonicalized input queue.
+.It OUT
+Number of characters in output queue.
+.It MODE
+See
+.Xr tty 4 .
+.It ADDR
+Physical device address.
+.It DEL
+Number of delimiters (newlines) in canonicalized input queue.
+.It COL
+Calculated column position of terminal.
+.It STATE
+Miscellaneous state variables encoded thus:
+.Bl -tag -width indent
+.It T
+delay timeout in progress
+.It W
+waiting for open to complete
+.It O
+open
+.It F
+outq has been flushed during DMA
+.It C
+carrier is on
+.It c
+connection open
+.It B
+busy doing output
+.It A
+process is waiting for space in output queue
+.It a
+process is waiting for output to complete
+.It X
+open for exclusive use
+.It S
+output stopped (ixon flow control)
+.It m
+output stopped (carrier flow control)
+.It o
+output stopped (CTS flow control)
+.It d
+output stopped (DSR flow control)
+.It K
+input stopped
+.It Y
+send SIGIO for input events
+.It D
+state for lowercase
+.Ql \e
+work
+.It E
+within a
+.Ql \e.../
+for PRTRUB
+.It L
+next character is literal
+.It P
+retyping suspended input (PENDIN)
+.It N
+counting tab width, ignore FLUSHO
+.It l
+block mode input routine in use
+.It s
+i/o being snooped
+.It Z
+connection lost
+.El
+.It SESS
+Kernel address of the session structure.
+.It PGID
+Process group for which this is controlling terminal.
+.It DISC
+Line discipline;
+.Ql term
+for
+TTYDISC
+or
+.Ql ntty
+for
+NTTYDISC
+or
+.Ql tab
+for
+TABLDISC
+or
+.Ql slip
+for
+SLIPDISC
+or
+.Ql ppp
+for
+PPPDISC.
+.El
+.It Fl v
+Print the active vnodes. Each group of vnodes corresponding
+to a particular filesystem is preceded by a two line header. The
+first line consists of the following:
+.Pp
+.Df I
+.No *** MOUNT Em fstype from
+on
+.Em on fsflags
+.De
+.Pp
+where
+.Em fstype
+is one of
+.Em ufs , nfs , mfs , or pc ;
+.Em from
+is the filesystem is mounted from;
+.Em on
+is the directory
+the filesystem is mounted on; and
+.Em fsflags
+is a list
+of optional flags applied to the mount (see
+.Xr mount 8 ) .
+.The second line is a header for the individual fields ,
+the first part of which are fixed, and the second part are filesystem
+type specific. The headers common to all vnodes are:
+.Bl -tag -width indent
+.It ADDR
+Location of this vnode.
+.It TYP
+File type.
+.It VFLAG
+.Pp
+A list of letters representing vnode flags:
+.Bl -tag -width indent
+.It R
+\- VROOT
+.It T
+\- VTEXT
+.It S
+\- VSYSTEM
+.It t
+\- VISTTY
+.It L
+\- VXLOCK
+.It W
+\- VXWANT
+.It B
+\- VBWAIT
+.It A
+\- VALIASED
+.It V
+\- VVMIO
+.It a
+\- VAGE
+.It l
+\- VOLOCK
+.It w
+\- VOWANT
+.El
+.Pp
+.It USE
+The number of references to this vnode.
+.It HOLD
+The number of I/O buffers held by this vnode.
+.It FILEID
+The vnode fileid.
+In the case of
+.Em ufs
+this is the inode number.
+.It IFLAG
+Miscellaneous filesystem specific state variables encoded thus:
+.Bl -tag -width indent
+.It "For ufs:"
+.Pp
+.Bl -tag -width indent
+.It L
+locked
+.It U
+update time
+.Pq Xr fs 5
+must be corrected
+.It A
+access time must be corrected
+.It W
+wanted by another process (L flag is on)
+.It C
+changed time must be corrected
+.It S
+shared lock applied
+.It E
+exclusive lock applied
+.It Z
+someone waiting for a lock
+.It M
+contains modifications
+.It R
+has a rename in progress
+.El
+.It "For nfs:"
+.Bl -tag -width indent
+.It W
+waiting for I/O buffer flush to complete
+.It P
+I/O buffers being flushed
+.It M
+locally modified data exists
+.It E
+an earlier write failed
+.It X
+non-cacheable lease (nqnfs)
+.It O
+write lease (nqnfs)
+.It G
+lease was evicted (nqnfs)
+.El
+.El
+.It SIZ/RDEV
+Number of bytes in an ordinary file, or
+major and minor device of special file.
+.El
+.It Fl i
+Same as
+.Fl v ,
+present for backwards-compatibility.
+.El
+.Sh FILES
+.Bl -tag -width /dev/memxxx -compact
+.It Pa /kernel
+namelist
+.It Pa /dev/mem
+default source of tables
+.El
+.Sh SEE ALSO
+.Xr ps 1 ,
+.Xr systat 1 ,
+.Xr stat 2 ,
+.Xr fs 5 ,
+.Xr iostat 8 ,
+.Xr vmstat 8
+.Rs
+.Rt Tn UNIX Rt Implementation ,
+.Ra K. Thompson
+.Re
+.Sh BUGS
+Does not understand NFS swap servers.
+.Sh HISTORY
+The
+.Nm pstat
+command appeared in
+.Bx 4.0 .
diff --git a/usr.sbin/pstat/pstat.c b/usr.sbin/pstat/pstat.c
new file mode 100644
index 0000000..3cc6f25
--- /dev/null
+++ b/usr.sbin/pstat/pstat.c
@@ -0,0 +1,1148 @@
+/*-
+ * Copyright (c) 1980, 1991, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 1980, 1991, 1993, 1994\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)pstat.c 8.16 (Berkeley) 5/9/95";
+#endif
+static const char rcsid[] =
+ "$Id: pstat.c,v 1.31 1997/10/09 07:22:08 charnier Exp $";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/vnode.h>
+#include <sys/ucred.h>
+#define KERNEL
+#include <sys/file.h>
+#include <ufs/ufs/quota.h>
+#include <ufs/ufs/inode.h>
+#include <sys/mount.h>
+#include <sys/uio.h>
+#include <sys/namei.h>
+#include <miscfs/union/union.h>
+#undef KERNEL
+#include <sys/stat.h>
+#include <nfs/rpcv2.h>
+#include <nfs/nfsproto.h>
+#include <nfs/nfs.h>
+#include <nfs/nfsnode.h>
+#include <sys/ioctl.h>
+#include <sys/ioctl_compat.h> /* XXX NTTYDISC is too well hidden */
+#include <sys/tty.h>
+#include <sys/conf.h>
+#include <sys/rlist.h>
+
+#include <sys/user.h>
+#include <sys/sysctl.h>
+
+#include <err.h>
+#include <fcntl.h>
+#include <kvm.h>
+#include <limits.h>
+#include <nlist.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+struct nlist nl[] = {
+#define VM_SWAPLIST 0
+ { "_swaplist" },/* list of free swap areas */
+#define VM_SWDEVT 1
+ { "_swdevt" }, /* list of swap devices and sizes */
+#define VM_NSWAP 2
+ { "_nswap" }, /* size of largest swap device */
+#define VM_NSWDEV 3
+ { "_nswdev" }, /* number of swap devices */
+#define VM_DMMAX 4
+ { "_dmmax" }, /* maximum size of a swap block */
+#define V_MOUNTLIST 5
+ { "_mountlist" }, /* address of head of mount list. */
+#define V_NUMV 6
+ { "_numvnodes" },
+#define FNL_NFILE 7
+ {"_nfiles"},
+#define FNL_MAXFILE 8
+ {"_maxfiles"},
+#define NLMANDATORY FNL_MAXFILE /* names up to here are mandatory */
+#define SCONS NLMANDATORY + 1
+ { "_cons" },
+#define SPTY NLMANDATORY + 2
+ { "_pt_tty" },
+#define SNPTY NLMANDATORY + 3
+ { "_npty" },
+
+#ifdef hp300
+#define SDCA (SNPTY+1)
+ { "_dca_tty" },
+#define SNDCA (SNPTY+2)
+ { "_ndca" },
+#define SDCM (SNPTY+3)
+ { "_dcm_tty" },
+#define SNDCM (SNPTY+4)
+ { "_ndcm" },
+#define SDCL (SNPTY+5)
+ { "_dcl_tty" },
+#define SNDCL (SNPTY+6)
+ { "_ndcl" },
+#define SITE (SNPTY+7)
+ { "_ite_tty" },
+#define SNITE (SNPTY+8)
+ { "_nite" },
+#endif
+
+#ifdef mips
+#define SDC (SNPTY+1)
+ { "_dc_tty" },
+#define SNDC (SNPTY+2)
+ { "_dc_cnt" },
+#endif
+
+#ifdef __FreeBSD__
+#define SCCONS (SNPTY+1)
+ { "_sccons" },
+#define NSCCONS (SNPTY+2)
+ { "_nsccons" },
+#define SIO (SNPTY+3)
+ { "_sio_tty" },
+#define NSIO (SNPTY+4)
+ { "_nsio_tty" },
+#define RC (SNPTY+5)
+ { "_rc_tty" },
+#define NRC (SNPTY+6)
+ { "_nrc_tty" },
+#define CY (SNPTY+7)
+ { "_cy_tty" },
+#define NCY (SNPTY+8)
+ { "_ncy_tty" },
+#define SI (SNPTY+9)
+ { "_si_tty" },
+#define NSI (SNPTY+10)
+ { "_si_Nports" },
+#endif
+ { "" }
+};
+
+int usenumflag;
+int totalflag;
+char *nlistf = NULL;
+char *memf = NULL;
+kvm_t *kd;
+
+char *usagestr;
+
+struct {
+ int m_flag;
+ const char *m_name;
+} mnt_flags[] = {
+ { MNT_RDONLY, "rdonly" },
+ { MNT_SYNCHRONOUS, "sync" },
+ { MNT_NOEXEC, "noexec" },
+ { MNT_NOSUID, "nosuid" },
+ { MNT_NODEV, "nodev" },
+ { MNT_UNION, "union" },
+ { MNT_ASYNC, "async" },
+ { MNT_NOATIME, "noatime" },
+ { MNT_EXRDONLY, "exrdonly" },
+ { MNT_EXPORTED, "exported" },
+ { MNT_DEFEXPORTED, "defexported" },
+ { MNT_EXPORTANON, "exportanon" },
+ { MNT_EXKERB, "exkerb" },
+ { MNT_LOCAL, "local" },
+ { MNT_QUOTA, "quota" },
+ { MNT_ROOTFS, "rootfs" },
+ { MNT_USER, "user" },
+ { MNT_UPDATE, "update" },
+ { MNT_DELEXPORT },
+ { MNT_UPDATE, "update" },
+ { MNT_DELEXPORT, "delexport" },
+ { MNT_RELOAD, "reload" },
+ { MNT_FORCE, "force" },
+ { MNT_UNMOUNT, "unmount" },
+ { MNT_MWAIT, "mwait" },
+ { MNT_WANTRDWR, "wantrdwr" },
+ { 0 }
+};
+
+
+#define SVAR(var) __STRING(var) /* to force expansion */
+#define KGET(idx, var) \
+ KGET1(idx, &var, sizeof(var), SVAR(var))
+#define KGET1(idx, p, s, msg) \
+ KGET2(nl[idx].n_value, p, s, msg)
+#define KGET2(addr, p, s, msg) \
+ if (kvm_read(kd, (u_long)(addr), p, s) != s) \
+ warnx("cannot read %s: %s", msg, kvm_geterr(kd))
+#define KGETRET(addr, p, s, msg) \
+ if (kvm_read(kd, (u_long)(addr), p, s) != s) { \
+ warnx("cannot read %s: %s", msg, kvm_geterr(kd)); \
+ return (0); \
+ }
+
+void filemode __P((void));
+int getfiles __P((char **, int *));
+struct mount *
+ getmnt __P((struct mount *));
+struct e_vnode *
+ kinfo_vnodes __P((int *));
+struct e_vnode *
+ loadvnodes __P((int *));
+void mount_print __P((struct mount *));
+void nfs_header __P((void));
+int nfs_print __P((struct vnode *));
+void swapmode __P((void));
+void ttymode __P((void));
+void ttyprt __P((struct tty *, int));
+void ttytype __P((struct tty *, char *, int, int, int));
+void ufs_header __P((void));
+int ufs_print __P((struct vnode *));
+void union_header __P((void));
+int union_print __P((struct vnode *));
+static void usage __P((void));
+void vnode_header __P((void));
+void vnode_print __P((struct vnode *, struct vnode *));
+void vnodemode __P((void));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int ch, i, quit, ret;
+ int fileflag, swapflag, ttyflag, vnodeflag;
+ char buf[_POSIX2_LINE_MAX],*opts;
+
+ fileflag = swapflag = ttyflag = vnodeflag = 0;
+
+ /* We will behave like good old swapinfo if thus invoked */
+ opts = strrchr(argv[0],'/');
+ if (opts)
+ opts++;
+ else
+ opts = argv[0];
+ if (!strcmp(opts,"swapinfo")) {
+ swapflag = 1;
+ opts = "kM:N:";
+ usagestr = "swapinfo [-k] [-M core] [-N system]";
+ } else {
+ opts = "TM:N:fiknstv";
+ usagestr = "pstat [-Tfknstv] [-M core] [-N system]";
+ }
+
+ while ((ch = getopt(argc, argv, opts)) != -1)
+ switch (ch) {
+ case 'f':
+ fileflag = 1;
+ break;
+ case 'k':
+ putenv("BLOCKSIZE=1K");
+ break;
+ case 'M':
+ memf = optarg;
+ break;
+ case 'N':
+ nlistf = optarg;
+ break;
+ case 'n':
+ usenumflag = 1;
+ break;
+ case 's':
+ swapflag = 1;
+ break;
+ case 'T':
+ totalflag = 1;
+ break;
+ case 't':
+ ttyflag = 1;
+ break;
+ case 'v':
+ case 'i': /* Backward compatibility. */
+ fprintf(stderr, "vnode mode not supported\n");
+ exit(1);
+#if 0
+ vnodeflag = 1;
+ break;
+#endif
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ /*
+ * Discard setgid privileges if not the running kernel so that bad
+ * guys can't print interesting stuff from kernel memory.
+ */
+ if (nlistf != NULL || memf != NULL)
+ (void)setgid(getgid());
+
+ if ((kd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, buf)) == 0)
+ errx(1, "kvm_openfiles: %s", buf);
+ if ((ret = kvm_nlist(kd, nl)) != 0) {
+ if (ret == -1)
+ errx(1, "kvm_nlist: %s", kvm_geterr(kd));
+ for (i = quit = 0; i <= NLMANDATORY; i++)
+ if (!nl[i].n_value) {
+ quit = 1;
+ warnx("undefined symbol: %s", nl[i].n_name);
+ }
+ if (quit)
+ exit(1);
+ }
+ if (!(fileflag | vnodeflag | ttyflag | swapflag | totalflag))
+ usage();
+ if (fileflag || totalflag)
+ filemode();
+ if (vnodeflag)
+ vnodemode();
+ if (ttyflag)
+ ttymode();
+ if (swapflag || totalflag)
+ swapmode();
+ exit (0);
+}
+
+static void
+usage()
+{
+ fprintf(stderr, "usage: %s\n", usagestr);
+ exit (1);
+}
+
+struct e_vnode {
+ struct vnode *avnode;
+ struct vnode vnode;
+};
+
+void
+vnodemode()
+{
+ struct e_vnode *e_vnodebase, *endvnode, *evp;
+ struct vnode *vp;
+ struct mount *maddr, *mp;
+ int numvnodes;
+
+ e_vnodebase = loadvnodes(&numvnodes);
+ if (totalflag) {
+ (void)printf("%7d vnodes\n", numvnodes);
+ return;
+ }
+ endvnode = e_vnodebase + numvnodes;
+ (void)printf("%d active vnodes\n", numvnodes);
+
+
+#define ST mp->mnt_stat
+ maddr = NULL;
+ for (evp = e_vnodebase; evp < endvnode; evp++) {
+ vp = &evp->vnode;
+ if (vp->v_mount != maddr) {
+ /*
+ * New filesystem
+ */
+ if ((mp = getmnt(vp->v_mount)) == NULL)
+ continue;
+ maddr = vp->v_mount;
+ mount_print(mp);
+ vnode_header();
+ if (!strcmp(ST.f_fstypename, "ufs") ||
+ !strcmp(ST.f_fstypename, "mfs"))
+ ufs_header();
+ else if (!strcmp(ST.f_fstypename, "nfs"))
+ nfs_header();
+ else if (!strcmp(ST.f_fstypename, "union"))
+ union_header();
+ (void)printf("\n");
+ }
+ vnode_print(evp->avnode, vp);
+ if (!strcmp(ST.f_fstypename, "ufs") ||
+ !strcmp(ST.f_fstypename, "mfs"))
+ ufs_print(vp);
+ else if (!strcmp(ST.f_fstypename, "nfs"))
+ nfs_print(vp);
+ else if (!strcmp(ST.f_fstypename, "union"))
+ union_print(vp);
+ (void)printf("\n");
+ }
+ free(e_vnodebase);
+}
+
+void
+vnode_header()
+{
+ (void)printf("ADDR TYP VFLAG USE HOLD");
+}
+
+void
+vnode_print(avnode, vp)
+ struct vnode *avnode;
+ struct vnode *vp;
+{
+ char *type, flags[16];
+ char *fp = flags;
+ int flag;
+
+ /*
+ * set type
+ */
+ switch (vp->v_type) {
+ case VNON:
+ type = "non"; break;
+ case VREG:
+ type = "reg"; break;
+ case VDIR:
+ type = "dir"; break;
+ case VBLK:
+ type = "blk"; break;
+ case VCHR:
+ type = "chr"; break;
+ case VLNK:
+ type = "lnk"; break;
+ case VSOCK:
+ type = "soc"; break;
+ case VFIFO:
+ type = "fif"; break;
+ case VBAD:
+ type = "bad"; break;
+ default:
+ type = "unk"; break;
+ }
+ /*
+ * gather flags
+ */
+ flag = vp->v_flag;
+ if (flag & VROOT)
+ *fp++ = 'R';
+ if (flag & VTEXT)
+ *fp++ = 'T';
+ if (flag & VSYSTEM)
+ *fp++ = 'S';
+ if (flag & VISTTY)
+ *fp++ = 't';
+ if (flag & VXLOCK)
+ *fp++ = 'L';
+ if (flag & VXWANT)
+ *fp++ = 'W';
+ if (flag & VBWAIT)
+ *fp++ = 'B';
+ if (flag & VALIASED)
+ *fp++ = 'A';
+ if (flag & VVMIO)
+ *fp++ = 'V';
+ if (flag & VAGE)
+ *fp++ = 'a';
+ if (flag & VOLOCK)
+ *fp++ = 'l';
+ if (flag & VOWANT)
+ *fp++ = 'w';
+ if (flag == 0)
+ *fp++ = '-';
+ *fp = '\0';
+ (void)printf("%8x %s %5s %4d %4d",
+ avnode, type, flags, vp->v_usecount, vp->v_holdcnt);
+}
+
+void
+ufs_header()
+{
+ (void)printf(" FILEID IFLAG RDEV|SZ");
+}
+
+int
+ufs_print(vp)
+ struct vnode *vp;
+{
+ int flag;
+ struct inode inode, *ip = &inode;
+ char flagbuf[16], *flags = flagbuf;
+ char *name;
+ mode_t type;
+
+ KGETRET(VTOI(vp), &inode, sizeof(struct inode), "vnode's inode");
+ flag = ip->i_flag;
+ if (flag & IN_RENAME)
+ *flags++ = 'R';
+ if (flag & IN_UPDATE)
+ *flags++ = 'U';
+ if (flag & IN_ACCESS)
+ *flags++ = 'A';
+ if (flag & IN_CHANGE)
+ *flags++ = 'C';
+ if (flag & IN_MODIFIED)
+ *flags++ = 'M';
+ if (flag & IN_SHLOCK)
+ *flags++ = 'S';
+ if (flag & IN_EXLOCK)
+ *flags++ = 'E';
+ if (flag == 0)
+ *flags++ = '-';
+ *flags = '\0';
+
+ (void)printf(" %6d %5s", ip->i_number, flagbuf);
+ type = ip->i_mode & S_IFMT;
+ if (S_ISCHR(ip->i_mode) || S_ISBLK(ip->i_mode))
+ if (usenumflag || ((name = devname(ip->i_rdev, type)) == NULL))
+ (void)printf(" %2d,%-2d",
+ major(ip->i_rdev), minor(ip->i_rdev));
+ else
+ (void)printf(" %7s", name);
+ else
+ (void)printf(" %7qd", ip->i_size);
+ return (0);
+}
+
+void
+nfs_header()
+{
+ (void)printf(" FILEID NFLAG RDEV|SZ");
+}
+
+int
+nfs_print(vp)
+ struct vnode *vp;
+{
+ struct nfsnode nfsnode, *np = &nfsnode;
+ char flagbuf[16], *flags = flagbuf;
+ int flag;
+ char *name;
+ mode_t type;
+
+ KGETRET(VTONFS(vp), &nfsnode, sizeof(nfsnode), "vnode's nfsnode");
+ flag = np->n_flag;
+ if (flag & NFLUSHWANT)
+ *flags++ = 'W';
+ if (flag & NFLUSHINPROG)
+ *flags++ = 'P';
+ if (flag & NMODIFIED)
+ *flags++ = 'M';
+ if (flag & NWRITEERR)
+ *flags++ = 'E';
+ if (flag & NQNFSNONCACHE)
+ *flags++ = 'X';
+ if (flag & NQNFSWRITE)
+ *flags++ = 'O';
+ if (flag & NQNFSEVICTED)
+ *flags++ = 'G';
+ if (flag == 0)
+ *flags++ = '-';
+ *flags = '\0';
+
+#define VT np->n_vattr
+ (void)printf(" %6d %5s", VT.va_fileid, flagbuf);
+ type = VT.va_mode & S_IFMT;
+ if (S_ISCHR(VT.va_mode) || S_ISBLK(VT.va_mode))
+ if (usenumflag || ((name = devname(VT.va_rdev, type)) == NULL))
+ (void)printf(" %2d,%-2d",
+ major(VT.va_rdev), minor(VT.va_rdev));
+ else
+ (void)printf(" %7s", name);
+ else
+ (void)printf(" %7qd", np->n_size);
+ return (0);
+}
+
+void
+union_header()
+{
+ (void)printf(" UPPER LOWER");
+}
+
+int
+union_print(vp)
+ struct vnode *vp;
+{
+ struct union_node unode, *up = &unode;
+
+ KGETRET(VTOUNION(vp), &unode, sizeof(unode), "vnode's unode");
+
+ (void)printf(" %8x %8x", up->un_uppervp, up->un_lowervp);
+ return (0);
+}
+
+/*
+ * Given a pointer to a mount structure in kernel space,
+ * read it in and return a usable pointer to it.
+ */
+struct mount *
+getmnt(maddr)
+ struct mount *maddr;
+{
+ static struct mtab {
+ struct mtab *next;
+ struct mount *maddr;
+ struct mount mount;
+ } *mhead = NULL;
+ struct mtab *mt;
+
+ for (mt = mhead; mt != NULL; mt = mt->next)
+ if (maddr == mt->maddr)
+ return (&mt->mount);
+ if ((mt = malloc(sizeof(struct mtab))) == NULL)
+ errx(1, "malloc");
+ KGETRET(maddr, &mt->mount, sizeof(struct mount), "mount table");
+ mt->maddr = maddr;
+ mt->next = mhead;
+ mhead = mt;
+ return (&mt->mount);
+}
+
+void
+mount_print(mp)
+ struct mount *mp;
+{
+ int flags;
+ const char *type;
+
+#define ST mp->mnt_stat
+ (void)printf("*** MOUNT %s %s on %s", ST.f_fstypename,
+ ST.f_mntfromname, ST.f_mntonname);
+ if ((flags = mp->mnt_flag)) {
+ int i;
+ const char *sep = " (";
+
+ for (i = 0; mnt_flags[i].m_flag; i++) {
+ if (flags & mnt_flags[i].m_flag) {
+ (void)printf("%s%s", sep, mnt_flags[i].m_name);
+ flags &= ~mnt_flags[i].m_flag;
+ sep = ",";
+ }
+ }
+ if (flags)
+ (void)printf("%sunknown_flags:%x", sep, flags);
+ (void)printf(")");
+ }
+ (void)printf("\n");
+#undef ST
+}
+
+struct e_vnode *
+loadvnodes(avnodes)
+ int *avnodes;
+{
+ int mib[2];
+ size_t copysize;
+ struct e_vnode *vnodebase;
+
+ if (memf != NULL) {
+ /*
+ * do it by hand
+ */
+ return (kinfo_vnodes(avnodes));
+ }
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_VNODE;
+ if (sysctl(mib, 2, NULL, &copysize, NULL, 0) == -1)
+ err(1, "sysctl: KERN_VNODE");
+ if ((vnodebase = malloc(copysize)) == NULL)
+ errx(1, "malloc");
+ if (sysctl(mib, 2, vnodebase, &copysize, NULL, 0) == -1)
+ err(1, "sysctl: KERN_VNODE");
+ if (copysize % sizeof(struct e_vnode))
+ errx(1, "vnode size mismatch");
+ *avnodes = copysize / sizeof(struct e_vnode);
+
+ return (vnodebase);
+}
+
+/*
+ * simulate what a running kernel does in in kinfo_vnode
+ */
+struct e_vnode *
+kinfo_vnodes(avnodes)
+ int *avnodes;
+{
+ struct mntlist mountlist;
+ struct mount *mp, mount, *mp_next;
+ struct vnode *vp, vnode, *vp_next;
+ char *vbuf, *evbuf, *bp;
+ int num, numvnodes;
+
+#define VPTRSZ sizeof(struct vnode *)
+#define VNODESZ sizeof(struct vnode)
+
+ KGET(V_NUMV, numvnodes);
+ if ((vbuf = malloc((numvnodes + 20) * (VPTRSZ + VNODESZ))) == NULL)
+ errx(1, "malloc");
+ bp = vbuf;
+ evbuf = vbuf + (numvnodes + 20) * (VPTRSZ + VNODESZ);
+ KGET(V_MOUNTLIST, mountlist);
+ for (num = 0, mp = mountlist.cqh_first; ; mp = mp_next) {
+ KGET2(mp, &mount, sizeof(mount), "mount entry");
+ mp_next = mount.mnt_list.cqe_next;
+ for (vp = mount.mnt_vnodelist.lh_first;
+ vp != NULL; vp = vp_next) {
+ KGET2(vp, &vnode, sizeof(vnode), "vnode");
+ vp_next = vnode.v_mntvnodes.le_next;
+ if ((bp + VPTRSZ + VNODESZ) > evbuf)
+ /* XXX - should realloc */
+ errx(1, "no more room for vnodes");
+ memmove(bp, &vp, VPTRSZ);
+ bp += VPTRSZ;
+ memmove(bp, &vnode, VNODESZ);
+ bp += VNODESZ;
+ num++;
+ }
+ if (mp == mountlist.cqh_last)
+ break;
+ }
+ *avnodes = num;
+ return ((struct e_vnode *)vbuf);
+}
+
+char hdr[]=" LINE RAW CAN OUT HWT LWT COL STATE SESS PGID DISC\n";
+int ttyspace = 128;
+
+void
+ttymode()
+{
+ struct tty *tty;
+
+ if ((tty = malloc(ttyspace * sizeof(*tty))) == NULL)
+ errx(1, "malloc");
+#if !defined(hp300) && !defined(mips)
+ if (nl[SCONS].n_type != 0) {
+ (void)printf("1 console\n");
+ KGET(SCONS, *tty);
+ (void)printf(hdr);
+ ttyprt(&tty[0], 0);
+ }
+#endif
+#ifdef vax
+ if (nl[SNQD].n_type != 0)
+ qdss();
+ if (nl[SNDZ].n_type != 0)
+ ttytype(tty, "dz", SDZ, SNDZ, 0);
+ if (nl[SNDH].n_type != 0)
+ ttytype(tty, "dh", SDH, SNDH, 0);
+ if (nl[SNDMF].n_type != 0)
+ ttytype(tty, "dmf", SDMF, SNDMF, 0);
+ if (nl[SNDHU].n_type != 0)
+ ttytype(tty, "dhu", SDHU, SNDHU, 0);
+ if (nl[SNDMZ].n_type != 0)
+ ttytype(tty, "dmz", SDMZ, SNDMZ, 0);
+#endif
+#ifdef tahoe
+ if (nl[SNVX].n_type != 0)
+ ttytype(tty, "vx", SVX, SNVX, 0);
+ if (nl[SNMP].n_type != 0)
+ ttytype(tty, "mp", SMP, SNMP, 0);
+#endif
+#ifdef hp300
+ if (nl[SNITE].n_type != 0)
+ ttytype(tty, "ite", SITE, SNITE, 0);
+ if (nl[SNDCA].n_type != 0)
+ ttytype(tty, "dca", SDCA, SNDCA, 0);
+ if (nl[SNDCM].n_type != 0)
+ ttytype(tty, "dcm", SDCM, SNDCM, 0);
+ if (nl[SNDCL].n_type != 0)
+ ttytype(tty, "dcl", SDCL, SNDCL, 0);
+#endif
+#ifdef mips
+ if (nl[SNDC].n_type != 0)
+ ttytype(tty, "dc", SDC, SNDC, 0);
+#endif
+#ifdef __FreeBSD__
+ if (nl[NSCCONS].n_type != 0)
+ ttytype(tty, "vty", SCCONS, NSCCONS, 0);
+ if (nl[NSIO].n_type != 0)
+ ttytype(tty, "sio", SIO, NSIO, 0);
+ if (nl[NRC].n_type != 0)
+ ttytype(tty, "rc", RC, NRC, 0);
+ if (nl[NCY].n_type != 0)
+ ttytype(tty, "cy", CY, NCY, 0);
+ if (nl[NSI].n_type != 0)
+ ttytype(tty, "si", SI, NSI, 1);
+#endif
+ if (nl[SNPTY].n_type != 0)
+ ttytype(tty, "pty", SPTY, SNPTY, 0);
+}
+
+void
+ttytype(tty, name, type, number, indir)
+ struct tty *tty;
+ char *name;
+ int type, number, indir;
+{
+ struct tty *tp;
+ int ntty;
+ struct tty **ttyaddr;
+
+ if (tty == NULL)
+ return;
+ KGET(number, ntty);
+ (void)printf("%d %s %s\n", ntty, name, (ntty == 1) ? "line" : "lines");
+ if (ntty > ttyspace) {
+ ttyspace = ntty;
+ if ((tty = realloc(tty, ttyspace * sizeof(*tty))) == 0)
+ errx(1, "realloc");
+ }
+ if (indir) {
+ KGET(type, ttyaddr);
+ KGET2(ttyaddr, tty, ntty * sizeof(struct tty), "tty structs");
+ } else {
+ KGET1(type, tty, ntty * sizeof(struct tty), "tty structs");
+ }
+ (void)printf(hdr);
+ for (tp = tty; tp < &tty[ntty]; tp++)
+ ttyprt(tp, tp - tty);
+}
+
+struct {
+ int flag;
+ char val;
+} ttystates[] = {
+#ifdef TS_WOPEN
+ { TS_WOPEN, 'W'},
+#endif
+ { TS_ISOPEN, 'O'},
+ { TS_CARR_ON, 'C'},
+#ifdef TS_CONNECTED
+ { TS_CONNECTED, 'c'},
+#endif
+ { TS_TIMEOUT, 'T'},
+ { TS_FLUSH, 'F'},
+ { TS_BUSY, 'B'},
+#ifdef TS_ASLEEP
+ { TS_ASLEEP, 'A'},
+#endif
+#ifdef TS_SO_OLOWAT
+ { TS_SO_OLOWAT, 'A'},
+#endif
+#ifdef TS_SO_OCOMPLETE
+ { TS_SO_OCOMPLETE, 'a'},
+#endif
+ { TS_XCLUDE, 'X'},
+ { TS_TTSTOP, 'S'},
+#ifdef TS_CAR_OFLOW
+ { TS_CAR_OFLOW, 'm'},
+#endif
+#ifdef TS_CTS_OFLOW
+ { TS_CTS_OFLOW, 'o'},
+#endif
+#ifdef TS_DSR_OFLOW
+ { TS_DSR_OFLOW, 'd'},
+#endif
+ { TS_TBLOCK, 'K'},
+ { TS_ASYNC, 'Y'},
+ { TS_BKSL, 'D'},
+ { TS_ERASE, 'E'},
+ { TS_LNCH, 'L'},
+ { TS_TYPEN, 'P'},
+ { TS_CNTTB, 'N'},
+#ifdef TS_CAN_BYPASS_L_RINT
+ { TS_CAN_BYPASS_L_RINT, 'l'},
+#endif
+#ifdef TS_SNOOP
+ { TS_SNOOP, 's'},
+#endif
+#ifdef TS_ZOMBIE
+ { TS_ZOMBIE, 'Z'},
+#endif
+ { 0, '\0'},
+};
+
+void
+ttyprt(tp, line)
+ struct tty *tp;
+ int line;
+{
+ int i, j;
+ pid_t pgid;
+ char *name, state[20];
+
+ if (usenumflag || tp->t_dev == 0 ||
+ (name = devname(tp->t_dev, S_IFCHR)) == NULL)
+ (void)printf("%7d ", line);
+ else
+ (void)printf("%7s ", name);
+ (void)printf("%2d %3d ", tp->t_rawq.c_cc, tp->t_canq.c_cc);
+ (void)printf("%3d %4d %3d %7d ", tp->t_outq.c_cc,
+ tp->t_hiwat, tp->t_lowat, tp->t_column);
+ for (i = j = 0; ttystates[i].flag; i++)
+ if (tp->t_state&ttystates[i].flag)
+ state[j++] = ttystates[i].val;
+ if (j == 0)
+ state[j++] = '-';
+ state[j] = '\0';
+ (void)printf("%-6s %8x", state, (u_long)tp->t_session);
+ pgid = 0;
+ if (tp->t_pgrp != NULL)
+ KGET2(&tp->t_pgrp->pg_id, &pgid, sizeof(pid_t), "pgid");
+ (void)printf("%6d ", pgid);
+ switch (tp->t_line) {
+ case TTYDISC:
+ (void)printf("term\n");
+ break;
+ case NTTYDISC:
+ (void)printf("ntty\n");
+ break;
+ case TABLDISC:
+ (void)printf("tab\n");
+ break;
+ case SLIPDISC:
+ (void)printf("slip\n");
+ break;
+ case PPPDISC:
+ (void)printf("ppp\n");
+ break;
+ default:
+ (void)printf("%d\n", tp->t_line);
+ break;
+ }
+}
+
+void
+filemode()
+{
+ struct file *fp;
+ struct file *addr;
+ char *buf, flagbuf[16], *fbp;
+ int len, maxfile, nfile;
+ static char *dtypes[] = { "???", "inode", "socket" };
+
+ KGET(FNL_MAXFILE, maxfile);
+ if (totalflag) {
+ KGET(FNL_NFILE, nfile);
+ (void)printf("%3d/%3d files\n", nfile, maxfile);
+ return;
+ }
+ if (getfiles(&buf, &len) == -1)
+ return;
+ /*
+ * Getfiles returns in malloc'd memory a pointer to the first file
+ * structure, and then an array of file structs (whose addresses are
+ * derivable from the previous entry).
+ */
+ addr = ((struct filelist *)buf)->lh_first;
+ fp = (struct file *)(buf + sizeof(struct filelist));
+ nfile = (len - sizeof(struct filelist)) / sizeof(struct file);
+
+ (void)printf("%d/%d open files\n", nfile, maxfile);
+ (void)printf(" LOC TYPE FLG CNT MSG DATA OFFSET\n");
+ for (; (char *)fp < buf + len; addr = fp->f_list.le_next, fp++) {
+ if ((unsigned)fp->f_type > DTYPE_SOCKET)
+ continue;
+ (void)printf("%x ", addr);
+ (void)printf("%-8.8s", dtypes[fp->f_type]);
+ fbp = flagbuf;
+ if (fp->f_flag & FREAD)
+ *fbp++ = 'R';
+ if (fp->f_flag & FWRITE)
+ *fbp++ = 'W';
+ if (fp->f_flag & FAPPEND)
+ *fbp++ = 'A';
+#ifdef FSHLOCK /* currently gone */
+ if (fp->f_flag & FSHLOCK)
+ *fbp++ = 'S';
+ if (fp->f_flag & FEXLOCK)
+ *fbp++ = 'X';
+#endif
+ if (fp->f_flag & FASYNC)
+ *fbp++ = 'I';
+ *fbp = '\0';
+ (void)printf("%6s %3d", flagbuf, fp->f_count);
+ (void)printf(" %3d", fp->f_msgcount);
+ (void)printf(" %8.1x", fp->f_data);
+ if (fp->f_offset < 0)
+ (void)printf(" %qx\n", fp->f_offset);
+ else
+ (void)printf(" %qd\n", fp->f_offset);
+ }
+ free(buf);
+}
+
+int
+getfiles(abuf, alen)
+ char **abuf;
+ int *alen;
+{
+ size_t len;
+ int mib[2];
+ char *buf;
+
+ /*
+ * XXX
+ * Add emulation of KINFO_FILE here.
+ */
+ if (memf != NULL)
+ errx(1, "files on dead kernel, not implemented");
+
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_FILE;
+ if (sysctl(mib, 2, NULL, &len, NULL, 0) == -1) {
+ warn("sysctl: KERN_FILE");
+ return (-1);
+ }
+ if ((buf = malloc(len)) == NULL)
+ errx(1, "malloc");
+ if (sysctl(mib, 2, buf, &len, NULL, 0) == -1) {
+ warn("sysctl: KERN_FILE");
+ return (-1);
+ }
+ *abuf = buf;
+ *alen = len;
+ return (0);
+}
+
+/*
+ * swapmode is based on a program called swapinfo written
+ * by Kevin Lahey <kml@rokkaku.atl.ga.us>.
+ */
+void
+swapmode()
+{
+ char *header, *p;
+ int hlen, nswap, nswdev, dmmax;
+ int i, div, avail, nfree, npfree, used;
+ struct swdevt *sw;
+ long blocksize, *perdev;
+ struct rlist head;
+ struct rlisthdr swaplist;
+ struct rlist *swapptr;
+ u_long ptr;
+
+ KGET(VM_NSWAP, nswap);
+ KGET(VM_NSWDEV, nswdev);
+ KGET(VM_DMMAX, dmmax);
+ KGET1(VM_SWAPLIST, &swaplist, sizeof swaplist, "swaplist");
+ if ((sw = malloc(nswdev * sizeof(*sw))) == NULL ||
+ (perdev = malloc(nswdev * sizeof(*perdev))) == NULL)
+ errx(1, "malloc");
+ KGET1(VM_SWDEVT, &ptr, sizeof ptr, "swdevt");
+ KGET2(ptr, sw, nswdev * sizeof(*sw), "*swdevt");
+
+ /* Count up swap space. */
+ nfree = 0;
+ memset(perdev, 0, nswdev * sizeof(*perdev));
+ swapptr = swaplist.rlh_list;
+ while (swapptr) {
+ int top, bottom, next_block;
+
+ KGET2(swapptr, &head, sizeof(struct rlist), "swapptr");
+
+ top = head.rl_end;
+ bottom = head.rl_start;
+
+ nfree += top - bottom + 1;
+
+ /*
+ * Swap space is split up among the configured disks.
+ *
+ * For interleaved swap devices, the first dmmax blocks
+ * of swap space some from the first disk, the next dmmax
+ * blocks from the next, and so on up to nswap blocks.
+ *
+ * The list of free space joins adjacent free blocks,
+ * ignoring device boundries. If we want to keep track
+ * of this information per device, we'll just have to
+ * extract it ourselves.
+ */
+ while (top / dmmax != bottom / dmmax) {
+ next_block = ((bottom + dmmax) / dmmax);
+ perdev[(bottom / dmmax) % nswdev] +=
+ next_block * dmmax - bottom;
+ bottom = next_block * dmmax;
+ }
+ perdev[(bottom / dmmax) % nswdev] +=
+ top - bottom + 1;
+
+ swapptr = head.rl_next;
+ }
+
+ header = getbsize(&hlen, &blocksize);
+ if (!totalflag)
+ (void)printf("%-11s %*s %8s %8s %8s %s\n",
+ "Device", hlen, header,
+ "Used", "Avail", "Capacity", "Type");
+ div = blocksize / 512;
+ avail = npfree = 0;
+ for (i = 0; i < nswdev; i++) {
+ int xsize, xfree;
+
+ /*
+ * Don't report statistics for partitions which have not
+ * yet been activated via swapon(8).
+ */
+ if (!(sw[i].sw_flags & SW_FREED))
+ continue;
+
+ if (!totalflag)
+ if (sw[i].sw_dev != NODEV) {
+ p = devname(sw[i].sw_dev, S_IFBLK);
+ (void)printf("/dev/%-6s %*d ",
+ p == NULL ? "??" : p,
+ hlen, sw[i].sw_nblks / div);
+ } else
+ (void)printf("[NFS swap] %*d ",
+ hlen, sw[i].sw_nblks / div);
+
+ /* The first dmmax is never allocated to avoid trashing of
+ * disklabels
+ */
+ xsize = sw[i].sw_nblks - dmmax;
+ xfree = perdev[i];
+ used = xsize - xfree;
+ npfree++;
+ avail += xsize;
+ if (totalflag)
+ continue;
+ (void)printf("%8d %8d %5.0f%% %s\n",
+ used / div, xfree / div,
+ (double)used / (double)xsize * 100.0,
+ (sw[i].sw_flags & SW_SEQUENTIAL) ?
+ "Sequential" : "Interleaved");
+ }
+
+ /*
+ * If only one partition has been set up via swapon(8), we don't
+ * need to bother with totals.
+ */
+ used = avail - nfree;
+ free(sw);
+ free(perdev);
+ if (totalflag) {
+ (void)printf("%dM/%dM swap space\n", used / 2048, avail / 2048);
+ return;
+ }
+ if (npfree > 1) {
+ (void)printf("%-11s %*d %8d %8d %5.0f%%\n",
+ "Total", hlen, avail / div, used / div, nfree / div,
+ (double)used / (double)avail * 100.0);
+ }
+}
diff --git a/usr.sbin/pw/Makefile b/usr.sbin/pw/Makefile
new file mode 100644
index 0000000..31b58b5
--- /dev/null
+++ b/usr.sbin/pw/Makefile
@@ -0,0 +1,19 @@
+# $Id$
+
+PROG= pw
+SRCS= pw.c pw_conf.c pw_user.c pw_group.c pw_log.c pw_nis.c \
+ grupd.c pwupd.c fileupd.c edgroup.c psdate.c \
+ bitmap.c cpdir.c rm_r.c
+
+MAN5= pw.conf.5
+MAN8= pw.8
+
+#RND= -DUSE_MD5RAND
+CFLAGS+= -Wall $(CDB) $(RND)
+LDADD= -lcrypt
+DPADD= ${LIBCRYPT}
+
+BINOWN= root
+BINMODE=0555
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/pw/README b/usr.sbin/pw/README
new file mode 100644
index 0000000..9302f6b
--- /dev/null
+++ b/usr.sbin/pw/README
@@ -0,0 +1,22 @@
+
+pw is a command-line driven passwd/group editor utility that provides
+an easy and safe means of modifying of any/all fields in the system
+password files, and has an add, modify and delete mode for user and
+group records. Command line options have been fashioned to be similar
+to those used by the Sun/shadow commands: useradd, usermod, userdel,
+groupadd, groupmod, groupdel, but combines all operations within the
+single command `pw'.
+
+User add mode also provides a means of easily setting system useradd
+defaults (see pw.conf.5), so that adding a user is as easy as issuing
+the command "pw useradd <loginid>". Creation of a unique primary
+group for each user and automatic memberhip in secondary groups
+is fully supported.
+
+This program may be FreBSD specific, but should be trivial to port to
+other bsd4.4 variants.
+
+Author and maintainer: David L. Nugent, <davidn@blaze.net.au>
+
+$Id$
+
diff --git a/usr.sbin/pw/bitmap.c b/usr.sbin/pw/bitmap.c
new file mode 100644
index 0000000..bb55c5b
--- /dev/null
+++ b/usr.sbin/pw/bitmap.c
@@ -0,0 +1,132 @@
+/*-
+ * Copyright (C) 1996
+ * David L. Nugent. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY DAVID L. NUGENT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL DAVID L. NUGENT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "bitmap.h"
+
+struct bitmap
+bm_alloc(int size)
+{
+ struct bitmap bm;
+ int szmap = (size / 8) + !!(size % 8);
+
+ bm.size = size;
+ bm.map = malloc(szmap);
+ if (bm.map)
+ memset(bm.map, 0, szmap);
+ return bm;
+}
+
+void
+bm_dealloc(struct bitmap * bm)
+{
+ if (bm->map)
+ free(bm->map);
+}
+
+static void
+bm_getmask(int *pos, unsigned char *bmask)
+{
+ *bmask = (unsigned char) (1 << (*pos % 8));
+ *pos /= 8;
+}
+
+void
+bm_setbit(struct bitmap * bm, int pos)
+{
+ unsigned char bmask;
+
+ bm_getmask(&pos, &bmask);
+ bm->map[pos] |= bmask;
+}
+
+void
+bm_clrbit(struct bitmap * bm, int pos)
+{
+ unsigned char bmask;
+
+ bm_getmask(&pos, &bmask);
+ bm->map[pos] &= ~bmask;
+}
+
+int
+bm_isset(struct bitmap * bm, int pos)
+{
+ unsigned char bmask;
+
+ bm_getmask(&pos, &bmask);
+ return !!(bm->map[pos] & bmask);
+}
+
+int
+bm_firstunset(struct bitmap * bm)
+{
+ int szmap = (bm->size / 8) + !!(bm->size % 8);
+ int at = 0;
+ int pos = 0;
+
+ while (pos < szmap) {
+ unsigned char bmv = bm->map[pos++];
+ unsigned char bmask = 1;
+
+ while (bmask & 0xff) {
+ if ((bmv & bmask) == 0)
+ return at;
+ bmask <<= 1;
+ ++at;
+ }
+ }
+ return at;
+}
+
+int
+bm_lastset(struct bitmap * bm)
+{
+ int szmap = (bm->size / 8) + !!(bm->size % 8);
+ int at = 0;
+ int pos = 0;
+ int ofs = 0;
+
+ while (pos < szmap) {
+ unsigned char bmv = bm->map[pos++];
+ unsigned char bmask = 1;
+
+ while (bmask & 0xff) {
+ if ((bmv & bmask) != 0)
+ ofs = at;
+ bmask <<= 1;
+ ++at;
+ }
+ }
+ return ofs;
+}
diff --git a/usr.sbin/pw/bitmap.h b/usr.sbin/pw/bitmap.h
new file mode 100644
index 0000000..a2ef01a
--- /dev/null
+++ b/usr.sbin/pw/bitmap.h
@@ -0,0 +1,50 @@
+/*-
+ * Copyright (C) 1996
+ * David L. Nugent. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY DAVID L. NUGENT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL DAVID L. NUGENT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id$
+ */
+
+#ifndef _BITMAP_H_
+#define _BITMAP_H_
+
+#include <sys/cdefs.h>
+
+struct bitmap
+{
+ int size;
+ unsigned char *map;
+};
+
+__BEGIN_DECLS
+struct bitmap bm_alloc __P((int size));
+void bm_dealloc __P((struct bitmap * bm));
+void bm_setbit __P((struct bitmap * bm, int pos));
+void bm_clrbit __P((struct bitmap * bm, int pos));
+int bm_isset __P((struct bitmap * bm, int pos));
+int bm_firstunset __P((struct bitmap * bm));
+int bm_lastset __P((struct bitmap * bm));
+__END_DECLS
+
+#endif /* !_BITMAP_H */
diff --git a/usr.sbin/pw/cpdir.c b/usr.sbin/pw/cpdir.c
new file mode 100644
index 0000000..048a87b
--- /dev/null
+++ b/usr.sbin/pw/cpdir.c
@@ -0,0 +1,116 @@
+/*-
+ * Copyright (C) 1996
+ * David L. Nugent. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY DAVID L. NUGENT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL DAVID L. NUGENT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <dirent.h>
+
+#include "pwupd.h"
+
+void
+copymkdir(char const * dir, char const * skel, mode_t mode, uid_t uid, gid_t gid)
+{
+ int rc = 0;
+ char src[MAXPATHLEN];
+ char dst[MAXPATHLEN];
+
+ if (mkdir(dir, mode) != 0 && errno != EEXIST) {
+ warn("mkdir(%s)", dir);
+ } else {
+ int infd, outfd;
+ struct stat st;
+
+ static char counter = 0;
+ static char *copybuf = NULL;
+
+ ++counter;
+ chown(dir, uid, gid);
+ if (skel == NULL || *skel == '\0')
+ rc = 1;
+ else {
+ DIR *d = opendir(skel);
+
+ if (d != NULL) {
+ struct dirent *e;
+
+ while ((e = readdir(d)) != NULL) {
+ char *p = e->d_name;
+
+ sprintf(src, "%s/%s", skel, p);
+ if (stat(src, &st) == 0) {
+ if (strncmp(p, "dot.", 4) == 0) /* Conversion */
+ p += 3;
+ sprintf(dst, "%s/%s", dir, p);
+ if (S_ISDIR(st.st_mode)) { /* Recurse for this */
+ if (strcmp(e->d_name, ".") != 0 && strcmp(e->d_name, "..") != 0)
+ copymkdir(dst, src, (st.st_mode & 0777), uid, gid);
+ /*
+ * Note: don't propogate 'special' attributes
+ */
+ } else if (S_ISREG(st.st_mode) && (outfd = open(dst, O_RDWR | O_CREAT | O_EXCL, st.st_mode)) != -1) {
+ if ((infd = open(src, O_RDONLY)) == -1) {
+ close(outfd);
+ remove(dst);
+ } else {
+ int b;
+
+ /*
+ * Allocate our copy buffer if we need to
+ */
+ if (copybuf == NULL)
+ copybuf = malloc(4096);
+ while ((b = read(infd, copybuf, 4096)) > 0)
+ write(outfd, copybuf, b);
+ close(infd);
+ close(outfd);
+ chown(dst, uid, gid);
+ }
+ }
+ }
+ }
+ closedir(d);
+ }
+ }
+ if (--counter == 0 && copybuf != NULL) {
+ free(copybuf);
+ copybuf = NULL;
+ }
+ }
+}
+
diff --git a/usr.sbin/pw/edgroup.c b/usr.sbin/pw/edgroup.c
new file mode 100644
index 0000000..c927b06
--- /dev/null
+++ b/usr.sbin/pw/edgroup.c
@@ -0,0 +1,226 @@
+/*-
+ * Copyright (C) 1996
+ * David L. Nugent. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY DAVID L. NUGENT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL DAVID L. NUGENT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <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;
+}
+
+static char groupfile[] = _PATH_GROUP;
+static char grouptmp[] = _PATH_GROUP ".new";
+
+int
+editgroups(char *name, char **groups)
+{
+ int rc = 0;
+ int infd;
+
+ if ((infd = open(groupfile, O_RDWR | O_CREAT | O_EXLOCK, 0644)) != -1) {
+ FILE *infp;
+
+ if ((infp = fdopen(infd, "r+")) == NULL)
+ close(infd);
+ else {
+ int outfd;
+
+ if ((outfd = open(grouptmp, O_RDWR | O_CREAT | O_TRUNC | 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 && pwd->pw_gid != 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..3782bf7
--- /dev/null
+++ b/usr.sbin/pw/fileupd.c
@@ -0,0 +1,191 @@
+/*-
+ * Copyright (C) 1996
+ * David L. Nugent. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY DAVID L. NUGENT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL DAVID L. NUGENT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include "pwupd.h"
+
+int
+extendline(char **buf, int * buflen, int needed)
+{
+ if (needed > *buflen) {
+ char *tmp = realloc(*buf, needed);
+ if (tmp == NULL)
+ return -1;
+ *buf = tmp;
+ *buflen = needed;
+ }
+ return *buflen;
+}
+
+int
+extendarray(char ***buf, int * buflen, int needed)
+{
+ if (needed > *buflen) {
+ char **tmp = realloc(*buf, needed * sizeof(char *));
+ if (tmp == NULL)
+ return -1;
+ *buf = tmp;
+ *buflen = needed;
+ }
+ return *buflen;
+}
+
+
+int
+fileupdate(char const * filename, mode_t fmode, char const * newline, char const * prefix, int pfxlen, int updmode)
+{
+ int rc = 0;
+
+ if (pfxlen <= 1)
+ errno = EINVAL;
+ else {
+ int infd = open(filename, O_RDWR | O_CREAT | O_EXLOCK, fmode);
+
+ if (infd != -1) {
+ FILE *infp = fdopen(infd, "r+");
+
+ if (infp == NULL)
+ close(infd);
+ else {
+ int outfd;
+ char file[MAXPATHLEN];
+
+ strcpy(file, filename);
+ strcat(file, ".new");
+ outfd = open(file, O_RDWR | O_CREAT | O_TRUNC | O_EXLOCK, fmode);
+ if (outfd != -1) {
+ FILE *outfp = fdopen(outfd, "w+");
+
+ if (outfp == NULL)
+ close(outfd);
+ else {
+ int updated = UPD_CREATE;
+ int linesize = PWBUFSZ;
+ char *line = malloc(linesize);
+
+ nextline:
+ while (fgets(line, linesize, infp) != NULL) {
+ char *p = strchr(line, '\n');
+
+ while ((p = strchr(line, '\n')) == NULL) {
+ int l;
+ if (extendline(&line, &linesize, linesize + PWBUFSZ) == -1) {
+ int ch;
+ fputs(line, outfp);
+ while ((ch = fgetc(infp)) != EOF) {
+ fputc(ch, outfp);
+ if (ch == '\n')
+ break;
+ }
+ goto nextline;
+ }
+ l = strlen(line);
+ if (fgets(line + l, linesize - l, infp) == NULL)
+ break;
+ }
+ if (*line != '#' && *line != '\n') {
+ if (!updated && strncmp(line, prefix, pfxlen) == 0) {
+ updated = updmode == UPD_REPLACE ? UPD_REPLACE : UPD_DELETE;
+
+ /*
+ * Only actually write changes if updating
+ */
+ if (updmode == UPD_REPLACE)
+ strcpy(line, newline);
+ else if (updmode == UPD_DELETE)
+ continue;
+ }
+ }
+ fputs(line, outfp);
+ }
+
+ /*
+ * Now, we need to decide what to do: If we are in
+ * update mode, and no record was updated, then error If
+ * we are in insert mode, and record already exists,
+ * then error
+ */
+ if (updmode != updated)
+ errno = (updmode == UPD_CREATE) ? EEXIST : ENOENT;
+ else {
+
+ /*
+ * If adding a new record, append it to the end
+ */
+ if (updmode == UPD_CREATE)
+ fputs(newline, outfp);
+
+ /*
+ * Flush the file and check for the result
+ */
+ rc = fflush(outfp) != EOF;
+ if (rc) {
+
+ /*
+ * Copy data back into the
+ * original file and truncate
+ */
+ rewind(infp);
+ rewind(outfp);
+ while (fgets(line, linesize, outfp) != NULL)
+ fputs(line, infp);
+
+ /*
+ * This is a gross hack, but we may have
+ * corrupted the original file
+ * Unfortunately, it will lose the inode.
+ */
+ if (fflush(infp) == EOF || ferror(infp))
+ rc = rename(file, filename) == 0;
+ 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..feff430
--- /dev/null
+++ b/usr.sbin/pw/grupd.c
@@ -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.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id$";
+#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 "pwupd.h"
+
+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(_PATH_GROUP, 0644, grbuf, pfx, l, mode);
+ 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..9b9cf78
--- /dev/null
+++ b/usr.sbin/pw/psdate.c
@@ -0,0 +1,300 @@
+/*-
+ * Copyright (C) 1996
+ * David L. Nugent. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY DAVID L. NUGENT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL DAVID L. NUGENT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "psdate.h"
+
+
+static int
+a2i(char const ** str)
+{
+ int i = 0;
+ char const *s = *str;
+
+ if (isdigit(*s)) {
+ i = atoi(s);
+ while (isdigit(*s))
+ ++s;
+ *str = s;
+ }
+ return i;
+}
+
+static int
+numerics(char const * str)
+{
+ int rc = isdigit(*str);
+
+ if (rc)
+ while (isdigit(*str) || *str == 'x')
+ ++str;
+ return rc && !*str;
+}
+
+static int
+aindex(char const * arr[], char const ** str, int len)
+{
+ int l, i;
+ char mystr[32];
+
+ mystr[len] = '\0';
+ l = strlen(strncpy(mystr, *str, len));
+ for (i = 0; i < l; i++)
+ mystr[i] = (char) tolower(mystr[i]);
+ for (i = 0; arr[i] && strcmp(mystr, arr[i]) != 0; i++);
+ if (arr[i] == NULL)
+ i = -1;
+ else { /* Skip past it */
+ while (**str && isalpha(**str))
+ ++(*str);
+ /* And any following whitespace */
+ while (**str && (**str == ',' || isspace(**str)))
+ ++(*str);
+ } /* Return index */
+ return i;
+}
+
+static int
+weekday(char const ** str)
+{
+ static char const *days[] =
+ {"sun", "mon", "tue", "wed", "thu", "fri", "sat", NULL};
+
+ return aindex(days, str, 3);
+}
+
+static int
+month(char const ** str)
+{
+ static char const *months[] =
+ {"jan", "feb", "mar", "apr", "may", "jun", "jul",
+ "aug", "sep", "oct", "nov", "dec", NULL};
+
+ return aindex(months, str, 3);
+}
+
+static void
+parse_time(char const * str, int *hour, int *min, int *sec)
+{
+ *hour = a2i(&str);
+ if ((str = strchr(str, ':')) == NULL)
+ *min = *sec = 0;
+ else {
+ ++str;
+ *min = a2i(&str);
+ *sec = ((str = strchr(str, ':')) == NULL) ? 0 : atoi(++str);
+ }
+}
+
+
+static void
+parse_datesub(char const * str, int *day, int *mon, int *year)
+{
+ int i;
+
+ static char const nchrs[] = "0123456789 \t,/-.";
+
+ if ((i = month(&str)) != -1) {
+ *mon = i;
+ if ((i = a2i(&str)) != 0)
+ *day = i;
+ } else if ((i = a2i(&str)) != 0) {
+ *day = i;
+ while (*str && strchr(nchrs + 10, *str) != NULL)
+ ++str;
+ if ((i = month(&str)) != -1)
+ *mon = i;
+ else if ((i = a2i(&str)) != 0)
+ *mon = i - 1;
+ } else
+ return;
+
+ while (*str && strchr(nchrs + 10, *str) != NULL)
+ ++str;
+ if (isdigit(*str)) {
+ *year = atoi(str);
+ if (*year > 1900)
+ *year -= 1900;
+ else if (*year < 32)
+ *year += 100;
+ }
+}
+
+
+/*-
+ * Parse time must be flexible, it handles the following formats:
+ * nnnnnnnnnnn UNIX timestamp (all numeric), 0 = now
+ * 0xnnnnnnnn UNIX timestamp in hexadecimal
+ * 0nnnnnnnnn UNIX timestamp in octal
+ * 0 Given time
+ * +nnnn[smhdwoy] Given time + nnnn hours, mins, days, weeks, months or years
+ * -nnnn[smhdwoy] Given time - nnnn hours, mins, days, weeks, months or years
+ * dd[ ./-]mmm[ ./-]yy Date }
+ * hh:mm:ss Time } May be combined
+ */
+
+time_t
+parse_date(time_t dt, char const * str)
+{
+ char *p;
+ int i;
+ long val;
+ struct tm *T;
+
+ if (dt == 0)
+ dt = time(NULL);
+
+ while (*str && isspace(*str))
+ ++str;
+
+ if (numerics(str)) {
+ val = strtol(str, &p, 0);
+ dt = val ? val : dt;
+ } else if (*str == '+' || *str == '-') {
+ val = strtol(str, &p, 0);
+ switch (*p) {
+ case 'h':
+ case 'H': /* hours */
+ dt += (val * 3600L);
+ break;
+ case '\0':
+ case 'm':
+ case 'M': /* minutes */
+ dt += (val * 60L);
+ break;
+ case 's':
+ case 'S': /* seconds */
+ dt += val;
+ break;
+ case 'd':
+ case 'D': /* days */
+ dt += (val * 86400L);
+ break;
+ case 'w':
+ case 'W': /* weeks */
+ dt += (val * 604800L);
+ break;
+ case 'o':
+ case 'O': /* months */
+ T = localtime(&dt);
+ T->tm_mon += (int) val;
+ i = T->tm_mday;
+ goto fixday;
+ case 'y':
+ case 'Y': /* years */
+ T = localtime(&dt);
+ T->tm_year += (int) val;
+ i = T->tm_mday;
+ fixday:
+ dt = mktime(T);
+ T = localtime(&dt);
+ if (T->tm_mday != i) {
+ T->tm_mday = 1;
+ dt = mktime(T);
+ dt -= (time_t) 86400L;
+ }
+ default: /* unknown */
+ break; /* leave untouched */
+ }
+ } else {
+ char *q, tmp[64];
+
+ /*
+ * Skip past any weekday prefix
+ */
+ weekday(&str);
+ str = strncpy(tmp, str, sizeof tmp - 1);
+ tmp[sizeof tmp - 1] = '\0';
+ T = localtime(&dt);
+
+ /*
+ * See if we can break off any timezone
+ */
+ while ((q = strrchr(tmp, ' ')) != NULL) {
+ if (strchr("(+-", q[1]) != NULL)
+ *q = '\0';
+ else {
+ int j = 1;
+
+ while (q[j] && isupper(q[j]))
+ ++j;
+ if (q[j] == '\0')
+ *q = '\0';
+ else
+ break;
+ }
+ }
+
+ /*
+ * See if there is a time hh:mm[:ss]
+ */
+ if ((p = strchr(tmp, ':')) == NULL) {
+
+ /*
+ * No time string involved
+ */
+ T->tm_hour = T->tm_min = T->tm_sec = 0;
+ parse_datesub(tmp, &T->tm_mday, &T->tm_mon, &T->tm_year);
+ } else {
+ char datestr[64], timestr[64];
+
+ /*
+ * Let's chip off the time string
+ */
+ if ((q = strpbrk(p, " \t")) != NULL) { /* Time first? */
+ int l = q - str;
+
+ strncpy(timestr, str, l);
+ timestr[l] = '\0';
+ strncpy(datestr, q + 1, sizeof datestr);
+ datestr[sizeof datestr - 1] = '\0';
+ parse_time(timestr, &T->tm_hour, &T->tm_min, &T->tm_sec);
+ parse_datesub(datestr, &T->tm_mday, &T->tm_mon, &T->tm_year);
+ } else if ((q = strrchr(tmp, ' ')) != NULL) { /* Time last */
+ int l = q - tmp;
+
+ strncpy(timestr, q + 1, sizeof timestr);
+ timestr[sizeof timestr - 1] = '\0';
+ strncpy(datestr, tmp, l);
+ datestr[l] = '\0';
+ } else /* Bail out */
+ return dt;
+ parse_time(timestr, &T->tm_hour, &T->tm_min, &T->tm_sec);
+ parse_datesub(datestr, &T->tm_mday, &T->tm_mon, &T->tm_year);
+ }
+ dt = mktime(T);
+ }
+ return dt;
+}
diff --git a/usr.sbin/pw/psdate.h b/usr.sbin/pw/psdate.h
new file mode 100644
index 0000000..f0f1522
--- /dev/null
+++ b/usr.sbin/pw/psdate.h
@@ -0,0 +1,40 @@
+/*-
+ * Copyright (C) 1996
+ * David L. Nugent. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY DAVID L. NUGENT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL DAVID L. NUGENT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id$
+ */
+
+#ifndef _PSDATE_H_
+#define _PSDATE_H_
+
+#include <time.h>
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+time_t parse_date __P((time_t dt, char const * str));
+void print_date __P((char *buf, time_t t, int dotime));
+__END_DECLS
+
+#endif /* !_PSDATE_H_ */
diff --git a/usr.sbin/pw/pw.8 b/usr.sbin/pw/pw.8
new file mode 100644
index 0000000..32c90e1
--- /dev/null
+++ b/usr.sbin/pw/pw.8
@@ -0,0 +1,847 @@
+.\" Copyright (C) 1996
+.\" David L. Nugent. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY DAVID L. NUGENT AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL DAVID L. NUGENT OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $Id: pw.8,v 1.10 1997/03/04 07:55:43 danny Exp $
+.\"
+.Dd December 9, 1996
+.Dt PW 8
+.Os
+.Sh NAME
+.Nm pw
+.Nd create, remove, modify & display system users and groups
+.Sh SYNOPSIS
+.Nm pw
+.Ar useradd
+.Op name|uid
+.Op Fl C Ar config
+.Op Fl q
+.Op Fl n Ar name
+.Op Fl u Ar uid
+.Op Fl c Ar comment
+.Op Fl d Ar dir
+.Op Fl e Ar date
+.Op Fl p Ar date
+.Op Fl g Ar group
+.Op Fl G Ar grouplist
+.Op Fl m
+.Op Fl k Ar dir
+.Op Fl s Ar shell
+.Op Fl o
+.Op Fl L Ar class
+.Op Fl h Ar fd
+.Op Fl N
+.Op Fl P
+.Op Fl Y
+.Nm pw
+.Ar useradd
+.Op name|uid
+.Fl D
+.Op Fl C Ar config
+.Op Fl q
+.Op Fl b Ar dir
+.Op Fl e Ar days
+.Op Fl p Ar days
+.Op Fl g Ar group
+.Op Fl G Ar grouplist
+.Op Fl k Ar dir
+.Op Fl u Ar min,max
+.Op Fl i Ar min,max
+.Op Fl w Ar method
+.Op Fl s Ar shell
+.Op Fl y Ar path
+.Nm pw
+.Ar userdel
+.Op name|uid
+.Op Fl n Ar name
+.Op Fl u Ar uid
+.Op Fl r
+.Op Fl Y
+.Nm pw
+.Ar usermod
+.Op name|uid
+.Op Fl C Ar config
+.Op Fl q
+.Op Fl n Ar name
+.Op Fl u Ar uid
+.Op Fl c Ar comment
+.Op Fl d Ar dir
+.Op Fl e Ar date
+.Op Fl p Ar date
+.Op Fl g Ar group
+.Op Fl G Ar grouplist
+.Op Fl l Ar name
+.Op Fl m
+.Op Fl k Ar dir
+.Op Fl w Ar method
+.Op Fl s Ar shell
+.Op Fl L Ar class
+.Op Fl h Ar fd
+.Op Fl N
+.Op Fl P
+.Op Fl Y
+.Nm pw
+.Ar usershow
+.Op name|uid
+.Op Fl n Ar name
+.Op Fl u Ar uid
+.Op Fl F
+.Op Fl P
+.Op Fl a
+.Nm pw
+.Ar usernext
+.Op Fl C Ar config
+.Op Fl q
+.Nm pw
+.Ar groupadd
+.Op group|gid
+.Op Fl C Ar config
+.Op Fl q
+.Op Fl n Ar group
+.Op Fl g Ar gid
+.Op Fl M Ar members
+.Op Fl o
+.Op Fl h Ar fd
+.Op Fl N
+.Op Fl P
+.Op Fl Y
+.Nm pw
+.Ar groupdel
+.Op Fl n Ar name
+.Op Fl g Ar gid
+.Op Fl Y
+.Nm pw
+.Ar groupmod
+.Op Fl C Ar config
+.Op Fl q
+.Op Fl F
+.Op Fl n Ar name
+.Op Fl g Ar gid
+.Op Fl l Ar name
+.Op Fl M Ar members
+.Op Fl m Ar newmembers
+.Op Fl h Ar fd
+.Op Fl N
+.Op Fl P
+.Op Fl Y
+.Nm pw
+.Ar groupshow
+.Op Fl n Ar name
+.Op Fl g Ar gid
+.Op Fl F
+.Op Fl P
+.Op Fl a
+.Nm pw
+.Ar groupnext
+.Op Fl C Ar config
+.Op Fl q
+.Sh DESCRIPTION
+.Nm Pw
+is a command-line based editor for the system
+.Ar user
+and
+.Ar group
+files, allowing the superuser an easy to use and standardized way of adding,
+modifying and removing users and groups.
+Note that
+.Nm
+only operates on the local user and group files; NIS users and groups must be
+maintained on the NIS server.
+.Nm Pw
+handles updating the
+.Pa passwd ,
+.Pa master.passwd ,
+.Pa group
+and the secure and insecure
+password database files, and must be run as root.
+.Pp
+The first one or two keywords provided on
+.Xr pw 8 's
+command line provide the context for the remainder of the arguments.
+One of the keywords
+.Ar user
+and
+.Ar group
+may be combined or provided separately with
+.Ar add ,
+.Ar del ,
+.Ar mod ,
+.Ar show ,
+or
+.Ar next ,
+and may be specified in either order (ie. showuser, usershow, show user and user show
+are all considered to be the same thing).
+This flexibility is useful for interactive scripts which call
+.Nm
+for the actual 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 all or most modes of operation:
+.Pp
+.Bl -tag -width "-G grouplist"
+.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,
+and the
+.Fl C
+option specifies a different configuration file.
+Most of the contents in the configuration file may be overridden via command line
+options, but it may be more useful to set up standard information for addition of
+new accounts in the 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 add and modify operations, and causes
+.Nm
+to skip updating the user/group databases and instead print the result
+of the operation without actually performing it.
+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 the 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 pw
+will automatically update it concurrently with the system password
+databases.
+.El
+.Pp
+.Sh USER OPTIONS
+The following options apply to the
+.Ar useradd ,
+and
+.Ar usermod ,
+commands:
+.Pp
+.Bl -tag -width "-G grouplist"
+.It Fl n Ar name
+Specify the user/account name.
+.It Fl u Ar uid
+Specify the user/account numeric id.
+.Pp
+Usually, you need only to provide one or the other of these options, as the account
+name will imply the uid, and vice versa.
+Also, you may provide either the account or userid immediately after the
+.Ar useradd ,
+.Ar userdel ,
+.Ar usermod
+or
+.Ar usershow
+keyword on the command line without the need to use
+.Ql Fl n
+or
+.Ql Fl u .
+There are times, however, were 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 on
+.Ar useradd ,
+then you should
+.Em not
+use the
+.Ql Fl u
+option.
+.El
+.Pp
+Options available with both
+.Ar useradd
+and
+.Ar usermod
+are:
+.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,
+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 in the passwd file.
+.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 (which is determined from pw.conf, which specifies the base home directory
+- 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 expiry date is to be set.
+.It Fl p Ar date
+Set the account's password expiration date.
+This field is identical to the account expiration date option, except that it
+applies to forced password changes.
+The same formats are accepted as with the account expiration option.
+.It Fl g Ar group
+Set the account's primary group to the given group.
+.Ar group
+may be either the group name or its corresponding group id number.
+.It Fl G Ar grouplist
+Sets the additional groups to which an account belongs.
+.Ar grouplist
+is a comma-separated list or group names or group ids.
+When adding a user, the user's name is added to the group lists in
+.Pa /etc/group ,
+and when editing a user, the user's name is also added to the group lists, and
+removed from any groups not specified in
+.Ar grouplist .
+Note: a user should not be added to their primary group in
+.Pa /etc/group .
+Also, group membership changes do not take effect immediately for current logins,
+only logins subsequent to the change.
+.It Fl L Ar class
+This option sets the login class for the user being created.
+See
+.Xr login.conf 5
+for more information on user classes.
+.It Fl m
+This option instructs
+.Nm
+to attempt to create the user's home directory.
+While primarily useful when adding a new account with
+.Ar useradd ,
+this may also be of use when moving an existing user's home directory elsewhere on
+the filesystem.
+The new home directory is populated with the contents of the
+.Ar skeleton
+directory, which typically contains a set of shell configuration files that the
+user may personalize to taste.
+When
+.Ql Fl m
+is used on an account with
+.Ar usermod ,
+any existing configuration files in the user's home directory are
+.Em not
+overwritten with the prototype files.
+.Pp
+When a user's home directory is created, it will be default be as a subdirectory of the
+.Ar basehome
+directory specified with the
+.Ql Fl b Ar dir
+option (see below), and will be named the same as the account.
+This may be overridden with the
+.Ql Fl d Ar dir
+option on the command line, if desired.
+.It Fl k Ar dir
+Set the
+.Ar skeleton
+subdirectory, from which the basic startup and configuration files are copied when
+the user's home directory is created.
+This option only has meaning when used with
+.Ql Fl D
+(see below) or
+.Ql Fl m .
+.It Fl s Ar shell
+Set or changes the user's login shell to
+.Ar shell .
+If the path to the shell program is omitted,
+.Nm
+searches the
+.Ar shellpath
+specified in
+.Pa /etc/pw.conf
+and fills it in as appropriate.
+Note that unless you have a specific reason to do so, you should avoid
+specifying the path - this will allow
+.Nm
+to validate that the program exists and is executable.
+Specifying a full path (or supplying a blank "" shell) avoids this check
+and allows for such entries as
+.Pa /nonexistent
+that should be set for accounts not intended for interactive login.
+.It Fl L Ar class
+Set the
+.Em class
+field in the user's passwd record.
+This field is not currently used, but will be in the future used to specify a
+.Em termcap
+entry like tag (see
+.Xr passwd 5
+for details).
+.It Fl h Ar fd
+This option provides a special interface by which interactive scripts can
+set an account password using
+.Nm pw .
+Because the command line and environment are fundamental 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 posses mechanisms by which this can be done.
+Alternatively,
+.Nm pw
+will prompt for the user's password if
+.Ql Fl h Ar 0
+is given, nominating
+.Em stdin
+as the file descriptor on which to read the password.
+Note that this password will be read once and once only and is intended
+for use by a script or similar rather than interactive use.
+If you wish to have new password confirmation along the lines of
+.Xr passwd 1 ,
+this must be implemented as part of the interactive script that calls
+.Nm pw .
+.Pp
+If a value of
+.Ql \&-
+is given as the argument
+.Ar fd ,
+then the password will be set to
+.Ql \&* ,
+rendering the account inaccessible via passworded login.
+.El
+.Pp
+It is possible to use
+.Ar useradd
+to create a new account that duplicates an existing user id.
+While this is normally considered an error and will be rejected, the
+.Ql Fl o
+option overrides the check for duplicates and allows the duplication of
+the user id.
+This may be useful if you allow the same user to login under
+different contexts (different group allocations, different home
+directory, different shell) while providing basically the same
+permissions for access to the user's files in each account.
+.Pp
+The
+.Ar useradd
+command also has the ability to set new user and group defaults by using the
+.Ql Fl D
+option.
+Instead of adding a new user,
+.Nm
+writes a new set of defaults to its configuration file,
+.Pa /etc/pw.conf .
+When using the
+.Ql Fl D
+option, you must not use either
+.Ql Fl n Ar name
+or
+.Ql Fl u Ar uid
+or an error will result.
+Use of
+.Ql Fl D
+changes the meaning of several command line switches in the
+.Ar useradd
+command.
+These are:
+.Bl -tag -width "-G grouplist"
+.It Fl D
+Set default values in
+.Pa /etc/pw.conf
+configuration file, or a different named configuration file if the
+.Ql Fl C Ar config
+option is used.
+.It Fl b Ar dir
+Set the root directory in which user home directories are created.
+The default value for this is
+.Pa /home ,
+but it may be set elsewhere as desired.
+.It Fl e Ar days
+Set the default account expiration period in days.
+Unlike use without
+.Ql Fl D ,
+the argument must be numeric, which specifies the number of days after creation when
+the account is to expire.
+A value of 0 suppresses automatic calculation of the expiry date.
+.It Fl p Ar days
+Set the default password expiration period in days.
+.It Fl g Ar group
+Set the default group for new users.
+If a blank group is specified using
+.Ql Fl g Ar \&"" ,
+then new users will be allocated their own private primary group (a new group created
+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 made members.
+This is a separate set of groups from the primary group, and you should avoid
+nominating the same group as both the primary and in 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, or a mixture of both, and are always
+stored in
+.Pa /etc/pw.conf
+by their symbolic names.
+.It Fl L Ar class
+This option sets the default login class for new users.
+.It Fl k Ar dir
+Set the default
+.Em skeleton
+directory, from which prototype shell and other initialization files are copied when
+.Nm
+creates a user's home directory.
+.It Fl u Ar min,max , Fl i Ar min,max
+These options set the minimum and maximum user and group ids allocated for new accounts
+and groups created by
+.Nm pw .
+The default values for each is 1000 minimum and 32000 maximum.
+.Ar min
+and
+.Ar max
+are both numbers, where max must be greater than min, and both must be between 0
+and 32767.
+In general, user and group ids less than 100 are reserved for use by the system,
+and numbers greater than 32000 may also be reserved for special purposes (used by
+some system daemons).
+.It Fl w Ar method
+The
+.Ql Fl w
+option sets the default method used to set passwords for newly created user accounts.
+.Ar method
+is one of:
+.Pp
+.Bl -tag -width random -offset indent -compact
+.It no
+disable login on newly created accounts
+.It yes
+force the password to be the account name
+.It none
+force a blank password
+.It random
+generate a random password
+.El
+.Pp
+The
+.Ql \&random
+or
+.Ql \&no
+methods are the most secure; in the former case,
+.Nm
+generates a password and prints it to stdout, which is suitable where you issue
+users with passwords to access their accounts rather than having the user nominate
+their own (possibly poorly chosen) password.
+The
+.Ql \&no
+method requires that the superuser use
+.Xr passwd 1
+to render the account accessible with a password.
+.It Fl y Ar path
+This sets the pathname of the database used by NIS if you are not sharing
+the information from
+.Pa /etc/master.passwd
+directly with NIS.
+You should only set this option on NIS servers.
+.El
+.Pp
+The
+.Ar userdel
+command has only three valid options. The
+.Ql Fl n Ar name
+and
+.Ql Fl u Ar uid
+options have already been covered above.
+The additional option is:
+.Bl -tag -width "-G grouplist"
+.It Fl r
+This tells
+.Nm
+to remove the user's home directory and all of its contents.
+.Nm Pw
+errs on the side of caution when removing files from the system.
+Firstly, it will not do so if the uid of the account being removed is also used by
+another account on the system, and the 'home' directory in the password file is
+a valid path that commences with the character
+.Ql \&/ .
+Secondly, it will only remove files and directories that are actually owned by
+the user, or symbolic links owned by anyone under the user's home directory.
+Finally, after deleting all contents owned by the user only empty directories
+will be removed.
+If any additional cleanup work is required, this is left to the administrator.
+.El
+.Pp
+Mail spool files and crontabs are always removed when an account is deleted as these
+are unconditionally attached to the user name.
+Jobs queued for processing by
+.Ar at
+are also removed if the user's uid is unique (not also used by another account on the
+system).
+.Pp
+The
+.Ar usershow
+command allows viewing of an account in one of two formats.
+By default, the format is identical to the format used in
+.Pa /etc/master.passwd
+with the password field replaced with a
+.Ql \&* .
+If the
+.Ql Fl P
+option is used, then
+.Nm
+outputs the account details in a more human readable form.
+The
+.Ql Fl a
+option lists all users currently on file.
+.Pp
+The command
+.Ar usernext
+returns the next available user and group ids separated by a colon.
+This is normally of interest only to interactive scripts or front-ends
+that use
+.Nm pw .
+.Pp
+.Sh GROUP OPTIONS
+The
+.Ql Fl C Ar config
+and
+.Ql Fl q
+options (explained at the start of the previous section) are available
+with the group manipulation commands.
+Other common options to all group-related commands are:
+.Bl -tag -width "-m newmembers"
+.It Fl n Ar name
+Specify the group name.
+.It Fl g Ar gid
+Specify the group numeric id.
+.Pp
+As with the account name and id fields, you will usually only need
+to supply one of these, as the group name implies the uid and vice
+versa.
+You will only need to use both when setting a specific group id
+against a new group or when changing the uid of an existing group.
+.It Fl M Ar memberlist
+This option provides an alternative way to add existing users to a
+new group (in groupadd) or replace an existing membership list (in
+groupmod).
+.Ar memberlist
+is a comma separated list of valid and existing user names or uids.
+.It Fl m Ar newmembers
+Similar to
+.Op M ,
+this option allows the
+.Em addition
+of existing users to a group without first replacing the existing list of
+members.
+Login names or user ids may be used, and duplicated users are automatically
+and silently eliminated.
+.El
+.Pp
+.Ar groupadd
+also has a
+.Ql Fl o
+option that allows allocation of an existing group id to 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 additonal option:
+.Pp
+.Bl -tag -width "-m newmembers"
+.It Fl l Ar name
+This option allows changing of an existing group name to
+.Ql \&name .
+The new name must not already exist, and any attempt to duplicate an existing group
+name will be rejected.
+.El
+.Pp
+Options for
+.Ar groupshow
+are the same as for
+.Ar usershow ,
+with the
+.Ql Fl g Ar gid
+replacing
+.Ql Fl u Ar uid
+to specify the group id.
+.Pp
+The command
+.Ar groupnext
+returns the next available group id on standard output.
+.Sh DIAGNOSTICS
+.Nm Pw
+returns EXIT_SUCCESS on successful operation, otherwise 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 flie 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-existant shell specified.
+.El
+.It EX_NOUSER
+.Bl -bullet -compact
+.It
+User, user id, group or group id specified does not exist.
+.It
+User or group recorded added or modified unexpectedly disappeared.
+.El
+.It EX_SOFTWARE
+.Bl -bullet -compact
+.It
+No more group or user ids available within specified range.
+.El
+.It EX_IOERR
+.Bl -bullet -compact
+.It
+Unable to rewrite configuration file.
+.It
+Error updating group or user database files.
+.It
+Update error for passwd or group database files.
+.El
+.It EX_CONFIG
+.Bl -bullet -compact
+.It
+No base home directory configured.
+.El
+.El
+.Pp
+.Sh NOTES
+For a summary of options available with each command, you can use
+.Dl pw [command] help
+For example,
+.Dl pw useradd help
+lists all available options for the useradd operation.
+.Pp
+.Nm Pw
+allows 8-bit characters in the passwd gecos field (user's full name,
+office, work and home phone number subfields), but disallows them in
+user login and group names.
+Use 8-bit characters with caution, as connection to the internet will
+require that your mail transport program supports 8BITMIME, and will
+convert headers containing 8-bit characters to 7-bit quoted-printable
+format.
+.Xr sendmail 8
+does support this.
+Use of 8-bit characters in the gecos field should be used in
+conjunction with the user's default locale and character set
+and should not be implemented without their use.
+Using 8-bit characters may also affect other
+programs that transmit the contents of the gecos field over the
+internet, such as
+.Xr fingerd 8 ,
+and a small number of tcpip clients, such as irc, where fullnames
+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..634716e
--- /dev/null
+++ b/usr.sbin/pw/pw.c
@@ -0,0 +1,343 @@
+/*-
+ * Copyright (C) 1996
+ * David L. Nugent. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY DAVID L. NUGENT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL DAVID L. NUGENT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#include "pw.h"
+#include <err.h>
+#include <paths.h>
+#include <sys/wait.h>
+
+const char *Modes[] = {"add", "del", "mod", "show", "next", NULL};
+const char *Which[] = {"user", "group", NULL};
+static const char *Combo1[] = {
+ "useradd", "userdel", "usermod", "usershow", "usernext",
+ "groupadd", "groupdel", "groupmod", "groupshow", "groupnext",
+ NULL};
+static const char *Combo2[] = {
+ "adduser", "deluser", "moduser", "showuser", "nextuser",
+ "addgroup", "delgroup", "modgroup", "showgroup", "nextgroup",
+NULL};
+
+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;
+ struct userconf *cnf;
+
+ static const char *opts[W_NUM][M_NUM] =
+ {
+ { /* user */
+ "C:qn:u:c:d:e:p:g:G:mk:s:oL:i:w:h:Db:NPy:Y",
+ "C:qn:u:rY",
+ "C:qn:u:c:d:e:p:g:G:ml:k:s:w:L:h:FNPY",
+ "C:qn:u:FPa",
+ "C:q"
+ },
+ { /* grp */
+ "C:qn:g:h:M:pNPY",
+ "C:qn:g:Y",
+ "C:qn:g:l:h:FM:m:NPY",
+ "C:qn:g:FPa",
+ "C:q"
+ }
+ };
+
+ static int (*funcs[W_NUM]) (struct userconf * _cnf, int _mode, struct cargs * _args) =
+ { /* Request handlers */
+ pw_user,
+ pw_group
+ };
+
+ umask(0); /* We wish to handle this manually */
+ LIST_INIT(&arglist);
+
+ /*
+ * Break off the first couple of words to determine what exactly
+ * we're being asked to do
+ */
+ while (argc > 1 && *argv[1] != '-') {
+ int tmp;
+
+ if ((tmp = getindex(Modes, argv[1])) != -1)
+ mode = tmp;
+ else if ((tmp = getindex(Which, argv[1])) != -1)
+ which = tmp;
+ else if ((tmp = getindex(Combo1, argv[1])) != -1 || (tmp = getindex(Combo2, argv[1])) != -1) {
+ which = tmp / M_NUM;
+ mode = tmp % M_NUM;
+ } else if (strcmp(argv[1], "help") == 0)
+ cmdhelp(mode, which);
+ else if (which != -1 && mode != -1 && arglist.lh_first == NULL)
+ addarg(&arglist, 'n', argv[1]);
+ else
+ errx(EX_USAGE, "unknown keyword `%s'", argv[1]);
+ ++argv;
+ --argc;
+ }
+
+ /*
+ * Bail out unless the user is specific!
+ */
+ if (mode == -1 || which == -1)
+ cmdhelp(mode, which);
+
+ /*
+ * We know which mode we're in and what we're about to do, so now
+ * let's dispatch the remaining command line args in a genric way.
+ */
+ optarg = NULL;
+
+ while ((ch = getopt(argc, argv, opts[which][mode])) != -1) {
+ if (ch == '?')
+ errx(EX_USAGE, NULL);
+ else
+ addarg(&arglist, ch, optarg);
+ optarg = NULL;
+ }
+
+ /*
+ * Must be root to attempt an update
+ */
+ if (geteuid() != 0 && mode != M_PRINT && mode != M_NEXT && getarg(&arglist, 'N')==NULL)
+ errx(EX_NOPERM, "you must be root to run this program");
+
+ /*
+ * We should immediately look for the -q 'quiet' switch so that we
+ * don't bother with extraneous errors
+ */
+ if (getarg(&arglist, 'q') != NULL)
+ freopen("/dev/null", "w", stderr);
+
+ /*
+ * Now, let's do the common initialisation
+ */
+ cnf = read_userconfig(getarg(&arglist, 'C') ? getarg(&arglist, 'C')->val : NULL);
+ ch = funcs[which] (cnf, mode, &arglist);
+
+ /*
+ * If everything went ok, and we've been asked to update
+ * the NIS maps, then do it now
+ */
+ if (ch == EXIT_SUCCESS && getarg(&arglist, 'Y') != NULL) {
+ pid_t pid;
+
+ fflush(NULL);
+ if (chdir(_PATH_YP) == -1)
+ warn("chdir(" _PATH_YP ")");
+ else if ((pid = fork()) == -1)
+ warn("fork()");
+ else if (pid == 0) {
+ /* Is make anywhere else? */
+ execlp("/usr/bin/make", "make", NULL);
+ _exit(1);
+ } else {
+ int i;
+ waitpid(pid, &i, 0);
+ if ((i = WEXITSTATUS(i)) != 0)
+ errx(ch, "make exited with status %d", i);
+ else
+ pw_log(cnf, mode, which, "NIS maps updated");
+ }
+ }
+ return ch;
+}
+
+static int
+getindex(const char *words[], const char *word)
+{
+ int i = 0;
+
+ while (words[i]) {
+ if (strcmp(words[i], word) == 0)
+ return i;
+ i++;
+ }
+ return -1;
+}
+
+
+/*
+ * This is probably an overkill for a cmdline help system, but it reflects
+ * the complexity of the command line.
+ */
+
+static void
+cmdhelp(int mode, int which)
+{
+ if (which == -1)
+ fprintf(stderr, "usage: pw [user|group] [add|del|mod|show|next] [ help | switches/values ]\n");
+ else if (mode == -1)
+ fprintf(stderr, "usage: pw %s [add|del|mod|show|next] [ help | switches/values ]\n", Which[which]);
+ else {
+
+ /*
+ * We need to give mode specific help
+ */
+ static const char *help[W_NUM][M_NUM] =
+ {
+ {
+ "usage: pw useradd [name] [switches]\n"
+ "\t-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-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-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-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-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",
+ "usage: pw usernext [switches]\n"
+ "\t-C config configuration file\n"
+ },
+ {
+ "usage: pw groupadd [group|gid] [switches]\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-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-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-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-C config configuration file\n"
+ }
+ };
+
+ fprintf(stderr, help[which][mode]);
+ }
+ exit(EXIT_FAILURE);
+}
+
+struct carg *
+getarg(struct cargs * _args, int ch)
+{
+ struct carg *c = _args->lh_first;
+
+ while (c != NULL && c->ch != ch)
+ c = c->list.le_next;
+ return c;
+}
+
+struct carg *
+addarg(struct cargs * _args, int ch, char *argstr)
+{
+ struct carg *ca = malloc(sizeof(struct carg));
+
+ if (ca == NULL)
+ errx(EX_OSERR, "out of memory");
+ ca->ch = ch;
+ ca->val = argstr;
+ LIST_INSERT_HEAD(_args, ca, list);
+ return ca;
+}
diff --git a/usr.sbin/pw/pw.conf.5 b/usr.sbin/pw/pw.conf.5
new file mode 100644
index 0000000..c06f040
--- /dev/null
+++ b/usr.sbin/pw/pw.conf.5
@@ -0,0 +1,301 @@
+.\" Copyright (C) 1996
+.\" David L. Nugent. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY DAVID L. NUGENT AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL DAVID L. NUGENT OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $Id: pw.conf.5,v 1.6 1997/02/22 16:12:26 peter Exp $
+.\"
+.Dd December 9, 1996
+.Dt PW.CONF 5
+.Os
+.Sh NAME
+.Nm pw.conf
+.Nd format of the pw.conf configuration file
+.Sh DESCRIPTION
+The file
+.Aq Pa /etc/pw.conf
+contains configuration data for the
+.Xr pw 8
+program.
+The
+.Xr pw 8
+program is used for maintenance of the system password and group
+files, allowing users and groups to be added, deleted and changed.
+This file may be modified via the
+.Xr pw 8
+command using the
+.Ar useradd
+command and the
+.Fl D
+option, or by editing it directly with a text editor.
+.Pp
+Each line in
+.Aq Pa /etc/pw.conf
+is treated either a comment or as configuration data;
+blank lines and lines commencing with a
+.Ql \&#
+character are considered comments, and any remaining lines are
+examined for a leading keyword, followed by corresponding data.
+.Pp
+Keywords recognised by
+.Xr pw 8
+are:
+.Bl -tag -width password_days -offset indent -compact
+.It defaultpasswd
+affect passwords generated for new users
+.It reuseuids
+reuse gaps in uid sequences
+.It reusegids
+reuse gaps in gid sequences
+.It nispasswd
+path to the NIS passwd database
+.It skeleton
+where to obtain default home contents
+.It newmail
+mail to send to new users
+.It logfile
+log user/group modifications to this file
+.It home
+root directory for home directories
+.It shellpath
+paths in which to locate shell programs
+.It shells
+list of valid shells (without path)
+.It defaultshell
+default shell (without path)
+.It defaultgroup
+default group
+.It extragroups
+add new users to this groups
+.It defaultclass
+place new users in this login class
+.It minuid
+.It maxuid
+range of valid default user ids
+.It mingid
+.It maxgid
+range of valid default group ids
+.It expire_days
+days after which account expires
+.It password_days
+days after which password expires
+.El
+.Pp
+Valid values for
+.Ar defaultpasswd
+are:
+.Bl -tag -width password_days -offset indent -compact
+.It no
+disable login on newly created accounts
+.It yes
+force the password to be the account name
+.It none
+force a blank password
+.It random
+generate a random password
+.El
+.Pp
+The second and third options are insecure and should be avoided if
+possible on a publicly accessible system.
+The first option requires that the superuser run
+.Xr passwd 1
+to set a password before the account may be used.
+This may also be useful for creating administrative accounts.
+The final option causes
+.Xr pw 8
+to respond by printing a randomly generated password on stdout.
+This is the preferred and most secure option.
+.Xr Pw 8
+also provides a method of setting a specific password for the new
+user via a filehandle (command lines are not secure).
+.Pp
+Both
+.Ar reuseuids
+and
+.Ar reusegids
+determine the method by which new user and group id numbers are
+generated.
+A
+.Ql \&yes
+in this field will cause
+.Xr pw 8
+to search for the first unused user or group id within the allowed
+range, whereas a
+.Ql \&no
+will ensure that no other existing user or group id within the range
+is numerically lower than the new one generated, and therefore avoids
+reusing gaps in the user or group id sequence that are caused by
+previous user or group deletions.
+Note that if the default group is not specified using the
+.Ar defaultgroup
+keyword,
+.Xr pw 8
+will create a new group for the user and attempt to keep the new
+user's uid and gid the same.
+If the new user's uid is currently in use as a group id, then the next
+available group id is chosen instead.
+.Pp
+On NIS servers which maintain a separate passwd database to
+.Pa /etc/master.passwd ,
+this option allows the additional file to be concurrently updated
+as user records are added, modified or removed.
+If blank or set to 'no', no additional database is updated.
+An absolute pathname must be used.
+.Pp
+The
+.Ar skeleton
+keyword nominates a directory from which the contents of a user's
+new home directory is constructed.
+This is
+.Pa /usr/share/skel
+by default.
+.Xr Pw 8 's
+.Fl m
+option causes the user's home directory to be created and populated
+using the files contained in the
+.Ar skeleton
+directory.
+.Pp
+To send an initial email to new users, the
+.Ar newmail
+keyword may be used to specify a path name to a file containing
+the message body of the message to be sent.
+To avoid sending mail when accounts are created, leave this entry
+blank or specify
+.Ql \&no .
+.Pp
+The
+.Ar logfile
+option allows logging of password file modifications into the
+nominated log file.
+To avoid creating or adding to such a logfile, then leave this
+field blank or specify
+.Ql \&no .
+.Pp
+The
+.Ar home
+keyword is mandatory.
+This specifies the location of the directory in which all new user
+home directories are created.
+.Pp
+.Ar shellpath
+specifies a list of directories - separated by colons
+.Ql \&:
+- which contain the programs used by the login shells.
+.Pp
+The
+.Ar shells
+keyword specifies a list of programs available for use as login
+shells.
+This list is a comma-separated list of shell names which should
+not contain a path.
+These shells must exist in one of the directories nominated by
+.Ar shellpath .
+.Pp
+The
+.Ar defaultshell
+keyword nominates which shell program to use for new users when
+none is specified on the
+.Xr pw 8
+command line.
+.Pp
+The
+.Ar defaultgroup
+keyword defines the primary group (the group id number in the
+password file) used for new accounts.
+If left blank, or the word
+.Ql \&no
+is used, then each new user will have a corresponding group of
+their own created automatically.
+This is the recommended procedure for new users as it best secures each
+user's files against interference by other users of the system
+irrespective of the
+.Em umask
+normally used by the user.
+.Pp
+.Ar extragroups
+provides an automatic means of placing new users into groups within
+the
+.Pa /etc/groups
+file.
+This is useful where all users share some resources, and is preferable
+to placing users into the same primary group.
+The effect of this keyword can be overridden using the
+.Fl G
+option on the
+.Xr pw 8
+command line.
+.Pp
+The
+.Ar defaultclass
+field determines the login class (See
+.Xr login.conf 5 )
+that new users will be allocated unless overwritten by
+.Xr pw 8 .
+.Pp
+The
+.Ar minuid ,
+.Ar maxuid ,
+.Ar mingid ,
+.Ar maxgid
+keywords determines the allowed ranges of automatically allocated user
+and group id numbers.
+The default values for both user and group ids are 1000 and 32000 as
+minimum and maximum respectively.
+The user and group id's actually used when creating an account with
+.Xr pw 8
+may be overridden using the
+.Fl u
+and
+.Fl g
+command line options.
+.Pp
+The
+.Ar expire_days
+and
+.Ar password_days
+are used to automatically calculate the number of days from the date
+on which an account is created when the account will expire or the
+user will be forced to change the account's password.
+A value of
+.Ql \&0
+in either field will disable the corresponding (account or password)
+expiration date.
+.Pp
+.Sh LIMITS
+The maximum line length of
+.Pa /etc/pw.conf
+is 1024 characters. Longer lines will be skipped and treated
+as comments.
+.Sh FILES
+.Bl -tag -width /etc/master.passwd -compact
+.It Pa /etc/pw.conf
+.It Pa /etc/passwd
+.It Pa /etc/master.passwd
+.It Pa /etc/group
+.El
+.Sh SEE ALSO
+.Xr passwd 1 ,
+.Xr group 5 ,
+.Xr login.conf 5 ,
+.Xr passwd 5 ,
+.Xr pw 8
diff --git a/usr.sbin/pw/pw.h b/usr.sbin/pw/pw.h
new file mode 100644
index 0000000..b52891a
--- /dev/null
+++ b/usr.sbin/pw/pw.h
@@ -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.
+ *
+ * $Id: pw.h,v 1.6 1997/02/22 16:12:27 peter Exp $
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <pwd.h>
+#include <grp.h>
+#include <sys/queue.h>
+#include <sysexits.h>
+
+#include "psdate.h"
+
+enum _mode
+{
+ M_ADD,
+ M_DELETE,
+ M_UPDATE,
+ M_PRINT,
+ M_NEXT,
+ M_NUM
+};
+
+enum _which
+{
+ W_USER,
+ W_GROUP,
+ W_NUM
+};
+
+struct carg
+{
+ int ch;
+ char *val;
+ LIST_ENTRY(carg) list;
+};
+
+extern LIST_HEAD(cargs, carg) arglist;
+
+struct userconf
+{
+ int default_password; /* Default password for new users? */
+ int reuse_uids; /* Reuse uids? */
+ int reuse_gids; /* Reuse gids? */
+ char *nispasswd; /* Path to NIS version of the passwd file */
+ char *dotdir; /* Where to obtain skeleton files */
+ char *newmail; /* Mail to send to new accounts */
+ char *logfile; /* Where to log changes */
+ char *home; /* Where to create home directory */
+ char *shelldir; /* Where shells are located */
+ char **shells; /* List of shells */
+ char *shell_default; /* Default shell */
+ char *default_group; /* Default group number */
+ char **groups; /* Default (additional) groups */
+ char *default_class; /* Default user class */
+ uid_t min_uid, max_uid; /* Allowed range of uids */
+ gid_t min_gid, max_gid; /* Allowed range of gids */
+ int expire_days; /* Days to expiry */
+ int password_days; /* Days to password expiry */
+ int numgroups; /* (internal) size of default_group array */
+};
+
+#define _PATH_PW_CONF "/etc/pw.conf"
+#define _UC_MAXLINE 1024
+#define _UC_MAXSHELLS 32
+
+struct userconf *read_userconfig(char const * file);
+int write_userconfig(char const * file);
+struct carg *addarg(struct cargs * _args, int ch, char *argstr);
+struct carg *getarg(struct cargs * _args, int ch);
+
+int pw_user(struct userconf * cnf, int mode, struct cargs * _args);
+int pw_group(struct userconf * cnf, int mode, struct cargs * _args);
+char *pw_checkname(u_char *name, int gecos);
+
+int addpwent(struct passwd * pwd);
+int delpwent(struct passwd * pwd);
+int chgpwent(char const * login, struct passwd * pwd);
+int fmtpwent(char *buf, struct passwd * pwd);
+
+int addnispwent(const char *path, struct passwd *pwd);
+int delnispwent(const char *path, const char *login);
+int chgnispwent(const char *path, const char *login, struct passwd *pwd);
+
+int addgrent(struct group * grp);
+int delgrent(struct group * grp);
+int chggrent(char const * login, struct group * grp);
+
+int boolean_val(char const * str, int dflt);
+char const *boolean_str(int val);
+char *newstr(char const * p);
+
+void pw_log(struct userconf * cnf, int mode, int which, char const * fmt,...);
+char *pw_pwcrypt(char *password);
+
+extern const char *Modes[];
+extern const char *Which[];
diff --git a/usr.sbin/pw/pw_conf.c b/usr.sbin/pw/pw_conf.c
new file mode 100644
index 0000000..63742a7
--- /dev/null
+++ b/usr.sbin/pw/pw_conf.c
@@ -0,0 +1,496 @@
+/*-
+ * Copyright (C) 1996
+ * David L. Nugent. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY DAVID L. NUGENT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL DAVID L. NUGENT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#include <string.h>
+#include <ctype.h>
+#include <fcntl.h>
+
+#include "pw.h"
+#include "pwupd.h"
+
+#define debugging 0
+
+enum {
+ _UC_NONE,
+ _UC_DEFAULTPWD,
+ _UC_REUSEUID,
+ _UC_REUSEGID,
+ _UC_NISPASSWD,
+ _UC_DOTDIR,
+ _UC_NEWMAIL,
+ _UC_LOGFILE,
+ _UC_HOMEROOT,
+ _UC_SHELLPATH,
+ _UC_SHELLS,
+ _UC_DEFAULTSHELL,
+ _UC_DEFAULTGROUP,
+ _UC_EXTRAGROUPS,
+ _UC_DEFAULTCLASS,
+ _UC_MINUID,
+ _UC_MAXUID,
+ _UC_MINGID,
+ _UC_MAXGID,
+ _UC_EXPIRE,
+ _UC_PASSWORD,
+ _UC_FIELDS
+};
+
+static char bourne_shell[] = "sh";
+
+static char *system_shells[_UC_MAXSHELLS] =
+{
+ bourne_shell,
+ "csh"
+};
+
+static char const *booltrue[] =
+{
+ "yes", "true", "1", "on", NULL
+};
+static char const *boolfalse[] =
+{
+ "no", "false", "0", "off", NULL
+};
+
+static struct userconf config =
+{
+ 0, /* Default password for new users? (nologin) */
+ 0, /* Reuse uids? */
+ 0, /* Reuse gids? */
+ NULL, /* NIS version of the passwd file */
+ "/usr/share/skel", /* Where to obtain skeleton files */
+ NULL, /* Mail to send to new accounts */
+ "/var/log/userlog", /* Where to log changes */
+ "/home", /* Where to create home directory */
+ "/bin", /* Where shells are located */
+ system_shells, /* List of shells (first is default) */
+ bourne_shell, /* Default shell */
+ NULL, /* Default group name */
+ NULL, /* Default (additional) groups */
+ NULL, /* Default login class */
+ 1000, 32000, /* Allowed range of uids */
+ 1000, 32000, /* Allowed range of gids */
+ 0, /* Days until account expires */
+ 0 /* Days until password expires */
+};
+
+static char const *comments[_UC_FIELDS] =
+{
+ "#\n# pw.conf - user/group configuration defaults\n#\n",
+ "\n# Password for new users? no=nologin yes=loginid none=blank random=random\n",
+ "\n# Reuse gaps in uid sequence? (yes or no)\n",
+ "\n# Reuse gaps in gid sequence? (yes or no)\n",
+ "\n# Path to the NIS passwd file (blank or 'no' for none)\n",
+ "\n# Obtain default dotfiles from this directory\n",
+ "\n# Mail this file to new user (/etc/newuser.msg or no)\n",
+ "\n# Log add/change/remove information in this file\n",
+ "\n# Root directory in which $HOME directory is created\n",
+ "\n# Colon separated list of directories containing valid shells\n",
+ "\n# Space separated list of available shells (without paths)\n",
+ "\n# Default shell (without path)\n",
+ "\n# Default group (leave blank for new group per user)\n",
+ "\n# Extra groups for new users\n",
+ "\n# Default login class for new users\n",
+ "\n# Range of valid default user ids\n",
+ NULL,
+ "\n# Range of valid default group ids\n",
+ NULL,
+ "\n# Days after which account expires (0=disabled)\n",
+ "\n# Days after which password expires (0=disabled)\n"
+};
+
+static char const *kwds[] =
+{
+ "",
+ "defaultpasswd",
+ "reuseuids",
+ "reusegids",
+ "nispasswd",
+ "skeleton",
+ "newmail",
+ "logfile",
+ "home",
+ "shellpath",
+ "shells",
+ "defaultshell",
+ "defaultgroup",
+ "extragroups",
+ "defaultclass",
+ "minuid",
+ "maxuid",
+ "mingid",
+ "maxgid",
+ "expire_days",
+ "password_days",
+ NULL
+};
+
+static char *
+unquote(char const * str)
+{
+ if (str && (*str == '"' || *str == '\'')) {
+ char *p = strchr(str + 1, *str);
+
+ if (p != NULL)
+ *p = '\0';
+ return (char *) (*++str ? str : NULL);
+ }
+ return (char *) str;
+}
+
+int
+boolean_val(char const * str, int dflt)
+{
+ if ((str = unquote(str)) != NULL) {
+ int i;
+
+ for (i = 0; booltrue[i]; i++)
+ if (strcmp(str, booltrue[i]) == 0)
+ return 1;
+ for (i = 0; boolfalse[i]; i++)
+ if (strcmp(str, boolfalse[i]) == 0)
+ return 0;
+
+ /*
+ * Special cases for defaultpassword
+ */
+ if (strcmp(str, "random") == 0)
+ return -1;
+ if (strcmp(str, "none") == 0)
+ return -2;
+ }
+ return dflt;
+}
+
+char const *
+boolean_str(int val)
+{
+ if (val == -1)
+ return "random";
+ else if (val == -2)
+ return "none";
+ else
+ return val ? booltrue[0] : boolfalse[0];
+}
+
+char *
+newstr(char const * p)
+{
+ char *q = NULL;
+
+ if ((p = unquote(p)) != NULL) {
+ int l = strlen(p) + 1;
+
+ if ((q = malloc(l)) != NULL)
+ memcpy(q, p, l);
+ }
+ return q;
+}
+
+#define LNBUFSZ 1024
+
+
+struct userconf *
+read_userconfig(char const * file)
+{
+ FILE *fp;
+
+ extendarray(&config.groups, &config.numgroups, 200);
+ memset(config.groups, 0, config.numgroups * sizeof(char *));
+ if (file == NULL)
+ file = _PATH_PW_CONF;
+ if ((fp = fopen(file, "r")) != NULL) {
+ int buflen = LNBUFSZ;
+ char *buf = malloc(buflen);
+
+ nextline:
+ while (fgets(buf, buflen, fp) != NULL) {
+ char *p;
+
+ while ((p = strchr(buf, '\n')) == NULL) {
+ int l;
+ if (extendline(&buf, &buflen, buflen + LNBUFSZ) == -1) {
+ int ch;
+ while ((ch = fgetc(fp)) != '\n' && ch != EOF);
+ goto nextline; /* Ignore it */
+ }
+ l = strlen(buf);
+ if (fgets(buf + l, buflen - l, fp) == NULL)
+ break; /* Unterminated last line */
+ }
+
+ if (p != NULL)
+ *p = '\0';
+
+ if (*buf && (p = strtok(buf, " \t\r\n=")) != NULL && *p != '#') {
+ static char const toks[] = " \t\r\n,=";
+ char *q = strtok(NULL, toks);
+ int i = 0;
+
+ while (i < _UC_FIELDS && strcmp(p, kwds[i]) != 0)
+ ++i;
+#if debugging
+ if (i == _UC_FIELDS)
+ printf("Got unknown kwd `%s' val=`%s'\n", p, q ? q : "");
+ else
+ printf("Got kwd[%s]=%s\n", p, q);
+#endif
+ switch (i) {
+ case _UC_DEFAULTPWD:
+ config.default_password = boolean_val(q, 1);
+ break;
+ case _UC_REUSEUID:
+ config.reuse_uids = boolean_val(q, 0);
+ break;
+ case _UC_REUSEGID:
+ config.reuse_gids = boolean_val(q, 0);
+ break;
+ case _UC_NISPASSWD:
+ config.nispasswd = (q == NULL || !boolean_val(q, 1))
+ ? NULL : newstr(q);
+ break;
+ case _UC_DOTDIR:
+ config.dotdir = (q == NULL || !boolean_val(q, 1))
+ ? NULL : newstr(q);
+ break;
+ case _UC_NEWMAIL:
+ config.newmail = (q == NULL || !boolean_val(q, 1))
+ ? NULL : newstr(q);
+ break;
+ case _UC_LOGFILE:
+ config.logfile = (q == NULL || !boolean_val(q, 1))
+ ? NULL : newstr(q);
+ break;
+ case _UC_HOMEROOT:
+ config.home = (q == NULL || !boolean_val(q, 1))
+ ? "/home" : newstr(q);
+ break;
+ case _UC_SHELLPATH:
+ config.shelldir = (q == NULL || !boolean_val(q, 1))
+ ? "/bin" : newstr(q);
+ break;
+ case _UC_SHELLS:
+ for (i = 0; i < _UC_MAXSHELLS && q != NULL; i++, q = strtok(NULL, toks))
+ system_shells[i] = newstr(q);
+ if (i > 0)
+ while (i < _UC_MAXSHELLS)
+ system_shells[i++] = NULL;
+ break;
+ case _UC_DEFAULTSHELL:
+ config.shell_default = (q == NULL || !boolean_val(q, 1))
+ ? (char *) bourne_shell : newstr(q);
+ break;
+ case _UC_DEFAULTGROUP:
+ q = unquote(q);
+ config.default_group = (q == NULL || !boolean_val(q, 1) || getgrnam(q) == NULL)
+ ? NULL : newstr(q);
+ break;
+ case _UC_EXTRAGROUPS:
+ for (i = 0; q != NULL; q = strtok(NULL, toks)) {
+ if (extendarray(&config.groups, &config.numgroups, i + 2) != -1)
+ config.groups[i++] = newstr(q);
+ }
+ if (i > 0)
+ while (i < config.numgroups)
+ config.groups[i++] = NULL;
+ break;
+ case _UC_DEFAULTCLASS:
+ config.default_class = (q == NULL || !boolean_val(q, 1))
+ ? NULL : newstr(q);
+ break;
+ case _UC_MINUID:
+ if ((q = unquote(q)) != NULL && isdigit(*q))
+ config.min_uid = (uid_t) atol(q);
+ break;
+ case _UC_MAXUID:
+ if ((q = unquote(q)) != NULL && isdigit(*q))
+ config.max_uid = (uid_t) atol(q);
+ break;
+ case _UC_MINGID:
+ if ((q = unquote(q)) != NULL && isdigit(*q))
+ config.min_gid = (gid_t) atol(q);
+ break;
+ case _UC_MAXGID:
+ if ((q = unquote(q)) != NULL && isdigit(*q))
+ config.max_gid = (gid_t) atol(q);
+ break;
+ case _UC_EXPIRE:
+ if ((q = unquote(q)) != NULL && isdigit(*q))
+ config.expire_days = atoi(q);
+ break;
+ case _UC_PASSWORD:
+ if ((q = unquote(q)) != NULL && isdigit(*q))
+ config.password_days = atoi(q);
+ break;
+ case _UC_FIELDS:
+ case _UC_NONE:
+ break;
+ }
+ }
+ }
+ free(buf);
+ fclose(fp);
+ }
+ return &config;
+}
+
+
+int
+write_userconfig(char const * file)
+{
+ int fd;
+
+ if (file == NULL)
+ file = _PATH_PW_CONF;
+
+ if ((fd = open(file, O_CREAT | O_RDWR | O_TRUNC | O_EXLOCK, 0644)) != -1) {
+ FILE *fp;
+
+ if ((fp = fdopen(fd, "w")) == NULL)
+ close(fd);
+ else {
+ int i, j, k;
+ int len = LNBUFSZ;
+ char *buf = malloc(len);
+
+ for (i = _UC_NONE; i < _UC_FIELDS; i++) {
+ int quote = 1;
+ char const *val = buf;
+
+ *buf = '\0';
+ switch (i) {
+ case _UC_DEFAULTPWD:
+ val = boolean_str(config.default_password);
+ break;
+ case _UC_REUSEUID:
+ val = boolean_str(config.reuse_uids);
+ break;
+ case _UC_REUSEGID:
+ val = boolean_str(config.reuse_gids);
+ break;
+ case _UC_NISPASSWD:
+ val = config.nispasswd ? config.nispasswd : "";
+ quote = 0;
+ break;
+ case _UC_DOTDIR:
+ val = config.dotdir ? config.dotdir : boolean_str(0);
+ break;
+ case _UC_NEWMAIL:
+ val = config.newmail ? config.newmail : boolean_str(0);
+ break;
+ case _UC_LOGFILE:
+ val = config.logfile ? config.logfile : boolean_str(0);
+ break;
+ case _UC_HOMEROOT:
+ val = config.home;
+ break;
+ case _UC_SHELLPATH:
+ val = config.shelldir;
+ break;
+ case _UC_SHELLS:
+ for (j = k = 0; j < _UC_MAXSHELLS && system_shells[j] != NULL; j++) {
+ char lbuf[64];
+ int l = snprintf(lbuf, sizeof lbuf, "%s\"%s\"", k ? "," : "", system_shells[j]);
+ if (l + k + 1 < len || extendline(&buf, &len, len + LNBUFSZ) != -1) {
+ strcpy(buf + k, lbuf);
+ k += l;
+ }
+ }
+ quote = 0;
+ break;
+ case _UC_DEFAULTSHELL:
+ val = config.shell_default ? config.shell_default : bourne_shell;
+ break;
+ case _UC_DEFAULTGROUP:
+ val = config.default_group ? config.default_group : "";
+ break;
+ case _UC_EXTRAGROUPS:
+ extendarray(&config.groups, &config.numgroups, 200);
+ for (j = k = 0; j < config.numgroups && config.groups[j] != NULL; j++) {
+ char lbuf[64];
+ int l = snprintf(lbuf, sizeof lbuf, "%s\"%s\"", k ? "," : "", config.groups[j]);
+ if (l + k + 1 < len || extendline(&buf, &len, len + 1024) != -1) {
+ strcpy(buf + k, lbuf);
+ k += l;
+ }
+ }
+ quote = 0;
+ break;
+ case _UC_DEFAULTCLASS:
+ val = config.default_class ? config.default_class : "";
+ break;
+ case _UC_MINUID:
+ sprintf(buf, "%lu", (unsigned long) config.min_uid);
+ quote = 0;
+ break;
+ case _UC_MAXUID:
+ sprintf(buf, "%lu", (unsigned long) config.max_uid);
+ quote = 0;
+ break;
+ case _UC_MINGID:
+ sprintf(buf, "%lu", (unsigned long) config.min_gid);
+ quote = 0;
+ break;
+ case _UC_MAXGID:
+ sprintf(buf, "%lu", (unsigned long) config.max_gid);
+ quote = 0;
+ break;
+ case _UC_EXPIRE:
+ sprintf(buf, "%d", config.expire_days);
+ quote = 0;
+ break;
+ case _UC_PASSWORD:
+ sprintf(buf, "%d", config.password_days);
+ quote = 0;
+ break;
+ case _UC_NONE:
+ break;
+ }
+
+ if (comments[i])
+ fputs(comments[i], fp);
+
+ if (*kwds[i]) {
+ if (quote)
+ fprintf(fp, "%s = \"%s\"\n", kwds[i], val);
+ else
+ fprintf(fp, "%s = %s\n", kwds[i], val);
+#if debugging
+ printf("WROTE: %s = %s\n", kwds[i], val);
+#endif
+ }
+ }
+ free(buf);
+ return fclose(fp) != EOF;
+ }
+ }
+ return 0;
+}
diff --git a/usr.sbin/pw/pw_group.c b/usr.sbin/pw/pw_group.c
new file mode 100644
index 0000000..ce1b230
--- /dev/null
+++ b/usr.sbin/pw/pw_group.c
@@ -0,0 +1,335 @@
+/*-
+ * Copyright (C) 1996
+ * David L. Nugent. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY DAVID L. NUGENT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL DAVID L. NUGENT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#include <ctype.h>
+#include <err.h>
+#include <termios.h>
+#include <unistd.h>
+
+#include "pw.h"
+#include "bitmap.h"
+#include "pwupd.h"
+
+
+static int print_group(struct group * grp, int pretty);
+static gid_t gr_gidpolicy(struct userconf * cnf, struct cargs * args);
+
+int
+pw_group(struct userconf * cnf, int mode, struct cargs * args)
+{
+ struct carg *a_name = getarg(args, 'n');
+ struct carg *a_gid = getarg(args, 'g');
+ struct carg *arg;
+ struct group *grp = NULL;
+ int grmembers = 0;
+ char **members = NULL;
+
+ static struct group fakegroup =
+ {
+ "nogroup",
+ "*",
+ -1,
+ NULL
+ };
+
+ /*
+ * With M_NEXT, we only need to return the
+ * next gid to stdout
+ */
+ if (mode == M_NEXT)
+ {
+ gid_t next = gr_gidpolicy(cnf, args);
+ if (getarg(args, 'q'))
+ return next;
+ printf("%ld\n", (long)next);
+ return EXIT_SUCCESS;
+ }
+
+ if (mode == M_PRINT && getarg(args, 'a')) {
+ int pretty = getarg(args, 'P') != NULL;
+
+ setgrent();
+ while ((grp = getgrent()) != NULL)
+ print_group(grp, pretty);
+ endgrent();
+ return EXIT_SUCCESS;
+ }
+ if (a_gid == NULL) {
+ if (a_name == NULL)
+ errx(EX_DATAERR, "group name or id required");
+
+ if (mode != M_ADD && grp == NULL && isdigit(*a_name->val)) {
+ (a_gid = a_name)->ch = 'g';
+ a_name = NULL;
+ }
+ }
+ grp = (a_name != NULL) ? getgrnam(a_name->val) : getgrgid((gid_t) atoi(a_gid->val));
+
+ if (mode == M_UPDATE || mode == M_DELETE || mode == M_PRINT) {
+ if (a_name == NULL && grp == NULL) /* Try harder */
+ grp = getgrgid(atoi(a_gid->val));
+
+ if (grp == NULL) {
+ if (mode == M_PRINT && getarg(args, 'F')) {
+ char *fmems[1];
+ fmems[0] = NULL;
+ fakegroup.gr_name = a_name ? a_name->val : "nogroup";
+ fakegroup.gr_gid = a_gid ? (gid_t) atol(a_gid->val) : -1;
+ fakegroup.gr_mem = fmems;
+ return print_group(&fakegroup, getarg(args, 'P') != NULL);
+ }
+ errx(EX_DATAERR, "unknown group `%s'", a_name ? a_name->val : a_gid->val);
+ }
+ if (a_name == NULL) /* Needed later */
+ a_name = addarg(args, 'n', grp->gr_name);
+
+ /*
+ * Handle deletions now
+ */
+ if (mode == M_DELETE) {
+ gid_t gid = grp->gr_gid;
+
+ if (delgrent(grp) == -1)
+ err(EX_IOERR, "error updating group file");
+ pw_log(cnf, mode, W_GROUP, "%s(%ld) removed", a_name->val, (long) gid);
+ return EXIT_SUCCESS;
+ } else if (mode == M_PRINT)
+ return print_group(grp, getarg(args, 'P') != NULL);
+
+ if (a_gid)
+ grp->gr_gid = (gid_t) atoi(a_gid->val);
+
+ if ((arg = getarg(args, 'l')) != NULL)
+ grp->gr_name = pw_checkname((u_char *)arg->val, 0);
+ } else {
+ if (a_name == NULL) /* Required */
+ errx(EX_DATAERR, "group name required");
+ else if (grp != NULL) /* Exists */
+ errx(EX_DATAERR, "group name `%s' already exists", a_name->val);
+
+ extendarray(&members, &grmembers, 200);
+ members[0] = NULL;
+ grp = &fakegroup;
+ grp->gr_name = pw_checkname((u_char *)a_name->val, 0);
+ grp->gr_passwd = "*";
+ grp->gr_gid = gr_gidpolicy(cnf, args);
+ grp->gr_mem = members;
+ }
+
+ /*
+ * This allows us to set a group password Group passwords is an
+ * antique idea, rarely used and insecure (no secure database) Should
+ * be discouraged, but it is apparently still supported by some
+ * software.
+ */
+
+ if ((arg = getarg(args, 'h')) != NULL) {
+ if (strcmp(arg->val, "-") == 0)
+ grp->gr_passwd = "*"; /* No access */
+ else {
+ int fd = atoi(arg->val);
+ int b;
+ int istty = isatty(fd);
+ struct termios t;
+ char *p, line[256];
+
+ if (istty) {
+ if (tcgetattr(fd, &t) == -1)
+ istty = 0;
+ else {
+ struct termios n = t;
+
+ /* Disable echo */
+ n.c_lflag &= ~(ECHO);
+ tcsetattr(fd, TCSANOW, &n);
+ printf("%sassword for group %s:", (mode == M_UPDATE) ? "New p" : "P", grp->gr_name);
+ fflush(stdout);
+ }
+ }
+ b = read(fd, line, sizeof(line) - 1);
+ if (istty) { /* Restore state */
+ tcsetattr(fd, TCSANOW, &t);
+ fputc('\n', stdout);
+ fflush(stdout);
+ }
+ if (b < 0) {
+ warn("-h file descriptor");
+ return EX_OSERR;
+ }
+ line[b] = '\0';
+ if ((p = strpbrk(line, " \t\r\n")) != NULL)
+ *p = '\0';
+ if (!*line)
+ errx(EX_DATAERR, "empty password read on file descriptor %d", fd);
+ grp->gr_passwd = pw_pwcrypt(line);
+ }
+ }
+
+ if (((arg = getarg(args, 'M')) != NULL || (arg = getarg(args, 'm')) != NULL) && arg->val) {
+ int i = 0;
+ char *p;
+ struct passwd *pwd;
+
+ /* Make sure this is not stay NULL with -M "" */
+ extendarray(&members, &grmembers, 200);
+ if (arg->ch == 'm') {
+ int k = 0;
+
+ while (grp->gr_mem[k] != NULL) {
+ if (extendarray(&members, &grmembers, i + 2) != -1) {
+ members[i++] = grp->gr_mem[k];
+ }
+ k++;
+ }
+ }
+ for (p = strtok(arg->val, ", \t"); p != NULL; p = strtok(NULL, ", \t")) {
+ int j;
+ if ((pwd = getpwnam(p)) == NULL) {
+ if (!isdigit(*p) || (pwd = getpwuid((uid_t) atoi(p))) == NULL)
+ errx(EX_NOUSER, "user `%s' does not exist", p);
+ }
+ /*
+ * Check for duplicates
+ */
+ for (j = 0; j < i && strcmp(members[j], pwd->pw_name)!=0; j++)
+ ;
+ if (j == i && extendarray(&members, &grmembers, i + 2) != -1)
+ members[i++] = newstr(pwd->pw_name);
+ }
+ while (i < grmembers)
+ members[i++] = NULL;
+ grp->gr_mem = members;
+ }
+
+ if (getarg(args, 'N') != NULL)
+ return print_group(grp, getarg(args, 'P') != NULL);
+
+ if ((mode == M_ADD && !addgrent(grp)) || (mode == M_UPDATE && !chggrent(a_name->val, grp))) {
+ warn("group update");
+ return EX_IOERR;
+ }
+ /* grp may have been invalidated */
+ if ((grp = getgrnam(a_name->val)) == NULL)
+ errx(EX_SOFTWARE, "group disappeared during update");
+
+ pw_log(cnf, mode, W_GROUP, "%s(%ld)", grp->gr_name, (long) grp->gr_gid);
+
+ if (members)
+ free(members);
+
+ return EXIT_SUCCESS;
+}
+
+
+static gid_t
+gr_gidpolicy(struct userconf * cnf, struct cargs * args)
+{
+ struct group *grp;
+ gid_t gid = (gid_t) - 1;
+ struct carg *a_gid = getarg(args, 'g');
+
+ /*
+ * Check the given gid, if any
+ */
+ if (a_gid != NULL) {
+ gid = (gid_t) atol(a_gid->val);
+
+ if ((grp = getgrgid(gid)) != NULL && getarg(args, 'o') == NULL)
+ errx(EX_DATAERR, "gid `%ld' has already been allocated", (long) grp->gr_gid);
+ } else {
+ struct bitmap bm;
+
+ /*
+ * We need to allocate the next available gid under one of
+ * two policies a) Grab the first unused gid b) Grab the
+ * highest possible unused gid
+ */
+ if (cnf->min_gid >= cnf->max_gid) { /* Sanity claus^H^H^H^Hheck */
+ cnf->min_gid = 1000;
+ cnf->max_gid = 32000;
+ }
+ bm = bm_alloc(cnf->max_gid - cnf->min_gid + 1);
+
+ /*
+ * Now, let's fill the bitmap from the password file
+ */
+ setgrent();
+ while ((grp = getgrent()) != NULL)
+ if (grp->gr_gid >= (int) cnf->min_gid && grp->gr_gid <= (int) cnf->max_gid)
+ bm_setbit(&bm, grp->gr_gid - cnf->min_gid);
+ endgrent();
+
+ /*
+ * Then apply the policy, with fallback to reuse if necessary
+ */
+ if (cnf->reuse_gids)
+ gid = (gid_t) (bm_firstunset(&bm) + cnf->min_gid);
+ else {
+ gid = (gid_t) (bm_lastset(&bm) + 1);
+ if (!bm_isset(&bm, gid))
+ gid += cnf->min_gid;
+ else
+ gid = (gid_t) (bm_firstunset(&bm) + cnf->min_gid);
+ }
+
+ /*
+ * Another sanity check
+ */
+ if (gid < cnf->min_gid || gid > cnf->max_gid)
+ errx(EX_SOFTWARE, "unable to allocate a new gid - range fully used");
+ bm_dealloc(&bm);
+ }
+ return gid;
+}
+
+
+static int
+print_group(struct group * grp, int pretty)
+{
+ if (!pretty) {
+ int buflen = 0;
+ char *buf = NULL;
+
+ fmtgrent(&buf, &buflen, grp);
+ fputs(buf, stdout);
+ free(buf);
+ } else {
+ int i;
+
+ printf("Group Name: %-15s #%lu\n"
+ " Members: ",
+ grp->gr_name, (long) grp->gr_gid);
+ for (i = 0; grp->gr_mem[i]; i++)
+ printf("%s%s", i ? "," : "", grp->gr_mem[i]);
+ fputs("\n\n", stdout);
+ }
+ return EXIT_SUCCESS;
+}
diff --git a/usr.sbin/pw/pw_log.c b/usr.sbin/pw/pw_log.c
new file mode 100644
index 0000000..e11e5ee
--- /dev/null
+++ b/usr.sbin/pw/pw_log.c
@@ -0,0 +1,67 @@
+/*-
+ * Copyright (C) 1996
+ * David L. Nugent. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY DAVID L. NUGENT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL DAVID L. NUGENT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#include <fcntl.h>
+
+#include "pw.h"
+
+static FILE *logfile = NULL;
+
+void
+pw_log(struct userconf * cnf, int mode, int which, char const * fmt,...)
+{
+ if (cnf->logfile && *cnf->logfile) {
+ if (logfile == NULL) { /* With umask==0 we need to control file access modes on create */
+ int fd = open(cnf->logfile, O_WRONLY | O_CREAT | O_APPEND, 0600);
+
+ if (fd != -1)
+ logfile = fdopen(fd, "a");
+ }
+ if (logfile != NULL) {
+ va_list argp;
+ int l;
+ time_t now = time(NULL);
+ struct tm *t = localtime(&now);
+ char nfmt[256];
+ char *name;
+
+ if ((name = getenv("LOGNAME")) == NULL && (name = getenv("USER")) == NULL)
+ name = "unknown";
+ strftime(nfmt, sizeof nfmt, "%d-%b-%Y %R ", t);
+ l = strlen(nfmt);
+ sprintf(nfmt + strlen(nfmt), "[%s:%s%s] %s\n", name, Which[which], Modes[mode], fmt);
+ va_start(argp, fmt);
+ vfprintf(logfile, nfmt, argp);
+ va_end(argp);
+ fflush(logfile);
+ }
+ }
+}
diff --git a/usr.sbin/pw/pw_nis.c b/usr.sbin/pw/pw_nis.c
new file mode 100644
index 0000000..fe89685
--- /dev/null
+++ b/usr.sbin/pw/pw_nis.c
@@ -0,0 +1,73 @@
+/*-
+ * Copyright (C) 1996
+ * David L. Nugent. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY DAVID L. NUGENT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL DAVID L. NUGENT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+
+#include "pwupd.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..d10c528
--- /dev/null
+++ b/usr.sbin/pw/pw_user.c
@@ -0,0 +1,1111 @@
+/*-
+ * Copyright (C) 1996
+ * David L. Nugent. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY DAVID L. NUGENT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL DAVID L. NUGENT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#include <ctype.h>
+#include <err.h>
+#include <fcntl.h>
+#include <sys/param.h>
+#include <dirent.h>
+#include <paths.h>
+#include <termios.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <unistd.h>
+#include <utmp.h>
+#if defined(USE_MD5RAND)
+#include <md5.h>
+#endif
+#include "pw.h"
+#include "bitmap.h"
+#include "pwupd.h"
+
+#if (MAXLOGNAME-1) > UT_NAMESIZE
+#define LOGNAMESIZE UT_NAMESIZE
+#else
+#define LOGNAMESIZE (MAXLOGNAME-1)
+#endif
+
+static randinit;
+
+static int print_user(struct passwd * pwd, int pretty);
+static uid_t pw_uidpolicy(struct userconf * cnf, struct cargs * args);
+static uid_t pw_gidpolicy(struct userconf * cnf, struct cargs * args, char *nam, gid_t prefer);
+static time_t pw_pwdpolicy(struct userconf * cnf, struct cargs * args);
+static time_t pw_exppolicy(struct userconf * cnf, struct cargs * args);
+static char *pw_homepolicy(struct userconf * cnf, struct cargs * args, char const * user);
+static char *pw_shellpolicy(struct userconf * cnf, struct cargs * args, char *newshell);
+static char *pw_password(struct userconf * cnf, struct cargs * args, char const * user);
+static char *shell_path(char const * path, char *shells[], char *sh);
+static void rmat(uid_t uid);
+static void rmskey(char const * name);
+
+/*-
+ * -C config configuration file
+ * -q quiet operation
+ * -n name login name
+ * -u uid user id
+ * -c comment user name/comment
+ * -d directory home directory
+ * -e date account expiry date
+ * -p date password expiry date
+ * -g grp primary group
+ * -G grp1,grp2 additional groups
+ * -m [ -k dir ] create and set up home
+ * -s shell name of login shell
+ * -o duplicate uid ok
+ * -L class user class
+ * -l name new login name
+ * -h fd password filehandle
+ * -F force print or add
+ * Setting defaults:
+ * -D set user defaults
+ * -b dir default home root dir
+ * -e period default expiry period
+ * -p period default password change period
+ * -g group default group
+ * -G grp1,grp2.. default additional groups
+ * -L class default login class
+ * -k dir default home skeleton
+ * -s shell default shell
+ * -w method default password method
+ */
+
+int
+pw_user(struct userconf * cnf, int mode, struct cargs * args)
+{
+ int r, r1;
+ char *p = NULL;
+ struct carg *a_name;
+ struct carg *a_uid;
+ struct carg *arg;
+ struct passwd *pwd = NULL;
+ struct group *grp;
+ struct stat st;
+ char line[_PASSWORD_LEN+1];
+
+ static struct passwd fakeuser =
+ {
+ NULL,
+ "*",
+ -1,
+ -1,
+ 0,
+ "",
+ "User &",
+ "/bin/sh",
+ 0,
+ 0
+ };
+
+ /*
+ * With M_NEXT, we only need to return the
+ * next uid to stdout
+ */
+ if (mode == M_NEXT)
+ {
+ uid_t next = pw_uidpolicy(cnf, args);
+ if (getarg(args, 'q'))
+ return next;
+ printf("%ld:", (long)next);
+ pw_group(cnf, mode, args);
+ return EXIT_SUCCESS;
+ }
+
+ /*
+ * We can do all of the common legwork here
+ */
+
+ if ((arg = getarg(args, 'b')) != NULL) {
+ cnf->home = arg->val;
+ }
+
+ /*
+ * If we'll need to use it or we're updating it,
+ * then create the base home directory if necessary
+ */
+ if (arg != NULL || getarg(args, 'm') != NULL) {
+ int l = strlen(cnf->home);
+
+ if (l > 1 && cnf->home[l-1] == '/') /* Shave off any trailing path delimiter */
+ cnf->home[--l] = '\0';
+
+ if (l < 2 || *cnf->home != '/') /* Check for absolute path name */
+ errx(EX_DATAERR, "invalid base directory for home '%s'", cnf->home);
+
+ if (stat(cnf->home, &st) == -1) {
+ char dbuf[MAXPATHLEN];
+
+ /*
+ * This is a kludge especially for Joerg :)
+ * If the home directory would be created in the root partition, then
+ * we really create it under /usr which is likely to have more space.
+ * But we create a symlink from cnf->home -> "/usr" -> cnf->home
+ */
+ if (strchr(cnf->home+1, '/') == NULL) {
+ strcpy(dbuf, "/usr");
+ strncat(dbuf, cnf->home, MAXPATHLEN-5);
+ if (mkdir(dbuf, 0755) != -1 || errno == EEXIST) {
+ chown(dbuf, 0, 0);
+ symlink(dbuf, cnf->home);
+ }
+ /* If this falls, fall back to old method */
+ }
+ p = strncpy(dbuf, cnf->home, sizeof dbuf);
+ dbuf[MAXPATHLEN-1] = '\0';
+ if (stat(dbuf, &st) == -1) {
+ while ((p = strchr(++p, '/')) != NULL) {
+ *p = '\0';
+ if (stat(dbuf, &st) == -1) {
+ if (mkdir(dbuf, 0755) == -1)
+ goto direrr;
+ chown(dbuf, 0, 0);
+ } else if (!S_ISDIR(st.st_mode))
+ errx(EX_OSFILE, "'%s' (root home parent) is not a directory", dbuf);
+ *p = '/';
+ }
+ }
+ if (stat(dbuf, &st) == -1) {
+ if (mkdir(dbuf, 0755) == -1) {
+ direrr: err(EX_OSFILE, "mkdir '%s'", dbuf);
+ }
+ chown(dbuf, 0, 0);
+ }
+ } else if (!S_ISDIR(st.st_mode))
+ errx(EX_OSFILE, "root home `%s' is not a directory", cnf->home);
+ }
+
+
+ if ((arg = getarg(args, 'e')) != NULL)
+ cnf->expire_days = atoi(arg->val);
+
+ if ((arg = getarg(args, 'y')) != NULL)
+ cnf->nispasswd = arg->val;
+
+ if ((arg = getarg(args, 'p')) != NULL && arg->val)
+ cnf->password_days = atoi(arg->val);
+
+ if ((arg = getarg(args, 'g')) != NULL) {
+ p = arg->val;
+ if ((grp = getgrnam(p)) == NULL) {
+ if (!isdigit(*p) || (grp = getgrgid((gid_t) atoi(p))) == NULL)
+ errx(EX_NOUSER, "group `%s' does not exist", p);
+ }
+ cnf->default_group = newstr(grp->gr_name);
+ }
+ if ((arg = getarg(args, 'L')) != NULL)
+ cnf->default_class = pw_checkname((u_char *)arg->val, 0);
+
+ if ((arg = getarg(args, 'G')) != NULL && arg->val) {
+ int i = 0;
+
+ for (p = strtok(arg->val, ", \t"); p != NULL; p = strtok(NULL, ", \t")) {
+ if ((grp = getgrnam(p)) == NULL) {
+ if (!isdigit(*p) || (grp = getgrgid((gid_t) atoi(p))) == NULL)
+ errx(EX_NOUSER, "group `%s' does not exist", p);
+ }
+ if (extendarray(&cnf->groups, &cnf->numgroups, i + 2) != -1)
+ cnf->groups[i++] = newstr(grp->gr_name);
+ }
+ while (i < cnf->numgroups)
+ cnf->groups[i++] = NULL;
+ }
+ if ((arg = getarg(args, 'k')) != NULL) {
+ if (stat(cnf->dotdir = arg->val, &st) == -1 || !S_ISDIR(st.st_mode))
+ errx(EX_OSFILE, "skeleton `%s' is not a directory or does not exist", cnf->dotdir);
+ }
+ if ((arg = getarg(args, 's')) != NULL)
+ cnf->shell_default = arg->val;
+
+ if (mode == M_ADD && getarg(args, 'D')) {
+ if (getarg(args, 'n') != NULL)
+ errx(EX_DATAERR, "can't combine `-D' with `-n name'");
+ if ((arg = getarg(args, 'u')) != NULL && (p = strtok(arg->val, ", \t")) != NULL) {
+ if ((cnf->min_uid = (uid_t) atoi(p)) == 0)
+ cnf->min_uid = 1000;
+ if ((p = strtok(NULL, " ,\t")) == NULL || (cnf->max_uid = (uid_t) atoi(p)) < cnf->min_uid)
+ cnf->max_uid = 32000;
+ }
+ if ((arg = getarg(args, 'i')) != NULL && (p = strtok(arg->val, ", \t")) != NULL) {
+ if ((cnf->min_gid = (gid_t) atoi(p)) == 0)
+ cnf->min_gid = 1000;
+ if ((p = strtok(NULL, " ,\t")) == NULL || (cnf->max_gid = (gid_t) atoi(p)) < cnf->min_gid)
+ cnf->max_gid = 32000;
+ }
+ if ((arg = getarg(args, 'w')) != NULL)
+ cnf->default_password = boolean_val(arg->val, cnf->default_password);
+
+ arg = getarg(args, 'C');
+ if (write_userconfig(arg ? arg->val : NULL))
+ return EXIT_SUCCESS;
+ warn("config update");
+ return EX_IOERR;
+ }
+ if (mode == M_PRINT && getarg(args, 'a')) {
+ int pretty = getarg(args, 'P') != NULL;
+
+ setpwent();
+ while ((pwd = getpwent()) != NULL)
+ print_user(pwd, pretty);
+ 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 && isdigit(*a_name->val) && atoi(a_name->val) > 0) { /* Assume uid */
+ (a_uid = a_name)->ch = 'u';
+ a_name = NULL;
+ }
+ }
+ /*
+ * Update, delete & print require that the user exists
+ */
+ if (mode == M_UPDATE || mode == M_DELETE || mode == M_PRINT) {
+ if (a_name == NULL && pwd == NULL) /* Try harder */
+ pwd = getpwuid(atoi(a_uid->val));
+
+ if (pwd == NULL) {
+ if (mode == M_PRINT && getarg(args, 'F')) {
+ fakeuser.pw_name = a_name ? a_name->val : "nouser";
+ fakeuser.pw_uid = a_uid ? (uid_t) atol(a_uid->val) : -1;
+ return print_user(&fakeuser, getarg(args, 'P') != NULL);
+ }
+ if (a_name == NULL)
+ errx(EX_NOUSER, "no such uid `%s'", a_uid->val);
+ errx(EX_NOUSER, "no such user `%s'", a_name->val);
+ }
+ if (a_name == NULL) /* May be needed later */
+ a_name = addarg(args, 'n', newstr(pwd->pw_name));
+
+ /*
+ * Handle deletions now
+ */
+ if (mode == M_DELETE) {
+ char file[MAXPATHLEN];
+ char home[MAXPATHLEN];
+ uid_t uid = pwd->pw_uid;
+
+ if (strcmp(pwd->pw_name, "root") == 0)
+ errx(EX_DATAERR, "cannot remove user 'root'");
+
+ /*
+ * Remove skey record from /etc/skeykeys
+ */
+
+ rmskey(pwd->pw_name);
+
+ /*
+ * Remove crontabs
+ */
+ sprintf(file, "/var/cron/tabs/%s", pwd->pw_name);
+ if (access(file, F_OK) == 0) {
+ sprintf(file, "crontab -u %s -r", pwd->pw_name);
+ system(file);
+ }
+ /*
+ * Save these for later, since contents of pwd may be
+ * invalidated by deletion
+ */
+ sprintf(file, "%s/%s", _PATH_MAILDIR, pwd->pw_name);
+ strncpy(home, pwd->pw_dir, sizeof home);
+ home[sizeof home - 1] = '\0';
+
+ if (!delpwent(pwd))
+ err(EX_IOERR, "error updating passwd file");
+
+ if (cnf->nispasswd && *cnf->nispasswd=='/' && !delnispwent(cnf->nispasswd, a_name->val))
+ warn("WARNING: NIS passwd update");
+
+ editgroups(a_name->val, NULL);
+
+ pw_log(cnf, mode, W_USER, "%s(%ld) account removed", a_name->val, (long) uid);
+
+ /*
+ * 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);
+
+ /*
+ * The rest is edit code
+ */
+ if ((arg = getarg(args, 'l')) != NULL) {
+ if (strcmp(pwd->pw_name, "root") == 0)
+ errx(EX_DATAERR, "can't rename `root' account");
+ pwd->pw_name = pw_checkname((u_char *)arg->val, 0);
+ }
+ if ((arg = getarg(args, 'u')) != NULL && isdigit(*arg->val)) {
+ pwd->pw_uid = (uid_t) atol(arg->val);
+ if (pwd->pw_uid != 0 && strcmp(pwd->pw_name, "root") == 0)
+ errx(EX_DATAERR, "can't change uid of `root' account");
+ if (pwd->pw_uid == 0 && strcmp(pwd->pw_name, "root") != 0)
+ warnx("WARNING: account `%s' will have a uid of 0 (superuser access!)", pwd->pw_name);
+ }
+ if ((arg = getarg(args, 'g')) != NULL && pwd->pw_uid != 0) /* Already checked this */
+ pwd->pw_gid = (gid_t) getgrnam(cnf->default_group)->gr_gid;
+
+ if ((arg = getarg(args, 'p')) != NULL) {
+ if (*arg->val == '\0' || strcmp(arg->val, "0") == 0)
+ pwd->pw_change = 0;
+ else {
+ time_t now = time(NULL);
+ time_t expire = parse_date(now, arg->val);
+
+ if (now == expire)
+ errx(EX_DATAERR, "invalid password change date `%s'", arg->val);
+ pwd->pw_change = expire;
+ }
+ }
+ if ((arg = getarg(args, 'e')) != NULL) {
+ if (*arg->val == '\0' || strcmp(arg->val, "0") == 0)
+ pwd->pw_expire = 0;
+ else {
+ time_t now = time(NULL);
+ time_t expire = parse_date(now, arg->val);
+
+ if (now == expire)
+ errx(EX_DATAERR, "invalid account expiry date `%s'", arg->val);
+ pwd->pw_expire = expire;
+ }
+ }
+ if ((arg = getarg(args, 's')) != NULL)
+ pwd->pw_shell = shell_path(cnf->shelldir, cnf->shells, arg->val);
+
+ if (getarg(args, 'L'))
+ pwd->pw_class = cnf->default_class;
+
+ if ((arg = getarg(args, 'd')) != NULL) {
+ if (stat(pwd->pw_dir = arg->val, &st) == -1) {
+ if (getarg(args, 'm') == NULL && strcmp(pwd->pw_dir, "/nonexistent") != 0)
+ warnx("WARNING: home `%s' does not exist", pwd->pw_dir);
+ } else if (!S_ISDIR(st.st_mode))
+ warnx("WARNING: home `%s' is not a directory", pwd->pw_dir);
+ }
+
+ if ((arg = getarg(args, 'w')) != NULL && getarg(args, 'h') == NULL)
+ pwd->pw_passwd = pw_password(cnf, args, pwd->pw_name);
+
+ } else {
+ if (a_name == NULL) /* Required */
+ errx(EX_DATAERR, "login name required");
+ else if ((pwd = getpwnam(a_name->val)) != NULL) /* Exists */
+ errx(EX_DATAERR, "login name `%s' already exists", a_name->val);
+
+ /*
+ * Now, set up defaults for a new user
+ */
+ pwd = &fakeuser;
+ pwd->pw_name = a_name->val;
+ pwd->pw_class = cnf->default_class ? cnf->default_class : "";
+ pwd->pw_passwd = pw_password(cnf, args, pwd->pw_name);
+ pwd->pw_uid = pw_uidpolicy(cnf, args);
+ pwd->pw_gid = pw_gidpolicy(cnf, args, pwd->pw_name, (gid_t) pwd->pw_uid);
+ pwd->pw_change = pw_pwdpolicy(cnf, args);
+ pwd->pw_expire = pw_exppolicy(cnf, args);
+ pwd->pw_dir = pw_homepolicy(cnf, args, pwd->pw_name);
+ pwd->pw_shell = pw_shellpolicy(cnf, args, NULL);
+
+ if (pwd->pw_uid == 0 && strcmp(pwd->pw_name, "root") != 0)
+ warnx("WARNING: new account `%s' has a uid of 0 (superuser access!)", pwd->pw_name);
+ }
+
+ /*
+ * Shared add/edit code
+ */
+ if ((arg = getarg(args, 'c')) != NULL)
+ pwd->pw_gecos = pw_checkname((u_char *)arg->val, 1);
+
+ if ((arg = getarg(args, 'h')) != NULL) {
+ if (strcmp(arg->val, "-") == 0)
+ pwd->pw_passwd = "*"; /* No access */
+ else {
+ int fd = atoi(arg->val);
+ int b;
+ int istty = isatty(fd);
+ struct termios t;
+
+ if (istty) {
+ if (tcgetattr(fd, &t) == -1)
+ istty = 0;
+ else {
+ struct termios n = t;
+
+ /* Disable echo */
+ n.c_lflag &= ~(ECHO);
+ tcsetattr(fd, TCSANOW, &n);
+ printf("%sassword for user %s:", (mode == M_UPDATE) ? "New p" : "P", pwd->pw_name);
+ fflush(stdout);
+ }
+ }
+ b = read(fd, line, sizeof(line) - 1);
+ if (istty) { /* Restore state */
+ tcsetattr(fd, TCSANOW, &t);
+ fputc('\n', stdout);
+ fflush(stdout);
+ }
+ if (b < 0) {
+ warn("-h file descriptor");
+ return EX_IOERR;
+ }
+ line[b] = '\0';
+ if ((p = strpbrk(line, " \t\r\n")) != NULL)
+ *p = '\0';
+ if (!*line)
+ errx(EX_DATAERR, "empty password read on file descriptor %d", fd);
+ pwd->pw_passwd = pw_pwcrypt(line);
+ }
+ }
+
+ /*
+ * Special case: -N only displays & exits
+ */
+ if (getarg(args, 'N') != NULL)
+ return print_user(pwd, getarg(args, 'P') != NULL);
+
+ r = r1 = 1;
+ if (mode == M_ADD) {
+ r = addpwent(pwd);
+ if (r && cnf->nispasswd && *cnf->nispasswd=='/')
+ r1 = addnispwent(cnf->nispasswd, pwd);
+ } else if (mode == M_UPDATE) {
+ r = chgpwent(a_name->val, pwd);
+ if (r && cnf->nispasswd && *cnf->nispasswd=='/')
+ r1 = chgnispwent(cnf->nispasswd, a_name->val, pwd);
+ }
+
+ if (!r) {
+ warn("password update");
+ return EX_IOERR;
+ } else if (!r1) {
+ warn("WARNING: NIS password update");
+ /* Keep on trucking */
+ }
+
+ /*
+ * Ok, user is created or changed - now edit group file
+ */
+
+ if (mode == M_ADD || getarg(args, 'G') != NULL)
+ editgroups(pwd->pw_name, cnf->groups);
+
+ /* pwd may have been invalidated */
+ if ((pwd = getpwnam(a_name->val)) == NULL)
+ errx(EX_NOUSER, "user '%s' disappeared during update", a_name->val);
+
+ grp = getgrgid(pwd->pw_gid);
+ pw_log(cnf, mode, W_USER, "%s(%ld):%s(%d):%s:%s:%s",
+ pwd->pw_name, (long) pwd->pw_uid,
+ grp ? grp->gr_name : "unknown", (long) (grp ? grp->gr_gid : -1),
+ pwd->pw_gecos, pwd->pw_dir, pwd->pw_shell);
+
+ /*
+ * If adding, let's touch and chown the user's mail file. This is not
+ * strictly necessary under BSD with a 0755 maildir but it also
+ * doesn't hurt anything to create the empty mailfile
+ */
+ if (mode == M_ADD) {
+ FILE *fp;
+
+ sprintf(line, "%s/%s", _PATH_MAILDIR, pwd->pw_name);
+ close(open(line, O_RDWR | O_CREAT, 0600)); /* Preserve contents &
+ * mtime */
+ chown(line, pwd->pw_uid, pwd->pw_gid);
+
+ /*
+ * Send mail to the new user as well, if we are asked to
+ */
+ if (cnf->newmail && *cnf->newmail && (fp = fopen(cnf->newmail, "r")) != NULL) {
+ FILE *pfp = popen(_PATH_SENDMAIL " -t", "w");
+
+ if (pfp == NULL)
+ warn("sendmail");
+ else {
+ fprintf(pfp, "From: root\n" "To: %s\n" "Subject: Welcome!\n\n", pwd->pw_name);
+ while (fgets(line, sizeof(line), fp) != NULL) {
+ /* Do substitutions? */
+ fputs(line, pfp);
+ }
+ pclose(pfp);
+ pw_log(cnf, mode, W_USER, "%s(%ld) new user mail sent",
+ pwd->pw_name, (long) pwd->pw_uid);
+ }
+ fclose(fp);
+ }
+ }
+ /*
+ * Finally, let's create and populate the user's home directory. Note
+ * that this also `works' for editing users if -m is used, but
+ * existing files will *not* be overwritten.
+ */
+ if (getarg(args, 'm') != NULL && pwd->pw_dir && *pwd->pw_dir == '/' && pwd->pw_dir[1]) {
+ copymkdir(pwd->pw_dir, cnf->dotdir, 0755, pwd->pw_uid, pwd->pw_gid);
+ pw_log(cnf, mode, W_USER, "%s(%ld) home %s made",
+ pwd->pw_name, (long) pwd->pw_uid, pwd->pw_dir);
+ }
+ return EXIT_SUCCESS;
+}
+
+
+static uid_t
+pw_uidpolicy(struct userconf * cnf, struct cargs * args)
+{
+ struct passwd *pwd;
+ uid_t uid = (uid_t) - 1;
+ struct carg *a_uid = getarg(args, 'u');
+
+ /*
+ * Check the given uid, if any
+ */
+ if (a_uid != NULL) {
+ uid = (uid_t) atol(a_uid->val);
+
+ if ((pwd = getpwuid(uid)) != NULL && getarg(args, 'o') == NULL)
+ errx(EX_DATAERR, "uid `%ld' has already been allocated", (long) pwd->pw_uid);
+ } else {
+ struct bitmap bm;
+
+ /*
+ * We need to allocate the next available uid under one of
+ * two policies a) Grab the first unused uid b) Grab the
+ * highest possible unused uid
+ */
+ if (cnf->min_uid >= cnf->max_uid) { /* Sanity
+ * claus^H^H^H^Hheck */
+ cnf->min_uid = 1000;
+ cnf->max_uid = 32000;
+ }
+ bm = bm_alloc(cnf->max_uid - cnf->min_uid + 1);
+
+ /*
+ * Now, let's fill the bitmap from the password file
+ */
+ setpwent();
+ while ((pwd = getpwent()) != NULL)
+ if (pwd->pw_uid >= (int) cnf->min_uid && pwd->pw_uid <= (int) cnf->max_uid)
+ bm_setbit(&bm, pwd->pw_uid - cnf->min_uid);
+ endpwent();
+
+ /*
+ * Then apply the policy, with fallback to reuse if necessary
+ */
+ if (cnf->reuse_uids || (uid = (uid_t) (bm_lastset(&bm) + cnf->min_uid + 1)) > cnf->max_uid)
+ uid = (uid_t) (bm_firstunset(&bm) + cnf->min_uid);
+
+ /*
+ * Another sanity check
+ */
+ if (uid < cnf->min_uid || uid > cnf->max_uid)
+ errx(EX_SOFTWARE, "unable to allocate a new uid - range fully used");
+ bm_dealloc(&bm);
+ }
+ return uid;
+}
+
+
+static uid_t
+pw_gidpolicy(struct userconf * cnf, struct cargs * args, char *nam, gid_t prefer)
+{
+ struct group *grp;
+ gid_t gid = (uid_t) - 1;
+ struct carg *a_gid = getarg(args, 'g');
+
+ /*
+ * If no arg given, see if default can help out
+ */
+ if (a_gid == NULL && cnf->default_group && *cnf->default_group)
+ a_gid = addarg(args, 'g', cnf->default_group);
+
+ /*
+ * Check the given gid, if any
+ */
+ setgrent();
+ if (a_gid != NULL) {
+ if ((grp = getgrnam(a_gid->val)) == NULL) {
+ gid = (gid_t) atol(a_gid->val);
+ if ((gid == 0 && !isdigit(*a_gid->val)) || (grp = getgrgid(gid)) == NULL)
+ errx(EX_NOUSER, "group `%s' is not defined", a_gid->val);
+ }
+ gid = grp->gr_gid;
+ } else if ((grp = getgrnam(nam)) != NULL && grp->gr_mem[0] == NULL) {
+ gid = grp->gr_gid; /* Already created? Use it anyway... */
+ } else {
+ struct cargs grpargs;
+ char tmp[32];
+
+ LIST_INIT(&grpargs);
+ addarg(&grpargs, 'n', nam);
+
+ /*
+ * We need to auto-create a group with the user's name. We
+ * can send all the appropriate output to our sister routine
+ * bit first see if we can create a group with gid==uid so we
+ * can keep the user and group ids in sync. We purposely do
+ * NOT check the gid range if we can force the sync. If the
+ * user's name dups an existing group, then the group add
+ * function will happily handle that case for us and exit.
+ */
+ if (getgrgid(prefer) == NULL) {
+ sprintf(tmp, "%lu", (unsigned long) prefer);
+ addarg(&grpargs, 'g', tmp);
+ }
+ if (getarg(args, 'N'))
+ {
+ addarg(&grpargs, 'N', NULL);
+ addarg(&grpargs, 'q', NULL);
+ gid = pw_group(cnf, M_NEXT, &grpargs);
+ }
+ else
+ {
+ pw_group(cnf, M_ADD, &grpargs);
+ if ((grp = getgrnam(nam)) != NULL)
+ gid = grp->gr_gid;
+ }
+ a_gid = grpargs.lh_first;
+ while (a_gid != NULL) {
+ struct carg *t = a_gid->list.le_next;
+ LIST_REMOVE(a_gid, list);
+ a_gid = t;
+ }
+ }
+ endgrent();
+ return gid;
+}
+
+
+static time_t
+pw_pwdpolicy(struct userconf * cnf, struct cargs * args)
+{
+ time_t result = 0;
+ time_t now = time(NULL);
+ struct carg *arg = getarg(args, 'p');
+
+ if (arg != NULL) {
+ if ((result = parse_date(now, arg->val)) == now)
+ errx(EX_DATAERR, "invalid date/time `%s'", arg->val);
+ } else if (cnf->password_days > 0)
+ result = now + ((long) cnf->password_days * 86400L);
+ return result;
+}
+
+
+static time_t
+pw_exppolicy(struct userconf * cnf, struct cargs * args)
+{
+ time_t result = 0;
+ time_t now = time(NULL);
+ struct carg *arg = getarg(args, 'e');
+
+ if (arg != NULL) {
+ if ((result = parse_date(now, arg->val)) == now)
+ errx(EX_DATAERR, "invalid date/time `%s'", arg->val);
+ } else if (cnf->expire_days > 0)
+ result = now + ((long) cnf->expire_days * 86400L);
+ return result;
+}
+
+
+static char *
+pw_homepolicy(struct userconf * cnf, struct cargs * args, char const * user)
+{
+ struct carg *arg = getarg(args, 'd');
+
+ if (arg)
+ return arg->val;
+ else {
+ static char home[128];
+
+ if (cnf->home == NULL || *cnf->home == '\0')
+ errx(EX_CONFIG, "no base home directory set");
+ sprintf(home, "%s/%s", cnf->home, user);
+ return home;
+ }
+}
+
+static char *
+shell_path(char const * path, char *shells[], char *sh)
+{
+ if (sh != NULL && (*sh == '/' || *sh == '\0'))
+ return sh; /* specified full path or forced none */
+ else {
+ char *p;
+ char paths[_UC_MAXLINE];
+
+ /*
+ * We need to search paths
+ */
+ strncpy(paths, path, sizeof paths);
+ paths[sizeof paths - 1] = '\0';
+ for (p = strtok(paths, ": \t\r\n"); p != NULL; p = strtok(NULL, ": \t\r\n")) {
+ int i;
+ static char shellpath[256];
+
+ if (sh != NULL) {
+ sprintf(shellpath, "%s/%s", p, sh);
+ if (access(shellpath, X_OK) == 0)
+ return shellpath;
+ } else
+ for (i = 0; i < _UC_MAXSHELLS && shells[i] != NULL; i++) {
+ sprintf(shellpath, "%s/%s", p, shells[i]);
+ if (access(shellpath, X_OK) == 0)
+ return shellpath;
+ }
+ }
+ if (sh == NULL)
+ errx(EX_OSFILE, "can't find shell `%s' in shell paths", sh);
+ errx(EX_CONFIG, "no default shell available or defined");
+ return NULL;
+ }
+}
+
+
+static char *
+pw_shellpolicy(struct userconf * cnf, struct cargs * args, char *newshell)
+{
+ char *sh = newshell;
+ struct carg *arg = getarg(args, 's');
+
+ if (newshell == NULL && arg != NULL)
+ sh = arg->val;
+ return shell_path(cnf->shelldir, cnf->shells, sh ? sh : cnf->shell_default);
+}
+
+static char const chars[] = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ.";
+
+char *
+pw_pwcrypt(char *password)
+{
+ int i;
+ char salt[12];
+
+ static char buf[256];
+
+ /*
+ * Calculate a salt value
+ */
+ if (!randinit) {
+ randinit = 1;
+#ifdef __FreeBSD__
+ srandomdev();
+#else
+ srandom((unsigned long) (time(NULL) ^ getpid()));
+#endif
+ }
+ for (i = 0; i < 8; i++)
+ salt[i] = chars[random() % 63];
+ salt[i] = '\0';
+
+ return strcpy(buf, crypt(password, salt));
+}
+
+#if defined(USE_MD5RAND)
+u_char *
+pw_getrand(u_char *buf, int len) /* cryptographically secure rng */
+{
+ int i;
+ for (i=0;i<len;i+=16) {
+ u_char ubuf[16];
+
+ MD5_CTX md5_ctx;
+ struct timeval tv, tvo;
+ struct rusage ru;
+ int n=0;
+ int t;
+
+ MD5Init (&md5_ctx);
+ t=getpid();
+ MD5Update (&md5_ctx, (u_char*)&t, sizeof t);
+ t=getppid();
+ MD5Update (&md5_ctx, (u_char*)&t, sizeof t);
+ gettimeofday (&tvo, NULL);
+ do {
+ getrusage (RUSAGE_SELF, &ru);
+ MD5Update (&md5_ctx, (u_char*)&ru, sizeof ru);
+ gettimeofday (&tv, NULL);
+ MD5Update (&md5_ctx, (u_char*)&tv, sizeof tv);
+ } while (n++<20 || tv.tv_usec-tvo.tv_usec<100*1000);
+ MD5Final (ubuf, &md5_ctx);
+ memcpy(buf+i, ubuf, MIN(16, len-n));
+ }
+ return buf;
+}
+
+#else /* Portable version */
+
+static u_char *
+pw_getrand(u_char *buf, int len)
+{
+ int i;
+
+ for (i = 0; i < len; i++) {
+ unsigned long val = random();
+ /* Use all bits in the random value */
+ buf[i]=(u_char)((val >> 24) ^ (val >> 16) ^ (val >> 8) ^ val);
+ }
+ return buf;
+}
+
+#endif
+
+static char *
+pw_password(struct userconf * cnf, struct cargs * args, char const * user)
+{
+ int i, l;
+ char pwbuf[32];
+ u_char rndbuf[sizeof pwbuf];
+
+ switch (cnf->default_password) {
+ case -1: /* Random password */
+ if (!randinit) {
+ randinit = 1;
+#ifdef __FreeBSD__
+ srandomdev();
+#else
+ srandom((unsigned long) (time(NULL) ^ getpid()));
+#endif
+ }
+ l = (random() % 8 + 8); /* 8 - 16 chars */
+ pw_getrand(rndbuf, l);
+ for (i = 0; i < l; i++)
+ pwbuf[i] = chars[rndbuf[i] % sizeof(chars)];
+ pwbuf[i] = '\0';
+
+ /*
+ * We give this information back to the user
+ */
+ if (getarg(args, 'h') == NULL && getarg(args, 'N') == NULL) {
+ if (isatty(1))
+ printf("Password for '%s' is: ", user);
+ printf("%s\n", pwbuf);
+ fflush(stdout);
+ }
+ break;
+
+ case -2: /* No password at all! */
+ return "";
+
+ case 0: /* No login - default */
+ default:
+ return "*";
+
+ case 1: /* user's name */
+ strncpy(pwbuf, user, sizeof pwbuf);
+ pwbuf[sizeof pwbuf - 1] = '\0';
+ break;
+ }
+ return pw_pwcrypt(pwbuf);
+}
+
+
+static int
+print_user(struct passwd * pwd, int pretty)
+{
+ if (!pretty) {
+ char buf[_UC_MAXLINE];
+
+ fmtpwent(buf, pwd);
+ fputs(buf, stdout);
+ } else {
+ int j;
+ char *p;
+ struct group *grp = getgrgid(pwd->pw_gid);
+ char uname[60] = "User &", office[60] = "[None]",
+ wphone[60] = "[None]", hphone[60] = "[None]";
+ char acexpire[32] = "[None]", pwexpire[32] = "[None]";
+ struct tm * tptr;
+
+ if ((p = strtok(pwd->pw_gecos, ",")) != NULL) {
+ strncpy(uname, p, sizeof uname);
+ uname[sizeof uname - 1] = '\0';
+ if ((p = strtok(NULL, ",")) != NULL) {
+ strncpy(office, p, sizeof office);
+ office[sizeof office - 1] = '\0';
+ if ((p = strtok(NULL, ",")) != NULL) {
+ strncpy(wphone, p, sizeof wphone);
+ wphone[sizeof wphone - 1] = '\0';
+ if ((p = strtok(NULL, "")) != NULL) {
+ strncpy(hphone, p, sizeof hphone);
+ hphone[sizeof hphone - 1] = '\0';
+ }
+ }
+ }
+ }
+ /*
+ * Handle '&' in gecos field
+ */
+ if ((p = strchr(uname, '&')) != NULL) {
+ int l = strlen(pwd->pw_name);
+ int m = strlen(p);
+
+ memmove(p + l, p + 1, m);
+ memmove(p, pwd->pw_name, l);
+ *p = (char) toupper(*p);
+ }
+ if (pwd->pw_expire > (time_t)0 && (tptr = localtime(&pwd->pw_expire)) != NULL)
+ strftime(acexpire, sizeof acexpire, "%c", tptr);
+ if (pwd->pw_change > (time_t)9 && (tptr = localtime(&pwd->pw_change)) != NULL)
+ strftime(pwexpire, sizeof pwexpire, "%c", tptr);
+ printf("Login Name: %-15s #%-12ld Group: %-15s #%ld\n"
+ " Full Name: %s\n"
+ " Home: %-26.26s Class: %s\n"
+ " Shell: %-26.26s Office: %s\n"
+ "Work Phone: %-26.26s Home Phone: %s\n"
+ "Acc Expire: %-26.26s Pwd Expire: %s\n",
+ pwd->pw_name, (long) pwd->pw_uid,
+ grp ? grp->gr_name : "(invalid)", (long) pwd->pw_gid,
+ uname, pwd->pw_dir, pwd->pw_class,
+ pwd->pw_shell, office, wphone, hphone,
+ acexpire, pwexpire);
+ setgrent();
+ j = 0;
+ while ((grp=getgrent()) != NULL)
+ {
+ int i = 0;
+ while (grp->gr_mem[i] != NULL)
+ {
+ if (strcmp(grp->gr_mem[i], pwd->pw_name)==0)
+ {
+ printf(j++ == 0 ? " Groups: %s" : ",%s", grp->gr_name);
+ break;
+ }
+ ++i;
+ }
+ }
+ endgrent();
+ printf("%s\n", j ? "\n" : "");
+ }
+ return EXIT_SUCCESS;
+}
+
+char *
+pw_checkname(u_char *name, int gecos)
+{
+ int l = 0;
+ char const *notch = gecos ? ":!@" : " ,\t:+&#%$^()!@~*?<>=|\\/\"";
+
+ while (name[l]) {
+ if (strchr(notch, name[l]) != NULL || name[l] < ' ' || name[l] == 127 ||
+ (!gecos && l==0 && name[l] == '-') || /* leading '-' */
+ (!gecos && name[l] & 0x80)) /* 8-bit */
+ errx(EX_DATAERR, (name[l] >= ' ' && name[l] < 127)
+ ? "invalid character `%c' in field"
+ : "invalid character 0x%02x in field",
+ name[l]);
+ ++l;
+ }
+ if (!gecos && l > LOGNAMESIZE)
+ errx(EX_DATAERR, "name too long `%s'", name);
+ return (char *)name;
+}
+
+
+static void
+rmat(uid_t uid)
+{
+ DIR *d = opendir("/var/at/jobs");
+
+ if (d != NULL) {
+ struct dirent *e;
+
+ while ((e = readdir(d)) != NULL) {
+ struct stat st;
+
+ if (strncmp(e->d_name, ".lock", 5) != 0 &&
+ stat(e->d_name, &st) == 0 &&
+ !S_ISDIR(st.st_mode) &&
+ st.st_uid == uid) {
+ char tmp[MAXPATHLEN];
+
+ sprintf(tmp, "/usr/bin/atrm %s", e->d_name);
+ system(tmp);
+ }
+ }
+ closedir(d);
+ }
+}
+
+static void
+rmskey(char const * name)
+{
+ static const char etcskey[] = "/etc/skeykeys";
+ FILE *fp = fopen(etcskey, "r+");
+
+ if (fp != NULL) {
+ char tmp[1024];
+ off_t atofs = 0;
+ int length = strlen(name);
+
+ while (fgets(tmp, sizeof tmp, fp) != NULL) {
+ if (strncmp(name, tmp, length) == 0 && tmp[length]==' ') {
+ if (fseek(fp, atofs, SEEK_SET) == 0) {
+ fwrite("#", 1, 1, fp); /* Comment username out */
+ }
+ break;
+ }
+ atofs = ftell(fp);
+ }
+ /*
+ * If we got an error of any sort, don't update!
+ */
+ fclose(fp);
+ }
+}
+
diff --git a/usr.sbin/pw/pwupd.c b/usr.sbin/pw/pwupd.c
new file mode 100644
index 0000000..0b68266
--- /dev/null
+++ b/usr.sbin/pw/pwupd.c
@@ -0,0 +1,163 @@
+/*-
+ * Copyright (C) 1996
+ * David L. Nugent. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY DAVID L. NUGENT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL DAVID L. NUGENT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+
+#include "pwupd.h"
+
+#define HAVE_PWDB_C 1
+
+static int
+pwdb(char *arg,...)
+{
+ int i = 0;
+ pid_t pid;
+ va_list ap;
+ char *args[8];
+
+ args[i++] = _PATH_PWD_MKDB;
+ va_start(ap, arg);
+ while (i < 6 && arg != NULL) {
+ args[i++] = arg;
+ arg = va_arg(ap, char *);
+ }
+ args[i++] = _PATH_MASTERPASSWD;
+ args[i] = NULL;
+
+ if ((pid = fork()) == -1) /* Error (errno set) */
+ i = -1;
+ else if (pid == 0) { /* Child */
+ execv(args[0], args);
+ _exit(1);
+ } else { /* Parent */
+ waitpid(pid, &i, 0);
+ if ((i = WEXITSTATUS(i)) != 0)
+ errno = EIO; /* set SOMETHING */
+ }
+ return i;
+}
+
+int
+fmtpwentry(char *buf, struct passwd * pwd, int type)
+{
+ int l;
+ char *pw;
+
+ pw = (pwd->pw_passwd == NULL || !*pwd->pw_passwd) ? "" : (type == PWF_MASTER) ? pwd->pw_passwd : "*";
+
+ if (type == PWF_PASSWD)
+ l = sprintf(buf, "%s:*:%ld:%ld:%s:%s:%s\n",
+ pwd->pw_name, (long) pwd->pw_uid, (long) pwd->pw_gid,
+ pwd->pw_gecos ? pwd->pw_gecos : "User &",
+ pwd->pw_dir, pwd->pw_shell);
+ else
+ l = sprintf(buf, "%s:%s:%ld:%ld:%s:%lu:%lu:%s:%s:%s\n",
+ pwd->pw_name, pw, (long) pwd->pw_uid, (long) pwd->pw_gid,
+ pwd->pw_class ? pwd->pw_class : "",
+ (unsigned long) pwd->pw_change,
+ (unsigned long) pwd->pw_expire,
+ pwd->pw_gecos, pwd->pw_dir, pwd->pw_shell);
+ return l;
+}
+
+
+int
+fmtpwent(char *buf, struct passwd * pwd)
+{
+ return fmtpwentry(buf, pwd, PWF_STANDARD);
+}
+
+static int
+pw_update(struct passwd * pwd, char const * user, int mode)
+{
+ int rc = 0;
+
+ endpwent();
+
+ /*
+ * First, let's check the see if the database is alright
+ * Note: -c is only available in FreeBSD 2.2 and above
+ */
+#ifdef HAVE_PWDB_C
+ if (pwdb("-c", NULL) == 0) { /* Check only */
+#else
+ { /* No -c */
+#endif
+ char pfx[32];
+ char pwbuf[PWBUFSZ];
+ int l = sprintf(pfx, "%s:", user);
+
+ /*
+ * Update the passwd file first
+ */
+ if (pwd == NULL)
+ *pwbuf = '\0';
+ else
+ fmtpwentry(pwbuf, pwd, PWF_PASSWD);
+ if ((rc = fileupdate(_PATH_PASSWD, 0644, pwbuf, pfx, l, mode)) != 0) {
+
+ /*
+ * Then the master.passwd file
+ */
+ if (pwd != NULL)
+ fmtpwentry(pwbuf, pwd, PWF_MASTER);
+ if ((rc = fileupdate(_PATH_MASTERPASSWD, 0644, pwbuf, pfx, l, mode)) != 0)
+ rc = pwdb(NULL) == 0;
+ }
+ }
+ return rc;
+}
+
+int
+addpwent(struct passwd * pwd)
+{
+ return pw_update(pwd, pwd->pw_name, UPD_CREATE);
+}
+
+int
+chgpwent(char const * login, struct passwd * pwd)
+{
+ return pw_update(pwd, login, UPD_REPLACE);
+}
+
+int
+delpwent(struct passwd * pwd)
+{
+ return pw_update(NULL, pwd->pw_name, UPD_DELETE);
+}
diff --git a/usr.sbin/pw/pwupd.h b/usr.sbin/pw/pwupd.h
new file mode 100644
index 0000000..0bb0389
--- /dev/null
+++ b/usr.sbin/pw/pwupd.h
@@ -0,0 +1,81 @@
+/*-
+ * Copyright (C) 1996
+ * David L. Nugent. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY DAVID L. NUGENT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL DAVID L. NUGENT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id$
+ */
+
+#ifndef _PWUPD_H_
+#define _PWUPD_H_
+
+#include <sys/types.h>
+#include <pwd.h>
+#include <grp.h>
+
+#include <sys/cdefs.h>
+
+enum updtype
+{
+ UPD_DELETE = -1,
+ UPD_CREATE = 0,
+ UPD_REPLACE = 1
+};
+
+__BEGIN_DECLS
+int fileupdate __P((char const * fname, mode_t fm, char const * nline, char const * pfx, int pfxlen, int updmode));
+__END_DECLS
+
+enum pwdfmttype
+{
+ PWF_STANDARD, /* MASTER format but with '*' as password */
+ PWF_PASSWD, /* V7 format */
+ PWF_GROUP = PWF_PASSWD,
+ PWF_MASTER /* MASTER format with password */
+};
+
+__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 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));
+__END_DECLS
+
+#define PWBUFSZ 1024
+
+__BEGIN_DECLS
+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
+
+#endif /* !_PWUPD_H */
diff --git a/usr.sbin/pw/rm_r.c b/usr.sbin/pw/rm_r.c
new file mode 100644
index 0000000..f671486
--- /dev/null
+++ b/usr.sbin/pw/rm_r.c
@@ -0,0 +1,74 @@
+/*-
+ * Copyright (C) 1996
+ * David L. Nugent. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY DAVID L. NUGENT AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL DAVID L. NUGENT OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <unistd.h>
+#include <dirent.h>
+
+#include "pwupd.h"
+
+void
+rm_r(char const * dir, uid_t uid)
+{
+ DIR *d = opendir(dir);
+
+ if (d != NULL) {
+ struct dirent *e;
+ struct stat st;
+ char file[MAXPATHLEN];
+
+ while ((e = readdir(d)) != NULL) {
+ if (strcmp(e->d_name, ".") != 0 && strcmp(e->d_name, "..") != 0) {
+ sprintf(file, "%s/%s", dir, e->d_name);
+ if (lstat(file, &st) == 0) { /* Need symlinks, not
+ * linked file */
+ if (S_ISDIR(st.st_mode)) /* Directory - recurse */
+ rm_r(file, uid);
+ else {
+ if (S_ISLNK(st.st_mode) || st.st_uid == uid)
+ remove(file);
+ }
+ }
+ }
+ }
+ closedir(d);
+ if (lstat(dir, &st) == 0) {
+ if (S_ISLNK(st.st_mode))
+ remove(dir);
+ else if (st.st_uid == uid)
+ rmdir(dir);
+ }
+ }
+}
diff --git a/usr.sbin/pwd_mkdb/Makefile b/usr.sbin/pwd_mkdb/Makefile
new file mode 100644
index 0000000..25d001e
--- /dev/null
+++ b/usr.sbin/pwd_mkdb/Makefile
@@ -0,0 +1,8 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= pwd_mkdb
+SRCS= pw_scan.c pwd_mkdb.c
+MAN8= pwd_mkdb.8
+CFLAGS+= -DPASSWD_IGNORE_COMMENTS
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/pwd_mkdb/pw_scan.c b/usr.sbin/pwd_mkdb/pw_scan.c
new file mode 100644
index 0000000..d474786
--- /dev/null
+++ b/usr.sbin/pwd_mkdb/pw_scan.c
@@ -0,0 +1,144 @@
+/*-
+ * Copyright (c) 1990, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)pw_scan.c 8.3 (Berkeley) 4/2/94";
+#endif
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+/*
+ * This module is used to "verify" password entries by chpass(1) and
+ * pwd_mkdb(8).
+ */
+
+#include <sys/param.h>
+
+#include <err.h>
+#include <fcntl.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "pw_scan.h"
+
+int
+pw_scan(bp, pw)
+ char *bp;
+ struct passwd *pw;
+{
+ long id;
+ int root;
+ char *p, *sh;
+
+ pw->pw_fields = 0;
+ if (!(pw->pw_name = strsep(&bp, ":"))) /* login */
+ goto fmt;
+ root = !strcmp(pw->pw_name, "root");
+ if(pw->pw_name[0] && (pw->pw_name[0] != '+' || pw->pw_name[1] == '\0'))
+ pw->pw_fields |= _PWF_NAME;
+
+ if (!(pw->pw_passwd = strsep(&bp, ":"))) /* passwd */
+ goto fmt;
+ if(pw->pw_passwd[0]) pw->pw_fields |= _PWF_PASSWD;
+
+ if (!(p = strsep(&bp, ":"))) /* uid */
+ goto fmt;
+ if(p[0]) pw->pw_fields |= _PWF_UID;
+ id = atol(p);
+ if (root && id) {
+ warnx("root uid should be 0");
+ return (0);
+ }
+ if (id > USHRT_MAX) {
+ warnx("%s > max uid value (%d)", p, USHRT_MAX);
+ /*return (0);*/ /* THIS SHOULD NOT BE FATAL! */
+ }
+ pw->pw_uid = id;
+
+ if (!(p = strsep(&bp, ":"))) /* gid */
+ goto fmt;
+ if(p[0]) pw->pw_fields |= _PWF_GID;
+ id = atol(p);
+ if (id > USHRT_MAX) {
+ warnx("%s > max gid value (%d)", p, USHRT_MAX);
+ /* return (0); This should not be fatal! */
+ }
+ pw->pw_gid = id;
+
+ pw->pw_class = strsep(&bp, ":"); /* class */
+ if(pw->pw_class[0]) pw->pw_fields |= _PWF_CLASS;
+
+ if (!(p = strsep(&bp, ":"))) /* change */
+ goto fmt;
+ if(p[0]) pw->pw_fields |= _PWF_CHANGE;
+ pw->pw_change = atol(p);
+
+ if (!(p = strsep(&bp, ":"))) /* expire */
+ goto fmt;
+ if(p[0]) pw->pw_fields |= _PWF_EXPIRE;
+ pw->pw_expire = atol(p);
+
+ if (!(pw->pw_gecos = strsep(&bp, ":"))) /* gecos */
+ goto fmt;
+ if(pw->pw_gecos[0]) pw->pw_fields |= _PWF_GECOS;
+
+ if (!(pw->pw_dir = strsep(&bp, ":"))) /* directory */
+ goto fmt;
+ if(pw->pw_dir[0]) pw->pw_fields |= _PWF_DIR;
+
+ if (!(pw->pw_shell = strsep(&bp, ":"))) /* shell */
+ goto fmt;
+
+ p = pw->pw_shell;
+ if (root && *p) /* empty == /bin/sh */
+ for (setusershell();;) {
+ if (!(sh = getusershell())) {
+ warnx("warning, unknown root shell");
+ break;
+ }
+ if (!strcmp(p, sh))
+ break;
+ }
+ if(p[0]) pw->pw_fields |= _PWF_SHELL;
+
+ if ((p = strsep(&bp, ":"))) { /* too many */
+fmt: warnx("corrupted entry");
+ return (0);
+ }
+ return (1);
+}
diff --git a/usr.sbin/pwd_mkdb/pw_scan.h b/usr.sbin/pwd_mkdb/pw_scan.h
new file mode 100644
index 0000000..d1d4bc1
--- /dev/null
+++ b/usr.sbin/pwd_mkdb/pw_scan.h
@@ -0,0 +1,36 @@
+/*-
+ * Copyright (c) 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)pw_scan.h 8.1 (Berkeley) 4/1/94
+ */
+
+extern int pw_scan __P((char *, struct passwd *));
diff --git a/usr.sbin/pwd_mkdb/pwd_mkdb.8 b/usr.sbin/pwd_mkdb/pwd_mkdb.8
new file mode 100644
index 0000000..589d0e9
--- /dev/null
+++ b/usr.sbin/pwd_mkdb/pwd_mkdb.8
@@ -0,0 +1,141 @@
+.\" Copyright (c) 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)pwd_mkdb.8 8.1 (Berkeley) 6/6/93
+.\"
+.Dd June 6, 1993
+.Dt PWD_MKDB 8
+.Os
+.Sh NAME
+.Nm pwd_mkdb
+.Nd "generate the password databases"
+.Sh SYNOPSIS
+.Nm pwd_mkdb
+.Op Fl c
+.Op Fl p
+.Op Fl d Ar directory
+.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 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.
+.El
+.Pp
+The two databases differ in that the secure version contains the user's
+encrypted password and the insecure version has an asterisk (``*'')
+.Pp
+The databases are used by the C library password routines (see
+.Xr getpwent 3 ) .
+.Pp
+.Nm Pwd_mkdb
+exits zero on success, non-zero on failure.
+.Sh FILES
+.Bl -tag -width Pa -compact
+.It Pa /etc/pwd.db
+The insecure password database file.
+.It Pa /etc/pwd.db.tmp
+A temporary file.
+.It Pa /etc/spwd.db
+The secure password database file.
+.It Pa /etc/spwd.db.tmp
+A temporary file.
+.It Pa /etc/master.passwd
+The current password file.
+.It Pa /etc/passwd
+A Version 7 format password file.
+.El
+.Sh BUGS
+Because of the necessity for atomic update of the password files,
+.Nm
+uses
+.Xr rename 2
+to install them.
+This, however, requires that the file specified on the command line live
+on the same file system as the
+.Pa /etc
+directory.
+.Pp
+There are the obvious races with multiple people running
+.Nm
+on different password files at the same time.
+The front-ends to
+.Nm pwd_mkdb ,
+.Xr chpass 1 ,
+.Xr passwd 1
+and
+.Xr vipw 8 ,
+handle the locking necessary to avoid this problem.
+.Sh COMPATIBILITY
+Previous versions of the system had a program similar to
+.Nm pwd_mkdb ,
+.Xr mkpasswd 8 ,
+which built
+.Xr dbm 3
+style databases for the password file but depended on the calling programs
+to install them.
+The program was renamed in order that previous users of the program
+not be surprised by the changes in functionality.
+.Sh SEE ALSO
+.Xr chpass 1 ,
+.Xr passwd 1 ,
+.Xr db 3 ,
+.Xr getpwent 3 ,
+.Xr passwd 5 ,
+.Xr vipw 8
diff --git a/usr.sbin/pwd_mkdb/pwd_mkdb.c b/usr.sbin/pwd_mkdb/pwd_mkdb.c
new file mode 100644
index 0000000..c856536
--- /dev/null
+++ b/usr.sbin/pwd_mkdb/pwd_mkdb.c
@@ -0,0 +1,577 @@
+/*-
+ * Copyright (c) 1991, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 1991, 1993, 1994\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)pwd_mkdb.c 8.5 (Berkeley) 4/20/94";
+#endif
+static const char rcsid[] =
+ "$Id$";
+#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 Cflag; /* flag for comments */
+static char line[LINE_MAX];
+
+void cleanup __P((void));
+void error __P((char *));
+void cp __P((char *, char *, mode_t mode));
+void mv __P((char *, char *));
+int scan __P((FILE *, struct passwd *));
+static void usage __P((void));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ DB *dp, *sdp, *pw_db;
+ DBT data, sdata, key;
+ FILE *fp, *oldfp;
+ sigset_t set;
+ int ch, cnt, ypcnt, len, makeold, tfd, yp_enabled = 0;
+ char *p, *t;
+ char buf[MAX(MAXPATHLEN, LINE_MAX * 2)], tbuf[1024];
+ char sbuf[MAX(MAXPATHLEN, LINE_MAX * 2)];
+ char buf2[MAXPATHLEN];
+ char sbuf2[MAXPATHLEN];
+ char *username;
+ u_int method, methoduid;
+ int cflag;
+
+ cflag = 0;
+ strcpy(prefix, _PATH_PWD);
+ makeold = 0;
+ username = NULL;
+ while ((ch = getopt(argc, argv, "cd:pu:v")) != -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 'u': /* only update this record */
+ username = optarg;
+ break;
+ case 'v': /* backward compatible */
+ break;
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc != 1 || (username && (*username == '+' || *username == '-')))
+ usage();
+
+ /*
+ * This could be changed to allow the user to interrupt.
+ * Probably not worth the effort.
+ */
+ sigemptyset(&set);
+ sigaddset(&set, SIGTSTP);
+ sigaddset(&set, SIGHUP);
+ sigaddset(&set, SIGINT);
+ sigaddset(&set, SIGQUIT);
+ sigaddset(&set, SIGTERM);
+ (void)sigprocmask(SIG_BLOCK, &set, (sigset_t *)NULL);
+
+ /* We don't care what the user wants. */
+ (void)umask(0);
+
+ pname = *argv;
+ /* Open the original password file */
+ if (!(fp = fopen(pname, "r")))
+ error(pname);
+
+ /* 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;
+ }
+ (void)(pw_db->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 (!Cflag &&
+ (pwd.pw_name[0] == '+' || pwd.pw_name[0] == '-'))
+ yp_enabled = 1;
+#define COMPACT(e) t = e; while ((*p++ = *t++));
+ if (!Cflag &&
+ (!username || (strcmp(username, pwd.pw_name) == 0))) {
+ /* Create insecure data. */
+ p = buf;
+ COMPACT(pwd.pw_name);
+ COMPACT("*");
+ memmove(p, &pwd.pw_uid, sizeof(int));
+ p += sizeof(int);
+ memmove(p, &pwd.pw_gid, sizeof(int));
+ p += sizeof(int);
+ memmove(p, &pwd.pw_change, sizeof(time_t));
+ p += sizeof(time_t);
+ COMPACT(pwd.pw_class);
+ COMPACT(pwd.pw_gecos);
+ COMPACT(pwd.pw_dir);
+ COMPACT(pwd.pw_shell);
+ memmove(p, &pwd.pw_expire, sizeof(time_t));
+ p += sizeof(time_t);
+ memmove(p, &pwd.pw_fields, sizeof pwd.pw_fields);
+ p += sizeof pwd.pw_fields;
+ data.size = p - buf;
+
+ /* Create secure data. */
+ p = sbuf;
+ COMPACT(pwd.pw_name);
+ COMPACT(pwd.pw_passwd);
+ memmove(p, &pwd.pw_uid, sizeof(int));
+ p += sizeof(int);
+ memmove(p, &pwd.pw_gid, sizeof(int));
+ p += sizeof(int);
+ memmove(p, &pwd.pw_change, sizeof(time_t));
+ p += sizeof(time_t);
+ COMPACT(pwd.pw_class);
+ COMPACT(pwd.pw_gecos);
+ COMPACT(pwd.pw_dir);
+ COMPACT(pwd.pw_shell);
+ memmove(p, &pwd.pw_expire, sizeof(time_t));
+ p += sizeof(time_t);
+ memmove(p, &pwd.pw_fields, sizeof pwd.pw_fields);
+ p += sizeof pwd.pw_fields;
+ sdata.size = p - sbuf;
+
+ /* Store insecure by name. */
+ tbuf[0] = _PW_KEYBYNAME;
+ len = strlen(pwd.pw_name);
+ memmove(tbuf + 1, pwd.pw_name, len);
+ key.size = len + 1;
+ if ((dp->put)(dp, &key, &data, method) == -1)
+ error("put");
+
+ /* Store insecure by number. */
+ tbuf[0] = _PW_KEYBYNUM;
+ memmove(tbuf + 1, &cnt, sizeof(cnt));
+ key.size = sizeof(cnt) + 1;
+ if ((dp->put)(dp, &key, &data, method) == -1)
+ error("put");
+
+ /* Store insecure by uid. */
+ tbuf[0] = _PW_KEYBYUID;
+ memmove(tbuf + 1, &pwd.pw_uid, sizeof(pwd.pw_uid));
+ key.size = sizeof(pwd.pw_uid) + 1;
+ if ((dp->put)(dp, &key, &data, methoduid) == -1)
+ error("put");
+
+ /* Store secure by name. */
+ tbuf[0] = _PW_KEYBYNAME;
+ len = strlen(pwd.pw_name);
+ memmove(tbuf + 1, pwd.pw_name, len);
+ key.size = len + 1;
+ if ((sdp->put)(sdp, &key, &sdata, method) == -1)
+ error("put");
+
+ /* Store secure by number. */
+ tbuf[0] = _PW_KEYBYNUM;
+ memmove(tbuf + 1, &cnt, sizeof(cnt));
+ key.size = sizeof(cnt) + 1;
+ if ((sdp->put)(sdp, &key, &sdata, method) == -1)
+ error("put");
+
+ /* Store secure by uid. */
+ tbuf[0] = _PW_KEYBYUID;
+ memmove(tbuf + 1, &pwd.pw_uid, sizeof(pwd.pw_uid));
+ key.size = sizeof(pwd.pw_uid) + 1;
+ if ((sdp->put)(sdp, &key, &sdata, methoduid) == -1)
+ error("put");
+
+ /* Store insecure and secure special plus and special minus */
+ if (pwd.pw_name[0] == '+' || pwd.pw_name[0] == '-') {
+ tbuf[0] = _PW_KEYYPBYNUM;
+ memmove(tbuf + 1, &ypcnt, sizeof(cnt));
+ ypcnt++;
+ key.size = sizeof(cnt) + 1;
+ if ((dp->put)(dp, &key, &data, method) == -1)
+ error("put");
+ if ((sdp->put)(sdp, &key, &sdata, method) == -1)
+ error("put");
+ }
+ }
+ /* Create original format password file entry */
+ if (Cflag && makeold) /* copy comments */
+ (void)fprintf(oldfp, "%s\n", line);
+ else if (makeold) {
+ char uidstr[20];
+ char gidstr[20];
+
+ snprintf(uidstr, sizeof(uidstr), "%d", pwd.pw_uid);
+ snprintf(gidstr, sizeof(gidstr), "%d", pwd.pw_gid);
+
+ (void)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);
+ }
+ }
+ /* 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);
+ (void)fclose(oldfp);
+ }
+
+ /* Set master.passwd permissions, in case caller forgot. */
+ (void)fchmod(fileno(fp), S_IRUSR|S_IWUSR);
+ (void)fclose(fp);
+
+ /* 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);
+ exit(0);
+}
+
+int
+scan(fp, pw)
+ FILE *fp;
+ struct passwd *pw;
+{
+ static int lcnt;
+ char *p;
+
+ if (!fgets(line, sizeof(line), fp))
+ return (0);
+ ++lcnt;
+ /*
+ * ``... if I swallow anything evil, put your fingers down my
+ * throat...''
+ * -- The Who
+ */
+ if (!(p = strchr(line, '\n'))) {
+ warnx("line too long");
+ goto fmt;
+
+ }
+ *p = '\0';
+
+#ifdef PASSWD_IGNORE_COMMENTS
+ /*
+ * Ignore comments: ^[ \t]*#
+ */
+ for (p = line; *p != '\0'; p++)
+ if (*p != ' ' && *p != '\t')
+ break;
+ if (*p == '#' || *p == '\0') {
+ Cflag = 1;
+ return(1);
+ } else
+ Cflag = 0;
+#endif
+
+ if (!pw_scan(line, pw)) {
+ warnx("at line #%d", lcnt);
+fmt: errno = EFTYPE; /* XXX */
+ error(pname);
+ }
+
+ return (1);
+}
+
+void
+cp(from, to, mode)
+ char *from, *to;
+ mode_t mode;
+{
+ static char buf[MAXBSIZE];
+ int from_fd, rcount, to_fd, wcount;
+
+ if ((from_fd = open(from, O_RDONLY, 0)) < 0)
+ error(from);
+ if ((to_fd = open(to, O_WRONLY|O_CREAT|O_EXCL, mode)) < 0)
+ error(to);
+ while ((rcount = read(from_fd, buf, MAXBSIZE)) > 0) {
+ wcount = write(to_fd, buf, rcount);
+ if (rcount != wcount || wcount == -1) {
+ int sverrno = errno;
+
+ (void)snprintf(buf, sizeof(buf), "%s to %s", from, to);
+ errno = sverrno;
+ error(buf);
+ }
+ }
+ if (rcount < 0) {
+ int sverrno = errno;
+
+ (void)snprintf(buf, sizeof(buf), "%s to %s", from, to);
+ errno = sverrno;
+ error(buf);
+ }
+}
+
+
+void
+mv(from, to)
+ char *from, *to;
+{
+ char buf[MAXPATHLEN];
+
+ if (rename(from, to)) {
+ int sverrno = errno;
+ (void)snprintf(buf, sizeof(buf), "%s to %s", from, to);
+ errno = sverrno;
+ error(buf);
+ }
+}
+
+void
+error(name)
+ char *name;
+{
+
+ warn("%s", name);
+ cleanup();
+ exit(1);
+}
+
+void
+cleanup()
+{
+ char buf[MAXPATHLEN];
+
+ switch(clean) {
+ case FILE_ORIG:
+ (void)snprintf(buf, sizeof(buf), "%s.orig", pname);
+ (void)unlink(buf);
+ /* FALLTHROUGH */
+ case FILE_SECURE:
+ (void)snprintf(buf, sizeof(buf), "%s/%s.tmp", prefix, _SMP_DB);
+ (void)unlink(buf);
+ /* FALLTHROUGH */
+ case FILE_INSECURE:
+ (void)snprintf(buf, sizeof(buf), "%s/%s.tmp", prefix, _MP_DB);
+ (void)unlink(buf);
+ }
+}
+
+static void
+usage()
+{
+
+ (void)fprintf(stderr,
+"usage: pwd_mkdb [-c] [-p] [-d <dest dir>] [-u <local username>] file\n");
+ exit(1);
+}
diff --git a/usr.sbin/qcamcontrol/Makefile b/usr.sbin/qcamcontrol/Makefile
new file mode 100644
index 0000000..d5f42cf
--- /dev/null
+++ b/usr.sbin/qcamcontrol/Makefile
@@ -0,0 +1,3 @@
+PROG= qcamcontrol
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/qcamcontrol/qcamcontrol.1 b/usr.sbin/qcamcontrol/qcamcontrol.1
new file mode 100644
index 0000000..eecb43b
--- /dev/null
+++ b/usr.sbin/qcamcontrol/qcamcontrol.1
@@ -0,0 +1,68 @@
+.Dd Feburary 29, 1996
+.Dt QCAMCONTROL 1
+.Os FreeBSD
+.Sh NAME
+.Nm qcamcontrol
+.Nd Connectix QuickCam control utility
+.Sh SYNOPSIS
+.Nm qcamcontrol
+.Op Fl b Ar brightness
+.Op Fl c Ar contrast
+.Op Fl d Ar depth
+.Op Fl p Ar device
+.Op Fl w Ar whitebalance
+.Op Fl x Ar xsize
+.Op Fl y Ar ysize
+.Op Fl z Ar zoom
+.Sh DESCRIPTION
+.Nm Qcamcontrol
+is a program to demonstrate control over the Connectix QuickCam(TM)
+parallel port camera and to take a single frame picture.
+.Pp
+If the device not specified,
+.Pa /dev/qcam0
+is assumed.
+If no command is given, then
+.Nm
+will use its defaults to grab a single frame from the camera. The control
+program will output a portable pixmap (ppm) file to stdout.
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl b Ar brightness
+Control the brightness of the picture (0..255).
+.It Fl c Ar contrast
+Control the contrast of the picture (0..255).
+.It Fl d Ar depth
+16 or 64 shades of gray (specified as 4 or 6 bits per pixel).
+.It Fl p Ar device
+Quickcam device (port) (default is
+.Pa /dev/qcam0 )
+.It Fl w Ar whitebalance
+Amount of white in the picture (0..255).
+.It Fl x Ar xsize
+Width of image (320 or less).
+.It Fl y Ar ysize
+Height of image (240 or less).
+.It Fl z Ar zoom
+Zoom in (1x, 1.5x, or 2x).
+.El
+.Sh BUGS
+.Nm Qcamcontrol
+does not enforce a proper aspect ratio for x y sizes.
+In practice, standard picture sizes are 320x240 and 180x160 and all smaller
+sizes that maintain a similar aspect ratio.
+Also, the camera is notoriously finicky until you get just the right
+combination of white-balance, contrast, and brightness. Improper values
+will return all-black or all-white pictures.
+.Sh FILES
+.Bl -tag -width /dev/qcam0 -compact
+.It Pa /dev/qcam0
+.El
+.Sh AUTHOR
+.An Paul Traina
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Fx 2.1.5 .
diff --git a/usr.sbin/qcamcontrol/qcamcontrol.c b/usr.sbin/qcamcontrol/qcamcontrol.c
new file mode 100644
index 0000000..b52cca5
--- /dev/null
+++ b/usr.sbin/qcamcontrol/qcamcontrol.c
@@ -0,0 +1,224 @@
+/*
+ * QuickCam(TM) driver control program.
+ * Copyright (c) 1996, Paul Traina.
+ *
+ * QuickCam(TM) is a registered trademark of Connectix Inc.
+ * Use this driver at your own risk, it is not warranted by
+ * Connectix or the authors.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer
+ * in this position and unchanged.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software withough specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/fcntl.h>
+#include <sys/ioctl.h>
+#include <machine/qcam.h>
+
+#ifdef LINUX
+#include <getopt.h>
+#endif
+
+void print_data(struct qcam *data)
+{
+ fprintf(stderr, "version=%d, (%d,%d) at (%d,%d) @%dbpp "
+ "zoom=%d, exp=%d, b/w/c=%d/%d/%d\n",
+ data->qc_version,
+ data->qc_xsize, data->qc_ysize,
+ data->qc_xorigin, data->qc_yorigin,
+ data->qc_bpp,
+ data->qc_zoom,
+ data->qc_exposure,
+ data->qc_brightness,
+ data->qc_whitebalance,
+ data->qc_contrast);
+}
+
+static void
+usage(void)
+{
+ fprintf(stderr, "usage: qcamcontrol [-p port] [-x xsize] [-y ysize] "
+ "[-z zoom] [-d depth]\n"
+ " [-b brightness] [-w whitebal] "
+ "[-c contrast] [-e exposure]\n");
+ exit(2);
+}
+
+int
+main(int argc, char **argv)
+{
+ struct qcam info;
+ int fd, len;
+ size_t bytes;
+ char opt;
+
+ static char buffer[QC_MAX_XSIZE*QC_MAX_YSIZE];
+
+ char *port = "/dev/qcam0";
+ int x_size, y_size, zoom, depth, brightness, whitebalance, contrast,
+ exposure;
+
+ /*
+ * Default everything to unset.
+ */
+ x_size = y_size = zoom = depth = brightness = whitebalance =
+ contrast = exposure = -1;
+
+ while ((opt = getopt(argc, argv, "p:x:y:z:d:b:w:c:e:")) != -1) {
+ switch (opt) {
+ case 'p':
+ port = optarg;
+ break;
+
+ case 'x':
+ x_size = atoi(optarg);
+ if (x_size > QC_MAX_XSIZE)
+ errx(2, "x size too large (max %d)", QC_MAX_XSIZE);
+ break;
+
+ case 'y':
+ y_size = atoi(optarg);
+ if (y_size > QC_MAX_YSIZE)
+ errx(2, "x size too large (max %d)", QC_MAX_YSIZE);
+ break;
+
+ case 'z':
+ zoom = atoi(optarg);
+ if (zoom > QC_ZOOM_200)
+ errx(2, "zoom too large (max %d)", QC_ZOOM_200);
+ break;
+
+ case 'd':
+ depth = atoi(optarg);
+ if (depth != 4 && depth != 6)
+ errx(2, "invalid depth (4 or 6)");
+ break;
+
+ case 'b':
+ brightness = atoi(optarg);
+ if (brightness > 255)
+ errx(2, "bad brightness (max 255)");
+ break;
+
+ case 'w':
+ whitebalance = atoi(optarg);
+ if (whitebalance > 255)
+ errx(2, "bad white balance (max 255)");
+ break;
+
+ case 'c':
+ contrast = atoi(optarg);
+ if (contrast > 255)
+ errx(2, "bad contrast (max 255)");
+ break;
+
+ case 'e':
+ exposure = atoi(optarg);
+ if (exposure < 100)
+ errx(2, "bad exposure (min 100)");
+ break;
+
+ default:
+ usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ /* open device */
+ if ((fd = open(port, O_RDONLY)) < 0)
+ err(1, "open");
+
+
+
+ if (ioctl(fd, QC_GET, &info) < 0) /* read in default info */
+ err(1, "ioctl(QC_GET)");
+
+ if (x_size > -1)
+ info.qc_xsize = x_size;
+ if (y_size > -1)
+ info.qc_ysize = y_size;
+ if (depth > -1)
+ info.qc_bpp = depth;
+ if (zoom > -1)
+ info.qc_zoom = zoom;
+ if (brightness > -1)
+ info.qc_brightness = brightness;
+ if (whitebalance > -1)
+ info.qc_whitebalance = whitebalance;
+ if (contrast > -1)
+ info.qc_contrast = contrast;
+ if (exposure > -1)
+ info.qc_exposure = exposure;
+
+ /*
+ * make sure we're in sync with the kernel version of the driver
+ * ioctl structure
+ */
+ info.qc_version = QC_IOCTL_VERSION;
+
+ if (ioctl(fd, QC_SET, &info) < 0)
+ err(1, "ioctl(QC_SET)");
+
+ /*
+ * Tell us what the kernel thinks we're asking for
+ */
+ if (ioctl(fd, QC_GET, &info) < 0)
+ err(1, "ioctl(QC_SET)");
+
+ print_data(&info);
+
+ /*
+ * Grab a frame -- a single read will always work, but give a
+ * particularly paranoid example.
+ */
+ len = info.qc_xsize * info.qc_ysize;
+ while (len) {
+ bytes = read(fd, buffer, len);
+ if (bytes < 0)
+ err(1, "read");
+ len -= bytes;
+
+ if (bytes == 0)
+ exit(0);
+ }
+
+ /*
+ * Write the frame to stdout as a PGM image.
+ */
+ fprintf(stdout, "P5\n%d %d\n%d\n",
+ info.qc_xsize, info.qc_ysize, (1<<info.qc_bpp) - 1);
+ fflush(stdout);
+
+ if (write(1, buffer, info.qc_xsize * info.qc_ysize) < 0)
+ err(1, "write");
+ return(0);
+}
diff --git a/usr.sbin/qcamcontrol/qcamtime/Makefile b/usr.sbin/qcamcontrol/qcamtime/Makefile
new file mode 100644
index 0000000..03c040c
--- /dev/null
+++ b/usr.sbin/qcamcontrol/qcamtime/Makefile
@@ -0,0 +1,8 @@
+#
+# qcamtime is a program for snarfing timing histograms out of the kernel
+# it is only meant for use by driver developers
+#
+PROG= qcamtime
+NOMAN=
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/qcamcontrol/qcamtime/qcamtime.c b/usr.sbin/qcamcontrol/qcamtime/qcamtime.c
new file mode 100644
index 0000000..d8a32f0
--- /dev/null
+++ b/usr.sbin/qcamcontrol/qcamtime/qcamtime.c
@@ -0,0 +1,101 @@
+/*
+ * Print out timing statistics from a QuickCam scan run yes, this is ugly,
+ * it's just for simple analysis of driver timing. This is not normally
+ * part of the system.
+ *
+ * Paul Traina, Feburary 1996
+ */
+
+#include <err.h>
+#include <nlist.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/fcntl.h>
+
+#include <machine/qcam.h>
+
+int kmem = -1;
+
+struct nlist names[] = {
+ {"_qcam_rsbhigh"},
+ {"_qcam_rsblow"}
+};
+#define MAX_SYMBOLS 2
+
+#define FBUFSIZE (QC_MAX_XSIZE*QC_MAX_YSIZE)+50
+static u_short high_times[FBUFSIZE];
+static u_short low_times[FBUFSIZE];
+
+void
+getaddrs(void)
+{
+ int i;
+
+ if (kmem < 0) {
+ if ((kmem = open(_PATH_KMEM, 0, 0)) < 0)
+ err(1, "open kmem");
+ (void) fcntl(kmem, F_SETFD, 1);
+
+ for (i = 0; i < MAX_SYMBOLS; i++) {
+ if (nlist("/kernel", &names[i]) < 0)
+ err(1, "nlist");
+ if (names[i].n_value == 0)
+ errx(1, "couldn't find names[%d]", i);
+ }
+ }
+}
+
+void
+getdata(void)
+{
+ if (lseek(kmem, (off_t) names[0].n_value, SEEK_SET) < 0)
+ err(1, "lseek high");
+ if (read(kmem, (u_short *) high_times, sizeof(high_times)) < 0)
+ err(1, "read high");
+ if (lseek(kmem, (off_t) names[1].n_value, SEEK_SET) < 0)
+ err(1, "lseek low");
+ if (read(kmem, (u_short *) low_times, sizeof(low_times)) < 0)
+ err(1, "read low");
+}
+
+
+/*
+ * slow and stupid, who cares? we're just learning about the camera's
+ * behavior
+ */
+int
+printdata(u_short * p, int length)
+{
+ int i, j, non_zero;
+
+ for (i = 0; i < length;) {
+ non_zero = 0;
+ for (j = 0; j < 16; j++)
+ if (p[j])
+ non_zero++;
+
+ if (non_zero) {
+ printf("%8d:", i);
+
+ for (j = 0; j < 16; j++) {
+ printf(" %d", *p++);
+ i++;
+ }
+ printf("\n");
+ } else
+ i += 16;
+ }
+ return(0);
+}
+
+int
+main(void)
+{
+ getaddrs();
+ getdata();
+ printdata(high_times, FBUFSIZE);
+ printdata(low_times, FBUFSIZE);
+ return(0);
+}
diff --git a/usr.sbin/quot/Makefile b/usr.sbin/quot/Makefile
new file mode 100644
index 0000000..4a25625
--- /dev/null
+++ b/usr.sbin/quot/Makefile
@@ -0,0 +1,6 @@
+# $Id$
+
+PROG= quot
+MAN8= quot.8
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/quot/quot.8 b/usr.sbin/quot/quot.8
new file mode 100644
index 0000000..0e479ee
--- /dev/null
+++ b/usr.sbin/quot/quot.8
@@ -0,0 +1,103 @@
+.\" Copyright (C) 1994 Wolfgang Solfrank.
+.\" Copyright (C) 1994 TooLs GmbH.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by TooLs GmbH.
+.\" 4. The name of TooLs GmbH may not be used to endorse or promote products
+.\" derived from this software without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+.\" IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+.\" SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+.\" PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+.\" OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+.\" WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+.\" OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+.\" ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+.\"
+.\" $Id: quot.8,v 1.7 1997/09/18 06:54:36 charnier Exp $
+.\"
+.Dd February 8, 1994
+.Dt QUOT 8
+.Os BSD 4
+.Sh NAME
+.Nm quot
+.Nd display disk space occupied by each user
+.Sh SYNOPSIS
+.Nm quot
+.Op Fl acfhknv
+.Op Ar filesystem ...
+.Sh DESCRIPTION
+.Nm Quot
+is used to gather statistics about the disk usage for each local user.
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl a
+Include statistics for all mounted filesystems.
+.It Fl c
+Display three columns containing number of blocks per file,
+number of files in this category, and aggregate total of
+blocks in files with this or lower size.
+.It Fl f
+For each user, display count of files and space occupied.
+.It Fl h
+Estimate the number of blocks in each file based on its size.
+Despite that this doesn't give the correct results (it doesn't
+account for the holes in files), this option isn't any faster
+and thus is discouraged.
+.It Fl k
+By default, all sizes are reported in 512-byte block counts.
+The
+.Fl k
+options causes the numbers to be reported in kilobyte counts.
+.It Fl n
+Given a list of inodes (plus some optional data on each line)
+in the standard input, for each file print out the owner (plus
+the remainder of the input line). This is traditionally used
+in the pipe:
+.Bd -literal -offset indent
+ncheck filesystem | sort +0n | quot -n filesystem
+.Ed
+.Pp
+to get a report of files and their owners.
+.It Fl v
+In addition to the default output, display the number of files
+not accessed within 30, 60 and 90 days.
+.El
+.Sh ENVIRONMENT VARIABLES
+.Bl -tag -width BLOCKSIZE
+.It Ev BLOCKSIZE
+If the environment variable
+.Ev BLOCKSIZE
+is set, and the
+.Gl k
+option is not specified, the block counts will be displayed in units of that
+size block.
+.El
+.Sh BUGS
+ncheck does not exist in FreeBSD.. :-)
+.Sh SEE ALSO
+.Xr df 1 ,
+.Xr quota 1 ,
+.Xr getmntinfo 3 ,
+.Xr fstab 5 ,
+.Xr mount 8
+.Sh HISTORY
+This implementation of
+.Nm
+is by
+.An Wolfgang Solfrank
+/ TooLs GmbH.
diff --git a/usr.sbin/quot/quot.c b/usr.sbin/quot/quot.c
new file mode 100644
index 0000000..64fdb28
--- /dev/null
+++ b/usr.sbin/quot/quot.c
@@ -0,0 +1,584 @@
+/*
+ * Copyright (C) 1991, 1994 Wolfgang Solfrank.
+ * Copyright (C) 1991, 1994 TooLs GmbH.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by TooLs GmbH.
+ * 4. The name of TooLs GmbH may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY TOOLS GMBH ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL TOOLS GMBH BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
+ * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
+ * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id: quot.c,v 1.6 1997/08/13 12:09:48 jkh Exp $";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <sys/time.h>
+#include <ufs/ffs/fs.h>
+#include <ufs/ufs/quota.h>
+#include <ufs/ufs/inode.h>
+
+#include <err.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+/* some flags of what to do: */
+static char estimate;
+static char count;
+static char unused;
+static void (*func)();
+static long blocksize;
+static char *header;
+static int headerlen;
+
+/*
+ * Original BSD quot doesn't round to number of frags/blocks,
+ * doesn't account for indirection blocks and gets it totally
+ * wrong if the size is a multiple of the blocksize.
+ * The new code always counts the number of 512 byte blocks
+ * instead of the number of kilobytes and converts them to
+ * kByte when done (on request).
+ */
+#ifdef COMPAT
+#define SIZE(n) (n)
+#else
+#define SIZE(n) (((n) * 512 + blocksize - 1)/blocksize)
+#endif
+
+#define INOCNT(fs) ((fs)->fs_ipg)
+#define INOSZ(fs) (sizeof(struct dinode) * INOCNT(fs))
+
+static struct dinode *
+get_inode(fd,super,ino)
+ struct fs *super;
+ ino_t ino;
+{
+ static struct dinode *ip;
+ static ino_t last;
+
+ if (fd < 0) { /* flush cache */
+ if (ip) {
+ free(ip);
+ ip = 0;
+ }
+ return 0;
+ }
+
+ if (!ip || ino < last || ino >= last + INOCNT(super)) {
+ if (!ip
+ && !(ip = (struct dinode *)malloc(INOSZ(super))))
+ errx(1, "allocate inodes");
+ last = (ino / INOCNT(super)) * INOCNT(super);
+ if (lseek(fd, (off_t)ino_to_fsba(super, last) << super->fs_fshift, 0) < (off_t)0
+ || read(fd,ip,INOSZ(super)) != INOSZ(super))
+ err(1, "read inodes");
+ }
+
+ return ip + ino % INOCNT(super);
+}
+
+#ifdef COMPAT
+#define actualblocks(super,ip) ((ip)->di_blocks/2)
+#else
+#define actualblocks(super,ip) ((ip)->di_blocks)
+#endif
+
+static int virtualblocks(super,ip)
+ struct fs *super;
+ struct dinode *ip;
+{
+ register off_t nblk, sz;
+
+ sz = ip->di_size;
+#ifdef COMPAT
+ if (lblkno(super,sz) >= NDADDR) {
+ nblk = blkroundup(super,sz);
+ if (sz == nblk)
+ nblk += super->fs_bsize;
+ }
+
+ return sz / 1024;
+
+#else /* COMPAT */
+
+ if (lblkno(super,sz) >= NDADDR) {
+ nblk = blkroundup(super,sz);
+ sz = lblkno(super,nblk);
+ sz = (sz - NDADDR + NINDIR(super) - 1) / NINDIR(super);
+ while (sz > 0) {
+ nblk += sz * super->fs_bsize;
+ /* sz - 1 rounded up */
+ sz = (sz - 1 + NINDIR(super) - 1) / NINDIR(super);
+ }
+ } else
+ nblk = fragroundup(super,sz);
+
+ return nblk / 512;
+#endif /* COMPAT */
+}
+
+static int
+isfree(ip)
+ struct dinode *ip;
+{
+#ifdef COMPAT
+ return (ip->di_mode&IFMT) == 0;
+#else /* COMPAT */
+
+ switch (ip->di_mode&IFMT) {
+ case IFIFO:
+ case IFLNK: /* should check FASTSYMLINK? */
+ case IFDIR:
+ case IFREG:
+ return 0;
+ default:
+ return 1;
+ }
+#endif
+}
+
+static struct user {
+ uid_t uid;
+ char *name;
+ daddr_t space;
+ long count;
+ daddr_t spc30;
+ daddr_t spc60;
+ daddr_t spc90;
+} *users;
+static int nusers;
+
+static void
+inituser()
+{
+ register i;
+ register struct user *usr;
+
+ if (!nusers) {
+ nusers = 8;
+ if (!(users =
+ (struct user *)calloc(nusers,sizeof(struct user))))
+ errx(1, "allocate users");
+ } else {
+ for (usr = users, i = nusers; --i >= 0; usr++) {
+ usr->space = usr->spc30 = usr->spc60 = usr->spc90 = 0;
+ usr->count = 0;
+ }
+ }
+}
+
+static void
+usrrehash()
+{
+ register i;
+ register struct user *usr, *usrn;
+ struct user *svusr;
+
+ svusr = users;
+ nusers <<= 1;
+ if (!(users = (struct user *)calloc(nusers,sizeof(struct user))))
+ errx(1, "allocate users");
+ for (usr = svusr, i = nusers >> 1; --i >= 0; usr++) {
+ for (usrn = users + (usr->uid&(nusers - 1)); usrn->name;
+ usrn--) {
+ if (usrn <= users)
+ usrn = users + nusers;
+ }
+ *usrn = *usr;
+ }
+}
+
+static struct user *
+user(uid)
+ uid_t uid;
+{
+ register struct user *usr;
+ register i;
+ struct passwd *pwd;
+
+ while (1) {
+ for (usr = users + (uid&(nusers - 1)), i = nusers; --i >= 0;
+ usr--) {
+ if (!usr->name) {
+ usr->uid = uid;
+
+ if (!(pwd = getpwuid(uid))) {
+ if ((usr->name = (char *)malloc(7)))
+ sprintf(usr->name,"#%d",uid);
+ } else {
+ if ((usr->name = (char *)
+ malloc(strlen(pwd->pw_name) + 1)))
+ strcpy(usr->name,pwd->pw_name);
+ }
+ if (!usr->name)
+ errx(1, "allocate users");
+
+ return usr;
+
+ } else if (usr->uid == uid)
+ return usr;
+
+ if (usr <= users)
+ usr = users + nusers;
+ }
+ usrrehash();
+ }
+}
+
+static int
+cmpusers(u1,u2)
+ struct user *u1, *u2;
+{
+ return u2->space - u1->space;
+}
+
+#define sortusers(users) (qsort((users),nusers,sizeof(struct user), \
+ cmpusers))
+
+static void
+uses(uid,blks,act)
+ uid_t uid;
+ daddr_t blks;
+ time_t act;
+{
+ static time_t today;
+ register struct user *usr;
+
+ if (!today)
+ time(&today);
+
+ usr = user(uid);
+ usr->count++;
+ usr->space += blks;
+
+ if (today - act > 90L * 24L * 60L * 60L)
+ usr->spc90 += blks;
+ if (today - act > 60L * 24L * 60L * 60L)
+ usr->spc60 += blks;
+ if (today - act > 30L * 24L * 60L * 60L)
+ usr->spc30 += blks;
+}
+
+#ifdef COMPAT
+#define FSZCNT 500
+#else
+#define FSZCNT 512
+#endif
+struct fsizes {
+ struct fsizes *fsz_next;
+ daddr_t fsz_first, fsz_last;
+ ino_t fsz_count[FSZCNT];
+ daddr_t fsz_sz[FSZCNT];
+} *fsizes;
+
+static void
+initfsizes()
+{
+ register struct fsizes *fp;
+ register i;
+
+ for (fp = fsizes; fp; fp = fp->fsz_next) {
+ for (i = FSZCNT; --i >= 0;) {
+ fp->fsz_count[i] = 0;
+ fp->fsz_sz[i] = 0;
+ }
+ }
+}
+
+static void
+dofsizes(fd,super,name)
+ struct fs *super;
+ char *name;
+{
+ ino_t inode, maxino;
+ struct dinode *ip;
+ daddr_t sz, ksz;
+ struct fsizes *fp, **fsp;
+ register i;
+
+ maxino = super->fs_ncg * super->fs_ipg - 1;
+#ifdef COMPAT
+ if (!(fsizes = (struct fsizes *)malloc(sizeof(struct fsizes))))
+ errx(1, "alloc fsize structure");
+#endif /* COMPAT */
+ for (inode = 0; inode < maxino; inode++) {
+ errno = 0;
+ if ((ip = get_inode(fd,super,inode))
+#ifdef COMPAT
+ && ((ip->di_mode&IFMT) == IFREG
+ || (ip->di_mode&IFMT) == IFDIR)
+#else /* COMPAT */
+ && !isfree(ip)
+#endif /* COMPAT */
+ ) {
+ sz = estimate ? virtualblocks(super,ip) :
+ actualblocks(super,ip);
+#ifdef COMPAT
+ if (sz >= FSZCNT) {
+ fsizes->fsz_count[FSZCNT-1]++;
+ fsizes->fsz_sz[FSZCNT-1] += sz;
+ } else {
+ fsizes->fsz_count[sz]++;
+ fsizes->fsz_sz[sz] += sz;
+ }
+#else /* COMPAT */
+ ksz = SIZE(sz);
+ for (fsp = &fsizes; (fp = *fsp); fsp = &fp->fsz_next) {
+ if (ksz < fp->fsz_last)
+ break;
+ }
+ if (!fp || ksz < fp->fsz_first) {
+ if (!(fp = (struct fsizes *)
+ malloc(sizeof(struct fsizes))))
+ errx(1, "alloc fsize structure");
+ fp->fsz_next = *fsp;
+ *fsp = fp;
+ fp->fsz_first = (ksz / FSZCNT) * FSZCNT;
+ fp->fsz_last = fp->fsz_first + FSZCNT;
+ for (i = FSZCNT; --i >= 0;) {
+ fp->fsz_count[i] = 0;
+ fp->fsz_sz[i] = 0;
+ }
+ }
+ fp->fsz_count[ksz % FSZCNT]++;
+ fp->fsz_sz[ksz % FSZCNT] += sz;
+#endif /* COMPAT */
+ } else if (errno) {
+ err(1, "%s", name);
+ }
+ }
+ sz = 0;
+ for (fp = fsizes; fp; fp = fp->fsz_next) {
+ for (i = 0; i < FSZCNT; i++) {
+ if (fp->fsz_count[i])
+ printf("%d\t%d\t%d\n",fp->fsz_first + i,
+ fp->fsz_count[i],
+ SIZE(sz += fp->fsz_sz[i]));
+ }
+ }
+}
+
+static void
+douser(fd,super,name)
+ struct fs *super;
+ char *name;
+{
+ ino_t inode, maxino;
+ struct user *usr, *usrs;
+ struct dinode *ip;
+ register n;
+
+ maxino = super->fs_ncg * super->fs_ipg - 1;
+ for (inode = 0; inode < maxino; inode++) {
+ errno = 0;
+ if ((ip = get_inode(fd,super,inode))
+ && !isfree(ip))
+ uses(ip->di_uid,
+ estimate ? virtualblocks(super,ip) :
+ actualblocks(super,ip),
+ ip->di_atime);
+ else if (errno) {
+ err(1, "%s", name);
+ }
+ }
+ if (!(usrs = (struct user *)malloc(nusers * sizeof(struct user))))
+ errx(1, "allocate users");
+ bcopy(users,usrs,nusers * sizeof(struct user));
+ sortusers(usrs);
+ for (usr = usrs, n = nusers; --n >= 0 && usr->count; usr++) {
+ printf("%5d",SIZE(usr->space));
+ if (count)
+ printf("\t%5d",usr->count);
+ printf("\t%-8s",usr->name);
+ if (unused)
+ printf("\t%5d\t%5d\t%5d",
+ SIZE(usr->spc30),
+ SIZE(usr->spc60),
+ SIZE(usr->spc90));
+ printf("\n");
+ }
+ free(usrs);
+}
+
+static void
+donames(fd,super,name)
+ struct fs *super;
+ char *name;
+{
+ int c;
+ ino_t inode, inode1;
+ ino_t maxino;
+ struct dinode *ip;
+
+ maxino = super->fs_ncg * super->fs_ipg - 1;
+ /* first skip the name of the filesystem */
+ while ((c = getchar()) != EOF && (c < '0' || c > '9'))
+ while ((c = getchar()) != EOF && c != '\n');
+ ungetc(c,stdin);
+ inode1 = -1;
+ while (scanf("%d",&inode) == 1) {
+ if (inode < 0 || inode > maxino) {
+ warnx("illegal inode %d",inode);
+ return;
+ }
+ errno = 0;
+ if ((ip = get_inode(fd,super,inode))
+ && !isfree(ip)) {
+ printf("%s\t",user(ip->di_uid)->name);
+ /* now skip whitespace */
+ while ((c = getchar()) == ' ' || c == '\t');
+ /* and print out the remainder of the input line */
+ while (c != EOF && c != '\n') {
+ putchar(c);
+ c = getchar();
+ }
+ putchar('\n');
+ inode1 = inode;
+ } else {
+ if (errno) {
+ err(1, "%s", name);
+ }
+ /* skip this line */
+ while ((c = getchar()) != EOF && c != '\n');
+ }
+ if (c == EOF)
+ break;
+ }
+}
+
+static void
+usage()
+{
+#ifdef COMPAT
+ fprintf(stderr,"usage: quot [-nfcvha] [filesystem ...]\n");
+#else /* COMPAT */
+ fprintf(stderr,"usage: quot [-acfhknv] [ filesystem ... ]\n");
+#endif /* COMPAT */
+ exit(1);
+}
+
+static char superblock[SBSIZE];
+
+void
+quot(name,mp)
+ char *name, *mp;
+{
+ int fd;
+
+ get_inode(-1); /* flush cache */
+ inituser();
+ initfsizes();
+ if ((fd = open(name,0)) < 0
+ || lseek(fd,SBOFF,0) != SBOFF
+ || read(fd,superblock,SBSIZE) != SBSIZE) {
+ warn("%s", name);
+ close(fd);
+ return;
+ }
+ if (((struct fs *)superblock)->fs_magic != FS_MAGIC) {
+ warnx("%s: not a BSD filesystem",name);
+ close(fd);
+ return;
+ }
+ printf("%s:",name);
+ if (mp)
+ printf(" (%s)",mp);
+ putchar('\n');
+ (*func)(fd,superblock,name);
+ close(fd);
+}
+
+int
+main(argc,argv)
+ char **argv;
+{
+ char all = 0;
+ struct statfs *mp;
+ struct vfsconf *vfsp;
+ 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);
+ vfsp = getvfsbyname("ufs");
+ if (vfsp == NULL)
+ errx(1, "cannot find ufs/ffs filesystem type!");
+ for (; --cnt >= 0; mp++) {
+ if (mp->f_type == vfsp->vfc_index) {
+ if ((nm = strrchr(mp->f_mntfromname,'/'))) {
+ sprintf(dev,"/dev/r%s",nm + 1);
+ nm = dev;
+ } else
+ nm = mp->f_mntfromname;
+ quot(nm,mp->f_mntonname);
+ }
+ }
+ }
+ while (--argc >= 0)
+ quot(*argv++,0);
+ return 0;
+}
diff --git a/usr.sbin/quotaon/Makefile b/usr.sbin/quotaon/Makefile
new file mode 100644
index 0000000..be3001e
--- /dev/null
+++ b/usr.sbin/quotaon/Makefile
@@ -0,0 +1,8 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= quotaon
+MAN8= quotaon.8
+MLINKS= quotaon.8 quotaoff.8
+LINKS= ${BINDIR}/quotaon ${BINDIR}/quotaoff
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/quotaon/quotaon.8 b/usr.sbin/quotaon/quotaon.8
new file mode 100644
index 0000000..7094de5
--- /dev/null
+++ b/usr.sbin/quotaon/quotaon.8
@@ -0,0 +1,137 @@
+.\" Copyright (c) 1983, 1990, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" Robert Elz at The University of Melbourne.
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)quotaon.8 8.2 (Berkeley) 12/11/93
+.\"
+.Dd December 11, 1993
+.Dt QUOTAON 8
+.Os BSD 4.2
+.Sh NAME
+.Nm quotaon ,
+.Nm quotaoff
+.Nd turn filesystem quotas on and off
+.Sh SYNOPSIS
+.Nm quotaon
+.Op Fl g
+.Op Fl u
+.Op Fl v
+.Ar filesystem Ar ...
+.Nm quotaon
+.Op Fl g
+.Op Fl u
+.Op Fl v
+.Fl a
+.Nm quotaoff
+.Op Fl g
+.Op Fl u
+.Op Fl v
+.Ar filesystem Ar ...
+.Nm quotaoff
+.Op Fl g
+.Op Fl u
+.Op Fl v
+.Fl a
+.Sh DESCRIPTION
+.Nm Quotaon
+announces to the system that disk quotas should be enabled on one or more
+filesystems.
+.Nm Quotaoff
+announces to the system that the specified
+filesystems should have any disk quotas
+diskquotas turned off.
+The filesystems specified must have entries in
+.Pa /etc/fstab
+and be mounted.
+.Nm Quotaon
+expects each filesystem to have quota files named
+.Pa quota.user
+and
+.Pa quota.group
+which are located at the root of the associated file system.
+These defaults may be overridden in
+.Pa /etc/fstab .
+By default both user and group quotas are enabled.
+.Pp
+Available options:
+.Bl -tag -width indent
+.It Fl a
+If supplied in place of any filesystem names,
+.Nm quotaon Ns / Ns Nm quotaoff
+will enable/disable all the filesystems indicated in
+.Pa /etc/fstab
+to be read-write with disk quotas.
+By default only the types of quotas listed in
+.Pa /etc/fstab
+are enabled.
+.It Fl g
+Only group quotas listed in
+.Pa /etc/fstab
+should be enabled/disabled.
+.It Fl u
+Only user quotas listed in
+.Pa /etc/fstab
+should be enabled/disabled.
+.It Fl v
+Cause
+.Nm quotaon
+and
+.Nm quotaoff
+to print a message for each filesystem where quotas are turned on or off.
+.El
+.Pp
+Specifying both
+.Fl g
+and
+.Fl u
+is equivalent to the default.
+.Sh FILES
+.Bl -tag -width quota.group -compact
+.It Pa quota.user
+at the filesystem root with user quotas
+.It Pa quota.group
+at the filesystem root with group quotas
+.It Pa /etc/fstab
+filesystem table
+.El
+.Sh SEE ALSO
+.Xr quota 1 ,
+.Xr quotactl 2 ,
+.Xr fstab 5 ,
+.Xr edquota 8 ,
+.Xr quotacheck 8 ,
+.Xr repquota 8
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 4.2 .
diff --git a/usr.sbin/quotaon/quotaon.c b/usr.sbin/quotaon/quotaon.c
new file mode 100644
index 0000000..61b8394
--- /dev/null
+++ b/usr.sbin/quotaon/quotaon.c
@@ -0,0 +1,265 @@
+/*
+ * Copyright (c) 1980, 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Robert Elz at The University of Melbourne.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 1980, 1990, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)quotaon.c 8.1 (Berkeley) 6/6/93";
+#endif
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+/*
+ * Turn quota on/off for a filesystem.
+ */
+#include <sys/param.h>
+#include <sys/file.h>
+#include <sys/mount.h>
+#include <ufs/ufs/quota.h>
+#include <err.h>
+#include <fstab.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+char *qfname = QUOTAFILENAME;
+char *qfextension[] = INITQFNAMES;
+
+int aflag; /* all file systems */
+int gflag; /* operate on group quotas */
+int uflag; /* operate on user quotas */
+int vflag; /* verbose */
+
+int hasquota __P((struct fstab *, int, char **));
+int oneof __P((char *, char *[], int));
+int quotaonoff __P((struct fstab *fs, int, int, char *));
+int readonly __P((struct fstab *));
+static void usage __P((void));
+
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ register struct fstab *fs;
+ char ch, *qfnp, *whoami;
+ long argnum, done = 0;
+ int i, offmode = 0, errs = 0;
+
+ whoami = rindex(*argv, '/') + 1;
+ if (whoami == (char *)1)
+ whoami = *argv;
+ if (strcmp(whoami, "quotaoff") == 0)
+ offmode++;
+ else if (strcmp(whoami, "quotaon") != 0)
+ errx(1, "name must be quotaon or quotaoff");
+ while ((ch = getopt(argc, argv, "avug")) != -1) {
+ switch(ch) {
+ case 'a':
+ aflag++;
+ break;
+ case 'g':
+ gflag++;
+ break;
+ case 'u':
+ uflag++;
+ break;
+ case 'v':
+ vflag++;
+ break;
+ default:
+ usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+ if (argc <= 0 && !aflag)
+ usage();
+ if (!gflag && !uflag) {
+ gflag++;
+ uflag++;
+ }
+ setfsent();
+ while ((fs = getfsent()) != NULL) {
+ if (strcmp(fs->fs_vfstype, "ufs") ||
+ strcmp(fs->fs_type, FSTAB_RW))
+ continue;
+ if (aflag) {
+ if (gflag && hasquota(fs, GRPQUOTA, &qfnp))
+ errs += quotaonoff(fs, offmode, GRPQUOTA, qfnp);
+ if (uflag && hasquota(fs, USRQUOTA, &qfnp))
+ errs += quotaonoff(fs, offmode, USRQUOTA, qfnp);
+ continue;
+ }
+ if ((argnum = oneof(fs->fs_file, argv, argc)) >= 0 ||
+ (argnum = oneof(fs->fs_spec, argv, argc)) >= 0) {
+ done |= 1 << argnum;
+ if (gflag && hasquota(fs, GRPQUOTA, &qfnp))
+ errs += quotaonoff(fs, offmode, GRPQUOTA, qfnp);
+ if (uflag && hasquota(fs, USRQUOTA, &qfnp))
+ errs += quotaonoff(fs, offmode, USRQUOTA, qfnp);
+ }
+ }
+ endfsent();
+ for (i = 0; i < argc; i++)
+ if ((done & (1 << i)) == 0)
+ warnx("%s not found in fstab", argv[i]);
+ exit(errs);
+}
+
+static void
+usage()
+{
+
+ fprintf(stderr, "%s\n%s\n%s\n%s\n",
+ "usage: quotaon [-g] [-u] [-v] -a",
+ " quotaon [-g] [-u] [-v] filesystem ...",
+ " quotaoff [-g] [-u] [-v] -a",
+ " quotaoff [-g] [-u] [-v] filesystem ...");
+ exit(1);
+}
+
+int
+quotaonoff(fs, offmode, type, qfpathname)
+ register struct fstab *fs;
+ int offmode, type;
+ char *qfpathname;
+{
+
+ if (strcmp(fs->fs_file, "/") && readonly(fs))
+ return (1);
+ if (offmode) {
+ if (quotactl(fs->fs_file, QCMD(Q_QUOTAOFF, type), 0, 0) < 0) {
+ warn("%s", fs->fs_file);
+ return (1);
+ }
+ if (vflag)
+ printf("%s: quotas turned off\n", fs->fs_file);
+ return (0);
+ }
+ if (quotactl(fs->fs_file, QCMD(Q_QUOTAON, type), 0, qfpathname) < 0) {
+ warnx("using %s on", qfpathname);
+ warn("%s", fs->fs_file);
+ return (1);
+ }
+ if (vflag)
+ printf("%s: %s quotas turned on\n", fs->fs_file,
+ qfextension[type]);
+ return (0);
+}
+
+/*
+ * Check to see if target appears in list of size cnt.
+ */
+int
+oneof(target, list, cnt)
+ register char *target, *list[];
+ int cnt;
+{
+ register int i;
+
+ for (i = 0; i < cnt; i++)
+ if (strcmp(target, list[i]) == 0)
+ return (i);
+ return (-1);
+}
+
+/*
+ * Check to see if a particular quota is to be enabled.
+ */
+int
+hasquota(fs, type, qfnamep)
+ register struct fstab *fs;
+ int type;
+ char **qfnamep;
+{
+ register char *opt;
+ char *cp;
+ static char initname, usrname[100], grpname[100];
+ static char buf[BUFSIZ];
+
+ if (!initname) {
+ sprintf(usrname, "%s%s", qfextension[USRQUOTA], qfname);
+ sprintf(grpname, "%s%s", qfextension[GRPQUOTA], qfname);
+ initname = 1;
+ }
+ strcpy(buf, fs->fs_mntops);
+ for (opt = strtok(buf, ","); opt; opt = strtok(NULL, ",")) {
+ if ((cp = index(opt, '=')))
+ *cp++ = '\0';
+ if (type == USRQUOTA && strcmp(opt, usrname) == 0)
+ break;
+ if (type == GRPQUOTA && strcmp(opt, grpname) == 0)
+ break;
+ }
+ if (!opt)
+ return (0);
+ if (cp) {
+ *qfnamep = cp;
+ return (1);
+ }
+ (void) sprintf(buf, "%s/%s.%s", fs->fs_file, qfname, qfextension[type]);
+ *qfnamep = buf;
+ return (1);
+}
+
+/*
+ * Verify file system is mounted and not readonly.
+ */
+int
+readonly(fs)
+ register struct fstab *fs;
+{
+ struct statfs fsbuf;
+
+ if (statfs(fs->fs_file, &fsbuf) < 0 ||
+ strcmp(fsbuf.f_mntonname, fs->fs_file) ||
+ strcmp(fsbuf.f_mntfromname, fs->fs_spec)) {
+ printf("%s: not mounted\n", fs->fs_file);
+ return (1);
+ }
+ if (fsbuf.f_flags & MNT_RDONLY) {
+ printf("%s: mounted read-only\n", fs->fs_file);
+ return (1);
+ }
+ return (0);
+}
diff --git a/usr.sbin/rarpd/Makefile b/usr.sbin/rarpd/Makefile
new file mode 100644
index 0000000..bc7e7f5
--- /dev/null
+++ b/usr.sbin/rarpd/Makefile
@@ -0,0 +1,10 @@
+# from: @(#)Makefile 5.8 (Berkeley) 7/28/90
+# $Id$
+
+PROG= rarpd
+MAN8= rarpd.8
+SRCS= rarpd.c
+
+CFLAGS+= -DTFTP_DIR=\"/tftpboot\"
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/rarpd/rarpd.8 b/usr.sbin/rarpd/rarpd.8
new file mode 100644
index 0000000..4422521
--- /dev/null
+++ b/usr.sbin/rarpd/rarpd.8
@@ -0,0 +1,117 @@
+.\" @(#) $Id$ (LBL)
+.\"
+.\" Copyright (c) 1990, 1991, 1993 The Regents of the University of
+.\" California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that: (1) source code distributions
+.\" retain the above copyright notice and this paragraph in its entirety, (2)
+.\" distributions including binary code include the above copyright notice and
+.\" this paragraph in its entirety in the documentation or other materials
+.\" provided with the distribution, and (3) all advertising materials mentioning
+.\" features or use of this software display the following acknowledgement:
+.\" ``This product includes software developed by the University of California,
+.\" Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+.\" the University nor the names of its contributors may be used to endorse
+.\" or promote products derived from this software without specific prior
+.\" written permission.
+.\" THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+.\" WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+.\" MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+.\"
+.Dd July 19, 1993
+.Dt RARPD 8
+.Os
+.Sh NAME
+.Nm rarpd
+.Nd reverse ARP daemon
+.Sh SYNOPSIS
+.Nm rarpd
+.Op Fl afsv
+.Op Ar interface
+.Sh DESCRIPTION
+.Nm Rarpd
+services Reverse ARP requests on the Ethernet connected to
+.Ar interface .
+Upon receiving a request,
+.Nm
+maps the target hardware address to an IP address via its name, which
+must be present in both the
+.Xr ethers 5
+and
+.Xr hosts 5
+databases.
+If a host does not exist in both databases, the translation cannot
+proceed and a reply will not be sent.
+
+By default, a request is honored only if the server
+(i.e., the host that
+.Nm
+is running on)
+can "boot" the target; that is, a file or directory matching the glob
+.Pa /tftpboot/\fIipaddr\fP*
+exists, where
+.Em ipaddr
+is the target IP address in hex.
+For example, the IP address 204.216.27.18 will be replied to if any of
+.Pa /tftpboot/CCD81B12 ,
+.Pa /tftpboot/CCD81B12.SUN3 ,
+or
+.Pa /tftpboot/CCD81B12-boot
+exist.
+This requirement can be overridden with the
+.Fl s
+flag (see below).
+
+In normal operation,
+.Nm
+forks a copy of itself and runs in
+the background. Anomalies and errors are reported via
+.Xr syslog 3 .
+
+.Sh OPTIONS
+The following options are available:
+.Bl -tag -width indent
+.It Fl a
+Listen on all the Ethernets attached to the system.
+If
+.Fl a
+is omitted, an interface must be specified.
+.It Fl f
+Run in the foreground.
+.It Fl s
+Supply a response to any RARP request for which an ethernet to IP address
+mapping exists; do not depend on the existence of
+.Pa /tftpboot/\fIipaddr\fP* .
+.It Fl v
+Enable verbose sysloging.
+.El
+.Sh FILES
+.Bl -tag -width /etc/ethers -compact
+.It Pa /etc/ethers
+.It Pa /etc/hosts
+.It Pa /tftpboot
+.El
+.Sh SEE ALSO
+.Xr bpf 4
+.Pp
+RFC 903: Finlayson, R.; Mann, T.; Mogul, J.C.; Theimer, M. Reverse Address
+Resolution Protocol. 1984 June; 4 p.
+.Sh AUTHORS
+.An Craig Leres Aq leres@ee.lbl.gov
+and
+.An Steven McCanne Aq mccanne@ee.lbl.gov .
+Lawrence Berkeley Laboratory, University of California, Berkeley, CA.
+.Sh BUGS
+.Nm Rarpd
+can depend on the DNS to resolve the name discovered from
+.Pa /etc/ethers .
+If this name is not in the DNS but is in
+.Pa /etc/hosts ,
+the DNS lookup
+can cause a delayed RARP response, so in this situation it is reccommended to
+configure
+.Pa /etc/host.conf
+to read
+.Pa /etc/hosts
+first.
diff --git a/usr.sbin/rarpd/rarpd.c b/usr.sbin/rarpd/rarpd.c
new file mode 100644
index 0000000..87f833d
--- /dev/null
+++ b/usr.sbin/rarpd/rarpd.c
@@ -0,0 +1,1010 @@
+/*
+ * Copyright (c) 1990, 1991, 1992, 1993, 1996
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University nor the names of its contributors may be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 1990, 1991, 1992, 1993, 1996\n\
+The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+/*
+ * rarpd - Reverse ARP Daemon
+ *
+ * Usage: rarpd -a [ -fsv ] [ hostname ]
+ * rarpd [ -fsv ] interface [ hostname ]
+ *
+ * 'hostname' is optional solely for backwards compatibility with Sun's rarpd.
+ * Currently, the argument is ignored.
+ */
+#include <sys/param.h>
+#include <sys/file.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+
+#include <net/bpf.h>
+#include <net/if.h>
+#include <net/if_var.h>
+#include <net/if_types.h>
+#include <net/if_dl.h>
+#include <net/route.h>
+
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+
+#include <arpa/inet.h>
+
+#include <errno.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <string.h>
+#include <syslog.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#if defined(SUNOS4) || defined(__FreeBSD__) /* XXX */
+#define HAVE_DIRENT_H
+#endif
+
+#ifdef HAVE_DIRENT_H
+#include <dirent.h>
+#else
+#include <sys/dir.h>
+#endif
+
+/* Cast a struct sockaddr to a structaddr_in */
+#define SATOSIN(sa) ((struct sockaddr_in *)(sa))
+
+#ifndef TFTP_DIR
+#define TFTP_DIR "/tftpboot"
+#endif
+
+#if BSD >= 199200
+#define ARPSECS (20 * 60) /* as per code in netinet/if_ether.c */
+#define REVARP_REQUEST ARPOP_REVREQUEST
+#define REVARP_REPLY ARPOP_REVREPLY
+#endif
+
+#ifndef ETHERTYPE_REVARP
+#define ETHERTYPE_REVARP 0x8035
+#define REVARP_REQUEST 3
+#define REVARP_REPLY 4
+#endif
+
+/*
+ * Map field names in ether_arp struct. What a pain in the neck.
+ */
+#ifdef SUNOS3
+#undef arp_sha
+#undef arp_spa
+#undef arp_tha
+#undef arp_tpa
+#define arp_sha arp_xsha
+#define arp_spa arp_xspa
+#define arp_tha arp_xtha
+#define arp_tpa arp_xtpa
+#endif
+
+#ifndef __GNUC__
+#define inline
+#endif
+
+/*
+ * The structure for each interface.
+ */
+struct if_info {
+ struct if_info *ii_next;
+ int ii_fd; /* BPF file descriptor */
+ u_long ii_ipaddr; /* IP address of this interface */
+ u_long ii_netmask; /* subnet or net mask */
+ u_char ii_eaddr[6]; /* Ethernet address of this interface */
+ char ii_ifname[sizeof(((struct ifreq *)0)->ifr_name) + 1];
+};
+
+/*
+ * The list of all interfaces that are being listened to. rarp_loop()
+ * "selects" on the descriptors in this list.
+ */
+struct if_info *iflist;
+
+int verbose; /* verbose messages */
+int s; /* inet datagram socket */
+char *tftp_dir = TFTP_DIR; /* tftp directory */
+
+#ifndef __P
+#define __P(protos) ()
+#endif
+
+#if BSD < 199200
+extern char *malloc();
+extern void exit();
+#endif
+extern int ether_ntohost();
+
+void init __P((char *));
+void init_one __P((struct ifreq *, char *));
+char *intoa __P((u_long));
+u_long ipaddrtonetmask __P((u_long));
+char *eatoa __P((u_char *));
+int rarp_bootable __P((u_long));
+void rarp_loop __P((void));
+int rarp_open __P((char *));
+void rarp_process __P((struct if_info *, u_char *, u_int));
+void rarp_reply __P((struct if_info *, struct ether_header *, u_long, u_int));
+void update_arptab __P((u_char *, u_long));
+static void usage __P((void));
+
+static u_char zero[6];
+
+int sflag = 0; /* ignore /tftpboot */
+
+void
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ int op;
+ char *ifname, *hostname, *name;
+
+ int aflag = 0; /* listen on "all" interfaces */
+ int fflag = 0; /* don't fork */
+
+ if ((name = strrchr(argv[0], '/')) != NULL)
+ ++name;
+ else
+ name = argv[0];
+ if (*name == '-')
+ ++name;
+
+ /*
+ * All error reporting is done through syslogs.
+ */
+ openlog(name, LOG_PID | LOG_CONS, LOG_DAEMON);
+
+ opterr = 0;
+ while ((op = getopt(argc, argv, "afsv")) != -1) {
+ switch (op) {
+ case 'a':
+ ++aflag;
+ break;
+
+ case 'f':
+ ++fflag;
+ break;
+
+ case 's':
+ ++sflag;
+ break;
+
+ case 'v':
+ ++verbose;
+ break;
+
+ default:
+ usage();
+ /* NOTREACHED */
+ }
+ }
+ ifname = argv[optind++];
+ hostname = ifname ? argv[optind] : NULL;
+ if ((aflag && ifname) || (!aflag && ifname == NULL))
+ usage();
+
+ if (aflag)
+ init(NULL);
+ else
+ init(ifname);
+
+ if (!fflag) {
+ if (daemon(0,0)) {
+ syslog(LOG_ERR, "cannot fork");
+ exit(1);
+ }
+ }
+ rarp_loop();
+}
+
+/*
+ * Add to the interface list.
+ */
+void
+init_one(ifrp, target)
+ register struct ifreq *ifrp;
+ register char *target;
+{
+ register struct if_info *ii;
+ register struct sockaddr_dl *ll;
+ int family;
+ struct ifreq ifr;
+
+ family = ifrp->ifr_addr.sa_family;
+ switch (family) {
+
+ case AF_INET:
+#if BSD >= 199100
+ case AF_LINK:
+#endif
+ (void)strncpy(ifr.ifr_name, ifrp->ifr_name,
+ sizeof(ifrp->ifr_name));
+ if (ioctl(s, SIOCGIFFLAGS, (char *)&ifr) < 0) {
+ syslog(LOG_ERR,
+ "SIOCGIFFLAGS: %.*s: %m",
+ sizeof(ifrp->ifr_name), ifrp->ifr_name);
+ exit(1);
+ }
+ if ((ifr.ifr_flags & IFF_UP) == 0 ||
+ (ifr.ifr_flags & (IFF_LOOPBACK | IFF_POINTOPOINT)) != 0)
+ return;
+ break;
+
+
+ default:
+ return;
+ }
+
+ /* Don't bother going any further if not the target interface */
+ if (target != NULL &&
+ strncmp(ifrp->ifr_name, target, sizeof(ifrp->ifr_name)) != 0)
+ return;
+
+ /* Look for interface in list */
+ for (ii = iflist; ii != NULL; ii = ii->ii_next)
+ if (strncmp(ifrp->ifr_name, ii->ii_ifname,
+ sizeof(ifrp->ifr_name)) == 0)
+ break;
+
+ /* Allocate a new one if not found */
+ if (ii == NULL) {
+ ii = (struct if_info *)malloc(sizeof(*ii));
+ if (ii == NULL) {
+ syslog(LOG_ERR, "malloc: %m");
+ exit(1);
+ }
+ bzero(ii, sizeof(*ii));
+ ii->ii_fd = -1;
+ (void)strncpy(ii->ii_ifname, ifrp->ifr_name,
+ sizeof(ifrp->ifr_name));
+ ii->ii_ifname[sizeof(ii->ii_ifname) - 1] = '\0';
+ ii->ii_next = iflist;
+ iflist = ii;
+ }
+
+ switch (family) {
+
+ case AF_INET:
+ if (ioctl(s, SIOCGIFADDR, (char *)&ifr) < 0) {
+ syslog(LOG_ERR, "ipaddr SIOCGIFADDR: %s: %m",
+ ii->ii_ifname);
+ exit(1);
+ }
+ ii->ii_ipaddr = SATOSIN(&ifr.ifr_addr)->sin_addr.s_addr;
+ if (ioctl(s, SIOCGIFNETMASK, (char *)&ifr) < 0) {
+ syslog(LOG_ERR, "SIOCGIFNETMASK: %m");
+ exit(1);
+ }
+ ii->ii_netmask = SATOSIN(&ifr.ifr_addr)->sin_addr.s_addr;
+ if (ii->ii_netmask == 0)
+ ii->ii_netmask = ipaddrtonetmask(ii->ii_ipaddr);
+ if (ii->ii_fd < 0) {
+ ii->ii_fd = rarp_open(ii->ii_ifname);
+#if BSD < 199100
+ /* Use BPF descriptor to get ethernet address. */
+ if (ioctl(ii->ii_fd, SIOCGIFADDR, (char *)&ifr) < 0) {
+ syslog(LOG_ERR, "eaddr SIOCGIFADDR: %s: %m",
+ ii->ii_ifname);
+ exit(1);
+ }
+ bcopy(&ifr.ifr_addr.sa_data[0], ii->ii_eaddr, 6);
+#endif
+ }
+ break;
+
+#if BSD >= 199100
+ case AF_LINK:
+ ll = (struct sockaddr_dl *)&ifrp->ifr_addr;
+ if (ll->sdl_type == IFT_ETHER)
+ bcopy(LLADDR(ll), ii->ii_eaddr, 6);
+ break;
+#endif
+ }
+}
+/*
+ * Initialize all "candidate" interfaces that are in the system
+ * configuration list. A "candidate" is up, not loopback and not
+ * point to point.
+ */
+void
+init(target)
+ char *target;
+{
+ register int n;
+ register struct ifreq *ifrp, *ifend;
+ register struct if_info *ii, *nii, *lii;
+ struct ifconf ifc;
+ struct ifreq ibuf[16];
+
+ if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+ syslog(LOG_ERR, "socket: %m");
+ exit(1);
+ }
+ ifc.ifc_len = sizeof ibuf;
+ ifc.ifc_buf = (caddr_t)ibuf;
+ if (ioctl(s, SIOCGIFCONF, (char *)&ifc) < 0 ||
+ (u_int)ifc.ifc_len < sizeof(struct ifreq)) {
+ syslog(LOG_ERR, "SIOCGIFCONF: %m");
+ exit(1);
+ }
+ ifrp = ibuf;
+ ifend = (struct ifreq *)((char *)ibuf + ifc.ifc_len);
+ while (ifrp < ifend) {
+ init_one(ifrp, target);
+
+#if BSD >= 199100
+ n = ifrp->ifr_addr.sa_len + sizeof(ifrp->ifr_name);
+ if (n < sizeof(*ifrp))
+ n = sizeof(*ifrp);
+ ifrp = (struct ifreq *)((char *)ifrp + n);
+#else
+ ++ifrp;
+#endif
+ }
+
+ /* Throw away incomplete interfaces */
+ lii = NULL;
+ for (ii = iflist; ii != NULL; ii = nii) {
+ nii = ii->ii_next;
+ if (ii->ii_ipaddr == 0 ||
+ bcmp(ii->ii_eaddr, zero, 6) == 0) {
+ if (lii == NULL)
+ iflist = nii;
+ else
+ lii->ii_next = nii;
+ if (ii->ii_fd >= 0)
+ close(ii->ii_fd);
+ free(ii);
+ continue;
+ }
+ lii = ii;
+ }
+
+ /* Verbose stuff */
+ if (verbose)
+ for (ii = iflist; ii != NULL; ii = ii->ii_next)
+ syslog(LOG_DEBUG, "%s %s 0x%08x %s",
+ ii->ii_ifname, intoa(ntohl(ii->ii_ipaddr)),
+ ntohl(ii->ii_netmask), eatoa(ii->ii_eaddr));
+}
+
+static void
+usage()
+{
+ (void)fprintf(stderr, "usage: rarpd [-afnv] [interface]\n");
+ exit(1);
+}
+
+static int
+bpf_open()
+{
+ int fd;
+ int n = 0;
+ char device[sizeof "/dev/bpf000"];
+
+ /*
+ * Go through all the minors and find one that isn't in use.
+ */
+ do {
+ (void)sprintf(device, "/dev/bpf%d", n++);
+ fd = open(device, O_RDWR);
+ } while (fd < 0 && errno == EBUSY);
+
+ if (fd < 0) {
+ syslog(LOG_ERR, "%s: %m", device);
+ exit(1);
+ }
+ return fd;
+}
+
+/*
+ * Open a BPF file and attach it to the interface named 'device'.
+ * Set immediate mode, and set a filter that accepts only RARP requests.
+ */
+int
+rarp_open(device)
+ char *device;
+{
+ int fd;
+ struct ifreq ifr;
+ u_int dlt;
+ int immediate;
+
+ static struct bpf_insn insns[] = {
+ BPF_STMT(BPF_LD|BPF_H|BPF_ABS, 12),
+ BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, ETHERTYPE_REVARP, 0, 3),
+ BPF_STMT(BPF_LD|BPF_H|BPF_ABS, 20),
+ BPF_JUMP(BPF_JMP|BPF_JEQ|BPF_K, REVARP_REQUEST, 0, 1),
+ BPF_STMT(BPF_RET|BPF_K, sizeof(struct ether_arp) +
+ sizeof(struct ether_header)),
+ BPF_STMT(BPF_RET|BPF_K, 0),
+ };
+ static struct bpf_program filter = {
+ sizeof insns / sizeof(insns[0]),
+ insns
+ };
+
+ fd = bpf_open();
+ /*
+ * Set immediate mode so packets are processed as they arrive.
+ */
+ immediate = 1;
+ if (ioctl(fd, BIOCIMMEDIATE, &immediate) < 0) {
+ syslog(LOG_ERR, "BIOCIMMEDIATE: %m");
+ exit(1);
+ }
+ (void)strncpy(ifr.ifr_name, device, sizeof ifr.ifr_name);
+ if (ioctl(fd, BIOCSETIF, (caddr_t)&ifr) < 0) {
+ syslog(LOG_ERR, "BIOCSETIF: %m");
+ exit(1);
+ }
+ /*
+ * Check that the data link layer is an Ethernet; this code won't
+ * work with anything else.
+ */
+ if (ioctl(fd, BIOCGDLT, (caddr_t)&dlt) < 0) {
+ syslog(LOG_ERR, "BIOCGDLT: %m");
+ exit(1);
+ }
+ if (dlt != DLT_EN10MB) {
+ syslog(LOG_ERR, "%s is not an ethernet", device);
+ exit(1);
+ }
+ /*
+ * Set filter program.
+ */
+ if (ioctl(fd, BIOCSETF, (caddr_t)&filter) < 0) {
+ syslog(LOG_ERR, "BIOCSETF: %m");
+ exit(1);
+ }
+ return fd;
+}
+
+/*
+ * Perform various sanity checks on the RARP request packet. Return
+ * false on failure and log the reason.
+ */
+static int
+rarp_check(p, len)
+ u_char *p;
+ u_int len;
+{
+ struct ether_header *ep = (struct ether_header *)p;
+ struct ether_arp *ap = (struct ether_arp *)(p + sizeof(*ep));
+
+ if (len < sizeof(*ep) + sizeof(*ap)) {
+ syslog(LOG_ERR, "truncated request, got %d, expected %d",
+ len, sizeof(*ep) + sizeof(*ap));
+ return 0;
+ }
+ /*
+ * XXX This test might be better off broken out...
+ */
+ if (ntohs(ep->ether_type) != ETHERTYPE_REVARP ||
+ ntohs(ap->arp_hrd) != ARPHRD_ETHER ||
+ ntohs(ap->arp_op) != REVARP_REQUEST ||
+ ntohs(ap->arp_pro) != ETHERTYPE_IP ||
+ ap->arp_hln != 6 || ap->arp_pln != 4) {
+ syslog(LOG_DEBUG, "request fails sanity check");
+ return 0;
+ }
+ if (bcmp((char *)&ep->ether_shost, (char *)&ap->arp_sha, 6) != 0) {
+ syslog(LOG_DEBUG, "ether/arp sender address mismatch");
+ return 0;
+ }
+ if (bcmp((char *)&ap->arp_sha, (char *)&ap->arp_tha, 6) != 0) {
+ syslog(LOG_DEBUG, "ether/arp target address mismatch");
+ return 0;
+ }
+ return 1;
+}
+
+#ifndef FD_SETSIZE
+#define FD_SET(n, fdp) ((fdp)->fds_bits[0] |= (1 << (n)))
+#define FD_ISSET(n, fdp) ((fdp)->fds_bits[0] & (1 << (n)))
+#define FD_ZERO(fdp) ((fdp)->fds_bits[0] = 0)
+#endif
+
+/*
+ * Loop indefinitely listening for RARP requests on the
+ * interfaces in 'iflist'.
+ */
+void
+rarp_loop()
+{
+ u_char *buf, *bp, *ep;
+ int cc, fd;
+ fd_set fds, listeners;
+ int bufsize, maxfd = 0;
+ struct if_info *ii;
+
+ if (iflist == NULL) {
+ syslog(LOG_ERR, "no interfaces");
+ exit(1);
+ }
+ if (ioctl(iflist->ii_fd, BIOCGBLEN, (caddr_t)&bufsize) < 0) {
+ syslog(LOG_ERR, "BIOCGBLEN: %m");
+ exit(1);
+ }
+ buf = (u_char *)malloc((unsigned)bufsize);
+ if (buf == NULL) {
+ syslog(LOG_ERR, "malloc: %m");
+ exit(1);
+ }
+
+ while (1) {
+ /*
+ * Find the highest numbered file descriptor for select().
+ * Initialize the set of descriptors to listen to.
+ */
+ FD_ZERO(&fds);
+ for (ii = iflist; ii != NULL; ii = ii->ii_next) {
+ FD_SET(ii->ii_fd, &fds);
+ if (ii->ii_fd > maxfd)
+ maxfd = ii->ii_fd;
+ }
+ listeners = fds;
+ if (select(maxfd + 1, &listeners, NULL, NULL, NULL) < 0) {
+ /* Don't choke when we get ptraced */
+ if (errno == EINTR)
+ continue;
+ syslog(LOG_ERR, "select: %m");
+ exit(1);
+ }
+ for (ii = iflist; ii != NULL; ii = ii->ii_next) {
+ fd = ii->ii_fd;
+ if (!FD_ISSET(fd, &listeners))
+ continue;
+ again:
+ cc = read(fd, (char *)buf, bufsize);
+ /* Don't choke when we get ptraced */
+ if (cc < 0 && errno == EINTR)
+ goto again;
+#if defined(SUNOS3) || defined(SUNOS4)
+ /*
+ * Due to a SunOS bug, after 2^31 bytes, the
+ * file offset overflows and read fails with
+ * EINVAL. The lseek() to 0 will fix things.
+ */
+ if (cc < 0) {
+ if (errno == EINVAL &&
+ (long)(tell(fd) + bufsize) < 0) {
+ (void)lseek(fd, 0, 0);
+ goto again;
+ }
+ syslog(LOG_ERR, "read: %m");
+ exit(1);
+ }
+#endif
+
+ /* Loop through the packet(s) */
+#define bhp ((struct bpf_hdr *)bp)
+ bp = buf;
+ ep = bp + cc;
+ while (bp < ep) {
+ register u_int caplen, hdrlen;
+
+ caplen = bhp->bh_caplen;
+ hdrlen = bhp->bh_hdrlen;
+ if (rarp_check(bp + hdrlen, caplen))
+ rarp_process(ii, bp + hdrlen, caplen);
+ bp += BPF_WORDALIGN(hdrlen + caplen);
+ }
+ }
+ }
+#undef bhp
+}
+
+/*
+ * True if this server can boot the host whose IP address is 'addr'.
+ * This check is made by looking in the tftp directory for the
+ * configuration file.
+ */
+int
+rarp_bootable(addr)
+ u_long addr;
+{
+#ifdef HAVE_DIRENT_H
+ register struct dirent *dent;
+#else
+ register struct direct *dent;
+#endif
+ register DIR *d;
+ char ipname[9];
+ static DIR *dd = NULL;
+
+ (void)sprintf(ipname, "%08X", (unsigned int )ntohl(addr));
+
+ /*
+ * If directory is already open, rewind it. Otherwise, open it.
+ */
+ if ((d = dd) != NULL)
+ rewinddir(d);
+ else {
+ if (chdir(tftp_dir) == -1) {
+ syslog(LOG_ERR, "chdir: %s: %m", tftp_dir);
+ exit(1);
+ }
+ d = opendir(".");
+ if (d == NULL) {
+ syslog(LOG_ERR, "opendir: %m");
+ exit(1);
+ }
+ dd = d;
+ }
+ while ((dent = readdir(d)) != NULL)
+ if (strncmp(dent->d_name, ipname, 8) == 0)
+ return 1;
+ return 0;
+}
+
+/*
+ * Given a list of IP addresses, 'alist', return the first address that
+ * is on network 'net'; 'netmask' is a mask indicating the network portion
+ * of the address.
+ */
+u_long
+choose_ipaddr(alist, net, netmask)
+ u_long **alist;
+ u_long net;
+ u_long netmask;
+{
+ for (; *alist; ++alist)
+ if ((**alist & netmask) == net)
+ return **alist;
+ return 0;
+}
+
+/*
+ * Answer the RARP request in 'pkt', on the interface 'ii'. 'pkt' has
+ * already been checked for validity. The reply is overlaid on the request.
+ */
+void
+rarp_process(ii, pkt, len)
+ struct if_info *ii;
+ u_char *pkt;
+ u_int len;
+{
+ struct ether_header *ep;
+ struct hostent *hp;
+ u_long target_ipaddr;
+ char ename[256];
+
+ ep = (struct ether_header *)pkt;
+ /* should this be arp_tha? */
+ if (ether_ntohost(ename, &ep->ether_shost) != 0) {
+ syslog(LOG_ERR, "cannot map %s to name",
+ eatoa(ep->ether_shost));
+ return;
+ }
+
+ if ((hp = gethostbyname(ename)) == NULL) {
+ syslog(LOG_ERR, "cannot map %s to IP address", ename);
+ return;
+ }
+
+ /*
+ * Choose correct address from list.
+ */
+ if (hp->h_addrtype != AF_INET) {
+ syslog(LOG_ERR, "cannot handle non IP addresses for %s",
+ ename);
+ return;
+ }
+ target_ipaddr = choose_ipaddr((u_long **)hp->h_addr_list,
+ ii->ii_ipaddr & ii->ii_netmask,
+ ii->ii_netmask);
+ if (target_ipaddr == 0) {
+ syslog(LOG_ERR, "cannot find %s on net %s",
+ ename, intoa(ntohl(ii->ii_ipaddr & ii->ii_netmask)));
+ return;
+ }
+ if (sflag || rarp_bootable(target_ipaddr))
+ rarp_reply(ii, ep, target_ipaddr, len);
+ else if (verbose > 1)
+ syslog(LOG_INFO, "%s %s at %s DENIED (not bootable)",
+ ii->ii_ifname,
+ eatoa(ep->ether_shost),
+ intoa(ntohl(target_ipaddr)));
+}
+
+/*
+ * Poke the kernel arp tables with the ethernet/ip address combinataion
+ * given. When processing a reply, we must do this so that the booting
+ * host (i.e. the guy running rarpd), won't try to ARP for the hardware
+ * address of the guy being booted (he cannot answer the ARP).
+ */
+#if BSD >= 199200
+static struct sockaddr_inarp sin_inarp = {
+ sizeof(struct sockaddr_inarp), AF_INET
+};
+static struct sockaddr_dl sin_dl = {
+ sizeof(struct sockaddr_dl), AF_LINK, 0, IFT_ETHER, 0, 6
+};
+static struct {
+ struct rt_msghdr rthdr;
+ char rtspace[512];
+} rtmsg;
+
+void
+update_arptab(ep, ipaddr)
+ u_char *ep;
+ u_long ipaddr;
+{
+ register int cc;
+ register struct sockaddr_inarp *ar, *ar2;
+ register struct sockaddr_dl *ll, *ll2;
+ register struct rt_msghdr *rt;
+ register int xtype, xindex;
+ static pid_t pid;
+ static int r, seq;
+ static init = 0;
+
+ if (!init) {
+ r = socket(PF_ROUTE, SOCK_RAW, 0);
+ if (r < 0) {
+ syslog(LOG_ERR, "raw route socket: %m");
+ exit(1);
+ }
+ pid = getpid();
+ ++init;
+ }
+
+ ar = &sin_inarp;
+ ar->sin_addr.s_addr = ipaddr;
+ ll = &sin_dl;
+ bcopy(ep, LLADDR(ll), 6);
+
+ /* Get the type and interface index */
+ rt = &rtmsg.rthdr;
+ bzero(rt, sizeof(rtmsg));
+ rt->rtm_version = RTM_VERSION;
+ rt->rtm_addrs = RTA_DST;
+ rt->rtm_type = RTM_GET;
+ rt->rtm_seq = ++seq;
+ ar2 = (struct sockaddr_inarp *)rtmsg.rtspace;
+ bcopy(ar, ar2, sizeof(*ar));
+ rt->rtm_msglen = sizeof(*rt) + sizeof(*ar);
+ errno = 0;
+ if (write(r, rt, rt->rtm_msglen) < 0 && errno != ESRCH) {
+ syslog(LOG_ERR, "rtmsg get write: %m");
+ return;
+ }
+ do {
+ cc = read(r, rt, sizeof(rtmsg));
+ } while (cc > 0 && (rt->rtm_seq != seq || rt->rtm_pid != pid));
+ if (cc < 0) {
+ syslog(LOG_ERR, "rtmsg get read: %m");
+ return;
+ }
+ ll2 = (struct sockaddr_dl *)((u_char *)ar2 + ar2->sin_len);
+ if (ll2->sdl_family != AF_LINK) {
+ /*
+ * XXX I think this means the ip address is not on a
+ * directly connected network (the family is AF_INET in
+ * this case).
+ */
+ syslog(LOG_ERR, "bogus link family (%d) wrong net for %08X?\n",
+ ll2->sdl_family, ipaddr);
+ return;
+ }
+ xtype = ll2->sdl_type;
+ xindex = ll2->sdl_index;
+
+ /* Set the new arp entry */
+ bzero(rt, sizeof(rtmsg));
+ rt->rtm_version = RTM_VERSION;
+ rt->rtm_addrs = RTA_DST | RTA_GATEWAY;
+ rt->rtm_inits = RTV_EXPIRE;
+ rt->rtm_rmx.rmx_expire = time(0) + ARPSECS;
+ rt->rtm_flags = RTF_HOST | RTF_STATIC;
+ rt->rtm_type = RTM_ADD;
+ rt->rtm_seq = ++seq;
+
+ bcopy(ar, ar2, sizeof(*ar));
+
+ ll2 = (struct sockaddr_dl *)((u_char *)ar2 + sizeof(*ar2));
+ bcopy(ll, ll2, sizeof(*ll));
+ ll2->sdl_type = xtype;
+ ll2->sdl_index = xindex;
+
+ rt->rtm_msglen = sizeof(*rt) + sizeof(*ar2) + sizeof(*ll2);
+ errno = 0;
+ if (write(r, rt, rt->rtm_msglen) < 0 && errno != EEXIST) {
+ syslog(LOG_ERR, "rtmsg add write: %m");
+ return;
+ }
+ do {
+ cc = read(r, rt, sizeof(rtmsg));
+ } while (cc > 0 && (rt->rtm_seq != seq || rt->rtm_pid != pid));
+ if (cc < 0) {
+ syslog(LOG_ERR, "rtmsg add read: %m");
+ return;
+ }
+}
+#else
+void
+update_arptab(ep, ipaddr)
+ u_char *ep;
+ u_long ipaddr;
+{
+ struct arpreq request;
+ struct sockaddr_in *sin;
+
+ request.arp_flags = 0;
+ sin = (struct sockaddr_in *)&request.arp_pa;
+ sin->sin_family = AF_INET;
+ sin->sin_addr.s_addr = ipaddr;
+ request.arp_ha.sa_family = AF_UNSPEC;
+ bcopy((char *)ep, (char *)request.arp_ha.sa_data, 6);
+
+ if (ioctl(s, SIOCSARP, (caddr_t)&request) < 0)
+ syslog(LOG_ERR, "SIOCSARP: %m");
+}
+#endif
+
+/*
+ * Build a reverse ARP packet and sent it out on the interface.
+ * 'ep' points to a valid REVARP_REQUEST. The REVARP_REPLY is built
+ * on top of the request, then written to the network.
+ *
+ * RFC 903 defines the ether_arp fields as follows. The following comments
+ * are taken (more or less) straight from this document.
+ *
+ * REVARP_REQUEST
+ *
+ * arp_sha is the hardware address of the sender of the packet.
+ * arp_spa is undefined.
+ * arp_tha is the 'target' hardware address.
+ * In the case where the sender wishes to determine his own
+ * protocol address, this, like arp_sha, will be the hardware
+ * address of the sender.
+ * arp_tpa is undefined.
+ *
+ * REVARP_REPLY
+ *
+ * arp_sha is the hardware address of the responder (the sender of the
+ * reply packet).
+ * arp_spa is the protocol address of the responder (see the note below).
+ * arp_tha is the hardware address of the target, and should be the same as
+ * that which was given in the request.
+ * arp_tpa is the protocol address of the target, that is, the desired address.
+ *
+ * Note that the requirement that arp_spa be filled in with the responder's
+ * protocol is purely for convenience. For instance, if a system were to use
+ * both ARP and RARP, then the inclusion of the valid protocol-hardware
+ * address pair (arp_spa, arp_sha) may eliminate the need for a subsequent
+ * ARP request.
+ */
+void
+rarp_reply(ii, ep, ipaddr, len)
+ struct if_info *ii;
+ struct ether_header *ep;
+ u_long ipaddr;
+ u_int len;
+{
+ int n;
+ struct ether_arp *ap = (struct ether_arp *)(ep + 1);
+
+ update_arptab((u_char *)&ap->arp_sha, ipaddr);
+
+ /*
+ * Build the rarp reply by modifying the rarp request in place.
+ */
+ ap->arp_op = htons(REVARP_REPLY);
+
+#ifdef BROKEN_BPF
+ ep->ether_type = ETHERTYPE_REVARP;
+#endif
+ bcopy((char *)&ap->arp_sha, (char *)&ep->ether_dhost, 6);
+ bcopy((char *)ii->ii_eaddr, (char *)&ep->ether_shost, 6);
+ bcopy((char *)ii->ii_eaddr, (char *)&ap->arp_sha, 6);
+
+ bcopy((char *)&ipaddr, (char *)ap->arp_tpa, 4);
+ /* Target hardware is unchanged. */
+ bcopy((char *)&ii->ii_ipaddr, (char *)ap->arp_spa, 4);
+
+ /* Zero possible garbage after packet. */
+ bzero((char *)ep + (sizeof(*ep) + sizeof(*ap)),
+ len - (sizeof(*ep) + sizeof(*ap)));
+ n = write(ii->ii_fd, (char *)ep, len);
+ if (n != len)
+ syslog(LOG_ERR, "write: only %d of %d bytes written", n, len);
+ if (verbose)
+ syslog(LOG_INFO, "%s %s at %s REPLIED", ii->ii_ifname,
+ eatoa(ap->arp_tha),
+ intoa(ntohl(ipaddr)));
+}
+
+/*
+ * Get the netmask of an IP address. This routine is used if
+ * SIOCGIFNETMASK doesn't work.
+ */
+u_long
+ipaddrtonetmask(addr)
+ u_long addr;
+{
+ addr = ntohl(addr);
+ if (IN_CLASSA(addr))
+ return htonl(IN_CLASSA_NET);
+ if (IN_CLASSB(addr))
+ return htonl(IN_CLASSB_NET);
+ if (IN_CLASSC(addr))
+ return htonl(IN_CLASSC_NET);
+ syslog(LOG_DEBUG, "unknown IP address class: %08X", addr);
+ return htonl(0xffffffff);
+}
+
+/*
+ * A faster replacement for inet_ntoa().
+ */
+char *
+intoa(addr)
+ u_long addr;
+{
+ register char *cp;
+ register u_int byte;
+ register int n;
+ static char buf[sizeof(".xxx.xxx.xxx.xxx")];
+
+ cp = &buf[sizeof buf];
+ *--cp = '\0';
+
+ n = 4;
+ do {
+ byte = addr & 0xff;
+ *--cp = byte % 10 + '0';
+ byte /= 10;
+ if (byte > 0) {
+ *--cp = byte % 10 + '0';
+ byte /= 10;
+ if (byte > 0)
+ *--cp = byte + '0';
+ }
+ *--cp = '.';
+ addr >>= 8;
+ } while (--n > 0);
+
+ return cp + 1;
+}
+
+char *
+eatoa(ea)
+ register u_char *ea;
+{
+ static char buf[sizeof("xx:xx:xx:xx:xx:xx")];
+
+ (void)sprintf(buf, "%x:%x:%x:%x:%x:%x",
+ ea[0], ea[1], ea[2], ea[3], ea[4], ea[5]);
+ return (buf);
+}
diff --git a/usr.sbin/repquota/Makefile b/usr.sbin/repquota/Makefile
new file mode 100644
index 0000000..eea7351
--- /dev/null
+++ b/usr.sbin/repquota/Makefile
@@ -0,0 +1,6 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= repquota
+MAN8= repquota.8
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/repquota/repquota.8 b/usr.sbin/repquota/repquota.8
new file mode 100644
index 0000000..220f652
--- /dev/null
+++ b/usr.sbin/repquota/repquota.8
@@ -0,0 +1,103 @@
+.\" Copyright (c) 1983, 1990, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" Robert Elz at The University of Melbourne.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)repquota.8 8.1 (Berkeley) 6/6/93
+.\"
+.Dd June 6, 1993
+.Dt REPQUOTA 8
+.Os BSD 4.2
+.Sh NAME
+.Nm repquota
+.Nd summarize quotas for a file system
+.Sh SYNOPSIS
+.Nm repquota
+.Op Fl g
+.Op Fl u
+.Op Fl v
+.Ar filesystem Ar ...
+.Nm repquota
+.Op Fl g
+.Op Fl u
+.Op Fl v
+.Fl a
+.Sh DESCRIPTION
+.Nm Repquota
+prints a summary of the disk usage and quotas for the
+specified file systems.
+.Pp
+Available options:
+.Bl -tag -width indent
+.It Fl a
+Print the quotas of all the filesystems listed in
+.Pa /etc/fstab .
+.It Fl g
+Print only group quotas (the default is to print both
+group and user quotas if they exist).
+.It Fl u
+Print only user quotas (the default is to print both
+group and user quotas if they exist).
+.It Fl v
+Print a header line before printing each filesystem quotas.
+.El
+.Pp
+For each user or group, the current
+number files and amount of space (in kilobytes) is
+printed, along with any quotas created with
+.Xr edquota 8 .
+.Pp
+Only members of the operator group or the super-user may
+use this command.
+.Sh FILES
+.Bl -tag -width quota.group -compact
+.It Pa quota.user
+at the filesystem root with user quotas
+.It Pa quota.group
+at the filesystem root with group quotas
+.It Pa /etc/fstab
+for file system names and locations
+.El
+.Sh SEE ALSO
+.Xr quota 1 ,
+.Xr quotactl 2 ,
+.Xr fstab 5 ,
+.Xr edquota 8 ,
+.Xr quotacheck 8 ,
+.Xr quotaon 8
+.Sh DIAGNOSTICS
+Various messages about inaccessible files; self-explanatory.
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 4.2 .
diff --git a/usr.sbin/repquota/repquota.c b/usr.sbin/repquota/repquota.c
new file mode 100644
index 0000000..1e6578e
--- /dev/null
+++ b/usr.sbin/repquota/repquota.c
@@ -0,0 +1,391 @@
+/*
+ * Copyright (c) 1980, 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Robert Elz at The University of Melbourne.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 1980, 1990, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)repquota.c 8.1 (Berkeley) 6/6/93";
+#endif
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+/*
+ * Quota report
+ */
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <ufs/ufs/quota.h>
+#include <err.h>
+#include <errno.h>
+#include <fstab.h>
+#include <grp.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+char *qfname = QUOTAFILENAME;
+char *qfextension[] = INITQFNAMES;
+
+struct fileusage {
+ struct fileusage *fu_next;
+ struct dqblk fu_dqblk;
+ u_long fu_id;
+ char fu_name[1];
+ /* actually bigger */
+};
+#define FUHASH 1024 /* must be power of two */
+struct fileusage *fuhead[MAXQUOTAS][FUHASH];
+struct fileusage *lookup();
+struct fileusage *addid();
+u_long highid[MAXQUOTAS]; /* highest addid()'ed identifier per type */
+
+int vflag; /* verbose */
+int aflag; /* all file systems */
+
+int hasquota __P((struct fstab *, int, char **));
+int oneof __P((char *, char *[], int));
+int repquota __P((struct fstab *, int, char *));
+char *timeprt __P((time_t));
+static void usage __P((void));
+
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ register struct fstab *fs;
+ register struct passwd *pw;
+ register struct group *gr;
+ int gflag = 0, uflag = 0, errs = 0;
+ long i, argnum, done = 0;
+ char ch, *qfnp;
+
+ while ((ch = getopt(argc, argv, "aguv")) != -1) {
+ switch(ch) {
+ case 'a':
+ aflag++;
+ break;
+ case 'g':
+ gflag++;
+ break;
+ case 'u':
+ uflag++;
+ break;
+ case 'v':
+ vflag++;
+ break;
+ default:
+ usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+ if (argc == 0 && !aflag)
+ usage();
+ if (!gflag && !uflag) {
+ if (aflag)
+ gflag++;
+ uflag++;
+ }
+ if (gflag) {
+ setgrent();
+ while ((gr = getgrent()) != 0)
+ (void) addid((u_long)gr->gr_gid, GRPQUOTA, gr->gr_name);
+ endgrent();
+ }
+ if (uflag) {
+ setpwent();
+ while ((pw = getpwent()) != 0)
+ (void) addid((u_long)pw->pw_uid, USRQUOTA, pw->pw_name);
+ endpwent();
+ }
+ setfsent();
+ while ((fs = getfsent()) != NULL) {
+ if (strcmp(fs->fs_vfstype, "ufs"))
+ continue;
+ if (aflag) {
+ if (gflag && hasquota(fs, GRPQUOTA, &qfnp))
+ errs += repquota(fs, GRPQUOTA, qfnp);
+ if (uflag && hasquota(fs, USRQUOTA, &qfnp))
+ errs += repquota(fs, USRQUOTA, qfnp);
+ continue;
+ }
+ if ((argnum = oneof(fs->fs_file, argv, argc)) >= 0 ||
+ (argnum = oneof(fs->fs_spec, argv, argc)) >= 0) {
+ done |= 1 << argnum;
+ if (gflag && hasquota(fs, GRPQUOTA, &qfnp))
+ errs += repquota(fs, GRPQUOTA, qfnp);
+ if (uflag && hasquota(fs, USRQUOTA, &qfnp))
+ errs += repquota(fs, USRQUOTA, qfnp);
+ }
+ }
+ endfsent();
+ for (i = 0; i < argc; i++)
+ if ((done & (1 << i)) == 0)
+ warnx("%s not found in fstab", argv[i]);
+ exit(errs);
+}
+
+static void
+usage()
+{
+ fprintf(stderr, "%s\n%s\n",
+ "usage: repquota [-v] [-g] [-u] -a",
+ " repquota [-v] [-g] [-u] filesystem ...");
+ exit(1);
+}
+
+int
+repquota(fs, type, qfpathname)
+ register struct fstab *fs;
+ int type;
+ char *qfpathname;
+{
+ register struct fileusage *fup;
+ FILE *qf;
+ u_long id;
+ struct dqblk dqbuf;
+ static struct dqblk zerodqblk;
+ static int warned = 0;
+ static int multiple = 0;
+
+ if (quotactl(fs->fs_file, QCMD(Q_SYNC, type), 0, 0) < 0 &&
+ errno == EOPNOTSUPP && !warned && vflag) {
+ warned++;
+ fprintf(stdout,
+ "*** Warning: Quotas are not compiled into this kernel\n");
+ }
+ if (multiple++)
+ printf("\n");
+ if (vflag)
+ fprintf(stdout, "*** Report for %s quotas on %s (%s)\n",
+ qfextension[type], fs->fs_file, fs->fs_spec);
+ if ((qf = fopen(qfpathname, "r")) == NULL) {
+ warn("%s", qfpathname);
+ return (1);
+ }
+ for (id = 0; ; id++) {
+ fread(&dqbuf, sizeof(struct dqblk), 1, qf);
+ if (feof(qf))
+ break;
+ if (dqbuf.dqb_curinodes == 0 && dqbuf.dqb_curblocks == 0)
+ continue;
+ if ((fup = lookup(id, type)) == 0)
+ fup = addid(id, type, (char *)0);
+ fup->fu_dqblk = dqbuf;
+ }
+ fclose(qf);
+ printf(" Block limits File limits\n");
+ printf("User used soft hard grace used soft hard grace\n");
+ 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("%-10s", fup->fu_name);
+ printf("%c%c%8lu%8lu%8lu%7s",
+ fup->fu_dqblk.dqb_bsoftlimit &&
+ fup->fu_dqblk.dqb_curblocks >=
+ fup->fu_dqblk.dqb_bsoftlimit ? '+' : '-',
+ fup->fu_dqblk.dqb_isoftlimit &&
+ fup->fu_dqblk.dqb_curinodes >=
+ fup->fu_dqblk.dqb_isoftlimit ? '+' : '-',
+ (u_long)(dbtob(fup->fu_dqblk.dqb_curblocks) / 1024),
+ (u_long)(dbtob(fup->fu_dqblk.dqb_bsoftlimit) / 1024),
+ (u_long)(dbtob(fup->fu_dqblk.dqb_bhardlimit) / 1024),
+ fup->fu_dqblk.dqb_bsoftlimit &&
+ fup->fu_dqblk.dqb_curblocks >=
+ fup->fu_dqblk.dqb_bsoftlimit ?
+ timeprt(fup->fu_dqblk.dqb_btime) : "");
+ printf(" %6lu%6lu%6lu%7s\n",
+ fup->fu_dqblk.dqb_curinodes,
+ fup->fu_dqblk.dqb_isoftlimit,
+ fup->fu_dqblk.dqb_ihardlimit,
+ fup->fu_dqblk.dqb_isoftlimit &&
+ fup->fu_dqblk.dqb_curinodes >=
+ fup->fu_dqblk.dqb_isoftlimit ?
+ timeprt(fup->fu_dqblk.dqb_itime) : "");
+ fup->fu_dqblk = zerodqblk;
+ }
+ return (0);
+}
+
+/*
+ * Check to see if target appears in list of size cnt.
+ */
+int
+oneof(target, list, cnt)
+ register char *target, *list[];
+ int cnt;
+{
+ register int i;
+
+ for (i = 0; i < cnt; i++)
+ if (strcmp(target, list[i]) == 0)
+ return (i);
+ return (-1);
+}
+
+/*
+ * Check to see if a particular quota is to be enabled.
+ */
+int
+hasquota(fs, type, qfnamep)
+ register struct fstab *fs;
+ int type;
+ char **qfnamep;
+{
+ register char *opt;
+ char *cp;
+ static char initname, usrname[100], grpname[100];
+ static char buf[BUFSIZ];
+
+ if (!initname) {
+ sprintf(usrname, "%s%s", qfextension[USRQUOTA], qfname);
+ sprintf(grpname, "%s%s", qfextension[GRPQUOTA], qfname);
+ initname = 1;
+ }
+ strcpy(buf, fs->fs_mntops);
+ for (opt = strtok(buf, ","); opt; opt = strtok(NULL, ",")) {
+ if ((cp = index(opt, '=')))
+ *cp++ = '\0';
+ if (type == USRQUOTA && strcmp(opt, usrname) == 0)
+ break;
+ if (type == GRPQUOTA && strcmp(opt, grpname) == 0)
+ break;
+ }
+ if (!opt)
+ return (0);
+ if (cp) {
+ *qfnamep = cp;
+ return (1);
+ }
+ (void) sprintf(buf, "%s/%s.%s", fs->fs_file, qfname, qfextension[type]);
+ *qfnamep = buf;
+ return (1);
+}
+
+/*
+ * Routines to manage the file usage table.
+ *
+ * Lookup an id of a specific type.
+ */
+struct fileusage *
+lookup(id, type)
+ u_long id;
+ int type;
+{
+ register struct fileusage *fup;
+
+ for (fup = fuhead[type][id & (FUHASH-1)]; fup != 0; fup = fup->fu_next)
+ if (fup->fu_id == id)
+ return (fup);
+ return ((struct fileusage *)0);
+}
+
+/*
+ * Add a new file usage id if it does not already exist.
+ */
+struct fileusage *
+addid(id, type, name)
+ u_long id;
+ int type;
+ char *name;
+{
+ struct fileusage *fup, **fhp;
+ int len;
+
+ if ((fup = lookup(id, type)))
+ return (fup);
+ if (name)
+ len = strlen(name);
+ else
+ len = 10;
+ if ((fup = (struct fileusage *)calloc(1, sizeof(*fup) + len)) == NULL)
+ errx(1, "out of memory for fileusage structures");
+ fhp = &fuhead[type][id & (FUHASH - 1)];
+ fup->fu_next = *fhp;
+ *fhp = fup;
+ fup->fu_id = id;
+ if (id > highid[type])
+ highid[type] = id;
+ if (name) {
+ bcopy(name, fup->fu_name, len + 1);
+ } else {
+ sprintf(fup->fu_name, "%lu", id);
+ }
+ return (fup);
+}
+
+/*
+ * Calculate the grace period and return a printable string for it.
+ */
+char *
+timeprt(seconds)
+ time_t seconds;
+{
+ time_t hours, minutes;
+ static char buf[20];
+ static time_t now;
+
+ if (now == 0)
+ time(&now);
+ if (now > seconds)
+ return ("none");
+ seconds -= now;
+ minutes = (seconds + 30) / 60;
+ hours = (minutes + 30) / 60;
+ if (hours >= 36) {
+ sprintf(buf, "%lddays", (hours + 12) / 24);
+ return (buf);
+ }
+ if (minutes >= 60) {
+ sprintf(buf, "%2ld:%ld", minutes / 60, minutes % 60);
+ return (buf);
+ }
+ sprintf(buf, "%2ld", minutes);
+ return (buf);
+}
diff --git a/usr.sbin/rmt/Makefile b/usr.sbin/rmt/Makefile
new file mode 100644
index 0000000..166d458
--- /dev/null
+++ b/usr.sbin/rmt/Makefile
@@ -0,0 +1,11 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= rmt
+MAN8= rmt.8
+
+# called from /usr/src/etc/Makefile
+etc-rmt:
+ rm -f ${DESTDIR}/etc/rmt
+ ln -s ${BINDIR}/rmt ${DESTDIR}/etc/rmt
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/rmt/rmt.8 b/usr.sbin/rmt/rmt.8
new file mode 100644
index 0000000..8f9d0df
--- /dev/null
+++ b/usr.sbin/rmt/rmt.8
@@ -0,0 +1,218 @@
+.\" Copyright (c) 1983, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)rmt.8 8.3 (Berkeley) 6/1/94
+.\"
+.Dd June 1, 1994
+.Dt RMT 8
+.Os BSD 4.2
+.Sh NAME
+.Nm rmt
+.Nd remote magtape protocol module
+.Sh SYNOPSIS
+.Nm rmt
+.Sh DESCRIPTION
+.Nm Rmt
+is a program used by the remote dump and restore programs
+in manipulating a magnetic tape drive through an interprocess
+communication connection.
+.Nm Rmt
+is normally started up with an
+.Xr rexec 3
+or
+.Xr rcmd 3
+call.
+.Pp
+The
+.Nm
+program accepts requests specific to the manipulation of
+magnetic tapes, performs the commands, then responds with
+a status indication. All responses are in
+.Tn ASCII
+and in
+one of two forms.
+Successful commands have responses of:
+.Bd -filled -offset indent
+.Sm off
+.Sy A Ar number No \en
+.Sm on
+.Ed
+.Pp
+.Ar Number
+is an
+.Tn ASCII
+representation of a decimal number.
+Unsuccessful commands are responded to with:
+.Bd -filled -offset indent
+.Sm off
+.Xo Sy E Ar error-number
+.No \en Ar error-message
+.No \en
+.Xc
+.Sm on
+.Ed
+.Pp
+.Ar Error-number
+is one of the possible error
+numbers described in
+.Xr intro 2
+and
+.Ar error-message
+is the corresponding error string as printed
+from a call to
+.Xr perror 3 .
+The protocol is comprised of the
+following commands, which are sent as indicated - no spaces are supplied
+between the command and its arguments, or between its arguments, and
+.Ql \en
+indicates that a newline should be supplied:
+.Bl -tag -width Ds
+.Sm off
+.It Xo Sy \&O Ar device
+.No \en Ar mode No \en
+.Xc
+Open the specified
+.Ar device
+using the indicated
+.Ar mode .
+.Ar Device
+is a full pathname and
+.Ar mode
+is an
+.Tn ASCII
+representation of a decimal
+number suitable for passing to
+.Xr open 2 .
+If a device had already been opened, it is
+closed before a new open is performed.
+.It Xo Sy C Ar device No \en
+.Xc
+Close the currently open device. The
+.Ar device
+specified is ignored.
+.It Xo Sy L
+.Ar whence No \en
+.Ar offset No \en
+.Xc
+.Sm on
+Perform an
+.Xr lseek 2
+operation using the specified parameters.
+The response value is that returned from the
+.Xr lseek
+call.
+.Sm off
+.It Sy W Ar count No \en
+.Sm on
+Write data onto the open device.
+.Nm Rmt
+reads
+.Ar count
+bytes from the connection, aborting if
+a premature end-of-file is encountered.
+The response value is that returned from
+the
+.Xr write 2
+call.
+.Sm off
+.It Sy R Ar count No \en
+.Sm on
+Read
+.Ar count
+bytes of data from the open device.
+If
+.Ar count
+exceeds the size of the data buffer (10 kilobytes), it is
+truncated to the data buffer size.
+.Nm Rmt
+then performs the requested
+.Xr read 2
+and responds with
+.Sm off
+.Sy A Ar count-read No \en
+.Sm on
+if the read was
+successful; otherwise an error in the
+standard format is returned. If the read
+was successful, the data read is then sent.
+.Sm off
+.It Xo Sy I Ar operation
+.No \en Ar count No \en
+.Xc
+.Sm on
+Perform a
+.Dv MTIOCOP
+.Xr ioctl 2
+command using the specified parameters.
+The parameters are interpreted as the
+.Tn ASCII
+representations of the decimal values
+to place in the
+.Ar mt_op
+and
+.Ar mt_count
+fields of the structure used in the
+.Xr ioctl
+call. The return value is the
+.Ar count
+parameter when the operation is successful.
+.ne 1i
+.It Sy S
+Return the status of the open device, as
+obtained with a
+.Dv MTIOCGET
+.Xr ioctl
+call. If the operation was successful,
+an ``ack'' is sent with the size of the
+status buffer, then the status buffer is
+sent (in binary).
+.El
+.Sm on
+.Pp
+Any other command causes
+.Nm
+to exit.
+.Sh DIAGNOSTICS
+All responses are of the form described above.
+.Sh SEE ALSO
+.Xr rcmd 3 ,
+.Xr rexec 3 ,
+.Xr mtio 4 ,
+.Xr rdump 8 ,
+.Xr rrestore 8
+.Sh BUGS
+People should be discouraged from using this for a remote
+file access protocol.
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 4.2 .
diff --git a/usr.sbin/rmt/rmt.c b/usr.sbin/rmt/rmt.c
new file mode 100644
index 0000000..88b7623
--- /dev/null
+++ b/usr.sbin/rmt/rmt.c
@@ -0,0 +1,253 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 1983, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)rmt.c 8.1 (Berkeley) 6/6/93";
+#endif
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+/*
+ * rmt
+ */
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/mtio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sgtty.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+int tape = -1;
+
+char *record;
+int maxrecsize = -1;
+
+#define SSIZE 64
+char device[SSIZE];
+char count[SSIZE], mode[SSIZE], pos[SSIZE], op[SSIZE];
+
+char resp[BUFSIZ];
+
+FILE *debug;
+#define DEBUG(f) if (debug) fprintf(debug, f)
+#define DEBUG1(f,a) if (debug) fprintf(debug, f, a)
+#define DEBUG2(f,a1,a2) if (debug) fprintf(debug, f, a1, a2)
+
+char *checkbuf __P((char *, int));
+void error __P((int));
+void getstring __P((char *));
+
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ int rval;
+ char c;
+ int n, i, cc;
+
+ argc--, argv++;
+ if (argc > 0) {
+ debug = fopen(*argv, "w");
+ if (debug == 0)
+ exit(1);
+ (void)setbuf(debug, (char *)0);
+ }
+top:
+ errno = 0;
+ rval = 0;
+ if (read(0, &c, 1) != 1)
+ exit(0);
+ switch (c) {
+
+ case 'O':
+ if (tape >= 0)
+ (void) close(tape);
+ getstring(device);
+ getstring(mode);
+ DEBUG2("rmtd: O %s %s\n", device, mode);
+ /*
+ * XXX the rmt protocol does not provide a means to
+ * specify the permission bits; allow rw for everyone,
+ * as modified by the users umask
+ */
+ tape = open(device, atoi(mode), 0666);
+ if (tape < 0)
+ goto ioerror;
+ goto respond;
+
+ case 'C':
+ DEBUG("rmtd: C\n");
+ getstring(device); /* discard */
+ if (close(tape) < 0)
+ goto ioerror;
+ tape = -1;
+ goto respond;
+
+ case 'L':
+ getstring(count);
+ getstring(pos);
+ DEBUG2("rmtd: L %s %s\n", count, pos);
+ rval = lseek(tape, (off_t)atol(count), atoi(pos));
+ if (rval < 0)
+ goto ioerror;
+ goto respond;
+
+ case 'W':
+ getstring(count);
+ n = atoi(count);
+ DEBUG1("rmtd: W %s\n", count);
+ record = checkbuf(record, n);
+ for (i = 0; i < n; i += cc) {
+ cc = read(0, &record[i], n - i);
+ if (cc <= 0) {
+ DEBUG("rmtd: premature eof\n");
+ exit(2);
+ }
+ }
+ rval = write(tape, record, n);
+ if (rval < 0)
+ goto ioerror;
+ goto respond;
+
+ case 'R':
+ getstring(count);
+ DEBUG1("rmtd: R %s\n", count);
+ n = atoi(count);
+ record = checkbuf(record, n);
+ rval = read(tape, record, n);
+ if (rval < 0)
+ goto ioerror;
+ (void)sprintf(resp, "A%d\n", rval);
+ (void)write(1, resp, strlen(resp));
+ (void)write(1, record, rval);
+ goto top;
+
+ case 'I':
+ getstring(op);
+ getstring(count);
+ DEBUG2("rmtd: I %s %s\n", op, count);
+ { struct mtop mtop;
+ mtop.mt_op = atoi(op);
+ mtop.mt_count = atoi(count);
+ if (ioctl(tape, MTIOCTOP, (char *)&mtop) < 0)
+ goto ioerror;
+ rval = mtop.mt_count;
+ }
+ goto respond;
+
+ case 'S': /* status */
+ DEBUG("rmtd: S\n");
+ { struct mtget mtget;
+ if (ioctl(tape, MTIOCGET, (char *)&mtget) < 0)
+ goto ioerror;
+ rval = sizeof (mtget);
+ (void)sprintf(resp, "A%d\n", rval);
+ (void)write(1, resp, strlen(resp));
+ (void)write(1, (char *)&mtget, sizeof (mtget));
+ goto top;
+ }
+
+ default:
+ DEBUG1("rmtd: garbage command %c\n", c);
+ exit(3);
+ }
+respond:
+ DEBUG1("rmtd: A %d\n", rval);
+ (void)sprintf(resp, "A%d\n", rval);
+ (void)write(1, resp, strlen(resp));
+ goto top;
+ioerror:
+ error(errno);
+ goto top;
+}
+
+void
+getstring(bp)
+ char *bp;
+{
+ int i;
+ char *cp = bp;
+
+ for (i = 0; i < SSIZE; i++) {
+ if (read(0, cp+i, 1) != 1)
+ exit(0);
+ if (cp[i] == '\n')
+ break;
+ }
+ cp[i] = '\0';
+}
+
+char *
+checkbuf(record, size)
+ char *record;
+ int size;
+{
+
+ if (size <= maxrecsize)
+ return (record);
+ if (record != 0)
+ free(record);
+ record = malloc(size);
+ if (record == 0) {
+ DEBUG("rmtd: cannot allocate buffer space\n");
+ exit(4);
+ }
+ maxrecsize = size;
+ while (size > 1024 &&
+ setsockopt(0, SOL_SOCKET, SO_RCVBUF, &size, sizeof (size)) < 0)
+ size -= 1024;
+ return (record);
+}
+
+void
+error(num)
+ int num;
+{
+
+ DEBUG2("rmtd: E %d (%s)\n", num, strerror(num));
+ (void)snprintf(resp, sizeof(resp), "E%d\n%s\n", num, strerror(num));
+ (void)write(1, resp, strlen(resp));
+}
diff --git a/usr.sbin/rndcontrol/Makefile b/usr.sbin/rndcontrol/Makefile
new file mode 100644
index 0000000..2cda5a8
--- /dev/null
+++ b/usr.sbin/rndcontrol/Makefile
@@ -0,0 +1,9 @@
+# $id$
+
+PROG= rndcontrol
+CFLAGS+= -Wall
+MAN4= random.4
+MAN8= rndcontrol.8
+MLINKS+= random.4 urandom.4
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/rndcontrol/random.4 b/usr.sbin/rndcontrol/random.4
new file mode 100644
index 0000000..b563e56
--- /dev/null
+++ b/usr.sbin/rndcontrol/random.4
@@ -0,0 +1,187 @@
+.\"
+.\" random.c -- A strong random number generator
+.\"
+.\" Version 0.92, last modified 21-Sep-95
+.\"
+.\" Copyright Theodore Ts'o, 1994, 1995. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, and the entire permission notice in its entirety,
+.\" including the disclaimer of warranties.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. The name of the author may not be used to endorse or promote
+.\" products derived from this software without specific prior
+.\" written permission.
+.\"
+.\" ALTERNATIVELY, this product may be distributed under the terms of
+.\" the GNU Public License, in which case the provisions of the GPL are
+.\" required INSTEAD OF the above restrictions. (This clause is
+.\" necessary due to a potential bad interaction between the GPL and
+.\" the restrictions contained in a BSD-style copyright.)
+.\"
+.\" THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+.\" WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+.\" DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+.\" INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+.\" (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+.\" SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+.\" STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+.\" OF THE POSSIBILITY OF SUCH DAMAGE.
+.\"
+.\" $Id: random.4,v 1.5 1997/02/22 16:12:46 peter Exp $
+.\"
+.Dd October 21, 1995
+.Dt RANDOM 4 i386
+.Os
+.Sh NAME
+.Nm random ,
+.Nm urandom
+.Nd random number devices
+.Sh DESCRIPTION
+This device gathers environmental noise from device drivers, etc.,
+and returns good random numbers, suitable for cryptographic use.
+Besides the obvious cryptographic uses, these numbers are also good
+for seeding TCP sequence numbers, and other places where it is
+desirable to have numbers which are not only random, but hard to
+predict by an attacker.
+.Ss Theory of operation
+Computers are very predictable devices. Hence it is extremely hard
+to produce truly random numbers on a computer \(em as opposed to
+pseudo-random numbers, which can easily generated by using a
+algorithm. Unfortunately, it is very easy for attackers to guess
+the sequence of pseudo-random number generators, and for some
+applications this is not acceptable. So instead, we must try to
+gather "environmental noise" from the computer's environment, which
+must be hard for outside attackers to observe, and use that to
+generate random numbers. In a Unix environment, this is best done
+from inside the kernel.
+.Pp
+Sources of randomness from the environment include inter-keyboard
+timings, inter-interrupt timings from some interrupts, and other
+events which are both (a) non-deterministic and (b) hard for an
+outside observer to measure. Randomness from these sources are
+added to an "entropy pool", which is periodically mixed using the
+MD5 compression function in CBC mode. As random bytes are mixed
+into the entropy pool, the routines keep an
+.Em estimate
+of how many bits of randomness have been stored into the random number
+generator's internal state.
+.Pp
+When random bytes are desired, they are obtained by taking the MD5
+hash of a counter plus the contents of the "entropy pool". The
+reason for the MD5 hash is so that we can avoid exposing the
+internal state of random number generator. Although the MD5 hash
+does protect the pool, each random byte which is generated from
+the pool reveals some information which was derived from the
+internal state, and thus increases the amount of information an
+outside attacker has available to try to make some guesses about
+the random number generator's internal state. For this reason,
+the routine decreases its internal estimate of how many bits of
+"true randomness" are contained in the entropy pool as it outputs
+random numbers.
+.Pp
+If this estimate goes to zero, the routine can still generate random
+numbers; however it may now be possible for an attacker to analyze
+the output of the random number generator, and the MD5 algorithm,
+and thus have some success in guessing the output of the routine.
+Phil Karn (who devised this mechanism of using MD5 plus a counter
+to extract random numbers from an entropy pool) calls this
+"practical randomness", since in the worse case this is equivalent
+to hashing MD5 with a counter and an undisclosed secret. If MD5 is
+a strong cryptographic hash, this should be fairly resistant to attack.
+.Ss Exported interfaces \(em output
+There are three exported interfaces; the first is one designed to
+be used from within the kernel:
+.Pp
+.Bl -tag -width Pa -compact
+.It Pa void get_random_bytes(void *buf, int nbytes);
+.El
+.Pp
+This interface will return the requested number of random bytes,
+and place it in the requested buffer.
+.Pp
+The two other interfaces are two character devices
+.Pa /dev/random
+and
+.Pa /dev/urandom .
+The
+.Pa /dev/random
+device is suitable for use when very high quality randomness is desired
+(e.g. for key generation), as it will only return a maximum
+of the number of bits of randomness (as estimated by the random number
+generator) contained in the entropy pool.
+.Pp
+The
+.Pa /dev/urandom
+device does not have this limit, and will return as many bytes as are
+requested. As more and more random bytes are requested without giving
+time for the entropy pool to recharge, this will result in lower quality
+random numbers. For many applications, however, this is acceptable.
+.Ss Exported interfaces \(em input
+The two current exported interfaces for gathering environmental
+noise from the devices are:
+.Pp
+.Bl -tag -width Pa -compact
+.It Pa void add_keyboard_randomness(unsigned char scancode);
+.It Pa void add_interrupt_randomness(int irq);
+.El
+.Pp
+The first function uses the inter-keypress timing, as well as the
+scancode as random inputs into the "entropy pool".
+.Pp
+The second function uses the inter-interrupt timing as random
+inputs to the entropy pool. Note that not all interrupts are good
+sources of randomness! For example, the timer interrupts is not a
+good choice, because the periodicity of the interrupts is too
+regular, and hence predictable to an attacker. Disk interrupts are
+a better measure, since the timing of the disk interrupts are more
+unpredictable. The routines try to estimate how many bits of
+randomness a particular interrupt channel offers, by keeping track
+of the first and second order deltas in the interrupt timings.
+.Sh ACKNOWLEDGEMENTS
+The original core code was written by
+.An Theodore Ts'o ,
+and was intended
+for the Linux platform. This was ported to
+.Bx Free
+by
+.An Mark Murray ,
+who also wrote the
+.Xr rndcontrol 8
+utility.
+.Pp
+Ideas for constructing this random number generator were derived
+from the Pretty Good Privacy's random number generator, and from
+private discussions with Phil Karn. This design has been further
+modified by myself, so any flaws are solely my responsibility, and
+should not be attributed to the authors of PGP or to Phil.
+.Pp
+The code for MD5 transform was taken from Colin Plumb's
+implementation, which has been placed in the public domain. The
+MD5 cryptographic checksum was devised by Ronald Rivest, and is
+documented in RFC 1321, "The MD5 Message Digest Algorithm".
+.Pp
+Further background information on this topic may be obtained from
+RFC 1750, "Randomness Recommendations for Security", by Donald
+Eastlake, Steve Crocker, and Jeff Schiller.
+.Sh "SEE ALSO"
+.Xr rndcontrol 8
+.Sh FILES
+.Bl -tag -width Pa -compact
+.It Pa /dev/random
+.It Pa /dev/urandom
+.El
+.Sh HISTORY
+The
+.Pa random ,
+.Pa urandom
+files appeared in
+.Fx 2.1.5 .
diff --git a/usr.sbin/rndcontrol/rndcontrol.8 b/usr.sbin/rndcontrol/rndcontrol.8
new file mode 100644
index 0000000..d2dedbf
--- /dev/null
+++ b/usr.sbin/rndcontrol/rndcontrol.8
@@ -0,0 +1,96 @@
+.\"
+.\" Copyright (c) 1995
+.\" Mark Murray. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by Mark Murray
+.\" and Theodore Ts'o
+.\" 4. Neither the name of the author nor the names of any co-contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY MARK MURRAY AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL MARK MURRAY OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $Id: rndcontrol.8,v 1.6 1997/02/22 16:12:47 peter Exp $
+.\"
+.Dd October 20, 1995
+.Dt RNDCONTROL 8
+.Os FreeBSD 2
+.Sh NAME
+.Nm rndcontrol
+.Nd a utility for manipulating the /dev/random device
+.Sh SYNOPSIS
+.Nm rndcontrol
+.Op Fl q
+.Op Fl s Ar irq_no
+.Op Fl c Ar irq_no
+.Sh DESCRIPTION
+The
+.Nm
+command is used to set which interrupts are used to help randomise
+the ``pool of entropy'' maintained by the kernel. The
+.Pa /dev/random
+and
+.Pa /dev/urandom
+devices are the user interface to this source of randomness.
+Any changes take effect immediately.
+.Sh OPTIONS
+The following command line options are supported:
+.Bl -tag -width indent
+.It Fl q
+Turn off all output except errors.
+.It Fl s Ar n
+Allow IRQ
+.Ar n
+to be used as a source of randomness. This option may be repeated for
+more than one IRQ.
+.It Fl c Ar n
+Stop IRQ
+.Ar n
+from being used as a source of randomness. This option may be repeated for
+more than one IRQ.
+.El
+.Pp
+The default is to have no IRQ's being used.
+.Pp
+.Sh FILES
+.Bl -tag -width /dev/urandom -compact
+.It Pa /dev/random
+secure random device
+.It Pa /dev/urandom
+random device
+.El
+.Sh BUGS
+Sure to be some.
+.Sh "SEE ALSO"
+.Xr random 4
+.Sh AUTHOR
+.An Theodore Ts'o
+wrote the core code.
+.An Mark Murray
+ported this code to
+.Bx Free
+and wrote the support routines and constructed the man pages.
+.Sh HISTORY
+.Nm Rndcontrol
+first appeared in
+.Fx 2.1.5 .
diff --git a/usr.sbin/rndcontrol/rndcontrol.c b/usr.sbin/rndcontrol/rndcontrol.c
new file mode 100644
index 0000000..581bb0e
--- /dev/null
+++ b/usr.sbin/rndcontrol/rndcontrol.c
@@ -0,0 +1,121 @@
+/*
+ * Copyright (c) 1995
+ * Mark Murray. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Mark Murray
+ * and Theodore Ts'o
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY MARK MURRAY AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL MARK MURRAY OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <machine/random.h>
+
+static void
+usage()
+{
+ fprintf(stderr, "usage: rndcontrol [-q] [-s irq_no] [-c irq_no]\n");
+ exit(1);
+}
+
+int
+main(int argc, char *argv[])
+{
+ int verbose, ch, fd, result, i;
+ u_int16_t irq;
+
+ verbose = 1;
+
+ fd = open("/dev/random", O_RDONLY, 0);
+ if (fd == -1) {
+ warn("/dev/random");
+ return (1);
+ }
+ else {
+ while ((ch = getopt(argc, argv, "qs:c:")) != -1)
+ switch (ch) {
+ case 'q':
+ verbose = 0;
+ break;
+ case 's':
+ irq = (u_int16_t)atoi(optarg);
+ if (verbose)
+ printf("%s: setting irq %d\n", argv[0], irq);
+ result = ioctl(fd, MEM_SETIRQ, (char *)&irq);
+ if (result == -1) {
+ warn("%s", argv[0]);
+ return (1);
+ }
+ break;
+ case 'c':
+ irq = (u_int16_t)atoi(optarg);
+ if (verbose)
+ printf("%s: clearing irq %d\n", argv[0], irq);
+ result = ioctl(fd, MEM_CLEARIRQ, (char *)&irq);
+ if (result == -1) {
+ warn("%s", argv[0]);
+ return (1);
+ }
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ }
+ if (verbose) {
+ result = ioctl(fd, MEM_RETURNIRQ, (char *)&irq);
+ if (result == -1) {
+ warn("%s", argv[0]);
+ return (1);
+ }
+ printf("%s: interrupts in use:", argv[0]);
+ for (i = 0; i < 16; i++)
+ if (irq & (1 << i))
+ printf(" %d", i);
+ printf("\n");
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc) {
+ fprintf(stderr, "%s: unknown argument(s): ", argv[-optind]);
+ for (i = 0; i < argc; i++)
+ fprintf(stderr, " %s", argv[i]);
+ fprintf(stderr, "\n");
+ }
+
+ return 0;
+}
diff --git a/usr.sbin/rpc.lockd/Makefile b/usr.sbin/rpc.lockd/Makefile
new file mode 100644
index 0000000..55835f4
--- /dev/null
+++ b/usr.sbin/rpc.lockd/Makefile
@@ -0,0 +1,26 @@
+# $Id: Makefile,v 1.4 1997/02/22 16:12:49 peter Exp $
+
+PROG = rpc.lockd
+SRCS = nlm_prot_svc.c lockd.c procs.c
+MAN8 = rpc.lockd.8
+
+DPADD= ${LIBRPCSVC}
+LDADD= -lrpcsvc
+
+CFLAGS+= -I.
+
+CLEANFILES= nlm_prot_svc.c nlm_prot.h
+
+RPCSRC= ${DESTDIR}/usr/include/rpcsvc/nlm_prot.x
+RPCGEN= rpcgen -L -C
+
+nlm_prot_svc.c: ${RPCSRC} nlm_prot.h
+ ${RPCGEN} -m -o ${.TARGET} ${RPCSRC}
+
+nlm_prot.h: ${RPCSRC}
+ ${RPCGEN} -h -o ${.TARGET} ${RPCSRC}
+
+test: test.c
+ cc -o test test.c -lrpcsvc
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/rpc.lockd/handles.c b/usr.sbin/rpc.lockd/handles.c
new file mode 100644
index 0000000..8be7a29
--- /dev/null
+++ b/usr.sbin/rpc.lockd/handles.c
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 1995
+ * A.R. Gordon (andrew.gordon@net-tel.co.uk). All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed for the FreeBSD project
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY ANDREW GORDON AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#include "nlm_prot.h"
+
+/* ------------------------------------------------------------------------- */
+/*
+
+
+* need to find all fds in use by a host and free them when host crashes
+ (need not be efficient)
+
+* need to find fd corresponding to <inode, device>
+
+*/
+
+typedef struct fdinfo
+{
+ int fd; /* The file descriptor itself */
+ int ref_count; /* Count of hosts using the fd - fd is */
+ /* closed when this reaches zero */
+ ino_t inode_no; /* The inode number of this file. */
+ dev_t device; /* device on which the file lives. */
+ struct fdinfo *next; /* Chain of FdInfo structures */
+ struct fdinfo *prev;
+} FdInfo;
diff --git a/usr.sbin/rpc.lockd/lockd.c b/usr.sbin/rpc.lockd/lockd.c
new file mode 100644
index 0000000..44a5e21
--- /dev/null
+++ b/usr.sbin/rpc.lockd/lockd.c
@@ -0,0 +1,107 @@
+/*
+ * Copyright (c) 1995
+ * A.R. Gordon (andrew.gordon@net-tel.co.uk). All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed for the FreeBSD project
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY ANDREW GORDON AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+/* main() function for NFS lock daemon. Most of the code in this */
+/* file was generated by running rpcgen /usr/include/rpcsvc/nlm_prot.x */
+/* The actual program logic is in the file procs.c */
+
+#include <err.h>
+#include <stdlib.h>
+#include <string.h>
+#include <rpc/rpc.h>
+#include <rpc/pmap_clnt.h>
+#include "lockd.h"
+
+void nlm_prog_1 __P((struct svc_req *, SVCXPRT *));
+void nlm_prog_3 __P((struct svc_req *, SVCXPRT *));
+static void usage __P((void));
+
+int debug_level = 0; /* Zero means no debugging syslog() calls */
+
+int
+main(int argc, char **argv)
+{
+ SVCXPRT *transp;
+
+ if (argc > 1)
+ {
+ if (strncmp(argv[1], "-d", 2))
+ usage();
+ if (argc > 2) debug_level = atoi(argv[2]);
+ else debug_level = atoi(argv[1] + 2);
+ /* Ensure at least some debug if -d with no specified level */
+ if (!debug_level) debug_level = 1;
+ }
+
+ (void)pmap_unset(NLM_PROG, NLM_VERS);
+ (void)pmap_unset(NLM_PROG, NLM_VERSX);
+
+ transp = svcudp_create(RPC_ANYSOCK);
+ if (transp == NULL)
+ errx(1, "cannot create udp service");
+ if (!svc_register(transp, NLM_PROG, NLM_VERS, nlm_prog_1, IPPROTO_UDP))
+ errx(1, "unable to register (NLM_PROG, NLM_VERS, udp)");
+ if (!svc_register(transp, NLM_PROG, NLM_VERSX, nlm_prog_3, IPPROTO_UDP))
+ errx(1, "unable to register (NLM_PROG, NLM_VERSX, udp)");
+
+ transp = svctcp_create(RPC_ANYSOCK, 0, 0);
+ if (transp == NULL)
+ errx(1, "cannot create tcp service");
+ if (!svc_register(transp, NLM_PROG, NLM_VERS, nlm_prog_1, IPPROTO_TCP))
+ errx(1, "unable to register (NLM_PROG, NLM_VERS, tcp)");
+ if (!svc_register(transp, NLM_PROG, NLM_VERSX, nlm_prog_3, IPPROTO_TCP))
+ errx(1, "unable to register (NLM_PROG, NLM_VERSX, tcp)");
+
+ /* Note that it is NOT sensible to run this program from inetd - the */
+ /* protocol assumes that it will run immediately at boot time. */
+ if (daemon(0,0))
+ err(1, "fork");
+ openlog("rpc.lockd", 0, LOG_DAEMON);
+ if (debug_level) syslog(LOG_INFO, "Starting, debug level %d", debug_level);
+ else syslog(LOG_INFO, "Starting");
+
+ svc_run(); /* Should never return */
+ exit(1);
+}
+
+static void
+usage()
+{
+ fprintf(stderr, "usage: rpc.lockd [-d [debuglevel]]\n");
+ exit(1);
+}
diff --git a/usr.sbin/rpc.lockd/lockd.h b/usr.sbin/rpc.lockd/lockd.h
new file mode 100644
index 0000000..39586bf
--- /dev/null
+++ b/usr.sbin/rpc.lockd/lockd.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 1995
+ * A.R. Gordon (andrew.gordon@net-tel.co.uk). All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed for the FreeBSD project
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY ANDREW GORDON AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+
+
+#include <stdio.h>
+#include <rpc/rpc.h>
+#include <syslog.h>
+#include <rpcsvc/sm_inter.h> /* protocol to talk to rpc.statd */
+#include "nlm_prot.h" /* The protocol we are implementing */
+
+
+/* global variables ------------------------------------------------------- */
+extern int debug_level;
diff --git a/usr.sbin/rpc.lockd/procs.c b/usr.sbin/rpc.lockd/procs.c
new file mode 100644
index 0000000..84ead97
--- /dev/null
+++ b/usr.sbin/rpc.lockd/procs.c
@@ -0,0 +1,592 @@
+/*
+ * Copyright (c) 1995
+ * A.R. Gordon (andrew.gordon@net-tel.co.uk). All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed for the FreeBSD project
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY ANDREW GORDON AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#include <string.h>
+#include "lockd.h"
+
+#include <sys/param.h> /* for MAXHOSTNAMELEN */
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <netdb.h>
+
+
+#define CLIENT_CACHE_SIZE 64 /* No. of client sockets cached */
+#define CLIENT_CACHE_LIFETIME 120 /* In seconds */
+
+
+/* log_from_addr ----------------------------------------------------------- */
+/*
+ Purpose: Log name of function called and source address
+ Returns: Nothing
+ Notes: Extracts the source address from the transport handle
+ passed in as part of the called procedure specification
+*/
+
+static void log_from_addr(char *fun_name, struct svc_req *req)
+{
+ struct sockaddr_in *addr;
+ struct hostent *host;
+ char hostname_buf[40];
+
+ addr = svc_getcaller(req->rq_xprt);
+ host = gethostbyaddr((char *)&(addr->sin_addr), addr->sin_len, AF_INET);
+ if (host)
+ {
+ strncpy(hostname_buf, host->h_name, sizeof(hostname_buf));
+ hostname_buf[sizeof(hostname_buf) -1] = '\0';
+ }
+ else /* No hostname available - print raw address */
+ {
+ strcpy(hostname_buf, inet_ntoa(addr->sin_addr));
+ }
+
+ syslog(LOG_DEBUG, "%s from %s", fun_name, hostname_buf);
+}
+
+
+/* get_client -------------------------------------------------------------- */
+/*
+ Purpose: Get a CLIENT* for making RPC calls to lockd on given host
+ Returns: CLIENT* pointer, from clnt_udp_create, or NULL if error
+ Notes: Creating a CLIENT* is quite expensive, involving a
+ conversation with the remote portmapper to get the
+ port number. Since a given client is quite likely
+ to make several locking requests in succession, it is
+ desirable to cache the created CLIENT*.
+
+ Since we are using UDP rather than TCP, there is no cost
+ to the remote system in keeping these cached indefinitely.
+ Unfortunately there is a snag: if the remote system
+ reboots, the cached portmapper results will be invalid,
+ and we will never detect this since all of the xxx_msg()
+ calls return no result - we just fire off a udp packet
+ and hope for the best.
+
+ We solve this by discarding cached values after two
+ minutes, regardless of whether they have been used
+ in the meanwhile (since a bad one might have been used
+ plenty of times, as the host keeps retrying the request
+ and we keep sending the reply back to the wrong port).
+
+ Given that the entries will always expire in the order
+ that they were created, there is no point in a LRU
+ algorithm for when the cache gets full - entries are
+ always re-used in sequence.
+*/
+
+static CLIENT *clnt_cache_ptr[CLIENT_CACHE_SIZE];
+static long clnt_cache_time[CLIENT_CACHE_SIZE]; /* time entry created */
+static struct in_addr clnt_cache_addr[CLIENT_CACHE_SIZE];
+static int clnt_cache_next_to_use = 0;
+
+static CLIENT *get_client(struct sockaddr_in *host_addr)
+{
+ CLIENT *client;
+ int sock_no;
+ struct timeval retry_time, time_now;
+ int i;
+
+ gettimeofday(&time_now, NULL);
+
+ /* Search for the given client in the cache, zapping any expired */
+ /* entries that we happen to notice in passing. */
+ for (i = 0; i < CLIENT_CACHE_SIZE; i++)
+ {
+ client = clnt_cache_ptr[i];
+ if (client &&
+ ((clnt_cache_time[i] + CLIENT_CACHE_LIFETIME) < time_now.tv_sec))
+ {
+ /* Cache entry has expired. */
+ if (debug_level > 3) syslog(LOG_DEBUG, "Expired CLIENT* in cache");
+ clnt_cache_time[i] = 0L;
+ clnt_destroy(client);
+ clnt_cache_ptr[i] = NULL;
+ client = NULL;
+ }
+
+ if (client && !memcmp(&clnt_cache_addr[i], &host_addr->sin_addr,
+ sizeof(struct in_addr)))
+ {
+ /* Found it! */
+ if (debug_level > 3) syslog(LOG_DEBUG, "Found CLIENT* in cache");
+ return (client);
+ }
+ }
+
+ /* Not found in cache. Free the next entry if it is in use */
+ if (clnt_cache_ptr[clnt_cache_next_to_use])
+ {
+ clnt_destroy(clnt_cache_ptr[clnt_cache_next_to_use]);
+ clnt_cache_ptr[clnt_cache_next_to_use] = NULL;
+ }
+
+ /* Create the new client handle */
+
+ sock_no = RPC_ANYSOCK;
+ retry_time.tv_sec = 5;
+ retry_time.tv_usec = 0;
+ host_addr->sin_port = 0; /* Force consultation with portmapper */
+ client = clntudp_create(host_addr, NLM_PROG, NLM_VERS, retry_time, &sock_no);
+ if (!client)
+ {
+ syslog(LOG_ERR, clnt_spcreateerror("clntudp_create"));
+ syslog(LOG_ERR, "Unable to return result to %s",
+ inet_ntoa(host_addr->sin_addr));
+ return NULL;
+ }
+
+ /* Success - update the cache entry */
+ clnt_cache_ptr[clnt_cache_next_to_use] = client;
+ clnt_cache_addr[clnt_cache_next_to_use] = host_addr->sin_addr;
+ clnt_cache_time[clnt_cache_next_to_use] = time_now.tv_sec;
+ if (++clnt_cache_next_to_use > CLIENT_CACHE_SIZE)
+ clnt_cache_next_to_use = 0;
+
+ /* Disable the default timeout, so we can specify our own in calls */
+ /* to clnt_call(). [note that the timeout is a different concept */
+ /* from the retry period set in clnt_udp_create() above.] */
+ retry_time.tv_sec = -1;
+ retry_time.tv_usec = -1;
+ clnt_control(client, CLSET_TIMEOUT, &retry_time);
+
+ if (debug_level > 3) syslog(LOG_DEBUG, "Created CLIENT* for %s",
+ inet_ntoa(host_addr->sin_addr));
+ return client;
+}
+
+
+/* transmit_result --------------------------------------------------------- */
+/*
+ Purpose: Transmit result for nlm_xxx_msg pseudo-RPCs
+ Returns: Nothing - we have no idea if the datagram got there
+ Notes: clnt_call() will always fail (with timeout) as we are
+ calling it with timeout 0 as a hack to just issue a datagram
+ without expecting a result
+*/
+
+static void transmit_result(int opcode, nlm_res *result, struct svc_req *req)
+{
+ static char dummy;
+ struct sockaddr_in *addr;
+ CLIENT *cli;
+ int success;
+ struct timeval timeo;
+
+ addr = svc_getcaller(req->rq_xprt);
+ if ((cli = get_client(addr)))
+ {
+ timeo.tv_sec = 0; /* No timeout - not expecting response */
+ timeo.tv_usec = 0;
+
+ success = clnt_call(cli, opcode, xdr_nlm_res, result, xdr_void,
+ &dummy, timeo);
+
+ if (debug_level > 2) syslog(LOG_DEBUG, "clnt_call returns %d\n", success);
+ }
+}
+
+/* ------------------------------------------------------------------------- */
+/*
+ Functions for Unix<->Unix locking (ie. monitored locking, with rpc.statd
+ involved to ensure reclaim of locks after a crash of the "stateless"
+ server.
+
+ These all come in two flavours - nlm_xxx() and nlm_xxx_msg().
+ The first are standard RPCs with argument and result.
+ The nlm_xxx_msg() calls implement exactly the same functions, but
+ use two pseudo-RPCs (one in each direction). These calls are NOT
+ standard use of the RPC protocol in that they do not return a result
+ at all (NB. this is quite different from returning a void result).
+ The effect of this is to make the nlm_xxx_msg() calls simple unacknowledged
+ datagrams, requiring higher-level code to perform retries.
+
+ Despite the disadvantages of the nlm_xxx_msg() approach (some of which
+ are documented in the comments to get_client() above), this is the
+ interface used by all current commercial NFS implementations
+ [Solaris, SCO, AIX etc.]. This is presumed to be because these allow
+ implementations to continue using the standard RPC libraries, while
+ avoiding the block-until-result nature of the library interface.
+
+ No client implementations have been identified so far that make use
+ of the true RPC version (early SunOS releases would be a likely candidate
+ for testing).
+*/
+
+
+/* nlm_test ---------------------------------------------------------------- */
+/*
+ Purpose: Test whether a specified lock would be granted if requested
+ Returns: nlm_granted (or error code)
+ Notes:
+*/
+
+nlm_testres *nlm_test_1_svc(nlm_testargs *arg, struct svc_req *rqstp)
+{
+ static nlm_testres res;
+ if (debug_level) log_from_addr("nlm_test", rqstp);
+
+ /* Copy the cookie from the argument into the result. Note that this */
+ /* is slightly hazardous, as the structure contains a pointer to a */
+ /* malloc()ed buffer that will get freed by the caller. However, the */
+ /* main function transmits the result before freeing the argument */
+ /* so it is in fact safe. */
+ res.cookie = arg->cookie;
+ res.stat.stat = nlm_granted;
+ return (&res);
+}
+
+void *nlm_test_msg_1_svc(nlm_testargs *arg, struct svc_req *rqstp)
+{
+ nlm_testres res;
+ static char dummy;
+ struct sockaddr_in *addr;
+ CLIENT *cli;
+ int success;
+ struct timeval timeo;
+
+ if (debug_level) log_from_addr("nlm_test_msg", rqstp);
+
+ res.cookie = arg->cookie;
+ res.stat.stat = nlm_granted;
+
+ /* nlm_test has different result type to the other operations, so */
+ /* can't use transmit_result() in this case */
+ addr = svc_getcaller(rqstp->rq_xprt);
+ if ((cli = get_client(addr)))
+ {
+ timeo.tv_sec = 0; /* No timeout - not expecting response */
+ timeo.tv_usec = 0;
+
+ success = clnt_call(cli, NLM_TEST_RES, xdr_nlm_testres, &res, xdr_void,
+ &dummy, timeo);
+
+ if (debug_level > 2) syslog(LOG_DEBUG, "clnt_call returns %d\n", success);
+ }
+ return (NULL);
+}
+
+/* nlm_lock ---------------------------------------------------------------- */
+/*
+ Purposes: Establish a lock
+ Returns: granted, denied or blocked
+ Notes: *** grace period support missing
+*/
+
+nlm_res *nlm_lock_1_svc(nlm_lockargs *arg, struct svc_req *rqstp)
+{
+ static nlm_res res;
+ if (debug_level) log_from_addr("nlm_lock", rqstp);
+
+ /* copy cookie from arg to result. See comment in nlm_test_1() */
+ res.cookie = arg->cookie;
+
+ res.stat.stat = nlm_granted;
+ return (&res);
+}
+
+void *nlm_lock_msg_1_svc(nlm_lockargs *arg, struct svc_req *rqstp)
+{
+ static nlm_res res;
+
+ if (debug_level) log_from_addr("nlm_lock_msg", rqstp);
+
+ res.cookie = arg->cookie;
+ res.stat.stat = nlm_granted;
+ transmit_result(NLM_LOCK_RES, &res, rqstp);
+
+ return (NULL);
+}
+
+/* nlm_cancel -------------------------------------------------------------- */
+/*
+ Purpose: Cancel a blocked lock request
+ Returns: granted or denied
+ Notes:
+*/
+
+nlm_res *nlm_cancel_1_svc(nlm_cancargs *arg, struct svc_req *rqstp)
+{
+ static nlm_res res;
+ if (debug_level) log_from_addr("nlm_cancel", rqstp);
+
+ /* copy cookie from arg to result. See comment in nlm_test_1() */
+ res.cookie = arg->cookie;
+
+ /* Since at present we never return 'nlm_blocked', there can never be */
+ /* a lock to cancel, so this call always fails. */
+ res.stat.stat = nlm_denied;
+ return (&res);
+}
+
+void *nlm_cancel_msg_1_svc(nlm_cancargs *arg, struct svc_req *rqstp)
+{
+ static nlm_res res;
+ if (debug_level) log_from_addr("nlm_cancel_msg", rqstp);
+
+ res.cookie = arg->cookie;
+ /* Since at present we never return 'nlm_blocked', there can never be */
+ /* a lock to cancel, so this call always fails. */
+ res.stat.stat = nlm_denied;
+ transmit_result(NLM_CANCEL_RES, &res, rqstp);
+ return (NULL);
+}
+
+/* nlm_unlock -------------------------------------------------------------- */
+/*
+ Purpose: Release an existing lock
+ Returns: Always granted, unless during grace period
+ Notes: "no such lock" error condition is ignored, as the
+ protocol uses unreliable UDP datagrams, and may well
+ re-try an unlock that has already succeeded.
+*/
+
+nlm_res *nlm_unlock_1_svc(nlm_unlockargs *arg, struct svc_req *rqstp)
+{
+ static nlm_res res;
+ if (debug_level) log_from_addr("nlm_unlock", rqstp);
+
+ res.stat.stat= nlm_granted;
+ res.cookie = arg->cookie;
+
+ return (&res);
+}
+
+void *nlm_unlock_msg_1_svc(nlm_unlockargs *arg, struct svc_req *rqstp)
+{
+ static nlm_res res;
+ if (debug_level) log_from_addr("nlm_unlock_msg", rqstp);
+
+ res.stat.stat = nlm_granted;
+ res.cookie = arg->cookie;
+
+ transmit_result(NLM_UNLOCK_RES, &res, rqstp);
+ return (NULL);
+}
+
+/* ------------------------------------------------------------------------- */
+/*
+ Client-side pseudo-RPCs for results. Note that for the client there
+ are only nlm_xxx_msg() versions of each call, since the 'real RPC'
+ version returns the results in the RPC result, and so the client
+ does not normally receive incoming RPCs.
+
+ The exception to this is nlm_granted(), which is genuinely an RPC
+ call from the server to the client - a 'call-back' in normal procedure
+ call terms.
+*/
+
+/* nlm_granted ------------------------------------------------------------- */
+/*
+ Purpose: Receive notification that formerly blocked lock now granted
+ Returns: always success ('granted')
+ Notes:
+*/
+
+nlm_res *nlm_granted_1_svc(nlm_testargs *arg, struct svc_req *rqstp)
+{
+ static nlm_res res;
+ if (debug_level) log_from_addr("nlm_granted", rqstp);
+
+ /* copy cookie from arg to result. See comment in nlm_test_1() */
+ res.cookie = arg->cookie;
+
+ res.stat.stat = nlm_granted;
+ return (&res);
+}
+
+void *nlm_granted_msg_1_svc(nlm_testargs *arg, struct svc_req *rqstp)
+{
+ nlm_res res;
+ if (debug_level) log_from_addr("nlm_granted_msg", rqstp);
+
+ res.cookie = arg->cookie;
+ res.stat.stat = nlm_granted;
+ transmit_result(NLM_GRANTED_RES, &res, rqstp);
+ return (NULL);
+}
+
+/* nlm_test_res ------------------------------------------------------------ */
+/*
+ Purpose: Accept result from earlier nlm_test_msg() call
+ Returns: Nothing
+*/
+
+void *nlm_test_res_1_svc(nlm_testres *arg, struct svc_req *rqstp)
+{
+ if (debug_level) log_from_addr("nlm_test_res", rqstp);
+ return (NULL);
+}
+
+/* nlm_lock_res ------------------------------------------------------------ */
+/*
+ Purpose: Accept result from earlier nlm_lock_msg() call
+ Returns: Nothing
+*/
+
+void *nlm_lock_res_1_svc(nlm_res *arg, struct svc_req *rqstp)
+{
+ if (debug_level) log_from_addr("nlm_lock_res", rqstp);
+
+ return (NULL);
+}
+
+/* nlm_cancel_res ---------------------------------------------------------- */
+/*
+ Purpose: Accept result from earlier nlm_cancel_msg() call
+ Returns: Nothing
+*/
+
+void *nlm_cancel_res_1_svc(nlm_res *arg, struct svc_req *rqstp)
+{
+ if (debug_level) log_from_addr("nlm_cancel_res", rqstp);
+ return (NULL);
+}
+
+/* nlm_unlock_res ---------------------------------------------------------- */
+/*
+ Purpose: Accept result from earlier nlm_unlock_msg() call
+ Returns: Nothing
+*/
+
+void *nlm_unlock_res_1_svc(nlm_res *arg, struct svc_req *rqstp)
+{
+ if (debug_level) log_from_addr("nlm_unlock_res", rqstp);
+ return (NULL);
+}
+
+/* nlm_granted_res --------------------------------------------------------- */
+/*
+ Purpose: Accept result from earlier nlm_granted_msg() call
+ Returns: Nothing
+*/
+
+void *nlm_granted_res_1_svc(nlm_res *arg, struct svc_req *rqstp)
+{
+ if (debug_level) log_from_addr("nlm_granted_res", rqstp);
+ return (NULL);
+}
+
+/* ------------------------------------------------------------------------- */
+/*
+ Calls for PCNFS locking (aka non-monitored locking, no involvement
+ of rpc.statd).
+
+ These are all genuine RPCs - no nlm_xxx_msg() nonsense here.
+*/
+
+
+/* nlm_share --------------------------------------------------------------- */
+/*
+ Purpose: Establish a DOS-style lock
+ Returns: success or failure
+ Notes: Blocking locks are not supported - client is expected
+ to retry if required.
+*/
+
+nlm_shareres *nlm_share_3_svc(nlm_shareargs *arg, struct svc_req *rqstp)
+{
+ static nlm_shareres res;
+ if (debug_level) log_from_addr("nlm_share", rqstp);
+
+ res.cookie = arg->cookie;
+ res.stat = nlm_granted;
+ res.sequence = 1234356; /* X/Open says this field is ignored? */
+ return (&res);
+}
+
+/* nlm_unshare ------------------------------------------------------------ */
+/*
+ Purpose: Release a DOS-style lock
+ Returns: nlm_granted, unless in grace period
+ Notes:
+*/
+
+nlm_shareres *nlm_unshare_3_svc(nlm_shareargs *arg, struct svc_req *rqstp)
+{
+ static nlm_shareres res;
+ if (debug_level) log_from_addr("nlm_unshare", rqstp);
+
+ res.cookie = arg->cookie;
+ res.stat = nlm_granted;
+ res.sequence = 1234356; /* X/Open says this field is ignored? */
+ return (&res);
+}
+
+/* nlm_nm_lock ------------------------------------------------------------ */
+/*
+ Purpose: non-monitored version of nlm_lock()
+ Returns: as for nlm_lock()
+ Notes: These locks are in the same style as the standard nlm_lock,
+ but the rpc.statd should not be called to establish a
+ monitor for the client machine, since that machine is
+ declared not to be running a rpc.statd, and so would not
+ respond to the statd protocol.
+*/
+
+nlm_res *nlm_nm_lock_3_svc(nlm_lockargs *arg, struct svc_req *rqstp)
+{
+ static nlm_res res;
+ if (debug_level) log_from_addr("nlm_nm_lock", rqstp);
+
+ /* copy cookie from arg to result. See comment in nlm_test_1() */
+ res.cookie = arg->cookie;
+ res.stat.stat = nlm_granted;
+ return (&res);
+}
+
+/* nlm_free_all ------------------------------------------------------------ */
+/*
+ Purpose: Release all locks held by a named client
+ Returns: Nothing
+ Notes: Potential denial of service security problem here - the
+ locks to be released are specified by a host name, independent
+ of the address from which the request has arrived.
+ Should probably be rejected if the named host has been
+ using monitored locks.
+*/
+
+void *nlm_free_all_3_svc(nlm_notify *arg, struct svc_req *rqstp)
+{
+ static char dummy;
+
+ if (debug_level) log_from_addr("nlm_free_all", rqstp);
+ return (&dummy);
+}
+
+
diff --git a/usr.sbin/rpc.lockd/rpc.lockd.8 b/usr.sbin/rpc.lockd/rpc.lockd.8
new file mode 100644
index 0000000..4fe66b6
--- /dev/null
+++ b/usr.sbin/rpc.lockd/rpc.lockd.8
@@ -0,0 +1,95 @@
+.\" -*- nroff -*-
+.\"
+.\" Copyright (c) 1995 A.R.Gordon, andrew.gordon@net-tel.co.uk
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\"
+.Dd September 24, 1995
+.Dt RPC.LOCKD 8
+.Os
+.Sh NAME
+.Nm rpc.lockd
+.Nd NFS file locking daemon
+.Sh SYNOPSIS
+.Nm rpc.lockd
+.Op Fl d Op Ar debug_level
+.Sh DESCRIPTION
+.Nm Rpc.lockd
+is a daemon which provides file- and record-locking services in an NFS
+environment.
+.Pp
+The following option is available:
+.Bl -tag -width indent
+.It Fl d
+Cause debugging information to be written to syslog, recording
+all RPC transactions to the daemon. These messages are logged with level
+LOG_DEBUG and facility LOG_DAEMON. If debug_level is not specified,
+level 1 is assumed, giving one log line per protocol operation. Higher
+debug levels can be specified, causing display of operation arguments
+and internal operations of the daemon.
+.El
+.Pp
+Error conditions are logged to syslog, irrespective of the debug level,
+using log level LOG_ERR and facility LOG_DAEMON.
+.Pp
+The
+.Nm
+daemon must NOT be invoked by
+.Xr inetd 8
+because the protocol assumes that the daemon will run from system start time.
+Instead, it should be run from
+.Xr rc 8
+after the network has been started.
+.Sh FILES
+.Bl -tag -width /usr/include/rpcsvc/nlm_prot.x -compact
+.It Pa /usr/include/rpcsvc/nlm_prot.x
+RPC protocol specification for the network lock manager protocol.
+.El
+.Sh SEE ALSO
+.Xr syslog 3 ,
+.Xr rc 8 ,
+.Xr rpc.statd 8
+.Sh BUGS
+The current implementation provides only the server side of the protocol
+(ie. clients running other OS types can establish locks on a
+.Bx Free
+fileserver,
+but there is currently no means for a
+.Bx Free
+client to establish locks).
+.Pp
+Versions 1, 2 and 3 of the protocol are supported. However, only versions
+2 (Unix systems) and 3 (PC-NFS clients) seem to be in common use - the version
+1 support has not been tested due to the lack of version 1 clients against
+which to test.
+.Sh STANDARDS
+The implementation is based on the specification in X/Open CAE Specification
+C218, "Protocols for X/Open PC Interworking: XNFS, Issue 4", ISBN 1 872630 66 9
diff --git a/usr.sbin/rpc.lockd/test.c b/usr.sbin/rpc.lockd/test.c
new file mode 100644
index 0000000..01142a4
--- /dev/null
+++ b/usr.sbin/rpc.lockd/test.c
@@ -0,0 +1,366 @@
+#ifndef lint
+#if 0
+static char sccsid[] = "from: @(#)nlm_prot.x 1.8 87/09/21 Copyr 1987 Sun Micro";
+static char sccsid[] = "from: * @(#)nlm_prot.x 2.1 88/08/01 4.0 RPCSRC";
+#endif
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#include <rpc/rpc.h>
+#include <rpcsvc/nlm_prot.h>
+
+/* Default timeout can be changed using clnt_control() */
+static struct timeval TIMEOUT = { 0, 0 };
+
+nlm_testres *
+nlm_test_1(argp, clnt)
+ struct nlm_testargs *argp;
+ CLIENT *clnt;
+{
+ static nlm_testres res;
+
+ bzero((char *)&res, sizeof(res));
+ if (clnt_call(clnt, NLM_TEST, xdr_nlm_testargs, argp, xdr_nlm_testres, &res, TIMEOUT) != RPC_SUCCESS) {
+ return (NULL);
+ }
+ return (&res);
+}
+
+
+nlm_res *
+nlm_lock_1(argp, clnt)
+ struct nlm_lockargs *argp;
+ CLIENT *clnt;
+{
+ enum clnt_stat st;
+ static nlm_res res;
+
+ bzero((char *)&res, sizeof(res));
+ if (st = clnt_call(clnt, NLM_LOCK, xdr_nlm_lockargs, argp, xdr_nlm_res, &res, TIMEOUT) != RPC_SUCCESS) {
+ printf("clnt_call returns %d\n", st);
+ clnt_perror(clnt, "humbug");
+ return (NULL);
+ }
+ return (&res);
+}
+
+
+nlm_res *
+nlm_cancel_1(argp, clnt)
+ struct nlm_cancargs *argp;
+ CLIENT *clnt;
+{
+ static nlm_res res;
+
+ bzero((char *)&res, sizeof(res));
+ if (clnt_call(clnt, NLM_CANCEL, xdr_nlm_cancargs, argp, xdr_nlm_res, &res, TIMEOUT) != RPC_SUCCESS) {
+ return (NULL);
+ }
+ return (&res);
+}
+
+
+nlm_res *
+nlm_unlock_1(argp, clnt)
+ struct nlm_unlockargs *argp;
+ CLIENT *clnt;
+{
+ static nlm_res res;
+
+ bzero((char *)&res, sizeof(res));
+ if (clnt_call(clnt, NLM_UNLOCK, xdr_nlm_unlockargs, argp, xdr_nlm_res, &res, TIMEOUT) != RPC_SUCCESS) {
+ return (NULL);
+ }
+ return (&res);
+}
+
+
+nlm_res *
+nlm_granted_1(argp, clnt)
+ struct nlm_testargs *argp;
+ CLIENT *clnt;
+{
+ static nlm_res res;
+
+ bzero((char *)&res, sizeof(res));
+ if (clnt_call(clnt, NLM_GRANTED, xdr_nlm_testargs, argp, xdr_nlm_res, &res, TIMEOUT) != RPC_SUCCESS) {
+ return (NULL);
+ }
+ return (&res);
+}
+
+
+void *
+nlm_test_msg_1(argp, clnt)
+ struct nlm_testargs *argp;
+ CLIENT *clnt;
+{
+ static char res;
+
+ bzero((char *)&res, sizeof(res));
+ if (clnt_call(clnt, NLM_TEST_MSG, xdr_nlm_testargs, argp, xdr_void, &res, TIMEOUT) != RPC_SUCCESS) {
+ return (NULL);
+ }
+ return ((void *)&res);
+}
+
+
+void *
+nlm_lock_msg_1(argp, clnt)
+ struct nlm_lockargs *argp;
+ CLIENT *clnt;
+{
+ static char res;
+
+ bzero((char *)&res, sizeof(res));
+ if (clnt_call(clnt, NLM_LOCK_MSG, xdr_nlm_lockargs, argp, xdr_void, NULL, TIMEOUT) != RPC_SUCCESS) {
+ clnt_perror(clnt, "nlm_lock_msg_1");
+ return (NULL);
+ }
+ return ((void *)&res);
+}
+
+
+void *
+nlm_cancel_msg_1(argp, clnt)
+ struct nlm_cancargs *argp;
+ CLIENT *clnt;
+{
+ static char res;
+
+ bzero((char *)&res, sizeof(res));
+ if (clnt_call(clnt, NLM_CANCEL_MSG, xdr_nlm_cancargs, argp, xdr_void, &res, TIMEOUT) != RPC_SUCCESS) {
+ return (NULL);
+ }
+ return ((void *)&res);
+}
+
+
+void *
+nlm_unlock_msg_1(argp, clnt)
+ struct nlm_unlockargs *argp;
+ CLIENT *clnt;
+{
+ static char res;
+
+ bzero((char *)&res, sizeof(res));
+ if (clnt_call(clnt, NLM_UNLOCK_MSG, xdr_nlm_unlockargs, argp, xdr_void, &res, TIMEOUT) != RPC_SUCCESS) {
+ return (NULL);
+ }
+ return ((void *)&res);
+}
+
+
+void *
+nlm_granted_msg_1(argp, clnt)
+ struct nlm_testargs *argp;
+ CLIENT *clnt;
+{
+ static char res;
+
+ bzero((char *)&res, sizeof(res));
+ if (clnt_call(clnt, NLM_GRANTED_MSG, xdr_nlm_testargs, argp, xdr_void, &res, TIMEOUT) != RPC_SUCCESS) {
+ return (NULL);
+ }
+ return ((void *)&res);
+}
+
+
+void *
+nlm_test_res_1(argp, clnt)
+ nlm_testres *argp;
+ CLIENT *clnt;
+{
+ static char res;
+
+ bzero((char *)&res, sizeof(res));
+ if (clnt_call(clnt, NLM_TEST_RES, xdr_nlm_testres, argp, xdr_void, &res, TIMEOUT) != RPC_SUCCESS) {
+ return (NULL);
+ }
+ return ((void *)&res);
+}
+
+
+void *
+nlm_lock_res_1(argp, clnt)
+ nlm_res *argp;
+ CLIENT *clnt;
+{
+ static char res;
+
+ bzero((char *)&res, sizeof(res));
+ if (clnt_call(clnt, NLM_LOCK_RES, xdr_nlm_res, argp, xdr_void, &res, TIMEOUT) != RPC_SUCCESS) {
+ return (NULL);
+ }
+ return ((void *)&res);
+}
+
+
+void *
+nlm_cancel_res_1(argp, clnt)
+ nlm_res *argp;
+ CLIENT *clnt;
+{
+ static char res;
+
+ bzero((char *)&res, sizeof(res));
+ if (clnt_call(clnt, NLM_CANCEL_RES, xdr_nlm_res, argp, xdr_void, &res, TIMEOUT) != RPC_SUCCESS) {
+ return (NULL);
+ }
+ return ((void *)&res);
+}
+
+
+void *
+nlm_unlock_res_1(argp, clnt)
+ nlm_res *argp;
+ CLIENT *clnt;
+{
+ static char res;
+
+ bzero((char *)&res, sizeof(res));
+ if (clnt_call(clnt, NLM_UNLOCK_RES, xdr_nlm_res, argp, xdr_void, &res, TIMEOUT) != RPC_SUCCESS) {
+ return (NULL);
+ }
+ return ((void *)&res);
+}
+
+
+void *
+nlm_granted_res_1(argp, clnt)
+ nlm_res *argp;
+ CLIENT *clnt;
+{
+ static char res;
+
+ bzero((char *)&res, sizeof(res));
+ if (clnt_call(clnt, NLM_GRANTED_RES, xdr_nlm_res, argp, xdr_void, &res, TIMEOUT) != RPC_SUCCESS) {
+ return (NULL);
+ }
+ return ((void *)&res);
+}
+
+
+nlm_shareres *
+nlm_share_3(argp, clnt)
+ nlm_shareargs *argp;
+ CLIENT *clnt;
+{
+ static nlm_shareres res;
+
+ bzero((char *)&res, sizeof(res));
+ if (clnt_call(clnt, NLM_SHARE, xdr_nlm_shareargs, argp, xdr_nlm_shareres, &res, TIMEOUT) != RPC_SUCCESS) {
+ return (NULL);
+ }
+ return (&res);
+}
+
+
+nlm_shareres *
+nlm_unshare_3(argp, clnt)
+ nlm_shareargs *argp;
+ CLIENT *clnt;
+{
+ static nlm_shareres res;
+
+ bzero((char *)&res, sizeof(res));
+ if (clnt_call(clnt, NLM_UNSHARE, xdr_nlm_shareargs, argp, xdr_nlm_shareres, &res, TIMEOUT) != RPC_SUCCESS) {
+ return (NULL);
+ }
+ return (&res);
+}
+
+
+nlm_res *
+nlm_nm_lock_3(argp, clnt)
+ nlm_lockargs *argp;
+ CLIENT *clnt;
+{
+ static nlm_res res;
+
+ bzero((char *)&res, sizeof(res));
+ if (clnt_call(clnt, NLM_NM_LOCK, xdr_nlm_lockargs, argp, xdr_nlm_res, &res, TIMEOUT) != RPC_SUCCESS) {
+ return (NULL);
+ }
+ return (&res);
+}
+
+
+void *
+nlm_free_all_3(argp, clnt)
+ nlm_notify *argp;
+ CLIENT *clnt;
+{
+ static char res;
+
+ bzero((char *)&res, sizeof(res));
+ if (clnt_call(clnt, NLM_FREE_ALL, xdr_nlm_notify, argp, xdr_void, &res, TIMEOUT) != RPC_SUCCESS) {
+ return (NULL);
+ }
+ return ((void *)&res);
+}
+
+
+int main(int argc, char **argv)
+{
+ CLIENT *cli;
+ nlm_res res_block;
+ nlm_res *out;
+ nlm_lockargs arg;
+ struct timeval tim;
+
+ printf("Creating client for host %s\n", argv[1]);
+ cli = clnt_create(argv[1], NLM_PROG, NLM_VERS, "udp");
+ if (!cli)
+ {
+ printf("Failed to create client\n");
+ exit(1);
+ }
+
+
+ clnt_control(cli, CLGET_TIMEOUT, &tim);
+ printf("Default timeout was %d.%d\n", tim.tv_sec, tim.tv_usec);
+ tim.tv_usec = -1;
+ tim.tv_sec = -1;
+ clnt_control(cli, CLSET_TIMEOUT, &tim);
+ clnt_control(cli, CLGET_TIMEOUT, &tim);
+ printf("timeout now %d.%d\n", tim.tv_sec, tim.tv_usec);
+
+
+ arg.cookie.n_len = 4;
+ arg.cookie.n_bytes = "hello";
+ arg.block = 0;
+ arg.exclusive = 0;
+ arg.reclaim = 0;
+ arg.state = 0x1234;
+ arg.alock.caller_name = "localhost";
+ arg.alock.fh.n_len = 32;
+ arg.alock.fh.n_bytes = "\x04\x04\x02\x00\x01\x00\x00\x00\x0c\x00\x00\x00\xff\xff\xff\xd0\x16\x00\x00\x5b\x7c\xff\xff\xff\xec\x2f\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x19\x54\xef\xbf\xd7\x94";
+ arg.alock.oh.n_len = 8;
+ arg.alock.oh.n_bytes = "\x00\x00\x02\xff\xff\xff\xd3";
+ arg.alock.svid = 0x5678;
+ arg.alock.l_offset = 0;
+ arg.alock.l_len = 100;
+
+ res_block.stat.stat = nlm_granted;
+ res_block.cookie.n_bytes = "hello";
+ res_block.cookie.n_len = 5;
+
+#if 0
+ if (nlm_lock_res_1(&res_block, cli)) printf("Success!\n");
+ else printf("Fail\n");
+#else
+ if (out = nlm_lock_msg_1(&arg, cli))
+ {
+ printf("Success!\n");
+ printf("out->stat = %d", out->stat);
+ }
+ else
+ {
+ printf("Fail\n");
+ }
+#endif
+
+ return 0;
+}
diff --git a/usr.sbin/rpc.statd/Makefile b/usr.sbin/rpc.statd/Makefile
new file mode 100644
index 0000000..72acc8a
--- /dev/null
+++ b/usr.sbin/rpc.statd/Makefile
@@ -0,0 +1,27 @@
+# $Id: Makefile,v 1.5 1997/02/22 16:12:51 peter Exp $
+
+PROG = rpc.statd
+SRCS = file.c sm_inter_svc.c statd.c procs.c
+MAN8 = rpc.statd.8
+DPSRCS+= sm_inter.h
+
+DPADD= ${LIBRPCSVC}
+LDADD= -lrpcsvc
+
+CFLAGS+= -I.
+
+CLEANFILES= sm_inter_svc.c sm_inter.h
+
+RPCSRC= ${DESTDIR}/usr/include/rpcsvc/sm_inter.x
+RPCGEN= rpcgen -L -C
+
+sm_inter_svc.c: ${RPCSRC} sm_inter.h
+ ${RPCGEN} -m -o ${.TARGET} ${RPCSRC}
+
+sm_inter.h: ${RPCSRC}
+ ${RPCGEN} -h -o ${.TARGET} ${RPCSRC}
+
+test: test.c
+ cc -o test test.c -lrpcsvc
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/rpc.statd/file.c b/usr.sbin/rpc.statd/file.c
new file mode 100644
index 0000000..b8fc5cc
--- /dev/null
+++ b/usr.sbin/rpc.statd/file.c
@@ -0,0 +1,350 @@
+/*
+ * Copyright (c) 1995
+ * A.R. Gordon (andrew.gordon@net-tel.co.uk). All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed for the FreeBSD project
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY ANDREW GORDON AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/mman.h> /* For mmap() */
+#include <rpc/rpc.h>
+#include <syslog.h>
+
+#include "statd.h"
+
+FileLayout *status_info; /* Pointer to the mmap()ed status file */
+static int status_fd; /* File descriptor for the open file */
+static off_t status_file_len; /* Current on-disc length of file */
+
+/* sync_file --------------------------------------------------------------- */
+/*
+ Purpose: Packaged call of msync() to flush changes to mmap()ed file
+ Returns: Nothing. Errors to syslog.
+*/
+
+void sync_file(void)
+{
+ if (msync((void *)status_info, 0, 0) < 0)
+ {
+ syslog(LOG_ERR, "msync() failed: %s", strerror(errno));
+ }
+}
+
+/* find_host -------------------------------------------------------------- */
+/*
+ Purpose: Find the entry in the status file for a given host
+ Returns: Pointer to that entry in the mmap() region, or NULL.
+ Notes: Also creates entries if requested.
+ Failure to create also returns NULL.
+*/
+
+HostInfo *find_host(char *hostname, int create)
+{
+ HostInfo *hp;
+ HostInfo *spare_slot = NULL;
+ HostInfo *result = NULL;
+ int i;
+
+ for (i = 0, hp = status_info->hosts; i < status_info->noOfHosts; i++, hp++)
+ {
+ if (!strncasecmp(hostname, hp->hostname, SM_MAXSTRLEN))
+ {
+ result = hp;
+ break;
+ }
+ if (!spare_slot && !hp->monList && !hp->notifyReqd)
+ spare_slot = hp;
+ }
+
+ /* Return if entry found, or if not asked to create one. */
+ if (result || !create) return (result);
+
+ /* Now create an entry, using the spare slot if one was found or */
+ /* adding to the end of the list otherwise, extending file if reqd */
+ if (!spare_slot)
+ {
+ off_t desired_size;
+ spare_slot = &status_info->hosts[status_info->noOfHosts];
+ desired_size = ((char*)spare_slot - (char*)status_info) + sizeof(HostInfo);
+ if (desired_size > status_file_len)
+ {
+ /* Extend file by writing 1 byte of junk at the desired end pos */
+ lseek(status_fd, desired_size - 1, SEEK_SET);
+ i = write(status_fd, &i, 1);
+ if (i < 1)
+ {
+ syslog(LOG_ERR, "Unable to extend status file");
+ return (NULL);
+ }
+ status_file_len = desired_size;
+ }
+ status_info->noOfHosts++;
+ }
+
+ /* Initialise the spare slot that has been found/created */
+ /* Note that we do not msync(), since the caller is presumed to be */
+ /* about to modify the entry further */
+ memset(spare_slot, 0, sizeof(HostInfo));
+ strncpy(spare_slot->hostname, hostname, SM_MAXSTRLEN);
+ return (spare_slot);
+}
+
+/* init_file -------------------------------------------------------------- */
+/*
+ Purpose: Open file, create if necessary, initialise it.
+ Returns: Nothing - exits on error
+ Notes: Called before process becomes daemon, hence logs to
+ stderr rather than syslog.
+ Opens the file, then mmap()s it for ease of access.
+ Also performs initial clean-up of the file, zeroing
+ monitor list pointers, setting the notifyReqd flag in
+ all hosts that had a monitor list, and incrementing
+ the state number to the next even value.
+*/
+
+void init_file(char *filename)
+{
+ int new_file = FALSE;
+ char buf[HEADER_LEN];
+ int i;
+
+ /* try to open existing file - if not present, create one */
+ status_fd = open(filename, O_RDWR);
+ if ((status_fd < 0) && (errno == ENOENT))
+ {
+ status_fd = open(filename, O_RDWR | O_CREAT, 0644);
+ new_file = TRUE;
+ }
+ if (status_fd < 0)
+ errx(1, "unable to open status file %s", filename);
+
+ /* File now open. mmap() it, with a generous size to allow for */
+ /* later growth, where we will extend the file but not re-map it. */
+ status_info = (FileLayout *)
+ mmap(NULL, 0x10000000, PROT_READ | PROT_WRITE, MAP_SHARED, status_fd, 0);
+
+ if (status_info == (FileLayout *) MAP_FAILED)
+ warn("unable to mmap() status file");
+
+ status_file_len = lseek(status_fd, 0L, SEEK_END);
+
+ /* If the file was not newly created, validate the contents, and if */
+ /* defective, re-create from scratch. */
+ if (!new_file)
+ {
+ if ((status_file_len < HEADER_LEN) || (status_file_len
+ < (HEADER_LEN + sizeof(HostInfo) * status_info->noOfHosts)) )
+ {
+ warnx("status file is corrupt");
+ new_file = TRUE;
+ }
+ }
+
+ /* Initialisation of a new, empty file. */
+ if (new_file)
+ {
+ memset(buf, 0, sizeof(buf));
+ lseek(status_fd, 0L, SEEK_SET);
+ write(status_fd, buf, HEADER_LEN);
+ status_file_len = HEADER_LEN;
+ }
+ else
+ {
+ /* Clean-up of existing file - monitored hosts will have a pointer */
+ /* to a list of clients, which refers to memory in the previous */
+ /* incarnation of the program and so are meaningless now. These */
+ /* pointers are zeroed and the fact that the host was previously */
+ /* monitored is recorded by setting the notifyReqd flag, which will */
+ /* in due course cause a SM_NOTIFY to be sent. */
+ /* Note that if we crash twice in quick succession, some hosts may */
+ /* already have notifyReqd set, where we didn't manage to notify */
+ /* them before the second crash occurred. */
+ for (i = 0; i < status_info->noOfHosts; i++)
+ {
+ HostInfo *this_host = &status_info->hosts[i];
+
+ if (this_host->monList)
+ {
+ this_host->notifyReqd = TRUE;
+ this_host->monList = NULL;
+ }
+ }
+ /* Select the next higher even number for the state counter */
+ status_info->ourState = (status_info->ourState + 2) & 0xfffffffe;
+/*???????******/ status_info->ourState++;
+ }
+}
+
+/* xdr_stat_chge ----------------------------------------------------------- */
+/*
+ Purpose: XDR-encode structure of type stat_chge
+ Returns: TRUE if successful
+ Notes: This function is missing from librpcsvc, because the
+ sm_inter.x distributed by Sun omits the SM_NOTIFY
+ procedure used between co-operating statd's
+*/
+
+bool_t xdr_stat_chge(XDR *xdrs, stat_chge *objp)
+{
+ if (!xdr_string(xdrs, &objp->mon_name, SM_MAXSTRLEN))
+ {
+ return (FALSE);
+ }
+ if (!xdr_int(xdrs, &objp->state))
+ {
+ return (FALSE);
+ }
+ return (TRUE);
+}
+
+
+/* notify_one_host --------------------------------------------------------- */
+/*
+ Purpose: Perform SM_NOTIFY procedure at specified host
+ Returns: TRUE if success, FALSE if failed.
+*/
+
+static int notify_one_host(char *hostname)
+{
+ struct timeval timeout = { 20, 0 }; /* 20 secs timeout */
+ CLIENT *cli;
+ char dummy;
+ stat_chge arg;
+ char our_hostname[SM_MAXSTRLEN+1];
+
+ gethostname(our_hostname, sizeof(our_hostname));
+ our_hostname[SM_MAXSTRLEN] = '\0';
+ arg.mon_name = our_hostname;
+ arg.state = status_info->ourState;
+
+ if (debug) syslog (LOG_DEBUG, "Sending SM_NOTIFY to host %s from %s", hostname, our_hostname);
+
+ cli = clnt_create(hostname, SM_PROG, SM_VERS, "udp");
+ if (!cli)
+ {
+ syslog(LOG_ERR, "Failed to contact host %s%s", hostname,
+ clnt_spcreateerror(""));
+ return (FALSE);
+ }
+
+ if (clnt_call(cli, SM_NOTIFY, xdr_stat_chge, &arg, xdr_void, &dummy, timeout)
+ != RPC_SUCCESS)
+ {
+ syslog(LOG_ERR, "Failed to contact rpc.statd at host %s", hostname);
+ clnt_destroy(cli);
+ return (FALSE);
+ }
+
+ clnt_destroy(cli);
+ return (TRUE);
+}
+
+/* notify_hosts ------------------------------------------------------------ */
+/*
+ Purpose: Send SM_NOTIFY to all hosts marked as requiring it
+ Returns: Nothing, immediately - forks a process to do the work.
+ Notes: Does nothing if there are no monitored hosts.
+ Called after all the initialisation has been done -
+ logs to syslog.
+*/
+
+void notify_hosts(void)
+{
+ int i;
+ int attempts;
+ int work_to_do = FALSE;
+ HostInfo *hp;
+ pid_t pid;
+
+ /* First check if there is in fact any work to do. */
+ for (i = status_info->noOfHosts, hp = status_info->hosts; i ; i--, hp++)
+ {
+ if (hp->notifyReqd)
+ {
+ work_to_do = TRUE;
+ break;
+ }
+ }
+
+ if (!work_to_do) return; /* No work found */
+
+ pid = fork();
+ if (pid == -1)
+ {
+ syslog(LOG_ERR, "Unable to fork notify process - %s", strerror(errno));
+ return;
+ }
+ if (pid) return;
+
+ /* Here in the child process. We continue until all the hosts marked */
+ /* as requiring notification have been duly notified. */
+ /* If one of the initial attempts fails, we sleep for a while and */
+ /* have another go. This is necessary because when we have crashed, */
+ /* (eg. a power outage) it is quite possible that we won't be able to */
+ /* contact all monitored hosts immediately on restart, either because */
+ /* they crashed too and take longer to come up (in which case the */
+ /* notification isn't really required), or more importantly if some */
+ /* router etc. needed to reach the monitored host has not come back */
+ /* up yet. In this case, we will be a bit late in re-establishing */
+ /* locks (after the grace period) but that is the best we can do. */
+ /* We try 10 times at 5 sec intervals, 10 more times at 1 minute */
+ /* intervals, then 24 more times at hourly intervals, finally */
+ /* giving up altogether if the host hasn't come back to life after */
+ /* 24 hours. */
+
+ for (attempts = 0; attempts < 44; attempts++)
+ {
+ work_to_do = FALSE; /* Unless anything fails */
+ for (i = status_info->noOfHosts, hp = status_info->hosts; i ; i--, hp++)
+ {
+ if (hp->notifyReqd)
+ {
+ if (notify_one_host(hp->hostname))
+ {
+ hp->notifyReqd = FALSE;
+ sync_file();
+ }
+ else work_to_do = TRUE;
+ }
+ }
+ if (!work_to_do) break;
+ if (attempts < 10) sleep(5);
+ else if (attempts < 20) sleep(60);
+ else sleep(60*60);
+ }
+ exit(0);
+}
+
+
diff --git a/usr.sbin/rpc.statd/procs.c b/usr.sbin/rpc.statd/procs.c
new file mode 100644
index 0000000..a33abd40
--- /dev/null
+++ b/usr.sbin/rpc.statd/procs.c
@@ -0,0 +1,355 @@
+/*
+ * Copyright (c) 1995
+ * A.R. Gordon (andrew.gordon@net-tel.co.uk). All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed for the FreeBSD project
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY ANDREW GORDON AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <rpc/rpc.h>
+#include <syslog.h>
+#include <netdb.h> /* for gethostbyname() */
+
+#include "statd.h"
+
+/* sm_stat_1 --------------------------------------------------------------- */
+/*
+ Purpose: RPC call to enquire if a host can be monitored
+ Returns: TRUE for any hostname that can be looked up to give
+ an address.
+*/
+
+struct sm_stat_res *sm_stat_1_svc(sm_name *arg, struct svc_req *req)
+{
+ static sm_stat_res res;
+
+ if (debug) syslog(LOG_DEBUG, "stat called for host %s", arg->mon_name);
+
+ if (gethostbyname(arg->mon_name)) res.res_stat = stat_succ;
+ else
+ {
+ syslog(LOG_ERR, "invalid hostname to sm_stat: %s", arg->mon_name);
+ res.res_stat = stat_fail;
+ }
+
+ res.state = status_info->ourState;
+ return (&res);
+}
+
+/* sm_mon_1 ---------------------------------------------------------------- */
+/*
+ Purpose: RPC procedure to establish a monitor request
+ Returns: Success, unless lack of resources prevents
+ the necessary structures from being set up
+ to record the request, or if the hostname is not
+ valid (as judged by gethostbyname())
+*/
+
+struct sm_stat_res *sm_mon_1_svc(mon *arg, struct svc_req *req)
+{
+ static sm_stat_res res;
+ HostInfo *hp;
+ MonList *lp;
+
+ if (debug)
+ {
+ syslog(LOG_DEBUG, "monitor request for host %s", arg->mon_id.mon_name);
+ syslog(LOG_DEBUG, "recall host: %s prog: %d ver: %d proc: %d",
+ arg->mon_id.mon_name, arg->mon_id.my_id.my_name,
+ arg->mon_id.my_id.my_prog, arg->mon_id.my_id.my_vers,
+ arg->mon_id.my_id.my_proc);
+ }
+
+ res.res_stat = stat_fail; /* Assume fail until set otherwise */
+ res.state = status_info->ourState;
+
+ /* Find existing host entry, or create one if not found */
+ /* If find_host() fails, it will have logged the error already. */
+ if (!gethostbyname(arg->mon_id.mon_name))
+ {
+ syslog(LOG_ERR, "Invalid hostname to sm_mon: %s", arg->mon_id.mon_name);
+ }
+ else if ((hp = find_host(arg->mon_id.mon_name, TRUE)))
+ {
+ lp = (MonList *)malloc(sizeof(MonList));
+ if (!lp)
+ {
+ syslog(LOG_ERR, "Out of memory");
+ }
+ else
+ {
+ strncpy(lp->notifyHost, arg->mon_id.my_id.my_name, SM_MAXSTRLEN);
+ lp->notifyProg = arg->mon_id.my_id.my_prog;
+ lp->notifyVers = arg->mon_id.my_id.my_vers;
+ lp->notifyProc = arg->mon_id.my_id.my_proc;
+ memcpy(lp->notifyData, arg->priv, sizeof(lp->notifyData));
+
+ lp->next = hp->monList;
+ hp->monList = lp;
+ sync_file();
+
+ res.res_stat = stat_succ; /* Report success */
+ }
+ }
+
+ return (&res);
+}
+
+/* do_unmon ---------------------------------------------------------------- */
+/*
+ Purpose: Remove a monitor request from a host
+ Returns: TRUE if found, FALSE if not found.
+ Notes: Common code from sm_unmon_1_svc and sm_unmon_all_1_svc
+ In the unlikely event of more than one identical monitor
+ request, all are removed.
+*/
+
+static int do_unmon(HostInfo *hp, my_id *idp)
+{
+ MonList *lp, *next;
+ MonList *last = NULL;
+ int result = FALSE;
+
+ lp = hp->monList;
+ while (lp)
+ {
+ if (!strncasecmp(idp->my_name, lp->notifyHost, SM_MAXSTRLEN)
+ && (idp->my_prog == lp->notifyProg) && (idp->my_proc == lp->notifyProc)
+ && (idp->my_vers == lp->notifyVers))
+ {
+ /* found one. Unhook from chain and free. */
+ next = lp->next;
+ if (last) last->next = next;
+ else hp->monList = next;
+ free(lp);
+ lp = next;
+ result = TRUE;
+ }
+ else
+ {
+ last = lp;
+ lp = lp->next;
+ }
+ }
+ return (result);
+}
+
+/* sm_unmon_1 -------------------------------------------------------------- */
+/*
+ Purpose: RPC procedure to release a monitor request.
+ Returns: Local machine's status number
+ Notes: The supplied mon_id should match the value passed in an
+ earlier call to sm_mon_1
+*/
+
+struct sm_stat *sm_unmon_1_svc(mon_id *arg, struct svc_req *req)
+{
+ static sm_stat res;
+ HostInfo *hp;
+
+ if (debug)
+ {
+ syslog(LOG_DEBUG, "un-monitor request for host %s", arg->mon_name);
+ syslog(LOG_DEBUG, "recall host: %s prog: %d ver: %d proc: %d",
+ arg->mon_name, arg->my_id.my_name, arg->my_id.my_prog,
+ arg->my_id.my_vers, arg->my_id.my_proc);
+ }
+
+ if ((hp = find_host(arg->mon_name, FALSE)))
+ {
+ if (do_unmon(hp, &arg->my_id)) sync_file();
+ else
+ {
+ syslog(LOG_ERR, "unmon request from %s, no matching monitor",
+ arg->my_id.my_name);
+ }
+ }
+ else syslog(LOG_ERR, "unmon request from %s for unknown host %s",
+ arg->my_id.my_name, arg->mon_name);
+
+ res.state = status_info->ourState;
+
+ return (&res);
+}
+
+/* sm_unmon_all_1 ---------------------------------------------------------- */
+/*
+ Purpose: RPC procedure to release monitor requests.
+ Returns: Local machine's status number
+ Notes: Releases all monitor requests (if any) from the specified
+ host and program number.
+*/
+
+struct sm_stat *sm_unmon_all_1_svc(my_id *arg, struct svc_req *req)
+{
+ static sm_stat res;
+ HostInfo *hp;
+ int i;
+
+ if (debug)
+ {
+ syslog(LOG_DEBUG, "unmon_all for host: %s prog: %d ver: %d proc: %d",
+ arg->my_name, arg->my_prog, arg->my_vers, arg->my_proc);
+ }
+
+ for (i = status_info->noOfHosts, hp = status_info->hosts; i; i--, hp++)
+ {
+ do_unmon(hp, arg);
+ }
+ sync_file();
+
+ res.state = status_info->ourState;
+
+ return (&res);
+}
+
+/* sm_simu_crash_1 --------------------------------------------------------- */
+/*
+ Purpose: RPC procedure to simulate a crash
+ Returns: Nothing
+ Notes: Standardised mechanism for debug purposes
+ The specification says that we should drop all of our
+ status information (apart from the list of monitored hosts
+ on disc). However, this would confuse the rpc.lockd
+ which would be unaware that all of its monitor requests
+ had been silently junked. Hence we in fact retain all
+ current requests and simply increment the status counter
+ and inform all hosts on the monitor list.
+*/
+
+void *sm_simu_crash_1_svc(void *v, struct svc_req *req)
+{
+ static char dummy;
+ int work_to_do;
+ HostInfo *hp;
+ int i;
+
+ if (debug) syslog(LOG_DEBUG, "simu_crash called!!");
+
+ /* Simulate crash by setting notify-required flag on all monitored */
+ /* hosts, and incrementing our status number. notify_hosts() is */
+ /* then called to fork a process to do the notifications. */
+
+ for (i = status_info->noOfHosts, hp = status_info->hosts; i ; i--, hp++)
+ {
+ if (hp->monList)
+ {
+ work_to_do = TRUE;
+ hp->notifyReqd = TRUE;
+ }
+ }
+ status_info->ourState += 2; /* always even numbers if not crashed */
+
+ if (work_to_do) notify_hosts();
+
+ return (&dummy);
+}
+
+/* sm_notify_1 ------------------------------------------------------------- */
+/*
+ Purpose: RPC procedure notifying local statd of the crash of another
+ Returns: Nothing
+ Notes: There is danger of deadlock, since it is quite likely that
+ the client procedure that we call will in turn call us
+ to remove or adjust the monitor request.
+ We therefore fork() a process to do the notifications.
+ Note that the main HostInfo structure is in a mmap()
+ region and so will be shared with the child, but the
+ monList pointed to by the HostInfo is in normal memory.
+ Hence if we read the monList before forking, we are
+ protected from the parent servicing other requests
+ that modify the list.
+*/
+
+void *sm_notify_1_svc(stat_chge *arg, struct svc_req *req)
+{
+ struct timeval timeout = { 20, 0 }; /* 20 secs timeout */
+ CLIENT *cli;
+ static char dummy;
+ status tx_arg; /* arg sent to callback procedure */
+ MonList *lp;
+ HostInfo *hp;
+ pid_t pid;
+
+ if (debug) syslog(LOG_DEBUG, "notify from host %s, new state %d",
+ arg->mon_name, arg->state);
+
+ hp = find_host(arg->mon_name, FALSE);
+ if (!hp)
+ {
+ /* Never heard of this host - why is it notifying us? */
+ syslog(LOG_ERR, "Unsolicited notification from host %s", arg->mon_name);
+ return;
+ }
+ lp = hp->monList;
+ if (!lp) return (FALSE); /* We know this host, but have no */
+ /* outstanding requests. */
+ pid = fork();
+ if (pid == -1)
+ {
+ syslog(LOG_ERR, "Unable to fork notify process - %s", strerror(errno));
+ return;
+ }
+ if (pid) return (&dummy); /* Parent returns */
+
+ while (lp)
+ {
+ tx_arg.mon_name = arg->mon_name;
+ tx_arg.state = arg->state;
+ memcpy(tx_arg.priv, lp->notifyData, sizeof(tx_arg.priv));
+ cli = clnt_create(lp->notifyHost, lp->notifyProg, lp->notifyVers, "udp");
+ if (!cli)
+ {
+ syslog(LOG_ERR, "Failed to contact host %s%s", lp->notifyHost,
+ clnt_spcreateerror(""));
+ }
+ else
+ {
+ if (clnt_call(cli, lp->notifyProc, xdr_status, &tx_arg, xdr_void, &dummy,
+ timeout) != RPC_SUCCESS)
+ {
+ syslog(LOG_ERR, "Failed to call rpc.statd client at host %s",
+ lp->notifyHost);
+ }
+ clnt_destroy(cli);
+ }
+ lp = lp->next;
+ }
+
+ exit (0); /* Child quits */
+}
diff --git a/usr.sbin/rpc.statd/rpc.statd.8 b/usr.sbin/rpc.statd/rpc.statd.8
new file mode 100644
index 0000000..9226b4f
--- /dev/null
+++ b/usr.sbin/rpc.statd/rpc.statd.8
@@ -0,0 +1,104 @@
+.\" -*- nroff -*-
+.\"
+.\" Copyright (c) 1995 A.R.Gordon, andrew.gordon@net-tel.co.uk
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\"
+.Dd September 19, 1995
+.Dt RPC.STATD 8
+.Os
+.Sh NAME
+.Nm rpc.statd
+.Nd host status monitoring daemon
+.Sh SYNOPSIS
+.Nm rpc.statd
+.Op Fl d
+.Sh DESCRIPTION
+.Nm Rpc.statd
+is a daemon which co-operates with rpc.statd daemons on other hosts to provide
+a status monitoring service. The daemon accepts requests from
+programs running on the local host (typically,
+.Xr rpc.lockd 8 ,
+the NFS file locking daemon) to monitor the status of specified
+hosts. If a monitored host crashes and restarts, the remote daemon will
+notify the local daemon, which in turn will notify the local program(s)
+which requested the monitoring service. Conversely, if this host crashes
+and re-starts, when the
+.Nm
+re-starts, it will notify all of the hosts which were being monitored
+at the time of the crash.
+.Pp
+The following option is available:
+.Bl -tag -width indent
+.It Fl d
+Cause debugging information to be written to syslog, recording
+all RPC transactions to the daemon. These messages are logged with level
+LOG_DEBUG and facility LOG_DAEMON. Error conditions are logged irrespective
+of this option, using level LOG_ERR.
+.El
+.Pp
+The
+.Nm
+daemon must NOT be invoked by
+.Xr inetd 8
+because the protocol assumes that the daemon will run from system start time.
+Instead, it should be run from
+.Xr rc 8
+after the network has been started.
+.Sh FILES
+.Bl -tag -width /usr/include/rpcsvc/sm_inter.x -compact
+.It Pa /var/db/statd.status
+non-volatile record of currently monitored hosts.
+.It Pa /usr/include/rpcsvc/sm_inter.x
+RPC protocol specification used by local applications to register monitoring requests.
+.El
+.Sh SEE ALSO
+.Xr syslog 3 ,
+.Xr rc 8 ,
+.Xr rpc.lockd 8
+.Sh BUGS
+There is no means for the daemon to tell when a monitored host has
+disappeared permanently (eg. catastrophic hardware failure), as opposed
+to transient failure of the host or an intermediate router. At present,
+it will re-try notification attempts at frequent intervals for 10 minutes,
+then hourly, and finally gives up after 24 hours.
+
+The protocol requires that symmetric monitor requests are made to both
+the local and remote daemon in order to establish a monitored relationship.
+This is convenient for the NFS locking protocol, but probably reduces the
+usefulness of the monitoring system for other applications.
+
+The current implementation uses more than 1Kbyte per monitored host in
+the status file (and also in VM). This may be inefficient for NFS servers
+with large numbers of clients.
+.Sh STANDARDS
+The implementation is based on the specification in X/Open CAE Specification
+C218, "Protocols for X/Open PC Interworking: XNFS, Issue 4", ISBN 1 872630 66 9
diff --git a/usr.sbin/rpc.statd/statd.c b/usr.sbin/rpc.statd/statd.c
new file mode 100644
index 0000000..0c08892
--- /dev/null
+++ b/usr.sbin/rpc.statd/statd.c
@@ -0,0 +1,140 @@
+/*
+ * Copyright (c) 1995
+ * A.R. Gordon (andrew.gordon@net-tel.co.uk). All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed for the FreeBSD project
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY ANDREW GORDON AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+/* main() function for status monitor daemon. Some of the code in this */
+/* file was generated by running rpcgen /usr/include/rpcsvc/sm_inter.x */
+/* The actual program logic is in the file procs.c */
+
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <rpc/rpc.h>
+#include <rpc/pmap_clnt.h>
+#include <syslog.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <signal.h>
+#include "statd.h"
+
+int debug = 0; /* Controls syslog() calls for debug messages */
+
+extern void sm_prog_1(struct svc_req *rqstp, SVCXPRT *transp);
+static void handle_sigchld();
+static void usage __P((void));
+
+int
+main(int argc, char **argv)
+{
+ SVCXPRT *transp;
+ struct sigaction sa;
+
+ if (argc > 1)
+ {
+ if (strcmp(argv[1], "-d"))
+ usage();
+ debug = 1;
+ }
+
+ (void)pmap_unset(SM_PROG, SM_VERS);
+
+ transp = svcudp_create(RPC_ANYSOCK);
+ if (transp == NULL)
+ errx(1, "cannot create udp service");
+ if (!svc_register(transp, SM_PROG, SM_VERS, sm_prog_1, IPPROTO_UDP))
+ errx(1, "unable to register (SM_PROG, SM_VERS, udp)");
+
+ transp = svctcp_create(RPC_ANYSOCK, 0, 0);
+ if (transp == NULL)
+ errx(1, "cannot create tcp service");
+ if (!svc_register(transp, SM_PROG, SM_VERS, sm_prog_1, IPPROTO_TCP))
+ errx(1, "unable to register (SM_PROG, SM_VERS, tcp)");
+ init_file("/var/db/statd.status");
+
+ /* Note that it is NOT sensible to run this program from inetd - the */
+ /* protocol assumes that it will run immediately at boot time. */
+ daemon(0, 0);
+ openlog("rpc.statd", 0, LOG_DAEMON);
+ if (debug) syslog(LOG_INFO, "Starting - debug enabled");
+ else syslog(LOG_INFO, "Starting");
+
+ /* Install signal handler to collect exit status of child processes */
+ sa.sa_handler = handle_sigchld;
+ sigemptyset(&sa.sa_mask);
+ sigaddset(&sa.sa_mask, SIGCHLD);
+ sa.sa_flags = SA_RESTART;
+ sigaction(SIGCHLD, &sa, NULL);
+
+ /* Initialisation now complete - start operating */
+ notify_hosts(); /* Forks a process (if necessary) to do the */
+ /* SM_NOTIFY calls, which may be slow. */
+
+ svc_run(); /* Should never return */
+ exit(1);
+}
+
+static void
+usage()
+{
+ fprintf(stderr, "usage: rpc.statd [-d]\n");
+ exit(1);
+}
+
+/* handle_sigchld ---------------------------------------------------------- */
+/*
+ Purpose: Catch SIGCHLD and collect process status
+ Retruns: Nothing.
+ Notes: No special action required, other than to collect the
+ process status and hence allow the child to die:
+ we only use child processes for asynchronous transmission
+ of SM_NOTIFY to other systems, so it is normal for the
+ children to exit when they have done their work.
+*/
+
+static void handle_sigchld(int sig, int code, struct sigcontext *scp)
+{
+ int pid, status;
+ pid = wait4(-1, &status, WNOHANG, (struct rusage*)0);
+ if (!pid) syslog(LOG_ERR, "Phantom SIGCHLD??");
+ else if (status == 0)
+ {
+ if (debug) syslog(LOG_DEBUG, "Child %d exited OK", pid);
+ }
+ else syslog(LOG_ERR, "Child %d failed with status %d", pid,
+ WEXITSTATUS(status));
+}
+
diff --git a/usr.sbin/rpc.statd/statd.h b/usr.sbin/rpc.statd/statd.h
new file mode 100644
index 0000000..7b4e659
--- /dev/null
+++ b/usr.sbin/rpc.statd/statd.h
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 1995
+ * A.R. Gordon (andrew.gordon@net-tel.co.uk). All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed for the FreeBSD project
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY ANDREW GORDON AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+
+
+#include "sm_inter.h"
+
+/* These pieces are missing from the distributed sm_inter.x, which */
+/* omits the SM_NOTIFY procedure used between cooperating rpc.statd's */
+
+#define SM_NOTIFY ((u_long)6)
+extern void *sm_notify_1();
+
+struct stat_chge
+{
+ char *mon_name;
+ int state;
+};
+typedef struct stat_chge stat_chge;
+bool_t xdr_stat_chge();
+
+/* ------------------------------------------------------------------------- */
+/*
+ Data structures for recording monitored hosts
+
+ The information held by the status monitor comprises a list of hosts
+ that we have been asked to monitor, and, associated with each monitored
+ host, one or more clients to be called back if the monitored host crashes.
+
+ The list of monitored hosts must be retained over a crash, so that upon
+ re-boot we can call the SM_NOTIFY procedure in all those hosts so as to
+ cause them to start recovery processing. On the other hand, the client
+ call-backs are not required to be preserved: they are assumed (in the
+ protocol design) to be local processes which will have crashed when
+ we did, and so are discarded on restart.
+
+ We handle this by keeping the list of monitored hosts in a file
+ (/var/statd.state) which is mmap()ed and whose format is described
+ by the typedef FileLayout. The lists of client callbacks are chained
+ off this structure, but are held in normal memory and so will be
+ lost after a re-boot. Hence the actual values of MonList * pointers
+ in the copy on disc have no significance, but their NULL/non-NULL
+ status indicates whether this host is actually being monitored or if it
+ is an empty slot in the file.
+*/
+
+typedef struct MonList_s
+{
+ struct MonList_s *next; /* Next in list or NULL */
+ char notifyHost[SM_MAXSTRLEN + 1]; /* Host to notify */
+ int notifyProg; /* RPC program number to call */
+ int notifyVers; /* version number */
+ int notifyProc; /* procedure number */
+ unsigned char notifyData[16]; /* Opaque data from caller */
+} MonList;
+
+typedef struct
+{
+ char hostname[SM_MAXSTRLEN + 1]; /* Name of monitored host */
+ int notifyReqd; /* TRUE if we've crashed and not yet */
+ /* informed the monitored host */
+ MonList *monList; /* List of clients to inform if we */
+ /* hear that the monitored host has */
+ /* crashed, NULL if no longer monitored */
+} HostInfo;
+
+
+/* Overall file layout. */
+
+typedef struct
+{
+ int ourState; /* State number as defined in statd protocol */
+ int noOfHosts; /* Number of elements in hosts[] */
+ char reserved[248]; /* Reserved for future use */
+ HostInfo hosts[1]; /* vector of monitored hosts */
+} FileLayout;
+
+#define HEADER_LEN (sizeof(FileLayout) - sizeof(HostInfo))
+
+/* ------------------------------------------------------------------------- */
+
+/* Global variables */
+
+extern FileLayout *status_info; /* The mmap()ed status file */
+
+extern int debug; /* =1 to enable diagnostics to syslog */
+
+/* Function prototypes */
+
+extern HostInfo *find_host(char * /*hostname*/, int /*create*/);
+extern void init_file(char * /*filename*/);
+extern void notify_hosts(void);
+extern void sync_file(void);
diff --git a/usr.sbin/rpc.statd/test.c b/usr.sbin/rpc.statd/test.c
new file mode 100644
index 0000000..6a6a2ff
--- /dev/null
+++ b/usr.sbin/rpc.statd/test.c
@@ -0,0 +1,144 @@
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#include <stdio.h>
+#include <rpc/rpc.h>
+#include <rpcsvc/sm_inter.h>
+
+
+/* Default timeout can be changed using clnt_control() */
+static struct timeval TIMEOUT = { 25, 0 };
+
+struct sm_stat_res *
+sm_stat_1(argp, clnt)
+ struct sm_name *argp;
+ CLIENT *clnt;
+{
+ static struct sm_stat_res res;
+
+ bzero((char *)&res, sizeof(res));
+ if (clnt_call(clnt, SM_STAT, xdr_sm_name, argp, xdr_sm_stat_res, &res, TIMEOUT) != RPC_SUCCESS) {
+ return (NULL);
+ }
+ return (&res);
+}
+
+
+struct sm_stat_res *
+sm_mon_1(argp, clnt)
+ struct mon *argp;
+ CLIENT *clnt;
+{
+ static struct sm_stat_res res;
+
+ bzero((char *)&res, sizeof(res));
+ if (clnt_call(clnt, SM_MON, xdr_mon, argp, xdr_sm_stat_res, &res, TIMEOUT) != RPC_SUCCESS) {
+ return (NULL);
+ }
+ return (&res);
+}
+
+
+struct sm_stat *
+sm_unmon_1(argp, clnt)
+ struct mon_id *argp;
+ CLIENT *clnt;
+{
+ static struct sm_stat res;
+
+ bzero((char *)&res, sizeof(res));
+ if (clnt_call(clnt, SM_UNMON, xdr_mon_id, argp, xdr_sm_stat, &res, TIMEOUT) != RPC_SUCCESS) {
+ return (NULL);
+ }
+ return (&res);
+}
+
+
+struct sm_stat *
+sm_unmon_all_1(argp, clnt)
+ struct my_id *argp;
+ CLIENT *clnt;
+{
+ static struct sm_stat res;
+
+ bzero((char *)&res, sizeof(res));
+ if (clnt_call(clnt, SM_UNMON_ALL, xdr_my_id, argp, xdr_sm_stat, &res, TIMEOUT) != RPC_SUCCESS) {
+ return (NULL);
+ }
+ return (&res);
+}
+
+
+void *
+sm_simu_crash_1(argp, clnt)
+ void *argp;
+ CLIENT *clnt;
+{
+ static char res;
+
+ bzero((char *)&res, sizeof(res));
+ if (clnt_call(clnt, SM_SIMU_CRASH, xdr_void, argp, xdr_void, &res, TIMEOUT) != RPC_SUCCESS) {
+ return (NULL);
+ }
+ return ((void *)&res);
+}
+
+
+int main(int argc, char **argv)
+{
+ CLIENT *cli;
+ char dummy;
+ void *out;
+ struct mon mon;
+
+ if (argc < 2)
+ {
+ fprintf(stderr, "usage: test <hostname> | crash\n");
+ fprintf(stderr, "always talks to statd at localhost\n");
+ exit(1);
+ }
+
+ printf("Creating client for localhost\n" );
+ cli = clnt_create("localhost", SM_PROG, SM_VERS, "udp");
+ if (!cli)
+ {
+ printf("Failed to create client\n");
+ exit(1);
+ }
+
+ mon.mon_id.mon_name = argv[1];
+ mon.mon_id.my_id.my_name = argv[1];
+ mon.mon_id.my_id.my_prog = SM_PROG;
+ mon.mon_id.my_id.my_vers = SM_VERS;
+ mon.mon_id.my_id.my_proc = 1; /* have it call sm_stat() !!! */
+
+ if (strcmp(argv[1], "crash"))
+ {
+ /* Hostname given */
+ struct sm_stat_res *res;
+ if (res = sm_mon_1(&mon, cli))
+ {
+ printf("Success!\n");
+ }
+ else
+ {
+ printf("Fail\n");
+ }
+ }
+ else
+ {
+ if (out = sm_simu_crash_1(&dummy, cli))
+ {
+ printf("Success!\n");
+ }
+ else
+ {
+ printf("Fail\n");
+ }
+ }
+
+ return 0;
+}
diff --git a/usr.sbin/rpc.yppasswdd/Makefile b/usr.sbin/rpc.yppasswdd/Makefile
new file mode 100644
index 0000000..60cb1e9
--- /dev/null
+++ b/usr.sbin/rpc.yppasswdd/Makefile
@@ -0,0 +1,65 @@
+# $Id: Makefile,v 1.2 1997/07/28 18:31:11 wpaul Exp $
+
+PROG= rpc.yppasswdd
+SRCS= pw_copy.c pw_util.c util.c yppasswd_svc.c yp_error.c ypxfr_misc.c \
+ yp_dblookup.c yp_dbwrite.c yp_access.c yppasswd_private_xdr.c \
+ yppasswd_private_svc.c yp_clnt.c yppasswdd_server.c yppasswdd_main.c
+
+RPCDIR= ${DESTDIR}/usr/include/rpcsvc
+
+.PATH: ${.CURDIR}/../../usr.sbin/ypserv ${.CURDIR}/../../usr.bin/chpass \
+ ${.CURDIR}/../../libexec/ypxfr ${RPCDIR}
+
+MAN8= rpc.yppasswdd.8
+
+CFLAGS+= -I${.CURDIR}/../../usr.sbin/vipw -I${.CURDIR}/../../usr.sbin/ypserv \
+ -I${.CURDIR}/../../libexec/ypxfr -I${.CURDIR}/../../usr.bin/chpass \
+ -I${.CURDIR} -I.
+
+DPADD= ${LIBRPCSVC} ${LIBCRYPT}
+LDADD= -lrpcsvc -lcrypt
+
+CLEANFILES= yppasswd_svc.c yppasswd.h \
+ yppasswd_private_xdr.c yppasswd_private.h \
+ yppasswd_private_svc.c yp.h yp_clnt.c
+
+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 yppasswd.h
+ 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 yp.h
+ 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 yppasswd_private.h
+ rm -f ${.TARGET}
+ ${RPCGEN} -c -o ${.TARGET} ${.CURDIR}/yppasswd_private.x
+
+yppasswd_private_svc.c: yppasswd_private.x yppasswd_private.h
+ rm -f ${.TARGET}
+ ${RPCGEN} -m ${.CURDIR}/yppasswd_private.x | \
+ sed s/"static int _rpcsvcstate = _IDLE"/"extern int _rpcsvcstate"/g > ${.TARGET}
+
+afterinstall:
+ ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \
+ ${.CURDIR}/yppwupdate \
+ ${DESTDIR}/usr/libexec/yppwupdate
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/rpc.yppasswdd/pw_copy.c b/usr.sbin/rpc.yppasswdd/pw_copy.c
new file mode 100644
index 0000000..653f5e8
--- /dev/null
+++ b/usr.sbin/rpc.yppasswdd/pw_copy.c
@@ -0,0 +1,143 @@
+/*-
+ * Copyright (c) 1990, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)pw_copy.c 8.4 (Berkeley) 4/2/94";
+#endif
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+/*
+ * This module is used to copy the master password file, replacing a single
+ * record, by chpass(1) and passwd(1).
+ */
+
+#include <err.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <string.h>
+
+#include <pw_util.h>
+#include "yppasswdd_extern.h"
+
+int
+pw_copy(ffd, tfd, pw)
+ int ffd, tfd;
+ struct passwd *pw;
+{
+ FILE *from, *to;
+ int done;
+ char *p, buf[8192];
+ char uidstr[20];
+ char gidstr[20];
+ char chgstr[20];
+ char expstr[20];
+
+ snprintf(uidstr, sizeof(uidstr), "%d", pw->pw_uid);
+ snprintf(gidstr, sizeof(gidstr), "%d", pw->pw_gid);
+ snprintf(chgstr, sizeof(chgstr), "%ld", pw->pw_change);
+ snprintf(expstr, sizeof(expstr), "%ld", pw->pw_expire);
+
+ if (!(from = fdopen(ffd, "r"))) {
+ pw_error(passfile, 1, 1);
+ return(-1);
+ }
+ if (!(to = fdopen(tfd, "w"))) {
+ pw_error(tempname, 1, 1);
+ return(-1);
+ }
+ for (done = 0; fgets(buf, sizeof(buf), from);) {
+ if (!strchr(buf, '\n')) {
+ yp_error("%s: line too long", passfile);
+ pw_error(NULL, 0, 1);
+ goto err;
+ }
+ if (done) {
+ (void)fprintf(to, "%s", buf);
+ if (ferror(to))
+ goto err;
+ continue;
+ }
+ if (!(p = strchr(buf, ':'))) {
+ yp_error("%s: corrupted entry", passfile);
+ pw_error(NULL, 0, 1);
+ goto err;
+ }
+ *p = '\0';
+ if (strcmp(buf, pw->pw_name)) {
+ *p = ':';
+ (void)fprintf(to, "%s", buf);
+ if (ferror(to))
+ goto err;
+ continue;
+ }
+ (void)fprintf(to, "%s:%s:%s:%s:%s:%s:%s:%s:%s:%s\n",
+ pw->pw_name, pw->pw_passwd,
+ pw->pw_fields & _PWF_UID ? uidstr : "",
+ pw->pw_fields & _PWF_GID ? gidstr : "",
+ pw->pw_class,
+ pw->pw_fields & _PWF_CHANGE ? chgstr : "",
+ pw->pw_fields & _PWF_EXPIRE ? expstr : "",
+ pw->pw_gecos, pw->pw_dir, pw->pw_shell);
+ done = 1;
+ if (ferror(to))
+ goto err;
+ }
+ if (!done) {
+ if (allow_additions) {
+ (void)fprintf(to, "%s:%s:%s:%s:%s:%s:%s:%s:%s:%s\n",
+ pw->pw_name, pw->pw_passwd,
+ pw->pw_fields & _PWF_UID ? uidstr : "",
+ pw->pw_fields & _PWF_GID ? gidstr : "",
+ pw->pw_class,
+ pw->pw_fields & _PWF_CHANGE ? chgstr : "",
+ pw->pw_fields & _PWF_EXPIRE ? expstr : "",
+ pw->pw_gecos, pw->pw_dir, pw->pw_shell);
+ } else {
+ yp_error("user \"%s\" not found in %s -- \
+NIS maps and password file possibly out of sync", pw->pw_name, passfile);
+ goto err;
+ }
+ }
+ if (ferror(to)) {
+err: pw_error(NULL, 1, 1);
+ (void)fclose(to);
+ (void)fclose(from);
+ return(-1);
+ }
+ (void)fclose(to);
+ (void)fclose(from);
+ return(0);
+}
diff --git a/usr.sbin/rpc.yppasswdd/pw_util.c b/usr.sbin/rpc.yppasswdd/pw_util.c
new file mode 100644
index 0000000..4b5de7c
--- /dev/null
+++ b/usr.sbin/rpc.yppasswdd/pw_util.c
@@ -0,0 +1,184 @@
+/*-
+ * Copyright (c) 1990, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)pw_util.c 8.3 (Berkeley) 4/2/94";
+#endif
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+/*
+ * This file is used by all the "password" programs; vipw(8), chpass(1),
+ * and passwd(1).
+ */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <paths.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <pw_util.h>
+#include "yppasswdd_extern.h"
+
+int pstat;
+pid_t pid;
+
+void
+pw_init()
+{
+ struct rlimit rlim;
+
+ /* Unlimited resource limits. */
+ rlim.rlim_cur = rlim.rlim_max = RLIM_INFINITY;
+ (void)setrlimit(RLIMIT_CPU, &rlim);
+ (void)setrlimit(RLIMIT_FSIZE, &rlim);
+ (void)setrlimit(RLIMIT_STACK, &rlim);
+ (void)setrlimit(RLIMIT_DATA, &rlim);
+ (void)setrlimit(RLIMIT_RSS, &rlim);
+
+ /* Don't drop core (not really necessary, but GP's). */
+ rlim.rlim_cur = rlim.rlim_max = 0;
+ (void)setrlimit(RLIMIT_CORE, &rlim);
+
+ /* Turn off signals. */
+ /* (void)signal(SIGALRM, SIG_IGN); */
+ (void)signal(SIGHUP, SIG_IGN);
+ (void)signal(SIGINT, SIG_IGN);
+ (void)signal(SIGPIPE, SIG_IGN);
+ (void)signal(SIGQUIT, SIG_IGN);
+ (void)signal(SIGTSTP, SIG_IGN);
+ (void)signal(SIGTTOU, SIG_IGN);
+
+ /* Create with exact permissions. */
+ (void)umask(0);
+}
+
+static int lockfd;
+
+int
+pw_lock()
+{
+ /*
+ * If the master password file doesn't exist, the system is hosed.
+ * Might as well try to build one. Set the close-on-exec bit so
+ * that users can't get at the encrypted passwords while editing.
+ * Open should allow flock'ing the file; see 4.4BSD. XXX
+ */
+ lockfd = open(passfile, O_RDONLY, 0);
+ if (lockfd < 0 || fcntl(lockfd, F_SETFD, 1) == -1) {
+ yp_error("%s: %s", passfile, strerror(errno));
+ return (-1);
+ }
+ if (flock(lockfd, LOCK_EX|LOCK_NB)) {
+ yp_error("%s: the password db file is busy", passfile);
+ return(-1);
+ }
+ return (lockfd);
+}
+
+int
+pw_tmp()
+{
+ static char path[MAXPATHLEN];
+ int fd;
+ char *p;
+
+ sprintf(path,"%s",passfile);
+ if ((p = strrchr(path, '/')))
+ ++p;
+ else
+ p = path;
+ strcpy(p, "pw.XXXXXX");
+ if ((fd = mkstemp(path)) == -1) {
+ yp_error("%s: %s", path, strerror(errno));
+ return(-1);
+ }
+ tempname = path;
+ return (fd);
+}
+
+int
+pw_mkdb(username)
+char *username;
+{
+
+ yp_error("rebuilding the database...");
+ (void)fflush(stderr);
+ /* Temporarily turn off SIGCHLD catching */
+ install_reaper(0);
+ if (!(pid = vfork())) {
+ if(!username) {
+ execl(_PATH_PWD_MKDB, "pwd_mkdb", "-p", tempname, NULL);
+ } else {
+ execl(_PATH_PWD_MKDB, "pwd_mkdb", "-p", "-u", username,
+ tempname, NULL);
+ }
+ pw_error(_PATH_PWD_MKDB, 1, 1);
+ return(-1);
+ }
+ /* Handle this ourselves. */
+ reaper(-1);
+ /* Put the handler back. Foo. */
+ install_reaper(1);
+ if (pid == -1 || !WIFEXITED(pstat) || WEXITSTATUS(pstat) != 0) {
+ return (-1);
+ }
+ yp_error("done");
+ return (0);
+}
+
+void
+pw_error(name, err, eval)
+ char *name;
+ int err, eval;
+{
+ if (err && name != NULL)
+ yp_error("%s", name);
+
+ yp_error("%s: unchanged", passfile);
+ (void)unlink(tempname);
+}
diff --git a/usr.sbin/rpc.yppasswdd/rpc.yppasswdd.8 b/usr.sbin/rpc.yppasswdd/rpc.yppasswdd.8
new file mode 100644
index 0000000..4506722
--- /dev/null
+++ b/usr.sbin/rpc.yppasswdd/rpc.yppasswdd.8
@@ -0,0 +1,327 @@
+.\" Copyright (c) 1995, 1996
+.\" Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by Bill Paul.
+.\" 4. Neither the name of the author nor the names of contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $Id: rpc.yppasswdd.8,v 1.7 1997/02/22 16:12:53 peter Exp $
+.\"
+.Dd February 8, 1996
+.Dt RPC.YPPASSWDD 8
+.Os
+.Sh NAME
+.Nm rpc.yppasswdd
+.Nd "server for updating NIS passwords"
+.Sh SYNOPSIS
+.Nm rpc.yppasswdd
+.Op Fl t Ar master.passwd template file
+.Op Fl d Ar default domain
+.Op Fl p Ar path
+.Op Fl s
+.Op Fl f
+.Op Fl a
+.Op Fl m
+.Op Fl i
+.Op Fl v
+.Op Fl u
+.Op Fl h
+.Sh DESCRIPTION
+The
+.Nm
+daemon allows users to change their NIS passwords and certain
+other information using the
+.Xr yppasswd 1
+and
+.Xr ypchpass 1
+commands.
+.Nm Rpc.yppasswdd
+is an RPC-based server that accepts incoming password change requests,
+authenticates them, places the updated information in the
+.Pa /var/yp/master.passwd
+template file and then updates the NIS
+.Pa master.passwd
+and
+.Pa passwd
+maps.
+.Pp
+The
+.Nm
+server allows a normal NIS user to change
+his or her NIS password, full name (also
+known as 'GECOS' field) or shell. These updates are typically done using
+the
+.Xr yppasswd 1 ,
+.Xr ypchfn 1 ,
+.Xr ypchsh 1 ,
+or
+.Xr ypchpass 1
+commands. (Some administrators don't want users to be able to change their
+full name information or shells; the server can be invoked with option flags
+that disallow such changes.) When the server receives an update request,
+it compares the address of the client making the request against the
+.Pa securenets
+rules outlined in
+.Pa /var/yp/securenets .
+(See the
+.Xr ypserv 8
+manual page for more information on securenets; the
+.Nm
+server uses the same access control mechanism as
+.Xr ypserv 8 .)
+.Pp
+The server then
+checks the 'old' password supplied by the user to make sure it's
+valid, then performs some sanity checks on the updated information (these
+include checking for embedded control characters, colons or invalid shells).
+Once it is satisfied that the update request is valid, the server modifies
+the template password file (the default is
+.Pa /var/yp/master.passwd )
+and then runs the
+.Pa /usr/libexec/yppwupdate
+script to rebuild the NIS maps. (This script has two arguments passed
+to it: the absolute pathname of the password template that was modified
+and the name of the domain that is to be updated. These in turn are
+passed to
+.Pa /var/yp/Makefile ) .
+.Pp
+The
+.Bx Free
+version of
+.Nm
+also allows the super-user on the NIS master server to perform more
+sophisticated updates on the NIS passwd maps. The super-user can modify
+any field in any user's master.passwd entry in any domain, and can
+do so without knowing the user's existing NIS password (when the server
+receives a request from the super-user, the password authentication
+check is bypassed). Furthermore, if the server is invoked with the
+.Fl a
+flag, the super-user can even add new entries to the maps using
+.Xr ypchpass 1 .
+Again, this only applies to the super-user on the NIS
+master server: none of these special functions can be peformed over
+the network.
+.Pp
+The
+.Nm
+daemon can only be run on a machine that is an NIS master server.
+.Sh OPTIONS
+The following options are available:
+.Bl -tag -width indent
+.It Fl t Ar master.passwd template file
+By default,
+.Nm
+assumes that the template file used to generates the
+.Pa master.passwd
+and
+.Pa passwd
+maps for the default domain is called
+.Pa /var/yp/master.passwd .
+This default can be overridden by specifying an alternate file name
+with the
+.Fl t
+flag.
+.Pp
+Note: if the template file specified with this flag is
+.Pa /etc/master.passwd ,
+.Nm
+will also automatically invoke
+.Xr pwd_mkdb 8
+to rebuild the local password databases in addition to the NIS
+maps.
+.It Fl d Ar domain
+The
+.Nm
+server can support multiple domains, however it must
+choose one domain as a default.
+It will try to use the system default domain name as set by the
+.Xr domainname 1
+command for this default. However,
+if the system domain name is not
+set, a default domain must be specified on
+the command line. If the system default domain is set,
+then this option can be used to override it.
+.It Fl p Ar path
+This option can be used to override the default path to
+the location of the NIS
+map databases. The compiled-in default path is
+.Pa /var/yp .
+.It Fl s
+Disallow changing of shell information.
+.It Fl f
+Disallow changing of full name ('GECOS') information.
+.It Fl a
+Allow additions to be made to the NIS passwd databases. The super-user on the
+NIS master server is permitted to use the
+.Xr ypchpass 1
+command to perform unrestricted modifications to any field in a user's
+.Pa master.passwd
+map entry. When
+.Nm
+is started with this flag, it will also allow the super-user to add new
+records to the NIS passwd maps, just as is possible when using
+.Xr chpass 1
+to modify the local password database.
+.It Fl m
+Turn on multi-domain mode. Even though
+.Xr ypserv 8
+can handle several simultaneous domains, most implementations of
+.Nm
+can only operate on a single NIS domain, which is generally the same as
+the system default domain of the NIS master server. The
+.Bx Free
+.Nm
+attempts to overcome this problem in spite of the inherent limitations
+of the
+.Pa yppasswd
+protocol, which does not allow for a
+.Pa domain
+argument in client requests. In multi-domain mode,
+.Nm
+will search through all the passwd maps of all the domains it
+can find under
+.Pa /var/yp
+until it finds an entry that matches the user information specified in
+a given update request. (Matches are determined by checking the username,
+UID and GID fields.) The matched entry and corresponding domain are then
+used for the update.
+.Pp
+Note that in order for multi-domain mode to work, there have to be
+seperate template files for each domain. For example, if a server
+supports three domains,
+.Pa foo ,
+.Pa bar ,
+and
+.Pa baz ,
+there should be three seperate master.passwd template files called
+.Pa /var/yp/foo/master.passwd ,
+.Pa /var/yp/bar/master.passwd ,
+and
+.Pa /var/yp/baz/master.passwd .
+If
+.Pa foo
+happens to be the system default domain, then its template file can
+be either
+.Pa /var/yp/foo/master.passwd
+or
+.Pa /var/yp/master.passwd .
+The server will check for the latter file first and then use the former
+if it can't find it.
+.Pp
+Multi-domain mode is off by default since it can fail if there are
+duplicate or near-duplicate user entries in different domains. The server
+will abort an update request if it finds more than one user entry that
+matches its search criteria. Even so, paranoid administrators
+may wish to leave multi-domain mode disabled.
+.It Fl i
+If
+.Nm
+is invoked with this flag, it will perform map updates in place. This
+means that instead of just modifying the password template file and
+starting a map update, the server will modify the map databases
+directly. This is useful when the password maps are large: if, for
+example, the password database has tens of thousands of entries, it
+can take several minutes for a map update to complete. Updating the
+maps in place reduces this time to a few seconds.
+.It Fl v
+Turn on verbose logging mode. The server normally only logs messages
+using the
+.Xr syslog 3
+facility when it encounters an error condition, or when processing
+updates for the super-user on the NIS master server. Running the server
+with the
+.Fl v
+flag will cause it to log informational messages for all updates.
+.It Fl u
+Many commercial
+.Xr yppasswd 1
+clients do not use a reserved port when sending requests to
+.Nm rpc.yppasswdd .
+This is either because the
+.Xr yppasswd 1
+program is not installed set-uid root, or because the RPC
+implementation does not place any emphasis on binding to reserved
+ports when establishing client connections for the super-user.
+By default,
+.Nm
+expects to receive requests from clients using reserved ports; requests
+received from non-privileged ports are rejected. Unfortunately, this
+behavior prevents any client systems that to not use privileged
+ports from sucessfully submitting password updates. Specifying
+the
+.Fl u
+flag to
+.Nm
+disables the privileged port check so that it will work with
+.Xr yppasswd 1
+clients that don't use privileged ports. This reduces security to
+a certain small degree, but it might be necessary in cases where it
+is not possible to change the client behavior.
+.It Fl h
+Display the list of flags and options understood by
+.Nm rpc.yppasswdd .
+.El
+.Sh FILES
+.Bl -tag -width Pa -compact
+.It Pa /usr/libexec/yppwupdate
+The script invoked by
+.Nm
+to update and push the NIS maps after
+an update.
+.It Pa /var/yp/master.passwd
+The template password file for the default domain.
+.It Pa /var/yp/[domainname]/[maps]
+The NIS maps for a particular NIS domain.
+.It Pa /var/yp/[domainname]/master.passwd
+The template password file(s) for non-default domains
+(used only in multi-domain mode).
+.El
+.Sh SEE ALSO
+.Xr yp 4 ,
+.Xr yppush 8 ,
+.Xr ypserv 8 ,
+.Xr ypxfr 8
+.Sh BUGS
+As listed in the yppasswd.x protocol definition, the YPPASSWDPROC_UPDATE
+procedure takes two arguments: a V7-style passwd structure containing
+updated user information and the user's existing unencrypted (cleartext)
+password. Since
+.Nm
+is supposed to handle update requests from remote NIS client machines,
+this means that
+.Xr yppasswd 1
+and similar client programs will in fact be transmitting users' cleartext
+passwords over the network.
+.Pp
+This is not a problem for password updates since the plaintext password
+sent with the update will no longer be valid once the new encrypted password
+is put into place, but if the user is only updating his or her 'GECOS'
+information or shell, then the cleartext password sent with the update
+will still be valid once the update is completed. If the network is
+insecure, this cleartext password could be intercepted and used to
+gain unauthorized access to the user's account.
+.Sh AUTHOR
+.An Bill Paul Aq wpaul@ctr.columbia.edu
diff --git a/usr.sbin/rpc.yppasswdd/yppasswd_private.x b/usr.sbin/rpc.yppasswdd/yppasswd_private.x
new file mode 100644
index 0000000..9f50671
--- /dev/null
+++ b/usr.sbin/rpc.yppasswdd/yppasswd_private.x
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 1995, 1996
+ * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef RPC_HDR
+%#ifndef lint
+%static const char rcsid[] =
+% "$Id: yppasswd_private.x,v 1.4 1997/07/29 15:43:18 wpaul Exp $";
+%#endif /* not lint */
+#endif
+
+#ifdef RPC_HDR
+%#define YP_SOCKNAME "/var/run/yppasswdsock"
+#endif
+
+struct x_master_passwd {
+ string pw_name<>; /* username */
+ string pw_passwd<>; /* encrypted password */
+ int pw_uid; /* user id */
+ int pw_gid; /* group id */
+ unsigned long pw_change;/* password change time */
+ string pw_class<>; /* user access class */
+ string pw_gecos<>; /* in real life name */
+ string pw_dir<>; /* home directory */
+ string pw_shell<>; /* default shell */
+ unsigned long pw_expire;/* account expiration */
+ unsigned long pw_fields;/* internal: fields filled in */
+};
+
+const _YPMAXDOMAIN = 64;
+
+struct master_yppasswd {
+ string oldpass<>; /* unencrypted old password */
+ string domain<_YPMAXDOMAIN>; /* domain we want to operate on */
+ x_master_passwd newpw; /* new passwd entry */
+};
+
+
+program MASTER_YPPASSWDPROG {
+ version MASTER_YPPASSWDVERS {
+ int
+ YPPASSWDPROC_UPDATE_MASTER(master_yppasswd) = 1;
+ } = 1;
+} = 600100009;
diff --git a/usr.sbin/rpc.yppasswdd/yppasswdd_extern.h b/usr.sbin/rpc.yppasswdd/yppasswdd_extern.h
new file mode 100644
index 0000000..76bacbc
--- /dev/null
+++ b/usr.sbin/rpc.yppasswdd/yppasswdd_extern.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 1995, 1996
+ * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: yppasswdd_extern.h,v 1.2 1997/07/28 18:31:11 wpaul Exp $
+ */
+
+#include <sys/types.h>
+#include <limits.h>
+#include <db.h>
+#include <paths.h>
+#include <rpc/rpc.h>
+#include <pwd.h>
+#include <err.h>
+#include <rpcsvc/yp.h>
+#include "yp_extern.h"
+#include "ypxfr_extern.h"
+
+#ifndef YPLIBDIR
+#define YPLIBDIR "/usr/libexec/"
+#endif
+
+#ifndef _PATH_YP
+#define _PATH_YP "/var/yp/"
+#endif
+
+#define MAP_UPDATE "yppwupdate"
+#define MAP_UPDATE_PATH YPLIBDIR "yppwupdate"
+
+extern char *yp_dir;
+extern char *progname;
+extern void do_master __P(( void ));
+extern void yppasswdprog_1 __P(( struct svc_req *, register SVCXPRT * ));
+extern void master_yppasswdprog_1 __P(( struct svc_req *,
+ register SVCXPRT * ));
+extern void reaper __P(( int ));
+extern void install_reaper __P(( int ));
+extern int pw_copy __P(( int, int, struct passwd * ));
+extern int pw_lock __P(( void ));
+extern int pw_mkdb __P(( char * ));
+extern int pw_tmp __P(( void ));
+extern void pw_init __P(( void ));
+extern char *ok_shell __P (( char * ));
+extern char *passfile;
+extern char *passfile_default;
+extern char *tempname;
+extern char *yppasswd_domain;
+extern int no_chsh;
+extern int no_chfn;
+extern int allow_additions;
+extern int multidomain;
+extern int resvport;
+extern int inplace;
+extern int verbose;
+extern int _rpc_dtablesize __P((void));
diff --git a/usr.sbin/rpc.yppasswdd/yppasswdd_main.c b/usr.sbin/rpc.yppasswdd/yppasswdd_main.c
new file mode 100644
index 0000000..f1a65ab
--- /dev/null
+++ b/usr.sbin/rpc.yppasswdd/yppasswdd_main.c
@@ -0,0 +1,337 @@
+/*
+ * Copyright (c) 1995, 1996
+ * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#include "yppasswd.h"
+#include <stdio.h>
+#include <sys/types.h>
+#include <stdlib.h> /* getenv, exit */
+#include <unistd.h>
+#include <string.h>
+#include <sys/param.h>
+#include <rpc/pmap_clnt.h> /* for pmap_unset */
+#include <string.h> /* strcmp */
+#include <signal.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/ttycom.h> /* TIOCNOTTY */
+#ifdef __cplusplus
+#include <sysent.h> /* getdtablesize, open */
+#endif /* __cplusplus */
+#include <memory.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <syslog.h>
+#include <err.h>
+#include <errno.h>
+#include <rpcsvc/yp.h>
+struct dom_binding {};
+#include <rpcsvc/ypclnt.h>
+#include "yppasswdd_extern.h"
+#include "yppasswd_private.h"
+#include "ypxfr_extern.h"
+
+#ifndef SIG_PF
+#define SIG_PF void(*)(int)
+#endif
+
+#ifdef DEBUG
+#define RPC_SVC_FG
+#endif
+
+#define _RPCSVC_CLOSEDOWN 120
+int _rpcpmstart = 0; /* Started by a port monitor ? */
+static int _rpcfdtype;
+ /* Whether Stream or Datagram ? */
+ /* States a server can be in wrt request */
+
+#define _IDLE 0
+#define _SERVED 1
+#define _SERVING 2
+
+extern int _rpcsvcstate; /* Set when a request is serviced */
+char *progname = "rpc.yppasswdd";
+char *yp_dir = _PATH_YP;
+char *passfile_default = _PATH_YP "master.passwd";
+char *passfile;
+char *yppasswd_domain = NULL;
+int no_chsh = 0;
+int no_chfn = 0;
+int allow_additions = 0;
+int multidomain = 0;
+int verbose = 0;
+int resvport = 1;
+int inplace = 0;
+char *sockname = YP_SOCKNAME;
+
+static void terminate(sig)
+ int sig;
+{
+ svc_unregister(YPPASSWDPROG, YPPASSWDVERS);
+ svc_unregister(MASTER_YPPASSWDPROG, MASTER_YPPASSWDVERS);
+ unlink(sockname);
+ exit(0);
+}
+
+static void reload(sig)
+ int sig;
+{
+ load_securenets();
+}
+
+static void
+closedown(int sig)
+{
+ if (_rpcsvcstate == _IDLE) {
+ extern fd_set svc_fdset;
+ static int size;
+ int i, openfd;
+
+ if (_rpcfdtype == SOCK_DGRAM) {
+ unlink(sockname);
+ exit(0);
+ }
+ if (size == 0) {
+ size = getdtablesize();
+ }
+ for (i = 0, openfd = 0; i < size && openfd < 2; i++)
+ if (FD_ISSET(i, &svc_fdset))
+ openfd++;
+ if (openfd <= 1) {
+ unlink(sockname);
+ exit(0);
+ }
+ }
+ if (_rpcsvcstate == _SERVED)
+ _rpcsvcstate = _IDLE;
+
+ (void) signal(SIGALRM, (SIG_PF) closedown);
+ (void) alarm(_RPCSVC_CLOSEDOWN/2);
+}
+
+static void usage()
+{
+ fprintf(stderr, "%s\n%s\n",
+"usage: rpc.yppasswdd [-t master.passwd file] [-d domain] [-p path] [-s]",
+" [-f] [-m] [-i] [-a] [-v] [-u] [-h]");
+ exit(1);
+}
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ register SVCXPRT *transp = NULL;
+ int sock;
+ int proto = 0;
+ struct sockaddr_in saddr;
+ int asize = sizeof (saddr);
+ int ch;
+ char *mastername;
+ char myname[MAXHOSTNAMELEN + 2];
+ extern int errno;
+ extern int debug;
+
+ debug = 1;
+
+ while ((ch = getopt(argc, argv, "t:d:p:sfamuivh")) != -1) {
+ switch(ch) {
+ case 't':
+ passfile_default = optarg;
+ break;
+ case 'd':
+ yppasswd_domain = optarg;
+ break;
+ case 's':
+ no_chsh++;
+ break;
+ case 'f':
+ no_chfn++;
+ break;
+ case 'p':
+ yp_dir = optarg;
+ break;
+ case 'a':
+ allow_additions++;
+ break;
+ case 'm':
+ multidomain++;
+ break;
+ case 'i':
+ inplace++;
+ break;
+ case 'v':
+ verbose++;
+ break;
+ case 'u':
+ resvport = 0;
+ break;
+ default:
+ case 'h':
+ usage();
+ break;
+ }
+ }
+
+ if (yppasswd_domain == NULL) {
+ if (yp_get_default_domain(&yppasswd_domain)) {
+ yp_error("no domain specified and system domain \
+name isn't set -- aborting");
+ usage();
+ }
+ }
+
+ load_securenets();
+
+ if (getrpcport("localhost", YPPROG, YPVERS, IPPROTO_UDP) <= 0) {
+ yp_error("no ypserv processes registered with local portmap");
+ yp_error("this host is not an NIS server -- aborting");
+ exit(1);
+ }
+
+ if ((mastername = ypxfr_get_master(yppasswd_domain, "passwd.byname",
+ "localhost",0)) == NULL) {
+ yp_error("can't get name of NIS master server for domain %s",
+ yppasswd_domain);
+ exit(1);
+ }
+
+ if (gethostname((char *)&myname, sizeof(myname)) == -1) {
+ yp_error("can't get local hostname: %s", strerror(errno));
+ exit(1);
+ }
+
+ if (strncmp(mastername, (char *)&myname, sizeof(myname))) {
+ yp_error("master of %s is %s, but we are %s",
+ "passwd.byname", mastername, myname);
+ yp_error("this host is not the NIS master server for \
+the %s domain -- aborting", yppasswd_domain);
+ exit(1);
+ }
+
+ debug = 0;
+
+ if (getsockname(0, (struct sockaddr *)&saddr, &asize) == 0) {
+ int ssize = sizeof (int);
+
+ if (saddr.sin_family != AF_INET)
+ exit(1);
+ if (getsockopt(0, SOL_SOCKET, SO_TYPE,
+ (char *)&_rpcfdtype, &ssize) == -1)
+ exit(1);
+ sock = 0;
+ _rpcpmstart = 1;
+ proto = 0;
+ openlog("rpc.yppasswdd", LOG_PID, LOG_DAEMON);
+ } else {
+ if (!debug) {
+ if (daemon(0,0)) {
+ err(1,"cannot fork");
+ }
+ }
+ openlog("rpc.yppasswdd", LOG_PID, LOG_DAEMON);
+ sock = RPC_ANYSOCK;
+ (void) pmap_unset(YPPASSWDPROG, YPPASSWDVERS);
+ (void) pmap_unset(MASTER_YPPASSWDPROG, MASTER_YPPASSWDVERS);
+ unlink(sockname);
+ }
+
+ if ((_rpcfdtype == 0) || (_rpcfdtype == SOCK_DGRAM)) {
+ transp = svcudp_create(sock);
+ if (transp == NULL) {
+ yp_error("cannot create udp service.");
+ exit(1);
+ }
+ if (!_rpcpmstart)
+ proto = IPPROTO_UDP;
+ if (!svc_register(transp, YPPASSWDPROG, YPPASSWDVERS, yppasswdprog_1, proto)) {
+ yp_error("unable to register (YPPASSWDPROG, YPPASSWDVERS, udp).");
+ exit(1);
+ }
+ }
+
+ if ((_rpcfdtype == 0) || (_rpcfdtype == SOCK_STREAM)) {
+ transp = svctcp_create(sock, 0, 0);
+ if (transp == NULL) {
+ yp_error("cannot create tcp service.");
+ exit(1);
+ }
+ if (!_rpcpmstart)
+ proto = IPPROTO_TCP;
+ if (!svc_register(transp, YPPASSWDPROG, YPPASSWDVERS, yppasswdprog_1, proto)) {
+ yp_error("unable to register (YPPASSWDPROG, YPPASSWDVERS, tcp).");
+ exit(1);
+ }
+ }
+
+ unlink(sockname);
+ transp = svcunix_create(sock, 0, 0, sockname);
+ if (transp == NULL) {
+ yp_error("cannot create AF_LOCAL service.");
+ exit(1);
+ }
+ if (!svc_register(transp, MASTER_YPPASSWDPROG, MASTER_YPPASSWDVERS, master_yppasswdprog_1, 0)) {
+ yp_error("unable to register (MASTER_YPPASSWDPROG, MASTER_YPPASSWDVERS, unix).");
+ exit(1);
+ }
+ /* Only root may connect() to the AF_UNIX link. */
+ if (chmod(sockname, 0))
+ err(1, "chmod of %s failed", sockname);
+
+ if (transp == (SVCXPRT *)NULL) {
+ yp_error("could not create a handle");
+ exit(1);
+ }
+ if (_rpcpmstart) {
+ (void) signal(SIGALRM, (SIG_PF) closedown);
+ (void) alarm(_RPCSVC_CLOSEDOWN/2);
+ }
+ /* set up resource limits and block signals */
+ pw_init();
+
+ /* except SIGCHLD, which we need to catch */
+ install_reaper(1);
+ signal(SIGTERM, (SIG_PF) terminate);
+
+ signal(SIGHUP, (SIG_PF) reload);
+
+ svc_run();
+ yp_error("svc_run returned");
+ exit(1);
+ /* NOTREACHED */
+}
diff --git a/usr.sbin/rpc.yppasswdd/yppasswdd_server.c b/usr.sbin/rpc.yppasswdd/yppasswdd_server.c
new file mode 100644
index 0000000..f41cce2
--- /dev/null
+++ b/usr.sbin/rpc.yppasswdd/yppasswdd_server.c
@@ -0,0 +1,868 @@
+/*
+ * Copyright (c) 1995, 1996
+ * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <dirent.h>
+#include <sys/stat.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <limits.h>
+#include <db.h>
+#include <pwd.h>
+#include <errno.h>
+#include <signal.h>
+#include <rpc/rpc.h>
+#include <rpcsvc/yp.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/param.h>
+#include <sys/fcntl.h>
+struct dom_binding {};
+#include <rpcsvc/ypclnt.h>
+#include "yppasswdd_extern.h"
+#include "yppasswd.h"
+#include "yppasswd_private.h"
+
+char *tempname;
+
+void reaper(sig)
+ int sig;
+{
+ extern pid_t pid;
+ extern int pstat;
+ int st;
+
+ if (sig > 0) {
+ if (sig == SIGCHLD)
+ while(wait3(&st, WNOHANG, NULL) > 0) ;
+ } else {
+ pid = waitpid(pid, &pstat, 0);
+ }
+ return;
+}
+
+void install_reaper(on)
+ int on;
+{
+ if (on) {
+ signal(SIGCHLD, reaper);
+ } else {
+ signal(SIGCHLD, SIG_DFL);
+ }
+ return;
+}
+
+static struct passwd yp_password;
+
+static void copy_yp_pass(p, x, m)
+char *p;
+int x, m;
+{
+ register char *t, *s = p;
+ static char *buf;
+
+ yp_password.pw_fields = 0;
+
+ buf = (char *)realloc(buf, m + 10);
+ bzero(buf, m + 10);
+
+ /* Turn all colons into NULLs */
+ while (strchr(s, ':')) {
+ s = (strchr(s, ':') + 1);
+ *(s - 1)= '\0';
+ }
+
+ t = buf;
+#define EXPAND(e) e = t; while ((*t++ = *p++));
+ EXPAND(yp_password.pw_name);
+ yp_password.pw_fields |= _PWF_NAME;
+ EXPAND(yp_password.pw_passwd);
+ yp_password.pw_fields |= _PWF_PASSWD;
+ yp_password.pw_uid = atoi(p);
+ p += (strlen(p) + 1);
+ yp_password.pw_fields |= _PWF_UID;
+ yp_password.pw_gid = atoi(p);
+ p += (strlen(p) + 1);
+ yp_password.pw_fields |= _PWF_GID;
+ if (x) {
+ EXPAND(yp_password.pw_class);
+ yp_password.pw_fields |= _PWF_CLASS;
+ yp_password.pw_change = atol(p);
+ p += (strlen(p) + 1);
+ yp_password.pw_fields |= _PWF_CHANGE;
+ yp_password.pw_expire = atol(p);
+ p += (strlen(p) + 1);
+ yp_password.pw_fields |= _PWF_EXPIRE;
+ }
+ EXPAND(yp_password.pw_gecos);
+ yp_password.pw_fields |= _PWF_GECOS;
+ EXPAND(yp_password.pw_dir);
+ yp_password.pw_fields |= _PWF_DIR;
+ EXPAND(yp_password.pw_shell);
+ yp_password.pw_fields |= _PWF_SHELL;
+
+ return;
+}
+
+static int validchars(arg)
+ char *arg;
+{
+ int i;
+
+ for (i = 0; i < strlen(arg); i++) {
+ if (iscntrl(arg[i])) {
+ yp_error("string contains a control character");
+ return(1);
+ }
+ if (arg[i] == ':') {
+ yp_error("string contains a colon");
+ return(1);
+ }
+ /* Be evil: truncate strings with \n in them silently. */
+ if (arg[i] == '\n') {
+ arg[i] = '\0';
+ return(0);
+ }
+ }
+ return(0);
+}
+
+static int validate_master(opw, npw)
+ struct passwd *opw;
+ struct x_master_passwd *npw;
+{
+
+ if (npw->pw_name[0] == '+' || npw->pw_name[0] == '-') {
+ yp_error("client tried to modify an NIS entry");
+ return(1);
+ }
+
+ if (validchars(npw->pw_shell)) {
+ yp_error("specified shell contains invalid characters");
+ return(1);
+ }
+
+ if (validchars(npw->pw_gecos)) {
+ yp_error("specified gecos field contains invalid characters");
+ return(1);
+ }
+
+ if (validchars(npw->pw_passwd)) {
+ yp_error("specified password contains invalid characters");
+ return(1);
+ }
+ return(0);
+}
+
+static int validate(opw, npw)
+ struct passwd *opw;
+ struct x_passwd *npw;
+{
+
+ if (npw->pw_name[0] == '+' || npw->pw_name[0] == '-') {
+ yp_error("client tried to modify an NIS entry");
+ return(1);
+ }
+
+ if (npw->pw_uid != opw->pw_uid) {
+ yp_error("UID mismatch: client says user %s has UID %d",
+ npw->pw_name, npw->pw_uid);
+ yp_error("database says user %s has UID %d", opw->pw_name,
+ opw->pw_uid);
+ return(1);
+ }
+
+ if (npw->pw_gid != opw->pw_gid) {
+ yp_error("GID mismatch: client says user %s has GID %d",
+ npw->pw_name, npw->pw_gid);
+ yp_error("database says user %s has GID %d", opw->pw_name,
+ opw->pw_gid);
+ return(1);
+ }
+
+ /*
+ * Don't allow the user to shoot himself in the foot,
+ * even on purpose.
+ */
+ if (!ok_shell(npw->pw_shell)) {
+ yp_error("%s is not a valid shell", npw->pw_shell);
+ return(1);
+ }
+
+ if (validchars(npw->pw_shell)) {
+ yp_error("specified shell contains invalid characters");
+ return(1);
+ }
+
+ if (validchars(npw->pw_gecos)) {
+ yp_error("specified gecos field contains invalid characters");
+ return(1);
+ }
+
+ if (validchars(npw->pw_passwd)) {
+ yp_error("specified password contains invalid characters");
+ return(1);
+ }
+ return(0);
+}
+
+/*
+ * Kludge alert:
+ * In order to have one rpc.yppasswdd support multiple domains,
+ * we have to cheat: we search each directory under /var/yp
+ * and try to match the user in each master.passwd.byname
+ * map that we find. If the user matches (username, uid and gid
+ * all agree), then we use that domain. If we match the user in
+ * more than one database, we must abort.
+ */
+static char *find_domain(pw)
+ struct x_passwd *pw;
+{
+ struct stat statbuf;
+ struct dirent *dirp;
+ DIR *dird;
+ char yp_mapdir[MAXPATHLEN + 2];
+ static char domain[YPMAXDOMAIN];
+ char *tmp = NULL;
+ DBT key, data;
+ int hit = 0;
+
+ yp_error("performing multidomain lookup");
+
+ if ((dird = opendir(yp_dir)) == NULL) {
+ yp_error("opendir(%s) failed: %s", yp_dir, strerror(errno));
+ return(NULL);
+ }
+
+ while ((dirp = readdir(dird)) != NULL) {
+ snprintf(yp_mapdir, sizeof(yp_mapdir), "%s/%s",
+ yp_dir, dirp->d_name);
+ if (stat(yp_mapdir, &statbuf) < 0) {
+ yp_error("stat(%s) failed: %s", yp_mapdir,
+ strerror(errno));
+ closedir(dird);
+ return(NULL);
+ }
+ if (S_ISDIR(statbuf.st_mode)) {
+ tmp = (char *)dirp->d_name;
+ key.data = pw->pw_name;
+ key.size = strlen(pw->pw_name);
+
+ if (yp_get_record(tmp,"master.passwd.byname",
+ &key, &data, 0) != YP_TRUE) {
+ continue;
+ }
+ *(char *)(data.data + data.size) = '\0';
+ copy_yp_pass(data.data, 1, data.size);
+ if (yp_password.pw_uid == pw->pw_uid &&
+ yp_password.pw_gid == pw->pw_gid) {
+ hit++;
+ snprintf(domain, YPMAXDOMAIN, "%s", tmp);
+ }
+ }
+ }
+
+ closedir(dird);
+ if (hit > 1) {
+ yp_error("found same user in two different domains");
+ return(NULL);
+ } else
+ return((char *)&domain);
+}
+
+static int update_inplace(pw, domain)
+ struct passwd *pw;
+ char *domain;
+{
+ DB *dbp = NULL;
+ DBT key = { NULL, 0 };
+ DBT data = { NULL, 0 };
+ char pwbuf[YPMAXRECORD];
+ char keybuf[20];
+ int i;
+ char *maps[] = { "master.passwd.byname", "master.passwd.byuid",
+ "passwd.byname", "passwd.byuid" };
+
+ char *formats[] = { "%s:%s:%d:%d:%s:%ld:%ld:%s:%s:%s",
+ "%s:%s:%d:%d:%s:%ld:%ld:%s:%s:%s",
+ "%s:%s:%d:%d:%s:%s:%s", "%s:%s:%d:%d:%s:%s:%s" };
+ char *ptr = NULL;
+ char *yp_last = "YP_LAST_MODIFIED";
+ char yplastbuf[YPMAXRECORD];
+
+ snprintf(yplastbuf, sizeof(yplastbuf), "%lu", time(NULL));
+
+ for (i = 0; i < 4; i++) {
+
+ if (i % 2) {
+ snprintf(keybuf, sizeof(keybuf), "%ld", pw->pw_uid);
+ key.data = (char *)&keybuf;
+ key.size = strlen(keybuf);
+ } else {
+ key.data = pw->pw_name;
+ key.size = strlen(pw->pw_name);
+ }
+
+ /*
+ * XXX The passwd.byname and passwd.byuid maps come in
+ * two flavors: secure and insecure. The secure version
+ * has a '*' in the password field whereas the insecure one
+ * has a real crypted password. The maps will be insecure
+ * if they were built with 'unsecure = TRUE' enabled in
+ * /var/yp/Makefile, but we'd have no way of knowing if
+ * this has been done unless we were to try parsing the
+ * Makefile, which is a disgusting thought. Instead, we
+ * read the records from the maps, skip to the first ':'
+ * in them, and then look at the character immediately
+ * following it. If it's an '*' then the map is 'secure'
+ * and we must not insert a real password into the pw_passwd
+ * field. If it's not an '*', then we put the real crypted
+ * password in.
+ */
+ if (yp_get_record(domain,maps[i],&key,&data,1) != YP_TRUE) {
+ yp_error("couldn't read %s/%s: %s", domain,
+ maps[i], strerror(errno));
+ return(1);
+ }
+
+ if ((ptr = strchr(data.data, ':')) == NULL) {
+ yp_error("no colon in passwd record?!");
+ return(1);
+ }
+
+ /*
+ * XXX Supposing we have more than one user with the same
+ * UID? (Or more than one user with the same name?) We could
+ * end up modifying the wrong record if were not careful.
+ */
+ if (i % 2) {
+ if (strncmp(data.data, pw->pw_name,
+ strlen(pw->pw_name))) {
+ yp_error("warning: found entry for UID %d \
+in map %s@%s with wrong name (%.*s)", pw->pw_uid, maps[i], domain,
+ ptr - (char *)data.data, data.data);
+ yp_error("there may be more than one user \
+with the same UID - continuing");
+ continue;
+ }
+ } else {
+ /*
+ * We're really being ultra-paranoid here.
+ * This is generally a 'can't happen' condition.
+ */
+ snprintf(pwbuf, sizeof(pwbuf), ":%d:%d:", pw->pw_uid,
+ pw->pw_gid);
+ if (!strstr(data.data, pwbuf)) {
+ yp_error("warning: found entry for user %s \
+in map %s@%s with wrong UID", pw->pw_name, maps[i], domain);
+ yp_error("there may ne more than one user
+with the same name - continuing");
+ continue;
+ }
+ }
+
+ if (i < 2) {
+ snprintf(pwbuf, sizeof(pwbuf), formats[i],
+ pw->pw_name, pw->pw_passwd, pw->pw_uid,
+ pw->pw_gid, pw->pw_class, pw->pw_change,
+ pw->pw_expire, pw->pw_gecos, pw->pw_dir,
+ pw->pw_shell);
+ } else {
+ snprintf(pwbuf, sizeof(pwbuf), formats[i],
+ pw->pw_name, *(ptr+1) == '*' ? "*" : pw->pw_passwd,
+ pw->pw_uid, pw->pw_gid, pw->pw_gecos, pw->pw_dir,
+ pw->pw_shell);
+ }
+
+#define FLAGS O_RDWR|O_CREAT
+
+ if ((dbp = yp_open_db_rw(domain, maps[i], FLAGS)) == NULL) {
+ yp_error("couldn't open %s/%s r/w: %s",domain,
+ maps[i],strerror(errno));
+ return(1);
+ }
+
+ data.data = pwbuf;
+ data.size = strlen(pwbuf);
+
+ if (yp_put_record(dbp, &key, &data, 1) != YP_TRUE) {
+ yp_error("failed to update record in %s/%s", domain,
+ maps[i]);
+ (void)(dbp->close)(dbp);
+ return(1);
+ }
+
+ key.data = yp_last;
+ key.size = strlen(yp_last);
+ data.data = (char *)&yplastbuf;
+ data.size = strlen(yplastbuf);
+
+ if (yp_put_record(dbp, &key, &data, 1) != YP_TRUE) {
+ yp_error("failed to update timestamp in %s/%s", domain,
+ maps[i]);
+ (void)(dbp->close)(dbp);
+ return(1);
+ }
+
+ (void)(dbp->close)(dbp);
+ }
+
+ return(0);
+}
+
+static char *yp_mktmpnam()
+{
+ static char path[MAXPATHLEN];
+ char *p;
+
+ sprintf(path,"%s",passfile);
+ if ((p = strrchr(path, '/')))
+ ++p;
+ else
+ p = path;
+ strcpy(p, "yppwtmp.XXXXXX");
+ return(mktemp(path));
+}
+
+int *
+yppasswdproc_update_1_svc(yppasswd *argp, struct svc_req *rqstp)
+{
+ static int result;
+ struct sockaddr_in *rqhost;
+ DBT key, data;
+ int rval = 0;
+ int pfd, tfd;
+ int pid;
+ int passwd_changed = 0;
+ int shell_changed = 0;
+ int gecos_changed = 0;
+ char *oldshell = NULL;
+ char *oldgecos = NULL;
+ char *passfile_hold;
+ char passfile_buf[MAXPATHLEN + 2];
+ char *domain = yppasswd_domain;
+ static struct sockaddr_in clntaddr;
+ static struct timeval t_saved, t_test;
+
+ /*
+ * Normal user updates always use the 'default' master.passwd file.
+ */
+
+ passfile = passfile_default;
+ result = 1;
+
+ rqhost = svc_getcaller(rqstp->rq_xprt);
+
+ gettimeofday(&t_test, NULL);
+ if (!bcmp((char *)rqhost, (char *)&clntaddr,
+ sizeof(struct sockaddr_in)) &&
+ t_test.tv_sec > t_saved.tv_sec &&
+ t_test.tv_sec - t_saved.tv_sec < 300) {
+
+ bzero((char *)&clntaddr, sizeof(struct sockaddr_in));
+ bzero((char *)&t_saved, sizeof(struct timeval));
+ return(NULL);
+ }
+
+ bcopy((char *)rqhost, (char *)&clntaddr, sizeof(struct sockaddr_in));
+ gettimeofday(&t_saved, NULL);
+
+ if (yp_access(resvport ? "master.passwd.byname" : NULL, rqstp)) {
+ yp_error("rejected update request from unauthorized host");
+ svcerr_auth(rqstp->rq_xprt, AUTH_BADCRED);
+ return(&result);
+ }
+
+ /*
+ * Step one: find the user. (It's kinda pointless to
+ * proceed if the user doesn't exist.) We look for the
+ * user in the master.passwd.byname database, _NOT_ by
+ * using getpwent() and friends! We can't use getpwent()
+ * since the NIS master server is not guaranteed to be
+ * configured as an NIS client.
+ */
+
+ if (multidomain) {
+ if ((domain = find_domain(&argp->newpw)) == NULL) {
+ yp_error("multidomain lookup failed - aborting update");
+ return(&result);
+ } else
+ yp_error("updating user %s in domain %s",
+ argp->newpw.pw_name, domain);
+ }
+
+ key.data = argp->newpw.pw_name;
+ key.size = strlen(argp->newpw.pw_name);
+
+ if ((rval=yp_get_record(domain,"master.passwd.byname",
+ &key, &data, 0)) != YP_TRUE) {
+ if (rval == YP_NOKEY) {
+ yp_error("user %s not found in passwd database",
+ argp->newpw.pw_name);
+ } else {
+ yp_error("database access error: %s",
+ yperr_string(rval));
+ }
+ return(&result);
+ }
+
+ /* Nul terminate, please. */
+ *(char *)(data.data + data.size) = '\0';
+
+ copy_yp_pass(data.data, 1, data.size);
+
+ /* Step 2: check that the supplied oldpass is valid. */
+
+ if (strcmp(crypt(argp->oldpass, yp_password.pw_passwd),
+ yp_password.pw_passwd)) {
+ yp_error("rejected change attempt -- bad password");
+ yp_error("client address: %s username: %s",
+ inet_ntoa(rqhost->sin_addr),
+ argp->newpw.pw_name);
+ return(&result);
+ }
+
+ /* Step 3: validate the arguments passed to us by the client. */
+
+ if (validate(&yp_password, &argp->newpw)) {
+ yp_error("rejecting change attempt: bad arguments");
+ yp_error("client address: %s username: %s",
+ inet_ntoa(rqhost->sin_addr),
+ argp->newpw.pw_name);
+ svcerr_decode(rqstp->rq_xprt);
+ return(&result);
+ }
+
+ /* Step 4: update the user's passwd structure. */
+
+ if (!no_chsh && strcmp(argp->newpw.pw_shell, yp_password.pw_shell)) {
+ oldshell = yp_password.pw_shell;
+ yp_password.pw_shell = argp->newpw.pw_shell;
+ shell_changed++;
+ }
+
+
+ if (!no_chfn && strcmp(argp->newpw.pw_gecos, yp_password.pw_gecos)) {
+ oldgecos = yp_password.pw_gecos;
+ yp_password.pw_gecos = argp->newpw.pw_gecos;
+ gecos_changed++;
+ }
+
+ if (strcmp(argp->newpw.pw_passwd, yp_password.pw_passwd)) {
+ yp_password.pw_passwd = argp->newpw.pw_passwd;
+ yp_password.pw_change = 0;
+ passwd_changed++;
+ }
+
+ /*
+ * If the caller specified a domain other than our 'default'
+ * domain, change the path to master.passwd accordingly.
+ */
+
+ if (strcmp(domain, yppasswd_domain)) {
+ snprintf(passfile_buf, sizeof(passfile_buf),
+ "%s/%s/master.passwd", yp_dir, domain);
+ passfile = (char *)&passfile_buf;
+ }
+
+ /* Step 5: make a new password file with the updated info. */
+
+ if ((pfd = pw_lock()) < 0) {
+ return (&result);
+ }
+ if ((tfd = pw_tmp()) < 0) {
+ return (&result);
+ }
+
+ if (pw_copy(pfd, tfd, &yp_password)) {
+ yp_error("failed to created updated password file -- \
+cleaning up and bailing out");
+ unlink(tempname);
+ return(&result);
+ }
+
+ passfile_hold = yp_mktmpnam();
+ rename(passfile, passfile_hold);
+ if (strcmp(passfile, _PATH_MASTERPASSWD)) {
+ rename(tempname, passfile);
+ } else {
+ if (pw_mkdb(argp->newpw.pw_name) < 0) {
+ yp_error("pwd_mkdb failed");
+ return(&result);
+ }
+ }
+
+ if (inplace) {
+ if ((rval = update_inplace(&yp_password, domain))) {
+ yp_error("inplace update failed -- rebuilding maps");
+ }
+ }
+
+ switch((pid = fork())) {
+ case 0:
+ if (inplace && !rval) {
+ execlp(MAP_UPDATE_PATH, MAP_UPDATE, passfile,
+ yppasswd_domain, "pushpw", NULL);
+ } else {
+ execlp(MAP_UPDATE_PATH, MAP_UPDATE, passfile,
+ yppasswd_domain, NULL);
+ }
+ yp_error("couldn't exec map update process: %s",
+ strerror(errno));
+ unlink(passfile);
+ rename(passfile_hold, passfile);
+ exit(1);
+ break;
+ case -1:
+ yp_error("fork() failed: %s", strerror(errno));
+ unlink(passfile);
+ rename(passfile_hold, passfile);
+ return(&result);
+ break;
+ default:
+ unlink(passfile_hold);
+ break;
+ }
+
+ if (verbose) {
+ yp_error("update completed for user %s (uid %d):",
+ argp->newpw.pw_name,
+ argp->newpw.pw_uid);
+
+ if (passwd_changed)
+ yp_error("password changed");
+
+ if (gecos_changed)
+ yp_error("gecos changed ('%s' -> '%s')",
+ oldgecos, argp->newpw.pw_gecos);
+
+ if (shell_changed)
+ yp_error("shell changed ('%s' -> '%s')",
+ oldshell, argp->newpw.pw_shell);
+ }
+
+ result = 0;
+ return (&result);
+}
+
+struct cmessage {
+ struct cmsghdr cmsg;
+ struct cmsgcred cmcred;
+};
+
+/*
+ * Note that this function performs a little less sanity checking
+ * than the last one. Since only the superuser is allowed to use it,
+ * it is assumed that the caller knows what he's doing.
+ */
+int *yppasswdproc_update_master_1_svc(master_yppasswd *argp,
+ struct svc_req *rqstp)
+{
+ static int result;
+ int pfd, tfd;
+ int pid;
+ int rval = 0;
+ DBT key, data;
+ char *passfile_hold;
+ char passfile_buf[MAXPATHLEN + 2];
+ struct sockaddr_in *rqhost;
+ struct cmessage *cm;
+ SVCXPRT *transp;
+
+ result = 1;
+
+ /*
+ * NO AF_INET CONNETCIONS ALLOWED!
+ */
+ rqhost = svc_getcaller(rqstp->rq_xprt);
+ if (rqhost->sin_family != AF_UNIX) {
+ yp_error("Alert! %s/%d attempted to use superuser-only \
+procedure!\n", inet_ntoa(rqhost->sin_addr), rqhost->sin_port);
+ svcerr_auth(rqstp->rq_xprt, AUTH_BADCRED);
+ return(&result);
+ }
+
+ transp = rqstp->rq_xprt;
+
+ if (transp->xp_verf.oa_length < sizeof(struct cmessage) ||
+ transp->xp_verf.oa_base == NULL ||
+ transp->xp_verf.oa_flavor != AUTH_UNIX) {
+ yp_error("caller didn't send proper credentials");
+ svcerr_auth(rqstp->rq_xprt, AUTH_BADCRED);
+ return(&result);
+ }
+
+ cm = (struct cmessage *)transp->xp_verf.oa_base;
+ if (cm->cmsg.cmsg_type != SCM_CREDS) {
+ yp_error("caller didn't send proper credentials");
+ svcerr_auth(rqstp->rq_xprt, AUTH_BADCRED);
+ return(&result);
+ }
+
+ if (cm->cmcred.cmcred_euid) {
+ yp_error("caller euid is %d, expecting 0 -- rejecting request",
+ cm->cmcred.cmcred_euid);
+ svcerr_auth(rqstp->rq_xprt, AUTH_BADCRED);
+ return(&result);
+ }
+
+ passfile = passfile_default;
+
+ key.data = argp->newpw.pw_name;
+ key.size = strlen(argp->newpw.pw_name);
+
+ /*
+ * The superuser may add entries to the passwd maps if
+ * rpc.yppasswdd is started with the -a flag. Paranoia
+ * prevents me from allowing additions by default.
+ */
+ if ((rval = yp_get_record(argp->domain, "master.passwd.byname",
+ &key, &data, 0)) != YP_TRUE) {
+ if (rval == YP_NOKEY) {
+ yp_error("user %s not found in passwd database",
+ argp->newpw.pw_name);
+ if (allow_additions)
+ yp_error("notice: adding user %s to \
+master.passwd database for domain %s", argp->newpw.pw_name, argp->domain);
+ else
+ yp_error("restart rpc.yppasswdd with the -a flag to \
+allow additions to be made to the password database");
+ } else {
+ yp_error("database access error: %s",
+ yperr_string(rval));
+ }
+ if (!allow_additions)
+ return(&result);
+ } else {
+
+ /* Nul terminate, please. */
+ *(char *)(data.data + data.size) = '\0';
+
+ copy_yp_pass(data.data, 1, data.size);
+ }
+
+ /*
+ * Perform a small bit of sanity checking.
+ */
+ if (validate_master(rval == YP_TRUE ? &yp_password:NULL,&argp->newpw)){
+ yp_error("rejecting update attempt for %s: bad arguments",
+ argp->newpw.pw_name);
+ return(&result);
+ }
+
+ /*
+ * If the caller specified a domain other than our 'default'
+ * domain, change the path to master.passwd accordingly.
+ */
+
+ if (strcmp(argp->domain, yppasswd_domain)) {
+ snprintf(passfile_buf, sizeof(passfile_buf),
+ "%s/%s/master.passwd", yp_dir, argp->domain);
+ passfile = (char *)&passfile_buf;
+ }
+
+ if ((pfd = pw_lock()) < 0) {
+ return (&result);
+ }
+ if ((tfd = pw_tmp()) < 0) {
+ return (&result);
+ }
+
+ if (pw_copy(pfd, tfd, (struct passwd *)&argp->newpw)) {
+ yp_error("failed to created updated password file -- \
+cleaning up and bailing out");
+ unlink(tempname);
+ return(&result);
+ }
+
+ passfile_hold = yp_mktmpnam();
+ rename(passfile, passfile_hold);
+ if (strcmp(passfile, _PATH_MASTERPASSWD)) {
+ rename(tempname, passfile);
+ } else {
+ if (pw_mkdb(argp->newpw.pw_name) < 0) {
+ yp_error("pwd_mkdb failed");
+ return(&result);
+ }
+ }
+
+ if (inplace) {
+ if ((rval = update_inplace((struct passwd *)&argp->newpw,
+ argp->domain))) {
+ yp_error("inplace update failed -- rebuilding maps");
+ }
+ }
+
+ switch((pid = fork())) {
+ case 0:
+ if (inplace && !rval) {
+ execlp(MAP_UPDATE_PATH, MAP_UPDATE, passfile,
+ argp->domain, "pushpw", NULL);
+ } else {
+ execlp(MAP_UPDATE_PATH, MAP_UPDATE, passfile,
+ argp->domain, NULL);
+ }
+ yp_error("couldn't exec map update process: %s",
+ strerror(errno));
+ unlink(passfile);
+ rename(passfile_hold, passfile);
+ exit(1);
+ break;
+ case -1:
+ yp_error("fork() failed: %s", strerror(errno));
+ unlink(passfile);
+ rename(passfile_hold, passfile);
+ return(&result);
+ break;
+ default:
+ unlink(passfile_hold);
+ break;
+ }
+
+ yp_error("performed update of user %s (uid %d) domain %s",
+ argp->newpw.pw_name,
+ argp->newpw.pw_uid,
+ argp->domain);
+
+ result = 0;
+ return(&result);
+}
diff --git a/usr.sbin/rpc.yppasswdd/yppwupdate b/usr.sbin/rpc.yppasswdd/yppwupdate
new file mode 100644
index 0000000..acec9f0
--- /dev/null
+++ b/usr.sbin/rpc.yppasswdd/yppwupdate
@@ -0,0 +1,34 @@
+#!/bin/sh
+#
+# This script is invoked by rpc.yppasswdd to update the password
+# maps after the master password file has been modified. It expects
+# to be passed two arguments: the name of the master.passwd template
+# file that was modified by the server, and the name of the domain to
+# update. These are passed to /var/yp/Makefile.
+#
+# Comment out the LOG=yes line to disable logging.
+#
+# $Id$
+#
+
+PATH=/bin:/usr/bin; export PATH
+LOG=yes
+LOGFILE=/var/yp/ypupdate.log
+
+umask 077
+
+if [ ! -f $LOGFILE ];
+then
+ touch $LOGFILE
+ echo "# Edit /usr/libexec/yppwupdate to disable" >> $LOGFILE
+ echo "# logging to this file from yppasswdd." >> $LOGFILE
+ echo -n "# Log started on: " >> $LOGFILE
+ date >> $LOGFILE
+fi
+
+if [ ! $LOG ];
+then
+ cd /var/yp && make MASTER_PASSWD=$1 UPDATE_DOMAIN=$2 $3 2>&1
+else
+ cd /var/yp && make MASTER_PASSWD=$1 UPDATE_DOMAIN=$2 $3 >> $LOGFILE 2>&1
+fi
diff --git a/usr.sbin/rpc.ypupdated/Makefile b/usr.sbin/rpc.ypupdated/Makefile
new file mode 100644
index 0000000..a278ff7
--- /dev/null
+++ b/usr.sbin/rpc.ypupdated/Makefile
@@ -0,0 +1,35 @@
+# $Id: Makefile,v 1.1.1.1 1997/05/28 15:47:10 wpaul Exp $
+
+PROG= rpc.ypupdated
+SRCS= ypupdate_prot_svc.c ypupdated_main.c \
+ yp_error.c update.c ypupdated_server.c \
+ yp_dblookup.c yp_dbwrite.c yp_dbdelete.c yp_dbupdate.c
+
+.PATH: ${.CURDIR}/../ypserv ${.CURDIR}/../../libexec/ypxfr
+
+RPCDIR= ${DESTDIR}/usr/include/rpcsvc
+
+NOMAN= yes
+
+CFLAGS+= -I${.CURDIR}/../ypserv -I.
+CFLAGS+= -I${.CURDIR}/../../libexec/ypxfr
+
+#CFLAGS+= -DYP
+
+LDADD+= -lrpcsvc
+
+CLEANFILES= ypupdate_prot_svc.c ypupdate_prot.h
+
+RPCGEN= rpcgen -I -C
+
+# We need to remove the 'static' keyword from _rpcsvcstate so that
+# ypupdated_main.c can see it.
+ypupdate_prot_svc.c: ${RPCDIR}/ypupdate_prot.x ypupdate_prot.h
+ rm -f ${.TARGET}
+ ${RPCGEN} -m ${RPCDIR}/ypupdate_prot.x | \
+ sed s/"static int _rpcsvcstate"/"int _rpcsvcstate"/g > ${.TARGET}
+
+ypupdate_prot.h: ${RPCDIR}/ypupdate_prot.x
+ ${RPCGEN} -h -o ${.TARGET} ${RPCDIR}/ypupdate_prot.x
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/rpc.ypupdated/update.c b/usr.sbin/rpc.ypupdated/update.c
new file mode 100644
index 0000000..5b11d83
--- /dev/null
+++ b/usr.sbin/rpc.ypupdated/update.c
@@ -0,0 +1,363 @@
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user or with the express written consent of
+ * Sun Microsystems, Inc.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)update.c 1.2 91/03/11 Copyr 1986 Sun Micro";
+#endif
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+/*
+ * Copyright (C) 1986, 1989, Sun Microsystems, Inc.
+ */
+
+/*
+ * Administrative tool to add a new user to the publickey database
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <rpc/rpc.h>
+#include <rpc/key_prot.h>
+#ifdef YP
+#include <rpcsvc/yp_prot.h>
+#include <rpcsvc/ypclnt.h>
+#include <sys/wait.h>
+#include <netdb.h>
+#endif /* YP */
+#include <pwd.h>
+#include <string.h>
+#include <sys/resource.h>
+#include <stdlib.h>
+#include "ypupdated_extern.h"
+
+#ifdef YP
+#define MAXMAPNAMELEN 256
+#else
+#define YPOP_CHANGE 1 /* change, do not add */
+#define YPOP_INSERT 2 /* add, do not change */
+#define YPOP_DELETE 3 /* delete this entry */
+#define YPOP_STORE 4 /* add, or change */
+#endif
+
+#ifdef YP
+static char SHELL[] = "/bin/sh";
+static char YPDBPATH[]="/var/yp"; /* This is defined but not used! */
+static char PKMAP[] = "publickey.byname";
+static char UPDATEFILE[] = "updaters";
+static char PKFILE[] = "/etc/publickey";
+#endif /* YP */
+
+#ifdef YP
+static int _openchild __P(( char *, FILE **, FILE ** ));
+
+/*
+ * Determine if requester is allowed to update the given map,
+ * and update it if so. Returns the yp status, which is zero
+ * if there is no access violation.
+ */
+mapupdate(requester, mapname, op, keylen, key, datalen, data)
+ char *requester;
+ char *mapname;
+ u_int op;
+ u_int keylen;
+ char *key;
+ u_int datalen;
+ char *data;
+{
+ char updater[MAXMAPNAMELEN + 40];
+ FILE *childargs;
+ FILE *childrslt;
+#ifdef WEXITSTATUS
+ int status;
+#else
+ union wait status;
+#endif
+ pid_t pid;
+ u_int yperrno;
+
+
+#ifdef DEBUG
+ printf("%s %s\n", key, data);
+#endif
+ (void)sprintf(updater, "make -s -f %s/%s %s", YPDBPATH, /* !!! */
+ UPDATEFILE, mapname);
+ pid = _openchild(updater, &childargs, &childrslt);
+ if (pid < 0) {
+ return (YPERR_YPERR);
+ }
+
+ /*
+ * Write to child
+ */
+ (void)fprintf(childargs, "%s\n", requester);
+ (void)fprintf(childargs, "%u\n", op);
+ (void)fprintf(childargs, "%u\n", keylen);
+ (void)fwrite(key, (int)keylen, 1, childargs);
+ (void)fprintf(childargs, "\n");
+ (void)fprintf(childargs, "%u\n", datalen);
+ (void)fwrite(data, (int)datalen, 1, childargs);
+ (void)fprintf(childargs, "\n");
+ (void)fclose(childargs);
+
+ /*
+ * Read from child
+ */
+ (void)fscanf(childrslt, "%d", &yperrno);
+ (void)fclose(childrslt);
+
+ (void)wait(&status);
+#ifdef WEXITSTATUS
+ if (WEXITSTATUS(status) != 0) {
+#else
+ if (status.w_retcode != 0) {
+#endif
+ return (YPERR_YPERR);
+ }
+ return (yperrno);
+}
+
+/*
+ * returns pid, or -1 for failure
+ */
+static
+_openchild(command, fto, ffrom)
+ char *command;
+ FILE **fto;
+ FILE **ffrom;
+{
+ int i;
+ pid_t pid;
+ int pdto[2];
+ int pdfrom[2];
+ char *com;
+ struct rlimit rl;
+
+ if (pipe(pdto) < 0) {
+ goto error1;
+ }
+ if (pipe(pdfrom) < 0) {
+ goto error2;
+ }
+#ifdef VFORK
+ switch (pid = vfork()) {
+#else
+ switch (pid = fork()) {
+#endif
+ case -1:
+ goto error3;
+
+ case 0:
+ /*
+ * child: read from pdto[0], write into pdfrom[1]
+ */
+ (void)close(0);
+ (void)dup(pdto[0]);
+ (void)close(1);
+ (void)dup(pdfrom[1]);
+ getrlimit(RLIMIT_NOFILE, &rl);
+ for (i = rl.rlim_max - 1; i >= 3; i--) {
+ (void) close(i);
+ }
+ com = malloc((unsigned) strlen(command) + 6);
+ if (com == NULL) {
+ _exit(~0);
+ }
+ (void)sprintf(com, "exec %s", command);
+ execl(SHELL, basename(SHELL), "-c", com, NULL);
+ _exit(~0);
+
+ default:
+ /*
+ * parent: write into pdto[1], read from pdfrom[0]
+ */
+ *fto = fdopen(pdto[1], "w");
+ (void)close(pdto[0]);
+ *ffrom = fdopen(pdfrom[0], "r");
+ (void)close(pdfrom[1]);
+ break;
+ }
+ return (pid);
+
+ /*
+ * error cleanup and return
+ */
+error3:
+ (void)close(pdfrom[0]);
+ (void)close(pdfrom[1]);
+error2:
+ (void)close(pdto[0]);
+ (void)close(pdto[1]);
+error1:
+ return (-1);
+}
+
+static char *
+basename(path)
+ char *path;
+{
+ char *p;
+
+ p = strrchr(path, '/');
+ if (p == NULL) {
+ return (path);
+ } else {
+ return (p + 1);
+ }
+}
+
+#else /* YP */
+
+#ifdef foo
+#define ERR_ACCESS 1
+#define ERR_MALLOC 2
+#define ERR_READ 3
+#define ERR_WRITE 4
+#define ERR_DBASE 5
+#define ERR_KEY 6
+extern char *malloc();
+#endif
+
+static int match __P(( char * , char * ));
+
+/*
+ * Determine if requester is allowed to update the given map,
+ * and update it if so. Returns the status, which is zero
+ * if there is no access violation. This function updates
+ * the local file and then shuts up.
+ */
+int
+localupdate(name, filename, op, keylen, key, datalen, data)
+ char *name; /* Name of the requestor */
+ char *filename;
+ u_int op;
+ u_int keylen; /* Not used */
+ char *key;
+ u_int datalen; /* Not used */
+ char *data;
+{
+ char line[256];
+ FILE *rf;
+ FILE *wf;
+ char *tmpname;
+ int err;
+
+ /*
+ * Check permission
+ */
+ if (strcmp(name, key) != 0) {
+ return (ERR_ACCESS);
+ }
+ if (strcmp(name, "nobody") == 0) {
+ /*
+ * Can't change "nobody"s key.
+ */
+ return (ERR_ACCESS);
+ }
+
+ /*
+ * Open files
+ */
+ tmpname = malloc(strlen(filename) + 4);
+ if (tmpname == NULL) {
+ return (ERR_MALLOC);
+ }
+ sprintf(tmpname, "%s.tmp", filename);
+ rf = fopen(filename, "r");
+ if (rf == NULL) {
+ return (ERR_READ);
+ }
+ wf = fopen(tmpname, "w");
+ if (wf == NULL) {
+ return (ERR_WRITE);
+ }
+ err = -1;
+ while (fgets(line, sizeof (line), rf)) {
+ if (err < 0 && match(line, name)) {
+ switch (op) {
+ case YPOP_INSERT:
+ err = ERR_KEY;
+ break;
+ case YPOP_STORE:
+ case YPOP_CHANGE:
+ fprintf(wf, "%s %s\n", key, data);
+ err = 0;
+ break;
+ case YPOP_DELETE:
+ /* do nothing */
+ err = 0;
+ break;
+ }
+ } else {
+ fputs(line, wf);
+ }
+ }
+ if (err < 0) {
+ switch (op) {
+ case YPOP_CHANGE:
+ case YPOP_DELETE:
+ err = ERR_KEY;
+ break;
+ case YPOP_INSERT:
+ case YPOP_STORE:
+ err = 0;
+ fprintf(wf, "%s %s\n", key, data);
+ break;
+ }
+ }
+ fclose(wf);
+ fclose(rf);
+ if (err == 0) {
+ if (rename(tmpname, filename) < 0) {
+ return (ERR_DBASE);
+ }
+ } else {
+ if (unlink(tmpname) < 0) {
+ return (ERR_DBASE);
+ }
+ }
+ return (err);
+}
+
+static int
+match(line, name)
+ char *line;
+ char *name;
+{
+ int len;
+
+ len = strlen(name);
+ return (strncmp(line, name, len) == 0 &&
+ (line[len] == ' ' || line[len] == '\t'));
+}
+#endif /* !YP */
+
diff --git a/usr.sbin/rpc.ypupdated/yp_dbdelete.c b/usr.sbin/rpc.ypupdated/yp_dbdelete.c
new file mode 100644
index 0000000..304db74
--- /dev/null
+++ b/usr.sbin/rpc.ypupdated/yp_dbdelete.c
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 1995, 1996
+ * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <string.h>
+#include <limits.h>
+#include <unistd.h>
+#include <db.h>
+#include <sys/stat.h>
+#include <errno.h>
+#include <paths.h>
+#include <rpcsvc/yp.h>
+#include "ypxfr_extern.h"
+
+int yp_del_record(dbp,key)
+ DB *dbp;
+ DBT *key;
+{
+ int rval;
+
+ if ((rval = (dbp->del)(dbp,key,0))) {
+ switch(rval) {
+ case 1:
+ return(YP_FALSE);
+ break;
+ case -1:
+ default:
+ (void)(dbp->close)(dbp);
+ return(YP_BADDB);
+ break;
+ }
+ }
+
+ return(YP_TRUE);
+}
diff --git a/usr.sbin/rpc.ypupdated/yp_dbupdate.c b/usr.sbin/rpc.ypupdated/yp_dbupdate.c
new file mode 100644
index 0000000..db84c77
--- /dev/null
+++ b/usr.sbin/rpc.ypupdated/yp_dbupdate.c
@@ -0,0 +1,154 @@
+/*
+ * Copyright (c) 1996
+ * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#include <sys/fcntl.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <limits.h>
+#include <db.h>
+#include <unistd.h>
+struct dom_binding {};
+#include <rpcsvc/ypclnt.h>
+#include <rpcsvc/ypupdate_prot.h>
+#include "ypxfr_extern.h"
+#include "ypupdated_extern.h"
+
+static int yp_domake(map, domain)
+ char *map;
+ char *domain;
+{
+ int pid;
+
+ switch((pid = fork())) {
+ case 0:
+ execlp(MAP_UPDATE_PATH, MAP_UPDATE, map, domain, NULL);
+ yp_error("couldn't exec map update process: %s",
+ strerror(errno));
+ exit(1);
+ break;
+ case -1:
+ yp_error("fork() failed: %s", strerror(errno));
+ return(YPERR_YPERR);
+ break;
+ default:
+ children++;
+ break;
+ }
+
+ return(0);
+}
+
+int ypmap_update(netname, map, op, keylen, keyval, datlen, datval)
+ char *netname;
+ char *map;
+ unsigned int op;
+ unsigned int keylen;
+ char *keyval;
+ unsigned int datlen;
+ char *datval;
+{
+ DB *dbp;
+ DBT key = { NULL, 0 }, data = { NULL, 0 };
+ char *yp_last = "YP_LAST_MODIFIED";
+ char yplastbuf[YPMAXRECORD];
+ char *domptr;
+ int rval = 0;
+
+ if ((domptr = strchr(netname, '@')) == NULL)
+ return(ERR_ACCESS);
+ domptr++;
+
+
+ dbp = yp_open_db_rw(domptr, map, O_RDWR);
+ if (dbp == NULL)
+ return(ERR_DBASE);
+
+ key.data = keyval;
+ key.size = keylen;
+ data.data = datval;
+ data.size = datlen;
+
+ switch(op) {
+ case YPOP_DELETE: /* delete this entry */
+ rval = yp_del_record(dbp, &key);
+ if (rval == YP_TRUE)
+ rval = 0;
+ break;
+ case YPOP_INSERT: /* add, do not change */
+ rval = yp_put_record(dbp, &key, &data, 0);
+ if (rval == YP_TRUE)
+ rval = 0;
+ break;
+ case YPOP_STORE: /* add, or change */
+ rval = yp_put_record(dbp, &key, &data, 1);
+ if (rval == YP_TRUE)
+ rval = 0;
+ break;
+ case YPOP_CHANGE: /* change, do not add */
+ if (yp_get_record(domptr, map, &key, &data, 0) != YP_TRUE) {
+ rval = ERR_KEY;
+ break;
+ }
+ rval = yp_put_record(dbp, &key, &data, 1);
+ if (rval == YP_TRUE)
+ rval = 0;
+ break;
+ default:
+ yp_error("unknown update command: (%d)", op);
+ }
+
+ if (rval) {
+ (void)(dbp->close)(dbp);
+ return(rval);
+ }
+
+ snprintf(yplastbuf, sizeof(yplastbuf), "%lu", time(NULL));
+ key.data = yp_last;
+ key.size = strlen(yp_last);
+ data.data = (char *)&yplastbuf;
+ data.size = strlen(yplastbuf);
+ if (yp_put_record(dbp, &key, &data, 1) != YP_TRUE) {
+ yp_error("failed to update timestamp in %s/%s", domptr, map);
+ (void)(dbp->close)(dbp);
+ return(ERR_DBASE);
+ }
+
+ (void)(dbp->close)(dbp);
+ return(yp_domake(map, domptr));
+}
diff --git a/usr.sbin/rpc.ypupdated/ypupdate b/usr.sbin/rpc.ypupdated/ypupdate
new file mode 100755
index 0000000..a06247f
--- /dev/null
+++ b/usr.sbin/rpc.ypupdated/ypupdate
@@ -0,0 +1,33 @@
+#!/bin/sh
+#
+# This script is invoked by rpc.ypupdatedd to propagate NIS maps
+# after the master map databases have been modified. It expects
+# to be passed two arguments: the name of the map that was updated
+# and the name of the domain where the map resides.
+# These are passed to /var/yp/Makefile.
+#
+# Comment out the LOG=yes line to disable logging.
+#
+# $Id: yppwupdate,v 1.3 1996/06/05 06:13:09 wpaul Exp $
+#
+
+LOG=yes
+LOGFILE=/var/yp/ypupdate.log
+
+umask 077
+
+if [ ! -f $LOGFILE ];
+then
+ /usr/bin/touch $LOGFILE
+ echo "# Edit /usr/libexec/yppwupdate to disable" >> $LOGFILE
+ echo "# logging to this file from yppasswdd." >> $LOGFILE
+ echo -n "# Log started on: " >> $LOGFILE
+ /bin/date >> $LOGFILE
+fi
+
+if [ ! $LOG ];
+then
+ cd /var/yp/$2; /usr/bin/make -f ../Makefile $1 2>&1
+else
+ cd /var/yp/$2; /usr/bin/make -f ../Makefile $1 >> $LOGFILE
+fi
diff --git a/usr.sbin/rpc.ypupdated/ypupdated_extern.h b/usr.sbin/rpc.ypupdated/ypupdated_extern.h
new file mode 100644
index 0000000..28ea7ac
--- /dev/null
+++ b/usr.sbin/rpc.ypupdated/ypupdated_extern.h
@@ -0,0 +1,29 @@
+#include <db.h>
+
+#define YPOP_CHANGE 1 /* change, do not add */
+#define YPOP_INSERT 2 /* add, do not change */
+#define YPOP_DELETE 3 /* delete this entry */
+#define YPOP_STORE 4 /* add, or change */
+
+#define ERR_ACCESS 1
+#define ERR_MALLOC 2
+#define ERR_READ 3
+#define ERR_WRITE 4
+#define ERR_DBASE 5
+#define ERR_KEY 6
+
+#ifndef YPLIBDIR
+#define YPLIBDIR "/usr/libexec/"
+#endif
+
+#ifndef MAP_UPPATE
+#define MAP_UPDATE "ypupdate"
+#endif
+
+#define MAP_UPDATE_PATH YPLIBDIR MAP_UPDATE
+
+extern int children;
+extern void ypu_prog_1 __P(( struct svc_req *, register SVCXPRT * ));
+extern int localupdate __P(( char *, char *, u_int, u_int, char *, u_int, char * ));
+extern int ypmap_update __P(( char *, char *, u_int, u_int, char *, u_int, char * ));
+extern int yp_del_record __P(( DB *, DBT * ));
diff --git a/usr.sbin/rpc.ypupdated/ypupdated_main.c b/usr.sbin/rpc.ypupdated/ypupdated_main.c
new file mode 100644
index 0000000..854f611
--- /dev/null
+++ b/usr.sbin/rpc.ypupdated/ypupdated_main.c
@@ -0,0 +1,289 @@
+/*
+ * Copyright (c) 1995, 1996
+ * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#include "ypupdate_prot.h"
+#include <stdio.h>
+#include <stdlib.h> /* getenv, exit */
+#include <rpc/pmap_clnt.h> /* for pmap_unset */
+#include <string.h> /* strcmp */
+#include <signal.h>
+#include <sys/ttycom.h> /* TIOCNOTTY */
+#ifdef __cplusplus
+#include <sysent.h> /* getdtablesize, open */
+#endif /* __cplusplus */
+#include <memory.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <syslog.h>
+#include <sys/wait.h>
+#include <errno.h>
+#include <err.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include "ypupdated_extern.h"
+#include "yp_extern.h"
+
+#ifndef SIG_PF
+#define SIG_PF void(*)(int)
+#endif
+
+#ifdef DEBUG
+#define RPC_SVC_FG
+#endif
+
+#define _RPCSVC_CLOSEDOWN 120
+int _rpcpmstart; /* Started by a port monitor ? */
+static int _rpcfdtype;
+ /* Whether Stream or Datagram ? */
+ /* States a server can be in wrt request */
+
+#define _IDLE 0
+#define _SERVED 1
+#define _SERVING 2
+
+extern int _rpcsvcstate; /* Set when a request is serviced */
+
+char *progname = "rpc.ypupdated";
+char *yp_dir = "/var/yp/";
+
+static
+void _msgout(char* msg)
+{
+#ifdef RPC_SVC_FG
+ if (_rpcpmstart)
+ syslog(LOG_ERR, msg);
+ else
+ warnx("%s", msg);
+#else
+ syslog(LOG_ERR, msg);
+#endif
+}
+
+static void
+closedown(int sig)
+{
+ if (_rpcsvcstate == _IDLE) {
+ extern fd_set svc_fdset;
+ static int size;
+ int i, openfd;
+
+ if (_rpcfdtype == SOCK_DGRAM)
+ exit(0);
+ if (size == 0) {
+ size = getdtablesize();
+ }
+ for (i = 0, openfd = 0; i < size && openfd < 2; i++)
+ if (FD_ISSET(i, &svc_fdset))
+ openfd++;
+ if (openfd <= 1)
+ exit(0);
+ }
+ if (_rpcsvcstate == _SERVED)
+ _rpcsvcstate = _IDLE;
+
+ (void) signal(SIGALRM, (SIG_PF) closedown);
+ (void) alarm(_RPCSVC_CLOSEDOWN/2);
+}
+
+static void
+ypupdated_svc_run()
+{
+#ifdef FD_SETSIZE
+ fd_set readfds;
+#else
+ int readfds;
+#endif /* def FD_SETSIZE */
+ extern int forked;
+ int pid;
+ int fd_setsize = _rpc_dtablesize();
+
+ /* Establish the identity of the parent ypupdated process. */
+ pid = getpid();
+
+ for (;;) {
+#ifdef FD_SETSIZE
+ readfds = svc_fdset;
+#else
+ readfds = svc_fds;
+#endif /* def FD_SETSIZE */
+ switch (select(fd_setsize, &readfds, NULL, NULL,
+ (struct timeval *)0)) {
+ case -1:
+ if (errno == EINTR) {
+ continue;
+ }
+ warn("svc_run: - select failed");
+ return;
+ case 0:
+ continue;
+ default:
+ svc_getreqset(&readfds);
+ if (forked && pid != getpid())
+ exit(0);
+ }
+ }
+}
+
+static void reaper(sig)
+ int sig;
+{
+ int status;
+
+ if (sig == SIGHUP) {
+#ifdef foo
+ load_securenets();
+#endif
+ return;
+ }
+
+ if (sig == SIGCHLD) {
+ while (wait3(&status, WNOHANG, NULL) > 0)
+ children--;
+ } else {
+ (void) pmap_unset(YPU_PROG, YPU_VERS);
+ exit(0);
+ }
+}
+
+void usage()
+{
+ fprintf(stderr, "rpc.ypupdatedd [-p path]\n");
+ exit(0);
+}
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ register SVCXPRT *transp = NULL;
+ int sock;
+ int proto = 0;
+ struct sockaddr_in saddr;
+ int asize = sizeof (saddr);
+ int ch;
+
+ while ((ch = getopt(argc, argv, "p:h")) != EOF) {
+ switch(ch) {
+ case 'p':
+ yp_dir = optarg;
+ break;
+ default:
+ usage();
+ break;
+ }
+ }
+#ifdef foo
+ load_securenets();
+#endif
+
+ if (svc_auth_reg(AUTH_DES, _svcauth_des) == -1) {
+ yp_error("failed to register AUTH_DES flavor");
+ exit(1);
+ }
+
+ if (getsockname(0, (struct sockaddr *)&saddr, &asize) == 0) {
+ int ssize = sizeof (int);
+
+ if (saddr.sin_family != AF_INET)
+ exit(1);
+ if (getsockopt(0, SOL_SOCKET, SO_TYPE,
+ (char *)&_rpcfdtype, &ssize) == -1)
+ exit(1);
+ sock = 0;
+ _rpcpmstart = 1;
+ proto = 0;
+ openlog("rpc.ypupdatedd", LOG_PID, LOG_DAEMON);
+ } else {
+#ifndef RPC_SVC_FG
+ if (daemon(0,0)) {
+ err(1, "cannot fork");
+ }
+ openlog("rpc.ypupdated", LOG_PID, LOG_DAEMON);
+#endif
+ sock = RPC_ANYSOCK;
+ (void) pmap_unset(YPU_PROG, YPU_VERS);
+ }
+
+ if ((_rpcfdtype == 0) || (_rpcfdtype == SOCK_DGRAM)) {
+ transp = svcudp_create(sock);
+ if (transp == NULL) {
+ _msgout("cannot create udp service.");
+ exit(1);
+ }
+ if (!_rpcpmstart)
+ proto = IPPROTO_UDP;
+ if (!svc_register(transp, YPU_PROG, YPU_VERS, ypu_prog_1, proto)) {
+ _msgout("unable to register (YPU_PROG, YPU_VERS, udp).");
+ exit(1);
+ }
+ }
+
+ if ((_rpcfdtype == 0) || (_rpcfdtype == SOCK_STREAM)) {
+ transp = svctcp_create(sock, 0, 0);
+ if (transp == NULL) {
+ _msgout("cannot create tcp service.");
+ exit(1);
+ }
+ if (!_rpcpmstart)
+ proto = IPPROTO_TCP;
+ if (!svc_register(transp, YPU_PROG, YPU_VERS, ypu_prog_1, proto)) {
+ _msgout("unable to register (YPU_PROG, YPU_VERS, tcp).");
+ exit(1);
+ }
+ }
+
+ if (transp == (SVCXPRT *)NULL) {
+ _msgout("could not create a handle");
+ exit(1);
+ }
+ if (_rpcpmstart) {
+ (void) signal(SIGALRM, (SIG_PF) closedown);
+ (void) alarm(_RPCSVC_CLOSEDOWN/2);
+ }
+
+ (void) signal(SIGPIPE, SIG_IGN);
+ (void) signal(SIGCHLD, (SIG_PF) reaper);
+ (void) signal(SIGTERM, (SIG_PF) reaper);
+ (void) signal(SIGINT, (SIG_PF) reaper);
+ (void) signal(SIGHUP, (SIG_PF) reaper);
+
+ ypupdated_svc_run();
+ _msgout("svc_run returned");
+ exit(1);
+ /* NOTREACHED */
+}
diff --git a/usr.sbin/rpc.ypupdated/ypupdated_server.c b/usr.sbin/rpc.ypupdated/ypupdated_server.c
new file mode 100644
index 0000000..4741c56
--- /dev/null
+++ b/usr.sbin/rpc.ypupdated/ypupdated_server.c
@@ -0,0 +1,235 @@
+/*
+ * Copyright (c) 1995, 1996
+ * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * ypupdate server implementation
+ *
+ * Written by Bill Paul <wpaul@ctr.columbia.edu>
+ * Center for Telecommunications Research
+ * Columbia University, New York City
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#include <stdio.h>
+#include <rpc/rpc.h>
+#include <rpc/auth_des.h>
+#include <rpc/key_prot.h>
+#include <sys/param.h>
+#include <sys/cdefs.h>
+#include <rpcsvc/yp.h>
+#include "ypupdate_prot.h"
+#include "ypupdated_extern.h"
+#include "yp_extern.h"
+#include "ypxfr_extern.h"
+
+int children = 0;
+int forked = 0;
+
+/*
+ * Try to avoid spoofing: if a client chooses to use a very large
+ * window and then tries a bunch of randomly chosen encrypted timestamps,
+ * there's a chance he might stumble onto a valid combination.
+ * We therefore reject any RPCs with a window size larger than a preset
+ * value.
+ */
+#ifndef WINDOW
+#define WINDOW (60*60)
+#endif
+
+static enum auth_stat yp_checkauth(svcreq)
+ struct svc_req *svcreq;
+{
+ struct authdes_cred *des_cred;
+
+ switch (svcreq->rq_cred.oa_flavor) {
+ case AUTH_DES:
+ des_cred = (struct authdes_cred *) svcreq->rq_clntcred;
+ if (des_cred->adc_fullname.window > WINDOW) {
+ yp_error("warning: client-specified window size \
+was too large -- possible spoof attempt");
+ return(AUTH_BADCRED);
+ }
+ return(AUTH_OK);
+ break;
+ case AUTH_UNIX:
+ case AUTH_NONE:
+ yp_error("warning: client didn't use DES authentication");
+ return(AUTH_TOOWEAK);
+ break;
+ default:
+ yp_error("client used unknown auth flavor");
+ return(AUTH_REJECTEDCRED);
+ break;
+ }
+}
+
+unsigned int *ypu_change_1_svc(args, svcreq)
+ struct ypupdate_args *args;
+ struct svc_req *svcreq;
+{
+ struct authdes_cred *des_cred;
+ static int res;
+ char *netname;
+ enum auth_stat astat;
+
+ res = 0;
+
+ astat = yp_checkauth(svcreq);
+
+ if (astat != AUTH_OK) {
+ svcerr_auth(svcreq->rq_xprt, astat);
+ return(&res);
+ }
+
+ des_cred = (struct authdes_cred *) svcreq->rq_clntcred;
+ netname = des_cred->adc_fullname.name;
+
+ res = localupdate(netname, "/etc/publickey", YPOP_CHANGE,
+ args->key.yp_buf_len, args->key.yp_buf_val,
+ args->datum.yp_buf_len, args->datum.yp_buf_val);
+
+ if (res)
+ return (&res);
+
+ res = ypmap_update(netname, args->mapname, YPOP_CHANGE,
+ args->key.yp_buf_len, args->key.yp_buf_val,
+ args->datum.yp_buf_len, args->datum.yp_buf_val);
+
+ return (&res);
+}
+
+unsigned int *ypu_insert_1_svc(args, svcreq)
+ struct ypupdate_args *args;
+ struct svc_req *svcreq;
+{
+ struct authdes_cred *des_cred;
+ static int res;
+ char *netname;
+ enum auth_stat astat;
+
+ res = 0;
+
+ astat = yp_checkauth(svcreq);
+
+ if (astat != AUTH_OK) {
+ svcerr_auth(svcreq->rq_xprt, astat);
+ return(&res);
+ }
+
+ des_cred = (struct authdes_cred *) svcreq->rq_clntcred;
+ netname = des_cred->adc_fullname.name;
+
+ res = localupdate(netname, "/etc/publickey", YPOP_INSERT,
+ args->key.yp_buf_len, args->key.yp_buf_val,
+ args->datum.yp_buf_len, args->datum.yp_buf_val);
+
+ if (res)
+ return (&res);
+
+ res = ypmap_update(netname, args->mapname, YPOP_INSERT,
+ args->key.yp_buf_len, args->key.yp_buf_val,
+ args->datum.yp_buf_len, args->datum.yp_buf_val);
+
+ return (&res);
+}
+
+unsigned int *ypu_delete_1_svc(args, svcreq)
+ struct ypdelete_args *args;
+ struct svc_req *svcreq;
+{
+ struct authdes_cred *des_cred;
+ static int res;
+ char *netname;
+ enum auth_stat astat;
+
+ res = 0;
+
+ astat = yp_checkauth(svcreq);
+
+ if (astat != AUTH_OK) {
+ svcerr_auth(svcreq->rq_xprt, astat);
+ return(&res);
+ }
+
+ des_cred = (struct authdes_cred *) svcreq->rq_clntcred;
+ netname = des_cred->adc_fullname.name;
+
+ res = localupdate(netname, "/etc/publickey", YPOP_DELETE,
+ args->key.yp_buf_len, args->key.yp_buf_val,
+ 0, NULL);
+
+ if (res)
+ return (&res);
+
+ res = ypmap_update(netname, args->mapname, YPOP_DELETE,
+ args->key.yp_buf_len, args->key.yp_buf_val,
+ 0, NULL);
+
+ return (&res);
+}
+
+unsigned int *ypu_store_1_svc(args, svcreq)
+ struct ypupdate_args *args;
+ struct svc_req *svcreq;
+{
+ struct authdes_cred *des_cred;
+ static int res;
+ char *netname;
+ enum auth_stat astat;
+
+ res = 0;
+
+ astat = yp_checkauth(svcreq);
+
+ if (astat != AUTH_OK) {
+ svcerr_auth(svcreq->rq_xprt, astat);
+ return(&res);
+ }
+
+ des_cred = (struct authdes_cred *) svcreq->rq_clntcred;
+ netname = des_cred->adc_fullname.name;
+
+ res = localupdate(netname, "/etc/publickey", YPOP_STORE,
+ args->key.yp_buf_len, args->key.yp_buf_val,
+ args->datum.yp_buf_len, args->datum.yp_buf_val);
+
+ if (res)
+ return (&res);
+
+ res = ypmap_update(netname, args->mapname, YPOP_STORE,
+ args->key.yp_buf_len, args->key.yp_buf_val,
+ args->datum.yp_buf_len, args->datum.yp_buf_val);
+
+ return (&res);
+}
diff --git a/usr.sbin/rpc.ypxfrd/Makefile b/usr.sbin/rpc.ypxfrd/Makefile
new file mode 100644
index 0000000..9b81dec
--- /dev/null
+++ b/usr.sbin/rpc.ypxfrd/Makefile
@@ -0,0 +1,37 @@
+# $Id$
+
+PROG= rpc.ypxfrd
+SRCS= ypxfrd_svc.c ypxfrd_server.c yp_error.c \
+ yp_access.c ypxfrd_main.c
+
+RPCDIR= ${.CURDIR}/../../include/rpcsvc
+
+.PATH: ${.CURDIR}/../../usr.sbin/ypserv
+
+MAN8= rpc.ypxfrd.8
+
+CFLAGS+= -I. -DXFRBLOCKSIZE=65535
+
+DPADD= ${LIBRPCSVC}
+LDADD= -lrpcsvc
+
+CLEANFILES= ypxfrd_svc.c ypxfrd.h
+
+RPCGEN= rpcgen -I -C
+
+# We need to remove the 'static' keyword from _rpcsvcstate so that
+# ypxfrd_main.c can see it.
+ypxfrd_svc.c: ${RPCDIR}/ypxfrd.x ypxfrd.h
+ rm -f ${.TARGET}
+ ${RPCGEN} -m ${RPCDIR}/ypxfrd.x | \
+ sed s/"static int _rpcsvcstate"/"int _rpcsvcstate"/g > ${.TARGET}
+
+# ypxfrd_xdr.c: ${RPCDIR}/ypxfrd.x ypxfrd.h
+# 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..5a403cc
--- /dev/null
+++ b/usr.sbin/rpc.ypxfrd/rpc.ypxfrd.8
@@ -0,0 +1,140 @@
+.\" Copyright (c) 1995, 1996
+.\" Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by Bill Paul.
+.\" 4. Neither the name of the author nor the names of contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $Id: rpc.ypxfrd.8,v 1.5 1997/02/22 16:13:01 peter Exp $
+.\"
+.Dd June 2, 1996
+.Dt RPC.YPXFRD 8
+.Os
+.Sh NAME
+.Nm rpc.ypxfrd
+.Nd "NIS map transfer server"
+.Sh SYNOPSIS
+.Nm rpc.ypxfrd
+.Op Fl p Ar path
+.Sh DESCRIPTION
+The
+.Nm
+daemon is used to speed up the distribtion of very large NIS maps
+from NIS master to NIS slave servers. The normal method for transfering
+maps involves several steps:
+.Bl -bullet -offset indent
+.It
+The master server calls
+.Xr yppush 8
+to inform the slave servers to start a transfer.
+.It
+The slave servers invoke
+.Xr ypxfr 8 ,
+which reads the entire contents of a map from the master server
+using the yp_all() function.
+.It
+The
+.Xr ypxfr 8
+program then creates a new map database file by using the
+.Xr db 3
+library hash method to store the data that it receives from the server.
+.It
+When all the data has been retrieved,
+.Xr ypxfr 8
+moves the new file into place and sends
+.Xr ypserv 8
+on the local machine a YPPROC_CLEAR to tell it to refresh its
+database handles.
+.El
+.Pp
+This process can take several minutes when there are very large
+maps involved. For example: a passwd database with several tens of
+thousands of entries can consume several megabytes of disk space,
+and it can take the
+.Xr db 3
+library package a long time to sort and store all the records
+in a hash database. Consider also that there are two sets of map
+files:
+.Pa master.passwd.by{name,uid}
+and
+.Pa passwd.by{name,uid} .
+.Pp
+The
+.Nm
+server speeds up the transfer process by allowing NIS slave servers to
+simply copy the master server's map files rather than building their
+own from scratch. Simply put,
+.Nm
+implements an RPC-based file transfer protocol. Transfering even
+a multi-megabyte file in this fashion takes only a few seconds compared
+to the several minutes it would take even a reasonably fast slave server
+to build a new map from scratch.
+.Pp
+The
+.Nm
+server uses the same access restriction mechanism as
+.Xr ypserv 8 .
+This means that slave servers will only be permitted to transfer
+files if the rules in the
+.Xr securenets 5
+database permit it. Furthermore, only slave servers using reserved
+ports will be allowed to transfer the
+.Pa master.passwd
+maps.
+.Sh OPTIONS
+The following option is available:
+.Bl -tag -width indent
+.It Fl p Ar path
+This option can be used to override the default path to
+the location of the NIS
+map databases. The compiled-in default path is
+.Pa /var/yp .
+.El
+.Sh FILES
+.Bl -tag -width Pa -compact
+.It Pa /var/yp/[domainname]/[maps]
+The NIS maps for a particular NIS domain.
+.El
+.Sh SEE ALSO
+.Xr yp 4 ,
+.Xr yppush 8 ,
+.Xr ypserv 8 ,
+.Xr ypxfr 8
+.Sh BUGS
+The
+.Bx Free
+.Nm ypxfrd
+protocol is not compatible with that used by SunOS. This is unfortunate
+but unavoidable: Sun's protocol is not freely available, and even if it
+were it would probably not be useful since the SunOS NIS v2 implementation
+uses the original ndbm package for its map databases whereas the
+.Bx Free
+implementation uses Berkeley DB. These two packages use vastly different
+file formats. Furthermore, ndbm is byte-order sensitive and not very
+smart about it, meaning that am ndbm database created on a big endian
+system can't be read on a little endian system.
+.Sh AUTHOR
+.An Bill Paul Aq wpaul@ctr.columbia.edu
diff --git a/usr.sbin/rpc.ypxfrd/ypxfrd_extern.h b/usr.sbin/rpc.ypxfrd/ypxfrd_extern.h
new file mode 100644
index 0000000..3a1f154
--- /dev/null
+++ b/usr.sbin/rpc.ypxfrd/ypxfrd_extern.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 1995, 1996
+ * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id$
+ */
+
+#ifndef MAX_CHILDREN
+#define MAX_CHILDREN 20
+#endif
+
+#ifndef XFRBLOCKSIZE
+#define XFRBLOCKSIZE YPXFRBLOCK
+#endif
+
+extern int forked;
+extern int children;
+extern void load_securenets __P(( void ));
+extern void yp_error __P((const char *, ...));
+extern int yp_access __P((const char *, const struct svc_req * ));
+extern int yp_validdomain __P((const char * ));
+extern char *yp_dir;
+extern void ypxfrd_freebsd_prog_1 __P(( struct svc_req *, register SVCXPRT * ));
diff --git a/usr.sbin/rpc.ypxfrd/ypxfrd_main.c b/usr.sbin/rpc.ypxfrd/ypxfrd_main.c
new file mode 100644
index 0000000..5168409
--- /dev/null
+++ b/usr.sbin/rpc.ypxfrd/ypxfrd_main.c
@@ -0,0 +1,297 @@
+/*
+ * Copyright (c) 1995, 1996
+ * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#include "ypxfrd.h"
+#include <err.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h> /* getenv, exit */
+#include <unistd.h>
+#include <rpc/pmap_clnt.h> /* for pmap_unset */
+#include <string.h> /* strcmp */
+#include <signal.h>
+#include <sys/ttycom.h> /* TIOCNOTTY */
+#ifdef __cplusplus
+#include <sysent.h> /* getdtablesize, open */
+#endif /* __cplusplus */
+#include <memory.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <syslog.h>
+#include "ypxfrd_extern.h"
+#include <sys/wait.h>
+#include <errno.h>
+
+#ifndef SIG_PF
+#define SIG_PF void(*)(int)
+#endif
+
+#ifdef DEBUG
+#define RPC_SVC_FG
+#endif
+
+#define _RPCSVC_CLOSEDOWN 120
+int _rpcpmstart; /* Started by a port monitor ? */
+static int _rpcfdtype;
+ /* Whether Stream or Datagram ? */
+ /* States a server can be in wrt request */
+
+#define _IDLE 0
+#define _SERVED 1
+#define _SERVING 2
+
+extern int _rpcsvcstate; /* Set when a request is serviced */
+
+char *progname = "rpc.ypxfrd";
+char *yp_dir = "/var/yp/";
+
+static
+void _msgout(char* msg)
+{
+#ifdef RPC_SVC_FG
+ if (_rpcpmstart)
+ syslog(LOG_ERR, msg);
+ else
+ warnx("%s", msg);
+#else
+ syslog(LOG_ERR, msg);
+#endif
+}
+
+static void
+closedown(int sig)
+{
+ if (_rpcsvcstate == _IDLE) {
+ extern fd_set svc_fdset;
+ static int size;
+ int i, openfd;
+
+ if (_rpcfdtype == SOCK_DGRAM)
+ exit(0);
+ if (size == 0) {
+ size = getdtablesize();
+ }
+ for (i = 0, openfd = 0; i < size && openfd < 2; i++)
+ if (FD_ISSET(i, &svc_fdset))
+ openfd++;
+ if (openfd <= 1)
+ exit(0);
+ }
+ if (_rpcsvcstate == _SERVED)
+ _rpcsvcstate = _IDLE;
+
+ (void) signal(SIGALRM, (SIG_PF) closedown);
+ (void) alarm(_RPCSVC_CLOSEDOWN/2);
+}
+
+
+static void
+ypxfrd_svc_run()
+{
+#ifdef FD_SETSIZE
+ fd_set readfds;
+#else
+ int readfds;
+#endif /* def FD_SETSIZE */
+ extern int forked;
+ int pid;
+ int fd_setsize = _rpc_dtablesize();
+
+ /* Establish the identity of the parent ypserv process. */
+ pid = getpid();
+
+ for (;;) {
+#ifdef FD_SETSIZE
+ readfds = svc_fdset;
+#else
+ readfds = svc_fds;
+#endif /* def FD_SETSIZE */
+ switch (select(fd_setsize, &readfds, NULL, NULL,
+ (struct timeval *)0)) {
+ case -1:
+ if (errno == EINTR) {
+ continue;
+ }
+ warn("svc_run: - select failed");
+ return;
+ case 0:
+ continue;
+ default:
+ svc_getreqset(&readfds);
+ if (forked && pid != getpid())
+ exit(0);
+ }
+ }
+}
+
+static void reaper(sig)
+ int sig;
+{
+ int status;
+
+ if (sig == SIGHUP) {
+ load_securenets();
+ return;
+ }
+
+ if (sig == SIGCHLD) {
+ while (wait3(&status, WNOHANG, NULL) > 0)
+ children--;
+ } else {
+ (void) pmap_unset(YPXFRD_FREEBSD_PROG, YPXFRD_FREEBSD_VERS);
+ exit(0);
+ }
+}
+
+void usage()
+{
+ fprintf(stderr, "usage: rpc.ypxfrd [-p path]\n");
+ exit(0);
+}
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ register SVCXPRT *transp = NULL;
+ int sock;
+ int proto = 0;
+ struct sockaddr_in saddr;
+ int asize = sizeof (saddr);
+ int ch;
+
+ while ((ch = getopt(argc, argv, "p:h")) != -1) {
+ switch(ch) {
+ case 'p':
+ yp_dir = optarg;
+ break;
+ default:
+ usage();
+ break;
+ }
+ }
+
+ load_securenets();
+
+ if (getsockname(0, (struct sockaddr *)&saddr, &asize) == 0) {
+ int ssize = sizeof (int);
+
+ if (saddr.sin_family != AF_INET)
+ exit(1);
+ if (getsockopt(0, SOL_SOCKET, SO_TYPE,
+ (char *)&_rpcfdtype, &ssize) == -1)
+ exit(1);
+ sock = 0;
+ _rpcpmstart = 1;
+ proto = 0;
+ openlog("rpc.ypxfrd", LOG_PID, LOG_DAEMON);
+ } else {
+#ifndef RPC_SVC_FG
+ int size;
+ int pid, i;
+
+ pid = fork();
+ if (pid < 0)
+ err(1, "fork");
+ if (pid)
+ exit(0);
+ size = getdtablesize();
+ for (i = 0; i < size; i++)
+ (void) close(i);
+ i = open("/dev/console", 2);
+ (void) dup2(i, 1);
+ (void) dup2(i, 2);
+ i = open("/dev/tty", 2);
+ if (i >= 0) {
+ (void) ioctl(i, TIOCNOTTY, (char *)NULL);
+ (void) close(i);
+ }
+ openlog("rpc.ypxfrd", LOG_PID, LOG_DAEMON);
+#endif
+ sock = RPC_ANYSOCK;
+ (void) pmap_unset(YPXFRD_FREEBSD_PROG, YPXFRD_FREEBSD_VERS);
+ }
+
+ if ((_rpcfdtype == 0) || (_rpcfdtype == SOCK_DGRAM)) {
+ transp = svcudp_create(sock);
+ if (transp == NULL) {
+ _msgout("cannot create udp service.");
+ exit(1);
+ }
+ if (!_rpcpmstart)
+ proto = IPPROTO_UDP;
+ if (!svc_register(transp, YPXFRD_FREEBSD_PROG, YPXFRD_FREEBSD_VERS, ypxfrd_freebsd_prog_1, proto)) {
+ _msgout("unable to register (YPXFRD_FREEBSD_PROG, YPXFRD_FREEBSD_VERS, udp).");
+ exit(1);
+ }
+ }
+
+ if ((_rpcfdtype == 0) || (_rpcfdtype == SOCK_STREAM)) {
+ transp = svctcp_create(sock, 0, 0);
+ if (transp == NULL) {
+ _msgout("cannot create tcp service.");
+ exit(1);
+ }
+ if (!_rpcpmstart)
+ proto = IPPROTO_TCP;
+ if (!svc_register(transp, YPXFRD_FREEBSD_PROG, YPXFRD_FREEBSD_VERS, ypxfrd_freebsd_prog_1, proto)) {
+ _msgout("unable to register (YPXFRD_FREEBSD_PROG, YPXFRD_FREEBSD_VERS, tcp).");
+ exit(1);
+ }
+ }
+
+ if (transp == (SVCXPRT *)NULL) {
+ _msgout("could not create a handle");
+ exit(1);
+ }
+ if (_rpcpmstart) {
+ (void) signal(SIGALRM, (SIG_PF) closedown);
+ (void) alarm(_RPCSVC_CLOSEDOWN/2);
+ }
+
+ (void) signal(SIGPIPE, SIG_IGN);
+ (void) signal(SIGCHLD, (SIG_PF) reaper);
+ (void) signal(SIGTERM, (SIG_PF) reaper);
+ (void) signal(SIGINT, (SIG_PF) reaper);
+ (void) signal(SIGHUP, (SIG_PF) reaper);
+
+ ypxfrd_svc_run();
+ _msgout("svc_run returned");
+ exit(1);
+ /* NOTREACHED */
+}
diff --git a/usr.sbin/rpc.ypxfrd/ypxfrd_server.c b/usr.sbin/rpc.ypxfrd/ypxfrd_server.c
new file mode 100644
index 0000000..a8fe278
--- /dev/null
+++ b/usr.sbin/rpc.ypxfrd/ypxfrd_server.c
@@ -0,0 +1,147 @@
+/*
+ * Copyright (c) 1995, 1996
+ * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#include "ypxfrd.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/uio.h>
+#include <sys/fcntl.h>
+#include <machine/endian.h>
+#include "ypxfrd_extern.h"
+
+int forked = 0;
+int children = 0;
+int fp = 0;
+
+static bool_t
+xdr_my_xfr(register XDR *xdrs, xfr *objp)
+{
+ unsigned char buf[XFRBLOCKSIZE];
+
+ while(1) {
+ if ((objp->xfr_u.xfrblock_buf.xfrblock_buf_len =
+ read(fp, &buf, XFRBLOCKSIZE)) != -1) {
+ objp->ok = TRUE;
+ objp->xfr_u.xfrblock_buf.xfrblock_buf_val = (char *)&buf;
+ } else {
+ objp->ok = FALSE;
+ objp->xfr_u.xfrstat = XFR_READ_ERR;
+ yp_error("read error: %s", strerror(errno));
+ }
+
+ /* Serialize */
+ if (!xdr_xfr(xdrs, objp))
+ return(FALSE);
+ if (objp->ok == FALSE)
+ return(TRUE);
+ if (objp->xfr_u.xfrblock_buf.xfrblock_buf_len < XFRBLOCKSIZE) {
+ objp->ok = FALSE;
+ objp->xfr_u.xfrstat = XFR_DONE;
+ if (!xdr_xfr(xdrs, objp))
+ return(FALSE);
+ return(TRUE);
+ }
+ }
+}
+
+struct xfr *
+ypxfrd_getmap_1_svc(ypxfr_mapname *argp, struct svc_req *rqstp)
+{
+ static struct xfr result;
+ char buf[MAXPATHLEN];
+
+ result.ok = FALSE;
+ result.xfr_u.xfrstat = XFR_DENIED;
+
+ if (yp_validdomain(argp->xfrdomain)) {
+ return(&result);
+ }
+
+ if (yp_access(argp->xfrmap, (struct svc_req *)rqstp)) {
+ return(&result);
+ }
+
+ snprintf (buf, sizeof(buf), "%s/%s/%s", yp_dir, argp->xfrdomain,
+ argp->xfrmap);
+ if (access((char *)&buf, R_OK) == -1) {
+ result.xfr_u.xfrstat = XFR_ACCESS;
+ return(&result);
+ }
+
+ if (argp->xfr_db_type != XFR_DB_BSD_HASH &&
+ argp->xfr_db_type != XFR_DB_ANY) {
+ result.xfr_u.xfrstat = XFR_DB_TYPE_MISMATCH;
+ return(&result);
+ }
+
+#if BYTE_ORDER == LITTLE_ENDIAN
+ if (argp->xfr_byte_order == XFR_ENDIAN_BIG) {
+#else
+ if (argp->xfr_byte_order == XFR_ENDIAN_LITTLE) {
+#endif
+ result.xfr_u.xfrstat = XFR_DB_ENDIAN_MISMATCH;
+ return(&result);
+ }
+
+#ifndef DEBUG
+ if (children < MAX_CHILDREN && fork()) {
+ children++;
+ forked = 0;
+ return (NULL);
+ } else {
+ forked++;
+ }
+#endif
+ if ((fp = open((char *)&buf, O_RDONLY)) == -1) {
+ result.xfr_u.xfrstat = XFR_READ_ERR;
+ return(&result);
+ }
+
+ /* Start sending the file. */
+
+ svc_sendreply(rqstp->rq_xprt, xdr_my_xfr, (char *)&result);
+
+ close(fp);
+
+ return (NULL);
+}
diff --git a/usr.sbin/rtprio/Makefile b/usr.sbin/rtprio/Makefile
new file mode 100644
index 0000000..c023e18
--- /dev/null
+++ b/usr.sbin/rtprio/Makefile
@@ -0,0 +1,10 @@
+# from: @(#)Makefile 5.5 (Berkeley) 5/11/90
+# $Id$
+
+BINDIR=/usr/sbin
+PROG= rtprio
+LINKS= ${BINDIR}/rtprio ${BINDIR}/idprio
+MLINKS= rtprio.1 idprio.1
+MAN1= rtprio.1
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/rtprio/rtprio.1 b/usr.sbin/rtprio/rtprio.1
new file mode 100644
index 0000000..7e5b499
--- /dev/null
+++ b/usr.sbin/rtprio/rtprio.1
@@ -0,0 +1,206 @@
+.\"
+.\" Copyright (c) 1994, Henrik Vestergaard Draboel
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by Henrik Vestergaard Draboel.
+.\" 4. The name of the author may not be used to endorse or promote products
+.\" derived from this software without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $Id: rtprio.1,v 1.11 1997/03/07 07:45:17 mpp Exp $
+.\"
+.Dd July 23, 1994
+.Dt RTPRIO 1
+.Os
+.Sh NAME
+.Nm rtprio ,
+.Nm idprio
+.Nd execute, examine or modify a utilitys or process realtime
+or idletime scheduling priority
+.Sh SYNOPSIS
+.Nm [id|rt]prio
+.Nm [id|rt]prio
+.Ar [-]pid
+.Nm [id|rt]prio
+.Ar priority
+.Ar command
+.Op args
+.Nm [id|rt]prio
+.Ar priority
+.Ar -pid
+.Nm [id|rt]prio
+.Fl t
+.Ar command
+.Op args
+.Nm [id|rt]prio
+.Fl t
+.Ar -pid
+.Sh DESCRIPTION
+.Nm Rtprio
+is used for controlling realtime process scheduling.
+.Pp
+.Nm Idprio
+is used for controlling idletime process scheduling, and can be called
+with the same options as
+.Nm rtprio .
+.Pp
+A process with a realtime priority is not subject to priority
+degradation, and will only be preempted by another process of equal or
+higher realtime priority.
+.Pp
+A process with an idle priority will run only when no other
+process is runnable and then only if it's idle priority is equal or
+greater than all other runnable idle priority processes.
+.Pp
+.Nm Rtprio
+or
+.Nm idprio
+when called without arguments will return the realtime priority
+of the current process.
+.Pp
+If
+.Nm
+is called with 1 argument, it will return the realtime priority
+of the process with the specified
+.Ar pid .
+.Pp
+If
+.Ar priority
+is specified, the process or program is run at that realtime priority.
+If
+.Fl t
+is specified, the process or program is run as a normal (non-realtime)
+process.
+.Pp
+If
+.Ar -pid
+is specified, the process with the process identifier
+.Ar pid
+will be modified, else if
+.Ar command
+is specified, that program is run with its arguments.
+.Pp
+.Ar Priority
+is an integer between 0 and RTP_PRIO_MAX (usually 31). 0 is the
+highest priority
+.Pp
+.Ar Pid
+of 0 means "the current process".
+.Pp
+Only root is allowed to set realtime priorities. Non-root processes may
+set idle priority levels for the current process only.
+.Sh RETURN VALUE
+If
+.Nm
+execute a command, the exit value is that of the command executed.
+In all other cases,
+.Nm
+exits with 0 for success and 1 for all other errors.
+.Sh EXAMPLES
+To see which realtime priority the current process is at:
+.Bd -literal -offset indent -compact
+.Sy "rtprio"
+.Ed
+.Pp
+To see which realtime priority of process
+.Em 1423 :
+.Bd -literal -offset indent -compact
+.Sy "rtprio 1423"
+.Ed
+.Pp
+To run
+.Xr cron 8
+at the lowest realtime priority:
+.Bd -literal -offset indent -compact
+.Sy "rtprio 31 cron"
+.Ed
+.Pp
+To change the realtime priority of process
+.Em 1423
+to
+.Em 16 :
+.Bd -literal -offset indent -compact
+.Sy "rtprio 16 -1423"
+.Ed
+.Pp
+To run
+.Xr tcpdump 8
+without realtime priority:
+.Bd -literal -offset indent -compact
+.Sy "rtprio -t tcpdump"
+.Ed
+.Pp
+To change the realtime priority of process
+.Em 1423
+to
+.Dv RTP_PRIO_NORMAL
+(non-realtime/normal priority):
+.Bd -literal -offset indent -compact
+.Sy "rtprio -t -1423"
+.Ed
+.Pp
+To make depend while not disturbing other machine usage:
+.Bd -literal -offset indent -compact
+.Sy "idprio 31 make depend"
+.Ed
+.Sh SEE ALSO
+.Xr nice 1 ,
+.Xr ps 1 ,
+.Xr rtprio 2 ,
+.Xr setpriority 2 ,
+.Xr nice 3 ,
+.Xr renice 8
+.Sh HISTORY
+The
+.Nm
+utility appeared in
+.Fx 2.0 ,
+but is similar to the HP-UX version.
+.Sh CAVEATS
+You can lock yourself out of the system by placing a cpu-heavy
+process in a realtime priority.
+.Sh BUGS
+There is no way to set/view the realtime priority of process 0
+(swapper) (see
+.Xr ps 1 ) .
+.Pp
+There is in
+.Bx Free
+no way to ensure that a process page is present in memory therefore
+the process may be stopped for pagein (see
+.Xr mprotect 2 ,
+.Xr madvise 2 ) .
+.Pp
+Under
+.Bx Free
+system calls are currently never preempted, therefore non-realtime
+processes can starve realtime processes, or idletime processes can
+starve normal priority processes.
+.Sh AUTHOR
+.An Henrik Vestergaard Draboel Aq hvd@terry.ping.dk
+is the original author. This
+implementation in
+.Bx Free
+was substantially rewritten by
+.An David Greenman .
diff --git a/usr.sbin/rtprio/rtprio.c b/usr.sbin/rtprio/rtprio.c
new file mode 100644
index 0000000..0088b40
--- /dev/null
+++ b/usr.sbin/rtprio/rtprio.c
@@ -0,0 +1,140 @@
+/*
+ * Copyright (c) 1994 David Greenman
+ * Copyright (c) 1994 Henrik Vestergaard Draboel (hvd@terry.ping.dk)
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Henrik Vestergaard Draboel.
+ * This product includes software developed by David Greenman.
+ * 4. Neither the names of the authors nor the names of contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id$";
+#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:
+ 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[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/rwhod/Makefile b/usr.sbin/rwhod/Makefile
new file mode 100644
index 0000000..0f884f5
--- /dev/null
+++ b/usr.sbin/rwhod/Makefile
@@ -0,0 +1,6 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= rwhod
+MAN8= rwhod.8
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/rwhod/rwhod.8 b/usr.sbin/rwhod/rwhod.8
new file mode 100644
index 0000000..2b03ca3
--- /dev/null
+++ b/usr.sbin/rwhod/rwhod.8
@@ -0,0 +1,201 @@
+.\" Copyright (c) 1983, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)rwhod.8 8.2 (Berkeley) 12/11/93
+.\"
+.Dd December 11, 1993
+.Dt RWHOD 8
+.Os BSD 4.2
+.Sh NAME
+.Nm rwhod
+.Nd system status server
+.Sh SYNOPSIS
+.Nm rwhod
+.Op Fl 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.
+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 m
+option causes
+.Nm
+to use IP multicast (instead of
+broadcast) on all interfaces that have
+the IFF_MULTICAST flag set in their "ifnet" structs
+(excluding the loopback interface). The multicast
+reports are sent with a time-to-live of 1, to prevent
+forwarding beyond the directly-connected subnet(s).
+.Pp
+If the optional
+.Ar ttl
+argument is supplied with the
+.Fl m
+flag,
+.Nm
+will send IP multicast datagrams with a
+time-to-live of
+.Ar ttl ,
+via a SINGLE interface rather
+than all interfaces.
+.Ar ttl
+must be between 0 and
+32 (or MAX_MULTICAST_SCOPE). Note that
+.Fl m Ar 1
+is different than
+.Fl m ,
+in that
+.Fl m Ar 1
+specifies transmission on one interface only.
+.Pp
+When
+.Fl m
+is used without a
+.Ar ttl
+argument, the program accepts multicast
+.Nm
+reports from all multicast-capable interfaces. If a
+.Ar ttl
+argument is given, it accepts multicast reports from only one interface, the
+one on which reports are sent (which may be controlled via the host's routing
+table). Regardless of the
+.Fl m
+option, the program accepts broadcast or
+unicast reports from all interfaces. Thus, this program will hear the
+reports of old, non-multicasting
+.Nm rwhod Ns s ,
+but, if multicasting is used,
+those old
+.Nm rwhod Ns s
+won't hear the reports generated by this program.
+.Pp
+The server transmits and receives messages at the port indicated
+in the ``rwho'' 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. In addition, if the host's name, as specified
+in the message, contains any unprintable
+.Tn ASCII
+characters, the
+message is discarded. Valid messages received by
+.Nm
+are placed in files named
+.Pa whod.hostname
+in the directory
+.Pa /var/rwho .
+These files contain only the most recent message, in the
+format described above.
+.Pp
+Status messages are generated approximately once every
+3 minutes.
+.Nm Rwhod
+performs an
+.Xr nlist 3
+on
+.Pa /kernel
+every 30 minutes to guard against
+the possibility that this file is not the system
+image currently operating.
+.Sh SEE ALSO
+.Xr ruptime 1 ,
+.Xr rwho 1
+.Sh BUGS
+Status information should be sent only upon request rather than continuously.
+People often interpret the server dying
+or network communication failures
+as a machine going down.
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 4.2 .
diff --git a/usr.sbin/rwhod/rwhod.c b/usr.sbin/rwhod/rwhod.c
new file mode 100644
index 0000000..229d1f17
--- /dev/null
+++ b/usr.sbin/rwhod/rwhod.c
@@ -0,0 +1,728 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 1983, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)rwhod.c 8.1 (Berkeley) 6/6/93";
+#endif
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/signal.h>
+#include <sys/ioctl.h>
+#include <sys/sysctl.h>
+
+#include <net/if.h>
+#include <net/if_dl.h>
+#include <net/route.h>
+#include <netinet/in.h>
+#include <protocols/rwhod.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <netdb.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <utmp.h>
+#include <pwd.h>
+#include <grp.h>
+
+/*
+ * This version of Berkeley's rwhod has been modified to use IP multicast
+ * datagrams, under control of a new command-line option:
+ *
+ * rwhod -m causes rwhod to use IP multicast (instead of
+ * broadcast or unicast) on all interfaces that have
+ * the IFF_MULTICAST flag set in their "ifnet" structs
+ * (excluding the loopback interface). The multicast
+ * reports are sent with a time-to-live of 1, to prevent
+ * forwarding beyond the directly-connected subnet(s).
+ *
+ * rwhod -m <ttl> causes rwhod to send IP multicast datagrams with a
+ * time-to-live of <ttl>, via a SINGLE interface rather
+ * than all interfaces. <ttl> must be between 0 and
+ * MAX_MULTICAST_SCOPE, defined below. Note that "-m 1"
+ * is different than "-m", in that "-m 1" specifies
+ * transmission on one interface only.
+ *
+ * When "-m" is used without a <ttl> argument, the program accepts multicast
+ * rwhod reports from all multicast-capable interfaces. If a <ttl> argument
+ * is given, it accepts multicast reports from only one interface, the one
+ * on which reports are sent (which may be controlled via the host's routing
+ * table). Regardless of the "-m" option, the program accepts broadcast or
+ * unicast reports from all interfaces. Thus, this program will hear the
+ * reports of old, non-multicasting rwhods, but, if multicasting is used,
+ * those old rwhods won't hear the reports generated by this program.
+ *
+ * -- Steve Deering, Stanford University, February 1989
+ */
+
+#define UNPRIV_USER "daemon"
+#define UNPRIV_GROUP "daemon"
+
+#define NO_MULTICAST 0 /* multicast modes */
+#define PER_INTERFACE_MULTICAST 1
+#define SCOPED_MULTICAST 2
+
+#define MAX_MULTICAST_SCOPE 32 /* "site-wide", by convention */
+
+#define INADDR_WHOD_GROUP (u_long)0xe0000103 /* 224.0.1.3 */
+ /* (belongs in protocols/rwhod.h) */
+
+int 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
+ usage();
+ argv++, argc--;
+ }
+ if (argc > 0)
+ usage();
+#ifndef DEBUG
+ daemon(1, 0);
+#endif
+ (void) signal(SIGHUP, getboottime);
+ openlog("rwhod", LOG_PID, LOG_DAEMON);
+ sp = getservbyname("who", "udp");
+ if (sp == NULL) {
+ syslog(LOG_ERR, "rwhod: udp/who: unknown service\n");
+ exit(1);
+ }
+ if (chdir(_PATH_RWHODIR) < 0) {
+ syslog(LOG_ERR, "rwhod: %s: %m\n", _PATH_RWHODIR);
+ exit(1);
+ }
+ /*
+ * Establish host name as returned by system.
+ */
+ if (gethostname(myname, sizeof(myname) - 1) < 0) {
+ syslog(LOG_ERR, "gethostname: %m");
+ exit(1);
+ }
+ if ((cp = index(myname, '.')) != NULL)
+ *cp = '\0';
+ strncpy(mywd.wd_hostname, myname, sizeof(mywd.wd_hostname) - 1);
+ mywd.wd_hostname[sizeof(mywd.wd_hostname) - 1] = '\0';
+ utmpf = open(_PATH_UTMP, O_RDONLY|O_CREAT, 0644);
+ if (utmpf < 0) {
+ syslog(LOG_ERR, "%s: %m", _PATH_UTMP);
+ exit(1);
+ }
+ getboottime(0);
+ if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+ syslog(LOG_ERR, "socket: %m");
+ exit(1);
+ }
+ if (setsockopt(s, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on)) < 0) {
+ syslog(LOG_ERR, "setsockopt SO_BROADCAST: %m");
+ exit(1);
+ }
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_len = sizeof(sin);
+ sin.sin_family = AF_INET;
+ sin.sin_port = sp->s_port;
+ if (bind(s, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
+ syslog(LOG_ERR, "bind: %m");
+ exit(1);
+ }
+ setgid(unpriv_gid);
+ setgroups(1, &unpriv_gid); /* XXX BOGUS groups[0] = egid */
+ setuid(unpriv_uid);
+ if (!configure(s))
+ exit(1);
+ signal(SIGALRM, onalrm);
+ onalrm(0);
+ for (;;) {
+ struct whod wd;
+ int cc, whod, len = sizeof(from);
+
+ cc = recvfrom(s, (char *)&wd, sizeof(struct whod), 0,
+ (struct sockaddr *)&from, &len);
+ if (cc <= 0) {
+ if (cc < 0 && errno != EINTR)
+ syslog(LOG_WARNING, "recv: %m");
+ continue;
+ }
+ if (from.sin_port != sp->s_port) {
+ syslog(LOG_WARNING, "%d: bad from port",
+ ntohs(from.sin_port));
+ continue;
+ }
+ if (wd.wd_vers != WHODVERSION)
+ continue;
+ if (wd.wd_type != WHODTYPE_STATUS)
+ continue;
+ if (!verify(wd.wd_hostname, sizeof wd.wd_hostname)) {
+ syslog(LOG_WARNING, "malformed host name from %x",
+ from.sin_addr);
+ continue;
+ }
+ (void) snprintf(path, sizeof path, "whod.%s", wd.wd_hostname);
+ /*
+ * Rather than truncating and growing the file each time,
+ * use ftruncate if size is less than previous size.
+ */
+ whod = open(path, O_WRONLY | O_CREAT, 0644);
+ if (whod < 0) {
+ syslog(LOG_WARNING, "%s: %m", path);
+ continue;
+ }
+#if ENDIAN != BIG_ENDIAN
+ {
+ int i, n = (cc - WHDRSIZE)/sizeof(struct whoent);
+ struct whoent *we;
+
+ /* undo header byte swapping before writing to file */
+ wd.wd_sendtime = ntohl(wd.wd_sendtime);
+ for (i = 0; i < 3; i++)
+ wd.wd_loadav[i] = ntohl(wd.wd_loadav[i]);
+ wd.wd_boottime = ntohl(wd.wd_boottime);
+ we = wd.wd_we;
+ for (i = 0; i < n; i++) {
+ we->we_idle = ntohl(we->we_idle);
+ we->we_utmp.out_time =
+ ntohl(we->we_utmp.out_time);
+ we++;
+ }
+ }
+#endif
+ (void) time((time_t *)&wd.wd_recvtime);
+ (void) write(whod, (char *)&wd, cc);
+ if (fstat(whod, &st) < 0 || st.st_size > cc)
+ ftruncate(whod, cc);
+ (void) close(whod);
+ }
+}
+
+static void
+usage()
+{
+ fprintf(stderr, "usage: rwhod [-m [ttl]]\n");
+ exit(1);
+}
+
+void
+run_as(uid, gid)
+ uid_t *uid;
+ gid_t *gid;
+{
+ struct passwd *pw;
+ struct group *gr;
+
+ pw = getpwnam(UNPRIV_USER);
+ if (!pw) {
+ syslog(LOG_ERR, "getpwnam(%s): %m", UNPRIV_USER);
+ exit(1);
+ }
+ *uid = pw->pw_uid;
+
+ gr = getgrnam(UNPRIV_GROUP);
+ if (!gr) {
+ syslog(LOG_ERR, "getgrnam(%s): %m", UNPRIV_GROUP);
+ exit(1);
+ }
+ *gid = gr->gr_gid;
+}
+
+/*
+ * Check out host name for unprintables
+ * and other funnies before allowing a file
+ * to be created. Sorry, but blanks aren't allowed.
+ */
+int
+verify(name, maxlen)
+ register char *name;
+ register int maxlen;
+{
+ register int size = 0;
+
+ while (*name && size < maxlen - 1) {
+ if (!isascii(*name) || !(isalnum(*name) || ispunct(*name)))
+ return (0);
+ name++, size++;
+ }
+ *name = '\0';
+ return (size > 0);
+}
+
+int utmptime;
+int utmpent;
+int utmpsize = 0;
+struct utmp *utmp;
+int alarmcount;
+
+void
+onalrm(signo)
+ int signo;
+{
+ register struct neighbor *np;
+ register struct whoent *we = mywd.wd_we, *wlast;
+ register int i;
+ struct stat stb;
+ double avenrun[3];
+ time_t now;
+ int cc;
+
+ now = time(NULL);
+ if (alarmcount % 10 == 0)
+ getboottime(0);
+ alarmcount++;
+ (void) fstat(utmpf, &stb);
+ if ((stb.st_mtime != utmptime) || (stb.st_size > utmpsize)) {
+ utmptime = stb.st_mtime;
+ if (stb.st_size > utmpsize) {
+ utmpsize = stb.st_size + 10 * sizeof(struct utmp);
+ if (utmp)
+ utmp = (struct utmp *)realloc(utmp, utmpsize);
+ else
+ utmp = (struct utmp *)malloc(utmpsize);
+ if (! utmp) {
+ syslog(LOG_WARNING, "malloc failed");
+ utmpsize = 0;
+ goto done;
+ }
+ }
+ (void) lseek(utmpf, (off_t)0, L_SET);
+ cc = read(utmpf, (char *)utmp, stb.st_size);
+ if (cc < 0) {
+ syslog(LOG_ERR, "read(%s): %m", _PATH_UTMP);
+ goto done;
+ }
+ wlast = &mywd.wd_we[1024 / sizeof(struct whoent) - 1];
+ utmpent = cc / sizeof(struct utmp);
+ for (i = 0; i < utmpent; i++)
+ if (utmp[i].ut_name[0]) {
+ memcpy(we->we_utmp.out_line, utmp[i].ut_line,
+ sizeof(utmp[i].ut_line));
+ memcpy(we->we_utmp.out_name, utmp[i].ut_name,
+ sizeof(utmp[i].ut_name));
+ we->we_utmp.out_time = htonl(utmp[i].ut_time);
+ if (we >= wlast)
+ break;
+ we++;
+ }
+ utmpent = we - mywd.wd_we;
+ }
+
+ /*
+ * The test on utmpent looks silly---after all, if no one is
+ * logged on, why worry about efficiency?---but is useful on
+ * (e.g.) compute servers.
+ */
+ if (utmpent && chdir(_PATH_DEV)) {
+ syslog(LOG_ERR, "chdir(%s): %m", _PATH_DEV);
+ exit(1);
+ }
+ we = mywd.wd_we;
+ for (i = 0; i < utmpent; i++) {
+ if (stat(we->we_utmp.out_line, &stb) >= 0)
+ we->we_idle = htonl(now - stb.st_atime);
+ we++;
+ }
+ (void)getloadavg(avenrun, sizeof(avenrun)/sizeof(avenrun[0]));
+ for (i = 0; i < 3; i++)
+ mywd.wd_loadav[i] = htonl((u_long)(avenrun[i] * 100));
+ cc = (char *)we - (char *)&mywd;
+ mywd.wd_sendtime = htonl(time(0));
+ mywd.wd_vers = WHODVERSION;
+ mywd.wd_type = WHODTYPE_STATUS;
+ if (multicast_mode == SCOPED_MULTICAST) {
+ (void) sendto(s, (char *)&mywd, cc, 0,
+ (struct sockaddr *)&multicast_addr,
+ sizeof(multicast_addr));
+ }
+ else for (np = neighbors; np != NULL; np = np->n_next) {
+ if (multicast_mode == PER_INTERFACE_MULTICAST &&
+ np->n_flags & IFF_MULTICAST) {
+ /*
+ * Select the outgoing interface for the multicast.
+ */
+ if (setsockopt(s, IPPROTO_IP, IP_MULTICAST_IF,
+ &(((struct sockaddr_in *)np->n_addr)->sin_addr),
+ sizeof(struct in_addr)) < 0) {
+ syslog(LOG_ERR,
+ "setsockopt IP_MULTICAST_IF: %m");
+ exit(1);
+ }
+ (void) sendto(s, (char *)&mywd, cc, 0,
+ (struct sockaddr *)&multicast_addr,
+ sizeof(multicast_addr));
+ } else (void) sendto(s, (char *)&mywd, cc, 0,
+ np->n_addr, np->n_addrlen);
+ }
+ if (utmpent && chdir(_PATH_RWHODIR)) {
+ syslog(LOG_ERR, "chdir(%s): %m", _PATH_RWHODIR);
+ exit(1);
+ }
+done:
+ (void) alarm(AL_INTERVAL);
+}
+
+void
+getboottime(signo)
+ int signo;
+{
+ int mib[2];
+ size_t size;
+ struct timeval tm;
+
+ mib[0] = CTL_KERN;
+ mib[1] = KERN_BOOTTIME;
+ size = sizeof(tm);
+ if (sysctl(mib, 2, &tm, &size, NULL, 0) == -1) {
+ syslog(LOG_ERR, "cannot get boottime: %m");
+ exit(1);
+ }
+ mywd.wd_boottime = htonl(tm.tv_sec);
+}
+
+void
+quit(msg)
+ char *msg;
+{
+ syslog(LOG_ERR, msg);
+ exit(1);
+}
+
+#define ROUNDUP(a) \
+ ((a) > 0 ? (1 + (((a) - 1) | (sizeof(long) - 1))) : sizeof(long))
+#define ADVANCE(x, n) (x += ROUNDUP((n)->sa_len))
+
+void
+rt_xaddrs(cp, cplim, rtinfo)
+ register caddr_t cp, cplim;
+ register struct rt_addrinfo *rtinfo;
+{
+ register struct sockaddr *sa;
+ register int i;
+
+ memset(rtinfo->rti_info, 0, sizeof(rtinfo->rti_info));
+ for (i = 0; (i < RTAX_MAX) && (cp < cplim); i++) {
+ if ((rtinfo->rti_addrs & (1 << i)) == 0)
+ continue;
+ rtinfo->rti_info[i] = sa = (struct sockaddr *)cp;
+ ADVANCE(cp, sa);
+ }
+}
+
+/*
+ * Figure out device configuration and select
+ * networks which deserve status information.
+ */
+int
+configure(s)
+ int s;
+{
+ register struct neighbor *np;
+ register struct if_msghdr *ifm;
+ register struct ifa_msghdr *ifam;
+ struct sockaddr_dl *sdl;
+ size_t needed;
+ int mib[6], flags = 0, len;
+ char *buf, *lim, *next;
+ struct rt_addrinfo info;
+
+ if (multicast_mode != NO_MULTICAST) {
+ multicast_addr.sin_addr.s_addr = htonl(INADDR_WHOD_GROUP);
+ multicast_addr.sin_port = sp->s_port;
+ }
+
+ if (multicast_mode == SCOPED_MULTICAST) {
+ struct ip_mreq mreq;
+ unsigned char ttl;
+
+ mreq.imr_multiaddr.s_addr = htonl(INADDR_WHOD_GROUP);
+ mreq.imr_interface.s_addr = htonl(INADDR_ANY);
+ if (setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP,
+ &mreq, sizeof(mreq)) < 0) {
+ syslog(LOG_ERR,
+ "setsockopt IP_ADD_MEMBERSHIP: %m");
+ return(0);
+ }
+ ttl = multicast_scope;
+ if (setsockopt(s, IPPROTO_IP, IP_MULTICAST_TTL,
+ &ttl, sizeof(ttl)) < 0) {
+ syslog(LOG_ERR,
+ "setsockopt IP_MULTICAST_TTL: %m");
+ return(0);
+ }
+ return(1);
+ }
+
+ mib[0] = CTL_NET;
+ mib[1] = PF_ROUTE;
+ mib[2] = 0;
+ mib[3] = AF_INET;
+ mib[4] = NET_RT_IFLIST;
+ mib[5] = 0;
+ if (sysctl(mib, 6, NULL, &needed, NULL, 0) < 0)
+ quit("route-sysctl-estimate");
+ if ((buf = malloc(needed)) == NULL)
+ quit("malloc");
+ if (sysctl(mib, 6, buf, &needed, NULL, 0) < 0)
+ quit("actual retrieval of interface table");
+ lim = buf + needed;
+
+ sdl = NULL; /* XXX just to keep gcc -Wall happy */
+ for (next = buf; next < lim; next += ifm->ifm_msglen) {
+ ifm = (struct if_msghdr *)next;
+ if (ifm->ifm_type == RTM_IFINFO) {
+ sdl = (struct sockaddr_dl *)(ifm + 1);
+ flags = ifm->ifm_flags;
+ continue;
+ }
+ if ((flags & IFF_UP) == 0 ||
+ (flags & (((multicast_mode == PER_INTERFACE_MULTICAST) ?
+ IFF_MULTICAST : 0) |
+ IFF_BROADCAST|IFF_POINTOPOINT)) == 0)
+ continue;
+ if (ifm->ifm_type != RTM_NEWADDR)
+ quit("out of sync parsing NET_RT_IFLIST");
+ ifam = (struct ifa_msghdr *)ifm;
+ info.rti_addrs = ifam->ifam_addrs;
+ rt_xaddrs((char *)(ifam + 1), ifam->ifam_msglen + (char *)ifam,
+ &info);
+ /* gag, wish we could get rid of Internet dependencies */
+#define dstaddr info.rti_info[RTAX_BRD]
+#define ifaddr info.rti_info[RTAX_IFA]
+#define IPADDR_SA(x) ((struct sockaddr_in *)(x))->sin_addr.s_addr
+#define PORT_SA(x) ((struct sockaddr_in *)(x))->sin_port
+ if (dstaddr == 0 || dstaddr->sa_family != AF_INET)
+ continue;
+ PORT_SA(dstaddr) = sp->s_port;
+ for (np = neighbors; np != NULL; np = np->n_next)
+ if (memcmp(sdl->sdl_data, np->n_name,
+ sdl->sdl_nlen) == 0 &&
+ IPADDR_SA(np->n_addr) == IPADDR_SA(dstaddr))
+ break;
+ if (np != NULL)
+ continue;
+ len = sizeof(*np) + dstaddr->sa_len + sdl->sdl_nlen + 1;
+ np = (struct neighbor *)malloc(len);
+ if (np == NULL)
+ quit("malloc of neighbor structure");
+ memset(np, 0, len);
+ np->n_flags = flags;
+ np->n_addr = (struct sockaddr *)(np + 1);
+ np->n_addrlen = dstaddr->sa_len;
+ np->n_name = np->n_addrlen + (char *)np->n_addr;
+ memcpy((char *)np->n_addr, (char *)dstaddr, np->n_addrlen);
+ memcpy(np->n_name, sdl->sdl_data, sdl->sdl_nlen);
+ if (multicast_mode == PER_INTERFACE_MULTICAST &&
+ (flags & IFF_MULTICAST) &&
+ !(flags & IFF_LOOPBACK)) {
+ struct ip_mreq mreq;
+
+ memcpy((char *)np->n_addr, (char *)ifaddr,
+ np->n_addrlen);
+ mreq.imr_multiaddr.s_addr = htonl(INADDR_WHOD_GROUP);
+ mreq.imr_interface.s_addr =
+ ((struct sockaddr_in *)np->n_addr)->sin_addr.s_addr;
+ if (setsockopt(s, IPPROTO_IP, IP_ADD_MEMBERSHIP,
+ &mreq, sizeof(mreq)) < 0) {
+ syslog(LOG_ERR,
+ "setsockopt IP_ADD_MEMBERSHIP: %m");
+#if 0
+ /* Fall back to broadcast on this if. */
+ np->n_flags &= ~IFF_MULTICAST;
+#else
+ free((char *)np);
+ continue;
+#endif
+ }
+ }
+ np->n_next = neighbors;
+ neighbors = np;
+ }
+ free(buf);
+ return (1);
+}
+
+#ifdef DEBUG
+void
+Sendto(s, buf, cc, flags, to, tolen)
+ int s;
+ const void *buf;
+ size_t cc;
+ int flags;
+ const struct sockaddr *to;
+ int tolen;
+{
+ register struct whod *w = (struct whod *)buf;
+ register struct whoent *we;
+ struct sockaddr_in *sin = (struct sockaddr_in *)to;
+
+ printf("sendto %x.%d\n", ntohl(sin->sin_addr.s_addr),
+ ntohs(sin->sin_port));
+ printf("hostname %s %s\n", w->wd_hostname,
+ interval(ntohl(w->wd_sendtime) - ntohl(w->wd_boottime), " up"));
+ printf("load %4.2f, %4.2f, %4.2f\n",
+ ntohl(w->wd_loadav[0]) / 100.0, ntohl(w->wd_loadav[1]) / 100.0,
+ ntohl(w->wd_loadav[2]) / 100.0);
+ cc -= WHDRSIZE;
+ for (we = w->wd_we, cc /= sizeof(struct whoent); cc > 0; cc--, we++) {
+ time_t t = ntohl(we->we_utmp.out_time);
+ printf("%-8.8s %s:%s %.12s",
+ we->we_utmp.out_name,
+ w->wd_hostname, we->we_utmp.out_line,
+ ctime(&t)+4);
+ we->we_idle = ntohl(we->we_idle) / 60;
+ if (we->we_idle) {
+ if (we->we_idle >= 100*60)
+ we->we_idle = 100*60 - 1;
+ if (we->we_idle >= 60)
+ printf(" %2d", we->we_idle / 60);
+ else
+ printf(" ");
+ printf(":%02d", we->we_idle % 60);
+ }
+ printf("\n");
+ }
+}
+
+char *
+interval(time, updown)
+ int time;
+ char *updown;
+{
+ static char resbuf[32];
+ int days, hours, minutes;
+
+ if (time < 0 || time > 3*30*24*60*60) {
+ (void) sprintf(resbuf, " %s ??:??", updown);
+ return (resbuf);
+ }
+ minutes = (time + 59) / 60; /* round to minutes */
+ hours = minutes / 60; minutes %= 60;
+ days = hours / 24; hours %= 24;
+ if (days)
+ (void) sprintf(resbuf, "%s %2d+%02d:%02d",
+ updown, days, hours, minutes);
+ else
+ (void) sprintf(resbuf, "%s %2d:%02d",
+ updown, hours, minutes);
+ return (resbuf);
+}
+#endif
diff --git a/usr.sbin/sa/Makefile b/usr.sbin/sa/Makefile
new file mode 100644
index 0000000..b51840a
--- /dev/null
+++ b/usr.sbin/sa/Makefile
@@ -0,0 +1,7 @@
+# $Id$
+
+PROG= sa
+MAN8= sa.8
+SRCS= main.c pdb.c usrdb.c
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/sa/extern.h b/usr.sbin/sa/extern.h
new file mode 100644
index 0000000..3ec7276
--- /dev/null
+++ b/usr.sbin/sa/extern.h
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 1994 Christopher G. Demetriou
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Christopher G. Demetriou.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $Id$
+ */
+
+#include <sys/types.h>
+#include <sys/param.h>
+#include <db.h>
+
+/* structures */
+
+struct cmdinfo {
+ char ci_comm[MAXCOMLEN+2]; /* command name (+ '*') */
+ u_long ci_uid; /* user id */
+ u_quad_t ci_calls; /* number of calls */
+ u_quad_t ci_etime; /* elapsed time */
+ u_quad_t ci_utime; /* user time */
+ u_quad_t ci_stime; /* system time */
+ u_quad_t ci_mem; /* memory use */
+ u_quad_t ci_io; /* number of disk i/o ops */
+ u_int ci_flags; /* flags; see below */
+};
+#define CI_UNPRINTABLE 0x0001 /* unprintable chars in name */
+
+struct userinfo {
+ u_long ui_uid; /* user id; for consistency */
+ u_quad_t ui_calls; /* number of invocations */
+ u_quad_t ui_utime; /* user time */
+ u_quad_t ui_stime; /* system time */
+ u_quad_t ui_mem; /* memory use */
+ u_quad_t ui_io; /* number of disk i/o ops */
+};
+
+/* typedefs */
+
+typedef int (*cmpf_t) __P((const DBT *, const DBT *));
+
+/* external functions in sa.c */
+int main __P((int, char **));
+
+/* external functions in pdb.c */
+int pacct_init __P((void));
+void pacct_destroy __P((void));
+int pacct_add __P((const struct cmdinfo *));
+int pacct_update __P((void));
+void pacct_print __P((void));
+
+/* external functions in usrdb.c */
+int usracct_init __P((void));
+void usracct_destroy __P((void));
+int usracct_add __P((const struct cmdinfo *));
+int usracct_update __P((void));
+void usracct_print __P((void));
+
+/* variables */
+
+extern int aflag, bflag, cflag, dflag, Dflag, fflag, iflag, jflag, kflag;
+extern int Kflag, lflag, mflag, qflag, rflag, sflag, tflag, uflag, vflag;
+extern int cutoff;
+extern cmpf_t sa_cmp;
+
+/* some #defines to help with db's stupidity */
+
+#define DB_CLOSE(db) \
+ ((*(db)->close)(db))
+#define DB_GET(db, key, data, flags) \
+ ((*(db)->get)((db), (key), (data), (flags)))
+#define DB_PUT(db, key, data, flags) \
+ ((*(db)->put)((db), (key), (data), (flags)))
+#define DB_SYNC(db, flags) \
+ ((*(db)->sync)((db), (flags)))
+#define DB_SEQ(db, key, data, flags) \
+ ((*(db)->seq)((db), (key), (data), (flags)))
diff --git a/usr.sbin/sa/main.c b/usr.sbin/sa/main.c
new file mode 100644
index 0000000..99b1d8a
--- /dev/null
+++ b/usr.sbin/sa/main.c
@@ -0,0 +1,553 @@
+/*
+ * Copyright (c) 1994 Christopher G. Demetriou
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Christopher G. Demetriou.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 1994 Christopher G. Demetriou\n\
+ All rights reserved.\n";
+#endif
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+/*
+ * sa: system accounting
+ */
+
+#include <sys/types.h>
+#include <sys/acct.h>
+#include <ctype.h>
+#include <err.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include "extern.h"
+#include "pathnames.h"
+
+static int acct_load __P((char *, int));
+static u_quad_t decode_comp_t __P((comp_t));
+static int cmp_comm __P((const char *, const char *));
+static int cmp_usrsys __P((const DBT *, const DBT *));
+static int cmp_avgusrsys __P((const DBT *, const DBT *));
+static int cmp_dkio __P((const DBT *, const DBT *));
+static int cmp_avgdkio __P((const DBT *, const DBT *));
+static int cmp_cpumem __P((const DBT *, const DBT *));
+static int cmp_avgcpumem __P((const DBT *, const DBT *));
+static int cmp_calls __P((const DBT *, const DBT *));
+static void usage __P((void));
+
+int aflag, bflag, cflag, dflag, Dflag, fflag, iflag, jflag, kflag;
+int Kflag, lflag, mflag, qflag, rflag, sflag, tflag, uflag, vflag;
+int cutoff = 1;
+
+static char *dfltargv[] = { _PATH_ACCT };
+static int dfltargc = (sizeof dfltargv/sizeof(char *));
+
+/* default to comparing by sum of user + system time */
+cmpf_t sa_cmp = cmp_usrsys;
+
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ char ch;
+ int error;
+
+ while ((ch = getopt(argc, argv, "abcdDfijkKlmnqrstuv:")) != -1)
+ switch (ch) {
+ case 'a':
+ /* print all commands */
+ aflag = 1;
+ break;
+ case 'b':
+ /* sort by per-call user/system time average */
+ bflag = 1;
+ sa_cmp = cmp_avgusrsys;
+ break;
+ case 'c':
+ /* print percentage total time */
+ cflag = 1;
+ break;
+ case 'd':
+ /* sort by averge number of disk I/O ops */
+ dflag = 1;
+ sa_cmp = cmp_avgdkio;
+ break;
+ case 'D':
+ /* print and sort by total disk I/O ops */
+ Dflag = 1;
+ sa_cmp = cmp_dkio;
+ break;
+ case 'f':
+ /* force no interactive threshold comprison */
+ fflag = 1;
+ break;
+ case 'i':
+ /* do not read in summary file */
+ iflag = 1;
+ break;
+ case 'j':
+ /* instead of total minutes, give sec/call */
+ jflag = 1;
+ break;
+ case 'k':
+ /* sort by cpu-time average memory usage */
+ kflag = 1;
+ sa_cmp = cmp_avgcpumem;
+ break;
+ case 'K':
+ /* print and sort by cpu-storage integral */
+ sa_cmp = cmp_cpumem;
+ Kflag = 1;
+ break;
+ case 'l':
+ /* seperate system and user time */
+ lflag = 1;
+ break;
+ case 'm':
+ /* print procs and time per-user */
+ mflag = 1;
+ break;
+ case 'n':
+ /* sort by number of calls */
+ sa_cmp = cmp_calls;
+ break;
+ case 'q':
+ /* quiet; error messages only */
+ qflag = 1;
+ break;
+ case 'r':
+ /* reverse order of sort */
+ rflag = 1;
+ break;
+ case 's':
+ /* merge accounting file into summaries */
+ sflag = 1;
+ break;
+ case 't':
+ /* report ratio of user and system times */
+ tflag = 1;
+ break;
+ case 'u':
+ /* first, print uid and command name */
+ uflag = 1;
+ break;
+ case 'v':
+ /* cull junk */
+ vflag = 1;
+ cutoff = atoi(optarg);
+ break;
+ case '?':
+ default:
+ usage();
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ /* various argument checking */
+ if (fflag && !vflag)
+ errx(1, "only one of -f requires -v");
+ if (fflag && aflag)
+ errx(1, "only one of -a and -v may be specified");
+ /* XXX need more argument checking */
+
+ if (!uflag) {
+ /* initialize tables */
+ if ((sflag || (!mflag && !qflag)) && pacct_init() != 0)
+ errx(1, "process accounting initialization failed");
+ if ((sflag || (mflag && !qflag)) && usracct_init() != 0)
+ errx(1, "user accounting initialization failed");
+ }
+
+ if (argc == 0) {
+ argc = dfltargc;
+ argv = dfltargv;
+ }
+
+ /* for each file specified */
+ for (; argc > 0; argc--, argv++) {
+ int fd;
+
+ /*
+ * load the accounting data from the file.
+ * if it fails, go on to the next file.
+ */
+ fd = acct_load(argv[0], sflag);
+ if (fd < 0)
+ continue;
+
+ if (!uflag && sflag) {
+#ifndef DEBUG
+ sigset_t nmask, omask;
+ int unmask = 1;
+
+ /*
+ * block most signals so we aren't interrupted during
+ * the update.
+ */
+ if (sigfillset(&nmask) == -1) {
+ warn("sigfillset");
+ unmask = 0;
+ error = 1;
+ }
+ if (unmask &&
+ (sigprocmask(SIG_BLOCK, &nmask, &omask) == -1)) {
+ warn("couldn't set signal mask");
+ unmask = 0;
+ error = 1;
+ }
+#endif /* DEBUG */
+
+ /*
+ * truncate the accounting data file ASAP, to avoid
+ * losing data. don't worry about errors in updating
+ * the saved stats; better to underbill than overbill,
+ * but we want every accounting record intact.
+ */
+ if (ftruncate(fd, 0) == -1) {
+ warn("couldn't truncate %s", argv);
+ error = 1;
+ }
+
+ /*
+ * update saved user and process accounting data.
+ * note errors for later.
+ */
+ if (pacct_update() != 0 || usracct_update() != 0)
+ error = 1;
+
+#ifndef DEBUG
+ /*
+ * restore signals
+ */
+ if (unmask &&
+ (sigprocmask(SIG_SETMASK, &omask, NULL) == -1)) {
+ warn("couldn't restore signal mask");
+ error = 1;
+ }
+#endif /* DEBUG */
+ }
+
+ /*
+ * close the opened accounting file
+ */
+ if (close(fd) == -1) {
+ warn("close %s", argv);
+ error = 1;
+ }
+ }
+
+ if (!uflag && !qflag) {
+ /* print any results we may have obtained. */
+ if (!mflag)
+ pacct_print();
+ else
+ usracct_print();
+ }
+
+ if (!uflag) {
+ /* finally, deallocate databases */
+ if (sflag || (!mflag && !qflag))
+ pacct_destroy();
+ if (sflag || (mflag && !qflag))
+ usracct_destroy();
+ }
+
+ exit(error);
+}
+
+static void
+usage()
+{
+ (void)fprintf(stderr,
+ "usage: sa [-abcdDfijkKlmnqrstu] [-v cutoff] [file ...]\n");
+ exit(1);
+}
+
+static int
+acct_load(pn, wr)
+ char *pn;
+ int wr;
+{
+ struct acct ac;
+ struct cmdinfo ci;
+ ssize_t rv;
+ int fd, i;
+
+ /*
+ * open the file
+ */
+ fd = open(pn, wr ? O_RDWR : O_RDONLY, 0);
+ if (fd == -1) {
+ warn("open %s %s", pn, wr ? "for read/write" : "read-only");
+ return (-1);
+ }
+
+ /*
+ * read all we can; don't stat and open because more processes
+ * could exit, and we'd miss them
+ */
+ while (1) {
+ /* get one accounting entry and punt if there's an error */
+ rv = read(fd, &ac, sizeof(struct acct));
+ if (rv == -1)
+ warn("error reading %s", pn);
+ else if (rv > 0 && rv < sizeof(struct acct))
+ warnx("short read of accounting data in %s", pn);
+ if (rv != sizeof(struct acct))
+ break;
+
+ /* decode it */
+ ci.ci_calls = 1;
+ for (i = 0; i < sizeof ac.ac_comm && ac.ac_comm[i] != '\0';
+ i++) {
+ char c = ac.ac_comm[i];
+
+ if (!isascii(c) || iscntrl(c)) {
+ ci.ci_comm[i] = '?';
+ ci.ci_flags |= CI_UNPRINTABLE;
+ } else
+ ci.ci_comm[i] = c;
+ }
+ if (ac.ac_flag & AFORK)
+ ci.ci_comm[i++] = '*';
+ ci.ci_comm[i++] = '\0';
+ ci.ci_etime = decode_comp_t(ac.ac_etime);
+ ci.ci_utime = decode_comp_t(ac.ac_utime);
+ ci.ci_stime = decode_comp_t(ac.ac_stime);
+ ci.ci_uid = ac.ac_uid;
+ ci.ci_mem = ac.ac_mem;
+ ci.ci_io = decode_comp_t(ac.ac_io) / AHZ;
+
+ if (!uflag) {
+ /* and enter it into the usracct and pacct databases */
+ if (sflag || (!mflag && !qflag))
+ pacct_add(&ci);
+ if (sflag || (mflag && !qflag))
+ usracct_add(&ci);
+ } else if (!qflag)
+ printf("%6lu %12.2f cpu %12quk mem %12qu io %s\n",
+ ci.ci_uid,
+ (ci.ci_utime + ci.ci_stime) / (double) AHZ,
+ ci.ci_mem, ci.ci_io, ci.ci_comm);
+ }
+
+ /* finally, return the file descriptor for possible truncation */
+ return (fd);
+}
+
+static u_quad_t
+decode_comp_t(comp)
+ comp_t comp;
+{
+ u_quad_t rv;
+
+ /*
+ * for more info on the comp_t format, see:
+ * /usr/src/sys/kern/kern_acct.c
+ * /usr/src/sys/sys/acct.h
+ * /usr/src/usr.bin/lastcomm/lastcomm.c
+ */
+ rv = comp & 0x1fff; /* 13 bit fraction */
+ comp >>= 13; /* 3 bit base-8 exponent */
+ while (comp--)
+ rv <<= 3;
+
+ return (rv);
+}
+
+/* sort commands, doing the right thing in terms of reversals */
+static int
+cmp_comm(s1, s2)
+ const char *s1, *s2;
+{
+ int rv;
+
+ rv = strcmp(s1, s2);
+ if (rv == 0)
+ rv = -1;
+ return (rflag ? rv : -rv);
+}
+
+/* sort by total user and system time */
+static int
+cmp_usrsys(d1, d2)
+ const DBT *d1, *d2;
+{
+ struct cmdinfo *c1, *c2;
+ u_quad_t t1, t2;
+
+ c1 = (struct cmdinfo *) d1->data;
+ c2 = (struct cmdinfo *) d2->data;
+
+ t1 = c1->ci_utime + c1->ci_stime;
+ t2 = c2->ci_utime + c2->ci_stime;
+
+ if (t1 < t2)
+ return -1;
+ else if (t1 == t2)
+ return (cmp_comm(c1->ci_comm, c2->ci_comm));
+ else
+ return 1;
+}
+
+/* sort by average user and system time */
+static int
+cmp_avgusrsys(d1, d2)
+ const DBT *d1, *d2;
+{
+ struct cmdinfo *c1, *c2;
+ double t1, t2;
+
+ c1 = (struct cmdinfo *) d1->data;
+ c2 = (struct cmdinfo *) d2->data;
+
+ t1 = c1->ci_utime + c1->ci_stime;
+ t1 /= (double) (c1->ci_calls ? c1->ci_calls : 1);
+
+ t2 = c2->ci_utime + c2->ci_stime;
+ t2 /= (double) (c2->ci_calls ? c2->ci_calls : 1);
+
+ if (t1 < t2)
+ return -1;
+ else if (t1 == t2)
+ return (cmp_comm(c1->ci_comm, c2->ci_comm));
+ else
+ return 1;
+}
+
+/* sort by total number of disk I/O operations */
+static int
+cmp_dkio(d1, d2)
+ const DBT *d1, *d2;
+{
+ struct cmdinfo *c1, *c2;
+
+ c1 = (struct cmdinfo *) d1->data;
+ c2 = (struct cmdinfo *) d2->data;
+
+ if (c1->ci_io < c2->ci_io)
+ return -1;
+ else if (c1->ci_io == c2->ci_io)
+ return (cmp_comm(c1->ci_comm, c2->ci_comm));
+ else
+ return 1;
+}
+
+/* sort by average number of disk I/O operations */
+static int
+cmp_avgdkio(d1, d2)
+ const DBT *d1, *d2;
+{
+ struct cmdinfo *c1, *c2;
+ double n1, n2;
+
+ c1 = (struct cmdinfo *) d1->data;
+ c2 = (struct cmdinfo *) d2->data;
+
+ n1 = (double) c1->ci_io / (double) (c1->ci_calls ? c1->ci_calls : 1);
+ n2 = (double) c2->ci_io / (double) (c2->ci_calls ? c2->ci_calls : 1);
+
+ if (n1 < n2)
+ return -1;
+ else if (n1 == n2)
+ return (cmp_comm(c1->ci_comm, c2->ci_comm));
+ else
+ return 1;
+}
+
+/* sort by the cpu-storage integral */
+static int
+cmp_cpumem(d1, d2)
+ const DBT *d1, *d2;
+{
+ struct cmdinfo *c1, *c2;
+
+ c1 = (struct cmdinfo *) d1->data;
+ c2 = (struct cmdinfo *) d2->data;
+
+ if (c1->ci_mem < c2->ci_mem)
+ return -1;
+ else if (c1->ci_mem == c2->ci_mem)
+ return (cmp_comm(c1->ci_comm, c2->ci_comm));
+ else
+ return 1;
+}
+
+/* sort by the cpu-time average memory usage */
+static int
+cmp_avgcpumem(d1, d2)
+ const DBT *d1, *d2;
+{
+ struct cmdinfo *c1, *c2;
+ u_quad_t t1, t2;
+ double n1, n2;
+
+ c1 = (struct cmdinfo *) d1->data;
+ c2 = (struct cmdinfo *) d2->data;
+
+ t1 = c1->ci_utime + c1->ci_stime;
+ t2 = c2->ci_utime + c2->ci_stime;
+
+ n1 = (double) c1->ci_mem / (double) (t1 ? t1 : 1);
+ n2 = (double) c2->ci_mem / (double) (t2 ? t2 : 1);
+
+ if (n1 < n2)
+ return -1;
+ else if (n1 == n2)
+ return (cmp_comm(c1->ci_comm, c2->ci_comm));
+ else
+ return 1;
+}
+
+/* sort by the number of invocations */
+static int
+cmp_calls(d1, d2)
+ const DBT *d1, *d2;
+{
+ struct cmdinfo *c1, *c2;
+
+ c1 = (struct cmdinfo *) d1->data;
+ c2 = (struct cmdinfo *) d2->data;
+
+ if (c1->ci_calls < c2->ci_calls)
+ return -1;
+ else if (c1->ci_calls == c2->ci_calls)
+ return (cmp_comm(c1->ci_comm, c2->ci_comm));
+ else
+ return 1;
+}
diff --git a/usr.sbin/sa/pathnames.h b/usr.sbin/sa/pathnames.h
new file mode 100644
index 0000000..861cfec
--- /dev/null
+++ b/usr.sbin/sa/pathnames.h
@@ -0,0 +1,35 @@
+/*
+ * Copyright (c) 1994 Christopher G. Demetriou
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Christopher G. Demetriou.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $Id$
+ */
+
+#define _PATH_ACCT "/var/account/acct"
+#define _PATH_SAVACCT "/var/account/savacct"
+#define _PATH_USRACCT "/var/account/usracct"
diff --git a/usr.sbin/sa/pdb.c b/usr.sbin/sa/pdb.c
new file mode 100644
index 0000000..8576915
--- /dev/null
+++ b/usr.sbin/sa/pdb.c
@@ -0,0 +1,420 @@
+/*
+ * Copyright (c) 1994 Christopher G. Demetriou
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Christopher G. Demetriou.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/acct.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include "extern.h"
+#include "pathnames.h"
+
+static int check_junk __P((struct cmdinfo *));
+static void add_ci __P((const struct cmdinfo *, struct cmdinfo *));
+static void print_ci __P((const struct cmdinfo *, const struct cmdinfo *));
+
+static DB *pacct_db;
+
+int
+pacct_init()
+{
+ DB *saved_pacct_db;
+ int error;
+
+ pacct_db = dbopen(NULL, O_RDWR, 0, DB_BTREE, NULL);
+ if (pacct_db == NULL)
+ return (-1);
+
+ error = 0;
+ if (!iflag) {
+ DBT key, data;
+ int serr, nerr;
+
+ saved_pacct_db = dbopen(_PATH_SAVACCT, O_RDONLY, 0, DB_BTREE,
+ NULL);
+ if (saved_pacct_db == NULL) {
+ error = errno == ENOENT ? 0 : -1;
+ if (error)
+ warn("retrieving process accounting summary");
+ goto out;
+ }
+
+ serr = DB_SEQ(saved_pacct_db, &key, &data, R_FIRST);
+ if (serr < 0) {
+ warn("retrieving process accounting summary");
+ error = -1;
+ goto closeout;
+ }
+ while (serr == 0) {
+ nerr = DB_PUT(pacct_db, &key, &data, 0);
+ if (nerr < 0) {
+ warn("initializing process accounting stats");
+ error = -1;
+ break;
+ }
+
+ serr = DB_SEQ(saved_pacct_db, &key, &data, R_NEXT);
+ if (serr < 0) {
+ warn("retrieving process accounting summary");
+ error = -1;
+ break;
+ }
+ }
+
+closeout: if (DB_CLOSE(saved_pacct_db) < 0) {
+ warn("closing process accounting summary");
+ error = -1;
+ }
+ }
+
+out: if (error != 0)
+ pacct_destroy();
+ return (error);
+}
+
+void
+pacct_destroy()
+{
+ if (DB_CLOSE(pacct_db) < 0)
+ warn("destroying process accounting stats");
+}
+
+int
+pacct_add(ci)
+ const struct cmdinfo *ci;
+{
+ DBT key, data;
+ struct cmdinfo newci;
+ char keydata[sizeof ci->ci_comm];
+ int rv;
+
+ bcopy(ci->ci_comm, &keydata, sizeof keydata);
+ key.data = &keydata;
+ key.size = strlen(keydata);
+
+ rv = DB_GET(pacct_db, &key, &data, 0);
+ if (rv < 0) {
+ warn("get key %s from process accounting stats", ci->ci_comm);
+ return (-1);
+ } else if (rv == 0) { /* it's there; copy whole thing */
+ /* XXX compare size if paranoid */
+ /* add the old data to the new data */
+ bcopy(data.data, &newci, data.size);
+ } else { /* it's not there; zero it and copy the key */
+ bzero(&newci, sizeof newci);
+ bcopy(key.data, newci.ci_comm, key.size);
+ }
+
+ add_ci(ci, &newci);
+
+ data.data = &newci;
+ data.size = sizeof newci;
+ rv = DB_PUT(pacct_db, &key, &data, 0);
+ if (rv < 0) {
+ warn("add key %s to process accounting stats", ci->ci_comm);
+ return (-1);
+ } else if (rv == 1) {
+ warnx("duplicate key %s in process accounting stats",
+ ci->ci_comm);
+ return (-1);
+ }
+
+ return (0);
+}
+
+int
+pacct_update()
+{
+ DB *saved_pacct_db;
+ DBT key, data;
+ int error, serr, nerr;
+
+ saved_pacct_db = dbopen(_PATH_SAVACCT, O_RDWR|O_CREAT|O_TRUNC, 0644,
+ DB_BTREE, NULL);
+ if (saved_pacct_db == NULL) {
+ warn("creating process accounting summary");
+ return (-1);
+ }
+
+ error = 0;
+
+ serr = DB_SEQ(pacct_db, &key, &data, R_FIRST);
+ if (serr < 0) {
+ warn("retrieving process accounting stats");
+ error = -1;
+ }
+ while (serr == 0) {
+ nerr = DB_PUT(saved_pacct_db, &key, &data, 0);
+ if (nerr < 0) {
+ warn("saving process accounting summary");
+ error = -1;
+ break;
+ }
+
+ serr = DB_SEQ(pacct_db, &key, &data, R_NEXT);
+ if (serr < 0) {
+ warn("retrieving process accounting stats");
+ error = -1;
+ break;
+ }
+ }
+
+ if (DB_SYNC(saved_pacct_db, 0) < 0) {
+ warn("syncing process accounting summary");
+ error = -1;
+ }
+ if (DB_CLOSE(saved_pacct_db) < 0) {
+ warn("closing process accounting summary");
+ error = -1;
+ }
+ return error;
+}
+
+void
+pacct_print()
+{
+ BTREEINFO bti;
+ DBT key, data, ndata;
+ DB *output_pacct_db;
+ struct cmdinfo *cip, ci, ci_total, ci_other, ci_junk;
+ int rv;
+
+ bzero(&ci_total, sizeof ci_total);
+ strcpy(ci_total.ci_comm, "");
+ bzero(&ci_other, sizeof ci_other);
+ strcpy(ci_other.ci_comm, "***other");
+ bzero(&ci_junk, sizeof ci_junk);
+ strcpy(ci_junk.ci_comm, "**junk**");
+
+ /*
+ * Retrieve them into new DB, sorted by appropriate key.
+ * At the same time, cull 'other' and 'junk'
+ */
+ bzero(&bti, sizeof bti);
+ bti.compare = sa_cmp;
+ output_pacct_db = dbopen(NULL, O_RDWR, 0, DB_BTREE, &bti);
+ if (output_pacct_db == NULL) {
+ warn("couldn't sort process accounting stats");
+ return;
+ }
+
+ ndata.data = NULL;
+ ndata.size = 0;
+ rv = DB_SEQ(pacct_db, &key, &data, R_FIRST);
+ if (rv < 0)
+ warn("retrieving process accounting stats");
+ while (rv == 0) {
+ cip = (struct cmdinfo *) data.data;
+ bcopy(cip, &ci, sizeof ci);
+
+ /* add to total */
+ add_ci(&ci, &ci_total);
+
+ if (vflag && ci.ci_calls <= cutoff &&
+ (fflag || check_junk(&ci))) {
+ /* put it into **junk** */
+ add_ci(&ci, &ci_junk);
+ goto next;
+ }
+ if (!aflag &&
+ ((ci.ci_flags & CI_UNPRINTABLE) != 0 || ci.ci_calls <= 1)) {
+ /* put into ***other */
+ add_ci(&ci, &ci_other);
+ goto next;
+ }
+ rv = DB_PUT(output_pacct_db, &data, &ndata, 0);
+ if (rv < 0)
+ warn("sorting process accounting stats");
+
+next: rv = DB_SEQ(pacct_db, &key, &data, R_NEXT);
+ if (rv < 0)
+ warn("retrieving process accounting stats");
+ }
+
+ /* insert **junk** and ***other */
+ if (ci_junk.ci_calls != 0) {
+ data.data = &ci_junk;
+ data.size = sizeof ci_junk;
+ rv = DB_PUT(output_pacct_db, &data, &ndata, 0);
+ if (rv < 0)
+ warn("sorting process accounting stats");
+ }
+ if (ci_other.ci_calls != 0) {
+ data.data = &ci_other;
+ data.size = sizeof ci_other;
+ rv = DB_PUT(output_pacct_db, &data, &ndata, 0);
+ if (rv < 0)
+ warn("sorting process accounting stats");
+ }
+
+ /* print out the total */
+ print_ci(&ci_total, &ci_total);
+
+ /* print out; if reversed, print first (smallest) first */
+ rv = DB_SEQ(output_pacct_db, &data, &ndata, rflag ? R_FIRST : R_LAST);
+ if (rv < 0)
+ warn("retrieving process accounting report");
+ while (rv == 0) {
+ cip = (struct cmdinfo *) data.data;
+ bcopy(cip, &ci, sizeof ci);
+
+ print_ci(&ci, &ci_total);
+
+ rv = DB_SEQ(output_pacct_db, &data, &ndata,
+ rflag ? R_NEXT : R_PREV);
+ if (rv < 0)
+ warn("retrieving process accounting report");
+ }
+ DB_CLOSE(output_pacct_db);
+}
+
+static int
+check_junk(cip)
+ struct cmdinfo *cip;
+{
+ char *cp;
+ size_t len;
+
+ fprintf(stderr, "%s (%qu) -- ", cip->ci_comm, cip->ci_calls);
+ cp = fgetln(stdin, &len);
+
+ return (cp && (cp[0] == 'y' || cp[0] == 'Y')) ? 1 : 0;
+}
+
+static void
+add_ci(fromcip, tocip)
+ const struct cmdinfo *fromcip;
+ struct cmdinfo *tocip;
+{
+ tocip->ci_calls += fromcip->ci_calls;
+ tocip->ci_etime += fromcip->ci_etime;
+ tocip->ci_utime += fromcip->ci_utime;
+ tocip->ci_stime += fromcip->ci_stime;
+ tocip->ci_mem += fromcip->ci_mem;
+ tocip->ci_io += fromcip->ci_io;
+}
+
+static void
+print_ci(cip, totalcip)
+ const struct cmdinfo *cip, *totalcip;
+{
+ double t, c;
+ int uflow;
+
+ c = cip->ci_calls ? cip->ci_calls : 1;
+ t = (cip->ci_utime + cip->ci_stime) / (double) AHZ;
+ if (t < 0.01) {
+ t = 0.01;
+ uflow = 1;
+ } else
+ uflow = 0;
+
+ printf("%8qu ", cip->ci_calls);
+ if (cflag) {
+ if (cip != totalcip)
+ printf(" %4.2f%% ",
+ cip->ci_calls / (double) totalcip->ci_calls);
+ else
+ printf(" %4s ", "");
+ }
+
+ if (jflag)
+ printf("%11.2fre ", cip->ci_etime / (double) (AHZ * c));
+ else
+ printf("%11.2fre ", cip->ci_etime / (60.0 * AHZ));
+ if (cflag) {
+ if (cip != totalcip)
+ printf(" %4.2f%% ",
+ cip->ci_etime / (double) totalcip->ci_etime);
+ else
+ printf(" %4s ", "");
+ }
+
+ if (!lflag) {
+ if (jflag)
+ printf("%11.2fcp ", t / (double) cip->ci_calls);
+ else
+ printf("%11.2fcp ", t / 60.0);
+ if (cflag) {
+ if (cip != totalcip)
+ printf(" %4.2f%% ",
+ (cip->ci_utime + cip->ci_stime) / (double)
+ (totalcip->ci_utime + totalcip->ci_stime));
+ else
+ printf(" %4s ", "");
+ }
+ } else {
+ if (jflag)
+ printf("%11.2fu ", cip->ci_utime / (double) (AHZ * c));
+ else
+ printf("%11.2fu ", cip->ci_utime / (60.0 * AHZ));
+ if (cflag) {
+ if (cip != totalcip)
+ printf(" %4.2f%% ", cip->ci_utime / (double) totalcip->ci_utime);
+ else
+ printf(" %4s ", "");
+ }
+ if (jflag)
+ printf("%11.2fs ", cip->ci_stime / (double) (AHZ * c));
+ else
+ printf("%11.2fs ", cip->ci_stime / (60.0 * AHZ));
+ if (cflag) {
+ if (cip != totalcip)
+ printf(" %4.2f%% ", cip->ci_stime / (double) totalcip->ci_stime);
+ else
+ printf(" %4s ", "");
+ }
+ }
+
+ if (tflag)
+ if (!uflow)
+ printf("%8.2fre/cp ", cip->ci_etime / (double) (cip->ci_utime + cip->ci_stime));
+ else
+ printf("*ignore* ");
+
+ if (Dflag)
+ printf("%10qutio ", cip->ci_io);
+ else
+ printf("%8.0favio ", cip->ci_io / c);
+
+ if (Kflag)
+ printf("%10quk*sec ", cip->ci_mem);
+ else
+ printf("%8.0fk ", cip->ci_mem / t);
+
+ printf(" %s\n", cip->ci_comm);
+}
diff --git a/usr.sbin/sa/sa.8 b/usr.sbin/sa/sa.8
new file mode 100644
index 0000000..fd6e8a4
--- /dev/null
+++ b/usr.sbin/sa/sa.8
@@ -0,0 +1,238 @@
+.\"
+.\" Copyright (c) 1994 Christopher G. Demetriou
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by Christopher G. Demetriou.
+.\" 3. The name of the author may not be used to endorse or promote products
+.\" derived from this software without specific prior written permission
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+.\"
+.\" $Id: sa.8,v 1.7 1997/02/22 16:13:12 peter Exp $
+.\"
+.Dd February 25, 1994
+.Dt SA 8
+.Os
+.Sh NAME
+.Nm sa
+.Nd print system accounting statistics
+.Sh SYNOPSIS
+.Nm sa
+.Op Fl abcdDfijkKlmnqrstu
+.Op Fl v Ar cutoff
+.Op Ar
+.Sh DESCRIPTION
+The
+.Nm
+utility reports on, cleans up,
+and generally maintains system
+accounting files.
+.Pp
+.Nm Sa
+is able to condense the information in
+.Pa /var/account/acct
+into the summary files
+.Pa /var/account/savacct
+and
+.Pa /var/account/usracct ,
+which contain system statistics according
+to command name and login id, respectively.
+This condensation is desirable because on a
+large system,
+.Pa /var/account/acct
+can grow by hundreds of blocks per day.
+The summary files are normally read before
+the accounting file, so that reports include
+all available information.
+.Pp
+If file names are supplied, they are read instead of
+.Pa /var/account/acct .
+After each file is read, if the summary
+files are being updated, an updated summary will
+be saved to disk. Only one report is printed,
+after the last file is processed.
+.Pp
+The labels used in the output indicate the following, except
+where otherwise specified by individual options:
+.Bl -tag -width k*sec
+.It Dv avio
+Average number of I/O operations per execution
+.It Dv cp
+Sum of user and system time, in minutes
+.It Dv cpu
+Same as
+.Dv cp
+.It Dv k
+CPU-time averaged core usage, in 1k units
+.It Dv k*sec
+CPU storage integral, in 1k-core seconds
+.It Dv re
+Real time, in minutes
+.It Dv s
+System time, in minutes
+.It Dv tio
+Total number of I/O operations
+.It Dv u
+User time, in minutes
+.El
+.Pp
+The options to
+.Nm
+are:
+.Bl -tag -width Ds
+.It Fl a
+List all command names, including those containing unprintable
+characters and those used only once. By default,
+.Nm
+places all names containing unprintable characters and
+those used only once under the name ``***other''.
+.It Fl b
+If printing command statistics, sort output by the sum of user and system
+time divided by number of calls.
+.It Fl c
+In addition to the number of calls and the user, system and real times
+for each command, print their percentage of the total over all commands.
+.It Fl d
+If printing command statistics, sort by the average number of disk
+I/O operations. If printing user statistics, print the average number of
+disk I/O operations per user.
+.It Fl D
+If printing command statistics, sort and print by the total number
+of disk I/O operations.
+.It Fl f
+Force no interactive threshold comparison with the
+.Fl v
+option.
+.It Fl i
+Do not read in the summary files.
+.It Fl j
+Instead of the total minutes per category, give seconds per call.
+.It Fl k
+If printing command statistics, sort by the cpu-time average memory
+usage. If printing user statistics, print the cpu-time average
+memory usage.
+.It Fl K
+If printing command statistics, print and sort by the cpu-storage integral.
+.It Fl l
+Separate system and user time; normally they are combined.
+.It Fl m
+Print per-user statistics rather than per-command statistics.
+.It Fl n
+Sort by number of calls.
+.It Fl q
+Create no output other than error messages.
+.It Fl r
+Reverse order of sort.
+.It Fl s
+Truncate the accounting files when done and merge their data
+into the summary files.
+.It Fl t
+For each command, report the ratio of real time to the sum
+of user and system cpu times.
+If the cpu time is too small to report, ``*ignore*'' appears in
+this field.
+.It Fl u
+Superseding all other flags, for each entry
+in the accounting file, print the user ID, total seconds of cpu usage,
+total memory usage, number of I/O operations performed, and
+command name.
+.It Fl v Ar cutoff
+For each command used
+.Ar cutoff
+times or fewer, print the command name and await a reply
+from the terminal. If the reply begins with ``y'', add
+the command to the category ``**junk**''. This flag is
+used to strip garbage from the report.
+.El
+.Pp
+By default, per-command statistics will be printed. The number of
+calls, the total elapsed time in minutes, total cpu and user time
+in minutes, average number of I/O operations, and CPU-time
+averaged core usage will be printed. If the
+.Fl m
+option is specified, per-user statistics will be printed, including
+the user name, the number of commands invoked, total cpu time used
+(in minutes), total number of I/O operations, and CPU storage integral
+for each user. If the
+.Fl u
+option is specified, the uid, user and system time (in seconds),
+CPU storage integral, I/O usage, and command name will be printed
+for each entry in the accounting data file.
+.Pp
+If the
+.Fl u
+flag is specified, all flags other than
+.Fl q
+are ignored. If the
+.Fl m
+flag is specified, only the
+.Fl b ,
+.Fl d ,
+.Fl i ,
+.Fl k ,
+.Fl q ,
+and
+.Fl s
+flags are honored.
+.Pp
+The
+.Nm
+utility exits 0 on success, and >0 if an error occurs.
+.Sh FILES
+.Bl -tag -width /var/account/usracct -compact
+.It Pa /var/account/acct
+raw accounting data file
+.It Pa /var/account/savacct
+per-command accounting summary database
+.It Pa /var/account/usracct
+per-user accounting summary database
+.El
+.Sh SEE ALSO
+.Xr lastcomm 1 ,
+.Xr acct 5 ,
+.Xr ac 8 ,
+.Xr accton 8
+.Sh BUGS
+The number of options to this program is absurd, especially considering
+that there's not much logic behind their lettering.
+.Pp
+The field labels should be more consistent.
+.Pp
+The VM system does not record the CPU storage integral.
+.Sh CAVEATS
+While the behavior of the options in this version of
+.Nm
+was modeled after the original version, there are some intentional
+differences and undoubtedly some unintentional ones as well. In
+particular, the
+.Fl q
+option has been added, and the
+.Fl m
+option now understands more options than it used to.
+.Pp
+The formats of the summary files created by this version of
+.Nm
+are very different than the those used by the original version.
+This is not considered a problem, however, because the accounting record
+format has changed as well (since user ids are now 32 bits).
+.Sh AUTHOR
+.An Chris G. Demetriou Aq cgd@postgres.berkeley.edu
diff --git a/usr.sbin/sa/usrdb.c b/usr.sbin/sa/usrdb.c
new file mode 100644
index 0000000..d3a0654
--- /dev/null
+++ b/usr.sbin/sa/usrdb.c
@@ -0,0 +1,284 @@
+/*
+ * Copyright (c) 1994 Christopher G. Demetriou
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Christopher G. Demetriou.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/acct.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "extern.h"
+#include "pathnames.h"
+
+static int uid_compare __P((const DBT *, const DBT *));
+
+static DB *usracct_db;
+
+int
+usracct_init()
+{
+ DB *saved_usracct_db;
+ BTREEINFO bti;
+ int error;
+
+ bzero(&bti, sizeof bti);
+ bti.compare = uid_compare;
+
+ usracct_db = dbopen(NULL, O_RDWR, 0, DB_BTREE, &bti);
+ if (usracct_db == NULL)
+ return (-1);
+
+ error = 0;
+ if (!iflag) {
+ DBT key, data;
+ int serr, nerr;
+
+ saved_usracct_db = dbopen(_PATH_USRACCT, O_RDONLY, 0, DB_BTREE,
+ &bti);
+ if (saved_usracct_db == NULL) {
+ error = (errno == ENOENT) ? 0 : -1;
+ if (error)
+ warn("retrieving user accounting summary");
+ goto out;
+ }
+
+ serr = DB_SEQ(saved_usracct_db, &key, &data, R_FIRST);
+ if (serr < 0) {
+ warn("retrieving user accounting summary");
+ error = -1;
+ goto closeout;
+ }
+ while (serr == 0) {
+ nerr = DB_PUT(usracct_db, &key, &data, 0);
+ if (nerr < 0) {
+ warn("initializing user accounting stats");
+ error = -1;
+ break;
+ }
+
+ serr = DB_SEQ(saved_usracct_db, &key, &data, R_NEXT);
+ if (serr < 0) {
+ warn("retrieving user accounting summary");
+ error = -1;
+ break;
+ }
+ }
+
+closeout:
+ if (DB_CLOSE(saved_usracct_db) < 0) {
+ warn("closing user accounting summary");
+ error = -1;
+ }
+ }
+
+out:
+ if (error != 0)
+ usracct_destroy();
+ return (error);
+}
+
+void
+usracct_destroy()
+{
+ if (DB_CLOSE(usracct_db) < 0)
+ warn("destroying user accounting stats");
+}
+
+int
+usracct_add(ci)
+ const struct cmdinfo *ci;
+{
+ DBT key, data;
+ struct userinfo newui;
+ u_long uid;
+ int rv;
+
+ uid = ci->ci_uid;
+ key.data = &uid;
+ key.size = sizeof uid;
+
+ rv = DB_GET(usracct_db, &key, &data, 0);
+ if (rv < 0) {
+ warn("get key %d from user accounting stats", uid);
+ return (-1);
+ } else if (rv == 0) { /* it's there; copy whole thing */
+ /* add the old data to the new data */
+ bcopy(data.data, &newui, data.size);
+ if (newui.ui_uid != uid) {
+ warnx("key %d != expected record number %d",
+ newui.ui_uid, uid);
+ warnx("inconsistent user accounting stats");
+ return (-1);
+ }
+ } else { /* it's not there; zero it and copy the key */
+ bzero(&newui, sizeof newui);
+ newui.ui_uid = ci->ci_uid;
+ }
+
+ newui.ui_calls += ci->ci_calls;
+ newui.ui_utime += ci->ci_utime;
+ newui.ui_stime += ci->ci_stime;
+ newui.ui_mem += ci->ci_mem;
+ newui.ui_io += ci->ci_io;
+
+ data.data = &newui;
+ data.size = sizeof newui;
+ rv = DB_PUT(usracct_db, &key, &data, 0);
+ if (rv < 0) {
+ warn("add key %d to user accounting stats", uid);
+ return (-1);
+ } else if (rv != 0) {
+ warnx("DB_PUT returned 1");
+ return (-1);
+ }
+
+ return (0);
+}
+
+int
+usracct_update()
+{
+ DB *saved_usracct_db;
+ DBT key, data;
+ BTREEINFO bti;
+ int error, serr, nerr;
+
+ bzero(&bti, sizeof bti);
+ bti.compare = uid_compare;
+
+ saved_usracct_db = dbopen(_PATH_USRACCT, O_RDWR|O_CREAT|O_TRUNC, 0644,
+ DB_BTREE, &bti);
+ if (saved_usracct_db == NULL) {
+ warn("creating user accounting summary");
+ return (-1);
+ }
+
+ error = 0;
+
+ serr = DB_SEQ(usracct_db, &key, &data, R_FIRST);
+ if (serr < 0) {
+ warn("retrieving user accounting stats");
+ error = -1;
+ }
+ while (serr == 0) {
+ nerr = DB_PUT(saved_usracct_db, &key, &data, 0);
+ if (nerr < 0) {
+ warn("saving user accounting summary");
+ error = -1;
+ break;
+ }
+
+ serr = DB_SEQ(usracct_db, &key, &data, R_NEXT);
+ if (serr < 0) {
+ warn("retrieving user accounting stats");
+ error = -1;
+ break;
+ }
+ }
+
+ if (DB_SYNC(saved_usracct_db, 0) < 0) {
+ warn("syncing process accounting summary");
+ error = -1;
+ }
+ if (DB_CLOSE(saved_usracct_db) < 0) {
+ warn("closing process accounting summary");
+ error = -1;
+ }
+ return error;
+}
+
+void
+usracct_print()
+{
+ DBT key, data;
+ struct userinfo *ui;
+ double t;
+ int rv;
+
+ rv = DB_SEQ(usracct_db, &key, &data, R_FIRST);
+ if (rv < 0)
+ warn("retrieving user accounting stats");
+
+ while (rv == 0) {
+ ui = (struct userinfo *) data.data;
+
+ printf("%-8s %9qu ",
+ user_from_uid(ui->ui_uid, 0), ui->ui_calls);
+
+ t = (double) (ui->ui_utime + ui->ui_stime) /
+ (double) AHZ;
+ if (t < 0.0001) /* kill divide by zero */
+ t = 0.0001;
+
+ printf("%12.2f%s ", t / 60.0, "cpu");
+
+ /* ui->ui_calls is always != 0 */
+ if (dflag)
+ printf("%12qu%s", ui->ui_io / ui->ui_calls, "avio");
+ else
+ printf("%12qu%s", ui->ui_io, "tio");
+
+ /* t is always >= 0.0001; see above */
+ if (kflag)
+ printf("%12qu%s", ui->ui_mem / t, "k");
+ else
+ printf("%12qu%s", ui->ui_mem, "k*sec");
+
+ printf("\n");
+
+ rv = DB_SEQ(usracct_db, &key, &data, R_NEXT);
+ if (rv < 0)
+ warn("retrieving user accounting stats");
+ }
+}
+
+static int
+uid_compare(k1, k2)
+ const DBT *k1, *k2;
+{
+ u_long d1, d2;
+
+ bcopy(k1->data, &d1, sizeof d1);
+ bcopy(k2->data, &d2, sizeof d2);
+
+ if (d1 < d2)
+ return -1;
+ else if (d1 == d2)
+ return 0;
+ else
+ return 1;
+}
diff --git a/usr.sbin/sade/Makefile b/usr.sbin/sade/Makefile
new file mode 100644
index 0000000..fd269a0
--- /dev/null
+++ b/usr.sbin/sade/Makefile
@@ -0,0 +1,79 @@
+PROG= sysinstall
+MAN8= sysinstall.8
+BINDIR=/stand
+
+CLEANFILES+= makedevs.c rtermcap rtermcap.tmp dumpnlist
+CLEANFILES+= keymap.tmp keymap.h
+
+.PATH: ${.CURDIR}/../disklabel ${.CURDIR}/../../usr.bin/cksum
+
+SRCS= anonFTP.c attr.c cdrom.c command.c config.c devices.c \
+ disks.c dispatch.c dist.c dmenu.c doc.c dos.c dosio.c floppy.c \
+ ftp.c globals.c index.c install.c installUpgrade.c keymap.c \
+ label.c lndir.c main.c makedevs.c media.c menus.c misc.c \
+ msg.c network.c nfs.c options.c package.c register.c system.c \
+ tape.c tcpip.c termcap.c ufs.c user.c variable.c wizard.c \
+ uc_eisa.c uc_isa.c uc_kmem.c uc_list.c uc_main.c uc_pci.c \
+ uc_scsi.c keymap.h
+
+CFLAGS+= -Wall -I${.CURDIR}/../../gnu/lib/libdialog -I${.OBJDIR}
+CFLAGS+= -I${.CURDIR}/../../sys
+CFLAGS+= -DUC_PRIVATE -DKERN_NO_SYMBOLS -DSAVE_USERCONFIG
+#CFLAGS+= -DUSE_XIG_ENVIRONMENT
+
+DPADD= ${LIBDIALOG} ${LIBNCURSES} ${LIBMYTINFO} ${LIBUTIL} ${LIBDISK} ${LIBFTPIO}
+LDADD= -ldialog -lncurses -lmytinfo -lutil -ldisk -lftpio
+
+makedevs.c: Makefile rtermcap keymap.h
+ rm -f makedevs.tmp
+ echo '#include <sys/types.h>' > makedevs.tmp
+ ./rtermcap ansi | \
+ file2c 'const char termcap_ansi[] = {' ',0};' \
+ >> makedevs.tmp
+ ./rtermcap cons25 | \
+ file2c 'const char termcap_cons25[] = {' ',0};' \
+ >> makedevs.tmp
+ ./rtermcap cons25-m | \
+ file2c 'const char termcap_cons25_m[] = {' ',0};' \
+ >> makedevs.tmp
+ ./rtermcap cons25r | \
+ file2c 'const char termcap_cons25r[] = {' ',0};' \
+ >> makedevs.tmp
+ ./rtermcap cons25r-m | \
+ file2c 'const char termcap_cons25r_m[] = {' ',0};' \
+ >> makedevs.tmp
+ ./rtermcap cons25l1 | \
+ file2c 'const char termcap_cons25l1[] = {' ',0};' \
+ >> makedevs.tmp
+ ./rtermcap cons25l1-m | \
+ file2c 'const char termcap_cons25l1_m[] = {' ',0};' \
+ >> makedevs.tmp
+ ./rtermcap vt100 | \
+ file2c 'const char termcap_vt100[] = {' ',0};' \
+ >> makedevs.tmp
+ mv makedevs.tmp makedevs.c
+
+rtermcap: ${.CURDIR}/rtermcap.c
+ ${CC} -o rtermcap ${.CURDIR}/rtermcap.c -ltermcap
+
+
+KEYMAPS= be.iso br275.iso danish.iso fr.iso german.iso it.iso jp.106 \
+ norwegian.iso ru.koi8-r spanish.iso swedish.iso \
+ swissgerman.iso uk.iso us.dvorak us.iso
+
+
+keymap.h:
+ rm -f keymap.tmp
+ for map in ${KEYMAPS} ; do \
+ kbdcontrol -L $$map >> keymap.tmp ; \
+ done
+ echo "static struct keymapInfo keymapInfos[] = {" >> keymap.tmp
+ for map in ${KEYMAPS} ; do \
+ echo -n ' { "'$$map'", ' >> keymap.tmp ; \
+ echo "&keymap_$$map }," | tr '[-.]' '_' >> keymap.tmp ; \
+ done
+ ( echo " { 0 }"; echo "};" ; echo "" ) >> keymap.tmp
+ mv keymap.tmp keymap.h
+
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/sade/command.c b/usr.sbin/sade/command.c
new file mode 100644
index 0000000..b14f913
--- /dev/null
+++ b/usr.sbin/sade/command.c
@@ -0,0 +1,179 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last program in the `sysinstall' line - the next
+ * generation being essentially a complete rewrite.
+ *
+ * $Id$
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include "sysinstall.h"
+
+#define MAX_NUM_COMMANDS 10
+
+typedef struct {
+ char key[FILENAME_MAX];
+ struct {
+ enum { CMD_SHELL, CMD_FUNCTION } type;
+ void *ptr, *data;
+ } cmds[MAX_NUM_COMMANDS];
+ int ncmds;
+} Command;
+
+#define MAX_CMDS 200
+static Command *commandStack[MAX_CMDS];
+int numCommands;
+
+/* Nuke the command stack */
+void
+command_clear(void)
+{
+ int i, j;
+
+ for (i = 0; i < numCommands; i++)
+ for (j = 0; j < commandStack[i]->ncmds; j++)
+ if (commandStack[i]->cmds[j].type == CMD_SHELL)
+ free(commandStack[i]->cmds[j].ptr);
+ free(commandStack[i]);
+ numCommands = 0;
+}
+
+static void
+addit(char *key, int type, void *cmd, void *data)
+{
+ int i;
+
+ /* First, look for the key already present and add a command to it if found */
+ for (i = 0; i < numCommands; i++) {
+ if (!strcmp(commandStack[i]->key, key)) {
+ if (commandStack[i]->ncmds == MAX_NUM_COMMANDS)
+ msgFatal("More than %d commands stacked up behind %s??", MAX_NUM_COMMANDS, key);
+ commandStack[i]->cmds[commandStack[i]->ncmds].type = type;
+ commandStack[i]->cmds[commandStack[i]->ncmds].ptr = cmd;
+ commandStack[i]->cmds[commandStack[i]->ncmds].data = data;
+ ++(commandStack[i]->ncmds);
+ return;
+ }
+ }
+ if (numCommands == MAX_CMDS)
+ msgFatal("More than %d commands accumulated??", MAX_CMDS);
+
+ /* If we fell to here, it's a new key */
+ commandStack[numCommands] = safe_malloc(sizeof(Command));
+ strcpy(commandStack[numCommands]->key, key);
+ commandStack[numCommands]->ncmds = 1;
+ commandStack[numCommands]->cmds[0].type = type;
+ commandStack[numCommands]->cmds[0].ptr = cmd;
+ commandStack[numCommands]->cmds[0].data = data;
+ ++numCommands;
+}
+
+/* Add a shell command under a given key */
+void
+command_shell_add(char *key, char *fmt, ...)
+{
+ va_list args;
+ char *cmd;
+
+ cmd = (char *)safe_malloc(256);
+ va_start(args, fmt);
+ vsnprintf(cmd, 256, fmt, args);
+ va_end(args);
+
+ addit(key, CMD_SHELL, cmd, NULL);
+}
+
+/* Add a shell command under a given key */
+void
+command_func_add(char *key, commandFunc func, void *data)
+{
+ addit(key, CMD_FUNCTION, func, data);
+}
+
+static int
+sort_compare(Command *p1, Command *p2)
+{
+ if (!p1 && !p2)
+ return 0;
+ else if (!p1 && p2) /* NULL has a "greater" value for commands */
+ return 1;
+ else if (p1 && !p2)
+ return -1;
+ else
+ return strcmp(p1->key, p2->key);
+}
+
+void
+command_sort(void)
+{
+ int i, j;
+
+ commandStack[numCommands] = NULL;
+ /* Just do a crude bubble sort since the list is small */
+ for (i = 0; i < numCommands; i++) {
+ for (j = 0; j < numCommands; j++) {
+ if (sort_compare(commandStack[j], commandStack[j + 1]) > 0) {
+ Command *tmp = commandStack[j];
+
+ commandStack[j] = commandStack[j + 1];
+ commandStack[j + 1] = tmp;
+ }
+ }
+ }
+}
+
+/* Run all accumulated commands in sorted order */
+void
+command_execute(void)
+{
+ int i, j, ret;
+ commandFunc func;
+
+ for (i = 0; i < numCommands; i++) {
+ for (j = 0; j < commandStack[i]->ncmds; j++) {
+ /* If it's a shell command, run system on it */
+ if (commandStack[i]->cmds[j].type == CMD_SHELL) {
+ msgNotify("Doing %s", commandStack[i]->cmds[j].ptr);
+ ret = vsystem((char *)commandStack[i]->cmds[j].ptr);
+ if (isDebug())
+ msgDebug("Command `%s' returns status %d\n", commandStack[i]->cmds[j].ptr, ret);
+ }
+ else {
+ /* It's a function pointer - call it with the key and the data */
+ func = (commandFunc)commandStack[i]->cmds[j].ptr;
+ if (isDebug())
+ msgDebug("%x: Execute(%s, %s)", func, commandStack[i]->key, commandStack[i]->cmds[j].data);
+ ret = (*func)(commandStack[i]->key, commandStack[i]->cmds[j].data);
+ if (isDebug())
+ msgDebug("Function @ %x returns status %d\n", commandStack[i]->cmds[j].ptr, ret);
+ }
+ }
+ }
+}
diff --git a/usr.sbin/sade/config.c b/usr.sbin/sade/config.c
new file mode 100644
index 0000000..2b37c89
--- /dev/null
+++ b/usr.sbin/sade/config.c
@@ -0,0 +1,869 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last program in the `sysinstall' line - the next
+ * generation being essentially a complete rewrite.
+ *
+ * $Id: config.c,v 1.103 1997/08/18 21:47:31 jkh Exp $
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include "sysinstall.h"
+#include <sys/disklabel.h>
+#include <sys/wait.h>
+#include <sys/errno.h>
+#include <sys/ioctl.h>
+#include <sys/fcntl.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <sys/mount.h>
+
+static Chunk *chunk_list[MAX_CHUNKS];
+static int nchunks;
+static int rootdev_is_od;
+
+/* arg to sort */
+static int
+chunk_compare(Chunk *c1, Chunk *c2)
+{
+ if (!c1 && !c2)
+ return 0;
+ else if (!c1 && c2)
+ return 1;
+ else if (c1 && !c2)
+ return -1;
+ else if (!c1->private_data && !c2->private_data)
+ return 0;
+ else if (c1->private_data && !c2->private_data)
+ return 1;
+ else if (!c1->private_data && c2->private_data)
+ return -1;
+ else
+ return strcmp(((PartInfo *)(c1->private_data))->mountpoint, ((PartInfo *)(c2->private_data))->mountpoint);
+}
+
+static void
+chunk_sort(void)
+{
+ int i, j;
+
+ for (i = 0; i < nchunks; i++) {
+ for (j = 0; j < nchunks; j++) {
+ if (chunk_compare(chunk_list[j], chunk_list[j + 1]) > 0) {
+ Chunk *tmp = chunk_list[j];
+
+ chunk_list[j] = chunk_list[j + 1];
+ chunk_list[j + 1] = tmp;
+ }
+ }
+ }
+}
+
+static void
+check_rootdev(Chunk **list, int n)
+{
+ int i;
+ Chunk *c;
+
+ rootdev_is_od = 0;
+ for (i = 0; i < n; i++) {
+ c = *list++;
+ if (c->type == part && (c->flags & CHUNK_IS_ROOT)
+ && strncmp(c->disk->name, "od", 2) == 0)
+ rootdev_is_od = 1;
+ }
+}
+
+static char *
+name_of(Chunk *c1)
+{
+ static char rootname[32];
+
+ /* Our boot blocks can't deal with root partitions on slices - need the compatbility name */
+ if (c1->type == part && c1->flags & CHUNK_IS_ROOT) {
+ sprintf(rootname, "%sa", c1->disk->name);
+ return rootname;
+ }
+ else
+ return c1->name;
+}
+
+static char *
+mount_point(Chunk *c1)
+{
+ if (c1->type == part && c1->subtype == FS_SWAP)
+ return "none";
+ else if (c1->type == part || c1->type == fat)
+ return ((PartInfo *)c1->private_data)->mountpoint;
+ return "/bogus";
+}
+
+static char *
+fstype(Chunk *c1)
+{
+ if (c1->type == fat)
+ return "msdos";
+ else if (c1->type == part) {
+ if (c1->subtype != FS_SWAP)
+ return "ufs";
+ else
+ return "swap";
+ }
+ return "bogus";
+}
+
+static char *
+fstype_short(Chunk *c1)
+{
+ if (c1->type == part) {
+ if (c1->subtype != FS_SWAP) {
+ if (rootdev_is_od == 0 && strncmp(c1->name, "od", 2) == 0)
+ return "rw,noauto";
+ else
+ return "rw";
+ }
+ else
+ return "sw";
+ }
+ else if (c1->type == fat) {
+ if (strncmp(c1->name, "od", 2) == 0)
+ return "ro,noauto";
+ else
+ return "ro";
+ }
+ return "bog";
+}
+
+static int
+seq_num(Chunk *c1)
+{
+ if (c1->type == part && c1->subtype != FS_SWAP) {
+ if (rootdev_is_od == 0 && strncmp(c1->name, "od", 2) == 0)
+ return 0;
+ else if (c1->flags & CHUNK_IS_ROOT)
+ return 1;
+ else
+ return 2;
+ }
+ return 0;
+}
+
+int
+configFstab(void)
+{
+ 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]));
+ Mkdir("/proc");
+ fprintf(fstab, "proc\t\t\t/proc\t\tprocfs\trw\t\t0\t0\n");
+
+ /* Now look for the CDROMs */
+ devs = deviceFind(NULL, DEVICE_TYPE_CDROM);
+ cnt = deviceCount(devs);
+
+ /* Write the first one out as /cdrom */
+ if (cnt) {
+ if (Mkdir("/cdrom")) {
+ msgConfirm("Unable to make mount point for: /cdrom");
+ }
+ else
+ fprintf(fstab, "/dev/%s\t\t/cdrom\t\tcd9660\tro,noauto\t0\t0\n", devs[0]->name);
+ }
+
+ /* Write the others out as /cdrom<n> */
+ for (i = 1; i < cnt; i++) {
+ char cdname[10];
+
+ sprintf(cdname, "/cdrom%d", i);
+ if (Mkdir(cdname)) {
+ msgConfirm("Unable to make mount point for: %s", cdname);
+ }
+ else
+ fprintf(fstab, "/dev/%s\t\t%s\tcd9660\tro,noauto\t0\t0\n", devs[i]->name, cdname);
+ }
+ 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 */
+
+/* Load the environment from an rc.conf file */
+void
+configEnvironmentRC_conf(char *config)
+{
+ 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 (strlen(cp))
+ variable_set2(lines[i], cp);
+ }
+ free(lines[i]);
+ }
+}
+
+/* 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 = FALSE;
+
+ if (!strncmp(lines[i], "domain", 6))
+ variable_set2(VAR_DOMAINNAME, string_skipwhite(string_prune(lines[i] + 6)));
+ else if (!strncmp(lines[i], "nameserver", 10) && !name_set) {
+ /* Only take the first nameserver setting - we're lame */
+ variable_set2(VAR_NAMESERVER, string_skipwhite(string_prune(lines[i] + 10)));
+ name_set = TRUE;
+ }
+ free(lines[i]);
+ }
+}
+
+/* Version of below for dispatch routines */
+int
+configRC(dialogMenuItem *unused)
+{
+ configRC_conf("/etc/rc.conf");
+ return DITEM_SUCCESS;
+}
+
+/*
+ * This sucks in /etc/rc.conf, substitutes anything needing substitution, then
+ * writes it all back out. It's pretty gross and needs re-writing at some point.
+ */
+void
+configRC_conf(char *config)
+{
+ FILE *fp;
+ char *lines[MAX_LINES], *cp;
+ Variable *v;
+ int i, nlines, len;
+
+ nlines = readConfig(config, lines, MAX_LINES);
+ if (nlines == -1)
+ return;
+
+ /* Now do variable substitutions */
+ for (v = VarHead; v; v = v->next) {
+ for (i = 0; i < nlines; i++) {
+ /* Skip the comments & non-variable settings */
+ if (lines[i][0] == '#' || !(cp = index(lines[i], '=')))
+ continue;
+ len = strlen(v->name);
+ if (!strncmp(lines[i], v->name, cp - lines[i]) && (cp - lines[i]) == len) {
+ char *cp2, *comment = NULL;
+
+ /* If trailing comment, try and preserve it */
+ if ((index(lines[i], '#')) != NULL) {
+ /* Find quotes */
+ if ((cp2 = index(cp, '"')) || (cp2 = index(cp, '\047')))
+ cp2 = index(cp2 + 1, *cp2);
+ if (cp2 && strlen(cp2 + 1)) {
+ comment = alloca(strlen(cp2));
+ strcpy(comment, cp2 + 1);
+ }
+ }
+ free(lines[i]);
+ lines[i] = (char *)malloc(strlen(v->name) + strlen(v->value) + (comment ? strlen(comment) : 0) + 10);
+ if (comment)
+ sprintf(lines[i], "%s=\"%s\"%s", v->name, v->value, comment);
+ else
+ sprintf(lines[i], "%s=\"%s\"\n", v->name, v->value);
+ }
+ }
+ }
+
+ /* Now write it all back out again */
+ if (isDebug())
+ msgDebug("Writing configuration changes to %s file..", config);
+ if (Fake)
+ fp = fdopen(DebugFD, "w");
+ else {
+ (void)vsystem("cp %s %s.previous", config, config);
+ fp = fopen(config, "w");
+ }
+ for (i = 0; i < nlines; i++) {
+ fprintf(fp, lines[i]);
+ /* Stand by for bogus special case handling - we try to dump the interface specs here */
+ if (!strncmp(lines[i], VAR_INTERFACES, strlen(VAR_INTERFACES))) {
+ Device **devp;
+ int j, cnt;
+
+ devp = deviceFind(NULL, DEVICE_TYPE_NETWORK);
+ cnt = deviceCount(devp);
+ for (j = 0; j < cnt; j++) {
+ char iname[255], toadd[512];
+ int k, addit = TRUE;
+
+ if (!strncmp(devp[j]->name, "ppp", 3) || !strncmp(devp[j]->name, "tun", 3))
+ continue;
+
+ snprintf(iname, 255, "%s%s", VAR_IFCONFIG, devp[j]->name);
+ if ((cp = variable_get(iname))) {
+ snprintf(toadd, sizeof toadd, "%s=\"%s\"\n", iname, cp);
+ for (k = 0; k < nlines; k++) {
+ if (!strcmp(lines[k], toadd)) {
+ addit = FALSE;
+ break;
+ }
+ }
+ if (addit)
+ fprintf(fp, toadd);
+ }
+ }
+ }
+ free(lines[i]);
+ }
+ fclose(fp);
+}
+
+int
+configSaver(dialogMenuItem *self)
+{
+ variable_set((char *)self->data);
+ if (!variable_get(VAR_BLANKTIME))
+ variable_set2(VAR_BLANKTIME, "300");
+ return DITEM_SUCCESS;
+}
+
+int
+configSaverTimeout(dialogMenuItem *self)
+{
+ return (variable_get_value(VAR_BLANKTIME,
+ "Enter time-out period in seconds for screen saver") ?
+ DITEM_SUCCESS : DITEM_FAILURE) | DITEM_RESTORE;
+}
+
+int
+configRegister(dialogMenuItem *self)
+{
+ return DITEM_STATUS(registerOpenDialog()) | DITEM_RESTORE;
+}
+
+int
+configNTP(dialogMenuItem *self)
+{
+ int status;
+
+ status = variable_get_value(VAR_NTPDATE_FLAGS,
+ "Enter the name of an NTP server")
+ ? DITEM_SUCCESS : DITEM_FAILURE;
+ if (status == DITEM_SUCCESS) {
+ static char tmp[255];
+
+ snprintf(tmp, sizeof(tmp), "ntpdate_enable=YES,ntpdate_flags=%s",
+ variable_get(VAR_NTPDATE_FLAGS));
+ self->data = tmp;
+ dmenuSetVariables(self);
+ }
+ return status | DITEM_RESTORE;
+}
+
+int
+configUsers(dialogMenuItem *self)
+{
+ dialog_clear_norefresh();
+ dmenuOpenSimple(&MenuUsermgmt, FALSE);
+ dialog_clear();
+ return DITEM_SUCCESS | DITEM_RESTORE;
+}
+
+int
+configXEnvironment(dialogMenuItem *self)
+{
+#ifndef USE_XIG_ENVIRONMENT
+ char *config, *execfile;
+
+ dialog_clear_norefresh();
+ if (!dmenuOpenSimple(&MenuXF86Config, FALSE))
+ return DITEM_FAILURE | DITEM_RESTORE;
+ systemExecute("/sbin/ldconfig /usr/lib /usr/X11R6/lib /usr/local/lib /usr/lib/compat");
+ config = variable_get(VAR_XF86_CONFIG);
+ if (!config)
+ return DITEM_FAILURE | DITEM_RESTORE;
+ execfile = string_concat("/usr/X11R6/bin/", config);
+ if (file_executable(execfile)) {
+ dialog_clear_norefresh();
+ if (!file_readable("/dev/mouse") && !msgYesNo("Does this system have a mouse attached to it?"))
+ dmenuOpenSimple(&MenuMouse, FALSE);
+ dialog_clear();
+ systemExecute(execfile);
+ return DITEM_SUCCESS | DITEM_RESTORE;
+ }
+ else {
+ dialog_clear_norefresh();
+ msgConfirm("XFree86 does not appear to be installed! Please install\n"
+ "The XFree86 distribution before attempting to configure it.");
+ return DITEM_FAILURE | DITEM_RESTORE;
+ }
+
+#else /* USE_XIG_ENVIRONMENT */
+ int i;
+
+ /* Make sure we're sane first */
+ if (!file_readable("/usr/X11R6/lib/X11/AcceleratedX/bin/Xinstall") ||
+ !file_readable("/usr/X11R6/lib/X11/AcceleratedX/bin/Xsetup")) {
+ dialog_clear_norefresh();
+ msgConfirm("Hmmm! It looks like you elected not to install the AccelleratedX\n"
+ "server package (or the installation failed somehow). If this was\n"
+ "an omission rather than an error, please go to the Distributions\n"
+ "menu, select the Custom distribution options and then choose your X11\n"
+ "distribution components from the XFree86 menu. AccelX will be selected\n"
+ "as the default server automatically.");
+ return DITEM_FAILURE | DITEM_RESTORE;
+ }
+
+ if (mediaDevice && mediaDevice->type != DEVICE_TYPE_CDROM) {
+ if (DITEM_STATUS(mediaSetCDROM(NULL)) != DITEM_SUCCESS || !mediaDevice || !mediaDevice->init(mediaDevice)) {
+ msgConfirm("I can't mount the CDE distribution from CDROM, sorry.\n"
+ "Please make sure you have the 1st CD of your FreeBSD Desktop/Pro\n"
+ "distribution in the drive before trying this operation.");
+ return DITEM_FAILURE | DITEM_RESTORE;
+ }
+ }
+
+ if (!directory_exists("/dist/CDE/")) {
+ msgConfirm("Hmmm! I can't find the CDE distribution. Please place the 1st CD of your\n"
+ "FreeBSD Desktop/Pro distribution in the drive and try this operation again.");
+ return DITEM_FAILURE | DITEM_RESTORE;
+ }
+
+ /* Pre-extract base sets in kludge to work around chicken-and-egg problem with CDE and Xaccel */
+ if (directory_exists("/dist/CDE/") && !file_readable("/usr/X11R6/bin/xterm")) {
+ msgNotify("Installing bootstrap X11 tools from CDE distribution.");
+ systemExecute("tar xpf /dist/CDE/FreeBSD/packages/X11-RUN/archive -C /");
+ systemExecute("tar xpf /dist/CDE/FreeBSD/packages/X11-PRG/archive -C /");
+ }
+ systemExecute("/sbin/ldconfig /usr/lib /usr/X11R6/lib /usr/dt/lib /usr/local/lib /usr/lib/compat");
+
+ dialog_clear_norefresh();
+ msgNotify("Running AcceleratedX 3.1 installation procedure, please wait.");
+ if ((i = vsystem("/usr/X11R6/lib/X11/AcceleratedX/bin/Xinstall"))) {
+ dialog_clear_norefresh();
+ msgConfirm("Installation procedure failed, error code %d! Please report\n"
+ "error to Walnut Creek CDROM tech support (either send email\n"
+ "to support@cdrom.com or call +1 510 603 1234). Thank you!", i);
+ return DITEM_FAILURE | DITEM_RESTORE;
+ }
+ else {
+ dialog_clear();
+ systemExecute("/usr/X11R6/lib/X11/AcceleratedX/bin/Xsetup");
+ }
+ if (directory_exists("/dist/CDE")) {
+ dialog_clear_norefresh();
+ msgNotify("Running CDE installation - please wait (this may take awhile!).");
+ dialog_clear();
+ clear();
+ refresh();
+ i = systemExecute("(cd /dist/CDE; sh Install)");
+ dialog_clear();
+ if (i) {
+ msgConfirm("/dist/CDE/dtinstall script returned an error status!\n\n"
+ "To try again, you should run this command manually after the system\n"
+ "is up (and if your CDROM is mounted in the standard location, the path\n"
+ "to it will actually be /cdrom/CDE/dtinstall when you run it later).\n");
+ }
+ else {
+ if (file_readable("/dist/CDE/post-install"))
+ systemExecute("/dist/CDE/post-install");
+ /* Repair the damage done by the CDE installation */
+ msgNotify("Doing final adjustments to Xaccel distribution...");
+ systemExecute("/usr/X11R6/lib/X11/AcceleratedX/bin/Xinstall");
+ }
+ }
+ return DITEM_SUCCESS | DITEM_RESTORE;
+#endif /* USE_XIG_ENVIRONMENT */
+}
+
+void
+configResolv(void)
+{
+ FILE *fp;
+ char *cp, *dp, *hp;
+
+ cp = variable_get(VAR_NAMESERVER);
+ if (!cp || !*cp)
+ goto skip;
+ Mkdir("/etc");
+ fp = fopen("/etc/resolv.conf", "w");
+ if (!fp)
+ return;
+ if (variable_get(VAR_DOMAINNAME))
+ fprintf(fp, "domain\t%s\n", variable_get(VAR_DOMAINNAME));
+ fprintf(fp, "nameserver\t%s\n", cp);
+ fclose(fp);
+ if (isDebug())
+ msgDebug("Wrote out /etc/resolv.conf\n");
+
+skip:
+ dp = variable_get(VAR_DOMAINNAME);
+ cp = variable_get(VAR_IPADDR);
+ hp = variable_get(VAR_HOSTNAME);
+ /* Tack ourselves into /etc/hosts */
+ fp = fopen("/etc/hosts", "w");
+ if (!fp)
+ return;
+ /* Add an entry for localhost */
+ fprintf(fp, "127.0.0.1\t\tlocalhost.%s localhost\n", dp ? dp : "my.domain");
+ /* Now the host entries, if applicable */
+ if (cp && cp[0] != '0' && hp) {
+ char cp2[255];
+
+ if (!index(hp, '.'))
+ cp2[0] = '\0';
+ else {
+ SAFE_STRCPY(cp2, hp);
+ *(index(cp2, '.')) = '\0';
+ }
+ fprintf(fp, "%s\t\t%s %s\n", cp, hp, cp2);
+ fprintf(fp, "%s\t\t%s.\n", cp, hp);
+ }
+ fclose(fp);
+ if (isDebug())
+ msgDebug("Wrote out /etc/hosts\n");
+}
+
+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")
+ ? 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");
+ if (!strcmp(cp, "gated")) {
+ if (package_add(variable_get(VAR_GATED_PKG)) != DITEM_SUCCESS) {
+ msgConfirm("Unable to load gated package. Falling back to no router.");
+ variable_unset(VAR_ROUTER);
+ variable_unset(VAR_ROUTERFLAGS);
+ variable_set2(VAR_ROUTER_ENABLE, "NO");
+ 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")
+ ? DITEM_SUCCESS : DITEM_FAILURE;
+ if (ret != DITEM_SUCCESS)
+ variable_unset(VAR_ROUTERFLAGS);
+ }
+ }
+ else {
+ /* No router case */
+ variable_set2(VAR_ROUTER_ENABLE, "NO");
+ variable_unset(VAR_ROUTERFLAGS);
+ variable_unset(VAR_ROUTER);
+ }
+ }
+ return ret | DITEM_RESTORE;
+}
+
+int
+configPackages(dialogMenuItem *self)
+{
+ static PkgNode top, plist;
+ static Boolean index_initted = FALSE;
+ PkgNodePtr tmp;
+ FILE *fp;
+
+ if (!mediaVerify())
+ return DITEM_FAILURE;
+
+ if (!mediaDevice->init(mediaDevice))
+ return DITEM_FAILURE;
+
+ if (!index_initted) {
+ msgNotify("Attempting to fetch packages/INDEX file from selected media.");
+ fp = mediaDevice->get(mediaDevice, "packages/INDEX", TRUE);
+ if (!fp) {
+ dialog_clear_norefresh();
+ msgConfirm("Unable to get packages/INDEX file from selected media.\n"
+ "This may be because the packages collection is not available at\n"
+ "on the distribution media you've chosen (most likely an FTP site\n"
+ "without the packages collection mirrored). Please verify media\n"
+ "(or path to media) and try again. If your local site does not\n"
+ "carry the packages collection, then we recommend either a CD\n"
+ "distribution or the master distribution on ftp.freebsd.org.");
+ mediaDevice->shutdown(mediaDevice);
+ return DITEM_FAILURE | DITEM_RESTORE;
+ }
+ msgNotify("Located INDEX, now reading package data from it...");
+ index_init(&top, &plist);
+ if (index_read(fp, &top)) {
+ msgConfirm("I/O or format error on packages/INDEX file.\n"
+ "Please verify media (or path to media) and try again.");
+ fclose(fp);
+ return DITEM_FAILURE | DITEM_RESTORE;
+ }
+ fclose(fp);
+ index_sort(&top);
+ index_initted = TRUE;
+ }
+ while (1) {
+ int ret, pos, scroll;
+
+ /* Bring up the packages menu */
+ pos = scroll = 0;
+ index_menu(&top, &plist, &pos, &scroll);
+
+ if (plist.kids && plist.kids->name) {
+ /* Now show the packing list menu */
+ pos = scroll = 0;
+ ret = index_menu(&plist, NULL, &pos, &scroll);
+ if (ret & DITEM_LEAVE_MENU)
+ break;
+ else if (DITEM_STATUS(ret) != DITEM_FAILURE) {
+ index_extract(mediaDevice, &top, &plist);
+ break;
+ }
+ }
+ else {
+ dialog_clear_norefresh();
+ msgConfirm("No packages were selected for extraction.");
+ break;
+ }
+ }
+ tmp = plist.kids;
+ while (tmp) {
+ PkgNodePtr tmp2 = tmp->next;
+
+ safe_free(tmp);
+ tmp = tmp2;
+ }
+ index_init(NULL, &plist);
+ return DITEM_SUCCESS | DITEM_RESTORE;
+}
+
+#ifdef NETCON_EXTENTIONS
+/* Load novell client/server package */
+int
+configNovell(dialogMenuItem *self)
+{
+ int ret = DITEM_SUCCESS;
+
+ if (!RunningAsInit) {
+ msgConfirm("This package can only be installed in multi-user mode.");
+ return ret;
+ }
+ if (variable_get(VAR_NOVELL))
+ variable_unset(VAR_NOVELL);
+ else {
+ ret = package_add(PACKAGE_NETCON);
+ if (DITEM_STATUS(ret) == DITEM_SUCCESS)
+ variable_set2(VAR_NOVELL, "YES");
+ }
+ return ret | DITEM_RESTORE;
+}
+#endif
+
+/* Load pcnfsd package */
+int
+configPCNFSD(dialogMenuItem *self)
+{
+ int ret = DITEM_SUCCESS;
+
+ if (variable_get(VAR_PCNFSD))
+ variable_unset(VAR_PCNFSD);
+ else {
+ ret = package_add(variable_get(VAR_PCNFSD_PKG));
+ if (DITEM_STATUS(ret) == DITEM_SUCCESS) {
+ variable_set2(VAR_PCNFSD, "YES");
+ variable_set2("mountd_flags", "-n");
+ }
+ }
+ return ret;
+}
+
+int
+configNFSServer(dialogMenuItem *self)
+{
+ char cmd[256];
+
+ /* If we're an NFS server, we need an exports file */
+ if (!file_readable("/etc/exports")) {
+ WINDOW *w = savescr();
+
+ if (file_readable("/etc/exports.disabled"))
+ vsystem("mv /etc/exports.disabled /etc/exports");
+ else {
+ dialog_clear_norefresh();
+ msgConfirm("Operating as an NFS server means that you must first configure\n"
+ "an /etc/exports file to indicate which hosts are allowed certain\n"
+ "kinds of access to your local file systems.\n"
+ "Press [ENTER] now to invoke an editor on /etc/exports\n");
+ vsystem("echo '#The following examples export /usr to 3 machines named after ducks,' > /etc/exports");
+ vsystem("echo '#/home and all directories under it to machines named after dead rock stars' >> /etc/exports");
+ vsystem("echo '#and, finally, /a to 2 privileged machines allowed to write on it as root.' >> /etc/exports");
+ vsystem("echo '#/usr huey louie dewie' >> /etc/exports");
+ vsystem("echo '#/home -alldirs janice jimmy frank' >> /etc/exports");
+ vsystem("echo '#/a -maproot=0 bill albert' >> /etc/exports");
+ vsystem("echo '#' >> /etc/exports");
+ vsystem("echo '# You should replace these lines with your actual exported filesystems.' >> /etc/exports");
+ vsystem("echo >> /etc/exports");
+ sprintf(cmd, "%s /etc/exports", variable_get(VAR_EDITOR));
+ dialog_clear();
+ systemExecute(cmd);
+ restorescr(w);
+ }
+ variable_set2(VAR_NFS_SERVER, "YES");
+ }
+ else if (variable_get(VAR_NFS_SERVER)) { /* We want to turn it off again? */
+ vsystem("mv -f /etc/exports /etc/exports.disabled");
+ variable_unset(VAR_NFS_SERVER);
+ }
+ return DITEM_SUCCESS;
+}
diff --git a/usr.sbin/sade/devices.c b/usr.sbin/sade/devices.c
new file mode 100644
index 0000000..e744255
--- /dev/null
+++ b/usr.sbin/sade/devices.c
@@ -0,0 +1,424 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last program in the `sysinstall' line - the next
+ * generation being essentially a complete rewrite.
+ *
+ * $Id$
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include "sysinstall.h"
+#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>
+
+static Device *Devices[DEV_MAX];
+static int numDevs;
+
+static struct {
+ DeviceType type;
+ char *name;
+ char *description;
+} device_names[] = {
+ { DEVICE_TYPE_CDROM, "cd0a", "SCSI CDROM drive" },
+ { DEVICE_TYPE_CDROM, "mcd0a", "Mitsumi (old model) CDROM drive" },
+ { DEVICE_TYPE_CDROM, "scd0a", "Sony CDROM drive - CDU31/33A type", },
+ { DEVICE_TYPE_CDROM, "matcd0a", "Matsushita CDROM ('sound blaster' type)" },
+ { DEVICE_TYPE_CDROM, "wcd0c", "ATAPI IDE CDROM" },
+ { DEVICE_TYPE_TAPE, "rst0", "SCSI tape drive" },
+ { DEVICE_TYPE_TAPE, "rft0", "Floppy tape drive (QIC-02)" },
+ { DEVICE_TYPE_TAPE, "rwt0", "Wangtek tape drive" },
+ { DEVICE_TYPE_DISK, "sd", "SCSI disk device" },
+ { DEVICE_TYPE_DISK, "wd", "IDE/ESDI/MFM/ST506 disk device" },
+ { DEVICE_TYPE_DISK, "od", "SCSI optical disk device" },
+ { DEVICE_TYPE_FLOPPY, "fd0", "floppy drive unit A" },
+ { DEVICE_TYPE_FLOPPY, "fd1", "floppy drive unit B" },
+ { DEVICE_TYPE_FLOPPY, "od0", "SCSI optical disk/floppy format" },
+ { DEVICE_TYPE_NETWORK, "cuaa0", "%s on serial port 0 (COM1)" },
+ { DEVICE_TYPE_NETWORK, "cuaa1", "%s on serial port 1 (COM2)" },
+ { DEVICE_TYPE_NETWORK, "cuaa2", "%s on serial port 2 (COM3)" },
+ { DEVICE_TYPE_NETWORK, "cuaa3", "%s on serial port 3 (COM4)" },
+ { DEVICE_TYPE_NETWORK, "lp0", "Parallel Port IP (PLIP) using laplink cable" },
+ { DEVICE_TYPE_NETWORK, "lo", "Loop-back (local) network interface" },
+ { DEVICE_TYPE_NETWORK, "de", "DEC DE435 PCI NIC or other DC21040-AA based card" },
+ { DEVICE_TYPE_NETWORK, "fxp", "Intel EtherExpress Pro/100B PCI Fast Ethernet card" },
+ { DEVICE_TYPE_NETWORK, "ed", "WD/SMC 80xx; Novell NE1000/2000; 3Com 3C503 card" },
+ { DEVICE_TYPE_NETWORK, "ep", "3Com 3C509 ethernet card" },
+ { DEVICE_TYPE_NETWORK, "el", "3Com 3C501 ethernet card" },
+ { DEVICE_TYPE_NETWORK, "ex", "Intel EtherExpress Pro/10 ethernet card" },
+ { DEVICE_TYPE_NETWORK, "fe", "Fujitsu MB86960A/MB86965A ethernet card" },
+ { DEVICE_TYPE_NETWORK, "ie", "AT&T StarLAN 10 and EN100; 3Com 3C507; NI5210" },
+ { DEVICE_TYPE_NETWORK, "ix", "Intel Etherexpress ethernet card" },
+ { DEVICE_TYPE_NETWORK, "le", "DEC EtherWorks 2 or 3 ethernet card" },
+ { DEVICE_TYPE_NETWORK, "lnc", "Lance/PCnet (Isolan/Novell NE2100/NE32-VL) ethernet" },
+ { DEVICE_TYPE_NETWORK, "vx", "3COM 3c590 / 3c595 / 3c9xx ethernet card" },
+ { DEVICE_TYPE_NETWORK, "ze", "IBM/National Semiconductor PCMCIA ethernet card" },
+ { DEVICE_TYPE_NETWORK, "zp", "3Com Etherlink III PCMCIA ethernet card" },
+ { NULL },
+};
+
+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(char *name, char *try)
+{
+ int fd;
+
+ snprintf(try, FILENAME_MAX, "/dev/%s", name);
+ fd = open(try, O_RDONLY);
+ if (fd > 0)
+ return fd;
+ if (errno == EBUSY)
+ return -1;
+ snprintf(try, FILENAME_MAX, "/mnt/dev/%s", name);
+ fd = open(try, O_RDONLY);
+ return fd;
+}
+
+/* Register a new device in the devices array */
+Device *
+deviceRegister(char *name, char *desc, char *devname, DeviceType type, Boolean enabled,
+ Boolean (*init)(Device *), FILE * (*get)(Device *, char *, Boolean),
+ void (*shutdown)(Device *), void *private)
+{
+ Device *newdev = NULL;
+
+ if (numDevs == DEV_MAX)
+ msgFatal("Too many devices found!");
+ else {
+ newdev = new_device(name);
+ newdev->description = desc;
+ newdev->devname = devname;
+ newdev->type = type;
+ newdev->enabled = enabled;
+ newdev->init = init ? init : dummyInit;
+ newdev->get = get ? get : dummyGet;
+ newdev->shutdown = shutdown ? shutdown : dummyShutdown;
+ newdev->private = private;
+ Devices[numDevs] = newdev;
+ Devices[++numDevs] = NULL;
+ }
+ return newdev;
+}
+
+/* Get all device information for devices we have attached */
+void
+deviceGetAll(void)
+{
+ int i, fd, s;
+ struct ifconf ifc;
+ struct ifreq *ifptr, *end;
+ int ifflags;
+ char buffer[INTERFACE_MAX * sizeof(struct ifreq)];
+ char **names;
+
+ /* Try and get the disks first */
+ if ((names = Disk_Names()) != NULL) {
+ int i;
+
+ for (i = 0; names[i]; i++) {
+ Chunk *c1;
+ Disk *d;
+
+ d = Open_Disk(names[i]);
+ if (!d)
+ msgFatal("Unable to open disk %s", names[i]);
+
+ (void)deviceRegister(names[i], names[i], d->name, DEVICE_TYPE_DISK, FALSE, NULL, NULL, NULL, d);
+ msgDebug("Found a disk device named %s\n", names[i]);
+
+ /* Look for existing DOS partitions to register */
+ for (c1 = d->chunks->part; c1; c1 = c1->next) {
+ if (c1->type == fat || c1->type == extended) {
+ Device *dev;
+ char devname[80];
+
+ /* Got one! */
+ sprintf(devname, "/dev/%s", c1->name);
+ dev = deviceRegister(c1->name, c1->name, strdup(devname), DEVICE_TYPE_DOS, TRUE,
+ mediaInitDOS, mediaGetDOS, mediaShutdownDOS, NULL);
+ dev->private = c1;
+ msgDebug("Found a DOS partition %s on drive %s\n", c1->name, d->name);
+ }
+ }
+ }
+ free(names);
+ }
+
+ /* Now go for the network interfaces. Stolen shamelessly from ifconfig! */
+ ifc.ifc_len = sizeof(buffer);
+ ifc.ifc_buf = buffer;
+
+ s = socket(AF_INET, SOCK_DGRAM, 0);
+ if (s < 0) {
+ msgConfirm("ifconfig: socket");
+ goto skipif; /* Jump over network iface probing */
+ }
+ if (ioctl(s, SIOCGIFCONF, (char *) &ifc) < 0) {
+ msgConfirm("ifconfig (SIOCGIFCONF)");
+ goto skipif; /* Jump over network iface probing */
+ }
+ ifflags = ifc.ifc_req->ifr_flags;
+ end = (struct ifreq *) (ifc.ifc_buf + ifc.ifc_len);
+ for (ifptr = ifc.ifc_req; ifptr < end; ifptr++) {
+ char *descr;
+
+ /* If it's not a link entry, forget it */
+ if (ifptr->ifr_ifru.ifru_addr.sa_family != AF_LINK)
+ continue;
+
+ /* Eliminate network devices that don't make sense */
+ if (!strncmp(ifptr->ifr_name, "lo0", 3))
+ continue;
+
+ /* If we have a slip device, don't register it */
+ if (!strncmp(ifptr->ifr_name, "sl", 2)) {
+ continue;
+ }
+ /* And the same for ppp */
+ if (!strncmp(ifptr->ifr_name, "tun", 3) || !strncmp(ifptr->ifr_name, "ppp", 3)) {
+ continue;
+ }
+ /* Try and find its description */
+ for (i = 0, descr = NULL; device_names[i].name; i++) {
+ int len = strlen(device_names[i].name);
+
+ if (!strncmp(ifptr->ifr_name, device_names[i].name, len)) {
+ descr = device_names[i].description;
+ break;
+ }
+ }
+ if (!descr)
+ descr = "<unknown network interface type>";
+
+ deviceRegister(ifptr->ifr_name, descr, strdup(ifptr->ifr_name), DEVICE_TYPE_NETWORK, TRUE,
+ mediaInitNetwork, NULL, mediaShutdownNetwork, NULL);
+ msgDebug("Found a network device named %s\n", ifptr->ifr_name);
+ close(s);
+ if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+ msgConfirm("ifconfig: socket");
+ continue;
+ }
+ if (ifptr->ifr_addr.sa_len) /* I'm not sure why this is here - it's inherited */
+ ifptr = (struct ifreq *)((caddr_t)ifptr + ifptr->ifr_addr.sa_len - sizeof(struct sockaddr));
+ }
+
+skipif:
+ /* Finally, 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++) {
+ char try[FILENAME_MAX];
+
+ switch(device_names[i].type) {
+ case DEVICE_TYPE_CDROM:
+ fd = deviceTry(device_names[i].name, try);
+ msgDebug("Try for %s returns errno %d\n", device_names[i].name, errno);
+ if (fd >= 0 || errno == EBUSY) { /* EBUSY if already mounted */
+ if (fd >= 0) close(fd);
+ (void)deviceRegister(device_names[i].name, device_names[i].description, strdup(try),
+ DEVICE_TYPE_CDROM, TRUE, mediaInitCDROM, mediaGetCDROM,
+ mediaShutdownCDROM, NULL);
+ msgDebug("Found a CDROM device named %s\n", device_names[i].name);
+ }
+ break;
+
+ case DEVICE_TYPE_TAPE:
+ fd = deviceTry(device_names[i].name, try);
+ if (fd >= 0) {
+ close(fd);
+ deviceRegister(device_names[i].name, device_names[i].description, strdup(try),
+ DEVICE_TYPE_TAPE, TRUE, mediaInitTape, mediaGetTape, mediaShutdownTape, NULL);
+ msgDebug("Found a TAPE device named %s\n", device_names[i].name);
+ }
+ break;
+
+ case DEVICE_TYPE_FLOPPY:
+ fd = deviceTry(device_names[i].name, try);
+ if (fd >= 0) {
+ close(fd);
+ deviceRegister(device_names[i].name, device_names[i].description, strdup(try),
+ DEVICE_TYPE_FLOPPY, TRUE, mediaInitFloppy, mediaGetFloppy,
+ mediaShutdownFloppy, NULL);
+ msgDebug("Found a floppy device named %s\n", device_names[i].name);
+ }
+ break;
+
+ case DEVICE_TYPE_NETWORK:
+ fd = deviceTry(device_names[i].name, try);
+ /* 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");
+ deviceRegister("sl0", newdesc, strdup(try), DEVICE_TYPE_NETWORK, TRUE, mediaInitNetwork,
+ NULL, mediaShutdownNetwork, NULL);
+ msgDebug("Add mapping for %s on %s to sl0\n", device_names[i].name, try);
+ newdesc = safe_malloc(strlen(cp) + 50);
+ sprintf(newdesc, cp, "PPP interface");
+ deviceRegister("ppp0", newdesc, strdup(try), DEVICE_TYPE_NETWORK, TRUE, mediaInitNetwork,
+ NULL, mediaShutdownNetwork, NULL);
+ msgDebug("Add mapping for %s on %s to ppp0\n", device_names[i].name, try);
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+}
+
+/*
+ * 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..9c8647f
--- /dev/null
+++ b/usr.sbin/sade/disks.c
@@ -0,0 +1,731 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last program in the `sysinstall' line - the next
+ * generation being essentially a complete rewrite.
+ *
+ * $Id: disks.c,v 1.92 1997/10/12 16:21:09 jkh Exp $
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include "sysinstall.h"
+#include <ctype.h>
+#include <sys/disklabel.h>
+
+/* Where we start displaying chunk information on the screen */
+#define CHUNK_START_ROW 5
+
+/* Where we keep track of MBR chunks */
+static struct chunk *chunk_info[16];
+static int current_chunk;
+
+static void diskPartitionNonInteractive(Device *dev);
+
+static void
+record_chunks(Disk *d)
+{
+ struct chunk *c1 = NULL;
+ int i = 0;
+ int last_free = 0;
+
+ if (!d->chunks)
+ msgFatal("No chunk list found for %s!", d->name);
+
+ for (c1 = d->chunks->part; c1; c1 = c1->next) {
+ if (c1->type == unused && c1->size > last_free) {
+ last_free = c1->size;
+ current_chunk = i;
+ }
+ chunk_info[i++] = c1;
+ }
+ chunk_info[i] = NULL;
+ if (current_chunk >= i)
+ current_chunk = i - 1;
+}
+
+static int Total;
+
+static void
+print_chunks(Disk *d)
+{
+ int row;
+ int i;
+
+ for (i = Total = 0; chunk_info[i]; i++)
+ Total += chunk_info[i]->size;
+ if (d->bios_cyl > 65536 || d->bios_hd > 256 || d->bios_sect >= 64) {
+ dialog_clear_norefresh();
+ msgConfirm("WARNING: A geometry of %d/%d/%d for %s is incorrect. Using\n"
+ "a more likely geometry. If this geometry is incorrect or you\n"
+ "are unsure as to whether or not it's correct, please consult\n"
+ "the Hardware Guide in the Documentation submenu or use the\n"
+ "(G)eometry command to change it now.\n\n"
+ "Remember: you need to enter whatever your BIOS thinks the\n"
+ "geometry is! For IDE, it's what you were told in the BIOS\n"
+ "setup. For SCSI, it's the translation mode your controller is\n"
+ "using. Do NOT use a ``physical geometry''.",
+ d->bios_cyl, d->bios_hd, d->bios_sect, d->name);
+ Sanitize_Bios_Geom(d);
+ }
+ attrset(A_NORMAL);
+ mvaddstr(0, 0, "Disk name:\t");
+ clrtobot();
+ attrset(A_REVERSE); addstr(d->name); attrset(A_NORMAL);
+ attrset(A_REVERSE); mvaddstr(0, 55, "FDISK Partition Editor"); attrset(A_NORMAL);
+ mvprintw(1, 0,
+ "DISK Geometry:\t%lu cyls/%lu heads/%lu sectors = %lu sectors",
+ d->bios_cyl, d->bios_hd, d->bios_sect,
+ d->bios_cyl * d->bios_hd * d->bios_sect);
+ mvprintw(3, 0, "%10s %10s %10s %8s %6s %10s %8s %8s",
+ "Offset", "Size", "End", "Name", "PType", "Desc",
+ "Subtype", "Flags");
+ for (i = 0, row = CHUNK_START_ROW; chunk_info[i]; i++, row++) {
+ if (i == current_chunk)
+ attrset(ATTR_SELECTED);
+ mvprintw(row, 0, "%10ld %10lu %10lu %8s %6d %10s %8d\t%-6s",
+ chunk_info[i]->offset, chunk_info[i]->size,
+ chunk_info[i]->end, chunk_info[i]->name,
+ chunk_info[i]->type,
+ slice_type_name(chunk_info[i]->type, chunk_info[i]->subtype),
+ chunk_info[i]->subtype, ShowChunkFlags(chunk_info[i]));
+ if (i == current_chunk)
+ attrset(A_NORMAL);
+ }
+}
+
+static void
+print_command_summary()
+{
+ mvprintw(14, 0, "The following commands are supported (in upper or lower case):");
+ mvprintw(16, 0, "A = Use Entire Disk B = Bad Block Scan C = Create Slice");
+ mvprintw(17, 0, "D = Delete Slice G = Set Drive Geometry S = Set Bootable");
+ mvprintw(18, 0, "T = Change Type U = Undo All Changes Q = Finish");
+ if (!RunningAsInit)
+ mvprintw(18, 48, "W = Write Changes");
+ mvprintw(21, 0, "Use F1 or ? to get more help, arrow keys to select.");
+ move(0, 0);
+}
+
+static u_char *
+getBootMgr(char *dname)
+{
+ extern u_char mbr[], bteasy17[];
+ char str[80];
+ char *cp;
+ int i = 0;
+
+ cp = variable_get(VAR_BOOTMGR);
+ if (!cp) {
+ /* Figure out what kind of MBR the user wants */
+ sprintf(str, "Install Boot Manager for drive %s?", dname);
+ MenuMBRType.title = str;
+ i = dmenuOpenSimple(&MenuMBRType, FALSE);
+ }
+ else {
+ if (!strncmp(cp, "boot", 4))
+ BootMgr = 0;
+ else if (!strcmp(cp, "standard"))
+ BootMgr = 1;
+ else
+ BootMgr = 2;
+ }
+ if (cp || i) {
+ switch (BootMgr) {
+ case 0:
+ return bteasy17;
+
+ case 1:
+ return mbr;
+
+ case 2:
+ default:
+ break;
+ }
+ }
+ return NULL;
+}
+
+int
+diskGetSelectCount(Device ***devs)
+{
+ int i, cnt, enabled;
+ char *cp;
+ Device **dp;
+
+ cp = variable_get(VAR_DISK);
+ dp = *devs = deviceFind(cp, DEVICE_TYPE_DISK);
+ cnt = deviceCount(dp);
+ if (!cnt)
+ return -1;
+ for (i = 0, enabled = 0; i < cnt; i++) {
+ if (dp[i]->enabled)
+ ++enabled;
+ }
+ return enabled;
+}
+
+void
+diskPartition(Device *dev)
+{
+ char *cp, *p;
+ int rv, key = 0;
+ Boolean chunking;
+ char *msg = NULL;
+ u_char *mbrContents;
+ WINDOW *w = savescr();
+ Disk *d = (Disk *)dev->private;
+
+ chunking = TRUE;
+ keypad(stdscr, TRUE);
+
+ /* Flush both the dialog and curses library views of the screen
+ since we don't always know who called us */
+ dialog_clear_norefresh(), clear();
+ current_chunk = 0;
+
+ /* Set up the chunk array */
+ record_chunks(d);
+
+ while (chunking) {
+ char *val, geometry[80];
+
+ /* Now print our overall state */
+ if (d)
+ print_chunks(d);
+ print_command_summary();
+ if (msg) {
+ attrset(title_attr); mvprintw(23, 0, msg); attrset(A_NORMAL);
+ beep();
+ msg = NULL;
+ }
+ else {
+ move(23, 0);
+ clrtoeol();
+ }
+
+ /* Get command character */
+ key = getch();
+ switch (toupper(key)) {
+ case '\014': /* ^L (redraw) */
+ clear();
+ msg = NULL;
+ break;
+
+ case '\020': /* ^P */
+ case KEY_UP:
+ case '-':
+ if (current_chunk != 0)
+ --current_chunk;
+ break;
+
+ case '\016': /* ^N */
+ case KEY_DOWN:
+ case '+':
+ case '\r':
+ case '\n':
+ if (chunk_info[current_chunk + 1])
+ ++current_chunk;
+ break;
+
+ case KEY_HOME:
+ current_chunk = 0;
+ break;
+
+ case KEY_END:
+ while (chunk_info[current_chunk + 1])
+ ++current_chunk;
+ break;
+
+ case KEY_F(1):
+ case '?':
+ systemDisplayHelp("slice");
+ clear();
+ break;
+
+ case 'A':
+ cp = variable_get(VAR_DEDICATE_DISK);
+ if (cp && !strcasecmp(cp, "always"))
+ rv = 1;
+ else {
+ rv = msgYesNo("Do you want to do this with a true partition entry\n"
+ "so as to remain cooperative with any future possible\n"
+ "operating systems on the drive(s)?\n"
+ "(See also the section about ``dangerously dedicated''\n"
+ "disks in the FreeBSD FAQ.)");
+ if (rv == -1)
+ rv = 0;
+ }
+ All_FreeBSD(d, rv);
+ variable_set2(DISK_PARTITIONED, "yes");
+ record_chunks(d);
+ clear();
+ break;
+
+ case 'B':
+ if (chunk_info[current_chunk]->type != freebsd)
+ msg = "Can only scan for bad blocks in FreeBSD slice.";
+ else if (strncmp(d->name, "sd", 2) ||
+ !msgYesNo("This typically makes sense only for ESDI, IDE or MFM drives.\n"
+ "Are you sure you want to do this on a SCSI disk?")) {
+ if (chunk_info[current_chunk]->flags & CHUNK_BAD144)
+ chunk_info[current_chunk]->flags &= ~CHUNK_BAD144;
+ else
+ chunk_info[current_chunk]->flags |= CHUNK_BAD144;
+ }
+ clear();
+ break;
+
+ case 'C':
+ if (chunk_info[current_chunk]->type != unused)
+ msg = "Slice in use, delete it first or move to an unused one.";
+ else {
+ char *val, tmp[20], *cp;
+ int size, subtype;
+ chunk_e partitiontype;
+
+ snprintf(tmp, 20, "%d", chunk_info[current_chunk]->size);
+ val = msgGetInput(tmp, "Please specify the size for new FreeBSD slice in blocks\n"
+ "or append a trailing `M' for megabytes (e.g. 20M).");
+ if (val && (size = strtol(val, &cp, 0)) > 0) {
+ if (*cp && toupper(*cp) == 'M')
+ size *= ONE_MEG;
+ strcpy(tmp, "165");
+ val = msgGetInput(tmp, "Enter type of partition to create:\n\n"
+ "Pressing Enter will choose the default, a native FreeBSD\n"
+ "slice (type 165). You can choose other types, 6 for a\n"
+ "DOS partition or 131 for a Linux partition, for example.\n\n"
+ "Note: If you choose a non-FreeBSD partition type, it will not\n"
+ "be formatted or otherwise prepared, it will simply reserve space\n"
+ "for you to use another tool, such as DOS FORMAT, to later format\n"
+ "and use the partition.");
+ if (val && (subtype = strtol(val, NULL, 0)) > 0) {
+ if (subtype == 165)
+ partitiontype = freebsd;
+ else if (subtype == 6)
+ partitiontype = fat;
+ else
+ partitiontype = unknown;
+ Create_Chunk(d, chunk_info[current_chunk]->offset, size, partitiontype, subtype,
+ (chunk_info[current_chunk]->flags & CHUNK_ALIGN));
+ variable_set2(DISK_PARTITIONED, "yes");
+ record_chunks(d);
+ }
+ }
+ clear();
+ }
+ break;
+
+ case KEY_DC:
+ case 'D':
+ if (chunk_info[current_chunk]->type == unused)
+ msg = "Slice is already unused!";
+ else {
+ Delete_Chunk(d, chunk_info[current_chunk]);
+ variable_set2(DISK_PARTITIONED, "yes");
+ record_chunks(d);
+ }
+ break;
+
+ case 'T':
+ if (chunk_info[current_chunk]->type == unused)
+ msg = "Slice is currently unused (use create instead)";
+ else {
+ char *val, tmp[20];
+ int subtype;
+ chunk_e partitiontype;
+ WINDOW *save = savescr();
+
+ strcpy(tmp, "165");
+ val = msgGetInput(tmp, "New partition type:\n\n"
+ "Pressing Enter will choose the default, a native FreeBSD\n"
+ "slice (type 165). Other popular values are 6 for\n"
+ "DOS PAT partition, 131 for a Linux ext2fs partition or\n"
+ "130 for a Linux swap partition.\n\n"
+ "Note: If you choose a non-FreeBSD partition type, it will not\n"
+ "be formatted or otherwise prepared, it will simply reserve space\n"
+ "for you to use another tool, such as DOS format, to later format\n"
+ "and actually use the partition.");
+ if (val && (subtype = strtol(val, NULL, 0)) > 0) {
+ if (subtype == 165)
+ partitiontype = freebsd;
+ else if (subtype == 6)
+ partitiontype = fat;
+ else
+ partitiontype = unknown;
+ chunk_info[current_chunk]->type = partitiontype;
+ chunk_info[current_chunk]->subtype = subtype;
+ }
+ restorescr(save);
+ }
+ break;
+
+ case 'G':
+ snprintf(geometry, 80, "%lu/%lu/%lu", d->bios_cyl, d->bios_hd, d->bios_sect);
+ val = msgGetInput(geometry, "Please specify the new geometry in cyl/hd/sect format.\n"
+ "Don't forget to use the two slash (/) separator characters!\n"
+ "It's not possible to parse the field without them.");
+ if (val) {
+ long nc, nh, ns;
+ nc = strtol(val, &val, 0);
+ nh = strtol(val + 1, &val, 0);
+ ns = strtol(val + 1, 0, 0);
+ Set_Bios_Geom(d, nc, nh, ns);
+ }
+ clear();
+ break;
+
+ case 'S':
+ /* Set Bootable */
+ chunk_info[current_chunk]->flags |= CHUNK_ACTIVE;
+ break;
+
+ case 'U':
+ if ((cp = variable_get(DISK_LABELLED)) && !strcmp(cp, "written")) {
+ msgConfirm("You've already written this information out - you\n"
+ "can't undo it.");
+ }
+ else if (!msgYesNo("Are you SURE you want to Undo everything?")) {
+ char cp[BUFSIZ];
+
+ sstrncpy(cp, d->name, sizeof cp);
+ Free_Disk(dev->private);
+ d = Open_Disk(cp);
+ if (!d)
+ msgConfirm("Can't reopen disk %s! Internal state is probably corrupted", cp);
+ dev->private = d;
+ variable_unset(DISK_PARTITIONED);
+ variable_unset(DISK_LABELLED);
+ if (d)
+ record_chunks(d);
+ }
+ clear();
+ break;
+
+ case 'W':
+ if ((cp = variable_get(DISK_LABELLED)) && !strcmp(cp, "written")) {
+ msgConfirm("You've already written this information out - if\n"
+ "you wish to overwrite it, you'll have to restart.");
+ }
+ else if (!msgYesNo("WARNING: This should only be used when modifying an EXISTING\n"
+ "installation. If you are installing FreeBSD for the first time\n"
+ "then you should simply type Q when you're finished here and your\n"
+ "changes will be committed in one batch automatically at the end of\n"
+ "these questions. 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");
+
+ /* Don't trash the MBR if the first (and therefore only) chunk is marked for a truly dedicated
+ * disk (i.e., the disklabel starts at sector 0), even in cases where the user has requested
+ * booteasy or a "standard" MBR -- both would be fatal in this case.
+ */
+ if (!(d->chunks->part->flags & CHUNK_FORCE_ALL) && (mbrContents = getBootMgr(d->name)) != NULL)
+ Set_Boot_Mgr(d, mbrContents);
+
+ if (DITEM_STATUS(diskPartitionWrite(NULL)) != DITEM_SUCCESS)
+ msgConfirm("Disk partition write returned an error status!");
+ else
+ msgConfirm("Wrote FDISK partition information out successfully.");
+ }
+ clear();
+ break;
+
+ case '|':
+ if (!msgYesNo("Are you SURE you want to go into Wizard mode?\n"
+ "No seat belts whatsoever are provided!")) {
+ clear();
+ refresh();
+ slice_wizard(d);
+ variable_set2(DISK_PARTITIONED, "yes");
+ record_chunks(d);
+ }
+ else
+ msg = "Wise choice!";
+ clear();
+ break;
+
+ case '\033': /* ESC */
+ case 'Q':
+ chunking = FALSE;
+ /* Don't trash the MBR if the first (and therefore only) chunk is marked for a truly dedicated
+ * disk (i.e., the disklabel starts at sector 0), even in cases where the user has requested
+ * booteasy or a "standard" MBR -- both would be fatal in this case.
+ */
+ if ((d->chunks->part->flags & CHUNK_FORCE_ALL) != CHUNK_FORCE_ALL
+ && (mbrContents = getBootMgr(d->name)) != NULL)
+ Set_Boot_Mgr(d, mbrContents);
+ break;
+
+ default:
+ beep();
+ msg = "Type F1 or ? for help";
+ break;
+ }
+ }
+ p = CheckRules(d);
+ if (p) {
+ char buf[FILENAME_MAX];
+
+ dialog_clear_norefresh();
+ use_helpline("Press F1 to read more about disk slices.");
+ use_helpfile(systemHelpFile("partition", buf));
+ if (!variable_get(VAR_NO_WARN))
+ dialog_mesgbox("Disk slicing warning:", p, -1, -1);
+ free(p);
+ }
+ restorescr(w);
+}
+
+static int
+partitionHook(dialogMenuItem *selected)
+{
+ Device **devs = NULL;
+
+ devs = deviceFind(selected->prompt, DEVICE_TYPE_DISK);
+ if (!devs) {
+ msgConfirm("Unable to find disk %s!", selected->prompt);
+ return DITEM_FAILURE;
+ }
+ /* Toggle enabled status? */
+ if (!devs[0]->enabled) {
+ devs[0]->enabled = TRUE;
+ diskPartition(devs[0]);
+ }
+ else
+ devs[0]->enabled = FALSE;
+ return DITEM_SUCCESS | DITEM_RESTORE;
+}
+
+static int
+partitionCheck(dialogMenuItem *selected)
+{
+ Device **devs = NULL;
+
+ devs = deviceFind(selected->prompt, DEVICE_TYPE_DISK);
+ if (!devs || devs[0]->enabled == FALSE)
+ return FALSE;
+ return TRUE;
+}
+
+int
+diskPartitionEditor(dialogMenuItem *self)
+{
+ DMenu *menu;
+ Device **devs;
+ int i, cnt, devcnt;
+
+ cnt = diskGetSelectCount(&devs);
+ devcnt = deviceCount(devs);
+ if (cnt == -1) {
+ msgConfirm("No disks found! Please verify that your disk controller is being\n"
+ "properly probed at boot time. See the Hardware Guide on the\n"
+ "Documentation menu for clues on diagnosing this type of problem.");
+ return DITEM_FAILURE;
+ }
+ else if (cnt) {
+ /* Some are already selected */
+ for (i = 0; i < devcnt; i++) {
+ if (devs[i]->enabled) {
+ if (variable_get(VAR_NONINTERACTIVE))
+ diskPartitionNonInteractive(devs[i]);
+ else
+ diskPartition(devs[i]);
+ }
+ }
+ }
+ else {
+ /* No disks are selected, fall-back case now */
+ if (devcnt == 1) {
+ devs[0]->enabled = TRUE;
+ if (variable_get(VAR_NONINTERACTIVE))
+ diskPartitionNonInteractive(devs[0]);
+ else
+ diskPartition(devs[0]);
+ return DITEM_SUCCESS;
+ }
+ else {
+ menu = deviceCreateMenu(&MenuDiskDevices, DEVICE_TYPE_DISK, partitionHook, partitionCheck);
+ if (!menu) {
+ msgConfirm("No devices suitable for installation found!\n\n"
+ "Please verify that your disk controller (and attached drives)\n"
+ "were detected properly. This can be done by pressing the\n"
+ "[Scroll Lock] key and using the Arrow keys to move back to\n"
+ "the boot messages. Press [Scroll Lock] again to return.");
+ return DITEM_FAILURE;
+ }
+ else {
+ i = dmenuOpenSimple(menu, FALSE) ? DITEM_SUCCESS : DITEM_FAILURE;
+ free(menu);
+ }
+ return i | DITEM_RESTORE;
+ }
+ }
+ return DITEM_FAILURE;
+}
+
+int
+diskPartitionWrite(dialogMenuItem *self)
+{
+ Device **devs;
+ int i;
+
+ 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++) {
+ Chunk *c1;
+ Disk *d = (Disk *)devs[i]->private;
+
+ if (!devs[i]->enabled)
+ continue;
+
+ Set_Boot_Blocks(d, boot1, boot2);
+ 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 scan for bad blocks, if necessary */
+ for (c1 = d->chunks->part; c1; c1 = c1->next) {
+ if (c1->flags & CHUNK_BAD144) {
+ int ret;
+
+ msgNotify("Running bad block scan on slice %s", c1->name);
+ if (!Fake) {
+ ret = vsystem("bad144 -v /dev/r%s 1234", c1->name);
+ if (ret)
+ msgConfirm("Bad144 init on %s returned status of %d!", c1->name, ret);
+ ret = vsystem("bad144 -v -s /dev/r%s", c1->name);
+ if (ret)
+ msgConfirm("Bad144 scan on %s returned status of %d!", c1->name, ret);
+ }
+ }
+ }
+ }
+ /* Now it's not "yes", but "written" */
+ variable_set2(DISK_PARTITIONED, "written");
+ return DITEM_SUCCESS;
+}
+
+/* Partition a disk based wholly on which variables are set */
+static void
+diskPartitionNonInteractive(Device *dev)
+{
+ char *cp;
+ int i, sz, all_disk = 0;
+ u_char *mbrContents;
+ Disk *d = (Disk *)dev->private;
+
+ record_chunks(d);
+ cp = variable_get(VAR_GEOMETRY);
+ if (cp) {
+ msgDebug("Setting geometry from script to: %s\n", cp);
+ d->bios_cyl = strtol(cp, &cp, 0);
+ d->bios_hd = strtol(cp + 1, &cp, 0);
+ d->bios_sect = strtol(cp + 1, 0, 0);
+ }
+
+ cp = variable_get(VAR_PARTITION);
+ if (cp) {
+ if (!strcmp(cp, "free")) {
+ /* Do free disk space case */
+ for (i = 0; chunk_info[i]; i++) {
+ /* If a chunk is at least 10MB in size, use it. */
+ if (chunk_info[i]->type == unused && chunk_info[i]->size > (10 * ONE_MEG)) {
+ Create_Chunk(d, chunk_info[i]->offset, chunk_info[i]->size, freebsd, 3,
+ (chunk_info[i]->flags & CHUNK_ALIGN));
+ variable_set2(DISK_PARTITIONED, "yes");
+ break;
+ }
+ }
+ if (!chunk_info[i]) {
+ dialog_clear();
+ msgConfirm("Unable to find any free space on this disk!");
+ return;
+ }
+ }
+ else if (!strcmp(cp, "all")) {
+ /* Do all disk space case */
+ msgDebug("Warning: Devoting all of disk %s to FreeBSD.\n", d->name);
+
+ All_FreeBSD(d, FALSE);
+ }
+ else if (!strcmp(cp, "exclusive")) {
+ /* Do really-all-the-disk-space case */
+ msgDebug("Warning: Devoting all of disk %s to FreeBSD.\n", d->name);
+
+ All_FreeBSD(d, all_disk = TRUE);
+ }
+ else if ((sz = strtol(cp, &cp, 0))) {
+ /* Look for sz bytes free */
+ if (*cp && toupper(*cp) == 'M')
+ sz *= ONE_MEG;
+ for (i = 0; chunk_info[i]; i++) {
+ /* If a chunk is at least sz MB, use it. */
+ if (chunk_info[i]->type == unused && chunk_info[i]->size >= sz) {
+ Create_Chunk(d, chunk_info[i]->offset, sz, freebsd, 3, (chunk_info[i]->flags & CHUNK_ALIGN));
+ variable_set2(DISK_PARTITIONED, "yes");
+ break;
+ }
+ }
+ if (!chunk_info[i]) {
+ dialog_clear();
+ msgConfirm("Unable to find %d free blocks on this disk!", sz);
+ return;
+ }
+ }
+ else if (!strcmp(cp, "existing")) {
+ /* Do existing FreeBSD case */
+ for (i = 0; chunk_info[i]; i++) {
+ if (chunk_info[i]->type == freebsd)
+ break;
+ }
+ if (!chunk_info[i]) {
+ dialog_clear();
+ msgConfirm("Unable to find any existing FreeBSD partitions on this disk!");
+ return;
+ }
+ }
+ else {
+ dialog_clear();
+ msgConfirm("`%s' is an invalid value for %s - is config file valid?", cp, VAR_PARTITION);
+ return;
+ }
+ if (!all_disk) {
+ mbrContents = getBootMgr(d->name);
+ Set_Boot_Mgr(d, mbrContents);
+ }
+ variable_set2(DISK_PARTITIONED, "yes");
+ }
+}
diff --git a/usr.sbin/sade/dispatch.c b/usr.sbin/sade/dispatch.c
new file mode 100644
index 0000000..67e09d6
--- /dev/null
+++ b/usr.sbin/sade/dispatch.c
@@ -0,0 +1,405 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last program in the `sysinstall' line - the next
+ * generation being essentially a complete rewrite.
+ *
+ * $Id: dispatch.c,v 1.23 1997/09/16 18:57:08 jkh Exp $
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include "sysinstall.h"
+#include <ctype.h>
+#include <errno.h>
+#include <sys/signal.h>
+#include <sys/fcntl.h>
+
+#include "list.h"
+
+static int dispatch_shutdown(dialogMenuItem *unused);
+static int dispatch_systemExecute(dialogMenuItem *unused);
+
+static struct _word {
+ char *name;
+ int (*handler)(dialogMenuItem *self);
+} resWords[] = {
+ { "configAnonFTP", configAnonFTP },
+ { "configRouter", configRouter },
+ { "configNFSServer", configNFSServer },
+ { "configNTP", configNTP },
+ { "configPCNFSD", configPCNFSD },
+ { "configPackages", configPackages },
+ { "configRegister", configRegister },
+ { "configUsers", configUsers },
+ { "configXEnvironment", configXEnvironment },
+ { "diskPartitionEditor", diskPartitionEditor },
+ { "diskPartitionWrite", diskPartitionWrite },
+ { "diskLabelEditor", diskLabelEditor },
+ { "diskLabelCommit", diskLabelCommit },
+ { "distReset", distReset },
+ { "distSetCustom", distSetCustom },
+ { "distSetDeveloper", distSetDeveloper },
+ { "distSetXDeveloper", distSetXDeveloper },
+ { "distSetKernDeveloper", distSetKernDeveloper },
+ { "distSetUser", distSetUser },
+ { "distSetXUser", distSetXUser },
+ { "distSetMinimum", distSetMinimum },
+ { "distSetEverything", distSetEverything },
+ { "distSetDES", distSetDES },
+ { "distSetSrc", distSetSrc },
+ { "distSetXF86", distSetXF86 },
+ { "distExtractAll", distExtractAll },
+ { "docBrowser", docBrowser },
+ { "docShowDocument", docShowDocument },
+ { "installCommit", installCommit },
+ { "installExpress", installExpress },
+ { "installNovice", installNovice },
+ { "installUpgrade", installUpgrade },
+ { "installFixup", installFixup },
+ { "installFixitHoloShell", installFixitHoloShell },
+ { "installFixitCDROM", installFixitCDROM },
+ { "installFixitFloppy", installFixitFloppy },
+ { "installFilesystems", installFilesystems },
+ { "installVarDefaults", installVarDefaults },
+ { "loadConfig", dispatch_load_file },
+ { "mediaSetCDROM", mediaSetCDROM },
+ { "mediaSetFloppy", mediaSetFloppy },
+ { "mediaSetDOS", mediaSetDOS },
+ { "mediaSetTape", mediaSetTape },
+ { "mediaSetFTP", mediaSetFTP },
+ { "mediaSetFTPActive", mediaSetFTPActive },
+ { "mediaSetFTPPassive", mediaSetFTPPassive },
+ { "mediaSetUFS", mediaSetUFS },
+ { "mediaSetNFS", mediaSetNFS },
+ { "mediaSetFTPUserPass", mediaSetFTPUserPass },
+ { "mediaSetCPIOVerbosity", mediaSetCPIOVerbosity },
+ { "mediaGetType", mediaGetType },
+ { "optionsEditor", optionsEditor },
+ { "register", configRegister }, /* Alias */
+ { "packageAdd", packageAdd },
+ { "addGroup", userAddGroup },
+ { "addUser", userAddUser },
+ { "shutdown", dispatch_shutdown },
+ { "system", dispatch_systemExecute },
+ { 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
+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);
+ i = DITEM_SUCCESS;
+ }
+ else {
+ /* A command might be a pathname if it's encoded in argv[0], which
+ we also support */
+ if ((cp = rindex(str, '/')) != NULL)
+ str = cp + 1;
+ if (isDebug())
+ msgDebug("dispatch: calling resword `%s'\n", str);
+ if (!call_possible_resword(str, NULL, &i)) {
+ msgNotify("Warning: No such command ``%s''", str);
+ i = DITEM_FAILURE;
+ }
+ }
+ return i;
+}
+
+
+/*
+ * File processing
+ */
+
+static qelement *
+dispatch_load_fp(FILE *fp)
+{
+ qelement *head;
+ char buf[BUFSIZ], *cp;
+
+ head = malloc(sizeof(qelement));
+
+ if (!head)
+ return NULL;
+
+ INITQUE(*head);
+
+ while (fgets(buf, sizeof buf, fp)) {
+
+ if ((cp = strchr(buf, '\n')) != NULL)
+ *cp = '\0';
+ if (*buf == '\0' || *buf == '#')
+ continue;
+
+ if (!dispatch_add_command(head, buf))
+ return NULL;
+ }
+
+ return head;
+}
+
+static int
+dispatch_execute(qelement *head)
+{
+ int result = DITEM_SUCCESS;
+ command_buffer *item;
+
+ if (!head)
+ return result | DITEM_FAILURE;
+
+ /* Hint to others that we're running from a script, should they care */
+ variable_set2(VAR_NONINTERACTIVE, "YES");
+
+ while (!EMPTYQUE(*head)) {
+ item = (command_buffer *) head->q_forw;
+
+ if (DITEM_STATUS(dispatchCommand(item->string)) != DITEM_SUCCESS) {
+ /*
+ * Allow a user to prefix a command with "noError" to cause
+ * us to ignore any errors for that one command.
+ */
+ if (variable_get(VAR_NO_ERROR))
+ variable_unset(VAR_NO_ERROR);
+ else {
+ msgConfirm("Command `%s' failed - rest of script aborted.\n",
+ item->string);
+ result |= DITEM_FAILURE;
+ break;
+ }
+ }
+ dispatch_free_command(item);
+ }
+
+ dispatch_free_all(head);
+
+ variable_unset(VAR_NONINTERACTIVE);
+
+ return result;
+}
+
+int
+dispatch_load_file_int(int quiet)
+{
+ FILE *fp;
+ char *cp;
+ int i;
+ qelement *list;
+
+ static const char *names[] = {
+ "install.cfg",
+ "/stand/install.cfg",
+ "/tmp/install.cfg",
+ NULL
+ };
+
+ fp = NULL;
+ cp = variable_get(VAR_CONFIG_FILE);
+ if (!cp) {
+ for (i = 0; names[i]; i++)
+ if ((fp = fopen(names[i], "r")) != NULL)
+ break;
+ } else
+ fp = fopen(cp, "r");
+
+ if (!fp) {
+ if (!quiet)
+ msgConfirm("Unable to open %s: %s", cp, strerror(errno));
+ return DITEM_FAILURE;
+ }
+
+ list = dispatch_load_fp(fp);
+ fclose(fp);
+
+ return dispatch_execute(list);
+}
+
+int
+dispatch_load_file(dialogMenuItem *self)
+{
+ return dispatch_load_file_int(FALSE);
+}
+
+int
+dispatch_load_floppy(dialogMenuItem *self)
+{
+ int what = DITEM_RESTORE | DITEM_SUCCESS;
+ extern char *distWanted;
+ char *cp;
+ FILE *fp;
+ qelement *list;
+
+ mediaClose();
+ dialog_clear_norefresh();
+
+ cp = variable_get_value(VAR_INSTALL_CFG,
+ "Specify the name of a configuration file\n"
+ "residing on a MSDOS or UFS floppy.");
+ if (!cp || !*cp) {
+ variable_unset(VAR_INSTALL_CFG);
+ what |= DITEM_FAILURE;
+ return what;
+ }
+
+ distWanted = cp;
+ /* Try to open the floppy drive */
+ if (DITEM_STATUS(mediaSetFloppy(NULL)) == DITEM_FAILURE) {
+ msgConfirm("Unable to set media device to floppy.");
+ what |= DITEM_FAILURE;
+ mediaClose();
+ return what;
+ }
+
+ if (!mediaDevice->init(mediaDevice)) {
+ msgConfirm("Unable to mount floppy filesystem.");
+ what |= DITEM_FAILURE;
+ mediaClose();
+ return what;
+ }
+
+ fp = mediaDevice->get(mediaDevice, cp, TRUE);
+ if (fp) {
+ list = dispatch_load_fp(fp);
+ fclose(fp);
+ mediaClose();
+
+ what |= dispatch_execute(list);
+ }
+ else {
+ 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..45c861e
--- /dev/null
+++ b/usr.sbin/sade/dmenu.c
@@ -0,0 +1,304 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last attempt in the `sysinstall' line, the next
+ * generation being slated for what's essentially a complete rewrite.
+ *
+ * $Id: dmenu.c,v 1.34 1997/09/17 16:18:14 pst Exp $
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include "sysinstall.h"
+#include <errno.h>
+
+#define MAX_MENU 15
+
+static Boolean exited;
+
+int
+dmenuDisplayFile(dialogMenuItem *tmp)
+{
+ systemDisplayHelp((char *)tmp->data);
+ return DITEM_SUCCESS | DITEM_RESTORE;
+}
+
+int
+dmenuSubmenu(dialogMenuItem *tmp)
+{
+ return (dmenuOpenSimple((DMenu *)(tmp->data), FALSE) ? DITEM_SUCCESS : DITEM_FAILURE) |
+ DITEM_RESTORE;
+}
+
+int
+dmenuSystemCommand(dialogMenuItem *self)
+{
+ WINDOW *w = NULL; /* Keep lint happy */
+
+ /* If aux is set, the command is known not to produce any screen-spoiling output */
+ if (!self->aux)
+ w = savescr();
+ systemExecute((char *)self->data);
+ if (!self->aux)
+ restorescr(w);
+ return DITEM_SUCCESS;
+}
+
+int
+dmenuSystemCommandBox(dialogMenuItem *tmp)
+{
+ use_helpfile(NULL);
+ use_helpline("Select OK to dismiss this dialog");
+ dialog_prgbox(tmp->title, (char *)tmp->data, 22, 76, 1, 1);
+ return DITEM_SUCCESS | DITEM_RESTORE;
+}
+
+int
+dmenuExit(dialogMenuItem *tmp)
+{
+ exited = TRUE;
+ return DITEM_LEAVE_MENU;
+}
+
+int
+dmenuSetVariable(dialogMenuItem *tmp)
+{
+ variable_set((char *)tmp->data);
+ 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 = cp2;
+ }
+ free(copy);
+ return DITEM_SUCCESS;
+}
+
+int
+dmenuSetKmapVariable(dialogMenuItem *tmp)
+{
+ char *lang;
+ int err;
+
+ variable_set((char *)tmp->data);
+ 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)
+{
+ if (!variable_get((char *)tmp->data))
+ variable_set((char *)tmp->data);
+ else
+ variable_unset((char *)tmp->data);
+ return DITEM_SUCCESS;
+}
+
+int
+dmenuISetVariable(dialogMenuItem *tmp)
+{
+ char *ans, *var;
+ WINDOW *w = NULL; /* Keep lint happy */
+
+ if (!(var = (char *)tmp->data)) {
+ msgConfirm("Incorrect data field for `%s'!", tmp->title);
+ return DITEM_FAILURE;
+ }
+ w = savescr();
+ ans = msgGetInput(variable_get(var), tmp->title);
+ restorescr(w);
+ if (!ans)
+ return DITEM_FAILURE;
+ else if (!*ans)
+ variable_unset(var);
+ else
+ variable_set2(var, ans);
+ 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;
+ if (!w)
+ return FALSE;
+ return variable_check(w);
+}
+
+int
+dmenuVarsCheck(dialogMenuItem *item)
+{
+ int res, init;
+ char *w, *cp1, *cp2;
+ char *copy;
+
+ w = (char *)item->aux;
+ if (!w)
+ w = (char *)item->data;
+ if (!w)
+ return FALSE;
+
+ copy = strdup(w);
+ res = TRUE;
+ init = FALSE;
+ for (cp1 = copy; cp1 != NULL;) {
+ init = TRUE;
+ cp2 = index(cp1, ',');
+ if (cp2 != NULL)
+ *cp2++ = '\0';
+ res = res && variable_check(cp1);
+ cp1 = cp2;
+ }
+ free(copy);
+ return res && init;
+}
+
+int
+dmenuRadioCheck(dialogMenuItem *item)
+{
+ return (*((unsigned int *)item->data) == item->aux);
+}
+
+static int
+menu_height(DMenu *menu, int n)
+{
+ int max;
+ char *t;
+
+ max = MAX_MENU;
+ if (StatusLine > 24)
+ max += StatusLine - 24;
+ for (t = menu->prompt; *t; t++) {
+ if (*t == '\n')
+ --max;
+ }
+ return n > max ? max : n;
+}
+
+/* Traverse over an internal menu */
+Boolean
+dmenuOpen(DMenu *menu, int *choice, int *scroll, int *curr, int *max, Boolean buttons)
+{
+ int n, rval = 0;
+ dialogMenuItem *items;
+
+ items = menu->items;
+ if (buttons)
+ items += 2;
+ /* Count up all the items */
+ for (n = 0; items[n].title; n++);
+
+ while (1) {
+ char buf[FILENAME_MAX];
+
+ /* Any helpful hints, put 'em up! */
+ use_helpline(menu->helpline);
+ use_helpfile(systemHelpFile(menu->helpfile, buf));
+
+ /* Pop up that dialog! */
+ dialog_clear_norefresh();
+ if (menu->type & DMENU_NORMAL_TYPE)
+ rval = dialog_menu((u_char *)menu->title, (u_char *)menu->prompt, -1, -1,
+ menu_height(menu, n), -n, items, (char *)buttons, choice, scroll);
+
+ else if (menu->type & DMENU_RADIO_TYPE)
+ rval = dialog_radiolist((u_char *)menu->title, (u_char *)menu->prompt, -1, -1,
+ menu_height(menu, n), -n, items, (char *)buttons);
+
+ else if (menu->type & DMENU_CHECKLIST_TYPE)
+ rval = dialog_checklist((u_char *)menu->title, (u_char *)menu->prompt, -1, -1,
+ menu_height(menu, n), -n, items, (char *)buttons);
+ else
+ msgFatal("Menu: `%s' is of an unknown type\n", menu->title);
+ clearok(stdscr, TRUE);
+ if (exited) {
+ exited = FALSE;
+ return TRUE;
+ }
+ else if (rval)
+ return FALSE;
+ else if (menu->type & DMENU_SELECTION_RETURNS)
+ return TRUE;
+ }
+}
diff --git a/usr.sbin/sade/globals.c b/usr.sbin/sade/globals.c
new file mode 100644
index 0000000..72aecb1
--- /dev/null
+++ b/usr.sbin/sade/globals.c
@@ -0,0 +1,71 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last program in the `sysinstall' line - the next
+ * generation being essentially a complete rewrite.
+ *
+ * $Id: globals.c,v 1.18 1997/02/22 14:11:43 peter Exp $
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include "sysinstall.h"
+
+/*
+ * Various global variables and an initialization hook to set them to
+ * whatever values we feel are appropriate.
+ */
+
+int DebugFD; /* Where diagnostic output goes */
+Boolean Fake; /* Only pretend to be useful */
+Boolean RunningAsInit; /* Are we running as init? */
+Boolean DialogActive; /* Is libdialog initialized? */
+Boolean ColorDisplay; /* Are we on a color display? */
+Boolean OnVTY; /* Are we on a VTY? */
+Variable *VarHead; /* The head of the variable chain */
+Device *mediaDevice; /* Where we're installing from */
+int BootMgr; /* Which boot manager we're using */
+int StatusLine; /* Where to stick our status messages */
+jmp_buf BailOut; /* Beam me up, scotty! The natives are pissed! */
+
+/*
+ * Yes, I know some of these are already automatically initialized as
+ * globals. I simply find it clearer to set everything explicitly.
+ */
+void
+globalsInit(void)
+{
+ DebugFD = -1;
+ ColorDisplay = FALSE;
+ Fake = FALSE;
+ OnVTY = FALSE;
+ DialogActive = FALSE;
+ VarHead = NULL;
+ mediaDevice = NULL;
+ RunningAsInit = FALSE;
+}
diff --git a/usr.sbin/sade/help/partition.hlp b/usr.sbin/sade/help/partition.hlp
new file mode 100644
index 0000000..5240d72
--- /dev/null
+++ b/usr.sbin/sade/help/partition.hlp
@@ -0,0 +1,153 @@
+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/sd0s1a'':
+
+ The first three characters represent the drive name. If we had
+ a system with two SCSI drives on it then we'd see /dev/sd0 and
+ /dev/sd1 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 sd0
+ contained two slices, a FreeBSD slice and a DOS slice, that
+ would give us /dev/sd0s1 and /dev/sd0s2 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 sd0:
+
+ Name Mountpoint
+ ---- ----------
+ sd0s1a /
+ sd0s1b <swap space>
+ sd0s1e /usr
+
+ Because of historical convention, there is also a short-cut,
+ or "compatibility slice", that is maintained for easy access
+ to the *first* FreeBSD slice on a disk. This gives some
+ backwards compatibility to utilities that still may not know
+ how to deal with the new slice scheme.
+
+ The compatibility slice names for our filesystem above would
+ also look like:
+
+ Name Mountpoint
+ ---- ----------
+ sd0a /
+ sd0b <swap space>
+ sd0e /usr
+
+ Again, let it be noted: FreeBSD automatically maps the
+ compatibility slice to the first FreeBSD slice it finds
+ (in this case, sd0s1). You may have multiple FreeBSD slices on a
+ drive, but only the first one will be mapped to the compatibility
+ slice!
+
+ The compatibility slice will eventually be phased out, but
+ it is still important right now for several reasons:
+
+ 1. Some programs, as mentioned before, still don't work
+ with the slice paradigm and need time to catch up.
+
+ 2. The FreeBSD boot blocks are unable to look for
+ a root file system in anything but a compatibility
+ slice right now. This means that our root will always
+ show up on "sd0a" in the above scenario, even though
+ it really lives over on sd0s1a and would otherwise be
+ referred to by its full slice name.
+
+Once you understand all this, then the purpose of the label editor
+becomes fairly clear: You're carving up the FreeBSD slices displayed
+at the top of the screen into smaller pieces, which are displayed in
+the middle of the screen, and then assigning FreeBSD file system names
+(mount points) to them.
+
+You can also use the label editor to mount existing partitions/slices
+into your filesystem hierarchy, as is frequently done for DOS FAT
+slices. For FreeBSD partitions, you can also toggle the "newfs" state
+so that the partitions are either (re)created from scratch or simply
+checked and mounted (the contents are preserved).
+
+When you're done, type `Q' to exit.
+
+No actual changes will be made to the disk until you (C)ommit from the
+Install menu or (W)rite directly from this one. You're working with
+what is essentially a copy of the disk label(s), both here and in the
+FDISK Partition Editor, and the actual on-disk labels won't be
+affected by any changes you make until you explicitly say so.
+
diff --git a/usr.sbin/sade/help/slice.hlp b/usr.sbin/sade/help/slice.hlp
new file mode 100644
index 0000000..33280a4
--- /dev/null
+++ b/usr.sbin/sade/help/slice.hlp
@@ -0,0 +1,59 @@
+This is the Main Slice (``FDISK'' or PC-style Partition) Editor.
+
+Possible commands are printed at the bottom and the Master Boot Record
+contents are shown at the top. You can move up and down with the
+arrow keys and (C)reate a new slice whenever the highlighted
+selection bar is over a slice whose type is marked as "unused."
+
+You are expected to leave this screen with at least one slice
+marked "FreeBSD." Note that unlike Linux, you don't need to create
+multiple FreeBSD FDISK partition entries for different things like
+swap, file systems, etc. The usual convention is to create ONE
+FreeBSD slice (FDISK partition) per drive and then subsection this slice
+into swap and file systems with the Label editor.
+
+No actual changes will be made to the disk until you (C)ommit from the
+Install menu or use the (W)rite option here! You're working with what
+is essentially a copy of the disk label(s), both here and in the Label
+Editor.
+
+If you want to use the entire disk for FreeBSD, type `A'. You'll be
+asked whether or not you wish to keep the disk (potentially) compatible
+with other operating systems, i.e. the information in the FDISK table
+should be kept valid. If you select the default of `Yes', slices will be
+aligned to fictitious cylinder boundaries and space will be reserved
+in front of the FreeBSD slice for a [future] possible boot manager.
+
+For the truly dedicated disk case, you can select `No' at the
+compatibility prompt. In that case, all BIOS geometry considerations
+will no longer be in effect and you can safely ignore any
+``The detected geometry is invalid'' warning messages you may later
+see. It is also not necessary in this case to set a slice bootable
+or install an MBR boot manager as both things are then irrelevant.
+
+The FreeBSD slice will start at absolute sector 0 of the disk (so that
+FreeBSD's disk label is identical to the Master Boot Record) and
+extend to the very last sector of the disk medium. Needless to say,
+such a disk cannot have any sort of a boot manager, `disk manager',
+or anything else that has to interact with the BIOS. This option is
+therefore only considered safe for SCSI disks and most IDE disks and
+is primarily intended for people who are going to set up a dedicated
+FreeBSD server or workstation, not a typical `home PC'.
+
+The flags field has the following legend:
+
+ '=' -- This slice is properly aligned.
+ '>' -- This slice doesn't end before cylinder 1024
+ 'R' -- This slice contains the root (/) filesystem
+ 'B' -- Slice employs BAD144 bad-spot handling
+ 'C' -- This is the FreeBSD 2.0-compatibility slice (default)
+ 'A' -- This slice is marked active.
+
+If you select a slice for Bad144 handling, it will be scanned
+for bad blocks before any new filesystems are made on it.
+
+If no slice is marked Active, you will need to either install
+a Boot Manager (the option for which will be presented later in the
+installation) or set one Active before leaving this screen.
+
+To leave the slice editor, type `Q'.
diff --git a/usr.sbin/sade/install.c b/usr.sbin/sade/install.c
new file mode 100644
index 0000000..6ae04ff
--- /dev/null
+++ b/usr.sbin/sade/install.c
@@ -0,0 +1,1138 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last program in the `sysinstall' line - the next
+ * generation being essentially a complete rewrite.
+ *
+ * $Id: install.c,v 1.201 1997/10/12 16:21:13 jkh Exp $
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include "sysinstall.h"
+#include "uc_main.h"
+#include <ctype.h>
+#include <sys/disklabel.h>
+#include <sys/errno.h>
+#include <sys/ioctl.h>
+#include <sys/fcntl.h>
+#include <sys/wait.h>
+#include <sys/param.h>
+#define MSDOSFS
+#include <sys/mount.h>
+#include <ufs/ufs/ufsmount.h>
+#include <msdosfs/msdosfsmount.h>
+#undef MSDOSFS
+#include <sys/stat.h>
+#include <unistd.h>
+
+static void create_termcap(void);
+static void fixit_common(void);
+#ifdef SAVE_USERCONFIG
+static void save_userconfig_to_kernel(char *);
+#endif
+
+#define TERMCAP_FILE "/usr/share/misc/termcap"
+
+static void installConfigure(void);
+
+Boolean
+checkLabels(Boolean whinge, Chunk **rdev, Chunk **sdev, Chunk **udev, Chunk **vdev)
+{
+ Device **devs;
+ Boolean status;
+ Disk *disk;
+ Chunk *c1, *c2, *rootdev, *swapdev, *usrdev, *vardev;
+ int i;
+
+ /* Don't allow whinging if noWarn is set */
+ if (variable_get(VAR_NO_WARN))
+ whinge = FALSE;
+
+ status = TRUE;
+ *rdev = *sdev = *udev = *vdev = rootdev = swapdev = usrdev = vardev = NULL;
+
+ /* We don't need to worry about root/usr/swap if we're already multiuser */
+ if (!RunningAsInit)
+ return status;
+
+ devs = deviceFind(NULL, DEVICE_TYPE_DISK);
+ /* First verify that we have a root device */
+ for (i = 0; devs[i]; i++) {
+ if (!devs[i]->enabled)
+ continue;
+ disk = (Disk *)devs[i]->private;
+ msgDebug("Scanning disk %s for root filesystem\n", disk->name);
+ if (!disk->chunks)
+ msgFatal("No chunk list found for %s!", disk->name);
+ for (c1 = disk->chunks->part; c1; c1 = c1->next) {
+ if (c1->type == freebsd) {
+ for (c2 = c1->part; c2; c2 = c2->next) {
+ if (c2->type == part && c2->subtype != FS_SWAP && c2->private_data) {
+ if (c2->flags & CHUNK_IS_ROOT) {
+ if (rootdev) {
+ if (whinge)
+ msgConfirm("WARNING: You have more than one root device set?!\n"
+ "Using the first one found.");
+ continue;
+ }
+ else {
+ rootdev = c2;
+ if (isDebug())
+ msgDebug("Found rootdev at %s!\n", rootdev->name);
+ }
+ }
+ else if (!strcmp(((PartInfo *)c2->private_data)->mountpoint, "/usr")) {
+ if (usrdev) {
+ if (whinge)
+ msgConfirm("WARNING: You have more than one /usr filesystem.\n"
+ "Using the first one found.");
+ continue;
+ }
+ else {
+ usrdev = c2;
+ if (isDebug())
+ msgDebug("Found usrdev at %s!\n", usrdev->name);
+ }
+ }
+ else if (!strcmp(((PartInfo *)c2->private_data)->mountpoint, "/var")) {
+ if (vardev) {
+ if (whinge)
+ msgConfirm("WARNING: You have more than one /var filesystem.\n"
+ "Using the first one found.");
+ continue;
+ }
+ else {
+ vardev = c2;
+ if (isDebug())
+ msgDebug("Found vardev at %s!\n", vardev->name);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /* Now check for swap devices */
+ for (i = 0; devs[i]; i++) {
+ if (!devs[i]->enabled)
+ continue;
+ disk = (Disk *)devs[i]->private;
+ msgDebug("Scanning disk %s for swap partitions\n", disk->name);
+ if (!disk->chunks)
+ msgFatal("No chunk list found for %s!", disk->name);
+ for (c1 = disk->chunks->part; c1; c1 = c1->next) {
+ if (c1->type == freebsd) {
+ for (c2 = c1->part; c2; c2 = c2->next) {
+ if (c2->type == part && c2->subtype == FS_SWAP && !swapdev) {
+ swapdev = c2;
+ if (isDebug())
+ msgDebug("Found swapdev at %s!\n", swapdev->name);
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ /* Copy our values over */
+ *rdev = rootdev;
+ *sdev = swapdev;
+ *udev = usrdev;
+ *vdev = vardev;
+
+ if (!rootdev && whinge) {
+ msgConfirm("No root device found - you must label a partition as /\n"
+ "in the label editor.");
+ status = FALSE;
+ }
+ if (!swapdev && whinge) {
+ msgConfirm("No swap devices found - you must create at least one\n"
+ "swap partition.");
+ status = FALSE;
+ }
+ if (!usrdev && whinge) {
+ msgConfirm("WARNING: No /usr filesystem found. This is not technically\n"
+ "an error if your root filesystem is big enough (or you later\n"
+ "intend to mount your /usr filesystem over NFS), but it may otherwise\n"
+ "cause you trouble if you're not exactly sure what you are doing!");
+ }
+ if (!vardev && whinge && variable_cmp(SYSTEM_STATE, "upgrade")) {
+ msgConfirm("WARNING: No /var filesystem found. This is not technically\n"
+ "an error if your root filesystem is big enough (or you later\n"
+ "intend to link /var to someplace else), but it may otherwise\n"
+ "cause your root filesystem to fill up if you receive lots of mail\n"
+ "or edit large temporary files.");
+ }
+ return status;
+}
+
+static int
+installInitial(void)
+{
+ static Boolean alreadyDone = FALSE;
+
+ 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");
+
+ /* If we refuse to proceed, bail. */
+ dialog_clear_norefresh();
+ if (msgYesNo("Last Chance! Are you SURE you want continue the installation?\n\n"
+ "If you're running this on a disk with data you wish to save\n"
+ "then WE STRONGLY ENCOURAGE YOU TO MAKE PROPER BACKUPS before\n"
+ "proceeding!\n\n"
+ "We can take no responsibility for lost disk contents!") != 0)
+ return DITEM_FAILURE | DITEM_RESTORE;
+
+ if (DITEM_STATUS(diskLabelCommit(NULL)) != DITEM_SUCCESS) {
+ msgConfirm("Couldn't make filesystems properly. Aborting.");
+ return DITEM_FAILURE;
+ }
+ else if (isDebug())
+ msgDebug("installInitial: Scribbled successfully on the disk(s)\n");
+
+ if (!copySelf()) {
+ msgConfirm("Couldn't clone the boot floppy onto the root file system.\n"
+ "Aborting.");
+ return DITEM_FAILURE;
+ }
+
+ if (chroot("/mnt") == -1) {
+ msgConfirm("Unable to chroot to %s - this is bad!", "/mnt");
+ return DITEM_FAILURE;
+ }
+
+ chdir("/");
+ variable_set2(RUNNING_ON_ROOT, "yes");
+ configResolv();
+
+ /* stick a helpful shell over on the 4th VTY */
+ systemCreateHoloshell();
+
+ alreadyDone = TRUE;
+ return DITEM_SUCCESS;
+}
+
+int
+installFixitHoloShell(dialogMenuItem *self)
+{
+ systemCreateHoloshell();
+ return DITEM_SUCCESS;
+}
+
+int
+installFixitCDROM(dialogMenuItem *self)
+{
+ struct stat sb;
+
+ if (!RunningAsInit)
+ return DITEM_SUCCESS;
+
+ variable_set2(SYSTEM_STATE, "fixit");
+ (void)unlink("/mnt2");
+ (void)rmdir("/mnt2");
+
+ while (1) {
+ msgConfirm("Please insert the second FreeBSD CDROM and press return");
+ if (DITEM_STATUS(mediaSetCDROM(NULL)) != DITEM_SUCCESS || !mediaDevice || !mediaDevice->init(mediaDevice)) {
+ /* If we can't initialize it, it's probably not a FreeBSD CDROM so punt on it */
+ if (mediaDevice) {
+ mediaDevice->shutdown(mediaDevice);
+ mediaDevice = NULL;
+ }
+ if (msgYesNo("Unable to mount the CDROM - do you want to try again?") != 0)
+ return DITEM_FAILURE;
+ }
+ else
+ break;
+ }
+
+ /* Since the fixit code expects everything to be in /mnt2, and the CDROM mounting stuff /dist, do
+ * a little kludge dance here..
+ */
+ if (symlink("/dist", "/mnt2")) {
+ msgConfirm("Unable to symlink /mnt2 to the CDROM mount point. Please report this\n"
+ "unexpected failure to freebsd-bugs@FreeBSD.org.");
+ return DITEM_FAILURE;
+ }
+
+ /*
+ * If /tmp points to /mnt2/tmp from a previous fixit floppy session, it's
+ * not very good for us if we point it to the CDROM now. Rather make it
+ * a directory in the root MFS then. Experienced admins will still be
+ * able to mount their disk's /tmp over this if they need.
+ */
+ if (lstat("/tmp", &sb) == 0 && (sb.st_mode & S_IFMT) == S_IFLNK)
+ (void)unlink("/tmp");
+ Mkdir("/tmp");
+
+ /*
+ * Since setuid binaries ignore LD_LIBRARY_PATH, we indeed need the
+ * ld.so.hints file. Fortunately, it's fairly small (~ 3 KB).
+ */
+ if (!file_readable("/var/run/ld.so.hints")) {
+ Mkdir("/var/run");
+ if (vsystem("/mnt2/sbin/ldconfig -s /mnt2/usr/lib")) {
+ msgConfirm("Warning: ldconfig could not create the ld.so hints file.\n"
+ "Dynamic executables from the CDROM likely won't work.");
+ }
+ }
+
+ /* Yet another iggly hardcoded pathname. */
+ if (!file_readable("/usr/libexec/ld.so")) {
+ Mkdir("/usr/libexec");
+ if (symlink("/mnt2/usr/libexec/ld.so", "/usr/libexec/ld.so")) {
+ msgConfirm("Warning: could not create the symlink for ld.so.\n"
+ "Dynamic executables from the CDROM likely won't work.");
+ }
+ }
+
+ fixit_common();
+
+ mediaDevice->shutdown(mediaDevice);
+ msgConfirm("Please remove the FreeBSD CDROM now.");
+ return DITEM_SUCCESS;
+}
+
+int
+installFixitFloppy(dialogMenuItem *self)
+{
+ struct ufs_args args;
+
+ if (!RunningAsInit)
+ return DITEM_SUCCESS;
+
+ variable_set2(SYSTEM_STATE, "fixit");
+ memset(&args, 0, sizeof(args));
+ args.fspec = "/dev/fd0";
+ Mkdir("/mnt2");
+
+ while (1) {
+ msgConfirm("Please insert a writable fixit floppy and press return");
+ if (mount(MOUNT_UFS, "/mnt2", 0, (caddr_t)&args) != -1)
+ break;
+ msgConfirm("An attempt to mount the fixit floppy failed, maybe the filesystem\n"
+ "is unclean. Trying a forcible mount as a last resort...");
+ if (mount(MOUNT_UFS, "/mnt2", MNT_FORCE, (caddr_t)&args) != -1)
+ break;
+ if (msgYesNo("Unable to mount the fixit floppy - do you want to try again?") != 0)
+ return DITEM_FAILURE;
+ }
+
+ if (!directory_exists("/tmp"))
+ (void)symlink("/mnt2/tmp", "/tmp");
+
+ fixit_common();
+
+ unmount("/mnt2", MNT_FORCE);
+ msgConfirm("Please remove the fixit floppy now.");
+ return DITEM_SUCCESS;
+}
+
+/*
+ * The common code for both fixit variants.
+ */
+static void
+fixit_common(void)
+{
+ pid_t child;
+ int waitstatus;
+
+ if (!directory_exists("/var/tmp/vi.recover")) {
+ if (DITEM_STATUS(Mkdir("/var/tmp/vi.recover")) != DITEM_SUCCESS) {
+ msgConfirm("Warning: Was unable to create a /var/tmp/vi.recover directory.\n"
+ "vi will kvetch and moan about it as a result but should still\n"
+ "be essentially usable.");
+ }
+ }
+ if (!directory_exists("/bin"))
+ (void)Mkdir("/bin");
+ (void)symlink("/stand/sh", "/bin/sh");
+ /* Link the /etc/ files */
+ if (DITEM_STATUS(Mkdir("/etc")) != DITEM_SUCCESS)
+ msgConfirm("Unable to create an /etc directory! Things are weird on this floppy..");
+ else if ((symlink("/mnt2/etc/spwd.db", "/etc/spwd.db") == -1 && errno != EEXIST) ||
+ (symlink("/mnt2/etc/protocols", "/etc/protocols") == -1 && errno != EEXIST) ||
+ (symlink("/mnt2/etc/services", "/etc/services") == -1 && errno != EEXIST))
+ msgConfirm("Couldn't symlink the /etc/ files! I'm not sure I like this..");
+ if (!file_readable(TERMCAP_FILE))
+ create_termcap();
+ if (!(child = fork())) {
+ int i, fd;
+ struct termios foo;
+ extern int login_tty(int);
+
+ ioctl(0, TIOCNOTTY, NULL);
+ for (i = getdtablesize(); i >= 0; --i)
+ close(i);
+ fd = open("/dev/ttyv3", O_RDWR);
+ ioctl(0, TIOCSCTTY, &fd);
+ dup2(0, 1);
+ dup2(0, 2);
+ DebugFD = 2;
+ if (login_tty(fd) == -1)
+ msgDebug("fixit: I can't set the controlling terminal.\n");
+
+ signal(SIGTTOU, SIG_IGN);
+ if (tcgetattr(0, &foo) != -1) {
+ foo.c_cc[VERASE] = '\010';
+ if (tcsetattr(0, TCSANOW, &foo) == -1)
+ msgDebug("fixit shell: Unable to set erase character.\n");
+ }
+ else
+ msgDebug("fixit shell: Unable to get terminal attributes!\n");
+ setenv("PATH", "/bin:/sbin:/usr/bin:/usr/sbin:/stand:"
+ "/mnt2/stand:/mnt2/bin:/mnt2/sbin:/mnt2/usr/bin:/mnt2/usr/sbin", 1);
+ /* use the .profile from the fixit medium */
+ setenv("HOME", "/mnt2", 1);
+ chdir("/mnt2");
+ execlp("sh", "-sh", 0);
+ msgDebug("fixit shell: Failed to execute shell!\n");
+ _exit(1);;
+ }
+ else {
+ msgNotify("Waiting for fixit shell to exit. Go to VTY4 now by\n"
+ "typing ALT-F4. When you are done, type ``exit'' to exit\n"
+ "the fixit shell and be returned here.");
+ (void)waitpid(child, &waitstatus, 0);
+ }
+ dialog_clear();
+}
+
+
+int
+installExpress(dialogMenuItem *self)
+{
+ int i;
+
+ variable_set2(SYSTEM_STATE, "express");
+ if (DITEM_STATUS((i = diskPartitionEditor(self))) == DITEM_FAILURE)
+ return i;
+
+ if (DITEM_STATUS((i = diskLabelEditor(self))) == DITEM_FAILURE)
+ return i;
+
+ dialog_clear_norefresh();
+ if (DITEM_STATUS((i = installCommit(self))) == DITEM_SUCCESS) {
+ i |= DITEM_LEAVE_MENU;
+ /* Give user the option of one last configuration spree */
+ installConfigure();
+ }
+ return i | DITEM_RESTORE;
+}
+
+/* Novice mode installation */
+int
+installNovice(dialogMenuItem *self)
+{
+ int i, tries = 0;
+ Device **devs;
+
+ variable_set2(SYSTEM_STATE, "novice");
+ dialog_clear_norefresh();
+ msgConfirm("In the next menu, you will need to set up a DOS-style (\"fdisk\") partitioning\n"
+ "scheme for your hard disk. If you simply wish to devote all disk space\n"
+ "to FreeBSD (overwriting anything else that might be on the disk(s) selected)\n"
+ "then use the (A)ll command to select the default partitioning scheme followed\n"
+ "by a (Q)uit. If you wish to allocate only free space to FreeBSD, move to a\n"
+ "partition marked \"unused\" and use the (C)reate command.");
+
+nodisks:
+ if (DITEM_STATUS(diskPartitionEditor(self)) == DITEM_FAILURE)
+ return DITEM_FAILURE;
+
+ if (diskGetSelectCount(&devs) <= 0 && tries < 3) {
+ msgConfirm("You need to select some disks to operate on! Be sure to use SPACE\n"
+ "instead of RETURN in the disk selection menu when selecting a disk.");
+ ++tries;
+ goto nodisks;
+ }
+
+ dialog_clear_norefresh();
+ msgConfirm("Next, 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.");
+
+ if (DITEM_STATUS(diskLabelEditor(self)) == DITEM_FAILURE)
+ return DITEM_FAILURE;
+
+ dialog_clear_norefresh();
+ if (DITEM_STATUS((i = installCommit(self))) == DITEM_FAILURE) {
+ dialog_clear_norefresh();
+ msgConfirm("Installation completed with some errors. You may wish to\n"
+ "scroll through the debugging messages on VTY1 with the\n"
+ "scroll-lock feature. You can also chose \"No\" at the next\n"
+ "prompt and go back into the installation menus to try and retry\n"
+ "whichever operations have failed.");
+ return i | DITEM_RESTORE;
+
+ }
+ else {
+ dialog_clear_norefresh();
+ msgConfirm("Congratulations! You now have FreeBSD installed on your system.\n\n"
+ "We will now move on to the final configuration questions.\n"
+ "For any option you do not wish to configure, simply select\n"
+ "No.\n\n"
+ "If you wish to re-enter this utility after the system is up, you\n"
+ "may do so by typing: /stand/sysinstall.");
+ }
+ if (mediaDevice->type != DEVICE_TYPE_FTP && mediaDevice->type != DEVICE_TYPE_NFS) {
+ if (!msgYesNo("Would you like to configure any Ethernet or SLIP/PPP network devices?")) {
+ Device *tmp;
+
+ dialog_clear_norefresh();
+ tmp = tcpDeviceSelect();
+ dialog_clear_norefresh();
+ if (tmp && !msgYesNo("Would you like to bring the %s interface up right now?", tmp->name))
+ if (!tmp->init(tmp))
+ msgConfirm("Initialization of %s device failed.", tmp->name);
+ }
+ }
+
+ dialog_clear_norefresh();
+ if (!msgYesNo("Will this machine be an IP gateway (e.g. will it forward packets\n"
+ "between interfaces)?"))
+ variable_set2("gateway", "YES");
+
+ dialog_clear_norefresh();
+ if (!msgYesNo("Do you want to allow anonymous FTP connections to this machine?"))
+ configAnonFTP(self);
+
+ dialog_clear_norefresh();
+ if (!msgYesNo("Do you want to configure this machine as an NFS server?"))
+ configNFSServer(self);
+
+ dialog_clear_norefresh();
+ if (!msgYesNo("Do you want to configure this machine as an NFS client?"))
+ variable_set2("nfs_client", "YES");
+
+ dialog_clear_norefresh();
+ if (!msgYesNo("Would you like to customize your system console settings?")) {
+ WINDOW *w = savescr();
+
+ dmenuOpenSimple(&MenuSyscons, FALSE);
+ restorescr(w);
+ }
+
+ dialog_clear_norefresh();
+ if (!msgYesNo("Would you like to set this machine's time zone now?")) {
+ WINDOW *w = savescr();
+
+ dialog_clear();
+ systemExecute("tzsetup");
+ restorescr(w);
+ }
+
+ dialog_clear_norefresh();
+ if (!msgYesNo("Does this system have a mouse attached to it?")) {
+ WINDOW *w = savescr();
+
+ dmenuOpenSimple(&MenuMouse, FALSE);
+ restorescr(w);
+ }
+
+ /* Now would be a good time to checkpoint the configuration data */
+ configRC_conf("/etc/rc.conf");
+ sync();
+
+#ifndef USE_XIG_ENVIRONMENT
+ if (directory_exists("/usr/X11R6")) {
+ dialog_clear_norefresh();
+ if (!msgYesNo("Would you like to configure your X server at this time?"))
+ configXEnvironment(self);
+ }
+#endif
+
+ dialog_clear_norefresh();
+ if (!msgYesNo("The FreeBSD package collection is a collection of hundreds of ready-to-run\n"
+ "applications, from text editors to games to WEB servers and more. Would you\n"
+ "like to browse the collection now?"))
+ configPackages(self);
+
+ dialog_clear_norefresh();
+ if (!msgYesNo("Would you like to add any initial user accounts to the system?\n"
+ "Adding at least one account for yourself at this stage is suggested\n"
+ "since working as the \"root\" user is dangerous (it is easy to do\n"
+ "things which adversely affect the entire system)."))
+ configUsers(self);
+
+ dialog_clear_norefresh();
+ msgConfirm("Now you must set the system manager's password.\n"
+ "This is the password you'll use to log in as \"root\".");
+ {
+ WINDOW *w = savescr();
+
+ if (!systemExecute("passwd root"))
+ variable_set2("root_password", "YES");
+ restorescr(w);
+ }
+
+ dialog_clear_norefresh();
+ if (!msgYesNo("Would you like to register your FreeBSD system at this time?\n\n"
+ "PLEASE, take just 5 minutes to do this. If we're ever to get any\n"
+ "significant base of commercial software for FreeBSD, we need to\n"
+ "be able to provide more information about the size of our user community.\n"
+ "This is where your registration can really help us, and you can also\n"
+ "sign up for the new FreeBSD newsletter (its free!) at the same time.\n"))
+ configRegister(NULL);
+ else {
+ dialog_clear_norefresh();
+ msgConfirm("OK, but if you should change your mind then you always can register\n"
+ "later by typing ``/stand/sysinstall register'' or by simply visiting our\n"
+ "web site at http://www.freebsd.org/register.html");
+
+ }
+ /* XXX Put whatever other nice configuration questions you'd like to ask the user here XXX */
+
+ /* Give user the option of one last configuration spree */
+ dialog_clear_norefresh();
+ installConfigure();
+
+ return DITEM_LEAVE_MENU | DITEM_RESTORE;
+}
+
+/* The version of commit we call from the Install Custom menu */
+int
+installCustomCommit(dialogMenuItem *self)
+{
+ int i;
+
+ dialog_clear_norefresh();
+ i = installCommit(self);
+ if (DITEM_STATUS(i) == DITEM_SUCCESS) {
+ /* Give user the option of one last configuration spree */
+ installConfigure();
+ return i;
+ }
+ else
+ msgConfirm("The commit operation completed with errors. Not\n"
+ "updating /etc files.");
+ return i;
+}
+
+/*
+ * What happens when we finally decide to going ahead with the installation.
+ *
+ * This is broken into multiple stages so that the user can do a full
+ * installation but come back here again to load more distributions,
+ * perhaps from a different media type. This would allow, for
+ * example, the user to load the majority of the system from CDROM and
+ * then use ftp to load just the DES dist.
+ */
+int
+installCommit(dialogMenuItem *self)
+{
+ int i;
+ char *str;
+ Boolean need_bin;
+
+ if (!Dists)
+ distConfig(NULL);
+
+ if (!Dists)
+ if (!dmenuOpenSimple(&MenuDistributions, FALSE) && !Dists)
+ return DITEM_FAILURE | DITEM_RESTORE;
+
+ if (!mediaVerify())
+ return DITEM_FAILURE | DITEM_RESTORE;
+
+ str = variable_get(SYSTEM_STATE);
+ if (isDebug())
+ msgDebug("installCommit: System state is `%s'\n", str);
+
+ if (RunningAsInit) {
+ /* Do things we wouldn't do to a multi-user system */
+ if (DITEM_STATUS((i = installInitial())) == DITEM_FAILURE)
+ return i;
+ if (DITEM_STATUS((i = configFstab())) == DITEM_FAILURE)
+ return i;
+ }
+
+try_media:
+ if (!mediaDevice->init(mediaDevice)) {
+ if (!msgYesNo("Unable to initialize selected media. Would you like to\n"
+ "adjust your media configuration and try again?")) {
+ mediaDevice = NULL;
+ if (!mediaVerify())
+ return DITEM_FAILURE | DITEM_RESTORE;
+ else
+ goto try_media;
+ }
+ else
+ return DITEM_FAILURE | DITEM_RESTORE;
+ }
+
+ need_bin = Dists & DIST_BIN;
+ i = distExtractAll(self);
+ if (DITEM_STATUS(i) == DITEM_SUCCESS) {
+ if (need_bin && !(Dists & DIST_BIN))
+ i = installFixup(self);
+ }
+ variable_set2(SYSTEM_STATE, DITEM_STATUS(i) == DITEM_FAILURE ? "error-install" : "full-install");
+
+ /* We always try to install X with the XiG product */
+#ifdef USE_XIG_ENVIRONMENT
+ if (directory_exists("/usr/X11R6"))
+ configXEnvironment(self);
+#endif
+ return i | DITEM_RESTORE;
+}
+
+static void
+installConfigure(void)
+{
+ /* Final menu of last resort */
+ dialog_clear_norefresh();
+ if (!msgYesNo("Visit the general configuration menu for a chance to set\n"
+ "any last options?")) {
+ WINDOW *w = savescr();
+
+ dmenuOpenSimple(&MenuConfigure, FALSE);
+ restorescr(w);
+ }
+ configRC_conf("/etc/rc.conf");
+ sync();
+}
+
+int
+installFixup(dialogMenuItem *self)
+{
+ Device **devs;
+ int i;
+
+ if (!file_readable("/kernel")) {
+ if (file_readable("/kernel.GENERIC")) {
+#ifdef SAVE_USERCONFIG
+ /* Snapshot any boot -c changes back to the GENERIC kernel */
+ if (!variable_cmp(VAR_RELNAME, RELEASE_NAME))
+ save_userconfig_to_kernel("/kernel.GENERIC");
+#endif
+ if (vsystem("cp -p /kernel.GENERIC /kernel")) {
+ msgConfirm("Unable to link /kernel into place!");
+ return DITEM_FAILURE;
+ }
+ }
+ else {
+ msgConfirm("Can't find a kernel image to link to on the root file system!\n"
+ "You're going to have a hard time getting this system to\n"
+ "boot from the hard disk, I'm afraid!");
+ return DITEM_FAILURE;
+ }
+ }
+
+ /* Resurrect /dev after bin distribution screws it up */
+ if (RunningAsInit) {
+ msgNotify("Remaking all devices.. Please wait!");
+ if (vsystem("cd /dev; sh MAKEDEV all")) {
+ msgConfirm("MAKEDEV returned non-zero status");
+ return DITEM_FAILURE;
+ }
+
+ msgNotify("Resurrecting /dev entries for slices..");
+ devs = deviceFind(NULL, DEVICE_TYPE_DISK);
+ if (!devs)
+ msgFatal("Couldn't get a disk device list!");
+
+ /* Resurrect the slices that the former clobbered */
+ for (i = 0; devs[i]; i++) {
+ Disk *disk = (Disk *)devs[i]->private;
+ Chunk *c1;
+
+ if (!devs[i]->enabled)
+ continue;
+ if (!disk->chunks)
+ msgFatal("No chunk list found for %s!", disk->name);
+ for (c1 = disk->chunks->part; c1; c1 = c1->next) {
+ if (c1->type == freebsd) {
+ msgNotify("Making slice entries for %s", c1->name);
+ if (vsystem("cd /dev; sh MAKEDEV %sh", c1->name)) {
+ msgConfirm("Unable to make slice entries for %s!", c1->name);
+ return DITEM_FAILURE;
+ }
+ }
+ }
+ }
+ /* XXX Do all the last ugly work-arounds here which we'll try and excise someday right?? XXX */
+
+ msgNotify("Fixing permissions..");
+ /* BOGON #1: XFree86 extracting /usr/X11R6 with root-only perms */
+ if (directory_exists("/usr/X11R6")) {
+ vsystem("chmod -R a+r /usr/X11R6");
+ vsystem("find /usr/X11R6 -type d | xargs chmod a+x");
+ }
+ /* 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");
+
+ /* BOGON #6: deal with new boot files */
+ vsystem("touch /kernel.config");
+ vsystem("touch /boot.config");
+ if (file_readable("/stand/boot.help") && !file_readable("/boot.help"))
+ vsystem("mv /stand/boot.help /");
+
+ /* 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");
+ }
+ return DITEM_SUCCESS;
+}
+
+/* Go newfs and/or mount all the filesystems we've been asked to */
+int
+installFilesystems(dialogMenuItem *self)
+{
+ int i;
+ Disk *disk;
+ Chunk *c1, *c2, *rootdev, *swapdev, *usrdev, *vardev;
+ Device **devs;
+ PartInfo *root;
+ char dname[80];
+ extern int MakeDevChunk(Chunk *c, char *n);
+ Boolean upgrade = FALSE;
+
+ /* If we've already done this, bail out */
+ if (!variable_cmp(DISK_LABELLED, "written"))
+ return DITEM_SUCCESS;
+
+ upgrade = !variable_cmp(SYSTEM_STATE, "upgrade");
+ if (!checkLabels(TRUE, &rootdev, &swapdev, &usrdev, &vardev))
+ return DITEM_FAILURE;
+
+ if (rootdev)
+ root = (PartInfo *)rootdev->private_data;
+ else
+ root = NULL;
+
+ command_clear();
+ if (swapdev && RunningAsInit) {
+ /* As the very first thing, try to get ourselves some swap space */
+ sprintf(dname, "/dev/%s", swapdev->name);
+ if (!Fake && (!MakeDevChunk(swapdev, "/dev") || !file_readable(dname))) {
+ msgConfirm("Unable to make device node for %s in /dev!\n"
+ "The creation of filesystems will be aborted.", dname);
+ return DITEM_FAILURE;
+ }
+
+ if (!Fake) {
+ if (!swapon(dname))
+ msgNotify("Added %s as initial swap device", dname);
+ else
+ msgConfirm("WARNING! Unable to swap to %s: %s\n"
+ "This may cause the installation to fail at some point\n"
+ "if you don't have a lot of memory.", dname, strerror(errno));
+ }
+ }
+
+ if (rootdev && RunningAsInit) {
+ /* Next, create and/or mount the root device */
+ sprintf(dname, "/dev/r%sa", rootdev->disk->name);
+ if (!Fake && (!MakeDevChunk(rootdev, "/dev") || !file_readable(dname))) {
+ msgConfirm("Unable to make device node for %s in /dev!\n"
+ "The creation of filesystems will be aborted.", dname);
+ return DITEM_FAILURE;
+ }
+ if (strcmp(root->mountpoint, "/"))
+ msgConfirm("Warning: %s is marked as a root partition but is mounted on %s", rootdev->name, root->mountpoint);
+
+ if (root->newfs && (!upgrade || !msgYesNo("You are upgrading - are you SURE you want to newfs the root partition?"))) {
+ int i;
+
+ msgNotify("Making a new root filesystem on %s", dname);
+ i = vsystem("%s %s", root->newfs_cmd, dname);
+ if (i) {
+ msgConfirm("Unable to make new root filesystem on %s!\n"
+ "Command returned status %d", dname, i);
+ return DITEM_FAILURE;
+ }
+ }
+ else {
+ if (!upgrade) {
+ msgConfirm("Warning: Using existing root partition. It will be assumed\n"
+ "that you have the appropriate device entries already in /dev.");
+ }
+ msgNotify("Checking integrity of existing %s filesystem.", dname);
+ i = vsystem("fsck -y %s", dname);
+ if (i)
+ msgConfirm("Warning: fsck returned status of %d for %s.\n"
+ "This partition may be unsafe to use.", i, dname);
+ }
+
+ /* Switch to block device */
+ sprintf(dname, "/dev/%sa", rootdev->disk->name);
+ if (Mount("/mnt", dname)) {
+ msgConfirm("Unable to mount the root file system on %s! Giving up.", dname);
+ return DITEM_FAILURE;
+ }
+ }
+
+ /* Now buzz through the rest of the partitions and mount them too */
+ devs = deviceFind(NULL, DEVICE_TYPE_DISK);
+ for (i = 0; devs[i]; i++) {
+ if (!devs[i]->enabled)
+ continue;
+
+ disk = (Disk *)devs[i]->private;
+ if (!disk->chunks) {
+ msgConfirm("No chunk list found for %s!", disk->name);
+ return DITEM_FAILURE;
+ }
+ if (RunningAsInit && root && (root->newfs || upgrade)) {
+ Mkdir("/mnt/dev");
+ if (!Fake)
+ MakeDevDisk(disk, "/mnt/dev");
+ }
+ else if (!RunningAsInit && !Fake)
+ MakeDevDisk(disk, "/dev");
+
+ for (c1 = disk->chunks->part; c1; c1 = c1->next) {
+ if (c1->type == freebsd) {
+ for (c2 = c1->part; c2; c2 = c2->next) {
+ if (c2->type == part && c2->subtype != FS_SWAP && c2->private_data) {
+ PartInfo *tmp = (PartInfo *)c2->private_data;
+
+ /* Already did root */
+ if (c2 == rootdev)
+ continue;
+
+ if (tmp->newfs && (!upgrade || !msgYesNo("You are upgradding - are you SURE you want to newfs /dev/%s?", c2->name)))
+ command_shell_add(tmp->mountpoint, "%s %s/dev/r%s", tmp->newfs_cmd, RunningAsInit ? "/mnt" : "", c2->name);
+ else
+ command_shell_add(tmp->mountpoint, "fsck -y %s/dev/r%s", RunningAsInit ? "/mnt" : "", c2->name);
+ command_func_add(tmp->mountpoint, Mount, c2->name);
+ }
+ else if (c2->type == part && c2->subtype == FS_SWAP) {
+ char fname[80];
+ int i;
+
+ if (c2 == swapdev)
+ continue;
+ sprintf(fname, "%s/dev/%s", RunningAsInit ? "/mnt" : "", c2->name);
+ i = (Fake || swapon(fname));
+ if (!i)
+ msgNotify("Added %s as an additional swap device", fname);
+ else
+ msgConfirm("Unable to add %s as a swap device: %s", fname, strerror(errno));
+ }
+ }
+ }
+ else if (c1->type == fat && c1->private_data && (root->newfs || upgrade)) {
+ char name[FILENAME_MAX];
+
+ sprintf(name, "%s/%s", RunningAsInit ? "/mnt" : "", ((PartInfo *)c1->private_data)->mountpoint);
+ Mkdir(name);
+ }
+ }
+ }
+
+ if (RunningAsInit) {
+ msgNotify("Copying initial device files..");
+ /* Copy the boot floppy's dev files */
+ if ((root->newfs || upgrade) && vsystem("find -x /dev | cpio %s -pdum /mnt", cpioVerbosity())) {
+ msgConfirm("Couldn't clone the /dev files!");
+ return DITEM_FAILURE;
+ }
+ }
+
+ command_sort();
+ command_execute();
+ return DITEM_SUCCESS;
+}
+
+/* Initialize various user-settable values to their defaults */
+int
+installVarDefaults(dialogMenuItem *self)
+{
+ char *cp;
+
+ /* Set default startup options */
+ variable_set2(VAR_ROUTER_ENABLE, "NO");
+ variable_set2(VAR_RELNAME, RELEASE_NAME);
+ variable_set2(VAR_CPIO_VERBOSITY, "high");
+ variable_set2(VAR_TAPE_BLOCKSIZE, DEFAULT_TAPE_BLOCKSIZE);
+ variable_set2(VAR_INSTALL_ROOT, "/");
+ variable_set2(VAR_INSTALL_CFG, "install.cfg");
+ cp = getenv("EDITOR");
+ if (!cp)
+ cp = "/usr/bin/ee";
+ variable_set2(VAR_EDITOR, cp);
+ variable_set2(VAR_FTP_USER, "ftp");
+ variable_set2(VAR_BROWSER_PACKAGE, PACKAGE_LYNX);
+ variable_set2(VAR_BROWSER_BINARY, "/usr/local/bin/lynx");
+ variable_set2(VAR_FTP_STATE, "passive");
+ variable_set2(VAR_NFS_SECURE, "YES");
+ variable_set2(VAR_PKG_TMPDIR, "/usr/tmp");
+ variable_set2(VAR_GATED_PKG, PACKAGE_GATED);
+ variable_set2(VAR_PCNFSD_PKG, PACKAGE_PCNFSD);
+ variable_set2(VAR_MEDIA_TIMEOUT, itoa(MEDIA_TIMEOUT));
+ if (getpid() != 1)
+ variable_set2(SYSTEM_STATE, "update");
+ else
+ variable_set2(SYSTEM_STATE, "init");
+ return DITEM_SUCCESS;
+}
+
+/* Load the environment up from various system configuration files */
+void
+installEnvironment(void)
+{
+ if (file_readable("/etc/rc.conf"))
+ configEnvironmentRC_conf("/etc/rc.conf");
+ if (file_readable("/etc/resolv.conf"))
+ configEnvironmentResolv("/etc/resolv.conf");
+}
+
+/* Copy the boot floppy contents into /stand */
+Boolean
+copySelf(void)
+{
+ int i;
+
+ if (file_readable("/boot.help"))
+ vsystem("cp /boot.help /mnt");
+ msgWeHaveOutput("Copying the boot floppy to /stand on root filesystem");
+ i = vsystem("find -x /stand | cpio %s -pdum /mnt", cpioVerbosity());
+ if (i) {
+ msgConfirm("Copy returned error status of %d!", i);
+ return FALSE;
+ }
+
+ /* Copy the /etc files into their rightful place */
+ if (vsystem("cd /mnt/stand; find etc | cpio %s -pdum /mnt", cpioVerbosity())) {
+ msgConfirm("Couldn't copy up the /etc files!");
+ return TRUE;
+ }
+ return TRUE;
+}
+
+static void
+create_termcap(void)
+{
+ FILE *fp;
+
+ const char *caps[] = {
+ termcap_vt100, termcap_cons25, termcap_cons25_m, termcap_cons25r,
+ termcap_cons25r_m, termcap_cons25l1, termcap_cons25l1_m, NULL,
+ };
+ const char **cp;
+
+ if (!file_readable(TERMCAP_FILE)) {
+ Mkdir("/usr/share/misc");
+ fp = fopen(TERMCAP_FILE, "w");
+ if (!fp) {
+ msgConfirm("Unable to initialize termcap file. Some screen-oriented\nutilities may not work.");
+ return;
+ }
+ cp = caps;
+ while (*cp)
+ fprintf(fp, "%s\n", *(cp++));
+ fclose(fp);
+ }
+}
+
+#ifdef SAVE_USERCONFIG
+static void
+save_userconfig_to_kernel(char *kern)
+{
+ struct kernel *core, *boot;
+ struct list *c_isa, *b_isa, *c_dev, *b_dev;
+ int i, d;
+
+ if ((core = uc_open("-incore")) == NULL) {
+ msgDebug("save_userconf: Can't read in-core information for kernel.\n");
+ return;
+ }
+
+ if ((boot = uc_open(kern)) == NULL) {
+ msgDebug("save_userconf: Can't read device information for kernel image %s\n", kern);
+ return;
+ }
+
+ msgNotify("Saving any boot -c changes to new kernel...");
+ c_isa = uc_getdev(core, "-isa");
+ b_isa = uc_getdev(boot, "-isa");
+ if (isDebug())
+ msgDebug("save_userconf: got %d ISA device entries from core, %d from boot.\n", c_isa->ac, b_isa->ac);
+ for (d = 0; d < c_isa->ac; d++) {
+ if (isDebug())
+ msgDebug("save_userconf: ISA device loop, c_isa->av[%d] = %s\n", d, c_isa->av[d]);
+ if (strcmp(c_isa->av[d], "npx0")) { /* special case npx0, which mucks with its id_irq member */
+ c_dev = uc_getdev(core, c_isa->av[d]);
+ b_dev = uc_getdev(boot, b_isa->av[d]);
+ if (!c_dev || !b_dev) {
+ msgDebug("save_userconf: c_dev: %x b_dev: %x\n", c_dev, b_dev);
+ continue;
+ }
+ if (isDebug())
+ msgDebug("save_userconf: ISA device %s: %d config parameters (core), %d (boot)\n",
+ c_isa->av[d], c_dev->ac, b_dev->ac);
+ for (i = 0; i < c_dev->ac; i++) {
+ if (isDebug())
+ msgDebug("save_userconf: c_dev->av[%d] = %s, b_dev->av[%d] = %s\n", i, c_dev->av[i], i, b_dev->av[i]);
+ if (strcmp(c_dev->av[i], b_dev->av[i])) {
+ if (isDebug())
+ msgDebug("save_userconf: %s (boot) -> %s (core)\n",
+ c_dev->av[i], b_dev->av[i]);
+ isa_setdev(boot, c_dev);
+ }
+ }
+ }
+ else {
+ if (isDebug())
+ msgDebug("skipping npx0\n");
+ }
+ }
+ if (isDebug())
+ msgDebug("Closing kernels\n");
+ uc_close(core, 0);
+ uc_close(boot, 1);
+}
+#endif
diff --git a/usr.sbin/sade/keymap.c b/usr.sbin/sade/keymap.c
new file mode 100644
index 0000000..459f4cb
--- /dev/null
+++ b/usr.sbin/sade/keymap.c
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 1996 Joerg Wunsch
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE DEVELOPERS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $Id$
+ *
+ */
+
+#include "sysinstall.h"
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <machine/console.h>
+
+struct keymapInfo {
+ const char *name;
+ const struct keymap *map;
+};
+
+#include "keymap.h"
+
+/*
+ * keymap.h is being automatically generated by the Makefile. It
+ * contains definitions for all desired keymaps. Note that since we
+ * don't support font loading nor screen mapping during installation,
+ * we simply don't care for any other keys than the ASCII subset.
+ *
+ * Therefore, if no keymap with the exact name has been found in the
+ * first pass, we make a second pass over the table looking just for
+ * the language name only.
+ */
+
+/*
+ * Return values:
+ *
+ * 0: OK
+ * -1: no appropriate keymap found
+ * -2: error installing map (other than ENXIO which means we're not on syscons)
+ */
+
+int
+loadKeymap(const char *lang)
+{
+ int passno, err;
+ char *llang;
+ size_t l;
+ struct keymapInfo *kip;
+
+ llang = strdup(lang);
+ if (llang == NULL)
+ abort();
+
+ for (passno = 0; passno < 2; passno++)
+ {
+ if (passno > 0)
+ {
+ /* make the match more fuzzy */
+ l = strspn(llang, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789");
+ llang[l] = '\0';
+ }
+
+ l = strlen(llang);
+
+ for (kip = keymapInfos; kip->name; kip++)
+ if (strncmp(kip->name, llang, l) == 0)
+ {
+ /* Yep, got it! */
+ err = ioctl(0, PIO_KEYMAP, kip->map);
+ free(llang);
+ return (err == -1 && errno != ENOTTY)? -2: 0;
+ }
+ }
+ free(llang);
+ return -1;
+}
diff --git a/usr.sbin/sade/label.c b/usr.sbin/sade/label.c
new file mode 100644
index 0000000..4b109f4
--- /dev/null
+++ b/usr.sbin/sade/label.c
@@ -0,0 +1,1249 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last program in the `sysinstall' line - the next
+ * generation being essentially a complete rewrite.
+ *
+ * $Id: label.c,v 1.63.2.15 1997/11/05 05:54:27 obrien Exp $
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include "sysinstall.h"
+#include <ctype.h>
+#include <sys/disklabel.h>
+#include <sys/param.h>
+#include <sys/sysctl.h>
+
+/*
+ * Everything to do with editing the contents of disk labels.
+ */
+
+/* A nice message we use a lot in the disklabel editor */
+#define MSG_NOT_APPLICABLE "That option is not applicable here"
+
+/* Where to start printing the freebsd slices */
+#define CHUNK_SLICE_START_ROW 2
+#define CHUNK_PART_START_ROW 11
+
+/* The smallest filesystem we're willing to create */
+#define FS_MIN_SIZE ONE_MEG
+
+/* The smallest root filesystem we're willing to create */
+#define ROOT_MIN_SIZE 20
+
+/* The smallest swap partition we want to create by default */
+#define SWAP_MIN_SIZE 16
+
+/* The smallest /usr partition we're willing to create by default */
+#define USR_MIN_SIZE 80
+
+/* The smallest /var partition we're willing to create by default */
+#define VAR_MIN_SIZE 30
+
+/* The bottom-most row we're allowed to scribble on */
+#define CHUNK_ROW_MAX 16
+
+
+/* All the chunks currently displayed on the screen */
+static struct {
+ struct chunk *c;
+ PartType type;
+} label_chunk_info[MAX_CHUNKS + 1];
+static int here;
+
+/*** with this value we try to track the most recently added label ***/
+static int label_focus = 0, pslice_focus = 0;
+
+static int diskLabel(Device *dev);
+static int diskLabelNonInteractive(Device *dev);
+
+static int
+labelHook(dialogMenuItem *selected)
+{
+ Device **devs = NULL;
+
+ devs = deviceFind(selected->prompt, DEVICE_TYPE_DISK);
+ if (!devs) {
+ msgConfirm("Unable to find disk %s!", selected->prompt);
+ return DITEM_FAILURE;
+ }
+ /* Toggle enabled status? */
+ if (!devs[0]->enabled) {
+ devs[0]->enabled = TRUE;
+ diskLabel(devs[0]);
+ }
+ else
+ devs[0]->enabled = FALSE;
+ return DITEM_SUCCESS | DITEM_RESTORE;
+}
+
+static int
+labelCheck(dialogMenuItem *selected)
+{
+ Device **devs = NULL;
+
+ devs = deviceFind(selected->prompt, DEVICE_TYPE_DISK);
+ if (!devs || devs[0]->enabled == FALSE)
+ return FALSE;
+ return TRUE;
+}
+
+int
+diskLabelEditor(dialogMenuItem *self)
+{
+ DMenu *menu;
+ Device **devs;
+ int i, cnt;
+
+ i = 0;
+ cnt = diskGetSelectCount(&devs);
+ if (cnt == -1) {
+ msgConfirm("No disks found! Please verify that your disk controller is being\n"
+ "properly probed at boot time. See the Hardware Guide on the\n"
+ "Documentation menu for clues on diagnosing this type of problem.");
+ return DITEM_FAILURE;
+ }
+ else if (cnt) {
+ int i;
+
+ /* Some are already selected */
+ if (variable_get(VAR_NONINTERACTIVE))
+ i |= diskLabelNonInteractive(NULL);
+ else
+ i |= diskLabel(NULL);
+ }
+ else {
+ /* No disks are selected, fall-back case now */
+ cnt = deviceCount(devs);
+ if (cnt == 1) {
+ devs[0]->enabled = TRUE;
+ if (variable_get(VAR_NONINTERACTIVE))
+ i = diskLabelNonInteractive(devs[0]);
+ else
+ i = diskLabel(devs[0]);
+ }
+ else {
+ menu = deviceCreateMenu(&MenuDiskDevices, DEVICE_TYPE_DISK, labelHook, labelCheck);
+ if (!menu) {
+ msgConfirm("No devices suitable for installation found!\n\n"
+ "Please verify that your disk controller (and attached drives)\n"
+ "were detected properly. This can be done by pressing the\n"
+ "[Scroll Lock] key and using the Arrow keys to move back to\n"
+ "the boot messages. Press [Scroll Lock] again to return.");
+ i = DITEM_FAILURE;
+ }
+ else {
+ i = dmenuOpenSimple(menu, FALSE) ? DITEM_SUCCESS : DITEM_FAILURE;
+ free(menu);
+ }
+ i |= DITEM_RESTORE;
+ }
+ }
+ if (DITEM_STATUS(i) != DITEM_FAILURE) {
+ char *cp;
+
+ if (((cp = variable_get(DISK_LABELLED)) == NULL) || (strcmp(cp, "written")))
+ variable_set2(DISK_LABELLED, "yes");
+ }
+ 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");
+ i = DITEM_SUCCESS;
+ }
+ return i;
+}
+
+/* See if we're already using a desired partition name */
+static Boolean
+check_conflict(char *name)
+{
+ int i;
+
+ for (i = 0; label_chunk_info[i].c; i++)
+ if ((label_chunk_info[i].type == PART_FILESYSTEM || label_chunk_info[i].type == PART_FAT)
+ && label_chunk_info[i].c->private_data
+ && !strcmp(((PartInfo *)label_chunk_info[i].c->private_data)->mountpoint, name))
+ return TRUE;
+ return FALSE;
+}
+
+/* How much space is in this FreeBSD slice? */
+static int
+space_free(struct chunk *c)
+{
+ struct chunk *c1;
+ int sz = c->size;
+
+ for (c1 = c->part; c1; c1 = c1->next) {
+ if (c1->type != unused)
+ sz -= c1->size;
+ }
+ if (sz < 0)
+ msgFatal("Partitions are larger than actual chunk??");
+ return sz;
+}
+
+/* Snapshot the current situation into the displayed chunks structure */
+static void
+record_label_chunks(Device **devs, Device *dev)
+{
+ int i, j, p;
+ struct chunk *c1, *c2;
+ Disk *d;
+
+ j = p = 0;
+ /* First buzz through and pick up the FreeBSD slices */
+ for (i = 0; devs[i]; i++) {
+ if ((dev && devs[i] != dev) || !devs[i]->enabled)
+ continue;
+ d = (Disk *)devs[i]->private;
+ if (!d->chunks)
+ msgFatal("No chunk list found for %s!", d->name);
+
+ /* Put the slice entries first */
+ for (c1 = d->chunks->part; c1; c1 = c1->next) {
+ if (c1->type == freebsd) {
+ label_chunk_info[j].type = PART_SLICE;
+ label_chunk_info[j].c = c1;
+ ++j;
+ }
+ }
+ }
+
+ /* Now run through again and get the FreeBSD partition entries */
+ for (i = 0; devs[i]; i++) {
+ if (!devs[i]->enabled)
+ continue;
+ d = (Disk *)devs[i]->private;
+ /* Then buzz through and pick up the partitions */
+ for (c1 = d->chunks->part; c1; c1 = c1->next) {
+ if (c1->type == freebsd) {
+ for (c2 = c1->part; c2; c2 = c2->next) {
+ if (c2->type == part) {
+ if (c2->subtype == FS_SWAP)
+ label_chunk_info[j].type = PART_SWAP;
+ else
+ label_chunk_info[j].type = PART_FILESYSTEM;
+ label_chunk_info[j].c = c2;
+ ++j;
+ }
+ }
+ }
+ else if (c1->type == fat) {
+ label_chunk_info[j].type = PART_FAT;
+ label_chunk_info[j].c = c1;
+ ++j;
+ }
+ }
+ }
+ label_chunk_info[j].c = NULL;
+ if (here >= j) {
+ here = j ? j - 1 : 0;
+ }
+}
+
+/* A new partition entry */
+static PartInfo *
+new_part(char *mpoint, Boolean newfs, u_long size)
+{
+ PartInfo *ret;
+
+ if (!mpoint)
+ mpoint = "/change_me";
+
+ ret = (PartInfo *)safe_malloc(sizeof(PartInfo));
+ sstrncpy(ret->mountpoint, mpoint, FILENAME_MAX);
+ strcpy(ret->newfs_cmd, "newfs -b 8192 -f 1024");
+ ret->newfs = newfs;
+ if (!size)
+ return ret;
+ return ret;
+}
+
+/* Get the mountpoint for a partition and save it away */
+static PartInfo *
+get_mountpoint(struct chunk *old)
+{
+ char *val;
+ PartInfo *tmp;
+
+ if (old && old->private_data)
+ tmp = old->private_data;
+ else
+ tmp = NULL;
+ if (!old) {
+ DialogX = 14;
+ DialogY = 16;
+ }
+ val = msgGetInput(tmp ? tmp->mountpoint : NULL, "Please specify a mount point for the partition");
+ DialogX = DialogY = 0;
+ if (!val || !*val) {
+ if (!old)
+ return NULL;
+ else {
+ free(old->private_data);
+ old->private_data = NULL;
+ }
+ return NULL;
+ }
+
+ /* Is it just the same value? */
+ if (tmp && !strcmp(tmp->mountpoint, val))
+ return NULL;
+
+ /* Did we use it already? */
+ if (check_conflict(val)) {
+ msgConfirm("You already have a mount point for %s assigned!", val);
+ return NULL;
+ }
+
+ /* Is it bogus? */
+ if (*val != '/') {
+ msgConfirm("Mount point must start with a / character");
+ return NULL;
+ }
+
+ /* Is it going to be mounted on root? */
+ if (!strcmp(val, "/")) {
+ if (old)
+ old->flags |= CHUNK_IS_ROOT;
+ }
+ else if (old)
+ old->flags &= ~CHUNK_IS_ROOT;
+
+ safe_free(tmp);
+ tmp = new_part(val, TRUE, 0);
+ if (old) {
+ old->private_data = tmp;
+ old->private_free = safe_free;
+ }
+ return tmp;
+}
+
+/* Get the type of the new partiton */
+static PartType
+get_partition_type(void)
+{
+ char selection[20];
+ int i;
+
+ static unsigned char *fs_types[] = {
+ "FS",
+ "A file system",
+ "Swap",
+ "A swap partition.",
+ };
+ DialogX = 7;
+ DialogY = 8;
+ i = dialog_menu("Please choose a partition type",
+ "If you want to use this partition for swap space, select Swap.\n"
+ "If you want to put a filesystem on it, choose FS.",
+ -1, -1, 2, 2, fs_types, selection, NULL, NULL);
+ DialogX = DialogY = 0;
+ if (!i) {
+ if (!strcmp(selection, "FS"))
+ return PART_FILESYSTEM;
+ else if (!strcmp(selection, "Swap"))
+ return PART_SWAP;
+ }
+ return PART_NONE;
+}
+
+/* If the user wants a special newfs command for this, set it */
+static void
+getNewfsCmd(PartInfo *p)
+{
+ char *val;
+
+ val = msgGetInput(p->newfs_cmd,
+ "Please enter the newfs command and options you'd like to use in\n"
+ "creating this file system.");
+ if (val)
+ sstrncpy(p->newfs_cmd, val, NEWFS_CMD_MAX);
+}
+
+#define MAX_MOUNT_NAME 12
+
+#define PART_PART_COL 0
+#define PART_MOUNT_COL 8
+#define PART_SIZE_COL (PART_MOUNT_COL + MAX_MOUNT_NAME + 3)
+#define PART_NEWFS_COL (PART_SIZE_COL + 7)
+#define PART_OFF 38
+
+#define TOTAL_AVAIL_LINES (10)
+#define PSLICE_SHOWABLE (4)
+
+
+/* stick this all up on the screen */
+static void
+print_label_chunks(void)
+{
+ int i, j, srow, prow, pcol;
+ int sz;
+ char clrmsg[80];
+ int ChunkPartStartRow;
+ WINDOW *ChunkWin;
+
+ /********************************************************/
+ /*** These values are for controling screen resources ***/
+ /*** Each label line holds up to 2 labels, so beware! ***/
+ /*** strategy will be to try to always make sure the ***/
+ /*** highlighted label is in the active display area. ***/
+ /********************************************************/
+ int pslice_max, label_max;
+ int pslice_count, label_count, label_focus_found, pslice_focus_found;
+
+ attrset(A_REVERSE);
+ mvaddstr(0, 25, "FreeBSD Disklabel Editor");
+ attrset(A_NORMAL);
+
+ /*** Count the number of parition slices ***/
+ pslice_count = 0;
+ for (i = 0; label_chunk_info[i].c ; i++) {
+ if (label_chunk_info[i].type == PART_SLICE)
+ ++pslice_count;
+ }
+ pslice_max = pslice_count;
+
+ /*** 4 line max for partition slices ***/
+ if (pslice_max > PSLICE_SHOWABLE) {
+ pslice_max = PSLICE_SHOWABLE;
+ }
+ ChunkPartStartRow = CHUNK_SLICE_START_ROW + 3 + pslice_max;
+
+ /*** View partition slices modulo pslice_max ***/
+ label_max = TOTAL_AVAIL_LINES - pslice_max;
+
+ for (i = 0; i < 2; i++) {
+ mvaddstr(ChunkPartStartRow - 2, PART_PART_COL + (i * PART_OFF), "Part");
+ mvaddstr(ChunkPartStartRow - 1, PART_PART_COL + (i * PART_OFF), "----");
+
+ mvaddstr(ChunkPartStartRow - 2, PART_MOUNT_COL + (i * PART_OFF), "Mount");
+ mvaddstr(ChunkPartStartRow - 1, PART_MOUNT_COL + (i * PART_OFF), "-----");
+
+ mvaddstr(ChunkPartStartRow - 2, PART_SIZE_COL + (i * PART_OFF) + 2, "Size");
+ mvaddstr(ChunkPartStartRow - 1, PART_SIZE_COL + (i * PART_OFF) + 2, "----");
+
+ mvaddstr(ChunkPartStartRow - 2, PART_NEWFS_COL + (i * PART_OFF), "Newfs");
+ mvaddstr(ChunkPartStartRow - 1, PART_NEWFS_COL + (i * PART_OFF), "-----");
+ }
+ srow = CHUNK_SLICE_START_ROW;
+ prow = 0;
+ pcol = 0;
+
+ /*** these variables indicate that the focused item is shown currently ***/
+ label_focus_found = 0;
+ pslice_focus_found = 0;
+
+ label_count = 0;
+ pslice_count = 0;
+ mvprintw(CHUNK_SLICE_START_ROW - 1, 0, " ");
+ mvprintw(CHUNK_SLICE_START_ROW + pslice_max, 0, " ");
+
+ ChunkWin = newwin(CHUNK_ROW_MAX - ChunkPartStartRow, 76, ChunkPartStartRow, 0);
+
+ wclear(ChunkWin);
+ /*** wrefresh(ChunkWin); ***/
+
+ for (i = 0; label_chunk_info[i].c; i++) {
+ /* Is it a slice entry displayed at the top? */
+ if (label_chunk_info[i].type == PART_SLICE) {
+ /*** This causes the new pslice to replace the previous display ***/
+ /*** focus must remain on the most recently active pslice ***/
+ if (pslice_count == pslice_max) {
+ if (pslice_focus_found) {
+ /*** This is where we can mark the more following ***/
+ attrset(A_BOLD);
+ mvprintw(CHUNK_SLICE_START_ROW + pslice_max, 0, "***MORE***");
+ attrset(A_NORMAL);
+ continue;
+ }
+ else {
+ /*** this is where we set the more previous ***/
+ attrset(A_BOLD);
+ mvprintw(CHUNK_SLICE_START_ROW - 1, 0, "***MORE***");
+ attrset(A_NORMAL);
+ pslice_count = 0;
+ srow = CHUNK_SLICE_START_ROW;
+ }
+ }
+
+ sz = space_free(label_chunk_info[i].c);
+ if (i == here)
+ attrset(ATTR_SELECTED);
+ if (i == pslice_focus)
+ pslice_focus_found = -1;
+
+ mvprintw(srow++, 0,
+ "Disk: %s\tPartition name: %s\tFree: %d blocks (%dMB)",
+ label_chunk_info[i].c->disk->name, label_chunk_info[i].c->name,
+ sz, (sz / ONE_MEG));
+ attrset(A_NORMAL);
+ clrtoeol();
+ move(0, 0);
+ /*** refresh(); ***/
+ ++pslice_count;
+ }
+ /* Otherwise it's a DOS, swap or filesystem entry in the Chunk window */
+ else {
+ char onestr[PART_OFF], num[10], *mountpoint, *newfs;
+
+ /*
+ * We copy this into a blank-padded string so that it looks like
+ * a solid bar in reverse-video
+ */
+ memset(onestr, ' ', PART_OFF - 1);
+ onestr[PART_OFF - 1] = '\0';
+
+ /*** Track how many labels have been displayed ***/
+ if (label_count == ((label_max - 1 ) * 2)) {
+ if (label_focus_found) {
+ continue;
+ }
+ else {
+ label_count = 0;
+ prow = 0;
+ pcol = 0;
+ }
+ }
+
+ /* Go for two columns if we've written one full columns worth */
+ /*** if (prow == (CHUNK_ROW_MAX - ChunkPartStartRow)) ***/
+ if (label_count == label_max - 1) {
+ pcol = PART_OFF;
+ prow = 0;
+ }
+ memcpy(onestr + PART_PART_COL, label_chunk_info[i].c->name, strlen(label_chunk_info[i].c->name));
+ /* If it's a filesystem, display the mountpoint */
+ if (label_chunk_info[i].c->private_data
+ && (label_chunk_info[i].type == PART_FILESYSTEM || label_chunk_info[i].type == PART_FAT))
+ mountpoint = ((PartInfo *)label_chunk_info[i].c->private_data)->mountpoint;
+ else if (label_chunk_info[i].type == PART_SWAP)
+ mountpoint = "swap";
+ else
+ mountpoint = "<none>";
+
+ /* Now display the newfs field */
+ if (label_chunk_info[i].type == PART_FAT)
+ newfs = "DOS";
+ else if (label_chunk_info[i].c->private_data && label_chunk_info[i].type == PART_FILESYSTEM)
+ newfs = ((PartInfo *)label_chunk_info[i].c->private_data)->newfs ? "UFS Y" : "UFS N";
+ else if (label_chunk_info[i].type == PART_SWAP)
+ newfs = "SWAP";
+ else
+ newfs = "*";
+ for (j = 0; j < MAX_MOUNT_NAME && mountpoint[j]; j++)
+ onestr[PART_MOUNT_COL + j] = mountpoint[j];
+ snprintf(num, 10, "%4ldMB", label_chunk_info[i].c->size ? label_chunk_info[i].c->size / ONE_MEG : 0);
+ memcpy(onestr + PART_SIZE_COL, num, strlen(num));
+ memcpy(onestr + PART_NEWFS_COL, newfs, strlen(newfs));
+ onestr[PART_NEWFS_COL + strlen(newfs)] = '\0';
+ if (i == label_focus) {
+ label_focus_found = -1;
+ wattrset(ChunkWin, A_BOLD);
+ }
+ if (i == here)
+ wattrset(ChunkWin, ATTR_SELECTED);
+
+ /*** lazy man's way of padding this string ***/
+ while (strlen( onestr ) < 37)
+ strcat(onestr, " ");
+
+ mvwaddstr(ChunkWin, prow, pcol, onestr);
+ wattrset(ChunkWin, A_NORMAL);
+ move(0, 0);
+ ++prow;
+ ++label_count;
+ }
+ }
+
+ /*** this will erase all the extra stuff ***/
+ memset(clrmsg, ' ', 37);
+ clrmsg[37] = '\0';
+
+ while (pslice_count < pslice_max) {
+ mvprintw(srow++, 0, clrmsg);
+ clrtoeol();
+ ++pslice_count;
+ }
+ while (label_count < (2 * (label_max - 1))) {
+ mvwaddstr(ChunkWin, prow++, pcol, clrmsg);
+ ++label_count;
+ if (prow == (label_max - 1)) {
+ prow = 0;
+ pcol = PART_OFF;
+ }
+ }
+ refresh();
+ wrefresh(ChunkWin);
+}
+
+static void
+print_command_summary(void)
+{
+ mvprintw(17, 0, "The following commands are valid here (upper or lower case):");
+ mvprintw(18, 0, "C = Create D = Delete M = Mount pt.");
+ if (!RunningAsInit)
+ mvprintw(18, 49, "W = Write");
+ mvprintw(19, 0, "N = Newfs Opts T = Newfs Toggle U = Undo Q = Finish");
+ mvprintw(20, 0, "A = Auto Defaults for all!");
+ mvprintw(22, 0, "Use F1 or ? to get more help, arrow keys to select.");
+ move(0, 0);
+}
+
+static void
+clear_wins(void)
+{
+ extern void print_label_chunks();
+ clear();
+ print_label_chunks();
+}
+
+static int
+diskLabel(Device *dev)
+{
+ int sz, key = 0;
+ Boolean labeling;
+ char *msg = NULL;
+ PartInfo *p, *oldp;
+ PartType type;
+ Device **devs;
+
+ label_focus = 0;
+ pslice_focus = 0;
+ here = 0;
+
+ devs = deviceFind(NULL, DEVICE_TYPE_DISK);
+ if (!devs) {
+ msgConfirm("No disks found!");
+ return DITEM_FAILURE;
+ }
+ labeling = TRUE;
+ keypad(stdscr, TRUE);
+ record_label_chunks(devs, dev);
+
+ clear();
+ while (labeling) {
+ char *cp;
+
+ print_label_chunks();
+ print_command_summary();
+ if (msg) {
+ attrset(title_attr); mvprintw(23, 0, msg); attrset(A_NORMAL);
+ clrtoeol();
+ beep();
+ msg = NULL;
+ }
+ else {
+ move(23, 0);
+ clrtoeol();
+ }
+
+ refresh();
+ key = getch();
+ switch (toupper(key)) {
+ int i;
+ static char _msg[40];
+
+ case '\014': /* ^L */
+ clear_wins();
+ break;
+
+ case '\020': /* ^P */
+ case KEY_UP:
+ case '-':
+ if (here != 0)
+ --here;
+ else
+ while (label_chunk_info[here + 1].c)
+ ++here;
+ break;
+
+ case '\016': /* ^N */
+ case KEY_DOWN:
+ case '+':
+ case '\r':
+ case '\n':
+ if (label_chunk_info[here + 1].c)
+ ++here;
+ else
+ here = 0;
+ break;
+
+ case KEY_HOME:
+ here = 0;
+ break;
+
+ case KEY_END:
+ while (label_chunk_info[here + 1].c)
+ ++here;
+ break;
+
+ case KEY_F(1):
+ case '?':
+ systemDisplayHelp("partition");
+ clear_wins();
+ break;
+
+ case 'A':
+ if (label_chunk_info[here].type != PART_SLICE) {
+ msg = "You can only do this in a disk slice (at top of screen)";
+ break;
+ }
+ sz = space_free(label_chunk_info[here].c);
+ if (sz <= FS_MIN_SIZE)
+ msg = "Not enough free space to create a new partition in the slice";
+ else {
+ struct chunk *tmp;
+ int mib[2];
+ int physmem;
+ size_t size, swsize;
+ char *cp;
+ Chunk *rootdev, *swapdev, *usrdev, *vardev;
+
+ (void)checkLabels(FALSE, &rootdev, &swapdev, &usrdev, &vardev);
+ if (!rootdev) {
+ cp = variable_get(VAR_ROOT_SIZE);
+ tmp = Create_Chunk_DWIM(label_chunk_info[here].c->disk, label_chunk_info[here].c,
+ (cp ? atoi(cp) : 32) * ONE_MEG, part, FS_BSDFFS, CHUNK_IS_ROOT);
+ if (!tmp) {
+ msgConfirm("Unable to create the root partition. Too big?");
+ clear_wins();
+ break;
+ }
+ tmp->private_data = new_part("/", TRUE, tmp->size);
+ tmp->private_free = safe_free;
+ record_label_chunks(devs, dev);
+ }
+
+ if (!swapdev) {
+ cp = variable_get(VAR_SWAP_SIZE);
+ if (cp)
+ swsize = atoi(cp) * ONE_MEG;
+ else {
+ mib[0] = CTL_HW;
+ mib[1] = HW_PHYSMEM;
+ size = sizeof physmem;
+ sysctl(mib, 2, &physmem, &size, (void *)0, (size_t)0);
+ swsize = 16 * ONE_MEG + (physmem * 2 / 512);
+ }
+ tmp = Create_Chunk_DWIM(label_chunk_info[here].c->disk, label_chunk_info[here].c,
+ swsize, part, FS_SWAP, 0);
+ if (!tmp) {
+ msgConfirm("Unable to create the swap partition. Too big?");
+ clear_wins();
+ break;
+ }
+ tmp->private_data = 0;
+ tmp->private_free = safe_free;
+ record_label_chunks(devs, dev);
+ }
+
+ if (!vardev) {
+ cp = variable_get(VAR_VAR_SIZE);
+ tmp = Create_Chunk_DWIM(label_chunk_info[here].c->disk, label_chunk_info[here].c,
+ (cp ? atoi(cp) : VAR_MIN_SIZE) * ONE_MEG, part, FS_BSDFFS, 0);
+ if (!tmp) {
+ msgConfirm("Less than %dMB free for /var - you will need to\n"
+ "partition your disk manually with a custom install!",
+ (cp ? atoi(cp) : VAR_MIN_SIZE));
+ clear_wins();
+ break;
+ }
+ tmp->private_data = new_part("/var", TRUE, tmp->size);
+ tmp->private_free = safe_free;
+ record_label_chunks(devs, dev);
+ }
+
+ if (!usrdev) {
+ cp = variable_get(VAR_USR_SIZE);
+ if (cp)
+ sz = atoi(cp) * ONE_MEG;
+ else
+ sz = space_free(label_chunk_info[here].c);
+ if (sz) {
+ if (sz < (USR_MIN_SIZE * ONE_MEG)) {
+ msgConfirm("Less than %dMB free for /usr - you will need to\n"
+ "partition your disk manually with a custom install!", USR_MIN_SIZE);
+ clear_wins();
+ break;
+ }
+
+ tmp = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
+ label_chunk_info[here].c,
+ sz, part, FS_BSDFFS, 0);
+ if (!tmp) {
+ msgConfirm("Unable to create the /usr partition. Not enough space?\n"
+ "You will need to partition your disk manually with a custom install!");
+ clear_wins();
+ break;
+ }
+ tmp->private_data = new_part("/usr", TRUE, tmp->size);
+ tmp->private_free = safe_free;
+ record_label_chunks(devs, dev);
+ }
+ }
+ /* At this point, we're reasonably "labelled" */
+ if (((cp = variable_get(DISK_LABELLED)) == NULL) || (strcmp(cp, "written")))
+ variable_set2(DISK_LABELLED, "yes");
+ }
+ break;
+
+ case 'C':
+ if (label_chunk_info[here].type != PART_SLICE) {
+ msg = "You can only do this in a master partition (see top of screen)";
+ break;
+ }
+ sz = space_free(label_chunk_info[here].c);
+ if (sz <= FS_MIN_SIZE) {
+ msg = "Not enough space to create an additional FreeBSD partition";
+ break;
+ }
+ else {
+ char *val;
+ int size;
+ struct chunk *tmp;
+ char osize[80];
+ u_long flags = 0;
+
+ sprintf(osize, "%d", sz);
+ DialogX = 3;
+ DialogY = 2;
+ val = msgGetInput(osize,
+ "Please specify the partition size in blocks or append a trailing M for\n"
+ "megabytes or C for cylinders. %d blocks (%dMB) are free.",
+ sz, sz / ONE_MEG);
+ DialogX = DialogY = 0;
+ if (!val || (size = strtol(val, &cp, 0)) <= 0) {
+ clear_wins();
+ break;
+ }
+
+ if (*cp) {
+ if (toupper(*cp) == 'M')
+ size *= ONE_MEG;
+ else if (toupper(*cp) == 'C')
+ size *= (label_chunk_info[here].c->disk->bios_hd * label_chunk_info[here].c->disk->bios_sect);
+ }
+ if (size <= FS_MIN_SIZE) {
+ msgConfirm("The minimum filesystem size is %dMB", FS_MIN_SIZE / ONE_MEG);
+ clear_wins();
+ break;
+ }
+ type = get_partition_type();
+ if (type == PART_NONE) {
+ clear_wins();
+ beep();
+ break;
+ }
+
+ if (type == PART_FILESYSTEM) {
+ if ((p = get_mountpoint(NULL)) == NULL) {
+ clear_wins();
+ beep();
+ break;
+ }
+ else if (!strcmp(p->mountpoint, "/"))
+ flags |= CHUNK_IS_ROOT;
+ else
+ flags &= ~CHUNK_IS_ROOT;
+ }
+ else
+ p = NULL;
+
+ if ((flags & CHUNK_IS_ROOT)) {
+ if (!(label_chunk_info[here].c->flags & CHUNK_BSD_COMPAT)) {
+ msgConfirm("This region cannot be used for your root partition as the\n"
+ "FreeBSD boot code cannot deal with a root partition created\n"
+ "in that location. Please choose another location or smaller\n"
+ "size for your root partition and try again!");
+ clear_wins();
+ break;
+ }
+ if (size < (ROOT_MIN_SIZE * ONE_MEG)) {
+ msgConfirm("Warning: This is smaller than the recommended size for a\n"
+ "root partition. For a variety of reasons, root\n"
+ "partitions should usually be at least %dMB in size", ROOT_MIN_SIZE);
+ }
+ }
+ tmp = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
+ label_chunk_info[here].c,
+ size, part,
+ (type == PART_SWAP) ? FS_SWAP : FS_BSDFFS,
+ flags);
+ if (!tmp) {
+ msgConfirm("Unable to create the partition. Too big?");
+ clear_wins();
+ break;
+ }
+ if ((flags & CHUNK_IS_ROOT) && (tmp->flags & CHUNK_PAST_1024)) {
+ msgConfirm("This region cannot be used for your root partition as it starts\n"
+ "or extends past the 1024'th cylinder mark and is thus a\n"
+ "poor location to boot from. Please choose another\n"
+ "location (or smaller size) for your root partition and try again!");
+ Delete_Chunk(label_chunk_info[here].c->disk, tmp);
+ clear_wins();
+ break;
+ }
+ if (type != PART_SWAP) {
+ /* This is needed to tell the newfs -u about the size */
+ tmp->private_data = new_part(p->mountpoint, p->newfs, tmp->size);
+ safe_free(p);
+ }
+ else
+ tmp->private_data = p;
+ tmp->private_free = safe_free;
+ if (((cp = variable_get(DISK_LABELLED)) == NULL) || (strcmp(cp, "written")))
+ variable_set2(DISK_LABELLED, "yes");
+ record_label_chunks(devs, dev);
+ clear_wins();
+ /*** This is where we assign focus to new label so it shows ***/
+ {
+ int i;
+ label_focus = -1;
+ for (i = 0; label_chunk_info[i].c; ++i) {
+ if (label_chunk_info[i].c == tmp) {
+ label_focus = i;
+ break;
+ }
+ }
+ if (label_focus == -1)
+ label_focus = i - 1;
+ }
+ }
+ break;
+
+ case KEY_DC:
+ case 'D': /* delete */
+ if (label_chunk_info[here].type == PART_SLICE) {
+ msg = MSG_NOT_APPLICABLE;
+ break;
+ }
+ else if (label_chunk_info[here].type == PART_FAT) {
+ msg = "Use the Disk Partition Editor to delete DOS partitions";
+ break;
+ }
+ Delete_Chunk(label_chunk_info[here].c->disk, label_chunk_info[here].c);
+ if (((cp = variable_get(DISK_LABELLED)) == NULL) || (strcmp(cp, "written")))
+ variable_set2(DISK_LABELLED, "yes");
+ record_label_chunks(devs, dev);
+ break;
+
+ case 'M': /* mount */
+ switch(label_chunk_info[here].type) {
+ case PART_SLICE:
+ msg = MSG_NOT_APPLICABLE;
+ break;
+
+ case PART_SWAP:
+ msg = "You don't need to specify a mountpoint for a swap partition.";
+ break;
+
+ case PART_FAT:
+ case PART_FILESYSTEM:
+ oldp = label_chunk_info[here].c->private_data;
+ p = get_mountpoint(label_chunk_info[here].c);
+ if (p) {
+ if (!oldp)
+ p->newfs = FALSE;
+ if (label_chunk_info[here].type == PART_FAT
+ && (!strcmp(p->mountpoint, "/") || !strcmp(p->mountpoint, "/usr")
+ || !strcmp(p->mountpoint, "/var"))) {
+ msgConfirm("%s is an invalid mount point for a DOS partition!", p->mountpoint);
+ strcpy(p->mountpoint, "/bogus");
+ }
+ }
+ if (((cp = variable_get(DISK_LABELLED)) == NULL) || (strcmp(cp, "written")))
+ variable_set2(DISK_LABELLED, "yes");
+ record_label_chunks(devs, dev);
+ clear_wins();
+ break;
+
+ default:
+ msgFatal("Bogus partition under cursor???");
+ break;
+ }
+ break;
+
+ case 'N': /* Set newfs options */
+ if (label_chunk_info[here].c->private_data &&
+ ((PartInfo *)label_chunk_info[here].c->private_data)->newfs)
+ getNewfsCmd(label_chunk_info[here].c->private_data);
+ else
+ msg = MSG_NOT_APPLICABLE;
+ clear_wins();
+ break;
+
+ case 'T': /* Toggle newfs state */
+ if (label_chunk_info[here].type == PART_FILESYSTEM) {
+ PartInfo *pi = ((PartInfo *)label_chunk_info[here].c->private_data);
+ label_chunk_info[here].c->private_data =
+ new_part(pi ? pi->mountpoint : NULL, pi ? !pi->newfs : TRUE, label_chunk_info[here].c->size);
+ safe_free(pi);
+ label_chunk_info[here].c->private_free = safe_free;
+ if (((cp = variable_get(DISK_LABELLED)) == NULL) || (strcmp(cp, "written")))
+ variable_set2(DISK_LABELLED, "yes");
+ }
+ else
+ msg = MSG_NOT_APPLICABLE;
+ break;
+
+ case 'U':
+ clear();
+ if ((cp = variable_get(DISK_LABELLED)) && !strcmp(cp, "written")) {
+ msgConfirm("You've already written out your changes -\n"
+ "it's too late to undo!");
+ }
+ else if (!msgYesNo("Are you SURE you want to Undo everything?")) {
+ variable_unset(DISK_PARTITIONED);
+ variable_unset(DISK_LABELLED);
+ for (i = 0; devs[i]; i++) {
+ Disk *d;
+
+ if (!devs[i]->enabled)
+ continue;
+ else if ((d = Open_Disk(devs[i]->name)) != NULL) {
+ Free_Disk(devs[i]->private);
+ devs[i]->private = d;
+ diskPartition(devs[i]);
+ }
+ }
+ record_label_chunks(devs, dev);
+ }
+ clear_wins();
+ break;
+
+ case 'W':
+ if ((cp = variable_get(DISK_LABELLED)) && !strcmp(cp, "written")) {
+ msgConfirm("You've already written out your changes - if you\n"
+ "wish to overwrite them, you'll have to start this\n"
+ "procedure again from the beginning.");
+ }
+ else if (!msgYesNo("WARNING: This should only be used when modifying an EXISTING\n"
+ "installation. If you are installing FreeBSD for the first time\n"
+ "then you should simply type Q when you're finished here and your\n"
+ "changes will be committed in one batch automatically at the end of\n"
+ "these questions.\n\n"
+ "Are you absolutely sure you want to do this now?")) {
+ variable_set2(DISK_LABELLED, "yes");
+ diskLabelCommit(NULL);
+ }
+ clear_wins();
+ break;
+
+ case '|':
+ if (!msgYesNo("Are you sure you want to go into Wizard mode?\n\n"
+ "This is an entirely undocumented feature which you are not\n"
+ "expected to understand!")) {
+ int i;
+ Device **devs;
+
+ dialog_clear();
+ end_dialog();
+ DialogActive = FALSE;
+ devs = deviceFind(NULL, DEVICE_TYPE_DISK);
+ if (!devs) {
+ msgConfirm("Can't find any disk devices!");
+ break;
+ }
+ for (i = 0; devs[i] && ((Disk *)devs[i]->private); i++) {
+ if (devs[i]->enabled)
+ slice_wizard(((Disk *)devs[i]->private));
+ }
+ if (((cp = variable_get(DISK_LABELLED)) == NULL) || (strcmp(cp, "written")))
+ variable_set2(DISK_LABELLED, "yes");
+ DialogActive = TRUE;
+ record_label_chunks(devs, dev);
+ clear_wins();
+ }
+ else
+ msg = "A most prudent choice!";
+ break;
+
+ case '\033': /* ESC */
+ case 'Q':
+ labeling = FALSE;
+ break;
+
+ default:
+ beep();
+ sprintf(_msg, "Invalid key %d - Type F1 or ? for help", key);
+ msg = _msg;
+ break;
+ }
+ if (label_chunk_info[here].type == PART_SLICE)
+ pslice_focus = here;
+ else
+ label_focus = here;
+ }
+ return DITEM_SUCCESS | DITEM_RESTORE;
+}
+
+static int
+diskLabelNonInteractive(Device *dev)
+{
+ char *cp;
+ PartType type;
+ PartInfo *p;
+ u_long flags = 0;
+ int i, status;
+ Device **devs;
+ Disk *d;
+
+ status = DITEM_SUCCESS;
+
+ cp = variable_get(VAR_DISK);
+ if (!cp) {
+ dialog_clear();
+ msgConfirm("diskLabel: No disk selected - can't label automatically.");
+ return DITEM_FAILURE;
+ }
+ devs = deviceFind(cp, DEVICE_TYPE_DISK);
+ if (!devs) {
+ msgConfirm("diskLabel: No disk device %s found!", cp);
+ return DITEM_FAILURE;
+ }
+ if (dev)
+ d = dev->private;
+ else
+ d = devs[0]->private;
+ record_label_chunks(devs, dev);
+ for (i = 0; label_chunk_info[i].c; i++) {
+ Chunk *c1 = label_chunk_info[i].c;
+
+ if (label_chunk_info[i].type == PART_SLICE) {
+ char name[512];
+ int entries = 1;
+
+ while (entries) {
+ snprintf(name, sizeof name, "%s-%d", c1->name, entries);
+ if ((cp = variable_get(name)) != NULL) {
+ int sz;
+ char typ[10], mpoint[50];
+
+ if (sscanf(cp, "%s %d %s", typ, &sz, mpoint) != 3) {
+ msgConfirm("For slice entry %s, got an invalid detail entry of: %s", c1->name, cp);
+ status = DITEM_FAILURE;
+ continue;
+ }
+ else {
+ Chunk *tmp;
+
+ if (!strcmp(typ, "swap")) {
+ type = PART_SWAP;
+ strcpy(mpoint, "SWAP");
+ }
+ else {
+ type = PART_FILESYSTEM;
+ if (!strcmp(mpoint, "/"))
+ flags |= CHUNK_IS_ROOT;
+ }
+ if (!sz)
+ sz = space_free(c1);
+ if (sz > space_free(c1)) {
+ msgConfirm("Not enough free space to create partition: %s", mpoint);
+ status = DITEM_FAILURE;
+ continue;
+ }
+ if (!(tmp = Create_Chunk_DWIM(d, c1, sz, part,
+ (type == PART_SWAP) ? FS_SWAP : FS_BSDFFS, flags))) {
+ msgConfirm("Unable to create from partition spec: %s. Too big?", cp);
+ status = DITEM_FAILURE;
+ break;
+ }
+ else {
+ tmp->private_data = new_part(mpoint, TRUE, sz);
+ tmp->private_free = safe_free;
+ status = DITEM_SUCCESS;
+ }
+ }
+ entries++;
+ }
+ else {
+ /* No more matches, leave the loop */
+ entries = 0;
+ }
+ }
+ }
+ else {
+ /* Must be something we can set a mountpoint for */
+ cp = variable_get(c1->name);
+ if (cp) {
+ char mpoint[50], do_newfs[8];
+ Boolean newfs = FALSE;
+
+ do_newfs[0] = '\0';
+ if (sscanf(cp, "%s %s", mpoint, do_newfs) != 2) {
+ dialog_clear();
+ msgConfirm("For slice entry %s, got an invalid detail entry of: %s", c1->name, cp);
+ status = DITEM_FAILURE;
+ continue;
+ }
+ newfs = toupper(do_newfs[0]) == 'Y' ? TRUE : FALSE;
+ if (c1->private_data) {
+ p = c1->private_data;
+ p->newfs = newfs;
+ strcpy(p->mountpoint, mpoint);
+ }
+ else {
+ c1->private_data = new_part(mpoint, newfs, 0);
+ c1->private_free = safe_free;
+ }
+ if (!strcmp(mpoint, "/"))
+ c1->flags |= CHUNK_IS_ROOT;
+ else
+ c1->flags &= ~CHUNK_IS_ROOT;
+ }
+ }
+ }
+ if (status == DITEM_SUCCESS)
+ variable_set2(DISK_LABELLED, "yes");
+ return status;
+}
diff --git a/usr.sbin/sade/list.h b/usr.sbin/sade/list.h
new file mode 100644
index 0000000..05a9fd5
--- /dev/null
+++ b/usr.sbin/sade/list.h
@@ -0,0 +1,60 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last attempt in the `sysinstall' line, the next
+ * generation being slated for what's essentially a complete rewrite.
+ *
+ * $Id: list.h,v 1.1 1997/09/16 17:03:58 pst Exp $
+ *
+ * Copyright (c) 1997 FreeBSD, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY PAUL TRAINA ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL PAUL TRAINA OR HIS KILLER RATS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+/* The structure */
+typedef struct _qelement {
+ struct _qelement *q_forw;
+ struct _qelement *q_back;
+} qelement;
+
+#define INITQUE(Xhead) { \
+ (Xhead).q_forw = &(Xhead); \
+ (Xhead).q_back = &(Xhead); \
+}
+
+#define EMPTYQUE(Xhead) \
+ ((Xhead).q_forw == &(Xhead))
+
+#define INSQUEUE(elem, pred) { \
+ register qelement *Xe = (qelement *) (elem); \
+ register qelement *Xp = (qelement *) (pred); \
+ Xp->q_forw = (Xe->q_forw = (Xe->q_back = Xp)->q_forw)->q_back = Xe; \
+}
+
+#define REMQUE(elem) { \
+ register qelement *Xe = (qelement *) (elem); \
+ (Xe->q_back->q_forw = Xe->q_forw)->q_back = Xe->q_back; \
+}
diff --git a/usr.sbin/sade/main.c b/usr.sbin/sade/main.c
new file mode 100644
index 0000000..d6d2726
--- /dev/null
+++ b/usr.sbin/sade/main.c
@@ -0,0 +1,134 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last attempt in the `sysinstall' line, the next
+ * generation being slated for what's essentially a complete rewrite.
+ *
+ * $Id: main.c,v 1.46 1997/06/05 09:47:58 jkh Exp $
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include "sysinstall.h"
+#include <sys/signal.h>
+#include <sys/fcntl.h>
+
+static void
+screech(int sig)
+{
+ msgDebug("\007Signal %d caught! That's bad!\n", sig);
+ longjmp(BailOut, sig);
+}
+
+int
+main(int argc, char **argv)
+{
+ int choice, scroll, curr, max, status;
+
+ /* Catch fatal signals and complain about them if running as init */
+ if (getpid() == 1) {
+ signal(SIGBUS, screech);
+ signal(SIGSEGV, screech);
+ }
+
+ /* We don't work too well when running as non-root anymore */
+ if (geteuid() != 0) {
+ fprintf(stderr, "Error: This utility should only be run as root.\n");
+ return 1;
+ }
+
+ /* Set up whatever things need setting up */
+ systemInitialize(argc, argv);
+
+ /* Set default flag and variable values */
+ installVarDefaults(NULL);
+ installEnvironment();
+
+ if (argc > 1 && !strcmp(argv[1], "-fake")) {
+ variable_set2(VAR_DEBUG, "YES");
+ Fake = TRUE;
+ msgConfirm("I'll be just faking it from here on out, OK?");
+ }
+
+ /* Try to preserve our scroll-back buffer */
+ if (OnVTY) {
+ for (curr = 0; curr < 25; curr++)
+ putchar('\n');
+ }
+ /* Move stderr aside */
+ if (DebugFD)
+ dup2(DebugFD, 2);
+
+ /* Probe for all relevant devices on the system */
+ deviceGetAll();
+
+ /* First, see if we have any arguments to process (and argv[0] counts if it's not "sysinstall") */
+ if (!RunningAsInit) {
+ int i, start_arg;
+
+ if (!strstr(argv[0], "sysinstall"))
+ start_arg = 0;
+ else if (Fake)
+ start_arg = 2;
+ else
+ start_arg = 1;
+ for (i = start_arg; i < argc; i++) {
+ if (DITEM_STATUS(dispatchCommand(argv[i])) != DITEM_SUCCESS)
+ systemShutdown(1);
+ }
+ if (argc > start_arg)
+ systemShutdown(0);
+ }
+ else
+ dispatch_load_file_int(TRUE);
+
+ status = setjmp(BailOut);
+ if (status) {
+ msgConfirm("A signal %d was caught - I'm saving what I can and shutting\n"
+ "this thing down! Please report this unfortunate incident\n"
+ "to jkh@FreeBSD.org. If you can reproduce the problem, please\n"
+ "also turn Debug on in the Options menu for the extra information\n"
+ "it provides in debugging problems like this. Thanks!", 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 || !msgYesNo("Are you sure you wish to exit? The system will reboot\n"
+ "(be sure to remove any floppies from the drives)."))
+ 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..375cef1
--- /dev/null
+++ b/usr.sbin/sade/menus.c
@@ -0,0 +1,1465 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last program in the `sysinstall' line - the next
+ * generation being essentially a complete rewrite.
+ *
+ * $Id: menus.c,v 1.149 1997/11/04 23:44:22 jkh Exp $
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include "sysinstall.h"
+
+/* Miscellaneous work routines for menus */
+static int
+setSrc(dialogMenuItem *self)
+{
+ Dists |= DIST_SRC;
+ SrcDists = DIST_SRC_ALL | DIST_SRC_SMAILCF;
+ return DITEM_SUCCESS | DITEM_REDRAW;
+}
+
+static int
+clearSrc(dialogMenuItem *self)
+{
+ Dists &= ~DIST_SRC;
+ SrcDists = 0;
+ return DITEM_SUCCESS | DITEM_REDRAW;
+}
+
+#ifndef USE_XIG_ENVIRONMENT
+static int
+setX11All(dialogMenuItem *self)
+{
+ XF86Dists = DIST_XF86_ALL;
+ XF86ServerDists = DIST_XF86_SERVER_ALL;
+ XF86FontDists = DIST_XF86_FONTS_ALL;
+ Dists |= DIST_XF86;
+ return DITEM_SUCCESS | DITEM_REDRAW;
+}
+
+static int
+clearX11All(dialogMenuItem *self)
+{
+ XF86Dists = 0;
+ XF86ServerDists = 0;
+ XF86FontDists = 0;
+ Dists &= ~DIST_XF86;
+ return DITEM_SUCCESS | DITEM_REDRAW;
+}
+
+static int
+setX11Misc(dialogMenuItem *self)
+{
+ XF86Dists |= DIST_XF86_MISC_ALL;
+ Dists |= DIST_XF86;
+ return DITEM_SUCCESS | DITEM_REDRAW;
+}
+
+static int
+clearX11Misc(dialogMenuItem *self)
+{
+ XF86Dists &= ~DIST_XF86_MISC_ALL;
+ if (!XF86ServerDists && !XF86FontDists)
+ Dists &= ~DIST_XF86;
+ return DITEM_SUCCESS | DITEM_REDRAW;
+}
+
+static int
+setX11Servers(dialogMenuItem *self)
+{
+ XF86Dists |= DIST_XF86_SERVER;
+ XF86ServerDists = DIST_XF86_SERVER_ALL;
+ return DITEM_SUCCESS | DITEM_REDRAW;
+}
+
+static int
+clearX11Servers(dialogMenuItem *self)
+{
+ XF86Dists &= ~DIST_XF86_SERVER;
+ XF86ServerDists = 0;
+ return DITEM_SUCCESS | DITEM_REDRAW;
+}
+
+static int
+setX11Fonts(dialogMenuItem *self)
+{
+ XF86Dists |= DIST_XF86_FONTS;
+ XF86FontDists = DIST_XF86_FONTS_ALL;
+ return DITEM_SUCCESS | DITEM_REDRAW;
+}
+
+static int
+clearX11Fonts(dialogMenuItem *self)
+{
+ XF86Dists &= ~DIST_XF86_FONTS;
+ XF86FontDists = 0;
+ return DITEM_SUCCESS | DITEM_REDRAW;
+}
+#endif /* !USE_XIG_ENVIRONMENT */
+
+#define IS_DEVELOPER(dist, extra) ((((dist) & (_DIST_DEVELOPER | (extra))) == (_DIST_DEVELOPER | (extra))) || \
+ (((dist) & (_DIST_DEVELOPER | DIST_DES | (extra))) == (_DIST_DEVELOPER | DIST_DES | (extra))))
+#define IS_USER(dist, extra) ((((dist) & (_DIST_USER | (extra))) == (_DIST_USER | (extra))) || \
+ (((dist) & (_DIST_USER | DIST_DES | (extra))) == (_DIST_USER | DIST_DES | (extra))))
+
+static int
+checkDistDeveloper(dialogMenuItem *self)
+{
+ return (IS_DEVELOPER(Dists, 0) && SrcDists == DIST_SRC_ALL);
+}
+
+static int
+checkDistXDeveloper(dialogMenuItem *self)
+{
+ return (IS_DEVELOPER(Dists, DIST_XF86) && SrcDists == DIST_SRC_ALL);
+}
+
+static int
+checkDistKernDeveloper(dialogMenuItem *self)
+{
+ return (IS_DEVELOPER(Dists, 0) && SrcDists == DIST_SRC_SYS);
+}
+
+static int
+checkDistUser(dialogMenuItem *self)
+{
+ return (IS_USER(Dists, 0));
+}
+
+static int
+checkDistXUser(dialogMenuItem *self)
+{
+ return (IS_USER(Dists, DIST_XF86));
+}
+
+static int
+checkDistMinimum(dialogMenuItem *self)
+{
+ return (Dists == DIST_BIN);
+}
+
+static int
+checkDistEverything(dialogMenuItem *self)
+{
+#ifdef USE_XIG_ENVIRONMENT
+ return (Dists == DIST_ALL && SrcDists == DIST_SRC_ALL);
+#else
+ return (Dists == DIST_ALL && SrcDists == DIST_SRC_ALL && XF86Dists == DIST_XF86_ALL &&
+ XF86ServerDists == DIST_XF86_SERVER_ALL && XF86FontDists == DIST_XF86_FONTS_ALL);
+#endif
+}
+
+static int
+DESFlagCheck(dialogMenuItem *item)
+{
+ return DESDists;
+}
+
+static int
+srcFlagCheck(dialogMenuItem *item)
+{
+ return SrcDists;
+}
+
+#ifndef USE_XIG_ENVIRONMENT
+static int
+x11FlagCheck(dialogMenuItem *item)
+{
+ return XF86Dists;
+}
+#endif
+
+static int
+checkTrue(dialogMenuItem *item)
+{
+ return TRUE;
+}
+
+/* All the system menus go here.
+ *
+ * Hardcoded things like version number strings will disappear from
+ * these menus just as soon as I add the code for doing inline variable
+ * expansion.
+ */
+
+DMenu MenuIndex = {
+ DMENU_NORMAL_TYPE,
+ "Glossary of functions",
+ "This menu contains an alphabetized index of the top level functions in\n"
+ "this program (sysinstall). Invoke an option by pressing [ENTER].\n"
+ "Leave the index page by selecting Cancel [TAB-ENTER].",
+ "Use PageUp or PageDown to move through this menu faster!",
+ NULL,
+ { { "Anon FTP", "Configure anonymous FTP logins.", dmenuVarCheck, configAnonFTP, NULL, "anon_ftp" },
+ { "Commit", "Commit any pending actions (dangerous!)", NULL, installCustomCommit },
+ { "Console settings", "Customize system console behavior.", NULL, dmenuSubmenu, NULL, &MenuSyscons },
+ { "Configure", "The system configuration menu.", NULL, dmenuSubmenu, NULL, &MenuConfigure },
+ { "Defaults, Load", "Load default settings.", NULL, dispatch_load_floppy },
+ { "Device, Mouse", "The mouse configuration menu.", NULL, dmenuSubmenu, NULL, &MenuMouse },
+ { "Dists, All", "Root of the distribution tree.", NULL, dmenuSubmenu, NULL, &MenuDistributions },
+ { "Dists, Basic", "Basic FreeBSD distribution menu.", NULL, dmenuSubmenu, NULL, &MenuSubDistributions },
+ { "Dists, DES", "DES distribution menu.", NULL, dmenuSubmenu, NULL, &MenuDESDistributions },
+ { "Dists, Developer", "Select developer's distribution.", checkDistDeveloper, distSetDeveloper },
+ { "Dists, Src", "Src distribution menu.", NULL, dmenuSubmenu, NULL, &MenuSrcDistributions },
+ { "Dists, X Developer", "Select X developer's distribution.", checkDistXDeveloper, distSetXDeveloper },
+ { "Dists, Kern Developer", "Select kernel developer's distribution.", checkDistKernDeveloper, distSetKernDeveloper },
+ { "Dists, User", "Select average user distribution.", checkDistUser, distSetUser },
+ { "Dists, X User", "Select average X user distribution.", checkDistXUser, distSetXUser },
+ { "Distributions, Adding", "Installing additional distribution sets", NULL, distExtractAll },
+#ifndef USE_XIG_ENVIRONMENT
+ { "Distributions, XFree86","XFree86 distribution menu.", NULL, distSetXF86 },
+#endif
+ { "Documentation", "Installation instructions, README, etc.", NULL, dmenuSubmenu, NULL, &MenuDocumentation },
+ { "Doc, README", "The distribution README file.", NULL, dmenuDisplayFile, NULL, "readme" },
+ { "Doc, Hardware", "The distribution hardware guide.", NULL, dmenuDisplayFile, NULL, "hardware" },
+ { "Doc, Install", "The distribution installation guide.", NULL, dmenuDisplayFile, NULL, "install" },
+ { "Doc, Copyright", "The distribution copyright notices.", NULL, dmenuDisplayFile, NULL, "COPYRIGHT" },
+ { "Doc, Release", "The distribution release notes.", NULL, dmenuDisplayFile, NULL, "relnotes" },
+ { "Doc, HTML", "The HTML documentation menu.", NULL, docBrowser },
+ { "Emergency shell", "Start an Emergency Holographic shell.", NULL, installFixitHoloShell },
+ { "Fixit", "Repair mode with CDROM or fixit floppy.", NULL, dmenuSubmenu, NULL, &MenuFixit },
+ { "FTP sites", "The FTP mirror site listing.", NULL, dmenuSubmenu, NULL, &MenuMediaFTP },
+ { "Gateway", "Set flag to route packets between interfaces.", dmenuVarCheck, dmenuToggleVariable, NULL, "gateway=YES" },
+ { "HTML Docs", "The HTML documentation menu", NULL, docBrowser },
+ { "Install, Novice", "A novice system installation.", NULL, installNovice },
+ { "Install, Express", "An express system installation.", NULL, installExpress },
+ { "Install, Custom", "The custom installation menu", NULL, dmenuSubmenu, NULL, &MenuInstallCustom },
+ { "Label", "The disk Label editor", NULL, diskLabelEditor },
+ { "Media", "Top level media selection menu.", NULL, dmenuSubmenu, NULL, &MenuMedia },
+ { "Media, Tape", "Select tape installation media.", NULL, mediaSetTape },
+ { "Media, NFS", "Select NFS installation media.", NULL, mediaSetNFS },
+ { "Media, Floppy", "Select floppy installation media.", NULL, mediaSetFloppy },
+ { "Media, CDROM", "Select CDROM installation media.", NULL, mediaSetCDROM },
+ { "Media, DOS", "Select DOS installation media.", NULL, mediaSetDOS },
+ { "Media, UFS", "Select UFS installation media.", NULL, mediaSetUFS },
+ { "Media, FTP", "Select FTP installation media.", NULL, mediaSetFTP },
+ { "Media, FTP Passive", "Select passive FTP installation media.", NULL, mediaSetFTPPassive },
+ { "Network Interfaces", "Configure network interfaces", NULL, tcpMenuSelect },
+ { "Networking Services", "The network services menu.", NULL, dmenuSubmenu, NULL, &MenuNetworking },
+ { "NFS, client", "Set NFS client flag.", dmenuVarCheck, dmenuToggleVariable, NULL, "nfs_client_enable=YES" },
+ { "NFS, server", "Set NFS server flag.", dmenuVarCheck, configNFSServer, NULL, "nfs_server_enable" },
+ { "NTP Menu", "The NTP configuration menu.", NULL, dmenuSubmenu, NULL, &MenuNTP },
+ { "Options", "The options editor.", NULL, optionsEditor },
+ { "Packages", "The packages collection", NULL, configPackages },
+ { "Partition", "The disk Slice (PC-style partition) Editor", NULL, diskPartitionEditor },
+ { "PCNFSD", "Run authentication server for PC-NFS.", dmenuVarCheck, configPCNFSD, NULL, "pcnfsd" },
+ { "Register", "Register yourself or company as a FreeBSD user.", dmenuVarCheck, configRegister, NULL, "registered" },
+ { "Root Password", "Set the system manager's password.", NULL, dmenuSystemCommand, NULL, "passwd root" },
+ { "Root Password", "Set the system manager's password.", NULL, dmenuSystemCommand, NULL, "passwd root" },
+ { "Router", "Select routing daemon (default: routed)", NULL, configRouter, NULL, "router" },
+ { "Syscons", "The system console configuration menu.", NULL, dmenuSubmenu, NULL, &MenuSyscons },
+ { "Syscons, Font", "The console screen font.", NULL, dmenuSubmenu, NULL, &MenuSysconsFont },
+ { "Syscons, Keymap", "The console keymap configuration menu.", NULL, dmenuSubmenu, NULL, &MenuSysconsKeymap },
+ { "Syscons, Keyrate", "The console key rate configuration menu.", NULL, dmenuSubmenu, NULL, &MenuSysconsKeyrate },
+ { "Syscons, Saver", "The console screen saver configuration menu.", NULL, dmenuSubmenu, NULL, &MenuSysconsSaver },
+ { "Syscons, Screenmap", "The console screenmap configuration menu.", NULL, dmenuSubmenu, NULL, &MenuSysconsScrnmap },
+ { "Time Zone", "Set the system's time zone.", NULL, dmenuSystemCommand, NULL, "tzsetup" },
+ { "Upgrade", "Upgrade an existing system.", NULL, installUpgrade },
+ { "Usage", "Quick start - How to use this menu system.", NULL, dmenuDisplayFile, NULL, "usage" },
+ { "User Management", "Add user and group information.", NULL, dmenuSubmenu, NULL, &MenuUsermgmt },
+#ifndef USE_XIG_ENVIRONMENT
+ { "XFree86, Fonts", "XFree86 Font selection menu.", NULL, dmenuSubmenu, NULL, &MenuXF86SelectFonts },
+ { "XFree86, Server", "XFree86 Server selection menu.", NULL, dmenuSubmenu, NULL, &MenuXF86SelectServer },
+ { "XFree86, PC98 Server", "XFree86 PC98 Server selection menu.", NULL, dmenuSubmenu, NULL, &MenuXF86SelectPC98Server },
+#endif
+ { NULL } },
+};
+
+/* The initial installation menu */
+DMenu MenuInitial = {
+ DMENU_NORMAL_TYPE,
+ "Welcome to FreeBSD! [" RELEASE_NAME "]", /* title */
+ "This is the main menu of the FreeBSD installation system. Please\n" /* prompt */
+ "select one of the options below by using the arrow keys or typing the\n"
+ "first character of the option name you're interested in. Invoke an\n"
+ "option by pressing [ENTER] or [TAB-ENTER] to exit the installation.",
+ "Press F1 for Installation Guide", /* help line */
+ "install", /* help file */
+ { { "Select" },
+ { "Exit Install", NULL, NULL, dmenuExit },
+ { "1 Usage", "Quick start - How to use this menu system", NULL, dmenuDisplayFile, NULL, "usage" },
+ { "2 Doc", "Installation instructions, README, etc.", NULL, dmenuSubmenu, NULL, &MenuDocumentation },
+ { "3 Keymap", "Select keyboard type", NULL, dmenuSubmenu, NULL, &MenuSysconsKeymap },
+ { "4 Options", "View/Set various installation options", NULL, optionsEditor },
+ { "5 Novice", "Begin a novice installation (for beginners)", NULL, installNovice },
+ { "6 Express", "Begin a quick installation (for the impatient)", NULL, installExpress },
+ { "7 Custom", "Begin a custom installation (for experts)", NULL, dmenuSubmenu, NULL, &MenuInstallCustom },
+ { "8 Fixit", "Enter repair mode with CDROM/floppy or start shell", NULL, dmenuSubmenu, NULL, &MenuFixit },
+ { "9 Upgrade", "Upgrade an existing system", NULL, installUpgrade },
+ { "c Configure", "Do post-install configuration of FreeBSD", NULL, dmenuSubmenu, NULL, &MenuConfigure },
+ { "l Load Config","Load default install configuration", NULL, dispatch_load_floppy },
+ { "0 Index", "Glossary of functions", NULL, dmenuSubmenu, NULL, &MenuIndex },
+ { NULL } },
+};
+
+/* The main documentation menu */
+DMenu MenuDocumentation = {
+ DMENU_NORMAL_TYPE,
+ "Documentation for FreeBSD " RELEASE_NAME,
+ "If you are at all unsure about the configuration of your hardware\n"
+ "or are looking to build a system specifically for FreeBSD, read the\n"
+ "Hardware guide! New users should also read the Install document for\n"
+ "a step-by-step tutorial on installing FreeBSD. For general information,\n"
+ "consult the README file.",
+ "Confused? Press F1 for help.",
+ "usage",
+ { { "1 README", "A general description of FreeBSD. Read this!", NULL, dmenuDisplayFile, NULL, "readme" },
+ { "2 Hardware", "The FreeBSD survival guide for PC hardware.", NULL, dmenuDisplayFile, NULL, "hardware" },
+ { "3 Install", "A step-by-step guide to installing FreeBSD.", NULL, dmenuDisplayFile, NULL, "install" },
+ { "4 Copyright", "The FreeBSD Copyright notices.", NULL, dmenuDisplayFile, NULL, "COPYRIGHT" },
+ { "5 Release" ,"The release notes for this version of FreeBSD.", NULL, dmenuDisplayFile, NULL, "relnotes" },
+ { "6 Shortcuts", "Creating shortcuts to sysinstall.", NULL, dmenuDisplayFile, NULL, "shortcuts" },
+ { "7 HTML Docs", "Go to the HTML documentation menu (post-install).", NULL, docBrowser },
+ { "0 Exit", "Exit this menu (returning to previous)", NULL, dmenuExit },
+ { NULL } },
+};
+
+static int
+whichMouse(dialogMenuItem *self)
+{
+ int i;
+ char buf[BUFSIZ];
+
+ if (!file_readable("/dev/mouse"))
+ return FALSE;
+ if ((i = readlink("/dev/mouse", buf, sizeof buf)) == -1)
+ return FALSE;
+ buf[i] = '\0';
+ if (!strcmp(self->prompt, "COM1"))
+ return !strcmp(buf, "/dev/cuaa0");
+ else if (!strcmp(self->prompt, "COM2"))
+ return !strcmp(buf, "/dev/cuaa1");
+ if (!strcmp(self->prompt, "COM3"))
+ return !strcmp(buf, "/dev/cuaa2");
+ if (!strcmp(self->prompt, "COM4"))
+ return !strcmp(buf, "/dev/cuaa3");
+ if (!strcmp(self->prompt, "BusMouse"))
+ return !strcmp(buf, "/dev/mse0");
+ if (!strcmp(self->prompt, "PS/2"))
+ return !strcmp(buf, "/dev/psm0");
+ return FALSE;
+}
+
+DMenu MenuMouse = {
+ DMENU_RADIO_TYPE | DMENU_SELECTION_RETURNS,
+ "Please select your mouse type from the following menu",
+ "There are many different types of mice currently on the market,\n"
+ "but this configuration menu should at least narrow down the choices\n"
+ "somewhat. Once you've selected one of the below, you can specify\n"
+ "/dev/mouse as your mouse device when running the X configuration\n"
+ "utility (see Configuration menu). Please note that for PS/2 mice,\n"
+ "you need to enable the psm driver in the kernel configuration menu\n"
+ "when installing for the first time.",
+ "For more information, visit the Documentation menu",
+ NULL,
+ { { "COM1", "Serial mouse on COM1", whichMouse, dmenuSystemCommand, NULL,
+ "ln -fs /dev/cuaa0 /dev/mouse", '(', '*', ')', 1 },
+ { "COM2", "Serial mouse on COM2", whichMouse, dmenuSystemCommand, NULL,
+ "ln -fs /dev/cuaa1 /dev/mouse", '(', '*', ')', 1 },
+ { "COM3", "Serial mouse on COM3", whichMouse, dmenuSystemCommand, NULL,
+ "ln -fs /dev/cuaa2 /dev/mouse", '(', '*', ')', 1 },
+ { "COM4", "Serial mouse on COM4", whichMouse, dmenuSystemCommand, NULL,
+ "ln -fs /dev/cuaa3 /dev/mouse", '(', '*', ')', 1 },
+ { "BusMouse", "Logitech or ATI bus mouse", whichMouse, dmenuSystemCommand, NULL,
+ "ln -fs /dev/mse0 /dev/mouse", '(', '*', ')', 1 },
+ { "PS/2", "PS/2 style mouse (must enable psm0 device)", whichMouse, dmenuSystemCommand, NULL,
+ "ln -fs /dev/psm0 /dev/mouse", '(', '*', ')', 1 },
+ { NULL } },
+};
+
+#ifndef USE_XIG_ENVIRONMENT
+DMenu MenuXF86Config = {
+ DMENU_NORMAL_TYPE | DMENU_SELECTION_RETURNS,
+ "Please select the XFree86 configuration tool you want to use.",
+ "The first tool, XF86Setup, is fully graphical and requires the\n"
+ "VGA16 server in order to work (should have been selected by\n"
+ "default, but if you de-selected it then you won't be able to\n"
+ "use this fancy setup tool). The second tool, xf86config, is\n"
+ "a more simplistic shell-script based tool and less friendly to\n"
+ "new users, but it may work in situations where the fancier one\n"
+ "does not.",
+ "Press F1 to read the XFree86 release notes for FreeBSD",
+ "XF86",
+ { { "XF86Setup", "Use the fully graphical XFree86 configuration tool.",
+ NULL, dmenuSetVariable, NULL, VAR_XF86_CONFIG "=XF86Setup" },
+ { "xf86config", "Use the shell-script based XFree86 configuration tool.",
+ NULL, dmenuSetVariable, NULL, VAR_XF86_CONFIG "=xf86config" },
+ { NULL } },
+};
+#endif
+
+DMenu MenuMediaCDROM = {
+ DMENU_NORMAL_TYPE | DMENU_SELECTION_RETURNS,
+ "Choose a CDROM type",
+ "FreeBSD can be installed directly from a CDROM containing a valid\n"
+ "FreeBSD distribution. If you are seeing this menu it is because\n"
+ "more than one CDROM drive was found on your system. Please select one\n"
+ "of the following CDROM drives as your installation drive.",
+ "Press F1 to read the installation guide",
+ "install",
+ { { NULL } },
+};
+
+DMenu MenuMediaFloppy = {
+ DMENU_NORMAL_TYPE | DMENU_SELECTION_RETURNS,
+ "Choose a Floppy drive",
+ "You have more than one floppy drive. Please chose which drive\n"
+ "you would like to use.",
+ NULL,
+ NULL,
+ { { NULL } },
+};
+
+DMenu MenuMediaDOS = {
+ DMENU_NORMAL_TYPE | DMENU_SELECTION_RETURNS,
+ "Choose a DOS partition",
+ "FreeBSD can be installed directly from a DOS partition\n"
+ "assuming, of course, that you have copied the relevant\n"
+ "distributions into your DOS partition before starting this\n"
+ "installation. If this is not the case then you should reboot\n"
+ "DOS at this time and copy the distributions you wish to install\n"
+ "into a \"FREEBSD\" subdirectory on one of your DOS partitions.\n"
+ "Otherwise, please select the DOS partition containing the FreeBSD\n"
+ "distribution files.",
+ "Press F1 to read the installation guide",
+ "install",
+ { { NULL } },
+};
+
+DMenu MenuMediaFTP = {
+ DMENU_NORMAL_TYPE | DMENU_SELECTION_RETURNS,
+ "Please select a FreeBSD FTP distribution site",
+ "Please select the site closest to you or \"other\" if you'd like to\n"
+ "specify a different choice. Also note that not every site listed here\n"
+ "carries more than the base distribution kits. Only the Primary site is\n"
+ "guaranteed to carry the full range of possible distributions.",
+ "Select a site that's close!",
+ "install",
+ { { "Primary Site", "ftp.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.freebsd.org/pub/FreeBSD/" },
+ { "URL", "Specify some other ftp site by URL", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=other" },
+ { "3.0 SNAP Server", "current.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://current.freebsd.org/pub/FreeBSD/" },
+ { "2.2 SNAP Server", "releng22.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://releng22.freebsd.org/pub/FreeBSD/" },
+ { "2.1 SNAP Server", "releng210.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://releng210.freebsd.org/pub/FreeBSD/" },
+ { "Argentina", "ftp.ar.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.ar.freebsd.org/pub/FreeBSD/" },
+ { "Australia", "ftp.au.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.au.freebsd.org/pub/FreeBSD/" },
+ { "Australia #2", "ftp2.au.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp2.au.freebsd.org/pub/FreeBSD/" },
+ { "Australia #3", "ftp3.au.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp3.au.freebsd.org/pub/FreeBSD/" },
+ { "Australia #4", "ftp4.au.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp4.au.freebsd.org/pub/FreeBSD/" },
+ { "Australia #5", "ftp5.au.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp5.au.freebsd.org/pub/FreeBSD/" },
+ { "Brazil", "ftp.br.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.br.freebsd.org/pub/FreeBSD/" },
+ { "Brazil #2", "ftp2.br.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp2.br.freebsd.org/pub/FreeBSD/" },
+ { "Brazil #3", "ftp3.br.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp3.br.freebsd.org/pub/FreeBSD/" },
+ { "Brazil #4", "ftp4.br.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp4.br.freebsd.org/pub/FreeBSD/" },
+ { "Brazil #5", "ftp5.br.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp5.br.freebsd.org/pub/FreeBSD/" },
+ { "Brazil #6", "ftp6.br.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp6.br.freebsd.org/pub/FreeBSD/" },
+ { "Brazil #7", "ftp7.br.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp7.br.freebsd.org/pub/FreeBSD/" },
+ { "Canada", "ftp.ca.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.ca.freebsd.org/pub/FreeBSD/" },
+ { "Czech Republic", "ftp.cz.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.cz.freebsd.org/pub/FreeBSD/" },
+ { "Estonia", "ftp.ee.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.ee.freebsd.org/pub/FreeBSD/" },
+ { "Finland", "ftp.fi.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.fi.freebsd.org/pub/FreeBSD/" },
+ { "France", "ftp.fr.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.fr.freebsd.org/pub/FreeBSD/" },
+ { "France #2", "ftp2.fr.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp2.fr.freebsd.org/pub/FreeBSD/" },
+ { "Germany", "ftp.de.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.de.freebsd.org/pub/FreeBSD/" },
+ { "Germany #2", "ftp2.de.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp2.de.freebsd.org/pub/FreeBSD/" },
+ { "Germany #3", "ftp3.de.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp3.de.freebsd.org/pub/FreeBSD/" },
+ { "Germany #4", "ftp4.de.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp4.de.freebsd.org/pub/FreeBSD/" },
+ { "Germany #5", "ftp5.de.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp5.de.freebsd.org/pub/FreeBSD/" },
+ { "Germany #6", "ftp6.de.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp6.de.freebsd.org/pub/FreeBSD/" },
+ { "Germany #7", "ftp7.de.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp7.de.freebsd.org/pub/FreeBSD/" },
+ { "Holland", "ftp.nl.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.nl.freebsd.org/pub/FreeBSD/" },
+ { "Hong Kong", "ftp.hk.super.net", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.hk.super.net/pub/FreeBSD/" },
+ { "Iceland", "ftp.is.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.is.freebsd.org/pub/FreeBSD/" },
+ { "Ireland", "ftp.ie.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.ie.freebsd.org/pub/FreeBSD/" },
+ { "Israel", "ftp.il.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.il.freebsd.org/pub/FreeBSD/" },
+ { "Israel #2", "ftp2.il.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp2.il.freebsd.org/pub/FreeBSD/" },
+ { "Japan", "ftp.jp.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.jp.freebsd.org/pub/FreeBSD/" },
+ { "Japan #2", "ftp2.jp.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp2.jp.freebsd.org/pub/FreeBSD/" },
+ { "Japan #3", "ftp3.jp.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp3.jp.freebsd.org/pub/FreeBSD/" },
+ { "Japan #4", "ftp4.jp.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp4.jp.freebsd.org/pub/FreeBSD/" },
+ { "Japan #5", "ftp5.jp.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp5.jp.freebsd.org/pub/FreeBSD/" },
+ { "Japan #6", "ftp6.jp.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp6.jp.freebsd.org/pub/FreeBSD/" },
+ { "Korea", "ftp.kr.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.kr.freebsd.org/pub/FreeBSD/" },
+ { "Korea #2", "ftp2.kr.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp2.kr.freebsd.org/pub/FreeBSD/" },
+ { "Poland", "ftp.pl.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.pl.freebsd.org/pub/FreeBSD/" },
+ { "Portugal", "ftp.pt.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.pt.freebsd.org/pub/misc/FreeBSD/" },
+ { "Portugal #2", "ftp2.pt.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp2.pt.freebsd.org/pub/FreeBSD/" },
+ { "Russia", "ftp.ru.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.ru.freebsd.org/pub/FreeBSD/" },
+ { "Russia #2", "ftp2.ru.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp2.ru.freebsd.org/pub/FreeBSD/" },
+ { "Russia #3", "ftp3.ru.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp3.ru.freebsd.org/pub/FreeBSD/" },
+ { "South Africa", "ftp.za.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.za.freebsd.org/pub/FreeBSD/" },
+ { "South Africa #2", "ftp2.za.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp2.za.freebsd.org/pub/FreeBSD/" },
+ { "South Africa #3", "ftp3.za.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp3.za.freebsd.org/pub/FreeBSD/" },
+ { "South Africa #4", "ftp4.za.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp4.za.freebsd.org/pub/FreeBSD/" },
+ { "Sweden", "ftp.se.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.se.freebsd.org/pub/FreeBSD/" },
+ { "Sweden #2", "ftp2.se.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp2.se.freebsd.org/pub/FreeBSD/" },
+ { "Sweden #3", "ftp3.se.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp3.se.freebsd.org/pub/FreeBSD/" },
+ { "Taiwan", "ftp.tw.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.tw.freebsd.org/pub/FreeBSD" },
+ { "Taiwan #2", "ftp2.tw.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp2.tw.freebsd.org/pub/FreeBSD" },
+ { "Taiwan #3", "ftp3.tw.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp3.tw.freebsd.org/pub/FreeBSD/" },
+ { "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/pub/FreeBSD/" },
+ { "UK #2", "ftp2.uk.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp2.uk.freebsd.org/pub/FreeBSD/" },
+ { "UK #3", "ftp3.uk.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp3.uk.freebsd.org/pub/FreeBSD/" },
+ { "UK #4", "ftp4.uk.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp4.uk.freebsd.org/pub/FreeBSD/" },
+ { "USA", "ftp.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.freebsd.org/pub/FreeBSD/" },
+ { "USA #2", "ftp2.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp2.freebsd.org/pub/FreeBSD/" },
+ { "USA #3", "ftp3.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp3.freebsd.org/pub/FreeBSD/" },
+ { "USA #4", "ftp4.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp4.freebsd.org/pub/FreeBSD/" },
+ { "USA #5", "ftp5.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp5.freebsd.org/pub/FreeBSD/" },
+ { "USA #6", "ftp6.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp6.freebsd.org/pub/FreeBSD/" },
+ { 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) version\n"
+ "of FreeBSD.",
+ "Press F1 to read network configuration manual",
+ "network_device",
+ { { NULL } },
+};
+
+/* The media selection menu */
+DMenu MenuMedia = {
+ DMENU_NORMAL_TYPE | DMENU_SELECTION_RETURNS,
+ "Choose Installation Media",
+ "FreeBSD can be installed from a variety of different installation\n"
+ "media, ranging from floppies to an Internet FTP server. If you're\n"
+ "installing FreeBSD from a supported CDROM drive then this is generally\n"
+ "the best media to use if you have no overriding reason for using other\n"
+ "media.",
+ "Press F1 for more information on the various media types",
+ "media",
+ { { "1 CDROM", "Install from a FreeBSD CDROM", NULL, mediaSetCDROM },
+ { "2 FTP", "Install from an FTP server", NULL, mediaSetFTPActive },
+ { "3 FTP Passive", "Install from an FTP server through a firewall", NULL, mediaSetFTPPassive },
+ { "4 DOS", "Install from a DOS partition", NULL, mediaSetDOS },
+ { "5 NFS", "Install over NFS", NULL, mediaSetNFS },
+ { "6 File System", "Install from an existing filesystem", NULL, mediaSetUFS },
+ { "7 Floppy", "Install from a floppy disk set", NULL, mediaSetFloppy },
+ { "8 Tape", "Install from SCSI or QIC tape", NULL, mediaSetTape },
+ { "9 Options", "Go to the Options screen", NULL, optionsEditor },
+ { NULL } },
+};
+
+/* The distributions menu */
+DMenu MenuDistributions = {
+ DMENU_CHECKLIST_TYPE | DMENU_SELECTION_RETURNS,
+ "Choose Distributions",
+ "As a convenience, we provide several \"canned\" distribution sets.\n"
+ "These select what we consider to be the most reasonable defaults for the\n"
+ "type of system in question. If you would prefer to pick and choose the\n"
+ "list of distributions yourself, simply select \"Custom\". You can also\n"
+ "pick a canned distribution set and then fine-tune it with the Custom item.\n\n"
+ "Choose an item by pressing [SPACE]. When you are finished, chose the Exit\n"
+ "item or press [ENTER].",
+ "Press F1 for more information on these options.",
+ "distributions",
+ { { "1 Developer", "Full sources, binaries and doc but no games",
+ checkDistDeveloper, distSetDeveloper },
+ { "2 X-Developer", "Same as above, but includes the X Window System",
+ checkDistXDeveloper, distSetXDeveloper },
+ { "3 Kern-Developer", "Full binaries and doc, kernel sources only",
+ checkDistKernDeveloper, distSetKernDeveloper },
+ { "4 User", "Average user - binaries and doc only",
+ checkDistUser, distSetUser },
+ { "5 X-User", "Same as above, but includes the X Window System",
+ checkDistXUser, distSetXUser },
+ { "6 Minimal", "The smallest configuration possible",
+ checkDistMinimum, distSetMinimum },
+ { "7 Custom", "Specify your own distribution set",
+ NULL, dmenuSubmenu, NULL, &MenuSubDistributions, '>', '>', '>' },
+ { "8 All", "All sources and binaries (incl X Window System)",
+ checkDistEverything, distSetEverything },
+ { "9 Clear", "Reset selected distribution list to nothing",
+ NULL, distReset, NULL, NULL, ' ', ' ', ' ' },
+ { "0 Exit", "Exit this menu (returning to previous)",
+ checkTrue, dmenuExit, NULL, NULL, '<', '<', '<' },
+ { NULL } },
+};
+
+DMenu MenuSubDistributions = {
+ DMENU_CHECKLIST_TYPE | DMENU_SELECTION_RETURNS,
+ "Select the distributions you wish to install.",
+ "Please check off the distributions you wish to install. At the\n"
+ "very minimum, this should be \"bin\". WARNING: Do not export the\n"
+ "DES distribution out of the U.S.! It is for U.S. customers only.",
+ NULL,
+ NULL,
+ { { "bin", "Binary base distribution (required)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &Dists, '[', 'X', ']', DIST_BIN },
+ { "compat1x", "FreeBSD 1.x binary compatibility",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &Dists, '[', 'X', ']', DIST_COMPAT1X },
+ { "compat20", "FreeBSD 2.0 binary compatibility",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &Dists, '[', 'X', ']', DIST_COMPAT20 },
+ { "compat21", "FreeBSD 2.1 binary compatibility",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &Dists, '[', 'X', ']', DIST_COMPAT21 },
+ { "DES", "DES encryption code - NOT FOR EXPORT!",
+ DESFlagCheck, distSetDES },
+ { "dict", "Spelling checker dictionary files",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &Dists, '[', 'X', ']', DIST_DICT },
+ { "doc", "Miscellaneous FreeBSD online docs",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &Dists, '[', 'X', ']', DIST_DOC },
+ { "games", "Games (non-commercial)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &Dists, '[', 'X', ']', DIST_GAMES },
+ { "info", "GNU info files",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &Dists, '[', 'X', ']', DIST_INFO },
+ { "man", "System manual pages - recommended",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &Dists, '[', 'X', ']', DIST_MANPAGES },
+ { "catman", "Preformatted system manual pages",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &Dists, '[', 'X', ']', DIST_CATPAGES },
+ { "proflibs", "Profiled versions of the libraries",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &Dists, '[', 'X', ']', DIST_PROFLIBS },
+ { "src", "Sources for everything but DES",
+ srcFlagCheck, distSetSrc },
+ { "ports", "The FreeBSD Ports collection",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &Dists, '[', 'X', ']', DIST_PORTS },
+#ifdef USE_XIG_ENVIRONMENT
+ { "Xaccel", "The XiG AcceleratedX 3.1 distribution",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &Dists, '[', 'X', ']', DIST_XIG_SERVER },
+#else
+ { "XFree86", "The XFree86 3.3.1 distribution",
+ x11FlagCheck, distSetXF86 },
+#endif
+ { "All", "All sources, binaries and X Window System binaries",
+ NULL, distSetEverything, NULL, NULL, ' ', ' ', ' ' },
+ { "Clear", "Reset all of the above",
+ NULL, distReset, NULL, NULL, ' ', ' ', ' ' },
+ { "Exit", "Exit this menu (returning to previous)",
+ checkTrue, dmenuExit, NULL, NULL, '<', '<', '<' },
+ { NULL } },
+};
+
+DMenu MenuDESDistributions = {
+ DMENU_CHECKLIST_TYPE | DMENU_SELECTION_RETURNS,
+ "Select the encryption facilities you wish to install.",
+ "Please check off any special DES-based encryption distributions\n"
+ "you would like to install. Please note that these services are NOT FOR\n"
+ "EXPORT from the United States. For information on non-U.S. FTP\n"
+ "distributions of this software, please consult the release notes.",
+ NULL,
+ NULL,
+ { { "des", "Basic DES encryption services",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &DESDists, '[', 'X', ']', DIST_DES_DES, },
+ { "krb", "Kerberos encryption services",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &DESDists, '[', 'X', ']', DIST_DES_KERBEROS },
+ { "skerbero", "Sources for Kerberos IV",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &DESDists, '[', 'X', ']', DIST_DES_SKERBEROS },
+ { "ssecure", "Sources for DES",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &DESDists, '[', 'X', ']', DIST_DES_SSECURE },
+ { "scrypto", "Export controlled crypto sources",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &DESDists, '[', 'X', ']', DIST_DES_SCRYPTO },
+ { "Exit", "Exit this menu (returning to previous)",
+ checkTrue, dmenuExit, NULL, NULL, '<', '<', '<' },
+ { NULL } },
+};
+
+DMenu MenuSrcDistributions = {
+ DMENU_CHECKLIST_TYPE | DMENU_SELECTION_RETURNS ,
+ "Select the sub-components of src you wish to install.",
+ "Please check off those portions of the FreeBSD source tree\n"
+ "you wish to install.",
+ NULL,
+ NULL,
+ { { "base", "top-level files in /usr/src",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &SrcDists, '[', 'X', ']', DIST_SRC_BASE },
+ { "contrib", "/usr/src/contrib (contributed software)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &SrcDists, '[', 'X', ']', DIST_SRC_CONTRIB },
+ { "gnu", "/usr/src/gnu (software from the GNU Project)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &SrcDists, '[', 'X', ']', DIST_SRC_GNU },
+ { "etc", "/usr/src/etc (miscellaneous system files)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &SrcDists, '[', 'X', ']', DIST_SRC_ETC },
+ { "games", "/usr/src/games (the obvious!)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &SrcDists, '[', 'X', ']', DIST_SRC_GAMES },
+ { "include", "/usr/src/include (header files)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &SrcDists, '[', 'X', ']', DIST_SRC_INCLUDE },
+ { "lib", "/usr/src/lib (system libraries)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &SrcDists, '[', 'X', ']', DIST_SRC_LIB },
+ { "libexec", "/usr/src/libexec (system programs)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &SrcDists, '[', 'X', ']', DIST_SRC_LIBEXEC },
+ { "lkm", "/usr/src/lkm (Loadable Kernel Modules)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &SrcDists, '[', 'X', ']', DIST_SRC_LKM },
+ { "release", "/usr/src/release (release-generation tools)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &SrcDists, '[', 'X', ']', DIST_SRC_RELEASE },
+ { "bin", "/usr/src/bin (system binaries)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &SrcDists, '[', 'X', ']', DIST_SRC_BIN },
+ { "sbin", "/usr/src/sbin (system binaries)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &SrcDists, '[', 'X', ']', DIST_SRC_SBIN },
+ { "share", "/usr/src/share (documents and shared files)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &SrcDists, '[', 'X', ']', DIST_SRC_SHARE },
+ { "sys", "/usr/src/sys (FreeBSD kernel)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &SrcDists, '[', 'X', ']', DIST_SRC_SYS },
+ { "ubin", "/usr/src/usr.bin (user binaries)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &SrcDists, '[', 'X', ']', DIST_SRC_UBIN },
+ { "usbin", "/usr/src/usr.sbin (aux system binaries)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &SrcDists, '[', 'X', ']', DIST_SRC_USBIN },
+ { "smailcf", "/usr/src/usr.sbin (sendmail config macros)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &SrcDists, '[', 'X', ']', DIST_SRC_SMAILCF },
+ { "All", "Select all of the above",
+ NULL, setSrc, NULL, NULL, ' ', ' ', ' ' },
+ { "Clear", "Reset all of the above",
+ NULL, clearSrc, NULL, NULL, ' ', ' ', ' ' },
+ { "Exit", "Exit this menu (returning to previous)",
+ checkTrue, dmenuExit, NULL, NULL, '<', '<', '<' },
+ { NULL } },
+};
+
+#ifndef USE_XIG_ENVIRONMENT
+DMenu MenuXF86Select = {
+ DMENU_NORMAL_TYPE,
+ "XFree86 3.3.1 Distribution",
+ "Please select the components you need from the XFree86 3.3.1\n"
+ "distribution sets.",
+ "Press F1 to read the XFree86 release notes for FreeBSD",
+ "XF86",
+ { { "Basic", "Basic component menu (required)", NULL, dmenuSubmenu, NULL, &MenuXF86SelectCore },
+ { "Server", "X server menu", NULL, dmenuSubmenu, NULL, &MenuXF86SelectServer },
+ { "Fonts", "Font set menu", NULL, dmenuSubmenu, NULL, &MenuXF86SelectFonts },
+ { "All", "Select all XFree86 distribution sets", NULL, setX11All },
+ { "Clear", "Reset XFree86 distribution list", NULL, clearX11All },
+ { "Exit", "Exit this menu (returning to previous)", checkTrue, dmenuExit, NULL, NULL, '<', '<', '<' },
+ { NULL } },
+};
+
+DMenu MenuXF86SelectCore = {
+ DMENU_CHECKLIST_TYPE | DMENU_SELECTION_RETURNS,
+ "XFree86 3.3.1 base distribution types",
+ "Please check off the basic XFree86 components you wish to install.\n"
+ "Bin, lib, and set are recommended for a minimum installaion.",
+ "Press F1 to read the XFree86 release notes for FreeBSD",
+ "XF86",
+ { { "bin", "Client applications and shared libs",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86Dists, '[', 'X', ']', DIST_XF86_BIN },
+ { "cfg", "Configuration files",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86Dists, '[', 'X', ']', DIST_XF86_CFG },
+ { "doc", "READMEs and release notes",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86Dists, '[', 'X', ']', DIST_XF86_DOC },
+ { "html", "HTML documentation files",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86Dists, '[', 'X', ']', DIST_XF86_HTML },
+ { "lib", "Data files needed at runtime",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86Dists, '[', 'X', ']', DIST_XF86_LIB },
+#ifndef USE_XIG_ENVIRONMENT
+ { "lk98", "Server link kit for PC98 machines",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86Dists, '[', 'X', ']', DIST_XF86_LKIT98 },
+ { "lkit", "Server link kit for all other machines",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86Dists, '[', 'X', ']', DIST_XF86_LKIT },
+#endif
+ { "man", "Manual pages",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86Dists, '[', 'X', ']', DIST_XF86_MAN },
+ { "prog", "Programmer's header and library files",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86Dists, '[', 'X', ']', DIST_XF86_PROG },
+ { "ps", "Postscript documentation",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86Dists, '[', 'X', ']', DIST_XF86_PS },
+#ifndef USE_XIG_ENVIRONMENT
+ { "set", "XFree86 Setup Utility",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86Dists, '[', 'X', ']', DIST_XF86_SET },
+#endif
+ { "sources", "XFree86 3.3.1 standard sources",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86Dists, '[', 'X', ']', DIST_XF86_SRC },
+ { "csources", "XFree86 3.3.1 contrib sources",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86Dists, '[', 'X', ']', DIST_XF86_CSRC },
+ { "All", "Select all of the above",
+ NULL, setX11Misc, NULL, NULL, ' ', ' ', ' ' },
+ { "Clear", "Reset all of the above",
+ NULL, clearX11Misc, NULL, NULL, ' ', ' ', ' ' },
+ { "Exit", "Exit this menu (returning to previous)",
+ checkTrue, dmenuExit, NULL, NULL, '<', '<', '<' },
+ { NULL } },
+};
+
+DMenu MenuXF86SelectFonts = {
+ DMENU_CHECKLIST_TYPE | DMENU_SELECTION_RETURNS ,
+ "Font distribution selection.",
+ "Please check off the individual font distributions you wish to\n\
+install. At the minimum, you should install the standard\n\
+75 DPI and misc fonts if you're also installing a server\n\
+(these are selected by default).",
+ "Press F1 to read the XFree86 release notes for FreeBSD",
+ "XF86",
+ { { "fnts", "Standard 75 DPI and miscellaneous fonts",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86FontDists, '[', 'X', ']', DIST_XF86_FONTS_MISC },
+ { "f100", "100 DPI fonts",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86FontDists, '[', 'X', ']', DIST_XF86_FONTS_100 },
+ { "fcyr", "Cyrillic Fonts",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86FontDists, '[', 'X', ']', DIST_XF86_FONTS_CYR },
+ { "fscl", "Speedo and Type scalable fonts",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86FontDists, '[', 'X', ']', DIST_XF86_FONTS_SCALE },
+ { "non", "Japanese, Chinese and other non-english fonts",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86FontDists, '[', 'X', ']', DIST_XF86_FONTS_NON },
+ { "server", "Font server",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86FontDists, '[', 'X', ']', DIST_XF86_FONTS_SERVER },
+ { "All", "All fonts",
+ NULL, setX11Fonts, NULL, NULL, ' ', ' ', ' ' },
+ { "Clear", "Reset font selections",
+ NULL, clearX11Fonts, NULL, NULL, ' ', ' ', ' ' },
+ { "Exit", "Exit this menu (returning to previous)",
+ checkTrue, dmenuExit, NULL, NULL, '<', '<', '<' },
+ { NULL } },
+};
+
+DMenu MenuXF86SelectServer = {
+ DMENU_CHECKLIST_TYPE | DMENU_SELECTION_RETURNS,
+ "X Server selection.",
+ "Please check off the types of X servers you wish to install.\n"
+ "If you are unsure as to which server will work for your graphics card,\n"
+ "it is recommended that try the SVGA or VGA16 servers or, for PC98\n"
+ "machines, the 9EGC or 9840 servers.",
+ "Press F1 to read the XFree86 release notes for FreeBSD",
+ "XF86",
+ { { "SVGA", "Standard VGA or Super VGA card",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86ServerDists, '[', 'X', ']', DIST_XF86_SERVER_SVGA },
+ { "VGA16", "Standard 16 color VGA card",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86ServerDists, '[', 'X', ']', DIST_XF86_SERVER_VGA16 },
+ { "Mono", "Standard Monochrome card",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86ServerDists, '[', 'X', ']', DIST_XF86_SERVER_MONO },
+ { "8514", "8-bit (256 color) IBM 8514 or compatible card",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86ServerDists, '[', 'X', ']', DIST_XF86_SERVER_8514 },
+ { "AGX", "8-bit AGX card",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86ServerDists, '[', 'X', ']', DIST_XF86_SERVER_AGX },
+ { "I128", "8, 16 and 24-bit #9 Imagine I128 card",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86ServerDists, '[', 'X', ']', DIST_XF86_SERVER_I128 },
+ { "Ma8", "8-bit ATI Mach8 card",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86ServerDists, '[', 'X', ']', DIST_XF86_SERVER_MACH8 },
+ { "Ma32", "8 and 16-bit (65K color) ATI Mach32 card",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86ServerDists, '[', 'X', ']', DIST_XF86_SERVER_MACH32 },
+ { "Ma64", "8 and 16-bit (65K color) ATI Mach64 card",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86ServerDists, '[', 'X', ']', DIST_XF86_SERVER_MACH64 },
+ { "P9K", "8, 16, and 24-bit color Weitek P9000 based boards",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86ServerDists, '[', 'X', ']', DIST_XF86_SERVER_P9000 },
+ { "S3", "8, 16 and 24-bit color S3 based boards",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86ServerDists, '[', 'X', ']', DIST_XF86_SERVER_S3 },
+ { "S3V", "8, 16 and 24-bit color S3 Virge based boards",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86ServerDists, '[', 'X', ']', DIST_XF86_SERVER_S3V },
+ { "W32", "8-bit ET4000/W32, /W32i and /W32p cards",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86ServerDists, '[', 'X', ']', DIST_XF86_SERVER_W32 },
+ { "nest", "A nested server for testing purposes",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86ServerDists, '[', 'X', ']', DIST_XF86_SERVER_NEST },
+ { "vfb", "A virtual frame-buffer server",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86ServerDists, '[', 'X', ']', DIST_XF86_SERVER_VFB },
+ { "PC98", "Select an X server for a NEC PC98 [Submenu]",
+ NULL, dmenuSubmenu, NULL, &MenuXF86SelectPC98Server, '>', ' ', '>', 0 },
+ { "All", "Select all of the above",
+ NULL, setX11Servers, NULL, NULL, ' ', ' ', ' ' },
+ { "Clear", "Reset all of the above",
+ NULL, clearX11Servers, NULL, NULL, ' ', ' ', ' ' },
+ { "Exit", "Exit this menu (returning to previous)",
+ checkTrue, dmenuExit, NULL, NULL, '<', '<', '<' },
+ { NULL } },
+};
+
+DMenu MenuXF86SelectPC98Server = {
+ DMENU_CHECKLIST_TYPE | DMENU_SELECTION_RETURNS,
+ "PC98 X Server selection.",
+ "Please check off the types of NEC PC98 X servers you wish to install.\n\
+If you are unsure as to which server will work for your graphics card,\n\
+it is recommended that try the SVGA or VGA16 servers (the VGA16 and\n\
+Mono servers are particularly well-suited to most LCD displays).",
+ "Press F1 to read the XFree86 release notes for FreeBSD",
+ "XF86",
+ { { "9480", "PC98 8-bit (256 color) PEGC-480 card",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86ServerDists, '[', 'X', ']', DIST_XF86_SERVER_9480 },
+ { "9EGC", "PC98 4-bit (16 color) EGC card",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86ServerDists, '[', 'X', ']', DIST_XF86_SERVER_9EGC },
+ { "9GA9", "PC98 GA-968V4/PCI (S3 968) card",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86ServerDists, '[', 'X', ']', DIST_XF86_SERVER_9GA9 },
+ { "9GAN", "PC98 GANB-WAP (cirrus) card",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86ServerDists, '[', 'X', ']', DIST_XF86_SERVER_9GAN },
+ { "9LPW", "PC98 PowerWindowLB (S3) card",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86ServerDists, '[', 'X', ']', DIST_XF86_SERVER_9LPW },
+ { "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 },
+ { "9TGU", "PC98 Cyber9320 and TGUI9680 cards",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86ServerDists, '[', 'X', ']', DIST_XF86_SERVER_9TGU },
+ { "9WEP", "PC98 WAB-EP (cirrus) card",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86ServerDists, '[', 'X', ']', DIST_XF86_SERVER_9WEP },
+ { "9WS", "PC98 WABS (cirrus) card",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86ServerDists, '[', 'X', ']', DIST_XF86_SERVER_9WS },
+ { "9WSN", "PC98 WSN-A2F (cirrus) card",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86ServerDists, '[', 'X', ']', DIST_XF86_SERVER_9WSN },
+ { "Exit", "Exit this menu (returning to previous)",
+ checkTrue, dmenuExit, NULL, NULL, '<', '<', '<' },
+ { NULL } }
+};
+#endif /* !USE_XIG_ENVIRONMENT */
+
+DMenu MenuDiskDevices = {
+ DMENU_CHECKLIST_TYPE | DMENU_SELECTION_RETURNS,
+ "Select Drive(s)",
+ "Please select the drive, or drives, on which you wish to perform\n"
+ "this operation. If you are attempting to install a boot partition\n"
+ "on a drive other than the first one or have multiple operating\n"
+ "systems on your machine, you will have the option to install a boot\n"
+ "manager later. To select a drive, use the arrow keys to move to it\n"
+ "and press [SPACE]. To de-select it, press [SPACE] again.\n\n"
+ "Select OK or Cancel to leave this menu.",
+ "Press F1 for important information regarding disk geometry!",
+ "drives",
+ { { NULL } },
+};
+
+DMenu MenuHTMLDoc = {
+ DMENU_NORMAL_TYPE,
+ "Select HTML Documentation pointer",
+ "Please select the body of documentation you're interested in, the main\n"
+ "ones right now being the FAQ and the Handbook. You can also chose \"other\"\n"
+ "to enter an arbitrary URL for browsing.",
+ "Press F1 for more help on what you see here.",
+ "html",
+ { { "Handbook", "The FreeBSD Handbook.", NULL, docShowDocument },
+ { "FAQ", "The Frequently Asked Questions guide.", NULL, docShowDocument },
+ { "Home", "The Home Pages for the FreeBSD Project (requires net)", NULL, docShowDocument },
+ { "Other", "Enter a URL.", NULL, docShowDocument },
+ { NULL } },
+};
+
+/* The main installation menu */
+DMenu MenuInstallCustom = {
+ DMENU_NORMAL_TYPE,
+ "Choose Custom Installation Options",
+ "This is the custom installation menu. You may use this menu to specify\n"
+ "details on the type of distribution you wish to have, where you wish\n"
+ "to install it from and how you wish to allocate disk storage to FreeBSD.",
+ "Press F1 to read the installation guide",
+ "install",
+ { { "1 Options", "View/Set various installation options", NULL, optionsEditor },
+ { "2 Partition", "Allocate disk space for FreeBSD", NULL, diskPartitionEditor },
+ { "3 Label", "Label allocated disk partitions", NULL, diskLabelEditor },
+ { "4 Distributions", "Select distribution(s) to extract", NULL, dmenuSubmenu, NULL, &MenuDistributions },
+ { "5 Media", "Choose the installation media type", NULL, dmenuSubmenu, NULL, &MenuMedia },
+ { "6 Commit", "Perform any pending Partition/Label/Extract actions", NULL, installCustomCommit },
+ { "0 Exit", "Exit this menu (returning to previous)", NULL, dmenuExit },
+ { NULL } },
+};
+
+/* MBR type menu */
+DMenu MenuMBRType = {
+ DMENU_RADIO_TYPE | DMENU_SELECTION_RETURNS,
+ "overwrite me", /* will be disk specific label */
+ "FreeBSD comes with a boot selector that allows you to easily\n"
+ "select between FreeBSD and any other operating systems on your machine\n"
+ "at boot time. If you have more than one drive and want to boot\n"
+ "from the second one, the boot selector will also make it possible\n"
+ "to do so (limitations in the PC BIOS usually prevent this otherwise).\n"
+ "If you do not want a boot selector, or wish to replace an existing\n"
+ "one, select \"standard\". If you would prefer your Master Boot\n"
+ "Record to remain untouched then select \"None\".\n\n"
+ " NOTE: PC-DOS users will almost certainly require \"None\"!",
+ "Press F1 to read about drive setup",
+ "drives",
+ { { "BootMgr", "Install the FreeBSD Boot Manager (\"Booteasy\")",
+ dmenuRadioCheck, dmenuSetValue, NULL, &BootMgr },
+ { "Standard", "Install a standard MBR (no boot manager)",
+ dmenuRadioCheck, dmenuSetValue, NULL, &BootMgr, '(', '*', ')', 1 },
+ { "None", "Leave the Master Boot Record untouched",
+ dmenuRadioCheck, dmenuSetValue, NULL, &BootMgr, '(', '*', ')', 2 },
+ { NULL } },
+};
+
+/* Final configuration menu */
+DMenu MenuConfigure = {
+ DMENU_NORMAL_TYPE,
+ "FreeBSD Configuration Menu", /* title */
+ "If you've already installed FreeBSD, you may use this menu to customize\n"
+ "it somewhat to suit your particular configuration. Most importantly,\n"
+ "you can use the Packages utility to load extra \"3rd party\"\n"
+ "software not provided in the base distributions.",
+ "Press F1 for more information on these options",
+ "configure",
+ { { "1 User Management", "Add user and group information",
+ NULL, dmenuSubmenu, NULL, &MenuUsermgmt },
+ { "2 Console", "Customize system console behavior",
+ NULL, dmenuSubmenu, NULL, &MenuSyscons },
+ { "3 Time Zone", "Set which time zone you're in",
+ NULL, dmenuSystemCommand, NULL, "tzsetup" },
+ { "4 Media", "Change the installation media type",
+ NULL, dmenuSubmenu, NULL, &MenuMedia },
+ { "5 Mouse", "Select the type of mouse you have",
+ NULL, dmenuSubmenu, NULL, &MenuMouse, NULL },
+ { "6 Networking", "Configure additional network services",
+ NULL, dmenuSubmenu, NULL, &MenuNetworking },
+ { "7 Startup", "Configure system startup services",
+ NULL, dmenuSubmenu, NULL, &MenuStartup },
+ { "8 Options", "View/Set various installation options",
+ NULL, optionsEditor },
+ { "9 Packages", "Install pre-packaged software for FreeBSD",
+ NULL, configPackages },
+ { "A Root Password", "Set the system manager's password",
+ NULL, dmenuSystemCommand, NULL, "passwd root" },
+ { "B HTML Docs", "Go to the HTML documentation menu (post-install)",
+ NULL, docBrowser },
+#ifdef USE_XIG_ENVIRONMENT
+ { "X X + CDE", "Configure X Window system & CDE environment",
+#else
+ { "X XFree86", "Configure XFree86",
+#endif
+ NULL, configXEnvironment },
+ { "D Distributions", "Install additional distribution sets",
+ NULL, distExtractAll },
+ { "L Label", "The disk Label editor",
+ NULL, diskLabelEditor },
+ { "P Partition", "The disk Slice (PC-style partition) Editor",
+ NULL, diskPartitionEditor },
+ { "R Register", "Register yourself or company as a FreeBSD user.", NULL, configRegister },
+ { "E Exit", "Exit this menu (returning to previous)",
+ NULL, dmenuExit },
+ { NULL } },
+};
+
+DMenu MenuStartup = {
+ DMENU_CHECKLIST_TYPE | DMENU_SELECTION_RETURNS,
+ "Startup Services Menu",
+ "This menu allows you to configure various aspects of your system's\n"
+ "startup configuration. Remember to use SPACE to select items! The\n"
+ "RETURN key will leave this menu (as with all checkbox menus).",
+ NULL,
+ NULL,
+ { { "APM", "Auto-power management services (typically laptops)",
+ dmenuVarCheck, dmenuToggleVariable, NULL, "apm_enable=YES" },
+ { "pccard", "Enable PCCARD (AKA PCMCIA) services (also laptops)",
+ dmenuVarCheck, dmenuToggleVariable, NULL, "pccard_enable=YES" },
+ { "pccard mem", "Set PCCARD memory address (if enabled)",
+ dmenuVarCheck, dmenuISetVariable, NULL, "pccard_mem" },
+ { "pccard ifconfig", "List of PCCARD ethernet devices to configure",
+ dmenuVarCheck, dmenuISetVariable, NULL, "pccard_ifconfig" },
+ { " ", " -- ", NULL, NULL, NULL, NULL, ' ', ' ', ' ' },
+ { "startup dirs", "Set the list of dirs to look for startup scripts",
+ dmenuVarCheck, dmenuISetVariable, NULL, "local_startup" },
+ { "named", "Run a local name server on this host",
+ dmenuVarCheck, dmenuToggleVariable, NULL, "named_enable=YES" },
+ { "named flags", "Set default flags to named (if enabled)",
+ dmenuVarCheck, dmenuISetVariable, NULL, "named_flags" },
+ { "nis client", "This host wishes to be an NIS client.",
+ dmenuVarCheck, dmenuToggleVariable, NULL, "nis_client_enable=YES" },
+ { "nis server", "This host wishes to be an NIS server.",
+ dmenuVarCheck, dmenuToggleVariable, NULL, "nis_server_enable=YES" },
+ { " ", " -- ", NULL, NULL, NULL, NULL, ' ', ' ', ' ' },
+ { "accounting", "This host wishes to run process accounting.",
+ dmenuVarCheck, dmenuToggleVariable, NULL, "accounting_enable=YES" },
+ { "lpd", "This host has a printer and wants to run lpd.",
+ dmenuVarCheck, dmenuToggleVariable, NULL, "lpd_enable=YES" },
+ { "linux", "This host wants to be able to run linux binaries.",
+ dmenuVarCheck, dmenuToggleVariable, NULL, "linux_enable=YES" },
+ { "quotas", "This host wishes to check quotas on startup.",
+ dmenuVarCheck, dmenuToggleVariable, NULL, "check_quotas=YES" },
+ { "SCO", "This host wants to be able to run IBCS2 binaries.",
+ dmenuVarCheck, dmenuToggleVariable, NULL, "ibcs2_enable=YES" },
+ { "Exit", "Exit this menu (returning to previous)",
+ checkTrue, dmenuExit, NULL, NULL, '<', '<', '<' },
+ { NULL } },
+};
+
+DMenu MenuNetworking = {
+ DMENU_CHECKLIST_TYPE | DMENU_SELECTION_RETURNS,
+ "Network Services Menu",
+ "You may have already configured one network device (and the other\n"
+ "various hostname/gateway/name server parameters) in the process\n"
+ "of installing FreeBSD. This menu allows you to configure other\n"
+ "aspects of your system's network configuration.",
+ NULL,
+ NULL,
+ { { "Interfaces", "Configure additional network interfaces",
+ NULL, tcpMenuSelect },
+ { "NFS client", "This machine will be an NFS client",
+ dmenuVarCheck, dmenuToggleVariable, NULL, "nfs_client_enable=YES" },
+ { "NFS server", "This machine will be an NFS server",
+ dmenuVarCheck, configNFSServer, NULL, "nfs_server_enable" },
+ { "AMD", "This machine wants to run the auto-mounter service",
+ dmenuVarCheck, dmenuToggleVariable, NULL, "amd_enable=YES" },
+ { "AMD Flags", "Set flags to AMD service (if enabled)",
+ dmenuVarCheck, dmenuISetVariable, NULL, "amd_flags" },
+ { "TCP Extentions", "Allow RFC1323 and RFC1544 TCP extentions?",
+ dmenuVarCheck, dmenuToggleVariable, NULL, "tcp_extentions=YES" },
+ { "Gateway", "This machine will route packets between interfaces",
+ dmenuVarCheck, dmenuToggleVariable, NULL, "gateway_enable=YES" },
+#ifdef NETCON_EXTENTIONS
+ { "Netcon", "Install the Novell client/server demo package",
+ dmenuVarCheck, configNovell, NULL, "novell" },
+#endif
+ { "Ntpdate", "Select a clock-syncronization server",
+ dmenuVarCheck, dmenuSubmenu, NULL, &MenuNTP, '[', 'X', ']', (int)"ntpdate_enable=YES" },
+ { "router", "Select routing daemon (default: routed)",
+ dmenuVarCheck, configRouter, NULL, "router" },
+ { "Rwhod", "This machine wants to run the rwho daemon",
+ dmenuVarCheck, dmenuToggleVariable, NULL, "rwhod_enable=YES" },
+ { "Anon FTP", "This machine wishes to allow anonymous FTP.",
+ dmenuVarCheck, configAnonFTP, NULL, "anon_ftp" },
+ { "PCNFSD", "Run authentication server for clients with PC-NFS.",
+ dmenuVarCheck, configPCNFSD, NULL, "pcnfsd" },
+ { "Exit", "Exit this menu (returning to previous)",
+ checkTrue, dmenuExit, NULL, NULL, '<', '<', '<' },
+ { NULL } },
+};
+
+DMenu MenuNTP = {
+ DMENU_RADIO_TYPE | DMENU_SELECTION_RETURNS,
+ "NTPDATE Server Selection",
+ "There are a number of time syncronization servers available\n"
+ "for public use around the Internet. Please select one reasonably\n"
+ "close to you to have your system time syncronized accordingly.",
+ "These are the primary open-access NTP servers",
+ NULL,
+ { { "None", "No ntp server",
+ dmenuVarCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=NO,ntpdate_flags=" },
+ { "Other", "Select a site not on this list",
+ dmenuVarsCheck, configNTP, NULL,
+ NULL },
+ { "Australia", "ntp.syd.dms.csiro.au (HP 5061 Cesium Beam)",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.syd.dms.csiro.au" },
+ { "Canada", "tick.usask.ca (GOES clock)",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=tick.usask.ca" },
+ { "France", "canon.inria.fr (TDF clock)",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=canon.inria.fr" },
+ { "Germany", "ntps1-{0,1,2}.uni-erlangen.de (GPS)",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntps1-0.uni-erlangen.de" },
+ { "Germany #2", "ntps1-0.cs.tu-berlin.de (GPS)",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntps1-0.cs.tu-berlin.de" },
+ { "Japan", "clock.nc.fukuoka-u.ac.jp (GPS clock)",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=clock.nc.fukuoka-u.ac.jp" },
+ { "Japan #2", "clock.tl.fukuoka-u.ac.jp (GPS clock)",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=clock.tl.fukuoka-u.ac.jp" },
+ { "Netherlands", "ntp0.nl.net (GPS clock)",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp0.nl.net" },
+ { "Norway", "timer.unik.no (NTP clock)",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=timer.unik.no" },
+ { "Sweden", "Time1.Stupi.SE (Cesium/GPS)",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=Time1.Stupi.SE" },
+ { "Switzerland", "swisstime.ethz.ch (DCF77 clock)",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=swisstime.ethz.ch" },
+ { "U.S. East Coast", "bitsy.mit.edu (WWV clock)",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=bitsy.mit.edu" },
+ { "U.S. East Coast #2", "otc1.psu.edu (WWV clock)",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=otc1.psu.edu" },
+ { "U.S. West Coast", "apple.com (WWV clock)",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=apple.com" },
+ { "U.S. West Coast #2", "clepsydra.dec.com (GOES clock)",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=clepsydra.dec.com" },
+ { "U.S. West Coast #3", "clock.llnl.gov (WWVB clock)",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=clock.llnl.gov" },
+ { "U.S. Midwest", "ncar.ucar.edu (WWVB clock)",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ncar.ucar.edu" },
+ { "U.S. Pacific", "chantry.hawaii.net (WWV/H clock)",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=chantry.hawaii.net" },
+ { "U.S. Southwest", "shorty.chpc.utexas.edu (WWV clock)",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=shorty.chpc.utexas.edu" },
+ { NULL } },
+};
+
+DMenu MenuSyscons = {
+ DMENU_NORMAL_TYPE,
+ "System Console Configuration",
+ "The default system console driver for FreeBSD (syscons) has a\n"
+ "number of configuration options which may be set according to\n"
+ "your preference.\n\n"
+ "When you are done setting configuration options, select Cancel.",
+ "Configure your system console settings",
+ NULL,
+ { { "Font", "Choose an alternate screen font", NULL, dmenuSubmenu, NULL, &MenuSysconsFont },
+ { "Keymap", "Choose an alternate keyboard map", NULL, dmenuSubmenu, NULL, &MenuSysconsKeymap },
+ { "Repeat", "Set the rate at which keys repeat", NULL, dmenuSubmenu, NULL, &MenuSysconsKeyrate },
+ { "Saver", "Configure the screen saver", NULL, dmenuSubmenu, NULL, &MenuSysconsSaver },
+ { "Screenmap", "Choose an alternate screenmap", NULL, dmenuSubmenu, NULL, &MenuSysconsScrnmap },
+ { "Exit", "Exit this menu (returning to previous)", NULL, dmenuExit },
+ { NULL } },
+};
+
+DMenu MenuSysconsKeymap = {
+ DMENU_RADIO_TYPE | DMENU_SELECTION_RETURNS,
+ "System Console Keymap",
+ "The default system console driver for FreeBSD (syscons) defaults\n"
+ "to a standard \"American\" keyboard map. Users in other countries\n"
+ "(or with different keyboard preferences) may wish to choose one of\n"
+ "the other keymaps below.\n"
+ "Note that sysinstall itself only uses the part of the keyboard map\n"
+ "which is required to generate the ANSI character subset, but your\n"
+ "choice of keymap will also be saved for later (fuller) use.",
+ "Choose a keyboard map",
+ NULL,
+ { { "Belgian", "Belgian ISO keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=be.iso" },
+ { "Brazil CP850", "Brazil CP850 keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=br275.cp850" },
+ { "Brazil ISO", "Brazil ISO keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=br275.iso" },
+ { "Danish CP865", "Danish Code Page 865 keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=danish.cp865" },
+ { "Danish ISO", "Danish ISO keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=danish.iso" },
+ { "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" },
+ { "Italian", "Italian ISO keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=it.iso" },
+ { "Japanese 106", "Japanese 106 keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=jp.106" },
+ { "Norway ISO", "Norwegian ISO keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=norwegian.iso" },
+ { "Russia CP866", "Russian CP866 keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=ru.cp866" },
+ { "Russia KOI8-R", "Russian KOI8-R keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=ru.koi8-r" },
+ { "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 German", "Swiss German ISO keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=swissgerman.iso.kbd" },
+ { "U.K. CP850", "United Kingdom Code Page 850 keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=uk.cp850" },
+ { "U.K. ISO", "United Kingdom ISO keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=uk.iso" },
+ { "U.S. Dvorak", "United States Dvorak keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=us.dvorak" },
+ { "U.S. ISO", "United States ISO keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=us.iso" },
+ { NULL } },
+};
+
+DMenu MenuSysconsKeyrate = {
+ DMENU_RADIO_TYPE | DMENU_SELECTION_RETURNS,
+ "System Console Keyboard Repeat Rate",
+ "This menu allows you to set the speed at which keys repeat\n"
+ "when held down.",
+ "Choose a keyboard repeat rate",
+ NULL,
+ { { "Slow", "Slow keyboard repeat rate", dmenuVarCheck, dmenuSetVariable, NULL, "keyrate=slow" },
+ { "Normal", "\"Normal\" keyboard repeat rate", dmenuVarCheck, dmenuSetVariable, NULL, "keyrate=normal" },
+ { "Fast", "Fast keyboard repeat rate", dmenuVarCheck, dmenuSetVariable, NULL, "keyrate=fast" },
+ { "Default", "Use default keyboard repeat rate", dmenuVarCheck, dmenuSetVariable, NULL, "keyrate=NO" },
+ { NULL } },
+};
+
+DMenu MenuSysconsSaver = {
+ DMENU_RADIO_TYPE | DMENU_SELECTION_RETURNS,
+ "System Console Screen Saver",
+ "By default, the console driver will not attempt to do anything\n"
+ "special with your screen when it's idle. If you expect to leave your\n"
+ "monitor switched on and idle for long periods of time then you should\n"
+ "probably enable one of these screen savers to prevent phosphor burn-in.",
+ "Choose a nifty-looking screen saver",
+ NULL,
+ { { "blank", "Simply blank the screen",
+ dmenuVarCheck, configSaver, NULL, "saver=blank" },
+ { "Daemon", "\"BSD Daemon\" animated screen saver",
+ dmenuVarCheck, configSaver, NULL, "saver=daemon" },
+ { "Green", "\"Green\" power saving mode (if supported by monitor)",
+ dmenuVarCheck, configSaver, NULL, "saver=green" },
+ { "Snake", "Draw a FreeBSD \"snake\" on your screen",
+ dmenuVarCheck, configSaver, NULL, "saver=snake" },
+ { "Star", "A \"twinkling stars\" effect",
+ dmenuVarCheck, configSaver, NULL, "saver=star" },
+ { "Timeout", "Set the screen saver timeout interval",
+ NULL, configSaverTimeout, NULL, NULL, ' ', ' ', ' ' },
+ { NULL } },
+};
+
+DMenu MenuSysconsScrnmap = {
+ DMENU_RADIO_TYPE | DMENU_SELECTION_RETURNS,
+ "System Console Screenmap",
+ "Unless you load a specific font, most PC hardware defaults to\n"
+ "displaying characters in the IBM 437 character set. However,\n"
+ "in the Unix world, this character set is very rarely used. Most\n"
+ "Western European countries, for example, prefer ISO 8859-1.\n"
+ "American users won't notice the difference since the bottom half\n"
+ "of all these character sets is ANSI anyway.\n"
+ "If your hardware is capable of downloading a new display font,\n"
+ "you should probably choose that option. However, for hardware\n"
+ "where this is not possible (e.g. monochrome adapters), a screen\n"
+ "map will give you the best approximation that your hardware can\n"
+ "display at all.",
+ "Choose a screen map",
+ NULL,
+ { { "None", "No screenmap, use default font", dmenuVarCheck, dmenuSetVariable, NULL, "scrnmap=NO" },
+ { "KOI8-R to IBM866", "Russian KOI8-R to IBM 866 screenmap", dmenuVarCheck, dmenuSetVariable, NULL, "scrnmap=koi8-r2cp866" },
+ { "ISO 8859-1 to IBM437", "W-Europe ISO 8859-1 to IBM 437 screenmap", dmenuVarCheck, dmenuSetVariable, NULL, "scrnmap=iso-8859-1_to_cp437" },
+ { NULL } },
+};
+
+DMenu MenuSysconsFont = {
+ DMENU_RADIO_TYPE | DMENU_SELECTION_RETURNS,
+ "System Console Font",
+ "Most PC hardware defaults to displaying characters in the\n"
+ "IBM 437 character set. However, in the Unix world, this\n"
+ "character set is very rarely used. Most Western European\n"
+ "countries, for example, prefer ISO 8859-1.\n"
+ "American users won't notice the difference since the bottom half\n"
+ "of all these charactersets is ANSI anyway. However, they might\n"
+ "want to load a font anyway to use the 30- or 50-line displays.\n"
+ "If your hardware is capable of downloading a new display font,\n"
+ "you can select the appropriate font below.",
+ "Choose a font",
+ NULL,
+ { { "None", "Use default font", dmenuVarCheck, dmenuSetVariables, NULL,
+ "font8x8=NO,font8x14=NO,font8x16=NO" },
+ { "IBM 437", "English", dmenuVarCheck, dmenuSetVariables, NULL,
+ "font8x8=cp437-8x8,font8x14=cp437-8x14,font8x16=cp437-8x16" },
+ { "IBM 850", "Western Europe, IBM encoding", dmenuVarCheck, dmenuSetVariables, NULL,
+ "font8x8=cp850-8x8,font8x14=cp850-8x14,font8x16=cp850-8x16" },
+ { "IBM 865", "Norwegian, IBM encoding", dmenuVarCheck, dmenuSetVariables, NULL,
+ "font8x8=cp865-8x8,font8x14=cp865-8x14,font8x16=cp865-8x16" },
+ { "IBM 866", "Russian, IBM encoding", dmenuVarCheck, dmenuSetVariables, NULL,
+ "font8x8=cp866-8x8,font8x14=cp866-8x14,font8x16=cp866-8x16" },
+ { "ISO 8859-1", "Western Europe, ISO encoding", dmenuVarCheck, dmenuSetVariables, NULL,
+ "font8x8=iso-8x8,font8x14=iso-8x14,font8x16=iso-8x16" },
+ { "KOI8-R", "Russian, KOI8-R encoding", dmenuVarCheck, dmenuSetVariables, NULL,
+ "font8x8=koi8-r-8x8,font8x14=koi8-r-8x14,font8x16=koi8-r-8x16" },
+ { NULL } },
+};
+
+DMenu MenuUsermgmt = {
+ DMENU_NORMAL_TYPE,
+ "User and group management",
+ "The submenus here allow to manipulate user groups and\n"
+ "login accounts.\n",
+ "Configure your user groups and users",
+ NULL,
+ { { "Add user", "Add a new user to the system.", NULL, userAddUser },
+ { "Add group", "Add a new user group to the system.", NULL, userAddGroup },
+ { "Exit", "Exit this menu (returning to previous)", NULL, dmenuExit },
+ { NULL } },
+};
+
+DMenu MenuFixit = {
+ DMENU_NORMAL_TYPE,
+ "Please choose a fixit option",
+ "There are three ways of going into \"fixit\" mode:\n"
+ "- you can use the 2nd FreeBSD CDROM, in which case there will be\n"
+ " full access to the complete set of FreeBSD commands and utilities,\n"
+ "- you can use the more limited (but perhaps customized) fixit floppy,\n"
+ "- or you can start an Emergency Holographic Shell now, which is\n"
+ " limited to the subset of commands that is already available right now.",
+ "Press F1 for more detailed repair instructions",
+ "fixit",
+{ { "1 CDROM", "Use the 2nd \"live\" CDROM from the distribution", NULL, installFixitCDROM },
+ { "2 Floppy", "Use a floppy generated from the fixit image", NULL, installFixitFloppy },
+ { "3 Shell", "Start an Emergency Holographic Shell", NULL, installFixitHoloShell },
+ { NULL } },
+};
+
diff --git a/usr.sbin/sade/misc.c b/usr.sbin/sade/misc.c
new file mode 100644
index 0000000..d3d67a7
--- /dev/null
+++ b/usr.sbin/sade/misc.c
@@ -0,0 +1,485 @@
+/*
+ * Miscellaneous support routines..
+ *
+ * $Id: misc.c,v 1.35 1997/06/13 07:11:56 jkh Exp $
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include "sysinstall.h"
+#include <ctype.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/errno.h>
+#include <sys/file.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <sys/wait.h>
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <ufs/ufs/ufsmount.h>
+#include <sys/reboot.h>
+#include <sys/dkbad.h>
+#include <sys/disklabel.h>
+
+/* Quick check to see if a file is readable */
+Boolean
+file_readable(char *fname)
+{
+ if (!access(fname, F_OK))
+ return TRUE;
+ return FALSE;
+}
+
+/* Quick check to see if a file is executable */
+Boolean
+file_executable(char *fname)
+{
+ if (!access(fname, X_OK))
+ return TRUE;
+ return FALSE;
+}
+
+/* Concatenate two strings into static storage */
+char *
+string_concat(char *one, char *two)
+{
+ static char tmp[FILENAME_MAX];
+
+ /* Yes, we're deliberately cavalier about not checking for overflow */
+ strcpy(tmp, one);
+ strcat(tmp, two);
+ return tmp;
+}
+
+/* sane strncpy() function */
+char *
+sstrncpy(char *dst, const char *src, int size)
+{
+ dst[size] = '\0';
+ return strncpy(dst, src, size);
+}
+
+/* Concatenate three strings into static storage */
+char *
+string_concat3(char *one, char *two, char *three)
+{
+ static char tmp[FILENAME_MAX];
+
+ /* Yes, we're deliberately cavalier about not checking for overflow */
+ strcpy(tmp, one);
+ strcat(tmp, two);
+ strcat(tmp, three);
+ return tmp;
+}
+
+/* Clip the whitespace off the end of a string */
+char *
+string_prune(char *str)
+{
+ int len = str ? strlen(str) : 0;
+
+ while (len && isspace(str[len - 1]))
+ str[--len] = '\0';
+ return str;
+}
+
+/* run the whitespace off the front of a string */
+char *
+string_skipwhite(char *str)
+{
+ while (*str && isspace(*str))
+ ++str;
+ return str;
+}
+
+/* copy optionally and allow second arg to be null */
+char *
+string_copy(char *s1, char *s2)
+{
+ if (!s1)
+ return NULL;
+ if (!s2)
+ s1[0] = '\0';
+ else
+ strcpy(s1, s2);
+ return s1;
+}
+
+/* convert an integer to a string, using a static buffer */
+char *
+itoa(int value)
+{
+ static char buf[13];
+
+ snprintf(buf, 12, "%d", value);
+ return buf;
+}
+
+Boolean
+directory_exists(const char *dirname)
+{
+ DIR *tptr;
+
+ if (!dirname)
+ return FALSE;
+ if (!strlen(dirname))
+ return FALSE;
+
+ tptr = opendir(dirname);
+ if (!tptr)
+ return (FALSE);
+
+ closedir(tptr);
+ return (TRUE);
+}
+
+char *
+pathBaseName(const char *path)
+{
+ char *pt;
+ char *ret = (char *)path;
+
+ pt = strrchr(path,(int)'/');
+
+ if (pt != 0) /* if there is a slash */
+ {
+ ret = ++pt; /* start the file after it */
+ }
+
+ return(ret);
+}
+
+/* A free guaranteed to take NULL ptrs */
+void
+safe_free(void *ptr)
+{
+ if (ptr)
+ free(ptr);
+}
+
+/* A malloc that checks errors */
+void *
+safe_malloc(size_t size)
+{
+ void *ptr;
+
+ if (size <= 0)
+ msgFatal("Invalid malloc size of %d!", size);
+ ptr = malloc(size);
+ if (!ptr)
+ msgFatal("Out of memory!");
+ bzero(ptr, size);
+ return ptr;
+}
+
+/* A realloc that checks errors */
+void *
+safe_realloc(void *orig, size_t size)
+{
+ void *ptr;
+
+ if (size <= 0)
+ msgFatal("Invalid realloc size of %d!", size);
+ ptr = realloc(orig, size);
+ if (!ptr)
+ msgFatal("Out of memory!");
+ return ptr;
+}
+
+/* Create a path biased from the VAR_INSTALL_ROOT variable (if not /) */
+char *
+root_bias(char *path)
+{
+ static char tmp[FILENAME_MAX];
+ char *cp = variable_get(VAR_INSTALL_ROOT);
+
+ if (!strcmp(cp, "/"))
+ return path;
+ strcpy(tmp, variable_get(VAR_INSTALL_ROOT));
+ strcat(tmp, path);
+ return tmp;
+}
+
+/*
+ * These next routines are kind of specialized just for building item lists
+ * for dialog_menu().
+ */
+
+/* Add an item to an item list */
+dialogMenuItem *
+item_add(dialogMenuItem *list, char *prompt, char *title,
+ int (*checked)(dialogMenuItem *self),
+ int (*fire)(dialogMenuItem *self),
+ void (*selected)(dialogMenuItem *self, int is_selected),
+ void *data, int aux, int *curr, int *max)
+{
+ dialogMenuItem *d;
+
+ if (*curr == *max) {
+ *max += 20;
+ list = (dialogMenuItem *)realloc(list, sizeof(dialogMenuItem) * *max);
+ }
+ d = &list[(*curr)++];
+ bzero(d, sizeof(*d));
+ d->prompt = prompt ? strdup(prompt) : NULL;
+ d->title = title ? strdup(title) : NULL;
+ d->checked = checked;
+ d->fire = fire;
+ d->selected = selected;
+ d->data = data;
+ d->aux = aux;
+ return list;
+}
+
+/* Toss the items out */
+void
+items_free(dialogMenuItem *list, int *curr, int *max)
+{
+ int i;
+
+ for (i = 0; list[i].prompt; i++) {
+ safe_free(list[i].prompt);
+ safe_free(list[i].title);
+ }
+ safe_free(list);
+ *curr = *max = 0;
+}
+
+int
+Mkdir(char *ipath)
+{
+ struct stat sb;
+ int final;
+ char *p, *path;
+
+ if (file_readable(ipath) || Fake)
+ return DITEM_SUCCESS;
+
+ path = strcpy(alloca(strlen(ipath) + 1), ipath);
+ if (isDebug())
+ msgDebug("mkdir(%s)\n", path);
+ p = path;
+ if (p[0] == '/') /* Skip leading '/'. */
+ ++p;
+ for (final = FALSE; !final; ++p) {
+ if (p[0] == '\0' || (p[0] == '/' && p[1] == '\0'))
+ final = TRUE;
+ else if (p[0] != '/')
+ continue;
+ *p = '\0';
+ if (stat(path, &sb)) {
+ if (errno != ENOENT) {
+ msgConfirm("Couldn't stat directory %s: %s", path, strerror(errno));
+ return DITEM_FAILURE;
+ }
+ if (isDebug())
+ msgDebug("mkdir(%s..)\n", path);
+ if (mkdir(path, S_IRWXU | S_IRWXG | S_IRWXO) < 0) {
+ msgConfirm("Couldn't create directory %s: %s", path,strerror(errno));
+ return DITEM_FAILURE;
+ }
+ }
+ *p = '/';
+ }
+ return DITEM_SUCCESS;
+}
+
+int
+Mount(char *mountp, void *dev)
+{
+ struct ufs_args ufsargs;
+ char device[80];
+ char mountpoint[FILENAME_MAX];
+
+ if (Fake)
+ return DITEM_SUCCESS;
+
+ if (*((char *)dev) != '/') {
+ sprintf(device, "%s/dev/%s", RunningAsInit ? "/mnt" : "", (char *)dev);
+ sprintf(mountpoint, "%s%s", RunningAsInit ? "/mnt" : "", mountp);
+ }
+ else {
+ strcpy(device, dev);
+ strcpy(mountpoint, mountp);
+ }
+ memset(&ufsargs,0,sizeof ufsargs);
+
+ if (Mkdir(mountpoint)) {
+ msgConfirm("Unable to make directory mountpoint for %s!", mountpoint);
+ return DITEM_FAILURE;
+ }
+ if (isDebug())
+ msgDebug("mount %s %s\n", device, mountpoint);
+
+ ufsargs.fspec = device;
+ if (mount(MOUNT_UFS, mountpoint, RunningAsInit ? MNT_ASYNC : 0,
+ (caddr_t)&ufsargs) == -1) {
+ msgConfirm("Error mounting %s on %s : %s", device, mountpoint, strerror(errno));
+ return DITEM_FAILURE;
+ }
+ return DITEM_SUCCESS;
+}
+
+WINDOW *
+openLayoutDialog(char *helpfile, char *title, int x, int y, int width, int height)
+{
+ WINDOW *win;
+ static char help[FILENAME_MAX];
+
+ /* We need a curses window */
+ win = newwin(LINES, COLS, 0, 0);
+ if (win) {
+ /* Say where our help comes from */
+ if (helpfile) {
+ use_helpline("Press F1 for more information on this screen.");
+ use_helpfile(systemHelpFile(helpfile, help));
+ }
+ /* Setup a nice screen for us to splat stuff onto */
+ draw_box(win, y, x, height, width, dialog_attr, border_attr);
+ wattrset(win, dialog_attr);
+ mvwaddstr(win, y, x + (COLS - strlen(title)) / 2, title);
+ }
+ return win;
+}
+
+ComposeObj *
+initLayoutDialog(WINDOW *win, Layout *layout, int x, int y, int *max)
+{
+ ComposeObj *obj = NULL, *first;
+ int n;
+
+ /* Loop over the layout list, create the objects, and add them
+ onto the chain of objects that dialog uses for traversal*/
+
+ n = 0;
+ while (layout[n].help != NULL) {
+ int t = TYPE_OF_OBJ(layout[n].type);
+
+ switch (t) {
+ case STRINGOBJ:
+ layout[n].obj = NewStringObj(win, layout[n].prompt, layout[n].var,
+ layout[n].y + y, layout[n].x + x, layout[n].len, layout[n].maxlen);
+ ((StringObj *)layout[n].obj)->attr_mask = ATTR_OF_OBJ(layout[n].type);
+ break;
+
+ case BUTTONOBJ:
+ layout[n].obj = NewButtonObj(win, layout[n].prompt, layout[n].var, layout[n].y + y, layout[n].x + x);
+ break;
+
+ default:
+ msgFatal("Don't support this object yet!");
+ }
+ AddObj(&obj, t, (void *) layout[n].obj);
+ n++;
+ }
+ *max = n - 1;
+ /* Find the first object in the list */
+ for (first = obj; first->prev; first = first->prev);
+ return first;
+}
+
+int
+layoutDialogLoop(WINDOW *win, Layout *layout, ComposeObj **obj, int *n, int max, int *cbutton, int *cancel)
+{
+ char help_line[80];
+ int ret, i, len = strlen(layout[*n].help);
+
+ /* Display the help line at the bottom of the screen */
+ for (i = 0; i < 79; i++)
+ help_line[i] = (i < len) ? layout[*n].help[i] : ' ';
+ help_line[i] = '\0';
+ use_helpline(help_line);
+ display_helpline(win, LINES - 1, COLS - 1);
+ wrefresh(win);
+
+ /* Ask for libdialog to do its stuff */
+ ret = PollObj(obj);
+ /* Handle special case stuff that libdialog misses. Sigh */
+ switch (ret) {
+ case SEL_ESC: /* Bail out */
+ *cancel = TRUE;
+ return FALSE;
+
+ /* This doesn't work for list dialogs. Oh well. Perhaps
+ should special case the move from the OK button ``up''
+ to make it go to the interface list, but then it gets
+ awkward for the user to go back and correct screw up's
+ in the per-interface section */
+ case KEY_DOWN:
+ case SEL_CR:
+ case SEL_TAB:
+ if (*n < max)
+ ++*n;
+ else
+ *n = 0;
+ break;
+
+ /* The user has pressed enter over a button object */
+ case SEL_BUTTON:
+ if (cbutton && *cbutton)
+ *cancel = TRUE;
+ else
+ *cancel = FALSE;
+ return FALSE;
+
+ case KEY_UP:
+ case SEL_BACKTAB:
+ if (*n)
+ --*n;
+ else
+ *n = max;
+ break;
+
+ case KEY_F(1):
+ display_helpfile();
+
+ /* They tried some key combination we don't support - tootle them forcefully! */
+ default:
+ beep();
+ }
+ return TRUE;
+}
+
+WINDOW *
+savescr(void)
+{
+ WINDOW *w;
+
+ w = dupwin(newscr);
+ return w;
+}
+
+void
+restorescr(WINDOW *w)
+{
+ touchwin(w);
+ wrefresh(w);
+ delwin(w);
+}
+
diff --git a/usr.sbin/sade/msg.c b/usr.sbin/sade/msg.c
new file mode 100644
index 0000000..e61b767
--- /dev/null
+++ b/usr.sbin/sade/msg.c
@@ -0,0 +1,320 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last program in the `sysinstall' line - the next
+ * generation being essentially a complete rewrite.
+ *
+ * $Id: msg.c,v 1.46 1997/09/09 16:27:50 jkh Exp $
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include "sysinstall.h"
+#include <stdarg.h>
+#include <sys/ioctl.h>
+#include <machine/console.h>
+
+Boolean
+isDebug(void)
+{
+ char *cp;
+
+ return (cp = variable_get(VAR_DEBUG)) && strcmp(cp, "no");
+}
+
+/* Whack up an informational message on the status line, in stand-out */
+void
+msgYap(char *fmt, ...)
+{
+ va_list args;
+ char *errstr;
+ int attrs;
+
+ errstr = (char *)alloca(FILENAME_MAX);
+ va_start(args, fmt);
+ vsnprintf(errstr, FILENAME_MAX, fmt, args);
+ va_end(args);
+ attrs = getattrs(stdscr);
+ attrset(A_REVERSE);
+ mvaddstr(StatusLine, 0, errstr);
+ attrset(attrs);
+ refresh();
+}
+
+/* Whack up an informational message on the status line */
+void
+msgInfo(char *fmt, ...)
+{
+ va_list args;
+ char *errstr;
+ int i, attrs;
+ char line[81];
+
+ attrs = getattrs(stdscr);
+ /* NULL is a special convention meaning "erase the old stuff" */
+ if (!fmt) {
+ move(StatusLine, 0);
+ clrtoeol();
+ return;
+ }
+ errstr = (char *)alloca(FILENAME_MAX);
+ va_start(args, fmt);
+ vsnprintf(errstr, FILENAME_MAX, fmt, args);
+ va_end(args);
+ memset(line, ' ', 80);
+ for (i = 0; i < 80; i++) {
+ if (errstr[i])
+ line[i] = errstr[i];
+ else
+ break;
+ }
+ line[80] = '\0';
+ attrset(ATTR_TITLE);
+ mvaddstr(StatusLine, 0, line);
+ attrset(attrs);
+ move(StatusLine, 79);
+ refresh();
+}
+
+/* Whack up a warning on the status line */
+void
+msgWarn(char *fmt, ...)
+{
+ va_list args;
+ char *errstr;
+ int attrs;
+
+ errstr = (char *)alloca(FILENAME_MAX);
+ strcpy(errstr, "Warning: ");
+ va_start(args, fmt);
+ vsnprintf((char *)(errstr + strlen(errstr)), FILENAME_MAX, fmt, args);
+ va_end(args);
+ attrs = getattrs(stdscr);
+ beep();
+ attrset(ATTR_TITLE);
+ mvaddstr(StatusLine, 0, errstr);
+ attrset(attrs);
+ refresh();
+ if (OnVTY && isDebug())
+ msgDebug("Warning message `%s'\n", errstr);
+}
+
+/* Whack up an error on the status line */
+void
+msgError(char *fmt, ...)
+{
+ va_list args;
+ char *errstr;
+ int attrs;
+
+ errstr = (char *)alloca(FILENAME_MAX);
+ strcpy(errstr, "Error: ");
+ va_start(args, fmt);
+ vsnprintf((char *)(errstr + strlen(errstr)), FILENAME_MAX, fmt, args);
+ va_end(args);
+ beep();
+ attrs = getattrs(stdscr);
+ attrset(ATTR_TITLE);
+ mvaddstr(StatusLine, 0, errstr);
+ attrset(attrs);
+ refresh();
+ if (OnVTY && isDebug())
+ msgDebug("Error message `%s'\n", errstr);
+}
+
+/* Whack up a fatal error on the status line */
+void
+msgFatal(char *fmt, ...)
+{
+ va_list args;
+ char *errstr;
+ int attrs;
+
+ errstr = (char *)alloca(FILENAME_MAX);
+ strcpy(errstr, "Fatal Error: ");
+ va_start(args, fmt);
+ vsnprintf((char *)(errstr + strlen(errstr)), FILENAME_MAX, fmt, args);
+ va_end(args);
+ beep();
+ attrs = getattrs(stdscr);
+ attrset(ATTR_TITLE);
+ mvaddstr(StatusLine, 0, errstr);
+ addstr(" - ");
+ addstr("PRESS ANY KEY TO ");
+ if (getpid() == 1)
+ addstr("REBOOT");
+ else
+ addstr("QUIT");
+ attrset(attrs);
+ refresh();
+ if (OnVTY)
+ msgDebug("Fatal error `%s'!\n", errstr);
+ getch();
+ systemShutdown(1);
+}
+
+/* Put up a message in a popup confirmation box */
+void
+msgConfirm(char *fmt, ...)
+{
+ va_list args;
+ char *errstr;
+
+ errstr = (char *)alloca(FILENAME_MAX);
+ va_start(args, fmt);
+ vsnprintf(errstr, FILENAME_MAX, fmt, args);
+ va_end(args);
+ use_helpline(NULL);
+ use_helpfile(NULL);
+ if (OnVTY) {
+ ioctl(0, VT_ACTIVATE, 1);
+ msgInfo(NULL);
+ }
+ dialog_notify(errstr);
+}
+
+/* Put up a message in a popup information box */
+void
+msgNotify(char *fmt, ...)
+{
+ va_list args;
+ char *errstr;
+
+ errstr = (char *)alloca(FILENAME_MAX);
+ va_start(args, fmt);
+ vsnprintf(errstr, FILENAME_MAX, fmt, args);
+ va_end(args);
+ use_helpline(NULL);
+ use_helpfile(NULL);
+ if (isDebug())
+ msgDebug("Notify: %s\n", errstr);
+ dialog_clear_norefresh();
+ dialog_msgbox(NULL, errstr, -1, -1, 0);
+}
+
+/* Put up a message in a popup yes/no box and return 1 for YES, 0 for NO */
+int
+msgYesNo(char *fmt, ...)
+{
+ va_list args;
+ char *errstr;
+ int ret;
+
+ errstr = (char *)alloca(FILENAME_MAX);
+ va_start(args, fmt);
+ vsnprintf(errstr, FILENAME_MAX, fmt, args);
+ va_end(args);
+ use_helpline(NULL);
+ use_helpfile(NULL);
+ if (OnVTY) {
+ ioctl(0, VT_ACTIVATE, 1); /* Switch back */
+ msgInfo(NULL);
+ }
+ ret = dialog_yesno("User Confirmation Requested", errstr, -1, -1);
+ return ret;
+}
+
+/* Put up a message in an input box and return the value */
+char *
+msgGetInput(char *buf, char *fmt, ...)
+{
+ va_list args;
+ char *errstr;
+ static char input_buffer[256];
+ int rval;
+
+ errstr = (char *)alloca(FILENAME_MAX);
+ va_start(args, fmt);
+ vsnprintf(errstr, FILENAME_MAX, fmt, args);
+ va_end(args);
+ use_helpline(NULL);
+ use_helpfile(NULL);
+ if (buf)
+ SAFE_STRCPY(input_buffer, buf);
+ else
+ input_buffer[0] = '\0';
+ if (OnVTY) {
+ ioctl(0, VT_ACTIVATE, 1); /* Switch back */
+ msgInfo(NULL);
+ }
+ rval = dialog_inputbox("Value Required", errstr, -1, -1, input_buffer);
+ if (!rval)
+ return input_buffer;
+ else
+ return NULL;
+}
+
+/* Write something to the debugging port */
+void
+msgDebug(char *fmt, ...)
+{
+ va_list args;
+ char *dbg;
+
+ if (DebugFD == -1)
+ return;
+ dbg = (char *)alloca(FILENAME_MAX);
+ strcpy(dbg, "DEBUG: ");
+ va_start(args, fmt);
+ vsnprintf((char *)(dbg + strlen(dbg)), FILENAME_MAX, fmt, args);
+ va_end(args);
+ write(DebugFD, dbg, strlen(dbg));
+}
+
+/* Tell the user there's some output to go look at */
+void
+msgWeHaveOutput(char *fmt, ...)
+{
+ va_list args;
+ char *errstr;
+
+ errstr = (char *)alloca(FILENAME_MAX);
+ va_start(args, fmt);
+ vsnprintf(errstr, FILENAME_MAX, fmt, args);
+ va_end(args);
+ use_helpline(NULL);
+ use_helpfile(NULL);
+ msgDebug("Notify: %s\n", errstr);
+ dialog_clear_norefresh();
+ dialog_msgbox(NULL, errstr, -1, -1, 0);
+}
+
+/* Simple versions of msgConfirm() and msgNotify() for calling from scripts */
+int
+msgSimpleConfirm(char *str)
+{
+ msgConfirm(str);
+ return DITEM_SUCCESS;
+}
+
+int
+msgSimpleNotify(char *str)
+{
+ msgNotify(str);
+ return DITEM_SUCCESS;
+}
diff --git a/usr.sbin/sade/rtermcap.c b/usr.sbin/sade/rtermcap.c
new file mode 100644
index 0000000..84b3feb
--- /dev/null
+++ b/usr.sbin/sade/rtermcap.c
@@ -0,0 +1,15 @@
+#include <stdio.h>
+#include <termcap.h>
+
+int
+main(int argc, char **argv)
+{
+ char buf[4096];
+ int i;
+
+ if (argc < 2)
+ return 1;
+ i = tgetent(buf, argv[1]);
+ printf("%s",buf);
+ return 0;
+}
diff --git a/usr.sbin/sade/sade.8 b/usr.sbin/sade/sade.8
new file mode 100644
index 0000000..67f5952
--- /dev/null
+++ b/usr.sbin/sade/sade.8
@@ -0,0 +1,804 @@
+.\" Copyright (c) 1997
+.\" Jordan Hubbard <jkh@freebsd.org>. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY Jordan Hubbard AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL Jordan Hubbard OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $Id: sysinstall.8,v 1.5 1997/09/10 10:15:41 jkh Exp $
+.\"
+.Dd August 9, 1997
+.Dt SYSINSTALL 8
+.Os
+.Sh NAME
+.Nm sysinstall
+.Nd system installation and configuration tool
+.Sh SYNOPSIS
+.Nm
+.Op Ar var=value
+.Op Ar function
+.Op Ar ...
+.Sh DESCRIPTION
+.Nm
+is a utility for installing and configuring FreeBSD systems.
+It is the first utility invoked by the FreeBSD installation boot
+floppy and is also copied into
+.Pa /stand/sysinstall
+on newly installed FreeBSD systems for use in later configuring the system.
+.Pp
+The
+.Nm
+program is generally invoked without arguments for the default
+behavior, where the main installation/configuration menu is presented.
+
+On those occasions where it is deemed necessary to invoke a subsystem
+of sysinstall directly, however, it is also possible to do so by
+naming the appropriate function entry points on the command line.
+Since this action is essentially identical to running an installation
+script, each command-line argument corresponding to a line of script,
+the reader is encouraged to read the section on scripting for more
+information on this feature.
+.Pp
+.Sh NOTES
+.Nm
+is essentially nothing more than a monolithic C program with
+the ability to write MBRs and disk labels (through the services
+of the
+.Xr libdisk 3
+library) and install distributions or packages onto new and
+existing FreeBSD systems. It also contains some extra intelligence
+for running as a replacement for
+.Xr init 8
+when it's invoked by the FreeBSD installation boot procedure. It
+assumes very little in the way of additional utility support and
+performs most file system operations by calling the relevant syscalls
+(such as
+.Xr mount 2 )
+directly.
+.Pp
+.Nm
+currently uses the
+.Xr libdialog 3
+library to do user interaction with simple ANSI line graphics, color
+support for which is enabled by either running on a syscons VTY or some
+other color-capable terminal emulator (newer versions of xterm will support
+color when using the ``xterm-color'' termcap entry).
+.Pp
+This product is currently at the end of its life cycle and will
+be replaced in FreeBSD 3.0 by the
+.Xr setup 1
+utility.
+.Sh RUNNING SCRIPTS
+.Nm
+may be either driven interactively through its various internal menus
+or run in batch mode, driven by an external script. Such a script may
+be loaded and executed in one of 3 ways:
+
+.Bl -tag -width Ds -compact
+.It Sy "LOAD_CONFIG_FILE"
+If
+.Nm
+is compiled with LOAD_CONFIG_FILE set in the environment
+(or in the Makefile) to some value, then that value will
+be used as the filename to automatically look for and load
+when
+.Nm
+starts up and with no user interaction required.
+This option is aimed primarily at large sites who wish to create a
+single prototype install for multiple machines with largely identical
+configurations and/or installation options.
+
+.It Sy "MAIN MENU"
+If
+.Nm
+is run interactively, that is to say in the default manner, it will
+bring up a main menu which contains a "load config file" option.
+Selecting this option will prompt for the name of a script file which
+it then will attempt to load from a DOS or UFS formatted floppy.
+
+.It Sy "COMMAND LINE"
+Each command line argument is treated as a script directive
+when
+.Nm
+is run in multi-user mode. Execution ends either by explicit request
+(e.g. calling the
+.Ar shutdown
+directive), upon reaching the end of the argument list or on error.
+.Pp
+For example:
+.nf
+
+/stand/sysinstall ftp=ftp:/ziggy/pub/ mediaSetFTP configPackages
+
+.fi
+Would initialize
+.Nm
+for FTP installation media (using the server `ziggy') and then
+bring up the package installation editor, exiting when finished.
+.El
+.Pp
+.Sh SCRIPT SYNTAX
+A script is a list of one or more directives, each directive taking
+the form of:
+
+.Ar var=value
+.Pp
+.Ar function
+.Pp
+or
+.Ar #somecomment
+
+Where
+.Ar var=value
+is the assignment of some internal
+.Nm
+variable, e.g. "ftpPass=FuNkYChiKn", and
+.Ar function
+is the name of an internal
+.Nm
+function, e.g. "mediaSetFTP", and
+.Ar #comment
+is a single-line comment for documentation purposes (ignored by
+sysinstall). Each directive must be by itself on a single line,
+functions taking their arguments by examining known variable names.
+This requires that you be sure to assign the relevant variables before
+calling a function which requires them. When and where a function
+depends on the settings of one or more variables will be noted in the
+following table:
+
+.Pp
+\fBFunction Glossary:\fR
+.Pp
+.Bl -tag -width indent
+.It configAnonFTP
+Invoke the Anonymous FTP configuration menu.
+.Pp
+\fBVariables:\fR None
+.It configRouter
+Select which routing daemon you wish to use, potentially
+loading any required 3rd-party routing daemons as necessary.
+.Pp
+\fBVariables:\fR
+.Bl -tag -width indent
+.It router
+can be set to the name of the desired routing daemon,
+e.g. ``routed'' or ``gated'', otherwise it is prompted for.
+.El
+.It configNFSServer
+Configure host as an NFS server.
+.Pp
+\fBVariables:\fR None
+.It configNTP
+Configure host as a user of the Network Time Protocol.
+.Pp
+\fBVariables:\fR
+.Bl -tag -width indent
+.It ntpdate_flags
+The flags to
+.Xr ntpdate 8 ,
+that is to say the name of the server to sync from.
+.El
+.It configPCNFSD
+Configure host to support PC NFS.
+.Pp
+\fBVariables:\fR
+.Bl -tag -width indent
+.It pcnfsd_pkg
+The name of the PCNFSD package to load if necessary (defaults to hard coded
+version).
+.El
+.It configPackages
+Bring up the interactive package management menu.
+.Pp
+\fBVariables:\fR None
+.It configRegister
+Register the user with the FreeBSD counter.
+.Pp
+\fBVariables:\fR None
+.It configUsers
+Add users and/or groups to the system.
+.Pp
+\fBVariables:\fR None
+.It configXEnvironment
+Configure the X display subsystem.
+.Pp
+\fBVariables:\fR None
+.It diskPartitionEditor
+Invokes the disk partition (MBR) editor.
+.Pp
+\fBVariables:\fR
+.Bl -tag -width findx
+.It geometry
+The disk geometry, as a cyls/heads/sectors formatted string. Default: no
+change to geometry.
+.It partition
+Set to disk partitioning type or size, its value being
+.Ar free
+in order to use only remaining free space for FreeBSD,
+.Ar all
+to use the entire disk for FreeBSD but maintain a proper partition
+table,
+.Ar existing
+to use an existing FreeBSD partition (first found),
+.Ar exclusive
+to use the disk in ``dangerously dedicated'' mode or, finally,
+.Ar somenumber
+to allocate
+.Ar somenumber
+blocks of available free space to a new FreeBSD partition.
+Default: Interactive mode.
+.It bootManager
+is set to one of
+.Ar boot
+to signify the installation of a boot manager,
+.Ar standard
+to signify installation of a "standard" non-boot MGR DOS
+MBR or
+.Ar none
+to indicate that no change to the boot manager is desired.
+Default: none.
+.El
+.Pp
+Note: Nothing is actually written to disk by this function, a explicit call to
+.Ar diskPartitionWrite
+being required for that to happen.
+.It diskPartitionWrite
+Causes any pending MBR changes (typically from the
+.Ar diskPartitionEditor
+function) to be written out.
+.Pp
+\fBVariables:\fR None
+.It diskLabelEditor
+Invokes the disk label editor. This is a bit trickier from a script
+since you need to essentially label everything inside each FreeBSD
+(type 0xA5) partition created by the
+.Ar diskPartitionEditor
+function, and that requires knowing a few rules about how things are
+laid out. When creating a script to automatically allocate disk space
+and partition it up, it is suggested that you first perform the
+installation interactively at least once and take careful notes as to
+what the slice names will be, then and only then hardwiring them into
+the script.
+.Pp
+For example, let's say you have a SCSI disk on which you've created a new
+FreeBSD partition in slice 2 (your DOS partition residing in slice 1).
+The slice name would be
+.Ar sd0s2
+for the whole FreeBSD partition (
+.Ar sd0s1
+being your DOS primary
+partition). Now let's further assume that you have 500MB in this
+partition and you want to sub-partition that space into root, swap,
+var and usr file systems for FreeBSD. Your invocation of the
+.Ar diskLabelEditor
+function might involve setting the following variables:
+.Bl -tag -width findx
+.It Li "sd0s2-1=ufs 40960 /"
+A 20MB root file system (all sizes are in 512 byte blocks).
+.It Li "sd0s2-2=swap 131072 /"
+A 64MB swap partition.
+.It Li "sd0s2-3=ufs 204800 /var"
+A 100MB /var file system.
+.It Li "sd0s2-4=ufs 0 /usr"
+With the balance of free space (around 316MB) going to the /usr
+file system.
+.El
+
+One can also use the
+.Ar diskLabelEditor
+for mounting or erasing existing partitions as well as creating new
+ones. Using the previous example again, let's say that we also wanted
+to mount our DOS partition and make sure that an
+.Pa /etc/fstab
+entry is created for it in the new installation. Before calling the
+.Ar diskLabelEditor
+function, we simply add an additional line:
+.nf
+ sd0s1=/dos_c N
+
+.fi
+before the call. This tells the label editor that you want to mount
+the first slice on
+.Pa /dos_c
+and not to attempt to newfs it (not that
+.Nm
+would attempt this for a DOS partition in any case, but it could just
+as easily be an existing UFS partition being named here and the 2nd
+field is non-optional).
+.Pp
+Note: No file system data is actually written to disk until an
+explicit call to
+.Ar diskLabelCommit
+is made.
+.It diskLabelCommit
+Writes out all pending disklabel information and creates and/or mounts any
+file systems which have requests pending from the
+.Ar diskLabelEditor
+function.
+.Pp
+\fBVariables:\fR None
+.It distReset
+Resets all selected distributions to the empty set (no distributions selected).
+.Pp
+\fBVariables:\fR None
+.It distSetCustom
+Allows the selection of a custom distribution set (e.g. not just on of the
+existing "canned" sets) with no user interaction.
+\fBVariables:\fR
+.Bl -tag -width indent
+.It dists
+List of distributions to load. Possible distribution values are:
+.Bl -tag -width indent
+.It Li bin
+The base binary distribution.
+.It Li doc
+Miscellaneous documentation
+.It Li games
+Games
+.It Li manpages
+Manual pages (unformatted)
+.It Li catpages
+Pre-formatted manual pages
+.It Li proflibs
+Profiled libraries for developers.
+.It Li dict
+Dictionary information (for tools like spell).
+.It Li info
+GNU info files and other extra docs.
+.It Li des
+DES encryption binaries and libraries.
+.It Li compat1x
+Compatibility with FreeBSD 1.x
+.It Li compat20
+Compatibility with FreeBSD 2.0
+.It Li compat21
+Compatibility with FreeBSD 2.1
+.It Li ports
+The ports collection.
+.It Li krb
+Kerberos binaries.
+.It Li ssecure
+/usr/src/secure
+.It Li sebones
+/usr/src/eBones
+.It Li sbase
+/usr/src/[top level files]
+.It Li scontrib
+/usr/src/contrib
+.It Li sgnu
+/usr/src/gnu
+.It Li setc
+/usr/src/etc
+.It Li sgames
+/usr/src/games
+.It Li sinclude
+/usr/src/include
+.It Li slib
+/usr/src/lib
+.It Li slibexec
+/usr/src/libexec
+.It Li slkm
+/usr/src/lkm
+.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 X331bin
+XFree86 3.3.1 binaries.
+.It Li X331cfg
+XFree86 3.3.1 configuration files.
+.It Li X331doc
+XFree86 3.3.1 documentation.
+.It Li X331html
+XFree86 3.3.1 HTML documentation.
+.It Li X331lib
+XFree86 3.3.1 libraries.
+.It Li X331lk98
+XFree86 3.3.1 server link-kit for PC98 machines.
+.It Li X331lkit
+XFree86 3.3.1 server link-kit for standard machines.
+.It Li X331man
+XFree86 3.3.1 manual pages.
+.It Li X331prog
+XFree86 3.3.1 programmer's distribution.
+.It Li X331ps
+XFree86 3.3.1 postscript documentation.
+.It Li X331set
+XFree86 3.3.1 graphical setup tool.
+.It Li X3318514
+XFree86 3.3.1 8514 server.
+.It Li X3319480
+XFree86 3.3.1 PC98 8-bit (256 color) PEGC-480 server.
+.It Li X3319EGC
+XFree86 3.3.1 PC98 4-bit (16 color) EGC server.
+.It Li X3319GA9
+XFree86 3.3.1 PC98 GA-968V4/PCI (S3 968) server.
+.It Li X3319GAN
+XFree86 3.3.1 PC98 GANB-WAP (cirrus) server.
+.It Li X3319LPW
+XFree86 3.3.1 PC98 PowerWindowLB (S3) server.
+.It Li X3319NKV
+XFree86 3.3.1 PC98 NKV-NEC (cirrus) server.
+.It Li X3319NS3
+XFree86 3.3.1 PC98 NEC (S3) server.
+.It Li X3319SPW
+XFree86 3.3.1 PC98 SKB-PowerWindow (S3) server.
+.It Li X3319TGU
+XFree86 3.3.1 PC98 Cyber9320 and TGUI9680 server.
+.It Li X3319WEP
+XFree86 3.3.1 PC98 WAB-EP (cirrus) server.
+.It Li X3319WS
+XFree86 3.3.1 PC98 WABS (cirrus) server.
+.It Li X3319WSN
+XFree86 3.3.1 PC98 WSN-A2F (cirrus) server.
+.It Li X331AGX
+XFree86 3.3.1 8 bit AGX server.
+.It Li X331I128
+XFree86 3.3.1 #9 Imagine I128 server.
+.It Li X331Ma8
+XFree86 3.3.1 ATI Mach8 server.
+.It Li X331Ma32
+XFree86 3.3.1 ATI Mach32 server.
+.It Li X331Ma64
+XFree86 3.3.1 ATI Mach64 server.
+.It Li X331Mono
+XFree86 3.3.1 monochrome server.
+.It Li X331P9K
+XFree86 3.3.1 P9000 server.
+.It Li X331S3
+XFree86 3.3.1 S3 server.
+.It Li X331S3V
+XFree86 3.3.1 S3 Virge server.
+.It Li X331SVGA
+XFree86 3.3.1 SVGA server.
+.It Li X331VG16
+XFree86 3.3.1 VGA16 server.
+.It Li X331W32
+XFree86 3.3.1 ET4000/W32, /W32i and /W32p server.
+.It Li X331nest
+XFree86 3.3.1 nested X server.
+.It Li X331vfb
+XFree86 3.3.1 virtual frame-buffer X server.
+.It Li X331fnts
+XFree86 3.3.1 base font set.
+.It Li X331f100
+XFree86 3.3.1 100DPI font set.
+.It Li X331fcyr
+XFree86 3.3.1 Cyrillic font set.
+.It Li X331fscl
+XFree86 3.3.1 scalable font set.
+.It Li X331fnon
+XFree86 3.3.1 non-english font set.
+.It Li X331fsrv
+XFree86 3.3.1 font server.
+.El
+.It distSetDeveloper
+Selects the standard Developer's distribution set.
+.Pp
+\fBVariables:\fR None
+.It distSetXDeveloper
+Selects the standard X Developer's distribution set.
+.Pp
+\fBVariables:\fR None
+.It distSetKernDeveloper
+Selects the standard kernel Developer's distribution set.
+.Pp
+\fBVariables:\fR None
+.It distSetUser
+Selects the standard user distribution set.
+.Pp
+\fBVariables:\fR None
+.It distSetXUser
+Selects the standard X user's distribution set.
+.Pp
+\fBVariables:\fR None
+.It distSetMinimum
+Selects the very minimum distribution set.
+.Pp
+\fBVariables:\fR None
+.It distSetEverything
+Selects the full whack - all available distributions.
+.Pp
+\fBVariables:\fR None
+.It distSetDES
+Interactively select DES subcomponents.
+.Pp
+\fBVariables:\fR None
+.It distSetSrc
+Interactively select source subcomponents.
+.Pp
+\fBVariables:\fR None
+.It distSetXF86
+Interactively select XFree86 3.3.1 subcomponents.
+.Pp
+\fBVariables:\fR None
+.It distExtractAll
+Install all currently selected distributions (requires that
+media device also be selected).
+.Pp
+\fBVariables:\fR None
+.It docBrowser
+Install (if necessary) an HTML documentation browser and go to the
+HTML documentation submenu.
+.Pp
+\fBVariables:\fR
+.Bl -tag -width indent
+.It browserPackage
+The name of the browser package to try and install as necessary.
+Defaults to latest lynx package.
+.It browserBinary
+The name of the browser binary itself (if overriding the
+.Ar browserPackage
+variable). Defaults to lynx.
+.El
+.It installCommit
+.Pp
+Commit any and all pending changes to disk. This function
+is essentially shorthand for a number of more granular "commit"
+functions.
+\fBVariables:\fR None
+.It installExpress
+Start an "express" installation, asking few questions of
+the user.
+.Pp
+\fBVariables:\fR None
+.It installNovice
+Start a "novice" installation, the most user-friendly
+installation type available.
+.Pp
+\fBVariables:\fR None
+.It installUpgrade
+Start an upgrade installation.
+.Pp
+\fBVariables:\fR None
+.It installFixitHoloShell
+Start up the "emergency holographic shell" over on VTY4
+if running as init.
+.Pp
+\fBVariables:\fR None
+.It installFixitCDROM
+Go into "fixit" mode, assuming a live file system CDROM
+currently in the drive.
+.Pp
+\fBVariables:\fR None
+.It installFixitFloppy
+Go into "fixit" mode, assuming an available fixit floppy
+disk (user will be prompted for it).
+.Pp
+\fBVariables:\fR None
+.It installFilesystems
+Do just the file system initialization part of an install.
+.Pp
+\fBVariables:\fR None
+.It installVarDefaults
+Initialize all variables to their defaults, overriding any
+previous settings.
+.Pp
+\fBVariables:\fR None
+.It loadConfig
+Sort of like an #include statement, it allows you to load one
+configuration file from another.
+.Pp
+\fBVariables:\fR
+.Bl -tag -width indent
+.It file
+The fully pathname of the file to load.
+.El
+.It mediaSetCDROM
+Select a FreeBSD CDROM as the installation media.
+.Pp
+\fBVariables:\fR None
+.It mediaSetFloppy
+Select a pre-made floppy installation set as the installation media.
+.Pp
+\fBVariables:\fR None
+.It mediaSetDOS
+Select an existing DOS primary partition as the installation media.
+The first primary partition found is used (e.g. C:).
+.Pp
+\fBVariables:\fR None
+.It mediaSetTape
+Select a tape device as the installation media.
+.Pp
+\fBVariables:\fR None
+.It mediaSetFTP
+Select an FTP site as the installation media.
+.Pp
+\fBVariables:\fR
+.Bl -tag -width indent
+.It hostname
+The name of the host being installed (non-optional).
+.It domainname
+The domain name of the host being installed (optional).
+.It defaultrouter
+The default router for this host (non-optional).
+.It netDev
+Which host interface to use (
+.Ar ed0
+or
+.Ar ep0 ,
+for example. Non-optional).
+.It netInteractive
+If set, bring up the interactive network setup form even
+if all relevant configuration variables are already set (optional).
+.It ipaddr
+The IP address for the selected host interface (non-optional).
+.It netmask
+The netmask for the selected host interface (non-optional).
+.It ftp
+The fully qualified URL of the FTP site containing the FreeBSD
+distribution you're interested in, e.g.
+.Ar ftp://ftp.freebsd.org/pub/FreeBSD/ .
+.El
+.It mediaSetFTPActive
+Alias for
+.Ar mediaSetFTP
+using "active" FTP transfer mode.
+.Pp
+\fBVariables:\fR Same as for
+.Ar mediaSetFTP .
+.It mediaSetFTPPassive
+Alias for
+.Ar mediaSetFTP
+using "passive" FTP transfer mode.
+.Pp
+\fBVariables:\fR Same as for
+.Ar mediaSetFTP .
+.It mediaSetUFS
+Select an existing UFS partition (mounted with the label editor) as
+the installation media.
+.Pp
+\fBVariables:\fR
+.Bl -tag -width indent
+.It ufs
+full /path to directory containing the FreeBSD distribution you're
+interested in.
+.El
+.It mediaSetNFS
+.Pp
+\fBVariables:\fR
+.Bl -tag -width indent
+.It hostname
+The name of the host being installed (non-optional).
+.It domainname
+The domain name of the host being installed (optional).
+.It defaultrouter
+The default router for this host (non-optional).
+.It netDev
+Which host interface to use (
+.Ar ed0
+or
+.Ar ep0 ,
+for example. Non-optional).
+.It netInteractive
+If set, bring up the interactive network setup form even
+if all relevant configuration variables are already set (optional).
+.It ipaddr
+The IP address for the selected host interface (non-optional).
+.It netmask
+The netmask for the selected host interface (non-optional).
+.It nfs
+full hostname:/path specification for directory containing
+the FreeBSD distribution you're interested in.
+.El
+.It mediaSetFTPUserPass
+.Pp
+\fBVariables:\fR
+.Bl -tag -width indent
+.It ftpUser
+The username to log in as on the ftp server site.
+Default: ftp
+.It ftpPass
+The password to use for this username on the ftp
+server site.
+Default: user@host
+.El
+.It mediaSetCPIOVerbosity
+.Pp
+\fBVariables:\fR
+.Bl -tag -width indent
+.It cpioVerbose
+Can be used to set the verbosity of cpio extractions to low, medium or
+high.
+.El
+.It mediaGetType
+Interactively get the user to specify some type of media.
+.Pp
+\fBVariables:\fR None
+.It optionsEditor
+Invoke the interactive options editor.
+.Pp
+\fBVariables:\fR None
+.It register
+Bring up the FreeBSD registration form.
+.Pp
+\fBVariables:\fR None
+.It packageAdd
+Try to fetch and add a package to the system (requires
+that a media type be set),
+.Pp
+\fBVariables:\fR
+.Bl -tag -width indent
+.It package
+The name of the package to add, e.g. bash-1.14.7 or ncftp-2.4.2.
+.El
+.It addGroup
+Invoke the interactive group editor.
+.Pp
+\fBVariables:\fR None
+.It addUser
+Invoke the interactive user editor.
+.Pp
+\fBVariables:\fR None
+.It shutdown
+Stop the script and terminate sysinstall.
+.Pp
+\fBVariables:\fR None
+.It system
+Execute an arbitrary command with
+.Xr system 3
+.Pp
+\fBVariables:\fR
+.Bl -tag -width indent
+.It command
+The name of the command to execute. When running
+from a boot floppy, very minimal expectations should
+be made as to what's available until/unless a relatively
+full system installation has just been done.
+.El
+.El
+.Sh FILES
+This utility may edit the contents of
+.Pa /etc/rc.conf ,
+.Pa /etc/hosts ,
+and
+.Pa /etc/resolv.conf
+as necessary to reflect changes in the network configuration.
+.Sh SEE ALSO
+If you have a reasonably complete source tree online, take
+a look at
+.Pa /usr/src/release/sysinstall/install.cfg
+for a sample installation script.
+.Sh BUGS
+This utility is a prototype which lasted approximately 2 years past
+its expiration date and is greatly in need of death.
+.Sh AUTHOR
+Jordan K. Hubbard <jkh@FreeBSD.org>
+.Sh HISTORY
+This version of
+.Nm
+first appeared in
+.Fx 2.0 .
diff --git a/usr.sbin/sade/sade.h b/usr.sbin/sade/sade.h
new file mode 100644
index 0000000..01d30d5
--- /dev/null
+++ b/usr.sbin/sade/sade.h
@@ -0,0 +1,726 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last attempt in the `sysinstall' line, the next
+ * generation being slated to essentially a complete rewrite.
+ *
+ * $Id: sysinstall.h,v 1.142 1997/10/12 16:21:19 jkh Exp $
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#ifndef _SYSINSTALL_H_INCLUDE
+#define _SYSINSTALL_H_INCLUDE
+
+#include <sys/types.h>
+#include <sys/wait.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"
+#include "version.h"
+
+/*** Defines ***/
+
+/* Different packages we depend on - update this when package version change! */
+#define PACKAGE_GATED "gated-3.5b3"
+#define PACKAGE_NETCON "commerce/netcon/bsd61"
+#define PACKAGE_PCNFSD "pcnfsd-93.02.16"
+#define PACKAGE_LYNX "lynx-2.7.1"
+
+/* device limits */
+#define DEV_NAME_MAX 64 /* The maximum length of a device name */
+#define DEV_MAX 100 /* The maximum number of devices we'll deal with */
+#define INTERFACE_MAX 50 /* Maximum number of network interfaces we'll deal with */
+#define IO_ERROR -2 /* Status code for I/O error rather than normal EOF */
+
+/* Number of seconds to wait for data to come off even the slowest media */
+#define MEDIA_TIMEOUT 300
+
+/*
+ * I make some pretty gross assumptions about having a max of 50 chunks
+ * total - 8 slices and 42 partitions. I can't easily display many more
+ * than that on the screen at once!
+ *
+ * For 2.1 I'll revisit this and try to make it more dynamic, but since
+ * this will catch 99.99% of all possible cases, I'm not too worried.
+ */
+#define MAX_CHUNKS 40
+
+/* Internal environment variable names */
+#define DISK_PARTITIONED "_diskPartitioned"
+#define DISK_LABELLED "_diskLabelled"
+#define DISK_SELECTED "_diskSelected"
+#define SYSTEM_STATE "_systemState"
+#define RUNNING_ON_ROOT "_runningOnRoot"
+#define TCP_CONFIGURED "_tcpConfigured"
+
+/* Ones that can be tweaked from config files */
+#define VAR_BLANKTIME "blanktime"
+#define VAR_BOOTMGR "bootManager"
+#define VAR_BROWSER_BINARY "browserBinary"
+#define VAR_BROWSER_PACKAGE "browserPackage"
+#define VAR_CPIO_VERBOSITY "cpioVerbose"
+#define VAR_DEBUG "debug"
+#define VAR_DISK "disk"
+#define VAR_DISTS "dists"
+#define VAR_DIST_MAIN "distMain"
+#define VAR_DIST_DES "distDES"
+#define VAR_DIST_SRC "distSRC"
+#define VAR_DIST_X11 "distX11"
+#define VAR_DIST_XSERVER "distXserver"
+#define VAR_DIST_XFONTS "distXfonts"
+#define VAR_DEDICATE_DISK "dedicateDisk"
+#define VAR_DOMAINNAME "domainname"
+#define VAR_EDITOR "editor"
+#define VAR_EXTRAS "ifconfig_"
+#define VAR_COMMAND "command"
+#define VAR_CONFIG_FILE "configFile"
+#define VAR_FTP_DIR "ftpDirectory"
+#define VAR_FTP_PASS "ftpPass"
+#define VAR_FTP_PATH "ftp"
+#define VAR_FTP_PORT "ftpPort"
+#define VAR_FTP_STATE "ftpState"
+#define VAR_FTP_USER "ftpUser"
+#define VAR_FTP_HOST "ftpHost"
+#define VAR_GATED_PKG "gated_pkg"
+#define VAR_GATEWAY "defaultrouter"
+#define VAR_GEOMETRY "geometry"
+#define VAR_HOSTNAME "hostname"
+#define VAR_IFCONFIG "ifconfig_"
+#define VAR_INTERFACES "network_interfaces"
+#define VAR_INSTALL_CFG "installConfig"
+#define VAR_INSTALL_ROOT "installRoot"
+#define VAR_IPADDR "ipaddr"
+#define VAR_KEYMAP "keymap"
+#define VAR_LABEL "label"
+#define VAR_LABEL_COUNT "labelCount"
+#define VAR_MEDIA_TYPE "mediaType"
+#define VAR_MEDIA_TIMEOUT "MEDIA_TIMEOUT"
+#define VAR_NAMESERVER "nameserver"
+#define VAR_NETINTERACTIVE "netInteractive"
+#define VAR_NETMASK "netmask"
+#define VAR_NETWORK_DEVICE "netDev"
+#define VAR_NFS_PATH "nfs"
+#define VAR_NFS_HOST "nfsHost"
+#define VAR_NFS_SECURE "nfsSecure"
+#define VAR_NFS_SERVER "nfs_server_enable"
+#define VAR_NO_CONFIRM "noConfirm"
+#define VAR_NO_ERROR "noError"
+#define VAR_NO_WARN "noWarn"
+#define VAR_NONINTERACTIVE "nonInteractive"
+#define VAR_NOVELL "novell"
+#define VAR_NTPDATE_FLAGS "ntpdate_flags"
+#define VAR_PACKAGE "package"
+#define VAR_PARTITION "partition"
+#define VAR_PCNFSD "pcnfsd"
+#define VAR_PCNFSD_PKG "pcnfsd_pkg"
+#define VAR_PKG_TMPDIR "PKG_TMPDIR"
+#define VAR_PORTS_PATH "ports"
+#define VAR_RELNAME "releaseName"
+#define VAR_ROOT_SIZE "rootSize"
+#define VAR_ROUTER "router"
+#define VAR_ROUTER_ENABLE "router_enable"
+#define VAR_ROUTERFLAGS "routerflags"
+#define VAR_SERIAL_SPEED "serialSpeed"
+#define VAR_SLOW_ETHER "slowEthernetCard"
+#define VAR_SWAP_SIZE "swapSize"
+#define VAR_TAPE_BLOCKSIZE "tapeBlocksize"
+#define VAR_UFS_PATH "ufs"
+#define VAR_USR_SIZE "usrSize"
+#define VAR_VAR_SIZE "varSize"
+#define VAR_XF86_CONFIG "xf86config"
+
+#define DEFAULT_TAPE_BLOCKSIZE "20"
+
+/* One MB worth of blocks */
+#define ONE_MEG 2048
+
+/* Which selection attributes to use */
+#define ATTR_SELECTED (ColorDisplay ? item_selected_attr : item_attr)
+#define ATTR_TITLE button_active_attr
+
+/* Handy strncpy() macro */
+#define SAFE_STRCPY(to, from) sstrncpy((to), (from), sizeof (to) - 1)
+
+/*** Types ***/
+typedef unsigned int Boolean;
+typedef struct disk Disk;
+typedef struct chunk Chunk;
+
+/* Bitfields for menu options */
+#define DMENU_NORMAL_TYPE 0x1 /* Normal dialog menu */
+#define DMENU_RADIO_TYPE 0x2 /* Radio dialog menu */
+#define DMENU_CHECKLIST_TYPE 0x4 /* Multiple choice menu */
+#define DMENU_SELECTION_RETURNS 0x8 /* Immediate return on item selection */
+
+typedef struct _dmenu {
+ int type; /* What sort of menu we are */
+ char *title; /* Our title */
+ char *prompt; /* Our prompt */
+ char *helpline; /* Line of help at bottom */
+ char *helpfile; /* Help file for "F1" */
+ dialogMenuItem items[0]; /* Array of menu items */
+} DMenu;
+
+/* An rc.conf variable */
+typedef struct _variable {
+ struct _variable *next;
+ char *name;
+ char *value;
+} 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;
+
+/* For attribs */
+#define MAX_ATTRIBS 200
+#define MAX_NAME 64
+#define MAX_VALUE 256
+
+typedef struct _attribs {
+ char name[MAX_NAME];
+ char value[MAX_VALUE];
+} Attribs;
+
+typedef enum {
+ DEVICE_TYPE_NONE,
+ DEVICE_TYPE_DISK,
+ DEVICE_TYPE_FLOPPY,
+ DEVICE_TYPE_FTP,
+ DEVICE_TYPE_NETWORK,
+ DEVICE_TYPE_CDROM,
+ DEVICE_TYPE_TAPE,
+ DEVICE_TYPE_DOS,
+ DEVICE_TYPE_UFS,
+ DEVICE_TYPE_NFS,
+ DEVICE_TYPE_ANY,
+} DeviceType;
+
+/* CDROM mount codes */
+#define CD_UNMOUNTED 0
+#define CD_ALREADY_MOUNTED 1
+#define CD_WE_MOUNTED_IT 2
+
+/* A "device" from sysinstall's point of view */
+typedef struct _device {
+ char name[DEV_NAME_MAX];
+ char *description;
+ char *devname;
+ DeviceType type;
+ Boolean enabled;
+ Boolean (*init)(struct _device *dev);
+ FILE * (*get)(struct _device *dev, char *file, Boolean probe);
+ void (*shutdown)(struct _device *dev);
+ void *private;
+ unsigned int flags;
+} Device;
+
+/* Some internal representations of partitions */
+typedef enum {
+ PART_NONE,
+ PART_SLICE,
+ PART_SWAP,
+ PART_FILESYSTEM,
+ PART_FAT,
+} PartType;
+
+/* The longest newfs command we'll hand to system() */
+#define NEWFS_CMD_MAX 256
+
+typedef struct _part_info {
+ Boolean newfs;
+ char mountpoint[FILENAME_MAX];
+ char newfs_cmd[NEWFS_CMD_MAX];
+} PartInfo;
+
+/* An option */
+typedef struct _opt {
+ char *name;
+ char *desc;
+ enum { OPT_IS_STRING, OPT_IS_INT, OPT_IS_FUNC, OPT_IS_VAR } type;
+ void *data;
+ void *aux;
+ char *(*check)();
+} Option;
+
+/* Weird index nodey things we use for keeping track of package information */
+typedef enum { PACKAGE, PLACE } node_type; /* Types of nodes */
+
+typedef struct _pkgnode { /* A node in the reconstructed hierarchy */
+ struct _pkgnode *next; /* My next sibling */
+ node_type type; /* What am I? */
+ char *name; /* My name */
+ char *desc; /* My description (Hook) */
+ struct _pkgnode *kids; /* My little children */
+ void *data; /* A place to hang my data */
+} PkgNode;
+typedef PkgNode *PkgNodePtr;
+
+/* A single package */
+typedef struct _indexEntry { /* A single entry in an INDEX file */
+ char *name; /* name */
+ char *path; /* full path to port */
+ char *prefix; /* port prefix */
+ char *comment; /* one line description */
+ char *descrfile; /* path to description file */
+ char *deps; /* packages this depends on */
+ char *maintainer; /* maintainer */
+} IndexEntry;
+typedef IndexEntry *IndexEntryPtr;
+
+typedef int (*commandFunc)(char *key, void *data);
+
+#define HOSTNAME_FIELD_LEN 128
+#define IPADDR_FIELD_LEN 16
+#define EXTRAS_FIELD_LEN 128
+
+/* This is the structure that Network devices carry around in their private, erm, structures */
+typedef struct _devPriv {
+ char ipaddr[IPADDR_FIELD_LEN];
+ char netmask[IPADDR_FIELD_LEN];
+ char extras[EXTRAS_FIELD_LEN];
+} DevInfo;
+
+
+/*** Externs ***/
+extern jmp_buf BailOut; /* Used to get the heck out */
+extern int DebugFD; /* Where diagnostic output goes */
+extern Boolean Fake; /* Don't actually modify anything - testing */
+extern Boolean SystemWasInstalled; /* Did we install it? */
+extern Boolean RunningAsInit; /* Are we running stand-alone? */
+extern Boolean DialogActive; /* Is the dialog() stuff up? */
+extern Boolean ColorDisplay; /* Are we on a color display? */
+extern Boolean OnVTY; /* On a syscons VTY? */
+extern Variable *VarHead; /* The head of the variable chain */
+extern Device *mediaDevice; /* Where we're getting our distribution from */
+extern unsigned int Dists; /* Which distributions we want */
+extern unsigned int DESDists; /* Which naughty distributions we want */
+extern unsigned int SrcDists; /* Which src distributions we want */
+extern unsigned int XF86Dists; /* Which XFree86 dists we want */
+extern unsigned int XF86ServerDists; /* The XFree86 servers we want */
+extern unsigned int XF86FontDists; /* The XFree86 fonts we want */
+extern int BootMgr; /* Which boot manager to use */
+extern int StatusLine; /* Where to print our status messages */
+extern DMenu MenuInitial; /* Initial installation menu */
+extern DMenu MenuFixit; /* Fixit repair menu */
+extern DMenu MenuMBRType; /* Type of MBR to write on the disk */
+extern DMenu MenuConfigure; /* Final configuration menu */
+extern DMenu MenuDocumentation; /* Documentation menu */
+extern DMenu MenuFTPOptions; /* FTP Installation options */
+extern DMenu MenuIndex; /* Index menu */
+extern DMenu MenuOptions; /* Installation options */
+extern DMenu MenuOptionsLanguage; /* Language options menu */
+extern DMenu MenuMedia; /* Media type menu */
+extern DMenu MenuMouse; /* Mouse type menu */
+extern DMenu MenuMediaCDROM; /* CDROM media menu */
+extern DMenu MenuMediaDOS; /* DOS media menu */
+extern DMenu MenuMediaFloppy; /* Floppy media menu */
+extern DMenu MenuMediaFTP; /* FTP media menu */
+extern DMenu MenuMediaTape; /* Tape media menu */
+extern DMenu MenuNetworkDevice; /* Network device menu */
+extern DMenu MenuNTP; /* NTP time server menu */
+extern DMenu MenuStartup; /* Startup services menu */
+extern DMenu MenuSyscons; /* System console configuration menu */
+extern DMenu MenuSysconsFont; /* System console font configuration menu */
+extern DMenu MenuSysconsKeymap; /* System console keymap configuration menu */
+extern DMenu MenuSysconsKeyrate; /* System console keyrate configuration menu */
+extern DMenu MenuSysconsSaver; /* System console saver configuration menu */
+extern DMenu MenuSysconsScrnmap; /* System console screenmap configuration menu */
+extern DMenu MenuNetworking; /* Network configuration menu */
+extern DMenu MenuInstallCustom; /* Custom Installation menu */
+extern DMenu MenuDistributions; /* Distribution menu */
+extern DMenu MenuSubDistributions; /* Custom distribution menu */
+extern DMenu MenuDESDistributions; /* DES distribution menu */
+extern DMenu MenuSrcDistributions; /* Source distribution menu */
+extern DMenu MenuXF86; /* XFree86 main menu */
+extern DMenu MenuXF86Select; /* XFree86 distribution selection menu */
+extern DMenu MenuXF86SelectCore; /* XFree86 core distribution menu */
+extern DMenu MenuXF86SelectServer; /* XFree86 server distribution menu */
+extern DMenu MenuXF86SelectPC98Server; /* XFree86 server distribution menu */
+extern DMenu MenuXF86SelectFonts; /* XFree86 font selection menu */
+extern DMenu MenuDiskDevices; /* Disk devices menu */
+extern DMenu MenuHTMLDoc; /* HTML Documentation menu */
+extern DMenu MenuUsermgmt; /* User management menu */
+extern DMenu MenuFixit; /* Fixit floppy/CDROM/shell menu */
+extern DMenu MenuXF86Config; /* Select XFree86 configuration type */
+
+/* Stuff from libdialog which isn't properly declared outside */
+extern void display_helpfile(void);
+extern void display_helpline(WINDOW *w, int y, int width);
+
+/*** Prototypes ***/
+
+/* anonFTP.c */
+extern int configAnonFTP(dialogMenuItem *self);
+
+/* attrs.c */
+extern char *attr_match(Attribs *attr, char *name);
+extern int attr_parse_file(Attribs *attr, char *file);
+extern int attr_parse(Attribs *attr, FILE *fp);
+
+/* cdrom.c */
+extern Boolean mediaInitCDROM(Device *dev);
+extern FILE *mediaGetCDROM(Device *dev, char *file, Boolean probe);
+extern void mediaShutdownCDROM(Device *dev);
+
+/* command.c */
+extern void command_clear(void);
+extern void command_sort(void);
+extern void command_execute(void);
+extern void command_shell_add(char *key, char *fmt, ...);
+extern void command_func_add(char *key, commandFunc func, void *data);
+
+/* config.c */
+extern int configFstab(void);
+extern void configEnvironmentRC_conf(char *config);
+extern void configEnvironmentResolv(char *config);
+extern void configRC_conf(char *config);
+extern int configRC(dialogMenuItem *self);
+extern int configRegister(dialogMenuItem *self);
+extern void configResolv(void);
+extern int configPackages(dialogMenuItem *self);
+extern int configSaver(dialogMenuItem *self);
+extern int configSaverTimeout(dialogMenuItem *self);
+extern int configNTP(dialogMenuItem *self);
+extern int configUsers(dialogMenuItem *self);
+extern int configXEnvironment(dialogMenuItem *self);
+extern int configRouter(dialogMenuItem *self);
+extern int configPCNFSD(dialogMenuItem *self);
+extern int configNFSServer(dialogMenuItem *self);
+extern int configWriteRC_conf(dialogMenuItem *self);
+#ifdef NETCON_EXTENTIONS
+extern int configNovell(dialogMenuItem *self);
+#endif
+
+/* 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 Device **deviceFind(char *name, DeviceType type);
+extern Device **deviceFindDescr(char *name, char *desc, DeviceType class);
+extern int deviceCount(Device **devs);
+extern Device *new_device(char *name);
+extern Device *deviceRegister(char *name, char *desc, char *devname, DeviceType type, Boolean enabled,
+ Boolean (*init)(Device *mediadev),
+ FILE * (*get)(Device *dev, char *file, Boolean probe),
+ void (*shutDown)(Device *mediadev),
+ void *private);
+extern Boolean dummyInit(Device *dev);
+extern FILE *dummyGet(Device *dev, char *dist, Boolean probe);
+extern void dummyShutdown(Device *dev);
+
+/* disks.c */
+extern int diskPartitionEditor(dialogMenuItem *self);
+extern int diskPartitionWrite(dialogMenuItem *self);
+extern int diskGetSelectCount(Device ***devs);
+extern void diskPartition(Device *dev);
+
+/* dispatch.c */
+extern int dispatchCommand(char *command);
+extern int dispatch_load_floppy(dialogMenuItem *self);
+extern int dispatch_load_file_int(int);
+extern int dispatch_load_file(dialogMenuItem *self);
+
+
+/* dist.c */
+extern int distReset(dialogMenuItem *self);
+extern int distConfig(dialogMenuItem *self);
+extern int distSetCustom(dialogMenuItem *self);
+extern int distSetDeveloper(dialogMenuItem *self);
+extern int distSetXDeveloper(dialogMenuItem *self);
+extern int distSetKernDeveloper(dialogMenuItem *self);
+extern int distSetUser(dialogMenuItem *self);
+extern int distSetXUser(dialogMenuItem *self);
+extern int distSetMinimum(dialogMenuItem *self);
+extern int distSetEverything(dialogMenuItem *self);
+extern int distSetDES(dialogMenuItem *self);
+extern int distSetSrc(dialogMenuItem *self);
+extern int distSetXF86(dialogMenuItem *self);
+extern int distExtractAll(dialogMenuItem *self);
+
+/* dmenu.c */
+extern int dmenuDisplayFile(dialogMenuItem *tmp);
+extern int dmenuSubmenu(dialogMenuItem *tmp);
+extern int dmenuSystemCommand(dialogMenuItem *tmp);
+extern int dmenuSystemCommandBox(dialogMenuItem *tmp);
+extern int dmenuExit(dialogMenuItem *tmp);
+extern int dmenuISetVariable(dialogMenuItem *tmp);
+extern int dmenuSetVariable(dialogMenuItem *tmp);
+extern int dmenuSetKmapVariable(dialogMenuItem *tmp);
+extern int dmenuSetVariables(dialogMenuItem *tmp);
+extern int dmenuToggleVariable(dialogMenuItem *tmp);
+extern int dmenuSetFlag(dialogMenuItem *tmp);
+extern int dmenuSetValue(dialogMenuItem *tmp);
+extern Boolean dmenuOpen(DMenu *menu, int *choice, int *scroll, int *curr, int *max, Boolean buttons);
+extern Boolean dmenuOpenSimple(DMenu *menu, Boolean buttons);
+extern int dmenuVarCheck(dialogMenuItem *item);
+extern int dmenuVarsCheck(dialogMenuItem *item);
+extern int dmenuFlagCheck(dialogMenuItem *item);
+extern int dmenuRadioCheck(dialogMenuItem *item);
+
+/* doc.c */
+extern int docBrowser(dialogMenuItem *self);
+extern int docShowDocument(dialogMenuItem *self);
+
+/* dos.c */
+extern Boolean mediaCloseDOS(Device *dev, FILE *fp);
+extern Boolean mediaInitDOS(Device *dev);
+extern FILE *mediaGetDOS(Device *dev, char *file, Boolean probe);
+extern void mediaShutdownDOS(Device *dev);
+
+/* floppy.c */
+extern int getRootFloppy(void);
+extern Boolean mediaInitFloppy(Device *dev);
+extern FILE *mediaGetFloppy(Device *dev, char *file, Boolean probe);
+extern void mediaShutdownFloppy(Device *dev);
+
+/* ftp_strat.c */
+extern Boolean mediaCloseFTP(Device *dev, FILE *fp);
+extern Boolean mediaInitFTP(Device *dev);
+extern FILE *mediaGetFTP(Device *dev, char *file, Boolean probe);
+extern void mediaShutdownFTP(Device *dev);
+
+/* globals.c */
+extern void globalsInit(void);
+
+/* index.c */
+int index_read(FILE *fp, PkgNodePtr papa);
+int index_menu(PkgNodePtr top, PkgNodePtr plist, int *pos, int *scroll);
+void index_init(PkgNodePtr top, PkgNodePtr plist);
+void index_node_free(PkgNodePtr top, PkgNodePtr plist);
+void index_sort(PkgNodePtr top);
+void index_print(PkgNodePtr top, int level);
+int index_extract(Device *dev, PkgNodePtr top, PkgNodePtr plist);
+
+/* install.c */
+extern Boolean checkLabels(Boolean whinge, Chunk **rdev, Chunk **sdev, Chunk **udev, Chunk **vdev);
+extern int installCommit(dialogMenuItem *self);
+extern int installCustomCommit(dialogMenuItem *self);
+extern int installExpress(dialogMenuItem *self);
+extern int installNovice(dialogMenuItem *self);
+extern int installFixitHoloShell(dialogMenuItem *self);
+extern int installFixitCDROM(dialogMenuItem *self);
+extern int installFixitFloppy(dialogMenuItem *self);
+extern int installFixup(dialogMenuItem *self);
+extern int installUpgrade(dialogMenuItem *self);
+extern int installFilesystems(dialogMenuItem *self);
+extern int installVarDefaults(dialogMenuItem *self);
+extern void installEnvironment(void);
+extern Boolean copySelf(void);
+
+/* keymap.c */
+extern int loadKeymap(const char *lang);
+
+/* label.c */
+extern int diskLabelEditor(dialogMenuItem *self);
+extern int diskLabelCommit(dialogMenuItem *self);
+
+/* lndir.c */
+extern int lndir(char *from, char *to);
+
+/* makedevs.c (auto-generated) */
+extern const char termcap_ansi[];
+extern const char termcap_vt100[];
+extern const char termcap_cons25[];
+extern const char termcap_cons25_m[];
+extern const char termcap_cons25r[];
+extern const char termcap_cons25r_m[];
+extern const char termcap_cons25l1[];
+extern const char termcap_cons25l1_m[];
+extern const u_char font_iso_8x16[];
+extern const u_char font_cp850_8x16[];
+extern const u_char font_cp866_8x16[];
+extern const u_char koi8_r2cp866[];
+extern u_char default_scrnmap[];
+
+/* media.c */
+extern char *cpioVerbosity(void);
+extern void mediaClose(void);
+extern int mediaTimeout(void);
+extern int mediaSetCDROM(dialogMenuItem *self);
+extern int mediaSetFloppy(dialogMenuItem *self);
+extern int mediaSetDOS(dialogMenuItem *self);
+extern int mediaSetTape(dialogMenuItem *self);
+extern int mediaSetFTP(dialogMenuItem *self);
+extern int mediaSetFTPActive(dialogMenuItem *self);
+extern int mediaSetFTPPassive(dialogMenuItem *self);
+extern int mediaSetUFS(dialogMenuItem *self);
+extern int mediaSetNFS(dialogMenuItem *self);
+extern int mediaSetFTPUserPass(dialogMenuItem *self);
+extern int mediaSetCPIOVerbosity(dialogMenuItem *self);
+extern int mediaGetType(dialogMenuItem *self);
+extern Boolean mediaExtractDist(char *dir, char *dist, FILE *fp);
+extern Boolean mediaExtractDistBegin(char *dir, int *fd, int *zpid, int *cpic);
+extern Boolean mediaExtractDistEnd(int zpid, int cpid);
+extern Boolean mediaVerify(void);
+
+/* 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);
+
+/* msg.c */
+extern Boolean isDebug(void);
+extern void msgInfo(char *fmt, ...);
+extern void msgYap(char *fmt, ...);
+extern void msgWarn(char *fmt, ...);
+extern void msgDebug(char *fmt, ...);
+extern void msgError(char *fmt, ...);
+extern void msgFatal(char *fmt, ...);
+extern void msgConfirm(char *fmt, ...);
+extern void msgNotify(char *fmt, ...);
+extern void msgWeHaveOutput(char *fmt, ...);
+extern int msgYesNo(char *fmt, ...);
+extern char *msgGetInput(char *buf, char *fmt, ...);
+extern int msgSimpleConfirm(char *);
+extern int msgSimpleNotify(char *);
+
+/* network.c */
+extern Boolean mediaInitNetwork(Device *dev);
+extern void mediaShutdownNetwork(Device *dev);
+
+/* nfs.c */
+extern Boolean mediaInitNFS(Device *dev);
+extern FILE *mediaGetNFS(Device *dev, char *file, Boolean probe);
+extern void mediaShutdownNFS(Device *dev);
+
+/* options.c */
+extern int optionsEditor(dialogMenuItem *self);
+
+/* package.c */
+extern int packageAdd(dialogMenuItem *self);
+extern int package_add(char *name);
+extern int package_extract(Device *dev, char *name, Boolean depended);
+extern Boolean package_exists(char *name);
+
+/* register.c */
+extern int registerOpenDialog(void);
+
+/* system.c */
+extern void systemInitialize(int argc, char **argv);
+extern void systemShutdown(int status);
+extern int execExecute(char *cmd, char *name);
+extern int systemExecute(char *cmd);
+extern int systemDisplayHelp(char *file);
+extern char *systemHelpFile(char *file, char *buf);
+extern void systemChangeFont(const u_char font[]);
+extern void systemChangeLang(char *lang);
+extern void systemChangeTerminal(char *color, const u_char c_termcap[], char *mono, const u_char m_termcap[]);
+extern void systemChangeScreenmap(const u_char newmap[]);
+extern void systemCreateHoloshell(void);
+extern int vsystem(char *fmt, ...);
+
+/* tape.c */
+extern char *mediaTapeBlocksize(void);
+extern Boolean mediaInitTape(Device *dev);
+extern FILE *mediaGetTape(Device *dev, char *file, Boolean probe);
+extern void mediaShutdownTape(Device *dev);
+
+/* tcpip.c */
+extern int tcpOpenDialog(Device *dev);
+extern int tcpMenuSelect(dialogMenuItem *self);
+extern Device *tcpDeviceSelect(void);
+
+/* termcap.c */
+extern int set_termcap(void);
+
+/* ufs.c */
+extern void mediaShutdownUFS(Device *dev);
+extern Boolean mediaInitUFS(Device *dev);
+extern FILE *mediaGetUFS(Device *dev, char *file, Boolean probe);
+
+/* user.c */
+extern int userAddGroup(dialogMenuItem *self);
+extern int userAddUser(dialogMenuItem *self);
+
+/* variable.c */
+extern void variable_set(char *var);
+extern void variable_set2(char *name, char *value);
+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);
+extern int variable_check(char *data);
+
+/* wizard.c */
+extern void slice_wizard(Disk *d);
+
+#endif
+/* _SYSINSTALL_H_INCLUDE */
diff --git a/usr.sbin/sade/system.c b/usr.sbin/sade/system.c
new file mode 100644
index 0000000..61a27c4e
--- /dev/null
+++ b/usr.sbin/sade/system.c
@@ -0,0 +1,376 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last program in the `sysinstall' line - the next
+ * generation being essentially a complete rewrite.
+ *
+ * $Id: system.c,v 1.81 1997/05/27 18:56:03 jkh Exp $
+ *
+ * Jordan Hubbard
+ *
+ * My contributions are in the public domain.
+ *
+ * Parts of this file are also blatently stolen from Poul-Henning Kamp's
+ * previous version of sysinstall, and as such fall under his "BEERWARE license"
+ * so buy him a beer if you like it! Buy him a beer for me, too!
+ * Heck, get him completely drunk and send me pictures! :-)
+ */
+
+#include "sysinstall.h"
+#include <signal.h>
+#include <sys/reboot.h>
+#include <machine/console.h>
+#include <sys/fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/wait.h>
+
+
+/* Where we stick our temporary expanded doc file */
+#define DOC_TMP_DIR "/tmp"
+#define DOC_TMP_FILE "/tmp/doc.tmp"
+
+static pid_t ehs_pid;
+
+/*
+ * Handle interrupt signals - this probably won't work in all cases
+ * due to our having bogotified the internal state of dialog or curses,
+ * but we'll give it a try.
+ */
+static void
+handle_intr(int sig)
+{
+ WINDOW *save = savescr();
+
+ if (!msgYesNo("Are you sure you want to abort the installation?"))
+ systemShutdown(-1);
+ else
+ restorescr(save);
+}
+
+/* Expand a file into a convenient location, nuking it each time */
+static char *
+expand(char *fname)
+{
+ Mkdir(DOC_TMP_DIR);
+ unlink(DOC_TMP_FILE);
+ if (!file_readable(fname) || vsystem("gzip -c -d %s > %s", fname, DOC_TMP_FILE))
+ return NULL;
+ return DOC_TMP_FILE;
+}
+
+/* Initialize system defaults */
+void
+systemInitialize(int argc, char **argv)
+{
+ int i;
+
+ signal(SIGINT, SIG_IGN);
+ globalsInit();
+
+ /* Are we running as init? */
+ if (getpid() == 1) {
+ int fd, type;
+
+ RunningAsInit = 1;
+ setsid();
+ close(0);
+ fd = open("/dev/ttyv0", O_RDWR);
+ if (fd == -1)
+ fd = open("/dev/console", O_RDWR); /* fallback */
+ else
+ OnVTY = TRUE;
+ /*
+ * To make _sure_ we're on a VTY and don't have /dev/console switched
+ * away to a serial port or something, attempt to set the cursor appearance.
+ */
+ type = 0; /* normal */
+ if (OnVTY) {
+ int fd2;
+
+ if ((fd2 = open("/dev/console", O_RDWR)) != -1) {
+ if (ioctl(fd2, CONS_CURSORTYPE, &type) == -1) {
+ OnVTY = FALSE;
+ close(fd); close(fd2);
+ open("/dev/console", O_RDWR);
+ }
+ else
+ close(fd2);
+ }
+ }
+ close(1); dup(0);
+ close(2); dup(0);
+ printf("%s running as init on %s\n", argv[0], OnVTY ? "vty0" : "serial console");
+ i = ioctl(0, TIOCSCTTY, (char *)NULL);
+ setlogin("root");
+ setenv("PATH", "/stand:/bin:/sbin:/usr/sbin:/usr/bin:/mnt/bin:/mnt/sbin:/mnt/usr/sbin:/mnt/usr/bin:/usr/X11R6/bin", 1);
+ setbuf(stdin, 0);
+ setbuf(stderr, 0);
+ }
+ else {
+ char hname[256];
+
+ /* Initalize various things for a multi-user environment */
+ if (!gethostname(hname, sizeof hname))
+ variable_set2(VAR_HOSTNAME, hname);
+ }
+
+ 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);
+}
+
+/* Close down and prepare to exit */
+void
+systemShutdown(int status)
+{
+ /* If some media is open, close it down */
+ if (status >=0 && mediaDevice)
+ mediaDevice->shutdown(mediaDevice);
+
+ /* write out any changes to rc.conf .. */
+ configRC_conf("/etc/rc.conf");
+
+ /* Shut down the dialog library */
+ if (DialogActive) {
+ end_dialog();
+ DialogActive = FALSE;
+ }
+
+ /* Shut down curses */
+ endwin();
+
+ /* If we have a temporary doc file lying around, nuke it */
+ unlink(DOC_TMP_FILE);
+
+ /* REALLY exit! */
+ if (RunningAsInit) {
+ /* Put the console back */
+ ioctl(0, VT_ACTIVATE, 2);
+ reboot(0);
+ }
+ else
+ exit(status);
+}
+
+/* Run some general command */
+int
+systemExecute(char *command)
+{
+ int status;
+ struct termios foo;
+
+ dialog_update();
+ end_dialog();
+ DialogActive = FALSE;
+ if (tcgetattr(0, &foo) != -1) {
+ foo.c_cc[VERASE] = '\010';
+ tcsetattr(0, TCSANOW, &foo);
+ }
+ if (!Fake)
+ status = system(command);
+ else {
+ status = 0;
+ msgDebug("systemExecute: Faked execution of `%s'\n", command);
+ }
+ DialogActive = TRUE;
+ return status;
+}
+
+/* Display a help file in a filebox */
+int
+systemDisplayHelp(char *file)
+{
+ char *fname = NULL;
+ char buf[FILENAME_MAX];
+ int ret = 0;
+
+ fname = systemHelpFile(file, buf);
+ if (!fname) {
+ snprintf(buf, FILENAME_MAX, "The %s file is not provided on this particular floppy image.", file);
+ use_helpfile(NULL);
+ use_helpline(NULL);
+ dialog_mesgbox("Sorry!", buf, -1, -1);
+ ret = 1;
+ }
+ else {
+ use_helpfile(NULL);
+ use_helpline(NULL);
+ dialog_textbox(file, fname, LINES, COLS);
+ }
+ return ret;
+}
+
+char *
+systemHelpFile(char *file, char *buf)
+{
+ if (!file)
+ return NULL;
+
+ snprintf(buf, FILENAME_MAX, "/stand/help/%s.hlp.gz", file);
+ if (file_readable(buf))
+ return expand(buf);
+ snprintf(buf, FILENAME_MAX, "/usr/src/release/sysinstall/help/%s.hlp", file);
+ if (file_readable(buf))
+ return buf;
+ return NULL;
+}
+
+void
+systemChangeTerminal(char *color, const u_char c_term[],
+ char *mono, const u_char m_term[])
+{
+ extern void init_acs(void);
+
+ if (OnVTY) {
+ if (ColorDisplay) {
+ setenv("TERM", color, 1);
+ setenv("TERMCAP", c_term, 1);
+ reset_shell_mode();
+ setterm(color);
+ init_acs();
+ cbreak(); noecho();
+ }
+ else {
+ setenv("TERM", mono, 1);
+ setenv("TERMCAP", m_term, 1);
+ reset_shell_mode();
+ setterm(mono);
+ init_acs();
+ cbreak(); noecho();
+ }
+ }
+ clear();
+ refresh();
+ dialog_clear();
+}
+
+int
+vsystem(char *fmt, ...)
+{
+ va_list args;
+ int pstat;
+ pid_t pid;
+ int omask;
+ sig_t intsave, quitsave;
+ char *cmd;
+ int i;
+
+ cmd = (char *)alloca(FILENAME_MAX);
+ cmd[0] = '\0';
+ va_start(args, fmt);
+ vsnprintf(cmd, FILENAME_MAX, fmt, args);
+ va_end(args);
+
+ omask = sigblock(sigmask(SIGCHLD));
+ if (Fake) {
+ msgDebug("vsystem: Faked execution of `%s'\n", cmd);
+ return 0;
+ }
+ if (isDebug())
+ msgDebug("Executing command `%s'\n", cmd);
+ pid = fork();
+ if (pid == -1) {
+ (void)sigsetmask(omask);
+ i = 127;
+ }
+ else if (!pid) { /* Junior */
+ (void)sigsetmask(omask);
+ if (DebugFD != -1) {
+ dup2(DebugFD, 0);
+ dup2(DebugFD, 1);
+ dup2(DebugFD, 2);
+ }
+ else {
+ close(1); open("/dev/null", O_WRONLY);
+ dup2(1, 2);
+ }
+ if (!RunningAsInit)
+ execl("/bin/sh", "/bin/sh", "-c", cmd, (char *)NULL);
+ else
+ execl("/stand/sh", "/stand/sh", "-c", cmd, (char *)NULL);
+ exit(1);
+ }
+ else {
+ intsave = signal(SIGINT, SIG_IGN);
+ quitsave = signal(SIGQUIT, SIG_IGN);
+ pid = waitpid(pid, &pstat, 0);
+ (void)sigsetmask(omask);
+ (void)signal(SIGINT, intsave);
+ (void)signal(SIGQUIT, quitsave);
+ i = (pid == -1) ? -1 : WEXITSTATUS(pstat);
+ if (isDebug())
+ msgDebug("Command `%s' returns status of %d\n", cmd, i);
+ }
+ return i;
+}
+
+void
+systemCreateHoloshell(void)
+{
+ if (OnVTY && RunningAsInit) {
+
+ if (ehs_pid != 0) {
+ int pstat;
+
+ if (kill(ehs_pid, 0) == 0) {
+
+ if (msgYesNo("There seems to be an emergency holographic shell\n"
+ "already running von VTY 4.\n"
+ "Kill it and start a new one?"))
+ return;
+
+ /* try cleaning up as much as possible */
+ (void) kill(ehs_pid, SIGHUP);
+ sleep(1);
+ (void) kill(ehs_pid, SIGKILL);
+ }
+
+ /* avoid too many zombies */
+ (void) waitpid(ehs_pid, &pstat, WNOHANG);
+ }
+
+ if ((ehs_pid = fork()) == 0) {
+ int i, fd;
+ struct termios foo;
+ extern int login_tty(int);
+
+ ioctl(0, TIOCNOTTY, NULL);
+ for (i = getdtablesize(); i >= 0; --i)
+ close(i);
+ fd = open("/dev/ttyv3", O_RDWR);
+ ioctl(0, TIOCSCTTY, &fd);
+ dup2(0, 1);
+ dup2(0, 2);
+ DebugFD = 2;
+ if (login_tty(fd) == -1)
+ msgDebug("Doctor: I can't set the controlling terminal.\n");
+ signal(SIGTTOU, SIG_IGN);
+ if (tcgetattr(fd, &foo) != -1) {
+ foo.c_cc[VERASE] = '\010';
+ if (tcsetattr(fd, TCSANOW, &foo) == -1)
+ msgDebug("Doctor: I'm unable to set the erase character.\n");
+ }
+ else
+ msgDebug("Doctor: I'm unable to get the terminal attributes!\n");
+ execlp("sh", "-sh", 0);
+ msgDebug("Was unable to execute sh for Holographic shell!\n");
+ exit(1);
+ }
+ else {
+ msgNotify("Starting an emergency holographic shell on VTY4");
+ sleep(2);
+ }
+ }
+}
diff --git a/usr.sbin/sade/termcap.c b/usr.sbin/sade/termcap.c
new file mode 100644
index 0000000..1569479
--- /dev/null
+++ b/usr.sbin/sade/termcap.c
@@ -0,0 +1,135 @@
+/*
+ * Copyright (c) 1994, Paul Richards.
+ *
+ * All rights reserved.
+ *
+ * This software may be used, modified, copied, distributed, and sold, in both
+ * source and binary form provided that the above copyright and these terms
+ * are retained, verbatim, as the first lines of this file. Under no
+ * circumstances is the author responsible for the proper functioning of this
+ * software, nor does the author assume any responsibility for damages
+ * incurred with its use.
+ */
+
+#include "sysinstall.h"
+#include <stdarg.h>
+#include <fcntl.h>
+#include <sys/errno.h>
+#include <sys/ioctl.h>
+#include <machine/console.h>
+
+#define VTY_STATUS_LINE 24
+#define TTY_STATUS_LINE 23
+
+static void
+prompt_term(char **termp, char **termcapp)
+{
+ char str[80];
+ static struct {
+ const char *term, *termcap;
+ } lookup[] = { { "ansi", termcap_ansi },
+ { "vt100", termcap_vt100 },
+ { "cons25", termcap_cons25 },
+ { "cons25-m", termcap_cons25_m } };
+
+ if (RunningAsInit) {
+ while (1) {
+ int i;
+
+ printf("\nThese are the predefined terminal types available to\n");
+ printf("sysinstall when running stand-alone. Please choose the\n");
+ printf("closest match for your particular terminal.\n\n");
+ printf("1 ...................... Standard ANSI terminal.\n");
+ printf("2 ...................... VT100 or compatible terminal.\n");
+ printf("3 ...................... FreeBSD system console (color).\n");
+ printf("4 ...................... FreeBSD system console (monochrome).\n\n");
+ printf("Your choice: (1-4) ");
+ fflush(stdout);
+ fgets(str, 80, stdin);
+ i = str[0] - '0';
+ if (i > 0 && i < 5) {
+ *termp = (char *)lookup[i - 1].term;
+ *termcapp = (char *)lookup[i - 1].termcap;
+ break;
+ }
+ else
+ printf("\007Invalid choice, please try again.\n\n");
+ }
+ }
+ else {
+ printf("\nPlease set your TERM variable before running this program.\n");
+ printf("Defaulting to an ANSI compatible terminal - please press RETURN\n");
+ fgets(str, 80, stdin); /* Just to make it interactive */
+ *termp = (char *)"ansi";
+ *termcapp = (char *)termcap_ansi;
+ }
+}
+
+int
+set_termcap(void)
+{
+ char *term;
+ int stat;
+ struct ttysize ts;
+
+ term = getenv("TERM");
+ stat = ioctl(STDERR_FILENO, GIO_COLOR, &ColorDisplay);
+
+ if (!RunningAsInit) {
+ if (getenv("SYSINSTALL_DEBUG"))
+ DebugFD = open("sysinstall.debug", O_WRONLY|O_CREAT|O_TRUNC, 0644);
+ else
+ DebugFD = -1;
+ if (DebugFD < 0)
+ DebugFD = open("/dev/null", O_RDWR, 0);
+ }
+
+ if (!OnVTY || (stat < 0)) {
+ if (!term) {
+ char *term, *termcap;
+
+ prompt_term(&term, &termcap);
+ if (setenv("TERM", term, 1) < 0)
+ return -1;
+ if (setenv("TERMCAP", termcap, 1) < 0)
+ return -1;
+ }
+ if (DebugFD < 0)
+ DebugFD = open("/dev/null", O_RDWR, 0);
+ }
+ else {
+ int i, on;
+
+ if (getpid() == 1) {
+ DebugFD = open("/dev/ttyv1", O_WRONLY);
+ if (DebugFD != -1) {
+ on = 1;
+ i = ioctl(DebugFD, TIOCCONS, (char *)&on);
+ msgDebug("ioctl(%d, TIOCCONS, NULL) = %d (%s)\n",
+ DebugFD, i, !i ? "success" : strerror(errno));
+ }
+ }
+ if (ColorDisplay) {
+ if (!term) {
+ if (setenv("TERM", "cons25", 1) < 0)
+ return -1;
+ if (setenv("TERMCAP", termcap_cons25, 1) < 0)
+ return -1;
+ }
+ }
+ else {
+ if (!term) {
+ if (setenv("TERM", "cons25-m", 1) < 0)
+ return -1;
+ if (setenv("TERMCAP", termcap_cons25_m, 1) < 0)
+ return -1;
+ }
+ }
+ }
+ if (ioctl(0, TIOCGSIZE, &ts) == -1) {
+ msgDebug("Unable to get terminal size - errno %d\n", errno);
+ ts.ts_lines = 0;
+ }
+ StatusLine = ts.ts_lines ? ts.ts_lines - 1: (OnVTY ? VTY_STATUS_LINE : TTY_STATUS_LINE);
+ return 0;
+}
diff --git a/usr.sbin/sade/variable.c b/usr.sbin/sade/variable.c
new file mode 100644
index 0000000..a13b71d
--- /dev/null
+++ b/usr.sbin/sade/variable.c
@@ -0,0 +1,193 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last program in the `sysinstall' line - the next
+ * generation being essentially a complete rewrite.
+ *
+ * $Id: variable.c,v 1.20 1997/06/13 14:21:22 jkh Exp $
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include "sysinstall.h"
+
+/* Routines for dealing with variable lists */
+
+static void
+make_variable(char *var, char *value)
+{
+ Variable *vp;
+
+ /* Trim leading and trailing whitespace */
+ var = string_skipwhite(string_prune(var));
+
+ if (!var || !*var)
+ return;
+
+ /* Put it in the environment in any case */
+ setenv(var, value, 1);
+
+ /* Now search to see if it's already in the list */
+ for (vp = VarHead; vp; vp = vp->next) {
+ if (!strcmp(vp->name, var)) {
+ if (isDebug())
+ msgDebug("variable %s was %s, now %s\n", vp->name, vp->value, value);
+ free(vp->value);
+ vp->value = strdup(value);
+ return;
+ }
+ }
+
+ /* No? Create a new one */
+ vp = (Variable *)safe_malloc(sizeof(Variable));
+ vp->name = strdup(var);
+ vp->value = strdup(value);
+ vp->next = VarHead;
+ VarHead = vp;
+ if (isDebug())
+ msgDebug("Setting variable %s to %s\n", vp->name, vp->value);
+}
+
+void
+variable_set(char *var)
+{
+ 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));
+}
+
+void
+variable_set2(char *var, char *value)
+{
+ if (!var || !value)
+ msgFatal("Null name or value passed to set_variable2!");
+ else if (!*var || !*value)
+ msgDebug("Warning: Zero length name or value passed to variable_set2()\n");
+ make_variable(var, value);
+}
+
+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)
+{
+ char *cp;
+
+ cp = variable_get(var);
+ if (cp && variable_get(VAR_NONINTERACTIVE))
+ return cp;
+ else if ((cp = msgGetInput(cp, prompt)) != NULL)
+ variable_set2(var, cp);
+ 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 *w, *cp, *cp2, *cp3, tmp[256];
+
+ w = data;
+ if (!w)
+ return FALSE;
+ SAFE_STRCPY(tmp, w);
+ if ((cp = index(tmp, '=')) != NULL) {
+ *(cp++) = '\0';
+ if ((cp3 = index(cp, ',')) != NULL)
+ *cp3 = '\0';
+ cp2 = getenv(tmp);
+
+ if (cp2)
+ return !strcmp(cp, cp2);
+ else
+ return FALSE;
+ }
+ else
+ return (int)getenv(tmp);
+}
diff --git a/usr.sbin/sade/wizard.c b/usr.sbin/sade/wizard.c
new file mode 100644
index 0000000..59831ea
--- /dev/null
+++ b/usr.sbin/sade/wizard.c
@@ -0,0 +1,232 @@
+/*
+ * ----------------------------------------------------------------------------
+ * "THE BEER-WARE LICENSE" (Revision 42):
+ * <phk@login.dknet.dk> wrote this file. As long as you retain this notice you
+ * can do whatever you want with this stuff. If we meet some day, and you think
+ * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp
+ * ----------------------------------------------------------------------------
+ *
+ * $Id$
+ *
+ */
+
+#include "sysinstall.h"
+#include <fcntl.h>
+#include <err.h>
+
+u_char mbr[] = {
+ 250,51,192,142,208,188,0,124,139,244,80,7,80,31,251,252,191,0,6,185,0,1,
+ 242,165,234,29,6,0,0,190,190,7,179,4,128,60,128,116,14,128,60,0,117,28,
+ 131,198,16,254,203,117,239,205,24,139,20,139,76,2,139,238,131,198,16,254,
+ 203,116,26,128,60,0,116,244,190,139,6,172,60,0,116,11,86,187,7,0,180,14,
+ 205,16,94,235,240,235,254,191,5,0,187,0,124,184,1,2,87,205,19,95,115,12,
+ 51,192,205,19,79,117,237,190,163,6,235,211,190,194,6,191,254,125,129,61,
+ 85,170,117,199,139,245,234,0,124,0,0,73,110,118,97,108,105,100,32,112,97,
+ 114,116,105,116,105,111,110,32,116,97,98,108,101,0,69,114,114,111,114,32,
+ 108,111,97,100,105,110,103,32,111,112,101,114,97,116,105,110,103,32,115,
+ 121,115,116,101,109,0,77,105,115,115,105,110,103,32,111,112,101,114,97,
+ 116,105,110,103,32,115,121,115,116,101,109,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,
+ 1,1,0,4,15,63,60,63,0,0,0,241,239,0,0,0,0,1,61,5,15,63,243,48,240,0,0,144,
+ 208,2,0,0,0,1,244,165,15,63,170,192,192,3,0,144,208,2,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,85,170
+};
+
+u_char bteasy17[] = {
+ 51,192,142,192,142,216,142,208,188,0,124,252,139,244,191,0,6,185,0,1,242,
+ 165,234,96,6,0,0,139,213,88,162,72,7,60,53,116,28,180,16,246,228,5,174,
+ 4,150,246,68,4,255,116,62,198,4,128,232,218,0,138,116,1,139,76,2,235,8,
+ 232,207,0,185,1,0,50,209,187,0,124,184,1,2,205,19,114,30,129,191,254,1,
+ 85,170,117,22,234,0,124,0,0,128,250,129,116,2,178,128,139,234,66,128,242,
+ 179,136,22,58,7,191,190,7,185,4,0,198,6,45,7,49,50,246,136,45,138,69,4,
+ 60,0,116,35,60,5,116,31,254,198,190,42,7,232,113,0,190,72,7,70,70,139,28,
+ 10,255,116,5,50,125,4,117,243,141,183,114,7,232,90,0,131,199,16,254,6,45,
+ 7,226,203,128,62,117,4,2,116,11,190,59,7,10,246,117,10,205,24,235,172,190,
+ 42,7,232,57,0,232,54,0,50,228,205,26,139,218,131,195,96,180,1,205,22,180,
+ 0,117,11,205,26,59,211,114,242,160,72,7,235,10,205,22,138,196,60,28,116,
+ 243,4,246,60,49,114,214,60,53,119,210,80,190,40,7,187,27,6,83,252,172,80,
+ 36,127,180,14,205,16,88,168,128,116,242,195,86,184,1,3,187,0,6,185,1,0,
+ 50,246,205,19,94,198,6,72,7,63,195,13,138,13,10,70,48,32,46,32,46,32,46,
+ 160,100,105,115,107,32,49,13,10,10,68,101,102,97,117,108,116,58,32,70,63,
+ 160,0,1,0,4,0,6,3,7,7,10,10,99,14,100,14,101,20,128,20,129,25,130,30,147,
+ 36,165,39,159,43,117,47,82,47,219,50,64,55,242,61,0,100,111,243,72,80,70,
+ 211,79,115,178,85,110,105,248,78,111,118,101,108,236,77,105,110,105,248,
+ 76,105,110,117,248,65,109,111,101,98,225,66,83,196,66,83,68,233,80,67,73,
+ 216,67,80,205,86,101,110,105,248,68,111,115,115,101,227,63,191,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,85,170
+};
+
+int
+scan_block(int fd, daddr_t block)
+{
+ u_char foo[512];
+
+ if (-1 == lseek(fd,block * 512,SEEK_SET))
+ err(1,"lseek");
+ if (512 != read(fd,foo, 512))
+ return 1;
+ return 0;
+}
+
+void
+Scan_Disk(Disk *d)
+{
+ char device[64];
+ u_long l;
+ int i,j,fd;
+
+ strcpy(device,"/dev/r");
+ strcat(device,d->name);
+
+ fd = open(device,O_RDWR);
+ if (fd < 0) {
+ msgWarn("open(%s) failed", device);
+ return;
+ }
+ for(i=-1,l=0;;l++) {
+ j = scan_block(fd,l);
+ if (j != i) {
+ if (i == -1) {
+ printf("%c: %lu.",j ? 'B' : 'G', l);
+ fflush(stdout);
+ } else if (i == 0) {
+ printf(".%lu\nB: %lu.",l-1,l);
+ fflush(stdout);
+ } else {
+ printf(".%lu\nG: %lu.",l-1,l);
+ fflush(stdout);
+ }
+ i = j;
+ }
+ }
+ close(fd);
+}
+
+void
+slice_wizard(Disk *d)
+{
+ Disk *db;
+ char myprompt[BUFSIZ];
+ char input[BUFSIZ];
+ char *p,*q=0;
+ char **cp,*cmds[200];
+ int ncmd,i;
+
+ sprintf(myprompt,"%s> ", d->name);
+ while(1) {
+ printf("--==##==--\n");
+ Debug_Disk(d);
+ p = CheckRules(d);
+ if (p) {
+ printf("%s",p);
+ free(p);
+ }
+ printf(myprompt);
+ fflush(stdout);
+ q = p = fgets(input,sizeof(input),stdin);
+ if(!p)
+ break;
+ for(cp = cmds; (*cp = strsep(&p, " \t\n")) != NULL;)
+ if (**cp != '\0')
+ cp++;
+ ncmd = cp - cmds;
+ if(!ncmd)
+ continue;
+ if (!strcasecmp(*cmds,"quit")) { break; }
+ if (!strcasecmp(*cmds,"exit")) { break; }
+ if (!strcasecmp(*cmds,"q")) { break; }
+ if (!strcasecmp(*cmds,"x")) { break; }
+ if (!strcasecmp(*cmds,"delete") && ncmd == 2) {
+ printf("delete = %d\n",
+ Delete_Chunk(d,
+ (struct chunk *)strtol(cmds[1],0,0)));
+ continue;
+ }
+ if (!strcasecmp(*cmds,"allfreebsd")) {
+ All_FreeBSD(d, 0);
+ continue;
+ }
+ if (!strcasecmp(*cmds,"dedicate")) {
+ All_FreeBSD(d, 1);
+ continue;
+ }
+ if (!strcasecmp(*cmds,"bios") && ncmd == 4) {
+ Set_Bios_Geom(d,
+ strtol(cmds[1],0,0),
+ strtol(cmds[2],0,0),
+ strtol(cmds[3],0,0));
+ continue;
+ }
+ if (!strcasecmp(*cmds,"list")) {
+ cp = Disk_Names();
+ printf("Disks:");
+ for(i=0;cp[i];i++) {
+ printf(" %s",cp[i]);
+ free(cp[i]);
+ }
+ free(cp);
+ continue;
+ }
+ if (!strcasecmp(*cmds,"create") && ncmd == 6) {
+
+ printf("Create=%d\n",
+ Create_Chunk(d,
+ strtol(cmds[1],0,0),
+ strtol(cmds[2],0,0),
+ strtol(cmds[3],0,0),
+ strtol(cmds[4],0,0),
+ strtol(cmds[5],0,0)));
+ continue;
+ }
+ if (!strcasecmp(*cmds,"read")) {
+ db = d;
+ if (ncmd > 1)
+ d = Open_Disk(cmds[1]);
+ else
+ d = Open_Disk(d->name);
+ if (d)
+ Free_Disk(db);
+ else
+ d = db;
+ continue;
+ }
+ if (!strcasecmp(*cmds,"scan")) {
+ Scan_Disk(d);
+ continue;
+ }
+ if (!strcasecmp(*cmds,"write")) {
+ printf("Write=%d\n",
+ Fake ? 0 : Write_Disk(d));
+ Free_Disk(d);
+ d = Open_Disk(d->name);
+ continue;
+ }
+ if (strcasecmp(*cmds,"help"))
+ printf("\007ERROR\n");
+ printf("CMDS:\n");
+ printf("allfreebsd\t\t");
+ printf("dedicate\t\t");
+ printf("bios cyl hd sect\n");
+ printf("collapse [pointer]\t\t");
+ printf("create offset size enum subtype flags\n");
+ printf("subtype(part): swap=1, ffs=7\t\t");
+ printf("delete pointer\n");
+ printf("list\t\t");
+ printf("quit\n");
+ printf("read [disk]\t\t");
+ printf("scan\n");
+ printf("write\t\t");
+ printf("ENUM:\n\t");
+ for(i=0;chunk_n[i];i++)
+ printf("%d = %s%s",i,chunk_n[i],i == 4 ? "\n\t" : " ");
+ printf("\n");
+
+ }
+}
diff --git a/usr.sbin/sendmail/Makefile b/usr.sbin/sendmail/Makefile
index cd28123..269c809 100644
--- a/usr.sbin/sendmail/Makefile
+++ b/usr.sbin/sendmail/Makefile
@@ -1,7 +1,7 @@
# @(#)Makefile 8.15 (Berkeley) 9/21/96
VER= XX
-SUBDIR= src mail.local mailstats makemap praliases smrsh cf/cf
+SUBDIR= src mailstats makemap praliases smrsh cf/cf
FTPDIR= mastodon:/disks/barad-dur/ftp/sendmail/.
DISTFILES=sendmail.${VER}.tar.Z sendmail.${VER}.tar.gz \
RELEASE_NOTES FAQ KNOWNBUGS
diff --git a/usr.sbin/sendmail/cf/cf/Makefile b/usr.sbin/sendmail/cf/cf/Makefile
index 7450b2a..3ab538c 100644
--- a/usr.sbin/sendmail/cf/cf/Makefile
+++ b/usr.sbin/sendmail/cf/cf/Makefile
@@ -21,27 +21,50 @@ RM= rm -f
.mc.cf:
$(RM) $@
- (cd ${.CURDIR} && $(M4) ${CFDIR}/m4/cf.m4 ${@:R}.mc > obj/$@)
+ (cd ${.CURDIR} && $(M4) ${CFDIR}/m4/cf.m4 ${@:R}.mc) > $@
$(CHMOD) $(ROMODE) $@
-ALL= generic-bsd4.4.cf generic-hpux9.cf generic-hpux10.cf \
- generic-osf1.cf generic-solaris2.cf \
- generic-sunos4.1.cf generic-ultrix4.cf \
- cs-hpux9.cf cs-osf1.cf cs-solaris2.cf \
- cs-sunos4.1.cf cs-ultrix4.cf \
- s2k-osf1.cf s2k-ultrix4.cf \
- chez.cs.cf huginn.cs.cf mail.cs.cf mail.eecs.cf mailspool.cs.cf \
- python.cs.cf ucbarpa.cf ucbvax.cf vangogh.cs.cf knecht.cf
+ALL= freebsd.cf
+
+# Local sendmail.cf, may be set in /etc/make.conf. Warning! If set, this
+# causes 'make install' to always copy it over /etc/sendmail.cf!!!
+# Caveat emptor! Be sure you want this before you enable it.
+.if defined(SENDMAIL_CF)
+ALL+= ${SENDMAIL_CF}
+.endif
+
+CLEANFILES+=$(ALL)
+
+#We dont build the generic stuff...
+#Use `make generic-bsd4.4.cf' if you want one..
+#ALL= generic-bsd4.4.cf generic-hpux9.cf generic-hpux10.cf \
+# generic-osf1.cf generic-solaris2.cf \
+# generic-sunos4.1.cf generic-ultrix4.cf \
+# cs-hpux9.cf cs-osf1.cf cs-solaris2.cf \
+# cs-sunos4.1.cf cs-ultrix4.cf \
+# s2k-osf1.cf s2k-ultrix4.cf \
+# chez.cs.cf huginn.cs.cf mail.cs.cf mail.eecs.cf mailspool.cs.cf \
+# python.cs.cf ucbarpa.cf ucbvax.cf vangogh.cs.cf knecht.cf
all: $(ALL)
-clean cleandir:
- $(RM) $(ALL) core
+depend:
+
+install:
+.if defined(SENDMAIL_CF)
+ ${INSTALL} ${COPY} -o ${BINOWN} -g ${BINGRP} -m 644 ${SENDMAIL_CF} \
+ ${DESTDIR}/etc/sendmail.cf
+.endif
+
-depend install:
+# Helper for src/etc/Makefile
+etc-sendmail.cf: freebsd.cf
+ ${INSTALL} ${COPY} -o ${BINOWN} -g ${BINGRP} -m 644 freebsd.cf \
+ ${DESTDIR}/etc/sendmail.cf
# this is overkill, but....
M4FILES=\
+ ${CFDIR}/domain/generic.m4 \
${CFDIR}/domain/Berkeley.EDU.m4 \
${CFDIR}/domain/CS.Berkeley.EDU.m4 \
${CFDIR}/domain/EECS.Berkeley.EDU.m4 \
diff --git a/usr.sbin/sendmail/cf/cf/Makefile.dist b/usr.sbin/sendmail/cf/cf/Makefile.dist
deleted file mode 100644
index 7dc55d9..0000000
--- a/usr.sbin/sendmail/cf/cf/Makefile.dist
+++ /dev/null
@@ -1,108 +0,0 @@
-#
-# Makefile for configuration files.
-#
-# @(#)Makefile.dist 8.9 (Berkeley) 9/12/95
-#
-
-#
-# Configuration files are created using "m4 file.mc > file.cf";
-# this may be easier than tweaking the Makefile. You do need to
-# have a fairly modern M4 available (GNU m4 works). On SunOS, use
-# /usr/5bin/m4.
-#
-
-M4= m4
-#M4= /usr/src/usr.bin/m4/obj/m4
-CFDIR= ..
-CHMOD= chmod
-ROMODE= 444
-RM= rm -f
-
-.SUFFIXES: .mc .cf
-
-.mc.cf:
- $(RM) $@
- $(M4) ${CFDIR}/m4/cf.m4 $*.mc > $@
- $(CHMOD) $(ROMODE) $@
-
-ALL= generic-bsd4.4.cf generic-hpux9.cf generic-hpux10.cf \
- generic-osf1.cf generic-solaris2.cf \
- cs-hpux9.cf cs-osf1.cf cs-solaris2.cf \
- cs-sunos4.1.cf cs-ultrix4.cf \
- s2k-osf1.cf s2k-ultrix4.cf \
- chez.cs.cf huginn.cs.cf mail.cs.cf mail.eecs.cf mailspool.cs.cf \
- python.cs.cf ucbarpa.cf ucbvax.cf vangogh.cs.cf
-
-all: $(ALL)
-
-clean cleandir:
- $(RM) $(ALL) core
-
-depend install:
-
-# this is overkill, but....
-M4FILES=\
- ${CFDIR}/domain/Berkeley.EDU.m4 \
- ${CFDIR}/domain/CS.Berkeley.EDU.m4 \
- ${CFDIR}/domain/EECS.Berkeley.EDU.m4 \
- ${CFDIR}/domain/S2K.Berkeley.EDU.m4 \
- ${CFDIR}/feature/allmasquerade.m4 \
- ${CFDIR}/feature/always_add_domain.m4 \
- ${CFDIR}/feature/bestmx_is_local.m4 \
- ${CFDIR}/feature/bitdomain.m4 \
- ${CFDIR}/feature/domaintable.m4 \
- ${CFDIR}/feature/local_procmail.m4 \
- ${CFDIR}/feature/mailertable.m4 \
- ${CFDIR}/feature/nocanonify.m4 \
- ${CFDIR}/feature/nodns.m4 \
- ${CFDIR}/feature/notsticky.m4 \
- ${CFDIR}/feature/nouucp.m4 \
- ${CFDIR}/feature/nullclient.m4 \
- ${CFDIR}/feature/redirect.m4 \
- ${CFDIR}/feature/smrsh.m4 \
- ${CFDIR}/feature/stickyhost.m4 \
- ${CFDIR}/feature/use_cw_file.m4 \
- ${CFDIR}/feature/uucpdomain.m4 \
- ${CFDIR}/hack/cssubdomain.m4 \
- ${CFDIR}/m4/cf.m4 \
- ${CFDIR}/m4/nullrelay.m4 \
- ${CFDIR}/m4/proto.m4 \
- ${CFDIR}/m4/version.m4 \
- ${CFDIR}/mailer/cyrus.m4 \
- ${CFDIR}/mailer/fax.m4 \
- ${CFDIR}/mailer/local.m4 \
- ${CFDIR}/mailer/mail11.m4 \
- ${CFDIR}/mailer/pop.m4 \
- ${CFDIR}/mailer/procmail.m4 \
- ${CFDIR}/mailer/smtp.m4 \
- ${CFDIR}/mailer/usenet.m4 \
- ${CFDIR}/mailer/uucp.m4 \
- ${CFDIR}/ostype/aix3.m4 \
- ${CFDIR}/ostype/amdahl-uts.m4 \
- ${CFDIR}/ostype/aux.m4 \
- ${CFDIR}/ostype/bsd4.3.m4 \
- ${CFDIR}/ostype/bsd4.4.m4 \
- ${CFDIR}/ostype/bsdi1.0.m4 \
- ${CFDIR}/ostype/dgux.m4 \
- ${CFDIR}/ostype/domainos.m4 \
- ${CFDIR}/ostype/dynix3.2.m4 \
- ${CFDIR}/ostype/hpux9.m4 \
- ${CFDIR}/ostype/irix4.m4 \
- ${CFDIR}/ostype/irix5.m4 \
- ${CFDIR}/ostype/linux.m4 \
- ${CFDIR}/ostype/nextstep.m4 \
- ${CFDIR}/ostype/osf1.m4 \
- ${CFDIR}/ostype/ptx2.m4 \
- ${CFDIR}/ostype/riscos4.5.m4 \
- ${CFDIR}/ostype/sco3.2.m4 \
- ${CFDIR}/ostype/solaris2.m4 \
- ${CFDIR}/ostype/sunos3.5.m4 \
- ${CFDIR}/ostype/sunos4.1.m4 \
- ${CFDIR}/ostype/svr4.m4 \
- ${CFDIR}/ostype/ultrix4.m4 \
- ${CFDIR}/siteconfig/uucp.cogsci.m4 \
- ${CFDIR}/siteconfig/uucp.old.arpa.m4 \
- ${CFDIR}/siteconfig/uucp.ucbarpa.m4 \
- ${CFDIR}/siteconfig/uucp.ucbvax.m4 \
-
-$(ALL): $(M4FILES)
diff --git a/usr.sbin/sendmail/cf/cf/freebsd.mc b/usr.sbin/sendmail/cf/cf/freebsd.mc
new file mode 100644
index 0000000..c1bbe07
--- /dev/null
+++ b/usr.sbin/sendmail/cf/cf/freebsd.mc
@@ -0,0 +1,54 @@
+divert(-1)
+#
+# Copyright (c) 1983 Eric P. Allman
+# 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.
+#
+
+#
+# This is a generic configuration file for 4.4 BSD-based systems,
+# including 4.4-Lite, BSDi, NetBSD, and FreeBSD.
+# It has support for local and SMTP mail only. If you want to
+# customize it, copy it to a name appropriate for your environment
+# and do the modifications there.
+#
+
+divert(0)dnl
+VERSIONID(`@(#)freebsd.mc $Revision: 1.2 $')
+OSTYPE(bsd4.4)dnl
+DOMAIN(generic)dnl
+MAILER(local)dnl
+MAILER(smtp)dnl
+FEATURE(mailertable, `hash -o /etc/mailertable')dnl
+define(`UUCP_RELAY', ucbvax.Berkeley.EDU)dnl
+define(`BITNET_RELAY', mailhost.Berkeley.EDU)dnl
+define(`CSNET_RELAY', mailhost.Berkeley.EDU)dnl
+define(`confCW_FILE', `-o /etc/sendmail.cw')dnl
diff --git a/usr.sbin/sendmail/cf/cf/freefall.mc b/usr.sbin/sendmail/cf/cf/freefall.mc
new file mode 100644
index 0000000..f73df95
--- /dev/null
+++ b/usr.sbin/sendmail/cf/cf/freefall.mc
@@ -0,0 +1,47 @@
+divert(-1)
+#
+# Copyright (c) 1983 Eric P. Allman
+# 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.
+#
+
+#
+# This the prototype for a "null client" -- that is, a client that
+# does nothing except forward all mail to a mail hub, plus an extra
+# line to make the email all appear as coming from "FreeBSD.org".
+#
+
+divert(0)dnl
+VERSIONID(`$Id$')
+
+OSTYPE(bsd4.4)
+FEATURE(nullclient, hub.$m)
+MASQUERADE_AS(FreeBSD.org)
diff --git a/usr.sbin/sendmail/cf/cf/hub.mc b/usr.sbin/sendmail/cf/cf/hub.mc
new file mode 100644
index 0000000..dce5d4a
--- /dev/null
+++ b/usr.sbin/sendmail/cf/cf/hub.mc
@@ -0,0 +1,124 @@
+divert(-1)
+#
+# Copyright (c) 1983 Eric P. Allman
+# 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.
+#
+
+#
+# This file is the configuration for hub.freebsd.org, the project's mail
+# server. It needs to handle some ugly mail volumes.
+#
+
+divert(0)dnl
+include(../m4/cf.m4)
+VERSIONID(`$Id: hub.mc,v 1.3 1997/10/06 00:09:17 jmb Exp $')
+
+OSTYPE(bsd4.4)dnl
+DOMAIN(generic)dnl
+MAILER(local)dnl
+MAILER(smtp)dnl
+MASQUERADE_AS(FreeBSD.ORG)dnl
+FEATURE(mailertable, `hash -o /etc/mailertable')dnl
+FEATURE(masquerade_envelope)dnl
+EXPOSED_USER(root)dnl
+EXPOSED_USER(mailman)dnl
+define(`UUCP_RELAY', uunet.uu.net)dnl
+define(`BITNET_RELAY', mailhost.Berkeley.EDU)dnl
+define(`CSNET_RELAY', mailhost.Berkeley.EDU)dnl
+define(`confCW_FILE', `-o /etc/sendmail.cw')dnl
+define(`confCHECKPOINT_INTERVAL', `4')dnl
+define(`confAUTO_REBUILD', `True')dnl
+define(`confMIN_FREE_BLOCKS', `1024')dnl
+define(`confSMTP_MAILER', `smtp8')dnl
+define(`confME_TOO', `True')dnl
+define(`confMCI_CACHE_TIMEOUT', `10m')dnl
+define(`confTO_QUEUEWARN', `1d')dnl
+define(`confTO_QUEUEWARN_NORMAL', `1d')dnl
+define(`confTO_RCPT', `10m')dnl
+define(`confTO_DATABLOCK', `10m')dnl
+define(`confTO_DATAFINAL', `10m')dnl
+define(`confTO_COMMAND', `10m')dnl
+define(`confTO_HOSTSTATUS', `30m')dnl
+define(`confMIN_QUEUE_AGE', `30m')dnl
+define(`confNO_RCPT_ACTION', `add-to-undisclosed')dnl
+define(`confTRUSTED_USERS', `majordom')dnl
+define(`confRECEIVED_HEADER', `$?sfrom $s $.$?_($?s$|from $.$_)
+ $.by $j ($v/$Z)$?r with $r$. id $i$?u
+ for $u; $|;
+ $.$b$?g
+ (envelope-from $g)$.')dnl
+define(`confHOST_STATUS_DIRECTORY', `.hoststat')dnl
+define(`confMAX_DAEMON_CHILDREN', `8')dnl
+define(`confCONNECTION_THROTTLE_RATE', `1')dnl
+define(`confFORWARD_PATH', `/var/forward/$u')dnl
+
+LOCAL_CONFIG
+Cw localhost freefall.freebsd.org
+
+Kdenyip hash -o -a.REJECT /etc/mail/denyip.db
+Kspamsites hash -o -a.REJECT /etc/mail/spamsites.db
+
+Scheck_relay
+# called with host.tld and IP address of connecting host.
+# ip address must NOT be in the "denyip" database
+R$* $| [$+ $1 $| $2 should not be needed
+R$* $| $+] $1 $| $2 same (bat 2nd ed p510)
+R$* $| $* $: $1 $| $(denyip $2 $)
+R$* $| $*.REJECT $#error $: 521 blocked. contact postmaster@FreeBSD.ORG ($2)
+# host must *not* be in the "spamsites" database
+R$+.$+.$+ $| $* $2.$3 $| $4
+R$+.$+ $| $* $: $(spamsites $1.$2 $) $| $3
+R$*.REJECT $| $* $#error $: 521 blocked. contact postmaster@FreeBSD.ORG ($1)
+# Host must be resolvable
+#R$* $| $* $: <?> <$1 $| $2> $>3 foo@$1
+#R<?> <$*> $*<@$*.> $: $1
+#R<?> <$*> $*<@$*> $#error $: 451 Domain does not resolve ($1)
+
+# spamsites database optional--fail safe, deliver the mail.
+Scheck_mail
+# called with envelope sender, "Mail From: xxx", of SMTP conversation
+#
+# can't force DNS, Poul-Henning Kamp and others dont resolve
+# <root@dgbmsu1.s2.dgb.tfs>... Domain does not resolve
+#
+R$* $: <?> $>3 $1
+R<?> $* < @ $+ . > $: $2
+# R<?> $* < @ $+ > $#error $: "451 Domain does not resolve"
+R<?> $* < @ $+ > $: $2
+R$+.$+.$+ $2.$3
+R$* $: $(spamsites $1 $: OK $)
+ROK $@ OK
+R$+.REJECT $#error $: 521 $1
+
+Sxlat # for sendmail -bt
+R$* $$| $* $: $1 $| $2
+R$* $| $* $@ $>check_relay $1 $| $2
diff --git a/usr.sbin/sendmail/cf/cf/knecht.mc b/usr.sbin/sendmail/cf/cf/knecht.mc
index 71ae12b..111ac78 100644
--- a/usr.sbin/sendmail/cf/cf/knecht.mc
+++ b/usr.sbin/sendmail/cf/cf/knecht.mc
@@ -38,7 +38,7 @@ divert(-1)
#
divert(0)dnl
-VERSIONID(`@(#)knecht.mc 8.15 (Berkeley) 10/20/97')
+VERSIONID(`@(#)knecht.mc 8.13 (Berkeley) 7/7/97')
OSTYPE(bsd4.4)dnl
DOMAIN(generic)dnl
define(`confFORWARD_PATH', `$z/.forward.$w:$z/.forward+$h:$z/.forward')dnl
@@ -48,7 +48,6 @@ define(`confTO_ICONNECT', `10s')dnl
define(`confCOPY_ERRORS_TO', `Postmaster')dnl
define(`confTO_QUEUEWARN', `8h')dnl
define(`confPRIVACY_FLAGS', ``authwarnings,noexpn,novrfy'')dnl
-define(`LOCAL_MAILER_FLAGS', `rmn9P')dnl
FEATURE(virtusertable)dnl
MAILER(local)dnl
MAILER(smtp)dnl
@@ -120,7 +119,7 @@ R<FAIL> $* $#error $: 451 Sender domain must resolve
# handle case of no @domain on address
R<?> $* $: < ? $&{client_name} > $1
R<?> $* $@ <OK> ...local unqualed ok
-R<? $+> $* $#error $: 550 Domain name required
+R<? $+> $* $#error $: 551 Domain name required
...remote is not
R<$+> $* $#error $: $1 error from domaincheck
diff --git a/usr.sbin/sendmail/cf/feature/smrsh.m4 b/usr.sbin/sendmail/cf/feature/smrsh.m4
index 6b4faab..a0fed03 100644
--- a/usr.sbin/sendmail/cf/feature/smrsh.m4
+++ b/usr.sbin/sendmail/cf/feature/smrsh.m4
@@ -39,4 +39,4 @@ divert(-1)
ifdef(`_MAILER_local_',
`errprint(`*** FEATURE(smrsh) must occur before MAILER(local)')')dnl
-define(`LOCAL_SHELL_PATH', ifelse(_ARG_, `', `/usr/local/etc/smrsh', _ARG_))
+define(`LOCAL_SHELL_PATH', ifelse(_ARG_, `', `/usr/libexec/smrsh', _ARG_))
diff --git a/usr.sbin/sendmail/cf/m4/cfhead.m4 b/usr.sbin/sendmail/cf/m4/cfhead.m4
index 6bef4c6..137a0cc 100644
--- a/usr.sbin/sendmail/cf/m4/cfhead.m4
+++ b/usr.sbin/sendmail/cf/m4/cfhead.m4
@@ -131,7 +131,8 @@ define(`confSMTP_LOGIN_MSG', `$j Sendmail $v/$Z; $b')
define(`confRECEIVED_HEADER', `$?sfrom $s $.$?_($?s$|from $.$_)
$.by $j ($v/$Z)$?r with $r$. id $i$?u
for $u; $|;
- $.$b')
+ $.$b$?g
+ (envelope-from $g)$.')
define(`confSEVEN_BIT_INPUT', `False')
define(`confEIGHT_BIT_HANDLING', `pass8')
define(`confALIAS_WAIT', `10')
diff --git a/usr.sbin/sendmail/cf/ostype/bsd4.4.m4 b/usr.sbin/sendmail/cf/ostype/bsd4.4.m4
index 835e4d8..fe66abd 100644
--- a/usr.sbin/sendmail/cf/ostype/bsd4.4.m4
+++ b/usr.sbin/sendmail/cf/ostype/bsd4.4.m4
@@ -39,4 +39,5 @@ VERSIONID(`@(#)bsd4.4.m4 8.4 (Berkeley) 11/13/95')
ifdef(`HELP_FILE',, `define(`HELP_FILE', /usr/share/misc/sendmail.hf)')dnl
ifdef(`STATUS_FILE',, `define(`STATUS_FILE', /var/log/sendmail.st)')dnl
ifdef(`LOCAL_MAILER_PATH',, `define(`LOCAL_MAILER_PATH', /usr/libexec/mail.local)')dnl
+ifdef(`LOCAL_MAILER_ARGS',, `define(`LOCAL_MAILER_ARGS', `mail $u')')dnl
ifdef(`UUCP_MAILER_ARGS',, `define(`UUCP_MAILER_ARGS', `uux - -r -z -a$g $h!rmail ($u)')')dnl
diff --git a/usr.sbin/sendmail/cf/sh/makeinfo.sh b/usr.sbin/sendmail/cf/sh/makeinfo.sh
index 68b85d3..67ea078 100644
--- a/usr.sbin/sendmail/cf/sh/makeinfo.sh
+++ b/usr.sbin/sendmail/cf/sh/makeinfo.sh
@@ -73,7 +73,7 @@ then
else
host=`uname -n`
fi
-echo '#####' built by $user@$host on `date`
+echo '#####' built by $user@$host on `LC_TIME=C date`
echo '#####' in `pwd` | sed 's/\/tmp_mnt//'
echo '#####' using $1 as configuration include directory | sed 's/\/tmp_mnt//'
echo "define(\`__HOST__', $host)dnl"
diff --git a/usr.sbin/sendmail/contrib/bitdomain.c b/usr.sbin/sendmail/contrib/bitdomain.c
index 52d6d21..6f1bded 100644
--- a/usr.sbin/sendmail/contrib/bitdomain.c
+++ b/usr.sbin/sendmail/contrib/bitdomain.c
@@ -51,7 +51,7 @@ char **argv;
{
int opt;
- while ((opt = getopt(argc, argv, "o:")) != EOF) {
+ while ((opt = getopt(argc, argv, "o:")) != -1) {
switch (opt) {
case 'o':
if (!freopen(optarg, "w", stdout)) {
@@ -187,7 +187,7 @@ char *domainlen;
case NO_DATA:
err = "registered in DNS, but not mailable";
break;
-
+
default:
err = "unknown nameserver error";
break;
@@ -210,7 +210,7 @@ valhost(host, hbsize)
int hbsize;
{
register u_char *eom, *ap;
- register int n;
+ register int n;
HEADER *hp;
querybuf answer;
int ancount, qdcount;
@@ -406,4 +406,4 @@ finish()
}
}
}
-
+
diff --git a/usr.sbin/sendmail/contrib/re-mqueue.pl b/usr.sbin/sendmail/contrib/re-mqueue.pl
index 61aef43..9a8f8b4 100644
--- a/usr.sbin/sendmail/contrib/re-mqueue.pl
+++ b/usr.sbin/sendmail/contrib/re-mqueue.pl
@@ -84,7 +84,7 @@
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.
#
-# @(#)$Id: re-mqueue,v 1.3 1995/05/25 18:14:53 p-pomes Exp $
+# @(#)$Id$
require "syslog.pl";
diff --git a/usr.sbin/sendmail/contrib/xla/README b/usr.sbin/sendmail/contrib/xla/README
deleted file mode 100644
index a72fd03..0000000
--- a/usr.sbin/sendmail/contrib/xla/README
+++ /dev/null
@@ -1,207 +0,0 @@
- XLA - Extended Load Average design for Sendmail R6
- --------------------------------------------------
-
- Christophe Wolfhugel - Herve Schauer Consultants
- wolf@grasp.insa-lyon.fr, wolf@hsc-sec.fr
-
-
-WARNING: this extension is supplied as a contribution to Sendmail.
-Should you have trouble, questions, please contact me directly, and
-*not* the Sendmail development team.
-
-
-ABSTRACT
-
-Sendmail currently furnishes a limitation mecanism which is based on
-the system load average, when available. Experience has prooven that
-this was not sufficiant for some particular situations, for example
-if you have slow and/or overloaded links. This can easily cause both
-system and network congestions with Sendmail having to handle a large
-number of simultaneous sessions on the same overloaded link, causing
-most of the SMTP sessions to timeout after a long time. The system
-load average is also generally too slow to react when your system
-gets a burst of incoming or outgoing SMTP sessions which on some
-stations can easily cause system unavailabilities.
-
-The extended load average module has been designed in order to furnish
-a way of limitation the load generated by Sendmail to both your
-system and your network. This design can be used either alone or as
-complementary to the system load average if your system supports it.
-
-Limitation is based on the number of incoming/outgoing SMTP sessions,
-and remote hosts are classified in classes. The system administrator
-will define a maximum number of incoming SMTP sessions as well as
-a maximum total (incoming + outgoing) sessions for each class of
-hosts. A class can be either an individual machine or a network.
-
-When the limit is reached for a given class, all incoming SMTP
-connections will be politely refused. When the limit is reached for
-all classes, the SMTP connections will be refused by the system
-(which one could consider as less politely :)).
-On outgoing mail, messages will be queued for delayed processing.
-
-The extended load average parameters are given in the Sendmail
-configuration file, and when not present, Sendmail behaves the
-usual way.
-
-
-COMPILATION
-
-Copy the xla.c module in the src sub-directory, edit the Makefile
-in order to define XLA (-DXLA). Also add the xla.[co] module name
-in the list of files so that it gets compiled.
-
-Regenerate sendmail by removing all objects, or at least those
-containing references to XLA (this list may vary, so use grep to
-get the module list). This will generate a new sendmail executable
-containing the xla code.
-
-Debugging level 59 has been assigned to this module and when used
-it provides some output (sendmail -d59.x). Please check the source
-code to see which levels are supported.
-
-
-CONFIGURATION
-
-The extended average uses a new set of configuration lines in the
-sendmail.cf file. All newly introduced line begin with the letter L
-(capital L).
-
-Before detailling the syntax, first an example (this can be placed
-at any section of the sendmail.cf file, note that the order is
-important). Fields are separated by (one or more) tabs/spaces.
-
-# File name used to store the counters
-L/etc/sendmail.la
-# Classes definition
-# Lname #queue #reject
-L*.insa-lyon.fr 8 3
-L*.univ-lyon1.fr 6 4
-L* 15 16
-
-The first line defines the working file which will be used in order
-to have the occurences of Sendmail read and update the counters. The
-format of this file is described in the "Design" section.
-This line is mandatory and the specified file must be absolute (ie
-begin with a slash).
-
-Then you can specify one or more classes. The last class (*) is also
-mandatory and should be in last position as the first match will stop
-the search and if there is no match the behavior of Sendmail is unknown.
-
-Each class has three fields separated by one or more tabs/spaces.
-
-L{mask} {queue_#} {refuse_#}
-
-The {mask} is a simple mask. It can be either an explicit host name
-(like grasp.insa-lyon.fr) or a mask starting with "*." or just "*".
-No other variants are allowed.
-
-Lgrasp.insa-lyon.fr will match exactely any session to/from this host.
-
-L*.insa-lyon.fr will match any session to/from any machine in the
- insa-lyon.fr domain as well as from the machine
- named "insa-lyon.fr" if it exists.
-
-L* will match any session, and thus should also be
- last in the list to act as a catchup line.
-
-The {queue_#} is the maximum number of SMTP sessions in the given class
-for both incoming and outgoing messages. The {refuse_#} indicates when
-to refuse incoming messages for this class. The interaction between
-those counters is somewhat subtle. It seems logical that a standard
-configuration has {queue_#} >= {refuse_#}, and in fact in most
-configurations they can be equal (that's why what I use in my environment).
-Thus, this is not mandatory. If {queue_#} < {refuse_#} outgoing messages
-will be lower priority than incoming messages and once a class gets loaded
-the outgoing messages are blocked first.
-
-I use very low values in some situations, for example I have a customer
-connected to the Internet via a 9600 bps line, they also have internal
-users sending burst of messages (10, 20 small messages coming in just
-one or two seconds). Both situations were unsupportable. The line is
-too slow to handle many simultaneous connections and the mail server
-does not have the ressources to handle such a heavy load (it's a 12 Megs
-Sun 3 also doing Usenet news).
-
-I have defined following section in the configuration file, and experience
-shows the benefits for everyone. Fake domain for the example: customer.fr.
-
-L/etc/sendmail.la
-L*.customer.fr 8 8
-L* 3 3
-
-This means that there might not be more than 8 simultaneous SMTP sessions
-between the mail server and any other internal host. This is to protect
-the station from heavy loads like users (or applications !) sending
-several tenths of messages in just a few seconds).
-No more than 3 SMTP sessions are authorized with any other host, this is
-to save the load of the slow 9600 line to the Internet.
-
-Drawback is that is you have 3 * 2 Megs sessions established from/to the
-outside, all your mail will be held until one slot gets available, on
-a 9600 bps line just make your counts, il blocks your line during over
-one hour.
-
-
-DESIGN
-
-Sendmail will analyze the "L" lines in the configuration file during
-startup (or read the initialized structure from the frozen file).
-When started in daemon mode (and only there), any existing working file
-will be cleared and a new one is created. Each class gets a record in
-the sendmail.la work file. The size of this record is a short integer
-(generally two bytes) and represents the count of active sessions in
-the given class. Read/Write operations in this file are done in
-one operation (as anyway the size is far below one disk sector). The
-file is locked with Sendmail's lockfile() function priori to any
-access.
-
-Handling incoming SMTP sessions.
-
-There is interaction is two points in the Sendmail source code. First
-on the listen system call: if all slots in all classes are in use,
-a listen(0) is done so that the system rejects any incoming SMTP session.
-This avois to fork and then reject the connexion.
-
-If there are some free slots, nothing better than accepting the
-connection, then forking can be done. The child process then checks if
-the adequate class is full or not. If full, it rejects the connection
-with a "421 Too many sessions" diagnostic to the sender (which should
-then appear when the remote users do a mailq). If the treshold {reject_#}
-is not reached, the connection is accepted and the counter is sendmail.la
-is updated.
-
-Handling outgoing SMTP sessions.
-
-As soon as Sendmail needs to connect to a distant host, the adequate class
-is checked against {queue_#} and if no slots are available, the message is
-queued for further processing.
-
-Sendmail's connection caching.
-
-Sendmail-R6 introduces a new design: connection caching, ie several SMTP
-sessions can be opened at the same time. This could cause some problems
-when sending mail, as after having a few connections opened, all slots
-could be in use and generate a partial delivery of the message. In
-order to deal with this, xla.c uses following design "for a given
-sendmail process, only the first connection in a given class is counted".
-This can be done because sendmail does not do parralel message sending
-on the different channels.
-
-End of connection.
-
-As soon as a connection is closed, the counters will be automatically
-updated.
-
-
-
-Please look at the code to understand of all this works. Comments,
-suggestions, questions welcome.
-
-
-
- Christophe Wolfhugel
- Herve Schauer Consultants
- Paris, France
- May 23, 1993
diff --git a/usr.sbin/sendmail/contrib/xla/xla.c b/usr.sbin/sendmail/contrib/xla/xla.c
deleted file mode 100644
index 627d383..0000000
--- a/usr.sbin/sendmail/contrib/xla/xla.c
+++ /dev/null
@@ -1,532 +0,0 @@
-/*
- * (C) Copyright 1993-94, Herve Schauer Consultants
- *
- * This module written by Christophe.Wolfhugel@hsc-sec.fr
- * is to be used under the same conditions and terms (and absence
- * or warranties) than the other modules of the Sendmail package.
- *
- * ABSOLUTELY NO WARRANTY. USE AT YOUR OWN RISKS.
- *
- * Version: 940417, applied a patch from Paul Graham <pjg@acsu.buffalo.edu>
- * (lockfile syntax in xla.c did not reflect anymore the
- * new one used by sendmail, now fine).
- *
- */
-
-
-#ifdef XLA
-
-#ifndef MAXLARULES
-# define MAXLARULES 20
-#endif
-
-# include "sendmail.h"
-
-typedef struct {
- short queue; /* # of connexions to have queueing */
- short reject; /* # of conn. to reject */
- short num; /* # of increments this process */
- char *mask; /* Mask (domain) */
- } XLARULE;
-
-char *XlaFname; /* Work file name */
-char XlaHostName[1024]; /* Temporary */
-int XlaNext; /* # of XlaRules */
-pid_t XlaPid; /* Pid updating the tables */
-XLARULE XlaRules[MAXLARULES]; /* The rules themselves */
-short XlaCtr[MAXLARULES]; /* Counter (for work file only) */
-
-extern bool lockfile();
-
-/*
-** XLAMATCH -- Matches a fnmatch like expression.
-**
-** Parameters:
-** mask -- mask to match the string too;
-** name -- string.
-**
-** Mask can either be a plain string or a simplified fnmatch like mask:
-** *.string or string.*
-** No other alternatives are accepted.
-**
-** Returns:
-** none.
-**
-** Side Effects:
-** none.
-*/
-
-bool
-XlaMatch(mask, name)
- char *mask, *name;
-{
- int l1, l2;
-
- l1 = strlen(mask); l2 = strlen(name);
- if (l1 == 1 && mask[0] == '*') return(TRUE);
- if (mask[0] == '*' && mask[1] == '.') {
- if (l2 < (l1 - 2)) return(FALSE);
- if (strcasecmp(&mask[2], name) == 0) return(TRUE);
- if (strcasecmp(&mask[1], name + l2 - l1 + 1) == 0) return(TRUE);
- return(FALSE);
- }
- if (l1 < 3) return(FALSE);
- if (mask[l1 -1] == '*') {
- if (l2 < l1 - 1) return(FALSE);
- if (strncasecmp(mask, name, l1 - 1) == 0) return(TRUE);
- return(FALSE);
- }
- if (strcasecmp(mask, name) == 0) return(TRUE);
- return(FALSE);
-}
-
-/*
-** XLAZERO -- Zeroes the used variables
-**
-** Just initializes some variables, called once at sendmail
-** startup.
-**
-** Parameters:
-** none.
-**
-** Returns:
-** none.
-**
-** Side Effects:
-** none.
-*/
-
-xla_zero()
-{
- if (tTd(59, 1)) {
- printf("xla_zero\n");
- }
- XlaFname = NULL;
- XlaNext = 0;
- XlaPid = 0;
- memset((char *) &XlaRules[0], 0, sizeof(XLARULE) * MAXLARULES);
-}
-
-
-/*
-** XLAINIT -- initialized extended load average stuff
-**
-** This routine handles the L lines appearing in the configuration
-** file.
-**
-** L/etc/sendmail.la indicates the working file
-** Lmask #1 #2 Xtended LA to apply to mask
-** #1 = Queueing # of connections
-** #2 = Reject connections.
-**
-** Parameters:
-** line -- the cf file line to parse.
-**
-** Returns:
-** none.
-**
-** Side Effects:
-** Builds several internal tables.
-*/
-
-xla_init(line)
- char *line;
-{
- char *s;
-
- if (tTd(59, 1)) {
- printf("xla_init line: %s\n", line);
- }
- if (XlaFname == NULL && *line == '/') { /* Work file name */
- XlaFname = newstr(line);
- if (tTd(59, 10))
- printf("xla_init: fname = %s\n", XlaFname);
- return;
- }
- if (XlaNext == MAXLARULES) {
- syserr("too many xla rules defined (%d max)", MAXLARULES);
- return;
- }
- s = strtok(line, " \t");
- if (s == NULL) {
- syserr("xla: line unparseable");
- return;
- }
- XlaRules[XlaNext].mask = newstr(s);
- s = strtok(NULL, " \t");
- if (s == NULL) {
- syserr("xla: line unparseable");
- return;
- }
- XlaRules[XlaNext].queue = atoi(s);
- s = strtok(NULL, " \t");
- if (s == NULL) {
- syserr("xla: line unparseable");
- return;
- }
- XlaRules[XlaNext].reject = atoi(s);
- if (tTd(59, 10))
- printf("xla_init: rule #%d = %s q=%d r=%d\n", XlaNext,
- XlaRules[XlaNext].mask,
- XlaRules[XlaNext].queue, XlaRules[XlaNext].reject);
- XlaNext++;
-}
-
-
-/*
-** XLACREATEFILE -- Create the working file
-**
-** Tries to create the working file, called each time sendmail is
-** invoked with the -bd option.
-**
-** Parameters:
-** none.
-**
-** Returns:
-** none.
-**
-** Side Effects:
-** Creates the working file (sendmail.la) and zeroes it.
-*/
-
-xla_create_file()
-{
- int fd, i;
-
- if (tTd(59, 1))
- printf("xla_create_file:\n");
- if (XlaFname == NULL) return;
- fd = open(XlaFname, O_RDWR|O_CREAT, 0644);
- if (fd == -1) {
- XlaFname = NULL;
- syserr("xla_create_file: open failed");
- return;
- }
- if (!lockfile(fd, XlaFname, NULL, LOCK_EX)) {
- close(fd);
- XlaFname = NULL;
- syserr("xla_create_file: can't set lock");
- return;
- }
- if (ftruncate(fd, 0) == -1) {
- close(fd);
- XlaFname = NULL;
- syserr("xla_create_file: can't truncate XlaFname");
- return;
- }
- if (write(fd, XlaCtr, sizeof(XlaCtr)) != sizeof(XlaCtr)) {
- XlaFname == NULL;
- syserr("xla_create_file: can't write XlaFname");
- }
- close(fd);
-}
-
-
-/*
-** XLASMTPOK -- Checks if all slots are in use
-**
-** Check is there are still some slots available for an SMTP
-** connection.
-**
-** Parameters:
-** none.
-**
-** Returns:
-** TRUE -- slots are available;
-** FALSE -- no more slots.
-**
-** Side Effects:
-** Reads a file, uses a lock and updates sendmail.la if a slot
-** is free for use.
-*/
-
-bool
-xla_smtp_ok()
-{
- int fd, i;
-
- if (tTd(59, 1))
- printf("xla_smtp_ok:\n");
- if (XlaFname == NULL) return(TRUE);
- fd = open(XlaFname, O_RDWR, 0644);
- if (fd == -1) {
- XlaFname = NULL;
- syserr("xla_smtp_ok: open failed");
- return(TRUE);
- }
- if (!lockfile(fd, XlaFname, NULL, LOCK_EX)) {
- close(fd);
- XlaFname = NULL;
- syserr("xla_smtp_ok: can't set lock");
- return(TRUE);
- }
- if (read(fd, XlaCtr, sizeof(XlaCtr)) != sizeof(XlaCtr)) {
- close(fd);
- XlaFname = NULL;
- syserr("xla_smtp_ok: can't read XlaFname");
- return(TRUE);
- }
- close(fd);
- for (i = 0; i < XlaNext; i++) {
- if (XlaCtr[i] < XlaRules[i].reject)
- return(TRUE);
- }
- return(FALSE);
-}
-
-
-/*
-** XLAHOSTOK -- Can we accept a connection from this host
-**
-** Check the quota for the indicated host
-**
-** Parameters:
-** name -- host name or IP# (string)
-**
-** Returns:
-** TRUE -- we can accept the connection;
-** FALSE -- we must refuse the connection.1
-**
-** Side Effects:
-** Reads and writes a file, uses a lock and still updates
-** sendmail.la is a slot gets assigned.
-*/
-
-bool
-xla_host_ok(name)
- char *name;
-{
- int fd, i;
-
- if (tTd(59, 1))
- printf("xla_host_ok:\n");
- if (XlaFname == NULL) return(TRUE);
- fd = open(XlaFname, O_RDWR, 0644);
- if (fd == -1) {
- XlaFname = NULL;
- syserr("xla_host_ok: open failed");
- return(TRUE);
- }
- XlaPid = getpid();
- if (!lockfile(fd, XlaFname, NULL, LOCK_EX)) {
- close(fd);
- XlaFname = NULL;
- syserr("xla_host_ok: can't set lock");
- return(TRUE);
- }
- if (read(fd, XlaCtr, sizeof(XlaCtr)) != sizeof(XlaCtr)) {
- close(fd);
- XlaFname = NULL;
- syserr("xla_smtp_ok: can't read XlaFname");
- return(TRUE);
- }
- strncpy(XlaHostName, name, sizeof(XlaHostName) -1);
- XlaHostName[sizeof(XlaHostName) -1] = 0;
- i = strlen(name) - 1;
- if (i >= 0 && XlaHostName[i] == '.') XlaHostName[i] = 0;
- for (i = 0; i < XlaNext; i++) {
- if (XlaMatch(XlaRules[i].mask, XlaHostName)) {
- if (XlaCtr[i] < XlaRules[i].reject) {
- if (XlaRules[i].num++ == 0) {
- XlaCtr[i]++;
- lseek(fd, i*sizeof(XlaCtr[i]), SEEK_SET);
- if (write(fd, &XlaCtr[i], sizeof(XlaCtr[i])) != sizeof(XlaCtr[i]))
- XlaFname = NULL;
- }
- close(fd);
- return(TRUE);
- }
- close(fd);
- return(FALSE);
- }
- }
- close(fd);
- return(TRUE);
-}
-
-/*
-** XLANOQUEUEOK -- Can we sent this message to the remote host
-**
-** Check if we can send to the remote host
-**
-** Parameters:
-** name -- host name or IP# (string)
-**
-** Returns:
-** TRUE -- we can send the message to the remote site;
-** FALSE -- we can't connect the remote host, queue.
-**
-** Side Effects:
-** Reads and writes a file, uses a lock.
-** And still updates the sendmail.la file.
-*/
-
-bool
-xla_noqueue_ok(name)
- char *name;
-{
- int fd, i;
-
- if (tTd(59, 1))
- printf("xla_noqueue_ok:\n");
- if (XlaFname == NULL) return(TRUE);
- fd = open(XlaFname, O_RDWR, 0644);
- if (fd == -1) {
- XlaFname = NULL;
- syserr("xla_noqueue_ok: open failed");
- return(TRUE);
- }
- if (!lockfile(fd, XlaFname, NULL, LOCK_EX)) {
- close(fd);
- XlaFname = NULL;
- syserr("xla_noqueue_ok: can't set lock");
- return(TRUE);
- }
- XlaPid = getpid();
- if (read(fd, XlaCtr, sizeof(XlaCtr)) != sizeof(XlaCtr)) {
- close(fd);
- XlaFname = NULL;
- syserr("xla_noqueue_ok: can't read XlaFname");
- return(TRUE);
- }
- strncpy(XlaHostName, name, sizeof(XlaHostName) -1);
- XlaHostName[sizeof(XlaHostName) -1] = 0;
- i = strlen(name) - 1;
- if (i >= 0 && XlaHostName[i] == '.') XlaHostName[i] = 0;
- for (i = 0; i < XlaNext; i++) {
- if (XlaMatch(XlaRules[i].mask, XlaHostName)) {
- if (XlaCtr[i] < XlaRules[i].queue) {
- if (XlaRules[i].num++ == 0) {
- XlaCtr[i]++;
- lseek(fd, i*sizeof(XlaCtr[i]), SEEK_SET);
- if (write(fd, &XlaCtr[i], sizeof(XlaCtr[i])) != sizeof(XlaCtr[i]))
- XlaFname = NULL;
- }
- close(fd);
- return(TRUE);
- }
- close(fd);
- return(FALSE);
- }
- }
- close(fd);
- return(TRUE);
-}
-
-
-/*
-** XLAHOSTEND -- Notice that a connection is terminated.
-**
-** Updates the counters to reflect the end of an SMTP session
-** (in or outgoing).
-**
-** Parameters:
-** name -- host name or IP# (string)
-**
-** Returns:
-** none.
-**
-** Side Effects:
-** Reads and writes a file, uses a lock.
-** And still updates sendmail.la.
-*/
-
-xla_host_end(name)
- char *name;
-{
- int fd, i;
-
- if (tTd(59, 1))
- printf("xla_host_end:\n");
- if (XlaFname == NULL || XlaPid != getpid()) return;
- fd = open(XlaFname, O_RDWR, 0644);
- if (fd == -1) {
- XlaFname = NULL;
- syserr("xla_host_end: open failed");
- return;
- }
- if (!lockfile(fd, XlaFname, NULL, LOCK_EX)) {
- close(fd);
- XlaFname = NULL;
- syserr("xla_host_end: can't set lock");
- return;
- }
- if (read(fd, XlaCtr, sizeof(XlaCtr)) != sizeof(XlaCtr)) {
- close(fd);
- XlaFname = NULL;
- syserr("xla_host_end: can't read XlaFname");
- return(TRUE);
- }
- strncpy(XlaHostName, name, sizeof(XlaHostName) -1);
- XlaHostName[sizeof(XlaHostName) -1] = 0;
- i = strlen(name) - 1;
- if (i >= 0 && XlaHostName[i] == '.') XlaHostName[i] = 0;
- for (i = 0; i < XlaNext; i++) {
- if (XlaMatch(XlaRules[i].mask, XlaHostName)) {
- if (XlaRules[i].num > 0 && XlaRules[i].num-- == 1) {
- if (XlaCtr[i]) XlaCtr[i]--;
- lseek(fd, i*sizeof(XlaCtr[i]), SEEK_SET);
- if (write(fd, &XlaCtr[i], sizeof(XlaCtr[i]))
- != sizeof(XlaCtr[i]))
- XlaFname = NULL;
- }
- close(fd);
- return;
- }
- }
- close(fd);
-}
-
-/*
-** XLAALLEND -- Mark all connections as closed.
-**
-** Generally due to an emergency exit.
-**
-** Parameters:
-** name -- host name or IP# (string)
-**
-** Returns:
-** none.
-**
-** Side Effects:
-** Reads and writes a file, uses a lock.
-** And guess what: updates sendmail.la.
-*/
-
-xla_all_end()
-{
- int fd, i;
-
- if (tTd(59, 1))
- printf("xla_all_end:\n");
- if (XlaFname == NULL || XlaPid != getpid()) return;
- fd = open(XlaFname, O_RDWR, 0644);
- if (fd == -1) {
- XlaFname = NULL;
- syserr("xla_all_end: open failed");
- return;
- }
- if (!lockfile(fd, XlaFname, NULL, LOCK_EX)) {
- close(fd);
- XlaFname = NULL;
- syserr("xla_all_end: can't set lock");
- return;
- }
- if (read(fd, XlaCtr, sizeof(XlaCtr)) != sizeof(XlaCtr)) {
- close(fd);
- XlaFname = NULL;
- syserr("xla_all_end: can't read XlaFname");
- return(TRUE);
- }
- for (i = 0; i < XlaNext; i++) {
- if (XlaCtr[i] > 0 && XlaRules[i].num > 0) {
- XlaCtr[i]--; XlaRules[i].num = 0;
- }
- }
- lseek(fd, 0, SEEK_SET);
- if (write(fd, XlaCtr, sizeof(XlaCtr)) != sizeof(XlaCtr)) {
- XlaFname = NULL;
- }
- close(fd);
-}
-#endif /* XLA */
diff --git a/usr.sbin/sendmail/doc/changes/changes.ps b/usr.sbin/sendmail/doc/changes/changes.ps
deleted file mode 100644
index 5ba54a4..0000000
--- a/usr.sbin/sendmail/doc/changes/changes.ps
+++ /dev/null
@@ -1,1092 +0,0 @@
-%!PS-Adobe-3.0
-%%Creator: groff version 1.08
-%%DocumentNeededResources: font Times-Roman
-%%+ font Times-Italic
-%%+ font Times-Bold
-%%+ font Symbol
-%%DocumentSuppliedResources: procset grops 1.08 0
-%%Pages: 11
-%%PageOrder: Ascend
-%%Orientation: Portrait
-%%EndComments
-%%BeginProlog
-%%BeginResource: procset grops 1.08 0
-/setpacking where{
-pop
-currentpacking
-true setpacking
-}if
-/grops 120 dict dup begin
-/SC 32 def
-/A/show load def
-/B{0 SC 3 -1 roll widthshow}bind def
-/C{0 exch ashow}bind def
-/D{0 exch 0 SC 5 2 roll awidthshow}bind def
-/E{0 rmoveto show}bind def
-/F{0 rmoveto 0 SC 3 -1 roll widthshow}bind def
-/G{0 rmoveto 0 exch ashow}bind def
-/H{0 rmoveto 0 exch 0 SC 5 2 roll awidthshow}bind def
-/I{0 exch rmoveto show}bind def
-/J{0 exch rmoveto 0 SC 3 -1 roll widthshow}bind def
-/K{0 exch rmoveto 0 exch ashow}bind def
-/L{0 exch rmoveto 0 exch 0 SC 5 2 roll awidthshow}bind def
-/M{rmoveto show}bind def
-/N{rmoveto 0 SC 3 -1 roll widthshow}bind def
-/O{rmoveto 0 exch ashow}bind def
-/P{rmoveto 0 exch 0 SC 5 2 roll awidthshow}bind def
-/Q{moveto show}bind def
-/R{moveto 0 SC 3 -1 roll widthshow}bind def
-/S{moveto 0 exch ashow}bind def
-/T{moveto 0 exch 0 SC 5 2 roll awidthshow}bind def
-/SF{
-findfont exch
-[exch dup 0 exch 0 exch neg 0 0]makefont
-dup setfont
-[exch/setfont cvx]cvx bind def
-}bind def
-/MF{
-findfont
-[5 2 roll
-0 3 1 roll
-neg 0 0]makefont
-dup setfont
-[exch/setfont cvx]cvx bind def
-}bind def
-/level0 0 def
-/RES 0 def
-/PL 0 def
-/LS 0 def
-/PLG{
-gsave newpath clippath pathbbox grestore
-exch pop add exch pop
-}bind def
-/BP{
-/level0 save def
-1 setlinecap
-1 setlinejoin
-72 RES div dup scale
-LS{
-90 rotate
-}{
-0 PL translate
-}ifelse
-1 -1 scale
-}bind def
-/EP{
-level0 restore
-showpage
-}bind def
-/DA{
-newpath arcn stroke
-}bind def
-/SN{
-transform
-.25 sub exch .25 sub exch
-round .25 add exch round .25 add exch
-itransform
-}bind def
-/DL{
-SN
-moveto
-SN
-lineto stroke
-}bind def
-/DC{
-newpath 0 360 arc closepath
-}bind def
-/TM matrix def
-/DE{
-TM currentmatrix pop
-translate scale newpath 0 0 .5 0 360 arc closepath
-TM setmatrix
-}bind def
-/RC/rcurveto load def
-/RL/rlineto load def
-/ST/stroke load def
-/MT/moveto load def
-/CL/closepath load def
-/FL{
-currentgray exch setgray fill setgray
-}bind def
-/BL/fill load def
-/LW/setlinewidth load def
-/RE{
-findfont
-dup maxlength 1 index/FontName known not{1 add}if dict begin
-{
-1 index/FID ne{def}{pop pop}ifelse
-}forall
-/Encoding exch def
-dup/FontName exch def
-currentdict end definefont pop
-}bind def
-/DEFS 0 def
-/EBEGIN{
-moveto
-DEFS begin
-}bind def
-/EEND/end load def
-/CNT 0 def
-/level1 0 def
-/PBEGIN{
-/level1 save def
-translate
-div 3 1 roll div exch scale
-neg exch neg exch translate
-0 setgray
-0 setlinecap
-1 setlinewidth
-0 setlinejoin
-10 setmiterlimit
-[]0 setdash
-/setstrokeadjust where{
-pop
-false setstrokeadjust
-}if
-/setoverprint where{
-pop
-false setoverprint
-}if
-newpath
-/CNT countdictstack def
-userdict begin
-/showpage{}def
-}bind def
-/PEND{
-clear
-countdictstack CNT sub{end}repeat
-level1 restore
-}bind def
-end def
-/setpacking where{
-pop
-setpacking
-}if
-%%EndResource
-%%IncludeResource: font Times-Roman
-%%IncludeResource: font Times-Italic
-%%IncludeResource: font Times-Bold
-%%IncludeResource: font Symbol
-grops begin/DEFS 1 dict def DEFS begin/u{.001 mul}bind def end/RES 72 def/PL
-792 def/LS false def/ENC0[/asciicircum/asciitilde/Scaron/Zcaron/scaron/zcaron
-/Ydieresis/trademark/quotesingle/.notdef/.notdef/.notdef/.notdef/.notdef
-/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef
-/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/space
-/exclam/quotedbl/numbersign/dollar/percent/ampersand/quoteright/parenleft
-/parenright/asterisk/plus/comma/hyphen/period/slash/zero/one/two/three/four
-/five/six/seven/eight/nine/colon/semicolon/less/equal/greater/question/at/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/bracketleft/backslash
-/bracketright/circumflex/underscore/quoteleft/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/braceleft/bar/braceright/tilde/.notdef/quotesinglbase
-/guillemotleft/guillemotright/bullet/florin/fraction/perthousand/dagger
-/daggerdbl/endash/emdash/ff/fi/fl/ffi/ffl/dotlessi/dotlessj/grave/hungarumlaut
-/dotaccent/breve/caron/ring/ogonek/quotedblleft/quotedblright/oe/lslash
-/quotedblbase/OE/Lslash/.notdef/exclamdown/cent/sterling/currency/yen/brokenbar
-/section/dieresis/copyright/ordfeminine/guilsinglleft/logicalnot/minus
-/registered/macron/degree/plusminus/twosuperior/threesuperior/acute/mu
-/paragraph/periodcentered/cedilla/onesuperior/ordmasculine/guilsinglright
-/onequarter/onehalf/threequarters/questiondown/Agrave/Aacute/Acircumflex/Atilde
-/Adieresis/Aring/AE/Ccedilla/Egrave/Eacute/Ecircumflex/Edieresis/Igrave/Iacute
-/Icircumflex/Idieresis/Eth/Ntilde/Ograve/Oacute/Ocircumflex/Otilde/Odieresis
-/multiply/Oslash/Ugrave/Uacute/Ucircumflex/Udieresis/Yacute/Thorn/germandbls
-/agrave/aacute/acircumflex/atilde/adieresis/aring/ae/ccedilla/egrave/eacute
-/ecircumflex/edieresis/igrave/iacute/icircumflex/idieresis/eth/ntilde/ograve
-/oacute/ocircumflex/otilde/odieresis/divide/oslash/ugrave/uacute/ucircumflex
-/udieresis/yacute/thorn/ydieresis]def/Times-Bold@0 ENC0/Times-Bold RE
-/Times-Italic@0 ENC0/Times-Italic RE/Times-Roman@0 ENC0/Times-Roman RE
-%%EndProlog
-%%Page: 1 1
-%%BeginPageSetup
-BP
-%%EndPageSetup
-/F0 14/Times-Roman@0 SF(Changes in Sendmail V)196.615 141 Q(ersion 8*)-1.554 E
-/F1 10/Times-Roman@0 SF(Eric Allman)263.42 165 Q/F2 10/Times-Italic@0 SF
-(Univer)220.2 183 Q(sity of California, Berk)-.1 E(ele)-.1 E(y)-.3 E
-(Mammoth Pr)251.98 195 Q(oject)-.45 E F1(ABSTRA)262.085 227.4 Q(CT)-.4 E -1.11
-(Ve)112 243.6 S 1.709(rsion 8 of)1.11 F F2(sendmail)4.209 E F1 1.709
-(includes a number of major changes from pre)4.209 F 1.71(vious v)-.25 F
-(ersions.)-.15 E .701(This paper gi)112 255.6 R -.15(ve)-.25 G 3.201(sav).15 G
-.701(ery short history of)194.794 255.6 R F2(sendmail)3.201 E F1 3.201(,as)C .7
-(ummary of the major dif)329.82 255.6 R(ferences)-.25 E .953(between v)112
-267.6 R .954(ersion 5 \(the last publically a)-.15 F -.25(va)-.2 G .954
-(ilable v).25 F .954(ersion\) and v)-.15 F .954(ersion 8, and some dis-)-.15 F
-(cussion of future directions.)112 279.6 Q .48
-(In 1987, the author stopped major w)97 324 R .48(ork on)-.1 F F2(sendmail)2.98
-E F1 .48(due to other time committments, only to return)2.98 F(to acti)72 336 Q
-.3 -.15(ve w)-.25 H(ork in 1991.).05 E(This paper e)5 E(xplores wh)-.15 E 2.5
-(yw)-.05 G(ork resumed and what changes ha)277 336 Q .3 -.15(ve b)-.2 H
-(een made.).15 E .58(Section 1 gi)97 352.2 R -.15(ve)-.25 G 3.08(sas).15 G .58
-(hort history of)173.36 352.2 R F2(sendmail)3.08 E F1 .58(through v)3.08 F .58
-(ersion 5 and the moti)-.15 F -.25(va)-.25 G .58(tion behind w).25 F .58
-(orking on)-.1 F -.15(ve)72 364.2 S .126(rsion 8.).15 F .126
-(Section 2 has a rather detailed description of what has changed between v)
-5.126 F .125(ersion 5 and v)-.15 F .125(ersion 8.)-.15 F
-(The paper \214nishes of)72 376.2 Q 2.5(fw)-.25 G
-(ith some thoughts about what still needs to be done.)168.95 376.2 Q/F3 10
-/Times-Bold@0 SF 2.5(1. HIST)72 400.2 R(OR)-.18 E(Y)-.35 E F1 .151
-(As discussed else)112 416.4 R .151
-(where, [Allman83a, Allman83b, Allman&Amos85] sendmail has e)-.25 F .151
-(xisted in v)-.15 F(ar)-.25 E(-)-.2 E .405(ious forms since 1980.)87 428.4 R
-.405(It w)5.405 F .405(as released under the name)-.1 F F2(delivermail)2.905 E
-F1 .404(in 4BSD and 4.1BSD, and as)2.905 F F2(send-)2.904 E(mail)87 440.4 Q F1
-(in 4.2BSD.)2.5 E(It quickly became the dominant mail system for netw)5 E(ork)
--.1 E(ed UNIX systems.)-.1 E 1.569(Prior the release of 4.3BSD in No)112 456.6
-R -.15(ve)-.15 G 1.569(mber 1986, the author had left the Uni).15 F -.15(ve)
--.25 G 1.57(rsity for pri).15 F -.25(va)-.25 G(te).25 E(industry)87 468.6 Q
-3.347(,b)-.65 G .847(ut continued to do some w)129.777 468.6 R .847(ork on)-.1
-F F2(sendmail)3.347 E F1 .847(with acti)3.347 F .846(vity slo)-.25 F .846
-(wly trailing of)-.25 F 3.346(fu)-.25 G .846(ntil ef)445.204 468.6 R(fecti)-.25
-E -.15(ve)-.25 G(ly).15 E .255(stopping after February 1987.)87 480.6 R .255
-(There w)5.255 F .255(as minimal support done by man)-.1 F 2.756(yp)-.15 G .256
-(eople for se)389.796 480.6 R -.15(ve)-.25 G .256(ral years, until).15 F
-(July of 1991 when the original author)87 492.6 Q 2.5(,w)-.4 G
-(ho had returned the Uni)249.36 492.6 Q -.15(ve)-.25 G(rsity).15 E 2.5(,s)-.65
-G(tarted acti)379.4 492.6 Q .3 -.15(ve w)-.25 H(ork on it ag).05 E(ain.)-.05 E
-1.271(There were se)112 508.8 R -.15(ve)-.25 G 1.271(ral reasons for rene).15 F
-1.271(wed w)-.25 F 1.271(ork on)-.1 F F2(sendmail)3.771 E F1 6.271(.T)C 1.271
-(here w)369.549 508.8 R 1.27(as a desire at Berk)-.1 F(ele)-.1 E 3.77(yt)-.15 G
-(o)499 508.8 Q(con)87 520.8 Q -.15(ve)-.4 G .097
-(rt to a subdomained structure so that indi).15 F .098
-(viduals were identi\214ed by their subdomain rather than by)-.25 F 1.758
-(their indi)87 532.8 R 1.758(vidual w)-.25 F 1.758(orkstation; although possib\
-le in the old code, there were some problems, and the)-.1 F .66(author w)87
-544.8 R .66(as the ob)-.1 F .66(vious person to address them.)-.15 F .66
-(The Computer Systems Research Group \(CSRG\), the)5.66 F 1.89
-(group that produced the Berk)87 556.8 R(ele)-.1 E 4.39(yS)-.15 G(oftw)238.12
-556.8 Q 1.89(are Distrib)-.1 F 1.89(utions, w)-.2 F 1.89(as w)-.1 F 1.89
-(orking on 4.4BSD, and w)-.1 F 1.89(anted an)-.1 F .053
-(update to the mail system.)87 568.8 R .053(Bryan Costales w)5.053 F .053(as w)
--.1 F .053(orking on a book on)-.1 F F2(sendmail)2.553 E F1 .053(that w)2.553 F
-.053(as being re)-.1 F(vie)-.25 E(wed)-.25 E .923(by the author)87 580.8 R
-3.423(,w)-.4 G .923(hich encouraged him to mak)154.359 580.8 R 3.422(es)-.1 G
-.922(ome re)283.572 580.8 R 3.422(visions. And)-.25 F .922(the author w)3.422 F
-.922(anted to try to unify)-.1 F(some of the disparate v)87 592.8 Q(ersions of)
--.15 E F2(sendmail)2.5 E F1(that had been permitted to proliferate.)2.5 E .023
-(During the 1987\25591 f)112 609 R(allo)-.1 E 2.523(wp)-.25 G .023(eriod, man)
-228.482 609 R 2.523(yv)-.15 G .023(endors and outside v)283.498 609 R .023
-(olunteers had produced v)-.2 F .024(ariants of)-.25 F F2(sendmail)87 621 Q F1
-5.518(.P)C .517(erhaps the best kno)136.688 621 R .517(wn is the ID)-.25 F
-3.017(Av)-.4 G .517(ersion [ID)280.317 621 R 3.017(A87]. Originally)-.4 F .517
-(intended to be a ne)3.017 F 3.017(ws)-.25 G .517(et of)485.433 621 R .268
-(con\214guration \214les, ID)87 633 R 2.768(Ae)-.4 G .269(xpanded into a f)
-189.464 633 R .269(airly lar)-.1 F .269(ge set of patches for the code.)-.18 F
-.269(Originally produced in)5.269 F .471(Sweden, ID)87 645 R 2.971(Ad)-.4 G
--2.15 -.25(ev e)149.472 645 T .471(lopment passed to the Uni).25 F -.15(ve)-.25
-G .471(rsity of Illinois, and w).15 F .47(as widely used by the f)-.1 F .47
-(airly lar)-.1 F(ge)-.18 E .077
-(set of people who prefer to get and compile their o)87 657 R .077
-(wn source code rather than use v)-.25 F(endor)-.15 E .078(-supplied bina-)-.2
-F(ries.)87 669 Q .32 LW 76 678.6 72 678.6 DL 80 678.6 76 678.6 DL 84 678.6 80
-678.6 DL 88 678.6 84 678.6 DL 92 678.6 88 678.6 DL 96 678.6 92 678.6 DL 100
-678.6 96 678.6 DL 104 678.6 100 678.6 DL 108 678.6 104 678.6 DL 112 678.6 108
-678.6 DL 116 678.6 112 678.6 DL 120 678.6 116 678.6 DL 124 678.6 120 678.6 DL
-128 678.6 124 678.6 DL 132 678.6 128 678.6 DL 136 678.6 132 678.6 DL 140 678.6
-136 678.6 DL 144 678.6 140 678.6 DL 148 678.6 144 678.6 DL 152 678.6 148 678.6
-DL 156 678.6 152 678.6 DL 160 678.6 156 678.6 DL 164 678.6 160 678.6 DL 168
-678.6 164 678.6 DL 172 678.6 168 678.6 DL 176 678.6 172 678.6 DL 180 678.6 176
-678.6 DL 184 678.6 180 678.6 DL 188 678.6 184 678.6 DL 192 678.6 188 678.6 DL
-196 678.6 192 678.6 DL 200 678.6 196 678.6 DL 204 678.6 200 678.6 DL 208 678.6
-204 678.6 DL 212 678.6 208 678.6 DL 216 678.6 212 678.6 DL/F4 8/Times-Roman@0
-SF .045(*An earlier v)93.6 690.6 R .045(ersion of this paper w)-.12 F .044
-(as printed in the Proceedings of the 1994 A)-.08 F .044
-(UUG Queensland Summer T)-.44 F .044(echnical Conference,)-.56 F(Gate)72 700.2
-Q -.08(wa)-.2 G 2(yH).08 G(otel, Brisbane, March 1994.)107.928 700.2 Q F3
-(Changes in Sendmail V)72 756 Q(ersion 8)-1 E(1)499 756 Q EP
-%%Page: 2 2
-%%BeginPageSetup
-BP
-%%EndPageSetup
-/F0 10/Times-Bold@0 SF 294.65(2C)72 60 S(hanges in Sendmail V)378.87 60 Q
-(ersion 8)-1 E/F1 10/Times-Roman@0 SF .151
-(In about the same time frame, attempts were made to clean up and e)112 96 R
-.151(xtend the Simple Mail T)-.15 F(rans-)-.35 E .468
-(port Protocol \(SMTP\) [RFC821].)87 108 R .468(This in)5.468 F -.2(vo)-.4 G
-(lv).2 E .469(ed clari\214cations of some ambiguities in the protocol, and)-.15
-F .085(correction of some problem areas [RFC1123], as well as e)87 120 R .084
-(xtensions for additional functionality \(dubbed)-.15 F 1.052
-(Extended Simple Mail T)87 132 R 1.053
-(ransport Protocol, or ESMTP\) [RFC1425, RFC1426, RFC1427] and a richer)-.35 F
-1.376(set of semantics in the body of messages \(the Multipurpose Internet Mai\
-l Extensions, a.k.a. MIME\))87 144 R .497([RFC1521, RFC1344].)87 156 R .497
-(Neither the ID)5.497 F 2.998(Ag)-.4 G .498(roup nor most v)258.526 156 R .498
-(endors were modifying)-.15 F/F2 10/Times-Italic@0 SF(sendmail)2.998 E F1 .498
-(to conform)2.998 F 1.7(to these ne)87 168 R 4.2(ws)-.25 G 4.2(tandards. It)
-148.23 168 R 1.699(seemed clear that these were `)4.2 F 1.699(`good things')
--.74 F 4.199('t)-.74 G 1.699(hat should be encouraged.)394.483 168 R(Ho)87 180
-Q(we)-.25 E -.15(ve)-.25 G 1.635 -.4(r, s).15 H .835(ince no one w).4 F .835
-(as w)-.1 F .835(orking on a publically a)-.1 F -.25(va)-.2 G .835(ilable v).25
-F .836(ersion of)-.15 F F2(sendmail)3.336 E F1 .836(with these updates,)3.336 F
-(the)87 192 Q 2.5(yw)-.15 G(ere unlik)113.79 192 Q(ely to be widely deplo)-.1 E
-(yed an)-.1 E 2.5(yt)-.15 G(ime in the near future.)274.25 192 Q .466
-(There are, of course, other mail transport agents a)112 208.2 R -.25(va)-.2 G
-.465(ilable, such as).25 F F2 .465(MMDF zmailer smail)2.965 F F1(and)2.965 E F2
-(PP)2.965 E F1(Ho)87 220.2 Q(we)-.25 E -.15(ve)-.25 G .842 -.4(r, n).15 H .042
-(one of these seemed to be g).4 F .043(aining the prominence of)-.05 F F2
-(sendmail)2.543 E F1 2.543(;i)C 2.543(ta)390.518 220.2 S .043
-(ppeared that most compa-)400.281 220.2 R .238(nies w)87 232.2 R .238
-(ould not con)-.1 F -.15(ve)-.4 G .238(rt to another mail transport agent an)
-.15 F 2.737(yt)-.15 G .237(ime in the forseeable future.)327.438 232.2 R(Ho)
-5.237 E(we)-.25 E -.15(ve)-.25 G 1.037 -.4(r, t).15 H(he).4 E(y)-.15 E
-(might be persuaded to con)87 244.2 Q -.15(ve)-.4 G(rt to a ne).15 E(wer v)-.25
-E(ersion of)-.15 E F2(sendmail)2.5 E F1(.)A .841(All of these con)112 260.4 R
-.841(vinced the author to w)-.4 F .841(ork on a updated v)-.1 F .841(ersion of)
--.15 F F2(sendmail)3.342 E F1 .842(for public distrib)3.342 F(u-)-.2 E(tion.)87
-272.4 Q 1.024(The ne)112 288.6 R 3.524(wv)-.25 G 1.023(ersion of)155.858 288.6
-R F2(sendmail)3.523 E F1 1.023(is referred to as v)3.523 F 1.023
-(ersion eight \(V8\).)-.15 F -1.11(Ve)6.023 G 1.023(rsions six and se)1.11 F
--.15(ve)-.25 G 3.523(nw).15 G(ere)491.79 288.6 Q 1.281
-(skipped because of an agreement that all \214les in 4.4BSD w)87 300.6 R 1.281
-(ould be numbered as \2318.1\232.)-.1 F 1.282(Rather than)6.282 F(ha)87 312.6 Q
-2.05 -.15(ve a)-.2 H 4.25(ne).15 G 1.75(xternal v)127.76 312.6 R 1.75
-(ersion number that dif)-.15 F 1.75(fered from the \214le v)-.25 F 1.75
-(ersion numbers,)-.15 F F2(sendmail)4.25 E F1 1.75(just jumped)4.25 F
-(directly to V8.)87 324.6 Q F0 2.5(2. CHANGES)72 348.6 R(IN VERSION EIGHT)2.5 E
-F1 .138(The follo)112 364.8 R .139
-(wing is a summary of the changes between the last commonly a)-.25 F -.25(va)
--.2 G .139(ilable v).25 F .139(ersion of send-)-.15 F(mail from Berk)87 376.8 Q
-(ele)-.1 E 2.5(y\()-.15 G(5.67\) and the latest v)170.9 376.8 Q
-(ersion \(8.6.6\).)-.15 E(Man)112 393 Q 2.5(yo)-.15 G 2.5(ft)142.68 393 S
-(hese are ideas that had been tried in ID)151.29 393 Q(A, b)-.4 E(ut man)-.2 E
-2.5(yo)-.15 G 2.5(ft)363.27 393 S(hem were generalized in V8.)371.88 393 Q F0
-2.5(2.1. P)87 417 R(erf)-.2 E(ormance Enhancements)-.25 E F1 .549
-(Instead of closing SMTP connections immediately)127 433.2 R 3.049(,o)-.65 G
-.549(pen connections are cached for possible)342.135 433.2 R .029(future use.)
-102 445.2 R .029(There is a limit to the number of simultaneous open connectio\
-ns and the idle time of an)5.029 F(y)-.15 E(indi)102 457.2 Q
-(vidual connection.)-.25 E 1.219(This is of best help during queue processing \
-\(since there is the potential of man)127 473.4 R 3.719(yd)-.15 G(if)474.82
-473.4 Q(ferent)-.25 E 1.113(messages going to one site\), although it can also\
- help when processing MX records which aren')102 485.4 R(t)-.18 E
-(handled by MX Piggybacking.)102 497.4 Q 1.258(If tw)127 513.6 R 3.757(oh)-.1 G
-1.257(osts with dif)161.075 513.6 R 1.257
-(ferent names in a single message happen to ha)-.25 F 1.557 -.15(ve t)-.2 H
-1.257(he same set of MX).15 F .94(hosts, the)102 525.6 R 3.44(yc)-.15 G .94
-(an be sent in the same transaction.)153.45 525.6 R -1.11(Ve)5.94 G .94
-(rsion 8 notices this and tries to batch the mes-)1.11 F(sages.)102 537.6 Q
--.15(Fo)127 553.8 S 3.638(re).15 G 1.138(xample, if tw)148.668 553.8 R 3.637
-(os)-.1 G 1.137(ites `)216.42 553.8 R(`foo.com')-.74 E 3.637('a)-.74 G 1.137
-(nd `)286.914 553.8 R(`bar)-.74 E(.com')-.55 E 3.637('a)-.74 G 1.137
-(re both serv)352.408 553.8 R 1.137(ed by UUNET)-.15 F 3.637(,t)-.74 G(he)
-470.513 553.8 Q 3.637(yw)-.15 G(ill)495.66 553.8 Q(ha)102 565.8 Q .557 -.15
-(ve t)-.2 H .257(he same set of MX hosts and will be sent in one transaction.)
-.15 F .258(UUNET will then split the mes-)5.258 F(sage and send it to the tw)
-102 577.8 Q 2.5(oi)-.1 G(ndi)213.28 577.8 Q(vidual hosts.)-.25 E F0 2.5
-(2.2. RFC)87 601.8 R(1123 Changes)2.5 E F1 2.607(An)127 618 S .107
-(umber of changes ha)141.827 618 R .407 -.15(ve b)-.2 H .106(een made to mak)
-.15 F 2.606(es)-.1 G .106(endmail `)321.07 618 R .106
-(`conditionally compliant')-.74 F 2.606('\()-.74 G .106(that is, it)469.058 618
-R(satis\214es all of the MUST clauses and most b)102 630 Q
-(ut not all of the SHOULD clauses in RFC 1123\).)-.2 E
-(The major areas of change are \(numbers are RFC 1123 section numbers\):)127
-646.2 Q 26.5(\2475.2.7 Response)102 662.4 R .565(to RCPT command is f)3.065 F
-3.065(ast. Pre)-.1 F(viously)-.25 E 3.065(,s)-.65 G .565(endmail e)362.295
-662.4 R .565(xpanded all aliases as f)-.15 F(ar)-.1 E .686
-(as it could \212 this could tak)156 674.4 R 3.186(eav)-.1 G .685
-(ery long time, particularly if there were name serv)290.118 674.4 R(er)-.15 E
-3.891(delays. V)156 686.4 R 1.391(ersion 8 only checks for the e)-1.11 F 1.392
-(xistence of an alias and does the e)-.15 F(xpansion)-.15 E(later)156 698.4 Q
-5.176(.I)-.55 G 2.676(td)184.226 698.4 S .176
-(oes still do a DNS lookup if there is an e)194.682 698.4 R .175
-(xplicit host name in the RCPT com-)-.15 F(mand, b)156 710.4 Q
-(ut this time is bounded.)-.2 E EP
-%%Page: 3 3
-%%BeginPageSetup
-BP
-%%EndPageSetup
-/F0 10/Times-Bold@0 SF(Changes in Sendmail V)72 60 Q(ersion 8)-1 E(3)499 60 Q
-/F1 10/Times-Roman@0 SF 26.5(\2475.2.8 Numeric)102 96 R .612
-(IP addresses are logged in Recei)3.112 F -.15(ve)-.25 G .613(d: lines.).15 F
-.613(This helps tracing spoofed mes-)5.613 F(sages.)156 108 Q 21.5
-(\2475.2.17 Self)102 124.2 R .127(domain literal is properly handled.)2.627 F
-(Pre)5.126 E(viously)-.25 E 2.626(,i)-.65 G 2.626(fs)368.196 124.2 S .126
-(omeone sent to user@[1.2.3.4],)378.042 124.2 R .12
-(where 1.2.3.4 is your IP address, the mail w)156 136.2 R .12
-(ould probably be rejected with a `)-.1 F(`con\214gu-)-.74 E(ration error')156
-148.2 Q 2.5('. V)-.74 F(ersion 8 can handle these addresses.)-1.11 E 26.5
-(\2475.3.2 Better)102 164.4 R 1.189(control o)3.69 F -.15(ve)-.15 G 3.689(ri)
-.15 G(ndi)240.088 164.4 Q 1.189(vidual timeouts.)-.25 F 1.189
-(RFC 821 speci\214ed no timeouts.)6.189 F 1.189(Older v)6.189 F(er)-.15 E(-)-.2
-E .002(sions of sendmail had a single timeout, typically set to tw)156 176.4 R
-2.502(oh)-.1 G 2.502(ours. V)398.142 176.4 R .002(ersion 8 allo)-1.11 F .002
-(ws the)-.25 F(con\214guration \214le to set timeouts for v)156 188.4 Q
-(arious SMTP commands indi)-.25 E(vidually)-.25 E(.)-.65 E 26.5
-(\2475.3.3 Error)102 204.6 R 1.06(messages are sent as From:<>.)3.56 F 1.059
-(This w)6.059 F 1.059(as ur)-.1 F 1.059(ged by RFC 821 and reiterated by)-.18 F
-.237(RFC 1123, b)156 216.6 R .237(ut older v)-.2 F .237(ersions of sendmail ne)
--.15 F -.15(ve)-.25 G 2.737(rr).15 G .237(eally did it properly)355.186 216.6 R
-5.237(.V)-.65 G .238(ersion 8 does.)448.254 216.6 R(Ho)156 228.6 Q(we)-.25 E
--.15(ve)-.25 G 1.934 -.4(r, s).15 H 1.134
-(ome systems cannot handle this perfectly le).4 F -.05(ga)-.15 G 3.633(la).05 G
-1.133(ddress; if necessary)402.941 228.6 R 3.633(,y)-.65 G(ou)494 228.6 Q
-(can create a special mailer that uses the `g' \215ag to disable this.)156
-240.6 Q 26.5(\2475.3.3 Error)102 256.8 R 3.212(messages are ne)5.712 F -.15(ve)
--.25 G 5.712(rs).15 G 3.212(ent to <>.)275.628 256.8 R(Pre)8.213 E(viously)-.25
-E 5.713(,s)-.65 G 3.213(endmail w)383.028 256.8 R 3.213(as happ)-.1 F 5.713(yt)
--.1 G 5.713(os)474.957 256.8 S(end)489.56 256.8 Q 6
-(responses-to-responses which sometimes resulted in responses-to-responses-to-)
-156 268.8 R(responses which resulted in ....)156 280.8 Q(you get the idea.)5 E
-26.5(\2475.3.3 Route-addrs)102 297 R .111(\(the ugly `)2.611 F
-(`<@hosta,@hostb:user@hostc>')-.74 E 2.611('s)-.74 G .111(yntax\) are pruned.)
-389.124 297 R .112(RFC 821)5.112 F(ur)156 309 Q 1.001
-(ged the use of this bletcherous syntax.)-.18 F 1
-(RFC 1123 has seen the light and of)6.001 F(\214cially)-.25 E 1.124
-(deprecates them, further ur)156 321 R 1.125(ging that you eliminate all b)-.18
-F 1.125(ut `)-.2 F(`user@hostc')-.74 E 3.625('s)-.74 G 1.125(hould you)462.595
-321 R(recei)156 333 Q 1.698 -.15(ve o)-.25 H 1.398(ne of these things.).15 F
--1.11(Ve)6.398 G 1.398(rsion 8 is slightly more generous than the standards)
-1.11 F .753(suggest; instead of stripping of)156 345 R 3.253(fa)-.25 G .753
-(ll the route addressees, it only strips hosts of)293.115 345 R 3.254(fu)-.25 G
-3.254(pt)487.966 345 S(o)499 345 Q 1.29(the one before the last one kno)156 357
-R 1.289(wn to DNS, thus allo)-.25 F 1.289(wing you to ha)-.25 F 1.589 -.15
-(ve p)-.2 H(seudo-hosts).15 E(such as foo.BITNET)156 369 Q 5(.T)-.74 G
-(he `R' option will turn this of)251.91 369 Q(f.)-.25 E
-(The areas in which sendmail is not `)102 385.2 Q(`unconditionally compliant')
--.74 E 2.5('a)-.74 G(re:)367.43 385.2 Q 26.5(\2475.2.6 Sendmail)102 401.4 R
-(does do header munging.)2.5 E 21.5(\2475.2.10 Sendmail)102 417.6 R(doesn')3.2
-E 3.2(ta)-.18 G -.1(lwa)233.88 417.6 S .7(ys use the e).1 F .701
-(xact SMTP message te)-.15 F .701(xt from RFC 821.)-.15 F .701(This is a)5.701
-F(rather silly requirement.)156 429.6 Q 19(\2475.3.1.1 Sendmail)102 445.8 R
-(doesn')3.512 E 3.512(tg)-.18 G 1.012
-(uarantee only one connect for each host on queue runs.)235.064 445.8 R
-(Connec-)6.011 E(tion caching gi)156 457.8 Q -.15(ve)-.25 G 2.5(sy).15 G
-(ou most of this, b)235.87 457.8 Q(ut it does not pro)-.2 E(vide a guarantee.)
--.15 E 19(\2475.3.1.1 Sendmail)102 474 R(doesn')2.843 E 2.843(ta)-.18 G -.1
-(lwa)233.166 474 S .343(ys pro).1 F .343(vide an adequate limit on concurrenc)
--.15 F 4.144 -.65(y. T)-.15 H .344(hat is, there can).65 F .757(be se)156 486 R
--.15(ve)-.25 G .757(ral independent sendmails running at once.).15 F .757
-(My feeling is that doing an abso-)5.757 F 1.047(lute limit w)156 498 R 1.047
-(ould be a mistak)-.1 F 3.547(e\()-.1 G 1.048(it might result in lost mail\).)
-284.302 498 R(Ho)6.048 E(we)-.25 E -.15(ve)-.25 G 1.848 -.4(r, i).15 H 3.548
-(fy).4 G 1.048(ou use the)461.354 498 R .801(XLA contrib)156 510 R .801
-(uted softw)-.2 F .801(are, most of this will be guaranteed \(b)-.1 F .801
-(ut I don')-.2 F 3.3(tg)-.18 G .8(uarantee the)454.61 510 R(guarantee\).)156
-522 Q F0 2.5(2.3. Extended)87 546 R(SMTP Support)2.5 E F1 -1.11(Ve)127 562.2 S
-.154(rsion 8 includes both sending and recei)1.11 F .155
-(ving support for Extended SMTP support as de\214ned)-.25 F .229(by RFC 1425 \
-\(basic\) and RFC 1427 \(SIZE\); and limited support for RFC 1426 \(BOD)102
-574.2 R 2.729(Y\). The)-.55 F(body)2.729 E .275(support is minimal because the\
- \2318BITMIME\232 body type is not currently adv)102 586.2 R 2.776
-(ertised. Although)-.15 F(such)2.776 E 3.076(ab)102 598.2 S .576
-(ody type will be accepted, it will not be correctly con)114.516 598.2 R -.15
-(ve)-.4 G .576(rted to 7 bits if speaking to a non-8-bit-).15 F(MIME a)102
-610.2 Q -.1(wa)-.15 G(re SMTP serv).1 E(er)-.15 E(.)-.55 E/F2 10/Times-Italic@0
-SF(Sendmail)127 626.4 Q F1 .287(tries to speak ESMTP if you ha)2.787 F .588
--.15(ve t)-.2 H .288(he `a' \215ag set in the \215ags for the mailer descrip-)
-.15 F(tor)102 638.4 Q 3.322(,o)-.4 G 3.322(ri)123.532 638.4 S 3.322(ft)132.964
-638.4 S .822(he other end adv)142.396 638.4 R .822(ertises the f)-.15 F .822
-(act that it speaks ESMTP)-.1 F 5.822(.T)-1.11 G .821
-(his is a non-standard adv)376.446 638.4 R(ertise-)-.15 E(ment:)102 650.4 Q F2
-(sendmail)2.98 E F1 .48(announces \231ESMTP spok)2.98 F .48
-(en here\232 during the initial connection message, and client)-.1 F .587
-(sendmails search for this message.)102 662.4 R .586
-(This creates some problems for some PC-based mailers, which)5.586 F
-(do not understand tw)102 674.4 Q
-(o-line greeting messages as required by RFC 821.)-.1 E EP
-%%Page: 4 4
-%%BeginPageSetup
-BP
-%%EndPageSetup
-/F0 10/Times-Bold@0 SF 294.65(4C)72 60 S(hanges in Sendmail V)378.87 60 Q
-(ersion 8)-1 E 2.5(2.4. Eight-Bit)87 96 R(Clean)2.5 E/F1 10/Times-Roman@0 SF
-(Pre)127 112.2 Q 1.263(vious v)-.25 F 1.263
-(ersions of sendmail used the 0200 bit for quoting.)-.15 F 1.264(This v)6.264 F
-1.264(ersion a)-.15 F -.2(vo)-.2 G 1.264(ids that use.).2 F(Ho)102 124.2 Q(we)
--.25 E -.15(ve)-.25 G 1.119 -.4(r, y).15 H .318
-(ou can set option `7' to get se).4 F -.15(ve)-.25 G 2.818(nb).15 G .318
-(it stripping for compatibility with RFC 821, which is)290.046 124.2 R 2.5(a7)
-102 136.2 S(-bit protocol.)113.94 136.2 Q(This option says `)5 E
-(`strip to 7 bits on input')-.74 E('.)-.74 E(Indi)127 152.4 Q .375
-(vidual mailers can still produce se)-.25 F -.15(ve)-.25 G 2.875(nb).15 G .376
-(it out put using the `7' mailer \215ag.)303.02 152.4 R .376(This \215ag says)
-5.376 F -.74(``)102 164.4 S(strip to 7 bits on output').74 E('.)-.74 E F0 2.5
-(2.5. User)87 188.4 R(Database)2.5 E F1 1.926
-(The User Database \(UDB\) is an as-yet e)127 204.6 R 1.926
-(xperimental attempt to pro)-.15 F 1.925(vide uni\214ed lar)-.15 F(ge-site)-.18
-E .396(name support.)102 216.6 R 1.996 -.8(We a)5.396 H .396
-(re installing it at Berk).8 F(ele)-.1 E .396(y; future v)-.15 F .396
-(ersions may sho)-.15 F 2.897(ws)-.25 G .397(igni\214cant modi\214cations.)
-406.373 216.6 R(Brie\215y)102 228.6 Q 3.583(,U)-.65 G 1.083
-(DB contains a database that is intended to contain all the per)142.433 228.6 R
-1.082(-user information for your)-.2 F -.1(wo)102 240.6 S .172
-(rkgroup, such as people').1 F 2.673(sf)-.55 G .173
-(ull names, their .plan information, their outgoing mail name, and their)222.29
-240.6 R(mail drop.)102 252.6 Q .438(The user database allo)127 268.8 R .438
-(ws you to map both incoming and outgoing addresses, much lik)-.25 F 2.937(eI)
--.1 G -.4(DA)487.46 268.8 S(.).4 E(Ho)102 280.8 Q(we)-.25 E -.15(ve)-.25 G
-1.799 -.4(r, t).15 H .999(he interf).4 F .999(ace is still better with ID)-.1 F
-.999(A; in particular)-.4 F 3.499(,t)-.4 G 1
-(he alias \214le with incoming/outgoing)355.55 280.8 R(marks pro)102 292.8 Q
-(vides better locality of information.)-.15 E F0 2.5(2.6. Impr)87 316.8 R -.1
-(ove)-.18 G 2.5(dB).1 G(IND Support)158.01 316.8 Q F1 .262
-(The BIND support, particularly for MX records, had a number of anno)127 333 R
-.261(ying `)-.1 F(`features')-.74 E 2.761('w)-.74 G(hich)486.78 333 Q(ha)102
-345 Q 1.212 -.15(ve b)-.2 H .912(een remo).15 F -.15(ve)-.15 G 3.412(di).15 G
-3.412(nt)187.116 345 S .912(his release.)198.308 345 R .912(In particular)5.912
-F 3.412(,t)-.4 G .912(hese more tightly bind \(pun intended\) the name)307.916
-345 R(serv)102 357 Q(er to sendmail, so that the name serv)-.15 E
-(er resolution rules are incorporated directly into sendmail.)-.15 E .688
-(The major change has been that the $[ ... $] operator didn')127 373.2 R 3.188
-(tf)-.18 G .688(ully qualify names that were in)376.41 373.2 R
-(DNS as A or MX records.)102 385.2 Q -1.11(Ve)5 G
-(rsion 8 does this quali\214cation.)1.11 E .429(This has pro)127 401.4 R -.15
-(ve)-.15 G 2.929(nt).15 G 2.929(ob)197.147 401.4 S 2.929(ea)210.076 401.4 S
-2.929(na)221.885 401.4 S(nno)234.254 401.4 Q .43
-(yance in Sun shops, who often still run without BIND support.)-.1 F(Ho)102
-413.4 Q(we)-.25 E -.15(ve)-.25 G 1.001 -.4(r, i).15 H 2.701(ti).4 G 2.701(sr)
-153.842 413.4 S .201
-(eally critical that this be supported, since MX records are mandatory)163.763
-413.4 R 5.2(.I)-.65 G 2.7(nS)450.26 413.4 S .2(unOS you)463.52 413.4 R .101
-(can choose either MX support or NIS support, b)102 425.4 R .101(ut not both.)
--.2 F .101(This is \214x)5.101 F .101(ed in Solaris, and some)-.15 F/F2 10
-/Times-Italic@0 SF(send-)2.602 E(mail)102 437.4 Q F1(support to allo)2.5 E 2.5
-(wt)-.25 G(his in SunOS should be forthcoming in a future release.)192.31 437.4
-Q F0 2.5(2.7. K)87 461.4 R(ey)-.25 E(ed Files)-.1 E F1 .242(Generalized k)127
-477.6 R -.15(ey)-.1 G .242(ed \214les is an idea tak).15 F .241
-(en directly from ID)-.1 F 2.741(As)-.4 G .241
-(endmail \(albeit with a completely)368.606 477.6 R(dif)102 489.6 Q
-(ferent implementation\).)-.25 E(The)5 E 2.5(yc)-.15 G(an be useful on lar)
-239.63 489.6 Q(ge sites.)-.18 E -1.11(Ve)127 505.8 S
-(rsion 8 includes the follo)1.11 E(wing b)-.25 E(uilt-in map classes:)-.2 E
-33.72(dbm Support)102 522 R(for the ndbm\(3\) library)2.5 E(.)-.65 E 33.17
-(hash Support)102 538.2 R 1.229(for the `)3.729 F(`Hash')-.74 E 3.729('t)-.74 G
-1.229(ype from the ne)261.636 538.2 R 3.729(wB)-.25 G(erk)345.732 538.2 Q(ele)
--.1 E 3.729(yd)-.15 G 1.229(b\(3\) library)383.641 538.2 R 6.23(.t)-.65 G 1.23
-(his library pro-)441.55 538.2 R 4.094(vides substantially better database sup\
-port than ndbm\(3\), including in-memory)156 550.2 R
-(caching, arbitrarily long k)156 562.2 Q -.15(ey)-.1 G 2.5(sa).15 G(nd v)279.89
-562.2 Q(alues, and better disk utilization.)-.25 E 31.51(btree Support)102
-578.4 R .547(for the `)3.047 F(`B-T)-.74 E(ree')-.35 E 3.047('t)-.74 G .547
-(ype from the ne)266.328 578.4 R 3.048(wB)-.25 G(erk)347.698 578.4 Q(ele)-.1 E
-3.048(yd)-.15 G .548(b\(3\) library)384.926 578.4 R 5.548(.B)-.65 G(-T)445.362
-578.4 Q .548(rees pro)-.35 F(vide)-.15 E .521(better clustering than Hashed \
-\214les if you are fetching lots of records that ha)156 590.4 R .821 -.15(ve s)
--.2 H(imilar).15 E -.1(ke)156 602.4 S(ys, such as searching a dictionary for w)
--.05 E(ords be)-.1 E(ginning with `)-.15 E(`detr')-.74 E('.)-.74 E 39.83
-(nis Support)102 618.6 R(for NIS \(a.k.a. YP\) maps.)2.5 E
-(NIS+ is not supported in this v)5 E(ersion.)-.15 E 34.83(host Support)102
-634.8 R(for DNS lookups.)2.5 E 19.84(dequote A)102 651 R -.74(``)2.642 G
-(pseudo-map').74 E 2.642('\()-.74 G .142(that is, once that does not ha)232.554
-651 R .442 -.15(ve a)-.2 H .442 -.15(ny ex).15 H .142(ternal data\) that allo)
-.15 F .142(ws a con-)-.25 F .099
-(\214guration \214le to break apart a quoted string in the address.)156 663 R
-.098(This is necessary primarily)5.098 F .726
-(for DECnet addresses, which often ha)156 675 R 1.026 -.15(ve q)-.2 H .726
-(uoted addresses that need to be unwrapped).15 F(on g)156 687 Q(ate)-.05 E -.1
-(wa)-.25 G(ys.).1 E EP
-%%Page: 5 5
-%%BeginPageSetup
-BP
-%%EndPageSetup
-/F0 10/Times-Bold@0 SF(Changes in Sendmail V)72 60 Q(ersion 8)-1 E(5)499 60 Q
-2.5(2.8. Multi-W)87 96 R(ord Classes & Macr)-.75 E(os in Classes)-.18 E/F1 10
-/Times-Roman@0 SF(Classes can no)127 112.2 Q 2.5(wb)-.25 G 2.5(em)200.35 112.2
-S(ultiple w)215.07 112.2 Q 2.5(ords. F)-.1 F(or e)-.15 E(xample,)-.15 E
-(CShofmann.CS.Berk)142 128.4 Q(ele)-.1 E -.65(y.)-.15 G(EDU).65 E(allo)102
-144.6 Q 2.395(ws you to match the entire string `)-.25 F(`hofmann.CS.Berk)-.74
-E(ele)-.1 E -.65(y.)-.15 G(EDU').65 E 4.894('u)-.74 G 2.394
-(sing the single construct)399.878 144.6 R -.74(``)102 156.6 S($=S').74 E('.)
--.74 E(Class de\214nitions are no)127 172.8 Q 2.5(wa)-.25 G(llo)234.52 172.8 Q
-(wed to include macros \212 for e)-.25 E(xample:)-.15 E(Cw$k)142 189 Q(is le)
-102 205.2 Q -.05(ga)-.15 G(l.).05 E F0 2.5(2.9. IDENT)87 229.2 R(Pr)2.5 E
-(otocol Support)-.18 E F1 .633
-(The IDENT protocol as de\214ned in RFC 1413 [RFC1413] is supported.)127 245.4
-R(Ho)5.633 E(we)-.25 E -.15(ve)-.25 G 1.433 -.4(r, m).15 H(an).4 E 3.134(ys)
--.15 G(ys-)491.78 245.4 Q .909(tems ha)102 257.4 R 1.209 -.15(ve a T)-.2 H .909
-(CP/IP b).15 F .908
-(ug that renders this useless, and the feature must be turned of)-.2 F 3.408
-(f. Roughly)-.25 F 3.408(,i)-.65 G(f)500.67 257.4 Q 8.538
-(one of these system recei)102 269.4 R -.15(ve)-.25 G 11.038(sa\231).15 G 8.539
-(No route to host\232 message \(ICMP message)280.568 269.4 R(ICMP_UNREA)102
-281.4 Q .829(CH_HOST\) on)-.4 F/F2 10/Times-Italic@0 SF(any)3.329 E F1 .828
-(connection, all connections to that host are closed.)3.329 F .828
-(Some \214re-)5.828 F -.1(wa)102 293.4 S .087
-(lls return this error if you try to connect to the IDENT port, so you can').1
-F 2.587(tr)-.18 G(ecei)408.889 293.4 Q .387 -.15(ve e)-.25 H .087
-(mail from these).15 F 1.712(hosts on these systems.)102 305.4 R(It')6.712 E
-4.212(sp)-.55 G 1.712(ossible that if the \214re)228.62 305.4 R -.1(wa)-.25 G
-1.712(ll used a more speci\214c message \(such as).1 F(ICMP_UNREA)102 317.4 Q
-(CH_PR)-.4 E -1.88 -.4(OT O)-.4 H 72.325(COL, ICMP_UNREA).4 F(CH_POR)-.4 E
-74.825(To)-.6 G(r)500.67 317.4 Q(ICMP_UNREA)102 329.4 Q(CH_NET_PR)-.4 E
-(OHIB\) it w)-.4 E(ould w)-.1 E(ork, b)-.1 E(ut this hasn')-.2 E 2.5(tb)-.18 G
-(een v)375.62 329.4 Q(eri\214ed.)-.15 E .678(IDENT protocol support cannot be \
-used on 4.3BSD, Apollo DomainOS, Apple A/UX, Con-)127 345.6 R -.15(vex)102
-357.6 S .949(OS, Data General DG/UX, HP-UX, Sequent Dynix, or Ultrix 4.x, x).15
-F/F3 10/Symbol SF<a3>3.449 E F1 3.449(3. It)3.449 F .949(seems to w)3.449 F
-.949(ork on)-.1 F
-(4.4BSD, IBM AIX 3.x, OSF/1, SGI IRIX, Solaris, SunOS, and Ultrix 4.4.)102
-369.6 Q F0 2.5(2.10. Separate)87 393.6 R(En)2.5 E -.1(ve)-.4 G(lope/Header Pr)
-.1 E(ocessing)-.18 E F1 .854
-(Since the From: line is passed in separately from the en)127 409.8 R -.15(ve)
--.4 G .854(lope sender).15 F 3.354(,t)-.4 G .854(hese ha)420.978 409.8 R 1.154
--.15(ve b)-.2 H .854(oth been).15 F .427
-(made visible; the $g macro is set to the en)102 421.8 R -.15(ve)-.4 G .428
-(lope sender during processing of mailer ar).15 F .428(gument v)-.18 F(ec-)-.15
-E(tors and the header sender during processing of headers.)102 433.8 Q .085
-(It is also possible to specify separate per)127 450 R .085(-mailer en)-.2 F
--.15(ve)-.4 G .084(lope and header processing.).15 F .084(The Sender)5.084 F(-)
--.2 E -.55(RW)102 462 S 1.085(Set and RecipientR).55 F 1.085(Wset ar)-.55 F
-1.085(guments for mailers can be speci\214ed as `)-.18 F(`en)-.74 E -.15(ve)-.4
-G(lope/header').15 E 3.585('t)-.74 G 3.585(og)478.595 462 S -2.15 -.25(iv e)
-492.18 462 T(dif)102 474 Q(ferent re)-.25 E(writings for en)-.25 E -.15(ve)-.4
-G(lope v).15 E(ersus header addresses.)-.15 E F0 2.5(2.11. Owner)87 498 R
-(-List Pr)-.37 E(opagates to En)-.18 E -.1(ve)-.4 G(lope).1 E F1 1.168
-(When an alias has an associated o)127 514.2 R(wner)-.25 E 1.168
-(-list name, that alias is used to change the en)-.2 F -.15(ve)-.4 G(lope).15 E
-(sender address.)102 526.2 Q(This will cause do)5 E
-(wnstream errors to be returned to that o)-.25 E(wner)-.25 E(.)-.55 E 1.813
-(Some people \214nd this confusing because the en)127 542.4 R -.15(ve)-.4 G
-1.813(lope sender is what appears in the \214rst).15 F -.74(``)102 554.4 S
-(From_').74 E 3.127('l)-.74 G .627(ine in UNIX messages \(that is, the line be)
-146.417 554.4 R .627(ginning `)-.15 F(`From<space>')-.74 E 3.127('i)-.74 G .627
-(nstead of `)424.797 554.4 R(`From:')-.74 E(';)-.74 E .502
-(the latter is the header from, which)102 566.4 R F2(does)3.002 E F1 .503
-(indicate the sender of the message\).)3.002 F .503(In pre)5.503 F .503
-(vious v)-.25 F(ersions,)-.15 E F2(sendmail)102 578.4 Q F1 .057(has tried to a)
-2.557 F -.2(vo)-.2 G .057(id changing the en).2 F -.15(ve)-.4 G .056
-(lope sender for back compatibility with UNIX con).15 F -.15(ve)-.4 G(n-).15 E
-.177(tion; at this point that back compatibility is creating too man)102 590.4
-R 2.678(yp)-.15 G .178(roblems, and it is necessary to mo)357.972 590.4 R -.15
-(ve)-.15 G(forw)102 602.4 Q(ard into the 1980s.)-.1 E F0 2.5(2.12. Command)87
-626.4 R(Line Flags)2.5 E F1(The)127 642.6 Q F0<ad42>2.5 E F1
-(\215ag has been added to pass in body type information.)2.5 E(The)127 658.8 Q
-F0<ad70>3.057 E F1 .557
-(\215ag has been added to pass in protocol information that w)3.057 F .557
-(as pre)-.1 F .556(viously passed in by)-.25 F(de\214ning the)102 670.8 Q F0
-($r)2.5 E F1(and)2.5 E F0($s)2.5 E F1(macros.)2.5 E(The)127 687 Q F0<ad58>2.6 E
-F1 .1(\215ag has been added to allo)2.6 F 2.6(wl)-.25 G .1
-(ogging of all protocol in and out of sendmail for deb)279.89 687 R(ug-)-.2 E
-2.732(ging. Y)102 699 R .232(ou can set \231\255X \214lename\232 and a complet\
-e transcript will be logged in that \214le.)-1.1 F .231(This gets big)5.231 F
--.1(fa)102 711 S(st: the option is only for deb).1 E(ugging.)-.2 E EP
-%%Page: 6 6
-%%BeginPageSetup
-BP
-%%EndPageSetup
-/F0 10/Times-Bold@0 SF 294.65(6C)72 60 S(hanges in Sendmail V)378.87 60 Q
-(ersion 8)-1 E/F1 10/Times-Roman@0 SF(The)127 96 Q F0<ad71>4.006 E F1 1.507(\
-\215ag can limit limit a queue run to speci\214c recipients, senders, or queue\
- ids using)4.006 F
-(\255qRsubstring, \255qSsubstring, or \255qIsubstring respecti)102 108 Q -.15
-(ve)-.25 G(ly).15 E(.)-.65 E F0 2.5(2.13. New)87 132 R(Con\214guration Line T)
-2.5 E(ypes)-.74 E F1 .674(The `T' \(T)127 148.2 R .674
-(rusted users\) con\214guration line has been deleted.)-.35 F .674
-(It will still be accepted b)5.674 F .674(ut will)-.2 F(be ignored.)102 160.2 Q
-(The `K' line has been added to declare database maps.)127 176.4 Q
-(The `V' line has been added to declare the con\214guration v)127 192.6 Q
-(ersion le)-.15 E -.15(ve)-.25 G(l.).15 E(The `M' \(mailer\) line tak)127 208.8
-Q(es a D= \214eld to specify e)-.1 E -.15(xe)-.15 G(cution directory).15 E(.)
--.65 E F0 2.5(2.14. New)87 232.8 R(and Extended Options)2.5 E F1(Se)127 249 Q
--.15(ve)-.25 G .9(ral ne).15 F 3.4(wo)-.25 G .9(ptions ha)184.8 249 R 1.2 -.15
-(ve b)-.2 H .9(een added, man).15 F 3.4(yt)-.15 G 3.4(os)314.89 249 S .9
-(upport ne)327.18 249 R 3.4(wf)-.25 G .9(eatures, others to allo)379.83 249 R
-3.4(wt)-.25 G(uning)481.22 249 Q(that w)102 261 Q(as pre)-.1 E(viously a)-.25 E
--.25(va)-.2 G(ilable only by recompiling.).25 E(Brie\215y:)5 E 28.78(AT)102
-277.2 S .099(he alias \214le speci\214cation can no)144.11 277.2 R 2.599(wb)
--.25 G 2.599(eal)286.654 277.2 S .099(ist of alias \214les.)303.512 277.2 R
-.098(Also, the con\214guration can spec-)5.099 F(ify a class of \214le.)138
-289.2 Q -.15(Fo)5 G 2.5(re).15 G(xample, to search the NIS aliases, use \231O)
-232.13 289.2 Q(Anis:mail.aliases\232.)-.35 E 31(bI)102 305.4 S
-(nsist on a minimum number of disk blocks.)141.33 305.4 Q 29.33(CD)102 321.6 S
-(eli)145.22 321.6 Q -.15(ve)-.25 G .24(ry checkpoint interv).15 F 2.74
-(al. Checkpoint)-.25 F .24(the queue \(to a)2.74 F -.2(vo)-.2 G .24
-(id duplicate deli).2 F -.15(ve)-.25 G .24(ries\) e).15 F -.15(ve)-.25 G .24
-(ry C).15 F(addresses.)138 333.6 Q 29.89(ED)102 349.8 S(ef)145.22 349.8 Q .712
-(ault error message.)-.1 F .711
-(This message \(or the contents of the indicated \214le\) are prepended)5.712 F
-(to error messages.)138 361.8 Q 28.78(GE)102 378 S .785(nable GECOS matching.)
-144.11 378 R .785(If you can')5.785 F 3.285<748c>-.18 G .786
-(nd a local user name and this option is enabled,)307.51 378 R .59
-(do a sequential scan of the passwd \214le to match ag)138 390 R .589
-(ainst full names.)-.05 F(Pre)5.589 E .589(viously a compile)-.25 F(option.)138
-402 Q 31(hM)102 418.2 S(aximum hop count.)146.89 418.2 Q(Pre)5 E
-(viously this w)-.25 E(as compiled in.)-.1 E 32.67(IT)102 434.4 S
-(his option has been e)144.11 434.4 Q(xtended to allo)-.15 E 2.5(ws)-.25 G
-(etting of resolv)300.64 434.4 Q(er parameters.)-.15 E 33.22(jS)102 450.6 S
-(end errors in MIME-encapsulated format.)143.56 450.6 Q 32.11(JF)102 466.8 S
-(orw)143.41 466.8 Q(ard \214le path.)-.1 E(Where to search for .forw)5 E
-(ard \214les \212 def)-.1 E(aults to $HOME/.forw)-.1 E(ard.)-.1 E 31(kC)102 483
-S .05(onnection cache size.)144.67 483 R .05
-(The total number of connections that will be k)5.05 F .05(ept open at an)-.1 F
-2.55(yt)-.15 G(ime.)486.5 483 Q 28.78(KC)102 499.2 S 1.395
-(onnection cache lifetime.)144.67 499.2 R 1.395(The amount of time an)6.395 F
-3.895(yc)-.15 G 1.394(onnection will be permitted to sit)364.53 499.2 R(idle.)
-138 511.2 Q 33.22(lE)102 527.4 S .333(nable Errors-T)144.11 527.4 R .333
-(o: header)-.8 F 5.334(.T)-.55 G .334
-(hese headers violate RFC 1123; this option is included to pro-)252.89 527.4 R
-(vide back compatibility with old v)138 539.4 Q(ersions of sendmail.)-.15 E
-28.78(OI)102 555.6 S(ncoming daemon options \(e.g., use alternate SMTP port\).)
-141.33 555.6 Q 31(pP)102 571.8 S(ri)143.56 571.8 Q -.25(va)-.25 G .3 -.15(cy o)
-.25 H 2.5(ptions. These).15 F(can be used to mak)2.5 E 2.5(ey)-.1 G
-(our SMTP serv)322.22 571.8 Q(er less friendly)-.15 E(.)-.65 E 32.67(rT)102 588
-S .67(his option has been e)144.11 588 R .67(xtended to allo)-.15 F 3.17<778c>
--.25 G .67(ner grained control o)307 588 R -.15(ve)-.15 G 3.17(rt).15 G 3.17
-(imeouts. F)411.02 588 R .67(or e)-.15 F(xample,)-.15 E
-(you can set the timeout for SMTP commands indi)138 600 Q(vidually)-.25 E(.)
--.65 E 29.33(RD)102 616.2 S(on')145.22 616.2 Q 11.797(tp)-.18 G 9.297
-(rune route-addrs.)177.947 616.2 R(Normally)269.851 616.2 Q 11.797(,i)-.65 G
-11.797(fv)324.608 616.2 S 9.297(ersion 8 sees an address lik)344.585 616.2 R(e)
--.1 E 1.256("<@hostA,@hostB:user@hostC>, sendmail will try to strip of)138
-628.2 R 3.755(fa)-.25 G 3.755(sm)406.48 628.2 S 1.255(uch as it can \(up to)
-421.905 628.2 R(user@hostC\) as suggested by RFC 1123.)138 640.2 Q
-(This option disables that beha)5 E(viour)-.2 E(.)-.55 E 29.89(TT)102 656.4 S
-1.485(he \231Return T)144.11 656.4 R 3.985(oS)-.8 G 1.485
-(ender\232 timeout has been e)213.035 656.4 R 1.485(xtended to allo)-.15 F
-3.986(ws)-.25 G 1.486(peci\214cation of a w)399.942 656.4 R(arning)-.1 E .789
-(message interv)138 668.4 R .789
-(al, typically something on the order of four hours.)-.25 F .788
-(If a message cannot be)5.788 F(deli)138 680.4 Q -.15(ve)-.25 G 1.245
-(red in that interv).15 F 1.245(al, a w)-.25 F 1.245
-(arning message is sent back to the sender b)-.1 F 1.246(ut the message)-.2 F
-(continues to be tried.)138 692.4 Q 28.78(UU)102 708.6 S(ser database spec.)
-145.22 708.6 Q(This is still e)5 E(xperimental.)-.15 E EP
-%%Page: 7 7
-%%BeginPageSetup
-BP
-%%EndPageSetup
-/F0 10/Times-Bold@0 SF(Changes in Sendmail V)72 60 Q(ersion 8)-1 E(7)499 60 Q
-/F1 10/Times-Roman@0 SF 28.78(VF)102 96 S .758(allback `)143.41 96 R(`MX')-.74
-E 3.258('h)-.74 G 3.258(ost. This)211.756 96 R .757
-(can be thought of as an MX host that applies to all addresses)3.258 F
-(that has a v)138 108 Q(ery high preference v)-.15 E
-(alue \(that is, use it only if e)-.25 E -.15(ve)-.25 G(rything else f).15 E
-(ails\).)-.1 E 28.78(wI)102 124.2 S 3.066(fs)141.33 124.2 S .566(et, assume th\
-at if you are the best MX host for a host, you should send directly to that)
-151.616 124.2 R 3.213(host. This)138 136.2 R .713
-(is intended for compatibility with UIUC sendmail, and may ha)3.213 F 1.013
--.15(ve s)-.2 H .712(ome use on).15 F(\214re)138 148.2 Q -.1(wa)-.25 G(lls.).1
-E 31(7D)102 164.4 S 2.758(on)145.22 164.4 S .258(ot run eight bit clean.)
-157.978 164.4 R -.7(Te)5.258 G(chnically).7 E 2.758(,y)-.65 G .258(ou ha)
-305.656 164.4 R .558 -.15(ve t)-.2 H 2.758(oa).15 G .259
-(ssert this option to be RFC 821 com-)354.68 164.4 R(patible.)138 176.4 Q F0
-2.5(2.15. New)87 200.4 R(Mailer De\214nitions)2.5 E F1 21.75(L= Set)102 216.6 R
-.93(the allo)3.43 F -.1(wa)-.25 G .93(ble line length.).1 F .93
-(In V5, the L mailer \215ag implied a line length limit of 990)5.93 F
-(characters; this is no)138 228.6 Q 2.5(ws)-.25 G(ettable to an arbitrary v)
-233.29 228.6 Q(alue.)-.25 E 17.86(F=a T)102 244.8 R(ry to use ESMTP)-.35 E 5
-(.I)-1.11 G 2.5(tw)222.65 244.8 S(ill f)235.15 244.8 Q
-(all back to SMTP if the initial EHLO pack)-.1 E(et is rejected.)-.1 E 17.3
-(F=b Ensure)102 261 R 2.5(ab)2.5 G(lank line at the end of messages.)180.21 261
-Q(Useful on the *\214le* mailer)5 E(.)-.55 E 17.86(F=c Strip)102 277.2 R .68(a\
-ll comments from addresses; this should only be used as a last resort when dea\
-ling)3.18 F(with crank)138 289.2 Q 2.5(ym)-.15 G(ailers.)195.62 289.2 Q 17.3
-(F=g Ne)102 305.4 R -.15(ve)-.25 G 2.88(ru).15 G .38
-(se the null sender as the en)169.91 305.4 R -.15(ve)-.4 G .379(lope sender).15
-F 2.879(,e)-.4 G -.15(ve)343.645 305.4 S 2.879(nw).15 G .379(hen running SMTP)
-368.034 305.4 R 5.379(.T)-1.11 G .379(his violates)458.341 305.4 R(RFC 1123.)
-138 317.4 Q 17.3(F=7 Strip)102 333.6 R(all output to this mailer to 7 bits.)2.5
-E 16.19(F=L Used)102 349.8 R .198
-(to set the line limit to 990 bytes for SMTP compatibility)2.697 F 5.198(.I)
--.65 G 2.698(tn)398.622 349.8 S .698 -.25(ow d)409.1 349.8 T .198
-(oes that only if the).25 F(L= k)138 361.8 Q -.15(ey)-.1 G
-(letter is not speci\214ed.).15 E
-(This \215ag is obsolete and should not be used.)5 E F0 2.5(2.16. New)87 385.8
-R(or Changed Pr)2.5 E(e-De\214ned Macr)-.18 E(os)-.18 E F1 23.5($k UUCP)102 402
-R(node name from uname\(2\).)2.5 E 20.72($m Domain)102 418.2 R
-(part of our full hostname.)2.5 E 23.5($_ RFC)102 434.4 R(1413-pro)2.5 E
-(vided sender address.)-.15 E 21.28($w Pre)102 450.6 R .148(viously w)-.25 F
-.148(as sometimes the full domain name, sometimes just the \214rst w)-.1 F
-2.647(ord. No)-.1 F 2.647(wg)-.25 G(uar)488.1 450.6 Q(-)-.2 E
-(anteed to be the \214rst w)138 462.6 Q
-(ord of the domain name \(i.e., the host name\).)-.1 E 25.72($j Pre)102 478.8 R
-.193(viously had to be de\214ned \212 it is no)-.25 F 2.693(wp)-.25 G .194
-(rede\214ned to be the full domain name, if that can)310.067 478.8 R
-(be determined.)138 490.8 Q(That is, it is equi)5 E -.25(va)-.25 G(lent to $w)
-.25 E(.$m.)-.65 E F0 2.5(2.17. New)87 514.8 R(and Changed Classes)2.5 E F1
-17.86($=k Initialized)102 531 R(to contain $k.)2.5 E 15.64($=w No)102 547.2 R
-3.069(wi)-.25 G .569
-(ncludes \231[1.2.3.4]\232 \(where 1.2.3.4 is your IP address\) to allo)163.039
-547.2 R 3.068(wt)-.25 G .568(he con\214guration \214le)422.314 547.2 R
-(to recognize your o)138 559.2 Q(wn IP address.)-.25 E F0 2.5(2.18. New)87
-583.2 R(Rewriting T)2.5 E(ok)-.92 E(ens)-.1 E F1(The)127 599.4 Q F0($&)3.25 E
-F1 .75(construct has been adopted from ID)3.25 F 3.25(At)-.4 G 3.25(od)322
-599.4 S .75(efer macro e)335.25 599.4 R -.25(va)-.25 G 3.25(luation. Normally)
-.25 F 3.25(,m)-.65 G(acros)482.9 599.4 Q .476
-(in rulesets are bound when the rule is \214rst parsed during startup.)102
-611.4 R .476(Some macros change during pro-)5.476 F .046
-(cessing and are uninteresting during startup.)102 623.4 R(Ho)5.046 E(we)-.25 E
--.15(ve)-.25 G .846 -.4(r, t).15 H .047
-(hat macro can be referenced using \231$&x\232 to).4 F(defer the e)102 635.4 Q
--.25(va)-.25 G(ulation of $x until the rule is processed.).25 E(The tok)127
-651.6 Q(ens)-.1 E F0($\()2.5 E F1(and)2.5 E F0($\))2.5 E F1(ha)2.5 E .3 -.15
-(ve b)-.2 H(een added to allo).15 E 2.5(ws)-.25 G(peci\214cation of map re)
-319.59 651.6 Q(writing.)-.25 E -1.11(Ve)127 667.8 S 1.499(rsion 8 allo)1.11 F
-(ws)-.25 E F0($@)3.999 E F1 1.499
-(on the Left Hand Side of an `R' line to match zero tok)3.999 F 3.998
-(ens. This)-.1 F(is)3.998 E(intended to be used to match the null input.)102
-679.8 Q EP
-%%Page: 8 8
-%%BeginPageSetup
-BP
-%%EndPageSetup
-/F0 10/Times-Bold@0 SF 294.65(8C)72 60 S(hanges in Sendmail V)378.87 60 Q
-(ersion 8)-1 E 2.5(2.19. Bigger)87 96 R(Defaults)2.5 E/F1 10/Times-Roman@0 SF
--1.11(Ve)127 112.2 S 1.283(rsion 8 allo)1.11 F 1.284
-(ws up to 100 rulesets instead of 30.)-.25 F 1.284
-(It is recommended that rulesets 0\2559 be)6.284 F(reserv)102 124.2 Q
-(ed for sendmail')-.15 E 2.5(sd)-.55 G(edicated use in future releases.)202.66
-124.2 Q(The total number of MX records that can be used has been raised to 20.)
-127 140.4 Q .335(The number of queued messages that can be handled at one time\
- has been raised from 600 to)127 156.6 R(1000.)102 168.6 Q F0 2.5(2.20. Differ)
-87 192.6 R(ent Default T)-.18 E(uning P)-.92 E(arameters)-.1 E F1 -1.11(Ve)127
-208.8 S .8(rsion 8 has changed the def)1.11 F .8
-(ault parameters for tuning queue costs to mak)-.1 F 3.3(et)-.1 G .8
-(he number of)449.08 208.8 R .712(recipients more important than the size of t\
-he message \(for small messages\).)102 220.8 R .712(This is reasonable if)5.712
-F(you are connected with reasonably f)102 232.8 Q(ast links.)-.1 E F0 2.5
-(2.21. A)87 256.8 R(uto-Quoting in Addr)-.5 E(esses)-.18 E F1(Pre)127 273 Q
-(viously)-.25 E 3.2(,t)-.65 G .701(he `)177.36 273 R .701
-(`Full Name <email address>')-.74 F 3.201('s)-.74 G .701(yntax w)322.025 273 R
-.701(ould generate incorrect protocol out-)-.1 F .006(put if `)102 285 R .006
-(`Full Name')-.74 F 2.506('h)-.74 G .006(ad special characters such as dot.)
-187.754 285 R .005(This v)5.006 F .005(ersion puts quotes around such names.)
--.15 F F0 2.5(2.22. Symbolic)87 309 R(Names On Err)2.5 E(or Mailer)-.18 E F1
-(Se)127 325.2 Q -.15(ve)-.25 G(ral names ha).15 E .3 -.15(ve b)-.2 H(een b).15
-E(uilt in to the $@ portion of the $#error mailer)-.2 E 5(.F)-.55 G(or e)428.96
-325.2 Q(xample:)-.15 E($#error $@NOHOST $: Host unkno)142 341.4 Q(wn)-.25 E
-(Prints the indicated message and sets the e)102 357.6 Q(xit status of)-.15 E
-/F2 10/Times-Italic@0 SF(sendmail)2.5 E F1(to)2.5 E/F3 9/Times-Roman@0 SF
-(EX_NOHOST)2.5 E F1(.)A F0 2.5(2.23. New)87 381.6 R(Built-In Mailers)2.5 E F1
--1 -.8(Tw o)127 397.8 T(ne)3.901 E 3.101(wm)-.25 G .601(ailers, *\214le* and *\
-include*, are included to de\214ne options when mailing to a \214le)174.822
-397.8 R(or a :include: \214le respecti)102 409.8 Q -.15(ve)-.25 G(ly).15 E 5
-(.P)-.65 G(re)232.88 409.8 Q(viously these were o)-.25 E -.15(ve)-.15 G
-(rloaded on the local mailer).15 E(.)-.55 E F0 2.5(2.24. SMTP)87 433.8 R
-(VRFY Doesn't Expand)2.5 E F1(Pre)127 450 Q 1.438(vious v)-.25 F 1.438
-(ersions of sendmail treated VRFY and EXPN the same.)-.15 F 1.437(In this v)
-6.437 F 1.437(ersion, VRFY)-.15 F(doesn')102 462 Q 2.5(te)-.18 G
-(xpand aliases or follo)138.05 462 Q 2.5(w.)-.25 G(forw)235.84 462 Q
-(ard \214les.)-.1 E .663(As an optimization, if you run with your def)127 478.2
-R .664(ault deli)-.1 F -.15(ve)-.25 G .664(ry mode being queue-only).15 F 3.164
-(,t)-.65 G .664(he RCPT)466.386 478.2 R 1.09
-(command will also not chase aliases and .forw)102 490.2 R 1.09(ard \214les.)
--.1 F 1.09(It will chase them when it processes the)6.09 F 2.5(queue. This)102
-502.2 R(speeds up RCPT processing.)2.5 E F0 2.5(2.25. [IPC])87 526.2 R
-(Mailers Allo)2.5 E 2.5(wM)-.1 G(ultiple Hosts)210.49 526.2 Q F1 .099
-(When an address resolv)127 542.4 R .099(es to a mailer that has `)-.15 F
-(`[IPC]')-.74 E 2.599('a)-.74 G 2.6(si)353.52 542.4 S .1(ts `)362.79 542.4 R
-(`P)-.74 E(ath')-.15 E .1(', the $@ part \(host name\))-.74 F .138
-(can be a colon-separated list of hosts instead of a single hostname.)102 554.4
-R .137(This asks sendmail to search the)5.137 F .16
-(list for the \214rst entry that is a)102 566.4 R -.25(va)-.2 G .16(ilable e)
-.25 F .161(xactly as though it were an MX record.)-.15 F .161
-(The intent is to route)5.161 F .738(internal traf)102 578.4 R .738
-(\214c through internal netw)-.25 F .738
-(orks without publishing an MX record to the net.)-.1 F .737(MX e)5.737 F
-(xpan-)-.15 E(sion is still done on the indi)102 590.4 Q(vidual items.)-.25 E
-F0 2.5(2.26. Aliases)87 614.4 R(Extended)2.5 E F1 .298
-(The implementation has been mer)127 630.6 R .298(ged with maps.)-.18 F .299
-(Among other things, this supports multiple)5.298 F
-(alias \214les and NIS-based aliases.)102 642.6 Q -.15(Fo)5 G 2.5(re).15 G
-(xample:)258.34 642.6 Q -.35(OA)142 658.8 S(/etc/aliases,nis:mail.aliases).35 E
-(will search \214rst the local database \231/etc/aliases\232 follo)102 675 Q
-(wed by the NIS map)-.25 E EP
-%%Page: 9 9
-%%BeginPageSetup
-BP
-%%EndPageSetup
-/F0 10/Times-Bold@0 SF(Changes in Sendmail V)72 60 Q(ersion 8)-1 E(9)499 60 Q
-2.5(2.27. P)87 96 R(ortability and Security Enhancements)-.2 E/F1 10
-/Times-Roman@0 SF 2.5(An)127 112.2 S(umber of internal changes ha)141.72 112.2
-Q .3 -.15(ve b)-.2 H(een made to enhance portability).15 E(.)-.65 E(Se)127
-128.4 Q -.15(ve)-.25 G(ral \214x).15 E(es ha)-.15 E .3 -.15(ve b)-.2 H
-(een made to increase the paranoia f).15 E(actor)-.1 E(.)-.55 E .46
-(In particular)127 144.6 R 2.96(,t)-.4 G .46(he permissions required for .forw)
-184.45 144.6 R .46(ard and :include: \214les ha)-.1 F .76 -.15(ve b)-.2 H .46
-(een tightened up).15 F(considerably)102 156.6 Q 5.182(.V)-.65 G 2.683(5w)
-167.352 156.6 S .183(ould pretty much read an)182.155 156.6 R 2.683<798c>-.15 G
-.183(le it could get to as root, which e)295.96 156.6 R .183(xposed some secu-)
--.15 F 1.02(rity holes.)102 168.6 R 1.02
-(V8 insists that all directories leading up to the .forw)6.02 F 1.02
-(ard or :include: \214le be searchable)-.1 F .334
-(\("x" permission\) by the controlling user" \(de\214ned belo)102 180.6 R .335
-(w\), that the \214le itself be readable by the con-)-.25 F(trolling user)102
-192.6 Q 2.5(,a)-.4 G(nd that .forw)159.65 192.6 Q(ard \214les be o)-.1 E
-(wned by the user who is being forw)-.25 E(arded to or root.)-.1 E .565
-(The "controlling user" is the user on whose behalf the mail is being deli)127
-208.8 R -.15(ve)-.25 G 3.065(red. F).15 F .565(or e)-.15 F(xample,)-.15 E .459
-(if you mail to "user1" then the controlling user for ~user1/.forw)102 220.8 R
-.46(ard and an)-.1 F 2.96(ym)-.15 G .46(ailers in)416.94 220.8 R -.2(vo)-.4 G
--.1(ke).2 G 2.96(db).1 G 2.96(yt)481.04 220.8 S(hat)491.78 220.8 Q(.forw)102
-232.8 Q(ard \214le, including :include: \214les.)-.1 E(Pre)127 249 Q(viously)
--.25 E 2.816(,a)-.65 G -.15(ny)178.636 249 S .316
-(one who had a home directory could create a .forw).15 F .316(ard could forw)
--.1 F .316(ard to a pro-)-.1 F 2.965(gram. No)102 261 R 1.765 -.65(w, s)-.25 H
-.466(endmail checks to mak).65 F 2.966(es)-.1 G .466(ure that the)262.934 261 R
-2.966(yh)-.15 G -2.25 -.2(av e)321.672 261 T .466(an "appro)3.166 F -.15(ve)
--.15 G 2.966(ds).15 G .466(hell", that is, a shell listed)398.42 261 R
-(in the /etc/shells \214le.)102 273 Q F0 2.5(2.28. Miscellaneous)87 297 R
-(Fixes and Enhancements)2.5 E F1 4.03(An)127 313.2 S 1.53(umber of small b)
-143.25 313.2 R 1.53(ugs ha)-.2 F 1.53(ving to do with things lik)-.2 F 4.03(eb)
--.1 G 1.53(ackslash-escaped quotes inside of)364.72 313.2 R(comments ha)102
-325.2 Q .3 -.15(ve b)-.2 H(een \214x).15 E(ed.)-.15 E 1.552(The \214x)127 341.4
-R 1.552(ed size limit on header lines \(such as \231T)-.15 F 1.553
-(o:\232 and \231Cc:\232\) has been eliminated; those)-.8 F -.2(bu)102 353.4 S
--.25(ff).2 G(ers are dynamically allocated no).25 E -.65(w.)-.25 G .289(Sendma\
-il writes a /etc/sendmail.pid \214le with the current process id and the curre\
-nt in)127 369.6 R -.2(vo)-.4 G(cation).2 E(\215ags.)102 381.6 Q -1 -.8(Tw o)127
-397.8 T .218
-(people using the same program \(e.g., submit\) are considered "dif)3.518 F
-.219(ferent" so that duplicate)-.25 F .508(elimination doesn')102 409.8 R 3.008
-(td)-.18 G .508(elete one of them.)187.836 409.8 R -.15(Fo)5.508 G 3.008(re).15
-G .508(xample, tw)287.556 409.8 R 3.008(op)-.1 G .508(eople forw)345.412 409.8
-R .508(arding their email to |submit)-.1 F(will be treated as tw)102 421.8 Q
-2.5(or)-.1 G(ecipients.)193.27 421.8 Q .721(The mailstats program prints maile\
-r names and gets the location of the sendmail.st \214le from)127 438 R
-(/etc/sendmail.cf.)102 450 Q(Man)127 466.2 Q 2.5(ym)-.15 G(inor b)160.46 466.2
-Q(ugs ha)-.2 E .3 -.15(ve b)-.2 H(een \214x).15 E
-(ed, such as handling of backslashes inside of quotes.)-.15 E 2.5(Ah)127 482.4
-S(ook has been added to allo)141.72 482.4 Q 2.5(wr)-.25 G -.25(ew)260.89 482.4
-S(riting of local addresses after aliasing.).25 E F0 2.5(3. FUTURE)72 506.4 R
--.1(WO)2.5 G(RK).1 E F1 1.719(The pre)112 522.6 R 1.719
-(vious section describes)-.25 F/F2 10/Times-Italic@0 SF(sendmail)4.219 E F1
-1.719(as of v)4.219 F 1.719(ersion 8.6.6.)-.15 F 1.718
-(There is still much to be done.)6.719 F(Some high points are described belo)87
-534.6 Q 3.8 -.65(w. T)-.25 H(his list is by no means e).65 E(xhausti)-.15 E
--.15(ve)-.25 G(.).15 E F0 2.5(3.1. Full)87 558.6 R(MIME Support)2.5 E F1
-(Currently)127 574.8 Q F2(sendmail)3.305 E F1 .805(only supports se)3.305 F
--.15(ve)-.25 G 3.305(nb).15 G .805(it MIME messages.)297.005 574.8 R .806
-(Although it can pass eight bit)5.805 F .371(MIME messages, it cannot adv)102
-586.8 R .371(ertise that f)-.15 F .37
-(act because the standards say that the mail agent must be)-.1 F .26
-(able to do 8- to 7-bit con)102 598.8 R -.15(ve)-.4 G .26(rsion to ha).15 F
-.561 -.15(ve f)-.2 H .261(ull 8-bit support.).15 F .261(This requires f)5.261 F
-.261(ar more e)-.1 F(xtensi)-.15 E .561 -.15(ve m)-.25 H(odi\214-).15 E
-(cation of the message body than is currently supported.)102 610.8 Q .464
-(The best w)127 627 R .464(ay to do this w)-.1 F .463
-(ould be to support the general concept of an e)-.1 F .463(xternal `)-.15 F
-.463(`message \214l-)-.74 F(ter')102 639 Q 3.319('t)-.74 G .819
-(hat could do arbitrary modi\214cations of the message.)124.569 639 R .819
-(This w)5.819 F .82(ould allo)-.1 F 3.32(wM)-.25 G .82(IME con)427.37 639 R
--.15(ve)-.4 G .82(rsion as).15 F .63
-(well as such things as automatic encryption of messages sent o)102 651 R -.15
-(ve)-.15 G 3.129(re).15 G .629(xternal links.)379.264 651 R .629
-(This is probably)5.629 F(an e)102 663 Q(xtremely non-tri)-.15 E(vial change.)
--.25 E F0 2.5(3.2. Ser)87 687 R(vice Switch Abstraction)-.1 E F1 .369(Most mod\
-ern systems include some concept of a \231service switch\232 \212 for e)127
-703.2 R .37(xample, to look up)-.15 F .984
-(host names you can try DNS, NIS, NIS+, te)102 715.2 R .984
-(xt tables, NetInfo, or other services in some arbitrary)-.15 F EP
-%%Page: 10 10
-%%BeginPageSetup
-BP
-%%EndPageSetup
-/F0 10/Times-Bold@0 SF 287.15(10 Changes)72 60 R(in Sendmail V)2.5 E(ersion 8)
--1 E/F1 10/Times-Roman@0 SF(order)102 96 Q 5.174(.T)-.55 G .174
-(his is currently v)136.334 96 R .174(ery clumsy in)-.15 F/F2 10/Times-Italic@0
-SF(sendmail)2.674 E F1 2.674(,w)C .174
-(ith only limited control of the services pro)309.612 96 R(vided.)-.15 E F0 2.5
-(3.3. Mor)87 120 R 2.5(eC)-.18 G(ontr)139.86 120 Q(ol of Local Addr)-.18 E
-(esses)-.18 E F1 .943(Currently some addresses are declared as \231local\232 a\
-nd are handled specially \212 for e)127 136.2 R(xample,)-.15 E(the)102 148.2 Q
-3.455(ym)-.15 G .955(ay ha)130.305 148.2 R 1.255 -.15(ve .)-.2 H(forw).15 E
-.956(ard \214les, may be translated into program calls or \214le deli)-.1 F
--.15(ve)-.25 G .956(ries, and so forth.).15 F .311(These should be brok)102
-160.2 R .311(en out into separate \215ags to allo)-.1 F 2.811(wt)-.25 G .31
-(he local system administrator to ha)330.29 160.2 R .61 -.15(ve m)-.2 H(ore).15
-E(\214ne-grained control o)102 172.2 Q -.15(ve)-.15 G 2.5(ro).15 G(perations.)
-208.62 172.2 Q F0 2.5(3.4. Mor)87 196.2 R 2.5(eR)-.18 G(un-T)139.86 196.2 Q
-(ime Con\214guration Options)-.18 E F1 .016(There are man)127 212.4 R 2.516(yo)
--.15 G .016(ptions that are con\214gured at compile time, such as the method o\
-f \214le locking)197.148 212.4 R .719
-(and the use of the IDENT protocol [RFC1413].)102 224.4 R .719
-(These should be transfered to run time by adding)5.719 F(ne)102 236.4 Q 2.5
-(wo)-.25 G(ptions.)125.91 236.4 Q(Similarly)127 252.6 Q 3.413(,s)-.65 G .913
-(ome options are currently o)173.383 252.6 R -.15(ve)-.15 G .913
-(rloaded, that is, a single option controls more than).15 F(one thing.)102
-264.6 Q(These should probably be brok)5 E(en out into separate options.)-.1 E
-(This implies that options will change from single characters to w)127 280.8 Q
-(ords.)-.1 E F0 2.5(3.5. Mor)87 304.8 R 2.5(eC)-.18 G(on\214guration Contr)
-139.86 304.8 Q(ol Ov)-.18 E(er Err)-.1 E(ors)-.18 E F1(Currently)127 321 Q
-3.649(,t)-.65 G 1.148
-(he con\214guration \214le can generate an error message during parsing.)
-173.609 321 R(Ho)6.148 E(we)-.25 E -.15(ve)-.25 G 1.948 -.4(r, i).15 H(t).4 E
-.569(cannot tweak other operations, such as issuing a w)102 333 R .57
-(arning message to the system postmaster)-.1 F 5.57(.S)-.55 G(imi-)487.33 333 Q
-(larly)102 345 Q 2.558(,s)-.65 G .057
-(ome errors should not be triggered if the)128.628 345 R 2.557(ya)-.15 G .057
-(re in aliases during an alias \214le reb)302.237 345 R .057(uild, b)-.2 F .057
-(ut should)-.2 F(be triggered if that alias is actually used.)102 357 Q F0 2.5
-(3.6. Long)87 381 R -.92(Te)2.5 G(rm Host State).92 E F1(Currently)127 397.2 Q
-(,)-.65 E F2(sendmail)3.731 E F1 1.231
-(only remembers host status during a single queue run.)3.731 F 1.232
-(This should be)6.232 F(con)102 409.2 Q -.15(ve)-.4 G .492(rted to long term s\
-tatus stored on disk so it can be shared between instantiations of).15 F F2
-(sendmail)2.991 E F1(.)A .866(Entries will ha)102 421.2 R 1.167 -.15(ve t)-.2 H
-3.367(ob).15 G 3.367(et)190.666 421.2 S .867(imestamped so the)201.253 421.2 R
-3.367(yc)-.15 G .867(an time out.)290.084 421.2 R .867(This will allo)5.867 F
-(w)-.25 E F2(sendmail)3.367 E F1 .867(to implement)3.367 F -.15(ex)102 433.2 S
-(ponential back).15 E(of)-.1 E 2.5(fo)-.25 G 2.5(nq)188.7 433.2 S
-(ueue runs on a per)201.2 433.2 Q(-host basis.)-.2 E F0 2.5(3.7. Connection)87
-457.2 R(Contr)2.5 E(ol)-.18 E F1 .819(Modern netw)127 473.4 R .819(orks ha)-.1
-F 1.119 -.15(ve d)-.2 H(if).15 E .819(ferent types of connecti)-.25 F .818
-(vity than the past.)-.25 F .818(In particular)5.818 F 3.318(,t)-.4 G .818
-(he rising)468.462 473.4 R .636
-(prominence of dialup IP has created certain challenges for automated serv)102
-485.4 R 3.136(ers. It)-.15 F .636(is not uncommon)3.136 F .732(to try to mak)
-102 497.4 R 3.232(eac)-.1 G .732(onnection to a host and ha)175.27 497.4 R
-1.032 -.15(ve i)-.2 H 3.232(tf).15 G .732(ail, e)307.984 497.4 R -.15(ve)-.25 G
-3.232(nt).15 G .732(hough if you tried ag)348.208 497.4 R .732(ain it w)-.05 F
-.731(ould suc-)-.1 F 2.5(ceed. The)102 509.4 R
-(connection management could be a bit cle)2.5 E -.15(ve)-.25 G
-(rer to try to adapt to such situations.).15 E F0 2.5(3.8. Other)87 533.4 R
-(Caching)2.5 E F1 .074(When you do an MX record lookup, the name serv)127 549.6
-R .075(er automatically returns the IP addresses of)-.15 F .518
-(the associated MX serv)102 561.6 R 3.018(ers. This)-.15 F .518
-(information is currently ignored, and another query is done to get)3.018 F
-(this information.)102 573.6 Q(It should be cached to a)5 E -.2(vo)-.2 G(id e)
-.2 E(xcess name serv)-.15 E(er traf)-.15 E(\214c.)-.25 E F0 2.5(4. REFERENCES)
-72 597.6 R F1([Allman83a])87 613.8 Q .137(\231Sendmail \212 An Internetw)123
-625.8 R .137(ork Mail Router)-.1 F 4.037 -.7(.\232 E)-.55 H 2.638(.A).7 G 2.638
-(llman. In)327.58 625.8 R F2 .138(Unix Pr)2.638 F -.1(og)-.45 G -.15(ra).1 G
-(mmer).15 E(s')-.1 E 2.638(sM)-.4 G(anual,)463.582 625.8 Q F1(4.2)2.638 E(Berk)
-123 637.8 Q(ele)-.1 E 2.5(yS)-.15 G(oftw)166.91 637.8 Q(are Distrib)-.1 E
-(ution, v)-.2 E(olume 2C.)-.2 E(August 1983.)5 E([Allman83b])87 654 Q .384
-(\231Mail Systems and Addressing in 4.2BSD.)123 666 R 5.384<9a45>-.7 G 2.884
-(.A)311.544 666 S .383(llman In)324.148 666 R F2 .383(UNICOM Confer)2.883 F
-.383(ence Pr)-.37 F(oceedings.)-.45 E F1(San Die)123 678 Q(go, California.)-.15
-E(January 1983.)5 E([Allman&Amos85])87 694.2 Q -.74(``)123 706.2 S 1.145
-(Sendmail Re).74 F(visited.)-.25 E 5.125 -.74('' E)-.7 H 3.645(.A).74 G 1.145
-(llman and M. Amos.)241.215 706.2 R(In)6.145 E F2 1.145
-(Usenix Summer 1985 Confer)3.645 F 1.145(ence Pr)-.37 F(o-)-.45 E(ceedings.)123
-718.2 Q F1(Portland, Ore)5 E 2.5(gon. June)-.15 F(1985.)2.5 E EP
-%%Page: 11 11
-%%BeginPageSetup
-BP
-%%EndPageSetup
-/F0 10/Times-Bold@0 SF(Changes in Sendmail V)72 60 Q(ersion 8)-1 E(11)494 60 Q
-/F1 10/Times-Roman@0 SF([ID)87 96 Q(A87])-.4 E/F2 10/Times-Italic@0 SF(Electr)
-1.97 E .983(onic Mail Addr)-.45 F .983(essing in Theory and Pr)-.37 F .982
-(actice with the ID)-.15 F 3.482(AS)-.35 G .982(endmail Enhancement Kit)398.156
-96 R .563(\(or The P)123 108 R(ostmaster')-.8 E 3.063(sL)-.4 G .563(ast W)
-215.989 108 R .564(ill and T)-.55 F(estament\).)-.92 E F1 .564(Lennart Lo)5.564
-F -.5(..)359.828 102 S 3.064(vstrand. Department)364.828 108 R .564
-(of Computer)3.064 F 1.267(and Information Science, Uni)123 120 R -.15(ve)-.25
-G 1.267(rsity of Link).15 F(o)-.1 E -.5(..)306.585 114 S 1.266
-(ping, Sweden, Report no. LiTH-ID)311.585 120 R(A-Ex-8715.)-.4 E(May 1987.)123
-132 Q([RFC821])87 148.2 Q F2(Simple Mail T)123 160.2 Q -.15(ra)-.55 G
-(nsport Pr).15 E(otocol.)-.45 E F1(J. Postel.)5 E(August 1982.)5 E([RFC1123])87
-176.4 Q F2(Requir)123 188.4 Q .163
-(ements for Internet Hosts \212 Application and Support.)-.37 F F1 .164
-(Internet Engineering T)5.164 F .164(ask F)-.8 F(orce,)-.15 E
-(R. Braden, Editor)123 200.4 Q 5(.O)-.55 G(ctober 1989.)207.72 200.4 Q
-([RFC1344])87 216.6 Q F2(Implications of MIME for Internet Mail Gate)123 228.6
-Q(ways.)-.15 E F1(N. Borenstein.)5 E(June 1992.)5 E([RFC1413])87 244.8 Q F2
-(Identi\214cation Pr)123 256.8 Q(otocol.)-.45 E F1(M. St. Johns.)5 E
-(February 1993.)5 E([RFC1425])87 273 Q F2 2.352(SMTP Service Extensions.)123
-285 R F1 2.352(J. Klensin, N. Freed, M. Rose, E. Stef)7.352 F 2.351
-(ferud, and D. Crock)-.25 F(er)-.1 E(.)-.55 E(February 1993.)123 297 Q
-([RFC1426])87 313.2 Q F2 .12(SMTP Service Extension for 8bit-MIMEtr)123 325.2 R
-(ansport.)-.15 E F1 .12(J. Klensin, N. Freed, M. Rose, E. Stef)5.12 F(ferud,)
--.25 E(and D. Crock)123 337.2 Q(er)-.1 E 5(.F)-.55 G(ebruary 1993.)196.78 337.2
-Q([RFC1427])87 353.4 Q F2 .813(SMTP Service Extension for Messa)123 365.4 R
-1.013 -.1(ge S)-.1 H .813(ize Declar).1 F(ation.)-.15 E F1 .813
-(J. Klensin, N. Freed, and K. Moore.)5.813 F(February 1993.)123 377.4 Q
-([RFC1521])87 393.6 Q F2 2.033
-(MIME \(Multipurpose Internet Mail Extensions\) P)123 405.6 R 2.033
-(art One: Mec)-.8 F 2.033(hanisms for Specifying and)-.15 F .933
-(Describing the F)123 417.6 R .933(ormat of Internet Messa)-1.05 F 1.133 -.1
-(ge B)-.1 H(odies.).1 E F1 .932(N. Borenstein and N. Freed.)5.932 F(September)
-5.932 E(1993.)123 429.6 Q EP
-%%Trailer
-end
-%%EOF
diff --git a/usr.sbin/sendmail/doc/intro/intro.ps b/usr.sbin/sendmail/doc/intro/intro.ps
deleted file mode 100644
index 57c4216..0000000
--- a/usr.sbin/sendmail/doc/intro/intro.ps
+++ /dev/null
@@ -1,1295 +0,0 @@
-%!PS-Adobe-3.0
-%%Creator: groff version 1.08
-%%DocumentNeededResources: font Times-Roman
-%%+ font Times-Italic
-%%+ font Times-Bold
-%%DocumentSuppliedResources: procset grops 1.08 0
-%%Pages: 13
-%%PageOrder: Ascend
-%%Orientation: Portrait
-%%EndComments
-%%BeginProlog
-%%BeginResource: procset grops 1.08 0
-/setpacking where{
-pop
-currentpacking
-true setpacking
-}if
-/grops 120 dict dup begin
-/SC 32 def
-/A/show load def
-/B{0 SC 3 -1 roll widthshow}bind def
-/C{0 exch ashow}bind def
-/D{0 exch 0 SC 5 2 roll awidthshow}bind def
-/E{0 rmoveto show}bind def
-/F{0 rmoveto 0 SC 3 -1 roll widthshow}bind def
-/G{0 rmoveto 0 exch ashow}bind def
-/H{0 rmoveto 0 exch 0 SC 5 2 roll awidthshow}bind def
-/I{0 exch rmoveto show}bind def
-/J{0 exch rmoveto 0 SC 3 -1 roll widthshow}bind def
-/K{0 exch rmoveto 0 exch ashow}bind def
-/L{0 exch rmoveto 0 exch 0 SC 5 2 roll awidthshow}bind def
-/M{rmoveto show}bind def
-/N{rmoveto 0 SC 3 -1 roll widthshow}bind def
-/O{rmoveto 0 exch ashow}bind def
-/P{rmoveto 0 exch 0 SC 5 2 roll awidthshow}bind def
-/Q{moveto show}bind def
-/R{moveto 0 SC 3 -1 roll widthshow}bind def
-/S{moveto 0 exch ashow}bind def
-/T{moveto 0 exch 0 SC 5 2 roll awidthshow}bind def
-/SF{
-findfont exch
-[exch dup 0 exch 0 exch neg 0 0]makefont
-dup setfont
-[exch/setfont cvx]cvx bind def
-}bind def
-/MF{
-findfont
-[5 2 roll
-0 3 1 roll
-neg 0 0]makefont
-dup setfont
-[exch/setfont cvx]cvx bind def
-}bind def
-/level0 0 def
-/RES 0 def
-/PL 0 def
-/LS 0 def
-/PLG{
-gsave newpath clippath pathbbox grestore
-exch pop add exch pop
-}bind def
-/BP{
-/level0 save def
-1 setlinecap
-1 setlinejoin
-72 RES div dup scale
-LS{
-90 rotate
-}{
-0 PL translate
-}ifelse
-1 -1 scale
-}bind def
-/EP{
-level0 restore
-showpage
-}bind def
-/DA{
-newpath arcn stroke
-}bind def
-/SN{
-transform
-.25 sub exch .25 sub exch
-round .25 add exch round .25 add exch
-itransform
-}bind def
-/DL{
-SN
-moveto
-SN
-lineto stroke
-}bind def
-/DC{
-newpath 0 360 arc closepath
-}bind def
-/TM matrix def
-/DE{
-TM currentmatrix pop
-translate scale newpath 0 0 .5 0 360 arc closepath
-TM setmatrix
-}bind def
-/RC/rcurveto load def
-/RL/rlineto load def
-/ST/stroke load def
-/MT/moveto load def
-/CL/closepath load def
-/FL{
-currentgray exch setgray fill setgray
-}bind def
-/BL/fill load def
-/LW/setlinewidth load def
-/RE{
-findfont
-dup maxlength 1 index/FontName known not{1 add}if dict begin
-{
-1 index/FID ne{def}{pop pop}ifelse
-}forall
-/Encoding exch def
-dup/FontName exch def
-currentdict end definefont pop
-}bind def
-/DEFS 0 def
-/EBEGIN{
-moveto
-DEFS begin
-}bind def
-/EEND/end load def
-/CNT 0 def
-/level1 0 def
-/PBEGIN{
-/level1 save def
-translate
-div 3 1 roll div exch scale
-neg exch neg exch translate
-0 setgray
-0 setlinecap
-1 setlinewidth
-0 setlinejoin
-10 setmiterlimit
-[]0 setdash
-/setstrokeadjust where{
-pop
-false setstrokeadjust
-}if
-/setoverprint where{
-pop
-false setoverprint
-}if
-newpath
-/CNT countdictstack def
-userdict begin
-/showpage{}def
-}bind def
-/PEND{
-clear
-countdictstack CNT sub{end}repeat
-level1 restore
-}bind def
-end def
-/setpacking where{
-pop
-setpacking
-}if
-%%EndResource
-%%IncludeResource: font Times-Roman
-%%IncludeResource: font Times-Italic
-%%IncludeResource: font Times-Bold
-grops begin/DEFS 1 dict def DEFS begin/u{.001 mul}bind def end/RES 72 def/PL
-792 def/LS false def/ENC0[/asciicircum/asciitilde/Scaron/Zcaron/scaron/zcaron
-/Ydieresis/trademark/quotesingle/.notdef/.notdef/.notdef/.notdef/.notdef
-/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef
-/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/space
-/exclam/quotedbl/numbersign/dollar/percent/ampersand/quoteright/parenleft
-/parenright/asterisk/plus/comma/hyphen/period/slash/zero/one/two/three/four
-/five/six/seven/eight/nine/colon/semicolon/less/equal/greater/question/at/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/bracketleft/backslash
-/bracketright/circumflex/underscore/quoteleft/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/braceleft/bar/braceright/tilde/.notdef/quotesinglbase
-/guillemotleft/guillemotright/bullet/florin/fraction/perthousand/dagger
-/daggerdbl/endash/emdash/ff/fi/fl/ffi/ffl/dotlessi/dotlessj/grave/hungarumlaut
-/dotaccent/breve/caron/ring/ogonek/quotedblleft/quotedblright/oe/lslash
-/quotedblbase/OE/Lslash/.notdef/exclamdown/cent/sterling/currency/yen/brokenbar
-/section/dieresis/copyright/ordfeminine/guilsinglleft/logicalnot/minus
-/registered/macron/degree/plusminus/twosuperior/threesuperior/acute/mu
-/paragraph/periodcentered/cedilla/onesuperior/ordmasculine/guilsinglright
-/onequarter/onehalf/threequarters/questiondown/Agrave/Aacute/Acircumflex/Atilde
-/Adieresis/Aring/AE/Ccedilla/Egrave/Eacute/Ecircumflex/Edieresis/Igrave/Iacute
-/Icircumflex/Idieresis/Eth/Ntilde/Ograve/Oacute/Ocircumflex/Otilde/Odieresis
-/multiply/Oslash/Ugrave/Uacute/Ucircumflex/Udieresis/Yacute/Thorn/germandbls
-/agrave/aacute/acircumflex/atilde/adieresis/aring/ae/ccedilla/egrave/eacute
-/ecircumflex/edieresis/igrave/iacute/icircumflex/idieresis/eth/ntilde/ograve
-/oacute/ocircumflex/otilde/odieresis/divide/oslash/ugrave/uacute/ucircumflex
-/udieresis/yacute/thorn/ydieresis]def/Times-Bold@0 ENC0/Times-Bold RE
-/Times-Italic@0 ENC0/Times-Italic RE/Times-Roman@0 ENC0/Times-Roman RE
-%%EndProlog
-%%Page: 1 1
-%%BeginPageSetup
-BP
-%%EndPageSetup
-/F0 14/Times-Roman@0 SF(SENDMAIL \212 An Internetw)159.172 141 Q
-(ork Mail Router)-.14 E/F1 10/Times-Roman@0 SF(Eric Allman*)260.92 165 Q/F2 10
-/Times-Italic@0 SF(Univer)220.2 183 Q(sity of California, Berk)-.1 E(ele)-.1 E
-(y)-.3 E(Mammoth Pr)251.98 195 Q(oject)-.45 E F1(ABSTRA)262.085 227.4 Q(CT)-.4
-E 1.41(Routing mail through a heterogenous internet presents man)112 243.6 R
-3.91(yn)-.15 G 1.91 -.25(ew p)372.55 243.6 T 3.91(roblems. Among).25 F .297
-(the w)112 255.6 R .297(orst of these is that of address mapping.)-.1 F
-(Historically)5.297 E 2.797(,t)-.65 G .298(his has been handled on an)355.03
-255.6 R F2(ad hoc)112 267.6 Q F1 2.5(basis. Ho)2.5 F(we)-.25 E -.15(ve)-.25 G
-.8 -.4(r, t).15 H(his approach has become unmanageable as internets gro).4 E
--.65(w.)-.25 G .15(Sendmail acts a uni\214ed "post of)112 283.8 R .15
-(\214ce" to which all mail can be submitted.)-.25 F .15(Address inter)5.15 F(-)
--.2 E .426(pretation is controlled by a production system, which can parse bot\
-h domain-based ad-)112 295.8 R .423(dressing and old-style)112 307.8 R F2 .423
-(ad hoc)2.923 F F1 2.923(addresses. The)2.923 F .422(production system is po)
-2.922 F .422(werful enough to)-.25 F(re)112 319.8 Q 1.357(write addresses in t\
-he message header to conform to the standards of a number of)-.25 F 1.15
-(common tar)112 331.8 R 1.15(get netw)-.18 F 1.15
-(orks, including old \(NCP/RFC733\) Arpanet, ne)-.1 F 3.65(w\()-.25 G
-(TCP/RFC822\))405.65 331.8 Q 1.119(Arpanet, UUCP)112 343.8 R 3.619(,a)-1.11 G
-1.119(nd Phonenet.)186.448 343.8 R 1.119(Sendmail also implements an SMTP serv)
-6.119 F(er)-.15 E 3.619(,m)-.4 G(essage)437.9 343.8 Q(queueing, and aliasing.)
-112 355.8 Q F2(Sendmail)97 400.2 Q F1 .501(implements a general internetw)3 F
-.501(ork mail routing f)-.1 F(acility)-.1 E 3.001(,f)-.65 G .501
-(eaturing aliasing and forw)369.847 400.2 R(arding,)-.1 E
-(automatic routing to netw)72 412.2 Q(ork g)-.1 E(ate)-.05 E -.1(wa)-.25 G
-(ys, and \215e).1 E(xible con\214guration.)-.15 E .624(In a simple netw)97
-428.4 R .624(ork, each node has an address, and resources can be identi\214ed \
-with a host-resource)-.1 F .374(pair; in particular)72 440.4 R 2.874(,t)-.4 G
-.374(he mail system can refer to users using a host-username pair)149.932 440.4
-R 5.374(.H)-.55 G .375(ost names and numbers)409.276 440.4 R(ha)72 452.4 Q .3
--.15(ve t)-.2 H 2.5(ob).15 G 2.5(ea)108.31 452.4 S
-(dministered by a central authority)119.69 452.4 Q 2.5(,b)-.65 G
-(ut usernames can be assigned locally to each host.)263.82 452.4 Q .649
-(In an internet, multiple netw)97 468.6 R .649(orks with dif)-.1 F .649
-(ferent characterstics and managements must communicate.)-.25 F .389
-(In particular)72 480.6 R 2.889(,t)-.4 G .389
-(he syntax and semantics of resource identi\214cation change.)129.308 480.6 R
-.39(Certain special cases can be han-)5.389 F 1.033(dled tri)72 492.6 R 1.033
-(vially by)-.25 F F2 1.033(ad hoc)3.533 F F1 1.032(techniques, such as pro)
-3.533 F 1.032(viding netw)-.15 F 1.032
-(ork names that appear local to hosts on other)-.1 F(netw)72 504.6 Q 1.454
-(orks, as with the Ethernet at Xerox P)-.1 F 3.955(ARC. Ho)-.92 F(we)-.25 E
--.15(ve)-.25 G 4.755 -.4(r, t).15 H 1.455(he general case is e).4 F 1.455
-(xtremely comple)-.15 F 3.955(x. F)-.15 F(or)-.15 E -.15(ex)72 516.6 S .192
-(ample, some netw).15 F .192(orks require point-to-point routing, which simpli\
-\214es the database update problem since)-.1 F .618(only adjacent hosts must b\
-e entered into the system tables, while others use end-to-end addressing.)72
-528.6 R(Some)5.618 E(netw)72 540.6 Q .123(orks use a left-associati)-.1 F .423
--.15(ve s)-.25 H .123(yntax and others use a right-associati).15 F .423 -.15
-(ve s)-.25 H .123(yntax, causing ambiguity in mix).15 F(ed)-.15 E(addresses.)72
-552.6 Q .678(Internet standards seek to eliminate these problems.)97 568.8 R
-(Initially)5.678 E 3.178(,t)-.65 G .679(hese proposed e)353.134 568.8 R .679
-(xpanding the address)-.15 F .65(pairs to address triples, consisting of {netw)
-72 580.8 R .649(ork, host, resource} triples.)-.1 F(Netw)5.649 E .649
-(ork numbers must be uni)-.1 F -.15(ve)-.25 G -.2(r-).15 G 1.452
-(sally agreed upon, and hosts can be assigned locally on each netw)72 592.8 R
-3.952(ork. The)-.1 F(user)3.952 E(-le)-.2 E -.15(ve)-.25 G 3.952(lp).15 G 1.452
-(resentation w)440.718 592.8 R(as)-.1 E 2.352(quickly e)72 604.8 R 2.352(xpand\
-ed to address domains, comprised of a local resource identi\214cation and a hi\
-erarchical)-.15 F .256(domain speci\214cation with a common static root.)72
-616.8 R .257(The domain technique separates the issue of ph)5.257 F .257
-(ysical v)-.05 F(er)-.15 E(-)-.2 E .807(sus logical addressing.)72 628.8 R -.15
-(Fo)5.807 G 3.307(re).15 G .807
-(xample, an address of the form \231eric@a.cc.berk)191.028 628.8 R(ele)-.1 E
--.65(y.)-.15 G .807(arpa\232 describes only the).65 F(logical or)72 640.8 Q
--.05(ga)-.18 G(nization of the address space.).05 E F2(Sendmail)97 657 Q F1
-.493(is intended to help bridge the g)2.992 F .493(ap between the totally)-.05
-F F2 .493(ad hoc)2.993 F F1 -.1(wo)2.993 G .493(rld of netw).1 F .493
-(orks that kno)-.1 F(w)-.25 E .855
-(nothing of each other and the clean, tightly-coupled w)72 669 R .854
-(orld of unique netw)-.1 F .854(ork numbers.)-.1 F .854(It can accept old)5.854
-F .32 LW 76 678.6 72 678.6 DL 80 678.6 76 678.6 DL 84 678.6 80 678.6 DL 88
-678.6 84 678.6 DL 92 678.6 88 678.6 DL 96 678.6 92 678.6 DL 100 678.6 96 678.6
-DL 104 678.6 100 678.6 DL 108 678.6 104 678.6 DL 112 678.6 108 678.6 DL 116
-678.6 112 678.6 DL 120 678.6 116 678.6 DL 124 678.6 120 678.6 DL 128 678.6 124
-678.6 DL 132 678.6 128 678.6 DL 136 678.6 132 678.6 DL 140 678.6 136 678.6 DL
-144 678.6 140 678.6 DL 148 678.6 144 678.6 DL 152 678.6 148 678.6 DL 156 678.6
-152 678.6 DL 160 678.6 156 678.6 DL 164 678.6 160 678.6 DL 168 678.6 164 678.6
-DL 172 678.6 168 678.6 DL 176 678.6 172 678.6 DL 180 678.6 176 678.6 DL 184
-678.6 180 678.6 DL 188 678.6 184 678.6 DL 192 678.6 188 678.6 DL 196 678.6 192
-678.6 DL 200 678.6 196 678.6 DL 204 678.6 200 678.6 DL 208 678.6 204 678.6 DL
-212 678.6 208 678.6 DL 216 678.6 212 678.6 DL/F3 8/Times-Roman@0 SF .557
-(*A considerable part of this w)93.6 690.6 R .557(ork w)-.08 F .557
-(as done while under the emplo)-.08 F 2.557(yo)-.08 G 2.556(ft)323.116 690.6 S
-.556(he INGRES Project at the Uni)330.56 690.6 R -.12(ve)-.2 G .556
-(rsity of California at).12 F(Berk)72 700.2 Q(ele)-.08 E 2(ya)-.12 G
-(nd at Britton Lee.)106.232 700.2 Q/F4 10/Times-Bold@0 SF
-(SENDMAIL \212 An Inter)72 756 Q(netw)-.15 E(ork Mail Router)-.1 E(SMM:9-1)
-462.9 756 Q EP
-%%Page: 2 2
-%%BeginPageSetup
-BP
-%%EndPageSetup
-/F0 10/Times-Bold@0 SF 192.28(SMM:9-2 SENDMAIL)72 60 R 2.5<8a41>2.5 G 2.5(nI)
-383.99 60 S(nter)395.94 60 Q(netw)-.15 E(ork Mail Router)-.1 E/F1 10
-/Times-Roman@0 SF .632(arbitrary address syntax)72 96 R .633(es, resolving amb\
-iguities using heuristics speci\214ed by the system administrator)-.15 F 3.133
-(,a)-.4 G(s)500.11 96 Q .348(well as domain-based addressing.)72 108 R .347
-(It helps guide the con)5.347 F -.15(ve)-.4 G .347
-(rsion of message formats between disparate net-).15 F -.1(wo)72 120 S 3.394
-(rks. In).1 F(short,)3.394 E/F2 10/Times-Italic@0 SF(sendmail)3.394 E F1 .894
-(is designed to assist a graceful transition to consistent internetw)3.394 F
-.895(ork addressing)-.1 F(schemes.)72 132 Q .153
-(Section 1 discusses the design goals for)97 160.2 R F2(sendmail)2.653 E F1
-5.153(.S)C .152(ection 2 gi)308.214 160.2 R -.15(ve)-.25 G 2.652(sa).15 G 2.652
-(no)370.76 160.2 S -.15(ve)383.262 160.2 S(rvie).15 E 2.652(wo)-.25 G 2.652(ft)
-422.724 160.2 S .152(he basic functions)431.486 160.2 R .644(of the system.)72
-172.2 R .644(In section 3, details of usage are discussed.)5.644 F .644
-(Section 4 compares)5.644 F F2(sendmail)3.144 E F1 .645(to other internet)3.144
-F(mail routers, and an e)72 184.2 Q -.25(va)-.25 G(luation of).25 E F2
-(sendmail)2.5 E F1(is gi)2.5 E -.15(ve)-.25 G 2.5(ni).15 G 2.5(ns)283.3 184.2 S
-(ection 5, including future plans.)294.69 184.2 Q F0 2.5(1. DESIGN)72 208.2 R
-(GO)2.5 E(ALS)-.4 E F1(Design goals for)112 224.4 Q F2(sendmail)2.5 E F1
-(include:)2.5 E 12.5(\(1\) Compatibility)92 240.6 R 1.363(with the e)3.864 F
-1.363(xisting mail programs, including Bell v)-.15 F 1.363
-(ersion 6 mail, Bell v)-.15 F 1.363(ersion 7)-.15 F 1.202(mail [UNIX83], Berk)
-118.66 252.6 R(ele)-.1 E(y)-.15 E F2(Mail)3.702 E F1 1.202
-([Shoens79], BerkNet mail [Schmidt79], and hopefully UUCP)3.702 F(mail [No)
-118.66 264.6 Q(witz78a, No)-.25 E 2.5(witz78b]. ARP)-.25 F(ANET mail [Crock)
--.92 E(er77a, Postel77] w)-.1 E(as also required.)-.1 E 12.5(\(2\) Reliability)
-92 280.8 R 4.003(,i)-.65 G 4.003(nt)169.523 280.8 S 1.502
-(he sense of guaranteeing that e)181.306 280.8 R -.15(ve)-.25 G 1.502
-(ry message is correctly deli).15 F -.15(ve)-.25 G 1.502(red or at least).15 F
-.368
-(brought to the attention of a human for correct disposal; no message should e)
-118.66 292.8 R -.15(ve)-.25 G 2.868(rb).15 G 2.868(ec)452.252 292.8 S
-(ompletely)464 292.8 Q 2.541(lost. This)118.66 304.8 R .041(goal w)2.541 F .041
-(as considered essential because of the emphasis on mail in our en)-.1 F 2.54
-(vironment. It)-.4 F 1.754
-(has turned out to be one of the hardest goals to satisfy)118.66 316.8 R 4.255
-(,e)-.65 G 1.755(specially in the f)363.75 316.8 R 1.755(ace of the man)-.1 F
-(y)-.15 E .978(anomalous message formats produced by v)118.66 328.8 R .977
-(arious ARP)-.25 F .977(ANET sites.)-.92 F -.15(Fo)5.977 G 3.477(re).15 G .977
-(xample, certain sites)420.116 328.8 R .069
-(generate improperly formated addresses, occasionally causing error)118.66
-340.8 R .069(-message loops.)-.2 F .069(Some hosts)5.069 F .063(use blanks in \
-names, causing problems with UNIX mail programs that assume that an address is)
-118.66 352.8 R .111(one w)118.66 364.8 R 2.611(ord. The)-.1 F .111
-(semantics of some \214elds are interpreted slightly dif)2.611 F .112
-(ferently by dif)-.25 F .112(ferent sites.)-.25 F(In)5.112 E(summary)118.66
-376.8 Q 3.023(,t)-.65 G .523(he obscure features of the ARP)163.533 376.8 R
-.523(ANET mail protocol really)-.92 F F2(ar)3.023 E(e)-.37 E F1 .522
-(used and are dif)3.023 F(\214cult)-.25 E(to support, b)118.66 388.8 Q
-(ut must be supported.)-.2 E 12.5(\(3\) Existing)92 405 R(softw)2.938 E .438
-(are to do actual deli)-.1 F -.15(ve)-.25 G .439(ry should be used whene).15 F
--.15(ve)-.25 G 2.939(rp).15 G 2.939(ossible. This)387.654 405 R .439(goal deri)
-2.939 F -.15(ve)-.25 G 2.939(sa).15 G(s)500.11 405 Q
-(much from political and practical considerations as technical.)118.66 417 Q
-12.5(\(4\) Easy)92 433.2 R -.15(ex)2.899 G .399(pansion to f).15 F .399
-(airly comple)-.1 F 2.898(xe)-.15 G -.4(nv)261.064 433.2 S .398
-(ironments, including multiple connections to a single net-).4 F -.1(wo)118.66
-445.2 S .115
-(rk type \(such as with multiple UUCP or Ether nets [Metcalfe76]\).).1 F .115
-(This goal requires consid-)5.115 F .587(eration of the contents of an address\
- as well as its syntax in order to determine which g)118.66 457.2 R(ate)-.05 E
--.1(wa)-.25 G(y).1 E 1.018(to use.)118.66 469.2 R -.15(Fo)6.018 G 3.518(re).15
-G 1.018(xample, the ARP)173.354 469.2 R 1.019
-(ANET is bringing up the TCP protocol to replace the old NCP)-.92 F 4.791
-(protocol. No)118.66 481.2 R 2.291(host at Berk)4.791 F(ele)-.1 E 4.791(yr)-.15
-G 2.291(uns both TCP and NCP)256.235 481.2 R 4.791(,s)-1.11 G 4.79(oi)369.37
-481.2 S 4.79(ti)381.94 481.2 S 4.79(sn)392.29 481.2 S 2.29
-(ecessary to look at the)405.97 481.2 R(ARP)118.66 493.2 Q .016
-(ANET host name to determine whether to route mail to an NCP g)-.92 F(ate)-.05
-E -.1(wa)-.25 G 2.517(yo).1 G 2.517(raT)435.569 493.2 S .017(CP g)454.483 493.2
-R(ate)-.05 E -.1(wa)-.25 G -.65(y.).1 G 12.5(\(5\) Con\214guration)92 509.4 R
-.145(should not be compiled into the code.)2.645 F 2.645(As)5.145 G .145
-(ingle compiled program should be able)346.905 509.4 R .91(to run as is at an)
-118.66 521.4 R 3.41(ys)-.15 G .91
-(ite \(barring such basic changes as the CPU type or the operating system\).)
-200.63 521.4 R 2.61 -.8(We h)118.66 533.4 T -2.25 -.2(av e).8 H 1.009
-(found this seemingly unimportant goal to be critical in real life.)3.71 F
-1.009(Besides the simple)6.009 F .66(problems that occur when an)118.66 545.4 R
-3.16(yp)-.15 G .66(rogram gets recompiled in a dif)249.84 545.4 R .66
-(ferent en)-.25 F .66(vironment, man)-.4 F 3.16(ys)-.15 G(ites)490.11 545.4 Q
-(lik)118.66 557.4 Q 2.5(et)-.1 G 2.5<6f99>138.84 557.4 S(\214ddle\232 with an)
-150.78 557.4 Q(ything that the)-.15 E 2.5(yw)-.15 G(ill be recompiling an)
-282.42 557.4 Q(yw)-.15 E(ay)-.1 E(.)-.65 E(\(6\))92 573.6 Q F2(Sendmail)118.66
-573.6 Q F1 .184(must be able to let v)2.684 F .184
-(arious groups maintain their o)-.25 F .184(wn mailing lists, and let indi)-.25
-F(viduals)-.25 E(specify their o)118.66 585.6 Q(wn forw)-.25 E
-(arding, without modifying the system alias \214le.)-.1 E 12.5(\(7\) Each)92
-601.8 R .313(user should be able to specify which mailer to e)2.813 F -.15(xe)
--.15 G .313(cute to process mail being deli).15 F -.15(ve)-.25 G .314(red for)
-.15 F 3.098(him. This)118.66 613.8 R .598(feature allo)3.098 F .598
-(ws users who are using specialized mailers that use a dif)-.25 F .598
-(ferent format to)-.25 F -.2(bu)118.66 625.8 S .25(ild their en).2 F .25
-(vironment without changing the system, and f)-.4 F .25
-(acilitates specialized functions \(such)-.1 F(as returning an \231I am on v)
-118.66 637.8 Q(acation\232 message\).)-.25 E 12.5(\(8\) Netw)92 654 R 1.553
-(ork traf)-.1 F 1.552(\214c should be minimized by batching addresses to a sin\
-gle host where possible,)-.25 F(without assistance from the user)118.66 666 Q
-(.)-.55 E .374(These goals moti)112 682.2 R -.25(va)-.25 G .374
-(ted the architecture illustrated in \214gure 1.).25 F .375
-(The user interacts with a mail gen-)5.375 F .491(erating and sending program.)
-87 694.2 R .491(When the mail is created, the generator calls)5.491 F F2
-(sendmail)2.99 E F1 2.99(,w)C .49(hich routes the)444.14 694.2 R .84
-(message to the correct mailer\(s\).)87 706.2 R .841
-(Since some of the senders may be netw)5.84 F .841(ork serv)-.1 F .841
-(ers and some of the)-.15 F(mailers may be netw)87 718.2 Q(ork clients,)-.1 E
-F2(sendmail)2.5 E F1(may be used as an internet mail g)2.5 E(ate)-.05 E -.1(wa)
--.25 G -.65(y.).1 G EP
-%%Page: 3 3
-%%BeginPageSetup
-BP
-%%EndPageSetup
-/F0 10/Times-Bold@0 SF(SENDMAIL \212 An Inter)72 60 Q(netw)-.15 E
-(ork Mail Router)-.1 E(SMM:9-3)462.9 60 Q .4 LW 77 108 72 108 DL 79 108 74 108
-DL 84 108 79 108 DL 89 108 84 108 DL 94 108 89 108 DL 99 108 94 108 DL 104 108
-99 108 DL 109 108 104 108 DL 114 108 109 108 DL 119 108 114 108 DL 124 108 119
-108 DL 129 108 124 108 DL 134 108 129 108 DL 139 108 134 108 DL 144 108 139 108
-DL 149 108 144 108 DL 154 108 149 108 DL 159 108 154 108 DL 164 108 159 108 DL
-169 108 164 108 DL 174 108 169 108 DL 179 108 174 108 DL 184 108 179 108 DL 189
-108 184 108 DL 194 108 189 108 DL 199 108 194 108 DL 204 108 199 108 DL 209 108
-204 108 DL 214 108 209 108 DL 219 108 214 108 DL 224 108 219 108 DL 229 108 224
-108 DL 234 108 229 108 DL 239 108 234 108 DL 244 108 239 108 DL 249 108 244 108
-DL 254 108 249 108 DL 259 108 254 108 DL 264 108 259 108 DL 269 108 264 108 DL
-274 108 269 108 DL 279 108 274 108 DL 284 108 279 108 DL 289 108 284 108 DL 294
-108 289 108 DL 299 108 294 108 DL 304 108 299 108 DL 309 108 304 108 DL 314 108
-309 108 DL 319 108 314 108 DL 324 108 319 108 DL 329 108 324 108 DL 334 108 329
-108 DL 339 108 334 108 DL 344 108 339 108 DL 349 108 344 108 DL 354 108 349 108
-DL 359 108 354 108 DL 364 108 359 108 DL 369 108 364 108 DL 374 108 369 108 DL
-379 108 374 108 DL 384 108 379 108 DL 389 108 384 108 DL 394 108 389 108 DL 399
-108 394 108 DL 404 108 399 108 DL 409 108 404 108 DL 414 108 409 108 DL 419 108
-414 108 DL 424 108 419 108 DL 429 108 424 108 DL 434 108 429 108 DL 439 108 434
-108 DL 444 108 439 108 DL 449 108 444 108 DL 454 108 449 108 DL 459 108 454 108
-DL 464 108 459 108 DL 469 108 464 108 DL 474 108 469 108 DL 479 108 474 108 DL
-484 108 479 108 DL 489 108 484 108 DL 494 108 489 108 DL 499 108 494 108 DL 504
-108 499 108 DL/F1 10/Times-Roman@0 SF(sender1)164.45 155.6 Q 144 135.6 144
-171.6 DL 216 135.6 144 135.6 DL 216 171.6 216 135.6 DL 144 171.6 216 171.6 DL
-(sender2)272.45 155.6 Q 252 135.6 252 171.6 DL 324 135.6 252 135.6 DL 324 171.6
-324 135.6 DL 252 171.6 324 171.6 DL(sender3)380.45 155.6 Q 360 135.6 360 171.6
-DL 432 135.6 360 135.6 DL 432 171.6 432 135.6 DL 360 171.6 432 171.6 DL 288
-207.6 288 171.6 DL 288 207.6 286.2 200.4 DL 288 207.6 289.8 200.4 DL(sendmail)
-269.945 227.6 Q 216 207.6 216 243.6 DL 360 207.6 216 207.6 DL 360 243.6 360
-207.6 DL 216 243.6 360 243.6 DL 288 279.6 288 243.6 DL 288 279.6 286.2 272.4 DL
-288 279.6 289.8 272.4 DL(mailer1)164.725 299.6 Q 144 279.6 144 315.6 DL 216
-279.6 144 279.6 DL 216 315.6 216 279.6 DL 144 315.6 216 315.6 DL(mailer2)
-272.725 299.6 Q 252 279.6 252 315.6 DL 324 279.6 252 279.6 DL 324 315.6 324
-279.6 DL 252 315.6 324 315.6 DL(mailer3)380.725 299.6 Q 360 279.6 360 315.6 DL
-432 279.6 360 279.6 DL 432 315.6 432 279.6 DL 360 315.6 432 315.6 DL 252 207.6
-180 171.6 DL 252 207.6 244.728 206.016 DL 252 207.6 246.384 202.776 DL 324
-207.6 396 171.6 DL 324 207.6 329.616 202.776 DL 324 207.6 331.272 206.016 DL
-180 279.6 252 243.6 DL 180 279.6 185.616 274.776 DL 180 279.6 187.272 278.016
-DL 396 279.6 324 243.6 DL 396 279.6 388.728 278.016 DL 396 279.6 390.384
-274.776 DL(Figure 1 \212 Sendmail System Structure.)208 346.8 Q 77 358.8 72
-358.8 DL 79 358.8 74 358.8 DL 84 358.8 79 358.8 DL 89 358.8 84 358.8 DL 94
-358.8 89 358.8 DL 99 358.8 94 358.8 DL 104 358.8 99 358.8 DL 109 358.8 104
-358.8 DL 114 358.8 109 358.8 DL 119 358.8 114 358.8 DL 124 358.8 119 358.8 DL
-129 358.8 124 358.8 DL 134 358.8 129 358.8 DL 139 358.8 134 358.8 DL 144 358.8
-139 358.8 DL 149 358.8 144 358.8 DL 154 358.8 149 358.8 DL 159 358.8 154 358.8
-DL 164 358.8 159 358.8 DL 169 358.8 164 358.8 DL 174 358.8 169 358.8 DL 179
-358.8 174 358.8 DL 184 358.8 179 358.8 DL 189 358.8 184 358.8 DL 194 358.8 189
-358.8 DL 199 358.8 194 358.8 DL 204 358.8 199 358.8 DL 209 358.8 204 358.8 DL
-214 358.8 209 358.8 DL 219 358.8 214 358.8 DL 224 358.8 219 358.8 DL 229 358.8
-224 358.8 DL 234 358.8 229 358.8 DL 239 358.8 234 358.8 DL 244 358.8 239 358.8
-DL 249 358.8 244 358.8 DL 254 358.8 249 358.8 DL 259 358.8 254 358.8 DL 264
-358.8 259 358.8 DL 269 358.8 264 358.8 DL 274 358.8 269 358.8 DL 279 358.8 274
-358.8 DL 284 358.8 279 358.8 DL 289 358.8 284 358.8 DL 294 358.8 289 358.8 DL
-299 358.8 294 358.8 DL 304 358.8 299 358.8 DL 309 358.8 304 358.8 DL 314 358.8
-309 358.8 DL 319 358.8 314 358.8 DL 324 358.8 319 358.8 DL 329 358.8 324 358.8
-DL 334 358.8 329 358.8 DL 339 358.8 334 358.8 DL 344 358.8 339 358.8 DL 349
-358.8 344 358.8 DL 354 358.8 349 358.8 DL 359 358.8 354 358.8 DL 364 358.8 359
-358.8 DL 369 358.8 364 358.8 DL 374 358.8 369 358.8 DL 379 358.8 374 358.8 DL
-384 358.8 379 358.8 DL 389 358.8 384 358.8 DL 394 358.8 389 358.8 DL 399 358.8
-394 358.8 DL 404 358.8 399 358.8 DL 409 358.8 404 358.8 DL 414 358.8 409 358.8
-DL 419 358.8 414 358.8 DL 424 358.8 419 358.8 DL 429 358.8 424 358.8 DL 434
-358.8 429 358.8 DL 439 358.8 434 358.8 DL 444 358.8 439 358.8 DL 449 358.8 444
-358.8 DL 454 358.8 449 358.8 DL 459 358.8 454 358.8 DL 464 358.8 459 358.8 DL
-469 358.8 464 358.8 DL 474 358.8 469 358.8 DL 479 358.8 474 358.8 DL 484 358.8
-479 358.8 DL 489 358.8 484 358.8 DL 494 358.8 489 358.8 DL 499 358.8 494 358.8
-DL 504 358.8 499 358.8 DL F0 2.5(2. O)72 394.8 R(VER)-.5 E(VIEW)-.55 E 2.5
-(2.1. System)87 418.8 R(Or)2.5 E(ganization)-.1 E/F2 10/Times-Italic@0 SF
-(Sendmail)127 435 Q F1 .874(neither interf)3.374 F .874
-(aces with the user nor does actual mail deli)-.1 F -.15(ve)-.25 G(ry).15 E
-5.873(.R)-.65 G(ather)431.241 435 Q 3.373(,i)-.4 G 3.373(tc)459.484 435 S .873
-(ollects a)470.077 435 R .619(message generated by a user interf)102 447 R .619
-(ace program \(UIP\) such as Berk)-.1 F(ele)-.1 E(y)-.15 E F2(Mail)3.12 E F1
-3.12(,M)C 3.12(S[)427.6 447 S(Crock)439.61 447 Q .62(er77b], or)-.1 F 1.428
-(MH [Borden79], edits the message as required by the destination netw)102 459 R
-1.427(ork, and calls appropriate)-.1 F .28(mailers to do mail deli)102 473 R
--.15(ve)-.25 G .281(ry or queueing for netw).15 F .281(ork transmission)-.1 F
-/F3 7/Times-Roman@0 SF(1)364.275 469 Q F1 5.281(.T)367.775 473 S .281
-(his discipline allo)381.666 473 R .281(ws the inser)-.25 F(-)-.2 E 1.354
-(tion of ne)102 485 R 3.854(wm)-.25 G 1.354(ailers at minimum cost.)161.642 485
-R 1.354(In this sense)6.354 F F2(sendmail)3.853 E F1 1.353
-(resembles the Message Processing)3.853 F(Module \(MPM\) of [Postel79b].)102
-497 Q F0 2.5(2.2. Interfaces)87 521 R(to the Outside W)2.5 E(orld)-.75 E F1
-.041(There are three w)127 537.2 R(ays)-.1 E F2(sendmail)2.541 E F1 .041
-(can communicate with the outside w)2.541 F .042(orld, both in recei)-.1 F .042
-(ving and)-.25 F 1.195(in sending mail.)102 549.2 R 1.194
-(These are using the con)6.194 F -.15(ve)-.4 G 1.194(ntional UNIX ar).15 F
-1.194(gument v)-.18 F 1.194(ector/return status, speaking)-.15 F(SMTP o)102
-561.2 Q -.15(ve)-.15 G 2.5(rap).15 G(air of UNIX pipes, and speaking SMTP o)
-162.53 561.2 Q -.15(ve)-.15 G 2.5(ra).15 G 2.5(ni)348.03 561.2 S
-(nterprocess\(or\) channel.)358.31 561.2 Q F0 2.5(2.2.1. Ar)102 585.2 R
-(gument v)-.1 E(ector/exit status)-.1 E F1 .52(This technique is the standard \
-UNIX method for communicating with the process.)142 601.4 R 3.02(Al)5.52 G(ist)
-494.55 601.4 Q .442(of recipients is sent in the ar)117 613.4 R .441(gument v)
--.18 F(ector)-.15 E 2.941(,a)-.4 G .441
-(nd the message body is sent on the standard input.)299.491 613.4 R(An)117
-625.4 Q .351(ything that the mailer prints is simply collected and sent back t\
-o the sender if there were an)-.15 F(y)-.15 E 2.621(problems. The)117 637.4 R
--.15(ex)2.621 G .121(it status from the mailer is collected after the message \
-is sent, and a diagnostic).15 F(is printed if appropriate.)117 649.4 Q .32 LW
-76 678.8 72 678.8 DL 80 678.8 76 678.8 DL 84 678.8 80 678.8 DL 88 678.8 84
-678.8 DL 92 678.8 88 678.8 DL 96 678.8 92 678.8 DL 100 678.8 96 678.8 DL 104
-678.8 100 678.8 DL 108 678.8 104 678.8 DL 112 678.8 108 678.8 DL 116 678.8 112
-678.8 DL 120 678.8 116 678.8 DL 124 678.8 120 678.8 DL 128 678.8 124 678.8 DL
-132 678.8 128 678.8 DL 136 678.8 132 678.8 DL 140 678.8 136 678.8 DL 144 678.8
-140 678.8 DL 148 678.8 144 678.8 DL 152 678.8 148 678.8 DL 156 678.8 152 678.8
-DL 160 678.8 156 678.8 DL 164 678.8 160 678.8 DL 168 678.8 164 678.8 DL 172
-678.8 168 678.8 DL 176 678.8 172 678.8 DL 180 678.8 176 678.8 DL 184 678.8 180
-678.8 DL 188 678.8 184 678.8 DL 192 678.8 188 678.8 DL 196 678.8 192 678.8 DL
-200 678.8 196 678.8 DL 204 678.8 200 678.8 DL 208 678.8 204 678.8 DL 212 678.8
-208 678.8 DL 216 678.8 212 678.8 DL/F4 5/Times-Roman@0 SF(1)93.6 689.2 Q/F5 8
-/Times-Roman@0 SF -.12(ex)3.2 K(cept when mailing to a \214le, when).12 E/F6 8
-/Times-Italic@0 SF(sendmail)2 E F5(does the deli)2 E -.12(ve)-.2 G(ry directly)
-.12 E(.)-.52 E EP
-%%Page: 4 4
-%%BeginPageSetup
-BP
-%%EndPageSetup
-/F0 10/Times-Bold@0 SF 192.28(SMM:9-4 SENDMAIL)72 60 R 2.5<8a41>2.5 G 2.5(nI)
-383.99 60 S(nter)395.94 60 Q(netw)-.15 E(ork Mail Router)-.1 E 2.5(2.2.2. SMTP)
-102 96 R -.1(ove)2.5 G 2.5(rp).1 G(ipes)186.52 96 Q/F1 10/Times-Roman@0 SF .774
-(The SMTP protocol [Postel82] can be used to run an interacti)142 112.2 R 1.074
--.15(ve l)-.25 H .774(ock-step interf).15 F .774(ace with)-.1 F .507
-(the mailer)117 124.2 R 5.507(.A)-.55 G .506(subprocess is still created, b)
-175.461 124.2 R .506(ut no recipient addresses are passed to the mailer via)-.2
-F .075(the ar)117 136.2 R .075(gument list.)-.18 F .075(Instead, the)5.075 F
-2.575(ya)-.15 G .075
-(re passed one at a time in commands sent to the processes stan-)249.805 136.2
-R .19(dard input.)117 148.2 R(An)5.19 E .19(ything appearing on the standard o\
-utput must be a reply code in a special format.)-.15 F F0 2.5(2.2.3. SMTP)102
-172.2 R -.1(ove)2.5 G 2.5(ra).1 G 2.5(nI)185.96 172.2 S(PC connection)197.91
-172.2 Q F1 .366(This technique is similar to the pre)142 188.4 R .366
-(vious technique, e)-.25 F .366(xcept that it uses a 4.2bsd IPC chan-)-.15 F
-.953(nel [UNIX83].)117 200.4 R .953(This method is e)5.953 F .953
-(xceptionally \215e)-.15 F .952
-(xible in that the mailer need not reside on the)-.15 F(same machine.)117 212.4
-Q(It is normally used to connect to a sendmail process on another machine.)5 E
-F0 2.5(2.3. Operational)87 236.4 R(Description)2.5 E F1 .228(When a sender w)
-127 252.6 R .228(ants to send a message, it issues a request to)-.1 F/F2 10
-/Times-Italic@0 SF(sendmail)2.729 E F1 .229(using one of the three)2.729 F
-1.028(methods described abo)102 264.6 R -.15(ve)-.15 G(.).15 E F2(Sendmail)
-6.028 E F1 1.028(operates in tw)3.528 F 3.528(od)-.1 G 1.028(istinct phases.)
-325.706 264.6 R 1.028(In the \214rst phase, it collects)6.028 F .612
-(and stores the message.)102 276.6 R .612(In the second phase, message deli)
-5.612 F -.15(ve)-.25 G .612(ry occurs.).15 F .612(If there were errors during)
-5.612 F 1.59(processing during the second phase,)102 288.6 R F2(sendmail)4.09 E
-F1 1.59(creates and returns a ne)4.09 F 4.09(wm)-.25 G 1.59
-(essage describing the)415.84 288.6 R
-(error and/or returns an status code telling what went wrong.)102 300.6 Q F0
-2.5(2.3.1. Ar)102 324.6 R(gument pr)-.1 E(ocessing and addr)-.18 E(ess parsing)
--.18 E F1(If)142 340.8 Q F2(sendmail)3.321 E F1 .821
-(is called using one of the tw)3.321 F 3.322(os)-.1 G .822
-(ubprocess techniques, the ar)320.66 340.8 R .822(guments are \214rst)-.18 F
-.797(scanned and option speci\214cations are processed.)117 352.8 R .796
-(Recipient addresses are then collected, either)5.796 F .717(from the command \
-line or from the SMTP RCPT command, and a list of recipients is created.)117
-364.8 R .347(Aliases are e)117 376.8 R .347
-(xpanded at this step, including mailing lists.)-.15 F .347(As much v)5.347 F
-.346(alidation as possible of the)-.25 F 1.001
-(addresses is done at this step: syntax is check)117 388.8 R 1.002
-(ed, and local addresses are v)-.1 F 1.002(eri\214ed, b)-.15 F 1.002
-(ut detailed)-.2 F .709
-(checking of host names and addresses is deferred until deli)117 400.8 R -.15
-(ve)-.25 G(ry).15 E 5.708(.F)-.65 G(orw)388.946 400.8 Q .708
-(arding is also performed)-.1 F(as the local addresses are v)117 412.8 Q
-(eri\214ed.)-.15 E F2(Sendmail)142 429 Q F1 .307
-(appends each address to the recipient list after parsing.)2.807 F .307
-(When a name is aliased)5.307 F .322(or forw)117 441 R .322(arded, the old nam\
-e is retained in the list, and a \215ag is set that tells the deli)-.1 F -.15
-(ve)-.25 G .322(ry phase to).15 F .479(ignore this recipient.)117 453 R .479
-(This list is k)5.479 F .479(ept free from duplicates, pre)-.1 F -.15(ve)-.25 G
-.48(nting alias loops and duplicate).15 F(messages deli)117 465 Q -.15(ve)-.25
-G(rd to the same recipient, as might occur if a person is in tw).15 E 2.5(og)
--.1 G(roups.)428.12 465 Q F0 2.5(2.3.2. Message)102 489 R(collection)2.5 E F2
-(Sendmail)142 505.2 Q F1 .454(then collects the message.)2.954 F .454
-(The message should ha)5.454 F .754 -.15(ve a h)-.2 H .453(eader at the be).15
-F(ginning.)-.15 E .778(No formatting requirements are imposed on the message e)
-117 517.2 R .778(xcept that the)-.15 F 3.278(ym)-.15 G .778(ust be lines of te)
-427.708 517.2 R(xt)-.15 E .78(\(i.e., binary data is not allo)117 529.2 R 3.28
-(wed\). The)-.25 F .779(header is parsed and stored in memory)3.28 F 3.279(,a)
--.65 G .779(nd the body of)443.613 529.2 R(the message is sa)117 541.2 Q -.15
-(ve)-.2 G 2.5(di).15 G 2.5(nat)204.97 541.2 S(emporary \214le.)222.19 541.2 Q
-3.227 -.8(To s)142 557.4 T 1.627(implify the program interf).8 F 1.628
-(ace, the message is collected e)-.1 F -.15(ve)-.25 G 4.128(ni).15 G 4.128(fn)
-420.536 557.4 S 4.128(oa)432.994 557.4 S 1.628(ddresses were)446.562 557.4 R
--.25(va)117 569.4 S 2.5(lid. The).25 F(message will be returned with an error)
-2.5 E(.)-.55 E F0 2.5(2.3.3. Message)102 593.4 R(deli)2.5 E -.1(ve)-.1 G(ry).1
-E F1 -.15(Fo)142 609.6 S 2.618(re).15 G .117
-(ach unique mailer and host in the recipient list,)162.798 609.6 R F2(sendmail)
-2.617 E F1 .117(calls the appropriate mailer)2.617 F(.)-.55 E .619
-(Each mailer in)117 621.6 R -.2(vo)-.4 G .619(cation sends to all users recei)
-.2 F .619(ving the message on one host.)-.25 F .62(Mailers that only)5.62 F
-(accept one recipient at a time are handled properly)117 633.6 Q(.)-.65 E .47
-(The message is sent to the mailer using one of the same three interf)142 649.8
-R .47(aces used to submit a)-.1 F 1.465(message to sendmail.)117 661.8 R 1.465
-(Each cop)6.465 F 3.965(yo)-.1 G 3.965(ft)263.925 661.8 S 1.465
-(he message is prepended by a customized header)274 661.8 R 6.465(.T)-.55 G(he)
-494.56 661.8 Q 1.455(mailer status code is caught and check)117 673.8 R 1.455
-(ed, and a suitable error message gi)-.1 F -.15(ve)-.25 G 3.955(na).15 G 3.955
-(sa)448.115 673.8 S(ppropriate.)460.4 673.8 Q .589(The e)117 685.8 R .589(xit \
-code must conform to a system standard or a generic message \(\231Service una)
--.15 F -.25(va)-.2 G(ilable\232\)).25 E(is gi)117 697.8 Q -.15(ve)-.25 G(n.).15
-E EP
-%%Page: 5 5
-%%BeginPageSetup
-BP
-%%EndPageSetup
-/F0 10/Times-Bold@0 SF(SENDMAIL \212 An Inter)72 60 Q(netw)-.15 E
-(ork Mail Router)-.1 E(SMM:9-5)462.9 60 Q 2.5(2.3.4. Queueing)102 96 R -.25(fo)
-2.5 G 2.5(rr).25 G(etransmission)192.4 96 Q/F1 10/Times-Roman@0 SF .209(If the\
- mailer returned an status that indicated that it might be able to handle the \
-mail later)142 112.2 R(,)-.4 E/F2 10/Times-Italic@0 SF(sendmail)117 124.2 Q F1
-(will queue the mail and try ag)2.5 E(ain later)-.05 E(.)-.55 E F0 2.5
-(2.3.5. Retur)102 148.2 R 2.5(nt)-.15 G 2.5(os)165.73 148.2 S(ender)177.12
-148.2 Q F1 .588(If errors occur during processing,)142 164.4 R F2(sendmail)
-3.088 E F1 .589(returns the message to the sender for retrans-)3.088 F 3.133
-(mission. The)117 176.4 R .632(letter can be mailed back or written in the \
-\214le \231dead.letter\232 in the sender')3.133 F 3.132(sh)-.55 G(ome)486.78
-176.4 Q(directory)117 190.4 Q/F3 7/Times-Roman@0 SF(2)153.1 186.4 Q F1(.)156.6
-190.4 Q F0 2.5(2.4. Message)87 214.4 R(Header Editing)2.5 E F1 1.756
-(Certain editing of the message header occurs automatically)127 230.6 R 6.756
-(.H)-.65 G 1.756(eader lines can be inserted)391.456 230.6 R .41
-(under control of the con\214guration \214le.)102 242.6 R .41
-(Some lines can be mer)5.41 F .41(ged; for e)-.18 F .41
-(xample, a \231From:\232 line and)-.15 F 2.5<6199>102 254.6 S
-(Full-name:\232 line can be mer)113.38 254.6 Q
-(ged under certain circumstances.)-.18 E F0 2.5(2.5. Con\214guration)87 278.6 R
-(File)2.5 E F1 .798(Almost all con\214guration information is read at runtime \
-from an ASCII \214le, encoding macro)127 294.8 R .679
-(de\214nitions \(de\214ning the v)102 306.8 R .678
-(alue of macros used internally\), header declarations \(telling sendmail the)
--.25 F 1.009(format of header lines that it will process specially)102 318.8 R
-3.509(,i)-.65 G 1.009(.e., lines that it will add or reformat\), mailer)320.398
-318.8 R .478(de\214nitions \(gi)102 330.8 R .478(ving information such as the \
-location and characteristics of each mailer\), and address)-.25 F(re)102 342.8
-Q .428(writing rules \(a limited production system to re)-.25 F .429
-(write addresses which is used to parse and re)-.25 F(write)-.25 E
-(the addresses\).)102 354.8 Q 2.828 -.8(To i)127 371 T(mpro).8 E 1.528 -.15
-(ve p)-.15 H 1.228(erformance when reading the con\214guration \214le, a memor\
-y image can be pro-).15 F 2.5(vided. This)102 383 R(pro)2.5 E
-(vides a \231compiled\232 form of the con\214guration \214le.)-.15 E F0 2.5
-(3. USA)72 407 R(GE AND IMPLEMENT)-.55 E -.95(AT)-.9 G(ION).95 E 2.5(3.1. Ar)87
-431 R(guments)-.1 E F1(Ar)127 447.2 Q .376
-(guments may be \215ags and addresses.)-.18 F .377(Flags set v)5.377 F .377
-(arious processing options.)-.25 F -.15(Fo)5.377 G(llo).15 E .377(wing \215ag)
--.25 F(ar)102 459.2 Q .281(guments, address ar)-.18 F .281(guments may be gi)
--.18 F -.15(ve)-.25 G .281(n, unless we are running in SMTP mode.).15 F .28
-(Addresses fol-)5.28 F(lo)102 471.2 Q 2.5(wt)-.25 G(he syntax in RFC822 [Crock)
-122.03 471.2 Q(er82] for ARP)-.1 E(ANET address formats.)-.92 E
-(In brief, the format is:)5 E 12.5(\(1\) An)107 487.4 R
-(ything in parentheses is thro)-.15 E(wn a)-.25 E -.1(wa)-.15 G 2.5(y\().1 G
-(as a comment\).)299.65 487.4 Q 12.5(\(2\) An)107 503.6 R .051
-(ything in angle brack)-.15 F .051(ets \(\231<)-.1 F .051
-(>\232\) is preferred o)1.666 F -.15(ve)-.15 G 2.551(ra).15 G -.15(ny)348.064
-503.6 S .051(thing else.).15 F .051(This rule implements the)5.051 F(ARP)133.66
-515.6 Q(ANET standard that addresses of the form)-.92 E
-(user name <machine-address>)173.66 531.8 Q(will send to the electronic \231ma\
-chine-address\232 rather than the human \231user name.)133.66 548 Q<9a>-.7 E
-12.5(\(3\) Double)107 564.2 R 2.246(quotes \()4.746 F -2.754 2.5("\) q)2.5 H
-2.246(uote phrases; backslashes quote characters.)224.188 564.2 R 2.246
-(Backslashes are more)7.246 F(po)133.66 576.2 Q .654(werful in that the)-.25 F
-3.154(yw)-.15 G .655(ill cause otherwise equi)229.196 576.2 R -.25(va)-.25 G
-.655(lent phrases to compare dif).25 F .655(ferently \212 for)-.25 F -.15(ex)
-133.66 588.2 S(ample,).15 E F2(user)2.5 E F1(and)2.5 E F2("user")2.5 E F1
-(are equi)2.5 E -.25(va)-.25 G(lent, b).25 E(ut)-.2 E F2(\\user)2.5 E F1
-(is dif)2.5 E(ferent from either of them.)-.25 E -.15(Pa)127 604.4 S 1.12
-(rentheses, angle brack).15 F 1.12
-(ets, and double quotes must be properly balanced and nested.)-.1 F(The)6.12 E
-(re)102 618.4 Q(writing rules control remaining parsing)-.25 E F3(3)266.17
-614.4 Q F1(.)269.67 618.4 Q .32 LW 76 646 72 646 DL 80 646 76 646 DL 84 646 80
-646 DL 88 646 84 646 DL 92 646 88 646 DL 96 646 92 646 DL 100 646 96 646 DL 104
-646 100 646 DL 108 646 104 646 DL 112 646 108 646 DL 116 646 112 646 DL 120 646
-116 646 DL 124 646 120 646 DL 128 646 124 646 DL 132 646 128 646 DL 136 646 132
-646 DL 140 646 136 646 DL 144 646 140 646 DL 148 646 144 646 DL 152 646 148 646
-DL 156 646 152 646 DL 160 646 156 646 DL 164 646 160 646 DL 168 646 164 646 DL
-172 646 168 646 DL 176 646 172 646 DL 180 646 176 646 DL 184 646 180 646 DL 188
-646 184 646 DL 192 646 188 646 DL 196 646 192 646 DL 200 646 196 646 DL 204 646
-200 646 DL 208 646 204 646 DL 212 646 208 646 DL 216 646 212 646 DL/F4 5
-/Times-Roman@0 SF(2)93.6 656.4 Q/F5 8/Times-Roman@0 SF(Ob)3.2 I(viously)-.12 E
-2.226(,i)-.52 G 2.226(ft)135.246 659.6 S .226(he site gi)142.36 659.6 R .226(v\
-ing the error is not the originating site, the only reasonable option is to ma\
-il back to the sender)-.2 F 4.227(.A)-.44 G(lso,)492.664 659.6 Q .191
-(there are man)72 669.2 R 2.191(ym)-.12 G .19(ore error disposition options, b)
-128.213 669.2 R .19(ut the)-.16 F 2.19(yo)-.12 G .19(nly ef)255.514 669.2 R .19
-(fect the error message \212 the \231return to sender\232 function is al)-.2 F
--.08(wa)-.08 G .19(ys han-).08 F(dled in one of these tw)72 678.8 Q 2(ow)-.08 G
-(ays.)156.272 678.8 Q F4(3)93.6 689.2 Q F5
-(Disclaimer: Some special processing is done after re)3.2 I
-(writing local names; see belo)-.2 E -.52(w.)-.2 G EP
-%%Page: 6 6
-%%BeginPageSetup
-BP
-%%EndPageSetup
-/F0 10/Times-Bold@0 SF 192.28(SMM:9-6 SENDMAIL)72 60 R 2.5<8a41>2.5 G 2.5(nI)
-383.99 60 S(nter)395.94 60 Q(netw)-.15 E(ork Mail Router)-.1 E 2.5(3.2. Mail)87
-96 R(to Files and Pr)2.5 E(ograms)-.18 E/F1 10/Times-Roman@0 SF .609
-(Files and programs are le)127 112.2 R .609(gitimate message recipients.)-.15 F
-.609(Files pro)5.609 F .609(vide archi)-.15 F -.25(va)-.25 G 3.109(ls).25 G .61
-(torage of mes-)445.02 112.2 R .124
-(sages, useful for project administration and history)102 124.2 R 5.124(.P)-.65
-G .124(rograms are useful as recipients in a v)318.308 124.2 R .124(ariety of)
--.25 F .69(situations, for e)102 136.2 R .691(xample, to maintain a public rep\
-ository of systems messages \(such as the Berk)-.15 F(ele)-.1 E(y)-.15 E/F2 10
-/Times-Italic@0 SF(msgs)102 148.2 Q F1(program, or the MARS system [Sattle)2.5
-E(y78]\).)-.15 E(An)127 164.4 Q 3.188(ya)-.15 G .688(ddress passing through th\
-e initial parsing algorithm as a local address \(i.e, not appear)151.698 164.4
-R(-)-.2 E .276(ing to be a v)102 176.4 R .276
-(alid address for another mailer\) is scanned for tw)-.25 F 2.776(os)-.1 G .277
-(pecial cases.)362.128 176.4 R .277(If pre\214x)5.277 F .277(ed by a v)-.15 F
-(erti-)-.15 E .18(cal bar \(\231)102 188.4 R .833<7c9a>.833 G 2.68(\)t)-.833 G
-.179(he rest of the address is processed as a shell command.)156.456 188.4 R
-.179(If the user name be)5.179 F .179(gins with a)-.15 F(slash mark \(\231/)102
-200.4 Q(\232\) the name is used as a \214le name, instead of a login name.).833
-E .241(Files that ha)127 216.6 R .541 -.15(ve s)-.2 H .241
-(etuid or setgid bits set b).15 F .241(ut no e)-.2 F -.15(xe)-.15 G .241
-(cute bits set ha).15 F .541 -.15(ve t)-.2 H .241(hose bits honored if).15 F F2
-(send-)2.742 E(mail)102 228.6 Q F1(is running as root.)2.5 E F0 2.5
-(3.3. Aliasing,)87 252.6 R -.25(Fo)2.5 G(rwarding, Inclusion).25 E F2(Sendmail)
-127 268.8 Q F1 1.075(reroutes mail three w)3.575 F 3.575(ays. Aliasing)-.1 F
-1.074(applies system wide.)3.575 F -.15(Fo)6.074 G(rw).15 E 1.074(arding allo)
--.1 F 1.074(ws each)-.25 F .233
-(user to reroute incoming mail destined for that account.)102 280.8 R .233
-(Inclusion directs)5.233 F F2(sendmail)2.733 E F1 .233(to read a \214le for)
-2.733 F 2.5(al)102 292.8 S
-(ist of addresses, and is normally used in conjunction with aliasing.)111.72
-292.8 Q F0 2.5(3.3.1. Aliasing)102 316.8 R F1 1.554
-(Aliasing maps names to address lists using a system-wide \214le.)142 333 R
-1.553(This \214le is inde)6.553 F -.15(xe)-.15 G 4.053(dt).15 G(o)499 333 Q 1.1
-(speed access.)117 345 R 1.101(Only names that parse as local are allo)6.1 F
-1.101(wed as aliases; this guarantees a unique)-.25 F -.1(ke)117 357 S 2.5(y\()
--.05 G(since there are no nicknames for the local host\).)137.02 357 Q F0 2.5
-(3.3.2. F)102 381 R(orwarding)-.25 E F1 .651
-(After aliasing, recipients that are local and v)142 397.2 R .651
-(alid are check)-.25 F .65(ed for the e)-.1 F .65(xistence of a \231.for)-.15 F
-(-)-.2 E -.1(wa)117 409.2 S .493(rd\232 \214le in their home directory).1 F
-5.493(.I)-.65 G 2.994(fi)264.178 409.2 S 2.994(te)273.282 409.2 S .494
-(xists, the message is)283.346 409.2 R F2(not)2.994 E F1 .494
-(sent to that user)2.994 F 2.994(,b)-.4 G .494(ut rather to)459.132 409.2 R .37
-(the list of users in that \214le.)117 421.2 R .37
-(Often this list will contain only one address, and the feature will be)5.37 F
-(used for netw)117 433.2 Q(ork mail forw)-.1 E(arding.)-.1 E -.15(Fo)142 449.4
-S(rw).15 E 1.151(arding also permits a user to specify a pri)-.1 F -.25(va)-.25
-G 1.152(te incoming mailer).25 F 6.152(.F)-.55 G 1.152(or e)437.346 449.4 R
-1.152(xample, for)-.15 F(-)-.2 E -.1(wa)117 461.4 S(rding to:).1 E -2.5 .833
-("| /)157 477.6 T(usr/local/ne)-.833 E(wmail myname")-.25 E(will use a dif)117
-493.8 Q(ferent incoming mailer)-.25 E(.)-.55 E F0 2.5(3.3.3. Inclusion)102
-517.8 R F1(Inclusion is speci\214ed in RFC 733 [Crock)142 534 Q(er77a] syntax:)
--.1 E(:Include: pathname)157 550.2 Q .391
-(An address of this form reads the \214le speci\214ed by)117 566.4 R F2
-(pathname)2.891 E F1 .391(and sends to all users listed in that)2.891 F
-(\214le.)117 578.4 Q .644(The intent is)142 594.6 R F2(not)3.144 E F1 .644
-(to support direct use of this feature, b)3.144 F .644
-(ut rather to use this as a subset of)-.2 F 2.5(aliasing. F)117 606.6 R(or e)
--.15 E(xample, an alias of the form:)-.15 E
-(project: :include:/usr/project/userlist)157 622.8 Q 1.93(is a method of letti\
-ng a project maintain a mailing list without interaction with the system)117
-639 R(administration, e)117 651 Q -.15(ve)-.25 G 2.5(ni).15 G 2.5(ft)203.54 651
-S(he alias \214le is protected.)212.15 651 Q 2.024(It is not necessary to reb)
-142 667.2 R 2.024(uild the inde)-.2 F 4.524(xo)-.15 G 4.524(nt)317.822 667.2 S
-2.025(he alias database when a :include: list is)330.126 667.2 R(changed.)117
-679.2 Q EP
-%%Page: 7 7
-%%BeginPageSetup
-BP
-%%EndPageSetup
-/F0 10/Times-Bold@0 SF(SENDMAIL \212 An Inter)72 60 Q(netw)-.15 E
-(ork Mail Router)-.1 E(SMM:9-7)462.9 60 Q 2.5(3.4. Message)87 96 R(Collection)
-2.5 E/F1 10/Times-Roman@0 SF .857
-(Once all recipient addresses are parsed and v)127 112.2 R .857
-(eri\214ed, the message is collected.)-.15 F .856(The message)5.857 F
-(comes in tw)102 124.2 Q 2.5(op)-.1 G
-(arts: a message header and a message body)162.73 124.2 Q 2.5(,s)-.65 G
-(eparated by a blank line.)343.42 124.2 Q
-(The header is formatted as a series of lines of the form)127 140.4 Q
-(\214eld-name: \214eld-v)178 156.6 Q(alue)-.25 E(Field-v)102 172.8 Q 1.366
-(alue can be split across lines by starting the follo)-.25 F 1.366
-(wing lines with a space or a tab)-.25 F 6.366(.S)-.4 G(ome)486.78 172.8 Q .211
-(header \214elds ha)102 184.8 R .511 -.15(ve s)-.2 H .211
-(pecial internal meaning, and ha).15 F .511 -.15(ve a)-.2 H .211
-(ppropriate special processing.).15 F .21(Other headers)5.21 F
-(are simply passed through.)102 196.8 Q
-(Some header \214elds may be added automatically)5 E 2.5(,s)-.65 G
-(uch as time stamps.)413.53 196.8 Q .86(The body is a series of te)127 213 R
-.861(xt lines.)-.15 F .861(It is completely uninterpreted and untouched, e)
-5.861 F .861(xcept that)-.15 F 1.43(lines be)102 225 R 1.43
-(ginning with a dot ha)-.15 F 1.729 -.15(ve t)-.2 H 1.429
-(he dot doubled when transmitted o).15 F -.15(ve)-.15 G 3.929(ra).15 G 3.929
-(nS)407.213 225 S 1.429(MTP channel.)421.702 225 R(This)6.429 E -.15(ex)102 237
-S(tra dot is stripped by the recei).15 E -.15(ve)-.25 G -.55(r.).15 G F0 2.5
-(3.5. Message)87 261 R(Deli)2.5 E -.1(ve)-.1 G(ry).1 E F1 .028
-(The send queue is ordered by recei)127 277.2 R .029
-(ving host before transmission to implement message batch-)-.25 F 3.07
-(ing. Each)102 289.2 R .57(address is mark)3.07 F .57
-(ed as it is sent so rescanning the list is safe.)-.1 F .57(An ar)5.57 F .57
-(gument list is b)-.18 F .57(uilt as)-.2 F 1.138(the scan proceeds.)102 301.2 R
-1.139(Mail to \214les is detected during the scan of the send list.)6.139 F
-1.139(The interf)6.139 F 1.139(ace to the)-.1 F
-(mailer is performed using one of the techniques described in section 2.2.)102
-313.2 Q .996(After a connection is established,)127 329.4 R/F2 10
-/Times-Italic@0 SF(sendmail)3.496 E F1(mak)3.495 E .995(es the per)-.1 F .995
-(-mailer changes to the header and)-.2 F .236(sends the result to the mailer)
-102 341.4 R 5.236(.I)-.55 G 2.736(fa)228.406 341.4 S .537 -.15(ny m)238.912
-341.4 T .237(ail is rejected by the mailer).15 F 2.737(,a\215)-.4 G .237
-(ag is set to in)386.628 341.4 R -.2(vo)-.4 G .437 -.1(ke t).2 H .237
-(he return-).1 F(to-sender function after all deli)102 353.4 Q -.15(ve)-.25 G
-(ry completes.).15 E F0 2.5(3.6. Queued)87 377.4 R(Messages)2.5 E F1 .163
-(If the mailer returns a \231temporary f)127 393.6 R .163(ailure\232 e)-.1 F
-.162(xit status, the message is queued.)-.15 F 2.662(Ac)5.162 G .162
-(ontrol \214le is)455.336 393.6 R .85
-(used to describe the recipients to be sent to and v)102 405.6 R .851
-(arious other parameters.)-.25 F .851(This control \214le is for)5.851 F(-)-.2
-E 1.011(matted as a series of lines, each describing a sender)102 417.6 R 3.511
-(,ar)-.4 G 1.011(ecipient, the time of submission, or some)333.494 417.6 R .776
-(other salient parameter of the message.)102 429.6 R .776
-(The header of the message is stored in the control \214le, so)5.776 F(that th\
-e associated data \214le in the queue is just the temporary \214le that w)102
-441.6 Q(as originally collected.)-.1 E F0 2.5(3.7. Con\214guration)87 465.6 R
-F1 .493(Con\214guration is controlled primarily by a con\214guration \214le re\
-ad at startup.)127 481.8 R F2(Sendmail)5.492 E F1(should)2.992 E
-(not need to be recomplied e)102 493.8 Q(xcept)-.15 E 12.5(\(1\) T)107 510 R
-2.5(oc)-.8 G(hange operating systems \(V6, V7/32V)150.91 510 Q 2.5(,4)-1.29 G
-(BSD\).)313.21 510 Q 12.5(\(2\) T)107 526.2 R 2.5(or)-.8 G(emo)149.8 526.2 Q .3
--.15(ve o)-.15 H 2.5(ri).15 G(nsert the DBM \(UNIX database\) library)192.27
-526.2 Q(.)-.65 E 12.5(\(3\) T)107 542.4 R 2.5(oc)-.8 G(hange ARP)150.91 542.4 Q
-(ANET reply codes.)-.92 E 12.5(\(4\) T)107 558.6 R 2.5(oa)-.8 G
-(dd headers \214elds requiring special processing.)150.91 558.6 Q .434
-(Adding mailers or changing parsing \(i.e., re)102 574.8 R .435
-(writing\) or routing information does not require recom-)-.25 F(pilation.)102
-586.8 Q 1.317(If the mail is being sent by a local user)127 603 R 3.817(,a)-.4
-G 1.317(nd the \214le \231.mailcf\232 e)303.914 603 R 1.317
-(xists in the sender')-.15 F 3.817(sh)-.55 G(ome)486.78 603 Q(directory)102 615
-Q 2.721(,t)-.65 G .221(hat \214le is read as a con\214guration \214le after th\
-e system con\214guration \214le.)145.451 615 R .222(The primary use)5.222 F
-(of this feature is to add header lines.)102 627 Q 3.25(The con\214guration \
-\214le encodes macro de\214nitions, header de\214nitions, mailer de\214nitions\
-,)127 643.2 R(re)102 655.2 Q(writing rules, and options.)-.25 E F0 2.5
-(3.7.1. Macr)102 679.2 R(os)-.18 E F1 .332(Macros can be used in three w)142
-695.4 R 2.833(ays. Certain)-.1 F .333(macros transmit unstructured te)2.833 F
-.333(xtual informa-)-.15 F .07(tion into the mail system, such as the name)117
-707.4 R F2(sendmail)2.57 E F1 .07
-(will use to identify itself in error messages.)2.57 F 1.247
-(Other macros transmit information from)117 719.4 R F2(sendmail)3.747 E F1
-1.247(to the con\214guration \214le for use in creating)3.747 F EP
-%%Page: 8 8
-%%BeginPageSetup
-BP
-%%EndPageSetup
-/F0 10/Times-Bold@0 SF 192.28(SMM:9-8 SENDMAIL)72 60 R 2.5<8a41>2.5 G 2.5(nI)
-383.99 60 S(nter)395.94 60 Q(netw)-.15 E(ork Mail Router)-.1 E/F1 10
-/Times-Roman@0 SF .312(other \214elds \(such as ar)117 96 R .312(gument v)-.18
-F .312(ectors to mailers\); e.g., the name of the sender)-.15 F 2.811(,a)-.4 G
-.311(nd the host and)442.237 96 R .848(user of the recipient.)117 108 R .848
-(Other macros are unused internally)5.848 F 3.348(,a)-.65 G .848
-(nd can be used as shorthand in the)361.142 108 R(con\214guration \214le.)117
-120 Q F0 2.5(3.7.2. Header)102 144 R(declarations)2.5 E F1 .355
-(Header declarations inform)142 160.2 R/F2 10/Times-Italic@0 SF(sendmail)2.854
-E F1 .354(of the format of kno)2.854 F .354(wn header lines.)-.25 F(Kno)5.354 E
-.354(wledge of)-.25 F 2.5(af)117 172.2 S .5 -.25(ew h)127.27 172.2 T
-(eader lines is b).25 E(uilt into)-.2 E F2(sendmail)2.5 E F1 2.5(,s)C
-(uch as the \231From:\232 and \231Date:\232 lines.)284.59 172.2 Q 1.201(Most c\
-on\214gured headers will be automatically inserted in the outgoing message if \
-the)142 188.4 R(y)-.15 E(don')117 200.4 Q 2.5(te)-.18 G
-(xist in the incoming message.)144.72 200.4 Q
-(Certain headers are suppressed by some mailers.)5 E F0 2.5(3.7.3. Mailer)102
-224.4 R(declarations)2.5 E F1 1.756(Mailer declarations tell)142 240.6 R F2
-(sendmail)4.256 E F1 1.756(of the v)4.256 F 1.756(arious mailers a)-.25 F -.25
-(va)-.2 G 1.756(ilable to it.).25 F 1.755(The de\214nition)6.755 F .119
-(speci\214es the internal name of the mailer)117 252.6 R 2.619(,t)-.4 G .12
-(he pathname of the program to call, some \215ags associ-)285.183 252.6 R 2.036
-(ated with the mailer)117 264.6 R 4.536(,a)-.4 G 2.036(nd an ar)213.894 264.6 R
-2.036(gument v)-.18 F 2.036(ector to be used on the call; this v)-.15 F 2.035
-(ector is macro-)-.15 F -.15(ex)117 276.6 S(panded before use.).15 E F0 2.5
-(3.7.4. Addr)102 300.6 R(ess r)-.18 E(ewriting rules)-.18 E F1 .458
-(The heart of address parsing in)142 316.8 R F2(sendmail)2.959 E F1 .459
-(is a set of re)2.959 F .459(writing rules.)-.25 F .459(These are an ordered)
-5.459 F .561(list of pattern-replacement rules, \(some)117 328.8 R .561
-(what lik)-.25 F 3.061(eap)-.1 G .561(roduction system, e)328.867 328.8 R .56
-(xcept that order is criti-)-.15 F 1.905
-(cal\), which are applied to each address.)117 340.8 R 1.905(The address is re)
-6.905 F 1.906(written te)-.25 F 1.906(xtually until it is either)-.15 F(re)117
-352.8 Q .308(written into a special canonical form \(i.e., a \(mailer)-.25 F
-2.807(,h)-.4 G .307(ost, user\) 3-tuple, such as {arpanet, usc-)342.118 352.8 R
-.64(isif, postel} representing the address \231postel@usc-isif\232\), or it f)
-117 364.8 R .641(alls of)-.1 F 3.141(ft)-.25 G .641(he end.)406.466 364.8 R
-.641(When a pattern)5.641 F(matches, the rule is reapplied until it f)117 376.8
-Q(ails.)-.1 E 1.222
-(The con\214guration \214le also supports the editing of addresses into dif)142
-393 R 1.221(ferent formats.)-.25 F -.15(Fo)6.221 G(r).15 E -.15(ex)117 405 S
-(ample, an address of the form:).15 E(ucsfcgl!tef)157 421.2 Q
-(might be mapped into:)117 437.4 Q(tef@ucsfcgl.UUCP)157 453.6 Q
-(to conform to the domain syntax.)117 469.8 Q -.35(Tr)5 G
-(anslations can also be done in the other direction.).35 E F0 2.5
-(3.7.5. Option)102 493.8 R(setting)2.5 E F1 1.168(There are se)142 510 R -.15
-(ve)-.25 G 1.169(ral options that can be set from the con\214guration \214le.)
-.15 F 1.169(These include the)6.169 F(pathnames of v)117 522 Q
-(arious support \214les, timeouts, def)-.25 E(ault modes, etc.)-.1 E F0 2.5
-(4. COMP)72 546 R(ARISON WITH O)-.74 E(THER MAILERS)-.4 E 2.5(4.1. Deli)87 570
-R -.1(ve)-.1 G(rmail).1 E F2(Sendmail)127 586.2 Q F1(is an outgro)2.5 E(wth of)
--.25 E F2(delivermail)2.5 E F1 5(.T)C(he primary dif)301.18 586.2 Q
-(ferences are:)-.25 E 12.5(\(1\) Con\214guration)107 602.4 R .273
-(information is not compiled in.)2.773 F .272(This change simpli\214es man)
-5.273 F 2.772(yo)-.15 G 2.772(ft)445.686 602.4 S .272(he problems)454.568 602.4
-R(of mo)133.66 614.4 Q(ving to other machines.)-.15 E(It also allo)5 E
-(ws easy deb)-.25 E(ugging of ne)-.2 E 2.5(wm)-.25 G(ailers.)413.89 614.4 Q
-12.5(\(2\) Address)107 630.6 R .681(parsing is more \215e)3.181 F 3.182
-(xible. F)-.15 F .682(or e)-.15 F(xample,)-.15 E F2(delivermail)3.182 E F1 .682
-(only supported one g)3.182 F(ate)-.05 E -.1(wa)-.25 G 3.182(yt).1 G(o)499
-630.6 Q(an)133.66 642.6 Q 2.817(yn)-.15 G(etw)155.767 642.6 Q .317
-(ork, whereas)-.1 F F2(sendmail)2.817 E F1 .317(can be sensiti)2.817 F .616
--.15(ve t)-.25 H 2.816(oh).15 G .316(ost names and reroute to dif)345.224 642.6
-R .316(ferent g)-.25 F(ate-)-.05 E -.1(wa)133.66 654.6 S(ys.).1 E 12.5(\(3\) F)
-107 670.8 R(orw)-.15 E 1.627(arding and :include: features eliminate the requi\
-rement that the system alias \214le be)-.1 F .074(writable by an)133.66 682.8 R
-2.574(yu)-.15 G .073
-(ser \(or that an update program be written, or that the system administration)
-203.442 682.8 R(mak)133.66 694.8 Q 2.5(ea)-.1 G(ll changes\).)162.16 694.8 Q
-(\(4\))107 711 Q F2(Sendmail)133.66 711 Q F1 .4
-(supports message batching across netw)2.9 F .401
-(orks when a message is being sent to mul-)-.1 F(tiple recipients.)133.66 723 Q
-EP
-%%Page: 9 9
-%%BeginPageSetup
-BP
-%%EndPageSetup
-/F0 10/Times-Bold@0 SF(SENDMAIL \212 An Inter)72 60 Q(netw)-.15 E
-(ork Mail Router)-.1 E(SMM:9-9)462.9 60 Q/F1 10/Times-Roman@0 SF 12.5(\(5\) A)
-107 96 R .875(mail queue is pro)3.375 F .874(vided in)-.15 F/F2 10
-/Times-Italic@0 SF(sendmail.)3.374 E F1 .874(Mail that cannot be deli)5.874 F
--.15(ve)-.25 G .874(red immediately b).15 F .874(ut can)-.2 F 1.063
-(potentially be deli)133.66 108 R -.15(ve)-.25 G 1.064
-(red later is stored in this queue for a later retry).15 F 6.064(.T)-.65 G
-1.064(he queue also pro-)427.218 108 R .896(vides a b)133.66 120 R(uf)-.2 E
-.896(fer ag)-.25 F .895
-(ainst system crashes; after the message has been collected it may be reli-)
--.05 F(ably redeli)133.66 132 Q -.15(ve)-.25 G(red e).15 E -.15(ve)-.25 G 2.5
-(ni).15 G 2.5(ft)224.22 132 S(he system crashes during the initial deli)232.83
-132 Q -.15(ve)-.25 G(ry).15 E(.)-.65 E(\(6\))107 148.2 Q F2(Sendmail)133.66
-148.2 Q F1 .197(uses the netw)2.696 F .197(orking support pro)-.1 F .197
-(vided by 4.2BSD to pro)-.15 F .197(vide a direct interf)-.15 F .197(ace net-)
--.1 F -.1(wo)133.66 160.2 S .07(rks such as the ARP).1 F .07
-(ANET and/or Ethernet using SMTP \(the Simple Mail T)-.92 F .07(ransfer Proto-)
--.35 F(col\) o)133.66 172.2 Q -.15(ve)-.15 G 2.5(raT).15 G(CP/IP connection.)
-184.73 172.2 Q F0 2.5(4.2. MMDF)87 196.2 R F1 .957(MMDF [Crock)127 212.4 R .957
-(er79] spans a wider problem set than)-.1 F F2(sendmail)3.458 E F1 5.958(.F)C
-.958(or e)395.058 212.4 R .958(xample, the domain of)-.15 F .721
-(MMDF includes a \231phone netw)102 224.4 R .721(ork\232 mailer)-.1 F 3.221(,w)
--.4 G(hereas)290.516 224.4 Q F2(sendmail)3.221 E F1 .721(calls on pree)3.221 F
-.72(xisting mailers in most)-.15 F(cases.)102 236.4 Q .175(MMDF and)127 252.6 R
-F2(sendmail)2.675 E F1 .175
-(both support aliasing, customized mailers, message batching, automatic)2.675 F
-(forw)102 264.6 Q .792(arding to g)-.1 F(ate)-.05 E -.1(wa)-.25 G .792
-(ys, queueing, and retransmission.).1 F .792(MMDF supports tw)5.792 F .792
-(o-stage timeout, which)-.1 F F2(sendmail)102 276.6 Q F1(does not support.)2.5
-E(The con\214guration for MMDF is compiled into the code)127 294.8 Q/F3 7
-/Times-Roman@0 SF(4)348.65 290.8 Q F1(.)352.15 294.8 Q .037
-(Since MMDF does not consider backw)127 311 R .037
-(ards compatibility as a design goal, the address parsing)-.1 F(is simpler b)
-102 323 Q(ut much less \215e)-.2 E(xible.)-.15 E 1.159(It is some)127 341.2 R
-1.159(what harder to inte)-.25 F 1.159(grate a ne)-.15 F 3.659(wc)-.25 G
-(hannel)302.802 341.2 Q F3(5)329.462 337.2 Q F1 1.159(into MMDF)336.621 341.2 R
-6.16(.I)-.8 G 3.66(np)397.59 341.2 S(articular)411.25 341.2 Q 3.66(,M)-.4 G
-1.16(MDF must)459.22 341.2 R(kno)102 353.2 Q 3.225(wt)-.25 G .725(he location \
-and format of host tables for all channels, and the channel must speak a speci\
-al)129.975 353.2 R 2.525(protocol. This)102 365.2 R(allo)2.525 E .025
-(ws MMDF to do additional v)-.25 F .025(eri\214cation \(such as v)-.15 F .025
-(erifying host names\) at submis-)-.15 F(sion time.)102 377.2 Q 1.761
-(MMDF strictly separates the submission and deli)127 393.4 R -.15(ve)-.25 G
-1.761(ry phases.).15 F(Although)6.761 E F2(sendmail)4.261 E F1 1.76(has the)
-4.261 F .784(concept of each of these stages, the)102 405.4 R 3.284(ya)-.15 G
-.784(re inte)260.068 405.4 R .785(grated into one program, whereas in MMDF the)
--.15 F 3.285(ya)-.15 G(re)496.23 405.4 Q(split into tw)102 417.4 Q 2.5(op)-.1 G
-(rograms.)162.19 417.4 Q F0 2.5(4.3. Message)87 441.4 R(Pr)2.5 E
-(ocessing Module)-.18 E F1 .925
-(The Message Processing Module \(MPM\) discussed by Postel [Postel79b] matches)
-127 457.6 R F2(sendmail)3.425 E F1 1.364
-(closely in terms of its basic architecture.)102 469.6 R(Ho)6.364 E(we)-.25 E
--.15(ve)-.25 G 2.164 -.4(r, l).15 H(ik).4 E 3.864(eM)-.1 G(MDF)347.526 469.6 Q
-3.864(,t)-.8 G 1.365(he MPM includes the netw)377.54 469.6 R(ork)-.1 E(interf)
-102 481.6 Q(ace softw)-.1 E(are as part of its domain.)-.1 E .408
-(MPM also postulates a duple)127 497.8 R 2.907(xc)-.15 G .407
-(hannel to the recei)256.937 497.8 R -.15(ve)-.25 G 1.207 -.4(r, a).15 H 2.907
-(sd).4 G .407(oes MMDF)365.362 497.8 R 2.907(,t)-.8 G .407(hus allo)419.546
-497.8 R .407(wing simpler)-.25 F .302
-(handling of errors by the mailer than is possible in)102 509.8 R F2(sendmail)
-2.802 E F1 5.302(.W)C .302(hen a message queued by)362.24 509.8 R F2(sendmail)
-2.802 E F1 .23(is sent, an)102 521.8 R 2.73(ye)-.15 G .23
-(rrors must be returned to the sender by the mailer itself.)154.2 521.8 R .229
-(Both MPM and MMDF mail-)5.229 F .883(ers can return an immediate error respon\
-se, and a single error processor can create an appropriate)102 533.8 R
-(response.)102 545.8 Q 2.24
-(MPM prefers passing the message as a structured object, with type-length-v)127
-564 R 2.24(alue tuples)-.25 F F3(6)498 560 Q F1(.)501.5 564 Q .874(Such a con)
-102 576 R -.15(ve)-.4 G .874(ntion requires a much higher de).15 F .875
-(gree of cooperation between mailers than is required)-.15 F(by)102 588 Q F2
-(sendmail)2.796 E F1 5.296(.M)C .296(PM also assumes a uni)167.592 588 R -.15
-(ve)-.25 G .296(rsally agreed upon internet name space \(with each address).15
-F(in the form of a net-host-user tuple\), which)102 600 Q F2(sendmail)2.5 E F1
-(does not.)2.5 E .32 LW 76 642 72 642 DL 80 642 76 642 DL 84 642 80 642 DL 88
-642 84 642 DL 92 642 88 642 DL 96 642 92 642 DL 100 642 96 642 DL 104 642 100
-642 DL 108 642 104 642 DL 112 642 108 642 DL 116 642 112 642 DL 120 642 116 642
-DL 124 642 120 642 DL 128 642 124 642 DL 132 642 128 642 DL 136 642 132 642 DL
-140 642 136 642 DL 144 642 140 642 DL 148 642 144 642 DL 152 642 148 642 DL 156
-642 152 642 DL 160 642 156 642 DL 164 642 160 642 DL 168 642 164 642 DL 172 642
-168 642 DL 176 642 172 642 DL 180 642 176 642 DL 184 642 180 642 DL 188 642 184
-642 DL 192 642 188 642 DL 196 642 192 642 DL 200 642 196 642 DL 204 642 200 642
-DL 208 642 204 642 DL 212 642 208 642 DL 216 642 212 642 DL/F4 5/Times-Roman@0
-SF(4)93.6 652.4 Q/F5 8/Times-Roman@0 SF .179
-(Dynamic con\214guration tables are currently being considered for MMDF; allo)
-3.2 J .18(wing the installer to select either compiled or dy-)-.2 F
-(namic tables.)72 665.2 Q F4(5)93.6 675.6 Q F5(The MMDF equi)3.2 I -.2(va)-.2 G
-(lent of a).2 E/F6 8/Times-Italic@0 SF(sendmail)2 E F5(\231mailer)2 E -.56
-<2e9a>-.44 G F4(6)93.6 689.2 Q F5(This is similar to the NBS standard.)3.2 I EP
-%%Page: 10 10
-%%BeginPageSetup
-BP
-%%EndPageSetup
-/F0 10/Times-Bold@0 SF 187.28(SMM:9-10 SENDMAIL)72 60 R 2.5<8a41>2.5 G 2.5(nI)
-383.99 60 S(nter)395.94 60 Q(netw)-.15 E(ork Mail Router)-.1 E 2.5(5. EV)72 96
-R(ALU)-1.35 E -.95(AT)-.6 G(IONS AND FUTURE PLANS).95 E/F1 10/Times-Italic@0 SF
-(Sendmail)112 112.2 Q/F2 10/Times-Roman@0 SF 1.851(is designed to w)4.351 F
-1.851(ork in a nonhomogeneous en)-.1 F 4.352(vironment. Ev)-.4 F 1.852
-(ery attempt is made to)-.15 F -.2(avo)87 124.2 S 1.037
-(id imposing unnecessary constraints on the underlying mailers.).2 F 1.036
-(This goal has dri)6.036 F -.15(ve)-.25 G 3.536(nm).15 G 1.036(uch of the)
-461.938 124.2 R 2.723(design. One)87 136.2 R .223(of the major problems has be\
-en the lack of a uniform address space, as postulated in [Pos-)2.723 F
-(tel79a] and [Postel79b].)87 148.2 Q 2.647(An)112 164.4 S .147(onuniform addre\
-ss space implies that a path will be speci\214ed in all addresses, either e)
-126.867 164.4 R(xplicitly)-.15 E .472
-(\(as part of the address\) or implicitly \(as with implied forw)87 176.4 R
-.473(arding to g)-.1 F(ate)-.05 E -.1(wa)-.25 G 2.973(ys\). This).1 F .473
-(restriction has the)2.973 F .493(unpleasant ef)87 188.4 R .493
-(fect of making replying to messages e)-.25 F .493(xceedingly dif)-.15 F .493
-(\214cult, since there is no one \231address\232)-.25 F(for an)87 200.4 Q 2.5
-(yp)-.15 G(erson, b)122.95 200.4 Q(ut only a w)-.2 E
-(ay to get there from where)-.1 E -.15(ve)-.25 G 2.5(ry).15 G(ou are.)324.7
-200.4 Q(Interf)112 216.6 Q .448(acing to mail programs that were not initially\
- intended to be applied in an internet en)-.1 F(viron-)-.4 E(ment has been ama\
-zingly successful, and has reduced the job to a manageable task.)87 228.6 Q F1
-(Sendmail)112 244.8 Q F2 2.906(has kno)5.406 F 2.906(wledge of a fe)-.25 F
-5.406(wd)-.25 G(if)271.126 244.8 Q 2.906(\214cult en)-.25 F 2.906(vironments b)
--.4 F 2.906(uilt in.)-.2 F 2.905(It generates ARP)7.906 F(ANET)-.92 E .648(FTP\
-/SMTP compatible error messages \(prepended with three-digit numbers [Neigus73\
-, Postel74, Pos-)87 256.8 R .771(tel82]\) as necessary)87 268.8 R 3.271(,o)-.65
-G .771(ptionally generates UNIX-style \231From\232 lines on the front of messa\
-ges for some)177.523 268.8 R 1.669(mailers, and kno)87 280.8 R 1.669(ws ho)-.25
-F 4.169(wt)-.25 G 4.169(op)195.666 280.8 S 1.669(arse the same lines on input.)
-209.835 280.8 R 1.67(Also, error handling has an option cus-)6.67 F
-(tomized for BerkNet.)87 292.8 Q 1.482(The decision to a)112 309 R -.2(vo)-.2 G
-1.482(id doing an).2 F 3.982(yt)-.15 G 1.481(ype of deli)254.222 309 R -.15(ve)
--.25 G 1.481(ry where possible \(e).15 F -.15(ve)-.25 G 1.481
-(n, or perhaps especially).15 F(,)-.65 E .574(local deli)87 321 R -.15(ve)-.25
-G .574(ry\) has turned out to be a good idea.).15 F(Ev)5.574 E .574
-(en with local deli)-.15 F -.15(ve)-.25 G(ry).15 E 3.074(,t)-.65 G .575
-(here are issues of the loca-)394.776 321 R .469(tion of the mailbox, the form\
-at of the mailbox, the locking protocol used, etc., that are best decided by)87
-333 R .038(other programs.)87 345 R .038(One surprisingly major anno)5.038 F
-.038(yance in man)-.1 F 2.538(yi)-.15 G .038
-(nternet mailers is that the location and for)333.684 345 R(-)-.2 E .138
-(mat of local mail is b)87 357 R .138(uilt in.)-.2 F .137
-(The feeling seems to be that local mail is so common that it should be ef)
-5.137 F<8c2d>-.25 E 3.045(cient. This)87 369 R .545
-(feeling is not born out by our e)3.045 F .545(xperience; on the contrary)-.15
-F 3.045(,t)-.65 G .545(he location and format of mail-)376.575 369 R(box)87 381
-Q(es seems to v)-.15 E(ary widely from system to system.)-.25 E .681
-(The ability to automatically generate a response to incoming mail \(by forw)
-112 397.2 R .68(arding mail to a pro-)-.1 F .435
-(gram\) seems useful \(\231I am on v)87 409.2 R .435
-(acation until late August....)-.25 F 2.935(\232\) b)-.7 F .435
-(ut can create problems such as forw)-.2 F(ard-)-.1 E .143(ing loops \(tw)87
-421.2 R 2.643(op)-.1 G .143(eople on v)152.609 421.2 R .143(acation whose prog\
-rams send notes back and forth, for instance\) if these pro-)-.25 F .732
-(grams are not well written.)87 433.2 R 3.232(Ap)5.732 G .732
-(rogram could be written to do standard tasks correctly)218.592 433.2 R 3.233
-(,b)-.65 G .733(ut this w)450.404 433.2 R(ould)-.1 E(solv)87 445.2 Q 2.5(et)
--.15 G(he general case.)113.24 445.2 Q .225
-(It might be desirable to implement some form of load limiting.)112 461.4 R
-2.725(Ia)5.225 G 2.724(mu)380.8 461.4 S(na)396.304 461.4 Q -.1(wa)-.15 G .224
-(re of an).1 F 2.724(ym)-.15 G .224(ail system)463.496 461.4 R
-(that addresses this problem, nor am I a)87 473.4 Q -.1(wa)-.15 G(re of an).1 E
-2.5(yr)-.15 G(easonable solution at this time.)294.05 473.4 Q .113(The con\214\
-guration \214le is currently practically inscrutable; considerable con)112
-489.6 R -.15(ve)-.4 G .114(nience could be real-).15 F(ized with a higher)87
-501.6 Q(-le)-.2 E -.15(ve)-.25 G 2.5(lf).15 G(ormat.)186.93 501.6 Q .778(It se\
-ems clear that common protocols will be changing soon to accommodate changing \
-require-)112 517.8 R 2.774(ments and en)87 529.8 R 5.274(vironments. These)-.4
-F 2.774(changes will include modi\214cations to the message header \(e.g.,)
-5.274 F .859([NBS80]\) or to the body of the message itself \(such as for mult\
-imedia messages [Postel80]\).)87 541.8 R(Experi-)5.859 E
-(ence indicates that these changes should be relati)87 553.8 Q -.15(ve)-.25 G
-(ly tri).15 E(vial to inte)-.25 E(grate into the e)-.15 E(xisting system.)-.15
-E .811(In tightly coupled en)112 570 R .812(vironments, it w)-.4 F .812
-(ould be nice to ha)-.1 F 1.112 -.15(ve a n)-.2 H .812(ame serv).15 F .812
-(er such as Grapvine [Bir)-.15 F(-)-.2 E .095(rell82] inte)87 582 R .095
-(grated into the mail system.)-.15 F .095(This w)5.095 F .095(ould allo)-.1 F
-2.594(was)-.25 G .094(ite such as \231Berk)330.768 582 R(ele)-.1 E .094
-(y\232 to appear as a single)-.15 F 2.606
-(host, rather than as a collection of hosts, and w)87 594 R 2.606(ould allo)-.1
-F 5.106(wp)-.25 G 2.606(eople to mo)352.786 594 R 2.906 -.15(ve t)-.15 H 2.606
-(ransparently among).15 F 1.664(machines without ha)87 606 R 1.664
-(ving to change their addresses.)-.2 F 1.664(Such a f)6.664 F 1.664(acility w)
--.1 F 1.663(ould require an automatically)-.1 F .428
-(updated database and some method of resolving con\215icts.)87 618 R .428
-(Ideally this w)5.428 F .428(ould be ef)-.1 F(fecti)-.25 E .728 -.15(ve e)-.25
-H -.15(ve)-.1 G 2.928(nw).15 G(ithout)480.66 618 Q .184
-(all hosts being under a single management.)87 630 R(Ho)5.184 E(we)-.25 E -.15
-(ve)-.25 G .984 -.4(r, i).15 H 2.684(ti).4 G 2.683(sn)317.576 630 S .183
-(ot clear whether this feature should be inte-)329.149 630 R
-(grated into the aliasing f)87 642 Q(acility or should be considered a \231v)
--.1 E(alue added\232 feature outside)-.25 E F1(sendmail)2.5 E F2(itself.)2.5 E
-.79(As a more interesting case, the CSNET name serv)112 658.2 R .791
-(er [Solomon81] pro)-.15 F .791(vides an f)-.15 F .791(acility that goes)-.1 F
-(be)87 670.2 Q .375(yond a single tightly-coupled en)-.15 F 2.875
-(vironment. Such)-.4 F 2.875(af)2.875 G .375(acility w)308.675 670.2 R .374
-(ould normally e)-.1 F .374(xist outside of)-.15 F F1(sendmail)2.874 E F2(ho)87
-682.2 Q(we)-.25 E -.15(ve)-.25 G -.55(r.).15 G EP
-%%Page: 11 11
-%%BeginPageSetup
-BP
-%%EndPageSetup
-/F0 10/Times-Bold@0 SF(SENDMAIL \212 An Inter)72 60 Q(netw)-.15 E
-(ork Mail Router)-.1 E(SMM:9-11)457.9 60 Q -.55(AC)72 96 S(KNO).55 E
-(WLEDGEMENTS)-.5 E/F1 10/Times-Roman@0 SF 1.203(Thanks are due to K)97 112.2 R
-1.204
-(urt Shoens for his continual cheerful assistance and good advice, Bill Jo)-.15
-F 3.704(yf)-.1 G(or)495.67 112.2 Q .102
-(pointing me in the correct direction \(o)72 124.2 R -.15(ve)-.15 G 2.602(ra)
-.15 G .102(nd o)244.324 124.2 R -.15(ve)-.15 G .102
-(r\), and Mark Horton for more advice, prodding, and man).15 F(y)-.15 E .453
-(of the good ideas.)72 136.2 R -.15(Ku)5.453 G .453
-(rt and Eric Schmidt are to be credited for using).15 F/F2 10/Times-Italic@0 SF
-(delivermail)2.953 E F1 .453(as a serv)2.953 F .453(er for their pro-)-.15 F
-1.663(grams \()72 148.2 R F2(Mail)A F1 1.663(and BerkNet respecti)4.163 F -.15
-(ve)-.25 G 1.663(ly\) before an).15 F 4.163(ys)-.15 G 1.663
-(ane person should ha)291.091 148.2 R -.15(ve)-.2 G 4.163(,a).15 G 1.662
-(nd making the necessary)400.423 148.2 R .078
-(modi\214cations promptly and happily)72 160.2 R 5.078(.E)-.65 G .078(ric g)
-228.332 160.2 R -2.25 -.2(av e)-.05 H .079
-(me considerable advice about the perils of netw)2.778 F .079(ork softw)-.1 F
-(are)-.1 E .179(which sa)72 172.2 R -.15(ve)-.2 G 2.679(dm).15 G 2.679(ea)
-131.998 172.2 S 2.679(nu)143.557 172.2 S(nkno)156.236 172.2 Q .178
-(wn amount of w)-.25 F .178(ork and grief.)-.1 F .178
-(Mark did the original implementation of the DBM)5.178 F -.15(ve)72 184.2 S
-.341(rsion of aliasing, installed the VFORK code, wrote the current v).15 F
-.341(ersion of)-.15 F F2(rmail)2.841 E F1 2.841(,a)C .341(nd w)411.083 184.2 R
-.342(as the person who)-.1 F .61(really con)72 196.2 R .61
-(vinced me to put the w)-.4 F .61(ork into)-.1 F F2(delivermail)3.109 E F1 .609
-(to turn it into)3.109 F F2(sendmail)3.109 E F1 5.609(.K)C .609(urt deserv)
-398.753 196.2 R .609(es accolades for)-.15 F(using)72 208.2 Q F2(sendmail)2.57
-E F1 .07(when I w)2.57 F .07(as myself afraid to tak)-.1 F 2.57(et)-.1 G .07
-(he risk; ho)271.01 208.2 R 2.57(wap)-.25 G .07
-(erson can continue to be so enthusiastic in)334.92 208.2 R(the f)72 220.2 Q
-(ace of so much bitter reality is be)-.1 E(yond me.)-.15 E -.15(Ku)97 236.4 S
-1.505(rt, Mark, Kirk McK).15 F 1.505(usick, Marvin Solomon, and man)-.15 F
-4.005(yo)-.15 G 1.504(thers ha)345.79 236.4 R 1.804 -.15(ve r)-.2 H -.25(ev).15
-G(ie).25 E 1.504(wed this paper)-.25 F 4.004(,g)-.4 G -.25(iv)483.69 236.4 S
-(ing).25 E(considerable useful advice.)72 248.4 Q .846
-(Special thanks are reserv)97 264.6 R .846(ed for Mik)-.15 F 3.346(eS)-.1 G
-(tonebrak)256.786 264.6 Q .846(er at Berk)-.1 F(ele)-.1 E 3.347(ya)-.15 G .847
-(nd Bob Epstein at Britton-Lee, who)356.995 264.6 R .542(both kno)72 276.6 R
-.542(wingly allo)-.25 F .542(wed me to put so much w)-.25 F .541
-(ork into this project when there were so man)-.1 F 3.041(yo)-.15 G .541
-(ther things I)454.588 276.6 R(really should ha)72 288.6 Q .3 -.15(ve b)-.2 H
-(een w).15 E(orking on.)-.1 E EP
-%%Page: 12 12
-%%BeginPageSetup
-BP
-%%EndPageSetup
-/F0 10/Times-Roman@0 SF(REFERENCES)256.605 132 Q 62.73([Birrell82] Birrell,)72
-148.2 R 1.084(A. D., Le)3.584 F 1.084(vin, R., Needham, R. M., and Schroeder)
--.25 F 3.584(,M)-.4 G 3.585(.D)433.49 148.2 S 1.085(., \231Grape)446.795 148.2
-R(vine:)-.25 E(An Ex)180 160.2 Q(ercise in Distrib)-.15 E(uted Computing.)-.2 E
-5<9a49>-.7 G(n)348.66 160.2 Q/F1 10/Times-Italic@0 SF(Comm. A.C.M. 25,)2.5 E F0
-(4, April 82.)2.5 E 59.4([Borden79] Borden,)72 176.4 R .796
-(S., Gaines, R. S., and Shapiro, N. Z.,)3.296 F F1 .795(The MH Messa)3.295 F
-.995 -.1(ge H)-.1 H .795(andling Sys-).1 F(tem: User)180 188.4 Q(s' Manual.)-.1
-E F0(R-2367-P)5 E(AF)-.92 E 5(.R)-.8 G(and Corporation.)332.06 188.4 Q
-(October 1979.)5 E([Crock)72 204.6 Q 52.29(er77a] Crock)-.1 F(er)-.1 E 2.508
-(,D)-.4 G 2.508(.H)223.938 204.6 S .008(., V)236.166 204.6 R .009
-(ittal, J. J., Pogran, K. T)-.6 F .009(., and Henderson, D. A. Jr)-.74 F(.,)
--.55 E F1(Standar)2.509 E 2.509(df)-.37 G(or)495.11 204.6 Q .955(the F)180
-216.6 R .955(ormat of ARP)-1.05 F 3.454(AN)-.9 G .954(etwork T)272.978 216.6 R
--.2(ex)-.92 G 3.454(tM).2 G(essa)331.536 216.6 Q -.1(ge)-.1 G(s.).1 E F0 .954
-(RFC 733, NIC 41952.)5.954 F .954(In [Fein-)5.954 F 2.5(ler78]. No)180 228.6 R
--.15(ve)-.15 G(mber 1977.).15 E([Crock)72 244.8 Q 51.73(er77b] Crock)-.1 F(er)
--.1 E 3.04(,D)-.4 G 3.04(.H)224.47 244.8 S(.,)237.23 244.8 Q F1 -1.55 -.55
-(Fr a)3.04 H(me).55 E .54(work and Functions of the MS P)-.15 F(er)-.8 E .54
-(sonal Messa)-.1 F .74 -.1(ge S)-.1 H(ystem.).1 E F0(R-2134-ARP)180 256.8 Q
-(A, Rand Corporation, Santa Monica, California.)-.92 E(1977.)5 E([Crock)72 273
-Q 56.73(er79] Crock)-.1 F(er)-.1 E 2.557(,D)-.4 G 2.557(.H)223.987 273 S .056
-(., Szurk)236.264 273 R -.25(ow)-.1 G .056(ski, E. S., and F).25 F(arber)-.15 E
-2.556(,D)-.4 G 2.556(.J)374.85 273 S(.,)383.796 273 Q F1 .056
-(An Internetwork Memo Dis-)2.556 F(trib)180 285 Q 1.341(ution F)-.2 F 1.341
-(acility \212 MMDF)-.75 F(.)-1.35 E F0 1.341
-(6th Data Communication Symposium, Asilomar)6.341 F(.)-.55 E(No)180 297 Q -.15
-(ve)-.15 G(mber 1979.).15 E([Crock)72 313.2 Q 56.73(er82] Crock)-.1 F(er)-.1 E
-3.383(,D)-.4 G 3.383(.H)224.813 313.2 S(.,)237.916 313.2 Q F1(Standar)3.383 E
-3.383(df)-.37 G .883(or the F)288.762 313.2 R .882(ormat of Arpa Internet T)
--1.05 F -.2(ex)-.92 G 3.382(tM).2 G(essa)446.368 313.2 Q -.1(ge)-.1 G(s.).1 E
-F0(RFC)5.882 E 4.197(822. Netw)180 325.2 R 1.697(ork Information Center)-.1 F
-4.197(,S)-.4 G 1.698(RI International, Menlo P)333.768 325.2 R 1.698
-(ark, California.)-.15 F(August 1982.)180 337.2 Q 53.3([Metcalfe76] Metcalfe,)
-72 353.4 R .727(R., and Boggs, D., \231Ethernet: Distrib)3.227 F .727(uted P)
--.2 F(ack)-.15 E .727(et Switching for Local)-.1 F(Computer Netw)180 365.4 Q
-(orks\232,)-.1 E F1(Communications of the A)2.5 E(CM 19,)-.3 E F0 2.5(7. July)
-2.5 F(1976.)2.5 E 60.51([Feinler78] Feinler)72 381.6 R 4.438(,E)-.4 G 1.938
-(., and Postel, J.)220.978 381.6 R(\(eds.\),)6.938 E F1(ARP)4.438 E 1.938
-(ANET Pr)-.9 F 1.938(otocol Handbook.)-.45 F F0 1.938(NIC 7104,)6.938 F(Netw)
-180 393.6 Q(ork Information Center)-.1 E 2.5(,S)-.4 G
-(RI International, Menlo P)304.48 393.6 Q(ark, California.)-.15 E(1978.)5 E
-69.39([NBS80] National)72 409.8 R 1.46(Bureau of Standards,)3.96 F F1 1.46
-(Speci\214cation of a Dr)3.96 F 1.46(aft Messa)-.15 F 1.66 -.1(ge F)-.1 H 1.46
-(ormat Stan-)-.95 F(dar)180 421.8 Q(d.)-.37 E F0(Report No. ICST/CBOS 80-2.)5 E
-(October 1980.)5 E 60.51([Neigus73] Neigus,)72 438 R(N.,)5.186 E F1 -.45(Fi)
-5.186 G 2.686(le T).45 F -.15(ra)-.55 G 2.686(nsfer Pr).15 F 2.686
-(otocol for the ARP)-.45 F 5.187(AN)-.9 G(etwork.)402.599 438 Q F0 2.687
-(RFC 542, NIC)7.687 F 2.5(17759. In)180 450 R 2.5([Feinler78]. August,)2.5 F
-(1973.)2.5 E([No)72 466.2 Q 55.21(witz78a] No)-.25 F 1.633
-(witz, D. A., and Lesk, M. E.,)-.25 F F1 4.132(AD)4.132 G 1.632
-(ial-Up Network of UNIX Systems.)338.9 466.2 R F0(Bell)6.632 E 5.403
-(Laboratories. In)180 478.2 R 2.904(UNIX Programmer')5.403 F 5.404(sM)-.55 G
-2.904(anual, Se)356.024 478.2 R -.15(ve)-.25 G 2.904(nth Edition, V).15 F 2.904
-(olume 2.)-1.29 F(August, 1978.)180 490.2 Q([No)72 506.4 Q 54.65(witz78b] No)
--.25 F .633(witz, D. A.,)-.25 F F1 .632(Uucp Implementation Description.)3.132
-F F0 .632(Bell Laboratories.)5.632 F .632(In UNIX)5.632 F(Programmer')180 518.4
-Q 2.5(sM)-.55 G(anual, Se)248.05 518.4 Q -.15(ve)-.25 G(nth Edition, V).15 E
-(olume 2.)-1.29 E(October)5 E 2.5(,1)-.4 G(978.)431.22 518.4 Q 64.39
-([Postel74] Postel,)72 534.6 R .24(J., and Neigus, N., Re)2.74 F .241
-(vised FTP Reply Codes.)-.25 F .241(RFC 640, NIC 30843.)5.241 F(In)5.241 E 2.5
-([Feinler78]. June,)180 546.6 R(1974.)2.5 E 64.39([Postel77] Postel,)72 562.8 R
-(J.,)2.5 E F1(Mail Pr)2.5 E(otocol.)-.45 E F0(NIC 29588.)5 E(In [Feinler78].)5
-E(No)5 E -.15(ve)-.15 G(mber 1977.).15 E 59.95([Postel79a] Postel,)72 579 R
-(J.,)3.144 E F1 .644(Internet Messa)3.144 F .844 -.1(ge P)-.1 H -.45(ro).1 G
-(tocol.).45 E F0 .644(RFC 753, IEN 85.)5.644 F(Netw)5.644 E .644
-(ork Information)-.1 F(Center)180 591 Q 2.5(,S)-.4 G(RI International, Menlo P)
-216.82 591 Q(ark, California.)-.15 E(March 1979.)5 E 59.39([Postel79b] Postel,)
-72 607.2 R 1.305(J. B.,)3.805 F F1 1.305(An Internetwork Messa)3.805 F 1.505
--.1(ge S)-.1 H(tructur).1 E -.15(e.)-.37 G F0(In)6.456 E F1(Pr)3.806 E 1.306
-(oceedings of the Sixth)-.45 F(Data Communications Symposium,)180 619.2 Q F0
-2.5(IEEE. Ne)2.5 F 2.5(wY)-.25 G 2.5(ork. No)379.74 619.2 R -.15(ve)-.15 G
-(mber 1979.).15 E 64.39([Postel80] Postel,)72 635.4 R .639(J. B.,)3.139 F F1
-3.139(AS)3.139 G(tructur)248.676 635.4 Q .639(ed F)-.37 F .639(ormat for T)
--1.05 F -.15(ra)-.55 G .639(nsmission of Multi-Media Documents.).15 F F0 .418
-(RFC 767.)180 647.4 R(Netw)5.419 E .419(ork Information Center)-.1 F 2.919(,S)
--.4 G .419(RI International, Menlo P)350.474 647.4 R .419(ark, Califor)-.15 F
-(-)-.2 E 2.5(nia. August)180 659.4 R(1980.)2.5 E 64.39([Postel82] Postel,)72
-675.6 R 2.05(J. B.,)4.55 F F1 2.05(Simple Mail T)4.55 F -.15(ra)-.55 G 2.05
-(nsfer Pr).15 F(otocol.)-.45 E F0 2.05(RFC821 \(obsoleting RFC788\).)7.05 F
-(Netw)180 687.6 Q .273(ork Information Center)-.1 F 2.774(,S)-.4 G .274
-(RI International, Menlo P)305.3 687.6 R .274(ark, California.)-.15 F(August)
-5.274 E(1982.)180 699.6 Q/F2 10/Times-Bold@0 SF 187.28(SMM:9-12 SENDMAIL)72 756
-R 2.5<8a41>2.5 G 2.5(nI)383.99 756 S(nter)395.94 756 Q(netw)-.15 E
-(ork Mail Router)-.1 E EP
-%%Page: 13 13
-%%BeginPageSetup
-BP
-%%EndPageSetup
-/F0 10/Times-Bold@0 SF(SENDMAIL \212 An Inter)72 60 Q(netw)-.15 E
-(ork Mail Router)-.1 E(SMM:9-13)457.9 60 Q/F1 10/Times-Roman@0 SF 55.5
-([Schmidt79] Schmidt,)72 96 R(E.,)2.972 E/F2 10/Times-Italic@0 SF .472(An Intr)
-2.972 F .472(oduction to the Berk)-.45 F(ele)-.1 E 2.972(yN)-.3 G(etwork.)
-369.664 96 Q F1(Uni)5.472 E -.15(ve)-.25 G .472(rsity of California,).15 F
-(Berk)180 108 Q(ele)-.1 E 2.5(yC)-.15 G 2.5(alifornia. 1979.)225.02 108 R 59.95
-([Shoens79] Shoens,)72 124.2 R(K.,)4.894 E F2 2.394(Mail Refer)4.894 F 2.394
-(ence Manual.)-.37 F F1(Uni)7.394 E -.15(ve)-.25 G 2.395
-(rsity of California, Berk).15 F(ele)-.1 E 6.195 -.65(y. I)-.15 H(n).65 E
-(UNIX Programmer')180 136.2 Q 2.5(sM)-.55 G(anual, Se)275.54 136.2 Q -.15(ve)
--.25 G(nth Edition, V).15 E(olume 2C.)-1.29 E(December 1979.)5 E 60.51
-([Sluizer81] Sluizer)72 152.4 R 2.872(,S)-.4 G .372(., and Postel, J. B.,)
-218.862 152.4 R F2 .372(Mail T)2.872 F -.15(ra)-.55 G .372(nsfer Pr).15 F
-(otocol.)-.45 E F1 .371(RFC 780.)5.371 F(Netw)5.371 E .371(ork Infor)-.1 F(-)
--.2 E(mation Center)180 164.4 Q 2.5(,S)-.4 G(RI International, Menlo P)247.1
-164.4 Q(ark, California.)-.15 E(May 1981.)5 E 52.72([Solomon81] Solomon,)72
-180.6 R .96(M., Landweber)3.46 F 3.46(,L)-.4 G .96
-(., and Neuhengen, D., \231The Design of the CSNET)296.08 180.6 R(Name Serv)180
-192.6 Q(er)-.15 E 3.9 -.7(.\232 C)-.55 H(S-DN-2, Uni).7 E -.15(ve)-.25 G
-(rsity of W).15 E(isconsin, Madison.)-.4 E(No)5 E -.15(ve)-.15 G(mber 1981.).15
-E 78.28([Su82] Su,)72 208.8 R(Za)4.344 E 1.844(w-Sing, and Postel, Jon,)-.15 F
-F2 1.844(The Domain Naming Con)4.344 F 1.844(vention for Internet)-.4 F 1.717
-(User Applications.)180 220.8 R F1 4.217(RFC819. Netw)6.717 F 1.717
-(ork Information Center)-.1 F 4.217(,S)-.4 G 1.718(RI International,)436.182
-220.8 R(Menlo P)180 232.8 Q(ark, California.)-.15 E(August 1982.)5 E([UNIX83])
-72 249 Q F2 2.12(The UNIX Pr)180 249 R -.1(og)-.45 G -.15(ra).1 G(mmer').15 E
-4.62(sM)-.4 G 2.12(anual, Se)298.3 249 R 2.12(venth Edition,)-.15 F F1 -.6(Vi)
-4.62 G 2.12(rtual V).6 F 2.12(AX-11 V)-1.35 F(ersion,)-1.11 E -1.29(Vo)180 261
-S 1.027(lume 1.)1.29 F 1.027(Bell Laboratories, modi\214ed by the Uni)6.027 F
--.15(ve)-.25 G 1.027(rsity of California, Berk).15 F(e-)-.1 E(le)180 273 Q 1.3
--.65(y, C)-.15 H 2.5(alifornia. March,).65 F(1983.)2.5 E EP
-%%Trailer
-end
-%%EOF
diff --git a/usr.sbin/sendmail/doc/op/op.ps b/usr.sbin/sendmail/doc/op/op.ps
deleted file mode 100644
index 355b3fc..0000000
--- a/usr.sbin/sendmail/doc/op/op.ps
+++ /dev/null
@@ -1,5944 +0,0 @@
-%!PS-Adobe-3.0
-%%Creator: groff version 1.08
-%%DocumentNeededResources: font Times-Bold
-%%+ font Times-Roman
-%%+ font Times-Italic
-%%+ font Symbol
-%%DocumentSuppliedResources: procset grops 1.08 0
-%%Pages: 69
-%%PageOrder: Ascend
-%%Orientation: Portrait
-%%EndComments
-%%BeginProlog
-%%BeginResource: procset grops 1.08 0
-/setpacking where{
-pop
-currentpacking
-true setpacking
-}if
-/grops 120 dict dup begin
-/SC 32 def
-/A/show load def
-/B{0 SC 3 -1 roll widthshow}bind def
-/C{0 exch ashow}bind def
-/D{0 exch 0 SC 5 2 roll awidthshow}bind def
-/E{0 rmoveto show}bind def
-/F{0 rmoveto 0 SC 3 -1 roll widthshow}bind def
-/G{0 rmoveto 0 exch ashow}bind def
-/H{0 rmoveto 0 exch 0 SC 5 2 roll awidthshow}bind def
-/I{0 exch rmoveto show}bind def
-/J{0 exch rmoveto 0 SC 3 -1 roll widthshow}bind def
-/K{0 exch rmoveto 0 exch ashow}bind def
-/L{0 exch rmoveto 0 exch 0 SC 5 2 roll awidthshow}bind def
-/M{rmoveto show}bind def
-/N{rmoveto 0 SC 3 -1 roll widthshow}bind def
-/O{rmoveto 0 exch ashow}bind def
-/P{rmoveto 0 exch 0 SC 5 2 roll awidthshow}bind def
-/Q{moveto show}bind def
-/R{moveto 0 SC 3 -1 roll widthshow}bind def
-/S{moveto 0 exch ashow}bind def
-/T{moveto 0 exch 0 SC 5 2 roll awidthshow}bind def
-/SF{
-findfont exch
-[exch dup 0 exch 0 exch neg 0 0]makefont
-dup setfont
-[exch/setfont cvx]cvx bind def
-}bind def
-/MF{
-findfont
-[5 2 roll
-0 3 1 roll
-neg 0 0]makefont
-dup setfont
-[exch/setfont cvx]cvx bind def
-}bind def
-/level0 0 def
-/RES 0 def
-/PL 0 def
-/LS 0 def
-/PLG{
-gsave newpath clippath pathbbox grestore
-exch pop add exch pop
-}bind def
-/BP{
-/level0 save def
-1 setlinecap
-1 setlinejoin
-72 RES div dup scale
-LS{
-90 rotate
-}{
-0 PL translate
-}ifelse
-1 -1 scale
-}bind def
-/EP{
-level0 restore
-showpage
-}bind def
-/DA{
-newpath arcn stroke
-}bind def
-/SN{
-transform
-.25 sub exch .25 sub exch
-round .25 add exch round .25 add exch
-itransform
-}bind def
-/DL{
-SN
-moveto
-SN
-lineto stroke
-}bind def
-/DC{
-newpath 0 360 arc closepath
-}bind def
-/TM matrix def
-/DE{
-TM currentmatrix pop
-translate scale newpath 0 0 .5 0 360 arc closepath
-TM setmatrix
-}bind def
-/RC/rcurveto load def
-/RL/rlineto load def
-/ST/stroke load def
-/MT/moveto load def
-/CL/closepath load def
-/FL{
-currentgray exch setgray fill setgray
-}bind def
-/BL/fill load def
-/LW/setlinewidth load def
-/RE{
-findfont
-dup maxlength 1 index/FontName known not{1 add}if dict begin
-{
-1 index/FID ne{def}{pop pop}ifelse
-}forall
-/Encoding exch def
-dup/FontName exch def
-currentdict end definefont pop
-}bind def
-/DEFS 0 def
-/EBEGIN{
-moveto
-DEFS begin
-}bind def
-/EEND/end load def
-/CNT 0 def
-/level1 0 def
-/PBEGIN{
-/level1 save def
-translate
-div 3 1 roll div exch scale
-neg exch neg exch translate
-0 setgray
-0 setlinecap
-1 setlinewidth
-0 setlinejoin
-10 setmiterlimit
-[]0 setdash
-/setstrokeadjust where{
-pop
-false setstrokeadjust
-}if
-/setoverprint where{
-pop
-false setoverprint
-}if
-newpath
-/CNT countdictstack def
-userdict begin
-/showpage{}def
-}bind def
-/PEND{
-clear
-countdictstack CNT sub{end}repeat
-level1 restore
-}bind def
-end def
-/setpacking where{
-pop
-setpacking
-}if
-%%EndResource
-%%IncludeResource: font Times-Bold
-%%IncludeResource: font Times-Roman
-%%IncludeResource: font Times-Italic
-%%IncludeResource: font Symbol
-grops begin/DEFS 1 dict def DEFS begin/u{.001 mul}bind def end/RES 72 def/PL
-792 def/LS false def/ENC0[/asciicircum/asciitilde/Scaron/Zcaron/scaron/zcaron
-/Ydieresis/trademark/quotesingle/.notdef/.notdef/.notdef/.notdef/.notdef
-/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef
-/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/space
-/exclam/quotedbl/numbersign/dollar/percent/ampersand/quoteright/parenleft
-/parenright/asterisk/plus/comma/hyphen/period/slash/zero/one/two/three/four
-/five/six/seven/eight/nine/colon/semicolon/less/equal/greater/question/at/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/bracketleft/backslash
-/bracketright/circumflex/underscore/quoteleft/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/braceleft/bar/braceright/tilde/.notdef/quotesinglbase
-/guillemotleft/guillemotright/bullet/florin/fraction/perthousand/dagger
-/daggerdbl/endash/emdash/ff/fi/fl/ffi/ffl/dotlessi/dotlessj/grave/hungarumlaut
-/dotaccent/breve/caron/ring/ogonek/quotedblleft/quotedblright/oe/lslash
-/quotedblbase/OE/Lslash/.notdef/exclamdown/cent/sterling/currency/yen/brokenbar
-/section/dieresis/copyright/ordfeminine/guilsinglleft/logicalnot/minus
-/registered/macron/degree/plusminus/twosuperior/threesuperior/acute/mu
-/paragraph/periodcentered/cedilla/onesuperior/ordmasculine/guilsinglright
-/onequarter/onehalf/threequarters/questiondown/Agrave/Aacute/Acircumflex/Atilde
-/Adieresis/Aring/AE/Ccedilla/Egrave/Eacute/Ecircumflex/Edieresis/Igrave/Iacute
-/Icircumflex/Idieresis/Eth/Ntilde/Ograve/Oacute/Ocircumflex/Otilde/Odieresis
-/multiply/Oslash/Ugrave/Uacute/Ucircumflex/Udieresis/Yacute/Thorn/germandbls
-/agrave/aacute/acircumflex/atilde/adieresis/aring/ae/ccedilla/egrave/eacute
-/ecircumflex/edieresis/igrave/iacute/icircumflex/idieresis/eth/ntilde/ograve
-/oacute/ocircumflex/otilde/odieresis/divide/oslash/ugrave/uacute/ucircumflex
-/udieresis/yacute/thorn/ydieresis]def/Times-Italic@0 ENC0/Times-Italic RE
-/Times-Roman@0 ENC0/Times-Roman RE/Times-Bold@0 ENC0/Times-Bold RE
-%%EndProlog
-%%Page: 1 1
-%%BeginPageSetup
-BP
-%%EndPageSetup
-/F0 16/Times-Bold@0 SF(SENDMAIL)244.888 143.4 Q/F1 12/Times-Bold@0 SF(INST)
-170.172 172.2 Q(ALLA)-1.08 E(TION AND OPERA)-1.14 E(TION GUIDE)-1.14 E/F2 10
-/Times-Roman@0 SF(Eric Allman)263.42 196.2 Q -.15(Pa)233.085 208.2 S
-(ng\346a Reference Systems).15 E(eric@Sendmail.ORG)245.205 220.2 Q -1.11(Ve)
-262.725 244.2 S(rsion 8.70)1.11 E -.15(Fo)236.965 268.2 S 2.5(rS).15 G
-(endmail V)258.765 268.2 Q(ersion 8.7)-1.11 E/F3 10/Times-Italic@0 SF(Sendmail)
-97 312.6 Q F2 .482(implements a general purpose internetw)2.982 F .482
-(ork mail routing f)-.1 F .481(acility under the UNIX\256 operat-)-.1 F .378
-(ing system.)72 324.6 R .378(It is not tied to an)5.378 F 2.878(yo)-.15 G .378
-(ne transport protocol \212 its function may be lik)208.214 324.6 R .378
-(ened to a crossbar switch,)-.1 F 1.036
-(relaying messages from one domain into another)72 336.6 R 6.036(.I)-.55 G
-3.536(nt)284.502 336.6 S 1.036
-(he process, it can do a limited amount of message)295.818 336.6 R .604(header\
- editing to put the message into a format that is appropriate for the recei)72
-348.6 R .604(ving domain.)-.25 F .604(All of this is)5.604 F
-(done under the control of a con\214guration \214le.)72 360.6 Q .711
-(Due to the requirements of \215e)97 376.8 R .711(xibility for)-.15 F F3
-(sendmail)3.211 E F2 3.211(,t)C .71(he con\214guration \214le can seem some)
-311.688 376.8 R .71(what unap-)-.25 F 2.893(proachable. Ho)72 388.8 R(we)-.25 E
--.15(ve)-.25 G 1.193 -.4(r, t).15 H .393(here are only a fe).4 F 2.893(wb)-.25
-G .394(asic con\214gurations for most sites, for which standard con\214gu-)
-253.381 388.8 R .646(ration \214les ha)72 400.8 R .946 -.15(ve b)-.2 H .646
-(een supplied.).15 F .645(Most other con\214gurations can be b)5.646 F .645
-(uilt by adjusting an e)-.2 F .645(xisting con\214gura-)-.15 F
-(tion \214les incrementally)72 412.8 Q(.)-.65 E F3(Sendmail)97 429 Q F2 .15
-(is based on RFC821 \(Simple Mail T)2.65 F .15
-(ransport Protocol\), RFC822 \(Internet Mail F)-.35 F .15(ormat Pro-)-.15 F
-.423(tocol\), RFC1123 \(Internet Host Requirements\), RFC1521 \(MIME\), RFC165\
-1 \(SMTP Service Extensions\),)72 441 R .994
-(and a series of as-yet-draft standards describing Deli)72 453 R -.15(ve)-.25 G
-.995(ry Status Noti\214cations \(DSNs\), a).15 F -.25(va)-.2 G .995
-(ilable from the).25 F 1.529
-(internet drafts sites as draft-ietf-notary-mime-deli)72 465 R -.15(ve)-.25 G
-(ry-).15 E F3(XX)A F2 1.528(.txt, draft-ietf-notary-mime-report-)B F3(XX)A F2
-1.528(.txt, draft-)B(ietf-notary-smtp-drpt-)72 477 Q F3(XX)A F2 1.93
-(.txt, and draft-ietf-notary-status-)B F3(XX)A F2 1.93(.txt \(replace)B F3(XX)
-4.43 E F2 1.93(by the latest draft number\).)4.43 F(Ho)72 489 Q(we)-.25 E -.15
-(ve)-.25 G .831 -.4(r, s).15 H(ince).4 E F3(sendmail)2.531 E F2 .031
-(is designed to w)2.531 F .031(ork in a wider w)-.1 F .03(orld, in man)-.1 F
-2.53(yc)-.15 G .03(ases it can be con\214gured to e)365.12 489 R(xceed)-.15 E
-(these protocols.)72 501 Q(These cases are described herein.)5 E(Although)97
-517.2 Q F3(sendmail)3.547 E F2 1.048(is intended to run without the need for m\
-onitoring, it has a number of features)3.547 F 1.972(that may be used to monit\
-or or adjust the operation under unusual circumstances.)72 529.2 R 1.972
-(These features are)6.972 F(described.)72 541.2 Q .816
-(Section one describes ho)97 557.4 R 3.316(wt)-.25 G 3.316(od)211.664 557.4 S
-3.316(oab)224.98 557.4 S(asic)246.052 557.4 Q F3(sendmail)3.316 E F2 3.317
-(installation. Section)3.317 F(tw)3.317 E 3.317(oe)-.1 G .817
-(xplains the day-to-day)412.936 557.4 R .283(information you should kno)72
-569.4 R 2.783(wt)-.25 G 2.783(om)196.772 569.4 S .282
-(aintain your mail system.)212.335 569.4 R .282(If you ha)5.282 F .582 -.15
-(ve a r)-.2 H(elati).15 E -.15(ve)-.25 G .282(ly normal site, these tw).15 F(o)
--.1 E .634(sections should contain suf)72 581.4 R .635
-(\214cient information for you to install)-.25 F F3(sendmail)3.135 E F2 .635
-(and k)3.135 F .635(eep it happ)-.1 F 4.435 -.65(y. S)-.1 H .635(ection three)
-.65 F .925(describes some parameters that may be safely tweak)72 593.4 R 3.425
-(ed. Section)-.1 F .925(four has information re)3.425 F -.05(ga)-.15 G .925
-(rding the com-).05 F .885(mand line ar)72 605.4 R 3.385(guments. Section)-.18
-F<8c76>3.385 E 3.385(ec)-.15 G .886
-(ontains the nitty-gritty information about the con\214guration \214le.)221.915
-605.4 R(This)5.886 E .005
-(section is for masochists and people who must write their o)72 617.4 R .004
-(wn con\214guration \214le.)-.25 F .004(Section six describes con-)5.004 F .886
-(\214guration that can be done at compile time.)72 629.4 R .886(Section se)
-5.886 F -.15(ve)-.25 G 3.386(ng).15 G -2.15 -.25(iv e)322.098 629.4 T 3.386
-(sab).25 G .886(rief description of dif)354.02 629.4 R .886(ferences in this)
--.25 F -.15(ve)72 641.4 S 1.62(rsion of).15 F F3(sendmail)4.12 E F2 6.62(.T)C
-1.62(he appendix)169.2 641.4 R 1.62(es gi)-.15 F 1.92 -.15(ve a b)-.25 H 1.619
-(rief b).15 F 1.619(ut detailed e)-.2 F 1.619
-(xplanation of a number of features not)-.15 F
-(described in the rest of the paper)72 653.4 Q(.)-.55 E/F4 10/Times-Bold@0 SF
--1.2(WA)97 669.6 S(RNING:)1.2 E F2(Se)3.267 E -.15(ve)-.25 G .767
-(ral major changes were introduced in v).15 F .767(ersion 8.7.)-.15 F -1.1(Yo)
-5.767 G 3.267(us)1.1 G .767(hould not attempt to use)404.26 669.6 R
-(this document for prior v)72 681.6 Q(ersions of)-.15 E F3(sendmail)2.5 E F2(.)
-A F4(Sendmail Installation and Operation Guide)72 756 Q(SMM:08-1)457.9 756 Q EP
-%%Page: 7 2
-%%BeginPageSetup
-BP
-%%EndPageSetup
-/F0 10/Times-Bold@0 SF(Sendmail Installation and Operation Guide)72 60 Q
-(SMM:08-7)457.9 60 Q 2.5(1. B)72 96 R(ASIC INST)-.3 E(ALLA)-.9 E(TION)-.95 E/F1
-10/Times-Roman@0 SF .234(There are tw)112 112.2 R 2.733(ob)-.1 G .233
-(asic steps to installing)175.631 112.2 R/F2 10/Times-Italic@0 SF(sendmail)
-2.733 E F1 5.233(.T)C .233(he hard part is to b)317.076 112.2 R .233
-(uild the con\214guration table.)-.2 F 1.185(This is a \214le that)87 124.2 R
-F2(sendmail)3.686 E F1 1.186
-(reads when it starts up that describes the mailers it kno)3.686 F 1.186
-(ws about, ho)-.25 F 3.686(wt)-.25 G(o)499 124.2 Q .715(parse addresses, ho)87
-136.2 R 3.215(wt)-.25 G 3.215(or)178.315 136.2 S -.25(ew)189.86 136.2 S .715
-(rite the message header).25 F 3.215(,a)-.4 G .715(nd the settings of v)306.75
-136.2 R .714(arious options.)-.25 F .714(Although the)5.714 F .852
-(con\214guration table is quite comple)87 148.2 R .852
-(x, a con\214guration can usually be b)-.15 F .852(uilt by adjusting an e)-.2 F
-.852(xisting of)-.15 F(f-)-.25 E 1.078(the-shelf con\214guration.)87 160.2 R
-1.078(The second part is actually doing the installation, i.e., creating the n\
-ecessary)6.078 F(\214les, etc.)87 172.2 Q .192
-(The remainder of this section will describe the installation of)112 188.4 R F2
-(sendmail)2.692 E F1 .192(assuming you can use one)2.692 F 1.432(of the e)87
-200.4 R 1.432(xisting con\214gurations and that the standard installation para\
-meters are acceptable.)-.15 F 1.431(All path-)6.431 F 8.62(names and e)87 212.4
-R 8.62(xamples are gi)-.15 F -.15(ve)-.25 G 11.12(nf).15 G 8.62
-(rom the root of the)257.57 212.4 R F2(sendmail)378.16 212.4 Q F1 8.62
-(subtree, normally)425.39 212.4 R F2(/usr/sr)87 224.4 Q(c/usr)-.37 E
-(.sbin/sendmail)-1.11 E F1(on 4.4BSD.)2.5 E .543(If you are loading this of)112
-240.6 R 3.042(ft)-.25 G .542(he tape, continue with the ne)222.766 240.6 R .542
-(xt section.)-.15 F .542(If you ha)5.542 F .842 -.15(ve a r)-.2 H .542
-(unning binary).15 F
-(already on your system, you should probably skip to section 1.2.)87 252.6 Q F0
-2.5(1.1. Compiling)87 276.6 R(Sendmail)2.5 E F1(All)127 292.8 Q F2(sendmail)
-2.934 E F1 .434(source is in the)2.934 F F2(sr)2.934 E(c)-.37 E F1
-(subdirectory)2.934 E 5.434(.I)-.65 G 2.934(fy)321.652 292.8 S .435
-(ou are running on a 4.4BSD system, com-)332.916 292.8 R .179
-(pile by typing \231mak)102 304.8 R 2.679(e\232. On)-.1 F .179
-(other systems, you may ha)2.679 F .479 -.15(ve t)-.2 H 2.679(om).15 G(ak)
-350.719 304.8 Q 2.679(es)-.1 G .178(ome other adjustments.)371.068 304.8 R .178
-(On most)5.178 F(systems, you can do the appropriate compilation by typing)102
-316.8 Q(sh mak)142 333 Q(esendmail)-.1 E .364(This will lea)102 349.2 R .664
--.15(ve t)-.2 H .364(he binary in an appropriately named subdirectory).15 F
-5.364(.I)-.65 G 2.864(tw)377.37 349.2 S .364(orks for multiple object v)390.134
-349.2 R(er)-.15 E(-)-.2 E(sions compiled out of the same directory)102 361.2 Q
-(.)-.65 E F0 2.5(1.1.1. T)102 385.2 R(weaking the Mak)-.74 E(e\214le)-.1 E F2
-(Sendmail)142 401.4 Q F1 2.181(supports tw)4.681 F 4.681(od)-.1 G(if)247.053
-401.4 Q 2.181(ferent formats for the local \(on disk\) v)-.25 F 2.18
-(ersion of databases,)-.15 F(notably the)117 413.4 Q F2(aliases)2.5 E F1 2.5
-(database. At)2.5 F(least one of these should be de\214ned if at all possible.)
-2.5 E 39.5(NDBM The)117 429.6 R -.74(``)3.166 G(ne).74 E 3.166(wD)-.25 G(BM')
-240.432 429.6 Q 3.166('f)-.74 G .666(ormat, a)268.408 429.6 R -.25(va)-.2 G
-.666(ilable on nearly all systems around today).25 F 5.667(.T)-.65 G(his)492.33
-429.6 Q -.1(wa)189 441.6 S 3.541(st).1 G 1.041
-(he preferred format prior to 4.4BSD.)210.771 441.6 R 1.041(It allo)6.041 F
-1.041(ws such comple)-.25 F 3.54(xt)-.15 G 1.04(hings as)470.46 441.6 R
-(multiple databases and closing a currently open database.)189 453.6 Q 32.84
-(NEWDB The)117 469.8 R(ne)3.323 E 3.323(wd)-.25 G .824
-(atabase package from Berk)232.606 469.8 R(ele)-.1 E 4.624 -.65(y. I)-.15 H
-3.324(fy).65 G .824(ou ha)382.716 469.8 R 1.124 -.15(ve t)-.2 H .824
-(his, use it.).15 F .824(It allo)5.824 F(ws)-.25 E .839
-(long records, multiple open databases, real in-memory caching, and so forth.)
-189 481.8 R -1.1(Yo)189 493.8 S 3.581(uc)1.1 G 1.081
-(an de\214ne this in conjunction with one of the other tw)213.141 493.8 R 1.082
-(o; if you do, old)-.1 F .693(databases are read, b)189 505.8 R .693
-(ut when a ne)-.2 F 3.193(wd)-.25 G .693
-(atabase is created it will be in NEWDB)341.681 505.8 R 2.851(format. As)189
-517.8 R 2.851(an)2.851 G .351(asty hack, if you ha)249.763 517.8 R .652 -.15
-(ve N)-.2 H .352(EWDB, NDBM, and NIS de\214ned, and).15 F .952
-(if the alias \214le name includes the substring \231/yp/\232,)189 529.8 R F2
-(sendmail)3.451 E F1 .951(will create both)3.451 F(ne)189 541.8 Q 3.975(wa)-.25
-G 1.475(nd old v)213.825 541.8 R 1.475(ersions of the alias \214le during a)
--.15 F F2(ne)3.976 E(walias)-.15 E F1 3.976(command. This)3.976 F(is)3.976 E
-.711(required because the Sun NIS/YP system reads the DBM v)189 553.8 R .71
-(ersion of the alias)-.15 F 2.5(\214le. It')189 565.8 R 2.5(su)-.55 G
-(gly as sin, b)229.56 565.8 Q(ut it w)-.2 E(orks.)-.1 E 1.112
-(If neither of these are de\214ned,)117 582 R F2(sendmail)3.612 E F1 1.112
-(reads the alias \214le into memory on e)3.612 F -.15(ve)-.25 G 1.112(ry in).15
-F -.2(vo)-.4 G(cation.).2 E 1.043(This can be slo)117 594 R 3.543(wa)-.25 G
-1.043(nd should be a)195.352 594 R -.2(vo)-.2 G 3.543(ided. There).2 F 1.043
-(are also se)3.543 F -.15(ve)-.25 G 1.042(ral methods for remote database).15 F
-(access:)117 606 Q 53.39(NIS Sun')117 622.2 R 2.5(sN)-.55 G(etw)220.95 622.2 Q
-(ork Information Services \(formerly YP\).)-.1 E 28.94(NISPLUS Sun')117 638.4 R
-2.5(sN)-.55 G(IS+ services.)220.95 638.4 Q 26.73(NETINFO NeXT')117 654.6 R 2.5
-(sN)-.55 G(etInfo service.)230.38 654.6 Q 32.84(HESIOD Hesiod)117 670.8 R
-(service \(from Athena\).)2.5 E .042(Other compilation \215ags are set in conf\
-.h and should be prede\214ned for you unless you are porting)117 687 R(to a ne)
-117 699 Q 2.5(we)-.25 G -.4(nv)157.57 699 S(ironment.).4 E EP
-%%Page: 8 3
-%%BeginPageSetup
-BP
-%%EndPageSetup
-/F0 10/Times-Bold@0 SF 198.36(SMM:08-8 Sendmail)72 60 R
-(Installation and Operation Guide)2.5 E 2.5(1.1.2. Compilation)102 96 R
-(and installation)2.5 E/F1 10/Times-Roman@0 SF .309
-(After making the local system con\214guration described abo)142 112.2 R -.15
-(ve)-.15 G 2.808(,Y).15 G .308(ou should be able to com-)398.86 112.2 R
-(pile and install the system.)117 124.2 Q(The script \231mak)5 E
-(esendmail\232 is the best approach on most systems:)-.1 E(sh mak)157 140.4 Q
-(esendmail)-.1 E(This will use)117 156.6 Q/F2 10/Times-Italic@0 SF(uname)2.5 E
-F1(\(1\) to select the correct Mak)A(e\214le for your en)-.1 E(vironment.)-.4 E
--1.1(Yo)142 172.8 S 2.5(um)1.1 G(ay be able to install using)168.4 172.8 Q
-(sh mak)157 189 Q(esendmail install)-.1 E 3.346
-(This should install the binary in /usr/sbin and create links from /usr/bin/ne)
-117 205.2 R -.1(wa)-.25 G 3.346(liases and).1 F 1.577
-(/usr/bin/mailq to /usr/sbin/sendmail.)117 217.2 R 1.577
-(On 4.4BSD systems it will also format and install man)6.577 F(pages.)117 229.2
-Q F0 2.5(1.2. Con\214guration)87 253.2 R(Files)2.5 E F2(Sendmail)127 269.4 Q F1
-2.079(cannot operate without a con\214guration \214le.)4.579 F 2.079
-(The con\214guration de\214nes the mail)7.079 F(deli)102 281.4 Q -.15(ve)-.25 G
-.889(ry mechanisms understood at this site, ho).15 F 3.389(wt)-.25 G 3.389(oa)
-309.783 281.4 S .889(ccess them, ho)322.612 281.4 R 3.388(wt)-.25 G 3.388(of)
-396.128 281.4 S(orw)407.846 281.4 Q .888(ard email to remote)-.1 F .088
-(mail systems, and a number of tuning parameters.)102 293.4 R .088
-(This con\214guration \214le is detailed in the later por)5.088 F(-)-.2 E
-(tion of this document.)102 305.4 Q(The)127 321.6 Q F2(sendmail)2.764 E F1 .264
-(con\214guration can be daunting at \214rst.)2.764 F .264(The w)5.264 F .264
-(orld is comple)-.1 F .264(x, and the mail con-)-.15 F .108
-(\214guration re\215ects that.)102 333.6 R .108(The distrib)5.108 F .109
-(ution includes an m4-based con\214guration package that hides a lot)-.2 F
-(of the comple)102 345.6 Q(xity)-.15 E(.)-.65 E .47
-(These con\214guration \214les are simpler than old v)127 361.8 R .47
-(ersions lar)-.15 F .47(gely because the w)-.18 F .47(orld has become)-.1 F
-1.448(simpler; in particular)102 373.8 R 3.948(,t)-.4 G -.15(ex)197.604 373.8 S
-1.448(t-based host \214les are of).15 F 1.449(\214cially eliminated, ob)-.25 F
-1.449(viating the need to \231hide\232)-.15 F(hosts behind a re)102 385.8 Q
-(gistered internet g)-.15 E(ate)-.05 E -.1(wa)-.25 G -.65(y.).1 G .092(These \
-\214les also assume that most of your neighbors use domain-based UUCP addressi\
-ng; that)127 402 R .361(is, instead of naming hosts as \231host!user\232 the)
-102 414 R 2.861(yw)-.15 G .361(ill use \231host.domain!user\232.)299.435 414 R
-.361(The con\214guration \214les)5.361 F(can be customized to w)102 426 Q
-(ork around this, b)-.1 E(ut it is more comple)-.2 E(x.)-.15 E .658
-(Our con\214guration \214les are processed by)127 442.2 R F2(m4)3.158 E F1 .658
-(to f)3.158 F .657(acilitate local customization; the directory)-.1 F F2(cf)
-3.157 E F1 .396(of the)102 454.2 R F2(sendmail)2.896 E F1(distrib)2.896 E .396
-(ution directory contains the source \214les.)-.2 F .396
-(This directory contains se)5.396 F -.15(ve)-.25 G .397(ral sub-).15 F
-(directories:)102 466.2 Q 61.73(cf Both)102 482.4 R .56
-(site-dependent and site-independent descriptions of hosts.)3.06 F .56
-(These can be lit-)5.56 F .445(eral host names \(e.g., \231ucb)174 494.4 R -.25
-(va)-.15 G .445(x.mc\232\) when the hosts are g).25 F(ate)-.05 E -.1(wa)-.25 G
-.445(ys or more general).1 F 3.589(descriptions \(such as \231tcpproto.mc\232 \
-as a general description of an SMTP-)174 506.4 R .536(connected host or \231uu\
-cpproto.mc\232 as a general description of a UUCP-connected)174 518.4 R 3.291
-(host\). Files)174 530.4 R(ending)3.291 E F0(.mc)3.291 E F1(\(`)3.291 E .791
-(`Master Con\214guration')-.74 F .791('\) are the input descriptions; the)-.74
-F 2.14(output is in the corresponding)174 542.4 R F0(.cf)4.64 E F1 4.64
-(\214le. The)4.64 F 2.14(general structure of these \214les is)4.64 F
-(described belo)174 554.4 Q -.65(w.)-.25 G 39.5(domain Site-dependent)102 570.6
-R .428(subdomain descriptions.)2.928 F .428(These are tied to the w)5.428 F
-.428(ay your or)-.1 F -.05(ga)-.18 G(niza-).05 E .292(tion w)174 582.6 R .292
-(ants to do addressing.)-.1 F -.15(Fo)5.292 G 2.792(re).15 G(xample,)313.122
-582.6 Q F0(domain/cs.exposed.m4)2.792 E F1 .292(is our descrip-)2.792 F .443
-(tion for hosts in the CS.Berk)174 594.6 R(ele)-.1 E -.65(y.)-.15 G .443
-(EDU subdomain that w).65 F .442(ant their indi)-.1 F .442(vidual host-)-.25 F
-.962(name to be e)174 606.6 R .963(xternally visible;)-.15 F F0
-(domain/cs.hidden.m4)3.463 E F1 .963(is the same e)3.463 F .963(xcept that the)
--.15 F 2.628(hostname is hidden \(e)174 618.6 R -.15(ve)-.25 G 2.628
-(rything looks lik).15 F 5.128(ei)-.1 G 5.128(tc)362.038 618.6 S 2.627
-(omes from CS.Berk)374.386 618.6 R(ele)-.1 E -.65(y.)-.15 G(EDU\).).65 E
-(These are referenced using the)174 630.6 Q/F3 9/Times-Roman@0 SF(DOMAIN)2.5 E
-F0(m4)2.5 E F1(macro in the)2.5 E F0(.mc)2.5 E F1(\214le.)2.5 E 41.74
-(feature De\214nitions)102 646.8 R .728
-(of speci\214c features that some particular host in your site might w)3.228 F
-(ant.)-.1 E 2.467(These are referenced using the)174 658.8 R F3(FEA)4.966 E
-(TURE)-.999 E F0(m4)4.966 E F1 4.966(macro. An)4.966 F -.15(ex)4.966 G 2.466
-(ample feature is).15 F 1.316(use_cw_\214le \(which tells)174 670.8 R F2
-(sendmail)3.816 E F1 1.317(to read an /etc/sendmail.cw \214le on startup to)
-3.816 F(\214nd the set of local names\).)174 682.8 Q 50.62(hack Local)102 699 R
-1.886(hacks, referenced using the)4.387 F F3(HA)4.386 E(CK)-.36 E F0(m4)4.386 E
-F1 4.386(macro. T)4.386 F 1.886(ry to a)-.35 F -.2(vo)-.2 G 1.886(id these.).2
-F(The)6.886 E(point of ha)174 711 Q(ving them here is to mak)-.2 E 2.5(ei)-.1 G
-2.5(tc)325.91 711 S(lear that the)335.63 711 Q 2.5(ys)-.15 G(mell.)394.08 711 Q
-EP
-%%Page: 9 4
-%%BeginPageSetup
-BP
-%%EndPageSetup
-/F0 10/Times-Bold@0 SF(Sendmail Installation and Operation Guide)72 60 Q
-(SMM:08-9)457.9 60 Q/F1 10/Times-Roman@0 SF 56.72(m4 Site-independent)102 96 R
-/F2 10/Times-Italic@0 SF(m4)2.538 E F1 .038(\(1\) include \214les that ha)B
-.338 -.15(ve i)-.2 H .038(nformation common to all con\214gu-).15 F
-(ration \214les.)174 108 Q
-(This can be thought of as a \231#include\232 directory)5 E(.)-.65 E 43.95
-(mailer De\214nitions)102 124.2 R .152(of mailers, referenced using the)2.653 F
-/F3 9/Times-Roman@0 SF(MAILER)2.652 E F0(m4)2.652 E F1 2.652(macro. The)2.652 F
-.152(mailer types)2.652 F 1.786(that are kno)174 136.2 R 1.787
-(wn in this distrib)-.25 F 1.787(ution are f)-.2 F 1.787
-(ax, local, smtp, uucp, and usenet.)-.1 F -.15(Fo)6.787 G(r).15 E -.15(ex)174
-148.2 S(ample, to include support for the UUCP-based mailers, use \231MAILER\(\
-uucp\)\232.).15 E 43.39(ostype De\214nitions)102 164.4 R 1.157(describing v)
-3.657 F 1.157(arious operating system en)-.25 F 1.156
-(vironments \(such as the loca-)-.4 F(tion of support \214les\).)174 176.4 Q
-(These are referenced using the)5 E F3(OSTYPE)2.5 E F0(m4)2.5 E F1(macro.)2.5 E
-60.61(sh Shell)102 192.6 R(\214les used by the)2.5 E F0(m4)2.5 E F1 -.2(bu)2.5
-G(ild process.).2 E -1.1(Yo)5 G 2.5(us)1.1 G(houldn')362.97 192.6 Q 2.5(th)-.18
-G -2.25 -.2(av e)404.18 192.6 T(to mess with these.)2.7 E 30.61
-(sitecon\214g Local)102 208.8 R .49
-(site con\214guration information, such as UUCP connecti)2.99 F(vity)-.25 E
-5.49(.T)-.65 G(he)450.61 208.8 Q 2.99(yn)-.15 G(ormally)472.89 208.8 Q
-(contain lists of site information, for e)174 220.8 Q(xample:)-.15 E
-(SITE\(contessa\))214 237 Q(SITE\(hoptoad\))214 249 Q(SITE\(nkainc\))214 261 Q
-(SITE\(well\))214 273 Q(The)174 289.2 Q 2.5(ya)-.15 G
-(re referenced using the SITECONFIG macro:)201.34 289.2 Q
-(SITECONFIG\(site.con\214g.\214le, name_of_site, X\))214 305.4 Q(where)174
-321.6 Q F2(X)2.704 E F1 .204(is the macro/class name to use.)2.704 F .203
-(It can be U \(indicating locally connected)5.204 F(hosts\) or one of W)174
-333.6 Q 2.5(,X)-.92 G 2.5(,o)259.73 333.6 S 2.5(rYf)269.73 333.6 S
-(or up to three remote UUCP hubs.)288.61 333.6 Q .756(If you are in a ne)127
-349.8 R 3.256(wd)-.25 G .756(omain \(e.g., a compan)214.036 349.8 R .757
-(y\), you will probably w)-.15 F .757(ant to create a cf/domain)-.1 F .871
-(\214le for your domain.)102 361.8 R .871
-(This consists primarily of relay de\214nitions: for e)5.871 F .87
-(xample, Berk)-.15 F(ele)-.1 E(y')-.15 E 3.37(sd)-.55 G(omain)479 361.8 Q .16
-(de\214nition de\214nes relays for BitNET)102 373.8 R 2.66(,C)-.74 G(SNET)
-257.61 373.8 Q 2.66(,a)-.74 G .16(nd UUCP)291.47 373.8 R 5.16(.O)-1.11 G 2.66
-(ft)344.57 373.8 S .16(hese, only the UUCP relay is particu-)353.34 373.8 R .46
-(larly speci\214c to Berk)102 385.8 R(ele)-.1 E 4.26 -.65(y. A)-.15 H .46
-(ll of these are internet-style domain names.).65 F .46(Please check to mak)
-5.46 F 2.96(ec)-.1 G(er)493.1 385.8 Q(-)-.2 E(tain the)102 397.8 Q 2.5(ya)-.15
-G(re reasonable for your domain.)143.51 397.8 Q 1.406(Subdomains at Berk)127
-414 R(ele)-.1 E 3.906(ya)-.15 G 1.407
-(re also represented in the cf/domain directory)235.678 414 R 6.407(.F)-.65 G
-1.407(or e)439.406 414 R 1.407(xample, the)-.15 F 1.491(domain cs-e)102 426 R
-1.491(xposed is the Computer Science subdomain with the local hostname sho)-.15
-F 1.49(wn to other)-.25 F 1.41(users; cs-hidden mak)102 438 R 1.411
-(es users appear to be from the CS.Berk)-.1 F(ele)-.1 E -.65(y.)-.15 G 1.411
-(EDU subdomain \(with no local).65 F 1.084(host information included\).)102 450
-R -1.1(Yo)6.084 G 3.584(uw)1.1 G 1.084(ill probably ha)246.336 450 R 1.384 -.15
-(ve t)-.2 H 3.584(ou).15 G 1.083(pdate this directory to be appropriate for)
-335.872 450 R(your domain.)102 462 Q -1.1(Yo)127 478.2 S 4.372(uw)1.1 G 1.872
-(ill ha)154.712 478.2 R 2.172 -.15(ve t)-.2 H 4.372(ou).15 G 1.872
-(se or create)207.478 478.2 R F0(.mc)4.372 E F1 1.872(\214les in the)4.372 F F2
-(cf/cf)4.372 E F1 1.873(subdirectory for your hosts.)4.373 F 1.873(This is)
-6.873 F(detailed in the cf/README \214le.)102 490.2 Q F0 2.5(1.3. Details)87
-514.2 R(of Installation Files)2.5 E F1
-(This subsection describes the \214les that comprise the)127 530.4 Q F2
-(sendmail)2.5 E F1(installation.)2.5 E F0 2.5(1.3.1. /usr/sbin/sendmail)102
-554.4 R F1 .079(The binary for)142 572.6 R F2(sendmail)2.579 E F1 .079
-(is located in /usr/sbin)2.579 F/F4 7/Times-Roman@0 SF(1)326.703 568.6 Q F1
-5.079(.I)330.203 572.6 S 2.579(ts)341.112 572.6 S .08(hould be setuid root.)
-350.361 572.6 R -.15(Fo)5.08 G 2.58(rs).15 G .08(ecurity rea-)458.11 572.6 R
-(sons, /, /usr)117 586.6 Q 2.5(,a)-.4 G(nd /usr/sbin should be o)171.6 586.6 Q
-(wned by root, mode 755)-.25 E F4(2)364.4 582.6 Q F1(.)367.9 586.6 Q F0 2.5
-(1.3.2. /etc/sendmail.cf)102 610.6 R F1 .699
-(This is the con\214guration \214le for)142 628.8 R F2(sendmail)3.199 E F4(3)
-311.744 624.8 Q F1 5.698(.T)315.244 628.8 S .698
-(his and /etc/sendmail.pid are the only non-)329.552 628.8 R .32 LW 76 638.4 72
-638.4 DL 80 638.4 76 638.4 DL 84 638.4 80 638.4 DL 88 638.4 84 638.4 DL 92
-638.4 88 638.4 DL 96 638.4 92 638.4 DL 100 638.4 96 638.4 DL 104 638.4 100
-638.4 DL 108 638.4 104 638.4 DL 112 638.4 108 638.4 DL 116 638.4 112 638.4 DL
-120 638.4 116 638.4 DL 124 638.4 120 638.4 DL 128 638.4 124 638.4 DL 132 638.4
-128 638.4 DL 136 638.4 132 638.4 DL 140 638.4 136 638.4 DL 144 638.4 140 638.4
-DL 148 638.4 144 638.4 DL 152 638.4 148 638.4 DL 156 638.4 152 638.4 DL 160
-638.4 156 638.4 DL 164 638.4 160 638.4 DL 168 638.4 164 638.4 DL 172 638.4 168
-638.4 DL 176 638.4 172 638.4 DL 180 638.4 176 638.4 DL 184 638.4 180 638.4 DL
-188 638.4 184 638.4 DL 192 638.4 188 638.4 DL 196 638.4 192 638.4 DL 200 638.4
-196 638.4 DL 204 638.4 200 638.4 DL 208 638.4 204 638.4 DL 212 638.4 208 638.4
-DL 216 638.4 212 638.4 DL/F5 5/Times-Roman@0 SF(1)93.6 648.8 Q/F6 8
-/Times-Roman@0 SF .385(This is usually /usr/sbin on 4.4BSD and ne)3.2 J .385
-(wer systems; man)-.2 F 2.385(ys)-.12 G .385(ystems install it in /usr/lib)
-302.966 652 R 4.384(.I)-.32 G .384(understand it is in /usr/ucblib on)398.744
-652 R(System V Release 4.)72 661.6 Q F5(2)93.6 672 Q F6 .15(Some v)3.2 J .15
-(endors ship them o)-.12 F .15
-(wned by bin; this creates a security hole that is not actually related to)-.2
-F/F7 8/Times-Italic@0 SF(sendmail)2.15 E F6 4.15(.O)C .149(ther important di-)
-447.262 675.2 R(rectories that should ha)72 684.8 Q .24 -.12(ve r)-.16 H
-(estricti).12 E .24 -.12(ve o)-.2 H(wnerships and permissions are /bin, /usr/b\
-in, /etc, /usr/etc, /lib, and /usr/lib)-.08 E(.)-.32 E F5(3)93.6 695.2 Q F6
-(Actually)3.2 I 2.276(,t)-.52 G .276(he pathname v)129.632 698.4 R .276
-(aries depending on the operating system; /etc is the preferred directory)-.2 F
-4.276(.S)-.52 G .277(ome older systems install it)415.332 698.4 R(in)72 708 Q
-/F8 8/Times-Bold@0 SF(/usr/lib/sendmail.cf)2 E F6 2(,a)C(nd I')153.344 708 Q
-.24 -.12(ve a)-.4 H(lso seen it in).12 E F8(/usr/ucblib)2 E F6(and)2 E F8
-(/etc/mail)2 E F6 4(.I)C 2(fy)313.928 708 S(ou w)322.592 708 Q(ant to mo)-.08 E
-.24 -.12(ve t)-.12 H(his \214le, change).12 E F7(sr)2 E(c/conf)-.296 E(.h)-.12
-E F6(.)A EP
-%%Page: 10 5
-%%BeginPageSetup
-BP
-%%EndPageSetup
-/F0 10/Times-Bold@0 SF 193.36(SMM:08-10 Sendmail)72 60 R
-(Installation and Operation Guide)2.5 E/F1 10/Times-Roman@0 SF
-(library \214le names compiled into)117 98 Q/F2 10/Times-Italic@0 SF(sendmail)
-2.5 E/F3 7/Times-Roman@0 SF(4)283.38 94 Q F1(.)286.88 98 Q .721
-(The con\214guration \214le is normally created using the distrib)142 114.2 R
-.721(ution \214les described abo)-.2 F -.15(ve)-.15 G 5.72(.I).15 G(f)500.67
-114.2 Q .64(you ha)117 126.2 R .94 -.15(ve a p)-.2 H .64
-(articularly unusual system con\214guration you may need to create a special v)
-.15 F(ersion.)-.15 E
-(The format of this \214le is detailed in later sections of this document.)117
-138.2 Q F0 2.5(1.3.3. /usr/bin/newaliases)102 162.2 R F1(The)142 178.4 Q F2(ne)
-2.5 E(waliases)-.15 E F1(command should just be a link to)2.5 E F2(sendmail)2.5
-E F1(:)A(rm \255f /usr/bin/ne)157 194.6 Q -.1(wa)-.25 G(liases).1 E
-(ln \255s /usr/sbin/sendmail /usr/bin/ne)157 206.6 Q -.1(wa)-.25 G(liases).1 E
-(This can be installed in whate)117 222.8 Q -.15(ve)-.25 G 2.5(rs).15 G
-(earch path you prefer for your system.)254.91 222.8 Q F0 2.5(1.3.4. /v)102
-246.8 R(ar/spool/mqueue)-.1 E F1 .218(The directory)142 263 R F2
-(/var/spool/mqueue)2.718 E F1 .217(should be created to hold the mail queue.)
-2.718 F .217(This directory)5.217 F(should be mode 700 and o)117 275 Q
-(wned by root.)-.25 E(The actual path of this directory is de\214ned in the)142
-291.2 Q F0(Q)2.5 E F1(option of the)2.5 E F2(sendmail.cf)2.5 E F1(\214le.)2.5 E
-F0 2.5(1.3.5. /etc/aliases*)102 315.2 R F1 1.492
-(The system aliases are held in \231/etc/aliases\232.)142 331.4 R 3.992(As)
-6.492 G 1.492(ample is gi)350.006 331.4 R -.15(ve)-.25 G 3.993(ni).15 G 3.993
-<6e99>417.694 331.4 S 1.493(lib/aliases\232 which)431.127 331.4 R
-(includes some aliases which)117 343.4 Q F2(must)2.5 E F1(be de\214ned:)2.5 E
-(cp lib/aliases /etc/aliases)157 359.6 Q F2(edit /etc/aliases)157 371.6 Q F1
--1.1(Yo)117 387.8 S 2.5(us)1.1 G(hould e)139.51 387.8 Q
-(xtend this \214le with an)-.15 E 2.5(ya)-.15 G
-(liases that are apropos to your system.)267.54 387.8 Q(Normally)142 404 Q F2
-(sendmail)3.61 E F1 1.109(looks at a v)3.61 F 1.109
-(ersion of these \214les maintained by the)-.15 F F2(dbm)3.609 E F1 1.109
-(\(3\) or)1.666 F F2(db)3.609 E F1(\(3\))1.666 E 3.46(routines. These)117 416 R
-.96(are stored either in \231/etc/aliases.dir\232 and \231/etc/aliases.pag\232\
- or \231/etc/aliases.db\232)3.46 F 1.022
-(depending on which database package you are using.)117 428 R 1.022
-(These can initially be created as empty)6.022 F(\214les, b)117 440 Q(ut the)
--.2 E 2.5(yw)-.15 G(ill ha)180.54 440 Q .3 -.15(ve t)-.2 H 2.5(ob).15 G 2.5(ei)
-227.69 440 S(nitialized promptly)237.41 440 Q 5(.T)-.65 G
-(hese should be mode 644:)326.76 440 Q(cp /de)157 456.2 Q
-(v/null /etc/aliases.dir)-.25 E(cp /de)157 468.2 Q(v/null /etc/aliases.pag)-.25
-E(chmod 644 /etc/aliases.*)157 480.2 Q(ne)157 492.2 Q -.1(wa)-.25 G(liases).1 E
-(The)117 508.4 Q F2(db)2.79 E F1 .29(routines preset the mode reasonably)2.79 F
-2.79(,s)-.65 G 2.79(ot)301.68 508.4 S .29(his step can be skipped.)312.25 508.4
-R .29(The actual path of this)5.29 F(\214le is de\214ned in the)117 520.4 Q F0
-(A)2.5 E F1(option of the)2.5 E F2(sendmail.cf)2.5 E F1(\214le.)2.5 E F0 2.5
-(1.3.6. /etc/r)102 544.4 R(c)-.18 E F1 .156
-(It will be necessary to start up the)142 560.6 R F2(sendmail)2.655 E F1 .155
-(daemon when your system reboots.)2.655 F .155(This dae-)5.155 F 1.537
-(mon performs tw)117 572.6 R 4.037(of)-.1 G 1.537
-(unctions: it listens on the SMTP sock)201.221 572.6 R 1.537
-(et for connections \(to recei)-.1 F 1.838 -.15(ve m)-.25 H(ail).15 E .442(fro\
-m a remote system\) and it processes the queue periodically to insure that mai\
-l gets deli)117 584.6 R -.15(ve)-.25 G(red).15 E(when hosts come up.)117 596.6
-Q .505(Add the follo)142 612.8 R .505(wing lines to \231/etc/rc\232 \(or \231/\
-etc/rc.local\232 as appropriate\) in the area where it)-.25 F
-(is starting up the daemons:)117 624.8 Q .32 LW 76 669.2 72 669.2 DL 80 669.2
-76 669.2 DL 84 669.2 80 669.2 DL 88 669.2 84 669.2 DL 92 669.2 88 669.2 DL 96
-669.2 92 669.2 DL 100 669.2 96 669.2 DL 104 669.2 100 669.2 DL 108 669.2 104
-669.2 DL 112 669.2 108 669.2 DL 116 669.2 112 669.2 DL 120 669.2 116 669.2 DL
-124 669.2 120 669.2 DL 128 669.2 124 669.2 DL 132 669.2 128 669.2 DL 136 669.2
-132 669.2 DL 140 669.2 136 669.2 DL 144 669.2 140 669.2 DL 148 669.2 144 669.2
-DL 152 669.2 148 669.2 DL 156 669.2 152 669.2 DL 160 669.2 156 669.2 DL 164
-669.2 160 669.2 DL 168 669.2 164 669.2 DL 172 669.2 168 669.2 DL 176 669.2 172
-669.2 DL 180 669.2 176 669.2 DL 184 669.2 180 669.2 DL 188 669.2 184 669.2 DL
-192 669.2 188 669.2 DL 196 669.2 192 669.2 DL 200 669.2 196 669.2 DL 204 669.2
-200 669.2 DL 208 669.2 204 669.2 DL 212 669.2 208 669.2 DL 216 669.2 212 669.2
-DL/F4 5/Times-Roman@0 SF(4)93.6 679.6 Q/F5 8/Times-Roman@0 SF .588
-(The system libraries can reference other \214les; in particular)3.2 J 2.589
-(,s)-.32 G .589(ystem library subroutines that)294.805 682.8 R/F6 8
-/Times-Italic@0 SF(sendmail)2.589 E F5 .589(calls probably reference)2.589 F F6
-(/etc/passwd)72 692.4 Q F5(and)2 E F6(/etc/r)2 E(esolv)-.296 E(.conf)-.592 E F5
-(.)A EP
-%%Page: 11 6
-%%BeginPageSetup
-BP
-%%EndPageSetup
-/F0 10/Times-Bold@0 SF(Sendmail Installation and Operation Guide)72 60 Q
-(SMM:08-11)452.9 60 Q/F1 10/Times-Roman@0 SF
-(if [ \255f /usr/sbin/sendmail \255a \255f /etc/sendmail.cf ]; then)157 96 Q
-(\(cd /v)193 108 Q(ar/spool/mqueue; rm \255f [lnx]f*\))-.25 E
-(/usr/sbin/sendmail \255bd \255q30m &)193 120 Q(echo \255n ' sendmail' >/de)193
-132 Q(v/console)-.25 E<8c>157 144 Q .174
-(The \231cd\232 and \231rm\232 commands insure that all lock \214les ha)117
-160.2 R .473 -.15(ve b)-.2 H .173(een remo).15 F -.15(ve)-.15 G .173(d; e).15 F
-.173(xtraneous lock \214les)-.15 F .004
-(may be left around if the system goes do)117 172.2 R .005
-(wn in the middle of processing a message.)-.25 F .005(The line that)5.005 F
-2.294(actually in)117 184.2 R -.2(vo)-.4 G -.1(ke).2 G(s).1 E/F2 10
-/Times-Italic@0 SF(sendmail)4.794 E F1 2.294(has tw)4.794 F 4.794<6f8d>-.1 G
-2.293(ags: \231\255bd\232 causes it to listen on the SMTP port, and)272.94
-184.2 R(\231\255q30m\232 causes it to run the queue e)117 196.2 Q -.15(ve)-.25
-G(ry half hour).15 E(.)-.55 E .378(Some people use a more comple)142 212.4 R
-2.879(xs)-.15 G .379(tartup script, remo)285.209 212.4 R .379
-(ving zero length qf \214les and df \214les)-.15 F 1.121
-(for which there is no qf \214le.)117 224.4 R -.15(Fo)6.121 G 3.621(re).15 G
-1.12(xample, see Figure 1 for an e)262.868 224.4 R 1.12(xample of a comple)-.15
-F 3.62(xs)-.15 G(tartup)480.67 224.4 Q(script.)117 236.4 Q .755
-(If you are not running a v)142 252.6 R .755(ersion of UNIX that supports Berk)
--.15 F(ele)-.1 E 3.256(yT)-.15 G(CP/IP)416.722 252.6 Q 3.256(,d)-1.11 G 3.256
-(on)450.268 252.6 S .756(ot include)463.524 252.6 R(the)117 264.6 Q F0(\255bd)
-2.5 E F1(\215ag.)2.5 E F0 2.5(1.3.7. /usr/lib/sendmail.hf)102 288.6 R F1 2.078
-(This is the help \214le used by the SMTP)142 304.8 R F0(HELP)4.578 E F1 4.578
-(command. It)4.578 F 2.078(should be copied from)4.578 F
-(\231lib/sendmail.hf\232:)117 316.8 Q(cp lib/sendmail.hf /usr/lib)157 333 Q
-(The actual path of this \214le is de\214ned in the)117 349.2 Q F0(H)2.5 E F1
-(option of the)2.5 E F2(sendmail.cf)2.5 E F1(\214le.)2.5 E F0 2.5
-(1.3.8. /etc/sendmail.st)102 373.2 R F1 3.04
-(If you wish to collect statistics about your mail traf)142 389.4 R 3.04
-(\214c, you should create the \214le)-.25 F(\231/etc/sendmail.st\232:)117 401.4
-Q(cp /de)157 417.6 Q(v/null /etc/sendmail.st)-.25 E(chmod 666 /etc/sendmail.st)
-157 429.6 Q .716(This \214le does not gro)117 445.8 R 4.516 -.65(w. I)-.25 H
-3.216(ti).65 G 3.216(sp)231.506 445.8 S .716
-(rinted with the program \231mailstats/mailstats.c.)243.612 445.8 R 5.715<9a54>
--.7 G .715(he actual path)447.03 445.8 R(of this \214le is de\214ned in the)117
-457.8 Q F0(S)2.5 E F1(option of the)2.5 E F2(sendmail.cf)2.5 E F1(\214le.)2.5 E
-F0 2.5(1.3.9. /usr/bin/mailq)102 481.8 R F1(If)142 498 Q F2(sendmail)3.439 E F1
-.939(is in)3.439 F -.2(vo)-.4 G -.1(ke).2 G 3.439(da).1 G 3.439<7399>241.156
-498 S(mailq,)252.925 498 Q 3.439<9a69>-.7 G 3.439(tw)288.164 498 S .939
-(ill simulate the)301.603 498 R F0(\255bp)3.439 E F1 .94(\215ag \(i.e.,)3.44 F
-F2(sendmail)3.44 E F1 .94(will print)3.44 F
-(the contents of the mail queue; see belo)117 510 Q 2.5(w\). This)-.25 F
-(should be a link to /usr/sbin/sendmail.)2.5 E F0 2.5(2. NORMAL)72 534 R(OPERA)
-2.5 E(TIONS)-.95 E 2.5(2.1. The)87 558 R(System Log)2.5 E F1 1.511
-(The system log is supported by the)127 574.2 R F2(syslo)4.011 E(gd)-.1 E F1
-1.511(\(8\) program.)1.666 F 1.511(All messages from)6.511 F F2(sendmail)4.011
-E F1(are)4.011 E(logged under the)102 588.2 Q/F3 9/Times-Roman@0 SF(LOG_MAIL)
-2.5 E F1 -.1(fa)2.5 G(cility).1 E/F4 7/Times-Roman@0 SF(5)248.43 584.2 Q F1(.)
-251.93 588.2 Q F0 2.5(2.1.1. F)102 612.2 R(ormat)-.25 E F1 .574(Each line in t\
-he system log consists of a timestamp, the name of the machine that gener)142
-628.4 R(-)-.2 E .849(ated it \(for logging from se)117 640.4 R -.15(ve)-.25 G
-.849(ral machines o).15 F -.15(ve)-.15 G 3.349(rt).15 G .848
-(he local area netw)316.942 640.4 R .848(ork\), the w)-.1 F .848
-(ord \231sendmail:\232,)-.1 F(and a message)117 654.4 Q F4(6)174.76 650.4 Q F1
-5(.M)178.26 654.4 S(ost messages are a sequence of)194.65 654.4 Q F2(name)2.5 E
-F1(=)A F2(value)A F1(pairs.)2.5 E .32 LW 76 665.2 72 665.2 DL 80 665.2 76 665.2
-DL 84 665.2 80 665.2 DL 88 665.2 84 665.2 DL 92 665.2 88 665.2 DL 96 665.2 92
-665.2 DL 100 665.2 96 665.2 DL 104 665.2 100 665.2 DL 108 665.2 104 665.2 DL
-112 665.2 108 665.2 DL 116 665.2 112 665.2 DL 120 665.2 116 665.2 DL 124 665.2
-120 665.2 DL 128 665.2 124 665.2 DL 132 665.2 128 665.2 DL 136 665.2 132 665.2
-DL 140 665.2 136 665.2 DL 144 665.2 140 665.2 DL 148 665.2 144 665.2 DL 152
-665.2 148 665.2 DL 156 665.2 152 665.2 DL 160 665.2 156 665.2 DL 164 665.2 160
-665.2 DL 168 665.2 164 665.2 DL 172 665.2 168 665.2 DL 176 665.2 172 665.2 DL
-180 665.2 176 665.2 DL 184 665.2 180 665.2 DL 188 665.2 184 665.2 DL 192 665.2
-188 665.2 DL 196 665.2 192 665.2 DL 200 665.2 196 665.2 DL 204 665.2 200 665.2
-DL 208 665.2 204 665.2 DL 212 665.2 208 665.2 DL 216 665.2 212 665.2 DL/F5 5
-/Times-Roman@0 SF(5)93.6 675.6 Q/F6 8/Times-Roman@0 SF
-(Except on Ultrix, which does not support f)3.2 I(acilities in the syslog.)-.08
-E F5(6)93.6 689.2 Q F6(This format may v)3.2 I(ary slightly if your v)-.2 E
-(endor has changed the syntax.)-.12 E EP
-%%Page: 12 7
-%%BeginPageSetup
-BP
-%%EndPageSetup
-/F0 10/Times-Bold@0 SF 193.36(SMM:08-12 Sendmail)72 60 R
-(Installation and Operation Guide)2.5 E .4 LW 77 108 72 108 DL 79 108 74 108 DL
-84 108 79 108 DL 89 108 84 108 DL 94 108 89 108 DL 99 108 94 108 DL 104 108 99
-108 DL 109 108 104 108 DL 114 108 109 108 DL 119 108 114 108 DL 124 108 119 108
-DL 129 108 124 108 DL 134 108 129 108 DL 139 108 134 108 DL 144 108 139 108 DL
-149 108 144 108 DL 154 108 149 108 DL 159 108 154 108 DL 164 108 159 108 DL 169
-108 164 108 DL 174 108 169 108 DL 179 108 174 108 DL 184 108 179 108 DL 189 108
-184 108 DL 194 108 189 108 DL 199 108 194 108 DL 204 108 199 108 DL 209 108 204
-108 DL 214 108 209 108 DL 219 108 214 108 DL 224 108 219 108 DL 229 108 224 108
-DL 234 108 229 108 DL 239 108 234 108 DL 244 108 239 108 DL 249 108 244 108 DL
-254 108 249 108 DL 259 108 254 108 DL 264 108 259 108 DL 269 108 264 108 DL 274
-108 269 108 DL 279 108 274 108 DL 284 108 279 108 DL 289 108 284 108 DL 294 108
-289 108 DL 299 108 294 108 DL 304 108 299 108 DL 309 108 304 108 DL 314 108 309
-108 DL 319 108 314 108 DL 324 108 319 108 DL 329 108 324 108 DL 334 108 329 108
-DL 339 108 334 108 DL 344 108 339 108 DL 349 108 344 108 DL 354 108 349 108 DL
-359 108 354 108 DL 364 108 359 108 DL 369 108 364 108 DL 374 108 369 108 DL 379
-108 374 108 DL 384 108 379 108 DL 389 108 384 108 DL 394 108 389 108 DL 399 108
-394 108 DL 404 108 399 108 DL 409 108 404 108 DL 414 108 409 108 DL 419 108 414
-108 DL 424 108 419 108 DL 429 108 424 108 DL 434 108 429 108 DL 439 108 434 108
-DL 444 108 439 108 DL 449 108 444 108 DL 454 108 449 108 DL 459 108 454 108 DL
-464 108 459 108 DL 469 108 464 108 DL 474 108 469 108 DL 479 108 474 108 DL 484
-108 479 108 DL 489 108 484 108 DL 494 108 489 108 DL 499 108 494 108 DL 504 108
-499 108 DL/F1 10/Times-Roman@0 SF 2.5(#r)72 132 S(emo)82.83 132 Q .3 -.15(ve z)
--.15 H(ero length qf \214les).15 E(for qf)72 144 Q(\214le in qf*)-.25 E(do)72
-156 Q(if [ \255r $qf)108 168 Q(\214le ])-.25 E(then)108 180 Q(if [ ! \255s $qf)
-144 192 Q(\214le ])-.25 E(then)144 204 Q(echo \255n " <zero: $qf)180 216 Q
-(\214le>" > /de)-.25 E(v/console)-.25 E(rm \255f $qf)180 228 Q(\214le)-.25 E
-<8c>144 240 Q<8c>108 252 Q(done)72 264 Q 2.5(#r)72 276 S
-(ename tf \214les to be qf if the qf does not e)82.83 276 Q(xist)-.15 E(for tf)
-72 288 Q(\214le in tf*)-.25 E(do)72 300 Q(qf)108 312 Q(\214le=`echo $tf)-.25 E
-(\214le | sed ')-.25 E(s/t/q/'`)-.55 E(if [ \255r $tf)108 324 Q
-(\214le \255a ! \255f $qf)-.25 E(\214le ])-.25 E(then)108 336 Q
-(echo \255n " <reco)144 348 Q -.15(ve)-.15 G(ring: $tf).15 E(\214le>" > /de)
--.25 E(v/console)-.25 E(mv $tf)144 360 Q(\214le $qf)-.25 E(\214le)-.25 E(else)
-108 372 Q(echo \255n " <e)144 384 Q(xtra: $tf)-.15 E(\214le>" > /de)-.25 E
-(v/console)-.25 E(rm \255f $tf)144 396 Q(\214le)-.25 E<8c>108 408 Q(done)72 420
-Q 2.5(#r)72 432 S(emo)82.83 432 Q .3 -.15(ve d)-.15 H 2.5<668c>.15 G
-(les with no corresponding qf \214les)128.08 432 Q(for df)72 444 Q
-(\214le in df*)-.25 E(do)72 456 Q(qf)108 468 Q(\214le=`echo $df)-.25 E
-(\214le | sed ')-.25 E(s/d/q/'`)-.55 E(if [ \255r $df)108 480 Q
-(\214le \255a ! \255f $qf)-.25 E(\214le ])-.25 E(then)108 492 Q
-(echo \255n " <incomplete: $df)144 504 Q(\214le>" > /de)-.25 E(v/console)-.25 E
-(mv $df)144 516 Q(\214le `echo $df)-.25 E(\214le | sed ')-.25 E(s/d/D/'`)-.55 E
-<8c>108 528 Q(done)72 540 Q 2.5(#a)72 552 S(nnounce \214les that ha)83.94 552 Q
-.3 -.15(ve b)-.2 H(een sa).15 E -.15(ve)-.2 G 2.5(dd).15 G(uring disaster reco)
-229.32 552 Q -.15(ve)-.15 G(ry).15 E(for xf)72 564 Q(\214le in [A-Z]f*)-.25 E
-(do)72 576 Q(echo \255n " <panic: $xf)108 588 Q(\214le>" > /de)-.25 E
-(v/console)-.25 E(done)72 600 Q(Figure 1 \212 A comple)214.47 624 Q 2.5(xs)-.15
-G(tartup script)313.48 624 Q 77 636 72 636 DL 79 636 74 636 DL 84 636 79 636 DL
-89 636 84 636 DL 94 636 89 636 DL 99 636 94 636 DL 104 636 99 636 DL 109 636
-104 636 DL 114 636 109 636 DL 119 636 114 636 DL 124 636 119 636 DL 129 636 124
-636 DL 134 636 129 636 DL 139 636 134 636 DL 144 636 139 636 DL 149 636 144 636
-DL 154 636 149 636 DL 159 636 154 636 DL 164 636 159 636 DL 169 636 164 636 DL
-174 636 169 636 DL 179 636 174 636 DL 184 636 179 636 DL 189 636 184 636 DL 194
-636 189 636 DL 199 636 194 636 DL 204 636 199 636 DL 209 636 204 636 DL 214 636
-209 636 DL 219 636 214 636 DL 224 636 219 636 DL 229 636 224 636 DL 234 636 229
-636 DL 239 636 234 636 DL 244 636 239 636 DL 249 636 244 636 DL 254 636 249 636
-DL 259 636 254 636 DL 264 636 259 636 DL 269 636 264 636 DL 274 636 269 636 DL
-279 636 274 636 DL 284 636 279 636 DL 289 636 284 636 DL 294 636 289 636 DL 299
-636 294 636 DL 304 636 299 636 DL 309 636 304 636 DL 314 636 309 636 DL 319 636
-314 636 DL 324 636 319 636 DL 329 636 324 636 DL 334 636 329 636 DL 339 636 334
-636 DL 344 636 339 636 DL 349 636 344 636 DL 354 636 349 636 DL 359 636 354 636
-DL 364 636 359 636 DL 369 636 364 636 DL 374 636 369 636 DL 379 636 374 636 DL
-384 636 379 636 DL 389 636 384 636 DL 394 636 389 636 DL 399 636 394 636 DL 404
-636 399 636 DL 409 636 404 636 DL 414 636 409 636 DL 419 636 414 636 DL 424 636
-419 636 DL 429 636 424 636 DL 434 636 429 636 DL 439 636 434 636 DL 444 636 439
-636 DL 449 636 444 636 DL 454 636 449 636 DL 459 636 454 636 DL 464 636 459 636
-DL 469 636 464 636 DL 474 636 469 636 DL 479 636 474 636 DL 484 636 479 636 DL
-489 636 484 636 DL 494 636 489 636 DL 499 636 494 636 DL 504 636 499 636 DL .68
-(The tw)142 672 R 3.18(om)-.1 G .68
-(ost common lines are logged when a message is processed.)186.59 672 R .68
-(The \214rst logs the)5.68 F .376(receipt of a message; there will be e)117 684
-R .376(xactly one of these per message.)-.15 F .376(Some \214elds may be omit-)
-5.376 F(ted if the)117 696 Q 2.5(yd)-.15 G 2.5(on)164.9 696 S
-(ot contain interesting information.)177.4 696 Q(Fields are:)5 E 50.06
-(from The)117 712.2 R(en)2.5 E -.15(ve)-.4 G(lope sender address.).15 E EP
-%%Page: 13 8
-%%BeginPageSetup
-BP
-%%EndPageSetup
-/F0 10/Times-Bold@0 SF(Sendmail Installation and Operation Guide)72 60 Q
-(SMM:08-13)452.9 60 Q/F1 10/Times-Roman@0 SF 53.95(size The)117 96 R
-(size of the message in bytes.)2.5 E 50.06(class The)117 112.2 R
-(class \(i.e., numeric precedence\) of the message.)2.5 E 58.39(pri The)117
-128.4 R(initial message priority \(used for queue sorting\).)2.5 E 45.06
-(nrcpts The)117 144.6 R 1.514(number of en)4.014 F -.15(ve)-.4 G 1.515
-(lope recipients for this message \(after aliasing and for).15 F(-)-.2 E -.1
-(wa)189 156.6 S(rding\).).1 E 45.05(msgid The)117 172.8 R
-(message id of the message \(from the header\).)2.5 E 48.39(proto The)117 189 R
-(protocol used to recei)2.5 E .3 -.15(ve t)-.25 H
-(his message \(e.g., ESMTP or UUCP\)).15 E 49.51(relay The)117 205.2 R
-(machine from which it w)2.5 E(as recei)-.1 E -.15(ve)-.25 G(d.).15 E .43
-(There is also one line logged per deli)117 221.4 R -.15(ve)-.25 G .43
-(ry attempt \(so there can be se).15 F -.15(ve)-.25 G .43
-(ral per message if deli).15 F(v-)-.25 E
-(ery is deferred or there are multiple recipients\).)117 233.4 Q(Fields are:)5
-E 61.72(to A)117 249.6 R(comma-separated list of the recipients to this mailer)
-2.5 E(.)-.55 E 41.73(ctladdr The)117 265.8 R -.74(``)2.726 G .226
-(controlling user').74 F .226
-(', that is, the name of the user whose credentials we use)-.74 F(for deli)189
-277.8 Q -.15(ve)-.25 G(ry).15 E(.)-.65 E 47.84(delay The)117 294 R 1.303
-(total delay between the time this message w)3.804 F 1.303(as recei)-.1 F -.15
-(ve)-.25 G 3.803(da).15 G 1.303(nd the time it)447.031 294 R -.1(wa)189 306 S
-2.5(sd).1 G(eli)211.95 306 Q -.15(ve)-.25 G(red.).15 E 42.84(xdelay The)117
-322.2 R .116(amount of time needed in this deli)2.615 F -.15(ve)-.25 G .116
-(ry attempt \(normally indicati).15 F .416 -.15(ve o)-.25 H 2.616(ft).15 G(he)
-494.56 322.2 Q(speed of the connection\).)189 334.2 Q 43.95(mailer The)117
-350.4 R(name of the mailer used to deli)2.5 E -.15(ve)-.25 G 2.5(rt).15 G 2.5
-(ot)348.57 350.4 S(his recipient.)358.85 350.4 Q 49.51(relay The)117 366.6 R
-(name of the host that actually accepted \(or rejected\) this recipient.)2.5 E
-55.61(stat The)117 382.8 R(deli)2.5 E -.15(ve)-.25 G(ry status.).15 E
-(Not all \214elds are present in all messages; for e)117 399 Q
-(xample, the relay is not listed for local deli)-.15 E -.15(ve)-.25 G(ries.).15
-E F0 2.5(2.1.2. Le)102 423 R -.1(ve)-.15 G(ls).1 E F1 .205(If you ha)142 439.2
-R -.15(ve)-.2 G/F2 10/Times-Italic@0 SF(syslo)2.855 E(gd)-.1 E F1 .205
-(\(8\) or an equi)1.666 F -.25(va)-.25 G .205
-(lent installed, you will be able to do logging.).25 F .204(There is)5.204 F
-2.787(al)117 451.2 S(ar)127.007 451.2 Q .287
-(ge amount of information that can be logged.)-.18 F .287
-(The log is arranged as a succession of le)5.287 F -.15(ve)-.25 G(ls.).15 E
-.651(At the lo)117 463.2 R .651(west le)-.25 F -.15(ve)-.25 G 3.151(lo).15 G
-.651(nly e)201.724 463.2 R .651(xtremely strange situations are logged.)-.15 F
-.65(At the highest le)5.651 F -.15(ve)-.25 G .65(l, e).15 F -.15(ve)-.25 G 3.15
-(nt).15 G(he)494.56 463.2 Q .825(most mundane and uninteresting e)117 475.2 R
--.15(ve)-.25 G .825(nts are recorded for posterity).15 F 5.826(.A)-.65 G 3.326
-(sac)400.266 475.2 S(on)419.688 475.2 Q -.15(ve)-.4 G .826(ntion, log le).15 F
--.15(ve)-.25 G(ls).15 E .201
-(under ten are considered generally \231useful;\232 log le)117 487.2 R -.15(ve)
--.25 G .201(ls abo).15 F .501 -.15(ve 6)-.15 H 2.701(4a).15 G .2(re reserv)
-381.57 487.2 R .2(ed for deb)-.15 F .2(ugging pur)-.2 F(-)-.2 E 2.5(poses. Le)
-117 499.2 R -.15(ve)-.25 G(ls from 11\25564 are reserv).15 E(ed for v)-.15 E
-(erbose information that some sites might w)-.15 E(ant.)-.1 E 2.5(Ac)142 515.4
-S(omplete description of the log le)156.16 515.4 Q -.15(ve)-.25 G(ls is gi).15
-E -.15(ve)-.25 G 2.5(ni).15 G 2.5(ns)340.35 515.4 S(ection 4.6.)351.74 515.4 Q
-F0 2.5(2.2. Dumping)87 539.4 R(State)2.5 E F1 -1.1(Yo)127 555.6 S 2.563(uc)1.1
-G .063(an ask)150.123 555.6 R F2(sendmail)2.563 E F1 .064
-(to log a dump of the open \214les and the connection cache by sending it a)
-2.563 F/F3 9/Times-Roman@0 SF(SIGUSR1)102 567.6 Q F1 2.5(signal. The)2.5 F
-(results are logged at)2.5 E F3(LOG_DEB)2.5 E(UG)-.09 E F1(priority)2.5 E(.)
--.65 E F0 2.5(2.3. The)87 591.6 R(Mail Queue)2.5 E F1 1.283
-(Sometimes a host cannot handle a message immediately)127 607.8 R 6.283(.F)-.65
-G 1.283(or e)374.224 607.8 R 1.283(xample, it may be do)-.15 F 1.282(wn or)-.25
-F -.15(ove)102 619.8 S .042(rloaded, causing it to refuse connections.).15 F
-.043(The sending host is then e)5.043 F .043(xpected to sa)-.15 F .343 -.15
-(ve t)-.2 H .043(his message).15 F(in its mail queue and attempt to deli)102
-631.8 Q -.15(ve)-.25 G 2.5(ri).15 G 2.5(tl)263.26 631.8 S(ater)271.32 631.8 Q
-(.)-.55 E .568
-(Under normal conditions the mail queue will be processed transparently)127 648
-R 5.568(.H)-.65 G -.25(ow)434.764 648 S -2.15 -.25(ev e).25 H 1.368 -.4(r, y)
-.25 H .568(ou may).4 F .993(\214nd that manual interv)102 660 R .993
-(ention is sometimes necessary)-.15 F 5.993(.F)-.65 G .993(or e)332.711 660 R
-.993(xample, if a major host is do)-.15 F .994(wn for a)-.25 F 1.699
-(period of time the queue may become clogged.)102 672 R(Although)6.699 E F2
-(sendmail)4.199 E F1 1.699(ought to reco)4.199 F -.15(ve)-.15 G 4.199(rg).15 G
-(racefully)468.46 672 Q(when the host comes up, you may \214nd performance una\
-cceptably bad in the meantime.)102 684 Q EP
-%%Page: 14 9
-%%BeginPageSetup
-BP
-%%EndPageSetup
-/F0 10/Times-Bold@0 SF 193.36(SMM:08-14 Sendmail)72 60 R
-(Installation and Operation Guide)2.5 E 2.5(2.3.1. Printing)102 96 R(the queue)
-2.5 E/F1 10/Times-Roman@0 SF .526
-(The contents of the queue can be printed using the)142 112.2 R/F2 10
-/Times-Italic@0 SF(mailq)3.026 E F1 .526(command \(or by specifying the)3.026 F
-F0(\255bp)117 124.2 Q F1(\215ag to)2.5 E F2(sendmail)2.5 E F1(\):)A(mailq)157
-140.4 Q 1.673(This will produce a listing of the queue id')117 156.6 R 1.673
-(s, the size of the message, the date the message)-.55 F
-(entered the queue, and the sender and recipients.)117 168.6 Q F0 2.5(2.3.2. F)
-102 192.6 R(or)-.25 E(cing the queue)-.18 E F2(Sendmail)142 208.8 Q F1 1.137
-(should run the queue automatically at interv)3.637 F 3.638(als. The)-.25 F
-1.138(algorithm is to read and)3.638 F .355
-(sort the queue, and then to attempt to process all jobs in order)117 220.8 R
-5.355(.W)-.55 G .355(hen it attempts to run the job,)384.37 220.8 R F2
-(sendmail)117 232.8 Q F1(\214rst checks to see if the job is lock)2.5 E 2.5
-(ed. If)-.1 F(so, it ignores the job)2.5 E(.)-.4 E .338
-(There is no attempt to insure that only one queue processor e)142 249 R .338
-(xists at an)-.15 F 2.838(yt)-.15 G .339(ime, since there)440.282 249 R .095
-(is no guarantee that a job cannot tak)117 261 R 2.595(ef)-.1 G(ore)272.07 261
-Q -.15(ve)-.25 G 2.595(rt).15 G 2.595(op)302.585 261 S .094(rocess \(ho)315.18
-261 R(we)-.25 E -.15(ve)-.25 G -.4(r,).15 G F2(sendmail)2.994 E F1 .094
-(does include heuris-)2.594 F 1.086
-(tics to try to abort jobs that are taking absurd amounts of time; technically)
-117 273 R 3.587(,t)-.65 G 1.087(his violates RFC)435.146 273 R .462(821, b)117
-285 R .461(ut is blessed by RFC 1123\).)-.2 F .461
-(Due to the locking algorithm, it is impossible for one job to)5.461 F 1.086
-(freeze the entire queue.)117 297 R(Ho)6.086 E(we)-.25 E -.15(ve)-.25 G 1.886
--.4(r, a).15 H 3.586(nu).4 G(ncooperati)279.346 297 Q 1.386 -.15(ve r)-.25 H
-1.086(ecipient host or a program recipient that).15 F(ne)117 309 Q -.15(ve)-.25
-G 3.351(rr).15 G .851(eturns can accumulate man)145.491 309 R 3.351(yp)-.15 G
-.851(rocesses in your system.)269.825 309 R(Unfortunately)5.851 E 3.351(,t)-.65
-G .85(here is no com-)439.52 309 R(pletely general w)117 321 Q(ay to solv)-.1 E
-2.5(et)-.15 G(his.)234.23 321 Q .082
-(In some cases, you may \214nd that a major host going do)142 337.2 R .083
-(wn for a couple of days may create)-.25 F 2.925(ap)117 349.2 S(rohibiti)
-129.365 349.2 Q -.15(ve)-.25 G .425(ly lar).15 F .425(ge queue.)-.18 F .424
-(This will result in)5.425 F F2(sendmail)2.924 E F1 .424
-(spending an inordinate amount of time)2.924 F 1.084(sorting the queue.)117
-361.2 R 1.084(This situation can be \214x)6.084 F 1.084(ed by mo)-.15 F 1.085
-(ving the queue to a temporary place and)-.15 F .023(creating a ne)117 373.2 R
-2.523(wq)-.25 G 2.523(ueue. The)182.629 373.2 R .022
-(old queue can be run later when the of)2.523 F .022
-(fending host returns to service.)-.25 F 1.6 -.8(To d)142 389.4 T 2.5(ot).8 G
-(his, it is acceptable to mo)170.09 389.4 Q .3 -.15(ve t)-.15 H
-(he entire queue directory:).15 E(cd /v)157 405.6 Q(ar/spool)-.25 E
-(mv mqueue omqueue; mkdir mqueue; chmod 700 mqueue)157 417.6 Q -1.1(Yo)117
-433.8 S 2.708(us)1.1 G .208(hould then kill the e)139.718 433.8 R .209
-(xisting daemon \(since it will still be processing in the old queue direc-)
--.15 F(tory\) and create a ne)117 445.8 Q 2.5(wd)-.25 G(aemon.)213.1 445.8 Q
-1.6 -.8(To r)142 462 T(un the old mail queue, run the follo).8 E(wing command:)
--.25 E(/usr/sbin/sendmail \255oQ/v)157 478.2 Q(ar/spool/omqueue \255q)-.25 E
-(The)117 494.4 Q F0(\255oQ)2.868 E F1 .367
-(\215ag speci\214es an alternate queue directory and the)2.868 F F0<ad71>2.867
-E F1 .367(\215ag says to just run e)2.867 F -.15(ve)-.25 G .367(ry job in).15 F
-.593(the queue.)117 506.4 R .593(If you ha)5.593 F .893 -.15(ve a t)-.2 H
-(endenc).15 E 3.093(yt)-.15 G -2.1 -.25(ow a)263.111 506.4 T .593(rd v).25 F
--.1(oy)-.2 G .593(eurism, you can use the).1 F F0<ad76>3.094 E F1 .594
-(\215ag to w)3.094 F .594(atch what is)-.1 F(going on.)117 518.4 Q
-(When the queue is \214nally emptied, you can remo)142 534.6 Q .3 -.15(ve t)
--.15 H(he directory:).15 E(rmdir /v)157 550.8 Q(ar/spool/omqueue)-.25 E F0 2.5
-(2.4. The)87 579 R(Ser)2.5 E(vice Switch)-.1 E F1 1.416(The implementation of \
-certain system services such as host and user name lookup is con-)127 595.2 R
-.335(trolled by the service switch.)102 607.2 R .336
-(If the host operating system supports such a switch)5.335 F F2(sendmail)2.836
-E F1 .336(will use)2.836 F(the nati)102 619.2 Q .3 -.15(ve ve)-.25 H 2.5
-(rsion. Ultrix,).15 F(Solaris, and DEC OSF/1 are e)2.5 E
-(xamples of such systems.)-.15 E .969(If the underlying operating system does \
-not support a service switch \(e.g., SunOS, HP-UX,)127 635.4 R .975(BSD\) then)
-102 647.4 R F2(sendmail)3.475 E F1 .975(will pro)3.475 F .975
-(vide a stub implementation.)-.15 F(The)5.975 E F0(Ser)3.475 E(viceSwitchFile)
--.1 E F1 .975(option points to)3.475 F .382(the name of a \214le that has the \
-service de\214nitions Each line has the name of a service and the possi-)102
-659.4 R(ble implementations of that service.)102 671.4 Q -.15(Fo)5 G 2.5(re).15
-G(xample, the \214le:)270.57 671.4 Q 12.94(hosts dns)142 687.6 R(\214les nis)
-2.5 E 6.84(aliases \214les)142 699.6 R(nis)2.5 E .328(will ask)102 715.8 R F2
-(sendmail)2.828 E F1 .328(to look for hosts in the Domain Name System \214rst.)
-2.828 F .329(If the requested host name is)5.329 F EP
-%%Page: 15 10
-%%BeginPageSetup
-BP
-%%EndPageSetup
-/F0 10/Times-Bold@0 SF(Sendmail Installation and Operation Guide)72 60 Q
-(SMM:08-15)452.9 60 Q/F1 10/Times-Roman@0 SF .379
-(not found, it tries local \214les, and if that f)102 96 R .379
-(ails it tries NIS.)-.1 F(Similarly)5.379 E 2.879(,w)-.65 G .379
-(hen looking for aliases it will)385.166 96 R
-(try the local \214les \214rst follo)102 108 Q(wed by NIS.)-.25 E 1.269
-(Service switches are not completely inte)127 124.2 R 3.769(grated. F)-.15 F
-1.269(or e)-.15 F 1.269(xample, despite the f)-.15 F 1.27(act that the host)-.1
-F .294(entry listed in the abo)102 136.2 R .594 -.15(ve ex)-.15 H .293
-(ample speci\214es to look in NIS, on SunOS this w).15 F(on')-.1 E 2.793(th)
--.18 G .293(appen because the)430.664 136.2 R 1.398(system implementation of)
-102 148.2 R/F2 10/Times-Italic@0 SF -.1(ge)3.898 G(thostbyname).1 E F1 1.398
-(\(3\) doesn')1.666 F 3.898(tu)-.18 G 1.399(nderstand this.)327.856 148.2 R
-1.399(If there is enough demand)6.399 F F2(sendmail)102 160.2 Q F1 .015
-(may reimplement)2.515 F F2 -.1(ge)2.515 G(thostbyname).1 E F1(\(3\),)1.666 E
-F2 -.1(ge)2.515 G(thostbyaddr).1 E F1(\(3\),)1.666 E F2 -.1(ge)2.515 G(tpwent)
-.1 E F1 .014(\(3\), and the other system)1.666 F(routines that w)102 172.2 Q
-(ould be necessary to mak)-.1 E 2.5(et)-.1 G(his w)272.05 172.2 Q
-(ork seamlessly)-.1 E(.)-.65 E F0 2.5(2.5. The)87 196.2 R(Alias Database)2.5 E
-F1 .36(The alias database e)127 212.4 R .36(xists in tw)-.15 F 2.86(of)-.1 G
-2.861(orms. One)261.11 212.4 R .361(is a te)2.861 F .361
-(xt form, maintained in the \214le)-.15 F F2(/etc/aliases.)2.861 E F1
-(The aliases are of the form)102 224.4 Q(name: name1, name2, ...)142 240.6 Q
-(Only local names may be aliased; e.g.,)102 256.8 Q(eric@prep.ai.MIT)142 273 Q
-(.EDU: eric@CS.Berk)-.74 E(ele)-.1 E -.65(y.)-.15 G(EDU).65 E 1.088
-(will not ha)102 291.2 R 1.388 -.15(ve t)-.2 H 1.088(he desired ef).15 F 1.088
-(fect \(e)-.25 F 1.088(xcept on prep.ai.MIT)-.15 F 1.088(.EDU, and the)-.74 F
-3.588(yp)-.15 G 1.088(robably don')400.868 291.2 R 3.587(tw)-.18 G 1.087
-(ant me\))466.643 291.2 R/F3 7/Times-Roman@0 SF(7)498 287.2 Q F1(.)501.5 291.2
-Q .561(Aliases may be continued by starting an)102 303.2 R 3.061(yc)-.15 G .561
-(ontinuation lines with a space or a tab)277.697 303.2 R 5.562(.B)-.4 G .562
-(lank lines and)447.326 303.2 R(lines be)102 315.2 Q
-(ginning with a sharp sign \(\231#\232\) are comments.)-.15 E 1.478
-(The second form is processed by the)127 333.4 R F2(ndbm)3.978 E F1(\(3\))1.666
-E F3(8)321.472 329.4 Q F1(or)328.95 333.4 Q F2(db)3.978 E F1 1.478
-(\(3\) library)1.666 F 6.478(.T)-.65 G 1.478(his form is in the \214les)409.66
-333.4 R F2(/etc/aliases.dir)102 345.4 Q F1(and)3.028 E F2(/etc/aliases.pa)3.028
-E -.15(g.)-.1 G F1 .528(This is the form that)5.678 F F2(sendmail)3.029 E F1
-.529(actually uses to resolv)3.029 F 3.029(ea)-.15 G(liases.)479.28 345.4 Q
-(This technique is used to impro)102 357.4 Q .3 -.15(ve p)-.15 H(erformance.)
-.15 E(The control of search order is actually set by the service switch.)127
-373.6 Q(Essentially)5 E 2.5(,t)-.65 G(he entry)437.96 373.6 Q -.35(OA)142 389.8
-S(switch:aliases).35 E .927(is al)102 406 R -.1(wa)-.1 G .927(ys added as the \
-\214rst alias entry; also, the \214rst alias \214le name without a class \(e.g\
-., without).1 F .268
-(\231nis:\232 on the front\) will be used as the name of the \214le for a `)102
-418 R(`\214les')-.74 E 2.769('e)-.74 G .269(ntry in the aliases switch.)382.535
-418 R -.15(Fo)5.269 G(r).15 E -.15(ex)102 430 S
-(ample, if the con\214guration \214le contains).15 E -.35(OA)142 446.2 S
-(/etc/aliases).35 E(and the service switch contains)102 462.4 Q 6.84
-(aliases nis)142 478.6 R(\214les nisplus)2.5 E 2.449(then aliases will \214rst\
- be searched in the NIS database, then in /etc/aliases, then in the NIS+)102
-494.8 R(database.)102 506.8 Q -1.1(Yo)127 523 S 2.5(uc)1.1 G(an also use)150.06
-523 Q/F4 9/Times-Roman@0 SF(NIS)2.5 E F1(-based alias \214les.)A -.15(Fo)5 G
-2.5(re).15 G(xample, the speci\214cation:)305.069 523 Q -.35(OA)142 539.2 S
-(/etc/aliases).35 E -.35(OA)142 551.2 S(nis:mail.aliases@my).35 E(.nis.domain)
--.65 E 1.725(will \214rst search the /etc/aliases \214le and then the map name\
-d \231mail.aliases\232 in \231my)102 567.4 R(.nis.domain\232.)-.65 E -.8(Wa)102
-579.4 S .59(rning: if you b).8 F .59(uild your o)-.2 F(wn)-.25 E F4(NIS)3.09 E
-F1 .589(-based alias \214les, be sure to pro)B .589(vide the)-.15 F F0<ad6c>
-3.089 E F1 .589(\215ag to)3.089 F F2(mak)3.089 E(edbm)-.1 E F1(\(8\))A .159
-(to map upper case letters in the k)102 591.4 R -.15(ey)-.1 G 2.659(st).15 G
-2.659(ol)253.55 591.4 S -.25(ow)263.989 591.4 S .159
-(er case; otherwise, aliases with upper case letters in their).25 F(names w)102
-603.4 Q(on')-.1 E 2.5(tm)-.18 G(atch incoming addresses.)163.38 603.4 Q
-(Additional \215ags can be added after the colon e)127 619.6 Q(xactly lik)-.15
-E 2.5(ea)-.1 G F0(K)A F1(line \212 for e)2.5 E(xample:)-.15 E -.35(OA)142 635.8
-S(nis:\255N mail.aliases@my).35 E(.nis.domain)-.65 E
-(will search the appropriate NIS map and al)102 652 Q -.1(wa)-.1 G
-(ys include null bytes in the k).1 E -.15(ey)-.1 G(.)-.5 E .32 LW 76 665.2 72
-665.2 DL 80 665.2 76 665.2 DL 84 665.2 80 665.2 DL 88 665.2 84 665.2 DL 92
-665.2 88 665.2 DL 96 665.2 92 665.2 DL 100 665.2 96 665.2 DL 104 665.2 100
-665.2 DL 108 665.2 104 665.2 DL 112 665.2 108 665.2 DL 116 665.2 112 665.2 DL
-120 665.2 116 665.2 DL 124 665.2 120 665.2 DL 128 665.2 124 665.2 DL 132 665.2
-128 665.2 DL 136 665.2 132 665.2 DL 140 665.2 136 665.2 DL 144 665.2 140 665.2
-DL 148 665.2 144 665.2 DL 152 665.2 148 665.2 DL 156 665.2 152 665.2 DL 160
-665.2 156 665.2 DL 164 665.2 160 665.2 DL 168 665.2 164 665.2 DL 172 665.2 168
-665.2 DL 176 665.2 172 665.2 DL 180 665.2 176 665.2 DL 184 665.2 180 665.2 DL
-188 665.2 184 665.2 DL 192 665.2 188 665.2 DL 196 665.2 192 665.2 DL 200 665.2
-196 665.2 DL 204 665.2 200 665.2 DL 208 665.2 204 665.2 DL 212 665.2 208 665.2
-DL 216 665.2 212 665.2 DL/F5 5/Times-Roman@0 SF(7)93.6 675.6 Q/F6 8
-/Times-Roman@0 SF(Actually)3.2 I 2(,a)-.52 G .24 -.12(ny m)130.684 678.8 T
-(ailer that has the `).12 E 1.776 -.888(A' m)-.64 H(ailer \215ag set will perm\
-it aliasing; this is normally limited to the local mailer).888 E(.)-.44 E F5(8)
-93.6 689.2 Q F6(The)3.2 I/F7 8/Times-Italic@0 SF(gdbm)2 E F6
-(package probably w)2 E(orks as well.)-.08 E EP
-%%Page: 16 11
-%%BeginPageSetup
-BP
-%%EndPageSetup
-/F0 10/Times-Bold@0 SF 193.36(SMM:08-16 Sendmail)72 60 R
-(Installation and Operation Guide)2.5 E 2.5(2.5.1. Reb)102 96 R
-(uilding the alias database)-.2 E/F1 10/Times-Roman@0 SF .542(The DB or DBM v)
-142 112.2 R .542(ersion of the database may be reb)-.15 F .542(uilt e)-.2 F
-.542(xplicitly by e)-.15 F -.15(xe)-.15 G .542(cuting the com-).15 F(mand)117
-124.2 Q(ne)157 140.4 Q -.1(wa)-.25 G(liases).1 E(This is equi)117 156.6 Q -.25
-(va)-.25 G(lent to gi).25 E(ving)-.25 E/F2 10/Times-Italic@0 SF(sendmail)2.5 E
-F1(the)2.5 E F0(\255bi)2.5 E F1(\215ag:)2.5 E(/usr/sbin/sendmail \255bi)157
-172.8 Q 2.29(If the)142 193.2 R F0(Reb)4.79 E(uildAliases)-.2 E F1(\(old)4.79 E
-F0(D)4.79 E F1 4.79(\)o)C 2.29(ption is speci\214ed in the con\214guration,)
-280.19 193.2 R F2(sendmail)4.79 E F1(will)4.79 E(reb)117 205.2 Q .775
-(uild the alias database automatically if possible when it is out of date.)-.2
-F(Auto-reb)5.774 E .774(uild can be)-.2 F 1.853(dangerous on hea)117 217.2 R
-1.853(vily loaded machines with lar)-.2 F 1.853
-(ge alias \214les; if it might tak)-.18 F 4.354(em)-.1 G 1.854(ore than the)
-453.082 217.2 R(reb)117 229.2 Q 2.832(uild timeout \(option)-.2 F F0(AliasW)
-5.332 E(ait)-.65 E F1 5.332(,o)C(ld)275.538 229.2 Q F0(a)5.332 E F1 5.332(,w)C
-2.831(hich is normally \214v)308.702 229.2 R 5.331(em)-.15 G 2.831
-(inutes\) to reb)412.657 229.2 R 2.831(uild the)-.2 F
-(database, there is a chance that se)117 241.2 Q -.15(ve)-.25 G
-(ral processes will start the reb).15 E(uild process simultaneously)-.2 E(.)
--.65 E 1.77(If you ha)142 257.4 R 2.07 -.15(ve m)-.2 H 1.77
-(ultiple aliases databases speci\214ed, the).15 F F0(\255bi)4.27 E F1 1.77
-(\215ag reb)4.27 F 1.77(uilds all the database)-.2 F
-(types it understands \(for e)117 269.4 Q(xample, it can reb)-.15 E
-(uild NDBM databases b)-.2 E(ut not NIS databases\).)-.2 E F0 2.5(2.5.2. P)102
-293.4 R(otential pr)-.2 E(oblems)-.18 E F1 1.131
-(There are a number of problems that can occur with the alias database.)142
-309.6 R(The)6.13 E 3.63(ya)-.15 G 1.13(ll result)472.59 309.6 R 1.103(from a)
-117 321.6 R F2(sendmail)3.603 E F1 1.103(process accessing the DBM v)3.603 F
-1.103(ersion while it is only partially b)-.15 F 3.604(uilt. This)-.2 F(can)
-3.604 E 1.249(happen under tw)117 333.6 R 3.749(oc)-.1 G 1.248
-(ircumstances: One process accesses the database while another process is)
-199.237 333.6 R(reb)117 345.6 Q .518(uilding it, or the process reb)-.2 F .518
-(uilding the database dies \(due to being killed or a system crash\))-.2 F
-(before completing the reb)117 357.6 Q(uild.)-.2 E .401
-(Sendmail has three techniques to try to relie)142 373.8 R .701 -.15(ve t)-.25
-H .401(hese problems.).15 F .4(First, it ignores interrupts)5.401 F .045
-(while reb)117 385.8 R .045(uilding the database; this a)-.2 F -.2(vo)-.2 G
-.045(ids the problem of someone aborting the process lea).2 F .045(ving a)-.2 F
-.177(partially reb)117 397.8 R .177(uilt database.)-.2 F .177
-(Second, it locks the database source \214le during the reb)5.177 F .176
-(uild \212 b)-.2 F .176(ut that)-.2 F .812(may not w)117 409.8 R .812(ork o)-.1
-F -.15(ve)-.15 G 3.312(rN).15 G .812(FS or if the \214le is unwritable.)205.388
-409.8 R .813(Third, at the end of the reb)5.813 F .813(uild it adds an)-.2 F
-(alias of the form)117 421.8 Q(@: @)157 438 Q .336(\(which is not normally le)
-117 454.2 R -.05(ga)-.15 G 2.836(l\). Before).05 F F2(sendmail)2.836 E F1 .336
-(will access the database, it checks to insure that)2.836 F(this entry e)117
-468.2 Q(xists)-.15 E/F3 7/Times-Roman@0 SF(9)179.63 464.2 Q F1(.)183.13 468.2 Q
-F0 2.5(2.5.3. List)102 492.2 R -.1(ow)2.5 G(ners).1 E F1 .4
-(If an error occurs on sending to a certain address, say \231)142 508.4 R F2(x)
-A F1<9a2c>A F2(sendmail)2.901 E F1 .401(will look for an alias)2.901 F .418
-(of the form \231o)117 520.4 R(wner)-.25 E(-)-.2 E F2(x)A F1 2.918<9a74>C 2.918
-(or)212.632 520.4 S(ecei)223.88 520.4 Q .718 -.15(ve t)-.25 H .418(he errors.)
-.15 F .417(This is typically useful for a mailing list where the)5.418 F 1.116
-(submitter of the list has no control o)117 532.4 R -.15(ve)-.15 G 3.617(rt).15
-G 1.117(he maintenance of the list itself; in this case the list)288.4 532.4 R
-(maintainer w)117 544.4 Q(ould be the o)-.1 E(wner of the list.)-.25 E -.15(Fo)
-5 G 2.5(re).15 G(xample:)309.38 544.4 Q
-(unix-wizards: eric@ucbarpa, wnj@monet, nosuchuser)157 560.6 Q(,)-.4 E
-(sam@matisse)193 572.6 Q -.25(ow)157 584.6 S(ner).25 E
-(-unix-wizards: unix-wizards-request)-.2 E(unix-wizards-request: eric@ucbarpa)
-157 596.6 Q -.1(wo)117 612.8 S 1.959(uld cause \231eric@ucbarpa\232 to get the\
- error that will occur when someone sends to unix-).1 F
-(wizards due to the inclusion of \231nosuchuser\232 on the list.)117 624.8 Q
-.958(List o)142 641 R .958(wners also cause the en)-.25 F -.15(ve)-.4 G .959
-(lope sender address to be modi\214ed.).15 F .959(The contents of the)5.959 F
--.25(ow)117 653 S .429(ner alias are used if the).25 F 2.929(yp)-.15 G .429
-(oint to a single user)236.364 653 R 2.928(,o)-.4 G .428
-(therwise the name of the alias itself is used.)326.436 653 R -.15(Fo)117 665 S
-3.454(rt).15 G .954(his reason, and to obe)136.974 665 R 3.454(yI)-.15 G .954
-(nternet con)239.354 665 R -.15(ve)-.4 G .954(ntions, the \231o).15 F(wner)-.25
-E .955(-\232 address normally points at the)-.2 F .504(\231-request\232 addres\
-s; this causes messages to go out with the typical Internet con)117 677 R -.15
-(ve)-.4 G .503(ntion of using).15 F .32 LW 76 686.6 72 686.6 DL 80 686.6 76
-686.6 DL 84 686.6 80 686.6 DL 88 686.6 84 686.6 DL 92 686.6 88 686.6 DL 96
-686.6 92 686.6 DL 100 686.6 96 686.6 DL 104 686.6 100 686.6 DL 108 686.6 104
-686.6 DL 112 686.6 108 686.6 DL 116 686.6 112 686.6 DL 120 686.6 116 686.6 DL
-124 686.6 120 686.6 DL 128 686.6 124 686.6 DL 132 686.6 128 686.6 DL 136 686.6
-132 686.6 DL 140 686.6 136 686.6 DL 144 686.6 140 686.6 DL 148 686.6 144 686.6
-DL 152 686.6 148 686.6 DL 156 686.6 152 686.6 DL 160 686.6 156 686.6 DL 164
-686.6 160 686.6 DL 168 686.6 164 686.6 DL 172 686.6 168 686.6 DL 176 686.6 172
-686.6 DL 180 686.6 176 686.6 DL 184 686.6 180 686.6 DL 188 686.6 184 686.6 DL
-192 686.6 188 686.6 DL 196 686.6 192 686.6 DL 200 686.6 196 686.6 DL 204 686.6
-200 686.6 DL 208 686.6 204 686.6 DL 212 686.6 208 686.6 DL 216 686.6 212 686.6
-DL/F4 5/Times-Roman@0 SF(9)93.6 697 Q/F5 8/Times-Roman@0 SF(The)3.2 I/F6 8
-/Times-Bold@0 SF(AliasW)2 E(ait)-.52 E F5
-(option is required in the con\214guration for this action to occur)2 E 4(.T)
--.44 G(his should normally be speci\214ed.)352.228 700.2 Q EP
-%%Page: 17 12
-%%BeginPageSetup
-BP
-%%EndPageSetup
-/F0 10/Times-Bold@0 SF(Sendmail Installation and Operation Guide)72 60 Q
-(SMM:08-17)452.9 60 Q/F1 10/Times-Roman@0 SF -.74(``)117 96 S/F2 10
-/Times-Italic@0 SF(list).74 E F1(-request')A 2.5('a)-.74 G 2.5(st)180.22 96 S
-(he return address.)189.39 96 Q F0 2.5(2.6. User)87 120 R(Inf)2.5 E
-(ormation Database)-.25 E F1 1.059(If you ha)127 136.2 R 1.359 -.15(ve a ve)-.2
-H 1.059(rsion of).15 F F2(sendmail)3.559 E F1 1.06
-(with the user information database compiled in, and you)3.559 F(ha)102 148.2 Q
-2.206 -.15(ve s)-.2 H 1.906(peci\214ed one or more databases using the).15 F F0
-(U)4.406 E F1 1.905(option, the databases will be searched for a)4.406 F F2
-(user)102 160.2 Q F1(:maildrop entry)A 5(.I)-.65 G 2.5(ff)191.34 160.2 S
-(ound, the mail will be sent to the speci\214ed address.)200.5 160.2 Q F0 2.5
-(2.7. P)87 184.2 R(er)-.2 E(-User F)-.37 E(orwarding \(.f)-.25 E
-(orward Files\))-.25 E F1 .12(As an alternati)127 200.4 R .42 -.15(ve t)-.25 H
-2.62(ot).15 G .12(he alias database, an)210.4 200.4 R 2.62(yu)-.15 G .121
-(ser may put a \214le with the name \231.forw)304.87 200.4 R .121
-(ard\232 in his)-.1 F .205(or her home directory)102 212.4 R 5.205(.I)-.65 G
-2.705(ft)199.92 212.4 S .205(his \214le e)208.735 212.4 R(xists,)-.15 E F2
-(sendmail)2.705 E F1 .205
-(redirects mail for that user to the list of addresses)2.705 F .908
-(listed in the .forw)102 224.4 R .908(ard \214le.)-.1 F -.15(Fo)5.908 G 3.408
-(re).15 G .908
-(xample, if the home directory for user \231mckusick\232 has a .forw)233.978
-224.4 R(ard)-.1 E(\214le with contents:)102 236.4 Q(mckusick@ernie)142 252.6 Q
-(kirk@calder)142 264.6 Q(then an)102 280.8 Q 2.5(ym)-.15 G(ail arri)146.29
-280.8 Q
-(ving for \231mckusick\232 will be redirected to the speci\214ed accounts.)-.25
-E(Actually)127 297 Q 3.375(,t)-.65 G .874
-(he con\214guration \214le de\214nes a sequence of \214lenames to check.)
-169.445 297 R .874(By def)5.874 F .874(ault, this is)-.1 F .687(the user')102
-309 R 3.187(s.)-.55 G(forw)146.424 309 Q .687(ard \214le, b)-.1 F .687
-(ut can be de\214ned to be more generally using the)-.2 F F0(J)3.187 E F1 3.188
-(option. If)3.188 F .688(you change)3.188 F .393(this, you will ha)102 321 R
-.693 -.15(ve t)-.2 H 2.893(oi).15 G .393
-(nform your user base of the change; .forw)193.065 321 R .393
-(ard is pretty well incorporated into)-.1 F(the collecti)102 333 Q .3 -.15
-(ve s)-.25 H(ubconscious.).15 E F0 2.5(2.8. Special)87 357 R(Header Lines)2.5 E
-F1(Se)127 373.2 Q -.15(ve)-.25 G 1.897(ral header lines ha).15 F 2.197 -.15
-(ve s)-.2 H 1.897
-(pecial interpretations de\214ned by the con\214guration \214le.).15 F(Others)
-6.898 E(ha)102 385.2 Q 1.206 -.15(ve i)-.2 H .906(nterpretations b).15 F .906
-(uilt into)-.2 F F2(sendmail)3.406 E F1 .905
-(that cannot be changed without changing the code.)3.406 F(These)5.905 E -.2
-(bu)102 397.2 S(iltins are described here.).2 E F0 2.5(2.8.1. Err)102 421.2 R
-(ors-T)-.18 E(o:)-.92 E F1 .22(If errors occur an)142 437.4 R .22
-(ywhere during processing, this header will cause error messages to go to)-.15
-F(the listed addresses.)117 449.4 Q(This is intended for mailing lists.)5 E
-.385(The Errors-T)142 465.6 R .385(o: header w)-.8 F .384
-(as created in the bad old days when UUCP didn')-.1 F 2.884(tu)-.18 G .384
-(nderstand the)450.016 465.6 R .889(distinction between an en)117 477.6 R -.15
-(ve)-.4 G .889(lope and a header; this w).15 F .889(as a hack to pro)-.1 F .89
-(vide what should no)-.15 F 3.39(wb)-.25 G(e)499.56 477.6 Q .81
-(passed as the en)117 489.6 R -.15(ve)-.4 G .81(lope sender address.).15 F .809
-(It should go a)5.81 F -.1(wa)-.15 G 4.609 -.65(y. I).1 H 3.309(ti).65 G 3.309
-(so)374.125 489.6 S .809(nly used if the)386.324 489.6 R F0(UseErr)3.309 E
-(orsT)-.18 E(o)-.92 E F1(option is set.)117 501.6 Q(The Errors-T)142 517.8 Q
-(o: header is of)-.8 E(\214cial deprecated and will go a)-.25 E -.1(wa)-.15 G
-2.5(yi).1 G 2.5(naf)392.3 517.8 S(uture release.)410.07 517.8 Q F0 2.5
-(2.8.2. A)102 541.8 R(ppar)-.25 E(ently-T)-.18 E(o:)-.92 E F1 .044
-(RFC 822 requires at least one recipient \214eld \(T)142 558 R .045
-(o:, Cc:, or Bcc: line\) in e)-.8 F -.15(ve)-.25 G .045(ry message.).15 F .045
-(If a)5.045 F .562
-(message comes in with no recipients listed in the message then)117 570 R F2
-(sendmail)3.062 E F1 .562(will adjust the header)3.062 F .085
-(based on the \231NoRecipientAction\232 option.)117 582 R .085
-(One of the possible actions is to add an \231)5.085 F(Apparently-)-.8 E -.8
-(To)117 594 S .08(:\232 header line for an).8 F 2.58(yr)-.15 G .08
-(ecipients it is a)218.36 594 R -.1(wa)-.15 G .08(re of.).1 F .08
-(This is not put in as a standard recipient line to)5.08 F -.1(wa)117 606 S
-(rn an).1 E 2.5(yr)-.15 G(ecipients that the list is not complete.)159.51 606 Q
-(The Apparently-T)142 622.2 Q(o: header is non-standard and is deprecated.)-.8
-E F0 2.5(2.8.3. Pr)102 646.2 R(ecedence)-.18 E F1 .425
-(The Precedence: header can be used as a crude control of message priority)142
-662.4 R 5.425(.I)-.65 G 2.925(tt)455.38 662.4 S .425(weaks the)463.865 662.4 R
-(sort order in the queue and can be con\214gured to change the message timeout\
- v)117 674.4 Q(alues.)-.25 E EP
-%%Page: 18 13
-%%BeginPageSetup
-BP
-%%EndPageSetup
-/F0 10/Times-Bold@0 SF 193.36(SMM:08-18 Sendmail)72 60 R
-(Installation and Operation Guide)2.5 E 2.5(2.9. IDENT)87 96 R(Pr)2.5 E
-(otocol Support)-.18 E/F1 10/Times-Italic@0 SF(Sendmail)127 112.2 Q/F2 10
-/Times-Roman@0 SF 1.835(supports the IDENT protocol as de\214ned in RFC 1413.)
-4.335 F 1.835(Although this enhances)6.835 F .289
-(identi\214cation of the author of an email message by doing a `)102 124.2 R
-.29(`call back')-.74 F 2.79('t)-.74 G 2.79(ot)396.17 124.2 S .29
-(he originating system to)406.74 124.2 R .469(include the o)102 136.2 R .469(w\
-ner of a particular TCP connection in the audit trail it is in no sense perfec\
-t; a deter)-.25 F(-)-.2 E 1.293(mined for)102 148.2 R 1.294
-(ger can easily spoof the IDENT protocol.)-.18 F 1.294(The follo)6.294 F 1.294
-(wing description is e)-.25 F 1.294(xcerpted from)-.15 F(RFC 1413:)102 160.2 Q
-2.5(6. Security)127 176.4 R(Considerations)2.5 E .006
-(The information returned by this protocol is at most as trustw)127 192.6 R
-(orth)-.1 E 2.505(ya)-.05 G 2.505(st)400.505 192.6 S .005(he host pro)409.68
-192.6 R .005(viding it OR)-.15 F .273(the or)127 204.6 R -.05(ga)-.18 G .273
-(nization operating the host.).05 F -.15(Fo)5.273 G 2.773(re).15 G .274
-(xample, a PC in an open lab has fe)295.308 204.6 R 2.774(wi)-.25 G 2.774(fa)
-448.612 204.6 S .574 -.15(ny c)459.156 204.6 T(ontrols).15 E .987(on it to pre)
-127 216.6 R -.15(ve)-.25 G .986(nt a user from ha).15 F .986
-(ving this protocol return an)-.2 F 3.486(yi)-.15 G .986
-(denti\214er the user w)378.056 216.6 R 3.486(ants. Lik)-.1 F(e-)-.1 E 1.441(w\
-ise, if the host has been compromised the information returned may be complete\
-ly erro-)127 228.6 R(neous and misleading.)127 240.6 Q .521(The Identi\214cati\
-on Protocol is not intended as an authorization or access control protocol.)127
-256.8 R(At)5.52 E 1.036(best, it pro)127 268.8 R 1.037
-(vides some additional auditing information with respect to TCP connections.)
--.15 F(At)6.037 E -.1(wo)127 280.8 S(rst, it can pro).1 E
-(vide misleading, incorrect, or maliciously incorrect information.)-.15 E 1.006
-(The use of the information returned by this protocol for other than auditing \
-is strongly dis-)127 297 R 2.697(couraged. Speci\214cally)127 309 R 2.697(,u)
--.65 G .197(sing Identi\214cation Protocol information to mak)228.114 309 R
-2.697(ea)-.1 G .197(ccess control deci-)429.186 309 R .514(sions - either as t\
-he primary method \(i.e., no other checks\) or as an adjunct to other methods)
-127 321 R(may result in a weak)127 333 Q(ening of normal host security)-.1 E(.)
--.65 E 1.778(An Identi\214cation serv)127 349.2 R 1.778(er may re)-.15 F -.15
-(ve)-.25 G 1.778(al information about users, entities, objects or processes).15
-F .337(which might normally be considered pri)127 361.2 R -.25(va)-.25 G 2.836
-(te. An).25 F .336(Identi\214cation serv)2.836 F .336(er pro)-.15 F .336
-(vides service which)-.15 F .806
-(is a rough analog of the CallerID services pro)127 373.2 R .806
-(vided by some phone companies and man)-.15 F 3.306(yo)-.15 G(f)500.67 373.2 Q
-1.398(the same pri)127 385.2 R -.25(va)-.25 G 1.698 -.15(cy c).25 H 1.398
-(onsiderations and ar).15 F 1.398
-(guments that apply to the CallerID service apply to)-.18 F 3.545
-(Identi\214cation. If)127 397.2 R 1.045(you w)3.545 F(ouldn')-.1 E 3.545(tr)
--.18 G 1.045(un a "\214nger" serv)260.33 397.2 R 1.046(er due to pri)-.15 F
--.25(va)-.25 G 1.346 -.15(cy c).25 H 1.046(onsiderations you may).15 F(not w)
-127 409.2 Q(ant to run this protocol.)-.1 E .377
-(In some cases your system may not w)102 425.4 R .377
-(ork properly with IDENT support due to a b)-.1 F .376(ug in the TCP/IP)-.2 F
-3.675(implementation. The)102 437.4 R 1.175
-(symptoms will be that for some hosts the SMTP connection will be closed)3.675
-F .566(almost immediately)102 449.4 R 5.566(.I)-.65 G 3.066(ft)192.482 449.4 S
-.565(his is true or if you do not w)201.658 449.4 R .565(ant to use IDENT)-.1 F
-3.065(,y)-.74 G .565(ou should set the IDENT)401.75 449.4 R
-(timeout to zero; this will disable the IDENT protocol.)102 461.4 Q F0 2.5
-(3. ARGUMENTS)72 485.4 R F2 .017(The complete list of ar)112 501.6 R .017
-(guments to)-.18 F F1(sendmail)2.517 E F2 .017
-(is described in detail in Appendix A.)2.517 F .018(Some important)5.018 F(ar)
-87 513.6 Q(guments are described here.)-.18 E F0 2.5(3.1. Queue)87 537.6 R
-(Inter)2.5 E -.1(va)-.1 G(l).1 E F2 .455(The amount of time between forking a \
-process to run through the queue is de\214ned by the)127 553.8 R F0<ad71>2.955
-E F2 2.675(\215ag. If)102 565.8 R .175(you run with deli)2.675 F -.15(ve)-.25 G
-.175(ry mode set to).15 F F0(i)2.675 E F2(or)2.675 E F0(b)2.675 E F2 .176
-(this can be relati)2.675 F -.15(ve)-.25 G .176(ly lar).15 F .176
-(ge, since it will only be rel-)-.18 F -.25(eva)102 577.8 S .207
-(nt when a host that w).25 F .207(as do)-.1 F .207(wn comes back up.)-.25 F
-.206(If you run in)5.207 F F0(q)2.706 E F2 .206(mode it should be relati)2.706
-F -.15(ve)-.25 G .206(ly short,).15 F 1.039(since it de\214nes the maximum amo\
-unt of time that a message may sit in the queue.)102 589.8 R 1.039
-(\(See also the)6.039 F(MinQueueAge option.\))102 601.8 Q 1.336
-(RFC 1123 section 5.3.1.1 says that this v)127 618 R 1.335
-(alue should be at least 30 minutes \(although that)-.25 F(probably doesn')102
-630 Q 2.5(tm)-.18 G(ak)179.59 630 Q 2.5(es)-.1 G(ense if you use `)199.76 630 Q
-(`queue-only')-.74 E 2.5('m)-.74 G(ode\).)329.08 630 Q F0 2.5(3.2. Daemon)87
-654 R(Mode)2.5 E F2 .084(If you allo)127 670.2 R 2.584(wi)-.25 G .084
-(ncoming mail o)181.162 670.2 R -.15(ve)-.15 G 2.585(ra).15 G 2.585(nI)263.605
-670.2 S .085(PC connection, you should ha)274.52 670.2 R .385 -.15(ve a d)-.2 H
-.085(aemon running.).15 F(This)5.085 E .07(should be set by your)102 682.2 R F1
-(/etc/r)2.57 E(c)-.37 E F2 .07(\214le using the)2.57 F F0(\255bd)2.57 E F2
-2.569(\215ag. The)2.57 F F0(\255bd)2.569 E F2 .069(\215ag and the)2.569 F F0
-<ad71>2.569 E F2 .069(\215ag may be combined)2.569 F(in one call:)102 694.2 Q
-(/usr/sbin/sendmail \255bd \255q30m)142 710.4 Q EP
-%%Page: 19 14
-%%BeginPageSetup
-BP
-%%EndPageSetup
-/F0 10/Times-Bold@0 SF(Sendmail Installation and Operation Guide)72 60 Q
-(SMM:08-19)452.9 60 Q/F1 10/Times-Roman@0 SF .292(An alternati)127 96 R .592
--.15(ve a)-.25 H .292(pproach is to in).15 F -.2(vo)-.4 G .493 -.1(ke s).2 H
-.293(endmail from).1 F/F2 10/Times-Italic@0 SF(inetd)2.793 E F1 .293
-(\(8\) \(use the)B F0(\255bs)2.793 E F1 .293(\215ag to ask sendmail)2.793 F
-.255(to speak SMTP on its standard input and output\).)102 108 R .255(This w)
-5.255 F .255(orks and allo)-.1 F .255(ws you to wrap)-.25 F F2(sendmail)2.755 E
-F1 .255(in a)2.755 F 1.39(TCP wrapper program, b)102 120 R 1.39
-(ut may be a bit slo)-.2 F 1.39
-(wer since the con\214guration \214le has to be re-read on)-.25 F -2.15 -.25
-(ev e)102 132 T .556(ry message that comes in.).25 F .556
-(If you do this, you still need to ha)5.556 F .856 -.15(ve a)-.2 H F2(sendmail)
-3.206 E F1 .555(running to \215ush the)3.055 F(queue:)102 144 Q
-(/usr/sbin/sendmail \255q30m)142 160.2 Q F0 2.5(3.3. F)87 188.4 R(or)-.25 E
-(cing the Queue)-.18 E F1 .04(In some cases you may \214nd that the queue has \
-gotten clogged for some reason.)127 204.6 R -1.1(Yo)5.04 G 2.54(uc)1.1 G .04
-(an force)471.48 204.6 R 3.185(aq)102 216.6 S .685(ueue run using the)114.625
-216.6 R F0<ad71>3.184 E F1 .684(\215ag \(with no v)3.184 F 3.184(alue\). It)
--.25 F .684(is entertaining to use the)3.184 F F0<ad76>3.184 E F1 .684
-(\215ag \(v)3.184 F .684(erbose\) when)-.15 F(this is done to w)102 228.6 Q
-(atch what happens:)-.1 E(/usr/sbin/sendmail \255q \255v)142 244.8 Q -1.1(Yo)
-127 265.2 S 4.004(uc)1.1 G 1.504
-(an also limit the jobs to those with a particular queue identi\214er)151.564
-265.2 R 4.004(,s)-.4 G(ender)428.362 265.2 Q 4.004(,o)-.4 G 4.004(rr)461.676
-265.2 S(ecipient)472.34 265.2 Q .687(using one of the queue modi\214ers.)102
-277.2 R -.15(Fo)5.687 G 3.187(re).15 G .687(xample, \231\255qRberk)265.659
-277.2 R(ele)-.1 E .686(y\232 restricts the queue run to jobs that)-.15 F(ha)102
-289.2 Q .525 -.15(ve t)-.2 H .225(he string \231berk).15 F(ele)-.1 E .225
-(y\232 some)-.15 F .225(where in one of the recipient addresses.)-.25 F
-(Similarly)5.226 E 2.726<2c99>-.65 G .226(\255qSstring\232 lim-)441.184 289.2 R
-(its the run to particular senders and \231\255qIstring\232 limits it to parti\
-cular queue identi\214ers.)102 301.2 Q F0 2.5(3.4. Deb)87 325.2 R(ugging)-.2 E
-F1 1.365(There are a f)127 341.4 R 1.365(airly lar)-.1 F 1.365
-(ge number of deb)-.18 F 1.365(ug \215ags b)-.2 F 1.365(uilt into)-.2 F F2
-(sendmail)3.865 E F1 6.365(.E)C 1.365(ach deb)417.65 341.4 R 1.365
-(ug \215ag has a)-.2 F 1.116(number and a le)102 353.4 R -.15(ve)-.25 G 1.116
-(l, where higher le).15 F -.15(ve)-.25 G 1.116
-(ls means to print out more information.).15 F 1.116(The con)6.116 F -.15(ve)
--.4 G 1.116(ntion is).15 F .294(that le)102 365.4 R -.15(ve)-.25 G .294
-(ls greater than nine are \231absurd,).15 F 2.794<9a69>-.7 G .294(.e., the)
-274.018 365.4 R 2.794(yp)-.15 G .293(rint out so much information that you w)
-313.616 365.4 R(ouldn')-.1 E(t)-.18 E .691(normally w)102 377.4 R .692
-(ant to see them e)-.1 F .692(xcept for deb)-.15 F .692
-(ugging that particular piece of code.)-.2 F(Deb)5.692 E .692
-(ug \215ags are set)-.2 F(using the)102 389.4 Q F0<ad64>2.5 E F1
-(option; the syntax is:)2.5 E(deb)142 405.6 Q(ug-\215ag:)-.2 E F0<ad64>200.13
-405.6 Q F1(deb)2.5 E(ug-list)-.2 E(deb)142 417.6 Q 13.05(ug-list: deb)-.2 F
-(ug-option [ , deb)-.2 E(ug-option ]*)-.2 E(deb)142 429.6 Q -.28
-(ug-option: deb)-.2 F(ug-range [ . deb)-.2 E(ug-le)-.2 E -.15(ve)-.25 G 2.5(l])
-.15 G(deb)142 441.6 Q 3.07(ug-range: inte)-.2 F(ger | inte)-.15 E
-(ger \255 inte)-.15 E(ger)-.15 E(deb)142 453.6 Q(ug-le)-.2 E -.15(ve)-.25 G
-6.24(l: inte).15 F(ger)-.15 E(where spaces are for reading ease only)102 469.8
-Q 5(.F)-.65 G(or e)268.64 469.8 Q(xample,)-.15 E 34.99(\255d12 Set)142 486 R
-(\215ag 12 to le)2.5 E -.15(ve)-.25 G 2.5(l1).15 G 27.49(\255d12.3 Set)142 498
-R(\215ag 12 to le)2.5 E -.15(ve)-.25 G 2.5(l3).15 G 24.35(\255d3\25517 Set)142
-510 R(\215ags 3 through 17 to le)2.5 E -.15(ve)-.25 G 2.5(l1).15 G 16.85
-(\255d3\25517.4 Set)142 522 R(\215ags 3 through 17 to le)2.5 E -.15(ve)-.25 G
-2.5(l4).15 G -.15(Fo)102 538.2 S 4.066(rac).15 G 1.566(omplete list of the a)
-132.752 538.2 R -.25(va)-.2 G 1.565(ilable deb).25 F 1.565
-(ug \215ags you will ha)-.2 F 1.865 -.15(ve t)-.2 H 4.065(ol).15 G 1.565
-(ook at the code \(the)380.9 538.2 R 4.065(ya)-.15 G 1.565(re too)479.385 538.2
-R(dynamic to k)102 550.2 Q(eep this documentation up to date\).)-.1 E F0 2.5
-(3.5. Changing)87 574.2 R(the V)2.5 E(alues of Options)-.92 E F1
-(Options can be o)127 590.4 Q -.15(ve)-.15 G(rridden using the).15 E F0<ad6f>
-2.5 E F1(or)2.5 E F0<ad4f>2.5 E F1(command line \215ags.)2.5 E -.15(Fo)5 G 2.5
-(re).15 G(xample,)420.27 590.4 Q(/usr/sbin/sendmail \255oT2m)142 606.6 Q .02
-(sets the)102 622.8 R F0(T)2.52 E F1 .02(\(timeout\) option to tw)2.52 F 2.52
-(om)-.1 G .021(inutes for this run only; the equi)246.77 622.8 R -.25(va)-.25 G
-.021(lent line using the long option).25 F(name is)102 634.8 Q
-(/usr/sbin/sendmail -OQueueT)142 651 Q(imeout=2m)-.35 E .72(Some options ha)127
-671.4 R 1.02 -.15(ve s)-.2 H .72(ecurity implications.).15 F .72(Sendmail allo)
-5.72 F .72(ws you to set these, b)-.25 F .72(ut relinquishes)-.2 F EP
-%%Page: 20 15
-%%BeginPageSetup
-BP
-%%EndPageSetup
-/F0 10/Times-Bold@0 SF 193.36(SMM:08-20 Sendmail)72 60 R
-(Installation and Operation Guide)2.5 E/F1 10/Times-Roman@0 SF
-(its setuid root permissions thereafter)102 98 Q/F2 7/Times-Roman@0 SF(10)
-247.54 94 Q F1(.)254.54 98 Q F0 2.5(3.6. T)87 122 R(rying a Differ)-.74 E
-(ent Con\214guration File)-.18 E F1(An alternati)127 138.2 Q .3 -.15(ve c)-.25
-H(on\214guration \214le can be speci\214ed using the).15 E F0<ad43>2.5 E F1
-(\215ag; for e)2.5 E(xample,)-.15 E
-(/usr/sbin/sendmail \255Ctest.cf \255oQ/tmp/mqueue)142 154.4 Q .429
-(uses the con\214guration \214le)102 170.6 R/F3 10/Times-Italic@0 SF(test.cf)
-2.928 E F1 .428(instead of the def)2.928 F(ault)-.1 E F3(/etc/sendmail.cf)2.928
-E(.)-.15 E F1 .428(If the)5.428 F F0<ad43>2.928 E F1 .428(\215ag has no v)2.928
-F(alue)-.25 E(it def)102 182.6 Q(aults to)-.1 E F3(sendmail.cf)2.5 E F1
-(in the current directory)2.5 E(.)-.65 E F3(Sendmail)127 198.8 Q F1(gi)2.679 E
--.15(ve)-.25 G 2.679(su).15 G 2.679(pi)195.288 198.8 S .18
-(ts setuid root permissions when you use this \215ag, so it is common to use a)
-205.747 198.8 R .069(publicly writable directory \(such as /tmp\) as the spool\
- directory \(QueueDirectory or Q option\) while)102 210.8 R(testing.)102 222.8
-Q F0 2.5(3.7. Logging)87 246.8 R -.74(Tr)2.5 G(af\214c).74 E F1(Man)127 263 Q
-3.254(yS)-.15 G .754(MTP implementations do not fully implement the protocol.)
-158.994 263 R -.15(Fo)5.754 G 3.254(re).15 G .755(xample, some per)428.54 263 R
-(-)-.2 E 1.178(sonal computer based SMTPs do not understand continuation lines\
- in reply codes.)102 275 R 1.177(These can be)6.178 F -.15(ve)102 287 S .13
-(ry hard to trace.).15 F .13(If you suspect such a problem, you can set traf)
-5.13 F .13(\214c logging using the)-.25 F F0<ad58>2.63 E F1 2.63(\215ag. F)2.63
-F(or)-.15 E -.15(ex)102 299 S(ample,).15 E(/usr/sbin/sendmail \255X /tmp/traf)
-142 315.2 Q(\214c \255bd)-.25 E(will log all traf)102 331.4 Q
-(\214c in the \214le)-.25 E F3(/tmp/tr)2.5 E(af)-.15 E<8c63>-.18 E F1(.)A .998
-(This logs a lot of data v)127 347.6 R .997(ery quickly and should)-.15 F F0
-(NEVER)3.497 E F1 .997(be used during normal operations.)3.497 F .962(After st\
-arting up such a daemon, force the errant implementation to send a message to \
-your host.)102 359.6 R .609(All message traf)102 371.6 R .609
-(\214c in and out of)-.25 F F3(sendmail)3.109 E F1 3.109(,i)C .609
-(ncluding the incoming SMTP traf)281.882 371.6 R .608(\214c, will be logged in)
--.25 F(this \214le.)102 383.6 Q F0 2.5(3.8. T)87 407.6 R
-(esting Con\214guration Files)-.92 E F1 .643(When you b)127 423.8 R .644(uild \
-a con\214guration table, you can do a certain amount of testing using the \231\
-test)-.2 F(mode\232 of)102 435.8 Q F3(sendmail)2.5 E F1 5(.F)C(or e)191.01
-435.8 Q(xample, you could in)-.15 E -.2(vo)-.4 G -.1(ke).2 G F3(sendmail)2.6 E
-F1(as:)2.5 E(sendmail \255bt \255Ctest.cf)142 452 Q .448(which w)102 468.2 R
-.448(ould read the con\214guration \214le \231test.cf\232 and enter test mode.)
--.1 F .447(In this mode, you enter lines)5.447 F(of the form:)102 480.2 Q
-(rwset address)142 496.4 Q(where)102 512.6 Q F3(rwset)3.006 E F1 .506
-(is the re)3.006 F .506(writing set you w)-.25 F .506(ant to use and)-.1 F F3
-(addr)3.007 E(ess)-.37 E F1 .507(is an address to apply the set to.)3.007 F -.7
-(Te)5.507 G(st).7 E .794(mode sho)102 524.6 R .794(ws you the steps it tak)-.25
-F .794(es as it proceeds, \214nally sho)-.1 F .794
-(wing you the address it ends up with.)-.25 F -1.1(Yo)102 536.6 S 3.331(um)1.1
-G .832(ay use a comma separated list of rwsets for sequential application of r\
-ules to an input.)129.231 536.6 R -.15(Fo)5.832 G(r).15 E -.15(ex)102 548.6 S
-(ample:).15 E(3,1,21,4 monet:bollard)142 564.8 Q .622
-(\214rst applies ruleset three to the input \231monet:bollard.)102 581 R 5.622
-<9a52>-.7 G .622(uleset one is then applied to the output of)334.036 581 R
-(ruleset three, follo)102 593 Q(wed similarly by rulesets twenty-one and four)
--.25 E(.)-.55 E 1.084(If you need more detail, you can also use the \231\255d2\
-1\232 \215ag to turn on more deb)127 609.2 R 3.585(ugging. F)-.2 F(or)-.15 E
--.15(ex)102 621.2 S(ample,).15 E(sendmail \255bt \255d21.99)142 637.4 Q .689
-(turns on an incredible amount of information; a single w)102 653.6 R .688
-(ord address is probably going to print out)-.1 F(se)102 665.6 Q -.15(ve)-.25 G
-(ral pages w).15 E(orth of information.)-.1 E .32 LW 76 678.8 72 678.8 DL 80
-678.8 76 678.8 DL 84 678.8 80 678.8 DL 88 678.8 84 678.8 DL 92 678.8 88 678.8
-DL 96 678.8 92 678.8 DL 100 678.8 96 678.8 DL 104 678.8 100 678.8 DL 108 678.8
-104 678.8 DL 112 678.8 108 678.8 DL 116 678.8 112 678.8 DL 120 678.8 116 678.8
-DL 124 678.8 120 678.8 DL 128 678.8 124 678.8 DL 132 678.8 128 678.8 DL 136
-678.8 132 678.8 DL 140 678.8 136 678.8 DL 144 678.8 140 678.8 DL 148 678.8 144
-678.8 DL 152 678.8 148 678.8 DL 156 678.8 152 678.8 DL 160 678.8 156 678.8 DL
-164 678.8 160 678.8 DL 168 678.8 164 678.8 DL 172 678.8 168 678.8 DL 176 678.8
-172 678.8 DL 180 678.8 176 678.8 DL 184 678.8 180 678.8 DL 188 678.8 184 678.8
-DL 192 678.8 188 678.8 DL 196 678.8 192 678.8 DL 200 678.8 196 678.8 DL 204
-678.8 200 678.8 DL 208 678.8 204 678.8 DL 212 678.8 208 678.8 DL 216 678.8 212
-678.8 DL/F4 5/Times-Roman@0 SF(10)93.6 689.2 Q/F5 8/Times-Roman@0 SF .497
-(That is, it sets its ef)3.2 J(fecti)-.2 E .737 -.12(ve u)-.2 H .497
-(id to the real uid; thus, if you are e).12 F -.12(xe)-.12 G .497
-(cuting as root, as from root').12 F 2.497(sc)-.44 G .497
-(rontab \214le or during system)413.572 692.4 R
-(startup the root permissions will still be honored.)72 702 Q EP
-%%Page: 21 16
-%%BeginPageSetup
-BP
-%%EndPageSetup
-/F0 10/Times-Bold@0 SF(Sendmail Installation and Operation Guide)72 60 Q
-(SMM:08-21)452.9 60 Q/F1 10/Times-Roman@0 SF -1.1(Yo)127 96 S 2.574(us)1.1 G
-.074(hould be w)149.584 96 R .074(arned that internally)-.1 F(,)-.65 E/F2 10
-/Times-Italic@0 SF(sendmail)2.575 E F1 .075
-(applies ruleset 3 to all addresses.)2.575 F .075(In test mode)5.075 F
-(you will ha)102 108 Q .3 -.15(ve t)-.2 H 2.5(od).15 G 2.5(ot)173.87 108 S
-(hat manually)184.15 108 Q 5(.F)-.65 G(or e)248.35 108 Q(xample, older v)-.15 E
-(ersions allo)-.15 E(wed you to use)-.25 E 2.5(0b)142 124.2 S
-(ruce@broadcast.son)154.5 124.2 Q -.65(y.)-.15 G(com).65 E(This v)102 140.4 Q
-(ersion requires that you use:)-.15 E(3,0 bruce@broadcast.son)142 156.6 Q -.65
-(y.)-.15 G(com).65 E(As of v)127 177 Q(ersion 8.7, some other syntax)-.15 E
-(es are a)-.15 E -.25(va)-.2 G(ilable in test mode:).25 E 5<832e>107 193.2 S
-1.666(Dxv)118 193.2 S .328(alue de\214nes macro)-1.916 F F2(x)2.828 E F1 .328
-(to ha)2.828 F .628 -.15(ve t)-.2 H .328(he indicated).15 F F2(value)2.828 E F1
-5.328(.T)C .328(his is useful when deb)346.134 193.2 R .327(ugging rules that)
--.2 F(use the)115.5 205.2 Q F0($&)2.5 E F2(x)A F1(syntax.)2.5 E 5<832e>107
-217.2 S 1.666(Ccv)118 217.2 S(alue adds the indicated)-1.916 E F2(value)2.5 E
-F1(to class)2.5 E F2(c)2.5 E F1(.)A 5<832e>107 229.2 S 1.666(Sr)118 229.2 S
-(uleset dumps the contents of the indicated ruleset.)-1.666 E 5<83ad>107 241.2
-S 1.666(dd)121.14 241.2 S(eb)-1.666 E(ug-spec is equi)-.2 E -.25(va)-.25 G
-(lent to the command-line \215ag.).25 E F0 2.5(4. TUNING)72 265.2 R F1 1.922
-(There are a number of con\214guration parameters you may w)112 281.4 R 1.922
-(ant to change, depending on the)-.1 F .367(requirements of your site.)87 293.4
-R .366(Most of these are set using an option in the con\214guration \214le.)
-5.367 F -.15(Fo)5.366 G 2.866(re).15 G(xample,)472.06 293.4 Q(the line \231O T)
-87 305.4 Q(imeout.queuereturn=5d\232 sets option \231T)-.35 E
-(imeout.queuereturn\232 to the v)-.35 E(alue \2315d\232 \(\214v)-.25 E 2.5(ed)
--.15 G(ays\).)476.47 305.4 Q .735(Most of these options ha)112 321.6 R 1.035
--.15(ve a)-.2 H .735(ppropriate def).15 F .735(aults for most sites.)-.1 F(Ho)
-5.735 E(we)-.25 E -.15(ve)-.25 G 1.535 -.4(r, s).15 H .735(ites ha).4 F .735
-(ving v)-.2 F .735(ery high)-.15 F .046(mail loads may \214nd the)87 333.6 R
-2.546(yn)-.15 G .046(eed to tune them as appropriate for their mail load.)
-193.47 333.6 R .045(In particular)5.045 F 2.545(,s)-.4 G .045(ites e)459.395
-333.6 R(xperi-)-.15 E 1.087(encing a lar)87 345.6 R 1.087
-(ge number of small messages, man)-.18 F 3.587(yo)-.15 G 3.588(fw)294.496 345.6
-S 1.088(hich are deli)308.634 345.6 R -.15(ve)-.25 G 1.088(red to man).15 F
-3.588(yr)-.15 G 1.088(ecipients, may \214nd)425.994 345.6 R(that the)87 357.6 Q
-2.5(yn)-.15 G(eed to adjust the parameters dealing with queue priorities.)
-129.07 357.6 Q .524(All v)112 373.8 R .524(ersions of)-.15 F F2(sendmail)3.024
-E F1 .524(prior to 8.7 had single character option names.)3.024 F .523
-(As of 8.7, options ha)5.524 F -.15(ve)-.2 G 1.215
-(long \(multi-character names\).)87 385.8 R 1.216
-(Although old short names are still accepted, most ne)6.215 F 3.716(wo)-.25 G
-1.216(ptions do not)449.338 385.8 R(ha)87 397.8 Q .3 -.15(ve s)-.2 H(hort equi)
-.15 E -.25(va)-.25 G(lents.).25 E .802
-(This section only describes the options you are most lik)112 414 R .802
-(ely to w)-.1 F .801(ant to tweak; read section 5 for)-.1 F(more details.)87
-426 Q F0 2.5(4.1. T)87 450 R(imeouts)-.18 E F1 .582(All time interv)127 466.2 R
-.583(als are set using a scaled syntax.)-.25 F -.15(Fo)5.583 G 3.083(re).15 G
-.583(xample, \23110m\232 represents ten minutes,)346.138 466.2 R
-(whereas \2312h30m\232 represents tw)102 478.2 Q 2.5(oa)-.1 G(nd a half hours.)
-241.3 478.2 Q(The full set of scales is:)5 E 16.11(ss)142 494.4 S(econds)165.89
-494.4 Q 12.22(mm)142 506.4 S(inutes)169.78 506.4 Q 15(hh)142 518.4 S(ours)167
-518.4 Q 15(dd)142 530.4 S(ays)167 530.4 Q 12.78(ww)142 542.4 S(eeks)169.22
-542.4 Q F0 2.5(4.1.1. Queue)102 570.6 R(inter)2.5 E -.1(va)-.1 G(l).1 E F1 .18
-(The ar)142 586.8 R .18(gument to the)-.18 F F0<ad71>2.68 E F1 .18
-(\215ag speci\214es ho)2.68 F 2.68(wo)-.25 G .18
-(ften a sub-daemon will run the queue.)319.25 586.8 R .18(This is)5.18 F .967
-(typically set to between \214fteen minutes and one hour)117 598.8 R 5.968(.R)
--.55 G .968(FC 1123 section 5.3.1.1 recommends)350.968 598.8 R
-(that this be at least 30 minutes.)117 610.8 Q F0 2.5(4.1.2. Read)102 634.8 R
-(timeouts)2.5 E F1 -.35(Ti)142 651 S 1.053(meouts all ha).35 F 1.352 -.15(ve o)
--.2 H 1.052(ption names \231T).15 F(imeout.)-.35 E F2(suboption)A F1 3.552
-(\232. The)B(recognized)3.552 E F2(suboption)3.552 E F1 1.052(s, their)B(def)
-117 663 Q(ault v)-.1 E(alues, and the minimum v)-.25 E(alues allo)-.25 E
-(wed by RFC 1123 section 5.3.2 are:)-.25 E 38.4(connect The)117 679.2 R .16
-(time to w)2.66 F .161(ait for an SMTP connection to open \(the)-.1 F F2
-(connect)2.661 E F1 .161(\(2\) system call\))B 1.154([0, unspeci\214ed].)189
-691.2 R 1.153(If zero, uses the k)6.153 F 1.153(ernel def)-.1 F 3.653(ault. In)
--.1 F 1.153(no case can this option)3.653 F -.15(ex)189 703.2 S .518
-(tend the timeout longer than the k).15 F .518(ernel pro)-.1 F .519(vides, b)
--.15 F .519(ut it can shorten it.)-.2 F(This)5.519 E .58(is to get around k)189
-715.2 R .579(ernels that pro)-.1 F .579
-(vide an absurdly long connection timeout \(90)-.15 F EP
-%%Page: 22 17
-%%BeginPageSetup
-BP
-%%EndPageSetup
-/F0 10/Times-Bold@0 SF 193.36(SMM:08-22 Sendmail)72 60 R
-(Installation and Operation Guide)2.5 E/F1 10/Times-Roman@0 SF
-(minutes in one case\).)189 96 Q 46.16(initial The)117 112.2 R -.1(wa)2.5 G
-(it for the initial 220 greeting message [5m, 5m].).1 E 52.28(helo The)117
-128.4 R -.1(wa)4.226 G 1.727
-(it for a reply from a HELO or EHLO command [5m, unspeci\214ed].).1 F .1
-(This may require a host name lookup, so \214v)189 140.4 R 2.6(em)-.15 G .1
-(inutes is probably a reasonable)380.29 140.4 R(minimum.)189 152.4 Q 46.72
-(mail\207 The)117 168.6 R -.1(wa)2.5 G
-(it for a reply from a MAIL command [10m, 5m].).1 E 48.95(rcpt\207 The)117
-184.8 R -.1(wa)3.481 G .981(it for a reply from a RCPT command [1h, 5m].).1 F
-.982(This should be long)5.982 F 1.556
-(because it could be pointing at a list that tak)189 196.8 R 1.556
-(es a long time to e)-.1 F 1.556(xpand \(see)-.15 F(belo)189 208.8 Q(w\).)-.25
-E 34.5(datainit\207 The)117 225 R -.1(wa)2.5 G(it for a reply from a D).1 E
--1.21 -1.11(AT A)-.4 H(command [5m, 2m].)3.61 E 25.62(datablock\207 The)117
-241.2 R -.1(wa)2.696 G .196
-(it for reading a data block \(that is, the body of the message\).).1 F .196
-([1h, 3m].)5.196 F .621
-(This should be long because it also applies to programs piping input to)189
-253.2 R/F2 10/Times-Italic@0 SF(send-)3.12 E(mail)189 265.2 Q F1(which ha)2.5 E
-.3 -.15(ve n)-.2 H 2.5(og).15 G(uarantee of promptness.)274.75 265.2 Q 30.06
-(data\214nal\207 The)117 281.4 R -.1(wa)2.806 G .306
-(it for a reply from the dot terminating a message.).1 F .306([1h, 10m].)5.306
-F .306(If this is)5.306 F .884
-(shorter than the time actually needed for the recei)189 293.4 R -.15(ve)-.25 G
-3.383(rt).15 G 3.383(od)412.881 293.4 S(eli)426.264 293.4 Q -.15(ve)-.25 G
-3.383(rt).15 G .883(he message,)454.797 293.4 R(duplicates will be generated.)
-189 305.4 Q(This is discussed in RFC 1047.)5 E 55.06(rset The)117 321.6 R -.1
-(wa)2.5 G(it for a reply from a RSET command [5m, unspeci\214ed].).1 E 53.94
-(quit The)117 337.8 R -.1(wa)2.5 G(it for a reply from a Q).1 E
-(UIT command [2m, unspeci\214ed].)-.1 E 50.61(misc The)117 354 R -.1(wa)2.76 G
-.261(it for a reply from miscellaneous \(b).1 F .261
-(ut short\) commands such as NOOP)-.2 F(\(no-operation\) and VERB \(go into v)
-189 366 Q(erbose mode\).)-.15 E([2m, unspeci\214ed].)5 E 25.06(command\207 In)
-117 382.2 R(serv)2.5 E(er SMTP)-.15 E 2.5(,t)-1.11 G(he time to w)259.4 382.2 Q
-(ait for another command.)-.1 E([1h, 5m].)5 E 49.5(ident The)117 400.4 R
-(timeout w)2.5 E(aiting for a reply to an IDENT query [30s)-.1 E/F3 7
-/Times-Roman@0 SF(11)413.86 396.4 Q F1 2.5(,u)420.86 400.4 S(nspeci\214ed].)
-430.86 400.4 Q -.15(Fo)117 416.6 S 4.609(rc).15 G 2.109
-(ompatibility with old con\214guration \214les, if no)139.789 416.6 R F2
-(suboption)4.608 E F1 2.108(is speci\214ed, all the timeouts)4.608 F(mark)117
-428.6 Q(ed with \207 are set to the indicated v)-.1 E(alue.)-.25 E(Man)142
-444.8 Q 2.5(yo)-.15 G 2.5(ft)172.68 444.8 S(he RFC 1123 minimum v)181.29 444.8
-Q .001(alues may well be too short.)-.25 F F2(Sendmail)5.001 E F1 -.1(wa)2.501
-G 2.501(sd).1 G .001(esigned to)463.169 444.8 R .712
-(the RFC 822 protocols, which did not specify read timeouts; hence, v)117 456.8
-R .711(ersions of)-.15 F F2(sendmail)3.211 E F1(prior)3.211 E .864(to v)117
-468.8 R .865(ersion 8.1 did not guarantee to reply to messages promptly)-.15 F
-5.865(.I)-.65 G 3.365(np)386.24 468.8 S(articular)399.605 468.8 Q 3.365(,a\231)
--.4 G .865(RCPT\232 com-)450.635 468.8 R .061
-(mand specifying a mailing list will e)117 480.8 R .061(xpand and v)-.15 F .06
-(erify the entire list; a lar)-.15 F .06(ge list on a slo)-.18 F 2.56(ws)-.25 G
-(ystem)480.11 480.8 Q .436(may easily tak)117 494.8 R 2.936(em)-.1 G .436
-(ore than \214v)190.698 494.8 R 2.936(em)-.15 G(inutes)252.126 494.8 Q F3(12)
-276.016 490.8 Q F1 5.436(.I)283.016 494.8 S .435
-(recommend a one hour timeout \212 since a commu-)297.218 494.8 R 1.365
-(nications f)117 506.8 R 1.366(ailure during the RCPT phase is rare, a long ti\
-meout is not onerous and may ulti-)-.1 F(mately help reduce netw)117 518.8 Q
-(ork load and duplicated messages.)-.1 E -.15(Fo)142 535 S 2.5(re).15 G
-(xample, the lines:)162.53 535 Q 2.5(OT)157 551.2 S(imeout.command=25m)172.48
-551.2 Q 2.5(OT)157 563.2 S(imeout.datablock=3h)172.48 563.2 Q .344
-(sets the serv)117 579.4 R .344(er SMTP command timeout to 25 minutes and the \
-input data block timeout to three)-.15 F(hours.)117 591.4 Q F0 2.5
-(4.1.3. Message)102 615.4 R(timeouts)2.5 E F1 .237
-(After sitting in the queue for a fe)142 631.6 R 2.737(wd)-.25 G .237
-(ays, a message will time out.)289.726 631.6 R .238(This is to insure that at)
-5.238 F .568(least the sender is a)117 643.6 R -.1(wa)-.15 G .568
-(re of the inability to send a message.).1 F .567
-(The timeout is typically set to \214v)5.568 F(e)-.15 E 2.599(days. It)117
-655.6 R .099(is sometimes considered con)2.599 F -.15(ve)-.4 G .099
-(nient to also send a w).15 F .1(arning message if the message is in)-.1 F .32
-LW 76 665.2 72 665.2 DL 80 665.2 76 665.2 DL 84 665.2 80 665.2 DL 88 665.2 84
-665.2 DL 92 665.2 88 665.2 DL 96 665.2 92 665.2 DL 100 665.2 96 665.2 DL 104
-665.2 100 665.2 DL 108 665.2 104 665.2 DL 112 665.2 108 665.2 DL 116 665.2 112
-665.2 DL 120 665.2 116 665.2 DL 124 665.2 120 665.2 DL 128 665.2 124 665.2 DL
-132 665.2 128 665.2 DL 136 665.2 132 665.2 DL 140 665.2 136 665.2 DL 144 665.2
-140 665.2 DL 148 665.2 144 665.2 DL 152 665.2 148 665.2 DL 156 665.2 152 665.2
-DL 160 665.2 156 665.2 DL 164 665.2 160 665.2 DL 168 665.2 164 665.2 DL 172
-665.2 168 665.2 DL 176 665.2 172 665.2 DL 180 665.2 176 665.2 DL 184 665.2 180
-665.2 DL 188 665.2 184 665.2 DL 192 665.2 188 665.2 DL 196 665.2 192 665.2 DL
-200 665.2 196 665.2 DL 204 665.2 200 665.2 DL 208 665.2 204 665.2 DL 212 665.2
-208 665.2 DL 216 665.2 212 665.2 DL/F4 5/Times-Roman@0 SF(11)93.6 675.6 Q/F5 8
-/Times-Roman@0 SF(On some systems the def)3.2 I
-(ault is zero to turn the protocol of)-.08 E 2(fe)-.2 G(ntirely)293.848 678.8 Q
-(.)-.52 E F4(12)93.6 689.2 Q F5 .212(This v)3.2 J .212
-(eri\214cation includes looking up e)-.12 F -.12(ve)-.2 G .212
-(ry address with the name serv).12 F .212(er; this in)-.12 F -.16(vo)-.32 G(lv)
-.16 E .212(es netw)-.12 F .213(ork delays, and can in some cases)-.08 F
-(can be considerable.)72 702 Q EP
-%%Page: 23 18
-%%BeginPageSetup
-BP
-%%EndPageSetup
-/F0 10/Times-Bold@0 SF(Sendmail Installation and Operation Guide)72 60 Q
-(SMM:08-23)452.9 60 Q/F1 10/Times-Roman@0 SF .176(the queue longer than a fe)
-117 96 R 2.675(wh)-.25 G .175(ours \(assuming you normally ha)236.105 96 R .475
--.15(ve g)-.2 H .175(ood connecti).15 F .175(vity; if your mes-)-.25 F .645
-(sages normally took se)117 108 R -.15(ve)-.25 G .645(ral hours to send you w)
-.15 F(ouldn')-.1 E 3.145(tw)-.18 G .645(ant to do this because it w)355.055 108
-R(ouldn')-.1 E 3.145(tb)-.18 G(e)499.56 108 Q 3.871(an unusual e)117 120 R -.15
-(ve)-.25 G 6.371(nt\). These).15 F 3.871(timeouts are set using the)6.371 F F0
--.18(Ti)6.371 G(meout.queuer).18 E(etur)-.18 E(n)-.15 E F1(and)6.371 E F0 -.18
-(Ti)6.37 G(me-).18 E(out.queuewar)117 132 Q(n)-.15 E F1
-(options in the con\214guration \214le \(pre)2.5 E
-(viously both were set using the)-.25 E F0(T)2.5 E F1(option\).)2.5 E .106
-(Since these options are global, and since you can not kno)142 148.2 R(w)-.25 E
-/F2 10/Times-Italic@0 SF 2.606(ap)2.606 G(riori)393.822 148.2 Q F1(ho)2.607 E
-2.607(wl)-.25 G .107(ong another host)437.126 148.2 R .476
-(outside your domain will be do)117 160.2 R .475(wn, a \214v)-.25 F 2.975(ed)
--.15 G .475(ay timeout is recommended.)291.785 160.2 R .475(This allo)5.475 F
-.475(ws a recipient)-.25 F 1.579(to \214x the problem e)117 172.2 R -.15(ve)
--.25 G 4.079(ni).15 G 4.079(fi)222.545 172.2 S 4.079(to)232.734 172.2 S 1.579
-(ccurs at the be)244.593 172.2 R 1.58(ginning of a long week)-.15 F 4.08
-(end. RFC)-.1 F 1.58(1123 section)4.08 F
-(5.3.1.1 says that this parameter should be `)117 184.2 Q
-(`at least 4\2555 days')-.74 E('.)-.74 E(The)142 200.4 Q F0 -.18(Ti)2.923 G
-(meout.queuewar).18 E(n)-.15 E F1 -.25(va)2.923 G .423(lue can be piggyback).25
-F .422(ed on the)-.1 F F0(T)2.922 E F1 .422(option by indicating a time)2.922 F
-.845(after which a w)117 212.4 R .845(arning message should be sent; the tw)-.1
-F 3.346(ot)-.1 G .846(imeouts are separated by a slash.)349.104 212.4 R -.15
-(Fo)5.846 G(r).15 E -.15(ex)117 224.4 S(ample, the line).15 E -.4(OT)157 240.6
-S(5d/4h).4 E .972(causes email to f)117 256.8 R .971(ail after \214v)-.1 F
-3.471(ed)-.15 G .971(ays, b)245.329 256.8 R .971(ut a w)-.2 F .971
-(arning message will be sent after four hours.)-.1 F(This)5.971 E
-(should be lar)117 268.8 Q(ge enough that the message will ha)-.18 E .3 -.15
-(ve b)-.2 H(een tried se).15 E -.15(ve)-.25 G(ral times.).15 E F0 2.5(4.2. F)87
-292.8 R(orking During Queue Runs)-.25 E F1 .848(By setting the)127 309 R F0
--.25(Fo)3.348 G(rkEachJ).25 E(ob)-.15 E F1(\()3.348 E F0(Y)A F1 3.348(\)o)C
-(ption,)271.12 309 Q F2(sendmail)3.348 E F1 .849(will fork before each indi)
-3.348 F .849(vidual message)-.25 F .293(while running the queue.)102 321 R .293
-(This will pre)5.293 F -.15(ve)-.25 G(nt).15 E F2(sendmail)2.793 E F1 .293
-(from consuming lar)2.793 F .293(ge amounts of memory)-.18 F 2.792(,s)-.65 G(o)
-499 321 Q 1.11(it may be useful in memory-poor en)102 333 R 3.61
-(vironments. Ho)-.4 F(we)-.25 E -.15(ve)-.25 G 1.91 -.4(r, i).15 H 3.61(ft).4 G
-(he)359.95 333 Q F0 -.25(Fo)3.61 G(rkEachJ).25 E(ob)-.15 E F1 1.11
-(option is not set,)3.61 F F2(sendmail)102 345 Q F1 .085(will k)2.585 F .085
-(eep track of hosts that are do)-.1 F .084
-(wn during a queue run, which can impro)-.25 F .384 -.15(ve p)-.15 H
-(erformance).15 E(dramatically)102 357 Q(.)-.65 E(If the)127 373.2 Q F0 -.25
-(Fo)2.5 G(rkEachJ).25 E(ob)-.15 E F1(option is set,)2.5 E F2(sendmail)2.5 E F1
-(can not use connection caching.)2.5 E F0 2.5(4.3. Queue)87 397.2 R(Priorities)
-2.5 E F1(Ev)127 413.4 Q 1.128(ery message is assigned a priority when it is \
-\214rst instantiated, consisting of the message)-.15 F .286
-(size \(in bytes\) of)102 425.4 R .286(fset by the message class \(which is de\
-termined from the Precedence: header\) times)-.25 F .342(the \231w)102 437.4 R
-.342(ork class f)-.1 F .343
-(actor\232 and the number of recipients times the \231w)-.1 F .343
-(ork recipient f)-.1 F(actor)-.1 E 4.243 -.7(.\232 T)-.55 H .343(he priority).7
-F .073(is used to order the queue.)102 449.4 R .073
-(Higher numbers for the priority mean that the message will be processed)5.073
-F(later when running the queue.)102 461.4 Q .328
-(The message size is included so that lar)127 477.6 R .329
-(ge messages are penalized relati)-.18 F .629 -.15(ve t)-.25 H 2.829(os).15 G
-.329(mall messages.)443.121 477.6 R .285(The message class allo)102 489.6 R
-.285(ws users to send \231high priority\232 messages by including a \231Preced\
-ence:\232 \214eld)-.25 F .007(in their message; the v)102 501.6 R .007
-(alue of this \214eld is look)-.25 F .008(ed up in the)-.1 F F0(P)2.508 E F1
-.008(lines of the con\214guration \214le.)2.508 F .008(Since the)5.008 F 1.967
-(number of recipients af)102 513.6 R 1.967
-(fects the amount of load a message presents to the system, this is also)-.25 F
-(included into the priority)102 525.6 Q(.)-.65 E .53(The recipient and class f)
-127 541.8 R .53(actors can be set in the con\214guration \214le using the)-.1 F
-F0(RecipientF)3.03 E(actor)-.25 E F1(\()102 553.8 Q F0(y)A F1 3.443(\)a)C(nd)
-121.543 553.8 Q F0(ClassF)3.443 E(actor)-.25 E F1(\()3.442 E F0(z)A F1 3.442
-(\)o)C .942(ptions respecti)208.82 553.8 R -.15(ve)-.25 G(ly).15 E 5.942(.T)
--.65 G(he)298.534 553.8 Q 3.442(yd)-.15 G(ef)321.266 553.8 Q .942
-(ault to 30000 \(for the recipient f)-.1 F .942(actor\) and)-.1 F
-(1800 \(for the class f)102 565.8 Q 2.5(actor\). The)-.1 F
-(initial priority is:)2.5 E F2(pri)168.495 583.8 Q/F3 10/Symbol SF(=)3.16 E F2
-(msgsize)3.18 E F3(-)2.38 E F1(\()2.2 E F2(class).2 E F3<b4>2.47 E F0
-(ClassFactor\))2.2 E F3(+)2.2 E F1(\()2.2 E F2(nrcpt).36 E F3<b4>2.88 E F0
-(RecipientFactor\))2.2 E F1(\(Remember)102 601.8 Q 3.328(,h)-.4 G .828(igher v)
-159.638 601.8 R .828
-(alues for this parameter actually mean that the job will be treated with lo)
--.25 F(wer)-.25 E(priority)102 613.8 Q(.\))-.65 E 1.519(The priority of a job \
-can also be adjusted each time it is processed \(that is, each time an)127 630
-R .235(attempt is made to deli)102 642 R -.15(ve)-.25 G 2.736(ri).15 G .236
-(t\) using the \231w)211.938 642 R .236(ork time f)-.1 F(actor)-.1 E 1.636 -.7
-(,\232 s)-.4 H .236(et by the).7 F F0(RetryF)2.736 E(actor)-.25 E F1(\()2.736 E
-F0(Z)A F1 2.736(\)o)C 2.736(ption. This)457.924 642 R .367
-(is added to the priority)102 654 R 2.867(,s)-.65 G 2.867(oi)202.625 654 S
-2.867(tn)213.272 654 S .366
-(ormally decreases the precedence of the job, on the grounds that jobs)223.919
-654 R .137(that ha)102 666 R .437 -.15(ve f)-.2 H .137(ailed man).05 F 2.637
-(yt)-.15 G .137(imes will tend to f)193.598 666 R .137(ail ag)-.1 F .137
-(ain in the future.)-.05 F(The)5.137 E F0(RetryF)2.637 E(actor)-.25 E F1 .137
-(option def)2.637 F .138(aults to)-.1 F(90000.)102 678 Q EP
-%%Page: 24 19
-%%BeginPageSetup
-BP
-%%EndPageSetup
-/F0 10/Times-Bold@0 SF 193.36(SMM:08-24 Sendmail)72 60 R
-(Installation and Operation Guide)2.5 E 2.5(4.4. Load)87 96 R(Limiting)2.5 E/F1
-10/Times-Italic@0 SF(Sendmail)127 112.2 Q/F2 10/Times-Roman@0 SF .102
-(can be ask)2.602 F .101(ed to queue \(b)-.1 F .101(ut not deli)-.2 F -.15(ve)
--.25 G .101(r\) mail if the system load a).15 F -.15(ve)-.2 G .101
-(rage gets too high).15 F .483(using the)102 124.2 R F0(QueueLA)2.983 E F2(\()
-2.983 E F0(x)A F2 2.983(\)o)C 2.983(ption. When)206.152 124.2 R .483
-(the load a)2.983 F -.15(ve)-.2 G .483(rage e).15 F .483(xceeds the v)-.15 F
-.484(alue of the)-.25 F F0(QueueLA)2.984 E F2(option,)2.984 E .532(the deli)102
-136.2 R -.15(ve)-.25 G .532(ry mode is set to).15 F F0(q)3.032 E F2 .532
-(\(queue only\) if the)3.032 F F0(QueueF)3.032 E(actor)-.25 E F2(\()3.032 E F0
-(q)A F2 3.032(\)o)C .531(ption di)379.066 136.2 R .531(vided by the dif)-.25 F
-(ference)-.25 E .004(in the current load a)102 148.2 R -.15(ve)-.2 G .004
-(rage and the).15 F F0(QueueLA)2.504 E F2 .004(option plus one e)2.504 F .004
-(xceeds the priority of the message \212)-.15 F
-(that is, the message is queued if)102 160.2 Q(f:)-.25 E F1(pri)251.425 183.61
-Q F2(>)3.16 E F0(QueueFactor)287.21 176.61 Q F1(LA)276.475 190.61 Q/F3 10
-/Symbol SF(-)2.23 E F0(QueueLA)2.2 E F3(+)2.2 E .4 LW 354.625 181.01 275.895
-181.01 DL F2(1)349.625 190.61 Q(The)102 206.87 Q F0(QueueF)2.616 E(actor)-.25 E
-F2 .116(option def)2.616 F .116(aults to 600000, so each point of load a)-.1 F
--.15(ve)-.2 G .116(rage is w).15 F .116(orth 600000 priority)-.1 F
-(points \(as described abo)102 218.87 Q -.15(ve)-.15 G(\).).15 E -.15(Fo)127
-235.07 S 3.893(rd).15 G 1.393(rastic cases, the)149.633 235.07 R F0(RefuseLA)
-3.893 E F2(\()3.893 E F0(X)A F2 3.893(\)o)C 1.394(ption de\214nes a load a)
-288.228 235.07 R -.15(ve)-.2 G 1.394(rage at which).15 F F1(sendmail)3.894 E F2
-(will)3.894 E .69(refuse to accept netw)102 247.07 R .689(ork connections.)-.1
-F .689(Locally generated mail \(including incoming UUCP mail\) is)5.689 F
-(still accepted.)102 259.07 Q F0 2.5(4.5. Deli)87 283.07 R -.1(ve)-.1 G
-(ry Mode).1 E F2 .253(There are a number of deli)127 299.27 R -.15(ve)-.25 G
-.253(ry modes that).15 F F1(sendmail)2.753 E F2 .254
-(can operate in, set by the)2.753 F F0(Deli)2.754 E -.1(ve)-.1 G(ryMode).1 E F2
-(\()102 311.27 Q F0(d)A F2 3.599(\)c)C 1.099(on\214guration option.)122.259
-311.27 R 1.099(These modes specify ho)6.099 F 3.598(wq)-.25 G 1.098
-(uickly mail will be deli)324.142 311.27 R -.15(ve)-.25 G 3.598(red. Le).15 F
--.05(ga)-.15 G 3.598(lm).05 G(odes)485.67 311.27 Q(are:)102 323.27 Q 17.22(id)
-142 339.47 S(eli)167 339.47 Q -.15(ve)-.25 G 2.5(ri).15 G(nteracti)194.65
-339.47 Q -.15(ve)-.25 G(ly \(synchronously\)).15 E 15(bd)142 351.47 S(eli)167
-351.47 Q -.15(ve)-.25 G 2.5(ri).15 G 2.5(nb)194.65 351.47 S
-(ackground \(asynchronously\))207.15 351.47 Q 15(qq)142 363.47 S
-(ueue only \(don')167 363.47 Q 2.5(td)-.18 G(eli)240.42 363.47 Q -.15(ve)-.25 G
-(r\)).15 E 15(dd)142 375.47 S(efer delv)167 375.47 Q(ery attempts \(don')-.15 E
-2.5(td)-.18 G(eli)285.53 375.47 Q -.15(ve)-.25 G(r\)).15 E 1.273
-(There are tradeof)102 391.67 R 3.773(fs. Mode)-.25 F 1.273(\231i\232 gi)3.773
-F -.15(ve)-.25 G 3.773(st).15 G 1.273(he sender the quick)258.938 391.67 R
-1.273(est feedback, b)-.1 F 1.274(ut may slo)-.2 F 3.774(wd)-.25 G -.25(ow)
-462.146 391.67 S 3.774(ns).25 G(ome)486.78 391.67 Q .799
-(mailers and is hardly e)102 403.67 R -.15(ve)-.25 G 3.299(rn).15 G(ecessary)
-216.405 403.67 Q 5.799(.M)-.65 G .799(ode \231b\232 deli)266.814 403.67 R -.15
-(ve)-.25 G .799(rs promptly b).15 F .798(ut can cause lar)-.2 F .798
-(ge numbers of)-.18 F .223(processes if you ha)102 415.67 R .524 -.15(ve a m)
--.2 H .224(ailer that tak).15 F .224(es a long time to deli)-.1 F -.15(ve)-.25
-G 2.724(ram).15 G 2.724(essage. Mode)370.904 415.67 R .224
-(\231q\232 minimizes the)2.724 F .597(load on your machine, b)102 427.67 R .597
-(ut means that deli)-.2 F -.15(ve)-.25 G .596
-(ry may be delayed for up to the queue interv).15 F 3.096(al. Mode)-.25 F .039
-(\231d\232 is identical to mode \231q\232 e)102 439.67 R .039
-(xcept that it also pre)-.15 F -.15(ve)-.25 G .04
-(nts all the early map lookups from w).15 F .04(orking; it is)-.1 F .086
-(intended for `)102 451.67 R .086(`dial on demand')-.74 F 2.586('s)-.74 G .085
-(ites where DNS lookups might cost real mone)233.42 451.67 R 3.885 -.65(y. S)
--.15 H .085(ome simple error).65 F .817(messages \(e.g., host unkno)102 463.67
-R .817(wn during the SMTP protocol\) will be delayed using this mode.)-.25 F
-(Mode)5.818 E(\231b\232 is the usual def)102 475.67 Q(ault.)-.1 E .052(If you \
-run in mode \231q\232 \(queue only\), \231d\232 \(defer\), or \231b\232 \(deli)
-127 491.87 R -.15(ve)-.25 G 2.552(ri).15 G 2.552(nb)389.136 491.87 S
-(ackground\))401.688 491.87 Q F1(sendmail)2.551 E F2(will)2.551 E 1.391(not e)
-102 503.87 R 1.392(xpand aliases and follo)-.15 F 3.892(w.)-.25 G(forw)232.428
-503.87 Q 1.392(ard \214les upon initial receipt of the mail.)-.1 F 1.392
-(This speeds up the)6.392 F(response to RCPT commands.)102 515.87 Q
-(Mode \231i\232 cannot be used by the SMTP serv)5 E(er)-.15 E(.)-.55 E F0 2.5
-(4.6. Log)87 539.87 R(Le)2.5 E -.1(ve)-.15 G(l).1 E F2 .19(The le)127 556.07 R
--.15(ve)-.25 G 2.69(lo).15 G 2.69(fl)171.97 556.07 S .19(ogging can be set for)
-180.77 556.07 R F1(sendmail)2.689 E F2 5.189(.T)C .189(he def)317.996 556.07 R
-.189(ault using a standard con\214guration table)-.1 F(is le)102 568.07 Q -.15
-(ve)-.25 G 2.5(l9).15 G 5(.T)137.71 568.07 S(he le)151.32 568.07 Q -.15(ve)-.25
-G(ls are as follo).15 E(ws:)-.25 E 31(0N)102 584.27 S 2.5(ol)145.22 584.27 S
-(ogging.)155.5 584.27 Q 31(1S)102 600.47 S(erious system f)143.56 600.47 Q
-(ailures and potential security problems.)-.1 E 31(2L)102 616.67 S
-(ost communications \(netw)144.11 616.67 Q(ork problems\) and protocol f)-.1 E
-(ailures.)-.1 E 31(3O)102 632.87 S(ther serious f)145.22 632.87 Q(ailures.)-.1
-E 31(4M)102 649.07 S(inor f)146.89 649.07 Q(ailures.)-.1 E 31(5M)102 665.27 S
-(essage collection statistics.)146.89 665.27 Q 31(6C)102 681.47 S
-(reation of error messages, VRFY and EXPN commands.)144.67 681.47 Q 31(7D)102
-697.67 S(eli)145.22 697.67 Q -.15(ve)-.25 G(ry f).15 E
-(ailures \(host or user unkno)-.1 E(wn, etc.\).)-.25 E 31(8S)102 713.87 S
-(uccessful deli)143.56 713.87 Q -.15(ve)-.25 G(ries and alias database reb).15
-E(uilds.)-.2 E EP
-%%Page: 25 20
-%%BeginPageSetup
-BP
-%%EndPageSetup
-/F0 10/Times-Bold@0 SF(Sendmail Installation and Operation Guide)72 60 Q
-(SMM:08-25)452.9 60 Q/F1 10/Times-Roman@0 SF 31(9M)102 96 S
-(essages being deferred \(due to a host being do)146.89 96 Q(wn, etc.\).)-.25 E
-23.5(10 Database)102 112.2 R -.15(ex)2.5 G(pansion \(alias, forw).15 E
-(ard, and userdb lookups\).)-.1 E 23.5(20 Logs)102 128.4 R .603
-(attempts to run lock)3.102 F .603(ed queue \214les.)-.1 F .603
-(These are not errors, b)5.603 F .603(ut can be useful to note if)-.2 F
-(your queue appears to be clogged.)138 140.4 Q 23.5(30 Lost)102 156.6 R
-(locks \(only if using lockf instead of \215ock\).)2.5 E(Additionally)102 172.8
-Q 2.717(,v)-.65 G .217(alues abo)161.877 172.8 R .516 -.15(ve 6)-.15 H 2.716
-(4a).15 G .216(re reserv)228.596 172.8 R .216(ed for e)-.15 F .216(xtremely v)
--.15 F .216(erbose deb)-.15 F .216(ugging output.)-.2 F .216(No normal site)
-5.216 F -.1(wo)102 184.8 S(uld e).1 E -.15(ve)-.25 G 2.5(rs).15 G(et these.)
-152.6 184.8 Q F0 2.5(4.7. File)87 208.8 R(Modes)2.5 E F1 .264
-(The modes used for \214les depend on what functionality you w)127 225 R .264
-(ant and the le)-.1 F -.15(ve)-.25 G 2.764(lo).15 G 2.764(fs)448.482 225 S .264
-(ecurity you)458.466 225 R(require.)102 237 Q F0 2.5(4.7.1. T)102 261 R 2.5(os)
--.92 G(uid or not to suid?)146.64 261 Q/F2 10/Times-Italic@0 SF(Sendmail)142
-277.2 Q F1 .934(can safely be made setuid to root.)3.434 F .934
-(At the point where it is about to)5.934 F F2 -.2(ex)3.433 G(ec).2 E F1 .933
-(\(2\) a)1.666 F(mailer)117 289.2 Q 2.582(,i)-.4 G 2.582(tc)150.012 289.2 S
-.082(hecks to see if the userid is zero; if so, it resets the userid and group\
-id to a def)159.814 289.2 R .083(ault \(set)-.1 F .577(by the)117 301.2 R F0(u)
-3.077 E F1(and)3.077 E F0(g)3.077 E F1 3.077(options\). \(This)3.077 F .576
-(can be o)3.076 F -.15(ve)-.15 G .576(rridden by setting the).15 F F0(S)3.076 E
-F1 .576(\215ag to the mailer for mailers)3.076 F 1.531
-(that are trusted and must be called as root.\))117 313.2 R(Ho)6.531 E(we)-.25
-E -.15(ve)-.25 G 2.331 -.4(r, t).15 H 1.532
-(his will cause mail processing to be).4 F(accounted \(using)117 325.2 Q F2(sa)
-2.5 E F1(\(8\)\) to root rather than to the user sending the mail.)1.666 E .339
-(If you don')142 341.4 R 2.839(tm)-.18 G(ak)200.887 341.4 Q(e)-.1 E F2
-(sendmail)2.839 E F1 .339(setuid to root, it will still run b)2.839 F .339
-(ut you lose a lot of functional-)-.2 F .007(ity and a lot of pri)117 353.4 R
--.25(va)-.25 G -.15(cy).25 G 2.507(,s)-.5 G .008(ince you')215.452 353.4 R .008
-(ll ha)-.1 F .308 -.15(ve t)-.2 H 2.508(om).15 G(ak)300.024 353.4 Q 2.508(et)
--.1 G .008(he queue directory w)319.092 353.4 R .008(orld readable.)-.1 F -1.1
-(Yo)5.008 G 2.508(uc)1.1 G(ould)486.22 353.4 Q .501(also mak)117 365.4 R(e)-.1
-E F2(sendmail)3.001 E F1 .501(setuid to some pseudo-user \(e.g., create a user\
- called \231sendmail\232 and mak)3.001 F(e)-.1 E F2(sendmail)117 377.4 Q F1
-1.533(setuid to that\) which will \214x the pri)4.033 F -.25(va)-.25 G 1.834
--.15(cy p).25 H 1.534(roblems b).15 F 1.534(ut not the functionality issues.)
--.2 F .642(Also, this isn')117 389.4 R 3.142(tag)-.18 G .641
-(uarantee of security: for e)192.448 389.4 R .641
-(xample, root occasionally sends mail, and the dae-)-.15 F
-(mon often runs as root.)117 401.4 Q F0 2.5(4.7.2. Should)102 425.4 R
-(my alias database be writable?)2.5 E F1 .058(At Berk)142 441.6 R(ele)-.1 E
-2.558(yw)-.15 G 2.558(eh)200.186 441.6 S -2.25 -.2(av e)212.184 441.6 T .058
-(the alias database \(/etc/aliases*\) mode 644.)2.758 F .058
-(While this is not as \215e)5.058 F(x-)-.15 E 1.719
-(ible as if the database were more 666, it a)117 453.6 R -.2(vo)-.2 G 1.718
-(ids potential security problems with a globally).2 F(writable database.)117
-465.6 Q 1.19(The database that)142 481.8 R F2(sendmail)3.69 E F1 1.191
-(actually used is represented by the tw)3.691 F 3.691<6f8c>-.1 G(les)429.118
-481.8 Q F2(aliases.dir)3.691 E F1(and)3.691 E F2(aliases.pa)117 493.8 Q(g)-.1 E
-F1 .159(\(both in /etc\) \(or)2.659 F F2(aliases.db)2.659 E F1 .159
-(if you are running with the ne)2.659 F 2.658(wB)-.25 G(erk)412.854 493.8 Q
-(ele)-.1 E 2.658(yd)-.15 G .158(atabase prim-)449.692 493.8 R(iti)117 505.8 Q
--.15(ve)-.25 G 3.606(s\). The).15 F 1.107
-(mode on these \214les should match the mode on /etc/aliases.)3.606 F(If)6.107
-E F2(aliases)3.607 E F1 1.107(is writable)3.607 F 1.624(and the DBM \214les \()
-117 517.8 R F2(aliases.dir)A F1(and)4.124 E F2(aliases.pa)4.124 E(g)-.1 E F1
-4.124(\)a)C 1.624(re not, users will be unable to re\215ect their)324.648 517.8
-R .719(desired changes through to the actual database.)117 529.8 R(Ho)5.719 E
-(we)-.25 E -.15(ve)-.25 G 1.519 -.4(r, i).15 H(f).4 E F2(aliases)3.219 E F1 .72
-(is read-only and the DBM)3.219 F(\214les are writable, a slightly sophisticat\
-ed user can arrange to steal mail an)117 541.8 Q(yw)-.15 E(ay)-.1 E(.)-.65 E
-.621(If your DBM \214les are not writable by the w)142 558 R .62
-(orld or you do not ha)-.1 F .92 -.15(ve a)-.2 H(uto-reb).15 E .62
-(uild enabled)-.2 F 3.028(\(with the)117 570 R F0 -.5(Au)5.528 G(toReb).5 E
-(uildAliases)-.2 E F1 3.028
-(option\), then you must be careful to reconstruct the alias)5.528 F
-(database each time you change the te)117 582 Q(xt v)-.15 E(ersion:)-.15 E(ne)
-157 598.2 Q -.1(wa)-.25 G(liases).1 E(If this step is ignored or for)117 614.4
-Q(gotten an)-.18 E 2.5(yi)-.15 G(ntended changes will also be ignored or for)
-273.32 614.4 Q(gotten.)-.18 E F0 2.5(4.8. Connection)87 638.4 R(Caching)2.5 E
-F1 .642(When processing the queue,)127 654.6 R F2(sendmail)3.142 E F1 .642
-(will try to k)3.142 F .642(eep the last fe)-.1 F 3.142(wo)-.25 G .642
-(pen connections open to)405.144 654.6 R -.2(avo)102 666.6 S
-(id startup and shutdo).2 E(wn costs.)-.25 E
-(This only applies to IPC connections.)5 E .286
-(When trying to open a connection the cache is \214rst searched.)127 682.8 R
-.287(If an open connection is found,)5.286 F .92
-(it is probed to see if it is still acti)102 694.8 R 1.22 -.15(ve b)-.25 H 3.42
-(ys).15 G .92(ending a)270.892 694.8 R/F3 9/Times-Roman@0 SF(NOOP)3.42 E F1
-3.42(command. It)3.42 F .92(is not an error if this f)3.42 F(ails;)-.1 E
-(instead, the connection is closed and reopened.)102 706.8 Q EP
-%%Page: 26 21
-%%BeginPageSetup
-BP
-%%EndPageSetup
-/F0 10/Times-Bold@0 SF 193.36(SMM:08-26 Sendmail)72 60 R
-(Installation and Operation Guide)2.5 E/F1 10/Times-Roman@0 SF -1 -.8(Tw o)127
-96 T .408(parameters control the connection cache.)3.708 F(The)5.408 E F0
-(ConnectionCacheSize)2.908 E F1(\()2.908 E F0(k)A F1 2.908(\)o)C .408
-(ption de\214nes)452.202 96 R .145
-(the number of simultaneous open connections that will be permitted.)102 108 R
-.145(If it is set to zero, connections)5.145 F .212
-(will be closed as quickly as possible.)102 120 R .212(The def)5.212 F .212
-(ault is one.)-.1 F .213(This should be set as appropriate for your)5.212 F .63
-(system size; it will limit the amount of system resources that)102 132 R/F2 10
-/Times-Italic@0 SF(sendmail)3.129 E F1 .629(will use during queue runs.)3.129 F
-(Ne)102 144 Q -.15(ve)-.25 G 2.5(rs).15 G(et this higher than 4.)132.42 144 Q
-(The)127 160.2 Q F0(ConnectionCacheT)2.74 E(imeout)-.18 E F1(\()2.741 E F0(K)A
-F1 2.741(\)o)C .241(ption speci\214es the maximum time that an)281.692 160.2 R
-2.741(yc)-.15 G .241(ached con-)460.169 160.2 R .9
-(nection will be permitted to idle.)102 172.2 R .899(When the idle time e)5.9 F
-.899(xceeds this v)-.15 F .899(alue the connection is closed.)-.25 F .34
-(This number should be small \(under ten minutes\) to pre)102 184.2 R -.15(ve)
--.25 G .34(nt you from grabbing too man).15 F 2.84(yr)-.15 G(esources)469.57
-184.2 Q(from other hosts.)102 196.2 Q(The def)5 E(ault is \214v)-.1 E 2.5(em)
--.15 G(inutes.)257.57 196.2 Q F0 2.5(4.9. Name)87 220.2 R(Ser)2.5 E -.1(ve)-.1
-G 2.5(rA).1 G(ccess)172.33 220.2 Q F1 .104
-(Control of host address lookups is set by the)127 236.4 R F0(hosts)2.604 E F1
-.103(service entry in your service switch \214le.)2.603 F(If)5.103 E .99
-(you are on a system that has b)102 248.4 R .99
-(uilt-in service switch support \(e.g., Ultrix, Solaris, or DEC OSF/1\))-.2 F
-.336(then your system is probably con\214gured properly already)102 260.4 R
-5.335(.O)-.65 G(therwise,)347.885 260.4 Q F2(sendmail)2.835 E F1 .335
-(will consult the \214le)2.835 F F0(/etc/ser)102 272.4 Q(vice.switch)-.1 E F1
-2.5(,w)C(hich should be created.)191.04 272.4 Q F2(Sendmail)5 E F1
-(only uses tw)2.5 E 2.5(oe)-.1 G(ntries:)389.8 272.4 Q F0(hosts)2.5 E F1(and)
-2.5 E F0(aliases)2.5 E F1(.)A(Ho)127 288.6 Q(we)-.25 E -.15(ve)-.25 G .908 -.4
-(r, s).15 H .108(ome systems \(such as SunOS\) will do DNS lookups re).4 F -.05
-(ga)-.15 G .108(rdless of the setting of the).05 F 1.558(service switch entry)
-102 300.6 R 6.558(.I)-.65 G 4.058(np)196.834 300.6 S(articular)210.892 300.6 Q
-4.058(,t)-.4 G 1.558(he system routine)253.15 300.6 R F2 -.1(ge)4.058 G
-(thostbyname).1 E F1 1.558(\(3\) is used to look up host)B .461(names, and man)
-102 312.6 R 2.961(yv)-.15 G .461(endor v)180.293 312.6 R .461
-(ersions try some combination of DNS, NIS, and \214le lookup in /etc/hosts)-.15
-F .537(without consulting a service switch.)102 324.6 R F2(Sendmail)5.537 E F1
-(mak)3.037 E .536(es no attempt to w)-.1 F .536(ork around this problem, and)
--.1 F .27(the DNS lookup will be done an)102 336.6 R(yw)-.15 E(ay)-.1 E 5.27
-(.I)-.65 G 2.77(fy)264.36 336.6 S .271(ou do not ha)275.46 336.6 R .571 -.15
-(ve a n)-.2 H(ameserv).15 E .271(er con\214gured at all, such as at)-.15 F
-2.855(aU)102 348.6 S .355(UCP-only site,)116.515 348.6 R F2(sendmail)2.855 E F1
-.354
-(will get a \231connection refused\232 message when it tries to connect to the)
-2.855 F .622(name serv)102 360.6 R(er)-.15 E 5.622(.I)-.55 G 3.122(ft)161.964
-360.6 S(he)171.196 360.6 Q F0(hosts)3.122 E F1 .623
-(switch entry has the service \231dns\232 listed some)3.122 F .623
-(where in the list,)-.25 F F2(sendmail)3.123 E F1 .912
-(will interpret this to mean a temporary f)102 372.6 R .912
-(ailure and will queue the mail for later processing; other)-.1 F(-)-.2 E
-(wise, it ignores the name serv)102 384.6 Q(er data.)-.15 E .672
-(The same technique is used to decide whether to do MX lookups.)127 400.8 R
-.673(If you w)5.673 F .673(ant MX support,)-.1 F(you)102 412.8 Q F2(must)2.5 E
-F1(ha)2.5 E .3 -.15(ve \231)-.2 H(dns\232 listed as a service in the).15 E F0
-(hosts)2.5 E F1(switch entry)2.5 E(.)-.65 E(The)127 429 Q F0(Resolv)3.87 E
-(erOptions)-.1 E F1(\()3.87 E F0(I)A F1 3.869(\)o)C 1.369(ption allo)240.719
-429 R 1.369(ws you to tweak name serv)-.25 F 1.369(er options.)-.15 F 1.369
-(The command)6.369 F .892(line tak)102 441 R .892
-(es a series of \215ags as documented in)-.1 F F2 -.37(re)3.392 G(solver).37 E
-F1 .892(\(3\) \(with the leading \231RES_\232 deleted\).)B(Each)5.892 E
-(can be preceded by an optional `+' or `)102 453 Q/F3 10/Symbol SF(-)A F1 2.5
-('. F)B(or e)-.15 E(xample, the line)-.15 E 2.5(OR)142 469.2 S(esolv)158.39
-469.2 Q(erOptions=+AA)-.15 E(ONL)-.55 E(Y)-1 E F3(-)2.5 E F1(DNSRCH)A .862
-(turns on the AA)102 485.4 R(ONL)-.55 E 3.362(Y\()-1 G .862(accept authoritati)
-201.658 485.4 R 1.162 -.15(ve a)-.25 H .861(nswers only\) and turns of).15 F
-3.361(ft)-.25 G .861(he DNSRCH \(search the)402.827 485.4 R 2.039
-(domain path\) options.)102 497.4 R 2.039(Most resolv)7.039 F 2.039
-(er libraries def)-.15 F 2.039(ault DNSRCH, DEFN)-.1 F 2.039(AMES, and RECURSE)
--.35 F .503(\215ags on and all others of)102 509.4 R 3.003(f. Y)-.25 F .503
-(ou can also include \231HasW)-1.1 F .503
-(ildcardMX\232 to specify that there is a wild-)-.4 F 1.972
-(card MX record matching your domain; this turns of)102 521.4 R 4.472(fM)-.25 G
-4.473(Xm)344.188 521.4 S 1.973(atching when canonifying names,)363.661 521.4 R
-(which can lead to inappropriate canoni\214cations.)102 533.4 Q -1.11(Ve)127
-549.6 S 2.257(rsion le)1.11 F -.15(ve)-.25 G 4.757(l1c).15 G 2.256
-(on\214gurations turn DNSRCH and DEFN)200.301 549.6 R 2.256(AMES of)-.35 F
-4.756(fw)-.25 G 2.256(hen doing deli)424.898 549.6 R -.15(ve)-.25 G(ry).15 E
-2.06(lookups, b)102 561.6 R 2.06(ut lea)-.2 F 2.36 -.15(ve t)-.2 H 2.06
-(hem on e).15 F -.15(ve)-.25 G 2.06(rywhere else.).15 F -1.11(Ve)7.06 G 2.06
-(rsion 8 of)1.11 F F2(sendmail)4.56 E F1 2.06(ignores them when doing)4.56 F
-.313(canoni\214cation lookups \(that is, when using $[ ... $]\), and al)102
-573.6 R -.1(wa)-.1 G .313(ys does the search.).1 F .313(If you don')5.313 F
-2.812(tw)-.18 G(ant)491.78 573.6 Q(to do automatic name e)102 585.6 Q
-(xtension, don')-.15 E 2.5(tc)-.18 G(all $[ ... $].)261.93 585.6 Q .485
-(The search rules for $[ ... $] are some)127 601.8 R .485(what dif)-.25 F .485
-(ferent than usual.)-.25 F .486(If the name being look)5.485 F .486(ed up)-.1 F
-.11(has at least one dot, it al)102 613.8 R -.1(wa)-.1 G .11
-(ys tries the unmodi\214ed name \214rst.).1 F .109(If that f)5.109 F .109
-(ails, it tries the reduced search)-.1 F .124
-(path, and lastly tries the unmodi\214ed name \(b)102 625.8 R .124
-(ut only for names without a dot, since names with a dot)-.2 F(ha)102 637.8 Q
-.789 -.15(ve a)-.2 H .489(lready been tried\).).15 F .489(This allo)5.489 F
-.489(ws names such as `)-.25 F(`utc.CS')-.74 E 2.989('t)-.74 G 2.988(om)362.81
-637.8 S .488(atch the site in Czechoslo)378.578 637.8 R -.25(va)-.15 G(kia).25
-E 1.587(rather than the site in your local Computer Science department.)102
-649.8 R 1.588(It also prefers A and CN)6.587 F(AME)-.35 E .513(records o)102
-661.8 R -.15(ve)-.15 G 3.013(rM).15 G 3.013(Xr)163.816 661.8 S .513
-(ecords \212 that is, if it \214nds an MX record it mak)177.379 661.8 R .512
-(es note of it, b)-.1 F .512(ut k)-.2 F .512(eeps looking.)-.1 F 1.541(This w)
-102 673.8 R(ay)-.1 E 4.041(,i)-.65 G 4.041(fy)149.052 673.8 S 1.541(ou ha)
-161.423 673.8 R 1.841 -.15(ve a w)-.2 H 1.541
-(ildcard MX record matching your domain, it will not assume that all).15 F
-(names match.)102 685.8 Q 3.454 -.8(To c)127 702 T 1.853(ompletely turn of).8 F
-4.353(fa)-.25 G 1.853(ll name serv)231.123 702 R 1.853
-(er access on systems without service switch support)-.15 F .578
-(\(such as SunOS\) you will ha)102 714 R .878 -.15(ve t)-.2 H 3.078(or).15 G
-.579(ecompile with \255DN)245.406 714 R .579(AMED_BIND=0 and remo)-.35 F .879
--.15(ve \255)-.15 H .579(lresolv from).15 F EP
-%%Page: 27 22
-%%BeginPageSetup
-BP
-%%EndPageSetup
-/F0 10/Times-Bold@0 SF(Sendmail Installation and Operation Guide)72 60 Q
-(SMM:08-27)452.9 60 Q/F1 10/Times-Roman@0 SF
-(the list of libraries to be searched when linking.)102 96 Q F0 2.5(4.10. Mo)87
-120 R(ving the P)-.1 E(er)-.2 E(-User F)-.37 E(orward Files)-.25 E F1 .772
-(Some sites mount each user')127 136.2 R 3.272(sh)-.55 G .772
-(ome directory from a local disk on their w)256.13 136.2 R .772
-(orkstation, so that)-.1 F .575(local access is f)102 148.2 R 3.075(ast. Ho)-.1
-F(we)-.25 E -.15(ve)-.25 G 1.375 -.4(r, t).15 H .575(he result is that .forw).4
-F .575(ard \214le lookups are slo)-.1 F 4.376 -.65(w. I)-.25 H 3.076(ns).65 G
-.576(ome cases, mail)439.248 148.2 R .216(can e)102 160.2 R -.15(ve)-.25 G
-2.716(nb).15 G 2.716(ed)144.792 160.2 S(eli)156.948 160.2 Q -.15(ve)-.25 G .216
-(red on machines inappropriately because of a \214le serv).15 F .216
-(er being do)-.15 F 2.716(wn. The)-.25 F(perfor)2.716 E(-)-.2 E
-(mance can be especially bad if you run the automounter)102 172.2 Q(.)-.55 E
-(The)127 188.4 Q F0 -.25(Fo)2.743 G(rwardP).25 E(ath)-.1 E F1(\()2.743 E F0(J)A
-F1 2.743(\)o)C .243(ption allo)224.859 188.4 R .243
-(ws you to set a path of forw)-.25 F .243(ard \214les.)-.1 F -.15(Fo)5.243 G
-2.743(re).15 G .244(xample, the con-)436.582 188.4 R(\214g \214le line)102
-200.4 Q 2.5(OF)142 216.6 S(orw)157.13 216.6 Q(ardP)-.1 E(ath=/v)-.15 E(ar/forw)
--.25 E(ard/$u:$z/.forw)-.1 E(ard.$w)-.1 E -.1(wo)102 232.8 S .208
-(uld \214rst look for a \214le with the same name as the user').1 F 2.707(sl)
--.55 G .207(ogin in /v)343.191 232.8 R(ar/forw)-.25 E .207
-(ard; if that is not found)-.1 F 1.17(\(or is inaccessible\) the \214le `)102
-244.8 R(`.forw)-.74 E(ard.)-.1 E/F2 10/Times-Italic@0 SF(mac)A(hinename)-.15 E
-F1 2.651 -.74('' i)D 3.671(nt).74 G 1.171(he user')337.014 244.8 R 3.671(sh)
--.55 G 1.171(ome directory is searched.)382.126 244.8 R(A)6.171 E(truly perv)
-102 256.8 Q(erse site could also search by sender by using $r)-.15 E 2.5(,$)-.4
-G(s, or $f.)343.07 256.8 Q .69(If you create a directory such as /v)127 273 R
-(ar/forw)-.25 E .69(ard, it should be mode 1777 \(that is, the stick)-.1 F 3.19
-(yb)-.15 G(it)498.44 273 Q(should be set\).)102 285 Q
-(Users should create the \214les mode 644.)5 E F0 2.5(4.11. Fr)87 309 R
-(ee Space)-.18 E F1 1.405(On systems that ha)127 325.2 R 1.705 -.15(ve o)-.2 H
-1.405(ne of the system calls in the).15 F F2(statfs)3.906 E F1 1.406(\(2\) f)B
-1.406(amily \(including)-.1 F F2(statvfs)3.906 E F1(and)3.906 E F2(ustat)102
-337.2 Q F1 .839(\), you can specify a minimum number of free blocks on the que\
-ue \214lesystem using the)B F0(Min-)3.339 E(Fr)102 349.2 Q(eeBlocks)-.18 E F1
-(\()2.553 E F0(b)A F1 2.553(\)o)C 2.553(ption. If)171.916 349.2 R .053
-(there are fe)2.553 F .053
-(wer than the indicated number of blocks free on the \214lesystem)-.25 F 1.355
-(on which the queue is mounted the SMTP serv)102 361.2 R 1.355
-(er will reject mail with the 452 error code.)-.15 F(This)6.354 E(in)102 373.2
-Q(vites the SMTP client to try ag)-.4 E(ain later)-.05 E(.)-.55 E(Be)127 389.4
-Q -.1(wa)-.25 G .746(re of setting this option too high; it can cause rejectio\
-n of email when that mail w).1 F(ould)-.1 E(be processed without dif)102 401.4
-Q(\214culty)-.25 E(.)-.65 E F0 2.5(4.12. Maximum)87 425.4 R(Message Size)2.5 E
-F1 2.078 -.8(To a)127 441.6 T -.2(vo).6 G .478(id o).2 F -.15(ve)-.15 G(r\215o)
-.15 E .478(wing your system with a lar)-.25 F .478(ge message, the)-.18 F F0
-(MaxMessageSize)2.977 E F1 .477(option can be)2.977 F .692
-(set to set an absolute limit on the size of an)102 453.6 R 3.193(yo)-.15 G
-.693(ne message.)294.176 453.6 R .693(This will be adv)5.693 F .693
-(ertised in the ESMTP)-.15 F(dialogue and check)102 465.6 Q
-(ed during message collection.)-.1 E F0 2.5(4.13. Pri)87 489.6 R -.1(va)-.1 G
-(cy Flags).1 E F1(The)127 505.8 Q F0(Pri)2.96 E -.1(va)-.1 G(cyOptions).1 E F1
-(\()2.96 E F0(p)A F1 2.96(\)o)C .46(ption allo)235.12 505.8 R .46
-(ws you to set certain `)-.25 F(`pri)-.74 E -.25(va)-.25 G -.15(cy).25 G 1.94
--.74('' \215).15 H 2.96(ags. Actually).74 F 2.96(,m)-.65 G(an)478.42 505.8 Q
-2.96(yo)-.15 G(f)500.67 505.8 Q .533(them don')102 517.8 R 3.033(tg)-.18 G
--2.15 -.25(iv e)153.996 517.8 T .533(you an)3.283 F 3.034(ye)-.15 G .534
-(xtra pri)208.496 517.8 R -.25(va)-.25 G -.15(cy).25 G 3.034(,r)-.5 G .534
-(ather just insisting that client SMTP serv)264.634 517.8 R .534
-(ers use the HELO)-.15 F 2.87
-(command before using certain commands or adding e)102 529.8 R 2.87
-(xtra headers to indicate possible spoof)-.15 F(attempts.)102 541.8 Q .123
-(The option tak)127 558 R .124(es a series of \215ag names; the \214nal pri)-.1
-F -.25(va)-.25 G .424 -.15(cy i).25 H 2.624(st).15 G .124(he inclusi)367.706
-558 R .424 -.15(ve o)-.25 H 2.624(ro).15 G 2.624(ft)434.058 558 S .124
-(hose \215ags.)442.792 558 R -.15(Fo)5.124 G(r).15 E -.15(ex)102 570 S(ample:)
-.15 E 2.5(OP)142 586.2 S(ri)157.28 586.2 Q -.25(va)-.25 G -.15(cy).25 G
-(Options=needmailhelo, noe).15 E(xpn)-.15 E .928(insists that the HELO or EHLO\
- command be used before a MAIL command is accepted and dis-)102 602.4 R
-(ables the EXPN command.)102 614.4 Q
-(The \215ags are detailed in section 5.1.6.)127 630.6 Q F0 2.5(4.14. Send)87
-654.6 R(to Me T)2.5 E(oo)-.92 E F1(Normally)127 670.8 Q(,)-.65 E F2(sendmail)
-3.423 E F1 .923(deletes the \(en)3.423 F -.15(ve)-.4 G .923
-(lope\) sender from an).15 F 3.423(yl)-.15 G .924(ist e)375.484 670.8 R 3.424
-(xpansions. F)-.15 F .924(or e)-.15 F .924(xample, if)-.15 F .761(\231matt\232\
- sends to a list that contains \231matt\232 as one of the members he w)102
-682.8 R(on')-.1 E 3.261(tg)-.18 G .761(et a cop)416.705 682.8 R 3.261(yo)-.1 G
-3.261(ft)462.488 682.8 S .761(he mes-)471.859 682.8 R 2.882(sage. If)102 694.8
-R(the)2.882 E F0<ad6d>2.882 E F1 .383
-(\(me too\) command line \215ag, or if the)2.882 F F0(MeT)2.883 E(oo)-.92 E F1
-(\()2.883 E F0(m)A F1 2.883(\)o)C .383(ption is set in the con\214guration)
-377.915 694.8 R(\214le, this beha)102 706.8 Q(viour is suppressed.)-.2 E
-(Some sites lik)5 E 2.5(et)-.1 G 2.5(or)305.31 706.8 S(un the)316.14 706.8 Q/F3
-9/Times-Roman@0 SF(SMTP)2.5 E F1(daemon with)2.5 E F0<ad6d>2.5 E F1(.)A EP
-%%Page: 28 23
-%%BeginPageSetup
-BP
-%%EndPageSetup
-/F0 10/Times-Bold@0 SF 193.36(SMM:08-28 Sendmail)72 60 R
-(Installation and Operation Guide)2.5 E 2.5(5. THE)72 96 R
-(WHOLE SCOOP ON THE CONFIGURA)2.5 E(TION FILE)-.95 E/F1 10/Times-Roman@0 SF
-(This section describes the con\214guration \214le in detail.)112 112.2 Q .648
-(There is one point that should be made clear immediately: the syntax of the c\
-on\214guration \214le is)112 128.4 R 1.076
-(designed to be reasonably easy to parse, since this is done e)87 140.4 R -.15
-(ve)-.25 G 1.077(ry time).15 F/F2 10/Times-Italic@0 SF(sendmail)3.577 E F1
-1.077(starts up, rather than)3.577 F(easy for a human to read or write.)87
-152.4 Q
-(On the \231future project\232 list is a con\214guration-\214le compiler)5 E(.)
--.55 E .243(The con\214guration \214le is or)112 168.6 R -.05(ga)-.18 G .243
-(nized as a series of lines, each of which be).05 F .243
-(gins with a single charac-)-.15 F .102
-(ter de\214ning the semantics for the rest of the line.)87 180.6 R .102
-(Lines be)5.102 F .102(ginning with a space or a tab are continuation)-.15 F
-1.323(lines \(although the semantics are not well de\214ned in man)87 192.6 R
-3.823(yp)-.15 G 3.822(laces\). Blank)340.61 192.6 R 1.322(lines and lines be)
-3.822 F(ginning)-.15 E(with a sharp symbol \(`#'\) are comments.)87 204.6 Q F0
-2.5(5.1. R)87 228.6 R(and S \212 Rewriting Rules)2.5 E F1 .465
-(The core of address parsing are the re)127 244.8 R .466(writing rules.)-.25 F
-.466(These are an ordered production system.)5.466 F F2(Sendmail)102 256.8 Q F1
-.19(scans through the set of re)2.69 F .19
-(writing rules looking for a match on the left hand side \(LHS\) of)-.25 F
-(the rule.)102 268.8 Q(When a rule matches, the address is replaced by the rig\
-ht hand side \(RHS\) of the rule.)5 E .921(There are se)127 285 R -.15(ve)-.25
-G .921(ral sets of re).15 F .921(writing rules.)-.25 F .921(Some of the re)
-5.921 F .922(writing sets are used internally and)-.25 F .36(must ha)102 297 R
-.66 -.15(ve s)-.2 H .36(peci\214c semantics.).15 F .359(Other re)5.359 F .359
-(writing sets do not ha)-.25 F .659 -.15(ve s)-.2 H .359
-(peci\214cally assigned semantics, and).15 F
-(may be referenced by the mailer de\214nitions or by other re)102 309 Q
-(writing sets.)-.25 E(The syntax of these tw)127 325.2 Q 2.5(oc)-.1 G
-(ommands are:)229.38 325.2 Q F0(S)142 341.4 Q F2(n)A F1 .248
-(Sets the current ruleset being collected to)102 357.6 R F2(n)2.748 E F1 5.248
-(.I)C 2.748(fy)287.284 357.6 S .248(ou be)298.362 357.6 R .249
-(gin a ruleset more than once it deletes the old)-.15 F(de\214nition.)102 369.6
-Q F0(R)142 385.8 Q F2(lhs rhs comments)A F1 1.185(The \214elds must be separat\
-ed by at least one tab character; there may be embedded spaces in the)102 402 R
-2.594(\214elds. The)102 414 R F2(lhs)2.594 E F1 .095
-(is a pattern that is applied to the input.)2.594 F .095
-(If it matches, the input is re)5.095 F .095(written to the)-.25 F F2(rhs)2.595
-E F1(.)A(The)102 426 Q F2(comments)2.5 E F1(are ignored.)2.5 E .755(Macro e)127
-442.2 R .755(xpansions of the form)-.15 F F0($)3.255 E F2(x)A F1 .755
-(are performed when the con\214guration \214le is read.)3.255 F(Expan-)5.755 E
-.283(sions of the form)102 454.2 R F0($&)2.783 E F2(x)A F1 .284
-(are performed at run time using a some)2.783 F .284
-(what less general algorithm.)-.25 F .284(This for)5.284 F
-(is intended only for referencing internally de\214ned macros such as)102 466.2
-Q F0($h)2.5 E F1(that are changed at runtime.)2.5 E F0 2.5(5.1.1. The)102 490.2
-R(left hand side)2.5 E F1 2.771(The left hand side of re)142 506.4 R 2.771
-(writing rules contains a pattern.)-.25 F 2.77(Normal w)7.771 F 2.77
-(ords are simply)-.1 F(matched directly)117 518.4 Q 5(.M)-.65 G
-(etasyntax is introduced using a dollar sign.)199.67 518.4 Q
-(The metasymbols are:)5 E F0($*)157 534.6 Q F1(Match zero or more tok)177.14
-534.6 Q(ens)-.1 E F0($+)157 546.6 Q F1(Match one or more tok)9.44 E(ens)-.1 E
-F0<24ad>157 558.6 Q F1(Match e)9.44 E(xactly one tok)-.15 E(en)-.1 E F0($=)157
-570.6 Q F2(x)A F1(Match an)5 E 2.5(yp)-.15 G(hrase in class)226.98 570.6 Q F2
-(x)2.5 E F0($~)157 582.6 Q F2(x)A F1(Match an)7.37 E 2.5(yw)-.15 G
-(ord not in class)229.1 582.6 Q F2(x)2.5 E F1 .131(If an)117 598.8 R 2.631(yo)
--.15 G 2.631(ft)148.212 598.8 S .131(hese match, the)156.953 598.8 R 2.631(ya)
--.15 G .132(re assigned to the symbol)231.066 598.8 R F0($)2.632 E F2(n)A F1
-.132(for replacement on the right hand side,)2.632 F(where)117 610.8 Q F2(n)2.5
-E F1(is the inde)2.5 E 2.5(xi)-.15 G 2.5(nt)202.67 610.8 S(he LHS.)212.95 610.8
-Q -.15(Fo)5 G 2.5(re).15 G(xample, if the LHS:)271.81 610.8 Q($\255:$+)157 627
-Q(is applied to the input:)117 643.2 Q(UCB)157 659.4 Q(ARP)-.35 E(A:eric)-.92 E
-(the rule will match, and the v)117 675.6 Q(alues passed to the RHS will be:)
--.25 E 7.5($1 UCB)157 691.8 R(ARP)-.35 E(A)-.92 E 7.5($2 eric)157 703.8 R EP
-%%Page: 29 24
-%%BeginPageSetup
-BP
-%%EndPageSetup
-/F0 10/Times-Bold@0 SF(Sendmail Installation and Operation Guide)72 60 Q
-(SMM:08-29)452.9 60 Q/F1 10/Times-Roman@0 SF(Additionally)142 96 Q 2.705(,t)
--.65 G .204(he LHS can include)199.895 96 R F0($@)2.704 E F1 .204
-(to match zero tok)2.704 F 2.704(ens. This)-.1 F(is)2.704 E/F2 10
-/Times-Italic@0 SF(not)2.704 E F1 .204(bound to a)2.704 F F0($)2.704 E F2(n)A
-F1(on)2.704 E(the RHS, and is normally only used when it stands alone in order\
- to match the null input.)117 108 Q F0 2.5(5.1.2. The)102 132 R
-(right hand side)2.5 E F1 .648(When the left hand side of a re)142 148.2 R .649
-(writing rule matches, the input is deleted and replaced by)-.25 F 1.037
-(the right hand side.)117 160.2 R -.8(To)6.037 G -.1(ke).8 G 1.036
-(ns are copied directly from the RHS unless the).1 F 3.536(yb)-.15 G -.15(eg)
-430.772 160.2 S 1.036(in with a dollar).15 F 2.5(sign. Metasymbols)117 172.2 R
-(are:)2.5 E F0($)157 188.4 Q F2(n)A F1(Substitute inde\214nite tok)207.55 188.4
-Q(en)-.1 E F2(n)2.5 E F1(from LHS)2.5 E F0($[)157 200.4 Q F2(name)A F0($])A F1
-(Canonicalize)207.55 200.4 Q F2(name)2.5 E F0($\()157 212.4 Q F2(map k)A -.3
-(ey)-.1 G F0($@)2.8 E F2(ar)A(guments)-.37 E F0($:)2.5 E F2(default)A F0($\))
-2.5 E F1(Generalized k)207.55 224.4 Q -.15(ey)-.1 G(ed mapping function).15 E
-F0($>)157 236.4 Q F2(n)A F1(\231Call\232 ruleset)207.55 236.4 Q F2(n)2.5 E F0
-($#)157 248.4 Q F2(mailer)A F1(Resolv)207.55 248.4 Q 2.5(et)-.15 G(o)244.9
-248.4 Q F2(mailer)2.5 E F0($@)157 260.4 Q F2(host)A F1(Specify)207.55 260.4 Q
-F2(host)2.5 E F0($:)157 272.4 Q F2(user)A F1(Specify)207.55 272.4 Q F2(user)2.5
-E F1(The)142 292.8 Q F0($)3.136 E F2(n)A F1 .637
-(syntax substitutes the corresponding v)3.136 F .637(alue from a)-.25 F F0($+)
-3.137 E F1(,)A F0<24ad>3.137 E F1(,)A F0($*)3.137 E F1(,)A F0($=)3.137 E F1
-3.137(,o)C(r)448.489 292.8 Q F0($~)3.137 E F1 .637(match on)3.137 F(the LHS.)
-117 304.8 Q(It may be used an)5 E(ywhere.)-.15 E 2.706(Ah)142 321 S .206
-(ost name enclosed between)156.926 321 R F0($[)2.706 E F1(and)2.706 E F0($])
-2.706 E F1 .206(is look)2.706 F .205
-(ed up in the host database\(s\) and replaced)-.1 F 1.683
-(by the canonical name)117 335 R/F3 7/Times-Roman@0 SF(13)211.749 331 Q F1
-6.683(.F)218.749 335 S 1.683(or e)233.342 335 R 1.683
-(xample, \231$[ftp$]\232 might become \231ftp.CS.Berk)-.15 F(ele)-.1 E -.65(y.)
--.15 G 1.683(EDU\232 and).65 F 2.707(\231$[[128.32.130.2]$]\232 w)117 347 R
-2.707(ould become \231v)-.1 F(angogh.CS.Berk)-.25 E(ele)-.1 E -.65(y.)-.15 G
-(EDU.).65 E<9a>-.7 E F2(Sendmail)7.707 E F1 2.706(recognizes it')5.206 F(s)-.55
-E(numeric IP address without calling the name serv)117 359 Q
-(er and replaces it with it')-.15 E 2.5(sc)-.55 G(anonical name.)424.3 359 Q
-(The)142 375.2 Q F0($\()3.003 E F1(...)3.003 E F0($\))5.503 E F1 .503
-(syntax is a more general form of lookup; it uses a named map instead of an)
-3.003 F .81(implicit map.)117 387.2 R .81(If no lookup is found, the indicated)
-5.81 F F2(default)3.309 E F1 .809(is inserted; if no def)3.309 F .809
-(ault is speci\214ed)-.1 F .775(and no lookup matches, the v)117 399.2 R .776
-(alue is left unchanged.)-.25 F(The)5.776 E F2(ar)3.276 E(guments)-.37 E F1
-.776(are passed to the map for)3.276 F(possible use.)117 411.2 Q(The)142 427.4
-Q F0($>)2.62 E F2(n)A F1 .119(syntax causes the remainder of the line to be su\
-bstituted as usual and then passed)2.62 F .586(as the ar)117 439.4 R .586
-(gument to ruleset)-.18 F F2(n)3.086 E F1 5.586(.T)C .586(he \214nal v)244.206
-439.4 R .586(alue of ruleset)-.25 F F2(n)3.087 E F1 .587
-(then becomes the substitution for this)3.087 F 3.075(rule. The)117 451.4 R F0
-($>)3.075 E F1 .575(syntax can only be used at the be)3.075 F .575
-(ginning of the right hand side; it can be only be)-.15 F(preceded by)117 463.4
-Q F0($@)2.5 E F1(or)2.5 E F0($:)2.5 E F1(.)A(The)142 479.6 Q F0($#)2.507 E F1
-.007(syntax should)2.507 F F2(only)2.507 E F1 .008
-(be used in ruleset zero or a subroutine of ruleset zero.)2.507 F .008
-(It causes)5.008 F -.25(eva)117 491.6 S .685
-(luation of the ruleset to terminate immediately).25 F 3.184(,a)-.65 G .684
-(nd signals to)329.502 491.6 R F2(sendmail)3.184 E F1 .684
-(that the address has)3.184 F(completely resolv)117 503.6 Q 2.5(ed. The)-.15 F
-(complete syntax is:)2.5 E F0($#)157 519.8 Q F2(mailer)A F0($@)2.5 E F2(host)A
-F0($:)2.5 E F2(user)A F1 1.394(This speci\214es the {mailer)117 536 R 3.894(,h)
--.4 G 1.394(ost, user} 3-tuple necessary to direct the mailer)234.466 536 R
-6.394(.I)-.55 G 3.894(ft)444.548 536 S 1.394(he mailer is)454.552 536 R .774
-(local the host part may be omitted)117 550 R F3(14)257.744 546 Q F1 5.774(.T)
-264.744 550 S(he)279.128 550 Q F2(mailer)3.274 E F1 .775(must be a single w)
-3.274 F .775(ord, b)-.1 F .775(ut the)-.2 F F2(host)3.275 E F1(and)3.275 E F2
-(user)3.275 E F1 .253(may be multi-part.)117 562 R .253(If the)5.253 F F2
-(mailer)2.753 E F1 .253(is the b)2.753 F .253(uiltin IPC mailer)-.2 F 2.753(,t)
--.4 G(he)354.733 562 Q F2(host)2.753 E F1 .253(may be a colon-separated list)
-2.753 F .5(of hosts that are searched in order for the \214rst w)117 574 R .5
-(orking address \(e)-.1 F .5(xactly lik)-.15 F 3(eM)-.1 G 3(Xr)437.47 574 S 3
-(ecords\). The)451.02 574 R F2(user)117 586 Q F1 .036(is later re)2.536 F .036
-(written by the mailer)-.25 F .036(-speci\214c en)-.2 F -.15(ve)-.4 G .036
-(lope re).15 F .036(writing set and assigned to the)-.25 F F0($u)2.536 E F1
-(macro.)2.536 E .492(As a special case, if the v)117 598 R .492(alue to)-.25 F
-F0($#)2.992 E F1 .492(is \231local\232 and the \214rst character of the)2.992 F
-F0($:)2.992 E F1 -.25(va)2.992 G .492(lue is \231@\232, the).25 F .017
-(\231@\232 is stripped of)117 610 R .017(f, and a \215ag is set in the address\
- descriptor that causes sendmail to not do rule-)-.25 F(set 5 processing.)117
-622 Q(Normally)142 638.2 Q 3.251(,ar)-.65 G .751
-(ule that matches is retried, that is, the rule loops until it f)196.452 638.2
-R 3.252(ails. A)-.1 F .752(RHS may)3.252 F 1.086(also be preceded by a)117
-650.2 R F0($@)3.586 E F1 1.085(or a)3.585 F F0($:)3.585 E F1 1.085
-(to change this beha)3.585 F(vior)-.2 E 6.085(.A)-.55 G F0($@)375.685 650.2 Q
-F1 1.085(pre\214x causes the ruleset to)3.585 F .32 LW 76 659.8 72 659.8 DL 80
-659.8 76 659.8 DL 84 659.8 80 659.8 DL 88 659.8 84 659.8 DL 92 659.8 88 659.8
-DL 96 659.8 92 659.8 DL 100 659.8 96 659.8 DL 104 659.8 100 659.8 DL 108 659.8
-104 659.8 DL 112 659.8 108 659.8 DL 116 659.8 112 659.8 DL 120 659.8 116 659.8
-DL 124 659.8 120 659.8 DL 128 659.8 124 659.8 DL 132 659.8 128 659.8 DL 136
-659.8 132 659.8 DL 140 659.8 136 659.8 DL 144 659.8 140 659.8 DL 148 659.8 144
-659.8 DL 152 659.8 148 659.8 DL 156 659.8 152 659.8 DL 160 659.8 156 659.8 DL
-164 659.8 160 659.8 DL 168 659.8 164 659.8 DL 172 659.8 168 659.8 DL 176 659.8
-172 659.8 DL 180 659.8 176 659.8 DL 184 659.8 180 659.8 DL 188 659.8 184 659.8
-DL 192 659.8 188 659.8 DL 196 659.8 192 659.8 DL 200 659.8 196 659.8 DL 204
-659.8 200 659.8 DL 208 659.8 204 659.8 DL 212 659.8 208 659.8 DL 216 659.8 212
-659.8 DL/F4 5/Times-Roman@0 SF(13)93.6 670.2 Q/F5 8/Times-Roman@0 SF
-(This is actually completely equi)3.2 I -.2(va)-.2 G(lent to $\(host).2 E/F6 8
-/Times-Italic@0 SF(hostname)2 E F5 2($\). In)B(particular)2 E 2(,a)-.32 G/F7 8
-/Times-Bold@0 SF($:)A F5(def)2 E(ault can be used.)-.08 E F4(14)93.6 683.8 Q F5
--.88(Yo)3.2 K 2.726(um).88 G .726(ay w)120.446 687 R .726
-(ant to use it for special \231per user\232 e)-.08 F 2.726(xtensions. F)-.12 F
-.726(or e)-.12 F .725
-(xample, in the address \231jgm+foo@CMU.EDU\232; the \231+foo\232)-.12 F(part \
-is not part of the user name, and is passed to the local mailer for local use.)
-72 696.6 Q EP
-%%Page: 30 25
-%%BeginPageSetup
-BP
-%%EndPageSetup
-/F0 10/Times-Bold@0 SF 193.36(SMM:08-30 Sendmail)72 60 R
-(Installation and Operation Guide)2.5 E/F1 10/Times-Roman@0 SF 1.46
-(return with the remainder of the RHS as the v)117 96 R 3.96(alue. A)-.25 F F0
-($:)3.96 E F1 1.46(pre\214x causes the rule to terminate)3.96 F(immediately)117
-108 Q 3.756(,b)-.65 G 1.256(ut the ruleset to continue; this can be used to a)
-177.406 108 R -.2(vo)-.2 G 1.256(id continued application of a).2 F 2.5
-(rule. The)117 120 R(pre\214x is stripped before continuing.)2.5 E(The)142
-136.2 Q F0($@)2.5 E F1(and)2.5 E F0($:)2.5 E F1(pre\214x)2.5 E
-(es may precede a)-.15 E F0($>)2.5 E F1(spec; for e)2.5 E(xample:)-.15 E 20.19
-(R$+ $:)157 152.4 R($>7 $1)2.5 E 1.256(matches an)117 168.6 R 1.256
-(ything, passes that to ruleset se)-.15 F -.15(ve)-.25 G 1.256
-(n, and continues; the).15 F F0($:)3.756 E F1 1.256(is necessary to a)3.756 F
--.2(vo)-.2 G 1.256(id an).2 F(in\214nite loop.)117 180.6 Q 1.205(Substitution \
-occurs in the order described, that is, parameters from the LHS are substi-)142
-196.8 R .219(tuted, hostnames are canonicalized, \231subroutines\232 are calle\
-d, and \214nally)117 208.8 R F0($#)2.719 E F1(,)A F0($@)2.719 E F1 2.72(,a)C
-(nd)448.64 208.8 Q F0($:)2.72 E F1 .22(are pro-)2.72 F(cessed.)117 220.8 Q F0
-2.5(5.1.3. Semantics)102 244.8 R(of r)2.5 E(ewriting rule sets)-.18 E F1 1.523
-(There are \214v)142 261 R 4.023(er)-.15 G -.25(ew)207.779 261 S 1.523
-(riting sets that ha).25 F 1.823 -.15(ve s)-.2 H 1.523(peci\214c semantics.).15
-F -.15(Fo)6.523 G 1.523(ur of these are related as).15 F
-(depicted by \214gure 1.)117 273 Q 1.029
-(Ruleset three should turn the address into \231canonical form.)142 289.2 R
-6.029<9a54>-.7 G 1.03(his form should ha)401.351 289.2 R 1.33 -.15(ve t)-.2 H
-(he).15 E(basic syntax:)117 301.2 Q(local-part@host-domain-spec)157 317.4 Q
-(Ruleset three is applied by)117 333.6 Q/F2 10/Times-Italic@0 SF(sendmail)2.5 E
-F1(before doing an)2.5 E(ything with an)-.15 E 2.5(ya)-.15 G(ddress.)396.39
-333.6 Q .302(If no \231@\232 sign is speci\214ed, then the host-domain-spec)142
-349.8 R F2(may)2.801 E F1 .301(be appended \(box \231D\232 in Fig-)2.801 F .577
-(ure 1\) from the sender address \(if the)117 361.8 R F0(C)3.077 E F1 .577
-(\215ag is set in the mailer de\214nition corresponding to the)3.077 F F2
-(sending)117 373.8 Q F1(mailer\).)2.5 E 1.021(Ruleset zero is applied after ru\
-leset three to addresses that are going to actually specify)142 390 R 3.663
-(recipients. It)117 402 R 1.163(must resolv)3.663 F 3.663(et)-.15 G 3.664(oa)
-232.602 402 S F2({mailer)A 3.664(,h)-1.11 G 1.164(ost, user})289.534 402 R F1
-3.664(triple. The)3.664 F F2(mailer)3.664 E F1 1.164(must be de\214ned in the)
-3.664 F .752(mailer de\214nitions from the con\214guration \214le.)117 414 R
-(The)5.751 E F2(host)3.251 E F1 .751(is de\214ned into the)3.251 F F0($h)3.251
-E F1 .751(macro for use in)3.251 F(the ar)117 426 Q(gv e)-.18 E
-(xpansion of the speci\214ed mailer)-.15 E(.)-.55 E .452(Rulesets one and tw)
-142 442.2 R 2.952(oa)-.1 G .452
-(re applied to all sender and recipient addresses respecti)235.918 442.2 R -.15
-(ve)-.25 G(ly).15 E 5.453(.T)-.65 G(he)489.71 442.2 Q(y)-.15 E
-(are applied before an)117 454.2 Q 2.5(ys)-.15 G
-(peci\214cation in the mailer de\214nition.)212.37 454.2 Q(The)5 E 2.5(ym)-.15
-G(ust ne)391.1 454.2 Q -.15(ve)-.25 G 2.5(rr).15 G(esolv)432.91 454.2 Q(e.)-.15
-E .4 LW 77 483.6 72 483.6 DL 79 483.6 74 483.6 DL 84 483.6 79 483.6 DL 89 483.6
-84 483.6 DL 94 483.6 89 483.6 DL 99 483.6 94 483.6 DL 104 483.6 99 483.6 DL 109
-483.6 104 483.6 DL 114 483.6 109 483.6 DL 119 483.6 114 483.6 DL 124 483.6 119
-483.6 DL 129 483.6 124 483.6 DL 134 483.6 129 483.6 DL 139 483.6 134 483.6 DL
-144 483.6 139 483.6 DL 149 483.6 144 483.6 DL 154 483.6 149 483.6 DL 159 483.6
-154 483.6 DL 164 483.6 159 483.6 DL 169 483.6 164 483.6 DL 174 483.6 169 483.6
-DL 179 483.6 174 483.6 DL 184 483.6 179 483.6 DL 189 483.6 184 483.6 DL 194
-483.6 189 483.6 DL 199 483.6 194 483.6 DL 204 483.6 199 483.6 DL 209 483.6 204
-483.6 DL 214 483.6 209 483.6 DL 219 483.6 214 483.6 DL 224 483.6 219 483.6 DL
-229 483.6 224 483.6 DL 234 483.6 229 483.6 DL 239 483.6 234 483.6 DL 244 483.6
-239 483.6 DL 249 483.6 244 483.6 DL 254 483.6 249 483.6 DL 259 483.6 254 483.6
-DL 264 483.6 259 483.6 DL 269 483.6 264 483.6 DL 274 483.6 269 483.6 DL 279
-483.6 274 483.6 DL 284 483.6 279 483.6 DL 289 483.6 284 483.6 DL 294 483.6 289
-483.6 DL 299 483.6 294 483.6 DL 304 483.6 299 483.6 DL 309 483.6 304 483.6 DL
-314 483.6 309 483.6 DL 319 483.6 314 483.6 DL 324 483.6 319 483.6 DL 329 483.6
-324 483.6 DL 334 483.6 329 483.6 DL 339 483.6 334 483.6 DL 344 483.6 339 483.6
-DL 349 483.6 344 483.6 DL 354 483.6 349 483.6 DL 359 483.6 354 483.6 DL 364
-483.6 359 483.6 DL 369 483.6 364 483.6 DL 374 483.6 369 483.6 DL 379 483.6 374
-483.6 DL 384 483.6 379 483.6 DL 389 483.6 384 483.6 DL 394 483.6 389 483.6 DL
-399 483.6 394 483.6 DL 404 483.6 399 483.6 DL 409 483.6 404 483.6 DL 414 483.6
-409 483.6 DL 419 483.6 414 483.6 DL 424 483.6 419 483.6 DL 429 483.6 424 483.6
-DL 434 483.6 429 483.6 DL 439 483.6 434 483.6 DL 444 483.6 439 483.6 DL 449
-483.6 444 483.6 DL 454 483.6 449 483.6 DL 459 483.6 454 483.6 DL 464 483.6 459
-483.6 DL 469 483.6 464 483.6 DL 474 483.6 469 483.6 DL 479 483.6 474 483.6 DL
-484 483.6 479 483.6 DL 489 483.6 484 483.6 DL 494 483.6 489 483.6 DL 499 483.6
-494 483.6 DL 504 483.6 499 483.6 DL(addr)91.915 578 Q 133.2 576 111.6 576 DL
-133.2 576 126 577.8 DL 133.2 576 126 574.2 DL(3)141.5 578 Q 133.2 565.2 133.2
-586.8 DL 154.8 565.2 133.2 565.2 DL 154.8 586.8 154.8 565.2 DL 133.2 586.8
-154.8 586.8 DL 176.4 576 154.8 576 DL 176.4 576 169.2 577.8 DL 176.4 576 169.2
-574.2 DL(D)183.59 578 Q 176.4 565.2 176.4 586.8 DL 198 565.2 176.4 565.2 DL 198
-586.8 198 565.2 DL 176.4 586.8 198 586.8 DL 219.6 576 198 576 DL 277.2 558
-255.6 558 DL 277.2 558 270 559.8 DL 277.2 558 270 556.2 DL(1)285.5 560 Q 277.2
-547.2 277.2 568.8 DL 298.8 547.2 277.2 547.2 DL 298.8 568.8 298.8 547.2 DL
-277.2 568.8 298.8 568.8 DL 320.4 558 298.8 558 DL 320.4 558 313.2 559.8 DL
-320.4 558 313.2 556.2 DL(S)328.42 560 Q 320.4 547.2 320.4 568.8 DL 342 547.2
-320.4 547.2 DL 342 568.8 342 547.2 DL 320.4 568.8 342 568.8 DL 363.6 558 342
-558 DL 277.2 594 255.6 594 DL 277.2 594 270 595.8 DL 277.2 594 270 592.2 DL(2)
-285.5 596 Q 277.2 583.2 277.2 604.8 DL 298.8 583.2 277.2 583.2 DL 298.8 604.8
-298.8 583.2 DL 277.2 604.8 298.8 604.8 DL 320.4 594 298.8 594 DL 320.4 594
-313.2 595.8 DL 320.4 594 313.2 592.2 DL(R)327.865 596 Q 320.4 583.2 320.4 604.8
-DL 342 583.2 320.4 583.2 DL 342 604.8 342 583.2 DL 320.4 604.8 342 604.8 DL
-363.6 594 342 594 DL 421.2 576 399.6 576 DL 421.2 576 414 577.8 DL 421.2 576
-414 574.2 DL(4)429.5 578 Q 421.2 565.2 421.2 586.8 DL 442.8 565.2 421.2 565.2
-DL 442.8 586.8 442.8 565.2 DL 421.2 586.8 442.8 586.8 DL 464.4 576 442.8 576 DL
-464.4 576 457.2 577.8 DL 464.4 576 457.2 574.2 DL(msg)466.865 578 Q 255.6 558
-219.6 576 DL 255.6 594 219.6 576 DL 399.6 576 363.6 558 DL 399.6 576 363.6 594
-DL 208.8 522 187.2 522 DL 208.8 522 201.6 523.8 DL 208.8 522 201.6 520.2 DL(0)
-217.1 524 Q 208.8 511.2 208.8 532.8 DL 230.4 511.2 208.8 511.2 DL 230.4 532.8
-230.4 511.2 DL 208.8 532.8 230.4 532.8 DL 252 522 230.4 522 DL 252 522 244.8
-523.8 DL 252 522 244.8 520.2 DL(resolv)265.69 524 Q(ed address)-.15 E 187.2 522
-162 576 DL(Figure 1 \212 Re)216.045 624 Q(writing set semantics)-.25 E 2.5
-(D\212s)209.35 636 S(ender domain addition)235.46 636 Q 2.5(S\212m)209.35 648 S
-(ailer)237.69 648 Q(-speci\214c sender re)-.2 E(writing)-.25 E 2.5(R\212m)
-209.35 660 S(ailer)238.8 660 Q(-speci\214c recipient re)-.2 E(writing)-.25 E 77
-672 72 672 DL 79 672 74 672 DL 84 672 79 672 DL 89 672 84 672 DL 94 672 89 672
-DL 99 672 94 672 DL 104 672 99 672 DL 109 672 104 672 DL 114 672 109 672 DL 119
-672 114 672 DL 124 672 119 672 DL 129 672 124 672 DL 134 672 129 672 DL 139 672
-134 672 DL 144 672 139 672 DL 149 672 144 672 DL 154 672 149 672 DL 159 672 154
-672 DL 164 672 159 672 DL 169 672 164 672 DL 174 672 169 672 DL 179 672 174 672
-DL 184 672 179 672 DL 189 672 184 672 DL 194 672 189 672 DL 199 672 194 672 DL
-204 672 199 672 DL 209 672 204 672 DL 214 672 209 672 DL 219 672 214 672 DL 224
-672 219 672 DL 229 672 224 672 DL 234 672 229 672 DL 239 672 234 672 DL 244 672
-239 672 DL 249 672 244 672 DL 254 672 249 672 DL 259 672 254 672 DL 264 672 259
-672 DL 269 672 264 672 DL 274 672 269 672 DL 279 672 274 672 DL 284 672 279 672
-DL 289 672 284 672 DL 294 672 289 672 DL 299 672 294 672 DL 304 672 299 672 DL
-309 672 304 672 DL 314 672 309 672 DL 319 672 314 672 DL 324 672 319 672 DL 329
-672 324 672 DL 334 672 329 672 DL 339 672 334 672 DL 344 672 339 672 DL 349 672
-344 672 DL 354 672 349 672 DL 359 672 354 672 DL 364 672 359 672 DL 369 672 364
-672 DL 374 672 369 672 DL 379 672 374 672 DL 384 672 379 672 DL 389 672 384 672
-DL 394 672 389 672 DL 399 672 394 672 DL 404 672 399 672 DL 409 672 404 672 DL
-414 672 409 672 DL 419 672 414 672 DL 424 672 419 672 DL 429 672 424 672 DL 434
-672 429 672 DL 439 672 434 672 DL 444 672 439 672 DL 449 672 444 672 DL 454 672
-449 672 DL 459 672 454 672 DL 464 672 459 672 DL 469 672 464 672 DL 474 672 469
-672 DL 479 672 474 672 DL 484 672 479 672 DL 489 672 484 672 DL 494 672 489 672
-DL 499 672 494 672 DL 504 672 499 672 DL EP
-%%Page: 31 26
-%%BeginPageSetup
-BP
-%%EndPageSetup
-/F0 10/Times-Bold@0 SF(Sendmail Installation and Operation Guide)72 60 Q
-(SMM:08-31)452.9 60 Q/F1 10/Times-Roman@0 SF 1.266
-(Ruleset four is applied to all addresses in the message.)142 96 R 1.265
-(It is typically used to translate)6.265 F(internal to e)117 108 Q
-(xternal form.)-.15 E F0 2.5(5.1.4. IPC)102 132 R(mailers)2.5 E F1 1.332
-(Some special processing occurs if the ruleset zero resolv)142 148.2 R 1.333
-(es to an IPC mailer \(that is, a)-.15 F 1.179
-(mailer that has \231[IPC]\232 listed as the P)117 160.2 R 1.179(ath in the)
--.15 F F0(M)3.679 E F1 1.179(con\214guration line.)3.679 F 1.178
-(The host name passed)6.178 F .168(after \231$@\232 has MX e)117 172.2 R .168
-(xpansion performed; this looks the name up in DNS to \214nd alternate deli)
--.15 F(v-)-.25 E(ery sites.)117 184.2 Q(The host name can also be pro)142 200.4
-Q(vided as a dotted quad in square brack)-.15 E(ets; for e)-.1 E(xample:)-.15 E
-([128.32.149.78])157 216.6 Q(This causes direct con)117 232.8 Q -.15(ve)-.4 G
-(rsion of the numeric v).15 E(alue to a TCP/IP host address.)-.25 E .214(The h\
-ost name passed in after the \231$@\232 may also be a colon-separated list of \
-hosts.)142 249 R(Each)5.213 E .484(is separately MX e)117 261 R .484
-(xpanded and the results are concatenated to mak)-.15 F 2.985(e\()-.1 G .485
-(essentially\) one long MX)401.165 261 R 3.465(list. The)117 273 R .964
-(intent here is to create \231f)3.465 F(ak)-.1 E .964
-(e\232 MX records that are not published in DNS for pri)-.1 F -.25(va)-.25 G
-(te).25 E(internal netw)117 285 Q(orks.)-.1 E
-(As a \214nal special case, the host name can be passed in as a te)142 301.2 Q
-(xt string in square brack)-.15 E(ets:)-.1 E([ucb)157 317.4 Q -.25(va)-.15 G
-(x.berk).25 E(ele)-.1 E -.65(y.)-.15 G(edu]).65 E .312(This form a)117 333.6 R
--.2(vo)-.2 G .312(ids the MX mapping.).2 F F0(N.B.:)5.312 E/F2 10
-/Times-Italic@0 SF .313(This is intended only for situations wher)2.812 F 2.813
-(ey)-.37 G .313(ou have a)464.494 333.6 R .338(network \214r)117 345.6 R -.15
-(ew)-.37 G .337(all or other host that will do special pr).15 F .337
-(ocessing for all your mail, so that your MX)-.45 F -.37(re)117 357.6 S(cor).37
-E 3.958(dp)-.37 G 1.458(oints to a gate)151.878 357.6 R 1.458(way mac)-.15 F
-1.458(hine; this mac)-.15 F 1.459(hine could then do dir)-.15 F 1.459
-(ect delivery to mac)-.37 F(hines)-.15 E .09(within your local domain.)117
-369.6 R .09(Use of this featur)5.09 F 2.59(ed)-.37 G(ir)306.8 369.6 Q .09
-(ectly violates RFC 1123 section 5.3.5: it should)-.37 F(not be used lightly)
-117 381.6 Q(.)-.55 E F0 2.5(5.2. D)87 405.6 R 2.5<8a44>2.5 G(e\214ne Macr)
-136.44 405.6 Q(o)-.18 E F1 .081
-(Macros are named with a single character or with a w)127 421.8 R .082
-(ord in {braces}.)-.1 F .082(Single character names)5.082 F .45
-(may be selected from the entire ASCII set, b)102 433.8 R .45(ut user)-.2 F .45
-(-de\214ned macros should be selected from the set)-.2 F .446
-(of upper case letters only)102 445.8 R 5.446(.L)-.65 G -.25(ow)217.72 445.8 S
-.446(er case letters and special symbols are used internally).25 F 5.446(.L)
--.65 G .446(ong names)460.504 445.8 R(be)102 457.8 Q .913(ginning with a lo)
--.15 F .913(wer case letter or a punctuation character are reserv)-.25 F .912
-(ed for use by sendmail, so)-.15 F(user)102 469.8 Q
-(-de\214ned long macro names should be)-.2 E(gin with an upper case letter)-.15
-E(.)-.55 E(The syntax for macro de\214nitions is:)127 486 Q F0(D)142 502.2 Q F2
-1.666(xv)C(al)-1.666 E F1(where)102 518.4 Q F2(x)3.068 E F1 .568
-(is the name of the macro \(which may be a single character or a w)3.068 F .569
-(ord in braces\) and)-.1 F F2(val)3.069 E F1(is)3.069 E .479(the v)102 530.4 R
-.479(alue it should ha)-.25 F -.15(ve)-.2 G 5.479(.T).15 G .478
-(here should be no spaces gi)212.395 530.4 R -.15(ve)-.25 G 2.978(nt).15 G .478
-(hat do not actually belong in the macro)344.284 530.4 R -.25(va)102 542.4 S
-(lue.).25 E .494(Macros are interpolated using the construct)127 558.6 R F0($)
-2.994 E F2(x)A F1 2.994(,w)C(here)327.638 558.6 Q F2(x)2.994 E F1 .494
-(is the name of the macro to be inter)2.994 F(-)-.2 E 2.933(polated. This)102
-570.6 R .433(interpolation is done when the con\214guration \214le is read, e)
-2.933 F .432(xcept in)-.15 F F0(M)2.932 E F1 2.932(lines. The)2.932 F(spe-)
-2.932 E(cial construct)102 582.6 Q F0($&)2.5 E F2(x)A F1(can be used in)2.5 E
-F0(R)2.5 E F1(lines to get deferred interpolation.)2.5 E
-(Conditionals can be speci\214ed using the syntax:)127 598.8 Q($?x te)142 615 Q
-(xt1 $| te)-.15 E(xt2 $.)-.15 E 1.127(This interpolates)102 631.2 R F2(te)3.627
-E(xt1)-.2 E F1 1.127(if the macro)3.627 F F0($x)3.627 E F1 1.128(is set, and)
-3.628 F F2(te)3.628 E(xt2)-.2 E F1 3.628(otherwise. The)3.628 F 1.128
-(\231else\232 \()3.628 F F0($|)A F1 3.628(\)c)C 1.128(lause may be)449.534
-631.2 R(omitted.)102 643.2 Q(Lo)127 659.4 Q .58
-(wer case macro names are reserv)-.25 F .58(ed to ha)-.15 F .88 -.15(ve s)-.2 H
-.58(pecial semantics, used to pass information in).15 F 1.56(or out of)102
-671.4 R F2(sendmail)4.06 E F1 4.06(,a)C 1.561(nd special characters are reserv)
-190.73 671.4 R 1.561(ed to pro)-.15 F 1.561(vide conditionals, etc.)-.15 F
-1.561(Upper case)6.561 F(names \(that is,)102 683.4 Q F0($A)2.5 E F1(through)
-2.5 E F0($Z)2.5 E F1 2.5(\)a)C(re speci\214cally reserv)232.82 683.4 Q
-(ed for con\214guration \214le authors.)-.15 E 1.303(The follo)127 699.6 R
-1.303(wing macros are de\214ned and/or used internally by)-.25 F F2(sendmail)
-3.802 E F1 1.302(for interpolation into)3.802 F EP
-%%Page: 32 27
-%%BeginPageSetup
-BP
-%%EndPageSetup
-/F0 10/Times-Bold@0 SF 193.36(SMM:08-32 Sendmail)72 60 R
-(Installation and Operation Guide)2.5 E/F1 10/Times-Roman@0 SF(ar)102 98 Q(gv')
--.18 E 2.792(sf)-.55 G .292(or mailers or for other conte)132.382 98 R 2.793
-(xts. The)-.15 F .293(ones mark)2.793 F .293
-(ed \207 are information passed into sendmail)-.1 F/F2 7/Times-Roman@0 SF(15)
-494.5 94 Q F1(,)501.5 98 Q .036(the ones mark)102 110 R .036
-(ed \210 are information passed both in and out of sendmail, and the unmark)-.1
-F .035(ed macros are)-.1 F(passed out of sendmail b)102 122 Q
-(ut are not otherwise used internally)-.2 E 5(.T)-.65 G(hese macros are:)354.45
-122 Q 13.06($a The)102 138.2 R(origination date in RFC 822 format.)2.5 E
-(This is e)5 E(xtracted from the Date: line.)-.15 E 12.5($b The)102 154.4 R
-(current date in RFC 822 format.)2.5 E 13.06($c The)102 170.6 R .002
-(hop count.)2.502 F .002(This is a count of the number of Recei)5.002 F -.15
-(ve)-.25 G .003(d: lines plus the v).15 F .003(alue of the)-.25 F F0<ad68>2.503
-E F1(com-)2.503 E(mand line \215ag.)127 182.6 Q 12.5($d The)102 198.8 R
-(current date in UNIX \(ctime\) format.)2.5 E 8.06($e\207 \(Obsolete;)102 215 R
-1.814(use SmtpGreetingMessage option instead.\))4.314 F 1.814
-(The SMTP entry message.)6.814 F 1.814(This is)6.814 F 2.008
-(printed out when SMTP starts up.)127 227 R 2.008(The \214rst w)7.008 F 2.008
-(ord must be the)-.1 F F0($j)4.508 E F1 2.009(macro as speci\214ed by)4.508 F
-2.732(RFC821. Def)127 239 R .232(aults to \231$j Sendmail $v ready at $b\232.)
--.1 F .231(Commonly rede\214ned to include the con-)5.231 F(\214guration v)127
-251 Q(ersion number)-.15 E 2.5(,e)-.4 G
-(.g., \231$j Sendmail $v/$Z ready at $b\232)239.77 251 Q 14.17($f The)102 267.2
-R(en)2.5 E -.15(ve)-.4 G(lope sender \(from\) address.).15 E 12.5($g The)102
-283.4 R .017(sender address relati)2.517 F .317 -.15(ve t)-.25 H 2.517(ot).15 G
-.017(he recipient.)251.375 283.4 R -.15(Fo)5.017 G 2.517(re).15 G .018
-(xample, if)326.386 283.4 R F0($f)2.518 E F1 .018(is \231foo\232,)2.518 F F0
-($g)2.518 E F1 .018(will be \231host!foo\232,)2.518 F
-(\231foo@host.domain\232, or whate)127 295.4 Q -.15(ve)-.25 G 2.5(ri).15 G 2.5
-(sa)264.95 295.4 S(ppropriate for the recei)275.78 295.4 Q(ving mailer)-.25 E
-(.)-.55 E 12.5($h The)102 311.6 R(recipient host.)2.5 E
-(This is set in ruleset 0 from the $# \214eld of a parsed address.)5 E 14.72
-($i The)102 327.8 R(queue id, e.g., \231HAA12345\232.)2.5 E 9.72($j\210 The)102
-344 R(\231of)2.747 E .247(\214cial\232 domain name for this site.)-.25 F .247
-(This is fully quali\214ed if the full quali\214cation can be)5.247 F 3.093
-(found. It)127 356 R/F3 10/Times-Italic@0 SF(must)3.093 E F1 .594(be rede\214n\
-ed to be the fully quali\214ed domain name if your system is not con-)3.093 F
-(\214gured so that information can \214nd it automatically)127 368 Q(.)-.65 E
-12.5($k The)102 384.2 R(UUCP node name \(from the uname system call\).)2.5 E
-9.72($l\207 \(Obsolete;)102 400.4 R 1.282(use UnixFromLine option instead.\))
-3.782 F 1.282(The format of the UNIX from line.)6.282 F(Unless)6.281 E 1.409
-(you ha)127 412.4 R 1.709 -.15(ve c)-.2 H 1.409
-(hanged the UNIX mailbox format, you should not change the def).15 F 1.41
-(ault, which is)-.1 F(\231From $g)127 424.4 Q($d\232.)5 E 9.72($m The)102 440.6
-R .719(domain part of the)3.219 F F3 -.1(ge)3.219 G(thostname).1 E F1 .718
-(return v)3.219 F 3.218(alue. Under)-.25 F .718(normal circumstances,)3.218 F
-F0($j)3.218 E F1 .718(is equi)3.218 F(v-)-.25 E(alent to)127 452.6 Q F0($w)2.5
-E(.$m)-.7 E F1(.)A 7.5($n\207 The)102 468.8 R
-(name of the daemon \(for error messages\).)2.5 E(Def)5 E
-(aults to \231MAILER-D)-.1 E(AEMON\232.)-.4 E 7.5($o\207 \(Obsolete:)102 485 R
-.65(use OperatorChars option instead.\))3.15 F .651
-(The set of \231operators\232 in addresses.)5.651 F 3.151(Al)5.651 G .651
-(ist of)483.069 485 R .582(characters which will be considered tok)127 497 R
-.581(ens and which will separate tok)-.1 F .581(ens when doing pars-)-.1 F
-3.277(ing. F)127 509 R .777(or e)-.15 F .777(xample, if \231@\232 were in the)
--.15 F F0($o)3.278 E F1 .778(macro, then the input \231a@b\232 w)3.278 F .778
-(ould be scanned as)-.1 F .628(three tok)127 521 R .628(ens: \231a,)-.1 F 3.128
-<9a99>-.7 G(@,)204.724 521 Q 3.128<9a61>-.7 G .628(nd \231b)227.742 521 R 4.527
--.7(.\232 D)-.4 H(ef).7 E .627
-(aults to \231.:@[]\232, which is the minimum set necessary to)-.1 F .856(do R\
-FC 822 parsing; a richer set of operators is \231.:%@!/[]\232, which adds supp\
-ort for UUCP)127 533 R(,)-1.11 E(the %-hack, and X.400 addresses.)127 545 Q
-12.5($p Sendmail')102 561.2 R 2.5(sp)-.55 G(rocess id.)178.95 561.2 Q 7.5
-($q\207 Def)102 577.4 R .404(ault format of sender address.)-.1 F(The)5.404 E
-F0($q)2.903 E F1 .403(macro speci\214es ho)2.903 F 2.903(wa)-.25 G 2.903(na)
-388.955 577.4 S .403(ddress should appear in a)401.298 577.4 R 1.18
-(message when it is def)127 589.4 R 3.681(aulted. Def)-.1 F 1.181
-(aults to \231<$g>\232.)-.1 F 1.181(It is commonly rede\214ned to be \231$?x$x)
-6.181 F(<$g>$|$g$.)127 601.4 Q 5<9a6f>-.7 G 2.5<7299>186.52 601.4 S
-($g$?x \($x\)$.)196.79 601.4 Q(\232, corresponding to the follo)-.7 E(wing tw)
--.25 E 2.5(of)-.1 G(ormats:)403.21 601.4 Q(Eric Allman <eric@CS.Berk)167 617.6
-Q(ele)-.1 E -.65(y.)-.15 G(EDU>).65 E(eric@CS.Berk)167 629.6 Q(ele)-.1 E -.65
-(y.)-.15 G(EDU \(Eric Allman\)).65 E F3(Sendmail)127 645.8 Q F1
-(properly quotes names that ha)2.5 E .3 -.15(ve s)-.2 H
-(pecial characters if the \214rst form is used.).15 E 14.17($r Protocol)102 662
-R .977(used to recei)3.477 F 1.277 -.15(ve t)-.25 H .976(he message.).15 F .976
-(Set from the)5.976 F F0<ad70>3.476 E F1 .976
-(command line \215ag or by the SMTP)3.476 F(serv)127 674 Q(er code.)-.15 E .32
-LW 76 688.4 72 688.4 DL 80 688.4 76 688.4 DL 84 688.4 80 688.4 DL 88 688.4 84
-688.4 DL 92 688.4 88 688.4 DL 96 688.4 92 688.4 DL 100 688.4 96 688.4 DL 104
-688.4 100 688.4 DL 108 688.4 104 688.4 DL 112 688.4 108 688.4 DL 116 688.4 112
-688.4 DL 120 688.4 116 688.4 DL 124 688.4 120 688.4 DL 128 688.4 124 688.4 DL
-132 688.4 128 688.4 DL 136 688.4 132 688.4 DL 140 688.4 136 688.4 DL 144 688.4
-140 688.4 DL 148 688.4 144 688.4 DL 152 688.4 148 688.4 DL 156 688.4 152 688.4
-DL 160 688.4 156 688.4 DL 164 688.4 160 688.4 DL 168 688.4 164 688.4 DL 172
-688.4 168 688.4 DL 176 688.4 172 688.4 DL 180 688.4 176 688.4 DL 184 688.4 180
-688.4 DL 188 688.4 184 688.4 DL 192 688.4 188 688.4 DL 196 688.4 192 688.4 DL
-200 688.4 196 688.4 DL 204 688.4 200 688.4 DL 208 688.4 204 688.4 DL 212 688.4
-208 688.4 DL 216 688.4 212 688.4 DL/F4 5/Times-Roman@0 SF(15)93.6 698.8 Q/F5 8
-/Times-Roman@0 SF(As of v)3.2 I(ersion 8.6, all of these macros ha)-.12 E .24
--.12(ve r)-.16 H(easonable def).12 E 2(aults. Pre)-.08 F(vious v)-.2 E
-(ersions required that the)-.12 E 2(yb)-.12 G 2(ed)424.728 702 S(e\214ned.)
-434.28 702 Q EP
-%%Page: 33 28
-%%BeginPageSetup
-BP
-%%EndPageSetup
-/F0 10/Times-Bold@0 SF(Sendmail Installation and Operation Guide)72 60 Q
-(SMM:08-33)452.9 60 Q/F1 10/Times-Roman@0 SF 13.61($s Sender')102 96 R 2.5(sh)
--.55 G(ost name.)168.94 96 Q(Set from the)5 E F0<ad70>2.5 E F1
-(command line \215ag or by the SMTP serv)2.5 E(er code.)-.15 E 14.72($t A)102
-112.2 R(numeric representation of the current time.)2.5 E 12.5($u The)102 128.4
-R(recipient user)2.5 E(.)-.55 E 12.5($v The)102 144.6 R -.15(ve)2.5 G
-(rsion number of the).15 E/F2 10/Times-Italic@0 SF(sendmail)2.5 E F1(binary)2.5
-E(.)-.65 E 5.28($w\210 The)102 160.8 R(hostname of this site.)2.5 E
-(This is the root name of this host \(b)5 E(ut see belo)-.2 E 2.5(wf)-.25 G
-(or ca)432.64 160.8 Q -.15(ve)-.2 G(ats\).).15 E 12.5($x The)102 177 R
-(full name of the sender)2.5 E(.)-.55 E 13.06($z The)102 193.2 R
-(home directory of the recipient.)2.5 E 12.5($_ The)102 209.4 R -.25(va)2.5 G
-(lidated sender address.).25 E .749
-(There are three types of dates that can be used.)127 225.6 R(The)5.749 E F0
-($a)3.249 E F1(and)3.249 E F0($b)3.249 E F1 .749(macros are in RFC 822 for)
-3.249 F(-)-.2 E(mat;)102 237.6 Q F0($a)3.214 E F1 .714(is the time as e)3.214 F
-.713(xtracted from the \231Date:\232 line of the message \(if there w)-.15 F
-.713(as one\), and)-.1 F F0($b)3.213 E F1(is)3.213 E .056
-(the current date and time \(used for postmarks\).)102 249.6 R .057
-(If no \231Date:\232 line is found in the incoming message,)5.056 F F0($a)102
-261.6 Q F1 .305(is set to the current time also.)2.805 F(The)5.305 E F0($d)
-2.805 E F1 .304(macro is equi)2.805 F -.25(va)-.25 G .304(lent to the).25 F F0
-($b)2.804 E F1 .304(macro in UNIX \(ctime\) for)2.804 F(-)-.2 E(mat.)102 273.6
-Q .238(The macros)127 289.8 R F0($w)2.738 E F1(,)A F0($j)2.738 E F1 2.738(,a)C
-(nd)212.372 289.8 Q F0($m)2.738 E F1 .238
-(are set to the identity of this host.)2.738 F F2(Sendmail)5.239 E F1 .239
-(tries to \214nd the fully)2.739 F .335
-(quali\214ed name of the host if at all possible; it does this by calling)102
-301.8 R F2 -.1(ge)2.834 G(thostname).1 E F1 .334(\(2\) to get the current)B
-.457(hostname and then passing that to)102 313.8 R F2 -.1(ge)2.957 G
-(thostbyname).1 E F1 .457(\(3\) which is supposed to return the canonical v)B
-(er)-.15 E(-)-.2 E .279(sion of that host name.)102 327.8 R/F3 7/Times-Roman@0
-SF(16)193.946 323.8 Q F1 .279(Assuming this is successful,)203.725 327.8 R F0
-($j)2.778 E F1 .278(is set to the fully quali\214ed name and)2.778 F F0($m)
-2.778 E F1(is)2.778 E .706(set to the domain part of the name \(e)102 339.8 R
--.15(ve)-.25 G .706(rything after the \214rst dot\).).15 F(The)5.706 E F0($w)
-3.206 E F1 .706(macro is set to the \214rst)3.206 F -.1(wo)102 351.8 S .359
-(rd \(e).1 F -.15(ve)-.25 G .358(rything before the \214rst dot\) if you ha).15
-F .658 -.15(ve a l)-.2 H -2.15 -.25(ev e).15 H 2.858(l5o).25 G 2.858(rh)345
-351.8 S .358(igher con\214guration \214le; otherwise, it)356.188 351.8 R .404
-(is set to the same v)102 363.8 R .405(alue as)-.25 F F0($j)2.905 E F1 5.405
-(.I)C 2.905(ft)229.965 363.8 S .405
-(he canoni\214cation is not successful, it is imperati)238.98 363.8 R .705 -.15
-(ve t)-.25 H .405(hat the con\214g).15 F(\214le set)102 377.8 Q F0($j)2.5 E F1
-(to the fully quali\214ed domain name)2.5 E F3(17)279.77 373.8 Q F1(.)286.77
-377.8 Q(The)127 394 Q F0($f)2.833 E F1 .333(macro is the id of the sender as o\
-riginally determined; when mailing to a speci\214c host)2.833 F(the)102 406 Q
-F0($g)3.224 E F1 .724(macro is set to the address of the sender)3.224 F F2 -.37
-(re)3.225 G .725(lative to the r).37 F(ecipient.)-.37 E F1 -.15(Fo)5.725 G
-3.225(re).15 G .725(xample, if I send to)423.61 406 R
-(\231bollard@matisse.CS.Berk)102 418 Q(ele)-.1 E -.65(y.)-.15 G .425
-(EDU\232 from the machine \231v).65 F(angogh.CS.Berk)-.25 E(ele)-.1 E -.65(y.)
--.15 G .424(EDU\232 the).65 F F0($f)2.924 E F1(macro)2.924 E
-(will be \231eric\232 and the)102 430 Q F0($g)2.5 E F1
-(macro will be \231eric@v)2.5 E(angogh.CS.Berk)-.25 E(ele)-.1 E -.65(y.)-.15 G
-(EDU.).65 E<9a>-.7 E(The)127 446.2 Q F0($x)2.562 E F1 .062
-(macro is set to the full name of the sender)2.562 F 5.062(.T)-.55 G .062
-(his can be determined in se)338.824 446.2 R -.15(ve)-.25 G .063(ral w).15 F
-2.563(ays. It)-.1 F .63(can be passed as \215ag to)102 458.2 R F2(sendmail)3.13
-E F1 5.629(.I)C 3.129(tc)249.439 458.2 S .629(an be de\214ned in the)259.788
-458.2 R/F4 9/Times-Roman@0 SF -.315(NA)3.129 G(ME).315 E F1(en)3.129 E .629
-(vironment v)-.4 F 3.129(ariable. The)-.25 F(third)3.129 E .948
-(choice is the v)102 470.2 R .948
-(alue of the \231Full-Name:\232 line in the header if it e)-.25 F .949
-(xists, and the fourth choice is the)-.15 F .526
-(comment \214eld of a \231From:\232 line.)102 482.2 R .526(If all of these f)
-5.526 F .526(ail, and if the message is being originated locally)-.1 F(,)-.65 E
-(the full name is look)102 494.2 Q(ed up in the)-.1 E F2(/etc/passwd)2.5 E F1
-(\214le.)2.5 E 1.32(When sending, the)127 510.4 R F0($h)3.82 E F1(,)A F0($u)
-3.82 E F1 3.82(,a)C(nd)246.37 510.4 Q F0($z)3.82 E F1 1.321
-(macros get set to the host, user)3.82 F 3.821(,a)-.4 G 1.321
-(nd home directory \(if)414.777 510.4 R .517(local\) of the recipient.)102
-522.4 R .517(The \214rst tw)5.517 F 3.016(oa)-.1 G .516(re set from the)256.878
-522.4 R F0($@)3.016 E F1(and)3.016 E F0($:)3.016 E F1 .516(part of the re)3.016
-F .516(writing rules, respec-)-.25 F(ti)102 534.4 Q -.15(ve)-.25 G(ly).15 E(.)
--.65 E(The)127 550.6 Q F0($p)3.806 E F1(and)3.806 E F0($t)3.806 E F1 1.306(mac\
-ros are used to create unique strings \(e.g., for the \231Message-Id:\232 \214\
-eld\).)3.806 F(The)102 562.6 Q F0($i)3.252 E F1 .751(macro is set to the queue\
- id on this host; if put into the timestamp line it can be e)3.252 F(xtremely)
--.15 E .164(useful for tracking messages.)102 574.6 R(The)5.164 E F0($v)2.664 E
-F1 .164(macro is set to be the v)2.664 F .165(ersion number of)-.15 F F2
-(sendmail)2.665 E F1 2.665(;t)C .165(his is nor)463.87 574.6 R(-)-.2 E
-(mally put in timestamps and has been pro)102 586.6 Q -.15(ve)-.15 G 2.5(ne).15
-G(xtremely useful for deb)289.31 586.6 Q(ugging.)-.2 E(The)127 602.8 Q F0($c)
-3.548 E F1 1.048(\214eld is set to the \231hop count,)3.548 F 3.548<9a69>-.7 G
-1.048(.e., the number of times this message has been pro-)290.162 602.8 R 2.856
-(cessed. This)102 614.8 R .356(can be determined by the)2.856 F F0<ad68>2.856 E
-F1 .357(\215ag on the command line or by counting the timestamps)2.856 F
-(in the message.)102 626.8 Q(The)127 643 Q F0($r)2.833 E F1(and)2.833 E F0($s)
-2.833 E F1 .333(\214elds are set to the protocol used to communicate with)2.833
-F F2(sendmail)2.833 E F1 .333(and the send-)2.833 F .194(ing hostname.)102 655
-R(The)5.194 E 2.694(yc)-.15 G .194(an be set together using the)191.032 655 R
-F0<ad70>2.694 E F1 .194(command line \215ag or separately using the)2.694 F F0
-<ad4d>2.695 E F1(or)102 667 Q F0(\255oM)2.5 E F1(\215ags.)2.5 E .32 LW 76 676.6
-72 676.6 DL 80 676.6 76 676.6 DL 84 676.6 80 676.6 DL 88 676.6 84 676.6 DL 92
-676.6 88 676.6 DL 96 676.6 92 676.6 DL 100 676.6 96 676.6 DL 104 676.6 100
-676.6 DL 108 676.6 104 676.6 DL 112 676.6 108 676.6 DL 116 676.6 112 676.6 DL
-120 676.6 116 676.6 DL 124 676.6 120 676.6 DL 128 676.6 124 676.6 DL 132 676.6
-128 676.6 DL 136 676.6 132 676.6 DL 140 676.6 136 676.6 DL 144 676.6 140 676.6
-DL 148 676.6 144 676.6 DL 152 676.6 148 676.6 DL 156 676.6 152 676.6 DL 160
-676.6 156 676.6 DL 164 676.6 160 676.6 DL 168 676.6 164 676.6 DL 172 676.6 168
-676.6 DL 176 676.6 172 676.6 DL 180 676.6 176 676.6 DL 184 676.6 180 676.6 DL
-188 676.6 184 676.6 DL 192 676.6 188 676.6 DL 196 676.6 192 676.6 DL 200 676.6
-196 676.6 DL 204 676.6 200 676.6 DL 208 676.6 204 676.6 DL 212 676.6 208 676.6
-DL 216 676.6 212 676.6 DL/F5 5/Times-Roman@0 SF(16)93.6 687 Q/F6 8
-/Times-Roman@0 SF -.12(Fo)3.2 K 2(re).12 G(xample, on some systems)115.024
-690.2 Q/F7 8/Times-Italic@0 SF -.08(ge)2 G(thostname).08 E F6
-(might return \231foo\232 which w)2 E(ould be mapped to \231foo.bar)-.08 E
-(.com\232 by)-.44 E F7 -.08(ge)2 G(thostbyname).08 E F6(.)A F5(17)93.6 700.6 Q
-F6(Older v)3.2 I(ersions of sendmail didn')-.12 E 2(tp)-.144 G(re-de\214ne)
-211.88 703.8 Q/F8 8/Times-Bold@0 SF($j)2 E F6
-(at all, so up until 8.6, con\214g \214les)2 E F7(always)2 E F6
-(had to de\214ne)2 E F8($j)2 E F6(.)A EP
-%%Page: 34 29
-%%BeginPageSetup
-BP
-%%EndPageSetup
-/F0 10/Times-Bold@0 SF 193.36(SMM:08-34 Sendmail)72 60 R
-(Installation and Operation Guide)2.5 E/F1 10/Times-Roman@0 SF(The)127 96 Q F0
-($_)2.967 E F1 .467(is set to a v)2.967 F .467(alidated sender host name.)-.25
-F .466(If the sender is running an RFC 1413 compli-)5.467 F .384
-(ant IDENT serv)102 108 R .384(er and the recei)-.15 F -.15(ve)-.25 G 2.884(rh)
-.15 G .384(as the IDENT protocol turned on, it will include the user name)
-249.254 108 R(on that host.)102 120 Q F0 2.5(5.3. C)87 144 R
-(and F \212 De\214ne Classes)2.5 E F1 .66
-(Classes of phrases may be de\214ned to match on the left hand side of re)127
-160.2 R .659(writing rules, where a)-.25 F .192(\231phrase\232 is a sequence o\
-f characters that do not contain space characters.)102 172.2 R -.15(Fo)5.192 G
-2.692(re).15 G .192(xample a class of all)421.582 172.2 R 1.428(local names fo\
-r this site might be created so that attempts to send to oneself can be elimin\
-ated.)102 184.2 R .041(These can either be de\214ned directly in the con\214gu\
-ration \214le or read in from another \214le.)102 196.2 R .041(Classes are)
-5.041 F .649(named as a single letter or a w)102 208.2 R .649(ord in {braces}.)
--.1 F .649(Class names be)5.649 F .649(ginning with lo)-.15 F .648
-(wer case letters and)-.25 F .638(special characters are reserv)102 220.2 R
-.638(ed for system use.)-.15 F .639
-(Classes de\214ned in con\214g \214les may be gi)5.639 F -.15(ve)-.25 G 3.139
-(nn).15 G(ames)483.45 220.2 Q 1.05
-(from the set of upper case letters for short names or be)102 232.2 R 1.05
-(ginning with an upper case letter for long)-.15 F(names.)102 244.2 Q
-(The syntax is:)127 260.4 Q F0(C)142 276.6 Q/F2 10/Times-Italic@0 SF 1.666(cp)C
-(hr)-1.666 E(ase1 phr)-.15 E(ase2...)-.15 E F0(F)142 288.6 Q F2 1.666<638c>C
-(le)-1.666 E F1 .661(The \214rst form de\214nes the class)102 304.8 R F2(c)
-3.161 E F1 .661(to match an)3.161 F 3.161(yo)-.15 G 3.161(ft)300.1 304.8 S .661
-(he named w)309.371 304.8 R 3.161(ords. It)-.1 F .661
-(is permissible to split them)3.161 F(among multiple lines; for e)102 316.8 Q
-(xample, the tw)-.15 E 2.5(of)-.1 G(orms:)280.07 316.8 Q(CHmonet ucbmonet)142
-333 Q(and)102 349.2 Q(CHmonet)142 365.4 Q(CHucbmonet)142 377.4 Q(are equi)102
-393.6 Q -.25(va)-.25 G 2.5(lent. The).25 F -.74(``)2.5 G(F').74 E 2.5('f)-.74 G
-(orm reads the elements of the class)206.65 393.6 Q F2(c)2.5 E F1
-(from the named)2.5 E F2(\214le)2.5 E F1(.)A 1.339
-(Elements of classes can be accessed in rules using)127 409.8 R F0($=)3.839 E
-F1(or)3.839 E F0($~)3.839 E F1 6.339(.T)C(he)392.048 409.8 Q F0($~)3.839 E F1
-1.338(\(match entries not in)3.839 F(class\) only matches a single w)102 421.8
-Q(ord; multi-w)-.1 E(ord entries in the class are ignored in this conte)-.1 E
-(xt.)-.15 E 1.098(The class)127 438 R F0($=w)3.598 E F1 1.098
-(is set to be the set of all names this host is kno)3.598 F 1.098(wn by)-.25 F
-6.098(.T)-.65 G 1.098(his can be used to)428.506 438 R(match local hostnames.)
-102 450 Q(The class)127 466.2 Q F0($=k)2.5 E F1(is set to be the same as)2.5 E
-F0($k)2.5 E F1 2.5(,t)C(hat is, the UUCP node name.)297.69 466.2 Q(The class)
-127 482.4 Q F0($=m)2.5 E F1
-(is set to the set of domains by which this host is kno)2.5 E
-(wn, initially just)-.25 E F0($m)2.5 E F1(.)A .239(The class)127 498.6 R F0
-($=t)2.739 E F1 .239(is set to the set of trusted users by the)2.739 F F0(T)
-2.739 E F1 .239(con\214guration line.)2.739 F .239(If you w)5.239 F .239
-(ant to read)-.1 F(trusted users from a \214le use)102 510.6 Q F0(Ft)2.5 E F2
-(/\214le/name)A F1(.)A .635(The class)127 526.8 R F0($=n)3.135 E F1 .635
-(can be set to the set of MIME body types that can ne)3.135 F -.15(ve)-.25 G
-3.136(rb).15 G 3.136(ee)426.306 526.8 S .636(ight to se)438.322 526.8 R -.15
-(ve)-.25 G 3.136(nb).15 G(it)498.44 526.8 Q 2.76(encoded. It)102 538.8 R(def)
-2.76 E .26(aults to \231multipart/signed\232.)-.1 F .26
-(Message types \231message/*\232 and \231multipart/*\232 are ne)5.26 F -.15(ve)
--.25 G(r).15 E .942(encoded directly)102 550.8 R 5.942(.M)-.65 G .943
-(ultipart messages are al)185.994 550.8 R -.1(wa)-.1 G .943(ys handled recursi)
-.1 F -.15(ve)-.25 G(ly).15 E 5.943(.T)-.65 G .943(he handling of message/*)
-399.241 550.8 R .658(messages are controlled by class)102 562.8 R F0($=s)3.158
-E F1 5.658(.T)C .657(he class)266.618 562.8 R F0($=e)3.157 E F1 .657
-(contains the Content-T)3.157 F(ransfer)-.35 E .657(-Encodings that)-.2 F .007
-(can be 8)102 574.8 R/F3 10/Symbol SF<ae>A F1 2.507(7b)C .007(it encoded.)
-157.711 574.8 R .007
-(It is prede\214ned to contain \2317bit\232, \2318bit\232, and \231binary\232.)
-5.007 F .007(The class)5.007 F F0($=s)2.507 E F1(con-)2.508 E 1.52
-(tains the set of subtypes of message that can be treated recursi)102 586.8 R
--.15(ve)-.25 G(ly).15 E 6.52(.B)-.65 G 4.02(yd)398.58 586.8 S(ef)412.6 586.8 Q
-1.52(ault it contains only)-.1 F 3.213(\231rfc822\232. Other)102 598.8 R .713
-(\231message/*\232 types cannot be 8)3.213 F F3<ae>A F1 3.214(7b)C .714
-(it encoded.)319.862 598.8 R .714(If a message containing eight bit)5.714 F
-1.714(data is sent to a se)102 610.8 R -.15(ve)-.25 G 4.214(nb).15 G 1.714
-(it host, and that message cannot be encoded into se)206.314 610.8 R -.15(ve)
--.25 G 4.213(nb).15 G 1.713(its, it will be)448.851 610.8 R
-(stripped to 7 bits.)102 622.8 Q F2(Sendmail)127 639 Q F1 .182
-(can be compiled to allo)2.682 F 2.682(wa)-.25 G F2(scanf)A F1 .182
-(\(3\) string on the)B F0(F)2.682 E F1 2.683(line. This)2.683 F .183
-(lets you do simplistic)2.683 F .555(parsing of te)102 651 R .555(xt \214les.)
--.15 F -.15(Fo)5.555 G 3.055(re).15 G .554
-(xample, to read all the user names in your system)209.595 651 R F2
-(/etc/passwd)3.054 E F1 .554(\214le into a)3.054 F(class, use)102 663 Q
-(FL/etc/passwd %[^:])142 679.2 Q(which reads e)102 695.4 Q -.15(ve)-.25 G
-(ry line up to the \214rst colon.).15 E EP
-%%Page: 35 30
-%%BeginPageSetup
-BP
-%%EndPageSetup
-/F0 10/Times-Bold@0 SF(Sendmail Installation and Operation Guide)72 60 Q
-(SMM:08-35)452.9 60 Q 2.5(5.4. M)87 96 R 2.5<8a44>2.5 G(e\214ne Mailer)138.66
-96 Q/F1 10/Times-Roman@0 SF(Programs and interf)127 112.2 Q
-(aces to mailers are de\214ned in this line.)-.1 E(The format is:)5 E F0(M)142
-128.4 Q/F2 10/Times-Italic@0 SF(name)A F1 2.5(,{)C F2(\214eld)182.9 128.4 Q F1
-(=)A F2(value)A F1(}*)1.666 E(where)102 144.6 Q F2(name)4.244 E F1 1.744(is th\
-e name of the mailer \(used internally only\) and the \231\214eld=name\232 pai\
-rs de\214ne)4.244 F(attrib)102 156.6 Q(utes of the mailer)-.2 E 5(.F)-.55 G
-(ields are:)205.13 156.6 Q -.15(Pa)142 172.8 S 51.87(th The).15 F
-(pathname of the mailer)2.5 E 47.83(Flags Special)142 184.8 R
-(\215ags for this mailer)2.5 E 41.73(Sender Re)142 196.8 R
-(writing set\(s\) for sender addresses)-.25 E 31.17(Recipient Re)142 208.8 R
-(writing set\(s\) for recipient addresses)-.25 E(Ar)142 220.8 Q 49.13(gv An)
--.18 F(ar)2.5 E(gument v)-.18 E(ector to pass to this mailer)-.15 E 55.61
-(Eol The)142 232.8 R(end-of-line string for this mailer)2.5 E 35.62
-(Maxsize The)142 244.8 R(maximum message length to this mailer)2.5 E 32.27
-(Linelimit The)142 256.8 R(maximum line length in the message body)2.5 E 31.18
-(Directory The)142 268.8 R -.1(wo)2.5 G(rking directory for the mailer).1 E
-42.84(Userid The)142 280.8 R(def)2.5 E(ault user and group id to run as)-.1 E
-50.62(Nice The)142 292.8 R(nice\(2\) increment for the mailer)2.5 E 38.95
-(Charset The)142 304.8 R(def)2.5 E(ault character set for 8-bit characters)-.1
-E -.8(Ty)142 316.8 S 49.75(pe The).8 F
-(MTS type information \(used for error messages\))2.5 E
-(Only the \214rst character of the \214eld name is check)102 333 Q(ed.)-.1 E
-.397(The follo)127 349.2 R .396
-(wing \215ags may be set in the mailer description.)-.25 F(An)5.396 E 2.896(yo)
--.15 G .396(ther \215ags may be used freely)386.77 349.2 R .075
-(to conditionally assign headers to messages destined for particular mailers.)
-102 361.2 R .075(Flags mark)5.075 F .075(ed with \207 are)-.1 F 1.193
-(not interpreted by the)102 373.2 R F2(sendmail)3.693 E F1 1.193
-(binary; these are the con)3.693 F -.15(ve)-.4 G 1.192
-(ntionally used to correlate to the \215ags).15 F .737(portion of the)102 385.2
-R F0(H)3.237 E F1 3.237(line. Flags)3.237 F(mark)3.237 E .737
-(ed with \210 apply to the mailers for the sender address rather than)-.1 F
-(the usual recipient mailers.)102 397.2 Q 15.56(aR)102 413.4 S .987(un Extende\
-d SMTP \(ESMTP\) protocol \(de\214ned in RFCs 1651, 1652, and 1653\).)128.67
-413.4 R .986(This \215ag)5.987 F(def)122 425.4 Q
-(aults on if the SMTP greeting message includes the w)-.1 E(ord \231ESMTP\232.)
--.1 E 12.78(AL)102 441.6 S .762
-(ook up the user part of the address in the alias database.)128.11 441.6 R .763
-(Normally this is only set for local)5.762 F(mailers.)122 453.6 Q 15(bF)102
-469.8 S .456(orce a blank line on the end of a message.)127.41 469.8 R .456
-(This is intended to w)5.456 F .456(ork around some stupid v)-.1 F(er)-.15 E(-)
--.2 E .361(sions of /bin/mail that require a blank line, b)122 481.8 R .362
-(ut do not pro)-.2 F .362(vide it themselv)-.15 F 2.862(es. It)-.15 F -.1(wo)
-2.862 G .362(uld not nor).1 F(-)-.2 E(mally be used on netw)122 493.8 Q
-(ork mail.)-.1 E 15.56(cD)102 510 S 2.663(on)129.22 510 S .163
-(ot include comments in addresses.)141.883 510 R .163
-(This should only be used if you ha)5.163 F .463 -.15(ve t)-.2 H 2.663(ow).15 G
-.163(ork around a)453.135 510 R 1.846
-(remote mailer that gets confused by comments.)122 522 R 1.846
-(This strips addresses of the form \231Phrase)6.846 F
-(<address>\232 or \231address \(Comment\)\232 do)122 534 Q
-(wn to just \231address\232.)-.25 E 5.83(C\210 If)102 550.2 R .214(mail is)
-2.714 F F2 -.37(re)2.714 G(ceived).37 E F1 .213
-(from a mailer with this \215ag set, an)2.713 F 2.713(ya)-.15 G .213
-(ddresses in the header that do not ha)348.169 550.2 R -.15(ve)-.2 G .97
-(an at sign \(\231@\232\) after being re)122 562.2 R .97
-(written by ruleset three will ha)-.25 F 1.27 -.15(ve t)-.2 H .97
-(he \231@domain\232 clause from).15 F(the sender en)122 574.2 Q -.15(ve)-.4 G
-(lope address tack).15 E(ed on.)-.1 E(This allo)5 E
-(ws mail with headers of the form:)-.25 E(From: usera@hosta)162 590.4 Q -.8(To)
-162 602.4 S 2.5(:u).8 G(serb@hostb, userc)182.59 602.4 Q(to be re)122 618.6 Q
-(written as:)-.25 E(From: usera@hosta)162 634.8 Q -.8(To)162 646.8 S 2.5(:u).8
-G(serb@hostb, userc@hosta)182.59 646.8 Q(automatically)122 663 Q 5(.H)-.65 G
--.25(ow)190.51 663 S -2.15 -.25(ev e).25 H .8 -.4(r, i).25 H 2.5(td).4 G(oesn')
-236.95 663 Q 2.5(tr)-.18 G(eally w)267.04 663 Q(ork reliably)-.1 E(.)-.65 E
-5.28(D\207 This)102 679.2 R(mailer w)2.5 E(ants a \231Date:\232 header line.)
--.1 E 15.56(eT)102 695.4 S .174(his mailer is e)128.11 695.4 R(xpensi)-.15 E
-.474 -.15(ve t)-.25 H 2.674(oc).15 G .173(onnect to, so try to a)237.03 695.4 R
--.2(vo)-.2 G .173(id connecting normally; an).2 F 2.673(yn)-.15 G .173
-(ecessary con-)449.687 695.4 R(nection will occur during a queue run.)122 707.4
-Q EP
-%%Page: 36 31
-%%BeginPageSetup
-BP
-%%EndPageSetup
-/F0 10/Times-Bold@0 SF 193.36(SMM:08-36 Sendmail)72 60 R
-(Installation and Operation Guide)2.5 E/F1 10/Times-Roman@0 SF 13.89(EE)102 96
-S(scape lines be)128.11 96 Q
-(ginning with \231From\232 in the message with a `>' sign.)-.15 E 16.67(fT)102
-112.2 S .19(he mailer w)128.11 112.2 R .19(ants a)-.1 F F0<ad66>2.69 E/F2 10
-/Times-Italic@0 SF(fr)2.69 E(om)-.45 E F1 .19(\215ag, b)2.69 F .19
-(ut only if this is a netw)-.2 F .19(ork forw)-.1 F .19
-(ard operation \(i.e., the mailer)-.1 F(will gi)122 124.2 Q .3 -.15(ve a)-.25 H
-2.5(ne).15 G(rror if the e)175.76 124.2 Q -.15(xe)-.15 G
-(cuting user does not ha).15 E .3 -.15(ve s)-.2 H(pecial permissions\).).15 E
-6.94(F\207 This)102 140.4 R(mailer w)2.5 E(ants a \231From:\232 header line.)
--.1 E 15(gN)102 156.6 S(ormally)129.22 156.6 Q(,)-.65 E F2(sendmail)4.893 E F1
-2.393(sends internally generated email \(e.g., error messages\) using the null)
-4.893 F 1.327(return address as required by RFC 1123.)122 168.6 R(Ho)6.327 E
-(we)-.25 E -.15(ve)-.25 G 2.127 -.4(r, s).15 H 1.327(ome mailers don').4 F
-3.827(ta)-.18 G 1.328(ccept a null return)427.537 168.6 R 3.311(address. If)122
-180.6 R(necessary)3.311 E 3.311(,y)-.65 G .811(ou can set the)219.303 180.6 R
-F0(g)3.311 E F1 .811(\215ag to pre)3.311 F -.15(ve)-.25 G(nt).15 E F2(sendmail)
-3.31 E F1 .81(from obe)3.31 F .81(ying the standards;)-.15 F 1.57
-(error messages will be sent as from the MAILER-D)122 192.6 R 1.57
-(AEMON \(actually)-.4 F 4.07(,t)-.65 G 1.57(he v)425.76 192.6 R 1.57
-(alue of the)-.25 F F0($n)4.07 E F1(macro\).)122 204.6 Q 15(hU)102 220.8 S
-(pper case should be preserv)129.22 220.8 Q(ed in host names for this mailer)
--.15 E(.)-.55 E 16.67(IT)102 237 S .475
-(his mailer will be speaking SMTP to another)128.11 237 R F2(sendmail)2.974 E
-F1 2.974<8a61>2.974 G 2.974(ss)370.066 237 S .474
-(uch it can use special protocol)380.82 237 R 3.632(features. This)122 249 R
-1.133(option is not required \(i.e., if this option is omitted the transmissio\
-n will still)3.632 F(operate successfully)122 261 Q 2.5(,a)-.65 G
-(lthough perhaps not as ef)211.6 261 Q(\214ciently as possible\).)-.25 E 15(kN)
-102 277.2 S 1.03(ormally when)129.22 277.2 R F2(sendmail)3.53 E F1 1.03
-(connects to a host via SMTP)3.53 F 3.529(,i)-1.11 G 3.529(tc)356.257 277.2 S
-1.029(hecks to mak)367.006 277.2 R 3.529(es)-.1 G 1.029(ure that this isn')
-433.593 277.2 R(t)-.18 E .562(accidently the same host name as might happen if)
-122 289.2 R F2(sendmail)3.062 E F1 .562(is miscon\214gured or if a long-haul)
-3.062 F(netw)122 301.2 Q 1.074(ork interf)-.1 F 1.074
-(ace is set in loopback mode.)-.1 F 1.073
-(This \215ag disables the loopback check.)6.074 F 1.073(It should)6.073 F
-(only be used under v)122 313.2 Q(ery unusual circumstances.)-.15 E 12.78(KC)
-102 329.4 S(urrently unimplemented.)128.67 329.4 Q(Reserv)5 E(ed for chunking.)
--.15 E 17.22(lT)102 345.6 S(his mailer is local \(i.e., \214nal deli)128.11
-345.6 Q -.15(ve)-.25 G(ry will be performed\).).15 E 13.89(LL)102 361.8 S .819
-(imit the line lengths as speci\214ed in RFC821.)128.11 361.8 R .82
-(This deprecated option should be replaced by)5.819 F(the)122 373.8 Q F0(L=)2.5
-E F1(mail declaration.)2.5 E -.15(Fo)5 G 2.5(rh).15 G(istoric reasons, the)
-245.04 373.8 Q F0(L)2.5 E F1(\215ag also sets the)2.5 E F0(7)2.5 E F1(\215ag.)
-2.5 E 12.22(mT)102 390 S .464
-(his mailer can send to multiple users on the same host in one transaction.)
-128.11 390 R .463(When a)5.463 F F0($u)2.963 E F1(macro)2.963 E .731
-(occurs in the)122 402 R F2(ar)3.231 E(gv)-.37 E F1 .732(part of the mailer de\
-\214nition, that \214eld will be repeated as necessary for all)3.231 F
-(qualifying users.)122 414 Q 3.61(M\207 This)102 430.2 R(mailer w)2.5 E
-(ants a \231Message-Id:\232 header line.)-.1 E 15(nD)102 446.4 S 2.5(on)129.22
-446.4 S(ot insert a UNIX-style \231From\232 line on the front of the message.)
-141.72 446.4 Q 15(oA)102 462.6 S -.1(lwa)129.22 462.6 S .816(ys run as the o).1
-F .816(wner of the recipient mailbox.)-.25 F(Normally)5.816 E F2(sendmail)3.316
-E F1 .816(runs as the sender for)3.316 F .198
-(locally generated mail or as \231daemon\232 \(actually)122 474.6 R 2.698(,t)
--.65 G .198(he user speci\214ed in the)321.576 474.6 R F0(u)2.698 E F1 .198
-(option\) when deli)2.698 F(v-)-.25 E .981(ering netw)122 486.6 R .981
-(ork mail.)-.1 F .981(The normal beha)5.981 F .981
-(viour is required by most local mailers, which will not)-.2 F(allo)122 498.6 Q
-2.52(wt)-.25 G .02(he en)149.27 498.6 R -.15(ve)-.4 G .021
-(lope sender address to be set unless the mailer is running as daemon.).15 F
-.021(This \215ag is)5.021 F(ignored if the)122 510.6 Q F0(S)2.5 E F1
-(\215ag is set.)2.5 E 15(pU)102 526.8 S .498(se the route-addr style re)129.22
-526.8 R -.15(ve)-.25 G .498(rse-path in the SMTP \231MAIL FR).15 F .497
-(OM:\232 command rather than just)-.4 F .385
-(the return address; although this is required in RFC821 section 3.1, man)122
-538.8 R 2.886(yh)-.15 G .386(osts do not process)427.012 538.8 R(re)122 550.8 Q
--.15(ve)-.25 G(rse-paths properly).15 E 5(.R)-.65 G -2.15 -.25(ev e)224.81
-550.8 T(rse-paths are of).25 E(\214cially discouraged by RFC 1123.)-.25 E 6.94
-(P\207 This)102 567 R(mailer w)2.5 E(ants a \231Return-P)-.1 E(ath:\232 line.)
--.15 E 16.67(rS)102 583.2 S(ame as)127.56 583.2 Q F0(f)2.5 E F1 2.5(,b)C
-(ut sends a)170.68 583.2 Q F0<ad72>2.5 E F1(\215ag.)2.5 E 16.11(sS)102 599.4 S
-(trip quote characters \(" and \\\) of)127.56 599.4 Q 2.5(fo)-.25 G 2.5(ft)
-266.07 599.4 S(he address before calling the mailer)274.68 599.4 Q(.)-.55 E
-14.44(SD)102 615.6 S(on')129.22 615.6 Q 3.332(tr)-.18 G .832
-(eset the userid before calling the mailer)151.812 615.6 R 5.831(.T)-.55 G .831
-(his w)328.433 615.6 R .831(ould be used in a secure en)-.1 F(vironment)-.4 E
-(where)122 627.6 Q F2(sendmail)3.317 E F1 .817(ran as root.)3.317 F .817
-(This could be used to a)5.817 F -.2(vo)-.2 G .817(id for).2 F .817
-(ged addresses.)-.18 F .817(If the)5.817 F F0(U=)3.317 E F1 .818(\214eld is)
-3.317 F .974(also speci\214ed, this \215ag causes the user id to al)122 639.6 R
--.1(wa)-.1 G .974(ys be set to that user and group \(instead of).1 F(lea)122
-651.6 Q(ving it as root\).)-.2 E 15(uU)102 667.8 S(pper case should be preserv)
-129.22 667.8 Q(ed in user names for this mailer)-.15 E(.)-.55 E 12.78(UT)102
-684 S(his mailer w)128.11 684 Q(ants UUCP-style \231From\232 lines with the ug\
-ly \231remote from <host>\232 on the end.)-.1 E 12.78(wT)102 700.2 S .565
-(he user must ha)128.11 700.2 R .865 -.15(ve a v)-.2 H .566
-(alid account on this machine, i.e., getpwnam must succeed.)-.1 F .566
-(If not, the)5.566 F(mail is bounced.)122 712.2 Q
-(This is required to get \231.forw)5 E(ard\232 capability)-.1 E(.)-.65 E EP
-%%Page: 37 32
-%%BeginPageSetup
-BP
-%%EndPageSetup
-/F0 10/Times-Bold@0 SF(Sendmail Installation and Operation Guide)72 60 Q
-(SMM:08-37)452.9 60 Q/F1 10/Times-Roman@0 SF 7.5(x\207 This)102 96 R(mailer w)
-2.5 E(ants a \231Full-Name:\232 header line.)-.1 E 12.78(XT)102 112.2 S .972
-(his mailer w)128.11 112.2 R .972
-(ant to use the hidden dot algorithm as speci\214ed in RFC821; basically)-.1 F
-3.472(,a)-.65 G 1.272 -.15(ny l)475.678 112.2 T(ine).15 E(be)122 124.2 Q .796
-(ginning with a dot will ha)-.15 F 1.096 -.15(ve a)-.2 H 3.296(ne).15 G .797
-(xtra dot prepended \(to be stripped at the other end\).)267.742 124.2 R(This)
-5.797 E(insures that lines in the message containing a dot will not terminate \
-the message prematurely)122 136.2 Q(.)-.65 E 15(5I)102 152.4 S 2.717(fn)125.33
-152.4 S 2.717(oa)136.377 152.4 S .217(liases are found for this address, pass \
-the address through ruleset 5 for possible alternate)148.534 152.4 R 2.5
-(resolution. This)122 164.4 R(is intended to forw)2.5 E
-(ard the mail to an alternate deli)-.1 E -.15(ve)-.25 G(ry spot.).15 E 15(7S)
-102 180.6 S 1.14(trip all output to se)127.56 180.6 R -.15(ve)-.25 G 3.64(nb)
-.15 G 3.64(its. This)230.36 180.6 R 1.14(is the def)3.64 F 1.141(ault if the)
--.1 F F0(L)3.641 E F1 1.141(\215ag is set.)3.641 F 1.141
-(Note that clearing this)6.141 F .295(option is not suf)122 192.6 R .295
-(\214cient to get full eight bit data passed through)-.25 F/F2 10
-/Times-Italic@0 SF(sendmail)2.795 E F1 5.295(.I)C 2.795(ft)423.635 192.6 S(he)
-432.54 192.6 Q F0(7)2.795 E F1 .295(option is set,)2.795 F .716
-(this is essentially al)122 204.6 R -.1(wa)-.1 G .717
-(ys set, since the eighth bit w).1 F .717(as stripped on input.)-.1 F .717
-(Note that this option)5.717 F(will only impact messages that didn')122 216.6 Q
-2.5(th)-.18 G -2.25 -.2(av e)279.04 216.6 T(8)2.7 E/F3 10/Symbol SF<ae>A F1 2.5
-(7b)C(it MIME con)322.44 216.6 Q -.15(ve)-.4 G(rsions performed.).15 E 15(8I)
-102 232.8 S 3.783(fs)125.33 232.8 S 1.283(et, it is acceptable to send eight b\
-it data to this mailer; the usual attempt to do 8)136.333 232.8 R F3<ae>A F1
-3.782(7b)C(it)498.44 232.8 Q(MIME con)122 244.8 Q -.15(ve)-.4 G
-(rsions will be bypassed.).15 E 17.22(:C)102 261 S .982
-(heck addresses to see if the)128.67 261 R 3.482(yb)-.15 G -.15(eg)255.492 261
-S .982(in \231:include:\232; if the).15 F 3.482(yd)-.15 G .982(o, con)361.33
-261 R -.15(ve)-.4 G .982(rt them to the \231*include*\232).15 F(mailer)122 273
-Q(.)-.55 E 18(|C)102 289.2 S(heck addresses to see if the)128.67 289.2 Q 2.5
-(yb)-.15 G -.15(eg)249.6 289.2 S(in with a `|'; if the).15 E 2.5(yd)-.15 G
-(o, con)343.51 289.2 Q -.15(ve)-.4 G(rt them to the \231prog\232 mailer).15 E
-(.)-.55 E 17.22(/C)102 305.4 S(heck addresses to see if the)128.67 305.4 Q 2.5
-(yb)-.15 G -.15(eg)249.6 305.4 S(in with a `/'; if the).15 E 2.5(yd)-.15 G
-(o, con)344.29 305.4 Q -.15(ve)-.4 G(rt them to the \231*\214le*\232 mailer).15
-E(.)-.55 E 10.79(@L)102 321.6 S(ook up addresses in the user database.)128.11
-321.6 Q .268(Con\214guration \214les prior to le)127 337.8 R -.15(ve)-.25 G
-2.768(l6a).15 G .268(ssume the `)271.538 337.8 R -1.11(A')-.8 G 2.768(,`)1.11 G
-.268(w', `5', `:', `|', `/', and `@' options on the)334.862 337.8 R
-(mailer named \231local\232.)102 349.8 Q .306(The mailer with the special name\
- \231error\232 can be used to generate a user error)127 366 R 5.306(.T)-.55 G
-.306(he \(optional\))452.314 366 R .324(host \214eld is an e)102 378 R .323
-(xit status to be returned, and the user \214eld is a message to be printed.)
--.15 F .323(The e)5.323 F .323(xit sta-)-.15 F .891
-(tus may be numeric or one of the v)102 390 R .891(alues USA)-.25 F .891
-(GE, NOUSER, NOHOST)-.4 F 3.391(,U)-.74 G -.35(NA)409.869 390 S -1.35(VA)-1 G
-.891(ILABLE, SOFT)1.35 F(-)-.92 E -1.2(WA)102 402 S 1.573(RE, TEMPF)1.2 F 1.573
-(AIL, PR)-.74 F -1.88 -.4(OT O)-.4 H 1.573
-(COL, or CONFIG to return the corresponding EX_ e).4 F 1.572(xit code.)-.15 F
--.15(Fo)6.572 G(r).15 E -.15(ex)102 414 S(ample, the entry:).15 E
-($#error $@ NOHOST $: Host unkno)142 430.2 Q(wn in this domain)-.25 E .145(on \
-the RHS of a rule will cause the speci\214ed error to be generated and the \
-\231Host unkno)102 446.4 R .146(wn\232 e)-.25 F .146(xit sta-)-.15 F
-(tus to be returned if the LHS matches.)102 458.4 Q
-(This mailer is only functional in rulesets zero or \214v)5 E(e.)-.15 E .468
-(The mailer named \231local\232)127 474.6 R F2(must)2.968 E F1 .468
-(be de\214ned in e)2.968 F -.15(ve)-.25 G .468(ry con\214guration \214le.).15 F
-.468(This is used to deli)5.468 F -.15(ve)-.25 G(r).15 E .25
-(local mail, and is treated specially in se)102 486.6 R -.15(ve)-.25 G .25
-(ral w).15 F 2.75(ays. Additionally)-.1 F 2.75(,t)-.65 G .25
-(hree other mailers named \231prog\232,)369.43 486.6 R .942
-(\231*\214le*\232, and \231*include*\232 may be de\214ned to tune the deli)102
-498.6 R -.15(ve)-.25 G .942(ry of messages to programs, \214les, and).15 F
-(:include: lists respecti)102 510.6 Q -.15(ve)-.25 G(ly).15 E 5(.T)-.65 G(he)
-219 510.6 Q 2.5(yd)-.15 G(ef)240.79 510.6 Q(ault to:)-.1 E
-(Mprog, P=/bin/sh, F=lsD, A=sh \255c $u)142 526.8 Q(M*\214le*, P=/de)142 538.8
-Q(v/null, F=lsDFMPEu, A=FILE)-.25 E(M*include*, P=/de)142 550.8 Q
-(v/null, F=su, A=INCLUDE)-.25 E .615(The Sender and Recipient re)127 571.2 R
-.615(writing sets may either be a simple ruleset id or may be tw)-.25 F 3.116
-(oi)-.1 G(ds)495.11 571.2 Q .576(separated by a slash; if so, the \214rst re)
-102 583.2 R .575(writing set is applied to en)-.25 F -.15(ve)-.4 G .575
-(lope addresses and the second is).15 F(applied to headers.)102 595.2 Q .196
-(The Directory is actually a colon-separated path of directories to try)127
-611.4 R 5.197(.F)-.65 G .197(or e)413.019 611.4 R .197(xample, the de\214ni-)
--.15 F .104(tion \231D=$z:/\232 \214rst tries to e)102 623.4 R -.15(xe)-.15 G
-.104(cute in the recipient').15 F 2.604(sh)-.55 G .104
-(ome directory; if that is not a)315.196 623.4 R -.25(va)-.2 G .103
-(ilable, it tries to).25 F -.15(exe)102 635.4 S .816
-(cute in the root of the \214lesystem.).15 F .816
-(This is intended to be used only on the \231prog\232 mailer)5.816 F 3.317(,s)
--.4 G(ince)487.34 635.4 Q .368(some shells \(such as)102 647.4 R F2(csh)2.868 E
-F1 2.868(\)r)C .368(efuse to e)210.21 647.4 R -.15(xe)-.15 G .368(cute if the)
-.15 F 2.868(yc)-.15 G .367(annot read the home directory)311.29 647.4 R 5.367
-(.S)-.65 G .367(ince the queue)445.506 647.4 R
-(directory is not normally readable by unpri)102 659.4 Q(vile)-.25 E(ged users)
--.15 E F2(csh)2.5 E F1(scripts as recipients can f)2.5 E(ail.)-.1 E 1.862
-(The Userid speci\214es the def)127 675.6 R 1.863
-(ault user and group id to run as, o)-.1 F -.15(ve)-.15 G 1.863(rriding the).15
-F F0(DefaultUser)4.363 E F1 .287(option \(q.v)102 687.6 R 2.787(.\). If)-.65 F
-(the)2.787 E F0(S)2.787 E F1 .287(mailer \215ag is also speci\214ed, this is t\
-he user and group to run as in all circum-)2.787 F 2.587(stances. This)102
-699.6 R .088(may be gi)2.587 F -.15(ve)-.25 G 2.588(na).15 G(s)219.518 699.6 Q
-F2(user:gr)2.588 E(oup)-.45 E F1 .088
-(to set both the user and group id; either may be an inte)2.588 F(ger)-.15 E
-.541(or a symbolic name to be look)102 711.6 R .541(ed up in the)-.1 F F2
-(passwd)3.041 E F1(and)3.041 E F2(gr)3.041 E(oup)-.45 E F1 .541
-(\214les respecti)3.041 F -.15(ve)-.25 G(ly).15 E 5.541(.I)-.65 G 3.041(fo)
-432.657 711.6 S .541(nly a symbolic)444.028 711.6 R
-(user name is speci\214ed, the group id in the)102 723.6 Q F2(passwd)2.5 E F1
-(\214le for that user is used as the group id.)2.5 E EP
-%%Page: 38 33
-%%BeginPageSetup
-BP
-%%EndPageSetup
-/F0 10/Times-Bold@0 SF 193.36(SMM:08-38 Sendmail)72 60 R
-(Installation and Operation Guide)2.5 E/F1 10/Times-Roman@0 SF .545
-(The Charset \214eld is used when con)127 96 R -.15(ve)-.4 G .545
-(rting a message to MIME; this is the character set used).15 F .466
-(in the Content-T)102 108 R .466(ype: header)-.8 F 5.466(.I)-.55 G 2.966(ft)
-225.824 108 S .466(his is not set, the)234.9 108 R F0(DefaultCharset)2.966 E F1
-.465(option is used, and if that is not)2.965 F .257(set, the v)102 120 R .257
-(alue \231unkno)-.25 F .257(wn-8bit\232 is used.)-.25 F F0 -1.2(WA)5.257 G
-(RNING:)1.2 E F1 .257(this \214eld applies to the sender')2.757 F 2.758(sm)-.55
-G(ailer)453.614 120 Q 2.758(,n)-.4 G .258(ot the)481.242 120 R(recipient')102
-132 Q 2.702(sm)-.55 G(ailer)154.142 132 Q 5.202(.F)-.55 G .202(or e)184.474 132
-R .202(xample, if the en)-.15 F -.15(ve)-.4 G .201
-(lope sender address lists an address on the local netw).15 F(ork)-.1 E .48
-(and the recipient is on an e)102 144 R .48(xternal netw)-.15 F .48
-(ork, the character set will be set from the Charset= \214eld for)-.1 F
-(the local netw)102 156 Q(ork mailer)-.1 E 2.5(,n)-.4 G(ot that of the e)208.98
-156 Q(xternal netw)-.15 E(ork mailer)-.1 E(.)-.55 E .795(The T)127 172.2 R .795
-(ype= \214eld sets the type information used in MIME error messages as de\214n\
-ed by RFC)-.8 F .15(XXX \(not yet published\).)102 184.2 R .15
-(It is actually three v)5.15 F .151(alues separated by slashes: the MT)-.25 F
-.151(A-type \(that is, the)-.93 F .36(description of ho)102 196.2 R 2.86(wh)
--.25 G .359(osts are named\), the address type \(the description of e-mail add\
-resses\), and the)185.32 196.2 R .221
-(diagnostic type \(the description of error diagnostic codes\).)102 208.2 R
-.222(Each of these must be a re)5.221 F .222(gistered v)-.15 F(alue)-.25 E
-(or be)102 220.2 Q(gin with \231X\255\232.)-.15 E(The def)5 E
-(ault is \231dns/rfc822/smtp\232.)-.1 E F0 2.5(5.5. H)87 244.2 R 2.5<8a44>2.5 G
-(e\214ne Header)137 244.2 Q F1 1.136(The format of the header lines that)127
-260.4 R/F2 10/Times-Italic@0 SF(sendmail)3.636 E F1 1.135
-(inserts into the message are de\214ned by the)3.636 F F0(H)3.635 E F1 2.5
-(line. The)102 272.4 R(syntax of this line is:)2.5 E F0(H)142 288.6 Q F1([)A F0
-(?)A F2(m\215a)A(gs)-.1 E F0(?)A F1(])A F2(hname)A F0(:)A F2(htemplate)2.5 E F1
-1.058(Continuation lines in this spec are re\215ected directly into the outgoi\
-ng message.)102 304.8 R(The)6.058 E F2(htemplate)3.558 E F1(is)3.558 E 1.098
-(macro e)102 316.8 R 1.098(xpanded before insertion into the message.)-.15 F
-1.098(If the)6.098 F F2(m\215a)3.598 E(gs)-.1 E F1 1.097
-(\(surrounded by question marks\))3.597 F .161(are speci\214ed, at least one o\
-f the speci\214ed \215ags must be stated in the mailer de\214nition for this h\
-eader)102 328.8 R .192(to be automatically output.)102 340.8 R .191
-(If one of these headers is in the input it is re\215ected to the output re)
-5.192 F -.05(ga)-.15 G(rd-).05 E(less of these \215ags.)102 352.8 Q
-(Some headers ha)127 369 Q .3 -.15(ve s)-.2 H
-(pecial semantics that will be described later).15 E(.)-.55 E F0 2.5(5.6. O)87
-393 R 2.5<8a53>2.5 G(et Option)135.34 393 Q F1 .962(There are a number of glob\
-al options that can be set from a con\214guration \214le.)127 409.2 R .963
-(Options are)5.963 F .86(represented by full w)102 421.2 R .86
-(ords; some are also representable as single characters for back compatibility)
--.1 F(.)-.65 E(The syntax of this line is:)102 433.2 Q F0(O)142 449.4 Q F2
-(option)7.5 E F0(=)A F2(value)A F1 .562(This sets option)102 465.6 R F2(option)
-3.062 E F1 .562(to be)3.062 F F2(value)3.062 E F1 5.562(.N)C .562
-(ote that there)258.434 465.6 R F2(must)3.062 E F1 .562
-(be a space between the letter `O' and the)3.062 F(name of the option.)102
-477.6 Q(An older v)5 E(ersion is:)-.15 E F0(O)142 493.8 Q F2 1.666(ov)C(alue)
--1.666 E F1 .13(where the option)102 510 R F2(o)2.63 E F1 .13
-(is a single character)2.63 F 5.13(.D)-.55 G .13(epending on the option,)273.56
-510 R F2(value)2.63 E F1 .13(may be a string, an inte)2.63 F(ger)-.15 E(,)-.4 E
-2.5(ab)102 522 S(oolean \(with le)113.94 522 Q -.05(ga)-.15 G 2.5(lv).05 G
-(alues \231t\232, \231T\232, \231f\232, or \231F\232; the def)193.2 522 Q
-(ault is TR)-.1 E(UE\), or a time interv)-.4 E(al.)-.25 E
-(The options supported \(with the old, one character names in brack)127 538.2 Q
-(ets\) are:)-.1 E(AliasFile=)102 554.4 Q F2(spec, spec, ...)A F1 .439
-([A] Specify possible alias \214le\(s\).)174 566.4 R(Each)5.439 E F2(spec)2.939
-E F1 .439(should be in the format `)2.939 F(`)-.74 E F2(class)A F0(:)A F2
-(\214le)2.94 E F1 -.74('')C(where)174 578.4 Q F2(class)3.1 E F0(:)A F1 .599
-(is optional and def)3.099 F .599(aults to `)-.1 F(`implicit')-.74 E 3.099
-('. Depending)-.74 F .599(on ho)3.099 F(w)-.25 E F2(sendmail)3.099 E F1 .186
-(is compiled, v)174 590.4 R .187(alid classes are \231implicit\232 \(search th\
-rough a compiled-in list of alias)-.25 F 2.055
-(\214le types, for back compatibility\), \231hash\232 \(if)174 602.4 R/F3 9
-/Times-Roman@0 SF(NEWDB)4.555 E F1 2.055(is speci\214ed\), \231dbm\232 \(if)
-4.555 F F3(NDBM)174 614.4 Q F1 1.588(is speci\214ed\), \231stab\232 \(internal\
- symbol table \212 not normally used unless)4.088 F .075(you ha)174 626.4 R
-.375 -.15(ve n)-.2 H 2.575(oo).15 G .075
-(ther database lookup\), or \231nis\232 \(if)230.255 626.4 R F3(NIS)2.574 E F1
-.074(is speci\214ed\).)2.574 F .074(If a list of)5.074 F F2(spec)2.574 E F1(s)A
-(are pro)174 638.4 Q(vided,)-.15 E F2(sendmail)2.5 E F1(searches them in order)
-2.5 E(.)-.55 E(AliasW)102 654.6 Q(ait=)-.8 E F2(timeout)A F1 .14([a] If set, w)
-174 666.6 R .14(ait up to)-.1 F F2(timeout)2.64 E F1 .141(\(units def)2.641 F
-.141(ault to minutes\) for an \231@:@\232 entry to e)-.1 F(xist)-.15 E .518
-(in the alias database before starting up.)174 678.6 R .517
-(If it does not appear in the)5.517 F F2(timeout)3.017 E F1(inter)3.017 E(-)-.2
-E -.25(va)174 690.6 S 3.21(lr).25 G(eb)192.51 690.6 Q .71
-(uild the database \(if the)-.2 F F0 -.5(Au)3.21 G(toReb).5 E(uildAliases)-.2 E
-F1 .71(option is also set\) or issue a)3.21 F -.1(wa)174 702.6 S(rning.).1 E EP
-%%Page: 39 34
-%%BeginPageSetup
-BP
-%%EndPageSetup
-/F0 10/Times-Bold@0 SF(Sendmail Installation and Operation Guide)72 60 Q
-(SMM:08-39)452.9 60 Q/F1 10/Times-Roman@0 SF(AutoReb)102 96 Q(uildAliases)-.2 E
-.128([D] If set, reb)174 108 R .128
-(uild the alias database if necessary and possible.)-.2 F .128
-(If this option is not)5.128 F(set,)174 120 Q/F2 10/Times-Italic@0 SF(sendmail)
-4.885 E F1 2.385(will ne)4.885 F -.15(ve)-.25 G 4.885(rr).15 G(eb)283.96 120 Q
-2.385(uild the alias database unless e)-.2 F 2.385(xplicitly requested)-.15 F
-(using)174 132 Q F0(\255bi)2.5 E F1 5(.N)C
-(ot recommended \212 can cause thrashing.)226.93 132 Q(BlankSub=)102 148.2 Q F2
-(c)A F1 1.255([B] Set the blank substitution character to)174 148.2 R F2(c)
-3.755 E F1 6.255(.U)C 1.255(nquoted spaces in addresses are)372.35 148.2 R
-(replaced by this character)174 160.2 Q 5(.D)-.55 G(ef)290.63 160.2 Q
-(aults to space \(i.e., no change is made\).)-.1 E 14.51(CheckAliases [n])102
-176.4 R -1.11(Va)2.5 G(lidate the RHS of aliases when reb)1.11 E
-(uilding the alias database.)-.2 E(CheckpointInterv)102 192.6 Q(al=)-.25 E F2
-(N)A F1 1.296([C] Checkpoints the queue e)174 204.6 R -.15(ve)-.25 G(ry).15 E
-F2(N)3.797 E F1(\(def)3.797 E 1.297(ault 10\) addresses sent.)-.1 F 1.297
-(If your system)6.297 F .747(crashes during deli)174 216.6 R -.15(ve)-.25 G
-.746(ry to a lar).15 F .746(ge list, this pre)-.18 F -.15(ve)-.25 G .746
-(nts retransmission to an).15 F 3.246(yb)-.15 G .746(ut the)480.754 216.6 R
-(last recipients.)174 228.6 Q(ClassF)102 244.8 Q(actor=)-.15 E F2(fact)A F1
-1.624([z] The indicated)4.29 F F2(fact)4.124 E F1 1.624
-(or is multiplied by the message class \(determined by the)B .719
-(Precedence: \214eld in the user header and the)174 256.8 R F0(P)3.219 E F1
-.718(lines in the con\214guration \214le\) and)3.218 F 2.637
-(subtracted from the priority)174 268.8 R 7.637(.T)-.65 G 2.637
-(hus, messages with a higher Priority: will be)307.768 268.8 R -.1(fa)174 280.8
-S -.2(vo)-.1 G 2.5(red. Def).2 F(aults to 1800.)-.1 E 3.95(ColonOkInAddr [no)
-102 297 R 4.679
-(short name] If set, colons are acceptable in e-mail addresses \(e.g.,)7.18 F
-3.54(\231host:user\232\). If)174 309 R 1.04(not set, colons indicate the be)
-3.54 F 1.04(ginning of a RFC 822 group con-)-.15 F 1.988
-(struct \(\231groupname: member1, member2, ... memberN;\232\).)174 321 R 1.987
-(Doubled colons are)6.987 F(al)174 333 Q -.1(wa)-.1 G 2.215(ys acceptable \(\
-\231nodename::user\232\) and proper route-addr nesting is under).1 F(-)-.2 E
-1.037(stood \(\231<@relay:user@host>\232\).)174 345 R 1.037
-(Furthermore, this option def)6.037 F 1.036(aults on if the con-)-.1 F .853
-(\214guration v)174 357 R .853(ersion le)-.15 F -.15(ve)-.25 G 3.353(li).15 G
-3.353(sl)274.059 357 S .853(ess than 6 \(for back compatibility\).)284.082 357
-R(Ho)5.854 E(we)-.25 E -.15(ve)-.25 G 1.654 -.4(r, i).15 H 3.354(tm).4 G(ust)
-492.33 357 Q(be of)174 369 Q 2.5(ff)-.25 G(or full compatibility with RFC 822.)
-203.18 369 Q(ConnectionCacheSize=)102 385.2 Q F2(N)A F1 .242
-([k] The maximum number of open connections that will be cached at a time.)174
-397.2 R(The)5.242 E(def)174 409.2 Q .385(ault is one.)-.1 F .386
-(This delays closing the current connection until either this in)5.386 F -.2
-(vo)-.4 G(ca-).2 E 1.192(tion of)174 421.2 R F2(sendmail)3.692 E F1 1.191
-(needs to connect to another host or it terminates.)3.692 F 1.191
-(Setting it to)6.191 F 2.046(zero def)174 433.2 R 2.046(aults to the old beha)
--.1 F(vior)-.2 E 4.546(,t)-.4 G 2.047
-(hat is, connections are closed immediately)322.496 433.2 R(.)-.65 E .266
-(Since this consumes \214le descriptors, the connection cache should be k)174
-445.2 R .265(ept small: 4)-.1 F(is probably a practical maximum.)174 457.2 Q
-(ConnectionCacheT)102 473.4 Q(imeout=)-.35 E F2(timeout)A F1 .708
-([K] The maximum amount of time a cached connection will be permitted to idle)
-174 485.4 R 1.083(without acti)174 497.4 R(vity)-.25 E 6.083(.I)-.65 G 3.583
-(ft)249.156 497.4 S 1.083(his time is e)258.849 497.4 R 1.082
-(xceeded, the connection is immediately closed.)-.15 F .417(This v)174 509.4 R
-.418(alue should be small \(on the order of ten minutes\).)-.25 F(Before)5.418
-E F2(sendmail)2.918 E F1 .418(uses a)2.918 F .508(cached connection, it al)174
-521.4 R -.1(wa)-.1 G .507(ys sends a RSET command to check the connection; if)
-.1 F .401(this f)174 533.4 R .401(ails, it reopens the connection.)-.1 F .401
-(This k)5.401 F .402(eeps your end from f)-.1 F .402(ailing if the other)-.1 F
-1.545(end times out.)174 545.4 R 1.545
-(The point of this option is to be a good netw)6.545 F 1.544(ork neighbor and)
--.1 F -.2(avo)174 557.4 S(id using up e).2 E(xcessi)-.15 E .3 -.15(ve r)-.25 H
-(esources on the other end.).15 E(The def)5 E(ault is \214v)-.1 E 2.5(em)-.15 G
-(inutes.)470.25 557.4 Q(DaemonPortOptions=)102 573.6 Q F2(options)A F1
-([O] Set serv)174 585.6 Q(er SMTP options.)-.15 E(The options are)5 E F2 -.1
-(ke)2.5 G(y=value)-.2 E F1 2.5(pairs. Kno)2.5 F(wn k)-.25 E -.15(ey)-.1 G 2.5
-(sa).15 G(re:)490.2 585.6 Q 52.83(Port Name/number)214 601.8 R
-(of listening port \(def)2.5 E(aults to "smtp"\))-.1 E 48.95(Addr Address)214
-613.8 R(mask \(def)2.5 E(aults IN)-.1 E(ADDR_ANY\))-.35 E -.15(Fa)214 625.8 S
-41.31(mily Address).15 F -.1(fa)2.5 G(mily \(def).1 E(aults to INET\))-.1 E
-44.5(Listen Size)214 637.8 R(of listen queue \(def)2.5 E(aults to 10\))-.1 E
-21.72(SndBufSize Size)214 649.8 R(of TCP send b)2.5 E(uf)-.2 E(fer)-.25 E 21.17
-(RcvBufSize Size)214 661.8 R(of TCP recei)2.5 E .3 -.15(ve b)-.25 H(uf)-.05 E
-(fer)-.25 E(The)174 678 Q F2(Addr)2.5 E F1
-(ess mask may be a numeric address in dot notation or a netw)A(ork name.)-.1 E
-(Def)102 694.2 Q(aultCharSet=)-.1 E F2 -.15(ch)C(ar).15 E(set)-.1 E F1 .16
-([no short name] When a message that has 8-bit characters b)174 706.2 R .161
-(ut is not in MIME for)-.2 F(-)-.2 E .495(mat is con)174 718.2 R -.15(ve)-.4 G
-.495(rted to MIME \(see the EightBitMode option\) a character set must be).15 F
-EP
-%%Page: 40 35
-%%BeginPageSetup
-BP
-%%EndPageSetup
-/F0 10/Times-Bold@0 SF 193.36(SMM:08-40 Sendmail)72 60 R
-(Installation and Operation Guide)2.5 E/F1 10/Times-Roman@0 SF .487
-(included in the Content-T)174 96 R .487(ype: header)-.8 F 5.487(.T)-.55 G .488
-(his character set is normally set from the)338.115 96 R .133
-(Charset= \214eld of the mailer descriptor)174 108 R 5.133(.I)-.55 G 2.633(ft)
-337.64 108 S .133(hat is not set, the v)346.383 108 R .133
-(alue of this option is)-.25 F 2.5(used. If)174 120 R
-(this option is not set, the v)2.5 E(alue \231unkno)-.25 E
-(wn-8bit\232 is used.)-.25 E(Def)102 136.2 Q(aultUser=)-.1 E/F2 10
-/Times-Italic@0 SF(user:gr)A(oup)-.45 E F1 .013([u] Set the def)174 148.2 R
-.013(ault userid for mailers to)-.1 F F2(user:gr)2.513 E(oup)-.45 E F1 5.013
-(.I)C(f)386.587 148.2 Q F2(gr)2.513 E(oup)-.45 E F1 .014(is omitted and)2.514 F
-F2(user)2.514 E F1(is)2.514 E 4.307(au)174 160.2 S 1.807
-(ser name \(as opposed to a numeric user id\) the def)187.747 160.2 R 1.806
-(ault group listed in the)-.1 F 1.153
-(/etc/passwd \214le for that user is used as the def)174 172.2 R 1.153
-(ault group.)-.1 F(Both)6.153 E F2(user)3.653 E F1(and)3.653 E F2(gr)3.653 E
-(oup)-.45 E F1 1.153(may be numeric.)174 184.2 R 1.152(Mailers without the)
-6.152 F F2(S)3.652 E F1 1.152(\215ag in the mailer de\214nition will run as)
-3.652 F .142(this user)174 198.2 R 5.142(.D)-.55 G(ef)222.064 198.2 Q .142
-(aults to 1:1.)-.1 F .142(The v)5.142 F .142(alue can also be gi)-.25 F -.15
-(ve)-.25 G 2.642(na).15 G 2.642(sas)400.612 198.2 S .142(ymbolic user name.)
-418.116 198.2 R/F3 7/Times-Roman@0 SF(18)497 194.2 Q F1(Deli)102 214.4 Q -.15
-(ve)-.25 G(ryMode=).15 E F2(x)A F1([d] Deli)4 E -.15(ve)-.25 G 2.5(ri).15 G 2.5
-(nm)223.03 214.4 S(ode)238.31 214.4 Q F2(x)2.5 E F1 5(.L)C -2.25 -.15(eg a)
-273.3 214.4 T 2.5(lm).15 G(odes are:)300.04 214.4 Q 17.22(iD)214 230.6 S(eli)
-241.22 230.6 Q -.15(ve)-.25 G 2.5(ri).15 G(nteracti)268.87 230.6 Q -.15(ve)-.25
-G(ly \(synchronously\)).15 E 15(bD)214 242.6 S(eli)241.22 242.6 Q -.15(ve)-.25
-G 2.5(ri).15 G 2.5(nb)268.87 242.6 S(ackground \(asynchronously\))281.37 242.6
-Q 15(qJ)214 254.6 S(ust queue the message \(deli)237.89 254.6 Q -.15(ve)-.25 G
-2.5(rd).15 G(uring queue run\))367.74 254.6 Q 15(dD)214 266.6 S(efer deli)
-241.22 266.6 Q -.15(ve)-.25 G(ry and all map lookups \(deli).15 E -.15(ve)-.25
-G 2.5(rd).15 G(uring queue run\))415.66 266.6 Q(Def)174 282.8 Q .712
-(aults to `)-.1 F(`b')-.74 E 3.212('i)-.74 G 3.212(fn)244.816 282.8 S 3.211(oo)
-256.358 282.8 S .711(ption is speci\214ed, `)269.569 282.8 R(`i')-.74 E 3.211
-('i)-.74 G 3.211(fi)365.093 282.8 S 3.211(ti)374.414 282.8 S 3.211(ss)383.185
-282.8 S .711(peci\214ed b)394.176 282.8 R .711(ut gi)-.2 F -.15(ve)-.25 G 3.211
-(nn).15 G 3.211(oa)474.869 282.8 S -.18(rg)487.52 282.8 S(u-).18 E .094
-(ment \(i.e., `)174 294.8 R(`Od')-.74 E 2.594('i)-.74 G 2.594(se)246.672 294.8
-S(qui)257.596 294.8 Q -.25(va)-.25 G .094(lent to `).25 F(`Odi')-.74 E 2.594
-('\). The)-.74 F F0<ad76>2.594 E F1 .094(command line \215ag sets this to)2.594
-F F0(i)2.594 E F1(.)A(DialDelay=)102 311 Q F2(sleeptime)A F1 .799
-([no short name] Dial-on-demand netw)174 323 R .798
-(ork connections can see timeouts if a con-)-.1 F .665
-(nection is opened before the call is set up.)174 335 R .665
-(If this is set to an interv)5.665 F .665(al and a con-)-.25 F .743
-(nection times out on the \214rst connection being attempted)174 347 R F2
-(sendmail)3.242 E F1 .742(will sleep for)3.242 F .31
-(this amount of time and try ag)174 359 R 2.81(ain. This)-.05 F .31(should gi)
-2.81 F .61 -.15(ve y)-.25 H .31(our system time to establish).15 F 1.543
-(the connection to your service pro)174 371 R(vider)-.15 E 6.543(.U)-.55 G
-1.543(nits def)354.188 371 R 1.542(ault to seconds, so \231DialDe-)-.1 F
-(lay=5\232 uses a \214v)174 383 Q 2.5(es)-.15 G(econd delay)251.7 383 Q 5(.D)
--.65 G(ef)313.81 383 Q(aults to zero \(no retry\).)-.1 E(DontExpandCnames)102
-399.2 Q .559([no short name] The standards say that all host addresses used in\
- a mail message)174 411.2 R 1.408(must be fully canonical.)174 423.2 R -.15(Fo)
-6.407 G 3.907(re).15 G 1.407(xample, if your host is named \231Cruft.F)302.668
-423.2 R(oo.ORG\232)-.15 E 1.462(and also has an alias of \231FTP)174 435.2 R
-(.F)-1.11 E 1.462(oo.ORG\232, the former name must be used at all)-.15 F 2.631
-(times. This)174 447.2 R .131
-(is enforced during host name canoni\214cation \($[ ... $] lookups\).)2.631 F
-.13(If this)5.13 F .661
-(option is set, the protocols are ignored and the \231wrong\232 thing is done.)
-174 459.2 R(Ho)5.662 E(we)-.25 E -.15(ve)-.25 G -.4(r,).15 G .455
-(the IETF is mo)174 471.2 R .455(ving to)-.15 F -.1(wa)-.25 G .455
-(rd changing this standard, so the beha).1 F .455(viour may become)-.2 F 3.009
-(acceptable. Please)174 483.2 R .509(note that hosts do)3.009 F .509
-(wnstream may still re)-.25 F .509(write the address to be)-.25 F
-(the true canonical name ho)174 495.2 Q(we)-.25 E -.15(ve)-.25 G -.55(r.).15 G
-6.17(DontInitGroups [no)102 511.4 R .25(short name] If set,)2.75 F F2(sendmail)
-2.75 E F1 .25(will a)2.75 F -.2(vo)-.2 G .25
-(id using the initgroups\(3\) call.).2 F .25(If you are)5.25 F .583(running NI\
-S, this causes a sequential scan of the groups.byname map, which can)174 523.4
-R .436(cause your NIS serv)174 535.4 R .436(er to be badly o)-.15 F -.15(ve)
--.15 G .435(rloaded in a lar).15 F .435(ge domain.)-.18 F .435
-(The cost of this)5.435 F .697(is that the only group found for users will be \
-their primary group \(the one in the)174 547.4 R(passw)174 559.4 Q 1.189
-(ord \214le\), which will mak)-.1 F 3.689<658c>-.1 G 1.189
-(le access permissions some)315.845 559.4 R 1.189(what more restric-)-.25 F(ti)
-174 571.4 Q -.15(ve)-.25 G 5(.H).15 G(as no ef)203.32 571.4 Q
-(fect on systems that don')-.25 E 2.5(th)-.18 G -2.25 -.2(av e)344.26 571.4 T
-(group lists.)2.7 E -1.61(DontPruneRoutes [R])102 587.6 R(Normally)3.905 E(,)
--.65 E F2(sendmail)3.905 E F1 1.405(tries to eliminate an)3.905 F 3.905(yu)-.15
-G 1.405(nnecessary e)372.465 587.6 R 1.405(xplicit routes when)-.15 F .155
-(sending an error message \(as discussed in RFC 1123 \247 5.2.6\).)174 599.6 R
--.15(Fo)5.154 G 2.654(re).15 G .154(xample, when)447.746 599.6 R
-(sending an error message to)174 611.6 Q(<@kno)214 627.8 Q(wn1,@kno)-.25 E
-(wn2,@kno)-.25 E(wn3:user@unkno)-.25 E(wn>)-.25 E F2(sendmail)174 644 Q F1
-1.155(will strip of)3.655 F 3.655(ft)-.25 G 1.155(he \231@kno)272.26 644 R
-(wn1,@kno)-.25 E 1.155(wn2\232 in order to mak)-.25 F 3.655(et)-.1 G 1.155
-(he route as)458.37 644 R .813(direct as possible.)174 656 R(Ho)5.813 E(we)-.25
-E -.15(ve)-.25 G 1.613 -.4(r, i).15 H 3.313(ft).4 G(he)306.435 656 Q F0(R)3.313
-E F1 .812(option is set, this will be disabled, and the)3.313 F .009
-(mail will be sent to the \214rst address in the route, e)174 668 R -.15(ve)
--.25 G 2.51(ni).15 G 2.51(fl)392.86 668 S .01(ater addresses are kno)401.48 668
-R(wn.)-.25 E(This may be useful if you are caught behind a \214re)174 680 Q -.1
-(wa)-.25 G(ll.).1 E .32 LW 76 689.6 72 689.6 DL 80 689.6 76 689.6 DL 84 689.6
-80 689.6 DL 88 689.6 84 689.6 DL 92 689.6 88 689.6 DL 96 689.6 92 689.6 DL 100
-689.6 96 689.6 DL 104 689.6 100 689.6 DL 108 689.6 104 689.6 DL 112 689.6 108
-689.6 DL 116 689.6 112 689.6 DL 120 689.6 116 689.6 DL 124 689.6 120 689.6 DL
-128 689.6 124 689.6 DL 132 689.6 128 689.6 DL 136 689.6 132 689.6 DL 140 689.6
-136 689.6 DL 144 689.6 140 689.6 DL 148 689.6 144 689.6 DL 152 689.6 148 689.6
-DL 156 689.6 152 689.6 DL 160 689.6 156 689.6 DL 164 689.6 160 689.6 DL 168
-689.6 164 689.6 DL 172 689.6 168 689.6 DL 176 689.6 172 689.6 DL 180 689.6 176
-689.6 DL 184 689.6 180 689.6 DL 188 689.6 184 689.6 DL 192 689.6 188 689.6 DL
-196 689.6 192 689.6 DL 200 689.6 196 689.6 DL 204 689.6 200 689.6 DL 208 689.6
-204 689.6 DL 212 689.6 208 689.6 DL 216 689.6 212 689.6 DL/F4 5/Times-Roman@0
-SF(18)93.6 700 Q/F5 8/Times-Roman@0 SF(The old)3.2 I/F6 8/Times-Bold@0 SF(g)2 E
-F5(option has been combined into the)2 E F6(DefaultUser)2 E F5(option.)2 E EP
-%%Page: 41 36
-%%BeginPageSetup
-BP
-%%EndPageSetup
-/F0 10/Times-Bold@0 SF(Sendmail Installation and Operation Guide)72 60 Q
-(SMM:08-41)452.9 60 Q/F1 10/Times-Roman@0 SF(EightBitMode=)102 96 Q/F2 10
-/Times-Italic@0 SF(action)A F1 1.956([8] Set handling of eight-bit data.)174
-108 R 1.955(There are tw)6.955 F 4.455(ok)-.1 G 1.955
-(inds of eight-bit data: that)392.85 108 R 3.334(declared as such using the)174
-120 R F0(BOD)5.834 E(Y=8BITMIME)-.4 E F1 3.335(ESMTP declaration or the)5.835 F
-F0(\255B8BITMIME)174 132 Q F1 .948
-(command line \215ag, and undeclared 8-bit data, that is, input that)3.449 F
-1.18(just happens to be eight bits.)174 144 R 1.18
-(There are three basic operations that can happen:)6.18 F .996
-(undeclared 8-bit data can be automatically con)174 156 R -.15(ve)-.4 G .995
-(rted to 8BITMIME, undeclared).15 F .887
-(8-bit data can be passed as-is without con)174 168 R -.15(ve)-.4 G .887
-(rsion to MIME \(`).15 F .887(`just send 8')-.74 F .887('\), and)-.74 F 5.989
-(declared 8-bit data can be con)174 180 R -.15(ve)-.4 G 5.989
-(rted to 7-bits for transmission to a).15 F(non-8BITMIME mailer)174 192 Q 5(.T)
--.55 G(he possible)281.77 192 Q F2(action)2.5 E F1 2.5(sa)C(re:)364.82 192 Q
-11.11(sR)219 208.2 S(eject undeclared 8-bit data \(`)240.67 208.2 Q(`strict')
--.74 E('\))-.74 E 7.22(mC)219 220.2 S(on)240.67 220.2 Q -.15(ve)-.4 G
-(rt undeclared 8-bit data to MIME \(`).15 E(`mime')-.74 E('\))-.74 E 10(pP)219
-232.2 S(ass undeclared 8-bit data \(`)239.41 232.2 Q(`pass')-.74 E('\))-.74 E
-2.227(In all cases properly declared 8BITMIME data will be con)174 248.4 R -.15
-(ve)-.4 G 2.228(rted to 7BIT as).15 F(needed.)174 260.4 Q(ErrorHeader=)102
-276.6 Q F2(\214le-or)A(-messa)-.2 E -.1(ge)-.1 G F1 .486
-([E] Prepend error messages with the indicated message.)174 288.6 R .486
-(If it be)5.486 F .486(gins with a slash,)-.15 F .246(it is assumed to be the \
-pathname of a \214le containing a message \(this is the recom-)174 300.6 R .86
-(mended setting\).)174 312.6 R .86(Otherwise, it is a literal message.)5.86 F
-.86(The error \214le might contain)5.86 F 1.116
-(the name, email address, and/or phone number of a local postmaster who could)
-174 324.6 R(pro)174 336.6 Q .174(vide assistance in to end users.)-.15 F .173
-(If the option is missing or null, or if it names a)5.174 F
-(\214le which does not e)174 348.6 Q
-(xist or which is not readable, no message is printed.)-.15 E(ErrorMode=)102
-364.8 Q F2(x)A F1([e] Dispose of errors using mode)174 364.8 Q F2(x)2.5 E F1 5
-(.T)C(he v)325.91 364.8 Q(alues for)-.25 E F2(x)2.5 E F1(are:)2.5 E 15(pP)214
-381 S(rint error messages \(def)239.56 381 Q(ault\))-.1 E 15(qN)214 393 S 2.5
-(om)241.22 393 S(essages, just gi)256.5 393 Q .3 -.15(ve ex)-.25 H(it status)
-.15 E 12.22(mM)214 405 S(ail back errors)242.89 405 Q 12.78(wW)214 417 S
-(rite back errors \(mail if user not logged in\))243.44 417 Q 15.56(eM)214 429
-S(ail back errors and gi)242.89 429 Q .3 -.15(ve z)-.25 H(ero e).15 E
-(xit stat al)-.15 E -.1(wa)-.1 G(ys).1 E -.15(Fa)102 449.4 S(llbackMXhost=).15
-E F2(fallbac)A(khost)-.2 E F1 .796([V] If speci\214ed, the)174 461.4 R F2
-(fallbac)3.296 E(khost)-.2 E F1 .796(acts lik)3.296 F 3.296(eav)-.1 G .797
-(ery lo)359.608 461.4 R 3.297(wp)-.25 G .797(riority MX on e)398.722 461.4 R
--.15(ve)-.25 G .797(ry host.).15 F
-(This is intended to be used by sites with poor netw)174 473.4 Q(ork connecti)
--.1 E(vity)-.25 E(.)-.65 E -.15(Fo)102 489.6 S 16.88(rkEachJob [Y]).15 F .708
-(If set, deli)3.208 F -.15(ve)-.25 G 3.208(re).15 G .707
-(ach job that is run from the queue in a separate process.)252.792 489.6 R(Use)
-5.707 E .274(this option if you are short of memory)174 501.6 R 2.774(,s)-.65 G
-.274(ince the def)336.922 501.6 R .275(ault tends to consume consid-)-.1 F
-(erable amounts of memory while the queue is being processed.)174 513.6 Q -.15
-(Fo)102 529.8 S(rw).15 E(ardP)-.1 E(ath=)-.15 E F2(path)A F1 4.675
-([J] Set the path for searching for users' .forw)174 541.8 R 4.675
-(ard \214les.)-.1 F 4.675(The def)9.675 F 4.675(ault is)-.1 F(\231$z/.forw)174
-553.8 Q 3.23(ard\232. Some)-.1 F .731
-(sites that use the automounter may prefer to change this to)3.23 F(\231/v)174
-565.8 Q(ar/forw)-.25 E 1.696
-(ard/$u\232 to search a \214le with the same name as the user in a system)-.1 F
-(directory)174 577.8 Q 5.487(.I)-.65 G 2.987(tc)220.767 577.8 S .488
-(an also be set to a sequence of paths separated by colons;)230.974 577.8 R F2
-(sendmail)2.988 E F1 4.218
-(stops at the \214rst \214le it can successfully and safely open.)174 589.8 R
--.15(Fo)9.217 G 6.717(re).15 G(xample,)472.06 589.8 Q(\231/v)174 601.8 Q
-(ar/forw)-.25 E(ard/$u:$z/.forw)-.1 E .681(ard\232 will search \214rst in /v)
--.1 F(ar/forw)-.25 E(ard/)-.1 E F2(username)A F1 .682(and then)3.182 F(in)174
-613.8 Q F2(~username)2.5 E F1(/.forw)A(ard \(b)-.1 E
-(ut only if the \214rst \214le does not e)-.2 E(xist\).)-.15 E(HelpFile=)102
-630 Q F2(\214le)A F1([H] Specify the help \214le for SMTP)174 630 Q(.)-1.11 E
-(HoldExpensi)102 646.2 Q 8.54 -.15(ve [)-.25 H 1.394
-(c] If an outgoing mailer is mark).15 F 1.393(ed as being e)-.1 F(xpensi)-.15 E
--.15(ve)-.25 G 3.893(,d).15 G(on')415.294 646.2 Q 3.893(tc)-.18 G 1.393
-(onnect immedi-)439.557 646.2 R(ately)174 658.2 Q 5.267(.T)-.65 G .268
-(his requires that queueing be compiled in, since it will depend on a queue)
-206.667 658.2 R(run process to actually send the mail.)174 670.2 Q 24.51
-(IgnoreDots [i])102 686.4 R 1.172(Ignore dots in incoming messages.)3.672 F
-1.172(This is al)6.172 F -.1(wa)-.1 G 1.171(ys disabled \(that is, dots are).1
-F(al)174 698.4 Q -.1(wa)-.1 G(ys accepted\) when reading SMTP mail.).1 E EP
-%%Page: 42 37
-%%BeginPageSetup
-BP
-%%EndPageSetup
-/F0 10/Times-Bold@0 SF 193.36(SMM:08-42 Sendmail)72 60 R
-(Installation and Operation Guide)2.5 E/F1 10/Times-Roman@0 SF(LogLe)102 96 Q
--.15(ve)-.25 G(l=).15 E/F2 10/Times-Italic@0 SF(n)A F1([L] Set the def)174 96 Q
-(ault log le)-.1 E -.15(ve)-.25 G 2.5(lt).15 G(o)289.04 96 Q F2(n)2.5 E F1 5
-(.D)C(ef)316.26 96 Q(aults to 9.)-.1 E(M)102 112.2 Q F2 1.666(xv)C(alue)-1.666
-E F1 .255([no long v)174 112.2 R .255(ersion] Set the macro)-.15 F F2(x)2.755 E
-F1(to)2.755 E F2(value)2.755 E F1 5.255(.T)C .255
-(his is intended only for use from the)357.505 112.2 R(command line.)174 124.2
-Q(The)5 E F0<ad4d>2.5 E F1(\215ag is preferred.)2.5 E 11.17(MatchGECOS [G])102
-140.4 R(Allo)3.334 E 3.334(wf)-.25 G .834(uzzy matching on the GECOS \214eld.)
-222.628 140.4 R .833(If this \215ag is set, and the usual)5.833 F .867
-(user name lookups f)174 152.4 R .867
-(ail \(that is, there is no alias with this name and a)-.1 F F2 -.1(ge)3.368 G
-(tpwnam).1 E F1 -.1(fa)174 164.4 S 1.155(ils\), sequentially search the passw)
-.1 F 1.155(ord \214le for a matching entry in the GECOS)-.1 F 3.696
-(\214eld. This)174 176.4 R 1.196(also requires that MA)3.696 F 1.196
-(TCHGECOS be turned on during compilation.)-1.11 F
-(This option is not recommended.)174 188.4 Q(MaxHopCount=)102 204.6 Q F2(N)A F1
-1.238([h] The maximum hop count.)174 216.6 R 1.238(Messages that ha)6.238 F
-1.537 -.15(ve b)-.2 H 1.237(een processed more than).15 F F2(N)3.737 E F1
-(times are assumed to be in a loop and are rejected.)174 228.6 Q(Def)5 E
-(aults to 25.)-.1 E(MaxHostStatAge=)102 244.8 Q F2 -.1(age)C F1 .438
-([no short name] Not yet implemented.)174 256.8 R .438
-(This option speci\214es ho)5.438 F 2.939(wl)-.25 G .439(ong host status)
-443.672 256.8 R .36(information will be retained.)174 268.8 R -.15(Fo)5.36 G
-2.86(re).15 G .36(xample, if a host is found to be do)315.76 268.8 R .36
-(wn, connec-)-.25 F .246
-(tions to that host will not be retried for this interv)174 280.8 R 2.746
-(al. The)-.25 F .246(units def)2.746 F .246(ault to minutes.)-.1 F
-(MaxQueueRunSize=)102 297 Q F2(N)A F1 .677
-([no short name] The maximum number of jobs that will be processed in a single)
-174 309 R .501(queue run.)174 321 R .501
-(If not set, there is no limit on the size.)5.501 F .501(If you ha)5.501 F .802
--.15(ve ve)-.2 H .502(ry lar).15 F .502(ge queues)-.18 F .445(or a v)174 333 R
-.445(ery short queue run interv)-.15 F .445(al this could be unstable.)-.25 F
-(Ho)5.445 E(we)-.25 E -.15(ve)-.25 G 1.245 -.4(r, s).15 H .445
-(ince the \214rst).4 F F2(N)174 345 Q F1 1.115
-(jobs in queue directory order are run \(rather than the)3.615 F F2(N)3.615 E
-F1 1.115(highest priority jobs\))3.615 F .136
-(this should be set as high as possible to a)174 357 R -.2(vo)-.2 G .136
-(id \231losing\232 jobs that happen to f).2 F .136(all late)-.1 F
-(in the queue directory)174 369 Q(.)-.65 E(MeT)102 385.2 Q 40.86(oo [m])-.8 F
-(Send to me too, e)2.5 E -.15(ve)-.25 G 2.5(ni).15 G 2.5(fIa)279.98 385.2 S 2.5
-(mi)296.08 385.2 S 2.5(na)309.14 385.2 S 2.5(na)321.08 385.2 S(lias e)333.02
-385.2 Q(xpansion.)-.15 E(MaxMessageSize=)102 401.4 Q F2(N)A F1 2.562
-([no short name] Specify the maximum message size to be adv)174 413.4 R 2.563
-(ertised in the)-.15 F(ESMTP EHLO response.)174 425.4 Q(Messages lar)5 E
-(ger than this will be rejected.)-.18 E(MinFreeBlocks=)102 441.6 Q F2(N)A F1
-1.539([b] Insist on at least)174 453.6 R F2(N)4.039 E F1 1.538
-(blocks free on the \214lesystem that holds the queue \214les)4.039 F .845
-(before accepting email via SMTP)174 465.6 R 5.846(.I)-1.11 G 3.346(ft)322.368
-465.6 S .846(here is insuf)331.824 465.6 R .846(\214cient space)-.25 F F2
-(sendmail)3.346 E F1(gi)3.346 E -.15(ve)-.25 G 3.346(sa).15 G
-(452 response to the MAIL command.)174 477.6 Q(This in)5 E
-(vites the sender to try ag)-.4 E(ain later)-.05 E(.)-.55 E(MinQueueAge=age)102
-493.8 Q .887([no short name] Don')174 505.8 R 3.387(tp)-.18 G .887(rocess an)
-274.018 505.8 R 3.387(yq)-.15 G .886(ueued jobs that ha)325.072 505.8 R 1.186
--.15(ve b)-.2 H .886(een in the queue less).15 F 1.899
-(than the indicated time interv)174 517.8 R 4.399(al. This)-.25 F 1.899
-(is intended to allo)4.399 F 4.399(wy)-.25 G 1.9(ou to get respon-)430.81 517.8
-R(si)174 529.8 Q -.15(ve)-.25 G .665(ness by processing the queue f).15 F .665
-(airly frequently without thrashing your system)-.1 F
-(by trying jobs too often.)174 541.8 Q(The def)5 E(ault units are minutes.)-.1
-E(NoRecipientAction)102 558 Q .554([no short name] The action to tak)174 570 R
-3.055(ew)-.1 G .555(hen you recei)325.25 570 R .855 -.15(ve a m)-.25 H .555
-(essage that has no v).15 F(alid)-.25 E .062(recipient headers \(T)174 582 R
-.062(o:, Cc:, Bcc:\).)-.8 F .062(It can be)5.062 F F0(None)2.561 E F1 .061
-(to pass the message on unmodi-)2.561 F .51
-(\214ed, which violates the protocol,)174 594 R F0(Add-T)3.01 E(o)-.92 E F1 .51
-(to add a T)3.01 F .51(o: header with an)-.8 F 3.01(yr)-.15 G(ecipients)468.45
-594 Q 4.41(it can \214nd in the en)174 606 R -.15(ve)-.4 G 4.41
-(lope \(which might e).15 F 4.41(xpose Bcc: recipients\),)-.15 F F0(Add-)6.91 E
--.25(Ap)174 618 S(par).25 E(ently-T)-.18 E(o)-.92 E F1 4.664
-(to add an Apparently-T)7.164 F 4.664(o: header \(this is only for back-)-.8 F
-.875(compatibility and is of)174 630 R .875(\214cially deprecated\),)-.25 F F0
-(Add-T)3.374 E(o-Undisclosed)-.92 E F1 .874(to add a header)3.374 F<9954>174
-642 Q 1.594(o: undisclosed-recipients:;\232 to mak)-.8 F 4.094(et)-.1 G 1.594
-(he header le)339.456 642 R -.05(ga)-.15 G 4.095(lw).05 G 1.595
-(ithout disclosing an)414.29 642 R(y-)-.15 E(thing, or)174 654 Q F0(Add-Bcc)2.5
-E F1(to add an empty Bcc: header)2.5 E(.)-.55 E 1.18(OldStyleHeaders [o])102
-670.2 R 1.713
-(Assume that the headers may be in old format, i.e., spaces delimit names.)
-4.214 F 1.068(This actually turns on an adapti)174 682.2 R 1.368 -.15(ve a)-.25
-H 1.068(lgorithm: if an).15 F 3.569(yr)-.15 G 1.069
-(ecipient address contains a)393.873 682.2 R 1.681
-(comma, parenthesis, or angle brack)174 694.2 R 1.681
-(et, it will be assumed that commas already)-.1 F -.15(ex)174 706.2 S 2.825
-(ist. If).15 F .325(this \215ag is not on, only commas delimit names.)2.825 F
-.325(Headers are al)5.325 F -.1(wa)-.1 G .325(ys out-).1 F
-(put with commas between the names.)174 718.2 Q(Def)5 E(aults to of)-.1 E(f.)
--.25 E EP
-%%Page: 43 38
-%%BeginPageSetup
-BP
-%%EndPageSetup
-/F0 10/Times-Bold@0 SF(Sendmail Installation and Operation Guide)72 60 Q
-(SMM:08-43)452.9 60 Q/F1 10/Times-Roman@0 SF(OperatorChars=)102 96 Q/F2 10
-/Times-Italic@0 SF -.15(ch)C(arlist).15 E F1 1.438([$o macro] The list of char\
-acters that are considered to be \231operators\232, that is,)174 108 R .82
-(characters that delimit tok)174 120 R 3.32(ens. All)-.1 F .82
-(operator characters are tok)3.32 F .82(ens by themselv)-.1 F(es;)-.15 E .078
-(sequences of non-operator characters are also tok)174 132 R 2.578(ens. White)
--.1 F .078(space characters sep-)2.578 F .269(arate tok)174 144 R .269(ens b)
--.1 F .269(ut are not tok)-.2 F .269(ens themselv)-.1 F .269(es \212 for e)-.15
-F .269(xample, \231)-.15 F .27(AAA.BBB\232 has three)-.8 F(tok)174 156 Q .433
-(ens, b)-.1 F .433(ut \231)-.2 F .433(AAA BBB\232 has tw)-.8 F 2.933(o. If)-.1
-F .433(not set, OperatorChars def)2.933 F .433(aults to \231.)-.1 F 1.666(:@[])
-1.666 G<9a3b>-1.666 E(additionally)174 168 Q 2.5(,t)-.65 G
-(he characters \231\()228.91 168 Q 1.666(\)<>,;)1.666 G 2.5<9a61>-1.666 G
-(re al)331.25 168 Q -.1(wa)-.1 G(ys operators.).1 E(PostmasterCop)102 184.2 Q
-(y=)-.1 E F2(postmaster)A F1 .003
-([P] If set, copies of error messages will be sent to the named)174 196.2 R F2
-(postmaster)2.504 E F1 5.004(.O)C .004(nly the)476.496 196.2 R .627
-(header of the f)174 208.2 R .627(ailed message is sent.)-.1 F .626
-(Since most errors are user problems, this is)5.626 F .453
-(probably not a good idea on lar)174 220.2 R .453(ge sites, and ar)-.18 F .453
-(guably contains all sorts of pri)-.18 F -.25(va)-.25 G -.15(cy).25 G 1.979
-(violations, b)174 232.2 R 1.978
-(ut it seems to be popular with certain operating systems v)-.2 F(endors.)-.15
-E(Def)174 244.2 Q(aults to no postmaster copies.)-.1 E(Pri)102 260.4 Q -.25(va)
--.25 G -.15(cy).25 G(Options=).15 E F2(opt,opt,...)1.666 E F1 1.191
-([p] Set the pri)174 272.4 R -.25(va)-.25 G -.15(cy).25 G F2(opt)3.841 E F1
-3.691(ions. `)B(`Pri)-.74 E -.25(va)-.25 G -.15(cy).25 G 2.671 -.74('' i).15 H
-3.692(sr).74 G 1.192(eally a misnomer; man)352.028 272.4 R 3.692(yo)-.15 G
-3.692(ft)460.526 272.4 S 1.192(hese are)470.328 272.4 R .929(just a w)174 284.4
-R .928(ay of insisting on stricter adherence to the SMTP protocol.)-.1 F(The)
-5.928 E F2(opt)3.428 E F1(ions)A(can be selected from:)174 296.4 Q 40.26
-(public Allo)214 312.6 R 2.5(wo)-.25 G(pen access)314.01 312.6 Q 11.38
-(needmailhelo Insist)214 324.6 R(on HELO or EHLO command before MAIL)2.5 E
-(neede)214 336.6 Q 9.87(xpnhelo Insist)-.15 F
-(on HELO or EHLO command before EXPN)2.5 E(noe)214 348.6 Q 35.97(xpn Disallo)
--.15 F 2.5(wE)-.25 G(XPN entirely)326.23 348.6 Q 12.5(needvrfyhelo Insist)214
-360.6 R(on HELO or EHLO command before VRFY)2.5 E(no)214 372.6 Q 38.75
-(vrfy Disallo)-.15 F 2.5(wV)-.25 G(RFY entirely)327.34 372.6 Q 14.71
-(restrictmailq Restrict)214 384.6 R(mailq command)2.5 E 19.16
-(restrictqrun Restrict)214 396.6 R(\255q command line \215ag)2.5 E 24.16
-(noreceipts Don')214 408.6 R 2.5(tr)-.18 G(eturn success DSNs)310.74 408.6 Q
-(goa)214 420.6 Q -.1(wa)-.15 G 36.91(yD).1 G(isallo)288.98 420.6 Q 2.5(we)-.25
-G(ssentially all SMTP status queries)324.56 420.6 Q(authw)214 432.6 Q 11.48
-(arnings Put)-.1 F(X-Authentication-W)2.5 E(arning: headers in messages)-.8 E
-.485(The \231goa)174 448.8 R -.1(wa)-.15 G .485
-(y\232 pseudo-\215ag sets all \215ags e).1 F .486
-(xcept \231restrictmailq\232 and \231restrictqrun\232.)-.15 F 1.175(If mailq i\
-s restricted, only people in the same group as the queue directory can)174
-460.8 R .207(print the queue.)174 472.8 R .207
-(If queue runs are restricted, only root and the o)5.207 F .208
-(wner of the queue)-.25 F .066(directory can run the queue.)174 484.8 R .066
-(Authentication W)5.066 F .066(arnings add w)-.8 F .066(arnings about v)-.1 F
-(arious)-.25 E .77(conditions that may indicate attempts to spoof the mail sys\
-tem, such as using an)174 496.8 R(non-standard queue directory)174 508.8 Q(.)
--.65 E(QueueDirectory=)102 525 Q F2(dir)A F1([Q] Use the named)174 537 Q F2
-(dir)2.5 E F1(as the queue directory)2.5 E(.)-.65 E(QueueF)102 553.2 Q(actor=)
--.15 E F2(factor)A F1 .614([q] Use)174 565.2 R F2(factor)3.114 E F1 .613
-(as the multiplier in the map function to decide when to just queue)3.114 F
-.415(up jobs rather than run them.)174 577.2 R .415(This v)5.415 F .415
-(alue is di)-.25 F .415(vided by the dif)-.25 F .415(ference between the)-.25 F
-1.004(current load a)174 589.2 R -.15(ve)-.2 G 1.004(rage and the load a).15 F
--.15(ve)-.2 G 1.004(rage limit \().15 F F0(QueueLA)A F1 1.003
-(option\) to determine)3.503 F(the maximum message priority that will be sent.)
-174 601.2 Q(Def)5 E(aults to 600000.)-.1 E(QueueLA=)102 617.4 Q F2(LA)A F1 .164
-([x] When the system load a)174 617.4 R -.15(ve)-.2 G .165(rage e).15 F(xceeds)
--.15 E F2(LA)2.665 E F1 2.665(,j)C .165(ust queue messages \(i.e., don')367.265
-617.4 R 2.665(tt)-.18 G(ry)495.67 617.4 Q(to send them\).)174 629.4 Q(Def)5 E
-(aults to 8.)-.1 E(QueueSortOrder=)102 645.6 Q F2(algorithm)A F1 .097
-([no short name] Sets the)174 657.6 R F2(algorithm)2.597 E F1 .096
-(used for sorting the queue.)2.597 F .096(Only the \214rst char)5.096 F(-)-.2 E
-1.021(acter of the v)174 669.6 R 1.021(alue is used.)-.25 F(Le)6.021 E -.05(ga)
--.15 G 3.521(lv).05 G 1.021
-(alues are \231host\232 \(to order by the name of the)317.357 669.6 R .922(\
-\214rst host name of the \214rst recipient\) and \231priority\232 \(to order s\
-trictly by message)174 681.6 R 2.527(priority\). Host)174 693.6 R .027
-(ordering mak)2.527 F .028(es better use of the connection cache, b)-.1 F .028
-(ut may tend to)-.2 F .323(process lo)174 705.6 R 2.823(wp)-.25 G .322
-(riority messages that go to a single host o)229.386 705.6 R -.15(ve)-.15 G
-2.822(rh).15 G .322(igh priority messages)417.806 705.6 R 1.824(that go to se)
-174 717.6 R -.15(ve)-.25 G 1.824(ral hosts; it probably shouldn').15 F 4.325
-(tb)-.18 G 4.325(eu)376.345 717.6 S 1.825(sed on slo)390.11 717.6 R 4.325(wn)
--.25 G(etw)450.055 717.6 Q 1.825(ork links.)-.1 F EP
-%%Page: 44 39
-%%BeginPageSetup
-BP
-%%EndPageSetup
-/F0 10/Times-Bold@0 SF 193.36(SMM:08-44 Sendmail)72 60 R
-(Installation and Operation Guide)2.5 E/F1 10/Times-Roman@0 SF
-(Priority ordering is the def)174 96 Q(ault.)-.1 E(Resolv)102 112.2 Q
-(erOptions=)-.15 E/F2 10/Times-Italic@0 SF(options)A F1 .128([I] Set resolv)174
-124.2 R .127(er options.)-.15 F -1.11(Va)5.127 G .127(lues can be set using)
-1.11 F F0(+)2.627 E F2<8d61>A(g)-.1 E F1 .127(and cleared using)2.627 F F0<ad>
-2.627 E F2<8d61>A(g)-.1 E F1 2.627(;t)C(he)494.56 124.2 Q F2<8d61>174 136.2 Q
-(g)-.1 E F1 5.013(sc)C 2.513(an be \231deb)202.243 136.2 R 2.513
-(ug\232, \231aaonly\232, \231use)-.2 F 2.514
-(vc\232, \231primary\232, \231igntc\232, \231recurse\232, \231def-)-.25 F .867
-(names\232, \231stayopen\232, or \231dnsrch\232.)174 148.2 R .867
-(The string \231HasW)5.867 F .867(ildcardMX\232 \(without a)-.4 F F0(+)3.367 E
-F1(or)3.367 E F0<ad>174 160.2 Q F1 3.82(\)c)C 1.32
-(an be speci\214ed to turn of)191.29 160.2 R 3.82(fm)-.25 G 1.32(atching ag)
-311.72 160.2 R 1.32(ainst MX records when doing name)-.05 F(canoni\214cations.)
-174 172.2 Q F0(N.B.)5.918 E F1 .917
-(Prior to 8.7, this option indicated that the name serv)5.918 F .917(er be)-.15
-F 1.025(responding in order to accept addresses.)174 184.2 R 1.025
-(This has been replaced by checking to)6.025 F .078(see if the \231dns\232 met\
-hod is listed in the service switch entry for the \231hosts\232 service.)174
-196.2 R(SmtpGreetingMessage=)102 212.4 Q F2(messa)A -.1(ge)-.1 G F1 .344
-([$e macro] The message printed when the SMTP serv)174 224.4 R .345
-(er starts up.)-.15 F(Def)5.345 E .345(aults to \231$j)-.1 F
-(Sendmail $v ready at $b\232.)174 236.4 Q -.35(Ti)102 252.6 S(meout.).35 E F2
-(type)A F1(=)A F2(timeout)1.666 E F1 .297
-([r; subsumes old T option as well] Set timeout v)174 264.6 R 2.796(alues. The)
--.25 F .296(actual timeout is indi-)2.796 F 1.678(cated by the)174 276.6 R F2
-(type)4.178 E F1 6.678(.T)C 1.678(he recognized timeouts and their def)261.802
-276.6 R 1.679(ault v)-.1 F 1.679(alues, and their)-.25 F(minimum v)174 288.6 Q
-(alues speci\214ed in RFC 1123 section 5.3.2 are:)-.25 E 23.6(initial w)214
-304.8 R(ait for initial greeting message [5m, 5m])-.1 E 29.72(helo reply)214
-316.8 R(to HELO or EHLO command [5m, none])2.5 E 29.16(mail reply)214 328.8 R
-(to MAIL command [10m, 5m])2.5 E 31.39(rcpt reply)214 340.8 R
-(to RCPT command [1h, 5m])2.5 E 16.94(datainit reply)214 352.8 R(to D)2.5 E
--1.21 -1.11(AT A)-.4 H(command [5m, 2m])3.61 E 8.06(datablock data)214 364.8 R
-(block read [1h, 3m])2.5 E 12.5(data\214nal reply)214 376.8 R(to \214nal `)2.5
-E(`.)-.74 E 1.48 -.74('' i)-.7 H 2.5(nd).74 G(ata [1h, 10m])348.47 376.8 Q 32.5
-(rset reply)214 388.8 R(to RSET command [5m, none])2.5 E 31.38(quit reply)214
-400.8 R(to Q)2.5 E(UIT command [2m, none])-.1 E 28.05(misc reply)214 412.8 R
-(to NOOP and VERB commands [2m, none])2.5 E 26.94(ident IDENT)214 424.8 R
-(protocol timeout [30s, none])2.5 E 9.72(\214leopen\207 timeout)214 436.8 R
-(on opening .forw)2.5 E(ard and :include: \214les [60s, none])-.1 E 2.5
-(command\207 command)214 448.8 R(read [1h, 5m])2.5 E(queuereturn\207ho)214
-460.8 Q 2.5(wl)-.25 G(ong until a message is returned [5d, 5d])289.01 460.8 Q
-(queue)214 472.8 Q -.1(wa)-.25 G -1.58(rn\207 ho).1 F 2.5(wl)-.25 G
-(ong until a w)285.69 472.8 Q(arning is sent [none, none])-.1 E .893(All b)174
-489 R .893(ut those mark)-.2 F .893
-(ed with a dagger \(\207\) apply to client SMTP)-.1 F 5.892(.I)-1.11 G 3.392
-(ft)437.724 489 S .892(he message is)447.226 489 R .273(submitted using the)174
-501 R/F3 9/Times-Roman@0 SF(NO)2.773 E .523(TIFY SMTP)-.36 F F1 -.15(ex)2.773 G
-.273(tension, w).15 F .274(arning messages will only be sent)-.1 F(if)174 513 Q
-F3(NO)3.038 E(TIFY=DELA)-.36 E(Y)-.945 E F1 .538(is speci\214ed.)3.038 F .537
-(The queuereturn and queue)5.537 F -.1(wa)-.25 G .537(rn timeouts can be).1 F
-1.234(further quali\214ed with a tag based on the Precedence: \214eld in the m\
-essage; the)174 525 R(y)-.15 E 1.9(must be one of \231ur)174 537 R 1.9
-(gent\232 \(indicating a positi)-.18 F 2.2 -.15(ve n)-.25 H 1.9
-(on-zero precedence\) \231normal\232).15 F .251
-(\(indicating a zero precedence\), or \231non-ur)174 549 R .251
-(gent\232 \(indicating ne)-.18 F -.05(ga)-.15 G(ti).05 E .552 -.15(ve p)-.25 H
-(recedences\).).15 E -.15(Fo)174 561 S 4.423(re).15 G 1.923
-(xample, setting \231T)196.453 561 R(imeout.queue)-.35 E -.1(wa)-.25 G(rn.ur).1
-E 1.923(gent=1h\232 sets the w)-.18 F 1.922(arning timeout)-.1 F .222(for ur)
-174 573 R .223(gent messages only to one hour)-.18 F 5.223(.T)-.55 G .223
-(he def)336.749 573 R .223(ault if no precedence is indicated is)-.1 F
-(to set the timeout for all precedences.)174 585 Q(RecipientF)102 601.2 Q
-(actor=)-.15 E F2(fact)A F1 .638([y] The indicated)174 613.2 R F2(fact)3.137 E
-F1 .637(or is added to the priority \(thus)B F2(lowering)3.137 E F1 .637
-(the priority of the)3.137 F .23(job\) for each recipient, i.e., this v)174
-625.2 R .231(alue penalizes jobs with lar)-.25 F .231(ge numbers of recipi-)
--.18 F 2.5(ents. Def)174 637.2 R(aults to 30000.)-.1 E(RefuseLA=)102 653.4 Q F2
-(LA)A F1 1.012([X] When the system load a)174 653.4 R -.15(ve)-.2 G 1.012
-(rage e).15 F(xceeds)-.15 E F2(LA)3.512 E F1 3.512(,r)C 1.011
-(efuse incoming SMTP connec-)376.816 653.4 R 2.5(tions. Def)174 665.4 R
-(aults to 12.)-.1 E(RetryF)102 681.6 Q(actor=)-.15 E F2(fact)A F1 .771([Z] The)
-3.74 F F2(fact)3.271 E F1 .771(or is added to the priority e)B -.15(ve)-.25 G
-.772(ry time a job is processed.).15 F .772(Thus, each)5.772 F .994
-(time a job is processed, its priority will be decreased by the indicated v)174
-693.6 R 3.493(alue. In)-.25 F 1.107(most en)174 705.6 R 1.107
-(vironments this should be positi)-.4 F -.15(ve)-.25 G 3.608(,s).15 G 1.108
-(ince hosts that are do)357.354 705.6 R 1.108(wn are all too)-.25 F(often do)
-174 717.6 Q(wn for a long time.)-.25 E(Def)5 E(aults to 90000.)-.1 E EP
-%%Page: 45 40
-%%BeginPageSetup
-BP
-%%EndPageSetup
-/F0 10/Times-Bold@0 SF(Sendmail Installation and Operation Guide)72 60 Q
-(SMM:08-45)452.9 60 Q/F1 10/Times-Roman@0 SF(Sa)102 96 Q -.15(ve)-.2 G 10.41
-(FromLine [f]).15 F(Sa)4.909 E 2.709 -.15(ve U)-.2 H 2.408
-(nix-style \231From\232 lines at the front of headers.).15 F 2.408
-(Normally the)7.408 F 4.908(ya)-.15 G(re)496.23 96 Q
-(assumed redundant and discarded.)174 108 Q(SendMIMEErrors)102 124.2 Q .815
-([j] If set, send error messages in MIME format \(see RFC1521 and RFC1344 for)
-174 136.2 R(details\).)174 148.2 Q(ServiceSwitchFile=)102 164.4 Q/F2 10
-/Times-Italic@0 SF(\214lename)A F1 1.533([no short name] If your host operatin\
-g system has a service switch abstraction)174 176.4 R .003(\(e.g., /etc/nsswit\
-ch.conf on Solaris or /etc/svc.conf on Ultrix and DEC OSF/1\) that)174 188.4 R
-.814(service will be consulted and this option is ignored.)174 200.4 R .814
-(Otherwise, this is the name)5.814 F 1.082(of a \214le that pro)174 212.4 R
-1.082(vides the list of methods used to implement particular services.)-.15 F
-1.069(The syntax is a series of lines, each of which is a sequence of w)174
-224.4 R 3.569(ords. The)-.1 F(\214rst)3.569 E -.1(wo)174 236.4 S 1.363
-(rd is the service name, and follo).1 F 1.363(wing w)-.25 F 1.364
-(ords are service types.)-.1 F 1.364(The services)6.364 F(that)174 248.4 Q F2
-(sendmail)4.11 E F1 1.61(consults directly are \231aliases\232 and \231hosts.)
-4.11 F 6.61<9a53>-.7 G 1.61(ervice types can be)422.81 248.4 R 1.754
-(\231dns\232, \231nis\232, \231nisplus\232, or \231\214les\232 \(with the ca)
-174 260.4 R -.15(ve)-.2 G 1.755(at that the appropriate support).15 F .791
-(must be compiled in before the service can be referenced\).)174 272.4 R .79
-(If ServiceSwitchFile)5.791 F 1.303(is not speci\214ed, it def)174 284.4 R
-1.303(aults to /etc/service.switch.)-.1 F 1.303(If that \214le does not e)6.303
-F 1.304(xist, the)-.15 F(def)174 296.4 Q(ault switch is:)-.1 E 20.28
-(aliases \214les)214 312.6 R 26.38(hosts dns)214 324.6 R(nis \214les)2.5 E
-(The def)174 340.8 Q(ault \214le is \231/etc/service.switch\232.)-.1 E(Se)102
-357 Q -.15(ve)-.25 G 12.12(nBitInput [7]).15 F .322(Strip input to se)2.822 F
--.15(ve)-.25 G 2.822(nb).15 G .321(its for compatibility with old systems.)
-274.93 357 R .321(This shouldn')5.321 F 2.821(tb)-.18 G(e)499.56 357 Q
-(necessary)174 369 Q(.)-.65 E(StatusFile=)102 385.2 Q F2(\214le)A F1 .299
-([S] Log summary statistics in the named)174 385.2 R F2(\214le)2.799 E F1 5.299
-(.I)C 2.799(fn)363.602 385.2 S .3(ot set, no summary statistics are)374.731
-385.2 R(sa)174 397.2 Q -.15(ve)-.2 G 3.775(d. This).15 F 1.275
-(\214le does not gro)3.775 F 3.775(wi)-.25 G 3.775(ns)308.82 397.2 S 3.775
-(ize. It)321.485 397.2 R 1.275(can be printed using the)3.775 F F2(mailstats)
-3.775 E F1(\(8\))A(program.)174 409.2 Q 28.4(SuperSafe [s])102 425.4 R .372
-(Be super)2.872 F .372(-safe when running things, i.e., al)-.2 F -.1(wa)-.1 G
-.373(ys instantiate the queue \214le, e).1 F -.15(ve)-.25 G(n).15 E .697
-(if you are going to attempt immediate deli)174 437.4 R -.15(ve)-.25 G(ry).15 E
-(.)-.65 E F2(Sendmail)5.697 E F1(al)3.197 E -.1(wa)-.1 G .697
-(ys instantiates the).1 F 2.688
-(queue \214le before returning control the client under an)174 449.4 R 5.188
-(yc)-.15 G 5.188(ircumstances. This)423.822 449.4 R(should really)174 461.4 Q
-F2(always)2.5 E F1(be set.)2.5 E -.7(Te)102 477.6 S(mpFileMode=).7 E F2(mode)A
-F1 .332([F] The \214le mode for queue \214les.)174 489.6 R .331
-(It is interpreted in octal by def)5.331 F 2.831(ault. Def)-.1 F .331(aults to)
--.1 F(0600.)174 501.6 Q -.35(Ti)102 517.8 S(meZoneSpec=).35 E F2(tzinfo)A F1
-.218([t] Set the local time zone info to)174 529.8 R F2(tzinfo)2.718 E F1 2.718
-<8a66>2.718 G .218(or e)351.168 529.8 R .218(xample, \231PST8PDT\232.)-.15 F
-(Actually)5.218 E 2.718(,i)-.65 G(f)500.67 529.8 Q 1.346
-(this is not set, the TZ en)174 541.8 R 1.346(vironment v)-.4 F 1.346
-(ariable is cleared \(so the system def)-.25 F 1.345(ault is)-.1 F .208
-(used\); if set b)174 553.8 R .208(ut null, the user')-.2 F 2.708(sT)-.55 G
-2.708(Zv)306.916 553.8 S .208(ariable is used, and if set and non-null the TZ)
-320.484 553.8 R -.25(va)174 565.8 S(riable is set to this v).25 E(alue.)-.25 E
--.35(Tr)102 582 S 5.96(yNullMXList [w]).35 F .114
-(If this system is the \231best\232 \(that is, lo)2.614 F .114
-(west preference\) MX for a gi)-.25 F -.15(ve)-.25 G 2.613(nh).15 G .113
-(ost, its)477.767 582 R 1.168(con\214guration rules should normally detect thi\
-s situation and treat that condition)174 594 R .258(specially by forw)174 606 R
-.258(arding the mail to a UUCP feed, treating it as local, or whate)-.1 F -.15
-(ve)-.25 G -.55(r.).15 G(Ho)174 618 Q(we)-.25 E -.15(ve)-.25 G 1.685 -.4(r, i)
-.15 H 3.385(ns).4 G .886(ome cases \(such as Internet \214re)230.54 618 R -.1
-(wa)-.25 G .886(lls\) you may w).1 F .886(ant to try to con-)-.1 F .07
-(nect directly to that host as though it had no MX records at all.)174 630 R
-.07(Setting this option)5.07 F(causes)174 642 Q F2(sendmail)3.013 E F1 .514
-(to try this.)3.013 F .514(The do)5.514 F .514
-(wnside is that errors in your con\214guration are)-.25 F(lik)174 654 Q 2.116
-(ely to be diagnosed as \231host unkno)-.1 F 2.116
-(wn\232 or \231message timed out\232 instead of)-.25 F
-(something more meaningful.)174 666 Q(This option is disrecommended.)5 E
-(UnixFromLine=)102 682.2 Q F2(fr)A(omline)-.45 E F1 .236
-([$l macro] De\214nes the format used when)174 694.2 R F2(sendmail)2.736 E F1
-.236(must add a UNIX-style From_)2.736 F 1.325(line \(that is, a line be)174
-706.2 R 1.325(ginning \231From<space>user\232\).)-.15 F(Def)6.324 E 1.324
-(aults to \231From $g)-.1 F($d\232.)6.324 E(Don')174 718.2 Q 2.645(tc)-.18 G
-.146(hange this unless your system uses a dif)204.235 718.2 R .146
-(ferent UNIX mailbox format \(v)-.25 F(ery)-.15 E EP
-%%Page: 46 41
-%%BeginPageSetup
-BP
-%%EndPageSetup
-/F0 10/Times-Bold@0 SF 193.36(SMM:08-46 Sendmail)72 60 R
-(Installation and Operation Guide)2.5 E/F1 10/Times-Roman@0 SF(unlik)174 96 Q
-(ely\).)-.1 E(UseErrorsT)102 112.2 Q 21.15(o[)-.8 G .826
-(l] If there is an \231Errors-T)177.33 112.2 R .826(o:\232 header)-.8 F 3.326
-(,s)-.4 G .826(end error messages to the addresses listed)332.414 112.2 R 3.134
-(there. The)174 124.2 R 3.134(yn)-.15 G .634(ormally go to the en)230.658 124.2
-R -.15(ve)-.4 G .635(lope sender).15 F 5.635(.U)-.55 G .635
-(se of this option causes)383.895 124.2 R/F2 10/Times-Italic@0 SF(send-)3.135 E
-(mail)174 136.2 Q F1(to violate RFC 1123.)2.5 E
-(This option is disrecommended and deprecated.)5 E(UserDatabaseSpec=)102 152.4
-Q F2(udbspec)A F1([U] The user database speci\214cation.)174 164.4 Q -1.11(Ve)
-102 180.6 S 37.29(rbose [v])1.11 F .561(Run in v)3.061 F .561(erbose mode.)-.15
-F .561(If this is set,)5.561 F F2(sendmail)3.061 E F1 .56(adjusts options)3.061
-F F0(HoldExpensi)3.06 E -.1(ve)-.1 G F1(\(old)174 192.6 Q F0(c)2.635 E F1 2.635
-(\)a)C(nd)207.59 192.6 Q F0(Deli)2.635 E -.1(ve)-.1 G(ryMode).1 E F1(\(old)
-2.635 E F0(d)2.635 E F1 2.635(\)s)C 2.635(ot)317.36 192.6 S .135
-(hat all mail is deli)327.775 192.6 R -.15(ve)-.25 G .136
-(red completely in a sin-).15 F 1.244
-(gle job so that you can see the entire deli)174 204.6 R -.15(ve)-.25 G 1.244
-(ry process.).15 F(Option)6.244 E F0 -1(Ve)3.743 G(rbose)1 E F1(should)3.743 E
-F2(ne)174 216.6 Q(ver)-.15 E F1(be set in the con\214guration \214le; it is in\
-tended for command line use only)2.5 E(.)-.65 E .108(All options can be speci\
-\214ed on the command line using the \255O or \255o \215ag, b)102 232.8 R .109
-(ut most will cause)-.2 F F2(send-)2.609 E(mail)102 244.8 Q F1 1.135
-(to relinquish its setuid permissions.)3.635 F 1.135
-(The options that will not cause this are MinFreeBlocks)6.135 F .513([b], Deli)
-102 256.8 R -.15(ve)-.25 G .513
-(ryMode [d], ErrorMode [e], IgnoreDots [i], LogLe).15 F -.15(ve)-.25 G 3.014
-(l[).15 G .514(L], MeT)369.118 256.8 R .514(oo [m], OldStyleHeaders)-.8 F .53
-([o], Pri)102 268.8 R -.25(va)-.25 G -.15(cy).25 G .53(Options [p], T).15 F .53
-(imeouts [r], SuperSafe [s], V)-.35 F .53(erbose [v], CheckpointInterv)-1.11 F
-.53(al [C], and Se)-.25 F(v-)-.25 E(enBitInput [7].)102 280.8 Q(Also, M \(de\
-\214ne macro\) when de\214ning the r or s macros is also considered \231safe\
-\232.)5 E F0 2.5(5.7. P)87 304.8 R 2.5<8a50>2.5 G -.18(re)134.22 304.8 S
-(cedence De\214nitions).18 E F1 -1.11(Va)127 321 S .164
-(lues for the \231Precedence:\232 \214eld may be de\214ned using the)1.11 F F0
-(P)2.664 E F1 .164(control line.)2.664 F .164(The syntax of this)5.164 F
-(\214eld is:)102 333 Q F0(P)142 349.2 Q F2(name)A F0(=)A F2(num)A F1 .384
-(When the)102 365.4 R F2(name)2.884 E F1 .384
-(is found in a \231Precedence:\232 \214eld, the message class is set to)2.884 F
-F2(num)2.883 E F1 5.383(.H)C .383(igher numbers)446.127 365.4 R .85
-(mean higher precedence.)102 377.4 R .85(Numbers less than zero ha)5.85 F 1.15
--.15(ve t)-.2 H .85(he special property that if an error occurs).15 F 1.551
-(during processing the body of the message will not be returned; this is e)102
-389.4 R 1.551(xpected to be used for)-.15 F<9962>102 401.4 Q .461
-(ulk\232 mail such as through mailing lists.)-.2 F .461(The def)5.461 F .461
-(ault precedence is zero.)-.1 F -.15(Fo)5.461 G 2.962(re).15 G .462
-(xample, our list of)429.284 401.4 R(precedences is:)102 413.4 Q
-(P\214rst-class=0)142 429.6 Q(Pspecial-deli)142 441.6 Q -.15(ve)-.25 G(ry=100)
-.15 E(Plist=\25530)142 453.6 Q(Pb)142 465.6 Q(ulk=\25560)-.2 E(Pjunk=\255100)
-142 477.6 Q 1.059(People writing mailing list e)102 493.8 R 1.058
-(xploders are encouraged to use \231Precedence: list\232.)-.15 F 1.058(Older v)
-6.058 F 1.058(ersions of)-.15 F F2(sendmail)102 505.8 Q F1 1.19
-(\(which discarded all error returns for ne)3.69 F -.05(ga)-.15 G(ti).05 E 1.49
--.15(ve p)-.25 H 1.19(recedences\) didn').15 F 3.69(tr)-.18 G 1.19
-(ecognize this name,)422.47 505.8 R(gi)102 517.8 Q .599(ving it a def)-.25 F
-.598(ault precedence of zero.)-.1 F .598(This allo)5.598 F .598
-(ws list maintainers to see error returns on both old)-.25 F(and ne)102 529.8 Q
-2.5(wv)-.25 G(ersions of)142.7 529.8 Q F2(sendmail)2.5 E F1(.)A F0 2.5(5.8. V)
-87 553.8 R 2.5<8a43>2.5 G(on\214guration V)136.44 553.8 Q(ersion Le)-1 E -.1
-(ve)-.15 G(l).1 E F1 3.181 -.8(To p)127 570 T(ro).8 E 1.581
-(vide compatibility with old con\214guration \214les, the)-.15 F F0(V)4.081 E
-F1 1.582(line has been added to de\214ne)4.082 F 1.11(some v)102 582 R 1.11
-(ery basic semantics of the con\214guration \214le.)-.15 F 1.11
-(These are not intended to be long term sup-)6.11 F .033(ports; rather)102 594
-R 2.533(,t)-.4 G(he)158.046 594 Q 2.533(yd)-.15 G .033
-(escribe compatibility features which will probably be remo)179.869 594 R -.15
-(ve)-.15 G 2.533(di).15 G 2.533(nf)435.903 594 S .034(uture releases.)446.766
-594 R F0(N.B.:)127 610.2 Q F1 .197(these v)2.697 F(ersion)-.15 E F2(le)2.697 E
-(vels)-.15 E F1(ha)2.697 E .496 -.15(ve n)-.2 H .196(othing to do with the v)
-.15 F(ersion)-.15 E F2(number)2.696 E F1 .196(on the \214les.)2.696 F -.15(Fo)
-5.196 G 2.696(re).15 G(xam-)483.45 610.2 Q(ple, as of this writing v)102 622.2
-Q(ersion 8 con\214g \214les \(speci\214cally)-.15 E 2.5(,8)-.65 G(.7\) used v)
-333.41 622.2 Q(ersion le)-.15 E -.15(ve)-.25 G 2.5(l6c).15 G(on\214gurations.)
-432.84 622.2 Q 1.102(\231Old\232 con\214guration \214les are de\214ned as v)127
-638.4 R 1.102(ersion le)-.15 F -.15(ve)-.25 G 3.602(lo).15 G 3.602(ne. V)
-353.006 638.4 R 1.102(ersion le)-1.11 F -.15(ve)-.25 G 3.602(lt).15 G 1.302 -.1
-(wo \214)430.622 638.4 T 1.103(les mak).1 F 3.603(et)-.1 G(he)494.56 638.4 Q
-(follo)102 650.4 Q(wing changes:)-.25 E 12.5(\(1\) Host)107 666.6 R .727(name \
-canoni\214cation \($[ ... $]\) appends a dot if the name is recognized; this g\
-i)3.227 F -.15(ve)-.25 G 3.226(st).15 G(he)494.56 666.6 Q 1.974
-(con\214g \214le a w)133.66 678.6 R 1.974(ay of \214nding out if an)-.1 F 1.974
-(ything matched.)-.15 F(\(Actually)6.974 E 4.475(,t)-.65 G 1.975
-(his just initializes the)413.345 678.6 R .739
-(\231host\232 map with the \231\255a.)133.66 690.6 R 5.739<9a8d>-.7 G .739
-(ag \212 you can reset it to an)251.445 690.6 R .738
-(ything you prefer by declaring the)-.15 F(map e)133.66 702.6 Q(xplicitly)-.15
-E(.\))-.65 E EP
-%%Page: 47 42
-%%BeginPageSetup
-BP
-%%EndPageSetup
-/F0 10/Times-Bold@0 SF(Sendmail Installation and Operation Guide)72 60 Q
-(SMM:08-47)452.9 60 Q/F1 10/Times-Roman@0 SF 12.5(\(2\) Def)107 96 R .384
-(ault host name e)-.1 F .385(xtension is consistent throughout processing; v)
--.15 F .385(ersion le)-.15 F -.15(ve)-.25 G 2.885(lo).15 G .385(ne con\214gu-)
-458.345 96 R .83(rations turned of)133.66 108 R 3.33(fd)-.25 G .83(omain e)
-212.83 108 R .83
-(xtension \(that is, adding the local domain name\) during certain)-.15 F .4
-(points in processing.)133.66 120 R -1.11(Ve)5.4 G .4(rsion le)1.11 F -.15(ve)
--.25 G 2.9(lt).15 G .6 -.1(wo c)280.53 120 T .4(on\214gurations are e).1 F .4
-(xpected to include a trailing dot)-.15 F
-(to indicate that the name is already canonical.)133.66 132 Q 12.5(\(3\) Local)
-107 148.2 R .072(names that are not aliases are passed through a ne)2.572 F
-2.572(wd)-.25 G .072(istinguished ruleset \214v)372.752 148.2 R .072
-(e; this can)-.15 F 1.426(be used to append a local relay)133.66 160.2 R 6.426
-(.T)-.65 G 1.426(his beha)279.902 160.2 R 1.426(viour can be pre)-.2 F -.15(ve)
--.25 G 1.426(nted by resolving the local).15 F .209(name with an initial `@'.)
-133.66 172.2 R .209(That is, something that resolv)5.209 F .209
-(es to a local mailer and a user name)-.15 F 1.072
-(of \231vikki\232 will be passed through ruleset \214v)133.66 184.2 R 1.072
-(e, b)-.15 F 1.073(ut a user name of \231@vikki\232 will ha)-.2 F 1.373 -.15
-(ve t)-.2 H(he).15 E .417
-(`@' stripped, will not be passed through ruleset \214v)133.66 196.2 R .417
-(e, b)-.15 F .416(ut will otherwise be treated the same)-.2 F 1.702
-(as the prior e)133.66 208.2 R 4.202(xample. The)-.15 F -.15(ex)4.202 G 1.703
-(pectation is that this might be used to implement a polic).15 F(y)-.15 E .136
-(where mail sent to \231vikki\232 w)133.66 220.2 R .135
-(as handled by a central hub, b)-.1 F .135
-(ut mail sent to \231vikki@localhost\232)-.2 F -.1(wa)133.66 232.2 S 2.5(sd).1
-G(eli)156.61 232.2 Q -.15(ve)-.25 G(red directly).15 E(.)-.65 E -1.11(Ve)127
-248.4 S 1.382(rsion le)1.11 F -.15(ve)-.25 G 3.882(lt).15 G 1.382
-(hree \214les allo)187.134 248.4 R 3.882(w#i)-.25 G 1.382
-(nitiated comments on all lines.)266.292 248.4 R 1.383
-(Exceptions are backslash)6.383 F(escaped # marks and the $# syntax.)102 260.4
-Q -1.11(Ve)127 276.6 S 1.208(rsion le)1.11 F -.15(ve)-.25 G 3.708(lf).15 G
-1.208(our con\214gurations are completely equi)187.336 276.6 R -.25(va)-.25 G
-1.207(lent to le).25 F -.15(ve)-.25 G 3.707(lt).15 G 1.207
-(hree for historical rea-)411.249 276.6 R(sons.)102 288.6 Q -1.11(Ve)127 304.8
-S 1.234(rsion le)1.11 F -.15(ve)-.25 G 3.734<6c8c>.15 G 1.534 -.15(ve c)189.618
-304.8 T 1.234(on\214guration \214les change the def).15 F 1.234
-(ault de\214nition of)-.1 F F0($w)3.734 E F1 1.234(to be just the \214rst)3.734
-F(component of the hostname.)102 316.8 Q -1.11(Ve)127 333 S 1.589(rsion le)1.11
-F -.15(ve)-.25 G 4.089(ls).15 G 1.589(ix con\214guration \214les change man)
-188.658 333 R 4.088(yo)-.15 G 4.088(ft)342.272 333 S 1.588
-(he local processing options \(such as)352.47 333 R .48
-(aliasing and matching the be)102 345 R .481
-(ginning of the address for `|' characters\) to be mailer \215ags; this allo)
--.15 F(ws)-.25 E 1.345(\214ne-grained control o)102 357 R -.15(ve)-.15 G 3.845
-(rt).15 G 1.345(he special local processing.)210.435 357 R(Le)6.345 E -.15(ve)
--.25 G 3.845(ls).15 G 1.344(ix con\214guration \214les may also use)360.34 357
-R 1.221(long option names.)102 369 R(The)6.221 E F0(ColonOkInAddr)3.721 E F1
-1.221(option \(to allo)3.721 F 3.722(wc)-.25 G 1.222
-(olons in the local-part of addresses\))355.42 369 R(def)102 381 Q(aults)-.1 E
-F0(on)3.44 E F1 .94(for lo)3.44 F .94(wer numbered con\214guration \214les; th\
-e con\214guration \214le requires some additional)-.25 F
-(intelligence to properly handle the RFC 822 group construct.)102 393 Q(The)127
-409.2 Q F0(V)2.677 E F1 .177(line may ha)2.677 F .477 -.15(ve a)-.2 H 2.677(no)
-.15 G(ptional)231.022 409.2 Q F0(/)2.677 E/F2 10/Times-Italic@0 SF(vendor)A F1
-.178(to indicate that this con\214guration \214le uses modi\214ca-)2.677 F .865
-(tions speci\214c to a particular v)102 423.2 R(endor)-.15 E/F3 7/Times-Roman@0
-SF(19)246.986 419.2 Q F1 5.866(.Y)253.986 423.2 S .866(ou may use \231/Berk)
-268.472 423.2 R(ele)-.1 E .866(y\232 to emphasize that this con\214gura-)-.15 F
-(tion \214le uses the Berk)102 435.2 Q(ele)-.1 E 2.5(yd)-.15 G(ialect of)213.13
-435.2 Q F2(sendmail)2.5 E F1(.)A F0 2.5(5.9. K)87 459.2 R 2.5<8a4b>2.5 G
-(ey File Declaration)137.31 459.2 Q F1
-(Special maps can be de\214ned using the line:)127 475.4 Q
-(Kmapname mapclass ar)142 491.6 Q(guments)-.18 E(The)102 507.8 Q F2(mapname)
-2.751 E F1 .251(is the handle by which this map is referenced in the re)2.751 F
-.25(writing rules.)-.25 F(The)5.25 E F2(mapclass)2.75 E F1(is)2.75 E 1.889
-(the name of a type of map; these are compiled in to)102 519.8 R F2(sendmail)
-4.389 E F1 6.889(.T)C(he)384.013 519.8 Q F2(ar)4.389 E(guments)-.37 E F1 1.889
-(are interpreted)4.389 F .791(depending on the class; typically)102 531.8 R
-3.291(,t)-.65 G .791(here w)244.185 531.8 R .791(ould be a single ar)-.1 F .79
-(gument naming the \214le containing the)-.18 F(map.)102 543.8 Q
-(Maps are referenced using the syntax:)127 560 Q($\()142 576.2 Q F2(map k)2.5 E
--.3(ey)-.1 G F1($@)2.8 E F2(ar)2.5 E(guments)-.37 E F1($:)2.5 E F2(default)2.5
-E F1($\))2.5 E .64(where either or both of the)102 592.4 R F2(ar)3.14 E
-(guments)-.37 E F1(or)3.141 E F2(default)3.141 E F1 .641
-(portion may be omitted.)3.141 F(The)5.641 E F2 .641($@ ar)3.141 F(guments)-.37
-E F1(may)3.141 E 1.277(appear more than once.)102 604.4 R 1.277(The indicated)
-6.277 F F2 -.1(ke)3.777 G(y)-.2 E F1(and)3.776 E F2(ar)3.776 E(guments)-.37 E
-F1 1.276(are passed to the appropriate mapping)3.776 F 3.253(function. If)102
-616.4 R .753(it returns a v)3.253 F .753(alue, it replaces the input.)-.25 F
-.753(If it does not return a v)5.753 F .753(alue and the)-.25 F F2(default)
-3.253 E F1(is)3.253 E(speci\214ed, the)102 628.4 Q F2(default)2.5 E F1
-(replaces the input.)2.5 E(Otherwise, the input is unchanged.)5 E 1.042
-(During replacement of either a map v)127 644.6 R 1.042(alue or def)-.25 F
-1.042(ault the string \231%)-.1 F F2(n)A F1 3.542<9a28>C(where)417.414 644.6 Q
-F2(n)3.542 E F1 1.041(is a digit\) is)3.541 F .481
-(replaced by the corresponding)102 656.6 R F2(ar)2.981 E(gument)-.37 E F1 5.481
-(.A)C -.18(rg)280.385 656.6 S .482(ument zero is al).18 F -.1(wa)-.1 G .482
-(ys the database k).1 F -.15(ey)-.1 G 5.482(.F)-.5 G .482(or e)456.458 656.6 R
-(xample,)-.15 E(the rule)102 668.6 Q .32 LW 76 678.2 72 678.2 DL 80 678.2 76
-678.2 DL 84 678.2 80 678.2 DL 88 678.2 84 678.2 DL 92 678.2 88 678.2 DL 96
-678.2 92 678.2 DL 100 678.2 96 678.2 DL 104 678.2 100 678.2 DL 108 678.2 104
-678.2 DL 112 678.2 108 678.2 DL 116 678.2 112 678.2 DL 120 678.2 116 678.2 DL
-124 678.2 120 678.2 DL 128 678.2 124 678.2 DL 132 678.2 128 678.2 DL 136 678.2
-132 678.2 DL 140 678.2 136 678.2 DL 144 678.2 140 678.2 DL 148 678.2 144 678.2
-DL 152 678.2 148 678.2 DL 156 678.2 152 678.2 DL 160 678.2 156 678.2 DL 164
-678.2 160 678.2 DL 168 678.2 164 678.2 DL 172 678.2 168 678.2 DL 176 678.2 172
-678.2 DL 180 678.2 176 678.2 DL 184 678.2 180 678.2 DL 188 678.2 184 678.2 DL
-192 678.2 188 678.2 DL 196 678.2 192 678.2 DL 200 678.2 196 678.2 DL 204 678.2
-200 678.2 DL 208 678.2 204 678.2 DL 212 678.2 208 678.2 DL 216 678.2 212 678.2
-DL/F4 5/Times-Roman@0 SF(19)93.6 688.6 Q/F5 8/Times-Roman@0 SF .214
-(And of course, v)3.2 J .214(endors are encouraged to add themselv)-.12 F .214
-(es to the list of recognized v)-.12 F .214(endors by editing the routine)-.12
-F/F6 8/Times-Italic@0 SF(setvendor)2.214 E F5(in)2.214 E F6(conf)72 701.4 Q(.c)
--.12 E F5 4(.P)C(lease send e-mail to sendmail@CS.Berk)101.656 701.4 Q(ele)-.08
-E -.52(y.)-.12 G(EDU to re).52 E(gister your v)-.12 E(endor dialect.)-.12 E EP
-%%Page: 48 43
-%%BeginPageSetup
-BP
-%%EndPageSetup
-/F0 10/Times-Bold@0 SF 193.36(SMM:08-48 Sendmail)72 60 R
-(Installation and Operation Guide)2.5 E/F1 10/Times-Roman@0 SF(R$\255 ! $+)142
-96 Q($: $\(uucp $1 $@ $2 $: %1 @ %0 . UUCP $\))250 96 Q 1.269(Looks up the UUC\
-P name in a \(user de\214ned\) UUCP map; if not found it turns it into \231.UU\
-CP\232)102 112.2 R 2.5(form. The)102 124.2 R
-(database might contain records lik)2.5 E(e:)-.1 E(decv)142 140.4 Q 77.43
-(ax %1@%0.DEC.COM)-.25 F 72.19(research %1@%0.A)142 152.4 R(TT)-1.11 E(.COM)
--.74 E .741(The b)127 172.8 R .741(uilt in map with both name and class \231ho\
-st\232 is the host name canonicalization lookup.)-.2 F(Thus, the syntax:)102
-184.8 Q($\(host)142 201 Q/F2 10/Times-Italic@0 SF(hostname)2.5 E F1($\))A
-(is equi)102 217.2 Q -.25(va)-.25 G(lent to:).25 E($[)142 233.4 Q F2(hostname)A
-F1($])A(There are man)127 253.8 Q 2.5(yd)-.15 G(e\214ned classes.)197.1 253.8 Q
-51.72(dbm Database)102 270 R 1.623(lookups using the ndbm\(3\) library)4.123 F
-(.)-.65 E F2(Sendmail)6.623 E F1 1.623(must be compiled with)4.123 F F0(NDBM)
-174 282 Q F1(de\214ned.)2.5 E 49.51(btree Database)102 298.2 R 1.284
-(lookups using the btree interf)3.784 F 1.285(ace to the Berk)-.1 F(ele)-.1 E
-3.785(yd)-.15 G 1.285(b\(3\) library)425.99 298.2 R(.)-.65 E F2(Send-)6.285 E
-(mail)174 310.2 Q F1(must be compiled with)2.5 E F0(NEWDB)2.5 E F1(de\214ned.)
-2.5 E 51.17(hash Database)102 326.4 R .122(lookups using the hash interf)2.622
-F .122(ace to the Berk)-.1 F(ele)-.1 E 2.622(yd)-.15 G .121(b\(3\) library)
-413.868 326.4 R(.)-.65 E F2(Sendmail)5.121 E F1(must be compiled with)174 338.4
-Q F0(NEWDB)2.5 E F1(de\214ned.)2.5 E 57.83(nis NIS)102 354.6 R(lookups.)2.5 E
-F2(Sendmail)5 E F1(must be compiled with)2.5 E F0(NIS)2.5 E F1(de\214ned.)2.5 E
-41.16(nisplus NIS+)102 370.8 R(lookups.)3.733 E F2(Sendmail)6.233 E F1 1.233
-(must be compiled with)3.733 F F0(NISPLUS)3.733 E F1 3.733(de\214ned. The)3.733
-F(ar)3.733 E(gu-)-.18 E .495
-(ment is the name of the table to use for lookups, and the)174 382.8 R F0<ad6b>
-2.995 E F1(and)2.995 E F0<ad76>2.995 E F1 .495(\215ags may be)2.995 F
-(used to set the k)174 394.8 Q .3 -.15(ey a)-.1 H(nd v).15 E
-(alue columns respecti)-.25 E -.15(ve)-.25 G(ly).15 E(.)-.65 E 43.39
-(hesiod Hesiod)102 411 R(lookups.)2.5 E F2(Sendmail)5 E F1
-(must be compiled with)2.5 E F0(HESIOD)2.5 E F1(de\214ned.)2.5 E 41.17
-(netinfo NeXT)102 427.2 R(NetInfo lookups.)2.5 E F2(Sendmail)5 E F1
-(must be compiled with)2.5 E F0(NETINFO)2.5 E F1(de\214ned.)2.5 E(te)102 443.4
-Q 54.65(xt T)-.15 F -.15(ex)-.7 G 2.917<748c>.15 G .417(le lookups.)199.957
-443.4 R .417(The format of the te)5.417 F .418(xt \214le is de\214ned by the)
--.15 F F0<ad6b>2.918 E F1(\(k)2.918 E .718 -.15(ey \214)-.1 H .418(eld num-).15
-F(ber\),)174 455.4 Q F0<ad76>2.5 E F1(\(v)2.5 E(alue \214eld number\), and)-.25
-E F0<ad7a>2.5 E F1(\(\214eld delimiter\) \215ags.)2.5 E 53.39(stab Internal)102
-471.6 R(symbol table lookups.)2.5 E(Used internally for aliasing.)5 E 38.38
-(implicit Really)102 487.8 R .546
-(should be called \231alias\232 \212 this is used to get the def)3.046 F .546
-(ault lookups for alias)-.1 F(\214les, and is the def)174 499.8 Q
-(ault if no class is speci\214ed for alias \214les.)-.1 E 52.84(user Looks)102
-516 R .476(up users using)2.976 F F2 -.1(ge)2.976 G(tpwnam).1 E F1 2.976
-(\(3\). The)B F0<ad76>2.976 E F1 .477(\215ag can be used to specify the name)
-2.976 F .142
-(of the \214eld to return \(although this is normally used only to check the e)
-174 528 R .142(xistence of)-.15 F 2.5(au)174 540 S(ser\).)185.94 540 Q 52.83
-(host Canoni\214es)102 556.2 R .2(host domain names.)2.7 F(Gi)5.2 E -.15(ve)
--.25 G 2.7(nah).15 G .2(ost name it calls the name serv)343.68 556.2 R .2
-(er to \214nd)-.15 F(the canonical name for that host.)174 568.2 Q 32.85
-(sequence The)102 584.4 R(ar)3.35 E .849
-(guments on the `K' line are a list of maps; the resulting map searches the)
--.18 F(ar)174 596.4 Q .438
-(gument maps in order until it \214nds a match for the indicated k)-.18 F -.15
-(ey)-.1 G 5.439(.F)-.5 G .439(or e)456.501 596.4 R(xample,)-.15 E(if the k)174
-608.4 Q .3 -.15(ey d)-.1 H(e\214nition is:).15 E(Kmap1 ...)214 624.6 Q
-(Kmap2 ...)214 636.6 Q(Kseqmap sequence map1 map2)214 648.6 Q .968
-(then a lookup ag)174 664.8 R .968
-(ainst \231seqmap\232 \214rst does a lookup in map1.)-.05 F .968
-(If that is found, it)5.968 F(returns immediately)174 676.8 Q 5(.O)-.65 G
-(therwise, the same k)268.34 676.8 Q .3 -.15(ey i)-.1 H 2.5(su).15 G
-(sed for map2.)375.85 676.8 Q 43.39(switch Much)102 693 R(lik)2.8 E 2.8(et)-.1
-G .3(he \231sequence\232 map e)220.61 693 R .301
-(xcept that the order of maps is determined by the)-.15 F .392(service switch.)
-174 705 R .392(The ar)5.392 F .391
-(gument is the name of the service to be look)-.18 F .391(ed up; the v)-.1 F
-(al-)-.25 E 1.492
-(ues from the service switch are appended to the map name to create ne)174 717
-R 3.993(wm)-.25 G(ap)494.56 717 Q EP
-%%Page: 49 44
-%%BeginPageSetup
-BP
-%%EndPageSetup
-/F0 10/Times-Bold@0 SF(Sendmail Installation and Operation Guide)72 60 Q
-(SMM:08-49)452.9 60 Q/F1 10/Times-Roman@0 SF 2.5(names. F)174 96 R(or e)-.15 E
-(xample, consider the k)-.15 E .3 -.15(ey d)-.1 H(e\214nition:).15 E
-(Kali switch aliases)214 112.2 Q(together with the service switch entry:)174
-128.4 Q 78.84(aliases nis)214 144.6 R(\214les)2.5 E 1.633
-(This causes a query ag)174 160.8 R 1.633
-(ainst the map \231ali\232 to search maps named \231ali.nis\232 and)-.05 F
-(\231ali.\214les\232 in that order)174 172.8 Q(.)-.55 E 37.84(dequote Strip)102
-189 R .96(double quotes \("\) from a name.)3.46 F .961
-(It does not strip backslashes, and will not)5.961 F .173
-(strip quotes if the resulting string w)174 201 R .172
-(ould contain unscannable syntax \(that is, basic)-.1 F .386(errors lik)174 213
-R 2.886(eu)-.1 G .386(nbalanced angle brack)222.992 213 R .386
-(ets; more sophisticated errors such as unkno)-.1 F(wn)-.25 E .252
-(hosts are not check)174 225 R 2.752(ed\). The)-.1 F .251
-(intent is for use when trying to accept mail from sys-)2.752 F
-(tems such as DECnet that routinely quote odd syntax such as)174 237 Q
-("49ers::ubell")214 253.2 Q 2.5(At)174 269.4 S
-(ypical usage is probably something lik)186.5 269.4 Q(e:)-.1 E
-(Kdequote dequote)214 285.6 Q(...)214 309.6 Q 88.19(R$\255 $:)214 333.6 R
-($\(dequote $1 $\))2.5 E(R$\255 $+)214 345.6 Q($: $>3 $1 $2)322 345.6 Q
-(Care must be tak)174 361.8 Q(en to pre)-.1 E -.15(ve)-.25 G(nt une).15 E
-(xpected results; for e)-.15 E(xample,)-.15 E("|someprogram < input > output")
-214 378 Q 1.31(will ha)174 394.2 R 1.61 -.15(ve q)-.2 H 1.31(uotes stripped, b)
-.15 F 1.31(ut the result is probably not what you had in mind.)-.2 F -.15(Fo)
-174 406.2 S(rtunately these cases are rare.).15 E .488
-(Most of these accept as ar)127 422.4 R .488
-(guments the same optional \215ags and a \214lename \(or a mapname for)-.18 F
-.31(NIS; the \214lename is the root of the database path, so that \231.db\232 \
-or some other e)102 434.4 R .31(xtension appropriate)-.15 F
-(for the database type will be added to get the actual database name\).)102
-446.4 Q(Kno)5 E(wn \215ags are:)-.25 E 58.86(\255o Indicates)102 462.6 R 1.147
-(that this map is optional \212 that is, if it cannot be opened, no error is)
-3.648 F(produced, and)174 474.6 Q/F2 10/Times-Italic@0 SF(sendmail)2.5 E F1
-(will beha)2.5 E .3 -.15(ve a)-.2 H 2.5(si).15 G 2.5(ft)333.9 474.6 S(he map e)
-342.51 474.6 Q(xisted b)-.15 E(ut w)-.2 E(as empty)-.1 E(.)-.65 E .647
-(\255N, \255O)102 490.8 R .647(If neither)174.647 490.8 R F0<ad4e>3.147 E F1
-(or)3.147 E F0<ad4f>3.147 E F1 .647(are speci\214ed,)3.147 F F2(sendmail)3.147
-E F1 .647(uses an adapti)3.147 F .947 -.15(ve a)-.25 H .648(lgorithm to decide)
-.15 F .108(whether or not to look for null bytes on the end of k)174 502.8 R
--.15(ey)-.1 G 2.608(s. It).15 F .107(starts by trying both; if)2.608 F .819
-(it \214nds an)174 514.8 R 3.319(yk)-.15 G 1.119 -.15(ey w)228.157 514.8 T .819
-(ith a null byte it ne).15 F -.15(ve)-.25 G 3.319(rt).15 G .82(ries ag)345.83
-514.8 R .82(ain without a null byte and vice)-.05 F -.15(ve)174 526.8 S 2.828
-(rsa. If).15 F F0<ad4e>2.828 E F1 .328(is speci\214ed it ne)2.828 F -.15(ve)
--.25 G 2.828(rt).15 G .328(ries without a null byte and if)311.696 526.8 R F0
-<ad4f>2.827 E F1 .327(is speci\214ed it)2.827 F(ne)174 538.8 Q -.15(ve)-.25 G
-2.886(rt).15 G .386(ries with a null byte.)201.476 538.8 R .386
-(Setting one of these can speed matches b)5.386 F .386(ut are ne)-.2 F -.15(ve)
--.25 G(r).15 E(necessary)174 550.8 Q 5.546(.I)-.65 G 3.046(fb)223.596 550.8 S
-(oth)234.972 550.8 Q F0<ad4e>3.046 E F1(and)3.046 E F0<ad4f>3.046 E F1 .545
-(are speci\214ed,)3.045 F F2(sendmail)3.045 E F1 .545(will ne)3.045 F -.15(ve)
--.25 G 3.045(rt).15 G .545(ry an)442.52 550.8 R 3.045(ym)-.15 G(atches)479.01
-550.8 Q(at all \212 that is, e)174 562.8 Q -.15(ve)-.25 G
-(rything will appear to f).15 E(ail.)-.1 E<ad61>102 579 Q F2(x)A F1 1.356
-(Append the string)174 579 R F2(x)3.856 E F1 1.357(on successful matches.)3.856
-F -.15(Fo)6.357 G 3.857(re).15 G 1.357(xample, the def)382.852 579 R(ault)-.1 E
-F2(host)3.857 E F1(map)3.857 E(appends a dot on successful matches.)174 591 Q
-60.53(\255f Do)102 607.2 R(not fold upper to lo)2.5 E
-(wer case before looking up the k)-.25 E -.15(ey)-.1 G(.)-.5 E 56.08
-(\255m Match)102 623.4 R .4(only \(without replacing the v)2.9 F 2.899
-(alue\). If)-.25 F .399(you only care about the e)2.899 F .399(xistence of)-.15
-F 7.306(ak)174 635.4 S 5.107 -.15(ey a)190.646 635.4 T 4.807(nd not the v).15 F
-4.807(alue \(as you might when searching the NIS map)-.25 F 1.947
-(\231hosts.byname\232 for e)174 647.4 R 1.947(xample\), this \215ag pre)-.15 F
--.15(ve)-.25 G 1.947(nts the map from substituting the).15 F -.25(va)174 659.4
-S 2.849(lue. Ho).25 F(we)-.25 E -.15(ve)-.25 G 1.149 -.4(r, T).15 H .349
-(he \255a ar).4 F .349(gument is still appended on a match, and the def)-.18 F
-.35(ault is)-.1 F(still tak)174 671.4 Q(en if the match f)-.1 E(ails.)-.1 E
-<ad6b>102 687.6 Q F2 -.1(ke)C(ycol)-.2 E F1(The k)174 687.6 Q .3 -.15(ey c)-.1
-H(olumn name \(for NIS+\) or number \(for te).15 E(xt lookups\).)-.15 E<ad76>
-102 703.8 Q F2(valcol)A F1(The v)174 703.8 Q
-(alue column name \(for NIS+\) or number \(for te)-.25 E(xt lookups\).)-.15 E
-EP
-%%Page: 50 45
-%%BeginPageSetup
-BP
-%%EndPageSetup
-/F0 10/Times-Bold@0 SF 193.36(SMM:08-50 Sendmail)72 60 R
-(Installation and Operation Guide)2.5 E/F1 10/Times-Roman@0 SF<ad7a>102 96 Q/F2
-10/Times-Italic@0 SF(delim)A F1 .219(The column delimiter \(for te)174 96 R
-.219(xt lookups\).)-.15 F .218(It can be a single character or one of the)5.219
-F 1.825(special strings \231)174 108 R 1.825(\\n\232 or \231)1.666 F 1.826
-(\\t\232 to indicate ne)1.666 F 1.826(wline or tab respecti)-.25 F -.15(ve)-.25
-G(ly).15 E 6.826(.I)-.65 G 4.326(fo)465.784 108 S(mitted)478.44 108 Q(entirely)
-174 120 Q 2.5(,t)-.65 G(he column separator is an)211.68 120 Q 2.5(ys)-.15 G
-(equence of whitespace.)325.12 120 Q<ad73>102 136.2 Q F2(spacesub)A F1 -.15(Fo)
-174 136.2 S 3.101(rt).15 G .601(he dequote map only)193.621 136.2 R 3.101(,t)
--.65 G .601(he character to use to replace space characters after a)286.755
-136.2 R(successful dequote.)174 148.2 Q(The)127 164.4 Q F2(dbm)3.356 E F1 .856
-(map appends the strings \231.pag\232 and \231.dir\232 to the gi)3.356 F -.15
-(ve)-.25 G 3.356<6e8c>.15 G .856(lename; the tw)399.052 164.4 R(o)-.1 E F2(db)
-3.356 E F1(-based)A(maps append \231.db\232.)102 176.4 Q -.15(Fo)5 G 2.5(re).15
-G(xample, the map speci\214cation)206.4 176.4 Q -.15(Ku)142 192.6 S
-(ucp dbm \255o \255N /usr/lib/uucpmap).15 E .21
-(speci\214es an optional map named \231uucp\232 of class \231dbm\232; it al)102
-208.8 R -.1(wa)-.1 G .21(ys has null bytes at the end of e).1 F -.15(ve)-.25 G
-(ry).15 E(string, and the data is located in /usr/lib/uucpmap.{dir)102 220.8 Q
-(,pag}.)-.4 E 1.094(The program)127 237 R F2(mak)3.594 E(emap)-.1 E F1 1.094
-(\(8\) can be used to b)B 1.094(uild an)-.2 F 3.594(yo)-.15 G 3.594(ft)347.736
-237 S 1.095(he three database-oriented maps.)357.44 237 R(It)6.095 E(tak)102
-249 Q(es the follo)-.1 E(wing \215ags:)-.25 E 60.53(\255f Do)102 265.2 R
-(not fold upper to lo)2.5 E(wer case in the map.)-.25 E 56.64(\255N Include)102
-281.4 R(null bytes in k)2.5 E -.15(ey)-.1 G(s.).15 E 58.86(\255o Append)102
-297.6 R(to an e)2.5 E(xisting \(old\) \214le.)-.15 E 60.53(\255r Allo)102 313.8
-R 3.669(wr)-.25 G 1.169(eplacement of e)205.749 313.8 R 1.168(xisting k)-.15 F
--.15(ey)-.1 G 1.168(s; normally).15 F 3.668(,r)-.65 G 1.168(e-inserting an e)
-371.63 313.8 R 1.168(xisting k)-.15 F 1.468 -.15(ey i)-.1 H 3.668(sa).15 G(n)
-499 313.8 Q(error)174 325.8 Q(.)-.55 E 58.86(\255v Print)102 342 R
-(what is happening.)2.5 E(The)102 358.2 Q F2(sendmail)3.605 E F1 1.105
-(daemon does not ha)3.605 F 1.405 -.15(ve t)-.2 H 3.605(ob).15 G 3.605(er)
-272.975 358.2 S 1.106(estarted to read the ne)284.35 358.2 R 3.606(wm)-.25 G
-1.106(aps as long as you change)394.88 358.2 R
-(them in place; \214le locking is used so that the maps w)102 372.2 Q(on')-.1 E
-2.5(tb)-.18 G 2.5(er)336.71 372.2 S(ead while the)346.98 372.2 Q 2.5(ya)-.15 G
-(re being updated.)412.09 372.2 Q/F3 7/Times-Roman@0 SF(20)481.24 368.2 Q F1
-(Ne)127 388.4 Q 2.5(wc)-.25 G(lasses can be added in the routine)152.57 388.4 Q
-F0(setupmaps)2.5 E F1(in \214le)2.5 E F0(conf)2.5 E(.c)-.15 E F1(.)A F0 2.5
-(5.10. The)87 412.4 R(User Database)2.5 E F1 .108(If you ha)127 428.6 R .408
--.15(ve a ve)-.2 H .109(rsion of).15 F F2(sendmail)2.609 E F1 .109
-(with the user database package compiled in, the handling of)2.609 F
-(sender and recipient addresses is modi\214ed.)102 440.6 Q
-(The location of this database is controlled with the)127 456.8 Q F0
-(UserDatabaseSpec)2.5 E F1(option.)2.5 E F0 2.5(5.10.1. Structur)102 480.8 R
-2.5(eo)-.18 G 2.5(ft)182.92 480.8 S(he user database)192.08 480.8 Q F1
-(The database is a sorted \(BT)142 497 Q(ree-based\) structure.)-.35 E
-(User records are stored with the k)5 E -.15(ey)-.1 G(:).15 E F2(user)157 513.2
-Q(-name)-.2 E F0(:)A F2(\214eld-name)A F1 .128
-(The sorted database format ensures that user records are clustered together)
-117 529.4 R 5.128(.M)-.55 G .128(eta-information is)432.492 529.4 R(al)117
-541.4 Q -.1(wa)-.1 G(ys stored with a leading colon.).1 E
-(Field names de\214ne both the syntax and semantics of the v)142 557.6 Q 2.5
-(alue. De\214ned)-.25 F(\214elds include:)2.5 E 33.39(maildrop The)117 573.8 R
-(deli)4.872 E -.15(ve)-.25 G 2.372(ry address for this user).15 F 7.372(.T)-.55
-G 2.373(here may be multiple v)349.472 573.8 R 2.373(alues of this)-.25 F 2.675
-(record. In)189 585.8 R(particular)2.675 E 2.675(,m)-.4 G .175
-(ailing lists will ha)284.095 585.8 R .475 -.15(ve o)-.2 H(ne).15 E F2(maildr)
-2.675 E(op)-.45 E F1 .175(record for each user)2.675 F(on the list.)189 597.8 Q
-30.06(mailname The)117 614 R 1.026(outgoing mailname for this user)3.526 F
-6.026(.F)-.55 G 1.027(or each outgoing name, there should)353.336 614 R .08
-(be an appropriate)189 626 R F2(maildr)2.58 E(op)-.45 E F1 .08
-(record for that name to allo)2.58 F 2.58(wr)-.25 G .08(eturn mail.)422.38 626
-R .08(See also)5.08 F F2(:default:mailname)189 638 Q F1(.)A 25.62
-(mailsender Changes)117 654.2 R(an)3.447 E 3.447(ym)-.15 G .947
-(ail sent to this address to ha)252.404 654.2 R 1.248 -.15(ve t)-.2 H .948
-(he indicated en).15 F -.15(ve)-.4 G .948(lope sender).15 F(.)-.55 E 2.736
-(This is intended for mailing lists, and will normally be the name of an)189
-666.2 R .32 LW 76 675.8 72 675.8 DL 80 675.8 76 675.8 DL 84 675.8 80 675.8 DL
-88 675.8 84 675.8 DL 92 675.8 88 675.8 DL 96 675.8 92 675.8 DL 100 675.8 96
-675.8 DL 104 675.8 100 675.8 DL 108 675.8 104 675.8 DL 112 675.8 108 675.8 DL
-116 675.8 112 675.8 DL 120 675.8 116 675.8 DL 124 675.8 120 675.8 DL 128 675.8
-124 675.8 DL 132 675.8 128 675.8 DL 136 675.8 132 675.8 DL 140 675.8 136 675.8
-DL 144 675.8 140 675.8 DL 148 675.8 144 675.8 DL 152 675.8 148 675.8 DL 156
-675.8 152 675.8 DL 160 675.8 156 675.8 DL 164 675.8 160 675.8 DL 168 675.8 164
-675.8 DL 172 675.8 168 675.8 DL 176 675.8 172 675.8 DL 180 675.8 176 675.8 DL
-184 675.8 180 675.8 DL 188 675.8 184 675.8 DL 192 675.8 188 675.8 DL 196 675.8
-192 675.8 DL 200 675.8 196 675.8 DL 204 675.8 200 675.8 DL 208 675.8 204 675.8
-DL 212 675.8 208 675.8 DL 216 675.8 212 675.8 DL/F4 5/Times-Roman@0 SF(20)93.6
-686.2 Q/F5 8/Times-Roman@0 SF .466(That is, don')3.2 J 2.466(tc)-.144 G .466
-(reate ne)148.294 689.4 R 2.466(wm)-.2 G .466(aps and then use)188.122 689.4 R
-/F6 8/Times-Italic@0 SF(mv)2.466 E F5 .466(\(1\) to mo)B .706 -.12(ve t)-.12 H
-.466(hem into place.).12 F .465(Since the maps are already open the ne)4.466 F
-2.465(wm)-.2 G(aps)493.336 689.4 Q(will ne)72 699 Q -.12(ve)-.2 G 2(rb).12 G 2
-(es)109.896 699 S(een.)118.56 699 Q EP
-%%Page: 51 46
-%%BeginPageSetup
-BP
-%%EndPageSetup
-/F0 10/Times-Bold@0 SF(Sendmail Installation and Operation Guide)72 60 Q
-(SMM:08-51)452.9 60 Q/F1 10/Times-Roman@0 SF .655
-(appropriate -request address.)189 96 R .656(It is v)5.656 F .656
-(ery similar to the o)-.15 F(wner)-.25 E(-)-.2 E/F2 10/Times-Italic@0 SF(list)A
-F1 .656(syntax in the)3.156 F(alias \214le.)189 108 Q 33.95(fullname The)117
-124.2 R(full name of the user)2.5 E(.)-.55 E(of)117 140.4 Q 13.66
-(\214ce-address The)-.25 F(of)2.5 E(\214ce address for this user)-.25 E(.)-.55
-E(of)117 156.6 Q 19.21(\214ce-phone The)-.25 F(of)2.5 E
-(\214ce phone number for this user)-.25 E(.)-.55 E(of)117 172.8 Q(\214ce-f)-.25
-E 30.98(ax The)-.1 F(of)2.5 E(\214ce F)-.25 E(AX number for this user)-.74 E(.)
--.55 E 13.96(home-address The)117 189 R(home address for this user)2.5 E(.)-.55
-E 19.51(home-phone The)117 205.2 R(home phone number for this user)2.5 E(.)-.55
-E(home-f)117 221.4 Q 31.28(ax The)-.1 F(home F)2.5 E(AX number for this user)
--.74 E(.)-.55 E 41.73(project A)117 237.6 R .856
-(\(short\) description of the project this person is af)3.356 F .855
-(\214liated with.)-.25 F .855(In the Uni-)5.855 F -.15(ve)189 249.6 S
-(rsity this is often just the name of their graduate advisor).15 E(.)-.55 E
-52.28(plan A)117 265.8 R
-(pointer to a \214le from which plan information can be g)2.5 E(athered.)-.05 E
-.924(As of this writing, only a fe)142 282 R 3.424(wo)-.25 G 3.424(ft)273.208
-282 S .925(hese \214elds are actually being used by)282.742 282 R F2(sendmail)
-3.425 E F1(:)A F2(mail-)3.425 E(dr)117 294 Q(op)-.45 E F1(and)2.5 E F2
-(mailname)2.5 E F1 5(.A)C F2(\214ng)211.54 294 Q(er)-.1 E F1
-(program that uses the other \214elds is planned.)2.5 E F0 2.5(5.10.2. User)102
-318 R(database semantics)2.5 E F1 .996(When the re)142 334.2 R .995
-(writing rules submit an address to the local mailer)-.25 F 3.495(,t)-.4 G .995
-(he user name is passed)408.93 334.2 R .78(through the alias \214le.)117 346.2
-R .781
-(If no alias is found \(or if the alias points back to the same address\), the)
-5.78 F 1.778(name \(with \231:maildrop\232 appended\) is then used as a k)117
-358.2 R 2.077 -.15(ey i)-.1 H 4.277(nt).15 G 1.777(he user database.)375.985
-358.2 R 1.777(If no match)6.777 F
-(occurs \(or if the maildrop points at the same address\), forw)117 370.2 Q
-(arding is tried.)-.1 E .55(If the \214rst tok)142 386.4 R .551(en of the user\
- name returned by ruleset 0 is an \231@\232 sign, the user database)-.1 F .626
-(lookup is skipped.)117 398.4 R .625
-(The intent is that the user database will act as a set of def)5.626 F .625
-(aults for a cluster)-.1 F 1.533(\(in our case, the Computer Science Di)117
-410.4 R 1.533(vision\); mail sent to a speci\214c machine should ignore)-.25 F
-(these def)117 422.4 Q(aults.)-.1 E .351
-(When mail is sent, the name of the sending user is look)142 438.6 R .351
-(ed up in the database.)-.1 F .351(If that user)5.351 F .04
-(has a \231mailname\232 record, the v)117 450.6 R .041
-(alue of that record is used as their outgoing name.)-.25 F -.15(Fo)5.041 G
-2.541(re).15 G .041(xample, I)466.189 450.6 R(might ha)117 462.6 Q .3 -.15
-(ve a r)-.2 H(ecord:).15 E 48.29(eric:mailname Eric.Allman@CS.Berk)157 478.8 R
-(ele)-.1 E -.65(y.)-.15 G(EDU).65 E(This w)117 495 Q
-(ould cause my outgoing mail to be sent as Eric.Allman.)-.1 E .52
-(If a \231maildrop\232 is found for the user)142 511.2 R 3.019(,b)-.4 G .519
-(ut no corresponding \231mailname\232 record e)299.686 511.2 R .519(xists, the)
--.15 F 1.127(record \231:def)117 523.2 R 1.127(ault:mailname\232 is consulted.)
--.1 F 1.127(If present, this is the name of a host to o)6.127 F -.15(ve)-.15 G
-1.128(rride the).15 F .625(local host.)117 535.2 R -.15(Fo)5.625 G 3.125(re).15
-G .625(xample, in our case we w)185.515 535.2 R .625
-(ould set it to \231CS.Berk)-.1 F(ele)-.1 E -.65(y.)-.15 G 3.125(EDU\232. The)
-.65 F(ef)3.125 E .625(fect is that)-.25 F(an)117 547.2 Q .881(yone kno)-.15 F
-.882(wn in the database gets their outgoing mail stamped as \231user@CS.Berk)
--.25 F(ele)-.1 E -.65(y.)-.15 G(EDU\232,).65 E -.2(bu)117 559.2 S 2.5(tp).2 G
-(eople not listed in the database use the local hostname.)137.08 559.2 Q F0 2.5
-(5.10.3. Cr)102 585.2 R(eating the database)-.18 E/F3 7/Times-Bold@0 SF(21)
-228.2 581.2 Q F1 .375(The user database is b)142 601.4 R .375(uilt from a te)
--.2 F .375(xt \214le using the)-.15 F F2(mak)2.875 E(emap)-.1 E F1 .375
-(utility \(in the distrib)2.875 F .375(ution in)-.2 F 1.039(the mak)117 613.4 R
-1.039(emap subdirectory\).)-.1 F 1.039(The te)6.039 F 1.038
-(xt \214le is a series of lines corresponding to userdb records;)-.15 F 1.588
-(each line has a k)117 625.4 R 1.889 -.15(ey a)-.1 H 1.589(nd a v).15 F 1.589
-(alue separated by white space.)-.25 F 1.589(The k)6.589 F 1.889 -.15(ey i)-.1
-H 4.089(sa).15 G -.1(lwa)421.943 625.4 S 1.589(ys in the format).1 F
-(described abo)117 637.4 Q .3 -.15(ve \212 f)-.15 H(or e).15 E(xample:)-.15 E
-(eric:maildrop)157 653.6 Q .448
-(This \214le is normally installed in a system directory; for e)117 669.8 R
-.447(xample, it might be called)-.15 F F2(/etc/user)2.947 E(db)-.37 E F1(.)A
-.32 LW 76 679.4 72 679.4 DL 80 679.4 76 679.4 DL 84 679.4 80 679.4 DL 88 679.4
-84 679.4 DL 92 679.4 88 679.4 DL 96 679.4 92 679.4 DL 100 679.4 96 679.4 DL 104
-679.4 100 679.4 DL 108 679.4 104 679.4 DL 112 679.4 108 679.4 DL 116 679.4 112
-679.4 DL 120 679.4 116 679.4 DL 124 679.4 120 679.4 DL 128 679.4 124 679.4 DL
-132 679.4 128 679.4 DL 136 679.4 132 679.4 DL 140 679.4 136 679.4 DL 144 679.4
-140 679.4 DL 148 679.4 144 679.4 DL 152 679.4 148 679.4 DL 156 679.4 152 679.4
-DL 160 679.4 156 679.4 DL 164 679.4 160 679.4 DL 168 679.4 164 679.4 DL 172
-679.4 168 679.4 DL 176 679.4 172 679.4 DL 180 679.4 176 679.4 DL 184 679.4 180
-679.4 DL 188 679.4 184 679.4 DL 192 679.4 188 679.4 DL 196 679.4 192 679.4 DL
-200 679.4 196 679.4 DL 204 679.4 200 679.4 DL 208 679.4 204 679.4 DL 212 679.4
-208 679.4 DL 216 679.4 212 679.4 DL/F4 5/Times-Roman@0 SF(21)93.6 689.8 Q/F5 8
-/Times-Roman@0 SF .289(These instructions are kno)3.2 J .289
-(wn to be incomplete.)-.2 F 2.289(Af)4.289 G .289(uture v)266.464 693 R .289
-(ersion of the user database is planned including things such as \214n-)-.12 F
-(ger service \212 and good documentation.)72 702.6 Q EP
-%%Page: 52 47
-%%BeginPageSetup
-BP
-%%EndPageSetup
-/F0 10/Times-Bold@0 SF 193.36(SMM:08-52 Sendmail)72 60 R
-(Installation and Operation Guide)2.5 E/F1 10/Times-Roman@0 SF 1.6 -.8(To m)117
-96 T(ak).8 E 2.5(et)-.1 G(he database v)156.65 96 Q
-(ersion of the map, run the program:)-.15 E(mak)157 112.2 Q
-(emap btree /etc/userdb)-.1 E(.db < /etc/userdb)-.4 E .077
-(Then create a con\214g \214le that uses this.)117 128.4 R -.15(Fo)5.077 G
-2.577(re).15 G .077(xample, using the V8 M4 con\214guration, include the)
-296.531 128.4 R(follo)117 140.4 Q(wing line in your .mc \214le:)-.25 E
-(de\214ne\(\222confUSERDB_SPEC\264, /etc/userdb)157 156.6 Q(.db\))-.4 E F0 2.5
-(6. O)72 184.8 R(THER CONFIGURA)-.4 E(TION)-.95 E F1 .907
-(There are some con\214guration changes that can be made by recompiling)112 201
-R/F2 10/Times-Italic@0 SF(sendmail)3.407 E F1 5.907(.T)C .906(his section)
-460.594 201 R 1.139
-(describes what changes can be made and what has to be modi\214ed to mak)87 213
-R 3.639(et)-.1 G 3.639(hem. In)403.894 213 R 1.139(most cases this)3.639 F
-(should be unnecessary unless you are porting)87 225 Q F2(sendmail)2.5 E F1
-(to a ne)2.5 E 2.5(we)-.25 G -.4(nv)349.76 225 S(ironment.).4 E F0 2.5(6.1. P)
-87 249 R(arameters in sr)-.1 E(c/Mak)-.18 E(e\214le)-.1 E F1 .92
-(These parameters are intended to describe the compilation en)127 265.2 R .92
-(vironment, not site polic)-.4 F 2.22 -.65(y, a)-.15 H(nd).65 E
-(should normally be de\214ned in src/Mak)102 277.2 Q(e\214le.)-.1 E 39.5
-(NDBM If)102 293.4 R .664(set, the ne)3.164 F 3.164(wv)-.25 G .664
-(ersion of the DBM library that allo)240.406 293.4 R .665
-(ws multiple databases will be)-.25 F 2.543(used. If)174 305.4 R .042
-(neither NDBM nor NEWDB are set, a much less ef)2.543 F .042
-(\214cient method of alias)-.25 F(lookup is used.)174 317.4 Q 32.84(NEWDB If)
-102 333.6 R .141(set, use the ne)2.641 F 2.642(wd)-.25 G .142
-(atabase package from Berk)254.436 333.6 R(ele)-.1 E 2.642(y\()-.15 G .142
-(from 4.4BSD\).)385.814 333.6 R .142(This package)5.142 F .267
-(is substantially f)174 345.6 R .267(aster than DBM or NDBM.)-.1 F .267
-(If NEWDB and NDBM are both set,)5.267 F F2(sendmail)174 357.6 Q F1
-(will read DBM \214les, b)2.5 E(ut will create and use NEWDB \214les.)-.2 E
-53.39(NIS Include)102 373.8 R .119(support for NIS.)2.619 F .119
-(If set together with)5.119 F F2(both)2.619 E F1 .119(NEWDB and NDBM,)2.619 F
-F2(sendmail)2.62 E F1 .947(will create both DBM and NEWDB \214les if and only \
-if an alias \214le includes the)174 385.8 R 3.409
-(substring \231/yp/\232 in the name.)174 397.8 R 3.409
-(This is intended for compatibility with Sun)8.409 F(Microsystems')174 409.8 Q
-F2(mkalias)2.5 E F1(program used on YP masters.)2.5 E 28.94(NISPLUS Compile)102
-426 R(in support for NIS+.)2.5 E 26.73(NETINFO Compile)102 442.2 R
-(in support for NetInfo \(NeXT stations\).)2.5 E 32.84(HESIOD Compile)102 458.4
-R(in support for Hesiod.)2.5 E(_P)102 474.6 Q -1.11(AT)-.92 G(H_SENDMAILCF)1.11
-E(The pathname of the sendmail.cf \214le.)174 486.6 Q(_P)102 502.8 Q -1.11(AT)
--.92 G(H_SENDMAILPID)1.11 E(The pathname of the sendmail.pid \214le.)174 514.8
-Q 1.44(There are also se)127 531 R -.15(ve)-.25 G 1.439
-(ral compilation \215ags to indicate the en).15 F 1.439
-(vironment such as \231_AIX3\232 and)-.4 F 2.5(\231_SCO_unix_\232. See)102 543
-R(the READ_ME \214le for the latest scoop on these \215ags.)2.5 E F0 2.5
-(6.2. P)87 567 R(arameters in sr)-.1 E(c/conf)-.18 E(.h)-.15 E F1 -.15(Pa)127
-583.2 S .895(rameters and compilation options are de\214ned in conf.h.).15 F
-.896(Most of these need not normally)5.895 F .193(be tweak)102 595.2 R .192
-(ed; common parameters are all in sendmail.cf.)-.1 F(Ho)5.192 E(we)-.25 E -.15
-(ve)-.25 G .992 -.4(r, t).15 H .192(he sizes of certain primiti).4 F .492 -.15
-(ve ve)-.25 H(c-).15 E(tors, etc., are included in this \214le.)102 607.2 Q
-(The numbers follo)5 E(wing the parameters are their def)-.25 E(ault v)-.1 E
-(alue.)-.25 E 1.247(This document is not the best source of information for co\
-mpilation \215ags in conf.h \212 see)127 623.4 R
-(src/READ_ME or src/conf.h itself.)102 635.4 Q 1.91(MAXLINE [2048])102 651.6 R
-1.909(The maximum line length of an)190.31 651.6 R 4.409(yi)-.15 G 1.909
-(nput line.)338.276 651.6 R 1.909(If message lines e)6.909 F 1.909(xceed this)
--.15 F .575(length the)188.4 663.6 R 3.075(yw)-.15 G .575
-(ill still be processed correctly; ho)243.84 663.6 R(we)-.25 E -.15(ve)-.25 G
-1.375 -.4(r, h).15 H .575(eader lines, con\214gura-).4 F
-(tion \214le lines, alias lines, etc., must \214t within this limit.)188.4
-675.6 Q(MAXN)102 691.8 Q(AME [256])-.35 E(The maximum length of an)9.82 E 2.5
-(yn)-.15 G(ame, such as a host or a user name.)309.63 691.8 Q .231(MAXPV [40])
-102 708 R .231(The maximum number of parameters to an)188.631 708 R 2.731(ym)
--.15 G(ailer)376.458 708 Q 5.231(.T)-.55 G .23(his limits the number of)407.519
-708 R .375(recipients that may be passed in one transaction.)188.4 720 R .376
-(It can be set to an)5.376 F 2.876(ya)-.15 G(rbitrary)474.01 720 Q EP
-%%Page: 53 48
-%%BeginPageSetup
-BP
-%%EndPageSetup
-/F0 10/Times-Bold@0 SF(Sendmail Installation and Operation Guide)72 60 Q
-(SMM:08-53)452.9 60 Q/F1 10/Times-Roman@0 SF .876(number abo)188.4 96 R 1.176
--.15(ve a)-.15 H .876(bout 10, since).15 F/F2 10/Times-Italic@0 SF(sendmail)
-3.376 E F1 .876(will break up a deli)3.376 F -.15(ve)-.25 G .875
-(ry into smaller).15 F .886(batches as needed.)188.4 108 R 3.386(Ah)5.886 G
-.887(igher number may reduce load on your system, ho)285.804 108 R(w-)-.25 E
--2.15 -.25(ev e)188.4 120 T -.55(r.).25 G(MAXA)102 136.2 Q -.18(TO)-1.11 G
-2.559(M[).18 G 8.26(100] The)159.369 136.2 R .059
-(maximum number of atoms \(tok)2.559 F .058(ens\) in a single address.)-.1 F
--.15(Fo)5.058 G 2.558(re).15 G .058(xample, the)457.282 136.2 R
-(address \231eric@CS.Berk)188.4 148.2 Q(ele)-.1 E -.65(y.)-.15 G(EDU\232 is se)
-.65 E -.15(ve)-.25 G 2.5(na).15 G(toms.)367.93 148.2 Q .112(MAXMAILERS [25])102
-164.4 R .112(The maximum number of mailers that may be de\214ned in the con\
-\214guration \214le.).02 F(MAXR)102 180.6 Q .401(WSETS [200])-.55 F .401
-(The maximum number of re).01 F .401(writing sets that may be de\214ned.)-.25 F
-.4(The \214rst half of)5.4 F .034(these are reserv)188.4 192.6 R .034
-(ed for numeric speci\214cation \(e.g., `)-.15 F(`S92')-.74 E .035
-('\), while the upper half)-.74 F .492(are reserv)188.4 204.6 R .492
-(ed for auto-numbering \(e.g., `)-.15 F(`Sfoo')-.74 E 2.992('\). Thus,)-.74 F
-.492(with a v)2.992 F .491(alue of 200 an)-.25 F(attempt to use `)188.4 216.6 Q
-(`S99')-.74 E 2.5('w)-.74 G(ill succeed, b)284.13 216.6 Q(ut `)-.2 E(`S100')
--.74 E 2.5('w)-.74 G(ill f)388.82 216.6 Q(ail.)-.1 E(MAXPRIORITIES [25])102
-232.8 Q 2.481(The maximum number of v)188.4 244.8 R 2.482
-(alues for the \231Precedence:\232 \214eld that may be)-.25 F
-(de\214ned \(using the)188.4 256.8 Q F0(P)2.5 E F1(line in sendmail.cf\).)2.5 E
-(MAXUSERENVIR)102 273 Q(ON [100])-.4 E .399
-(The maximum number of items in the user en)188.4 285 R .399
-(vironment that will be passed to)-.4 F(subordinate mailers.)188.4 297 Q
-(MAXMXHOSTS [20])102 313.2 Q
-(The maximum number of MX records we will accept for an)188.4 325.2 Q 2.5(ys)
--.15 G(ingle host.)439.03 325.2 Q .712(MAXALIASDB [12])102 341.4 R .712
-(The maximum number of alias databases that can be open at an).58 F 3.213(yt)
--.15 G 3.213(ime. Note)461.347 341.4 R
-(that there may also be an open \214le limit.)188.4 353.4 Q(MAXMAPST)102 369.6
-Q -.4(AC)-.93 G 2.5(K[).4 G(12])184.28 369.6 Q 1.65
-(The maximum number of maps that may be "stack)188.4 381.6 R 1.65(ed" in a)-.1
-F F0(sequence)4.15 E F1(class)4.15 E(map.)188.4 393.6 Q(MAXMIMEARGS [20])102
-409.8 Q .718(The maximum number of ar)188.4 421.8 R .718
-(guments in a MIME Content-T)-.18 F .718(ype: header; addi-)-.8 F(tional ar)
-188.4 433.8 Q(guments will be ignored.)-.18 E(MAXMIMENESTING [20])102 450 Q .4
-(The maximum depth to which MIME messages may be nested \(that is, nested)188.4
-462 R 1.344
-(Message or Multipart documents; this does not limit the number of compo-)188.4
-474 R(nents in a single Multipart document\).)188.4 486 Q 2.851(An)102 502.2 S
-.351(umber of other compilation options e)117.071 502.2 R 2.851(xist. These)
--.15 F .35(specify whether or not speci\214c code should be)2.851 F
-(compiled in.)102 514.2 Q(Ones mark)5 E(ed with \207 are 0/1 v)-.1 E(alued.)
--.25 E 36.69(NETINET\207 If)102 530.4 R .829
-(set, support for Internet protocol netw)3.329 F .829(orking is compiled in.)
--.1 F(Pre)5.829 E .83(vious v)-.25 F(er)-.15 E(-)-.2 E .178(sions of)188.4
-542.4 R F2(sendmail)2.678 E F1 .178(referred to this as)2.678 F/F3 9
-/Times-Roman@0 SF -.36(DA)2.678 G(EMON).36 E F1 2.677(;t)C .177
-(his old usage is no)381.715 542.4 R 2.677(wi)-.25 G(ncorrect.)468.74 542.4 Q
-(Def)188.4 554.4 Q 1.87(aults on; turn it of)-.1 F 4.37(fi)-.25 G 4.37(nt)
-292.67 554.4 S 1.87(he Mak)304.82 554.4 R 1.87(e\214le if your system doesn')
--.1 F 4.37(ts)-.18 G 1.87(upport the)461.3 554.4 R(Internet protocols.)188.4
-566.4 Q 43.35(NETISO\207 If)102 582.6 R .143
-(set, support for ISO protocol netw)2.643 F .142
-(orking is compiled in \(it may be appropri-)-.1 F
-(ate to #de\214ne this in the Mak)188.4 594.6 Q(e\214le instead of conf.h\).)
--.1 E 63.35(LOG If)102 610.8 R .5(set, the)3 F F2(syslo)3 E(g)-.1 E F1 .5
-(routine in use at some sites is used.)3 F .5(This mak)5.5 F .5(es an informa-)
--.1 F .504(tional log record for each message processed, and mak)188.4 622.8 R
-.504(es a higher priority log)-.1 F .052(record for internal system errors.)
-188.4 634.8 R F0(STR)5.052 E(ONGL)-.3 E 2.552(YR)-.92 G(ECOMMENDED)389.682
-634.8 Q F1 2.553<8a69>2.552 G 2.553(fy)483.117 634.8 S(ou)494 634.8 Q -.1(wa)
-188.4 646.8 S(nt no logging, turn it of).1 E 2.5(fi)-.25 G 2.5(nt)301.66 646.8
-S(he con\214guration \214le.)311.94 646.8 Q(MA)102 663 Q 11.12
-(TCHGECOS\207 Compile)-1.11 F 3.555(in the code to do `)6.055 F 3.555
-(`fuzzy matching')-.74 F 6.055('o)-.74 G 6.055(nt)404.22 663 S 3.555
-(he GECOS \214eld in)418.055 663 R 2.5(/etc/passwd. This)188.4 675 R
-(also requires that the)2.5 E F0(MatchGECOS)2.5 E F1(option be turned on.)2.5 E
--.35(NA)102 691.2 S 13.15(MED_BIND\207 Compile).35 F .412
-(in code to use the Berk)2.912 F(ele)-.1 E 2.913(yI)-.15 G .413
-(nternet Name Domain \(BIND\) serv)342.405 691.2 R .413(er to)-.15 F(resolv)
-188.4 703.2 Q 2.5(eT)-.15 G(CP/IP host names.)225.74 703.2 Q EP
-%%Page: 54 49
-%%BeginPageSetup
-BP
-%%EndPageSetup
-/F0 10/Times-Bold@0 SF 193.36(SMM:08-54 Sendmail)72 60 R
-(Installation and Operation Guide)2.5 E/F1 10/Times-Roman@0 SF(NO)102 96 Q
-38.76(TUNIX If)-.4 F .248
-(you are using a non-UNIX mail format, you can set this \215ag to turn of)2.748
-F 2.747(fs)-.25 G(pe-)491.23 96 Q
-(cial processing of UNIX-style \231From \232 lines.)188.4 108 Q -.1(QU)102
-124.2 S 50.12(EUE This).1 F 1.559
-(\215ag should be set to compile in the queueing code.)4.059 F 1.56
-(If this is not set,)6.56 F
-(mailers must accept the mail immediately or it will be returned to the sender)
-188.4 136.2 Q(.)-.55 E 57.78(SMTP If)102 152.4 R .756
-(set, the code to handle user and serv)3.256 F .756
-(er SMTP will be compiled in.)-.15 F .756(This is)5.756 F 2.507
-(only necessary if your machine has some mailer that speaks SMTP \(this)188.4
-164.4 R(means most machines e)188.4 176.4 Q -.15(ve)-.25 G(rywhere\).).15 E
-39.45(USERDB\207 Include)102 192.6 R(the)3.449 E F0(experimental)3.449 E F1
-(Berk)3.449 E(ele)-.1 E 3.449(yu)-.15 G .949(ser information database package.)
-341.356 192.6 R(This)5.948 E .27(adds a ne)188.4 204.6 R 2.77(wl)-.25 G -2.15
--.25(ev e)238.67 204.6 T 2.77(lo).25 G 2.77(fl)262.7 204.6 S .27(ocal name e)
-271.58 204.6 R .27(xpansion between aliasing and forw)-.15 F 2.77(arding. It)
--.1 F(also uses the NEWDB package.)188.4 216.6 Q
-(This may change in future releases.)5 E(The follo)102 232.8 Q
-(wing options are normally turned on in per)-.25 E
-(-operating-system clauses in conf.h.)-.2 E(IDENTPR)102 249 Q -1.88 -.4(OT O)
--.4 H 19.61<8743>.4 G .376
-(ompile in the IDENT protocol as de\214ned in RFC 1413.)195.07 249 R .375
-(This def)5.375 F .375(aults on for)-.1 F 1.053(all systems e)188.4 261 R 1.053
-(xcept Ultrix, which apparently has the interesting \231feature\232 that)-.15 F
-.83(when it recei)188.4 273 R -.15(ve)-.25 G 3.33(sa\231).15 G .83
-(host unreachable\232 message it closes all open connections)270.18 273 R 1.921
-(to that host.)188.4 285 R 1.921(Since some \214re)6.921 F -.1(wa)-.25 G 1.922
-(ll g).1 F(ate)-.05 E -.1(wa)-.25 G 1.922(ys send this error code when you).1 F
-2.055
-(access an unauthorized port \(such as 113, used by IDENT\), Ultrix cannot)
-188.4 297 R(recei)188.4 309 Q .3 -.15(ve e)-.25 H(mail from such hosts.).15 E
-39.45(SYSTEM5 Set)102 325.2 R
-(all of the compilation parameters appropriate for System V)2.5 E(.)-1.29 E
-26.12(HASFLOCK\207 Use)102 341.4 R(Berk)2.844 E(ele)-.1 E(y-style)-.15 E F0
-(\215ock)2.844 E F1 .344(instead of System V)2.844 F F0(lockf)2.845 E F1 .345
-(to do \214le locking.)2.845 F .345(Due to)5.345 F .184
-(the highly unusual semantics of locks across forks in)188.4 353.4 R F0(lockf)
-2.684 E F1 2.684(,t)C .184(his should al)432.722 353.4 R -.1(wa)-.1 G(ys).1 E
-(be used if at all possible.)188.4 365.4 Q(HASINITGR)102 381.6 Q 4.86(OUPS Set)
--.4 F 1.284(this if your system has the)3.783 F/F2 10/Times-Italic@0 SF(initgr)
-3.784 E(oups\(\))-.45 E F1 1.284(call \(if you ha)3.784 F 1.584 -.15(ve m)-.2 H
-1.284(ultiple group).15 F 4.417(support\). This)188.4 393.6 R 1.917(is the def)
-4.417 F 1.917(ault if SYSTEM5 is)-.1 F F2(not)4.416 E F1 1.916
-(de\214ned or if you are on)4.416 F(HPUX.)188.4 405.6 Q(HASUN)102 421.8 Q 27.59
-(AME Set)-.35 F 1.148(this if you ha)3.648 F 1.448 -.15(ve t)-.2 H(he).15 E F2
-(uname)3.648 E F1 1.149(\(2\) system call \(or corresponding library rou-)B 2.5
-(tine\). Set)188.4 433.8 R(by def)2.5 E(ault if SYSTEM5 is set.)-.1 E(HASGETDT)
-102 450 Q(ABLESIZE)-.93 E(Set this if you ha)188.4 462 Q .3 -.15(ve t)-.2 H(he)
-.15 E F2 -.1(ge)2.5 G(tdtablesize).1 E F1(\(2\) system call.)A(HASW)102 478.2 Q
-22.89(AITPID Set)-1.2 F(this if you ha)2.5 E .3 -.15(ve t)-.2 H(he).15 E F2
-(haswaitpid)2.5 E F1(\(2\) system call.)A 37.22(SFS_TYPE The)102 494.4 R .517
-(mechanism that can be used to get \214le system capacity information.)3.017 F
-(The)5.516 E -.25(va)188.4 506.4 S .214(lues can be one of SFS_UST).25 F 2.435
--1.11(AT \()-.93 H .215(use the ustat\(2\) syscall\), SFS_4ARGS \(use)1.11 F
-.415(the four ar)188.4 518.4 R .415
-(gument statfs\(2\) syscall\), SFS_VFS \(use the tw)-.18 F 2.915(oa)-.1 G -.18
-(rg)435.165 518.4 S .415(ument statfs\(2\)).18 F .716
-(syscall including <sys/vfs.h>\), SFS_MOUNT \(use the tw)188.4 530.4 R 3.217
-(oa)-.1 G -.18(rg)434.863 530.4 S .717(ument statfs\(2\)).18 F 4.32
-(syscall including <sys/mount.h>\), SFS_ST)188.4 542.4 R -1.11(AT)-.93 G 4.32
-(FS \(use the tw)1.11 F 6.82(oa)-.1 G -.18(rg)470.85 542.4 S(ument).18 E 1.108
-(statfs\(2\) syscall including <sys/statfs.h>\), SFS_ST)188.4 554.4 R -1.11(AT)
--.93 G 1.109(VFS \(use the tw)1.11 F 3.609(oa)-.1 G -.18(rg)487.52 554.4 S(u-)
-.18 E 1.511
-(ment statfs\(2\) syscall including <sys/statvfs.h>\), or SFS_NONE \(no w)188.4
-566.4 R 1.511(ay to)-.1 F(get this information\).)188.4 578.4 Q 40.57
-(LA_TYPE The)102 594.6 R(load a)2.5 E -.15(ve)-.2 G(rage type.).15 E
-(Details are described belo)5 E -.65(w.)-.25 G .342(The are se)102 610.8 R -.15
-(ve)-.25 G .342(ral b).15 F .342(uilt-in w)-.2 F .342
-(ays of computing the load a)-.1 F -.15(ve)-.2 G(rage.).15 E F2(Sendmail)5.342
-E F1 .343(tries to auto-con\214gure them)2.842 F .267
-(based on imperfect guesses; you can select one using the)102 622.8 R F2(cc)
-2.766 E F1(option)2.766 E F0(\255DLA_TYPE=)2.766 E F2(type)A F1 2.766(,w)C
-(here)467.364 622.8 Q F2(type)2.766 E F1(is:)102 634.8 Q 48.91(LA_INT The)102
-651 R -.1(ke)3.452 G .952(rnel stores the load a).1 F -.15(ve)-.2 G .952
-(rage in the k).15 F .952(ernel as an array of long inte)-.1 F(gers.)-.15 E
-(The actual v)188.4 663 Q(alues are scaled by a f)-.25 E(actor FSCALE \(def)-.1
-E(ault 256\).)-.1 E(LA_SHOR)102 679.2 Q 35.89(TT)-.6 G .794(he k)194.51 679.2 R
-.794(ernel stores the load a)-.1 F -.15(ve)-.2 G .794(rage in the k).15 F .793
-(ernel as an array of short inte)-.1 F(gers.)-.15 E(The actual v)188.4 691.2 Q
-(alues are scaled by a f)-.25 E(actor FSCALE \(def)-.1 E(ault 256\).)-.1 E
-(LA_FLO)102 707.4 Q 37.03 -1.11(AT T)-.35 H .088(he k)1.11 F .088
-(ernel stores the load a)-.1 F -.15(ve)-.2 G .089(rage in the k).15 F .089
-(ernel as an array of double precision)-.1 F(\215oats.)188.4 719.4 Q EP
-%%Page: 55 50
-%%BeginPageSetup
-BP
-%%EndPageSetup
-/F0 10/Times-Bold@0 SF(Sendmail Installation and Operation Guide)72 60 Q
-(SMM:08-55)452.9 60 Q/F1 10/Times-Roman@0 SF(LA_MA)102 96 Q 35.97(CH Use)-.4 F
-(MA)2.5 E(CH-style load a)-.4 E -.15(ve)-.2 G(rages.).15 E 39.45(LA_SUBR Call)
-102 112.2 R(the)2.5 E/F2 10/Times-Italic@0 SF -.1(ge)2.5 G(tloadavg).1 E F1
-(routine to get the load a)2.5 E -.15(ve)-.2 G(rage as an array of doubles.).15
-E(LA_ZER)102 128.4 Q 42.36(OA)-.4 G -.1(lwa)195.62 128.4 S
-(ys return zero as the load a).1 E -.15(ve)-.2 G 2.5(rage. This).15 F(is the f)
-2.5 E(allback case.)-.1 E .494(If type)102 144.6 R/F3 9/Times-Roman@0 SF
-(LA_INT)2.994 E F1(,)A F3(LA_SHOR)2.994 E(T)-.54 E F1 2.994(,o)C(r)224.806
-144.6 Q F3(LA_FLO)2.993 E -.999(AT)-.315 G F1 .493
-(is speci\214ed, you may also need to specify)3.992 F F3(_P)2.993 E -.999(AT)
--.828 G(H_UNIX).999 E F1 .948(\(the path to your system binary\) and)102 156.6
-R F3(LA_A)3.448 E(VENR)-1.215 E(UN)-.36 E F1 .949(\(the name of the v)3.448 F
-.949(ariable containing the load)-.25 F -2.25 -.2(av e)102 168.6 T
-(rage in the k).2 E(ernel; usually \231_a)-.1 E -.15(ve)-.2 G
-(nrun\232 or \231a).15 E -.15(ve)-.2 G(nrun\232\).).15 E F0 2.5
-(6.3. Con\214guration)87 192.6 R(in sr)2.5 E(c/conf)-.18 E(.c)-.15 E F1
-(The follo)127 208.8 Q(wing changes can be made in conf.c.)-.25 E F0 2.5
-(6.3.1. Built-in)102 232.8 R(Header Semantics)2.5 E F1 1.248
-(Not all header semantics are de\214ned in the con\214guration \214le.)142 249
-R 1.247(Header lines that should)6.247 F .305(only be included by certain mail\
-ers \(as well as other more obscure semantics\) must be speci\214ed)117 261 R
-.047(in the)117 273 R F2(HdrInfo)2.547 E F1 .047(table in)2.547 F F2(conf)2.547
-E(.c)-.15 E F1 5.047(.T)C .046
-(his table contains the header name \(which should be in all lo)246.842 273 R
-(wer)-.25 E(case\) and a set of header control \215ags \(described belo)117 285
-Q(w\), The \215ags are:)-.25 E(H_A)117 301.2 Q 30.97(CHECK Normally)-.4 F .007
-(when the check is made to see if a header line is compatible with)2.507 F
-2.941(am)203.4 313.2 S(ailer)218.561 313.2 Q(,)-.4 E F2(sendmail)2.941 E F1
-.441(will not delete an e)2.941 F .441(xisting line.)-.15 F .44
-(If this \215ag is set,)5.441 F F2(send-)2.94 E(mail)203.4 325.2 Q F1 .152
-(will delete e)2.652 F -.15(ve)-.25 G 2.652(ne).15 G .152
-(xisting header lines.)293.998 325.2 R .152
-(That is, if this bit is set and the)5.152 F 1.425(mailer does not ha)203.4
-337.2 R 1.725 -.15(ve \215)-.2 H 1.425
-(ag bits set that intersect with the required mailer).15 F 2.204
-(\215ags in the header de\214nition in sendmail.cf, the header line is)203.4
-349.2 R F2(always)4.704 E F1(deleted.)203.4 361.2 Q 51.13(H_EOH If)117 377.4 R
-.206(this header \214eld is set, treat it lik)2.706 F 2.706(eab)-.1 G .206
-(lank line, i.e., it will signal the end)363.95 377.4 R
-(of the header and the be)203.4 389.4 Q(ginning of the message te)-.15 E(xt.)
--.15 E 39.45(H_FORCE Add)117 405.6 R 2.038(this header entry e)4.538 F -.15(ve)
--.25 G 4.538(ni).15 G 4.538(fo)326.22 405.6 S 2.038(ne e)339.088 405.6 R 2.039
-(xisted in the message before.)-.15 F 2.039(If a)7.039 F 2.189
-(header entry does not ha)203.4 417.6 R 2.488 -.15(ve t)-.2 H 2.188
-(his bit set,).15 F F2(sendmail)4.688 E F1 2.188(will not add another)4.688 F
-.62(header line if a header line of this name already e)203.4 429.6 R 3.12
-(xisted. This)-.15 F -.1(wo)3.12 G .62(uld nor).1 F(-)-.2 E
-(mally be used to stamp the message by e)203.4 441.6 Q -.15(ve)-.25 G
-(ryone who handled it.).15 E(H_TRA)117 457.8 Q 39.3(CE If)-.4 F 1.044
-(set, this is a timestamp \(trace\) \214eld.)3.544 F 1.043
-(If the number of trace \214elds in a)6.043 F .705(message e)203.4 469.8 R .705
-(xceeds a preset amount the message is returned on the assump-)-.15 F
-(tion that it has an aliasing loop.)203.4 481.8 Q 46.67(H_RCPT If)117 498 R
-.332(set, this \214eld contains recipient addresses.)2.833 F .332
-(This is used by the)5.332 F F0<ad74>2.832 E F1 .332(\215ag to)2.832 F 1.349
-(determine who to send to when it is collecting recipients from the mes-)203.4
-510 R(sage.)203.4 522 Q(H_FR)117 538.2 Q 43.74(OM This)-.4 F 1.673
-(\215ag indicates that this \214eld speci\214es a sender)4.174 F 6.673(.T)-.55
-G 1.673(he order of these)432.061 538.2 R .898(\214elds in the)203.4 550.2 R F2
-(HdrInfo)3.398 E F1 .898(table speci\214es)3.398 F F2(sendmail)3.398 E F1 1.998
--.55('s p)D .898(reference for which \214eld).55 F
-(to return error messages to.)203.4 562.2 Q(H_ERR)117 578.4 Q(ORST)-.4 E 22.53
-(OA)-.18 G(ddresses in this header should recei)210.62 578.4 Q .3 -.15(ve e)
--.25 H(rror messages.).15 E 52.79(H_CTE This)117 594.6 R(header is a Content-T)
-2.5 E(ransfer)-.35 E(-Encoding header)-.2 E(.)-.55 E 40.01(H_CTYPE This)117
-610.8 R(header is a Content-T)2.5 E(ype header)-.8 E(.)-.55 E(H_STRIPV)117 627
-Q 25.25(AL Strip)-1.35 F(the v)2.5 E(alue from the header \(for Bcc:\).)-.25 E
-(Let')117 643.2 Q 2.5(sl)-.55 G(ook at a sample)142.28 643.2 Q F2(HdrInfo)2.5 E
-F1(speci\214cation:)2.5 E EP
-%%Page: 56 51
-%%BeginPageSetup
-BP
-%%EndPageSetup
-/F0 10/Times-Bold@0 SF 193.36(SMM:08-56 Sendmail)72 60 R
-(Installation and Operation Guide)2.5 E/F1 10/Times-Roman@0 SF(struct hdrinfo)
-157 96 Q(HdrInfo[] =)295.76 96 Q({)157 108 Q
-(/* originator \214elds, most to least signi\214cant)189.5 120 Q(*/)5 E 52.29
-("resent-sender", H_FR)177 132 R(OM,)-.4 E 58.95("resent-from", H_FR)177 144 R
-(OM,)-.4 E 79.5("sender", H_FR)177 156 R(OM,)-.4 E 86.16("from", H_FR)177 168 R
-(OM,)-.4 E 66.72("full-name", H_A)177 180 R(CHECK,)-.4 E 71.17
-("errors-to", H_FR)177 192 R -1.667(OM | H_ERR)-.4 F(ORST)-.4 E(O,)-.18 E
-(/* destination \214elds */)189.5 204 Q 97.82("to", H_RCPT)177 216 R(,)-.74 E
-70.61("resent-to", H_RCPT)177 228 R(,)-.74 E 96.72("cc", H_RCPT)177 240 R(,)
--.74 E 91.72("bcc", H_RCPT)177 252 R .833(|H).833 G(_STRIPV)-.833 E(AL,)-1.35 E
-(/* message identi\214cation and control */)189.5 264 Q 71.72
-("message", H_EOH,)177 276 R("te)177 288 Q 90.75(xt", H_EOH,)-.15 F
-(/* trace \214elds */)189.5 300 Q("recei)177 312 Q -.15(ve)-.25 G 72.13
-(d", H_TRA).15 F -1.667(CE | H_FORCE,)-.4 F(/* miscellaneous \214elds */)189.5
-324 Q("content-transfer)177 336 Q 2.5(-encoding", H_CTE,)-.2 F 55.61
-("content-type", H_CTYPE,)177 348 R 87.1(NULL, 0,)177 372 R(};)157 384 Q 2.435
-(This structure indicates that the \231T)117 400.2 R 2.435
-(o:\232, \231Resent-T)-.8 F 2.435
-(o:\232, and \231Cc:\232 \214elds all specify recipient)-.8 F 3.161
-(addresses. An)117 412.2 R 3.161<7999>-.15 G .662(Full-Name:\232 \214eld will \
-be deleted unless the required mailer \215ag \(indicated in)188.152 412.2 R
-.246(the con\214guration \214le\) is speci\214ed.)117 424.2 R .245
-(The \231Message:\232 and \231T)5.246 F -.15(ex)-.7 G .245
-(t:\232 \214elds will terminate the header;).15 F 1.936
-(these are used by random dissenters around the netw)117 436.2 R 1.936(ork w)
--.1 F 4.436(orld. The)-.1 F(\231Recei)4.436 E -.15(ve)-.25 G 1.937
-(d:\232 \214eld will).15 F(al)117 448.2 Q -.1(wa)-.1 G
-(ys be added, and can be used to trace messages.).1 E .446
-(There are a number of important points here.)142 464.4 R .445
-(First, header \214elds are not added automati-)5.446 F .656
-(cally just because the)117 476.4 R 3.156(ya)-.15 G .656(re in the)216.674
-476.4 R/F2 10/Times-Italic@0 SF(HdrInfo)3.157 E F1 .657(structure; the)3.157 F
-3.157(ym)-.15 G .657(ust be speci\214ed in the con\214guration)358.225 476.4 R
-.728(\214le in order to be added to the message.)117 488.4 R(An)5.727 E 3.227
-(yh)-.15 G .727(eader \214elds mentioned in the con\214guration \214le)312.988
-488.4 R -.2(bu)117 500.4 S 3.24(tn).2 G .74(ot mentioned in the)137.82 500.4 R
-F2(HdrInfo)3.24 E F1 .74(structure ha)3.24 F 1.04 -.15(ve d)-.2 H(ef).15 E .74
-(ault processing performed; that is, the)-.1 F 3.24(ya)-.15 G(re)496.23 500.4 Q
-1.375(added unless the)117 512.4 R 3.875(yw)-.15 G 1.375
-(ere in the message already)201.795 512.4 R 6.375(.S)-.65 G 1.374(econd, the)
-326.6 512.4 R F2(HdrInfo)3.874 E F1 1.374(structure only speci\214es)3.874 F
-.324
-(cliched processing; certain headers are processed specially by ad hoc code re)
-117 524.4 R -.05(ga)-.15 G .325(rdless of the sta-).05 F .481
-(tus speci\214ed in)117 536.4 R F2(HdrInfo)2.981 E F1 5.481(.F)C .481(or e)
-226.554 536.4 R .481
-(xample, the \231Sender:\232 and \231From:\232 \214elds are al)-.15 F -.1(wa)
--.1 G .48(ys scanned on).1 F(ARP)117 550.4 Q .751
-(ANET mail to determine the sender)-.92 F/F3 7/Times-Roman@0 SF(22)282.315
-546.4 Q F1 3.251(;t)289.315 550.4 S .75
-(his is used to perform the \231return to sender\232 func-)298.126 550.4 R
-2.976(tion. The)117 562.4 R .476(\231From:\232 and \231Full-Name:\232 \214elds\
- are used to determine the full name of the sender if)2.976 F
-(possible; this is stored in the macro)117 574.4 Q F0($x)2.5 E F1
-(and used in a number of w)2.5 E(ays.)-.1 E F0 2.5(6.3.2. Restricting)102 598.4
-R(Use of Email)2.5 E F1 .15
-(If it is necessary to restrict mail through a relay)142 614.6 R 2.649(,t)-.65
-G(he)339.755 614.6 Q F2 -.15(ch)2.649 G(ec).15 E(kcompat)-.2 E F1 .149
-(routine can be modi\214ed.)2.649 F .163(This routine is called for e)117 626.6
-R -.15(ve)-.25 G .163(ry recipient address.).15 F .163(It returns an e)5.163 F
-.163(xit status indicating the status of)-.15 F .895(the message.)117 638.6 R
-.895(The status)5.895 F/F4 9/Times-Roman@0 SF(EX_OK)3.395 E F1 .895
-(accepts the address,)3.395 F F4(EX_TEMPF)3.395 E(AIL)-.666 E F1 .895
-(queues the message for a)3.395 F .263(later try)117 650.6 R 2.763(,a)-.65 G
-.263(nd other v)157.696 650.6 R .264(alues \(commonly)-.25 F F4(EX_UN)2.764 E
--1.215(AVA)-.315 G(ILABLE)1.215 E F1 2.764(\)r)C .264(eject the message.)
-358.372 650.6 R .264(It is up to)5.264 F F2 -.15(ch)2.764 G(ec).15 E(k-)-.2 E
-(compat)117 662.6 Q F1 2.477(to print an error message \(using)4.978 F F2(usr)
-4.977 E(err)-.37 E F1 4.977(\)i)C 4.977(ft)331.418 662.6 S 2.477
-(he message is rejected.)342.505 662.6 R -.15(Fo)7.477 G 4.977(re).15 G
-(xample,)472.06 662.6 Q .32 LW 76 672.2 72 672.2 DL 80 672.2 76 672.2 DL 84
-672.2 80 672.2 DL 88 672.2 84 672.2 DL 92 672.2 88 672.2 DL 96 672.2 92 672.2
-DL 100 672.2 96 672.2 DL 104 672.2 100 672.2 DL 108 672.2 104 672.2 DL 112
-672.2 108 672.2 DL 116 672.2 112 672.2 DL 120 672.2 116 672.2 DL 124 672.2 120
-672.2 DL 128 672.2 124 672.2 DL 132 672.2 128 672.2 DL 136 672.2 132 672.2 DL
-140 672.2 136 672.2 DL 144 672.2 140 672.2 DL 148 672.2 144 672.2 DL 152 672.2
-148 672.2 DL 156 672.2 152 672.2 DL 160 672.2 156 672.2 DL 164 672.2 160 672.2
-DL 168 672.2 164 672.2 DL 172 672.2 168 672.2 DL 176 672.2 172 672.2 DL 180
-672.2 176 672.2 DL 184 672.2 180 672.2 DL 188 672.2 184 672.2 DL 192 672.2 188
-672.2 DL 196 672.2 192 672.2 DL 200 672.2 196 672.2 DL 204 672.2 200 672.2 DL
-208 672.2 204 672.2 DL 212 672.2 208 672.2 DL 216 672.2 212 672.2 DL/F5 5
-/Times-Roman@0 SF(22)93.6 682.6 Q/F6 8/Times-Roman@0 SF(Actually)3.2 I 2.631
-(,t)-.52 G .631
-(his is no longer true in SMTP; this information is contained in the en)132.487
-685.8 R -.12(ve)-.32 G 2.632(lope. The).12 F .632(older ARP)2.632 F .632
-(ANET protocols did)-.736 F(not completely distinguish en)72 695.4 Q -.12(ve)
--.32 G(lope from header).12 E(.)-.44 E EP
-%%Page: 57 52
-%%BeginPageSetup
-BP
-%%EndPageSetup
-/F0 10/Times-Bold@0 SF(Sendmail Installation and Operation Guide)72 60 Q
-(SMM:08-57)452.9 60 Q/F1 10/Times-Italic@0 SF -.15(ch)117 96 S(ec).15 E
-(kcompat)-.2 E/F2 10/Times-Roman@0 SF(could read:)2.5 E/F3 9/Times-Roman@0 SF
-(int)157 111 Q(checkcompat\(to, e\))157 121.8 Q(re)175 132.6 Q
-(gister ADDRESS *to;)-.135 E(re)175 143.4 Q(gister ENVELOPE *e;)-.135 E({)157
-154.2 Q(re)175 165 Q(gister ST)-.135 E(AB *s;)-.837 E 2.25(s=s)175 186.6 S
-(tab\("pri)191.578 186.6 Q -.225(va)-.225 G(te", ST_MAILER, ST_FIND\);).225 E
-(if \(s != NULL && e\255>e_from.q_mailer != LocalMailer &&)175 197.4 Q
-(to->q_mailer == s->s_mailer\))184 208.2 Q({)175 219 Q(usrerr\("No pri)193
-229.8 Q -.225(va)-.225 G(te net mail allo).225 E(wed through this machine"\);)
--.225 E(return \(EX_UN)193 240.6 Q -1.215(AVA)-.315 G(ILABLE\);)1.215 E(})175
-251.4 Q(if \(MsgSize > 50000 && bitnset\(M_LOCALMAILER, to\255>q_mailer\)\))175
-262.2 Q({)175 273 Q(usrerr\("Message too lar)193 283.8 Q(ge for non-local deli)
--.162 E -.135(ve)-.225 G(ry"\);).135 E(e\255>e_\215ags |= EF_NORETURN;)193
-294.6 Q(return \(EX_UN)193 305.4 Q -1.215(AVA)-.315 G(ILABLE\);)1.215 E(})175
-316.2 Q(return \(EX_OK\);)175 327 Q(})157 337.8 Q F2 5.146(This w)117 354 R
-5.147(ould reject messages greater than 50000 bytes unless the)-.1 F 7.647(yw)
--.15 G 5.147(ere local.)436.506 354 R(The)488.45 354 Q F1(EF_NORETURN)117 366 Q
-F2 .942(\215ag can be set in)3.442 F F1(e)3.441 E/F4 10/Symbol SF<ae>A F1
-(e_\215a)A(gs)-.1 E F2 .941(to suppress the return of the actual body of the)
-3.441 F .128(message in the error return.)117 378 R .129
-(The actual use of this routine is highly dependent on the implemen-)5.129 F
-(tation, and use should be limited.)117 390 Q F0 2.5(6.3.3. Load)102 414 R -.6
--1(Av e)2.5 H(rage Computation)1 E F2 .18(The routine)142 430.2 R F1 -.1(ge)
-2.68 G(tla).1 E F2 .18
-(should return an approximation of the current system load a)2.68 F -.15(ve)-.2
-G .18(rage as an).15 F(inte)117 442.2 Q(ger)-.15 E 5(.T)-.55 G(here are se)
-157.68 442.2 Q -.15(ve)-.25 G(ral v).15 E
-(ersions included on compilation \215ags as described abo)-.15 E -.15(ve)-.15 G
-(.).15 E F0 2.5(6.3.4. New)102 466.2 R(Database Map Classes)2.5 E F2(Ne)142
-482.4 Q 2.875(wk)-.25 G .675 -.15(ey m)168.405 482.4 T .375(aps can be added b\
-y creating a class initialization function and a lookup func-).15 F 2.5
-(tion. These)117 494.4 R(are then added to the routine)2.5 E F1(setupmaps.)2.5
-E F2(The initialization function is called as)142 510.6 Q F1(xxx)157 526.8 Q F2
-(_map_init\(MAP *map, char *mapname, char *ar)A(gs\))-.18 E(The)117 543 Q F1
-(map)2.555 E F2 .055(is an internal data structure.)2.555 F(The)5.055 E F1
-(mapname)2.555 E F2 .054(is the name of the map \(used for error mes-)2.554 F
-2.819(sages\). The)117 555 R F1(ar)2.819 E(gs)-.37 E F2 .32(is a pointer to th\
-e rest of the con\214guration \214le line; \215ags and \214lenames can be)2.819
-F -.15(ex)117 567 S .675(tracted from this line.).15 F .675
-(The initialization function must return)5.675 F F3(TR)3.175 E(UE)-.36 E F2
-.674(if it successfully opened)3.174 F(the map,)117 579 Q F3 -.666(FA)2.5 G
-(LSE).666 E F2(otherwise.)2.5 E(The lookup function is called as)142 595.2 Q F1
-(xxx)157 611.4 Q F2(_map_lookup\(MAP *map, char b)A(uf[], int b)-.2 E
-(ufsize, char **a)-.2 E 1.3 -.65(v, i)-.2 H(nt *statp\)).65 E(The)117 627.6 Q
-F1(map)3.475 E F2 .975(de\214nes the map internally)3.475 F 5.975(.T)-.65 G
-.975(he parameters)277.18 627.6 R F1 -.2(bu)3.475 G(f).2 E F2(and)3.475 E F1
--.2(bu)3.475 G(fsize).2 E F2(ha)3.476 E 1.276 -.15(ve t)-.2 H .976(he input k)
-.15 F -.15(ey)-.1 G 5.976(.T)-.5 G(his)492.33 627.6 Q .043
-(may be \(and often is\) used destructi)117 639.6 R -.15(ve)-.25 G(ly).15 E
-5.043(.T)-.65 G(he)289.831 639.6 Q F1(av)2.543 E F2 .043(is a list of ar)2.543
-F .042(guments passed in from the re)-.18 F(write)-.25 E 3.654(line. The)117
-651.6 R 1.154(lookup function should return a pointer to the ne)3.654 F 3.655
-(wv)-.25 G 3.655(alue. IF)378.335 651.6 R 1.155(the map lookup f)3.655 F(ails,)
--.1 E F1(*statp)117 663.6 Q F2 1.272(should be set to an e)3.772 F 1.272
-(xit status code; in particular)-.15 F 3.772(,i)-.4 G 3.771(ts)357.652 663.6 S
-1.271(hould be set to)368.093 663.6 R F3(EX_TEMPF)3.771 E(AIL)-.666 E F2(if)
-3.771 E(reco)117 675.6 Q -.15(ve)-.15 G(ry is to be attempted by the higher le)
-.15 E -.15(ve)-.25 G 2.5(lc).15 G(ode.)308.76 675.6 Q EP
-%%Page: 58 53
-%%BeginPageSetup
-BP
-%%EndPageSetup
-/F0 10/Times-Bold@0 SF 193.36(SMM:08-58 Sendmail)72 60 R
-(Installation and Operation Guide)2.5 E 2.5(6.3.5. Queueing)102 96 R(Function)
-2.5 E/F1 10/Times-Roman@0 SF .782(The routine)142 112.2 R/F2 10/Times-Italic@0
-SF(shouldqueue)3.282 E F1 .783
-(is called to decide if a message should be queued or processed)3.283 F
-(immediately)117 124.2 Q 6.619(.T)-.65 G 1.618
-(ypically this compares the message priority to the current load a)180.779
-124.2 R -.15(ve)-.2 G 4.118(rage. The).15 F(def)117 136.2 Q
-(ault de\214nition is:)-.1 E(bool)157 152.4 Q(shouldqueue\(pri, ctime\))157
-164.4 Q(long pri;)175 176.4 Q(time_t ctime;)175 188.4 Q({)157 200.4 Q
-(if \(CurrentLA < QueueLA\))175 212.4 Q(return \(F)193 224.4 Q(ALSE\);)-.74 E
-(return \(pri > \(QueueF)175 236.4 Q
-(actor / \(CurrentLA \255 QueueLA + 1\)\)\);)-.15 E(})157 248.4 Q 2.062
-(If the current load a)117 264.6 R -.15(ve)-.2 G 2.062(rage \(global v).15 F
-(ariable)-.25 E F2(Curr)4.562 E(entLA)-.37 E F1 4.562(,w)C 2.062
-(hich is set before this function is)361.636 264.6 R 1.058
-(called\) is less than the lo)117 276.6 R 3.558(wt)-.25 G 1.058
-(hreshold load a)234.198 276.6 R -.15(ve)-.2 G 1.058(rage \(option).15 F F0(x)
-3.557 E F1 3.557(,v)C(ariable)375.526 276.6 Q F2(QueueLA)3.557 E F1(\),)A F2
-(shouldqueue)3.557 E F1(returns)117 288.6 Q/F3 9/Times-Roman@0 SF -.666(FA)
-2.586 G(LSE).666 E F1 .086(immediately \(that is, it should)2.586 F F2(not)
-2.586 E F1 2.586(queue\). If)2.586 F .086(the current load a)2.586 F -.15(ve)
--.2 G .087(rage e).15 F .087(xceeds the)-.15 F .588(high threshold load a)117
-300.6 R -.15(ve)-.2 G .588(rage \(option).15 F F0(X)3.087 E F1 3.087(,v)C
-(ariable)281.846 300.6 Q F2(RefuseLA)3.087 E F1(\),)A F2(shouldqueue)3.087 E F1
-(returns)3.087 E F3(TR)3.087 E(UE)-.36 E F1(immedi-)3.087 E(ately)117 312.6 Q
-7.125(.O)-.65 G 2.125
-(therwise, it computes the function based on the message priority)152.635 312.6
-R 4.626(,t)-.65 G 2.126(he queue f)438.208 312.6 R(actor)-.1 E(\(option)117
-324.6 Q F0(q)2.5 E F1 2.5(,g)C(lobal v)163.95 324.6 Q(ariable)-.25 E F2(QueueF)
-2.5 E(actor)-.75 E F1(\), and the current and threshold load a)A -.15(ve)-.2 G
-(rages.).15 E 1.067(An implementation wishing to tak)142 340.8 R 3.567(et)-.1 G
-1.066(he actual age of the message into account can also)293.625 340.8 R 1.41
-(use the)117 352.8 R F2(ctime)3.91 E F1(parameter)3.91 E 3.91(,w)-.4 G 1.41
-(hich is the time that the message w)229.15 352.8 R 1.41
-(as \214rst submitted to)-.1 F F2(sendmail)3.91 E F1(.)A .929(Note that the)117
-364.8 R F2(pri)3.428 E F1 .928
-(parameter is already weighted by the number of times the message has been)
-3.428 F .395(tried \(although this tends to lo)117 376.8 R .395
-(wer the priority of the message with time\); the e)-.25 F .395
-(xpectation is that)-.15 F(the)117 388.8 Q F2(ctime)2.674 E F1 -.1(wo)2.674 G
-.174(uld be used as an \231escape clause\232 to ensure that messages are e).1 F
--.15(ve)-.25 G .174(ntually processed.).15 F F0 2.5(6.3.6. Refusing)102 412.8 R
-(Incoming SMTP Connections)2.5 E F1 1.148(The function)142 429 R F2 -.37(re)
-3.648 G(fuseconnections).37 E F1(returns)3.648 E F3(TR)3.648 E(UE)-.36 E F1
-1.148(if incoming SMTP connections should be)3.648 F 3.564(refused. The)117 441
-R 1.063(current implementation is based e)3.563 F(xclusi)-.15 E -.15(ve)-.25 G
-1.063(ly on the current load a).15 F -.15(ve)-.2 G 1.063(rage and the).15 F
-(refuse load a)117 453 Q -.15(ve)-.2 G(rage option \(option).15 E F0(X)2.5 E F1
-2.5(,g)C(lobal v)273.56 453 Q(ariable)-.25 E F2(RefuseLA)2.5 E F1(\):)A(bool)
-157 469.2 Q(refuseconnections\(\))157 481.2 Q({)157 493.2 Q
-(return \(CurrentLA >= RefuseLA\);)175 505.2 Q(})157 517.2 Q 2.5(Am)117 533.4 S
-(ore cle)134.5 533.4 Q -.15(ve)-.25 G 2.5(ri).15 G
-(mplementation could look at more system resources.)179.08 533.4 Q F0 2.5
-(6.3.7. Load)102 557.4 R -.6 -1(Av e)2.5 H(rage Computation)1 E F1 .243
-(The routine)142 573.6 R F2 -.1(ge)2.743 G(tla).1 E F1 .243
-(returns the current load a)2.743 F -.15(ve)-.2 G .243
-(rage \(as a rounded inte).15 F 2.743(ger\). The)-.15 F(distrib)2.744 E(ution)
--.2 E 1.157(includes se)117 585.6 R -.15(ve)-.25 G 1.157
-(ral possible implementations.).15 F 1.157(If you are porting to a ne)6.157 F
-3.657(we)-.25 G -.4(nv)418.757 585.6 S 1.157(ironment you may).4 F
-(need to add some ne)117 599.6 Q 2.5(wt)-.25 G(weaks.)210.9 599.6 Q/F4 7
-/Times-Roman@0 SF(23)238.39 595.6 Q F0 2.5(6.4. Con\214guration)87 623.6 R
-(in sr)2.5 E(c/daemon.c)-.18 E F1 .4(The \214le)127 639.8 R F2(sr)2.9 E
-(c/daemon.c)-.37 E F1 .4
-(contains a number of routines that are dependent on the local netw)2.9 F(ork-)
--.1 E(ing en)102 651.8 Q 2.5(vironment. The)-.4 F -.15(ve)2.5 G
-(rsion supplied assumes you ha).15 E .3 -.15(ve B)-.2 H(SD style sock).15 E
-(ets.)-.1 E 2.16(In pre)127 668 R 2.16
-(vious releases, we recommended that you modify the routine)-.25 F F2
-(maphostname)4.66 E F1 2.16(if you)4.66 F -.1(wa)102 680 S 1.918
-(nted to generalize).1 F F0($[)4.418 E F1(...)4.418 E F0($])4.418 E F1 4.418
-(lookups. W)4.418 F 4.418(en)-.8 G 2.418 -.25(ow r)293.904 680 T 1.918
-(ecommend that you create a ne).25 F 4.419(wk)-.25 G -.15(ey)463.631 680 S
-1.919(ed map).15 F .32 LW 76 689.6 72 689.6 DL 80 689.6 76 689.6 DL 84 689.6 80
-689.6 DL 88 689.6 84 689.6 DL 92 689.6 88 689.6 DL 96 689.6 92 689.6 DL 100
-689.6 96 689.6 DL 104 689.6 100 689.6 DL 108 689.6 104 689.6 DL 112 689.6 108
-689.6 DL 116 689.6 112 689.6 DL 120 689.6 116 689.6 DL 124 689.6 120 689.6 DL
-128 689.6 124 689.6 DL 132 689.6 128 689.6 DL 136 689.6 132 689.6 DL 140 689.6
-136 689.6 DL 144 689.6 140 689.6 DL 148 689.6 144 689.6 DL 152 689.6 148 689.6
-DL 156 689.6 152 689.6 DL 160 689.6 156 689.6 DL 164 689.6 160 689.6 DL 168
-689.6 164 689.6 DL 172 689.6 168 689.6 DL 176 689.6 172 689.6 DL 180 689.6 176
-689.6 DL 184 689.6 180 689.6 DL 188 689.6 184 689.6 DL 192 689.6 188 689.6 DL
-196 689.6 192 689.6 DL 200 689.6 196 689.6 DL 204 689.6 200 689.6 DL 208 689.6
-204 689.6 DL 212 689.6 208 689.6 DL 216 689.6 212 689.6 DL/F5 5/Times-Roman@0
-SF(23)93.6 700 Q/F6 8/Times-Roman@0 SF
-(If you do, please send updates to sendmail@CS.Berk)3.2 I(ele)-.08 E -.52(y.)
--.12 G(EDU.).52 E EP
-%%Page: 59 54
-%%BeginPageSetup
-BP
-%%EndPageSetup
-/F0 10/Times-Bold@0 SF(Sendmail Installation and Operation Guide)72 60 Q
-(SMM:08-59)452.9 60 Q/F1 10/Times-Roman@0 SF(instead.)102 96 Q F0 2.5
-(7. CHANGES)72 120 R(IN VERSION 8)2.5 E F1 .196(The follo)112 136.2 R .196
-(wing summarizes changes since the last commonly a)-.25 F -.25(va)-.2 G .196
-(ilable v).25 F .196(ersion of)-.15 F/F2 10/Times-Italic@0 SF(sendmail)2.695 E
-F1(\(5.67\).)2.695 E -.15(Fo)87 148.2 S 2.702(rad).15 G .202
-(etailed list, consult the \214le RELEASE_NO)115.584 148.2 R .203
-(TES in the root directory of the)-.4 F F2(sendmail)2.703 E F1(distrib)2.703 E
-(ution.)-.2 E F0 2.5(7.1. Connection)87 172.2 R(Caching)2.5 E F1 .398
-(Instead of closing SMTP connections immediately)127 188.4 R 2.897(,t)-.65 G
-.397(hose connections are cached for possible)339.005 188.4 R .597(future use.)
-102 200.4 R .597(The adv)5.597 F .597(ent of MX records made this ef)-.15 F
-(fecti)-.25 E .897 -.15(ve f)-.25 H .598
-(or mailing lists; in addition, substantial).15 F(performance impro)102 212.4 Q
--.15(ve)-.15 G(ments can be e).15 E(xpected for queue processing.)-.15 E F0 2.5
-(7.2. MX)87 236.4 R(Piggybacking)2.5 E F1 1.258(If tw)127 252.6 R 3.757(oh)-.1
-G 1.257(osts with dif)161.075 252.6 R 1.257
-(ferent names in a single message happen to ha)-.25 F 1.557 -.15(ve t)-.2 H
-1.257(he same set of MX).15 F .94(hosts, the)102 264.6 R 3.44(yc)-.15 G .94
-(an be sent in the same transaction.)153.45 264.6 R -1.11(Ve)5.94 G .94
-(rsion 8 notices this and tries to batch the mes-)1.11 F(sages.)102 276.6 Q F0
-2.5(7.3. RFC)87 300.6 R(1123 Compliance)2.5 E F1 3.463(An)127 316.8 S .963
-(umber of changes ha)142.683 316.8 R 1.262 -.15(ve b)-.2 H .962
-(een made to mak).15 F(e)-.1 E F2(sendmail)3.462 E F1 .962
-(\231conditionally compliant\232 \(that is,)3.462 F F2(sendmail)102 328.8 Q F1
-.049(satis\214es all of the \231MUST\232 clauses and most b)2.549 F .05
-(ut not all of the \231SHOULD\232 clauses in RFC)-.2 F(1123\).)102 340.8 Q
-(The major areas of change are \(numbers are RFC 1123 section numbers\):)127
-357 Q 15(5.2.7 Response)102 373.2 R(to RCPT command is f)2.5 E(ast.)-.1 E 15
-(5.2.8 Numeric)102 389.4 R(IP addresses are logged in Recei)2.5 E -.15(ve)-.25
-G(d: lines.).15 E 10(5.2.17 Self)102 405.6 R
-(domain literal is properly handled.)2.5 E 15(5.3.2 Better)102 421.8 R
-(control o)2.5 E -.15(ve)-.15 G 2.5(ri).15 G(ndi)220.02 421.8 Q
-(vidual timeouts.)-.25 E 15(5.3.3 Error)102 438 R
-(messages are sent as \231From:<>\232.)2.5 E 15(5.3.3 Error)102 454.2 R
-(messages are ne)2.5 E -.15(ve)-.25 G 2.5(rs).15 G(ent to \231<>\232.)246.28
-454.2 Q 15(5.3.3 Route-addrs)102 470.4 R(are pruned.)2.5 E(The areas in which)
-102 486.6 Q F2(sendmail)2.5 E F1(is not \231unconditionally compliant\232 are:)
-2.5 E(5.2.6)102 502.8 Q F2(Sendmail)139.5 502.8 Q F1(does do header munging.)
-2.5 E(5.2.10)102 519 Q F2(Sendmail)139.5 519 Q F1(doesn')2.5 E 2.5(ta)-.18 G
--.1(lwa)215.42 519 S(ys use the e).1 E(xact SMTP message te)-.15 E
-(xt as listed in RFC 821.)-.15 E(5.3.1.1)102 535.2 Q F2(Sendmail)139.5 535.2 Q
-F1(doesn')2.5 E 2.5(tg)-.18 G
-(uarantee only one connect for each host in queue runs.)215.98 535.2 Q(5.3.1.1)
-102 551.4 Q F2(Sendmail)139.5 551.4 Q F1(doesn')2.5 E 2.5(ta)-.18 G -.1(lwa)
-215.42 551.4 S(ys pro).1 E(vide adequate concurrenc)-.15 E 2.5(yl)-.15 G
-(imits.)366.54 551.4 Q F0 2.5(7.4. Extended)87 575.4 R(SMTP Support)2.5 E F1
--1.11(Ve)127 591.6 S .155(rsion 8 includes both sending and recei)1.11 F .154
-(ving support for Extended SMTP support as de\214ned)-.25 F(by RFC 1651 \(basi\
-c\) and RFC 1653 \(SIZE\); and limited support for RFC 1652 \(BOD)102 603.6 Q
-(Y\).)-.55 E F0 2.5(7.5. Eight-Bit)87 627.6 R(Clean)2.5 E F1(Pre)127 643.8 Q
-1.263(vious v)-.25 F 1.263(ersions of)-.15 F F2(sendmail)3.763 E F1 1.264
-(used the 0200 bit for quoting.)3.763 F 1.264(This v)6.264 F 1.264(ersion a)
--.15 F -.2(vo)-.2 G 1.264(ids that use.).2 F(Ho)102 655.8 Q(we)-.25 E -.15(ve)
--.25 G .8 -.4(r, f).15 H
-(or compatibility with RFC 822, you can set option `7' to get se).4 E -.15(ve)
--.25 G 2.5(nb).15 G(it stripping.)418.86 655.8 Q(Indi)127 672 Q
-(vidual mailers can still produce se)-.25 E -.15(ve)-.25 G 2.5(nb).15 G
-(it output using the `7' mailer \215ag.)300.77 672 Q F0 2.5(7.6. User)87 696 R
-(Database)2.5 E F1 1.073(The user database is an as-yet e)127 712.2 R 1.072
-(xperimental attempt to pro)-.15 F 1.072(vide uni\214ed lar)-.15 F 1.072
-(ge-site name sup-)-.18 F 2.5(port. W)102 724.2 R 2.5(ea)-.8 G
-(re installing it at Berk)145.63 724.2 Q(ele)-.1 E(y; future v)-.15 E
-(ersions may sho)-.15 E 2.5(ws)-.25 G(igni\214cant modi\214cations.)363.57
-724.2 Q EP
-%%Page: 60 55
-%%BeginPageSetup
-BP
-%%EndPageSetup
-/F0 10/Times-Bold@0 SF 193.36(SMM:08-60 Sendmail)72 60 R
-(Installation and Operation Guide)2.5 E 2.5(7.7. Impr)87 96 R -.1(ove)-.18 G
-2.5(dB).1 G(IND Support)158.01 96 Q/F1 10/Times-Roman@0 SF .489
-(The BIND support, particularly for MX records, had a number of anno)127 112.2
-R .49(ying \231features\232 which)-.1 F(ha)102 124.2 Q 1.212 -.15(ve b)-.2 H
-.912(een remo).15 F -.15(ve)-.15 G 3.412(di).15 G 3.412(nt)187.116 124.2 S .912
-(his release.)198.308 124.2 R .912(In particular)5.912 F 3.412(,t)-.4 G .912
-(hese more tightly bind \(pun intended\) the name)307.916 124.2 R(serv)102
-136.2 Q(er to)-.15 E/F2 10/Times-Italic@0 SF(sendmail)2.5 E F1 2.5(,s)C 2.5(ot)
-184.06 136.2 S(hat the name serv)194.34 136.2 Q
-(er resolution rules are incorporated directly into)-.15 E F0(sendmail)2.5 E F1
-(.)A F0 2.5(7.8. K)87 160.2 R(ey)-.25 E(ed Files)-.1 E F1 .365(Generalized k)
-127 176.4 R -.15(ey)-.1 G .365(ed \214les is an idea tak).15 F .365
-(en directly from)-.1 F/F3 9/Times-Roman@0 SF(ID)2.866 E(A)-.36 E F2(sendmail)
-2.866 E F1 .366(\(albeit with a completely)2.866 F(dif)102 188.4 Q
-(ferent implementation\).)-.25 E(The)5 E 2.5(yc)-.15 G(an be useful on lar)
-239.63 188.4 Q(ge sites.)-.18 E -1.11(Ve)127 204.6 S
-(rsion 8 also understands YP)1.11 E(.)-1.11 E F0 2.5(7.9. Multi-W)87 228.6 R
-(ord Classes)-.75 E F1(Classes can no)127 244.8 Q 2.5(wb)-.25 G 2.5(em)200.35
-244.8 S(ultiple w)215.07 244.8 Q 2.5(ords. F)-.1 F(or e)-.15 E(xample,)-.15 E
-(CShofmann.CS.Berk)142 261 Q(ele)-.1 E -.65(y.)-.15 G(EDU).65 E(allo)102 277.2
-Q 2.664(ws you to match the entire string \231hofmann.CS.Berk)-.25 F(ele)-.1 E
--.65(y.)-.15 G 2.663(EDU\232 using the single construct).65 F(\231$=S\232.)102
-289.2 Q F0 2.5(7.10. Deferr)87 313.2 R(ed Macr)-.18 E 2.5(oE)-.18 G(xpansion)
-189.94 313.2 Q F1(The)127 329.4 Q F0($&)2.5 E F2(x)A F1
-(construct has been adopted from)2.5 E F3(ID)2.5 E(A)-.36 E F1(.)A F0 2.5
-(7.11. IDENT)87 353.4 R(Pr)2.5 E(otocol Support)-.18 E F1
-(The IDENT protocol as de\214ned in RFC 1413 is supported.)127 369.6 Q F0 2.5
-(7.12. P)87 393.6 R(arsing Bug Fixes)-.1 E F1 4.03(An)127 409.8 S 1.53
-(umber of small b)143.25 409.8 R 1.53(ugs ha)-.2 F 1.53
-(ving to do with things lik)-.2 F 4.03(eb)-.1 G 1.53
-(ackslash-escaped quotes inside of)364.72 409.8 R(comments ha)102 421.8 Q .3
--.15(ve b)-.2 H(een \214x).15 E(ed.)-.15 E F0 2.5(7.13. Separate)87 445.8 R(En)
-2.5 E -.1(ve)-.4 G(lope/Header Pr).1 E(ocessing)-.18 E F1 .854
-(Since the From: line is passed in separately from the en)127 462 R -.15(ve)-.4
-G .854(lope sender).15 F 3.354(,t)-.4 G .854(hese ha)420.978 462 R 1.154 -.15
-(ve b)-.2 H .854(oth been).15 F .427(made visible; the)102 474 R F0($g)2.927 E
-F1 .427(macro is set to the en)2.927 F -.15(ve)-.4 G .428
-(lope sender during processing of mailer ar).15 F .428(gument v)-.18 F(ec-)-.15
-E(tors and the header sender during processing of headers.)102 486 Q .085
-(It is also possible to specify separate per)127 502.2 R .085(-mailer en)-.2 F
--.15(ve)-.4 G .084(lope and header processing.).15 F(The)5.084 E F0(S)2.584 E
-F1(ender)A(-)-.2 E -.55(RW)102 514.2 S .512(Set and).55 F F0(R)3.012 E F1
-(ecipientR)A .512(Wset ar)-.55 F .512
-(guments for mailers can be speci\214ed as)-.18 F F2(en)3.013 E(velope/header)
--.4 E F1 .513(to gi)3.013 F .813 -.15(ve d)-.25 H(if-).15 E(ferent re)102 526.2
-Q(writings for en)-.25 E -.15(ve)-.4 G(lope v).15 E(ersus header addresses.)
--.15 E F0 2.5(7.14. Owner)87 550.2 R(-List Pr)-.37 E(opagates to En)-.18 E -.1
-(ve)-.4 G(lope).1 E F1 1.001(When an alias has an associated o)127 566.4 R 1
-(wner\255list name, that alias is used to change the en)-.25 F -.15(ve)-.4 G
-(lope).15 E(sender address.)102 578.4 Q(This will cause do)5 E
-(wnstream errors to be returned to that o)-.25 E(wner)-.25 E(.)-.55 E F0 2.5
-(7.15. Dynamic)87 602.4 R(Header Allocation)2.5 E F1(The \214x)127 618.6 Q
-(ed size limit on header lines has been eliminated.)-.15 E F0 2.5(7.16. New)87
-642.6 R(Command Line Flags)2.5 E F1(The)127 658.8 Q F0<ad42>2.5 E F1
-(\215ag has been added to pass in body type information.)2.5 E(The)127 675 Q F0
-<ad70>2.5 E F1(\215ag has been added to pass in protocol information.)2.5 E
-(The)127 691.2 Q F0<ad58>2.6 E F1 .1(\215ag has been added to allo)2.6 F 2.6
-(wl)-.25 G .1(ogging of all protocol in and out of)279.89 691.2 R F2(sendmail)
-2.6 E F1 .1(for deb)2.6 F(ug-)-.2 E(ging.)102 703.2 Q EP
-%%Page: 61 56
-%%BeginPageSetup
-BP
-%%EndPageSetup
-/F0 10/Times-Bold@0 SF(Sendmail Installation and Operation Guide)72 60 Q
-(SMM:08-61)452.9 60 Q/F1 10/Times-Roman@0 SF(The)127 96 Q F0<ad4f>2.5 E F1
-(\215ag simplies setting long-form options.)2.5 E F0 2.5(7.17. Enhanced)87 120
-R(Command Line Flags)2.5 E F1(The)127 136.2 Q F0<ad71>4.007 E F1 1.507(\215ag \
-can limit limit a queue run to speci\214c recipients, senders, or queue ids us\
-ing)4.007 F F0(\255qR)102 148.2 Q/F2 10/Times-Italic@0 SF(substring)A F0 2.5
-<2cad>C(qS)168.41 148.2 Q F2(substring)A F0 2.5(,o)C 2.5<72ad>226.76 148.2 S
-(qI)239.4 148.2 Q F2(substring)A F0 -.18(re)2.5 G(specti).18 E -.1(ve)-.1 G(ly)
-.1 E(.)-.7 E 2.5(7.18. New)87 172.2 R(and Old Con\214guration Line T)2.5 E
-(ypes)-.74 E F1(The)127 188.4 Q F0(K)2.5 E F1
-(line has been added to declare database maps.)2.5 E(The)127 204.6 Q F0(V)2.5 E
-F1(line has been added to declare the con\214guration v)2.5 E(ersion le)-.15 E
--.15(ve)-.25 G(l.).15 E(The)127 220.8 Q F0(M)2.796 E F1 .297(line has a \231D=\
-\232 \214eld that lets you change into a temporary directory while that mailer)
-2.796 F .581(is running.)102 232.8 R .581
-(It also has a \231U=\232 \214eld to allo)5.581 F 3.081(wy)-.25 G .58
-(ou to set the user and group id to be used when run-)289.85 232.8 R
-(ning the mailer)102 244.8 Q(.)-.55 E F0 2.5(7.19. New)87 268.8 R(Options)2.5 E
-F1(Se)127 285 Q -.15(ve)-.25 G .9(ral ne).15 F 3.4(wo)-.25 G .9(ptions ha)184.8
-285 R 1.2 -.15(ve b)-.2 H .9(een added, man).15 F 3.4(yt)-.15 G 3.4(os)314.89
-285 S .9(upport ne)327.18 285 R 3.4(wf)-.25 G .9(eatures, others to allo)379.83
-285 R 3.4(wt)-.25 G(uning)481.22 285 Q 1.187(that w)102 297 R 1.187(as pre)-.1
-F 1.187(viously a)-.25 F -.25(va)-.2 G 1.187(ilable only by recompiling.).25 F
-(The)6.186 E 3.686(ya)-.15 G 1.186(re described in detail in Section 5.1.5.)
-345.514 297 R(Brie\215y)102 309 Q(,)-.65 E 31(bI)102 325.2 S
-(nsist on a minimum number of disk blocks.)141.33 325.2 Q 29.33(CS)102 341.4 S
-(et checkpoint interv)143.56 341.4 Q(al.)-.25 E 29.89(ED)102 357.6 S(ef)145.22
-357.6 Q(ault error message.)-.1 E 28.78(GE)102 373.8 S(nable GECOS matching.)
-144.11 373.8 Q 31(hM)102 390 S(aximum hop count.)146.89 390 Q 33.22(jS)102
-406.2 S(end errors in MIME-encapsulated format.)143.56 406.2 Q 32.11(JF)102
-422.4 S(orw)143.41 422.4 Q(ard \214le path.)-.1 E 31(kC)102 438.6 S
-(onnection cache size)144.67 438.6 Q 28.78(KC)102 454.8 S
-(onnection cache lifetime.)144.67 454.8 Q 33.22(lE)102 471 S .333
-(nable Errors-T)144.11 471 R .333(o: header)-.8 F 5.334(.T)-.55 G .334
-(hese headers violate RFC 1123; this option is included to pro-)252.89 471 R
-(vide back compatibility with old v)138 483 Q(ersions of)-.15 E F2(sendmail)2.5
-E F1(.)A 28.78(OS)102 499.2 S
-(et incoming SMTP daemon options, such as an alternate SMTP port.)143.56 499.2
-Q 31(pP)102 515.4 S(ri)143.56 515.4 Q -.25(va)-.25 G .3 -.15(cy o).25 H
-(ptions.).15 E 29.33(RD)102 531.6 S(on')145.22 531.6 Q 2.5(tp)-.18 G
-(rune route-addrs.)168.65 531.6 Q 28.78(UU)102 547.8 S(ser database spec.)
-145.22 547.8 Q 28.78(VF)102 564 S(allback \231MX\232 host.)143.41 564 Q 28.78
-<7799>102 580.2 S(Best MX\232 handling technique.)142.44 580.2 Q 31(7D)102
-596.4 S 2.5(on)145.22 596.4 S(ot run eight bit clean.)157.72 596.4 Q 31(8E)102
-612.6 S(ight bit data handling mode.)144.11 612.6 Q F0 2.5(7.20. Extended)87
-636.6 R(Options)2.5 E F1(The)127 652.8 Q F0(r)3.764 E F1 1.264
-(\(read timeout\),)3.764 F F0(I)3.764 E F1 1.264(\(use BIND\), and)3.764 F F0
-(T)3.764 E F1 1.264(\(queue timeout\) options ha)3.764 F 1.564 -.15(ve b)-.2 H
-1.264(een e).15 F 1.264(xtended to)-.15 F(pass in more information.)102 664.8 Q
-F0 2.5(7.21. New)87 688.8 R(Mailer Flags)2.5 E F1(Se)127 705 Q -.15(ve)-.25 G
-(ral ne).15 E 2.5(wm)-.25 G(ailer \215ags ha)185.78 705 Q .3 -.15(ve b)-.2 H
-(een added.).15 E EP
-%%Page: 62 57
-%%BeginPageSetup
-BP
-%%EndPageSetup
-/F0 10/Times-Bold@0 SF 193.36(SMM:08-62 Sendmail)72 60 R
-(Installation and Operation Guide)2.5 E/F1 10/Times-Roman@0 SF 31.56(aT)102 96
-S .636(ry to use ESMTP when creating a connection.)143.76 96 R .636
-(If this is not set,)5.636 F/F2 10/Times-Italic@0 SF(sendmail)3.136 E F1 .636
-(will still try if)3.136 F .221(the other end hints that it kno)138 108 R .22
-(ws about ESMTP in its greeting message; this \215ag says to try)-.25 F -2.15
--.25(ev e)138 120 T 2.595(ni).25 G 2.595(fi)161.855 120 S 2.595(td)170.56 120 S
-(oesn')180.935 120 Q 2.595(th)-.18 G 2.595(int. If)212.79 120 R .095
-(the EHLO \(e)2.595 F .095(xtended hello\) command f)-.15 F(ails,)-.1 E F2
-(sendmail)2.596 E F1 -.1(fa)2.596 G .096(lls back to).1 F(old SMTP)138 132 Q(.)
--1.11 E 28.78(AT)102 148.2 S
-(ry the user part of addresses for this mailer as aliases.)143.76 148.2 Q 31
-(bE)102 164.4 S(nsure that there is a blank line at the end of all messages.)
-144.11 164.4 Q 31.56(cS)102 180.6 S .68(trip all comments from addresses; this\
- should only be used as a last resort when dealing)143.56 180.6 R(with crank)
-138 192.6 Q 2.5(ym)-.15 G(ailers.)195.62 192.6 Q 31(gN)102 208.8 S -2.15 -.25
-(ev e)145.22 208.8 T 2.64(ru).25 G .14(se the null sender as the en)169.67
-208.8 R -.15(ve)-.4 G .141(lope sender).15 F 2.641(,e)-.4 G -.15(ve)341.495
-208.8 S 2.641(nw).15 G .141(hen running SMTP)365.646 208.8 R 5.141(.A)-1.11 G
-.141(lthough this)456.349 208.8 R 1.521(violates RFC 1123, it may be necessary\
- when you must deal with some obnoxious old)138 220.8 R(hosts.)138 232.8 Q 31
-(kT)102 249 S(urn of)143.66 249 Q 2.5(ft)-.25 G
-(he loopback check in the HELO protocol; doing this may cause mailer loops.)
-176.18 249 Q 31(oA)102 265.2 S -.1(lwa)145.22 265.2 S
-(ys run the mailer as the recipient of the message.).1 E 28.78(wT)102 281.4 S
-(his user should ha)144.11 281.4 Q .3 -.15(ve a p)-.2 H(asswd \214le entry).15
-E(.)-.65 E 31(5T)102 297.6 S(ry ruleset 5 if no local aliases.)143.76 297.6 Q
-31(7S)102 313.8 S(trip all output to 7 bits.)143.56 313.8 Q 33.22(:C)102 330 S
-(heck for :include: \214les.)144.67 330 Q 34(|C)102 346.2 S
-(heck for |program addresses.)144.67 346.2 Q 33.22(/C)102 362.4 S
-(heck for /\214le addresses.)144.67 362.4 Q 26.79(@C)102 378.6 S
-(heck this user ag)144.67 378.6 Q(ainst the user database.)-.05 E F0 2.5
-(7.22. Long)87 402.6 R(Option Names)2.5 E F1 .856
-(All options can be speci\214ed using long names, and some ne)127 418.8 R 3.356
-(wo)-.25 G .856(ptions can only be speci\214ed)389.476 418.8 R
-(with long names.)102 430.8 Q F0 2.5(7.23. New)87 454.8 R(Pr)2.5 E
-(e-De\214ned Macr)-.18 E(os)-.18 E F1(The follo)127 471 Q
-(wing macros are pre-de\214ned:)-.25 E 23.5($k The)102 487.2 R
-(UUCP node name, nominally from)2.5 E F2(uname)2.5 E F1(\(2\) call.)A 20.72
-($m The)102 503.4 R(domain part of our full hostname.)2.5 E 23.5($_ The)102
-519.6 R(RFC 1413-pro)2.5 E(vided sender address.)-.15 E F0 2.5(7.24. New)87
-543.6 R(LHS T)2.5 E(ok)-.92 E(en)-.1 E F1 -1.11(Ve)127 559.8 S 1.376
-(rsion 8 allo)1.11 F(ws)-.25 E F0($@)3.876 E F1 1.376
-(on the Left Hand Side of an \231R\232 line to match zero tok)3.876 F 3.875
-(ens. This)-.1 F(is)3.875 E(intended to be used to match the null input.)102
-571.8 Q F0 2.5(7.25. Bigger)87 595.8 R(Defaults)2.5 E F1 -1.11(Ve)127 612 S
-1.283(rsion 8 allo)1.11 F 1.284(ws up to 100 rulesets instead of 30.)-.25 F
-1.284(It is recommended that rulesets 0\2559 be)6.284 F(reserv)102 624 Q
-(ed for)-.15 E F2(sendmail)2.5 E F1 1.1 -.55('s d)D
-(edicated use in future releases.).55 E
-(The total number of MX records that can be used has been raised to 20.)127
-640.2 Q .335(The number of queued messages that can be handled at one time has\
- been raised from 600 to)127 656.4 R(1000.)102 668.4 Q F0 2.5(7.26. Differ)87
-692.4 R(ent Default T)-.18 E(uning P)-.92 E(arameters)-.1 E F1 -1.11(Ve)127
-708.6 S .8(rsion 8 has changed the def)1.11 F .8
-(ault parameters for tuning queue costs to mak)-.1 F 3.3(et)-.1 G .8
-(he number of)449.08 708.6 R .712(recipients more important than the size of t\
-he message \(for small messages\).)102 720.6 R .712(This is reasonable if)5.712
-F EP
-%%Page: 63 58
-%%BeginPageSetup
-BP
-%%EndPageSetup
-/F0 10/Times-Bold@0 SF(Sendmail Installation and Operation Guide)72 60 Q
-(SMM:08-63)452.9 60 Q/F1 10/Times-Roman@0 SF
-(you are connected with reasonably f)102 96 Q(ast links.)-.1 E F0 2.5(7.27. A)
-87 120 R(uto-Quoting in Addr)-.5 E(esses)-.18 E F1(Pre)127 136.2 Q(viously)-.25
-E 2.61(,t)-.65 G .111(he \231Full Name <email address>\232 syntax w)176.77
-136.2 R .111(ould generate incorrect protocol output)-.1 F
-(if \231Full Name\232 had special characters such as dot.)102 148.2 Q(This v)5
-E(ersion puts quotes around such names.)-.15 E F0 2.5(7.28. Symbolic)87 172.2 R
-(Names On Err)2.5 E(or Mailer)-.18 E F1(Se)127 188.4 Q -.15(ve)-.25 G
-(ral names ha).15 E .3 -.15(ve b)-.2 H(een b).15 E
-(uilt in to the $@ portion of the $#error mailer)-.2 E(.)-.55 E F0 2.5
-(7.29. SMTP)87 212.4 R(VRFY Doesn't Expand)2.5 E F1(Pre)127 228.6 Q 1.438
-(vious v)-.25 F 1.438(ersions of)-.15 F/F2 10/Times-Italic@0 SF(sendmail)3.938
-E F1 1.438(treated VRFY and EXPN the same.)3.938 F 1.437(In this v)6.437 F
-1.437(ersion, VRFY)-.15 F(doesn')102 240.6 Q 2.5(te)-.18 G
-(xpand aliases or follo)138.05 240.6 Q 2.5(w.)-.25 G(forw)235.84 240.6 Q
-(ard \214les.)-.1 E(EXPN still does.)5 E .681
-(As an optimization, if you run with your def)127 256.8 R .682(ault deli)-.1 F
--.15(ve)-.25 G .682(ry mode being queue-only or deli).15 F -.15(ve)-.25 G -.2
-(r-).15 G 1.582
-(in-background, the RCPT command will also not chase aliases and .forw)102
-268.8 R 1.582(ard \214les.)-.1 F 1.582(It will chase)6.582 F
-(them when it processes the queue.)102 280.8 Q F0 2.5(7.30. [IPC])87 304.8 R
-(Mailers Allo)2.5 E 2.5(wM)-.1 G(ultiple Hosts)210.49 304.8 Q F1 .447
-(When an address resolv)127 321 R .448
-(es to a mailer that has \231[IPC]\232 as its \231P)-.15 F .448
-(ath\232, the $@ part \(host name\))-.15 F .138
-(can be a colon-separated list of hosts instead of a single hostname.)102 333 R
-.137(This asks)5.137 F F2(sendmail)2.637 E F1 .137(to search the)2.637 F .16
-(list for the \214rst entry that is a)102 345 R -.25(va)-.2 G .16(ilable e).25
-F .161(xactly as though it were an MX record.)-.15 F .161
-(The intent is to route)5.161 F .738(internal traf)102 357 R .738
-(\214c through internal netw)-.25 F .738
-(orks without publishing an MX record to the net.)-.1 F .737(MX e)5.737 F
-(xpan-)-.15 E(sion is still done on the indi)102 369 Q(vidual items.)-.25 E F0
-2.5(7.31. Aliases)87 393 R(Extended)2.5 E F1 1.456
-(The implementation has been mer)127 409.2 R 1.457(ged with maps.)-.18 F 1.457
-(Among other things, this supports NIS-)6.457 F(based aliases.)102 421.2 Q F0
-2.5(7.32. P)87 445.2 R(ortability and Security Enhancements)-.2 E F1 2.5(An)127
-461.4 S(umber of internal changes ha)141.72 461.4 Q .3 -.15(ve b)-.2 H
-(een made to enhance portability).15 E(.)-.65 E(Se)127 477.6 Q -.15(ve)-.25 G
-(ral \214x).15 E(es ha)-.15 E .3 -.15(ve b)-.2 H
-(een made to increase the paranoia f).15 E(actor)-.1 E(.)-.55 E F0 2.5
-(7.33. Miscellaneous)87 501.6 R(Changes)2.5 E F2(Sendmail)127 517.8 Q F1
-(writes a)2.5 E F2(/etc/sendmail.pid)2.5 E F1
-(\214le with the current process id of the SMTP daemon.)2.5 E -1 -.8(Tw o)127
-534 T 1.647(people using the same program in their .forw)4.947 F 1.646
-(ard \214le are considered dif)-.1 F 1.646(ferent so that)-.25 F
-(duplicate elimination doesn')102 546 Q 2.5(td)-.18 G(elete one of them.)225.98
-546 Q(The)127 562.2 Q F2(mailstats)3.18 E F1 .681
-(program prints mailer names and gets the location of the)3.18 F F2
-(sendmail.st)3.181 E F1 .681(\214le from)3.181 F F2(/etc/sendmail.cf)102 574.2
-Q F1(.)A(Man)127 590.4 Q 2.5(ym)-.15 G(inor b)160.46 590.4 Q(ugs ha)-.2 E .3
--.15(ve b)-.2 H(een \214x).15 E
-(ed, such as handling of backslashes inside of quotes.)-.15 E 2.5(Ah)127 606.6
-S(ook \(ruleset 5\) has been added to allo)141.72 606.6 Q 2.5(wr)-.25 G -.25
-(ew)304.21 606.6 S(riting of local addresses after aliasing.).25 E F0 2.5(8. A)
-72 630.6 R(CKNO)-.55 E(WLEDGEMENTS)-.5 E F1(I')112 646.8 Q 2.037 -.15(ve w)-.5
-H(ork).05 E 1.737(ed on)-.1 F F2(sendmail)4.237 E F1 1.737(for man)4.237 F
-4.237(yy)-.15 G 1.737(ears, and man)267.502 646.8 R 4.237(ye)-.15 G(mplo)
-339.763 646.8 Q 1.737(yers ha)-.1 F 2.037 -.15(ve b)-.2 H 1.737
-(een remarkably patient).15 F .403(about letting me w)87 658.8 R .403
-(ork on a lar)-.1 F .403(ge project that w)-.18 F .404(as not part of my of)-.1
-F .404(\214cial job)-.25 F 5.404(.T)-.4 G .404(his includes time on the)407.384
-658.8 R .282(INGRES Project at the Uni)87 670.8 R -.15(ve)-.25 G .282
-(rsity of California at Berk).15 F(ele)-.1 E 1.582 -.65(y, a)-.15 H 2.782(tB)
-.65 G .282(ritton Lee, and ag)348.21 670.8 R .281(ain on the Mammoth)-.05 F
-(and T)87 682.8 Q(itan Projects at Berk)-.35 E(ele)-.1 E -.65(y.)-.15 G .453
-(Much of the second w)112 699 R -2.25 -.2(av e)-.1 H .453(of impro)3.153 F -.15
-(ve)-.15 G .453(ments should be credited to Bryan Costales of ICSI.).15 F .454
-(As he)5.454 F .781(passed me drafts of his book on)87 711 R F2(sendmail)3.281
-E F1 3.281(Iw)3.281 G .781(as inspired to start w)274.741 711 R .781
-(orking on things ag)-.1 F 3.281(ain. Bryan)-.05 F -.1(wa)3.281 G(s).1 E
-(also a)87 723 Q -.25(va)-.2 G(ilable to bounce ideas of).25 E 2.5(fo)-.25 G
-(f.)227.38 723 Q EP
-%%Page: 64 59
-%%BeginPageSetup
-BP
-%%EndPageSetup
-/F0 10/Times-Bold@0 SF 193.36(SMM:08-64 Sendmail)72 60 R
-(Installation and Operation Guide)2.5 E/F1 10/Times-Roman@0 SF(Man)112 96 Q
-2.856 -.65(y, m)-.15 H(an).65 E 4.056(yp)-.15 G 1.556(eople contrib)172.212 96
-R 1.556(uted chunks of code and ideas to)-.2 F/F2 10/Times-Italic@0 SF
-(sendmail)4.056 E F1 6.556(.I)C 4.056(th)418.476 96 S 1.557(as pro)430.312 96 R
--.15(ve)-.15 G 4.057(nt).15 G 4.057(ob)477.006 96 S 4.057(ea)491.063 96 S .464
-(group netw)87 108 R .464(ork ef)-.1 F 2.964(fort. V)-.25 F .464
-(ersion 8 in particular w)-1.11 F .463(as a group project.)-.1 F .463
-(The follo)5.463 F .463(wing people made notable)-.25 F(contrib)87 120 Q
-(utions:)-.2 E(John Beck, He)127 136.2 Q(wlett-P)-.25 E(ackard)-.15 E -.25(Ke)
-127 148.2 S(ith Bostic, CSRG, Uni).25 E -.15(ve)-.25 G
-(rsity of California, Berk).15 E(ele)-.1 E(y)-.15 E(Andre)127 160.2 Q 2.5(wC)
--.25 G(heng, Sun Microsystems)168.13 160.2 Q(Michael J. Corrig)127 172.2 Q
-(an, Uni)-.05 E -.15(ve)-.25 G(rsity of California, San Die).15 E(go)-.15 E
-(Bryan Costales, International Computer Science Institute)127 184.2 Q -.15(Pa)
-127 196.2 S -.5(..)132.298 190.2 S 2.5(r\()136.85 196.2 S(Pell\) Emanuelsson)
-146.01 196.2 Q(Craig Ev)127 208.2 Q(erhart, T)-.15 E(ransarc Corporation)-.35 E
--.8(To)127 220.2 S 2.5(mI).8 G -.25(va)150.92 220.2 S 2.5(rH).25 G
-(elbekkmo, Norwe)173.16 220.2 Q(gian School of Economics)-.15 E
-(Allan E. Johannesen, WPI)127 232.2 Q(Jonathan Kamens, OpenV)127 244.2 Q
-(ision T)-.6 E(echnologies, Inc.)-.7 E -.8(Ta)127 256.2 S
-(kahiro Kanbe, Fuji Xerox Information Systems Co., Ltd.).8 E(Brian Kantor)127
-268.2 Q 2.5(,U)-.4 G(ni)191.31 268.2 Q -.15(ve)-.25 G
-(rsity of California, San Die).15 E(go)-.15 E(Murray S. K)127 280.2 Q(uchera)
--.15 E(wy)-.15 E 2.5(,H)-.65 G(ookUp Communication Corp.)227.41 280.2 Q
-(Bruce Lilly)127 292.2 Q 2.5(,S)-.65 G(on)182.74 292.2 Q 2.5(yU)-.15 G(.S.)
-207.31 292.2 Q(Karl London)127 304.2 Q(Motonori Nakamura, Ritsumeikan Uni)127
-316.2 Q -.15(ve)-.25 G(rsity & K).15 E(yoto Uni)-.25 E -.15(ve)-.25 G(rsity).15
-E(John Gardiner Myers, Carne)127 328.2 Q(gie Mellon Uni)-.15 E -.15(ve)-.25 G
-(rsity).15 E(Neil Rick)127 340.2 Q(ert, Northern Illinois Uni)-.1 E -.15(ve)
--.25 G(rsity).15 E(Eric Schnoebelen, Con)127 352.2 Q .3 -.15(vex C)-.4 H
-(omputer Corp.).15 E(Eric W)127 364.2 Q(assenaar)-.8 E 2.5(,N)-.4 G
-(ational Institute for Nuclear and High Ener)200.49 364.2 Q(gy Ph)-.18 E
-(ysics, Amsterdam)-.05 E(Christophe W)127 376.2 Q(olfhugel, P)-.8 E
-(asteur Institute & Herv)-.15 E 2.5(eS)-.15 G(chauer Consultants \(P)330.05
-376.2 Q(aris\))-.15 E 3.219(Ia)87 392.4 S .719(pologize for an)97.989 392.4 R
-.719(yone I ha)-.15 F 1.019 -.15(ve o)-.2 H .719(mitted, misspelled, misattrib)
-.15 F .719(uted, or otherwise missed.)-.2 F .72(At this point, I)5.72 F 1.093
-(suspect that at least a hundred people ha)87 404.4 R 1.393 -.15(ve c)-.2 H
-(ontrib).15 E 1.093(uted code, and man)-.2 F 3.592(ym)-.15 G 1.092(ore ha)
-393.524 404.4 R 1.392 -.15(ve c)-.2 H(ontrib).15 E 1.092(uted ideas,)-.2 F
-1.533(comments, and encouragement.)87 416.4 R(I')6.534 E 1.834 -.15(ve t)-.5 H
-1.534(ried to list them in the RELEASE_NO).15 F 1.534(TES in the distrib)-.4 F
-(ution)-.2 E(directory)87 428.4 Q 5(.I)-.65 G(appreciate their contrib)135.78
-428.4 Q(ution as well.)-.2 E .743(Special thanks are reserv)112 444.6 R .743
-(ed for Michael Corrig)-.15 F .742(an and Christophe W)-.05 F .742
-(olfhugel, who besides being)-.8 F -.1(wo)87 456.6 S 5.714
-(nderful guinea pigs and contrib).1 F 5.714(utors ha)-.2 F 6.015 -.15(ve a)-.2
-H 5.715(lso consented to be added to the `).15 F(`send-)-.74 E(mail@CS.Berk)87
-468.6 Q(ele)-.1 E -.65(y.)-.15 G(EDU').65 E 3.335('l)-.74 G .835
-(ist and, by answering the b)199.005 468.6 R .835
-(ulk of the questions sent to that list, ha)-.2 F 1.134 -.15(ve f)-.2 H(reed)
-.15 E(me up to do other w)87 480.6 Q(ork.)-.1 E EP
-%%Page: 65 60
-%%BeginPageSetup
-BP
-%%EndPageSetup
-/F0 12/Times-Bold@0 SF 3(APPENDIX A)257.172 98.4 R(COMMAND LINE FLA)224.832
-141.6 Q(GS)-.66 E/F1 10/Times-Roman@0 SF(Ar)97 201 Q
-(guments must be presented with \215ags before addresses.)-.18 E
-(The \215ags are:)5 E<ad62>72 217.2 Q/F2 10/Times-Italic@0 SF(x)A F1
-(Set operation mode to)144 217.2 Q F2(x)2.5 E F1 5(.O)C(peration modes are:)
-253.71 217.2 Q 12.22(mD)184 233.4 S(eli)211.22 233.4 Q -.15(ve)-.25 G 2.5(rm)
-.15 G(ail \(def)243.87 233.4 Q(ault\))-.1 E 16.11(sS)184 245.4 S
-(peak SMTP on input side)209.56 245.4 Q 8.06(a\207 `)184 257.4 R -.8(`A)-.74 G
-(rpanet').8 E 2.5('m)-.74 G(ode \(get en)257.53 257.4 Q -.15(ve)-.4 G
-(lope sender information from header\)).15 E 15(dR)184 269.4 S(un as a daemon)
-210.67 269.4 Q 17.22(tR)184 281.4 S(un in test mode)210.67 281.4 Q 15(vJ)184
-293.4 S(ust v)207.89 293.4 Q(erify addresses, don')-.15 E 2.5(tc)-.18 G
-(ollect or deli)319.48 293.4 Q -.15(ve)-.25 G(r).15 E 17.22(iI)184 305.4 S
-(nitialize the alias database)207.33 305.4 Q 15(pP)184 317.4 S
-(rint the mail queue)209.56 317.4 Q<ad42>72 337.8 Q F2(type)A F1
-(Indicate body type.)144 337.8 Q<ad43>72 354 Q F2(\214le)A F1 .946(Use a dif)
-144 354 R .946(ferent con\214guration \214le.)-.25 F F2(Sendmail)5.946 E F1
-.946(runs as the in)3.446 F -.2(vo)-.4 G .946(king user \(rather than root\)).2
-F(when this \215ag is speci\214ed.)144 366 Q<ad64>72 382.2 Q F2(le)A(vel)-.15 E
-F1(Set deb)144 382.2 Q(ugging le)-.2 E -.15(ve)-.25 G(l.).15 E<ad66>72 398.4 Q
-F2(addr)2.5 E F1(The sender')144 398.4 Q 2.5(sm)-.55 G(achine address is)205.1
-398.4 Q F2(addr)2.5 E F1(.)A<ad46>72 414.6 Q F2(name)A F1
-(Sets the full name of this user to)144 414.6 Q F2(name)2.5 E F1(.)A<ad68>72
-430.8 Q F2(cnt)2.5 E F1 .726(Sets the \231hop count\232 to)144 430.8 R F2(cnt)
-3.226 E F1 5.725(.T)C .725
-(his represents the number of times this message has been)269.455 430.8 R .02
-(processed by)144 442.8 R F2(sendmail)2.52 E F1 .02(\(to the e)2.52 F .02
-(xtent that it is supported by the underlying netw)-.15 F(orks\).)-.1 E F2(Cnt)
-5.02 E F1 1.521
-(is incremented during processing, and if it reaches MAXHOP \(currently 30\))
-144 454.8 R F2(sendmail)4.02 E F1(thro)144 466.8 Q(ws a)-.25 E -.1(wa)-.15 G
-2.5(yt).1 G(he message with an error)199.6 466.8 Q(.)-.55 E 58.86(\255n Don')72
-483 R 2.5(td)-.18 G 2.5(oa)174.65 483 S(liasing or forw)186.59 483 Q(arding.)
--.1 E<ad72>72 499.2 Q F2(addr)2.5 E F1(An obsolete form of)144 499.2 Q/F3 10
-/Times-Bold@0 SF<ad66>2.5 E F1(.)A<ad6f>72 515.4 Q F2 1.666(xv)C(alue)-1.666 E
-F1(Set option)144 515.4 Q F2(x)2.5 E F1(to the speci\214ed)2.5 E F2(value)2.5 E
-F1 5(.T)C(hese options are described in Section 5.6.)292.6 515.4 Q<ad4f>72
-531.6 Q F2(option)A F3(=)A F2(value)A F1(Set)6.22 E F2(option)5.173 E F1 2.674
-(to the speci\214ed)5.173 F F2(value)5.174 E F1 2.674
-(\(for long form option names\).)5.174 F 2.674(These options are)7.674 F
-(described in Section 5.6.)144 543.6 Q<ad4d>72 559.8 Q F2 1.666(xv)C 27.204
-(alue Set)-1.666 F(macr)2.5 E 2.5(oxt)-.45 G 2.5(ot)196.04 559.8 S
-(he speci\214ed value)206.32 559.8 Q(.)-.15 E F1<ad70>72 576 Q F2(pr)A(otocol)
--.45 E F1 .401(Set the sending protocol.)144 576 R .401
-(Programs are encouraged to set this.)5.401 F .4(The protocol \214eld can be)
-5.401 F .114(in the form)144 588 R F2(pr)2.614 E(otocol)-.45 E F3(:)A F2(host)A
-F1 .114(to set both the sending protocol and sending host.)2.614 F -.15(Fo)
-5.115 G 2.615(re).15 G(xample,)472.06 588 Q 2.147(\231\255pUUCP:uunet\232 sets\
- the sending protocol to UUCP and the sending host to uunet.)144 600 R .973
-(\(Some e)144 612 R .974
-(xisting programs use \255oM to set the r and s macros; this is equi)-.15 F
--.25(va)-.25 G .974(lent to using).25 F(\255p.\))144 624 Q<ad71>72 640.2 Q F2
-(time)A F1 -.35(Tr)144 640.2 S 3.168(yt).35 G 3.167(op)164.038 640.2 S .667
-(rocess the queued up mail.)177.205 640.2 R .667(If the time is gi)5.667 F -.15
-(ve)-.25 G .667(n, a).15 F F2(sendmail)3.167 E F1 .667(will run through the)
-3.167 F(queue at the speci\214ed interv)144 652.2 Q(al to deli)-.25 E -.15(ve)
--.25 G 2.5(rq).15 G(ueued mail; otherwise, it only runs once.)310.82 652.2 Q
-<ad71>72 668.4 Q F2(Xstring)A F1 .312
-(Run the queue once, limiting the jobs to those matching)144 668.4 R F2
-(Xstring)2.813 E F1 5.313(.T)C .313(he k)416.325 668.4 R .613 -.15(ey l)-.1 H
-(etter).15 E F2(X)2.813 E F1 .313(can be)2.813 F F3(I)144 680.4 Q F1 .671
-(to limit based on queue identi\214er)3.171 F(,)-.4 E F3(R)3.171 E F1 .67
-(to limit based on recipient, or)3.171 F F3(S)3.17 E F1 .67(to limit based on)
-3.17 F .32 LW 76 690 72 690 DL 80 690 76 690 DL 84 690 80 690 DL 88 690 84 690
-DL 92 690 88 690 DL 96 690 92 690 DL 100 690 96 690 DL 104 690 100 690 DL 108
-690 104 690 DL 112 690 108 690 DL 116 690 112 690 DL 120 690 116 690 DL 124 690
-120 690 DL 128 690 124 690 DL 132 690 128 690 DL 136 690 132 690 DL 140 690 136
-690 DL 144 690 140 690 DL 148 690 144 690 DL 152 690 148 690 DL 156 690 152 690
-DL 160 690 156 690 DL 164 690 160 690 DL 168 690 164 690 DL 172 690 168 690 DL
-176 690 172 690 DL 180 690 176 690 DL 184 690 180 690 DL 188 690 184 690 DL 192
-690 188 690 DL 196 690 192 690 DL 200 690 196 690 DL 204 690 200 690 DL 208 690
-204 690 DL 212 690 208 690 DL 216 690 212 690 DL/F4 8/Times-Roman@0 SF
-(\207Deprecated.)93.6 702 Q F3(Sendmail Installation and Operation Guide)72 756
-Q(SMM:08-65)452.9 756 Q EP
-%%Page: 66 61
-%%BeginPageSetup
-BP
-%%EndPageSetup
-/F0 10/Times-Bold@0 SF 193.36(SMM:08-66 Sendmail)72 60 R
-(Installation and Operation Guide)2.5 E/F1 10/Times-Roman@0 SF(sender)144 96 Q
-6.053(.A)-.55 G 1.054
-(particular queued job is accepted if one of the corresponding addresses con-)
-188.876 96 R(tains the indicated)144 108 Q/F2 10/Times-Italic@0 SF(string)2.5 E
-F1(.)A 61.08(\255t Read)72 124.2 R .752(the header for \231T)3.252 F .752
-(o:\232, \231Cc:\232, and \231Bcc:\232 lines, and send to e)-.8 F -.15(ve)-.25
-G .752(ryone listed in those).15 F 2.539(lists. The)144 136.2 R .039
-(\231Bcc:\232 line will be deleted before sending.)2.539 F(An)5.039 E 2.539(ya)
--.15 G .04(ddresses in the ar)385.31 136.2 R .04(gument v)-.18 F(ec-)-.15 E
-(tor will be deleted from the send list.)144 148.2 Q<ad58>72 164.4 Q F2(lo)3.18
-E(g\214le)-.1 E F1 .68(Log all traf)144.68 164.4 R .68(\214c in and out of)-.25
-F F2(sendmail)3.179 E F1 .679(in the indicated)3.179 F F2(lo)3.179 E(g\214le)
--.1 E F1 .679(for deb)3.179 F .679(ugging mailer prob-)-.2 F 2.5(lems. This)144
-176.4 R(produces a lot of data v)2.5 E
-(ery quickly and should be used sparingly)-.15 E(.)-.65 E .637
-(There are a number of options that may be speci\214ed as primiti)97 192.6 R
-.938 -.15(ve \215)-.25 H 3.138(ags. These).15 F .638(are the e, i, m, and v)
-3.138 F 2.5(options. Also,)72 204.6 R(the f option may be speci\214ed as the)
-2.5 E F0<ad73>2.5 E F1(\215ag.)2.5 E EP
-%%Page: 67 62
-%%BeginPageSetup
-BP
-%%EndPageSetup
-/F0 12/Times-Bold@0 SF 3(APPENDIX B)250.002 98.4 R -.12(QU)220.29 141.6 S
-(EUE FILE FORMA).12 E(TS)-1.14 E/F1 10/Times-Roman@0 SF .292
-(This appendix describes the format of the queue \214les.)97 201 R .292
-(These \214les li)5.292 F .592 -.15(ve i)-.25 H 2.792(nt).15 G .291
-(he directory de\214ned by the)395.636 201 R/F2 10/Times-Bold@0 SF(Q)72 213 Q
-F1(option in the)2.5 E/F3 10/Times-Italic@0 SF(sendmail.cf)2.5 E F1
-(\214le, usually)2.5 E F3(/var/spool/mqueue)2.5 E F1(or)2.5 E F3
-(/usr/spool/mqueue)2.5 E F1(.)A .229(All queue \214les ha)97 229.2 R .529 -.15
-(ve t)-.2 H .229(he name).15 F F3(x)2.729 E F2(f)1.666 E F3(AAA99999)A F1
-(where)2.73 E F3(AAA99999)2.73 E F1 .23(is the)2.73 F F3(id)2.73 E F1 .23
-(for this message and the)2.73 F F3(x)2.73 E F1 .23(is a)2.73 F 3.601
-(type. The)72 241.2 R 1.101
-(\214rst letter of the id encodes the hour of the day that the message w)3.601
-F 1.101(as recei)-.1 F -.15(ve)-.25 G 3.601(db).15 G 3.601(yt)451.798 241.2 S
-1.101(he system)463.179 241.2 R .551
-(\(with A being the hour between midnight and 1:00AM\).)72 253.2 R .552
-(All \214les with the same id collecti)5.552 F -.15(ve)-.25 G .552
-(ly de\214ne one).15 F(message.)72 265.2 Q(The types are:)97 281.4 Q 31(dT)72
-297.6 S(he data \214le.)114.11 297.6 Q(The message body \(e)5 E
-(xcluding the header\) is k)-.15 E(ept in this \214le.)-.1 E 31(qT)72 313.8 S
-(he queue control \214le.)114.11 313.8 Q
-(This \214le contains the information necessary to process the job)5 E(.)-.4 E
-33.22(tA)72 330 S .345(temporary \214le.)118.065 330 R .344
-(These are an image of the)5.345 F F2(qf)2.844 E F1 .344
-(\214le when it is being reb)2.844 F 2.844(uilt. It)-.2 F .344
-(should be renamed)2.844 F(to a)108 342 Q F2(qf)2.5 E F1(\214le v)2.5 E
-(ery quickly)-.15 E(.)-.65 E 31(xA)72 358.2 S .566(transcript \214le, e)118.286
-358.2 R .567(xisting during the life of a session sho)-.15 F .567(wing e)-.25 F
--.15(ve)-.25 G .567(rything that happens during that).15 F(session.)108 370.2 Q
-(The)97 386.4 Q F2(qf)3.334 E F1 .834
-(\214le is structured as a series of lines each be)3.334 F .833
-(ginning with a code letter)-.15 F 5.833(.T)-.55 G .833(he lines are as fol-)
-427.358 386.4 R(lo)72 398.4 Q(ws:)-.25 E 28.78(VT)72 414.6 S .819(he v)114.11
-414.6 R .819(ersion number of the queue \214le format, used to allo)-.15 F 3.32
-(wn)-.25 G -.25(ew)359.35 414.6 S F3(sendmail)3.57 E F1 .82
-(binaries to read queue)3.32 F .004(\214les created by older v)108 426.6 R
-2.504(ersions. Def)-.15 F .004(aults to v)-.1 F .004(ersion zero.)-.15 F .004
-(Must be the \214rst line of the \214le if present.)5.004 F 28.78(HA)72 442.8 S
-.329(header de\214nition.)118.049 442.8 R .329(There may be an)5.329 F 2.829
-(yn)-.15 G .329(umber of these lines.)274.283 442.8 R .33
-(The order is important: the)5.33 F 2.83(yr)-.15 G(epre-)483.46 442.8 Q .046
-(sent the order in the \214nal message.)108 454.8 R .046
-(These use the same syntax as header de\214nitions in the con\214gu-)5.046 F
-(ration \214le.)108 466.8 Q 29.33(CT)72 483 S .575(he controlling address.)
-114.11 483 R .575(The syntax is \231localuser:aliasname\232.)5.575 F .575
-(Recipient addresses follo)5.575 F .575(wing this)-.25 F 2.814
-(line will be \215agged so that deli)108 495 R -.15(ve)-.25 G 2.814
-(ries will be run as the).15 F F3(localuser)5.314 E F1 2.814
-(\(a user name from the)5.314 F .561(/etc/passwd \214le\);)108 507 R F3
-(aliasname)3.061 E F1 .561(is the name of the alias that e)3.061 F .562
-(xpanded to this address \(used for print-)-.15 F(ing messages\).)108 519 Q
-28.78(QT)72 535.2 S .798(he `)114.11 535.2 R .798(`original recipient')-.74 F
-.798(', speci\214ed by the ORCPT= \214eld in an ESMTP transaction.)-.74 F .797
-(Used e)5.797 F(xclu-)-.15 E(si)108 547.2 Q -.15(ve)-.25 G(ly for Deli).15 E
--.15(ve)-.25 G(ry Status Noti\214cations.).15 E
-(It applies only to the immediately follo)5 E(wing `R' line.)-.25 E 29.33(RA)72
-563.4 S .705(recipient address.)118.425 563.4 R .705
-(This will normally be completely aliased, b)5.705 F .705
-(ut is actually realiased when the)-.2 F .493(job is processed.)108 575.4 R
-.492(There will be one line for each recipient.)5.493 F -1.11(Ve)5.492 G .492
-(rsion 1 qf \214les also include a lead-)1.11 F .689(ing colon-terminated list\
- of \215ags, which can be `S' to return a message on successful \214nal deli)
-108 587.4 R(v-)-.25 E(ery)108 599.4 Q 3.328(,`)-.65 G .828
-(F' to return a message on f)129.278 599.4 R .828
-(ailure, `D' to return a message if the message is delayed, `B' to)-.1 F .94
-(indicate that the body should be returned, `N' to suppress returning the body)
-108 611.4 R 3.441(,a)-.65 G .941(nd `P' to declare)434.807 611.4 R(this as a `)
-108 623.4 Q(`primary')-.74 E 2.5('\()-.74 G
-(command line or SMTP-session\) address.)192.05 623.4 Q 30.44(ST)72 639.6 S
-(he sender address.)114.11 639.6 Q(There may only be one of these lines.)5 E
-29.89(TT)72 655.8 S(he job creation time.)114.11 655.8 Q
-(This is used to compute when to time out the job)5 E(.)-.4 E 30.44(PT)72 672 S
-.114(he current message priority)114.11 672 R 5.114(.T)-.65 G .113
-(his is used to order the queue.)236.666 672 R .113(Higher numbers mean lo)
-5.113 F .113(wer priori-)-.25 F 3.676(ties. The)108 684 R 1.176
-(priority changes as the message sits in the queue.)3.676 F 1.177
-(The initial priority depends on the)6.176 F
-(message class and the size of the message.)108 696 Q 27.11(MA)72 712.2 S 2.704
-(message. This)117.924 712.2 R .204(line is printed by the)2.704 F F3(mailq)
-2.704 E F1 .203(command, and is generally used to store status infor)2.704 F(-)
--.2 E 2.5(mation. It)108 724.2 R(can contain an)2.5 E 2.5(yt)-.15 G -.15(ex)
-219.78 724.2 S(t.).15 E F2(Sendmail Installation and Operation Guide)72 756 Q
-(SMM:08-67)452.9 756 Q EP
-%%Page: 68 63
-%%BeginPageSetup
-BP
-%%EndPageSetup
-/F0 10/Times-Bold@0 SF 193.36(SMM:08-68 Sendmail)72 60 R
-(Installation and Operation Guide)2.5 E/F1 10/Times-Roman@0 SF 30.44(FF)72 96 S
-.043(lag bits, represented as one letter per \215ag.)113.56 96 R .043
-(De\214ned \215ag bits are)5.043 F F0(r)2.543 E F1 .044
-(indicating that this is a response)2.544 F .143(message and)108 108 R F0(w)
-2.643 E F1 .143(indicating that a w)2.643 F .142
-(arning message has been sent announcing that the mail has been)-.1 F(delayed.)
-108 120 Q 28.78(NT)72 136.2 S(he total number of deli)114.11 136.2 Q -.15(ve)
--.25 G(ry attempts.).15 E 28.78(KT)72 152.4 S
-(he time \(as seconds since January 1, 1970\) of the last deli)114.11 152.4 Q
--.15(ve)-.25 G(ry attempt.).15 E 32.67(IT)72 168.6 S .724
-(he i-number of the data \214le; this can be used to reco)114.11 168.6 R -.15
-(ve)-.15 G 3.225(ry).15 G .725(our mail queue after a disastrous disk)350.23
-168.6 R(crash.)108 180.6 Q 31($A)72 196.8 S .83(macro de\214nition.)118.55
-196.8 R .83(The v)5.83 F .829
-(alues of certain macros \(as of this writing, only)-.25 F F0($r)3.329 E F1
-(and)3.329 E F0($s)3.329 E F1 3.329(\)a)C .829(re passed)466.241 196.8 R
-(through to the queue run phase.)108 208.8 Q 29.33(BT)72 225 S .924
-(he body type.)114.11 225 R .925(The remainder of the line is a te)5.924 F .925
-(xt string de\214ning the body type.)-.15 F .925(If this \214eld is)5.925 F
-.009(missing, the body type is assumed to be \231unde\214ned\232 and no specia\
-l processing is attempted.)108 237 R(Le)5.008 E -.05(ga)-.15 G(l).05 E -.25(va)
-108 249 S(lues are \2317BIT\232 and \2318BITMIME\232.).25 E 28.78(OT)72 265.2 S
-(he original MTS v)114.11 265.2 Q(alue \(from the ESMTP transaction\).)-.25 E
--.15(Fo)5 G 2.5(rD).15 G(eli)359.52 265.2 Q -.15(ve)-.25 G 2.5(rS).15 G
-(tatus Noti\214cations only)389.95 265.2 Q(.)-.65 E 29.89(ZT)72 281.4 S
-(he original en)114.11 281.4 Q -.15(ve)-.4 G
-(lope id \(from the ESMTP transaction\).).15 E -.15(Fo)5 G 2.5(rD).15 G(eli)
-360.88 281.4 Q -.15(ve)-.25 G 2.5(rS).15 G(tatus Noti\214cations only)391.31
-281.4 Q(.)-.65 E 4.072(As an e)97 297.6 R 4.072(xample, the follo)-.15 F 4.073
-(wing is a queue \214le sent to \231eric@mammoth.Berk)-.25 F(ele)-.1 E -.65(y.)
--.15 G 4.073(EDU\232 and).65 F(\231bostic@ok)72 311.6 Q(eef)-.1 E(fe.CS.Berk)
--.25 E(ele)-.1 E -.65(y.)-.15 G(EDU\232).65 E/F2 7/Times-Roman@0 SF(1)219.09
-307.6 Q F1(:)222.59 311.6 Q(P835771)112 327.8 Q(T404261372)112 339.8 Q(Seric)
-112 351.8 Q(Ceric:sendmail@v)112 363.8 Q(angogh.CS.Berk)-.25 E(ele)-.1 E -.65
-(y.)-.15 G(EDU).65 E(Reric@mammoth.Berk)112 375.8 Q(ele)-.1 E -.65(y.)-.15 G
-(EDU).65 E(Rbostic@ok)112 387.8 Q(eef)-.1 E(fe.CS.Berk)-.25 E(ele)-.1 E -.65
-(y.)-.15 G(EDU).65 E(H?P?return-path: <o)112 399.8 Q(wner)-.25 E(-sendmail@v)
--.2 E(angogh.CS.Berk)-.25 E(ele)-.1 E -.65(y.)-.15 G(EDU>).65 E(Hrecei)112
-411.8 Q -.15(ve)-.25 G(d: by v).15 E(angogh.CS.Berk)-.25 E(ele)-.1 E -.65(y.)
--.15 G(EDU \(5.108/2.7\) id AAA06703;).65 E(Fri, 17 Jul 92 00:28:55 -0700)132
-423.8 Q(Hrecei)112 435.8 Q -.15(ve)-.25 G(d: from mail.CS.Berk).15 E(ele)-.1 E
--.65(y.)-.15 G(EDU by v).65 E(angogh.CS.Berk)-.25 E(ele)-.1 E -.65(y.)-.15 G
-(EDU \(5.108/2.7\)).65 E(id AAA06698; Fri, 17 Jul 92 00:28:54 -0700)132 447.8 Q
-(Hrecei)112 459.8 Q -.15(ve)-.25 G(d: from [128.32.31.21] by mail.CS.Berk).15 E
-(ele)-.1 E -.65(y.)-.15 G(EDU \(5.96/2.5\)).65 E
-(id AA22777; Fri, 17 Jul 92 03:29:14 -0400)132 471.8 Q(Hrecei)112 483.8 Q -.15
-(ve)-.25 G(d: by foo.bar).15 E(.baz.de \(5.57/Ultrix3.0-C\))-.55 E
-(id AA22757; Fri, 17 Jul 92 09:31:25 GMT)132 495.8 Q(H?F?from: eric@foo.bar)112
-507.8 Q(.baz.de \(Eric Allman\))-.55 E(H?x?full-name: Eric Allman)112 519.8 Q
-(Hmessage-id: <9207170931.AA22757@foo.bar)112 531.8 Q(.baz.de>)-.55 E(HT)112
-543.8 Q(o: sendmail@v)-.8 E(angogh.CS.Berk)-.25 E(ele)-.1 E -.65(y.)-.15 G(EDU)
-.65 E(Hsubject: this is an e)112 555.8 Q(xample message)-.15 E .657(This sho)72
-572 R .658(ws the person who sent the message, the submission time \(in second\
-s since January 1, 1970\), the)-.25 F(message priority)72 584 Q 2.5(,t)-.65 G
-(he message class, the recipients, and the headers for the message.)145.51 584
-Q .32 LW 76 669.2 72 669.2 DL 80 669.2 76 669.2 DL 84 669.2 80 669.2 DL 88
-669.2 84 669.2 DL 92 669.2 88 669.2 DL 96 669.2 92 669.2 DL 100 669.2 96 669.2
-DL 104 669.2 100 669.2 DL 108 669.2 104 669.2 DL 112 669.2 108 669.2 DL 116
-669.2 112 669.2 DL 120 669.2 116 669.2 DL 124 669.2 120 669.2 DL 128 669.2 124
-669.2 DL 132 669.2 128 669.2 DL 136 669.2 132 669.2 DL 140 669.2 136 669.2 DL
-144 669.2 140 669.2 DL 148 669.2 144 669.2 DL 152 669.2 148 669.2 DL 156 669.2
-152 669.2 DL 160 669.2 156 669.2 DL 164 669.2 160 669.2 DL 168 669.2 164 669.2
-DL 172 669.2 168 669.2 DL 176 669.2 172 669.2 DL 180 669.2 176 669.2 DL 184
-669.2 180 669.2 DL 188 669.2 184 669.2 DL 192 669.2 188 669.2 DL 196 669.2 192
-669.2 DL 200 669.2 196 669.2 DL 204 669.2 200 669.2 DL 208 669.2 204 669.2 DL
-212 669.2 208 669.2 DL 216 669.2 212 669.2 DL/F3 5/Times-Roman@0 SF(1)93.6
-679.6 Q/F4 8/Times-Roman@0 SF .719(This e)3.2 J .719(xample is contri)-.12 F
--.12(ve)-.2 G 2.719(da).12 G .719(nd probably inaccurate for your en)186.968
-682.8 R 2.719(vironment. Glance)-.32 F -.12(ove)2.718 G 2.718(ri).12 G 2.718
-(tt)384.998 682.8 S 2.718(og)392.164 682.8 S .718
-(et an idea; nothing can replace)402.882 682.8 R(looking at what your o)72
-692.4 Q(wn system generates.)-.2 E EP
-%%Page: 69 64
-%%BeginPageSetup
-BP
-%%EndPageSetup
-/F0 12/Times-Bold@0 SF 3(APPENDIX C)249.672 98.4 R(SUMMAR)198.282 141.6 Q 3(YO)
--.42 G 3(FS)274.182 141.6 S(UPPOR)291.186 141.6 Q 3(TF)-.48 G(ILES)350.37 141.6
-Q/F1 10/Times-Roman@0 SF 1.52(This is a summary of the support \214les that)97
-201 R/F2 10/Times-Italic@0 SF(sendmail)4.019 E F1 1.519(creates or generates.)
-4.019 F(Man)6.519 E 4.019(yo)-.15 G 4.019(ft)444.743 201 S 1.519(hese can be)
-454.872 201 R(changed by editing the sendmail.cf \214le; check there to \214nd\
- the actual pathnames.)72 213 Q(/usr/sbin/sendmail)72 229.2 Q(The binary of)144
-241.2 Q F2(sendmail)2.5 E F1(.)A(/usr/bin/ne)72 257.4 Q -.1(wa)-.25 G(liases).1
-E 3.734(Al)144 269.4 S 1.235
-(ink to /usr/sbin/sendmail; causes the alias database to be reb)157.734 269.4 R
-3.735(uilt. Running)-.2 F 1.235(this pro-)3.735 F(gram is completely equi)144
-281.4 Q -.25(va)-.25 G(lent to gi).25 E(ving)-.25 E F2(sendmail)2.5 E F1(the)
-2.5 E/F3 10/Times-Bold@0 SF(\255bi)2.5 E F1(\215ag.)2.5 E 13.38
-(/usr/bin/mailq Prints)72 297.6 R 3.703(al)3.703 G 1.203
-(isting of the mail queue.)181.966 297.6 R 1.202(This program is equi)6.203 F
--.25(va)-.25 G 1.202(lent to using the).25 F F3(\255bp)3.702 E F1 1.202
-(\215ag to)3.702 F F2(sendmail)144 309.6 Q F1(.)A 5.9(/etc/sendmail.cf The)72
-325.8 R(con\214guration \214le, in te)2.5 E(xtual form.)-.15 E
-(/usr/lib/sendmail.hf)72 342 Q(The SMTP help \214le.)144 354 Q 7
-(/etc/sendmail.st A)72 370.2 R(statistics \214le; need not be present.)2.5 E
-.89(/etc/sendmail.pid Created)72 386.4 R .318
-(in daemon mode; it contains the process id of the current SMTP daemon.)2.818 F
-.318(If you)5.318 F .338(use this in scripts; use `)144 398.4 R .338
-(`head \2551')-.74 F 2.838('t)-.74 G 2.838(og)285.786 398.4 S .338
-(et just the \214rst line; later v)298.624 398.4 R .337(ersions of)-.15 F F2
-(sendmail)2.837 E F1(may)2.837 E(add information to subsequent lines.)144 410.4
-Q 25.62(/etc/aliases The)72 426.6 R(te)2.5 E(xtual v)-.15 E
-(ersion of the alias \214le.)-.15 E(/etc/aliases.{pag,dir})72 442.8 Q
-(The alias \214le in)144 454.8 Q F2(dbm)2.5 E F1(\(3\) format.)1.666 E(/v)72
-471 Q(ar/spool/mqueue)-.25 E
-(The directory in which the mail queue and temporary \214les reside.)144 483 Q
-(/v)72 499.2 Q(ar/spool/mqueue/qf*)-.25 E
-(Control \(queue\) \214les for messages.)144 511.2 Q(/v)72 527.4 Q
-(ar/spool/mqueue/df*)-.25 E(Data \214les.)144 539.4 Q(/v)72 555.6 Q
-(ar/spool/mqueue/tf*)-.25 E -.7(Te)144 567.6 S(mporary v).7 E
-(ersions of the qf \214les, used during queue \214le reb)-.15 E(uild.)-.2 E(/v)
-72 583.8 Q(ar/spool/mqueue/xf*)-.25 E 2.5(At)144 595.8 S
-(ranscript of the current session.)156.5 595.8 Q F3
-(Sendmail Installation and Operation Guide)72 756 Q(SMM:08-69)452.9 756 Q EP
-%%Page: 2 65
-%%BeginPageSetup
-BP
-%%EndPageSetup
-/F0 10/Times-Bold@0 SF 198.36(SMM:08-2 Sendmail)72 60 R
-(Installation and Operation Guide)2.5 E/F1 10/Times-Roman@0 SF
-(This page intentionally left blank;)256.225 300 Q
-(replace it with a blank sheet for double-sided output.)218.6 312 Q EP
-%%Page: 3 66
-%%BeginPageSetup
-BP
-%%EndPageSetup
-/F0 10/Times-Bold@0 SF(Sendmail Installation and Operation Guide)72 60 Q
-(SMM:08-3)457.9 60 Q/F1 12/Times-Roman@0 SF -1.116(TA)263.226 98.4 S
-(BLE OF CONTENTS)1.116 E/F2 10/Times-Roman@0 SF 2.5(1. B)72 124.8 R(ASIC INST)
--.35 E(ALLA)-.93 E 1.18(TION .................................................\
-..............................................................)-1.11 F(7)499
-124.8 Q 2.5(1.1. Compiling)87 139.2 R .43(Sendmail ...........................\
-..............................................................................\
-.....)2.5 F(7)499 139.2 Q 2.5(1.1.1. T)102 153.6 R(weaking the Mak)-.8 E 1.64(\
-e\214le ......................................................................\
-...........................)-.1 F(7)499 153.6 Q 2.5(1.1.2. Compilation)102 168
-R(and installation)2.5 E 28.5(................................................\
-........................................ 8)4.6 F 2.5(1.2. Con\214guration)87
-182.4 R .99(Files ............................................................\
-....................................................)2.5 F(8)499 182.4 Q 2.5
-(1.3. Details)87 196.8 R(of Installation Files)2.5 E 28.5(....................\
-..............................................................................\
-. 9)4.89 F 2.5(1.3.1. /usr/sbin/sendmail)102 211.2 R 28.5(....................\
-..............................................................................\
-....... 9)2.66 F 2.5(1.3.2. /etc/sendmail.cf)102 225.6 R 28.5(................\
-..............................................................................\
-.............. 9)4.9 F 2.5(1.3.3. /usr/bin/ne)102 240 R -.1(wa)-.25 G 2.19(lia\
-ses ..........................................................................\
-.............................).1 F(10)494 240 Q 2.5(1.3.4. /v)102 254.4 R 1.81
-(ar/spool/mqueue .............................................................\
-...........................................)-.25 F(10)494 254.4 Q 2.5
-(1.3.5. /etc/aliases*)102 268.8 R 23.5(.......................................\
-........................................................................... 10)
-4.62 F 2.5(1.3.6. /etc/rc)102 283.2 R 23.5(...................................\
-..............................................................................\
-........... 10)3.51 F 2.5(1.3.7. /usr/lib/sendmail.hf)102 297.6 R 23.5(.......\
-..............................................................................\
-.................. 11)2.94 F 2.5(1.3.8. /etc/sendmail.st)102 312 R 23.5(......\
-..............................................................................\
-......................... 11)3.5 F 2.5(1.3.9. /usr/bin/mailq)102 326.4 R 23.5(\
-..............................................................................\
-................................. 11)4.88 F 2.5(2. NORMAL)72 340.8 R(OPERA)2.5
-E 1.56(TIONS .................................................................\
-............................................)-1.11 F(11)494 340.8 Q 2.5
-(2.1. The)87 355.2 R(System Log)2.5 E 23.5(...................................\
-..............................................................................\
-... 11)4.89 F 2.5(2.1.1. F)102 369.6 R 2.26(ormat ............................\
-..............................................................................\
-................)-.15 F(11)494 369.6 Q 2.5(2.1.2. Le)102 384 R -.15(ve)-.25 G
-2.24(ls ......................................................................\
-.....................................................).15 F(13)494 384 Q 2.5
-(2.2. Dumping)87 398.4 R .72(State ...........................................\
-............................................................................)
-2.5 F(13)494 398.4 Q 2.5(2.3. The)87 412.8 R(Mail Queue)2.5 E 23.5(...........\
-..............................................................................\
-............................ 13)2.96 F 2.5(2.3.1. Printing)102 427.2 R
-(the queue)2.5 E 23.5(........................................................\
-................................................. 14)2.67 F 2.5(2.3.2. F)102
-441.6 R(orcing the queue)-.15 E 23.5(.........................................\
-................................................................ 14)3.94 F 2.5
-(2.4. The)87 456 R(Service Switch)2.5 E 23.5(.................................\
-..............................................................................\
-. 14)2.68 F 2.5(2.5. The)87 470.4 R(Alias Database)2.5 E 23.5(................\
-..............................................................................\
-.................. 15)2.69 F 2.5(2.5.1. Reb)102 484.8 R
-(uilding the alias database)-.2 E 23.5(.......................................\
-................................................ 16)4.27 F 2.5
-(2.5.2. Potential)102 499.2 R .72(problems ...................................\
-.....................................................................)2.5 F(16)
-494 499.2 Q 2.5(2.5.3. List)102 513.6 R -.25(ow)2.5 G 1.81(ners ..............\
-..............................................................................\
-.......................).25 F(16)494 513.6 Q 2.5(2.6. User)87 528 R
-(Information Database)2.5 E 23.5(.............................................\
-....................................................... 17)2.7 F 2.5(2.7. Per)
-87 542.4 R(-User F)-.2 E(orw)-.15 E(arding \(.forw)-.1 E(ard Files\))-.1 E 23.5
-(.............................................................................\
-...... 17)4.09 F 2.5(2.8. Special)87 556.8 R(Header Lines)2.5 E 23.5(.........\
-..............................................................................\
-...................... 17)2.97 F 2.5(2.8.1. Errors-T)102 571.2 R 2.09(o: .....\
-..............................................................................\
-..................................)-.8 F(17)494 571.2 Q 2.5
-(2.8.2. Apparently-T)102 585.6 R 2.09(o: .....................................\
-........................................................................)-.8 F
-(17)494 585.6 Q 2.5(2.8.3. Precedence)102 600 R 23.5(.........................\
-..............................................................................\
-............. 17)2.97 F 2.5(2.9. IDENT)87 614.4 R(Protocol Support)2.5 E 23.5(\
-..............................................................................\
-......................... 18)2.95 F 2.5(3. ARGUMENTS)72 628.8 R 23.5(.........\
-..............................................................................\
-........................................ 18)3.78 F 2.5(3.1. Queue)87 643.2 R
-(Interv)2.5 E 1.55(al ........................................................\
-...............................................................)-.25 F(18)494
-643.2 Q 2.5(3.2. Daemon)87 657.6 R 1.29(Mode .................................\
-..............................................................................\
-........)2.5 F(18)494 657.6 Q 2.5(3.3. F)87 672 R(orcing the Queue)-.15 E 23.5
-(.............................................................................\
-.................................... 19)4.22 F 2.5(3.4. Deb)87 686.4 R 1.76(ug\
-ging .........................................................................\
-....................................................)-.2 F(19)494 686.4 Q 2.5
-(3.5. Changing)87 700.8 R(the V)2.5 E(alues of Options)-1.11 E 23.5(..........\
-..............................................................................\
-.... 19)3.23 F 2.5(3.6. T)87 715.2 R(rying a Dif)-.35 E
-(ferent Con\214guration File)-.25 E 23.5(.....................................\
-.............................................. 20)4.67 F EP
-%%Page: 4 67
-%%BeginPageSetup
-BP
-%%EndPageSetup
-/F0 10/Times-Bold@0 SF 198.36(SMM:08-4 Sendmail)72 60 R
-(Installation and Operation Guide)2.5 E/F1 10/Times-Roman@0 SF 2.5
-(3.7. Logging)87 96 R -.35(Tr)2.5 G(af).35 E .5(\214c ........................\
-..............................................................................\
-................)-.25 F(20)494 96 Q 2.5(3.8. T)87 110.4 R
-(esting Con\214guration Files)-.7 E 23.5(.....................................\
-.............................................................. 20)4.19 F 2.5
-(4. TUNING)72 124.8 R 23.5(...................................................\
-..............................................................................\
-........ 21)2.68 F 2.5(4.1. T)87 139.2 R 1.07(imeouts ........................\
-..............................................................................\
-..........................)-.35 F(21)494 139.2 Q 2.5(4.1.1. Queue)102 153.6 R
-(interv)2.5 E 2.1(al .........................................................\
-.....................................................)-.25 F(21)494 153.6 Q 2.5
-(4.1.2. Read)102 168 R 1(timeouts ............................................\
-...................................................................)2.5 F(21)
-494 168 Q 2.5(4.1.3. Message)102 182.4 R 1.56(timeouts .......................\
-..............................................................................\
-....)2.5 F(22)494 182.4 Q 2.5(4.2. F)87 196.8 R(orking During Queue Runs)-.15 E
-23.5(.........................................................................\
-........................ 23)4.49 F 2.5(4.3. Queue)87 211.2 R .73(Priorities ..\
-..............................................................................\
-.....................................)2.5 F(23)494 211.2 Q 2.5(4.4. Load)87
-225.6 R .44(Limiting .........................................................\
-...............................................................)2.5 F(24)494
-225.6 Q 2.5(4.5. Deli)87 240 R -.15(ve)-.25 G(ry Mode).15 E 23.5(.............\
-..............................................................................\
-............................ 24)3.08 F 2.5(4.6. Log)87 254.4 R(Le)2.5 E -.15
-(ve)-.25 G 2.52(l.).15 G 23.5(................................................\
-..............................................................................\
- 24)153 254.4 R 2.5(4.7. File)87 268.8 R .72(Modes ...........................\
-..............................................................................\
-....................)2.5 F(25)494 268.8 Q 2.5(4.7.1. T)102 283.2 R 2.5(os)-.8 G
-(uid or not to suid?)146.2 283.2 Q 23.5(......................................\
-........................................................... 25)6.52 F 2.5
-(4.7.2. Should)102 297.6 R(my alias database be writable?)2.5 E 23.5
-(........................................................................ 25)
-5.47 F 2.5(4.8. Connection)87 312 R 1.56(Caching .............................\
-..............................................................................\
-...)2.5 F(25)494 312 Q 2.5(4.9. Name)87 326.4 R(Serv)2.5 E(er Access)-.15 E
-23.5(.........................................................................\
-..................................... 26)2.85 F 2.5(4.10. Mo)87 340.8 R
-(ving the Per)-.15 E(-User F)-.2 E(orw)-.15 E(ard Files)-.1 E 23.5(...........\
-......................................................................... 27)
-3.84 F 2.5(4.11. Free)87 355.2 R 1.85(Space ..................................\
-..............................................................................\
-...........)2.5 F(27)494 355.2 Q 2.5(4.12. Maximum)87 369.6 R(Message Size)2.5
-E 23.5(.......................................................................\
-.............................. 27)4.62 F 2.5(4.13. Pri)87 384 R -.25(va)-.25 G
-.3 -.15(cy F).25 H 1.93(lags .................................................\
-......................................................................).15 F
-(27)494 384 Q 2.5(4.14. Send)87 398.4 R(to Me T)2.5 E 2.08(oo ................\
-..............................................................................\
-.....................)-.8 F(27)494 398.4 Q 2.5(5. THE)72 412.8 R
-(WHOLE SCOOP ON THE CONFIGURA)2.5 E(TION FILE)-1.11 E 23.5
-(........................................................ 28)4.64 F 2.5(5.1. R)
-87 427.2 R(and S \212 Re)2.5 E(writing Rules)-.25 E 23.5(.....................\
-............................................................................. \
-28)4.3 F 2.5(5.1.1. The)102 441.6 R(left hand side)2.5 E 23.5(................\
-..............................................................................\
-........... 28)4.07 F 2.5(5.1.2. The)102 456 R(right hand side)2.5 E 23.5(....\
-..............................................................................\
-..................... 29)3.51 F 2.5(5.1.3. Semantics)102 470.4 R(of re)2.5 E
-(writing rule sets)-.25 E 23.5(...............................................\
-.................................... 30)4.6 F 2.5(5.1.4. IPC)102 484.8 R 1(mai\
-lers .........................................................................\
-..........................................)2.5 F(31)494 484.8 Q 2.5(5.2. D)87
-499.2 R 2.5<8a44>2.5 G(e\214ne Macro)136.44 499.2 Q 23.5(.....................\
-..............................................................................\
-............. 31)3.52 F 2.5(5.3. C)87 513.6 R(and F \212 De\214ne Classes)2.5 E
-23.5(.........................................................................\
-............................ 34)2.67 F 2.5(5.4. M)87 528 R 2.5<8a44>2.5 G
-(e\214ne Mailer)138.11 528 Q 23.5(............................................\
-................................................................... 35)3.79 F
-2.5(5.5. H)87 542.4 R 2.5<8a44>2.5 G(e\214ne Header)136.44 542.4 Q 23.5(......\
-..............................................................................\
-........................... 38)3.25 F 2.5(5.6. O)87 556.8 R 2.5<8a53>2.5 G
-(et Option)134.78 556.8 Q 23.5(...............................................\
-...................................................................... 38)3.22
-F 2.5(5.7. P)87 571.2 R 2.5<8a50>2.5 G(recedence De\214nitions)133.12 571.2 Q
-23.5(.........................................................................\
-......................... 46)2.96 F 2.5(5.8. V)87 585.6 R 2.5<8a43>2.5 G
-(on\214guration V)135.89 585.6 Q(ersion Le)-1.11 E -.15(ve)-.25 G 2.8(l.).15 G
-23.5(.........................................................................\
-............... 46)248 585.6 R 2.5(5.9. K)87 600 R 2.5<8a4b>2.5 G .3 -.15(ey F)
-136.19 600 T(ile Declaration).15 E 23.5(......................................\
-............................................................... 47)2.81 F 2.5
-(5.10. The)87 614.4 R(User Database)2.5 E 23.5(...............................\
-..............................................................................\
-. 50)4.92 F 2.5(5.10.1. Structure)102 628.8 R(of the user database)2.5 E 23.5(\
-..............................................................................\
-....... 50)2.7 F 2.5(5.10.2. User)102 643.2 R(database semantics)2.5 E 23.5(..\
-..............................................................................\
-............. 51)3.25 F 2.5(5.10.3. Creating)102 659.6 R(the database)2.5 E/F2
-7/Times-Roman@0 SF(21)220.59 655.6 Q F1 23.5(.................................\
-.............................................................. 51)230.5 659.6 R
-2.5(6. O)72 674 R(THER CONFIGURA)-.4 E 1.97(TION .............................\
-............................................................................)
--1.11 F(52)494 674 Q 2.5(6.1. P)87 688.4 R(arameters in src/Mak)-.15 E 1.55(e\
-\214le .......................................................................\
-.............................)-.1 F(52)494 688.4 Q 2.5(6.2. P)87 702.8 R
-(arameters in src/conf.h)-.15 E 23.5(.........................................\
-............................................................... 52)4.23 F 2.5
-(6.3. Con\214guration)87 717.2 R(in src/conf.c)2.5 E 23.5(....................\
-..............................................................................\
-.. 55)3.51 F EP
-%%Page: 5 68
-%%BeginPageSetup
-BP
-%%EndPageSetup
-/F0 10/Times-Bold@0 SF(Sendmail Installation and Operation Guide)72 60 Q
-(SMM:08-5)457.9 60 Q/F1 10/Times-Roman@0 SF 2.5(6.3.1. Built-in)102 96 R
-(Header Semantics)2.5 E 23.5(.................................................\
-.......................................... 55)4.9 F 2.5(6.3.2. Restricting)102
-110.4 R(Use of Email)2.5 E 23.5(..............................................\
-................................................ 56)4.34 F 2.5(6.3.3. Load)102
-124.8 R -1.17 -.74(Av e)2.5 H(rage Computation).74 E 23.5(....................\
-...................................................................... 57)2.74
-F 2.5(6.3.4. Ne)102 139.2 R 2.5(wD)-.25 G(atabase Map Classes)157.85 139.2 Q
-23.5(.........................................................................\
-................ 57)4.89 F 2.5(6.3.5. Queueing)102 153.6 R 1.56(Function .....\
-..............................................................................\
-....................)2.5 F(58)494 153.6 Q 2.5(6.3.6. Refusing)102 168 R
-(Incoming SMTP Connections)2.5 E 23.5
-(....................................................................... 58)
-2.94 F 2.5(6.3.7. Load)102 182.4 R -1.17 -.74(Av e)2.5 H(rage Computation).74 E
-23.5(.........................................................................\
-................. 58)2.74 F 2.5(6.4. Con\214guration)87 196.8 R
-(in src/daemon.c)2.5 E 23.5(..................................................\
-............................................ 58)4.62 F 2.5(7. CHANGES)72 211.2
-R(IN VERSION 8)2.5 E 23.5(....................................................\
-...................................................... 59)4.9 F 2.5
-(7.1. Connection)87 225.6 R 1.56(Caching .....................................\
-.........................................................................)2.5 F
-(59)494 225.6 Q 2.5(7.2. MX)87 240 R 2.39(Piggybacking .......................\
-..............................................................................\
-............)2.5 F(59)494 240 Q 2.5(7.3. RFC)87 254.4 R(1123 Compliance)2.5 E
-23.5(.........................................................................\
-................................. 59)3.77 F 2.5(7.4. Extended)87 268.8 R
-(SMTP Support)2.5 E 23.5(.....................................................\
-.................................................. 59)2.94 F 2.5
-(7.5. Eight-Bit)87 283.2 R .44(Clean .........................................\
-.............................................................................)
-2.5 F(59)494 283.2 Q 2.5(7.6. User)87 297.6 R .47(Database ...................\
-..............................................................................\
-.......................)2.5 F(59)494 297.6 Q 2.5(7.7. Impro)87 312 R -.15(ve)
--.15 G 2.5(dB).15 G(IND Support)154.75 312 Q 23.5(............................\
-........................................................................... 60)
-3.81 F 2.5(7.8. K)87 326.4 R -.15(ey)-.25 G(ed Files).15 E 23.5(..............\
-..............................................................................\
-................................ 60)3.35 F 2.5(7.9. Multi-W)87 340.8 R
-(ord Classes)-.8 E 23.5(......................................................\
-......................................................... 60)3.47 F 2.5
-(7.10. Deferred)87 355.2 R(Macro Expansion)2.5 E 23.5(........................\
-......................................................................... 60)
-4.65 F 2.5(7.11. IDENT)87 369.6 R(Protocol Support)2.5 E 23.5(................\
-..............................................................................\
-....... 60)2.95 F 2.5(7.12. P)87 384 R(arsing Bug Fix)-.15 E .46(es ..........\
-..............................................................................\
-........................)-.15 F(60)494 384 Q 2.5(7.13. Separate)87 398.4 R(En)
-2.5 E -.15(ve)-.4 G(lope/Header Processing).15 E 23.5(........................\
-........................................................ 60)4.37 F 2.5
-(7.14. Owner)87 412.8 R(-List Propag)-.2 E(ates to En)-.05 E -.15(ve)-.4 G 1.27
-(lope ........................................................................\
-............).15 F(60)494 412.8 Q 2.5(7.15. Dynamic)87 427.2 R
-(Header Allocation)2.5 E 23.5(................................................\
-................................................ 60)3.25 F 2.5(7.16. Ne)87
-441.6 R 2.5(wC)-.25 G(ommand Line Flags)139.8 441.6 Q 23.5(...................\
-..............................................................................\
-. 60)3.2 F 2.5(7.17. Enhanced)87 456 R(Command Line Flags)2.5 E 23.5(.........\
-..............................................................................\
-.. 61)4.9 F 2.5(7.18. Ne)87 470.4 R 2.5(wa)-.25 G
-(nd Old Con\214guration Line T)137.57 470.4 Q .4(ypes ........................\
-......................................................)-.8 F(61)494 470.4 Q 2.5
-(7.19. Ne)87 484.8 R 2.5(wO)-.25 G .7(ptions .................................\
-..............................................................................\
-.........)140.35 484.8 R(61)494 484.8 Q 2.5(7.20. Extended)87 499.2 R 1.56(Opt\
-ions .........................................................................\
-.......................................)2.5 F(61)494 499.2 Q 2.5(7.21. Ne)87
-513.6 R 2.5(wM)-.25 G(ailer Flags)142.02 513.6 Q 23.5(........................\
-..............................................................................\
-.......... 61)4.04 F 2.5(7.22. Long)87 528 R(Option Names)2.5 E 23.5(.........\
-..............................................................................\
-..................... 62)4.34 F 2.5(7.23. Ne)87 542.4 R 2.5(wP)-.25 G
-(re-De\214ned Macros)138.69 542.4 Q 23.5(.....................................\
-............................................................... 62)4.06 F 2.5
-(7.24. Ne)87 556.8 R 2.5(wL)-.25 G(HS T)139.24 556.8 Q(ok)-.8 E 1.33(en ......\
-..............................................................................\
-..............................)-.1 F(62)494 556.8 Q 2.5(7.25. Bigger)87 571.2 R
-(Def)2.5 E(aults .............................................................\
-.......................................................)-.1 E(62)494 571.2 Q
-2.5(7.26. Dif)87 585.6 R(ferent Def)-.25 E(ault T)-.1 E(uning P)-.45 E 1.99(ar\
-ameters ......................................................................\
-............)-.15 F(62)494 585.6 Q 2.5(7.27. Auto-Quoting)87 600 R
-(in Addresses)2.5 E 23.5(.....................................................\
-............................................ 63)3.51 F 2.5(7.28. Symbolic)87
-614.4 R(Names On Error Mailer)2.5 E 23.5(.....................................\
-................................................. 63)4.91 F 2.5(7.29. SMTP)87
-628.8 R(VRFY Doesn')2.5 E 2.5(tE)-.18 G 1.18(xpand ...........................\
-.................................................................)209.88 628.8
-R(63)494 628.8 Q 2.5(7.30. [IPC])87 643.2 R(Mailers Allo)2.5 E 2.5(wM)-.25 G
-(ultiple Hosts)205.91 643.2 Q 23.5(...........................................\
-........................................ 63)3.75 F 2.5(7.31. Aliases)87 657.6 R
-1.29(Extended ................................................................\
-.................................................)2.5 F(63)494 657.6 Q 2.5
-(7.32. Portability)87 672 R(and Security Enhancements)2.5 E 23.5(.............\
-.................................................................. 63)2.68 F
-2.5(7.33. Miscellaneous)87 686.4 R 1.29(Changes ..............................\
-.........................................................................)2.5 F
-(63)494 686.4 Q 2.5(8. A)72 700.8 R(CKNO)-.4 E .1(WLEDGEMENTS ................\
-..............................................................................\
-..............)-.35 F(63)494 700.8 Q(Appendix A.)72 715.2 Q(COMMAND LINE FLA)5
-E 1.97(GS ....................................................................\
-.....................)-.4 F(65)494 715.2 Q EP
-%%Page: 6 69
-%%BeginPageSetup
-BP
-%%EndPageSetup
-/F0 10/Times-Bold@0 SF 198.36(SMM:08-6 Sendmail)72 60 R
-(Installation and Operation Guide)2.5 E/F1 10/Times-Roman@0 SF(Appendix B.)72
-96 Q -.1(QU)5 G(EUE FILE FORMA).1 E 1.38(TS ..................................\
-..........................................................)-1.11 F(67)494 96 Q
-(Appendix C.)72 110.4 Q(SUMMAR)5 E 2.5(YO)-.65 G 2.5(FS)188.85 110.4 S(UPPOR)
-202.47 110.4 Q 2.5(TF)-.6 G 1.12(ILES ........................................\
-......................................)248.27 110.4 R(69)494 110.4 Q EP
-%%Trailer
-end
-%%EOF
diff --git a/usr.sbin/sendmail/doc/usenix/usenix.ps b/usr.sbin/sendmail/doc/usenix/usenix.ps
deleted file mode 100644
index 31f2f67..0000000
--- a/usr.sbin/sendmail/doc/usenix/usenix.ps
+++ /dev/null
@@ -1,1004 +0,0 @@
-%!PS-Adobe-3.0
-%%Creator: groff version 1.08
-%%DocumentNeededResources: font Times-Roman
-%%+ font Times-Italic
-%%+ font Times-Bold
-%%DocumentSuppliedResources: procset grops 1.08 0
-%%Pages: 9
-%%PageOrder: Ascend
-%%Orientation: Portrait
-%%EndComments
-%%BeginProlog
-%%BeginResource: procset grops 1.08 0
-/setpacking where{
-pop
-currentpacking
-true setpacking
-}if
-/grops 120 dict dup begin
-/SC 32 def
-/A/show load def
-/B{0 SC 3 -1 roll widthshow}bind def
-/C{0 exch ashow}bind def
-/D{0 exch 0 SC 5 2 roll awidthshow}bind def
-/E{0 rmoveto show}bind def
-/F{0 rmoveto 0 SC 3 -1 roll widthshow}bind def
-/G{0 rmoveto 0 exch ashow}bind def
-/H{0 rmoveto 0 exch 0 SC 5 2 roll awidthshow}bind def
-/I{0 exch rmoveto show}bind def
-/J{0 exch rmoveto 0 SC 3 -1 roll widthshow}bind def
-/K{0 exch rmoveto 0 exch ashow}bind def
-/L{0 exch rmoveto 0 exch 0 SC 5 2 roll awidthshow}bind def
-/M{rmoveto show}bind def
-/N{rmoveto 0 SC 3 -1 roll widthshow}bind def
-/O{rmoveto 0 exch ashow}bind def
-/P{rmoveto 0 exch 0 SC 5 2 roll awidthshow}bind def
-/Q{moveto show}bind def
-/R{moveto 0 SC 3 -1 roll widthshow}bind def
-/S{moveto 0 exch ashow}bind def
-/T{moveto 0 exch 0 SC 5 2 roll awidthshow}bind def
-/SF{
-findfont exch
-[exch dup 0 exch 0 exch neg 0 0]makefont
-dup setfont
-[exch/setfont cvx]cvx bind def
-}bind def
-/MF{
-findfont
-[5 2 roll
-0 3 1 roll
-neg 0 0]makefont
-dup setfont
-[exch/setfont cvx]cvx bind def
-}bind def
-/level0 0 def
-/RES 0 def
-/PL 0 def
-/LS 0 def
-/PLG{
-gsave newpath clippath pathbbox grestore
-exch pop add exch pop
-}bind def
-/BP{
-/level0 save def
-1 setlinecap
-1 setlinejoin
-72 RES div dup scale
-LS{
-90 rotate
-}{
-0 PL translate
-}ifelse
-1 -1 scale
-}bind def
-/EP{
-level0 restore
-showpage
-}bind def
-/DA{
-newpath arcn stroke
-}bind def
-/SN{
-transform
-.25 sub exch .25 sub exch
-round .25 add exch round .25 add exch
-itransform
-}bind def
-/DL{
-SN
-moveto
-SN
-lineto stroke
-}bind def
-/DC{
-newpath 0 360 arc closepath
-}bind def
-/TM matrix def
-/DE{
-TM currentmatrix pop
-translate scale newpath 0 0 .5 0 360 arc closepath
-TM setmatrix
-}bind def
-/RC/rcurveto load def
-/RL/rlineto load def
-/ST/stroke load def
-/MT/moveto load def
-/CL/closepath load def
-/FL{
-currentgray exch setgray fill setgray
-}bind def
-/BL/fill load def
-/LW/setlinewidth load def
-/RE{
-findfont
-dup maxlength 1 index/FontName known not{1 add}if dict begin
-{
-1 index/FID ne{def}{pop pop}ifelse
-}forall
-/Encoding exch def
-dup/FontName exch def
-currentdict end definefont pop
-}bind def
-/DEFS 0 def
-/EBEGIN{
-moveto
-DEFS begin
-}bind def
-/EEND/end load def
-/CNT 0 def
-/level1 0 def
-/PBEGIN{
-/level1 save def
-translate
-div 3 1 roll div exch scale
-neg exch neg exch translate
-0 setgray
-0 setlinecap
-1 setlinewidth
-0 setlinejoin
-10 setmiterlimit
-[]0 setdash
-/setstrokeadjust where{
-pop
-false setstrokeadjust
-}if
-/setoverprint where{
-pop
-false setoverprint
-}if
-newpath
-/CNT countdictstack def
-userdict begin
-/showpage{}def
-}bind def
-/PEND{
-clear
-countdictstack CNT sub{end}repeat
-level1 restore
-}bind def
-end def
-/setpacking where{
-pop
-setpacking
-}if
-%%EndResource
-%%IncludeResource: font Times-Roman
-%%IncludeResource: font Times-Italic
-%%IncludeResource: font Times-Bold
-grops begin/DEFS 1 dict def DEFS begin/u{.001 mul}bind def end/RES 72 def/PL
-792 def/LS false def/ENC0[/asciicircum/asciitilde/Scaron/Zcaron/scaron/zcaron
-/Ydieresis/trademark/quotesingle/.notdef/.notdef/.notdef/.notdef/.notdef
-/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef
-/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/.notdef/space
-/exclam/quotedbl/numbersign/dollar/percent/ampersand/quoteright/parenleft
-/parenright/asterisk/plus/comma/hyphen/period/slash/zero/one/two/three/four
-/five/six/seven/eight/nine/colon/semicolon/less/equal/greater/question/at/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/bracketleft/backslash
-/bracketright/circumflex/underscore/quoteleft/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/braceleft/bar/braceright/tilde/.notdef/quotesinglbase
-/guillemotleft/guillemotright/bullet/florin/fraction/perthousand/dagger
-/daggerdbl/endash/emdash/ff/fi/fl/ffi/ffl/dotlessi/dotlessj/grave/hungarumlaut
-/dotaccent/breve/caron/ring/ogonek/quotedblleft/quotedblright/oe/lslash
-/quotedblbase/OE/Lslash/.notdef/exclamdown/cent/sterling/currency/yen/brokenbar
-/section/dieresis/copyright/ordfeminine/guilsinglleft/logicalnot/minus
-/registered/macron/degree/plusminus/twosuperior/threesuperior/acute/mu
-/paragraph/periodcentered/cedilla/onesuperior/ordmasculine/guilsinglright
-/onequarter/onehalf/threequarters/questiondown/Agrave/Aacute/Acircumflex/Atilde
-/Adieresis/Aring/AE/Ccedilla/Egrave/Eacute/Ecircumflex/Edieresis/Igrave/Iacute
-/Icircumflex/Idieresis/Eth/Ntilde/Ograve/Oacute/Ocircumflex/Otilde/Odieresis
-/multiply/Oslash/Ugrave/Uacute/Ucircumflex/Udieresis/Yacute/Thorn/germandbls
-/agrave/aacute/acircumflex/atilde/adieresis/aring/ae/ccedilla/egrave/eacute
-/ecircumflex/edieresis/igrave/iacute/icircumflex/idieresis/eth/ntilde/ograve
-/oacute/ocircumflex/otilde/odieresis/divide/oslash/ugrave/uacute/ucircumflex
-/udieresis/yacute/thorn/ydieresis]def/Times-Bold@0 ENC0/Times-Bold RE
-/Times-Italic@0 ENC0/Times-Italic RE/Times-Roman@0 ENC0/Times-Roman RE
-%%EndProlog
-%%Page: 1 1
-%%BeginPageSetup
-BP
-%%EndPageSetup
-/F0 14/Times-Roman@0 SF(Mail Systems and Addressing)204.196 141 Q(in 4.2bsd)
-262.331 157.8 Q/F1 10/Times-Roman@0 SF(Eric Allman*)260.92 181.8 Q/F2 10
-/Times-Italic@0 SF(Britton-Lee)254.86 199.8 Q 2.5(,I)-.1 G(nc.)309.2 199.8 Q
-(1919 Addison Str)225.13 211.8 Q(eet, Suite 105.)-.37 E(Berk)232.645 223.8 Q
-(ele)-.1 E 1.1 -.55(y, C)-.3 H(alifornia 94704.).55 E F1(eric@Berk)244.175
-241.8 Q(ele)-.1 E -.65(y.)-.15 G(ARP).65 E(A)-.92 E(ucb)264.6 253.8 Q -.25(va)
--.15 G(x!eric).25 E(ABSTRA)262.085 286.2 Q(CT)-.4 E .966
-(Routing mail through a heterogeneous internet presents man)112 302.4 R 3.466
-(yn)-.15 G 1.466 -.25(ew p)373.438 302.4 T 3.466(roblems. Among).25 F .297
-(the w)112 314.4 R .297(orst of these is that of address mapping.)-.1 F
-(Historically)5.297 E 2.797(,t)-.65 G .298(his has been handled on an)355.03
-314.4 R(ad hoc basis.)112 326.4 Q(Ho)5 E(we)-.25 E -.15(ve)-.25 G .8 -.4(r, t)
-.15 H(his approach has become unmanageable as internets gro).4 E -.65(w.)-.25 G
-.099(Sendmail acts a uni\214ed \231post of)112 342.6 R .098
-(\214ce\232 to which all mail can be submitted.)-.25 F .098(Address inter)5.098
-F(-)-.2 E .754(pretation is controlled by a production system, which can parse\
- both old and ne)112 354.6 R 3.255(wf)-.25 G(or)452.54 354.6 Q(-)-.2 E .242
-(mat addresses.)112 366.6 R .242(The ne)5.242 F 2.742(wf)-.25 G .242
-(ormat is \231domain-based,)216.578 366.6 R 2.742<9a618d>-.7 G -.15(ex)334.326
-366.6 S .241(ible technique that can handle).15 F(man)112 378.6 Q 2.606(yc)-.15
-G .106(ommon situations.)141.116 378.6 R .106
-(Sendmail is not intended to perform user interf)5.106 F .107(ace functions.)
--.1 F .399(Sendmail will replace deli)112 394.8 R -.15(ve)-.25 G .399
-(rmail in the Berk).15 F(ele)-.1 E 2.899(y4)-.15 G .399(.2 distrib)320.504
-394.8 R 2.899(ution. Se)-.2 F -.15(ve)-.25 G .399(ral major hosts).15 F .421
-(are no)112 406.8 R 2.921(wo)-.25 G 2.921(rw)152.022 406.8 S .421
-(ill soon be running sendmail.)165.493 406.8 R .421(This change will af)5.421 F
-.422(fect an)-.25 F 2.922(yu)-.15 G .422(sers that route)407.056 406.8 R 1.5
-(mail through a sendmail g)112 418.8 R(ate)-.05 E -.1(wa)-.25 G 5.3 -.65(y. T)
-.1 H 1.5(he changes that will be user visible are empha-).65 F(sized.)112 430.8
-Q .906(The mail system to appear in 4.2bsd will contain a number of changes.)97
-475.2 R .906(Most of these changes are)5.906 F .469
-(based on the replacement of)72 487.2 R F2(delivermail)2.969 E F1 .469
-(with a ne)2.969 F 2.969(wm)-.25 G .469(odule called)292.871 487.2 R F2 2.97
-(sendmail. Sendmail)2.97 F F1 .47(implements a gen-)2.97 F 1.834
-(eral internetw)72 499.2 R 1.834(ork mail routing f)-.1 F(acility)-.1 E 4.333
-(,f)-.65 G 1.833(eaturing aliasing and forw)239.739 499.2 R 1.833
-(arding, automatic routing to netw)-.1 F(ork)-.1 E -.05(ga)72 511.2 S(te).05 E
--.1(wa)-.25 G .205(ys, and \215e).1 F .205(xible con\214guration.)-.15 F .205
-(Of k)5.205 F .505 -.15(ey i)-.1 H .205
-(nterest to the mail system user will be the changes in the net-).15 F -.1(wo)
-72 523.2 S(rk addressing structure.).1 E .624(In a simple netw)97 539.4 R .624
-(ork, each node has an address, and resources can be identi\214ed with a host-\
-resource)-.1 F .374(pair; in particular)72 551.4 R 2.874(,t)-.4 G .374
-(he mail system can refer to users using a host-username pair)149.932 551.4 R
-5.374(.H)-.55 G .375(ost names and numbers)409.276 551.4 R(ha)72 563.4 Q .3
--.15(ve t)-.2 H 2.5(ob).15 G 2.5(ea)108.31 563.4 S
-(dministered by a central authority)119.69 563.4 Q 2.5(,b)-.65 G
-(ut usernames can be assigned locally to each host.)263.82 563.4 Q .397
-(In an internet, multiple netw)97 579.6 R .396(orks with dif)-.1 F .396
-(ferent characteristics and managements must communicate.)-.25 F .389
-(In particular)72 591.6 R 2.889(,t)-.4 G .389
-(he syntax and semantics of resource identi\214cation change.)129.308 591.6 R
-.39(Certain special cases can be han-)5.389 F 1.033(dled tri)72 603.6 R 1.033
-(vially by)-.25 F F2 1.033(ad hoc)3.533 F F1 1.032(techniques, such as pro)
-3.533 F 1.032(viding netw)-.15 F 1.032
-(ork names that appear local to hosts on other)-.1 F(netw)72 615.6 Q 1.621
-(orks, as with the Ethernet at Xerox P)-.1 F 4.121(ARC. Ho)-.92 F(we)-.25 E
--.15(ve)-.25 G 2.421 -.4(r, t).15 H 1.622(he general case is e).4 F 1.622
-(xtremely comple)-.15 F 4.122(x. F)-.15 F(or)-.15 E -.15(ex)72 627.6 S .29
-(ample, some netw).15 F .29(orks require that the route the message tak)-.1 F
-.29(es be e)-.1 F .29(xplicitly speci\214ed by the sender)-.15 F 2.79(,s)-.4 G
-(im-)490.11 627.6 Q 1.618(plifying the database update problem since only adja\
-cent hosts must be entered into the system tables,)72 639.6 R .573(while other\
-s use logical addressing, where the sender speci\214es the location of the rec\
-ipient b)72 651.6 R .573(ut not ho)-.2 F 3.072(wt)-.25 G(o)499 651.6 Q 1.065
-(get there.)72 663.6 R 1.065(Some netw)6.065 F 1.066(orks use a left-associati)
--.1 F 1.366 -.15(ve s)-.25 H 1.066(yntax and others use a right-associati).15 F
-1.366 -.15(ve s)-.25 H 1.066(yntax, causing).15 F .32 LW 76 673.2 72 673.2 DL
-80 673.2 76 673.2 DL 84 673.2 80 673.2 DL 88 673.2 84 673.2 DL 92 673.2 88
-673.2 DL 96 673.2 92 673.2 DL 100 673.2 96 673.2 DL 104 673.2 100 673.2 DL 108
-673.2 104 673.2 DL 112 673.2 108 673.2 DL 116 673.2 112 673.2 DL 120 673.2 116
-673.2 DL 124 673.2 120 673.2 DL 128 673.2 124 673.2 DL 132 673.2 128 673.2 DL
-136 673.2 132 673.2 DL 140 673.2 136 673.2 DL 144 673.2 140 673.2 DL 148 673.2
-144 673.2 DL 152 673.2 148 673.2 DL 156 673.2 152 673.2 DL 160 673.2 156 673.2
-DL 164 673.2 160 673.2 DL 168 673.2 164 673.2 DL 172 673.2 168 673.2 DL 176
-673.2 172 673.2 DL 180 673.2 176 673.2 DL 184 673.2 180 673.2 DL 188 673.2 184
-673.2 DL 192 673.2 188 673.2 DL 196 673.2 192 673.2 DL 200 673.2 196 673.2 DL
-204 673.2 200 673.2 DL 208 673.2 204 673.2 DL 212 673.2 208 673.2 DL 216 673.2
-212 673.2 DL/F3 8/Times-Roman@0 SF .556(*A considerable part of this w)93.6
-685.2 R .556(ork w)-.08 F .556(as done while under the emplo)-.08 F 2.557(yo)
--.08 G 2.557(ft)323.107 685.2 S .557(he INGRES Project at the Uni)330.552 685.2
-R -.12(ve)-.2 G .557(rsity of California at).12 F(Berk)72 694.8 Q(ele)-.08 E
--.52(y.)-.12 G/F4 10/Times-Bold@0 SF(Mail Systems and Addr)72 756 Q
-(essing in 4.2bsd)-.18 E(1)499 756 Q EP
-%%Page: 2 2
-%%BeginPageSetup
-BP
-%%EndPageSetup
-/F0 10/Times-Bold@0 SF(Mail Systems and Addr)72 60 Q(essing in 4.2bsd)-.18 E(2)
-499 60 Q/F1 10/Times-Roman@0 SF(ambiguity in mix)72 96 Q(ed addresses.)-.15 E
-.679(Internet standards seek to eliminate these problems.)97 112.2 R(Initially)
-5.678 E 3.178(,t)-.65 G .678(hese proposed e)353.138 112.2 R .678
-(xpanding the address)-.15 F .331
-(pairs to address triples, consisting of {netw)72 124.2 R .331
-(ork, host, username} triples.)-.1 F(Netw)5.332 E .332(ork numbers must be uni)
--.1 F -.15(ve)-.25 G -.2(r-).15 G 1.452
-(sally agreed upon, and hosts can be assigned locally on each netw)72 136.2 R
-3.952(ork. The)-.1 F(user)3.952 E(-le)-.2 E -.15(ve)-.25 G 3.952(lp).15 G 1.452
-(resentation w)440.718 136.2 R(as)-.1 E .249(changed to address domains, compr\
-ised of a local resource identi\214cation and a hierarchical domain speci\214-)
-72 148.2 R 1.54(cation with a common static root.)72 160.2 R 1.539
-(The domain technique separates the issue of ph)6.539 F 1.539(ysical v)-.05 F
-1.539(ersus logical)-.15 F 3.001(addressing. F)72 172.2 R .501(or e)-.15 F .502
-(xample, an address of the form \231eric@a.cc.berk)-.15 F(ele)-.1 E -.65(y.)
--.15 G .502(arpa\232 describes the logical or).65 F -.05(ga)-.18 G(niza-).05 E
-.443(tion of the address space \(user \231eric\232 on host \231a\232 in the Co\
-mputer Center at Berk)72 184.2 R(ele)-.1 E .443(y\) b)-.15 F .443
-(ut not the ph)-.2 F(ysical)-.05 E(netw)72 196.2 Q .934(orks used \(for e)-.1 F
-.934(xample, this could go o)-.15 F -.15(ve)-.15 G 3.434(rd).15 G(if)274.722
-196.2 Q .934(ferent netw)-.25 F .935
-(orks depending on whether \231a\232 were on an)-.1 F
-(ethernet or a store-and-forw)72 208.2 Q(ard netw)-.1 E(ork\).)-.1 E/F2 10
-/Times-Italic@0 SF(Sendmail)97 224.4 Q F1 .493
-(is intended to help bridge the g)2.993 F .493(ap between the totally)-.05 F F2
-.493(ad hoc)2.993 F F1 -.1(wo)2.993 G .493(rld of netw).1 F .493(orks that kno)
--.1 F(w)-.25 E .854(nothing of each other and the clean, tightly-coupled w)72
-236.4 R .854(orld of unique netw)-.1 F .855(ork numbers.)-.1 F .855
-(It can accept old)5.855 F .633(arbitrary address syntax)72 248.4 R .632(es, r\
-esolving ambiguities using heuristics speci\214ed by the system administrator)
--.15 F 3.132(,a)-.4 G(s)500.11 248.4 Q .347(well as domain-based addressing.)72
-260.4 R .347(It helps guide the con)5.347 F -.15(ve)-.4 G .347
-(rsion of message formats between disparate net-).15 F -.1(wo)72 272.4 S 3.395
-(rks. In).1 F(short,)3.395 E F2(sendmail)3.395 E F1 .894
-(is designed to assist a graceful transition to consistent internetw)3.395 F
-.894(ork addressing)-.1 F(schemes.)72 284.4 Q .689
-(Section 1 de\214nes some of the terms frequently left fuzzy when w)97 312.6 R
-.69(orking in mail systems.)-.1 F .69(Section 2)5.69 F .595
-(discusses the design goals for)72 324.6 R F2(sendmail)3.095 E F1 5.595(.I)C
-3.095(ns)243.33 324.6 S .595(ection 3, the ne)255.315 324.6 R 3.095(wa)-.25 G
-.594(ddress formats and basic features of)332.705 324.6 R F2(send-)3.094 E
-(mail)72 336.6 Q F1 .893(are described.)3.393 F .893
-(Section 4 discusses some of the special problems of the UUCP netw)5.893 F
-3.394(ork. The)-.1 F(dif)3.394 E(fer)-.25 E(-)-.2 E(ences between)72 348.6 Q F2
-(sendmail)2.5 E F1(and)2.5 E F2(delivermail)2.5 E F1
-(are presented in section 5.)2.5 E F0(DISCLAIMER:)112 376.8 Q F1 3.333(An)3.333
-G .833(umber of e)199.216 376.8 R .832
-(xamples in this paper use names of actual people and)-.15 F(or)112 388.8 Q
--.05(ga)-.18 G 4.572(nizations. This).05 F 2.072
-(is not intended to imply a commitment or e)4.572 F -.15(ve)-.25 G 4.573(na).15
-G 4.573(ni)409.987 388.8 S(ntellectual)422.34 388.8 Q 1.094
-(agreement on the part of these people or or)112 400.8 R -.05(ga)-.18 G 3.594
-(nizations. In).05 F(particular)3.594 E 3.594(,B)-.4 G 1.094(ell T)408.896
-400.8 R(elephone)-.7 E .656
-(Laboratories \(BTL\), Digital Equipment Corporation \(DEC\), La)112 412.8 R
-.657(wrence Berk)-.15 F(ele)-.1 E 3.157(yL)-.15 G(abo-)446.23 412.8 Q 2.136
-(ratories \(LBL\), Britton-Lee Incorporated \(BLI\), and the Uni)112 424.8 R
--.15(ve)-.25 G 2.136(rsity of California at).15 F(Berk)112 436.8 Q(ele)-.1 E
-3.088(ya)-.15 G .588(re not committed to an)155.378 436.8 R 3.089(yo)-.15 G
-3.089(ft)261.219 436.8 S .589(hese proposals at this time.)270.418 436.8 R .589
-(Much of this paper)5.589 F
-(represents no more than the personal opinions of the author)112 448.8 Q(.)-.55
-E F0 2.5(1. DEFINITIONS)72 477 R F1 .266(There are four basic concepts that mu\
-st be clearly distinguished when dealing with mail systems:)112 493.2 R .514
-(the user \(or the user')87 505.2 R 3.014(sa)-.55 G .515(gent\), the user')
-182.6 505.2 R 3.015(si)-.55 G .515(denti\214cation, the user')253.025 505.2 R
-3.015(sa)-.55 G .515(ddress, and the route.)354.56 505.2 R .515(These are dis-)
-5.515 F(tinguished primarily by their position independence.)87 517.2 Q F0 2.5
-(1.1. User)87 541.2 R(and Identi\214cation)2.5 E F1 .264
-(The user is the being \(a person or program\) that is creating or recei)127
-557.4 R .263(ving a message.)-.25 F(An)5.263 E F2 -.1(age)2.763 G(nt).1 E F1
-.659(is an entity operating on behalf of the user \212 such as a secretary who\
- handles my mail.)102 569.4 R .66(or a pro-)5.66 F(gram that automatically ret\
-urns a message such as \231I am at the UNICOM conference.)102 581.4 Q<9a>-.7 E
-.931(The identi\214cation is the tag that goes along with the particular user)
-127 597.6 R 5.931(.T)-.55 G .931(his tag is completely)418.707 597.6 R .216
-(independent of location.)102 609.6 R -.15(Fo)5.216 G 2.716(re).15 G .216
-(xample, my identi\214cation is the string \231Eric Allman,)225.324 609.6 R
-2.717<9a61>-.7 G .217(nd this identi-)448.006 609.6 R 1.228
-(\214cation does not change whether I am located at U.C. Berk)102 621.6 R(ele)
--.1 E 2.527 -.65(y, a)-.15 H 3.727(tB).65 G 1.227
-(ritton-Lee, or at a scienti\214c)390.502 621.6 R(institute in Austria.)102
-633.6 Q 2.379
-(Since the identi\214cation is frequently ambiguous \(e.g., there are tw)127
-649.8 R 4.879<6f99>-.1 G 2.38(Robert Henry\232s at)426.48 649.8 R(Berk)102
-661.8 Q(ele)-.1 E .316(y\) it is common to add other disambiguating informatio\
-n that is not strictly part of the iden-)-.15 F
-(ti\214cation \(e.g., Robert \231Code Generator\232 Henry v)102 673.8 Q
-(ersus Robert \231System Administrator\232 Henry\).)-.15 E F0 -1(Ve)72 756 S
-(rsion 8.2)1 E(USENIX \255 J)249.805 756 Q(an 83)-.15 E(Last Mod 11/27/93)
-424.55 756 Q EP
-%%Page: 3 3
-%%BeginPageSetup
-BP
-%%EndPageSetup
-/F0 10/Times-Bold@0 SF(Mail Systems and Addr)72 60 Q(essing in 4.2bsd)-.18 E(3)
-499 60 Q 2.5(1.2. Addr)87 96 R(ess)-.18 E/F1 10/Times-Roman@0 SF .785
-(The address speci\214es a location.)127 112.2 R .786(As I mo)5.786 F 1.086
--.15(ve a)-.15 H .786(round, my address changes.).15 F -.15(Fo)5.786 G 3.286
-(re).15 G .786(xample, my)455.994 112.2 R 9.712
-(address might change from \231eric@Berk)102 124.2 R(ele)-.1 E -.65(y.)-.15 G
-(ARP).65 E 9.711(A\232 to \231eric@bli.UUCP\232 or \231all-)-.92 F
-(man@IIASA.Austria\232 depending on my current af)102 136.2 Q(\214liation.)-.25
-E(Ho)127 152.4 Q(we)-.25 E -.15(ve)-.25 G 2.819 -.4(r, a).15 H 4.519(na).4 G
-2.019(ddress is independent of the location of an)188.018 152.4 R 2.019
-(yone else.)-.15 F 2.02(That is, my address)7.02 F .385(remains the same to e)
-102 164.4 R -.15(ve)-.25 G .385(ryone who might be sending me mail.).15 F -.15
-(Fo)5.385 G 2.885(re).15 G .385(xample, a person at MIT and a)379.22 164.4 R
-(person at USC could both send to \231eric@Berk)102 176.4 Q(ele)-.1 E -.65(y.)
--.15 G(ARP).65 E(A\232 and ha)-.92 E .3 -.15(ve i)-.2 H 2.5(ta).15 G(rri)388.44
-176.4 Q .3 -.15(ve t)-.25 H 2.5(ot).15 G(he same mailbox.)422.48 176.4 Q .627
-(Ideally a \231white pages\232 service w)127 192.6 R .627(ould be pro)-.1 F
-.627(vided to map user identi\214cations into addresses)-.15 F .444(\(for e)102
-204.6 R .444(xample, see [Solomon81]\).)-.15 F .444
-(Currently this is handled by passing around scraps of paper or by)5.444 F
-(calling people on the telephone to \214nd out their address.)102 216.6 Q F0
-2.5(1.3. Route)87 240.6 R F1 .288(While an address speci\214es)127 256.8 R/F2
-10/Times-Italic@0 SF(wher)2.788 E(e)-.37 E F1 .289
-(to \214nd a mailbox, a route speci\214es)2.789 F F2(how)2.789 E F1 .289
-(to \214nd the mailbox.)2.789 F(Speci\214cally)102 268.8 Q 2.607(,i)-.65 G
-2.607(ts)156.457 268.8 S .106(peci\214es a path from sender to recei)165.734
-268.8 R -.15(ve)-.25 G 3.706 -.55(r. A).15 H 2.606(ss).55 G .106
-(uch, the route is potentially dif)343.364 268.8 R .106(ferent for)-.25 F -2.15
--.25(ev e)102 280.8 T(ry pair of people in the electronic uni).25 E -.15(ve)
--.25 G(rse.).15 E .258(Normally the route is hidden from the user by the softw)
-127 297 R 2.758(are. Ho)-.1 F(we)-.25 E -.15(ve)-.25 G 1.058 -.4(r, s).15 H
-.258(ome netw).4 F .258(orks put the)-.1 F -.2(bu)102 309 S 1.972
-(rden of determining the route onto the sender).2 F 6.971(.A)-.55 G 1.971
-(lthough this simpli\214es the softw)322.544 309 R 1.971(are, it also)-.1 F
-(greatly impairs the usability for most users.)102 321 Q(The UUCP netw)5 E
-(ork is an e)-.1 E(xample of such a netw)-.15 E(ork.)-.1 E F0 2.5(2. DESIGN)72
-345 R(GO)2.5 E(ALS)-.4 E F1(Design goals for)112 363.2 Q F2(sendmail)2.5 E/F3 7
-/Times-Roman@0 SF(1)216.71 359.2 Q F1(include:)222.71 363.2 Q 12.5
-(\(1\) Compatibility)92 379.4 R 1.363(with the e)3.863 F 1.363
-(xisting mail programs, including Bell v)-.15 F 1.363(ersion 6 mail, Bell v)
--.15 F 1.364(ersion 7)-.15 F 3.589(mail, Berk)118.66 391.4 R(ele)-.1 E(y)-.15 E
-F2(Mail)6.089 E F1 3.589
-([Shoens79], BerkNet mail [Schmidt79], and hopefully UUCP mail)6.089 F([No)
-118.66 403.4 Q 2.5(witz78]. ARP)-.25 F(ANET mail [Crock)-.92 E(er82] w)-.1 E
-(as also required.)-.1 E 12.5(\(2\) Reliability)92 419.6 R 4.002(,i)-.65 G
-4.002(nt)169.522 419.6 S 1.502(he sense of guaranteeing that e)181.304 419.6 R
--.15(ve)-.25 G 1.502(ry message is correctly deli).15 F -.15(ve)-.25 G 1.503
-(red or at least).15 F .368
-(brought to the attention of a human for correct disposal; no message should e)
-118.66 431.6 R -.15(ve)-.25 G 2.868(rb).15 G 2.868(ec)452.252 431.6 S
-(ompletely)464 431.6 Q 2.54(lost. This)118.66 443.6 R .04(goal w)2.54 F .041
-(as considered essential because of the emphasis on mail in our en)-.1 F 2.541
-(vironment. It)-.4 F 1.755
-(has turned out to be one of the hardest goals to satisfy)118.66 455.6 R 4.254
-(,e)-.65 G 1.754(specially in the f)363.756 455.6 R 1.754(ace of the man)-.1 F
-(y)-.15 E .977(anomalous message formats produced by v)118.66 467.6 R .977
-(arious ARP)-.25 F .977(ANET sites.)-.92 F -.15(Fo)5.977 G 3.478(re).15 G .978
-(xample, certain sites)420.114 467.6 R .069
-(generate improperly formated addresses, occasionally causing error)118.66
-479.6 R .069(-message loops.)-.2 F .068(Some hosts)5.069 F .766(use blanks in \
-names, causing problems with mail programs that assume that an address is one)
-118.66 491.6 R -.1(wo)118.66 503.6 S 3.924(rd. The).1 F 1.423
-(semantics of some \214elds are interpreted slightly dif)3.923 F 1.423
-(ferently by dif)-.25 F 1.423(ferent sites.)-.25 F(In)6.423 E(summary)118.66
-515.6 Q 3.022(,t)-.65 G .523(he obscure features of the ARP)163.532 515.6 R
-.523(ANET mail protocol really)-.92 F F2(ar)3.023 E(e)-.37 E F1 .523
-(used and are dif)3.023 F(\214cult)-.25 E(to support, b)118.66 527.6 Q
-(ut must be supported.)-.2 E 12.5(\(3\) Existing)92 543.8 R(softw)2.939 E .439
-(are to do actual deli)-.1 F -.15(ve)-.25 G .439(ry should be used whene).15 F
--.15(ve)-.25 G 2.938(rp).15 G 2.938(ossible. This)387.658 543.8 R .438
-(goal deri)2.938 F -.15(ve)-.25 G 2.938(sa).15 G(s)500.11 543.8 Q
-(much from political and practical considerations as technical.)118.66 555.8 Q
-12.5(\(4\) Easy)92 572 R -.15(ex)2.898 G .398(pansion to f).15 F .398
-(airly comple)-.1 F 2.898(xe)-.15 G -.4(nv)261.06 572 S .399
-(ironments, including multiple connections to a single net-).4 F -.1(wo)118.66
-584 S .63(rk type \(such as with multiple UUCP or Ethernets\).).1 F .63
-(This goal requires consideration of the)5.63 F
-(contents of an address as well as its syntax in order to determine which g)
-118.66 596 Q(ate)-.05 E -.1(wa)-.25 G 2.5(yt).1 G 2.5(ou)443.48 596 S(se.)
-455.98 596 Q 12.5(\(5\) Con\214guration)92 612.2 R 1.048
-(information should not be compiled into the code.)3.548 F 3.549(As)6.049 G
-1.049(ingle compiled program)405.802 612.2 R .084
-(should be able to run as is at an)118.66 624.2 R 2.584(ys)-.15 G .083
-(ite \(barring such basic changes as the CPU type or the operat-)256.196 624.2
-R .342(ing system\).)118.66 636.2 R 1.942 -.8(We h)5.342 H -2.25 -.2(av e).8 H
-.343(found this seemingly unimportant goal to be critical in real life.)3.042 F
-(Besides)5.343 E .734(the simple problems that occur when an)118.66 648.2 R
-3.234(yp)-.15 G .734(rogram gets recompiled in a dif)295.568 648.2 R .733
-(ferent en)-.25 F(vironment,)-.4 E(man)118.66 660.2 Q 2.5(ys)-.15 G(ites lik)
-147.12 660.2 Q 2.5(et)-.1 G 2.5<6f99>183.69 660.2 S(\214ddle\232 with an)195.63
-660.2 Q(ything that the)-.15 E 2.5(yw)-.15 G(ill be recompiling an)327.27 660.2
-Q(yw)-.15 E(ay)-.1 E(.)-.65 E .32 LW 76 678.8 72 678.8 DL 80 678.8 76 678.8 DL
-84 678.8 80 678.8 DL 88 678.8 84 678.8 DL 92 678.8 88 678.8 DL 96 678.8 92
-678.8 DL 100 678.8 96 678.8 DL 104 678.8 100 678.8 DL 108 678.8 104 678.8 DL
-112 678.8 108 678.8 DL 116 678.8 112 678.8 DL 120 678.8 116 678.8 DL 124 678.8
-120 678.8 DL 128 678.8 124 678.8 DL 132 678.8 128 678.8 DL 136 678.8 132 678.8
-DL 140 678.8 136 678.8 DL 144 678.8 140 678.8 DL 148 678.8 144 678.8 DL 152
-678.8 148 678.8 DL 156 678.8 152 678.8 DL 160 678.8 156 678.8 DL 164 678.8 160
-678.8 DL 168 678.8 164 678.8 DL 172 678.8 168 678.8 DL 176 678.8 172 678.8 DL
-180 678.8 176 678.8 DL 184 678.8 180 678.8 DL 188 678.8 184 678.8 DL 192 678.8
-188 678.8 DL 196 678.8 192 678.8 DL 200 678.8 196 678.8 DL 204 678.8 200 678.8
-DL 208 678.8 204 678.8 DL 212 678.8 208 678.8 DL 216 678.8 212 678.8 DL/F4 5
-/Times-Roman@0 SF(1)93.6 689.2 Q/F5 8/Times-Roman@0 SF(This section mak)3.2 I
-(es no distinction between)-.08 E/F6 8/Times-Italic@0 SF(delivermail)2 E F5
-(and)2 E F6(sendmail.)2 E F0 -1(Ve)72 756 S(rsion 8.2)1 E(USENIX \255 J)249.805
-756 Q(an 83)-.15 E(Last Mod 11/27/93)424.55 756 Q EP
-%%Page: 4 4
-%%BeginPageSetup
-BP
-%%EndPageSetup
-/F0 10/Times-Bold@0 SF(Mail Systems and Addr)72 60 Q(essing in 4.2bsd)-.18 E(4)
-499 60 Q/F1 10/Times-Roman@0 SF(\(6\))92 96 Q/F2 10/Times-Italic@0 SF(Sendmail)
-118.66 96 Q F1 .184(must be able to let v)2.684 F .184
-(arious groups maintain their o)-.25 F .184(wn mailing lists, and let indi)-.25
-F(viduals)-.25 E(specify their o)118.66 108 Q(wn forw)-.25 E
-(arding, without modifying the system alias \214le.)-.1 E 12.5(\(7\) Each)92
-124.2 R .313(user should be able to specify which mailer to e)2.814 F -.15(xe)
--.15 G .313(cute to process mail being deli).15 F -.15(ve)-.25 G .313(red for)
-.15 F 3.098(him. This)118.66 136.2 R .598(feature allo)3.098 F .598
-(ws users who are using specialized mailers that use a dif)-.25 F .598
-(ferent format to)-.25 F -.2(bu)118.66 148.2 S .25(ild their en).2 F .25
-(vironment without changing the system, and f)-.4 F .25
-(acilitates specialized functions \(such)-.1 F(as returning an \231I am on v)
-118.66 160.2 Q(acation\232 message\).)-.25 E 12.5(\(8\) Netw)92 176.4 R 1.552
-(ork traf)-.1 F 1.552(\214c should be minimized by batching addresses to a sin\
-gle host where possible,)-.25 F(without assistance from the user)118.66 188.4 Q
-(.)-.55 E .375(These goals moti)112 204.6 R -.25(va)-.25 G .375
-(ted the architecture illustrated in \214gure 1.).25 F .374
-(The user interacts with a mail gen-)5.375 F .49(erating and sending program.)
-87 216.6 R .491(When the mail is created, the generator calls)5.49 F F2
-(sendmail)2.991 E F1 2.991(,w)C .491(hich routes the)444.138 216.6 R .841
-(message to the correct mailer\(s\).)87 228.6 R .841
-(Since some of the senders may be netw)5.841 F .84(ork serv)-.1 F .84
-(ers and some of the)-.15 F(mailers may be netw)87 240.6 Q(ork clients,)-.1 E
-F2(sendmail)2.5 E F1(may be used as an internet mail g)2.5 E(ate)-.05 E -.1(wa)
--.25 G -.65(y.).1 G F0 2.5(3. USA)72 264.6 R(GE)-.55 E 2.5(3.1. Addr)87 288.6 R
-(ess F)-.18 E(ormats)-.25 E F1(Ar)127 304.8 Q .886
-(guments may be \215ags or addresses.)-.18 F .886(Flags set v)5.886 F .886
-(arious processing options.)-.25 F -.15(Fo)5.886 G(llo).15 E .886(wing \215ag)
--.25 F(ar)102 316.8 Q .611(guments, address ar)-.18 F .611(guments may be gi)
--.18 F -.15(ve)-.25 G 3.111(n. Addresses).15 F(follo)3.111 E 3.111(wt)-.25 G
-.611(he syntax in RFC822 [Crock)365.558 316.8 R(er82])-.1 E(for ARP)102 328.8 Q
-(ANET address formats.)-.92 E(In brief, the format is:)5 E 12.5(\(1\) An)107
-345 R(ything in parentheses is thro)-.15 E(wn a)-.25 E -.1(wa)-.15 G 2.5(y\().1
-G(as a comment\).)299.65 345 Q 12.5(\(2\) An)107 361.2 R .051
-(ything in angle brack)-.15 F .051(ets \(\231<)-.1 F .051
-(>\232\) is preferred o)1.666 F -.15(ve)-.15 G 2.551(ra).15 G -.15(ny)348.064
-361.2 S .051(thing else.).15 F .051(This rule implements the)5.051 F(ARP)133.66
-373.2 Q(ANET standard that addresses of the form)-.92 E .4 LW 77 408 72 408 DL
-79 408 74 408 DL 84 408 79 408 DL 89 408 84 408 DL 94 408 89 408 DL 99 408 94
-408 DL 104 408 99 408 DL 109 408 104 408 DL 114 408 109 408 DL 119 408 114 408
-DL 124 408 119 408 DL 129 408 124 408 DL 134 408 129 408 DL 139 408 134 408 DL
-144 408 139 408 DL 149 408 144 408 DL 154 408 149 408 DL 159 408 154 408 DL 164
-408 159 408 DL 169 408 164 408 DL 174 408 169 408 DL 179 408 174 408 DL 184 408
-179 408 DL 189 408 184 408 DL 194 408 189 408 DL 199 408 194 408 DL 204 408 199
-408 DL 209 408 204 408 DL 214 408 209 408 DL 219 408 214 408 DL 224 408 219 408
-DL 229 408 224 408 DL 234 408 229 408 DL 239 408 234 408 DL 244 408 239 408 DL
-249 408 244 408 DL 254 408 249 408 DL 259 408 254 408 DL 264 408 259 408 DL 269
-408 264 408 DL 274 408 269 408 DL 279 408 274 408 DL 284 408 279 408 DL 289 408
-284 408 DL 294 408 289 408 DL 299 408 294 408 DL 304 408 299 408 DL 309 408 304
-408 DL 314 408 309 408 DL 319 408 314 408 DL 324 408 319 408 DL 329 408 324 408
-DL 334 408 329 408 DL 339 408 334 408 DL 344 408 339 408 DL 349 408 344 408 DL
-354 408 349 408 DL 359 408 354 408 DL 364 408 359 408 DL 369 408 364 408 DL 374
-408 369 408 DL 379 408 374 408 DL 384 408 379 408 DL 389 408 384 408 DL 394 408
-389 408 DL 399 408 394 408 DL 404 408 399 408 DL 409 408 404 408 DL 414 408 409
-408 DL 419 408 414 408 DL 424 408 419 408 DL 429 408 424 408 DL 434 408 429 408
-DL 439 408 434 408 DL 444 408 439 408 DL 449 408 444 408 DL 454 408 449 408 DL
-459 408 454 408 DL 464 408 459 408 DL 469 408 464 408 DL 474 408 469 408 DL 479
-408 474 408 DL 484 408 479 408 DL 489 408 484 408 DL 494 408 489 408 DL 499 408
-494 408 DL 504 408 499 408 DL(Figure 1 \212 Sendmail System Structure.)208 660
-Q 77 672 72 672 DL 79 672 74 672 DL 84 672 79 672 DL 89 672 84 672 DL 94 672 89
-672 DL 99 672 94 672 DL 104 672 99 672 DL 109 672 104 672 DL 114 672 109 672 DL
-119 672 114 672 DL 124 672 119 672 DL 129 672 124 672 DL 134 672 129 672 DL 139
-672 134 672 DL 144 672 139 672 DL 149 672 144 672 DL 154 672 149 672 DL 159 672
-154 672 DL 164 672 159 672 DL 169 672 164 672 DL 174 672 169 672 DL 179 672 174
-672 DL 184 672 179 672 DL 189 672 184 672 DL 194 672 189 672 DL 199 672 194 672
-DL 204 672 199 672 DL 209 672 204 672 DL 214 672 209 672 DL 219 672 214 672 DL
-224 672 219 672 DL 229 672 224 672 DL 234 672 229 672 DL 239 672 234 672 DL 244
-672 239 672 DL 249 672 244 672 DL 254 672 249 672 DL 259 672 254 672 DL 264 672
-259 672 DL 269 672 264 672 DL 274 672 269 672 DL 279 672 274 672 DL 284 672 279
-672 DL 289 672 284 672 DL 294 672 289 672 DL 299 672 294 672 DL 304 672 299 672
-DL 309 672 304 672 DL 314 672 309 672 DL 319 672 314 672 DL 324 672 319 672 DL
-329 672 324 672 DL 334 672 329 672 DL 339 672 334 672 DL 344 672 339 672 DL 349
-672 344 672 DL 354 672 349 672 DL 359 672 354 672 DL 364 672 359 672 DL 369 672
-364 672 DL 374 672 369 672 DL 379 672 374 672 DL 384 672 379 672 DL 389 672 384
-672 DL 394 672 389 672 DL 399 672 394 672 DL 404 672 399 672 DL 409 672 404 672
-DL 414 672 409 672 DL 419 672 414 672 DL 424 672 419 672 DL 429 672 424 672 DL
-434 672 429 672 DL 439 672 434 672 DL 444 672 439 672 DL 449 672 444 672 DL 454
-672 449 672 DL 459 672 454 672 DL 464 672 459 672 DL 469 672 464 672 DL 474 672
-469 672 DL 479 672 474 672 DL 484 672 479 672 DL 489 672 484 672 DL 494 672 489
-672 DL 499 672 494 672 DL 504 672 499 672 DL F0 -1(Ve)72 756 S(rsion 8.2)1 E
-(USENIX \255 J)249.805 756 Q(an 83)-.15 E(Last Mod 11/27/93)424.55 756 Q EP
-%%Page: 5 5
-%%BeginPageSetup
-BP
-%%EndPageSetup
-/F0 10/Times-Bold@0 SF(Mail Systems and Addr)72 60 Q(essing in 4.2bsd)-.18 E(5)
-499 60 Q/F1 10/Times-Roman@0 SF(user name <machine-address>)173.66 96 Q(will s\
-end to the electronic \231machine-address\232 rather than the human \231user n\
-ame.)133.66 112.2 Q<9a>-.7 E 12.5(\(3\) Double)107 128.4 R 2.246(quotes \()
-4.746 F -2.754 2.5("\) q)2.5 H 2.246
-(uote phrases; backslashes quote characters.)224.188 128.4 R 2.246
-(Backslashes are more)7.246 F(po)133.66 140.4 Q .654(werful in that the)-.25 F
-3.154(yw)-.15 G .655(ill cause otherwise equi)229.196 140.4 R -.25(va)-.25 G
-.655(lent phrases to compare dif).25 F .655(ferently \212 for)-.25 F -.15(ex)
-133.66 152.4 S(ample,).15 E/F2 10/Times-Italic@0 SF(user)3.873 E F1(and)3.873 E
-F2("user")3.872 E F1 1.372(are equi)3.872 F -.25(va)-.25 G 1.372(lent, b).25 F
-(ut)-.2 E F2(\\user)3.872 E F1 1.372(is dif)3.872 F 1.372
-(ferent from either of them.)-.25 F(This)6.372 E(might be used to a)133.66
-164.4 Q -.2(vo)-.2 G(id normal aliasing or duplicate suppression algorithms.).2
-E -.15(Pa)127 180.6 S 1.12(rentheses, angle brack).15 F 1.12
-(ets, and double quotes must be properly balanced and nested.)-.1 F(The)6.12 E
-(re)102 194.6 Q(writing rules control remaining parsing)-.25 E/F3 7
-/Times-Roman@0 SF(2)266.17 190.6 Q F1(.)269.67 194.6 Q .644(Although old style\
- addresses are still accepted in most cases, the preferred address format is)
-127 210.8 R .299(based on ARP)102 222.8 R(ANET)-.92 E .299
-(-style domain-based addresses [Su82a].)-.92 F .299
-(These addresses are based on a hierar)5.299 F(-)-.2 E .13
-(chical, logical decomposition of the address space.)102 234.8 R .13
-(The addresses are hierarchical in a sense similar)5.13 F 1.133(to the U.S. po\
-stal addresses: the messages may \214rst be routed to the correct state, with \
-no initial)102 246.8 R .72
-(consideration of the city or other addressing details.)102 258.8 R .72
-(The addresses are logical in that each step in)5.72 F(the hierarch)102 270.8 Q
-2.5(yc)-.05 G
-(orresponds to a set of \231naming authorities\232 rather than a ph)161.37
-270.8 Q(ysical netw)-.05 E(ork.)-.1 E -.15(Fo)127 287 S 2.5(re).15 G
-(xample, the address:)147.53 287 Q(eric@HostA.BigSite.ARP)142 303.2 Q(A)-.92 E
--.1(wo)102 319.4 S .851
-(uld \214rst look up the domain BigSite in the namespace administrated by ARP)
-.1 F 3.351(A. A)-.92 F .851(query could)3.351 F 1.476
-(then be sent to BigSite for interpretation of HostA.)102 331.4 R(Ev)6.475 E
-1.475(entually the mail w)-.15 F 1.475(ould arri)-.1 F 1.775 -.15(ve a)-.25 H
-3.975(tH).15 G(ostA,)482.61 331.4 Q(which w)102 343.4 Q
-(ould then do \214nal deli)-.1 E -.15(ve)-.25 G(ry to user \231eric.).15 E<9a>
--.7 E F0 2.5(3.2. Mail)87 367.4 R(to Files and Pr)2.5 E(ograms)-.18 E F1 .609
-(Files and programs are le)127 383.6 R .609(gitimate message recipients.)-.15 F
-.609(Files pro)5.609 F .609(vide archi)-.15 F -.25(va)-.25 G 3.109(ls).25 G .61
-(torage of mes-)445.02 383.6 R .124
-(sages, useful for project administration and history)102 395.6 R 5.124(.P)-.65
-G .124(rograms are useful as recipients in a v)318.308 395.6 R .124(ariety of)
--.25 F .69(situations, for e)102 407.6 R .691(xample, to maintain a public rep\
-ository of systems messages \(such as the Berk)-.15 F(ele)-.1 E(y)-.15 E F2
-(msgs)102 419.6 Q F1(program\).)2.5 E(An)127 435.8 Q 3.188(ya)-.15 G .688(ddre\
-ss passing through the initial parsing algorithm as a local address \(i.e, not\
- appear)151.698 435.8 R(-)-.2 E .276(ing to be a v)102 447.8 R .276
-(alid address for another mailer\) is scanned for tw)-.25 F 2.776(os)-.1 G .277
-(pecial cases.)362.128 447.8 R .277(If pre\214x)5.277 F .277(ed by a v)-.15 F
-(erti-)-.15 E .18(cal bar \(\231)102 459.8 R .833<7c9a>.833 G 2.68(\)t)-.833 G
-.179(he rest of the address is processed as a shell command.)156.456 459.8 R
-.179(If the user name be)5.179 F .179(gins with a)-.15 F(slash mark \(\231/)102
-471.8 Q(\232\) the name is used as a \214le name, instead of a login name.).833
-E F0 2.5(3.3. Aliasing,)87 495.8 R -.25(Fo)2.5 G(rwarding, Inclusion).25 E F2
-(Sendmail)127 512 Q F1 1.074(reroutes mail three w)3.574 F 3.574(ays. Aliasing)
--.1 F 1.075(applies system wide.)3.575 F -.15(Fo)6.075 G(rw).15 E 1.075
-(arding allo)-.1 F 1.075(ws each)-.25 F .233
-(user to reroute incoming mail destined for that account.)102 524 R .233
-(Inclusion directs)5.233 F F2(sendmail)2.733 E F1 .233(to read a \214le for)
-2.733 F 2.5(al)102 536 S
-(ist of addresses, and is normally used in conjunction with aliasing.)111.72
-536 Q F0 2.5(3.3.1. Aliasing)102 560 R F1 .065
-(Aliasing maps local addresses to address lists using a system-wide \214le.)142
-576.2 R .065(This \214le is hashed)5.065 F 1.546(to speed access.)117 588.2 R
-1.545(Only addresses that parse as local are allo)6.546 F 1.545
-(wed as aliases; this guarantees a)-.25 F(unique k)117 600.2 Q .3 -.15(ey \()
--.1 H(since there are no nicknames for the local host\).).15 E F0 2.5(3.3.2. F)
-102 624.2 R(orwarding)-.25 E F1 .641
-(After aliasing, if an recipient address speci\214es a local user)142 640.4 R
-F2(sendmail)3.141 E F1 .641(searches for a \231.for)3.141 F(-)-.2 E -.1(wa)117
-652.4 S .413(rd\232 \214le in the recipient').1 F 2.913(sh)-.55 G .413
-(ome directory)235.335 652.4 R 5.413(.I)-.65 G 2.913(fi)302.161 652.4 S 2.913
-(te)311.184 652.4 S .413(xists, the message is)321.167 652.4 R F2(not)2.913 E
-F1 .412(sent to that user)2.913 F 2.912(,b)-.4 G(ut)496.22 652.4 Q .745
-(rather to the list of addresses in that \214le.)117 664.4 R .746
-(Often this list will contain only one address, and the)5.746 F
-(feature will be used for netw)117 676.4 Q(ork mail forw)-.1 E(arding.)-.1 E
-.32 LW 76 686 72 686 DL 80 686 76 686 DL 84 686 80 686 DL 88 686 84 686 DL 92
-686 88 686 DL 96 686 92 686 DL 100 686 96 686 DL 104 686 100 686 DL 108 686 104
-686 DL 112 686 108 686 DL 116 686 112 686 DL 120 686 116 686 DL 124 686 120 686
-DL 128 686 124 686 DL 132 686 128 686 DL 136 686 132 686 DL 140 686 136 686 DL
-144 686 140 686 DL 148 686 144 686 DL 152 686 148 686 DL 156 686 152 686 DL 160
-686 156 686 DL 164 686 160 686 DL 168 686 164 686 DL 172 686 168 686 DL 176 686
-172 686 DL 180 686 176 686 DL 184 686 180 686 DL 188 686 184 686 DL 192 686 188
-686 DL 196 686 192 686 DL 200 686 196 686 DL 204 686 200 686 DL 208 686 204 686
-DL 212 686 208 686 DL 216 686 212 686 DL/F4 5/Times-Roman@0 SF(2)93.6 696.4 Q
-/F5 8/Times-Roman@0 SF(Disclaimer: Some special processing is done after re)3.2
-I(writing local names; see belo)-.2 E -.52(w.)-.2 G F0 -1(Ve)72 756 S
-(rsion 8.2)1 E(USENIX \255 J)249.805 756 Q(an 83)-.15 E(Last Mod 11/27/93)
-424.55 756 Q EP
-%%Page: 6 6
-%%BeginPageSetup
-BP
-%%EndPageSetup
-/F0 10/Times-Bold@0 SF(Mail Systems and Addr)72 60 Q(essing in 4.2bsd)-.18 E(6)
-499 60 Q/F1 10/Times-Roman@0 SF -.15(Fo)142 96 S(rw).15 E 1.152
-(arding also permits a user to specify a pri)-.1 F -.25(va)-.25 G 1.151
-(te incoming mailer).25 F 6.151(.F)-.55 G 1.151(or e)437.348 96 R 1.151
-(xample, for)-.15 F(-)-.2 E -.1(wa)117 108 S(rding to:).1 E -2.5 .833("| /)157
-124.2 T(usr/local/ne)-.833 E(wmail myname")-.25 E(will use a dif)117 140.4 Q
-(ferent incoming mailer)-.25 E(.)-.55 E F0 2.5(3.3.3. Inclusion)102 164.4 R F1
-(Inclusion is speci\214ed in RFC 733 [Crock)142 180.6 Q(er77] syntax:)-.1 E
-(:Include: pathname)157 196.8 Q .391
-(An address of this form reads the \214le speci\214ed by)117 213 R/F2 10
-/Times-Italic@0 SF(pathname)2.891 E F1 .391
-(and sends to all users listed in that)2.891 F(\214le.)117 225 Q .645
-(The intent is)142 241.2 R F2(not)3.145 E F1 .644
-(to support direct use of this feature, b)3.145 F .644
-(ut rather to use this as a subset of)-.2 F 2.5(aliasing. F)117 253.2 R(or e)
--.15 E(xample, an alias of the form:)-.15 E
-(project: :include:/usr/project/userlist)157 269.4 Q 1.93(is a method of letti\
-ng a project maintain a mailing list without interaction with the system)117
-285.6 R(administration, e)117 297.6 Q -.15(ve)-.25 G 2.5(ni).15 G 2.5(ft)203.54
-297.6 S(he alias \214le is protected.)212.15 297.6 Q 2.025
-(It is not necessary to reb)142 313.8 R 2.025(uild the inde)-.2 F 4.524(xo)-.15
-G 4.524(nt)317.828 313.8 S 2.024(he alias database when a :include: list is)
-330.132 313.8 R(changed.)117 325.8 Q F0 2.5(3.4. Message)87 349.8 R(Collection)
-2.5 E F1 .857(Once all recipient addresses are parsed and v)127 366 R .857
-(eri\214ed, the message is collected.)-.15 F .857(The message)5.857 F .574
-(comes in tw)102 378 R 3.074(op)-.1 G .574
-(arts: a message header and a message body)164.452 378 R 3.074(,s)-.65 G .574
-(eparated by a blank line.)349.734 378 R .573(The body is)5.574 F
-(an uninterpreted sequence of te)102 390 Q(xt lines.)-.15 E
-(The header is formated as a series of lines of the form)127 406.2 Q
-(\214eld-name: \214eld-v)178 422.4 Q(alue)-.25 E(Field-v)102 438.6 Q 1.366
-(alue can be split across lines by starting the follo)-.25 F 1.366
-(wing lines with a space or a tab)-.25 F 6.366(.S)-.4 G(ome)486.78 438.6 Q .211
-(header \214elds ha)102 450.6 R .511 -.15(ve s)-.2 H .211
-(pecial internal meaning, and ha).15 F .511 -.15(ve a)-.2 H .211
-(ppropriate special processing.).15 F .21(Other headers)5.21 F
-(are simply passed through.)102 462.6 Q
-(Some header \214elds may be added automatically)5 E 2.5(,s)-.65 G
-(uch as time stamps.)413.53 462.6 Q F0 2.5(4. THE)72 486.6 R(UUCP PR)2.5 E
-(OBLEM)-.3 E F1 .43(Of particular interest is the UUCP netw)112 502.8 R 2.93
-(ork. The)-.1 F -.15(ex)2.93 G .43(plicit routing used in the UUCP en).15 F
-(vironment)-.4 E .909(causes a number of serious problems.)87 514.8 R .909
-(First, gi)5.909 F .908(ving out an address is impossible without kno)-.25 F
-.908(wing the)-.25 F .453(address of your potential correspondent.)87 526.8 R
-.454(This is typically handled by specifying the address relati)5.453 F .754
--.15(ve t)-.25 H(o).15 E 1.208(some \231well-kno)87 538.8 R 1.208
-(wn\232 host \(e.g., ucb)-.25 F -.25(va)-.15 G 3.708(xo).25 G 3.708(rd)253.47
-538.8 S(ecv)265.508 538.8 Q 3.708(ax\). Second,)-.25 F 1.207(it is often dif)
-3.708 F 1.207(\214cult to compute the set of)-.25 F .157
-(addresses to reply to without some kno)87 550.8 R .157
-(wledge of the topology of the netw)-.25 F 2.657(ork. Although)-.1 F .157
-(it may be easy)2.657 F .352(for a human being to do this under man)87 562.8 R
-2.851(yc)-.15 G .351(ircumstances, a program does not ha)259.713 562.8 R .651
--.15(ve e)-.2 H .351(qually sophisticated).15 F 1.153(heuristics b)87 574.8 R
-1.153(uilt in.)-.2 F 1.154(Third, certain addresses will become painfully and \
-unnecessarily long, as when a)6.153 F .406(message is routed through man)87
-586.8 R 2.906(yh)-.15 G .406(osts in the USENET)225.81 586.8 R 5.406(.A)-.74 G
-.406(nd \214nally)322.804 586.8 R 2.905(,c)-.65 G .405(ertain \231mix)370.465
-586.8 R .405(ed domain\232 addresses)-.15 F
-(are impossible to parse unambiguously \212 e.g.,)87 598.8 Q(decv)127 615 Q
-(ax!ucb)-.25 E -.25(va)-.15 G(x!lbl-h!user@LBL-CSAM).25 E .378(might ha)87
-631.2 R .678 -.15(ve m)-.2 H(an).15 E 2.878(yp)-.15 G .379
-(ossible resolutions, depending on whether the message w)164.574 631.2 R .379
-(as \214rst routed to decv)-.1 F .379(ax or)-.25 F(to LBL-CSAM.)87 643.2 Q 2.32
--.8(To s)112 659.4 T(olv).8 E 3.22(et)-.15 G .72
-(his problem, the UUCP syntax w)152.49 659.4 R .719(ould ha)-.1 F 1.019 -.15
-(ve t)-.2 H 3.219(ob).15 G 3.219(ec)346.956 659.4 S .719
-(hanged to use addresses rather than)359.055 659.4 R 3.718(routes. F)87 671.4 R
-1.218(or e)-.15 F 1.218(xample, the address \231decv)-.15 F(ax!ucb)-.25 E -.25
-(va)-.15 G 1.218(x!eric\232 might be e).25 F 1.218(xpressed as \231eric@ucb)
--.15 F -.25(va)-.15 G(x.UUCP\232).25 E .079(\(with the hop through decv)87
-683.4 R .079(ax implied\).)-.25 F .079(This address w)5.079 F .078
-(ould itself be a domain-based address; for e)-.1 F(xam-)-.15 E
-(ple, an address might be of the form:)87 695.4 Q(mark@d.cbosg.btl.UUCP)127
-711.6 Q F0 -1(Ve)72 756 S(rsion 8.2)1 E(USENIX \255 J)249.805 756 Q(an 83)-.15
-E(Last Mod 11/27/93)424.55 756 Q EP
-%%Page: 7 7
-%%BeginPageSetup
-BP
-%%EndPageSetup
-/F0 10/Times-Bold@0 SF(Mail Systems and Addr)72 60 Q(essing in 4.2bsd)-.18 E(7)
-499 60 Q/F1 10/Times-Roman@0 SF .311(Hosts outside of Bell T)87 96 R .311
-(elephone Laboratories w)-.7 F .311(ould then only need to kno)-.1 F 2.811(wh)
--.25 G .811 -.25(ow t)402.982 96 T 2.811(og).25 G .312(et to a designated)
-433.354 96 R(BTL relay)87 108 Q 2.5(,a)-.65 G(nd the BTL topology w)137.17 108
-Q(ould only be maintained inside Bell.)-.1 E .543(There are three major proble\
-ms associated with turning UUCP addresses into something reason-)112 124.2 R
-.465(able: de\214ning the namespace, creating and propag)87 136.2 R .465
-(ating the necessary softw)-.05 F .466(are, and b)-.1 F .466(uilding and main-)
--.2 F(taining the database.)87 148.2 Q F0 2.5(4.1. De\214ning)87 172.2 R
-(the Namespace)2.5 E F1 1.015(Putting all UUCP hosts into a \215at namespace \
-\(e.g., \231...@host.UUCP\232\) is not practical for a)127 188.4 R .222
-(number of reasons.)102 200.4 R .222(First, with o)5.222 F -.15(ve)-.15 G 2.722
-(r1).15 G .222(600 sites already)253.292 200.4 R 2.722(,a)-.65 G .222
-(nd \(with the increasing a)329.958 200.4 R -.25(va)-.2 G .222
-(ilability of ine).25 F(x-)-.15 E(pensi)102 212.4 Q 1.973 -.15(ve m)-.25 H
-1.673(icrocomputers and autodialers\) se).15 F -.15(ve)-.25 G 1.672
-(ral thousand more coming within a fe).15 F 4.172(wy)-.25 G 1.672(ears, the)
-469.008 212.4 R .078
-(database update problem is simply intractable if the namespace is \215at.)102
-224.4 R .078(Second, there are almost cer)5.078 F(-)-.2 E 2.446
-(tainly name con\215icts today)102 236.4 R 7.446(.T)-.65 G 2.446
-(hird, as the number of sites gro)232.794 236.4 R 4.946(wt)-.25 G 2.446
-(he names become e)386.316 236.4 R -.15(ve)-.25 G 4.946(rl).15 G(ess)491.78
-236.4 Q(mnemonic.)102 248.4 Q .534(It seems ine)127 264.6 R .535
-(vitable that there be some sort of naming authority for the set of top le)-.25
-F -.15(ve)-.25 G 3.035(ln).15 G(ames)483.45 264.6 Q .157
-(in the UUCP domain, as unpleasant a possibility as that may seem.)102 276.6 R
-.157(It will simply not be possible to)5.157 F(ha)102 288.6 Q .536 -.15(ve o)
--.2 H .236(ne host resolving all names.).15 F .236(It may ho)5.236 F(we)-.25 E
--.15(ve)-.25 G 2.736(rb).15 G 2.736(ep)316.144 288.6 S .236
-(ossible to handle this in a f)328.32 288.6 R .237(ashion similar to)-.1 F
-1.582(that of assigning names of ne)102 300.6 R 1.582(wsgroups in USENET)-.25 F
-6.582(.H)-.74 G -.25(ow)334.758 300.6 S -2.15 -.25(ev e).25 H 2.382 -.4(r, i)
-.25 H 4.082(tw).4 G 1.582(ill be essential to encourage)386.582 300.6 R -2.15
--.25(ev e)102 312.6 T .52(ryone to become subdomains of an e).25 F .52
-(xisting domain whene)-.15 F -.15(ve)-.25 G 3.02(rp).15 G .52(ossible \212 e)
-374.85 312.6 R -.15(ve)-.25 G 3.02(nt).15 G .52(hough this will)442.95 312.6 R
-.077(certainly bruise some e)102 324.6 R 2.577(gos. F)-.15 F .077(or e)-.15 F
-.077(xample, if a ne)-.15 F 2.577(wh)-.25 G .076
-(ost named \231blid\232 were to be added to the UUCP)310.843 324.6 R(netw)102
-336.6 Q .65(ork, it w)-.1 F .651(ould probably actually be addressed as \231d.\
-bli.UUCP\232 \(i.e., as host \231d\232 in the pseudo-)-.1 F
-(domain \231bli\232 rather than as host \231blid\232 in the UUCP domain\).)102
-348.6 Q F0 2.5(4.2. Cr)87 372.6 R(eating and Pr)-.18 E(opagating the Softwar)
--.18 E(e)-.18 E F1 .078(The softw)127 388.8 R .078
-(are required to implement a consistent namespace is relati)-.1 F -.15(ve)-.25
-G .077(ly tri).15 F 2.577(vial. T)-.25 F .277 -.1(wo m)-.8 H(odules).1 E
-(are needed, one to handle incoming mail and one to handle outgoing mail.)102
-400.8 Q 1.136(The incoming module must be prepared to handle either old or ne)
-127 417 R 3.636(ws)-.25 G 1.136(tyle addresses.)416.448 417 R(Ne)6.136 E(w-)
--.25 E .025(style addresses can be passed through unchanged.)102 429 R .024
-(Old style addresses must be turned into ne)5.025 F 2.524(ws)-.25 G(tyle)489
-429 Q(addresses where possible.)102 441 Q 2.247
-(The outgoing module is slightly trickier)127 457.2 R 7.247(.I)-.55 G 4.747(tm)
-309.932 457.2 S 2.247(ust do a database lookup on the recipient)325.239 457.2 R
-.823(addresses \(passed on the command line\) to determine what hosts to send \
-the message to.)102 469.2 R .823(If those)5.823 F .023(hosts do not accept ne)
-102 481.2 R .024(w-style addresses, it must transform all addresses in the hea\
-der of the message)-.25 F(into old style using the database lookup.)102 493.2 Q
-1.197(Both of these modules are straightforw)127 509.4 R 1.197(ard e)-.1 F
-1.197(xcept for the issue of modifying the header)-.15 F 6.197(.I)-.55 G(t)
-501.22 509.4 Q .944
-(seems prudent to choose one format for the message headers.)102 521.4 R -.15
-(Fo)5.944 G 3.444(ran).15 G .944(umber of reasons, Berk)391.448 521.4 R(ele)-.1
-E(y)-.15 E .824(has elected to use the ARP)102 533.4 R .824
-(ANET protocols for message formats.)-.92 F(Ho)5.823 E(we)-.25 E -.15(ve)-.25 G
-1.623 -.4(r, t).15 H .823(his protocol is some-).4 F(what dif)102 545.4 Q
-(\214cult to parse.)-.25 E(Propag)127 561.6 Q 1.903(ation is some)-.05 F 1.903
-(what more dif)-.25 F 4.403(\214cult. There)-.25 F 1.903(are a lar)4.403 F
-1.903(ge number of hosts connected to)-.18 F .812(UUCP that will w)102 573.6 R
-.811(ant to run completely standard systems \(for v)-.1 F .811
-(ery good reasons\).)-.15 F .811(The strate)5.811 F .811(gy is)-.15 F
-(not to con)102 585.6 Q -.15(ve)-.4 G(rt the entire netw).15 E
-(ork \212 only enough of it it alle)-.1 E(viate the problem.)-.25 E F0 2.5
-(4.3. Building)87 609.6 R(and Maintaining the Database)2.5 E F1 .127
-(This is by f)127 625.8 R .127(ar the most dif)-.1 F .128(\214cult problem.)
--.25 F 2.628(Ap)5.128 G .128(rototype for this database already e)309.736 625.8
-R .128(xists, b)-.15 F .128(ut it is)-.2 F
-(maintained by hand and does not pretend to be complete.)102 637.8 Q .701(This\
- problem will be reduced considerably if people choose to group their hosts in\
-to subdo-)127 654 R 3.219(mains. This)102 666 R -.1(wo)3.219 G .719
-(uld require a global update only when a ne).1 F 3.22(wt)-.25 G .72(op le)
-356.47 666 R -.15(ve)-.25 G 3.22(ld).15 G .72(omain joined the netw)396.95 666
-R(ork.)-.1 E 2.805(Am)102 678 S .305
-(essage to a host in a subdomain could simply be routed to a kno)119.805 678 R
-.304(wn domain g)-.25 F(ate)-.05 E -.1(wa)-.25 G 2.804(yf).1 G .304(or further)
-465.656 678 R 3.073(processing. F)102 690 R .573(or e)-.15 F .573(xample, the \
-address \231eric@a.bli.UUCP\232 might be routed to the \231bli\232 g)-.15 F
-(ate)-.05 E -.1(wa)-.25 G 3.074(yf).1 G(or)495.67 690 Q(redistrib)102 702 Q
-1.376(ution; ne)-.2 F 3.876(wh)-.25 G 1.375
-(osts could be added within BLI without notifying the rest of the w)187.632 702
-R 3.875(orld. Of)-.1 F(course, other hosts)102 714 Q/F2 10/Times-Italic@0 SF
-(could)2.5 E F1(be noti\214ed as an ef)2.5 E(\214cienc)-.25 E 2.5(ym)-.15 G
-(easure.)321.01 714 Q F0 -1(Ve)72 756 S(rsion 8.2)1 E(USENIX \255 J)249.805 756
-Q(an 83)-.15 E(Last Mod 11/27/93)424.55 756 Q EP
-%%Page: 8 8
-%%BeginPageSetup
-BP
-%%EndPageSetup
-/F0 10/Times-Bold@0 SF(Mail Systems and Addr)72 60 Q(essing in 4.2bsd)-.18 E(8)
-499 60 Q/F1 10/Times-Roman@0 SF .966(There may be more than one domain g)127 96
-R(ate)-.05 E -.1(wa)-.25 G 4.767 -.65(y. A).1 H .967
-(domain such as BTL, for instance, might)4.117 F(ha)102 108 Q .653 -.15(ve a d)
--.2 H .353(ozen g).15 F(ate)-.05 E -.1(wa)-.25 G .353(ys to the outside w).1 F
-.352(orld; a non-BTL site could choose the closest g)-.1 F(ate)-.05 E -.1(wa)
--.25 G 4.152 -.65(y. T).1 H(he).65 E .308(only restriction w)102 120 R .308
-(ould be that all g)-.1 F(ate)-.05 E -.1(wa)-.25 G .308
-(ys maintain a consistent vie).1 F 2.808(wo)-.25 G 2.808(ft)390.998 120 S .308
-(he domain the)399.916 120 R 2.808(yr)-.15 G(epresent.)468.18 120 Q F0 2.5
-(4.4. Logical)87 144 R(Structur)2.5 E(e)-.18 E F1(Logically)127 160.2 Q 3.803
-(,d)-.65 G 1.303(omains are or)175.983 160.2 R -.05(ga)-.18 G 1.303
-(nized into a tree.).05 F 1.303(There need not be a host actually associated)
-6.303 F .462(with each le)102 172.2 R -.15(ve)-.25 G 2.962(li).15 G 2.962(nt)
-168.806 172.2 S .462(he tree \212 for e)179.548 172.2 R .462
-(xample, there will be no host associated with the name \231UUCP)-.15 F -.7
-<2e9a>-1.11 G(Similarly)102 184.2 Q 3.115(,a)-.65 G 3.115(no)148.635 184.2 S
--2.19 -.18(rg a)161.75 184.2 T .614
-(nization might group names together for administrati).18 F .914 -.15(ve r)-.25
-H .614(easons; for e).15 F .614(xample, the)-.15 F(name)102 196.2 Q
-(CAD.research.BigCorp.UUCP)142 212.4 Q(might not actually ha)102 228.6 Q .3
--.15(ve a h)-.2 H(ost representing \231research.).15 E<9a>-.7 E(Ho)127 244.8 Q
-(we)-.25 E -.15(ve)-.25 G 1.531 -.4(r, i).15 H 3.231(tm).4 G .731
-(ay frequently be con)184.902 244.8 R -.15(ve)-.4 G .731(nient to ha).15 F
-1.031 -.15(ve a h)-.2 H .732(ost or hosts that \231represent\232 a domain.).15
-F -.15(Fo)102 256.8 S 3.466(re).15 G .966(xample, if a single host e)123.496
-256.8 R .966(xists that represents Berk)-.15 F(ele)-.1 E 2.266 -.65(y, t)-.15 H
-.966(hen mail from outside Berk).65 F(ele)-.1 E 3.466(yc)-.15 G(an)494.56 256.8
-Q(forw)102 268.8 Q .796
-(ard mail to that host for further resolution without kno)-.1 F .796(wing Berk)
--.25 F(ele)-.1 E(y')-.15 E 3.296(s\()-.55 G .797(rather v)417.066 268.8 R .797
-(olatile\) topol-)-.2 F(ogy)102 280.8 Q 5(.T)-.65 G(his is not unlik)129.96
-280.8 Q 2.5(et)-.1 G(he operation of the telephone netw)198.76 280.8 Q(ork.)-.1
-E .053(This may also be useful inside certain lar)127 297 R .053(ge domains.)
--.18 F -.15(Fo)5.053 G 2.553(re).15 G .053(xample, at Berk)365.352 297 R(ele)
--.1 E 2.553(yi)-.15 G 2.553(tm)450.801 297 S .053(ay be pre-)463.914 297 R .722
-(sumed that most hosts kno)102 309 R 3.222(wa)-.25 G .722
-(bout other hosts inside the Berk)225.64 309 R(ele)-.1 E 3.223(yd)-.15 G 3.223
-(omain. But)380.825 309 R .723(if the)3.223 F 3.223(yp)-.15 G .723(rocess an)
-466.347 309 R .405(address that is unkno)102 321 R .405(wn, the)-.25 F 2.905
-(yc)-.15 G .405(an pass it \231upstairs\232 for further e)229.165 321 R 2.905
-(xamination. Thus)-.15 F .405(as ne)2.905 F 2.905(wh)-.25 G .405(osts are)
-473.325 321 R .488(added only one host \(the domain master\))102 333 R/F2 10
-/Times-Italic@0 SF(must)2.989 E F1 .489
-(be updated immediately; other hosts can be updated)2.989 F(as con)102 345 Q
--.15(ve)-.4 G(nient.).15 E .583(Ideally this name resolution process w)127
-361.2 R .583(ould be performed by a name serv)-.1 F .582
-(er \(e.g., [Su82b]\) to)-.15 F -.2(avo)102 373.2 S .507(id unnecessary cop).2
-F .507(ying of the message.)-.1 F(Ho)5.507 E(we)-.25 E -.15(ve)-.25 G 1.307 -.4
-(r, i).15 H 3.007(nab).4 G .507(atch netw)346.623 373.2 R .508
-(ork such as UUCP this could)-.1 F(result in unnecessary delays.)102 385.2 Q F0
-2.5(5. COMP)72 409.2 R(ARISON WITH DELIVERMAIL)-.74 E F2(Sendmail)112 425.4 Q
-F1(is an outgro)2.5 E(wth of)-.25 E F2(delivermail)2.5 E F1 5(.T)C
-(he primary dif)286.18 425.4 Q(ferences are:)-.25 E 12.5(\(1\) Con\214guration)
-92 441.6 R .573(information is not compiled in.)3.073 F .572
-(This change simpli\214es man)5.572 F 3.072(yo)-.15 G 3.072(ft)433.684 441.6 S
-.572(he problems of)442.866 441.6 R(mo)118.66 453.6 Q(ving to other machines.)
--.15 E(It also allo)5 E(ws easy deb)-.25 E(ugging of ne)-.2 E 2.5(wm)-.25 G
-(ailers.)388.06 453.6 Q 12.5(\(2\) Address)92 469.8 R .491
-(parsing is more \215e)2.991 F 2.991(xible. F)-.15 F .491(or e)-.15 F(xample,)
--.15 E F2(delivermail)2.992 E F1 .492(only supported one g)2.992 F(ate)-.05 E
--.1(wa)-.25 G 2.992(yt).1 G 2.992(oa)481.718 469.8 S -.15(ny)494.15 469.8 S
-(netw)118.66 481.8 Q(ork, whereas)-.1 E F2(sendmail)2.5 E F1(can be sensiti)2.5
-E .3 -.15(ve t)-.25 H 2.5(oh).15 G(ost names and reroute to dif)310.9 481.8 Q
-(ferent g)-.25 E(ate)-.05 E -.1(wa)-.25 G(ys.).1 E 12.5(\(3\) F)92 498 R(orw)
--.15 E 2.878(arding and :include: features eliminate the requirement that the \
-system alias \214le be)-.1 F 1.073(writable by an)118.66 510 R 3.573(yu)-.15 G
-1.073
-(ser \(or that an update program be written, or that the system administration)
-191.439 510 R(mak)118.66 522 Q 2.5(ea)-.1 G(ll changes\).)147.16 522 Q(\(4\))92
-538.2 Q F2(Sendmail)118.66 538.2 Q F1 .443
-(supports message batching across netw)2.944 F .443
-(orks when a message is being sent to multiple)-.1 F(recipients.)118.66 550.2 Q
-12.5(\(5\) A)92 566.4 R 1.945(mail queue is pro)4.445 F 1.946(vided in)-.15 F
-F2(sendmail.)4.446 E F1 1.946(Mail that cannot be deli)6.946 F -.15(ve)-.25 G
-1.946(red immediately b).15 F 1.946(ut can)-.2 F .439(potentially be deli)
-118.66 578.4 R -.15(ve)-.25 G .438
-(red later is stored in this queue for a later retry).15 F 5.438(.T)-.65 G .438
-(he queue also pro)404.088 578.4 R .438(vides a)-.15 F -.2(bu)118.66 590.4 S
--.25(ff).2 G .838(er ag).25 F .839(ainst system crashes; after the message has\
- been collected it may be reliably redeli)-.05 F(v-)-.25 E(ered e)118.66 602.4
-Q -.15(ve)-.25 G 2.5(ni).15 G 2.5(ft)162.13 602.4 S
-(he system crashes during the initial deli)170.74 602.4 Q -.15(ve)-.25 G(ry).15
-E(.)-.65 E(\(6\))92 618.6 Q F2(Sendmail)118.66 618.6 Q F1 1.351(uses the netw)
-3.851 F 1.351(orking support pro)-.1 F 1.351(vided by 4.2BSD to pro)-.15 F 1.35
-(vide a direct interf)-.15 F 1.35(ace net-)-.1 F -.1(wo)118.66 630.6 S .283
-(rks such as the ARP).1 F .284
-(ANET and/or Ethernet using SMTP \(the Simple Mail T)-.92 F .284
-(ransfer Protocol\))-.35 F -.15(ove)118.66 642.6 S 2.5(raT).15 G
-(CP/IP connection.)151.68 642.6 Q F0 -1(Ve)72 756 S(rsion 8.2)1 E
-(USENIX \255 J)249.805 756 Q(an 83)-.15 E(Last Mod 11/27/93)424.55 756 Q EP
-%%Page: 9 9
-%%BeginPageSetup
-BP
-%%EndPageSetup
-/F0 10/Times-Roman@0 SF(REFERENCES)264.105 132 Q([Crock)87 148.2 Q 56.73
-(er77] Crock)-.1 F(er)-.1 E 3.535(,D)-.4 G 3.535(.H)239.965 148.2 S 1.035(., V)
-253.22 148.2 R 1.035(ittal, J. J., Pogran, K. T)-.6 F 1.035
-(., and Henderson, D. A. Jr)-.74 F(.,)-.55 E/F1 10/Times-Italic@0 SF(Stan-)
-3.535 E(dar)195 160.2 Q 2.627(df)-.37 G .127(or the F)218.927 160.2 R .127
-(ormat of ARP)-1.05 F 2.627(AN)-.9 G .128(etwork T)320.112 160.2 R -.2(ex)-.92
-G 2.628(tM).2 G(essa)377.018 160.2 Q -.1(ge)-.1 G(s.).1 E F0 .128
-(RFC 733, NIC 41952.)5.128 F(In [Feinler78].)195 172.2 Q(No)5 E -.15(ve)-.15 G
-(mber 1977.).15 E([Crock)87 188.4 Q 56.73(er82] Crock)-.1 F(er)-.1 E 4.272(,D)
--.4 G 4.272(.H)240.702 188.4 S(.,)254.694 188.4 Q F1(Standar)4.272 E 4.272(df)
--.37 G 1.772(or the F)307.318 188.4 R 1.772(ormat of Arpa Internet T)-1.05 F
--.2(ex)-.92 G 4.271(tM).2 G(essa)471.15 188.4 Q -.1(ge)-.1 G(s.).1 E F0 .025
-(RFC 822.)195 200.4 R(Netw)5.025 E .025(ork Information Center)-.1 F 2.526(,S)
--.4 G .026(RI International, Menlo P)363.506 200.4 R .026(ark, Cali-)-.15 F 2.5
-(fornia. August)195 212.4 R(1982.)2.5 E 60.51([Feinler78] Feinler)87 228.6 R
-2.938(,E)-.4 G .438(., and Postel, J.)234.478 228.6 R(\(eds.\),)5.438 E F1(ARP)
-2.938 E .438(ANET Pr)-.9 F .438(otocol Handbook.)-.45 F F0 .438(NIC 7104,)5.438
-F(Netw)195 240.6 Q 3.011(ork Information Center)-.1 F 5.511(,S)-.4 G 3.012
-(RI International, Menlo P)328.513 240.6 R 3.012(ark, California.)-.15 F(1978.)
-195 252.6 Q([No)87 268.8 Q 59.65(witz78] No)-.25 F .479
-(witz, D. A., and Lesk, M. E.,)-.25 F F1 2.978(AD)2.978 G .478
-(ial-Up Network of UNIX Systems.)344.67 268.8 R F0(Bell)5.478 E 3.528
-(Laboratories. In)195 280.8 R 1.029(UNIX Programmer')3.528 F 3.529(sM)-.55 G
-1.029(anual, Se)363.524 280.8 R -.15(ve)-.25 G 1.029(nth Edition, V).15 F 1.029
-(olume 2.)-1.29 F(August, 1978.)195 292.8 Q 55.5([Schmidt79] Schmidt,)87 309 R
-(E.,)2.631 E F1 .131(An Intr)2.631 F .131(oduction to the Berk)-.45 F(ele)-.1 E
-2.631(yN)-.3 G(etwork.)382.277 309 Q F0(Uni)5.131 E -.15(ve)-.25 G .131
-(rsity of Califor).15 F(-)-.2 E(nia, Berk)195 321 Q(ele)-.1 E 2.5(yC)-.15 G 2.5
-(alifornia. 1979.)257.24 321 R 59.95([Shoens79] Shoens,)87 337.2 R(K.,)3.227 E
-F1 .728(Mail Refer)3.227 F .728(ence Manual.)-.37 F F0(Uni)5.728 E -.15(ve)-.25
-G .728(rsity of California, Berk).15 F(ele)-.1 E 4.528 -.65(y. I)-.15 H(n).65 E
-3.478(UNIX Programmer')195 349.2 R 5.977(sM)-.55 G 3.477(anual, Se)297.495
-349.2 R -.15(ve)-.25 G 3.477(nth Edition, V).15 F 3.477(olume 2C.)-1.29 F
-(December)8.477 E(1979.)195 361.2 Q 52.72([Solomon81] Solomon,)87 377.4 R .251
-(M., Landweber)2.75 F 2.751(,L)-.4 G .251(., and Neuhengen, D.,)308.952 377.4 R
-F1 .251(The Design of the CSNET)2.751 F .397(Name Server)195 389.4 R(.)-1.11 E
-F0 2.896(CS-DN-2. Uni)5.397 F -.15(ve)-.25 G .396(rsity of W).15 F .396
-(isconsin, Madison.)-.4 F .396(October 1981.)5.396 F 73.84([Su82a] Su,)87 405.6
-R(Za)2.844 E .344(w-Sing, and Postel, Jon,)-.15 F F1 .344
-(The Domain Naming Con)2.844 F .344(vention for Internet)-.4 F 2.71
-(User Applications.)195 417.6 R F0 5.21(RFC819. Netw)7.71 F 2.71
-(ork Information Center)-.1 F 5.21(,S)-.4 G 2.71(RI Interna-)457.14 417.6 R
-(tional, Menlo P)195 429.6 Q(ark, California.)-.15 E(August 1982.)5 E 73.28
-([Su82b] Su,)87 445.8 R(Za)4.174 E(w-Sing,)-.15 E F1 4.174(AD)4.174 G(istrib)
-275.702 445.8 Q 1.675(uted System for Internet Name Service)-.2 F(.)-.15 E F0
-(RFC830.)6.675 E(Netw)195 457.8 Q 3.012(ork Information Center)-.1 F 5.512(,S)
--.4 G 3.011(RI International, Menlo P)328.516 457.8 R 3.011(ark, California.)
--.15 F(October 1982.)195 469.8 Q/F2 10/Times-Bold@0 SF(Mail Systems and Addr)72
-756 Q(essing in 4.2bsd)-.18 E(9)499 756 Q EP
-%%Trailer
-end
-%%EOF
diff --git a/usr.sbin/sendmail/mail.local/Makefile b/usr.sbin/sendmail/mail.local/Makefile
index e8556d8..86579d7 100644
--- a/usr.sbin/sendmail/mail.local/Makefile
+++ b/usr.sbin/sendmail/mail.local/Makefile
@@ -1,9 +1,10 @@
# @(#)Makefile 8.1 (Berkeley) 7/19/93
PROG= mail.local
-MAN8= mail.local.0
+MAN8= mail.local.8
BINOWN= root
BINMODE=4555
INSTALLFLAGS=-fschg
+BINDIR= /usr/libexec
.include <bsd.prog.mk>
diff --git a/usr.sbin/sendmail/mail.local/mail.local.8 b/usr.sbin/sendmail/mail.local/mail.local.8
index 661615c..d92ba00 100644
--- a/usr.sbin/sendmail/mail.local/mail.local.8
+++ b/usr.sbin/sendmail/mail.local/mail.local.8
@@ -30,6 +30,7 @@
.\" SUCH DAMAGE.
.\"
.\" @(#)mail.local.8 8.2 (Berkeley) 12/11/93
+.\" $Id$
.\"
.Dd December 11, 1993
.Dt MAIL.LOCAL 8
@@ -40,6 +41,8 @@
.Sh SYNOPSIS
.Nm mail.local
.Op Fl f Ar from
+.Op Fl b
+.Op Fl s
.Ar user ...
.Sh DESCRIPTION
.Nm Mail.local
@@ -55,6 +58,16 @@ The options are as follows:
.Bl -tag -width xxxfrom
.It Fl f Ar from
Specify the sender's name.
+.It Fl b
+Turn off the attempts to notify the
+.Dq biff
+service.
+.It Fl s
+Turn off the
+.Xr fsync 2
+call that forces the mailbox to be committed to disk before returning a
+.Dq success
+status.
.El
.Pp
Individual mail messages in the mailbox are delimited by an empty
@@ -90,7 +103,6 @@ user's mailbox directory
.El
.Sh SEE ALSO
.Xr mail 1 ,
-.Xr xsend 1 ,
.Xr flock 2 ,
.Xr getservbyname 3 ,
.Xr comsat 8 ,
diff --git a/usr.sbin/sendmail/mail.local/mail.local.c b/usr.sbin/sendmail/mail.local/mail.local.c
index e7f1211..ee2c6422 100644
--- a/usr.sbin/sendmail/mail.local/mail.local.c
+++ b/usr.sbin/sendmail/mail.local/mail.local.c
@@ -29,6 +29,8 @@
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
+ *
+ * $Id: mail.local.c,v 1.9 1997/06/27 15:17:01 peter Exp $
*/
#ifndef lint
@@ -183,9 +185,9 @@ extern FILE *fdopen __P((int, const char *));
int eval = EX_OK; /* sysexits.h error value. */
-void deliver __P((int, char *));
+void deliver __P((int, char *, int, int));
void e_to_sys __P((int));
-__dead void err __P((const char *, ...));
+void err __P((const char *, ...)) __dead2;
void notifybiff __P((char *));
int store __P((char *));
void usage __P((void));
@@ -200,7 +202,7 @@ main(argc, argv)
char *argv[];
{
struct passwd *pw;
- int ch, fd;
+ int ch, fd, nobiff, nofsync;
uid_t uid;
char *from;
extern char *optarg;
@@ -220,8 +222,13 @@ main(argc, argv)
#endif
from = NULL;
- while ((ch = getopt(argc, argv, "df:r:")) != EOF)
+ nobiff = 0;
+ nofsync = 0;
+ while ((ch = getopt(argc, argv, "bdf:r:s")) != -1)
switch(ch) {
+ case 'b':
+ nobiff++;
+ break;
case 'd': /* Backward compatible. */
break;
case 'f':
@@ -232,6 +239,9 @@ main(argc, argv)
}
from = optarg;
break;
+ case 's':
+ nofsync++;
+ break;
case '?':
default:
usage();
@@ -262,7 +272,7 @@ main(argc, argv)
* at the expense of repeated failures and multiple deliveries.
*/
for (fd = store(from); *argv; ++argv)
- deliver(fd, *argv);
+ deliver(fd, *argv, nobiff, nofsync);
exit(eval);
}
@@ -317,8 +327,8 @@ store(from)
}
void
-deliver(fd, name)
- int fd;
+deliver(fd, name, nobiff, nofsync)
+ int fd, nobiff, nofsync;
char *name;
{
struct stat fsb, sb;
@@ -440,11 +450,13 @@ tryagain:
goto err1;
}
- /* Get the starting offset of the new message for biff. */
- curoff = lseek(mbfd, (off_t)0, SEEK_END);
- (void)snprintf(biffmsg, sizeof(biffmsg),
- sizeof curoff > sizeof(long) ? "%s@%qd\n" : "%s@%ld\n",
- name, curoff);
+ if (!nobiff) {
+ /* Get the starting offset of the new message for biff. */
+ curoff = lseek(mbfd, (off_t)0, SEEK_END);
+ (void)snprintf(biffmsg, sizeof(biffmsg),
+ sizeof curoff > sizeof(long) ? "%s@%qd\n" : "%s@%ld\n",
+ name, curoff);
+ }
/* Copy the message into the file. */
if (lseek(fd, (off_t)0, SEEK_SET) == (off_t)-1) {
@@ -475,7 +487,7 @@ tryagain:
}
/* Flush to disk, don't wait for update. */
- if (fsync(mbfd)) {
+ if (!nofsync && fsync(mbfd)) {
e_to_sys(errno);
warn("%s: %s", path, strerror(errno));
err3:
@@ -497,7 +509,7 @@ err0: unlockmbox();
e_to_sys(errno);
warn("%s: %s", path, strerror(errno));
truncate(path, curoff);
- } else
+ } else if (!nobiff)
notifybiff(biffmsg);
if (setreuid(0, 0) < 0) {
@@ -600,7 +612,7 @@ void
usage()
{
eval = EX_USAGE;
- err("usage: mail.local [-f from] user ...");
+ err("usage: mail.local [-b] [-f from] [-s] user ...");
}
#if __STDC__
diff --git a/usr.sbin/sendmail/mailstats/Makefile b/usr.sbin/sendmail/mailstats/Makefile
index d1a3996..427c8df 100644
--- a/usr.sbin/sendmail/mailstats/Makefile
+++ b/usr.sbin/sendmail/mailstats/Makefile
@@ -1,7 +1,7 @@
# @(#)Makefile 8.2 (Berkeley) 9/21/96
PROG= mailstats
-MAN8= mailstats.0
+MAN8= mailstats.8
CFLAGS+=-I${.CURDIR}/../src
.include "../../Makefile.inc"
diff --git a/usr.sbin/sendmail/mailstats/mailstats.8 b/usr.sbin/sendmail/mailstats/mailstats.8
index 3fe87c9..b233969 100644
--- a/usr.sbin/sendmail/mailstats/mailstats.8
+++ b/usr.sbin/sendmail/mailstats/mailstats.8
@@ -1,80 +1,78 @@
-.\" @(#)mailstats.8 8.1 (Berkeley) 9/21/96
-.Dd April 25, 1996
-.Dt MAILSTATS 1
-.Os BSD 3
+.\"/*
+.\" * 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.
+.\" */
+.\" $Id: mailstats.8,v 1.4 1997/02/22 16:13:25 peter Exp $
+.Dd August 13, 1996
+.Dt MAILSTATS 8
+.Os
.Sh NAME
.Nm mailstats
-.Nd display mail statistics
+.Nd display mail protocol statistics
.Sh SYNOPSIS
.Nm mailstats
.Op Fl o
-.Op Fl C Ar cffile
-.Op Fl f Ar stfile
+.Op Fl C Ar sendmail.cf
+.Op Fl f Ar sendmail.st
.Sh DESCRIPTION
-The
-.Nm mailstats
-utility displays the current mail statistics.
-.Pp
-First, the time at which statistics started being kept is displayed,
-in the format specified by
-.Xr ctime 3 .
-Then,
-the statistics for each mailer are displayed on a single line,
-each with the following whitespace separated fields:
-.Pp
-.Bl -tag -width 10n -offset indent -compact
-.It Sy M
-The mailer number.
-.It Sy msgsfr
-Number of messages from the mailer.
-.It Sy bytes_from
-Kbytes from the mailer.
-.It Sy msgsto
-Number of messages to the mailer.
-.It Sy bytes_to
-Kbytes to the mailer.
-.It Sy Mailer
-The name of the mailer.
-.El
-.Pp
-After this display, a line totaling the values for all of the mailers
-is displayed,
-separated from the previous information by a line containing only equals
-.Pq Dq \&=
-characters.
+.Nm Mailstats
+displays mail statistics on a per mailer basis.
+Each line of output contains
+the mailer number, the count and byte-count of incoming messages,
+the count and byte-count of outgoing messages, and the name of the
+mailer unless the
+.Fl o
+flag is specified. Common mailers include smtp and local (eg:
+.Nm mail.local,
+the program which handles local delivery of mail).
.Pp
-The options are as follows:
-.Bl -tag -width Ds
-.It Fl C
-Read the specified file instead of the default
-.Nm sendmail
-.Dq cf
-file.
-.It Fl f
-Read the specified statistics file instead of the statistics file
-specified in the
+Statistics are read from the
.Nm sendmail
-.Dq cf
-file.
-.It Fl o
-Don't display the name of the mailer in the output.
-.El
-.Pp
-The
-.Nm mailstats
-utility exits 0 on success, and >0 if an error occurs.
+statistics file
+.Ar sendmail.st,
+the location of which is defined in
+.Ar sendmail.cf,
+or specified with the
+.Fl f
+flag. Mailers are likewise defined in the
+.Ar sendmail.cf
+file. Statistics are cumulative; zero the statistics file
+to reset the counters.
.Sh FILES
-.Bl -tag -width /var/log/sendmail.stXX -compact
+.Bl -tag -width /var/log/sendmail.st -compact
.It Pa /etc/sendmail.cf
-The default
-.Nm sendmail
-.Dq cf
-file.
+sendmail configuration file
.It Pa /var/log/sendmail.st
-The default
-.Nm sendmail
-statistics file.
+sendmail statistics file
.El
.Sh SEE ALSO
-.Xr mailq 1 ,
+.Xr mail.local 8 ,
.Xr sendmail 8
+
diff --git a/usr.sbin/sendmail/mailstats/mailstats.c b/usr.sbin/sendmail/mailstats/mailstats.c
index ffbb2bb..625b44c 100644
--- a/usr.sbin/sendmail/mailstats/mailstats.c
+++ b/usr.sbin/sendmail/mailstats/mailstats.c
@@ -73,7 +73,7 @@ main(argc, argv)
cfile = _PATH_SENDMAILCF;
sfile = NULL;
mnames = TRUE;
- while ((ch = getopt(argc, argv, "C:f:o")) != EOF)
+ while ((ch = getopt(argc, argv, "C:f:o")) != -1)
{
switch (ch)
{
@@ -92,7 +92,7 @@ main(argc, argv)
case '?':
default:
usage:
- fputs("usage: mailstats [-C cffile] [-f stfile] -o\n",
+ fputs("usage: mailstats [-o] [-C cffile] [-f stfile]\n",
stderr);
exit(EX_USAGE);
}
diff --git a/usr.sbin/sendmail/makemap/Makefile b/usr.sbin/sendmail/makemap/Makefile
index e64e093..db59c4f 100644
--- a/usr.sbin/sendmail/makemap/Makefile
+++ b/usr.sbin/sendmail/makemap/Makefile
@@ -1,7 +1,7 @@
# @(#)Makefile 8.4 (Berkeley) 6/10/97
PROG= makemap
-MAN8= makemap.0
+MAN8= makemap.8
CFLAGS+=-I${.CURDIR}/../src -DNEWDB -DNOT_SENDMAIL
SRCS= makemap.c safefile.c
diff --git a/usr.sbin/sendmail/makemap/makemap.c b/usr.sbin/sendmail/makemap/makemap.c
index 9d088c6..8e10907 100644
--- a/usr.sbin/sendmail/makemap/makemap.c
+++ b/usr.sbin/sendmail/makemap/makemap.c
@@ -33,7 +33,7 @@
*/
#ifndef lint
-static char sccsid[] = "@(#)makemap.c 8.38 (Berkeley) 9/23/97";
+static char sccsid[] = "@(#)makemap.c 8.37 (Berkeley) 7/10/97";
#endif /* not lint */
#include <sys/types.h>
@@ -152,7 +152,7 @@ main(argc, argv)
#else
#define OPTIONS "Ndforv"
#endif
- while ((opt = getopt(argc, argv, OPTIONS)) != EOF)
+ while ((opt = getopt(argc, argv, OPTIONS)) != -1)
{
switch (opt)
{
@@ -363,6 +363,13 @@ main(argc, argv)
pbuf, errstring(st));
exit(EX_CANTCREAT);
}
+ if (std.st_dev == stp.st_dev && std.st_ino == stp.st_ino)
+ {
+ fprintf(stderr,
+ "%s: cannot run with GDBM\n",
+ mapname);
+ exit(EX_CONFIG);
+ }
break;
#endif
default:
@@ -400,14 +407,6 @@ main(argc, argv)
#ifdef NDBM
case T_DBM:
dbp.dbm = dbm_open(mapname, mode, 0644);
- if (dbp.dbm != NULL &&
- dbm_dirfno(dbp.dbm) == dbm_pagfno(dbp.dbm))
- {
- fprintf(stderr, "dbm map %s: cannot run with GDBM\n",
- mapname);
- dbm_close(dbp.dbm);
- exit(EX_CONFIG);
- }
if (!ignoresafeties && dbp.dbm != NULL &&
(filechanged(dbuf, dbm_dirfno(dbp.dbm), &std, sff) ||
filechanged(pbuf, dbm_pagfno(dbp.dbm), &stp, sff)))
@@ -510,7 +509,7 @@ main(argc, argv)
progname, mapname, lineno, sizeof ibuf);
continue;
}
-
+
if (ibuf[0] == '\0' || ibuf[0] == '#')
continue;
if (isspace(ibuf[0]))
diff --git a/usr.sbin/sendmail/praliases/Makefile b/usr.sbin/sendmail/praliases/Makefile
index 4285a5b..af13ac3 100644
--- a/usr.sbin/sendmail/praliases/Makefile
+++ b/usr.sbin/sendmail/praliases/Makefile
@@ -1,9 +1,8 @@
# @(#)Makefile 8.2 (Berkeley) 9/21/96
PROG= praliases
-MAN8= praliases.0
-CFLAGS+=-I${.CURDIR}/../src
-DPADD= ${LIBDBM}
+MAN8= praliases.8
+CFLAGS+=-I${.CURDIR}/../src -DNEWDB
.include "../../Makefile.inc"
.include <bsd.prog.mk>
diff --git a/usr.sbin/sendmail/praliases/praliases.c b/usr.sbin/sendmail/praliases/praliases.c
index 361edac..e0cae65 100644
--- a/usr.sbin/sendmail/praliases/praliases.c
+++ b/usr.sbin/sendmail/praliases/praliases.c
@@ -56,8 +56,10 @@ main(argc, argv)
{
extern char *optarg;
extern int optind;
+#ifdef NDBM
DBM *dbp;
datum content, key;
+#endif
char *filename;
int ch;
#ifdef NEWDB
@@ -67,7 +69,7 @@ main(argc, argv)
#endif
filename = "/etc/aliases";
- while ((ch = getopt(argc, argv, "f:")) != EOF)
+ while ((ch = getopt(argc, argv, "f:")) != -1)
switch((char)ch) {
case 'f':
filename = optarg;
@@ -106,8 +108,11 @@ main(argc, argv)
newdbkey.data);
}
}
- else {
#endif
+#ifdef NDBM
+#ifdef NEWDB
+ else {
+#endif /* NEWDB */
if ((dbp = dbm_open(filename, O_RDONLY, 0)) == NULL) {
(void)fprintf(stderr,
"praliases: %s: %s\n", filename, strerror(errno));
@@ -133,6 +138,7 @@ main(argc, argv)
}
#ifdef NEWDB
}
-#endif
+#endif /* NEWDB */
+#endif /* NDBM */
exit(EX_OK);
}
diff --git a/usr.sbin/sendmail/rmail/Makefile b/usr.sbin/sendmail/rmail/Makefile
index eb2fb48..731e81c 100644
--- a/usr.sbin/sendmail/rmail/Makefile
+++ b/usr.sbin/sendmail/rmail/Makefile
@@ -1,6 +1,13 @@
# @(#)Makefile 8.1 (Berkeley) 5/31/93
+# $Id$
PROG= rmail
-MAN8= rmail.0
+MAN8= rmail.8
+
+# If you want to have your rmail queuing the mail only, uncomment the
+# following:
+# CFLAGS+= -DQUEUE_ONLY
+
+BINDIR= /bin
.include <bsd.prog.mk>
diff --git a/usr.sbin/sendmail/rmail/rmail.c b/usr.sbin/sendmail/rmail/rmail.c
index 29af02a..f330622 100644
--- a/usr.sbin/sendmail/rmail/rmail.c
+++ b/usr.sbin/sendmail/rmail/rmail.c
@@ -29,6 +29,8 @@
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
+ *
+ * $Id: rmail.c,v 1.4 1997/02/22 16:13:27 peter Exp $
*/
#ifndef lint
@@ -103,7 +105,7 @@ main(argc, argv)
debug = 0;
domain = "UUCP"; /* Default "domain". */
- while ((ch = getopt(argc, argv, "D:T")) != EOF)
+ while ((ch = getopt(argc, argv, "D:T")) != -1)
switch (ch) {
case 'T':
debug = 1;
@@ -121,6 +123,8 @@ main(argc, argv)
if (argc < 1)
usage();
+ fplen = fptlen = 0;
+ addrp = "";
from_path = from_sys = from_user = NULL;
for (offset = 0;;) {
@@ -232,7 +236,11 @@ main(argc, argv)
i = 0;
args[i++] = _PATH_SENDMAIL; /* Build sendmail's argument list. */
args[i++] = "-oee"; /* No errors, just status. */
+#ifdef QUEUE_ONLY
args[i++] = "-odq"; /* Queue it, don't try to deliver. */
+#else
+ args[i++] = "-odi"; /* Deliver in foreground. */
+#endif
args[i++] = "-oi"; /* Ignore '.' on a line by itself. */
/* set from system and protocol used */
diff --git a/usr.sbin/sendmail/smrsh/Makefile b/usr.sbin/sendmail/smrsh/Makefile
index f2629a8..0572ec6 100644
--- a/usr.sbin/sendmail/smrsh/Makefile
+++ b/usr.sbin/sendmail/smrsh/Makefile
@@ -1,8 +1,8 @@
# @(#)Makefile 8.1 (Berkeley) 7/2/95
PROG= smrsh
-MAN8= smrsh.0
-CFLAGS+=-I${.CURDIR}/../src -DNDBM -DNEWDB
+MAN8= smrsh.8
+CFLAGS+=-I${.CURDIR}/../src -DNEWDB
-.include "../../Makefile.inc"
+BINDIR= /usr/libexec
.include <bsd.prog.mk>
diff --git a/usr.sbin/sendmail/smrsh/smrsh.8 b/usr.sbin/sendmail/smrsh/smrsh.8
index a28bd0a..9615bf6 100644
--- a/usr.sbin/sendmail/smrsh/smrsh.8
+++ b/usr.sbin/sendmail/smrsh/smrsh.8
@@ -59,7 +59,7 @@ limits the set of programs that he or she can execute.
Briefly,
.I smrsh
limits programs to be in the directory
-/usr/adm/sm.bin,
+/usr/libexec/sm.bin,
allowing the system administrator to choose the set of acceptable commands.
It also rejects any commands with the characters
`\`', `<', `>', `|', `;', `&', `$', `(', `)', `\er' (carriage return),
@@ -67,16 +67,15 @@ or `\en' (newline)
on the command line to prevent ``end run'' attacks.
.PP
Initial pathnames on programs are stripped,
-so forwarding to ``/usr/ucb/vacation'',
-``/usr/bin/vacation'',
+so forwarding to ``/usr/bin/vacation'',
``/home/server/mydir/bin/vacation'',
and
``vacation''
all actually forward to
-``/usr/adm/sm.bin/vacation''.
+``/usr/libexec/sm.bin/vacation''.
.PP
System administrators should be conservative about populating
-/usr/adm/sm.bin.
+/usr/libexec/sm.bin.
Reasonable additions are
.IR vacation (1),
.IR procmail (1),
@@ -95,11 +94,11 @@ it simply disallows execution of arbitrary programs.
Compilation should be trivial on most systems.
You may need to use \-DPATH=\e"\fIpath\fP\e"
to adjust the default search path
-(defaults to ``/bin:/usr/bin:/usr/ucb'')
+(defaults to ``/bin:/usr/bin'')
and/or \-DCMDBIN=\e"\fIdir\fP\e"
to change the default program directory
-(defaults to ``/usr/adm/sm.bin'').
+(defaults to ``/usr/libexec/sm.bin'').
.SH FILES
-/usr/adm/sm.bin \- directory for restricted programs
+/usr/libexec/sm.bin \- directory for restricted programs
.SH SEE ALSO
sendmail(8)
diff --git a/usr.sbin/sendmail/smrsh/smrsh.c b/usr.sbin/sendmail/smrsh/smrsh.c
index c3314e6..49201e9 100644
--- a/usr.sbin/sendmail/smrsh/smrsh.c
+++ b/usr.sbin/sendmail/smrsh/smrsh.c
@@ -33,7 +33,7 @@
*/
#ifndef lint
-static char sccsid[] = "@(#)smrsh.c 8.5 (Berkeley) 10/19/97";
+static char sccsid[] = "@(#)smrsh.c 8.4 (Berkeley) 11/11/95";
#endif /* not lint */
/*
@@ -58,8 +58,8 @@ static char sccsid[] = "@(#)smrsh.c 8.5 (Berkeley) 10/19/97";
** This is more restrictive than strictly necessary.
**
** To use this, edit /etc/sendmail.cf, search for ^Mprog, and
-** change P=/bin/sh to P=/usr/local/etc/smrsh, where this compiled
-** binary is installed /usr/local/etc/smrsh.
+** change P=/bin/sh to P=/usr/libexec/smrsh, where this compiled
+** binary is installed /usr/libexec/smrsh.
**
** This can be used on any version of sendmail.
**
@@ -76,11 +76,10 @@ static char sccsid[] = "@(#)smrsh.c 8.5 (Berkeley) 10/19/97";
#endif
#include <sysexits.h>
#include <syslog.h>
-#include <stdlib.h>
/* directory in which all commands must reside */
#ifndef CMDDIR
-# define CMDDIR "/usr/adm/sm.bin"
+# define CMDDIR "/usr/libexec/sm.bin"
#endif
/* characters disallowed in the shell "-c" argument */
@@ -88,7 +87,7 @@ static char sccsid[] = "@(#)smrsh.c 8.5 (Berkeley) 10/19/97";
/* default search path */
#ifndef PATH
-# define PATH "/bin:/usr/bin:/usr/ucb"
+# define PATH "/bin:/usr/bin"
#endif
main(argc, argv)
diff --git a/usr.sbin/sendmail/src/Makefile b/usr.sbin/sendmail/src/Makefile
index 0be76ad..fa51a76 100644
--- a/usr.sbin/sendmail/src/Makefile
+++ b/usr.sbin/sendmail/src/Makefile
@@ -17,31 +17,41 @@ PROG= sendmail
# spiral snail, but it will work.
DBMDEF= -DNEWDB
-CFLAGS+=-I${.CURDIR} ${DBMDEF} -DNETISO
+# if you don't want NIS support, comment out this line
+# FreeBSD supports NIS
+NIS= -DNIS
+
+# If you want tcp wrapper support, uncomment the following two lines
+#TCPWRAPPERSBASEDIR= /usr/local
+#TCPWRAPPERS= -DTCPWRAPPERS -I${TCPWRAPPERSBASEDIR}/include
+
+CFLAGS+=-I${.CURDIR} ${DBMDEF} ${NIS} ${TCPWRAPPERS} #-DNETISO
SRCS= alias.c arpadate.c clock.c collect.c conf.c convtime.c daemon.c \
deliver.c domain.c envelope.c err.c headers.c macro.c main.c map.c \
mci.c mime.c parseaddr.c queue.c readcf.c recipient.c safefile.c \
savemail.c srvrsmtp.c stab.c stats.c sysexits.c trace.c udb.c \
usersmtp.c util.c version.c
-DPADD=
-LDADD=
-MAN1= mailq.0 newaliases.0
-MAN5= aliases.0
-MAN8= sendmail.0
+DPADD= ${LIBUTIL}
+LDADD= -lutil
+.if defined(TCPWRAPPERS)
+LDADD+= -L${TCPWRAPPERSBASEDIR}/lib -lwrap
+.endif
+MAN1= mailq.1 newaliases.1
+MAN5= aliases.5
+MAN8= sendmail.8
LINKS= /usr/sbin/sendmail /usr/bin/newaliases \
- /usr/sbin/sendmail /usr/bin/mailq
+ /usr/sbin/sendmail /usr/bin/mailq \
+ /usr/sbin/sendmail /usr/bin/hoststat \
+ /usr/sbin/sendmail /usr/sbin/purgestat
BINDIR= /usr/sbin
BINOWN= root
-BINGRP= kmem
-BINMODE=6555
+BINMODE=4555
beforeinstall:
-# install -c -o ${BINOWN} -g ${BINGRP} -m 644 /dev/null \
-# ${DESTDIR}/etc/sendmail.fc
- install -c -o ${BINOWN} -g ${BINGRP} -m 644 /dev/null \
+ ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 644 /dev/null \
${DESTDIR}/var/log/sendmail.st
- install -c -o ${BINOWN} -g ${BINGRP} -m 444 ${.CURDIR}/sendmail.hf \
+ ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 444 ${.CURDIR}/sendmail.hf \
${DESTDIR}/usr/share/misc
.include <bsd.prog.mk>
diff --git a/usr.sbin/sendmail/src/Makefiles/Makefile.386BSD b/usr.sbin/sendmail/src/Makefiles/Makefile.386BSD
deleted file mode 100644
index 10b2791..0000000
--- a/usr.sbin/sendmail/src/Makefiles/Makefile.386BSD
+++ /dev/null
@@ -1,43 +0,0 @@
-# @(#)Makefile.386BSD 8.3 (Berkeley) 9/13/95
-
-PROG= sendmail
-
-# define the database format to use for aliases et al. Can be -DNEWDB (for
-# the new BSD database package -- this is preferred) or -DNDBM for the NDBM
-# database package. The old putrescent V7 DBM package is no longer
-# supported.
-# You can define both NEWDB and NDBM during a transition period; old
-# databases are read, but the new format will be used on any rebuilds. On
-# really gnarly systems, you can set this to null; it will crawl like a high
-# spiral snail, but it will work.
-DBMDEF= -DNEWDB
-
-CFLAGS+=-I${.CURDIR} ${DBMDEF} -DMIME
-
-SRCS= alias.c arpadate.c clock.c collect.c conf.c convtime.c daemon.c \
- deliver.c domain.c envelope.c err.c headers.c macro.c main.c map.c \
- mci.c mime.c parseaddr.c queue.c readcf.c recipient.c savemail.c \
- srvrsmtp.c stab.c stats.c sysexits.c trace.c udb.c usersmtp.c \
- util.c version.c
-DPADD=
-LDADD= $(LIBUTIL)
-MAN1= newaliases.0 mailq.0
-MAN5= aliases.0
-MAN8= sendmail.0
-LINKS= /usr/sbin/sendmail /usr/bin/newaliases \
- /usr/sbin/sendmail /usr/bin/mailq
-INSTALL=install
-BINDIR= /usr/sbin
-BINOWN= root
-BINGRP= kmem
-BINMODE=6555
-
-beforeinstall:
-# ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 644 /dev/null \
-# ${DESTDIR}/etc/sendmail.fc
- ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 644 /dev/null \
- ${DESTDIR}/var/log/sendmail.st
- ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 444 ${.CURDIR}/sendmail.hf \
- ${DESTDIR}/usr/share/misc
-
-.include <bsd.prog.mk>
diff --git a/usr.sbin/sendmail/src/Makefiles/Makefile.A-UX b/usr.sbin/sendmail/src/Makefiles/Makefile.A-UX
deleted file mode 100644
index 6a303c0..0000000
--- a/usr.sbin/sendmail/src/Makefiles/Makefile.A-UX
+++ /dev/null
@@ -1,113 +0,0 @@
-#
-# This Makefile is designed to work on the old "make" program. It does
-# not use the obj subdirectory. It also does not install documentation
-# automatically -- think of it as a quick start for sites that have the
-# old make program (I recommend that you get and port the new make if you
-# are going to be doing any signficant work on sendmail).
-#
-# @(#)Makefile.A-UX 8.12 (Berkeley) 9/13/95
-#
-# Tested on A/UX 3.1.
-#
-
-# use O=-O (usual) or O=-g (debugging)
-O= -O
-
-# define the database mechanisms available for map & alias lookups:
-# -DNDBM -- use new DBM
-# -DNEWDB -- use new Berkeley DB
-# -DNIS -- include NIS support
-# The really old (V7) DBM library is no longer supported.
-# See READ_ME for a description of how these flags interact.
-# If you are running A/UX prior to 3.1, delete -DNEWDB
-DBMDEF= -DNDBM -DNEWDB
-
-# environment definitions (e.g., -D_AIX3)
-ENVDEF= -D_POSIX_SOURCE
-
-# see also conf.h for additional compilation flags
-
-# include directories
-INCDIRS=
-
-# loader options
-LDOPTS=
-
-# library directories
-LIBDIRS=
-
-# libraries required on your system
-# If you are running A/UX prior to 3.1, delete -ldb
-LIBS= -ldbm -ldb -lposix -lmalloc
-
-# location of sendmail binary (usually /usr/sbin or /usr/lib)
-BINDIR= ${DESTDIR}/usr/lib
-
-# location of sendmail.st file (usually /var/log or /usr/lib)
-STDIR= ${DESTDIR}/usr/lib
-
-# location of sendmail.hf file (usually /usr/share/misc or /usr/lib)
-HFDIR= ${DESTDIR}/usr/lib
-
-# additional .o files needed
-OBJADD=
-
-################### end of user configuration flags ######################
-
-CFLAGS= -I. $O ${INCDIRS} ${DBMDEF} ${ENVDEF}
-
-OBJS= alias.o arpadate.o clock.o collect.o conf.o convtime.o daemon.o \
- deliver.o domain.o envelope.o err.o headers.o macro.o main.o \
- map.o mci.o mime.o parseaddr.o queue.o readcf.o recipient.o \
- savemail.o srvrsmtp.o stab.o stats.o sysexits.o \
- trace.o udb.o usersmtp.o util.o version.o ${OBJADD}
-
-LINKS= ${DESTDIR}/usr/ucb/newaliases ${DESTDIR}/usr/ucb/mailq
-INSTALL=install
-BINOWN= root
-BINGRP= sys
-BINMODE=6555
-
-ALL= sendmail aliases.0 mailq.0 newaliases.0 sendmail.0
-
-all: ${ALL}
-
-sendmail: ${BEFORE} ${OBJS}
- ${CC} -o sendmail ${LDOPTS} ${OBJS} ${LIBDIRS} ${LIBS}
-
-#NROFF= nroff -h
-NROFF= groff -Tascii
-MANDOC= -mandoc
-
-aliases.0: aliases.5
- ${NROFF} ${MANDOC} aliases.5 > aliases.0
-
-mailq.0: mailq.1
- ${NROFF} ${MANDOC} mailq.1 > mailq.0
-
-newaliases.0: newaliases.1
- ${NROFF} ${MANDOC} newaliases.1 > newaliases.0
-
-sendmail.0: sendmail.8
- ${NROFF} ${MANDOC} sendmail.8 > sendmail.0
-
-install: install-sendmail install-docs
-
-install-sendmail: sendmail
- ${INSTALL} -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} sendmail ${BINDIR}
- for i in ${LINKS}; do ; rm -f $$i; ln -s ${BINDIR}/sendmail $$i; done
- ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 644 /dev/null \
- ${STDIR}/sendmail.st
- ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 444 sendmail.hf ${HFDIR}
-
-# doesn't actually install them -- you may want to install pre-nroff versions
-install-docs: aliases.0 mailq.0 newaliases.0 sendmail.0
-
-clean:
- rm -f ${OBJS} sendmail aliases.0 mailq.0 newaliases.0 sendmail.0
-
-# dependencies
-# gross overkill, and yet still not quite enough....
-${OBJS}: sendmail.h conf.h
-
-depend:
diff --git a/usr.sbin/sendmail/src/Makefiles/Makefile.AIX b/usr.sbin/sendmail/src/Makefiles/Makefile.AIX
deleted file mode 100644
index 2858b93..0000000
--- a/usr.sbin/sendmail/src/Makefiles/Makefile.AIX
+++ /dev/null
@@ -1,116 +0,0 @@
-#
-# This Makefile is designed to work on the old "make" program. It does
-# not use the obj subdirectory. It also does not install documentation
-# automatically -- think of it as a quick start for sites that have the
-# old make program (I recommend that you get and port the new make if you
-# are going to be doing any signficant work on sendmail).
-#
-# This has been tested on AIX 3.1.5 and 3.2.3e.
-#
-# @(#)Makefile.AIX 8.10 (Berkeley) 9/13/95
-#
-
-# use O=-O (usual) or O=-g (debugging)
-# you can use -O3 on AIX 3.2.4 or greater ONLY!
-O= -g
-
-# define the database mechanism used for alias lookups:
-# -DNDBM -- use new DBM
-# -DNEWDB -- use new Berkeley DB
-# -DNIS -- include NIS support
-# The really old (V7) DBM library is no longer supported.
-# See READ_ME for a description of how these flags interact.
-#
-DBMDEF= -DNDBM -DNEWDB -DNIS
-#
-# If you did not install the NEWDB on your AIX platform, use:
-#DBMDEF=-DNDBM
-
-# environment definitions (e.g., -D_AIX3)
-ENVDEF= -D_AIX3
-
-# see also conf.h for additional compilation flags
-
-# include directories
-#INCDIRS=-I/usr/sww/include
-
-# library directories
-#LIBDIRS=-L/usr/sww/lib
-
-# libraries required on your system -- add -ls if you define USEGETCONFATTR
-LIBS= -ldbm -ldb
-#
-# If you did not install the NEWDB on your AIX platform, use:
-#LIBS= -ldbm
-
-# location of sendmail binary (usually /usr/sbin or /usr/lib)
-BINDIR= ${DESTDIR}/usr/sbin
-
-# location of sendmail.st file (usually /var/log or /usr/lib)
-STDIR= ${DESTDIR}/etc
-
-# location of sendmail.hf file (usually /usr/share/misc or /usr/lib)
-HFDIR= ${DESTDIR}/usr/lib
-
-# additional .o files needed
-OBJADD=
-
-################### end of user configuration flags ######################
-
-CFLAGS= -I. $O ${INCDIRS} ${DBMDEF} ${ENVDEF}
-
-OBJS= alias.o arpadate.o clock.o collect.o conf.o convtime.o daemon.o \
- deliver.o domain.o envelope.o err.o headers.o macro.o main.o \
- map.o mci.o mime.o parseaddr.o queue.o readcf.o recipient.o \
- savemail.o srvrsmtp.o stab.o stats.o sysexits.o \
- trace.o udb.o usersmtp.o util.o version.o ${OBJADD}
-
-LINKS= ${DESTDIR}/usr/sbin/newaliases ${DESTDIR}/usr/sbin/mailq
-INSTALL=/usr/ucb/install
-BINOWN= root
-BINGRP= system
-BINMODE=6555
-
-ALL= sendmail aliases.0 mailq.0 newaliases.0 sendmail.0
-
-all: ${ALL}
-
-sendmail: ${BEFORE} ${OBJS}
- ${CC} -o sendmail ${OBJS} ${LIBDIRS} ${LIBS}
-
-#NROFF= nroff -h
-NROFF= groff -Tascii
-MANDOC= -mandoc
-
-aliases.0: aliases.5
- ${NROFF} ${MANDOC} aliases.5 > aliases.0
-
-mailq.0: mailq.1
- ${NROFF} ${MANDOC} mailq.1 > mailq.0
-
-newaliases.0: newaliases.1
- ${NROFF} ${MANDOC} newaliases.1 > newaliases.0
-
-sendmail.0: sendmail.8
- ${NROFF} ${MANDOC} sendmail.8 > sendmail.0
-
-install: install-sendmail install-docs
-
-install-sendmail: sendmail
- ${INSTALL} -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} sendmail ${BINDIR}
- for i in ${LINKS}; do rm -f $$i; ln -s ${BINDIR}/sendmail $$i; done
- ${INSTALL} -o ${BINOWN} -g ${BINGRP} -m 644 /dev/null \
- ${STDIR}/sendmail.st
- ${INSTALL} -o ${BINOWN} -g ${BINGRP} -m 444 sendmail.hf ${HFDIR}
-
-# doesn't actually install them -- you may want to install pre-nroff versions
-install-docs: aliases.0 mailq.0 newaliases.0 sendmail.0
-
-clean:
- rm -f ${OBJS} sendmail aliases.0 mailq.0 newaliases.0 sendmail.0
-
-# dependencies
-# gross overkill, and yet still not quite enough....
-${OBJS}: sendmail.h conf.h
-
-depend:
diff --git a/usr.sbin/sendmail/src/Makefiles/Makefile.Altos b/usr.sbin/sendmail/src/Makefiles/Makefile.Altos
deleted file mode 100644
index 2b37f92..0000000
--- a/usr.sbin/sendmail/src/Makefiles/Makefile.Altos
+++ /dev/null
@@ -1,109 +0,0 @@
-#
-# This Makefile is designed to work on the old "make" program. It does
-# not use the obj subdirectory. It also does not install documentation
-# automatically -- think of it as a quick start for sites that have the
-# old make program (I recommend that you get and port the new make if you
-# are going to be doing any signficant work on sendmail).
-#
-# This has been tested on Altos System V.
-#
-# @(#)Makefile.Altos 8.4 (Berkeley) 9/13/95
-#
-
-# use O=-O (usual) or O=-g (debugging)
-O= -O
-
-# define the database mechanisms available for map & alias lookups:
-# -DNDBM -- use new DBM
-# -DNEWDB -- use new Berkeley DB
-# -DNIS -- include NIS support
-# The really old (V7) DBM library is no longer supported.
-# See READ_ME for a description of how these flags interact.
-#
-DBMDEF=
-
-# environment definitions (e.g., -D_AIX3)
-ENVDEF= -DALTOS_SYS_V
-
-# see also conf.h for additional compilation flags
-
-# include directories
-INCDIRS=
-
-# library directories
-LIBDIRS=
-
-# libraries required on your system
-LIBS= -lsocket -lrpc
-
-# location of sendmail binary (usually /usr/sbin or /usr/lib)
-BINDIR= ${DESTDIR}/usr/lib
-
-# location of sendmail.st file (usually /var/log or /usr/lib)
-STDIR= ${DESTDIR}/usr/lib
-
-# location of sendmail.hf file (usually /usr/share/misc or /usr/lib)
-HFDIR= ${DESTDIR}/usr/lib
-
-# additional .o files needed
-OBJADD=
-
-################### end of user configuration flags ######################
-
-CFLAGS= -I. $O ${INCDIRS} ${DBMDEF} ${ENVDEF}
-
-OBJS= alias.o arpadate.o clock.o collect.o conf.o convtime.o daemon.o \
- deliver.o domain.o envelope.o err.o headers.o macro.o main.o \
- map.o mci.o mime.o parseaddr.o queue.o readcf.o recipient.o \
- savemail.o srvrsmtp.o stab.o stats.o sysexits.o \
- trace.o udb.o usersmtp.o util.o version.o ${OBJADD}
-
-LINKS= ${DESTDIR}/usr/ucb/newaliases ${DESTDIR}/usr/ucb/mailq
-INSTALL=install
-BINOWN= root
-BINGRP= kmem
-BINMODE=6555
-
-ALL= sendmail aliases.0 mailq.0 newaliases.0 sendmail.0
-
-all: ${ALL}
-
-sendmail: ${BEFORE} ${OBJS}
- ${CC} -o sendmail ${OBJS} ${LIBDIRS} ${LIBS}
-
-#NROFF= nroff -h
-NROFF= groff -Tascii
-MANDOC= -mandoc
-
-aliases.0: aliases.5
- ${NROFF} ${MANDOC} aliases.5 > aliases.0
-
-mailq.0: mailq.1
- ${NROFF} ${MANDOC} mailq.1 > mailq.0
-
-newaliases.0: newaliases.1
- ${NROFF} ${MANDOC} newaliases.1 > newaliases.0
-
-sendmail.0: sendmail.8
- ${NROFF} ${MANDOC} sendmail.8 > sendmail.0
-
-install: install-sendmail install-docs
-
-install-sendmail: sendmail
- ${INSTALL} -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} sendmail ${BINDIR}
- for i in ${LINKS}; do rm -f $$i; ln -s ${BINDIR}/sendmail $$i; done
- ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 644 /dev/null \
- ${STDIR}/sendmail.st
- ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 444 sendmail.hf ${HFDIR}
-
-# doesn't actually install them -- you may want to install pre-nroff versions
-install-docs: aliases.0 mailq.0 newaliases.0 sendmail.0
-
-clean:
- rm -f ${OBJS} sendmail aliases.0 mailq.0 newaliases.0 sendmail.0
-
-# dependencies
-# gross overkill, and yet still not quite enough....
-${OBJS}: sendmail.h conf.h
-
-depend:
diff --git a/usr.sbin/sendmail/src/Makefiles/Makefile.BSD-OS b/usr.sbin/sendmail/src/Makefiles/Makefile.BSD-OS
deleted file mode 100644
index 7b79abb..0000000
--- a/usr.sbin/sendmail/src/Makefiles/Makefile.BSD-OS
+++ /dev/null
@@ -1,37 +0,0 @@
-#
-# This Makefile is for BSDI boxes running BSD-OS (formerly BSD-386).
-#
-# @(#)Makefile.BSD-OS 8.4 (Berkeley) 9/13/95
-#
-
-PROG= sendmail
-DBMDEF= -DNEWDB
-CFLAGS+=-I${.CURDIR} ${DBMDEF} -DNETISO
-
-SRCS= alias.c arpadate.c clock.c collect.c conf.c convtime.c daemon.c \
- deliver.c domain.c envelope.c err.c headers.c macro.c main.c map.c \
- mci.c mime.c parseaddr.c queue.c readcf.c recipient.c savemail.c \
- srvrsmtp.c stab.c stats.c sysexits.c trace.c udb.c usersmtp.c \
- util.c version.c
-DPADD= ${LIBUTIL} ${LIBKVM}
-LDADD= -lutil -lkvm
-MAN1= mailq.0 newaliases.0
-MAN5= aliases.0
-MAN8= sendmail.0
-LINKS= /usr/sbin/sendmail /usr/bin/newaliases \
- /usr/sbin/sendmail /usr/bin/mailq
-INSTALL=install
-BINDIR= /usr/sbin
-BINOWN= root
-BINGRP= kmem
-BINMODE=6555
-
-beforeinstall:
-# ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 644 /dev/null \
-# ${DESTDIR}/etc/sendmail.fc
- ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 644 /dev/null \
- ${DESTDIR}/var/log/sendmail.st
- ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 444 ${.CURDIR}/sendmail.hf \
- ${DESTDIR}/usr/share/misc
-
-.include <bsd.prog.mk>
diff --git a/usr.sbin/sendmail/src/Makefiles/Makefile.BSD43 b/usr.sbin/sendmail/src/Makefiles/Makefile.BSD43
deleted file mode 100644
index 37423c7..0000000
--- a/usr.sbin/sendmail/src/Makefiles/Makefile.BSD43
+++ /dev/null
@@ -1,128 +0,0 @@
-#
-# This Makefile is designed to work on the old "make" program. It does
-# not use the obj subdirectory. It also does not install documentation
-# automatically -- think of it as a quick start for sites that have the
-# old make program (I recommend that you get and port the new make if you
-# are going to be doing any signficant work on sendmail).
-#
-# This is based on work from Jim Oldroyd -- I believe he was
-# using a fairly old Mt Xinu port.
-#
-# It should also work on UMIPS-BSD from MIPS, if you still have
-# any lying around.
-#
-# @(#)Makefile.BSD43 8.8 (Berkeley) 9/13/95
-#
-
-# use O=-O (usual) or O=-g (debugging)
-O= -O
-
-# define the database mechanism used for alias lookups:
-# -DNDBM -- use new DBM
-# -DNEWDB -- use new Berkeley DB
-# -DNIS -- include NIS support
-# The really old (V7) DBM library is no longer supported.
-# See READ_ME for a description of how these flags interact.
-#
-DBMDEF= -DNDBM
-
-# environment definitions (e.g., -D_AIX3)
-ENVDEF= -DoldBSD43
-
-# see also conf.h for additional compilation flags
-
-# include directories
-#INCDIRS=-I/usr/sww/include
-
-# library directories
-#LIBDIRS=-L/usr/sww/lib
-
-# libraries required on your system
-LIBS= -ldbm -lresolv -ll
-
-# location of sendmail binary (usually /usr/sbin or /usr/lib)
-BINDIR= ${DESTDIR}/usr/lib
-
-# location of sendmail.st file (usually /var/log or /usr/lib)
-STDIR= ${DESTDIR}/usr/lib
-
-# location of sendmail.hf file (usually /usr/share/misc or /usr/lib)
-HFDIR= ${DESTDIR}/usr/lib
-
-# additional .o files needed
-OBJADD=
-
-# additional pseudo-sources needed
-BEFORE= unistd.h stddef.h stdlib.h dirent.h sys/time.h
-
-################### end of user configuration flags ######################
-
-CFLAGS= -I. $O ${INCDIRS} ${DBMDEF} ${ENVDEF}
-
-OBJS= alias.o arpadate.o clock.o collect.o conf.o convtime.o daemon.o \
- deliver.o domain.o envelope.o err.o headers.o macro.o main.o \
- map.o mci.o mime.o parseaddr.o queue.o readcf.o recipient.o \
- savemail.o srvrsmtp.o stab.o stats.o sysexits.o \
- trace.o udb.o usersmtp.o util.o version.o ${OBJADD}
-
-LINKS= ${DESTDIR}/usr/ucb/newaliases ${DESTDIR}/usr/ucb/mailq
-INSTALL=install
-BINOWN= root
-BINGRP= kmem
-BINMODE=6555
-
-ALL= sendmail aliases.0 mailq.0 newaliases.0 sendmail.0
-
-all: ${ALL}
-
-sendmail: ${BEFORE} ${OBJS}
- ${CC} -o sendmail ${OBJS} ${LIBDIRS} ${LIBS}
-
-unistd.h stddef.h stdlib.h sys/time.h:
- cp /dev/null $@
-
-sys/time.h: sys
-
-sys:
- mkdir sys
-
-dirent.h:
- echo "#include <sys/dir.h>" > dirent.h
- echo "#define dirent direct" >> dirent.h
-
-#NROFF= nroff -h
-NROFF= groff -Tascii
-MANDOC= -mandoc
-
-aliases.0: aliases.5
- ${NROFF} ${MANDOC} aliases.5 > aliases.0
-
-mailq.0: mailq.1
- ${NROFF} ${MANDOC} mailq.1 > mailq.0
-
-newaliases.0: newaliases.1
- ${NROFF} ${MANDOC} newaliases.1 > newaliases.0
-
-sendmail.0: sendmail.8
- ${NROFF} ${MANDOC} sendmail.8 > sendmail.0
-
-install: install-sendmail install-docs
-
-install-sendmail: sendmail
- ${INSTALL} -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} sendmail ${BINDIR}
- for i in ${LINKS}; do rm -f $$i; ln -s ${BINDIR}/sendmail $$i; done
- ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 644 /dev/null \
- ${STDIR}/sendmail.st
- ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 444 sendmail.hf ${HFDIR}
-
-# doesn't actually install them -- you may want to install pre-nroff versions
-install-docs: aliases.0 mailq.0 newaliases.0 sendmail.0
-
-clean:
- rm -f ${OBJS} sendmail aliases.0 mailq.0 newaliases.0 sendmail.0
-
-# dependencies
-# gross overkill, and yet still not quite enough....
-${OBJS}: sendmail.h conf.h
-
-depend:
diff --git a/usr.sbin/sendmail/src/Makefiles/Makefile.CLIX b/usr.sbin/sendmail/src/Makefiles/Makefile.CLIX
deleted file mode 100644
index debb2e1..0000000
--- a/usr.sbin/sendmail/src/Makefiles/Makefile.CLIX
+++ /dev/null
@@ -1,119 +0,0 @@
-#
-# This makefile is for clipper-based Intergraph systems running CLIX.
-# It and the defines supporting it in the source tree should be considered
-# alpha-quality and used at own risk.
-#
-# Porting done for CICNet, Inc., on behalf the Michigan State Department
-# of Natural Resources.
-#
-# --Paul Southworth <pauls@cic.net>
-#
-# @(#)Makefile.CLIX 8.5 (Berkeley) 9/13/95
-#
-
-# make sure the shell constructs below use the right shell
-SHELL= /bin/sh
-
-# use O=-O (usual) or O=-g (debugging)
-O= -O
-
-CC= gcc
-
-# define the database mechanism used for alias lookups:
-# -DNDBM -- use new DBM
-# -DNEWDB -- use new Berkeley DB
-# -DNIS -- include NIS support
-# The really old (V7) DBM library is no longer supported.
-# See READ_ME for a description of how these flags interact.
-#
-DBMDEF= -DNDBM
-
-# environment definitions (e.g., -D_AIX3)
-ENVDEF= -DCLIX
-
-# see also conf.h for additional compilation flags
-
-# include directories
-INCDIRS= -I/usr/include
-
-# library directories
-LIBDIRS=
-
-# libraries required on your system
-LIBS= -lnsl -lbsd
-
-# location of sendmail binary (usually /usr/sbin or /usr/lib)
-BINDIR= ${DESTDIR}/usr/lib
-
-# location of sendmail.st file (usually /var/log or /usr/lib)
-STDIR= ${DESTDIR}/usr/lib
-
-# location of sendmail.hf file (usually /usr/share/misc or /usr/lib)
-HFDIR= ${DESTDIR}/usr/lib
-
-# additional .o files needed
-OBJADD= getusershell.o
-
-################### end of user configuration flags ######################
-
-CFLAGS= -I. $O ${INCDIRS} ${DBMDEF} ${ENVDEF}
-
-OBJS= alias.o arpadate.o clock.o collect.o conf.o convtime.o daemon.o \
- deliver.o domain.o envelope.o err.o headers.o macro.o main.o \
- map.o mci.o mime.o parseaddr.o queue.o readcf.o recipient.o \
- savemail.o srvrsmtp.o stab.o stats.o sysexits.o \
- trace.o udb.o usersmtp.o util.o version.o ${OBJADD}
-
-LINKS= ${DESTDIR}/usr/bin/newaliases ${DESTDIR}/usr/bin/mailq
-INSTALL=cp
-BINOWN= root
-BINGRP= mail
-BINMODE=6555
-
-ALL= sendmail # aliases.0 mailq.0 newaliases.0 sendmail.0
-
-all: ${ALL}
-
-sendmail: ${BEFORE} ${OBJS}
- ${CC} -o sendmail ${OBJS} ${LIBDIRS} ${LIBS}
-
-#NROFF= nroff -h
-NROFF= groff -Tascii
-MANDOC= -mandoc
-
-aliases.0: aliases.5
- ${NROFF} ${MANDOC} aliases.5 > aliases.0
-
-mailq.0: mailq.1
- ${NROFF} ${MANDOC} mailq.1 > mailq.0
-
-newaliases.0: newaliases.1
- ${NROFF} ${MANDOC} newaliases.1 > newaliases.0
-
-sendmail.0: sendmail.8
- ${NROFF} ${MANDOC} sendmail.8 > sendmail.0
-
-install: install-sendmail #install-docs
-
-install-sendmail: sendmail
- #${INSTALL} -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} sendmail ${BINDIR}
- ${INSTALL} sendmail ${BINDIR}
- chmod ${BINMODE} ${BINDIR}/sendmail
- for i in ${LINKS}; do rm -f $$i; ln -s ${BINDIR}/sendmail $$i; done
- #${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 644 /dev/null \
- # ${STDIR}/sendmail.st
- ${INSTALL} /dev/null ${STDIR}/sendmail.st
- #${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 444 sendmail.hf ${HFDIR}
- ${INSTALL} sendmail.hf ${HFDIR}
-
-# doesn't actually install them -- you may want to install pre-nroff versions
-install-docs: aliases.0 mailq.0 newaliases.0 sendmail.0
-
-clean:
- rm -f ${OBJS} sendmail #aliases.0 mailq.0 newaliases.0 sendmail.0
-
-# dependencies
-# gross overkill, and yet still not quite enough....
-${OBJS}: sendmail.h conf.h
-
-depend:
diff --git a/usr.sbin/sendmail/src/Makefiles/Makefile.CSOS b/usr.sbin/sendmail/src/Makefiles/Makefile.CSOS
deleted file mode 100644
index 8cf3229..0000000
--- a/usr.sbin/sendmail/src/Makefiles/Makefile.CSOS
+++ /dev/null
@@ -1,114 +0,0 @@
-#
-# This Makefile is designed to work on the old "make" program. It does
-# not use the obj subdirectory. It also does not install documentation
-# automatically -- which is fine since there is no nroff under CSOS.
-#
-# Contributed by Scott Bolte <scott@craycos.com>.
-#
-# @(#)Makefile.CSOS 8.6 (Berkeley) 9/13/95
-#
-
-# use O=-O (usual) or O=-g (debugging)
-O= -O
-
-# define the database mechanisms available for map & alias lookups:
-# -DNDBM -- use new DBM
-# -DNEWDB -- use new Berkeley DB
-# -DNIS -- include NIS support
-# The really old (V7) DBM library is no longer supported.
-# See READ_ME for a description of how these flags interact.
-#
-# Contact CCC for new db support. If all goes well, it should be
-# available soon.
-#
-DBMDEF=
-
-# environment definitions (e.g., -D_AIX3)
-ENVDEF=
-
-# see also conf.h for additional compilation flags
-
-# include directories
-#INCDIRS=-I/usr/sww/include
-
-# loader options
-LDOPTS=
-
-# library directories
-#LIBDIRS=-L/usr/sww/lib
-
-# libraries required on your system
-#LIBS= -ldb -ldbm
-LIBS= -lnet
-
-# location of sendmail binary (usually /usr/sbin or /usr/lib)
-BINDIR= ${DESTDIR}/usr/lib
-
-# location of sendmail.st file (usually /var/log or /usr/lib)
-STDIR= ${DESTDIR}/usr/lib
-
-# location of sendmail.hf file (usually /usr/share/misc or /usr/lib)
-HFDIR= ${DESTDIR}/usr/lib
-
-# additional .o files needed
-OBJADD=
-
-################### end of user configuration flags ######################
-
-CFLAGS= -I. $O ${INCDIRS} ${DBMDEF} ${ENVDEF}
-
-OBJS= alias.o arpadate.o clock.o collect.o conf.o convtime.o daemon.o \
- deliver.o domain.o envelope.o err.o headers.o macro.o main.o \
- map.o mci.o mime.o parseaddr.o queue.o readcf.o recipient.o \
- savemail.o srvrsmtp.o stab.o stats.o sysexits.o \
- trace.o udb.o usersmtp.o util.o version.o ${OBJADD}
-
-LINKS= ${DESTDIR}/usr/ucb/newaliases ${DESTDIR}/usr/ucb/mailq
-INSTALL=cpset
-BINOWN= root
-BINGRP= kmem
-BINMODE=6555
-SHELL= /bin/sh
-
-ALL= sendmail
-
-all: ${ALL}
-
-sendmail: ${BEFORE} ${OBJS}
- ${CC} -o sendmail ${LDOPTS} ${OBJS} ${LIBDIRS} ${LIBS}
-
-#NROFF= nroff -h
-NROFF= groff -Tascii
-MANDOC= -mandoc
-
-aliases.0: aliases.5
- ${NROFF} ${MANDOC} aliases.5 > aliases.0
-
-mailq.0: mailq.1
- ${NROFF} ${MANDOC} mailq.1 > mailq.0
-
-newaliases.0: newaliases.1
- ${NROFF} ${MANDOC} newaliases.1 > newaliases.0
-
-sendmail.0: sendmail.8
- ${NROFF} ${MANDOC} sendmail.8 > sendmail.0
-
-install: install-sendmail
-
-install-sendmail: sendmail
- ${INSTALL} sendmail ${BINDIR} ${BINMODE} ${BINOWN} ${BINGRP}
- for i in ${LINKS}; do rm -f $$i; ln -s ${BINDIR}/sendmail $$i; done
- ${INSTALL} /dev/null ${STDIR}/sendmail.st 644 ${BINOWN} ${BINGRP}
- ${INSTALL} sendmail.hf ${HFDIR} 444 ${BINOWN} ${BINGRP}
-
-# doesn't actually install them -- you may want to install pre-nroff versions
-install-docs: aliases.0 mailq.0 newaliases.0 sendmail.0
-
-clean:
- rm -f ${OBJS} sendmail aliases.0 mailq.0 newaliases.0 sendmail.0
-
-# dependencies
-# gross overkill, and yet still not quite enough....
-${OBJS}: sendmail.h conf.h
-
-depend:
diff --git a/usr.sbin/sendmail/src/Makefiles/Makefile.ConvexOS b/usr.sbin/sendmail/src/Makefiles/Makefile.ConvexOS
deleted file mode 100644
index e158b17..0000000
--- a/usr.sbin/sendmail/src/Makefiles/Makefile.ConvexOS
+++ /dev/null
@@ -1,110 +0,0 @@
-#
-# This Makefile is designed to work on the old "make" program. It does
-# not use the obj subdirectory. It also does not install documentation
-# automatically -- think of it as a quick start for sites that have the
-# old make program (I recommend that you get and port the new make if you
-# are going to be doing any signficant work on sendmail).
-#
-# This has been tested on CxOS 11.0 beta 1 and 10.x.
-#
-# @(#)Makefile.ConvexOS 8.9 (Berkeley) 9/13/95
-#
-
-
-# use O=-O (usual) or O=-g (debugging)
-O= -g -D__STDC__ -d non_int_bit_field
-
-# define the database mechanisms available for map & alias lookups:
-# -DNDBM -- use new DBM
-# -DNEWDB -- use new Berkeley DB
-# -DNIS -- include NIS support
-# The really old (V7) DBM library is no longer supported.
-# See READ_ME for a description of how these flags interact.
-#
-DBMDEF= -DNDBM -DYPCOMPAT -DNIS
-
-# environment definitions (e.g., -D_AIX3)
-ENVDEF=
-
-# see also conf.h for additional compilation flags
-
-# include directories
-#INCDIRS=-I/usr/sww/include
-
-# library directories
-#LIBDIRS=-L/usr/sww/lib
-
-# libraries required on your system
-LIBS=
-
-# location of sendmail binary (usually /usr/sbin or /usr/lib)
-BINDIR= ${DESTDIR}/usr/lib
-
-# location of sendmail.st file (usually /var/log or /usr/lib)
-STDIR= ${DESTDIR}/usr/lib
-
-# location of sendmail.hf file (usually /usr/share/misc or /usr/lib)
-HFDIR= ${DESTDIR}/usr/lib
-
-# additional .o files needed
-OBJADD=
-
-################### end of user configuration flags ######################
-
-CFLAGS= -I. $O ${INCDIRS} ${DBMDEF} ${ENVDEF}
-
-OBJS= alias.o arpadate.o clock.o collect.o conf.o convtime.o daemon.o \
- deliver.o domain.o envelope.o err.o headers.o macro.o main.o \
- map.o mci.o mime.o parseaddr.o queue.o readcf.o recipient.o \
- savemail.o srvrsmtp.o stab.o stats.o sysexits.o \
- trace.o udb.o usersmtp.o util.o version.o ${OBJADD}
-
-LINKS= ${DESTDIR}/usr/ucb/newaliases ${DESTDIR}/usr/ucb/mailq
-INSTALL=install
-BINOWN= root
-BINGRP= kmem
-BINMODE=6555
-
-ALL= sendmail aliases.0 mailq.0 newaliases.0 sendmail.0
-
-all: ${ALL}
-
-sendmail: ${BEFORE} ${OBJS}
- ${CC} -o sendmail ${OBJS} ${LIBDIRS} ${LIBS}
-
-#NROFF= nroff -h
-NROFF= groff -Tascii
-MANDOC= -mandoc
-
-aliases.0: aliases.5
- ${NROFF} ${MANDOC} aliases.5 > aliases.0
-
-mailq.0: mailq.1
- ${NROFF} ${MANDOC} mailq.1 > mailq.0
-
-newaliases.0: newaliases.1
- ${NROFF} ${MANDOC} newaliases.1 > newaliases.0
-
-sendmail.0: sendmail.8
- ${NROFF} ${MANDOC} sendmail.8 > sendmail.0
-
-install: install-sendmail install-docs
-
-install-sendmail: sendmail
- ${INSTALL} -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} sendmail ${BINDIR}
- for i in ${LINKS}; do rm -f $$i; ln -s ${BINDIR}/sendmail $$i; done
- ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 644 /dev/null \
- ${STDIR}/sendmail.st
- ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 444 sendmail.hf ${HFDIR}
-
-# doesn't actually install them -- you may want to install pre-nroff versions
-install-docs: aliases.0 mailq.0 newaliases.0 sendmail.0
-
-clean:
- rm -f ${OBJS} sendmail aliases.0 mailq.0 newaliases.0 sendmail.0
-
-# dependencies
-# gross overkill, and yet still not quite enough....
-${OBJS}: sendmail.h conf.h
-
-depend:
diff --git a/usr.sbin/sendmail/src/Makefiles/Makefile.Dell b/usr.sbin/sendmail/src/Makefiles/Makefile.Dell
deleted file mode 100644
index ffce495..0000000
--- a/usr.sbin/sendmail/src/Makefiles/Makefile.Dell
+++ /dev/null
@@ -1,117 +0,0 @@
-#
-# This Makefile is designed to work on the old "make" program. It does
-# not use the obj subdirectory. It also does not install documentation
-# automatically -- think of it as a quick start for sites that have the
-# old make program (I recommend that you get and port the new make if you
-# are going to be doing any signficant work on sendmail).
-#
-# Based on a Makefile for Dell SVR4 Issue 2.2 from Kimmo Suominen
-# <kim@grendel.lut.fi> -- I haven't tested this myself. It may
-# work on other SVR4 ports.
-#
-# @(#)Makefile.Dell 8.7 (Berkeley) 9/13/95
-#
-
-# make sure the shell constructs below use the right shell
-SHELL= /bin/sh
-
-# use O=-O (usual) or O=-g (debugging)
-O= -O2
-
-CC= gcc
-#DESTDIR=/usr/local/sendmail
-
-# define the database mechanism used for alias lookups:
-# -DNDBM -- use new DBM
-# -DNEWDB -- use new Berkeley DB
-# -DNIS -- include NIS support
-# The really old (V7) DBM library is no longer supported.
-# See READ_ME for a description of how these flags interact.
-#
-DBMDEF= -DNEWDB -DNDBM
-
-# environment definitions (e.g., -D_AIX3)
-ENVDEF= -D__svr4__
-
-# see also conf.h for additional compilation flags
-
-# include directories
-INCDIRS=
-
-# library directories
-LIBDIRS=
-
-# libraries required on your system
-LIBS= -ldb -ldbm -lresolv -lsocket -lnsl -lelf
-
-# location of sendmail binary (usually /usr/sbin or /usr/lib)
-BINDIR= ${DESTDIR}/usr/ucblib
-
-# location of sendmail.st file (usually /var/log or /usr/lib)
-STDIR= ${DESTDIR}/usr/ucblib
-
-# location of sendmail.hf file (usually /usr/share/misc or /usr/lib)
-HFDIR= ${DESTDIR}/usr/ucblib
-
-# additional .o files needed
-OBJADD=
-
-################### end of user configuration flags ######################
-
-CFLAGS= -I. $O ${INCDIRS} ${DBMDEF} ${ENVDEF}
-
-OBJS= alias.o arpadate.o clock.o collect.o conf.o convtime.o daemon.o \
- deliver.o domain.o envelope.o err.o headers.o macro.o main.o \
- map.o mci.o mime.o parseaddr.o queue.o readcf.o recipient.o \
- savemail.o srvrsmtp.o stab.o stats.o sysexits.o \
- trace.o udb.o usersmtp.o util.o version.o ${OBJADD}
-
-LINKS= ${DESTDIR}/usr/ucb/newaliases ${DESTDIR}/usr/ucb/mailq
-INSTALL=/usr/ucb/install
-BINOWN= root
-BINGRP= mail
-BINMODE=6555
-
-ALL= sendmail aliases.0 mailq.0 newaliases.0 sendmail.0
-
-all: ${ALL}
-
-sendmail: ${BEFORE} ${OBJS}
- ${CC} -o sendmail ${OBJS} ${LIBDIRS} ${LIBS}
-
-#NROFF= nroff -h
-NROFF= groff -Tascii
-MANDOC= -mandoc
-
-aliases.0: aliases.5
- ${NROFF} ${MANDOC} aliases.5 > aliases.0
-
-mailq.0: mailq.1
- ${NROFF} ${MANDOC} mailq.1 > mailq.0
-
-newaliases.0: newaliases.1
- ${NROFF} ${MANDOC} newaliases.1 > newaliases.0
-
-sendmail.0: sendmail.8
- ${NROFF} ${MANDOC} sendmail.8 > sendmail.0
-
-install: install-sendmail install-docs
-
-install-sendmail: sendmail
- ${INSTALL} -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} sendmail ${BINDIR}
- for i in ${LINKS}; do rm -f $$i; ln -s ${BINDIR}/sendmail $$i; done
- ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 644 /dev/null \
- ${STDIR}/sendmail.st
- ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 444 sendmail.hf ${HFDIR}
-
-# doesn't actually install them -- you may want to install pre-nroff versions
-install-docs: aliases.0 mailq.0 newaliases.0 sendmail.0
-
-clean:
- rm -f ${OBJS} sendmail aliases.0 mailq.0 newaliases.0 sendmail.0
-
-# dependencies
-# gross overkill, and yet still not quite enough....
-${OBJS}: sendmail.h conf.h
-
-depend:
diff --git a/usr.sbin/sendmail/src/Makefiles/Makefile.DomainOS b/usr.sbin/sendmail/src/Makefiles/Makefile.DomainOS
deleted file mode 100644
index 5f8417d..0000000
--- a/usr.sbin/sendmail/src/Makefiles/Makefile.DomainOS
+++ /dev/null
@@ -1,128 +0,0 @@
-#
-# This Makefile is designed to work on the old "make" program. It does
-# not use the obj subdirectory. It also does not install documentation
-# automatically -- think of it as a quick start for sites that have the
-# old make program (I recommend that you get and port the new make if you
-# are going to be doing any signficant work on sendmail).
-#
-# This has been tested on DomainOS 10.3.5
-#
-# @(#)Makefile.DomainOS 8.10 (Berkeley) 10/29/95
-#
-#
-
-# use O=-O (usual) or O=-g (debugging)
-O= -O
-
-# define the database mechanisms available for map & alias lookups:
-# -DNDBM -- use new DBM
-# -DNEWDB -- use new Berkeley DB
-# -DNDBM -DNEWDB -DYPCOMPAT -- use both plus YP compatility
-# -DNIS -- include client NIS support
-# The really old (V7) DBM library is no longer supported.
-# If YPCOMPAT is defined and /var/yp/Makefile exists, sendmail will build
-# both the NEWDB and DBM libraries (the DBM just for YP).
-#
-
-DBMDEF= -DNDBM
-
-# environment definitions (e.g., -D_AIX3)
-ENVDEF=
-
-# see also conf.h for additional compilation flags
-
-# include directories
-INCDIRS=
-
-# loader options
-LDOPTS=
-
-# library directories
-LIBDIRS=
-
-# libraries required on your system
-# You might want to use the BIND 4.9 resolver library here
-#LIBS= -ldb
-LIBS= -lresolv
-
-# location of sendmail binary (usually /usr/sbin or /usr/lib)
-BINDIR= ${DESTDIR}/usr/lib
-
-# location of sendmail.st file (usually /var/log or /usr/lib)
-STDIR= ${DESTDIR}/usr/lib
-
-# location of sendmail.hf file (usually /usr/share/misc or /usr/lib)
-HFDIR= ${DESTDIR}/usr/lib
-
-# additional .o files needed
-OBJADD=
-
-# additional pseudo-sources needed
-BEFORE= unistd.h dirent.h
-
-################### end of user configuration flags ######################
-
-CFLAGS= -I. -A nansi $O ${INCDIRS} ${DBMDEF} ${ENVDEF}
-
-OBJS= alias.o arpadate.o clock.o collect.o conf.o convtime.o daemon.o \
- deliver.o domain.o envelope.o err.o headers.o macro.o main.o \
- map.o mci.o mime.o parseaddr.o queue.o readcf.o recipient.o \
- savemail.o srvrsmtp.o stab.o stats.o sysexits.o \
- trace.o udb.o usersmtp.o util.o version.o ${OBJADD}
-
-LINKS= ${DESTDIR}/usr/ucb/newaliases ${DESTDIR}/usr/ucb/mailq
-INSTALL=install
-BINOWN= root
-BINGRP= kmem
-BINMODE=6555
-
-ALL= sendmail aliases.0 mailq.0 newaliases.0 sendmail.0
-
-all: ${ALL}
-
-sendmail: ${BEFORE} ${OBJS}
- ${CC} -o sendmail ${LDOPTS} ${OBJS} ${LIBDIRS} ${LIBS}
-
-unistd.h:
- cp /dev/null unistd.h
-
-dirent.h:
- echo "#include <sys/dir.h>" > dirent.h
- echo "#define dirent direct" >> dirent.h
-
-#NROFF= nroff -h
-NROFF= groff -Tascii
-MANDOC= -mandoc
-
-aliases.0: aliases.5
- ${NROFF} ${MANDOC} aliases.5 > aliases.0
-
-mailq.0: mailq.1
- ${NROFF} ${MANDOC} mailq.1 > mailq.0
-
-newaliases.0: newaliases.1
- ${NROFF} ${MANDOC} newaliases.1 > newaliases.0
-
-sendmail.0: sendmail.8
- ${NROFF} ${MANDOC} sendmail.8 > sendmail.0
-
-install: install-sendmail install-docs
-
-install-sendmail: sendmail
- ${INSTALL} -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} sendmail ${BINDIR}
- for i in ${LINKS}; do rm -f $$i; ln -s ${BINDIR}/sendmail $$i; done
- ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 644 /dev/null \
- ${STDIR}/sendmail.st
- ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 444 sendmail.hf ${HFDIR}
-
-# doesn't actually install them -- you may want to install pre-nroff versions
-install-docs: aliases.0 mailq.0 newaliases.0 sendmail.0
-
-clean:
- rm -f ${OBJS} sendmail aliases.0 mailq.0 newaliases.0 sendmail.0
-
-# dependencies
-# gross overkill, and yet still not quite enough....
-${OBJS}: sendmail.h conf.h
-
-depend:
diff --git a/usr.sbin/sendmail/src/Makefiles/Makefile.Dynix b/usr.sbin/sendmail/src/Makefiles/Makefile.Dynix
deleted file mode 100644
index ee5a6ce..0000000
--- a/usr.sbin/sendmail/src/Makefiles/Makefile.Dynix
+++ /dev/null
@@ -1,118 +0,0 @@
-#
-# Tested on Dynix 3.2.0.
-#
-# From Jim Davis <jdavis@cs.arizona.edu>.
-#
-# ``There is no strtol in libc (well there is in the 'att universe'
-# libc, but I couldn't figure out how to link that in), so I
-# got the Chris Torek strtol.c from bsd-sources on uunet and
-# compiled that. There is no native ndbm either; I couldn't
-# get db 1.72 to pass it's regression test, so I used gdbm-1.7
-# instead. I compiled it with gcc 1.40a. The -lseq is to pick
-# up getopt.''
-#
-# @(#)Makefile.Dynix 8.7 (Berkeley) 9/13/95
-#
-
-CC= gcc
-
-# use O=-O (usual) or O=-g (debugging)
-O= -O -g
-
-# define the database mechanisms available for map & alias lookups:
-# -DNDBM -- use new DBM
-# -DNEWDB -- use new Berkeley DB
-# -DNIS -- include NIS support
-# The really old (V7) DBM library is no longer supported.
-# See READ_ME for a description of how these flags interact.
-#
-DBMDEF= -DNDBM
-
-# environment definitions (e.g., -D_AIX3)
-ENVDEF=
-
-# see also conf.h for additional compilation flags
-
-# include directories
-INCDIRS=
-
-# loader options
-LDOPTS=
-
-# library directories
-LIBDIRS=
-
-# libraries required on your system
-LIBS= -lseq -lgdbm
-
-# location of sendmail binary (usually /usr/sbin or /usr/lib)
-BINDIR= ${DESTDIR}/usr/lib
-
-# location of sendmail.st file (usually /var/log or /usr/lib)
-STDIR= ${DESTDIR}/usr/lib
-
-# location of sendmail.hf file (usually /usr/share/misc or /usr/lib)
-HFDIR= ${DESTDIR}/usr/lib
-
-# additional .o files needed
-OBJADD=strtol.o
-
-################### end of user configuration flags ######################
-
-CFLAGS= -I. $O ${INCDIRS} ${DBMDEF} ${ENVDEF}
-
-OBJS= alias.o arpadate.o clock.o collect.o conf.o convtime.o daemon.o \
- deliver.o domain.o envelope.o err.o headers.o macro.o main.o \
- map.o mci.o mime.o parseaddr.o queue.o readcf.o recipient.o \
- savemail.o srvrsmtp.o stab.o stats.o sysexits.o \
- trace.o udb.o usersmtp.o util.o version.o ${OBJADD}
-
-LINKS= ${DESTDIR}/usr/ucb/newaliases ${DESTDIR}/usr/ucb/mailq
-INSTALL=install
-BINOWN= root
-BINGRP= staff # no kmem group,
-BINMODE=4555 # so not setgid
-
-ALL= sendmail aliases.0 mailq.0 newaliases.0 sendmail.0
-
-all: ${ALL}
-
-sendmail: ${BEFORE} ${OBJS}
- ${CC} -o sendmail ${LDOPTS} ${OBJS} ${LIBDIRS} ${LIBS}
-
-#NROFF= nroff -h
-NROFF= groff -Tascii
-MANDOC= -mandoc
-
-aliases.0: aliases.5
- ${NROFF} ${MANDOC} aliases.5 > aliases.0
-
-mailq.0: mailq.1
- ${NROFF} ${MANDOC} mailq.1 > mailq.0
-
-newaliases.0: newaliases.1
- ${NROFF} ${MANDOC} newaliases.1 > newaliases.0
-
-sendmail.0: sendmail.8
- ${NROFF} ${MANDOC} sendmail.8 > sendmail.0
-
-install: install-sendmail install-docs
-
-install-sendmail: sendmail
- ${INSTALL} -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} sendmail ${BINDIR}
- for i in ${LINKS}; do rm -f $$i; ln -s ${BINDIR}/sendmail $$i; done
- ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 644 /dev/null \
- ${STDIR}/sendmail.st
- ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 444 sendmail.hf ${HFDIR}
-
-# doesn't actually install them -- you may want to install pre-nroff versions
-install-docs: aliases.0 mailq.0 newaliases.0 sendmail.0
-
-clean:
- rm -f ${OBJS} sendmail aliases.0 mailq.0 newaliases.0 sendmail.0
-
-# dependencies
-# gross overkill, and yet still not quite enough....
-${OBJS}: sendmail.h conf.h
-
-depend:
diff --git a/usr.sbin/sendmail/src/Makefiles/Makefile.EWS-UX_V b/usr.sbin/sendmail/src/Makefiles/Makefile.EWS-UX_V
deleted file mode 100644
index 5eed831..0000000
--- a/usr.sbin/sendmail/src/Makefiles/Makefile.EWS-UX_V
+++ /dev/null
@@ -1,132 +0,0 @@
-#
-# This Makefile is designed to work on the old "make" program. It does
-# not use the obj subdirectory. It also does not install documentation
-# automatically -- think of it as a quick start for sites that have the
-# old make program (I recommend that you get and port the new make if you
-# are going to be doing any signficant work on sendmail).
-#
-# This has been tested on NEC EWS-UX/V 4.2
-#
-# @(#)Makefile.EWS-UX_V 8.5 (Berkeley) 9/13/95
-#
-
-# use O=-O (usual) or O=-g (debugging)
-O= -O
-
-# make sure that /usr/abiccs/bin/cc is used (do not use /usr/ucb/cc).
-#CC= /bin/cc -KOlimit=900
-CC= /usr/abiccs/bin/cc -KOlimit=900
-
-# define the database mechanism used for alias lookups:
-# -DNDBM -- use new DBM
-# -DNEWDB -- use new Berkeley DB
-# -DNIS -- include NIS support
-# The really old (V7) DBM library is no longer supported.
-# See READ_ME for a description of how these flags interact.
-#
-DBMDEF= -DNDBM -DNIS
-
-# environment definitions (e.g., -D_AIX3)
-ENVDEF= -Dnec_ews_svr4
-
-# see also conf.h for additional compilation flags
-
-# include directories
-INCDIRS=-I/usr/sww/include
-
-# library directories
-LIBDIRS=-L/usr/sww/lib
-
-# libraries required on your system
-# delete -l44bsd if you are not running BIND 4.9.x
-LIBS= ndbm.o -lsocket -lnsl -lelf -lresolv # -l44bsd # with NDBM
-#LIBS= -lsocket -lnsl -lelf -ldb -lresolv # -l44bsd # with NEWDB
-
-# location of sendmail binary (usually /usr/sbin or /usr/lib)
-BINDIR= ${DESTDIR}/usr/ucblib
-
-# location of sendmail.st file (usually /var/log or /usr/lib)
-STDIR= ${DESTDIR}/var/ucblib
-
-# location of sendmail.hf file (usually /usr/share/misc or /usr/lib)
-HFDIR= ${DESTDIR}/var/ucblib
-
-# additional .o files needed
-OBJADD=
-
-# things to be made before compilation begins
-BEFORE= sysexits.h ndbm.h ndbm.o
-
-################### end of user configuration flags ######################
-
-CFLAGS= -I. $O ${INCDIRS} ${DBMDEF} ${ENVDEF}
-
-OBJS= alias.o arpadate.o clock.o collect.o conf.o convtime.o daemon.o \
- deliver.o domain.o envelope.o err.o headers.o macro.o main.o \
- map.o mci.o mime.o parseaddr.o queue.o readcf.o recipient.o \
- savemail.o srvrsmtp.o stab.o stats.o sysexits.o \
- trace.o udb.o usersmtp.o util.o version.o ${OBJADD}
-
-LINKS= ${DESTDIR}/usr/ucb/newaliases ${DESTDIR}/usr/ucb/mailq
-INSTALL=/usr/ucb/install
-BINOWN= root
-BINGRP= sys
-BINMODE=6555
-
-ALL= sendmail aliases.0 mailq.0 newaliases.0 sendmail.0
-
-all: ${ALL}
-
-sendmail: ${BEFORE} ${OBJS}
- ${CC} -o sendmail ${OBJS} ${LIBDIRS} ${LIBS}
-
-sysexits.h:
- echo '#ifndef _LOCAL_SYSEXITS_H_' > sysexits.h;
- echo '#define _LOCAL_SYSEXITS_H_' >> sysexits.h;
- cat /usr/abiccs/ucbinclude/sysexits.h >> sysexits.h;
- echo '#endif /* _LOCAL_SYSEXITS_H_ */' >> sysexits.h;
-# ln -s /usr/abiccs/ucbinclude/sysexits.h .
-
-ndbm.h:
- ln -s /usr/abiccs/ucbinclude/ndbm.h .
-
-ndbm.o:
- ar x /usr/abiccs/ucblib/libucb.a ndbm.o
-# ar x /usr/ucblib/libucb.a ndbm.o
-
-#NROFF= nroff -h
-NROFF= groff -Tascii
-MANDOC= -mandoc
-
-aliases.0: aliases.5
- ${NROFF} ${MANDOC} aliases.5 > aliases.0
-
-mailq.0: mailq.1
- ${NROFF} ${MANDOC} mailq.1 > mailq.0
-
-newaliases.0: newaliases.1
- ${NROFF} ${MANDOC} newaliases.1 > newaliases.0
-
-sendmail.0: sendmail.8
- ${NROFF} ${MANDOC} sendmail.8 > sendmail.0
-
-install: install-sendmail install-docs
-
-install-sendmail: sendmail
- ${INSTALL} -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} sendmail ${BINDIR}
- for i in ${LINKS}; do rm -f $$i; ln -s ${BINDIR}/sendmail $$i; done
- ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 644 /dev/null \
- ${STDIR}/sendmail.st
- ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 444 sendmail.hf ${HFDIR}
-
-# doesn't actually install them -- you may want to install pre-nroff versions
-install-docs: aliases.0 mailq.0 newaliases.0 sendmail.0
-
-clean:
- rm -f ${OBJS} sendmail aliases.0 mailq.0 newaliases.0 sendmail.0
-
-# dependencies
-# gross overkill, and yet still not quite enough....
-${OBJS}: sendmail.h conf.h
-
-depend:
diff --git a/usr.sbin/sendmail/src/Makefiles/Makefile.FreeBSD b/usr.sbin/sendmail/src/Makefiles/Makefile.FreeBSD
deleted file mode 100644
index a163f04..0000000
--- a/usr.sbin/sendmail/src/Makefiles/Makefile.FreeBSD
+++ /dev/null
@@ -1,50 +0,0 @@
-#
-# Makefile for FreeBSD
-#
-# @(#)Makefile.FreeBSD 8.4 (Berkeley) 9/13/95
-
-PROG= sendmail
-
-# define the database format to use for aliases et al. Can be -DNEWDB (for
-# the new BSD database package -- this is preferred) or -DNDBM for the NDBM
-# database package. The old putrescent V7 DBM package is no longer
-# supported.
-# You can define both NEWDB and NDBM during a transition period; old
-# databases are read, but the new format will be used on any rebuilds. On
-# really gnarly systems, you can set this to null; it will crawl like a high
-# spiral snail, but it will work.
-DBMDEF= -DNEWDB
-
-CFLAGS+=-I${.CURDIR} ${DBMDEF}
-
-SRCS= alias.c arpadate.c clock.c collect.c conf.c convtime.c daemon.c \
- deliver.c domain.c envelope.c err.c headers.c macro.c main.c map.c \
- mci.c mime.c parseaddr.c queue.c readcf.c recipient.c savemail.c \
- srvrsmtp.c stab.c stats.c sysexits.c trace.c udb.c usersmtp.c \
- util.c version.c
-DPADD=
-LDADD= $(LIBUTIL)
-#
-# FreeBSD 1.0 RELEASE has GNU man and doesn't need preformatted man pages anymore
-# (assuming you consider a slower "man" command a feature)
-#
-MAN1= mailq.1 newaliases.1
-MAN5= aliases.5
-MAN8= sendmail.8
-LINKS= /usr/sbin/sendmail /usr/bin/newaliases \
- /usr/sbin/sendmail /usr/bin/mailq
-INSTALL=install
-BINDIR= /usr/sbin
-BINOWN= root
-BINGRP= kmem
-BINMODE=6555
-
-beforeinstall:
-# ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 644 /dev/null \
-# ${DESTDIR}/etc/sendmail.fc
- ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 644 /dev/null \
- ${DESTDIR}/var/log/sendmail.st
- ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 444 ${.CURDIR}/sendmail.hf \
- ${DESTDIR}/usr/share/misc
-
-.include <bsd.prog.mk>
diff --git a/usr.sbin/sendmail/src/Makefiles/Makefile.HP-UX b/usr.sbin/sendmail/src/Makefiles/Makefile.HP-UX
deleted file mode 100644
index 575dae3..0000000
--- a/usr.sbin/sendmail/src/Makefiles/Makefile.HP-UX
+++ /dev/null
@@ -1,114 +0,0 @@
-#
-# This Makefile is designed to work on the old "make" program. It does
-# not use the obj subdirectory. It also does not install documentation
-# automatically -- think of it as a quick start for sites that have the
-# old make program (I recommend that you get and port the new make if you
-# are going to be doing any signficant work on sendmail).
-#
-# This has been tested on HP-UX 9.05 on 7xx series and HP-UX 9.04
-# on 8xx series.
-#
-# @(#)Makefile.HP-UX 8.14 (Berkeley) 11/1/95
-#
-
-CC= cc -Aa -D_HPUX_SOURCE
-
-# use O=-O (usual) or O=-g (debugging)
-# +O is OK on 7xx, and 300xx at 9.0
-O= +O1
-
-# define the database mechanism used for alias lookups:
-# -DNDBM -- use new DBM
-# -DNEWDB -- use new Berkeley DB
-# -DNIS -- include NIS support
-# The really old (V7) DBM library is no longer supported.
-# See READ_ME for a description of how these flags interact.
-#
-DBMDEF= -DNDBM -DNEWDB -DNIS
-
-# environment definitions (e.g., -D_AIX3)
-ENVDEF=
-
-# see also conf.h for additional compilation flags
-
-# include directories
-INCDIRS=-I/usr/sww/include
-
-# library directories
-LIBDIRS=-L/usr/sww/lib
-
-# libraries required on your system
-# delete -lresolv if you are not running BIND 4.9.x
-LIBS= -ldb -lndbm -lresolv
-
-# location of sendmail binary (usually /usr/sbin or /usr/lib)
-BINDIR= ${DESTDIR}/usr/lib
-
-# location of sendmail.st file (usually /var/log or /usr/lib)
-STDIR= ${DESTDIR}/usr/lib
-
-# location of sendmail.hf file (usually /usr/share/misc or /usr/lib)
-HFDIR= ${DESTDIR}/usr/lib
-
-# additional .o files needed
-OBJADD=
-
-
-################### end of user configuration flags ######################
-
-CFLAGS= -I. $O ${INCDIRS} ${DBMDEF} ${ENVDEF}
-
-OBJS= alias.o arpadate.o clock.o collect.o conf.o convtime.o daemon.o \
- deliver.o domain.o envelope.o err.o headers.o macro.o main.o \
- map.o mci.o mime.o parseaddr.o queue.o readcf.o recipient.o \
- savemail.o srvrsmtp.o stab.o stats.o sysexits.o \
- trace.o udb.o usersmtp.o util.o version.o ${OBJADD}
-
-LINKS= ${DESTDIR}/usr/bin/newaliases ${DESTDIR}/usr/bin/mailq
-INSTALL=install
-BINOWN= root
-BINGRP= mail
-BINMODE=6555
-
-ALL= sendmail aliases.0 mailq.0 newaliases.0 sendmail.0
-
-all: ${ALL}
-
-sendmail: ${BEFORE} ${OBJS}
- ${CC} -o sendmail ${OBJS} ${LIBDIRS} ${LIBS}
-
-#NROFF= nroff -h
-NROFF= groff -Tascii
-MANDOC= -mandoc
-
-aliases.0: aliases.5
- ${NROFF} ${MANDOC} aliases.5 > aliases.0
-
-mailq.0: mailq.1
- ${NROFF} ${MANDOC} mailq.1 > mailq.0
-
-newaliases.0: newaliases.1
- ${NROFF} ${MANDOC} newaliases.1 > newaliases.0
-
-sendmail.0: sendmail.8
- ${NROFF} ${MANDOC} sendmail.8 > sendmail.0
-
-install: install-sendmail install-docs
-
-install-sendmail: sendmail
- cpset sendmail ${BINDIR} ${BINMODE} ${BINOWN} ${BINGRP}
- for i in ${LINKS}; do rm -f $$i; ln -s ${BINDIR}/sendmail $$i; done
- cpset /dev/null ${STDIR}/sendmail.st 644 ${BINOWN} ${BINGRP}
- cpset sendmail.hf ${HFDIR} 444 ${BINOWN} ${BINGRP}
-
-# doesn't actually install them -- you may want to install pre-nroff versions
-install-docs: aliases.0 mailq.0 newaliases.0 sendmail.0
-
-clean:
- rm -f ${OBJS} sendmail aliases.0 mailq.0 newaliases.0 sendmail.0
-
-# dependencies
-# gross overkill, and yet still not quite enough....
-${OBJS}: sendmail.h conf.h
-
-depend:
diff --git a/usr.sbin/sendmail/src/Makefiles/Makefile.HP-UX.10.x b/usr.sbin/sendmail/src/Makefiles/Makefile.HP-UX.10.x
deleted file mode 100644
index 993efed..0000000
--- a/usr.sbin/sendmail/src/Makefiles/Makefile.HP-UX.10.x
+++ /dev/null
@@ -1,114 +0,0 @@
-#
-# This Makefile is designed to work on the old "make" program. It does
-# not use the obj subdirectory. It also does not install documentation
-# automatically -- think of it as a quick start for sites that have the
-# old make program (I recommend that you get and port the new make if you
-# are going to be doing any signficant work on sendmail).
-#
-# This has been tested on HP-UX 10.x. Changes for 10.0 contributed
-# by John Beck of Hewlett-Packard.
-#
-# @(#)Makefile.HP-UX.10.x 8.8 (Berkeley) 11/1/95
-#
-
-CC= cc -Aa -D_HPUX_SOURCE
-
-# use O=-O (usual) or O=-g (debugging)
-# +O is OK on 7xx, and 300xx at 9.0
-O= +O3
-
-# define the database mechanism used for alias lookups:
-# -DNDBM -- use new DBM
-# -DNEWDB -- use new Berkeley DB
-# -DNIS -- include NIS support
-# The really old (V7) DBM library is no longer supported.
-# See READ_ME for a description of how these flags interact.
-#
-DBMDEF= -DNDBM -DNEWDB -DNIS
-
-# environment definitions (e.g., -D_AIX3)
-ENVDEF= -DV4FS
-
-# see also conf.h for additional compilation flags
-
-# include directories
-INCDIRS=-I/usr/sww/include
-
-# library directories
-LIBDIRS=-L/usr/sww/lib
-
-# libraries required on your system
-# delete -lresolv if you are not running BIND 4.9.x
-LIBS= -ldb -lndbm -lresolv
-
-# location of sendmail binary (usually /usr/sbin or /usr/lib)
-BINDIR= ${DESTDIR}/usr/sbin
-
-# location of sendmail.st file (usually /var/log or /usr/lib)
-STDIR= ${DESTDIR}/etc/mail
-
-# location of sendmail.hf file (usually /usr/share/misc or /usr/lib)
-HFDIR= ${DESTDIR}/usr/share/lib
-
-# additional .o files needed
-OBJADD=
-
-
-################### end of user configuration flags ######################
-
-CFLAGS= -I. $O ${INCDIRS} ${DBMDEF} ${ENVDEF}
-
-OBJS= alias.o arpadate.o clock.o collect.o conf.o convtime.o daemon.o \
- deliver.o domain.o envelope.o err.o headers.o macro.o main.o \
- map.o mci.o mime.o parseaddr.o queue.o readcf.o recipient.o \
- savemail.o srvrsmtp.o stab.o stats.o sysexits.o \
- trace.o udb.o usersmtp.o util.o version.o ${OBJADD}
-
-LINKS= ${DESTDIR}/usr/bin/newaliases ${DESTDIR}/usr/bin/mailq
-INSTALL=install
-BINOWN= root
-BINGRP= mail
-BINMODE=6555
-
-ALL= sendmail aliases.0 mailq.0 newaliases.0 sendmail.0
-
-all: ${ALL}
-
-sendmail: ${BEFORE} ${OBJS}
- ${CC} -o sendmail ${OBJS} ${LIBDIRS} ${LIBS}
-
-#NROFF= nroff -h
-NROFF= groff -Tascii
-MANDOC= -mandoc
-
-aliases.0: aliases.5
- ${NROFF} ${MANDOC} aliases.5 > aliases.0
-
-mailq.0: mailq.1
- ${NROFF} ${MANDOC} mailq.1 > mailq.0
-
-newaliases.0: newaliases.1
- ${NROFF} ${MANDOC} newaliases.1 > newaliases.0
-
-sendmail.0: sendmail.8
- ${NROFF} ${MANDOC} sendmail.8 > sendmail.0
-
-install: install-sendmail install-docs
-
-install-sendmail: sendmail
- cpset sendmail ${BINDIR} ${BINMODE} ${BINOWN} ${BINGRP}
- for i in ${LINKS}; do rm -f $$i; ln -s ${BINDIR}/sendmail $$i; done
- cpset /dev/null ${STDIR}/sendmail.st 644 ${BINOWN} ${BINGRP}
- cpset sendmail.hf ${HFDIR} 444 ${BINOWN} ${BINGRP}
-
-# doesn't actually install them -- you may want to install pre-nroff versions
-install-docs: aliases.0 mailq.0 newaliases.0 sendmail.0
-
-clean:
- rm -f ${OBJS} sendmail aliases.0 mailq.0 newaliases.0 sendmail.0
-
-# dependencies
-# gross overkill, and yet still not quite enough....
-${OBJS}: sendmail.h conf.h
-
-depend:
diff --git a/usr.sbin/sendmail/src/Makefiles/Makefile.IRIX b/usr.sbin/sendmail/src/Makefiles/Makefile.IRIX
deleted file mode 100644
index 2e2f3a1..0000000
--- a/usr.sbin/sendmail/src/Makefiles/Makefile.IRIX
+++ /dev/null
@@ -1,113 +0,0 @@
-#
-# This Makefile is designed to work on the old "make" program. It does
-# not use the obj subdirectory. It also does not install documentation
-# automatically -- think of it as a quick start for sites that have the
-# old make program (I recommend that you get and port the new make if you
-# are going to be doing any signficant work on sendmail).
-#
-# This has been tested on IRIX 4.0.4.
-#
-# @(#)Makefile.IRIX 8.10 (Berkeley) 9/13/95
-#
-SHELL= /bin/sh
-
-# use O=-O (usual) or O=-g (debugging)
-O= -O
-CC=gcc
-
-# define the database mechanisms available for map & alias lookups:
-# -DNDBM -- use new DBM
-# -DNEWDB -- use new Berkeley DB (requires -ldb)
-# -DNIS -- include NIS support (requires -lsun)
-# The really old (V7) DBM library is no longer supported.
-# See READ_ME for a description of how these flags interact.
-#
-DBMDEF= -DNDBM -DNIS
-
-# environment definitions (e.g., -D_AIX3)
-ENVDEF= -DIRIX
-
-# see also conf.h for additional compilation flags
-
-# include directories
-INCDIRS=
-
-# library directories
-LIBDIRS=
-
-# libraries required on your system
-LIBS= -lmld -lmalloc -lsun
-
-# location of sendmail binary (usually /usr/sbin or /usr/lib)
-BINDIR= ${DESTDIR}/usr/lib
-
-# location of sendmail.st file (usually /var/log or /usr/lib)
-STDIR= ${DESTDIR}/usr/lib
-
-# location of sendmail.hf file (usually /usr/share/misc or /usr/lib)
-HFDIR= ${DESTDIR}/usr/lib
-
-# additional .o files needed
-OBJADD=
-
-################### end of user configuration flags ######################
-
-CFLAGS= -I. $O ${INCDIRS} ${DBMDEF} ${ENVDEF}
-
-OBJS= alias.o arpadate.o clock.o collect.o conf.o convtime.o daemon.o \
- deliver.o domain.o envelope.o err.o headers.o macro.o main.o \
- map.o mci.o mime.o parseaddr.o queue.o readcf.o recipient.o \
- savemail.o srvrsmtp.o stab.o stats.o sysexits.o \
- trace.o udb.o usersmtp.o util.o version.o ${OBJADD}
-
-LINKS= ${DESTDIR}/usr/bsd/newaliases ${DESTDIR}/usr/bsd/mailq
-INSTALL=install
-BINOWN= root
-BINGRP= sys
-BINMODE=6555
-
-ALL= sendmail aliases.0 mailq.0 newaliases.0 sendmail.0
-
-all: ${ALL}
-
-sendmail: ${BEFORE} ${OBJS}
- ${CC} -o sendmail ${OBJS} ${LIBDIRS} ${LIBS}
-
-#NROFF= nroff -h
-NROFF= groff -Tascii
-MANDOC= -mandoc
-
-aliases.0: aliases.5
- ${NROFF} ${MANDOC} aliases.5 > aliases.0
-
-mailq.0: mailq.1
- ${NROFF} ${MANDOC} mailq.1 > mailq.0
-
-newaliases.0: newaliases.1
- ${NROFF} ${MANDOC} newaliases.1 > newaliases.0
-
-sendmail.0: sendmail.8
- ${NROFF} ${MANDOC} sendmail.8 > sendmail.0
-
-install: install-sendmail install-docs
-
-install-sendmail: sendmail
- ${INSTALL} -u ${BINOWN} -g ${BINGRP} -m ${BINMODE} -f ${BINDIR} sendmail
- for i in ${LINKS}; do rm -f $$i; ln -s ${BINDIR}/sendmail $$i; done
- cp /dev/null ${STDIR}/sendmail.st
- chmod 644 ${STDIR}/sendmail.st
- chown ${BINOWN} ${STDIR}/sendmail.st
- chgrp ${BINGRP} ${STDIR}/sendmail.st
- ${INSTALL} -u ${BINOWN} -g ${BINGRP} -m 444 -f ${HFDIR} sendmail.hf
-
-# doesn't actually install them -- you may want to install pre-nroff versions
-install-docs: aliases.0 mailq.0 newaliases.0 sendmail.0
-
-clean:
- rm -f ${OBJS} sendmail aliases.0 mailq.0 newaliases.0 sendmail.0
-
-# dependencies
-# gross overkill, and yet still not quite enough....
-${OBJS}: sendmail.h conf.h
-
-depend:
diff --git a/usr.sbin/sendmail/src/Makefiles/Makefile.IRIX.5.x b/usr.sbin/sendmail/src/Makefiles/Makefile.IRIX.5.x
deleted file mode 100644
index cb1a7e8..0000000
--- a/usr.sbin/sendmail/src/Makefiles/Makefile.IRIX.5.x
+++ /dev/null
@@ -1,115 +0,0 @@
-#
-# This Makefile is designed to work on the old "make" program. It does
-# not use the obj subdirectory. It also does not install documentation
-# automatically -- think of it as a quick start for sites that have the
-# old make program (I recommend that you get and port the new make if you
-# are going to be doing any signficant work on sendmail).
-#
-# Tested on IRIX 5.3 by Kari E. Hurtta <Kari.Hurtta@fmi.fi>.
-#
-# @(#)Makefile.IRIX.5.x 8.7 (Berkeley) 9/26/95
-#
-SHELL= /bin/sh
-
-# use O=-O (usual) or O=-g (debugging)
-O= -O
-
-# define the database mechanisms available for map & alias lookups:
-# -DNDBM -- use new DBM
-# -DNEWDB -- use new Berkeley DB (requires -ldb)
-# -DNIS -- include NIS support (requires -lsun)
-# The really old (V7) DBM library is no longer supported.
-# See READ_ME for a description of how these flags interact.
-#
-# N.B.: Include -D__BIT_TYPES_DEFINED__ if you use -DNEWDB!
-#
-DBMDEF= -DNDBM -DNIS
-#DBMDEF= -DNDBM -DNIS -DNEWDB -D__BIT_TYPES_DEFINED__
-
-# environment definitions (e.g., -D_AIX3)
-ENVDEF= -DIRIX5
-
-# see also conf.h for additional compilation flags
-
-# include directories
-INCDIRS=
-
-# library directories
-LIBDIRS=
-
-# libraries required on your system
-LIBS= -lmld -lmalloc -lsun
-
-# location of sendmail binary (usually /usr/sbin or /usr/lib)
-BINDIR= ${DESTDIR}/usr/lib
-
-# location of sendmail.st file (usually /var/log or /usr/lib)
-STDIR= ${DESTDIR}/var
-
-# location of sendmail.hf file (usually /usr/share/misc or /usr/lib)
-HFDIR= ${DESTDIR}/usr/lib
-
-# additional .o files needed
-OBJADD=
-
-################### end of user configuration flags ######################
-
-CFLAGS= -I. $O ${INCDIRS} ${DBMDEF} ${ENVDEF}
-
-OBJS= alias.o arpadate.o clock.o collect.o conf.o convtime.o daemon.o \
- deliver.o domain.o envelope.o err.o headers.o macro.o main.o \
- map.o mci.o mime.o parseaddr.o queue.o readcf.o recipient.o \
- savemail.o srvrsmtp.o stab.o stats.o sysexits.o \
- trace.o udb.o usersmtp.o util.o version.o ${OBJADD}
-
-LINKS= ${DESTDIR}/usr/bsd/newaliases ${DESTDIR}/usr/bsd/mailq
-INSTALL=install
-BINOWN= root
-BINGRP= sys
-BINMODE=6555
-
-ALL= sendmail aliases.0 mailq.0 newaliases.0 sendmail.0
-
-all: ${ALL}
-
-sendmail: ${BEFORE} ${OBJS}
- ${CC} -o sendmail ${OBJS} ${LIBDIRS} ${LIBS}
-
-#NROFF= nroff -h
-NROFF= groff -Tascii
-MANDOC= -mandoc
-
-aliases.0: aliases.5
- ${NROFF} ${MANDOC} aliases.5 > aliases.0
-
-mailq.0: mailq.1
- ${NROFF} ${MANDOC} mailq.1 > mailq.0
-
-newaliases.0: newaliases.1
- ${NROFF} ${MANDOC} newaliases.1 > newaliases.0
-
-sendmail.0: sendmail.8
- ${NROFF} ${MANDOC} sendmail.8 > sendmail.0
-
-install: install-sendmail install-docs
-
-install-sendmail: sendmail
- ${INSTALL} -u ${BINOWN} -g ${BINGRP} -m ${BINMODE} -f ${BINDIR} sendmail
- for i in ${LINKS}; do rm -f $$i; ln -s ${BINDIR}/sendmail $$i; done
- cp /dev/null ${STDIR}/sendmail.st
- chmod 644 ${STDIR}/sendmail.st
- chown ${BINOWN} ${STDIR}/sendmail.st
- chgrp ${BINGRP} ${STDIR}/sendmail.st
- ${INSTALL} -u ${BINOWN} -g ${BINGRP} -m 444 -f ${HFDIR} sendmail.hf
-
-# doesn't actually install them -- you may want to install pre-nroff versions
-install-docs: aliases.0 mailq.0 newaliases.0 sendmail.0
-
-clean:
- rm -f ${OBJS} sendmail aliases.0 mailq.0 newaliases.0 sendmail.0
-
-# dependencies
-# gross overkill, and yet still not quite enough....
-${OBJS}: sendmail.h conf.h
-
-depend:
diff --git a/usr.sbin/sendmail/src/Makefiles/Makefile.IRIX64 b/usr.sbin/sendmail/src/Makefiles/Makefile.IRIX64
deleted file mode 100644
index 1fec703..0000000
--- a/usr.sbin/sendmail/src/Makefiles/Makefile.IRIX64
+++ /dev/null
@@ -1,114 +0,0 @@
-#
-# This Makefile is designed to work on the old "make" program. It does
-# not use the obj subdirectory. It also does not install documentation
-# automatically -- think of it as a quick start for sites that have the
-# old make program (I recommend that you get and port the new make if you
-# are going to be doing any signficant work on sendmail).
-#
-# This has been tested on IRIX64 6.0.
-# Changes from Mark R. Levinson <ml@cvdev.rochester.edu>.
-#
-# @(#)Makefile.IRIX64 8.4 (Berkeley) 9/13/95
-#
-SHELL= /bin/sh
-
-# use O=-O (usual) or O=-g (debugging)
-O= -O
-CC=gcc
-
-# define the database mechanisms available for map & alias lookups:
-# -DNDBM -- use new DBM
-# -DNEWDB -- use new Berkeley DB (requires -ldb)
-# -DNIS -- include NIS support (requires -lsun)
-# The really old (V7) DBM library is no longer supported.
-# See READ_ME for a description of how these flags interact.
-#
-DBMDEF= -DNDBM
-
-# environment definitions (e.g., -D_AIX3)
-ENVDEF= -DIRIX64
-
-# see also conf.h for additional compilation flags
-
-# include directories
-INCDIRS=
-
-# library directories
-LIBDIRS=
-
-# libraries required on your system
-LIBS= -lelf -lmalloc
-
-# location of sendmail binary (usually /usr/sbin or /usr/lib)
-BINDIR= ${DESTDIR}/usr/lib
-
-# location of sendmail.st file (usually /var/log or /usr/lib)
-STDIR= ${DESTDIR}/usr/lib
-
-# location of sendmail.hf file (usually /usr/share/misc or /usr/lib)
-HFDIR= ${DESTDIR}/usr/lib
-
-# additional .o files needed
-OBJADD=
-
-################### end of user configuration flags ######################
-
-CFLAGS= -I. $O ${INCDIRS} ${DBMDEF} ${ENVDEF}
-
-OBJS= alias.o arpadate.o clock.o collect.o conf.o convtime.o daemon.o \
- deliver.o domain.o envelope.o err.o headers.o macro.o main.o \
- map.o mci.o mime.o parseaddr.o queue.o readcf.o recipient.o \
- savemail.o srvrsmtp.o stab.o stats.o sysexits.o \
- trace.o udb.o usersmtp.o util.o version.o ${OBJADD}
-
-LINKS= ${DESTDIR}/usr/bsd/newaliases ${DESTDIR}/usr/bsd/mailq
-INSTALL=install
-BINOWN= root
-BINGRP= sys
-BINMODE=6555
-
-ALL= sendmail aliases.0 mailq.0 newaliases.0 sendmail.0
-
-all: ${ALL}
-
-sendmail: ${BEFORE} ${OBJS}
- ${CC} -o sendmail ${OBJS} ${LIBDIRS} ${LIBS}
-
-#NROFF= nroff -h
-NROFF= groff -Tascii
-MANDOC= -mandoc
-
-aliases.0: aliases.5
- ${NROFF} ${MANDOC} aliases.5 > aliases.0
-
-mailq.0: mailq.1
- ${NROFF} ${MANDOC} mailq.1 > mailq.0
-
-newaliases.0: newaliases.1
- ${NROFF} ${MANDOC} newaliases.1 > newaliases.0
-
-sendmail.0: sendmail.8
- ${NROFF} ${MANDOC} sendmail.8 > sendmail.0
-
-install: install-sendmail install-docs
-
-install-sendmail: sendmail
- ${INSTALL} -u ${BINOWN} -g ${BINGRP} -m ${BINMODE} -f ${BINDIR} sendmail
- for i in ${LINKS}; do rm -f $$i; ln -s ${BINDIR}/sendmail $$i; done
- cp /dev/null ${STDIR}/sendmail.st
- chmod 644 ${STDIR}/sendmail.st
- chown ${BINOWN} ${STDIR}/sendmail.st
- chgrp ${BINGRP} ${STDIR}/sendmail.st
- ${INSTALL} -u ${BINOWN} -g ${BINGRP} -m 444 -f ${HFDIR} sendmail.hf
-
-# doesn't actually install them -- you may want to install pre-nroff versions
-install-docs: aliases.0 mailq.0 newaliases.0 sendmail.0
-
-clean:
- rm -f ${OBJS} sendmail aliases.0 mailq.0 newaliases.0 sendmail.0
-
-# dependencies
-# gross overkill, and yet still not quite enough....
-${OBJS}: sendmail.h conf.h
-
-depend:
diff --git a/usr.sbin/sendmail/src/Makefiles/Makefile.ISC b/usr.sbin/sendmail/src/Makefiles/Makefile.ISC
deleted file mode 100644
index 56f5131..0000000
--- a/usr.sbin/sendmail/src/Makefiles/Makefile.ISC
+++ /dev/null
@@ -1,108 +0,0 @@
-#
-# Makefile for ISC (SunSoft) UNIX.
-#
-# Contributed by J.J. Bailey <jjb@jagware.bcc.com>
-#
-# @(#)Makefile.ISC 8.8 (Berkeley) 9/13/95
-#
-
-# use O=-O (usual) or O=-g (debugging)
-O= -O
-
-# define the database mechanisms available for map & alias lookups:
-# -DNDBM -- use new DBM
-# -DNEWDB -- use new Berkeley DB
-# -DNIS -- include NIS support
-# The really old (V7) DBM library is no longer supported.
-# See READ_ME for a description of how these flags interact.
-#
-DBMDEF= -DNDBM -DNIS
-
-# environment definitions (e.g., -D_AIX3)
-ENVDEF= -DISC_UNIX -D_POSIX_SOURCE -D_SYSV3
-
-# see also conf.h for additional compilation flags
-
-# include directories
-INCDIRS=-I/usr/local/include
-
-# loader options
-LDOPTS=
-
-# library directories
-LIBDIRS=-L/usr/local/lib
-
-# libraries required on your system
-LIBS= -lyp -lrpc -lndbm -linet -lcposix
-
-# location of sendmail binary (usually /usr/sbin or /usr/lib)
-BINDIR= ${DESTDIR}/usr/lib
-
-# location of sendmail.st file (usually /var/log or /usr/lib)
-STDIR= ${DESTDIR}/usr/spool/log
-
-# location of sendmail.hf file (usually /usr/share/misc or /usr/lib)
-HFDIR= ${DESTDIR}/usr/lib
-
-# additional .o files needed
-OBJADD=
-
-################### end of user configuration flags ######################
-
-CFLAGS= -I. $O ${INCDIRS} ${DBMDEF} ${ENVDEF}
-
-OBJS= alias.o arpadate.o clock.o collect.o conf.o convtime.o daemon.o \
- deliver.o domain.o envelope.o err.o headers.o macro.o main.o \
- map.o mci.o mime.o parseaddr.o queue.o readcf.o recipient.o \
- savemail.o srvrsmtp.o stab.o stats.o sysexits.o \
- trace.o udb.o usersmtp.o util.o version.o ${OBJADD}
-
-LINKS= ${DESTDIR}/usr/ucb/newaliases ${DESTDIR}/usr/ucb/mailq
-INSTALL=install
-BINOWN= root
-BINGRP= kmem
-BINMODE=6555
-
-ALL= sendmail aliases.0 mailq.0 newaliases.0 sendmail.0
-
-all: ${ALL}
-
-sendmail: ${BEFORE} ${OBJS}
- ${CC} -o sendmail ${LDOPTS} ${OBJS} ${LIBDIRS} ${LIBS}
-
-#NROFF= nroff -h
-NROFF= groff -Tascii
-MANDOC= -mandoc
-
-aliases.0: aliases.5
- ${NROFF} ${MANDOC} aliases.5 > aliases.0
-
-mailq.0: mailq.1
- ${NROFF} ${MANDOC} mailq.1 > mailq.0
-
-newaliases.0: newaliases.1
- ${NROFF} ${MANDOC} newaliases.1 > newaliases.0
-
-sendmail.0: sendmail.8
- ${NROFF} ${MANDOC} sendmail.8 > sendmail.0
-
-install: install-sendmail install-docs
-
-install-sendmail: sendmail
- ${INSTALL} -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} sendmail ${BINDIR}
- for i in ${LINKS}; do rm -f $$i; ln -s ${BINDIR}/sendmail $$i; done
- ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 644 /dev/null \
- ${STDIR}/sendmail.st
- ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 444 sendmail.hf ${HFDIR}
-
-# doesn't actually install them -- you may want to install pre-nroff versions
-install-docs: aliases.0 mailq.0 newaliases.0 sendmail.0
-
-clean:
- rm -f ${OBJS} sendmail aliases.0 mailq.0 newaliases.0 sendmail.0
-
-# dependencies
-# gross overkill, and yet still not quite enough....
-${OBJS}: sendmail.h conf.h
-
-depend:
diff --git a/usr.sbin/sendmail/src/Makefiles/Makefile.KSR b/usr.sbin/sendmail/src/Makefiles/Makefile.KSR
deleted file mode 100644
index c599587..0000000
--- a/usr.sbin/sendmail/src/Makefiles/Makefile.KSR
+++ /dev/null
@@ -1,112 +0,0 @@
-#
-# This Makefile is designed to work on the old "make" program. It does
-# not use the obj subdirectory. It also does not install documentation
-# automatically -- think of it as a quick start for sites that have the
-# old make program (I recommend that you get and port the new make if you
-# are going to be doing any signficant work on sendmail).
-#
-# This has been tested on KSR OS 1.2.2. Contributed by Todd C. Miller
-# <Todd.Miller@cs.colorado.edu>
-#
-# @(#)Makefile.KSR 8.2 (Berkeley) 9/13/95
-#
-
-# use O=-O (usual) or O=-g (debugging)
-O= -O
-
-# define the database mechanism used for alias lookups:
-# -DNDBM -- use new DBM
-# -DNEWDB -- use new Berkeley DB
-# -DNIS -- include NIS support
-# The really old (V7) DBM library is no longer supported.
-# See READ_ME for a description of how these flags interact.
-#
-DBMDEF= -DNDBM -DNEWDB -DNIS
-
-# environment definitions (e.g., -D_AIX3)
-ENVDEF=
-
-# see also conf.h for additional compilation flags
-
-# include directories
-INCDIRS=-I/usr/sww/include
-
-# library directories
-LIBDIRS=-L/usr/sww/lib -L/usr/shlib -L/usr/lib
-
-# libraries required on your system
-# delete -lresolv if you are not running BIND 4.9.x
-LIBS= -ldbm -ldb -lresolv
-
-# location of sendmail binary (usually /usr/sbin or /usr/lib)
-BINDIR= ${DESTDIR}/usr/sbin
-
-# location of sendmail.st file (usually /var/log or /usr/lib)
-STDIR= ${DESTDIR}/var/adm/sendmail
-
-# location of sendmail.hf file (usually /usr/share/misc or /usr/lib)
-HFDIR= ${DESTDIR}/usr/share/lib
-
-# additional .o files needed
-OBJADD=
-
-
-################### end of user configuration flags ######################
-
-CFLAGS= -I. $O ${INCDIRS} ${DBMDEF} ${ENVDEF}
-
-OBJS= alias.o arpadate.o clock.o collect.o conf.o convtime.o daemon.o \
- deliver.o domain.o envelope.o err.o headers.o macro.o main.o \
- map.o mci.o mime.o parseaddr.o queue.o readcf.o recipient.o \
- savemail.o srvrsmtp.o stab.o stats.o sysexits.o \
- trace.o udb.o usersmtp.o util.o version.o ${OBJADD}
-
-LINKS= ${BINDIR}/newaliases ${BINDIR}/mailq
-INSTALL=installbsd
-BINOWN= root
-BINGRP= kmem
-BINMODE=6555
-
-ALL= sendmail aliases.0 mailq.0 newaliases.0 sendmail.0
-
-all: ${ALL}
-
-sendmail: ${BEFORE} ${OBJS}
- ${CC} -o sendmail ${LDADD} ${OBJS} ${LIBDIRS} ${LIBS}
-
-#NROFF= nroff -h
-NROFF= groff -Tascii
-MANDOC= -mandoc
-
-aliases.0: aliases.5
- ${NROFF} ${MANDOC} aliases.5 > aliases.0
-
-mailq.0: mailq.1
- ${NROFF} ${MANDOC} mailq.1 > mailq.0
-
-newaliases.0: newaliases.1
- ${NROFF} ${MANDOC} newaliases.1 > newaliases.0
-
-sendmail.0: sendmail.8
- ${NROFF} ${MANDOC} sendmail.8 > sendmail.0
-
-install: install-sendmail install-docs
-
-install-sendmail: sendmail
- ${INSTALL} -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} sendmail ${BINDIR}
- for i in ${LINKS}; do rm -f $$i; ln -s ${BINDIR}/sendmail $$i; done
- cp /dev/null ${STDIR}/sendmail.st
- chmod 644 ${STDIR}/sendmail.st
- chown ${BINOWN}.${BINGRP} ${STDIR}/sendmail.st
- ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 444 sendmail.hf ${HFDIR}
- rm -f /usr/sbin/smtpd
-
-# doesn't actually install them -- you may want to install pre-nroff versions
-install-docs: aliases.0 mailq.0 newaliases.0 sendmail.0
-
-clean:
- rm -f ${OBJS} sendmail aliases.0 mailq.0 newaliases.0 sendmail.0
-
-# dependencies
-# gross overkill, and yet still not quite enough....
-${OBJS}: sendmail.h conf.h
diff --git a/usr.sbin/sendmail/src/Makefiles/Makefile.LUNA b/usr.sbin/sendmail/src/Makefiles/Makefile.LUNA
deleted file mode 100644
index 29b2d58..0000000
--- a/usr.sbin/sendmail/src/Makefiles/Makefile.LUNA
+++ /dev/null
@@ -1,147 +0,0 @@
-#
-# This Makefile is designed to work on the old "make" program. It does
-# not use the obj subdirectory. It also does not install documentation
-# automatically -- think of it as a quick start for sites that have the
-# old make program (I recommend that you get and port the new make if you
-# are going to be doing any signficant work on sendmail).
-#
-# @(#)Makefile.LUNA 8.5 (Berkeley) 9/13/95
-#
-
-# use O=-O (usual) or O=-g (debugging)
-O= -O
-
-# define the database mechanisms available for map & alias lookups:
-# -DNDBM -- use new DBM
-# -DNEWDB -- use new Berkeley DB
-# -DNIS -- include NIS support
-# The really old (V7) DBM library is no longer supported.
-# See READ_ME for a description of how these flags interact.
-#
-DBMDEF= -DNDBM
-
-# environment definitions (e.g., -D_AIX3)
-ENVDEF=
-
-# see also conf.h for additional compilation flags
-
-# include directories
-INCDIRS=-I/usr/sww/include
-
-# loader options
-LDOPTS=
-
-# library directories
-LIBDIRS=-L/usr/sww/lib
-
-# libraries required on your system
-LIBS=
-
-# location of sendmail binary (usually /usr/sbin or /usr/lib)
-BINDIR= ${DESTDIR}/usr/lib
-
-# location of sendmail.st file (usually /var/log or /usr/lib)
-STDIR= ${DESTDIR}/usr/lib
-
-# location of sendmail.hf file (usually /usr/share/misc or /usr/lib)
-HFDIR= ${DESTDIR}/usr/lib
-
-# additional .o files needed
-OBJADD=
-
-# additional pseudo-sources needed
-BEFORE= dirent.h stddef.h stdlib.h unistd.h limits.h time.h sys/time.h
-
-################### end of user configuration flags ######################
-
-CFLAGS= -I. $O ${INCDIRS} ${DBMDEF} ${ENVDEF}
-
-OBJS= alias.o arpadate.o clock.o collect.o conf.o convtime.o daemon.o \
- deliver.o domain.o envelope.o err.o headers.o macro.o main.o \
- map.o mci.o mime.o parseaddr.o queue.o readcf.o recipient.o \
- savemail.o srvrsmtp.o stab.o stats.o sysexits.o \
- trace.o udb.o usersmtp.o util.o version.o ${OBJADD}
-
-LINKS= ${DESTDIR}/usr/ucb/newaliases ${DESTDIR}/usr/ucb/mailq
-INSTALL=install
-BINOWN= root
-BINGRP= kmem
-BINMODE=6555
-
-ALL= sendmail aliases.0 mailq.0 newaliases.0 sendmail.0
-
-all: ${ALL}
-
-sendmail: ${BEFORE} ${OBJS}
- ${CC} -o sendmail ${LDOPTS} ${OBJS} ${LIBDIRS} ${LIBS}
-
-dirent.h:
- echo "#include <sys/dir.h>" > dirent.h
- echo "#define dirent direct" >> dirent.h
-
-stddef.h unistd.h limits.h:
- if [ -f /usr/include/$@ ]; then \
- ln -s /usr/include/$@ .; \
- else \
- cp /dev/null $@; \
- fi
-
-stdlib.h:
- if [ -f /usr/include/stdlib.h ]; then \
- ln -s /usr/include/stdlib.h .; \
- else \
- if [ -f /usr/include/libc.h ]; then \
- ln -s /usr/include/libc.h stdlib.h; \
- else \
- cp /dev/null stdlib.h; \
- fi; \
- fi
-
-# just for UNIOS-B
-time.h:
- echo "#ifndef _LOCAL_TIME_H_" > time.h
- echo "#define _LOCAL_TIME_H_" >> time.h
- cat /usr/include/time.h >> time.h
- echo "#endif" >> time.h
-
-sys/time.h:
- -mkdir sys
- echo "#ifndef _LOCAL_SYS_TIME_H_" > sys/time.h
- echo "#define _LOCAL_SYS_TIME_H_" >> sys/time.h
- cat /usr/include/sys/time.h >> sys/time.h
- echo "#endif" >> sys/time.h
-
-NROFF= nroff -h
-
-aliases.0: aliases.5
- ${NROFF} -mandoc aliases.5 > aliases.0
-
-mailq.0: mailq.1
- ${NROFF} -mandoc mailq.1 > mailq.0
-
-newaliases.0: newaliases.1
- ${NROFF} -mandoc newaliases.1 > newaliases.0
-
-sendmail.0: sendmail.8
- ${NROFF} -mandoc sendmail.8 > sendmail.0
-
-install: install-sendmail install-docs
-
-install-sendmail: sendmail
- ${INSTALL} -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} sendmail ${BINDIR}
- for i in ${LINKS}; do rm -f $$i; ln -s ${BINDIR}/sendmail $$i; done
- ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 644 /dev/null \
- ${STDIR}/sendmail.st
- ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 444 sendmail.hf ${HFDIR}
-
-# doesn't actually install them -- you may want to install pre-nroff versions
-install-docs: aliases.0 mailq.0 newaliases.0 sendmail.0
-
-clean:
- rm -f ${OBJS} sendmail aliases.0 mailq.0 newaliases.0 sendmail.0
-
-# dependencies
-# gross overkill, and yet still not quite enough....
-${OBJS}: sendmail.h conf.h
-
-depend:
diff --git a/usr.sbin/sendmail/src/Makefiles/Makefile.Linux b/usr.sbin/sendmail/src/Makefiles/Makefile.Linux
deleted file mode 100644
index dc74ca8..0000000
--- a/usr.sbin/sendmail/src/Makefiles/Makefile.Linux
+++ /dev/null
@@ -1,134 +0,0 @@
-#
-# This Makefile is designed to work on the old "make" program. It does
-# not use the obj subdirectory. It also does not install documentation
-# automatically -- think of it as a quick start for sites that have the
-# old make program (I recommend that you get and port the new make if you
-# are going to be doing any signficant work on sendmail).
-#
-# This has been tested on Linux 0.99p10.
-#
-# Linux doesn't really have standard places to install things, so this
-# Makefile is likely to require a lot of customization. Read it over
-# carefully before proceeding.
-#
-# If you don't want to install the Berkeley db package, remove -DNEWDB
-# from DBMDEF and -ldb from LIBS (but please consider installing it; see
-# the READ_ME file for details).
-#
-# This assumes libc 4.7.0 or later. If you have an earlier version of
-# the library, you may need to add -lbsd to LIBS *or* add -DHASSNPRINTF=0
-# to ENVDEF. If you are running libc < 4.4.4, you must use -DHASSNPRINTF=0
-# (or upgrade your libc -- an even better idea!).
-#
-# @(#)Makefile.Linux 8.15 (Berkeley) 9/26/95
-#
-
-# use O=-O (usual) or O=-g (debugging)
-O= -O
-
-# define the database mechanisms available for map & alias lookups:
-# -DNDBM -- use new DBM
-# -DNEWDB -- use new Berkeley DB
-# -DNIS -- include NIS support
-# The really old (V7) DBM library is no longer supported.
-# See READ_ME for a description of how these flags interact.
-#
-DBMDEF= -DNDBM -DNEWDB
-
-# environment definitions (e.g., -D_AIX3)
-ENVDEF=
-
-# see also conf.h for additional compilation flags
-
-# include directories
-INCDIRS=-I/usr/local/include
-
-# library directories
-LIBDIRS=-L/usr/local/lib
-
-# libraries required on your system
-LIBS= -lgdbm -ldb
-
-# location of sendmail binary (usually /usr/sbin or /usr/lib)
-BINDIR= ${DESTDIR}/usr/sbin
-
-# location of sendmail.st file (usually /var/log or /usr/lib)
-STDIR= ${DESTDIR}/etc
-
-# location of sendmail.hf file (usually /usr/share/misc or /usr/lib)
-HFDIR= ${DESTDIR}/usr/lib
-
-# additional .o files needed
-OBJADD=
-
-# additional pseudo-sources needed
-BEFORE=
-
-################### end of user configuration flags ######################
-
-CFLAGS= -I. $O ${INCDIRS} ${DBMDEF} ${ENVDEF}
-
-OBJS= alias.o arpadate.o clock.o collect.o conf.o convtime.o daemon.o \
- deliver.o domain.o envelope.o err.o headers.o macro.o main.o \
- map.o mci.o mime.o parseaddr.o queue.o readcf.o recipient.o \
- savemail.o srvrsmtp.o stab.o stats.o sysexits.o \
- trace.o udb.o usersmtp.o util.o version.o ${OBJADD}
-
-LINKS= ${DESTDIR}/usr/bin/newaliases ${DESTDIR}/usr/bin/mailq
-INSTALL=install
-BINOWN= root
-BINGRP= kmem
-BINMODE=6555
-
-ALL= sendmail aliases.0 mailq.0 newaliases.0 sendmail.0
-
-all: ${ALL}
-
-sendmail: ${BEFORE} ${OBJS}
- ${CC} -o sendmail ${OBJS} ${LIBDIRS} ${LIBS}
-
-unistd.h:
- cp /dev/null unistd.h
-
-dirent.h:
- echo "#include <sys/dir.h>" > dirent.h
- echo "#define dirent direct" >> dirent.h
-
-#NROFF= nroff -h
-NROFF= groff -Tascii
-MANDOC= -mandoc
-
-aliases.0: aliases.5
- ${NROFF} ${MANDOC} aliases.5 > aliases.0
-
-mailq.0: mailq.1
- ${NROFF} ${MANDOC} mailq.1 > mailq.0
-
-newaliases.0: newaliases.1
- ${NROFF} ${MANDOC} newaliases.1 > newaliases.0
-
-sendmail.0: sendmail.8
- ${NROFF} ${MANDOC} sendmail.8 > sendmail.0
-
-install: install-sendmail install-docs
-
-install-sendmail: sendmail
- ${INSTALL} -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} sendmail ${BINDIR}
- for i in ${LINKS}; do rm -f $$i; ln -s ${BINDIR}/sendmail $$i; done
- cp /dev/null ${STDIR}/sendmail.st
- chmod 644 ${STDIR}/sendmail.st
- chown ${BINOWN} ${STDIR}/sendmail.st
- chgrp ${BINGRP} ${STDIR}/sendmail.st
- ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 444 sendmail.hf ${HFDIR}
-
-# doesn't actually install them -- you may want to install pre-nroff versions
-install-docs: aliases.0 mailq.0 newaliases.0 sendmail.0
-
-clean:
- rm -f ${OBJS} sendmail aliases.0 mailq.0 newaliases.0 sendmail.0
-
-# dependencies
-# gross overkill, and yet still not quite enough....
-${OBJS}: sendmail.h conf.h
-
-depend:
diff --git a/usr.sbin/sendmail/src/Makefiles/Makefile.Mach386 b/usr.sbin/sendmail/src/Makefiles/Makefile.Mach386
deleted file mode 100644
index b72826f..0000000
--- a/usr.sbin/sendmail/src/Makefiles/Makefile.Mach386
+++ /dev/null
@@ -1,112 +0,0 @@
-#
-# This Makefile is designed to work on the old "make" program. It does
-# not use the obj subdirectory. It also does not install documentation
-# automatically -- think of it as a quick start for sites that have the
-# old make program (I recommend that you get and port the new make if you
-# are going to be doing any signficant work on sendmail).
-#
-# @(#)Makefile.Mach386 8.7 (Berkeley) 9/13/95
-#
-
-CC= gcc
-
-# use O=-O (usual) or O=-g (debugging)
-O= -O
-
-# define the database mechanisms available for map & alias lookups:
-# -DNDBM -- use new DBM
-# -DNEWDB -- use new Berkeley DB
-# -DNIS -- include NIS support
-# The really old (V7) DBM library is no longer supported.
-# See READ_ME for a description of how these flags interact.
-#
-DBMDEF= -DNDBM
-
-# environment definitions (e.g., -D_AIX3)
-ENVDEF=
-
-# see also conf.h for additional compilation flags
-
-# include directories
-INCDIRS=
-
-# loader options
-LDOPTS=
-
-# library directories
-LIBDIRS=
-
-# libraries required on your system
-LIBS= -ldbm
-
-# location of sendmail binary (usually /usr/sbin or /usr/lib)
-BINDIR= ${DESTDIR}/usr/lib
-
-# location of sendmail.st file (usually /var/log or /usr/lib)
-STDIR= ${DESTDIR}/usr/lib
-
-# location of sendmail.hf file (usually /usr/share/misc or /usr/lib)
-HFDIR= ${DESTDIR}/usr/lib
-
-# additional .o files needed
-OBJADD=
-
-################### end of user configuration flags ######################
-
-CFLAGS= -I. $O ${INCDIRS} ${DBMDEF} ${ENVDEF}
-
-OBJS= alias.o arpadate.o clock.o collect.o conf.o convtime.o daemon.o \
- deliver.o domain.o envelope.o err.o headers.o macro.o main.o \
- map.o mci.o mime.o parseaddr.o queue.o readcf.o recipient.o \
- savemail.o srvrsmtp.o stab.o stats.o sysexits.o \
- trace.o udb.o usersmtp.o util.o version.o ${OBJADD}
-
-LINKS= ${DESTDIR}/usr/ucb/newaliases ${DESTDIR}/usr/ucb/mailq
-INSTALL=install
-BINOWN= root
-BINGRP= kmem
-BINMODE=6555
-
-ALL= sendmail aliases.0 mailq.0 newaliases.0 sendmail.0
-
-all: ${ALL}
-
-sendmail: ${BEFORE} ${OBJS}
- ${CC} -o sendmail ${LDOPTS} ${OBJS} ${LIBDIRS} ${LIBS}
-
-#NROFF= nroff -h
-NROFF= groff -Tascii
-MANDOC= -mandoc
-
-aliases.0: aliases.5
- ${NROFF} ${MANDOC} aliases.5 > aliases.0
-
-mailq.0: mailq.1
- ${NROFF} ${MANDOC} mailq.1 > mailq.0
-
-newaliases.0: newaliases.1
- ${NROFF} ${MANDOC} newaliases.1 > newaliases.0
-
-sendmail.0: sendmail.8
- ${NROFF} ${MANDOC} sendmail.8 > sendmail.0
-
-install: install-sendmail install-docs
-
-install-sendmail: sendmail
- ${INSTALL} -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} sendmail ${BINDIR}
- for i in ${LINKS}; do rm -f $$i; ln -s ${BINDIR}/sendmail $$i; done
- ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 644 /dev/null \
- ${STDIR}/sendmail.st
- ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 444 sendmail.hf ${HFDIR}
-
-# doesn't actually install them -- you may want to install pre-nroff versions
-install-docs: aliases.0 mailq.0 newaliases.0 sendmail.0
-
-clean:
- rm -f ${OBJS} sendmail aliases.0 mailq.0 newaliases.0 sendmail.0
-
-# dependencies
-# gross overkill, and yet still not quite enough....
-${OBJS}: sendmail.h conf.h
-
-depend:
diff --git a/usr.sbin/sendmail/src/Makefiles/Makefile.NCR3000 b/usr.sbin/sendmail/src/Makefiles/Makefile.NCR3000
deleted file mode 100644
index 894407b..0000000
--- a/usr.sbin/sendmail/src/Makefiles/Makefile.NCR3000
+++ /dev/null
@@ -1,113 +0,0 @@
-#
-# This Makefile is designed to work on the old "make" program. It does
-# not use the obj subdirectory. It also does not install documentation
-# automatically -- think of it as a quick start for sites that have the
-# old make program (I recommend that you get and port the new make if you
-# are going to be doing any signficant work on sendmail).
-#
-# NCR 3000 support from Kevin Darcy <kevin@tech.mis.cfc.com>
-# and Tom Moore <tmoore@fievel.DaytonOH.NCR.COM>.
-#
-# @(#)Makefile.NCR3000 8.11 (Berkeley) 9/13/95
-#
-
-# use O=-O (usual) or O=-g (debugging)
-O= -O
-
-# define the database mechanisms available for map & alias lookups:
-# -DNDBM -- use new DBM
-# -DNEWDB -- use new Berkeley DB
-# -DNIS -- include NIS support
-# The really old (V7) DBM library is no longer supported.
-# See READ_ME for a description of how these flags interact.
-#
-DBMDEF= -DNDBM
-
-# environment definitions (e.g., -D_AIX3)
-ENVDEF= -DNCR3000
-
-# see also conf.h for additional compilation flags
-
-# include directories
-INCDIRS=-I/usr/include -I/usr/ucbinclude
-
-# loader options
-LDOPTS=
-
-# library directories
-LIBDIRS=-L/usr/ucblib
-
-# libraries required on your system
-LIBS= -lnsl -lnet -lsocket -lelf -lc -lucb
-
-# location of sendmail binary (usually /usr/sbin or /usr/lib)
-BINDIR= ${DESTDIR}/usr/ucblib
-
-# location of sendmail.st file (usually /var/log or /usr/lib)
-STDIR= ${DESTDIR}/var/ucblib
-
-# location of sendmail.hf file (usually /usr/share/misc or /usr/lib)
-HFDIR= ${DESTDIR}/usr/ucblib
-
-# additional .o files needed
-OBJADD=
-
-################### end of user configuration flags ######################
-
-CFLAGS= -I. $O ${INCDIRS} ${DBMDEF} ${ENVDEF}
-
-OBJS= alias.o arpadate.o clock.o collect.o conf.o convtime.o daemon.o \
- deliver.o domain.o envelope.o err.o headers.o macro.o main.o \
- map.o mci.o mime.o parseaddr.o queue.o readcf.o recipient.o \
- savemail.o srvrsmtp.o stab.o stats.o sysexits.o \
- trace.o udb.o usersmtp.o util.o version.o ${OBJADD}
-
-LINKS= ${DESTDIR}/usr/ucb/newaliases ${DESTDIR}/usr/ucb/mailq
-INSTALL=/usr/ucb/install
-BINOWN= root
-BINGRP= kmem
-BINMODE=6555
-
-ALL= sendmail aliases.0 mailq.0 newaliases.0 sendmail.0
-
-all: ${ALL}
-
-sendmail: ${BEFORE} ${OBJS}
- ${CC} -o sendmail ${LDOPTS} ${OBJS} ${LIBDIRS} ${LIBS}
-
-NROFF= /usr/ucb/nroff -h
-#NROFF= groff -Tascii
-MANDOC= -mandoc
-
-aliases.0: aliases.5
- ${NROFF} ${MANDOC} aliases.5 > aliases.0
-
-mailq.0: mailq.1
- ${NROFF} ${MANDOC} mailq.1 > mailq.0
-
-newaliases.0: newaliases.1
- ${NROFF} ${MANDOC} newaliases.1 > newaliases.0
-
-sendmail.0: sendmail.8
- ${NROFF} ${MANDOC} sendmail.8 > sendmail.0
-
-install: install-sendmail install-docs
-
-install-sendmail: sendmail
- ${INSTALL} -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} sendmail ${BINDIR}
- for i in ${LINKS}; do rm -f $$i; ln -s ${BINDIR}/sendmail $$i; done
- ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 644 /dev/null \
- ${STDIR}/sendmail.st
- ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 444 sendmail.hf ${HFDIR}
-
-# doesn't actually install them -- you may want to install pre-nroff versions
-install-docs: aliases.0 mailq.0 newaliases.0 sendmail.0
-
-clean:
- rm -f ${OBJS} sendmail aliases.0 mailq.0 newaliases.0 sendmail.0
-
-# dependencies
-# gross overkill, and yet still not quite enough....
-${OBJS}: sendmail.h conf.h
-
-depend:
diff --git a/usr.sbin/sendmail/src/Makefiles/Makefile.NEWS-OS.4.x b/usr.sbin/sendmail/src/Makefiles/Makefile.NEWS-OS.4.x
deleted file mode 100644
index 3add6ac..0000000
--- a/usr.sbin/sendmail/src/Makefiles/Makefile.NEWS-OS.4.x
+++ /dev/null
@@ -1,110 +0,0 @@
-#
-# This Makefile is designed to work on the old "make" program. It does
-# not use the obj subdirectory. It also does not install documentation
-# automatically -- think of it as a quick start for sites that have the
-# old make program (I recommend that you get and port the new make if you
-# are going to be doing any signficant work on sendmail).
-#
-# @(#)Makefile.NEWS-OS.4.x 8.4 (Berkeley) 9/13/95
-#
-
-# use O=-O (usual) or O=-g (debugging)
-O= -O
-
-# define the database mechanisms available for map & alias lookups:
-# -DNDBM -- use new DBM
-# -DNEWDB -- use new Berkeley DB
-# -DNIS -- include NIS support
-# The really old (V7) DBM library is no longer supported.
-# See READ_ME for a description of how these flags interact.
-#
-DBMDEF= -DNDBM
-
-# environment definitions (e.g., -D_AIX3)
-ENVDEF=
-
-# see also conf.h for additional compilation flags
-
-# include directories
-INCDIRS=-I/usr/sww/include
-
-# loader options
-LDOPTS=
-
-# library directories
-LIBDIRS=-L/usr/sww/lib
-
-# libraries required on your system
-LIBS= -lmld
-
-# location of sendmail binary (usually /usr/sbin or /usr/lib)
-BINDIR= ${DESTDIR}/usr/lib
-
-# location of sendmail.st file (usually /var/log or /usr/lib)
-STDIR= ${DESTDIR}/usr/lib
-
-# location of sendmail.hf file (usually /usr/share/misc or /usr/lib)
-HFDIR= ${DESTDIR}/usr/lib
-
-# additional .o files needed
-OBJADD=
-
-################### end of user configuration flags ######################
-
-CFLAGS= -I. $O ${INCDIRS} ${DBMDEF} ${ENVDEF}
-
-OBJS= alias.o arpadate.o clock.o collect.o conf.o convtime.o daemon.o \
- deliver.o domain.o envelope.o err.o headers.o macro.o main.o \
- map.o mci.o mime.o parseaddr.o queue.o readcf.o recipient.o \
- savemail.o srvrsmtp.o stab.o stats.o sysexits.o \
- trace.o udb.o usersmtp.o util.o version.o ${OBJADD}
-
-LINKS= ${DESTDIR}/usr/ucb/newaliases ${DESTDIR}/usr/ucb/mailq
-INSTALL=install
-BINOWN= root
-BINGRP= kmem
-BINMODE=6555
-
-ALL= sendmail aliases.0 mailq.0 newaliases.0 sendmail.0
-
-all: ${ALL}
-
-sendmail: ${BEFORE} ${OBJS}
- ${CC} -o sendmail ${LDOPTS} ${OBJS} ${LIBDIRS} ${LIBS}
-
-#NROFF= nroff -h
-NROFF= groff -Tascii
-MANDOC= -mandoc
-
-aliases.0: aliases.5
- ${NROFF} ${MANDOC} aliases.5 > aliases.0
-
-mailq.0: mailq.1
- ${NROFF} ${MANDOC} mailq.1 > mailq.0
-
-newaliases.0: newaliases.1
- ${NROFF} ${MANDOC} newaliases.1 > newaliases.0
-
-sendmail.0: sendmail.8
- ${NROFF} ${MANDOC} sendmail.8 > sendmail.0
-
-install: install-sendmail install-docs
-
-install-sendmail: sendmail
- ${INSTALL} -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} sendmail ${BINDIR}
- for i in ${LINKS}; do rm -f $$i; ln -s ${BINDIR}/sendmail $$i; done
- ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 644 /dev/null \
- ${STDIR}/sendmail.st
- ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 444 sendmail.hf ${HFDIR}
-
-# doesn't actually install them -- you may want to install pre-nroff versions
-install-docs: aliases.0 mailq.0 newaliases.0 sendmail.0
-
-clean:
- rm -f ${OBJS} sendmail aliases.0 mailq.0 newaliases.0 sendmail.0
-
-# dependencies
-# gross overkill, and yet still not quite enough....
-${OBJS}: sendmail.h conf.h
-
-depend:
diff --git a/usr.sbin/sendmail/src/Makefiles/Makefile.NEWS-OS.6.x b/usr.sbin/sendmail/src/Makefiles/Makefile.NEWS-OS.6.x
deleted file mode 100644
index 3bdd6a0..0000000
--- a/usr.sbin/sendmail/src/Makefiles/Makefile.NEWS-OS.6.x
+++ /dev/null
@@ -1,133 +0,0 @@
-#
-# This Makefile is designed to work on the old "make" program. It does
-# not use the obj subdirectory. It also does not install documentation
-# automatically -- think of it as a quick start for sites that have the
-# old make program (I recommend that you get and port the new make if you
-# are going to be doing any signficant work on sendmail).
-#
-# This has been tested on NEWS-OS 6.0.3
-#
-# @(#)Makefile.NEWS-OS.6.x 8.5 (Berkeley) 9/13/95
-#
-
-# use O=-O (usual) or O=-g (debugging)
-O=
-
-# make sure that /bin/cc is used (do not use /usr/ucb/cc).
-CC= /bin/cc
-
-# define the database mechanism used for alias lookups:
-# -DNDBM -- use new DBM
-# -DNEWDB -- use new Berkeley DB
-# -DNIS -- include NIS support
-# The really old (V7) DBM library is no longer supported.
-# See READ_ME for a description of how these flags interact.
-#
-DBMDEF= -DNDBM -DNIS
-
-# environment definitions (e.g., -D_AIX3)
-# define SYSLOG_BUFSIZE=256 if you have a problem on syslog buffer size
-# define SPT_TYPE=SPT_NONE if you are using NEWS-OS 6.0.1
-ENVDEF= -DSYSLOG_BUFSIZE=256 # -DSPT_TYPE=SPT_NONE
-
-# see also conf.h for additional compilation flags
-
-# include directories
-INCDIRS=-I/usr/sww/include
-
-# library directories
-LIBDIRS=-L/usr/sww/lib
-
-# libraries required on your system
-# delete -l44bsd if you are not running BIND 4.9.x
-# -lndbm can be used instead of ndbm.o with NEWS-OS 6.1 or later
-LIBS= ndbm.o -lelf -lsocket -lnsl -lresolv # -l44bsd # with NDBM
-#LIBS= -lelf -lsocket -lnsl -ldb -lresolv # -l44bsd # with NEWDB
-
-# location of sendmail binary (usually /usr/sbin or /usr/lib)
-BINDIR= ${DESTDIR}/usr/lib
-
-# location of sendmail.st file (usually /var/log or /usr/lib)
-STDIR= ${DESTDIR}/etc
-
-# location of sendmail.hf file (usually /usr/share/misc or /usr/lib)
-HFDIR= ${DESTDIR}/usr/lib
-
-# additional .o files needed
-OBJADD=
-
-# things to be made before compilation begins
-BEFORE= sysexits.h ndbm.o
-
-################### end of user configuration flags ######################
-
-CFLAGS= -I. $O ${INCDIRS} ${DBMDEF} ${ENVDEF}
-
-OBJS= alias.o arpadate.o clock.o collect.o conf.o convtime.o daemon.o \
- deliver.o domain.o envelope.o err.o headers.o macro.o main.o \
- map.o mci.o mime.o parseaddr.o queue.o readcf.o recipient.o \
- savemail.o srvrsmtp.o stab.o stats.o sysexits.o \
- trace.o udb.o usersmtp.o util.o version.o ${OBJADD}
-
-LINKS= ${DESTDIR}/usr/ucb/newaliases ${DESTDIR}/usr/ucb/mailq
-INSTALL=/usr/ucb/install
-BINOWN= root
-BINGRP= sys
-BINMODE=6555
-
-ALL= sendmail aliases.0 mailq.0 newaliases.0 sendmail.0
-
-all: ${ALL}
-
-sendmail: ${BEFORE} ${OBJS}
- ${CC} -o sendmail ${OBJS} ${LIBDIRS} ${LIBS}
-
-sysexits.h:
- ln -s /usr/ucbinclude/sysexits.h .
-
-ndbm.o:
- if [ ! -f /usr/include/ndbm.h ]; then \
- ln -s /usr/ucbinclude/ndbm.h .; \
- fi; \
- if [ -f /usr/lib/libndbm.a ]; then \
- ar x /usr/lib/libndbm.a ndbm.o; \
- else \
- ar x /usr/ucblib/libucb.a ndbm.o; \
- fi;
-
-#NROFF= nroff -h
-NROFF= groff -Tascii
-MANDOC= -mandoc
-
-aliases.0: aliases.5
- ${NROFF} ${MANDOC} aliases.5 > aliases.0
-
-mailq.0: mailq.1
- ${NROFF} ${MANDOC} mailq.1 > mailq.0
-
-newaliases.0: newaliases.1
- ${NROFF} ${MANDOC} newaliases.1 > newaliases.0
-
-sendmail.0: sendmail.8
- ${NROFF} ${MANDOC} sendmail.8 > sendmail.0
-
-install: install-sendmail install-docs
-
-install-sendmail: sendmail
- ${INSTALL} -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} sendmail ${BINDIR}
- for i in ${LINKS}; do rm -f $$i; ln -s ${BINDIR}/sendmail $$i; done
- ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 644 /dev/null \
- ${STDIR}/sendmail.st
- ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 444 sendmail.hf ${HFDIR}
-
-# doesn't actually install them -- you may want to install pre-nroff versions
-install-docs: aliases.0 mailq.0 newaliases.0 sendmail.0
-
-clean:
- rm -f ${OBJS} sendmail aliases.0 mailq.0 newaliases.0 sendmail.0
-
-# dependencies
-# gross overkill, and yet still not quite enough....
-${OBJS}: sendmail.h conf.h
-
-depend:
diff --git a/usr.sbin/sendmail/src/Makefiles/Makefile.NEXTSTEP b/usr.sbin/sendmail/src/Makefiles/Makefile.NEXTSTEP
deleted file mode 100644
index 492cba1..0000000
--- a/usr.sbin/sendmail/src/Makefiles/Makefile.NEXTSTEP
+++ /dev/null
@@ -1,128 +0,0 @@
-#
-# This Makefile is designed to work on the old "make" program. It does
-# not use the obj subdirectory. It also does not install documentation
-# automatically -- think of it as a quick start for sites that have the
-# old make program (I recommend that you get and port the new make if you
-# are going to be doing any signficant work on sendmail).
-#
-# This has been tested on NEXTSTEP 3.3.
-#
-# @(#)Makefile.NEXTSTEP 8.4 (Berkeley) 9/14/95
-#
-
-# use O=-O (usual) or O=-g (debugging)
-O= -O
-
-# NEXTSTEP 3.1 and 3.2 only support m68k and i386
-ARCH= -arch m68k -arch i386 -arch hppa -arch sparc
-#ARCH= -arch m68k -arch i386
-
-COPTS= -Wno-precomp -pipe
-
-# define the database mechanisms available for map & alias lookups:
-# -DNDBM -- use new DBM
-# -DNEWDB -- use new Berkeley DB
-# -DNIS -- include NIS support
-# The really old (V7) DBM library is no longer supported.
-# See READ_ME for a description of how these flags interact.
-#
-DBMDEF= -DNDBM -DNIS -DNETINFO
-#DBMDEF= -DNDBM -DNEWDB -DNIS -DNETINFO
-
-# environment definitions (e.g., -D_AIX3)
-ENVDEF= -DNeXT
-
-# see also conf.h for additional compilation flags
-
-# include directories
-INCDIRS=-I/usr/local/include
-
-# library directories
-LIBDIRS=-L/usr/local/lib
-
-# libraries required on your system
-LIBS= -ldbm
-#LIBS= -ldbm -ldb
-
-# location of sendmail binary (usually /usr/sbin or /usr/lib)
-BINDIR= ${DESTDIR}/usr/lib
-
-# location of sendmail.st file (usually /var/log or /usr/lib)
-STDIR= ${DESTDIR}/etc/sendmail
-
-# location of sendmail.hf file (usually /usr/share/misc or /usr/lib)
-HFDIR= ${DESTDIR}/usr/lib
-
-# additional .o files needed
-OBJADD=
-
-# additional pseudo-sources needed
-BEFORE= unistd.h dirent.h
-
-################### end of user configuration flags ######################
-
-CFLAGS= -I. $O ${INCDIRS} ${DBMDEF} ${ENVDEF} ${COPTS} ${ARCH}
-
-OBJS= alias.o arpadate.o clock.o collect.o conf.o convtime.o daemon.o \
- deliver.o domain.o envelope.o err.o headers.o macro.o main.o \
- map.o mci.o mime.o parseaddr.o queue.o readcf.o recipient.o \
- savemail.o srvrsmtp.o stab.o stats.o sysexits.o \
- trace.o udb.o usersmtp.o util.o version.o ${OBJADD}
-
-LINKS= ${DESTDIR}/usr/ucb/newaliases ${DESTDIR}/usr/ucb/mailq
-INSTALL=install
-BINOWN= root
-BINGRP= kmem
-BINMODE=6555
-
-ALL= sendmail aliases.0 mailq.0 newaliases.0 sendmail.0
-
-all: ${ALL}
-
-sendmail: ${BEFORE} ${OBJS}
- ${CC} ${ARCH} -o sendmail ${OBJS} ${LIBDIRS} ${LIBS}
-
-unistd.h:
- cp /dev/null unistd.h
-
-dirent.h:
- echo "#include <sys/dir.h>" > dirent.h
- echo "#define dirent direct" >> dirent.h
-
-#NROFF= nroff -h
-NROFF= groff -Tascii
-MANDOC= -mandoc
-
-aliases.0: aliases.5
- ${NROFF} ${MANDOC} aliases.5 > aliases.0
-
-mailq.0: mailq.1
- ${NROFF} ${MANDOC} mailq.1 > mailq.0
-
-newaliases.0: newaliases.1
- ${NROFF} ${MANDOC} newaliases.1 > newaliases.0
-
-sendmail.0: sendmail.8
- ${NROFF} ${MANDOC} sendmail.8 > sendmail.0
-
-install: install-sendmail install-docs
-
-install-sendmail: sendmail
- ${INSTALL} -s -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} sendmail ${BINDIR}
- for i in ${LINKS}; do rm -f $$i; ln -s ${BINDIR}/sendmail $$i; done
- cp /dev/null ${STDIR}/sendmail.st
- chown ${BINOWN}.${BINGRP} ${STDIR}/sendmail.st
- chmod 644 ${STDIR}/sendmail.st
- ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 444 sendmail.hf ${HFDIR}
-
-# doesn't actually install them -- you may want to install pre-nroff versions
-install-docs: aliases.0 mailq.0 newaliases.0 sendmail.0
-
-clean:
- rm -f ${OBJS} sendmail aliases.0 mailq.0 newaliases.0 sendmail.0
-
-# dependencies
-# gross overkill, and yet still not quite enough....
-${OBJS}: sendmail.h conf.h
-
-depend:
diff --git a/usr.sbin/sendmail/src/Makefiles/Makefile.NeXT b/usr.sbin/sendmail/src/Makefiles/Makefile.NeXT
deleted file mode 100644
index 5e3fa5f..0000000
--- a/usr.sbin/sendmail/src/Makefiles/Makefile.NeXT
+++ /dev/null
@@ -1,122 +0,0 @@
-#
-# This Makefile is designed to work on the old "make" program. It does
-# not use the obj subdirectory. It also does not install documentation
-# automatically -- think of it as a quick start for sites that have the
-# old make program (I recommend that you get and port the new make if you
-# are going to be doing any signficant work on sendmail).
-#
-# This has been tested on NeXT 2.1.
-#
-# @(#)Makefile.NeXT 8.10 (Berkeley) 9/13/95
-#
-
-# use O=-O (usual) or O=-g (debugging)
-O= -O
-
-# define the database mechanisms available for map & alias lookups:
-# -DNDBM -- use new DBM
-# -DNEWDB -- use new Berkeley DB
-# -DNIS -- include NIS support
-# The really old (V7) DBM library is no longer supported.
-# See READ_ME for a description of how these flags interact.
-#
-DBMDEF= -DNDBM -DNIS -DNETINFO
-#DBMDEF= -DNDBM -DNEWDB -DNIS -DNETINFO
-
-# environment definitions (e.g., -D_AIX3)
-ENVDEF= -DNeXT
-
-# see also conf.h for additional compilation flags
-
-# include directories
-INCDIRS=-I/usr/local/include
-
-# library directories
-LIBDIRS=-L/usr/local/lib
-
-# libraries required on your system
-LIBS= -ldbm
-#LIBS= -ldbm -ldb
-
-# location of sendmail binary (usually /usr/sbin or /usr/lib)
-BINDIR= ${DESTDIR}/usr/lib
-
-# location of sendmail.st file (usually /var/log or /usr/lib)
-STDIR= ${DESTDIR}/etc/sendmail
-
-# location of sendmail.hf file (usually /usr/share/misc or /usr/lib)
-HFDIR= ${DESTDIR}/usr/lib
-
-# additional .o files needed
-OBJADD=
-
-# additional pseudo-sources needed
-BEFORE= unistd.h dirent.h
-
-################### end of user configuration flags ######################
-
-CFLAGS= -I. $O ${INCDIRS} ${DBMDEF} ${ENVDEF} ${COPTS}
-
-OBJS= alias.o arpadate.o clock.o collect.o conf.o convtime.o daemon.o \
- deliver.o domain.o envelope.o err.o headers.o macro.o main.o \
- map.o mci.o mime.o parseaddr.o queue.o readcf.o recipient.o \
- savemail.o srvrsmtp.o stab.o stats.o sysexits.o \
- trace.o udb.o usersmtp.o util.o version.o ${OBJADD}
-
-LINKS= ${DESTDIR}/usr/ucb/newaliases ${DESTDIR}/usr/ucb/mailq
-INSTALL=install
-BINOWN= root
-BINGRP= kmem
-BINMODE=6555
-
-ALL= sendmail aliases.0 mailq.0 newaliases.0 sendmail.0
-
-all: ${ALL}
-
-sendmail: ${BEFORE} ${OBJS}
- ${CC} -o sendmail ${OBJS} ${LIBDIRS} ${LIBS}
-
-unistd.h:
- cp /dev/null unistd.h
-
-dirent.h:
- echo "#include <sys/dir.h>" > dirent.h
- echo "#define dirent direct" >> dirent.h
-
-#NROFF= nroff -h
-NROFF= groff -Tascii
-MANDOC= -mandoc
-
-aliases.0: aliases.5
- ${NROFF} ${MANDOC} aliases.5 > aliases.0
-
-mailq.0: mailq.1
- ${NROFF} ${MANDOC} mailq.1 > mailq.0
-
-newaliases.0: newaliases.1
- ${NROFF} ${MANDOC} newaliases.1 > newaliases.0
-
-sendmail.0: sendmail.8
- ${NROFF} ${MANDOC} sendmail.8 > sendmail.0
-
-install: install-sendmail install-docs
-
-install-sendmail: sendmail
- ${INSTALL} -s -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} sendmail ${BINDIR}
- for i in ${LINKS}; do rm -f $$i; ln -s ${BINDIR}/sendmail $$i; done
- cp /dev/null ${STDIR}/sendmail.st
- chown ${BINOWN}.${BINGRP} ${STDIR}/sendmail.st
- chmod 644 ${STDIR}/sendmail.st
- ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 444 sendmail.hf ${HFDIR}
-
-# doesn't actually install them -- you may want to install pre-nroff versions
-install-docs: aliases.0 mailq.0 newaliases.0 sendmail.0
-
-clean:
- rm -f ${OBJS} sendmail aliases.0 mailq.0 newaliases.0 sendmail.0
-
-# dependencies
-# gross overkill, and yet still not quite enough....
-${OBJS}: sendmail.h conf.h
-
-depend:
diff --git a/usr.sbin/sendmail/src/Makefiles/Makefile.NetBSD b/usr.sbin/sendmail/src/Makefiles/Makefile.NetBSD
deleted file mode 100644
index 9a6f36f..0000000
--- a/usr.sbin/sendmail/src/Makefiles/Makefile.NetBSD
+++ /dev/null
@@ -1,47 +0,0 @@
-#
-# NetBSD Makefile
-#
-# @(#)Makefile.NetBSD 8.3 (Berkeley) 9/13/95
-# @Id: Makefile.NetBSD,v 1.3 1994/02/01 05:33:44 glass Exp $
-#
-
-PROG= sendmail
-
-# define the database format to use for aliases et al. Can be -DNEWDB (for
-# the new BSD database package -- this is preferred) or -DNDBM for the NDBM
-# database package. The old putrescent V7 DBM package is no longer
-# supported.
-# You can define both NEWDB and NDBM during a transition period; old
-# databases are read, but the new format will be used on any rebuilds. On
-# really gnarly systems, you can set this to null; it will crawl like a high
-# spiral snail, but it will work.
-DBMDEF= -DNEWDB -DNIS
-
-#nasty warning about gcc 2.4.x caused bugs
-CFLAGS=-I${.CURDIR} ${DBMDEF} -DNETISO
-#CFLAGS+=-I${.CURDIR} ${DBMDEF} -DNETISO
-
-SRCS= alias.c arpadate.c clock.c collect.c conf.c convtime.c daemon.c \
- deliver.c domain.c envelope.c err.c headers.c macro.c main.c map.c \
- mci.c mime.c parseaddr.c queue.c readcf.c recipient.c savemail.c \
- srvrsmtp.c stab.c stats.c sysexits.c trace.c udb.c usersmtp.c \
- util.c version.c
-MAN1= mailq.0 newaliases.0
-MAN5= aliases.0
-MAN8= sendmail.0
-LINKS= /usr/sbin/sendmail /usr/bin/newaliases \
- /usr/sbin/sendmail /usr/bin/mailq
-INSTALL=install
-BINDIR= /usr/sbin
-BINOWN= root
-BINMODE=4555
-
-beforeinstall:
-# ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 644 /dev/null \
-# ${DESTDIR}/etc/sendmail.fc
- ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 644 /dev/null \
- ${DESTDIR}/var/log/sendmail.st
- ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 444 ${.CURDIR}/sendmail.hf \
- ${DESTDIR}/usr/share/misc
-
-.include <bsd.prog.mk>
diff --git a/usr.sbin/sendmail/src/Makefiles/Makefile.NonStop-UX b/usr.sbin/sendmail/src/Makefiles/Makefile.NonStop-UX
deleted file mode 100644
index acb8215..0000000
--- a/usr.sbin/sendmail/src/Makefiles/Makefile.NonStop-UX
+++ /dev/null
@@ -1,116 +0,0 @@
-#
-# This Makefile is designed to work on the old "make" program. It does
-# not use the obj subdirectory. It also does not install documentation
-# automatically -- think of it as a quick start for sites that have the
-# old make program (I recommend that you get and port the new make if you
-# are going to be doing any signficant work on sendmail).
-#
-# Tandem Support from Rick McCarty <mccarty@mpd.tandem.com>.
-# (I don't think this actually compiles cleanly -- I had trouble
-# integrating Rick's changes. EPA 6/94)
-#
-# @(#)Makefile.NonStop-UX 8.6 (Berkeley) 9/13/95
-#
-
-# make sure the shell constructs below use the right shell
-SHELL= /bin/sh
-
-# use O=-O (usual) or O=-g (debugging)
-O= -O
-
-CC= gcc
-
-# define the database mechanism used for alias lookups:
-# -DNDBM -- use new DBM
-# -DNEWDB -- use new Berkeley DB
-# -DNIS -- include NIS support
-# The really old (V7) DBM library is no longer supported.
-# See READ_ME for a description of how these flags interact.
-#
-DBMDEF= -DNDBM
-
-# environment definitions (e.g., -D_AIX3)
-ENVDEF= -DNonStop_UX_BXX -D_SVID
-
-# see also conf.h for additional compilation flags
-
-# include directories
-INCDIRS=-I/usr/include -I/usr/ucbinclude
-
-# library directories
-LIBDIRS=-L/usr/ucblib
-
-# libraries required on your system
-LIBS= -lresolv -lsocket -lnsl -lelf -lucb
-
-# location of sendmail binary (usually /usr/sbin or /usr/lib)
-BINDIR= ${DESTDIR}/usr/ucblib
-
-# location of sendmail.st file (usually /var/log or /usr/lib)
-STDIR= ${DESTDIR}/usr/ucblib
-
-# location of sendmail.hf file (usually /usr/share/misc or /usr/lib)
-HFDIR= ${DESTDIR}/usr/ucblib
-
-# additional .o files needed
-OBJADD=
-
-################### end of user configuration flags ######################
-
-CFLAGS= -I. $O ${INCDIRS} ${DBMDEF} ${ENVDEF}
-
-OBJS= alias.o arpadate.o clock.o collect.o conf.o convtime.o daemon.o \
- deliver.o domain.o envelope.o err.o headers.o macro.o main.o \
- map.o mci.o mime.o parseaddr.o queue.o readcf.o recipient.o \
- savemail.o srvrsmtp.o stab.o stats.o sysexits.o \
- trace.o udb.o usersmtp.o util.o version.o ${OBJADD}
-
-LINKS= ${DESTDIR}/usr/ucb/newaliases ${DESTDIR}/usr/ucb/mailq
-INSTALL=/usr/ucb/install
-BINOWN= root
-BINGRP= mail
-BINMODE=6555
-
-ALL= sendmail aliases.0 mailq.0 newaliases.0 sendmail.0
-
-all: ${ALL}
-
-sendmail: ${BEFORE} ${OBJS}
- ${CC} -o sendmail ${OBJS} ${LIBDIRS} ${LIBS}
-
-#NROFF= nroff -h
-NROFF= groff -Tascii
-MANDOC= -mandoc
-
-aliases.0: aliases.5
- ${NROFF} ${MANDOC} aliases.5 > aliases.0
-
-mailq.0: mailq.1
- ${NROFF} ${MANDOC} mailq.1 > mailq.0
-
-newaliases.0: newaliases.1
- ${NROFF} ${MANDOC} newaliases.1 > newaliases.0
-
-sendmail.0: sendmail.8
- ${NROFF} ${MANDOC} sendmail.8 > sendmail.0
-
-install: install-sendmail install-docs
-
-install-sendmail: sendmail
- ${INSTALL} -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} sendmail ${BINDIR}
- for i in ${LINKS}; do rm -f $$i; ln -s ${BINDIR}/sendmail $$i; done
- ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 644 /dev/null \
- ${STDIR}/sendmail.st
- ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 444 sendmail.hf ${HFDIR}
-
-# doesn't actually install them -- you may want to install pre-nroff versions
-install-docs: aliases.0 mailq.0 newaliases.0 sendmail.0
-
-clean:
- rm -f ${OBJS} sendmail aliases.0 mailq.0 newaliases.0 sendmail.0
-
-# dependencies
-# gross overkill, and yet still not quite enough....
-${OBJS}: sendmail.h conf.h
-
-depend:
diff --git a/usr.sbin/sendmail/src/Makefiles/Makefile.OSF1 b/usr.sbin/sendmail/src/Makefiles/Makefile.OSF1
deleted file mode 100644
index de556d6..0000000
--- a/usr.sbin/sendmail/src/Makefiles/Makefile.OSF1
+++ /dev/null
@@ -1,118 +0,0 @@
-#
-# This Makefile is designed to work on the old "make" program. It does
-# not use the obj subdirectory. It also does not install documentation
-# automatically -- think of it as a quick start for sites that have the
-# old make program (I recommend that you get and port the new make if you
-# are going to be doing any signficant work on sendmail).
-#
-# This has been tested on OSF/1 1.3
-#
-# @(#)Makefile.OSF1 8.9 (Berkeley) 9/13/95
-#
-
-# use O=-O (usual) or O=-g (debugging)
-O= -O
-
-# native compiler requires -Olimit to optimize properly
-CC= cc -Olimit 1000
-
-# define the database mechanism used for alias lookups:
-# -DNDBM -- use new DBM
-# -DNEWDB -- use new Berkeley DB
-# -DNIS -- include NIS support
-# The really old (V7) DBM library is no longer supported.
-# See READ_ME for a description of how these flags interact.
-#
-DBMDEF= -DNDBM -DNEWDB -DNIS
-
-# environment definitions (e.g., -D_AIX3)
-ENVDEF=
-
-# see also conf.h for additional compilation flags
-
-# include directories
-INCDIRS=-I/usr/sww/include
-
-# library directories
-LIBDIRS=-L/usr/sww/lib -L/usr/shlib -L/usr/lib
-
-# libraries required on your system
-# delete -lresolv if you are not running BIND 4.9.x
-LIBS= -ldbm -ldb -lresolv
-
-# location of sendmail binary (usually /usr/sbin or /usr/lib)
-BINDIR= ${DESTDIR}/usr/sbin
-
-# location of sendmail.st file (usually /var/log or /usr/lib)
-STDIR= ${DESTDIR}/var/adm/sendmail
-
-# location of sendmail.hf file (usually /usr/share/misc or /usr/lib)
-HFDIR= ${DESTDIR}/usr/share/lib
-
-# additional .o files needed
-OBJADD=
-
-# additional link flags
-#LDADD= -non_shared
-
-################### end of user configuration flags ######################
-
-CFLAGS= -I. $O ${INCDIRS} ${DBMDEF} ${ENVDEF}
-
-OBJS= alias.o arpadate.o clock.o collect.o conf.o convtime.o daemon.o \
- deliver.o domain.o envelope.o err.o headers.o macro.o main.o \
- map.o mci.o mime.o parseaddr.o queue.o readcf.o recipient.o \
- savemail.o srvrsmtp.o stab.o stats.o sysexits.o \
- trace.o udb.o usersmtp.o util.o version.o ${OBJADD}
-
-LINKS= ${BINDIR}/newaliases ${BINDIR}/mailq
-INSTALL=installbsd
-BINOWN= root
-BINGRP= kmem
-BINMODE=6555
-
-ALL= sendmail aliases.0 mailq.0 newaliases.0 sendmail.0
-
-all: ${ALL}
-
-sendmail: ${BEFORE} ${OBJS}
- ${CC} -o sendmail ${LDADD} ${OBJS} ${LIBDIRS} ${LIBS}
-
-#NROFF= nroff -h
-NROFF= groff -Tascii
-MANDOC= -mandoc
-
-aliases.0: aliases.5
- ${NROFF} ${MANDOC} aliases.5 > aliases.0
-
-mailq.0: mailq.1
- ${NROFF} ${MANDOC} mailq.1 > mailq.0
-
-newaliases.0: newaliases.1
- ${NROFF} ${MANDOC} newaliases.1 > newaliases.0
-
-sendmail.0: sendmail.8
- ${NROFF} ${MANDOC} sendmail.8 > sendmail.0
-
-install: install-sendmail install-docs
-
-install-sendmail: sendmail
- ${INSTALL} -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} sendmail ${BINDIR}
- for i in ${LINKS}; do rm -f $$i; ln -s ${BINDIR}/sendmail $$i; done
- cp /dev/null ${STDIR}/sendmail.st
- chmod 644 ${STDIR}/sendmail.st
- chown ${BINOWN}.${BINGRP} ${STDIR}/sendmail.st
- ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 444 sendmail.hf ${HFDIR}
- rm -f /usr/sbin/smtpd
-
-# doesn't actually install them -- you may want to install pre-nroff versions
-install-docs: aliases.0 mailq.0 newaliases.0 sendmail.0
-
-clean:
- rm -f ${OBJS} sendmail aliases.0 mailq.0 newaliases.0 sendmail.0
-
-# dependencies
-# gross overkill, and yet still not quite enough....
-${OBJS}: sendmail.h conf.h
-
-depend:
diff --git a/usr.sbin/sendmail/src/Makefiles/Makefile.PTX b/usr.sbin/sendmail/src/Makefiles/Makefile.PTX
deleted file mode 100644
index 2a6ffce..0000000
--- a/usr.sbin/sendmail/src/Makefiles/Makefile.PTX
+++ /dev/null
@@ -1,117 +0,0 @@
-#
-# This Makefile is designed to work on the old "make" program. It does
-# not use the obj subdirectory. It also does not install documentation
-# automatically -- think of it as a quick start for sites that have the
-# old make program (I recommend that you get and port the new make if you
-# are going to be doing any signficant work on sendmail).
-#
-# For Sequent DYNIX/ptx.
-#
-# From Tim "Pinball Wizard" Wright <timw@sequent.com>.
-#
-# @(#)Makefile.PTX 8.8 (Berkeley) 9/13/95
-#
-
-# use O=-O (usual) or O=-g (debugging)
-O= -g
-
-# define the database mechanisms available for map & alias lookups:
-# -DNDBM -- use new DBM
-# -DNEWDB -- use new Berkeley DB
-# -DNIS -- include NIS support
-# The really old (V7) DBM library is no longer supported.
-# See READ_ME for a description of how these flags interact.
-#
-DBMDEF= -DNDBM
-
-# environment definitions (e.g., -D_AIX3)
-ENVDEF=
-
-# see also conf.h for additional compilation flags
-
-# include directories
-#INCDIRS=-I/usr/sww/include
-INCDIRS=
-
-# loader options
-LDOPTS=
-
-# library directories
-#LIBDIRS=-L/usr/sww/lib
-LIBDIRS=
-
-# libraries required on your system
-#LIBS= -ldb -ldbm
-LIBS= -lsocket -linet -lnsl -lseq
-
-# location of sendmail binary (usually /usr/sbin or /usr/lib)
-BINDIR= ${DESTDIR}/usr/lib
-
-# location of sendmail.st file (usually /var/log or /usr/lib)
-STDIR= ${DESTDIR}/usr/lib
-
-# location of sendmail.hf file (usually /usr/share/misc or /usr/lib)
-HFDIR= ${DESTDIR}/usr/lib
-
-# additional .o files needed
-OBJADD=
-
-################### end of user configuration flags ######################
-
-CFLAGS= -I. $O ${INCDIRS} ${DBMDEF} ${ENVDEF}
-
-OBJS= alias.o arpadate.o clock.o collect.o conf.o convtime.o daemon.o \
- deliver.o domain.o envelope.o err.o headers.o macro.o main.o \
- map.o mci.o mime.o parseaddr.o queue.o readcf.o recipient.o \
- savemail.o srvrsmtp.o stab.o stats.o sysexits.o \
- trace.o udb.o usersmtp.o util.o version.o ${OBJADD}
-
-LINKS= ${DESTDIR}/usr/bin/newaliases ${DESTDIR}/usr/bin/mailq
-INSTALL=install
-BINOWN= root
-BINGRP= sys
-BINMODE=6555
-
-ALL= sendmail aliases.0 newaliases.0 sendmail.0
-
-all: ${ALL}
-
-sendmail: $& ${BEFORE} ${OBJS}
- ${CC} -o sendmail ${LDOPTS} ${OBJS} ${LIBDIRS} ${LIBS}
-
-#NROFF= nroff -h
-NROFF= groff -Tascii
-MANDOC= -mandoc
-
-aliases.0: aliases.5
- ${NROFF} ${MANDOC} aliases.5 > aliases.0
-
-mailq.0: mailq.1
- ${NROFF} ${MANDOC} mailq.1 > mailq.0
-
-newaliases.0: newaliases.1
- ${NROFF} ${MANDOC} newaliases.1 > newaliases.0
-
-sendmail.0: sendmail.8
- ${NROFF} ${MANDOC} sendmail.8 > sendmail.0
-
-install: install-sendmail install-docs
-
-install-sendmail: sendmail
- ${INSTALL} -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} sendmail ${BINDIR}
- for i in ${LINKS}; do rm -f $$i; ln -s ${BINDIR}/sendmail $$i; done
- ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 644 /dev/null \
- ${STDIR}/sendmail.st
- ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 444 sendmail.hf ${HFDIR}
-
-# doesn't actually install them -- you may want to install pre-nroff versions
-install-docs: aliases.0 mailq.0 newaliases.0 sendmail.0
-
-clean:
- rm -f ${OBJS} sendmail aliases.0 mailq.0 newaliases.0 sendmail.0
-
-# dependencies
-# gross overkill, and yet still not quite enough....
-${OBJS}: sendmail.h conf.h
-
-depend:
diff --git a/usr.sbin/sendmail/src/Makefiles/Makefile.Paragon b/usr.sbin/sendmail/src/Makefiles/Makefile.Paragon
deleted file mode 100644
index d58119b..0000000
--- a/usr.sbin/sendmail/src/Makefiles/Makefile.Paragon
+++ /dev/null
@@ -1,114 +0,0 @@
-#
-# This Makefile is designed to work on the old "make" program. It does
-# not use the obj subdirectory. It also does not install documentation
-# automatically -- think of it as a quick start for sites that have the
-# old make program (I recommend that you get and port the new make if you
-# are going to be doing any signficant work on sendmail).
-#
-# This has been tested on OSF/1 1.3
-#
-# @(#)Makefile.Paragon 8.5 (Berkeley) 9/13/95
-#
-
-# use O=-O (usual) or O=-g (debugging)
-O= -O
-
-# define the database mechanism used for alias lookups:
-# -DNDBM -- use new DBM
-# -DNEWDB -- use new Berkeley DB
-# -DNIS -- include NIS support
-# The really old (V7) DBM library is no longer supported.
-# See READ_ME for a description of how these flags interact.
-#
-DBMDEF= -DNDBM
-
-# environment definitions (e.g., -D_AIX3)
-ENVDEF=
-
-# see also conf.h for additional compilation flags
-
-# include directories
-INCDIRS=-I/usr/sww/include
-
-# library directories
-LIBDIRS=-L/usr/sww/lib -L/usr/shlib -L/usr/lib
-
-# libraries required on your system
-LIBS= -ldbm
-
-# location of sendmail binary (usually /usr/sbin or /usr/lib)
-BINDIR= ${DESTDIR}/usr/sbin
-
-# location of sendmail.st file (usually /var/log or /usr/lib)
-STDIR= ${DESTDIR}/var/adm/sendmail
-
-# location of sendmail.hf file (usually /usr/share/misc or /usr/lib)
-HFDIR= ${DESTDIR}/usr/share/lib
-
-# additional .o files needed
-OBJADD=
-
-# additional link flags
-#LDADD= -non_shared
-
-################### end of user configuration flags ######################
-
-CFLAGS= -I. $O ${INCDIRS} ${DBMDEF} ${ENVDEF}
-
-OBJS= alias.o arpadate.o clock.o collect.o conf.o convtime.o daemon.o \
- deliver.o domain.o envelope.o err.o headers.o macro.o main.o \
- map.o mci.o mime.o parseaddr.o queue.o readcf.o recipient.o \
- savemail.o srvrsmtp.o stab.o stats.o sysexits.o \
- trace.o udb.o usersmtp.o util.o version.o ${OBJADD}
-
-LINKS= ${BINDIR}/newaliases ${BINDIR}/mailq
-INSTALL=installbsd
-BINOWN= root
-BINGRP= kmem
-BINMODE=6555
-
-ALL= sendmail aliases.0 mailq.0 newaliases.0 sendmail.0
-
-all: ${ALL}
-
-sendmail: ${BEFORE} ${OBJS}
- ${CC} -o sendmail ${LDADD} ${OBJS} ${LIBDIRS} ${LIBS}
-
-#NROFF= nroff -h
-NROFF= groff -Tascii
-MANDOC= -mandoc
-
-aliases.0: aliases.5
- ${NROFF} ${MANDOC} aliases.5 > aliases.0
-
-mailq.0: mailq.1
- ${NROFF} ${MANDOC} mailq.1 > mailq.0
-
-newaliases.0: newaliases.1
- ${NROFF} ${MANDOC} newaliases.1 > newaliases.0
-
-sendmail.0: sendmail.8
- ${NROFF} ${MANDOC} sendmail.8 > sendmail.0
-
-install: install-sendmail install-docs
-
-install-sendmail: sendmail
- ${INSTALL} -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} sendmail ${BINDIR}
- for i in ${LINKS}; do rm -f $$i; ln -s ${BINDIR}/sendmail $$i; done
- cp /dev/null ${STDIR}/sendmail.st
- chmod 644 ${STDIR}/sendmail.st
- chown ${BINOWN}.${BINGRP} ${STDIR}/sendmail.st
- ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 444 sendmail.hf ${HFDIR}
- rm -f /usr/sbin/smtpd
-
-# doesn't actually install them -- you may want to install pre-nroff versions
-install-docs: aliases.0 mailq.0 newaliases.0 sendmail.0
-
-clean:
- rm -f ${OBJS} sendmail aliases.0 mailq.0 newaliases.0 sendmail.0
-
-# dependencies
-# gross overkill, and yet still not quite enough....
-${OBJS}: sendmail.h conf.h
-
-depend:
diff --git a/usr.sbin/sendmail/src/Makefiles/Makefile.RISCos b/usr.sbin/sendmail/src/Makefiles/Makefile.RISCos
deleted file mode 100644
index bbb507d..0000000
--- a/usr.sbin/sendmail/src/Makefiles/Makefile.RISCos
+++ /dev/null
@@ -1,122 +0,0 @@
-#
-# This Makefile is designed to work on the old "make" program. It does
-# not use the obj subdirectory. It also does not install documentation
-# automatically -- think of it as a quick start for sites that have the
-# old make program (I recommend that you get and port the new make if you
-# are going to be doing any signficant work on sendmail).
-#
-# For Mips RISC/os 4.52.
-#
-# @(#)Makefile.RISCos 8.11 (Berkeley) 9/13/95
-#
-
-# use O=-O (usual) or O=-g (debugging)
-O= -O
-
-# be sure we are compiling in BSD mode
-CC= cc -systype bsd43 -Olimit 900
-
-# define the database mechanism used for alias lookups:
-# -DNDBM -- use new DBM
-# -DNEWDB -- use new Berkeley DB
-# -DNIS -- include NIS support
-# The really old (V7) DBM library is no longer supported.
-# See READ_ME for a description of how these flags interact.
-#
-DBMDEF= -DNDBM
-
-# environment definitions (e.g., -D_AIX3)
-ENVDEF= -DRISCOS
-
-# see also conf.h for additional compilation flags
-
-# include directories
-#INCDIRS=-I/usr/sww/include
-
-# library directories
-#LIBDIRS=-L/usr/sww/lib
-
-# libraries required on your system
-LIBS= -lmld
-
-# location of sendmail binary (usually /usr/sbin or /usr/lib)
-BINDIR= ${DESTDIR}/usr/lib
-
-# location of sendmail.st file (usually /var/log or /usr/lib)
-STDIR= ${DESTDIR}/etc
-
-# location of sendmail.hf file (usually /usr/share/misc or /usr/lib)
-HFDIR= ${DESTDIR}/usr/lib
-
-# additional .o files needed
-OBJADD=
-
-# additional pseudo-sources needed
-BEFORE= stdlib.h dirent.h unistd.h stddef.h
-
-################### end of user configuration flags ######################
-
-CFLAGS= -I. $O ${INCDIRS} ${DBMDEF} ${ENVDEF}
-
-OBJS= alias.o arpadate.o clock.o collect.o conf.o convtime.o daemon.o \
- deliver.o domain.o envelope.o err.o headers.o macro.o main.o \
- map.o mci.o mime.o parseaddr.o queue.o readcf.o recipient.o \
- savemail.o srvrsmtp.o stab.o stats.o sysexits.o \
- trace.o udb.o usersmtp.o util.o version.o ${OBJADD}
-
-LINKS= ${DESTDIR}/usr/ucb/newaliases ${DESTDIR}/usr/ucb/mailq
-INSTALL=/usr/bsd43/bin/install
-BINOWN= root
-BINGRP= sys
-BINMODE=6555
-
-ALL= sendmail aliases.0 mailq.0 newaliases.0 sendmail.0
-
-all: ${ALL}
-
-sendmail: ${BEFORE} ${OBJS}
- ${CC} -o sendmail ${OBJS} ${LIBDIRS} ${LIBS}
-
-stdlib.h unistd.h stddef.h:
- cp /dev/null $@
-
-dirent.h:
- echo "#include <sys/dir.h>" > dirent.h
- echo "#define dirent direct" >> dirent.h
-
-#NROFF= nroff -h
-NROFF= groff -Tascii
-MANDOC= -mandoc
-
-aliases.0: aliases.5
- ${NROFF} ${MANDOC} aliases.5 > aliases.0
-
-mailq.0: mailq.1
- ${NROFF} ${MANDOC} mailq.1 > mailq.0
-
-newaliases.0: newaliases.1
- ${NROFF} ${MANDOC} newaliases.1 > newaliases.0
-
-sendmail.0: sendmail.8
- ${NROFF} ${MANDOC} sendmail.8 > sendmail.0
-
-install: install-sendmail install-docs
-
-install-sendmail: sendmail
- ${INSTALL} -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} sendmail ${BINDIR}
- for i in ${LINKS}; do rm -f $$i; ln -s ${BINDIR}/sendmail $$i; done
- ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 644 /dev/null \
- ${STDIR}/sendmail.st
- ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 444 sendmail.hf ${HFDIR}
-
-# doesn't actually install them -- you may want to install pre-nroff versions
-install-docs: aliases.0 mailq.0 newaliases.0 sendmail.0
-
-clean:
- rm -f ${OBJS} sendmail aliases.0 mailq.0 newaliases.0 sendmail.0
-
-# dependencies
-# gross overkill, and yet still not quite enough....
-${OBJS}: sendmail.h conf.h
-
-depend:
diff --git a/usr.sbin/sendmail/src/Makefiles/Makefile.SCO b/usr.sbin/sendmail/src/Makefiles/Makefile.SCO
deleted file mode 100644
index c6ffaaf..0000000
--- a/usr.sbin/sendmail/src/Makefiles/Makefile.SCO
+++ /dev/null
@@ -1,109 +0,0 @@
-#
-# This Makefile is designed to work on the old "make" program. It does
-# not use the obj subdirectory. It also does not install documentation
-# automatically -- think of it as a quick start for sites that have the
-# old make program (I recommend that you get and port the new make if you
-# are going to be doing any signficant work on sendmail).
-#
-# This has been tested on SCO.
-#
-# @(#)Makefile.SCO 8.8 (Berkeley) 9/13/95
-#
-
-# use O=-O (usual) or O=-g (debugging)
-O= -O
-
-# define the database mechanisms available for map & alias lookups:
-# -DNDBM -- use new DBM
-# -DNEWDB -- use new Berkeley DB
-# -DNIS -- include NIS support
-# The really old (V7) DBM library is no longer supported.
-# See READ_ME for a description of how these flags interact.
-#
-DBMDEF=
-
-# environment definitions (e.g., -D_AIX3)
-ENVDEF= -D_SCO_unix_
-
-# see also conf.h for additional compilation flags
-
-# include directories
-INCDIRS=
-
-# library directories
-LIBDIRS=
-
-# libraries required on your system
-LIBS= -lsocket
-
-# location of sendmail binary (usually /usr/sbin or /usr/lib)
-BINDIR= ${DESTDIR}/usr/lib
-
-# location of sendmail.st file (usually /var/log or /usr/lib)
-STDIR= ${DESTDIR}/usr/lib
-
-# location of sendmail.hf file (usually /usr/share/misc or /usr/lib)
-HFDIR= ${DESTDIR}/usr/lib
-
-# additional .o files needed
-OBJADD=
-
-################### end of user configuration flags ######################
-
-CFLAGS= -I. $O ${INCDIRS} ${DBMDEF} ${ENVDEF}
-
-OBJS= alias.o arpadate.o clock.o collect.o conf.o convtime.o daemon.o \
- deliver.o domain.o envelope.o err.o headers.o macro.o main.o \
- map.o mci.o mime.o parseaddr.o queue.o readcf.o recipient.o \
- savemail.o srvrsmtp.o stab.o stats.o sysexits.o \
- trace.o udb.o usersmtp.o util.o version.o ${OBJADD}
-
-LINKS= ${DESTDIR}/usr/ucb/newaliases ${DESTDIR}/usr/ucb/mailq
-INSTALL=install
-BINOWN= root
-BINGRP= kmem
-BINMODE=6555
-
-ALL= sendmail aliases.0 mailq.0 newaliases.0 sendmail.0
-
-all: ${ALL}
-
-sendmail: ${BEFORE} ${OBJS}
- ${CC} -o sendmail ${OBJS} ${LIBDIRS} ${LIBS}
-
-#NROFF= nroff -h
-NROFF= groff -Tascii
-MANDOC= -mandoc
-
-aliases.0: aliases.5
- ${NROFF} ${MANDOC} aliases.5 > aliases.0
-
-mailq.0: mailq.1
- ${NROFF} ${MANDOC} mailq.1 > mailq.0
-
-newaliases.0: newaliases.1
- ${NROFF} ${MANDOC} newaliases.1 > newaliases.0
-
-sendmail.0: sendmail.8
- ${NROFF} ${MANDOC} sendmail.8 > sendmail.0
-
-install: install-sendmail install-docs
-
-install-sendmail: sendmail
- ${INSTALL} -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} sendmail ${BINDIR}
- for i in ${LINKS}; do rm -f $$i; ln -s ${BINDIR}/sendmail $$i; done
- ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 644 /dev/null \
- ${STDIR}/sendmail.st
- ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 444 sendmail.hf ${HFDIR}
-
-# doesn't actually install them -- you may want to install pre-nroff versions
-install-docs: aliases.0 mailq.0 newaliases.0 sendmail.0
-
-clean:
- rm -f ${OBJS} sendmail aliases.0 mailq.0 newaliases.0 sendmail.0
-
-# dependencies
-# gross overkill, and yet still not quite enough....
-${OBJS}: sendmail.h conf.h
-
-depend:
diff --git a/usr.sbin/sendmail/src/Makefiles/Makefile.SCO.3.2v4.2 b/usr.sbin/sendmail/src/Makefiles/Makefile.SCO.3.2v4.2
deleted file mode 100644
index 33289f62..0000000
--- a/usr.sbin/sendmail/src/Makefiles/Makefile.SCO.3.2v4.2
+++ /dev/null
@@ -1,109 +0,0 @@
-#
-# This Makefile is designed to work on the old "make" program. It does
-# not use the obj subdirectory. It also does not install documentation
-# automatically -- think of it as a quick start for sites that have the
-# old make program (I recommend that you get and port the new make if you
-# are going to be doing any signficant work on sendmail).
-#
-# Tested on SCO rel 4.2 by Marian Durkovic <marian@svf.stuba.sk>.
-#
-# @(#)Makefile.SCO.3.2v4.2 8.2 (Berkeley) 9/13/95
-#
-
-# use O=-O (usual) or O=-g (debugging)
-O= -O
-
-# define the database mechanisms available for map & alias lookups:
-# -DNDBM -- use new DBM
-# -DNEWDB -- use new Berkeley DB
-# -DNIS -- include NIS support
-# The really old (V7) DBM library is no longer supported.
-# See READ_ME for a description of how these flags interact.
-#
-DBMDEF=-DNDBM
-
-# environment definitions (e.g., -D_AIX3)
-ENVDEF= -D_SCO_unix_4_2
-
-# see also conf.h for additional compilation flags
-
-# include directories
-INCDIRS=
-
-# library directories
-LIBDIRS=
-
-# libraries required on your system
-LIBS= -lsocket -lndbm
-
-# location of sendmail binary (usually /usr/sbin or /usr/lib)
-BINDIR= ${DESTDIR}/usr/lib
-
-# location of sendmail.st file (usually /var/log or /usr/lib)
-STDIR= ${DESTDIR}/usr/lib
-
-# location of sendmail.hf file (usually /usr/share/misc or /usr/lib)
-HFDIR= ${DESTDIR}/usr/lib
-
-# additional .o files needed
-OBJADD=
-
-################### end of user configuration flags ######################
-
-CFLAGS= -I. $O ${INCDIRS} ${DBMDEF} ${ENVDEF}
-
-OBJS= alias.o arpadate.o clock.o collect.o conf.o convtime.o daemon.o \
- deliver.o domain.o envelope.o err.o headers.o macro.o main.o \
- map.o mci.o mime.o parseaddr.o queue.o readcf.o recipient.o \
- savemail.o srvrsmtp.o stab.o stats.o sysexits.o \
- trace.o udb.o usersmtp.o util.o version.o ${OBJADD}
-
-LINKS= ${DESTDIR}/usr/ucb/newaliases ${DESTDIR}/usr/ucb/mailq
-INSTALL=install
-BINOWN= root
-BINGRP= kmem
-BINMODE=6555
-
-ALL= sendmail aliases.0 mailq.0 newaliases.0 sendmail.0
-
-all: ${ALL}
-
-sendmail: ${BEFORE} ${OBJS}
- ${CC} -o sendmail ${OBJS} ${LIBDIRS} ${LIBS}
-
-#NROFF= nroff -h
-NROFF= groff -Tascii
-MANDOC= -mandoc
-
-aliases.0: aliases.5
- ${NROFF} ${MANDOC} aliases.5 > aliases.0
-
-mailq.0: mailq.1
- ${NROFF} ${MANDOC} mailq.1 > mailq.0
-
-newaliases.0: newaliases.1
- ${NROFF} ${MANDOC} newaliases.1 > newaliases.0
-
-sendmail.0: sendmail.8
- ${NROFF} ${MANDOC} sendmail.8 > sendmail.0
-
-install: install-sendmail install-docs
-
-install-sendmail: sendmail
- ${INSTALL} -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} sendmail ${BINDIR}
- for i in ${LINKS}; do rm -f $$i; ln -s ${BINDIR}/sendmail $$i; done
- ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 644 /dev/null \
- ${STDIR}/sendmail.st
- ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 444 sendmail.hf ${HFDIR}
-
-# doesn't actually install them -- you may want to install pre-nroff versions
-install-docs: aliases.0 mailq.0 newaliases.0 sendmail.0
-
-clean:
- rm -f ${OBJS} sendmail aliases.0 mailq.0 newaliases.0 sendmail.0
-
-# dependencies
-# gross overkill, and yet still not quite enough....
-${OBJS}: sendmail.h conf.h
-
-depend:
diff --git a/usr.sbin/sendmail/src/Makefiles/Makefile.SVR4 b/usr.sbin/sendmail/src/Makefiles/Makefile.SVR4
deleted file mode 100644
index a6da0ba..0000000
--- a/usr.sbin/sendmail/src/Makefiles/Makefile.SVR4
+++ /dev/null
@@ -1,117 +0,0 @@
-#
-# This Makefile is designed to work on the old "make" program. It does
-# not use the obj subdirectory. It also does not install documentation
-# automatically -- think of it as a quick start for sites that have the
-# old make program (I recommend that you get and port the new make if you
-# are going to be doing any signficant work on sendmail).
-#
-# Based on a Makefile for Dell SVR4 Issue 2.2 from Kimmo Suominen
-# <kim@grendel.lut.fi> -- I haven't tested this myself. It may
-# work on other SVR4 ports.
-#
-# @(#)Makefile.SVR4 8.7 (Berkeley) 9/13/95
-#
-
-# make sure the shell constructs below use the right shell
-SHELL= /bin/sh
-
-# use O=-O (usual) or O=-g (debugging)
-O= -O
-
-CC= gcc
-#DESTDIR=/usr/local/sendmail
-
-# define the database mechanism used for alias lookups:
-# -DNDBM -- use new DBM
-# -DNEWDB -- use new Berkeley DB
-# -DNIS -- include NIS support
-# The really old (V7) DBM library is no longer supported.
-# See READ_ME for a description of how these flags interact.
-#
-DBMDEF= -DNEWDB -DNDBM
-
-# environment definitions (e.g., -D_AIX3)
-ENVDEF= -D__svr4__
-
-# see also conf.h for additional compilation flags
-
-# include directories
-INCDIRS=
-
-# library directories
-LIBDIRS=
-
-# libraries required on your system
-LIBS= -ldb -ldbm -lresolv -lsocket -lnsl -lelf
-
-# location of sendmail binary (usually /usr/sbin or /usr/lib)
-BINDIR= ${DESTDIR}/usr/ucblib
-
-# location of sendmail.st file (usually /var/log or /usr/lib)
-STDIR= ${DESTDIR}/usr/ucblib
-
-# location of sendmail.hf file (usually /usr/share/misc or /usr/lib)
-HFDIR= ${DESTDIR}/usr/ucblib
-
-# additional .o files needed
-OBJADD=
-
-################### end of user configuration flags ######################
-
-CFLAGS= -I. $O ${INCDIRS} ${DBMDEF} ${ENVDEF}
-
-OBJS= alias.o arpadate.o clock.o collect.o conf.o convtime.o daemon.o \
- deliver.o domain.o envelope.o err.o headers.o macro.o main.o \
- map.o mci.o mime.o parseaddr.o queue.o readcf.o recipient.o \
- savemail.o srvrsmtp.o stab.o stats.o sysexits.o \
- trace.o udb.o usersmtp.o util.o version.o ${OBJADD}
-
-LINKS= ${DESTDIR}/usr/ucb/newaliases ${DESTDIR}/usr/ucb/mailq
-INSTALL=/usr/ucb/install
-BINOWN= root
-BINGRP= mail
-BINMODE=6555
-
-ALL= sendmail aliases.0 mailq.0 newaliases.0 sendmail.0
-
-all: ${ALL}
-
-sendmail: ${BEFORE} ${OBJS}
- ${CC} -o sendmail ${OBJS} ${LIBDIRS} ${LIBS}
-
-#NROFF= nroff -h
-NROFF= groff -Tascii
-MANDOC= -mandoc
-
-aliases.0: aliases.5
- ${NROFF} ${MANDOC} aliases.5 > aliases.0
-
-mailq.0: mailq.1
- ${NROFF} ${MANDOC} mailq.1 > mailq.0
-
-newaliases.0: newaliases.1
- ${NROFF} ${MANDOC} newaliases.1 > newaliases.0
-
-sendmail.0: sendmail.8
- ${NROFF} ${MANDOC} sendmail.8 > sendmail.0
-
-install: install-sendmail install-docs
-
-install-sendmail: sendmail
- ${INSTALL} -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} sendmail ${BINDIR}
- for i in ${LINKS}; do rm -f $$i; ln -s ${BINDIR}/sendmail $$i; done
- ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 644 /dev/null \
- ${STDIR}/sendmail.st
- ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 444 sendmail.hf ${HFDIR}
-
-# doesn't actually install them -- you may want to install pre-nroff versions
-install-docs: aliases.0 mailq.0 newaliases.0 sendmail.0
-
-clean:
- rm -f ${OBJS} sendmail aliases.0 mailq.0 newaliases.0 sendmail.0
-
-# dependencies
-# gross overkill, and yet still not quite enough....
-${OBJS}: sendmail.h conf.h
-
-depend:
diff --git a/usr.sbin/sendmail/src/Makefiles/Makefile.Solaris b/usr.sbin/sendmail/src/Makefiles/Makefile.Solaris
deleted file mode 100644
index ccdffc5..0000000
--- a/usr.sbin/sendmail/src/Makefiles/Makefile.Solaris
+++ /dev/null
@@ -1,124 +0,0 @@
-#
-# This Makefile is designed to work on the old "make" program. It does
-# not use the obj subdirectory. It also does not install documentation
-# automatically -- think of it as a quick start for sites that have the
-# old make program (I recommend that you get and port the new make if you
-# are going to be doing any significant work on sendmail).
-#
-# This has been tested on Solaris 2.1 and 2.2. If you are compiling
-# for Solaris 2.3, use Makefile.SunOS.5.x.
-#
-# @(#)Makefile.Solaris 8.15 (Berkeley) 9/13/95
-#
-
-# use O=-O (usual) or O=-g (debugging)
-# warning: do not use -O with versions of gcc prior to 2.6
-O= -O
-
-CC= gcc
-
-# define the database mechanism used for alias lookups:
-# -DNDBM -- use new DBM
-# -DNEWDB -- use new Berkeley DB
-# -DNIS -- include NIS support
-# The really old (V7) DBM library is no longer supported.
-# See READ_ME for a description of how these flags interact.
-#
-DBMDEF= -DNDBM -DNIS
-
-# environment definitions (e.g., -D_AIX3)
-# include -DSOLARIS_2_3 for version 2.3 and higher
-ENVDEF= -DSOLARIS
-
-# see also conf.h for additional compilation flags
-
-# include directories
-INCDIRS=-I/usr/sww/include
-
-# library directories
-LIBDIRS=-L/usr/sww/lib
-
-# libraries required on your system
-# delete -l44bsd if you are not running BIND 4.9.x
-LIBS= -lresolv -l44bsd -lsocket -lnsl -lelf
-
-# location of sendmail binary (usually /usr/sbin or /usr/lib)
-BINDIR= ${DESTDIR}/usr/lib
-
-# location of sendmail.st file (usually /var/log or /usr/lib)
-STDIR= ${DESTDIR}/var/log
-
-# location of sendmail.hf file (usually /usr/share/misc or /usr/lib)
-HFDIR= ${DESTDIR}/etc/mail
-
-# additional .o files needed
-OBJADD=
-
-# things to be made before compilation begins
-BEFORE= sysexits.h
-
-################### end of user configuration flags ######################
-
-CFLAGS= -I. $O ${INCDIRS} ${DBMDEF} ${ENVDEF}
-
-OBJS= alias.o arpadate.o clock.o collect.o conf.o convtime.o daemon.o \
- deliver.o domain.o envelope.o err.o headers.o macro.o main.o \
- map.o mci.o mime.o parseaddr.o queue.o readcf.o recipient.o \
- savemail.o srvrsmtp.o stab.o stats.o sysexits.o \
- trace.o udb.o usersmtp.o util.o version.o ${OBJADD}
-
-LINKS= ${DESTDIR}/usr/ucb/newaliases ${DESTDIR}/usr/ucb/mailq
-INSTALL=/usr/ucb/install
-BINOWN= root
-BINGRP= sys
-BINMODE=6555
-
-ALL= sendmail aliases.0 mailq.0 newaliases.0 sendmail.0
-
-all: ${ALL}
-
-sendmail: ${BEFORE} ${OBJS}
- ${CC} -o sendmail ${OBJS} ${LIBDIRS} ${LIBS}
-
-sysexits.h:
- if [ -r /usr/ucbinclude/sysexits.h ]; \
- then \
- ln -s /usr/ucbinclude/sysexits.h; \
- fi
-
-#NROFF= nroff -h
-NROFF= groff -Tascii
-MANDOC= -mandoc
-
-aliases.0: aliases.5
- ${NROFF} ${MANDOC} aliases.5 > aliases.0
-
-mailq.0: mailq.1
- ${NROFF} ${MANDOC} mailq.1 > mailq.0
-
-newaliases.0: newaliases.1
- ${NROFF} ${MANDOC} newaliases.1 > newaliases.0
-
-sendmail.0: sendmail.8
- ${NROFF} ${MANDOC} sendmail.8 > sendmail.0
-
-install: install-sendmail install-docs
-
-install-sendmail: sendmail
- ${INSTALL} -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} sendmail ${BINDIR}
- for i in ${LINKS}; do rm -f $$i; ln -s ${BINDIR}/sendmail $$i; done
- ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 644 /dev/null \
- ${STDIR}/sendmail.st
- ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 444 sendmail.hf ${HFDIR}
-
-# doesn't actually install them -- you may want to install pre-nroff versions
-install-docs: aliases.0 mailq.0 newaliases.0 sendmail.0
-
-clean:
- rm -f ${OBJS} sendmail aliases.0 mailq.0 newaliases.0 sendmail.0
-
-# dependencies
-# gross overkill, and yet still not quite enough....
-${OBJS}: sendmail.h conf.h
-
-depend:
diff --git a/usr.sbin/sendmail/src/Makefiles/Makefile.SunOS b/usr.sbin/sendmail/src/Makefiles/Makefile.SunOS
deleted file mode 100644
index 61bb3d6..0000000
--- a/usr.sbin/sendmail/src/Makefiles/Makefile.SunOS
+++ /dev/null
@@ -1,116 +0,0 @@
-#
-# This Makefile is designed to work on the old "make" program. It does
-# not use the obj subdirectory. It also does not install documentation
-# automatically -- think of it as a quick start for sites that have the
-# old make program (I recommend that you get and port the new make if you
-# are going to be doing any signficant work on sendmail).
-#
-# This has been tested on SunOS 4.1.[12].
-# For SunOS 4.0.3, add -DSUNOS403 to the ENVDEF macro, and
-# create empty files stdlib.h and stddef.h in your
-# compile directory.
-#
-# @(#)Makefile.SunOS 8.9 (Berkeley) 9/30/95
-#
-
-# use O=-O (usual) or O=-g (debugging)
-O= -O
-
-# define the database mechanisms available for map & alias lookups:
-# -DNDBM -- use new DBM
-# -DNEWDB -- use new Berkeley DB
-# -DNIS -- include NIS support
-# The really old (V7) DBM library is no longer supported.
-# See READ_ME for a description of how these flags interact.
-#
-DBMDEF= -DNDBM -DNEWDB -DNIS
-
-# environment definitions (e.g., -D_AIX3)
-# need to add -DSUNOS403 if you are on a SunOS 4.0.3 system
-ENVDEF=
-
-# see also conf.h for additional compilation flags
-
-# include directories
-INCDIRS=-I/usr/sww/include
-
-# loader options
-LDOPTS= -Bstatic
-
-# library directories
-LIBDIRS=-L/usr/sww/lib
-
-# libraries required on your system
-LIBS= -ldb -lresolv
-
-# location of sendmail binary (usually /usr/sbin or /usr/lib)
-BINDIR= ${DESTDIR}/usr/lib
-
-# location of sendmail.st file (usually /var/log or /usr/lib)
-STDIR= ${DESTDIR}/etc
-
-# location of sendmail.hf file (usually /usr/share/misc or /usr/lib)
-HFDIR= ${DESTDIR}/usr/lib
-
-# additional .o files needed
-OBJADD=
-
-################### end of user configuration flags ######################
-
-CFLAGS= -I. $O ${INCDIRS} ${DBMDEF} ${ENVDEF}
-
-OBJS= alias.o arpadate.o clock.o collect.o conf.o convtime.o daemon.o \
- deliver.o domain.o envelope.o err.o headers.o macro.o main.o \
- map.o mci.o mime.o parseaddr.o queue.o readcf.o recipient.o \
- savemail.o srvrsmtp.o stab.o stats.o sysexits.o \
- trace.o udb.o usersmtp.o util.o version.o ${OBJADD}
-
-LINKS= ${DESTDIR}/usr/ucb/newaliases ${DESTDIR}/usr/ucb/mailq
-INSTALL=install
-BINOWN= root
-BINGRP= kmem
-BINMODE=6555
-
-ALL= sendmail aliases.0 mailq.0 newaliases.0 sendmail.0
-
-all: ${ALL}
-
-sendmail: ${BEFORE} ${OBJS}
- ${CC} -o sendmail ${LDOPTS} ${OBJS} ${LIBDIRS} ${LIBS}
-
-#NROFF= nroff -h
-NROFF= groff -Tascii
-MANDOC= -mandoc
-
-aliases.0: aliases.5
- ${NROFF} ${MANDOC} aliases.5 > aliases.0
-
-mailq.0: mailq.1
- ${NROFF} ${MANDOC} mailq.1 > mailq.0
-
-newaliases.0: newaliases.1
- ${NROFF} ${MANDOC} newaliases.1 > newaliases.0
-
-sendmail.0: sendmail.8
- ${NROFF} ${MANDOC} sendmail.8 > sendmail.0
-
-install: install-sendmail install-docs
-
-install-sendmail: sendmail
- ${INSTALL} -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} sendmail ${BINDIR}
- for i in ${LINKS}; do rm -f $$i; ln -s ${BINDIR}/sendmail $$i; done
- ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 644 /dev/null \
- ${STDIR}/sendmail.st
- ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 444 sendmail.hf ${HFDIR}
-
-# doesn't actually install them -- you may want to install pre-nroff versions
-install-docs: aliases.0 mailq.0 newaliases.0 sendmail.0
-
-clean:
- rm -f ${OBJS} sendmail aliases.0 mailq.0 newaliases.0 sendmail.0
-
-# dependencies
-# gross overkill, and yet still not quite enough....
-${OBJS}: sendmail.h conf.h
-
-depend:
diff --git a/usr.sbin/sendmail/src/Makefiles/Makefile.SunOS.4.0 b/usr.sbin/sendmail/src/Makefiles/Makefile.SunOS.4.0
deleted file mode 100644
index 083ac3a..0000000
--- a/usr.sbin/sendmail/src/Makefiles/Makefile.SunOS.4.0
+++ /dev/null
@@ -1,118 +0,0 @@
-#
-# This Makefile is designed to work on the old "make" program. It does
-# not use the obj subdirectory. It also does not install documentation
-# automatically -- think of it as a quick start for sites that have the
-# old make program (I recommend that you get and port the new make if you
-# are going to be doing any significant work on sendmail).
-#
-# You may find you need to find versions of some routines
-# such as strcasecmp in order to link this on SunOS 4.0.3.
-#
-# @(#)Makefile.SunOS.4.0 8.10 (Berkeley) 9/30/95
-#
-
-# use O=-O (usual) or O=-g (debugging)
-O= -O
-
-# define the database mechanisms available for map & alias lookups:
-# -DNDBM -- use new DBM
-# -DNEWDB -- use new Berkeley DB
-# -DNIS -- include NIS support
-# The really old (V7) DBM library is no longer supported.
-# See READ_ME for a description of how these flags interact.
-#
-DBMDEF= -DNDBM -DNEWDB -DNIS
-
-# environment definitions (e.g., -D_AIX3)
-ENVDEF= -DSUNOS403
-
-# see also conf.h for additional compilation flags
-
-# include directories
-INCDIRS=-I/usr/sww/include
-
-# loader options
-LDOPTS= -Bstatic
-
-# library directories
-LIBDIRS=-L/usr/sww/lib
-
-# libraries required on your system
-LIBS= -ldb -lresolv
-
-# location of sendmail binary (usually /usr/sbin or /usr/lib)
-BINDIR= ${DESTDIR}/usr/lib
-
-# location of sendmail.st file (usually /var/log or /usr/lib)
-STDIR= ${DESTDIR}/etc
-
-# location of sendmail.hf file (usually /usr/share/misc or /usr/lib)
-HFDIR= ${DESTDIR}/usr/lib
-
-# additional .o files needed
-OBJADD=
-
-BEFORE= stdlib.h stddef.h
-
-################### end of user configuration flags ######################
-
-CFLAGS= -I. $O ${INCDIRS} ${DBMDEF} ${ENVDEF}
-
-OBJS= alias.o arpadate.o clock.o collect.o conf.o convtime.o daemon.o \
- deliver.o domain.o envelope.o err.o headers.o macro.o main.o \
- map.o mci.o mime.o parseaddr.o queue.o readcf.o recipient.o \
- savemail.o srvrsmtp.o stab.o stats.o sysexits.o \
- trace.o udb.o usersmtp.o util.o version.o ${OBJADD}
-
-LINKS= ${DESTDIR}/usr/ucb/newaliases ${DESTDIR}/usr/ucb/mailq
-INSTALL=install
-BINOWN= root
-BINGRP= kmem
-BINMODE=6555
-
-ALL= sendmail aliases.0 mailq.0 newaliases.0 sendmail.0
-
-all: ${ALL}
-
-sendmail: ${BEFORE} ${OBJS}
- ${CC} -o sendmail ${LDOPTS} ${OBJS} ${LIBDIRS} ${LIBS}
-
-stddef.h stdlib.h:
- cp /dev/null $@
-
-#NROFF= nroff -h
-NROFF= groff -Tascii
-MANDOC= -mandoc
-
-aliases.0: aliases.5
- ${NROFF} ${MANDOC} aliases.5 > aliases.0
-
-mailq.0: mailq.1
- ${NROFF} ${MANDOC} mailq.1 > mailq.0
-
-newaliases.0: newaliases.1
- ${NROFF} ${MANDOC} newaliases.1 > newaliases.0
-
-sendmail.0: sendmail.8
- ${NROFF} ${MANDOC} sendmail.8 > sendmail.0
-
-install: install-sendmail install-docs
-
-install-sendmail: sendmail
- ${INSTALL} -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} sendmail ${BINDIR}
- for i in ${LINKS}; do rm -f $$i; ln -s ${BINDIR}/sendmail $$i; done
- ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 644 /dev/null \
- ${STDIR}/sendmail.st
- ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 444 sendmail.hf ${HFDIR}
-
-# doesn't actually install them -- you may want to install pre-nroff versions
-install-docs: aliases.0 mailq.0 newaliases.0 sendmail.0
-
-clean:
- rm -f ${OBJS} sendmail aliases.0 mailq.0 newaliases.0 sendmail.0
-
-# dependencies
-# gross overkill, and yet still not quite enough....
-${OBJS}: sendmail.h conf.h
-
-depend:
diff --git a/usr.sbin/sendmail/src/Makefiles/Makefile.SunOS.5.1 b/usr.sbin/sendmail/src/Makefiles/Makefile.SunOS.5.1
deleted file mode 100644
index ccdffc5..0000000
--- a/usr.sbin/sendmail/src/Makefiles/Makefile.SunOS.5.1
+++ /dev/null
@@ -1,124 +0,0 @@
-#
-# This Makefile is designed to work on the old "make" program. It does
-# not use the obj subdirectory. It also does not install documentation
-# automatically -- think of it as a quick start for sites that have the
-# old make program (I recommend that you get and port the new make if you
-# are going to be doing any significant work on sendmail).
-#
-# This has been tested on Solaris 2.1 and 2.2. If you are compiling
-# for Solaris 2.3, use Makefile.SunOS.5.x.
-#
-# @(#)Makefile.Solaris 8.15 (Berkeley) 9/13/95
-#
-
-# use O=-O (usual) or O=-g (debugging)
-# warning: do not use -O with versions of gcc prior to 2.6
-O= -O
-
-CC= gcc
-
-# define the database mechanism used for alias lookups:
-# -DNDBM -- use new DBM
-# -DNEWDB -- use new Berkeley DB
-# -DNIS -- include NIS support
-# The really old (V7) DBM library is no longer supported.
-# See READ_ME for a description of how these flags interact.
-#
-DBMDEF= -DNDBM -DNIS
-
-# environment definitions (e.g., -D_AIX3)
-# include -DSOLARIS_2_3 for version 2.3 and higher
-ENVDEF= -DSOLARIS
-
-# see also conf.h for additional compilation flags
-
-# include directories
-INCDIRS=-I/usr/sww/include
-
-# library directories
-LIBDIRS=-L/usr/sww/lib
-
-# libraries required on your system
-# delete -l44bsd if you are not running BIND 4.9.x
-LIBS= -lresolv -l44bsd -lsocket -lnsl -lelf
-
-# location of sendmail binary (usually /usr/sbin or /usr/lib)
-BINDIR= ${DESTDIR}/usr/lib
-
-# location of sendmail.st file (usually /var/log or /usr/lib)
-STDIR= ${DESTDIR}/var/log
-
-# location of sendmail.hf file (usually /usr/share/misc or /usr/lib)
-HFDIR= ${DESTDIR}/etc/mail
-
-# additional .o files needed
-OBJADD=
-
-# things to be made before compilation begins
-BEFORE= sysexits.h
-
-################### end of user configuration flags ######################
-
-CFLAGS= -I. $O ${INCDIRS} ${DBMDEF} ${ENVDEF}
-
-OBJS= alias.o arpadate.o clock.o collect.o conf.o convtime.o daemon.o \
- deliver.o domain.o envelope.o err.o headers.o macro.o main.o \
- map.o mci.o mime.o parseaddr.o queue.o readcf.o recipient.o \
- savemail.o srvrsmtp.o stab.o stats.o sysexits.o \
- trace.o udb.o usersmtp.o util.o version.o ${OBJADD}
-
-LINKS= ${DESTDIR}/usr/ucb/newaliases ${DESTDIR}/usr/ucb/mailq
-INSTALL=/usr/ucb/install
-BINOWN= root
-BINGRP= sys
-BINMODE=6555
-
-ALL= sendmail aliases.0 mailq.0 newaliases.0 sendmail.0
-
-all: ${ALL}
-
-sendmail: ${BEFORE} ${OBJS}
- ${CC} -o sendmail ${OBJS} ${LIBDIRS} ${LIBS}
-
-sysexits.h:
- if [ -r /usr/ucbinclude/sysexits.h ]; \
- then \
- ln -s /usr/ucbinclude/sysexits.h; \
- fi
-
-#NROFF= nroff -h
-NROFF= groff -Tascii
-MANDOC= -mandoc
-
-aliases.0: aliases.5
- ${NROFF} ${MANDOC} aliases.5 > aliases.0
-
-mailq.0: mailq.1
- ${NROFF} ${MANDOC} mailq.1 > mailq.0
-
-newaliases.0: newaliases.1
- ${NROFF} ${MANDOC} newaliases.1 > newaliases.0
-
-sendmail.0: sendmail.8
- ${NROFF} ${MANDOC} sendmail.8 > sendmail.0
-
-install: install-sendmail install-docs
-
-install-sendmail: sendmail
- ${INSTALL} -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} sendmail ${BINDIR}
- for i in ${LINKS}; do rm -f $$i; ln -s ${BINDIR}/sendmail $$i; done
- ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 644 /dev/null \
- ${STDIR}/sendmail.st
- ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 444 sendmail.hf ${HFDIR}
-
-# doesn't actually install them -- you may want to install pre-nroff versions
-install-docs: aliases.0 mailq.0 newaliases.0 sendmail.0
-
-clean:
- rm -f ${OBJS} sendmail aliases.0 mailq.0 newaliases.0 sendmail.0
-
-# dependencies
-# gross overkill, and yet still not quite enough....
-${OBJS}: sendmail.h conf.h
-
-depend:
diff --git a/usr.sbin/sendmail/src/Makefiles/Makefile.SunOS.5.2 b/usr.sbin/sendmail/src/Makefiles/Makefile.SunOS.5.2
deleted file mode 100644
index ccdffc5..0000000
--- a/usr.sbin/sendmail/src/Makefiles/Makefile.SunOS.5.2
+++ /dev/null
@@ -1,124 +0,0 @@
-#
-# This Makefile is designed to work on the old "make" program. It does
-# not use the obj subdirectory. It also does not install documentation
-# automatically -- think of it as a quick start for sites that have the
-# old make program (I recommend that you get and port the new make if you
-# are going to be doing any significant work on sendmail).
-#
-# This has been tested on Solaris 2.1 and 2.2. If you are compiling
-# for Solaris 2.3, use Makefile.SunOS.5.x.
-#
-# @(#)Makefile.Solaris 8.15 (Berkeley) 9/13/95
-#
-
-# use O=-O (usual) or O=-g (debugging)
-# warning: do not use -O with versions of gcc prior to 2.6
-O= -O
-
-CC= gcc
-
-# define the database mechanism used for alias lookups:
-# -DNDBM -- use new DBM
-# -DNEWDB -- use new Berkeley DB
-# -DNIS -- include NIS support
-# The really old (V7) DBM library is no longer supported.
-# See READ_ME for a description of how these flags interact.
-#
-DBMDEF= -DNDBM -DNIS
-
-# environment definitions (e.g., -D_AIX3)
-# include -DSOLARIS_2_3 for version 2.3 and higher
-ENVDEF= -DSOLARIS
-
-# see also conf.h for additional compilation flags
-
-# include directories
-INCDIRS=-I/usr/sww/include
-
-# library directories
-LIBDIRS=-L/usr/sww/lib
-
-# libraries required on your system
-# delete -l44bsd if you are not running BIND 4.9.x
-LIBS= -lresolv -l44bsd -lsocket -lnsl -lelf
-
-# location of sendmail binary (usually /usr/sbin or /usr/lib)
-BINDIR= ${DESTDIR}/usr/lib
-
-# location of sendmail.st file (usually /var/log or /usr/lib)
-STDIR= ${DESTDIR}/var/log
-
-# location of sendmail.hf file (usually /usr/share/misc or /usr/lib)
-HFDIR= ${DESTDIR}/etc/mail
-
-# additional .o files needed
-OBJADD=
-
-# things to be made before compilation begins
-BEFORE= sysexits.h
-
-################### end of user configuration flags ######################
-
-CFLAGS= -I. $O ${INCDIRS} ${DBMDEF} ${ENVDEF}
-
-OBJS= alias.o arpadate.o clock.o collect.o conf.o convtime.o daemon.o \
- deliver.o domain.o envelope.o err.o headers.o macro.o main.o \
- map.o mci.o mime.o parseaddr.o queue.o readcf.o recipient.o \
- savemail.o srvrsmtp.o stab.o stats.o sysexits.o \
- trace.o udb.o usersmtp.o util.o version.o ${OBJADD}
-
-LINKS= ${DESTDIR}/usr/ucb/newaliases ${DESTDIR}/usr/ucb/mailq
-INSTALL=/usr/ucb/install
-BINOWN= root
-BINGRP= sys
-BINMODE=6555
-
-ALL= sendmail aliases.0 mailq.0 newaliases.0 sendmail.0
-
-all: ${ALL}
-
-sendmail: ${BEFORE} ${OBJS}
- ${CC} -o sendmail ${OBJS} ${LIBDIRS} ${LIBS}
-
-sysexits.h:
- if [ -r /usr/ucbinclude/sysexits.h ]; \
- then \
- ln -s /usr/ucbinclude/sysexits.h; \
- fi
-
-#NROFF= nroff -h
-NROFF= groff -Tascii
-MANDOC= -mandoc
-
-aliases.0: aliases.5
- ${NROFF} ${MANDOC} aliases.5 > aliases.0
-
-mailq.0: mailq.1
- ${NROFF} ${MANDOC} mailq.1 > mailq.0
-
-newaliases.0: newaliases.1
- ${NROFF} ${MANDOC} newaliases.1 > newaliases.0
-
-sendmail.0: sendmail.8
- ${NROFF} ${MANDOC} sendmail.8 > sendmail.0
-
-install: install-sendmail install-docs
-
-install-sendmail: sendmail
- ${INSTALL} -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} sendmail ${BINDIR}
- for i in ${LINKS}; do rm -f $$i; ln -s ${BINDIR}/sendmail $$i; done
- ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 644 /dev/null \
- ${STDIR}/sendmail.st
- ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 444 sendmail.hf ${HFDIR}
-
-# doesn't actually install them -- you may want to install pre-nroff versions
-install-docs: aliases.0 mailq.0 newaliases.0 sendmail.0
-
-clean:
- rm -f ${OBJS} sendmail aliases.0 mailq.0 newaliases.0 sendmail.0
-
-# dependencies
-# gross overkill, and yet still not quite enough....
-${OBJS}: sendmail.h conf.h
-
-depend:
diff --git a/usr.sbin/sendmail/src/Makefiles/Makefile.SunOS.5.3 b/usr.sbin/sendmail/src/Makefiles/Makefile.SunOS.5.3
deleted file mode 100644
index 125502c..0000000
--- a/usr.sbin/sendmail/src/Makefiles/Makefile.SunOS.5.3
+++ /dev/null
@@ -1,122 +0,0 @@
-#
-# This Makefile is designed to work on the old "make" program. It does
-# not use the obj subdirectory. It also does not install documentation
-# automatically -- think of it as a quick start for sites that have the
-# old make program (I recommend that you get and port the new make if you
-# are going to be doing any signficant work on sendmail).
-#
-# This has been tested on Solaris 2.3.
-#
-# @(#)Makefile.SunOS.5.3 8.16 (Berkeley) 10/20/95
-#
-
-# use O=-O (usual) or O=-g (debugging)
-# warning: do not use -O with versions of gcc prior to 2.6
-O= -O
-
-CC= gcc
-
-# define the database mechanism used for alias lookups:
-# -DNDBM -- use new DBM
-# -DNEWDB -- use new Berkeley DB
-# -DNIS -- include NIS support
-# The really old (V7) DBM library is no longer supported.
-# See READ_ME for a description of how these flags interact.
-#
-DBMDEF= -DNDBM -DNIS -DNISPLUS
-
-# environment definitions (e.g., -D_AIX3)
-ENVDEF= -DSOLARIS=203
-
-# see also conf.h for additional compilation flags
-
-# include directories
-INCDIRS=-I/usr/sww/include
-
-# library directories
-LIBDIRS=-L/usr/sww/lib
-
-# libraries required on your system
-# delete -l44bsd if you are not running BIND 4.9.x
-LIBS= -lresolv -l44bsd -lsocket -lnsl -lelf
-
-# location of sendmail binary (usually /usr/sbin or /usr/lib)
-BINDIR= ${DESTDIR}/usr/lib
-
-# location of sendmail.st file (usually /var/log or /usr/lib)
-STDIR= ${DESTDIR}/var/log
-
-# location of sendmail.hf file (usually /usr/share/misc or /usr/lib)
-HFDIR= ${DESTDIR}/etc/mail
-
-# additional .o files needed
-OBJADD=
-
-# things to be made before compilation begins
-BEFORE= sysexits.h
-
-################### end of user configuration flags ######################
-
-CFLAGS= -I. $O ${INCDIRS} ${DBMDEF} ${ENVDEF}
-
-OBJS= alias.o arpadate.o clock.o collect.o conf.o convtime.o daemon.o \
- deliver.o domain.o envelope.o err.o headers.o macro.o main.o \
- map.o mci.o mime.o parseaddr.o queue.o readcf.o recipient.o \
- savemail.o srvrsmtp.o stab.o stats.o sysexits.o \
- trace.o udb.o usersmtp.o util.o version.o ${OBJADD}
-
-LINKS= ${DESTDIR}/usr/bin/newaliases ${DESTDIR}/usr/bin/mailq
-INSTALL=/usr/ucb/install
-BINOWN= root
-BINGRP= sys
-BINMODE=6555
-
-ALL= sendmail aliases.0 mailq.0 newaliases.0 sendmail.0
-
-all: ${ALL}
-
-sendmail: ${BEFORE} ${OBJS}
- ${CC} -o sendmail ${OBJS} ${LIBDIRS} ${LIBS}
-
-sysexits.h:
- if [ -r /usr/ucbinclude/sysexits.h ]; \
- then \
- ln -s /usr/ucbinclude/sysexits.h; \
- fi
-
-#NROFF= nroff -h
-NROFF= groff -Tascii
-MANDOC= -mandoc
-
-aliases.0: aliases.5
- ${NROFF} ${MANDOC} aliases.5 > aliases.0
-
-mailq.0: mailq.1
- ${NROFF} ${MANDOC} mailq.1 > mailq.0
-
-newaliases.0: newaliases.1
- ${NROFF} ${MANDOC} newaliases.1 > newaliases.0
-
-sendmail.0: sendmail.8
- ${NROFF} ${MANDOC} sendmail.8 > sendmail.0
-
-install: install-sendmail install-docs
-
-install-sendmail: sendmail
- ${INSTALL} -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} sendmail ${BINDIR}
- for i in ${LINKS}; do rm -f $$i; ln -s ${BINDIR}/sendmail $$i; done
- ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 644 /dev/null \
- ${STDIR}/sendmail.st
- ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 444 sendmail.hf ${HFDIR}
-
-# doesn't actually install them -- you may want to install pre-nroff versions
-install-docs: aliases.0 mailq.0 newaliases.0 sendmail.0
-
-clean:
- rm -f ${OBJS} sendmail aliases.0 mailq.0 newaliases.0 sendmail.0
-
-# dependencies
-# gross overkill, and yet still not quite enough....
-${OBJS}: sendmail.h conf.h
-
-depend:
diff --git a/usr.sbin/sendmail/src/Makefiles/Makefile.SunOS.5.4 b/usr.sbin/sendmail/src/Makefiles/Makefile.SunOS.5.4
deleted file mode 100644
index 272505a..0000000
--- a/usr.sbin/sendmail/src/Makefiles/Makefile.SunOS.5.4
+++ /dev/null
@@ -1,122 +0,0 @@
-#
-# This Makefile is designed to work on the old "make" program. It does
-# not use the obj subdirectory. It also does not install documentation
-# automatically -- think of it as a quick start for sites that have the
-# old make program (I recommend that you get and port the new make if you
-# are going to be doing any signficant work on sendmail).
-#
-# This has been tested on Solaris 2.4.
-#
-# @(#)Makefile.SunOS.5.4 8.17 (Berkeley) 10/20/95
-#
-
-# use O=-O (usual) or O=-g (debugging)
-# warning: do not use -O with versions of gcc prior to 2.6
-O= -O
-
-CC= gcc
-
-# define the database mechanism used for alias lookups:
-# -DNDBM -- use new DBM
-# -DNEWDB -- use new Berkeley DB
-# -DNIS -- include NIS support
-# The really old (V7) DBM library is no longer supported.
-# See READ_ME for a description of how these flags interact.
-#
-DBMDEF= -DNDBM -DNIS -DNISPLUS
-
-# environment definitions (e.g., -D_AIX3)
-ENVDEF= -DSOLARIS=204
-
-# see also conf.h for additional compilation flags
-
-# include directories
-INCDIRS=-I/usr/sww/include
-
-# library directories
-LIBDIRS=-L/usr/sww/lib
-
-# libraries required on your system
-# delete -l44bsd if you are not running BIND 4.9.x
-LIBS= -lresolv -l44bsd -lsocket -lnsl -lelf
-
-# location of sendmail binary (usually /usr/sbin or /usr/lib)
-BINDIR= ${DESTDIR}/usr/lib
-
-# location of sendmail.st file (usually /var/log or /usr/lib)
-STDIR= ${DESTDIR}/var/log
-
-# location of sendmail.hf file (usually /usr/share/misc or /usr/lib)
-HFDIR= ${DESTDIR}/etc/mail
-
-# additional .o files needed
-OBJADD=
-
-# things to be made before compilation begins
-BEFORE= sysexits.h
-
-################### end of user configuration flags ######################
-
-CFLAGS= -I. $O ${INCDIRS} ${DBMDEF} ${ENVDEF}
-
-OBJS= alias.o arpadate.o clock.o collect.o conf.o convtime.o daemon.o \
- deliver.o domain.o envelope.o err.o headers.o macro.o main.o \
- map.o mci.o mime.o parseaddr.o queue.o readcf.o recipient.o \
- savemail.o srvrsmtp.o stab.o stats.o sysexits.o \
- trace.o udb.o usersmtp.o util.o version.o ${OBJADD}
-
-LINKS= ${DESTDIR}/usr/bin/newaliases ${DESTDIR}/usr/bin/mailq
-INSTALL=/usr/ucb/install
-BINOWN= root
-BINGRP= sys
-BINMODE=6555
-
-ALL= sendmail aliases.0 mailq.0 newaliases.0 sendmail.0
-
-all: ${ALL}
-
-sendmail: ${BEFORE} ${OBJS}
- ${CC} -o sendmail ${OBJS} ${LIBDIRS} ${LIBS}
-
-sysexits.h:
- if [ -r /usr/include/sysexits.h ]; \
- then \
- ln -s /usr/include/sysexits.h; \
- fi
-
-#NROFF= nroff -h
-NROFF= groff -Tascii
-MANDOC= -mandoc
-
-aliases.0: aliases.5
- ${NROFF} ${MANDOC} aliases.5 > aliases.0
-
-mailq.0: mailq.1
- ${NROFF} ${MANDOC} mailq.1 > mailq.0
-
-newaliases.0: newaliases.1
- ${NROFF} ${MANDOC} newaliases.1 > newaliases.0
-
-sendmail.0: sendmail.8
- ${NROFF} ${MANDOC} sendmail.8 > sendmail.0
-
-install: install-sendmail install-docs
-
-install-sendmail: sendmail
- ${INSTALL} -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} sendmail ${BINDIR}
- for i in ${LINKS}; do rm -f $$i; ln -s ${BINDIR}/sendmail $$i; done
- ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 644 /dev/null \
- ${STDIR}/sendmail.st
- ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 444 sendmail.hf ${HFDIR}
-
-# doesn't actually install them -- you may want to install pre-nroff versions
-install-docs: aliases.0 mailq.0 newaliases.0 sendmail.0
-
-clean:
- rm -f ${OBJS} sendmail aliases.0 mailq.0 newaliases.0 sendmail.0
-
-# dependencies
-# gross overkill, and yet still not quite enough....
-${OBJS}: sendmail.h conf.h
-
-depend:
diff --git a/usr.sbin/sendmail/src/Makefiles/Makefile.SunOS.5.5 b/usr.sbin/sendmail/src/Makefiles/Makefile.SunOS.5.5
deleted file mode 100644
index 67a47c8..0000000
--- a/usr.sbin/sendmail/src/Makefiles/Makefile.SunOS.5.5
+++ /dev/null
@@ -1,122 +0,0 @@
-#
-# This Makefile is designed to work on the old "make" program. It does
-# not use the obj subdirectory. It also does not install documentation
-# automatically -- think of it as a quick start for sites that have the
-# old make program (I recommend that you get and port the new make if you
-# are going to be doing any signficant work on sendmail).
-#
-# This has been tested on Solaris 2.5.
-#
-# @(#)Makefile.SunOS.5.5 8.5 (Berkeley) 10/20/95
-#
-
-# use O=-O (usual) or O=-g (debugging)
-# warning: do not use -O with versions of gcc prior to 2.6
-O= -O
-
-CC= gcc
-
-# define the database mechanism used for alias lookups:
-# -DNDBM -- use new DBM
-# -DNEWDB -- use new Berkeley DB
-# -DNIS -- include NIS support
-# The really old (V7) DBM library is no longer supported.
-# See READ_ME for a description of how these flags interact.
-#
-DBMDEF= -DNDBM -DNIS -DNISPLUS
-
-# environment definitions (e.g., -D_AIX3)
-ENVDEF= -DSOLARIS=205
-
-# see also conf.h for additional compilation flags
-
-# include directories
-INCDIRS=-I/usr/sww/include
-
-# library directories
-LIBDIRS=-L/usr/sww/lib
-
-# libraries required on your system
-# delete -l44bsd if you are not running BIND 4.9.x
-LIBS= -lresolv -l44bsd -lsocket -lnsl -lelf
-
-# location of sendmail binary (usually /usr/sbin or /usr/lib)
-BINDIR= ${DESTDIR}/usr/lib
-
-# location of sendmail.st file (usually /var/log or /usr/lib)
-STDIR= ${DESTDIR}/var/log
-
-# location of sendmail.hf file (usually /usr/share/misc or /usr/lib)
-HFDIR= ${DESTDIR}/etc/mail
-
-# additional .o files needed
-OBJADD=
-
-# things to be made before compilation begins
-BEFORE= sysexits.h
-
-################### end of user configuration flags ######################
-
-CFLAGS= -I. $O ${INCDIRS} ${DBMDEF} ${ENVDEF}
-
-OBJS= alias.o arpadate.o clock.o collect.o conf.o convtime.o daemon.o \
- deliver.o domain.o envelope.o err.o headers.o macro.o main.o \
- map.o mci.o mime.o parseaddr.o queue.o readcf.o recipient.o \
- savemail.o srvrsmtp.o stab.o stats.o sysexits.o \
- trace.o udb.o usersmtp.o util.o version.o ${OBJADD}
-
-LINKS= ${DESTDIR}/usr/bin/newaliases ${DESTDIR}/usr/bin/mailq
-INSTALL=/usr/ucb/install
-BINOWN= root
-BINGRP= sys
-BINMODE=6555
-
-ALL= sendmail aliases.0 mailq.0 newaliases.0 sendmail.0
-
-all: ${ALL}
-
-sendmail: ${BEFORE} ${OBJS}
- ${CC} -o sendmail ${OBJS} ${LIBDIRS} ${LIBS}
-
-sysexits.h:
- if [ -r /usr/include/sysexits.h ]; \
- then \
- ln -s /usr/include/sysexits.h; \
- fi
-
-#NROFF= nroff -h
-NROFF= groff -Tascii
-MANDOC= -mandoc
-
-aliases.0: aliases.5
- ${NROFF} ${MANDOC} aliases.5 > aliases.0
-
-mailq.0: mailq.1
- ${NROFF} ${MANDOC} mailq.1 > mailq.0
-
-newaliases.0: newaliases.1
- ${NROFF} ${MANDOC} newaliases.1 > newaliases.0
-
-sendmail.0: sendmail.8
- ${NROFF} ${MANDOC} sendmail.8 > sendmail.0
-
-install: install-sendmail install-docs
-
-install-sendmail: sendmail
- ${INSTALL} -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} sendmail ${BINDIR}
- for i in ${LINKS}; do rm -f $$i; ln -s ${BINDIR}/sendmail $$i; done
- ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 644 /dev/null \
- ${STDIR}/sendmail.st
- ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 444 sendmail.hf ${HFDIR}
-
-# doesn't actually install them -- you may want to install pre-nroff versions
-install-docs: aliases.0 mailq.0 newaliases.0 sendmail.0
-
-clean:
- rm -f ${OBJS} sendmail aliases.0 mailq.0 newaliases.0 sendmail.0
-
-# dependencies
-# gross overkill, and yet still not quite enough....
-${OBJS}: sendmail.h conf.h
-
-depend:
diff --git a/usr.sbin/sendmail/src/Makefiles/Makefile.Titan b/usr.sbin/sendmail/src/Makefiles/Makefile.Titan
deleted file mode 100644
index 2419929..0000000
--- a/usr.sbin/sendmail/src/Makefiles/Makefile.Titan
+++ /dev/null
@@ -1,119 +0,0 @@
-#
-# This Makefile is designed to work on the old "make" program. It does
-# not use the obj subdirectory. It also does not install documentation
-# automatically -- think of it as a quick start for sites that have the
-# old make program (I recommend that you get and port the new make if you
-# are going to be doing any signficant work on sendmail).
-#
-# @(#)Makefile.Titan 8.7 (Berkeley) 9/13/95
-#
-
-# put the compiler in BSD mode
-CC= cc -43
-
-# use O=-O (usual) or O=-g (debugging)
-O= -O
-
-# define the database mechanisms available for map & alias lookups:
-# -DNDBM -- use new DBM
-# -DNEWDB -- use new Berkeley DB
-# -DNIS -- include NIS support
-# The really old (V7) DBM library is no longer supported.
-# See READ_ME for a description of how these flags interact.
-#
-DBMDEF= -DNDBM
-
-# environment definitions (e.g., -D_AIX3)
-ENVDEF=
-
-# see also conf.h for additional compilation flags
-
-# include directories
-INCDIRS=
-
-# loader options
-LDOPTS=
-
-# library directories
-LIBDIRS=
-
-# libraries required on your system
-LIBS= -ldbm
-
-# location of sendmail binary (usually /usr/sbin or /usr/lib)
-BINDIR= ${DESTDIR}/usr/lib
-
-# location of sendmail.st file (usually /var/log or /usr/lib)
-STDIR= ${DESTDIR}/var/log
-
-# location of sendmail.hf file (usually /usr/share/misc or /usr/lib)
-HFDIR= ${DESTDIR}/usr/share/misc
-
-# additional .o files needed
-OBJADD=
-
-# additional pseudo-sources needed
-BEFORE= stddef.h stdlib.h
-
-################### end of user configuration flags ######################
-
-CFLAGS= -I. $O ${INCDIRS} ${DBMDEF} ${ENVDEF}
-
-OBJS= alias.o arpadate.o clock.o collect.o conf.o convtime.o daemon.o \
- deliver.o domain.o envelope.o err.o headers.o macro.o main.o \
- map.o mci.o mime.o parseaddr.o queue.o readcf.o recipient.o \
- savemail.o srvrsmtp.o stab.o stats.o sysexits.o \
- trace.o udb.o usersmtp.o util.o version.o ${OBJADD}
-
-LINKS= ${DESTDIR}/usr/ucb/newaliases ${DESTDIR}/usr/ucb/mailq
-INSTALL=install
-BINOWN= root
-BINGRP= kmem
-BINMODE=6555
-
-ALL= sendmail aliases.0 mailq.0 newaliases.0 sendmail.0
-
-all: ${ALL}
-
-sendmail: ${BEFORE} ${OBJS}
- ${CC} -o sendmail ${LDOPTS} ${OBJS} ${LIBDIRS} ${LIBS}
-
-stddef.h stdlib.h:
- cp /dev/null $@
-
-#NROFF= nroff -h
-NROFF= groff -Tascii
-MANDOC= -mandoc
-
-aliases.0: aliases.5
- ${NROFF} ${MANDOC} aliases.5 > aliases.0
-
-mailq.0: mailq.1
- ${NROFF} ${MANDOC} mailq.1 > mailq.0
-
-newaliases.0: newaliases.1
- ${NROFF} ${MANDOC} newaliases.1 > newaliases.0
-
-sendmail.0: sendmail.8
- ${NROFF} ${MANDOC} sendmail.8 > sendmail.0
-
-install: install-sendmail install-docs
-
-install-sendmail: sendmail
- ${INSTALL} -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} sendmail ${BINDIR}
- for i in ${LINKS}; do rm -f $$i; ln -s ${BINDIR}/sendmail $$i; done
- ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 644 /dev/null \
- ${STDIR}/sendmail.st
- ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 444 sendmail.hf ${HFDIR}
-
-# doesn't actually install them -- you may want to install pre-nroff versions
-install-docs: aliases.0 mailq.0 newaliases.0 sendmail.0
-
-clean:
- rm -f ${OBJS} sendmail aliases.0 mailq.0 newaliases.0 sendmail.0
-
-# dependencies
-# gross overkill, and yet still not quite enough....
-${OBJS}: sendmail.h conf.h
-
-depend:
diff --git a/usr.sbin/sendmail/src/Makefiles/Makefile.ULTRIX b/usr.sbin/sendmail/src/Makefiles/Makefile.ULTRIX
deleted file mode 100644
index 0f3cd1d..0000000
--- a/usr.sbin/sendmail/src/Makefiles/Makefile.ULTRIX
+++ /dev/null
@@ -1,117 +0,0 @@
-#
-# This Makefile is designed to work on the old "make" program. It does
-# not use the obj subdirectory. It also does not install documentation
-# automatically -- think of it as a quick start for sites that have the
-# old make program (I recommend that you get and port the new make if you
-# are going to be doing any signficant work on sendmail).
-#
-# This has been tested on Ultrix 4.2A and 4.3A.
-#
-# @(#)Makefile.ULTRIX 8.11 (Berkeley) 9/13/95
-#
-
-# use O=-O (usual) or O=-g (debugging)
-O= -O
-
-# native compiler requires -Olimit to optimize properly
-CC= cc -Olimit 900
-
-# define the database mechanisms available for map & alias lookups:
-# -DNDBM -- use new DBM
-# -DNEWDB -- use new Berkeley DB
-# -DNIS -- include NIS support
-# The really old (V7) DBM library is no longer supported.
-# See READ_ME for a description of how these flags interact.
-#
-DBMDEF= -DNDBM -DNEWDB -DNIS
-
-# environment definitions (e.g., -D_AIX3)
-# On Ultrix 4.4 and later, you can set IDENTPROTO=1.
-ENVDEF= -DIDENTPROTO=0
-
-# see also conf.h for additional compilation flags
-
-# include directories
-INCDIRS=-I/usr/sww/include
-
-# loader options
-LDOPTS=
-
-# library directories
-LIBDIRS=-L/usr/sww/lib
-
-# libraries required on your system
-# delete -lresolv and -l44bsd if you are not running BIND 4.9.x
-LIBS= -ldb -lresolv -l44bsd
-
-# location of sendmail binary (usually /usr/sbin or /usr/lib)
-BINDIR= ${DESTDIR}/usr/lib
-
-# location of sendmail.st file (usually /var/log or /usr/lib)
-STDIR= ${DESTDIR}/var/log
-
-# location of sendmail.hf file (usually /usr/share/misc or /usr/lib)
-HFDIR= ${DESTDIR}/usr/lib
-
-# additional .o files needed
-OBJADD=
-
-################### end of user configuration flags ######################
-
-CFLAGS= -I. $O ${INCDIRS} ${DBMDEF} ${ENVDEF}
-
-OBJS= alias.o arpadate.o clock.o collect.o conf.o convtime.o daemon.o \
- deliver.o domain.o envelope.o err.o headers.o macro.o main.o \
- map.o mci.o mime.o parseaddr.o queue.o readcf.o recipient.o \
- savemail.o srvrsmtp.o stab.o stats.o sysexits.o \
- trace.o udb.o usersmtp.o util.o version.o ${OBJADD}
-
-LINKS= ${DESTDIR}/usr/ucb/newaliases ${DESTDIR}/usr/ucb/mailq
-INSTALL=install
-BINOWN= root
-BINGRP= kmem
-BINMODE=6555
-
-ALL= sendmail aliases.0 mailq.0 newaliases.0 sendmail.0
-
-all: ${ALL}
-
-sendmail: ${BEFORE} ${OBJS}
- ${CC} -o sendmail ${LDOPTS} ${OBJS} ${LIBDIRS} ${LIBS}
-
-#NROFF= nroff -h
-NROFF= groff -Tascii
-MANDOC= -mandoc
-
-aliases.0: aliases.5
- ${NROFF} ${MANDOC} aliases.5 > aliases.0
-
-mailq.0: mailq.1
- ${NROFF} ${MANDOC} mailq.1 > mailq.0
-
-newaliases.0: newaliases.1
- ${NROFF} ${MANDOC} newaliases.1 > newaliases.0
-
-sendmail.0: sendmail.8
- ${NROFF} ${MANDOC} sendmail.8 > sendmail.0
-
-install: install-sendmail install-docs
-
-install-sendmail: sendmail
- ${INSTALL} -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} sendmail ${BINDIR}
- for i in ${LINKS}; do rm -f $$i; ln -s ${BINDIR}/sendmail $$i; done
- ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 644 /dev/null \
- ${STDIR}/sendmail.st
- ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 444 sendmail.hf ${HFDIR}
-
-# doesn't actually install them -- you may want to install pre-nroff versions
-install-docs: aliases.0 mailq.0 newaliases.0 sendmail.0
-
-clean:
- rm -f ${OBJS} sendmail aliases.0 mailq.0 newaliases.0 sendmail.0
-
-# dependencies
-# gross overkill, and yet still not quite enough....
-${OBJS}: sendmail.h conf.h
-
-depend:
diff --git a/usr.sbin/sendmail/src/Makefiles/Makefile.UMAX b/usr.sbin/sendmail/src/Makefiles/Makefile.UMAX
deleted file mode 100644
index f37c058..0000000
--- a/usr.sbin/sendmail/src/Makefiles/Makefile.UMAX
+++ /dev/null
@@ -1,119 +0,0 @@
-#
-# This Makefile is designed to work on the old "make" program. It does
-# not use the obj subdirectory. It also does not install documentation
-# automatically -- think of it as a quick start for sites that have the
-# old make program (I recommend that you get and port the new make if you
-# are going to be doing any signficant work on sendmail).
-#
-# This has been tested on Encore UMAX V
-#
-# @(#)Makefile.UMAX 8.7 (Berkeley) 9/13/95
-#
-
-# use O=-O (usual) or O=-g (debugging)
-O= -O
-
-# define the database mechanisms available for map & alias lookups:
-# -DNDBM -- use new DBM
-# -DNEWDB -- use new Berkeley DB
-# -DNIS -- include NIS support
-# The really old (V7) DBM library is no longer supported.
-# See READ_ME for a description of how these flags interact.
-#
-DBMDEF= -DNIS
-
-# environment definitions (e.g., -D_AIX3)
-ENVDEF= -DUMAXV
-
-# see also conf.h for additional compilation flags
-
-# include directories
-INCDIRS=
-
-# loader options
-LDOPTS=
-
-# library directories
-LIBDIRS=
-
-# libraries required on your system
-LIBS= -lyp -lrpc
-
-# location of sendmail binary (usually /usr/sbin or /usr/lib)
-BINDIR= ${DESTDIR}/usr/lib
-
-# location of sendmail.st file (usually /var/log or /usr/lib)
-STDIR= ${DESTDIR}/var/log
-
-# location of sendmail.hf file (usually /usr/share/misc or /usr/lib)
-HFDIR= ${DESTDIR}/usr/lib
-
-# additional .o files needed
-OBJADD=
-
-# things to do before compilation
-BEFORE= stddef.h
-
-stddef.h:
- echo "#define _STDDEF_H" > stddef.h
- chmod 444 stddef.h
-
-################### end of user configuration flags ######################
-
-CFLAGS= -I. $O ${INCDIRS} ${DBMDEF} ${ENVDEF}
-
-OBJS= alias.o arpadate.o clock.o collect.o conf.o convtime.o daemon.o \
- deliver.o domain.o envelope.o err.o headers.o macro.o main.o \
- map.o mci.o mime.o parseaddr.o queue.o readcf.o recipient.o \
- savemail.o srvrsmtp.o stab.o stats.o sysexits.o \
- trace.o udb.o usersmtp.o util.o version.o ${OBJADD}
-
-LINKS= ${DESTDIR}/usr/ucb/newaliases ${DESTDIR}/usr/ucb/mailq
-INSTALL=install
-BINOWN= root
-BINGRP= kmem
-BINMODE=6555
-
-ALL= sendmail aliases.0 mailq.0 newaliases.0 sendmail.0
-
-all: ${ALL}
-
-sendmail: ${BEFORE} ${OBJS}
- ${CC} -o sendmail ${LDOPTS} ${OBJS} ${LIBDIRS} ${LIBS}
-
-#NROFF= nroff -h
-NROFF= groff -Tascii
-MANDOC= -mandoc
-
-aliases.0: aliases.5
- ${NROFF} ${MANDOC} aliases.5 > aliases.0
-
-mailq.0: mailq.1
- ${NROFF} ${MANDOC} mailq.1 > mailq.0
-
-newaliases.0: newaliases.1
- ${NROFF} ${MANDOC} newaliases.1 > newaliases.0
-
-sendmail.0: sendmail.8
- ${NROFF} ${MANDOC} sendmail.8 > sendmail.0
-
-install: install-sendmail install-docs
-
-install-sendmail: sendmail
- ${INSTALL} -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} sendmail ${BINDIR}
- for i in ${LINKS}; do rm -f $$i; ln -s ${BINDIR}/sendmail $$i; done
- ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 644 /dev/null \
- ${STDIR}/sendmail.st
- ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 444 sendmail.hf ${HFDIR}
-
-# doesn't actually install them -- you may want to install pre-nroff versions
-install-docs: aliases.0 mailq.0 newaliases.0 sendmail.0
-
-clean:
- rm -f ${OBJS} sendmail aliases.0 mailq.0 newaliases.0 sendmail.0
-
-# dependencies
-# gross overkill, and yet still not quite enough....
-${OBJS}: sendmail.h conf.h
-
-depend:
diff --git a/usr.sbin/sendmail/src/Makefiles/Makefile.UNICOS b/usr.sbin/sendmail/src/Makefiles/Makefile.UNICOS
deleted file mode 100644
index f68e285..0000000
--- a/usr.sbin/sendmail/src/Makefiles/Makefile.UNICOS
+++ /dev/null
@@ -1,117 +0,0 @@
-#
-# This Makefile is designed to work on the old "make" program. It does
-# not use the obj subdirectory. It also does not install documentation
-# automatically -- think of it as a quick start for sites that have the
-# old make program (I recommend that you get and port the new make if you
-# are going to be doing any signficant work on sendmail).
-#
-# Should work with UNICOS 8.0. Note that you must also acquire
-# gdbm, as UNICOS does not have ndbm, and I had no luck at all
-# getting the Berkeley DB package to compile.
-# Douglas K. Rand, University of North Dakota
-# rand@aero.und.nodak.edu
-#
-# @(#)Makefile.UNICOS 8.4 (Berkeley) 9/13/95
-#
-
-# make sure the shell constructs below use the right shell
-SHELL= /bin/sh
-
-# use O=-O (usual) or O=-g (debugging)
-O= -O
-
-
-# define the database mechanism used for alias lookups:
-# -DNDBM -- use new DBM
-# -DNEWDB -- use new Berkeley DB
-# -DNIS -- include NIS support
-# The really old (V7) DBM library is no longer supported.
-# See READ_ME for a description of how these flags interact.
-#
-DBMDEF= -DNDBM
-
-# environment definitions (e.g., -D_AIX3)
-ENVDEF= -DUNICOS
-
-# see also conf.h for additional compilation flags
-
-# include directories
-INCDIRS=-I/usr/local/include
-
-# library directories
-LIBDIRS=-L/usr/local/lib
-
-# libraries required on your system
-LIBS=-lgdbm
-
-# location of sendmail binary (usually /usr/sbin or /usr/lib)
-BINDIR= ${DESTDIR}/usr/lib
-
-# location of sendmail.st file (usually /var/log or /usr/lib)
-STDIR= ${DESTDIR}/etc/mail
-
-# location of sendmail.hf file (usually /usr/share/misc or /usr/lib)
-HFDIR= ${DESTDIR}/etc/mail
-
-# additional .o files needed
-OBJADD=
-
-################### end of user configuration flags ######################
-
-CFLAGS= -I. $O ${INCDIRS} ${DBMDEF} ${ENVDEF}
-
-OBJS= alias.o arpadate.o clock.o collect.o conf.o convtime.o daemon.o \
- deliver.o domain.o envelope.o err.o headers.o macro.o main.o \
- map.o mci.o mime.o parseaddr.o queue.o readcf.o recipient.o \
- savemail.o srvrsmtp.o stab.o stats.o sysexits.o \
- trace.o udb.o usersmtp.o util.o version.o ${OBJADD}
-
-LINKS= ${DESTDIR}/usr/ucb/newaliases ${DESTDIR}/usr/ucb/mailq
-INSTALL=install
-BINOWN= root
-BINGRP= kmem
-BINMODE=6555
-
-ALL= sendmail aliases.0 mailq.0 newaliases.0 sendmail.0
-
-all: ${ALL}
-
-sendmail: ${BEFORE} ${OBJS}
- ${CC} -o sendmail ${LDOPTS} ${OBJS} ${LIBDIRS} ${LIBS}
-
-#NROFF= nroff -h
-NROFF= groff -Tascii
-MANDOC= -mandoc
-
-aliases.0: aliases.5
- ${NROFF} ${MANDOC} aliases.5 > aliases.0
-
-mailq.0: mailq.1
- ${NROFF} ${MANDOC} mailq.1 > mailq.0
-
-newaliases.0: newaliases.1
- ${NROFF} ${MANDOC} newaliases.1 > newaliases.0
-
-sendmail.0: sendmail.8
- ${NROFF} ${MANDOC} sendmail.8 > sendmail.0
-
-install: install-sendmail install-docs
-
-install-sendmail: sendmail
- ${INSTALL} -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} sendmail ${BINDIR}
- for i in ${LINKS}; do rm -f $$i; ln -s ${BINDIR}/sendmail $$i; done
- ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 644 /dev/null \
- ${STDIR}/sendmail.st
- ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 444 sendmail.hf ${HFDIR}
-
-# doesn't actually install them -- you may want to install pre-nroff versions
-install-docs: aliases.0 mailq.0 newaliases.0 sendmail.0
-
-clean:
- rm -f ${OBJS} sendmail aliases.0 mailq.0 newaliases.0 sendmail.0
-
-# dependencies
-# gross overkill, and yet still not quite enough....
-${OBJS}: sendmail.h conf.h
-
-depend:
diff --git a/usr.sbin/sendmail/src/Makefiles/Makefile.UNIX_SV.4.x.i386 b/usr.sbin/sendmail/src/Makefiles/Makefile.UNIX_SV.4.x.i386
deleted file mode 100644
index 598bbc0..0000000
--- a/usr.sbin/sendmail/src/Makefiles/Makefile.UNIX_SV.4.x.i386
+++ /dev/null
@@ -1,118 +0,0 @@
-#
-# This Makefile is designed to work on the old "make" program. It does
-# not use the obj subdirectory. It also does not install documentation
-# automatically -- think of it as a quick start for sites that have the
-# old make program (I recommend that you get and port the new make if you
-# are going to be doing any signficant work on sendmail).
-#
-# UnixWare 1.1 Makefile from John Warburton <jwarb@SACBH.com.au>.
-#
-# For UnixWare 2.x, use -DUNIXWARE2 in ENVDEF in place of -DUNIXWARE.
-#
-# @(#)Makefile.UNIX_SV.4.x.i386 8.6 (Berkeley) 9/26/95
-#
-
-# make sure the shell constructs below use the right shell
-SHELL= /bin/sh
-
-# use O=-O (usual) or O=-g (debugging)
-O= -O
-
-CC= gcc
-#DESTDIR=/usr/local/sendmail
-
-# define the database mechanism used for alias lookups:
-# -DNDBM -- use new DBM
-# -DNEWDB -- use new Berkeley DB
-# -DNIS -- include NIS support
-# The really old (V7) DBM library is no longer supported.
-# See READ_ME for a description of how these flags interact.
-#
-DBMDEF= -DNEWDB -DNDBM
-
-# environment definitions (e.g., -D_AIX3)
-ENVDEF= -D__svr4__ -DUNIXWARE
-#ENVDEF= -D__svr4__ -DUNIXWARE2
-
-# see also conf.h for additional compilation flags
-
-# include directories
-INCDIRS=
-
-# library directories
-LIBDIRS=
-
-# libraries required on your system
-LIBS= -lc -ldbm -lresolv -lsocket -lnsl -lgen -lelf
-
-# location of sendmail binary (usually /usr/sbin or /usr/lib)
-BINDIR= ${DESTDIR}/usr/ucblib
-
-# location of sendmail.st file (usually /var/log or /usr/lib)
-STDIR= ${DESTDIR}/usr/ucblib
-
-# location of sendmail.hf file (usually /usr/share/misc or /usr/lib)
-HFDIR= ${DESTDIR}/usr/ucblib
-
-# additional .o files needed
-OBJADD=
-
-################### end of user configuration flags ######################
-
-CFLAGS= -I. $O ${INCDIRS} ${DBMDEF} ${ENVDEF}
-
-OBJS= alias.o arpadate.o clock.o collect.o conf.o convtime.o daemon.o \
- deliver.o domain.o envelope.o err.o headers.o macro.o main.o \
- map.o mci.o mime.o parseaddr.o queue.o readcf.o recipient.o \
- savemail.o srvrsmtp.o stab.o stats.o sysexits.o \
- trace.o udb.o usersmtp.o util.o version.o ${OBJADD}
-
-LINKS= ${DESTDIR}/usr/ucb/newaliases ${DESTDIR}/usr/ucb/mailq
-INSTALL=/usr/ucb/install
-BINOWN= root
-BINGRP= mail
-BINMODE=6555
-
-ALL= sendmail aliases.0 mailq.0 newaliases.0 sendmail.0
-
-all: ${ALL}
-
-sendmail: ${BEFORE} ${OBJS}
- ${CC} -o sendmail ${OBJS} ${LIBDIRS} ${LIBS}
-
-#NROFF= nroff -h
-NROFF= groff -Tascii
-MANDOC= -mandoc
-
-aliases.0: aliases.5
- ${NROFF} ${MANDOC} aliases.5 > aliases.0
-
-mailq.0: mailq.1
- ${NROFF} ${MANDOC} mailq.1 > mailq.0
-
-newaliases.0: newaliases.1
- ${NROFF} ${MANDOC} newaliases.1 > newaliases.0
-
-sendmail.0: sendmail.8
- ${NROFF} ${MANDOC} sendmail.8 > sendmail.0
-
-install: install-sendmail install-docs
-
-install-sendmail: sendmail
- ${INSTALL} -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} sendmail ${BINDIR}
- for i in ${LINKS}; do rm -f $$i; ln -s ${BINDIR}/sendmail $$i; done
- ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 644 /dev/null \
- ${STDIR}/sendmail.st
- ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 444 sendmail.hf ${HFDIR}
-
-# doesn't actually install them -- you may want to install pre-nroff versions
-install-docs: aliases.0 mailq.0 newaliases.0 sendmail.0
-
-clean:
- rm -f ${OBJS} sendmail aliases.0 mailq.0 newaliases.0 sendmail.0
-
-# dependencies
-# gross overkill, and yet still not quite enough....
-${OBJS}: sendmail.h conf.h
-
-depend:
diff --git a/usr.sbin/sendmail/src/Makefiles/Makefile.UX4800 b/usr.sbin/sendmail/src/Makefiles/Makefile.UX4800
deleted file mode 100644
index 5ea2957..0000000
--- a/usr.sbin/sendmail/src/Makefiles/Makefile.UX4800
+++ /dev/null
@@ -1,129 +0,0 @@
-#
-# This Makefile is designed to work on the old "make" program. It does
-# not use the obj subdirectory. It also does not install documentation
-# automatically -- think of it as a quick start for sites that have the
-# old make program (I recommend that you get and port the new make if you
-# are going to be doing any signficant work on sendmail).
-#
-# This has been tested on NEC UX4800.
-# Contributed by Kazuhisa Shimizu <shimizu@lang.csd.nes.nec.co.jp>.
-#
-# @(#)Makefile.UX4800 8.3 (Berkeley) 9/13/95
-#
-
-# use O=-O (usual) or O=-g (debugging)
-O= -O
-
-# make sure that /usr/abiccs/bin/cc is used (do not use /usr/ucb/cc).
-#CC= /bin/cc -KOlimit=900
-CC= /usr/abiccs/bin/cc -KOlimit=900
-
-# define the database mechanism used for alias lookups:
-# -DNDBM -- use new DBM
-# -DNEWDB -- use new Berkeley DB
-# -DNIS -- include NIS support
-# The really old (V7) DBM library is no longer supported.
-# See READ_ME for a description of how these flags interact.
-#
-DBMDEF= -DNDBM -DNIS # without NEWDB
-#DBMDEF= -DNEWDB -DNDBM -DNIS # with NEWDB
-
-# environment definitions (e.g., -D_AIX3)
-ENVDEF= -DHASSNPRINTF=1
-
-# see also conf.h for additional compilation flags
-
-# include directories
-INCDIRS=-I/usr/local/include
-
-# library directories
-LIBDIRS=-L/usr/local/lib
-
-# libraries required on your system
-# delete -l44bsd if you are not running BIND 4.9.x
-LIBS= -lsocket -lnsl -lelf -lresolv # -l44bsd # without NEWDB
-#LIBS= -lsocket -lnsl -lelf -ldb -lresolv # -l44bsd # with NEWDB
-
-# location of sendmail binary (usually /usr/sbin or /usr/lib)
-BINDIR= ${DESTDIR}/usr/ucblib
-
-# location of sendmail.st file (usually /var/log or /usr/lib)
-STDIR= ${DESTDIR}/var/ucblib
-
-# location of sendmail.hf file (usually /usr/share/misc or /usr/lib)
-HFDIR= ${DESTDIR}/var/ucblib
-
-# additional .o files needed
-OBJADD=
-
-# things to be made before compilation begins
-BEFORE= sysexits.h ndbm.h
-
-################### end of user configuration flags ######################
-
-CFLAGS= -I. $O ${INCDIRS} ${DBMDEF} ${ENVDEF}
-
-OBJS= alias.o arpadate.o clock.o collect.o conf.o convtime.o daemon.o \
- deliver.o domain.o envelope.o err.o headers.o macro.o main.o \
- map.o mci.o mime.o parseaddr.o queue.o readcf.o recipient.o \
- savemail.o srvrsmtp.o stab.o stats.o sysexits.o \
- trace.o udb.o usersmtp.o util.o version.o ${OBJADD}
-
-LINKS= ${DESTDIR}/usr/ucb/newaliases ${DESTDIR}/usr/ucb/mailq
-INSTALL=/usr/ucb/install
-BINOWN= root
-BINGRP= sys
-BINMODE=6555
-
-ALL= sendmail aliases.0 mailq.0 newaliases.0 sendmail.0
-
-all: ${ALL}
-
-sendmail: ${BEFORE} ${OBJS}
- ${CC} -o sendmail ${OBJS} ${LIBDIRS} ${LIBS}
-
-sysexits.h:
- echo '#ifndef _LOCAL_SYSEXITS_H_' > sysexits.h;
- echo '#define _LOCAL_SYSEXITS_H_' >> sysexits.h;
- cat /usr/abiccs/ucbinclude/sysexits.h >> sysexits.h;
- echo '#endif /* _LOCAL_SYSEXITS_H_ */' >> sysexits.h;
-
-ndbm.h:
- sed 's/void/char/' /usr/abiccs/include/ndbm.h > ndbm.h
-
-#NROFF= nroff -h
-NROFF= groff -Tascii
-MANDOC= -mandoc
-
-aliases.0: aliases.5
- ${NROFF} ${MANDOC} aliases.5 > aliases.0
-
-mailq.0: mailq.1
- ${NROFF} ${MANDOC} mailq.1 > mailq.0
-
-newaliases.0: newaliases.1
- ${NROFF} ${MANDOC} newaliases.1 > newaliases.0
-
-sendmail.0: sendmail.8
- ${NROFF} ${MANDOC} sendmail.8 > sendmail.0
-
-install: install-sendmail install-docs
-
-install-sendmail: sendmail
- ${INSTALL} -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} sendmail ${BINDIR}
- for i in ${LINKS}; do rm -f $$i; ln -s ${BINDIR}/sendmail $$i; done
- ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 644 /dev/null \
- ${STDIR}/sendmail.st
- ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 444 sendmail.hf ${HFDIR}
-
-# doesn't actually install them -- you may want to install pre-nroff versions
-install-docs: aliases.0 mailq.0 newaliases.0 sendmail.0
-
-clean:
- rm -f ${OBJS} sendmail aliases.0 mailq.0 newaliases.0 sendmail.0
-
-# dependencies
-# gross overkill, and yet still not quite enough....
-${OBJS}: sendmail.h conf.h
-
-depend:
diff --git a/usr.sbin/sendmail/src/Makefiles/Makefile.UXPDS b/usr.sbin/sendmail/src/Makefiles/Makefile.UXPDS
deleted file mode 100644
index d4bfba4..0000000
--- a/usr.sbin/sendmail/src/Makefiles/Makefile.UXPDS
+++ /dev/null
@@ -1,130 +0,0 @@
-#
-# This Makefile is designed to work on the old "make" program. It does
-# not use the obj subdirectory. It also does not install documentation
-# automatically -- think of it as a quick start for sites that have the
-# old make program (I recommend that you get and port the new make if you
-# are going to be doing any signficant work on sendmail).
-#
-# Contributed by Diego R. Lopez <drlopez@cica.es>. Based on
-# Makefile.SVR4. I haven't tested this myself. It may
-# work on other SVR4 ports.
-#
-# @(#)Makefile.UXPDS 8.2 (Berkeley) 11/13/95
-#
-
-# make sure the shell constructs below use the right shell
-SHELL= /bin/sh
-
-# use O=-O (usual) or O=-g (debugging)
-O= -O
-
-#DESTDIR=/usr/local/sendmail
-
-# define the database mechanism used for alias lookups:
-# -DNDBM -- use new DBM
-# -DNEWDB -- use new Berkeley DB
-# -DNIS -- include NIS support
-# The really old (V7) DBM library is no longer supported.
-# See READ_ME for a description of how these flags interact.
-#
-DBMDEF= -DNDBM -DNIS
-
-# environment definitions (e.g., -D_AIX3)
-ENVDEF= -DUXPDS
-
-# see also conf.h for additional compilation flags
-
-# include directories
-INCDIRS=-I/usr/include -I/usr/ucbinclude
-
-# library directories
-LIBDIRS=-L/usr/ucblib
-
-# libraries required on your system
-LIBS= -ldbm -lresolv -lsocket -lnsl -lelf -lucb
-
-# location of sendmail binary (usually /usr/sbin or /usr/lib)
-BINDIR= ${DESTDIR}/usr/ucblib
-
-# location of sendmail.st file (usually /var/log or /usr/lib)
-STDIR= ${DESTDIR}/usr/ucblib
-
-# location of sendmail.hf file (usually /usr/share/misc or /usr/lib)
-HFDIR= ${DESTDIR}/usr/ucblib
-
-# location and mode for man pages
-MAN1= /usr/share/man/bsd_man/cat1
-MAN4= /usr/share/man/bsd_man/cat4
-MANMODE=444
-
-# additional .o files needed
-OBJADD=
-
-# things to be made before compilation begins
-BEFORE=
-
-################### end of user configuration flags ######################
-
-CFLAGS= -I. $O ${INCDIRS} ${DBMDEF} ${ENVDEF}
-
-OBJS= alias.o arpadate.o clock.o collect.o conf.o convtime.o daemon.o \
- deliver.o domain.o envelope.o err.o headers.o macro.o main.o \
- map.o mci.o mime.o parseaddr.o queue.o readcf.o recipient.o \
- savemail.o srvrsmtp.o stab.o stats.o sysexits.o \
- trace.o udb.o usersmtp.o util.o version.o ${OBJADD}
-
-LINKS= ${DESTDIR}/usr/ucb/newaliases ${DESTDIR}/usr/ucb/mailq
-BINOWN= root
-BINGRP= mail
-BINMODE=6555
-INSTALL=/usr/ucb/install
-
-ALL= sendmail man-pages
-
-all: ${ALL}
-
-man-pages: aliases.0 mailq.0 newaliases.0 sendmail.0
-
-sendmail: ${BEFORE} ${OBJS}
- ${CC} -o sendmail ${OBJS} ${LIBDIRS} ${LIBS}
-
-NROFF= nroff -h
-
-aliases.0: aliases.5
- ${NROFF} -mandoc aliases.5 > aliases.0
-
-mailq.0: mailq.1
- ${NROFF} -mandoc mailq.1 > mailq.0
-
-newaliases.0: newaliases.1
- ${NROFF} -mandoc newaliases.1 > newaliases.0
-
-sendmail.0: sendmail.8
- ${NROFF} -mandoc sendmail.8 > sendmail.0
-
-install: install-sendmail install-docs
-
-install-sendmail: sendmail
- ${INSTALL} -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} sendmail ${BINDIR}
- for i in ${LINKS}; do rm -f $$i; ln -s ${BINDIR}/sendmail $$i; done
- ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 644 /dev/null \
- ${STDIR}/sendmail.st
- ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 444 sendmail.hf ${HFDIR}
-
-# doesn't actually install them -- you may want to install pre-nroff versions
-install-docs: aliases.0 mailq.0 newaliases.0 sendmail.0
- cp aliases.0 ${MAN4}/aliases.4
- chmod ${MANMODE} ${MAN4}/aliases.4
- cp mailq.0 ${MAN1}/mailq.1m
- chmod ${MANMODE} ${MAN1}/mailq.1m
- cp newaliases.0 ${MAN1}/newaliases.1m
- chmod ${MANMODE} ${MAN1}/newaliases.1m
- cp sendmail.0 ${MAN1}/sendmail.1m
- chmod ${MANMODE} ${MAN1}/sendmail.1m
-
-clean:
- rm -f ${OBJS} sendmail aliases.0 mailq.0 newaliases.0 sendmail.0
-
-# dependencies
-# gross overkill, and yet still not quite enough....
-${OBJS}: sendmail.h conf.h
diff --git a/usr.sbin/sendmail/src/Makefiles/Makefile.Utah b/usr.sbin/sendmail/src/Makefiles/Makefile.Utah
deleted file mode 100644
index c5232b7..0000000
--- a/usr.sbin/sendmail/src/Makefiles/Makefile.Utah
+++ /dev/null
@@ -1,41 +0,0 @@
-# @(#)Makefile.Utah 8.3 (Berkeley) 9/13/95
-
-PROG= sendmail
-
-# define the database format to use for aliases et al. Can be -DNEWDB (for
-# the new BSD database package -- this is preferred) or -DNDBM for the NDBM
-# database package. The old putrescent V7 DBM package is no longer
-# supported.
-# You can define both NEWDB and NDBM during a transition period; old
-# databases are read, but the new format will be used on any rebuilds. On
-# really gnarly systems, you can set this to null; it will crawl like a high
-# spiral snail, but it will work.
-DBMDEF= -DNEWDB -DNDBM -DOLD_NEWDB
-
-CFLAGS+=-I${.CURDIR} ${DBMDEF} -Dsetpgid=setpgrp
-
-SRCS= alias.c arpadate.c clock.c collect.c conf.c convtime.c daemon.c \
- deliver.c domain.c envelope.c err.c headers.c macro.c main.c map.c \
- mci.c mci.c parseaddr.c queue.c readcf.c recipient.c savemail.c \
- srvrsmtp.c stab.c stats.c sysexits.c trace.c udb.c usersmtp.c \
- util.c version.c
-DPADD= ${LIBDBM} ${LIBCOMPAT}
-LDADD=
-MAN1= mailq.0 newaliases.0
-MAN5= aliases.0
-MAN8= sendmail.0
-LINKS= ${DESTDIR}/usr/sbin/sendmail ${DESTDIR}/usr/bin/newaliases \
- ${DESTDIR}/usr/sbin/sendmail ${DESTDIR}/usr/bin/mailq
-INSTALL=install
-BINDIR= /usr/sbin
-BINOWN= root
-BINGRP= kmem
-BINMODE=6555
-
-beforeinstall:
- ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 644 /dev/null \
- ${DESTDIR}/var/log/sendmail.st
- ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 444 ${.CURDIR}/sendmail.hf \
- ${DESTDIR}/usr/share/misc
-
-.include <bsd.prog.mk>
diff --git a/usr.sbin/sendmail/src/Makefiles/Makefile.dgux b/usr.sbin/sendmail/src/Makefiles/Makefile.dgux
deleted file mode 100644
index a84aadf..0000000
--- a/usr.sbin/sendmail/src/Makefiles/Makefile.dgux
+++ /dev/null
@@ -1,108 +0,0 @@
-#
-# Tested on DG/UX 5.4.2 by A. Bryan Curnutt <bryan@Stoner.COM>.
-# Updated for DG/UX 5.4.3 by Mark T. Robinson <mtr@ornl.gov>.
-#
-# @(#)Makefile.dgux 8.9 (Berkeley) 9/13/95
-#
-
-# use O=-O (usual) or O=-g (debugging)
-O= -O
-
-# define the database mechanisms available for map & alias lookups:
-# -DNDBM -- use new DBM
-# -DNEWDB -- use new Berkeley DB
-# -DNIS -- include NIS support
-# The really old (V7) DBM library is no longer supported.
-# See READ_ME for a description of how these flags interact.
-#
-DBMDEF= -DNDBM -DNIS
-
-# environment definitions (e.g., -D_AIX3)
-# use DGUX_5_4_2 for versions prior to 5.4.3.
-ENVDEF=-DDGUX
-
-# see also conf.h for additional compilation flags
-
-# include directories
-INCDIRS=-I/usr/sww/include
-
-# loader options
-LDOPTS=
-
-# library directories
-LIBDIRS=-L/usr/sww/lib
-
-# libraries required on your system
-LIBS= -ldbm
-
-# location of sendmail binary (usually /usr/sbin or /usr/lib)
-BINDIR= ${DESTDIR}/usr/bin
-
-# location of sendmail.st file (usually /var/log or /usr/lib)
-STDIR= ${DESTDIR}/usr/lib
-
-# location of sendmail.hf file (usually /usr/share/misc or /usr/lib)
-HFDIR= ${DESTDIR}/etc
-
-# additional .o files needed
-OBJADD=
-
-################### end of user configuration flags ######################
-
-CFLAGS= -I. $O ${INCDIRS} ${DBMDEF} ${ENVDEF}
-
-OBJS= alias.o arpadate.o clock.o collect.o conf.o convtime.o daemon.o \
- deliver.o domain.o envelope.o err.o headers.o macro.o main.o \
- map.o mci.o mime.o parseaddr.o queue.o readcf.o recipient.o \
- savemail.o srvrsmtp.o stab.o stats.o sysexits.o \
- trace.o udb.o usersmtp.o util.o version.o ${OBJADD}
-
-LINKS= ${DESTDIR}/usr/ucb/newaliases ${DESTDIR}/usr/ucb/mailq
-INSTALL=install
-BINOWN= root
-BINGRP= bin
-BINMODE=6555
-
-ALL= sendmail aliases.0 mailq.0 newaliases.0 sendmail.0
-
-all: ${ALL}
-
-sendmail: ${BEFORE} ${OBJS}
- ${CC} -o sendmail ${LDOPTS} ${OBJS} ${LIBDIRS} ${LIBS}
-
-#NROFF= nroff -h
-NROFF= groff -Tascii
-MANDOC= -mandoc
-
-aliases.0: aliases.5
- ${NROFF} ${MANDOC} aliases.5 > aliases.0
-
-mailq.0: mailq.1
- ${NROFF} ${MANDOC} mailq.1 > mailq.0
-
-newaliases.0: newaliases.1
- ${NROFF} ${MANDOC} newaliases.1 > newaliases.0
-
-sendmail.0: sendmail.8
- ${NROFF} ${MANDOC} sendmail.8 > sendmail.0
-
-install: install-sendmail install-docs
-
-install-sendmail: sendmail
- ${INSTALL} -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} sendmail ${BINDIR}
- for i in ${LINKS}; do rm -f $$i; ln -s ${BINDIR}/sendmail $$i; done
- ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 644 /dev/null \
- ${STDIR}/sendmail.st
- ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m 444 sendmail.hf ${HFDIR}
-
-# doesn't actually install them -- you may want to install pre-nroff versions
-install-docs: aliases.0 mailq.0 newaliases.0 sendmail.0
-
-clean:
- rm -f ${OBJS} sendmail aliases.0 mailq.0 newaliases.0 sendmail.0
-
-# dependencies
-# gross overkill, and yet still not quite enough....
-${OBJS}: sendmail.h conf.h
-
-depend:
diff --git a/usr.sbin/sendmail/src/Makefiles/Makefile.uts.systemV b/usr.sbin/sendmail/src/Makefiles/Makefile.uts.systemV
deleted file mode 100644
index df843fb..0000000
--- a/usr.sbin/sendmail/src/Makefiles/Makefile.uts.systemV
+++ /dev/null
@@ -1,189 +0,0 @@
-# This Makefile is designed to work on the old "make" program. It does
-# not use the obj subdirectory. It also does not install documentation
-# automatically -- think of it as a quick start for sites that have the
-# old make program (I recommend that you get and port the new make if you
-# are going to be doing any signficant work on sendmail).
-#
-# Makefile for an Amdahl 5890 running UTS System V 2.1.5 (SVr3)
-# By Janet Jackson <janet@dialix.oz.au> 1994-11-24
-# This has been tested on (uname -a output) uts bsuts systemV 2.1.5 5890
-#
-# @(#)Makefile.uts.systemV 8.4 (Berkeley) 6/20/95
-#
-
-# Sendmail 8 on UTS requires BIND 4.9's include files and lib44bsd and
-# libresolv libraries. The BIND version on UTS is much too old.
-#
-BINDPATH=../../../bind
-
-# use O=-O (usual) or O=-g (debugging)
-O= -g
-
-# define the database mechanisms available for map & alias lookups:
-# -DNDBM -- use new DBM
-# -DNEWDB -- use new Berkeley DB
-# -DNIS -- include NIS support
-# The really old (V7) DBM library is no longer supported.
-# See READ_ME for a description of how these flags interact.
-#
-# Getting NIS working on UTS is possible (I did it!) but awkward.
-# And forget it unless you're behind some sort of a firewall.
-#
-DBMDEF= -DNIS -DNDBM
-
-# environment definitions (e.g., -D_AIX3)
-ENVDEF= -D_UTS
-
-# see also conf.h for additional compilation flags
-
-# include directories
-# To find new BIND header files. This path assumes we are using "makesendmail".
-INCDIRS=-I${BINDPATH}/include -I${BINDPATH}/compat/include
-
-# loader options
-LDOPTS=
-
-# library directories
-# To find new libresolv.a. This path assumes we are using "makesendmail".
-LIBDIRS=-L${BINDPATH}/res -L${BINDPATH}/compat/lib
-
-# libraries required on your system
-LIBS= -lyp -lrpc -lresolv -l44bsd -lbsd -lsocket -la
-
-# location of sendmail binary (usually /usr/sbin or /usr/lib)
-BINDIR= ${DESTDIR}/usr/lib
-
-# location of sendmail.st file (usually /var/log or /usr/lib)
-STDIR= ${DESTDIR}/usr/lib
-
-# location of sendmail.hf file (usually /usr/share/misc or /usr/lib)
-HFDIR= ${DESTDIR}/usr/lib
-
-# additional .o files needed
-OBJADD=
-
-# things to do before compilation
-BEFORE= stddef.h
-
-################### end of user configuration flags ######################
-
-CFLAGS= -eft -I. $O ${INCDIRS} ${DBMDEF} ${ENVDEF}
-
-OBJS= alias.o arpadate.o clock.o collect.o conf.o convtime.o daemon.o \
- deliver.o domain.o envelope.o err.o headers.o macro.o main.o \
- map.o mci.o mime.o parseaddr.o queue.o readcf.o recipient.o \
- savemail.o srvrsmtp.o stab.o stats.o sysexits.o \
- trace.o udb.o usersmtp.o util.o version.o ${OBJADD}
-
-LINKS= ${DESTDIR}/usr/lib/newaliases ${DESTDIR}/usr/lib/mailq
-BINOWN= root
-BINGRP= mail
-BINMODE=6555
-
-#ALL= sendmail aliases.0 mailq.0 newaliases.0 sendmail.0
-ALL= sendmail
-
-all: ${ALL}
-
-sendmail: ${BEFORE} ${OBJS}
- ${CC} -o sendmail ${LDOPTS} ${OBJS} ${LIBDIRS} ${LIBS}
-
-stddef.h:
- echo "#include <sys/types.h>" > stddef.h
-
-#NROFF= nroff -h
-NROFF= groff -Tascii
-MANDOC= -mandoc
-
-aliases.0: aliases.5
- ${NROFF} ${MANDOC} aliases.5 > aliases.0
-
-mailq.0: mailq.1
- ${NROFF} ${MANDOC} mailq.1 > mailq.0
-
-newaliases.0: newaliases.1
- ${NROFF} ${MANDOC} newaliases.1 > newaliases.0
-
-sendmail.0: sendmail.8
- ${NROFF} ${MANDOC} sendmail.8 > sendmail.0
-
-# new target to save original sendmail files before installation
-save-orig:
- if [ -f ${BINDIR}/sendmail.orig ]; then \
- echo "Error: original already saved" 1>&2; \
- exit 1; \
- else \
- /bin/mv ${BINDIR}/sendmail ${BINDIR}/sendmail.orig; \
- for i in ${LINKS}; do \
- if [ -h $$i ]; then \
- /bin/mv $$i $$i.orig; \
- fi; \
- done; \
- if [ -f ${STDIR}/sendmail.st ]; then \
- /bin/mv ${STDIR}/sendmail.st ${STDIR}/sendmail.st.orig; \
- fi; \
- if [ -f ${HFDIR}/sendmail.hf ]; then \
- /bin/mv ${HFDIR}/sendmail.hf ${HFDIR}/sendmail.hf.orig; \
- fi; \
- echo "Now run 'make install'." 1>&2; \
- echo "(To back out, run 'make backout'.)" 1>&2; \
- fi
-
-# new target to back out, ie, put back original files
-backout:
- if [ ! -f ${BINDIR}/sendmail.orig ]; then \
- echo "Error: original was not saved" 1>&2; \
- exit 1; \
- else \
- /bin/mv ${BINDIR}/sendmail.orig ${BINDIR}/sendmail; \
- for i in ${LINKS}; do \
- if [ -h $$i.orig ]; then \
- /bin/mv $$i.orig $$i; \
- else \
- rm -f $$i; \
- fi; \
- done; \
- if [ -f ${STDIR}/sendmail.st.orig ]; then \
- /bin/mv ${STDIR}/sendmail.st.orig ${STDIR}/sendmail.st; \
- else \
- rm -f ${STDIR}/sendmail.st; \
- fi; \
- if [ -f ${HFDIR}/sendmail.hf.orig ]; then \
- /bin/mv ${HFDIR}/sendmail.hf.orig ${HFDIR}/sendmail.hf; \
- else \
- rm -f ${HFDIR}/sendmail.hf; \
- fi; \
- echo "Now back out config file change if necessary." 1>&2; \
- fi
-
-#install: install-sendmail install-docs
-install: install-sendmail
-
-install-sendmail: sendmail
- if [ ! -f ${BINDIR}/sendmail.orig ]; then \
- echo "Error: Original not saved yet \
- (no ${BINDIR}/sendmail.orig)" 1>&2; \
- exit 1; \
- else \
- cpset -o sendmail ${BINDIR} ${BINMODE} ${BINOWN} ${BINGRP}; \
- for i in ${LINKS}; do \
- rm -f $$i; \
- ln ${BINDIR}/sendmail $$i; \
- done; \
- cpset -o /dev/null ${STDIR}/sendmail.st 644 ${BINOWN} ${BINGRP}; \
- cpset -o sendmail.hf ${HFDIR} 444 ${BINOWN} ${BINGRP}; \
- echo "Now install the new config file: \
- go to ../../cf/cf and run ./Install-dcd-config" 1>&2; \
- fi
-
-# doesn't actually install them -- you may want to install pre-nroff versions
-install-docs: aliases.0 mailq.0 newaliases.0 sendmail.0
-
-clean:
- rm -f ${OBJS} sendmail aliases.0 mailq.0 newaliases.0 sendmail.0
-
-# dependencies
-# gross overkill, and yet still not quite enough....
-${OBJS}: sendmail.h conf.h
-
-depend:
diff --git a/usr.sbin/sendmail/src/aliases.5 b/usr.sbin/sendmail/src/aliases.5
index 3519467..2c7e52d 100644
--- a/usr.sbin/sendmail/src/aliases.5
+++ b/usr.sbin/sendmail/src/aliases.5
@@ -50,7 +50,7 @@ The file resides in
and
is formatted as a series of lines of the form
.Bd -filled -offset indent
-name: name_1, name2, name_3, . . .
+name: name_1, name_2, name_3, . . .
.Ed
.Pp
The
@@ -82,8 +82,8 @@ command should be executed each time the aliases file is changed for the
change to take effect.
.Sh SEE ALSO
.Xr newaliases 1 ,
-.Xr dbopen 3 ,
.Xr dbm 3 ,
+.Xr dbopen 3 ,
.Xr sendmail 8
.Rs
.%T "SENDMAIL Installation and Operation Guide"
diff --git a/usr.sbin/sendmail/src/collect.c b/usr.sbin/sendmail/src/collect.c
index 58f0dc7..de4ee06 100644
--- a/usr.sbin/sendmail/src/collect.c
+++ b/usr.sbin/sendmail/src/collect.c
@@ -33,7 +33,7 @@
*/
#ifndef lint
-static char sccsid[] = "@(#)collect.c 8.72 (Berkeley) 10/6/97";
+static char sccsid[] = "@(#)collect.c 8.71 (Berkeley) 6/30/97";
#endif /* not lint */
# include <errno.h>
@@ -567,7 +567,7 @@ readerr:
/* check for message too large */
if (MaxMessageSize > 0 && e->e_msgsize > MaxMessageSize)
{
- e->e_flags |= EF_NO_BODY_RETN|EF_CLRQUEUE;
+ e->e_flags |= EF_NO_BODY_RETN;
e->e_status = "5.2.3";
usrerr("552 Message exceeds maximum fixed size (%ld)",
MaxMessageSize);
diff --git a/usr.sbin/sendmail/src/conf.c b/usr.sbin/sendmail/src/conf.c
index cd51efc..60502b3 100644
--- a/usr.sbin/sendmail/src/conf.c
+++ b/usr.sbin/sendmail/src/conf.c
@@ -33,7 +33,7 @@
*/
#ifndef lint
-static char sccsid[] = "@(#)conf.c 8.379 (Berkeley) 10/20/97";
+static char sccsid[] = "@(#)conf.c 8.374 (Berkeley) 8/2/97";
#endif /* not lint */
# include "sendmail.h"
@@ -1972,9 +1972,6 @@ getla()
*/
/* Non Apollo stuff removed by Don Lewis 11/15/93 */
-#ifndef lint
-static char rcsid[] = "@(#)$Id: getloadavg.c,v 1.16 1991/06/21 12:51:15 paul Exp $";
-#endif /* !lint */
#ifdef apollo
# undef volatile
@@ -2066,6 +2063,7 @@ refuseconnections(port)
static time_t lastconn = (time_t) 0;
static int conncnt = 0;
extern bool enoughdiskspace();
+ extern void setproctitle __P((const char *, ...));
#ifdef XLA
if (!xla_smtp_ok())
@@ -2224,7 +2222,7 @@ initsetproctitle(argc, argv, envp)
char **argv;
char **envp;
{
- register int i, envpsize = 0;
+ register int i;
extern char **environ;
/*
@@ -2233,7 +2231,7 @@ initsetproctitle(argc, argv, envp)
*/
for (i = 0; envp[i] != NULL; i++)
- envpsize += strlen(envp[i]) + 1;
+ continue;
environ = (char **) xalloc(sizeof (char *) * (i + 1));
for (i = 0; envp[i] != NULL; i++)
environ[i] = newstr(envp[i]);
@@ -2244,16 +2242,6 @@ initsetproctitle(argc, argv, envp)
*/
Argv = argv;
-
- /*
- ** Find the last environment variable within sendmail's
- ** process memory area.
- */
- while (i > 0 && (envp[i - 1] < argv[0] ||
- envp[i - 1] > (argv[argc - 1] +
- strlen(argv[argc - 1]) + 1 + envpsize)))
- i--;
-
if (i > 0)
LastArgv = envp[i - 1] + strlen(envp[i - 1]);
else
@@ -2829,7 +2817,6 @@ char *optarg = NULL; /* argument associated with option */
#define tell(s) if (opterr) {fputs(*nargv,stderr);fputs(s,stderr); \
fputc(optopt,stderr);fputc('\n',stderr);return(BADCH);}
-int
getopt(nargc,nargv,ostr)
int nargc;
char *const *nargv;
@@ -2837,7 +2824,7 @@ getopt(nargc,nargv,ostr)
{
static char *place = EMSG; /* option letter processing */
static char atend = 0;
- register char *oli = NULL; /* option letter list index */
+ register char *oli; /* option letter list index */
if (atend) {
atend = 0;
@@ -2939,7 +2926,6 @@ vsprintf(s, fmt, ap)
* causing nast effects.
**************************************************************/
-/*static char _id[] = "$Id: snprintf.c,v 1.2 1995/10/09 11:19:47 roberto Exp $";*/
static void sm_dopr();
static char *DoprEnd;
static int SnprfOverflow;
@@ -3854,7 +3840,7 @@ chownsafe(fd, safedir)
bool safedir;
{
#if (!defined(_POSIX_CHOWN_RESTRICTED) || _POSIX_CHOWN_RESTRICTED != -1) && \
- (defined(_PC_CHOWN_RESTRICTED) || defined(_GNU_TYPES_H))
+ defined(_PC_CHOWN_RESTRICTED)
int rval;
/* give the system administrator a chance to override */
diff --git a/usr.sbin/sendmail/src/conf.h b/usr.sbin/sendmail/src/conf.h
index de37c14..2da4506 100644
--- a/usr.sbin/sendmail/src/conf.h
+++ b/usr.sbin/sendmail/src/conf.h
@@ -31,7 +31,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * @(#)conf.h 8.335 (Berkeley) 10/24/97
+ * @(#)conf.h 8.328 (Berkeley) 8/3/97
*/
/*
@@ -294,9 +294,7 @@ typedef int pid_t;
define BSD_REMAP_SIGNAL_TO_SIGVEC
RTisms needed above */
/* make this sendmail in a completely different place */
-# ifndef _PATH_VENDOR_CF
-# define _PATH_VENDOR_CF "/usr/local/newmail/sendmail.cf"
-# endif
+# define _PATH_VENDORCF "/usr/local/newmail/sendmail.cf"
# ifndef _PATH_SENDMAILPID
# define _PATH_SENDMAILPID "/usr/local/newmail/sendmail.pid"
# endif
@@ -350,7 +348,6 @@ typedef int pid_t;
# ifdef IRIX6
# define LA_TYPE LA_IRIX6 /* figure out at run time */
# define SAFENFSPATHCONF 0 /* pathconf(2) lies on NFS filesystems */
-# define SYSLOG_BUFSIZE 512
# else
# define LA_TYPE LA_INT
@@ -385,6 +382,7 @@ typedef int pid_t;
# include <sys/time.h>
# define HASINITGROUPS 1 /* has initgroups(3) call */
# define HASUNAME 1 /* use System V uname(2) system call */
+# define HASGETUSERSHELL 1 /* DOES have getusershell(3) call in libc */
# define HASFCHMOD 1 /* has fchmod(2) syscall */
# define IP_SRCROUTE 1 /* can check IP source routing */
# define SAFENFSPATHCONF 1 /* pathconf(2) pessimizes on NFS filesystems */
@@ -440,7 +438,6 @@ typedef int pid_t;
# else
/* SunOS 4.0.3 or 4.1.x */
-# define HASGETUSERSHELL 1 /* DOES have getusershell(3) call in libc */
# define HASSETREUID 1 /* has setreuid(2) call */
# ifndef HASFLOCK
# define HASFLOCK 1 /* has flock(2) call */
@@ -758,12 +755,16 @@ typedef int pid_t;
# endif
# if defined(__FreeBSD__)
# undef SPT_TYPE
-# if __FreeBSD__ == 2
+# if __FreeBSD__ >= 2
# include <osreldate.h> /* and this works */
# if __FreeBSD_version >= 199512 /* 2.2-current right now */
# include <libutil.h>
# define SPT_TYPE SPT_BUILTIN
# endif
+# if __FreeBSD_version >= 300000 /* 3.0-current right now */
+# include <login_cap.h>
+# define HASSETUSERCONTEXT 1 /* BSDI-style login classes */
+# endif
# endif
# ifndef SPT_TYPE
# define SPT_TYPE SPT_REUSEARGV
@@ -906,7 +907,6 @@ extern int errno;
#if _SCO_DS >= 1
# include <paths.h>
# define _SCO_unix_4_2
-# define SIOCGIFNUM_IS_BROKEN 1 /* SIOCGIFNUM returns bogus value */
# define HASSNPRINTF 1 /* has snprintf(3) call */
# define HASFCHMOD 1 /* has fchmod(2) call */
# define HASSETRLIMIT 1 /* has setrlimit(2) call */
@@ -1192,7 +1192,6 @@ extern void *malloc();
# define LA_TYPE LA_PROCSTR
# endif
# define SFS_TYPE SFS_VFS /* use <sys/vfs.h> statfs() impl */
-# define SPT_PADCHAR '\0' /* pad process title with nulls */
# ifndef _PATH_SENDMAILPID
# define _PATH_SENDMAILPID "/var/run/sendmail.pid"
# endif
@@ -1229,7 +1228,6 @@ extern void *malloc();
# define BSD /* has BSD routines */
# define HASSETRLIMIT 0 /* ... but not setrlimit(2) */
# define BROKEN_RES_SEARCH 1 /* res_search(unknown) returns h_errno=0 */
-# define BOGUS_O_EXCL 1 /* exclusive open follows symlinks */
# define HASUNAME 1 /* use System V uname(2) system call */
# define HASFCHMOD 1 /* has fchmod(2) syscall */
# define HASINITGROUPS 1 /* has initgroups(3) call */
@@ -1842,15 +1840,6 @@ typedef struct msgb mblk_t;
# undef offsetof /* avoid stddefs.h and sys/sysmacros.h conflict */
#endif
-/*
-** Siemens Nixdorf Informationssysteme AG SINIX
-**
-** Contributed by Gerald Rinske <Gerald.Rinske@mch.sni.de>
-** of Siemens Business Services VAS.
-*/
-#ifdef _sinix_
-# define SYSLOG_BUFSIZE 1024
-#endif
/**********************************************************************
** End of Per-Operating System defines
diff --git a/usr.sbin/sendmail/src/daemon.c b/usr.sbin/sendmail/src/daemon.c
index e62aaf1..bc8fd6f 100644
--- a/usr.sbin/sendmail/src/daemon.c
+++ b/usr.sbin/sendmail/src/daemon.c
@@ -37,9 +37,9 @@
#ifndef lint
#ifdef DAEMON
-static char sccsid[] = "@(#)daemon.c 8.195 (Berkeley) 10/23/97 (with daemon mode)";
+static char sccsid[] = "@(#)daemon.c 8.186 (Berkeley) 8/2/97 (with daemon mode)";
#else
-static char sccsid[] = "@(#)daemon.c 8.195 (Berkeley) 10/23/97 (without daemon mode)";
+static char sccsid[] = "@(#)daemon.c 8.186 (Berkeley) 8/2/97 (without daemon mode)";
#endif
#endif /* not lint */
@@ -103,7 +103,9 @@ static char sccsid[] = "@(#)daemon.c 8.195 (Berkeley) 10/23/97 (without daemon m
** e -- the current envelope.
**
** Returns:
-** none.
+** TRUE -- if a "null server" should be used -- that is, one
+** that rejects all commands.
+** FALSE -- to use a normal server.
**
** Side Effects:
** Waits until some interesting activity occurs. When
@@ -120,7 +122,7 @@ int ListenQueueSize = 10; /* size of listen queue */
int TcpRcvBufferSize = 0; /* size of TCP receive buffer */
int TcpSndBufferSize = 0; /* size of TCP send buffer */
-void
+bool
getrequests(e)
ENVELOPE *e;
{
@@ -186,7 +188,7 @@ getrequests(e)
*/
if (tTd(15, 1))
- printf("getrequests: port 0x%x\n", port);
+ printf("getrequests: port 0x%x\n", DaemonAddr.sin.sin_port);
/* get a socket for the SMTP connection */
socksize = opendaemonsocket(TRUE);
@@ -236,7 +238,7 @@ getrequests(e)
/* see if we are rejecting connections */
(void) blocksignal(SIGALRM);
- if (refuseconnections(ntohs(port)))
+ if (refuseconnections(ntohs(DaemonAddr.sin.sin_port)))
{
if (DaemonSocket >= 0)
{
@@ -282,7 +284,7 @@ getrequests(e)
/* wait for a connection */
setproctitle("accepting connections on port %d",
- ntohs(port));
+ ntohs(DaemonAddr.sin.sin_port));
#if 0
/*
** Andrew Sun <asun@ieps-sun.ml.com> claims that this will
@@ -370,6 +372,7 @@ getrequests(e)
char *p;
extern SIGFUNC_DECL intsig __P((int));
FILE *inchannel, *outchannel;
+ bool nullconn;
/*
** CHILD -- return to caller.
@@ -434,6 +437,13 @@ getrequests(e)
/* open maps for check_relay ruleset */
initmaps(FALSE, e);
+ /* validate the connection */
+ HoldErrs = TRUE;
+ nullconn = !validate_connection(&RealHostAddr, RealHostName, e);
+ HoldErrs = FALSE;
+ if (nullconn)
+ break;
+
#ifdef XLA
if (!xla_host_ok(RealHostName))
{
@@ -442,7 +452,9 @@ getrequests(e)
}
#endif
- break;
+ if (tTd(15, 2))
+ printf("getreq: returning (normal server)\n");
+ return FALSE;
}
/* parent -- keep track of children */
@@ -461,8 +473,8 @@ getrequests(e)
(void) close(pipefd[1]);
}
if (tTd(15, 2))
- printf("getreq: returning\n");
- return;
+ printf("getreq: returning (null server)\n");
+ return TRUE;
}
/*
** OPENDAEMONSOCKET -- open the SMTP socket
@@ -1253,7 +1265,7 @@ getauthinfo(fd)
int nleft;
struct hostent *hp;
char **ha;
- volatile bool may_be_forged;
+ bool may_be_forged;
char ibuf[MAXNAME + 1];
static char hbuf[MAXNAME * 2 + 2];
@@ -1738,7 +1750,6 @@ host_map_lookup(map, name, av, statp)
}
/* found a match -- copy out */
- hp->h_name = denlstring((char *) hp->h_name, TRUE, TRUE);
s->s_namecanon.nc_stat = *statp = EX_OK;
s->s_namecanon.nc_cname = newstr(hp->h_name);
if (bitset(MF_MATCHONLY, map->map_mflags))
@@ -2016,9 +2027,7 @@ hostnamebyanyaddr(sap)
#endif /* NAMED_BIND */
if (hp != NULL && hp->h_name[0] != '[')
- return denlstring((char *) hp->h_name, TRUE, TRUE);
- else if (sap->sa.sa_family == AF_UNIX && sap->sunix.sun_path[0] == '\0')
- return "localhost";
+ return (char *) hp->h_name;
else
{
/* produce a dotted quad */
diff --git a/usr.sbin/sendmail/src/deliver.c b/usr.sbin/sendmail/src/deliver.c
index 9575515..8805ab9 100644
--- a/usr.sbin/sendmail/src/deliver.c
+++ b/usr.sbin/sendmail/src/deliver.c
@@ -33,7 +33,7 @@
*/
#ifndef lint
-static char sccsid[] = "@(#)deliver.c 8.296 (Berkeley) 10/22/97";
+static char sccsid[] = "@(#)deliver.c 8.285 (Berkeley) 8/2/97";
#endif /* not lint */
#include "sendmail.h"
@@ -44,10 +44,6 @@ static char sccsid[] = "@(#)deliver.c 8.296 (Berkeley) 10/22/97";
extern int h_errno;
#endif
-#if HASSETUSERCONTEXT
-# include <login_cap.h>
-#endif
-
#if SMTP
extern char SmtpError[];
#endif
@@ -81,7 +77,7 @@ sendall(e, mode)
register ENVELOPE *ee;
ENVELOPE *splitenv = NULL;
int oldverbose = Verbose;
- bool somedeliveries = FALSE, expensive = FALSE;
+ bool somedeliveries = FALSE;
pid_t pid;
extern void sendenvelope();
@@ -285,7 +281,6 @@ sendall(e, mode)
if (tTd(13, 30))
printf(" ... expensive\n");
q->q_flags |= QQUEUEUP;
- expensive = TRUE;
}
else
{
@@ -398,13 +393,11 @@ sendall(e, mode)
/* treat this as a delivery in terms of counting tries */
e->e_dtime = curtime();
- if (!expensive)
- e->e_ntries++;
+ e->e_ntries++;
for (ee = splitenv; ee != NULL; ee = ee->e_sibling)
{
ee->e_dtime = curtime();
- if (!expensive)
- ee->e_ntries++;
+ ee->e_ntries++;
}
}
@@ -473,13 +466,34 @@ sendall(e, mode)
/*
** Since fcntl locking has the interesting semantic that
** the lock is owned by a process, not by an open file
- ** descriptor, we have to unlock this envelope, and
+ ** descriptor, we have to flush this to the queue, and
** then restart from scratch in the child.
*/
- unlockqueue(e);
+ {
+ /* save id for future use */
+ char *qid = e->e_id;
+
+ /* now drop the envelope in the parent */
+ e->e_flags |= EF_INQUEUE;
+ dropenvelope(e, FALSE);
+
+ /* arrange to reacquire lock after fork */
+ e->e_id = qid;
+ }
+
for (ee = splitenv; ee != NULL; ee = ee->e_sibling)
- unlockqueue(ee);
+ {
+ /* save id for future use */
+ char *qid = ee->e_id;
+
+ /* drop envelope in parent */
+ ee->e_flags |= EF_INQUEUE;
+ dropenvelope(ee, FALSE);
+
+ /* and save qid for reacquisition */
+ ee->e_id = qid;
+ }
# endif /* !HASFLOCK */
@@ -1354,7 +1368,7 @@ tryhost:
hostbuf, m->m_name);
else
message("Connecting to %s port %d via %s...",
- hostbuf, ntohs(port), m->m_name);
+ hostbuf, port, m->m_name);
i = makeconnection(hostbuf, port, mci, e);
mci->mci_lastuse = curtime();
mci->mci_exitstat = i;
@@ -1887,7 +1901,7 @@ tryhost:
for (to = tochain; to != NULL; to = to->q_tchain)
{
e->e_to = to->q_paddr;
- if (strlen(to->q_paddr) + (t - tobuf) + 2 > sizeof tobuf)
+ if (strlen(to->q_paddr) + (t - tobuf) + 2 >= sizeof tobuf)
{
/* not enough room */
continue;
@@ -2230,8 +2244,6 @@ endmailer(mci, e, pv)
{
int st;
- mci_unlock_host(mci);
-
/* close any connections */
if (mci->mci_in != NULL)
(void) xfclose(mci->mci_in, mci->mci_mailer->m_name, "mci_in");
@@ -2319,9 +2331,6 @@ giveresponse(stat, m, mci, ctladdr, xstart, e)
extern int N_SysEx;
char buf[MAXLINE];
- if (e == NULL)
- syserr("giveresponse: null envelope");
-
/*
** Compute status message from code.
*/
@@ -2419,8 +2428,7 @@ giveresponse(stat, m, mci, ctladdr, xstart, e)
** that.
*/
- if (OpMode != MD_VERIFY && !bitset(EF_VRFYONLY, e->e_flags) &&
- LogLevel > ((stat == EX_TEMPFAIL) ? 8 : (stat == EX_OK) ? 7 : 6))
+ if (LogLevel > ((stat == EX_TEMPFAIL) ? 8 : (stat == EX_OK) ? 7 : 6))
logdelivery(m, mci, &statmsg[4], ctladdr, xstart, e);
if (tTd(11, 2))
@@ -2733,7 +2741,7 @@ putfromline(mci, e)
}
}
expand(template, buf, sizeof buf, e);
- putxline(buf, strlen(buf), mci, PXLF_HEADER);
+ putxline(buf, strlen(buf), mci, PXLF_NOTHINGSPECIAL);
}
/*
** PUTBODY -- put the body of a message.
@@ -3088,21 +3096,17 @@ endofmessage:
** none.
*/
-static jmp_buf CtxMailfileTimeout;
-static void mailfiletimeout();
-
int
mailfile(filename, ctladdr, sfflags, e)
- char *volatile filename;
+ char *filename;
ADDRESS *ctladdr;
- volatile int sfflags;
+ int sfflags;
register ENVELOPE *e;
{
register FILE *f;
register pid_t pid = -1;
- volatile int mode = ST_MODE_NOFILE;
+ int mode = ST_MODE_NOFILE;
bool suidwarn = geteuid() == 0;
- EVENT *ev;
if (tTd(11, 1))
{
@@ -3136,7 +3140,7 @@ mailfile(filename, ctladdr, sfflags, e)
/* child -- actually write to file */
struct stat stb;
MCI mcibuf;
- volatile int oflags = O_WRONLY|O_APPEND;
+ int oflags = O_WRONLY|O_APPEND;
if (e->e_lockfp != NULL)
(void) close(fileno(e->e_lockfp));
@@ -3148,16 +3152,6 @@ mailfile(filename, ctladdr, sfflags, e)
e->e_to = filename;
ExitStat = EX_OK;
- if (setjmp(CtxMailfileTimeout) != 0)
- {
- exit(EX_TEMPFAIL);
- }
-
- if (TimeOuts.to_fileopen > 0)
- ev = setevent(TimeOuts.to_fileopen, mailfiletimeout, 0);
- else
- ev = NULL;
-
#ifdef HASLSTAT
if (lstat(filename, &stb) < 0)
#else
@@ -3293,9 +3287,6 @@ mailfile(filename, ctladdr, sfflags, e)
exit(EX_CANTCREAT);
}
- if (ev != NULL)
- clrevent(ev);
-
bzero(&mcibuf, sizeof mcibuf);
mcibuf.mci_mailer = FileMailer;
mcibuf.mci_out = f;
@@ -3341,12 +3332,6 @@ mailfile(filename, ctladdr, sfflags, e)
}
return EX_UNAVAILABLE; /* avoid compiler warning on IRIX */
}
-
-static void
-mailfiletimeout()
-{
- longjmp(CtxMailfileTimeout, 1);
-}
/*
** HOSTSIGNATURE -- return the "signature" for a host.
**
diff --git a/usr.sbin/sendmail/src/domain.c b/usr.sbin/sendmail/src/domain.c
index 2031726..a9bc023 100644
--- a/usr.sbin/sendmail/src/domain.c
+++ b/usr.sbin/sendmail/src/domain.c
@@ -486,7 +486,7 @@ bestmx_map_lookup(map, name, av, statp)
** This algorithm tries to be smart about wildcard MX records.
** This is hard to do because DNS doesn't tell is if we matched
** against a wildcard or a specific MX.
-**
+**
** We always prefer A & CNAME records, since these are presumed
** to be specific.
**
@@ -519,7 +519,7 @@ dns_getcanonname(host, hbsize, trymx, statp)
{
register u_char *eom, *ap;
register char *cp;
- register int n;
+ register int n;
HEADER *hp;
querybuf answer;
int ancount, qdcount;
diff --git a/usr.sbin/sendmail/src/headers.c b/usr.sbin/sendmail/src/headers.c
index 857e9c3..ac67f2c 100644
--- a/usr.sbin/sendmail/src/headers.c
+++ b/usr.sbin/sendmail/src/headers.c
@@ -33,7 +33,7 @@
*/
#ifndef lint
-static char sccsid[] = "@(#)headers.c 8.115 (Berkeley) 10/22/97";
+static char sccsid[] = "@(#)headers.c 8.111 (Berkeley) 7/9/97";
#endif /* not lint */
# include <errno.h>
@@ -119,7 +119,7 @@ chompheader(line, def, hdrp, e)
{
/* have some */
register char *q = strchr(p + 1, *p);
-
+
if (q != NULL)
{
*q++ = '\0';
@@ -522,16 +522,9 @@ eatheader(e, full)
(void) sendtolist(h->h_value, NULLADDR,
&e->e_sendqueue, 0, e);
-#if 0
- /*
- ** Change functionality so a fatal error on an
- ** address doesn't affect the entire envelope.
- */
-
/* delete fatal errors generated by this address */
- if (!bitset(EF_FATALERRS, saveflags))
+ if (!GrabTo && !bitset(EF_FATALERRS, saveflags))
e->e_flags &= ~EF_FATALERRS;
-#endif
}
/* save the message-id for logging */
@@ -1337,7 +1330,7 @@ put_vanilla_header(h, v, mci)
int putflags;
char obuf[MAXLINE];
- putflags = PXLF_HEADER;
+ putflags = 0;
#if _FFR_7BITHDRS
if (bitnset(M_7BITHDRS, mci->mci_mailer->m_flags))
putflags |= PXLF_STRIP8BIT;
@@ -1349,8 +1342,8 @@ put_vanilla_header(h, v, mci)
int l;
l = nlp - v;
- if (SPACELEFT(obuf, obp) - 1 < l)
- l = SPACELEFT(obuf, obp) - 1;
+ if (sizeof obuf - (obp - obuf) < l)
+ l = sizeof obuf - (obp - obuf);
snprintf(obp, SPACELEFT(obuf, obp), "%.*s", l, v);
putxline(obuf, strlen(obuf), mci, putflags);
@@ -1392,7 +1385,7 @@ commaize(h, p, oldstyle, mci, e)
int opos;
int omax;
bool firstone = TRUE;
- int putflags = PXLF_HEADER;
+ int putflags = 0;
char obuf[MAXLINE + 3];
/*
@@ -1498,7 +1491,6 @@ commaize(h, p, oldstyle, mci, e)
*p = savechar;
continue;
}
- name = denlstring(name, FALSE, TRUE);
/* output the name with nice formatting */
opos += strlen(name);
@@ -1560,6 +1552,6 @@ copyheader(header)
header = header->h_link;
}
*tail = NULL;
-
+
return ret;
}
diff --git a/usr.sbin/sendmail/src/main.c b/usr.sbin/sendmail/src/main.c
index c496adb..61c54e7 100644
--- a/usr.sbin/sendmail/src/main.c
+++ b/usr.sbin/sendmail/src/main.c
@@ -39,7 +39,7 @@ static char copyright[] =
#endif /* not lint */
#ifndef lint
-static char sccsid[] = "@(#)main.c 8.258 (Berkeley) 10/20/97";
+static char sccsid[] = "@(#)main.c 8.249 (Berkeley) 7/25/97";
#endif /* not lint */
#define _DEFINE
@@ -50,6 +50,10 @@ static char sccsid[] = "@(#)main.c 8.258 (Berkeley) 10/20/97";
#include <resolv.h>
#endif
+# ifdef lint
+char edata, end;
+# endif /* lint */
+
/*
** SENDMAIL -- Post mail to a set of destinations.
**
@@ -118,6 +122,7 @@ main(argc, argv, envp)
char **av;
extern char Version[];
char *ep, *from;
+ typedef int (*fnptr)();
STAB *st;
register int i;
int j;
@@ -128,6 +133,7 @@ main(argc, argv, envp)
bool run_in_foreground = FALSE; /* -bD mode */
static bool reenter = FALSE;
struct passwd *pw;
+ struct stat stb;
struct hostent *hp;
bool nullserver = FALSE;
char jbuf[MAXHOSTNAMELEN]; /* holds MyHostName */
@@ -204,7 +210,7 @@ main(argc, argv, envp)
#if LOG
# ifdef LOG_MAIL
openlog("sendmail", LOG_PID, LOG_MAIL);
-# else
+# else
openlog("sendmail", LOG_PID);
# endif
#endif
@@ -430,8 +436,6 @@ main(argc, argv, envp)
res_init();
if (tTd(8, 8))
_res.options |= RES_DEBUG;
- else
- _res.options &= ~RES_DEBUG;
# ifdef RES_NOALIASES
_res.options |= RES_NOALIASES;
# endif
@@ -627,7 +631,7 @@ main(argc, argv, envp)
ExitStat = EX_USAGE;
}
break;
-
+
case 'n': /* don't alias */
NoAlias = TRUE;
break;
@@ -828,15 +832,6 @@ main(argc, argv, envp)
ConfigFileRead = TRUE;
vendor_post_defaults(CurEnv);
- /* Enforce use of local time (null string overrides this) */
- if (TimeZoneSpec == NULL)
- unsetenv("TZ");
- else if (TimeZoneSpec[0] != '\0')
- setuserenv("TZ", TimeZoneSpec);
- else
- setuserenv("TZ", NULL);
- tzset();
-
/* avoid denial-of-service attacks */
resetlimits();
@@ -908,6 +903,15 @@ main(argc, argv, envp)
if (DefaultNotify == 0)
DefaultNotify = QPINGONFAILURE|QPINGONDELAY;
+ /* Enforce use of local time (null string overrides this) */
+ if (TimeZoneSpec == NULL)
+ unsetenv("TZ");
+ else if (TimeZoneSpec[0] != '\0')
+ setuserenv("TZ", TimeZoneSpec);
+ else
+ setuserenv("TZ", NULL);
+ tzset();
+
/* be sure we don't pick up bogus HOSTALIASES environment variable */
if (queuemode && RealUid != 0)
(void) unsetenv("HOSTALIASES");
@@ -1317,7 +1321,7 @@ main(argc, argv, envp)
if (OpMode == MD_DAEMON || QueueIntvl != 0)
{
char dtype[200];
- extern void getrequests __P((ENVELOPE *));
+ extern bool getrequests __P((ENVELOPE *));
if (!run_in_foreground && !tTd(99, 100))
{
@@ -1367,7 +1371,7 @@ main(argc, argv, envp)
dropenvelope(CurEnv, TRUE);
#if DAEMON
- getrequests(CurEnv);
+ nullserver = getrequests(CurEnv);
/* drop privileges */
(void) drop_privileges(FALSE);
@@ -1381,11 +1385,6 @@ main(argc, argv, envp)
p = getauthinfo(fileno(InChannel));
define('_', p, &BlankEnvelope);
-
- /* validate the connection */
- HoldErrs = TRUE;
- nullserver = !validate_connection(&RealHostAddr, RealHostName, CurEnv);
- HoldErrs = FALSE;
#endif /* DAEMON */
}
@@ -1477,9 +1476,10 @@ main(argc, argv, envp)
CurEnv->e_flags &= ~EF_FATALERRS;
collect(InChannel, FALSE, NULL, CurEnv);
- /* bail out if message too large */
- if (bitset(EF_CLRQUEUE, CurEnv->e_flags))
+ /* bail out if there were fatal errors in collect */
+ if (OpMode != MD_VERIFY && bitset(EF_FATALERRS, CurEnv->e_flags))
{
+ CurEnv->e_flags |= EF_CLRQUEUE;
finis();
/*NOTREACHED*/
return -1;
@@ -2047,7 +2047,7 @@ sighup(sig)
RunAsUid, RunAsGid);
exit(EX_OSERR);
}
- execve(SaveArgv[0], (ARGV_T) SaveArgv, (ARGV_T) ExternalEnviron);
+ execv(SaveArgv[0], (ARGV_T) SaveArgv);
if (LogLevel > 0)
sm_syslog(LOG_ALERT, NOQID, "could not exec %s: %m", SaveArgv[0]);
exit(EX_OSFILE);
@@ -2089,14 +2089,14 @@ drop_privileges(to_real_uid)
#ifdef NGROUPS_MAX
/* reset group permissions; these can be set later */
- emptygidset[0] = (to_real_uid || RunAsGid != 0) ? RunAsGid : getegid();
+ emptygidset[0] = RunAsGid == 0 ? getegid() : RunAsGid;
(void) setgroups(1, emptygidset);
#endif
/* reset primary group and user id */
- if ((to_real_uid || RunAsGid != 0) && setgid(RunAsGid) < 0)
+ if (RunAsGid != 0 && setgid(RunAsGid) < 0)
rval = EX_OSERR;
- if ((to_real_uid || RunAsUid != 0) && setuid(RunAsUid) < 0)
+ if (RunAsUid != 0 && setuid(RunAsUid) < 0)
rval = EX_OSERR;
return rval;
}
diff --git a/usr.sbin/sendmail/src/makesendmail b/usr.sbin/sendmail/src/makesendmail
deleted file mode 100644
index a28e2f4..0000000
--- a/usr.sbin/sendmail/src/makesendmail
+++ /dev/null
@@ -1,331 +0,0 @@
-#!/bin/sh
-
-# Copyright (c) 1993, 1996-1997 Eric P. Allman
-# 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.
-#
-# @(#)makesendmail 8.45 (Berkeley) 4/12/97
-#
-
-#
-# A quick-and-dirty script to compile sendmail in the presence of
-# multiple architectures and Makefiles.
-#
-
-if [ "x${1-""}" = "x-m" ]
-then
- # show Makefile name only
- mflag=1
-else
- mflag=""
-fi
-
-#
-# Do heuristic guesses !ONLY! for machines that do not have uname
-#
-if [ -d /NextApps -a ! -f /bin/uname -a ! -f /usr/bin/uname ]
-then
- # probably a NeXT box
- arch=`hostinfo | sed -n 's/.*Processor type: \([^ ]*\).*/\1/p'`
- os=NeXT
- rel=`hostinfo | sed -n 's/.*NeXT Mach \([0-9\.]*\).*/\1/p'`
-elif [ -f /usr/sony/bin/machine -a -f /etc/osversion ]
-then
- # probably a Sony NEWS 4.x
- os=NEWS-OS
- rel=`awk '{ print $3}' /etc/osversion`
- arch=`/usr/sony/bin/machine`
-elif [ -d /usr/omron -a -f /bin/luna ]
-then
- # probably a Omron LUNA
- os=LUNA
- if [ -f /bin/luna1 ] && /bin/luna1
- then
- rel=unios-b
- arch=luna1
- elif [ -f /bin/luna2 ] && /bin/luna2
- then
- rel=Mach
- arch=luna2
- elif [ -f /bin/luna88k ] && /bin/luna88k
- then
- rel=Mach
- arch=luna88k
- fi
-fi
-
-if [ ! "$arch" -a ! "$os" -a ! "$rel" ]
-then
- arch=`uname -m | sed -e 's/ //g'`
- os=`uname -s | sed -e 's/\//-/g' -e 's/ //g'`
- rel=`uname -r | sed -e 's/(/-/g' -e 's/)//g'`
-fi
-
-#
-# Tweak the values we have already got. PLEASE LIMIT THESE to
-# tweaks that are absolutely necessary because your system uname
-# routine doesn't return something sufficiently unique. Don't do
-# it just because you don't like the name that is returned. You
-# can combine the architecture name with the os name to create a
-# unique Makefile name.
-#
-
-# tweak machine architecture
-case $arch
-in
- sun4*) arch=sun4;;
-
- 9000/*) arch=`echo $arch | sed -e 's/9000.//' -e 's/..$/xx/'`;;
-
- DS/907000) arch=ds90;;
-esac
-
-# tweak operating system type and release
-node=`uname -n | sed -e 's/\//-/g' -e 's/ //g'`
-if [ "$os" = "$node" -a "$arch" = "i386" -a "$rel" = 3.2 -a "`uname -v`" = 2 ]
-then
- # old versions of SCO UNIX set uname -s the same as uname -n
- os=SCO_SV
-fi
-if [ "$os" = "$node" -a "$rel" = 4.0 -a "$arch" = "3360,3430-R" ]
-then
- # AT&T/NCR Machines also set uname -s == uname -n
- if [ -d /usr/sadm/sysadm/add-ons/WIN-TCP ]
- then
- os=NCR.MP-RAS.2.x
- else
- os=NCR.MP-RAS.3.x
- fi
-fi
-
-case $os
-in
- DYNIX-ptx) os=PTX;;
- Paragon*) os=Paragon;;
- HP-UX) rel=`echo $rel | sed -e 's/^[^.]*\.0*//'`;;
- AIX) rel=`uname -v`
- if [ "$rel" = "2" ]
- then
- arch=""
- fi;;
- BSD-386) os=BSD-OS;;
- SCO_SV) os=SCO; rel=`uname -X | sed -n 's/Release = 3.2v//p'`;;
- UNIX_System_V) if [ "$arch" = "ds90" ]
- then
- os="UXPDS"
- rel=`uname -v | sed -e 's/\(V.*\)L.*/\1/'`
- fi;;
- SINIX-?) os=SINIX;;
-esac
-
-# get "base part" of operating system release
-rroot=`echo $rel | sed -e 's/\.[^.]*$//'`
-rbase=`echo $rel | sed -e 's/\..*//'`
-if [ "$rroot" = "$rbase" ]
-then
- rroot=$rel
-fi
-
-# heuristic tweaks to clean up names -- PLEASE LIMIT THESE!
-if [ "$os" = "unix" ]
-then
- # might be Altos System V
- case $rel
- in
- 5.3*) os=Altos;;
- esac
-elif [ -r /unix -a -r /usr/lib/libseq.a -a -r /lib/cpp ]
-then
- # might be a DYNIX/ptx 2.x system, which has a broken uname
- if strings /lib/cpp | grep _SEQUENT_ > /dev/null
- then
- os=PTX
- fi
-elif [ -d /usr/nec ]
-then
- # NEC machine -- what is it running?
- if [ "$os" = "UNIX_System_V" ]
- then
- os=EWS-UX_V
- elif [ "$os" = "UNIX_SV" ]
- then
- os=UX4800
- fi
-elif [ "$arch" = "mips" ]
-then
- case $rel
- in
- 4_*)
- if [ `uname -v` = "UMIPS" ]
- then
- os=RISCos
- fi;;
- esac
-fi
-
-# see if there is a "user suffix" specified
-if [ "${SENDMAIL_SUFFIX-}x" = "x" ]
-then
- sfx=""
-else
- sfx=".${SENDMAIL_SUFFIX}"
-fi
-
-echo "Configuration: os=$os, rel=$rel, rbase=$rbase, rroot=$rroot, arch=$arch, sfx=$sfx"
-
-# now try to find a reasonable object directory
-if [ -r obj.$os.$rel.$arch$sfx ]; then
- obj=obj.$os.$rel.$arch$sfx
-elif [ -r obj.$os.$rroot.$arch$sfx ]; then
- obj=obj.$os.$rroot.$arch$sfx
-elif [ -r obj.$os.$rbase.x.$arch$sfx ]; then
- obj=obj.$os.$rbase.x.$arch$sfx
-elif [ -r obj.$os.$rel$sfx ]; then
- obj=obj.$os.$rel$sfx
-elif [ -r obj.$os.$rbase.x$sfx ]; then
- obj=obj.$os.$rbase.x$sfx
-elif [ -r obj.$os.$arch$sfx ]; then
- obj=obj.$os.$arch$sfx
-elif [ -r obj.$rel.$arch$sfx ]; then
- obj=obj.$rel.$arch$sfx
-elif [ -r obj.$rbase.x.$arch$sfx ]; then
- obj=obj.$rbase.x.$arch$sfx
-elif [ -r obj.$os$sfx ]; then
- obj=obj.$os$sfx
-elif [ -r obj.$arch$sfx ]; then
- obj=obj.$arch$sfx
-elif [ -r obj.$rel$sfx ]; then
- obj=obj.$rel$sfx
-elif [ -r obj$sfx ]; then
- obj=obj$sfx
-else
- # no existing obj directory -- try to create one if Makefile found
- obj=obj.$os.$rel.$arch$sfx
- if [ -r Makefiles/Makefile.$os.$rel.$arch$sfx ]; then
- makefile=Makefile.$os.$rel.$arch$sfx
- elif [ -r Makefiles/Makefile.$os.$rel.$arch ]; then
- makefile=Makefile.$os.$rel.$arch
- elif [ -r Makefiles/Makefile.$os.$rroot.$arch$sfx ]; then
- makefile=Makefile.$os.$rroot.$arch$sfx
- elif [ -r Makefiles/Makefile.$os.$rroot.$arch ]; then
- makefile=Makefile.$os.$rroot.$arch
- elif [ -r Makefiles/Makefile.$os.$rbase.x.$arch$sfx ]; then
- makefile=Makefile.$os.$rbase.x.$arch$sfx
- elif [ -r Makefiles/Makefile.$os.$rbase.x.$arch ]; then
- makefile=Makefile.$os.$rbase.x.$arch
- elif [ -r Makefiles/Makefile.$os.$rel$sfx ]; then
- makefile=Makefile.$os.$rel$sfx
- elif [ -r Makefiles/Makefile.$os.$rel ]; then
- makefile=Makefile.$os.$rel
- elif [ -r Makefiles/Makefile.$os.$rroot$sfx ]; then
- makefile=Makefile.$os.$rroot$sfx
- elif [ -r Makefiles/Makefile.$os.$rroot ]; then
- makefile=Makefile.$os.$rroot
- elif [ -r Makefiles/Makefile.$os.$rbase.x$sfx ]; then
- makefile=Makefile.$os.$rbase.x$sfx
- elif [ -r Makefiles/Makefile.$os.$rbase.x ]; then
- makefile=Makefile.$os.$rbase.x
- elif [ -r Makefiles/Makefile.$os.$arch$sfx ]; then
- makefile=Makefile.$os.$arch$sfx
- elif [ -r Makefiles/Makefile.$os.$arch ]; then
- makefile=Makefile.$os.$arch
- elif [ -r Makefiles/Makefile.$rel.$arch$sfx ]; then
- makefile=Makefile.$rel.$arch$sfx
- elif [ -r Makefiles/Makefile.$rel.$arch ]; then
- makefile=Makefile.$rel.$arch
- elif [ -r Makefiles/Makefile.$rroot.$arch$sfx ]; then
- makefile=Makefile.$rroot.$arch$sfx
- elif [ -r Makefiles/Makefile.$rroot.$arch ]; then
- makefile=Makefile.$rroot.$arch
- elif [ -r Makefiles/Makefile.$rbase.x.$arch$sfx ]; then
- makefile=Makefile.$rbase.x.$arch$sfx
- elif [ -r Makefiles/Makefile.$rbase.x.$arch ]; then
- makefile=Makefile.$rbase.x.$arch
- elif [ -r Makefiles/Makefile.$os$sfx ]; then
- makefile=Makefile.$os$sfx
- elif [ -r Makefiles/Makefile.$os ]; then
- makefile=Makefile.$os
- elif [ -r Makefiles/Makefile.$arch$sfx ]; then
- makefile=Makefile.$arch$sfx
- elif [ -r Makefiles/Makefile.$arch ]; then
- makefile=Makefile.$arch
- elif [ -r Makefiles/Makefile.$rel$sfx ]; then
- makefile=Makefile.$rel$sfx
- elif [ -r Makefiles/Makefile.$rel ]; then
- makefile=Makefile.$rel
- elif [ -r Makefiles/Makefile.$rel$sfx ]; then
- makefile=Makefile.$rel$sfx
- else
- echo "Cannot determine how to support $arch.$os.$rel"
- exit 1
- fi
- if [ "$mflag" ]
- then
- echo "Will run in virgin $obj using $makefile"
- exit 0
- fi
- echo "Creating $obj using $makefile"
- mkdir $obj
- (cd $obj; ln -s ../*.[ch158] ../sendmail.hf .; ln -s ../Makefiles/$makefile Makefile)
- echo "Making dependencies in $obj"
- (cd $obj; ${MAKE-make} depend)
-fi
-
-if [ "$mflag" ]
-then
- makefile=`ls -l $obj/Makefile | sed 's/.* //'`
- if [ -z "$makefile" ]
- then
- echo "ERROR: $obj exists but has no Makefile"
- exit 1
- fi
- case $makefile
- in
- ../Makefiles/*)
- makefile=`echo $makefile | sed 's/...Makefiles.//'`
- echo "Will run in existing $obj using $makefile"
- ;;
-
- *)
- echo "Will run in existing $obj using custom $makefile"
- ;;
- esac
- exit 0
-fi
-
-echo "Making in $obj"
-cd $obj
-if [ $# = 0 ]
-then
- exec ${MAKE-make}
-else
- exec ${MAKE-make} "$@"
-fi
diff --git a/usr.sbin/sendmail/src/parseaddr.c b/usr.sbin/sendmail/src/parseaddr.c
index 7831834..75b9089 100644
--- a/usr.sbin/sendmail/src/parseaddr.c
+++ b/usr.sbin/sendmail/src/parseaddr.c
@@ -33,7 +33,7 @@
*/
#ifndef lint
-static char sccsid[] = "@(#)parseaddr.c 8.132 (Berkeley) 10/20/97";
+static char sccsid[] = "@(#)parseaddr.c 8.130 (Berkeley) 8/2/97";
#endif /* not lint */
# include "sendmail.h"
@@ -1859,7 +1859,6 @@ struct qflags AddressFlags[] =
{ "QDELIVERED", QDELIVERED },
{ "QDELAYED", QDELAYED },
{ "QTHISPASS", QTHISPASS },
- { "QRCPTOK", QRCPTOK },
{ NULL }
};
@@ -2137,11 +2136,7 @@ maplocaluser(a, sendq, aliaslevel, e)
}
pvp = prescan(a->q_user, '\0', pvpbuf, sizeof pvpbuf, &delimptr, NULL);
if (pvp == NULL)
- {
- if (tTd(29, 9))
- printf("maplocaluser: cannot prescan %s\n", a->q_user);
return;
- }
define('h', a->q_host, e);
define('u', a->q_user, e);
@@ -2149,25 +2144,17 @@ maplocaluser(a, sendq, aliaslevel, e)
if (rewrite(pvp, 5, 0, e) == EX_TEMPFAIL)
{
- if (tTd(29, 9))
- printf("maplocaluser: rewrite tempfail\n");
a->q_flags |= QQUEUEUP;
a->q_status = "4.4.3";
return;
}
if (pvp[0] == NULL || (pvp[0][0] & 0377) != CANONNET)
- {
- if (tTd(29, 9))
- printf("maplocaluser: doesn't resolve\n");
return;
- }
/* if non-null, mailer destination specified -- has it changed? */
a1 = buildaddr(pvp, NULL, 0, e);
if (a1 == NULL || sameaddr(a, a1))
{
- if (tTd(29, 9))
- printf("maplocaluser: address unchanged\n");
if (a1 != NULL)
free(a1);
return;
diff --git a/usr.sbin/sendmail/src/readcf.c b/usr.sbin/sendmail/src/readcf.c
index 681c3d3..a8c82f7 100644
--- a/usr.sbin/sendmail/src/readcf.c
+++ b/usr.sbin/sendmail/src/readcf.c
@@ -33,7 +33,7 @@
*/
#ifndef lint
-static char sccsid[] = "@(#)readcf.c 8.201 (Berkeley) 10/1/97";
+static char sccsid[] = "@(#)readcf.c 8.200 (Berkeley) 8/2/97";
#endif /* not lint */
# include "sendmail.h"
@@ -480,7 +480,7 @@ readcf(cfname, safe, e)
continue;
if (!isascii(*p) || !isdigit(*p))
{
- syserr("invalid argument to V line: \"%.20s\"",
+ syserr("invalid argument to V line: \"%.20s\"",
&bp[1]);
break;
}
@@ -1521,10 +1521,6 @@ struct optioninfo
#define O_DONTLOCK 0xa5
{ "DontLockFilesForRead", O_DONTLOCK, FALSE },
#endif
-#if _FFR_MAXALIASRECURSION_OPTION
-#define O_MAXALIASRCSN 0xa6
- { "MaxAliasRecursion", O_MAXALIASRCSN, FALSE },
-#endif
{ NULL, '\0', FALSE }
};
@@ -2359,12 +2355,6 @@ setoption(opt, val, safe, sticky, e)
break;
#endif
-#if _FFR_MAXALIASRECURSION_OPTION
- case O_MAXALIASRCSN:
- MaxAliasRecursion = atoi(val);
- break;
-#endif
-
default:
if (tTd(37, 1))
{
diff --git a/usr.sbin/sendmail/src/recipient.c b/usr.sbin/sendmail/src/recipient.c
index adcce7a..2076884 100644
--- a/usr.sbin/sendmail/src/recipient.c
+++ b/usr.sbin/sendmail/src/recipient.c
@@ -33,7 +33,7 @@
*/
#ifndef lint
-static char sccsid[] = "@(#)recipient.c 8.133 (Berkeley) 10/19/97";
+static char sccsid[] = "@(#)recipient.c 8.130 (Berkeley) 5/29/97";
#endif /* not lint */
# include "sendmail.h"
@@ -263,7 +263,6 @@ recipient(a, sendq, aliaslevel, e)
/* break aliasing loops */
if (aliaslevel > MaxAliasRecursion)
{
- a->q_flags |= QBADADDR;
a->q_status = "5.4.6";
usrerr("554 aliasing/forwarding loop broken (%d aliases deep; %d max)",
aliaslevel, MaxAliasRecursion);
@@ -328,7 +327,7 @@ recipient(a, sendq, aliaslevel, e)
for (pq = sendq; (q = *pq) != NULL; pq = &q->q_next)
{
- if (sameaddr(q, a) && bitset(QRCPTOK, q->q_flags))
+ if (sameaddr(q, a))
{
if (tTd(26, 1))
{
@@ -642,7 +641,7 @@ recipient(a, sendq, aliaslevel, e)
a->q_paddr);
}
}
- a->q_flags |= QRCPTOK;
+
return (a);
}
/*
@@ -923,7 +922,7 @@ include(fname, forwarding, ctladdr, sendq, aliaslevel, e)
volatile int sfflags = SFF_REGONLY;
register char *p;
bool safechown = FALSE;
- volatile bool safedir = FALSE;
+ bool safedir = FALSE;
struct stat st;
char buf[MAXLINE];
extern bool chownsafe();
diff --git a/usr.sbin/sendmail/src/savemail.c b/usr.sbin/sendmail/src/savemail.c
index 76209dc..40ad883 100644
--- a/usr.sbin/sendmail/src/savemail.c
+++ b/usr.sbin/sendmail/src/savemail.c
@@ -33,7 +33,7 @@
*/
#ifndef lint
-static char sccsid[] = "@(#)savemail.c 8.121 (Berkeley) 10/22/97";
+static char sccsid[] = "@(#)savemail.c 8.114 (Berkeley) 8/2/97";
#endif /* not lint */
# include "sendmail.h"
@@ -85,6 +85,7 @@ savemail(e, sendbody)
int flags;
char buf[MAXLINE+1];
extern char *ttypath();
+ typedef int (*fnptr)();
extern bool writable();
if (tTd(6, 1))
@@ -253,29 +254,8 @@ savemail(e, sendbody)
}
if (!emptyaddr(&e->e_from))
{
- char from[TOBUFSIZE];
- extern bool pruneroute __P((char *));
-
- if (strlen(e->e_from.q_paddr) + 1 > sizeof from)
- {
- state = ESM_POSTMASTER;
- break;
- }
- strcpy(from, e->e_from.q_paddr);
-
- if (!DontPruneRoutes && pruneroute(from))
- {
- ADDRESS *a;
-
- for (a = e->e_errorqueue; a != NULL;
- a = a->q_next)
- {
- if (sameaddr(a, &e->e_from))
- a->q_flags |= QDONTSEND;
- }
- }
- (void) sendtolist(from, NULLADDR,
- &e->e_errorqueue, 0, e);
+ (void) sendtolist(e->e_from.q_paddr,
+ NULLADDR, &e->e_errorqueue, 0, e);
}
/*
@@ -399,7 +379,7 @@ savemail(e, sendbody)
break;
}
- flags = SFF_NOLINK|SFF_CREAT|SFF_REGONLY|SFF_ROOTOK|SFF_OPENASROOT|SFF_MUSTOWN;
+ flags = SFF_NOLINK|SFF_CREAT|SFF_REGONLY|SFF_OPENASROOT|SFF_MUSTOWN;
if (!writable(DeadLetterDrop, NULL, flags) ||
(fp = safefopen(DeadLetterDrop, O_WRONLY|O_APPEND,
FileMode, flags)) == NULL)
@@ -539,12 +519,26 @@ returntosender(msg, returnq, flags, e)
initsys(ee);
for (q = returnq; q != NULL; q = q->q_next)
{
+ extern bool pruneroute __P((char *));
+
if (bitset(QBADADDR, q->q_flags))
continue;
q->q_flags &= ~(QHASNOTIFY|Q_PINGFLAGS);
q->q_flags |= QPINGONFAILURE;
+ if (!DontPruneRoutes && pruneroute(q->q_paddr))
+ {
+ register ADDRESS *p;
+
+ parseaddr(q->q_paddr, q, RF_COPYPARSE, '\0', NULL, e);
+ for (p = returnq; p != NULL; p = p->q_next)
+ {
+ if (p != q && sameaddr(p, q))
+ q->q_flags |= QDONTSEND;
+ }
+ }
+
if (!bitset(QDONTSEND, q->q_flags))
ee->e_nrcpts++;
@@ -656,7 +650,7 @@ returntosender(msg, returnq, flags, e)
return 0;
for (q = ee->e_sendqueue; q != NULL; q = q->q_next)
{
- if (bitset(QQUEUEUP|QSENT, q->q_flags))
+ if (bitset(QSENT, q->q_flags))
return 0;
}
return -1;
@@ -687,7 +681,7 @@ errbody(mci, e, separator)
{
register FILE *xfile;
char *p;
- register ADDRESS *q = NULL;
+ register ADDRESS *q;
bool printheader;
bool sendbody;
bool pm_notify;
@@ -1007,23 +1001,17 @@ errbody(mci, e, separator)
p = "rfc822";
for (r = q; r->q_alias != NULL; r = r->q_alias)
continue;
- if (strchr(r->q_user, '@') != NULL)
- {
- (void) snprintf(buf, sizeof buf,
- "Final-Recipient: %s; %.800s",
- p, r->q_user);
- }
- else if (strchr(r->q_paddr, '@') != NULL)
+ if (strchr(r->q_user, '@') == NULL)
{
(void) snprintf(buf, sizeof buf,
- "Final-Recipient: %s; %.800s",
- p, r->q_paddr);
+ "Final-Recipient: %s; %.700s@%.100s",
+ p, r->q_user, MyHostName);
}
else
{
(void) snprintf(buf, sizeof buf,
- "Final-Recipient: %s; %.700s@%.100s",
- p, r->q_user, MyHostName);
+ "Final-Recipient: %s; %.800s",
+ p, r->q_user);
}
putline(buf, mci);
@@ -1440,14 +1428,14 @@ xtextok(s)
**
** Trims down a source route to the last internet-registered hop.
** This is encouraged by RFC 1123 section 5.3.3.
-**
+**
** Parameters:
** addr -- the address
-**
+**
** Returns:
** TRUE -- address was modified
** FALSE -- address could not be pruned
-**
+**
** Side Effects:
** modifies addr in-place
*/
diff --git a/usr.sbin/sendmail/src/sendmail.8 b/usr.sbin/sendmail/src/sendmail.8
index 9a0a687..67cef7f 100644
--- a/usr.sbin/sendmail/src/sendmail.8
+++ b/usr.sbin/sendmail/src/sendmail.8
@@ -566,7 +566,7 @@ raw data for alias names
data base of alias names
.It Pa /etc/sendmail.cf
configuration file
-.It Pa /etc/sendmail.hf
+.It Pa /usr/share/misc/sendmail.hf
help file
.It Pa /var/log/sendmail.st
collected statistics
@@ -576,13 +576,13 @@ temp files
The process id of the daemon
.El
.Sh SEE ALSO
-.Xr binmail 1 ,
.Xr mail 1 ,
-.Xr rmail 1 ,
.Xr syslog 3 ,
.Xr aliases 5 ,
.Xr mailaddr 7 ,
-.Xr rc 8 ;
+.Xr mail.local 8 ,
+.Xr rc 8 ,
+.Xr rmail 8 ;
.Pp
DARPA
Internet Request For Comments
diff --git a/usr.sbin/sendmail/src/sendmail.h b/usr.sbin/sendmail/src/sendmail.h
index fc3bf49..00559ea 100644
--- a/usr.sbin/sendmail/src/sendmail.h
+++ b/usr.sbin/sendmail/src/sendmail.h
@@ -31,7 +31,7 @@
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
- * @(#)sendmail.h 8.245 (Berkeley) 10/22/97
+ * @(#)sendmail.h 8.242 (Berkeley) 8/2/97
*/
/*
@@ -41,7 +41,7 @@
# ifdef _DEFINE
# define EXTERN
# ifndef lint
-static char SmailSccsId[] = "@(#)sendmail.h 8.245 10/22/97";
+static char SmailSccsId[] = "@(#)sendmail.h 8.242 8/2/97";
# endif
# else /* _DEFINE */
# define EXTERN extern
@@ -206,7 +206,6 @@ typedef struct address ADDRESS;
# define QDELIVERED 0x00040000 /* DSN: successful final delivery */
# define QDELAYED 0x00080000 /* DSN: message delayed */
# define QTHISPASS 0x40000000 /* temp: address set this pass */
-# define QRCPTOK 0x80000000 /* recipient() processed address */
# define Q_PINGFLAGS (QPINGONSUCCESS|QPINGONFAILURE|QPINGONDELAY)
@@ -929,7 +928,6 @@ EXTERN int NoRecipientAction;
#define PXLF_NOTHINGSPECIAL 0 /* no special mapping */
#define PXLF_MAPFROM 0x0001 /* map From_ to >From_ */
#define PXLF_STRIP8BIT 0x0002 /* strip 8th bit */
-#define PXLF_HEADER 0x0004 /* map newlines in headers */
/*
** Additional definitions
*/
@@ -1233,6 +1231,7 @@ EXTERN int MaxRuleRecursion; /* maximum depth of ruleset recursion */
EXTERN char *MustQuoteChars; /* quote these characters in phrases */
EXTERN char *ServiceSwitchFile; /* backup service switch */
EXTERN char *DefaultCharSet; /* default character set for MIME */
+EXTERN int DeliveryNiceness; /* how nice to be during delivery */
EXTERN char *PostMasterCopy; /* address to get errs cc's */
EXTERN int CheckpointInterval; /* queue file checkpoint interval */
EXTERN bool DontPruneRoutes; /* don't prune source routes */
diff --git a/usr.sbin/sendmail/src/srvrsmtp.c b/usr.sbin/sendmail/src/srvrsmtp.c
index cd47d5d..3255e83 100644
--- a/usr.sbin/sendmail/src/srvrsmtp.c
+++ b/usr.sbin/sendmail/src/srvrsmtp.c
@@ -36,9 +36,9 @@
#ifndef lint
#if SMTP
-static char sccsid[] = "@(#)srvrsmtp.c 8.159 (Berkeley) 10/19/97 (with SMTP)";
+static char sccsid[] = "@(#)srvrsmtp.c 8.154 (Berkeley) 8/2/97 (with SMTP)";
#else
-static char sccsid[] = "@(#)srvrsmtp.c 8.159 (Berkeley) 10/19/97 (without SMTP)";
+static char sccsid[] = "@(#)srvrsmtp.c 8.154 (Berkeley) 8/2/97 (without SMTP)";
#endif
#endif /* not lint */
@@ -153,7 +153,7 @@ smtp(nullserver, e)
volatile int n_noop = 0; /* count of NOOP/VERB/ONEX etc cmds */
volatile int n_helo = 0; /* count of HELO/EHLO commands */
bool ok;
- volatile int lognullconnection = TRUE;
+ int lognullconnection = TRUE;
register char *q;
char inp[MAXLINE];
char cmdbuf[MAXLINE];
@@ -252,18 +252,6 @@ smtp(nullserver, e)
sm_syslog(LOG_NOTICE, e->e_id,
"lost input channel from %.100s",
CurSmtpClient);
- if (lognullconnection && LogLevel > 5)
- sm_syslog(LOG_INFO, NULL,
- "Null connection from %.100s",
- CurSmtpClient);
-
- /*
- ** If have not accepted mail (DATA), do not bounce
- ** bad addresses back to sender.
- */
- if (bitset(EF_CLRQUEUE, e->e_flags))
- e->e_sendqueue = NULL;
-
if (InChild)
ExitStat = EX_QUIT;
finis();
@@ -884,7 +872,6 @@ smtp(nullserver, e)
shortenstring(inp, 203));
if (setjmp(TopFrame) > 0)
goto undo_subproc;
- QuickAbort = TRUE;
vrfyqueue = NULL;
if (vrfy)
e->e_flags |= EF_VRFYONLY;
@@ -930,17 +917,16 @@ smtp(nullserver, e)
/* crude way to avoid denial-of-service attacks */
checksmtpattack(&n_etrn, MAXETRNCOMMANDS, "ETRN", e);
- if (LogLevel > 5)
- sm_syslog(LOG_INFO, e->e_id,
- "%.100s: ETRN %s",
- CurSmtpClient,
- shortenstring(p, 203));
-
id = p;
if (*id == '@')
id++;
else
*--id = '@';
+ if (LogLevel > 5)
+ sm_syslog(LOG_INFO, e->e_id,
+ "%.100s: ETRN %s",
+ CurSmtpClient,
+ shortenstring(id, 203));
QueueLimitRecipient = id;
ok = runqueue(TRUE, TRUE);
QueueLimitRecipient = NULL;
diff --git a/usr.sbin/sendmail/src/sysexits.h b/usr.sbin/sendmail/src/sysexits.h
deleted file mode 100644
index 464cb11..0000000
--- a/usr.sbin/sendmail/src/sysexits.h
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * 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.
- *
- * @(#)sysexits.h 8.1 (Berkeley) 6/2/93
- */
-
-#ifndef _SYSEXITS_H_
-#define _SYSEXITS_H_
-
-/*
- * SYSEXITS.H -- Exit status codes for system programs.
- *
- * This include file attempts to categorize possible error
- * exit statuses for system programs, notably delivermail
- * and the Berkeley network.
- *
- * Error numbers begin at EX__BASE to reduce the possibility of
- * clashing with other exit statuses that random programs may
- * already return. The meaning of the codes is approximately
- * as follows:
- *
- * EX_USAGE -- The command was used incorrectly, e.g., with
- * the wrong number of arguments, a bad flag, a bad
- * syntax in a parameter, or whatever.
- * EX_DATAERR -- The input data was incorrect in some way.
- * This should only be used for user's data & not
- * system files.
- * EX_NOINPUT -- An input file (not a system file) did not
- * exist or was not readable. This could also include
- * errors like "No message" to a mailer (if it cared
- * to catch it).
- * EX_NOUSER -- The user specified did not exist. This might
- * be used for mail addresses or remote logins.
- * EX_NOHOST -- The host specified did not exist. This is used
- * in mail addresses or network requests.
- * EX_UNAVAILABLE -- A service is unavailable. This can occur
- * if a support program or file does not exist. This
- * can also be used as a catchall message when something
- * you wanted to do doesn't work, but you don't know
- * why.
- * EX_SOFTWARE -- An internal software error has been detected.
- * This should be limited to non-operating system related
- * errors as possible.
- * EX_OSERR -- An operating system error has been detected.
- * This is intended to be used for such things as "cannot
- * fork", "cannot create pipe", or the like. It includes
- * things like getuid returning a user that does not
- * exist in the passwd file.
- * EX_OSFILE -- Some system file (e.g., /etc/passwd, /etc/utmp,
- * etc.) does not exist, cannot be opened, or has some
- * sort of error (e.g., syntax error).
- * EX_CANTCREAT -- A (user specified) output file cannot be
- * created.
- * EX_IOERR -- An error occurred while doing I/O on some file.
- * EX_TEMPFAIL -- temporary failure, indicating something that
- * is not really an error. In sendmail, this means
- * that a mailer (e.g.) could not create a connection,
- * and the request should be reattempted later.
- * EX_PROTOCOL -- the remote system returned something that
- * was "not possible" during a protocol exchange.
- * EX_NOPERM -- You did not have sufficient permission to
- * perform the operation. This is not intended for
- * file system problems, which should use NOINPUT or
- * CANTCREAT, but rather for higher level permissions.
- */
-
-#define EX_OK 0 /* successful termination */
-
-#define EX__BASE 64 /* base value for error messages */
-
-#define EX_USAGE 64 /* command line usage error */
-#define EX_DATAERR 65 /* data format error */
-#define EX_NOINPUT 66 /* cannot open input */
-#define EX_NOUSER 67 /* addressee unknown */
-#define EX_NOHOST 68 /* host name unknown */
-#define EX_UNAVAILABLE 69 /* service unavailable */
-#define EX_SOFTWARE 70 /* internal software error */
-#define EX_OSERR 71 /* system error (e.g., can't fork) */
-#define EX_OSFILE 72 /* critical OS file missing */
-#define EX_CANTCREAT 73 /* can't create (user) output file */
-#define EX_IOERR 74 /* input/output error */
-#define EX_TEMPFAIL 75 /* temp failure; user is invited to retry */
-#define EX_PROTOCOL 76 /* remote error in protocol */
-#define EX_NOPERM 77 /* permission denied */
-#define EX_CONFIG 78 /* configuration error */
-
-#define EX__MAX 78 /* maximum listed value */
-
-#endif /* !_SYSEXITS_H_ */
diff --git a/usr.sbin/sendmail/src/udb.c b/usr.sbin/sendmail/src/udb.c
index cf77bdc..51c6873 100644
--- a/usr.sbin/sendmail/src/udb.c
+++ b/usr.sbin/sendmail/src/udb.c
@@ -605,7 +605,7 @@ udbmatch(user, field)
case UDB_HESIOD:
key.data = keybuf;
key.size = keylen;
- i = hes_udb_get(&key, &info);
+ i = hes_udb_get(&key, &info);
if (i != 0 || info.size <= 0)
{
if (tTd(28, 2))
@@ -693,7 +693,7 @@ udbmatch(user, field)
{
key.data = ":default:mailname";
key.size = strlen(key.data);
- i = hes_udb_get(&key, &info);
+ i = hes_udb_get(&key, &info);
if (i != 0 || info.size <= 0)
{
diff --git a/usr.sbin/sendmail/src/usersmtp.c b/usr.sbin/sendmail/src/usersmtp.c
index b088d60..8529497 100644
--- a/usr.sbin/sendmail/src/usersmtp.c
+++ b/usr.sbin/sendmail/src/usersmtp.c
@@ -36,9 +36,9 @@
#ifndef lint
#if SMTP
-static char sccsid[] = "@(#)usersmtp.c 8.88 (Berkeley) 10/20/97 (with SMTP)";
+static char sccsid[] = "@(#)usersmtp.c 8.87 (Berkeley) 6/3/97 (with SMTP)";
#else
-static char sccsid[] = "@(#)usersmtp.c 8.88 (Berkeley) 10/20/97 (without SMTP)";
+static char sccsid[] = "@(#)usersmtp.c 8.87 (Berkeley) 6/3/97 (without SMTP)";
#endif
#endif /* not lint */
@@ -60,6 +60,7 @@ static char sccsid[] = "@(#)usersmtp.c 8.88 (Berkeley) 10/20/97 (without SMTP)";
char SmtpMsgBuffer[MAXLINE]; /* buffer for commands */
char SmtpReplyBuffer[MAXLINE]; /* buffer for replies */
char SmtpError[MAXLINE] = ""; /* save failure error messages */
+int SmtpPid; /* pid of mailer */
bool SmtpNeedIntro; /* need "while talking" in transcript */
extern void smtpmessage __P((char *f, MAILER *m, MCI *mci, ...));
@@ -908,7 +909,7 @@ smtpquit(m, mci, e)
/*
** Suppress errors here -- we may be processing a different
- ** job when we do the quit connection, and we don't want the
+ ** job when we do the quit connection, and we don't want the
** new job to be penalized for something that isn't it's
** problem.
*/
diff --git a/usr.sbin/sendmail/src/util.c b/usr.sbin/sendmail/src/util.c
index 2e5aebe..c655b62 100644
--- a/usr.sbin/sendmail/src/util.c
+++ b/usr.sbin/sendmail/src/util.c
@@ -33,7 +33,7 @@
*/
#ifndef lint
-static char sccsid[] = "@(#)util.c 8.137 (Berkeley) 10/22/97";
+static char sccsid[] = "@(#)util.c 8.133 (Berkeley) 8/1/97";
#endif /* not lint */
# include "sendmail.h"
@@ -192,7 +192,7 @@ copyqueue(addr)
addr = addr->q_next;
}
*tail = NULL;
-
+
return ret;
}
/*
@@ -520,7 +520,6 @@ putline(l, mci)
** pxflags -- flag bits:
** PXLF_MAPFROM -- map From_ to >From_.
** PXLF_STRIP8BIT -- strip 8th bit.
-** PXLF_HEADER -- map bare newline in header to newline space.
**
** Returns:
** none
@@ -627,8 +626,7 @@ putxline(l, len, mci, pxflags)
fputs(mci->mci_mailer->m_eol, mci->mci_out);
if (l < end && *l == '\n')
{
- if (*++l != ' ' && *l != '\t' && *l != '\0' &&
- bitset(PXLF_HEADER, pxflags))
+ if (*++l != ' ' && *l != '\t' && *l != '\0')
{
(void) putc(' ', mci->mci_out);
if (TrafficLogFile != NULL)
@@ -1060,6 +1058,7 @@ checkfd012(where)
{
#if XDEBUG
register int i;
+ struct stat stbuf;
for (i = 0; i < 3; i++)
fill_fd(i, where);
@@ -1200,6 +1199,7 @@ dumpfd(fd, printclosed, logit)
int i;
struct stat st;
char buf[200];
+ extern char *hostnamebyanyaddr();
p = buf;
snprintf(p, SPACELEFT(buf, p), "%3d: ", fd);
@@ -1311,7 +1311,7 @@ defprint:
printit:
if (logit)
- sm_syslog(LOG_DEBUG, CurEnv ? CurEnv->e_id : NULL,
+ sm_syslog(LOG_DEBUG, CurEnv->e_id,
"%.800s", buf);
else
printf("%s\n", buf);
diff --git a/usr.sbin/sgsc/Makefile b/usr.sbin/sgsc/Makefile
new file mode 100644
index 0000000..a25675d
--- /dev/null
+++ b/usr.sbin/sgsc/Makefile
@@ -0,0 +1,5 @@
+# $Id$
+
+PROG= sgsc
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/sgsc/sgsc.1 b/usr.sbin/sgsc/sgsc.1
new file mode 100644
index 0000000..5078c0b
--- /dev/null
+++ b/usr.sbin/sgsc/sgsc.1
@@ -0,0 +1,97 @@
+.\" sgsc(1) - manual page for the `gsc' scanner device driver utility
+.\"
+.\"
+.\" Copyright (c) 1995 Gunther Schadow. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by Gunther Schadow.
+.\" 4. The name of the author may not be used to endorse or promote products
+.\" derived from this software without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+.Dd January 6, 1995
+.Dt SGSC 1
+.Os
+.Sh NAME
+.Nm sgsc
+.Nd set the options of the gsc scanner device
+.Sh SYNOPSIS
+.Nm sgsc
+.Op Fl sq
+.Op Fl b Ar len
+.Op Fl f Ar file
+.Op Fl h Ar height
+.Op Fl r Ar resolution
+.Op Fl t Ar timeout
+.Op Fl w Ar width
+.Sh DESCRIPTION
+The
+.Nm
+utility provides shell level access to the ioctl
+requests served by the handy scanner device driver gsc. Please see
+.Xr gsc 4
+for the exact meaning of the requests. Generally they modify
+the output and behaviour of the gsc scanner device. When
+.Nm
+is called with no option only the current settings are reported.
+.Sh OPTIONS
+The following options are available:
+.Bl -tag -width indent
+.It Fl s Ar [GSC_SRESSW]
+Set the scanner with the values of the resolution selector switch.
+.It Fl q
+Operate in quiet mode, i.e. do not report any of the current settings.
+Normally the parameters are shown after the modifications have been
+performed.
+.It Fl f Ar file
+Operate on a different scanner device node given by filename. Note
+that even though there may exist more than one node of scanner device
+several of them will refer the same device unit. The modifications are
+performed od the unit regardless of the device node by which it is
+accessed.
+.It Fl r Ar resolution [GSC_SRES]
+Set the resolution in dpi.
+.It Fl w Ar width [GSC_SWIDHT]
+Set the width of the bitmap in pixels.
+.It Fl h Ar height [GSC_SHEIGHT]
+Set the height of the bitmap in lines of pixels.
+.It Fl b Ar len [GSC_SBLEN]
+Set the internal dma buffer to be
+.Ar len
+lines in size.
+.It Fl t Ar timeout [GSC_SBTIME]
+Set the timeout time for reading one dma buffer.
+.El
+.Sh FILES
+.Bl -tag -width /dev/gsc0p -compact
+.It Pa /dev/gsc0
+device node for
+.Em raw
+output
+.It Pa /dev/gsc0p
+device node for output in
+.Em pbm
+file format
+.El
+.Sh SEE ALSO
+.Xr gsc 4
+.Sh AUTHOR
+.An Gunther Schadow Aq gusw@fub46.zedat.fu-berlin.de
diff --git a/usr.sbin/sgsc/sgsc.c b/usr.sbin/sgsc/sgsc.c
new file mode 100644
index 0000000..f9850c7
--- /dev/null
+++ b/usr.sbin/sgsc/sgsc.c
@@ -0,0 +1,163 @@
+/* sgsc(1) - utility for the `gsc' scanner device driver
+ *
+ *
+ * Copyright (c) 1995 Gunther Schadow. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Gunther Schadow.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/file.h>
+#include <sys/ioctl.h>
+#include <machine/gsc.h>
+
+#ifndef DEFAULT_FILE
+#define DEFAULT_FILE "/dev/gsc0"
+#endif
+#ifdef FAIL
+#undef FAIL
+#endif
+#define FAIL -1
+
+static void
+usage()
+{
+ fprintf(stderr, "%s\n%s\n",
+ "usage: sgsc [-sq] [-f file] [-r dpi] [-w width] [-h height]",
+ " [-b len] [-t time]");
+ exit(1);
+}
+
+int
+main(int argc, char **argv)
+{
+ char c;
+ int fd;
+
+ char *file = DEFAULT_FILE;
+
+ int show_dpi = 0;
+ int show_width = 0;
+ int show_height = 0;
+ int show_blen = 0;
+ int show_btime = 0;
+ int show_all = 1;
+
+ int set_blen = 0;
+ int set_dpi = 0;
+ int set_width = 0;
+ int set_height = 0;
+ int set_btime = 0;
+ int set_switch = 0;
+
+ if (argc == 0) usage();
+
+ while( (c = getopt(argc, argv, "sqf:b:r:w:h:t:")) != FAIL)
+ {
+ switch(c) {
+ case 'f': file = optarg; break;
+ case 'r': set_dpi = atoi(optarg); break;
+ case 'w': set_width = atoi(optarg); break;
+ case 'h': set_height = atoi(optarg); break;
+ case 'b': set_blen = atoi(optarg); break;
+ case 't': set_btime = atoi(optarg); break;
+ case 's': set_switch = 1; break;
+ case 'q': show_all = 0; break;
+ default: usage();
+ }
+ }
+
+ fd = open(file, O_RDONLY);
+ if ( fd == FAIL )
+ err(1, "%s", file);
+
+ if (set_switch != 0)
+ {
+ if(ioctl(fd, GSC_SRESSW) == FAIL)
+ err(1, "GSC_SRESSW");
+ }
+
+ if (set_dpi != 0)
+ {
+ if(ioctl(fd, GSC_SRES, &set_dpi) == FAIL)
+ err(1, "GSC_SRES");
+ }
+
+ if (set_width != 0)
+ {
+ if(ioctl(fd, GSC_SWIDTH, &set_width) == FAIL)
+ err(1, "GSC_SWIDTH");
+ }
+
+ if (set_height != 0)
+ {
+ if(ioctl(fd, GSC_SHEIGHT, &set_height) == FAIL)
+ err(1, "GSC_SHEIGHT");
+ }
+
+ if (set_blen != 0)
+ {
+ if(ioctl(fd, GSC_SBLEN, &set_blen) == FAIL)
+ err(1, "GSC_SBLEN");
+ }
+
+ if (set_btime != 0)
+ {
+ if(ioctl(fd, GSC_SBTIME, &set_btime) == FAIL)
+ err(1, "GSC_SBTIME");
+ }
+
+ if (show_all != 0)
+ {
+ if(ioctl(fd, GSC_GRES, &show_dpi) == FAIL)
+ err(1, "GSC_GRES");
+ if(ioctl(fd, GSC_GWIDTH, &show_width) == FAIL)
+ err(1, "GSC_GWIDTH");
+ if(ioctl(fd, GSC_GHEIGHT, &show_height) == FAIL)
+ err(1, "GSC_GHEIGHT");
+ if(ioctl(fd, GSC_GBLEN, &show_blen) == FAIL)
+ err(1, "GSC_GBLEN");
+ if(ioctl(fd, GSC_GBTIME, &show_btime) == FAIL)
+ err(1, "GSC_GBTIME");
+
+ printf("%s:\n", file);
+ printf("resolution\t %d dpi\n", show_dpi);
+ printf("width\t\t %d\n", show_width);
+ printf("height\t\t %d\n",show_height);
+ printf("buffer length\t %d\n", show_blen);
+ printf("buffer timeout\t %d\n", show_btime);
+ }
+
+ return 0;
+}
diff --git a/usr.sbin/sicontrol/Makefile b/usr.sbin/sicontrol/Makefile
new file mode 100644
index 0000000..48dab58
--- /dev/null
+++ b/usr.sbin/sicontrol/Makefile
@@ -0,0 +1,5 @@
+PROG= sicontrol
+SRCS= sicontrol.c
+MAN8= sicontrol.8
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/sicontrol/sicontrol.8 b/usr.sbin/sicontrol/sicontrol.8
new file mode 100644
index 0000000..5443a1b
--- /dev/null
+++ b/usr.sbin/sicontrol/sicontrol.8
@@ -0,0 +1,114 @@
+.\" $Id: sicontrol.8,v 1.6 1997/02/22 16:13:30 peter Exp $
+.\" The following requests are required for all man pages.
+.Dd September 26,1995
+.Dt SICONTROL 8
+.Os FreeBSD
+.Sh NAME
+.Nm sicontrol
+.Nd Specialix SI/XIO driver configuration and debugging
+.Sh SYNOPSIS
+.Nm sicontrol
+device
+.Ar command Op Cm Ar param ...
+.Sh DESCRIPTION
+.Nm Sicontrol
+is used to configure and monitor the SI/XIO device driver.
+.Pp
+.Nm Sicontrol
+operates on the specified
+.Ar device
+to indicate which port is to be used.
+.Pp
+The special
+.Ar device
+string `-' is used to indicate the global driver settings instead.
+.Pp
+A '/dev/' is included if necessary.
+.Pp
+The following commands are used for the global settings and should be
+specified with the '-' device name.
+.Bl -tag -width 4n
+.It Cm int_throttle Op Cm value
+Configure the `aggregate interrupt throttle value'.
+The maximum number of host adapter interrupts per second is determined by:
+.Pp
+.Ar "controller CPU clock / (8 * int_throttle)"
+.Pp
+The default value at boot time is 25000. The host adapter cpu clock is
+25Mhz. This gives a maximum interrupt rate of about 125 interrupts per
+second.
+.Pp
+Lowering this value will increase the rate in which the host adapter can
+interrupt the operating system for attention.
+.\"
+.It Cm rxint_throttle Op Cm value
+Configure the receiver interrupt throttle value.
+The default value of 4 at boot time allows an interrupt rate of
+approximately 25.
+.Pp
+Lowering this value will increase the rate in which the host adapter can
+interrupt the operating system to empty the receiver fifos.
+.\"
+.It Cm nport
+Return the number of ports under the control of the device driver.
+.El
+.Pp
+The following commands are used for the individual ports and should be
+specified with a device name from
+.Pa /dev :
+.Bl -tag -width 4n
+.It Cm mstate
+Show the current incoming modem control signals.
+.It Cm ccbstat
+Show the current "ccb" structure for the specified port. This is not of
+much use outside of debugging the driver and determining why a port is
+wedged.
+.It Cm ttystat
+Show the current "tty" structure that the kernel has for the specified port.
+This is not much use outside of debugging the driver.
+.El
+.\" The following requests should be uncommented and used where appropriate.
+.\" This next request is for sections 2 and 3 function return values only.
+.\" .Sh RETURN VALUES
+.\" This next request is for sections 1, 6, 7 & 8 only
+.\" .Sh ENVIRONMENT
+.Sh FILES
+.Bl -tag -width /dev/si_control -compact
+.It Pa /dev/si_control
+global driver control file for use by
+.Xr sicontrol 8 .
+.It Pa /dev/ttyA*
+terminal control ports
+.It Pa /dev/ttyiA*
+initial termios state devices, for use by
+.Xr stty 1
+.It Pa /dev/ttylA*
+locked termios state devices, for use by
+.Xr stty 1
+.El
+.\" .Sh EXAMPLES
+.\" This next request is for sections 1, 6, 7 & 8 only
+.\" (command return values (to shell) and fprintf/stderr type diagnostics)
+.Sh DIAGNOSTICS
+Generally self explanatory.....
+.\" The next request is for sections 2 and 3 error and signal handling only.
+.\" .Sh ERRORS
+.Sh SEE ALSO
+.Xr stty 1 ,
+.Xr si 4 ,
+.Xr termios 4 ,
+.Xr tty 4 ,
+.Xr comcontrol 8 .
+.\" .Sh STANDARDS
+.Sh HISTORY
+.Nm Sicontrol
+is loosely based on a utility called
+.Nm siconfig
+which was written by
+.An Andy Rutter Aq andy@acronym.co.uk
+.Pp
+Specialix International do not support this device driver in any way.
+.Sh AUTHORS
+.An Peter Wemm Aq peter@freebsd.org
+.Sh BUGS
+Bound to be many... :-)
diff --git a/usr.sbin/sicontrol/sicontrol.c b/usr.sbin/sicontrol/sicontrol.c
new file mode 100644
index 0000000..eb93819
--- /dev/null
+++ b/usr.sbin/sicontrol/sicontrol.c
@@ -0,0 +1,585 @@
+/*
+ * Device driver for Specialix range (SLXOS) of serial line multiplexors.
+ * SLXOS configuration and debug interface
+ *
+ * Copyright (C) 1990, 1992 Specialix International,
+ * Copyright (C) 1993, Andy Rutter <andy@acronym.co.uk>
+ * Copyright (C) 1995, Peter Wemm <peter@haywire.dialix.com>
+ *
+ * Derived from: SunOS 4.x version
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notices, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notices, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Andy Rutter of
+ * Advanced Methods and Tools Ltd. based on original information
+ * from Specialix International.
+ * 4. Neither the name of Advanced Methods and Tools, nor Specialix
+ * International may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
+ * NO EVENT SHALL THE AUTHORS BE LIABLE.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#include <ctype.h>
+#include <err.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <sys/device.h>
+#include <sys/tty.h>
+
+#include <machine/si.h>
+
+struct lv {
+ char *lv_name;
+ int lv_bit;
+} lv[] = {
+ {"entry", DBG_ENTRY},
+ {"open", DBG_OPEN},
+ {"close", DBG_CLOSE},
+ {"read", DBG_READ},
+ {"write", DBG_WRITE},
+ {"param", DBG_PARAM},
+ {"modem", DBG_MODEM},
+ {"select", DBG_SELECT},
+ {"optim", DBG_OPTIM},
+ {"intr", DBG_INTR},
+ {"start", DBG_START},
+ {"lstart", DBG_LSTART},
+ {"ioctl", DBG_IOCTL},
+ {"fail", DBG_FAIL},
+ {"autoboot", DBG_AUTOBOOT},
+ {"download", DBG_DOWNLOAD},
+ {"drain", DBG_DRAIN},
+ {"poll", DBG_POLL},
+ {0, 0}
+};
+
+static int alldev = 0;
+
+void ccb_stat __P((int, char **));
+void debug __P((int, char **));
+void dostat __P((void));
+int getnum __P((char *));
+int islevel __P((char *));
+int lvls2bits __P((char *));
+void mstate __P((int, char **));
+void nport __P((int, char **));
+void onoff __P((int, char **, int, char *, char *, int));
+int opencontrol __P((void));
+void prlevels __P((int));
+void prusage __P((int, int));
+void rxint __P((int, char **));
+void tty_stat __P((int, char **));
+void txint __P((int, char **));
+
+struct opt {
+ char *o_name;
+ void (*o_func)();
+} opt[] = {
+ {"debug", debug},
+ {"rxint_throttle", rxint},
+ {"int_throttle", txint},
+ {"nport", nport},
+ {"mstate", mstate},
+ {"ccbstat", ccb_stat},
+ {"ttystat", tty_stat},
+ {0, 0}
+};
+
+struct stat_list {
+ void (*st_func)();
+} stat_list[] = {
+ {mstate},
+ {0}
+};
+
+#define U_DEBUG 0
+#define U_TXINT 1
+#define U_RXINT 2
+#define U_NPORT 3
+#define U_MSTATE 4
+#define U_STAT_CCB 5
+#define U_STAT_TTY 6
+
+#define U_MAX 7
+#define U_ALL -1
+char *usage[] = {
+ "debug [[add|del|set debug_levels] | [off]]\n",
+ "int_throttle [newvalue]\n",
+ "rxint_throttle [newvalue]\n",
+ "nport\n",
+ "mstate\n",
+ "ccbstat\n",
+ "ttystat\n",
+ 0
+};
+
+int ctlfd;
+char *Devname;
+struct si_tcsi tc;
+
+int
+main(argc, argv)
+ char **argv;
+{
+ struct opt *op;
+ void (*func)() = NULL;
+
+ if (argc < 2)
+ prusage(U_ALL, 1);
+ Devname = argv[1];
+ if (strcmp(Devname, "-") == 0) {
+ alldev = 1;
+ } else {
+ sidev_t dev;
+ struct stat st;
+
+ if (strchr(Devname, '/') == NULL) {
+ char *acp = malloc(6 + strlen(Devname));
+ strcpy(acp, "/dev/");
+ strcat(acp, Devname);
+ Devname = acp;
+ }
+ if (stat(Devname, &st) < 0)
+ errx(1, "can't stat %s", Devname);
+ dev.sid_card = SI_CARD(minor(st.st_rdev));
+ dev.sid_port = SI_PORT(minor(st.st_rdev));
+ tc.tc_dev = dev;
+ }
+ ctlfd = opencontrol();
+ if (argc == 2) {
+ dostat();
+ exit(0);
+ }
+
+ argc--; argv++;
+ for (op = opt; op->o_name; op++) {
+ if (strcmp(argv[1], op->o_name) == 0) {
+ func = op->o_func;
+ break;
+ }
+ }
+ if (func == NULL)
+ prusage(U_ALL, 1);
+
+ argc -= 2;
+ argv += 2;
+ (*func)(argc, argv);
+ exit(0);
+}
+
+int
+opencontrol()
+{
+ int fd;
+
+ fd = open(CONTROLDEV, O_RDWR|O_NDELAY);
+ if (fd < 0)
+ err(1, "open on %s", CONTROLDEV);
+ return(fd);
+}
+
+/*
+ * Print a usage message - this relies on U_DEBUG==0 and U_BOOT==1.
+ * Don't print the DEBUG usage string unless explicity requested.
+ */
+void
+prusage(strn, eflag)
+ int strn, eflag;
+{
+ char **cp;
+
+ if (strn == U_ALL) {
+ fprintf(stderr, "usage: sicontrol %s", usage[1]);
+ fprintf(stderr, " sicontrol %s", usage[2]);
+ fprintf(stderr, " sicontrol %s", usage[3]);
+ fprintf(stderr, " sicontrol devname %s", usage[4]);
+ for (cp = &usage[5]; *cp; cp++)
+ fprintf(stderr, " sicontrol devname %s", *cp);
+ }
+ else if (strn >= 0 && strn <= U_MAX)
+ fprintf(stderr, "usage: sicontrol devname %s", usage[strn]);
+ else
+ fprintf(stderr, "sicontrol: usage ???\n");
+ exit(eflag);
+}
+
+/* print port status */
+void
+dostat()
+{
+ char *av[1], *acp;
+ struct stat_list *stp;
+ struct si_tcsi stc;
+ int donefirst = 0;
+
+ printf("%s: ", alldev ? "ALL" : Devname);
+ acp = malloc(strlen(Devname) + 3);
+ memset(acp, ' ', strlen(Devname));
+ strcat(acp, " ");
+ stc = tc;
+ for (stp = stat_list; stp->st_func != NULL; stp++) {
+ if (donefirst)
+ fputs(acp, stdout);
+ else
+ donefirst++;
+ av[0] = NULL;
+ tc = stc;
+ (*stp->st_func)(-1, av);
+ }
+}
+
+/*
+ * debug
+ * debug [[set|add|del debug_lvls] | [off]]
+ */
+void
+debug(ac, av)
+ char **av;
+{
+ int level;
+
+ if (ac > 2)
+ prusage(U_DEBUG, 1);
+ if (alldev) {
+ if (ioctl(ctlfd, TCSIGDBG_ALL, &tc.tc_dbglvl) < 0)
+ err(1, "TCSIGDBG_ALL on %s", Devname);
+ } else {
+ if (ioctl(ctlfd, TCSIGDBG_LEVEL, &tc) < 0)
+ err(1, "TCSIGDBG_LEVEL on %s", Devname);
+ }
+
+ switch (ac) {
+ case 0:
+ printf("%s: debug levels - ", Devname);
+ prlevels(tc.tc_dbglvl);
+ return;
+ case 1:
+ if (strcmp(av[0], "off") == 0) {
+ tc.tc_dbglvl = 0;
+ break;
+ }
+ prusage(U_DEBUG, 1);
+ /* no return */
+ case 2:
+ level = lvls2bits(av[1]);
+ if (strcmp(av[0], "add") == 0)
+ tc.tc_dbglvl |= level;
+ else if (strcmp(av[0], "del") == 0)
+ tc.tc_dbglvl &= ~level;
+ else if (strcmp(av[0], "set") == 0)
+ tc.tc_dbglvl = level;
+ else
+ prusage(U_DEBUG, 1);
+ }
+ if (alldev) {
+ if (ioctl(ctlfd, TCSISDBG_ALL, &tc.tc_dbglvl) < 0)
+ err(1, "TCSISDBG_ALL on %s", Devname);
+ } else {
+ if (ioctl(ctlfd, TCSISDBG_LEVEL, &tc) < 0)
+ err(1, "TCSISDBG_LEVEL on %s", Devname);
+ }
+}
+
+void
+rxint(ac, av)
+ char **av;
+{
+ tc.tc_port = 0;
+ switch (ac) {
+ case 0:
+ printf("%s: ", Devname);
+ case -1:
+ if (ioctl(ctlfd, TCSIGRXIT, &tc) < 0)
+ err(1, "TCSIGRXIT");
+ printf("RX interrupt throttle: %d msec\n", tc.tc_int*10);
+ break;
+ case 1:
+ tc.tc_int = getnum(av[0]) / 10;
+ if (tc.tc_int == 0)
+ tc.tc_int = 1;
+ if (ioctl(ctlfd, TCSIRXIT, &tc) < 0)
+ err(1, "TCSIRXIT on %s at %d msec",
+ Devname, tc.tc_int*10);
+ break;
+ default:
+ prusage(U_RXINT, 1);
+ }
+}
+
+void
+txint(ac, av)
+ char **av;
+{
+
+ tc.tc_port = 0;
+ switch (ac) {
+ case 0:
+ printf("%s: ", Devname);
+ case -1:
+ if (ioctl(ctlfd, TCSIGIT, &tc) < 0)
+ err(1, "TCSIGIT");
+ printf("aggregate interrupt throttle: %d\n", tc.tc_int);
+ break;
+ case 1:
+ tc.tc_int = getnum(av[0]);
+ if (ioctl(ctlfd, TCSIIT, &tc) < 0)
+ err(1, "TCSIIT on %s at %d", Devname, tc.tc_int);
+ break;
+ default:
+ prusage(U_TXINT, 1);
+ }
+}
+
+void
+onoff(ac, av, cmd, cmdstr, prstr, usage)
+ char **av, *cmdstr, *prstr;
+ int ac, cmd, usage;
+{
+ if (ac > 1)
+ prusage(usage, 1);
+ if (ac == 1) {
+ if (strcmp(av[0], "on") == 0)
+ tc.tc_int = 1;
+ else if (strcmp(av[0], "off") == 0)
+ tc.tc_int = 0;
+ else
+ prusage(usage, 1);
+ } else
+ tc.tc_int = -1;
+ if (ioctl(ctlfd, cmd, &tc) < 0)
+ err(1, "%s on %s", cmdstr, Devname);
+ switch (ac) {
+ case 0:
+ printf("%s: ", Devname);
+ case -1:
+ printf("%s ", prstr);
+ if (tc.tc_int)
+ printf("on\n");
+ else
+ printf("off\n");
+ }
+}
+
+void
+mstate(ac, av)
+ char **av;
+{
+ switch (ac) {
+ case 0:
+ printf("%s: ", Devname);
+ case -1:
+ break;
+ default:
+ prusage(U_MSTATE, 1);
+ }
+ if (ioctl(ctlfd, TCSISTATE, &tc) < 0)
+ err(1, "TCSISTATE on %s", Devname);
+ printf("modem bits state - (0x%x)", tc.tc_int);
+ if (tc.tc_int & IP_DCD) printf(" DCD");
+ if (tc.tc_int & IP_DTR) printf(" DTR");
+ if (tc.tc_int & IP_RTS) printf(" RTS");
+ printf("\n");
+}
+
+void
+nport(ac, av)
+ char **av;
+{
+ int ports;
+
+ if (ac != 0)
+ prusage(U_NPORT, 1);
+ if (ioctl(ctlfd, TCSIPORTS, &ports) < 0)
+ err(1, "TCSIPORTS on %s", Devname);
+ printf("SLXOS: total of %d ports\n", ports);
+}
+
+void
+ccb_stat(ac, av)
+char **av;
+{
+ struct si_pstat sip;
+#define CCB sip.tc_ccb
+
+ if (ac != 0)
+ prusage(U_STAT_CCB, 1);
+ sip.tc_dev = tc.tc_dev;
+ if (ioctl(ctlfd, TCSI_CCB, &sip) < 0)
+ err(1, "TCSI_CCB on %s", Devname);
+ printf("%s: ", Devname);
+
+ /* WORD next - Next Channel */
+ /* WORD addr_uart - Uart address */
+ /* WORD module - address of module struct */
+ printf("\tuart_type 0x%x\n", CCB.type); /* BYTE type - Uart type */
+ /* BYTE fill - */
+ printf("\tx_status 0x%x\n", CCB.x_status); /* BYTE x_status - XON / XOFF status */
+ printf("\tc_status 0x%x\n", CCB.c_status); /* BYTE c_status - cooking status */
+ printf("\thi_rxipos 0x%x\n", CCB.hi_rxipos); /* BYTE hi_rxipos - stuff into rx buff */
+ printf("\thi_rxopos 0x%x\n", CCB.hi_rxopos); /* BYTE hi_rxopos - stuff out of rx buffer */
+ printf("\thi_txopos 0x%x\n", CCB.hi_txopos); /* BYTE hi_txopos - Stuff into tx ptr */
+ printf("\thi_txipos 0x%x\n", CCB.hi_txipos); /* BYTE hi_txipos - ditto out */
+ printf("\thi_stat 0x%x\n", CCB.hi_stat); /* BYTE hi_stat - Command register */
+ printf("\tdsr_bit 0x%x\n", CCB.dsr_bit); /* BYTE dsr_bit - Magic bit for DSR */
+ printf("\ttxon 0x%x\n", CCB.txon); /* BYTE txon - TX XON char */
+ printf("\ttxoff 0x%x\n", CCB.txoff); /* BYTE txoff - ditto XOFF */
+ printf("\trxon 0x%x\n", CCB.rxon); /* BYTE rxon - RX XON char */
+ printf("\trxoff 0x%x\n", CCB.rxoff); /* BYTE rxoff - ditto XOFF */
+ printf("\thi_mr1 0x%x\n", CCB.hi_mr1); /* BYTE hi_mr1 - mode 1 image */
+ printf("\thi_mr2 0x%x\n", CCB.hi_mr2); /* BYTE hi_mr2 - mode 2 image */
+ printf("\thi_csr 0x%x\n", CCB.hi_csr); /* BYTE hi_csr - clock register */
+ printf("\thi_op 0x%x\n", CCB.hi_op); /* BYTE hi_op - Op control */
+ printf("\thi_ip 0x%x\n", CCB.hi_ip); /* BYTE hi_ip - Input pins */
+ printf("\thi_state 0x%x\n", CCB.hi_state); /* BYTE hi_state - status */
+ printf("\thi_prtcl 0x%x\n", CCB.hi_prtcl); /* BYTE hi_prtcl - Protocol */
+ printf("\thi_txon 0x%x\n", CCB.hi_txon); /* BYTE hi_txon - host copy tx xon stuff */
+ printf("\thi_txoff 0x%x\n", CCB.hi_txoff); /* BYTE hi_txoff - */
+ printf("\thi_rxon 0x%x\n", CCB.hi_rxon); /* BYTE hi_rxon - */
+ printf("\thi_rxoff 0x%x\n", CCB.hi_rxoff); /* BYTE hi_rxoff - */
+ printf("\tclose_prev 0x%x\n", CCB.close_prev); /* BYTE close_prev - Was channel previously closed */
+ printf("\thi_break 0x%x\n", CCB.hi_break); /* BYTE hi_break - host copy break process */
+ printf("\tbreak_state 0x%x\n", CCB.break_state); /* BYTE break_state - local copy ditto */
+ printf("\thi_mask 0x%x\n", CCB.hi_mask); /* BYTE hi_mask - Mask for CS7 etc. */
+ printf("\tmask_z280 0x%x\n", CCB.mask_z280); /* BYTE mask_z280 - Z280's copy */
+ /* BYTE res[0x60 - 36] - */
+ /* BYTE hi_txbuf[SLXOS_BUFFERSIZE] - */
+ /* BYTE hi_rxbuf[SLXOS_BUFFERSIZE] - */
+ /* BYTE res1[0xA0] - */
+}
+
+void
+tty_stat(ac, av)
+char **av;
+{
+ struct si_pstat sip;
+#define TTY sip.tc_tty
+
+ if (ac != 0)
+ prusage(U_STAT_TTY, 1);
+ sip.tc_dev = tc.tc_dev;
+ if (ioctl(ctlfd, TCSI_TTY, &sip) < 0)
+ err(1, "TCSI_TTY on %s", Devname);
+ printf("%s: ", Devname);
+
+ printf("\tt_outq.c_cc %d.\n", TTY.t_outq.c_cc); /* struct clist t_outq */
+ printf("\tt_dev 0x%x\n", TTY.t_dev); /* dev_t t_dev */
+ printf("\tt_flags 0x%x\n", TTY.t_flags); /* int t_flags */
+ printf("\tt_state 0x%x\n", TTY.t_state); /* int t_state */
+ printf("\tt_hiwat %d.\n", TTY.t_hiwat); /* short t_hiwat */
+ printf("\tt_lowat %d.\n", TTY.t_lowat); /* short t_lowat */
+ printf("\tt_iflag 0x%x\n", TTY.t_iflag); /* t_iflag */
+ printf("\tt_oflag 0x%x\n", TTY.t_oflag); /* t_oflag */
+ printf("\tt_cflag 0x%x\n", TTY.t_cflag); /* t_cflag */
+ printf("\tt_lflag 0x%x\n", TTY.t_lflag); /* t_lflag */
+ printf("\tt_cc 0x%x\n", TTY.t_cc); /* t_cc */
+ printf("\tt_termios.c_ispeed 0x%x\n", TTY.t_termios.c_ispeed); /* t_termios.c_ispeed */
+ printf("\tt_termios.c_ospeed 0x%x\n", TTY.t_termios.c_ospeed); /* t_termios.c_ospeed */
+}
+
+int
+islevel(tk)
+ char *tk;
+{
+ register struct lv *lvp;
+ register char *acp;
+
+ for (acp = tk; *acp; acp++)
+ if (isupper(*acp))
+ *acp = tolower(*acp);
+ for (lvp = lv; lvp->lv_name; lvp++)
+ if (strcmp(lvp->lv_name, tk) == 0)
+ return(lvp->lv_bit);
+ return(0);
+}
+
+/*
+ * Convert a string consisting of tokens separated by white space, commas
+ * or `|' into a bitfield - flag any unrecognised tokens.
+ */
+int
+lvls2bits(str)
+ char *str;
+{
+ int i, bits = 0;
+ int errflag = 0;
+ char token[20];
+
+ while (sscanf(str, "%[^,| \t]", token) == 1) {
+ str += strlen(token);
+ while (isspace(*str) || *str==',' || *str=='|')
+ str++;
+ if (strcmp(token, "all") == 0)
+ return(0xffffffff);
+ if ((i = islevel(token)) == 0) {
+ warnx("unknown token '%s'", token);
+ errflag++;
+ } else
+ bits |= i;
+ }
+ if (errflag)
+ exit(1);
+
+ return(bits);
+}
+
+int
+getnum(char *str)
+{
+ int x;
+ register char *acp = str;
+
+ x = 0;
+ while (*acp) {
+ if (!isdigit(*acp))
+ errx(1, "%s is not a number", str);
+ x *= 10;
+ x += (*acp - '0');
+ acp++;
+ }
+ return(x);
+}
+
+void
+prlevels(x)
+ int x;
+{
+ struct lv *lvp;
+
+ switch (x) {
+ case 0:
+ printf("(none)\n");
+ break;
+ case 0xffffffff:
+ printf("all\n");
+ break;
+ default:
+ for (lvp = lv; lvp->lv_name; lvp++)
+ if (x & lvp->lv_bit)
+ printf(" %s", lvp->lv_name);
+ printf("\n");
+ }
+}
+
diff --git a/usr.sbin/sliplogin/Makefile b/usr.sbin/sliplogin/Makefile
new file mode 100644
index 0000000..d8efa9b
--- /dev/null
+++ b/usr.sbin/sliplogin/Makefile
@@ -0,0 +1,10 @@
+# @(#)Makefile 8.1 (Berkeley) 7/19/93
+
+PROG= sliplogin
+MAN8= 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..3bee951
--- /dev/null
+++ b/usr.sbin/sliplogin/pathnames.h
@@ -0,0 +1,47 @@
+/*-
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)pathnames.h 8.1 (Berkeley) 6/6/93
+ */
+
+#ifndef COMPAT
+#include <paths.h>
+#else
+#define _PATH_DEVNULL "/dev/null"
+#endif
+
+#define _PATH_ACCESS "/etc/sliphome/slip.hosts"
+#define _PATH_SLPARMS "/etc/sliphome/slip.slparms"
+#define _PATH_LOGIN "/etc/sliphome/slip.login"
+#define _PATH_LOGOUT "/etc/sliphome/slip.logout"
+#define _PATH_DEBUG "/tmp/sliplogin.XXXXXX"
+
diff --git a/usr.sbin/sliplogin/sliplogin.8 b/usr.sbin/sliplogin/sliplogin.8
new file mode 100644
index 0000000..9d10175
--- /dev/null
+++ b/usr.sbin/sliplogin/sliplogin.8
@@ -0,0 +1,313 @@
+.\" Copyright (c) 1990, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)sliplogin.8 8.2 (Berkeley) 1/5/94
+.\"
+.Dd January 5, 1994
+.Dt SLIPLOGIN 8
+.Os
+.Sh NAME
+.Nm sliplogin
+.Nd attach a serial line network interface
+.Sh SYNOPSIS
+.Nm
+.Op Ar loginname Op Ar device
+.Sh DESCRIPTION
+.Nm Sliplogin
+is used to turn the terminal line on standard input (or
+.Ar device )
+into a Serial Line IP
+.Pq Tn SLIP
+link to a remote host. To do this, the program
+searches the file
+.Pa /etc/sliphome/slip.hosts
+for an entry matching
+.Ar loginname
+(which defaults to the current login name if omitted).
+If a matching entry is found, the line is configured appropriately
+for slip (8-bit transparent i/o) and converted to
+.Tn SLIP
+line discipline using the optional line discipline parameters.
+.Pp
+The optional line discipline parameters consist of one or more of
+the following;
+.Sq normal ,
+.Sq compress ,
+.Sq noicmp ,
+or
+.Sq autocomp
+which correspond respectively to
+.Sq use normal line discipline
+(no header compression),
+.Sq enable VJ header compression ,
+.Sq throw away ICMP packets ,
+and
+.Sq auto enable VJ header compression
+(only if the remote end of the link also supports it).
+.Pp
+Then a shell script is invoked to initialize the slip
+interface with the appropriate local and remote
+.Tn IP
+address,
+netmask, etc.
+.Pp
+The usual initialization script is
+.Pa /etc/sliphome/slip.login
+but, if particular hosts need special initialization, the file
+.Pa /etc/sliphome/slip.login. Ns Ar loginname
+will be executed instead if it exists.
+The script is invoked with the parameters
+.Bl -tag -width slipunit
+.It Em slipunit
+The unit number of the slip interface assigned to this line. E.g.,
+.Sy 0
+for
+.Sy sl0 .
+.It Em speed
+The speed of the line.
+.It Em args
+The arguments from the
+.Pa /etc/sliphome/slip.hosts
+entry, in order starting with
+.Ar loginname .
+.El
+.Pp
+Only the super-user may attach a network interface. The interface is
+automatically detached when the other end hangs up or the
+.Nm
+process dies. If the kernel slip
+module has been configured for it, all routes through that interface will
+also disappear at the same time. If there is other processing a site
+would like done on hangup, the file
+.Pa /etc/sliphome/slip.logout
+or
+.Pa /etc/sliphome/slip.logout. Ns Ar loginname
+is executed if it exists. It is given the same arguments as the login script.
+.Ss Format of /etc/sliphome/slip.hosts
+Comments (lines starting with a `#') and blank lines (or started
+with space) are ignored.
+Other lines must start with a
+.Ar loginname
+but the remaining arguments can be whatever is appropriate for the
+.Pa slip.login
+file that will be executed for that name.
+Arguments are separated by white space and follow normal
+.Xr sh 1
+quoting conventions (however,
+.Ar loginname
+cannot be quoted).
+Usually, lines have the form
+.Bd -literal -offset indent
+loginname local-address remote-address netmask opt-args
+.Ed
+.Pp
+where
+.Em local-address
+and
+.Em remote-address
+are the IP host names or addresses of the local and remote ends of the
+slip line and
+.Em netmask
+is the appropriate IP netmask. These arguments are passed
+directly to
+.Xr ifconfig 8 .
+.Em Opt-args
+are optional arguments used to configure the line.
+.Pp
+.Sh FreeBSD Additions
+An additional SLIP configuration file (if present) is
+.Pa /etc/sliphome/slip.slparms .
+If particular hosts need different configurations, the file
+.Pa /etc/sliphome/slip.slparms. Ns Ar loginname
+will be parsed instead if it exists.
+.Ss Format of /etc/sliphome/slip.slparms*
+Comments (lines starting with a `#') and blank lines (or started with
+space) are ignored.
+This file contains from one to three numeric parameters separated with spaces,
+in order:
+.Ar keepalive ,
+.Ar outfill
+and
+.Ar slunit .
+.Bl -tag -width keepalive
+.It Ar keepalive
+Set SLIP "keep alive" timeout in seconds. If FRAME_END is not received in
+this amount of time,
+.Nm
+closes the line and exits.
+The default value is no timeout (zero).
+.It Ar outfill
+Set SLIP "out fill" timeout in seconds. It forces at least one FRAME_END
+to be sent during this time period, which is necessary for the "keep alive"
+timeout on the remote side.
+The default value is no timeout (zero).
+.It Ar slunit
+Set the SLIP unit number directly. Use with caution, because no check is made
+for two interfaces with same number.
+By default sliplogin dynamically assigns the unit number.
+.El
+.Pp
+If latter two parameters are omitted, they will not affect the
+corresponding SLIP configuration.
+If any of first two parameters is equal to zero, it will not affect
+the corresponding SLIP configuration.
+.Sh EXAMPLE
+The normal use of
+.Nm
+is to create a
+.Pa /etc/passwd
+entry for each legal, remote slip site with
+.Nm
+as the shell for that entry. E.g.,
+.Bd -literal
+Sfoo:ikhuy6:2010:1:slip line to foo:/tmp:/usr/sbin/sliplogin
+.Ed
+.Pp
+(Our convention is to name the account used by remote host
+.Ar hostname
+as
+.Em Shostname . )
+Then an entry is added to
+.Pa slip.hosts
+that looks like:
+.Pp
+.Bd -literal -offset indent -compact
+Sfoo `hostname` foo netmask
+.Ed
+.Pp
+where
+.Em `hostname`
+will be evaluated by
+.Xr sh
+to the local host name and
+.Em netmask
+is the local host IP netmask.
+.Pp
+Note that
+.Nm
+must be setuid to root and, while not a security hole, moral defectives
+can use it to place terminal lines in an unusable state and/or deny
+access to legitimate users of a remote slip line. To prevent this,
+.Nm
+is installed as user
+.Em root ,
+group
+.Em network
+and mode 4550 so that only members of group
+.Em network
+may run
+.Nm sliplogin .
+The system administrator should make sure that all legitimate users
+are a member of the correct group.
+.Sh DIAGNOSTICS
+.Nm Sliplogin
+logs various information to the system log daemon,
+.Xr syslogd 8 ,
+with a facility code of
+.Em daemon .
+The messages are listed here, grouped by severity level.
+.Pp
+.Sy Error Severity
+.Bl -tag -width Ds -compact
+.It Sy ioctl (TCGETS): Em reason
+A
+.Dv TCGETS
+.Fn ioctl
+to get the line parameters failed.
+.Pp
+.It Sy ioctl (TCSETS): Em reason
+A
+.Dv TCSETS
+.Fn ioctl
+to set the line parameters failed.
+.Pp
+.It Sy /etc/sliphome/slip.hosts: Em reason
+The
+.Pa /etc/sliphome/slip.hosts
+file could not be opened.
+.Pp
+.It Sy access denied for Em user
+No entry for
+.Em user
+was found in
+.Pa /etc/sliphome/slip.hosts .
+.El
+.Pp
+.Sy Notice Severity
+.Bl -tag -width Ds -compact
+.It Sy "attaching slip unit" Em unit Sy for Ar loginname
+.Tn SLIP
+unit
+.Em unit
+was successfully attached.
+.El
+.Sh FILES
+.Ar /etc/sliphome/slip.hosts
+list of host login names and parameters.
+.Pp
+.Ar /etc/sliphome/slip.login
+script executed when a connection is made.
+.Pp
+.Ar /etc/sliphome/slip.login.loginname
+script executed when a connection is made by
+.Ar loginname .
+.Pp
+.Ar /etc/sliphome/slip.logout
+script executed when a connection is lost.
+.Pp
+.Ar /etc/sliphome/slip.logout.loginname
+script executed when a connection is lost by
+.Ar loginname .
+.Pp
+.Ar /etc/sliphome/slip.slparms
+extra parameters file.
+.Pp
+.Ar /etc/sliphome/slip.slparms.loginname
+extra parameters file for
+.Ar loginname .
+.Pp
+.Ar /var/run/ttyXn.if
+contains the name of the network interface used by the sliplogin process on
+.Ar ttyXn .
+.Pp
+.Ar /var/run/slX.pid
+contains the PID of the sliplogin process which is using interface
+.Ar slX .
+.Pp
+.Sh SEE ALSO
+.Xr slattach 8 ,
+.Xr syslogd 8 ,
+.Pa /usr/share/examples/sliplogin
+.Sh HISTORY
+The
+.Nm
+command
+.Bt
diff --git a/usr.sbin/sliplogin/sliplogin.c b/usr.sbin/sliplogin/sliplogin.c
new file mode 100644
index 0000000..e8c2f0c
--- /dev/null
+++ b/usr.sbin/sliplogin/sliplogin.c
@@ -0,0 +1,543 @@
+/*-
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static char copyright[] =
+"@(#) Copyright (c) 1990, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+static char sccsid[] = "@(#)sliplogin.c 8.2 (Berkeley) 2/1/94";
+#endif /* not lint */
+
+/*
+ * sliplogin.c
+ * [MUST BE RUN SUID, SLOPEN DOES A SUSER()!]
+ *
+ * This program initializes its own tty port to be an async TCP/IP interface.
+ * It sets the line discipline to slip, invokes a shell script to initialize
+ * the network interface, then pauses forever waiting for hangup.
+ *
+ * It is a remote descendant of several similar programs with incestuous ties:
+ * - Kirk Smith's slipconf, modified by Richard Johnsson @ DEC WRL.
+ * - slattach, probably by Rick Adams but touched by countless hordes.
+ * - the original sliplogin for 4.2bsd, Doug Kingston the mover behind it.
+ *
+ * There are two forms of usage:
+ *
+ * "sliplogin"
+ * Invoked simply as "sliplogin", the program looks up the username
+ * in the file /etc/slip.hosts.
+ * If an entry is found, the line on fd0 is configured for SLIP operation
+ * as specified in the file.
+ *
+ * "sliplogin IPhostlogin </dev/ttyb"
+ * Invoked by root with a username, the name is looked up in the
+ * /etc/slip.hosts file and if found fd0 is configured as in case 1.
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <syslog.h>
+#include <netdb.h>
+
+#include <termios.h>
+#include <sys/ioctl.h>
+#include <net/slip.h>
+#include <net/if.h>
+
+#include <stdio.h>
+#include <errno.h>
+#include <ctype.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <signal.h>
+#include "pathnames.h"
+
+extern char **environ;
+
+static char *restricted_environ[] = {
+ "PATH=" _PATH_STDPATH,
+ NULL
+};
+
+int unit;
+int slip_mode;
+speed_t speed;
+int uid;
+int keepal;
+int outfill;
+int slunit;
+char loginargs[BUFSIZ];
+char loginfile[MAXPATHLEN];
+char loginname[BUFSIZ];
+static char raddr[32]; /* remote address */
+char ifname[IFNAMSIZ]; /* interface name */
+static char pidfilename[MAXPATHLEN]; /* name of pid file */
+static char iffilename[MAXPATHLEN]; /* name of if file */
+static pid_t pid; /* our pid */
+
+char *
+make_ipaddr(void)
+{
+static char address[20] ="";
+struct hostent *he;
+unsigned long ipaddr;
+int i;
+
+address[0] = '\0';
+if ((he = gethostbyname(raddr)) != NULL) {
+ ipaddr = ntohl(*(long *)he->h_addr_list[0]);
+ sprintf(address, "%lu.%lu.%lu.%lu",
+ ipaddr >> 24,
+ (ipaddr & 0x00ff0000) >> 16,
+ (ipaddr & 0x0000ff00) >> 8,
+ (ipaddr & 0x000000ff));
+ }
+
+return address;
+}
+
+struct slip_modes {
+ char *sm_name;
+ int sm_or_flag;
+ int sm_and_flag;
+} modes[] = {
+ "normal", 0 , 0 ,
+ "compress", IFF_LINK0, IFF_LINK2,
+ "noicmp", IFF_LINK1, 0 ,
+ "autocomp", IFF_LINK2, IFF_LINK0,
+};
+
+void
+findid(name)
+ char *name;
+{
+ FILE *fp;
+ static char slopt[5][16];
+ static char laddr[16];
+ static char mask[16];
+ char slparmsfile[MAXPATHLEN];
+ char user[16];
+ char buf[128];
+ int i, j, n;
+
+ environ = restricted_environ; /* minimal protection for system() */
+
+ (void)strncpy(loginname, name, sizeof(loginname)-1);
+ loginname[sizeof(loginname)-1] = '\0';
+
+ if ((fp = fopen(_PATH_ACCESS, "r")) == NULL) {
+ accfile_err:
+ syslog(LOG_ERR, "%s: %m\n", _PATH_ACCESS);
+ exit(1);
+ }
+ while (fgets(loginargs, sizeof(loginargs) - 1, fp)) {
+ if (ferror(fp))
+ goto accfile_err;
+ if (loginargs[0] == '#' || isspace(loginargs[0]))
+ continue;
+ n = sscanf(loginargs, "%15s%*[ \t]%15s%*[ \t]%15s%*[ \t]%15s%*[ \t]%15s%*[ \t]%15s%*[ \t]%15s%*[ \t]%15s%*[ \t]%15s\n",
+ user, laddr, raddr, mask, slopt[0], slopt[1],
+ slopt[2], slopt[3], slopt[4]);
+ if (n < 4) {
+ syslog(LOG_ERR, "%s: wrong format\n", _PATH_ACCESS);
+ exit(1);
+ }
+ if (strcmp(user, name) != 0)
+ continue;
+
+ (void) fclose(fp);
+
+ slip_mode = 0;
+ for (i = 0; i < n - 4; i++) {
+ for (j = 0; j < sizeof(modes)/sizeof(struct slip_modes);
+ j++) {
+ if (strcmp(modes[j].sm_name, slopt[i]) == 0) {
+ slip_mode |= (modes[j].sm_or_flag);
+ slip_mode &= ~(modes[j].sm_and_flag);
+ break;
+ }
+ }
+ }
+
+ /*
+ * we've found the guy we're looking for -- see if
+ * there's a login file we can use. First check for
+ * one specific to this host. If none found, try for
+ * a generic one.
+ */
+ (void)snprintf(loginfile, sizeof(loginfile), "%s.%s", _PATH_LOGIN, name);
+ if (access(loginfile, R_OK|X_OK) != 0) {
+ (void)strncpy(loginfile, _PATH_LOGIN, sizeof(loginfile)-1);
+ loginfile[sizeof(loginfile)-1] = '\0';
+ if (access(loginfile, R_OK|X_OK)) {
+ syslog(LOG_ERR,
+ "access denied for %s - no %s\n",
+ name, _PATH_LOGIN);
+ exit(5);
+ }
+ }
+ (void)snprintf(slparmsfile, sizeof(slparmsfile), "%s.%s", _PATH_SLPARMS, name);
+ if (access(slparmsfile, R_OK|X_OK) != 0) {
+ (void)strncpy(slparmsfile, _PATH_SLPARMS, sizeof(slparmsfile)-1);
+ slparmsfile[sizeof(slparmsfile)-1] = '\0';
+ if (access(slparmsfile, R_OK|X_OK))
+ *slparmsfile = '\0';
+ }
+ keepal = outfill = 0;
+ slunit = -1;
+ if (*slparmsfile) {
+ if ((fp = fopen(slparmsfile, "r")) == NULL) {
+ slfile_err:
+ syslog(LOG_ERR, "%s: %m\n", slparmsfile);
+ exit(1);
+ }
+ n = 0;
+ while (fgets(buf, sizeof(buf) - 1, fp) != NULL) {
+ if (ferror(fp))
+ goto slfile_err;
+ if (buf[0] == '#' || isspace(buf[0]))
+ continue;
+ n = sscanf(buf, "%d %d %d", &keepal, &outfill, &slunit);
+ if (n < 1) {
+ slwrong_fmt:
+ syslog(LOG_ERR, "%s: wrong format\n", slparmsfile);
+ exit(1);
+ }
+ (void) fclose(fp);
+ break;
+ }
+ if (n == 0)
+ goto slwrong_fmt;
+ }
+
+ return;
+ }
+ syslog(LOG_ERR, "SLIP access denied for %s\n", name);
+ exit(4);
+ /* NOTREACHED */
+}
+
+char *
+sigstr(s)
+ int s;
+{
+ static char buf[32];
+
+ switch (s) {
+ case SIGHUP: return("HUP");
+ case SIGINT: return("INT");
+ case SIGQUIT: return("QUIT");
+ case SIGILL: return("ILL");
+ case SIGTRAP: return("TRAP");
+ case SIGIOT: return("IOT");
+ case SIGEMT: return("EMT");
+ case SIGFPE: return("FPE");
+ case SIGKILL: return("KILL");
+ case SIGBUS: return("BUS");
+ case SIGSEGV: return("SEGV");
+ case SIGSYS: return("SYS");
+ case SIGPIPE: return("PIPE");
+ case SIGALRM: return("ALRM");
+ case SIGTERM: return("TERM");
+ case SIGURG: return("URG");
+ case SIGSTOP: return("STOP");
+ case SIGTSTP: return("TSTP");
+ case SIGCONT: return("CONT");
+ case SIGCHLD: return("CHLD");
+ case SIGTTIN: return("TTIN");
+ case SIGTTOU: return("TTOU");
+ case SIGIO: return("IO");
+ case SIGXCPU: return("XCPU");
+ case SIGXFSZ: return("XFSZ");
+ case SIGVTALRM: return("VTALRM");
+ case SIGPROF: return("PROF");
+ case SIGWINCH: return("WINCH");
+#ifdef SIGLOST
+ case SIGLOST: return("LOST");
+#endif
+ case SIGUSR1: return("USR1");
+ case SIGUSR2: return("USR2");
+ }
+ (void)snprintf(buf, sizeof(buf), "sig %d", s);
+ return(buf);
+}
+
+void
+hup_handler(s)
+ int s;
+{
+ char logoutfile[MAXPATHLEN];
+
+ (void) close(0);
+ seteuid(0);
+ (void)snprintf(logoutfile, sizeof(logoutfile), "%s.%s", _PATH_LOGOUT, loginname);
+ if (access(logoutfile, R_OK|X_OK) != 0) {
+ (void)strncpy(logoutfile, _PATH_LOGOUT, sizeof(logoutfile)-1);
+ logoutfile[sizeof(logoutfile)-1] = '\0';
+ }
+ if (access(logoutfile, R_OK|X_OK) == 0) {
+ char logincmd[2*MAXPATHLEN+32];
+
+ (void) snprintf(logincmd, sizeof(logincmd), "%s %d %ld %s", logoutfile, unit, speed, loginargs);
+ (void) system(logincmd);
+ }
+ syslog(LOG_INFO, "closed %s slip unit %d (%s)\n", loginname, unit,
+ sigstr(s));
+ if (unlink(pidfilename) < 0 && errno != ENOENT)
+ syslog(LOG_WARNING, "unable to delete pid file: %m");
+ if (unlink(iffilename) < 0 && errno != ENOENT)
+ syslog(LOG_WARNING, "unable to delete if file: %m");
+ exit(1);
+ /* NOTREACHED */
+}
+
+
+/* Modify the slip line mode and add any compression or no-icmp flags. */
+void line_flags(unit)
+ int unit;
+{
+ struct ifreq ifr;
+ int s;
+
+ /* open a socket as the handle to the interface */
+ s = socket(AF_INET, SOCK_DGRAM, 0);
+ if (s < 0) {
+ syslog(LOG_ERR, "socket: %m");
+ exit(1);
+ }
+ sprintf(ifr.ifr_name, "sl%d", unit);
+
+ /* get the flags for the interface */
+ if (ioctl(s, SIOCGIFFLAGS, (caddr_t)&ifr) < 0) {
+ syslog(LOG_ERR, "ioctl (SIOCGIFFLAGS): %m");
+ exit(1);
+ }
+
+ /* Assert any compression or no-icmp flags. */
+#define SLMASK (~(IFF_LINK0 | IFF_LINK1 | IFF_LINK2))
+ ifr.ifr_flags &= SLMASK;
+ ifr.ifr_flags |= slip_mode;
+ if (ioctl(s, SIOCSIFFLAGS, (caddr_t)&ifr) < 0) {
+ syslog(LOG_ERR, "ioctl (SIOCSIFFLAGS): %m");
+ exit(1);
+ }
+ close(s);
+}
+
+
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int fd, s, ldisc;
+ char *name;
+ struct termios tios, otios;
+ char logincmd[2*BUFSIZ+32];
+ extern uid_t getuid();
+
+ FILE *pidfile; /* pid file */
+ FILE *iffile; /* interfaces file */
+ char *p;
+ int n;
+ char devnam[MAXPATHLEN] = "/dev/tty"; /* Device name */
+
+ if ((name = strrchr(argv[0], '/')) == NULL)
+ name = argv[0];
+ s = getdtablesize();
+ for (fd = 3 ; fd < s ; fd++)
+ (void) close(fd);
+ openlog(name, LOG_PID|LOG_PERROR, LOG_DAEMON);
+ uid = getuid();
+ if (argc > 1) {
+ findid(argv[1]);
+
+ /*
+ * Disassociate from current controlling terminal, if any,
+ * and ensure that the slip line is our controlling terminal.
+ */
+ if (daemon(1, 1)) {
+ syslog(LOG_ERR, "daemon(1, 1): %m");
+ exit(1);
+ }
+ if (argc > 2) {
+ if ((fd = open(argv[2], O_RDWR)) == -1) {
+ syslog(LOG_ERR, "open %s: %m", argv[2]);
+ exit(2);
+ }
+ (void) dup2(fd, 0);
+ if (fd > 2)
+ close(fd);
+ }
+ if (ioctl(0, TIOCSCTTY, 0) == -1) {
+ syslog(LOG_ERR, "ioctl (TIOCSCTTY): %m");
+ exit(1);
+ }
+ if (tcsetpgrp(0, getpid()) < 0) {
+ syslog(LOG_ERR, "tcsetpgrp failed: %m");
+ exit(1);
+ }
+ } else {
+ if ((name = getlogin()) == NULL) {
+ syslog(LOG_ERR, "access denied - login name not found\n");
+ exit(1);
+ }
+ findid(name);
+ }
+ (void) fchmod(0, 0600);
+ (void) fprintf(stderr, "starting slip login for %s\n", loginname);
+ (void) fprintf(stderr, "your address is %s\n\n", make_ipaddr());
+
+ (void) fflush(stderr);
+ sleep(1);
+
+ /* set up the line parameters */
+ if (tcgetattr(0, &tios) < 0) {
+ syslog(LOG_ERR, "tcgetattr: %m");
+ exit(1);
+ }
+ otios = tios;
+ cfmakeraw(&tios);
+ if (tcsetattr(0, TCSAFLUSH, &tios) < 0) {
+ syslog(LOG_ERR, "tcsetattr: %m");
+ exit(1);
+ }
+ speed = cfgetispeed(&tios);
+
+ ldisc = SLIPDISC;
+ if (ioctl(0, TIOCSETD, &ldisc) < 0) {
+ syslog(LOG_ERR, "ioctl(TIOCSETD): %m");
+ exit(1);
+ }
+ if (slunit >= 0 && ioctl(0, SLIOCSUNIT, &slunit) < 0) {
+ syslog(LOG_ERR, "ioctl (SLIOCSUNIT): %m");
+ exit(1);
+ }
+ /* find out what unit number we were assigned */
+ if (ioctl(0, SLIOCGUNIT, &unit) < 0) {
+ syslog(LOG_ERR, "ioctl (SLIOCGUNIT): %m");
+ exit(1);
+ }
+ (void) signal(SIGHUP, hup_handler);
+ (void) signal(SIGTERM, hup_handler);
+
+ if (keepal > 0) {
+ (void) signal(SIGURG, hup_handler);
+ if (ioctl(0, SLIOCSKEEPAL, &keepal) < 0) {
+ syslog(LOG_ERR, "ioctl(SLIOCSKEEPAL): %m");
+ exit(1);
+ }
+ }
+ if (outfill > 0 && ioctl(0, SLIOCSOUTFILL, &outfill) < 0) {
+ syslog(LOG_ERR, "ioctl(SLIOCSOUTFILL): %m");
+ exit(1);
+ }
+
+ /* write pid to file */
+ pid = getpid();
+ (void) sprintf(ifname, "sl%d", unit);
+ (void) sprintf(pidfilename, "%s%s.pid", _PATH_VARRUN, ifname);
+ if ((pidfile = fopen(pidfilename, "w")) != NULL) {
+ fprintf(pidfile, "%d\n", pid);
+ (void) fclose(pidfile);
+ } else {
+ syslog(LOG_ERR, "Failed to create pid file %s: %m",
+ pidfilename);
+ pidfilename[0] = 0;
+ }
+
+ /* write interface unit number to file */
+ p = ttyname(0);
+ if (p)
+ strcpy(devnam, p);
+ for (n = strlen(devnam); n > 0; n--)
+ if (devnam[n] == '/') {
+ n++;
+ break;
+ }
+ (void) sprintf(iffilename, "%s%s.if", _PATH_VARRUN, &devnam[n]);
+ if ((iffile = fopen(iffilename, "w")) != NULL) {
+ fprintf(iffile, "sl%d\n", unit);
+ (void) fclose(iffile);
+ } else {
+ syslog(LOG_ERR, "Failed to create if file %s: %m", iffilename);
+ iffilename[0] = 0;
+ }
+
+
+ syslog(LOG_INFO, "attaching slip unit %d for %s\n", unit, loginname);
+ (void)snprintf(logincmd, sizeof(logincmd), "%s %d %ld %s", loginfile, unit, speed,
+ loginargs);
+ /*
+ * aim stdout and errout at /dev/null so logincmd output won't
+ * babble into the slip tty line.
+ */
+ (void) close(1);
+ if ((fd = open(_PATH_DEVNULL, O_WRONLY)) != 1) {
+ if (fd < 0) {
+ syslog(LOG_ERR, "open /dev/null: %m");
+ exit(1);
+ }
+ (void) dup2(fd, 1);
+ (void) close(fd);
+ }
+ (void) dup2(1, 2);
+
+ /*
+ * Run login and logout scripts as root (real and effective);
+ * current route(8) is setuid root, and checks the real uid
+ * to see whether changes are allowed (or just "route get").
+ */
+ (void) setuid(0);
+ if (s = system(logincmd)) {
+ syslog(LOG_ERR, "%s login failed: exit status %d from %s",
+ loginname, s, loginfile);
+ exit(6);
+ }
+
+ /* Handle any compression or no-icmp flags. */
+ line_flags(unit);
+
+ /* reset uid to users' to allow the user to give a signal. */
+ seteuid(uid);
+ /* twiddle thumbs until we get a signal */
+ while (1)
+ sigpause(0);
+
+ /* NOTREACHED */
+}
diff --git a/usr.sbin/slstat/Makefile b/usr.sbin/slstat/Makefile
new file mode 100644
index 0000000..a74a30f
--- /dev/null
+++ b/usr.sbin/slstat/Makefile
@@ -0,0 +1,7 @@
+# from: @(#)Makefile 5.6 (Berkeley) 4/23/91
+# $Id$
+
+PROG= slstat
+MAN8= slstat.8
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/slstat/slstat.8 b/usr.sbin/slstat/slstat.8
new file mode 100644
index 0000000..5b45a66
--- /dev/null
+++ b/usr.sbin/slstat/slstat.8
@@ -0,0 +1,128 @@
+.\" Copyright (c) 1986 The Regents of the University of California.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" From: @(#)slstat.8 6.8 (Berkeley) 6/20/91
+.\" $Id: slstat.8,v 1.10 1997/02/22 16:13:33 peter Exp $
+.\"
+.Dd October 11, 1996
+.Dt SLSTAT 8
+.Os BSD 4
+.Sh NAME
+.Nm slstat
+.Nd report serial line IP statistics
+.Sh SYNOPSIS
+.Nm
+.Op Fl i Ar interval
+.Op Fl vr
+.Op Ar unit
+.Sh DESCRIPTION
+The
+.Nm
+utility
+reports certain kernel statistics kept about serial line internet
+protocol traffic.
+.Pp
+The options are as follows:
+.Bl -tag -width indent
+.It Fl i
+Repeat the display indefinitely every
+.Ar interval
+seconds.
+If no
+.Ar interval
+is specified, the default is 5 seconds.
+.It Fl v
+Verbose display of extra fields of information.
+.It Fl r
+Display all values in rate per second that 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..3881421
--- /dev/null
+++ b/usr.sbin/slstat/slstat.c
@@ -0,0 +1,246 @@
+/*
+ * print serial line IP statistics:
+ * slstat [-i interval] [-v] [interface]
+ *
+ * Copyright (c) 1989, 1990, 1991, 1992 Regents of the University of
+ * California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Van Jacobson (van@ee.lbl.gov), Dec 31, 1989:
+ * - Initial distribution.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id: slstat.c,v 1.11 1997/02/22 16:13:34 peter Exp $";
+#endif
+
+#include <sys/param.h>
+#include <sys/mbuf.h>
+#include <sys/socket.h>
+#include <sys/sysctl.h>
+#include <sys/time.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define INET
+
+#include <net/if.h>
+#include <net/if_var.h>
+#include <net/if_mib.h>
+#include <net/if_types.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <net/slcompress.h>
+#include <net/if_slvar.h>
+
+static void usage __P((void));
+static void intpr __P((void));
+static void catchalarm __P((int));
+
+#define INTERFACE_PREFIX "sl%d"
+char interface[IFNAMSIZ];
+
+int rflag;
+int vflag;
+unsigned interval = 5;
+int unit;
+int name[6];
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int c, i;
+ size_t len;
+ int maxifno;
+ int indx;
+ struct ifmibdata ifmd;
+
+ while ((c = getopt(argc, argv, "vri:")) != -1) {
+ switch(c) {
+ case 'v':
+ ++vflag;
+ break;
+ case 'r':
+ ++rflag;
+ break;
+ case 'i':
+ interval = atoi(optarg);
+ if (interval <= 0)
+ usage();
+ break;
+ default:
+ usage();
+ }
+ }
+ if (optind >= argc)
+ sprintf(interface, INTERFACE_PREFIX, unit);
+ else if (isdigit(argv[optind][0])) {
+ unit = atoi(argv[optind]);
+ if (unit < 0)
+ usage();
+ sprintf(interface, INTERFACE_PREFIX, unit);
+ } else if (strncmp(argv[optind], "sl", 2) == 0
+ && isdigit(argv[optind][2])
+ && sscanf(argv[optind], "sl%d", &unit) == 1) {
+ strncpy(interface, argv[optind], IFNAMSIZ);
+ } else
+ usage();
+
+ name[0] = CTL_NET;
+ name[1] = PF_LINK;
+ name[2] = NETLINK_GENERIC;
+ name[3] = IFMIB_SYSTEM;
+ name[4] = IFMIB_IFCOUNT;
+ len = sizeof maxifno;
+ if (sysctl(name, 5, &maxifno, &len, 0, 0) < 0)
+ err(1, "sysctl net.link.generic.system.ifcount");
+
+ name[3] = IFMIB_IFDATA;
+ name[5] = IFDATA_GENERAL;
+ len = sizeof ifmd;
+ for (i = 1; ; i++) {
+ name[4] = i;
+
+ if (sysctl(name, 6, &ifmd, &len, 0, 0) < 0)
+ err(1, "sysctl");
+ if (strncmp(interface, ifmd.ifmd_name, IFNAMSIZ) == 0
+ && ifmd.ifmd_data.ifi_type == IFT_SLIP) {
+ indx = i;
+ break;
+ }
+ if (i >= maxifno)
+ errx(1, "interface %s does not exist", interface);
+ }
+
+ name[4] = indx;
+ name[5] = IFDATA_LINKSPECIFIC;
+ intpr();
+ exit(0);
+}
+
+#define V(offset) ((line % 20)? ((sc->offset - osc->offset) / \
+ (rflag ? interval : 1)) : sc->offset)
+#define AMT (sizeof(*sc) - 2 * sizeof(sc->sc_comp.tstate))
+
+static void
+usage()
+{
+ fprintf(stderr, "usage: slstat [-i interval] [-vr] [unit]\n");
+ exit(1);
+}
+
+u_char signalled; /* set if alarm goes off "early" */
+
+/*
+ * Print a running summary of interface statistics.
+ * Repeat display every interval seconds, showing statistics
+ * collected over that interval. Assumes that interval is non-zero.
+ * First line printed at top of screen is always cumulative.
+ */
+static void
+intpr()
+{
+ register int line = 0;
+ int oldmask;
+ struct sl_softc *sc, *osc;
+ size_t len;
+
+ sc = (struct sl_softc *)malloc(AMT);
+ osc = (struct sl_softc *)malloc(AMT);
+ bzero((char *)osc, AMT);
+ len = AMT;
+
+ while (1) {
+ if (sysctl(name, 6, sc, &len, 0, 0) < 0 &&
+ (errno != ENOMEM || len != AMT))
+ err(1, "sysctl linkspecific");
+
+ (void)signal(SIGALRM, catchalarm);
+ signalled = 0;
+ (void)alarm(interval);
+
+ if ((line % 20) == 0) {
+ printf("%8.8s %6.6s %6.6s %6.6s %6.6s",
+ "in", "pack", "comp", "uncomp", "unknwn");
+ if (vflag)
+ printf(" %6.6s %6.6s %6.6s",
+ "toss", "other", "err");
+ printf(" | %8.8s %6.6s %6.6s %6.6s %6.6s",
+ "out", "pack", "comp", "uncomp", "other");
+ if (vflag)
+ printf(" %6.6s %6.6s %6.6s %6.6s",
+ "search", "miss", "err", "coll");
+ putchar('\n');
+ }
+ printf("%8lu %6ld %6u %6u %6u",
+ V(sc_if.if_ibytes),
+ V(sc_if.if_ipackets),
+ V(sc_comp.sls_compressedin),
+ V(sc_comp.sls_uncompressedin),
+ V(sc_comp.sls_errorin));
+ if (vflag)
+ printf(" %6u %6lu %6lu",
+ V(sc_comp.sls_tossed),
+ V(sc_if.if_ipackets) -
+ V(sc_comp.sls_compressedin) -
+ V(sc_comp.sls_uncompressedin) -
+ V(sc_comp.sls_errorin),
+ V(sc_if.if_ierrors));
+ printf(" | %8lu %6ld %6u %6u %6lu",
+ V(sc_if.if_obytes) / (rflag ? interval : 1),
+ V(sc_if.if_opackets),
+ V(sc_comp.sls_compressed),
+ V(sc_comp.sls_packets) - V(sc_comp.sls_compressed),
+ V(sc_if.if_opackets) - V(sc_comp.sls_packets));
+ if (vflag)
+ printf(" %6u %6u %6lu %6lu",
+ V(sc_comp.sls_searches),
+ V(sc_comp.sls_misses),
+ V(sc_if.if_oerrors),
+ V(sc_if.if_collisions));
+ putchar('\n');
+ fflush(stdout);
+ line++;
+ oldmask = sigblock(sigmask(SIGALRM));
+ if (! signalled) {
+ sigpause(0);
+ }
+ sigsetmask(oldmask);
+ signalled = 0;
+ (void)alarm(interval);
+ bcopy((char *)sc, (char *)osc, AMT);
+ }
+}
+
+/*
+ * Called if an interval expires before sidewaysintpr has completed a loop.
+ * Sets a flag to not wait for the alarm.
+ */
+static void
+catchalarm(sig)
+ int sig;
+{
+ signalled = 1;
+}
diff --git a/usr.sbin/spkrtest/Makefile b/usr.sbin/spkrtest/Makefile
new file mode 100644
index 0000000..8816413
--- /dev/null
+++ b/usr.sbin/spkrtest/Makefile
@@ -0,0 +1,7 @@
+MAN8= spkrtest.8
+
+beforeinstall:
+ ${INSTALL} -c -o ${BINOWN} -g ${BINGRP} -m ${BINMODE} \
+ ${.CURDIR}/spkrtest.pl ${DESTDIR}${BINDIR}/spkrtest
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/spkrtest/spkrtest.8 b/usr.sbin/spkrtest/spkrtest.8
new file mode 100644
index 0000000..3e91132
--- /dev/null
+++ b/usr.sbin/spkrtest/spkrtest.8
@@ -0,0 +1,54 @@
+.\" Copyright (c) May 1995 Wolfram Schneider <wosch@FreeBSD.org>. Berlin.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $Id: spkrtest.8,v 1.6 1997/06/23 04:52:10 steve Exp $
+
+.Dd July 23, 1995
+.Dt SPKRTEST 8
+.Os FreeBSD
+
+.Sh NAME
+.Nm spkrtest
+.Nd test script for the speaker driver
+
+.Sh DESCRIPTION
+.Nm Spkrtest
+is an easy to use test script for the speaker driver.
+
+.Sh FILES
+.Bl -tag -width /dev/speakerxx
+.It Pa /dev/speaker
+speaker device file
+.El
+
+.Sh SEE ALSO
+.Xr dialog 1 ,
+.Xr perl 1 ,
+.Xr spkr 4
+
+.Sh HISTORY
+The
+.Nm
+script appeared in
+.Fx 1.0
diff --git a/usr.sbin/spkrtest/spkrtest.pl b/usr.sbin/spkrtest/spkrtest.pl
new file mode 100644
index 0000000..fa00caa
--- /dev/null
+++ b/usr.sbin/spkrtest/spkrtest.pl
@@ -0,0 +1,137 @@
+#!/usr/bin/perl
+#
+# Copyright (c) May 1995 Wolfram Schneider <wosch@FreeBSD.org>. Berlin.
+# All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 1. Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# 2. Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+#
+# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+# SUCH DAMAGE.
+#
+# spkrtest - Test script for the speaker driver
+#
+# v1.0 by Eric S. Raymond (Feb 1990)
+# v1.1 rightstuff contributed by Eric S. Tiedemann (est@snark.thyrsus.com)
+# v2.0 dialog+perl by Wolfram Schneider <wosch@FreeBSD.org>, May 1995
+#
+# NOTE for iso-* (latin1) fonts: use TERM=cons25-iso8859-1
+#
+# $Id$
+
+$title = qq{
+reveille -- Reveille
+contact -- Contact theme from Close Encounters
+dance -- Lord of the Dance (aka Simple Gifts)
+loony -- Loony Toons theme
+sinister -- standard villain's entrance music
+rightstuff -- a trope from "The Right Stuff" score by Bill Conti
+toccata -- opening bars of Bach's Toccata and Fugue in D Minor
+startrek -- opening bars of the theme from Star Trek Classic
+};
+
+$music = qq{
+reveille -- t255l8c.f.afc~c.f.afc~c.f.afc.f.a..f.~c.f.afc~c.f.afc~c.f.afc~c.f..
+contact -- <cd<a#~<a#>f
+dance -- t240<cfcfgagaa#b#>dc<a#a.~fg.gaa#.agagegc.~cfcfgagaa#b#>dc<a#a.~fg.gga.agfgfgf.
+loony -- t255cf8f8edc<a>~cf8f8edd#e~ce8cdce8cd.<a>c8c8c#def8af8
+sinister -- mst200o2ola.l8bc.~a.~>l2d#
+rightstuff -- olcega.a8f>cd2bgc.c8dee2
+toccata -- msl16oldcd4mll8pcb-agf+4.g4p4<msl16dcd4mll8pa.a+f+4p16g4
+startrek -- l2b.f+.p16a.c+.p l4mn<b.>e8a2mspg+e8c+f+8b2
+};
+
+@checklist = ('/usr/bin/dialog', '--title', 'Speaker test', '--checklist',
+ 'Please select the melodies you wish to play (space for select)',
+ '-1', '-1', '10');
+
+$speaker = '/dev/speaker';
+
+sub Exit {
+ unlink $tmp if $tmp; exit;
+}
+
+$SIG{'INT'} = $SIG{'HUP'} = $SIG{'TRAP'} = $SIG{'QUIT'} =
+ $SIG{'TERM'} = '&Exit';
+
+
+# make assoc array from variable 'var'
+# 'name -- description' -> $var{$name} = $description
+sub splitconfig {
+ local(*var) = @_;
+ local($t, $name, $description);
+
+ foreach $t (split('\n', $var)) {
+ ($name, $description) = split('--', $t);
+
+ $name =~ s/^\s+//; $name =~ s/\s+$//;
+ $description =~ s/\s+//; $description =~ s/\s+$//;
+
+ $var{$name} = $description if $name && $description;
+ }
+}
+
+&splitconfig(*title);
+&splitconfig(*music);
+
+foreach (sort keys %title) {
+ push(@checklist, ($_, $title{$_}, 'OFF'));
+}
+
+$tmp = ($ENV{'TMP'} || "/tmp") . "/_spkrtest$$";
+
+if (!open(SPEAKER, "> $speaker")) {
+ warn "You have no write access to $speaker or the speaker device is not " .
+ "enabled\nin kernel. Cannot play any melody! See spkr(4).\a\n";
+ sleep 2;
+}
+
+open(SAVEERR, ">&STDERR") || die "open >&STDERR: $!\n";
+open(STDERR, "> $tmp") || do { die "open > $tmp: $!\n"; };
+system @checklist; # start dialog
+open(STDERR, ">&SAVEERR") || die "open >&SAVEERR: $!\n";
+$return = $? >> 8;
+
+# die if speaker device not avaiable
+if (fileno(SPEAKER) eq "") {
+ print "\nSorry, cannot play any melody!!!\n"; &Exit;
+}
+
+
+if (!$return) { # not cancel
+ select(SPEAKER); $| = 1;
+ select(STDOUT); $| = 1;
+
+ if (! -z $tmp) { # select melod(y/ies)
+ print STDOUT "\n";
+ open(STDIN, $tmp) || do { die "open $tmp: $!\n"; };
+ foreach $melody (split($", <STDIN>)) {
+ $melody =~ s/^"//; $melody =~ s/"$//;
+ print STDOUT "$title{$melody}\n";
+ print SPEAKER "$music{$melody}";
+ sleep 1;
+ }
+ } else { # use default melody
+ $melody = (sort keys %title)[0];
+ print STDOUT "Use default melody: $title{$melody}\n";
+ print SPEAKER "$music{$melody}";
+ }
+ close SPEAKER;
+}
+
+&Exit;
diff --git a/usr.sbin/spray/Makefile b/usr.sbin/spray/Makefile
new file mode 100644
index 0000000..d008c7f
--- /dev/null
+++ b/usr.sbin/spray/Makefile
@@ -0,0 +1,8 @@
+# $Id$
+
+PROG= spray
+MAN8= spray.8
+DPADD= ${LIBRPCSVC}
+LDADD= -lrpcsvc
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/spray/spray.8 b/usr.sbin/spray/spray.8
new file mode 100644
index 0000000..d57abd9
--- /dev/null
+++ b/usr.sbin/spray/spray.8
@@ -0,0 +1,72 @@
+.\"
+.\" Copyright (c) 1994 James A. Jegers
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. The name of the author may not be used to endorse or promote products
+.\" derived from this software without specific prior written permission
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+.\"
+.Dd July 10, 1995
+.Dt SPRAY 8
+.Os FreeBSD
+.Sh NAME
+.Nm spray
+.Nd send many packets to host
+.Sh SYNOPSIS
+.Nm spray
+.Op Fl c Ar count
+.Op Fl d Ar delay
+.Op Fl l Ar length
+.Ar host
+.Sh DESCRIPTION
+.Nm Spray
+sends multiple RPC packets to
+.Ar host
+and records how many of them were correctly received and how long it took.
+.Pp
+The options are as follows:
+.Bl -tag -width indent
+.It Fl c Ar count
+Send
+.Ar count
+packets.
+.It Fl d Ar delay
+Pause
+.Ar delay
+microseconds between sending each packet.
+.It Fl l Ar length
+Set the length of the packet that holds the RPC call message to
+.Ar length
+bytes.
+Not all values of
+.Ar length
+are possible because RPC data is encoded using XDR.
+.Nm Spray
+rounds up to the nearest possible value.
+.El
+.Pp
+.Nm Spray
+is intended for use in network testing, measurement, and management.
+This command
+.Em "can be very hard on a network and should be used with caution" .
+.Pp
+.Sh SEE ALSO
+.Xr netstat 1 ,
+.Xr ifconfig 8 ,
+.Xr ping 8 ,
+.Xr rpc.sprayd 8
diff --git a/usr.sbin/spray/spray.c b/usr.sbin/spray/spray.c
new file mode 100644
index 0000000..d6f53f1
--- /dev/null
+++ b/usr.sbin/spray/spray.c
@@ -0,0 +1,226 @@
+/*
+ * Copyright (c) 1993 Winning Strategies, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Winning Strategies, Inc.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <rpc/rpc.h>
+#include <rpcsvc/spray.h>
+
+#ifndef SPRAYOVERHEAD
+#define SPRAYOVERHEAD 86
+#endif
+
+static void usage ();
+void print_xferstats ();
+
+/* spray buffer */
+char spray_buffer[SPRAYMAX];
+
+/* RPC timeouts */
+struct timeval NO_DEFAULT = { -1, -1 };
+struct timeval ONE_WAY = { 0, 0 };
+struct timeval TIMEOUT = { 25, 0 };
+
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ spraycumul host_stats;
+ sprayarr host_array;
+ CLIENT *cl;
+ int c;
+ int i;
+ int count = 0;
+ int delay = 0;
+ int length = 0;
+ double xmit_time; /* time to receive data */
+
+ while ((c = getopt(argc, argv, "c:d:l:")) != -1) {
+ switch (c) {
+ case 'c':
+ count = atoi(optarg);
+ break;
+ case 'd':
+ delay = atoi(optarg);
+ break;
+ case 'l':
+ length = atoi(optarg);
+ break;
+ default:
+ usage();
+ /* NOTREACHED */
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc != 1) {
+ usage();
+ /* NOTREACHED */
+ }
+
+
+ /* Correct packet length. */
+ if (length > SPRAYMAX) {
+ length = SPRAYMAX;
+ } else if (length < SPRAYOVERHEAD) {
+ length = SPRAYOVERHEAD;
+ } else {
+ /* The RPC portion of the packet is a multiple of 32 bits. */
+ length -= SPRAYOVERHEAD - 3;
+ length &= ~3;
+ length += SPRAYOVERHEAD;
+ }
+
+
+ /*
+ * The default value of count is the number of packets required
+ * to make the total stream size 100000 bytes.
+ */
+ if (!count) {
+ count = 100000 / length;
+ }
+
+ /* Initialize spray argument */
+ host_array.sprayarr_len = length - SPRAYOVERHEAD;
+ host_array.sprayarr_val = spray_buffer;
+
+
+ /* create connection with server */
+ cl = clnt_create(*argv, SPRAYPROG, SPRAYVERS, "udp");
+ if (cl == NULL) {
+ clnt_pcreateerror("spray");
+ exit(1);
+ }
+
+
+ /*
+ * For some strange reason, RPC 4.0 sets the default timeout,
+ * thus timeouts specified in clnt_call() are always ignored.
+ *
+ * The following (undocumented) hack resets the internal state
+ * of the client handle.
+ */
+ clnt_control(cl, CLSET_TIMEOUT, (caddr_t)&NO_DEFAULT);
+
+
+ /* Clear server statistics */
+ if (clnt_call(cl, SPRAYPROC_CLEAR, xdr_void, NULL, xdr_void, NULL, TIMEOUT) != RPC_SUCCESS) {
+ clnt_perror(cl, "spray");
+ exit(1);
+ }
+
+
+ /* Spray server with packets */
+ printf ("sending %d packets of lnth %d to %s ...", count, length, *argv);
+ fflush (stdout);
+
+ for (i = 0; i < count; i++) {
+ clnt_call(cl, SPRAYPROC_SPRAY, xdr_sprayarr, &host_array, xdr_void, NULL, ONE_WAY);
+
+ if (delay) {
+ usleep(delay);
+ }
+ }
+
+
+ /* Collect statistics from server */
+ if (clnt_call(cl, SPRAYPROC_GET, xdr_void, NULL, xdr_spraycumul, &host_stats, TIMEOUT) != RPC_SUCCESS) {
+ clnt_perror(cl, "spray");
+ exit(1);
+ }
+
+ xmit_time = host_stats.clock.sec +
+ (host_stats.clock.usec / 1000000.0);
+
+ printf ("\n\tin %.2f seconds elapsed time\n", xmit_time);
+
+
+ /* report dropped packets */
+ if (host_stats.counter != count) {
+ int packets_dropped = count - host_stats.counter;
+
+ printf("\t%d packets (%.2f%%) dropped\n",
+ packets_dropped,
+ 100.0 * packets_dropped / count );
+ } else {
+ printf("\tno packets dropped\n");
+ }
+
+ printf("Sent:");
+ print_xferstats(count, length, xmit_time);
+
+ printf("Rcvd:");
+ print_xferstats(host_stats.counter, length, xmit_time);
+
+ exit (0);
+}
+
+
+void
+print_xferstats(packets, packetlen, xfertime)
+ int packets;
+ int packetlen;
+ double xfertime;
+{
+ int datalen;
+ double pps; /* packets per second */
+ double bps; /* bytes per second */
+
+ datalen = packets * packetlen;
+ pps = packets / xfertime;
+ bps = datalen / xfertime;
+
+ printf("\t%.0f packets/sec, ", pps);
+
+ if (bps >= 1024)
+ printf ("%.1fK ", bps / 1024);
+ else
+ printf ("%.0f ", bps);
+
+ printf("bytes/sec\n");
+}
+
+
+static void
+usage ()
+{
+ fprintf(stderr,
+ "usage: spray [-c count] [-l length] [-d delay] host\n");
+ exit(1);
+}
diff --git a/usr.sbin/stallion/Makefile b/usr.sbin/stallion/Makefile
new file mode 100644
index 0000000..44c3b63
--- /dev/null
+++ b/usr.sbin/stallion/Makefile
@@ -0,0 +1,5 @@
+# $Id$
+
+SUBDIR= bootcode stlload stlstats
+
+.include <bsd.subdir.mk>
diff --git a/usr.sbin/stallion/Makefile.inc b/usr.sbin/stallion/Makefile.inc
new file mode 100644
index 0000000..53b9b3e
--- /dev/null
+++ b/usr.sbin/stallion/Makefile.inc
@@ -0,0 +1,7 @@
+# $Id$
+
+BOOTDIR= /usr/libdata/stallion
+
+.if exists(${.CURDIR}/../../Makefile.inc)
+.include "${.CURDIR}/../../Makefile.inc"
+.endif
diff --git a/usr.sbin/stallion/bootcode/2681.sys.uu b/usr.sbin/stallion/bootcode/2681.sys.uu
new file mode 100644
index 0000000..db3dd28
--- /dev/null
+++ b/usr.sbin/stallion/bootcode/2681.sys.uu
@@ -0,0 +1,690 @@
+begin 444 2681.sys
+M````````````````````````````````````````````````````````````
+M``````!E`0``````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M````````````````````````````````````````````````````````````
+M`-(1SV0``````````````````````````````````````!``````8V1K:"YC
+M```F`````````````````.P5````````````````&`8```,`````````````
+M;F\@96-P86YE;"!D<FEV97(@:6YS=&%L;&5D`&YO(&5C8G5S(&1R:79E<B!I
+M;G-T86QL960`;65M;W)Y(&UA;&QO8R!F86EL960``````%!%3D0@8W1R;&EN
+M(2`E9`!014Y$(&-T<FQI;B$@)60`4$5.1"!C=')L:6XA("5D`$%34T525$E/
+M3B!&04E,55)%(&%T("5S*"5D*0H``-@`24Y&3U)-`%=!4DY)3D<`1D%404P`
+M4$%.24,`15)23U(@)7,Z("5S*"5D*3H@``H`^`#_``<!#0$3`3$N,2XP`"A#
+M*2!#;W!Y<FEG:'0@4W1A;&QI;VX@5&5C:&YO;&]G:65S(#$Y.3,@+2`Q.3DV
+M``H*+2TM+2TM+2TM+2TM+2TM("!3=&%L;&EO;B!);G1E;&QI9V5N="!#;VUM
+M=6YI8V%T:6]N<R!0<F]C97-S;W(@("TM+2TM+2TM+2TM+2TM+0H`("`@("`@
+M("`@("`@("`@("5S"@H`("!";V%R9"!4>7!E("`@("`@("`@("`@("`@("`@
+M("4R9"`@("`@("`@("`@($-O9&4@5F5R<VEO;B`@("`@("`@("`@("`@("5S
+M"@`@($UE;6]R>2!4;W1A;"`@("`@("`@("`@,'@E,#9L>"`@("`@("`@("`@
+M($UE;6]R>2!5<V5D("`@("`@("`@("`@(#!X)3`V;'@*`"`@365M;W)Y($9R
+M964@("`@("`@("`@("`P>"4P-FQX("`@("`@("`@("`@365M;W)Y($UA<"`@
+M("`@("`@("`@("`@,'@E,#9L>`H`("!#;V1E(%-I>F4@("`@("`@("`@("`@
+M(#!X)3`V;'@@("`@("`@("`@("!$871A(%-I>F4@("`@("`@("`@("`@("`P
+M>"4P-FQX"@`@($-O9&4@4V5G;65N="`@("`@("`@("`@("`P>"4P-'@@("`@
+M("`@("`@("!$871A(%-E9VUE;G0@("`@("`@("`@("`@(#!X)3`T>`H`("!3
+M=&%C:R!396=M96YT("`@("`@("`@("`@,'@E,#1X("`@("`@("`@("`@17AT
+M<F$@4V5G;65N="`@("`@("`@("`@("`P>"4P-'@*"@`@($%S>6YC:')O;F]U
+M<R!086YE;',@("`@("`@("`@("5D("`@("`@("`@("`@07-Y;F-H<F]N;W5S
+M(%!O<G1S("`@("`@("`@("`@)3)D"@`*+2TM+2TM+2TM+2TM+2TM+2TM+2TM
+M+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM+2TM
+M+2TM+2TM+2TM+2TM+0H*```R`3@!,#$R,S0U-C<X.6%B8V1E9@``3`2O*[4K
+MNRO!*\<KS2O3*]DKWROE*^LK\2OW*_TK!"P++!(L&2P@+"<L+BPU+#PL0RQ*
+M+%$L6"Q?+&8L;2QT+'LL@BR)+)`LERR>+*4LK"RS++HLP2S(+,\LUBS=+.0L
+MZRSR+/DL`"T'+0XM%2T<+2,M*BTQ+3@M/RU&+4TM5"U;+59%0U]&055,5"`H
+M)60@)60@)60@)60@)60@)60@)60@)60@)60@)60@)60@)60@)60@)60@)60I
+M```R-C@Q+F,``!H%`````````````#(```!+````;@```(8```"6````R```
+M`"P!``!8`@``L`0```@'``!@"0``P!(``(`E````2P```)8```#A``!@`&``
+MX`!@`&``X`!@`&``8`!@`.``8`!@`&``X`!@`&``````````$0`B`#,`,P!$
+M`%4`9@"J`(@`F0"[`,P`S`#,```````0`(``D````1`!@`&0`0`"$`(@`C`"
+M0`)0`F`"<`(``!``@`"0```!$`$@`3`!0`%0`6`!<`&``9`!H`&P`<`!T`'@
+M`?`!``(0`B`",`)``E`"8`)P`H`"D`*@`K`"0`!``#1-%D@``-8OIC&:,@``
+M%#<H/!0^3%$````````````````````````E9``N````````````````^KP`
+M!#/`CM".P+@``8[8,\".P":!/@`"I_)U)R:!/@("2:%U'B:!/@0"4F-U%2:!
+M/@8"(?%U#":A"@(FBQX,`NMBD+E:T#/VCL8FH0``)HD.``"^`!"+%@``CL8F
+M.0X``'0*@<8`$('^`(!RZ3/2CL(FHP``C,B[4&*#X_#!ZP0#V$..PR:A```F
+MB0X``(L6```F.0X``'4))J,``$,[WG+A,\"C#`#'!@X```"+T\'B!`$6#`"#
+M%@X``(O3P>H,`18.`*$,`(L6#@"Y4&*)#A@`@\$?@^'PB_&+^2O!@]H`P>@$
+MP>(,"]".PD].C,B.V/WSI/P&:/H`R[@``8[8N5`?B_&+^8O9B1XD``,>(`"#
+MPP^#X_"+P\'H!"O0CL).3_WSI/R,P([8CL".T(OC@^P$OU`&,\"Y4!^)#AP`
+M*\_\\ZJ,R*,&`(S8HP0`+J-P`8S0HPH`C,"C"`",V,'@!*,4`(S8P>@,HQ8`
+MZ#8>Z_X``,@$``!75HM&")F+'GX'.5<0<F]W!3E'#G)HBW<$F8OXB]H#?@03
+M7@:#[P&#VP"+S@K)=`C1Z]'?_LEU^(O'F8O/B]J+1@2+5@:)3OR)7OZ+S@K)
+M=`C1ZM'8_LEU^#M&_'4%.U;^=#F+QYF+S@K)=`C1X-'2_LEU^(E&!(E6!NL@
+MD)"9.5<0=QAR!3E'#G,1:.(`_S8N`/\V]@#HI2*#Q`:+1@2+5@9>7\G#R!``
+M`%=6BQY^!XM'$HM7%#D6%@!W'W(&.084`',7H10`BQ86`"M&!!M6!M'JT=@#
+M1@035@8K1@0;5@:)1O")5O(K_XM'#HM7$(E&](E6]CO7=00[QW0-4E#_=O+_
+M=O#HA%Z+^`O_=`B+1O8+1O1U#[\!`(M&\(M6\HE&](E6]HM&"-'@`\<KTO?W
+MB_`+]G4#O@$`:@!6_W;V_W;TZ$=>B4;XBT8(T>`#QVH`4/]V\O]V\.@Q7HE&
+M_HM&^#E&_G8&BT;^B4;X/8``<P7'1OB``(M&^%Y?R<.0R!P``%=6QT;N``/'
+M1O```,<&2A^``L<&3!\``,0>2A\FBT<$HTX?)HI''"4!`#T!`!K`_L"B6``F
+MBT<2)@M'$'0*)HM'$*-<!^@6&8M&[HM6\-'JT=C1ZM'8T>K1V-'JT=B*3NZ#
+MX0^)#EX'HV`'@4;N@`"#5O``:(``4%'H62>#Q`;$'EX')L='!@$`Q!Y>!R;'
+M1T+_`,0>7@<FQD=,`<0>7@<FQD=-`<0>7@<FQD=.`,0>2A\FBT<,B4;DBQY^
+M!XL'+1<`?`1(2'X.H4H`HT`?QP84'VA9ZQ)H60!H9P'_-BX`:@+HX1J#Q`B+
+M1N1`HU(&Q!Y>!R:)1U*A4@9(F3/"*\+!^`,SPBO"0*,$'Z%2!@4'`)DSPBO"
+MP?@#,\(KPD`D_HE&[(M&[HM6\,0>7@<FB4=8)HE76HM&["O2`4;N$5;PBT;N
+MBU;PQ!Y>!R:)1UPFB5=>BT;L*](!1NX15O!0Q!Y>!R:+1U@FBU=:T>K1V-'J
+MT=C1ZM'8T>K1V":*5UB#X@^+RE!1Z$TF@\0&_W;LQ!Y>!R:+1UPFBU=>T>K1
+MV-'JT=C1ZM'8T>K1V":*5UR#X@^+RE!1Z!TF@\0&H5(&B4;LN`8`]V;LB4;L
+MBT;NBU;PQ!Y>!R:)1U0FB5=6BT;L*](!1NX15O#$'EX')HM'5":+5U;1ZM'8
+MT>K1V-'JT=C1ZM'8)HI/5(/A#XE.Z(E&ZO]V[%!1Z+TE@\0&BQY^!XL'+1<`
+M?`1(2'X(QP92```#ZQ#'!E(```&+'GX'@W\H`'0(QT;\`0#K!I#'1OP"`&B2
+M`/]V\/]V[N@?_(/$!HE&[HE6\(M&_,1>Z":)!XM&[HM6\,1>Z":)1P(FB5<$
+M:)(`BT;NBU;PT>K1V-'JT=C1ZM'8T>K1V(I6[H/B#XO*4%'H,26#Q`:!1NZ2
+M`(-6\`"#1N@&BQY^!X-_*`!^$(M&Y"M'*)GW?RB)1O3K!I#'1O0``(-^]`!]
+M!<=&]```QT;^``#'1N8``"OVZ0(!D(M^_M'GBT$8BT[T0??I.T;F?@O$7N@F
+MQP<`!.L)D,1>Z";'!P``BT;NBU;PQ%[H)HE'`B:)5P1HO`"+1NZ+5O#1ZM'8
+MT>K1V-'JT=C1ZM'8BE;N@^(/B\I04>B*)(/$!H%&[KP`@U;P`/]&YHM^_M'G
+MBQY^!XM!&(M.]$'WZ3M&YG]YQT;F``#_1OZ#?OX(?&O$'DH?)HM'%(E&\B:+
+M1QB)1O:#?O(`=`D+P'4%QT;V``&#?O8`=`N#?O(`=07'1O(``8-^\@!U&X-^
+M]@!U%?]VY/]V\/]V[NA;^X/$!HE&\HE&]L0>7@<FBT=4)HM75HE&Z(E6ZBOV
+MZT60D$:#1N@&.7;D?HMHO`#_=O#_=N[H>OJ#Q`:)1NZ)5O"+1N:+?O[1YXL>
+M?@<Y01A_`^G1_L1>Z";'!R``Z>S^1H-&Z`8Y-E(&?P/IH@#$7N@F]P?@_W3H
+M)HM'`B:+5P31ZM'8T>K1V-'JT=C1ZM'8)HI/`H/A#XE.^(E&^O]V\O]V\/]V
+M[N@)^H/$!HE&[HE6\,1>^":)A[(`)HF7M`"+1O+$7O@FB8>V`(M&\BO2`4;N
+M$5;P_W;V_W;P_W;NZ-'Y@\0&B4;NB5;PQ%[X)HF'I@`FB9>H`(M&]L1>^":)
+MAZH`BT;V*](!1NX15O#I4/]J`&H$:*P*:@SH#B>#Q`B+1NZ+5O"CP`:)%L(&
+MHQ``B182`"O`7E_)PY#(%@``5U9K!E(&*E#HF1J#Q`*C>`:)%GH&BQY`'_\7
+MH7H&"P9X!G42:(\`:#H"_S8N`&H"Z(T6@\0(:P92!BI0_S9Z!O\V>`;HB"*#
+MQ`;$'EX')HM'5":+5U;1ZM'8T>K1V-'JT=C1ZM'8)HI/5(/A#XE.[HE&\,=&
+M]#``H7@&BQ9Z!HE&^HE6_,=&_O__QT;R`0`K_ROVZ8@!)HM'!":+5P:)1NJ)
+M5NS$7NHFBX>R`":+E[0`T>K1V-'JT=C1ZM'8T>K1V":*C[(`@^$/Q%[Z)HE/
+M"":)1PK$7NHFBX>F`":+EZ@`T>K1V-'JT=C1ZM'8T>K1V":*CZ8`@^$/Q%[Z
+M)HE/#":)1P[$7OHF]T<FX`-U`^FG`(-^\@!U`^F>`/]&_HM>_M'CB;=*!XM&
+M_HL>?@<Y1Q9^!3T(`'Q\BQY^!XM?%M'CB;=*!\0>7@<FBT=8)HM76M'JT=C1
+MZM'8T>K1V-'JT=@FBD]8@^$/B0X&'Z,('R:+1UPFBU=>T>K1V-'JT=C1ZM'8
+MT>K1V":*3UR#X0^)#F('HV0'QT;V8@+'1O@``,1>]B;&!PW$7O8F@#\`=0/I
+MY`#K\BO_B7[RB\<JY)F+R(I&_HO:F8/*_XKRBM2*X"K`"\$+T\1>^B:)!R:)
+M5P*+'D`?@W\"`'08C4;T4,1>^B;_=P(F_S>+'D`?_U<"@\0&1XM>_M'C`QY^
+M!SE_&'\%QT;R`0!&@T;Z*H-&[@8Y-E(&?P/I$?_$7OHFB7<DQ%[N)HL'Q%[Z
+M)HE')L1>[B:+1P(FBU<$T>K1V-'JT=C1ZM'8T>K1V":*3P*#X0_$7OHFB4\$
+M)HE'!HO&P>`#F?<^4@:+R+`!TN#$7OHFB$<HQ%[Z)O9')A]UC":#?R8`=`/I
+M`_[I?__$'EX')H-_0@!U!L<&4@```"O`7E_)PY#H'".#/E```'0*4.@@(X/$
+M`BO`P_\&4`!0Z!(C@\0"Z`@`QP90````Z^?(#```5U;$'EX')O]'0,0>7@<F
+M@W]0`'1"H:0`_P:D`#UD`'8*_P:F`,<&I````":`?T\`=`O'!J8````FQD=/
+M`*&F`,0>7@<F.4=0=PWH?1#$'EX')L='4```Q!Y>!R:`?V$`=&`K_^LGA7;X
+M=!1K1OHJ`P9X!HL6>@924.@0`8/$!/]&^M'FBT;^.4;Z?-I'.3X$'WXOQ!YB
+M!R:*`9B)1O@+P'3JB\?!X`.)1OH%"`")1OXY!E(&?0:A4@:)1OZ^`0#KPY#$
+M'EX')H!_8`!U`^F)`,=&]@``*__K09"%=OAT+6M&^BH#!G@&BQ9Z!E)0Z#((
+M@\0$"\!]%6M>^BH#'G@&C@9Z!B:*1R@JY`E&]O]&^M'FBT;^.4;Z?,%'.3X$
+M'WXNQ!X&'R:*`9B)1O@+P'3JB\?!X`.)1OH%"`")1OXY!E(&?0:A4@:)1OZ^
+M`0#KPXI&]L0>7@<FB$=@@SY2``!T&8,^5```=!)J`/\V4@#H*QZ#Q`3'!E0`
+M``"+'D`?@W\4`'0#_U<4*\!>7\G#R"P``%=6Q%X$)HM'!":+5P:)1NB)5NHF
+M]D<F'W0,)HI')BO2M/\*].L')HL')HM7`HE&_(E6_L1>Z":`/P!U`^E]`2:+
+M1P(FBU<$B4;@B5;B*\`FB4<$)HE'`L1>Z";&!P"+'D`?.4<&=#O$7N@F_W<$
+M)O]W`O]V_O]V_(L>0!__5P:#Q`B+^(/_`70F"_]\`XU%`8E&W)G$7N@FB4<"
+M)HE7!.L.D,1>Z";'1P(!`":)1P2+'D`?@W\*`'4#Z<L`C4;LB\B+%@0`4E%J
+M`&@-8?]V_O]V_(L>0!__5PJ#Q`R+^(/_`74B:!L#:*@`Z%X7@\0$B\=(=1%H
+M'`/_-BX`_S;V`.A(%X/$!@O_?'[V1O`$=2*`3O`$Q%X$)H!/$`C$7N@FBX>V
+M`+D%`"O2]_'$7@0FB4<2]D;P`G4,@$[P`L1>!":`3Q`0]D;Q`74,@$[Q`<1>
+M!":`3Q"`Q%X$)O9'$)AT((U&[(O(BQ8$`%)1:@!H#F'_=O[_=OR+'D`?_U<*
+M@\0,Q%X$)H!G$)O'!E0``0#$7@0FBD\D@.$'L`'2X":+7R3!ZP,#'@8?C@8(
+M'R8(!\1>!":*1RC$'EX')@A'8,1>Z":+1PPF"T<*=0/I[P+$7N@FBT<*)HM7
+M#(E&X(E6XHM&X(M6XB8Y1PIUXR8Y5PQUW2O`)HE'$":)1P[$7N@FB4<,)HE'
+M"HE&Y(M&X.E1`9"+1NB+5NH%$@")1MB)5MK$7M@F]D<$!'43Q%X$)H!/$`C$
+M7M@F@$\$!.L)D,1>!":`9Q#WQ%[8)O9'!`)U$L1>!":`3Q`0Q%[8)H!/!`+K
+M",1>!":`9Q#OQ%[8)O9'!0%U%,1>!":`3Q"`Q%[8)H!/!0'I!P&0Q%X$)H!G
+M$'_I^P"0Q%[H)HM'$HE&Y/]VY/]V!O]V!.CF"X/$!NG>`,=&Y`(`@7[@#&%U
+M!H-^X@!T#8%^X`IA==6#?N(`=<^`3N0!Z\F0QT;D`@"!?N`'874&@W[B`'0-
+M@7[@!6%U"H-^X@!U!(!.Y`'_=N3_=@;_=@3HBPN#Q`:+1NB+5NH%$@")1MB)
+M5MK$7N@FBX>V`,1>V"8Y1R1R$<1>Z":+A[8`2$C$7M@FB4<DQ%[8)H-_)`!U
+M$<1>Z":+A[8`N04`*]+W\>L$)HM'),1>!":)1Q+K+9`M`6$]#0!W)-'@DR[_
+MI]@/^@[T#_0/;@\\#SP/00_T#_0/%@\6#QL/]`]X#HL>0!^#?PH`=0/IW0#_
+M=N+_=N#HU`J#Q`0+P'0-Q%[H)HM'$B:+5Q3K"8M&Z(M6Z@42`(E&V(E6VE)0
+M_W;B_W;@_W;^_W;\BQY`'_]7"H/$#(OX"_]U8XM&X"T2875;BT;HBU;J!1(`
+MB4;4B5;6BT;H!;(`4E#H]02#Q`3$7M0F`0>+1NB+5NH%I@!24.B<!(/$!,1>
+MU"8!1P+$7N@FBX>V`$C$7M0F`4<$Q%[H)HN'J@!(Q%[4)@%'!H/_`70="_]\
+M!8U%`>L"B\>)1M29Q%[H)HE'#B:)5Q#K(9!HI@-HN`#HVQ.#Q`3K$L1>Z";'
+M1PX!`";'1Q```+___\<&5``!`,1>!":*3R2`X0>P`=+@)HM?),'K`P,>!A^.
+M!@@?)@@'Q%X$)HI'*,0>7@<F"$=@Q%X$)O9'$0%T3\=&\`(`QT;R```KP(E&
+M]HE&](E&[HE&[":`9Q'^Q%X$)H!/$"#'!E8``0"-3NR+%@0`4E%J`&@/8<1>
+M!";_=P(F_S?H=P2#Q`S'!E8```#$7@0F]D<0!'0#Z<@!BT;HBU;J!;(`4E#H
+MR0.#Q`2+\`OV?P/IK@'$7@0F.7<2=P4F@&<0W\1>!":`9Q"_QT;>``"`/E@`
+M`'0QBQY`'X-_"@!T)XU&X!90:@!H$V'_=O[_=OR+'D`?_U<*@\0,/0$`&\!`
+MB4;>"\!T""O`B4;BB4;@@W[>`74,:-H#:,@`Z*42@\0$BT;B"T;@=`/I-@&+
+M'D`?@W\0`'4#Z2D!Q%[H)HN'M@`FBX^Z`"O!.\9W#R:+A[H`)HN_M@`K^.L#
+MD(O^5\1>!":+1P@FBU<*Q%[H)HN/N@`#P5)0_W;^_W;\BQY`'_]7$(/$"HE&
+MU`O`?07'1M0``#EVU'TS.7[4=2Z+QBM&U%#$7@0F_W<*)O]W"/]V_O]V_(L>
+M0!__5Q"#Q`J+^`O_?0(K_P%^U.L"*_^#?M0`?P/IC@`Y=M1T",1>!":`3Q`$
+M*W;4Q%[H)HN'N@`!1M2+1M0F.8>V`'<#B7[4BT;4)HF'N@#$7@0F.7<2=E4F
+M]D<0*'5.*\")1O*)1O")1O:)1O2)1NZ)1NPF]D<0('4)@$[P!":`3Q`@QP96
+M``$`C4[LBQ8$`%)1:@!H#V'$7@0F_W<")O\WZ*("@\0,QP96````Q%[H)H!_
+M`0!U`^F6`":+1P8FBU<(B4;@B5;B*\`FB4<()HE'!L1>Z";&1P$`BQY`'SE'
+M"'0K_W;^_W;\_U<(@\0$B_B#_P%T)0O_?`.-10&)1M29Q%[H)HE'!B:)5PCK
+M#<1>Z";'1P8!`":)1PC'!E0``0#$7@0FBD\D@.$'L`'2X":+7R3!ZP,#'@8?
+MC@8('R8(!\1>!":*1RC$'EX')@A'8"O`7E_)P\@$``!6Q%X$)HM'!":+5P:)
+M1OR)5O[$7OPF@+^2``!T![C__U[)PY#$7@0F]D<0`71`:A`%E`!24(O#C,(%
+M%`!24.A8%H/$"L1>!":*1Q`D@CP"=0G$7OPF@(^9``'$7OPFQH>2``'$7@0F
+M@&<0_NNPD":*1Q`D@CP"=3QJ$(M&_`64`%)0Z$@6@\0&Q%[\)H"/F0`!BT;\
+MBU;^!90`4E!J`&@/8<1>!";_=P(F_S?H/@&#Q`SI:?_$7OPFBT<$)@M'`G0#
+MZ5G_)HM'""8+1P9T`^E,_R:+1Q`F"T<.=`/I/__$7@0F]D<F'W45BT;\!:8`
+M4E#H+0"#Q`0+P'0#Z2#_Q%X$)HI/)(#A![`!TN#VT":+7R3!ZP/$-@8?)B``
+M*\!>R</(`@``5U;$7@0FBW<()HM_"#OW=`J+]R:+?P@[]W7V)HM'!HE&_CEV
+M_G()BT;^*\9>7\G#)HM'!"O&`T;^7E_)PY#(`@``5U;$7@0FBW<&)HM_!CO^
+M=`J+]R:+?P8[_G7V)HM'"(E&_CEV_G<)B\8K1OY>7\G#)HM'!"M&_@/&7E_)
+MPY!5B^Q6BW8(@_X!=2"+1@2+5@8%L@!24.BD_X/$!,1>!"8KA[8`]]A(7LG#
+MD(M&!(M6!@6F`%)0Z$+_@\0$Q%X$)BN'J@#KWI#(%```5U:#?@3_=0:#?@;_
+M=!.+1@2+5@8JTCW__W4?@?H`_W49:.H$_S8N`/\V]@#HE0Z#Q`:X__]>7\G#
+MD(M6!BK`*M(]`/]U1CO0=4(K]NL!1CDV4@9^%HI&!"KD:]XJ`QYX!HX&>@8F
+M.4<F=>.*1@0JY&O>*@,>>`:.!GH&)CE')G0%:/,$ZYYKQBKK79"+1@2+5@:*
+MQ(K6*O8JY(E&_HI&!(E&\(-^_@!\#(M&_HL>?@<Y1Q9_!VC[!.EI_Y"+V-'C
+MB[=*!X-^\`!\$(M&\`/&BU[^T>,YATP'?P9H``7I1/^+1O`#QFO`*@,&>`:+
+M%GH&B4;RB5;TQ%[R)HM'!":+5P:)1OJ)5OR!?@@/870#Z?T"@WX*`'0#Z?0"
+MBT8."T8,=1*+1@X+1@QT`^D%_V@*!>GQ_I"+1@R+5@Z)1O:)5O@F]D<F'W0#
+MZ0H!)O9'$`)U#<1>]B;V1P4!=0/I]@!J`/]V_/]V^NA+_H/$!HE&[`O`=0O$
+M7O(F@$\0`NG6`,1>^B:+AZH`)HN/K``KP3M&['<.)HN'K``FB[^J`"OXZP.+
+M?NQ7Q%[R)HM'#":+5P[$7OHFBX^L``/!4E#_=@;_=@2+'D`?_U<.@\0*B_`+
+M]GT"*_8+]GXQ._YU+8M&["O&4,1>\B;_=PXF_W<,_W8&_W8$BQY`'_]7#H/$
+M"HOX"_]]`BO_`_?K`BO_.7;L?B%J`/]V!O]V!(L>0!__5Q*#Q`8+P'4+Q%[R
+M)H!G$/WK"9#$7O(F@$\0`@OV?AC$7OHFBX>L``/P)CFWJ@!W`HOW)HFWK`"#
+M/E8``'0#Z:``Q%[V)O='!`8!=0/ID@`F]D<$!G0(Q%[R)H!G$/O$7O8F@&<$
+M^\1>]B;V1P0"=!6+1OJ+5OP%L@!24.C7_(/$!`O`?PK$7O(F]D<00'0(Q%[V
+M)H!G!/W$7O(F]D<0$'0(Q%[V)H!G!/W$7O(F]D<0@'0(Q%[V)H!G!?[$7O8F
+MBT<")@L'=1HFBT<*)@M'"'40)HM'!B8+1P1U!BO`7E_)P\1>^B:`OY(``'4#
+MZ:H`Q%[R)O9'$`%U$6H0B\.,P@44`%)0Z+D1@\0&Q%[V)HL')HM7`L1>\B8)
+M1Q0F"5<6Q%[V)O9'!!YT",1>\B:`9QCAQ%[V)O9'!1YT",1>\B:`9QGAQ%[V
+M)HM'!":+5P;$7O(F"4<8)@E7&L1>]B:+1P@FBU<*Q%[R)@E''"8)5Q[$7O8F
+MBT<*)@M'"'03)HM'#":+5P[$7O(FB4<@)HE7(L1>\B:`3Q`!ZR!J$(O#C,(%
+ME`!24/]V#O]V#.C?$(/$"L1>^B;&AY(``<<&5``!`,1>\B:*3R2`X0>P`=+@
+M)HM?),'K`P,>!A^.!@@?)@@'Q%[R)HI'*,0>7@<F"$=@Z8$`@7X(`&=U(X-^
+M"@!U'8-^#@!\`_]&#(M&#(M6#L1>^B:)1PXFB5<0ZUB0@7X(`6=U(X-^"@!U
+M'8-^#@!\`_]&#(M&#(M6#L1>^B:)1P(FB5<$ZRZ0@7X(`F=T`^G&^X-^"@!T
+M`^F]^X-^#@!\`_]&#(M&#(M6#L1>^B:)1P8FB5<(QP94``$`Q%[R)HI/)(#A
+M![`!TN`FBU\DP>L#`QX&'XX&"!\F"`?$7O(FBD<HQ!Y>!R8(1V"#/E(``'4#
+MZ2K^@SY4``!U`^D@_FH`_S92`.BD#X/$!,<&5````.D+_I!5B^R+1@0M`&%\
+M"4A(?P6X`0#)PRO`R</("```5HMV".C*$HE&^,1>!":+1P0FBU<&B4;ZB5;\
+M]\8!`'0JQ%[Z)HN'K``FB8>N`,1>^B:+AZX`B4;^BT;^)CF'K`!UWL1>!":`
+M9Q#]]\8"`'1+Q%X$)O9'$!!U%\1>^B:+A[H`)CF'N`!T",1>!":`3Q$!Q%[Z
+M)HN'N``FB8>Z`,1>^B:+A[@`B4;^BT;^)CF'N@!UWL1>!":`9Q";_W;XZ#X2
+M@\0"*\!>R</($```5U;H'A*+^*%X!HL6>@:)1OR)5OZ+P@M&_'4'*\!>7\G#
+MD&H,C4[PBQ8$`%)1Z/4.@\0&QT;T___'1O;__ROVZUR0Q%[\)O9')B!T+HL>
+M0!^#?PH`="2-1O"+R(L6!`!246H`:`EABU[\)O]W`B;_-XL>0!__5PJ#Q`R+
+M'D`?@W\(`'04Q%[\)O]W`B;_-XL>0!__5PB#Q`1&@T;\*CDV4@9_GU?HBQ&#
+MQ`+I;O^0R`H``%=6QP9^!Q8?QT;\``+'1OX``,1>_":!/Z?R=`/I*`$F@7\"
+M2:%T`^D=`2:!?P128W0#Z1(!)H%_!B'Q=`/I!P$FBT<4HQ8?ZT^0QP8H'P``
+MQP8J'P$`QP8D'P!`QP8F'P``QP8:'PX`ZU+'!B@?``#'!BH?`0#'!B0?``#'
+M!B8?`0#'!AH?$`#K,L<&*!\``,<&*A\"`.O>2#T*`'<>T>"3+O^G`!W$'*0<
+MQ!S$'*0<I!SD'!8=Q!S$',0<QP88'P$`Q%[\)H-_&`!U!B;'1QA0`&H!:*"&
+MQ%[\:@`F_W<8Z"M$HQP?B18>'VH`:@124.AK0J,@'XD6(A_$7OPF@W\:`'00
+M)HM'&HE&]L<&/A\0`.L.D":+1PZ)1O;'!CX?```K]NL"D$:#_A!]"XO.BT;V
+MT^`+P'7O`38^'\<&+!\!`*$^'Z,N'\=&^``"QT;Z``#$7O@F@3]%0W0#Z0`!
+M)H%_`E`A=`/I]0#'!A8?%P"#/EP'`'0=@SY<!QAU"<<&%A\8`.L.D(,^7`<9
+M=0;'!A8?&0"A%A\M&`!T%<<&)!\`$,<&)A\``,<&&A\,`.L3D,<&)!\``,<&
+M)A\!`,<&&A\0`,<&*!\``,<&*A\"`,1>^":+1P:C&!_'!AP?0'C'!AX??0%J
+M`&H$_S8>'_\V'!_H:T&C(!^)%B(?QP8^'P``QP8L'P``*_;K!2O``_!&@_X(
+M?4K$7OB#PP@FB@`JY(E&]CW_`'0WBD;V)0<`.\9U+8I&]B4@`#T!`!O_@^?X
+M@\<0`3X^'XL>+!_1XXF_+A__!BP?@_\0=;&X`0#KKBO`7E_)PU6+[/]V"/]V
+M!HM>!-'C_[<H`?\V,`'HW06+Y8U&#%#_=@IJ`&H`Z`D&B^5H)@'HAP4KP,G#
+MD&AH`>BZ!8/$`O\V2@1HNP'HK06#Q`3_-D@$_S86'VC0`>B<!8/$!O\V1A__
+M-D0?_S8.`/\V#`!H'0+H@P6#Q`K_-L(&_S;`!O\V;@?_-FP':&H"Z&H%@\0*
+M_S8>`/\V'`#_-AH`_S88`&BW`NA1!8/$"O\V!`#_-@8`:`0#Z$`%@\0&_S8(
+M`/\V"@!H4P/H+P6#Q`;_-CX?_S8L'VBC`^@>!8/$!FCT`^@5!8/$`L.0Z*_\
+MZ)(.Z#D+Z$3CZ`\`Z$3HZ#O_Z*8-Z7L*D,.0R`0``*$4`(L6%@`K!A``&Q82
+M`*-L!XD6;@>A#`"+%@X`*P9L!QL6;@>C1!^)%D8?H1``BQ82`-'JT=C1ZM'8
+MT>K1V-'JT=B*#A``@^$/B4[\B4;^H6P'BQ9N!\1>_":)!R:)5P+$7OPKP":)
+M1P8FB4<$Q%[\)HE'"B:)1PB+1OR+5OZC>@>)%GP'HPP?B18.'RO`R<.0R`0`
+M`*%Z!XL6?`?K"9`FBT<$)HM7!HE&_(E6_HO""T;\=!:+1@2+5@;$7OPF.5<"
+M<MQW!28Y!W+5BT;\BU;^R<.0R`0``(M&!@M&!'17H7H'BQ9\!\1>!":)1P0F
+MB5<&Q%X$*\`FB4<*)HE'"*%\!PL&>@=U$(M&!(M6!J,,'XD6#A_K$Y"+1@2+
+M5@;$'GH')HE'"":)5PJ+1@2+5@:C>@>)%GP'*\#)PY!5B^R+1@8+1@1T9\1>
+M!":+1PHF"T<(=1(FBT<$)HM7!J-Z!XD6?`?K%9`FBT<$)HM7!B;$7P@FB4<$
+M)HE7!L1>!":+1P8F"T<$=1,FBT<()HM7"J,,'XD6#A\KP,G#)HM'"":+5PHF
+MQ%\$)HE'"":)5PHKP,G#R!0``(M&!(M6!H#D#XK$BN**UBKVT>K1V-'JT=C1
+MZM'8T>K1V`-&!(/2`(E&^(E6^L1>!":+!R:+5P(#1O@35OJ)1NR)5NZA>@>+
+M%GP'ZTB+1O2+5O8Y1OAU-3E6^G4PQ%X$)HL')HM7`L1>_"8!!R815P+_=O[_
+M=OSH#?^#Q`2+1OR+5OZ)1@2)5@;IB`"0)HM'!":+5P:)1OR)5OZ+P@M&_'1R
+MBT;\@.0/BL2*XHK6*O;1ZM'8T>K1V-'JT=C1ZM'8`T;\@](`B4;PB5;RQ%[\
+M)HL')HM7`@-&\!-6\HE&](E6]HM&\(M6\CE&['0#Z5W_.5;N=`/I5?\&4^B*
+M_H/$!,1>_":+!R:+5P+$7@0F`0<F$5<"BT;^"T;\=`/IXOZ+1@2+5@;)P\@2
+M``"+1@0%!0`D_HE&!"T,`!O)]]$CP04,`(E&],=&]@``Z`,+B4;R_W;V_W;T
+MZ(3]@\0$B4;\B5;^B\(+1OQU#O]V\NCP"H/$`BO`F<G#4O]V_.@)_H/$!(M&
+M_(M6_HE&[HE6\,1>_":+!R:+5P(K1O0;5O:)1OB)5OH+TG4%/0P`<F6+PXS"
+M@.0/BL2*XHK6*O;1ZM'8T>K1V-'JT=C1ZM'8`\.#T@`#1O035O;1ZM'8T>K1
+MV-'JT=C1ZM'8BD[\`D[T@^$/B4[\B4;^BT;XBU;ZQ%[\)HD')HE7`O]V_O]V
+M_.@1_8/$!/]V\NA("HM&](M6]L1>[B:)!R:)5P*#1NX$BT;TBU;V`09$'Q$6
+M1A\I!FP'&19N!XM&[HM6\,G#R`X``(M&!(M6!H#D#XK$BN**UBKVT>K1V-'J
+MT=C1ZM'8T>K1V`-&!(/2`#D6$@!W%G(&.080`'<..186`'<-<@8Y!A0`<P6X
+M___)PX-N!`2+1@2+5@:)1O*)5O3$7O(FBP<FBU<"B4;\B5;^Z)@)B4;V_W;T
+M_W;RZ#G]@\0$B4;RB\(+1O)T"E+_=O+H1_R#Q`3_=O;H?@F+1OR+5OXI!D0?
+M&19&'P$&;`<1%FX'*\#)PU6+[(,^3A\`=!"#/A0?`'0)_W8$:@#_%A0?*\#)
+MPU6+[(,^3A\`=!&#/A0?`'0*_W8&_W8$_Q84'RO`R<.058OLC48&4/]V!&H`
+M:@#H*P#)PY!5B^R-1@A0_W8&_W8$:@#H%@#)PU6+[(U&"%#_=@9J`/]V!.@"
+M`,G#R`X``%=6BT8*B4;RBUX(_T8(B@>8B_B#_R5T,@O_=1(Y?@9T!HM>!L8'
+M`"O`7E_)PY"#?@8`=`J+7@:(!_]&!NO*5_]V!.A;_X/$!.N^BUX(@#\E=1*#
+M?@8`=`B+7@;&!R7KV6HEZ]N`/RUU"_]&",=&]@$`ZP:0QT;V``"+7@B`/S!U
+M"_]&",=&_`$`ZP:0QT;\``"+7@B`/RMU"_]&",=&^@$`ZP:0QT;Z``"+7@B`
+M/R!U"_]&",=&]`$`ZP:0QT;T``"+7@B`/R-U`_]&""OVZQ6#_SE_((O&P>`"
+M`\;1X`/'+3``B_"+7@C_1@B*!YB+^(/_,'W;@_\N=16+7@C_1@B*!YB+^(/_
+M,'P%@_\Y?NL]9`!U`^G[`'X#Z0P"/54`=0/I(P%^`^GG`2U$`'4#Z18!+0L`
+M=0/I#@'I'`*#?O8`=2S'1O@!`.L/D&H@_W8$Z$[^@\0$_T;X.7;X?1&#?@8`
+M=.>+7@;&!R#_1@;KYX-^!@!T'HM>\HH'BUX&B`?_1@:#?O8`=0/IS@''1O@!
+M`.LCD(M>\HH'*N10_W8$Z`#^@\0$Z]R0:B#_=@3H\OV#Q`3_1O@Y=OA\`^F<
+M`8-^!@!TY(M>!L8'(/]&!NODD&H`:@#_=O;_=OQ6:@*+7O)J`/\WC48&4/]V
+M!.AT`8/$%.EF`6H`:@#_=O;_=OQ6:A#KVI!J`&H`_W;V_W;\5FH(Z\J0_W;T
+M_W;Z_W;V_W;\5FH*BU[RBP>94E#KMY!J`&H`_W;V_W;\5FH*ZZ"0BUX(_T8(
+MB@>8B_B#_T%\"(/_6G\#@\<@B\<]:0!T5W]M+6(`=`9(2'1,ZR)J`&H`_W;V
+M_W;\5FH"BU[R_W<"_S>-1@90_W8$Z-D`@\04@T;R`NG'`)!J`&H`_W;V_W;\
+M5FH0Z]20:@!J`/]V]O]V_%9J".O$D/]V]/]V^NL$:@!J`/]V]O]V_%9J"NNL
+MD"UO`'33+08`=.8M`P!TN>NOD(M>\HL'B4;^BU[^_T;^B@>8B_@+_W1E@WX&
+M`'0+BUX&B`?_1@;KX9!7_W8$Z)7\@\0$Z]0M6`!U`^DO_RT*`'4#Z:[^2'4#
+MZ1;^ZS`M:0`]#P!W*-'@DR[_IRPH!B=,*$PH,"=,*$PH]B9,*$PH3"C0)TPH
+M("=,*$PHYB:#1O("Z:G\D,@F``!75HM&"(M6"HE&_(E6_@O2?0KW7OR#5OX`
+M]U[^*_:+1@R94E#_=O[_=OSHUS>+V`,>7@2*!XA"W$:+1@R94E"-1OQ0Z-@V
+M@W[^`'_2?`:#?OP`=<J#?@H`?0^#?A0`=0:#?A8`=`/_3@Z#?A(`=3&#?A``
+M=2N+_NL-D&H@_W8$Z+K[@\0$1SE^#GX5BUX&@S\`=.>+'\8'((M>!O\'Z^:0
+M@WX6`'4&@WX4`'08@WX*`'T2BUX&@S\`="J+'\8'+8M>!O\'@WX4`'0S@WX*
+M`'PMBUX&@S\`=!J+'\8'*XM>!O\'ZQEJ+?]V!.A0^X/$!.O3D&HK_W8$Z$+[
+M@\0$@WX6`'0>@WX4`'48@WX*`'P2BUX&@S\`=!N+'\8'((M>!O\'@WX2`'4_
+M@WX0`'0YB_[K&Y!J(/]V!.@"^X/$!.OBD&HP_W8$Z/3Z@\0$1SE^#GX5BUX&
+M@S\`=.>+'\8',(M>!O\'Z^:0C43_B4;:ZQB+7MJ-1MP#V(H'F%#_=@3HO/J#
+MQ`3_3MJ#?MH`?""+7@:#/P!TVHM>VHU&W`/8B@>+7@:+'X@'BUX&_P?KUX-^
+M$@!T*HO^ZPQJ(/]V!.A^^H/$!$<Y?@Y^%8M>!H,_`'3GBQ_&!R"+7@;_!^OF
+MD"O`7E_)PX,&@`<!@Q:"!P#K]`````````````#ZNB+_N`"`[[HH_[!M[NL`
+MNF;_,\#OZP`SP([8CM".P.H``/__B]R+5P**1P3NPXO<BU<",\#LPXO<BU<"
+MBT<$[\.+W(M7`NW#58OL5E<>!L1^",5V!(M.#(O&,\>I`0!U$_?&`0!T!@O)
+M=`ND2='I\Z6#T0#SI`<?7UY=PU6+[%<&Q'X$BTX(,\#1Z?.K<P&J!U]=PP#(
+M`@``:@!J9/\V(A__-B`?Z`DVB4;^:`'@:&;_Z(/_@\0$_W;^:&+_Z'?_@\0$
+M:@!J!&BH86H3Z,H#@\0(T6[^_W;^:&#_Z%G_@\0$ZPJ#!G('`8,6=`<`:&#_
+MZ$[_@\0".T;^=^CK"H,&<@<!@Q9T!P!H8/_H-/^#Q`([1OYRZ&H(:'('Z"HT
+M*\#)PP``````````````````'@93+HX><`&Z*O_M4(N'R@;O^_\&$A__EX0>
+M^KHB_[@`@.]8NBK_[X/$`@<?8<]@NP``Z\M@NP(`Z\5@NP0`Z[]@NP8`Z[E@
+MNP@`Z[-@NPH`ZZU@NPP`ZZ=@NPX`ZZ%@NQ``ZYM@NQ(`ZY5@NQ0`ZX]@NQ8`
+MZXE@NQ@`ZX-@NQH`Z7S_8+L<`.EU_V"['@#I;O]@NR``Z6?_8+LB`.E@_V"[
+M)`#I6?]@NR8`Z5+_8+LH`.E+_V"[*@#I1/]@NRP`Z3W_8+LN`.DV_V"[,`#I
+M+_]@NS(`Z2C_8+LT`.DA_V"[-@#I&O]@NS@`Z1/_8+LZ`.D,_V"[/`#I!?]@
+MNSX`Z?[^8+M``.GW_F"[0@#I\/Y@NT0`Z>G^8+M&`.GB_F"[2`#IV_Y@NTH`
+MZ=3^8+M,`.G-_F"[3@#IQOY@NU``Z;_^8+M2`.FX_F"[5`#IL?Y@NU8`Z:K^
+M8+M8`.FC_F"[6@#IG/Y@NUP`Z97^8+M>`.F._F"[8`#IA_Y@NV(`Z8#^8+MD
+M`.EY_F"[9@#I<OY@NV@`Z6O^8+MJ`.ED_F"[;`#I7?Y@NVX`Z5;^8+MP`.E/
+M_F"[<@#I2/Y@NW0`Z4'^8+MV`.DZ_F"[>`#I,_Y@NWH`Z2S^8+M\`.DE_F"[
+M?@#I'O[ZNBK_[8O8N`<`[_N+P\/ZNBK_[8O8N`8`[_N+P\/ZNBK_[8O8N`4`
+M[_N+P\/ZNBK_[8O8N`0`[_N+P\/ZNBK_[8O8N`,`[_N+P\/ZNBK_[8O8N`(`
+M[_N+P\/ZNBK_[8O8N`$`[_N+P\/ZNBK_[8O8N```[_N+P\/ZB]R+1P*Z*O_O
+M^\.Z*O_M]]`E!P##^L/[PYSZ6,.+W/]W`IW#S,,`PY!5B^S_=B#_=A[_=AS_
+M=AK_=AC_=A;_=A3_=A+_=A#_=@[_=@S_=@K_=@C_=@;_=@1HX`3H>O;)P\@(
+M``!6:`"`:"+_Z#[\@\0$:`"`:"+_Z#+\@\0$:/T`:"C_Z";\@\0$:@!H*O_H
+M&_R#Q`1J"&@X_^@0_(/$!&H(:#K_Z`7\@\0$:@AH//_H^ON#Q`1J"&@^_^CO
+M^X/$!"OVB][1X\>'A!X$+HO>T>/'A\H&``!&@_Y`?.8KP(E&^HE&^"OVCD;Z
+MB][1XXN'8`2+#@8`BU[X)HD')HE/`D:#1O@$@_Y`?.`KP%[)PU6+[(-^!`!\
+M$H-^!$!]#(-^"`!\!H-^"`=^!;C__\G#N`@`*T8(B48(/0<`?@7'1@@'`(M&
+M!HM>!-'CB8>$'HM&"$B+7@31XXF'R@:+1@3K;Y#_=@AH,O_H0_N+Y>F(`(-^
+M"@'U&\`E$``+1@A0:#C_Z^60@WX*`?4;P"40``M&"%!H.O_KT9"#?@H!]1O`
+M)1``"T8(4&@\_^N]D(-^"@'U&\`E$``+1@A0:#[_ZZF0_W8(:#3_ZZ#_=@AH
+M-O_KF"T(`#T+`'<@T>"3+O^GNB_2+](OFB^B+THO7B]R+X8OTB_2+](O/"\K
+MP,G#R`(``%=6BWX$@>?_`'P&.3X0'W\'N/__7E_)PVGWN`"!QH0']H2!`(!U
+MZO:$@``!=`8KP%Y?R<.`C($`$(U%`3L&0A]]`Z%"'Z-"'\=$!(`EQT0&``#'
+M1`B`)<=$"@``QD0V`,9$-`C&1#4`QT0,@!#'1`X``"O`B402B400B406B404
+MB40>B40<QT0D`0#'1"8!`,9$-Q/&1#@1QD0Y$\9$.A&A:`>)1"BA=@:)1"JA
+M9@>)1"RAQ`:)1"ZA"A^)1##'1OX``(M>_L9`/0#_1OZ#?OX+?/`KP(E$5HE$
+M5(E$6HE$6(E$7HE$7(E$9HE$9(E$:HE$:(E$;HE$;,:$F0`!QH28``")A(8`
+M4%!6Z)41@\0&5NAX$(/$`CET>'46BX24``4<`%#H?OF#Q`+'1OX``.L&D,=&
+M_@$`BUQXBX>4``4(`%#H8/F#Q`**3OZ`P0+3^"4!`#T!`!K`]MB(A)P`BUQX
+MBX>4``4(`%#H.?F#Q`**3O[3^"4!`#T!`!K`]MB(A)X`:B"+A)0`!00`4.@,
+M^8/$!&H!BX24``4$`%#H_/B#Q`1J!(N$E``%!`!0Z.SX@\0$@(R```&`I($`
+M[\1<=":#!P$F@U<"`.EC_I#(!```5U:+?@2!Y_\`?`8Y/A`??P>X__]>7\G#
+M:?>X`('&A`?VA(```74'*\!>7\G#D/:$@`"`=`=6Z-<3@\0".71X=0N+7'B`
+MIZ(`^>L)D(M<>("GH@"?BUQXBH>B`"KD4/^TC`#H9OB#Q`1J((N$E``%!`!0
+MZ%;X@\0$QH28``#HRON)1O['A*8```#'A+0```#'A+8```#'A*P```#'A*X`
+M``!6Z)<4@\0"*\")A((`B82``(F$A`!6Z`(/@\0"_W;^Z(O[@\0"H4(?2(E&
+M_.L0D&E>_+@`]H<$"`%U"?]._(-^_`!]ZXM&_$"C0A_I./^0R%0``%=6BD8$
+M*N2)1O@+P'P&.080'W\(N/__7E_)PY!I\+@`@<:$!\=&^@``BT8(Z2T#D/]V
+M#%;HBR"#Q`3I5P.0_W8,5NC!%.OPD(M&#(M6#HE&\(E6\E:-=`3$?O"Y(@#S
+MI5[I,`.+1@R+5@Z)1O")5O+$7O`F]D<$$'0;Z-GZB4;V@*2``/=6Z(T3@\0"
+M_W;VZ,CZ@\0"Q%[P)O9'!"!T&^BT^HE&]H",@``(5NB:$X/$`O]V]NBC^H/$
+M`L1>\";V!R!T&.B0^HE&]FH`5NB?$H/$!/]V]NB"^H/$`L1>\";V!Q!T&.AO
+M^HE&]FH!5NA^$H/$!/]V]NAA^H/$`L1>\";V1P1`=0/IBP)J`8O#!0D`4/]V
+M!O]V!.AP"(/$".ET`BO_@7X(!6%U!3E^"G0,@7X(!V%U"#E^"G4#OP$`@7X(
+M!6%U!H-^"@!T#8%^"`9A=0F#?@H`=0.#SP)75NAC'X/$!(M&#(M6#HE&\(E6
+M\E:-?JR-=`2,T([`N2(`\Z5>BT;PBU;R5AZ-?`2+\!X'CMJY(@#SI1]>C4:L
+M%E!6Z#X.@\0&Z>X!_W8._W8,5NC&'^ON_W8._W8,5N@*$^OB5N@J(H/$`E;H
+M:2"#Q`*+1@R+5@Z)1JR)5JY6C71(Q'ZLN08`\Z5>BUZL)L='!#\`)L='!@``
+M@&1)?^F9`9`K_X%^"`IA=04Y?@IT#(%^"`QA=0@Y?@IU`[\!`(%^"`IA=0:#
+M?@H`=`V!?@@+874)@WX*`'4#@\\"5U;HAQZ#Q`2+1@R+5@Z)1JR)5J[$7JPF
+M]D<$!'0:)HI'""4$`#T!`/4;P"4$``P!4%;H12*#Q`3$7JPF]D<$`74#Z1D!
+M)HI'""4!`#T!`/4;P"4$``P!4%;H[B+IHOV0BT8,BU8.B4:LB5:N5HUT5,1^
+MK+D(`.FO_9"+1@R+5@Z)1JR)5JY6'HU\5(OP'@>.VKD(`/.E'UZ-1`0>Z<;^
+MD(M&#(M6#HE&K(E6KHM$=(M4=E8>B_".VL1^K+DZ`/.E'UX&_W:L5NA`(.F=
+M_I"+1'8+1'1U`^F%`,1<=":+1W")1O9J=/]T=O]T=.@+]8/$!HM&]IG$7'0F
+MB4=P)HE7<O\V%@:-A+``4.AB(X/$!)G$7'0FB4<$)HE7!NM!D,=&^O__ZSF0
+M+0%A/1,`=_#1X),N_Z<*-LXRW#+F,@`TOC.^,[XS7#3<-)HTFC2:-#0U3C7R
+M-0(S=#5$-%`THC6+1OI>7\G#D,@,``!75HM>#(L'B4;VBUX$BX>H`(N7J@")
+M1OJ)5OR+1@XK1@H;R2/!`T8*B48.BT<,B4;XBD;X)0@`/0$`&\!`B4;^*_;K
+M.XI&])@M"@!T!RT#`'0:ZPKV1O@0=`3&1O0-BD;TQ%X&_T8&)H@'ZQ+V1O@@
+M=`;&1O0*Z^;V1OA`=.!&.78.?D+$7OJ+?O8FB@&(1O3_1O:+1O8Y!A0&=P7'
+M1O8``(!^]"!\G8!^]'Y_EX-^_@!TJX!^]$%\I8!^]%I_GX!&]"#KF9"+1O:+
+M7@R)!XM&#EY?R<.0R`@``%=6BD8$*N2)1OQI\+@`@<:$!_:$@``!=0>X__]>
+M7\G#BX2L`(E&_HN$K@")1OJ+1OXY1OIW"(OX*W[ZZPJ0B_@K?OH#/A0&.7PL
+M<P6`I(4`^_9$#'AT&U>-1OI0_W8,_W8*_W8(5NBY_H/$#(E&#.FM`(M&##O'
+M?@*+QXE&#`O`?P/IF@"A%`8K1OH[1@Q^`XM&#(E&^`O`=1%H+`/_-B(%_S;V
+M`.CT[(/$!J$4!CE&^'(1:"T#_S8B!?\V]@#HV^R#Q`;_=OC_=@K_=@B+A*@`
+MBY2J``-&^E)0Z*3R@\0*BT8,.4;X?1TK1OA0BT8(BU8*`T;X4E#_M*H`_[2H
+M`.A_\H/$"H-^#`!^%"M^#(M&#`%&^J$4!CE&^G(#*4;ZH10&.4;Z<A%H0@/_
+M-B(%_S;V`.AG[(/$!NBA]8E&_(M&^HF$K@"+1OXY1OIU"H"D@`"_@*2%`(`Y
+M?"YV!8"DA0#WQX2&````_W;\Z'/U@\0"BT8,"\!]`BO`F<1<="8!1P@F$5<*
+M.7PL<@[VA(``@'0'5N@-#8/$`HM&#%Y?R<.0R"8``%:+7@R+-XM>!(N'L`"+
+ME[(`B4;\B5;^H18&B4;>BT<0BU<2B4;FB5;HBH>D`"KDB4;@QT;T``#'1NP`
+M`(I&YB4!`#T!`!O`0(E&]O9&YX!T$?9&Z`%U"\9&\`#'1NH%`.L7]D;G@'4&
+M]D;H`70&QD;P?^OGQT;J`P#'1NP``.L)_TX._T;@_T;LBT8*.4;L?`/IK@+$
+M7@;_1@8FB@>(1MH\('PR/'Y_+H-^#@!U`^F1`H-^]@!T##QA?`@\>G\$@&[:
+M((I&VL1>_":(`$8Y=MYWKBOVZZJ+1N")1N3'1MP``,=&\@``BT;JB4;XBD;P
+MB$;ZBD;:F.D_`9#V1N9`=`K'1MP%`,=&\@$`@W[@`'\#Z4$!_T[@Z3L!D/9&
+MYA!T`^G.`/9&Y@)U`^FD`/9&Y@AT"8-^X`!U`^FV`,9&X@V`3O@(Z:L`]D;G
+M`G0BBD;@)0<`+0@`]]C1^(E&W`O`?@:X`@#K`Y`KP(E&\NLPD/9&YP1T#,=&
+MW`H`QT;R`@#K'?9&YPAT%\=&^`0`BD;@)0<`+0@`]]B)1O+&1OH@BT;@#`=`
+MB4;@Z:L`D/9&YH!U`^FA`,=&W,@`QT;R___IE`#V1N<!Z^CV1N8$=!;&1MH*
+M]D;F('1^QT;<"@#'1O("`.MR]D;F"'0)@W[@`'4#Z97^]D;G$'0;BT;@P?@"
+M!04`/0H`?0.X"@")1MS'1O("`.L>]D;G('0(QT;<"@#K[)#V1N=`=`K'1MP/
+M`,=&\@0`QT;@``#K'9`M"``]!0!W%-'@DR[_IP0[MCD".M8Y9CI\.H(ZQT;N
+M``#V1O@!=`/_1N[V1O@(=`/_1N[V1O@$=`:+1O(!1NZ+1@XY1NY^#XM&Y(E&
+MX,=&#@``Z;4`D(-^W`!^(/9&^`)T&H-^[`!U"XM>!/>'@``"('0)BT;DB4;@
+MZ8X`]D;X`701BD;:Q%[\)H@`1CEVWG<"*_;V1O@(=!&*1N+$7OPFB`!&.7;>
+M=P(K]H-^W`!^'O9&^`)T&(M>!("/@0`@BD;<BUX$B(>E`/]&[.L]D(-^\@!^
+M+/9&^`1T)HM&\D@!1O3K!)#_3O*#?O(`?A.*1OK$7OPFB`!&.7;>=^@K]NOD
+MBT;N*48.Z47]D(M&]`-&[)F+7@3$7W0F`4<$)A%7!HI&X(M>!(B'I`"+7@R)
+M-XM&[%[)P\@*``!75HI&!"KDB4;\:?"X`('&A`?VA(```74'N/__7E_)P^BE
+M\8E&^O:$@0`(=`>+A*8`ZP60BX2T`(E&_HN$M@")1OC_=OKHA/&#Q`*+1OXY
+M1OAW"XL^%@8K^`-^^.L%BW[X*_A/.4;X=06`I(0`@*$6!BO'0#M$*G,%@*2$
+M`/>#[P0Y/A8&<Q-HA@3_-B(%_S;V`.CSYX/$!BO_BT02"T00=!Q7C4;^4/]V
+M#/]V"O]V"%;HV/N#Q`R)1@SIO@"0BT8,.\=^`HO'B48,"\!_`^FJ`*$6!BM&
+M_CM&#'X#BT8,B4;V"\!U$6B6!/\V(@7_-O8`Z)3G@\0&H18&.4;V<A%HEP3_
+M-B(%_S;V`.A[YX/$!O]V]HN$L`"+E+(``T;^4E#_=@K_=@CH1.V#Q`J+1@PY
+M1O9]'2M&]E#_M+(`_[2P`(M&"(M6"@-&]E)0Z!_M@\0*@WX,`'XDBT8,F<1<
+M="8!1P0F$5<&BT8,`4;^BT;^.086!G<&H18&*4;^BT;^.086!G<1:*H$_S8B
+M!?\V]@#H]N:#Q`;H,/")1OKVA($`"'0*BT;^B82F`.L(D(M&_HF$M`"A%@8K
+MQT"+^#E\*',%@*2$`/OVA(``"G4'5NBX"(/$`O]V^NCS[X/$`HM&#%Y?R<.0
+M*\##D,@"``!6BW8(BD8$*N2)1@3'1@8``*$0'YD[5@9W#G(%.T8$=P>X__]>
+MR<.0:48$N``%A`>+V/:'@0"`=>@+]G41_S84!@6H`%#HXAJ#Q`1>R<.#_@%U
+M"?\V%@8%L`#KZ(/^`G4'H10&2%[)PX/^`W4%H18&Z_(KP%[)PY#(!@``5U:+
+M=@3VA($`('07BH2E`/Z,I0`*P'0&*\!>7\G#@*2!`-_VA(```G4<]H2```1T
+M#H!,:`*`C(``(("DA`"`@*2``/OKTO:$@0`(=`>+A*8`ZP60BX2T`(E&_HN$
+MM@")1OR+1OXY1OQW"(OX*W[\ZPJ0BSX6!BM^_`/X.7PH<ACVA(0`!'61@(R$
+M``2`3&@$@(R``"#K@9`Y?"IV`^EX__9$6`AU`^EO__:$A``(=`/I9?^`C(0`
+M"(!,:`CKTL@&``!75HMV!(N$K`")1OZ+A*X`B4;\BT;^.4;\=P>+^"M^_.L)
+MBSX4!BM^_`/XB\<KA(8`]H2%``%U&3F$B`!S!3E\)G<.@(R%``&`3&D!@(R`
+M`"`Y?"YW&/9$60AT,O:$A0`(=2N`C(4`"(!,:0CK&SE\+'(;]D19!'05]H2%
+M``1U#H",A0`$@$QI!(",@``@B;R&`"O`7E_)P\@(``!75HMV"/?&$`!T%XM>
+M!,1?=":#1R0!)H-7)@"+7@2`3VH0]\8@`'07BUX$Q%]T)H-'+`$F@U<N`(M>
+M!(!/:B#WQD``=!>+7@3$7W0F@T<H`2:#5RH`BUX$@$]J0/?&@`!T'HM>!,1?
+M=":#1V0!)H-79@"+7@2`3TF`BUX$@$]I@/?&\`!T"(M>!("/@``@BUX$BH>?
+M`"KD]]`C\(J'H``JY(7&=`<KP%Y?R<.0BX>H`(N7J@")1OR)5OZ+OZP`BX>N
+M`(E&^CO'=PNA%`8KQP-&^NL#D"O'B4;X_T[XBH>A`"KDA<9T2H-^^`-](HM>
+M!,1?=":#1S`!)H-7,@"+7@3$7W0F@T<T`2:#5S8`ZY?$7OPFQ@'_1SD^%`9W
+M`BO_Q%[\)L8!`$<Y/A0&=R,K_^L?]\;P`'09@W[X`7RP]\:``'0&BD<\ZP20
+MBD<[F(E&!HI&!L1>_":(`4<Y/A0&=P(K_XM>!(F_K`"+7@2`CX``0.DR_Y#(
+M`@``5U:+7@2+=WB-O+@`BIVC`"K_T>.+AVX%B4;^]X2```$0=`Z`3OX$]D0<
+M('0$@$[^`?>%@``!$'0.@$[^"/9%'"!T!(!._@+_=OZ+A)0`!0@`4.BVZ(/$
+M!%Y?R</(`@``5U:+=@2+A)0`B4;^@'PV`G4$*__K#X!\-@%U!K\$`.L$D+\0
+M`(!\-`5T((!\-`9U!8//`>L5@'PT!W4&@\\"ZPJ0@'PT"'4#@\\#]D0<$'0$
+M@<^``&H0BT;^!00`4.A*Z(/$!%?_=O[H0.B#Q`0K__9$'"!T`[\0`(!\-0)U
+M!8//#^L/@'PU`74&@\\(ZP20@\\']D08$'0$@<^``%?_=O[H!^B#Q`16Z/'^
+M@\0"7E_)P\@4``!75HMV!(M$>(E&_(U\!,=&_@``]D4)$'0$@$[^</9%"(!T
+M!(!._H"*1OZ(A)\`QT;V``#V10D@=`2`3O9P]D4)`70$@$[V@(I&]HB$H`#'
+M1O0``/9%"4!T!(!.]'"*1O2(A*$`]D44`70$@$T8$/9%%`)T!(!-&""#?20`
+M=`BA%@8Y121R!J%H!XE%)(-])@!T"*$6!CE%)G(&H78&B44F@WTH`'0(H10&
+M.44H=@:A9@>)12B#?2H`=`BA%`8Y12IR!J'$!HE%*H-]+`!T"*$4!CE%+'(&
+MH0H?B44LQT;X``#K!)#_1OB#?O@1?1F+!8M5`HM>^,'C`CF7+`5RYG<&.8<J
+M!7+>@W[X$74#_T[X@SXD!0!T!NA;ZHE&^HM&"`M&!G0.Q%X&)HM'&":+5QKK
+M!Y"+11B+51I24%;H?`&#Q`:+1@@+1@9T%8M%"(M5"L1>!B8Y1PAU!B8Y5PIT
+M5/9%"`%T&CEV_'4+BU[\@*>B`/GK(Y"+7OR`IZ(`G^L8.7;\=0N+7OR`CZ(`
+M!NL)D(M>_("/H@!@BU[\@(^B`("+7OR*AZ(`*N10_[2,`.A&YH/$!(M&"`M&
+M!G1"BD4RQ%X&)CA',G4VBD4P)CA','4MBT48BU4:)CE'&'4A)CE7&G4;BD4Q
+M)CA',742BT44BU46)CE'%'4&)CE7%G0'5NA&_8/$`HM%`@L%=0/IE@"+1@@+
+M1@9T$XL%BU4"Q%X&)CD'=08F.5<"='N*1OB(A*,`:@!J9&H`:@7_=0+_->A#
+M'%)0Z#X<0(F$E@`Y='AT'%;HE/R#Q`**G*,`*O_1X_^WD`6+A)0`0$#K,Y#_
+M=`;_=`1J`6@`PN@*'(E&\(I&\2KD4(N$E``%#`!0Z&GE@\0$BD;P*N10BX24
+M``4.`%#H5>6#Q`3HS`R#/B0%`'0)_W;ZZ,7H@\0"7E_)PU6+[%:+=@2+1!R+
+M5!XQ1@8Q5@CVA(``@'1$]D8&$'03]D0<$'0$:@+K`FH&5NCD$8/$!/9&!@)T
+M%/9$'`)T!6H`ZP.0:@%6Z(@`@\0$]D8&0'0I]D0<0'0::@+K&)#V1@80=`EJ
+M!E;HJA&#Q`3V1@9`=`EJ!E;H:Q*#Q`1>R<.058OL5U:+=@3VA(``@'0^Z"CH
+MB_CV1!P"=`EJ`5;H,@"#Q`3V1!P0=`EJ!E;H91&#Q`3V1!Q`=`EJ!E;H)A*#
+MQ`2`I(``?U?H].>#Q`)>7\G#D,@"``!75HMV!(-^!@!T%8I$.HA&_L1<=":#
+M1S@!)H-7.@#K$XI$.8A&_L1<=":#1SP!)H-7/@#_M)``Z#7D@\0"J`1T$8I&
+M_IA0_[22`.@8Y(/$!.M`B[RV`/:$@0`(=1B`C($`"(N$M`")A*8`B;RT`%;H
+M,@"#Q`(YO+0`=0D+_W4$BSX6!D_$G+``BD;^)H@!B;RV`,1<=":#1P0!)H-7
+M!@!>7\G#D%6+[%:+=@2#?`(`=2&A)@6)!,=$`B8%@SXF!0!T!XL>)@6)=P*)
+M-B8%@(R```)>R<.058OL5HMV!(-\`@!T)X,\`'0(BT0"BQR)1P*+!(M<`HD'
+MQP0``,=$`@``@(R```2`I(``_5[)PY#(!```5HMV!(M$'(M4'HE&_/9&_`)T
+M"6H`5NC1_H/$!/9&_!!T"6H"5N@$$(/$!/9&_$!T"6H"5NC%$(/$!(",@`"`
+M7LG#58OL5HMV!%;H&P^#Q`+$7@8KP":)1P(FB0?VA(``"'0(Q%X&)H!/`@'V
+M1!P@=`[V1%`(=0C$7@8F@$\"!%[)PU6+[%=6BW8$Z$7FB_B#?@;^=1UJ<(N$
+ME``%!`!0Z+'B@\0$@*2!`/O'A(H```#K-H",@0`$:F"+A)0`!00`4.B/XH/$
+M!(-^!O]T#HM&!KD*`"O2]_&)A(H`Q%QT)H-'8`$F@U=B`%?HZN6#Q`)>7\G#
+MD,@"``!75HMV"(-^!/]U!H-^!O]T$XM&!(M6!BK2/?__=0R!^@#_=08KP%Y?
+MR<.*1@0JY(E&_@O`?`BA$!\Y1OY\"+C__UY?R<.0:7[^N`"!QX0'"_9TZXL$
+MB45ZZ\I5B^Q75BO_B_?K$I#1__9&!<!T!('/@`#!9@0"1H/^"'SJB\?WT%Y?
+MR<-75O\VQ@;HS?^#Q`*+\/\VR`;HP?^#Q`*+^(,^R`8`=3^#/E`&`'4#Z?T`
+M:A=H@`+HI.&#Q`1J(&B"`NB9X8/$!&H':(("Z([A@\0$5FB"`NB$X8/$!&B`
+M`&B``NG#`)!J%6C``NAOX8/$!&H@:,("Z&3A@\0$:@-HP@+H6>&#Q`1J!VC"
+M`NA.X8/$!&C\`&C"`NA"X8/$!&B``&C``N@VX8/$!&H5:,0"Z"OA@\0$:B!H
+MQ@+H(.&#Q`1J`&C&`N@5X8/$!&H%:,8"Z`KA@\0$5FC&`N@`X8/$!&B``&C$
+M`NCTX(/$!&H5:,@"Z.G@@\0$:BAHR@+HWN"#Q`1J`6C*`NC3X(/$!&H%:,H"
+MZ,C@@\0$5VC*`NB^X(/$!&B``&C(`NBRX(/$!%Y?PY#(&```5U:A$!\]$`!^
+M`[@0`(E&^,=&Z@``*__I]`%J=%+_='3HW."#Q`:+QR3^B4;^:<"X``6$!XE&
+M]HO'T>`%U`6)1O2+1O[1X`74!8E&Z(O8BP<%"@")A(X`BU[HBP<%"@")A(P`
+MBU[HBP<%"@"+7O:)AXX`BU[HBP<%"@"+7O:)AXP`BU[TBP>)A)0`BU[HBP>+
+M7O:)AY0`BU[TBP=`0(F$D`"+7O2+!P4&`(F$D@"+1O:)1'B+QRKDF8#._XE$
+M?(E4?HO'N1``F??YB\>+RIDSPBO"P?@$,\(KPHO8T>.X`(#3Z(6'Q@9U`^D!
+M`H-^Z@!]`^GX`8M&[(M6[M'JT=C1ZM'8T>K1V-'JT=B*3NR#X0^)C+``B82R
+M`,>$I@```,>$M````,>$M@```*$6!BO2`4;L$5;NBT;PBU;RT>K1V-'JT=C1
+MZM'8T>K1V(I.\(/A#XF,J`")A*H`QX2L````QX2N````H10&*](!1O`15O(Y
+M='AU3U*+A)0`!1H`4.@FWX/$!&HCBX24``4<`%#H%M^#Q`1JW(N$E``%'@!0
+MZ`;?@\0$:-T`BX24`$!`4.CVWH/$!&IOBX24``4(`%#HYMZ#Q`0Y='AU!2K)
+MZP.0L0&`P0*X`0#3X%"+7'B+AY0`!1X`4.C`WH/$!#ET>'4%*LGK`Y"Q`;@!
+M`-/@4(M<>(N'E``%'@!0Z)W>@\0$1SD^$!]_`^GE`&GWN`"!QH0']\</`'0#
+MZ:P`BT;X]R84!@40`%#HGM:#Q`*)1O")5O*+1OCW)A8&!1``4.B'UH/$`HE&
+M[(E6[HM&\@M&\'0'B\(+1NQU!<=&ZO__BT;PBU;R@.0/BL2*XHK6*O;1ZM'8
+MT>K1V-'JT=C1ZM'8`T;P@](`!0\`@](`)/")1O")5O*+1NR+5NZ`Y`^*Q(KB
+MBM8J]M'JT=C1ZM'8T>K1V-'JT=@#1NR#T@`%#P"#T@`D\(E&[(E6[FITZ/O5
+M@\0"B41TB51VB\(+1'1T`^DN_<>$@```@,>$@@```.D2_Y"A%`;!X`*Y!0`K
+MTO?QH\0&H10&*P;$!CT0`'8(H10&+1``ZP6A%`9(2*,*'Z'$!CD&"A]S!J$*
+M'Z/$!J$4!M'HHVH'H10&*]+W\:-F!Z$6!BO2]_&C:`>A%@;!X`(KTO?QHW8&
+M*\!>7\G#R!8``%=6QT;X``+'1OH``,1>^":+1PZCQ@8FBT<:H\@&)HM'$*-0
+M!J'&!HE&\*'(!HE&\L=&]!\`ZQJ0BT;TF3/"*\+!^`0SPBO"B_C1Y]%K\/].
+M](-^]`!\&8M&])DSPBO"P?@$,\(KPHOXT>?V0_`!=,B+1O1`HQ`?/2``?@;'
+M!A`?(`"#?O(`=2#'1O0``(M>]-'CBX>T!8M>]-'CB8?4!?]&](-^]!!\Y<<&
+M)@4``,<&*`4``&H`:@5H?EIJ".CRX(/$".B@^H,^$!\`?E^#/E`&`'0Z:@!J
+M!6A]7FH@Z-+@@\0(:@!J!6B07FHAZ,/@@\0(:@!J!6@0/FH-Z+3@@\0(:B)H
+M.O_H1]R#Q`3K'FH`:@5H?5YJ#>B8X(/$"&H`:@5HD%YJ#NB)X(/$"(,^$!\$
+M?AYJ`&H%:*->:B+H<^"#Q`AJ`&H%:+9>:B/H9."#Q`B#/A`?"'X>:@!J!6C)
+M7FHDZ$[@@\0(:@!J!6C<7FHEZ#_@@\0(@SX0'PQ^'FH`:@5H[UYJ)N@IX(/$
+M"&H`:@5H`E]J)^@:X(/$"(,^$!\0?GAJ`&H%:!5?:BCH!."#Q`AJ`&H%:"%?
+M:BGH]=^#Q`AJ`&H%:"U?:BKHYM^#Q`AJ`&H%:#E?:BOHU]^#Q`AJ`&H%:$5?
+M:BSHR-^#Q`AJ`&H%:%%?:BWHN=^#Q`AJ`&H%:%U?:B[HJM^#Q`AJ`&H%:&E?
+M:B_HF]^#Q`BA(!^+%B(?B4;\B5;^QT;T``!J`&H*_S90!?\V3@7HCQ%24/]V
+M_O]V_.@Z$(M>]-'CB8=4!O]&](-^]`I\U,=&]`H`:@!J"HM>],'C`O^W+`7_
+MMRH%Z%@14E#_=O[_=OSH`Q"+7O31XXF'5`;_1O2#?O01?,['!A0&``#'!A8&
+M``"#/A`?`'YGH1`?T>"94E"#/A`?$'X%N%``ZP.X*`"9BPYL!XL>;@<KR!O:
+M:P80'WB9*\@;VE-1Z/40B4;JB5;L"])U!3WP#W8(QP84!O`/ZQ<+TG4./4``
+M<PG'!A0&0`#K!I`D\*,4!J$4!J,6!NB&^;Z$!\=$!(`EQT0&``#&1#0(QD0V
+M`,9$-0`KP(E$'HE$'%!05N@1\H/$!L=&]```Z)KM_T;T@W[T9'STQT;J(`#'
+M1NP``*$&`,1>ZB;'!S!:)HE'`L=&ZDP`QT;L``"A!@#$7NHFQP=&6B:)1P+'
+M!B0%`0`KP%Y?R<.0R`0``%:+=@2#?'H`=`F+7'J#?PP`=0N`I(``WRO`7LG#
+MD(M$5(M45B%$9"%49HM$6(M46B%$:"%4:HM$7(M47B%$;"%4;HM$9@M$9'40
+MBT1J"T1H=0B+1&X+1&QTN8U$9!Y0:@!H#V'_='[_='R+7'K_5PR#Q`PKP(E$
+M9HE$9(E$:HE$:(E$;HE$;.N*D,@"``!75FD&0A^X``6$!XE&_BO_:38P!K@`
+M@<:$!^M@.7;^=P.^A`?VA(```71,]H2```9T!U;H"NV#Q`+VA(``0'0'5NB^
+M[8/$`H"\F@``=`=6Z,@`@\0"@[R*``!T#?^,B@!U!U;H"@>#Q`+VA(``('0'
+M5NCZ_H/$`H'&N`!'.3Y"'W^:H4(?.08P!GP-QP8P!@``*\!>7\G#D/\&,`8K
+MP%Y?R</(`@``5U:^A`?'1OX``"O_ZQKVA(```70.BH2C`"KD.T;^=@.)1OY'
+M@<:X`#D^$!]_X*&R!3E&_G0YBT;^H[(%"\!U!E!H5O_K(V@!X&A6_^A#V(/$
+M!(M>_M'C_[=4!FA2_^@QV(/$!&H`:%#_Z";8@\0$7E_)PY#(!```5HMV!.A_
+MVXE&_HM<>(N'E``%"`!0Z/G7@\0""H2:`(A&_,:$F@``_W;^Z%W;@\0".71X
+M=0N`9OQ5:@%J!.L)D(!F_*IJ`FH(BD;\4%;H!@"#Q`A>R</(`@``5HMV!(I&
+M"")&!CP!&L#VV(B$G`"*1@HB1@8\`1K`]MB(A)X`BD8&P.@$B$;^A$8(=!?V
+M1!A`=1'$7'0F@T=(`2:#5TH`@$QL`HI&_H1&"G0J]D08"'03Q%QT)H-'2`$F
+M@U=*`(!,;`+K$<1<=":#1U`!)H-74@"`3&P(5NA#`X/$`HM$4(M44HE$<(E4
+M<H",@``@*\!>R</(`@``5U:+=@3V1@8"=$KH@MJ)1O[_-A8&C82P`%#HP06#
+MQ`2+^)G$7'0F*4<$)AE7!H"DA`#`]H2!``AT"HN$M`")A*8`ZPB+A+8`B82T
+M`/]V_NA"VH/$`O9&!@%T4.@RVHE&_O\V%`:-A*@`4.AQ!8/$!(OXF<1<="8!
+M1P@F$5<*QX2L````QX2N````@*2%`,"`I(``O_]V_NCZV8/$`O:$@`"`=`=6
+MZ*[Q@\0"7E_)PY#(`@``5HMV!*$6!DC$7@8FB4<$H10&2,1>!B:)1P;HO]F)
+M1O[_-A8&C82P`%#H_@2#Q`3$7@8FB0?_-A0&C82H`%#HZ02#Q`3$7@8FB4<"
+M_W;^Z(_9@\0"Q%X&*\`FB4<*)HE'"/:$@``"=`:X`0#K!)"X`@`KTL1>!B8)
+M1PCVA(``0'0%N``!ZP.X``+$7@8F"4<(]H2``(!T!;@`0.L"*\#$7@8F"4<(
+M]H2```AT",1>!B:`3PH!7LG#D,@(``!75HMV!(U\2(IE`24`@(D%QT4"``#H
+M"-F)1OS_-A8&C82P`%#H1P2#Q`2)1OC_-A0&C82H`%#H-02#Q`2)1OK_=OSH
+MW]B#Q`+VA(```G0%N`$`ZP.X`@`KT@D%]H2!``1T!KB``.L#D"O`"07VA(``
+M"'0$@$T"`?9$'"!T"O9%"`AU!(!-`@3V10('=`.`#4"+1O@Y1"AR`X`-!(M&
+M^#E$*G<#@`T(H18&*T;X2'4#@`T0]H2``$!T!;@``>L#N``""07VA(``@'0&
+MN`!`ZP.0*\`)!8M&^CE$+'($@$T!!(M&^CE$+G<$@$T!"(M&^CE$,'<$@$T!
+M(*$4!BM&^DAU!(!-`1!>7\G#D%6+[%:+=@0+]G4#Z9L`BT8("T8&=0/ID`!6
+MZ(\`@\0"5NC._H/$`HM$2(M42L1>!B:)1VPFB5=NBT10BU12Q%X&)HE':":)
+M5VK_-A8&C82P`%#H#P.#Q`29Q%X&)HE'#":)5P[_-A0&C82H`%#H]`*#Q`29
+MQ%X&)HE'$":)5Q+$7@8FBT<,)HM7#B8I1P0F&5<&Q%X&)HM'$":+5Q(F`4<(
+M)A%7"H!D27]>R</("```5HMV!(I$4"4$`(E$4,=$4@``BUQXBX>4``4(`%#H
+MT=.#Q`*)1OXY='AU!\=&_```ZP7'1OP!`(I._(#!`M/X)0$`/0$`&\#WV(E&
+M^(I._(M&_M/X)0$`/0$`&\#WV(E&^O9$&`AT"X!,4`B#?OH`ZPV0"\!T!(!,
+M4`B#?O@`=`2`3%`"]D080'0$@$Q0`O9$&`1T"("\G0``ZQB0@+R=``!T!X!,
+M4`3K!9"`9%#[@+R;``!T!(!,4`&`3%`07LG#R`8``%=6BW8$BWX&]D08!'0#
+MZ;8`]\<"`'4&Z(C6B4;Z]\<!`'0'QT;\``'K!<=&_``"QT;^``#WQP0`=!2+
+M1OR+5O[WT/?2(82``"&4@@#K#HM&_(M6_@F$@``)E((`]\<$`'0BQH2=``$Y
+M='AU!"K)ZP*Q`;@!`-/@4(M<>(N'E``%'`#K(,:$G0``.71X=00JR>L"L0&X
+M`0#3X%"+7'B+AY0`!1X`4.A[TH/$!,1<=":#1U0!)H-75@#WQP(`=0G_=OKH
+MXM6#Q`)>7\G#D,@$``!75HMV!(M^!O?'`@!U!NC!U8E&_O9$&`1T",=&_```
+MZP:0QT;\`@#WQP0`="?&A)L``3ET>'4%*LGK`Y"Q`0)._+@!`-/@4(M<>(N'
+ME``%'`#K))#&A)L``#ET>'4$*LGK`K$!`D[\N`$`T^!0BUQXBX>4``4>`%#H
+MV-&#Q`3$7'0F@T=,`2:#5TX`]\<"`'4)_W;^Z#_5@\0"7E_)PU6+[%:+=@1J
+M<(N$E``%!`!0Z*'1@\0$@*2!`/O'A(H```!>R<.058OL5U:+=@3H`]6+^("D
+M@`#O:@2+A)0`!00`4.APT8/$!%?H[-2#Q`)>7\G#D%6+[%=6BW8$Z-74B_B`
+MC(``$&H(BX24``4$`%#H0M&#Q`17Z+[4@\0"7E_)PY!5B^Q75HM>!(MW!(M_
+M!CO^=PB+QBO'7E_)PXM&!BO'`\9>7\G#D,@&``!75HMV!.B`U(OX:<:X``6$
+M!XE&^HO8BX>``(N7@@")1OSV1OP0=`=3Z$__@\0"@WX&"G4IB][1XXN'U`5`
+M0%#HT="#Q`*H!'3K:@V+WM'CBX?4!04&`%#HK]"#Q`2+WM'CBX?4!4!`4.BH
+MT(/$`J@$=.O_=@:+WM'CBX?4!04&`%#HA="#Q`3V1OP0=!Z+WM'CBX?4!4!`
+M4.AXT(/$`J@$=.O_=OKH`/^#Q`)7Z-W3@\0"7E_)PP``````````````````
+M8!X&+HX><`'H0P"Z(O^X`(#O!Q]ASV`>!BZ.'G`!NB+_N`"`[XL64`"#^@!U
+M'/\&4`"Z*O_M4+@$`._[Z#D'^EBZ*O_O,\"C4``''V'/NR8%BQ^#^P!T+HN7
+MD`#LJ`1T\(N/M`"+O[8`._ET'L2WL``FB@6+EY(`[D<[/A8&=`>)O[8`Z\O#
+M,__K]?>'@```"'0:BX^F`(F/M`"!IX``__<[^70(]X>```@`=,"+?P*+-XDU
+M@_X`=`.)?`+'1P(``,<'``"#CX``!(.G@`#]B][K@(.E@`#W@WT"`'484U:[
+M)@6+-X/^`'0#B7P"B3^)-8E=`EY;PX.-@``(]X6````(=1Q35HM=`HLUB3>#
+M_@!T`XE<`EY;QT4"``#'!0``PXN5E`"#P@CLBN`D4'0$"(6:`(OW@<:X`(#D
+MH'0$"*2:`.L7D(N5C``SP.Z+E8X`["*%H@"+Z*B`=<;WQ08`=0/I9P'$A:@`
+MB[6L`(N=K@`[\W('B\8KP^L(D"O>H10&*\,[12YR$?>%@`"``'4)!E?H6NN#
+MQ`('BYV4`(/#!HN-D`"+T>R*X/;$`74#Z?,`B]/L]L3P=`/IS``\`'4']T4.
+M`@!UW?=%#`0`=`(D?XI5'/;"`74#ZU"0BK6``#I%.70M.D4Z=!#VQ@AT//;"
+M!'0WZ-[^ZS*0B],&Q%UT)H-'0`$F@U="``>+VNC%_NN3B],&Q%UT)H-'1`$F
+M@U=&``>+VNC1_NEZ__:%H0#_=!@\_W44)H@$1CLV%`9U!"LV%`8[M:X`=!<F
+MB`1&.S84!G4$*S84!CNUK@!T`^E$_U<&Q'UT)H-%,`$F@U4R`":#130!)H-5
+M-@`'7X/^`'4$BS84!D[I'/^)M:P`8#+VBM12,N105^@FXX/$!F&+M:P`Z0#_
+MB;6L`/?%!`!T"HN5E`"#P@2P4.Z#C8``0("]F```=0B*A9D`B(68`/?%8`!U
+M`^EO`8''N`#$A:@`B[6L`(N=K@`[\W('B\8KP^L(D"O>H10&*\,[12YR$?>%
+M@`"``'4)!E?HYNF#Q`('BYV4`(/#!HN-D`"+T>R*X/;$`74#Z?,`B]/L]L3P
+M=`/IS``\`'4']T4.`@!UW?=%#`0`=`(D?XI5'/;"`74#ZU"0BK6``#I%.70M
+M.D4Z=!#VQ@AT//;"!'0WZ&K]ZS*0B],&Q%UT)H-'0`$F@U="``>+VNA1_>N3
+MB],&Q%UT)H-'1`$F@U=&``>+VNA=_>EZ__:%H0#_=!@\_W44)H@$1CLV%`9U
+M!"LV%`8[M:X`=!<FB`1&.S84!G4$*S84!CNUK@!T`^E$_U<&Q'UT)H-%,`$F
+M@U4R`":#130!)H-5-@`'7X/^`'4$BS84!D[I'/^)M:P`8#+VBM12,N105^BR
+MX8/$!F&+M:P`Z0#_B;6L`/?%0`!T"HN5E`"#P@2P4.Z#C8``0("]F```=0B*
+MA9D`B(68`('ON`"+E8P`BH6B`.[#@S[(!@!T!KK$`K`@[K^$!^GE_(,^R`8`
+M=`:ZQ`*P(.Z_]`CITOR#/L@&`'0&NL0"L"#NOV0*Z;_\@S[(!@!T!KK$`K`@
+M[K_4"^FL_(,^R`8`=`:ZQ`*P(.Z_1`WIF?R#/L@&`'0&NL0"L"#NO[0.Z8;\
+M@S[(!@!T!KK$`K`@[K\D$.ES_(,^R`8`=`:ZQ`*P(.Z_E!'I8/RZR`*P(.Z_
+M!!/I5/RZR`*P(.Z_=!3I2/RZR`*P(.Z_Y!7I//RZR`*P(.Z_5!?I,/RZR`*P
+M(.Z_Q!CI)/RZR`*P(.Z_-!KI&/RZR`*P(.Z_I!OI#/RZR`*P(.Z_%!WI`/P`
+M58OLBUX$_W8(_W8&_W<"_S?H+@"+7@2)5P*)!XOE7<(&`%6+[(M>!(L'BU<"
+MBTX&Z/,!BUX$B0>)5P*+Y5W"!```58OL5U93,_^+1@8+P'T2]]>+5@3WV/?:
+M'0``B48&B58$BT8*"\!]$O?7BU8(]]CWVAT``(E&"HE6"`O`=1:+3@B+1@8S
+MTO?QB]B+1@3W\8O3ZSR0B]B+3@B+5@:+1@31Z]'9T>K1V`O;=?3W\8OP,])2
+M4/]V"O]V".@T`3M6!G<'<@@[1@1V`X/N`3/2B\8+_W0']]KWV(/:`%M>7XOE
+M7<((``!5B^Q35S/_BT8&"\!]$O?7BU8$]]CWVAT``(E&!HE6!(M&"@O`?1"+
+M5@CWV/?:'0``B48*B58("\!U&HM."(M&!C/2]_&+1@3W\8O",](+_W5%ZTJ0
+MB]B+3@B+5@:+1@31Z]'9T>K1V`O;=?3W\3/24E#_=@K_=@CHD0`[5@9W!W(+
+M.T8$=@8K1@@;5@HK1@0;5@8+_W4']]KWV(/:`%];B^5=P@@`58OL4U:+1@H+
+MP'46BTX(BT8&,]+W\8O8BT8$]_&+T^L\D(O(BUX(BU8&BT8$T>G1V]'JT=@+
+MR77T]_.+\#/24E#_=@K_=@CH'``[5@9W!W((.T8$=@.#[@$STHO&7EN+Y5W"
+M"```58OLBT8&BUX*"]B+7@AU"XM&!/?CB^5=P@@`]^.+R(M&!/=F"@/(BT8$
+M]^,#T8OE7<((`#+MXP;1X-'2XOK#`(,&O`8!@Q:^!@#_!G`'@SYP!V1S`^F!
+M`(,&=@<!@Q9X!P#'!G`'``"A=`<+!G('="C_-G0'_S9R!VH`:F2A<@>+%G0'
+M*P:`!QL6@@=24.AO_U)0Z`3_HT@?@SY('P!\!X,^2!]D?@;'!D@?``"#/DX?
+M`WP-_S9('VA`!NB.PH/$!(,^3A\"?`EH0P;H?L*#Q`(KP*."!Z.`!^F2J```
+*`````````````.B.
+`
+end
diff --git a/usr.sbin/stallion/bootcode/Makefile b/usr.sbin/stallion/bootcode/Makefile
new file mode 100644
index 0000000..415143a
--- /dev/null
+++ b/usr.sbin/stallion/bootcode/Makefile
@@ -0,0 +1,34 @@
+# $Id: Makefile,v 1.6 1997/03/13 04:21:21 davidn Exp $
+
+FILES = 2681.sys cdk.sys
+
+MAN4 = stl.4
+
+LIBMODE = 444
+LIBOWN = bin
+LIBGRP = bin
+CLEANFILES= ${FILES}
+MLINKS = stl.4 stli.4
+
+all: $(FILES)
+
+install: maninstall
+ @if [ ! -d ${DESTDIR}${BOOTDIR} ]; then mkdir ${DESTDIR}${BOOTDIR};fi
+ @for i in ${FILES}; do \
+ ${ECHO} "installing $$i into ${DESTDIR}${BOOTDIR}"; \
+ ${INSTALL} ${COPY} -m ${LIBMODE} -o ${LIBOWN} -g ${LIBGRP} \
+ $$i ${DESTDIR}${BOOTDIR}; \
+ done
+
+clean:
+ rm -f ${CLEANFILES}
+
+.include <bsd.prog.mk>
+
+cdk.sys: ${.CURDIR}/cdk.sys.uu
+ @rm -f $@
+ uudecode ${.CURDIR}/$@.uu
+
+2681.sys: ${.CURDIR}/2681.sys.uu
+ @rm -f $@
+ uudecode ${.CURDIR}/$@.uu
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..8717449
--- /dev/null
+++ b/usr.sbin/stallion/bootcode/stl.4
@@ -0,0 +1,324 @@
+.\" Copyright (c) 1996 Greg Ungerer (gerg@stallion.oz.au).
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by Greg Ungerer.
+.\" 4. Neither the name of the author nor the names of any co-contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.Dd December 2, 1996
+.Os FreeBSD
+.Dt STL 4 i386
+.Sh NAME
+.Nm stl ,
+.Nm stli
+.Nd "drivers for Stallion Technologies multiport serial controllers"
+.Sh SYNOPSIS
+.Cd "stl0 at isa? port <addr> tty irq <irq> vector stlintr"
+.Cd "stli0 at isa? port <io-addr> tty iomem <mem-addr> iosiz <size> flags <type> "
+.Cd "stli0 at eisa? port <io-addr> tty iomem <mem-addr> iosiz <size> flags <type> "
+.Sh DESCRIPTION
+This is a kernel driver for Stallion Technologies multiport serial boards.
+There are two drivers, each supporting a different class of boards.
+The
+.Nm stl
+driver supports the EasyIO and EasyConnection 8/32
+boards, while the
+.Nm stli
+driver supports all other types, including
+ONboard, Brumby and EasyConnection 8/64.
+.Sh CONFIGURATION
+Each board installed in the system needs a configuration entry in the
+kernel configuration file.
+Slightly different options and parameters are required for each of the
+different board types.
+Depending on the type of board one of the
+.Nm stl
+or
+.Nm stli
+drivers will be used. The
+.Nm stl
+and
+.Nm stli
+drivers can support up to 8 boards.
+.Pp
+Configuration of the hardware - DIP switches, jumpers, etc - varies
+from board to board.
+Consult documentation supplied with the board for hardware
+configuration details.
+Alternatively the board documentation is available on Stallion
+Technologies WWW site at http://www.stallion.com.
+.Pp
+The EasyIO and EasyConnection 8/32 families of boards use the
+.Nm stl
+driver.
+ISA board configuration entries for the
+.Nm stl
+driver take the general form of:
+.Pp
+.Cd "stlX at isa? port <io-addr> tty irq <irq> vector stlintr"
+.Pp
+.Ar X
+is the unit number assigned to the board.
+Any unique value between 0 and 7 is valid.
+.Pp
+The I/O address used by the board is specified by
+.Ar <io-addr> .
+Each of the EasyIO and EasyConnection 8/32-AT boards can use
+an I/O address in the range from 0 to 0x400.
+.Pp
+All EasyIO and EasyConnection 8/32 boards require an interrupt,
+and this interrupt is specified by
+.Ar <irq> .
+Legal IRQ values for the ISA boards are 3, 4, 5, 7, 10, 11, 12 and 15.
+Interrupts are software programmed on all boards except the EasyIO-8M.
+.Pp
+The EasyConnection 8/32-AT board uses a secondary I/O address region,
+and this is fixed at address 0x280 in the driver code.
+All EasyConnection 8/32-AT boards may share the same secondary address
+region.
+.Pp
+EasyConnection 8/32 PCI boards are detected automatically by the
+system on boot up.
+No configuration information is required in advance for these
+board types.
+During boot up the
+.Nm stl
+driver will issue messages to indicate that a EasyConnection 8/32
+PCI board was found, and some information about it.
+.Pp
+Following are some examples of configuration entries for each of the ISA
+boards supported by the
+.Nm stl
+driver.
+Each example also describes some important details about each of the
+board types.
+.Pp
+Each EasyIO board requires 8 bytes of I/O address space and 1 IRQ line.
+A configuration entry for an EasyIO board would look like:
+.Pp
+.Cd "stl0 at isa? port 0x2a8 tty irq 15 vector stlintr"
+.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 vector stlintr"
+.Pp
+This specifies an EasyConnection 8/32-AT with primary I/O address 0x2a0,
+secondary I/O address of 0x280 and IRQ 10.
+.Pp
+The ONboard, Brumby and EasyConnection 8/64 families of boards use the
+.Nm stli
+driver. The
+.Nm stli
+driver supports the ISA and EISA members of these families.
+.Pp
+ISA board configuration entries for the
+.Nm stli
+driver take the general form of:
+.Pp
+.Cd "stliX at isa? port <io-addr> tty iomem <mem-addr> iosiz <size> flags <type>"
+.Pp
+.Ar X
+is the unit number assigned to the board.
+Any unique value between 0 and 7 is valid.
+.Pp
+The I/O address used by the board is specified by
+.Ar <io-addr> .
+Each of the different supported board types has restrictions on valid
+I/O addresses and also the amount of I/O space required varies between
+the boards.
+.Pp
+All boards using the
+.Nm stli
+driver require a shared memory region to operate.
+Depending on the board type the region required varies in size
+from 4 kbytes to 64 kbytes. The size of the board region is specified
+by field
+.Ar <size>
+of the configuration entry, and the address of the region is specified by
+.Ar <mem-addr> .
+.Pp
+The flags field specifies the particular board type that this entry
+applies to.
+Not all board types are distinguishable by the driver at runtime,
+so this field is required by the driver.
+Valid board types are:
+.Bd -literal -offset indent
+BOARD NAME TYPE I/O SIZE
+
+Brumby 2 0x4000
+ONboard 4 0x10000
+ONboard/E 7 0x10000
+EasyConnection 8/64-AT 23 0x1000
+EasyConnection 8/64-EISA 24 0x10000
+.Ed
+.Pp
+Following are some examples of configuration entries for each of the
+boards supported by the
+.Nm stli
+driver. Each example also describes some important details about
+each of the board types.
+.Pp
+The EasyConnection 8/64-AT board requires 4 bytes of I/O address space and
+4 kbytes of memory space.
+A configuration entry would look like:
+.Pp
+.Cd "stli0 at isa? port 0x2a0 tty iomem 0xcc000 iosiz 0x1000 flags 23"
+.Pp
+The flags field of this entry specifies that this is an
+EasyConnection 8/64-AT board.
+It is set to I/O address 0x2a0 and memory address 0xcc000.
+The
+.Ar iosiz
+parameter specifies a memory region size
+of 4 kbytes.
+.Pp
+The EasyConnection 8/64-EISA board requires a 64 kbyte region of
+memory space.
+This region can be anywhere in the 32 bit memory address space.
+A configuration entry would be like:
+.Pp
+.Cd "stli0 at eisa? port 0x2000 tty iomem 0x80000000 iosiz 0x10000 flags 24"
+.Pp
+The flags field is used to specify that this is an EasyConnection 8/64-EISA
+board.
+The I/O (port) address resource is derived from the EISA slot that
+the board is in.
+Each EISA slot is allocated a section of the I/O address space by the
+hardware of the system.
+That address being 0xX000 where X is the slot number.
+The example board is at memory address 0x80000000 which is 2 Gbyte.
+The
+.Ar iosiz
+parameter specifies the size of the memory region,
+in this case 64 kbytes.
+.Pp
+Each ONboard ISA board requires 16 bytes of I/O space and a 64 kbyte
+section of memory address space.
+Valid ONboard I/O addresses are in the range 0x200 to 0x300.
+A configuration entry for an ONboard ISA would look like:
+.Pp
+.Cd "stli0 at isa? port 0x240 tty iomem 0xd0000 iosiz 0x10000 flags 4"
+.Pp
+This entry specifies an ONboard ISA by setting flags to 4.
+It uses I/O address 0x240 and a memory region of 64 kbytes at memory
+address 0xd0000.
+.Pp
+Each ONboard/E board requires a 64 kbyte memory region, and this
+can be anywhere in the 32 bit address space (that is from 0 to 4 Gbyte).
+A configuration entry would look like:
+.Pp
+.Cd "stli0 at eisa? port 0x3000 tty iomem 0xc0000000 iosiz 0x10000 flags 7"
+.Pp
+The specifies an ONboard/E in slot 3 using a shared memory address
+of 0xc0000000 (3 Gbyte).
+.Pp
+Each Brumby board requires 16 bytes of I/O address space and a 4 kbyte
+region of shared memory space.
+The valid Brumby I/O addresses are in the range 0x300 to 0x400.
+The shared memory region of the Brumby must be in the 0xc0000 to
+0xdc000 region of the memory address space.
+A configuration entry for a Brumby would be like:
+.Pp
+.Cd "stli0 at isa? port 0x360 tty iomem 0xc8000 iosiz 0x4000 flags 2"
+.Pp
+This specifies a Brumby board at I/O address 0x360 using a shared memory
+region at address 0xc8000.
+.Sh NOTES
+When building the device nodes for the ports be sure to use the correct
+driver name,
+.Nm stl
+or
+.Nm stli.
+Each driver has a separate major number allocated,
+so even though the port device names are the same for each driver,
+the major number of the device node is different.
+Use the
+.Xr MAKEDEV 8
+script to create the devices.
+Use the ttyE and cue tag for the
+.Nm stl
+driver, and
+the ttyEi and cuei tags for the
+.Nm stli
+driver.
+.Pp
+The intelligent board types (ONboard, Brumby and EasyConnection 8/64)
+require a firmware download before the ports will be operational.
+This is achieved by using the
+.Nm stlload
+command.
+See its manual page for details on usage.
+.Sh FILES
+.Bl -tag -width "/dev/staliomem?" -compact
+.It Pa /dev/ttyE?
+standard callin devices
+.It Pa /dev/ttyiE?
+initial-state callin devices
+.It Pa /dev/ttylE?
+lock-state callin devices
+.It Pa /dev/cue?
+standard callout devices
+.It Pa /dev/cuie?
+initial-state callout devices
+.It Pa /dev/cule?
+lock-state callout devices
+.It Pa /dev/staliomem?
+board control device
+.El
+.sp
+Note that the port numbers start at 0 for port 0 of board 0.
+Each board has 64 port slots allocated for it.
+So the second boards ports start at 64 and go through 127.
+Use the
+.Xr MAKEDEV 8
+script to create the devices.
+Use the ttyE and cue tag for the
+.Nm stl
+driver, and
+the ttyEi and cuei tags for the
+.Nm stli
+driver.
+.Sh SEE ALSO
+.Xr stty 1 ,
+.Xr termios 4 ,
+.Xr tty 4 ,
+.Xr comcontrol 8 ,
+.Xr MAKEDEV 8 ,
+.Xr stlload 8 ,
+.Xr stlstats 8
+.Sh HISTORY
+This driver was originally developed by
+.An Greg Ungerer Aq gerg@stallion.com .
diff --git a/usr.sbin/stallion/stlload/Makefile b/usr.sbin/stallion/stlload/Makefile
new file mode 100644
index 0000000..46048a5
--- /dev/null
+++ b/usr.sbin/stallion/stlload/Makefile
@@ -0,0 +1,8 @@
+# $Id: Makefile,v 1.3 1997/02/22 16:13:45 peter Exp $
+
+PROG= stlload
+MAN8= stlload.8
+
+CFLAGS+=-DBOOTDIR=\"${BOOTDIR}\"
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/stallion/stlload/stlload.8 b/usr.sbin/stallion/stlload/stlload.8
new file mode 100644
index 0000000..51e1197
--- /dev/null
+++ b/usr.sbin/stallion/stlload/stlload.8
@@ -0,0 +1,124 @@
+.\" Copyright (c) 1996 Greg Ungerer (gerg@stallion.oz.au).
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by Greg Ungerer.
+.\" 4. Neither the name of the author nor the names of any co-contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.Dd December 2, 1996
+.Os FreeBSD
+.Dt STLLOAD 8 i386
+.Sh NAME
+.Nm stlload
+.Nd "Stallion Technologies multiport serial board down loader"
+.Sh SYNOPSIS
+.Nm stlload
+.Op Fl vhVR
+.Op Fl i Ar image-file
+.Op Fl c Ar control-device
+.Op Fl r Ar rx-buf-size
+.Op Fl t Ar tx-buf-size
+.Op Fl B Ar boot-banner
+.Op Fl b Ar unit-number
+.Sh DESCRIPTION
+.Nm Stlload
+is used to download the firmware code to Stallion Technologies intelligent
+multiport serial boards.
+A firmware download is only required for those boards that use the Stallion
+.Nm stli
+driver.
+This includes the EasyConnection 8/64, ONboard and Brumby families of boards.
+.Pp
+Different board types require different firmware images.
+If the wrong firmware is loaded into a board it will fail to operate.
+.Pp
+The download process is achieved through the Stallion
+.Nm stli
+driver control device,
+.Pa /dev/staliomem? .
+This device implements a file type device that can read and write into the
+boards shared memory region.
+It also implements a number of special
+.Em ioctls
+that reset and restart the board.
+.Pp
+The options are:
+.Bl -tag -width indent
+.It Fl v
+Verbose output generation.
+Trace is generated at each phase of the download and startup process.
+.It Fl h
+Output usage information.
+.It Fl V
+Output version information.
+.It Fl R
+Reset the board only.
+Does not proceed to download firmware to the board.
+.It Fl i Ar image-file
+Specify the firmware image file to download.
+The default firmware image is
+.Pa /usr/libdata/stallion/cdk.sys .
+.It Fl c Ar control-device
+Specify the board control device through which to download the firmware
+and start up the board.
+The default is
+.Pa /dev/staliomem0 .
+.It Fl r Ar rx-buf-size
+Specify the size of the boards shared memory Receive Data buffer.
+By default the buffer is dynamically sized to use the maximum
+available shared memory.
+.It Fl t Ar tx-buf-size
+Specify the size of the boards shared memory Transmit Data buffer.
+By default the buffer is dynamically sized to use the maximum
+available shared memory.
+.It Fl B Ar boot-banner
+Enable the slave debug trace flag during download.
+This enables debug trace output from the firmware code.
+This trace is output on port 0 of the board,
+and the port is set to 9600 baud, 8 data bits, no parity and 1 stop bit.
+.It Fl b Ar unit-number
+Specify the unit (board) number to be downloaded. The default is to
+download board 0.
+.El
+.Pp
+.Nm Stlload
+would typically be run from
+.Pa /etc/rc.serial .
+.Sh FILES
+.Bl -tag -width /usr/libdata/stallion/2681.sys
+.It Pa /usr/libdata/stallion/cdk.sys
+firmware code to EasyConnection 8/64 class boards
+.It Pa /usr/libdata/stallion/2681.sys
+firmware code to ONboard and Brumby class boards
+.It Pa /dev/staliomem?
+driver board control device
+.Sh SEE ALSO
+.Xr stl 4 ,
+.Xr stli 4 ,
+.Xr stlstats 8
+.Sh HISTORY
+This program was originally developed by
+.An Greg Ungerer Aq gerg@stallion.com .
diff --git a/usr.sbin/stallion/stlload/stlload.c b/usr.sbin/stallion/stlload/stlload.c
new file mode 100644
index 0000000..5e8df3e
--- /dev/null
+++ b/usr.sbin/stallion/stlload/stlload.c
@@ -0,0 +1,504 @@
+/*****************************************************************************/
+
+/*
+ * stlload.c -- stallion intelligent multiport down loader.
+ *
+ * Copyright (c) 1994-1996 Greg Ungerer (gerg@stallion.oz.au).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Greg Ungerer.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*****************************************************************************/
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#include <err.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <time.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+
+#include <machine/cdk.h>
+
+/*****************************************************************************/
+
+char *version = "1.0.0";
+char *defdevice = "/dev/staliomem%d";
+char *image = BOOTDIR "/cdk.sys";
+char *oldimage = BOOTDIR "/2681.sys";
+
+char *memdevice;
+char devstr[128];
+int brdnr = 0;
+int verbose = 0;
+int reset = 0;
+
+/*
+ * Define a local buffer for copying the image into the shared memory.
+ */
+#define BUFSIZE 4096
+
+char buf[BUFSIZE];
+
+/*
+ * Define the timeout length when waiting for slave to start up.
+ * The quantity is measured in seconds.
+ */
+#define TIMEOUT 5
+
+/*
+ * Set up a default feature area structure.
+ */
+cdkfeature_t feature = { 0, 0, ETYP_CDK, 0, 0, 0, 0, 0 };
+
+/*
+ * Have local copies of the board signatures ready.
+ */
+cdkecpsig_t ecpsig;
+cdkonbsig_t onbsig;
+
+/*****************************************************************************/
+
+/*
+ * Declare internal function prototypes here.
+ */
+static void usage(void);
+int ecpfindports(cdkecpsig_t *sigp);
+int onbfindports(cdkonbsig_t *sigp);
+int download(void);
+
+/*****************************************************************************/
+
+static void usage()
+{
+ fprintf(stderr, "%s\n%s\n",
+"usage: stlload [-vhVR] [-i image-file] [-c control-device] [-r rx-buf-size]",
+" [-t tx-buf-size] [-B boot-banner] [-b unit-number]");
+ exit(0);
+}
+
+/*****************************************************************************/
+
+/*
+ * Given a boards signature determine how many ports it has. We need to
+ * know this to setup the slave feature arguments. This function is for
+ * ECP boards only.
+ */
+
+int ecpfindports(cdkecpsig_t *sigp)
+{
+ unsigned int id;
+ int bank, nrports;
+
+ nrports = 0;
+ for (bank = 0; (bank < 8); bank++) {
+ id = (unsigned int) sigp->panelid[bank];
+ if (id == 0xff)
+ break;
+ if ((id & 0x07) != bank)
+ break;
+ if (id & 0x20) {
+ nrports += 16;
+ bank++;
+ } else {
+ nrports += 8;
+ }
+ }
+
+ return(nrports);
+}
+
+/*****************************************************************************/
+
+/*
+ * Given a boards signature determine how many ports it has. We need to
+ * know this to setup the slave feature arguments. This function is for
+ * ONboards and Brumbys.
+ */
+
+int onbfindports(cdkonbsig_t *sigp)
+{
+ int i, nrports;
+
+ if (sigp->amask1) {
+ nrports = 32;
+ } else {
+ for (i = 0; (i < 16); i++) {
+ if (((sigp->amask0 << i) & 0x8000) == 0)
+ break;
+ }
+ nrports = i;
+ }
+
+ return(nrports);
+}
+
+/*****************************************************************************/
+
+/*
+ * Download an image to the slave board. There is a long sequence of
+ * things to do to get the slave running, but it is basically a simple
+ * process. Main things to do are: copy slave image into shared memory,
+ * start slave running and then read shared memory map.
+ */
+
+int download()
+{
+ unsigned char alivemarker;
+ time_t strttime;
+ int memfd, ifd;
+ int nrdevs, sigok, n;
+
+ if (verbose)
+ printf("Opening shared memory device %s\n", memdevice);
+ if ((memfd = open(memdevice, O_RDWR)) < 0) {
+ warn("failed to open memory device %s", memdevice);
+ return(-1);
+ }
+
+/*
+ * Before starting the download must tell driver that we are about to
+ * stop its slave. This is only important if it is already running.
+ * Once we have told the driver its stopped then do a hardware reset
+ * on it, to get it into a known state.
+ */
+ if (verbose)
+ printf("Stoping any current slave\n");
+ if (ioctl(memfd, STL_BSTOP, 0) < 0) {
+ warn("ioctl(STL_BSTOP)");
+ return(-1);
+ }
+
+ if (verbose)
+ printf("Reseting the board\n");
+ if (ioctl(memfd, STL_BRESET, 0) < 0) {
+ warn("ioctl(STL_BRESET)");
+ return(-1);
+ }
+ if (reset)
+ return(0);
+
+/*
+ * After reseting the board we need to send an interrupt to the older
+ * board types to get them to become active. Do that now.
+ */
+ if (verbose)
+ printf("Interrupting board to activate shared memory\n");
+ if (ioctl(memfd, STL_BINTR, 0) < 0) {
+ warn("ioctl(STL_BINTR)");
+ return(-1);
+ }
+ /*sleep(1);*/
+
+ if (verbose)
+ printf("Opening slave image file %s\n", image);
+ if ((ifd = open(image, O_RDONLY)) < 0) {
+ warn("failed to open image file %s", image);
+ return(-1);
+ }
+
+/*
+ * At this point get the signature of the board from the shared memory.
+ * Do a double check that it is a board we know about. We will also need
+ * to calculate the number of ports on this board (to use later).
+ */
+ sigok = 0;
+ if (verbose)
+ printf("Reading ROM signature from board\n");
+
+ if (lseek(memfd, CDK_SIGADDR, SEEK_SET) != CDK_SIGADDR) {
+ warn("lseek(%x) failed on memory file", CDK_FEATADDR);
+ return(-1);
+ }
+ if (read(memfd, &ecpsig, sizeof(cdkecpsig_t)) < 0) {
+ warn("read of ROM signature failed");
+ return(-1);
+ }
+ if (ecpsig.magic == ECP_MAGIC) {
+ nrdevs = ecpfindports(&ecpsig);
+ if (nrdevs < 0)
+ return(-1);
+ sigok++;
+ }
+
+ if (lseek(memfd, CDK_SIGADDR, SEEK_SET) != CDK_SIGADDR) {
+ warn("lseek(%x) failed on memory file", CDK_FEATADDR);
+ return(-1);
+ }
+ if (read(memfd, &onbsig, sizeof(cdkonbsig_t)) < 0) {
+ warn("read of ROM signature failed");
+ return(-1);
+ }
+ if ((onbsig.magic0 == ONB_MAGIC0) && (onbsig.magic1 == ONB_MAGIC1) &&
+ (onbsig.magic2 == ONB_MAGIC2) &&
+ (onbsig.magic3 == ONB_MAGIC3)) {
+ nrdevs = onbfindports(&onbsig);
+ if (nrdevs < 0)
+ return(-1);
+ sigok++;
+ }
+
+ if (! sigok) {
+ warnx("unknown signature from board");
+ return(-1);
+ }
+
+ if (verbose)
+ printf("Board signature reports %d ports\n", nrdevs);
+
+/*
+ * Start to copy the image file into shared memory. The first thing to
+ * do is copy the vector region in from shared memory address 0. We will
+ * then skip over the signature and feature area and start copying the
+ * actual image data and code from 4k upwards.
+ */
+ if (verbose)
+ printf("Copying vector table into shared memory\n");
+ if ((n = read(ifd, buf, CDK_SIGADDR)) < 0) {
+ warn("read of image file failed");
+ return(-1);
+ }
+ if (lseek(memfd, 0, SEEK_SET) != 0) {
+ warn("lseek(%x) failed on memory file", CDK_FEATADDR);
+ return(-1);
+ }
+ if (write(memfd, buf, n) < 0) {
+ warn("write to memory device failed");
+ return(-1);
+ }
+
+ if (lseek(ifd, 0x1000, SEEK_SET) != 0x1000) {
+ warn("lseek(%x) failed on image file", CDK_FEATADDR);
+ return(-1);
+ }
+ if (lseek(memfd, 0x1000, SEEK_SET) != 0x1000) {
+ warn("lseek(%x) failed on memory device", CDK_FEATADDR);
+ return(-1);
+ }
+
+/*
+ * Copy buffer size chunks of data from the image file into shared memory.
+ */
+ do {
+ if ((n = read(ifd, buf, BUFSIZE)) < 0) {
+ warn("read of image file failed");
+ return(-1);
+ }
+ if (write(memfd, buf, n) < 0) {
+ warn("write to memory device failed");
+ return(-1);
+ }
+ } while (n == BUFSIZE);
+
+ close(ifd);
+
+/*
+ * We need to down load the start up parameters for the slave. This is
+ * done via the feature area of shared memory. Think of the feature area
+ * as a way of passing "command line" arguments to the slave.
+ * FIX: should do something here to load "brdspec" as well...
+ */
+ feature.nrdevs = nrdevs;
+ if (verbose)
+ printf("Loading features into shared memory\n");
+ if (lseek(memfd, CDK_FEATADDR, SEEK_SET) != CDK_FEATADDR) {
+ warn("lseek(%x) failed on memory device", CDK_FEATADDR);
+ return(-1);
+ }
+ if (write(memfd, &feature, sizeof(cdkfeature_t)) < 0) {
+ warn("write to memory device failed");
+ return(-1);
+ }
+
+/*
+ * Wait for board alive marker to be set. The slave image will set the
+ * byte at address CDK_RDYADDR to 0x13 after it has successfully started.
+ * If this doesn't happen we timeout and fail.
+ */
+ if (verbose)
+ printf("Setting alive marker to 0\n");
+ if (lseek(memfd, CDK_RDYADDR, SEEK_SET) != CDK_RDYADDR) {
+ warn("lseek(%x) failed on memory device", CDK_RDYADDR);
+ return(-1);
+ }
+ alivemarker = 0;
+ if (write(memfd, &alivemarker, 1) < 0) {
+ warn("write to memory device failed");
+ return(-1);
+ }
+
+/*
+ * At this point the entire image is loaded into shared memory. To start
+ * it executiong we poke the board with an interrupt.
+ */
+ if (verbose)
+ printf("Interrupting board to start slave image\n");
+ if (ioctl(memfd, STL_BINTR, 0) < 0) {
+ warn("ioctl(STL_BINTR) failed");
+ return(-1);
+ }
+
+ strttime = time((time_t *) NULL);
+ if (verbose)
+ printf("Waiting for slave alive marker, time=%x timeout=%d\n",
+ strttime, TIMEOUT);
+ while (time((time_t *) NULL) < (strttime + TIMEOUT)) {
+ if (lseek(memfd, CDK_RDYADDR, SEEK_SET) != CDK_RDYADDR) {
+ warn("lseek(%x) failed on memory device", CDK_RDYADDR);
+ return(-1);
+ }
+ if (read(memfd, &alivemarker, 1) < 0){
+ warn("read of image file failed");
+ return(-1);
+ }
+ if (alivemarker == CDK_ALIVEMARKER)
+ break;
+ }
+
+ if (alivemarker != CDK_ALIVEMARKER) {
+ warnx("slave image failed to start");
+ return(-1);
+ }
+
+ if (lseek(memfd, CDK_RDYADDR, SEEK_SET) != CDK_RDYADDR) {
+ warn("lseek(%x) failed on memory device", CDK_RDYADDR);
+ return(-1);
+ }
+ alivemarker = 0;
+ if (write(memfd, &alivemarker, 1) < 0) {
+ warn("write to memory device failed");
+ return(-1);
+ }
+
+ if (verbose)
+ printf("Slave image started successfully\n");
+
+/*
+ * The last thing to do now is to get the driver started. Now that the
+ * slave is operational it must read in the memory map and gets its
+ * internal tables initialized.
+ */
+ if (verbose)
+ printf("Driver initializing host shared memory interface\n");
+ if (ioctl(memfd, STL_BSTART, 0) < 0) {
+ warn("ioctl(STL_BSTART) failed");
+ return(-1);
+ }
+
+ close(memfd);
+ return(0);
+}
+
+/*****************************************************************************/
+
+void main(int argc, char *argv[])
+{
+ struct stat statinfo;
+ int optind, c;
+
+ optind = 0;
+
+ while ((c = getopt(argc, argv, "hvVRB:i:b:d: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 'd':
+ 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..8f1b8e7
--- /dev/null
+++ b/usr.sbin/stallion/stlstats/Makefile
@@ -0,0 +1,9 @@
+# $Id: Makefile,v 1.3 1997/02/22 16:13:48 peter Exp $
+
+PROG= stlstats
+MAN8= stlstats.8
+
+LDADD= -lncurses
+DPADD= ${LIBNCURSES}
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/stallion/stlstats/stlstats.8 b/usr.sbin/stallion/stlstats/stlstats.8
new file mode 100644
index 0000000..05dec3c
--- /dev/null
+++ b/usr.sbin/stallion/stlstats/stlstats.8
@@ -0,0 +1,135 @@
+.\" Copyright (c) 1996 Greg Ungerer (gerg@stallion.oz.au).
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by Greg Ungerer.
+.\" 4. Neither the name of the author nor the names of any co-contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.Dd December 2, 1996
+.Os FreeBSD
+.Dt STLSTATS 8 i386
+.Sh NAME
+.Nm stlstats
+.Nd "Stallion Technologies multiport serial statistics display"
+.Sh SYNOPSIS
+.Nm stlstats
+.Op Fl hVi
+.Op Fl c Ar control-device
+.Op Fl b Ar board-number
+.Op Fl p Ar port-number
+.Op Fl d Ar port-device
+.Sh DESCRIPTION
+.Nm Stlstats
+is used to display statistical information about the ports on Stallion
+Technologies multiport serial boards.
+.Pp
+.Nm Stlstats
+normally runs as a full screen menu driven application.
+A help line is displayed at the bottom of each screen with the valid
+input keys for this screen.
+.Pp
+Generally the digit keys ('0' through '9') specify the number of the
+device to display statistics for.
+Where digits alone are not enough to access all possible devices
+(for example on 16 port panels) then the first letters of the alphabet
+are used to access the remaining devices.
+The letters 'a' through 'f' are used to access devices 10 through 15.
+.Pp
+The 'q' key is always used to move back to the previous level screen.
+The escape key can also be used to move back to the previous screen.
+.Pp
+The first screen is a display of all ports on panel 0 of board 0.
+Values displayed on this screen are a summary of the information for
+each port. The statistics displayed are: driver and TTY state flags,
+termios flags (cflags, iflags, oflags, lflags), RS-232 signal values
+(as per TIOCM signal defines), total transmit and receive character
+counts.
+.Pp
+From this screen you can look at summary information
+about each panel and board installed in the system.
+Each board is accessed by the digit keys ('0' through '7'),
+while panels of each board can be cycled through using the 'n' key.
+.Pp
+The per port screen displays some detailed information about a
+particular port.
+This is accessed from the board screen using the 'p' key.
+The first port displayed will be port 0.
+To display other ports use the digit and alphabetic keys
+('0' through '9' and 'a' through 'f').
+This screen displays: driver and TTY state flags, hardware ID,
+termios flags (cflags, iflags, oflags, lflags),
+total transmitted and received character counts,
+current transmit and receive characters buffered,
+receiver error counts (overruns, parity, framing, lost),
+software flow control characters transmitted and received,
+hardware flow control actions taken,
+count of transmitted and received breaks,
+modem signal transitions and
+current RS-232 signal states.
+.Pp
+The options are:
+.Bl -tag -width indent
+.It Fl h
+Output usage information.
+.It Fl V
+Output version information.
+.It Fl i
+Output only the board type information.
+This output is useful for scripts or other programs that need to know
+a little bit about the board (for example an automated download script).
+.Nm Stlstats
+will not enter full screen interactive mode.
+.It Fl c Ar control-device
+Specify the board control device through which to gather port statistics.
+The default is
+.Pa /dev/staliomem0 .
+.It Fl b Ar board-number
+Specify the board number to display first.
+The default is to display board 0.
+.It Fl p Ar port-number
+Specify the port number to display first.
+.Nm Stlstats
+will go straight into the port display screen (bypassing board display)
+when this option is used.
+.It Fl d Ar port-device
+Specify the port special device file (the
+.Pa /dev/ttyXXX
+file) to
+display first.
+The board screen is bypassed and the port statistics screen is shown
+immediately on start up.
+.El
+.Sh FILES
+.Bl -tag -width /dev/staliomem0
+.It Pa /dev/staliomem0
+driver control device used for statistics collection
+.Sh SEE ALSO
+.Xr stl 4 ,
+.Xr stli 4 ,
+.Xr stlload 8
+.Sh HISTORY
+This program was originally developed by
+.An Greg Ungerer Aq gerg@stallion.com .
diff --git a/usr.sbin/stallion/stlstats/stlstats.c b/usr.sbin/stallion/stlstats/stlstats.c
new file mode 100644
index 0000000..d4d6941
--- /dev/null
+++ b/usr.sbin/stallion/stlstats/stlstats.c
@@ -0,0 +1,581 @@
+/*****************************************************************************/
+
+/*
+ * stlstats.c -- stallion intelligent multiport stats display.
+ *
+ * Copyright (c) 1994-1996 Greg Ungerer (gerg@stallion.oz.au).
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Greg Ungerer.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*****************************************************************************/
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#include <err.h>
+#include <fcntl.h>
+#include <ncurses.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+
+#include <machine/cdk.h>
+#include <machine/comstats.h>
+
+/*****************************************************************************/
+
+char *version = "1.0.0";
+char *defdevice = "/dev/staliomem0";
+
+char *ctrldevice;
+int ctrlfd;
+int displaybrdnr = 0;
+int displaypanelnr = 0;
+int displayportnr = 0;
+int displayportbank = 0;
+
+#define MAXBRDS 8
+#define MAXPORTS 32
+
+combrd_t brdstats;
+comstats_t stats[MAXPORTS];
+
+char *line = " ";
+
+/*****************************************************************************/
+
+/*
+ * Declare internal function prototypes here.
+ */
+static void usage(void);
+void useportdevice(char *devname);
+void localexit(int nr);
+void menuport();
+void displayport();
+void menuallports();
+void displayallports();
+void getallstats();
+void getbrdstats();
+void clearportstats();
+void clearallstats();
+
+/*****************************************************************************/
+
+static void usage()
+{
+ fprintf(stderr, "%s\n%s\n",
+ "usage: stlstats [-hVi] [-c control-device] [-b board-number]",
+ " [-p port-number] [-d port-device]");
+ exit(0);
+}
+
+/*****************************************************************************/
+
+void useportdevice(char *devname)
+{
+ struct stat statinfo;
+ int portnr, portcnt;
+ int i;
+
+ if (stat(devname, &statinfo) < 0)
+ errx(1, "port device %s does not exist", devname);
+ if ((statinfo.st_mode & S_IFMT) != S_IFCHR)
+ errx(1, "port device %s is not a char device", devname);
+
+ displaybrdnr = (statinfo.st_rdev & 0x00700000) >> 20;
+ portnr = (statinfo.st_rdev & 0x1f) |
+ ((statinfo.st_rdev & 0x00010000) >> 11);
+ getbrdstats();
+ if (brdstats.ioaddr == 0)
+ errx(1, "device %s does not exist", devname);
+
+ for (portcnt = 0, i = 0; (i < brdstats.nrpanels); i++) {
+ if ((portnr >= portcnt) &&
+ (portnr < (portcnt + brdstats.panels[i].nrports)))
+ break;
+ portcnt += brdstats.panels[i].nrports;
+ }
+ if (i >= brdstats.nrpanels)
+ errx(1, "device %s does not exist", devname);
+ displaypanelnr = i;
+ displayportnr = portnr - portcnt;
+ if (displayportnr >= 16)
+ displayportbank = 16;
+}
+
+/*****************************************************************************/
+
+/*
+ * Get the board stats for the current display board.
+ */
+
+void getbrdstats()
+{
+ brdstats.brd = displaybrdnr;
+ if (ioctl(ctrlfd, COM_GETBRDSTATS, &brdstats) < 0)
+ memset((combrd_t *) &brdstats, 0, sizeof(combrd_t));
+}
+
+/*****************************************************************************/
+
+/*
+ * Zero out stats for the current display port.
+ */
+
+void clearportstats()
+{
+ stats[displayportnr].brd = displaybrdnr;
+ stats[displayportnr].panel = displaypanelnr;
+ stats[displayportnr].port = displayportnr;
+ ioctl(ctrlfd, COM_CLRPORTSTATS, &stats[displayportnr]);
+}
+
+/*****************************************************************************/
+
+/*
+ * Zero out all stats for all ports on all boards.
+ */
+
+void clearallstats()
+{
+ int brdnr, panelnr, portnr;
+
+ for (brdnr = 0; (brdnr < MAXBRDS); brdnr++) {
+ for (panelnr = 0; (panelnr < COM_MAXPANELS); panelnr++) {
+ for (portnr = 0; (portnr < MAXPORTS); portnr++) {
+ stats[0].brd = brdnr;
+ stats[0].panel = panelnr;
+ stats[0].port = portnr;
+ ioctl(ctrlfd, COM_CLRPORTSTATS, &stats[0]);
+ }
+ }
+ }
+}
+
+/*****************************************************************************/
+
+/*
+ * Get the stats for the current display board/panel.
+ */
+
+void getallstats()
+{
+ int i;
+
+ for (i = 0; (i < brdstats.panels[displaypanelnr].nrports); i++) {
+ stats[i].brd = displaybrdnr;
+ stats[i].panel = displaypanelnr;
+ stats[i].port = i;
+ if (ioctl(ctrlfd, COM_GETPORTSTATS, &stats[i]) < 0) {
+ warn("ioctl(COM_GETPORTSTATS) failed");
+ localexit(1);
+ }
+ }
+}
+
+/*****************************************************************************/
+
+/*
+ * Display the per ports stats screen.
+ */
+
+void displayport()
+{
+ mvprintw(0, 0, "STALLION SERIAL PORT STATISTICS");
+ mvprintw(2, 0,
+ "Board=%d Type=%d HwID=%02x State=%06x TotalPorts=%d",
+ displaybrdnr, brdstats.type, brdstats.hwid, brdstats.state,
+ brdstats.nrports);
+ mvprintw(3, 0, "Panel=%d HwID=%02x Ports=%d", displaypanelnr,
+ brdstats.panels[displaypanelnr].hwid,
+ brdstats.panels[displaypanelnr].nrports);
+
+ attron(A_REVERSE);
+ mvprintw(5, 0, line);
+ mvprintw(5, 0, "Port=%d ", displayportnr);
+ attroff(A_REVERSE);
+
+ mvprintw(7, 0, "STATE: State=%08x", stats[displayportnr].state);
+ mvprintw(7, 29, "Tty=%08x", stats[displayportnr].ttystate);
+ mvprintw(7, 47, "Flags=%08x", stats[displayportnr].flags);
+ mvprintw(7, 65, "HwID=%02x", stats[displayportnr].hwid);
+
+ mvprintw(8, 0, "CONFIG: Cflag=%08x", stats[displayportnr].cflags);
+ mvprintw(8, 29, "Iflag=%08x", stats[displayportnr].iflags);
+ mvprintw(8, 47, "Oflag=%08x", stats[displayportnr].oflags);
+ mvprintw(8, 65, "Lflag=%08x", stats[displayportnr].lflags);
+
+ mvprintw(10, 0, "TX DATA: Total=%d", stats[displayportnr].txtotal);
+ mvprintw(10, 29, "Buffered=%d ", stats[displayportnr].txbuffered);
+ mvprintw(11, 0, "RX DATA: Total=%d", stats[displayportnr].rxtotal);
+ mvprintw(11, 29, "Buffered=%d ", stats[displayportnr].rxbuffered);
+ mvprintw(12, 0, "RX ERRORS: Parity=%d", stats[displayportnr].rxparity);
+ mvprintw(12, 29, "Framing=%d", stats[displayportnr].rxframing);
+ mvprintw(12, 47, "Overrun=%d", stats[displayportnr].rxoverrun);
+ mvprintw(12, 65, "Lost=%d", stats[displayportnr].rxlost);
+
+ mvprintw(14, 0, "FLOW TX: Xoff=%d", stats[displayportnr].txxoff);
+ mvprintw(14, 29, "Xon=%d", stats[displayportnr].txxon);
+#if 0
+ mvprintw(14, 47, "CTSoff=%d", stats[displayportnr].txctsoff);
+ mvprintw(14, 65, "CTSon=%d", stats[displayportnr].txctson);
+#endif
+ mvprintw(15, 0, "FLOW RX: Xoff=%d", stats[displayportnr].rxxoff);
+ mvprintw(15, 29, "Xon=%d", stats[displayportnr].rxxon);
+ mvprintw(15, 47, "RTSoff=%d", stats[displayportnr].rxrtsoff);
+ mvprintw(15, 65, "RTSon=%d", stats[displayportnr].rxrtson);
+
+ mvprintw(17, 0, "OTHER: TXbreaks=%d",
+ stats[displayportnr].txbreaks);
+ mvprintw(17, 29, "RXbreaks=%d", stats[displayportnr].rxbreaks);
+ mvprintw(17, 47, "Modem=%d", stats[displayportnr].modem);
+
+ mvprintw(19, 0, "SIGNALS: DCD=%d DTR=%d CTS=%d RTS=%d "
+ "DSR=%d RI=%d",
+ (stats[displayportnr].signals & TIOCM_CD) ? 1 : 0,
+ (stats[displayportnr].signals & TIOCM_DTR) ? 1 : 0,
+ (stats[displayportnr].signals & TIOCM_CTS) ? 1 : 0,
+ (stats[displayportnr].signals & TIOCM_RTS) ? 1 : 0,
+ (stats[displayportnr].signals & TIOCM_DSR) ? 1 : 0,
+ (stats[displayportnr].signals & TIOCM_RI) ? 1 : 0);
+
+ attron(A_REVERSE);
+ mvprintw(22, 0, line);
+ attroff(A_REVERSE);
+
+ mvprintw(24, 19, "(q=Quit,0123456789abcdef=Port,Z=ZeroStats)");
+ refresh();
+}
+
+/*****************************************************************************/
+
+/*
+ * Continuously update and display the per ports stats screen.
+ * Also checks for keyboard input, and processes it as appropriate.
+ */
+
+void menuport()
+{
+ int ch, done;
+
+ clear();
+ done = 0;
+
+ while ((ch = getch()) != 27) {
+ switch (ch) {
+ case ERR:
+ break;
+ case ' ':
+ refresh();
+ break;
+ case 'a':
+ case 'b':
+ case 'c':
+ case 'd':
+ case 'e':
+ case 'f':
+ ch = (ch - 'a' + '0' + 10);
+ /* fall thru */
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ case '8':
+ case '9':
+ ch -= '0';
+ if (ch >= brdstats.panels[displaypanelnr].nrports) {
+ beep();
+ } else {
+ displayportnr = displayportbank + ch;
+ clear();
+ }
+ break;
+ case 'Z':
+ clearportstats();
+ clear();
+ break;
+ case 'q':
+ done = 1;
+ break;
+ default:
+ beep();
+ break;
+ }
+
+ if (done)
+ break;
+
+ getallstats();
+ displayport();
+ }
+}
+
+/*****************************************************************************/
+
+/*
+ * Display the all ports stats screen.
+ */
+
+void displayallports()
+{
+ int i, nrports, portnr;;
+
+ nrports = brdstats.panels[displaypanelnr].nrports;
+
+ mvprintw(0, 0, "STALLION SERIAL PORT STATISTICS");
+ mvprintw(2, 0, "Board=%d Type=%d HwID=%02x State=%06x TotalPorts=%d",
+ displaybrdnr, brdstats.type, brdstats.hwid, brdstats.state,
+ brdstats.nrports);
+ mvprintw(3, 0, "Panel=%d HwID=%02x Ports=%d", displaypanelnr,
+ brdstats.panels[displaypanelnr].hwid, nrports);
+
+ attron(A_REVERSE);
+ mvprintw(5, 0, "Port State Tty Flags Cflag Iflag Oflag Lflag "
+ "Sigs TX Total RX Total ");
+ attroff(A_REVERSE);
+
+ if (nrports > 0) {
+ if (nrports > 16)
+ nrports = 16;
+ portnr = displayportbank;
+ for (i = 0; (i < nrports); i++, portnr++) {
+ mvprintw((6 + i), 1, "%2d", portnr);
+ mvprintw((6 + i), 5, "%06x", stats[portnr].state);
+ mvprintw((6 + i), 12, "%06x", stats[portnr].ttystate);
+ mvprintw((6 + i), 19, "%08x", stats[portnr].flags);
+ mvprintw((6 + i), 28, "%05x", stats[portnr].cflags);
+ mvprintw((6 + i), 34, "%05x", stats[portnr].iflags);
+ mvprintw((6 + i), 40, "%05x", stats[portnr].oflags);
+ mvprintw((6 + i), 46, "%05x", stats[portnr].lflags);
+ mvprintw((6 + i), 52, "%04x", stats[portnr].signals);
+ mvprintw((6 + i), 58, "%10d", stats[portnr].txtotal);
+ mvprintw((6 + i), 69, "%10d", stats[portnr].rxtotal);
+ }
+ } else {
+ mvprintw(12, 32, "NO BOARD %d FOUND", displaybrdnr);
+ i = 16;
+ }
+
+ attron(A_REVERSE);
+ mvprintw((6 + i), 0, line);
+ attroff(A_REVERSE);
+
+ mvprintw(24, 14,
+ "(q=Quit,01234567=Board,n=Panels,p=Ports,Z=ZeroStats)");
+ refresh();
+}
+
+/*****************************************************************************/
+
+/*
+ * Continuously update and display the all ports stats screen.
+ * Also checks for keyboard input, and processes it as appropriate.
+ */
+
+void menuallports()
+{
+ int ch, done;
+
+ clear();
+ getbrdstats();
+
+ done = 0;
+ while ((ch = getch()) != 27) {
+ switch (ch) {
+ case ERR:
+ break;
+ case ' ':
+ refresh();
+ break;
+ case '0':
+ case '1':
+ case '2':
+ case '3':
+ case '4':
+ case '5':
+ case '6':
+ case '7':
+ displaybrdnr = ch - '0';
+ displaypanelnr = 0;
+ getbrdstats();
+ if (brdstats.state == 0)
+ beep();
+ clear();
+ break;
+ case 'n':
+ if (brdstats.panels[displaypanelnr].nrports > 16) {
+ if (displayportbank == 0) {
+ displayportbank = 16;
+ clear();
+ break;
+ }
+ }
+ displayportbank = 0;
+ displaypanelnr++;
+ if (displaypanelnr >= brdstats.nrpanels)
+ displaypanelnr = 0;
+ clear();
+ break;
+ case 'p':
+ if (brdstats.panels[displaypanelnr].nrports > 0) {
+ displayportnr = displayportbank;
+ menuport();
+ clear();
+ } else {
+ beep();
+ }
+ break;
+ case 'Z':
+ clearallstats();
+ clear();
+ break;
+ case 'q':
+ done = 1;
+ break;
+ default:
+ beep();
+ break;
+ }
+
+ if (done)
+ break;
+
+ getallstats();
+ displayallports();
+ }
+}
+
+/*****************************************************************************/
+
+/*
+ * A local exit routine - shuts down curses before exiting.
+ */
+
+void localexit(int nr)
+{
+ refresh();
+ endwin();
+ exit(nr);
+}
+
+/*****************************************************************************/
+
+void main(int argc, char *argv[])
+{
+ struct stat statinfo;
+ int optind, c, useport;
+ char *portdev;
+
+ optind = 0;
+ ctrldevice = defdevice;
+ useport = 0;
+
+ while ((c = getopt(argc, argv, "hvVb:p:d:c:")) != -1) {
+ switch (c) {
+ case 'V':
+ printf("stlstats version %s\n", version);
+ exit(0);
+ break;
+ case 'h':
+ usage();
+ break;
+ case 'b':
+ displaybrdnr = atoi(optarg);
+ break;
+ case 'p':
+ displaypanelnr = atoi(optarg);
+ break;
+ case 'd':
+ useport++;
+ portdev = optarg;
+ break;
+ case 'c':
+ ctrldevice = optarg;
+ break;
+ case '?':
+ default:
+ usage();
+ break;
+ }
+ }
+
+/*
+ * Check that the control device exits and is a character device.
+ */
+ if (stat(ctrldevice, &statinfo) < 0)
+ errx(1, "control device %s does not exist", ctrldevice);
+ if ((statinfo.st_mode & S_IFMT) != S_IFCHR)
+ errx(1, "control device %s is not a char device", ctrldevice);
+ if ((ctrlfd = open(ctrldevice, O_RDWR)) < 0)
+ errx(1, "open of %s failed", ctrldevice);
+
+/*
+ * Validate the panel number supplied by user. We do this now since we
+ * need to have parsed the entire command line first.
+ */
+ getbrdstats();
+ if (displaypanelnr >= brdstats.nrpanels)
+ displaypanelnr = 0;
+
+ if (useport)
+ useportdevice(portdev);
+
+/*
+ * Everything is now ready, lets go!
+ */
+ initscr();
+ cbreak();
+ halfdelay(5);
+ noecho();
+ clear();
+ if (useport) {
+ menuport();
+ clear();
+ }
+ menuallports();
+ refresh();
+ endwin();
+
+ close(ctrlfd);
+ printf("\n");
+ exit(0);
+}
+
+/*****************************************************************************/
diff --git a/usr.sbin/sysctl/Makefile b/usr.sbin/sysctl/Makefile
new file mode 100644
index 0000000..8d94c66
--- /dev/null
+++ b/usr.sbin/sysctl/Makefile
@@ -0,0 +1,6 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= sysctl
+MAN8= sysctl.8
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/sysctl/pathconf.c b/usr.sbin/sysctl/pathconf.c
new file mode 100644
index 0000000..07f786d
--- /dev/null
+++ b/usr.sbin/sysctl/pathconf.c
@@ -0,0 +1,240 @@
+/*
+ * Copyright (c) 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)pathconf.c 8.1 (Berkeley) 6/6/93";
+#endif
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/sysctl.h>
+#include <sys/unistd.h>
+
+#include <err.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define PC_NAMES { \
+ { 0, 0 }, \
+ { "link_max", CTLTYPE_INT }, \
+ { "max_canon", CTLTYPE_INT }, \
+ { "max_input", CTLTYPE_INT }, \
+ { "name_max", CTLTYPE_INT }, \
+ { "path_max", CTLTYPE_INT }, \
+ { "pipe_buf", CTLTYPE_INT }, \
+ { "chown_restricted", CTLTYPE_INT }, \
+ { "no_trunc", CTLTYPE_INT }, \
+ { "vdisable", CTLTYPE_INT }, \
+}
+#define PC_MAXID 10
+
+struct ctlname pcnames[] = PC_NAMES;
+char names[BUFSIZ];
+
+struct list {
+ struct ctlname *list;
+ int size;
+};
+struct list pclist = { pcnames, PC_MAXID };
+
+int Aflag, aflag, nflag, wflag, stdinflag;
+
+int findname __P((char *, char *, char**, struct list *));
+void listall __P((char *, struct list *));
+void parse __P((char *, char *, int));
+static void usage __P((void));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ char *path;
+ int ch;
+
+ while ((ch = getopt(argc, argv, "Aan")) != -1) {
+ switch (ch) {
+
+ case 'A':
+ Aflag = 1;
+ break;
+
+ case 'a':
+ aflag = 1;
+ break;
+
+ case 'n':
+ nflag = 1;
+ break;
+
+ default:
+ usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (argc == 0)
+ usage();
+ path = *argv++;
+ if (strcmp(path, "-") == 0)
+ stdinflag = 1;
+ argc--;
+ if (Aflag || aflag) {
+ listall(path, &pclist);
+ exit(0);
+ }
+ if (argc == 0)
+ usage();
+ while (argc-- > 0)
+ parse(path, *argv, 1);
+ exit(0);
+}
+
+/*
+ * List all variables known to the system.
+ */
+void
+listall(path, lp)
+ char *path;
+ struct list *lp;
+{
+ int lvl2;
+
+ if (lp->list == 0)
+ return;
+ for (lvl2 = 0; lvl2 < lp->size; lvl2++) {
+ if (lp->list[lvl2].ctl_name == 0)
+ continue;
+ parse(path, lp->list[lvl2].ctl_name, Aflag);
+ }
+}
+
+/*
+ * Parse a name into an index.
+ * Lookup and print out the attribute if it exists.
+ */
+void
+parse(pathname, string, flags)
+ char *pathname;
+ char *string;
+ int flags;
+{
+ int indx, value;
+ char *bufp, buf[BUFSIZ];
+
+ bufp = buf;
+ snprintf(buf, BUFSIZ, "%s", string);
+ if ((indx = findname(string, "top", &bufp, &pclist)) == -1)
+ return;
+ if (bufp) {
+ warnx("name %s in %s is unknown", *bufp, string);
+ return;
+ }
+ if (stdinflag)
+ value = fpathconf(0, indx);
+ else
+ value = pathconf(pathname, indx);
+ if (value == -1) {
+ if (flags == 0)
+ return;
+ switch (errno) {
+ case EOPNOTSUPP:
+ warnx("%s: value is not available", string);
+ return;
+ case ENOTDIR:
+ warnx("%s: specification is incomplete", string);
+ return;
+ case ENOMEM:
+ warnx("%s: type is unknown to this program", string);
+ return;
+ default:
+ warn("%s", string);
+ return;
+ }
+ }
+ if (!nflag)
+ fprintf(stdout, "%s = ", string);
+ fprintf(stdout, "%d\n", value);
+}
+
+/*
+ * Scan a list of names searching for a particular name.
+ */
+int
+findname(string, level, bufp, namelist)
+ char *string;
+ char *level;
+ char **bufp;
+ struct list *namelist;
+{
+ char *name;
+ int i;
+
+ if (namelist->list == 0 || (name = strsep(bufp, ".")) == NULL) {
+ warnx("%s: incomplete specification", string);
+ return (-1);
+ }
+ for (i = 0; i < namelist->size; i++)
+ if (namelist->list[i].ctl_name != NULL &&
+ strcmp(name, namelist->list[i].ctl_name) == 0)
+ break;
+ if (i == namelist->size) {
+ warnx("%s level name %s in %s is invalid", level, name, string);
+ return (-1);
+ }
+ return (i);
+}
+
+static void
+usage()
+{
+
+ (void)fprintf(stderr, "%s\n%s\n%s\n",
+ "usage: pathname [-n] variable ...",
+ " pathname [-n] -a",
+ " pathname [-n] -A");
+ exit(1);
+}
diff --git a/usr.sbin/sysctl/sysctl.8 b/usr.sbin/sysctl/sysctl.8
new file mode 100644
index 0000000..477cfe9
--- /dev/null
+++ b/usr.sbin/sysctl/sysctl.8
@@ -0,0 +1,238 @@
+.\" Copyright (c) 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" From: @(#)sysctl.8 8.1 (Berkeley) 6/6/93
+.\" $Id: sysctl.8,v 1.13 1997/08/30 02:28:00 kato Exp $
+.\"
+.Dd September 23, 1994
+.Dt SYSCTL 8
+.Os
+.Sh NAME
+.Nm sysctl
+.Nd get or set kernel state
+.Sh SYNOPSIS
+.Nm sysctl
+.Op Fl bn
+.Ar name ...
+.Nm sysctl
+.Op Fl bn
+.Fl w
+.Ar name=value ...
+.Nm sysctl
+.Op Fl bn
+.Fl aAX
+.Sh DESCRIPTION
+The
+.Nm
+utility retrieves kernel state and allows processes with
+appropriate privilege to set kernel state.
+The state to be retrieved or set is described using a
+``Management Information Base'' (``MIB'') style name,
+described as a dotted set of components.
+.Pp
+The following options are available:
+.Bl -tag -width indent
+.It Fl a
+List all the currently available string or integer values.
+.It Fl A
+List all the known MIB names including opaques.
+Those with string or integer values will be printed as with the
+.Fl a
+flag; for the opaque values,
+information about the format and the length is printed in addition the first
+few bytes is dumped in hex.
+.It Fl X
+Same as
+.Fl A
+except the entire value of opaque variables is hexdumped.
+.It Fl n
+Specify that the printing of the field name should be
+suppressed and that only its value should be output.
+This flag is useful for setting shell variables.
+For example, to save the pagesize in variable psize, use:
+.Bd -literal -offset indent -compact
+set psize=`sysctl -n hw.pagesize`
+.Ed
+.It Fl b
+Force the value of the variable(s) to be output in raw, binary
+format. No names are printed and no terminating newlines are output.
+This is mostly useful with a single variable.
+.It Fl w Ar name=value ...
+Set the MIB
+.Ar name
+to the new
+.Ar value .
+If just a MIB style
+.Ar name
+is given,
+the corresponding value is retrieved.
+.El
+.Pp
+The information available from
+.Nm
+consists of integers, strings, and opaques.
+.Nm Sysctl
+only knows about a couple of opaque types, and will resort to hexdumps
+for the rest.
+The opaque information is much more useful if retrieved by special
+purpose programs such as
+.Nm ps ,
+.Nm systat ,
+and
+.Nm netstat .
+.Pp
+The string and integer information is summarized below.
+For a detailed description of these variable see
+.Xr sysctl 3 .
+.Pp
+The changeable column indicates whether a process with appropriate
+privilege can change the value.
+.Bl -column net.inet.ip.forwardingxxxxxx integerxxx
+.It Sy Name Type Changeable
+.It kern.ostype string no
+.It kern.osrelease string no
+.It kern.osrevision integer no
+.It kern.version string no
+.It kern.maxvnodes integer yes
+.It kern.maxproc integer yes
+.It kern.maxprocperuid integer yes
+.It kern.maxfiles integer yes
+.It kern.maxfilesperproc integer yes
+.It kern.argmax integer no
+.It kern.securelevel integer raise only
+.It kern.hostname string yes
+.It kern.hostid integer yes
+.It kern.clockrate struct no
+.It kern.posix1version integer no
+.It kern.ngroups integer no
+.It kern.job_control integer no
+.It kern.saved_ids integer no
+.It kern.boottime struct no
+.It kern.domainname string yes
+.It kern.update integer yes
+.It kern.osreldate string no
+.It kern.bootfile string yes
+.It vm.loadavg struct no
+.It hw.machine string no
+.It hw.model string no
+.It hw.ncpu integer no
+.It hw.byteorder integer no
+.It hw.physmem integer no
+.It hw.usermem integer no
+.It hw.pagesize integer no
+.It hw.floatingpoint integer no
+.It hw.machine_arch string no
+.It machdep.console_device dev_t no
+.It machdep.adjkerntz integer yes
+.It machdep.disable_rtc_set integer yes
+.It user.cs_path string no
+.It user.bc_base_max integer no
+.It user.bc_dim_max integer no
+.It user.bc_scale_max integer no
+.It user.bc_string_max integer no
+.It user.coll_weights_max integer no
+.It user.expr_nest_max integer no
+.It user.line_max integer no
+.It user.re_dup_max integer no
+.It user.posix2_version integer no
+.It user.posix2_c_bind integer no
+.It user.posix2_c_dev integer no
+.It user.posix2_char_term integer no
+.It user.posix2_fort_dev integer no
+.It user.posix2_fort_run integer no
+.It user.posix2_localedef integer no
+.It user.posix2_sw_dev integer no
+.It user.posix2_upe integer no
+.It user.stream_max integer no
+.It user.tzname_max integer no
+.El
+.Sh EXAMPLES
+For example, to retrieve the maximum number of processes allowed
+in the system, one would use the follow request:
+.Bd -literal -offset indent -compact
+sysctl kern.maxproc
+.Ed
+.Pp
+To set the maximum number of processes allowed
+in the system to 1000, one would use the follow request:
+.Bd -literal -offset indent -compact
+sysctl -w kern.maxproc=1000
+.Ed
+.Pp
+Information about the system clock rate may be obtained with:
+.Bd -literal -offset indent -compact
+sysctl kern.clockrate
+.Ed
+.Pp
+Information about the load average history may be obtained with:
+.Bd -literal -offset indent -compact
+sysctl vm.loadavg
+.Ed
+.Pp
+More variables than these exist, and the best and likely only place
+to search for their deeper meaning is undoubtedly the source where
+they are defined.
+.Sh FILES
+.Bl -tag -width <netinet/icmpXvar.h> -compact
+.It Pa <sys/sysctl.h>
+definitions for top level identifiers, second level kernel and hardware
+identifiers, and user level identifiers
+.It Pa <sys/socket.h>
+definitions for second level network identifiers
+.It Pa <sys/gmon.h>
+definitions for third level profiling identifiers
+.It Pa <vm/vm_param.h>
+definitions for second level virtual memory identifiers
+.It Pa <netinet/in.h>
+definitions for third level Internet identifiers and
+fourth level IP identifiers
+.It Pa <netinet/icmp_var.h>
+definitions for fourth level ICMP identifiers
+.It Pa <netinet/udp_var.h>
+definitions for fourth level UDP identifiers
+.El
+.Sh SEE ALSO
+.Xr sysctl 3
+.Sh BUGS
+.Nm Sysctl
+presently exploits an undocumented interface to the kernel
+sysctl facility to traverse the sysctl tree and to retrieve format
+and name information.
+This correct interface is being thought about for the time being.
+.Sh HISTORY
+.Nm Sysctl
+first appeared in
+.Bx 4.4 .
+.Pp
+In
+.Fx 2.2 ,
+.Nm
+was significantly remodeled.
diff --git a/usr.sbin/sysctl/sysctl.c b/usr.sbin/sysctl/sysctl.c
new file mode 100644
index 0000000..ef0996a
--- /dev/null
+++ b/usr.sbin/sysctl/sysctl.c
@@ -0,0 +1,461 @@
+/*
+ * Copyright (c) 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)from: sysctl.c 8.1 (Berkeley) 6/6/93";
+#endif
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/sysctl.h>
+#include <sys/resource.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+static int Aflag, aflag, nflag, wflag, Xflag, bflag;
+
+static int oidfmt(int *, int, char *, u_int *);
+static void parse(char *);
+static int show_var(int *, int);
+static int sysctl_all (int *oid, int len);
+static int name2oid(char *, int *);
+
+static void
+usage(void)
+{
+
+ (void)fprintf(stderr, "%s\n%s\n%s\n%s\n",
+ "usage: sysctl [-bnX] variable ...",
+ " sysctl [-bnX] -w variable=value ...",
+ " sysctl [-bnX] -a",
+ " sysctl [-bnX] -A");
+ exit(1);
+}
+
+int
+main(int argc, char **argv)
+{
+ int ch;
+ setbuf(stdout,0);
+ setbuf(stderr,0);
+
+ while ((ch = getopt(argc, argv, "AabnwX")) != -1) {
+ switch (ch) {
+ case 'A': Aflag = 1; break;
+ case 'a': aflag = 1; break;
+ case 'b': bflag = 1; break;
+ case 'n': nflag = 1; break;
+ case 'w': wflag = 1; break;
+ case 'X': Xflag = Aflag = 1; break;
+ default: usage();
+ }
+ }
+ argc -= optind;
+ argv += optind;
+
+ if (Aflag || aflag)
+ exit (sysctl_all(0, 0));
+ if (argc == 0)
+ usage();
+ while (argc-- > 0)
+ parse(*argv++);
+ exit(0);
+}
+
+/*
+ * Parse a name into a MIB entry.
+ * Lookup and print out the MIB entry if it exists.
+ * Set a new value if requested.
+ */
+static void
+parse(char *string)
+{
+ int len, i, j;
+ void *newval = 0;
+ int intval, newsize = 0;
+ quad_t quadval;
+ int mib[CTL_MAXNAME];
+ char *cp, *bufp, buf[BUFSIZ];
+ u_int kind;
+
+ bufp = buf;
+ snprintf(buf, BUFSIZ, "%s", string);
+ if ((cp = strchr(string, '=')) != NULL) {
+ if (!wflag)
+ errx(2, "must specify -w to set variables");
+ *strchr(buf, '=') = '\0';
+ *cp++ = '\0';
+ while (isspace(*cp))
+ cp++;
+ newval = cp;
+ newsize = strlen(cp);
+ } else {
+ if (wflag)
+ usage();
+ }
+ len = name2oid(bufp, mib);
+
+ if (len < 0)
+ errx(1, "unknown oid '%s'", bufp);
+
+ if (oidfmt(mib, len, 0, &kind))
+ err(1, "couldn't find format of oid '%s'", bufp);
+
+ if (!wflag) {
+ if ((kind & CTLTYPE) == CTLTYPE_NODE) {
+ sysctl_all(mib, len);
+ } else {
+ i = show_var(mib, len);
+ if (!i && !bflag)
+ putchar('\n');
+ }
+ } else {
+ if ((kind & CTLTYPE) == CTLTYPE_NODE)
+ errx(1, "oid '%s' isn't a leaf node", bufp);
+
+ if (!(kind&CTLFLAG_WR))
+ errx(1, "oid '%s' is read only", bufp);
+
+ switch (kind & CTLTYPE) {
+ case CTLTYPE_INT:
+ intval = atoi(newval);
+ newval = &intval;
+ newsize = sizeof intval;
+ break;
+ break;
+ case CTLTYPE_STRING:
+ break;
+ case CTLTYPE_QUAD:
+ break;
+ sscanf(newval, "%qd", &quadval);
+ newval = &quadval;
+ newsize = sizeof quadval;
+ break;
+ default:
+ errx(1, "oid '%s' is type %d,"
+ " cannot set that", bufp);
+ }
+
+ i = show_var(mib, len);
+ if (sysctl(mib, len, 0, 0, newval, newsize) == -1) {
+ if (!i && !bflag)
+ putchar('\n');
+ switch (errno) {
+ case EOPNOTSUPP:
+ errx(1, "%s: value is not available",
+ string);
+ case ENOTDIR:
+ errx(1, "%s: specification is incomplete",
+ string);
+ case ENOMEM:
+ errx(1, "%s: type is unknown to this program",
+ string);
+ default:
+ warn("%s", string);
+ return;
+ }
+ }
+ if (!bflag)
+ printf(" -> ");
+ i = nflag;
+ nflag = 1;
+ j = show_var(mib, len);
+ if (!j && !bflag)
+ putchar('\n');
+ nflag = i;
+ }
+}
+
+/* These functions will dump out various interesting structures. */
+
+static int
+S_clockinfo(int l2, void *p)
+{
+ struct clockinfo *ci = (struct clockinfo*)p;
+ if (l2 != sizeof *ci)
+ err(1, "S_clockinfo %d != %d", l2, sizeof *ci);
+ printf("{ hz = %d, tick = %d, tickadj = %d, profhz = %d, stathz = %d }",
+ ci->hz, ci->tick, ci->tickadj, ci->profhz, ci->stathz);
+ return (0);
+}
+
+static int
+S_loadavg(int l2, void *p)
+{
+ struct loadavg *tv = (struct loadavg*)p;
+
+ if (l2 != sizeof *tv)
+ err(1, "S_loadavg %d != %d", l2, sizeof *tv);
+
+ printf("{ %.2f %.2f %.2f }",
+ (double)tv->ldavg[0]/(double)tv->fscale,
+ (double)tv->ldavg[1]/(double)tv->fscale,
+ (double)tv->ldavg[2]/(double)tv->fscale);
+ return (0);
+}
+
+static int
+S_timeval(int l2, void *p)
+{
+ struct timeval *tv = (struct timeval*)p;
+ char *p1, *p2;
+
+ if (l2 != sizeof *tv)
+ err(1, "S_timeval %d != %d", l2, sizeof *tv);
+ printf("{ sec = %ld, usec = %ld } ",
+ tv->tv_sec, tv->tv_usec);
+ p1 = strdup(ctime(&tv->tv_sec));
+ for (p2=p1; *p2 ; p2++)
+ if (*p2 == '\n')
+ *p2 = '\0';
+ fputs(p1, stdout);
+ return (0);
+}
+
+static int
+T_dev_t(int l2, void *p)
+{
+ dev_t *d = (dev_t *)p;
+ if (l2 != sizeof *d)
+ err(1, "T_dev_T %d != %d", l2, sizeof *d);
+ printf("{ major = %d, minor = %d }",
+ major(*d), minor(*d));
+ return (0);
+}
+
+/*
+ * These functions uses a presently undocumented interface to the kernel
+ * to walk the tree and get the type so it can print the value.
+ * This interface is under work and consideration, and should probably
+ * be killed with a big axe by the first person who can find the time.
+ * (be aware though, that the proper interface isn't as obvious as it
+ * may seem, there are various conflicting requirements.
+ */
+
+static int
+name2oid(char *name, int *oidp)
+{
+ int oid[2];
+ int i, j;
+
+ oid[0] = 0;
+ oid[1] = 3;
+
+ j = CTL_MAXNAME * sizeof (int);
+ i = sysctl(oid, 2, oidp, &j, name, strlen(name));
+ if (i < 0)
+ return i;
+ j /= sizeof (int);
+ return (j);
+}
+
+static int
+oidfmt(int *oid, int len, char *fmt, u_int *kind)
+{
+ int qoid[CTL_MAXNAME+2];
+ u_char buf[BUFSIZ];
+ int i, j;
+
+ qoid[0] = 0;
+ qoid[1] = 4;
+ memcpy(qoid + 2, oid, len * sizeof(int));
+
+ j = sizeof buf;
+ i = sysctl(qoid, len + 2, buf, &j, 0, 0);
+ if (i)
+ err(1, "sysctl fmt %d %d %d", i, j, errno);
+
+ if (kind)
+ *kind = *(u_int *)buf;
+
+ if (fmt)
+ strcpy(fmt, (char *)(buf + sizeof(u_int)));
+ return 0;
+}
+
+/*
+ * This formats and outputs the value of one variable
+ *
+ * Returns zero if anything was actually output.
+ * Returns one if didn't know what to do with this.
+ * Return minus one if we had errors.
+ */
+
+static int
+show_var(int *oid, int nlen)
+{
+ u_char buf[BUFSIZ], *val, *p;
+ char name[BUFSIZ], *fmt;
+ int qoid[CTL_MAXNAME+2];
+ int i, j, len;
+ u_int kind;
+ int (*func)(int, void *) = 0;
+
+ /* find an estimate of how much we need for this var */
+ j = 0;
+ i = sysctl(oid, nlen, 0, &j, 0, 0);
+ j += j; /* we want to be sure :-) */
+
+ val = alloca(j);
+ len = j;
+ i = sysctl(oid, nlen, val, &len, 0, 0);
+ if (i || !len)
+ return (1);
+
+ if (bflag) {
+ fwrite(val, 1, len, stdout);
+ return (0);
+ }
+
+ qoid[0] = 0;
+ qoid[1] = 4;
+ memcpy(qoid + 2, oid, nlen * sizeof(int));
+
+ j = sizeof buf;
+ i = sysctl(qoid, nlen + 2, buf, &j, 0, 0);
+ if (i || !j)
+ err(1, "sysctl fmt %d %d %d", i, j, errno);
+
+ kind = *(u_int *)buf;
+
+ fmt = (char *)(buf + sizeof(u_int));
+
+ qoid[1] = 1;
+ j = sizeof name;
+ i = sysctl(qoid, nlen + 2, name, &j, 0, 0);
+ if (i || !j)
+ err(1, "sysctl name %d %d %d", i, j, errno);
+
+ p = val;
+ switch (*fmt) {
+ case 'A':
+ if (!nflag)
+ printf("%s: ", name);
+ printf("%s", p);
+ return (0);
+
+ case 'I':
+ if (!nflag)
+ printf("%s: ", name);
+ printf("%d", *(int *)p);
+ return (0);
+
+ case 'T':
+ case 'S':
+ i = 0;
+ if (!strcmp(fmt, "S,clockinfo")) func = S_clockinfo;
+ else if (!strcmp(fmt, "S,timeval")) func = S_timeval;
+ else if (!strcmp(fmt, "S,loadavg")) func = S_loadavg;
+ else if (!strcmp(fmt, "T,dev_t")) func = T_dev_t;
+ if (func) {
+ if (!nflag)
+ printf("%s: ", name);
+ return ((*func)(len, p));
+ }
+ /* FALL THROUGH */
+ default:
+ if (!Aflag)
+ return (1);
+ if (!nflag)
+ printf("%s: ", name);
+ printf("Format:%s Length:%d Dump:0x", fmt, len);
+ while (len--) {
+ printf("%02x", *p++);
+ if (Xflag || p < val+16)
+ continue;
+ printf("...");
+ break;
+ }
+ return (0);
+ }
+ return (1);
+}
+
+static int
+sysctl_all (int *oid, int len)
+{
+ int name1[22], name2[22];
+ int i, j, l1, l2;
+
+ name1[0] = 0;
+ name1[1] = 2;
+ l1 = 2;
+ if (len) {
+ memcpy(name1+2, oid, len*sizeof (int));
+ l1 += len;
+ } else {
+ name1[2] = 1;
+ l1++;
+ }
+ while (1) {
+ l2 = sizeof name2;
+ j = sysctl(name1, l1, name2, &l2, 0, 0);
+ if (j < 0)
+ if (errno == ENOENT)
+ return 0;
+ else
+ err(1, "sysctl(getnext) %d %d", j, l2);
+
+ l2 /= sizeof (int);
+
+ if (l2 < len)
+ return 0;
+
+ for (i = 0; i < len; i++)
+ if (name2[i] != oid[i])
+ return 0;
+
+ i = show_var(name2, l2);
+ if (!i && !bflag)
+ putchar('\n');
+
+ memcpy(name1+2, name2, l2*sizeof (int));
+ l1 = 2 + l2;
+ }
+}
diff --git a/usr.sbin/sysinstall/Makefile b/usr.sbin/sysinstall/Makefile
new file mode 100644
index 0000000..fd269a0
--- /dev/null
+++ b/usr.sbin/sysinstall/Makefile
@@ -0,0 +1,79 @@
+PROG= sysinstall
+MAN8= sysinstall.8
+BINDIR=/stand
+
+CLEANFILES+= makedevs.c rtermcap rtermcap.tmp dumpnlist
+CLEANFILES+= keymap.tmp keymap.h
+
+.PATH: ${.CURDIR}/../disklabel ${.CURDIR}/../../usr.bin/cksum
+
+SRCS= anonFTP.c attr.c cdrom.c command.c config.c devices.c \
+ disks.c dispatch.c dist.c dmenu.c doc.c dos.c dosio.c floppy.c \
+ ftp.c globals.c index.c install.c installUpgrade.c keymap.c \
+ label.c lndir.c main.c makedevs.c media.c menus.c misc.c \
+ msg.c network.c nfs.c options.c package.c register.c system.c \
+ tape.c tcpip.c termcap.c ufs.c user.c variable.c wizard.c \
+ uc_eisa.c uc_isa.c uc_kmem.c uc_list.c uc_main.c uc_pci.c \
+ uc_scsi.c keymap.h
+
+CFLAGS+= -Wall -I${.CURDIR}/../../gnu/lib/libdialog -I${.OBJDIR}
+CFLAGS+= -I${.CURDIR}/../../sys
+CFLAGS+= -DUC_PRIVATE -DKERN_NO_SYMBOLS -DSAVE_USERCONFIG
+#CFLAGS+= -DUSE_XIG_ENVIRONMENT
+
+DPADD= ${LIBDIALOG} ${LIBNCURSES} ${LIBMYTINFO} ${LIBUTIL} ${LIBDISK} ${LIBFTPIO}
+LDADD= -ldialog -lncurses -lmytinfo -lutil -ldisk -lftpio
+
+makedevs.c: Makefile rtermcap keymap.h
+ rm -f makedevs.tmp
+ echo '#include <sys/types.h>' > makedevs.tmp
+ ./rtermcap ansi | \
+ file2c 'const char termcap_ansi[] = {' ',0};' \
+ >> makedevs.tmp
+ ./rtermcap cons25 | \
+ file2c 'const char termcap_cons25[] = {' ',0};' \
+ >> makedevs.tmp
+ ./rtermcap cons25-m | \
+ file2c 'const char termcap_cons25_m[] = {' ',0};' \
+ >> makedevs.tmp
+ ./rtermcap cons25r | \
+ file2c 'const char termcap_cons25r[] = {' ',0};' \
+ >> makedevs.tmp
+ ./rtermcap cons25r-m | \
+ file2c 'const char termcap_cons25r_m[] = {' ',0};' \
+ >> makedevs.tmp
+ ./rtermcap cons25l1 | \
+ file2c 'const char termcap_cons25l1[] = {' ',0};' \
+ >> makedevs.tmp
+ ./rtermcap cons25l1-m | \
+ file2c 'const char termcap_cons25l1_m[] = {' ',0};' \
+ >> makedevs.tmp
+ ./rtermcap vt100 | \
+ file2c 'const char termcap_vt100[] = {' ',0};' \
+ >> makedevs.tmp
+ mv makedevs.tmp makedevs.c
+
+rtermcap: ${.CURDIR}/rtermcap.c
+ ${CC} -o rtermcap ${.CURDIR}/rtermcap.c -ltermcap
+
+
+KEYMAPS= be.iso br275.iso danish.iso fr.iso german.iso it.iso jp.106 \
+ norwegian.iso ru.koi8-r spanish.iso swedish.iso \
+ swissgerman.iso uk.iso us.dvorak us.iso
+
+
+keymap.h:
+ rm -f keymap.tmp
+ for map in ${KEYMAPS} ; do \
+ kbdcontrol -L $$map >> 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..45edf71
--- /dev/null
+++ b/usr.sbin/sysinstall/anonFTP.c
@@ -0,0 +1,316 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last program in the `sysinstall' line - the next
+ * generation being essentially a complete rewrite.
+ *
+ * $Id: anonFTP.c,v 1.22 1997/03/09 22:25:38 jkh Exp $
+ *
+ * Copyright (c) 1995
+ * Coranth Gryphon. All rights reserved.
+ * Copyright (c) 1996
+ * Jordan K. Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR THEIR PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include "sysinstall.h"
+#include <sys/param.h>
+#include <pwd.h>
+#include <grp.h>
+
+/* This doesn't change until FTP itself changes */
+
+#define FTP_NAME "ftp"
+#define MOTD_FILE "ftpmotd"
+
+/* These change if we want to use different defaults */
+
+#define FTP_UID 14
+#define FTP_GID 5
+#define FTP_GROUP "operator"
+#define FTP_UPLOAD "incoming"
+#define FTP_COMMENT "Anonymous FTP Admin"
+#define FTP_HOMEDIR "/var/ftp"
+
+#define ANONFTP_HELPFILE "anonftp"
+
+/* Set up the structure to hold configuration information */
+/* Note that this is only what we could fit onto the one screen */
+
+typedef struct
+{
+ char homedir[64]; /* Home Dir for Anon FTP */
+ char group[32]; /* Group */
+ char uid[8]; /* UID */
+ char comment[64]; /* PWD Comment */
+ char upload[32]; /* Upload Dir */
+} FTPConf;
+
+static FTPConf tconf;
+
+#define ANONFTP_HOMEDIR_LEN 64
+#define ANONFTP_COMMENT_LEN 64
+#define ANONFTP_UPLOAD_LEN 32
+#define ANONFTP_GROUP_LEN 32
+#define ANONFTP_UID_LEN 8
+
+static int okbutton, cancelbutton;
+
+/* What the screen size is meant to be */
+#define ANONFTP_DIALOG_Y 0
+#define ANONFTP_DIALOG_X 8
+#define ANONFTP_DIALOG_WIDTH COLS - 16
+#define ANONFTP_DIALOG_HEIGHT LINES - 2
+
+static Layout layout[] = {
+#define LAYOUT_UID 0
+ { 2, 3, 8, ANONFTP_UID_LEN - 1,
+ "UID:", "What user ID to assign to FTP Admin",
+ tconf.uid, STRINGOBJ, NULL },
+#define LAYOUT_GROUP 1
+ { 2, 15, 15, ANONFTP_GROUP_LEN - 1,
+ "Group:", "Group name that ftp process belongs to",
+ tconf.group, STRINGOBJ, NULL },
+#define LAYOUT_COMMENT 2
+ { 2, 35, 24, ANONFTP_COMMENT_LEN - 1,
+ "Comment:", "Password file comment for FTP Admin",
+ tconf.comment, STRINGOBJ, NULL },
+#define LAYOUT_HOMEDIR 3
+ { 9, 10, 43, ANONFTP_HOMEDIR_LEN - 1,
+ "FTP Root Directory:",
+ "The top directory to chroot to when doing anonymous ftp",
+ tconf.homedir, STRINGOBJ, NULL },
+#define LAYOUT_UPLOAD 4
+ { 14, 20, 22, ANONFTP_UPLOAD_LEN - 1,
+ "Upload Subdirectory:", "Designated sub-directory that holds uploads",
+ tconf.upload, STRINGOBJ, NULL },
+#define LAYOUT_OKBUTTON 5
+ { 19, 15, 0, 0,
+ "OK", "Select this if you are happy with these settings",
+ &okbutton, BUTTONOBJ, NULL },
+#define LAYOUT_CANCELBUTTON 6
+ { 19, 35, 0, 0,
+ "CANCEL", "Select this if you wish to cancel this screen",
+ &cancelbutton, BUTTONOBJ, NULL },
+ { NULL },
+};
+
+int
+createFtpUser(void)
+{
+ struct passwd *tpw;
+ struct group *tgrp;
+ char pwline[256];
+ char *tptr;
+ int gid;
+ FILE *fptr;
+
+ if ((gid = atoi(tconf.group)) <= 0) {
+ if (!(tgrp = getgrnam(tconf.group))) {
+ /* group does not exist, create it by name */
+
+ tptr = msgGetInput("14", "What group ID to use for group %s ?", tconf.group);
+ if (tptr && *tptr && ((gid = atoi(tptr)) > 0)) {
+ if ((fptr = fopen(_PATH_GROUP,"a"))) {
+ fprintf(fptr,"%s:*:%d:%s\n",tconf.group,gid,FTP_NAME);
+ fclose(fptr);
+ }
+ }
+ else
+ gid = FTP_GID;
+ }
+ else
+ gid = tgrp->gr_gid;
+ }
+ else if (!getgrgid(gid)) {
+ /* group does not exist, create it by number */
+
+ tptr = msgGetInput("14", "What group name to use for gid %d ?", gid);
+ if (tptr && *tptr) {
+ SAFE_STRCPY(tconf.group, tptr);
+ if ((tgrp = getgrnam(tconf.group))) {
+ gid = tgrp->gr_gid;
+ }
+ else if ((fptr = fopen(_PATH_GROUP,"a"))) {
+ fprintf(fptr,"%s:*:%d:%s\n",tconf.group,gid,FTP_NAME);
+ fclose(fptr);
+ }
+ }
+ }
+
+ if ((tpw = getpwnam(FTP_NAME))) {
+ if (tpw->pw_uid != FTP_UID)
+ msgConfirm("FTP user already exists with a different uid.");
+
+ return DITEM_SUCCESS; /* succeeds if already exists */
+ }
+
+ sprintf(pwline, "%s:*:%s:%d::0:0:%s:%s:/nonexistent\n", FTP_NAME, tconf.uid, gid, tconf.comment, tconf.homedir);
+
+ fptr = fopen(_PATH_MASTERPASSWD,"a");
+ if (! fptr) {
+ msgConfirm("Could not open master password file.");
+ return DITEM_FAILURE;
+ }
+ fprintf(fptr, pwline);
+ fclose(fptr);
+ msgNotify("Remaking password file: %s", _PATH_MASTERPASSWD);
+ vsystem("pwd_mkdb -p %s", _PATH_MASTERPASSWD);
+ return DITEM_SUCCESS;
+}
+
+/* This is it - how to get the setup values */
+static int
+anonftpOpenDialog(void)
+{
+ WINDOW *ds_win;
+ ComposeObj *obj = NULL;
+ int n = 0, cancel = FALSE;
+ int max;
+ char title[80];
+
+ /* We need a curses window */
+ if (!(ds_win = openLayoutDialog(ANONFTP_HELPFILE, " Anonymous FTP Configuration ",
+ ANONFTP_DIALOG_X, ANONFTP_DIALOG_Y, ANONFTP_DIALOG_WIDTH, ANONFTP_DIALOG_HEIGHT))) {
+ beep();
+ msgConfirm("Cannot open anonymous ftp dialog window!!");
+ return DITEM_FAILURE;
+ }
+
+ /* Draw a sub-box for the path configuration */
+ draw_box(ds_win, ANONFTP_DIALOG_Y + 7, ANONFTP_DIALOG_X + 8,
+ ANONFTP_DIALOG_HEIGHT - 11, ANONFTP_DIALOG_WIDTH - 17,
+ dialog_attr, border_attr);
+ wattrset(ds_win, dialog_attr);
+ sprintf(title, " Path Configuration ");
+ mvwaddstr(ds_win, ANONFTP_DIALOG_Y + 7, ANONFTP_DIALOG_X + 22, title);
+
+ /** Initialize the config Data Structure **/
+ bzero(&tconf, sizeof(tconf));
+
+ SAFE_STRCPY(tconf.group, FTP_GROUP);
+ SAFE_STRCPY(tconf.upload, FTP_UPLOAD);
+ SAFE_STRCPY(tconf.comment, FTP_COMMENT);
+ SAFE_STRCPY(tconf.homedir, FTP_HOMEDIR);
+ sprintf(tconf.uid, "%d", FTP_UID);
+
+ /* Some more initialisation before we go into the main input loop */
+ obj = initLayoutDialog(ds_win, layout, ANONFTP_DIALOG_X, ANONFTP_DIALOG_Y, &max);
+
+ cancelbutton = okbutton = 0;
+ while (layoutDialogLoop(ds_win, layout, &obj, &n, max, &cancelbutton, &cancel));
+
+ /* Clear this crap off the screen */
+ delwin(ds_win);
+ dialog_clear_norefresh();
+ use_helpfile(NULL);
+
+ if (cancel)
+ return DITEM_FAILURE;
+ return DITEM_SUCCESS;
+}
+
+int
+configAnonFTP(dialogMenuItem *self)
+{
+ int i;
+
+ /* Be optimistic */
+ i = DITEM_SUCCESS;
+
+ dialog_clear_norefresh();
+ i = anonftpOpenDialog();
+ if (DITEM_STATUS(i) != DITEM_SUCCESS) {
+ msgConfirm("Configuration of Anonymous FTP cancelled per user request.");
+ return i | DITEM_RESTORE;
+ }
+
+ /*** Use defaults for any invalid values ***/
+ if (atoi(tconf.uid) <= 0)
+ sprintf(tconf.uid, "%d", FTP_UID);
+
+ if (!tconf.group[0])
+ SAFE_STRCPY(tconf.group, FTP_GROUP);
+
+ if (!tconf.upload[0])
+ SAFE_STRCPY(tconf.upload, FTP_UPLOAD);
+
+ /*** If the user did not specify a directory, use default ***/
+
+ if (tconf.homedir[strlen(tconf.homedir) - 1] == '/')
+ tconf.homedir[strlen(tconf.homedir) - 1] = '\0';
+
+ if (!tconf.homedir[0])
+ SAFE_STRCPY(tconf.homedir, FTP_HOMEDIR);
+
+ /*** If HomeDir does not exist, create it ***/
+
+ if (!directory_exists(tconf.homedir))
+ vsystem("mkdir -p %s", tconf.homedir);
+
+ if (directory_exists(tconf.homedir)) {
+ msgNotify("Configuring %s for use by anon FTP.", tconf.homedir);
+ vsystem("chmod 555 %s && chown root.%s %s", tconf.homedir, tconf.group, tconf.homedir);
+ vsystem("mkdir %s/bin && chmod 555 %s/bin", tconf.homedir, tconf.homedir);
+ vsystem("cp /bin/ls %s/bin && chmod 111 %s/bin/ls", tconf.homedir, tconf.homedir);
+ vsystem("cp /bin/date %s/bin && chmod 111 %s/bin/date", tconf.homedir, tconf.homedir);
+ vsystem("mkdir %s/etc && chmod 555 %s/etc", tconf.homedir, tconf.homedir);
+ vsystem("mkdir -p %s/pub", tconf.homedir);
+ vsystem("mkdir -p %s/%s", tconf.homedir, tconf.upload);
+ vsystem("chmod 1777 %s/%s", tconf.homedir, tconf.upload);
+
+ if (DITEM_STATUS(createFtpUser()) == DITEM_SUCCESS) {
+ msgNotify("Copying password information for anon FTP.");
+ vsystem("cp /etc/pwd.db %s/etc && chmod 444 %s/etc/pwd.db", tconf.homedir, tconf.homedir);
+ vsystem("cp /etc/passwd %s/etc && chmod 444 %s/etc/passwd", tconf.homedir, tconf.homedir);
+ vsystem("cp /etc/group %s/etc && chmod 444 %s/etc/group", tconf.homedir, tconf.homedir);
+ vsystem("chown -R root.%s %s/pub", tconf.group, tconf.homedir);
+ }
+ else {
+ msgConfirm("Unable to create FTP user! Anonymous FTP setup failed.");
+ i = DITEM_FAILURE;
+ }
+
+ if (!msgYesNo("Create a welcome message file for anonymous FTP users?")) {
+ char cmd[256];
+ dialog_clear();
+ vsystem("echo Your welcome message here. > %s/etc/%s", tconf.homedir, MOTD_FILE);
+ sprintf(cmd, "%s %s/etc/%s", variable_get(VAR_EDITOR), tconf.homedir, MOTD_FILE);
+ if (!systemExecute(cmd))
+ i = DITEM_SUCCESS;
+ else
+ i = DITEM_FAILURE;
+ }
+ }
+ else {
+ msgConfirm("Invalid Directory: %s\n"
+ "Anonymous FTP will not be set up.", tconf.homedir);
+ i = DITEM_FAILURE;
+ }
+ if (DITEM_STATUS(i) == DITEM_SUCCESS)
+ variable_set2("anon_ftp", "YES");
+ return i | DITEM_RESTORE;
+}
diff --git a/usr.sbin/sysinstall/cdrom.c b/usr.sbin/sysinstall/cdrom.c
new file mode 100644
index 0000000..ad5460e
--- /dev/null
+++ b/usr.sbin/sysinstall/cdrom.c
@@ -0,0 +1,163 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last attempt in the `sysinstall' line, the next
+ * generation being slated to essentially a complete rewrite.
+ *
+ * $Id: cdrom.c,v 1.38 1997/02/22 14:11:12 peter Exp $
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ * Copyright (c) 1995
+ * Gary J Palmer. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+/* These routines deal with getting things off of CDROM media */
+
+#include "sysinstall.h"
+#include <sys/stat.h>
+#include <sys/errno.h>
+#include <sys/param.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <grp.h>
+#include <fcntl.h>
+
+#define CD9660
+#include <sys/mount.h>
+#include <isofs/cd9660/cd9660_mount.h>
+#undef CD9660
+
+static Boolean cdromMounted;
+
+Boolean
+mediaInitCDROM(Device *dev)
+{
+ struct iso_args args;
+ Attribs *cd_attr;
+ char *cp;
+ Boolean readInfo = TRUE;
+ char *mountpoint = "/dist";
+
+ if (cdromMounted)
+ return TRUE;
+
+ bzero(&args, sizeof(args));
+ args.fspec = dev->devname;
+ args.flags = 0;
+
+ cd_attr = alloca(sizeof(Attribs) * MAX_ATTRIBS);
+ cp = NULL;
+
+ Mkdir(mountpoint);
+
+ if (mount(MOUNT_CD9660, mountpoint, MNT_RDONLY, (caddr_t) &args) == -1) {
+ if (errno == EINVAL) {
+ msgConfirm("The CD in your drive looks more like an Audio CD than a FreeBSD release.");
+ return FALSE;
+ }
+ else if (errno != EBUSY) {
+ msgConfirm("Error mounting %s on %s: %s (%u)", dev->devname, mountpoint, strerror(errno), errno);
+ return FALSE;
+ }
+ cdromMounted = TRUE;
+ }
+
+ if (!file_readable(string_concat(mountpoint, "/cdrom.inf"))) {
+ if (msgYesNo("Warning: The CD currently in the drive is either not a FreeBSD\n"
+ "CD or it is an older (pre 2.1.5) FreeBSD CD which does not\n"
+ "have a version number on it. Do you wish to use this CD anyway?") != 0) {
+ unmount(mountpoint, MNT_FORCE);
+ return FALSE;
+ }
+ else
+ readInfo = FALSE;
+ }
+
+ if (readInfo &&
+ (DITEM_STATUS(attr_parse_file(cd_attr, string_concat(mountpoint, "/cdrom.inf"))) == DITEM_FAILURE ||
+ !(cp = attr_match(cd_attr, "CD_VERSION")) || (strcmp(cp, variable_get(VAR_RELNAME)) && strcmp("none", variable_get(VAR_RELNAME))))) {
+ if (!cp)
+ msgConfirm("Unable to find a %s/cdrom.inf file.\n"
+ "Either this is not a FreeBSD CDROM, there is a problem with\n"
+ "the CDROM driver or something is wrong with your hardware.\n"
+ "Please fix this problem (check the console logs on VTY2) and\n"
+ "try again.", mountpoint);
+ else
+ msgConfirm("Warning: The version of the FreeBSD CD currently in the drive\n"
+ "(%s) does not match the version of the boot floppy\n"
+ "(%s).\n\n"
+ "If this is intentional, to avoid this message in the future\n"
+ "please visit the Options editor to set the boot floppy version\n"
+ "string to match that of the CD before selecting it as your\n"
+ "installation media.", cp, variable_get(VAR_RELNAME));
+
+ if (msgYesNo("Would you like to try and use this CDROM anyway?") != 0) {
+ unmount(mountpoint, MNT_FORCE);
+ cdromMounted = FALSE;
+ return FALSE;
+ }
+ }
+ msgDebug("Mounted FreeBSD CDROM from device %s\n", dev->devname);
+ return TRUE;
+}
+
+FILE *
+mediaGetCDROM(Device *dev, char *file, Boolean probe)
+{
+ char buf[PATH_MAX];
+
+ if (isDebug())
+ msgDebug("Request for %s from CDROM\n", file);
+ snprintf(buf, PATH_MAX, "/dist/%s", file);
+ if (file_readable(buf))
+ return fopen(buf, "r");
+ snprintf(buf, PATH_MAX, "/dist/dists/%s", file);
+ if (file_readable(buf))
+ return fopen(buf, "r");
+ snprintf(buf, PATH_MAX, "/dist/%s/%s", variable_get(VAR_RELNAME), file);
+ if (file_readable(buf))
+ return fopen(buf, "r");
+ snprintf(buf, PATH_MAX, "/dist/%s/dists/%s", variable_get(VAR_RELNAME), file);
+ return fopen(buf, "r");
+}
+
+void
+mediaShutdownCDROM(Device *dev)
+{
+ char *mountpoint = "/dist";
+
+ if (!cdromMounted)
+ return;
+ msgDebug("Unmounting %s from %s\n", dev->devname, mountpoint);
+ if (unmount(mountpoint, MNT_FORCE) != 0)
+ msgConfirm("Could not unmount the CDROM from %s: %s", mountpoint, strerror(errno));
+ else {
+ msgDebug("Unmount of CDROM successful\n");
+ cdromMounted = FALSE;
+ }
+}
diff --git a/usr.sbin/sysinstall/command.c b/usr.sbin/sysinstall/command.c
new file mode 100644
index 0000000..b14f913
--- /dev/null
+++ b/usr.sbin/sysinstall/command.c
@@ -0,0 +1,179 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last program in the `sysinstall' line - the next
+ * generation being essentially a complete rewrite.
+ *
+ * $Id$
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include "sysinstall.h"
+
+#define MAX_NUM_COMMANDS 10
+
+typedef struct {
+ char key[FILENAME_MAX];
+ struct {
+ enum { CMD_SHELL, CMD_FUNCTION } type;
+ void *ptr, *data;
+ } cmds[MAX_NUM_COMMANDS];
+ int ncmds;
+} Command;
+
+#define MAX_CMDS 200
+static Command *commandStack[MAX_CMDS];
+int numCommands;
+
+/* Nuke the command stack */
+void
+command_clear(void)
+{
+ int i, j;
+
+ for (i = 0; i < numCommands; i++)
+ for (j = 0; j < commandStack[i]->ncmds; j++)
+ if (commandStack[i]->cmds[j].type == CMD_SHELL)
+ free(commandStack[i]->cmds[j].ptr);
+ free(commandStack[i]);
+ numCommands = 0;
+}
+
+static void
+addit(char *key, int type, void *cmd, void *data)
+{
+ int i;
+
+ /* First, look for the key already present and add a command to it if found */
+ for (i = 0; i < numCommands; i++) {
+ if (!strcmp(commandStack[i]->key, key)) {
+ if (commandStack[i]->ncmds == MAX_NUM_COMMANDS)
+ msgFatal("More than %d commands stacked up behind %s??", MAX_NUM_COMMANDS, key);
+ commandStack[i]->cmds[commandStack[i]->ncmds].type = type;
+ commandStack[i]->cmds[commandStack[i]->ncmds].ptr = cmd;
+ commandStack[i]->cmds[commandStack[i]->ncmds].data = data;
+ ++(commandStack[i]->ncmds);
+ return;
+ }
+ }
+ if (numCommands == MAX_CMDS)
+ msgFatal("More than %d commands accumulated??", MAX_CMDS);
+
+ /* If we fell to here, it's a new key */
+ commandStack[numCommands] = safe_malloc(sizeof(Command));
+ strcpy(commandStack[numCommands]->key, key);
+ commandStack[numCommands]->ncmds = 1;
+ commandStack[numCommands]->cmds[0].type = type;
+ commandStack[numCommands]->cmds[0].ptr = cmd;
+ commandStack[numCommands]->cmds[0].data = data;
+ ++numCommands;
+}
+
+/* Add a shell command under a given key */
+void
+command_shell_add(char *key, char *fmt, ...)
+{
+ va_list args;
+ char *cmd;
+
+ cmd = (char *)safe_malloc(256);
+ va_start(args, fmt);
+ vsnprintf(cmd, 256, fmt, args);
+ va_end(args);
+
+ addit(key, CMD_SHELL, cmd, NULL);
+}
+
+/* Add a shell command under a given key */
+void
+command_func_add(char *key, commandFunc func, void *data)
+{
+ addit(key, CMD_FUNCTION, func, data);
+}
+
+static int
+sort_compare(Command *p1, Command *p2)
+{
+ if (!p1 && !p2)
+ return 0;
+ else if (!p1 && p2) /* NULL has a "greater" value for commands */
+ return 1;
+ else if (p1 && !p2)
+ return -1;
+ else
+ return strcmp(p1->key, p2->key);
+}
+
+void
+command_sort(void)
+{
+ int i, j;
+
+ commandStack[numCommands] = NULL;
+ /* Just do a crude bubble sort since the list is small */
+ for (i = 0; i < numCommands; i++) {
+ for (j = 0; j < numCommands; j++) {
+ if (sort_compare(commandStack[j], commandStack[j + 1]) > 0) {
+ Command *tmp = commandStack[j];
+
+ commandStack[j] = commandStack[j + 1];
+ commandStack[j + 1] = tmp;
+ }
+ }
+ }
+}
+
+/* Run all accumulated commands in sorted order */
+void
+command_execute(void)
+{
+ int i, j, ret;
+ commandFunc func;
+
+ for (i = 0; i < numCommands; i++) {
+ for (j = 0; j < commandStack[i]->ncmds; j++) {
+ /* If it's a shell command, run system on it */
+ if (commandStack[i]->cmds[j].type == CMD_SHELL) {
+ msgNotify("Doing %s", commandStack[i]->cmds[j].ptr);
+ ret = vsystem((char *)commandStack[i]->cmds[j].ptr);
+ if (isDebug())
+ msgDebug("Command `%s' returns status %d\n", commandStack[i]->cmds[j].ptr, ret);
+ }
+ else {
+ /* It's a function pointer - call it with the key and the data */
+ func = (commandFunc)commandStack[i]->cmds[j].ptr;
+ if (isDebug())
+ msgDebug("%x: Execute(%s, %s)", func, commandStack[i]->key, commandStack[i]->cmds[j].data);
+ ret = (*func)(commandStack[i]->key, commandStack[i]->cmds[j].data);
+ if (isDebug())
+ msgDebug("Function @ %x returns status %d\n", commandStack[i]->cmds[j].ptr, ret);
+ }
+ }
+ }
+}
diff --git a/usr.sbin/sysinstall/config.c b/usr.sbin/sysinstall/config.c
new file mode 100644
index 0000000..2b37c89
--- /dev/null
+++ b/usr.sbin/sysinstall/config.c
@@ -0,0 +1,869 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last program in the `sysinstall' line - the next
+ * generation being essentially a complete rewrite.
+ *
+ * $Id: config.c,v 1.103 1997/08/18 21:47:31 jkh Exp $
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include "sysinstall.h"
+#include <sys/disklabel.h>
+#include <sys/wait.h>
+#include <sys/errno.h>
+#include <sys/ioctl.h>
+#include <sys/fcntl.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <sys/mount.h>
+
+static Chunk *chunk_list[MAX_CHUNKS];
+static int nchunks;
+static int rootdev_is_od;
+
+/* arg to sort */
+static int
+chunk_compare(Chunk *c1, Chunk *c2)
+{
+ if (!c1 && !c2)
+ return 0;
+ else if (!c1 && c2)
+ return 1;
+ else if (c1 && !c2)
+ return -1;
+ else if (!c1->private_data && !c2->private_data)
+ return 0;
+ else if (c1->private_data && !c2->private_data)
+ return 1;
+ else if (!c1->private_data && c2->private_data)
+ return -1;
+ else
+ return strcmp(((PartInfo *)(c1->private_data))->mountpoint, ((PartInfo *)(c2->private_data))->mountpoint);
+}
+
+static void
+chunk_sort(void)
+{
+ int i, j;
+
+ for (i = 0; i < nchunks; i++) {
+ for (j = 0; j < nchunks; j++) {
+ if (chunk_compare(chunk_list[j], chunk_list[j + 1]) > 0) {
+ Chunk *tmp = chunk_list[j];
+
+ chunk_list[j] = chunk_list[j + 1];
+ chunk_list[j + 1] = tmp;
+ }
+ }
+ }
+}
+
+static void
+check_rootdev(Chunk **list, int n)
+{
+ int i;
+ Chunk *c;
+
+ rootdev_is_od = 0;
+ for (i = 0; i < n; i++) {
+ c = *list++;
+ if (c->type == part && (c->flags & CHUNK_IS_ROOT)
+ && strncmp(c->disk->name, "od", 2) == 0)
+ rootdev_is_od = 1;
+ }
+}
+
+static char *
+name_of(Chunk *c1)
+{
+ static char rootname[32];
+
+ /* Our boot blocks can't deal with root partitions on slices - need the compatbility name */
+ if (c1->type == part && c1->flags & CHUNK_IS_ROOT) {
+ sprintf(rootname, "%sa", c1->disk->name);
+ return rootname;
+ }
+ else
+ return c1->name;
+}
+
+static char *
+mount_point(Chunk *c1)
+{
+ if (c1->type == part && c1->subtype == FS_SWAP)
+ return "none";
+ else if (c1->type == part || c1->type == fat)
+ return ((PartInfo *)c1->private_data)->mountpoint;
+ return "/bogus";
+}
+
+static char *
+fstype(Chunk *c1)
+{
+ if (c1->type == fat)
+ return "msdos";
+ else if (c1->type == part) {
+ if (c1->subtype != FS_SWAP)
+ return "ufs";
+ else
+ return "swap";
+ }
+ return "bogus";
+}
+
+static char *
+fstype_short(Chunk *c1)
+{
+ if (c1->type == part) {
+ if (c1->subtype != FS_SWAP) {
+ if (rootdev_is_od == 0 && strncmp(c1->name, "od", 2) == 0)
+ return "rw,noauto";
+ else
+ return "rw";
+ }
+ else
+ return "sw";
+ }
+ else if (c1->type == fat) {
+ if (strncmp(c1->name, "od", 2) == 0)
+ return "ro,noauto";
+ else
+ return "ro";
+ }
+ return "bog";
+}
+
+static int
+seq_num(Chunk *c1)
+{
+ if (c1->type == part && c1->subtype != FS_SWAP) {
+ if (rootdev_is_od == 0 && strncmp(c1->name, "od", 2) == 0)
+ return 0;
+ else if (c1->flags & CHUNK_IS_ROOT)
+ return 1;
+ else
+ return 2;
+ }
+ return 0;
+}
+
+int
+configFstab(void)
+{
+ 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]));
+ Mkdir("/proc");
+ fprintf(fstab, "proc\t\t\t/proc\t\tprocfs\trw\t\t0\t0\n");
+
+ /* Now look for the CDROMs */
+ devs = deviceFind(NULL, DEVICE_TYPE_CDROM);
+ cnt = deviceCount(devs);
+
+ /* Write the first one out as /cdrom */
+ if (cnt) {
+ if (Mkdir("/cdrom")) {
+ msgConfirm("Unable to make mount point for: /cdrom");
+ }
+ else
+ fprintf(fstab, "/dev/%s\t\t/cdrom\t\tcd9660\tro,noauto\t0\t0\n", devs[0]->name);
+ }
+
+ /* Write the others out as /cdrom<n> */
+ for (i = 1; i < cnt; i++) {
+ char cdname[10];
+
+ sprintf(cdname, "/cdrom%d", i);
+ if (Mkdir(cdname)) {
+ msgConfirm("Unable to make mount point for: %s", cdname);
+ }
+ else
+ fprintf(fstab, "/dev/%s\t\t%s\tcd9660\tro,noauto\t0\t0\n", devs[i]->name, cdname);
+ }
+ 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 */
+
+/* Load the environment from an rc.conf file */
+void
+configEnvironmentRC_conf(char *config)
+{
+ 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 (strlen(cp))
+ variable_set2(lines[i], cp);
+ }
+ free(lines[i]);
+ }
+}
+
+/* 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 = FALSE;
+
+ if (!strncmp(lines[i], "domain", 6))
+ variable_set2(VAR_DOMAINNAME, string_skipwhite(string_prune(lines[i] + 6)));
+ else if (!strncmp(lines[i], "nameserver", 10) && !name_set) {
+ /* Only take the first nameserver setting - we're lame */
+ variable_set2(VAR_NAMESERVER, string_skipwhite(string_prune(lines[i] + 10)));
+ name_set = TRUE;
+ }
+ free(lines[i]);
+ }
+}
+
+/* Version of below for dispatch routines */
+int
+configRC(dialogMenuItem *unused)
+{
+ configRC_conf("/etc/rc.conf");
+ return DITEM_SUCCESS;
+}
+
+/*
+ * This sucks in /etc/rc.conf, substitutes anything needing substitution, then
+ * writes it all back out. It's pretty gross and needs re-writing at some point.
+ */
+void
+configRC_conf(char *config)
+{
+ FILE *fp;
+ char *lines[MAX_LINES], *cp;
+ Variable *v;
+ int i, nlines, len;
+
+ nlines = readConfig(config, lines, MAX_LINES);
+ if (nlines == -1)
+ return;
+
+ /* Now do variable substitutions */
+ for (v = VarHead; v; v = v->next) {
+ for (i = 0; i < nlines; i++) {
+ /* Skip the comments & non-variable settings */
+ if (lines[i][0] == '#' || !(cp = index(lines[i], '=')))
+ continue;
+ len = strlen(v->name);
+ if (!strncmp(lines[i], v->name, cp - lines[i]) && (cp - lines[i]) == len) {
+ char *cp2, *comment = NULL;
+
+ /* If trailing comment, try and preserve it */
+ if ((index(lines[i], '#')) != NULL) {
+ /* Find quotes */
+ if ((cp2 = index(cp, '"')) || (cp2 = index(cp, '\047')))
+ cp2 = index(cp2 + 1, *cp2);
+ if (cp2 && strlen(cp2 + 1)) {
+ comment = alloca(strlen(cp2));
+ strcpy(comment, cp2 + 1);
+ }
+ }
+ free(lines[i]);
+ lines[i] = (char *)malloc(strlen(v->name) + strlen(v->value) + (comment ? strlen(comment) : 0) + 10);
+ if (comment)
+ sprintf(lines[i], "%s=\"%s\"%s", v->name, v->value, comment);
+ else
+ sprintf(lines[i], "%s=\"%s\"\n", v->name, v->value);
+ }
+ }
+ }
+
+ /* Now write it all back out again */
+ if (isDebug())
+ msgDebug("Writing configuration changes to %s file..", config);
+ if (Fake)
+ fp = fdopen(DebugFD, "w");
+ else {
+ (void)vsystem("cp %s %s.previous", config, config);
+ fp = fopen(config, "w");
+ }
+ for (i = 0; i < nlines; i++) {
+ fprintf(fp, lines[i]);
+ /* Stand by for bogus special case handling - we try to dump the interface specs here */
+ if (!strncmp(lines[i], VAR_INTERFACES, strlen(VAR_INTERFACES))) {
+ Device **devp;
+ int j, cnt;
+
+ devp = deviceFind(NULL, DEVICE_TYPE_NETWORK);
+ cnt = deviceCount(devp);
+ for (j = 0; j < cnt; j++) {
+ char iname[255], toadd[512];
+ int k, addit = TRUE;
+
+ if (!strncmp(devp[j]->name, "ppp", 3) || !strncmp(devp[j]->name, "tun", 3))
+ continue;
+
+ snprintf(iname, 255, "%s%s", VAR_IFCONFIG, devp[j]->name);
+ if ((cp = variable_get(iname))) {
+ snprintf(toadd, sizeof toadd, "%s=\"%s\"\n", iname, cp);
+ for (k = 0; k < nlines; k++) {
+ if (!strcmp(lines[k], toadd)) {
+ addit = FALSE;
+ break;
+ }
+ }
+ if (addit)
+ fprintf(fp, toadd);
+ }
+ }
+ }
+ free(lines[i]);
+ }
+ fclose(fp);
+}
+
+int
+configSaver(dialogMenuItem *self)
+{
+ variable_set((char *)self->data);
+ if (!variable_get(VAR_BLANKTIME))
+ variable_set2(VAR_BLANKTIME, "300");
+ return DITEM_SUCCESS;
+}
+
+int
+configSaverTimeout(dialogMenuItem *self)
+{
+ return (variable_get_value(VAR_BLANKTIME,
+ "Enter time-out period in seconds for screen saver") ?
+ DITEM_SUCCESS : DITEM_FAILURE) | DITEM_RESTORE;
+}
+
+int
+configRegister(dialogMenuItem *self)
+{
+ return DITEM_STATUS(registerOpenDialog()) | DITEM_RESTORE;
+}
+
+int
+configNTP(dialogMenuItem *self)
+{
+ int status;
+
+ status = variable_get_value(VAR_NTPDATE_FLAGS,
+ "Enter the name of an NTP server")
+ ? DITEM_SUCCESS : DITEM_FAILURE;
+ if (status == DITEM_SUCCESS) {
+ static char tmp[255];
+
+ snprintf(tmp, sizeof(tmp), "ntpdate_enable=YES,ntpdate_flags=%s",
+ variable_get(VAR_NTPDATE_FLAGS));
+ self->data = tmp;
+ dmenuSetVariables(self);
+ }
+ return status | DITEM_RESTORE;
+}
+
+int
+configUsers(dialogMenuItem *self)
+{
+ dialog_clear_norefresh();
+ dmenuOpenSimple(&MenuUsermgmt, FALSE);
+ dialog_clear();
+ return DITEM_SUCCESS | DITEM_RESTORE;
+}
+
+int
+configXEnvironment(dialogMenuItem *self)
+{
+#ifndef USE_XIG_ENVIRONMENT
+ char *config, *execfile;
+
+ dialog_clear_norefresh();
+ if (!dmenuOpenSimple(&MenuXF86Config, FALSE))
+ return DITEM_FAILURE | DITEM_RESTORE;
+ systemExecute("/sbin/ldconfig /usr/lib /usr/X11R6/lib /usr/local/lib /usr/lib/compat");
+ config = variable_get(VAR_XF86_CONFIG);
+ if (!config)
+ return DITEM_FAILURE | DITEM_RESTORE;
+ execfile = string_concat("/usr/X11R6/bin/", config);
+ if (file_executable(execfile)) {
+ dialog_clear_norefresh();
+ if (!file_readable("/dev/mouse") && !msgYesNo("Does this system have a mouse attached to it?"))
+ dmenuOpenSimple(&MenuMouse, FALSE);
+ dialog_clear();
+ systemExecute(execfile);
+ return DITEM_SUCCESS | DITEM_RESTORE;
+ }
+ else {
+ dialog_clear_norefresh();
+ msgConfirm("XFree86 does not appear to be installed! Please install\n"
+ "The XFree86 distribution before attempting to configure it.");
+ return DITEM_FAILURE | DITEM_RESTORE;
+ }
+
+#else /* USE_XIG_ENVIRONMENT */
+ int i;
+
+ /* Make sure we're sane first */
+ if (!file_readable("/usr/X11R6/lib/X11/AcceleratedX/bin/Xinstall") ||
+ !file_readable("/usr/X11R6/lib/X11/AcceleratedX/bin/Xsetup")) {
+ dialog_clear_norefresh();
+ msgConfirm("Hmmm! It looks like you elected not to install the AccelleratedX\n"
+ "server package (or the installation failed somehow). If this was\n"
+ "an omission rather than an error, please go to the Distributions\n"
+ "menu, select the Custom distribution options and then choose your X11\n"
+ "distribution components from the XFree86 menu. AccelX will be selected\n"
+ "as the default server automatically.");
+ return DITEM_FAILURE | DITEM_RESTORE;
+ }
+
+ if (mediaDevice && mediaDevice->type != DEVICE_TYPE_CDROM) {
+ if (DITEM_STATUS(mediaSetCDROM(NULL)) != DITEM_SUCCESS || !mediaDevice || !mediaDevice->init(mediaDevice)) {
+ msgConfirm("I can't mount the CDE distribution from CDROM, sorry.\n"
+ "Please make sure you have the 1st CD of your FreeBSD Desktop/Pro\n"
+ "distribution in the drive before trying this operation.");
+ return DITEM_FAILURE | DITEM_RESTORE;
+ }
+ }
+
+ if (!directory_exists("/dist/CDE/")) {
+ msgConfirm("Hmmm! I can't find the CDE distribution. Please place the 1st CD of your\n"
+ "FreeBSD Desktop/Pro distribution in the drive and try this operation again.");
+ return DITEM_FAILURE | DITEM_RESTORE;
+ }
+
+ /* Pre-extract base sets in kludge to work around chicken-and-egg problem with CDE and Xaccel */
+ if (directory_exists("/dist/CDE/") && !file_readable("/usr/X11R6/bin/xterm")) {
+ msgNotify("Installing bootstrap X11 tools from CDE distribution.");
+ systemExecute("tar xpf /dist/CDE/FreeBSD/packages/X11-RUN/archive -C /");
+ systemExecute("tar xpf /dist/CDE/FreeBSD/packages/X11-PRG/archive -C /");
+ }
+ systemExecute("/sbin/ldconfig /usr/lib /usr/X11R6/lib /usr/dt/lib /usr/local/lib /usr/lib/compat");
+
+ dialog_clear_norefresh();
+ msgNotify("Running AcceleratedX 3.1 installation procedure, please wait.");
+ if ((i = vsystem("/usr/X11R6/lib/X11/AcceleratedX/bin/Xinstall"))) {
+ dialog_clear_norefresh();
+ msgConfirm("Installation procedure failed, error code %d! Please report\n"
+ "error to Walnut Creek CDROM tech support (either send email\n"
+ "to support@cdrom.com or call +1 510 603 1234). Thank you!", i);
+ return DITEM_FAILURE | DITEM_RESTORE;
+ }
+ else {
+ dialog_clear();
+ systemExecute("/usr/X11R6/lib/X11/AcceleratedX/bin/Xsetup");
+ }
+ if (directory_exists("/dist/CDE")) {
+ dialog_clear_norefresh();
+ msgNotify("Running CDE installation - please wait (this may take awhile!).");
+ dialog_clear();
+ clear();
+ refresh();
+ i = systemExecute("(cd /dist/CDE; sh Install)");
+ dialog_clear();
+ if (i) {
+ msgConfirm("/dist/CDE/dtinstall script returned an error status!\n\n"
+ "To try again, you should run this command manually after the system\n"
+ "is up (and if your CDROM is mounted in the standard location, the path\n"
+ "to it will actually be /cdrom/CDE/dtinstall when you run it later).\n");
+ }
+ else {
+ if (file_readable("/dist/CDE/post-install"))
+ systemExecute("/dist/CDE/post-install");
+ /* Repair the damage done by the CDE installation */
+ msgNotify("Doing final adjustments to Xaccel distribution...");
+ systemExecute("/usr/X11R6/lib/X11/AcceleratedX/bin/Xinstall");
+ }
+ }
+ return DITEM_SUCCESS | DITEM_RESTORE;
+#endif /* USE_XIG_ENVIRONMENT */
+}
+
+void
+configResolv(void)
+{
+ FILE *fp;
+ char *cp, *dp, *hp;
+
+ cp = variable_get(VAR_NAMESERVER);
+ if (!cp || !*cp)
+ goto skip;
+ Mkdir("/etc");
+ fp = fopen("/etc/resolv.conf", "w");
+ if (!fp)
+ return;
+ if (variable_get(VAR_DOMAINNAME))
+ fprintf(fp, "domain\t%s\n", variable_get(VAR_DOMAINNAME));
+ fprintf(fp, "nameserver\t%s\n", cp);
+ fclose(fp);
+ if (isDebug())
+ msgDebug("Wrote out /etc/resolv.conf\n");
+
+skip:
+ dp = variable_get(VAR_DOMAINNAME);
+ cp = variable_get(VAR_IPADDR);
+ hp = variable_get(VAR_HOSTNAME);
+ /* Tack ourselves into /etc/hosts */
+ fp = fopen("/etc/hosts", "w");
+ if (!fp)
+ return;
+ /* Add an entry for localhost */
+ fprintf(fp, "127.0.0.1\t\tlocalhost.%s localhost\n", dp ? dp : "my.domain");
+ /* Now the host entries, if applicable */
+ if (cp && cp[0] != '0' && hp) {
+ char cp2[255];
+
+ if (!index(hp, '.'))
+ cp2[0] = '\0';
+ else {
+ SAFE_STRCPY(cp2, hp);
+ *(index(cp2, '.')) = '\0';
+ }
+ fprintf(fp, "%s\t\t%s %s\n", cp, hp, cp2);
+ fprintf(fp, "%s\t\t%s.\n", cp, hp);
+ }
+ fclose(fp);
+ if (isDebug())
+ msgDebug("Wrote out /etc/hosts\n");
+}
+
+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")
+ ? 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");
+ if (!strcmp(cp, "gated")) {
+ if (package_add(variable_get(VAR_GATED_PKG)) != DITEM_SUCCESS) {
+ msgConfirm("Unable to load gated package. Falling back to no router.");
+ variable_unset(VAR_ROUTER);
+ variable_unset(VAR_ROUTERFLAGS);
+ variable_set2(VAR_ROUTER_ENABLE, "NO");
+ 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")
+ ? DITEM_SUCCESS : DITEM_FAILURE;
+ if (ret != DITEM_SUCCESS)
+ variable_unset(VAR_ROUTERFLAGS);
+ }
+ }
+ else {
+ /* No router case */
+ variable_set2(VAR_ROUTER_ENABLE, "NO");
+ variable_unset(VAR_ROUTERFLAGS);
+ variable_unset(VAR_ROUTER);
+ }
+ }
+ return ret | DITEM_RESTORE;
+}
+
+int
+configPackages(dialogMenuItem *self)
+{
+ static PkgNode top, plist;
+ static Boolean index_initted = FALSE;
+ PkgNodePtr tmp;
+ FILE *fp;
+
+ if (!mediaVerify())
+ return DITEM_FAILURE;
+
+ if (!mediaDevice->init(mediaDevice))
+ return DITEM_FAILURE;
+
+ if (!index_initted) {
+ msgNotify("Attempting to fetch packages/INDEX file from selected media.");
+ fp = mediaDevice->get(mediaDevice, "packages/INDEX", TRUE);
+ if (!fp) {
+ dialog_clear_norefresh();
+ msgConfirm("Unable to get packages/INDEX file from selected media.\n"
+ "This may be because the packages collection is not available at\n"
+ "on the distribution media you've chosen (most likely an FTP site\n"
+ "without the packages collection mirrored). Please verify media\n"
+ "(or path to media) and try again. If your local site does not\n"
+ "carry the packages collection, then we recommend either a CD\n"
+ "distribution or the master distribution on ftp.freebsd.org.");
+ mediaDevice->shutdown(mediaDevice);
+ return DITEM_FAILURE | DITEM_RESTORE;
+ }
+ msgNotify("Located INDEX, now reading package data from it...");
+ index_init(&top, &plist);
+ if (index_read(fp, &top)) {
+ msgConfirm("I/O or format error on packages/INDEX file.\n"
+ "Please verify media (or path to media) and try again.");
+ fclose(fp);
+ return DITEM_FAILURE | DITEM_RESTORE;
+ }
+ fclose(fp);
+ index_sort(&top);
+ index_initted = TRUE;
+ }
+ while (1) {
+ int ret, pos, scroll;
+
+ /* Bring up the packages menu */
+ pos = scroll = 0;
+ index_menu(&top, &plist, &pos, &scroll);
+
+ if (plist.kids && plist.kids->name) {
+ /* Now show the packing list menu */
+ pos = scroll = 0;
+ ret = index_menu(&plist, NULL, &pos, &scroll);
+ if (ret & DITEM_LEAVE_MENU)
+ break;
+ else if (DITEM_STATUS(ret) != DITEM_FAILURE) {
+ index_extract(mediaDevice, &top, &plist);
+ break;
+ }
+ }
+ else {
+ dialog_clear_norefresh();
+ msgConfirm("No packages were selected for extraction.");
+ break;
+ }
+ }
+ tmp = plist.kids;
+ while (tmp) {
+ PkgNodePtr tmp2 = tmp->next;
+
+ safe_free(tmp);
+ tmp = tmp2;
+ }
+ index_init(NULL, &plist);
+ return DITEM_SUCCESS | DITEM_RESTORE;
+}
+
+#ifdef NETCON_EXTENTIONS
+/* Load novell client/server package */
+int
+configNovell(dialogMenuItem *self)
+{
+ int ret = DITEM_SUCCESS;
+
+ if (!RunningAsInit) {
+ msgConfirm("This package can only be installed in multi-user mode.");
+ return ret;
+ }
+ if (variable_get(VAR_NOVELL))
+ variable_unset(VAR_NOVELL);
+ else {
+ ret = package_add(PACKAGE_NETCON);
+ if (DITEM_STATUS(ret) == DITEM_SUCCESS)
+ variable_set2(VAR_NOVELL, "YES");
+ }
+ return ret | DITEM_RESTORE;
+}
+#endif
+
+/* Load pcnfsd package */
+int
+configPCNFSD(dialogMenuItem *self)
+{
+ int ret = DITEM_SUCCESS;
+
+ if (variable_get(VAR_PCNFSD))
+ variable_unset(VAR_PCNFSD);
+ else {
+ ret = package_add(variable_get(VAR_PCNFSD_PKG));
+ if (DITEM_STATUS(ret) == DITEM_SUCCESS) {
+ variable_set2(VAR_PCNFSD, "YES");
+ variable_set2("mountd_flags", "-n");
+ }
+ }
+ return ret;
+}
+
+int
+configNFSServer(dialogMenuItem *self)
+{
+ char cmd[256];
+
+ /* If we're an NFS server, we need an exports file */
+ if (!file_readable("/etc/exports")) {
+ WINDOW *w = savescr();
+
+ if (file_readable("/etc/exports.disabled"))
+ vsystem("mv /etc/exports.disabled /etc/exports");
+ else {
+ dialog_clear_norefresh();
+ msgConfirm("Operating as an NFS server means that you must first configure\n"
+ "an /etc/exports file to indicate which hosts are allowed certain\n"
+ "kinds of access to your local file systems.\n"
+ "Press [ENTER] now to invoke an editor on /etc/exports\n");
+ vsystem("echo '#The following examples export /usr to 3 machines named after ducks,' > /etc/exports");
+ vsystem("echo '#/home and all directories under it to machines named after dead rock stars' >> /etc/exports");
+ vsystem("echo '#and, finally, /a to 2 privileged machines allowed to write on it as root.' >> /etc/exports");
+ vsystem("echo '#/usr huey louie dewie' >> /etc/exports");
+ vsystem("echo '#/home -alldirs janice jimmy frank' >> /etc/exports");
+ vsystem("echo '#/a -maproot=0 bill albert' >> /etc/exports");
+ vsystem("echo '#' >> /etc/exports");
+ vsystem("echo '# You should replace these lines with your actual exported filesystems.' >> /etc/exports");
+ vsystem("echo >> /etc/exports");
+ sprintf(cmd, "%s /etc/exports", variable_get(VAR_EDITOR));
+ dialog_clear();
+ systemExecute(cmd);
+ restorescr(w);
+ }
+ variable_set2(VAR_NFS_SERVER, "YES");
+ }
+ else if (variable_get(VAR_NFS_SERVER)) { /* We want to turn it off again? */
+ vsystem("mv -f /etc/exports /etc/exports.disabled");
+ variable_unset(VAR_NFS_SERVER);
+ }
+ return DITEM_SUCCESS;
+}
diff --git a/usr.sbin/sysinstall/dev2c.sh b/usr.sbin/sysinstall/dev2c.sh
new file mode 100644
index 0000000..929e9e7
--- /dev/null
+++ b/usr.sbin/sysinstall/dev2c.sh
@@ -0,0 +1,80 @@
+:
+#
+# ----------------------------------------------------------------------------
+# "THE BEER-WARE LICENSE" (Revision 42):
+# <phk@login.dknet.dk> wrote this file. As long as you retain this notice you
+# can do whatever you want with this stuff. If we meet some day, and you think
+# this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp
+# ----------------------------------------------------------------------------
+#
+# $Id$
+#
+# During installation, we suffer badly of we have to run MAKEDEV. MAKEDEV
+# need sh, ln, chown, mknod, awk, rm, test and probably emacs too when
+# we come down to it. So instead this script will make a C-procedure which
+# makes all the B & C nodes of a specified directory.
+#
+# Poul-Henning
+
+(cd $1; ls -li ) | sed 's/,//' | awk '
+BEGIN {
+ while (getline < "/etc/passwd") {
+ split($0,a,":")
+ uid[a[1]] = a[3]
+ }
+ while (getline < "/etc/group") {
+ split($0,a,":")
+ gid[a[1]] = a[3]
+ }
+ printf("/*\n");
+ printf(" * This file is generated from the contents of /dev\n");
+ printf(" */\n");
+ printf("#define CHK(foo) {i = foo;}\n");
+ printf("#include <unistd.h>\n");
+ printf("#include <sys/types.h>\n");
+ printf("#include <sys/stat.h>\n");
+ printf("int makedevs()\n{\n\tint i=0;\n");
+ }
+ {
+ printf ("/* %s */\n",$0)
+ $4 = uid[$4]
+ $5 = gid[$5]
+ if (substr($2,1,1) == "b") {
+ k="S_IFBLK"
+ } else if (substr($2,1,1) == "c") {
+ k="S_IFCHR"
+ } else if (substr($2,1,1) == "d") {
+ next
+ } else if (substr($2,1,1) == "-") {
+ next
+ } else {
+ next
+ }
+ m = 0;
+ if (substr($2,2,1) == "r") m += 400;
+ if (substr($2,3,1) == "w") m += 200;
+ if (substr($2,4,1) == "x") m += 100;
+ if (substr($2,5,1) == "r") m += 40;
+ if (substr($2,6,1) == "w") m += 20;
+ if (substr($2,7,1) == "x") m += 10;
+ if (substr($2,8,1) == "r") m += 4;
+ if (substr($2,9,1) == "w") m += 2;
+ if (substr($2,10,1) == "x") m += 1;
+
+ if (a[$1] != 0) {
+ printf ("\tCHK(link(\"%s\",\"%s\"));\n", \
+ a[$1],$11)
+ } else {
+ printf ("\tCHK(mknod(\"%s\",%s,makedev(%d,%d)));\n", \
+ $11, k, $6, $7)
+ printf ("\tCHK(chmod(\"%s\",0%d));\n", \
+ $11, m)
+ printf ("\tCHK(chown(\"%s\",%d,%d));\n", \
+ $11, $4,$5)
+ a[$1] = $11
+ }
+ }
+END {
+ printf("\treturn i;\n}\n");
+ }
+'
diff --git a/usr.sbin/sysinstall/devices.c b/usr.sbin/sysinstall/devices.c
new file mode 100644
index 0000000..e744255
--- /dev/null
+++ b/usr.sbin/sysinstall/devices.c
@@ -0,0 +1,424 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last program in the `sysinstall' line - the next
+ * generation being essentially a complete rewrite.
+ *
+ * $Id$
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include "sysinstall.h"
+#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>
+
+static Device *Devices[DEV_MAX];
+static int numDevs;
+
+static struct {
+ DeviceType type;
+ char *name;
+ char *description;
+} device_names[] = {
+ { DEVICE_TYPE_CDROM, "cd0a", "SCSI CDROM drive" },
+ { DEVICE_TYPE_CDROM, "mcd0a", "Mitsumi (old model) CDROM drive" },
+ { DEVICE_TYPE_CDROM, "scd0a", "Sony CDROM drive - CDU31/33A type", },
+ { DEVICE_TYPE_CDROM, "matcd0a", "Matsushita CDROM ('sound blaster' type)" },
+ { DEVICE_TYPE_CDROM, "wcd0c", "ATAPI IDE CDROM" },
+ { DEVICE_TYPE_TAPE, "rst0", "SCSI tape drive" },
+ { DEVICE_TYPE_TAPE, "rft0", "Floppy tape drive (QIC-02)" },
+ { DEVICE_TYPE_TAPE, "rwt0", "Wangtek tape drive" },
+ { DEVICE_TYPE_DISK, "sd", "SCSI disk device" },
+ { DEVICE_TYPE_DISK, "wd", "IDE/ESDI/MFM/ST506 disk device" },
+ { DEVICE_TYPE_DISK, "od", "SCSI optical disk device" },
+ { DEVICE_TYPE_FLOPPY, "fd0", "floppy drive unit A" },
+ { DEVICE_TYPE_FLOPPY, "fd1", "floppy drive unit B" },
+ { DEVICE_TYPE_FLOPPY, "od0", "SCSI optical disk/floppy format" },
+ { DEVICE_TYPE_NETWORK, "cuaa0", "%s on serial port 0 (COM1)" },
+ { DEVICE_TYPE_NETWORK, "cuaa1", "%s on serial port 1 (COM2)" },
+ { DEVICE_TYPE_NETWORK, "cuaa2", "%s on serial port 2 (COM3)" },
+ { DEVICE_TYPE_NETWORK, "cuaa3", "%s on serial port 3 (COM4)" },
+ { DEVICE_TYPE_NETWORK, "lp0", "Parallel Port IP (PLIP) using laplink cable" },
+ { DEVICE_TYPE_NETWORK, "lo", "Loop-back (local) network interface" },
+ { DEVICE_TYPE_NETWORK, "de", "DEC DE435 PCI NIC or other DC21040-AA based card" },
+ { DEVICE_TYPE_NETWORK, "fxp", "Intel EtherExpress Pro/100B PCI Fast Ethernet card" },
+ { DEVICE_TYPE_NETWORK, "ed", "WD/SMC 80xx; Novell NE1000/2000; 3Com 3C503 card" },
+ { DEVICE_TYPE_NETWORK, "ep", "3Com 3C509 ethernet card" },
+ { DEVICE_TYPE_NETWORK, "el", "3Com 3C501 ethernet card" },
+ { DEVICE_TYPE_NETWORK, "ex", "Intel EtherExpress Pro/10 ethernet card" },
+ { DEVICE_TYPE_NETWORK, "fe", "Fujitsu MB86960A/MB86965A ethernet card" },
+ { DEVICE_TYPE_NETWORK, "ie", "AT&T StarLAN 10 and EN100; 3Com 3C507; NI5210" },
+ { DEVICE_TYPE_NETWORK, "ix", "Intel Etherexpress ethernet card" },
+ { DEVICE_TYPE_NETWORK, "le", "DEC EtherWorks 2 or 3 ethernet card" },
+ { DEVICE_TYPE_NETWORK, "lnc", "Lance/PCnet (Isolan/Novell NE2100/NE32-VL) ethernet" },
+ { DEVICE_TYPE_NETWORK, "vx", "3COM 3c590 / 3c595 / 3c9xx ethernet card" },
+ { DEVICE_TYPE_NETWORK, "ze", "IBM/National Semiconductor PCMCIA ethernet card" },
+ { DEVICE_TYPE_NETWORK, "zp", "3Com Etherlink III PCMCIA ethernet card" },
+ { NULL },
+};
+
+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(char *name, char *try)
+{
+ int fd;
+
+ snprintf(try, FILENAME_MAX, "/dev/%s", name);
+ fd = open(try, O_RDONLY);
+ if (fd > 0)
+ return fd;
+ if (errno == EBUSY)
+ return -1;
+ snprintf(try, FILENAME_MAX, "/mnt/dev/%s", name);
+ fd = open(try, O_RDONLY);
+ return fd;
+}
+
+/* Register a new device in the devices array */
+Device *
+deviceRegister(char *name, char *desc, char *devname, DeviceType type, Boolean enabled,
+ Boolean (*init)(Device *), FILE * (*get)(Device *, char *, Boolean),
+ void (*shutdown)(Device *), void *private)
+{
+ Device *newdev = NULL;
+
+ if (numDevs == DEV_MAX)
+ msgFatal("Too many devices found!");
+ else {
+ newdev = new_device(name);
+ newdev->description = desc;
+ newdev->devname = devname;
+ newdev->type = type;
+ newdev->enabled = enabled;
+ newdev->init = init ? init : dummyInit;
+ newdev->get = get ? get : dummyGet;
+ newdev->shutdown = shutdown ? shutdown : dummyShutdown;
+ newdev->private = private;
+ Devices[numDevs] = newdev;
+ Devices[++numDevs] = NULL;
+ }
+ return newdev;
+}
+
+/* Get all device information for devices we have attached */
+void
+deviceGetAll(void)
+{
+ int i, fd, s;
+ struct ifconf ifc;
+ struct ifreq *ifptr, *end;
+ int ifflags;
+ char buffer[INTERFACE_MAX * sizeof(struct ifreq)];
+ char **names;
+
+ /* Try and get the disks first */
+ if ((names = Disk_Names()) != NULL) {
+ int i;
+
+ for (i = 0; names[i]; i++) {
+ Chunk *c1;
+ Disk *d;
+
+ d = Open_Disk(names[i]);
+ if (!d)
+ msgFatal("Unable to open disk %s", names[i]);
+
+ (void)deviceRegister(names[i], names[i], d->name, DEVICE_TYPE_DISK, FALSE, NULL, NULL, NULL, d);
+ msgDebug("Found a disk device named %s\n", names[i]);
+
+ /* Look for existing DOS partitions to register */
+ for (c1 = d->chunks->part; c1; c1 = c1->next) {
+ if (c1->type == fat || c1->type == extended) {
+ Device *dev;
+ char devname[80];
+
+ /* Got one! */
+ sprintf(devname, "/dev/%s", c1->name);
+ dev = deviceRegister(c1->name, c1->name, strdup(devname), DEVICE_TYPE_DOS, TRUE,
+ mediaInitDOS, mediaGetDOS, mediaShutdownDOS, NULL);
+ dev->private = c1;
+ msgDebug("Found a DOS partition %s on drive %s\n", c1->name, d->name);
+ }
+ }
+ }
+ free(names);
+ }
+
+ /* Now go for the network interfaces. Stolen shamelessly from ifconfig! */
+ ifc.ifc_len = sizeof(buffer);
+ ifc.ifc_buf = buffer;
+
+ s = socket(AF_INET, SOCK_DGRAM, 0);
+ if (s < 0) {
+ msgConfirm("ifconfig: socket");
+ goto skipif; /* Jump over network iface probing */
+ }
+ if (ioctl(s, SIOCGIFCONF, (char *) &ifc) < 0) {
+ msgConfirm("ifconfig (SIOCGIFCONF)");
+ goto skipif; /* Jump over network iface probing */
+ }
+ ifflags = ifc.ifc_req->ifr_flags;
+ end = (struct ifreq *) (ifc.ifc_buf + ifc.ifc_len);
+ for (ifptr = ifc.ifc_req; ifptr < end; ifptr++) {
+ char *descr;
+
+ /* If it's not a link entry, forget it */
+ if (ifptr->ifr_ifru.ifru_addr.sa_family != AF_LINK)
+ continue;
+
+ /* Eliminate network devices that don't make sense */
+ if (!strncmp(ifptr->ifr_name, "lo0", 3))
+ continue;
+
+ /* If we have a slip device, don't register it */
+ if (!strncmp(ifptr->ifr_name, "sl", 2)) {
+ continue;
+ }
+ /* And the same for ppp */
+ if (!strncmp(ifptr->ifr_name, "tun", 3) || !strncmp(ifptr->ifr_name, "ppp", 3)) {
+ continue;
+ }
+ /* Try and find its description */
+ for (i = 0, descr = NULL; device_names[i].name; i++) {
+ int len = strlen(device_names[i].name);
+
+ if (!strncmp(ifptr->ifr_name, device_names[i].name, len)) {
+ descr = device_names[i].description;
+ break;
+ }
+ }
+ if (!descr)
+ descr = "<unknown network interface type>";
+
+ deviceRegister(ifptr->ifr_name, descr, strdup(ifptr->ifr_name), DEVICE_TYPE_NETWORK, TRUE,
+ mediaInitNetwork, NULL, mediaShutdownNetwork, NULL);
+ msgDebug("Found a network device named %s\n", ifptr->ifr_name);
+ close(s);
+ if ((s = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+ msgConfirm("ifconfig: socket");
+ continue;
+ }
+ if (ifptr->ifr_addr.sa_len) /* I'm not sure why this is here - it's inherited */
+ ifptr = (struct ifreq *)((caddr_t)ifptr + ifptr->ifr_addr.sa_len - sizeof(struct sockaddr));
+ }
+
+skipif:
+ /* Finally, 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++) {
+ char try[FILENAME_MAX];
+
+ switch(device_names[i].type) {
+ case DEVICE_TYPE_CDROM:
+ fd = deviceTry(device_names[i].name, try);
+ msgDebug("Try for %s returns errno %d\n", device_names[i].name, errno);
+ if (fd >= 0 || errno == EBUSY) { /* EBUSY if already mounted */
+ if (fd >= 0) close(fd);
+ (void)deviceRegister(device_names[i].name, device_names[i].description, strdup(try),
+ DEVICE_TYPE_CDROM, TRUE, mediaInitCDROM, mediaGetCDROM,
+ mediaShutdownCDROM, NULL);
+ msgDebug("Found a CDROM device named %s\n", device_names[i].name);
+ }
+ break;
+
+ case DEVICE_TYPE_TAPE:
+ fd = deviceTry(device_names[i].name, try);
+ if (fd >= 0) {
+ close(fd);
+ deviceRegister(device_names[i].name, device_names[i].description, strdup(try),
+ DEVICE_TYPE_TAPE, TRUE, mediaInitTape, mediaGetTape, mediaShutdownTape, NULL);
+ msgDebug("Found a TAPE device named %s\n", device_names[i].name);
+ }
+ break;
+
+ case DEVICE_TYPE_FLOPPY:
+ fd = deviceTry(device_names[i].name, try);
+ if (fd >= 0) {
+ close(fd);
+ deviceRegister(device_names[i].name, device_names[i].description, strdup(try),
+ DEVICE_TYPE_FLOPPY, TRUE, mediaInitFloppy, mediaGetFloppy,
+ mediaShutdownFloppy, NULL);
+ msgDebug("Found a floppy device named %s\n", device_names[i].name);
+ }
+ break;
+
+ case DEVICE_TYPE_NETWORK:
+ fd = deviceTry(device_names[i].name, try);
+ /* 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");
+ deviceRegister("sl0", newdesc, strdup(try), DEVICE_TYPE_NETWORK, TRUE, mediaInitNetwork,
+ NULL, mediaShutdownNetwork, NULL);
+ msgDebug("Add mapping for %s on %s to sl0\n", device_names[i].name, try);
+ newdesc = safe_malloc(strlen(cp) + 50);
+ sprintf(newdesc, cp, "PPP interface");
+ deviceRegister("ppp0", newdesc, strdup(try), DEVICE_TYPE_NETWORK, TRUE, mediaInitNetwork,
+ NULL, mediaShutdownNetwork, NULL);
+ msgDebug("Add mapping for %s on %s to ppp0\n", device_names[i].name, try);
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+}
+
+/*
+ * Find all devices that match the criteria, allowing "wildcarding" as well
+ * by allowing NULL or ANY values to match all. The array returned is static
+ * and may be used until the next invocation of deviceFind().
+ */
+Device **
+deviceFind(char *name, DeviceType class)
+{
+ static Device *found[DEV_MAX];
+ int i, j;
+
+ j = 0;
+ for (i = 0; i < numDevs; i++) {
+ if ((!name || !strcmp(Devices[i]->name, name))
+ && (class == DEVICE_TYPE_ANY || class == Devices[i]->type))
+ found[j++] = Devices[i];
+ }
+ found[j] = NULL;
+ return j ? found : NULL;
+}
+
+Device **
+deviceFindDescr(char *name, char *desc, DeviceType class)
+{
+ static Device *found[DEV_MAX];
+ int i, j;
+
+ j = 0;
+ for (i = 0; i < numDevs; i++) {
+ if ((!name || !strcmp(Devices[i]->name, name)) &&
+ (!desc || !strcmp(Devices[i]->description, desc)) &&
+ (class == DEVICE_TYPE_ANY || class == Devices[i]->type))
+ found[j++] = Devices[i];
+ }
+ found[j] = NULL;
+ return j ? found : NULL;
+}
+
+int
+deviceCount(Device **devs)
+{
+ int i;
+
+ if (!devs)
+ return 0;
+ for (i = 0; devs[i]; i++);
+ return i;
+}
+
+/*
+ * Create a menu listing all the devices of a certain type in the system.
+ * The passed-in menu is expected to be a "prototype" from which the new
+ * menu is cloned.
+ */
+DMenu *
+deviceCreateMenu(DMenu *menu, DeviceType type, int (*hook)(dialogMenuItem *d), int (*check)(dialogMenuItem *d))
+{
+ Device **devs;
+ int numdevs;
+ DMenu *tmp = NULL;
+ int i, j;
+
+ devs = deviceFind(NULL, type);
+ numdevs = deviceCount(devs);
+ if (!numdevs)
+ return NULL;
+ tmp = (DMenu *)safe_malloc(sizeof(DMenu) + (sizeof(dialogMenuItem) * (numdevs + 1)));
+ bcopy(menu, tmp, sizeof(DMenu));
+ for (i = 0; devs[i]; i++) {
+ tmp->items[i].prompt = devs[i]->name;
+ for (j = 0; j < numDevs; j++) {
+ if (devs[i] == Devices[j]) {
+ tmp->items[i].title = Devices[j]->description;
+ break;
+ }
+ }
+ if (j == numDevs)
+ tmp->items[i].title = "<unknown device type>";
+ tmp->items[i].fire = hook;
+ tmp->items[i].checked = check;
+ }
+ tmp->items[i].title = NULL;
+ return tmp;
+}
diff --git a/usr.sbin/sysinstall/disks.c b/usr.sbin/sysinstall/disks.c
new file mode 100644
index 0000000..9c8647f
--- /dev/null
+++ b/usr.sbin/sysinstall/disks.c
@@ -0,0 +1,731 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last program in the `sysinstall' line - the next
+ * generation being essentially a complete rewrite.
+ *
+ * $Id: disks.c,v 1.92 1997/10/12 16:21:09 jkh Exp $
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include "sysinstall.h"
+#include <ctype.h>
+#include <sys/disklabel.h>
+
+/* Where we start displaying chunk information on the screen */
+#define CHUNK_START_ROW 5
+
+/* Where we keep track of MBR chunks */
+static struct chunk *chunk_info[16];
+static int current_chunk;
+
+static void diskPartitionNonInteractive(Device *dev);
+
+static void
+record_chunks(Disk *d)
+{
+ struct chunk *c1 = NULL;
+ int i = 0;
+ int last_free = 0;
+
+ if (!d->chunks)
+ msgFatal("No chunk list found for %s!", d->name);
+
+ for (c1 = d->chunks->part; c1; c1 = c1->next) {
+ if (c1->type == unused && c1->size > last_free) {
+ last_free = c1->size;
+ current_chunk = i;
+ }
+ chunk_info[i++] = c1;
+ }
+ chunk_info[i] = NULL;
+ if (current_chunk >= i)
+ current_chunk = i - 1;
+}
+
+static int Total;
+
+static void
+print_chunks(Disk *d)
+{
+ int row;
+ int i;
+
+ for (i = Total = 0; chunk_info[i]; i++)
+ Total += chunk_info[i]->size;
+ if (d->bios_cyl > 65536 || d->bios_hd > 256 || d->bios_sect >= 64) {
+ dialog_clear_norefresh();
+ msgConfirm("WARNING: A geometry of %d/%d/%d for %s is incorrect. Using\n"
+ "a more likely geometry. If this geometry is incorrect or you\n"
+ "are unsure as to whether or not it's correct, please consult\n"
+ "the Hardware Guide in the Documentation submenu or use the\n"
+ "(G)eometry command to change it now.\n\n"
+ "Remember: you need to enter whatever your BIOS thinks the\n"
+ "geometry is! For IDE, it's what you were told in the BIOS\n"
+ "setup. For SCSI, it's the translation mode your controller is\n"
+ "using. Do NOT use a ``physical geometry''.",
+ d->bios_cyl, d->bios_hd, d->bios_sect, d->name);
+ Sanitize_Bios_Geom(d);
+ }
+ attrset(A_NORMAL);
+ mvaddstr(0, 0, "Disk name:\t");
+ clrtobot();
+ attrset(A_REVERSE); addstr(d->name); attrset(A_NORMAL);
+ attrset(A_REVERSE); mvaddstr(0, 55, "FDISK Partition Editor"); attrset(A_NORMAL);
+ mvprintw(1, 0,
+ "DISK Geometry:\t%lu cyls/%lu heads/%lu sectors = %lu sectors",
+ d->bios_cyl, d->bios_hd, d->bios_sect,
+ d->bios_cyl * d->bios_hd * d->bios_sect);
+ mvprintw(3, 0, "%10s %10s %10s %8s %6s %10s %8s %8s",
+ "Offset", "Size", "End", "Name", "PType", "Desc",
+ "Subtype", "Flags");
+ for (i = 0, row = CHUNK_START_ROW; chunk_info[i]; i++, row++) {
+ if (i == current_chunk)
+ attrset(ATTR_SELECTED);
+ mvprintw(row, 0, "%10ld %10lu %10lu %8s %6d %10s %8d\t%-6s",
+ chunk_info[i]->offset, chunk_info[i]->size,
+ chunk_info[i]->end, chunk_info[i]->name,
+ chunk_info[i]->type,
+ slice_type_name(chunk_info[i]->type, chunk_info[i]->subtype),
+ chunk_info[i]->subtype, ShowChunkFlags(chunk_info[i]));
+ if (i == current_chunk)
+ attrset(A_NORMAL);
+ }
+}
+
+static void
+print_command_summary()
+{
+ mvprintw(14, 0, "The following commands are supported (in upper or lower case):");
+ mvprintw(16, 0, "A = Use Entire Disk B = Bad Block Scan C = Create Slice");
+ mvprintw(17, 0, "D = Delete Slice G = Set Drive Geometry S = Set Bootable");
+ mvprintw(18, 0, "T = Change Type U = Undo All Changes Q = Finish");
+ if (!RunningAsInit)
+ mvprintw(18, 48, "W = Write Changes");
+ mvprintw(21, 0, "Use F1 or ? to get more help, arrow keys to select.");
+ move(0, 0);
+}
+
+static u_char *
+getBootMgr(char *dname)
+{
+ extern u_char mbr[], bteasy17[];
+ char str[80];
+ char *cp;
+ int i = 0;
+
+ cp = variable_get(VAR_BOOTMGR);
+ if (!cp) {
+ /* Figure out what kind of MBR the user wants */
+ sprintf(str, "Install Boot Manager for drive %s?", dname);
+ MenuMBRType.title = str;
+ i = dmenuOpenSimple(&MenuMBRType, FALSE);
+ }
+ else {
+ if (!strncmp(cp, "boot", 4))
+ BootMgr = 0;
+ else if (!strcmp(cp, "standard"))
+ BootMgr = 1;
+ else
+ BootMgr = 2;
+ }
+ if (cp || i) {
+ switch (BootMgr) {
+ case 0:
+ return bteasy17;
+
+ case 1:
+ return mbr;
+
+ case 2:
+ default:
+ break;
+ }
+ }
+ return NULL;
+}
+
+int
+diskGetSelectCount(Device ***devs)
+{
+ int i, cnt, enabled;
+ char *cp;
+ Device **dp;
+
+ cp = variable_get(VAR_DISK);
+ dp = *devs = deviceFind(cp, DEVICE_TYPE_DISK);
+ cnt = deviceCount(dp);
+ if (!cnt)
+ return -1;
+ for (i = 0, enabled = 0; i < cnt; i++) {
+ if (dp[i]->enabled)
+ ++enabled;
+ }
+ return enabled;
+}
+
+void
+diskPartition(Device *dev)
+{
+ char *cp, *p;
+ int rv, key = 0;
+ Boolean chunking;
+ char *msg = NULL;
+ u_char *mbrContents;
+ WINDOW *w = savescr();
+ Disk *d = (Disk *)dev->private;
+
+ chunking = TRUE;
+ keypad(stdscr, TRUE);
+
+ /* Flush both the dialog and curses library views of the screen
+ since we don't always know who called us */
+ dialog_clear_norefresh(), clear();
+ current_chunk = 0;
+
+ /* Set up the chunk array */
+ record_chunks(d);
+
+ while (chunking) {
+ char *val, geometry[80];
+
+ /* Now print our overall state */
+ if (d)
+ print_chunks(d);
+ print_command_summary();
+ if (msg) {
+ attrset(title_attr); mvprintw(23, 0, msg); attrset(A_NORMAL);
+ beep();
+ msg = NULL;
+ }
+ else {
+ move(23, 0);
+ clrtoeol();
+ }
+
+ /* Get command character */
+ key = getch();
+ switch (toupper(key)) {
+ case '\014': /* ^L (redraw) */
+ clear();
+ msg = NULL;
+ break;
+
+ case '\020': /* ^P */
+ case KEY_UP:
+ case '-':
+ if (current_chunk != 0)
+ --current_chunk;
+ break;
+
+ case '\016': /* ^N */
+ case KEY_DOWN:
+ case '+':
+ case '\r':
+ case '\n':
+ if (chunk_info[current_chunk + 1])
+ ++current_chunk;
+ break;
+
+ case KEY_HOME:
+ current_chunk = 0;
+ break;
+
+ case KEY_END:
+ while (chunk_info[current_chunk + 1])
+ ++current_chunk;
+ break;
+
+ case KEY_F(1):
+ case '?':
+ systemDisplayHelp("slice");
+ clear();
+ break;
+
+ case 'A':
+ cp = variable_get(VAR_DEDICATE_DISK);
+ if (cp && !strcasecmp(cp, "always"))
+ rv = 1;
+ else {
+ rv = msgYesNo("Do you want to do this with a true partition entry\n"
+ "so as to remain cooperative with any future possible\n"
+ "operating systems on the drive(s)?\n"
+ "(See also the section about ``dangerously dedicated''\n"
+ "disks in the FreeBSD FAQ.)");
+ if (rv == -1)
+ rv = 0;
+ }
+ All_FreeBSD(d, rv);
+ variable_set2(DISK_PARTITIONED, "yes");
+ record_chunks(d);
+ clear();
+ break;
+
+ case 'B':
+ if (chunk_info[current_chunk]->type != freebsd)
+ msg = "Can only scan for bad blocks in FreeBSD slice.";
+ else if (strncmp(d->name, "sd", 2) ||
+ !msgYesNo("This typically makes sense only for ESDI, IDE or MFM drives.\n"
+ "Are you sure you want to do this on a SCSI disk?")) {
+ if (chunk_info[current_chunk]->flags & CHUNK_BAD144)
+ chunk_info[current_chunk]->flags &= ~CHUNK_BAD144;
+ else
+ chunk_info[current_chunk]->flags |= CHUNK_BAD144;
+ }
+ clear();
+ break;
+
+ case 'C':
+ if (chunk_info[current_chunk]->type != unused)
+ msg = "Slice in use, delete it first or move to an unused one.";
+ else {
+ char *val, tmp[20], *cp;
+ int size, subtype;
+ chunk_e partitiontype;
+
+ snprintf(tmp, 20, "%d", chunk_info[current_chunk]->size);
+ val = msgGetInput(tmp, "Please specify the size for new FreeBSD slice in blocks\n"
+ "or append a trailing `M' for megabytes (e.g. 20M).");
+ if (val && (size = strtol(val, &cp, 0)) > 0) {
+ if (*cp && toupper(*cp) == 'M')
+ size *= ONE_MEG;
+ strcpy(tmp, "165");
+ val = msgGetInput(tmp, "Enter type of partition to create:\n\n"
+ "Pressing Enter will choose the default, a native FreeBSD\n"
+ "slice (type 165). You can choose other types, 6 for a\n"
+ "DOS partition or 131 for a Linux partition, for example.\n\n"
+ "Note: If you choose a non-FreeBSD partition type, it will not\n"
+ "be formatted or otherwise prepared, it will simply reserve space\n"
+ "for you to use another tool, such as DOS FORMAT, to later format\n"
+ "and use the partition.");
+ if (val && (subtype = strtol(val, NULL, 0)) > 0) {
+ if (subtype == 165)
+ partitiontype = freebsd;
+ else if (subtype == 6)
+ partitiontype = fat;
+ else
+ partitiontype = unknown;
+ Create_Chunk(d, chunk_info[current_chunk]->offset, size, partitiontype, subtype,
+ (chunk_info[current_chunk]->flags & CHUNK_ALIGN));
+ variable_set2(DISK_PARTITIONED, "yes");
+ record_chunks(d);
+ }
+ }
+ clear();
+ }
+ break;
+
+ case KEY_DC:
+ case 'D':
+ if (chunk_info[current_chunk]->type == unused)
+ msg = "Slice is already unused!";
+ else {
+ Delete_Chunk(d, chunk_info[current_chunk]);
+ variable_set2(DISK_PARTITIONED, "yes");
+ record_chunks(d);
+ }
+ break;
+
+ case 'T':
+ if (chunk_info[current_chunk]->type == unused)
+ msg = "Slice is currently unused (use create instead)";
+ else {
+ char *val, tmp[20];
+ int subtype;
+ chunk_e partitiontype;
+ WINDOW *save = savescr();
+
+ strcpy(tmp, "165");
+ val = msgGetInput(tmp, "New partition type:\n\n"
+ "Pressing Enter will choose the default, a native FreeBSD\n"
+ "slice (type 165). Other popular values are 6 for\n"
+ "DOS PAT partition, 131 for a Linux ext2fs partition or\n"
+ "130 for a Linux swap partition.\n\n"
+ "Note: If you choose a non-FreeBSD partition type, it will not\n"
+ "be formatted or otherwise prepared, it will simply reserve space\n"
+ "for you to use another tool, such as DOS format, to later format\n"
+ "and actually use the partition.");
+ if (val && (subtype = strtol(val, NULL, 0)) > 0) {
+ if (subtype == 165)
+ partitiontype = freebsd;
+ else if (subtype == 6)
+ partitiontype = fat;
+ else
+ partitiontype = unknown;
+ chunk_info[current_chunk]->type = partitiontype;
+ chunk_info[current_chunk]->subtype = subtype;
+ }
+ restorescr(save);
+ }
+ break;
+
+ case 'G':
+ snprintf(geometry, 80, "%lu/%lu/%lu", d->bios_cyl, d->bios_hd, d->bios_sect);
+ val = msgGetInput(geometry, "Please specify the new geometry in cyl/hd/sect format.\n"
+ "Don't forget to use the two slash (/) separator characters!\n"
+ "It's not possible to parse the field without them.");
+ if (val) {
+ long nc, nh, ns;
+ nc = strtol(val, &val, 0);
+ nh = strtol(val + 1, &val, 0);
+ ns = strtol(val + 1, 0, 0);
+ Set_Bios_Geom(d, nc, nh, ns);
+ }
+ clear();
+ break;
+
+ case 'S':
+ /* Set Bootable */
+ chunk_info[current_chunk]->flags |= CHUNK_ACTIVE;
+ break;
+
+ case 'U':
+ if ((cp = variable_get(DISK_LABELLED)) && !strcmp(cp, "written")) {
+ msgConfirm("You've already written this information out - you\n"
+ "can't undo it.");
+ }
+ else if (!msgYesNo("Are you SURE you want to Undo everything?")) {
+ char cp[BUFSIZ];
+
+ sstrncpy(cp, d->name, sizeof cp);
+ Free_Disk(dev->private);
+ d = Open_Disk(cp);
+ if (!d)
+ msgConfirm("Can't reopen disk %s! Internal state is probably corrupted", cp);
+ dev->private = d;
+ variable_unset(DISK_PARTITIONED);
+ variable_unset(DISK_LABELLED);
+ if (d)
+ record_chunks(d);
+ }
+ clear();
+ break;
+
+ case 'W':
+ if ((cp = variable_get(DISK_LABELLED)) && !strcmp(cp, "written")) {
+ msgConfirm("You've already written this information out - if\n"
+ "you wish to overwrite it, you'll have to restart.");
+ }
+ else if (!msgYesNo("WARNING: This should only be used when modifying an EXISTING\n"
+ "installation. If you are installing FreeBSD for the first time\n"
+ "then you should simply type Q when you're finished here and your\n"
+ "changes will be committed in one batch automatically at the end of\n"
+ "these questions. 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");
+
+ /* Don't trash the MBR if the first (and therefore only) chunk is marked for a truly dedicated
+ * disk (i.e., the disklabel starts at sector 0), even in cases where the user has requested
+ * booteasy or a "standard" MBR -- both would be fatal in this case.
+ */
+ if (!(d->chunks->part->flags & CHUNK_FORCE_ALL) && (mbrContents = getBootMgr(d->name)) != NULL)
+ Set_Boot_Mgr(d, mbrContents);
+
+ if (DITEM_STATUS(diskPartitionWrite(NULL)) != DITEM_SUCCESS)
+ msgConfirm("Disk partition write returned an error status!");
+ else
+ msgConfirm("Wrote FDISK partition information out successfully.");
+ }
+ clear();
+ break;
+
+ case '|':
+ if (!msgYesNo("Are you SURE you want to go into Wizard mode?\n"
+ "No seat belts whatsoever are provided!")) {
+ clear();
+ refresh();
+ slice_wizard(d);
+ variable_set2(DISK_PARTITIONED, "yes");
+ record_chunks(d);
+ }
+ else
+ msg = "Wise choice!";
+ clear();
+ break;
+
+ case '\033': /* ESC */
+ case 'Q':
+ chunking = FALSE;
+ /* Don't trash the MBR if the first (and therefore only) chunk is marked for a truly dedicated
+ * disk (i.e., the disklabel starts at sector 0), even in cases where the user has requested
+ * booteasy or a "standard" MBR -- both would be fatal in this case.
+ */
+ if ((d->chunks->part->flags & CHUNK_FORCE_ALL) != CHUNK_FORCE_ALL
+ && (mbrContents = getBootMgr(d->name)) != NULL)
+ Set_Boot_Mgr(d, mbrContents);
+ break;
+
+ default:
+ beep();
+ msg = "Type F1 or ? for help";
+ break;
+ }
+ }
+ p = CheckRules(d);
+ if (p) {
+ char buf[FILENAME_MAX];
+
+ dialog_clear_norefresh();
+ use_helpline("Press F1 to read more about disk slices.");
+ use_helpfile(systemHelpFile("partition", buf));
+ if (!variable_get(VAR_NO_WARN))
+ dialog_mesgbox("Disk slicing warning:", p, -1, -1);
+ free(p);
+ }
+ restorescr(w);
+}
+
+static int
+partitionHook(dialogMenuItem *selected)
+{
+ Device **devs = NULL;
+
+ devs = deviceFind(selected->prompt, DEVICE_TYPE_DISK);
+ if (!devs) {
+ msgConfirm("Unable to find disk %s!", selected->prompt);
+ return DITEM_FAILURE;
+ }
+ /* Toggle enabled status? */
+ if (!devs[0]->enabled) {
+ devs[0]->enabled = TRUE;
+ diskPartition(devs[0]);
+ }
+ else
+ devs[0]->enabled = FALSE;
+ return DITEM_SUCCESS | DITEM_RESTORE;
+}
+
+static int
+partitionCheck(dialogMenuItem *selected)
+{
+ Device **devs = NULL;
+
+ devs = deviceFind(selected->prompt, DEVICE_TYPE_DISK);
+ if (!devs || devs[0]->enabled == FALSE)
+ return FALSE;
+ return TRUE;
+}
+
+int
+diskPartitionEditor(dialogMenuItem *self)
+{
+ DMenu *menu;
+ Device **devs;
+ int i, cnt, devcnt;
+
+ cnt = diskGetSelectCount(&devs);
+ devcnt = deviceCount(devs);
+ if (cnt == -1) {
+ msgConfirm("No disks found! Please verify that your disk controller is being\n"
+ "properly probed at boot time. See the Hardware Guide on the\n"
+ "Documentation menu for clues on diagnosing this type of problem.");
+ return DITEM_FAILURE;
+ }
+ else if (cnt) {
+ /* Some are already selected */
+ for (i = 0; i < devcnt; i++) {
+ if (devs[i]->enabled) {
+ if (variable_get(VAR_NONINTERACTIVE))
+ diskPartitionNonInteractive(devs[i]);
+ else
+ diskPartition(devs[i]);
+ }
+ }
+ }
+ else {
+ /* No disks are selected, fall-back case now */
+ if (devcnt == 1) {
+ devs[0]->enabled = TRUE;
+ if (variable_get(VAR_NONINTERACTIVE))
+ diskPartitionNonInteractive(devs[0]);
+ else
+ diskPartition(devs[0]);
+ return DITEM_SUCCESS;
+ }
+ else {
+ menu = deviceCreateMenu(&MenuDiskDevices, DEVICE_TYPE_DISK, partitionHook, partitionCheck);
+ if (!menu) {
+ msgConfirm("No devices suitable for installation found!\n\n"
+ "Please verify that your disk controller (and attached drives)\n"
+ "were detected properly. This can be done by pressing the\n"
+ "[Scroll Lock] key and using the Arrow keys to move back to\n"
+ "the boot messages. Press [Scroll Lock] again to return.");
+ return DITEM_FAILURE;
+ }
+ else {
+ i = dmenuOpenSimple(menu, FALSE) ? DITEM_SUCCESS : DITEM_FAILURE;
+ free(menu);
+ }
+ return i | DITEM_RESTORE;
+ }
+ }
+ return DITEM_FAILURE;
+}
+
+int
+diskPartitionWrite(dialogMenuItem *self)
+{
+ Device **devs;
+ int i;
+
+ 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++) {
+ Chunk *c1;
+ Disk *d = (Disk *)devs[i]->private;
+
+ if (!devs[i]->enabled)
+ continue;
+
+ Set_Boot_Blocks(d, boot1, boot2);
+ 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 scan for bad blocks, if necessary */
+ for (c1 = d->chunks->part; c1; c1 = c1->next) {
+ if (c1->flags & CHUNK_BAD144) {
+ int ret;
+
+ msgNotify("Running bad block scan on slice %s", c1->name);
+ if (!Fake) {
+ ret = vsystem("bad144 -v /dev/r%s 1234", c1->name);
+ if (ret)
+ msgConfirm("Bad144 init on %s returned status of %d!", c1->name, ret);
+ ret = vsystem("bad144 -v -s /dev/r%s", c1->name);
+ if (ret)
+ msgConfirm("Bad144 scan on %s returned status of %d!", c1->name, ret);
+ }
+ }
+ }
+ }
+ /* Now it's not "yes", but "written" */
+ variable_set2(DISK_PARTITIONED, "written");
+ return DITEM_SUCCESS;
+}
+
+/* Partition a disk based wholly on which variables are set */
+static void
+diskPartitionNonInteractive(Device *dev)
+{
+ char *cp;
+ int i, sz, all_disk = 0;
+ u_char *mbrContents;
+ Disk *d = (Disk *)dev->private;
+
+ record_chunks(d);
+ cp = variable_get(VAR_GEOMETRY);
+ if (cp) {
+ msgDebug("Setting geometry from script to: %s\n", cp);
+ d->bios_cyl = strtol(cp, &cp, 0);
+ d->bios_hd = strtol(cp + 1, &cp, 0);
+ d->bios_sect = strtol(cp + 1, 0, 0);
+ }
+
+ cp = variable_get(VAR_PARTITION);
+ if (cp) {
+ if (!strcmp(cp, "free")) {
+ /* Do free disk space case */
+ for (i = 0; chunk_info[i]; i++) {
+ /* If a chunk is at least 10MB in size, use it. */
+ if (chunk_info[i]->type == unused && chunk_info[i]->size > (10 * ONE_MEG)) {
+ Create_Chunk(d, chunk_info[i]->offset, chunk_info[i]->size, freebsd, 3,
+ (chunk_info[i]->flags & CHUNK_ALIGN));
+ variable_set2(DISK_PARTITIONED, "yes");
+ break;
+ }
+ }
+ if (!chunk_info[i]) {
+ dialog_clear();
+ msgConfirm("Unable to find any free space on this disk!");
+ return;
+ }
+ }
+ else if (!strcmp(cp, "all")) {
+ /* Do all disk space case */
+ msgDebug("Warning: Devoting all of disk %s to FreeBSD.\n", d->name);
+
+ All_FreeBSD(d, FALSE);
+ }
+ else if (!strcmp(cp, "exclusive")) {
+ /* Do really-all-the-disk-space case */
+ msgDebug("Warning: Devoting all of disk %s to FreeBSD.\n", d->name);
+
+ All_FreeBSD(d, all_disk = TRUE);
+ }
+ else if ((sz = strtol(cp, &cp, 0))) {
+ /* Look for sz bytes free */
+ if (*cp && toupper(*cp) == 'M')
+ sz *= ONE_MEG;
+ for (i = 0; chunk_info[i]; i++) {
+ /* If a chunk is at least sz MB, use it. */
+ if (chunk_info[i]->type == unused && chunk_info[i]->size >= sz) {
+ Create_Chunk(d, chunk_info[i]->offset, sz, freebsd, 3, (chunk_info[i]->flags & CHUNK_ALIGN));
+ variable_set2(DISK_PARTITIONED, "yes");
+ break;
+ }
+ }
+ if (!chunk_info[i]) {
+ dialog_clear();
+ msgConfirm("Unable to find %d free blocks on this disk!", sz);
+ return;
+ }
+ }
+ else if (!strcmp(cp, "existing")) {
+ /* Do existing FreeBSD case */
+ for (i = 0; chunk_info[i]; i++) {
+ if (chunk_info[i]->type == freebsd)
+ break;
+ }
+ if (!chunk_info[i]) {
+ dialog_clear();
+ msgConfirm("Unable to find any existing FreeBSD partitions on this disk!");
+ return;
+ }
+ }
+ else {
+ dialog_clear();
+ msgConfirm("`%s' is an invalid value for %s - is config file valid?", cp, VAR_PARTITION);
+ return;
+ }
+ if (!all_disk) {
+ mbrContents = getBootMgr(d->name);
+ Set_Boot_Mgr(d, mbrContents);
+ }
+ variable_set2(DISK_PARTITIONED, "yes");
+ }
+}
diff --git a/usr.sbin/sysinstall/dispatch.c b/usr.sbin/sysinstall/dispatch.c
new file mode 100644
index 0000000..67e09d6
--- /dev/null
+++ b/usr.sbin/sysinstall/dispatch.c
@@ -0,0 +1,405 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last program in the `sysinstall' line - the next
+ * generation being essentially a complete rewrite.
+ *
+ * $Id: dispatch.c,v 1.23 1997/09/16 18:57:08 jkh Exp $
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include "sysinstall.h"
+#include <ctype.h>
+#include <errno.h>
+#include <sys/signal.h>
+#include <sys/fcntl.h>
+
+#include "list.h"
+
+static int dispatch_shutdown(dialogMenuItem *unused);
+static int dispatch_systemExecute(dialogMenuItem *unused);
+
+static struct _word {
+ char *name;
+ int (*handler)(dialogMenuItem *self);
+} resWords[] = {
+ { "configAnonFTP", configAnonFTP },
+ { "configRouter", configRouter },
+ { "configNFSServer", configNFSServer },
+ { "configNTP", configNTP },
+ { "configPCNFSD", configPCNFSD },
+ { "configPackages", configPackages },
+ { "configRegister", configRegister },
+ { "configUsers", configUsers },
+ { "configXEnvironment", configXEnvironment },
+ { "diskPartitionEditor", diskPartitionEditor },
+ { "diskPartitionWrite", diskPartitionWrite },
+ { "diskLabelEditor", diskLabelEditor },
+ { "diskLabelCommit", diskLabelCommit },
+ { "distReset", distReset },
+ { "distSetCustom", distSetCustom },
+ { "distSetDeveloper", distSetDeveloper },
+ { "distSetXDeveloper", distSetXDeveloper },
+ { "distSetKernDeveloper", distSetKernDeveloper },
+ { "distSetUser", distSetUser },
+ { "distSetXUser", distSetXUser },
+ { "distSetMinimum", distSetMinimum },
+ { "distSetEverything", distSetEverything },
+ { "distSetDES", distSetDES },
+ { "distSetSrc", distSetSrc },
+ { "distSetXF86", distSetXF86 },
+ { "distExtractAll", distExtractAll },
+ { "docBrowser", docBrowser },
+ { "docShowDocument", docShowDocument },
+ { "installCommit", installCommit },
+ { "installExpress", installExpress },
+ { "installNovice", installNovice },
+ { "installUpgrade", installUpgrade },
+ { "installFixup", installFixup },
+ { "installFixitHoloShell", installFixitHoloShell },
+ { "installFixitCDROM", installFixitCDROM },
+ { "installFixitFloppy", installFixitFloppy },
+ { "installFilesystems", installFilesystems },
+ { "installVarDefaults", installVarDefaults },
+ { "loadConfig", dispatch_load_file },
+ { "mediaSetCDROM", mediaSetCDROM },
+ { "mediaSetFloppy", mediaSetFloppy },
+ { "mediaSetDOS", mediaSetDOS },
+ { "mediaSetTape", mediaSetTape },
+ { "mediaSetFTP", mediaSetFTP },
+ { "mediaSetFTPActive", mediaSetFTPActive },
+ { "mediaSetFTPPassive", mediaSetFTPPassive },
+ { "mediaSetUFS", mediaSetUFS },
+ { "mediaSetNFS", mediaSetNFS },
+ { "mediaSetFTPUserPass", mediaSetFTPUserPass },
+ { "mediaSetCPIOVerbosity", mediaSetCPIOVerbosity },
+ { "mediaGetType", mediaGetType },
+ { "optionsEditor", optionsEditor },
+ { "register", configRegister }, /* Alias */
+ { "packageAdd", packageAdd },
+ { "addGroup", userAddGroup },
+ { "addUser", userAddUser },
+ { "shutdown", dispatch_shutdown },
+ { "system", dispatch_systemExecute },
+ { 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
+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);
+ i = DITEM_SUCCESS;
+ }
+ else {
+ /* A command might be a pathname if it's encoded in argv[0], which
+ we also support */
+ if ((cp = rindex(str, '/')) != NULL)
+ str = cp + 1;
+ if (isDebug())
+ msgDebug("dispatch: calling resword `%s'\n", str);
+ if (!call_possible_resword(str, NULL, &i)) {
+ msgNotify("Warning: No such command ``%s''", str);
+ i = DITEM_FAILURE;
+ }
+ }
+ return i;
+}
+
+
+/*
+ * File processing
+ */
+
+static qelement *
+dispatch_load_fp(FILE *fp)
+{
+ qelement *head;
+ char buf[BUFSIZ], *cp;
+
+ head = malloc(sizeof(qelement));
+
+ if (!head)
+ return NULL;
+
+ INITQUE(*head);
+
+ while (fgets(buf, sizeof buf, fp)) {
+
+ if ((cp = strchr(buf, '\n')) != NULL)
+ *cp = '\0';
+ if (*buf == '\0' || *buf == '#')
+ continue;
+
+ if (!dispatch_add_command(head, buf))
+ return NULL;
+ }
+
+ return head;
+}
+
+static int
+dispatch_execute(qelement *head)
+{
+ int result = DITEM_SUCCESS;
+ command_buffer *item;
+
+ if (!head)
+ return result | DITEM_FAILURE;
+
+ /* Hint to others that we're running from a script, should they care */
+ variable_set2(VAR_NONINTERACTIVE, "YES");
+
+ while (!EMPTYQUE(*head)) {
+ item = (command_buffer *) head->q_forw;
+
+ if (DITEM_STATUS(dispatchCommand(item->string)) != DITEM_SUCCESS) {
+ /*
+ * Allow a user to prefix a command with "noError" to cause
+ * us to ignore any errors for that one command.
+ */
+ if (variable_get(VAR_NO_ERROR))
+ variable_unset(VAR_NO_ERROR);
+ else {
+ msgConfirm("Command `%s' failed - rest of script aborted.\n",
+ item->string);
+ result |= DITEM_FAILURE;
+ break;
+ }
+ }
+ dispatch_free_command(item);
+ }
+
+ dispatch_free_all(head);
+
+ variable_unset(VAR_NONINTERACTIVE);
+
+ return result;
+}
+
+int
+dispatch_load_file_int(int quiet)
+{
+ FILE *fp;
+ char *cp;
+ int i;
+ qelement *list;
+
+ static const char *names[] = {
+ "install.cfg",
+ "/stand/install.cfg",
+ "/tmp/install.cfg",
+ NULL
+ };
+
+ fp = NULL;
+ cp = variable_get(VAR_CONFIG_FILE);
+ if (!cp) {
+ for (i = 0; names[i]; i++)
+ if ((fp = fopen(names[i], "r")) != NULL)
+ break;
+ } else
+ fp = fopen(cp, "r");
+
+ if (!fp) {
+ if (!quiet)
+ msgConfirm("Unable to open %s: %s", cp, strerror(errno));
+ return DITEM_FAILURE;
+ }
+
+ list = dispatch_load_fp(fp);
+ fclose(fp);
+
+ return dispatch_execute(list);
+}
+
+int
+dispatch_load_file(dialogMenuItem *self)
+{
+ return dispatch_load_file_int(FALSE);
+}
+
+int
+dispatch_load_floppy(dialogMenuItem *self)
+{
+ int what = DITEM_RESTORE | DITEM_SUCCESS;
+ extern char *distWanted;
+ char *cp;
+ FILE *fp;
+ qelement *list;
+
+ mediaClose();
+ dialog_clear_norefresh();
+
+ cp = variable_get_value(VAR_INSTALL_CFG,
+ "Specify the name of a configuration file\n"
+ "residing on a MSDOS or UFS floppy.");
+ if (!cp || !*cp) {
+ variable_unset(VAR_INSTALL_CFG);
+ what |= DITEM_FAILURE;
+ return what;
+ }
+
+ distWanted = cp;
+ /* Try to open the floppy drive */
+ if (DITEM_STATUS(mediaSetFloppy(NULL)) == DITEM_FAILURE) {
+ msgConfirm("Unable to set media device to floppy.");
+ what |= DITEM_FAILURE;
+ mediaClose();
+ return what;
+ }
+
+ if (!mediaDevice->init(mediaDevice)) {
+ msgConfirm("Unable to mount floppy filesystem.");
+ what |= DITEM_FAILURE;
+ mediaClose();
+ return what;
+ }
+
+ fp = mediaDevice->get(mediaDevice, cp, TRUE);
+ if (fp) {
+ list = dispatch_load_fp(fp);
+ fclose(fp);
+ mediaClose();
+
+ what |= dispatch_execute(list);
+ }
+ else {
+ 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..ee1a415
--- /dev/null
+++ b/usr.sbin/sysinstall/dist.c
@@ -0,0 +1,827 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last program in the `sysinstall' line - the next
+ * generation being essentially a complete rewrite.
+ *
+ * $Id: dist.c,v 1.119 1997/10/12 12:11:14 jkh Exp $
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include "sysinstall.h"
+#include <sys/time.h>
+#include <signal.h>
+
+unsigned int Dists;
+unsigned int DESDists;
+unsigned int SrcDists;
+#ifndef USE_XIG_ENVIRONMENT
+unsigned int XF86Dists;
+unsigned int XF86ServerDists;
+unsigned int XF86FontDists;
+#endif
+
+typedef struct _dist {
+ char *my_name;
+ char *my_dir;
+ unsigned int *my_mask;
+ unsigned int my_bit;
+ struct _dist *my_dist;
+} Distribution;
+
+extern Distribution DistTable[];
+extern Distribution DESDistTable[];
+extern Distribution SrcDistTable[];
+#ifndef USE_XIG_ENVIRONMENT
+extern Distribution XF86DistTable[];
+extern Distribution XF86FontDistTable[];
+extern Distribution XF86ServerDistTable[];
+#endif
+
+/* The top-level distribution categories */
+static Distribution DistTable[] = {
+{ "bin", "/", &Dists, DIST_BIN, NULL },
+{ "doc", "/", &Dists, DIST_DOC, NULL },
+{ "games", "/", &Dists, DIST_GAMES, NULL },
+{ "manpages", "/", &Dists, DIST_MANPAGES, NULL },
+{ "catpages", "/", &Dists, DIST_CATPAGES, NULL },
+{ "proflibs", "/", &Dists, DIST_PROFLIBS, NULL },
+{ "dict", "/", &Dists, DIST_DICT, NULL },
+{ "info", "/", &Dists, DIST_INFO, NULL },
+{ "src", "/", &Dists, DIST_SRC, SrcDistTable },
+{ "des", "/", &Dists, DIST_DES, DESDistTable },
+{ "compat1x", "/", &Dists, DIST_COMPAT1X, NULL },
+{ "compat20", "/", &Dists, DIST_COMPAT20, NULL },
+{ "compat21", "/", &Dists, DIST_COMPAT21, NULL },
+{ "ports", "/usr", &Dists, DIST_PORTS, NULL },
+#ifdef USE_XIG_ENVIRONMENT
+{ "accelx", "/usr/X11R6/lib/X11", &Dists, DIST_XIG_SERVER, NULL },
+#else
+{ "XF86331", "/usr", &Dists, DIST_XF86, XF86DistTable },
+#endif
+{ NULL },
+};
+
+/* The DES distribution (not for export!) */
+static Distribution DESDistTable[] = {
+{ "des", "/", &DESDists, DIST_DES_DES, NULL },
+{ "krb", "/", &DESDists, DIST_DES_KERBEROS, NULL },
+{ "ssecure", "/usr/src", &DESDists, DIST_DES_SSECURE, NULL },
+{ "scrypto", "/usr/src", &DESDists, DIST_DES_SCRYPTO, NULL },
+{ "skerbero", "/usr/src", &DESDists, DIST_DES_SKERBEROS, NULL },
+{ NULL },
+};
+
+/* The /usr/src distribution */
+static Distribution SrcDistTable[] = {
+{ "sbase", "/usr/src", &SrcDists, DIST_SRC_BASE, NULL },
+{ "scontrib", "/usr/src", &SrcDists, DIST_SRC_CONTRIB, NULL },
+{ "sgnu", "/usr/src", &SrcDists, DIST_SRC_GNU, NULL },
+{ "setc", "/usr/src", &SrcDists, DIST_SRC_ETC, NULL },
+{ "sgames", "/usr/src", &SrcDists, DIST_SRC_GAMES, NULL },
+{ "sinclude", "/usr/src", &SrcDists, DIST_SRC_INCLUDE, NULL },
+{ "slib", "/usr/src", &SrcDists, DIST_SRC_LIB, NULL },
+{ "slibexec", "/usr/src", &SrcDists, DIST_SRC_LIBEXEC, NULL },
+{ "slkm", "/usr/src", &SrcDists, DIST_SRC_LKM, 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 },
+{ "ssmailcf", "/usr/src", &SrcDists, DIST_SRC_SMAILCF, NULL },
+{ NULL },
+};
+
+#ifndef USE_XIG_ENVIRONMENT
+/* The XFree86 distribution */
+static Distribution XF86DistTable[] = {
+{ "XF86331", "/usr/X11R6", &XF86Dists, DIST_XF86_FONTS, XF86FontDistTable },
+{ "XF86331", "/usr/X11R6", &XF86Dists, DIST_XF86_SERVER, XF86ServerDistTable },
+{ "X331src-1", "/usr/X11R6/src", &XF86Dists, DIST_XF86_SRC, NULL },
+{ "X331contrib", "/usr/X11R6/src", &XF86Dists, DIST_XF86_CSRC, NULL },
+{ "X331bin", "/usr/X11R6", &XF86Dists, DIST_XF86_BIN, NULL },
+{ "X331cfg", "/usr/X11R6", &XF86Dists, DIST_XF86_CFG, NULL },
+{ "X331doc", "/usr/X11R6", &XF86Dists, DIST_XF86_DOC, NULL },
+{ "X331html", "/usr/X11R6", &XF86Dists, DIST_XF86_HTML, NULL },
+{ "X331lib", "/usr/X11R6", &XF86Dists, DIST_XF86_LIB, NULL },
+{ "X331lk98", "/usr/X11R6", &XF86Dists, DIST_XF86_LKIT98, NULL },
+{ "X331lkit", "/usr/X11R6", &XF86Dists, DIST_XF86_LKIT, NULL },
+{ "X331man", "/usr/X11R6", &XF86Dists, DIST_XF86_MAN, NULL },
+{ "X331prog", "/usr/X11R6", &XF86Dists, DIST_XF86_PROG, NULL },
+{ "X331ps", "/usr/X11R6", &XF86Dists, DIST_XF86_PS, NULL },
+{ "X331set", "/usr/X11R6", &XF86Dists, DIST_XF86_SET, NULL },
+{ NULL },
+};
+
+/* The XFree86 server distribution */
+static Distribution XF86ServerDistTable[] = {
+{ "X3318514", "/usr/X11R6", &XF86ServerDists, DIST_XF86_SERVER_8514, NULL },
+{ "X3319480", "/usr/X11R6", &XF86ServerDists, DIST_XF86_SERVER_9480, NULL },
+{ "X3319EGC", "/usr/X11R6", &XF86ServerDists, DIST_XF86_SERVER_9EGC, NULL },
+{ "X3319GA9", "/usr/X11R6", &XF86ServerDists, DIST_XF86_SERVER_9GA9, NULL },
+{ "X3319GAN", "/usr/X11R6", &XF86ServerDists, DIST_XF86_SERVER_9GAN, NULL },
+{ "X3319LPW", "/usr/X11R6", &XF86ServerDists, DIST_XF86_SERVER_9LPW, NULL },
+{ "X3319NKV", "/usr/X11R6", &XF86ServerDists, DIST_XF86_SERVER_9NKV, NULL },
+{ "X3319NS3", "/usr/X11R6", &XF86ServerDists, DIST_XF86_SERVER_9NS3, NULL },
+{ "X3319SPW", "/usr/X11R6", &XF86ServerDists, DIST_XF86_SERVER_9SPW, NULL },
+{ "X3319TGU", "/usr/X11R6", &XF86ServerDists, DIST_XF86_SERVER_9TGU, NULL },
+{ "X3319WEP", "/usr/X11R6", &XF86ServerDists, DIST_XF86_SERVER_9WEP, NULL },
+{ "X3319WS", "/usr/X11R6", &XF86ServerDists, DIST_XF86_SERVER_9WS, NULL },
+{ "X3319WSN", "/usr/X11R6", &XF86ServerDists, DIST_XF86_SERVER_9WSN, NULL },
+{ "X331AGX", "/usr/X11R6", &XF86ServerDists, DIST_XF86_SERVER_AGX, NULL },
+{ "X331I128", "/usr/X11R6", &XF86ServerDists, DIST_XF86_SERVER_I128, NULL },
+{ "X331Ma8", "/usr/X11R6", &XF86ServerDists, DIST_XF86_SERVER_MACH8, NULL },
+{ "X331Ma32", "/usr/X11R6", &XF86ServerDists, DIST_XF86_SERVER_MACH32,NULL },
+{ "X331Ma64", "/usr/X11R6", &XF86ServerDists, DIST_XF86_SERVER_MACH64,NULL },
+{ "X331Mono", "/usr/X11R6", &XF86ServerDists, DIST_XF86_SERVER_MONO, NULL },
+{ "X331P9K", "/usr/X11R6", &XF86ServerDists, DIST_XF86_SERVER_P9000, NULL },
+{ "X331S3", "/usr/X11R6", &XF86ServerDists, DIST_XF86_SERVER_S3, NULL },
+{ "X331S3V", "/usr/X11R6", &XF86ServerDists, DIST_XF86_SERVER_S3V, NULL },
+{ "X331SVGA", "/usr/X11R6", &XF86ServerDists, DIST_XF86_SERVER_SVGA, NULL },
+{ "X331VG16", "/usr/X11R6", &XF86ServerDists, DIST_XF86_SERVER_VGA16, NULL },
+{ "X331W32", "/usr/X11R6", &XF86ServerDists, DIST_XF86_SERVER_W32, NULL },
+{ "X331nest", "/usr/X11R6", &XF86ServerDists, DIST_XF86_SERVER_NEST, NULL },
+{ "X331vfb", "/usr/X11R6", &XF86ServerDists, DIST_XF86_SERVER_VFB, NULL },
+{ NULL },
+};
+
+/* The XFree86 font distribution */
+static Distribution XF86FontDistTable[] = {
+{ "X331fnts", "/usr/X11R6", &XF86FontDists, DIST_XF86_FONTS_MISC, NULL },
+{ "X331f100", "/usr/X11R6", &XF86FontDists, DIST_XF86_FONTS_100, NULL },
+{ "X331fcyr", "/usr/X11R6", &XF86FontDists, DIST_XF86_FONTS_CYR, NULL },
+{ "X331fscl", "/usr/X11R6", &XF86FontDists, DIST_XF86_FONTS_SCALE, NULL },
+{ "X331fnon", "/usr/X11R6", &XF86FontDists, DIST_XF86_FONTS_NON, NULL },
+{ "X331fsrv", "/usr/X11R6", &XF86FontDists, DIST_XF86_FONTS_SERVER, NULL },
+{ NULL },
+};
+#endif /* !USE_XIG_ENVIRONMENT */
+
+static int distMaybeSetDES(dialogMenuItem *self);
+static int distMaybeSetPorts(dialogMenuItem *self);
+
+
+static void
+distVerifyFlags(void)
+{
+ if (SrcDists)
+ Dists |= DIST_SRC;
+ if (DESDists) {
+ if (DESDists & DIST_DES_KERBEROS)
+ DESDists |= DIST_DES_DES;
+ Dists |= DIST_DES;
+ }
+#ifndef USE_XIG_ENVIRONMENT
+ if (XF86Dists & DIST_XF86_SET)
+ XF86ServerDists |= DIST_XF86_SERVER_VGA16;
+ if (XF86ServerDists)
+ XF86Dists |= DIST_XF86_SERVER;
+ if (XF86FontDists)
+ XF86Dists |= DIST_XF86_FONTS;
+ if (XF86Dists)
+ Dists |= (DIST_XF86 | DIST_COMPAT21);
+#endif
+ if (isDebug())
+ msgDebug("Dist Masks: Dists: %0x, DES: %0x, Srcs: %0x\nXServer: %0x, XFonts: %0x, XDists: %0x\n",
+ Dists, DESDists, SrcDists, XF86ServerDists, XF86FontDists, XF86Dists);
+}
+
+int
+distReset(dialogMenuItem *self)
+{
+ Dists = 0;
+ DESDists = 0;
+ SrcDists = 0;
+#ifndef USE_XIG_ENVIRONMENT
+ XF86Dists = 0;
+ XF86ServerDists = 0;
+ XF86FontDists = 0;
+#endif
+ return DITEM_SUCCESS | DITEM_REDRAW;
+}
+
+int
+distConfig(dialogMenuItem *self)
+{
+ char *cp;
+
+ distReset(NULL);
+
+ if ((cp = variable_get(VAR_DIST_MAIN)) != NULL)
+ Dists = atoi(cp);
+
+ if ((cp = variable_get(VAR_DIST_DES)) != NULL)
+ DESDists = atoi(cp);
+
+ if ((cp = variable_get(VAR_DIST_SRC)) != NULL)
+ SrcDists = atoi(cp);
+
+#ifndef USE_XIG_ENVIRONMENT
+ 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);
+#endif
+ distVerifyFlags();
+ return DITEM_SUCCESS | DITEM_REDRAW;
+}
+
+int
+distSetDeveloper(dialogMenuItem *self)
+{
+ int i;
+
+ distReset(NULL);
+ Dists = _DIST_DEVELOPER;
+ SrcDists = DIST_SRC_ALL;
+ i = distMaybeSetDES(self) | distMaybeSetPorts(self);
+ distVerifyFlags();
+ return i;
+}
+
+int
+distSetXDeveloper(dialogMenuItem *self)
+{
+ int i;
+
+ distReset(NULL);
+ Dists = _DIST_DEVELOPER;
+ SrcDists = DIST_SRC_ALL;
+#ifdef USE_XIG_ENVIRONMENT
+ Dists |= (DIST_XIG_SERVER | DIST_COMPAT21);
+#else
+ XF86Dists = DIST_XF86_BIN | DIST_COMPAT21 | DIST_XF86_SET | DIST_XF86_CFG | DIST_XF86_LIB | DIST_XF86_PROG | DIST_XF86_MAN | DIST_XF86_SERVER | DIST_XF86_FONTS;
+ XF86ServerDists = DIST_XF86_SERVER_SVGA | DIST_XF86_SERVER_VGA16;
+ XF86FontDists = DIST_XF86_FONTS_MISC;
+#endif
+ i = distSetXF86(NULL) | distMaybeSetDES(self) | distMaybeSetPorts(self);
+ distVerifyFlags();
+ return i;
+}
+
+int
+distSetKernDeveloper(dialogMenuItem *self)
+{
+ int i;
+
+ distReset(NULL);
+ Dists = _DIST_DEVELOPER;
+ SrcDists = DIST_SRC_SYS;
+ i = distMaybeSetDES(self) | distMaybeSetPorts(self);
+ distVerifyFlags();
+ return i;
+}
+
+int
+distSetUser(dialogMenuItem *self)
+{
+ int i;
+
+ distReset(NULL);
+ Dists = _DIST_USER;
+ i = distMaybeSetDES(self) | distMaybeSetPorts(self);
+ distVerifyFlags();
+ return i;
+}
+
+int
+distSetXUser(dialogMenuItem *self)
+{
+ int i;
+
+ distReset(NULL);
+ Dists = _DIST_USER;
+#ifdef USE_XIG_ENVIRONMENT
+ Dists |= (DIST_XIG_SERVER | DIST_COMPAT21);
+#else
+ XF86ServerDists = DIST_XF86_SERVER_SVGA | DIST_XF86_SERVER_VGA16;
+ XF86Dists = DIST_XF86_BIN | DIST_COMPAT21 | DIST_XF86_SET | DIST_XF86_CFG | DIST_XF86_LIB | DIST_XF86_MAN | DIST_XF86_SERVER | DIST_XF86_FONTS;
+ XF86FontDists = DIST_XF86_FONTS_MISC;
+#endif
+ i = distSetXF86(NULL) | distMaybeSetDES(self) | distMaybeSetPorts(self);
+ distVerifyFlags();
+ return i;
+}
+
+int
+distSetMinimum(dialogMenuItem *self)
+{
+ distReset(NULL);
+ Dists = DIST_BIN;
+ return DITEM_SUCCESS | DITEM_REDRAW;
+}
+
+int
+distSetEverything(dialogMenuItem *self)
+{
+ int i;
+
+ Dists = DIST_ALL;
+ SrcDists = DIST_SRC_ALL;
+#ifdef USE_XIG_ENVIRONMENT
+ Dists |= (DIST_XIG_SERVER | DIST_COMPAT21);
+#else
+ XF86Dists = DIST_XF86_ALL;
+ XF86ServerDists = DIST_XF86_SERVER_ALL;
+ XF86FontDists = DIST_XF86_FONTS_ALL;
+#endif
+ i = distMaybeSetDES(self) | distMaybeSetPorts(self);
+ distVerifyFlags();
+ return i;
+}
+
+int
+distSetDES(dialogMenuItem *self)
+{
+ int i;
+
+ if (!dmenuOpenSimple(&MenuDESDistributions, FALSE))
+ i = DITEM_FAILURE;
+ else
+ i = DITEM_SUCCESS;
+ distVerifyFlags();
+ return i | DITEM_RESTORE;
+}
+
+static int
+distMaybeSetDES(dialogMenuItem *self)
+{
+ int i = DITEM_SUCCESS;
+
+ dialog_clear_norefresh();
+ if (!msgYesNo("Do wish to install DES cryptographic software?\n\n"
+ "If you choose No, FreeBSD will use an MD5 based password scheme which,\n"
+ "while perhaps more secure, is not interoperable with the traditional\n"
+ "UNIX DES passwords on other non-FreeBSD systems.\n\n"
+ "Please do NOT choose Yes at this point if you are outside the\n"
+ "United States and Canada yet are installing from a U.S. FTP server.\n"
+ "This will violate U.S. export restrictions and possibly get the\n"
+ "server site into trouble! In such cases, install everything but the\n"
+ "DES distribution from the U.S. server then switch your media type to\n"
+ "point to an international FTP server, using the Custom installation\n"
+ "option to select and extract the DES distribution in a second pass.")) {
+ if (!dmenuOpenSimple(&MenuDESDistributions, FALSE))
+ i = DITEM_FAILURE;
+ }
+ distVerifyFlags();
+ return i | DITEM_RESTORE;
+}
+
+static int
+distMaybeSetPorts(dialogMenuItem *self)
+{
+ dialog_clear_norefresh();
+ if (!msgYesNo("Would you like to install the FreeBSD ports collection?\n\n"
+ "This will give you ready access to over 1000 ported software packages,\n"
+ "though at a cost of around 35MB of disk space when \"clean\" and possibly\n"
+ "much more than that if a lot of the distribution tarballs are loaded\n"
+ "(unless you have the 2nd CD from a FreeBSD CDROM distribution available\n"
+ "and can mount it on /cdrom, in which case this is far less of a problem).\n\n"
+ "The ports collection is a very valuable resource and, if you have at least\n"
+ "100MB to spare in your /usr partition, well worth having around.\n\n"
+ "For more information on the ports collection & the latest ports, visit:\n"
+ " http://www.freebsd.org/ports\n"))
+ Dists |= DIST_PORTS;
+ else
+ Dists &= ~DIST_PORTS;
+ return DITEM_SUCCESS | DITEM_RESTORE;
+}
+
+static Boolean
+distSetByName(Distribution *dist, char *name)
+{
+ int i, status = FALSE;
+
+ /* Loop through current set */
+ for (i = 0; dist[i].my_name; i++) {
+ /* This is shorthand for "dist currently disabled" */
+ if (!dist[i].my_dir)
+ continue;
+ if (!strcmp(dist[i].my_name, name)) {
+ *(dist[i].my_mask) |= dist[i].my_bit;
+ status = TRUE;
+ }
+ if (dist[i].my_dist) {
+ if (distSetByName(dist[i].my_dist, name)) {
+ status = TRUE;
+ }
+ }
+ }
+ distVerifyFlags();
+ return status;
+}
+
+/* Just for the dispatch stuff */
+int
+distSetCustom(dialogMenuItem *self)
+{
+ char *cp, *cp2, *tmp;
+
+ if (!(tmp = variable_get(VAR_DISTS))) {
+ msgDebug("distSetCustom() called without %s variable set.\n", VAR_DISTS);
+ return DITEM_FAILURE;
+ }
+
+ cp = alloca(strlen(tmp) + 1);
+ if (!cp)
+ msgFatal("Couldn't alloca() %d bytes!\n", strlen(tmp) + 1);
+ strcpy(cp, tmp);
+ while (cp) {
+ if ((cp2 = index(cp, ' ')) != NULL)
+ *(cp2++) = '\0';
+ if (!distSetByName(DistTable, cp))
+ msgDebug("distSetCustom: Warning, no such release \"%s\"\n", cp);
+ cp = cp2;
+ }
+ distVerifyFlags();
+ return DITEM_SUCCESS;
+}
+
+int
+distSetSrc(dialogMenuItem *self)
+{
+ int i;
+
+ if (!dmenuOpenSimple(&MenuSrcDistributions, FALSE))
+ i = DITEM_FAILURE;
+ else
+ i = DITEM_SUCCESS;
+ distVerifyFlags();
+ return i | DITEM_RESTORE;
+}
+
+int
+distSetXF86(dialogMenuItem *self)
+{
+ int i = DITEM_SUCCESS;
+
+#ifdef USE_XIG_ENVIRONMENT
+ Dists |= (DIST_XIG_SERVER | DIST_COMPAT21);
+#else
+ if (!dmenuOpenSimple(&MenuXF86Select, FALSE))
+ i = DITEM_FAILURE;
+#endif
+ distVerifyFlags();
+ return i | DITEM_RESTORE;
+}
+
+static Boolean got_intr = FALSE;
+
+/* timeout handler */
+static void
+handle_intr(int sig)
+{
+ msgDebug("User generated interrupt.\n");
+ got_intr = TRUE;
+}
+
+static int
+check_for_interrupt(void)
+{
+ if (got_intr) {
+ got_intr = FALSE;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static Boolean
+distExtract(char *parent, Distribution *me)
+{
+ int i, status, total, intr;
+ int cpid, zpid, fd2, chunk, numchunks;
+ char *path, *dist, buf[BUFSIZ];
+ const char *tmp;
+ FILE *fp;
+ WINDOW *w = savescr();
+ struct timeval start, stop;
+ struct sigaction old, new;
+
+ status = TRUE;
+ dialog_clear_norefresh();
+ if (isDebug())
+ msgDebug("distExtract: parent: %s, me: %s\n", parent ? parent : "(none)", me->my_name);
+
+ /* Make ^C fake a sudden timeout */
+ new.sa_handler = handle_intr;
+ new.sa_flags = 0;
+ new.sa_mask = 0;
+ sigaction(SIGINT, &new, &old);
+
+ /* Loop through to see if we're in our parent's plans */
+ for (i = 0; me[i].my_name; i++) {
+ dist = me[i].my_name;
+ path = parent ? parent : dist;
+
+ /* If our bit isn't set, go to the next */
+ if (!(me[i].my_bit & *(me[i].my_mask)))
+ continue;
+
+ /* This is shorthand for "dist currently disabled" */
+ if (!me[i].my_dir) {
+ *(me[i].my_mask) &= ~(me[i].my_bit);
+ continue;
+ }
+
+ /* Recurse if we actually have a sub-distribution */
+ if (me[i].my_dist) {
+ if ((status = distExtract(dist, me[i].my_dist)) == TRUE)
+ *(me[i].my_mask) &= ~(me[i].my_bit);
+ goto done;
+ }
+
+ /*
+ * Try to get distribution as multiple pieces, locating and parsing an
+ * info file which tells us how many we need for this distribution.
+ */
+ numchunks = 0;
+ snprintf(buf, sizeof buf, "%s/%s.inf", path, dist);
+
+ getinfo:
+ fp = mediaDevice->get(mediaDevice, buf, TRUE);
+ intr = check_for_interrupt();
+ if (fp == (FILE *)IO_ERROR || intr) { /* Hard error, can't continue */
+ if (!msgYesNo("Unable to open %s: %s.\nReinitialize media?",
+ buf, !intr ? "I/O error." : "User interrupt.")) {
+ mediaDevice->shutdown(mediaDevice);
+ if (!mediaDevice->init(mediaDevice)) {
+ status = FALSE;
+ goto done;
+ }
+ else
+ goto getinfo;
+ }
+ else {
+ status = FALSE;
+ goto done;
+ }
+ }
+ else if (fp > 0) {
+ int status;
+ Attribs *dist_attr;
+
+ if (isDebug())
+ msgDebug("Parsing attributes file for distribution %s\n", dist);
+ dist_attr = alloca(sizeof(Attribs) * MAX_ATTRIBS);
+
+ status = attr_parse(dist_attr, fp);
+ intr = check_for_interrupt();
+ if (intr || DITEM_STATUS(status) == DITEM_FAILURE)
+ 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 = attr_match(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.tgz", path, dist);
+ /*
+ * Passing TRUE as 3rd parm to get routine makes this a "probing" get, for which errors
+ * are not considered too significant.
+ */
+ getsingle:
+ fp = mediaDevice->get(mediaDevice, buf, TRUE);
+ intr = check_for_interrupt();
+ if (fp == (FILE *)IO_ERROR || intr) { /* Hard error, can't continue */
+ if (intr) /* result of an interrupt */
+ msgConfirm("Unable to open %s: User interrupt", buf);
+ else
+ msgConfirm("Unable to open %s: I/O error", buf);
+ mediaDevice->shutdown(mediaDevice);
+ if (!mediaDevice->init(mediaDevice)) {
+ status = FALSE;
+ goto done;
+ }
+ else
+ goto getsingle;
+ }
+ else if (fp > 0) {
+ char *dir = root_bias(me[i].my_dir);
+
+ msgNotify("Extracting %s into %s directory...", dist, dir);
+ status = mediaExtractDist(dir, dist, fp);
+ fclose(fp);
+ goto done;
+ }
+ else
+ numchunks = 0;
+ }
+
+ /* Fall through from "we got the attribute file, now get the pieces" step */
+ if (!numchunks)
+ continue;
+
+ if (isDebug())
+ msgDebug("Attempting to extract distribution from %u chunks.\n", numchunks);
+
+ total = 0;
+ (void)gettimeofday(&start, (struct timezone *)0);
+
+ /* We have one or more chunks, initialize unpackers... */
+ mediaExtractDistBegin(root_bias(me[i].my_dir), &fd2, &zpid, &cpid);
+
+ /* And go for all the chunks */
+ for (chunk = 0; chunk < numchunks; chunk++) {
+ int n, retval, last_msg;
+ char prompt[80];
+
+ last_msg = 0;
+
+ getchunk:
+ snprintf(buf, sizeof buf, "%s/%s.%c%c", path, dist, (chunk / 26) + 'a', (chunk % 26) + 'a');
+ if (isDebug())
+ msgDebug("trying for piece %d of %d: %s\n", chunk + 1, numchunks, buf);
+ fp = mediaDevice->get(mediaDevice, buf, FALSE);
+ intr = check_for_interrupt();
+ if (fp <= (FILE *)0 || intr) {
+ if (fp == (FILE *)0)
+ msgConfirm("Failed to find %s on this media. Reinitializing media.", buf);
+ else
+ msgConfirm("failed to retreive piece file %s.\n"
+ "%s: Reinitializing media.", buf, !intr ? "I/O error" : "User interrupt");
+ mediaDevice->shutdown(mediaDevice);
+ if (!mediaDevice->init(mediaDevice))
+ goto punt;
+ else
+ goto getchunk;
+ }
+
+ snprintf(prompt, sizeof prompt, "Extracting %s into %s directory...", dist, root_bias(me[i].my_dir));
+ dialog_gauge("Progress", prompt, 8, 15, 6, 50, (int)((float)(chunk + 1) / numchunks * 100));
+
+ while (1) {
+ int seconds;
+
+ n = fread(buf, 1, BUFSIZ, fp);
+ if (check_for_interrupt()) {
+ msgConfirm("Media read error: User interrupt.");
+ fclose(fp);
+ goto punt;
+ }
+ else if (n <= 0)
+ break;
+ total += n;
+
+ /* Print statistics about how we're doing */
+ (void) gettimeofday(&stop, (struct timezone *)0);
+ stop.tv_sec = stop.tv_sec - start.tv_sec;
+ stop.tv_usec = stop.tv_usec - start.tv_usec;
+ if (stop.tv_usec < 0)
+ stop.tv_sec--, stop.tv_usec += 1000000;
+ seconds = stop.tv_sec + (stop.tv_usec / 1000000.0);
+ if (!seconds)
+ seconds = 1;
+
+ if (seconds != last_msg) {
+ last_msg = seconds;
+ msgInfo("%10d bytes read from %s dist, chunk %2d of %2d @ %.1f KB/sec.",
+ total, dist, chunk + 1, numchunks, (total / seconds) / 1024.0);
+ }
+ retval = write(fd2, buf, n);
+ if (retval != n) {
+ fclose(fp);
+ dialog_clear_norefresh();
+ msgConfirm("Write failure on transfer! (wrote %d bytes of %d bytes)", retval, n);
+ goto punt;
+ }
+ }
+ fclose(fp);
+ }
+ close(fd2);
+ status = mediaExtractDistEnd(zpid, cpid);
+ goto done;
+
+ punt:
+ close(fd2);
+ mediaExtractDistEnd(zpid, cpid);
+ status = FALSE;
+
+ done:
+ if (!status) {
+ if (me[i].my_dist) {
+ msgConfirm("Unable to transfer all components of the %s distribution.\n"
+ "If this is a CDROM install, it may be because export restrictions prohibit\n"
+ "DES code from being shipped from the U.S. Try to get this code from a\n"
+ "local FTP site instead!", me[i].my_name);
+ }
+ else {
+ status = msgYesNo("Unable to transfer the %s distribution from\n%s.\n\n"
+ "Do you want to try to retrieve it again?",
+ me[i].my_name, mediaDevice->name);
+ if (!status)
+ --i;
+ dialog_clear();
+ }
+ }
+ /* If extract was successful, remove ourselves from further consideration */
+ if (status)
+ *(me[i].my_mask) &= ~(me[i].my_bit);
+ else
+ continue;
+ }
+ sigaction(SIGINT, &old, NULL); /* Restore signal handler */
+ restorescr(w);
+ return status;
+}
+
+static void
+printSelected(char *buf, int selected, Distribution *me, int *col)
+{
+ int i;
+
+ /* Loop through to see if we're in our parent's plans */
+ for (i = 0; me[i].my_name; i++) {
+
+ /* If our bit isn't set, go to the next */
+ if (!(me[i].my_bit & selected))
+ continue;
+
+ /* This is shorthand for "dist currently disabled" */
+ if (!me[i].my_dir)
+ continue;
+
+ *col += strlen(me[i].my_name);
+ if (*col > 50) {
+ *col = 0;
+ strcat(buf, "\n");
+ }
+ sprintf(&buf[strlen(buf)], " %s", me[i].my_name);
+ /* Recurse if have a sub-distribution */
+ if (me[i].my_dist)
+ printSelected(buf, *(me[i].my_mask), me[i].my_dist, col);
+ }
+}
+
+int
+distExtractAll(dialogMenuItem *self)
+{
+ int retries = 0;
+ char buf[512];
+
+ /* paranoia */
+ if (!Dists) {
+ if (!dmenuOpenSimple(&MenuDistributions, FALSE) || !Dists)
+ return DITEM_FAILURE | DITEM_RESTORE;
+ }
+
+ if (!mediaVerify() || !mediaDevice->init(mediaDevice))
+ return DITEM_FAILURE;
+
+ dialog_clear_norefresh();
+ msgNotify("Attempting to install all selected distributions..");
+ /* Try for 3 times around the loop, then give up. */
+ while (Dists && ++retries < 3)
+ distExtract(NULL, DistTable);
+
+ if (Dists) {
+ int col = 0;
+
+ buf[0] = '\0';
+ printSelected(buf, Dists, DistTable, &col);
+ dialog_clear_norefresh();
+ msgConfirm("Couldn't extract the following distributions. This may\n"
+ "be because they were not available on the installation\n"
+ "media you've chosen:\n\n\t%s", buf);
+ return DITEM_SUCCESS | DITEM_RESTORE;
+ }
+ return DITEM_SUCCESS;
+}
diff --git a/usr.sbin/sysinstall/dist.h b/usr.sbin/sysinstall/dist.h
new file mode 100644
index 0000000..2da05df
--- /dev/null
+++ b/usr.sbin/sysinstall/dist.h
@@ -0,0 +1,117 @@
+#ifndef _DIST_H_INCLUDE
+#define _DIST_H_INCLUDE
+
+/* Bitfields for distributions - hope we never have more than 32! :-) */
+#define DIST_BIN 0x0001
+#define DIST_GAMES 0x0002
+#define DIST_MANPAGES 0x0004
+#define DIST_PROFLIBS 0x0008
+#define DIST_DICT 0x0010
+#define DIST_SRC 0x0020
+#define DIST_DOC 0x0040
+#define DIST_INFO 0x0080
+#define DIST_COMPAT1X 0x0100
+#define DIST_COMPAT20 0x0200
+#define DIST_COMPAT21 0x0400
+#define DIST_XF86 0x0800
+#define DIST_DES 0x1000
+#define DIST_CATPAGES 0x2000
+#define DIST_PORTS 0x4000
+#ifdef USE_XIG_ENVIRONMENT
+#define DIST_XIG_SERVER 0x8000
+#else
+#define DIST_USR1 0x8000
+#endif
+#define DIST_ALL 0x7FFF
+
+/* Canned distribution sets */
+#define _DIST_DEVELOPER \
+ (DIST_BIN | DIST_DOC | DIST_MANPAGES | DIST_DICT | DIST_PROFLIBS | DIST_INFO | DIST_SRC)
+
+#define _DIST_USER \
+ (DIST_BIN | DIST_DOC | DIST_MANPAGES | DIST_DICT)
+
+/* Subtypes for DES distribution */
+#define DIST_DES_DES 0x0001
+#define DIST_DES_SCRYPTO 0x0002
+#define DIST_DES_SSECURE 0x0004
+#define DIST_DES_KERBEROS 0x0008
+#define DIST_DES_SKERBEROS 0x0010
+
+/* Subtypes for SRC distribution */
+#define DIST_SRC_BASE 0x00001
+#define DIST_SRC_CONTRIB 0x00002
+#define DIST_SRC_GNU 0x00004
+#define DIST_SRC_ETC 0x00008
+#define DIST_SRC_GAMES 0x00010
+#define DIST_SRC_INCLUDE 0x00020
+#define DIST_SRC_LIB 0x00040
+#define DIST_SRC_LIBEXEC 0x00080
+#define DIST_SRC_LKM 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_SMAILCF 0x10000
+#define DIST_SRC_ALL 0xFFFF /* no SMAILCF, it's part of USBIN */
+
+#ifndef USE_XIG_ENVIRONMENT
+/* 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_SRC 0x0800
+#define DIST_XF86_CSRC 0x1000
+#define DIST_XF86_MISC_ALL 0x1FFF
+#define DIST_XF86_SERVER 0x8000
+#define DIST_XF86_SERVER_8514 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_9SPW 0x0000100
+#define DIST_XF86_SERVER_9TGU 0x0000200
+#define DIST_XF86_SERVER_9WEP 0x0000400
+#define DIST_XF86_SERVER_9WS 0x0000800
+#define DIST_XF86_SERVER_9WSN 0x0001000
+#define DIST_XF86_SERVER_AGX 0x0002000
+#define DIST_XF86_SERVER_I128 0x0004000
+#define DIST_XF86_SERVER_MACH8 0x0008000
+#define DIST_XF86_SERVER_MACH32 0x0010000
+#define DIST_XF86_SERVER_MACH64 0x0020000
+#define DIST_XF86_SERVER_MONO 0x0040000
+#define DIST_XF86_SERVER_P9000 0x0080000
+#define DIST_XF86_SERVER_S3 0x0100000
+#define DIST_XF86_SERVER_S3V 0x0200000
+#define DIST_XF86_SERVER_SVGA 0x0400000
+#define DIST_XF86_SERVER_VGA16 0x0800000
+#define DIST_XF86_SERVER_W32 0x1000000
+#define DIST_XF86_SERVER_NEST 0x2000000
+#define DIST_XF86_SERVER_VFB 0x4000000
+#define DIST_XF86_SERVER_ALL 0x7FFFFFF
+#define DIST_XF86_FONTS 0x10000
+#define DIST_XF86_FONTS_MISC 0x0001
+#define DIST_XF86_FONTS_100 0x0002
+#define DIST_XF86_FONTS_CYR 0x0004
+#define DIST_XF86_FONTS_SCALE 0x0008
+#define DIST_XF86_FONTS_NON 0x0010
+#define DIST_XF86_FONTS_SERVER 0x0020
+#define DIST_XF86_FONTS_ALL 0x003F
+#define DIST_XF86_ALL 0x1FFFF
+#endif /* !USE_XIG_ENVIRONMENT */
+
+#endif /* _DIST_H_INCLUDE */
diff --git a/usr.sbin/sysinstall/dmenu.c b/usr.sbin/sysinstall/dmenu.c
new file mode 100644
index 0000000..45c861e
--- /dev/null
+++ b/usr.sbin/sysinstall/dmenu.c
@@ -0,0 +1,304 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last attempt in the `sysinstall' line, the next
+ * generation being slated for what's essentially a complete rewrite.
+ *
+ * $Id: dmenu.c,v 1.34 1997/09/17 16:18:14 pst Exp $
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include "sysinstall.h"
+#include <errno.h>
+
+#define MAX_MENU 15
+
+static Boolean exited;
+
+int
+dmenuDisplayFile(dialogMenuItem *tmp)
+{
+ systemDisplayHelp((char *)tmp->data);
+ return DITEM_SUCCESS | DITEM_RESTORE;
+}
+
+int
+dmenuSubmenu(dialogMenuItem *tmp)
+{
+ return (dmenuOpenSimple((DMenu *)(tmp->data), FALSE) ? DITEM_SUCCESS : DITEM_FAILURE) |
+ DITEM_RESTORE;
+}
+
+int
+dmenuSystemCommand(dialogMenuItem *self)
+{
+ WINDOW *w = NULL; /* Keep lint happy */
+
+ /* If aux is set, the command is known not to produce any screen-spoiling output */
+ if (!self->aux)
+ w = savescr();
+ systemExecute((char *)self->data);
+ if (!self->aux)
+ restorescr(w);
+ return DITEM_SUCCESS;
+}
+
+int
+dmenuSystemCommandBox(dialogMenuItem *tmp)
+{
+ use_helpfile(NULL);
+ use_helpline("Select OK to dismiss this dialog");
+ dialog_prgbox(tmp->title, (char *)tmp->data, 22, 76, 1, 1);
+ return DITEM_SUCCESS | DITEM_RESTORE;
+}
+
+int
+dmenuExit(dialogMenuItem *tmp)
+{
+ exited = TRUE;
+ return DITEM_LEAVE_MENU;
+}
+
+int
+dmenuSetVariable(dialogMenuItem *tmp)
+{
+ variable_set((char *)tmp->data);
+ 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 = cp2;
+ }
+ free(copy);
+ return DITEM_SUCCESS;
+}
+
+int
+dmenuSetKmapVariable(dialogMenuItem *tmp)
+{
+ char *lang;
+ int err;
+
+ variable_set((char *)tmp->data);
+ 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)
+{
+ if (!variable_get((char *)tmp->data))
+ variable_set((char *)tmp->data);
+ else
+ variable_unset((char *)tmp->data);
+ return DITEM_SUCCESS;
+}
+
+int
+dmenuISetVariable(dialogMenuItem *tmp)
+{
+ char *ans, *var;
+ WINDOW *w = NULL; /* Keep lint happy */
+
+ if (!(var = (char *)tmp->data)) {
+ msgConfirm("Incorrect data field for `%s'!", tmp->title);
+ return DITEM_FAILURE;
+ }
+ w = savescr();
+ ans = msgGetInput(variable_get(var), tmp->title);
+ restorescr(w);
+ if (!ans)
+ return DITEM_FAILURE;
+ else if (!*ans)
+ variable_unset(var);
+ else
+ variable_set2(var, ans);
+ 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;
+ if (!w)
+ return FALSE;
+ return variable_check(w);
+}
+
+int
+dmenuVarsCheck(dialogMenuItem *item)
+{
+ int res, init;
+ char *w, *cp1, *cp2;
+ char *copy;
+
+ w = (char *)item->aux;
+ if (!w)
+ w = (char *)item->data;
+ if (!w)
+ return FALSE;
+
+ copy = strdup(w);
+ res = TRUE;
+ init = FALSE;
+ for (cp1 = copy; cp1 != NULL;) {
+ init = TRUE;
+ cp2 = index(cp1, ',');
+ if (cp2 != NULL)
+ *cp2++ = '\0';
+ res = res && variable_check(cp1);
+ cp1 = cp2;
+ }
+ free(copy);
+ return res && init;
+}
+
+int
+dmenuRadioCheck(dialogMenuItem *item)
+{
+ return (*((unsigned int *)item->data) == item->aux);
+}
+
+static int
+menu_height(DMenu *menu, int n)
+{
+ int max;
+ char *t;
+
+ max = MAX_MENU;
+ if (StatusLine > 24)
+ max += StatusLine - 24;
+ for (t = menu->prompt; *t; t++) {
+ if (*t == '\n')
+ --max;
+ }
+ return n > max ? max : n;
+}
+
+/* Traverse over an internal menu */
+Boolean
+dmenuOpen(DMenu *menu, int *choice, int *scroll, int *curr, int *max, Boolean buttons)
+{
+ int n, rval = 0;
+ dialogMenuItem *items;
+
+ items = menu->items;
+ if (buttons)
+ items += 2;
+ /* Count up all the items */
+ for (n = 0; items[n].title; n++);
+
+ while (1) {
+ char buf[FILENAME_MAX];
+
+ /* Any helpful hints, put 'em up! */
+ use_helpline(menu->helpline);
+ use_helpfile(systemHelpFile(menu->helpfile, buf));
+
+ /* Pop up that dialog! */
+ dialog_clear_norefresh();
+ if (menu->type & DMENU_NORMAL_TYPE)
+ rval = dialog_menu((u_char *)menu->title, (u_char *)menu->prompt, -1, -1,
+ menu_height(menu, n), -n, items, (char *)buttons, choice, scroll);
+
+ else if (menu->type & DMENU_RADIO_TYPE)
+ rval = dialog_radiolist((u_char *)menu->title, (u_char *)menu->prompt, -1, -1,
+ menu_height(menu, n), -n, items, (char *)buttons);
+
+ else if (menu->type & DMENU_CHECKLIST_TYPE)
+ rval = dialog_checklist((u_char *)menu->title, (u_char *)menu->prompt, -1, -1,
+ menu_height(menu, n), -n, items, (char *)buttons);
+ else
+ msgFatal("Menu: `%s' is of an unknown type\n", menu->title);
+ clearok(stdscr, TRUE);
+ if (exited) {
+ exited = FALSE;
+ return TRUE;
+ }
+ else if (rval)
+ return FALSE;
+ else if (menu->type & DMENU_SELECTION_RETURNS)
+ return TRUE;
+ }
+}
diff --git a/usr.sbin/sysinstall/doc.c b/usr.sbin/sysinstall/doc.c
new file mode 100644
index 0000000..9d82f26
--- /dev/null
+++ b/usr.sbin/sysinstall/doc.c
@@ -0,0 +1,126 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last attempt in the `sysinstall' line, the next
+ * generation being slated for what's essentially a complete rewrite.
+ *
+ * $Id: doc.c,v 1.22 1997/02/07 04:25:53 jkh Exp $
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+
+ *
+ */
+
+#include "sysinstall.h"
+
+/*
+ * This is called from the main menu. Try to find a copy of Lynx from somewhere
+ * and fire it up on the first copy of the handbook we can find.
+ */
+int
+docBrowser(dialogMenuItem *self)
+{
+ int ret;
+ char *browser = variable_get(VAR_BROWSER_PACKAGE);
+
+ if (RunningAsInit && !strstr(variable_get(SYSTEM_STATE), "install")) {
+ msgConfirm("This option may only be used after the system is installed, sorry!");
+ return DITEM_FAILURE;
+ }
+
+ /* First, make sure we have whatever browser we've chosen is here */
+ if (!package_exists(browser)) {
+ ret = package_add(browser);
+ if (DITEM_STATUS(ret) != DITEM_SUCCESS) {
+ msgConfirm("Unable to install the %s HTML browser package. You may\n"
+ "wish to verify that your media is configured correctly and\n"
+ "try again.", browser);
+ return ret;
+ }
+ }
+
+ if (!file_executable(variable_get(VAR_BROWSER_BINARY))) {
+ if (!msgYesNo("Hmmm. The %s package claims to have installed, but I can't\n"
+ "find its binary in %s! You may wish to try a different\n"
+ "location to load the package from (go to Media menu) and see if that\n"
+ "makes a difference.\n\n"
+ "I suggest that we remove the version that was extracted since it does\n"
+ "not appear to be correct. Would you like me to do that now?",
+ browser, variable_get(VAR_BROWSER_BINARY)))
+ vsystem("pkg_delete %s %s", !strcmp(variable_get(VAR_CPIO_VERBOSITY), "high") ? "-v" : "", browser);
+ return DITEM_FAILURE | DITEM_RESTORE;
+ }
+
+ /* Run browser on the appropriate doc */
+ if (dmenuOpenSimple(&MenuHTMLDoc, FALSE))
+ return DITEM_SUCCESS | DITEM_RESTORE;
+ else
+ return DITEM_FAILURE | DITEM_RESTORE;
+}
+
+/* Try to show one of the documents requested from the HTML doc menu */
+int
+docShowDocument(dialogMenuItem *self)
+{
+ char tmp[512], target[512];
+ char *where = NULL;
+ char *browser = variable_get(VAR_BROWSER_BINARY);
+ char *str = self->prompt;
+
+ if (!file_executable(browser)) {
+ msgConfirm("Can't find the browser in %s! Please ensure that it's\n"
+ "properly set in the Options editor.", browser);
+ return DITEM_FAILURE;
+ }
+ if (!strcmp(str, "Home"))
+ where = "http://www.freebsd.org";
+ else if (!strcmp(str, "Other"))
+ where = msgGetInput("http://www.freebsd.org", "Please enter the URL of the location you wish to visit.");
+ else if (!strcmp(str, "FAQ")) {
+ strcpy(target, "/usr/share/doc/FAQ/FAQ.html");
+ if (!file_readable(target))
+ strcpy(target, "http://www.freebsd.org/FAQ");
+ where = target;
+ }
+ else if (!strcmp(str, "Handbook")) {
+ strcpy(target, "/usr/share/doc/handbook/handbook.html");
+ if (!file_readable(target))
+ strcpy(target, "http://www.freebsd.org/handbook");
+ where = target;
+ }
+ if (where) {
+ sprintf(tmp, "%s %s", browser, where);
+ dialog_clear();
+ systemExecute(tmp);
+ return DITEM_SUCCESS | DITEM_RESTORE;
+ }
+ else {
+ msgConfirm("Hmmmmm! I can't seem to access the documentation you selected!\n"
+ "Have you loaded the bin distribution? Is your network connected?");
+ return DITEM_FAILURE;
+ }
+}
diff --git a/usr.sbin/sysinstall/dos.c b/usr.sbin/sysinstall/dos.c
new file mode 100644
index 0000000..ccdb204
--- /dev/null
+++ b/usr.sbin/sysinstall/dos.c
@@ -0,0 +1,106 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last attempt in the `sysinstall' line, the next
+ * generation being slated to essentially a complete rewrite.
+ *
+ * $Id$
+ *
+ * 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>
+#include "dosio.h"
+
+static DOS_FS DOSFS;
+static Boolean DOSMounted;
+
+Boolean
+mediaInitDOS(Device *dev)
+{
+ if (!RunningAsInit || DOSMounted)
+ return TRUE;
+
+ if (dos_mount(&DOSFS, dev->devname)) {
+ msgConfirm("Error mounting DOS partition %s : %s (%u)", dev->devname, strerror(errno), errno);
+ return FALSE;
+ }
+ DOSMounted = TRUE;
+ msgDebug("DOS partition %s mounted\n", dev->devname);
+ return TRUE;
+}
+
+FILE *
+mediaGetDOS(Device *dev, char *file, Boolean probe)
+{
+ char buf[PATH_MAX];
+ FILE *fp;
+
+ if (!DOSMounted) {
+ msgDebug("Can't get DOS file %s - DOSFS currently unmounted!\n", file);
+ return NULL;
+ }
+
+ if (isDebug())
+ msgDebug("Request for %s from DOS\n", file);
+ snprintf(buf, PATH_MAX, "/freebsd/%s", file);
+ if ((fp = dos_open(&DOSFS, buf)))
+ return fp;
+ snprintf(buf, PATH_MAX, "/freebsd/dists/%s", file);
+ if ((fp = dos_open(&DOSFS, buf)))
+ return fp;
+ snprintf(buf, PATH_MAX, "/%s", file);
+ if ((fp = dos_open(&DOSFS, buf)))
+ return fp;
+ snprintf(buf, PATH_MAX, "/dists/%s", file);
+ if ((fp = dos_open(&DOSFS, buf)))
+ return fp;
+ return NULL;
+}
+
+void
+mediaShutdownDOS(Device *dev)
+{
+ if (!RunningAsInit || !DOSMounted)
+ return;
+ if (dos_unmount(&DOSFS))
+ msgConfirm("Could not unmount DOS partition %s : %s", dev->devname, strerror(errno));
+ else if (isDebug())
+ msgDebug("Unmount of DOS partition on %s successful\n", dev->devname);
+ DOSMounted = FALSE;
+ return;
+}
diff --git a/usr.sbin/sysinstall/floppy.c b/usr.sbin/sysinstall/floppy.c
new file mode 100644
index 0000000..d5c5c59
--- /dev/null
+++ b/usr.sbin/sysinstall/floppy.c
@@ -0,0 +1,204 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last attempt in the `sysinstall' line, the next
+ * generation being slated to essentially a complete rewrite.
+ *
+ * $Id: floppy.c,v 1.24 1997/03/19 10:09:16 jkh Exp $
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ * Copyright (c) 1995
+ * Gary J Palmer. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+/* These routines deal with getting things off of floppy media */
+
+#include "sysinstall.h"
+#include <sys/fcntl.h>
+#include <sys/stat.h>
+#include <sys/errno.h>
+#include <sys/param.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <grp.h>
+
+#define MSDOSFS
+#include <sys/mount.h>
+#include <msdosfs/msdosfsmount.h>
+#undef MSDOSFS
+
+#include <ufs/ufs/ufsmount.h>
+static Device *floppyDev;
+static Boolean floppyMounted;
+
+char *distWanted;
+
+/* For finding floppies */
+static int
+floppyChoiceHook(dialogMenuItem *self)
+{
+ Device **devs;
+
+ devs = deviceFind(self->prompt, DEVICE_TYPE_FLOPPY);
+ if (devs)
+ floppyDev = devs[0];
+ return devs ? DITEM_LEAVE_MENU : DITEM_FAILURE;
+}
+
+/* Our last-ditch routine for getting ROOT from a floppy */
+int
+getRootFloppy(void)
+{
+ int fd = -1;
+
+ if (mediaDevice->type == DEVICE_TYPE_FLOPPY)
+ floppyDev = mediaDevice;
+ else {
+ Device **devs;
+ int cnt;
+
+ devs = deviceFind(NULL, DEVICE_TYPE_FLOPPY);
+ cnt = deviceCount(devs);
+ if (!cnt) {
+ msgConfirm("No floppy devices found! Something is seriously wrong!");
+ return -1;
+ }
+ else if (cnt == 1)
+ floppyDev = devs[0];
+ else {
+ DMenu *menu;
+ int ret;
+ WINDOW *save = savescr();
+
+ menu = deviceCreateMenu(&MenuMediaFloppy, DEVICE_TYPE_FLOPPY, floppyChoiceHook, NULL);
+ menu->title = "Please insert the ROOT floppy";
+ ret = dmenuOpenSimple(menu, FALSE);
+ restorescr(save);
+ if (!ret)
+ return -1;
+ }
+ }
+ while (fd == -1) {
+ msgConfirm("Please insert the ROOT floppy in %s and press [ENTER]", floppyDev->description);
+ fd = open(floppyDev->devname, O_RDONLY);
+ if (isDebug())
+ msgDebug("getRootFloppy on %s yields fd of %d\n", floppyDev->devname, fd);
+ if (fd == -1 && msgYesNo("Couldn't open the floppy - do you want to try again?") != 0)
+ break;
+ }
+ return fd;
+}
+
+Boolean
+mediaInitFloppy(Device *dev)
+{
+ struct msdosfs_args dosargs;
+ struct ufs_args u_args;
+ char *mountpoint = "/dist";
+
+ if (floppyMounted)
+ return TRUE;
+
+ if (Mkdir(mountpoint)) {
+ msgConfirm("Unable to make %s directory mountpoint for %s!", mountpoint, dev->devname);
+ return FALSE;
+ }
+
+ msgDebug("Init floppy called for %s distribution.\n", distWanted ? distWanted : "some");
+ if (!distWanted)
+ msgConfirm("Please insert floppy in %s", dev->description);
+ else
+ msgConfirm("Please insert floppy containing %s in %s", distWanted, dev->description);
+
+ memset(&dosargs, 0, sizeof dosargs);
+ dosargs.fspec = dev->devname;
+ dosargs.uid = dosargs.gid = 0;
+ dosargs.mask = 0777;
+
+ memset(&u_args, 0, sizeof(u_args));
+ u_args.fspec = dev->devname;
+
+ if (mount(MOUNT_MSDOS, mountpoint, MNT_RDONLY, (caddr_t)&dosargs) == -1) {
+ if (mount(MOUNT_UFS, mountpoint, MNT_RDONLY, (caddr_t)&u_args) == -1) {
+ msgConfirm("Error mounting floppy %s (%s) on %s : %s", dev->name, dev->devname, mountpoint,
+ strerror(errno));
+ return FALSE;
+ }
+ }
+ msgDebug("initFloppy: mounted floppy %s successfully on %s\n", dev->devname, mountpoint);
+ floppyMounted = TRUE;
+ distWanted = NULL;
+ return TRUE;
+}
+
+FILE *
+mediaGetFloppy(Device *dev, char *file, Boolean probe)
+{
+ char buf[PATH_MAX];
+ FILE *fp;
+ int nretries = 5;
+
+ snprintf(buf, PATH_MAX, "/dist/%s", file);
+
+ if (isDebug())
+ msgDebug("Request for %s from floppy on /dist, probe is %d.\n", buf, probe);
+ 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)
+{
+ char *mountpoint = "/dist";
+
+ if (floppyMounted) {
+ if (unmount(mountpoint, MNT_FORCE) != 0)
+ msgDebug("Umount of floppy on %s failed: %s (%d)\n", mountpoint, strerror(errno), errno);
+ else {
+ floppyMounted = FALSE;
+ msgDebug("Floppy unmounted successfully.\n");
+ 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..3c91b748
--- /dev/null
+++ b/usr.sbin/sysinstall/ftp.c
@@ -0,0 +1,257 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last attempt in the `sysinstall' line, the next
+ * generation being slated to essentially a complete rewrite.
+ *
+ * $Id: ftp.c,v 1.33 1997/10/03 03:32:03 jkh Exp $
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include "sysinstall.h"
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <sys/param.h>
+#include <sys/wait.h>
+#include <netdb.h>
+#include <pwd.h>
+#include <ftpio.h>
+
+Boolean ftpInitted = FALSE;
+static FILE *OpenConn;
+int FtpPort;
+
+/* Brings up attached network device, if any - takes FTP device as arg */
+static Boolean
+netUp(Device *dev)
+{
+ Device *netdev = (Device *)dev->private;
+
+ if (netdev)
+ return netdev->init(netdev);
+ else
+ return TRUE; /* No net == happy net */
+}
+
+/* Brings down attached network device, if any - takes FTP device as arg */
+static void
+netDown(Device *dev)
+{
+ Device *netdev = (Device *)dev->private;
+
+ if (netdev)
+ netdev->shutdown(netdev);
+}
+
+Boolean
+mediaInitFTP(Device *dev)
+{
+ int i, code;
+ char *cp, *rel, *hostname, *dir;
+ char *user, *login_name, password[80];
+
+ if (ftpInitted)
+ return TRUE;
+
+ if (isDebug())
+ msgDebug("Init routine for FTP called.\n");
+
+ if (OpenConn) {
+ fclose(OpenConn);
+ OpenConn = NULL;
+ }
+
+ /* If we can't initialize the network, bag it! */
+ if (!netUp(dev))
+ return FALSE;
+
+try:
+ cp = variable_get(VAR_FTP_PATH);
+ if (!cp) {
+ if (DITEM_STATUS(mediaSetFTP(NULL)) == DITEM_FAILURE || (cp = variable_get(VAR_FTP_PATH)) == NULL) {
+ msgConfirm("Unable to get proper FTP path. FTP media not initialized.");
+ netDown(dev);
+ return FALSE;
+ }
+ }
+
+ hostname = variable_get(VAR_FTP_HOST);
+ dir = variable_get(VAR_FTP_DIR);
+ if (!hostname || !dir) {
+ msgConfirm("Missing FTP host or directory specification. FTP media not initialized,");
+ netDown(dev);
+ return FALSE;
+ }
+ user = variable_get(VAR_FTP_USER);
+ login_name = (!user || !*user) ? "anonymous" : user;
+
+ if (variable_get(VAR_FTP_PASS))
+ SAFE_STRCPY(password, variable_get(VAR_FTP_PASS));
+ else if (RunningAsInit)
+ sprintf(password, "installer@%s", variable_get(VAR_HOSTNAME));
+ else {
+ struct passwd *pw;
+ char *user;
+
+ pw = getpwuid(getuid());
+ user = pw ? pw->pw_name : "ftp";
+ sprintf(password, "%s@%s", user, variable_get(VAR_HOSTNAME));
+ }
+ msgNotify("Logging in to %s@%s..", login_name, hostname);
+ if ((OpenConn = ftpLogin(hostname, login_name, password, FtpPort, isDebug(), &code)) == NULL) {
+ msgConfirm("Couldn't open FTP connection to %s:\n %s.", hostname, ftpErrString(code));
+ goto punt;
+ }
+
+ ftpPassive(OpenConn, !strcmp(variable_get(VAR_FTP_STATE), "passive"));
+ ftpBinary(OpenConn);
+ if (dir && *dir != '\0') {
+ if ((i = ftpChdir(OpenConn, dir)) != 0) {
+ if (i == 550)
+ msgConfirm("No such directory ftp://%s/%s\n"
+ "please check your URL and try again.", hostname, dir);
+ else
+ msgConfirm("FTP chdir to ftp://%s/%s returned error status:\n %s.", hostname, dir, ftpErrString(i));
+ goto punt;
+ }
+ }
+
+ /* Give it a shot - can't hurt to try and zoom in if we can, unless the release is set to
+ __RELEASE or "none" which signifies that it's not set */
+ rel = variable_get(VAR_RELNAME);
+ if (strcmp(rel, "__RELEASE") && strcmp(rel, "none"))
+ i = ftpChdir(OpenConn, rel);
+ else
+ i = 0;
+ if (i) {
+ if (!msgYesNo("Warning: Can't CD to `%s' distribution on this\n"
+ "FTP server. You may need to visit a different server for\n"
+ "the release you're trying to fetch or go to the Options\n"
+ "menu and to set the release name to explicitly match what's\n"
+ "available on %s (or set to \"none\").\n\n"
+ "Would you like to select another FTP server?",
+ rel, hostname)) {
+ variable_unset(VAR_FTP_PATH);
+ if (DITEM_STATUS(mediaSetFTP(NULL)) == DITEM_FAILURE)
+ goto punt;
+ else
+ goto try;
+ }
+ else
+ goto punt;
+ }
+ if (isDebug())
+ msgDebug("mediaInitFTP was successful (logged in and chdir'd)\n");
+ ftpInitted = TRUE;
+ return TRUE;
+
+punt:
+ ftpInitted = FALSE;
+ if (OpenConn != NULL) {
+ fclose(OpenConn);
+ OpenConn = NULL;
+ }
+ netDown(dev);
+ variable_unset(VAR_FTP_PATH);
+ return FALSE;
+}
+
+FILE *
+mediaGetFTP(Device *dev, char *file, Boolean probe)
+{
+ int nretries = 1;
+ FILE *fp;
+ char *try, buf[PATH_MAX];
+
+ if (!OpenConn) {
+ msgDebug("No FTP connection open, can't get file %s\n", file);
+ return NULL;
+ }
+
+ try = file;
+ while ((fp = ftpGet(OpenConn, try, 0)) == NULL) {
+ int ftperr = ftpErrno(OpenConn);
+
+ /* If a hard fail, try to "bounce" the ftp server to clear it */
+ if (ftperr != 550) {
+ if (ftperr != 421) /* Timeout? */
+ variable_unset(VAR_FTP_PATH);
+ /* If we can't re-initialize, just forget it */
+ dev->shutdown(dev);
+ if (!dev->init(dev)) {
+ netDown(dev);
+ 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, "dists/%s", file);
+ try = buf;
+ break;
+
+ case 2:
+ sprintf(buf, "%s/%s", variable_get(VAR_RELNAME), file);
+ try = buf;
+ break;
+
+ case 3:
+ sprintf(buf, "%s/dists/%s", variable_get(VAR_RELNAME), file);
+ try = buf;
+ break;
+
+ case 4:
+ try = file;
+ break;
+ }
+ }
+ }
+ return fp;
+}
+
+void
+mediaShutdownFTP(Device *dev)
+{
+ if (!ftpInitted)
+ return;
+
+ msgDebug("FTP shutdown called. OpenConn = %x\n", OpenConn);
+ if (OpenConn != NULL) {
+ fclose(OpenConn);
+ OpenConn = NULL;
+ }
+ ftpInitted = FALSE;
+}
diff --git a/usr.sbin/sysinstall/globals.c b/usr.sbin/sysinstall/globals.c
new file mode 100644
index 0000000..72aecb1
--- /dev/null
+++ b/usr.sbin/sysinstall/globals.c
@@ -0,0 +1,71 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last program in the `sysinstall' line - the next
+ * generation being essentially a complete rewrite.
+ *
+ * $Id: globals.c,v 1.18 1997/02/22 14:11:43 peter Exp $
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include "sysinstall.h"
+
+/*
+ * Various global variables and an initialization hook to set them to
+ * whatever values we feel are appropriate.
+ */
+
+int DebugFD; /* Where diagnostic output goes */
+Boolean Fake; /* Only pretend to be useful */
+Boolean RunningAsInit; /* Are we running as init? */
+Boolean DialogActive; /* Is libdialog initialized? */
+Boolean ColorDisplay; /* Are we on a color display? */
+Boolean OnVTY; /* Are we on a VTY? */
+Variable *VarHead; /* The head of the variable chain */
+Device *mediaDevice; /* Where we're installing from */
+int BootMgr; /* Which boot manager we're using */
+int StatusLine; /* Where to stick our status messages */
+jmp_buf BailOut; /* Beam me up, scotty! The natives are pissed! */
+
+/*
+ * Yes, I know some of these are already automatically initialized as
+ * globals. I simply find it clearer to set everything explicitly.
+ */
+void
+globalsInit(void)
+{
+ DebugFD = -1;
+ ColorDisplay = FALSE;
+ Fake = FALSE;
+ OnVTY = FALSE;
+ DialogActive = FALSE;
+ VarHead = NULL;
+ mediaDevice = NULL;
+ RunningAsInit = FALSE;
+}
diff --git a/usr.sbin/sysinstall/help/anonftp.hlp b/usr.sbin/sysinstall/help/anonftp.hlp
new file mode 100644
index 0000000..e90985e
--- /dev/null
+++ b/usr.sbin/sysinstall/help/anonftp.hlp
@@ -0,0 +1,19 @@
+This screen allows you to configure the anonymous FTP user.
+
+The following configuration values are editable:
+
+UID: The user ID you wish to assign to the anonymous FTP user.
+ All files uploaded will be owned by this ID.
+
+Group: Which group you wish the anonymous FTP user to be in.
+
+Comment: String describing this user in /etc/passwd
+
+
+FTP Root Directory:
+
+ Where files available for anonymous FTP will be kept.
+
+Upload subdirectory:
+
+ Where files uploaded by anonymous FTP users will go.
diff --git a/usr.sbin/sysinstall/help/configure.hlp b/usr.sbin/sysinstall/help/configure.hlp
new file mode 100644
index 0000000..5530d7d
--- /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/sysconfig'' file.
diff --git a/usr.sbin/sysinstall/help/distributions.hlp b/usr.sbin/sysinstall/help/distributions.hlp
new file mode 100644
index 0000000..01d0fc8
--- /dev/null
+++ b/usr.sbin/sysinstall/help/distributions.hlp
@@ -0,0 +1,62 @@
+DISTRIBUTION INFORMATION
+------------------------
+
+An ``X-'' prefixed before a distribution set means that the XFree86
+3.3.1 base distribution, libraries, manual pages, SVGA server and a
+set of default fonts will be selected in addition to the set itself.
+If you select such a set, you will also be presented with a set of
+menus for customizing the selections to your desired X Window System
+setup.
+
+Any distribution may be further customized by selecting the `Custom'
+item before leaving the menu.
+
+N.B. All references in this document to `complete source' mean the
+complete source tree minus any legally encumbered cryptography code.
+
+The current "canned" installations are provided:
+
+Developer: Base ("bin") distribution, man pages, dictionary
+ files, profiling libraries and the complete source tree.
+
+Kern-Developer: As above, but with only kernel sources instead of
+ the complete source tree.
+
+User: The base distribution, man pages, dictionary files and
+ the FreeBSD 1.x, 2.0 and 2.1.x compatibility sets.
+
+Minimal: Only the base distribution.
+
+Everything: The base distribution, man pages, dictionary files,
+ profiling libraries, the FreeBSD 1.x and the FreeBSD 2.0
+ compatibility libraries, the complete source tree,
+ games and your choice of XFree86 distribution components.
+ Note that the cryptography source code is NOT included
+ in this collection. You will need to select that by
+ hand if you're inside the United States.
+
+Custom: Allows you to create or modify your distribution set on
+ a piece-by-piece basis.
+
+Reset: Clear all currently selected distributions.
+
+---
+
+ENCRYPTION SOFTWARE:
+--------------------
+You may notice that certain distributions, like "des" and "krb",
+are marked "NOT FOR EXPORT!" This is because it's illegal to export
+them from the United States (or any other country which considers
+encryption technology to be on its restricted export list). Since
+breaking this law only gets the _originating_ site (US!) in trouble,
+please do not load these distributions from U.S. servers! We don't
+like these restrictions any more than you do, but can't do much about
+it (write your U.S. congress person!).
+
+A number of "foreign" servers do exist for the benefit of
+non-U.S. sites, the official site being:
+
+ ftp://ftp.internat.freebsd.org/pub/FreeBSD
+
+Please get all such export restricted software from there
+if you are outside the U.S., thanks!
diff --git a/usr.sbin/sysinstall/help/drives.hlp b/usr.sbin/sysinstall/help/drives.hlp
new file mode 100644
index 0000000..946a1b2
--- /dev/null
+++ b/usr.sbin/sysinstall/help/drives.hlp
@@ -0,0 +1,92 @@
+Boot Manager Selection:
+-----------------------
+
+If you wish to switch between multiple operating systems on your
+machine, or if you are trying to install FreeBSD on a drive other than
+your 1st drive, then you must install a boot manager. In the case
+where you wish to boot off an alternate drive, it should also be noted
+that you still need to install a boot manager on the FIRST drive!
+Even if you do not intend to create a FreeBSD partition on that drive
+(e.g. it's being wholly used by something else), the boot manager
+still needs to reside on the first disk in order to function as a
+"redirector" for the boot process.
+
+To do this, simply select your 1st drive in the drive selection menu
+and when the partition editor comes up, don't make any changes - just
+(Q)uit. At the boot manager menu which follows, select the first
+option (install a boot manager) and then proceed to setup the other
+drive(s) for FreeBSD as normal.
+
+It should also be noted that "operating systems" such as Windows 95
+will completely overwrite your boot manager without so much as a
+polite "may I please destroy your boot manager?" prompt if you make
+the mistake of installing them second. If this happens to you after
+FreeBSD is already installed, all is not lost! Simply revisit your
+FreeBSD distribution directory and look for a tools/ subdirectory, in
+which you'll find "bootinst.exe" and "boot.bin". To reinstall, simply
+say "bootinst boot.bin" while in the tools/ subdirectory.
+
+
+If you see the boot manager displaying ``F?'' when you try to come up
+for the first time and it refuses to change, no matter how often you
+whap on the function key assigned to FreeBSD, then you have a geometry
+mismatch problem and you should read the next section for important
+information on how to prevent that exact problem from happening!
+
+
+Geometry Translation / Sharing the disk(s) with another OS:
+----------------------------------------------------------
+
+If you are going to actually install some portion of FreeBSD on a
+drive then PLEASE BE VERY CERTAIN that the Geometry reported in the
+Partition Editor is the correct one for your drive and controller
+combination!
+
+IDE drives often have a certain geometry set during the PC BIOS setup,
+or (in the case of larger IDE drives) have their geometry "translated"
+by either the IDE controller or a special boot-sector translation
+utility such as that by OnTrack Systems. In these cases, knowing the
+correct geometry gets even more complicated as it's not something you
+can easily tell by looking at the drive or the PC BIOS setup. The
+best way of verifying that your geometry is being correctly calculated
+in such situations is to boot DOS (from the hard disk, not a floppy!)
+and run the ``pfdisk'' utility provided in the tools/ subdirectory of
+the FreeBSD CDROM or FTP site. It will report the geometry that DOS
+sees, which is generally the correct one.
+
+If you have no DOS partition sharing the disk at all, then you may
+find that you have better luck with Geometry detection if you create a
+very small DOS partition first, before installing FreeBSD. Once
+FreeBSD is installed you can always delete it again if you need the
+space.
+
+It's actually not a bad idea (believe it or not) to have a small
+bootable DOS partition on your FreeBSD machine anyway: Should the
+machine become unstable or exhibit strange behavior at some point in
+the future (which is not uncommon behavior for PC hardware!) you can
+then at least use DOS for installing and running one of the
+commercially available system diagnostic utilities.
+
+IMPORTANT NOTE:
+
+Any root partition you try to boot from must also reside below the
+1024th cylinder. If you're using a translated geometry then this is
+probably not a problem, but if you are using a native disk geometry
+which exceeds 1024 cylinders then you could have a failure to boot if
+you end up installing a root partition (or even just the kernel file
+in a root partition) out past cylinder 1024. If you are trying to
+share your first disk with FreeBSD and another OS which was installed
+previously, you are particularly susceptible to this problem and should
+check your disk addresses very carefully.
+
+If you find that you have insufficient space below cylinder 1024 to
+make a root partition for FreeBSD (and again, this ONLY applies to the
+root partition - once FreeBSD's kernel is loaded, it doesn't care
+about the geometry issues) then you will probably need to install on a
+completely different disk (see the boot manager section above) or
+resize your existing partitions so that both operating systems can
+have boot partitions below cylinder 1024.
+
+You may blame IBM for the limitations of a 10 bit cylinder address.
+"No one will have a disk with more than 1024 cylinders." I'm sure
+someone said.
diff --git a/usr.sbin/sysinstall/help/fixit.hlp b/usr.sbin/sysinstall/help/fixit.hlp
new file mode 100644
index 0000000..2ec1765
--- /dev/null
+++ b/usr.sbin/sysinstall/help/fixit.hlp
@@ -0,0 +1,9 @@
+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).
+
+When you're done, exit the shell to reboot.
diff --git a/usr.sbin/sysinstall/help/html.hlp b/usr.sbin/sysinstall/help/html.hlp
new file mode 100644
index 0000000..f63dd30
--- /dev/null
+++ b/usr.sbin/sysinstall/help/html.hlp
@@ -0,0 +1,19 @@
+In this screen, you can jump to remote or local HTML
+resources such as the FreeBSD Handbook & FreeBSD FAQ
+(Frequently Asked Questions) documents located in:
+
+ file:/usr/share/doc/
+
+If you've loaded the doc distribution.
+
+The default browser package used is lynx (a text based
+browser), which will be automatically loaded from the
+installation media if it is available. You may change
+the selection of browser & browser package to auto-load
+by visiting the Options editor.
+
+In order to visit remote URLs, you naturally must have
+some sort of working Internet connection. If you have not
+yet brought up any network interfaces, please visit
+the ``Networking'' item in the Configuration menu
+before attempting to reference any http://.. style URLs.
diff --git a/usr.sbin/sysinstall/help/media.hlp b/usr.sbin/sysinstall/help/media.hlp
new file mode 100644
index 0000000..54f04f5
--- /dev/null
+++ b/usr.sbin/sysinstall/help/media.hlp
@@ -0,0 +1,50 @@
+You can install from the following types of media:
+
+ CDROM requires one of the following supported CDROM drives:
+ Sony CDU 31/33A
+ Matushita/Panasonic "Sound Blaster" CDROM.
+ Mitsumi FX-001{A-D} (older non-IDE drives).
+ IDE CDROM (only some models - experimental!)
+ 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.
+
+
+ FTP Get the distribution files from an anonymous ftp server
+ (you will be presented with a list). Please note that
+ there are also two ways of invoking FTP in either
+ "Active" and "Passive" mode.
+
+ Active mode is the standard way of fetching files and
+ Passive mode is for use when you're behind a firewall or
+ some other security mechanism that blocks active FTP
+ connections. If you chose "other" in the FTP menu, please
+ also note that all paths are *relative* to the home
+ directory of the user being logged in as. By default, this
+ is the user "ftp" (anonymous ftp) but you may change this
+ in the Options screen.
+
+
+ NFS Get the distribution files from an NFS server somewhere
+ (make sure that permissions on the server allow this!).
+ If this install method hangs on you or refuses to work
+ properly, you may need to set some special options for
+ your NFS server. See the Options screen for more details.
+
+
+ Tape Extract distribution files from tape into a temporary
+ directory and install from there. If the tape was created
+ with blocksize other than 20, you may wish to change this
+ in the Options screen.
diff --git a/usr.sbin/sysinstall/help/network_device.hlp b/usr.sbin/sysinstall/help/network_device.hlp
new file mode 100644
index 0000000..7151e91
--- /dev/null
+++ b/usr.sbin/sysinstall/help/network_device.hlp
@@ -0,0 +1,56 @@
+You can do network installations over 3 types of communications links:
+
+ Serial port: SLIP / PPP
+ Parallel port: PLIP (laplink cable)
+ Ethernet: A standard Ethernet controller (includes some
+ PCMCIA networking cards).
+
+SLIP support is rather primitive and limited primarily to directly
+connected links, such as a serial cable running between a laptop
+computer and another PC. The link must be hard-wired as the SLIP
+installation doesn't currently offer a dialing capability (that
+facility is offered by the PPP utility, which should be used in
+preference to SLIP whenever possible). When you choose the SLIP
+option, you'll be given the option of later editing the slattach
+command before it's run on the serial line. It is expected that
+you'll run slattach (or some equivalent command) on the other end of
+the link at that time and bring up the line. FreeBSD will then
+install itself at serial speeds of up to 115.2K/baud (the recommended
+speed for a hardwired cable).
+
+If you're using a modem then PPP is almost certainly your only choice.
+Make sure that you have your service provider's information handy as
+you'll need to know it fairly early in the installation process. You
+will need to know, at the minimum, your service provider's IP address
+and possibly your own (though you can also leave it blank and allow
+PPP to negotiate it with your ISP if your ISP supports such dynamic
+negotiation). You will also need to know how to use the various "AT
+commands" to dial the ISP with your particular brand of modem as the
+PPP dialer provides only a very simple terminal emulator and has no
+"modem capabilities database".
+
+If a hard-wired connection to another FreeBSD (2.0R or later) machine
+is available, you might also consider installing over a "laplink"
+parallel port cable. The data rate over the parallel port is much
+higher than what is typically possible over a serial line, and speeds
+of up to 50KB/sec are not uncommon.
+
+Finally, for the fastest possible network installation, an Ethernet
+adaptor is always a good choice! FreeBSD supports most common PC
+Ethernet cards, a table of which is provided in the FreeBSD Hardware
+Guide (see the Documentation menu on the boot floppy). If you are
+using one of the supported PCMCIA Ethernet cards, also be sure that
+it's plugged in _before_ the laptop is powered on! FreeBSD does not,
+unfortunately, currently support "hot insertion" of PCMCIA cards.
+
+You will also need to know your IP address on the network, the
+"netmask" value for your address class, and the name of your machine.
+Your system administrator can tell you which values to use for your
+particular network setup. If you will be referring to other hosts by
+name rather than IP address, you'll also need a name server and
+possibly the address of a gateway (if you're using PPP, it's your
+provider's IP address) to use in talking to it. If you do not know
+the answers to all or most of these questions then you should really
+probably talk to your system administrator _first_ before trying this
+type of installation! Choosing the wrong IP address on a busy network
+will NOT make you popular with your systems administrator! :-)
diff --git a/usr.sbin/sysinstall/help/options.hlp b/usr.sbin/sysinstall/help/options.hlp
new file mode 100644
index 0000000..dced216
--- /dev/null
+++ b/usr.sbin/sysinstall/help/options.hlp
@@ -0,0 +1,124 @@
+The following options may be set from this screen. Use the SPACE key
+to toggle an option's value, Q to leave when you're done.
+
+NFS Secure: NFS server talks only on a secure port
+
+ This is most commonly used when talking to Sun workstations, which
+ will not talk NFS over "non privileged" ports.
+
+
+NFS Slow: User is using a slow PC or Ethernet card
+
+ Use this option if you have a slow PC (386) or an Ethernet card
+ with poor performance being "fed" by NFS on a higher-performance
+ workstation. This will throttle the workstation back to prevent
+ the PC from becoming swamped with data.
+
+
+Debugging: Turn on the extra debugging flag
+
+ This turns on a lot of extra noise over on the second screen
+ (ALT-F2 to see it, ALT-F1 to switch back). If your installation
+ should fail for any reason, PLEASE turn this flag on when
+ attempting to reproduce the problem. It will provide a lot of
+ extra debugging at the failure point and may be very helpful to
+ the developers in tracking such problems down!
+
+
+Yes To All: Assume "Yes" answers to all non-critical dialogs
+
+ This flag should be used with caution. It will essentially
+ decide NOT to ask the user about any "boundary" conditions that
+ might not constitute actual errors but may be warnings indicative
+ of other problems. It's most useful to those who are doing unattended
+ installs.
+
+
+FTP username: Specify username and password instead of anonymous.
+
+ By default, the installation attempts to log in as the
+ anonymous user. If you wish to log in as someone else,
+ specify the username and password with this option.
+
+
+Install Root: Specify some directory other than / as your "root".
+
+ This should be left as / unless you have a really good reason to
+ change it. One good reason might be if you were installing to a
+ disk other than your own, as might happen if you needed to prepare a
+ disk for another machine which couldn't load FreeBSD directly
+ for some reason.
+
+ Note: If you set this option, you will only be able to install
+ packages if the bin distribution is also installed (usually
+ the case anyway) since /usr/sbin/pkg_add will otherwise not be
+ found after the chroot() call.
+
+
+Editor: Specify which screen editor to use.
+
+ At various points during the installation it may be necessary
+ to customize some text file, at which point the user will be
+ thrown unceremoniously into a screen editor. A relatively
+ simplistic editor which shows its command set on-screen is
+ selected by default, but UNIX purists may wish to change this
+ setting to /usr/bin/vi
+
+
+Tape Blocksize: Specify block size in 512 byte blocks of tape.
+
+ This defaults to 20 blocks, which should work with most
+ tape drive + tar combinations. It may not allow your particular
+ drive to win any records for speed, however, and the more
+ adventurous among you might try experimenting with larger sizes.
+
+
+Extract Detail: How to show filenames on debug screen as they're extracted.
+
+ While a distribution is being extracted, the default detail level
+ of "high" will show the full file names as they're extracted.
+ If you would prefer a more terse form for this, namely dots, select
+ the "medium" detail level. If you want nothing to be printed
+ on the debugging screen during extraction, select "low".
+
+
+Release Name: Which release to attempt to load from installation media.
+
+ You should only change this option if you're really sure you know
+ what you are doing! This will change the release name used by
+ sysinstall when fetching components of any distributions, and
+ is a useful way of using a more recent installation boot floppy
+ with an older release (say, on CDROM).
+
+
+Browser Package: Which package to load for an HTML browser.
+
+ By default, this is set to lynx but may also be set to any other
+ text capable HTML browser for which a package exists. If you set this
+ to an X based browser, you will not be able to use it if you're running
+ in text mode! :)
+
+
+Browser Exec: Which binary to run for the HTML browser.
+
+ The full pathname to the main executable in Browser Package
+
+
+Media Type: Which media type is being used.
+
+ This is mostly informational and indicates which media type (if any)
+ was last selected in the Media menu. It's also a convenient short-cut
+ to the media menu itself.
+
+
+Package Temp: Where package temporary files should go
+
+ Some packages, like emacs, can use a LOT of temporary space - up to
+ 20 or 30MB. If you are going to configure a small / directory (and
+ hence a small /tmp) then you may wish to set this to point at another
+ location (say, /usr/tmp).
+
+
+Use Defaults: Use default values.
+
+ Reset all options back to their default values.
diff --git a/usr.sbin/sysinstall/help/partition.hlp b/usr.sbin/sysinstall/help/partition.hlp
new file mode 100644
index 0000000..5240d72
--- /dev/null
+++ b/usr.sbin/sysinstall/help/partition.hlp
@@ -0,0 +1,153 @@
+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/sd0s1a'':
+
+ The first three characters represent the drive name. If we had
+ a system with two SCSI drives on it then we'd see /dev/sd0 and
+ /dev/sd1 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 sd0
+ contained two slices, a FreeBSD slice and a DOS slice, that
+ would give us /dev/sd0s1 and /dev/sd0s2 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 sd0:
+
+ Name Mountpoint
+ ---- ----------
+ sd0s1a /
+ sd0s1b <swap space>
+ sd0s1e /usr
+
+ Because of historical convention, there is also a short-cut,
+ or "compatibility slice", that is maintained for easy access
+ to the *first* FreeBSD slice on a disk. This gives some
+ backwards compatibility to utilities that still may not know
+ how to deal with the new slice scheme.
+
+ The compatibility slice names for our filesystem above would
+ also look like:
+
+ Name Mountpoint
+ ---- ----------
+ sd0a /
+ sd0b <swap space>
+ sd0e /usr
+
+ Again, let it be noted: FreeBSD automatically maps the
+ compatibility slice to the first FreeBSD slice it finds
+ (in this case, sd0s1). You may have multiple FreeBSD slices on a
+ drive, but only the first one will be mapped to the compatibility
+ slice!
+
+ The compatibility slice will eventually be phased out, but
+ it is still important right now for several reasons:
+
+ 1. Some programs, as mentioned before, still don't work
+ with the slice paradigm and need time to catch up.
+
+ 2. The FreeBSD boot blocks are unable to look for
+ a root file system in anything but a compatibility
+ slice right now. This means that our root will always
+ show up on "sd0a" in the above scenario, even though
+ it really lives over on sd0s1a and would otherwise be
+ referred to by its full slice name.
+
+Once you understand all this, then the purpose of the label editor
+becomes fairly clear: You're carving up the FreeBSD slices displayed
+at the top of the screen into smaller pieces, which are displayed in
+the middle of the screen, and then assigning FreeBSD file system names
+(mount points) to them.
+
+You can also use the label editor to mount existing partitions/slices
+into your filesystem hierarchy, as is frequently done for DOS FAT
+slices. For FreeBSD partitions, you can also toggle the "newfs" state
+so that the partitions are either (re)created from scratch or simply
+checked and mounted (the contents are preserved).
+
+When you're done, type `Q' to exit.
+
+No actual changes will be made to the disk until you (C)ommit from the
+Install menu or (W)rite directly from this one. You're working with
+what is essentially a copy of the disk label(s), both here and in the
+FDISK Partition Editor, and the actual on-disk labels won't be
+affected by any changes you make until you explicitly say so.
+
diff --git a/usr.sbin/sysinstall/help/register.hlp b/usr.sbin/sysinstall/help/register.hlp
new file mode 100644
index 0000000..a7b397c
--- /dev/null
+++ b/usr.sbin/sysinstall/help/register.hlp
@@ -0,0 +1,76 @@
+This screen allows you to register yourself with the FreeBSD Project's
+user counter & statistics database.
+
+** IT IS VERY IMPORTANT THAT YOU DO THIS! **
+
+Believe me, I hate filling out forms as much as anyone, and most
+people's understandable reaction to a registration form is to say "Eh,
+what's this? They want to send me junk mail and then on top of that
+they expect me to go to *extra* trouble in order to make it easy for
+them?! Forget it!"
+
+This is not that kind of registration, and I strongly urge you to take
+just a few minutes to read this and find out how much the simple act
+of registering can help both you and FreeBSD.
+
+1. It is very much in your best interest, as a FreeBSD user, to stand
+ up and be counted so that various software vendors will begin to
+ take you and your operating system seriously. There are numerous
+ ISVs (Independent Software Vendors) who would be only too happy
+ to port the kinds of applications that many FreeBSD users are
+ currently screaming for (everything from spreadsheets and word
+ processing packages to games) if they only had some idea that it
+ might be worth the trouble. The only way to convince the ISVs that
+ FreeBSD is worth their trouble is to show them how many users
+ we have, and to do that we need your registration! At this time
+ we literally do not know how many users FreeBSD has, and that's
+ hardly helpful when you're trying to convince someone to port
+ software to it.
+
+
+2. We will not send you *anything* you do not ask for. Some people
+ are genuinely interested in new product announcements for FreeBSD
+ or want to hear about security issues & other important advisories
+ as they come up, and for such people we've added registration
+ options for selecting various types of additional material they
+ might be interested in receiving as a side-effect of registration.
+
+ The default behavior is to NOT put the user on any special mailing
+ lists or provide their names in mailing list data sent to
+ (carefully screened) FreeBSD product advertisers - all of that must
+ be specifically requested during the registration.
+
+Most fields in the form are fairly self-explanatory. At the minimum,
+you should enter your first and last name as well as your email
+address so that we can weed obvious duplicates from the counter. You
+will NOT be sent any mail at this address unless you also sign up for
+one of the additional notification services, and it's only used to
+provide us with a way of differentiating "John Smith <smith@foo.org>"
+from "John Smith <jsm@bar.com>" in the simple, no-frills registration
+case. If you do not have an email address, some sort of postal
+address will serve the same purpose.
+
+If you also wish to receive the FreeBSD Newsletter, published and
+distributed free of charge by Walnut Creek CDROM in printed form,
+then you must specify some sort of postal address. Likewise, if you
+elect to receive notification on the email version then you should
+specify a valid Email address. Back-issues of the FreeBSD newsletter
+are available at http://ftp.freebsd.org/pub/FreeBSD/newsletter/
+
+Should you wish to unsubscribe to the FreeBSD Newsletter or otherwise
+de-register yourself at a later time, you can simply send mail to
+register-request@freebsd.org. If you subscribe to the announce
+mailing list (and it's a good idea) then you can modify your
+subscription at any time by sending mail to majordomo@freebsd.org
+
+
+Your cooperation with this new registration service is greatly
+appreciated, and by taking just 5 minutes to fill this out now you
+will be helping us to gather data which will greatly assist FreeBSD in
+firmly establishing a position as a serious UN*X operating system
+contender.
+
+Regards,
+
+ Jordan Hubbard,
+ FreeBSD PR Officer
diff --git a/usr.sbin/sysinstall/help/shortcuts.hlp b/usr.sbin/sysinstall/help/shortcuts.hlp
new file mode 100644
index 0000000..34a9eec
--- /dev/null
+++ b/usr.sbin/sysinstall/help/shortcuts.hlp
@@ -0,0 +1,116 @@
+/stand/sysinstall now supports command-line "shortcuts" which can
+often replace outdated commands, like pkg_manage. Multiple commands
+can be invoked in sequence, and variables may be set on-the-fly to
+customize the installation program's behavior in various ways.
+
+Syntax:
+
+/stand/sysinstall [var=value ..] [command ..]
+
+Where "var" can be one or more of:
+
+blanktime Screen blank time setting in seconds
+bootManager Select boot manager: booteasy, standard or none
+browserBinary Which doc browser to use (default: lynx)
+browserPackage Which package to get browser from (default: lynx)
+cpioVerbose How verbose to be with cpio: high, medium or low
+debug Extra debugging?
+disk Which disk to operate on (wd0, sd0, etc).
+domainname Domain name
+editor Which screen editor to use
+ifconfig_<iface> For each <iface> in network_devices
+ftpDirectory Root of the FreeBSD distribution tree on FTP server
+ftpOnError Set to retry or abort
+ftpPass Which password to use when logging into FTP server
+ftp Which FTP site/dir to use (URL ftp://site/dir/..)
+ftpPort Which FTP port to use (default: 21)
+ftpRetryCount How many times to retry a fetch operation
+ftpUser Which username to use when logging into FTP server
+ftpHost Which FTP hostname to use (overrides ftp variable)
+gated Use gated instead of routed
+defaultrouter IP address of default route
+geometry Geometry to use for selected disk ("cyl/hd/sec")
+hostname Fully qualified domain name for host.
+network_interfaces Which network interfaces to configure
+ipaddr IP address for this host's primary interface
+nameserver IP address of name server
+netmask Netmask for this host's primary interface
+nfs Full host:/path/ specification to NFS media
+nfsHost Host portion of nfs path
+nfsSecure Use NFS secure mount (-P flag)
+nfs_server Configure this machine as an NFS server
+noConfirm Don't ask for confirmation on non-fatal errors
+ntpDate Which ntp clock synchronization server to use
+pcnfsd Install the PCNFSD package
+ports Path to the ports collection
+releaseName Which FreeBSD release to install
+rootSize Size of the root partition to create for Auto
+routedflags Which flags to pass to routed, if enabled
+serialSpeed How fast to run a SLIP/PPP connection
+slowEthernetCard PC ethernet card is uncommonly slow
+swapSize Size of the swap partition to create for Auto
+tapeBlocksize Tape size in blocks
+ufs Full path to UFS media directory
+usrSize Size of the /usr partition to create for Auto
+varSize Size of the /var partition to create for Auto
+
+And "command" can be one or more of:
+
+addUser Add a new user to the system
+addGroup Add a new group to the system
+configAnonFTP Configure system for anonymous FTP
+configApache Configure and install the Apache WEB server
+configGated Configure and install gated
+configNFSServer Configure host as an NFS server
+configSamba Configure and install Samba
+configPackages Browse / install packages
+diskPartitionEditor Partition a new or existing disk
+diskPartitionWrite Write out any changed partition information
+diskLabelEditor Label/Newfs/Mount new or existing filesystems
+diskLabelCommit Write out any changed label information
+distReset Reset distribution information
+distSetDeveloper Select developer distribution
+distSetXDeveloper Select X developer distribution
+distSetKernDeveloper Select kernel developer distribution
+distSetUser Select user distribution
+distSetXUser Select X user distribution
+distSetMinimum Select minimal distribution
+distSetEverything Select all distributions
+distSetDES Select DES sub-distributions
+distSetSrc Select source sub-distributions
+distSetXF86 Select XFree86 sub-distributions
+distExtractAll Extract all selected distributions
+docBrowser Browse documentation
+installCommit Commit any pending installation operations
+installExpress Express installation
+installUpgrade Upgrade installation
+installFixup Go into "fixit" mode
+mediaSetCDROM Select CDROM media
+mediaSetFloppy Select floppy media
+mediaSetDOS Select DOS media
+mediaSetTape Select tape media
+mediaSetFTP Select FTP media
+mediaSetFTPPassive Select FTP media in passive mode
+mediaSetUFS Select UFS media
+mediaSetNFS Select NFS media
+mediaSetFtpUserPass Prompt for FTP username and password
+mediaSetCPIOVerbosity Prompt for CPIO verbosity
+mediaGetType Prompt for media type
+optionsEditor Go to options editor
+register Go to registration editor.
+
+Examples:
+
+/stand/sysinstall mediaSetFTP configPackages
+
+Selects an FTP site and then goes to the package configuration menu.
+
+
+/stand/sysinstall disk=sd0 diskPartitionEditor
+
+Invokes the disk partition editor on disk sd0.
+
+
+If /stand/sysinstall is linked to another filename, say
+`/usr/local/bin/configPackages', then the basename will be used
+as an implicit command name.
diff --git a/usr.sbin/sysinstall/help/slice.hlp b/usr.sbin/sysinstall/help/slice.hlp
new file mode 100644
index 0000000..33280a4
--- /dev/null
+++ b/usr.sbin/sysinstall/help/slice.hlp
@@ -0,0 +1,59 @@
+This is the Main Slice (``FDISK'' or PC-style Partition) Editor.
+
+Possible commands are printed at the bottom and the Master Boot Record
+contents are shown at the top. You can move up and down with the
+arrow keys and (C)reate a new slice whenever the highlighted
+selection bar is over a slice whose type is marked as "unused."
+
+You are expected to leave this screen with at least one slice
+marked "FreeBSD." Note that unlike Linux, you don't need to create
+multiple FreeBSD FDISK partition entries for different things like
+swap, file systems, etc. The usual convention is to create ONE
+FreeBSD slice (FDISK partition) per drive and then subsection this slice
+into swap and file systems with the Label editor.
+
+No actual changes will be made to the disk until you (C)ommit from the
+Install menu or use the (W)rite option here! You're working with what
+is essentially a copy of the disk label(s), both here and in the Label
+Editor.
+
+If you want to use the entire disk for FreeBSD, type `A'. You'll be
+asked whether or not you wish to keep the disk (potentially) compatible
+with other operating systems, i.e. the information in the FDISK table
+should be kept valid. If you select the default of `Yes', slices will be
+aligned to fictitious cylinder boundaries and space will be reserved
+in front of the FreeBSD slice for a [future] possible boot manager.
+
+For the truly dedicated disk case, you can select `No' at the
+compatibility prompt. In that case, all BIOS geometry considerations
+will no longer be in effect and you can safely ignore any
+``The detected geometry is invalid'' warning messages you may later
+see. It is also not necessary in this case to set a slice bootable
+or install an MBR boot manager as both things are then irrelevant.
+
+The FreeBSD slice will start at absolute sector 0 of the disk (so that
+FreeBSD's disk label is identical to the Master Boot Record) and
+extend to the very last sector of the disk medium. Needless to say,
+such a disk cannot have any sort of a boot manager, `disk manager',
+or anything else that has to interact with the BIOS. This option is
+therefore only considered safe for SCSI disks and most IDE disks and
+is primarily intended for people who are going to set up a dedicated
+FreeBSD server or workstation, not a typical `home PC'.
+
+The flags field has the following legend:
+
+ '=' -- This slice is properly aligned.
+ '>' -- This slice doesn't end before cylinder 1024
+ 'R' -- This slice contains the root (/) filesystem
+ 'B' -- Slice employs BAD144 bad-spot handling
+ 'C' -- This is the FreeBSD 2.0-compatibility slice (default)
+ 'A' -- This slice is marked active.
+
+If you select a slice for Bad144 handling, it will be scanned
+for bad blocks before any new filesystems are made on it.
+
+If no slice is marked Active, you will need to either install
+a Boot Manager (the option for which will be presented later in the
+installation) or set one Active before leaving this screen.
+
+To leave the slice editor, type `Q'.
diff --git a/usr.sbin/sysinstall/help/tcp.hlp b/usr.sbin/sysinstall/help/tcp.hlp
new file mode 100644
index 0000000..fdee91c
--- /dev/null
+++ b/usr.sbin/sysinstall/help/tcp.hlp
@@ -0,0 +1,30 @@
+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).
+
+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..d9274d5
--- /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.
+
+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.
+
+Once the system is fully installed and running multi-user, you will
+find that you have multiple "virtual consoles" which you can use to in
+order to have several active sessions at once. Use ALT-F<n> to switch
+between screens, where `F<n>' is the function key corresponding to the
+screen you wish to see. By default, the system comes with 3 virtual
+consoles enabled - you can enable more by editing the /etc/ttys file
+and turning the "off" field to "on" in the relevant vty entries (up to
+12).
diff --git a/usr.sbin/sysinstall/help/usermgmt.hlp b/usr.sbin/sysinstall/help/usermgmt.hlp
new file mode 100644
index 0000000..8a2664d
--- /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 (8 characters) alphanumeric ID the user
+must enter when logging into the system. It's often the initial
+letters of the user's name, and commonly used in lower case. It's
+also the local mail name for this user (though it's possible to also
+setup more descriptive mail alias names later).
+
+The user's login group determines which group access rights the user
+will initially get when logging in. If an additional list of groups is
+provided where the user will become a member of, (s)he will also be
+able to access files of those groups later without providing any
+additional password etc. Except for the "wheel" case mentioned below,
+the additional group membership list should normally not contain the
+login group again.
+
+The user's password can also be set here, and should be chosen with
+care - 6 or more characters, intermixing punctuation and numerics, and
+*not* a word from the dictionary or related to the username is a good
+password choice.
+
+Some of the system's groups have a special meaning. In particular,
+members of group "wheel" are the only people who are later allowed to
+become superuser using the command su(1). So if you're going to add a
+new user who should later perform administrative tasks, don't forget
+to add him to this group! (Well, ``he'' will most likely be yourself
+in the very first place. :)
+
+Also, members of group "operator" will by default get permissions for
+minor administrative operations, like performing system backups, or
+shutting down the system -- without first becoming superuser! So,
+take care with adding people to this group.
+
+The ``full name'' field serves as a comment only. It is also used by
+mail front ends to determine the real name of the user, hence you
+should actually fill in the first and last name of this user. By
+convention, this field can be divided into comma-separated subfields,
+where the office location, the work phone number, and the home phone
+number follow the full name of the user.
+
+The home directory is the directory in the filesystem where the user
+is being logged into, and where his personalized setup files (``dot
+files'', since they usually begin with a `.' and are not displayed by
+the ls(1) command by default) will be looked up. It is often created
+under /usr/home/ or /home/.
+
+Finally, the shell is the user's initial command interpreter. The
+default shell is /bin/sh, some users prefer the more historic
+/bin/csh. Other, often more user-friendly and comfortable shells can
+be found in the ports and packages collection.
diff --git a/usr.sbin/sysinstall/index.c b/usr.sbin/sysinstall/index.c
new file mode 100644
index 0000000..b200541
--- /dev/null
+++ b/usr.sbin/sysinstall/index.c
@@ -0,0 +1,599 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last program in the `sysinstall' line - the next
+ * generation being essentially a complete rewrite.
+ *
+ * $Id: index.c,v 1.50 1997/05/05 08:38:12 jkh Exp $
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ncurses.h>
+#include <dialog.h>
+#include "sysinstall.h"
+
+/* Macros and magic values */
+#define MAX_MENU 12
+#define _MAX_DESC 55
+
+static int index_extract_one(Device *dev, PkgNodePtr top, PkgNodePtr who, Boolean depended);
+
+/* Smarter strdup */
+inline char *
+_strdup(char *ptr)
+{
+ return ptr ? strdup(ptr) : NULL;
+}
+
+static char *descrs[] = {
+ "Package Selection", "To mark a package, move to it and press SPACE. If the package is\n"
+ "already marked, it will be unmarked or deleted (if installed).\n"
+ "To search for a package by name, press ESC. To select a category,\n"
+ "press RETURN. NOTE: The All category selection creates a very large\n"
+ "submenu. If you select it, please be patient while it comes up.",
+ "Package Targets", "These are the packages you've selected for extraction.\n\n"
+ "If you're sure of these choices, select OK.\n"
+ "If not, select Cancel to go back to the package selection menu.\n",
+ "All", "All available packages in all categories.",
+ "applications", "User application software.",
+ "astro", "Applications related to astronomy.",
+ "archivers", "Utilities for archiving and unarchiving data.",
+ "audio", "Audio utilities - most require a supported sound card.",
+ "benchmarks", "Utilities for measuring system performance.",
+ "benchmarking", "Utilities for measuring system performance.",
+ "cad", "Computer Aided Design utilities.",
+ "chinese", "Ported software for the Chinese market.",
+ "comms", "Communications utilities.",
+ "converters", "Format conversion utilities..",
+ "databases", "Database software.",
+ "devel", "Software development utilities and libraries.",
+ "development", "Software development utilities and libraries.",
+ "documentation", "Document preparation utilities.",
+ "editors", "Common text editors.",
+ "emulation", "Utilities for emulating other OS types.",
+ "emulators", "Utilities for emulating other OS types.",
+ "games", "Various and sundry amusements.",
+ "graphics", "Graphics libraries and utilities.",
+ "japanese", "Ported software for the Japanese market.",
+ "korean", "Ported software for the Korean market.",
+ "lang", "Computer languages.",
+ "languages", "Computer languages.",
+ "libraries", "Software development libraries.",
+ "mail", "Electronic mail packages and utilities.",
+ "math", "Mathematical computation software.",
+ "mbone", "Applications and utilities for the mbone.",
+ "misc", "Miscellaneous utilities.",
+ "net", "Networking utilities.",
+ "networking", "Networking utilities.",
+ "news", "USENET News support software.",
+ "numeric", "Mathematical computation software.",
+ "orphans", "Packages without a home elsewhere.",
+ "perl5", "Utilities/modules for the PERL5 language..",
+ "plan9", "Software from the plan9 Operating System.",
+ "print", "Utilities for dealing with printing.",
+ "printing", "Utilities for dealing with printing.",
+ "programming", "Software development utilities and libraries.",
+ "russian", "Ported software for the Russian market.",
+ "security", "System security software.",
+ "shells", "Various shells (tcsh, bash, etc).",
+ "sysutils", "Various system utilities.",
+ "textproc", "Text processing/search utilities.",
+ "tcl75", "TCL v7.5 and packages which depend on it.",
+ "tcl76", "TCL v7.6 and packages which depend on it.",
+ "tk41", "Tk4.1 and packages which depend on it.",
+ "tk42", "Tk4.2 and packages which depend on it.",
+ "tk80", "Tk8.0 and packages which depend on it.",
+ "troff", "TROFF Text formatting utilities.",
+ "utils", "Various user utilities.",
+ "utilities", "Various user utilities.",
+ "vietnamese", "Ported software for the Vietnamese market.",
+ "www", "WEB utilities (browers, HTTP servers, etc).",
+ "x11", "X Window System based utilities.",
+ NULL, NULL,
+};
+
+static char *
+fetch_desc(char *name)
+{
+ int i;
+
+ for (i = 0; descrs[i]; i += 2) {
+ if (!strcmp(descrs[i], name))
+ return descrs[i + 1];
+ }
+ return "No description provided";
+}
+
+static PkgNodePtr
+new_pkg_node(char *name, node_type type)
+{
+ PkgNodePtr tmp = safe_malloc(sizeof(PkgNode));
+
+ tmp->name = _strdup(name);
+ tmp->type = type;
+ return tmp;
+}
+
+static char *
+strip(char *buf)
+{
+ int i;
+
+ for (i = 0; buf[i]; i++)
+ if (buf[i] == '\t' || buf[i] == '\n')
+ buf[i] = ' ';
+ return buf;
+}
+
+static IndexEntryPtr
+new_index(char *name, char *pathto, char *prefix, char *comment, char *descr, char *maint, char *deps)
+{
+ IndexEntryPtr tmp = safe_malloc(sizeof(IndexEntry));
+
+ tmp->name = _strdup(name);
+ tmp->path = _strdup(pathto);
+ tmp->prefix = _strdup(prefix);
+ tmp->comment = _strdup(comment);
+ tmp->descrfile = strip(_strdup(descr));
+ tmp->maintainer = _strdup(maint);
+ tmp->deps = _strdup(deps);
+ return tmp;
+}
+
+static void
+index_register(PkgNodePtr top, char *where, IndexEntryPtr ptr)
+{
+ PkgNodePtr p, q;
+
+ for (q = NULL, p = top->kids; p; p = p->next) {
+ if (!strcmp(p->name, where)) {
+ q = p;
+ break;
+ }
+ }
+ if (!p) {
+ /* Add new category */
+ q = new_pkg_node(where, PLACE);
+ q->desc = fetch_desc(where);
+ q->next = top->kids;
+ top->kids = q;
+ }
+ p = new_pkg_node(ptr->name, PACKAGE);
+ p->desc = ptr->comment;
+ p->data = ptr;
+ p->next = q->kids;
+ q->kids = p;
+}
+
+static int
+copy_to_sep(char *to, char *from, int sep)
+{
+ char *tok;
+
+ tok = strchr(from, sep);
+ if (!tok) {
+ *to = '\0';
+ return 0;
+ }
+ *tok = '\0';
+ strcpy(to, from);
+ return tok + 1 - from;
+}
+
+static int
+readline(FILE *fp, char *buf, int max)
+{
+ int rv, i = 0;
+ char ch;
+
+ while ((rv = fread(&ch, 1, 1, fp)) == 1 && ch != '\n' && i < max)
+ buf[i++] = ch;
+ if (i < max)
+ buf[i] = '\0';
+ return rv;
+}
+
+int
+index_parse(FILE *fp, char *name, char *pathto, char *prefix, char *comment, char *descr, char *maint, char *cats, char *rdeps)
+{
+ char line[1024];
+ char junk[256];
+ char *cp;
+ int i;
+
+ i = readline(fp, line, 1024);
+ if (i <= 0)
+ return EOF;
+ cp = line;
+ cp += copy_to_sep(name, cp, '|');
+ cp += copy_to_sep(pathto, cp, '|');
+ cp += copy_to_sep(prefix, cp, '|');
+ cp += copy_to_sep(comment, cp, '|');
+ cp += copy_to_sep(descr, cp, '|');
+ cp += copy_to_sep(maint, cp, '|');
+ cp += copy_to_sep(cats, cp, '|');
+ cp += copy_to_sep(junk, cp, '|'); /* build deps - not used */
+ if (index(cp, '|'))
+ copy_to_sep(rdeps, cp, '|');
+ else
+ strncpy(rdeps, cp, 510);
+ return 0;
+}
+
+int
+index_read(FILE *fp, PkgNodePtr papa)
+{
+ char name[127], pathto[255], prefix[255], comment[255], descr[127], maint[127], cats[511], deps[511];
+
+ while (index_parse(fp, name, pathto, prefix, comment, descr, maint, cats, deps) != EOF) {
+ char *cp, *cp2, tmp[511];
+ IndexEntryPtr idx;
+
+ idx = new_index(name, pathto, prefix, comment, descr, maint, deps);
+ /* For now, we only add things to menus if they're in categories. Keywords are ignored */
+ for (cp = strcpy(tmp, cats); (cp2 = strchr(cp, ' ')) != NULL; cp = cp2 + 1) {
+ *cp2 = '\0';
+ index_register(papa, cp, idx);
+ }
+ index_register(papa, cp, idx);
+
+ /* Add to special "All" category */
+ index_register(papa, "All", idx);
+ }
+ return 0;
+}
+
+void
+index_init(PkgNodePtr top, PkgNodePtr plist)
+{
+ if (top) {
+ top->next = top->kids = NULL;
+ top->name = "Package Selection";
+ top->type = PLACE;
+ top->desc = fetch_desc(top->name);
+ top->data = NULL;
+ }
+ if (plist) {
+ plist->next = plist->kids = NULL;
+ plist->name = "Package Targets";
+ plist->type = PLACE;
+ plist->desc = fetch_desc(plist->name);
+ plist->data = NULL;
+ }
+}
+
+void
+index_print(PkgNodePtr top, int level)
+{
+ int i;
+
+ while (top) {
+ for (i = 0; i < level; i++) putchar('\t');
+ printf("name [%s]: %s\n", top->type == PLACE ? "place" : "package", top->name);
+ for (i = 0; i < level; i++) putchar('\t');
+ printf("desc: %s\n", top->desc);
+ if (top->kids)
+ index_print(top->kids, level + 1);
+ top = top->next;
+ }
+}
+
+/* Swap one node for another */
+static void
+swap_nodes(PkgNodePtr a, PkgNodePtr b)
+{
+ PkgNode tmp;
+
+ tmp = *a;
+ *a = *b;
+ a->next = tmp.next;
+ tmp.next = b->next;
+ *b = tmp;
+}
+
+/* Use a disgustingly simplistic bubble sort to put our lists in order */
+void
+index_sort(PkgNodePtr top)
+{
+ PkgNodePtr p, q;
+
+ /* Sort everything at the top level */
+ for (p = top->kids; p; p = p->next) {
+ for (q = top->kids; q; q = q->next) {
+ if (q->next && strcmp(q->name, q->next->name) > 0)
+ swap_nodes(q, q->next);
+ }
+ }
+
+ /* Now sub-sort everything n levels down */
+
+ for (p = top->kids; p; p = p->next) {
+ if (p->kids)
+ index_sort(p);
+ }
+}
+
+/* Delete an entry out of the list it's in (only the plist, at present) */
+void
+index_delete(PkgNodePtr n)
+{
+ if (n->next) {
+ PkgNodePtr p = n->next;
+
+ *n = *(n->next);
+ safe_free(p);
+ }
+ else /* Kludgy end sentinal */
+ n->name = NULL;
+}
+
+/*
+ * Search for a given node by name, returning the category in if
+ * tp is non-NULL.
+ */
+PkgNodePtr
+index_search(PkgNodePtr top, char *str, PkgNodePtr *tp)
+{
+ PkgNodePtr p, sp;
+
+ for (p = top->kids; p && p->name; p = p->next) {
+ /* Subtract out the All category from searches */
+ if (!strcmp(p->name, "All"))
+ continue;
+
+ /* If tp == NULL, we're looking for an exact package match */
+ if (!tp && !strncmp(p->name, str, strlen(str)))
+ return p;
+
+ /* If tp, we're looking for both a package and a pointer to the place it's in */
+ if (tp && !strncmp(p->name, str, strlen(str))) {
+ *tp = top;
+ return p;
+ }
+
+ /* The usual recursion-out-of-laziness ploy */
+ if (p->kids)
+ if ((sp = index_search(p, str, tp)) != NULL)
+ return sp;
+ }
+ if (p && !p->name)
+ p = NULL;
+ return p;
+}
+
+int
+pkg_checked(dialogMenuItem *self)
+{
+ PkgNodePtr kp = self->data, plist = (PkgNodePtr)self->aux;
+ int i;
+
+ i = index_search(plist, kp->name, NULL) ? TRUE : FALSE;
+ if (kp->type == PACKAGE && plist)
+ return i || package_exists(kp->name);
+ else
+ return FALSE;
+}
+
+int
+pkg_fire(dialogMenuItem *self)
+{
+ int ret;
+ PkgNodePtr sp, kp = self->data, plist = (PkgNodePtr)self->aux;
+
+ if (!plist)
+ ret = DITEM_FAILURE;
+ else if (kp->type == PACKAGE) {
+ sp = index_search(plist, kp->name, NULL);
+ /* Not already selected? */
+ if (!sp) {
+ if (!package_exists(kp->name)) {
+ PkgNodePtr np = (PkgNodePtr)safe_malloc(sizeof(PkgNode));
+
+ *np = *kp;
+ np->next = plist->kids;
+ plist->kids = np;
+ msgInfo("Added %s to selection list", kp->name);
+ }
+ else {
+ WINDOW *save = savescr();
+
+ if (!msgYesNo("Do you really want to delete %s from the system?", kp->name))
+ if (vsystem("pkg_delete %s %s", isDebug() ? "-v" : "", kp->name))
+ msgConfirm("Warning: pkg_delete of %s failed.\n Check debug output for details.", kp->name);
+ restorescr(save);
+ }
+ }
+ else {
+ msgInfo("Removed %s from selection list", kp->name);
+ index_delete(sp);
+ }
+ ret = DITEM_SUCCESS;
+ }
+ else { /* Not a package, must be a directory */
+ int p, s;
+
+ p = s = 0;
+ index_menu(kp, plist, &p, &s);
+ ret = DITEM_SUCCESS | DITEM_CONTINUE;
+ }
+ return ret;
+}
+
+void
+pkg_selected(dialogMenuItem *self, int is_selected)
+{
+ PkgNodePtr kp = self->data;
+
+ if (!is_selected || kp->type != PACKAGE)
+ return;
+ msgInfo(kp->desc);
+}
+
+int
+index_menu(PkgNodePtr top, PkgNodePtr plist, int *pos, int *scroll)
+{
+ int n, rval, maxname;
+ int curr, max;
+ PkgNodePtr kp;
+ dialogMenuItem *nitems;
+ Boolean hasPackages;
+ WINDOW *w;
+
+ hasPackages = FALSE;
+ nitems = NULL;
+
+ w = savescr();
+ n = maxname = 0;
+ /* Figure out if this menu is full of "leaves" or "branches" */
+ for (kp = top->kids; kp && kp->name; kp = kp->next) {
+ int len;
+
+ ++n;
+ if (kp->type == PACKAGE && plist) {
+ hasPackages = TRUE;
+ if ((len = strlen(kp->name)) > maxname)
+ maxname = len;
+ }
+ }
+ if (!n && plist) {
+ msgConfirm("The %s menu is empty.", top->name);
+ restorescr(w);
+ return DITEM_LEAVE_MENU;
+ }
+
+ while (1) {
+ n = 0;
+ curr = max = 0;
+ use_helpline(NULL);
+ use_helpfile(NULL);
+ kp = top->kids;
+ if (!hasPackages && plist) {
+ nitems = item_add(nitems, "OK", NULL, NULL, NULL, NULL, NULL, 0, &curr, &max);
+ nitems = item_add(nitems, "Install", NULL, NULL, NULL, NULL, NULL, 0, &curr, &max);
+ }
+ while (kp && kp->name) {
+ char buf[256];
+ IndexEntryPtr ie = kp->data;
+
+ /* Brutally adjust description to fit in menu */
+ if (kp->type == PACKAGE)
+ snprintf(buf, sizeof buf, "[%s]", ie->path ? ie->path : "External vendor");
+ else
+ SAFE_STRCPY(buf, kp->desc);
+ if (strlen(buf) > (_MAX_DESC - maxname))
+ buf[_MAX_DESC - maxname] = '\0';
+ nitems = item_add(nitems, kp->name, buf, pkg_checked, pkg_fire, pkg_selected, kp, (int)plist, &curr, &max);
+ ++n;
+ kp = kp->next;
+ }
+ /* NULL delimiter so item_free() knows when to stop later */
+ nitems = item_add(nitems, NULL, NULL, NULL, NULL, NULL, NULL, 0, &curr, &max);
+
+recycle:
+ dialog_clear_norefresh();
+ if (hasPackages)
+ rval = dialog_checklist(top->name, top->desc, -1, -1, n > MAX_MENU ? MAX_MENU : n, -n, nitems, NULL);
+ else
+ rval = dialog_menu(top->name, top->desc, -1, -1, n > MAX_MENU ? MAX_MENU : n, -n, nitems + (plist ? 2 : 0), (char *)plist, pos, scroll);
+ if (rval == -1 && plist) {
+ static char *cp;
+ PkgNodePtr menu;
+
+ /* Search */
+ if ((cp = msgGetInput(cp, "Search by package name. Please enter search string:")) != NULL) {
+ PkgNodePtr p = index_search(top, cp, &menu);
+
+ if (p) {
+ int pos, scroll;
+
+ /* These need to be set to point at the found item, actually. Hmmm! */
+ pos = scroll = 0;
+ index_menu(menu, plist, &pos, &scroll);
+ }
+ else
+ msgConfirm("Search string: %s yielded no hits.", cp);
+ }
+ goto recycle;
+ }
+ items_free(nitems, &curr, &max);
+ restorescr(w);
+ return rval ? DITEM_FAILURE : DITEM_SUCCESS;
+ }
+}
+
+int
+index_extract(Device *dev, PkgNodePtr top, PkgNodePtr plist)
+{
+ PkgNodePtr tmp;
+ int status = DITEM_SUCCESS;
+
+ for (tmp = plist->kids; tmp && tmp->name; tmp = tmp->next)
+ status = index_extract_one(dev, top, tmp, FALSE);
+ return status;
+}
+
+static int
+index_extract_one(Device *dev, PkgNodePtr top, PkgNodePtr who, Boolean depended)
+{
+ int status = DITEM_SUCCESS;
+ PkgNodePtr tmp2;
+ IndexEntryPtr id = who->data;
+
+ if (id && id->deps && strlen(id->deps)) {
+ char t[1024], *cp, *cp2;
+
+ SAFE_STRCPY(t, id->deps);
+ cp = t;
+ while (cp && DITEM_STATUS(status) == DITEM_SUCCESS) {
+ if ((cp2 = index(cp, ' ')) != NULL)
+ *cp2 = '\0';
+ if ((tmp2 = index_search(top, cp, NULL)) != NULL) {
+ status = index_extract_one(dev, top, tmp2, TRUE);
+ if (DITEM_STATUS(status) != DITEM_SUCCESS) {
+ if (variable_get(VAR_NO_CONFIRM))
+ msgNotify("Loading of dependant package %s failed", cp);
+ else
+ msgConfirm("Loading of dependant package %s failed", cp);
+ }
+ }
+ 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);
+ return status;
+}
diff --git a/usr.sbin/sysinstall/install.c b/usr.sbin/sysinstall/install.c
new file mode 100644
index 0000000..6ae04ff
--- /dev/null
+++ b/usr.sbin/sysinstall/install.c
@@ -0,0 +1,1138 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last program in the `sysinstall' line - the next
+ * generation being essentially a complete rewrite.
+ *
+ * $Id: install.c,v 1.201 1997/10/12 16:21:13 jkh Exp $
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include "sysinstall.h"
+#include "uc_main.h"
+#include <ctype.h>
+#include <sys/disklabel.h>
+#include <sys/errno.h>
+#include <sys/ioctl.h>
+#include <sys/fcntl.h>
+#include <sys/wait.h>
+#include <sys/param.h>
+#define MSDOSFS
+#include <sys/mount.h>
+#include <ufs/ufs/ufsmount.h>
+#include <msdosfs/msdosfsmount.h>
+#undef MSDOSFS
+#include <sys/stat.h>
+#include <unistd.h>
+
+static void create_termcap(void);
+static void fixit_common(void);
+#ifdef SAVE_USERCONFIG
+static void save_userconfig_to_kernel(char *);
+#endif
+
+#define TERMCAP_FILE "/usr/share/misc/termcap"
+
+static void installConfigure(void);
+
+Boolean
+checkLabels(Boolean whinge, Chunk **rdev, Chunk **sdev, Chunk **udev, Chunk **vdev)
+{
+ Device **devs;
+ Boolean status;
+ Disk *disk;
+ Chunk *c1, *c2, *rootdev, *swapdev, *usrdev, *vardev;
+ int i;
+
+ /* Don't allow whinging if noWarn is set */
+ if (variable_get(VAR_NO_WARN))
+ whinge = FALSE;
+
+ status = TRUE;
+ *rdev = *sdev = *udev = *vdev = rootdev = swapdev = usrdev = vardev = NULL;
+
+ /* We don't need to worry about root/usr/swap if we're already multiuser */
+ if (!RunningAsInit)
+ return status;
+
+ devs = deviceFind(NULL, DEVICE_TYPE_DISK);
+ /* First verify that we have a root device */
+ for (i = 0; devs[i]; i++) {
+ if (!devs[i]->enabled)
+ continue;
+ disk = (Disk *)devs[i]->private;
+ msgDebug("Scanning disk %s for root filesystem\n", disk->name);
+ if (!disk->chunks)
+ msgFatal("No chunk list found for %s!", disk->name);
+ for (c1 = disk->chunks->part; c1; c1 = c1->next) {
+ if (c1->type == freebsd) {
+ for (c2 = c1->part; c2; c2 = c2->next) {
+ if (c2->type == part && c2->subtype != FS_SWAP && c2->private_data) {
+ if (c2->flags & CHUNK_IS_ROOT) {
+ if (rootdev) {
+ if (whinge)
+ msgConfirm("WARNING: You have more than one root device set?!\n"
+ "Using the first one found.");
+ continue;
+ }
+ else {
+ rootdev = c2;
+ if (isDebug())
+ msgDebug("Found rootdev at %s!\n", rootdev->name);
+ }
+ }
+ else if (!strcmp(((PartInfo *)c2->private_data)->mountpoint, "/usr")) {
+ if (usrdev) {
+ if (whinge)
+ msgConfirm("WARNING: You have more than one /usr filesystem.\n"
+ "Using the first one found.");
+ continue;
+ }
+ else {
+ usrdev = c2;
+ if (isDebug())
+ msgDebug("Found usrdev at %s!\n", usrdev->name);
+ }
+ }
+ else if (!strcmp(((PartInfo *)c2->private_data)->mountpoint, "/var")) {
+ if (vardev) {
+ if (whinge)
+ msgConfirm("WARNING: You have more than one /var filesystem.\n"
+ "Using the first one found.");
+ continue;
+ }
+ else {
+ vardev = c2;
+ if (isDebug())
+ msgDebug("Found vardev at %s!\n", vardev->name);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /* Now check for swap devices */
+ for (i = 0; devs[i]; i++) {
+ if (!devs[i]->enabled)
+ continue;
+ disk = (Disk *)devs[i]->private;
+ msgDebug("Scanning disk %s for swap partitions\n", disk->name);
+ if (!disk->chunks)
+ msgFatal("No chunk list found for %s!", disk->name);
+ for (c1 = disk->chunks->part; c1; c1 = c1->next) {
+ if (c1->type == freebsd) {
+ for (c2 = c1->part; c2; c2 = c2->next) {
+ if (c2->type == part && c2->subtype == FS_SWAP && !swapdev) {
+ swapdev = c2;
+ if (isDebug())
+ msgDebug("Found swapdev at %s!\n", swapdev->name);
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ /* Copy our values over */
+ *rdev = rootdev;
+ *sdev = swapdev;
+ *udev = usrdev;
+ *vdev = vardev;
+
+ if (!rootdev && whinge) {
+ msgConfirm("No root device found - you must label a partition as /\n"
+ "in the label editor.");
+ status = FALSE;
+ }
+ if (!swapdev && whinge) {
+ msgConfirm("No swap devices found - you must create at least one\n"
+ "swap partition.");
+ status = FALSE;
+ }
+ if (!usrdev && whinge) {
+ msgConfirm("WARNING: No /usr filesystem found. This is not technically\n"
+ "an error if your root filesystem is big enough (or you later\n"
+ "intend to mount your /usr filesystem over NFS), but it may otherwise\n"
+ "cause you trouble if you're not exactly sure what you are doing!");
+ }
+ if (!vardev && whinge && variable_cmp(SYSTEM_STATE, "upgrade")) {
+ msgConfirm("WARNING: No /var filesystem found. This is not technically\n"
+ "an error if your root filesystem is big enough (or you later\n"
+ "intend to link /var to someplace else), but it may otherwise\n"
+ "cause your root filesystem to fill up if you receive lots of mail\n"
+ "or edit large temporary files.");
+ }
+ return status;
+}
+
+static int
+installInitial(void)
+{
+ static Boolean alreadyDone = FALSE;
+
+ 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");
+
+ /* If we refuse to proceed, bail. */
+ dialog_clear_norefresh();
+ if (msgYesNo("Last Chance! Are you SURE you want continue the installation?\n\n"
+ "If you're running this on a disk with data you wish to save\n"
+ "then WE STRONGLY ENCOURAGE YOU TO MAKE PROPER BACKUPS before\n"
+ "proceeding!\n\n"
+ "We can take no responsibility for lost disk contents!") != 0)
+ return DITEM_FAILURE | DITEM_RESTORE;
+
+ if (DITEM_STATUS(diskLabelCommit(NULL)) != DITEM_SUCCESS) {
+ msgConfirm("Couldn't make filesystems properly. Aborting.");
+ return DITEM_FAILURE;
+ }
+ else if (isDebug())
+ msgDebug("installInitial: Scribbled successfully on the disk(s)\n");
+
+ if (!copySelf()) {
+ msgConfirm("Couldn't clone the boot floppy onto the root file system.\n"
+ "Aborting.");
+ return DITEM_FAILURE;
+ }
+
+ if (chroot("/mnt") == -1) {
+ msgConfirm("Unable to chroot to %s - this is bad!", "/mnt");
+ return DITEM_FAILURE;
+ }
+
+ chdir("/");
+ variable_set2(RUNNING_ON_ROOT, "yes");
+ configResolv();
+
+ /* stick a helpful shell over on the 4th VTY */
+ systemCreateHoloshell();
+
+ alreadyDone = TRUE;
+ return DITEM_SUCCESS;
+}
+
+int
+installFixitHoloShell(dialogMenuItem *self)
+{
+ systemCreateHoloshell();
+ return DITEM_SUCCESS;
+}
+
+int
+installFixitCDROM(dialogMenuItem *self)
+{
+ struct stat sb;
+
+ if (!RunningAsInit)
+ return DITEM_SUCCESS;
+
+ variable_set2(SYSTEM_STATE, "fixit");
+ (void)unlink("/mnt2");
+ (void)rmdir("/mnt2");
+
+ while (1) {
+ msgConfirm("Please insert the second FreeBSD CDROM and press return");
+ if (DITEM_STATUS(mediaSetCDROM(NULL)) != DITEM_SUCCESS || !mediaDevice || !mediaDevice->init(mediaDevice)) {
+ /* If we can't initialize it, it's probably not a FreeBSD CDROM so punt on it */
+ if (mediaDevice) {
+ mediaDevice->shutdown(mediaDevice);
+ mediaDevice = NULL;
+ }
+ if (msgYesNo("Unable to mount the CDROM - do you want to try again?") != 0)
+ return DITEM_FAILURE;
+ }
+ else
+ break;
+ }
+
+ /* Since the fixit code expects everything to be in /mnt2, and the CDROM mounting stuff /dist, do
+ * a little kludge dance here..
+ */
+ if (symlink("/dist", "/mnt2")) {
+ msgConfirm("Unable to symlink /mnt2 to the CDROM mount point. Please report this\n"
+ "unexpected failure to freebsd-bugs@FreeBSD.org.");
+ return DITEM_FAILURE;
+ }
+
+ /*
+ * If /tmp points to /mnt2/tmp from a previous fixit floppy session, it's
+ * not very good for us if we point it to the CDROM now. Rather make it
+ * a directory in the root MFS then. Experienced admins will still be
+ * able to mount their disk's /tmp over this if they need.
+ */
+ if (lstat("/tmp", &sb) == 0 && (sb.st_mode & S_IFMT) == S_IFLNK)
+ (void)unlink("/tmp");
+ Mkdir("/tmp");
+
+ /*
+ * Since setuid binaries ignore LD_LIBRARY_PATH, we indeed need the
+ * ld.so.hints file. Fortunately, it's fairly small (~ 3 KB).
+ */
+ if (!file_readable("/var/run/ld.so.hints")) {
+ Mkdir("/var/run");
+ if (vsystem("/mnt2/sbin/ldconfig -s /mnt2/usr/lib")) {
+ msgConfirm("Warning: ldconfig could not create the ld.so hints file.\n"
+ "Dynamic executables from the CDROM likely won't work.");
+ }
+ }
+
+ /* Yet another iggly hardcoded pathname. */
+ if (!file_readable("/usr/libexec/ld.so")) {
+ Mkdir("/usr/libexec");
+ if (symlink("/mnt2/usr/libexec/ld.so", "/usr/libexec/ld.so")) {
+ msgConfirm("Warning: could not create the symlink for ld.so.\n"
+ "Dynamic executables from the CDROM likely won't work.");
+ }
+ }
+
+ fixit_common();
+
+ mediaDevice->shutdown(mediaDevice);
+ msgConfirm("Please remove the FreeBSD CDROM now.");
+ return DITEM_SUCCESS;
+}
+
+int
+installFixitFloppy(dialogMenuItem *self)
+{
+ struct ufs_args args;
+
+ if (!RunningAsInit)
+ return DITEM_SUCCESS;
+
+ variable_set2(SYSTEM_STATE, "fixit");
+ memset(&args, 0, sizeof(args));
+ args.fspec = "/dev/fd0";
+ Mkdir("/mnt2");
+
+ while (1) {
+ msgConfirm("Please insert a writable fixit floppy and press return");
+ if (mount(MOUNT_UFS, "/mnt2", 0, (caddr_t)&args) != -1)
+ break;
+ msgConfirm("An attempt to mount the fixit floppy failed, maybe the filesystem\n"
+ "is unclean. Trying a forcible mount as a last resort...");
+ if (mount(MOUNT_UFS, "/mnt2", MNT_FORCE, (caddr_t)&args) != -1)
+ break;
+ if (msgYesNo("Unable to mount the fixit floppy - do you want to try again?") != 0)
+ return DITEM_FAILURE;
+ }
+
+ if (!directory_exists("/tmp"))
+ (void)symlink("/mnt2/tmp", "/tmp");
+
+ fixit_common();
+
+ unmount("/mnt2", MNT_FORCE);
+ msgConfirm("Please remove the fixit floppy now.");
+ return DITEM_SUCCESS;
+}
+
+/*
+ * The common code for both fixit variants.
+ */
+static void
+fixit_common(void)
+{
+ pid_t child;
+ int waitstatus;
+
+ if (!directory_exists("/var/tmp/vi.recover")) {
+ if (DITEM_STATUS(Mkdir("/var/tmp/vi.recover")) != DITEM_SUCCESS) {
+ msgConfirm("Warning: Was unable to create a /var/tmp/vi.recover directory.\n"
+ "vi will kvetch and moan about it as a result but should still\n"
+ "be essentially usable.");
+ }
+ }
+ if (!directory_exists("/bin"))
+ (void)Mkdir("/bin");
+ (void)symlink("/stand/sh", "/bin/sh");
+ /* Link the /etc/ files */
+ if (DITEM_STATUS(Mkdir("/etc")) != DITEM_SUCCESS)
+ msgConfirm("Unable to create an /etc directory! Things are weird on this floppy..");
+ else if ((symlink("/mnt2/etc/spwd.db", "/etc/spwd.db") == -1 && errno != EEXIST) ||
+ (symlink("/mnt2/etc/protocols", "/etc/protocols") == -1 && errno != EEXIST) ||
+ (symlink("/mnt2/etc/services", "/etc/services") == -1 && errno != EEXIST))
+ msgConfirm("Couldn't symlink the /etc/ files! I'm not sure I like this..");
+ if (!file_readable(TERMCAP_FILE))
+ create_termcap();
+ if (!(child = fork())) {
+ int i, fd;
+ struct termios foo;
+ extern int login_tty(int);
+
+ ioctl(0, TIOCNOTTY, NULL);
+ for (i = getdtablesize(); i >= 0; --i)
+ close(i);
+ fd = open("/dev/ttyv3", O_RDWR);
+ ioctl(0, TIOCSCTTY, &fd);
+ dup2(0, 1);
+ dup2(0, 2);
+ DebugFD = 2;
+ if (login_tty(fd) == -1)
+ msgDebug("fixit: I can't set the controlling terminal.\n");
+
+ signal(SIGTTOU, SIG_IGN);
+ if (tcgetattr(0, &foo) != -1) {
+ foo.c_cc[VERASE] = '\010';
+ if (tcsetattr(0, TCSANOW, &foo) == -1)
+ msgDebug("fixit shell: Unable to set erase character.\n");
+ }
+ else
+ msgDebug("fixit shell: Unable to get terminal attributes!\n");
+ setenv("PATH", "/bin:/sbin:/usr/bin:/usr/sbin:/stand:"
+ "/mnt2/stand:/mnt2/bin:/mnt2/sbin:/mnt2/usr/bin:/mnt2/usr/sbin", 1);
+ /* use the .profile from the fixit medium */
+ setenv("HOME", "/mnt2", 1);
+ chdir("/mnt2");
+ execlp("sh", "-sh", 0);
+ msgDebug("fixit shell: Failed to execute shell!\n");
+ _exit(1);;
+ }
+ else {
+ msgNotify("Waiting for fixit shell to exit. Go to VTY4 now by\n"
+ "typing ALT-F4. When you are done, type ``exit'' to exit\n"
+ "the fixit shell and be returned here.");
+ (void)waitpid(child, &waitstatus, 0);
+ }
+ dialog_clear();
+}
+
+
+int
+installExpress(dialogMenuItem *self)
+{
+ int i;
+
+ variable_set2(SYSTEM_STATE, "express");
+ if (DITEM_STATUS((i = diskPartitionEditor(self))) == DITEM_FAILURE)
+ return i;
+
+ if (DITEM_STATUS((i = diskLabelEditor(self))) == DITEM_FAILURE)
+ return i;
+
+ dialog_clear_norefresh();
+ if (DITEM_STATUS((i = installCommit(self))) == DITEM_SUCCESS) {
+ i |= DITEM_LEAVE_MENU;
+ /* Give user the option of one last configuration spree */
+ installConfigure();
+ }
+ return i | DITEM_RESTORE;
+}
+
+/* Novice mode installation */
+int
+installNovice(dialogMenuItem *self)
+{
+ int i, tries = 0;
+ Device **devs;
+
+ variable_set2(SYSTEM_STATE, "novice");
+ dialog_clear_norefresh();
+ msgConfirm("In the next menu, you will need to set up a DOS-style (\"fdisk\") partitioning\n"
+ "scheme for your hard disk. If you simply wish to devote all disk space\n"
+ "to FreeBSD (overwriting anything else that might be on the disk(s) selected)\n"
+ "then use the (A)ll command to select the default partitioning scheme followed\n"
+ "by a (Q)uit. If you wish to allocate only free space to FreeBSD, move to a\n"
+ "partition marked \"unused\" and use the (C)reate command.");
+
+nodisks:
+ if (DITEM_STATUS(diskPartitionEditor(self)) == DITEM_FAILURE)
+ return DITEM_FAILURE;
+
+ if (diskGetSelectCount(&devs) <= 0 && tries < 3) {
+ msgConfirm("You need to select some disks to operate on! Be sure to use SPACE\n"
+ "instead of RETURN in the disk selection menu when selecting a disk.");
+ ++tries;
+ goto nodisks;
+ }
+
+ dialog_clear_norefresh();
+ msgConfirm("Next, 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.");
+
+ if (DITEM_STATUS(diskLabelEditor(self)) == DITEM_FAILURE)
+ return DITEM_FAILURE;
+
+ dialog_clear_norefresh();
+ if (DITEM_STATUS((i = installCommit(self))) == DITEM_FAILURE) {
+ dialog_clear_norefresh();
+ msgConfirm("Installation completed with some errors. You may wish to\n"
+ "scroll through the debugging messages on VTY1 with the\n"
+ "scroll-lock feature. You can also chose \"No\" at the next\n"
+ "prompt and go back into the installation menus to try and retry\n"
+ "whichever operations have failed.");
+ return i | DITEM_RESTORE;
+
+ }
+ else {
+ dialog_clear_norefresh();
+ msgConfirm("Congratulations! You now have FreeBSD installed on your system.\n\n"
+ "We will now move on to the final configuration questions.\n"
+ "For any option you do not wish to configure, simply select\n"
+ "No.\n\n"
+ "If you wish to re-enter this utility after the system is up, you\n"
+ "may do so by typing: /stand/sysinstall.");
+ }
+ if (mediaDevice->type != DEVICE_TYPE_FTP && mediaDevice->type != DEVICE_TYPE_NFS) {
+ if (!msgYesNo("Would you like to configure any Ethernet or SLIP/PPP network devices?")) {
+ Device *tmp;
+
+ dialog_clear_norefresh();
+ tmp = tcpDeviceSelect();
+ dialog_clear_norefresh();
+ if (tmp && !msgYesNo("Would you like to bring the %s interface up right now?", tmp->name))
+ if (!tmp->init(tmp))
+ msgConfirm("Initialization of %s device failed.", tmp->name);
+ }
+ }
+
+ dialog_clear_norefresh();
+ if (!msgYesNo("Will this machine be an IP gateway (e.g. will it forward packets\n"
+ "between interfaces)?"))
+ variable_set2("gateway", "YES");
+
+ dialog_clear_norefresh();
+ if (!msgYesNo("Do you want to allow anonymous FTP connections to this machine?"))
+ configAnonFTP(self);
+
+ dialog_clear_norefresh();
+ if (!msgYesNo("Do you want to configure this machine as an NFS server?"))
+ configNFSServer(self);
+
+ dialog_clear_norefresh();
+ if (!msgYesNo("Do you want to configure this machine as an NFS client?"))
+ variable_set2("nfs_client", "YES");
+
+ dialog_clear_norefresh();
+ if (!msgYesNo("Would you like to customize your system console settings?")) {
+ WINDOW *w = savescr();
+
+ dmenuOpenSimple(&MenuSyscons, FALSE);
+ restorescr(w);
+ }
+
+ dialog_clear_norefresh();
+ if (!msgYesNo("Would you like to set this machine's time zone now?")) {
+ WINDOW *w = savescr();
+
+ dialog_clear();
+ systemExecute("tzsetup");
+ restorescr(w);
+ }
+
+ dialog_clear_norefresh();
+ if (!msgYesNo("Does this system have a mouse attached to it?")) {
+ WINDOW *w = savescr();
+
+ dmenuOpenSimple(&MenuMouse, FALSE);
+ restorescr(w);
+ }
+
+ /* Now would be a good time to checkpoint the configuration data */
+ configRC_conf("/etc/rc.conf");
+ sync();
+
+#ifndef USE_XIG_ENVIRONMENT
+ if (directory_exists("/usr/X11R6")) {
+ dialog_clear_norefresh();
+ if (!msgYesNo("Would you like to configure your X server at this time?"))
+ configXEnvironment(self);
+ }
+#endif
+
+ dialog_clear_norefresh();
+ if (!msgYesNo("The FreeBSD package collection is a collection of hundreds of ready-to-run\n"
+ "applications, from text editors to games to WEB servers and more. Would you\n"
+ "like to browse the collection now?"))
+ configPackages(self);
+
+ dialog_clear_norefresh();
+ if (!msgYesNo("Would you like to add any initial user accounts to the system?\n"
+ "Adding at least one account for yourself at this stage is suggested\n"
+ "since working as the \"root\" user is dangerous (it is easy to do\n"
+ "things which adversely affect the entire system)."))
+ configUsers(self);
+
+ dialog_clear_norefresh();
+ msgConfirm("Now you must set the system manager's password.\n"
+ "This is the password you'll use to log in as \"root\".");
+ {
+ WINDOW *w = savescr();
+
+ if (!systemExecute("passwd root"))
+ variable_set2("root_password", "YES");
+ restorescr(w);
+ }
+
+ dialog_clear_norefresh();
+ if (!msgYesNo("Would you like to register your FreeBSD system at this time?\n\n"
+ "PLEASE, take just 5 minutes to do this. If we're ever to get any\n"
+ "significant base of commercial software for FreeBSD, we need to\n"
+ "be able to provide more information about the size of our user community.\n"
+ "This is where your registration can really help us, and you can also\n"
+ "sign up for the new FreeBSD newsletter (its free!) at the same time.\n"))
+ configRegister(NULL);
+ else {
+ dialog_clear_norefresh();
+ msgConfirm("OK, but if you should change your mind then you always can register\n"
+ "later by typing ``/stand/sysinstall register'' or by simply visiting our\n"
+ "web site at http://www.freebsd.org/register.html");
+
+ }
+ /* XXX Put whatever other nice configuration questions you'd like to ask the user here XXX */
+
+ /* Give user the option of one last configuration spree */
+ dialog_clear_norefresh();
+ installConfigure();
+
+ return DITEM_LEAVE_MENU | DITEM_RESTORE;
+}
+
+/* The version of commit we call from the Install Custom menu */
+int
+installCustomCommit(dialogMenuItem *self)
+{
+ int i;
+
+ dialog_clear_norefresh();
+ i = installCommit(self);
+ if (DITEM_STATUS(i) == DITEM_SUCCESS) {
+ /* Give user the option of one last configuration spree */
+ installConfigure();
+ return i;
+ }
+ else
+ msgConfirm("The commit operation completed with errors. Not\n"
+ "updating /etc files.");
+ return i;
+}
+
+/*
+ * What happens when we finally decide to going ahead with the installation.
+ *
+ * This is broken into multiple stages so that the user can do a full
+ * installation but come back here again to load more distributions,
+ * perhaps from a different media type. This would allow, for
+ * example, the user to load the majority of the system from CDROM and
+ * then use ftp to load just the DES dist.
+ */
+int
+installCommit(dialogMenuItem *self)
+{
+ int i;
+ char *str;
+ Boolean need_bin;
+
+ if (!Dists)
+ distConfig(NULL);
+
+ if (!Dists)
+ if (!dmenuOpenSimple(&MenuDistributions, FALSE) && !Dists)
+ return DITEM_FAILURE | DITEM_RESTORE;
+
+ if (!mediaVerify())
+ return DITEM_FAILURE | DITEM_RESTORE;
+
+ str = variable_get(SYSTEM_STATE);
+ if (isDebug())
+ msgDebug("installCommit: System state is `%s'\n", str);
+
+ if (RunningAsInit) {
+ /* Do things we wouldn't do to a multi-user system */
+ if (DITEM_STATUS((i = installInitial())) == DITEM_FAILURE)
+ return i;
+ if (DITEM_STATUS((i = configFstab())) == DITEM_FAILURE)
+ return i;
+ }
+
+try_media:
+ if (!mediaDevice->init(mediaDevice)) {
+ if (!msgYesNo("Unable to initialize selected media. Would you like to\n"
+ "adjust your media configuration and try again?")) {
+ mediaDevice = NULL;
+ if (!mediaVerify())
+ return DITEM_FAILURE | DITEM_RESTORE;
+ else
+ goto try_media;
+ }
+ else
+ return DITEM_FAILURE | DITEM_RESTORE;
+ }
+
+ need_bin = Dists & DIST_BIN;
+ i = distExtractAll(self);
+ if (DITEM_STATUS(i) == DITEM_SUCCESS) {
+ if (need_bin && !(Dists & DIST_BIN))
+ i = installFixup(self);
+ }
+ variable_set2(SYSTEM_STATE, DITEM_STATUS(i) == DITEM_FAILURE ? "error-install" : "full-install");
+
+ /* We always try to install X with the XiG product */
+#ifdef USE_XIG_ENVIRONMENT
+ if (directory_exists("/usr/X11R6"))
+ configXEnvironment(self);
+#endif
+ return i | DITEM_RESTORE;
+}
+
+static void
+installConfigure(void)
+{
+ /* Final menu of last resort */
+ dialog_clear_norefresh();
+ if (!msgYesNo("Visit the general configuration menu for a chance to set\n"
+ "any last options?")) {
+ WINDOW *w = savescr();
+
+ dmenuOpenSimple(&MenuConfigure, FALSE);
+ restorescr(w);
+ }
+ configRC_conf("/etc/rc.conf");
+ sync();
+}
+
+int
+installFixup(dialogMenuItem *self)
+{
+ Device **devs;
+ int i;
+
+ if (!file_readable("/kernel")) {
+ if (file_readable("/kernel.GENERIC")) {
+#ifdef SAVE_USERCONFIG
+ /* Snapshot any boot -c changes back to the GENERIC kernel */
+ if (!variable_cmp(VAR_RELNAME, RELEASE_NAME))
+ save_userconfig_to_kernel("/kernel.GENERIC");
+#endif
+ if (vsystem("cp -p /kernel.GENERIC /kernel")) {
+ msgConfirm("Unable to link /kernel into place!");
+ return DITEM_FAILURE;
+ }
+ }
+ else {
+ msgConfirm("Can't find a kernel image to link to on the root file system!\n"
+ "You're going to have a hard time getting this system to\n"
+ "boot from the hard disk, I'm afraid!");
+ return DITEM_FAILURE;
+ }
+ }
+
+ /* Resurrect /dev after bin distribution screws it up */
+ if (RunningAsInit) {
+ msgNotify("Remaking all devices.. Please wait!");
+ if (vsystem("cd /dev; sh MAKEDEV all")) {
+ msgConfirm("MAKEDEV returned non-zero status");
+ return DITEM_FAILURE;
+ }
+
+ msgNotify("Resurrecting /dev entries for slices..");
+ devs = deviceFind(NULL, DEVICE_TYPE_DISK);
+ if (!devs)
+ msgFatal("Couldn't get a disk device list!");
+
+ /* Resurrect the slices that the former clobbered */
+ for (i = 0; devs[i]; i++) {
+ Disk *disk = (Disk *)devs[i]->private;
+ Chunk *c1;
+
+ if (!devs[i]->enabled)
+ continue;
+ if (!disk->chunks)
+ msgFatal("No chunk list found for %s!", disk->name);
+ for (c1 = disk->chunks->part; c1; c1 = c1->next) {
+ if (c1->type == freebsd) {
+ msgNotify("Making slice entries for %s", c1->name);
+ if (vsystem("cd /dev; sh MAKEDEV %sh", c1->name)) {
+ msgConfirm("Unable to make slice entries for %s!", c1->name);
+ return DITEM_FAILURE;
+ }
+ }
+ }
+ }
+ /* XXX Do all the last ugly work-arounds here which we'll try and excise someday right?? XXX */
+
+ msgNotify("Fixing permissions..");
+ /* BOGON #1: XFree86 extracting /usr/X11R6 with root-only perms */
+ if (directory_exists("/usr/X11R6")) {
+ vsystem("chmod -R a+r /usr/X11R6");
+ vsystem("find /usr/X11R6 -type d | xargs chmod a+x");
+ }
+ /* 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");
+
+ /* BOGON #6: deal with new boot files */
+ vsystem("touch /kernel.config");
+ vsystem("touch /boot.config");
+ if (file_readable("/stand/boot.help") && !file_readable("/boot.help"))
+ vsystem("mv /stand/boot.help /");
+
+ /* 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");
+ }
+ return DITEM_SUCCESS;
+}
+
+/* Go newfs and/or mount all the filesystems we've been asked to */
+int
+installFilesystems(dialogMenuItem *self)
+{
+ int i;
+ Disk *disk;
+ Chunk *c1, *c2, *rootdev, *swapdev, *usrdev, *vardev;
+ Device **devs;
+ PartInfo *root;
+ char dname[80];
+ extern int MakeDevChunk(Chunk *c, char *n);
+ Boolean upgrade = FALSE;
+
+ /* If we've already done this, bail out */
+ if (!variable_cmp(DISK_LABELLED, "written"))
+ return DITEM_SUCCESS;
+
+ upgrade = !variable_cmp(SYSTEM_STATE, "upgrade");
+ if (!checkLabels(TRUE, &rootdev, &swapdev, &usrdev, &vardev))
+ return DITEM_FAILURE;
+
+ if (rootdev)
+ root = (PartInfo *)rootdev->private_data;
+ else
+ root = NULL;
+
+ command_clear();
+ if (swapdev && RunningAsInit) {
+ /* As the very first thing, try to get ourselves some swap space */
+ sprintf(dname, "/dev/%s", swapdev->name);
+ if (!Fake && (!MakeDevChunk(swapdev, "/dev") || !file_readable(dname))) {
+ msgConfirm("Unable to make device node for %s in /dev!\n"
+ "The creation of filesystems will be aborted.", dname);
+ return DITEM_FAILURE;
+ }
+
+ if (!Fake) {
+ if (!swapon(dname))
+ msgNotify("Added %s as initial swap device", dname);
+ else
+ msgConfirm("WARNING! Unable to swap to %s: %s\n"
+ "This may cause the installation to fail at some point\n"
+ "if you don't have a lot of memory.", dname, strerror(errno));
+ }
+ }
+
+ if (rootdev && RunningAsInit) {
+ /* Next, create and/or mount the root device */
+ sprintf(dname, "/dev/r%sa", rootdev->disk->name);
+ if (!Fake && (!MakeDevChunk(rootdev, "/dev") || !file_readable(dname))) {
+ msgConfirm("Unable to make device node for %s in /dev!\n"
+ "The creation of filesystems will be aborted.", dname);
+ return DITEM_FAILURE;
+ }
+ if (strcmp(root->mountpoint, "/"))
+ msgConfirm("Warning: %s is marked as a root partition but is mounted on %s", rootdev->name, root->mountpoint);
+
+ if (root->newfs && (!upgrade || !msgYesNo("You are upgrading - are you SURE you want to newfs the root partition?"))) {
+ int i;
+
+ msgNotify("Making a new root filesystem on %s", dname);
+ i = vsystem("%s %s", root->newfs_cmd, dname);
+ if (i) {
+ msgConfirm("Unable to make new root filesystem on %s!\n"
+ "Command returned status %d", dname, i);
+ return DITEM_FAILURE;
+ }
+ }
+ else {
+ if (!upgrade) {
+ msgConfirm("Warning: Using existing root partition. It will be assumed\n"
+ "that you have the appropriate device entries already in /dev.");
+ }
+ msgNotify("Checking integrity of existing %s filesystem.", dname);
+ i = vsystem("fsck -y %s", dname);
+ if (i)
+ msgConfirm("Warning: fsck returned status of %d for %s.\n"
+ "This partition may be unsafe to use.", i, dname);
+ }
+
+ /* Switch to block device */
+ sprintf(dname, "/dev/%sa", rootdev->disk->name);
+ if (Mount("/mnt", dname)) {
+ msgConfirm("Unable to mount the root file system on %s! Giving up.", dname);
+ return DITEM_FAILURE;
+ }
+ }
+
+ /* Now buzz through the rest of the partitions and mount them too */
+ devs = deviceFind(NULL, DEVICE_TYPE_DISK);
+ for (i = 0; devs[i]; i++) {
+ if (!devs[i]->enabled)
+ continue;
+
+ disk = (Disk *)devs[i]->private;
+ if (!disk->chunks) {
+ msgConfirm("No chunk list found for %s!", disk->name);
+ return DITEM_FAILURE;
+ }
+ if (RunningAsInit && root && (root->newfs || upgrade)) {
+ Mkdir("/mnt/dev");
+ if (!Fake)
+ MakeDevDisk(disk, "/mnt/dev");
+ }
+ else if (!RunningAsInit && !Fake)
+ MakeDevDisk(disk, "/dev");
+
+ for (c1 = disk->chunks->part; c1; c1 = c1->next) {
+ if (c1->type == freebsd) {
+ for (c2 = c1->part; c2; c2 = c2->next) {
+ if (c2->type == part && c2->subtype != FS_SWAP && c2->private_data) {
+ PartInfo *tmp = (PartInfo *)c2->private_data;
+
+ /* Already did root */
+ if (c2 == rootdev)
+ continue;
+
+ if (tmp->newfs && (!upgrade || !msgYesNo("You are upgradding - are you SURE you want to newfs /dev/%s?", c2->name)))
+ command_shell_add(tmp->mountpoint, "%s %s/dev/r%s", tmp->newfs_cmd, RunningAsInit ? "/mnt" : "", c2->name);
+ else
+ command_shell_add(tmp->mountpoint, "fsck -y %s/dev/r%s", RunningAsInit ? "/mnt" : "", c2->name);
+ command_func_add(tmp->mountpoint, Mount, c2->name);
+ }
+ else if (c2->type == part && c2->subtype == FS_SWAP) {
+ char fname[80];
+ int i;
+
+ if (c2 == swapdev)
+ continue;
+ sprintf(fname, "%s/dev/%s", RunningAsInit ? "/mnt" : "", c2->name);
+ i = (Fake || swapon(fname));
+ if (!i)
+ msgNotify("Added %s as an additional swap device", fname);
+ else
+ msgConfirm("Unable to add %s as a swap device: %s", fname, strerror(errno));
+ }
+ }
+ }
+ else if (c1->type == fat && c1->private_data && (root->newfs || upgrade)) {
+ char name[FILENAME_MAX];
+
+ sprintf(name, "%s/%s", RunningAsInit ? "/mnt" : "", ((PartInfo *)c1->private_data)->mountpoint);
+ Mkdir(name);
+ }
+ }
+ }
+
+ if (RunningAsInit) {
+ msgNotify("Copying initial device files..");
+ /* Copy the boot floppy's dev files */
+ if ((root->newfs || upgrade) && vsystem("find -x /dev | cpio %s -pdum /mnt", cpioVerbosity())) {
+ msgConfirm("Couldn't clone the /dev files!");
+ return DITEM_FAILURE;
+ }
+ }
+
+ command_sort();
+ command_execute();
+ return DITEM_SUCCESS;
+}
+
+/* Initialize various user-settable values to their defaults */
+int
+installVarDefaults(dialogMenuItem *self)
+{
+ char *cp;
+
+ /* Set default startup options */
+ variable_set2(VAR_ROUTER_ENABLE, "NO");
+ variable_set2(VAR_RELNAME, RELEASE_NAME);
+ variable_set2(VAR_CPIO_VERBOSITY, "high");
+ variable_set2(VAR_TAPE_BLOCKSIZE, DEFAULT_TAPE_BLOCKSIZE);
+ variable_set2(VAR_INSTALL_ROOT, "/");
+ variable_set2(VAR_INSTALL_CFG, "install.cfg");
+ cp = getenv("EDITOR");
+ if (!cp)
+ cp = "/usr/bin/ee";
+ variable_set2(VAR_EDITOR, cp);
+ variable_set2(VAR_FTP_USER, "ftp");
+ variable_set2(VAR_BROWSER_PACKAGE, PACKAGE_LYNX);
+ variable_set2(VAR_BROWSER_BINARY, "/usr/local/bin/lynx");
+ variable_set2(VAR_FTP_STATE, "passive");
+ variable_set2(VAR_NFS_SECURE, "YES");
+ variable_set2(VAR_PKG_TMPDIR, "/usr/tmp");
+ variable_set2(VAR_GATED_PKG, PACKAGE_GATED);
+ variable_set2(VAR_PCNFSD_PKG, PACKAGE_PCNFSD);
+ variable_set2(VAR_MEDIA_TIMEOUT, itoa(MEDIA_TIMEOUT));
+ if (getpid() != 1)
+ variable_set2(SYSTEM_STATE, "update");
+ else
+ variable_set2(SYSTEM_STATE, "init");
+ return DITEM_SUCCESS;
+}
+
+/* Load the environment up from various system configuration files */
+void
+installEnvironment(void)
+{
+ if (file_readable("/etc/rc.conf"))
+ configEnvironmentRC_conf("/etc/rc.conf");
+ if (file_readable("/etc/resolv.conf"))
+ configEnvironmentResolv("/etc/resolv.conf");
+}
+
+/* Copy the boot floppy contents into /stand */
+Boolean
+copySelf(void)
+{
+ int i;
+
+ if (file_readable("/boot.help"))
+ vsystem("cp /boot.help /mnt");
+ msgWeHaveOutput("Copying the boot floppy to /stand on root filesystem");
+ i = vsystem("find -x /stand | cpio %s -pdum /mnt", cpioVerbosity());
+ if (i) {
+ msgConfirm("Copy returned error status of %d!", i);
+ return FALSE;
+ }
+
+ /* Copy the /etc files into their rightful place */
+ if (vsystem("cd /mnt/stand; find etc | cpio %s -pdum /mnt", cpioVerbosity())) {
+ msgConfirm("Couldn't copy up the /etc files!");
+ return TRUE;
+ }
+ return TRUE;
+}
+
+static void
+create_termcap(void)
+{
+ FILE *fp;
+
+ const char *caps[] = {
+ termcap_vt100, termcap_cons25, termcap_cons25_m, termcap_cons25r,
+ termcap_cons25r_m, termcap_cons25l1, termcap_cons25l1_m, NULL,
+ };
+ const char **cp;
+
+ if (!file_readable(TERMCAP_FILE)) {
+ Mkdir("/usr/share/misc");
+ fp = fopen(TERMCAP_FILE, "w");
+ if (!fp) {
+ msgConfirm("Unable to initialize termcap file. Some screen-oriented\nutilities may not work.");
+ return;
+ }
+ cp = caps;
+ while (*cp)
+ fprintf(fp, "%s\n", *(cp++));
+ fclose(fp);
+ }
+}
+
+#ifdef SAVE_USERCONFIG
+static void
+save_userconfig_to_kernel(char *kern)
+{
+ struct kernel *core, *boot;
+ struct list *c_isa, *b_isa, *c_dev, *b_dev;
+ int i, d;
+
+ if ((core = uc_open("-incore")) == NULL) {
+ msgDebug("save_userconf: Can't read in-core information for kernel.\n");
+ return;
+ }
+
+ if ((boot = uc_open(kern)) == NULL) {
+ msgDebug("save_userconf: Can't read device information for kernel image %s\n", kern);
+ return;
+ }
+
+ msgNotify("Saving any boot -c changes to new kernel...");
+ c_isa = uc_getdev(core, "-isa");
+ b_isa = uc_getdev(boot, "-isa");
+ if (isDebug())
+ msgDebug("save_userconf: got %d ISA device entries from core, %d from boot.\n", c_isa->ac, b_isa->ac);
+ for (d = 0; d < c_isa->ac; d++) {
+ if (isDebug())
+ msgDebug("save_userconf: ISA device loop, c_isa->av[%d] = %s\n", d, c_isa->av[d]);
+ if (strcmp(c_isa->av[d], "npx0")) { /* special case npx0, which mucks with its id_irq member */
+ c_dev = uc_getdev(core, c_isa->av[d]);
+ b_dev = uc_getdev(boot, b_isa->av[d]);
+ if (!c_dev || !b_dev) {
+ msgDebug("save_userconf: c_dev: %x b_dev: %x\n", c_dev, b_dev);
+ continue;
+ }
+ if (isDebug())
+ msgDebug("save_userconf: ISA device %s: %d config parameters (core), %d (boot)\n",
+ c_isa->av[d], c_dev->ac, b_dev->ac);
+ for (i = 0; i < c_dev->ac; i++) {
+ if (isDebug())
+ msgDebug("save_userconf: c_dev->av[%d] = %s, b_dev->av[%d] = %s\n", i, c_dev->av[i], i, b_dev->av[i]);
+ if (strcmp(c_dev->av[i], b_dev->av[i])) {
+ if (isDebug())
+ msgDebug("save_userconf: %s (boot) -> %s (core)\n",
+ c_dev->av[i], b_dev->av[i]);
+ isa_setdev(boot, c_dev);
+ }
+ }
+ }
+ else {
+ if (isDebug())
+ msgDebug("skipping npx0\n");
+ }
+ }
+ if (isDebug())
+ msgDebug("Closing kernels\n");
+ uc_close(core, 0);
+ uc_close(boot, 1);
+}
+#endif
diff --git a/usr.sbin/sysinstall/install.cfg b/usr.sbin/sysinstall/install.cfg
new file mode 100644
index 0000000..478156d
--- /dev/null
+++ b/usr.sbin/sysinstall/install.cfg
@@ -0,0 +1,96 @@
+# This is the installation configuration file for my test machine,
+# crate.cdrom.com.
+# It is included here merely as a sort-of-documented example.
+
+# Turn on extra debugging.
+debug=yes
+
+################################
+# My host specific data
+hostname=crate.cdrom.com
+domainname=cdrom.com
+nameserver=204.216.27.3
+defaultrouter=204.216.27.228
+ipaddr=204.216.27.230
+netmask=255.255.255.240
+################################
+
+################################
+# Which installation device to use - ftp is pointed directly at my local
+# machine and the installation device is my WD8013 ethernet interface.
+ftp=ftp://time.cdrom.com/pub
+netDev=ed0
+mediaSetFTP
+################################
+
+################################
+# Select which distributions we want.
+dists=bin doc manpages info compat21 des src sbase slkm ssys
+distSetCustom
+################################
+
+################################
+# Now set the parameters for the partition editor on wd0. Set to use the
+# disk exclusively (could also be "all" to use the whole disk but
+# respecting the MBR or "free" to use only unallocated space for FreeBSD).
+disk=wd0
+partition=exclusive
+diskPartitionEditor
+
+# Uncomment this instead to use only the free space and install boot manager.
+#partition=free
+#bootManager=booteasy
+#diskPartitionEditor
+################################
+
+################################
+
+# This assumes that slice 1 is a DOS partition and mounts it as /dos,
+# which is the case on my laptop.
+#
+# All sizes are expressed in 512 byte blocks!
+
+# A 20MB root partition
+wd0s1-1=ufs 40960 /
+# And a 20MB swap partition
+wd0s1-2=swap 40960 none
+# Followed by a /usr partition using all remaining space (size 0 = free space)
+wd0s1-3=ufs 0 /usr
+# Let's do it!
+diskLabelEditor
+
+################################
+
+################################
+# Now partition the 2nd disk.
+disk=wd1
+partition=exclusive
+diskPartitionEditor
+
+wd1s1-1=ufs 40960 /var
+wd1s1-2=ufs 0 /usr/src
+diskLabelEditor
+################################
+
+################################
+# And the 3rd.
+disk=sd0
+partition=exclusive
+diskPartitionEditor
+
+sd0s1-1=swap 40960 none
+sd0s1-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..ab4337a
--- /dev/null
+++ b/usr.sbin/sysinstall/installUpgrade.c
@@ -0,0 +1,495 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last program in the `sysinstall' line - the next
+ * generation being essentially a complete rewrite.
+ *
+ * $Id: installUpgrade.c,v 1.53 1997/10/12 16:21:15 jkh Exp $
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include "sysinstall.h"
+#include <sys/disklabel.h>
+#include <sys/errno.h>
+#include <sys/ioctl.h>
+#include <sys/fcntl.h>
+#include <sys/wait.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <sys/mount.h>
+
+static int installUpgradeNonInteractive(dialogMenuItem *self);
+
+typedef struct _hitList {
+ enum { JUST_COPY, CALL_HANDLER } action ;
+ char *name;
+ Boolean optional;
+ void (*handler)(struct _hitList *self);
+} HitList;
+
+/* These are the only meaningful files I know about */
+static HitList etc_files [] = {
+ { JUST_COPY, "Xaccel.ini", TRUE, NULL },
+ { JUST_COPY, "adduser.conf", TRUE, NULL },
+ { JUST_COPY, "aliases", TRUE, NULL },
+ { JUST_COPY, "aliases.db", TRUE, NULL },
+ { JUST_COPY, "amd.map", 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, "daily", TRUE, NULL },
+ { JUST_COPY, "disktab", TRUE, NULL },
+ { JUST_COPY, "dm.conf", TRUE, NULL },
+ { JUST_COPY, "exports", TRUE, NULL },
+ { JUST_COPY, "fbtab", TRUE, NULL },
+ { JUST_COPY, "fstab", FALSE, NULL },
+ { JUST_COPY, "ftpusers", TRUE, NULL },
+ { JUST_COPY, "gettytab", TRUE, NULL },
+ { JUST_COPY, "gnats", TRUE, NULL },
+ { JUST_COPY, "group", FALSE, NULL },
+ { JUST_COPY, "host.conf", TRUE, NULL },
+ { JUST_COPY, "hosts", TRUE, NULL },
+ { JUST_COPY, "hosts.equiv", TRUE, NULL },
+ { JUST_COPY, "hosts.lpd", TRUE, NULL },
+ { JUST_COPY, "inetd.conf", TRUE, NULL },
+ { JUST_COPY, "kerberosIV", TRUE, NULL },
+ { JUST_COPY, "localtime", TRUE, NULL },
+ { JUST_COPY, "login.access", TRUE, NULL },
+ { JUST_COPY, "mail.rc", TRUE, NULL },
+ { JUST_COPY, "make.conf", TRUE, NULL },
+ { JUST_COPY, "manpath.config", TRUE, NULL },
+ { JUST_COPY, "master.passwd", TRUE, NULL },
+ { JUST_COPY, "mib.txt", TRUE, NULL },
+ { JUST_COPY, "modems", TRUE, NULL },
+ { JUST_COPY, "monthly", TRUE, NULL },
+ { JUST_COPY, "motd", TRUE, NULL },
+ { JUST_COPY, "namedb", TRUE, NULL },
+ { JUST_COPY, "networks", TRUE, NULL },
+ { JUST_COPY, "passwd", FALSE, NULL },
+ { JUST_COPY, "phones", TRUE, NULL },
+ { JUST_COPY, "ppp", TRUE, NULL },
+ { JUST_COPY, "printcap", TRUE, NULL },
+ { JUST_COPY, "profile", TRUE, NULL },
+ { JUST_COPY, "protocols", TRUE, NULL },
+ { JUST_COPY, "pwd.db", TRUE, NULL },
+ { JUST_COPY, "rc.local", TRUE, NULL },
+ { JUST_COPY, "rc.conf", FALSE, NULL },
+ { JUST_COPY, "remote", TRUE, NULL },
+ { JUST_COPY, "resolv.conf", TRUE, NULL },
+ { JUST_COPY, "rmt", TRUE, NULL },
+ { JUST_COPY, "security", TRUE, NULL },
+ { JUST_COPY, "sendmail.cf", 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, "supfile", TRUE, NULL },
+ { JUST_COPY, "syslog.conf", TRUE, NULL },
+ { JUST_COPY, "termcap", TRUE, NULL },
+ { JUST_COPY, "ttys", TRUE, NULL },
+ { JUST_COPY, "uucp", TRUE, NULL },
+ { JUST_COPY, "weekly", 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");
+ systemDisplayHelp("upgrade");
+
+ dialog_clear_norefresh();
+ if (msgYesNo("Given all that scary stuff you just read, are you sure you want to\n"
+ "risk it all and proceed with this upgrade?") != 0)
+ return DITEM_FAILURE | DITEM_RESTORE;
+
+ if (!Dists) {
+ msgConfirm("First, you must select some distribution components. The upgrade procedure\n"
+ "will only upgrade the distributions you select in the next set of menus.");
+ if (!dmenuOpenSimple(&MenuDistributions, FALSE) || !Dists)
+ return DITEM_FAILURE | DITEM_RESTORE;
+ dialog_clear_norefresh();
+ }
+ else if (!(Dists & DIST_BIN)) { /* No bin selected? Not much of an upgrade.. */
+ if (msgYesNo("You didn't select the bin distribution as one of the distributons to load.\n"
+ "This one is pretty vital to a successful upgrade. Are you SURE you don't\n"
+ "want to select the bin distribution? Chose No to bring up the Distributions\n"
+ "menu again.") != 0) {
+ if (!dmenuOpenSimple(&MenuDistributions, FALSE))
+ return DITEM_FAILURE | DITEM_RESTORE;
+ dialog_clear_norefresh();
+ }
+ }
+
+ /* Still?! OK! They must know what they're doing.. */
+ if (!(Dists & DIST_BIN))
+ extractingBin = FALSE;
+
+ if (RunningAsInit) {
+ Device **devs;
+ int i, cnt;
+ char *cp;
+
+ cp = variable_get(VAR_DISK);
+ devs = deviceFind(cp, DEVICE_TYPE_DISK);
+ cnt = deviceCount(devs);
+ if (!cnt) {
+ msgConfirm("No disks found! Please verify that your disk controller is being\n"
+ "properly probed at boot time. See the Hardware Guide on the\n"
+ "Documentation menu for clues on diagnosing this type of problem.");
+ return DITEM_FAILURE;
+ }
+ else {
+ /* Enable all the drives befor we start */
+ for (i = 0; i < cnt; i++)
+ devs[i]->enabled = TRUE;
+ }
+
+ msgConfirm("OK. First, we're going to go to the disk label editor. In this editor\n"
+ "you will be expected to Mount any partitions you're interested in\n"
+ "upgrading. DO NOT set the Newfs flag to Y on anything in the label editor\n"
+ "unless you're absolutely sure you know what you're doing! In this\n"
+ "instance, you'll be using the label editor as little more than a fancy\n"
+ "screen-oriented partition mounting tool.\n\n"
+ "Once you're done in the label editor, press Q to return here for the next\n"
+ "step.");
+
+ if (DITEM_STATUS(diskLabelEditor(self)) == DITEM_FAILURE) {
+ msgConfirm("The disk label editor returned an error status. Upgrade operation\n"
+ "aborted.");
+ return DITEM_FAILURE | DITEM_RESTORE;
+ }
+
+ /* Don't write out MBR info */
+ variable_set2(DISK_PARTITIONED, "written");
+ 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("/");
+ systemCreateHoloshell();
+ }
+
+ saved_etc[0] = '\0';
+ if (extractingBin) {
+ while (!*saved_etc) {
+ char *cp = msgGetInput("/usr/tmp/etc", "Under which directory do you wish to save your current /etc?");
+
+ if (!cp || !*cp || Mkdir(cp)) {
+ if (msgYesNo("Directory was not specified, was invalid or user selected Cancel.\n\n"
+ "Doing an upgrade without first backing up your /etc directory is a very\n"
+ "bad idea! Do you want to go back and specify the save directory again?") != 0)
+ break;
+ }
+ else {
+ SAFE_STRCPY(saved_etc, cp);
+ }
+ }
+
+ if (saved_etc[0]) {
+ msgNotify("Preserving /etc directory..");
+ if (vsystem("tar -cBpf - -C /etc . | tar --unlink -xBpf - -C %s", saved_etc))
+ if (msgYesNo("Unable to backup your /etc into %s.\n"
+ "Do you want to continue anyway?", saved_etc) != 0)
+ return DITEM_FAILURE | DITEM_RESTORE;
+ }
+
+ msgNotify("chflags'ing old binaries - please wait.");
+ (void)vsystem("chflags -R noschg /bin /sbin /usr/sbin /usr/bin /kernel*");
+
+ if (file_readable("/kernel")) {
+ msgNotify("Moving old kernel to /kernel.prev");
+ if (system("chflags noschg /kernel && 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 /* Give us a working kernel in case we crash and reboot */
+ system("cp /kernel.prev /kernel");
+ }
+ }
+
+media:
+ /* We do this very late, but we unfortunately need to back up /etc first */
+ if (!mediaVerify())
+ return DITEM_FAILURE | DITEM_RESTORE;
+
+ if (!mediaDevice->init(mediaDevice)) {
+ if (!msgYesNo("Couldn't initialize the media. Would you like\n"
+ "to adjust your media selection and try again?")) {
+ mediaDevice = NULL;
+ goto media;
+ }
+ else
+ return DITEM_FAILURE | DITEM_REDRAW;
+ }
+
+ msgNotify("Beginning extraction of distributions..");
+ if (DITEM_STATUS(distExtractAll(self)) == DITEM_FAILURE) {
+ msgConfirm("Hmmmm. We couldn't even extract the bin distribution. This upgrade\n"
+ "should be considered a failure and started from the beginning, sorry!\n"
+ "The system will reboot now.");
+ dialog_clear();
+ systemShutdown(1);
+ }
+ else if (Dists) {
+ if (!extractingBin || !(Dists & DIST_BIN)) {
+ msgNotify("The extraction process seems to have had some problems, but we got most\n"
+ "of the essentials. We'll treat this as a warning since it may have been\n"
+ "only non-essential distributions which failed to load.");
+ }
+ else {
+ msgConfirm("Hmmmm. We couldn't even extract the bin distribution. This upgrade\n"
+ "should be considered a failure and started from the beginning, sorry!\n"
+ "The system will reboot now.");
+ dialog_clear();
+ systemShutdown(1);
+ }
+ }
+
+ if (extractingBin) {
+ msgNotify("OK, now it's time to go pound on your root a little bit to create all the\n"
+ "/dev entries and such that a new system expects to see. I'll also perform a\n"
+ "few \"fixup\" operations to repair the effects of splatting a bin distribution\n"
+ "on top of an existing system..");
+ if (DITEM_STATUS(installFixup(self)) == DITEM_FAILURE) {
+ msgConfirm("Hmmmmm. The fixups don't seem to have been very happy.\n"
+ "You may wish to examine the system a little more closely when\n"
+ "it comes time to merge your /etc customizations back.");
+ }
+ }
+
+ msgNotify("First stage of upgrade completed successfully!\n\n"
+ "Next comes stage 2, where we attempt to resurrect your /etc\n"
+ "directory!");
+
+ if (saved_etc && chdir(saved_etc)) {
+ msgConfirm("Unable to go to your saved /etc directory in %s?! Argh!\n"
+ "Something went seriously wrong! It's quite possible that\n"
+ "your former /etc is toast. I hope you didn't have any\n"
+ "important customizations you wanted to keep in there.. :(", saved_etc);
+ }
+ else {
+ /* Now try to resurrect the /etc files */
+ traverseHitlist(etc_files);
+ }
+
+ msgConfirm("OK! At this stage, we've resurrected all the /etc files\n"
+ "and moved each new copy over to /etc/upgrade/<file> in case you want\n"
+ "to see what the new versions look like. If you want to wander over\n"
+ "to the Emergency Holographic Shell [ALT-F4] at this point to do\n"
+ "that, now would be a good time. When you're ready to reboot into\n"
+ "the new system, just exit the installation.");
+ return DITEM_SUCCESS | DITEM_REDRAW;
+}
+
+static int
+installUpgradeNonInteractive(dialogMenuItem *self)
+{
+ char *saved_etc;
+ Boolean extractingBin = TRUE;
+
+ variable_set2(SYSTEM_STATE, "upgrade");
+
+ /* Make sure at least BIN is selected */
+ Dists |= DIST_BIN;
+
+ if (RunningAsInit) {
+ Device **devs;
+ int i, cnt;
+ char *cp;
+
+ cp = variable_get(VAR_DISK);
+ devs = deviceFind(cp, DEVICE_TYPE_DISK);
+ cnt = deviceCount(devs);
+ if (!cnt) {
+ msgConfirm("No disks found! Please verify that your disk controller is being\n"
+ "properly probed at boot time. See the Hardware Guide on the\n"
+ "Documentation menu for clues on diagnosing this type of problem.");
+ return DITEM_FAILURE;
+ }
+ else {
+ /* Enable all the drives befor we start */
+ for (i = 0; i < cnt; i++)
+ devs[i]->enabled = TRUE;
+ }
+
+ msgConfirm("OK. First, we're going to go to the disk label editor. In this editor\n"
+ "you will be expected to Mount any partitions you're interested in\n"
+ "upgrading. DO NOT set the Newfs flag to Y on anything in the label editor\n"
+ "unless you're absolutely sure you know what you're doing! In this\n"
+ "instance, you'll be using the label editor as little more than a fancy\n"
+ "screen-oriented partition mounting tool.\n\n"
+ "Once you're done in the label editor, press Q to return here for the next\n"
+ "step.");
+
+ if (DITEM_STATUS(diskLabelEditor(self)) == DITEM_FAILURE) {
+ msgConfirm("The disk label editor returned an error status. Upgrade operation\n"
+ "aborted.");
+ return DITEM_FAILURE | DITEM_RESTORE;
+ }
+
+ /* Don't write out MBR info */
+ variable_set2(DISK_PARTITIONED, "written");
+ if (DITEM_STATUS(diskLabelCommit(self)) == DITEM_FAILURE) {
+ msgConfirm("Not all file systems were properly mounted. Upgrade operation\n"
+ "aborted.");
+ variable_unset(DISK_PARTITIONED);
+ return DITEM_FAILURE | DITEM_RESTORE;
+ }
+
+ if (extractingBin) {
+ msgNotify("chflags'ing old binaries - please wait.");
+ (void)vsystem("chflags -R noschg /mnt/");
+ }
+ msgNotify("Updating /stand on root filesystem");
+ (void)vsystem("find -x /stand | cpio %s -pdum /mnt", cpioVerbosity());
+
+ if (DITEM_STATUS(chroot("/mnt")) == DITEM_FAILURE) {
+ msgConfirm("Unable to chroot to /mnt - something is wrong with the\n"
+ "root partition or the way it's mounted if this doesn't work.");
+ variable_unset(DISK_PARTITIONED);
+ return DITEM_FAILURE | DITEM_RESTORE;
+ }
+ chdir("/");
+ systemCreateHoloshell();
+ }
+
+ if (!mediaVerify() || !mediaDevice->init(mediaDevice)) {
+ msgNotify("Upgrade: Couldn't initialize media.");
+ return DITEM_FAILURE | DITEM_RESTORE;
+ }
+
+ saved_etc = "/usr/tmp/etc";
+ Mkdir(saved_etc);
+ msgNotify("Preserving /etc directory..");
+ if (vsystem("tar -cpBf - -C /etc . | tar -xpBf - -C %s", saved_etc)) {
+ msgNotify("Unable to backup your /etc into %s.", saved_etc);
+ return DITEM_FAILURE | DITEM_RESTORE;
+ }
+
+ if (file_readable("/kernel")) {
+ msgNotify("Moving old kernel to /kernel.prev");
+ if (!system("chflags noschg /kernel && mv /kernel /kernel.prev")) {
+ /* Give us a working kernel in case we crash and reboot */
+ system("cp /kernel.prev /kernel");
+ }
+ }
+
+ msgNotify("Beginning extraction of distributions..");
+ if (DITEM_STATUS(distExtractAll(self)) == DITEM_FAILURE) {
+ msgConfirm("Hmmmm. We couldn't even extract the bin distribution. This upgrade\n"
+ "should be considered a failure and started from the beginning, sorry!\n"
+ "The system will reboot now.");
+ dialog_clear();
+ systemShutdown(1);
+ }
+ else if (Dists) {
+ if (!(Dists & DIST_BIN)) {
+ msgNotify("The extraction process seems to have had some problems, but we got most\n"
+ "of the essentials. We'll treat this as a warning since it may have been\n"
+ "only non-essential distributions which failed to upgrade.");
+ }
+ else {
+ msgConfirm("Hmmmm. We couldn't even extract the bin distribution. This upgrade\n"
+ "should be considered a failure and started from the beginning, sorry!\n"
+ "The system will reboot now.");
+ dialog_clear();
+ systemShutdown(1);
+ }
+ }
+
+ msgNotify("OK, now it's time to go pound on your root a little bit to create all the\n"
+ "/dev entries and such that a new system expects to see. I'll also perform a\n"
+ "few \"fixup\" operations to repair the effects of splatting a bin distribution\n"
+ "on top of an existing system..");
+ if (DITEM_STATUS(installFixup(self)) == DITEM_FAILURE) {
+ msgNotify("Hmmmmm. The fixups don't seem to have been very happy.\n"
+ "You may wish to examine the system a little more closely when\n"
+ "it comes time to merge your /etc customizations back.");
+ }
+
+ msgNotify("First stage of upgrade completed successfully.");
+ if (vsystem("tar -cpBf - -C %s . | tar --unlink -xpBf - -C /etc", saved_etc)) {
+ msgNotify("Unable to resurrect your old /etc!");
+ return DITEM_FAILURE | DITEM_RESTORE;
+ }
+ return DITEM_SUCCESS | DITEM_REDRAW;
+}
diff --git a/usr.sbin/sysinstall/keymap.c b/usr.sbin/sysinstall/keymap.c
new file mode 100644
index 0000000..459f4cb
--- /dev/null
+++ b/usr.sbin/sysinstall/keymap.c
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 1996 Joerg Wunsch
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE DEVELOPERS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $Id$
+ *
+ */
+
+#include "sysinstall.h"
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <machine/console.h>
+
+struct keymapInfo {
+ const char *name;
+ const struct keymap *map;
+};
+
+#include "keymap.h"
+
+/*
+ * keymap.h is being automatically generated by the Makefile. It
+ * contains definitions for all desired keymaps. Note that since we
+ * don't support font loading nor screen mapping during installation,
+ * we simply don't care for any other keys than the ASCII subset.
+ *
+ * Therefore, if no keymap with the exact name has been found in the
+ * first pass, we make a second pass over the table looking just for
+ * the language name only.
+ */
+
+/*
+ * Return values:
+ *
+ * 0: OK
+ * -1: no appropriate keymap found
+ * -2: error installing map (other than ENXIO which means we're not on syscons)
+ */
+
+int
+loadKeymap(const char *lang)
+{
+ int passno, err;
+ char *llang;
+ size_t l;
+ struct keymapInfo *kip;
+
+ llang = strdup(lang);
+ if (llang == NULL)
+ abort();
+
+ for (passno = 0; passno < 2; passno++)
+ {
+ if (passno > 0)
+ {
+ /* make the match more fuzzy */
+ l = strspn(llang, "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789");
+ llang[l] = '\0';
+ }
+
+ l = strlen(llang);
+
+ for (kip = keymapInfos; kip->name; kip++)
+ if (strncmp(kip->name, llang, l) == 0)
+ {
+ /* Yep, got it! */
+ err = ioctl(0, PIO_KEYMAP, kip->map);
+ free(llang);
+ return (err == -1 && errno != ENOTTY)? -2: 0;
+ }
+ }
+ free(llang);
+ return -1;
+}
diff --git a/usr.sbin/sysinstall/label.c b/usr.sbin/sysinstall/label.c
new file mode 100644
index 0000000..4b109f4
--- /dev/null
+++ b/usr.sbin/sysinstall/label.c
@@ -0,0 +1,1249 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last program in the `sysinstall' line - the next
+ * generation being essentially a complete rewrite.
+ *
+ * $Id: label.c,v 1.63.2.15 1997/11/05 05:54:27 obrien Exp $
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include "sysinstall.h"
+#include <ctype.h>
+#include <sys/disklabel.h>
+#include <sys/param.h>
+#include <sys/sysctl.h>
+
+/*
+ * Everything to do with editing the contents of disk labels.
+ */
+
+/* A nice message we use a lot in the disklabel editor */
+#define MSG_NOT_APPLICABLE "That option is not applicable here"
+
+/* Where to start printing the freebsd slices */
+#define CHUNK_SLICE_START_ROW 2
+#define CHUNK_PART_START_ROW 11
+
+/* The smallest filesystem we're willing to create */
+#define FS_MIN_SIZE ONE_MEG
+
+/* The smallest root filesystem we're willing to create */
+#define ROOT_MIN_SIZE 20
+
+/* The smallest swap partition we want to create by default */
+#define SWAP_MIN_SIZE 16
+
+/* The smallest /usr partition we're willing to create by default */
+#define USR_MIN_SIZE 80
+
+/* The smallest /var partition we're willing to create by default */
+#define VAR_MIN_SIZE 30
+
+/* The bottom-most row we're allowed to scribble on */
+#define CHUNK_ROW_MAX 16
+
+
+/* All the chunks currently displayed on the screen */
+static struct {
+ struct chunk *c;
+ PartType type;
+} label_chunk_info[MAX_CHUNKS + 1];
+static int here;
+
+/*** with this value we try to track the most recently added label ***/
+static int label_focus = 0, pslice_focus = 0;
+
+static int diskLabel(Device *dev);
+static int diskLabelNonInteractive(Device *dev);
+
+static int
+labelHook(dialogMenuItem *selected)
+{
+ Device **devs = NULL;
+
+ devs = deviceFind(selected->prompt, DEVICE_TYPE_DISK);
+ if (!devs) {
+ msgConfirm("Unable to find disk %s!", selected->prompt);
+ return DITEM_FAILURE;
+ }
+ /* Toggle enabled status? */
+ if (!devs[0]->enabled) {
+ devs[0]->enabled = TRUE;
+ diskLabel(devs[0]);
+ }
+ else
+ devs[0]->enabled = FALSE;
+ return DITEM_SUCCESS | DITEM_RESTORE;
+}
+
+static int
+labelCheck(dialogMenuItem *selected)
+{
+ Device **devs = NULL;
+
+ devs = deviceFind(selected->prompt, DEVICE_TYPE_DISK);
+ if (!devs || devs[0]->enabled == FALSE)
+ return FALSE;
+ return TRUE;
+}
+
+int
+diskLabelEditor(dialogMenuItem *self)
+{
+ DMenu *menu;
+ Device **devs;
+ int i, cnt;
+
+ i = 0;
+ cnt = diskGetSelectCount(&devs);
+ if (cnt == -1) {
+ msgConfirm("No disks found! Please verify that your disk controller is being\n"
+ "properly probed at boot time. See the Hardware Guide on the\n"
+ "Documentation menu for clues on diagnosing this type of problem.");
+ return DITEM_FAILURE;
+ }
+ else if (cnt) {
+ int i;
+
+ /* Some are already selected */
+ if (variable_get(VAR_NONINTERACTIVE))
+ i |= diskLabelNonInteractive(NULL);
+ else
+ i |= diskLabel(NULL);
+ }
+ else {
+ /* No disks are selected, fall-back case now */
+ cnt = deviceCount(devs);
+ if (cnt == 1) {
+ devs[0]->enabled = TRUE;
+ if (variable_get(VAR_NONINTERACTIVE))
+ i = diskLabelNonInteractive(devs[0]);
+ else
+ i = diskLabel(devs[0]);
+ }
+ else {
+ menu = deviceCreateMenu(&MenuDiskDevices, DEVICE_TYPE_DISK, labelHook, labelCheck);
+ if (!menu) {
+ msgConfirm("No devices suitable for installation found!\n\n"
+ "Please verify that your disk controller (and attached drives)\n"
+ "were detected properly. This can be done by pressing the\n"
+ "[Scroll Lock] key and using the Arrow keys to move back to\n"
+ "the boot messages. Press [Scroll Lock] again to return.");
+ i = DITEM_FAILURE;
+ }
+ else {
+ i = dmenuOpenSimple(menu, FALSE) ? DITEM_SUCCESS : DITEM_FAILURE;
+ free(menu);
+ }
+ i |= DITEM_RESTORE;
+ }
+ }
+ if (DITEM_STATUS(i) != DITEM_FAILURE) {
+ char *cp;
+
+ if (((cp = variable_get(DISK_LABELLED)) == NULL) || (strcmp(cp, "written")))
+ variable_set2(DISK_LABELLED, "yes");
+ }
+ 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");
+ i = DITEM_SUCCESS;
+ }
+ return i;
+}
+
+/* See if we're already using a desired partition name */
+static Boolean
+check_conflict(char *name)
+{
+ int i;
+
+ for (i = 0; label_chunk_info[i].c; i++)
+ if ((label_chunk_info[i].type == PART_FILESYSTEM || label_chunk_info[i].type == PART_FAT)
+ && label_chunk_info[i].c->private_data
+ && !strcmp(((PartInfo *)label_chunk_info[i].c->private_data)->mountpoint, name))
+ return TRUE;
+ return FALSE;
+}
+
+/* How much space is in this FreeBSD slice? */
+static int
+space_free(struct chunk *c)
+{
+ struct chunk *c1;
+ int sz = c->size;
+
+ for (c1 = c->part; c1; c1 = c1->next) {
+ if (c1->type != unused)
+ sz -= c1->size;
+ }
+ if (sz < 0)
+ msgFatal("Partitions are larger than actual chunk??");
+ return sz;
+}
+
+/* Snapshot the current situation into the displayed chunks structure */
+static void
+record_label_chunks(Device **devs, Device *dev)
+{
+ int i, j, p;
+ struct chunk *c1, *c2;
+ Disk *d;
+
+ j = p = 0;
+ /* First buzz through and pick up the FreeBSD slices */
+ for (i = 0; devs[i]; i++) {
+ if ((dev && devs[i] != dev) || !devs[i]->enabled)
+ continue;
+ d = (Disk *)devs[i]->private;
+ if (!d->chunks)
+ msgFatal("No chunk list found for %s!", d->name);
+
+ /* Put the slice entries first */
+ for (c1 = d->chunks->part; c1; c1 = c1->next) {
+ if (c1->type == freebsd) {
+ label_chunk_info[j].type = PART_SLICE;
+ label_chunk_info[j].c = c1;
+ ++j;
+ }
+ }
+ }
+
+ /* Now run through again and get the FreeBSD partition entries */
+ for (i = 0; devs[i]; i++) {
+ if (!devs[i]->enabled)
+ continue;
+ d = (Disk *)devs[i]->private;
+ /* Then buzz through and pick up the partitions */
+ for (c1 = d->chunks->part; c1; c1 = c1->next) {
+ if (c1->type == freebsd) {
+ for (c2 = c1->part; c2; c2 = c2->next) {
+ if (c2->type == part) {
+ if (c2->subtype == FS_SWAP)
+ label_chunk_info[j].type = PART_SWAP;
+ else
+ label_chunk_info[j].type = PART_FILESYSTEM;
+ label_chunk_info[j].c = c2;
+ ++j;
+ }
+ }
+ }
+ else if (c1->type == fat) {
+ label_chunk_info[j].type = PART_FAT;
+ label_chunk_info[j].c = c1;
+ ++j;
+ }
+ }
+ }
+ label_chunk_info[j].c = NULL;
+ if (here >= j) {
+ here = j ? j - 1 : 0;
+ }
+}
+
+/* A new partition entry */
+static PartInfo *
+new_part(char *mpoint, Boolean newfs, u_long size)
+{
+ PartInfo *ret;
+
+ if (!mpoint)
+ mpoint = "/change_me";
+
+ ret = (PartInfo *)safe_malloc(sizeof(PartInfo));
+ sstrncpy(ret->mountpoint, mpoint, FILENAME_MAX);
+ strcpy(ret->newfs_cmd, "newfs -b 8192 -f 1024");
+ ret->newfs = newfs;
+ if (!size)
+ return ret;
+ return ret;
+}
+
+/* Get the mountpoint for a partition and save it away */
+static PartInfo *
+get_mountpoint(struct chunk *old)
+{
+ char *val;
+ PartInfo *tmp;
+
+ if (old && old->private_data)
+ tmp = old->private_data;
+ else
+ tmp = NULL;
+ if (!old) {
+ DialogX = 14;
+ DialogY = 16;
+ }
+ val = msgGetInput(tmp ? tmp->mountpoint : NULL, "Please specify a mount point for the partition");
+ DialogX = DialogY = 0;
+ if (!val || !*val) {
+ if (!old)
+ return NULL;
+ else {
+ free(old->private_data);
+ old->private_data = NULL;
+ }
+ return NULL;
+ }
+
+ /* Is it just the same value? */
+ if (tmp && !strcmp(tmp->mountpoint, val))
+ return NULL;
+
+ /* Did we use it already? */
+ if (check_conflict(val)) {
+ msgConfirm("You already have a mount point for %s assigned!", val);
+ return NULL;
+ }
+
+ /* Is it bogus? */
+ if (*val != '/') {
+ msgConfirm("Mount point must start with a / character");
+ return NULL;
+ }
+
+ /* Is it going to be mounted on root? */
+ if (!strcmp(val, "/")) {
+ if (old)
+ old->flags |= CHUNK_IS_ROOT;
+ }
+ else if (old)
+ old->flags &= ~CHUNK_IS_ROOT;
+
+ safe_free(tmp);
+ tmp = new_part(val, TRUE, 0);
+ if (old) {
+ old->private_data = tmp;
+ old->private_free = safe_free;
+ }
+ return tmp;
+}
+
+/* Get the type of the new partiton */
+static PartType
+get_partition_type(void)
+{
+ char selection[20];
+ int i;
+
+ static unsigned char *fs_types[] = {
+ "FS",
+ "A file system",
+ "Swap",
+ "A swap partition.",
+ };
+ DialogX = 7;
+ DialogY = 8;
+ i = dialog_menu("Please choose a partition type",
+ "If you want to use this partition for swap space, select Swap.\n"
+ "If you want to put a filesystem on it, choose FS.",
+ -1, -1, 2, 2, fs_types, selection, NULL, NULL);
+ DialogX = DialogY = 0;
+ if (!i) {
+ if (!strcmp(selection, "FS"))
+ return PART_FILESYSTEM;
+ else if (!strcmp(selection, "Swap"))
+ return PART_SWAP;
+ }
+ return PART_NONE;
+}
+
+/* If the user wants a special newfs command for this, set it */
+static void
+getNewfsCmd(PartInfo *p)
+{
+ char *val;
+
+ val = msgGetInput(p->newfs_cmd,
+ "Please enter the newfs command and options you'd like to use in\n"
+ "creating this file system.");
+ if (val)
+ sstrncpy(p->newfs_cmd, val, NEWFS_CMD_MAX);
+}
+
+#define MAX_MOUNT_NAME 12
+
+#define PART_PART_COL 0
+#define PART_MOUNT_COL 8
+#define PART_SIZE_COL (PART_MOUNT_COL + MAX_MOUNT_NAME + 3)
+#define PART_NEWFS_COL (PART_SIZE_COL + 7)
+#define PART_OFF 38
+
+#define TOTAL_AVAIL_LINES (10)
+#define PSLICE_SHOWABLE (4)
+
+
+/* stick this all up on the screen */
+static void
+print_label_chunks(void)
+{
+ int i, j, srow, prow, pcol;
+ int sz;
+ char clrmsg[80];
+ int ChunkPartStartRow;
+ WINDOW *ChunkWin;
+
+ /********************************************************/
+ /*** These values are for controling screen resources ***/
+ /*** Each label line holds up to 2 labels, so beware! ***/
+ /*** strategy will be to try to always make sure the ***/
+ /*** highlighted label is in the active display area. ***/
+ /********************************************************/
+ int pslice_max, label_max;
+ int pslice_count, label_count, label_focus_found, pslice_focus_found;
+
+ attrset(A_REVERSE);
+ mvaddstr(0, 25, "FreeBSD Disklabel Editor");
+ attrset(A_NORMAL);
+
+ /*** Count the number of parition slices ***/
+ pslice_count = 0;
+ for (i = 0; label_chunk_info[i].c ; i++) {
+ if (label_chunk_info[i].type == PART_SLICE)
+ ++pslice_count;
+ }
+ pslice_max = pslice_count;
+
+ /*** 4 line max for partition slices ***/
+ if (pslice_max > PSLICE_SHOWABLE) {
+ pslice_max = PSLICE_SHOWABLE;
+ }
+ ChunkPartStartRow = CHUNK_SLICE_START_ROW + 3 + pslice_max;
+
+ /*** View partition slices modulo pslice_max ***/
+ label_max = TOTAL_AVAIL_LINES - pslice_max;
+
+ for (i = 0; i < 2; i++) {
+ mvaddstr(ChunkPartStartRow - 2, PART_PART_COL + (i * PART_OFF), "Part");
+ mvaddstr(ChunkPartStartRow - 1, PART_PART_COL + (i * PART_OFF), "----");
+
+ mvaddstr(ChunkPartStartRow - 2, PART_MOUNT_COL + (i * PART_OFF), "Mount");
+ mvaddstr(ChunkPartStartRow - 1, PART_MOUNT_COL + (i * PART_OFF), "-----");
+
+ mvaddstr(ChunkPartStartRow - 2, PART_SIZE_COL + (i * PART_OFF) + 2, "Size");
+ mvaddstr(ChunkPartStartRow - 1, PART_SIZE_COL + (i * PART_OFF) + 2, "----");
+
+ mvaddstr(ChunkPartStartRow - 2, PART_NEWFS_COL + (i * PART_OFF), "Newfs");
+ mvaddstr(ChunkPartStartRow - 1, PART_NEWFS_COL + (i * PART_OFF), "-----");
+ }
+ srow = CHUNK_SLICE_START_ROW;
+ prow = 0;
+ pcol = 0;
+
+ /*** these variables indicate that the focused item is shown currently ***/
+ label_focus_found = 0;
+ pslice_focus_found = 0;
+
+ label_count = 0;
+ pslice_count = 0;
+ mvprintw(CHUNK_SLICE_START_ROW - 1, 0, " ");
+ mvprintw(CHUNK_SLICE_START_ROW + pslice_max, 0, " ");
+
+ ChunkWin = newwin(CHUNK_ROW_MAX - ChunkPartStartRow, 76, ChunkPartStartRow, 0);
+
+ wclear(ChunkWin);
+ /*** wrefresh(ChunkWin); ***/
+
+ for (i = 0; label_chunk_info[i].c; i++) {
+ /* Is it a slice entry displayed at the top? */
+ if (label_chunk_info[i].type == PART_SLICE) {
+ /*** This causes the new pslice to replace the previous display ***/
+ /*** focus must remain on the most recently active pslice ***/
+ if (pslice_count == pslice_max) {
+ if (pslice_focus_found) {
+ /*** This is where we can mark the more following ***/
+ attrset(A_BOLD);
+ mvprintw(CHUNK_SLICE_START_ROW + pslice_max, 0, "***MORE***");
+ attrset(A_NORMAL);
+ continue;
+ }
+ else {
+ /*** this is where we set the more previous ***/
+ attrset(A_BOLD);
+ mvprintw(CHUNK_SLICE_START_ROW - 1, 0, "***MORE***");
+ attrset(A_NORMAL);
+ pslice_count = 0;
+ srow = CHUNK_SLICE_START_ROW;
+ }
+ }
+
+ sz = space_free(label_chunk_info[i].c);
+ if (i == here)
+ attrset(ATTR_SELECTED);
+ if (i == pslice_focus)
+ pslice_focus_found = -1;
+
+ mvprintw(srow++, 0,
+ "Disk: %s\tPartition name: %s\tFree: %d blocks (%dMB)",
+ label_chunk_info[i].c->disk->name, label_chunk_info[i].c->name,
+ sz, (sz / ONE_MEG));
+ attrset(A_NORMAL);
+ clrtoeol();
+ move(0, 0);
+ /*** refresh(); ***/
+ ++pslice_count;
+ }
+ /* Otherwise it's a DOS, swap or filesystem entry in the Chunk window */
+ else {
+ char onestr[PART_OFF], num[10], *mountpoint, *newfs;
+
+ /*
+ * We copy this into a blank-padded string so that it looks like
+ * a solid bar in reverse-video
+ */
+ memset(onestr, ' ', PART_OFF - 1);
+ onestr[PART_OFF - 1] = '\0';
+
+ /*** Track how many labels have been displayed ***/
+ if (label_count == ((label_max - 1 ) * 2)) {
+ if (label_focus_found) {
+ continue;
+ }
+ else {
+ label_count = 0;
+ prow = 0;
+ pcol = 0;
+ }
+ }
+
+ /* Go for two columns if we've written one full columns worth */
+ /*** if (prow == (CHUNK_ROW_MAX - ChunkPartStartRow)) ***/
+ if (label_count == label_max - 1) {
+ pcol = PART_OFF;
+ prow = 0;
+ }
+ memcpy(onestr + PART_PART_COL, label_chunk_info[i].c->name, strlen(label_chunk_info[i].c->name));
+ /* If it's a filesystem, display the mountpoint */
+ if (label_chunk_info[i].c->private_data
+ && (label_chunk_info[i].type == PART_FILESYSTEM || label_chunk_info[i].type == PART_FAT))
+ mountpoint = ((PartInfo *)label_chunk_info[i].c->private_data)->mountpoint;
+ else if (label_chunk_info[i].type == PART_SWAP)
+ mountpoint = "swap";
+ else
+ mountpoint = "<none>";
+
+ /* Now display the newfs field */
+ if (label_chunk_info[i].type == PART_FAT)
+ newfs = "DOS";
+ else if (label_chunk_info[i].c->private_data && label_chunk_info[i].type == PART_FILESYSTEM)
+ newfs = ((PartInfo *)label_chunk_info[i].c->private_data)->newfs ? "UFS Y" : "UFS N";
+ else if (label_chunk_info[i].type == PART_SWAP)
+ newfs = "SWAP";
+ else
+ newfs = "*";
+ for (j = 0; j < MAX_MOUNT_NAME && mountpoint[j]; j++)
+ onestr[PART_MOUNT_COL + j] = mountpoint[j];
+ snprintf(num, 10, "%4ldMB", label_chunk_info[i].c->size ? label_chunk_info[i].c->size / ONE_MEG : 0);
+ memcpy(onestr + PART_SIZE_COL, num, strlen(num));
+ memcpy(onestr + PART_NEWFS_COL, newfs, strlen(newfs));
+ onestr[PART_NEWFS_COL + strlen(newfs)] = '\0';
+ if (i == label_focus) {
+ label_focus_found = -1;
+ wattrset(ChunkWin, A_BOLD);
+ }
+ if (i == here)
+ wattrset(ChunkWin, ATTR_SELECTED);
+
+ /*** lazy man's way of padding this string ***/
+ while (strlen( onestr ) < 37)
+ strcat(onestr, " ");
+
+ mvwaddstr(ChunkWin, prow, pcol, onestr);
+ wattrset(ChunkWin, A_NORMAL);
+ move(0, 0);
+ ++prow;
+ ++label_count;
+ }
+ }
+
+ /*** this will erase all the extra stuff ***/
+ memset(clrmsg, ' ', 37);
+ clrmsg[37] = '\0';
+
+ while (pslice_count < pslice_max) {
+ mvprintw(srow++, 0, clrmsg);
+ clrtoeol();
+ ++pslice_count;
+ }
+ while (label_count < (2 * (label_max - 1))) {
+ mvwaddstr(ChunkWin, prow++, pcol, clrmsg);
+ ++label_count;
+ if (prow == (label_max - 1)) {
+ prow = 0;
+ pcol = PART_OFF;
+ }
+ }
+ refresh();
+ wrefresh(ChunkWin);
+}
+
+static void
+print_command_summary(void)
+{
+ mvprintw(17, 0, "The following commands are valid here (upper or lower case):");
+ mvprintw(18, 0, "C = Create D = Delete M = Mount pt.");
+ if (!RunningAsInit)
+ mvprintw(18, 49, "W = Write");
+ mvprintw(19, 0, "N = Newfs Opts T = Newfs Toggle U = Undo Q = Finish");
+ mvprintw(20, 0, "A = Auto Defaults for all!");
+ mvprintw(22, 0, "Use F1 or ? to get more help, arrow keys to select.");
+ move(0, 0);
+}
+
+static void
+clear_wins(void)
+{
+ extern void print_label_chunks();
+ clear();
+ print_label_chunks();
+}
+
+static int
+diskLabel(Device *dev)
+{
+ int sz, key = 0;
+ Boolean labeling;
+ char *msg = NULL;
+ PartInfo *p, *oldp;
+ PartType type;
+ Device **devs;
+
+ label_focus = 0;
+ pslice_focus = 0;
+ here = 0;
+
+ devs = deviceFind(NULL, DEVICE_TYPE_DISK);
+ if (!devs) {
+ msgConfirm("No disks found!");
+ return DITEM_FAILURE;
+ }
+ labeling = TRUE;
+ keypad(stdscr, TRUE);
+ record_label_chunks(devs, dev);
+
+ clear();
+ while (labeling) {
+ char *cp;
+
+ print_label_chunks();
+ print_command_summary();
+ if (msg) {
+ attrset(title_attr); mvprintw(23, 0, msg); attrset(A_NORMAL);
+ clrtoeol();
+ beep();
+ msg = NULL;
+ }
+ else {
+ move(23, 0);
+ clrtoeol();
+ }
+
+ refresh();
+ key = getch();
+ switch (toupper(key)) {
+ int i;
+ static char _msg[40];
+
+ case '\014': /* ^L */
+ clear_wins();
+ break;
+
+ case '\020': /* ^P */
+ case KEY_UP:
+ case '-':
+ if (here != 0)
+ --here;
+ else
+ while (label_chunk_info[here + 1].c)
+ ++here;
+ break;
+
+ case '\016': /* ^N */
+ case KEY_DOWN:
+ case '+':
+ case '\r':
+ case '\n':
+ if (label_chunk_info[here + 1].c)
+ ++here;
+ else
+ here = 0;
+ break;
+
+ case KEY_HOME:
+ here = 0;
+ break;
+
+ case KEY_END:
+ while (label_chunk_info[here + 1].c)
+ ++here;
+ break;
+
+ case KEY_F(1):
+ case '?':
+ systemDisplayHelp("partition");
+ clear_wins();
+ break;
+
+ case 'A':
+ if (label_chunk_info[here].type != PART_SLICE) {
+ msg = "You can only do this in a disk slice (at top of screen)";
+ break;
+ }
+ sz = space_free(label_chunk_info[here].c);
+ if (sz <= FS_MIN_SIZE)
+ msg = "Not enough free space to create a new partition in the slice";
+ else {
+ struct chunk *tmp;
+ int mib[2];
+ int physmem;
+ size_t size, swsize;
+ char *cp;
+ Chunk *rootdev, *swapdev, *usrdev, *vardev;
+
+ (void)checkLabels(FALSE, &rootdev, &swapdev, &usrdev, &vardev);
+ if (!rootdev) {
+ cp = variable_get(VAR_ROOT_SIZE);
+ tmp = Create_Chunk_DWIM(label_chunk_info[here].c->disk, label_chunk_info[here].c,
+ (cp ? atoi(cp) : 32) * ONE_MEG, part, FS_BSDFFS, CHUNK_IS_ROOT);
+ if (!tmp) {
+ msgConfirm("Unable to create the root partition. Too big?");
+ clear_wins();
+ break;
+ }
+ tmp->private_data = new_part("/", TRUE, tmp->size);
+ tmp->private_free = safe_free;
+ record_label_chunks(devs, dev);
+ }
+
+ if (!swapdev) {
+ cp = variable_get(VAR_SWAP_SIZE);
+ if (cp)
+ swsize = atoi(cp) * ONE_MEG;
+ else {
+ mib[0] = CTL_HW;
+ mib[1] = HW_PHYSMEM;
+ size = sizeof physmem;
+ sysctl(mib, 2, &physmem, &size, (void *)0, (size_t)0);
+ swsize = 16 * ONE_MEG + (physmem * 2 / 512);
+ }
+ tmp = Create_Chunk_DWIM(label_chunk_info[here].c->disk, label_chunk_info[here].c,
+ swsize, part, FS_SWAP, 0);
+ if (!tmp) {
+ msgConfirm("Unable to create the swap partition. Too big?");
+ clear_wins();
+ break;
+ }
+ tmp->private_data = 0;
+ tmp->private_free = safe_free;
+ record_label_chunks(devs, dev);
+ }
+
+ if (!vardev) {
+ cp = variable_get(VAR_VAR_SIZE);
+ tmp = Create_Chunk_DWIM(label_chunk_info[here].c->disk, label_chunk_info[here].c,
+ (cp ? atoi(cp) : VAR_MIN_SIZE) * ONE_MEG, part, FS_BSDFFS, 0);
+ if (!tmp) {
+ msgConfirm("Less than %dMB free for /var - you will need to\n"
+ "partition your disk manually with a custom install!",
+ (cp ? atoi(cp) : VAR_MIN_SIZE));
+ clear_wins();
+ break;
+ }
+ tmp->private_data = new_part("/var", TRUE, tmp->size);
+ tmp->private_free = safe_free;
+ record_label_chunks(devs, dev);
+ }
+
+ if (!usrdev) {
+ cp = variable_get(VAR_USR_SIZE);
+ if (cp)
+ sz = atoi(cp) * ONE_MEG;
+ else
+ sz = space_free(label_chunk_info[here].c);
+ if (sz) {
+ if (sz < (USR_MIN_SIZE * ONE_MEG)) {
+ msgConfirm("Less than %dMB free for /usr - you will need to\n"
+ "partition your disk manually with a custom install!", USR_MIN_SIZE);
+ clear_wins();
+ break;
+ }
+
+ tmp = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
+ label_chunk_info[here].c,
+ sz, part, FS_BSDFFS, 0);
+ if (!tmp) {
+ msgConfirm("Unable to create the /usr partition. Not enough space?\n"
+ "You will need to partition your disk manually with a custom install!");
+ clear_wins();
+ break;
+ }
+ tmp->private_data = new_part("/usr", TRUE, tmp->size);
+ tmp->private_free = safe_free;
+ record_label_chunks(devs, dev);
+ }
+ }
+ /* At this point, we're reasonably "labelled" */
+ if (((cp = variable_get(DISK_LABELLED)) == NULL) || (strcmp(cp, "written")))
+ variable_set2(DISK_LABELLED, "yes");
+ }
+ break;
+
+ case 'C':
+ if (label_chunk_info[here].type != PART_SLICE) {
+ msg = "You can only do this in a master partition (see top of screen)";
+ break;
+ }
+ sz = space_free(label_chunk_info[here].c);
+ if (sz <= FS_MIN_SIZE) {
+ msg = "Not enough space to create an additional FreeBSD partition";
+ break;
+ }
+ else {
+ char *val;
+ int size;
+ struct chunk *tmp;
+ char osize[80];
+ u_long flags = 0;
+
+ sprintf(osize, "%d", sz);
+ DialogX = 3;
+ DialogY = 2;
+ val = msgGetInput(osize,
+ "Please specify the partition size in blocks or append a trailing M for\n"
+ "megabytes or C for cylinders. %d blocks (%dMB) are free.",
+ sz, sz / ONE_MEG);
+ DialogX = DialogY = 0;
+ if (!val || (size = strtol(val, &cp, 0)) <= 0) {
+ clear_wins();
+ break;
+ }
+
+ if (*cp) {
+ if (toupper(*cp) == 'M')
+ size *= ONE_MEG;
+ else if (toupper(*cp) == 'C')
+ size *= (label_chunk_info[here].c->disk->bios_hd * label_chunk_info[here].c->disk->bios_sect);
+ }
+ if (size <= FS_MIN_SIZE) {
+ msgConfirm("The minimum filesystem size is %dMB", FS_MIN_SIZE / ONE_MEG);
+ clear_wins();
+ break;
+ }
+ type = get_partition_type();
+ if (type == PART_NONE) {
+ clear_wins();
+ beep();
+ break;
+ }
+
+ if (type == PART_FILESYSTEM) {
+ if ((p = get_mountpoint(NULL)) == NULL) {
+ clear_wins();
+ beep();
+ break;
+ }
+ else if (!strcmp(p->mountpoint, "/"))
+ flags |= CHUNK_IS_ROOT;
+ else
+ flags &= ~CHUNK_IS_ROOT;
+ }
+ else
+ p = NULL;
+
+ if ((flags & CHUNK_IS_ROOT)) {
+ if (!(label_chunk_info[here].c->flags & CHUNK_BSD_COMPAT)) {
+ msgConfirm("This region cannot be used for your root partition as the\n"
+ "FreeBSD boot code cannot deal with a root partition created\n"
+ "in that location. Please choose another location or smaller\n"
+ "size for your root partition and try again!");
+ clear_wins();
+ break;
+ }
+ if (size < (ROOT_MIN_SIZE * ONE_MEG)) {
+ msgConfirm("Warning: This is smaller than the recommended size for a\n"
+ "root partition. For a variety of reasons, root\n"
+ "partitions should usually be at least %dMB in size", ROOT_MIN_SIZE);
+ }
+ }
+ tmp = Create_Chunk_DWIM(label_chunk_info[here].c->disk,
+ label_chunk_info[here].c,
+ size, part,
+ (type == PART_SWAP) ? FS_SWAP : FS_BSDFFS,
+ flags);
+ if (!tmp) {
+ msgConfirm("Unable to create the partition. Too big?");
+ clear_wins();
+ break;
+ }
+ if ((flags & CHUNK_IS_ROOT) && (tmp->flags & CHUNK_PAST_1024)) {
+ msgConfirm("This region cannot be used for your root partition as it starts\n"
+ "or extends past the 1024'th cylinder mark and is thus a\n"
+ "poor location to boot from. Please choose another\n"
+ "location (or smaller size) for your root partition and try again!");
+ Delete_Chunk(label_chunk_info[here].c->disk, tmp);
+ clear_wins();
+ break;
+ }
+ if (type != PART_SWAP) {
+ /* This is needed to tell the newfs -u about the size */
+ tmp->private_data = new_part(p->mountpoint, p->newfs, tmp->size);
+ safe_free(p);
+ }
+ else
+ tmp->private_data = p;
+ tmp->private_free = safe_free;
+ if (((cp = variable_get(DISK_LABELLED)) == NULL) || (strcmp(cp, "written")))
+ variable_set2(DISK_LABELLED, "yes");
+ record_label_chunks(devs, dev);
+ clear_wins();
+ /*** This is where we assign focus to new label so it shows ***/
+ {
+ int i;
+ label_focus = -1;
+ for (i = 0; label_chunk_info[i].c; ++i) {
+ if (label_chunk_info[i].c == tmp) {
+ label_focus = i;
+ break;
+ }
+ }
+ if (label_focus == -1)
+ label_focus = i - 1;
+ }
+ }
+ break;
+
+ case KEY_DC:
+ case 'D': /* delete */
+ if (label_chunk_info[here].type == PART_SLICE) {
+ msg = MSG_NOT_APPLICABLE;
+ break;
+ }
+ else if (label_chunk_info[here].type == PART_FAT) {
+ msg = "Use the Disk Partition Editor to delete DOS partitions";
+ break;
+ }
+ Delete_Chunk(label_chunk_info[here].c->disk, label_chunk_info[here].c);
+ if (((cp = variable_get(DISK_LABELLED)) == NULL) || (strcmp(cp, "written")))
+ variable_set2(DISK_LABELLED, "yes");
+ record_label_chunks(devs, dev);
+ break;
+
+ case 'M': /* mount */
+ switch(label_chunk_info[here].type) {
+ case PART_SLICE:
+ msg = MSG_NOT_APPLICABLE;
+ break;
+
+ case PART_SWAP:
+ msg = "You don't need to specify a mountpoint for a swap partition.";
+ break;
+
+ case PART_FAT:
+ case PART_FILESYSTEM:
+ oldp = label_chunk_info[here].c->private_data;
+ p = get_mountpoint(label_chunk_info[here].c);
+ if (p) {
+ if (!oldp)
+ p->newfs = FALSE;
+ if (label_chunk_info[here].type == PART_FAT
+ && (!strcmp(p->mountpoint, "/") || !strcmp(p->mountpoint, "/usr")
+ || !strcmp(p->mountpoint, "/var"))) {
+ msgConfirm("%s is an invalid mount point for a DOS partition!", p->mountpoint);
+ strcpy(p->mountpoint, "/bogus");
+ }
+ }
+ if (((cp = variable_get(DISK_LABELLED)) == NULL) || (strcmp(cp, "written")))
+ variable_set2(DISK_LABELLED, "yes");
+ record_label_chunks(devs, dev);
+ clear_wins();
+ break;
+
+ default:
+ msgFatal("Bogus partition under cursor???");
+ break;
+ }
+ break;
+
+ case 'N': /* Set newfs options */
+ if (label_chunk_info[here].c->private_data &&
+ ((PartInfo *)label_chunk_info[here].c->private_data)->newfs)
+ getNewfsCmd(label_chunk_info[here].c->private_data);
+ else
+ msg = MSG_NOT_APPLICABLE;
+ clear_wins();
+ break;
+
+ case 'T': /* Toggle newfs state */
+ if (label_chunk_info[here].type == PART_FILESYSTEM) {
+ PartInfo *pi = ((PartInfo *)label_chunk_info[here].c->private_data);
+ label_chunk_info[here].c->private_data =
+ new_part(pi ? pi->mountpoint : NULL, pi ? !pi->newfs : TRUE, label_chunk_info[here].c->size);
+ safe_free(pi);
+ label_chunk_info[here].c->private_free = safe_free;
+ if (((cp = variable_get(DISK_LABELLED)) == NULL) || (strcmp(cp, "written")))
+ variable_set2(DISK_LABELLED, "yes");
+ }
+ else
+ msg = MSG_NOT_APPLICABLE;
+ break;
+
+ case 'U':
+ clear();
+ if ((cp = variable_get(DISK_LABELLED)) && !strcmp(cp, "written")) {
+ msgConfirm("You've already written out your changes -\n"
+ "it's too late to undo!");
+ }
+ else if (!msgYesNo("Are you SURE you want to Undo everything?")) {
+ variable_unset(DISK_PARTITIONED);
+ variable_unset(DISK_LABELLED);
+ for (i = 0; devs[i]; i++) {
+ Disk *d;
+
+ if (!devs[i]->enabled)
+ continue;
+ else if ((d = Open_Disk(devs[i]->name)) != NULL) {
+ Free_Disk(devs[i]->private);
+ devs[i]->private = d;
+ diskPartition(devs[i]);
+ }
+ }
+ record_label_chunks(devs, dev);
+ }
+ clear_wins();
+ break;
+
+ case 'W':
+ if ((cp = variable_get(DISK_LABELLED)) && !strcmp(cp, "written")) {
+ msgConfirm("You've already written out your changes - if you\n"
+ "wish to overwrite them, you'll have to start this\n"
+ "procedure again from the beginning.");
+ }
+ else if (!msgYesNo("WARNING: This should only be used when modifying an EXISTING\n"
+ "installation. If you are installing FreeBSD for the first time\n"
+ "then you should simply type Q when you're finished here and your\n"
+ "changes will be committed in one batch automatically at the end of\n"
+ "these questions.\n\n"
+ "Are you absolutely sure you want to do this now?")) {
+ variable_set2(DISK_LABELLED, "yes");
+ diskLabelCommit(NULL);
+ }
+ clear_wins();
+ break;
+
+ case '|':
+ if (!msgYesNo("Are you sure you want to go into Wizard mode?\n\n"
+ "This is an entirely undocumented feature which you are not\n"
+ "expected to understand!")) {
+ int i;
+ Device **devs;
+
+ dialog_clear();
+ end_dialog();
+ DialogActive = FALSE;
+ devs = deviceFind(NULL, DEVICE_TYPE_DISK);
+ if (!devs) {
+ msgConfirm("Can't find any disk devices!");
+ break;
+ }
+ for (i = 0; devs[i] && ((Disk *)devs[i]->private); i++) {
+ if (devs[i]->enabled)
+ slice_wizard(((Disk *)devs[i]->private));
+ }
+ if (((cp = variable_get(DISK_LABELLED)) == NULL) || (strcmp(cp, "written")))
+ variable_set2(DISK_LABELLED, "yes");
+ DialogActive = TRUE;
+ record_label_chunks(devs, dev);
+ clear_wins();
+ }
+ else
+ msg = "A most prudent choice!";
+ break;
+
+ case '\033': /* ESC */
+ case 'Q':
+ labeling = FALSE;
+ break;
+
+ default:
+ beep();
+ sprintf(_msg, "Invalid key %d - Type F1 or ? for help", key);
+ msg = _msg;
+ break;
+ }
+ if (label_chunk_info[here].type == PART_SLICE)
+ pslice_focus = here;
+ else
+ label_focus = here;
+ }
+ return DITEM_SUCCESS | DITEM_RESTORE;
+}
+
+static int
+diskLabelNonInteractive(Device *dev)
+{
+ char *cp;
+ PartType type;
+ PartInfo *p;
+ u_long flags = 0;
+ int i, status;
+ Device **devs;
+ Disk *d;
+
+ status = DITEM_SUCCESS;
+
+ cp = variable_get(VAR_DISK);
+ if (!cp) {
+ dialog_clear();
+ msgConfirm("diskLabel: No disk selected - can't label automatically.");
+ return DITEM_FAILURE;
+ }
+ devs = deviceFind(cp, DEVICE_TYPE_DISK);
+ if (!devs) {
+ msgConfirm("diskLabel: No disk device %s found!", cp);
+ return DITEM_FAILURE;
+ }
+ if (dev)
+ d = dev->private;
+ else
+ d = devs[0]->private;
+ record_label_chunks(devs, dev);
+ for (i = 0; label_chunk_info[i].c; i++) {
+ Chunk *c1 = label_chunk_info[i].c;
+
+ if (label_chunk_info[i].type == PART_SLICE) {
+ char name[512];
+ int entries = 1;
+
+ while (entries) {
+ snprintf(name, sizeof name, "%s-%d", c1->name, entries);
+ if ((cp = variable_get(name)) != NULL) {
+ int sz;
+ char typ[10], mpoint[50];
+
+ if (sscanf(cp, "%s %d %s", typ, &sz, mpoint) != 3) {
+ msgConfirm("For slice entry %s, got an invalid detail entry of: %s", c1->name, cp);
+ status = DITEM_FAILURE;
+ continue;
+ }
+ else {
+ Chunk *tmp;
+
+ if (!strcmp(typ, "swap")) {
+ type = PART_SWAP;
+ strcpy(mpoint, "SWAP");
+ }
+ else {
+ type = PART_FILESYSTEM;
+ if (!strcmp(mpoint, "/"))
+ flags |= CHUNK_IS_ROOT;
+ }
+ if (!sz)
+ sz = space_free(c1);
+ if (sz > space_free(c1)) {
+ msgConfirm("Not enough free space to create partition: %s", mpoint);
+ status = DITEM_FAILURE;
+ continue;
+ }
+ if (!(tmp = Create_Chunk_DWIM(d, c1, sz, part,
+ (type == PART_SWAP) ? FS_SWAP : FS_BSDFFS, flags))) {
+ msgConfirm("Unable to create from partition spec: %s. Too big?", cp);
+ status = DITEM_FAILURE;
+ break;
+ }
+ else {
+ tmp->private_data = new_part(mpoint, TRUE, sz);
+ tmp->private_free = safe_free;
+ status = DITEM_SUCCESS;
+ }
+ }
+ entries++;
+ }
+ else {
+ /* No more matches, leave the loop */
+ entries = 0;
+ }
+ }
+ }
+ else {
+ /* Must be something we can set a mountpoint for */
+ cp = variable_get(c1->name);
+ if (cp) {
+ char mpoint[50], do_newfs[8];
+ Boolean newfs = FALSE;
+
+ do_newfs[0] = '\0';
+ if (sscanf(cp, "%s %s", mpoint, do_newfs) != 2) {
+ dialog_clear();
+ msgConfirm("For slice entry %s, got an invalid detail entry of: %s", c1->name, cp);
+ status = DITEM_FAILURE;
+ continue;
+ }
+ newfs = toupper(do_newfs[0]) == 'Y' ? TRUE : FALSE;
+ if (c1->private_data) {
+ p = c1->private_data;
+ p->newfs = newfs;
+ strcpy(p->mountpoint, mpoint);
+ }
+ else {
+ c1->private_data = new_part(mpoint, newfs, 0);
+ c1->private_free = safe_free;
+ }
+ if (!strcmp(mpoint, "/"))
+ c1->flags |= CHUNK_IS_ROOT;
+ else
+ c1->flags &= ~CHUNK_IS_ROOT;
+ }
+ }
+ }
+ if (status == DITEM_SUCCESS)
+ variable_set2(DISK_LABELLED, "yes");
+ return status;
+}
diff --git a/usr.sbin/sysinstall/list.h b/usr.sbin/sysinstall/list.h
new file mode 100644
index 0000000..05a9fd5
--- /dev/null
+++ b/usr.sbin/sysinstall/list.h
@@ -0,0 +1,60 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last attempt in the `sysinstall' line, the next
+ * generation being slated for what's essentially a complete rewrite.
+ *
+ * $Id: list.h,v 1.1 1997/09/16 17:03:58 pst Exp $
+ *
+ * Copyright (c) 1997 FreeBSD, Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY PAUL TRAINA ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL PAUL TRAINA OR HIS KILLER RATS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+/* The structure */
+typedef struct _qelement {
+ struct _qelement *q_forw;
+ struct _qelement *q_back;
+} qelement;
+
+#define INITQUE(Xhead) { \
+ (Xhead).q_forw = &(Xhead); \
+ (Xhead).q_back = &(Xhead); \
+}
+
+#define EMPTYQUE(Xhead) \
+ ((Xhead).q_forw == &(Xhead))
+
+#define INSQUEUE(elem, pred) { \
+ register qelement *Xe = (qelement *) (elem); \
+ register qelement *Xp = (qelement *) (pred); \
+ Xp->q_forw = (Xe->q_forw = (Xe->q_back = Xp)->q_forw)->q_back = Xe; \
+}
+
+#define REMQUE(elem) { \
+ register qelement *Xe = (qelement *) (elem); \
+ (Xe->q_back->q_forw = Xe->q_forw)->q_back = Xe->q_back; \
+}
diff --git a/usr.sbin/sysinstall/main.c b/usr.sbin/sysinstall/main.c
new file mode 100644
index 0000000..d6d2726
--- /dev/null
+++ b/usr.sbin/sysinstall/main.c
@@ -0,0 +1,134 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last attempt in the `sysinstall' line, the next
+ * generation being slated for what's essentially a complete rewrite.
+ *
+ * $Id: main.c,v 1.46 1997/06/05 09:47:58 jkh Exp $
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include "sysinstall.h"
+#include <sys/signal.h>
+#include <sys/fcntl.h>
+
+static void
+screech(int sig)
+{
+ msgDebug("\007Signal %d caught! That's bad!\n", sig);
+ longjmp(BailOut, sig);
+}
+
+int
+main(int argc, char **argv)
+{
+ int choice, scroll, curr, max, status;
+
+ /* Catch fatal signals and complain about them if running as init */
+ if (getpid() == 1) {
+ signal(SIGBUS, screech);
+ signal(SIGSEGV, screech);
+ }
+
+ /* We don't work too well when running as non-root anymore */
+ if (geteuid() != 0) {
+ fprintf(stderr, "Error: This utility should only be run as root.\n");
+ return 1;
+ }
+
+ /* Set up whatever things need setting up */
+ systemInitialize(argc, argv);
+
+ /* Set default flag and variable values */
+ installVarDefaults(NULL);
+ installEnvironment();
+
+ if (argc > 1 && !strcmp(argv[1], "-fake")) {
+ variable_set2(VAR_DEBUG, "YES");
+ Fake = TRUE;
+ msgConfirm("I'll be just faking it from here on out, OK?");
+ }
+
+ /* Try to preserve our scroll-back buffer */
+ if (OnVTY) {
+ for (curr = 0; curr < 25; curr++)
+ putchar('\n');
+ }
+ /* Move stderr aside */
+ if (DebugFD)
+ dup2(DebugFD, 2);
+
+ /* Probe for all relevant devices on the system */
+ deviceGetAll();
+
+ /* First, see if we have any arguments to process (and argv[0] counts if it's not "sysinstall") */
+ if (!RunningAsInit) {
+ int i, start_arg;
+
+ if (!strstr(argv[0], "sysinstall"))
+ start_arg = 0;
+ else if (Fake)
+ start_arg = 2;
+ else
+ start_arg = 1;
+ for (i = start_arg; i < argc; i++) {
+ if (DITEM_STATUS(dispatchCommand(argv[i])) != DITEM_SUCCESS)
+ systemShutdown(1);
+ }
+ if (argc > start_arg)
+ systemShutdown(0);
+ }
+ else
+ dispatch_load_file_int(TRUE);
+
+ status = setjmp(BailOut);
+ if (status) {
+ msgConfirm("A signal %d was caught - I'm saving what I can and shutting\n"
+ "this thing down! Please report this unfortunate incident\n"
+ "to jkh@FreeBSD.org. If you can reproduce the problem, please\n"
+ "also turn Debug on in the Options menu for the extra information\n"
+ "it provides in debugging problems like this. Thanks!", 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 || !msgYesNo("Are you sure you wish to exit? The system will reboot\n"
+ "(be sure to remove any floppies from the drives)."))
+ 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..8154be6
--- /dev/null
+++ b/usr.sbin/sysinstall/media.c
@@ -0,0 +1,754 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last attempt in the `sysinstall' line, the next
+ * generation being slated to essentially a complete rewrite.
+ *
+ * $Id: media.c,v 1.86 1997/08/01 04:41:38 jkh Exp $
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include "sysinstall.h"
+#include <signal.h>
+#include <netdb.h>
+#include <sys/socket.h>
+#include <sys/param.h>
+#include <sys/errno.h>
+#include <sys/fcntl.h>
+#include <sys/stat.h>
+#include <sys/mman.h>
+#include <sys/wait.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <resolv.h>
+
+static Boolean got_intr = FALSE;
+
+/* timeout handler */
+static void
+handle_intr(int sig)
+{
+ msgDebug("User generated interrupt.\n");
+ got_intr = TRUE;
+}
+
+static int
+check_for_interrupt(void)
+{
+ if (got_intr) {
+ got_intr = FALSE;
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static int
+genericHook(dialogMenuItem *self, DeviceType type)
+{
+ Device **devs;
+
+ devs = deviceFind(self->prompt, type);
+ if (devs)
+ mediaDevice = devs[0];
+ return (devs ? DITEM_LEAVE_MENU : DITEM_FAILURE);
+}
+
+static int
+cdromHook(dialogMenuItem *self)
+{
+ return genericHook(self, DEVICE_TYPE_CDROM);
+}
+
+static void
+kickstart_dns(void)
+{
+ static Boolean initted = FALSE;
+ int time;
+ char *cp;
+
+ cp = variable_get(VAR_MEDIA_TIMEOUT);
+ if (!cp)
+ time = MEDIA_TIMEOUT;
+ else
+ time = atoi(cp);
+ if (!time)
+ time = 100;
+ if (!initted) {
+ res_init();
+ _res.retry = 2; /* 2 times seems a reasonable number to me */
+ _res.retrans = time / 2; /* so spend half our alloted time on each try */
+ initted = TRUE;
+ }
+}
+
+char *
+cpioVerbosity()
+{
+ char *cp = variable_get(VAR_CPIO_VERBOSITY);
+
+ if (cp && !strcmp(cp, "high"))
+ return "-v";
+ else if (cp && !strcmp(cp, "medium"))
+ return "-V";
+ return "";
+}
+
+void
+mediaClose(void)
+{
+ if (mediaDevice)
+ mediaDevice->shutdown(mediaDevice);
+ mediaDevice = NULL;
+}
+
+/*
+ * Return 1 if we successfully found and set the installation type to
+ * be a CD.
+ */
+int
+mediaSetCDROM(dialogMenuItem *self)
+{
+ Device **devs;
+ int cnt;
+
+ mediaClose();
+ devs = deviceFind(NULL, DEVICE_TYPE_CDROM);
+ cnt = deviceCount(devs);
+ if (!cnt) {
+ if (self) /* Interactive? */
+ msgConfirm("No CDROM devices found! Please check that your system's\n"
+ "configuration is correct and that the CDROM drive is of a supported\n"
+ "type. For more information, consult the hardware guide\n"
+ "in the Doc menu.");
+ return DITEM_FAILURE | DITEM_CONTINUE;
+ }
+ else if (cnt > 1) {
+ DMenu *menu;
+ int status;
+
+ menu = deviceCreateMenu(&MenuMediaCDROM, DEVICE_TYPE_CDROM, cdromHook, NULL);
+ if (!menu)
+ msgFatal("Unable to create CDROM menu! Something is seriously wrong.");
+ status = dmenuOpenSimple(menu, FALSE);
+ free(menu);
+ if (!status)
+ return DITEM_FAILURE | DITEM_RESTORE;
+ }
+ else
+ mediaDevice = devs[0];
+ return (mediaDevice ? DITEM_SUCCESS | DITEM_LEAVE_MENU : DITEM_FAILURE) | DITEM_RESTORE;
+}
+
+static int
+floppyHook(dialogMenuItem *self)
+{
+ return genericHook(self, DEVICE_TYPE_FLOPPY);
+}
+
+/*
+ * Return 1 if we successfully found and set the installation type to
+ * be a floppy
+ */
+int
+mediaSetFloppy(dialogMenuItem *self)
+{
+ Device **devs;
+ int cnt;
+
+ mediaClose();
+ devs = deviceFind(NULL, DEVICE_TYPE_FLOPPY);
+ cnt = deviceCount(devs);
+ if (!cnt) {
+ msgConfirm("No floppy devices found! Please check that your system's configuration\n"
+ "is correct. For more information, consult the hardware guide in the Doc\n"
+ "menu.");
+ return DITEM_FAILURE | DITEM_CONTINUE;
+ }
+ else if (cnt > 1) {
+ DMenu *menu;
+ int status;
+
+ menu = deviceCreateMenu(&MenuMediaFloppy, DEVICE_TYPE_FLOPPY, floppyHook, NULL);
+ if (!menu)
+ msgFatal("Unable to create Floppy menu! Something is seriously wrong.");
+ status = dmenuOpenSimple(menu, FALSE);
+ free(menu);
+ if (!status)
+ return DITEM_FAILURE | DITEM_RESTORE;
+ }
+ else
+ mediaDevice = devs[0];
+ return (mediaDevice ? DITEM_LEAVE_MENU : DITEM_FAILURE) | DITEM_RESTORE;
+}
+
+static int
+DOSHook(dialogMenuItem *self)
+{
+ return genericHook(self, DEVICE_TYPE_DOS);
+}
+
+/*
+ * Return 1 if we successfully found and set the installation type to
+ * be a DOS partition.
+ */
+int
+mediaSetDOS(dialogMenuItem *self)
+{
+ Device **devs;
+ int cnt;
+
+ mediaClose();
+ devs = deviceFind(NULL, DEVICE_TYPE_DOS);
+ cnt = deviceCount(devs);
+ if (!cnt) {
+ msgConfirm("No DOS primary partitions found! This installation method is unavailable");
+ return DITEM_FAILURE | DITEM_CONTINUE;
+ }
+ else if (cnt > 1) {
+ DMenu *menu;
+ int status;
+
+ menu = deviceCreateMenu(&MenuMediaDOS, DEVICE_TYPE_DOS, DOSHook, NULL);
+ if (!menu)
+ msgFatal("Unable to create DOS menu! Something is seriously wrong.");
+ status = dmenuOpenSimple(menu, FALSE);
+ free(menu);
+ if (!status)
+ return DITEM_FAILURE | DITEM_RESTORE;
+ }
+ else
+ mediaDevice = devs[0];
+ return (mediaDevice ? DITEM_LEAVE_MENU : DITEM_FAILURE) | DITEM_RESTORE;
+}
+
+static int
+tapeHook(dialogMenuItem *self)
+{
+ return genericHook(self, DEVICE_TYPE_TAPE);
+}
+
+/*
+ * Return 1 if we successfully found and set the installation type to
+ * be a tape drive.
+ */
+int
+mediaSetTape(dialogMenuItem *self)
+{
+ Device **devs;
+ int cnt;
+
+ mediaClose();
+ devs = deviceFind(NULL, DEVICE_TYPE_TAPE);
+ cnt = deviceCount(devs);
+ if (!cnt) {
+ msgConfirm("No tape drive devices found! Please check that your system's configuration\n"
+ "is correct. For more information, consult the hardware guide in the Doc\n"
+ "menu.");
+ return DITEM_FAILURE | DITEM_CONTINUE;
+ }
+ else if (cnt > 1) {
+ DMenu *menu;
+ int status;
+
+ menu = deviceCreateMenu(&MenuMediaTape, DEVICE_TYPE_TAPE, tapeHook, NULL);
+ if (!menu)
+ msgFatal("Unable to create tape drive menu! Something is seriously wrong.");
+ status = dmenuOpenSimple(menu, FALSE);
+ free(menu);
+ if (!status)
+ return DITEM_FAILURE | DITEM_RESTORE;
+ }
+ else
+ mediaDevice = devs[0];
+ if (mediaDevice) {
+ char *val;
+
+ val = msgGetInput("/usr/tmp", "Please enter the name of a temporary directory containing\n"
+ "sufficient space for holding the contents of this tape (or\n"
+ "tapes). The contents of this directory will be removed\n"
+ "after installation, so be sure to specify a directory that\n"
+ "can be erased afterwards!\n");
+ if (!val)
+ mediaDevice = NULL;
+ else
+ mediaDevice->private = strdup(val);
+ }
+ return (mediaDevice ? DITEM_LEAVE_MENU : DITEM_FAILURE) | DITEM_RESTORE;
+}
+
+/*
+ * Return 0 if we successfully found and set the installation type to
+ * be an ftp server
+ */
+int
+mediaSetFTP(dialogMenuItem *self)
+{
+ static Device ftpDevice;
+ char *cp, hostname[MAXHOSTNAMELEN], *dir;
+ extern int FtpPort;
+ static Device *networkDev = NULL;
+ int what = DITEM_RESTORE;
+
+ mediaClose();
+ cp = variable_get(VAR_FTP_PATH);
+ /* If we've been through here before ... */
+ if (!variable_get(VAR_NONINTERACTIVE))
+ if (networkDev && cp && msgYesNo("Re-use old FTP site selection values?"))
+ cp = NULL;
+
+ if (!cp) {
+ dialog_clear_norefresh();
+ if (!dmenuOpenSimple(&MenuMediaFTP, FALSE))
+ return DITEM_FAILURE | DITEM_RESTORE;
+ else
+ cp = variable_get(VAR_FTP_PATH);
+ what = DITEM_RESTORE;
+ }
+ if (!cp)
+ return DITEM_FAILURE | what;
+ else if (!strcmp(cp, "other")) {
+ variable_set2(VAR_FTP_PATH, "ftp://");
+ dialog_clear_norefresh();
+ cp = variable_get_value(VAR_FTP_PATH, "Please specify the URL of a FreeBSD distribution on a\n"
+ "remote ftp site. This site must accept either anonymous\n"
+ "ftp or you should have set an ftp username and password\n"
+ "in the Options screen.\n\n"
+ "A URL looks like this: ftp://<hostname>/<path>\n"
+ "Where <path> is relative to the anonymous ftp directory or the\n"
+ "home directory of the user being logged in as.");
+ if (!cp || !*cp || !strcmp(cp, "ftp://")) {
+ variable_unset(VAR_FTP_PATH);
+ return DITEM_FAILURE | what;
+ }
+ }
+ if (strncmp("ftp://", cp, 6)) {
+ msgConfirm("Sorry, %s is an invalid URL!", cp);
+ variable_unset(VAR_FTP_PATH);
+ return DITEM_FAILURE | what;
+ }
+ SAFE_STRCPY(ftpDevice.name, cp);
+ SAFE_STRCPY(hostname, cp + 6);
+
+ dialog_clear_norefresh();
+ if (!networkDev || msgYesNo("You've already done the network configuration once,\n"
+ "would you like to skip over it now?") != 0) {
+ if (networkDev)
+ networkDev->shutdown(networkDev);
+ if (!(networkDev = tcpDeviceSelect())) {
+ variable_unset(VAR_FTP_PATH);
+ return DITEM_FAILURE | what;
+ }
+ }
+ if (!networkDev->init(networkDev)) {
+ if (isDebug())
+ msgDebug("mediaSetFTP: Net device init failed.\n");
+ variable_unset(VAR_FTP_PATH);
+ return DITEM_FAILURE | what;
+ }
+ if ((cp = index(hostname, ':')) != NULL) {
+ *(cp++) = '\0';
+ FtpPort = strtol(cp, 0, 0);
+ }
+ else
+ FtpPort = 21;
+ if ((dir = index(cp ? cp : hostname, '/')) != NULL)
+ *(dir++) = '\0';
+ if (isDebug()) {
+ msgDebug("hostname = `%s'\n", hostname);
+ msgDebug("dir = `%s'\n", dir ? dir : "/");
+ msgDebug("port # = `%d'\n", FtpPort);
+ }
+ if (variable_get(VAR_NAMESERVER)) {
+ kickstart_dns();
+ if ((inet_addr(hostname) == INADDR_NONE) && (gethostbyname(hostname) == NULL)) {
+ msgConfirm("Cannot resolve hostname `%s'! Are you sure that your\n"
+ "name server, gateway and network interface are correctly configured?", hostname);
+ if (networkDev)
+ networkDev->shutdown(networkDev);
+ networkDev = NULL;
+ variable_unset(VAR_FTP_PATH);
+ return DITEM_FAILURE | what;
+ }
+ else
+ msgDebug("Found DNS entry for %s successfully..", hostname);
+ }
+ variable_set2(VAR_FTP_HOST, hostname);
+ variable_set2(VAR_FTP_DIR, dir ? dir : "/");
+ variable_set2(VAR_FTP_PORT, itoa(FtpPort));
+ ftpDevice.type = DEVICE_TYPE_FTP;
+ ftpDevice.init = mediaInitFTP;
+ ftpDevice.get = mediaGetFTP;
+ ftpDevice.shutdown = mediaShutdownFTP;
+ ftpDevice.private = networkDev;
+ mediaDevice = &ftpDevice;
+ return DITEM_SUCCESS | DITEM_LEAVE_MENU | what;
+}
+
+int
+mediaSetFTPActive(dialogMenuItem *self)
+{
+ variable_set2(VAR_FTP_STATE, "active");
+ return mediaSetFTP(self);
+}
+
+int
+mediaSetFTPPassive(dialogMenuItem *self)
+{
+ variable_set2(VAR_FTP_STATE, "passive");
+ return mediaSetFTP(self);
+}
+
+int
+mediaSetUFS(dialogMenuItem *self)
+{
+ static Device ufsDevice;
+ char *cp;
+
+ mediaClose();
+ dialog_clear_norefresh();
+ cp = variable_get_value(VAR_UFS_PATH, "Enter a fully qualified pathname for the directory\n"
+ "containing the FreeBSD distribution files:");
+ if (!cp)
+ return DITEM_FAILURE;
+ strcpy(ufsDevice.name, "ufs");
+ ufsDevice.type = DEVICE_TYPE_UFS;
+ ufsDevice.init = dummyInit;
+ ufsDevice.get = mediaGetUFS;
+ ufsDevice.shutdown = dummyShutdown;
+ ufsDevice.private = strdup(cp);
+ mediaDevice = &ufsDevice;
+ return DITEM_LEAVE_MENU;
+}
+
+int
+mediaSetNFS(dialogMenuItem *self)
+{
+ static Device nfsDevice;
+ static Device *networkDev = NULL;
+ char *cp, *idx;
+ char hostname[MAXHOSTNAMELEN];
+ mediaClose();
+ dialog_clear_norefresh();
+ cp = variable_get_value(VAR_NFS_PATH, "Please enter the full NFS file specification for the remote\n"
+ "host and directory containing the FreeBSD distribution files.\n"
+ "This should be in the format: hostname:/some/freebsd/dir");
+ if (!cp)
+ return DITEM_FAILURE;
+ SAFE_STRCPY(hostname, cp);
+ if (!(idx = index(hostname, ':'))) {
+ msgConfirm("Invalid NFS path specification. Must be of the form:\n"
+ "host:/full/pathname/to/FreeBSD/distdir");
+ return DITEM_FAILURE;
+ }
+ SAFE_STRCPY(nfsDevice.name, hostname);
+ *idx = '\0';
+ if (!networkDev || msgYesNo("You've already done the network configuration once,\n"
+ "would you like to skip over it now?") != 0) {
+ if (networkDev)
+ networkDev->shutdown(networkDev);
+ if (!(networkDev = tcpDeviceSelect()))
+ return DITEM_FAILURE;
+ }
+ if (!networkDev->init(networkDev)) {
+ if (isDebug())
+ msgDebug("mediaSetNFS: Net device init failed\n");
+ }
+ if (variable_get(VAR_NAMESERVER)) {
+ kickstart_dns();
+ if ((inet_addr(hostname) == INADDR_NONE) && (gethostbyname(hostname) == NULL)) {
+ msgConfirm("Cannot resolve hostname `%s'! Are you sure that your\n"
+ "name server, gateway and network interface are correctly configured?", hostname);
+ if (networkDev)
+ networkDev->shutdown(networkDev);
+ networkDev = NULL;
+ variable_unset(VAR_NFS_PATH);
+ return DITEM_FAILURE;
+ }
+ else
+ msgDebug("Found DNS entry for %s successfully..", hostname);
+ }
+ variable_set2(VAR_NFS_HOST, hostname);
+ nfsDevice.type = DEVICE_TYPE_NFS;
+ nfsDevice.init = mediaInitNFS;
+ nfsDevice.get = mediaGetNFS;
+ nfsDevice.shutdown = mediaShutdownNFS;
+ nfsDevice.private = networkDev;
+ mediaDevice = &nfsDevice;
+ return DITEM_LEAVE_MENU;
+}
+
+Boolean
+mediaExtractDistBegin(char *dir, int *fd, int *zpid, int *cpid)
+{
+ int i, pfd[2],qfd[2];
+
+ if (!dir)
+ dir = "/";
+ Mkdir(dir);
+ chdir(dir);
+ pipe(pfd);
+ pipe(qfd);
+ *zpid = fork();
+ if (!*zpid) {
+ char *gunzip = RunningAsInit ? "/stand/gunzip" : "/usr/bin/gunzip";
+
+ dup2(qfd[0], 0); close(qfd[0]);
+ dup2(pfd[1], 1); close(pfd[1]);
+ if (DebugFD != -1)
+ dup2(DebugFD, 2);
+ else {
+ close(2);
+ open("/dev/null", O_WRONLY);
+ }
+ close(qfd[1]);
+ close(pfd[0]);
+ i = execl(gunzip, gunzip, 0);
+ if (isDebug())
+ msgDebug("%s command returns %d status\n", gunzip, i);
+ exit(i);
+ }
+ *fd = qfd[1];
+ close(qfd[0]);
+ *cpid = fork();
+ if (!*cpid) {
+ char *cpio = RunningAsInit ? "/stand/cpio" : "/usr/bin/cpio";
+
+ dup2(pfd[0], 0); close(pfd[0]);
+ close(pfd[1]);
+ close(qfd[1]);
+ if (DebugFD != -1) {
+ dup2(DebugFD, 1);
+ dup2(DebugFD, 2);
+ }
+ else {
+ close(1); open("/dev/null", O_WRONLY);
+ dup2(1, 2);
+ }
+ if (strlen(cpioVerbosity()))
+ i = execl(cpio, cpio, "-idum", cpioVerbosity(), "--block-size", mediaTapeBlocksize(), 0);
+ else
+ i = execl(cpio, cpio, "-idum", "--block-size", mediaTapeBlocksize(), 0);
+ if (isDebug())
+ msgDebug("%s command returns %d status\n", cpio, i);
+ exit(i);
+ }
+ close(pfd[0]);
+ close(pfd[1]);
+ return TRUE;
+}
+
+Boolean
+mediaExtractDistEnd(int zpid, int cpid)
+{
+ int i,j;
+
+ i = waitpid(zpid, &j, 0);
+ /* Don't check exit status - gunzip seems to return a bogus one! */
+ if (i < 0) {
+ if (isDebug())
+ msgDebug("wait for gunzip returned status of %d!\n", i);
+ return FALSE;
+ }
+ i = waitpid(cpid, &j, 0);
+ if (i < 0 || WEXITSTATUS(j)) {
+ if (isDebug())
+ msgDebug("cpio returned error status of %d!\n", WEXITSTATUS(j));
+ return FALSE;
+ }
+ return TRUE;
+}
+
+Boolean
+mediaExtractDist(char *dir, char *dist, FILE *fp)
+{
+ int i, j, total, seconds, zpid, cpid, pfd[2], qfd[2];
+ char buf[BUFSIZ];
+ struct timeval start, stop;
+ struct sigaction new, old;
+
+ if (!dir)
+ dir = "/";
+
+ Mkdir(dir);
+ chdir(dir);
+ pipe(pfd); /* read end */
+ pipe(qfd); /* write end */
+ zpid = fork();
+ if (!zpid) {
+ char *gunzip = RunningAsInit ? "/stand/gunzip" : "/usr/bin/gunzip";
+
+ fclose(fp);
+ close(qfd[1]);
+ dup2(qfd[0], 0); close(qfd[0]);
+
+ close(pfd[0]);
+ dup2(pfd[1], 1); close(pfd[1]);
+
+ if (DebugFD != -1)
+ dup2(DebugFD, 2);
+ else {
+ close(2);
+ open("/dev/null", O_WRONLY);
+ }
+ i = execl(gunzip, gunzip, 0);
+ if (isDebug())
+ msgDebug("%s command returns %d status\n", gunzip, i);
+ exit(i);
+ }
+ cpid = fork();
+ if (!cpid) {
+ char *cpio = RunningAsInit ? "/stand/cpio" : "/usr/bin/cpio";
+
+ close(pfd[1]);
+ dup2(pfd[0], 0); close(pfd[0]);
+ close (qfd[0]); close(qfd[1]);
+ fclose(fp);
+ if (DebugFD != -1) {
+ dup2(DebugFD, 1);
+ dup2(DebugFD, 2);
+ }
+ else {
+ dup2(open("/dev/null", O_WRONLY), 1);
+ dup2(1, 2);
+ }
+ if (strlen(cpioVerbosity()))
+ i = execl(cpio, cpio, "-idum", cpioVerbosity(), "--block-size", mediaTapeBlocksize(), 0);
+ else
+ i = execl(cpio, cpio, "-idum", "--block-size", mediaTapeBlocksize(), 0);
+ if (isDebug())
+ msgDebug("%s command returns %d status\n", cpio, i);
+ exit(i);
+ }
+ close(pfd[0]); close(pfd[1]);
+ close(qfd[0]);
+
+ total = 0;
+ (void)gettimeofday(&start, (struct timezone *)0);
+
+ /* Make ^C abort the current transfer rather than the whole show */
+ new.sa_handler = handle_intr;
+ new.sa_flags = 0;
+ new.sa_mask = 0;
+ sigaction(SIGINT, &new, &old);
+
+ while ((i = fread(buf, 1, BUFSIZ, fp)) > 0) {
+ if (check_for_interrupt()) {
+ msgConfirm("Failure to read from media: User interrupt.");
+ break;
+ }
+ if (write(qfd[1], buf, i) != i) {
+ msgConfirm("Write error on transfer to cpio process, try of %d bytes.", i);
+ break;
+ }
+ else {
+ (void)gettimeofday(&stop, (struct timezone *)0);
+ stop.tv_sec = stop.tv_sec - start.tv_sec;
+ stop.tv_usec = stop.tv_usec - start.tv_usec;
+ if (stop.tv_usec < 0)
+ stop.tv_sec--, stop.tv_usec += 1000000;
+ seconds = stop.tv_sec + (stop.tv_usec / 1000000.0);
+ if (!seconds)
+ seconds = 1;
+ total += i;
+ msgInfo("%10d bytes read from %s dist @ %.1f KB/sec.",
+ total, dist, (total / seconds) / 1024.0);
+ }
+ }
+ sigaction(SIGINT, &old, NULL); /* restore sigint */
+ close(qfd[1]);
+
+ i = waitpid(zpid, &j, 0);
+ /* Don't check exit status - gunzip seems to return a bogus one! */
+ if (i < 0) {
+ if (isDebug())
+ msgDebug("wait for gunzip returned status of %d!\n", i);
+ return FALSE;
+ }
+ i = waitpid(cpid, &j, 0);
+ if (i < 0 || WEXITSTATUS(j)) {
+ if (isDebug())
+ msgDebug("cpio returned error status of %d!\n", WEXITSTATUS(j));
+ return FALSE;
+ }
+ return TRUE;
+}
+
+int
+mediaGetType(dialogMenuItem *self)
+{
+ return ((dmenuOpenSimple(&MenuMedia, FALSE) && mediaDevice) ? DITEM_SUCCESS : DITEM_FAILURE) | DITEM_RESTORE;
+}
+
+/* Return TRUE if all the media variables are set up correctly */
+Boolean
+mediaVerify(void)
+{
+ if (!mediaDevice)
+ return (DITEM_STATUS(mediaGetType(NULL)) == DITEM_SUCCESS);
+ return TRUE;
+}
+
+/* Set the FTP username and password fields */
+int
+mediaSetFTPUserPass(dialogMenuItem *self)
+{
+ char *pass;
+
+ dialog_clear_norefresh();
+ if (variable_get_value(VAR_FTP_USER, "Please enter the username you wish to login as:")) {
+ dialog_clear_norefresh();
+ DialogInputAttrs |= DITEM_NO_ECHO;
+ pass = variable_get_value(VAR_FTP_PASS, "Please enter the password for this user:");
+ DialogInputAttrs &= ~DITEM_NO_ECHO;
+ }
+ else
+ pass = NULL;
+ return (pass ? DITEM_SUCCESS : DITEM_FAILURE) | DITEM_RESTORE;
+}
+
+/* Set CPIO verbosity level */
+int
+mediaSetCPIOVerbosity(dialogMenuItem *self)
+{
+ char *cp = variable_get(VAR_CPIO_VERBOSITY);
+
+ if (!cp) {
+ msgConfirm("CPIO Verbosity is not set to anything!");
+ return DITEM_FAILURE;
+ }
+ else {
+ if (!strcmp(cp, "low"))
+ variable_set2(VAR_CPIO_VERBOSITY, "medium");
+ else if (!strcmp(cp, "medium"))
+ variable_set2(VAR_CPIO_VERBOSITY, "high");
+ else /* must be "high" - wrap around */
+ variable_set2(VAR_CPIO_VERBOSITY, "low");
+ }
+ return DITEM_SUCCESS;
+}
diff --git a/usr.sbin/sysinstall/menus.c b/usr.sbin/sysinstall/menus.c
new file mode 100644
index 0000000..375cef1
--- /dev/null
+++ b/usr.sbin/sysinstall/menus.c
@@ -0,0 +1,1465 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last program in the `sysinstall' line - the next
+ * generation being essentially a complete rewrite.
+ *
+ * $Id: menus.c,v 1.149 1997/11/04 23:44:22 jkh Exp $
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include "sysinstall.h"
+
+/* Miscellaneous work routines for menus */
+static int
+setSrc(dialogMenuItem *self)
+{
+ Dists |= DIST_SRC;
+ SrcDists = DIST_SRC_ALL | DIST_SRC_SMAILCF;
+ return DITEM_SUCCESS | DITEM_REDRAW;
+}
+
+static int
+clearSrc(dialogMenuItem *self)
+{
+ Dists &= ~DIST_SRC;
+ SrcDists = 0;
+ return DITEM_SUCCESS | DITEM_REDRAW;
+}
+
+#ifndef USE_XIG_ENVIRONMENT
+static int
+setX11All(dialogMenuItem *self)
+{
+ XF86Dists = DIST_XF86_ALL;
+ XF86ServerDists = DIST_XF86_SERVER_ALL;
+ XF86FontDists = DIST_XF86_FONTS_ALL;
+ Dists |= DIST_XF86;
+ return DITEM_SUCCESS | DITEM_REDRAW;
+}
+
+static int
+clearX11All(dialogMenuItem *self)
+{
+ XF86Dists = 0;
+ XF86ServerDists = 0;
+ XF86FontDists = 0;
+ Dists &= ~DIST_XF86;
+ return DITEM_SUCCESS | DITEM_REDRAW;
+}
+
+static int
+setX11Misc(dialogMenuItem *self)
+{
+ XF86Dists |= DIST_XF86_MISC_ALL;
+ Dists |= DIST_XF86;
+ return DITEM_SUCCESS | DITEM_REDRAW;
+}
+
+static int
+clearX11Misc(dialogMenuItem *self)
+{
+ XF86Dists &= ~DIST_XF86_MISC_ALL;
+ if (!XF86ServerDists && !XF86FontDists)
+ Dists &= ~DIST_XF86;
+ return DITEM_SUCCESS | DITEM_REDRAW;
+}
+
+static int
+setX11Servers(dialogMenuItem *self)
+{
+ XF86Dists |= DIST_XF86_SERVER;
+ XF86ServerDists = DIST_XF86_SERVER_ALL;
+ return DITEM_SUCCESS | DITEM_REDRAW;
+}
+
+static int
+clearX11Servers(dialogMenuItem *self)
+{
+ XF86Dists &= ~DIST_XF86_SERVER;
+ XF86ServerDists = 0;
+ return DITEM_SUCCESS | DITEM_REDRAW;
+}
+
+static int
+setX11Fonts(dialogMenuItem *self)
+{
+ XF86Dists |= DIST_XF86_FONTS;
+ XF86FontDists = DIST_XF86_FONTS_ALL;
+ return DITEM_SUCCESS | DITEM_REDRAW;
+}
+
+static int
+clearX11Fonts(dialogMenuItem *self)
+{
+ XF86Dists &= ~DIST_XF86_FONTS;
+ XF86FontDists = 0;
+ return DITEM_SUCCESS | DITEM_REDRAW;
+}
+#endif /* !USE_XIG_ENVIRONMENT */
+
+#define IS_DEVELOPER(dist, extra) ((((dist) & (_DIST_DEVELOPER | (extra))) == (_DIST_DEVELOPER | (extra))) || \
+ (((dist) & (_DIST_DEVELOPER | DIST_DES | (extra))) == (_DIST_DEVELOPER | DIST_DES | (extra))))
+#define IS_USER(dist, extra) ((((dist) & (_DIST_USER | (extra))) == (_DIST_USER | (extra))) || \
+ (((dist) & (_DIST_USER | DIST_DES | (extra))) == (_DIST_USER | DIST_DES | (extra))))
+
+static int
+checkDistDeveloper(dialogMenuItem *self)
+{
+ return (IS_DEVELOPER(Dists, 0) && SrcDists == DIST_SRC_ALL);
+}
+
+static int
+checkDistXDeveloper(dialogMenuItem *self)
+{
+ return (IS_DEVELOPER(Dists, DIST_XF86) && SrcDists == DIST_SRC_ALL);
+}
+
+static int
+checkDistKernDeveloper(dialogMenuItem *self)
+{
+ return (IS_DEVELOPER(Dists, 0) && SrcDists == DIST_SRC_SYS);
+}
+
+static int
+checkDistUser(dialogMenuItem *self)
+{
+ return (IS_USER(Dists, 0));
+}
+
+static int
+checkDistXUser(dialogMenuItem *self)
+{
+ return (IS_USER(Dists, DIST_XF86));
+}
+
+static int
+checkDistMinimum(dialogMenuItem *self)
+{
+ return (Dists == DIST_BIN);
+}
+
+static int
+checkDistEverything(dialogMenuItem *self)
+{
+#ifdef USE_XIG_ENVIRONMENT
+ return (Dists == DIST_ALL && SrcDists == DIST_SRC_ALL);
+#else
+ return (Dists == DIST_ALL && SrcDists == DIST_SRC_ALL && XF86Dists == DIST_XF86_ALL &&
+ XF86ServerDists == DIST_XF86_SERVER_ALL && XF86FontDists == DIST_XF86_FONTS_ALL);
+#endif
+}
+
+static int
+DESFlagCheck(dialogMenuItem *item)
+{
+ return DESDists;
+}
+
+static int
+srcFlagCheck(dialogMenuItem *item)
+{
+ return SrcDists;
+}
+
+#ifndef USE_XIG_ENVIRONMENT
+static int
+x11FlagCheck(dialogMenuItem *item)
+{
+ return XF86Dists;
+}
+#endif
+
+static int
+checkTrue(dialogMenuItem *item)
+{
+ return TRUE;
+}
+
+/* All the system menus go here.
+ *
+ * Hardcoded things like version number strings will disappear from
+ * these menus just as soon as I add the code for doing inline variable
+ * expansion.
+ */
+
+DMenu MenuIndex = {
+ DMENU_NORMAL_TYPE,
+ "Glossary of functions",
+ "This menu contains an alphabetized index of the top level functions in\n"
+ "this program (sysinstall). Invoke an option by pressing [ENTER].\n"
+ "Leave the index page by selecting Cancel [TAB-ENTER].",
+ "Use PageUp or PageDown to move through this menu faster!",
+ NULL,
+ { { "Anon FTP", "Configure anonymous FTP logins.", dmenuVarCheck, configAnonFTP, NULL, "anon_ftp" },
+ { "Commit", "Commit any pending actions (dangerous!)", NULL, installCustomCommit },
+ { "Console settings", "Customize system console behavior.", NULL, dmenuSubmenu, NULL, &MenuSyscons },
+ { "Configure", "The system configuration menu.", NULL, dmenuSubmenu, NULL, &MenuConfigure },
+ { "Defaults, Load", "Load default settings.", NULL, dispatch_load_floppy },
+ { "Device, Mouse", "The mouse configuration menu.", NULL, dmenuSubmenu, NULL, &MenuMouse },
+ { "Dists, All", "Root of the distribution tree.", NULL, dmenuSubmenu, NULL, &MenuDistributions },
+ { "Dists, Basic", "Basic FreeBSD distribution menu.", NULL, dmenuSubmenu, NULL, &MenuSubDistributions },
+ { "Dists, DES", "DES distribution menu.", NULL, dmenuSubmenu, NULL, &MenuDESDistributions },
+ { "Dists, Developer", "Select developer's distribution.", checkDistDeveloper, distSetDeveloper },
+ { "Dists, Src", "Src distribution menu.", NULL, dmenuSubmenu, NULL, &MenuSrcDistributions },
+ { "Dists, X Developer", "Select X developer's distribution.", checkDistXDeveloper, distSetXDeveloper },
+ { "Dists, Kern Developer", "Select kernel developer's distribution.", checkDistKernDeveloper, distSetKernDeveloper },
+ { "Dists, User", "Select average user distribution.", checkDistUser, distSetUser },
+ { "Dists, X User", "Select average X user distribution.", checkDistXUser, distSetXUser },
+ { "Distributions, Adding", "Installing additional distribution sets", NULL, distExtractAll },
+#ifndef USE_XIG_ENVIRONMENT
+ { "Distributions, XFree86","XFree86 distribution menu.", NULL, distSetXF86 },
+#endif
+ { "Documentation", "Installation instructions, README, etc.", NULL, dmenuSubmenu, NULL, &MenuDocumentation },
+ { "Doc, README", "The distribution README file.", NULL, dmenuDisplayFile, NULL, "readme" },
+ { "Doc, Hardware", "The distribution hardware guide.", NULL, dmenuDisplayFile, NULL, "hardware" },
+ { "Doc, Install", "The distribution installation guide.", NULL, dmenuDisplayFile, NULL, "install" },
+ { "Doc, Copyright", "The distribution copyright notices.", NULL, dmenuDisplayFile, NULL, "COPYRIGHT" },
+ { "Doc, Release", "The distribution release notes.", NULL, dmenuDisplayFile, NULL, "relnotes" },
+ { "Doc, HTML", "The HTML documentation menu.", NULL, docBrowser },
+ { "Emergency shell", "Start an Emergency Holographic shell.", NULL, installFixitHoloShell },
+ { "Fixit", "Repair mode with CDROM or fixit floppy.", NULL, dmenuSubmenu, NULL, &MenuFixit },
+ { "FTP sites", "The FTP mirror site listing.", NULL, dmenuSubmenu, NULL, &MenuMediaFTP },
+ { "Gateway", "Set flag to route packets between interfaces.", dmenuVarCheck, dmenuToggleVariable, NULL, "gateway=YES" },
+ { "HTML Docs", "The HTML documentation menu", NULL, docBrowser },
+ { "Install, Novice", "A novice system installation.", NULL, installNovice },
+ { "Install, Express", "An express system installation.", NULL, installExpress },
+ { "Install, Custom", "The custom installation menu", NULL, dmenuSubmenu, NULL, &MenuInstallCustom },
+ { "Label", "The disk Label editor", NULL, diskLabelEditor },
+ { "Media", "Top level media selection menu.", NULL, dmenuSubmenu, NULL, &MenuMedia },
+ { "Media, Tape", "Select tape installation media.", NULL, mediaSetTape },
+ { "Media, NFS", "Select NFS installation media.", NULL, mediaSetNFS },
+ { "Media, Floppy", "Select floppy installation media.", NULL, mediaSetFloppy },
+ { "Media, CDROM", "Select CDROM installation media.", NULL, mediaSetCDROM },
+ { "Media, DOS", "Select DOS installation media.", NULL, mediaSetDOS },
+ { "Media, UFS", "Select UFS installation media.", NULL, mediaSetUFS },
+ { "Media, FTP", "Select FTP installation media.", NULL, mediaSetFTP },
+ { "Media, FTP Passive", "Select passive FTP installation media.", NULL, mediaSetFTPPassive },
+ { "Network Interfaces", "Configure network interfaces", NULL, tcpMenuSelect },
+ { "Networking Services", "The network services menu.", NULL, dmenuSubmenu, NULL, &MenuNetworking },
+ { "NFS, client", "Set NFS client flag.", dmenuVarCheck, dmenuToggleVariable, NULL, "nfs_client_enable=YES" },
+ { "NFS, server", "Set NFS server flag.", dmenuVarCheck, configNFSServer, NULL, "nfs_server_enable" },
+ { "NTP Menu", "The NTP configuration menu.", NULL, dmenuSubmenu, NULL, &MenuNTP },
+ { "Options", "The options editor.", NULL, optionsEditor },
+ { "Packages", "The packages collection", NULL, configPackages },
+ { "Partition", "The disk Slice (PC-style partition) Editor", NULL, diskPartitionEditor },
+ { "PCNFSD", "Run authentication server for PC-NFS.", dmenuVarCheck, configPCNFSD, NULL, "pcnfsd" },
+ { "Register", "Register yourself or company as a FreeBSD user.", dmenuVarCheck, configRegister, NULL, "registered" },
+ { "Root Password", "Set the system manager's password.", NULL, dmenuSystemCommand, NULL, "passwd root" },
+ { "Root Password", "Set the system manager's password.", NULL, dmenuSystemCommand, NULL, "passwd root" },
+ { "Router", "Select routing daemon (default: routed)", NULL, configRouter, NULL, "router" },
+ { "Syscons", "The system console configuration menu.", NULL, dmenuSubmenu, NULL, &MenuSyscons },
+ { "Syscons, Font", "The console screen font.", NULL, dmenuSubmenu, NULL, &MenuSysconsFont },
+ { "Syscons, Keymap", "The console keymap configuration menu.", NULL, dmenuSubmenu, NULL, &MenuSysconsKeymap },
+ { "Syscons, Keyrate", "The console key rate configuration menu.", NULL, dmenuSubmenu, NULL, &MenuSysconsKeyrate },
+ { "Syscons, Saver", "The console screen saver configuration menu.", NULL, dmenuSubmenu, NULL, &MenuSysconsSaver },
+ { "Syscons, Screenmap", "The console screenmap configuration menu.", NULL, dmenuSubmenu, NULL, &MenuSysconsScrnmap },
+ { "Time Zone", "Set the system's time zone.", NULL, dmenuSystemCommand, NULL, "tzsetup" },
+ { "Upgrade", "Upgrade an existing system.", NULL, installUpgrade },
+ { "Usage", "Quick start - How to use this menu system.", NULL, dmenuDisplayFile, NULL, "usage" },
+ { "User Management", "Add user and group information.", NULL, dmenuSubmenu, NULL, &MenuUsermgmt },
+#ifndef USE_XIG_ENVIRONMENT
+ { "XFree86, Fonts", "XFree86 Font selection menu.", NULL, dmenuSubmenu, NULL, &MenuXF86SelectFonts },
+ { "XFree86, Server", "XFree86 Server selection menu.", NULL, dmenuSubmenu, NULL, &MenuXF86SelectServer },
+ { "XFree86, PC98 Server", "XFree86 PC98 Server selection menu.", NULL, dmenuSubmenu, NULL, &MenuXF86SelectPC98Server },
+#endif
+ { NULL } },
+};
+
+/* The initial installation menu */
+DMenu MenuInitial = {
+ DMENU_NORMAL_TYPE,
+ "Welcome to FreeBSD! [" RELEASE_NAME "]", /* title */
+ "This is the main menu of the FreeBSD installation system. Please\n" /* prompt */
+ "select one of the options below by using the arrow keys or typing the\n"
+ "first character of the option name you're interested in. Invoke an\n"
+ "option by pressing [ENTER] or [TAB-ENTER] to exit the installation.",
+ "Press F1 for Installation Guide", /* help line */
+ "install", /* help file */
+ { { "Select" },
+ { "Exit Install", NULL, NULL, dmenuExit },
+ { "1 Usage", "Quick start - How to use this menu system", NULL, dmenuDisplayFile, NULL, "usage" },
+ { "2 Doc", "Installation instructions, README, etc.", NULL, dmenuSubmenu, NULL, &MenuDocumentation },
+ { "3 Keymap", "Select keyboard type", NULL, dmenuSubmenu, NULL, &MenuSysconsKeymap },
+ { "4 Options", "View/Set various installation options", NULL, optionsEditor },
+ { "5 Novice", "Begin a novice installation (for beginners)", NULL, installNovice },
+ { "6 Express", "Begin a quick installation (for the impatient)", NULL, installExpress },
+ { "7 Custom", "Begin a custom installation (for experts)", NULL, dmenuSubmenu, NULL, &MenuInstallCustom },
+ { "8 Fixit", "Enter repair mode with CDROM/floppy or start shell", NULL, dmenuSubmenu, NULL, &MenuFixit },
+ { "9 Upgrade", "Upgrade an existing system", NULL, installUpgrade },
+ { "c Configure", "Do post-install configuration of FreeBSD", NULL, dmenuSubmenu, NULL, &MenuConfigure },
+ { "l Load Config","Load default install configuration", NULL, dispatch_load_floppy },
+ { "0 Index", "Glossary of functions", NULL, dmenuSubmenu, NULL, &MenuIndex },
+ { NULL } },
+};
+
+/* The main documentation menu */
+DMenu MenuDocumentation = {
+ DMENU_NORMAL_TYPE,
+ "Documentation for FreeBSD " RELEASE_NAME,
+ "If you are at all unsure about the configuration of your hardware\n"
+ "or are looking to build a system specifically for FreeBSD, read the\n"
+ "Hardware guide! New users should also read the Install document for\n"
+ "a step-by-step tutorial on installing FreeBSD. For general information,\n"
+ "consult the README file.",
+ "Confused? Press F1 for help.",
+ "usage",
+ { { "1 README", "A general description of FreeBSD. Read this!", NULL, dmenuDisplayFile, NULL, "readme" },
+ { "2 Hardware", "The FreeBSD survival guide for PC hardware.", NULL, dmenuDisplayFile, NULL, "hardware" },
+ { "3 Install", "A step-by-step guide to installing FreeBSD.", NULL, dmenuDisplayFile, NULL, "install" },
+ { "4 Copyright", "The FreeBSD Copyright notices.", NULL, dmenuDisplayFile, NULL, "COPYRIGHT" },
+ { "5 Release" ,"The release notes for this version of FreeBSD.", NULL, dmenuDisplayFile, NULL, "relnotes" },
+ { "6 Shortcuts", "Creating shortcuts to sysinstall.", NULL, dmenuDisplayFile, NULL, "shortcuts" },
+ { "7 HTML Docs", "Go to the HTML documentation menu (post-install).", NULL, docBrowser },
+ { "0 Exit", "Exit this menu (returning to previous)", NULL, dmenuExit },
+ { NULL } },
+};
+
+static int
+whichMouse(dialogMenuItem *self)
+{
+ int i;
+ char buf[BUFSIZ];
+
+ if (!file_readable("/dev/mouse"))
+ return FALSE;
+ if ((i = readlink("/dev/mouse", buf, sizeof buf)) == -1)
+ return FALSE;
+ buf[i] = '\0';
+ if (!strcmp(self->prompt, "COM1"))
+ return !strcmp(buf, "/dev/cuaa0");
+ else if (!strcmp(self->prompt, "COM2"))
+ return !strcmp(buf, "/dev/cuaa1");
+ if (!strcmp(self->prompt, "COM3"))
+ return !strcmp(buf, "/dev/cuaa2");
+ if (!strcmp(self->prompt, "COM4"))
+ return !strcmp(buf, "/dev/cuaa3");
+ if (!strcmp(self->prompt, "BusMouse"))
+ return !strcmp(buf, "/dev/mse0");
+ if (!strcmp(self->prompt, "PS/2"))
+ return !strcmp(buf, "/dev/psm0");
+ return FALSE;
+}
+
+DMenu MenuMouse = {
+ DMENU_RADIO_TYPE | DMENU_SELECTION_RETURNS,
+ "Please select your mouse type from the following menu",
+ "There are many different types of mice currently on the market,\n"
+ "but this configuration menu should at least narrow down the choices\n"
+ "somewhat. Once you've selected one of the below, you can specify\n"
+ "/dev/mouse as your mouse device when running the X configuration\n"
+ "utility (see Configuration menu). Please note that for PS/2 mice,\n"
+ "you need to enable the psm driver in the kernel configuration menu\n"
+ "when installing for the first time.",
+ "For more information, visit the Documentation menu",
+ NULL,
+ { { "COM1", "Serial mouse on COM1", whichMouse, dmenuSystemCommand, NULL,
+ "ln -fs /dev/cuaa0 /dev/mouse", '(', '*', ')', 1 },
+ { "COM2", "Serial mouse on COM2", whichMouse, dmenuSystemCommand, NULL,
+ "ln -fs /dev/cuaa1 /dev/mouse", '(', '*', ')', 1 },
+ { "COM3", "Serial mouse on COM3", whichMouse, dmenuSystemCommand, NULL,
+ "ln -fs /dev/cuaa2 /dev/mouse", '(', '*', ')', 1 },
+ { "COM4", "Serial mouse on COM4", whichMouse, dmenuSystemCommand, NULL,
+ "ln -fs /dev/cuaa3 /dev/mouse", '(', '*', ')', 1 },
+ { "BusMouse", "Logitech or ATI bus mouse", whichMouse, dmenuSystemCommand, NULL,
+ "ln -fs /dev/mse0 /dev/mouse", '(', '*', ')', 1 },
+ { "PS/2", "PS/2 style mouse (must enable psm0 device)", whichMouse, dmenuSystemCommand, NULL,
+ "ln -fs /dev/psm0 /dev/mouse", '(', '*', ')', 1 },
+ { NULL } },
+};
+
+#ifndef USE_XIG_ENVIRONMENT
+DMenu MenuXF86Config = {
+ DMENU_NORMAL_TYPE | DMENU_SELECTION_RETURNS,
+ "Please select the XFree86 configuration tool you want to use.",
+ "The first tool, XF86Setup, is fully graphical and requires the\n"
+ "VGA16 server in order to work (should have been selected by\n"
+ "default, but if you de-selected it then you won't be able to\n"
+ "use this fancy setup tool). The second tool, xf86config, is\n"
+ "a more simplistic shell-script based tool and less friendly to\n"
+ "new users, but it may work in situations where the fancier one\n"
+ "does not.",
+ "Press F1 to read the XFree86 release notes for FreeBSD",
+ "XF86",
+ { { "XF86Setup", "Use the fully graphical XFree86 configuration tool.",
+ NULL, dmenuSetVariable, NULL, VAR_XF86_CONFIG "=XF86Setup" },
+ { "xf86config", "Use the shell-script based XFree86 configuration tool.",
+ NULL, dmenuSetVariable, NULL, VAR_XF86_CONFIG "=xf86config" },
+ { NULL } },
+};
+#endif
+
+DMenu MenuMediaCDROM = {
+ DMENU_NORMAL_TYPE | DMENU_SELECTION_RETURNS,
+ "Choose a CDROM type",
+ "FreeBSD can be installed directly from a CDROM containing a valid\n"
+ "FreeBSD distribution. If you are seeing this menu it is because\n"
+ "more than one CDROM drive was found on your system. Please select one\n"
+ "of the following CDROM drives as your installation drive.",
+ "Press F1 to read the installation guide",
+ "install",
+ { { NULL } },
+};
+
+DMenu MenuMediaFloppy = {
+ DMENU_NORMAL_TYPE | DMENU_SELECTION_RETURNS,
+ "Choose a Floppy drive",
+ "You have more than one floppy drive. Please chose which drive\n"
+ "you would like to use.",
+ NULL,
+ NULL,
+ { { NULL } },
+};
+
+DMenu MenuMediaDOS = {
+ DMENU_NORMAL_TYPE | DMENU_SELECTION_RETURNS,
+ "Choose a DOS partition",
+ "FreeBSD can be installed directly from a DOS partition\n"
+ "assuming, of course, that you have copied the relevant\n"
+ "distributions into your DOS partition before starting this\n"
+ "installation. If this is not the case then you should reboot\n"
+ "DOS at this time and copy the distributions you wish to install\n"
+ "into a \"FREEBSD\" subdirectory on one of your DOS partitions.\n"
+ "Otherwise, please select the DOS partition containing the FreeBSD\n"
+ "distribution files.",
+ "Press F1 to read the installation guide",
+ "install",
+ { { NULL } },
+};
+
+DMenu MenuMediaFTP = {
+ DMENU_NORMAL_TYPE | DMENU_SELECTION_RETURNS,
+ "Please select a FreeBSD FTP distribution site",
+ "Please select the site closest to you or \"other\" if you'd like to\n"
+ "specify a different choice. Also note that not every site listed here\n"
+ "carries more than the base distribution kits. Only the Primary site is\n"
+ "guaranteed to carry the full range of possible distributions.",
+ "Select a site that's close!",
+ "install",
+ { { "Primary Site", "ftp.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.freebsd.org/pub/FreeBSD/" },
+ { "URL", "Specify some other ftp site by URL", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=other" },
+ { "3.0 SNAP Server", "current.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://current.freebsd.org/pub/FreeBSD/" },
+ { "2.2 SNAP Server", "releng22.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://releng22.freebsd.org/pub/FreeBSD/" },
+ { "2.1 SNAP Server", "releng210.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://releng210.freebsd.org/pub/FreeBSD/" },
+ { "Argentina", "ftp.ar.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.ar.freebsd.org/pub/FreeBSD/" },
+ { "Australia", "ftp.au.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.au.freebsd.org/pub/FreeBSD/" },
+ { "Australia #2", "ftp2.au.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp2.au.freebsd.org/pub/FreeBSD/" },
+ { "Australia #3", "ftp3.au.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp3.au.freebsd.org/pub/FreeBSD/" },
+ { "Australia #4", "ftp4.au.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp4.au.freebsd.org/pub/FreeBSD/" },
+ { "Australia #5", "ftp5.au.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp5.au.freebsd.org/pub/FreeBSD/" },
+ { "Brazil", "ftp.br.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.br.freebsd.org/pub/FreeBSD/" },
+ { "Brazil #2", "ftp2.br.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp2.br.freebsd.org/pub/FreeBSD/" },
+ { "Brazil #3", "ftp3.br.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp3.br.freebsd.org/pub/FreeBSD/" },
+ { "Brazil #4", "ftp4.br.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp4.br.freebsd.org/pub/FreeBSD/" },
+ { "Brazil #5", "ftp5.br.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp5.br.freebsd.org/pub/FreeBSD/" },
+ { "Brazil #6", "ftp6.br.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp6.br.freebsd.org/pub/FreeBSD/" },
+ { "Brazil #7", "ftp7.br.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp7.br.freebsd.org/pub/FreeBSD/" },
+ { "Canada", "ftp.ca.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.ca.freebsd.org/pub/FreeBSD/" },
+ { "Czech Republic", "ftp.cz.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.cz.freebsd.org/pub/FreeBSD/" },
+ { "Estonia", "ftp.ee.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.ee.freebsd.org/pub/FreeBSD/" },
+ { "Finland", "ftp.fi.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.fi.freebsd.org/pub/FreeBSD/" },
+ { "France", "ftp.fr.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.fr.freebsd.org/pub/FreeBSD/" },
+ { "France #2", "ftp2.fr.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp2.fr.freebsd.org/pub/FreeBSD/" },
+ { "Germany", "ftp.de.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.de.freebsd.org/pub/FreeBSD/" },
+ { "Germany #2", "ftp2.de.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp2.de.freebsd.org/pub/FreeBSD/" },
+ { "Germany #3", "ftp3.de.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp3.de.freebsd.org/pub/FreeBSD/" },
+ { "Germany #4", "ftp4.de.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp4.de.freebsd.org/pub/FreeBSD/" },
+ { "Germany #5", "ftp5.de.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp5.de.freebsd.org/pub/FreeBSD/" },
+ { "Germany #6", "ftp6.de.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp6.de.freebsd.org/pub/FreeBSD/" },
+ { "Germany #7", "ftp7.de.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp7.de.freebsd.org/pub/FreeBSD/" },
+ { "Holland", "ftp.nl.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.nl.freebsd.org/pub/FreeBSD/" },
+ { "Hong Kong", "ftp.hk.super.net", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.hk.super.net/pub/FreeBSD/" },
+ { "Iceland", "ftp.is.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.is.freebsd.org/pub/FreeBSD/" },
+ { "Ireland", "ftp.ie.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.ie.freebsd.org/pub/FreeBSD/" },
+ { "Israel", "ftp.il.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.il.freebsd.org/pub/FreeBSD/" },
+ { "Israel #2", "ftp2.il.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp2.il.freebsd.org/pub/FreeBSD/" },
+ { "Japan", "ftp.jp.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.jp.freebsd.org/pub/FreeBSD/" },
+ { "Japan #2", "ftp2.jp.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp2.jp.freebsd.org/pub/FreeBSD/" },
+ { "Japan #3", "ftp3.jp.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp3.jp.freebsd.org/pub/FreeBSD/" },
+ { "Japan #4", "ftp4.jp.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp4.jp.freebsd.org/pub/FreeBSD/" },
+ { "Japan #5", "ftp5.jp.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp5.jp.freebsd.org/pub/FreeBSD/" },
+ { "Japan #6", "ftp6.jp.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp6.jp.freebsd.org/pub/FreeBSD/" },
+ { "Korea", "ftp.kr.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.kr.freebsd.org/pub/FreeBSD/" },
+ { "Korea #2", "ftp2.kr.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp2.kr.freebsd.org/pub/FreeBSD/" },
+ { "Poland", "ftp.pl.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.pl.freebsd.org/pub/FreeBSD/" },
+ { "Portugal", "ftp.pt.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.pt.freebsd.org/pub/misc/FreeBSD/" },
+ { "Portugal #2", "ftp2.pt.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp2.pt.freebsd.org/pub/FreeBSD/" },
+ { "Russia", "ftp.ru.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.ru.freebsd.org/pub/FreeBSD/" },
+ { "Russia #2", "ftp2.ru.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp2.ru.freebsd.org/pub/FreeBSD/" },
+ { "Russia #3", "ftp3.ru.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp3.ru.freebsd.org/pub/FreeBSD/" },
+ { "South Africa", "ftp.za.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.za.freebsd.org/pub/FreeBSD/" },
+ { "South Africa #2", "ftp2.za.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp2.za.freebsd.org/pub/FreeBSD/" },
+ { "South Africa #3", "ftp3.za.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp3.za.freebsd.org/pub/FreeBSD/" },
+ { "South Africa #4", "ftp4.za.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp4.za.freebsd.org/pub/FreeBSD/" },
+ { "Sweden", "ftp.se.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.se.freebsd.org/pub/FreeBSD/" },
+ { "Sweden #2", "ftp2.se.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp2.se.freebsd.org/pub/FreeBSD/" },
+ { "Sweden #3", "ftp3.se.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp3.se.freebsd.org/pub/FreeBSD/" },
+ { "Taiwan", "ftp.tw.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.tw.freebsd.org/pub/FreeBSD" },
+ { "Taiwan #2", "ftp2.tw.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp2.tw.freebsd.org/pub/FreeBSD" },
+ { "Taiwan #3", "ftp3.tw.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp3.tw.freebsd.org/pub/FreeBSD/" },
+ { "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/pub/FreeBSD/" },
+ { "UK #2", "ftp2.uk.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp2.uk.freebsd.org/pub/FreeBSD/" },
+ { "UK #3", "ftp3.uk.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp3.uk.freebsd.org/pub/FreeBSD/" },
+ { "UK #4", "ftp4.uk.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp4.uk.freebsd.org/pub/FreeBSD/" },
+ { "USA", "ftp.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp.freebsd.org/pub/FreeBSD/" },
+ { "USA #2", "ftp2.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp2.freebsd.org/pub/FreeBSD/" },
+ { "USA #3", "ftp3.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp3.freebsd.org/pub/FreeBSD/" },
+ { "USA #4", "ftp4.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp4.freebsd.org/pub/FreeBSD/" },
+ { "USA #5", "ftp5.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp5.freebsd.org/pub/FreeBSD/" },
+ { "USA #6", "ftp6.freebsd.org", NULL, dmenuSetVariable, NULL,
+ VAR_FTP_PATH "=ftp://ftp6.freebsd.org/pub/FreeBSD/" },
+ { 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) version\n"
+ "of FreeBSD.",
+ "Press F1 to read network configuration manual",
+ "network_device",
+ { { NULL } },
+};
+
+/* The media selection menu */
+DMenu MenuMedia = {
+ DMENU_NORMAL_TYPE | DMENU_SELECTION_RETURNS,
+ "Choose Installation Media",
+ "FreeBSD can be installed from a variety of different installation\n"
+ "media, ranging from floppies to an Internet FTP server. If you're\n"
+ "installing FreeBSD from a supported CDROM drive then this is generally\n"
+ "the best media to use if you have no overriding reason for using other\n"
+ "media.",
+ "Press F1 for more information on the various media types",
+ "media",
+ { { "1 CDROM", "Install from a FreeBSD CDROM", NULL, mediaSetCDROM },
+ { "2 FTP", "Install from an FTP server", NULL, mediaSetFTPActive },
+ { "3 FTP Passive", "Install from an FTP server through a firewall", NULL, mediaSetFTPPassive },
+ { "4 DOS", "Install from a DOS partition", NULL, mediaSetDOS },
+ { "5 NFS", "Install over NFS", NULL, mediaSetNFS },
+ { "6 File System", "Install from an existing filesystem", NULL, mediaSetUFS },
+ { "7 Floppy", "Install from a floppy disk set", NULL, mediaSetFloppy },
+ { "8 Tape", "Install from SCSI or QIC tape", NULL, mediaSetTape },
+ { "9 Options", "Go to the Options screen", NULL, optionsEditor },
+ { NULL } },
+};
+
+/* The distributions menu */
+DMenu MenuDistributions = {
+ DMENU_CHECKLIST_TYPE | DMENU_SELECTION_RETURNS,
+ "Choose Distributions",
+ "As a convenience, we provide several \"canned\" distribution sets.\n"
+ "These select what we consider to be the most reasonable defaults for the\n"
+ "type of system in question. If you would prefer to pick and choose the\n"
+ "list of distributions yourself, simply select \"Custom\". You can also\n"
+ "pick a canned distribution set and then fine-tune it with the Custom item.\n\n"
+ "Choose an item by pressing [SPACE]. When you are finished, chose the Exit\n"
+ "item or press [ENTER].",
+ "Press F1 for more information on these options.",
+ "distributions",
+ { { "1 Developer", "Full sources, binaries and doc but no games",
+ checkDistDeveloper, distSetDeveloper },
+ { "2 X-Developer", "Same as above, but includes the X Window System",
+ checkDistXDeveloper, distSetXDeveloper },
+ { "3 Kern-Developer", "Full binaries and doc, kernel sources only",
+ checkDistKernDeveloper, distSetKernDeveloper },
+ { "4 User", "Average user - binaries and doc only",
+ checkDistUser, distSetUser },
+ { "5 X-User", "Same as above, but includes the X Window System",
+ checkDistXUser, distSetXUser },
+ { "6 Minimal", "The smallest configuration possible",
+ checkDistMinimum, distSetMinimum },
+ { "7 Custom", "Specify your own distribution set",
+ NULL, dmenuSubmenu, NULL, &MenuSubDistributions, '>', '>', '>' },
+ { "8 All", "All sources and binaries (incl X Window System)",
+ checkDistEverything, distSetEverything },
+ { "9 Clear", "Reset selected distribution list to nothing",
+ NULL, distReset, NULL, NULL, ' ', ' ', ' ' },
+ { "0 Exit", "Exit this menu (returning to previous)",
+ checkTrue, dmenuExit, NULL, NULL, '<', '<', '<' },
+ { NULL } },
+};
+
+DMenu MenuSubDistributions = {
+ DMENU_CHECKLIST_TYPE | DMENU_SELECTION_RETURNS,
+ "Select the distributions you wish to install.",
+ "Please check off the distributions you wish to install. At the\n"
+ "very minimum, this should be \"bin\". WARNING: Do not export the\n"
+ "DES distribution out of the U.S.! It is for U.S. customers only.",
+ NULL,
+ NULL,
+ { { "bin", "Binary base distribution (required)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &Dists, '[', 'X', ']', DIST_BIN },
+ { "compat1x", "FreeBSD 1.x binary compatibility",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &Dists, '[', 'X', ']', DIST_COMPAT1X },
+ { "compat20", "FreeBSD 2.0 binary compatibility",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &Dists, '[', 'X', ']', DIST_COMPAT20 },
+ { "compat21", "FreeBSD 2.1 binary compatibility",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &Dists, '[', 'X', ']', DIST_COMPAT21 },
+ { "DES", "DES encryption code - NOT FOR EXPORT!",
+ DESFlagCheck, distSetDES },
+ { "dict", "Spelling checker dictionary files",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &Dists, '[', 'X', ']', DIST_DICT },
+ { "doc", "Miscellaneous FreeBSD online docs",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &Dists, '[', 'X', ']', DIST_DOC },
+ { "games", "Games (non-commercial)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &Dists, '[', 'X', ']', DIST_GAMES },
+ { "info", "GNU info files",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &Dists, '[', 'X', ']', DIST_INFO },
+ { "man", "System manual pages - recommended",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &Dists, '[', 'X', ']', DIST_MANPAGES },
+ { "catman", "Preformatted system manual pages",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &Dists, '[', 'X', ']', DIST_CATPAGES },
+ { "proflibs", "Profiled versions of the libraries",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &Dists, '[', 'X', ']', DIST_PROFLIBS },
+ { "src", "Sources for everything but DES",
+ srcFlagCheck, distSetSrc },
+ { "ports", "The FreeBSD Ports collection",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &Dists, '[', 'X', ']', DIST_PORTS },
+#ifdef USE_XIG_ENVIRONMENT
+ { "Xaccel", "The XiG AcceleratedX 3.1 distribution",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &Dists, '[', 'X', ']', DIST_XIG_SERVER },
+#else
+ { "XFree86", "The XFree86 3.3.1 distribution",
+ x11FlagCheck, distSetXF86 },
+#endif
+ { "All", "All sources, binaries and X Window System binaries",
+ NULL, distSetEverything, NULL, NULL, ' ', ' ', ' ' },
+ { "Clear", "Reset all of the above",
+ NULL, distReset, NULL, NULL, ' ', ' ', ' ' },
+ { "Exit", "Exit this menu (returning to previous)",
+ checkTrue, dmenuExit, NULL, NULL, '<', '<', '<' },
+ { NULL } },
+};
+
+DMenu MenuDESDistributions = {
+ DMENU_CHECKLIST_TYPE | DMENU_SELECTION_RETURNS,
+ "Select the encryption facilities you wish to install.",
+ "Please check off any special DES-based encryption distributions\n"
+ "you would like to install. Please note that these services are NOT FOR\n"
+ "EXPORT from the United States. For information on non-U.S. FTP\n"
+ "distributions of this software, please consult the release notes.",
+ NULL,
+ NULL,
+ { { "des", "Basic DES encryption services",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &DESDists, '[', 'X', ']', DIST_DES_DES, },
+ { "krb", "Kerberos encryption services",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &DESDists, '[', 'X', ']', DIST_DES_KERBEROS },
+ { "skerbero", "Sources for Kerberos IV",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &DESDists, '[', 'X', ']', DIST_DES_SKERBEROS },
+ { "ssecure", "Sources for DES",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &DESDists, '[', 'X', ']', DIST_DES_SSECURE },
+ { "scrypto", "Export controlled crypto sources",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &DESDists, '[', 'X', ']', DIST_DES_SCRYPTO },
+ { "Exit", "Exit this menu (returning to previous)",
+ checkTrue, dmenuExit, NULL, NULL, '<', '<', '<' },
+ { NULL } },
+};
+
+DMenu MenuSrcDistributions = {
+ DMENU_CHECKLIST_TYPE | DMENU_SELECTION_RETURNS ,
+ "Select the sub-components of src you wish to install.",
+ "Please check off those portions of the FreeBSD source tree\n"
+ "you wish to install.",
+ NULL,
+ NULL,
+ { { "base", "top-level files in /usr/src",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &SrcDists, '[', 'X', ']', DIST_SRC_BASE },
+ { "contrib", "/usr/src/contrib (contributed software)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &SrcDists, '[', 'X', ']', DIST_SRC_CONTRIB },
+ { "gnu", "/usr/src/gnu (software from the GNU Project)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &SrcDists, '[', 'X', ']', DIST_SRC_GNU },
+ { "etc", "/usr/src/etc (miscellaneous system files)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &SrcDists, '[', 'X', ']', DIST_SRC_ETC },
+ { "games", "/usr/src/games (the obvious!)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &SrcDists, '[', 'X', ']', DIST_SRC_GAMES },
+ { "include", "/usr/src/include (header files)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &SrcDists, '[', 'X', ']', DIST_SRC_INCLUDE },
+ { "lib", "/usr/src/lib (system libraries)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &SrcDists, '[', 'X', ']', DIST_SRC_LIB },
+ { "libexec", "/usr/src/libexec (system programs)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &SrcDists, '[', 'X', ']', DIST_SRC_LIBEXEC },
+ { "lkm", "/usr/src/lkm (Loadable Kernel Modules)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &SrcDists, '[', 'X', ']', DIST_SRC_LKM },
+ { "release", "/usr/src/release (release-generation tools)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &SrcDists, '[', 'X', ']', DIST_SRC_RELEASE },
+ { "bin", "/usr/src/bin (system binaries)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &SrcDists, '[', 'X', ']', DIST_SRC_BIN },
+ { "sbin", "/usr/src/sbin (system binaries)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &SrcDists, '[', 'X', ']', DIST_SRC_SBIN },
+ { "share", "/usr/src/share (documents and shared files)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &SrcDists, '[', 'X', ']', DIST_SRC_SHARE },
+ { "sys", "/usr/src/sys (FreeBSD kernel)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &SrcDists, '[', 'X', ']', DIST_SRC_SYS },
+ { "ubin", "/usr/src/usr.bin (user binaries)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &SrcDists, '[', 'X', ']', DIST_SRC_UBIN },
+ { "usbin", "/usr/src/usr.sbin (aux system binaries)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &SrcDists, '[', 'X', ']', DIST_SRC_USBIN },
+ { "smailcf", "/usr/src/usr.sbin (sendmail config macros)",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &SrcDists, '[', 'X', ']', DIST_SRC_SMAILCF },
+ { "All", "Select all of the above",
+ NULL, setSrc, NULL, NULL, ' ', ' ', ' ' },
+ { "Clear", "Reset all of the above",
+ NULL, clearSrc, NULL, NULL, ' ', ' ', ' ' },
+ { "Exit", "Exit this menu (returning to previous)",
+ checkTrue, dmenuExit, NULL, NULL, '<', '<', '<' },
+ { NULL } },
+};
+
+#ifndef USE_XIG_ENVIRONMENT
+DMenu MenuXF86Select = {
+ DMENU_NORMAL_TYPE,
+ "XFree86 3.3.1 Distribution",
+ "Please select the components you need from the XFree86 3.3.1\n"
+ "distribution sets.",
+ "Press F1 to read the XFree86 release notes for FreeBSD",
+ "XF86",
+ { { "Basic", "Basic component menu (required)", NULL, dmenuSubmenu, NULL, &MenuXF86SelectCore },
+ { "Server", "X server menu", NULL, dmenuSubmenu, NULL, &MenuXF86SelectServer },
+ { "Fonts", "Font set menu", NULL, dmenuSubmenu, NULL, &MenuXF86SelectFonts },
+ { "All", "Select all XFree86 distribution sets", NULL, setX11All },
+ { "Clear", "Reset XFree86 distribution list", NULL, clearX11All },
+ { "Exit", "Exit this menu (returning to previous)", checkTrue, dmenuExit, NULL, NULL, '<', '<', '<' },
+ { NULL } },
+};
+
+DMenu MenuXF86SelectCore = {
+ DMENU_CHECKLIST_TYPE | DMENU_SELECTION_RETURNS,
+ "XFree86 3.3.1 base distribution types",
+ "Please check off the basic XFree86 components you wish to install.\n"
+ "Bin, lib, and set are recommended for a minimum installaion.",
+ "Press F1 to read the XFree86 release notes for FreeBSD",
+ "XF86",
+ { { "bin", "Client applications and shared libs",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86Dists, '[', 'X', ']', DIST_XF86_BIN },
+ { "cfg", "Configuration files",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86Dists, '[', 'X', ']', DIST_XF86_CFG },
+ { "doc", "READMEs and release notes",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86Dists, '[', 'X', ']', DIST_XF86_DOC },
+ { "html", "HTML documentation files",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86Dists, '[', 'X', ']', DIST_XF86_HTML },
+ { "lib", "Data files needed at runtime",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86Dists, '[', 'X', ']', DIST_XF86_LIB },
+#ifndef USE_XIG_ENVIRONMENT
+ { "lk98", "Server link kit for PC98 machines",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86Dists, '[', 'X', ']', DIST_XF86_LKIT98 },
+ { "lkit", "Server link kit for all other machines",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86Dists, '[', 'X', ']', DIST_XF86_LKIT },
+#endif
+ { "man", "Manual pages",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86Dists, '[', 'X', ']', DIST_XF86_MAN },
+ { "prog", "Programmer's header and library files",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86Dists, '[', 'X', ']', DIST_XF86_PROG },
+ { "ps", "Postscript documentation",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86Dists, '[', 'X', ']', DIST_XF86_PS },
+#ifndef USE_XIG_ENVIRONMENT
+ { "set", "XFree86 Setup Utility",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86Dists, '[', 'X', ']', DIST_XF86_SET },
+#endif
+ { "sources", "XFree86 3.3.1 standard sources",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86Dists, '[', 'X', ']', DIST_XF86_SRC },
+ { "csources", "XFree86 3.3.1 contrib sources",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86Dists, '[', 'X', ']', DIST_XF86_CSRC },
+ { "All", "Select all of the above",
+ NULL, setX11Misc, NULL, NULL, ' ', ' ', ' ' },
+ { "Clear", "Reset all of the above",
+ NULL, clearX11Misc, NULL, NULL, ' ', ' ', ' ' },
+ { "Exit", "Exit this menu (returning to previous)",
+ checkTrue, dmenuExit, NULL, NULL, '<', '<', '<' },
+ { NULL } },
+};
+
+DMenu MenuXF86SelectFonts = {
+ DMENU_CHECKLIST_TYPE | DMENU_SELECTION_RETURNS ,
+ "Font distribution selection.",
+ "Please check off the individual font distributions you wish to\n\
+install. At the minimum, you should install the standard\n\
+75 DPI and misc fonts if you're also installing a server\n\
+(these are selected by default).",
+ "Press F1 to read the XFree86 release notes for FreeBSD",
+ "XF86",
+ { { "fnts", "Standard 75 DPI and miscellaneous fonts",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86FontDists, '[', 'X', ']', DIST_XF86_FONTS_MISC },
+ { "f100", "100 DPI fonts",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86FontDists, '[', 'X', ']', DIST_XF86_FONTS_100 },
+ { "fcyr", "Cyrillic Fonts",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86FontDists, '[', 'X', ']', DIST_XF86_FONTS_CYR },
+ { "fscl", "Speedo and Type scalable fonts",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86FontDists, '[', 'X', ']', DIST_XF86_FONTS_SCALE },
+ { "non", "Japanese, Chinese and other non-english fonts",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86FontDists, '[', 'X', ']', DIST_XF86_FONTS_NON },
+ { "server", "Font server",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86FontDists, '[', 'X', ']', DIST_XF86_FONTS_SERVER },
+ { "All", "All fonts",
+ NULL, setX11Fonts, NULL, NULL, ' ', ' ', ' ' },
+ { "Clear", "Reset font selections",
+ NULL, clearX11Fonts, NULL, NULL, ' ', ' ', ' ' },
+ { "Exit", "Exit this menu (returning to previous)",
+ checkTrue, dmenuExit, NULL, NULL, '<', '<', '<' },
+ { NULL } },
+};
+
+DMenu MenuXF86SelectServer = {
+ DMENU_CHECKLIST_TYPE | DMENU_SELECTION_RETURNS,
+ "X Server selection.",
+ "Please check off the types of X servers you wish to install.\n"
+ "If you are unsure as to which server will work for your graphics card,\n"
+ "it is recommended that try the SVGA or VGA16 servers or, for PC98\n"
+ "machines, the 9EGC or 9840 servers.",
+ "Press F1 to read the XFree86 release notes for FreeBSD",
+ "XF86",
+ { { "SVGA", "Standard VGA or Super VGA card",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86ServerDists, '[', 'X', ']', DIST_XF86_SERVER_SVGA },
+ { "VGA16", "Standard 16 color VGA card",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86ServerDists, '[', 'X', ']', DIST_XF86_SERVER_VGA16 },
+ { "Mono", "Standard Monochrome card",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86ServerDists, '[', 'X', ']', DIST_XF86_SERVER_MONO },
+ { "8514", "8-bit (256 color) IBM 8514 or compatible card",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86ServerDists, '[', 'X', ']', DIST_XF86_SERVER_8514 },
+ { "AGX", "8-bit AGX card",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86ServerDists, '[', 'X', ']', DIST_XF86_SERVER_AGX },
+ { "I128", "8, 16 and 24-bit #9 Imagine I128 card",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86ServerDists, '[', 'X', ']', DIST_XF86_SERVER_I128 },
+ { "Ma8", "8-bit ATI Mach8 card",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86ServerDists, '[', 'X', ']', DIST_XF86_SERVER_MACH8 },
+ { "Ma32", "8 and 16-bit (65K color) ATI Mach32 card",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86ServerDists, '[', 'X', ']', DIST_XF86_SERVER_MACH32 },
+ { "Ma64", "8 and 16-bit (65K color) ATI Mach64 card",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86ServerDists, '[', 'X', ']', DIST_XF86_SERVER_MACH64 },
+ { "P9K", "8, 16, and 24-bit color Weitek P9000 based boards",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86ServerDists, '[', 'X', ']', DIST_XF86_SERVER_P9000 },
+ { "S3", "8, 16 and 24-bit color S3 based boards",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86ServerDists, '[', 'X', ']', DIST_XF86_SERVER_S3 },
+ { "S3V", "8, 16 and 24-bit color S3 Virge based boards",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86ServerDists, '[', 'X', ']', DIST_XF86_SERVER_S3V },
+ { "W32", "8-bit ET4000/W32, /W32i and /W32p cards",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86ServerDists, '[', 'X', ']', DIST_XF86_SERVER_W32 },
+ { "nest", "A nested server for testing purposes",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86ServerDists, '[', 'X', ']', DIST_XF86_SERVER_NEST },
+ { "vfb", "A virtual frame-buffer server",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86ServerDists, '[', 'X', ']', DIST_XF86_SERVER_VFB },
+ { "PC98", "Select an X server for a NEC PC98 [Submenu]",
+ NULL, dmenuSubmenu, NULL, &MenuXF86SelectPC98Server, '>', ' ', '>', 0 },
+ { "All", "Select all of the above",
+ NULL, setX11Servers, NULL, NULL, ' ', ' ', ' ' },
+ { "Clear", "Reset all of the above",
+ NULL, clearX11Servers, NULL, NULL, ' ', ' ', ' ' },
+ { "Exit", "Exit this menu (returning to previous)",
+ checkTrue, dmenuExit, NULL, NULL, '<', '<', '<' },
+ { NULL } },
+};
+
+DMenu MenuXF86SelectPC98Server = {
+ DMENU_CHECKLIST_TYPE | DMENU_SELECTION_RETURNS,
+ "PC98 X Server selection.",
+ "Please check off the types of NEC PC98 X servers you wish to install.\n\
+If you are unsure as to which server will work for your graphics card,\n\
+it is recommended that try the SVGA or VGA16 servers (the VGA16 and\n\
+Mono servers are particularly well-suited to most LCD displays).",
+ "Press F1 to read the XFree86 release notes for FreeBSD",
+ "XF86",
+ { { "9480", "PC98 8-bit (256 color) PEGC-480 card",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86ServerDists, '[', 'X', ']', DIST_XF86_SERVER_9480 },
+ { "9EGC", "PC98 4-bit (16 color) EGC card",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86ServerDists, '[', 'X', ']', DIST_XF86_SERVER_9EGC },
+ { "9GA9", "PC98 GA-968V4/PCI (S3 968) card",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86ServerDists, '[', 'X', ']', DIST_XF86_SERVER_9GA9 },
+ { "9GAN", "PC98 GANB-WAP (cirrus) card",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86ServerDists, '[', 'X', ']', DIST_XF86_SERVER_9GAN },
+ { "9LPW", "PC98 PowerWindowLB (S3) card",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86ServerDists, '[', 'X', ']', DIST_XF86_SERVER_9LPW },
+ { "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 },
+ { "9TGU", "PC98 Cyber9320 and TGUI9680 cards",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86ServerDists, '[', 'X', ']', DIST_XF86_SERVER_9TGU },
+ { "9WEP", "PC98 WAB-EP (cirrus) card",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86ServerDists, '[', 'X', ']', DIST_XF86_SERVER_9WEP },
+ { "9WS", "PC98 WABS (cirrus) card",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86ServerDists, '[', 'X', ']', DIST_XF86_SERVER_9WS },
+ { "9WSN", "PC98 WSN-A2F (cirrus) card",
+ dmenuFlagCheck, dmenuSetFlag, NULL, &XF86ServerDists, '[', 'X', ']', DIST_XF86_SERVER_9WSN },
+ { "Exit", "Exit this menu (returning to previous)",
+ checkTrue, dmenuExit, NULL, NULL, '<', '<', '<' },
+ { NULL } }
+};
+#endif /* !USE_XIG_ENVIRONMENT */
+
+DMenu MenuDiskDevices = {
+ DMENU_CHECKLIST_TYPE | DMENU_SELECTION_RETURNS,
+ "Select Drive(s)",
+ "Please select the drive, or drives, on which you wish to perform\n"
+ "this operation. If you are attempting to install a boot partition\n"
+ "on a drive other than the first one or have multiple operating\n"
+ "systems on your machine, you will have the option to install a boot\n"
+ "manager later. To select a drive, use the arrow keys to move to it\n"
+ "and press [SPACE]. To de-select it, press [SPACE] again.\n\n"
+ "Select OK or Cancel to leave this menu.",
+ "Press F1 for important information regarding disk geometry!",
+ "drives",
+ { { NULL } },
+};
+
+DMenu MenuHTMLDoc = {
+ DMENU_NORMAL_TYPE,
+ "Select HTML Documentation pointer",
+ "Please select the body of documentation you're interested in, the main\n"
+ "ones right now being the FAQ and the Handbook. You can also chose \"other\"\n"
+ "to enter an arbitrary URL for browsing.",
+ "Press F1 for more help on what you see here.",
+ "html",
+ { { "Handbook", "The FreeBSD Handbook.", NULL, docShowDocument },
+ { "FAQ", "The Frequently Asked Questions guide.", NULL, docShowDocument },
+ { "Home", "The Home Pages for the FreeBSD Project (requires net)", NULL, docShowDocument },
+ { "Other", "Enter a URL.", NULL, docShowDocument },
+ { NULL } },
+};
+
+/* The main installation menu */
+DMenu MenuInstallCustom = {
+ DMENU_NORMAL_TYPE,
+ "Choose Custom Installation Options",
+ "This is the custom installation menu. You may use this menu to specify\n"
+ "details on the type of distribution you wish to have, where you wish\n"
+ "to install it from and how you wish to allocate disk storage to FreeBSD.",
+ "Press F1 to read the installation guide",
+ "install",
+ { { "1 Options", "View/Set various installation options", NULL, optionsEditor },
+ { "2 Partition", "Allocate disk space for FreeBSD", NULL, diskPartitionEditor },
+ { "3 Label", "Label allocated disk partitions", NULL, diskLabelEditor },
+ { "4 Distributions", "Select distribution(s) to extract", NULL, dmenuSubmenu, NULL, &MenuDistributions },
+ { "5 Media", "Choose the installation media type", NULL, dmenuSubmenu, NULL, &MenuMedia },
+ { "6 Commit", "Perform any pending Partition/Label/Extract actions", NULL, installCustomCommit },
+ { "0 Exit", "Exit this menu (returning to previous)", NULL, dmenuExit },
+ { NULL } },
+};
+
+/* MBR type menu */
+DMenu MenuMBRType = {
+ DMENU_RADIO_TYPE | DMENU_SELECTION_RETURNS,
+ "overwrite me", /* will be disk specific label */
+ "FreeBSD comes with a boot selector that allows you to easily\n"
+ "select between FreeBSD and any other operating systems on your machine\n"
+ "at boot time. If you have more than one drive and want to boot\n"
+ "from the second one, the boot selector will also make it possible\n"
+ "to do so (limitations in the PC BIOS usually prevent this otherwise).\n"
+ "If you do not want a boot selector, or wish to replace an existing\n"
+ "one, select \"standard\". If you would prefer your Master Boot\n"
+ "Record to remain untouched then select \"None\".\n\n"
+ " NOTE: PC-DOS users will almost certainly require \"None\"!",
+ "Press F1 to read about drive setup",
+ "drives",
+ { { "BootMgr", "Install the FreeBSD Boot Manager (\"Booteasy\")",
+ dmenuRadioCheck, dmenuSetValue, NULL, &BootMgr },
+ { "Standard", "Install a standard MBR (no boot manager)",
+ dmenuRadioCheck, dmenuSetValue, NULL, &BootMgr, '(', '*', ')', 1 },
+ { "None", "Leave the Master Boot Record untouched",
+ dmenuRadioCheck, dmenuSetValue, NULL, &BootMgr, '(', '*', ')', 2 },
+ { NULL } },
+};
+
+/* Final configuration menu */
+DMenu MenuConfigure = {
+ DMENU_NORMAL_TYPE,
+ "FreeBSD Configuration Menu", /* title */
+ "If you've already installed FreeBSD, you may use this menu to customize\n"
+ "it somewhat to suit your particular configuration. Most importantly,\n"
+ "you can use the Packages utility to load extra \"3rd party\"\n"
+ "software not provided in the base distributions.",
+ "Press F1 for more information on these options",
+ "configure",
+ { { "1 User Management", "Add user and group information",
+ NULL, dmenuSubmenu, NULL, &MenuUsermgmt },
+ { "2 Console", "Customize system console behavior",
+ NULL, dmenuSubmenu, NULL, &MenuSyscons },
+ { "3 Time Zone", "Set which time zone you're in",
+ NULL, dmenuSystemCommand, NULL, "tzsetup" },
+ { "4 Media", "Change the installation media type",
+ NULL, dmenuSubmenu, NULL, &MenuMedia },
+ { "5 Mouse", "Select the type of mouse you have",
+ NULL, dmenuSubmenu, NULL, &MenuMouse, NULL },
+ { "6 Networking", "Configure additional network services",
+ NULL, dmenuSubmenu, NULL, &MenuNetworking },
+ { "7 Startup", "Configure system startup services",
+ NULL, dmenuSubmenu, NULL, &MenuStartup },
+ { "8 Options", "View/Set various installation options",
+ NULL, optionsEditor },
+ { "9 Packages", "Install pre-packaged software for FreeBSD",
+ NULL, configPackages },
+ { "A Root Password", "Set the system manager's password",
+ NULL, dmenuSystemCommand, NULL, "passwd root" },
+ { "B HTML Docs", "Go to the HTML documentation menu (post-install)",
+ NULL, docBrowser },
+#ifdef USE_XIG_ENVIRONMENT
+ { "X X + CDE", "Configure X Window system & CDE environment",
+#else
+ { "X XFree86", "Configure XFree86",
+#endif
+ NULL, configXEnvironment },
+ { "D Distributions", "Install additional distribution sets",
+ NULL, distExtractAll },
+ { "L Label", "The disk Label editor",
+ NULL, diskLabelEditor },
+ { "P Partition", "The disk Slice (PC-style partition) Editor",
+ NULL, diskPartitionEditor },
+ { "R Register", "Register yourself or company as a FreeBSD user.", NULL, configRegister },
+ { "E Exit", "Exit this menu (returning to previous)",
+ NULL, dmenuExit },
+ { NULL } },
+};
+
+DMenu MenuStartup = {
+ DMENU_CHECKLIST_TYPE | DMENU_SELECTION_RETURNS,
+ "Startup Services Menu",
+ "This menu allows you to configure various aspects of your system's\n"
+ "startup configuration. Remember to use SPACE to select items! The\n"
+ "RETURN key will leave this menu (as with all checkbox menus).",
+ NULL,
+ NULL,
+ { { "APM", "Auto-power management services (typically laptops)",
+ dmenuVarCheck, dmenuToggleVariable, NULL, "apm_enable=YES" },
+ { "pccard", "Enable PCCARD (AKA PCMCIA) services (also laptops)",
+ dmenuVarCheck, dmenuToggleVariable, NULL, "pccard_enable=YES" },
+ { "pccard mem", "Set PCCARD memory address (if enabled)",
+ dmenuVarCheck, dmenuISetVariable, NULL, "pccard_mem" },
+ { "pccard ifconfig", "List of PCCARD ethernet devices to configure",
+ dmenuVarCheck, dmenuISetVariable, NULL, "pccard_ifconfig" },
+ { " ", " -- ", NULL, NULL, NULL, NULL, ' ', ' ', ' ' },
+ { "startup dirs", "Set the list of dirs to look for startup scripts",
+ dmenuVarCheck, dmenuISetVariable, NULL, "local_startup" },
+ { "named", "Run a local name server on this host",
+ dmenuVarCheck, dmenuToggleVariable, NULL, "named_enable=YES" },
+ { "named flags", "Set default flags to named (if enabled)",
+ dmenuVarCheck, dmenuISetVariable, NULL, "named_flags" },
+ { "nis client", "This host wishes to be an NIS client.",
+ dmenuVarCheck, dmenuToggleVariable, NULL, "nis_client_enable=YES" },
+ { "nis server", "This host wishes to be an NIS server.",
+ dmenuVarCheck, dmenuToggleVariable, NULL, "nis_server_enable=YES" },
+ { " ", " -- ", NULL, NULL, NULL, NULL, ' ', ' ', ' ' },
+ { "accounting", "This host wishes to run process accounting.",
+ dmenuVarCheck, dmenuToggleVariable, NULL, "accounting_enable=YES" },
+ { "lpd", "This host has a printer and wants to run lpd.",
+ dmenuVarCheck, dmenuToggleVariable, NULL, "lpd_enable=YES" },
+ { "linux", "This host wants to be able to run linux binaries.",
+ dmenuVarCheck, dmenuToggleVariable, NULL, "linux_enable=YES" },
+ { "quotas", "This host wishes to check quotas on startup.",
+ dmenuVarCheck, dmenuToggleVariable, NULL, "check_quotas=YES" },
+ { "SCO", "This host wants to be able to run IBCS2 binaries.",
+ dmenuVarCheck, dmenuToggleVariable, NULL, "ibcs2_enable=YES" },
+ { "Exit", "Exit this menu (returning to previous)",
+ checkTrue, dmenuExit, NULL, NULL, '<', '<', '<' },
+ { NULL } },
+};
+
+DMenu MenuNetworking = {
+ DMENU_CHECKLIST_TYPE | DMENU_SELECTION_RETURNS,
+ "Network Services Menu",
+ "You may have already configured one network device (and the other\n"
+ "various hostname/gateway/name server parameters) in the process\n"
+ "of installing FreeBSD. This menu allows you to configure other\n"
+ "aspects of your system's network configuration.",
+ NULL,
+ NULL,
+ { { "Interfaces", "Configure additional network interfaces",
+ NULL, tcpMenuSelect },
+ { "NFS client", "This machine will be an NFS client",
+ dmenuVarCheck, dmenuToggleVariable, NULL, "nfs_client_enable=YES" },
+ { "NFS server", "This machine will be an NFS server",
+ dmenuVarCheck, configNFSServer, NULL, "nfs_server_enable" },
+ { "AMD", "This machine wants to run the auto-mounter service",
+ dmenuVarCheck, dmenuToggleVariable, NULL, "amd_enable=YES" },
+ { "AMD Flags", "Set flags to AMD service (if enabled)",
+ dmenuVarCheck, dmenuISetVariable, NULL, "amd_flags" },
+ { "TCP Extentions", "Allow RFC1323 and RFC1544 TCP extentions?",
+ dmenuVarCheck, dmenuToggleVariable, NULL, "tcp_extentions=YES" },
+ { "Gateway", "This machine will route packets between interfaces",
+ dmenuVarCheck, dmenuToggleVariable, NULL, "gateway_enable=YES" },
+#ifdef NETCON_EXTENTIONS
+ { "Netcon", "Install the Novell client/server demo package",
+ dmenuVarCheck, configNovell, NULL, "novell" },
+#endif
+ { "Ntpdate", "Select a clock-syncronization server",
+ dmenuVarCheck, dmenuSubmenu, NULL, &MenuNTP, '[', 'X', ']', (int)"ntpdate_enable=YES" },
+ { "router", "Select routing daemon (default: routed)",
+ dmenuVarCheck, configRouter, NULL, "router" },
+ { "Rwhod", "This machine wants to run the rwho daemon",
+ dmenuVarCheck, dmenuToggleVariable, NULL, "rwhod_enable=YES" },
+ { "Anon FTP", "This machine wishes to allow anonymous FTP.",
+ dmenuVarCheck, configAnonFTP, NULL, "anon_ftp" },
+ { "PCNFSD", "Run authentication server for clients with PC-NFS.",
+ dmenuVarCheck, configPCNFSD, NULL, "pcnfsd" },
+ { "Exit", "Exit this menu (returning to previous)",
+ checkTrue, dmenuExit, NULL, NULL, '<', '<', '<' },
+ { NULL } },
+};
+
+DMenu MenuNTP = {
+ DMENU_RADIO_TYPE | DMENU_SELECTION_RETURNS,
+ "NTPDATE Server Selection",
+ "There are a number of time syncronization servers available\n"
+ "for public use around the Internet. Please select one reasonably\n"
+ "close to you to have your system time syncronized accordingly.",
+ "These are the primary open-access NTP servers",
+ NULL,
+ { { "None", "No ntp server",
+ dmenuVarCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=NO,ntpdate_flags=" },
+ { "Other", "Select a site not on this list",
+ dmenuVarsCheck, configNTP, NULL,
+ NULL },
+ { "Australia", "ntp.syd.dms.csiro.au (HP 5061 Cesium Beam)",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp.syd.dms.csiro.au" },
+ { "Canada", "tick.usask.ca (GOES clock)",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=tick.usask.ca" },
+ { "France", "canon.inria.fr (TDF clock)",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=canon.inria.fr" },
+ { "Germany", "ntps1-{0,1,2}.uni-erlangen.de (GPS)",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntps1-0.uni-erlangen.de" },
+ { "Germany #2", "ntps1-0.cs.tu-berlin.de (GPS)",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntps1-0.cs.tu-berlin.de" },
+ { "Japan", "clock.nc.fukuoka-u.ac.jp (GPS clock)",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=clock.nc.fukuoka-u.ac.jp" },
+ { "Japan #2", "clock.tl.fukuoka-u.ac.jp (GPS clock)",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=clock.tl.fukuoka-u.ac.jp" },
+ { "Netherlands", "ntp0.nl.net (GPS clock)",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ntp0.nl.net" },
+ { "Norway", "timer.unik.no (NTP clock)",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=timer.unik.no" },
+ { "Sweden", "Time1.Stupi.SE (Cesium/GPS)",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=Time1.Stupi.SE" },
+ { "Switzerland", "swisstime.ethz.ch (DCF77 clock)",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=swisstime.ethz.ch" },
+ { "U.S. East Coast", "bitsy.mit.edu (WWV clock)",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=bitsy.mit.edu" },
+ { "U.S. East Coast #2", "otc1.psu.edu (WWV clock)",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=otc1.psu.edu" },
+ { "U.S. West Coast", "apple.com (WWV clock)",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=apple.com" },
+ { "U.S. West Coast #2", "clepsydra.dec.com (GOES clock)",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=clepsydra.dec.com" },
+ { "U.S. West Coast #3", "clock.llnl.gov (WWVB clock)",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=clock.llnl.gov" },
+ { "U.S. Midwest", "ncar.ucar.edu (WWVB clock)",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=ncar.ucar.edu" },
+ { "U.S. Pacific", "chantry.hawaii.net (WWV/H clock)",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=chantry.hawaii.net" },
+ { "U.S. Southwest", "shorty.chpc.utexas.edu (WWV clock)",
+ dmenuVarsCheck, dmenuSetVariables, NULL,
+ "ntpdate_enable=YES,ntpdate_flags=shorty.chpc.utexas.edu" },
+ { NULL } },
+};
+
+DMenu MenuSyscons = {
+ DMENU_NORMAL_TYPE,
+ "System Console Configuration",
+ "The default system console driver for FreeBSD (syscons) has a\n"
+ "number of configuration options which may be set according to\n"
+ "your preference.\n\n"
+ "When you are done setting configuration options, select Cancel.",
+ "Configure your system console settings",
+ NULL,
+ { { "Font", "Choose an alternate screen font", NULL, dmenuSubmenu, NULL, &MenuSysconsFont },
+ { "Keymap", "Choose an alternate keyboard map", NULL, dmenuSubmenu, NULL, &MenuSysconsKeymap },
+ { "Repeat", "Set the rate at which keys repeat", NULL, dmenuSubmenu, NULL, &MenuSysconsKeyrate },
+ { "Saver", "Configure the screen saver", NULL, dmenuSubmenu, NULL, &MenuSysconsSaver },
+ { "Screenmap", "Choose an alternate screenmap", NULL, dmenuSubmenu, NULL, &MenuSysconsScrnmap },
+ { "Exit", "Exit this menu (returning to previous)", NULL, dmenuExit },
+ { NULL } },
+};
+
+DMenu MenuSysconsKeymap = {
+ DMENU_RADIO_TYPE | DMENU_SELECTION_RETURNS,
+ "System Console Keymap",
+ "The default system console driver for FreeBSD (syscons) defaults\n"
+ "to a standard \"American\" keyboard map. Users in other countries\n"
+ "(or with different keyboard preferences) may wish to choose one of\n"
+ "the other keymaps below.\n"
+ "Note that sysinstall itself only uses the part of the keyboard map\n"
+ "which is required to generate the ANSI character subset, but your\n"
+ "choice of keymap will also be saved for later (fuller) use.",
+ "Choose a keyboard map",
+ NULL,
+ { { "Belgian", "Belgian ISO keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=be.iso" },
+ { "Brazil CP850", "Brazil CP850 keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=br275.cp850" },
+ { "Brazil ISO", "Brazil ISO keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=br275.iso" },
+ { "Danish CP865", "Danish Code Page 865 keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=danish.cp865" },
+ { "Danish ISO", "Danish ISO keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=danish.iso" },
+ { "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" },
+ { "Italian", "Italian ISO keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=it.iso" },
+ { "Japanese 106", "Japanese 106 keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=jp.106" },
+ { "Norway ISO", "Norwegian ISO keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=norwegian.iso" },
+ { "Russia CP866", "Russian CP866 keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=ru.cp866" },
+ { "Russia KOI8-R", "Russian KOI8-R keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=ru.koi8-r" },
+ { "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 German", "Swiss German ISO keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=swissgerman.iso.kbd" },
+ { "U.K. CP850", "United Kingdom Code Page 850 keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=uk.cp850" },
+ { "U.K. ISO", "United Kingdom ISO keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=uk.iso" },
+ { "U.S. Dvorak", "United States Dvorak keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=us.dvorak" },
+ { "U.S. ISO", "United States ISO keymap", dmenuVarCheck, dmenuSetKmapVariable, NULL, "keymap=us.iso" },
+ { NULL } },
+};
+
+DMenu MenuSysconsKeyrate = {
+ DMENU_RADIO_TYPE | DMENU_SELECTION_RETURNS,
+ "System Console Keyboard Repeat Rate",
+ "This menu allows you to set the speed at which keys repeat\n"
+ "when held down.",
+ "Choose a keyboard repeat rate",
+ NULL,
+ { { "Slow", "Slow keyboard repeat rate", dmenuVarCheck, dmenuSetVariable, NULL, "keyrate=slow" },
+ { "Normal", "\"Normal\" keyboard repeat rate", dmenuVarCheck, dmenuSetVariable, NULL, "keyrate=normal" },
+ { "Fast", "Fast keyboard repeat rate", dmenuVarCheck, dmenuSetVariable, NULL, "keyrate=fast" },
+ { "Default", "Use default keyboard repeat rate", dmenuVarCheck, dmenuSetVariable, NULL, "keyrate=NO" },
+ { NULL } },
+};
+
+DMenu MenuSysconsSaver = {
+ DMENU_RADIO_TYPE | DMENU_SELECTION_RETURNS,
+ "System Console Screen Saver",
+ "By default, the console driver will not attempt to do anything\n"
+ "special with your screen when it's idle. If you expect to leave your\n"
+ "monitor switched on and idle for long periods of time then you should\n"
+ "probably enable one of these screen savers to prevent phosphor burn-in.",
+ "Choose a nifty-looking screen saver",
+ NULL,
+ { { "blank", "Simply blank the screen",
+ dmenuVarCheck, configSaver, NULL, "saver=blank" },
+ { "Daemon", "\"BSD Daemon\" animated screen saver",
+ dmenuVarCheck, configSaver, NULL, "saver=daemon" },
+ { "Green", "\"Green\" power saving mode (if supported by monitor)",
+ dmenuVarCheck, configSaver, NULL, "saver=green" },
+ { "Snake", "Draw a FreeBSD \"snake\" on your screen",
+ dmenuVarCheck, configSaver, NULL, "saver=snake" },
+ { "Star", "A \"twinkling stars\" effect",
+ dmenuVarCheck, configSaver, NULL, "saver=star" },
+ { "Timeout", "Set the screen saver timeout interval",
+ NULL, configSaverTimeout, NULL, NULL, ' ', ' ', ' ' },
+ { NULL } },
+};
+
+DMenu MenuSysconsScrnmap = {
+ DMENU_RADIO_TYPE | DMENU_SELECTION_RETURNS,
+ "System Console Screenmap",
+ "Unless you load a specific font, most PC hardware defaults to\n"
+ "displaying characters in the IBM 437 character set. However,\n"
+ "in the Unix world, this character set is very rarely used. Most\n"
+ "Western European countries, for example, prefer ISO 8859-1.\n"
+ "American users won't notice the difference since the bottom half\n"
+ "of all these character sets is ANSI anyway.\n"
+ "If your hardware is capable of downloading a new display font,\n"
+ "you should probably choose that option. However, for hardware\n"
+ "where this is not possible (e.g. monochrome adapters), a screen\n"
+ "map will give you the best approximation that your hardware can\n"
+ "display at all.",
+ "Choose a screen map",
+ NULL,
+ { { "None", "No screenmap, use default font", dmenuVarCheck, dmenuSetVariable, NULL, "scrnmap=NO" },
+ { "KOI8-R to IBM866", "Russian KOI8-R to IBM 866 screenmap", dmenuVarCheck, dmenuSetVariable, NULL, "scrnmap=koi8-r2cp866" },
+ { "ISO 8859-1 to IBM437", "W-Europe ISO 8859-1 to IBM 437 screenmap", dmenuVarCheck, dmenuSetVariable, NULL, "scrnmap=iso-8859-1_to_cp437" },
+ { NULL } },
+};
+
+DMenu MenuSysconsFont = {
+ DMENU_RADIO_TYPE | DMENU_SELECTION_RETURNS,
+ "System Console Font",
+ "Most PC hardware defaults to displaying characters in the\n"
+ "IBM 437 character set. However, in the Unix world, this\n"
+ "character set is very rarely used. Most Western European\n"
+ "countries, for example, prefer ISO 8859-1.\n"
+ "American users won't notice the difference since the bottom half\n"
+ "of all these charactersets is ANSI anyway. However, they might\n"
+ "want to load a font anyway to use the 30- or 50-line displays.\n"
+ "If your hardware is capable of downloading a new display font,\n"
+ "you can select the appropriate font below.",
+ "Choose a font",
+ NULL,
+ { { "None", "Use default font", dmenuVarCheck, dmenuSetVariables, NULL,
+ "font8x8=NO,font8x14=NO,font8x16=NO" },
+ { "IBM 437", "English", dmenuVarCheck, dmenuSetVariables, NULL,
+ "font8x8=cp437-8x8,font8x14=cp437-8x14,font8x16=cp437-8x16" },
+ { "IBM 850", "Western Europe, IBM encoding", dmenuVarCheck, dmenuSetVariables, NULL,
+ "font8x8=cp850-8x8,font8x14=cp850-8x14,font8x16=cp850-8x16" },
+ { "IBM 865", "Norwegian, IBM encoding", dmenuVarCheck, dmenuSetVariables, NULL,
+ "font8x8=cp865-8x8,font8x14=cp865-8x14,font8x16=cp865-8x16" },
+ { "IBM 866", "Russian, IBM encoding", dmenuVarCheck, dmenuSetVariables, NULL,
+ "font8x8=cp866-8x8,font8x14=cp866-8x14,font8x16=cp866-8x16" },
+ { "ISO 8859-1", "Western Europe, ISO encoding", dmenuVarCheck, dmenuSetVariables, NULL,
+ "font8x8=iso-8x8,font8x14=iso-8x14,font8x16=iso-8x16" },
+ { "KOI8-R", "Russian, KOI8-R encoding", dmenuVarCheck, dmenuSetVariables, NULL,
+ "font8x8=koi8-r-8x8,font8x14=koi8-r-8x14,font8x16=koi8-r-8x16" },
+ { NULL } },
+};
+
+DMenu MenuUsermgmt = {
+ DMENU_NORMAL_TYPE,
+ "User and group management",
+ "The submenus here allow to manipulate user groups and\n"
+ "login accounts.\n",
+ "Configure your user groups and users",
+ NULL,
+ { { "Add user", "Add a new user to the system.", NULL, userAddUser },
+ { "Add group", "Add a new user group to the system.", NULL, userAddGroup },
+ { "Exit", "Exit this menu (returning to previous)", NULL, dmenuExit },
+ { NULL } },
+};
+
+DMenu MenuFixit = {
+ DMENU_NORMAL_TYPE,
+ "Please choose a fixit option",
+ "There are three ways of going into \"fixit\" mode:\n"
+ "- you can use the 2nd FreeBSD CDROM, in which case there will be\n"
+ " full access to the complete set of FreeBSD commands and utilities,\n"
+ "- you can use the more limited (but perhaps customized) fixit floppy,\n"
+ "- or you can start an Emergency Holographic Shell now, which is\n"
+ " limited to the subset of commands that is already available right now.",
+ "Press F1 for more detailed repair instructions",
+ "fixit",
+{ { "1 CDROM", "Use the 2nd \"live\" CDROM from the distribution", NULL, installFixitCDROM },
+ { "2 Floppy", "Use a floppy generated from the fixit image", NULL, installFixitFloppy },
+ { "3 Shell", "Start an Emergency Holographic Shell", NULL, installFixitHoloShell },
+ { NULL } },
+};
+
diff --git a/usr.sbin/sysinstall/misc.c b/usr.sbin/sysinstall/misc.c
new file mode 100644
index 0000000..d3d67a7
--- /dev/null
+++ b/usr.sbin/sysinstall/misc.c
@@ -0,0 +1,485 @@
+/*
+ * Miscellaneous support routines..
+ *
+ * $Id: misc.c,v 1.35 1997/06/13 07:11:56 jkh Exp $
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include "sysinstall.h"
+#include <ctype.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/errno.h>
+#include <sys/file.h>
+#include <sys/types.h>
+#include <dirent.h>
+#include <sys/wait.h>
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <ufs/ufs/ufsmount.h>
+#include <sys/reboot.h>
+#include <sys/dkbad.h>
+#include <sys/disklabel.h>
+
+/* Quick check to see if a file is readable */
+Boolean
+file_readable(char *fname)
+{
+ if (!access(fname, F_OK))
+ return TRUE;
+ return FALSE;
+}
+
+/* Quick check to see if a file is executable */
+Boolean
+file_executable(char *fname)
+{
+ if (!access(fname, X_OK))
+ return TRUE;
+ return FALSE;
+}
+
+/* Concatenate two strings into static storage */
+char *
+string_concat(char *one, char *two)
+{
+ static char tmp[FILENAME_MAX];
+
+ /* Yes, we're deliberately cavalier about not checking for overflow */
+ strcpy(tmp, one);
+ strcat(tmp, two);
+ return tmp;
+}
+
+/* sane strncpy() function */
+char *
+sstrncpy(char *dst, const char *src, int size)
+{
+ dst[size] = '\0';
+ return strncpy(dst, src, size);
+}
+
+/* Concatenate three strings into static storage */
+char *
+string_concat3(char *one, char *two, char *three)
+{
+ static char tmp[FILENAME_MAX];
+
+ /* Yes, we're deliberately cavalier about not checking for overflow */
+ strcpy(tmp, one);
+ strcat(tmp, two);
+ strcat(tmp, three);
+ return tmp;
+}
+
+/* Clip the whitespace off the end of a string */
+char *
+string_prune(char *str)
+{
+ int len = str ? strlen(str) : 0;
+
+ while (len && isspace(str[len - 1]))
+ str[--len] = '\0';
+ return str;
+}
+
+/* run the whitespace off the front of a string */
+char *
+string_skipwhite(char *str)
+{
+ while (*str && isspace(*str))
+ ++str;
+ return str;
+}
+
+/* copy optionally and allow second arg to be null */
+char *
+string_copy(char *s1, char *s2)
+{
+ if (!s1)
+ return NULL;
+ if (!s2)
+ s1[0] = '\0';
+ else
+ strcpy(s1, s2);
+ return s1;
+}
+
+/* convert an integer to a string, using a static buffer */
+char *
+itoa(int value)
+{
+ static char buf[13];
+
+ snprintf(buf, 12, "%d", value);
+ return buf;
+}
+
+Boolean
+directory_exists(const char *dirname)
+{
+ DIR *tptr;
+
+ if (!dirname)
+ return FALSE;
+ if (!strlen(dirname))
+ return FALSE;
+
+ tptr = opendir(dirname);
+ if (!tptr)
+ return (FALSE);
+
+ closedir(tptr);
+ return (TRUE);
+}
+
+char *
+pathBaseName(const char *path)
+{
+ char *pt;
+ char *ret = (char *)path;
+
+ pt = strrchr(path,(int)'/');
+
+ if (pt != 0) /* if there is a slash */
+ {
+ ret = ++pt; /* start the file after it */
+ }
+
+ return(ret);
+}
+
+/* A free guaranteed to take NULL ptrs */
+void
+safe_free(void *ptr)
+{
+ if (ptr)
+ free(ptr);
+}
+
+/* A malloc that checks errors */
+void *
+safe_malloc(size_t size)
+{
+ void *ptr;
+
+ if (size <= 0)
+ msgFatal("Invalid malloc size of %d!", size);
+ ptr = malloc(size);
+ if (!ptr)
+ msgFatal("Out of memory!");
+ bzero(ptr, size);
+ return ptr;
+}
+
+/* A realloc that checks errors */
+void *
+safe_realloc(void *orig, size_t size)
+{
+ void *ptr;
+
+ if (size <= 0)
+ msgFatal("Invalid realloc size of %d!", size);
+ ptr = realloc(orig, size);
+ if (!ptr)
+ msgFatal("Out of memory!");
+ return ptr;
+}
+
+/* Create a path biased from the VAR_INSTALL_ROOT variable (if not /) */
+char *
+root_bias(char *path)
+{
+ static char tmp[FILENAME_MAX];
+ char *cp = variable_get(VAR_INSTALL_ROOT);
+
+ if (!strcmp(cp, "/"))
+ return path;
+ strcpy(tmp, variable_get(VAR_INSTALL_ROOT));
+ strcat(tmp, path);
+ return tmp;
+}
+
+/*
+ * These next routines are kind of specialized just for building item lists
+ * for dialog_menu().
+ */
+
+/* Add an item to an item list */
+dialogMenuItem *
+item_add(dialogMenuItem *list, char *prompt, char *title,
+ int (*checked)(dialogMenuItem *self),
+ int (*fire)(dialogMenuItem *self),
+ void (*selected)(dialogMenuItem *self, int is_selected),
+ void *data, int aux, int *curr, int *max)
+{
+ dialogMenuItem *d;
+
+ if (*curr == *max) {
+ *max += 20;
+ list = (dialogMenuItem *)realloc(list, sizeof(dialogMenuItem) * *max);
+ }
+ d = &list[(*curr)++];
+ bzero(d, sizeof(*d));
+ d->prompt = prompt ? strdup(prompt) : NULL;
+ d->title = title ? strdup(title) : NULL;
+ d->checked = checked;
+ d->fire = fire;
+ d->selected = selected;
+ d->data = data;
+ d->aux = aux;
+ return list;
+}
+
+/* Toss the items out */
+void
+items_free(dialogMenuItem *list, int *curr, int *max)
+{
+ int i;
+
+ for (i = 0; list[i].prompt; i++) {
+ safe_free(list[i].prompt);
+ safe_free(list[i].title);
+ }
+ safe_free(list);
+ *curr = *max = 0;
+}
+
+int
+Mkdir(char *ipath)
+{
+ struct stat sb;
+ int final;
+ char *p, *path;
+
+ if (file_readable(ipath) || Fake)
+ return DITEM_SUCCESS;
+
+ path = strcpy(alloca(strlen(ipath) + 1), ipath);
+ if (isDebug())
+ msgDebug("mkdir(%s)\n", path);
+ p = path;
+ if (p[0] == '/') /* Skip leading '/'. */
+ ++p;
+ for (final = FALSE; !final; ++p) {
+ if (p[0] == '\0' || (p[0] == '/' && p[1] == '\0'))
+ final = TRUE;
+ else if (p[0] != '/')
+ continue;
+ *p = '\0';
+ if (stat(path, &sb)) {
+ if (errno != ENOENT) {
+ msgConfirm("Couldn't stat directory %s: %s", path, strerror(errno));
+ return DITEM_FAILURE;
+ }
+ if (isDebug())
+ msgDebug("mkdir(%s..)\n", path);
+ if (mkdir(path, S_IRWXU | S_IRWXG | S_IRWXO) < 0) {
+ msgConfirm("Couldn't create directory %s: %s", path,strerror(errno));
+ return DITEM_FAILURE;
+ }
+ }
+ *p = '/';
+ }
+ return DITEM_SUCCESS;
+}
+
+int
+Mount(char *mountp, void *dev)
+{
+ struct ufs_args ufsargs;
+ char device[80];
+ char mountpoint[FILENAME_MAX];
+
+ if (Fake)
+ return DITEM_SUCCESS;
+
+ if (*((char *)dev) != '/') {
+ sprintf(device, "%s/dev/%s", RunningAsInit ? "/mnt" : "", (char *)dev);
+ sprintf(mountpoint, "%s%s", RunningAsInit ? "/mnt" : "", mountp);
+ }
+ else {
+ strcpy(device, dev);
+ strcpy(mountpoint, mountp);
+ }
+ memset(&ufsargs,0,sizeof ufsargs);
+
+ if (Mkdir(mountpoint)) {
+ msgConfirm("Unable to make directory mountpoint for %s!", mountpoint);
+ return DITEM_FAILURE;
+ }
+ if (isDebug())
+ msgDebug("mount %s %s\n", device, mountpoint);
+
+ ufsargs.fspec = device;
+ if (mount(MOUNT_UFS, mountpoint, RunningAsInit ? MNT_ASYNC : 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/msg.c b/usr.sbin/sysinstall/msg.c
new file mode 100644
index 0000000..e61b767
--- /dev/null
+++ b/usr.sbin/sysinstall/msg.c
@@ -0,0 +1,320 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last program in the `sysinstall' line - the next
+ * generation being essentially a complete rewrite.
+ *
+ * $Id: msg.c,v 1.46 1997/09/09 16:27:50 jkh Exp $
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include "sysinstall.h"
+#include <stdarg.h>
+#include <sys/ioctl.h>
+#include <machine/console.h>
+
+Boolean
+isDebug(void)
+{
+ char *cp;
+
+ return (cp = variable_get(VAR_DEBUG)) && strcmp(cp, "no");
+}
+
+/* Whack up an informational message on the status line, in stand-out */
+void
+msgYap(char *fmt, ...)
+{
+ va_list args;
+ char *errstr;
+ int attrs;
+
+ errstr = (char *)alloca(FILENAME_MAX);
+ va_start(args, fmt);
+ vsnprintf(errstr, FILENAME_MAX, fmt, args);
+ va_end(args);
+ attrs = getattrs(stdscr);
+ attrset(A_REVERSE);
+ mvaddstr(StatusLine, 0, errstr);
+ attrset(attrs);
+ refresh();
+}
+
+/* Whack up an informational message on the status line */
+void
+msgInfo(char *fmt, ...)
+{
+ va_list args;
+ char *errstr;
+ int i, attrs;
+ char line[81];
+
+ attrs = getattrs(stdscr);
+ /* NULL is a special convention meaning "erase the old stuff" */
+ if (!fmt) {
+ move(StatusLine, 0);
+ clrtoeol();
+ return;
+ }
+ errstr = (char *)alloca(FILENAME_MAX);
+ va_start(args, fmt);
+ vsnprintf(errstr, FILENAME_MAX, fmt, args);
+ va_end(args);
+ memset(line, ' ', 80);
+ for (i = 0; i < 80; i++) {
+ if (errstr[i])
+ line[i] = errstr[i];
+ else
+ break;
+ }
+ line[80] = '\0';
+ attrset(ATTR_TITLE);
+ mvaddstr(StatusLine, 0, line);
+ attrset(attrs);
+ move(StatusLine, 79);
+ refresh();
+}
+
+/* Whack up a warning on the status line */
+void
+msgWarn(char *fmt, ...)
+{
+ va_list args;
+ char *errstr;
+ int attrs;
+
+ errstr = (char *)alloca(FILENAME_MAX);
+ strcpy(errstr, "Warning: ");
+ va_start(args, fmt);
+ vsnprintf((char *)(errstr + strlen(errstr)), FILENAME_MAX, fmt, args);
+ va_end(args);
+ attrs = getattrs(stdscr);
+ beep();
+ attrset(ATTR_TITLE);
+ mvaddstr(StatusLine, 0, errstr);
+ attrset(attrs);
+ refresh();
+ if (OnVTY && isDebug())
+ msgDebug("Warning message `%s'\n", errstr);
+}
+
+/* Whack up an error on the status line */
+void
+msgError(char *fmt, ...)
+{
+ va_list args;
+ char *errstr;
+ int attrs;
+
+ errstr = (char *)alloca(FILENAME_MAX);
+ strcpy(errstr, "Error: ");
+ va_start(args, fmt);
+ vsnprintf((char *)(errstr + strlen(errstr)), FILENAME_MAX, fmt, args);
+ va_end(args);
+ beep();
+ attrs = getattrs(stdscr);
+ attrset(ATTR_TITLE);
+ mvaddstr(StatusLine, 0, errstr);
+ attrset(attrs);
+ refresh();
+ if (OnVTY && isDebug())
+ msgDebug("Error message `%s'\n", errstr);
+}
+
+/* Whack up a fatal error on the status line */
+void
+msgFatal(char *fmt, ...)
+{
+ va_list args;
+ char *errstr;
+ int attrs;
+
+ errstr = (char *)alloca(FILENAME_MAX);
+ strcpy(errstr, "Fatal Error: ");
+ va_start(args, fmt);
+ vsnprintf((char *)(errstr + strlen(errstr)), FILENAME_MAX, fmt, args);
+ va_end(args);
+ beep();
+ attrs = getattrs(stdscr);
+ attrset(ATTR_TITLE);
+ mvaddstr(StatusLine, 0, errstr);
+ addstr(" - ");
+ addstr("PRESS ANY KEY TO ");
+ if (getpid() == 1)
+ addstr("REBOOT");
+ else
+ addstr("QUIT");
+ attrset(attrs);
+ refresh();
+ if (OnVTY)
+ msgDebug("Fatal error `%s'!\n", errstr);
+ getch();
+ systemShutdown(1);
+}
+
+/* Put up a message in a popup confirmation box */
+void
+msgConfirm(char *fmt, ...)
+{
+ va_list args;
+ char *errstr;
+
+ errstr = (char *)alloca(FILENAME_MAX);
+ va_start(args, fmt);
+ vsnprintf(errstr, FILENAME_MAX, fmt, args);
+ va_end(args);
+ use_helpline(NULL);
+ use_helpfile(NULL);
+ if (OnVTY) {
+ ioctl(0, VT_ACTIVATE, 1);
+ msgInfo(NULL);
+ }
+ dialog_notify(errstr);
+}
+
+/* Put up a message in a popup information box */
+void
+msgNotify(char *fmt, ...)
+{
+ va_list args;
+ char *errstr;
+
+ errstr = (char *)alloca(FILENAME_MAX);
+ va_start(args, fmt);
+ vsnprintf(errstr, FILENAME_MAX, fmt, args);
+ va_end(args);
+ use_helpline(NULL);
+ use_helpfile(NULL);
+ if (isDebug())
+ msgDebug("Notify: %s\n", errstr);
+ dialog_clear_norefresh();
+ dialog_msgbox(NULL, errstr, -1, -1, 0);
+}
+
+/* Put up a message in a popup yes/no box and return 1 for YES, 0 for NO */
+int
+msgYesNo(char *fmt, ...)
+{
+ va_list args;
+ char *errstr;
+ int ret;
+
+ errstr = (char *)alloca(FILENAME_MAX);
+ va_start(args, fmt);
+ vsnprintf(errstr, FILENAME_MAX, fmt, args);
+ va_end(args);
+ use_helpline(NULL);
+ use_helpfile(NULL);
+ if (OnVTY) {
+ ioctl(0, VT_ACTIVATE, 1); /* Switch back */
+ msgInfo(NULL);
+ }
+ ret = dialog_yesno("User Confirmation Requested", errstr, -1, -1);
+ return ret;
+}
+
+/* Put up a message in an input box and return the value */
+char *
+msgGetInput(char *buf, char *fmt, ...)
+{
+ va_list args;
+ char *errstr;
+ static char input_buffer[256];
+ int rval;
+
+ errstr = (char *)alloca(FILENAME_MAX);
+ va_start(args, fmt);
+ vsnprintf(errstr, FILENAME_MAX, fmt, args);
+ va_end(args);
+ use_helpline(NULL);
+ use_helpfile(NULL);
+ if (buf)
+ SAFE_STRCPY(input_buffer, buf);
+ else
+ input_buffer[0] = '\0';
+ if (OnVTY) {
+ ioctl(0, VT_ACTIVATE, 1); /* Switch back */
+ msgInfo(NULL);
+ }
+ rval = dialog_inputbox("Value Required", errstr, -1, -1, input_buffer);
+ if (!rval)
+ return input_buffer;
+ else
+ return NULL;
+}
+
+/* Write something to the debugging port */
+void
+msgDebug(char *fmt, ...)
+{
+ va_list args;
+ char *dbg;
+
+ if (DebugFD == -1)
+ return;
+ dbg = (char *)alloca(FILENAME_MAX);
+ strcpy(dbg, "DEBUG: ");
+ va_start(args, fmt);
+ vsnprintf((char *)(dbg + strlen(dbg)), FILENAME_MAX, fmt, args);
+ va_end(args);
+ write(DebugFD, dbg, strlen(dbg));
+}
+
+/* Tell the user there's some output to go look at */
+void
+msgWeHaveOutput(char *fmt, ...)
+{
+ va_list args;
+ char *errstr;
+
+ errstr = (char *)alloca(FILENAME_MAX);
+ va_start(args, fmt);
+ vsnprintf(errstr, FILENAME_MAX, fmt, args);
+ va_end(args);
+ use_helpline(NULL);
+ use_helpfile(NULL);
+ msgDebug("Notify: %s\n", errstr);
+ dialog_clear_norefresh();
+ dialog_msgbox(NULL, errstr, -1, -1, 0);
+}
+
+/* Simple versions of msgConfirm() and msgNotify() for calling from scripts */
+int
+msgSimpleConfirm(char *str)
+{
+ msgConfirm(str);
+ return DITEM_SUCCESS;
+}
+
+int
+msgSimpleNotify(char *str)
+{
+ msgNotify(str);
+ return DITEM_SUCCESS;
+}
diff --git a/usr.sbin/sysinstall/network.c b/usr.sbin/sysinstall/network.c
new file mode 100644
index 0000000..bb60e28
--- /dev/null
+++ b/usr.sbin/sysinstall/network.c
@@ -0,0 +1,300 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last attempt in the `sysinstall' line, the next
+ * generation being slated to essentially a complete rewrite.
+ *
+ * $Id$
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in the file.
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+/* These routines deal with getting things off of network media */
+
+#include "sysinstall.h"
+#include <signal.h>
+#include <sys/fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+
+static Boolean networkInitialized;
+static pid_t startPPP(Device *devp);
+
+static pid_t pppPID;
+
+Boolean
+mediaInitNetwork(Device *dev)
+{
+ int i;
+ char *rp;
+ char *cp, ifconfig[255];
+
+ if (!RunningAsInit || networkInitialized)
+ return TRUE;
+
+ msgDebug("Init routine called for network device %s.\n", dev->name);
+ if (!file_readable("/etc/resolv.conf"))
+ configResolv();
+
+ /* Old PPP process lying around? */
+ if (pppPID) {
+ msgNotify("Killing previous PPP process %d.", pppPID);
+ kill(pppPID, SIGTERM);
+ pppPID = 0;
+ }
+ if (!strncmp("ppp", dev->name, 3)) { /* PPP? */
+ if (!(pppPID = startPPP(dev))) {
+ msgConfirm("Unable to start PPP! This installation method cannot be used.");
+ return FALSE;
+ }
+ networkInitialized = TRUE;
+ return TRUE;
+ }
+ else if (!strncmp("sl", dev->name, 2)) { /* SLIP? */
+ char *val;
+ char attach[256];
+
+ dialog_clear_norefresh();
+ /* Cheesy slip attach */
+ snprintf(attach, 256, "slattach -a -h -l -s 9600 %s", dev->devname);
+ val = msgGetInput(attach,
+ "Warning: SLIP is rather poorly supported in this revision\n"
+ "of the installation due to the lack of a dialing utility.\n"
+ "If you can use PPP for this instead then you're much better\n"
+ "off doing so, otherwise SLIP works fairly well for *hardwired*\n"
+ "links. Please edit the following slattach command for\n"
+ "correctness (default here is: VJ compression, Hardware flow-\n"
+ "control, ignore carrier and 9600 baud data rate). When you're\n"
+ "ready, press [ENTER] to execute it.");
+ if (!val) {
+ msgConfirm("slattach command was empty. Try again!");
+ return FALSE;
+ }
+ else
+ SAFE_STRCPY(attach, val);
+ /*
+ * Doing this with vsystem() is actually bogus since we should be storing the pid of slattach
+ * for later killing. It's just too convenient to call vsystem(), however, rather than
+ * constructing a proper argument for exec() so we punt on doing slip right for now.
+ */
+ if (vsystem(attach)) {
+ msgConfirm("slattach returned a bad status! Please verify that\n"
+ "the command is correct and try this operation again.");
+ return FALSE;
+ }
+ }
+
+ snprintf(ifconfig, 255, "%s%s", VAR_IFCONFIG, dev->name);
+ cp = variable_get(ifconfig);
+ if (!cp) {
+ msgConfirm("The %s device is not configured. You will need to do so\n"
+ "in the Networking configuration menu before proceeding.", dev->name);
+ return FALSE;
+ }
+ msgNotify("ifconfig %s %s", dev->name, cp);
+ i = vsystem("ifconfig %s %s", dev->name, cp);
+ if (i) {
+ msgConfirm("Unable to configure the %s interface!\n"
+ "This installation method cannot be used.", dev->name);
+ return FALSE;
+ }
+
+ rp = variable_get(VAR_GATEWAY);
+ if (!rp || *rp == '0') {
+ msgConfirm("No gateway has been set. You may be unable to access hosts\n"
+ "not on your local network");
+ }
+ else {
+ msgNotify("Adding default route to %s.", rp);
+ vsystem("route add default %s", rp);
+ }
+ if (isDebug())
+ msgDebug("Network initialized successfully.\n");
+ networkInitialized = TRUE;
+ return TRUE;
+}
+
+void
+mediaShutdownNetwork(Device *dev)
+{
+ char *cp;
+
+ if (!RunningAsInit || !networkInitialized)
+ return;
+
+ msgDebug("Shutdown called for network device %s\n", dev->name);
+ /* Not a serial device? */
+ if (strncmp("sl", dev->name, 2) && strncmp("ppp", dev->name, 3)) {
+ int i;
+ char ifconfig[255];
+
+ snprintf(ifconfig, 255, "%s%s", VAR_IFCONFIG, dev->name);
+ cp = variable_get(ifconfig);
+ if (!cp)
+ return;
+ msgNotify("ifconfig %s down", dev->name);
+ i = vsystem("ifconfig %s down", dev->name);
+ if (i)
+ msgConfirm("Warning: Unable to down the %s interface properly", dev->name);
+ cp = variable_get(VAR_GATEWAY);
+ if (cp) {
+ msgNotify("Deleting default route.");
+ vsystem("route delete default");
+ }
+ }
+ else if (pppPID) {
+ msgNotify("Killing previous PPP process %d.", pppPID);
+ kill(pppPID, SIGTERM);
+ pppPID = 0;
+ }
+ networkInitialized = FALSE;
+}
+
+/* Start PPP on the 3rd screen */
+static pid_t
+startPPP(Device *devp)
+{
+ int fd2;
+ FILE *fp;
+ char *val;
+ pid_t pid = 0;
+ char myaddr[16], provider[16], speed[16];
+
+ /* These are needed to make ppp work */
+ Mkdir("/var/log");
+ Mkdir("/var/spool/lock");
+ Mkdir("/etc/ppp");
+
+ dialog_clear_norefresh();
+ if (!variable_get(VAR_SERIAL_SPEED))
+ variable_set2(VAR_SERIAL_SPEED, "115200");
+ /* 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.");
+ SAFE_STRCPY(speed, (val && *val) ? val : "115200");
+
+ val = variable_get(VAR_GATEWAY);
+ SAFE_STRCPY(provider, (val && *val) ? val : "0");
+
+ dialog_clear_norefresh();
+ val = msgGetInput(provider, "Enter the IP address of your service provider or 0 if you\n"
+ "don't know it and would prefer to negotiate it dynamically.");
+ SAFE_STRCPY(provider, (val && *val) ? val : "0");
+
+ if (devp->private && ((DevInfo *)devp->private)->ipaddr[0])
+ SAFE_STRCPY(myaddr, ((DevInfo *)devp->private)->ipaddr);
+ else
+ strcpy(myaddr, "0");
+
+ if (!Fake)
+ fp = fopen("/etc/ppp/ppp.linkup", "w");
+ else
+ fp = fopen("/dev/stderr", "w");
+ if (fp != NULL) {
+ fprintf(fp, "MYADDR:\n");
+ fprintf(fp, " delete ALL\n");
+ fprintf(fp, " add 0 0 HISADDR\n");
+ fchmod(fileno(fp), 0755);
+ fclose(fp);
+ }
+ if (!Fake)
+ fd2 = open("/etc/ppp/ppp.secret", O_CREAT);
+ else
+ fd2 = -1;
+ if (fd2 != -1) {
+ fchmod(fd2, 0700);
+ close(fd2);
+ }
+ if (!Fake)
+ fp = fopen("/etc/ppp/ppp.conf", "w");
+ else
+ fp = fopen("/dev/stderr", "w");
+ if (!fp) {
+ msgConfirm("Couldn't open /etc/ppp/ppp.conf file! This isn't going to work");
+ return 0;
+ }
+ fprintf(fp, "default:\n");
+ fprintf(fp, " set speed %s\n", speed);
+ fprintf(fp, " set device %s\n", devp->devname);
+ fprintf(fp, " set ifaddr %s %s\n", myaddr, provider);
+ fprintf(fp, " set timeout 0\n");
+ fclose(fp);
+
+ if (!Fake && !file_readable("/dev/tun0") && mknod("/dev/tun0", 0600 | S_IFCHR, makedev(52, 0))) {
+ msgConfirm("Warning: No /dev/tun0 device. PPP will not work!");
+ return 0;
+ }
+
+ if (isDebug())
+ msgDebug("About to start PPP on device %s @ %s baud. Provider = %s\n", devp->devname, speed, provider);
+
+ if (!Fake && !(pid = fork())) {
+ int i, fd;
+ struct termios foo;
+ extern int login_tty(int);
+
+ for (i = getdtablesize(); i >= 0; i--)
+ close(i);
+
+ /* We're going over to VTY2 */
+ fd = open("/dev/ttyv2", O_RDWR);
+ ioctl(0, TIOCSCTTY, &fd);
+ dup2(0, 1);
+ dup2(0, 2);
+ DebugFD = 2;
+ if (login_tty(fd) == -1)
+ msgDebug("ppp: Can't set the controlling terminal.\n");
+ signal(SIGTTOU, SIG_IGN);
+ if (tcgetattr(fd, &foo) != -1) {
+ foo.c_cc[VERASE] = '\010';
+ if (tcsetattr(fd, TCSANOW, &foo) == -1)
+ msgDebug("ppp: Unable to set the erase character.\n");
+ }
+ else
+ msgDebug("ppp: Unable to get the terminal attributes!\n");
+ execlp("ppp", "ppp", (char *)NULL);
+ msgDebug("PPP process failed to exec!\n");
+ exit(1);
+ }
+ else {
+ dialog_clear_norefresh();
+ msgConfirm("NOTICE: The PPP command is now started on VTY3 (type ALT-F3 to\n"
+ "interact with it, ALT-F1 to switch back here). The only command\n"
+ "you'll probably want or need to use is the \"term\" command\n"
+ "which starts a terminal emulator you can use to talk to your\n"
+ "modem and dial the service provider. Once you're connected,\n"
+ "come back to this screen and press return.\n\n"
+ "DO NOT PRESS [ENTER] HERE UNTIL THE CONNECTION IS FULLY\n"
+ "ESTABLISHED!");
+ }
+ return pid;
+}
diff --git a/usr.sbin/sysinstall/nfs.c b/usr.sbin/sysinstall/nfs.c
new file mode 100644
index 0000000..8ab7521
--- /dev/null
+++ b/usr.sbin/sysinstall/nfs.c
@@ -0,0 +1,110 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last attempt in the `sysinstall' line, the next
+ * generation being slated to essentially a complete rewrite.
+ *
+ * $Id$
+ *
+ * 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;
+
+Boolean
+mediaInitNFS(Device *dev)
+{
+ char *mountpoint = "/dist";
+ Device *netDevice = (Device *)dev->private;
+
+ if (NFSMounted)
+ return TRUE;
+
+ if (netDevice && !netDevice->init(netDevice))
+ return FALSE;
+
+ if (Mkdir(mountpoint))
+ return FALSE;
+
+ msgNotify("Mounting %s over NFS on %s", dev->name, mountpoint);
+ if (vsystem("mount_nfs %s %s %s %s",
+ variable_get(VAR_SLOW_ETHER) ? "-r 1024 -w 1024" : "",
+ variable_get(VAR_NFS_SECURE) ? "-P" : "", dev->name, mountpoint)) {
+ msgConfirm("Error mounting %s on %s: %s.", dev->name, mountpoint, strerror(errno));
+ if (netDevice)
+ netDevice->shutdown(netDevice);
+ return FALSE;
+ }
+ NFSMounted = TRUE;
+ msgDebug("Mounted NFS device %s onto %s\n", dev->name, mountpoint);
+ return TRUE;
+}
+
+FILE *
+mediaGetNFS(Device *dev, char *file, Boolean probe)
+{
+ char buf[PATH_MAX];
+
+ if (isDebug())
+ msgDebug("Request for %s from NFS\n", file);
+ snprintf(buf, PATH_MAX, "/dist/%s", file);
+ if (file_readable(buf))
+ return fopen(buf, "r");
+ snprintf(buf, PATH_MAX, "/dist/dists/%s", file);
+ if (file_readable(buf))
+ return fopen(buf, "r");
+ snprintf(buf, PATH_MAX, "/dist/%s/%s", variable_get(VAR_RELNAME), file);
+ if (file_readable(buf))
+ return fopen(buf, "r");
+ snprintf(buf, PATH_MAX, "/dist/%s/dists/%s", variable_get(VAR_RELNAME), file);
+ return fopen(buf, "r");
+}
+
+void
+mediaShutdownNFS(Device *dev)
+{
+ /* Device *netdev = (Device *)dev->private; */
+ char *mountpoint = "/dist";
+
+ if (!NFSMounted)
+ return;
+ msgNotify("Unmounting NFS partition on %s", mountpoint);
+ if (unmount(mountpoint, MNT_FORCE) != 0)
+ msgConfirm("Could not unmount the NFS partition: %s", strerror(errno));
+ msgDebug("Unmount of NFS partition successful\n");
+ /* if (netdev) netdev->shutdown(netdev); */
+ NFSMounted = FALSE;
+ return;
+}
diff --git a/usr.sbin/sysinstall/options.c b/usr.sbin/sysinstall/options.c
new file mode 100644
index 0000000..b4b724d
--- /dev/null
+++ b/usr.sbin/sysinstall/options.c
@@ -0,0 +1,304 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last attempt in the `sysinstall' line, the next
+ * generation being slated for what's essentially a complete rewrite.
+ *
+ * $Id: options.c,v 1.55 1997/06/18 05:11:37 jkh Exp $
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include "sysinstall.h"
+#include <ctype.h>
+
+static char *
+varCheck(Option opt)
+{
+ char *cp = NULL;
+
+ if (opt.aux)
+ cp = variable_get((char *)opt.aux);
+ if (!cp)
+ return "NO";
+ return cp;
+}
+
+/* Show our little logo */
+static char *
+resetLogo(char *str)
+{
+ return "[RESET!]";
+}
+
+static char *
+mediaCheck(Option opt)
+{
+ if (mediaDevice) {
+ switch(mediaDevice->type) {
+ case DEVICE_TYPE_UFS:
+ case DEVICE_TYPE_DISK:
+ return "File system";
+
+ case DEVICE_TYPE_FLOPPY:
+ return "Floppy";
+
+ case DEVICE_TYPE_FTP:
+ return "FTP";
+
+ case DEVICE_TYPE_CDROM:
+ return "CDROM";
+
+ case DEVICE_TYPE_TAPE:
+ return "Tape";
+
+ case DEVICE_TYPE_DOS:
+ return "DOS";
+
+ case DEVICE_TYPE_NFS:
+ return "NFS";
+
+ case DEVICE_TYPE_NONE:
+ case DEVICE_TYPE_NETWORK:
+ case DEVICE_TYPE_ANY:
+ default:
+ return "<unknown>";
+ }
+ }
+ return "<not yet set>";
+}
+
+#define TAPE_PROMPT "Please enter the tape block size in 512 byte blocks:"
+#define RELNAME_PROMPT "Please specify the release you wish to load or\n\"none\" for a generic release install:"
+#define BPKG_PROMPT "Please specify the name of the HTML browser package:"
+#define BBIN_PROMPT "Please specify a full pathname to the HTML browser binary:"
+#define EDITOR_PROMPT "Please specify the name of the text editor you wish to use:"
+#define PKG_PROMPT "Please specify a temporary directory with lots of free space:"
+#define INSTROOT_PROMPT "Please specify a root directory if installing somewhere other than /"
+#define TIMEOUT_PROMPT "Please specify the number of seconds to wait for slow media:"
+#define GATED_PKG_PROMPT "Please specify the package name for the gated software:"
+#define PCNFSD_PKG_PROMPT "Please specify the package name for the PCNFSD server:"
+
+static Option Options[] = {
+{ "NFS Secure", "NFS server talks only on a secure port",
+ OPT_IS_VAR, NULL, VAR_NFS_SECURE, varCheck },
+{ "NFS Slow", "User is using a slow PC or ethernet card",
+ OPT_IS_VAR, NULL, VAR_SLOW_ETHER, varCheck },
+{ "Debugging", "Emit extra debugging output on VTY2 (ALT-F2)",
+ OPT_IS_VAR, NULL, VAR_DEBUG, varCheck },
+{ "No Warnings", "Don't Warn the user when a setting seems incorrect",
+ OPT_IS_VAR, NULL, VAR_NO_WARN, varCheck },
+{ "Yes to All", "Assume \"Yes\" answers to all non-critical dialogs",
+ OPT_IS_VAR, NULL, VAR_NO_CONFIRM, varCheck },
+{ "FTP username", "Username and password to use instead of anonymous",
+ OPT_IS_FUNC, mediaSetFTPUserPass, VAR_FTP_USER, varCheck },
+{ "Editor", "Which text editor to use during installation",
+ OPT_IS_VAR, EDITOR_PROMPT, VAR_EDITOR, varCheck },
+{ "Tape Blocksize", "Tape media block size in 512 byte blocks",
+ OPT_IS_VAR, TAPE_PROMPT, VAR_TAPE_BLOCKSIZE, varCheck },
+{ "Extract Detail", "How verbosely to display file name information during extractions",
+ OPT_IS_FUNC, mediaSetCPIOVerbosity, VAR_CPIO_VERBOSITY, varCheck },
+{ "Release Name", "Which release to attempt to load from installation media",
+ OPT_IS_VAR, RELNAME_PROMPT, VAR_RELNAME, varCheck },
+{ "Install Root", "Which directory to unpack distributions or packages relative to",
+ OPT_IS_VAR, INSTROOT_PROMPT, VAR_INSTALL_ROOT, varCheck },
+{ "Browser package", "This is the browser package that will be used for viewing HTML docs",
+ OPT_IS_VAR, BPKG_PROMPT, VAR_BROWSER_PACKAGE, varCheck },
+{ "Browser Exec", "This is the path to the main binary of the browser package",
+ OPT_IS_VAR, BBIN_PROMPT, VAR_BROWSER_BINARY, varCheck },
+{ "Media Type", "The current installation media type.",
+ OPT_IS_FUNC, mediaGetType, VAR_MEDIA_TYPE, mediaCheck },
+{ "Media Timeout", "Timeout value in seconds for slow media.",
+ OPT_IS_VAR, TIMEOUT_PROMPT, VAR_MEDIA_TIMEOUT, varCheck },
+{ "Package Temp", "The directory where package temporary files should go",
+ OPT_IS_VAR, PKG_PROMPT, VAR_PKG_TMPDIR, varCheck },
+{ "Gated package", "The name of the gated package to install if requested",
+ OPT_IS_VAR, GATED_PKG_PROMPT, VAR_GATED_PKG, varCheck },
+{ "PCNFSD package", "The name of the PCNFSD package to install if requested",
+ OPT_IS_VAR, PCNFSD_PKG_PROMPT, VAR_PCNFSD_PKG, varCheck },
+{ "Use Defaults", "Reset all values to startup defaults",
+ OPT_IS_FUNC, installVarDefaults, 0, resetLogo },
+{ NULL },
+};
+
+#define OPT_START_ROW 4
+#define OPT_END_ROW 19
+#define OPT_NAME_COL 0
+#define OPT_VALUE_COL 16
+#define GROUP_OFFSET 40
+
+static char *
+value_of(Option opt)
+{
+ static char ival[40];
+
+ switch (opt.type) {
+ case OPT_IS_STRING:
+ return (char *)opt.data;
+
+ case OPT_IS_INT:
+ sprintf(ival, "%d", (int)opt.data);
+ return ival;
+
+ case OPT_IS_FUNC:
+ case OPT_IS_VAR:
+ if (opt.check)
+ return opt.check(opt);
+ else
+ return "<*>";
+ }
+ return "<unknown>";
+}
+
+static int
+fire(Option opt)
+{
+ int status = 0;
+
+ if (opt.type == OPT_IS_FUNC) {
+ int (*cp)(char *) = opt.data, rcode;
+
+ rcode = cp(NULL);
+ if (rcode & (DITEM_RECREATE | DITEM_RESTORE))
+ status = 1;
+ }
+ else if (opt.type == OPT_IS_VAR) {
+ if (opt.data) {
+ (void)variable_get_value(opt.aux, opt.data);
+ status = 1;
+ }
+ else if (variable_get(opt.aux))
+ variable_unset(opt.aux);
+ else
+ variable_set2(opt.aux, "YES");
+ }
+ if (opt.check)
+ opt.check(opt);
+ refresh();
+ return status;
+}
+
+int
+optionsEditor(dialogMenuItem *self)
+{
+ int i, optcol, optrow, key;
+ static int currOpt = 0;
+
+ dialog_clear_norefresh();
+ clear();
+
+ while (1) {
+ /* Whap up the header */
+ attrset(A_REVERSE); mvaddstr(0, 0, "Options Editor"); attrset(A_NORMAL);
+ for (i = 0; i < 2; i++) {
+ mvaddstr(OPT_START_ROW - 2, OPT_NAME_COL + (i * GROUP_OFFSET), "Name");
+ mvaddstr(OPT_START_ROW - 1, OPT_NAME_COL + (i * GROUP_OFFSET), "----");
+
+ mvaddstr(OPT_START_ROW - 2, OPT_VALUE_COL + (i * GROUP_OFFSET), "Value");
+ mvaddstr(OPT_START_ROW - 1, OPT_VALUE_COL + (i * GROUP_OFFSET), "-----");
+ }
+ /* And the footer */
+ mvprintw(OPT_END_ROW + 1, 0, "Use SPACE to select/toggle an option, arrow keys to move,");
+ mvprintw(OPT_END_ROW + 2, 0, "? or F1 for more help. When you're done, type Q to Quit.");
+
+ optrow = OPT_START_ROW;
+ optcol = OPT_NAME_COL;
+ for (i = 0; Options[i].name; i++) {
+ /* Names are painted somewhat gratuitously each time, but it's easier this way */
+ mvprintw(optrow, OPT_NAME_COL + optcol, Options[i].name);
+ if (currOpt == i)
+ attrset(ATTR_SELECTED);
+ mvprintw(optrow++, OPT_VALUE_COL + optcol, value_of(Options[i]));
+ if (currOpt == i)
+ attrset(A_NORMAL);
+ if (optrow == OPT_END_ROW) {
+ optrow = OPT_START_ROW;
+ optcol += GROUP_OFFSET;
+ }
+ clrtoeol();
+ }
+ attrset(ATTR_TITLE);
+ mvaddstr(OPT_END_ROW + 4, 0, Options[currOpt].desc);
+ attrset(A_NORMAL);
+ clrtoeol();
+ move(0, 14);
+ refresh();
+
+ /* Start the edit loop */
+ key = toupper(getch());
+ switch (key) {
+ case KEY_F(1):
+ case '?':
+ systemDisplayHelp("options");
+ clear();
+ break;
+
+ case '\020': /* ^P */
+ case KEY_UP:
+ if (currOpt)
+ --currOpt;
+ else
+ for (currOpt = 0; Options[currOpt + 1].name; currOpt++);
+ continue;
+
+ case '\016': /* ^N */
+ case KEY_DOWN:
+ if (Options[currOpt + 1].name)
+ ++currOpt;
+ else
+ currOpt = 0;
+ continue;
+
+ case KEY_HOME:
+ currOpt = 0;
+ continue;
+
+ case KEY_END:
+ while (Options[currOpt + 1].name)
+ ++currOpt;
+ continue;
+
+ case ' ':
+ if (fire(Options[currOpt]))
+ clear();
+ continue;
+
+ case '\033': /* ESC */
+ case 'Q':
+ clear();
+ dialog_clear();
+ return DITEM_SUCCESS | DITEM_RESTORE;
+
+ default:
+ beep();
+ }
+ }
+ /* NOTREACHED */
+ return DITEM_SUCCESS | DITEM_RESTORE;
+}
+
diff --git a/usr.sbin/sysinstall/package.c b/usr.sbin/sysinstall/package.c
new file mode 100644
index 0000000..da1ec9c
--- /dev/null
+++ b/usr.sbin/sysinstall/package.c
@@ -0,0 +1,219 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last program in the `sysinstall' line - the next
+ * generation being essentially a complete rewrite.
+ *
+ * $Id: package.c,v 1.64 1997/09/17 16:18:16 pst Exp $
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include "sysinstall.h"
+#include <sys/errno.h>
+#include <sys/time.h>
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+
+static Boolean sigpipe_caught = FALSE;
+
+static void
+catch_pipe(int sig)
+{
+ sigpipe_caught = TRUE;
+}
+
+/* Like package_extract, but assumes current media device */
+int
+package_add(char *name)
+{
+ if (!mediaVerify())
+ return DITEM_FAILURE;
+ return package_extract(mediaDevice, name, FALSE);
+}
+
+/* For use by dispatch */
+int
+packageAdd(dialogMenuItem *self)
+{
+ char *cp;
+
+ cp = variable_get(VAR_PACKAGE);
+ if (!cp) {
+ msgDebug("packageAdd: No package name passed in package variable\n");
+ return DITEM_FAILURE;
+ }
+ else
+ return package_add(cp);
+}
+
+Boolean
+package_exists(char *name)
+{
+ char fname[FILENAME_MAX];
+ int status /* = vsystem("pkg_info -e %s", name) */;
+
+ /* XXX KLUDGE ALERT! This makes evil assumptions about how XXX
+ * packages register themselves and should *really be done with
+ * `pkg_info -e <name>' except that this it's too slow for an
+ * item check routine.. :-(
+ */
+ snprintf(fname, FILENAME_MAX, "/var/db/pkg/%s", name);
+ status = access(fname, R_OK);
+ if (isDebug())
+ msgDebug("package check for %s returns %s.\n", name, status ? "failure" : "success");
+ return !status;
+}
+
+/* Extract a package based on a namespec and a media device */
+int
+package_extract(Device *dev, char *name, Boolean depended)
+{
+ char path[511];
+ int ret, last_msg = 0;
+ FILE *fp;
+
+ /* Check to make sure it's not already there */
+ if (package_exists(name))
+ return DITEM_SUCCESS;
+
+ /* If necessary, initialize the ldconfig hints */
+ if (!file_readable("/var/run/ld.so.hints"))
+ vsystem("ldconfig /usr/lib /usr/local/lib /usr/X11R6/lib");
+
+ if (!dev->init(dev)) {
+ msgConfirm("Unable to initialize media type for package extract.");
+ return DITEM_FAILURE;
+ }
+
+ /* Be initially optimistic */
+ ret = DITEM_SUCCESS | DITEM_RESTORE;
+ /* Make a couple of paranoid locations for temp files to live if user specified none */
+ if (!variable_get(VAR_PKG_TMPDIR)) {
+ /* Set it to a location with as much space as possible */
+ variable_set2(VAR_PKG_TMPDIR, "/usr/tmp");
+ }
+ Mkdir(variable_get(VAR_PKG_TMPDIR));
+ vsystem("chmod 1777 %s", variable_get(VAR_PKG_TMPDIR));
+
+ if (!index(name, '/'))
+ sprintf(path, "packages/All/%s%s", name, strstr(name, ".tgz") ? "" : ".tgz");
+ else
+ sprintf(path, "%s%s", name, strstr(name, ".tgz") ? "" : ".tgz");
+ fp = dev->get(dev, path, TRUE);
+ if (fp) {
+ int i = 0, tot, pfd[2];
+ pid_t pid;
+
+ signal(SIGPIPE, catch_pipe);
+ msgNotify("Adding %s%s\nfrom %s", path, depended ? " (as a dependency)" : "", dev->name);
+ pipe(pfd);
+ pid = fork();
+ if (!pid) {
+ dup2(pfd[0], 0); close(pfd[0]);
+ dup2(DebugFD, 1);
+ close(2);
+ close(pfd[1]);
+ i = execl("/usr/sbin/pkg_add", "/usr/sbin/pkg_add", "-", 0);
+ if (isDebug())
+ msgDebug("pkg_add returns %d status\n", i);
+ }
+ else {
+ char buf[BUFSIZ];
+ WINDOW *w = savescr();
+ struct timeval start, stop;
+
+ close(pfd[0]);
+ tot = 0;
+ (void)gettimeofday(&start, (struct timezone *)0);
+
+ while (!sigpipe_caught && (i = fread(buf, 1, BUFSIZ, fp)) > 0) {
+ int seconds;
+
+ tot += i;
+ /* Print statistics about how we're doing */
+ (void) gettimeofday(&stop, (struct timezone *)0);
+ stop.tv_sec = stop.tv_sec - start.tv_sec;
+ stop.tv_usec = stop.tv_usec - start.tv_usec;
+ if (stop.tv_usec < 0)
+ stop.tv_sec--, stop.tv_usec += 1000000;
+ seconds = stop.tv_sec + (stop.tv_usec / 1000000.0);
+ if (!seconds)
+ seconds = 1;
+ if (seconds != last_msg) {
+ last_msg = seconds;
+ msgInfo("%10d bytes read from package %s @ %4.1f KBytes/second", tot, name, (tot / seconds) / 1024.0);
+ }
+ /* Write it out */
+ if (sigpipe_caught || write(pfd[1], buf, i) != i) {
+ msgInfo("Write failure to pkg_add! Package may be corrupt.");
+ break;
+ }
+ }
+ close(pfd[1]);
+ fclose(fp);
+ if (sigpipe_caught)
+ msgInfo("pkg_add(1) apparently did not like the %s package.", name);
+ else if (i == -1)
+ msgInfo("I/O error while reading in the %s package.", name);
+ else
+ msgInfo("Package %s read successfully - waiting for pkg_add(1)", name);
+ refresh();
+ i = waitpid(pid, &tot, 0);
+ if (sigpipe_caught || i < 0 || WEXITSTATUS(tot)) {
+ if (variable_get(VAR_NO_CONFIRM))
+ msgNotify("Add of package %s aborted, error code %d -\n"
+ "Please check the debug screen for more info.", name, WEXITSTATUS(tot));
+ else
+ msgConfirm("Add of package %s aborted, error code %d -\n"
+ "Please check the debug screen for more info.", name, WEXITSTATUS(tot));
+ }
+ else
+ msgNotify("Package %s was added successfully", name);
+
+ /* Now catch any stragglers */
+ while (wait3(&tot, WNOHANG, NULL) > 0);
+
+ sleep(1);
+ restorescr(w);
+ sigpipe_caught = FALSE;
+ }
+ }
+ else {
+ dialog_clear_norefresh();
+ if (variable_get(VAR_NO_CONFIRM))
+ msgNotify("Unable to fetch package %s from selected media.\n"
+ "No package add will be done.", name);
+ else
+ msgConfirm("Unable to fetch package %s from selected media.\n"
+ "No package add will be done.", name);
+ ret = DITEM_FAILURE | DITEM_RESTORE;
+ }
+ return ret;
+}
diff --git a/usr.sbin/sysinstall/rtermcap.c b/usr.sbin/sysinstall/rtermcap.c
new file mode 100644
index 0000000..84b3feb
--- /dev/null
+++ b/usr.sbin/sysinstall/rtermcap.c
@@ -0,0 +1,15 @@
+#include <stdio.h>
+#include <termcap.h>
+
+int
+main(int argc, char **argv)
+{
+ char buf[4096];
+ int i;
+
+ if (argc < 2)
+ return 1;
+ i = tgetent(buf, argv[1]);
+ printf("%s",buf);
+ return 0;
+}
diff --git a/usr.sbin/sysinstall/sysinstall.8 b/usr.sbin/sysinstall/sysinstall.8
new file mode 100644
index 0000000..67f5952
--- /dev/null
+++ b/usr.sbin/sysinstall/sysinstall.8
@@ -0,0 +1,804 @@
+.\" Copyright (c) 1997
+.\" Jordan Hubbard <jkh@freebsd.org>. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY Jordan Hubbard AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL Jordan Hubbard OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $Id: sysinstall.8,v 1.5 1997/09/10 10:15:41 jkh Exp $
+.\"
+.Dd August 9, 1997
+.Dt SYSINSTALL 8
+.Os
+.Sh NAME
+.Nm sysinstall
+.Nd system installation and configuration tool
+.Sh SYNOPSIS
+.Nm
+.Op Ar var=value
+.Op Ar function
+.Op Ar ...
+.Sh DESCRIPTION
+.Nm
+is a utility for installing and configuring FreeBSD systems.
+It is the first utility invoked by the FreeBSD installation boot
+floppy and is also copied into
+.Pa /stand/sysinstall
+on newly installed FreeBSD systems for use in later configuring the system.
+.Pp
+The
+.Nm
+program is generally invoked without arguments for the default
+behavior, where the main installation/configuration menu is presented.
+
+On those occasions where it is deemed necessary to invoke a subsystem
+of sysinstall directly, however, it is also possible to do so by
+naming the appropriate function entry points on the command line.
+Since this action is essentially identical to running an installation
+script, each command-line argument corresponding to a line of script,
+the reader is encouraged to read the section on scripting for more
+information on this feature.
+.Pp
+.Sh NOTES
+.Nm
+is essentially nothing more than a monolithic C program with
+the ability to write MBRs and disk labels (through the services
+of the
+.Xr libdisk 3
+library) and install distributions or packages onto new and
+existing FreeBSD systems. It also contains some extra intelligence
+for running as a replacement for
+.Xr init 8
+when it's invoked by the FreeBSD installation boot procedure. It
+assumes very little in the way of additional utility support and
+performs most file system operations by calling the relevant syscalls
+(such as
+.Xr mount 2 )
+directly.
+.Pp
+.Nm
+currently uses the
+.Xr libdialog 3
+library to do user interaction with simple ANSI line graphics, color
+support for which is enabled by either running on a syscons VTY or some
+other color-capable terminal emulator (newer versions of xterm will support
+color when using the ``xterm-color'' termcap entry).
+.Pp
+This product is currently at the end of its life cycle and will
+be replaced in FreeBSD 3.0 by the
+.Xr setup 1
+utility.
+.Sh RUNNING SCRIPTS
+.Nm
+may be either driven interactively through its various internal menus
+or run in batch mode, driven by an external script. Such a script may
+be loaded and executed in one of 3 ways:
+
+.Bl -tag -width Ds -compact
+.It Sy "LOAD_CONFIG_FILE"
+If
+.Nm
+is compiled with LOAD_CONFIG_FILE set in the environment
+(or in the Makefile) to some value, then that value will
+be used as the filename to automatically look for and load
+when
+.Nm
+starts up and with no user interaction required.
+This option is aimed primarily at large sites who wish to create a
+single prototype install for multiple machines with largely identical
+configurations and/or installation options.
+
+.It Sy "MAIN MENU"
+If
+.Nm
+is run interactively, that is to say in the default manner, it will
+bring up a main menu which contains a "load config file" option.
+Selecting this option will prompt for the name of a script file which
+it then will attempt to load from a DOS or UFS formatted floppy.
+
+.It Sy "COMMAND LINE"
+Each command line argument is treated as a script directive
+when
+.Nm
+is run in multi-user mode. Execution ends either by explicit request
+(e.g. calling the
+.Ar shutdown
+directive), upon reaching the end of the argument list or on error.
+.Pp
+For example:
+.nf
+
+/stand/sysinstall ftp=ftp:/ziggy/pub/ mediaSetFTP configPackages
+
+.fi
+Would initialize
+.Nm
+for FTP installation media (using the server `ziggy') and then
+bring up the package installation editor, exiting when finished.
+.El
+.Pp
+.Sh SCRIPT SYNTAX
+A script is a list of one or more directives, each directive taking
+the form of:
+
+.Ar var=value
+.Pp
+.Ar function
+.Pp
+or
+.Ar #somecomment
+
+Where
+.Ar var=value
+is the assignment of some internal
+.Nm
+variable, e.g. "ftpPass=FuNkYChiKn", and
+.Ar function
+is the name of an internal
+.Nm
+function, e.g. "mediaSetFTP", and
+.Ar #comment
+is a single-line comment for documentation purposes (ignored by
+sysinstall). Each directive must be by itself on a single line,
+functions taking their arguments by examining known variable names.
+This requires that you be sure to assign the relevant variables before
+calling a function which requires them. When and where a function
+depends on the settings of one or more variables will be noted in the
+following table:
+
+.Pp
+\fBFunction Glossary:\fR
+.Pp
+.Bl -tag -width indent
+.It configAnonFTP
+Invoke the Anonymous FTP configuration menu.
+.Pp
+\fBVariables:\fR None
+.It configRouter
+Select which routing daemon you wish to use, potentially
+loading any required 3rd-party routing daemons as necessary.
+.Pp
+\fBVariables:\fR
+.Bl -tag -width indent
+.It router
+can be set to the name of the desired routing daemon,
+e.g. ``routed'' or ``gated'', otherwise it is prompted for.
+.El
+.It configNFSServer
+Configure host as an NFS server.
+.Pp
+\fBVariables:\fR None
+.It configNTP
+Configure host as a user of the Network Time Protocol.
+.Pp
+\fBVariables:\fR
+.Bl -tag -width indent
+.It ntpdate_flags
+The flags to
+.Xr ntpdate 8 ,
+that is to say the name of the server to sync from.
+.El
+.It configPCNFSD
+Configure host to support PC NFS.
+.Pp
+\fBVariables:\fR
+.Bl -tag -width indent
+.It pcnfsd_pkg
+The name of the PCNFSD package to load if necessary (defaults to hard coded
+version).
+.El
+.It configPackages
+Bring up the interactive package management menu.
+.Pp
+\fBVariables:\fR None
+.It configRegister
+Register the user with the FreeBSD counter.
+.Pp
+\fBVariables:\fR None
+.It configUsers
+Add users and/or groups to the system.
+.Pp
+\fBVariables:\fR None
+.It configXEnvironment
+Configure the X display subsystem.
+.Pp
+\fBVariables:\fR None
+.It diskPartitionEditor
+Invokes the disk partition (MBR) editor.
+.Pp
+\fBVariables:\fR
+.Bl -tag -width findx
+.It geometry
+The disk geometry, as a cyls/heads/sectors formatted string. Default: no
+change to geometry.
+.It partition
+Set to disk partitioning type or size, its value being
+.Ar free
+in order to use only remaining free space for FreeBSD,
+.Ar all
+to use the entire disk for FreeBSD but maintain a proper partition
+table,
+.Ar existing
+to use an existing FreeBSD partition (first found),
+.Ar exclusive
+to use the disk in ``dangerously dedicated'' mode or, finally,
+.Ar somenumber
+to allocate
+.Ar somenumber
+blocks of available free space to a new FreeBSD partition.
+Default: Interactive mode.
+.It bootManager
+is set to one of
+.Ar boot
+to signify the installation of a boot manager,
+.Ar standard
+to signify installation of a "standard" non-boot MGR DOS
+MBR or
+.Ar none
+to indicate that no change to the boot manager is desired.
+Default: none.
+.El
+.Pp
+Note: Nothing is actually written to disk by this function, a explicit call to
+.Ar diskPartitionWrite
+being required for that to happen.
+.It diskPartitionWrite
+Causes any pending MBR changes (typically from the
+.Ar diskPartitionEditor
+function) to be written out.
+.Pp
+\fBVariables:\fR None
+.It diskLabelEditor
+Invokes the disk label editor. This is a bit trickier from a script
+since you need to essentially label everything inside each FreeBSD
+(type 0xA5) partition created by the
+.Ar diskPartitionEditor
+function, and that requires knowing a few rules about how things are
+laid out. When creating a script to automatically allocate disk space
+and partition it up, it is suggested that you first perform the
+installation interactively at least once and take careful notes as to
+what the slice names will be, then and only then hardwiring them into
+the script.
+.Pp
+For example, let's say you have a SCSI disk on which you've created a new
+FreeBSD partition in slice 2 (your DOS partition residing in slice 1).
+The slice name would be
+.Ar sd0s2
+for the whole FreeBSD partition (
+.Ar sd0s1
+being your DOS primary
+partition). Now let's further assume that you have 500MB in this
+partition and you want to sub-partition that space into root, swap,
+var and usr file systems for FreeBSD. Your invocation of the
+.Ar diskLabelEditor
+function might involve setting the following variables:
+.Bl -tag -width findx
+.It Li "sd0s2-1=ufs 40960 /"
+A 20MB root file system (all sizes are in 512 byte blocks).
+.It Li "sd0s2-2=swap 131072 /"
+A 64MB swap partition.
+.It Li "sd0s2-3=ufs 204800 /var"
+A 100MB /var file system.
+.It Li "sd0s2-4=ufs 0 /usr"
+With the balance of free space (around 316MB) going to the /usr
+file system.
+.El
+
+One can also use the
+.Ar diskLabelEditor
+for mounting or erasing existing partitions as well as creating new
+ones. Using the previous example again, let's say that we also wanted
+to mount our DOS partition and make sure that an
+.Pa /etc/fstab
+entry is created for it in the new installation. Before calling the
+.Ar diskLabelEditor
+function, we simply add an additional line:
+.nf
+ sd0s1=/dos_c N
+
+.fi
+before the call. This tells the label editor that you want to mount
+the first slice on
+.Pa /dos_c
+and not to attempt to newfs it (not that
+.Nm
+would attempt this for a DOS partition in any case, but it could just
+as easily be an existing UFS partition being named here and the 2nd
+field is non-optional).
+.Pp
+Note: No file system data is actually written to disk until an
+explicit call to
+.Ar diskLabelCommit
+is made.
+.It diskLabelCommit
+Writes out all pending disklabel information and creates and/or mounts any
+file systems which have requests pending from the
+.Ar diskLabelEditor
+function.
+.Pp
+\fBVariables:\fR None
+.It distReset
+Resets all selected distributions to the empty set (no distributions selected).
+.Pp
+\fBVariables:\fR None
+.It distSetCustom
+Allows the selection of a custom distribution set (e.g. not just on of the
+existing "canned" sets) with no user interaction.
+\fBVariables:\fR
+.Bl -tag -width indent
+.It dists
+List of distributions to load. Possible distribution values are:
+.Bl -tag -width indent
+.It Li bin
+The base binary distribution.
+.It Li doc
+Miscellaneous documentation
+.It Li games
+Games
+.It Li manpages
+Manual pages (unformatted)
+.It Li catpages
+Pre-formatted manual pages
+.It Li proflibs
+Profiled libraries for developers.
+.It Li dict
+Dictionary information (for tools like spell).
+.It Li info
+GNU info files and other extra docs.
+.It Li des
+DES encryption binaries and libraries.
+.It Li compat1x
+Compatibility with FreeBSD 1.x
+.It Li compat20
+Compatibility with FreeBSD 2.0
+.It Li compat21
+Compatibility with FreeBSD 2.1
+.It Li ports
+The ports collection.
+.It Li krb
+Kerberos binaries.
+.It Li ssecure
+/usr/src/secure
+.It Li sebones
+/usr/src/eBones
+.It Li sbase
+/usr/src/[top level files]
+.It Li scontrib
+/usr/src/contrib
+.It Li sgnu
+/usr/src/gnu
+.It Li setc
+/usr/src/etc
+.It Li sgames
+/usr/src/games
+.It Li sinclude
+/usr/src/include
+.It Li slib
+/usr/src/lib
+.It Li slibexec
+/usr/src/libexec
+.It Li slkm
+/usr/src/lkm
+.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 X331bin
+XFree86 3.3.1 binaries.
+.It Li X331cfg
+XFree86 3.3.1 configuration files.
+.It Li X331doc
+XFree86 3.3.1 documentation.
+.It Li X331html
+XFree86 3.3.1 HTML documentation.
+.It Li X331lib
+XFree86 3.3.1 libraries.
+.It Li X331lk98
+XFree86 3.3.1 server link-kit for PC98 machines.
+.It Li X331lkit
+XFree86 3.3.1 server link-kit for standard machines.
+.It Li X331man
+XFree86 3.3.1 manual pages.
+.It Li X331prog
+XFree86 3.3.1 programmer's distribution.
+.It Li X331ps
+XFree86 3.3.1 postscript documentation.
+.It Li X331set
+XFree86 3.3.1 graphical setup tool.
+.It Li X3318514
+XFree86 3.3.1 8514 server.
+.It Li X3319480
+XFree86 3.3.1 PC98 8-bit (256 color) PEGC-480 server.
+.It Li X3319EGC
+XFree86 3.3.1 PC98 4-bit (16 color) EGC server.
+.It Li X3319GA9
+XFree86 3.3.1 PC98 GA-968V4/PCI (S3 968) server.
+.It Li X3319GAN
+XFree86 3.3.1 PC98 GANB-WAP (cirrus) server.
+.It Li X3319LPW
+XFree86 3.3.1 PC98 PowerWindowLB (S3) server.
+.It Li X3319NKV
+XFree86 3.3.1 PC98 NKV-NEC (cirrus) server.
+.It Li X3319NS3
+XFree86 3.3.1 PC98 NEC (S3) server.
+.It Li X3319SPW
+XFree86 3.3.1 PC98 SKB-PowerWindow (S3) server.
+.It Li X3319TGU
+XFree86 3.3.1 PC98 Cyber9320 and TGUI9680 server.
+.It Li X3319WEP
+XFree86 3.3.1 PC98 WAB-EP (cirrus) server.
+.It Li X3319WS
+XFree86 3.3.1 PC98 WABS (cirrus) server.
+.It Li X3319WSN
+XFree86 3.3.1 PC98 WSN-A2F (cirrus) server.
+.It Li X331AGX
+XFree86 3.3.1 8 bit AGX server.
+.It Li X331I128
+XFree86 3.3.1 #9 Imagine I128 server.
+.It Li X331Ma8
+XFree86 3.3.1 ATI Mach8 server.
+.It Li X331Ma32
+XFree86 3.3.1 ATI Mach32 server.
+.It Li X331Ma64
+XFree86 3.3.1 ATI Mach64 server.
+.It Li X331Mono
+XFree86 3.3.1 monochrome server.
+.It Li X331P9K
+XFree86 3.3.1 P9000 server.
+.It Li X331S3
+XFree86 3.3.1 S3 server.
+.It Li X331S3V
+XFree86 3.3.1 S3 Virge server.
+.It Li X331SVGA
+XFree86 3.3.1 SVGA server.
+.It Li X331VG16
+XFree86 3.3.1 VGA16 server.
+.It Li X331W32
+XFree86 3.3.1 ET4000/W32, /W32i and /W32p server.
+.It Li X331nest
+XFree86 3.3.1 nested X server.
+.It Li X331vfb
+XFree86 3.3.1 virtual frame-buffer X server.
+.It Li X331fnts
+XFree86 3.3.1 base font set.
+.It Li X331f100
+XFree86 3.3.1 100DPI font set.
+.It Li X331fcyr
+XFree86 3.3.1 Cyrillic font set.
+.It Li X331fscl
+XFree86 3.3.1 scalable font set.
+.It Li X331fnon
+XFree86 3.3.1 non-english font set.
+.It Li X331fsrv
+XFree86 3.3.1 font server.
+.El
+.It distSetDeveloper
+Selects the standard Developer's distribution set.
+.Pp
+\fBVariables:\fR None
+.It distSetXDeveloper
+Selects the standard X Developer's distribution set.
+.Pp
+\fBVariables:\fR None
+.It distSetKernDeveloper
+Selects the standard kernel Developer's distribution set.
+.Pp
+\fBVariables:\fR None
+.It distSetUser
+Selects the standard user distribution set.
+.Pp
+\fBVariables:\fR None
+.It distSetXUser
+Selects the standard X user's distribution set.
+.Pp
+\fBVariables:\fR None
+.It distSetMinimum
+Selects the very minimum distribution set.
+.Pp
+\fBVariables:\fR None
+.It distSetEverything
+Selects the full whack - all available distributions.
+.Pp
+\fBVariables:\fR None
+.It distSetDES
+Interactively select DES subcomponents.
+.Pp
+\fBVariables:\fR None
+.It distSetSrc
+Interactively select source subcomponents.
+.Pp
+\fBVariables:\fR None
+.It distSetXF86
+Interactively select XFree86 3.3.1 subcomponents.
+.Pp
+\fBVariables:\fR None
+.It distExtractAll
+Install all currently selected distributions (requires that
+media device also be selected).
+.Pp
+\fBVariables:\fR None
+.It docBrowser
+Install (if necessary) an HTML documentation browser and go to the
+HTML documentation submenu.
+.Pp
+\fBVariables:\fR
+.Bl -tag -width indent
+.It browserPackage
+The name of the browser package to try and install as necessary.
+Defaults to latest lynx package.
+.It browserBinary
+The name of the browser binary itself (if overriding the
+.Ar browserPackage
+variable). Defaults to lynx.
+.El
+.It installCommit
+.Pp
+Commit any and all pending changes to disk. This function
+is essentially shorthand for a number of more granular "commit"
+functions.
+\fBVariables:\fR None
+.It installExpress
+Start an "express" installation, asking few questions of
+the user.
+.Pp
+\fBVariables:\fR None
+.It installNovice
+Start a "novice" installation, the most user-friendly
+installation type available.
+.Pp
+\fBVariables:\fR None
+.It installUpgrade
+Start an upgrade installation.
+.Pp
+\fBVariables:\fR None
+.It installFixitHoloShell
+Start up the "emergency holographic shell" over on VTY4
+if running as init.
+.Pp
+\fBVariables:\fR None
+.It installFixitCDROM
+Go into "fixit" mode, assuming a live file system CDROM
+currently in the drive.
+.Pp
+\fBVariables:\fR None
+.It installFixitFloppy
+Go into "fixit" mode, assuming an available fixit floppy
+disk (user will be prompted for it).
+.Pp
+\fBVariables:\fR None
+.It installFilesystems
+Do just the file system initialization part of an install.
+.Pp
+\fBVariables:\fR None
+.It installVarDefaults
+Initialize all variables to their defaults, overriding any
+previous settings.
+.Pp
+\fBVariables:\fR None
+.It loadConfig
+Sort of like an #include statement, it allows you to load one
+configuration file from another.
+.Pp
+\fBVariables:\fR
+.Bl -tag -width indent
+.It file
+The fully pathname of the file to load.
+.El
+.It mediaSetCDROM
+Select a FreeBSD CDROM as the installation media.
+.Pp
+\fBVariables:\fR None
+.It mediaSetFloppy
+Select a pre-made floppy installation set as the installation media.
+.Pp
+\fBVariables:\fR None
+.It mediaSetDOS
+Select an existing DOS primary partition as the installation media.
+The first primary partition found is used (e.g. C:).
+.Pp
+\fBVariables:\fR None
+.It mediaSetTape
+Select a tape device as the installation media.
+.Pp
+\fBVariables:\fR None
+.It mediaSetFTP
+Select an FTP site as the installation media.
+.Pp
+\fBVariables:\fR
+.Bl -tag -width indent
+.It hostname
+The name of the host being installed (non-optional).
+.It domainname
+The domain name of the host being installed (optional).
+.It defaultrouter
+The default router for this host (non-optional).
+.It netDev
+Which host interface to use (
+.Ar ed0
+or
+.Ar ep0 ,
+for example. Non-optional).
+.It netInteractive
+If set, bring up the interactive network setup form even
+if all relevant configuration variables are already set (optional).
+.It ipaddr
+The IP address for the selected host interface (non-optional).
+.It netmask
+The netmask for the selected host interface (non-optional).
+.It ftp
+The fully qualified URL of the FTP site containing the FreeBSD
+distribution you're interested in, e.g.
+.Ar ftp://ftp.freebsd.org/pub/FreeBSD/ .
+.El
+.It mediaSetFTPActive
+Alias for
+.Ar mediaSetFTP
+using "active" FTP transfer mode.
+.Pp
+\fBVariables:\fR Same as for
+.Ar mediaSetFTP .
+.It mediaSetFTPPassive
+Alias for
+.Ar mediaSetFTP
+using "passive" FTP transfer mode.
+.Pp
+\fBVariables:\fR Same as for
+.Ar mediaSetFTP .
+.It mediaSetUFS
+Select an existing UFS partition (mounted with the label editor) as
+the installation media.
+.Pp
+\fBVariables:\fR
+.Bl -tag -width indent
+.It ufs
+full /path to directory containing the FreeBSD distribution you're
+interested in.
+.El
+.It mediaSetNFS
+.Pp
+\fBVariables:\fR
+.Bl -tag -width indent
+.It hostname
+The name of the host being installed (non-optional).
+.It domainname
+The domain name of the host being installed (optional).
+.It defaultrouter
+The default router for this host (non-optional).
+.It netDev
+Which host interface to use (
+.Ar ed0
+or
+.Ar ep0 ,
+for example. Non-optional).
+.It netInteractive
+If set, bring up the interactive network setup form even
+if all relevant configuration variables are already set (optional).
+.It ipaddr
+The IP address for the selected host interface (non-optional).
+.It netmask
+The netmask for the selected host interface (non-optional).
+.It nfs
+full hostname:/path specification for directory containing
+the FreeBSD distribution you're interested in.
+.El
+.It mediaSetFTPUserPass
+.Pp
+\fBVariables:\fR
+.Bl -tag -width indent
+.It ftpUser
+The username to log in as on the ftp server site.
+Default: ftp
+.It ftpPass
+The password to use for this username on the ftp
+server site.
+Default: user@host
+.El
+.It mediaSetCPIOVerbosity
+.Pp
+\fBVariables:\fR
+.Bl -tag -width indent
+.It cpioVerbose
+Can be used to set the verbosity of cpio extractions to low, medium or
+high.
+.El
+.It mediaGetType
+Interactively get the user to specify some type of media.
+.Pp
+\fBVariables:\fR None
+.It optionsEditor
+Invoke the interactive options editor.
+.Pp
+\fBVariables:\fR None
+.It register
+Bring up the FreeBSD registration form.
+.Pp
+\fBVariables:\fR None
+.It packageAdd
+Try to fetch and add a package to the system (requires
+that a media type be set),
+.Pp
+\fBVariables:\fR
+.Bl -tag -width indent
+.It package
+The name of the package to add, e.g. bash-1.14.7 or ncftp-2.4.2.
+.El
+.It addGroup
+Invoke the interactive group editor.
+.Pp
+\fBVariables:\fR None
+.It addUser
+Invoke the interactive user editor.
+.Pp
+\fBVariables:\fR None
+.It shutdown
+Stop the script and terminate sysinstall.
+.Pp
+\fBVariables:\fR None
+.It system
+Execute an arbitrary command with
+.Xr system 3
+.Pp
+\fBVariables:\fR
+.Bl -tag -width indent
+.It command
+The name of the command to execute. When running
+from a boot floppy, very minimal expectations should
+be made as to what's available until/unless a relatively
+full system installation has just been done.
+.El
+.El
+.Sh FILES
+This utility may edit the contents of
+.Pa /etc/rc.conf ,
+.Pa /etc/hosts ,
+and
+.Pa /etc/resolv.conf
+as necessary to reflect changes in the network configuration.
+.Sh SEE ALSO
+If you have a reasonably complete source tree online, take
+a look at
+.Pa /usr/src/release/sysinstall/install.cfg
+for a sample installation script.
+.Sh BUGS
+This utility is a prototype which lasted approximately 2 years past
+its expiration date and is greatly in need of death.
+.Sh AUTHOR
+Jordan K. Hubbard <jkh@FreeBSD.org>
+.Sh HISTORY
+This version of
+.Nm
+first appeared in
+.Fx 2.0 .
diff --git a/usr.sbin/sysinstall/sysinstall.h b/usr.sbin/sysinstall/sysinstall.h
new file mode 100644
index 0000000..01d30d5
--- /dev/null
+++ b/usr.sbin/sysinstall/sysinstall.h
@@ -0,0 +1,726 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last attempt in the `sysinstall' line, the next
+ * generation being slated to essentially a complete rewrite.
+ *
+ * $Id: sysinstall.h,v 1.142 1997/10/12 16:21:19 jkh Exp $
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#ifndef _SYSINSTALL_H_INCLUDE
+#define _SYSINSTALL_H_INCLUDE
+
+#include <sys/types.h>
+#include <sys/wait.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"
+#include "version.h"
+
+/*** Defines ***/
+
+/* Different packages we depend on - update this when package version change! */
+#define PACKAGE_GATED "gated-3.5b3"
+#define PACKAGE_NETCON "commerce/netcon/bsd61"
+#define PACKAGE_PCNFSD "pcnfsd-93.02.16"
+#define PACKAGE_LYNX "lynx-2.7.1"
+
+/* device limits */
+#define DEV_NAME_MAX 64 /* The maximum length of a device name */
+#define DEV_MAX 100 /* The maximum number of devices we'll deal with */
+#define INTERFACE_MAX 50 /* Maximum number of network interfaces we'll deal with */
+#define IO_ERROR -2 /* Status code for I/O error rather than normal EOF */
+
+/* Number of seconds to wait for data to come off even the slowest media */
+#define MEDIA_TIMEOUT 300
+
+/*
+ * I make some pretty gross assumptions about having a max of 50 chunks
+ * total - 8 slices and 42 partitions. I can't easily display many more
+ * than that on the screen at once!
+ *
+ * For 2.1 I'll revisit this and try to make it more dynamic, but since
+ * this will catch 99.99% of all possible cases, I'm not too worried.
+ */
+#define MAX_CHUNKS 40
+
+/* Internal environment variable names */
+#define DISK_PARTITIONED "_diskPartitioned"
+#define DISK_LABELLED "_diskLabelled"
+#define DISK_SELECTED "_diskSelected"
+#define SYSTEM_STATE "_systemState"
+#define RUNNING_ON_ROOT "_runningOnRoot"
+#define TCP_CONFIGURED "_tcpConfigured"
+
+/* Ones that can be tweaked from config files */
+#define VAR_BLANKTIME "blanktime"
+#define VAR_BOOTMGR "bootManager"
+#define VAR_BROWSER_BINARY "browserBinary"
+#define VAR_BROWSER_PACKAGE "browserPackage"
+#define VAR_CPIO_VERBOSITY "cpioVerbose"
+#define VAR_DEBUG "debug"
+#define VAR_DISK "disk"
+#define VAR_DISTS "dists"
+#define VAR_DIST_MAIN "distMain"
+#define VAR_DIST_DES "distDES"
+#define VAR_DIST_SRC "distSRC"
+#define VAR_DIST_X11 "distX11"
+#define VAR_DIST_XSERVER "distXserver"
+#define VAR_DIST_XFONTS "distXfonts"
+#define VAR_DEDICATE_DISK "dedicateDisk"
+#define VAR_DOMAINNAME "domainname"
+#define VAR_EDITOR "editor"
+#define VAR_EXTRAS "ifconfig_"
+#define VAR_COMMAND "command"
+#define VAR_CONFIG_FILE "configFile"
+#define VAR_FTP_DIR "ftpDirectory"
+#define VAR_FTP_PASS "ftpPass"
+#define VAR_FTP_PATH "ftp"
+#define VAR_FTP_PORT "ftpPort"
+#define VAR_FTP_STATE "ftpState"
+#define VAR_FTP_USER "ftpUser"
+#define VAR_FTP_HOST "ftpHost"
+#define VAR_GATED_PKG "gated_pkg"
+#define VAR_GATEWAY "defaultrouter"
+#define VAR_GEOMETRY "geometry"
+#define VAR_HOSTNAME "hostname"
+#define VAR_IFCONFIG "ifconfig_"
+#define VAR_INTERFACES "network_interfaces"
+#define VAR_INSTALL_CFG "installConfig"
+#define VAR_INSTALL_ROOT "installRoot"
+#define VAR_IPADDR "ipaddr"
+#define VAR_KEYMAP "keymap"
+#define VAR_LABEL "label"
+#define VAR_LABEL_COUNT "labelCount"
+#define VAR_MEDIA_TYPE "mediaType"
+#define VAR_MEDIA_TIMEOUT "MEDIA_TIMEOUT"
+#define VAR_NAMESERVER "nameserver"
+#define VAR_NETINTERACTIVE "netInteractive"
+#define VAR_NETMASK "netmask"
+#define VAR_NETWORK_DEVICE "netDev"
+#define VAR_NFS_PATH "nfs"
+#define VAR_NFS_HOST "nfsHost"
+#define VAR_NFS_SECURE "nfsSecure"
+#define VAR_NFS_SERVER "nfs_server_enable"
+#define VAR_NO_CONFIRM "noConfirm"
+#define VAR_NO_ERROR "noError"
+#define VAR_NO_WARN "noWarn"
+#define VAR_NONINTERACTIVE "nonInteractive"
+#define VAR_NOVELL "novell"
+#define VAR_NTPDATE_FLAGS "ntpdate_flags"
+#define VAR_PACKAGE "package"
+#define VAR_PARTITION "partition"
+#define VAR_PCNFSD "pcnfsd"
+#define VAR_PCNFSD_PKG "pcnfsd_pkg"
+#define VAR_PKG_TMPDIR "PKG_TMPDIR"
+#define VAR_PORTS_PATH "ports"
+#define VAR_RELNAME "releaseName"
+#define VAR_ROOT_SIZE "rootSize"
+#define VAR_ROUTER "router"
+#define VAR_ROUTER_ENABLE "router_enable"
+#define VAR_ROUTERFLAGS "routerflags"
+#define VAR_SERIAL_SPEED "serialSpeed"
+#define VAR_SLOW_ETHER "slowEthernetCard"
+#define VAR_SWAP_SIZE "swapSize"
+#define VAR_TAPE_BLOCKSIZE "tapeBlocksize"
+#define VAR_UFS_PATH "ufs"
+#define VAR_USR_SIZE "usrSize"
+#define VAR_VAR_SIZE "varSize"
+#define VAR_XF86_CONFIG "xf86config"
+
+#define DEFAULT_TAPE_BLOCKSIZE "20"
+
+/* One MB worth of blocks */
+#define ONE_MEG 2048
+
+/* Which selection attributes to use */
+#define ATTR_SELECTED (ColorDisplay ? item_selected_attr : item_attr)
+#define ATTR_TITLE button_active_attr
+
+/* Handy strncpy() macro */
+#define SAFE_STRCPY(to, from) sstrncpy((to), (from), sizeof (to) - 1)
+
+/*** Types ***/
+typedef unsigned int Boolean;
+typedef struct disk Disk;
+typedef struct chunk Chunk;
+
+/* Bitfields for menu options */
+#define DMENU_NORMAL_TYPE 0x1 /* Normal dialog menu */
+#define DMENU_RADIO_TYPE 0x2 /* Radio dialog menu */
+#define DMENU_CHECKLIST_TYPE 0x4 /* Multiple choice menu */
+#define DMENU_SELECTION_RETURNS 0x8 /* Immediate return on item selection */
+
+typedef struct _dmenu {
+ int type; /* What sort of menu we are */
+ char *title; /* Our title */
+ char *prompt; /* Our prompt */
+ char *helpline; /* Line of help at bottom */
+ char *helpfile; /* Help file for "F1" */
+ dialogMenuItem items[0]; /* Array of menu items */
+} DMenu;
+
+/* An rc.conf variable */
+typedef struct _variable {
+ struct _variable *next;
+ char *name;
+ char *value;
+} 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;
+
+/* For attribs */
+#define MAX_ATTRIBS 200
+#define MAX_NAME 64
+#define MAX_VALUE 256
+
+typedef struct _attribs {
+ char name[MAX_NAME];
+ char value[MAX_VALUE];
+} Attribs;
+
+typedef enum {
+ DEVICE_TYPE_NONE,
+ DEVICE_TYPE_DISK,
+ DEVICE_TYPE_FLOPPY,
+ DEVICE_TYPE_FTP,
+ DEVICE_TYPE_NETWORK,
+ DEVICE_TYPE_CDROM,
+ DEVICE_TYPE_TAPE,
+ DEVICE_TYPE_DOS,
+ DEVICE_TYPE_UFS,
+ DEVICE_TYPE_NFS,
+ DEVICE_TYPE_ANY,
+} DeviceType;
+
+/* CDROM mount codes */
+#define CD_UNMOUNTED 0
+#define CD_ALREADY_MOUNTED 1
+#define CD_WE_MOUNTED_IT 2
+
+/* A "device" from sysinstall's point of view */
+typedef struct _device {
+ char name[DEV_NAME_MAX];
+ char *description;
+ char *devname;
+ DeviceType type;
+ Boolean enabled;
+ Boolean (*init)(struct _device *dev);
+ FILE * (*get)(struct _device *dev, char *file, Boolean probe);
+ void (*shutdown)(struct _device *dev);
+ void *private;
+ unsigned int flags;
+} Device;
+
+/* Some internal representations of partitions */
+typedef enum {
+ PART_NONE,
+ PART_SLICE,
+ PART_SWAP,
+ PART_FILESYSTEM,
+ PART_FAT,
+} PartType;
+
+/* The longest newfs command we'll hand to system() */
+#define NEWFS_CMD_MAX 256
+
+typedef struct _part_info {
+ Boolean newfs;
+ char mountpoint[FILENAME_MAX];
+ char newfs_cmd[NEWFS_CMD_MAX];
+} PartInfo;
+
+/* An option */
+typedef struct _opt {
+ char *name;
+ char *desc;
+ enum { OPT_IS_STRING, OPT_IS_INT, OPT_IS_FUNC, OPT_IS_VAR } type;
+ void *data;
+ void *aux;
+ char *(*check)();
+} Option;
+
+/* Weird index nodey things we use for keeping track of package information */
+typedef enum { PACKAGE, PLACE } node_type; /* Types of nodes */
+
+typedef struct _pkgnode { /* A node in the reconstructed hierarchy */
+ struct _pkgnode *next; /* My next sibling */
+ node_type type; /* What am I? */
+ char *name; /* My name */
+ char *desc; /* My description (Hook) */
+ struct _pkgnode *kids; /* My little children */
+ void *data; /* A place to hang my data */
+} PkgNode;
+typedef PkgNode *PkgNodePtr;
+
+/* A single package */
+typedef struct _indexEntry { /* A single entry in an INDEX file */
+ char *name; /* name */
+ char *path; /* full path to port */
+ char *prefix; /* port prefix */
+ char *comment; /* one line description */
+ char *descrfile; /* path to description file */
+ char *deps; /* packages this depends on */
+ char *maintainer; /* maintainer */
+} IndexEntry;
+typedef IndexEntry *IndexEntryPtr;
+
+typedef int (*commandFunc)(char *key, void *data);
+
+#define HOSTNAME_FIELD_LEN 128
+#define IPADDR_FIELD_LEN 16
+#define EXTRAS_FIELD_LEN 128
+
+/* This is the structure that Network devices carry around in their private, erm, structures */
+typedef struct _devPriv {
+ char ipaddr[IPADDR_FIELD_LEN];
+ char netmask[IPADDR_FIELD_LEN];
+ char extras[EXTRAS_FIELD_LEN];
+} DevInfo;
+
+
+/*** Externs ***/
+extern jmp_buf BailOut; /* Used to get the heck out */
+extern int DebugFD; /* Where diagnostic output goes */
+extern Boolean Fake; /* Don't actually modify anything - testing */
+extern Boolean SystemWasInstalled; /* Did we install it? */
+extern Boolean RunningAsInit; /* Are we running stand-alone? */
+extern Boolean DialogActive; /* Is the dialog() stuff up? */
+extern Boolean ColorDisplay; /* Are we on a color display? */
+extern Boolean OnVTY; /* On a syscons VTY? */
+extern Variable *VarHead; /* The head of the variable chain */
+extern Device *mediaDevice; /* Where we're getting our distribution from */
+extern unsigned int Dists; /* Which distributions we want */
+extern unsigned int DESDists; /* Which naughty distributions we want */
+extern unsigned int SrcDists; /* Which src distributions we want */
+extern unsigned int XF86Dists; /* Which XFree86 dists we want */
+extern unsigned int XF86ServerDists; /* The XFree86 servers we want */
+extern unsigned int XF86FontDists; /* The XFree86 fonts we want */
+extern int BootMgr; /* Which boot manager to use */
+extern int StatusLine; /* Where to print our status messages */
+extern DMenu MenuInitial; /* Initial installation menu */
+extern DMenu MenuFixit; /* Fixit repair menu */
+extern DMenu MenuMBRType; /* Type of MBR to write on the disk */
+extern DMenu MenuConfigure; /* Final configuration menu */
+extern DMenu MenuDocumentation; /* Documentation menu */
+extern DMenu MenuFTPOptions; /* FTP Installation options */
+extern DMenu MenuIndex; /* Index menu */
+extern DMenu MenuOptions; /* Installation options */
+extern DMenu MenuOptionsLanguage; /* Language options menu */
+extern DMenu MenuMedia; /* Media type menu */
+extern DMenu MenuMouse; /* Mouse type menu */
+extern DMenu MenuMediaCDROM; /* CDROM media menu */
+extern DMenu MenuMediaDOS; /* DOS media menu */
+extern DMenu MenuMediaFloppy; /* Floppy media menu */
+extern DMenu MenuMediaFTP; /* FTP media menu */
+extern DMenu MenuMediaTape; /* Tape media menu */
+extern DMenu MenuNetworkDevice; /* Network device menu */
+extern DMenu MenuNTP; /* NTP time server menu */
+extern DMenu MenuStartup; /* Startup services menu */
+extern DMenu MenuSyscons; /* System console configuration menu */
+extern DMenu MenuSysconsFont; /* System console font configuration menu */
+extern DMenu MenuSysconsKeymap; /* System console keymap configuration menu */
+extern DMenu MenuSysconsKeyrate; /* System console keyrate configuration menu */
+extern DMenu MenuSysconsSaver; /* System console saver configuration menu */
+extern DMenu MenuSysconsScrnmap; /* System console screenmap configuration menu */
+extern DMenu MenuNetworking; /* Network configuration menu */
+extern DMenu MenuInstallCustom; /* Custom Installation menu */
+extern DMenu MenuDistributions; /* Distribution menu */
+extern DMenu MenuSubDistributions; /* Custom distribution menu */
+extern DMenu MenuDESDistributions; /* DES distribution menu */
+extern DMenu MenuSrcDistributions; /* Source distribution menu */
+extern DMenu MenuXF86; /* XFree86 main menu */
+extern DMenu MenuXF86Select; /* XFree86 distribution selection menu */
+extern DMenu MenuXF86SelectCore; /* XFree86 core distribution menu */
+extern DMenu MenuXF86SelectServer; /* XFree86 server distribution menu */
+extern DMenu MenuXF86SelectPC98Server; /* XFree86 server distribution menu */
+extern DMenu MenuXF86SelectFonts; /* XFree86 font selection menu */
+extern DMenu MenuDiskDevices; /* Disk devices menu */
+extern DMenu MenuHTMLDoc; /* HTML Documentation menu */
+extern DMenu MenuUsermgmt; /* User management menu */
+extern DMenu MenuFixit; /* Fixit floppy/CDROM/shell menu */
+extern DMenu MenuXF86Config; /* Select XFree86 configuration type */
+
+/* Stuff from libdialog which isn't properly declared outside */
+extern void display_helpfile(void);
+extern void display_helpline(WINDOW *w, int y, int width);
+
+/*** Prototypes ***/
+
+/* anonFTP.c */
+extern int configAnonFTP(dialogMenuItem *self);
+
+/* attrs.c */
+extern char *attr_match(Attribs *attr, char *name);
+extern int attr_parse_file(Attribs *attr, char *file);
+extern int attr_parse(Attribs *attr, FILE *fp);
+
+/* cdrom.c */
+extern Boolean mediaInitCDROM(Device *dev);
+extern FILE *mediaGetCDROM(Device *dev, char *file, Boolean probe);
+extern void mediaShutdownCDROM(Device *dev);
+
+/* command.c */
+extern void command_clear(void);
+extern void command_sort(void);
+extern void command_execute(void);
+extern void command_shell_add(char *key, char *fmt, ...);
+extern void command_func_add(char *key, commandFunc func, void *data);
+
+/* config.c */
+extern int configFstab(void);
+extern void configEnvironmentRC_conf(char *config);
+extern void configEnvironmentResolv(char *config);
+extern void configRC_conf(char *config);
+extern int configRC(dialogMenuItem *self);
+extern int configRegister(dialogMenuItem *self);
+extern void configResolv(void);
+extern int configPackages(dialogMenuItem *self);
+extern int configSaver(dialogMenuItem *self);
+extern int configSaverTimeout(dialogMenuItem *self);
+extern int configNTP(dialogMenuItem *self);
+extern int configUsers(dialogMenuItem *self);
+extern int configXEnvironment(dialogMenuItem *self);
+extern int configRouter(dialogMenuItem *self);
+extern int configPCNFSD(dialogMenuItem *self);
+extern int configNFSServer(dialogMenuItem *self);
+extern int configWriteRC_conf(dialogMenuItem *self);
+#ifdef NETCON_EXTENTIONS
+extern int configNovell(dialogMenuItem *self);
+#endif
+
+/* 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 Device **deviceFind(char *name, DeviceType type);
+extern Device **deviceFindDescr(char *name, char *desc, DeviceType class);
+extern int deviceCount(Device **devs);
+extern Device *new_device(char *name);
+extern Device *deviceRegister(char *name, char *desc, char *devname, DeviceType type, Boolean enabled,
+ Boolean (*init)(Device *mediadev),
+ FILE * (*get)(Device *dev, char *file, Boolean probe),
+ void (*shutDown)(Device *mediadev),
+ void *private);
+extern Boolean dummyInit(Device *dev);
+extern FILE *dummyGet(Device *dev, char *dist, Boolean probe);
+extern void dummyShutdown(Device *dev);
+
+/* disks.c */
+extern int diskPartitionEditor(dialogMenuItem *self);
+extern int diskPartitionWrite(dialogMenuItem *self);
+extern int diskGetSelectCount(Device ***devs);
+extern void diskPartition(Device *dev);
+
+/* dispatch.c */
+extern int dispatchCommand(char *command);
+extern int dispatch_load_floppy(dialogMenuItem *self);
+extern int dispatch_load_file_int(int);
+extern int dispatch_load_file(dialogMenuItem *self);
+
+
+/* dist.c */
+extern int distReset(dialogMenuItem *self);
+extern int distConfig(dialogMenuItem *self);
+extern int distSetCustom(dialogMenuItem *self);
+extern int distSetDeveloper(dialogMenuItem *self);
+extern int distSetXDeveloper(dialogMenuItem *self);
+extern int distSetKernDeveloper(dialogMenuItem *self);
+extern int distSetUser(dialogMenuItem *self);
+extern int distSetXUser(dialogMenuItem *self);
+extern int distSetMinimum(dialogMenuItem *self);
+extern int distSetEverything(dialogMenuItem *self);
+extern int distSetDES(dialogMenuItem *self);
+extern int distSetSrc(dialogMenuItem *self);
+extern int distSetXF86(dialogMenuItem *self);
+extern int distExtractAll(dialogMenuItem *self);
+
+/* dmenu.c */
+extern int dmenuDisplayFile(dialogMenuItem *tmp);
+extern int dmenuSubmenu(dialogMenuItem *tmp);
+extern int dmenuSystemCommand(dialogMenuItem *tmp);
+extern int dmenuSystemCommandBox(dialogMenuItem *tmp);
+extern int dmenuExit(dialogMenuItem *tmp);
+extern int dmenuISetVariable(dialogMenuItem *tmp);
+extern int dmenuSetVariable(dialogMenuItem *tmp);
+extern int dmenuSetKmapVariable(dialogMenuItem *tmp);
+extern int dmenuSetVariables(dialogMenuItem *tmp);
+extern int dmenuToggleVariable(dialogMenuItem *tmp);
+extern int dmenuSetFlag(dialogMenuItem *tmp);
+extern int dmenuSetValue(dialogMenuItem *tmp);
+extern Boolean dmenuOpen(DMenu *menu, int *choice, int *scroll, int *curr, int *max, Boolean buttons);
+extern Boolean dmenuOpenSimple(DMenu *menu, Boolean buttons);
+extern int dmenuVarCheck(dialogMenuItem *item);
+extern int dmenuVarsCheck(dialogMenuItem *item);
+extern int dmenuFlagCheck(dialogMenuItem *item);
+extern int dmenuRadioCheck(dialogMenuItem *item);
+
+/* doc.c */
+extern int docBrowser(dialogMenuItem *self);
+extern int docShowDocument(dialogMenuItem *self);
+
+/* dos.c */
+extern Boolean mediaCloseDOS(Device *dev, FILE *fp);
+extern Boolean mediaInitDOS(Device *dev);
+extern FILE *mediaGetDOS(Device *dev, char *file, Boolean probe);
+extern void mediaShutdownDOS(Device *dev);
+
+/* floppy.c */
+extern int getRootFloppy(void);
+extern Boolean mediaInitFloppy(Device *dev);
+extern FILE *mediaGetFloppy(Device *dev, char *file, Boolean probe);
+extern void mediaShutdownFloppy(Device *dev);
+
+/* ftp_strat.c */
+extern Boolean mediaCloseFTP(Device *dev, FILE *fp);
+extern Boolean mediaInitFTP(Device *dev);
+extern FILE *mediaGetFTP(Device *dev, char *file, Boolean probe);
+extern void mediaShutdownFTP(Device *dev);
+
+/* globals.c */
+extern void globalsInit(void);
+
+/* index.c */
+int index_read(FILE *fp, PkgNodePtr papa);
+int index_menu(PkgNodePtr top, PkgNodePtr plist, int *pos, int *scroll);
+void index_init(PkgNodePtr top, PkgNodePtr plist);
+void index_node_free(PkgNodePtr top, PkgNodePtr plist);
+void index_sort(PkgNodePtr top);
+void index_print(PkgNodePtr top, int level);
+int index_extract(Device *dev, PkgNodePtr top, PkgNodePtr plist);
+
+/* install.c */
+extern Boolean checkLabels(Boolean whinge, Chunk **rdev, Chunk **sdev, Chunk **udev, Chunk **vdev);
+extern int installCommit(dialogMenuItem *self);
+extern int installCustomCommit(dialogMenuItem *self);
+extern int installExpress(dialogMenuItem *self);
+extern int installNovice(dialogMenuItem *self);
+extern int installFixitHoloShell(dialogMenuItem *self);
+extern int installFixitCDROM(dialogMenuItem *self);
+extern int installFixitFloppy(dialogMenuItem *self);
+extern int installFixup(dialogMenuItem *self);
+extern int installUpgrade(dialogMenuItem *self);
+extern int installFilesystems(dialogMenuItem *self);
+extern int installVarDefaults(dialogMenuItem *self);
+extern void installEnvironment(void);
+extern Boolean copySelf(void);
+
+/* keymap.c */
+extern int loadKeymap(const char *lang);
+
+/* label.c */
+extern int diskLabelEditor(dialogMenuItem *self);
+extern int diskLabelCommit(dialogMenuItem *self);
+
+/* lndir.c */
+extern int lndir(char *from, char *to);
+
+/* makedevs.c (auto-generated) */
+extern const char termcap_ansi[];
+extern const char termcap_vt100[];
+extern const char termcap_cons25[];
+extern const char termcap_cons25_m[];
+extern const char termcap_cons25r[];
+extern const char termcap_cons25r_m[];
+extern const char termcap_cons25l1[];
+extern const char termcap_cons25l1_m[];
+extern const u_char font_iso_8x16[];
+extern const u_char font_cp850_8x16[];
+extern const u_char font_cp866_8x16[];
+extern const u_char koi8_r2cp866[];
+extern u_char default_scrnmap[];
+
+/* media.c */
+extern char *cpioVerbosity(void);
+extern void mediaClose(void);
+extern int mediaTimeout(void);
+extern int mediaSetCDROM(dialogMenuItem *self);
+extern int mediaSetFloppy(dialogMenuItem *self);
+extern int mediaSetDOS(dialogMenuItem *self);
+extern int mediaSetTape(dialogMenuItem *self);
+extern int mediaSetFTP(dialogMenuItem *self);
+extern int mediaSetFTPActive(dialogMenuItem *self);
+extern int mediaSetFTPPassive(dialogMenuItem *self);
+extern int mediaSetUFS(dialogMenuItem *self);
+extern int mediaSetNFS(dialogMenuItem *self);
+extern int mediaSetFTPUserPass(dialogMenuItem *self);
+extern int mediaSetCPIOVerbosity(dialogMenuItem *self);
+extern int mediaGetType(dialogMenuItem *self);
+extern Boolean mediaExtractDist(char *dir, char *dist, FILE *fp);
+extern Boolean mediaExtractDistBegin(char *dir, int *fd, int *zpid, int *cpic);
+extern Boolean mediaExtractDistEnd(int zpid, int cpid);
+extern Boolean mediaVerify(void);
+
+/* 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);
+
+/* msg.c */
+extern Boolean isDebug(void);
+extern void msgInfo(char *fmt, ...);
+extern void msgYap(char *fmt, ...);
+extern void msgWarn(char *fmt, ...);
+extern void msgDebug(char *fmt, ...);
+extern void msgError(char *fmt, ...);
+extern void msgFatal(char *fmt, ...);
+extern void msgConfirm(char *fmt, ...);
+extern void msgNotify(char *fmt, ...);
+extern void msgWeHaveOutput(char *fmt, ...);
+extern int msgYesNo(char *fmt, ...);
+extern char *msgGetInput(char *buf, char *fmt, ...);
+extern int msgSimpleConfirm(char *);
+extern int msgSimpleNotify(char *);
+
+/* network.c */
+extern Boolean mediaInitNetwork(Device *dev);
+extern void mediaShutdownNetwork(Device *dev);
+
+/* nfs.c */
+extern Boolean mediaInitNFS(Device *dev);
+extern FILE *mediaGetNFS(Device *dev, char *file, Boolean probe);
+extern void mediaShutdownNFS(Device *dev);
+
+/* options.c */
+extern int optionsEditor(dialogMenuItem *self);
+
+/* package.c */
+extern int packageAdd(dialogMenuItem *self);
+extern int package_add(char *name);
+extern int package_extract(Device *dev, char *name, Boolean depended);
+extern Boolean package_exists(char *name);
+
+/* register.c */
+extern int registerOpenDialog(void);
+
+/* system.c */
+extern void systemInitialize(int argc, char **argv);
+extern void systemShutdown(int status);
+extern int execExecute(char *cmd, char *name);
+extern int systemExecute(char *cmd);
+extern int systemDisplayHelp(char *file);
+extern char *systemHelpFile(char *file, char *buf);
+extern void systemChangeFont(const u_char font[]);
+extern void systemChangeLang(char *lang);
+extern void systemChangeTerminal(char *color, const u_char c_termcap[], char *mono, const u_char m_termcap[]);
+extern void systemChangeScreenmap(const u_char newmap[]);
+extern void systemCreateHoloshell(void);
+extern int vsystem(char *fmt, ...);
+
+/* tape.c */
+extern char *mediaTapeBlocksize(void);
+extern Boolean mediaInitTape(Device *dev);
+extern FILE *mediaGetTape(Device *dev, char *file, Boolean probe);
+extern void mediaShutdownTape(Device *dev);
+
+/* tcpip.c */
+extern int tcpOpenDialog(Device *dev);
+extern int tcpMenuSelect(dialogMenuItem *self);
+extern Device *tcpDeviceSelect(void);
+
+/* termcap.c */
+extern int set_termcap(void);
+
+/* ufs.c */
+extern void mediaShutdownUFS(Device *dev);
+extern Boolean mediaInitUFS(Device *dev);
+extern FILE *mediaGetUFS(Device *dev, char *file, Boolean probe);
+
+/* user.c */
+extern int userAddGroup(dialogMenuItem *self);
+extern int userAddUser(dialogMenuItem *self);
+
+/* variable.c */
+extern void variable_set(char *var);
+extern void variable_set2(char *name, char *value);
+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);
+extern int variable_check(char *data);
+
+/* wizard.c */
+extern void slice_wizard(Disk *d);
+
+#endif
+/* _SYSINSTALL_H_INCLUDE */
diff --git a/usr.sbin/sysinstall/system.c b/usr.sbin/sysinstall/system.c
new file mode 100644
index 0000000..61a27c4e
--- /dev/null
+++ b/usr.sbin/sysinstall/system.c
@@ -0,0 +1,376 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last program in the `sysinstall' line - the next
+ * generation being essentially a complete rewrite.
+ *
+ * $Id: system.c,v 1.81 1997/05/27 18:56:03 jkh Exp $
+ *
+ * Jordan Hubbard
+ *
+ * My contributions are in the public domain.
+ *
+ * Parts of this file are also blatently stolen from Poul-Henning Kamp's
+ * previous version of sysinstall, and as such fall under his "BEERWARE license"
+ * so buy him a beer if you like it! Buy him a beer for me, too!
+ * Heck, get him completely drunk and send me pictures! :-)
+ */
+
+#include "sysinstall.h"
+#include <signal.h>
+#include <sys/reboot.h>
+#include <machine/console.h>
+#include <sys/fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/wait.h>
+
+
+/* Where we stick our temporary expanded doc file */
+#define DOC_TMP_DIR "/tmp"
+#define DOC_TMP_FILE "/tmp/doc.tmp"
+
+static pid_t ehs_pid;
+
+/*
+ * Handle interrupt signals - this probably won't work in all cases
+ * due to our having bogotified the internal state of dialog or curses,
+ * but we'll give it a try.
+ */
+static void
+handle_intr(int sig)
+{
+ WINDOW *save = savescr();
+
+ if (!msgYesNo("Are you sure you want to abort the installation?"))
+ systemShutdown(-1);
+ else
+ restorescr(save);
+}
+
+/* Expand a file into a convenient location, nuking it each time */
+static char *
+expand(char *fname)
+{
+ Mkdir(DOC_TMP_DIR);
+ unlink(DOC_TMP_FILE);
+ if (!file_readable(fname) || vsystem("gzip -c -d %s > %s", fname, DOC_TMP_FILE))
+ return NULL;
+ return DOC_TMP_FILE;
+}
+
+/* Initialize system defaults */
+void
+systemInitialize(int argc, char **argv)
+{
+ int i;
+
+ signal(SIGINT, SIG_IGN);
+ globalsInit();
+
+ /* Are we running as init? */
+ if (getpid() == 1) {
+ int fd, type;
+
+ RunningAsInit = 1;
+ setsid();
+ close(0);
+ fd = open("/dev/ttyv0", O_RDWR);
+ if (fd == -1)
+ fd = open("/dev/console", O_RDWR); /* fallback */
+ else
+ OnVTY = TRUE;
+ /*
+ * To make _sure_ we're on a VTY and don't have /dev/console switched
+ * away to a serial port or something, attempt to set the cursor appearance.
+ */
+ type = 0; /* normal */
+ if (OnVTY) {
+ int fd2;
+
+ if ((fd2 = open("/dev/console", O_RDWR)) != -1) {
+ if (ioctl(fd2, CONS_CURSORTYPE, &type) == -1) {
+ OnVTY = FALSE;
+ close(fd); close(fd2);
+ open("/dev/console", O_RDWR);
+ }
+ else
+ close(fd2);
+ }
+ }
+ close(1); dup(0);
+ close(2); dup(0);
+ printf("%s running as init on %s\n", argv[0], OnVTY ? "vty0" : "serial console");
+ i = ioctl(0, TIOCSCTTY, (char *)NULL);
+ setlogin("root");
+ setenv("PATH", "/stand:/bin:/sbin:/usr/sbin:/usr/bin:/mnt/bin:/mnt/sbin:/mnt/usr/sbin:/mnt/usr/bin:/usr/X11R6/bin", 1);
+ setbuf(stdin, 0);
+ setbuf(stderr, 0);
+ }
+ else {
+ char hname[256];
+
+ /* Initalize various things for a multi-user environment */
+ if (!gethostname(hname, sizeof hname))
+ variable_set2(VAR_HOSTNAME, hname);
+ }
+
+ 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);
+}
+
+/* Close down and prepare to exit */
+void
+systemShutdown(int status)
+{
+ /* If some media is open, close it down */
+ if (status >=0 && mediaDevice)
+ mediaDevice->shutdown(mediaDevice);
+
+ /* write out any changes to rc.conf .. */
+ configRC_conf("/etc/rc.conf");
+
+ /* Shut down the dialog library */
+ if (DialogActive) {
+ end_dialog();
+ DialogActive = FALSE;
+ }
+
+ /* Shut down curses */
+ endwin();
+
+ /* If we have a temporary doc file lying around, nuke it */
+ unlink(DOC_TMP_FILE);
+
+ /* REALLY exit! */
+ if (RunningAsInit) {
+ /* Put the console back */
+ ioctl(0, VT_ACTIVATE, 2);
+ reboot(0);
+ }
+ else
+ exit(status);
+}
+
+/* Run some general command */
+int
+systemExecute(char *command)
+{
+ int status;
+ struct termios foo;
+
+ dialog_update();
+ end_dialog();
+ DialogActive = FALSE;
+ if (tcgetattr(0, &foo) != -1) {
+ foo.c_cc[VERASE] = '\010';
+ tcsetattr(0, TCSANOW, &foo);
+ }
+ if (!Fake)
+ status = system(command);
+ else {
+ status = 0;
+ msgDebug("systemExecute: Faked execution of `%s'\n", command);
+ }
+ DialogActive = TRUE;
+ return status;
+}
+
+/* Display a help file in a filebox */
+int
+systemDisplayHelp(char *file)
+{
+ char *fname = NULL;
+ char buf[FILENAME_MAX];
+ int ret = 0;
+
+ fname = systemHelpFile(file, buf);
+ if (!fname) {
+ snprintf(buf, FILENAME_MAX, "The %s file is not provided on this particular floppy image.", file);
+ use_helpfile(NULL);
+ use_helpline(NULL);
+ dialog_mesgbox("Sorry!", buf, -1, -1);
+ ret = 1;
+ }
+ else {
+ use_helpfile(NULL);
+ use_helpline(NULL);
+ dialog_textbox(file, fname, LINES, COLS);
+ }
+ return ret;
+}
+
+char *
+systemHelpFile(char *file, char *buf)
+{
+ if (!file)
+ return NULL;
+
+ snprintf(buf, FILENAME_MAX, "/stand/help/%s.hlp.gz", file);
+ if (file_readable(buf))
+ return expand(buf);
+ snprintf(buf, FILENAME_MAX, "/usr/src/release/sysinstall/help/%s.hlp", file);
+ if (file_readable(buf))
+ return buf;
+ return NULL;
+}
+
+void
+systemChangeTerminal(char *color, const u_char c_term[],
+ char *mono, const u_char m_term[])
+{
+ extern void init_acs(void);
+
+ if (OnVTY) {
+ if (ColorDisplay) {
+ setenv("TERM", color, 1);
+ setenv("TERMCAP", c_term, 1);
+ reset_shell_mode();
+ setterm(color);
+ init_acs();
+ cbreak(); noecho();
+ }
+ else {
+ setenv("TERM", mono, 1);
+ setenv("TERMCAP", m_term, 1);
+ reset_shell_mode();
+ setterm(mono);
+ init_acs();
+ cbreak(); noecho();
+ }
+ }
+ clear();
+ refresh();
+ dialog_clear();
+}
+
+int
+vsystem(char *fmt, ...)
+{
+ va_list args;
+ int pstat;
+ pid_t pid;
+ int omask;
+ sig_t intsave, quitsave;
+ char *cmd;
+ int i;
+
+ cmd = (char *)alloca(FILENAME_MAX);
+ cmd[0] = '\0';
+ va_start(args, fmt);
+ vsnprintf(cmd, FILENAME_MAX, fmt, args);
+ va_end(args);
+
+ omask = sigblock(sigmask(SIGCHLD));
+ if (Fake) {
+ msgDebug("vsystem: Faked execution of `%s'\n", cmd);
+ return 0;
+ }
+ if (isDebug())
+ msgDebug("Executing command `%s'\n", cmd);
+ pid = fork();
+ if (pid == -1) {
+ (void)sigsetmask(omask);
+ i = 127;
+ }
+ else if (!pid) { /* Junior */
+ (void)sigsetmask(omask);
+ if (DebugFD != -1) {
+ dup2(DebugFD, 0);
+ dup2(DebugFD, 1);
+ dup2(DebugFD, 2);
+ }
+ else {
+ close(1); open("/dev/null", O_WRONLY);
+ dup2(1, 2);
+ }
+ if (!RunningAsInit)
+ execl("/bin/sh", "/bin/sh", "-c", cmd, (char *)NULL);
+ else
+ execl("/stand/sh", "/stand/sh", "-c", cmd, (char *)NULL);
+ exit(1);
+ }
+ else {
+ intsave = signal(SIGINT, SIG_IGN);
+ quitsave = signal(SIGQUIT, SIG_IGN);
+ pid = waitpid(pid, &pstat, 0);
+ (void)sigsetmask(omask);
+ (void)signal(SIGINT, intsave);
+ (void)signal(SIGQUIT, quitsave);
+ i = (pid == -1) ? -1 : WEXITSTATUS(pstat);
+ if (isDebug())
+ msgDebug("Command `%s' returns status of %d\n", cmd, i);
+ }
+ return i;
+}
+
+void
+systemCreateHoloshell(void)
+{
+ if (OnVTY && RunningAsInit) {
+
+ if (ehs_pid != 0) {
+ int pstat;
+
+ if (kill(ehs_pid, 0) == 0) {
+
+ if (msgYesNo("There seems to be an emergency holographic shell\n"
+ "already running von VTY 4.\n"
+ "Kill it and start a new one?"))
+ return;
+
+ /* try cleaning up as much as possible */
+ (void) kill(ehs_pid, SIGHUP);
+ sleep(1);
+ (void) kill(ehs_pid, SIGKILL);
+ }
+
+ /* avoid too many zombies */
+ (void) waitpid(ehs_pid, &pstat, WNOHANG);
+ }
+
+ if ((ehs_pid = fork()) == 0) {
+ int i, fd;
+ struct termios foo;
+ extern int login_tty(int);
+
+ ioctl(0, TIOCNOTTY, NULL);
+ for (i = getdtablesize(); i >= 0; --i)
+ close(i);
+ fd = open("/dev/ttyv3", O_RDWR);
+ ioctl(0, TIOCSCTTY, &fd);
+ dup2(0, 1);
+ dup2(0, 2);
+ DebugFD = 2;
+ if (login_tty(fd) == -1)
+ msgDebug("Doctor: I can't set the controlling terminal.\n");
+ signal(SIGTTOU, SIG_IGN);
+ if (tcgetattr(fd, &foo) != -1) {
+ foo.c_cc[VERASE] = '\010';
+ if (tcsetattr(fd, TCSANOW, &foo) == -1)
+ msgDebug("Doctor: I'm unable to set the erase character.\n");
+ }
+ else
+ msgDebug("Doctor: I'm unable to get the terminal attributes!\n");
+ execlp("sh", "-sh", 0);
+ msgDebug("Was unable to execute sh for Holographic shell!\n");
+ exit(1);
+ }
+ else {
+ msgNotify("Starting an emergency holographic shell on VTY4");
+ sleep(2);
+ }
+ }
+}
diff --git a/usr.sbin/sysinstall/tape.c b/usr.sbin/sysinstall/tape.c
new file mode 100644
index 0000000..df39892
--- /dev/null
+++ b/usr.sbin/sysinstall/tape.c
@@ -0,0 +1,120 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last attempt in the `sysinstall' line, the next
+ * generation being slated to essentially a complete rewrite.
+ *
+ * $Id$
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+/* These routines deal with getting things off of tape media */
+
+#include "sysinstall.h"
+#include <sys/fcntl.h>
+#include <sys/param.h>
+
+static Boolean tapeInitted;
+
+char *
+mediaTapeBlocksize(void)
+{
+ char *cp = variable_get(VAR_TAPE_BLOCKSIZE);
+
+ return cp ? cp : DEFAULT_TAPE_BLOCKSIZE;
+}
+
+Boolean
+mediaInitTape(Device *dev)
+{
+ /* This is REALLY gross, but we need to do the init later in get due to the fact
+ * that media is initialized BEFORE a filesystem is mounted now.
+ */
+ return TRUE;
+}
+
+FILE *
+mediaGetTape(Device *dev, char *file, Boolean probe)
+{
+ char buf[PATH_MAX];
+ FILE *fp;
+
+ int i;
+
+ if (!tapeInitted) {
+ msgDebug("Tape init routine called for %s (private dir is %s)\n", dev->name, dev->private);
+ Mkdir(dev->private);
+ if (chdir(dev->private)) {
+ msgConfirm("Unable to CD to %s before extracting tape!\n"
+ "Tape media is not selected and thus cannot be installed from.", dev->private);
+ return (FILE *)IO_ERROR;
+ }
+ /* We know the tape is already in the drive, so go for it */
+ msgNotify("First extracting distributions from %s...", dev->description);
+ if (!strcmp(dev->name, "rft0"))
+ i = vsystem("ft | cpio -idum %s --block-size %s", cpioVerbosity(), mediaTapeBlocksize());
+ else
+ i = vsystem("cpio -idum %s --block-size %s -I %s", cpioVerbosity(), mediaTapeBlocksize(), dev->devname);
+ if (!i) {
+ tapeInitted = TRUE;
+ msgDebug("Tape initialized successfully.\n");
+ }
+ else {
+ msgConfirm("Tape extract command failed with status %d!\n"
+ "Unable to use tape media.", i);
+ return (FILE *)IO_ERROR;
+ }
+ }
+
+ sprintf(buf, "%s/%s", (char *)dev->private, file);
+ if (isDebug())
+ msgDebug("Request for %s from tape (looking in %s)\n", file, buf);
+ if (file_readable(buf))
+ fp = fopen(buf, "r");
+ else {
+ sprintf(buf, "%s/dists/%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(dev->private)) {
+ msgNotify("Cleaning up results of tape extract in %s..", 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..a78bc85
--- /dev/null
+++ b/usr.sbin/sysinstall/tcpip.c
@@ -0,0 +1,397 @@
+/*
+ * $Id: tcpip.c,v 1.71 1997/09/17 13:33:14 jkh Exp $
+ *
+ * Copyright (c) 1995
+ * Gary J Palmer. All rights reserved.
+ * Copyright (c) 1996
+ * Jordan K. Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+/*
+ * All kinds of hacking also performed by jkh on this code. Don't
+ * blame Gary for every bogosity you see here.. :-)
+ *
+ * -jkh
+ */
+
+#include "sysinstall.h"
+#include <sys/param.h>
+
+/* The help file for the TCP/IP setup screen */
+#define TCP_HELPFILE "tcp"
+
+/* These are nasty, but they make the layout structure a lot easier ... */
+
+static char hostname[HOSTNAME_FIELD_LEN], domainname[HOSTNAME_FIELD_LEN],
+ gateway[IPADDR_FIELD_LEN], nameserver[IPADDR_FIELD_LEN];
+static int okbutton, cancelbutton;
+static char ipaddr[IPADDR_FIELD_LEN], netmask[IPADDR_FIELD_LEN], extras[EXTRAS_FIELD_LEN];
+
+/* What the screen size is meant to be */
+#define TCP_DIALOG_Y 0
+#define TCP_DIALOG_X 8
+#define TCP_DIALOG_WIDTH COLS - 16
+#define TCP_DIALOG_HEIGHT LINES - 2
+
+static Layout layout[] = {
+#define LAYOUT_HOSTNAME 0
+ { 1, 2, 25, HOSTNAME_FIELD_LEN - 1,
+ "Host:", "Your fully-qualified hostname, e.g. foo.bar.com",
+ hostname, STRINGOBJ, NULL },
+#define LAYOUT_DOMAINNAME 1
+ { 1, 35, 20, HOSTNAME_FIELD_LEN - 1,
+ "Domain:",
+ "The name of the domain that your machine is in, e.g. bar.com",
+ domainname, STRINGOBJ, NULL },
+#define LAYOUT_GATEWAY 2
+ { 5, 2, 18, IPADDR_FIELD_LEN - 1,
+ "Gateway:",
+ "IP address of host forwarding packets to non-local destinations",
+ gateway, STRINGOBJ, NULL },
+#define LAYOUT_NAMESERVER 3
+ { 5, 35, 18, IPADDR_FIELD_LEN - 1,
+ "Name server:", "IP address of your local DNS server",
+ nameserver, STRINGOBJ, NULL },
+#define LAYOUT_IPADDR 4
+ { 10, 10, 18, IPADDR_FIELD_LEN - 1,
+ "IP Address:",
+ "The IP address to be used for this interface",
+ ipaddr, STRINGOBJ, NULL },
+#define LAYOUT_NETMASK 5
+ { 10, 35, 18, IPADDR_FIELD_LEN - 1,
+ "Netmask:",
+ "The netmask for this interface, e.g. 0xffffff00 for a class C network",
+ netmask, STRINGOBJ, NULL },
+#define LAYOUT_EXTRAS 6
+ { 14, 10, 37, HOSTNAME_FIELD_LEN - 1,
+ "Extra options to ifconfig:",
+ "Any interface-specific options to ifconfig you would like to add",
+ extras, STRINGOBJ, NULL },
+#define LAYOUT_OKBUTTON 7
+ { 19, 15, 0, 0,
+ "OK", "Select this if you are happy with these settings",
+ &okbutton, BUTTONOBJ, NULL },
+#define LAYOUT_CANCELBUTTON 8
+ { 19, 35, 0, 0,
+ "CANCEL", "Select this if you wish to cancel this screen",
+ &cancelbutton, BUTTONOBJ, NULL },
+ { NULL },
+};
+
+#define _validByte(b) ((b) >= 0 && (b) <= 255)
+
+/* whine */
+static void
+feepout(char *msg)
+{
+ beep();
+ msgConfirm(msg);
+}
+
+/* Very basic IP address integrity check - could be drastically improved */
+static int
+verifyIP(char *ip)
+{
+ int a, b, c, d;
+
+ if (ip && sscanf(ip, "%d.%d.%d.%d", &a, &b, &c, &d) == 4 &&
+ _validByte(a) && _validByte(b) && _validByte(c) &&
+ _validByte(d) && (d != 255))
+ return 1;
+ else
+ return 0;
+}
+
+/* Check for the settings on the screen - the per-interface stuff is
+ moved to the main handling code now to do it on the fly - sigh */
+static int
+verifySettings(void)
+{
+ if (!hostname[0])
+ feepout("Must specify a host name of some sort!");
+ else if (gateway[0] && !verifyIP(gateway))
+ feepout("Invalid gateway IP address specified");
+ else if (nameserver[0] && !verifyIP(nameserver))
+ feepout("Invalid name server IP address specified");
+ else if (netmask[0] && (netmask[0] < '0' && netmask[0] > '3'))
+ feepout("Invalid netmask value");
+ else if (ipaddr[0] && !verifyIP(ipaddr))
+ feepout("Invalid IP address");
+ else
+ return 1;
+ return 0;
+}
+
+/* This is it - how to get TCP setup values */
+int
+tcpOpenDialog(Device *devp)
+{
+ WINDOW *ds_win, *save = NULL;
+ ComposeObj *obj = NULL;
+ int n = 0, cancel = FALSE;
+ int max, ret = DITEM_SUCCESS;
+ char *tmp;
+ char title[80];
+
+ /* Initialise vars from previous device values */
+ if (devp->private) {
+ DevInfo *di = (DevInfo *)devp->private;
+
+ SAFE_STRCPY(ipaddr, di->ipaddr);
+ SAFE_STRCPY(netmask, di->netmask);
+ SAFE_STRCPY(extras, di->extras);
+ }
+ else { /* See if there are any defaults */
+ char *cp;
+
+ if (!ipaddr[0]) {
+ if ((cp = variable_get(VAR_IPADDR)) != NULL)
+ SAFE_STRCPY(ipaddr, cp);
+ else if ((cp = variable_get(string_concat3(devp->name, "_", VAR_IPADDR))) != NULL)
+ SAFE_STRCPY(ipaddr, cp);
+ }
+ if (!netmask[0]) {
+ if ((cp = variable_get(VAR_NETMASK)) != NULL)
+ SAFE_STRCPY(netmask, cp);
+ else if ((cp = variable_get(string_concat3(devp->name, "_", VAR_NETMASK))) != NULL)
+ SAFE_STRCPY(netmask, cp);
+ }
+ if (!extras[0]) {
+ if ((cp = variable_get(VAR_EXTRAS)) != NULL)
+ SAFE_STRCPY(extras, cp);
+ else if ((cp = variable_get(string_concat3(devp->name, "_", VAR_EXTRAS))) != NULL)
+ SAFE_STRCPY(extras, cp);
+ }
+ }
+
+ /* Look up values already recorded with the system, or blank the string variables ready to accept some new data */
+ tmp = variable_get(VAR_HOSTNAME);
+ if (tmp)
+ SAFE_STRCPY(hostname, tmp);
+ else
+ bzero(hostname, sizeof(hostname));
+ tmp = variable_get(VAR_DOMAINNAME);
+ if (tmp)
+ SAFE_STRCPY(domainname, tmp);
+ else
+ bzero(domainname, sizeof(domainname));
+ tmp = variable_get(VAR_GATEWAY);
+ if (tmp)
+ SAFE_STRCPY(gateway, tmp);
+ else
+ bzero(gateway, sizeof(gateway));
+ tmp = variable_get(VAR_NAMESERVER);
+ if (tmp)
+ SAFE_STRCPY(nameserver, tmp);
+ else
+ bzero(nameserver, sizeof(nameserver));
+
+ save = savescr();
+ /* If non-interactive, jump straight over the dialog crap and into config section */
+ if (variable_get(VAR_NONINTERACTIVE) &&
+ !variable_get(VAR_NETINTERACTIVE)) {
+ if (!hostname[0])
+ msgConfirm("WARNING: hostname variable not set and is a non-optional\n"
+ "parameter. Please add this to your installation script\n"
+ "or set the netInteractive variable (see sysinstall man page)");
+ else
+ goto netconfig;
+ }
+
+ /* Now do all the screen I/O */
+ dialog_clear_norefresh();
+
+ /* We need a curses window */
+ if (!(ds_win = openLayoutDialog(TCP_HELPFILE, " Network Configuration ",
+ TCP_DIALOG_X, TCP_DIALOG_Y, TCP_DIALOG_WIDTH, TCP_DIALOG_HEIGHT))) {
+ beep();
+ msgConfirm("Cannot open TCP/IP dialog window!!");
+ restorescr(save);
+ return DITEM_FAILURE;
+ }
+
+ /* Draw interface configuration box */
+ draw_box(ds_win, TCP_DIALOG_Y + 9, TCP_DIALOG_X + 8, TCP_DIALOG_HEIGHT - 13, TCP_DIALOG_WIDTH - 17,
+ dialog_attr, border_attr);
+ wattrset(ds_win, dialog_attr);
+ sprintf(title, " Configuration for Interface %s ", devp->name);
+ mvwaddstr(ds_win, TCP_DIALOG_Y + 9, TCP_DIALOG_X + 14, title);
+
+ /* Some more initialisation before we go into the main input loop */
+ obj = initLayoutDialog(ds_win, layout, TCP_DIALOG_X, TCP_DIALOG_Y, &max);
+
+reenter:
+ cancelbutton = okbutton = 0;
+ while (layoutDialogLoop(ds_win, layout, &obj, &n, max, &cancelbutton, &cancel)) {
+ /* Insert a default value for the netmask, 0xffffff00 is
+ the most appropriate one (entire class C, or subnetted
+ class A/B network). */
+ if (netmask[0] == '\0') {
+ strcpy(netmask, "255.255.255.0");
+ RefreshStringObj(layout[LAYOUT_NETMASK].obj);
+ }
+ if (!index(hostname, '.') && domainname[0]) {
+ strcat(hostname, ".");
+ strcat(hostname, domainname);
+ RefreshStringObj(layout[LAYOUT_HOSTNAME].obj);
+ }
+ else if (((tmp = index(hostname, '.')) != NULL) && !domainname[0]) {
+ SAFE_STRCPY(domainname, tmp + 1);
+ RefreshStringObj(layout[LAYOUT_DOMAINNAME].obj);
+ }
+ }
+
+ if (!cancel && !verifySettings())
+ goto reenter;
+
+ /* Clear this crap off the screen */
+ delwin(ds_win);
+ dialog_clear_norefresh();
+ use_helpfile(NULL);
+
+ /* We actually need to inform the rest of sysinstall about this
+ data now if the user hasn't selected cancel. Save the stuff
+ out to the environment via the variable_set() mechanism */
+
+netconfig:
+ if (!cancel) {
+ DevInfo *di;
+ char temp[512], ifn[255];
+ char *ifaces;
+
+ variable_set2(VAR_HOSTNAME, hostname);
+ sethostname(hostname, strlen(hostname));
+ if (domainname[0])
+ variable_set2(VAR_DOMAINNAME, domainname);
+ if (gateway[0])
+ variable_set2(VAR_GATEWAY, gateway);
+ if (nameserver[0])
+ variable_set2(VAR_NAMESERVER, nameserver);
+
+ if (!devp->private)
+ devp->private = (DevInfo *)safe_malloc(sizeof(DevInfo));
+ di = devp->private;
+ SAFE_STRCPY(di->ipaddr, ipaddr);
+ SAFE_STRCPY(di->netmask, netmask);
+ SAFE_STRCPY(di->extras, extras);
+
+ sprintf(temp, "inet %s %s netmask %s", ipaddr, extras, netmask);
+ sprintf(ifn, "%s%s", VAR_IFCONFIG, devp->name);
+ variable_set2(ifn, temp);
+ ifaces = variable_get(VAR_INTERFACES);
+ if (!ifaces)
+ variable_set2(VAR_INTERFACES, ifaces = "lo0");
+ /* Only add it if it's not there already */
+ if (!strstr(ifaces, devp->name)) {
+ sprintf(ifn, "%s %s", devp->name, ifaces);
+ variable_set2(VAR_INTERFACES, ifn);
+ }
+ if (ipaddr[0])
+ variable_set2(VAR_IPADDR, ipaddr);
+ configResolv(); /* XXX this will do it on the MFS copy XXX */
+ ret = DITEM_SUCCESS;
+ }
+ else
+ ret = DITEM_FAILURE;
+ restorescr(save);
+ return ret;
+}
+
+static Device *NetDev;
+
+static int
+netHook(dialogMenuItem *self)
+{
+ Device **devs;
+
+ devs = deviceFindDescr(self->prompt, self->title, DEVICE_TYPE_NETWORK);
+ if (devs) {
+ if (DITEM_STATUS(tcpOpenDialog(devs[0])) != DITEM_FAILURE)
+ NetDev = devs[0];
+ else
+ NetDev = NULL;
+ }
+ return devs ? DITEM_LEAVE_MENU : DITEM_FAILURE;
+}
+
+/* Get a network device */
+Device *
+tcpDeviceSelect(void)
+{
+ DMenu *menu;
+ Device **devs, *rval;
+ int cnt;
+
+ devs = deviceFind(NULL, DEVICE_TYPE_NETWORK);
+ cnt = deviceCount(devs);
+ rval = NULL;
+
+ if (!cnt) {
+ msgConfirm("No network devices available!");
+ return NULL;
+ }
+ else if (!RunningAsInit) {
+ if (!msgYesNo("Running multi-user, assume that the network is already configured?"))
+ return devs[0];
+ }
+ if (cnt == 1) {
+ if (DITEM_STATUS(tcpOpenDialog(devs[0]) == DITEM_SUCCESS))
+ rval = devs[0];
+ }
+ else if (variable_get(VAR_NONINTERACTIVE) && variable_get(VAR_NETWORK_DEVICE)) {
+ devs = deviceFind(variable_get(VAR_NETWORK_DEVICE), DEVICE_TYPE_NETWORK);
+ cnt = deviceCount(devs);
+ if (cnt) {
+ if (DITEM_STATUS(tcpOpenDialog(devs[0]) == DITEM_SUCCESS))
+ rval = devs[0];
+ }
+ }
+ else {
+ int status;
+
+ menu = deviceCreateMenu(&MenuNetworkDevice, DEVICE_TYPE_NETWORK, netHook, NULL);
+ if (!menu)
+ msgFatal("Unable to create network device menu! Argh!");
+ status = dmenuOpenSimple(menu, FALSE);
+ free(menu);
+ if (status)
+ rval = NetDev;
+ }
+ return rval;
+}
+
+/* Do it from a menu that doesn't care about status */
+int
+tcpMenuSelect(dialogMenuItem *self)
+{
+ Device *tmp;
+
+ tmp = tcpDeviceSelect();
+ if (tmp && !msgYesNo("Would you like to bring the %s interface up right now?", tmp->name))
+ if (!tmp->init(tmp))
+ msgConfirm("Initialization of %s device failed.", tmp->name);
+ return DITEM_SUCCESS | DITEM_RESTORE;
+}
diff --git a/usr.sbin/sysinstall/termcap.c b/usr.sbin/sysinstall/termcap.c
new file mode 100644
index 0000000..1569479
--- /dev/null
+++ b/usr.sbin/sysinstall/termcap.c
@@ -0,0 +1,135 @@
+/*
+ * Copyright (c) 1994, Paul Richards.
+ *
+ * All rights reserved.
+ *
+ * This software may be used, modified, copied, distributed, and sold, in both
+ * source and binary form provided that the above copyright and these terms
+ * are retained, verbatim, as the first lines of this file. Under no
+ * circumstances is the author responsible for the proper functioning of this
+ * software, nor does the author assume any responsibility for damages
+ * incurred with its use.
+ */
+
+#include "sysinstall.h"
+#include <stdarg.h>
+#include <fcntl.h>
+#include <sys/errno.h>
+#include <sys/ioctl.h>
+#include <machine/console.h>
+
+#define VTY_STATUS_LINE 24
+#define TTY_STATUS_LINE 23
+
+static void
+prompt_term(char **termp, char **termcapp)
+{
+ char str[80];
+ static struct {
+ const char *term, *termcap;
+ } lookup[] = { { "ansi", termcap_ansi },
+ { "vt100", termcap_vt100 },
+ { "cons25", termcap_cons25 },
+ { "cons25-m", termcap_cons25_m } };
+
+ if (RunningAsInit) {
+ while (1) {
+ int i;
+
+ printf("\nThese are the predefined terminal types available to\n");
+ printf("sysinstall when running stand-alone. Please choose the\n");
+ printf("closest match for your particular terminal.\n\n");
+ printf("1 ...................... Standard ANSI terminal.\n");
+ printf("2 ...................... VT100 or compatible terminal.\n");
+ printf("3 ...................... FreeBSD system console (color).\n");
+ printf("4 ...................... FreeBSD system console (monochrome).\n\n");
+ printf("Your choice: (1-4) ");
+ fflush(stdout);
+ fgets(str, 80, stdin);
+ i = str[0] - '0';
+ if (i > 0 && i < 5) {
+ *termp = (char *)lookup[i - 1].term;
+ *termcapp = (char *)lookup[i - 1].termcap;
+ break;
+ }
+ else
+ printf("\007Invalid choice, please try again.\n\n");
+ }
+ }
+ else {
+ printf("\nPlease set your TERM variable before running this program.\n");
+ printf("Defaulting to an ANSI compatible terminal - please press RETURN\n");
+ fgets(str, 80, stdin); /* Just to make it interactive */
+ *termp = (char *)"ansi";
+ *termcapp = (char *)termcap_ansi;
+ }
+}
+
+int
+set_termcap(void)
+{
+ char *term;
+ int stat;
+ struct ttysize ts;
+
+ term = getenv("TERM");
+ stat = ioctl(STDERR_FILENO, GIO_COLOR, &ColorDisplay);
+
+ if (!RunningAsInit) {
+ if (getenv("SYSINSTALL_DEBUG"))
+ DebugFD = open("sysinstall.debug", O_WRONLY|O_CREAT|O_TRUNC, 0644);
+ else
+ DebugFD = -1;
+ if (DebugFD < 0)
+ DebugFD = open("/dev/null", O_RDWR, 0);
+ }
+
+ if (!OnVTY || (stat < 0)) {
+ if (!term) {
+ char *term, *termcap;
+
+ prompt_term(&term, &termcap);
+ if (setenv("TERM", term, 1) < 0)
+ return -1;
+ if (setenv("TERMCAP", termcap, 1) < 0)
+ return -1;
+ }
+ if (DebugFD < 0)
+ DebugFD = open("/dev/null", O_RDWR, 0);
+ }
+ else {
+ int i, on;
+
+ if (getpid() == 1) {
+ DebugFD = open("/dev/ttyv1", O_WRONLY);
+ if (DebugFD != -1) {
+ on = 1;
+ i = ioctl(DebugFD, TIOCCONS, (char *)&on);
+ msgDebug("ioctl(%d, TIOCCONS, NULL) = %d (%s)\n",
+ DebugFD, i, !i ? "success" : strerror(errno));
+ }
+ }
+ if (ColorDisplay) {
+ if (!term) {
+ if (setenv("TERM", "cons25", 1) < 0)
+ return -1;
+ if (setenv("TERMCAP", termcap_cons25, 1) < 0)
+ return -1;
+ }
+ }
+ else {
+ if (!term) {
+ if (setenv("TERM", "cons25-m", 1) < 0)
+ return -1;
+ if (setenv("TERMCAP", termcap_cons25_m, 1) < 0)
+ return -1;
+ }
+ }
+ }
+ if (ioctl(0, TIOCGSIZE, &ts) == -1) {
+ msgDebug("Unable to get terminal size - errno %d\n", errno);
+ ts.ts_lines = 0;
+ }
+ StatusLine = ts.ts_lines ? ts.ts_lines - 1: (OnVTY ? VTY_STATUS_LINE : TTY_STATUS_LINE);
+ return 0;
+}
diff --git a/usr.sbin/sysinstall/ufs.c b/usr.sbin/sysinstall/ufs.c
new file mode 100644
index 0000000..1f321d2
--- /dev/null
+++ b/usr.sbin/sysinstall/ufs.c
@@ -0,0 +1,63 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last attempt in the `sysinstall' line, the next
+ * generation being slated to essentially a complete rewrite.
+ *
+ * $Id$
+ *
+ * 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)
+{
+ char buf[PATH_MAX];
+
+ if (isDebug())
+ msgDebug("Request for %s from UFS\n", file);
+ snprintf(buf, PATH_MAX, "%s/%s", dev->private, file);
+ if (file_readable(buf))
+ return fopen(buf, "r");
+ snprintf(buf, PATH_MAX, "%s/dists/%s", dev->private, file);
+ if (file_readable(buf))
+ return fopen(buf, "r");
+ snprintf(buf, PATH_MAX, "%s/%s/%s", dev->private, variable_get(VAR_RELNAME), file);
+ if (file_readable(buf))
+ return fopen(buf, "r");
+ snprintf(buf, PATH_MAX, "%s/%s/dists/%s", dev->private, variable_get(VAR_RELNAME), file);
+ return fopen(buf, "r");
+}
diff --git a/usr.sbin/sysinstall/user.c b/usr.sbin/sysinstall/user.c
new file mode 100644
index 0000000..680a543
--- /dev/null
+++ b/usr.sbin/sysinstall/user.c
@@ -0,0 +1,727 @@
+/*
+ * $Id: user.c,v 1.13 1997/02/22 14:12:36 peter Exp $
+ *
+ * Copyright (c) 1996
+ * Jörg Wunsch. All rights reserved.
+ *
+ * The basic structure has been taken from tcpip.c, which is:
+ *
+ * Copyright (c) 1995
+ * Gary J Palmer. All rights reserved.
+ * Jordan K Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+ * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
+ * OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
+ * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ */
+
+#include "sysinstall.h"
+#include <utmp.h>
+#include <ctype.h>
+#include <sys/param.h>
+#include <sysexits.h>
+
+/* The help file for the user mgmt screen */
+#define USER_HELPFILE "usermgmt"
+
+/* XXX should they be moved out to sysinstall.h? */
+#define GNAME_FIELD_LEN 32
+#define GID_FIELD_LEN 10
+#define GMEMB_FIELD_LEN 64
+
+#define UID_FIELD_LEN 10
+#define UGROUP_FIELD_LEN GNAME_FIELD_LEN
+#define GECOS_FIELD_LEN 64
+#define UMEMB_FIELD_LEN GMEMB_FIELD_LEN
+#define HOMEDIR_FIELD_LEN 48
+#define SHELL_FIELD_LEN 48
+#define PASSWD_FIELD_LEN 32
+
+/* These are nasty, but they make the layout structure a lot easier ... */
+
+static char gname[GNAME_FIELD_LEN],
+ gid[GID_FIELD_LEN],
+ gmemb[GMEMB_FIELD_LEN],
+ uname[UT_NAMESIZE + 1],
+ passwd[PASSWD_FIELD_LEN],
+ uid[UID_FIELD_LEN],
+ ugroup[UGROUP_FIELD_LEN],
+ gecos[GECOS_FIELD_LEN],
+ umemb[UMEMB_FIELD_LEN],
+ homedir[HOMEDIR_FIELD_LEN],
+ shell[SHELL_FIELD_LEN];
+#define CLEAR(v) memset(v, 0, sizeof v)
+
+static int okbutton, cancelbutton;
+
+
+/* What the screen size is meant to be */
+#define USER_DIALOG_Y 0
+#define USER_DIALOG_X 8
+#define USER_DIALOG_WIDTH COLS - 16
+#define USER_DIALOG_HEIGHT LINES - 2
+
+/* The group configuration menu. */
+static Layout groupLayout[] = {
+#define LAYOUT_GNAME 0
+ { 4, 10, 20, GNAME_FIELD_LEN - 1,
+ "Group name:", "The alphanumeric name of the new group (mandatory)",
+ gname, STRINGOBJ, NULL },
+#define LAYOUT_GID 1
+ { 4, 38, 10, GID_FIELD_LEN - 1,
+ "GID:", "The numerical ID for this group (leave blank for automatic choice)",
+ gid, STRINGOBJ, NULL },
+#define LAYOUT_GMEMB 2
+ { 11, 10, 40, GMEMB_FIELD_LEN - 1,
+ "Group members:", "Who belongs to this group (i.e., gets access rights for it)",
+ gmemb, STRINGOBJ, NULL },
+#define LAYOUT_OKBUTTON 3
+ { 18, 15, 0, 0,
+ "OK", "Select this if you are happy with these settings",
+ &okbutton, BUTTONOBJ, NULL },
+#define LAYOUT_CANCELBUTTON 4
+ { 18, 35, 0, 0,
+ "CANCEL", "Select this if you wish to cancel this screen",
+ &cancelbutton, BUTTONOBJ, NULL },
+ { NULL },
+};
+
+/* The user configuration menu. */
+static Layout userLayout[] = {
+#define LAYOUT_UNAME 0
+ { 3, 6, UT_NAMESIZE, UT_NAMESIZE + 1,
+ "Login ID:", "The login name of the new user (mandatory)",
+ uname, STRINGOBJ, NULL },
+#define LAYOUT_UID 1
+ { 3, 23, 8, UID_FIELD_LEN - 1,
+ "UID:", "The numerical ID for this user (leave blank for automatic choice)",
+ uid, STRINGOBJ, NULL },
+#define LAYOUT_UGROUP 2
+ { 3, 33, 8, UGROUP_FIELD_LEN - 1,
+ "Group:", "The login group name for this user (leave blank for automatic choice)",
+ ugroup, STRINGOBJ, NULL },
+#define LAYOUT_PASSWD 3
+ { 3, 43, 15, PASSWD_FIELD_LEN - 1,
+ "Password:", "The password for this user (enter this field with care!)",
+ passwd, NO_ECHO_OBJ(STRINGOBJ), NULL },
+#define LAYOUT_GECOS 4
+ { 8, 6, 33, GECOS_FIELD_LEN - 1,
+ "Full name:", "The user's full name (comment)",
+ gecos, STRINGOBJ, NULL },
+#define LAYOUT_UMEMB 5
+ { 8, 43, 15, UMEMB_FIELD_LEN - 1,
+ "Member groups:", "The groups this user belongs to (i.e. gets access rights for)",
+ umemb, STRINGOBJ, NULL },
+#define LAYOUT_HOMEDIR 6
+ { 13, 6, 20, HOMEDIR_FIELD_LEN - 1,
+ "Home directory:", "The user's home directory (leave blank for default)",
+ homedir, STRINGOBJ, NULL },
+#define LAYOUT_SHELL 7
+ { 13, 29, 29, SHELL_FIELD_LEN - 1,
+ "Login shell:", "The user's login shell (leave blank for default)",
+ shell, STRINGOBJ, NULL },
+#define LAYOUT_U_OKBUTTON 8
+ { 18, 15, 0, 0,
+ "OK", "Select this if you are happy with these settings",
+ &okbutton, BUTTONOBJ, NULL },
+#define LAYOUT_U_CANCELBUTTON 9
+ { 18, 35, 0, 0,
+ "CANCEL", "Select this if you wish to cancel this screen",
+ &cancelbutton, BUTTONOBJ, NULL },
+ { NULL },
+};
+
+/* whine */
+static void
+feepout(char *msg)
+{
+ beep();
+ dialog_notify(msg);
+}
+
+/* Check for the settings on the screen. */
+
+static int
+verifyGroupSettings(void)
+{
+ char tmp[256], *cp;
+ long lgid;
+
+ if (strlen(gname) == 0) {
+ feepout("The group name field must not be empty!");
+ return 0;
+ }
+ snprintf(tmp, 256, "pw group show -q -n %s > /dev/null", gname);
+ if (vsystem(tmp) == 0) {
+ feepout("This group name is already in use.");
+ return 0;
+ }
+ if (strlen(gid) > 0) {
+ lgid = strtol(gid, &cp, 10);
+ if (lgid < 0 || lgid >= 65536 || (*cp != '\0' && !isspace(*cp))) {
+ feepout("The GID must be a number between 1 and 65535.");
+ return 0;
+ }
+ }
+ if (strlen(gmemb) > 0) {
+ if (strpbrk(gmemb, " \t") != NULL) {
+ feepout("The group member list must not contain any whitespace;\n"
+ "use commas to separate the names.");
+ return 0;
+ }
+#ifndef notyet /* XXX */
+ feepout("Sorry, the group member list feature\n"
+ "is currently not yet implemented.");
+ return 0;
+#endif
+ }
+
+ return 1;
+}
+
+/*
+ * Ask pw(8) to fill in the blanks for us.
+ * Works solely on the global variables.
+ */
+
+static void
+completeGroup(void)
+{
+ int pfd[2], i;
+ char tmp[256], *cp;
+ ssize_t l;
+ size_t amnt;
+ pid_t pid;
+ char *vec[4] =
+ {
+ "pw", "group", "next", 0
+ };
+
+ pipe (pfd);
+ if ((pid = fork()) == 0)
+ {
+ /* The kiddy. */
+ dup2(pfd[1], 1);
+ dup2(pfd[1], 2);
+ for (i = getdtablesize(); i > 2; i--)
+ close(i);
+
+ execv("/usr/sbin/pw", vec);
+ msgDebug("Cannot execv() /usr/sbin/pw.\n");
+ _exit(99);
+ }
+ else
+ {
+ /* The oldie. */
+ close(pfd[1]);
+ amnt = sizeof tmp;
+ i = 0;
+ while((l = read(pfd[0], &tmp[i], amnt)) > 0)
+ {
+ amnt -= l;
+ i += l;
+ if (amnt == 0)
+ {
+ close(pfd[0]);
+ break;
+ }
+ }
+ close(pfd[0]);
+ tmp[i] = '\0';
+ waitpid(pid, &i, 0);
+ if (WIFSIGNALED(i) || WEXITSTATUS(i) != 0)
+ /* ignore by now */
+ return;
+ if ((cp = strchr(tmp, '\n')) != NULL)
+ *cp = '\0';
+ strncpy(gid, tmp, sizeof gid);
+ }
+}
+
+static void
+addGroup(WINDOW *ds_win)
+{
+ char tmp[256];
+ int pfd[2], i;
+ ssize_t l;
+ size_t amnt;
+ pid_t pid;
+ char *vec[8] =
+ {
+ "pw", "group", "add", "-n", 0, "-g", 0, 0
+ };
+#define VEC_GNAME 4
+#define VEC_GID 6
+
+ msgNotify("Adding group \"%s\"...", gname);
+
+ pipe (pfd);
+ if ((pid = fork()) == 0)
+ {
+ /* The kiddy. */
+ dup2(pfd[1], 1);
+ dup2(pfd[1], 2);
+ for (i = getdtablesize(); i > 2; i--)
+ close(i);
+
+ vec[VEC_GNAME] = gname;
+
+ if (strlen(gid) > 0)
+ vec[VEC_GID] = gid;
+ else
+ vec[VEC_GID - 1] = 0;
+
+ execv("/usr/sbin/pw", vec);
+ msgDebug("Cannot execv() /usr/sbin/pw.\n");
+ _exit(99);
+ }
+ else
+ {
+ /* The oldie. */
+ close(pfd[1]);
+ amnt = sizeof tmp;
+ i = 0;
+ while((l = read(pfd[0], &tmp[i], amnt)) > 0)
+ {
+ amnt -= l;
+ i += l;
+ if (amnt == 0)
+ {
+ close(pfd[0]);
+ break;
+ }
+ }
+ close(pfd[0]);
+ tmp[i] = '\0';
+ waitpid(pid, &i, 0);
+ if (WIFSIGNALED(i))
+ msgDebug("pw(8) exited with signal %d.\n", WTERMSIG(i));
+ else if(WEXITSTATUS(i))
+ {
+ i = 0;
+ if(strncmp(tmp, "pw: ", 4) == 0)
+ i = 4;
+ tmp[sizeof tmp - 1] = '\0'; /* sanity */
+ msgConfirm("The `pw' command exited with an error status.\n"
+ "Its error message was:\n\n%s",
+ &tmp[i]);
+ }
+ }
+#undef VEC_GNAME
+#undef VEC_GID
+}
+
+int
+userAddGroup(dialogMenuItem *self)
+{
+ WINDOW *ds_win, *save;
+ ComposeObj *obj = NULL;
+ int n = 0, cancel = FALSE, ret;
+ int max, firsttime = TRUE;
+
+ if (RunningAsInit && !strstr(variable_get(SYSTEM_STATE), "install")) {
+ msgConfirm("This option may only be used after the system is installed, sorry!");
+ return DITEM_FAILURE;
+ }
+
+ save = savescr();
+ dialog_clear_norefresh();
+ /* We need a curses window */
+ if (!(ds_win = openLayoutDialog(USER_HELPFILE, " User and Group Management ",
+ USER_DIALOG_X, USER_DIALOG_Y, USER_DIALOG_WIDTH, USER_DIALOG_HEIGHT))) {
+ beep();
+ msgConfirm("Cannot open addgroup dialog window!!");
+ return(DITEM_FAILURE);
+ }
+
+ /* Draw a group entry box */
+ draw_box(ds_win, USER_DIALOG_Y + 2, USER_DIALOG_X + 8, USER_DIALOG_HEIGHT - 8,
+ USER_DIALOG_WIDTH - 17, dialog_attr, border_attr);
+ wattrset(ds_win, dialog_attr);
+ mvwaddstr(ds_win, USER_DIALOG_Y + 2, USER_DIALOG_X + 22, " Add a new group ");
+
+ CLEAR(gname);
+ CLEAR(gid);
+ CLEAR(gmemb);
+
+ /* Some more initialisation before we go into the main input loop */
+ obj = initLayoutDialog(ds_win, groupLayout, USER_DIALOG_X, USER_DIALOG_Y, &max);
+
+reenter:
+ cancelbutton = okbutton = 0;
+ if (firsttime) {
+ /* fill in the blanks, well, just the GID */
+ completeGroup();
+ RefreshStringObj(groupLayout[LAYOUT_GID].obj);
+ firsttime = FALSE;
+ }
+
+ while (layoutDialogLoop(ds_win, groupLayout, &obj, &n, max, &cancelbutton, &cancel));
+
+ if (!cancel && !verifyGroupSettings())
+ goto reenter;
+
+ /* Clear this crap off the screen */
+ delwin(ds_win);
+ dialog_clear_norefresh();
+ use_helpfile(NULL);
+
+ if (!cancel) {
+ addGroup(ds_win);
+ ret = DITEM_SUCCESS;
+ }
+ else
+ ret = DITEM_FAILURE;
+ restorescr(save);
+ return ret;
+}
+
+/* Check for the settings on the screen. */
+
+static int
+verifyUserSettings(WINDOW *ds_win)
+{
+ char tmp[256], *cp;
+ long luid;
+ WINDOW *save;
+ int rv;
+
+ if (strlen(uname) == 0) {
+ feepout("The user name field must not be empty!");
+ return 0;
+ }
+ snprintf(tmp, 256, "pw user show -q -n %s > /dev/null", uname);
+ if (vsystem(tmp) == 0) {
+ feepout("This user name is already in use.");
+ return 0;
+ }
+ if (strlen(uid) > 0) {
+ luid = strtol(uid, &cp, 10);
+ if (luid < 0 || luid >= 65536 || (*cp != '\0' && !isspace(*cp))) {
+ feepout("The UID must be a number between 1 and 65535.");
+ return 0;
+ }
+ }
+ if (strlen(shell) > 0) {
+ while((cp = getusershell()) != NULL)
+ if (strcmp(cp, shell) == 0)
+ break;
+ endusershell();
+ if (cp == NULL) {
+ save = savescr();
+ rv = msgYesNo("Warning:\n\n"
+ "The requested shell \"%s\" is not\n"
+ "a valid user shell.\n\n"
+ "Use it anyway?\n", shell);
+ restorescr(save);
+ wrefresh(ds_win);
+ if (rv != DITEM_SUCCESS)
+ return 0;
+ }
+
+ }
+
+ if (strlen(umemb) > 0) {
+ if (strpbrk(umemb, " \t") != NULL) {
+ feepout("The member groups list must not contain any whitespace;\n"
+ "use commas to separate the names.");
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+/*
+ * Ask pw(8) to fill in the blanks for us.
+ * Works solely on the global variables.
+ */
+
+static void
+completeUser(void)
+{
+ int pfd[2], i;
+ char tmp[256], *cp, *cp2;
+ ssize_t l;
+ size_t amnt;
+ pid_t pid;
+ char *vec[7] =
+ {
+ "pw", "user", "add", "-N", "-n", 0, 0
+ };
+#define VEC_UNAME 5
+
+ pipe (pfd);
+ if ((pid = fork()) == 0)
+ {
+ /* The kiddy. */
+ dup2(pfd[1], 1);
+ dup2(pfd[1], 2);
+ for (i = getdtablesize(); i > 2; i--)
+ close(i);
+
+ vec[VEC_UNAME] = uname;
+
+ execv("/usr/sbin/pw", vec);
+ msgDebug("Cannot execv() /usr/sbin/pw.\n");
+ _exit(99);
+ }
+ else
+ {
+ /* The oldie. */
+ close(pfd[1]);
+ amnt = sizeof tmp;
+ i = 0;
+ while((l = read(pfd[0], &tmp[i], amnt)) > 0)
+ {
+ amnt -= l;
+ i += l;
+ if (amnt == 0)
+ {
+ close(pfd[0]);
+ break;
+ }
+ }
+ close(pfd[0]);
+ tmp[i] = '\0';
+ waitpid(pid, &i, 0);
+ if (WIFSIGNALED(i) || WEXITSTATUS(i) != 0)
+ /* ignore by now */
+ return;
+ if ((cp = strchr(tmp, '\n')) != NULL)
+ *cp = '\0';
+ if ((cp = strchr(tmp, ':')) == NULL || (cp = strchr(++cp, ':')) == NULL)
+ return;
+ cp++;
+ if ((cp2 = strchr(cp, ':')) == NULL)
+ return;
+ *cp2++ = '\0';
+ strncpy(uid, cp, sizeof uid);
+ cp = cp2;
+ if ((cp2 = strchr(cp, ':')) == NULL)
+ return;
+ *cp2++ = '\0';
+#ifdef notyet /* XXX pw user add -g doesn't accept a numerical GID */
+ strncpy(ugroup, cp, sizeof ugroup);
+#endif
+ cp = cp2;
+ if ((cp2 = strchr(cp, ':')) == NULL || (cp2 = strchr(++cp2, ':')) == NULL ||
+ (cp = cp2 = strchr(++cp2, ':')) == NULL || (cp2 = strchr(++cp2, ':')) == NULL)
+ return;
+ *cp2++ = '\0';
+ cp++;
+ strncpy(gecos, cp, sizeof gecos);
+ cp = cp2;
+ if ((cp2 = strchr(cp, ':')) == NULL)
+ return;
+ *cp2++ = '\0';
+ if (*cp2)
+ strncpy(shell, cp2, sizeof shell);
+ }
+#undef VEC_UNAME
+}
+
+static void
+addUser(WINDOW *ds_win)
+{
+ char tmp[256], *msg;
+ int pfd[2], ipfd[2], i, j;
+ ssize_t l;
+ size_t amnt;
+ pid_t pid;
+ /*
+ * Maximal list:
+ * pw user add -m -n uname -g grp -u uid -c comment -d homedir -s shell -G grplist -h 0
+ */
+ char *vec[21] =
+ {
+ "pw", "user", "add", "-m", "-n", /* ... */
+ };
+#define VEC_UNAME 5
+
+ msgNotify("Adding user \"%s\"...", uname);
+
+ pipe (pfd);
+ pipe (ipfd);
+ if ((pid = fork()) == 0)
+ {
+ /* The kiddy. */
+ dup2(ipfd[0], 0);
+ dup2(pfd[1], 1);
+ dup2(pfd[1], 2);
+ for (i = getdtablesize(); i > 2; i--)
+ close(i);
+
+ vec[i = VEC_UNAME] = uname;
+ i++;
+#define ADDVEC(var, option) do { if (strlen(var) > 0) { vec[i++] = option; vec[i++] = var; } } while (0)
+ ADDVEC(ugroup, "-g");
+ ADDVEC(uid, "-u");
+ ADDVEC(gecos, "-c");
+ ADDVEC(homedir, "-d");
+ ADDVEC(shell, "-s");
+ ADDVEC(umemb, "-G");
+ if (passwd[0]) {
+ vec[i++] = "-h";
+ vec[i++] = "0";
+ }
+ vec[i] = 0;
+
+ execv("/usr/sbin/pw", vec);
+ msgDebug("Cannot execv() /usr/sbin/pw.\n");
+ _exit(99);
+ }
+ else
+ {
+ /* The oldie. */
+ close(pfd[1]);
+ close(ipfd[0]);
+
+ if (passwd[0])
+ write(ipfd[1], passwd, strlen(passwd));
+ close(ipfd[1]);
+ amnt = sizeof tmp;
+ i = 0;
+ while((l = read(pfd[0], &tmp[i], amnt)) > 0)
+ {
+ amnt -= l;
+ i += l;
+ if (amnt == 0)
+ {
+ close(pfd[0]);
+ break;
+ }
+ }
+ close(pfd[0]);
+ tmp[i] = '\0';
+ waitpid(pid, &i, 0);
+ if (WIFSIGNALED(i))
+ {
+ j = WTERMSIG(i);
+ msg = "The `pw' command exited with signal %d.\n";
+ goto sysfail;
+ }
+ else if((j = WEXITSTATUS(i)))
+ {
+ i = 0;
+ if(strncmp(tmp, "pw: ", 4) == 0)
+ i = 4;
+ tmp[sizeof tmp - 1] = '\0'; /* sanity */
+ if (j == EX_DATAERR || j == EX_NOUSER || j == EX_SOFTWARE)
+ msgConfirm("The `pw' command exited with an error status.\n"
+ "Its error message was:\n\n%s",
+ &tmp[i]);
+ else
+ {
+ msg = "The `pw' command exited with unexpected status %d.\n";
+ sysfail:
+ msgDebug(msg, j);
+ msgDebug("Command stdout and stderr was:\n\n%s", tmp);
+ msgConfirm(msg, j);
+ }
+ }
+ else if (!passwd[0])
+ msgConfirm("You will need to enter a password for this user\n"
+ "later, using the passwd(1) command from the shell.\n\n"
+ "The account for `%s' is currently still disabled.",
+ uname);
+ }
+#undef VEC_UNAME
+#undef ADDVEC
+}
+
+int
+userAddUser(dialogMenuItem *self)
+{
+ WINDOW *ds_win, *save;
+ ComposeObj *obj = NULL;
+ int n = 0, cancel = FALSE, ret;
+ int max, firsttime = TRUE;
+
+ if (RunningAsInit && !strstr(variable_get(SYSTEM_STATE), "install")) {
+ msgConfirm("This option may only be used after the system is installed, sorry!");
+ return DITEM_FAILURE;
+ }
+
+ save = savescr();
+ dialog_clear_norefresh();
+
+ /* We need a curses window */
+ if (!(ds_win = openLayoutDialog(USER_HELPFILE, " User and Group Management ",
+ USER_DIALOG_X, USER_DIALOG_Y, USER_DIALOG_WIDTH, USER_DIALOG_HEIGHT))) {
+ beep();
+ msgConfirm("Cannot open adduser dialog window!!");
+ return(DITEM_FAILURE);
+ }
+
+ /* Draw a user entry box */
+ draw_box(ds_win, USER_DIALOG_Y + 1, USER_DIALOG_X + 3, USER_DIALOG_HEIGHT - 6,
+ USER_DIALOG_WIDTH - 6, dialog_attr, border_attr);
+ wattrset(ds_win, dialog_attr);
+ mvwaddstr(ds_win, USER_DIALOG_Y + 1, USER_DIALOG_X + 22, " Add a new user ");
+
+ CLEAR(uname);
+ CLEAR(uid);
+ CLEAR(ugroup);
+ CLEAR(gecos);
+ CLEAR(passwd);
+ CLEAR(umemb);
+ CLEAR(homedir);
+ CLEAR(shell);
+
+ /* Some more initialisation before we go into the main input loop */
+ obj = initLayoutDialog(ds_win, userLayout, USER_DIALOG_X, USER_DIALOG_Y, &max);
+
+reenter:
+ cancelbutton = okbutton = 0;
+ if (firsttime) {
+ /* fill in the blanks, well, just the GID */
+ completeUser();
+ RefreshStringObj(userLayout[LAYOUT_UID].obj);
+ RefreshStringObj(userLayout[LAYOUT_UGROUP].obj);
+ RefreshStringObj(userLayout[LAYOUT_GECOS].obj);
+ RefreshStringObj(userLayout[LAYOUT_UMEMB].obj);
+ RefreshStringObj(userLayout[LAYOUT_HOMEDIR].obj);
+ RefreshStringObj(userLayout[LAYOUT_SHELL].obj);
+ firsttime = FALSE;
+ }
+
+ while (layoutDialogLoop(ds_win, userLayout, &obj, &n, max, &cancelbutton, &cancel));
+
+ if (!cancel && !verifyUserSettings(ds_win))
+ goto reenter;
+
+ /* Clear this crap off the screen */
+ delwin(ds_win);
+ dialog_clear_norefresh();
+ use_helpfile(NULL);
+
+ if (!cancel) {
+ addUser(ds_win);
+ ret = DITEM_SUCCESS;
+ }
+ else
+ ret = DITEM_FAILURE;
+ restorescr(save);
+ return ret;
+}
+
diff --git a/usr.sbin/sysinstall/variable.c b/usr.sbin/sysinstall/variable.c
new file mode 100644
index 0000000..a13b71d
--- /dev/null
+++ b/usr.sbin/sysinstall/variable.c
@@ -0,0 +1,193 @@
+/*
+ * The new sysinstall program.
+ *
+ * This is probably the last program in the `sysinstall' line - the next
+ * generation being essentially a complete rewrite.
+ *
+ * $Id: variable.c,v 1.20 1997/06/13 14:21:22 jkh Exp $
+ *
+ * Copyright (c) 1995
+ * Jordan Hubbard. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * verbatim and that no modifications are made prior to this
+ * point in the file.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY JORDAN HUBBARD ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL JORDAN HUBBARD OR HIS PETS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, LIFE OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include "sysinstall.h"
+
+/* Routines for dealing with variable lists */
+
+static void
+make_variable(char *var, char *value)
+{
+ Variable *vp;
+
+ /* Trim leading and trailing whitespace */
+ var = string_skipwhite(string_prune(var));
+
+ if (!var || !*var)
+ return;
+
+ /* Put it in the environment in any case */
+ setenv(var, value, 1);
+
+ /* Now search to see if it's already in the list */
+ for (vp = VarHead; vp; vp = vp->next) {
+ if (!strcmp(vp->name, var)) {
+ if (isDebug())
+ msgDebug("variable %s was %s, now %s\n", vp->name, vp->value, value);
+ free(vp->value);
+ vp->value = strdup(value);
+ return;
+ }
+ }
+
+ /* No? Create a new one */
+ vp = (Variable *)safe_malloc(sizeof(Variable));
+ vp->name = strdup(var);
+ vp->value = strdup(value);
+ vp->next = VarHead;
+ VarHead = vp;
+ if (isDebug())
+ msgDebug("Setting variable %s to %s\n", vp->name, vp->value);
+}
+
+void
+variable_set(char *var)
+{
+ 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));
+}
+
+void
+variable_set2(char *var, char *value)
+{
+ if (!var || !value)
+ msgFatal("Null name or value passed to set_variable2!");
+ else if (!*var || !*value)
+ msgDebug("Warning: Zero length name or value passed to variable_set2()\n");
+ make_variable(var, value);
+}
+
+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)
+{
+ char *cp;
+
+ cp = variable_get(var);
+ if (cp && variable_get(VAR_NONINTERACTIVE))
+ return cp;
+ else if ((cp = msgGetInput(cp, prompt)) != NULL)
+ variable_set2(var, cp);
+ 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 *w, *cp, *cp2, *cp3, tmp[256];
+
+ w = data;
+ if (!w)
+ return FALSE;
+ SAFE_STRCPY(tmp, w);
+ if ((cp = index(tmp, '=')) != NULL) {
+ *(cp++) = '\0';
+ if ((cp3 = index(cp, ',')) != NULL)
+ *cp3 = '\0';
+ cp2 = getenv(tmp);
+
+ if (cp2)
+ return !strcmp(cp, cp2);
+ else
+ return FALSE;
+ }
+ else
+ return (int)getenv(tmp);
+}
diff --git a/usr.sbin/sysinstall/wizard.c b/usr.sbin/sysinstall/wizard.c
new file mode 100644
index 0000000..59831ea
--- /dev/null
+++ b/usr.sbin/sysinstall/wizard.c
@@ -0,0 +1,232 @@
+/*
+ * ----------------------------------------------------------------------------
+ * "THE BEER-WARE LICENSE" (Revision 42):
+ * <phk@login.dknet.dk> wrote this file. As long as you retain this notice you
+ * can do whatever you want with this stuff. If we meet some day, and you think
+ * this stuff is worth it, you can buy me a beer in return. Poul-Henning Kamp
+ * ----------------------------------------------------------------------------
+ *
+ * $Id$
+ *
+ */
+
+#include "sysinstall.h"
+#include <fcntl.h>
+#include <err.h>
+
+u_char mbr[] = {
+ 250,51,192,142,208,188,0,124,139,244,80,7,80,31,251,252,191,0,6,185,0,1,
+ 242,165,234,29,6,0,0,190,190,7,179,4,128,60,128,116,14,128,60,0,117,28,
+ 131,198,16,254,203,117,239,205,24,139,20,139,76,2,139,238,131,198,16,254,
+ 203,116,26,128,60,0,116,244,190,139,6,172,60,0,116,11,86,187,7,0,180,14,
+ 205,16,94,235,240,235,254,191,5,0,187,0,124,184,1,2,87,205,19,95,115,12,
+ 51,192,205,19,79,117,237,190,163,6,235,211,190,194,6,191,254,125,129,61,
+ 85,170,117,199,139,245,234,0,124,0,0,73,110,118,97,108,105,100,32,112,97,
+ 114,116,105,116,105,111,110,32,116,97,98,108,101,0,69,114,114,111,114,32,
+ 108,111,97,100,105,110,103,32,111,112,101,114,97,116,105,110,103,32,115,
+ 121,115,116,101,109,0,77,105,115,115,105,110,103,32,111,112,101,114,97,
+ 116,105,110,103,32,115,121,115,116,101,109,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,
+ 1,1,0,4,15,63,60,63,0,0,0,241,239,0,0,0,0,1,61,5,15,63,243,48,240,0,0,144,
+ 208,2,0,0,0,1,244,165,15,63,170,192,192,3,0,144,208,2,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,85,170
+};
+
+u_char bteasy17[] = {
+ 51,192,142,192,142,216,142,208,188,0,124,252,139,244,191,0,6,185,0,1,242,
+ 165,234,96,6,0,0,139,213,88,162,72,7,60,53,116,28,180,16,246,228,5,174,
+ 4,150,246,68,4,255,116,62,198,4,128,232,218,0,138,116,1,139,76,2,235,8,
+ 232,207,0,185,1,0,50,209,187,0,124,184,1,2,205,19,114,30,129,191,254,1,
+ 85,170,117,22,234,0,124,0,0,128,250,129,116,2,178,128,139,234,66,128,242,
+ 179,136,22,58,7,191,190,7,185,4,0,198,6,45,7,49,50,246,136,45,138,69,4,
+ 60,0,116,35,60,5,116,31,254,198,190,42,7,232,113,0,190,72,7,70,70,139,28,
+ 10,255,116,5,50,125,4,117,243,141,183,114,7,232,90,0,131,199,16,254,6,45,
+ 7,226,203,128,62,117,4,2,116,11,190,59,7,10,246,117,10,205,24,235,172,190,
+ 42,7,232,57,0,232,54,0,50,228,205,26,139,218,131,195,96,180,1,205,22,180,
+ 0,117,11,205,26,59,211,114,242,160,72,7,235,10,205,22,138,196,60,28,116,
+ 243,4,246,60,49,114,214,60,53,119,210,80,190,40,7,187,27,6,83,252,172,80,
+ 36,127,180,14,205,16,88,168,128,116,242,195,86,184,1,3,187,0,6,185,1,0,
+ 50,246,205,19,94,198,6,72,7,63,195,13,138,13,10,70,48,32,46,32,46,32,46,
+ 160,100,105,115,107,32,49,13,10,10,68,101,102,97,117,108,116,58,32,70,63,
+ 160,0,1,0,4,0,6,3,7,7,10,10,99,14,100,14,101,20,128,20,129,25,130,30,147,
+ 36,165,39,159,43,117,47,82,47,219,50,64,55,242,61,0,100,111,243,72,80,70,
+ 211,79,115,178,85,110,105,248,78,111,118,101,108,236,77,105,110,105,248,
+ 76,105,110,117,248,65,109,111,101,98,225,66,83,196,66,83,68,233,80,67,73,
+ 216,67,80,205,86,101,110,105,248,68,111,115,115,101,227,63,191,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+ 0,85,170
+};
+
+int
+scan_block(int fd, daddr_t block)
+{
+ u_char foo[512];
+
+ if (-1 == lseek(fd,block * 512,SEEK_SET))
+ err(1,"lseek");
+ if (512 != read(fd,foo, 512))
+ return 1;
+ return 0;
+}
+
+void
+Scan_Disk(Disk *d)
+{
+ char device[64];
+ u_long l;
+ int i,j,fd;
+
+ strcpy(device,"/dev/r");
+ strcat(device,d->name);
+
+ fd = open(device,O_RDWR);
+ if (fd < 0) {
+ msgWarn("open(%s) failed", device);
+ return;
+ }
+ for(i=-1,l=0;;l++) {
+ j = scan_block(fd,l);
+ if (j != i) {
+ if (i == -1) {
+ printf("%c: %lu.",j ? 'B' : 'G', l);
+ fflush(stdout);
+ } else if (i == 0) {
+ printf(".%lu\nB: %lu.",l-1,l);
+ fflush(stdout);
+ } else {
+ printf(".%lu\nG: %lu.",l-1,l);
+ fflush(stdout);
+ }
+ i = j;
+ }
+ }
+ close(fd);
+}
+
+void
+slice_wizard(Disk *d)
+{
+ Disk *db;
+ char myprompt[BUFSIZ];
+ char input[BUFSIZ];
+ char *p,*q=0;
+ char **cp,*cmds[200];
+ int ncmd,i;
+
+ sprintf(myprompt,"%s> ", d->name);
+ while(1) {
+ printf("--==##==--\n");
+ Debug_Disk(d);
+ p = CheckRules(d);
+ if (p) {
+ printf("%s",p);
+ free(p);
+ }
+ printf(myprompt);
+ fflush(stdout);
+ q = p = fgets(input,sizeof(input),stdin);
+ if(!p)
+ break;
+ for(cp = cmds; (*cp = strsep(&p, " \t\n")) != NULL;)
+ if (**cp != '\0')
+ cp++;
+ ncmd = cp - cmds;
+ if(!ncmd)
+ continue;
+ if (!strcasecmp(*cmds,"quit")) { break; }
+ if (!strcasecmp(*cmds,"exit")) { break; }
+ if (!strcasecmp(*cmds,"q")) { break; }
+ if (!strcasecmp(*cmds,"x")) { break; }
+ if (!strcasecmp(*cmds,"delete") && ncmd == 2) {
+ printf("delete = %d\n",
+ Delete_Chunk(d,
+ (struct chunk *)strtol(cmds[1],0,0)));
+ continue;
+ }
+ if (!strcasecmp(*cmds,"allfreebsd")) {
+ All_FreeBSD(d, 0);
+ continue;
+ }
+ if (!strcasecmp(*cmds,"dedicate")) {
+ All_FreeBSD(d, 1);
+ continue;
+ }
+ if (!strcasecmp(*cmds,"bios") && ncmd == 4) {
+ Set_Bios_Geom(d,
+ strtol(cmds[1],0,0),
+ strtol(cmds[2],0,0),
+ strtol(cmds[3],0,0));
+ continue;
+ }
+ if (!strcasecmp(*cmds,"list")) {
+ cp = Disk_Names();
+ printf("Disks:");
+ for(i=0;cp[i];i++) {
+ printf(" %s",cp[i]);
+ free(cp[i]);
+ }
+ free(cp);
+ continue;
+ }
+ if (!strcasecmp(*cmds,"create") && ncmd == 6) {
+
+ printf("Create=%d\n",
+ Create_Chunk(d,
+ strtol(cmds[1],0,0),
+ strtol(cmds[2],0,0),
+ strtol(cmds[3],0,0),
+ strtol(cmds[4],0,0),
+ strtol(cmds[5],0,0)));
+ continue;
+ }
+ if (!strcasecmp(*cmds,"read")) {
+ db = d;
+ if (ncmd > 1)
+ d = Open_Disk(cmds[1]);
+ else
+ d = Open_Disk(d->name);
+ if (d)
+ Free_Disk(db);
+ else
+ d = db;
+ continue;
+ }
+ if (!strcasecmp(*cmds,"scan")) {
+ Scan_Disk(d);
+ continue;
+ }
+ if (!strcasecmp(*cmds,"write")) {
+ printf("Write=%d\n",
+ Fake ? 0 : Write_Disk(d));
+ Free_Disk(d);
+ d = Open_Disk(d->name);
+ continue;
+ }
+ if (strcasecmp(*cmds,"help"))
+ printf("\007ERROR\n");
+ printf("CMDS:\n");
+ printf("allfreebsd\t\t");
+ printf("dedicate\t\t");
+ printf("bios cyl hd sect\n");
+ printf("collapse [pointer]\t\t");
+ printf("create offset size enum subtype flags\n");
+ printf("subtype(part): swap=1, ffs=7\t\t");
+ printf("delete pointer\n");
+ printf("list\t\t");
+ printf("quit\n");
+ printf("read [disk]\t\t");
+ printf("scan\n");
+ printf("write\t\t");
+ printf("ENUM:\n\t");
+ for(i=0;chunk_n[i];i++)
+ printf("%d = %s%s",i,chunk_n[i],i == 4 ? "\n\t" : " ");
+ printf("\n");
+
+ }
+}
diff --git a/usr.sbin/syslogd/Makefile b/usr.sbin/syslogd/Makefile
new file mode 100644
index 0000000..5112bd4
--- /dev/null
+++ b/usr.sbin/syslogd/Makefile
@@ -0,0 +1,9 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= syslogd
+SRCS= syslogd.c ttymsg.c
+.PATH: ${.CURDIR}/../../usr.bin/wall
+MAN5= syslog.conf.5
+MAN8= syslogd.8
+
+.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..f8e214d
--- /dev/null
+++ b/usr.sbin/syslogd/syslog.conf.5
@@ -0,0 +1,324 @@
+.\" Copyright (c) 1990, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)syslog.conf.5 8.1 (Berkeley) 6/9/93
+.\" $Id: syslog.conf.5,v 1.8 1997/10/06 20:37:50 joerg Exp $
+.\"
+.Dd June 9, 1993
+.Dt SYSLOG.CONF 5
+.Os
+.Sh NAME
+.Nm syslog.conf
+.Nd
+.Xr syslogd 8
+configuration file
+.Sh DESCRIPTION
+The
+.Nm
+file is the configuration file for the
+.Xr syslogd 8
+program.
+It consists of
+blocks of lines separated by
+.Em program
+specifications,
+with each line containing two fields: the
+.Em selector
+field which specifies the types of messages and priorities to which the
+line applies, and an
+.Em action
+field which specifies the action to be taken if a message
+.Xr syslogd
+receives matches the selection criteria.
+The
+.Em selector
+field is separated from the
+.Em action
+field by one or more tab characters.
+.Pp
+The
+.Em Selectors
+function
+are encoded as a
+.Em facility ,
+a period
+.Pq Dq \&. ,
+and a
+.Em level ,
+with no intervening white-space.
+Both the
+.Em facility
+and the
+.Em level
+are case insensitive.
+.Pp
+The
+.Em facility
+describes the part of the system generating the message, and is one of
+the following keywords: auth, authpriv, cron, daemon, ftp, kern, lpr, mail,
+mark, news, ntp, syslog, user, uucp and local0 through local7.
+These keywords (with the exception of mark) correspond to the
+similar
+.Dq Dv LOG_
+values specified to the
+.Xr openlog 3
+and
+.Xr syslog 3
+library routines.
+.Pp
+The
+.Em level
+describes the severity of the message, and is a keyword from the
+following ordered list (higher to lower): emerg, alert, crit, err,
+warning, notice, info and debug.
+These keywords correspond to the
+similar
+.Dq Dv LOG_
+values specified to the
+.Xr syslog
+library routine.
+.Pp
+Each block of lines is separated from the previous block by a tag. The tag
+is a line beginning with
+.Em #!prog
+or
+.Em !prog
+(the former is for compatibility with the previous syslogd, if one is sharing
+syslog.conf files, for example)
+and each block will be associated with calls to syslog from that specific
+program.
+.Pp
+See
+.Xr syslog 3
+for a further descriptions of both the
+.Em facility
+and
+.Em level
+keywords and their significance. It's preferred that selections be made on
+.Em facility
+rather than
+.Em program ,
+since the latter can easily vary in a networked environment. In some cases,
+though, an appropriate
+.Em facility
+simply doesn't exist.
+.Pp
+If a received message matches the specified
+.Em facility
+and is of the specified
+.Em level
+.Em (or a higher level) ,
+and the first word in the message after the date matches the
+.Em program ,
+the action specified in the
+.Em action
+field will be taken.
+.Pp
+Multiple
+.Em selectors
+may be specified for a single
+.Em action
+by separating them with semicolon
+.Pq Dq \&;
+characters.
+It is important to note, however, that each
+.Em selector
+can modify the ones preceding it.
+.Pp
+Multiple
+.Em facilities
+may be specified for a single
+.Em level
+by separating them with comma
+.Pq Dq \&,
+characters.
+.Pp
+An asterisk
+.Pq Dq *
+can be used to specify all
+.Em facilities
+all
+.Em levels
+or all
+.Em programs .
+.Pp
+The special
+.Em facility
+.Dq mark
+receives a message at priority
+.Dq info
+every 20 minutes
+(see
+.Xr syslogd 8 ) .
+This is not enabled by a
+.Em facility
+field containing an asterisk.
+.Pp
+The special
+.Em level
+.Dq none
+disables a particular
+.Em facility .
+.Pp
+The
+.Em action
+field of each line specifies the action to be taken when the
+.Em selector
+field selects a message.
+There are five forms:
+.Bl -bullet
+.It
+A pathname (beginning with a leading slash).
+Selected messages are appended to the file.
+.It
+A hostname (preceded by an at
+.Pq Dq @
+sign).
+Selected messages are forwarded to the
+.Xr syslogd
+program on the named host.
+.It
+A comma separated list of users.
+Selected messages are written to those users
+if they are logged in.
+.It
+An asterisk.
+Selected messages are written to all logged-in users.
+.It
+A vertical bar
+.Pq Dq \&| ,
+followed by a command to pipe the selected
+messages to. The command is passed to a
+.Pa /bin/sh
+for evaluation, so usual shell metacharacters or input/output
+redirection can occur. (Note however that redirecting
+.Xr stdio 3
+buffered output from the invoked command can cause additional delays,
+or even lost output data in case a logging subprocess exited with a
+signal.) The command itself runs with
+.Em stdout
+and
+.Em stderr
+redirected to
+.Pa /dev/null .
+Upon receipt of a
+.Dv SIGHUP ,
+.Nm
+will close the pipe to the process. If the process didn't exit
+voluntarily, it will be sent a
+.Dv SIGTERM
+signal after a grace period of up to 60 seconds.
+.Pp
+The command will only be started once data arrives that should be piped
+to it. If it exited later, it will be restarted as necessary. So if it
+is desired that the subprocess should get exactly one line of input only
+(which can be very resource-consuming if there are a lot of messages
+flowing quickly), this can be achieved by exiting after just one line of
+input. If necessary, a script wrapper can be written to this effect.
+.Pp
+Unless the command is a full pipeline, it's probably useful to
+start the command with
+.Em exec
+so that the invoking shell process does not wait for the command to
+complete. Warning: the process is started under the UID invoking
+.Xr syslogd 8 ,
+normally the superuser.
+.El
+.Pp
+Blank lines and lines whose first non-blank character is a hash
+.Pq Dq #
+character are ignored.
+.Sh EXAMPLES
+.Pp
+A configuration file might appear as follows:
+.Bd -literal
+# Log all kernel messages, authentication messages of
+# level notice or higher and anything of level err or
+# higher to the console.
+# Don't log private authentication messages!
+*.err;kern.*;auth.notice;authpriv.none /dev/console
+
+# Log anything (except mail) of level info or higher.
+# Don't log private authentication messages!
+*.info;mail.none;authpriv.none /var/log/messages
+
+# 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
+.Ed
+.Sh FILES
+.Bl -tag -width /etc/syslog.conf -compact
+.It Pa /etc/syslog.conf
+.Xr syslogd 8
+configuration file
+.El
+.Sh BUGS
+The effects of multiple selectors are sometimes not intuitive.
+For example
+.Dq mail.crit,*.err
+will select
+.Dq mail
+facility messages at the level of
+.Dq err
+or higher, not at the level of
+.Dq crit
+or higher.
+.Pp
+In networked environments, note that not all operating systems
+implement the same set of facilities. The facilities
+authpriv, cron, ftp, and ntp that are known to this implementation
+might be absent on the target system. Even worse, DEC UNIX uses
+facility number 10 (which is authpriv in this implementation) to
+log events for their AdvFS file system.
+.Sh SEE ALSO
+.Xr syslog 3 ,
+.Xr syslogd 8
diff --git a/usr.sbin/syslogd/syslogd.8 b/usr.sbin/syslogd/syslogd.8
new file mode 100644
index 0000000..f01ed97
--- /dev/null
+++ b/usr.sbin/syslogd/syslogd.8
@@ -0,0 +1,218 @@
+.\" Copyright (c) 1983, 1986, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)syslogd.8 8.1 (Berkeley) 6/6/93
+.\" $Id: syslogd.8,v 1.12 1997/09/19 22:22:03 brian Exp $
+.\"
+.Dd October 12, 1995
+.Dt SYSLOGD 8
+.Os BSD 4.2
+.Sh NAME
+.Nm syslogd
+.Nd log systems messages
+.Sh SYNOPSIS
+.Nm
+.Op Fl ds
+.Op Fl a Ar allowed_peer
+.Op Fl f Ar config_file
+.Op Fl m Ar mark_interval
+.Op Fl p Ar log_socket
+.Sh DESCRIPTION
+The
+.Nm
+daemon reads and logs messages to the system console, log files, other
+machines and/or users as specified by its configuration file.
+The options are as follows:
+.Bl -tag -width indent
+.It Fl a Ar allowed_peer
+Allow
+.Ar allowed_peer
+to log to this
+.Nm
+using UDP datagrams. Multiple
+.Fl a
+options may be specified.
+.Pp
+.Ar Allowed_peer
+can be any of the following:
+.Bl -tag -width "ipaddr/masklen[:service]XX"
+.It Ar ipaddr/masklen Ns Op Ar :service
+Accept datagrams from
+.Ar ipaddr
+(in the usual dotted quad notation) with
+.Ar masklen
+bits being taken into account when doing the address comparision. If
+specified,
+.Ar service
+is the name or number of an UDP service (see
+.Xr services 5 ) Ns
+the source packet must belong to. A
+.Ar service
+of
+.Ql \&*
+allows packets being sent from any UDP port. The default
+.Ar service
+is
+.Ql syslog .
+A missing
+.Ar masklen
+will be substituted by the historic class A or class B netmasks if
+.Ar ipaddr
+belongs into the address range of class A or B, respectively, or
+by 24 otherwise.
+.It Ar domainname Ns Op Ar :service
+Accept datagrams where the reverse address lookup yields
+.Ar domainname
+for the sender address. The meaning of
+.Ar service
+is as explained above.
+.It Ar *domainname Ns Op Ar :service
+Same as before, except that any source host whose name
+.Em ends
+in
+.Ar domainname
+will get permission.
+.El
+.It Fl d
+Put
+.Nm
+into debugging mode. This is probably only of use to developers working on
+.Nm Ns .
+.It Fl f
+Specify the pathname of an alternate configuration file;
+the default is
+.Pa /etc/syslog.conf .
+.It Fl m
+Select the number of minutes between
+.Dq mark
+messages; the default is 20 minutes.
+.It Fl p
+Specify the pathname of an alternate log socket;
+the default is
+.Pa /var/run/log .
+.It Fl s
+Operate in secure mode. Do not listen for log message from remote machines.
+.El
+.Pp
+The
+.Nm
+daemon reads its configuration file when it starts up and whenever it
+receives a hangup signal.
+For information on the format of the configuration file,
+see
+.Xr syslog.conf 5 .
+.Pp
+The
+.Nm
+daemon reads messages from the
+.Tn UNIX
+domain socket
+.Pa /var/run/log ,
+from an Internet domain socket specified in
+.Pa /etc/services ,
+and from the special device
+.Pa /dev/klog
+(to read kernel messages).
+.Pp
+The
+.Nm
+daemon creates the file
+.Pa /var/run/syslog.pid ,
+and stores its process
+id there.
+This can be used to kill or reconfigure
+.Nm Ns .
+.Pp
+The message sent to
+.Nm
+should consist of a single line.
+The message can contain a priority code, which should be a preceding
+decimal number in angle braces, for example,
+.Sq Aq 5.
+This priority code should map into the priorities defined in the
+include file
+.Aq Pa sys/syslog.h .
+.Sh FILES
+.Bl -tag -width /var/run/syslog.pid -compact
+.It Pa /etc/syslog.conf
+configuration file
+.It Pa /var/run/syslog.pid
+process id of current
+.Nm
+.It Pa /var/run/log
+name of the
+.Tn UNIX
+domain datagram log socket
+.It Pa /dev/klog
+kernel log device
+.El
+.Sh SEE ALSO
+.Xr logger 1 ,
+.Xr syslog 3 ,
+.Xr services 5 ,
+.Xr syslog.conf 5
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 4.3 .
+.Pp
+The
+.Fl s
+and
+.Fl a
+options are
+.Fx 2.2
+extensions.
+.Sh BUGS
+The ability to log messages received in UDP packets is equivalent to
+an unauthenticated remote disk-filling service, and should probably be
+disabled by default. Some sort of
+.No inter- Ns Nm syslogd
+authentication mechanism ought to be worked out. To prevent the worst
+abuse, use of the
+.Fl a
+option is therefore highly recommended.
+.Pp
+The
+.Fl a
+matching algorithm doesn't pretend to be very efficient; use of numeric
+IP addresses is faster than domain name comparision. Since the allowed
+peer list is being walked linearly, peer groups where frequent messages
+are being anticipated from should be put early into the
+.Fl a
+list.
+.Pp
+The log socket was moved from
+.Pa /dev
+to ease the use of a read-only root filesystem. This may confuse
+some old binaries so that a symbolic link might be used for a
+transitional period.
diff --git a/usr.sbin/syslogd/syslogd.c b/usr.sbin/syslogd/syslogd.c
new file mode 100644
index 0000000..fc871d5
--- /dev/null
+++ b/usr.sbin/syslogd/syslogd.c
@@ -0,0 +1,1749 @@
+/*
+ * Copyright (c) 1983, 1988, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 1983, 1988, 1993, 1994\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)syslogd.c 8.3 (Berkeley) 4/4/94";
+#endif
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+/*
+ * syslogd -- log system messages
+ *
+ * This program implements a system log. It takes a series of lines.
+ * Each line may have a priority, signified as "<n>" as
+ * the first characters of the line. If this is
+ * not present, a default priority is used.
+ *
+ * To kill syslogd, send a signal 15 (terminate). A signal 1 (hup) will
+ * cause it to reread its configuration file.
+ *
+ * Defined Constants:
+ *
+ * MAXLINE -- the maximimum line length that can be handled.
+ * DEFUPRI -- the default priority for user messages
+ * DEFSPRI -- the default priority for kernel messages
+ *
+ * Author: Eric Allman
+ * extensive changes by Ralph Campbell
+ * more extensive changes by Eric Allman (again)
+ * Extension to log by program name as well as facility and priority
+ * by Peter da Silva.
+ */
+
+#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/msgbuf.h>
+#include <sys/queue.h>
+#include <sys/uio.h>
+#include <sys/un.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/syslimits.h>
+#include <paths.h>
+
+#include <netinet/in.h>
+#include <netdb.h>
+#include <arpa/inet.h>
+
+#include <ctype.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <regex.h>
+#include <setjmp.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <unistd.h>
+#include <utmp.h>
+#include "pathnames.h"
+
+#define SYSLOG_NAMES
+#include <sys/syslog.h>
+
+const char *LogName = _PATH_LOG;
+const char *ConfFile = _PATH_LOGCONF;
+const char *PidFile = _PATH_LOGPID;
+const char ctty[] = _PATH_CONSOLE;
+
+#define FDMASK(fd) (1 << (fd))
+
+#define dprintf if (Debug) printf
+
+#define MAXUNAMES 20 /* maximum number of user names */
+
+/*
+ * 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 */
+
+/*
+ * This structure represents the files that will have log
+ * copies printed.
+ */
+
+struct filed {
+ struct filed *f_next; /* next in linked list */
+ short f_type; /* entry type, see below */
+ short f_file; /* file descriptor */
+ time_t f_time; /* time this was last written */
+ u_char f_pmask[LOG_NFACILITIES+1]; /* priority mask */
+ char *f_program; /* program this applies to */
+ union {
+ char f_uname[MAXUNAMES][UT_NAMESIZE+1];
+ struct {
+ char f_hname[MAXHOSTNAMELEN+1];
+ struct sockaddr_in f_addr;
+ } f_forw; /* forwarding address */
+ char f_fname[MAXPATHLEN];
+ struct {
+ char f_pname[MAXPATHLEN];
+ pid_t f_pid;
+ } f_pipe;
+ } f_un;
+ char f_prevline[MAXSVLINE]; /* last message logged */
+ char f_lasttime[16]; /* time of last occurrence */
+ char f_prevhost[MAXHOSTNAMELEN+1]; /* host from which recd. */
+ int f_prevpri; /* pri of f_prevline */
+ int f_prevlen; /* length of f_prevline */
+ int f_prevcount; /* repetition cnt of prevline */
+ int f_repeatcount; /* number of "repeated" msgs */
+};
+
+/*
+ * Queue of about-to-be dead processes we should watch out for.
+ */
+
+TAILQ_HEAD(stailhead, deadq_entry) deadq_head;
+struct stailhead *deadq_headp;
+
+struct deadq_entry {
+ pid_t dq_pid;
+ int dq_timeout;
+ TAILQ_ENTRY(deadq_entry) dq_entries;
+};
+
+/*
+ * The timeout to apply to processes waiting on the dead queue. Unit
+ * of measure is `mark intervals', i.e. 20 minutes by default.
+ * Processes on the dead queue will be terminated after that time.
+ */
+
+#define DQ_TIMO_INIT 2
+
+typedef struct deadq_entry *dq_t;
+
+
+/*
+ * Struct to hold records of network addresses that are allowed to log
+ * to us.
+ */
+struct allowedpeer {
+ int isnumeric;
+ u_short port;
+ union {
+ struct {
+ struct in_addr addr;
+ struct in_addr mask;
+ } numeric;
+ char *name;
+ } u;
+#define a_addr u.numeric.addr
+#define a_mask u.numeric.mask
+#define a_name u.name
+};
+
+
+/*
+ * Intervals at which we flush out "message repeated" messages,
+ * in seconds after previous message is logged. After each flush,
+ * we move to the next interval until we reach the largest.
+ */
+int repeatinterval[] = { 30, 120, 600 }; /* # of secs before flush */
+#define MAXREPEAT ((sizeof(repeatinterval) / sizeof(repeatinterval[0])) - 1)
+#define REPEATTIME(f) ((f)->f_time + repeatinterval[(f)->f_repeatcount])
+#define BACKOFF(f) { if (++(f)->f_repeatcount > MAXREPEAT) \
+ (f)->f_repeatcount = MAXREPEAT; \
+ }
+
+/* values for f_type */
+#define F_UNUSED 0 /* unused entry */
+#define F_FILE 1 /* regular file */
+#define F_TTY 2 /* terminal */
+#define F_CONSOLE 3 /* console terminal */
+#define F_FORW 4 /* remote machine */
+#define F_USERS 5 /* list of users */
+#define F_WALL 6 /* everyone logged on */
+#define F_PIPE 7 /* pipe to program */
+
+char *TypeNames[8] = {
+ "UNUSED", "FILE", "TTY", "CONSOLE",
+ "FORW", "USERS", "WALL", "PIPE"
+};
+
+struct filed *Files;
+struct filed consfile;
+
+int Debug; /* debug flag */
+char LocalHostName[MAXHOSTNAMELEN+1]; /* our hostname */
+char *LocalDomain; /* our local domain name */
+int finet; /* Internet datagram socket */
+int LogPort; /* port number for INET connections */
+int Initialized = 0; /* set when we have initialized ourselves */
+int MarkInterval = 20 * 60; /* interval between marks in seconds */
+int MarkSeq = 0; /* mark sequence number */
+int SecureMode = 0; /* when true, speak only unix domain socks */
+
+int created_lsock = 0; /* Flag if local socket created */
+char bootfile[MAXLINE+1]; /* booted kernel file */
+
+struct allowedpeer *AllowedPeers;
+int NumAllowed = 0; /* # of AllowedPeer entries */
+
+int allowaddr __P((char *));
+void cfline __P((char *, struct filed *, char *));
+char *cvthname __P((struct sockaddr_in *));
+void deadq_enter __P((pid_t));
+int decode __P((const char *, CODE *));
+void die __P((int));
+void domark __P((int));
+void fprintlog __P((struct filed *, int, char *));
+void init __P((int));
+void logerror __P((const char *));
+void logmsg __P((int, char *, char *, int));
+void printline __P((char *, char *));
+void printsys __P((char *));
+int p_open __P((char *, pid_t *));
+void reapchild __P((int));
+char *ttymsg __P((struct iovec *, int, char *, int));
+static void usage __P((void));
+int validate __P((struct sockaddr_in *, const char *));
+void wallmsg __P((struct filed *, struct iovec *));
+int waitdaemon __P((int, int, int));
+void timedout __P((int));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int ch, funix, i, inetm, fklog, klogm, len;
+ struct sockaddr_un sunx, fromunix;
+ struct sockaddr_in sin, frominet;
+ FILE *fp;
+ char *p, *hname, line[MSG_BSIZE + 1];
+ struct timeval tv, *tvp;
+ pid_t ppid;
+
+ while ((ch = getopt(argc, argv, "a:dsf:m:p:")) != -1)
+ switch(ch) {
+ case 'd': /* debug */
+ Debug++;
+ break;
+ case 'a': /* allow specific network addresses only */
+ if (allowaddr(optarg) == -1)
+ usage();
+ break;
+ case 'f': /* configuration file */
+ ConfFile = optarg;
+ break;
+ case 'm': /* mark interval */
+ MarkInterval = atoi(optarg) * 60;
+ break;
+ case 'p': /* path */
+ LogName = optarg;
+ break;
+ case 's': /* no network mode */
+ SecureMode++;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ if ((argc -= optind) != 0)
+ usage();
+
+ if (!Debug) {
+ ppid = waitdaemon(0, 0, 30);
+ if (ppid < 0)
+ err(1, "could not become daemon");
+ } else
+ setlinebuf(stdout);
+
+ if (NumAllowed)
+ endservent();
+
+ consfile.f_type = F_CONSOLE;
+ (void)strcpy(consfile.f_un.f_fname, ctty + sizeof _PATH_DEV - 1);
+ (void)gethostname(LocalHostName, sizeof(LocalHostName));
+ if ((p = strchr(LocalHostName, '.')) != NULL) {
+ *p++ = '\0';
+ LocalDomain = p;
+ } else
+ LocalDomain = "";
+ (void)strcpy(bootfile, getbootfile());
+ (void)signal(SIGTERM, die);
+ (void)signal(SIGINT, Debug ? die : SIG_IGN);
+ (void)signal(SIGQUIT, Debug ? die : SIG_IGN);
+ (void)signal(SIGCHLD, reapchild);
+ (void)signal(SIGALRM, domark);
+ (void)signal(SIGPIPE, SIG_IGN); /* We'll catch EPIPE instead. */
+ (void)alarm(TIMERINTVL);
+
+ TAILQ_INIT(&deadq_head);
+
+#ifndef SUN_LEN
+#define SUN_LEN(unp) (strlen((unp)->sun_path) + 2)
+#endif
+ memset(&sunx, 0, sizeof(sunx));
+ sunx.sun_family = AF_UNIX;
+ (void)strncpy(sunx.sun_path, LogName, sizeof(sunx.sun_path));
+ (void)unlink(LogName);
+ funix = socket(AF_UNIX, SOCK_DGRAM, 0);
+ if (funix < 0 ||
+ bind(funix, (struct sockaddr *)&sunx, SUN_LEN(&sunx)) < 0 ||
+ chmod(LogName, 0666) < 0) {
+ (void) snprintf(line, sizeof line, "cannot create %s", LogName);
+ logerror(line);
+ dprintf("cannot create %s (%d)\n", LogName, errno);
+ die(0);
+ } else
+ created_lsock = 1;
+
+ inetm = 0;
+ finet = socket(AF_INET, SOCK_DGRAM, 0);
+ if (finet >= 0) {
+ struct servent *sp;
+
+ sp = getservbyname("syslog", "udp");
+ if (sp == NULL) {
+ errno = 0;
+ logerror("syslog/udp: unknown service");
+ die(0);
+ }
+ memset(&sin, 0, sizeof(sin));
+ sin.sin_family = AF_INET;
+ sin.sin_port = LogPort = sp->s_port;
+
+ if (!SecureMode) {
+ if (bind(finet, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
+ logerror("bind");
+ if (!Debug)
+ die(0);
+ } else {
+ inetm = FDMASK(finet);
+ }
+ }
+ }
+ if ((fklog = open(_PATH_KLOG, O_RDONLY, 0)) >= 0)
+ klogm = FDMASK(fklog);
+ else {
+ dprintf("can't open %s (%d)\n", _PATH_KLOG, errno);
+ klogm = 0;
+ }
+
+ /* tuck my process id away */
+ fp = fopen(PidFile, "w");
+ if (fp != NULL) {
+ fprintf(fp, "%d\n", getpid());
+ (void) fclose(fp);
+ }
+
+ dprintf("off & running....\n");
+
+ init(0);
+ (void)signal(SIGHUP, init);
+
+ tvp = &tv;
+ tv.tv_sec = tv.tv_usec = 0;
+
+ for (;;) {
+ int nfds, readfds = FDMASK(funix) | inetm | klogm;
+
+ dprintf("readfds = %#x\n", readfds);
+ nfds = select(20, (fd_set *)&readfds, (fd_set *)NULL,
+ (fd_set *)NULL, tvp);
+ if (nfds == 0) {
+ if (tvp) {
+ tvp = NULL;
+ if (ppid != 1)
+ kill(ppid, SIGALRM);
+ }
+ continue;
+ }
+ if (nfds < 0) {
+ if (errno != EINTR)
+ logerror("select");
+ continue;
+ }
+ dprintf("got a message (%d, %#x)\n", nfds, readfds);
+ if (readfds & klogm) {
+ i = read(fklog, line, sizeof(line) - 1);
+ if (i > 0) {
+ line[i] = '\0';
+ printsys(line);
+ } else if (i < 0 && errno != EINTR) {
+ logerror("klog");
+ fklog = -1;
+ klogm = 0;
+ }
+ }
+ if (readfds & FDMASK(funix)) {
+ len = sizeof(fromunix);
+ i = recvfrom(funix, line, MAXLINE, 0,
+ (struct sockaddr *)&fromunix, &len);
+ if (i > 0) {
+ line[i] = '\0';
+ printline(LocalHostName, line);
+ } else if (i < 0 && errno != EINTR)
+ logerror("recvfrom unix");
+ }
+ if (readfds & inetm) {
+ len = sizeof(frominet);
+ i = recvfrom(finet, line, MAXLINE, 0,
+ (struct sockaddr *)&frominet, &len);
+ if (i > 0) {
+ line[i] = '\0';
+ hname = cvthname(&frominet);
+ if (validate(&frominet, hname))
+ printline(hname, line);
+ } else if (i < 0 && errno != EINTR)
+ logerror("recvfrom inet");
+ }
+ }
+}
+
+static void
+usage()
+{
+
+ fprintf(stderr, "%s\n%s\n",
+ "usage: syslogd [-ds] [-a allowed_peer] [-f config_file]",
+ " [-m mark_interval] [-p log_socket]");
+ exit(1);
+}
+
+/*
+ * Take a raw input line, decode the message, and print the message
+ * on the appropriate log files.
+ */
+void
+printline(hname, msg)
+ char *hname;
+ char *msg;
+{
+ int c, pri;
+ char *p, *q, line[MAXLINE + 1];
+
+ /* test for special codes */
+ pri = DEFUPRI;
+ p = msg;
+ if (*p == '<') {
+ pri = 0;
+ while (isdigit(*++p))
+ pri = 10 * pri + (*p - '0');
+ if (*p == '>')
+ ++p;
+ }
+ if (pri &~ (LOG_FACMASK|LOG_PRIMASK))
+ pri = DEFUPRI;
+
+ /* don't allow users to log kernel messages */
+ if (LOG_FAC(pri) == LOG_KERN)
+ pri = LOG_MAKEPRI(LOG_USER, LOG_PRI(pri));
+
+ q = line;
+
+ while ((c = *p++ & 0177) != '\0' &&
+ q < &line[sizeof(line) - 1])
+ if (iscntrl(c))
+ if (c == '\n')
+ *q++ = ' ';
+ else if (c == '\t')
+ *q++ = '\t';
+ else {
+ *q++ = '^';
+ *q++ = c ^ 0100;
+ }
+ else
+ *q++ = c;
+ *q = '\0';
+
+ logmsg(pri, line, hname, 0);
+}
+
+/*
+ * Take a raw input line from /dev/klog, split and format similar to syslog().
+ */
+void
+printsys(msg)
+ char *msg;
+{
+ int c, pri, flags;
+ char *lp, *p, *q, line[MAXLINE + 1];
+
+ (void)strcpy(line, bootfile);
+ (void)strcat(line, ": ");
+ lp = line + strlen(line);
+ for (p = msg; *p != '\0'; ) {
+ flags = SYNC_FILE | ADDDATE; /* fsync file after write */
+ pri = DEFSPRI;
+ if (*p == '<') {
+ pri = 0;
+ while (isdigit(*++p))
+ pri = 10 * pri + (*p - '0');
+ if (*p == '>')
+ ++p;
+ } else {
+ /* kernel printf's come out on console */
+ flags |= IGN_CONS;
+ }
+ if (pri &~ (LOG_FACMASK|LOG_PRIMASK))
+ pri = DEFSPRI;
+ q = lp;
+ while (*p != '\0' && (c = *p++) != '\n' &&
+ q < &line[MAXLINE])
+ *q++ = c;
+ *q = '\0';
+ logmsg(pri, line, LocalHostName, flags);
+ }
+}
+
+time_t now;
+
+/*
+ * Log a message to the appropriate log files, users, etc. based on
+ * the priority.
+ */
+void
+logmsg(pri, msg, from, flags)
+ int pri;
+ char *msg, *from;
+ int flags;
+{
+ struct filed *f;
+ int fac, msglen, omask, prilev;
+ char *timestamp;
+ char prog[NAME_MAX+1];
+ int i;
+
+ 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;
+
+ /* 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_pmask[fac] < prilev ||
+ f->f_pmask[fac] == INTERNAL_NOPRI)
+ continue;
+ /* skip messages with the incorrect program name */
+ if(f->f_program)
+ if(strcmp(prog, f->f_program) != 0)
+ continue;
+
+ if (f->f_type == F_CONSOLE && (flags & IGN_CONS))
+ continue;
+
+ /* don't output marks to recently written files */
+ if ((flags & MARK) && (now - f->f_time) < MarkInterval / 2)
+ continue;
+
+ /*
+ * suppress duplicate lines to this file
+ */
+ if ((flags & MARK) == 0 && msglen == f->f_prevlen &&
+ !strcmp(msg, f->f_prevline) &&
+ !strcmp(from, f->f_prevhost)) {
+ (void)strncpy(f->f_lasttime, timestamp, 15);
+ f->f_prevcount++;
+ dprintf("msg repeated %d times, %ld sec of %d\n",
+ f->f_prevcount, now - f->f_time,
+ repeatinterval[f->f_repeatcount]);
+ /*
+ * If domark would have logged this by now,
+ * flush it now (so we don't hold isolated messages),
+ * but back off so we'll flush less often
+ * in the future.
+ */
+ if (now > REPEATTIME(f)) {
+ fprintlog(f, flags, (char *)NULL);
+ BACKOFF(f);
+ }
+ } else {
+ /* new line, save it */
+ if (f->f_prevcount)
+ fprintlog(f, 0, (char *)NULL);
+ f->f_repeatcount = 0;
+ f->f_prevpri = pri;
+ (void)strncpy(f->f_lasttime, timestamp, 15);
+ (void)strncpy(f->f_prevhost, from,
+ sizeof(f->f_prevhost));
+ if (msglen < MAXSVLINE) {
+ f->f_prevlen = msglen;
+ (void)strcpy(f->f_prevline, msg);
+ fprintlog(f, flags, (char *)NULL);
+ } else {
+ f->f_prevline[0] = 0;
+ f->f_prevlen = 0;
+ fprintlog(f, flags, msg);
+ }
+ }
+ }
+ (void)sigsetmask(omask);
+}
+
+void
+fprintlog(f, flags, msg)
+ struct filed *f;
+ int flags;
+ char *msg;
+{
+ struct iovec iov[6];
+ struct iovec *v;
+ int l;
+ char line[MAXLINE + 1], repbuf[80], greetings[200];
+ char *msgret;
+
+ v = iov;
+ if (f->f_type == F_WALL) {
+ v->iov_base = greetings;
+ v->iov_len = snprintf(greetings, sizeof greetings,
+ "\r\n\7Message from syslogd@%s at %.24s ...\r\n",
+ f->f_prevhost, ctime(&now));
+ v++;
+ v->iov_base = "";
+ v->iov_len = 0;
+ v++;
+ } else {
+ v->iov_base = f->f_lasttime;
+ v->iov_len = 15;
+ v++;
+ v->iov_base = " ";
+ v->iov_len = 1;
+ v++;
+ }
+ v->iov_base = f->f_prevhost;
+ v->iov_len = strlen(v->iov_base);
+ v++;
+ v->iov_base = " ";
+ v->iov_len = 1;
+ v++;
+
+ if (msg) {
+ v->iov_base = msg;
+ v->iov_len = strlen(msg);
+ } else if (f->f_prevcount > 1) {
+ v->iov_base = repbuf;
+ v->iov_len = sprintf(repbuf, "last message repeated %d times",
+ f->f_prevcount);
+ } else {
+ v->iov_base = f->f_prevline;
+ v->iov_len = f->f_prevlen;
+ }
+ v++;
+
+ dprintf("Logging to %s", TypeNames[f->f_type]);
+ f->f_time = now;
+
+ switch (f->f_type) {
+ case F_UNUSED:
+ dprintf("\n");
+ break;
+
+ case F_FORW:
+ dprintf(" %s\n", f->f_un.f_forw.f_hname);
+ l = snprintf(line, sizeof line - 1, "<%d>%.15s %s",
+ f->f_prevpri, iov[0].iov_base, iov[4].iov_base);
+ if (l > MAXLINE)
+ l = MAXLINE;
+ if ((finet >= 0) &&
+ (sendto(finet, line, l, 0,
+ (struct sockaddr *)&f->f_un.f_forw.f_addr,
+ sizeof(f->f_un.f_forw.f_addr)) != l)) {
+ int e = errno;
+ (void)close(f->f_file);
+ f->f_type = F_UNUSED;
+ errno = e;
+ logerror("sendto");
+ }
+ break;
+
+ case F_FILE:
+ dprintf(" %s\n", f->f_un.f_fname);
+ v->iov_base = "\n";
+ v->iov_len = 1;
+ if (writev(f->f_file, iov, 6) < 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, 6) < 0) {
+ int e = errno;
+ (void)close(f->f_file);
+ if (f->f_un.f_pipe.f_pid > 0)
+ deadq_enter(f->f_un.f_pipe.f_pid);
+ f->f_un.f_pipe.f_pid = 0;
+ errno = e;
+ logerror(f->f_un.f_pipe.f_pname);
+ }
+ break;
+
+ case F_CONSOLE:
+ if (flags & IGN_CONS) {
+ dprintf(" (ignored)\n");
+ break;
+ }
+ /* FALLTHROUGH */
+
+ case F_TTY:
+ dprintf(" %s%s\n", _PATH_DEV, f->f_un.f_fname);
+ v->iov_base = "\r\n";
+ v->iov_len = 2;
+
+ errno = 0; /* ttymsg() only sometimes returns an errno */
+ if ((msgret = ttymsg(iov, 6, f->f_un.f_fname, 10))) {
+ f->f_type = F_UNUSED;
+ logerror(msgret);
+ }
+ break;
+
+ case F_USERS:
+ case F_WALL:
+ dprintf("\n");
+ v->iov_base = "\r\n";
+ v->iov_len = 2;
+ wallmsg(f, iov);
+ break;
+ }
+ f->f_prevcount = 0;
+}
+
+/*
+ * WALLMSG -- Write a message to the world at large
+ *
+ * Write the specified message to either the entire
+ * world, or a list of approved users.
+ */
+void
+wallmsg(f, iov)
+ struct filed *f;
+ struct iovec *iov;
+{
+ static int reenter; /* avoid calling ourselves */
+ FILE *uf;
+ struct utmp ut;
+ int i;
+ char *p;
+ char line[sizeof(ut.ut_line) + 1];
+
+ if (reenter++)
+ return;
+ if ((uf = fopen(_PATH_UTMP, "r")) == NULL) {
+ logerror(_PATH_UTMP);
+ reenter = 0;
+ return;
+ }
+ /* NOSTRICT */
+ while (fread((char *)&ut, sizeof(ut), 1, uf) == 1) {
+ if (ut.ut_name[0] == '\0')
+ continue;
+ strncpy(line, ut.ut_line, sizeof(ut.ut_line));
+ line[sizeof(ut.ut_line)] = '\0';
+ if (f->f_type == F_WALL) {
+ if ((p = ttymsg(iov, 6, 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, 6, line, TTYMSGTIME))
+ != NULL) {
+ errno = 0; /* already in msg */
+ logerror(p);
+ }
+ break;
+ }
+ }
+ }
+ (void)fclose(uf);
+ reenter = 0;
+}
+
+void
+reapchild(signo)
+ int signo;
+{
+ int status, code;
+ pid_t pid;
+ struct filed *f;
+ char buf[256];
+ const char *reason;
+ dq_t q;
+
+ while ((pid = wait3(&status, WNOHANG, (struct rusage *)NULL)) > 0) {
+ if (!Initialized)
+ /* Don't tell while we are initting. */
+ continue;
+
+ /* First, look if it's a process from the dead queue. */
+ for (q = TAILQ_FIRST(&deadq_head); q != NULL; q = TAILQ_NEXT(q, dq_entries))
+ if (q->dq_pid == pid) {
+ TAILQ_REMOVE(&deadq_head, q, dq_entries);
+ free(q);
+ goto oncemore;
+ }
+
+ /* Now, look in list of active processes. */
+ for (f = Files; f; f = f->f_next)
+ if (f->f_type == F_PIPE &&
+ f->f_un.f_pipe.f_pid == pid) {
+ (void)close(f->f_file);
+
+ errno = 0; /* Keep strerror() stuff out of logerror messages. */
+ f->f_un.f_pipe.f_pid = 0;
+ if (WIFSIGNALED(status)) {
+ reason = "due to signal";
+ code = WTERMSIG(status);
+ } else {
+ reason = "with status";
+ code = WEXITSTATUS(status);
+ if (code == 0)
+ goto oncemore; /* Exited OK. */
+ }
+ (void)snprintf(buf, sizeof buf,
+ "Logging subprocess %d (%s) exited %s %d.",
+ pid, f->f_un.f_pipe.f_pname,
+ reason, code);
+ logerror(buf);
+ break;
+ }
+ oncemore:
+ }
+}
+
+/*
+ * Return a printable representation of a host address.
+ */
+char *
+cvthname(f)
+ struct sockaddr_in *f;
+{
+ struct hostent *hp;
+ char *p;
+
+ dprintf("cvthname(%s)\n", inet_ntoa(f->sin_addr));
+
+ if (f->sin_family != AF_INET) {
+ dprintf("Malformed from address\n");
+ return ("???");
+ }
+ hp = gethostbyaddr((char *)&f->sin_addr,
+ sizeof(struct in_addr), f->sin_family);
+ if (hp == 0) {
+ dprintf("Host name for your address (%s) unknown\n",
+ inet_ntoa(f->sin_addr));
+ return (inet_ntoa(f->sin_addr));
+ }
+ if ((p = strchr(hp->h_name, '.')) && strcmp(p + 1, LocalDomain) == 0)
+ *p = '\0';
+ return (hp->h_name);
+}
+
+void
+domark(signo)
+ int signo;
+{
+ struct filed *f;
+ dq_t q;
+
+ now = time((time_t *)NULL);
+ MarkSeq += TIMERINTVL;
+ if (MarkSeq >= MarkInterval) {
+ logmsg(LOG_INFO, "-- MARK --", LocalHostName, ADDDATE|MARK);
+ MarkSeq = 0;
+ }
+
+ for (f = Files; f; f = f->f_next) {
+ if (f->f_prevcount && now >= REPEATTIME(f)) {
+ dprintf("flush %s: repeated %d times, %d sec.\n",
+ TypeNames[f->f_type], f->f_prevcount,
+ repeatinterval[f->f_repeatcount]);
+ fprintlog(f, 0, (char *)NULL);
+ BACKOFF(f);
+ }
+ }
+
+ /* Walk the dead queue, and see if we should signal somebody. */
+ for (q = TAILQ_FIRST(&deadq_head); q != NULL; q = TAILQ_NEXT(q, dq_entries))
+ switch (q->dq_timeout) {
+ case 0:
+ /* Already signalled once, try harder now. */
+ kill(q->dq_pid, SIGKILL);
+ break;
+
+ case 1:
+ /*
+ * Timed out on dead queue, send terminate
+ * signal. Note that we leave the removal
+ * from the dead queue to reapchild(), which
+ * will also log the event.
+ */
+ kill(q->dq_pid, SIGTERM);
+ /* FALLTROUGH */
+
+ default:
+ q->dq_timeout--;
+ }
+
+ (void)alarm(TIMERINTVL);
+}
+
+/*
+ * Print syslogd errors some place.
+ */
+void
+logerror(type)
+ const char *type;
+{
+ char buf[512];
+
+ if (errno)
+ (void)snprintf(buf,
+ sizeof buf, "syslogd: %s: %s", type, strerror(errno));
+ else
+ (void)snprintf(buf, sizeof buf, "syslogd: %s", type);
+ errno = 0;
+ dprintf("%s\n", buf);
+ logmsg(LOG_SYSLOG|LOG_ERR, buf, LocalHostName, ADDDATE);
+}
+
+void
+die(signo)
+ int signo;
+{
+ struct filed *f;
+ int was_initialized;
+ char buf[100];
+
+ was_initialized = Initialized;
+ Initialized = 0; /* Don't log SIGCHLDs. */
+ for (f = Files; f != NULL; f = f->f_next) {
+ /* flush any pending output */
+ if (f->f_prevcount)
+ fprintlog(f, 0, (char *)NULL);
+ if (f->f_type == F_PIPE)
+ (void)close(f->f_file);
+ }
+ Initialized = was_initialized;
+ if (signo) {
+ dprintf("syslogd: exiting on signal %d\n", signo);
+ (void)sprintf(buf, "exiting on signal %d", signo);
+ errno = 0;
+ logerror(buf);
+ }
+ if (created_lsock)
+ (void)unlink(LogName);
+ exit(1);
+}
+
+/*
+ * INIT -- Initialize syslogd from configuration table
+ */
+void
+init(signo)
+ int signo;
+{
+ int i;
+ FILE *cf;
+ struct filed *f, *next, **nextp;
+ char *p;
+ char cline[LINE_MAX];
+ char prog[NAME_MAX+1];
+
+ dprintf("init\n");
+
+ /*
+ * Close all open log files.
+ */
+ Initialized = 0;
+ for (f = Files; f != NULL; f = next) {
+ /* flush any pending output */
+ if (f->f_prevcount)
+ fprintlog(f, 0, (char *)NULL);
+
+ switch (f->f_type) {
+ case F_FILE:
+ case F_FORW:
+ case F_CONSOLE:
+ case F_TTY:
+ (void)close(f->f_file);
+ break;
+ case F_PIPE:
+ (void)close(f->f_file);
+ if (f->f_un.f_pipe.f_pid > 0)
+ deadq_enter(f->f_un.f_pipe.f_pid);
+ f->f_un.f_pipe.f_pid = 0;
+ break;
+ }
+ next = f->f_next;
+ if(f->f_program) free(f->f_program);
+ free((char *)f);
+ }
+ Files = NULL;
+ nextp = &Files;
+
+ /* open the configuration file */
+ if ((cf = fopen(ConfFile, "r")) == NULL) {
+ dprintf("cannot open %s\n", ConfFile);
+ *nextp = (struct filed *)calloc(1, sizeof(*f));
+ cfline("*.ERR\t/dev/console", *nextp, "*");
+ (*nextp)->f_next = (struct filed *)calloc(1, sizeof(*f));
+ cfline("*.PANIC\t*", (*nextp)->f_next, "*");
+ Initialized = 1;
+ return;
+ }
+
+ /*
+ * Foreach line in the conf table, open that file.
+ */
+ f = NULL;
+ strcpy(prog, "*");
+ while (fgets(cline, sizeof(cline), cf) != NULL) {
+ /*
+ * check for end-of-section, comments, strip off trailing
+ * spaces and newline character. #!prog is treated specially:
+ * following lines apply only to that program.
+ */
+ for (p = cline; isspace(*p); ++p)
+ continue;
+ if (*p == 0)
+ continue;
+ if(*p == '#') {
+ p++;
+ if(*p!='!')
+ continue;
+ }
+ if(*p=='!') {
+ p++;
+ while(isspace(*p)) p++;
+ if(!*p) {
+ strcpy(prog, "*");
+ continue;
+ }
+ for(i = 0; i < NAME_MAX; i++) {
+ if(!isalnum(p[i]))
+ break;
+ prog[i] = p[i];
+ }
+ prog[i] = 0;
+ continue;
+ }
+ for (p = strchr(cline, '\0'); isspace(*--p);)
+ continue;
+ *++p = '\0';
+ f = (struct filed *)calloc(1, sizeof(*f));
+ *nextp = f;
+ nextp = &f->f_next;
+ cfline(cline, f, prog);
+ }
+
+ /* close the configuration file */
+ (void)fclose(cf);
+
+ Initialized = 1;
+
+ if (Debug) {
+ for (f = Files; f; f = f->f_next) {
+ for (i = 0; i <= LOG_NFACILITIES; i++)
+ if (f->f_pmask[i] == INTERNAL_NOPRI)
+ printf("X ");
+ else
+ printf("%d ", f->f_pmask[i]);
+ printf("%s: ", TypeNames[f->f_type]);
+ switch (f->f_type) {
+ case F_FILE:
+ printf("%s", f->f_un.f_fname);
+ break;
+
+ case F_CONSOLE:
+ case F_TTY:
+ printf("%s%s", _PATH_DEV, f->f_un.f_fname);
+ break;
+
+ case F_FORW:
+ printf("%s", f->f_un.f_forw.f_hname);
+ break;
+
+ case F_PIPE:
+ printf("%s", f->f_un.f_pipe.f_pname);
+ break;
+
+ case F_USERS:
+ for (i = 0; i < MAXUNAMES && *f->f_un.f_uname[i]; i++)
+ printf("%s, ", f->f_un.f_uname[i]);
+ break;
+ }
+ if(f->f_program) {
+ printf(" (%s)", f->f_program);
+ }
+ printf("\n");
+ }
+ }
+
+ logmsg(LOG_SYSLOG|LOG_INFO, "syslogd: restart", LocalHostName, ADDDATE);
+ dprintf("syslogd: restarted\n");
+}
+
+/*
+ * Crack a configuration file line
+ */
+void
+cfline(line, f, prog)
+ char *line;
+ struct filed *f;
+ char *prog;
+{
+ struct hostent *hp;
+ int i, pri;
+ char *bp, *p, *q;
+ char buf[MAXLINE], ebuf[100];
+
+ dprintf("cfline(\"%s\", f, \"%s\")\n", line, prog);
+
+ errno = 0; /* keep strerror() stuff out of logerror messages */
+
+ /* clear out file entry */
+ memset(f, 0, sizeof(*f));
+ for (i = 0; i <= LOG_NFACILITIES; i++)
+ f->f_pmask[i] = INTERNAL_NOPRI;
+
+ /* save program name if any */
+ if(prog && *prog=='*') prog = NULL;
+ if(prog) {
+ f->f_program = calloc(1, strlen(prog)+1);
+ if(f->f_program) {
+ strcpy(f->f_program, prog);
+ }
+ }
+
+ /* scan through the list of selectors */
+ for (p = line; *p && *p != '\t';) {
+
+ /* find the end of this facility name list */
+ for (q = p; *q && *q != '\t' && *q++ != '.'; )
+ continue;
+
+ /* 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;
+ 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;
+ }
+ while (*p == ',' || *p == ' ')
+ p++;
+ }
+
+ p = q;
+ }
+
+ /* skip to action part */
+ while (*p == '\t')
+ p++;
+
+ switch (*p)
+ {
+ case '@':
+ (void)strcpy(f->f_un.f_forw.f_hname, ++p);
+ hp = gethostbyname(p);
+ if (hp == NULL) {
+ extern int h_errno;
+
+ logerror(hstrerror(h_errno));
+ break;
+ }
+ memset(&f->f_un.f_forw.f_addr, 0,
+ sizeof(f->f_un.f_forw.f_addr));
+ f->f_un.f_forw.f_addr.sin_family = AF_INET;
+ f->f_un.f_forw.f_addr.sin_port = LogPort;
+ memmove(&f->f_un.f_forw.f_addr.sin_addr, hp->h_addr, hp->h_length);
+ f->f_type = F_FORW;
+ break;
+
+ case '/':
+ if ((f->f_file = open(p, O_WRONLY|O_APPEND, 0)) < 0) {
+ f->f_type = F_UNUSED;
+ logerror(p);
+ break;
+ }
+ if (isatty(f->f_file)) {
+ if (strcmp(p, ctty) == 0)
+ f->f_type = F_CONSOLE;
+ else
+ f->f_type = F_TTY;
+ (void)strcpy(f->f_un.f_fname, p + sizeof _PATH_DEV - 1);
+ } else {
+ (void)strcpy(f->f_un.f_fname, p);
+ f->f_type = F_FILE;
+ }
+ break;
+
+ case '|':
+ f->f_un.f_pipe.f_pid = 0;
+ (void)strcpy(f->f_un.f_pipe.f_pname, p + 1);
+ f->f_type = F_PIPE;
+ break;
+
+ case '*':
+ f->f_type = F_WALL;
+ break;
+
+ default:
+ for (i = 0; i < MAXUNAMES && *p; i++) {
+ for (q = p; *q && *q != ','; )
+ q++;
+ (void)strncpy(f->f_un.f_uname[i], p, UT_NAMESIZE);
+ if ((q - p) > UT_NAMESIZE)
+ f->f_un.f_uname[i][UT_NAMESIZE] = '\0';
+ else
+ f->f_un.f_uname[i][q - p] = '\0';
+ while (*q == ',' || *q == ' ')
+ q++;
+ p = q;
+ }
+ f->f_type = F_USERS;
+ break;
+ }
+}
+
+
+/*
+ * Decode a symbolic name to a numeric value
+ */
+int
+decode(name, codetab)
+ const char *name;
+ CODE *codetab;
+{
+ CODE *c;
+ char *p, buf[40];
+
+ if (isdigit(*name))
+ return (atoi(name));
+
+ for (p = buf; *name && p < &buf[sizeof(buf) - 1]; p++, name++) {
+ if (isupper(*name))
+ *p = tolower(*name);
+ else
+ *p = *name;
+ }
+ *p = '\0';
+ for (c = codetab; c->c_name; c++)
+ if (!strcmp(buf, c->c_name))
+ return (c->c_val);
+
+ return (-1);
+}
+
+/*
+ * fork off and become a daemon, but wait for the child to come online
+ * before returing to the parent, or we get disk thrashing at boot etc.
+ * Set a timer so we don't hang forever if it wedges.
+ */
+int
+waitdaemon(nochdir, noclose, maxwait)
+ int nochdir, noclose, maxwait;
+{
+ int fd;
+ int status;
+ pid_t pid, childpid;
+
+ switch (childpid = fork()) {
+ case -1:
+ return (-1);
+ case 0:
+ break;
+ default:
+ signal(SIGALRM, timedout);
+ alarm(maxwait);
+ while ((pid = wait3(&status, 0, NULL)) != -1) {
+ if (WIFEXITED(status))
+ errx(1, "child pid %d exited with return code %d",
+ pid, WEXITSTATUS(status));
+ if (WIFSIGNALED(status))
+ errx(1, "child pid %d exited on signal %d%s",
+ pid, WTERMSIG(status),
+ WCOREDUMP(status) ? " (core dumped)" :
+ "");
+ if (pid == childpid) /* it's gone... */
+ break;
+ }
+ exit(0);
+ }
+
+ if (setsid() == -1)
+ return (-1);
+
+ if (!nochdir)
+ (void)chdir("/");
+
+ if (!noclose && (fd = open(_PATH_DEVNULL, O_RDWR, 0)) != -1) {
+ (void)dup2(fd, STDIN_FILENO);
+ (void)dup2(fd, STDOUT_FILENO);
+ (void)dup2(fd, STDERR_FILENO);
+ if (fd > 2)
+ (void)close (fd);
+ }
+ return (getppid());
+}
+
+/*
+ * We get a SIGALRM from the child when it's running and finished doing it's
+ * fsync()'s or O_SYNC writes for all the boot messages.
+ *
+ * We also get a signal from the kernel if the timer expires, so check to
+ * see what happened.
+ */
+void
+timedout(sig)
+ int sig __unused;
+{
+ int left;
+ left = alarm(0);
+ signal(SIGALRM, SIG_DFL);
+ if (left == 0)
+ errx(1, "timed out waiting for child");
+ else
+ exit(0);
+}
+
+/*
+ * Add `s' to the list of allowable peer addresses to accept messages
+ * from.
+ *
+ * `s' is a string in the form:
+ *
+ * [*]domainname[:{servicename|portnumber|*}]
+ *
+ * or
+ *
+ * netaddr/maskbits[:{servicename|portnumber|*}]
+ *
+ * Returns -1 on error, 0 if the argument was valid.
+ */
+int
+allowaddr(s)
+ char *s;
+{
+ char *cp1, *cp2;
+ struct allowedpeer ap;
+ struct servent *se;
+ regex_t re;
+ int i;
+
+ if ((cp1 = strrchr(s, ':'))) {
+ /* service/port provided */
+ *cp1++ = '\0';
+ if (strlen(cp1) == 1 && *cp1 == '*')
+ /* any port allowed */
+ ap.port = htons(0);
+ else if ((se = getservbyname(cp1, "udp")))
+ ap.port = se->s_port;
+ else {
+ ap.port = htons((int)strtol(cp1, &cp2, 0));
+ if (*cp2 != '\0')
+ return -1; /* port not numeric */
+ }
+ } else {
+ if ((se = getservbyname("syslog", "udp")))
+ ap.port = se->s_port;
+ else
+ /* sanity, should not happen */
+ ap.port = htons(514);
+ }
+
+ /* the regexp's are ugly, but the cleanest way */
+
+ if (regcomp(&re, "^[0-9]+\\.[0-9]+\\.[0-9]+\\.[0-9]+(/[0-9]+)?$",
+ REG_EXTENDED))
+ /* if RE compilation fails, that's an internal error */
+ abort();
+ if (regexec(&re, s, 0, 0, 0) == 0) {
+ /* arg `s' is numeric */
+ ap.isnumeric = 1;
+ if ((cp1 = strchr(s, '/')) != NULL) {
+ *cp1++ = '\0';
+ i = atoi(cp1);
+ if (i < 0 || i > 32)
+ return -1;
+ /* convert masklen to netmask */
+ ap.a_mask.s_addr = htonl(~((1 << (32 - i)) - 1));
+ }
+ if (ascii2addr(AF_INET, s, &ap.a_addr) == -1)
+ return -1;
+ if (cp1 == NULL) {
+ /* use default netmask */
+ if (IN_CLASSA(ntohl(ap.a_addr.s_addr)))
+ ap.a_mask.s_addr = htonl(IN_CLASSA_NET);
+ else if (IN_CLASSB(ntohl(ap.a_addr.s_addr)))
+ ap.a_mask.s_addr = htonl(IN_CLASSB_NET);
+ else
+ ap.a_mask.s_addr = htonl(IN_CLASSC_NET);
+ }
+ } else {
+ /* arg `s' is domain name */
+ ap.isnumeric = 0;
+ ap.a_name = s;
+ }
+ regfree(&re);
+
+ if (Debug) {
+ printf("allowaddr: rule %d: ", NumAllowed);
+ if (ap.isnumeric) {
+ printf("numeric, ");
+ printf("addr = %s, ",
+ addr2ascii(AF_INET, &ap.a_addr, sizeof(struct in_addr), 0));
+ printf("mask = %s; ",
+ addr2ascii(AF_INET, &ap.a_mask, sizeof(struct in_addr), 0));
+ } else
+ printf("domainname = %s; ", ap.a_name);
+ printf("port = %d\n", ntohs(ap.port));
+ }
+
+ if ((AllowedPeers = realloc(AllowedPeers,
+ ++NumAllowed * sizeof(struct allowedpeer)))
+ == NULL) {
+ fprintf(stderr, "Out of memory!\n");
+ exit(EX_OSERR);
+ }
+ memcpy(&AllowedPeers[NumAllowed - 1], &ap, sizeof(struct allowedpeer));
+ return 0;
+}
+
+/*
+ * Validate that the remote peer has permission to log to us.
+ */
+int
+validate(sin, hname)
+ struct sockaddr_in *sin;
+ const char *hname;
+{
+ int i;
+ size_t l1, l2;
+ char *cp, name[MAXHOSTNAMELEN];
+ struct allowedpeer *ap;
+
+ if (NumAllowed == 0)
+ /* traditional behaviour, allow everything */
+ return 1;
+
+ strncpy(name, hname, sizeof name);
+ if (strchr(name, '.') == NULL) {
+ strncat(name, ".", sizeof name - strlen(name) - 1);
+ strncat(name, LocalDomain, sizeof name - strlen(name) - 1);
+ }
+ dprintf("validate: dgram from IP %s, port %d, name %s;\n",
+ addr2ascii(AF_INET, &sin->sin_addr, sizeof(struct in_addr), 0),
+ ntohs(sin->sin_port), name);
+
+ /* now, walk down the list */
+ for (i = 0, ap = AllowedPeers; i < NumAllowed; i++, ap++) {
+ if (ntohs(ap->port) != 0 && ap->port != sin->sin_port) {
+ dprintf("rejected in rule %d due to port mismatch.\n", i);
+ continue;
+ }
+
+ if (ap->isnumeric) {
+ if ((sin->sin_addr.s_addr & ap->a_mask.s_addr)
+ != ap->a_addr.s_addr) {
+ dprintf("rejected in rule %d due to IP mismatch.\n", i);
+ continue;
+ }
+ } else {
+ cp = ap->a_name;
+ l1 = strlen(name);
+ if (*cp == '*') {
+ /* allow wildmatch */
+ cp++;
+ l2 = strlen(cp);
+ if (l2 > l1 || memcmp(cp, &name[l1 - l2], l2) != 0) {
+ dprintf("rejected in rule %d due to name mismatch.\n", i);
+ continue;
+ }
+ } else {
+ /* exact match */
+ l2 = strlen(cp);
+ if (l2 != l1 || memcmp(cp, name, l1) != 0) {
+ dprintf("rejected in rule %d due to name mismatch.\n", i);
+ continue;
+ }
+ }
+ }
+ dprintf("accepted in rule %d.\n", i);
+ return 1; /* hooray! */
+ }
+ return 0;
+}
+
+/*
+ * Fairly similar to popen(3), but returns an open descriptor, as
+ * opposed to a FILE *.
+ */
+int
+p_open(prog, pid)
+ char *prog;
+ pid_t *pid;
+{
+ int pfd[2], nulldesc, i;
+ sigset_t omask, mask;
+ char *argv[4]; /* sh -c cmd NULL */
+ char errmsg[200];
+
+ if (pipe(pfd) == -1)
+ return -1;
+ if ((nulldesc = open(_PATH_DEVNULL, O_RDWR)) == -1)
+ /* we are royally screwed anyway */
+ return -1;
+
+ mask = sigmask(SIGALRM) | sigmask(SIGHUP);
+ sigprocmask(SIG_BLOCK, &omask, &mask);
+ switch ((*pid = fork())) {
+ case -1:
+ sigprocmask(SIG_SETMASK, 0, &omask);
+ close(nulldesc);
+ return -1;
+
+ case 0:
+ argv[0] = "sh";
+ argv[1] = "-c";
+ argv[2] = prog;
+ argv[3] = NULL;
+
+ alarm(0);
+ (void)setsid(); /* Avoid catching SIGHUPs. */
+
+ /*
+ * Throw away pending signals, and reset signal
+ * behaviour to standard values.
+ */
+ signal(SIGALRM, SIG_IGN);
+ signal(SIGHUP, SIG_IGN);
+ sigprocmask(SIG_SETMASK, 0, &omask);
+ 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, 0, &omask);
+ close(nulldesc);
+ close(pfd[0]);
+ /*
+ * Avoid blocking on a hung pipe. With O_NONBLOCK, we are
+ * supposed to get an EWOULDBLOCK on writev(2), which is
+ * caught by the logic above anyway, which will in turn close
+ * the pipe, and fork a new logging subprocess if necessary.
+ * The stale subprocess will be killed some time later unless
+ * it terminated itself due to closing its input pipe (so we
+ * get rid of really dead puppies).
+ */
+ if (fcntl(pfd[1], F_SETFL, O_NONBLOCK) == -1) {
+ /* This is bad. */
+ (void)snprintf(errmsg, sizeof errmsg,
+ "Warning: cannot change pipe to PID %d to "
+ "non-blocking behaviour.",
+ (int)*pid);
+ logerror(errmsg);
+ }
+ return pfd[1];
+}
+
+void
+deadq_enter(pid)
+ pid_t pid;
+{
+ dq_t p;
+
+ p = malloc(sizeof(struct deadq_entry));
+ if (p == 0) {
+ errno = 0;
+ logerror("panic: out of virtual memory!");
+ exit(1);
+ }
+
+ p->dq_pid = pid;
+ p->dq_timeout = DQ_TIMO_INIT;
+ TAILQ_INSERT_TAIL(&deadq_head, p, dq_entries);
+}
diff --git a/usr.sbin/tcpdump/Makefile b/usr.sbin/tcpdump/Makefile
new file mode 100644
index 0000000..0a1f253
--- /dev/null
+++ b/usr.sbin/tcpdump/Makefile
@@ -0,0 +1,5 @@
+# @(#)Makefile 0.1 (RGrimes) 4/4/93
+
+SUBDIR= tcpdump tcpslice
+
+.include <bsd.subdir.mk>
diff --git a/usr.sbin/tcpdump/Makefile.inc b/usr.sbin/tcpdump/Makefile.inc
new file mode 100644
index 0000000..26c6f1c
--- /dev/null
+++ b/usr.sbin/tcpdump/Makefile.inc
@@ -0,0 +1,3 @@
+# @(#)Makefile.inc 5.1 (Berkeley) 5/11/90
+
+BINDIR?= /usr/sbin
diff --git a/usr.sbin/tcpdump/tcpdump/Makefile b/usr.sbin/tcpdump/tcpdump/Makefile
new file mode 100644
index 0000000..7a482aa
--- /dev/null
+++ b/usr.sbin/tcpdump/tcpdump/Makefile
@@ -0,0 +1,30 @@
+# $Id: Makefile,v 1.16 1997/02/22 16:14:02 peter Exp $
+
+PROG= tcpdump
+CFLAGS+=-DHAVE_FCNTL_H=1 -DHAVE_NET_SLIP_H=1 -DTIME_WITH_SYS_TIME=1 \
+ -DHAVE_ETHER_NTOA=1 -DHAVE_SETLINEBUF=1 -DSTDC_HEADERS=1 \
+ -DRETSIGTYPE=void -DRETSIGVAL= -DHAVE_SOCKADDR_SA_LEN=1 \
+ -DHAVE_TM_GMTOFF=1 -DLBL_ALIGN=1 -DPPP -DHAVE_FDDI
+MAN1= tcpdump.1
+SRCS = version.c tcpdump.c \
+ print-arp.c print-atalk.c print-atm.c print-bootp.c \
+ print-decnet.c print-domain.c print-dvmrp.c print-egp.c \
+ print-ether.c print-fddi.c print-gre.c print-icmp.c \
+ print-igrp.c print-ip.c print-ipx.c print-isoclns.c print-krb.c \
+ print-llc.c print-nfs.c print-ntp.c print-null.c print-ospf.c \
+ print-pim.c print-ppp.c print-rip.c print-sl.c print-snmp.c \
+ print-sunrpc.c print-tcp.c print-tftp.c print-udp.c print-wb.c \
+ addrtoname.c bpf_dump.c machdep.c parsenfsfh.c util.c
+CLEANFILES+= version.c
+DPADD+= ${LIBL} ${LIBPCAP}
+LDADD+= -ll -lpcap
+
+TCPDUMP_DISTDIR?= ${.CURDIR}/../../../contrib/tcpdump
+CFLAGS+= -I${TCPDUMP_DISTDIR}/lbl
+.PATH: ${TCPDUMP_DISTDIR}
+
+version.c: ${TCPDUMP_DISTDIR}/VERSION
+ rm -f version.c ; \
+ sed 's/.*/char version[] = "&";/' ${TCPDUMP_DISTDIR}/VERSION > version.c
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/tcpdump/tcpslice/Makefile b/usr.sbin/tcpdump/tcpslice/Makefile
new file mode 100644
index 0000000..b652ba5
--- /dev/null
+++ b/usr.sbin/tcpdump/tcpslice/Makefile
@@ -0,0 +1,20 @@
+# @(#)Makefile 0.1 (RWGrimes) 3/24/93
+
+PROG= tcpslice
+CFLAGS+=-Wall -I.
+MAN1= tcpslice.1
+SRCS= version.c tcpslice.c gwtm2secs.c search.c util.c
+CLEANFILES+= version.c version.h
+DPADD+= ${LIBPCAP}
+LDADD+= -lpcap
+
+TCPDUMP_DISTDIR?= ${.CURDIR}/../../../contrib/tcpdump
+
+version.c version.h: ${TCPDUMP_DISTDIR}/VERSION
+ rm -f version.c ; \
+ sed 's/.*/char version[] = "&";/' ${TCPDUMP_DISTDIR}/VERSION > version.c
+ set `sed 's/\([0-9]*\)\.\([0-9]*\).*/\1 \2/' ${TCPDUMP_DISTDIR}/VERSION` ; \
+ { echo '#define VERSION_MAJOR' $$1 ; \
+ echo '#define VERSION_MINOR' $$2 ; } > version.h
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/tcpdump/tcpslice/gwtm2secs.c b/usr.sbin/tcpdump/tcpslice/gwtm2secs.c
new file mode 100644
index 0000000..51ea194
--- /dev/null
+++ b/usr.sbin/tcpdump/tcpslice/gwtm2secs.c
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University nor the names of its contributors may be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+#if !defined(lint) && !defined(__GNUC__)
+static char rcsid[] =
+ "@(#)$Header: /home/ncvs/src/usr.sbin/tcpdump/tcpslice/gwtm2secs.c,v 1.2 1995/03/08 12:53:38 olah Exp $ (LBL)";
+#endif
+
+/*
+ * gwtm2secs.c - convert "tm" structs for Greenwich time to Unix timestamp
+ */
+
+#include "tcpslice.h"
+
+static int days_in_month[] =
+ /* Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec */
+ { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
+
+#define IS_LEAP_YEAR(year) \
+ (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0))
+
+time_t gwtm2secs( struct tm *tm )
+ {
+ int i, days, year;
+
+ year = tm->tm_year;
+
+ /* Allow for year being specified with either 2 digits or 4 digits.
+ * 2-digit years are either 19xx or 20xx - a simple heuristic
+ * distinguishes them, since we can't represent any time < 1970.
+ */
+ if ( year < 100 )
+ if ( year >= 70 )
+ year += 1900;
+ else
+ year += 2000;
+
+ days = 0;
+ for ( i = 1970; i < year; ++i )
+ {
+ days += 365;
+ if ( IS_LEAP_YEAR(i) )
+ ++days;
+ }
+
+ for ( i = 0; i < tm->tm_mon; ++i )
+ days += days_in_month[i];
+
+ if ( IS_LEAP_YEAR(year) && tm->tm_mon > 1 ) /* 1 is February */
+ ++days;
+
+ days += tm->tm_mday - 1; /* -1 since days are numbered starting at 1 */
+
+ return days * 86400 + tm->tm_hour * 3600 + tm->tm_min * 60 + tm->tm_sec;
+ }
diff --git a/usr.sbin/tcpdump/tcpslice/search.c b/usr.sbin/tcpdump/tcpslice/search.c
new file mode 100644
index 0000000..b546158
--- /dev/null
+++ b/usr.sbin/tcpdump/tcpslice/search.c
@@ -0,0 +1,566 @@
+/*
+ * Copyright (c) 1990, 1991, 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University nor the names of its contributors may be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+#if !defined(lint) && !defined(__GNUC__)
+static char rcsid[] =
+ "@(#)$Header: /home/ncvs/src/usr.sbin/tcpdump/tcpslice/search.c,v 1.2 1995/03/08 12:53:39 olah Exp $ (LBL)";
+#endif
+
+/*
+ * search.c - supports fast searching through tcpdump files for timestamps
+ */
+
+#include "tcpslice.h"
+
+
+/* Maximum number of seconds that we can conceive of a dump file spanning. */
+#define MAX_REASONABLE_FILE_SPAN (3600*24*366) /* one year */
+
+/* Maximum packet length we ever expect to see. */
+#define MAX_REASONABLE_PACKET_LENGTH 65535
+
+/* Size of a packet header in bytes; easier than typing the sizeof() all
+ * the time ...
+ */
+#define PACKET_HDR_LEN (sizeof( struct pcap_pkthdr ))
+
+extern int snaplen;
+
+/* The maximum size of a packet, including its header. */
+#define MAX_PACKET_SIZE (PACKET_HDR_LEN + snaplen)
+
+/* Number of contiguous bytes from a dumpfile in which there's guaranteed
+ * to be enough information to find a "definite" header if one exists
+ * therein. This takes 3 full packets - the first to be just misaligned
+ * (one byte short of a full packet), missing its timestamp; the second
+ * to have the legitimate timestamp; and the third to provide confirmation
+ * that the second is legit, making it a "definite" header. We could
+ * scrimp a bit here since not the entire third packet is required, but
+ * it doesn't seem worth it
+ */
+#define MAX_BYTES_FOR_DEFINITE_HEADER (3 * MAX_PACKET_SIZE)
+
+/* Maximum number of seconds that might reasonably separate two headers. */
+#define MAX_REASONABLE_HDR_SEPARATION (3600 * 24 * 7) /* one week */
+
+/* When searching a file for a packet, if we think we're within this many
+ * bytes of the packet we just search linearly. Since linear searches are
+ * probably much faster than random ones (random ones require searching for
+ * the beginning of the packet, which may be unaligned in memory), we make
+ * this value pretty hefty.
+ */
+#define STRAIGHT_SCAN_THRESHOLD (100 * MAX_PACKET_SIZE)
+
+
+/* Given a header and an acceptable first and last time stamp, returns non-zero
+ * if the header looks reasonable and zero otherwise.
+ */
+static int
+reasonable_header( struct pcap_pkthdr *hdr, long first_time, long last_time )
+ {
+ if ( last_time == 0 )
+ last_time = first_time + MAX_REASONABLE_FILE_SPAN;
+
+ return hdr->ts.tv_sec >= first_time &&
+ hdr->ts.tv_sec <= last_time &&
+ hdr->len > 0 &&
+ hdr->len <= MAX_REASONABLE_PACKET_LENGTH &&
+ hdr->caplen > 0 &&
+ hdr->caplen <= MAX_REASONABLE_PACKET_LENGTH;
+ }
+
+
+#define SWAPLONG(y) \
+((((y)&0xff)<<24) | (((y)&0xff00)<<8) | (((y)&0xff0000)>>8) | (((y)>>24)&0xff))
+#define SWAPSHORT(y) \
+ ( (((y)&0xff)<<8) | (((y)&0xff00)>>8) )
+
+/* Given a buffer, extracts a (properly aligned) packet header from it. */
+
+static void
+extract_header( pcap_t *p, u_char *buf, struct pcap_pkthdr *hdr )
+ {
+ bcopy((char *) buf, (char *) hdr, sizeof(struct pcap_pkthdr));
+
+ if ( pcap_is_swapped( p ) )
+ {
+ hdr->ts.tv_sec = SWAPLONG(hdr->ts.tv_sec);
+ hdr->ts.tv_usec = SWAPLONG(hdr->ts.tv_usec);
+ hdr->len = SWAPLONG(hdr->len);
+ hdr->caplen = SWAPLONG(hdr->caplen);
+ }
+
+ /*
+ * From bpf/libpcap/savefile.c:
+ *
+ * We interchanged the caplen and len fields at version 2.3,
+ * in order to match the bpf header layout. But unfortunately
+ * some files were written with version 2.3 in their headers
+ * but without the interchanged fields.
+ */
+ if ( pcap_minor_version( p ) < 3 ||
+ (pcap_minor_version( p ) == 3 && hdr->caplen > hdr->len) )
+ {
+ int t = hdr->caplen;
+ hdr->caplen = hdr->len;
+ hdr->len = t;
+ }
+ }
+
+
+/* Search a buffer to locate the first header within it. Return values
+ * are HEADER_NONE, HEADER_CLASH, HEADER_PERHAPS, and HEADER_DEFINITELY.
+ * The first indicates that no evidence of a header was found; the second
+ * that two or more possible headers were found, neither more convincing
+ * than the other(s); the third that exactly one "possible" header was
+ * found; and the fourth that exactly one "definite" header was found.
+ *
+ * Headers are detected by looking for positions in the buffer which have
+ * reasonable timestamps and lengths. If there is enough room in the buffer
+ * for another header to follow a candidate header, a check is made for
+ * that following header. If it is present then the header is *definite*
+ * (unless another "perhaps" or "definite" header is found); if not, then
+ * the header is discarded. If there is not enough room in the buffer for
+ * another header then the candidate is *perhaps* (unless another header
+ * is subsequently found). A "tie" between a "definite" header and a
+ * "perhaps" header is resolved in favor of the definite header. Any
+ * other tie leads to HEADER_CLASH.
+ *
+ * The buffer position of the header is returned in hdrpos_addr and
+ * for convenience the corresponding header in return_hdr.
+ *
+ * first_time is the earliest possible acceptable timestamp in the
+ * header. last_time, if non-zero, is the last such timestamp. If
+ * zero, then up to MAX_REASONABLE_FILE_SPAN seconds after first_time
+ * is acceptable.
+ */
+
+#define HEADER_NONE 0
+#define HEADER_CLASH 1
+#define HEADER_PERHAPS 2
+#define HEADER_DEFINITELY 3
+
+static int
+find_header( pcap_t *p, u_char *buf, int buf_len,
+ long first_time, long last_time,
+ u_char **hdrpos_addr, struct pcap_pkthdr *return_hdr )
+ {
+ u_char *bufptr, *bufend, *last_pos_to_try;
+ struct pcap_pkthdr hdr, hdr2;
+ int status = HEADER_NONE;
+ int saw_PERHAPS_clash = 0;
+
+ /* Initially, try each buffer position to see whether it looks like
+ * a valid packet header. We may later restrict the positions we look
+ * at to avoid seeing a sequence of legitimate headers as conflicting
+ * with one another.
+ */
+ bufend = buf + buf_len;
+ last_pos_to_try = bufend - PACKET_HDR_LEN;
+
+ for ( bufptr = buf; bufptr < last_pos_to_try; ++bufptr )
+ {
+ extract_header( p, bufptr, &hdr );
+
+ if ( reasonable_header( &hdr, first_time, last_time ) )
+ {
+ u_char *next_header = bufptr + PACKET_HDR_LEN + hdr.caplen;
+
+ if ( next_header + PACKET_HDR_LEN < bufend )
+ { /* check for another good header */
+ extract_header( p, next_header, &hdr2 );
+
+ if ( reasonable_header( &hdr2, hdr.ts.tv_sec,
+ hdr.ts.tv_sec + MAX_REASONABLE_HDR_SEPARATION ) )
+ { /* a confirmed header */
+ switch ( status )
+ {
+ case HEADER_NONE:
+ case HEADER_PERHAPS:
+ status = HEADER_DEFINITELY;
+ *hdrpos_addr = bufptr;
+ *return_hdr = hdr;
+
+ /* Make sure we don't demote this "definite"
+ * to a "clash" if we stumble across its
+ * successor.
+ */
+ last_pos_to_try = next_header - PACKET_HDR_LEN;
+ break;
+
+ case HEADER_DEFINITELY:
+ return HEADER_CLASH;
+
+ default:
+ error( "bad status in find_header()" );
+ }
+ }
+
+ /* ... else the header is bogus - we've verified that it's
+ * not followed by a reasonable header.
+ */
+ }
+
+ else
+ { /* can't check for another good header */
+ switch ( status )
+ {
+ case HEADER_NONE:
+ status = HEADER_PERHAPS;
+ *hdrpos_addr = bufptr;
+ *return_hdr = hdr;
+ break;
+
+ case HEADER_PERHAPS:
+ /* We don't immediately turn this into a
+ * clash because perhaps we'll later see a
+ * "definite" which will save us ...
+ */
+ saw_PERHAPS_clash = 1;
+ break;
+
+ case HEADER_DEFINITELY:
+ /* Keep the definite in preference to this one. */
+ break;
+
+ default:
+ error( "bad status in find_header()" );
+ }
+ }
+ }
+ }
+
+ if ( status == HEADER_PERHAPS && saw_PERHAPS_clash )
+ status = HEADER_CLASH;
+
+ return status;
+ }
+
+
+/* Positions the sf_readfile stream such that the next sf_read() will
+ * read the final full packet in the file. Returns non-zero if
+ * successful, zero if unsuccessful. If successful, returns the
+ * timestamp of the last packet in last_timestamp.
+ *
+ * Note that this routine is a special case of sf_find_packet(). In
+ * order to use sf_find_packet(), one first must use this routine in
+ * order to give sf_find_packet() an upper bound on the timestamps
+ * present in the dump file.
+ */
+int
+sf_find_end( pcap_t *p, struct timeval *first_timestamp,
+ struct timeval *last_timestamp )
+ {
+ long first_time = first_timestamp->tv_sec;
+ u_int num_bytes;
+ u_char *buf, *bufpos, *bufend;
+ u_char *hdrpos;
+ struct pcap_pkthdr hdr, successor_hdr;
+ int status;
+
+ /* Allow enough room for at least two full (untruncated) packets,
+ * perhaps followed by a truncated packet, so we have a shot at
+ * finding a "definite" header and following its chain to the
+ * end of the file.
+ */
+ num_bytes = MAX_BYTES_FOR_DEFINITE_HEADER;
+ if ( fseek( pcap_file( p ), (long) -num_bytes, 2 ) < 0 )
+ return 0;
+
+ buf = (u_char *)malloc((u_int) num_bytes);
+ if ( ! buf )
+ return 0;
+
+ status = 0;
+ bufpos = buf;
+ bufend = buf + num_bytes;
+
+ if ( fread( (char *) bufpos, num_bytes, 1, pcap_file( p ) ) != 1 )
+ goto done;
+
+ if ( find_header( p, bufpos, num_bytes,
+ first_time, 0L, &hdrpos, &hdr ) != HEADER_DEFINITELY )
+ goto done;
+
+ /* Okay, we have a definite header in our hands. Follow its
+ * chain till we find the last valid packet in the file ...
+ */
+ for ( ; ; )
+ {
+ /* move to the next header position */
+ bufpos = hdrpos + PACKET_HDR_LEN + hdr.caplen;
+
+ /* bufpos now points to a candidate packet, which if valid
+ * should replace the current packet pointed to by hdrpos as
+ * the last valid packet ...
+ */
+ if ( bufpos >= bufend - PACKET_HDR_LEN )
+ /* not enough room for another header */
+ break;
+
+ extract_header( p, bufpos, &successor_hdr );
+
+ first_time = hdr.ts.tv_sec;
+ if ( ! reasonable_header( &successor_hdr, first_time, 0L ) )
+ /* this bodes ill - it means bufpos is perhaps a
+ * bogus packet header after all ...
+ */
+ break;
+
+ /* Note that the following test is for whether the next
+ * packet starts at a position > bufend, *not* for a
+ * position >= bufend. If this is the last packet in the
+ * file and there isn't a subsequent partial packet, then
+ * we expect the first buffer position beyond this packet
+ * to be just beyond the end of the buffer, i.e., at bufend
+ * itself.
+ */
+ if ( bufpos + PACKET_HDR_LEN + successor_hdr.caplen > bufend )
+ /* the packet is truncated */
+ break;
+
+ /* Accept this packet as fully legit. */
+ hdrpos = bufpos;
+ hdr = successor_hdr;
+ }
+
+ /* Success! Last valid packet is at hdrpos. */
+ *last_timestamp = hdr.ts;
+ status = 1;
+
+ /* Seek so that the next read will start at last valid packet. */
+ if ( fseek( pcap_file( p ), (long) -(bufend - hdrpos), 2 ) < 0 )
+ error( "final fseek() failed in sf_find_end()" );
+
+ done:
+ free( (char *) buf );
+
+ return status;
+ }
+
+
+/* Takes two timeval's and returns the difference, tv2 - tv1, as a double. */
+
+static double
+timeval_diff( struct timeval *tv1, struct timeval *tv2 )
+ {
+ double result = (tv2->tv_sec - tv1->tv_sec);
+ result += (tv2->tv_usec - tv1->tv_usec) / 1000000.0;
+
+ return result;
+ }
+
+
+/* Returns true if timestamp t1 is chronologically less than timestamp t2. */
+
+int
+sf_timestamp_less_than( struct timeval *t1, struct timeval *t2 )
+ {
+ return t1->tv_sec < t2->tv_sec ||
+ (t1->tv_sec == t2->tv_sec &&
+ t1->tv_usec < t2->tv_usec);
+ }
+
+
+/* Given two timestamps on either side of desired_time and their positions,
+ * returns the interpolated position of the desired_time packet. Returns a
+ * negative value if the desired_time is outside the given range.
+ */
+
+static long
+interpolated_position( struct timeval *min_time, long min_pos,
+ struct timeval *max_time, long max_pos,
+ struct timeval *desired_time )
+ {
+ double full_span = timeval_diff( max_time, min_time );
+ double desired_span = timeval_diff( desired_time, min_time );
+ long full_span_pos = max_pos - min_pos;
+ double fractional_offset = desired_span / full_span;
+
+ if ( fractional_offset < 0.0 || fractional_offset > 1.0 )
+ return -1;
+
+ return min_pos + (long) (fractional_offset * (double) full_span_pos);
+ }
+
+
+/* Reads packets linearly until one with a time >= the given desired time
+ * is found; positions the dump file so that the next read will start
+ * at the given packet. Returns non-zero on success, 0 if an EOF was
+ * first encountered.
+ */
+
+static int
+read_up_to( pcap_t *p, struct timeval *desired_time )
+ {
+ struct pcap_pkthdr hdr;
+ const u_char *buf;
+ long pos;
+ int status;
+
+ for ( ; ; )
+ {
+ struct timeval *timestamp;
+
+ pos = ftell( pcap_file( p ) );
+ buf = pcap_next( p, &hdr );
+
+ if ( buf == 0 )
+ {
+ if ( feof( pcap_file( p ) ) )
+ {
+ status = 0;
+ clearerr( pcap_file( p ) );
+ break;
+ }
+
+ error( "bad status in read_up_to()" );
+ }
+
+ timestamp = &hdr.ts;
+
+ if ( ! sf_timestamp_less_than( timestamp, desired_time ) )
+ {
+ status = 1;
+ break;
+ }
+ }
+
+ if ( fseek( pcap_file( p ), pos, 0 ) < 0 )
+ error( "fseek() failed in read_up_to()" );
+
+ return (status);
+ }
+
+
+/* Positions the sf_readfile stream so that the next sf_read() will
+ * return the first packet with a time greater than or equal to
+ * desired_time. desired_time must be greater than min_time and less
+ * than max_time, which should correspond to actual packets in the
+ * file. min_pos is the file position (byte offset) corresponding to
+ * the min_time packet and max_pos is the same for the max_time packet.
+ *
+ * Returns non-zero on success, 0 if the given position is beyond max_pos.
+ *
+ * NOTE: when calling this routine, the sf_readfile stream *must* be
+ * already aligned so that the next call to sf_next_packet() will yield
+ * a valid packet.
+ */
+
+int
+sf_find_packet( pcap_t *p,
+ struct timeval *min_time, long min_pos,
+ struct timeval *max_time, long max_pos,
+ struct timeval *desired_time )
+ {
+ int status = 1;
+ struct timeval min_time_copy, max_time_copy;
+ u_int num_bytes = MAX_BYTES_FOR_DEFINITE_HEADER;
+ int num_bytes_read;
+ long desired_pos, present_pos;
+ u_char *buf, *hdrpos;
+ struct pcap_pkthdr hdr;
+
+ buf = (u_char *) malloc( num_bytes );
+ if ( ! buf )
+ error( "malloc() failured in sf_find_packet()" );
+
+ min_time_copy = *min_time;
+ min_time = &min_time_copy;
+
+ max_time_copy = *max_time;
+ max_time = &max_time_copy;
+
+ for ( ; ; ) /* loop until positioned correctly */
+ {
+ desired_pos =
+ interpolated_position( min_time, min_pos,
+ max_time, max_pos,
+ desired_time );
+
+ if ( desired_pos < 0 )
+ {
+ status = 0;
+ break;
+ }
+
+ present_pos = ftell( pcap_file( p ) );
+
+ if ( present_pos <= desired_pos &&
+ desired_pos - present_pos < STRAIGHT_SCAN_THRESHOLD )
+ { /* we're close enough to just blindly read ahead */
+ status = read_up_to( p, desired_time );
+ break;
+ }
+
+ /* Undershoot the target a little bit - it's much easier to
+ * then scan straight forward than to try to read backwards ...
+ */
+ desired_pos -= STRAIGHT_SCAN_THRESHOLD / 2;
+ if ( desired_pos < min_pos )
+ desired_pos = min_pos;
+
+ if ( fseek( pcap_file( p ), desired_pos, 0 ) < 0 )
+ error( "fseek() failed in sf_find_packet()" );
+
+ num_bytes_read =
+ fread( (char *) buf, 1, num_bytes, pcap_file( p ) );
+
+ if ( num_bytes_read == 0 )
+ /* This shouldn't ever happen because we try to
+ * undershoot, unless the dump file has only a
+ * couple packets in it ...
+ */
+ error( "fread() failed in sf_find_packet()" );
+
+ if ( find_header( p, buf, num_bytes, min_time->tv_sec,
+ max_time->tv_sec, &hdrpos, &hdr ) !=
+ HEADER_DEFINITELY )
+ error( "can't find header at position %ld in dump file",
+ desired_pos );
+
+ /* Correct desired_pos to reflect beginning of packet. */
+ desired_pos += (hdrpos - buf);
+
+ /* Seek to the beginning of the header. */
+ if ( fseek( pcap_file( p ), desired_pos, 0 ) < 0 )
+ error( "fseek() failed in sf_find_packet()" );
+
+ if ( sf_timestamp_less_than( &hdr.ts, desired_time ) )
+ { /* too early in the file */
+ *min_time = hdr.ts;
+ min_pos = desired_pos;
+ }
+
+ else if ( sf_timestamp_less_than( desired_time, &hdr.ts ) )
+ { /* too late in the file */
+ *max_time = hdr.ts;
+ max_pos = desired_pos;
+ }
+
+ else
+ /* got it! */
+ break;
+ }
+
+ free( (char *) buf );
+
+ return status;
+ }
diff --git a/usr.sbin/tcpdump/tcpslice/tcpslice.1 b/usr.sbin/tcpdump/tcpslice/tcpslice.1
new file mode 100644
index 0000000..184e8e5
--- /dev/null
+++ b/usr.sbin/tcpdump/tcpslice/tcpslice.1
@@ -0,0 +1,260 @@
+.\" @(#) $Header: /home/ncvs/src/usr.sbin/tcpdump/tcpslice/tcpslice.1,v 1.3 1995/03/08 12:53:39 olah Exp $ (LBL)
+.\"
+.\" 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.
+.\"
+.TH TCPSLICE 1 "14 Oct 1991"
+.SH NAME
+tcpslice \- extract pieces of and/or glue together tcpdump files
+.SH SYNOPSIS
+.na
+.B tcpslice
+[
+.B \-dRrt
+] [
+.B \-w
+.I file
+]
+.br
+.ti +9
+[
+.I start-time
+[
+.I end-time
+] ]
+.I file ...
+.br
+.ad
+.SH DESCRIPTION
+.LP
+.I Tcpslice
+is a program for extracting portions of packet-trace files generated using
+\fItcpdump(1)\fP's
+.B \-w
+flag.
+It can also be used to glue together several such files, as discussed
+below.
+.LP
+The basic operation of
+.I tcpslice
+is to copy to
+.I 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
+.I first time.
+The ending time defaults to ten years after the starting time.
+Thus, the command
+.I tcpslice trace-file
+simply copies
+.I trace-file
+to \fIstdout\fP (assuming the file does not include more than
+ten years' worth of data).
+.LP
+There are a number of ways to specify times. The first is using
+Unix timestamps of the form
+.I sssssssss.uuuuuu
+(this is the format specified by \fItcpdump\fP's
+.B \-tt
+flag).
+For example,
+.B 654321098.7654
+specifies 38 seconds and 765,400 microseconds
+after 8:51PM PDT, Sept. 25, 1990.
+.LP
+All examples in this manual are given
+for PDT times, but when displaying times and interpreting times symbolically
+as discussed below,
+.I tcpslice
+uses the local timezone, regardless of the timezone in which the \fItcpdump\fP
+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.
+.LP
+Times may also be specified relative
+to either the
+.I 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
+.B +200
+indicates 200 seconds after the
+.I first time,
+and the two arguments
+.B +200 +300
+indicate from 200 seconds after the
+.I first time
+through 500 seconds after the
+.I first time.
+.LP
+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
+.B 90y9m25d20h51m38s765400u.
+.LP
+When specifying times using this style, fields that are omitted default
+as follows. If the omitted field is a unit
+.I greater
+than that of the first specified field, then its value defaults to
+the corresponding value taken from either
+.I 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
+.I less
+than that of the first specified field, then it defaults to zero.
+For example, suppose that the input file has a
+.I 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
+.B 21h36m.
+To specify a range from 9:36PM PDT through 1:54AM PDT the next day we
+could use
+.B 21h36m 26d1h54m.
+.LP
+Relative times can also be specified when using the
+.I ymdhmsu
+format. Omitted fields then default to 0 if the unit of the field is
+.I greater
+than that of the first specified field, and to the corresponding value
+taken from either the
+.I first time
+or the starting time if the omitted field's unit is
+.I less
+than that of the first specified field. Given a
+.I first time
+of the Unix timestamp mentioned above,
+.B 22h +1h10m
+specifies a range from 10:00PM PDT on that date through 11:10PM PDT, and
+.B +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
+.B +0 +1h.
+.LP
+Note that with the
+.I ymdhmsu
+format there is an ambiguity between using
+.I m
+for `month' or for `minute'. The ambiguity is resolved as follows: if an
+.I m
+field is followed by a
+.I d
+field then it is interpreted as specifying months; otherwise it
+specifies minutes.
+.LP
+If more than one input file is specified then
+.I tcpslice
+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
+.I 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
+.LP
+If any of
+.B \-R,
+.B \-r
+or
+.B \-t
+are specified then
+.I tcpslice
+reports the timestamps of the first and last packets in each input file
+and exits. Only one of these three options may be specified.
+.TP
+.B \-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
+.B \-R,
+.B \-r
+or
+.B \-t
+has been specified then the times are dumped in the corresponding
+format; otherwise, raw format (\fB \-R\fP) is used.
+.TP
+.B \-R
+Dump the timestamps of the first and last packets in each input file
+as raw timestamps (i.e., in the form \fI sssssssss.uuuuuu\fP).
+.TP
+.B \-r
+Same as
+.B \-R
+except the timestamps are dumped in human-readable format, similar
+to that used by \fI date(1)\fP.
+.TP
+.B \-t
+Same as
+.B \-R
+except the timestamps are dumped in
+.I tcpslice
+format, i.e., in the
+.I ymdhmsu
+format discussed above.
+.TP
+.B \-w
+Direct the output to \fIfile\fR rather than \fIstdout\fP.
+.SH "SEE ALSO"
+tcpdump(1)
+.SH AUTHOR
+Vern Paxson (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'.
+.LP
+.I tcpslice
+cannot read its input from \fIstdin\fP, since it uses random-access
+to rummage through its input files.
+.LP
+.I tcpslice
+refuses to write to its output if it is a terminal
+(as indicated by \fIisatty(3)\fP). 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 \fIstdout\fP or specify an
+output file via \fB\-w\fP.
+.LP
+.I tcpslice
+will not work properly on \fItcpdump\fP 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
+.I tcpslice
+to greatly speed up its processing when dealing with large trace files.
+Note that
+.I tcpslice
+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 \fItcpdump\fP
+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..d0abf26
--- /dev/null
+++ b/usr.sbin/tcpdump/tcpslice/tcpslice.c
@@ -0,0 +1,618 @@
+/*
+ * 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.
+ */
+#if !defined(lint) && !defined(__GNUC__)
+char copyright[] =
+ "@(#) Copyright (c) 1987-1990 The Regents of the University of California.\nAll rights reserved.\n";
+static char rcsid[] =
+ "@(#)$Header: /home/ncvs/src/usr.sbin/tcpdump/tcpslice/tcpslice.c,v 1.3 1995/08/23 05:18:59 pst Exp $ (LBL)";
+#endif
+
+/*
+ * tcpslice - extract pieces of and/or glue together tcpdump files
+ */
+
+#include "tcpslice.h"
+
+int tflag = 0; /* global that util routines are sensitive to */
+int fddipad; /* XXX: libpcap needs this global */
+
+char *program_name;
+
+/* 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[]);
+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;
+
+ extern char *optarg;
+ extern int optind, opterr;
+
+ program_name = argv[0];
+
+ opterr = 0;
+ while ((op = getopt(argc, argv, "dRrtw:")) != -1)
+ switch (op) {
+
+ case 'd':
+ dump_flag = 1;
+ break;
+
+ case 'R':
+ ++report_times;
+ timestamp_style = TIMESTAMP_RAW;
+ break;
+
+ case 'r':
+ ++report_times;
+ timestamp_style = TIMESTAMP_READABLE;
+ break;
+
+ case 't':
+ ++report_times;
+ timestamp_style = TIMESTAMP_PARSEABLE;
+ break;
+
+ case 'w':
+ write_file_name = optarg;
+ break;
+
+ default:
+ usage();
+ /* NOTREACHED */
+ }
+
+ if ( report_times > 1 )
+ error( "only one of -R, -r, or -t can be specified" );
+
+
+ if (optind < argc)
+ /* See if the next argument looks like a possible
+ * start time, and if so assume it is one.
+ */
+ if (isdigit(argv[optind][0]) || argv[optind][0] == '+')
+ start_time_string = argv[optind++];
+
+ if (optind < argc)
+ if (isdigit(argv[optind][0]) || argv[optind][0] == '+')
+ stop_time_string = argv[optind++];
+
+
+ if (optind >= argc)
+ error("at least one input file must be given");
+
+
+ first_time = first_packet_time(argv[optind], &pcap);
+ pcap_close(pcap);
+
+
+ if (start_time_string)
+ start_time = parse_time(start_time_string, first_time);
+ else
+ start_time = first_time;
+
+ if (stop_time_string)
+ stop_time = parse_time(stop_time_string, start_time);
+
+ else
+ {
+ stop_time = start_time;
+ stop_time.tv_sec += 86400*3660; /* + 10 years; "forever" */
+ }
+
+
+ if (report_times) {
+ for (; optind < argc; ++optind)
+ dump_times(&pcap, argv[optind]);
+ }
+
+ if (dump_flag) {
+ printf( "start\t%s\nstop\t%s\n",
+ timestamp_to_string( &start_time ),
+ timestamp_to_string( &stop_time ) );
+ }
+
+ if (! report_times && ! dump_flag) {
+ if ( ! strcmp( write_file_name, "-" ) &&
+ isatty( fileno(stdout) ) )
+ error("stdout is a terminal; redirect or use -w");
+
+ for (; optind < argc; ++optind)
+ extract_slice(argv[optind], write_file_name,
+ &start_time, &stop_time);
+ }
+
+ return 0;
+}
+
+
+/* Returns non-zero if a string matches the format for a timestamp,
+ * 0 otherwise.
+ */
+int is_timestamp( char *str )
+ {
+ while ( isdigit(*str) || *str == '.' )
+ ++str;
+
+ return *str == '\0';
+ }
+
+
+/* Return the correction in seconds for the local time zone with respect
+ * to Greenwich time.
+ */
+long local_time_zone(long timestamp)
+{
+ struct timeval now;
+ struct timezone tz;
+ long localzone;
+
+ if (gettimeofday(&now, &tz) < 0) {
+ perror("tcpslice: gettimeofday");
+ exit(1);
+ }
+ localzone = tz.tz_minuteswest * -60;
+
+ if (localtime((time_t *) &timestamp)->tm_isdst)
+ localzone += 3600;
+
+ return localzone;
+}
+
+/* Given a string specifying a time (or a time offset) and a "base time"
+ * from which to compute offsets and fill in defaults, returns a timeval
+ * containing the specified time.
+ */
+
+struct timeval
+parse_time(char *time_string, struct timeval base_time)
+{
+ struct tm *bt = localtime((time_t *) &base_time.tv_sec);
+ struct tm t;
+ struct timeval result;
+ time_t usecs = 0;
+ int is_delta = (time_string[0] == '+');
+
+ if ( is_delta )
+ ++time_string; /* skip over '+' sign */
+
+ if ( is_timestamp( time_string ) )
+ { /* interpret as a raw timestamp or timestamp offset */
+ char *time_ptr;
+
+ result.tv_sec = atoi( time_string );
+ time_ptr = strchr( time_string, '.' );
+
+ if ( time_ptr )
+ { /* microseconds are specified, too */
+ int num_digits = strlen( time_ptr + 1 );
+ result.tv_usec = atoi( time_ptr + 1 );
+
+ /* turn 123.456 into 123 seconds plus 456000 usec */
+ while ( num_digits++ < 6 )
+ result.tv_usec *= 10;
+ }
+
+ else
+ result.tv_usec = 0;
+
+ if ( is_delta )
+ {
+ result.tv_sec += base_time.tv_sec;
+ result.tv_usec += base_time.tv_usec;
+
+ if ( result.tv_usec > 1000000 )
+ {
+ result.tv_usec -= 1000000;
+ ++result.tv_sec;
+ }
+ }
+
+ return result;
+ }
+
+ if (is_delta) {
+ t = *bt;
+ usecs = base_time.tv_usec;
+ } else {
+ /* Zero struct (easy way around lack of tm_gmtoff/tm_zone
+ * under older systems) */
+ bzero((char *)&t, sizeof(t));
+
+ /* Set values to "not set" flag so we can later identify
+ * and default them.
+ */
+ t.tm_sec = t.tm_min = t.tm_hour = t.tm_mday = t.tm_mon =
+ t.tm_year = -1;
+ }
+
+ fill_tm(time_string, is_delta, &t, &usecs);
+
+ /* Now until we reach a field that was specified, fill in the
+ * missing fields from the base time.
+ */
+#define CHECK_FIELD(field_name) \
+ if (t.field_name < 0) \
+ t.field_name = bt->field_name; \
+ else \
+ break
+
+ do { /* bogus do-while loop so "break" in CHECK_FIELD will work */
+ CHECK_FIELD(tm_year);
+ CHECK_FIELD(tm_mon);
+ CHECK_FIELD(tm_mday);
+ CHECK_FIELD(tm_hour);
+ CHECK_FIELD(tm_min);
+ CHECK_FIELD(tm_sec);
+ } while ( 0 );
+
+ /* Set remaining unspecified fields to 0. */
+#define ZERO_FIELD_IF_NOT_SET(field_name,zero_val) \
+ if (t.field_name < 0) \
+ t.field_name = zero_val
+
+ if (! is_delta) {
+ ZERO_FIELD_IF_NOT_SET(tm_year,90); /* should never happen */
+ ZERO_FIELD_IF_NOT_SET(tm_mon,0);
+ ZERO_FIELD_IF_NOT_SET(tm_mday,1);
+ ZERO_FIELD_IF_NOT_SET(tm_hour,0);
+ ZERO_FIELD_IF_NOT_SET(tm_min,0);
+ ZERO_FIELD_IF_NOT_SET(tm_sec,0);
+ }
+
+ result.tv_sec = gwtm2secs(&t);
+ result.tv_sec -= local_time_zone(result.tv_sec);
+ result.tv_usec = usecs;
+
+ return result;
+}
+
+
+/* Fill in (or add to, if is_delta is true) the time values in the
+ * tm struct "t" as specified by the time specified in the string
+ * "time_string". "usecs_addr" is updated with the specified number
+ * of microseconds, if any.
+ */
+void
+fill_tm(char *time_string, int is_delta, struct tm *t, time_t *usecs_addr)
+{
+ char *t_start, *t_stop, format_ch;
+ int val;
+
+#define SET_VAL(lhs,rhs) \
+ if (is_delta) \
+ lhs += rhs; \
+ else \
+ lhs = rhs
+
+ /* Loop through the time string parsing one specification at
+ * a time. Each specification has the form <number><letter>
+ * where <number> indicates the amount of time and <letter>
+ * the units.
+ */
+ for (t_stop = t_start = time_string; *t_start; t_start = ++t_stop) {
+ if (! isdigit(*t_start))
+ error("bad date format %s, problem starting at %s",
+ time_string, t_start);
+
+ while (isdigit(*t_stop))
+ ++t_stop;
+ if (! t_stop)
+ error("bad date format %s, problem starting at %s",
+ time_string, t_start);
+
+ val = atoi(t_start);
+
+ format_ch = *t_stop;
+ if ( isupper( format_ch ) )
+ format_ch = tolower( format_ch );
+
+ switch (format_ch) {
+ case 'y':
+ if ( val > 1900 )
+ val -= 1900;
+ SET_VAL(t->tm_year, val);
+ break;
+
+ case 'm':
+ if (strchr(t_stop+1, 'D') ||
+ strchr(t_stop+1, 'd'))
+ /* it's months */
+ SET_VAL(t->tm_mon, val - 1);
+ else /* it's minutes */
+ SET_VAL(t->tm_min, val);
+ break;
+
+ case 'd':
+ SET_VAL(t->tm_mday, val);
+ break;
+
+ case 'h':
+ SET_VAL(t->tm_hour, val);
+ break;
+
+ case 's':
+ SET_VAL(t->tm_sec, val);
+ break;
+
+ case 'u':
+ SET_VAL(*usecs_addr, val);
+ break;
+
+ default:
+ error(
+ "bad date format %s, problem starting at %s",
+ time_string, t_start);
+ }
+ }
+}
+
+
+/* Return in first_time and last_time the timestamps of the first and
+ * last packets in the given file.
+ */
+void
+get_file_range( char filename[], pcap_t **p,
+ struct timeval *first_time, struct timeval *last_time )
+{
+ *first_time = first_packet_time( filename, p );
+
+ if ( ! sf_find_end( *p, first_time, last_time ) )
+ error( "couldn't find final packet in file %s", filename );
+}
+
+int snaplen;
+
+/* Returns the timestamp of the first packet in the given tcpdump save
+ * file, which as a side-effect is initialized for further save-file
+ * reading.
+ */
+
+struct timeval
+first_packet_time(char filename[], pcap_t **p_addr)
+{
+ struct pcap_pkthdr hdr;
+ pcap_t *p;
+ char errbuf[PCAP_ERRBUF_SIZE];
+
+ p = *p_addr = pcap_open_offline(filename, errbuf);
+ if (! p)
+ error( "bad tcpdump file %s: %s", filename, errbuf );
+
+ snaplen = pcap_snapshot( p );
+
+ if (pcap_next(p, &hdr) == 0)
+ error( "bad status reading first packet in %s", filename );
+
+ return hdr.ts;
+}
+
+
+/* Extract from the given file all packets with timestamps between
+ * the two time values given (inclusive). These packets are written
+ * to the save file given by write_file_name.
+ *
+ * Upon return, start_time is adjusted to reflect a time just after
+ * that of the last packet written to the output.
+ */
+
+void
+extract_slice(char filename[], char write_file_name[],
+ struct timeval *start_time, struct timeval *stop_time)
+{
+ long start_pos, stop_pos;
+ struct timeval file_start_time, file_stop_time;
+ struct pcap_pkthdr hdr;
+ pcap_t *p;
+ char errbuf[PCAP_ERRBUF_SIZE];
+
+ p = pcap_open_offline(filename, errbuf);
+ if (! p)
+ error( "bad tcpdump file %s: %s", filename, errbuf );
+
+ snaplen = pcap_snapshot( p );
+ start_pos = ftell( pcap_file( p ) );
+
+ if ( ! dumper )
+ {
+ dumper = pcap_dump_open(p, write_file_name);
+ if ( ! dumper )
+ error( "error creating output file %s: ",
+ write_file_name, pcap_geterr( p ) );
+ }
+
+ if (pcap_next(p, &hdr) == 0)
+ error( "error reading packet in %s: ",
+ filename, pcap_geterr( p ) );
+
+ file_start_time = hdr.ts;
+
+
+ if ( ! sf_find_end( p, &file_start_time, &file_stop_time ) )
+ error( "problems finding end packet of file %s",
+ filename );
+
+ stop_pos = ftell( pcap_file( p ) );
+
+
+ /* sf_find_packet() requires that the time it's passed as its last
+ * argument be in the range [min_time, max_time], so we enforce
+ * that constraint here.
+ */
+ if ( sf_timestamp_less_than( start_time, &file_start_time ) )
+ *start_time = file_start_time;
+
+ if ( sf_timestamp_less_than( &file_stop_time, start_time ) )
+ return; /* there aren't any packets of interest in the file */
+
+
+ sf_find_packet( p, &file_start_time, start_pos,
+ &file_stop_time, stop_pos,
+ start_time );
+
+ for ( ; ; )
+ {
+ struct timeval *timestamp;
+ const u_char *pkt = pcap_next( p, &hdr );
+
+ if ( pkt == 0 )
+ {
+#ifdef notdef
+ int status;
+ if ( status != SFERR_EOF )
+ error( "bad status %d reading packet in %s",
+ status, filename );
+#endif
+ break;
+ }
+
+ timestamp = &hdr.ts;
+
+ if ( ! sf_timestamp_less_than( timestamp, start_time ) )
+ { /* packet is recent enough */
+ if ( sf_timestamp_less_than( stop_time, timestamp ) )
+ /* We've gone beyond the end of the region
+ * of interest ... We're done with this file.
+ */
+ break;
+
+ pcap_dump((u_char *) dumper, &hdr, pkt);
+
+ *start_time = *timestamp;
+
+ /* We know that each packet is guaranteed to have
+ * a unique timestamp, so we push forward the
+ * allowed minimum time to weed out duplicate
+ * packets.
+ */
+ ++start_time->tv_usec;
+ }
+ }
+
+ pcap_close( p );
+}
+
+
+/* Translates a timestamp to the time format specified by the user.
+ * Returns a pointer to the translation residing in a static buffer.
+ * There are two such buffers, which are alternated on subseqeuent
+ * calls, so two calls may be made to this routine without worrying
+ * about the results of the first call being overwritten by the
+ * results of the second.
+ */
+
+char *
+timestamp_to_string(struct timeval *timestamp)
+{
+ struct tm *t;
+#define NUM_BUFFERS 2
+ static char buffers[NUM_BUFFERS][128];
+ static int buffer_to_use = 0;
+ char *buf;
+
+ buf = buffers[buffer_to_use];
+ buffer_to_use = (buffer_to_use + 1) % NUM_BUFFERS;
+
+ switch ( timestamp_style )
+ {
+ case TIMESTAMP_RAW:
+ sprintf(buf, "%ld.%ld", timestamp->tv_sec, timestamp->tv_usec);
+ break;
+
+ case TIMESTAMP_READABLE:
+ t = localtime((time_t *) &timestamp->tv_sec);
+ strcpy( buf, asctime( t ) );
+ buf[24] = '\0'; /* nuke final newline */
+ break;
+
+ case TIMESTAMP_PARSEABLE:
+ t = localtime((time_t *) &timestamp->tv_sec);
+ 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 ) );
+}
+
+void
+usage(void)
+{
+ (void)fprintf(stderr, "tcpslice for tcpdump version %d.%d\n",
+ VERSION_MAJOR, VERSION_MINOR);
+ (void)fprintf(stderr,
+"Usage: tcpslice [-dRrt] [-w file] [start-time [end-time]] file ... \n");
+
+ exit(-1);
+}
+
diff --git a/usr.sbin/tcpdump/tcpslice/tcpslice.h b/usr.sbin/tcpdump/tcpslice/tcpslice.h
new file mode 100644
index 0000000..14c69a7
--- /dev/null
+++ b/usr.sbin/tcpdump/tcpslice/tcpslice.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 1987-1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University nor the names of its contributors may be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+
+#include <net/bpf.h>
+
+#include <ctype.h>
+#ifdef SOLARIS
+#include <fcntl.h>
+#endif
+#ifdef __STDC__
+#include <stdlib.h>
+#endif
+#include <stdio.h>
+#if __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+#include <string.h>
+#include <unistd.h>
+
+#include "pcap.h"
+#include "version.h"
+
+
+time_t gwtm2secs( struct tm *tm );
+
+int sf_find_end( struct pcap *p, struct timeval *first_timestamp,
+ struct timeval *last_timestamp );
+int sf_timestamp_less_than( struct timeval *t1, struct timeval *t2 );
+int sf_find_packet( struct pcap *p,
+ struct timeval *min_time, long min_pos,
+ struct timeval *max_time, long max_pos,
+ struct timeval *desired_time );
+
+void error(const char *fmt, ...);
diff --git a/usr.sbin/tcpdump/tcpslice/util.c b/usr.sbin/tcpdump/tcpslice/util.c
new file mode 100644
index 0000000..6596f6e
--- /dev/null
+++ b/usr.sbin/tcpdump/tcpslice/util.c
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 1988-1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that: (1) source code distributions
+ * retain the above copyright notice and this paragraph in its entirety, (2)
+ * distributions including binary code include the above copyright notice and
+ * this paragraph in its entirety in the documentation or other materials
+ * provided with the distribution, and (3) all advertising materials mentioning
+ * features or use of this software display the following acknowledgement:
+ * ``This product includes software developed by the University of California,
+ * Lawrence Berkeley Laboratory and its contributors.'' Neither the name of
+ * the University nor the names of its contributors may be used to endorse
+ * or promote products derived from this software without specific prior
+ * written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#if !defined(lint) && !defined(__GNUC__)
+static char rcsid[] =
+ "@(#) $Header: /home/ncvs/src/usr.sbin/tcpdump/tcpslice/util.c,v 1.1 1995/03/08 12:53:42 olah Exp $ (LBL)";
+#endif
+
+#include "tcpslice.h"
+
+/* VARARGS */
+void
+#if __STDC__
+error(const char *fmt, ...)
+#else
+error(fmt, va_alist)
+ char *fmt;
+ va_dcl
+#endif
+{
+ va_list ap;
+
+ (void)fprintf(stderr, "tcpslice: ");
+#if __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+ (void)vfprintf(stderr, fmt, ap);
+ va_end(ap);
+ if (*fmt) {
+ fmt += strlen(fmt);
+ if (fmt[-1] != '\n')
+ (void)fputc('\n', stderr);
+ }
+ exit(1);
+ /* NOTREACHED */
+}
diff --git a/usr.sbin/timed/Makefile b/usr.sbin/timed/Makefile
new file mode 100644
index 0000000..a89ab16
--- /dev/null
+++ b/usr.sbin/timed/Makefile
@@ -0,0 +1,5 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+SUBDIR= timed timedc
+
+.include <bsd.subdir.mk>
diff --git a/usr.sbin/timed/SMM.doc/timed/Makefile b/usr.sbin/timed/SMM.doc/timed/Makefile
new file mode 100644
index 0000000..22ff6e6
--- /dev/null
+++ b/usr.sbin/timed/SMM.doc/timed/Makefile
@@ -0,0 +1,11 @@
+# @(#)Makefile 8.1 (Berkeley) 6/8/93
+
+DIR= smm/12.timed
+SRCS= timed.ms
+MACROS= -ms
+PRINTER=Pdp
+
+paper.${PRINTER}: ${SRCS}
+ ${SOELIM} ${SRCS} | ${TBL} | ${ROFF} > ${.TARGET}
+
+.include <bsd.doc.mk>
diff --git a/usr.sbin/timed/SMM.doc/timed/date b/usr.sbin/timed/SMM.doc/timed/date
new file mode 100644
index 0000000..e4e4d58
--- /dev/null
+++ b/usr.sbin/timed/SMM.doc/timed/date
@@ -0,0 +1,53 @@
+.\" Copyright (c) 1986, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)date 8.1 (Berkeley) 6/8/93
+.\"
+.ft B
+.TS
+center;
+ce | ce | ce | ce
+| c | c | c | s |
+| c s s s |.
+Byte 1 Byte 2 Byte 3 Byte 4
+=
+Type Version No. Sequence No.
+_
+Seconds of Time to Set
+_
+Microseconds of Time to Set
+_
+Machine Name
+_
+\&. . .
+_
+.TE
+.ft R
diff --git a/usr.sbin/timed/SMM.doc/timed/loop b/usr.sbin/timed/SMM.doc/timed/loop
new file mode 100644
index 0000000..11ccb4d
--- /dev/null
+++ b/usr.sbin/timed/SMM.doc/timed/loop
@@ -0,0 +1,54 @@
+.\" Copyright (c) 1986, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)loop 8.1 (Berkeley) 6/8/93
+.\"
+.ft B
+.TS
+center;
+ce | ce | ce | ce
+| c | c | c | s |
+| c | c s s |
+| c s s s |.
+Byte 1 Byte 2 Byte 3 Byte 4
+=
+Type Version No. Sequence No.
+_
+Hop Count ( unused )
+_
+( unused )
+_
+Machine Name
+_
+\&. . .
+_
+.TE
+.ft R
diff --git a/usr.sbin/timed/SMM.doc/timed/spell.ok b/usr.sbin/timed/SMM.doc/timed/spell.ok
new file mode 100644
index 0000000..8ecfe15
--- /dev/null
+++ b/usr.sbin/timed/SMM.doc/timed/spell.ok
@@ -0,0 +1,34 @@
+ACK
+ADJTIME
+Adjtime
+CS
+CSELT
+Candidature
+DATEACK
+DoD
+Gusella
+MASTERACK
+MASTERREQ
+MASTERUP
+MSITE
+MSITEREQ
+Protocol''SMM:22
+Riccardo
+SETDATE
+SETDATEREQ
+SETTIME
+SLAVEUP
+SMM:22
+Stefano
+TRACEOFF
+TRACEON
+TSP
+Timedc
+UDP
+USENIX
+Zatti
+candidature
+ce
+daemon
+daemons
+timedc
diff --git a/usr.sbin/timed/SMM.doc/timed/time b/usr.sbin/timed/SMM.doc/timed/time
new file mode 100644
index 0000000..619d171
--- /dev/null
+++ b/usr.sbin/timed/SMM.doc/timed/time
@@ -0,0 +1,53 @@
+.\" Copyright (c) 1986, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)time 8.1 (Berkeley) 6/8/93
+.\"
+.ft B
+.TS
+center;
+ce | ce | ce | ce
+| c | c | c | s |
+| c s s s |.
+Byte 1 Byte 2 Byte 3 Byte 4
+=
+Type Version No. Sequence No.
+_
+Seconds of Adjustment
+_
+Microseconds of Adjustment
+_
+Machine Name
+_
+\&. . .
+_
+.TE
+.ft R
diff --git a/usr.sbin/timed/SMM.doc/timed/timed.ms b/usr.sbin/timed/SMM.doc/timed/timed.ms
new file mode 100644
index 0000000..f5a3b19
--- /dev/null
+++ b/usr.sbin/timed/SMM.doc/timed/timed.ms
@@ -0,0 +1,504 @@
+.\" Copyright (c) 1986, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)timed.ms 8.1 (Berkeley) 6/8/93
+.\"
+.TL
+The Berkeley
+.UX
+.br
+Time Synchronization Protocol
+.AU
+Riccardo Gusella, Stefano Zatti, and James M. Bloom
+.AI
+Computer Systems Research Group
+Computer Science Division
+Department of Electrical Engineering and Computer Science
+University of California, Berkeley
+Berkeley, CA 94720
+.FS
+This work was sponsored by the Defense Advanced Research Projects Agency
+(DoD), monitored by the Naval Electronics Systems
+Command under contract No. N00039-84-C-0089, and by the Italian CSELT
+Corporation.
+The views and conclusions contained in this document are those of the
+authors and should not be interpreted as representing official policies,
+either expressed or implied, of the Defense Research Projects Agency,
+of the US Government, or of CSELT.
+.FE
+.LP
+.OH 'The Berkeley UNIX Time Synchronization Protocol''SMM:12-%'
+.EH 'SMM:12-%''The Berkeley UNIX Time Synchronization Protocol'
+.SH
+Introduction
+.PP
+The Time Synchronization Protocol (TSP)
+has been designed for specific use by the program \fItimed\fP,
+a local area network clock synchronizer for
+the UNIX 4.3BSD operating
+system.
+Timed is built on the DARPA UDP protocol [4] and
+is based on a master slave scheme.
+.PP
+TSP serves a dual purpose.
+First, it supports messages for the synchronization of the clocks
+of the various hosts in a local area network.
+Second, it supports messages for the election that occurs
+among slave time daemons when, for any reason, the master disappears.
+The synchronization mechanism and the election procedure
+employed by the program timed are described
+in other documents [1,2,3].
+.PP
+Briefly, the synchronization software, which works in a
+local area network, consists of a collection of \fItime daemons\fP
+(one per machine) and is based on a master-slave
+structure.
+The present implementation keeps processor clocks synchronized
+within 20 milliseconds.
+A \fImaster time daemon\fP measures the time
+difference between the clock of the machine on which it
+is running and those of all other machines. The current implementation
+uses ICMP \fITime Stamp Requests\fP [5] to measure the clock difference
+between machines.
+The master computes the \fInetwork time\fP as the average of the
+times provided by nonfaulty clocks.\**
+.FS
+A clock is considered to be faulty when its value
+is more than a small specified
+interval apart from the majority of the clocks
+of the machines on the same network.
+See [1,2] for more details.
+.FE
+It then sends to each \fIslave time daemon\fP the
+correction that should be performed on the clock of its machine.
+This process is repeated periodically.
+Since the correction is expressed as a time difference rather than an
+absolute time, transmission delays do not interfere with synchronization.
+When a machine comes up and joins the network,
+it starts a slave time daemon, which
+will ask the master for the correct time and will reset the machine's clock
+before any user activity can begin.
+The time daemons therefore maintain a single network time in spite of
+the drift of clocks away from each other.
+.PP
+Additionally, a time daemon on gateway machines may run as
+a \fIsubmaster\fP.
+A submaster time daemon functions as a slave on one network that
+already has a master and as master on other networks.
+In addition, a submaster is responsible for propagating broadcast
+packets from one network to the other.
+.PP
+To ensure that service provided is continuous and reliable,
+it is necessary to implement an election algorithm that will elect a
+new master should the machine running the current master crash, the master
+terminate (for example, because of a run-time error), or the network be
+partitioned.
+Under our algorithm, slaves are able to realize when the master has
+stopped functioning and to elect a new master from among themselves.
+It is important to note that since the failure of the master results
+only in a gradual divergence of clock values, the election
+need not occur immediately.
+.PP
+All the communication occurring among time daemons uses the TSP
+protocol.
+While some messages need not be sent in a reliable way,
+most communication in TSP requires reliability not provided by the underlying
+protocol.
+Reliability is achieved by the use of acknowledgements, sequence numbers, and
+retransmission when message losses occur.
+When a message that requires acknowledgment is not acknowledged after
+multiple attempts,
+the time daemon that has sent the message will assume that the
+addressee is down.
+This document will not describe the details of how reliability is
+implemented, but will only point out when
+a message type requires a reliable transport mechanism.
+.PP
+The message format in TSP is the same for all message types;
+however, in some instances, one or more fields are not used.
+The next section describes the message format.
+The following sections describe
+in detail the different message types, their use and the contents
+of each field. NOTE: The message format is likely to change in
+future versions of timed.
+.sp 2
+.SH
+Message Format
+.PP
+All fields are based upon 8-bit bytes. Fields should be sent in
+network byte order if they are more than one byte long.
+The structure of a TSP message is the following:
+.IP 1)
+A one byte message type.
+.IP 2)
+A one byte version number, specifying the protocol version which the
+message uses.
+.IP 3)
+A two byte sequence number to be used for recognizing duplicate messages
+that occur when messages are retransmitted.
+.IP 4)
+Eight bytes of packet specific data. This field contains two 4 byte time
+values, a one byte hop count, or may be unused depending on the type
+of the packet.
+.IP 5)
+A zero-terminated string of up to 256 \s-2ASCII\s+2 characters with the name of
+the machine sending the message.
+.PP
+The following charts describe the message types,
+show their fields, and explain their usages.
+For the purpose of the following discussion, a time daemon can
+be considered to be in
+one of three states: slave, master, or candidate for election to master.
+Also, the term \fIbroadcast\fP refers to
+the sending of a message to all active time daemons.
+.sp 1
+.DS L
+.SH
+Adjtime Message
+.so time
+.LP
+Type: TSP_ADJTIME (1)
+.sp 1
+.PP
+The master sends this message to a slave to communicate
+the difference between
+the clock of the slave and
+the network time the master has just computed.
+The slave will accordingly
+adjust the time of its machine.
+This message requires an acknowledgment.
+.sp 1
+.DE
+.DS L
+.SH
+Acknowledgment Message
+.so unused
+.LP
+Type: TSP_ACK (2)
+.sp 1
+.PP
+Both the master and the slaves use this message for
+acknowledgment only.
+It is used in several different contexts, for example
+in reply to an Adjtime message.
+.sp 1
+.DE
+.DS L
+.SH
+Master Request Message
+.so unused
+.LP
+Type: TSP_MASTERREQ (3)
+.sp 1
+.PP
+A newly-started time daemon broadcasts this message to
+locate a master. No other action is implied by this packet.
+It requires a Master Acknowledgment.
+.sp 1
+.DE
+.DS L
+.SH
+Master Acknowledgement
+.so unused
+.LP
+Type: TSP_MASTERACK (4)
+.sp 1
+.PP
+The master sends this message to acknowledge the Master Request message
+and the Conflict Resolution Message.
+.sp 1
+.DE
+.DS L
+.SH
+Set Network Time Message
+.so date
+.LP
+Type: TSP_SETTIME (5)
+.sp 1
+.PP
+The master sends this message to slave time daemons to set their time.
+This packet is sent to newly started time daemons and when the network
+date is changed.
+It contains the master's time as an approximation of the network time.
+It requires an acknowledgment.
+The next
+synchronization round will eliminate the small time difference
+caused by the random delay in the communication channel.
+.sp 1
+.DE
+.DS L
+.SH
+Master Active Message
+.so unused
+.LP
+Type: TSP_MASTERUP (6)
+.sp 1
+.PP
+The master broadcasts this message to
+solicit the names of the active slaves.
+Slaves will reply with a Slave Active message.
+.sp 1
+.DE
+.DS L
+.SH
+Slave Active Message
+.so unused
+.LP
+Type: TSP_SLAVEUP (7)
+.sp 1
+.PP
+A slave sends this message to the master in answer to a Master Active message.
+This message is also sent when a new slave starts up to inform the master that
+it wants to be synchronized.
+.sp 1
+.DE
+.DS L
+.SH
+Master Candidature Message
+.so unused
+.LP
+Type: TSP_ELECTION (8)
+.sp 1
+.PP
+A slave eligible to become a master broadcasts this message when its election
+timer expires.
+The message declares that the slave wishes to become the new master.
+.sp 1
+.DE
+.DS L
+.SH
+Candidature Acceptance Message
+.so unused
+.LP
+Type: TSP_ACCEPT (9)
+.sp 1
+.PP
+A slave sends this message to accept the candidature of the time daemon
+that has broadcast an Election message.
+The candidate will add the slave's name to the list of machines that it
+will control should it become the master.
+.sp 1
+.DE
+.DS L
+.SH
+Candidature Rejection Message
+.so unused
+.LP
+Type: TSP_REFUSE (10)
+.sp 1
+.PP
+After a slave accepts the candidature of a time daemon, it will reply
+to any election messages from other slaves
+with this message.
+This rejects any candidature other than the first received.
+.sp 1
+.DE
+.DS L
+.SH
+Multiple Master Notification Message
+.so unused
+.LP
+Type: TSP_CONFLICT (11)
+.sp 1
+.PP
+When two or more masters reply to a Master Request message, the slave
+uses this message to inform one of them that more than one master exists.
+.sp 1
+.DE
+.DS L
+.SH
+Conflict Resolution Message
+.so unused
+.LP
+Type: TSP_RESOLVE (12)
+.sp 1
+.PP
+A master which has been informed of the existence of other masters
+broadcasts this message to determine who the other masters are.
+.sp 1
+.DE
+.DS L
+.SH
+Quit Message
+.so unused
+.LP
+Type: TSP_QUIT (13)
+.sp 1
+.PP
+This message is sent by the master in three different contexts:
+1) to a candidate that broadcasts an Master Candidature message,
+2) to another master when notified of its existence,
+3) to another master if a loop is detected.
+In all cases, the recipient time daemon will become a slave.
+This message requires an acknowledgement.
+.sp 1
+.DE
+.DS L
+.SH
+Set Date Message
+.so date
+.LP
+Type: TSP_SETDATE (22)
+.sp 1
+.PP
+The program \fIdate\fP\|(1) sends this message to the local time daemon
+when a super-user wants to set the network date.
+If the local time daemon is the master, it will set the date;
+if it is a slave, it will communicate the desired date to the master.
+.sp 1
+.DE
+.DS L
+.SH
+Set Date Request Message
+.so date
+.LP
+Type: TSP_SETDATEREQ (23)
+.sp 1
+.PP
+A slave that has received a Set Date message will communicate the
+desired date to the master using this message.
+.sp 1
+.DE
+.DS L
+.SH
+Set Date Acknowledgment Message
+.so unused
+.LP
+Type: TSP_DATEACK (16)
+.sp 1
+.PP
+The master sends this message to a slave in acknowledgment of a
+Set Date Request Message.
+The same message is sent by the local time daemon to the program
+\fIdate(1)\fP to confirm that the network date has been set by the
+master.
+.sp 1
+.DE
+.DS L
+.SH
+Start Tracing Message
+.so unused
+.LP
+Type: TSP_TRACEON (17)
+.sp 1
+.PP
+The controlling program \fItimedc\fP sends this message to the local
+time daemon to start the recording in a system file of
+all messages received.
+.sp 1
+.DE
+.DS L
+.SH
+Stop Tracing Message
+.so unused
+.LP
+Type: TSP_TRACEOFF (18)
+.sp 1
+.PP
+\fITimedc\fP sends this message to the local
+time daemon to stop the recording of
+messages received.
+.sp 1
+.DE
+.DS L
+.SH
+Master Site Message
+.so unused
+.LP
+Type: TSP_MSITE (19)
+.sp 1
+.PP
+\fITimedc\fP sends this message to the local time daemon to find out
+where the master is running.
+.sp 1
+.DE
+.DS L
+.SH
+Remote Master Site Message
+.so unused
+.LP
+Type: TSP_MSITEREQ (20)
+.sp 1
+.PP
+A local time daemon broadcasts this message to find the location
+of the master.
+It then uses the Acknowledgement message to
+communicate this location to \fItimedc\fP.
+.sp 1
+.DE
+.DS L
+.SH
+Test Message
+.so unused
+.LP
+Type: TSP_TEST (21)
+.sp 1
+.PP
+For testing purposes, \fItimedc\fP sends this message to a slave
+to cause its election timer to expire. NOTE: \fItimed\fP
+is not normally compiled to support this.
+.sp 1
+.DE
+.SH
+.DS L
+.SH
+Loop Detection Message
+.so loop
+.LP
+Type: TSP_LOOP (24)
+.sp 1
+.PP
+This packet is initiated by all masters occasionally to attempt to detect loops.
+All submasters forward this packet onto the networks over which they are master.
+If a master receives a packet it sent out initially,
+it knows that a loop exists and tries to correct the problem.
+.DE
+.SH
+References
+.IP 1.
+R. Gusella and S. Zatti,
+\fITEMPO: A Network Time Controller for Distributed Berkeley UNIX System\fP,
+USENIX Summer Conference Proceedings, Salt Lake City, June 1984.
+.IP 2.
+R. Gusella and S. Zatti, \fIClock Synchronization in a Local Area Network\fP,
+University of California, Berkeley, Technical Report, \fIto appear\fP.
+.IP 3.
+R. Gusella and S. Zatti,
+\fIAn Election Algorithm for a Distributed Clock Synchronization Program\fP,
+University of California, Berkeley, CS Technical Report #275, Dec. 1985.
+.IP 4.
+Postel, J., \fIUser Datagram Protocol\fP, RFC 768.
+Network Information Center, SRI International, Menlo Park, California,
+August 1980.
+.IP 5.
+Postel, J., \fIInternet Control Message Protocol\fP, RFC 792.
+Network Information Center, SRI International, Menlo Park, California,
+September 1981.
diff --git a/usr.sbin/timed/SMM.doc/timed/unused b/usr.sbin/timed/SMM.doc/timed/unused
new file mode 100644
index 0000000..adadfc3
--- /dev/null
+++ b/usr.sbin/timed/SMM.doc/timed/unused
@@ -0,0 +1,53 @@
+.\" Copyright (c) 1986, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)unused 8.1 (Berkeley) 6/8/93
+.\"
+.ft B
+.TS
+center;
+ce | ce | ce | ce
+| c | c | c | s |
+| c s s s |.
+Byte 1 Byte 2 Byte 3 Byte 4
+=
+Type Version No. Sequence No.
+_
+( unused )
+_
+( unused )
+_
+Machine Name
+_
+\&. . .
+_
+.TE
+.ft R
diff --git a/usr.sbin/timed/SMM.doc/timedop/Makefile b/usr.sbin/timed/SMM.doc/timedop/Makefile
new file mode 100644
index 0000000..ae43850
--- /dev/null
+++ b/usr.sbin/timed/SMM.doc/timedop/Makefile
@@ -0,0 +1,7 @@
+# @(#)Makefile 8.1 (Berkeley) 6/8/93
+
+DIR= smm/11.timedop
+SRCS= timed.ms
+MACROS= -ms
+
+.include <bsd.doc.mk>
diff --git a/usr.sbin/timed/SMM.doc/timedop/timed.ms b/usr.sbin/timed/SMM.doc/timedop/timed.ms
new file mode 100644
index 0000000..feea0b5
--- /dev/null
+++ b/usr.sbin/timed/SMM.doc/timedop/timed.ms
@@ -0,0 +1,279 @@
+.\" Copyright (c) 1986, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)timed.ms 8.1 (Berkeley) 6/8/93
+.\"
+.TL
+Timed Installation and Operation Guide
+.AU
+Riccardo Gusella, Stefano Zatti, James M. Bloom
+.AI
+Computer Systems Research Group
+Computer Science Division
+Department of Electrical Engineering and Computer Science
+University of California, Berkeley
+Berkeley, CA 94720
+.AU
+Kirk Smith
+.AI
+Engineering Computer Network
+Department of Electrical Engineering
+Purdue University
+West Lafayette, IN 47906
+.FS
+This work was sponsored by the Defense Advanced Research Projects Agency
+(DoD), monitored by the Naval Electronics Systems
+Command under contract No. N00039-84-C-0089, and by the CSELT
+Corporation of Italy.
+The views and conclusions contained in this document are those of the
+authors and should not be interpreted as representing official policies,
+either expressed or implied, of the Defense Research Projects Agency,
+of the US Government, or of CSELT.
+.FE
+.LP
+.EH 'SMM:11-%''Timed Installation and Operation'
+.OH 'Timed Installation and Operation''SMM:11-%'
+.SH
+Introduction
+.PP
+The clock synchronization service for
+the UNIX 4.3BSD operating system is composed of a collection of
+time daemons (\fItimed\fP) running on the machines in a local
+area network.
+The algorithms implemented by the service is based on a master-slave scheme.
+The time daemons communicate with each other using the
+\fITime Synchronization Protocol\fP (TSP) which
+is built on the DARPA UDP protocol and described in detail in [4].
+.PP
+A time daemon has a twofold function.
+First, it supports the synchronization of the clocks
+of the various hosts in a local area network.
+Second, it starts (or takes part in) the election that occurs
+among slave time daemons when, for any reason, the master disappears.
+The synchronization mechanism and the election procedure
+employed by the program \fItimed\fP are described
+in other documents [1,2,3].
+The next paragraphs are a brief overview of how the time daemon works.
+This document is mainly concerned with the administrative and technical
+issues of running \fItimed\fP at a particular site.
+.PP
+A \fImaster time daemon\fP measures the time
+differences between the clock of the machine on which it
+is running and those of all other machines.
+The master computes the \fInetwork time\fP as the average of the
+times provided by nonfaulty clocks.\**
+.FS
+A clock is considered to be faulty when its value
+is more than a small specified
+interval apart from the majority of the clocks
+of the other machines [1,2].
+.FE
+It then sends to each \fIslave time daemon\fP the
+correction that should be performed on the clock of its machine.
+This process is repeated periodically.
+Since the correction is expressed as a time difference rather than an
+absolute time, transmission delays do not interfere with
+the accuracy of the synchronization.
+When a machine comes up and joins the network,
+it starts a slave time daemon which
+will ask the master for the correct time and will reset the machine's clock
+before any user activity can begin.
+The time daemons are able to maintain a single network time in spite of
+the drift of clocks away from each other.
+The present implementation keeps processor clocks synchronized
+within 20 milliseconds.
+.PP
+To ensure that the service provided is continuous and reliable,
+it is necessary to implement an election algorithm to elect a
+new master should the machine running the current master crash, the master
+terminate (for example, because of a run-time error), or
+the network be partitioned.
+Under our algorithm, slaves are able to realize when the master has
+stopped functioning and to elect a new master from among themselves.
+It is important to note that, since the failure of the master results
+only in a gradual divergence of clock values, the election
+need not occur immediately.
+.PP
+The machines that are gateways between distinct local area
+networks require particular care.
+A time daemon on such machines may act as a \fIsubmaster\fP.
+This artifact depends on the current inability of
+transmission protocols to broadcast a message on a network
+other than the one to which the broadcasting machine is connected.
+The submaster appears as a slave on one network, and as a master
+on one or more of the other networks to which it is connected.
+.PP
+A submaster classifies each network as one of three types.
+A \fIslave network\fP is a network on which the submaster acts as a slave.
+There can only be one slave network.
+A \fImaster network\fP is a network on which the submaster acts as a master.
+An \fIignored network\fP is any other network which already has a valid master.
+The submaster tries periodically to become master on an ignored
+network, but gives up immediately if a master already exists.
+.SH
+Guidelines
+.PP
+While the synchronization algorithm is quite general, the election
+one, requiring a broadcast mechanism, puts constraints on
+the kind of network on which time daemons can run.
+The time daemon will only work on networks with broadcast capability
+augmented with point-to-point links.
+Machines that are only connected to point-to-point,
+non-broadcast networks may not use the time daemon.
+.PP
+If we exclude submasters, there will normally be, at most, one master time
+daemon in a local area internetwork.
+During an election, only one of the slave time daemons
+will become the new master.
+However, because of the characteristics of its machine,
+a slave can be prevented from becoming the master.
+Therefore, a subset of machines must be designated as potential
+master time daemons.
+A master time daemon will require CPU resources
+proportional to the number of slaves, in general, more than
+a slave time daemon, so it may be advisable to limit master time
+daemons to machines with more powerful processors or lighter loads.
+Also, machines with inaccurate clocks should not be used as masters.
+This is a purely administrative decision: an organization may
+well allow all of its machines to run master time daemons.
+.PP
+At the administrative level, a time daemon on a machine
+with multiple network interfaces, may be told to ignore all
+but one network or to ignore one network.
+This is done with the \fI\-n network\fP and \fI\-i network\fP
+options respectively at start-up time.
+Typically, the time daemon would be instructed to ignore all but
+the networks belonging to the local administrative control.
+.PP
+There are some limitations to the current
+implementation of the time daemon.
+It is expected that these limitations will be removed in future releases.
+The constant NHOSTS in /usr/src/etc/timed/globals.h limits the
+maximum number of machines that may be directly controlled by one
+master time daemon.
+The current maximum is 29 (NHOSTS \- 1).
+The constant must be changed and the program recompiled if a site wishes to
+run \fItimed\fP on a larger (inter)network.
+.PP
+In addition, there is a \fIpathological situation\fP to
+be avoided at all costs, that might occur when
+time daemons run on multiply-connected local area networks.
+In this case, as we have seen, time daemons running on gateway machines
+will be submasters and they will act on some of those
+networks as master time daemons.
+Consider machines A and B that are both gateways between
+networks X and Y.
+If time daemons were started on both A and B without constraints, it would be
+possible for submaster time daemon A to be a slave on network X
+and the master on network Y, while submaster time daemon B is a slave on
+network Y and the master on network X.
+This \fIloop\fP of master time daemons will not function properly
+or guarantee a unique time on both networks, and will cause
+the submasters to use large amounts of system resources in the form
+of network bandwidth and CPU time.
+In fact, this kind of \fIloop\fP can also be generated with more
+than two master time daemons,
+when several local area networks are interconnected.
+.SH
+Installation
+.PP
+In order to start the time daemon on a given machine,
+the following lines should be
+added to the \fIlocal daemons\fP section in the file \fI/etc/rc.local\fP:
+.sp 2
+.in 1i
+.nf
+if [ -f /etc/timed ]; then
+ /etc/timed \fIflags\fP & echo -n ' timed' >/dev/console
+fi
+.fi
+.in -1i
+.sp
+.LP
+In any case, they must appear after the network
+is configured via ifconfig(8).
+.PP
+Also, the file \fI/etc/services\fP should contain the following
+line:
+.sp 2
+.ti 1i
+timed 525/udp timeserver
+.sp
+.LP
+The \fIflags\fP are:
+.IP "-n network" 13
+to consider the named network.
+.IP "-i network"
+to ignore the named network.
+.IP -t
+to place tracing information in \fI/usr/adm/timed.log\fP.
+.IP -M
+to allow this time daemon to become a master.
+A time daemon run without this option will be forced in the state of
+slave during an election.
+.SH
+Daily Operation
+.PP
+\fITimedc(8)\fP is used to control the operation of the time daemon.
+It may be used to:
+.IP \(bu
+measure the differences between machines' clocks,
+.IP \(bu
+find the location where the master \fItimed\fP is running,
+.IP \(bu
+cause election timers on several machines to expire at the same time,
+.IP \(bu
+enable or disable tracing of messages received by \fItimed\fP.
+.LP
+See the manual page on \fItimed\fP\|(8) and \fItimedc\fP\|(8)
+for more detailed information.
+.PP
+The \fIdate(1)\fP command can be used to set the network date.
+In order to set the time on a single machine, the \fI-n\fP flag
+can be given to date(1).
+.bp
+.SH
+References
+.IP 1.
+R. Gusella and S. Zatti,
+\fITEMPO: A Network Time Controller for Distributed Berkeley UNIX System\fP,
+USENIX Summer Conference Proceedings, Salt Lake City, June 1984.
+.IP 2.
+R. Gusella and S. Zatti, \fIClock Synchronization in a Local Area Network\fP,
+University of California, Berkeley, Technical Report, \fIto appear\fP.
+.IP 3.
+R. Gusella and S. Zatti,
+\fIAn Election Algorithm for a Distributed Clock Synchronization Program\fP,
+University of California, Berkeley, CS Technical Report #275, Dec. 1985.
+.IP 4.
+R. Gusella and S. Zatti,
+\fIThe Berkeley UNIX 4.3BSD Time Synchronization Protocol\fP,
+UNIX Programmer's Manual, 4.3 Berkeley Software Distribution, Volume 2c.
diff --git a/usr.sbin/timed/timed/CHANGES b/usr.sbin/timed/timed/CHANGES
new file mode 100644
index 0000000..773f477
--- /dev/null
+++ b/usr.sbin/timed/timed/CHANGES
@@ -0,0 +1,144 @@
+# @(#)CHANGES 5.1 (Berkeley) 5/11/93
+
+This new version is almost identical to the timed and timedc code
+that has been shipped for years by a workstation vendor.
+
+Among the many changes:
+
+improve `timedc msite` to accept a list of hostnames.
+
+change slave-masters to answer the packets generated by `timedc msite`
+ with the name of the real master, not their own. This makes it
+ possible to "chase the chain" of slave servers to the ultimate
+ master.
+
+much improve the log caused by `timedc trace on`:
+ -made `timed -t` work.
+ -suppression of repeated entries, which both slowed down the daemon
+ (sometimes catastrophically) and tended to make disks fill up
+ even more quickly.
+ -better time stamps on log entries
+ -more messages
+ -dump information about slaves, master, and so on each time
+ a message asking the log be turned on is received, and
+ when the log is turned off.
+ -fewer CPU cycles
+
+use a hash table to keep track of slaves, instead of the stupid linear
+ list. This becomes handy with hundreds of slaves, instead of
+ the original design limit of "a room with a few VAX's."
+
+separate the main protocol timer from that used to look for other networks
+ to master.
+
+time stamp packets received by the daemon, so that time corrections
+ are not made (even more) inaccurate by waiting in the internal,
+ timed queue while the daemon is processing other messages.
+
+made -n and -i work with subnets not named in /etc/networks
+
+compute the median of the measured clocks, instead of the average
+ of "good" times.
+
+vastly improve the accuracy of the clock difference measure by
+ `timedc clockdiff`.
+
+use adjtime() when possible, and directly set the clock only when
+ necessary.
+
+when the requested adjustment is small, perform only part of it, to
+ damp oscillations and improve the long term accuracy of the
+ adjustments.
+
+fix uncounted core-dumps on machines that do not allow dereferencing 0
+ in both the daemon and timedc.
+
+fix "master loop detection".
+
+fix several cases in which multi-homed masters could get into shouting
+ matches, consuming all available network bandwidth and CPU cycles
+ (which ever runs out first), and convincing all bystanders to stop
+ advancing their own clocks.
+
+refuse to behave badly when other machines do. Instead of arguing forever,
+ go off and sulk when other machines refuse to play by the rules.
+
+increase the maximum number of clients.
+
+add "-F host,host2,..." to "freerun" or "trust" only some hosts. This
+ is handy both when only some machines should be trusted to let
+ root use the `date` command to change time in the network.
+
+ It is also handy when one machine has some other way of adjusting
+ its clock, whether NTP or a direct radio or atomic connection.
+ "-F localhost" causes `timed` to "trust" only itself.
+
+ It is also handy to build a hierarchy of timed masters crossing
+ networks. The TSP protocol has no provision of "goodness of clock",
+ no natural way to completely heal network paritions. Judicious
+ use of -F or -G can cause each gateway to trust only itself and
+ machines closer to a central machine with a radio or atomic clock.
+
+add #ifdef code that supports NIS "netgroups" of trusted hosts, which
+ can be easier to administer than -F.
+
+add #ifdef code to compute an aged total adjustment. This can be used
+ in systems that can make long term changes in their system clock
+ frequency, e.g. "timetrim" in the Silicon Graphics kernel.
+
+
+Problems observed by others that are unresolved include:
+
+Practically any users can send to the master TSP messages and this
+ way corrupt the reliability of the system. Authentication
+ of messages should be provided. Unfortunately, that would
+ require changing the protocol with all of the implied
+ compatiblity problems. Fortunately, the new -F and -G args
+ can be used to cause the daemon to ignore time changes from
+ untrusted machines.
+
+MAN. The limit of 1013 on the number of slaves hosts should be doc'ed.
+
+ It should be dynamically allocated with no limit. On a
+ large network, one host could possibly master over many
+ more than 30 hosts. Given the timers in the code and
+ effectively in the protocol, and the time required by each
+ master to talk to each slave, it is not practical to have
+ more than 200-300 slaves. The master cannot keep up because
+ the slave-chatting is single-threaded. when the master
+ gets behind, slaves start demanding elections. To
+ significantly increase the number of slaves would require
+ multi-treading things, and given that a network with more
+ than 300 directly addressable machines has worse problems
+ than keep the time of day right, not worth worrying about.
+
+UGLY,CODE. timedc/cmds.c has a lots of repeated code in it.
+
+**** The first thing is that each command is set up as if it
+ were an individual program taking argc and argv. A more
+ conventional calling style should be used. I don't think
+ any of the routines take more than a couple arguments.
+
+UGLY. fxn definition syntax does't follow convention:
+ has type on same line.
+
+**** It needs to be fixed at least enough that tags
+ will work on it. An entire cleanup might be nice later, but
+ is noncritical.
+
+LOBBY(mildly),CODE: Would be very convenient if date(1) took a
+ +-<number> argument to set the time relatively. With
+ the advent of timed it is now reasonable to synchronize
+ with WWV, which is nearly impossible to do "by hand"
+ with just an absolute date, and scripts are too slow.
+ format could be +-nn...nn.ss, where the '.' is required
+ to remove ambiguity.
+
+**** If you want to do it go ahead. It sounds useful. As far as
+ syntax goes, the normal format for the date should work just
+ fine for this. If the date is preceeded by a plus or minus,
+ the change is relative, otherwise it is absolute.
+
+
+Vernon Schryver.
+vjs@sgi.com
diff --git a/usr.sbin/timed/timed/Makefile b/usr.sbin/timed/timed/Makefile
new file mode 100644
index 0000000..7edc521
--- /dev/null
+++ b/usr.sbin/timed/timed/Makefile
@@ -0,0 +1,13 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+#
+# optional flags are: MEASURE TESTING DEBUG
+
+PROG= timed
+SRCS= acksend.c candidate.c correct.c master.c networkdelta.c readmsg.c \
+ slave.c timed.c byteorder.c measure.c cksum.c
+DPADD= ${LIBUTIL}
+LDADD= -lutil
+MAN8= timed.8
+
+.include "../../Makefile.inc"
+.include <bsd.prog.mk>
diff --git a/usr.sbin/timed/timed/acksend.c b/usr.sbin/timed/timed/acksend.c
new file mode 100644
index 0000000..766505c
--- /dev/null
+++ b/usr.sbin/timed/timed/acksend.c
@@ -0,0 +1,132 @@
+/*-
+ * Copyright (c) 1985, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)acksend.c 8.1 (Berkeley) 6/6/93";
+#endif
+static const char rcsid[] =
+ "$Id: acksend.c,v 1.2 1997/10/22 06:19:48 charnier Exp $";
+#endif /* not lint */
+
+#include "globals.h"
+
+struct tsp *answer;
+
+extern u_short sequence;
+
+void
+xmit(type, seq, addr)
+ int type;
+ u_int seq;
+ struct sockaddr_in *addr;
+{
+ static struct tsp msg;
+
+ msg.tsp_type = type;
+ msg.tsp_seq = seq;
+ msg.tsp_vers = TSPVERSION;
+ (void)strcpy(msg.tsp_name, hostname);
+ bytenetorder(&msg);
+ if (sendto(sock, (char *)&msg, sizeof(struct tsp), 0,
+ (struct sockaddr*)addr, sizeof(struct sockaddr)) < 0) {
+ trace_sendto_err(addr->sin_addr);
+ }
+}
+
+
+/*
+ * Acksend implements reliable datagram transmission by using sequence
+ * numbers and retransmission when necessary.
+ * If `name' is ANYADDR, this routine implements reliable broadcast.
+ *
+ * Because this function calls readmsg(), none of its args may be in
+ * a message provided by readmsg().
+ */
+struct tsp *
+acksend(message, addr, name, ack, net, bad)
+ struct tsp *message; /* this message */
+ struct sockaddr_in *addr; /* to here */
+ char *name;
+ int ack; /* look for this ack */
+ struct netinfo *net; /* receive from this network */
+ int bad; /* 1=losing patience */
+{
+ struct timeval twait;
+ int count;
+ long msec;
+
+ message->tsp_vers = TSPVERSION;
+ message->tsp_seq = sequence;
+ if (trace) {
+ fprintf(fd, "acksend: to %s: ",
+ (name == ANYADDR ? "broadcast" : name));
+ print(message, addr);
+ }
+ bytenetorder(message);
+
+ msec = 200;
+ count = bad ? 1 : 5; /* 5 packets in 6.4 seconds */
+ answer = 0;
+ do {
+ if (!answer) {
+ /* do not go crazy transmitting just because the
+ * other guy cannot keep our sequence numbers
+ * straight.
+ */
+ if (sendto(sock, (char *)message, sizeof(struct tsp),
+ 0, (struct sockaddr*)addr,
+ sizeof(struct sockaddr)) < 0) {
+ trace_sendto_err(addr->sin_addr);
+ break;
+ }
+ }
+
+ mstotvround(&twait, msec);
+ answer = readmsg(ack, name, &twait, net);
+ if (answer != 0) {
+ if (answer->tsp_seq != sequence) {
+ if (trace)
+ fprintf(fd,"acksend: seq # %u!=%u\n",
+ answer->tsp_seq, sequence);
+ continue;
+ }
+ break;
+ }
+
+ msec *= 2;
+ } while (--count > 0);
+ sequence++;
+
+ return(answer);
+}
diff --git a/usr.sbin/timed/timed/byteorder.c b/usr.sbin/timed/timed/byteorder.c
new file mode 100644
index 0000000..3a6ef02
--- /dev/null
+++ b/usr.sbin/timed/timed/byteorder.c
@@ -0,0 +1,86 @@
+/*-
+ * Copyright (c) 1985, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)byteorder.c 8.1 (Berkeley) 6/6/93";
+#endif
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#include "globals.h"
+
+/*
+ * Two routines to do the necessary byte swapping for timed protocol
+ * messages. Protocol is defined in /usr/include/protocols/timed.h
+ */
+void
+bytenetorder(ptr)
+ struct tsp *ptr;
+{
+ ptr->tsp_seq = htons((u_short)ptr->tsp_seq);
+ switch (ptr->tsp_type) {
+
+ case TSP_SETTIME:
+ case TSP_ADJTIME:
+ case TSP_SETDATE:
+ case TSP_SETDATEREQ:
+ ptr->tsp_time.tv_sec = htonl((u_long)ptr->tsp_time.tv_sec);
+ ptr->tsp_time.tv_usec = htonl((u_long)ptr->tsp_time.tv_usec);
+ break;
+
+ default:
+ break; /* nothing more needed */
+ }
+}
+
+void
+bytehostorder(ptr)
+ struct tsp *ptr;
+{
+ ptr->tsp_seq = ntohs((u_short)ptr->tsp_seq);
+ switch (ptr->tsp_type) {
+
+ case TSP_SETTIME:
+ case TSP_ADJTIME:
+ case TSP_SETDATE:
+ case TSP_SETDATEREQ:
+ ptr->tsp_time.tv_sec = ntohl((u_long)ptr->tsp_time.tv_sec);
+ ptr->tsp_time.tv_usec = ntohl((u_long)ptr->tsp_time.tv_usec);
+ break;
+
+ default:
+ break; /* nothing more needed */
+ }
+}
diff --git a/usr.sbin/timed/timed/candidate.c b/usr.sbin/timed/timed/candidate.c
new file mode 100644
index 0000000..b64f296
--- /dev/null
+++ b/usr.sbin/timed/timed/candidate.c
@@ -0,0 +1,167 @@
+/*-
+ * Copyright (c) 1985, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)candidate.c 8.1 (Berkeley) 6/6/93";
+#endif
+static const char rcsid[] =
+ "$Id: candidate.c,v 1.3 1997/10/22 06:19:48 charnier Exp $";
+#endif /* not lint */
+
+#include "globals.h"
+
+/*
+ * `election' candidates a host as master: it is called by a slave
+ * which runs with the -M option set when its election timeout expires.
+ * Note the conservative approach: if a new timed comes up, or another
+ * candidate sends an election request, the candidature is withdrawn.
+ */
+int
+election(net)
+ struct netinfo *net;
+{
+ struct tsp *resp, msg;
+ struct timeval then, wait;
+ struct tsp *answer;
+ struct hosttbl *htp;
+ char loop_lim = 0;
+
+/* This code can get totally confused if it gets slightly behind. For
+ * example, if readmsg() has some QUIT messages waiting from the last
+ * round, we would send an ELECTION message, get the stale QUIT,
+ * and give up. This results in network storms when several machines
+ * do it at once.
+ */
+ wait.tv_sec = 0;
+ wait.tv_usec = 0;
+ while (0 != readmsg(TSP_REFUSE, ANYADDR, &wait, net)) {
+ if (trace)
+ fprintf(fd, "election: discarded stale REFUSE\n");
+ }
+ while (0 != readmsg(TSP_QUIT, ANYADDR, &wait, net)) {
+ if (trace)
+ fprintf(fd, "election: discarded stale QUIT\n");
+ }
+
+again:
+ syslog(LOG_INFO, "This machine is a candidate time master");
+ if (trace)
+ fprintf(fd, "This machine is a candidate time master\n");
+ msg.tsp_type = TSP_ELECTION;
+ msg.tsp_vers = TSPVERSION;
+ (void)strcpy(msg.tsp_name, hostname);
+ bytenetorder(&msg);
+ if (sendto(sock, (char *)&msg, sizeof(struct tsp), 0,
+ (struct sockaddr*)&net->dest_addr,
+ sizeof(struct sockaddr)) < 0) {
+ trace_sendto_err(net->dest_addr.sin_addr);
+ return(SLAVE);
+ }
+
+ (void)gettimeofday(&then, 0);
+ then.tv_sec += 3;
+ for (;;) {
+ (void)gettimeofday(&wait, 0);
+ timevalsub(&wait,&then,&wait);
+ resp = readmsg(TSP_ANY, ANYADDR, &wait, net);
+ if (!resp)
+ return(MASTER);
+
+ switch (resp->tsp_type) {
+
+ case TSP_ACCEPT:
+ (void)addmach(resp->tsp_name, &from,fromnet);
+ break;
+
+ case TSP_MASTERUP:
+ case TSP_MASTERREQ:
+ /*
+ * If another timedaemon is coming up at the same
+ * time, give up, and let it be the master.
+ */
+ if (++loop_lim < 5
+ && !good_host_name(resp->tsp_name)) {
+ (void)addmach(resp->tsp_name, &from,fromnet);
+ suppress(&from, resp->tsp_name, net);
+ goto again;
+ }
+ rmnetmachs(net);
+ return(SLAVE);
+
+ case TSP_QUIT:
+ case TSP_REFUSE:
+ /*
+ * Collision: change value of election timer
+ * using exponential backoff.
+ *
+ * Fooey.
+ * An exponential backoff on a delay starting at
+ * 6 to 15 minutes for a process that takes
+ * milliseconds is silly. It is particularly
+ * strange that the original code would increase
+ * the backoff without bound.
+ */
+ rmnetmachs(net);
+ return(SLAVE);
+
+ case TSP_ELECTION:
+ /* no master for another round */
+ htp = addmach(resp->tsp_name,&from,fromnet);
+ msg.tsp_type = TSP_REFUSE;
+ (void)strcpy(msg.tsp_name, hostname);
+ answer = acksend(&msg, &htp->addr, htp->name,
+ TSP_ACK, 0, htp->noanswer);
+ if (!answer) {
+ syslog(LOG_ERR, "error in election from %s",
+ htp->name);
+ }
+ break;
+
+ case TSP_SLAVEUP:
+ (void)addmach(resp->tsp_name, &from,fromnet);
+ break;
+
+ case TSP_SETDATE:
+ case TSP_SETDATEREQ:
+ break;
+
+ default:
+ if (trace) {
+ fprintf(fd, "candidate: ");
+ print(resp, &from);
+ }
+ break;
+ }
+ }
+}
diff --git a/usr.sbin/timed/timed/cksum.c b/usr.sbin/timed/timed/cksum.c
new file mode 100644
index 0000000..14cbf52
--- /dev/null
+++ b/usr.sbin/timed/timed/cksum.c
@@ -0,0 +1,87 @@
+/*-
+ * Copyright (c) 1985, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)cksum.c 8.1 (Berkeley) 6/6/93";
+#endif
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+/*
+ * I N _ C K S U M
+ *
+ * Checksum routine for Internet Protocol family headers (C Version)
+ *
+ * There is no profit in a specialized version of the checksum
+ * function for any machine where int's are 32 bits and shorts are 16.
+ *
+ * All timed packets are smaller than 32K shorts, so there is no need to
+ * worry about carries except at the end.
+ */
+int
+in_cksum(addr, len)
+ u_short *addr;
+ int len;
+{
+ register int nleft = len;
+ register u_short *w = addr;
+ register u_short answer;
+ register int sum = 0;
+
+ /*
+ * Our algorithm is simple, using a 32 bit accumulator (sum),
+ * we add sequential 16 bit words to it, and at the end, fold
+ * back all the carry bits from the top 16 bits into the lower
+ * 16 bits.
+ */
+ while( nleft > 1 ) {
+ sum += *w++;
+ nleft -= 2;
+ }
+
+ /* mop up an odd byte, if necessary */
+ if( nleft == 1 )
+ sum += (*(u_char *)w) << 8;
+
+ /*
+ * add back carry outs from top 16 bits to low 16 bits
+ */
+ sum = (sum >> 16) + (sum & 0xffff); /* add hi 16 to low 16 */
+ sum += (sum >> 16); /* add carry */
+ answer = ~sum; /* truncate to 16 bits */
+ return (answer);
+}
diff --git a/usr.sbin/timed/timed/correct.c b/usr.sbin/timed/timed/correct.c
new file mode 100644
index 0000000..f28d103
--- /dev/null
+++ b/usr.sbin/timed/timed/correct.c
@@ -0,0 +1,294 @@
+/*-
+ * Copyright (c) 1985, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)correct.c 8.1 (Berkeley) 6/6/93";
+#endif
+static const char rcsid[] =
+ "$Id: correct.c,v 1.2 1997/10/22 06:19:48 charnier Exp $";
+#endif /* not lint */
+
+#include "globals.h"
+#include <math.h>
+#include <sys/types.h>
+#include <sys/times.h>
+#ifdef sgi
+#include <sys/syssgi.h>
+#endif /* sgi */
+
+static void adjclock __P((struct timeval *));
+
+/*
+ * sends to the slaves the corrections for their clocks after fixing our
+ * own
+ */
+void
+correct(avdelta)
+ long avdelta;
+{
+ struct hosttbl *htp;
+ int corr;
+ struct timeval adjlocal;
+ struct tsp to;
+ struct tsp *answer;
+
+ mstotvround(&adjlocal, avdelta);
+
+ for (htp = self.l_fwd; htp != &self; htp = htp->l_fwd) {
+ if (htp->delta != HOSTDOWN) {
+ corr = avdelta - htp->delta;
+/* If the other machine is off in the weeds, set its time directly.
+ * If a slave gets the wrong day, the original code would simply
+ * fix the minutes. If you fix a network partition, you can get
+ * into such situations.
+ */
+ if (htp->need_set
+ || corr >= MAXADJ*1000
+ || corr <= -MAXADJ*1000) {
+ htp->need_set = 0;
+ (void)gettimeofday(&to.tsp_time,0);
+ timevaladd(&to.tsp_time, &adjlocal);
+ to.tsp_type = TSP_SETTIME;
+ } else {
+ mstotvround(&to.tsp_time, corr);
+ to.tsp_type = TSP_ADJTIME;
+ }
+ (void)strcpy(to.tsp_name, hostname);
+ answer = acksend(&to, &htp->addr, htp->name,
+ TSP_ACK, 0, 0);
+ if (!answer) {
+ htp->delta = HOSTDOWN;
+ syslog(LOG_WARNING,
+ "no reply to time correction from %s",
+ htp->name);
+ if (++htp->noanswer >= LOSTHOST) {
+ if (trace) {
+ fprintf(fd,
+ "purging %s for not answering\n",
+ htp->name);
+ (void)fflush(fd);
+ }
+ htp = remmach(htp);
+ }
+ }
+ }
+ }
+
+ /*
+ * adjust our own clock now that we are not sending it out
+ */
+ adjclock(&adjlocal);
+}
+
+
+static void
+adjclock(corr)
+ struct timeval *corr;
+{
+ static int passes = 0;
+ static int smoother = 0;
+ long delta; /* adjustment in usec */
+ long ndelta;
+ struct timeval now;
+ struct timeval adj;
+
+ if (!timerisset(corr))
+ return;
+
+ adj = *corr;
+ if (adj.tv_sec < MAXADJ && adj.tv_sec > - MAXADJ) {
+ delta = adj.tv_sec*1000000 + adj.tv_usec;
+ /* If the correction is less than the minimum round
+ * trip time for an ICMP packet, and thus
+ * less than the likely error in the measurement,
+ * do not do the entire correction. Do half
+ * or a quarter of it.
+ */
+
+ if (delta > -MIN_ROUND*1000
+ && delta < MIN_ROUND*1000) {
+ if (smoother <= 4)
+ smoother++;
+ ndelta = delta >> smoother;
+ if (trace)
+ fprintf(fd,
+ "trimming delta %ld usec to %ld\n",
+ delta, ndelta);
+ adj.tv_usec = ndelta;
+ adj.tv_sec = 0;
+ } else if (smoother > 0) {
+ smoother--;
+ }
+ if (0 > adjtime(corr, 0)) {
+ syslog(LOG_ERR, "adjtime: %m");
+ }
+ if (passes > 1
+ && (delta < -BIG_ADJ || delta > BIG_ADJ)) {
+ smoother = 0;
+ passes = 0;
+ syslog(LOG_WARNING,
+ "large time adjustment of %+.3f sec",
+ delta/1000000.0);
+ }
+ } else {
+ syslog(LOG_WARNING,
+ "clock correction %d sec too large to adjust",
+ adj.tv_sec);
+ (void) gettimeofday(&now, 0);
+ timevaladd(&now, corr);
+ if (settimeofday(&now, 0) < 0)
+ syslog(LOG_ERR, "settimeofday: %m");
+ }
+
+#ifdef sgi
+ /* Accumulate the total change, and use it to adjust the basic
+ * clock rate.
+ */
+ if (++passes > 2) {
+#define F_USEC_PER_SEC (1000000*1.0) /* reduce typos */
+#define F_NSEC_PER_SEC (F_USEC_PER_SEC*1000.0)
+
+ extern char *timetrim_fn;
+ extern char *timetrim_wpat;
+ extern long timetrim;
+ extern double tot_adj, hr_adj; /* totals in nsec */
+ extern double tot_ticks, hr_ticks;
+
+ static double nag_tick;
+ double cur_ticks, hr_delta_ticks, tot_delta_ticks;
+ double tru_tot_adj, tru_hr_adj; /* nsecs of adjustment */
+ double tot_trim, hr_trim; /* nsec/sec */
+ struct tms tm;
+ FILE *timetrim_st;
+
+ cur_ticks = times(&tm);
+ tot_adj += delta*1000.0;
+ hr_adj += delta*1000.0;
+
+ tot_delta_ticks = cur_ticks-tot_ticks;
+ if (tot_delta_ticks >= 16*SECDAY*CLK_TCK) {
+ tot_adj -= rint(tot_adj/16);
+ tot_ticks += rint(tot_delta_ticks/16);
+ tot_delta_ticks = cur_ticks-tot_ticks;
+ }
+ hr_delta_ticks = cur_ticks-hr_ticks;
+
+ tru_hr_adj = hr_adj + timetrim*rint(hr_delta_ticks/CLK_TCK);
+ tru_tot_adj = (tot_adj
+ + timetrim*rint(tot_delta_ticks/CLK_TCK));
+
+ if (hr_delta_ticks >= SECDAY*CLK_TCK
+ || (tot_delta_ticks < 4*SECDAY*CLK_TCK
+ && hr_delta_ticks >= SECHR*CLK_TCK)
+ || (trace && hr_delta_ticks >= (SECHR/10)*CLK_TCK)) {
+
+ tot_trim = rint(tru_tot_adj*CLK_TCK/tot_delta_ticks);
+ hr_trim = rint(tru_hr_adj*CLK_TCK/hr_delta_ticks);
+
+ if (trace
+ || (abs(timetrim - hr_trim) > 100000.0
+ && 0 == timetrim_fn
+ && ((cur_ticks - nag_tick)
+ >= 24*SECDAY*CLK_TCK))) {
+ nag_tick = cur_ticks;
+ syslog(LOG_NOTICE,
+ "%+.3f/%.2f or %+.3f/%.2f sec/hr; timetrim=%+.0f or %+.0f",
+ tru_tot_adj/F_NSEC_PER_SEC,
+ tot_delta_ticks/(SECHR*CLK_TCK*1.0),
+ tru_hr_adj/F_NSEC_PER_SEC,
+ hr_delta_ticks/(SECHR*CLK_TCK*1.0),
+ tot_trim,
+ hr_trim);
+ }
+
+ if (tot_trim < -MAX_TRIM || tot_trim > MAX_TRIM) {
+ tot_ticks = hr_ticks;
+ tot_adj = hr_adj;
+ } else if (0 > syssgi(SGI_SETTIMETRIM,
+ (long)tot_trim)) {
+ syslog(LOG_ERR, "SETTIMETRIM(%d): %m",
+ (long)tot_trim);
+ } else {
+ if (0 != timetrim_fn) {
+ timetrim_st = fopen(timetrim_fn, "w");
+ if (0 == timetrim_st) {
+ syslog(LOG_ERR, "fopen(%s): %m",
+ timetrim_fn);
+ } else {
+ if (0 > fprintf(timetrim_st,
+ timetrim_wpat,
+ (long)tot_trim,
+ tru_tot_adj,
+ tot_delta_ticks)) {
+ syslog(LOG_ERR,
+ "fprintf(%s): %m",
+ timetrim_fn);
+ }
+ (void)fclose(timetrim_st);
+ }
+ }
+
+ tot_adj -= ((tot_trim - timetrim)
+ * rint(tot_delta_ticks/CLK_TCK));
+ timetrim = tot_trim;
+ }
+
+ hr_ticks = cur_ticks;
+ hr_adj = 0;
+ }
+ }
+#endif /* sgi */
+}
+
+
+/* adjust the time in a message by the time it
+ * spent in the queue
+ */
+void
+adj_msg_time(msg, now)
+ struct tsp *msg;
+ struct timeval *now;
+{
+ msg->tsp_time.tv_sec += (now->tv_sec - from_when.tv_sec);
+ msg->tsp_time.tv_usec += (now->tv_usec - from_when.tv_usec);
+
+ while (msg->tsp_time.tv_usec < 0) {
+ msg->tsp_time.tv_sec--;
+ msg->tsp_time.tv_usec += 1000000;
+ }
+ while (msg->tsp_time.tv_usec >= 1000000) {
+ msg->tsp_time.tv_sec++;
+ msg->tsp_time.tv_usec -= 1000000;
+ }
+}
diff --git a/usr.sbin/timed/timed/extern.h b/usr.sbin/timed/timed/extern.h
new file mode 100644
index 0000000..09dfaaa
--- /dev/null
+++ b/usr.sbin/timed/timed/extern.h
@@ -0,0 +1,89 @@
+/*-
+ * Copyright (c) 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)extern.h 8.1 (Berkeley) 6/6/93
+ */
+
+struct hosttbl;
+struct netinfo;
+struct sockaddr_in;
+struct timeval;
+struct tsp;
+
+struct hosttbl *addmach __P((char *, struct sockaddr_in *, struct netinfo *));
+struct hosttbl *findhost __P((char *));
+struct hosttbl *remmach __P((struct hosttbl *));
+
+struct tsp *readmsg __P((int,
+ char *, struct timeval *, struct netinfo *));
+struct tsp *acksend __P((struct tsp *,
+ struct sockaddr_in *, char *, int, struct netinfo *, int));
+
+void addnetname __P((char *));
+void adj_msg_time __P((struct tsp *, struct timeval *));
+void bytehostorder __P((struct tsp *));
+void bytenetorder __P((struct tsp *));
+void byteorder __P((struct tsp *));
+long casual __P((long, long));
+int cksum __P((u_short *, int));
+void correct __P((long));
+char *date __P((void));
+void doquit __P((struct tsp *));
+int election __P((struct netinfo *));
+void get_goodgroup __P((int));
+int good_host_name __P((char *));
+void ignoreack __P((void));
+int in_cksum __P((u_short *, int));
+void lookformaster __P((struct netinfo *));
+void makeslave __P((struct netinfo *));
+int master __P((void));
+void masterack __P((void));
+void masterup __P((struct netinfo *));
+int measure __P((u_long, u_long, char *, struct sockaddr_in *, int));
+void msterup __P((struct netinfo *));
+void mstotvround __P((struct timeval *, long));
+long networkdelta __P((void));
+void newslave __P((struct tsp *));
+void print __P((struct tsp *, struct sockaddr_in *));
+void prthp __P((clock_t));
+void rmnetmachs __P((struct netinfo *));
+void setstatus __P((void));
+int slave __P((void));
+void slaveack __P((void));
+void spreadtime __P((void));
+void suppress __P((struct sockaddr_in *, char *, struct netinfo *));
+void synch __P((long));
+void timevaladd __P((struct timeval *, struct timeval *));
+void timevalsub __P((struct timeval *, struct timeval *, struct timeval *));
+void traceoff __P((char *));
+void traceon __P((void));
+void xmit __P((int, u_int, struct sockaddr_in *));
diff --git a/usr.sbin/timed/timed/globals.h b/usr.sbin/timed/timed/globals.h
new file mode 100644
index 0000000..852dd0c
--- /dev/null
+++ b/usr.sbin/timed/timed/globals.h
@@ -0,0 +1,182 @@
+/*-
+ * Copyright (c) 1985, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)globals.h 8.1 (Berkeley) 6/6/93
+ */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <err.h>
+#include <errno.h>
+#include <limits.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <syslog.h>
+#include <unistd.h>
+
+#include <protocols/timed.h>
+#ifdef sgi
+#include <bstring.h>
+#include <sys/clock.h>
+/* use the constant HZ instead of the function CLK_TCK */
+#undef CLK_TCK
+#define CLK_TCK HZ
+#else
+#define SECHR (60*60)
+#define SECDAY (24*SECHR)
+#endif /* sgi */
+
+extern int sock;
+
+/* Best expected round trip for a measurement.
+ * This is essentially the number of milliseconds per CPU tick (CLK_TCK?).
+ * All delays shorter than this are usually reported as 0.
+ */
+#define MIN_ROUND ((1000-1)/CLK_TCK)
+
+
+#define SAMPLEINTVL 240 /* synch() freq for master in sec */
+#define MAXADJ 20 /* max adjtime() correction in sec */
+
+#define MAX_TRIM 3000000 /* max drift in nsec/sec, 0.3% */
+#define BIG_ADJ (MAX_TRIM/1000*SAMPLEINTVL*2) /* max good adj */
+
+#define MINTOUT 360 /* election delays, 6-15 minutes */
+#define MAXTOUT 900
+
+#define BAD_STATUS (-1)
+#define GOOD 1
+#define UNREACHABLE 2
+#define NONSTDTIME 3
+#define HOSTDOWN 0x7fffffff
+
+#define OFF 0
+#define ON 1
+
+#define MAX_HOPCNT 10 /* max value for tsp_hpcnt */
+
+#define LOSTHOST 3 /* forget after this many failures */
+
+#define VALID_RANGE (MAXADJ*1000) /* good times in milliseconds */
+#define GOOD_RANGE (MIN_ROUND*2)
+#define VGOOD_RANGE (MIN_ROUND-1)
+
+
+/*
+ * Global and per-network states.
+ */
+#define NOMASTER 0 /* no good master */
+#define SLAVE 1
+#define MASTER 2
+#define IGNORE 4
+#define ALL (SLAVE|MASTER|IGNORE)
+#define SUBMASTER (SLAVE|MASTER)
+
+#define NHOSTS 1013 /* max of hosts controlled by timed
+ * This must be a prime number.
+ */
+struct hosttbl {
+ struct hosttbl *h_bak; /* hash chain */
+ struct hosttbl *h_fwd;
+ struct hosttbl *l_bak; /* "sequential" list */
+ struct hosttbl *l_fwd;
+ struct netinfo *ntp;
+ struct sockaddr_in addr;
+ char name[MAXHOSTNAMELEN];
+ u_char head; /* 1=head of hash chain */
+ u_char good; /* 0=trusted host, for averaging */
+ u_char noanswer; /* count of failures to answer */
+ u_char need_set; /* need a SETTIME */
+ u_short seq;
+ long delta;
+};
+
+/* closed hash table with internal chaining */
+extern struct hosttbl hosttbl[NHOSTS+1];
+#define self hosttbl[0]
+#define hostname (self.name)
+
+
+struct netinfo {
+ struct netinfo *next;
+ struct in_addr net;
+ u_long mask;
+ struct in_addr my_addr;
+ struct sockaddr_in dest_addr; /* broadcast addr or point-point */
+ long status;
+ struct timeval slvwait; /* delay before sending our time */
+ int quit_count; /* recent QUITs */
+};
+
+#include "extern.h"
+
+#define tvtomsround(tv) ((tv).tv_sec*1000 + ((tv).tv_usec + 500)/1000)
+
+extern struct netinfo *nettab;
+extern int status;
+extern int trace;
+extern int sock;
+extern struct sockaddr_in from;
+extern struct timeval from_when; /* when the last msg arrived */
+extern u_short sequence; /* TSP message sequence number */
+extern struct netinfo *fromnet, *slavenet;
+extern FILE *fd;
+extern long delay1, delay2;
+extern int nslavenets; /* nets were I could be a slave */
+extern int nmasternets; /* nets were I could be a master */
+extern int nignorednets; /* ignored nets */
+extern int nnets; /* nets I am connected to */
+
+
+#define trace_msg(msg) {if (trace) fprintf(fd, msg);}
+
+#define trace_sendto_err(addr) { \
+ int st_errno = errno; \
+ syslog(LOG_ERR, "%s %d: sendto %s: %m", \
+ __FILE__, __LINE__, inet_ntoa(addr)); \
+ if (trace) \
+ fprintf(fd, "%s %d: sendto %s: %d", __FILE__, __LINE__, \
+ inet_ntoa(addr), st_errno); \
+}
+
+
+# define max(a,b) (a<b ? b : a)
+# define min(a,b) (a>b ? b : a)
+# define abs(x) (x>=0 ? x : -(x))
diff --git a/usr.sbin/timed/timed/master.c b/usr.sbin/timed/timed/master.c
new file mode 100644
index 0000000..14dc6d1
--- /dev/null
+++ b/usr.sbin/timed/timed/master.c
@@ -0,0 +1,907 @@
+/*-
+ * Copyright (c) 1985, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)master.c 8.1 (Berkeley) 6/6/93";
+#endif
+static const char rcsid[] =
+ "$Id: master.c,v 1.3 1997/10/22 06:19:48 charnier Exp $";
+#endif /* not lint */
+
+#include "globals.h"
+#include <sys/file.h>
+#include <sys/types.h>
+#include <sys/times.h>
+#include <setjmp.h>
+#ifdef sgi
+#include <sys/schedctl.h>
+#endif /* sgi */
+#include <utmp.h>
+#include "pathnames.h"
+
+extern int measure_delta;
+extern jmp_buf jmpenv;
+extern int Mflag;
+extern int justquit;
+
+static int dictate;
+static int slvcount; /* slaves listening to our clock */
+
+static void mchgdate __P((struct tsp *));
+
+#ifdef sgi
+extern void logwtmp __P((struct timeval *, struct timeval *));
+#else
+extern void logwtmp __P((char *, char *, char *));
+#endif /* sgi */
+
+/*
+ * The main function of `master' is to periodically compute the differences
+ * (deltas) between its clock and the clocks of the slaves, to compute the
+ * network average delta, and to send to the slaves the differences between
+ * their individual deltas and the network delta.
+ * While waiting, it receives messages from the slaves (i.e. requests for
+ * master's name, remote requests to set the network time, ...), and
+ * takes the appropriate action.
+ */
+int
+master()
+{
+ struct hosttbl *htp;
+ long pollingtime;
+#define POLLRATE 4
+ int polls;
+ struct timeval wait, ntime;
+ struct tsp *msg, *answer, to;
+ char newdate[32];
+ struct sockaddr_in taddr;
+ char tname[MAXHOSTNAMELEN];
+ struct netinfo *ntp;
+ int i;
+
+ syslog(LOG_NOTICE, "This machine is master");
+ if (trace)
+ fprintf(fd, "This machine is master\n");
+ for (ntp = nettab; ntp != NULL; ntp = ntp->next) {
+ if (ntp->status == MASTER)
+ masterup(ntp);
+ }
+ (void)gettimeofday(&ntime, 0);
+ pollingtime = ntime.tv_sec+3;
+ if (justquit)
+ polls = 0;
+ else
+ polls = POLLRATE-1;
+
+/* Process all outstanding messages before spending the long time necessary
+ * to update all timers.
+ */
+loop:
+ (void)gettimeofday(&ntime, 0);
+ wait.tv_sec = pollingtime - ntime.tv_sec;
+ if (wait.tv_sec < 0)
+ wait.tv_sec = 0;
+ wait.tv_usec = 0;
+ msg = readmsg(TSP_ANY, ANYADDR, &wait, 0);
+ if (!msg) {
+ (void)gettimeofday(&ntime, 0);
+ if (ntime.tv_sec >= pollingtime) {
+ pollingtime = ntime.tv_sec + SAMPLEINTVL;
+ get_goodgroup(0);
+
+/* If a bogus master told us to quit, we can have decided to ignore a
+ * network. Therefore, periodically try to take over everything.
+ */
+ polls = (polls + 1) % POLLRATE;
+ if (0 == polls && nignorednets > 0) {
+ trace_msg("Looking for nets to re-master\n");
+ for (ntp = nettab; ntp; ntp = ntp->next) {
+ if (ntp->status == IGNORE
+ || ntp->status == NOMASTER) {
+ lookformaster(ntp);
+ if (ntp->status == MASTER) {
+ masterup(ntp);
+ polls = POLLRATE-1;
+ }
+ }
+ if (ntp->status == MASTER
+ && --ntp->quit_count < 0)
+ ntp->quit_count = 0;
+ }
+ if (polls != 0)
+ setstatus();
+ }
+
+ synch(0L);
+
+ for (ntp = nettab; ntp != NULL; ntp = ntp->next) {
+ to.tsp_type = TSP_LOOP;
+ to.tsp_vers = TSPVERSION;
+ to.tsp_seq = sequence++;
+ to.tsp_hopcnt = MAX_HOPCNT;
+ (void)strcpy(to.tsp_name, hostname);
+ bytenetorder(&to);
+ if (sendto(sock, (char *)&to,
+ sizeof(struct tsp), 0,
+ (struct sockaddr*)&ntp->dest_addr,
+ sizeof(ntp->dest_addr)) < 0) {
+ trace_sendto_err(ntp->dest_addr.sin_addr);
+ }
+ }
+ }
+
+
+ } else {
+ switch (msg->tsp_type) {
+
+ case TSP_MASTERREQ:
+ break;
+
+ case TSP_SLAVEUP:
+ newslave(msg);
+ break;
+
+ case TSP_SETDATE:
+ /*
+ * XXX check to see it is from ourself
+ */
+#ifdef sgi
+ (void)cftime(newdate, "%D %T", &msg->tsp_time.tv_sec);
+#else
+ (void)strcpy(newdate, ctime(&msg->tsp_time.tv_sec));
+#endif /* sgi */
+ if (!good_host_name(msg->tsp_name)) {
+ syslog(LOG_NOTICE,
+ "attempted date change by %s to %s",
+ msg->tsp_name, newdate);
+ spreadtime();
+ break;
+ }
+
+ mchgdate(msg);
+ (void)gettimeofday(&ntime, 0);
+ pollingtime = ntime.tv_sec + SAMPLEINTVL;
+ break;
+
+ case TSP_SETDATEREQ:
+ if (!fromnet || fromnet->status != MASTER)
+ break;
+#ifdef sgi
+ (void)cftime(newdate, "%D %T", &msg->tsp_time.tv_sec);
+#else
+ (void)strcpy(newdate, ctime(&msg->tsp_time.tv_sec));
+#endif /* sgi */
+ htp = findhost(msg->tsp_name);
+ if (htp == 0) {
+ syslog(LOG_ERR,
+ "attempted SET DATEREQ by uncontrolled %s to %s",
+ msg->tsp_name, newdate);
+ break;
+ }
+ if (htp->seq == msg->tsp_seq)
+ break;
+ htp->seq = msg->tsp_seq;
+ if (!htp->good) {
+ syslog(LOG_NOTICE,
+ "attempted SET DATEREQ by untrusted %s to %s",
+ msg->tsp_name, newdate);
+ spreadtime();
+ break;
+ }
+
+ mchgdate(msg);
+ (void)gettimeofday(&ntime, 0);
+ pollingtime = ntime.tv_sec + SAMPLEINTVL;
+ break;
+
+ case TSP_MSITE:
+ xmit(TSP_ACK, msg->tsp_seq, &from);
+ break;
+
+ case TSP_MSITEREQ:
+ break;
+
+ case TSP_TRACEON:
+ traceon();
+ break;
+
+ case TSP_TRACEOFF:
+ traceoff("Tracing ended at %s\n");
+ break;
+
+ case TSP_ELECTION:
+ if (!fromnet)
+ break;
+ if (fromnet->status == MASTER) {
+ pollingtime = 0;
+ (void)addmach(msg->tsp_name, &from,fromnet);
+ }
+ taddr = from;
+ (void)strcpy(tname, msg->tsp_name);
+ to.tsp_type = TSP_QUIT;
+ (void)strcpy(to.tsp_name, hostname);
+ answer = acksend(&to, &taddr, tname,
+ TSP_ACK, 0, 1);
+ if (answer == NULL) {
+ syslog(LOG_ERR, "election error by %s",
+ tname);
+ }
+ break;
+
+ case TSP_CONFLICT:
+ /*
+ * After a network partition, there can be
+ * more than one master: the first slave to
+ * come up will notify here the situation.
+ */
+ if (!fromnet || fromnet->status != MASTER)
+ break;
+ (void)strcpy(to.tsp_name, hostname);
+
+ /* The other master often gets into the same state,
+ * with boring results if we stay at it forever.
+ */
+ ntp = fromnet; /* (acksend() can leave fromnet=0 */
+ for (i = 0; i < 3; i++) {
+ to.tsp_type = TSP_RESOLVE;
+ (void)strcpy(to.tsp_name, hostname);
+ answer = acksend(&to, &ntp->dest_addr,
+ ANYADDR, TSP_MASTERACK,
+ ntp, 0);
+ if (!answer)
+ break;
+ htp = addmach(answer->tsp_name,&from,ntp);
+ to.tsp_type = TSP_QUIT;
+ msg = acksend(&to, &htp->addr, htp->name,
+ TSP_ACK, 0, htp->noanswer);
+ if (msg == NULL) {
+ syslog(LOG_ERR,
+ "no response from %s to CONFLICT-QUIT",
+ htp->name);
+ }
+ }
+ masterup(ntp);
+ pollingtime = 0;
+ break;
+
+ case TSP_RESOLVE:
+ if (!fromnet || fromnet->status != MASTER)
+ break;
+ /*
+ * do not want to call synch() while waiting
+ * to be killed!
+ */
+ (void)gettimeofday(&ntime, (struct timezone *)0);
+ pollingtime = ntime.tv_sec + SAMPLEINTVL;
+ break;
+
+ case TSP_QUIT:
+ doquit(msg); /* become a slave */
+ break;
+
+ case TSP_LOOP:
+ if (!fromnet || fromnet->status != MASTER
+ || !strcmp(msg->tsp_name, hostname))
+ break;
+ /*
+ * We should not have received this from a net
+ * we are master on. There must be two masters.
+ */
+ htp = addmach(msg->tsp_name, &from,fromnet);
+ to.tsp_type = TSP_QUIT;
+ (void)strcpy(to.tsp_name, hostname);
+ answer = acksend(&to, &htp->addr, htp->name,
+ TSP_ACK, 0, 1);
+ if (!answer) {
+ syslog(LOG_WARNING,
+ "loop breakage: no reply from %s=%s to QUIT",
+ htp->name, inet_ntoa(htp->addr.sin_addr));
+ (void)remmach(htp);
+ }
+
+ case TSP_TEST:
+ if (trace) {
+ fprintf(fd,
+ "\tnets = %d, masters = %d, slaves = %d, ignored = %d\n",
+ nnets, nmasternets, nslavenets, nignorednets);
+ setstatus();
+ }
+ pollingtime = 0;
+ polls = POLLRATE-1;
+ break;
+
+ default:
+ if (trace) {
+ fprintf(fd, "garbage message: ");
+ print(msg, &from);
+ }
+ break;
+ }
+ }
+ goto loop;
+}
+
+
+/*
+ * change the system date on the master
+ */
+static void
+mchgdate(msg)
+ struct tsp *msg;
+{
+ char tname[MAXHOSTNAMELEN];
+ char olddate[32];
+ struct timeval otime, ntime;
+
+ (void)strcpy(tname, msg->tsp_name);
+
+ xmit(TSP_DATEACK, msg->tsp_seq, &from);
+
+ (void)strcpy(olddate, date());
+
+ /* adjust time for residence on the queue */
+ (void)gettimeofday(&otime, 0);
+ adj_msg_time(msg,&otime);
+
+ timevalsub(&ntime, &msg->tsp_time, &otime);
+ if (ntime.tv_sec < MAXADJ && ntime.tv_sec > -MAXADJ) {
+ /*
+ * do not change the clock if we can adjust it
+ */
+ dictate = 3;
+ synch(tvtomsround(ntime));
+ } else {
+#ifdef sgi
+ if (0 > settimeofday(&msg->tsp_time, 0)) {
+ syslog(LOG_ERR, "settimeofday(): %m");
+ }
+ logwtmp(&otime, &msg->tsp_time);
+#else
+ logwtmp("|", "date", "");
+ (void)settimeofday(&msg->tsp_time, 0);
+ logwtmp("{", "date", "");
+#endif /* sgi */
+ spreadtime();
+ }
+
+ syslog(LOG_NOTICE, "date changed by %s from %s",
+ tname, olddate);
+}
+
+
+/*
+ * synchronize all of the slaves
+ */
+void
+synch(mydelta)
+ long mydelta;
+{
+ struct hosttbl *htp;
+ int measure_status;
+ struct timeval check, stop, wait;
+#ifdef sgi
+ int pri;
+#endif /* sgi */
+
+ if (slvcount > 0) {
+ if (trace)
+ fprintf(fd, "measurements starting at %s\n", date());
+ (void)gettimeofday(&check, 0);
+#ifdef sgi
+ /* run fast to get good time */
+ pri = schedctl(NDPRI,0,NDPHIMIN);
+ if (pri < 0)
+ syslog(LOG_ERR, "schedctl(): %m");
+#endif /* sgi */
+ for (htp = self.l_fwd; htp != &self; htp = htp->l_fwd) {
+ if (htp->noanswer != 0) {
+ measure_status = measure(500, 100,
+ htp->name,
+ &htp->addr,0);
+ } else {
+ measure_status = measure(3000, 100,
+ htp->name,
+ &htp->addr,0);
+ }
+ if (measure_status != GOOD) {
+ /* The slave did not respond. We have
+ * just wasted lots of time on it.
+ */
+ htp->delta = HOSTDOWN;
+ if (++htp->noanswer >= LOSTHOST) {
+ if (trace) {
+ fprintf(fd,
+ "purging %s for not answering ICMP\n",
+ htp->name);
+ (void)fflush(fd);
+ }
+ htp = remmach(htp);
+ }
+ } else {
+ htp->delta = measure_delta;
+ }
+ (void)gettimeofday(&stop, 0);
+ timevalsub(&stop, &stop, &check);
+ if (stop.tv_sec >= 1) {
+ if (trace)
+ (void)fflush(fd);
+ /*
+ * ack messages periodically
+ */
+ wait.tv_sec = 0;
+ wait.tv_usec = 0;
+ if (0 != readmsg(TSP_TRACEON,ANYADDR,
+ &wait,0))
+ traceon();
+ (void)gettimeofday(&check, 0);
+ }
+ }
+#ifdef sgi
+ if (pri >= 0)
+ (void)schedctl(NDPRI,0,pri);
+#endif /* sgi */
+ if (trace)
+ fprintf(fd, "measurements finished at %s\n", date());
+ }
+ if (!(status & SLAVE)) {
+ if (!dictate) {
+ mydelta = networkdelta();
+ } else {
+ dictate--;
+ }
+ }
+ if (trace && (mydelta != 0 || (status & SLAVE)))
+ fprintf(fd,"local correction of %ld ms.\n", mydelta);
+ correct(mydelta);
+}
+
+/*
+ * sends the time to each slave after the master
+ * has received the command to set the network time
+ */
+void
+spreadtime()
+{
+ struct hosttbl *htp;
+ struct tsp to;
+ struct tsp *answer;
+
+/* Do not listen to the consensus after forcing the time. This is because
+ * the consensus takes a while to reach the time we are dictating.
+ */
+ dictate = 2;
+ for (htp = self.l_fwd; htp != &self; htp = htp->l_fwd) {
+ to.tsp_type = TSP_SETTIME;
+ (void)strcpy(to.tsp_name, hostname);
+ (void)gettimeofday(&to.tsp_time, 0);
+ answer = acksend(&to, &htp->addr, htp->name,
+ TSP_ACK, 0, htp->noanswer);
+ if (answer == 0) {
+ /* We client does not respond, then we have
+ * just wasted lots of time on it.
+ */
+ syslog(LOG_WARNING,
+ "no reply to SETTIME from %s", htp->name);
+ if (++htp->noanswer >= LOSTHOST) {
+ if (trace) {
+ fprintf(fd,
+ "purging %s for not answering",
+ htp->name);
+ (void)fflush(fd);
+ }
+ htp = remmach(htp);
+ }
+ }
+ }
+}
+
+void
+prthp(delta)
+ clock_t delta;
+{
+ static time_t next_time;
+ time_t this_time;
+ struct tms tm;
+ struct hosttbl *htp;
+ int length, l;
+ int i;
+
+ if (!fd) /* quit if tracing already off */
+ return;
+
+ this_time = times(&tm);
+ if (this_time + delta < next_time)
+ return;
+ next_time = this_time + CLK_TCK;
+
+ fprintf(fd, "host table: %d entries at %s\n", slvcount, date());
+ htp = self.l_fwd;
+ length = 1;
+ for (i = 1; i <= slvcount; i++, htp = htp->l_fwd) {
+ l = strlen(htp->name) + 1;
+ if (length+l >= 80) {
+ fprintf(fd, "\n");
+ length = 0;
+ }
+ length += l;
+ fprintf(fd, " %s", htp->name);
+ }
+ fprintf(fd, "\n");
+}
+
+
+static struct hosttbl *newhost_hash;
+static struct hosttbl *lasthfree = &hosttbl[0];
+
+
+struct hosttbl * /* answer or 0 */
+findhost(name)
+ char *name;
+{
+ int i, j;
+ struct hosttbl *htp;
+ char *p;
+
+ j= 0;
+ for (p = name, i = 0; i < 8 && *p != '\0'; i++, p++)
+ j = (j << 2) ^ *p;
+ newhost_hash = &hosttbl[j % NHOSTS];
+
+ htp = newhost_hash;
+ if (htp->name[0] == '\0')
+ return(0);
+ do {
+ if (!strcmp(name, htp->name))
+ return(htp);
+ htp = htp->h_fwd;
+ } while (htp != newhost_hash);
+ return(0);
+}
+
+/*
+ * add a host to the list of controlled machines if not already there
+ */
+struct hosttbl *
+addmach(name, addr, ntp)
+ char *name;
+ struct sockaddr_in *addr;
+ struct netinfo *ntp;
+{
+ struct hosttbl *ret, *p, *b, *f;
+
+ ret = findhost(name);
+ if (ret == 0) {
+ if (slvcount >= NHOSTS) {
+ if (trace) {
+ fprintf(fd, "no more slots in host table\n");
+ prthp(CLK_TCK);
+ }
+ syslog(LOG_ERR, "no more slots in host table");
+ Mflag = 0;
+ longjmp(jmpenv, 2); /* give up and be a slave */
+ }
+
+ /* if our home hash slot is occupied, find a free entry
+ * in the hash table
+ */
+ if (newhost_hash->name[0] != '\0') {
+ do {
+ ret = lasthfree;
+ if (++lasthfree > &hosttbl[NHOSTS])
+ lasthfree = &hosttbl[1];
+ } while (ret->name[0] != '\0');
+
+ if (!newhost_hash->head) {
+ /* Move an interloper using our home. Use
+ * scratch pointers in case the new head is
+ * pointing to itself.
+ */
+ f = newhost_hash->h_fwd;
+ b = newhost_hash->h_bak;
+ f->h_bak = ret;
+ b->h_fwd = ret;
+ f = newhost_hash->l_fwd;
+ b = newhost_hash->l_bak;
+ f->l_bak = ret;
+ b->l_fwd = ret;
+ bcopy(newhost_hash,ret,sizeof(*ret));
+ ret = newhost_hash;
+ ret->head = 1;
+ ret->h_fwd = ret;
+ ret->h_bak = ret;
+ } else {
+ /* link to an existing chain in our home
+ */
+ ret->head = 0;
+ p = newhost_hash->h_bak;
+ ret->h_fwd = newhost_hash;
+ ret->h_bak = p;
+ p->h_fwd = ret;
+ newhost_hash->h_bak = ret;
+ }
+ } else {
+ ret = newhost_hash;
+ ret->head = 1;
+ ret->h_fwd = ret;
+ ret->h_bak = ret;
+ }
+ ret->addr = *addr;
+ ret->ntp = ntp;
+ (void)strncpy(ret->name, name, sizeof(ret->name));
+ ret->good = good_host_name(name);
+ ret->l_fwd = &self;
+ ret->l_bak = self.l_bak;
+ self.l_bak->l_fwd = ret;
+ self.l_bak = ret;
+ slvcount++;
+
+ ret->noanswer = 0;
+ ret->need_set = 1;
+
+ } else {
+ ret->noanswer = (ret->noanswer != 0);
+ }
+
+ /* need to clear sequence number anyhow */
+ ret->seq = 0;
+ return(ret);
+}
+
+/*
+ * remove the machine with the given index in the host table.
+ */
+struct hosttbl *
+remmach(htp)
+ struct hosttbl *htp;
+{
+ struct hosttbl *lprv, *hnxt, *f, *b;
+
+ if (trace)
+ fprintf(fd, "remove %s\n", htp->name);
+
+ /* get out of the lists */
+ htp->l_fwd->l_bak = lprv = htp->l_bak;
+ htp->l_bak->l_fwd = htp->l_fwd;
+ htp->h_fwd->h_bak = htp->h_bak;
+ htp->h_bak->h_fwd = hnxt = htp->h_fwd;
+
+ /* If we are in the home slot, pull up the chain */
+ if (htp->head && hnxt != htp) {
+ if (lprv == hnxt)
+ lprv = htp;
+
+ /* Use scratch pointers in case the new head is pointing to
+ * itself.
+ */
+ f = hnxt->h_fwd;
+ b = hnxt->h_bak;
+ f->h_bak = htp;
+ b->h_fwd = htp;
+ f = hnxt->l_fwd;
+ b = hnxt->l_bak;
+ f->l_bak = htp;
+ b->l_fwd = htp;
+ hnxt->head = 1;
+ bcopy(hnxt, htp, sizeof(*htp));
+ lasthfree = hnxt;
+ } else {
+ lasthfree = htp;
+ }
+
+ lasthfree->name[0] = '\0';
+ lasthfree->h_fwd = 0;
+ lasthfree->l_fwd = 0;
+ slvcount--;
+
+ return lprv;
+}
+
+
+/*
+ * Remove all the machines from the host table that exist on the given
+ * network. This is called when a master transitions to a slave on a
+ * given network.
+ */
+void
+rmnetmachs(ntp)
+ struct netinfo *ntp;
+{
+ struct hosttbl *htp;
+
+ if (trace)
+ prthp(CLK_TCK);
+ for (htp = self.l_fwd; htp != &self; htp = htp->l_fwd) {
+ if (ntp == htp->ntp)
+ htp = remmach(htp);
+ }
+ if (trace)
+ prthp(CLK_TCK);
+}
+
+void
+masterup(net)
+ struct netinfo *net;
+{
+ xmit(TSP_MASTERUP, 0, &net->dest_addr);
+
+ /*
+ * Do not tell new slaves our time for a while. This ensures
+ * we do not tell them to start using our time, before we have
+ * found a good master.
+ */
+ (void)gettimeofday(&net->slvwait, 0);
+}
+
+void
+newslave(msg)
+ struct tsp *msg;
+{
+ struct hosttbl *htp;
+ struct tsp *answer, to;
+ struct timeval now;
+
+ if (!fromnet || fromnet->status != MASTER)
+ return;
+
+ htp = addmach(msg->tsp_name, &from,fromnet);
+ htp->seq = msg->tsp_seq;
+ if (trace)
+ prthp(0);
+
+ /*
+ * If we are stable, send our time to the slave.
+ * Do not go crazy if the date has been changed.
+ */
+ (void)gettimeofday(&now, 0);
+ if (now.tv_sec >= fromnet->slvwait.tv_sec+3
+ || now.tv_sec < fromnet->slvwait.tv_sec) {
+ to.tsp_type = TSP_SETTIME;
+ (void)strcpy(to.tsp_name, hostname);
+ (void)gettimeofday(&to.tsp_time, 0);
+ answer = acksend(&to, &htp->addr,
+ htp->name, TSP_ACK,
+ 0, htp->noanswer);
+ if (answer) {
+ htp->need_set = 0;
+ } else {
+ syslog(LOG_WARNING,
+ "no reply to initial SETTIME from %s",
+ htp->name);
+ htp->noanswer = LOSTHOST;
+ }
+ }
+}
+
+
+/*
+ * react to a TSP_QUIT:
+ */
+void
+doquit(msg)
+ struct tsp *msg;
+{
+ if (fromnet->status == MASTER) {
+ if (!good_host_name(msg->tsp_name)) {
+ if (fromnet->quit_count <= 0) {
+ syslog(LOG_NOTICE,"untrusted %s told us QUIT",
+ msg->tsp_name);
+ suppress(&from, msg->tsp_name, fromnet);
+ fromnet->quit_count = 1;
+ return;
+ }
+ syslog(LOG_NOTICE, "untrusted %s told us QUIT twice",
+ msg->tsp_name);
+ fromnet->quit_count = 2;
+ fromnet->status = NOMASTER;
+ } else {
+ fromnet->status = SLAVE;
+ }
+ rmnetmachs(fromnet);
+ longjmp(jmpenv, 2); /* give up and be a slave */
+
+ } else {
+ if (!good_host_name(msg->tsp_name)) {
+ syslog(LOG_NOTICE, "untrusted %s told us QUIT",
+ msg->tsp_name);
+ fromnet->quit_count = 2;
+ }
+ }
+}
+
+void
+traceon()
+{
+ if (!fd) {
+ fd = fopen(_PATH_TIMEDLOG, "w");
+ if (!fd) {
+ trace = 0;
+ return;
+ }
+ fprintf(fd,"Tracing started at %s\n", date());
+ }
+ trace = 1;
+ get_goodgroup(1);
+ setstatus();
+ prthp(CLK_TCK);
+}
+
+
+void
+traceoff(msg)
+ char *msg;
+{
+ get_goodgroup(1);
+ setstatus();
+ prthp(CLK_TCK);
+ if (trace) {
+ fprintf(fd, msg, date());
+ (void)fclose(fd);
+ fd = 0;
+ }
+#ifdef GPROF
+ moncontrol(0);
+ _mcleanup();
+ moncontrol(1);
+#endif
+ trace = OFF;
+}
+
+
+#ifdef sgi
+void
+logwtmp(otime, ntime)
+ struct timeval *otime, *ntime;
+{
+ static struct utmp wtmp[2] = {
+ {"","",OTIME_MSG,0,OLD_TIME,0,0,0},
+ {"","",NTIME_MSG,0,NEW_TIME,0,0,0}
+ };
+ static char *wtmpfile = WTMP_FILE;
+ int f;
+
+ wtmp[0].ut_time = otime->tv_sec + (otime->tv_usec + 500000) / 1000000;
+ wtmp[1].ut_time = ntime->tv_sec + (ntime->tv_usec + 500000) / 1000000;
+ if (wtmp[0].ut_time == wtmp[1].ut_time)
+ return;
+
+ setutent();
+ (void)pututline(&wtmp[0]);
+ (void)pututline(&wtmp[1]);
+ endutent();
+ if ((f = open(wtmpfile, O_WRONLY|O_APPEND)) >= 0) {
+ (void) write(f, (char *)wtmp, sizeof(wtmp));
+ (void) close(f);
+ }
+}
+#endif /* sgi */
diff --git a/usr.sbin/timed/timed/measure.c b/usr.sbin/timed/timed/measure.c
new file mode 100644
index 0000000..0882d5e
--- /dev/null
+++ b/usr.sbin/timed/timed/measure.c
@@ -0,0 +1,352 @@
+/*-
+ * Copyright (c) 1985, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)measure.c 8.1 (Berkeley) 6/6/93";
+#endif
+static const char rcsid[] =
+ "$Id: measure.c,v 1.4 1997/10/22 06:19:48 charnier Exp $";
+#endif /* not lint */
+
+#include "globals.h"
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/ip_icmp.h>
+
+#define MSEC_DAY (SECDAY*1000)
+
+#define PACKET_IN 1024
+
+#define MSGS 5 /* timestamps to average */
+#define TRIALS 10 /* max # of timestamps sent */
+
+extern int sock_raw;
+
+int measure_delta;
+
+static n_short seqno = 0;
+
+/*
+ * Measures the differences between machines' clocks using
+ * ICMP timestamp messages.
+ */
+int /* status val defined in globals.h */
+measure(maxmsec, wmsec, hname, addr, print)
+ u_long maxmsec; /* wait this many msec at most */
+ u_long wmsec; /* msec to wait for an answer */
+ char *hname;
+ struct sockaddr_in *addr;
+ int print; /* print complaints on stderr */
+{
+ int length;
+ int measure_status;
+ int rcvcount, trials;
+ int cc, count;
+ fd_set ready;
+ long sendtime, recvtime, histime1, histime2;
+ long idelta, odelta, total;
+ long min_idelta, min_odelta;
+ struct timeval tdone, tcur, ttrans, twait, tout;
+ u_char packet[PACKET_IN], opacket[64];
+ register struct icmp *icp = (struct icmp *) packet;
+ register struct icmp *oicp = (struct icmp *) opacket;
+ struct ip *ip = (struct ip *) packet;
+
+ min_idelta = min_odelta = 0x7fffffff;
+ measure_status = HOSTDOWN;
+ measure_delta = HOSTDOWN;
+ errno = 0;
+
+ /* open raw socket used to measure time differences */
+ if (sock_raw < 0) {
+ sock_raw = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
+ if (sock_raw < 0) {
+ syslog(LOG_ERR, "opening raw socket: %m");
+ goto quit;
+ }
+ }
+
+
+ /*
+ * empty the icmp input queue
+ */
+ FD_ZERO(&ready);
+ for (;;) {
+ tout.tv_sec = tout.tv_usec = 0;
+ FD_SET(sock_raw, &ready);
+ if (select(sock_raw+1, &ready, 0,0, &tout)) {
+ length = sizeof(struct sockaddr_in);
+ cc = recvfrom(sock_raw, (char *)packet, PACKET_IN, 0,
+ 0,&length);
+ if (cc < 0)
+ goto quit;
+ continue;
+ }
+ break;
+ }
+
+ /*
+ * Choose the smallest transmission time in each of the two
+ * directions. Use these two latter quantities to compute the delta
+ * between the two clocks.
+ */
+
+ oicp->icmp_type = ICMP_TSTAMP;
+ oicp->icmp_code = 0;
+ oicp->icmp_id = getpid();
+ oicp->icmp_rtime = 0;
+ oicp->icmp_ttime = 0;
+ oicp->icmp_seq = seqno;
+
+ FD_ZERO(&ready);
+
+#ifdef sgi
+ sginap(1); /* start at a clock tick */
+#endif /* sgi */
+
+ (void)gettimeofday(&tdone, 0);
+ mstotvround(&tout, maxmsec);
+ timevaladd(&tdone, &tout); /* when we give up */
+
+ mstotvround(&twait, wmsec);
+
+ rcvcount = 0;
+ trials = 0;
+ while (rcvcount < MSGS) {
+ (void)gettimeofday(&tcur, 0);
+
+ /*
+ * keep sending until we have sent the max
+ */
+ if (trials < TRIALS) {
+ trials++;
+ oicp->icmp_otime = htonl((tcur.tv_sec % SECDAY) * 1000
+ + tcur.tv_usec / 1000);
+ oicp->icmp_cksum = 0;
+ oicp->icmp_cksum = in_cksum((u_short*)oicp,
+ sizeof(*oicp));
+
+ count = sendto(sock_raw, opacket, sizeof(*oicp), 0,
+ (struct sockaddr*)addr,
+ sizeof(struct sockaddr));
+ if (count < 0) {
+ if (measure_status == HOSTDOWN)
+ measure_status = UNREACHABLE;
+ goto quit;
+ }
+ ++oicp->icmp_seq;
+
+ ttrans = tcur;
+ timevaladd(&ttrans, &twait);
+ } else {
+ ttrans = tdone;
+ }
+
+ while (rcvcount < trials) {
+ timevalsub(&tout, &ttrans, &tcur);
+ if (tout.tv_sec < 0)
+ tout.tv_sec = 0;
+
+ FD_SET(sock_raw, &ready);
+ count = select(sock_raw+1, &ready, (fd_set *)0,
+ (fd_set *)0, &tout);
+ (void)gettimeofday(&tcur, (struct timezone *)0);
+ if (count <= 0)
+ break;
+
+ length = sizeof(struct sockaddr_in);
+ cc = recvfrom(sock_raw, (char *)packet, PACKET_IN, 0,
+ 0,&length);
+ if (cc < 0)
+ goto quit;
+
+ /*
+ * got something. See if it is ours
+ */
+ icp = (struct icmp *)(packet + (ip->ip_hl << 2));
+ if (cc < sizeof(*ip)
+ || icp->icmp_type != ICMP_TSTAMPREPLY
+ || icp->icmp_id != oicp->icmp_id
+ || icp->icmp_seq < seqno
+ || icp->icmp_seq >= oicp->icmp_seq)
+ continue;
+
+
+ sendtime = ntohl(icp->icmp_otime);
+ recvtime = ((tcur.tv_sec % SECDAY) * 1000 +
+ tcur.tv_usec / 1000);
+
+ total = recvtime-sendtime;
+ if (total < 0) /* do not hassle midnight */
+ continue;
+
+ rcvcount++;
+ histime1 = ntohl(icp->icmp_rtime);
+ histime2 = ntohl(icp->icmp_ttime);
+ /*
+ * a host using a time format different from
+ * msec. since midnight UT (as per RFC792) should
+ * set the high order bit of the 32-bit time
+ * value it transmits.
+ */
+ if ((histime1 & 0x80000000) != 0) {
+ measure_status = NONSTDTIME;
+ goto quit;
+ }
+ measure_status = GOOD;
+
+ idelta = recvtime-histime2;
+ odelta = histime1-sendtime;
+
+ /* do not be confused by midnight */
+ if (idelta < -MSEC_DAY/2) idelta += MSEC_DAY;
+ else if (idelta > MSEC_DAY/2) idelta -= MSEC_DAY;
+
+ if (odelta < -MSEC_DAY/2) odelta += MSEC_DAY;
+ else if (odelta > MSEC_DAY/2) odelta -= MSEC_DAY;
+
+ /* save the quantization error so that we can get a
+ * measurement finer than our system clock.
+ */
+ if (total < MIN_ROUND) {
+ measure_delta = (odelta - idelta)/2;
+ goto quit;
+ }
+
+ if (idelta < min_idelta)
+ min_idelta = idelta;
+ if (odelta < min_odelta)
+ min_odelta = odelta;
+
+ measure_delta = (min_odelta - min_idelta)/2;
+ }
+
+ if (tcur.tv_sec > tdone.tv_sec
+ || (tcur.tv_sec == tdone.tv_sec
+ && tcur.tv_usec >= tdone.tv_usec))
+ break;
+ }
+
+quit:
+ seqno += TRIALS; /* allocate our sequence numbers */
+
+ /*
+ * If no answer is received for TRIALS consecutive times,
+ * the machine is assumed to be down
+ */
+ if (measure_status == GOOD) {
+ if (trace) {
+ fprintf(fd,
+ "measured delta %4d, %d trials to %-15s %s\n",
+ measure_delta, trials,
+ inet_ntoa(addr->sin_addr), hname);
+ }
+ } else if (print) {
+ if (errno != 0)
+ warn("measure %s", hname);
+ } else {
+ if (errno != 0) {
+ syslog(LOG_ERR, "measure %s: %m", hname);
+ } else {
+ syslog(LOG_ERR, "measure: %s did not respond", hname);
+ }
+ if (trace) {
+ fprintf(fd,
+ "measure: %s failed after %d trials\n",
+ hname, trials);
+ (void)fflush(fd);
+ }
+ }
+
+ return(measure_status);
+}
+
+
+
+
+
+/*
+ * round a number of milliseconds into a struct timeval
+ */
+void
+mstotvround(res, x)
+ struct timeval *res;
+ long x;
+{
+#ifndef sgi
+ if (x < 0)
+ x = -((-x + 3)/5);
+ else
+ x = (x+3)/5;
+ x *= 5;
+#endif /* sgi */
+ res->tv_sec = x/1000;
+ res->tv_usec = (x-res->tv_sec*1000)*1000;
+ if (res->tv_usec < 0) {
+ res->tv_usec += 1000000;
+ res->tv_sec--;
+ }
+}
+
+void
+timevaladd(tv1, tv2)
+ struct timeval *tv1, *tv2;
+{
+ tv1->tv_sec += tv2->tv_sec;
+ tv1->tv_usec += tv2->tv_usec;
+ if (tv1->tv_usec >= 1000000) {
+ tv1->tv_sec++;
+ tv1->tv_usec -= 1000000;
+ }
+ if (tv1->tv_usec < 0) {
+ tv1->tv_sec--;
+ tv1->tv_usec += 1000000;
+ }
+}
+
+void
+timevalsub(res, tv1, tv2)
+ struct timeval *res, *tv1, *tv2;
+{
+ res->tv_sec = tv1->tv_sec - tv2->tv_sec;
+ res->tv_usec = tv1->tv_usec - tv2->tv_usec;
+ if (res->tv_usec >= 1000000) {
+ res->tv_sec++;
+ res->tv_usec -= 1000000;
+ }
+ if (res->tv_usec < 0) {
+ res->tv_sec--;
+ res->tv_usec += 1000000;
+ }
+}
diff --git a/usr.sbin/timed/timed/networkdelta.c b/usr.sbin/timed/timed/networkdelta.c
new file mode 100644
index 0000000..aeb4a51
--- /dev/null
+++ b/usr.sbin/timed/timed/networkdelta.c
@@ -0,0 +1,264 @@
+/*-
+ * Copyright (c) 1985, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)networkdelta.c 8.1 (Berkeley) 6/6/93";
+#endif
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#include "globals.h"
+
+static long median __P((float, float *, long *, long *, unsigned int));
+
+/*
+ * Compute a corrected date.
+ * Compute the median of the reasonable differences. First compute
+ * the median of all authorized differences, and then compute the
+ * median of all differences that are reasonably close to the first
+ * median.
+ *
+ * This differs from the original BSD implementation, which looked for
+ * the largest group of machines with essentially the same date.
+ * That assumed that machines with bad clocks would be uniformly
+ * distributed. Unfortunately, in real life networks, the distribution
+ * of machines is not uniform among models of machines, and the
+ * distribution of errors in clocks tends to be quite consistent
+ * for a given model. In other words, all model VI Supre Servres
+ * from GoFast Inc. tend to have about the same error.
+ * The original BSD implementation would chose the clock of the
+ * most common model, and discard all others.
+ *
+ * Therefore, get best we can do is to try to average over all
+ * of the machines in the network, while discarding "obviously"
+ * bad values.
+ */
+long
+networkdelta()
+{
+ struct hosttbl *htp;
+ long med;
+ long lodelta, hidelta;
+ long logood, higood;
+ long x[NHOSTS];
+ long *xp;
+ int numdelta;
+ float eps;
+
+ /*
+ * compute the median of the good values
+ */
+ med = 0;
+ numdelta = 1;
+ xp = &x[0];
+ *xp = 0; /* account for ourself */
+ for (htp = self.l_fwd; htp != &self; htp = htp->l_fwd) {
+ if (htp->good
+ && htp->noanswer == 0
+ && htp->delta != HOSTDOWN) {
+ med += htp->delta;
+ numdelta++;
+ *++xp = htp->delta;
+ }
+ }
+
+ /*
+ * If we are the only trusted time keeper, then do not change our
+ * clock. There may be another time keeping service active.
+ */
+ if (numdelta == 1)
+ return 0;
+
+ med /= numdelta;
+ eps = med - x[0];
+ if (trace)
+ fprintf(fd, "median of %d values starting at %ld is about ",
+ numdelta, med);
+ med = median(med, &eps, &x[0], xp+1, VALID_RANGE);
+
+ /*
+ * compute the median of all values near the good median
+ */
+ hidelta = med + GOOD_RANGE;
+ lodelta = med - GOOD_RANGE;
+ higood = med + VGOOD_RANGE;
+ logood = med - VGOOD_RANGE;
+ xp = &x[0];
+ htp = &self;
+ do {
+ if (htp->noanswer == 0
+ && htp->delta >= lodelta
+ && htp->delta <= hidelta
+ && (htp->good
+ || (htp->delta >= logood
+ && htp->delta <= higood))) {
+ *xp++ = htp->delta;
+ }
+ } while (&self != (htp = htp->l_fwd));
+
+ if (xp == &x[0]) {
+ if (trace)
+ fprintf(fd, "nothing close to median %ld\n", med);
+ return med;
+ }
+
+ if (xp == &x[1]) {
+ if (trace)
+ fprintf(fd, "only value near median is %ld\n", x[0]);
+ return x[0];
+ }
+
+ if (trace)
+ fprintf(fd, "median of %d values starting at %ld is ",
+ xp-&x[0], med);
+ return median(med, &eps, &x[0], xp, 1);
+}
+
+
+/*
+ * compute the median of an array of signed integers, using the idea
+ * in <<Numerical Recipes>>.
+ */
+static long
+median(a, eps_ptr, x, xlim, gnuf)
+ float a; /* initial guess for the median */
+ float *eps_ptr; /* spacing near the median */
+ long *x, *xlim; /* the data */
+ unsigned int gnuf; /* good enough estimate */
+{
+ long *xptr;
+ float ap = LONG_MAX; /* bounds on the median */
+ float am = -LONG_MAX;
+ float aa;
+ int npts; /* # of points above & below guess */
+ float xp; /* closet point above the guess */
+ float xm; /* closet point below the guess */
+ float eps;
+ float dum, sum, sumx;
+ int pass;
+#define AMP 1.5 /* smoothing constants */
+#define AFAC 1.5
+
+ eps = *eps_ptr;
+ if (eps < 1.0) {
+ eps = -eps;
+ if (eps < 1.0)
+ eps = 1.0;
+ }
+
+ for (pass = 1; ; pass++) { /* loop over the data */
+ sum = 0.0;
+ sumx = 0.0;
+ npts = 0;
+ xp = LONG_MAX;
+ xm = -LONG_MAX;
+
+ for (xptr = x; xptr != xlim; xptr++) {
+ float xx = *xptr;
+
+ dum = xx - a;
+ if (dum != 0.0) { /* avoid dividing by 0 */
+ if (dum > 0.0) {
+ npts++;
+ if (xx < xp)
+ xp = xx;
+ } else {
+ npts--;
+ if (xx > xm)
+ xm = xx;
+ dum = -dum;
+ }
+ dum = 1.0/(eps + dum);
+ sum += dum;
+ sumx += xx * dum;
+ }
+ }
+
+ if (ap-am < gnuf || sum == 0) {
+ if (trace)
+ fprintf(fd,
+ "%ld in %d passes; early out balance=%d\n",
+ (long)a, pass, npts);
+ return a; /* guess was good enough */
+ }
+
+ aa = (sumx/sum-a)*AMP;
+ if (npts >= 2) { /* guess was too low */
+ am = a;
+ aa = xp + max(0.0, aa);;
+ if (aa > ap)
+ aa = (a + ap)/2;
+
+ } else if (npts <= -2) { /* guess was two high */
+ ap = a;
+ aa = xm + min(0.0, aa);;
+ if (aa < am)
+ aa = (a + am)/2;
+
+ } else {
+ break; /* got it */
+ }
+
+ if (a == aa) {
+ if (trace)
+ fprintf(fd,
+ "%ld in %d passes; force out balance=%d\n",
+ (long)a, pass, npts);
+ return a;
+ }
+ eps = AFAC*abs(aa - a);
+ *eps_ptr = eps;
+ a = aa;
+ }
+
+ if (((x - xlim) % 2) != 0) { /* even number of points? */
+ if (npts == 0) /* yes, return an average */
+ a = (xp+xm)/2;
+ else if (npts > 0)
+ a = (a+xp)/2;
+ else
+ a = (xm+a)/2;
+
+ } else if (npts != 0) { /* odd number of points */
+ if (npts > 0)
+ a = xp;
+ else
+ a = xm;
+ }
+
+ if (trace)
+ fprintf(fd, "%ld in %d passes\n", (long)a, pass);
+ return a;
+}
diff --git a/usr.sbin/timed/timed/pathnames.h b/usr.sbin/timed/timed/pathnames.h
new file mode 100644
index 0000000..ae5e1c5
--- /dev/null
+++ b/usr.sbin/timed/timed/pathnames.h
@@ -0,0 +1,44 @@
+/*-
+ * Copyright (c) 1985, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)pathnames.h 8.1 (Berkeley) 6/6/93
+ */
+
+#include <paths.h>
+
+#ifdef sgi
+#define _PATH_MASTERLOG "/usr/adm/timed.masterlog"
+#define _PATH_TIMEDLOG "/usr/adm/timed.log"
+#else
+#define _PATH_MASTERLOG "/var/log/timed.masterlog"
+#define _PATH_TIMEDLOG "/var/log/timed.log"
+#endif
diff --git a/usr.sbin/timed/timed/readmsg.c b/usr.sbin/timed/timed/readmsg.c
new file mode 100644
index 0000000..580bbbf
--- /dev/null
+++ b/usr.sbin/timed/timed/readmsg.c
@@ -0,0 +1,488 @@
+/*-
+ * Copyright (c) 1985, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)readmsg.c 8.1 (Berkeley) 6/6/93";
+#endif
+static const char rcsid[] =
+ "$Id: readmsg.c,v 1.2 1997/10/22 06:19:48 charnier Exp $";
+#endif /* not lint */
+
+#include "globals.h"
+
+extern char *tsptype[];
+
+/*
+ * LOOKAT checks if the message is of the requested type and comes from
+ * the right machine, returning 1 in case of affirmative answer
+ */
+#define LOOKAT(msg, mtype, mfrom, netp, froms) \
+ (((mtype) == TSP_ANY || (mtype) == (msg).tsp_type) && \
+ ((mfrom) == 0 || !strcmp((mfrom), (msg).tsp_name)) && \
+ ((netp) == 0 || \
+ ((netp)->mask & (froms).sin_addr.s_addr) == (netp)->net.s_addr))
+
+struct timeval rtime, rwait, rtout;
+struct tsp msgin;
+static struct tsplist {
+ struct tsp info;
+ struct timeval when;
+ struct sockaddr_in addr;
+ struct tsplist *p;
+} msgslist;
+struct sockaddr_in from;
+struct netinfo *fromnet;
+struct timeval from_when;
+
+/*
+ * `readmsg' returns message `type' sent by `machfrom' if it finds it
+ * either in the receive queue, or in a linked list of previously received
+ * messages that it maintains.
+ * Otherwise it waits to see if the appropriate message arrives within
+ * `intvl' seconds. If not, it returns NULL.
+ */
+
+struct tsp *
+readmsg(type, machfrom, intvl, netfrom)
+ int type;
+ char *machfrom;
+ struct timeval *intvl;
+ struct netinfo *netfrom;
+{
+ int length;
+ fd_set ready;
+ static struct tsplist *head = &msgslist;
+ static struct tsplist *tail = &msgslist;
+ static int msgcnt = 0;
+ struct tsplist *prev;
+ register struct netinfo *ntp;
+ register struct tsplist *ptr;
+
+ if (trace) {
+ fprintf(fd, "readmsg: looking for %s from %s, %s\n",
+ tsptype[type], machfrom == NULL ? "ANY" : machfrom,
+ netfrom == NULL ? "ANYNET" : inet_ntoa(netfrom->net));
+ if (head->p != 0) {
+ length = 1;
+ for (ptr = head->p; ptr != 0; ptr = ptr->p) {
+ /* do not repeat the hundreds of messages */
+ if (++length > 3) {
+ if (ptr == tail) {
+ fprintf(fd,"\t ...%d skipped\n",
+ length);
+ } else {
+ continue;
+ }
+ }
+ fprintf(fd, length > 1 ? "\t" : "queue:\t");
+ print(&ptr->info, &ptr->addr);
+ }
+ }
+ }
+
+ ptr = head->p;
+ prev = head;
+
+ /*
+ * Look for the requested message scanning through the
+ * linked list. If found, return it and free the space
+ */
+
+ while (ptr != NULL) {
+ if (LOOKAT(ptr->info, type, machfrom, netfrom, ptr->addr)) {
+again:
+ msgin = ptr->info;
+ from = ptr->addr;
+ from_when = ptr->when;
+ prev->p = ptr->p;
+ if (ptr == tail)
+ tail = prev;
+ free((char *)ptr);
+ fromnet = NULL;
+ if (netfrom == NULL)
+ for (ntp = nettab; ntp != NULL; ntp = ntp->next) {
+ if ((ntp->mask & from.sin_addr.s_addr) ==
+ ntp->net.s_addr) {
+ fromnet = ntp;
+ break;
+ }
+ }
+ else
+ fromnet = netfrom;
+ if (trace) {
+ fprintf(fd, "readmsg: found ");
+ print(&msgin, &from);
+ }
+
+/* The protocol can get far behind. When it does, it gets
+ * hopelessly confused. So delete duplicate messages.
+ */
+ for (ptr = prev; (ptr = ptr->p) != NULL; prev = ptr) {
+ if (ptr->addr.sin_addr.s_addr
+ == from.sin_addr.s_addr
+ && ptr->info.tsp_type == msgin.tsp_type) {
+ if (trace)
+ fprintf(fd, "\tdup ");
+ goto again;
+ }
+ }
+ msgcnt--;
+ return(&msgin);
+ } else {
+ prev = ptr;
+ ptr = ptr->p;
+ }
+ }
+
+ /*
+ * If the message was not in the linked list, it may still be
+ * coming from the network. Set the timer and wait
+ * on a select to read the next incoming message: if it is the
+ * right one, return it, otherwise insert it in the linked list.
+ */
+
+ (void)gettimeofday(&rtout, 0);
+ timevaladd(&rtout, intvl);
+ FD_ZERO(&ready);
+ for (;;) {
+ (void)gettimeofday(&rtime, 0);
+ timevalsub(&rwait, &rtout, &rtime);
+ if (rwait.tv_sec < 0)
+ rwait.tv_sec = rwait.tv_usec = 0;
+ else if (rwait.tv_sec == 0
+ && rwait.tv_usec < 1000000/CLK_TCK)
+ rwait.tv_usec = 1000000/CLK_TCK;
+
+ if (trace) {
+ fprintf(fd, "readmsg: wait %ld.%6ld at %s\n",
+ rwait.tv_sec, rwait.tv_usec, date());
+ /* Notice a full disk, as we flush trace info.
+ * It is better to flush periodically than at
+ * every line because the tracing consists of bursts
+ * of many lines. Without care, tracing slows
+ * down the code enough to break the protocol.
+ */
+ if (rwait.tv_sec != 0
+ && EOF == fflush(fd))
+ traceoff("Tracing ended for cause at %s\n");
+ }
+
+ FD_SET(sock, &ready);
+ if (!select(sock+1, &ready, (fd_set *)0, (fd_set *)0,
+ &rwait)) {
+ if (rwait.tv_sec == 0 && rwait.tv_usec == 0)
+ return(0);
+ continue;
+ }
+ length = sizeof(from);
+ if (recvfrom(sock, (char *)&msgin, sizeof(struct tsp), 0,
+ (struct sockaddr*)&from, &length) < 0) {
+ syslog(LOG_ERR, "recvfrom: %m");
+ exit(1);
+ }
+ (void)gettimeofday(&from_when, (struct timezone *)0);
+ bytehostorder(&msgin);
+
+ if (msgin.tsp_vers > TSPVERSION) {
+ if (trace) {
+ fprintf(fd,"readmsg: version mismatch\n");
+ /* should do a dump of the packet */
+ }
+ continue;
+ }
+
+ fromnet = NULL;
+ for (ntp = nettab; ntp != NULL; ntp = ntp->next)
+ if ((ntp->mask & from.sin_addr.s_addr) ==
+ ntp->net.s_addr) {
+ fromnet = ntp;
+ break;
+ }
+
+ /*
+ * drop packets from nets we are ignoring permanently
+ */
+ if (fromnet == NULL) {
+ /*
+ * The following messages may originate on
+ * this host with an ignored network address
+ */
+ if (msgin.tsp_type != TSP_TRACEON &&
+ msgin.tsp_type != TSP_SETDATE &&
+ msgin.tsp_type != TSP_MSITE &&
+ msgin.tsp_type != TSP_TEST &&
+ msgin.tsp_type != TSP_TRACEOFF) {
+ if (trace) {
+ fprintf(fd,"readmsg: discard null net ");
+ print(&msgin, &from);
+ }
+ continue;
+ }
+ }
+
+ /*
+ * Throw away messages coming from this machine,
+ * unless they are of some particular type.
+ * This gets rid of broadcast messages and reduces
+ * master processing time.
+ */
+ if (!strcmp(msgin.tsp_name, hostname)
+ && msgin.tsp_type != TSP_SETDATE
+ && msgin.tsp_type != TSP_TEST
+ && msgin.tsp_type != TSP_MSITE
+ && msgin.tsp_type != TSP_TRACEON
+ && msgin.tsp_type != TSP_TRACEOFF
+ && msgin.tsp_type != TSP_LOOP) {
+ if (trace) {
+ fprintf(fd, "readmsg: discard own ");
+ print(&msgin, &from);
+ }
+ continue;
+ }
+
+ /*
+ * Send acknowledgements here; this is faster and
+ * avoids deadlocks that would occur if acks were
+ * sent from a higher level routine. Different
+ * acknowledgements are necessary, depending on
+ * status.
+ */
+ if (fromnet == NULL) /* do not de-reference 0 */
+ ignoreack();
+ else if (fromnet->status == MASTER)
+ masterack();
+ else if (fromnet->status == SLAVE)
+ slaveack();
+ else
+ ignoreack();
+
+ if (LOOKAT(msgin, type, machfrom, netfrom, from)) {
+ if (trace) {
+ fprintf(fd, "readmsg: ");
+ print(&msgin, &from);
+ }
+ return(&msgin);
+ } else if (++msgcnt > NHOSTS*3) {
+
+/* The protocol gets hopelessly confused if it gets too far
+* behind. However, it seems able to recover from all cases of lost
+* packets. Therefore, if we are swamped, throw everything away.
+*/
+ if (trace)
+ fprintf(fd,
+ "readmsg: discarding %d msgs\n",
+ msgcnt);
+ msgcnt = 0;
+ while ((ptr=head->p) != NULL) {
+ head->p = ptr->p;
+ free((char *)ptr);
+ }
+ tail = head;
+ } else {
+ tail->p = (struct tsplist *)
+ malloc(sizeof(struct tsplist));
+ tail = tail->p;
+ tail->p = NULL;
+ tail->info = msgin;
+ tail->addr = from;
+ /* timestamp msgs so SETTIMEs are correct */
+ tail->when = from_when;
+ }
+ }
+}
+
+/*
+ * Send the necessary acknowledgements:
+ * only the type ACK is to be sent by a slave
+ */
+void
+slaveack()
+{
+ switch(msgin.tsp_type) {
+
+ case TSP_ADJTIME:
+ case TSP_SETTIME:
+ case TSP_ACCEPT:
+ case TSP_REFUSE:
+ case TSP_TRACEON:
+ case TSP_TRACEOFF:
+ case TSP_QUIT:
+ if (trace) {
+ fprintf(fd, "Slaveack: ");
+ print(&msgin, &from);
+ }
+ xmit(TSP_ACK,msgin.tsp_seq, &from);
+ break;
+
+ default:
+ if (trace) {
+ fprintf(fd, "Slaveack: no ack: ");
+ print(&msgin, &from);
+ }
+ break;
+ }
+}
+
+/*
+ * Certain packets may arrive from this machine on ignored networks.
+ * These packets should be acknowledged.
+ */
+void
+ignoreack()
+{
+ switch(msgin.tsp_type) {
+
+ case TSP_TRACEON:
+ case TSP_TRACEOFF:
+ case TSP_QUIT:
+ if (trace) {
+ fprintf(fd, "Ignoreack: ");
+ print(&msgin, &from);
+ }
+ xmit(TSP_ACK,msgin.tsp_seq, &from);
+ break;
+
+ default:
+ if (trace) {
+ fprintf(fd, "Ignoreack: no ack: ");
+ print(&msgin, &from);
+ }
+ break;
+ }
+}
+
+/*
+ * `masterack' sends the necessary acknowledgments
+ * to the messages received by a master
+ */
+void
+masterack()
+{
+ struct tsp resp;
+
+ resp = msgin;
+ resp.tsp_vers = TSPVERSION;
+ (void)strcpy(resp.tsp_name, hostname);
+
+ switch(msgin.tsp_type) {
+
+ case TSP_QUIT:
+ case TSP_TRACEON:
+ case TSP_TRACEOFF:
+ case TSP_MSITEREQ:
+ if (trace) {
+ fprintf(fd, "Masterack: ");
+ print(&msgin, &from);
+ }
+ xmit(TSP_ACK,msgin.tsp_seq, &from);
+ break;
+
+ case TSP_RESOLVE:
+ case TSP_MASTERREQ:
+ if (trace) {
+ fprintf(fd, "Masterack: ");
+ print(&msgin, &from);
+ }
+ xmit(TSP_MASTERACK,msgin.tsp_seq, &from);
+ break;
+
+ default:
+ if (trace) {
+ fprintf(fd,"Masterack: no ack: ");
+ print(&msgin, &from);
+ }
+ break;
+ }
+}
+
+/*
+ * Print a TSP message
+ */
+void
+print(msg, addr)
+ struct tsp *msg;
+ struct sockaddr_in *addr;
+{
+ char tm[26];
+ switch (msg->tsp_type) {
+
+ case TSP_LOOP:
+ fprintf(fd, "%s %d %-6u #%d %-15s %s\n",
+ tsptype[msg->tsp_type],
+ msg->tsp_vers,
+ msg->tsp_seq,
+ msg->tsp_hopcnt,
+ inet_ntoa(addr->sin_addr),
+ msg->tsp_name);
+ break;
+
+ case TSP_SETTIME:
+ case TSP_SETDATE:
+ case TSP_SETDATEREQ:
+#ifdef sgi
+ (void)cftime(tm, "%D %T", &msg->tsp_time.tv_sec);
+#else
+ strncpy(tm, ctime(&msg->tsp_time.tv_sec)+3+1, sizeof(tm));
+ tm[15] = '\0'; /* ugh */
+#endif /* sgi */
+ fprintf(fd, "%s %d %-6u %s %-15s %s\n",
+ tsptype[msg->tsp_type],
+ msg->tsp_vers,
+ msg->tsp_seq,
+ tm,
+ inet_ntoa(addr->sin_addr),
+ msg->tsp_name);
+ break;
+
+ case TSP_ADJTIME:
+ fprintf(fd, "%s %d %-6u (%ld,%ld) %-15s %s\n",
+ tsptype[msg->tsp_type],
+ msg->tsp_vers,
+ msg->tsp_seq,
+ msg->tsp_time.tv_sec,
+ msg->tsp_time.tv_usec,
+ inet_ntoa(addr->sin_addr),
+ msg->tsp_name);
+ break;
+
+ default:
+ fprintf(fd, "%s %d %-6u %-15s %s\n",
+ tsptype[msg->tsp_type],
+ msg->tsp_vers,
+ msg->tsp_seq,
+ inet_ntoa(addr->sin_addr),
+ msg->tsp_name);
+ break;
+ }
+}
diff --git a/usr.sbin/timed/timed/slave.c b/usr.sbin/timed/timed/slave.c
new file mode 100644
index 0000000..bab9bcd
--- /dev/null
+++ b/usr.sbin/timed/timed/slave.c
@@ -0,0 +1,716 @@
+/*-
+ * Copyright (c) 1985, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)slave.c 8.1 (Berkeley) 6/6/93";
+#endif
+static const char rcsid[] =
+ "$Id: slave.c,v 1.4 1997/10/29 07:32:29 charnier Exp $";
+#endif /* not lint */
+
+#include "globals.h"
+#include <setjmp.h>
+#include "pathnames.h"
+
+extern jmp_buf jmpenv;
+extern int Mflag;
+extern int justquit;
+
+extern u_short sequence;
+
+static char master_name[MAXHOSTNAMELEN];
+static struct netinfo *old_slavenet;
+static int old_status;
+
+static void schgdate __P((struct tsp *, char *));
+static void setmaster __P((struct tsp *));
+static void answerdelay __P((void));
+
+#ifdef sgi
+extern void logwtmp __P((struct timeval *, struct timeval *));
+#else
+extern void logwtmp __P((char *, char *, char *));
+#endif /* sgi */
+
+int
+slave()
+{
+ int tries;
+ long electiontime, refusetime, looktime, looptime, adjtime;
+ u_short seq;
+ long fastelection;
+#define FASTTOUT 3
+ struct in_addr cadr;
+ struct timeval otime;
+ struct sockaddr_in taddr;
+ char tname[MAXHOSTNAMELEN];
+ struct tsp *msg, to;
+ struct timeval ntime, wait;
+ struct tsp *answer;
+ int timeout();
+ char olddate[32];
+ char newdate[32];
+ struct netinfo *ntp;
+ struct hosttbl *htp;
+
+
+ old_slavenet = 0;
+ seq = 0;
+ refusetime = 0;
+ adjtime = 0;
+
+ (void)gettimeofday(&ntime, 0);
+ electiontime = ntime.tv_sec + delay2;
+ fastelection = ntime.tv_sec + FASTTOUT;
+ if (justquit)
+ looktime = electiontime;
+ else
+ looktime = fastelection;
+ looptime = fastelection;
+
+ if (slavenet)
+ xmit(TSP_SLAVEUP, 0, &slavenet->dest_addr);
+ if (status & MASTER) {
+ for (ntp = nettab; ntp != NULL; ntp = ntp->next) {
+ if (ntp->status == MASTER)
+ masterup(ntp);
+ }
+ }
+
+loop:
+ get_goodgroup(0);
+ (void)gettimeofday(&ntime, (struct timezone *)0);
+ if (ntime.tv_sec > electiontime) {
+ if (trace)
+ fprintf(fd, "election timer expired\n");
+ longjmp(jmpenv, 1);
+ }
+
+ if (ntime.tv_sec >= looktime) {
+ if (trace)
+ fprintf(fd, "Looking for nets to master\n");
+
+ if (Mflag && nignorednets > 0) {
+ for (ntp = nettab; ntp != NULL; ntp = ntp->next) {
+ if (ntp->status == IGNORE
+ || ntp->status == NOMASTER) {
+ lookformaster(ntp);
+ if (ntp->status == MASTER) {
+ masterup(ntp);
+ } else if (ntp->status == MASTER) {
+ ntp->status = NOMASTER;
+ }
+ }
+ if (ntp->status == MASTER
+ && --ntp->quit_count < 0)
+ ntp->quit_count = 0;
+ }
+ makeslave(slavenet); /* prune extras */
+ setstatus();
+ }
+ (void)gettimeofday(&ntime, 0);
+ looktime = ntime.tv_sec + delay2;
+ }
+ if (ntime.tv_sec >= looptime) {
+ if (trace)
+ fprintf(fd, "Looking for loops\n");
+ for (ntp = nettab; ntp != NULL; ntp = ntp->next) {
+ if (ntp->status == MASTER) {
+ to.tsp_type = TSP_LOOP;
+ to.tsp_vers = TSPVERSION;
+ to.tsp_seq = sequence++;
+ to.tsp_hopcnt = MAX_HOPCNT;
+ (void)strcpy(to.tsp_name, hostname);
+ bytenetorder(&to);
+ if (sendto(sock, (char *)&to, sizeof(struct tsp), 0,
+ (struct sockaddr*)&ntp->dest_addr,
+ sizeof(ntp->dest_addr)) < 0) {
+ trace_sendto_err(ntp->dest_addr.sin_addr);
+ }
+ }
+ }
+ (void)gettimeofday(&ntime, 0);
+ looptime = ntime.tv_sec + delay2;
+ }
+
+ wait.tv_sec = min(electiontime,min(looktime,looptime)) - ntime.tv_sec;
+ if (wait.tv_sec < 0)
+ wait.tv_sec = 0;
+ wait.tv_sec += FASTTOUT;
+ wait.tv_usec = 0;
+ msg = readmsg(TSP_ANY, ANYADDR, &wait, 0);
+
+ if (msg != NULL) {
+ /*
+ * filter stuff not for us
+ */
+ switch (msg->tsp_type) {
+ case TSP_SETDATE:
+ case TSP_TRACEOFF:
+ case TSP_TRACEON:
+ /*
+ * XXX check to see they are from ourself
+ */
+ break;
+
+ case TSP_TEST:
+ case TSP_MSITE:
+ break;
+
+ case TSP_MASTERUP:
+ if (!fromnet) {
+ if (trace) {
+ fprintf(fd, "slave ignored: ");
+ print(msg, &from);
+ }
+ goto loop;
+ }
+ break;
+
+ default:
+ if (!fromnet
+ || fromnet->status == IGNORE
+ || fromnet->status == NOMASTER) {
+ if (trace) {
+ fprintf(fd, "slave ignored: ");
+ print(msg, &from);
+ }
+ goto loop;
+ }
+ break;
+ }
+
+
+ /*
+ * now process the message
+ */
+ switch (msg->tsp_type) {
+
+ case TSP_ADJTIME:
+ if (fromnet != slavenet)
+ break;
+ if (!good_host_name(msg->tsp_name)) {
+ syslog(LOG_NOTICE,
+ "attempted time adjustment by %s",
+ msg->tsp_name);
+ suppress(&from, msg->tsp_name, fromnet);
+ break;
+ }
+ /*
+ * Speed up loop detection in case we have a loop.
+ * Otherwise the clocks can race until the loop
+ * is found.
+ */
+ (void)gettimeofday(&otime, 0);
+ if (adjtime < otime.tv_sec)
+ looptime -= (looptime-otime.tv_sec)/2 + 1;
+
+ setmaster(msg);
+ if (seq != msg->tsp_seq) {
+ seq = msg->tsp_seq;
+ synch(tvtomsround(msg->tsp_time));
+ }
+ (void)gettimeofday(&ntime, 0);
+ electiontime = ntime.tv_sec + delay2;
+ fastelection = ntime.tv_sec + FASTTOUT;
+ adjtime = ntime.tv_sec + SAMPLEINTVL*2;
+ break;
+
+ case TSP_SETTIME:
+ if (fromnet != slavenet)
+ break;
+ if (seq == msg->tsp_seq)
+ break;
+ seq = msg->tsp_seq;
+
+ /* adjust time for residence on the queue */
+ (void)gettimeofday(&otime, 0);
+ adj_msg_time(msg,&otime);
+#ifdef sgi
+ (void)cftime(newdate, "%D %T", &msg->tsp_time.tv_sec);
+ (void)cftime(olddate, "%D %T", &otime.tv_sec);
+#else
+ /*
+ * the following line is necessary due to syslog
+ * calling ctime() which clobbers the static buffer
+ */
+ (void)strcpy(olddate, date());
+ (void)strcpy(newdate, ctime(&msg->tsp_time.tv_sec));
+#endif /* sgi */
+
+ if (!good_host_name(msg->tsp_name)) {
+ syslog(LOG_NOTICE,
+ "attempted time setting by untrusted %s to %s",
+ msg->tsp_name, newdate);
+ suppress(&from, msg->tsp_name, fromnet);
+ break;
+ }
+
+ setmaster(msg);
+ timevalsub(&ntime, &msg->tsp_time, &otime);
+ if (ntime.tv_sec < MAXADJ && ntime.tv_sec > -MAXADJ) {
+ /*
+ * do not change the clock if we can adjust it
+ */
+ synch(tvtomsround(ntime));
+ } else {
+#ifdef sgi
+ if (0 > settimeofday(&msg->tsp_time, 0)) {
+ syslog(LOG_ERR,"settimeofdate(): %m");
+ break;
+ }
+ logwtmp(&otime, &msg->tsp_time);
+#else
+ logwtmp("|", "date", "");
+ (void)settimeofday(&msg->tsp_time, 0);
+ logwtmp("{", "date", "");
+#endif /* sgi */
+ syslog(LOG_NOTICE,
+ "date changed by %s from %s",
+ msg->tsp_name, olddate);
+ if (status & MASTER)
+ spreadtime();
+ }
+ (void)gettimeofday(&ntime, 0);
+ electiontime = ntime.tv_sec + delay2;
+ fastelection = ntime.tv_sec + FASTTOUT;
+
+/* This patches a bad protocol bug. Imagine a system with several networks,
+ * where there are a pair of redundant gateways between a pair of networks,
+ * each running timed. Assume that we start with a third machine mastering
+ * one of the networks, and one of the gateways mastering the other.
+ * Imagine that the third machine goes away and the non-master gateway
+ * decides to replace it. If things are timed just 'right,' we will have
+ * each gateway mastering one network for a little while. If a SETTIME
+ * message gets into the network at that time, perhaps from the newly
+ * masterful gateway as it was taking control, the SETTIME will loop
+ * forever. Each time a gateway receives it on its slave side, it will
+ * call spreadtime to forward it on its mastered network. We are now in
+ * a permanent loop, since the SETTIME msgs will keep any clock
+ * in the network from advancing. Normally, the 'LOOP' stuff will detect
+ * and correct the situation. However, with the clocks stopped, the
+ * 'looptime' timer cannot expire. While they are in this state, the
+ * masters will try to saturate the network with SETTIME packets.
+ */
+ looptime = ntime.tv_sec + (looptime-otime.tv_sec)/2-1;
+ break;
+
+ case TSP_MASTERUP:
+ if (slavenet && fromnet != slavenet)
+ break;
+ if (!good_host_name(msg->tsp_name)) {
+ suppress(&from, msg->tsp_name, fromnet);
+ if (electiontime > fastelection)
+ electiontime = fastelection;
+ break;
+ }
+ makeslave(fromnet);
+ setmaster(msg);
+ setstatus();
+ answerdelay();
+ xmit(TSP_SLAVEUP, 0, &from);
+ (void)gettimeofday(&ntime, 0);
+ electiontime = ntime.tv_sec + delay2;
+ fastelection = ntime.tv_sec + FASTTOUT;
+ refusetime = 0;
+ break;
+
+ case TSP_MASTERREQ:
+ if (fromnet->status != SLAVE)
+ break;
+ (void)gettimeofday(&ntime, 0);
+ electiontime = ntime.tv_sec + delay2;
+ break;
+
+ case TSP_SETDATE:
+#ifdef sgi
+ (void)cftime(newdate, "%D %T", &msg->tsp_time.tv_sec);
+#else
+ (void)strcpy(newdate, ctime(&msg->tsp_time.tv_sec));
+#endif /* sgi */
+ schgdate(msg, newdate);
+ break;
+
+ case TSP_SETDATEREQ:
+ if (fromnet->status != MASTER)
+ break;
+#ifdef sgi
+ (void)cftime(newdate, "%D %T", &msg->tsp_time.tv_sec);
+#else
+ (void)strcpy(newdate, ctime(&msg->tsp_time.tv_sec));
+#endif /* sgi */
+ htp = findhost(msg->tsp_name);
+ if (0 == htp) {
+ syslog(LOG_WARNING,
+ "DATEREQ from uncontrolled machine");
+ break;
+ }
+ if (!htp->good) {
+ syslog(LOG_WARNING,
+ "attempted date change by untrusted %s to %s",
+ htp->name, newdate);
+ spreadtime();
+ break;
+ }
+ schgdate(msg, newdate);
+ break;
+
+ case TSP_TRACEON:
+ traceon();
+ break;
+
+ case TSP_TRACEOFF:
+ traceoff("Tracing ended at %s\n");
+ break;
+
+ case TSP_SLAVEUP:
+ newslave(msg);
+ break;
+
+ case TSP_ELECTION:
+ if (fromnet->status == SLAVE) {
+ (void)gettimeofday(&ntime, 0);
+ electiontime = ntime.tv_sec + delay2;
+ fastelection = ntime.tv_sec + FASTTOUT;
+ seq = 0;
+ if (!good_host_name(msg->tsp_name)) {
+ syslog(LOG_NOTICE,
+ "suppress election of %s",
+ msg->tsp_name);
+ to.tsp_type = TSP_QUIT;
+ electiontime = fastelection;
+ } else if (cadr.s_addr != from.sin_addr.s_addr
+ && ntime.tv_sec < refusetime) {
+/* if the candidate has to repeat itself, the old code would refuse it
+ * the second time. That would prevent elections.
+ */
+ to.tsp_type = TSP_REFUSE;
+ } else {
+ cadr.s_addr = from.sin_addr.s_addr;
+ to.tsp_type = TSP_ACCEPT;
+ refusetime = ntime.tv_sec + 30;
+ }
+ taddr = from;
+ (void)strcpy(tname, msg->tsp_name);
+ (void)strcpy(to.tsp_name, hostname);
+ answerdelay();
+ if (!acksend(&to, &taddr, tname,
+ TSP_ACK, 0, 0))
+ syslog(LOG_WARNING,
+ "no answer from candidate %s\n",
+ tname);
+
+ } else { /* fromnet->status == MASTER */
+ htp = addmach(msg->tsp_name, &from,fromnet);
+ to.tsp_type = TSP_QUIT;
+ (void)strcpy(to.tsp_name, hostname);
+ if (!acksend(&to, &htp->addr, htp->name,
+ TSP_ACK, 0, htp->noanswer)) {
+ syslog(LOG_ERR,
+ "no reply from %s to ELECTION-QUIT",
+ htp->name);
+ (void)remmach(htp);
+ }
+ }
+ break;
+
+ case TSP_CONFLICT:
+ if (fromnet->status != MASTER)
+ break;
+ /*
+ * After a network partition, there can be
+ * more than one master: the first slave to
+ * come up will notify here the situation.
+ */
+ (void)strcpy(to.tsp_name, hostname);
+
+ /* The other master often gets into the same state,
+ * with boring results.
+ */
+ ntp = fromnet; /* (acksend() can leave fromnet=0 */
+ for (tries = 0; tries < 3; tries++) {
+ to.tsp_type = TSP_RESOLVE;
+ answer = acksend(&to, &ntp->dest_addr,
+ ANYADDR, TSP_MASTERACK,
+ ntp, 0);
+ if (answer == NULL)
+ break;
+ htp = addmach(answer->tsp_name,&from,ntp);
+ to.tsp_type = TSP_QUIT;
+ answer = acksend(&to, &htp->addr, htp->name,
+ TSP_ACK, 0, htp->noanswer);
+ if (!answer) {
+ syslog(LOG_WARNING,
+ "conflict error: no reply from %s to QUIT",
+ htp->name);
+ (void)remmach(htp);
+ }
+ }
+ masterup(ntp);
+ break;
+
+ case TSP_MSITE:
+ if (!slavenet)
+ break;
+ taddr = from;
+ to.tsp_type = TSP_MSITEREQ;
+ to.tsp_vers = TSPVERSION;
+ to.tsp_seq = 0;
+ (void)strcpy(to.tsp_name, hostname);
+ answer = acksend(&to, &slavenet->dest_addr,
+ ANYADDR, TSP_ACK,
+ slavenet, 0);
+ if (answer != NULL
+ && good_host_name(answer->tsp_name)) {
+ setmaster(answer);
+ to.tsp_type = TSP_ACK;
+ (void)strcpy(to.tsp_name, answer->tsp_name);
+ bytenetorder(&to);
+ if (sendto(sock, (char *)&to,
+ sizeof(struct tsp), 0,
+ (struct sockaddr*)&taddr,
+ sizeof(taddr)) < 0) {
+ trace_sendto_err(taddr.sin_addr);
+ }
+ }
+ break;
+
+ case TSP_MSITEREQ:
+ break;
+
+ case TSP_ACCEPT:
+ case TSP_REFUSE:
+ case TSP_RESOLVE:
+ break;
+
+ case TSP_QUIT:
+ doquit(msg); /* become a slave */
+ break;
+
+ case TSP_TEST:
+ electiontime = 0;
+ break;
+
+ case TSP_LOOP:
+ /* looking for loops of masters */
+ if (!(status & MASTER))
+ break;
+ if (fromnet->status == SLAVE) {
+ if (!strcmp(msg->tsp_name, hostname)) {
+ /*
+ * Someone forwarded our message back to
+ * us. There must be a loop. Tell the
+ * master of this network to quit.
+ *
+ * The other master often gets into
+ * the same state, with boring results.
+ */
+ ntp = fromnet;
+ for (tries = 0; tries < 3; tries++) {
+ to.tsp_type = TSP_RESOLVE;
+ answer = acksend(&to, &ntp->dest_addr,
+ ANYADDR, TSP_MASTERACK,
+ ntp,0);
+ if (answer == NULL)
+ break;
+ taddr = from;
+ (void)strcpy(tname, answer->tsp_name);
+ to.tsp_type = TSP_QUIT;
+ (void)strcpy(to.tsp_name, hostname);
+ if (!acksend(&to, &taddr, tname,
+ TSP_ACK, 0, 1)) {
+ syslog(LOG_ERR,
+ "no reply from %s to slave LOOP-QUIT",
+ tname);
+ } else {
+ electiontime = 0;
+ }
+ }
+ (void)gettimeofday(&ntime, 0);
+ looptime = ntime.tv_sec + FASTTOUT;
+ } else {
+ if (msg->tsp_hopcnt-- < 1)
+ break;
+ bytenetorder(msg);
+ for (ntp = nettab; ntp != 0; ntp = ntp->next) {
+ if (ntp->status == MASTER
+ && 0 > sendto(sock, (char *)msg,
+ sizeof(struct tsp), 0,
+ (struct sockaddr*)&ntp->dest_addr,
+ sizeof(ntp->dest_addr)))
+ trace_sendto_err(ntp->dest_addr.sin_addr);
+ }
+ }
+ } else { /* fromnet->status == MASTER */
+ /*
+ * We should not have received this from a net
+ * we are master on. There must be two masters,
+ * unless the packet was really from us.
+ */
+ if (from.sin_addr.s_addr
+ == fromnet->my_addr.s_addr) {
+ if (trace)
+ fprintf(fd,"discarding forwarded LOOP\n");
+ break;
+ }
+
+ /*
+ * The other master often gets into the same
+ * state, with boring results.
+ */
+ ntp = fromnet;
+ for (tries = 0; tries < 3; tries++) {
+ to.tsp_type = TSP_RESOLVE;
+ answer = acksend(&to, &ntp->dest_addr,
+ ANYADDR, TSP_MASTERACK,
+ ntp,0);
+ if (!answer)
+ break;
+ htp = addmach(answer->tsp_name,
+ &from,ntp);
+ to.tsp_type = TSP_QUIT;
+ (void)strcpy(to.tsp_name, hostname);
+ if (!acksend(&to,&htp->addr,htp->name,
+ TSP_ACK, 0, htp->noanswer)) {
+ syslog(LOG_ERR,
+ "no reply from %s to master LOOP-QUIT",
+ htp->name);
+ (void)remmach(htp);
+ }
+ }
+ (void)gettimeofday(&ntime, 0);
+ looptime = ntime.tv_sec + FASTTOUT;
+ }
+ break;
+ default:
+ if (trace) {
+ fprintf(fd, "garbage message: ");
+ print(msg, &from);
+ }
+ break;
+ }
+ }
+ goto loop;
+}
+
+
+/*
+ * tell the world who our master is
+ */
+static void
+setmaster(msg)
+ struct tsp *msg;
+{
+ if (slavenet
+ && (slavenet != old_slavenet
+ || strcmp(msg->tsp_name, master_name)
+ || old_status != status)) {
+ (void)strcpy(master_name, msg->tsp_name);
+ old_slavenet = slavenet;
+ old_status = status;
+
+ if (status & MASTER) {
+ syslog(LOG_NOTICE, "submaster to %s", master_name);
+ if (trace)
+ fprintf(fd, "submaster to %s\n", master_name);
+
+ } else {
+ syslog(LOG_NOTICE, "slave to %s", master_name);
+ if (trace)
+ fprintf(fd, "slave to %s\n", master_name);
+ }
+ }
+}
+
+
+
+/*
+ * handle date change request on a slave
+ */
+static void
+schgdate(msg, newdate)
+ struct tsp *msg;
+ char *newdate;
+{
+ struct tsp to;
+ u_short seq;
+ struct sockaddr_in taddr;
+ struct timeval otime;
+
+ if (!slavenet)
+ return; /* no where to forward */
+
+ taddr = from;
+ seq = msg->tsp_seq;
+
+ syslog(LOG_INFO,
+ "forwarding date change by %s to %s",
+ msg->tsp_name, newdate);
+
+ /* adjust time for residence on the queue */
+ (void)gettimeofday(&otime, 0);
+ adj_msg_time(msg, &otime);
+
+ to.tsp_type = TSP_SETDATEREQ;
+ to.tsp_time = msg->tsp_time;
+ (void)strcpy(to.tsp_name, hostname);
+ if (!acksend(&to, &slavenet->dest_addr,
+ ANYADDR, TSP_DATEACK,
+ slavenet, 0))
+ return; /* no answer */
+
+ xmit(TSP_DATEACK, seq, &taddr);
+}
+
+
+/*
+ * Used before answering a broadcast message to avoid network
+ * contention and likely collisions.
+ */
+static void
+answerdelay()
+{
+#ifdef sgi
+ sginap(delay1);
+#else
+ struct timeval timeout;
+
+ timeout.tv_sec = 0;
+ timeout.tv_usec = delay1;
+
+ (void)select(0, (fd_set *)NULL, (fd_set *)NULL, (fd_set *)NULL,
+ &timeout);
+ return;
+#endif /* sgi */
+}
diff --git a/usr.sbin/timed/timed/timed.8 b/usr.sbin/timed/timed/timed.8
new file mode 100644
index 0000000..a8eb74e
--- /dev/null
+++ b/usr.sbin/timed/timed/timed.8
@@ -0,0 +1,226 @@
+.\" Copyright (c) 1980, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)timed.8 8.1 (Berkeley) 6/6/93
+.\"
+.Dd June 6, 1993
+.Dt TIMED 8
+.Os BSD 4.3
+.Sh NAME
+.Nm timed
+.Nd time server daemon
+.Sh SYNOPSIS
+.Nm timed
+.Op Fl M
+.Op Fl t
+.Op Fl d
+.Op Fl i Ar network
+.Op Fl n Ar network
+.Op Fl F Ar host1 host2 ...
+.Sh DESCRIPTION
+This
+is a time server daemon and is normally invoked
+at boot time from the
+.Xr rc 8
+file.
+It synchronizes the host's time with the time of other
+machines in a local area network running
+.Nm Ns .
+These time servers will slow down the clocks of some machines
+and speed up the clocks of others to bring them to the average network time.
+The average network time is computed from measurements of clock differences
+using the
+.Tn ICMP
+timestamp request message.
+.Pp
+The service provided by
+.Nm
+is based on a master-slave
+scheme.
+When
+.Nm
+is started on a machine, it asks the master for the network time
+and sets the host's clock to that time.
+After that, it accepts synchronization messages periodically sent by
+the master and calls
+.Xr adjtime 2
+to perform the needed corrections on the host's clock.
+.Pp
+It also communicates with
+.Xr date 1
+in order to set the date globally,
+and with
+.Xr timedc 8 ,
+a timed control program.
+If the machine running the master crashes, then the slaves will elect
+a new master from among slaves running with the
+.Fl M
+flag.
+A
+.Nm
+running without the
+.Fl M
+or
+.Fl F
+flags will remain a slave.
+The
+.Fl t
+flag enables
+.Nm timed
+to trace the messages it receives in the
+file
+.Pa /var/log/timed.log .
+Tracing can be turned on or off by the program
+.Xr timedc 8 .
+The
+.Fl d
+flag is for debugging the daemon.
+It causes the program to not put itself into the background.
+.Pp
+Normally
+.Nm
+checks for a master time server on each network to which
+it is connected, except as modified by the options described below.
+It will request synchronization service from the first master server
+located.
+If permitted by the
+.Fl M
+flag, it will provide synchronization service on any attached networks
+on which no current master server was detected.
+Such a server propagates the time computed by the top-level master.
+The
+.Fl n
+flag, followed by the name of a network which the host is connected to
+(see
+.Xr networks 5 ) ,
+overrides the default choice of the
+network addresses made by the program.
+Each time the
+.Fl n
+flag appears, that network name is added to a list of valid networks.
+All other networks are ignored.
+The
+.Fl i
+flag, followed by the name of a network to which the host is connected
+(see
+.Xr networks 5 ) ,
+overrides the default choice of the network addresses made by the program.
+Each time the
+.Fl i
+flag appears, that network name is added to a list of networks to ignore.
+All other networks are used by the time daemon.
+The
+.Fl n
+and
+.Fl i
+flags are meaningless if used together.
+.Pp
+.Nm Timed
+checks for a master time server on each network to which
+it is connected, except as modified by the
+.Fl n
+and
+.Fl i
+options described above.
+If it finds masters on more than one network, it chooses one network
+on which to be a "slave," and then periodically checks the other
+networks to see if the masters there have disappeared.
+.Pp
+One way to synchronize a group of machines is to use an
+.Tn NTP
+daemon to
+synchronize the clock of one machine to a distant standard or a radio
+receiver and
+.Fl F Ar hostname
+to tell its timed daemon to trust only itself.
+.Pp
+Messages printed by the kernel on the system console occur with
+interrupts disabled.
+This means that the clock stops while they are printing.
+A machine with many disk or network hardware problems and consequent
+messages cannot keep good time by itself. Each message typically causes
+the clock to lose a dozen milliseconds. A time daemon can
+correct the result.
+.Pp
+Messages in the system log about machines that failed to respond
+usually indicate machines that crashed or were turned off.
+Complaints about machines that failed to respond to initial time
+settings are often associated with "multi-homed" machines
+that looked for time masters on more than one network and eventually
+chose to become a slave on the other network.
+.Sh WARNINGS
+If two or more time daemons, whether
+.Nm Ns ,
+.Tn NTP ,
+try to adjust the same clock, temporal chaos will result.
+If both
+.Nm
+and another time daemon are run on the same machine,
+ensure that the
+.Fl F
+flag is used, so that
+.Nm
+never attempts to adjust the local clock.
+.Pp
+The protocol is based on
+.Tn UDP/IP
+broadcasts. All machines within
+the range of a broadcast that are using the
+.Tn TSP
+protocol must cooperate.
+There cannot be more than a single administrative domain using the
+.Fl F
+flag among all machines reached by a broadcast packet.
+Failure to follow this rule is usually indicated by complaints concerning
+"untrusted" machines in the system log.
+.Sh FILES
+.Bl -tag -width /var/log/timed.masterlog -compact
+.It Pa /var/log/timed.log
+tracing file for timed
+.It Pa /var/log/timed.masterlog
+log file for master timed
+.El
+.Sh SEE ALSO
+.Xr date 1 ,
+.Xr adjtime 2 ,
+.Xr gettimeofday 2 ,
+.Xr icmp 4 ,
+.Xr timedc 8
+.Rs
+.%T "TSP: The Time Synchronization Protocol for UNIX 4.3BSD"
+.%A R. Gusella
+.%A S. Zatti
+.Re
+.Sh HISTORY
+The
+.Nm
+daemon appeared in
+.Bx 4.3 .
diff --git a/usr.sbin/timed/timed/timed.c b/usr.sbin/timed/timed/timed.c
new file mode 100644
index 0000000..ec0ca63
--- /dev/null
+++ b/usr.sbin/timed/timed/timed.c
@@ -0,0 +1,964 @@
+/*-
+ * Copyright (c) 1985, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 1985, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)timed.c 8.1 (Berkeley) 6/6/93";
+#endif
+static const char rcsid[] =
+ "$Id: timed.c,v 1.6 1997/10/29 07:32:30 charnier Exp $";
+#endif /* not lint */
+
+#define TSPTYPES
+#include "globals.h"
+#include <net/if.h>
+#include <sys/file.h>
+#include <sys/ioctl.h>
+#include <setjmp.h>
+#include "pathnames.h"
+#include <math.h>
+#include <sys/types.h>
+#include <sys/times.h>
+#ifdef sgi
+#include <unistd.h>
+#include <sys/syssgi.h>
+#include <sys/schedctl.h>
+#endif /* sgi */
+
+int trace = 0;
+int sock, sock_raw = -1;
+int status = 0;
+u_short sequence; /* sequence number */
+long delay1;
+long delay2;
+
+int nslavenets; /* nets were I could be a slave */
+int nmasternets; /* nets were I could be a master */
+int nignorednets; /* ignored nets */
+int nnets; /* nets I am connected to */
+
+FILE *fd; /* trace file FD */
+
+jmp_buf jmpenv;
+
+struct netinfo *nettab = 0;
+struct netinfo *slavenet;
+int Mflag;
+int justquit = 0;
+int debug;
+
+static struct nets {
+ char *name;
+ long net;
+ struct nets *next;
+} *nets = 0;
+
+struct hosttbl hosttbl[NHOSTS+1]; /* known hosts */
+
+static struct goodhost { /* hosts that we trust */
+ char name[MAXHOSTNAMELEN];
+ struct goodhost *next;
+ char perm;
+} *goodhosts;
+
+static char *goodgroup; /* net group of trusted hosts */
+static void checkignorednets __P((void));
+static void pickslavenet __P((struct netinfo *));
+static void add_good_host __P((char *, int));
+
+#ifdef sgi
+char *timetrim_fn;
+char *timetrim_wpat = "long timetrim = %ld;\ndouble tot_adj = %.0f;\ndouble tot_ticks = %.0f;\n/* timed version 2 */\n";
+char *timetrim_rpat = "long timetrim = %ld;\ndouble tot_adj = %lf;\ndouble tot_ticks = %lf;";
+long timetrim;
+double tot_adj, hr_adj; /* totals in nsec */
+double tot_ticks, hr_ticks;
+
+int bufspace = 60*1024;
+#endif
+
+static void usage __P((void));
+
+/*
+ * The timedaemons synchronize the clocks of hosts in a local area network.
+ * One daemon runs as master, all the others as slaves. The master
+ * performs the task of computing clock differences and sends correction
+ * values to the slaves.
+ * Slaves start an election to choose a new master when the latter disappears
+ * because of a machine crash, network partition, or when killed.
+ * A resolution protocol is used to kill all but one of the masters
+ * that happen to exist in segments of a partitioned network when the
+ * network partition is fixed.
+ *
+ * Authors: Riccardo Gusella & Stefano Zatti
+ *
+ * overhauled at Silicon Graphics
+ */
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int on;
+ int ret;
+ int nflag, iflag;
+ struct timeval ntime;
+ struct servent *srvp;
+ char buf[BUFSIZ], *cp, *cplim;
+ struct ifconf ifc;
+ struct ifreq ifreq, ifreqf, *ifr;
+ register struct netinfo *ntp;
+ struct netinfo *ntip;
+ struct netinfo *savefromnet;
+ struct netent *nentp;
+ struct nets *nt;
+ struct sockaddr_in server;
+ u_short port;
+ char c;
+#ifdef sgi
+ FILE *timetrim_st;
+#endif
+
+#ifdef sgi
+ struct tms tms;
+#endif /* sgi */
+
+#ifdef lint
+ ntip = NULL;
+#endif
+
+ on = 1;
+ nflag = OFF;
+ iflag = OFF;
+
+#ifdef sgi
+ if (0 > syssgi(SGI_GETTIMETRIM, &timetrim)) {
+ warn("syssgi(GETTIMETRIM)");
+ timetrim = 0;
+ }
+ tot_ticks = hr_ticks = times(&tms);
+#endif /* sgi */
+
+ opterr = 0;
+ while ((c = getopt(argc, argv, "Mtdn:i:F:G:P:")) != -1) {
+ switch (c) {
+ case 'M':
+ Mflag = 1;
+ break;
+
+ case 't':
+ trace = 1;
+ break;
+
+ case 'n':
+ if (iflag) {
+ errx(1, "-i and -n make no sense together");
+ } else {
+ nflag = ON;
+ addnetname(optarg);
+ }
+ break;
+
+ case 'i':
+ if (nflag) {
+ errx(1, "-i and -n make no sense together");
+ } else {
+ iflag = ON;
+ addnetname(optarg);
+ }
+ break;
+
+ case 'F':
+ add_good_host(optarg,1);
+ while (optind < argc && argv[optind][0] != '-')
+ add_good_host(argv[optind++], 1);
+ break;
+
+ case 'd':
+ debug = 1;
+ break;
+ case 'G':
+ if (goodgroup != 0)
+ errx(1, "only one net group");
+ goodgroup = optarg;
+ break;
+#ifdef sgi
+ case 'P':
+ timetrim_fn = optarg;
+ timetrim_st = fopen(timetrim_fn, "r+");
+ if (0 == timetrim_st) {
+ if (errno != ENOENT) {
+ warn("%s", timetrim_fn);
+ timetrim_fn = 0;
+ }
+ } else {
+ int i;
+ long trim;
+ double adj, ticks;
+
+ i = fscanf(timetrim_st, timetrim_rpat,
+ &trim, &adj, &ticks);
+ if (i < 1
+ || trim > MAX_TRIM
+ || trim < -MAX_TRIM
+ || i == 2
+ || (i == 3
+ && trim != rint(adj*CLK_TCK/ticks))) {
+ if (trace && i != EOF)
+ warn(
+ "unrecognized contents in %s",
+ timetrim_fn);
+ } else {
+ if (0 > syssgi(SGI_SETTIMETRIM,
+ trim)) {
+ warn("syssgi(SETTIMETRIM)");
+ } else {
+ timetrim = trim;
+ }
+ if (i == 3) {
+ tot_adj = adj;
+ tot_ticks -= ticks;
+ }
+ }
+ (void)fclose(timetrim_st);
+ }
+ break;
+#endif /* sgi */
+
+ default:
+ usage();
+ break;
+ }
+ }
+ if (optind < argc)
+ usage();
+
+ /* If we care about which machine is the master, then we must
+ * be willing to be a master
+ */
+ if (0 != goodgroup || 0 != goodhosts)
+ Mflag = 1;
+
+ if (gethostname(hostname, sizeof(hostname) - 1) < 0)
+ err(1, "gethostname");
+ self.l_bak = &self;
+ self.l_fwd = &self;
+ self.h_bak = &self;
+ self.h_fwd = &self;
+ self.head = 1;
+ self.good = 1;
+
+ if (goodhosts != 0) /* trust ourself */
+ add_good_host(hostname,1);
+
+ srvp = getservbyname("timed", "udp");
+ if (srvp == 0)
+ errx(1, "unknown service 'timed/udp'");
+ port = srvp->s_port;
+ bzero(&server, sizeof(struct sockaddr_in));
+ server.sin_port = srvp->s_port;
+ server.sin_family = AF_INET;
+ sock = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sock < 0)
+ err(1, "socket");
+ if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (char *)&on,
+ sizeof(on)) < 0)
+ err(1, "setsockopt");
+ if (bind(sock, (struct sockaddr*)&server, sizeof(server))) {
+ if (errno == EADDRINUSE)
+ warnx("time daemon already running");
+ else
+ warn("bind");
+ exit(1);
+ }
+#ifdef sgi
+ /*
+ * handle many slaves with our buffer
+ */
+ if (0 > setsockopt(sock, SOL_SOCKET, SO_RCVBUF, (char*)&bufspace,
+ sizeof(bufspace)))
+ err(1, "setsockopt");
+#endif /* sgi */
+
+ /* choose a unique seed for random number generation */
+ (void)gettimeofday(&ntime, 0);
+ srandom(ntime.tv_sec + ntime.tv_usec);
+
+ sequence = random(); /* initial seq number */
+
+#ifndef sgi
+ /* rounds kernel variable time to multiple of 5 ms. */
+ ntime.tv_sec = 0;
+ ntime.tv_usec = -((ntime.tv_usec/1000) % 5) * 1000;
+ (void)adjtime(&ntime, (struct timeval *)0);
+#endif /* sgi */
+
+ for (nt = nets; nt; nt = nt->next) {
+ nentp = getnetbyname(nt->name);
+ if (nentp == 0) {
+ nt->net = inet_network(nt->name);
+ if (nt->net != INADDR_NONE)
+ nentp = getnetbyaddr(nt->net, AF_INET);
+ }
+ if (nentp != 0) {
+ nt->net = nentp->n_net;
+ } else if (nt->net == INADDR_NONE) {
+ errx(1, "unknown net %s", nt->name);
+ } else if (nt->net == INADDR_ANY) {
+ errx(1, "bad net %s", nt->name);
+ } else {
+ warnx("warning: %s unknown in /etc/networks",
+ nt->name);
+ }
+
+ if (0 == (nt->net & 0xff000000))
+ nt->net <<= 8;
+ if (0 == (nt->net & 0xff000000))
+ nt->net <<= 8;
+ if (0 == (nt->net & 0xff000000))
+ nt->net <<= 8;
+ }
+ ifc.ifc_len = sizeof(buf);
+ ifc.ifc_buf = buf;
+ if (ioctl(sock, SIOCGIFCONF, (char *)&ifc) < 0)
+ err(1, "get interface configuration");
+ ntp = NULL;
+#ifdef sgi
+#define size(p) (sizeof(*ifr) - sizeof(ifr->ifr_name)) /* XXX hack. kludge */
+#else
+#define size(p) max((p).sa_len, sizeof(p))
+#endif
+ cplim = buf + ifc.ifc_len; /*skip over if's with big ifr_addr's */
+ for (cp = buf; cp < cplim;
+ cp += sizeof (ifr->ifr_name) + size(ifr->ifr_addr)) {
+ ifr = (struct ifreq *)cp;
+ if (ifr->ifr_addr.sa_family != AF_INET)
+ continue;
+ if (!ntp)
+ ntp = (struct netinfo*)malloc(sizeof(struct netinfo));
+ bzero(ntp,sizeof(*ntp));
+ ntp->my_addr=((struct sockaddr_in *)&ifr->ifr_addr)->sin_addr;
+ ntp->status = NOMASTER;
+ ifreq = *ifr;
+ ifreqf = *ifr;
+
+ if (ioctl(sock, SIOCGIFFLAGS, (char *)&ifreqf) < 0) {
+ warn("get interface flags");
+ continue;
+ }
+ if ((ifreqf.ifr_flags & IFF_UP) == 0)
+ continue;
+ if ((ifreqf.ifr_flags & IFF_BROADCAST) == 0 &&
+ (ifreqf.ifr_flags & IFF_POINTOPOINT) == 0) {
+ continue;
+ }
+
+
+ if (ioctl(sock, SIOCGIFNETMASK, (char *)&ifreq) < 0) {
+ warn("get netmask");
+ continue;
+ }
+ ntp->mask = ((struct sockaddr_in *)
+ &ifreq.ifr_addr)->sin_addr.s_addr;
+
+ if (ifreqf.ifr_flags & IFF_BROADCAST) {
+ if (ioctl(sock, SIOCGIFBRDADDR, (char *)&ifreq) < 0) {
+ warn("get broadaddr");
+ continue;
+ }
+ ntp->dest_addr = *(struct sockaddr_in *)&ifreq.ifr_broadaddr;
+ /* What if the broadcast address is all ones?
+ * So we cannot just mask ntp->dest_addr. */
+ ntp->net = ntp->my_addr;
+ ntp->net.s_addr &= ntp->mask;
+ } else {
+ if (ioctl(sock, SIOCGIFDSTADDR,
+ (char *)&ifreq) < 0) {
+ warn("get destaddr");
+ continue;
+ }
+ ntp->dest_addr = *(struct sockaddr_in *)&ifreq.ifr_dstaddr;
+ ntp->net = ntp->dest_addr.sin_addr;
+ }
+
+ ntp->dest_addr.sin_port = port;
+
+ for (nt = nets; nt; nt = nt->next) {
+ if (ntp->net.s_addr == htonl(nt->net))
+ break;
+ }
+ if ((nflag && !nt) || (iflag && nt))
+ continue;
+
+ ntp->next = NULL;
+ if (nettab == NULL) {
+ nettab = ntp;
+ } else {
+ ntip->next = ntp;
+ }
+ ntip = ntp;
+ ntp = NULL;
+ }
+ if (ntp)
+ (void) free((char *)ntp);
+ if (nettab == NULL)
+ errx(1, "no network usable");
+
+
+#ifdef sgi
+ (void)schedctl(RENICE,0,10); /* run fast to get good time */
+
+ /* ticks to delay before responding to a broadcast */
+ delay1 = casual(0, CLK_TCK/10);
+#else
+
+ /* microseconds to delay before responding to a broadcast */
+ delay1 = casual(1, 100*1000);
+#endif /* sgi */
+
+ /* election timer delay in secs. */
+ delay2 = casual(MINTOUT, MAXTOUT);
+
+
+#ifdef sgi
+ (void)_daemonize(debug ? _DF_NOFORK|_DF_NOCHDIR : 0, sock, -1, -1);
+#else
+ if (!debug)
+ daemon(debug, 0);
+#endif /* sgi */
+
+ if (trace)
+ traceon();
+ openlog("timed", LOG_CONS|LOG_PID, LOG_DAEMON);
+
+ /*
+ * keep returning here
+ */
+ ret = setjmp(jmpenv);
+ savefromnet = fromnet;
+ setstatus();
+
+ if (Mflag) {
+ switch (ret) {
+
+ case 0:
+ checkignorednets();
+ pickslavenet(0);
+ break;
+ case 1:
+ /* Just lost our master */
+ if (slavenet != 0)
+ slavenet->status = election(slavenet);
+ if (!slavenet || slavenet->status == MASTER) {
+ checkignorednets();
+ pickslavenet(0);
+ } else {
+ makeslave(slavenet); /* prune extras */
+ }
+ break;
+
+ case 2:
+ /* Just been told to quit */
+ justquit = 1;
+ pickslavenet(savefromnet);
+ break;
+ }
+
+ setstatus();
+ if (!(status & MASTER) && sock_raw != -1) {
+ /* sock_raw is not being used now */
+ (void)close(sock_raw);
+ sock_raw = -1;
+ }
+
+ if (status == MASTER)
+ master();
+ else
+ slave();
+
+ } else {
+ if (sock_raw != -1) {
+ (void)close(sock_raw);
+ sock_raw = -1;
+ }
+
+ if (ret) {
+ /* we just lost our master or were told to quit */
+ justquit = 1;
+ }
+ for (ntp = nettab; ntp != NULL; ntp = ntp->next) {
+ if (ntp->status == MASTER)
+ rmnetmachs(ntp);
+ ntp->status = NOMASTER;
+ }
+ checkignorednets();
+ pickslavenet(0);
+ setstatus();
+
+ slave();
+ }
+ /* NOTREACHED */
+ return(0);
+}
+
+static void
+usage()
+{
+#ifdef sgi
+ fprintf(stderr, "%s\n%s\n",
+"usage: timed [-dtM] [-i net|-n net] [-F host1 host2 ...]",
+" [-G netgp] [-P trimfile]");
+#else
+#ifdef HAVENIS
+ fprintf(stderr,
+"usage: timed [-dtM] [-i net|-n net] [-F host1 host2 ...] [-G netgp]\n");
+#else
+ fprintf(stderr,
+"usage: timed [-dtM] [-i net|-n net] [-F host1 host2 ...]\n");
+#endif /* HAVENIS */
+#endif /* sgi */
+ exit(1);
+}
+
+/*
+ * suppress an upstart, untrustworthy, self-appointed master
+ */
+void
+suppress(addr, name,net)
+ struct sockaddr_in *addr;
+ char *name;
+ struct netinfo *net;
+{
+ struct sockaddr_in tgt;
+ char tname[MAXHOSTNAMELEN];
+ struct tsp msg;
+ static struct timeval wait;
+
+ if (trace)
+ fprintf(fd, "suppress: %s\n", name);
+ tgt = *addr;
+ (void)strcpy(tname, name);
+
+ while (0 != readmsg(TSP_ANY, ANYADDR, &wait, net)) {
+ if (trace)
+ fprintf(fd, "suppress:\tdiscarded packet from %s\n",
+ name);
+ }
+
+ syslog(LOG_NOTICE, "suppressing false master %s", tname);
+ msg.tsp_type = TSP_QUIT;
+ (void)strcpy(msg.tsp_name, hostname);
+ (void)acksend(&msg, &tgt, tname, TSP_ACK, 0, 1);
+}
+
+void
+lookformaster(ntp)
+ struct netinfo *ntp;
+{
+ struct tsp resp, conflict, *answer;
+ struct timeval ntime;
+ char mastername[MAXHOSTNAMELEN];
+ struct sockaddr_in masteraddr;
+
+ get_goodgroup(0);
+ ntp->status = SLAVE;
+
+ /* look for master */
+ resp.tsp_type = TSP_MASTERREQ;
+ (void)strcpy(resp.tsp_name, hostname);
+ answer = acksend(&resp, &ntp->dest_addr, ANYADDR,
+ TSP_MASTERACK, ntp, 0);
+ if (answer != 0 && !good_host_name(answer->tsp_name)) {
+ suppress(&from, answer->tsp_name, ntp);
+ ntp->status = NOMASTER;
+ answer = 0;
+ }
+ if (answer == 0) {
+ /*
+ * Various conditions can cause conflict: races between
+ * two just started timedaemons when no master is
+ * present, or timedaemons started during an election.
+ * A conservative approach is taken. Give up and became a
+ * slave, postponing election of a master until first
+ * timer expires.
+ */
+ ntime.tv_sec = ntime.tv_usec = 0;
+ answer = readmsg(TSP_MASTERREQ, ANYADDR, &ntime, ntp);
+ if (answer != 0) {
+ if (!good_host_name(answer->tsp_name)) {
+ suppress(&from, answer->tsp_name, ntp);
+ ntp->status = NOMASTER;
+ }
+ return;
+ }
+
+ ntime.tv_sec = ntime.tv_usec = 0;
+ answer = readmsg(TSP_MASTERUP, ANYADDR, &ntime, ntp);
+ if (answer != 0) {
+ if (!good_host_name(answer->tsp_name)) {
+ suppress(&from, answer->tsp_name, ntp);
+ ntp->status = NOMASTER;
+ }
+ return;
+ }
+
+ ntime.tv_sec = ntime.tv_usec = 0;
+ answer = readmsg(TSP_ELECTION, ANYADDR, &ntime, ntp);
+ if (answer != 0) {
+ if (!good_host_name(answer->tsp_name)) {
+ suppress(&from, answer->tsp_name, ntp);
+ ntp->status = NOMASTER;
+ }
+ return;
+ }
+
+ if (Mflag)
+ ntp->status = MASTER;
+ else
+ ntp->status = NOMASTER;
+ return;
+ }
+
+ ntp->status = SLAVE;
+ (void)strcpy(mastername, answer->tsp_name);
+ masteraddr = from;
+
+ /*
+ * If network has been partitioned, there might be other
+ * masters; tell the one we have just acknowledged that
+ * it has to gain control over the others.
+ */
+ ntime.tv_sec = 0;
+ ntime.tv_usec = 300000;
+ answer = readmsg(TSP_MASTERACK, ANYADDR, &ntime, ntp);
+ /*
+ * checking also not to send CONFLICT to ack'ed master
+ * due to duplicated MASTERACKs
+ */
+ if (answer != NULL &&
+ strcmp(answer->tsp_name, mastername) != 0) {
+ conflict.tsp_type = TSP_CONFLICT;
+ (void)strcpy(conflict.tsp_name, hostname);
+ if (!acksend(&conflict, &masteraddr, mastername,
+ TSP_ACK, 0, 0)) {
+ syslog(LOG_ERR,
+ "error on sending TSP_CONFLICT");
+ }
+ }
+}
+
+/*
+ * based on the current network configuration, set the status, and count
+ * networks;
+ */
+void
+setstatus()
+{
+ struct netinfo *ntp;
+
+ status = 0;
+ nmasternets = nslavenets = nnets = nignorednets = 0;
+ if (trace)
+ fprintf(fd, "Net status:\n");
+ for (ntp = nettab; ntp != NULL; ntp = ntp->next) {
+ switch ((int)ntp->status) {
+ case MASTER:
+ nmasternets++;
+ break;
+ case SLAVE:
+ nslavenets++;
+ break;
+ case NOMASTER:
+ case IGNORE:
+ nignorednets++;
+ break;
+ }
+ if (trace) {
+ fprintf(fd, "\t%-16s", inet_ntoa(ntp->net));
+ switch ((int)ntp->status) {
+ case NOMASTER:
+ fprintf(fd, "NOMASTER\n");
+ break;
+ case MASTER:
+ fprintf(fd, "MASTER\n");
+ break;
+ case SLAVE:
+ fprintf(fd, "SLAVE\n");
+ break;
+ case IGNORE:
+ fprintf(fd, "IGNORE\n");
+ break;
+ default:
+ fprintf(fd, "invalid state %d\n",
+ (int)ntp->status);
+ break;
+ }
+ }
+ nnets++;
+ status |= ntp->status;
+ }
+ status &= ~IGNORE;
+ if (trace)
+ fprintf(fd,
+ "\tnets=%d masters=%d slaves=%d ignored=%d delay2=%d\n",
+ nnets, nmasternets, nslavenets, nignorednets, delay2);
+}
+
+void
+makeslave(net)
+ struct netinfo *net;
+{
+ register struct netinfo *ntp;
+
+ for (ntp = nettab; ntp != NULL; ntp = ntp->next) {
+ if (ntp->status == SLAVE && ntp != net)
+ ntp->status = IGNORE;
+ }
+ slavenet = net;
+}
+
+/*
+ * Try to become master over ignored nets..
+ */
+static void
+checkignorednets()
+{
+ register struct netinfo *ntp;
+
+ for (ntp = nettab; ntp != NULL; ntp = ntp->next) {
+ if (!Mflag && ntp->status == SLAVE)
+ break;
+
+ if (ntp->status == IGNORE || ntp->status == NOMASTER) {
+ lookformaster(ntp);
+ if (!Mflag && ntp->status == SLAVE)
+ break;
+ }
+ }
+}
+
+/*
+ * choose a good network on which to be a slave
+ * The ignored networks must have already been checked.
+ * Take a hint about for a good network.
+ */
+static void
+pickslavenet(ntp)
+ struct netinfo *ntp;
+{
+ if (slavenet != 0 && slavenet->status == SLAVE) {
+ makeslave(slavenet); /* prune extras */
+ return;
+ }
+
+ if (ntp == 0 || ntp->status != SLAVE) {
+ for (ntp = nettab; ntp != 0; ntp = ntp->next) {
+ if (ntp->status == SLAVE)
+ break;
+ }
+ }
+ makeslave(ntp);
+}
+
+/*
+ * returns a random number in the range [inf, sup]
+ */
+long
+casual(inf, sup)
+ long inf, sup;
+{
+ double value;
+
+ value = ((double)(random() & 0x7fffffff)) / (0x7fffffff*1.0);
+ return(inf + (sup - inf)*value);
+}
+
+char *
+date()
+{
+#ifdef sgi
+ struct timeval tv;
+ static char tm[32];
+
+ (void)gettimeofday(&tv, (struct timezone *)0);
+ (void)cftime(tm, "%D %T", &tv.tv_sec);
+ return (tm);
+#else
+ struct timeval tv;
+
+ (void)gettimeofday(&tv, (struct timezone *)0);
+ return (ctime(&tv.tv_sec));
+#endif /* sgi */
+}
+
+void
+addnetname(name)
+ char *name;
+{
+ register struct nets **netlist = &nets;
+
+ while (*netlist)
+ netlist = &((*netlist)->next);
+ *netlist = (struct nets *)malloc(sizeof **netlist);
+ if (*netlist == 0)
+ errx(1, "malloc failed");
+ bzero((char *)*netlist, sizeof(**netlist));
+ (*netlist)->name = name;
+}
+
+/* note a host as trustworthy */
+static void
+add_good_host(name, perm)
+ char *name;
+ int perm; /* 1=not part of the netgroup */
+{
+ register struct goodhost *ghp;
+ register struct hostent *hentp;
+
+ ghp = (struct goodhost*)malloc(sizeof(*ghp));
+ if (!ghp) {
+ syslog(LOG_ERR, "malloc failed");
+ exit(1);
+ }
+
+ bzero((char*)ghp, sizeof(*ghp));
+ (void)strncpy(&ghp->name[0], name, sizeof(ghp->name));
+ ghp->next = goodhosts;
+ ghp->perm = perm;
+ goodhosts = ghp;
+
+ hentp = gethostbyname(name);
+ if (0 == hentp && perm)
+ warnx("unknown host %s", name);
+}
+
+
+/* update our image of the net-group of trustworthy hosts
+ */
+void
+get_goodgroup(force)
+ int force;
+{
+# define NG_DELAY (30*60*CLK_TCK) /* 30 minutes */
+ static unsigned long last_update = -NG_DELAY;
+ unsigned long new_update;
+ struct goodhost *ghp, **ghpp;
+#ifdef HAVENIS
+ struct hosttbl *htp;
+ char *mach, *usr, *dom;
+#endif /* HAVENIS */
+ struct tms tm;
+
+
+ /* if no netgroup, then we are finished */
+ if (goodgroup == 0 || !Mflag)
+ return;
+
+ /* Do not chatter with the netgroup master too often.
+ */
+ new_update = times(&tm);
+ if (new_update < last_update + NG_DELAY
+ && !force)
+ return;
+ last_update = new_update;
+
+ /* forget the old temporary entries */
+ ghpp = &goodhosts;
+ while (0 != (ghp = *ghpp)) {
+ if (!ghp->perm) {
+ *ghpp = ghp->next;
+ free((char*)ghp);
+ } else {
+ ghpp = &ghp->next;
+ }
+ }
+
+#ifdef HAVENIS
+ /* quit now if we are not one of the trusted masters
+ */
+ if (!innetgr(goodgroup, &hostname[0], 0,0)) {
+ if (trace)
+ (void)fprintf(fd, "get_goodgroup: %s not in %s\n",
+ &hostname[0], goodgroup);
+ return;
+ }
+ if (trace)
+ (void)fprintf(fd, "get_goodgroup: %s in %s\n",
+ &hostname[0], goodgroup);
+
+ /* mark the entire netgroup as trusted */
+ (void)setnetgrent(goodgroup);
+ while (getnetgrent(&mach,&usr,&dom)) {
+ if (0 != mach)
+ add_good_host(mach,0);
+ }
+ (void)endnetgrent();
+
+ /* update list of slaves */
+ for (htp = self.l_fwd; htp != &self; htp = htp->l_fwd) {
+ htp->good = good_host_name(&htp->name[0]);
+ }
+#endif /* HAVENIS */
+}
+
+
+/* see if a machine is trustworthy
+ */
+int /* 1=trust hp to change our date */
+good_host_name(name)
+ char *name;
+{
+ register struct goodhost *ghp = goodhosts;
+ register char c;
+
+ if (!ghp || !Mflag) /* trust everyone if no one named */
+ return 1;
+
+ c = *name;
+ do {
+ if (c == ghp->name[0]
+ && !strcasecmp(name, ghp->name))
+ return 1; /* found him, so say so */
+ } while (0 != (ghp = ghp->next));
+
+ if (!strcasecmp(name,hostname)) /* trust ourself */
+ return 1;
+
+ return 0; /* did not find him */
+}
diff --git a/usr.sbin/timed/timedc/Makefile b/usr.sbin/timed/timedc/Makefile
new file mode 100644
index 0000000..582ea96
--- /dev/null
+++ b/usr.sbin/timed/timedc/Makefile
@@ -0,0 +1,11 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= timedc
+SRCS= cmds.c cmdtab.c timedc.c byteorder.c measure.c cksum.c
+MAN8= timedc.8
+BINOWN= root
+BINMODE=4555
+.PATH: ${.CURDIR}/../timed
+
+.include "../../Makefile.inc"
+.include <bsd.prog.mk>
diff --git a/usr.sbin/timed/timedc/cmds.c b/usr.sbin/timed/timedc/cmds.c
new file mode 100644
index 0000000..2c97808
--- /dev/null
+++ b/usr.sbin/timed/timedc/cmds.c
@@ -0,0 +1,526 @@
+/*-
+ * Copyright (c) 1985, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)cmds.c 8.1 (Berkeley) 6/6/93";
+#endif
+static const char rcsid[] =
+ "$Id: cmds.c,v 1.4 1997/10/29 07:35:27 charnier Exp $";
+#endif /* not lint */
+
+#include "timedc.h"
+#include <sys/file.h>
+
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/ip_icmp.h>
+
+#include <err.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <unistd.h>
+
+#define TSPTYPES
+#include <protocols/timed.h>
+
+#ifdef sgi
+#include <bstring.h>
+#include <sys/clock.h>
+#else
+#define SECHR (60*60)
+#define SECDAY (24*SECHR)
+#endif /* sgi */
+
+# define DATE_PROTO "udp"
+# define DATE_PORT "time"
+
+
+int sock;
+int sock_raw;
+char myname[MAXHOSTNAMELEN];
+struct hostent *hp;
+struct sockaddr_in server;
+struct sockaddr_in dayaddr;
+extern int measure_delta;
+
+void bytenetorder(struct tsp *);
+void bytehostorder(struct tsp *);
+
+
+#define BU (2208988800UL) /* seconds before UNIX epoch */
+
+
+/* compute the difference between our date and another machine
+ */
+static int /* difference in days from our time */
+daydiff(hostname)
+ char *hostname;
+{
+ int i;
+ int trials;
+ struct timeval tout, now;
+ fd_set ready;
+ struct sockaddr from;
+ int fromlen;
+ unsigned long sec;
+
+
+ /* wait 2 seconds between 10 tries */
+ tout.tv_sec = 2;
+ tout.tv_usec = 0;
+ for (trials = 0; trials < 10; trials++) {
+ /* ask for the time */
+ sec = 0;
+ if (sendto(sock, &sec, sizeof(sec), 0,
+ (struct sockaddr*)&dayaddr, sizeof(dayaddr)) < 0) {
+ warn("sendto(sock)");
+ return 0;
+ }
+
+ for (;;) {
+ FD_ZERO(&ready);
+ FD_SET(sock, &ready);
+ i = select(sock+1, &ready, (fd_set *)0,
+ (fd_set *)0, &tout);
+ if (i < 0) {
+ if (errno == EINTR)
+ continue;
+ warn("select(date read)");
+ return 0;
+ }
+ if (0 == i)
+ break;
+
+ fromlen = sizeof(from);
+ if (recvfrom(sock,&sec,sizeof(sec),0,
+ &from,&fromlen) < 0) {
+ warn("recvfrom(date read)");
+ return 0;
+ }
+
+ sec = ntohl(sec);
+ if (sec < BU) {
+ warnx("%s says it is before 1970: %lu",
+ hostname, sec);
+ return 0;
+ }
+ sec -= BU;
+
+ (void)gettimeofday(&now, (struct timezone*)0);
+ return (sec - now.tv_sec);
+ }
+ }
+
+ /* if we get here, we tried too many times */
+ warnx("%s will not tell us the date", hostname);
+ return 0;
+}
+
+
+/*
+ * Clockdiff computes the difference between the time of the machine on
+ * which it is called and the time of the machines given as argument.
+ * The time differences measured by clockdiff are obtained using a sequence
+ * of ICMP TSTAMP messages which are returned to the sender by the IP module
+ * in the remote machine.
+ * In order to compare clocks of machines in different time zones, the time
+ * is transmitted (as a 32-bit value) in milliseconds since midnight UT.
+ * If a hosts uses a different time format, it should set the high order
+ * bit of the 32-bit quantity it transmits.
+ * However, VMS apparently transmits the time in milliseconds since midnight
+ * local time (rather than GMT) without setting the high order bit.
+ * Furthermore, it does not understand daylight-saving time. This makes
+ * clockdiff behaving inconsistently with hosts running VMS.
+ *
+ * In order to reduce the sensitivity to the variance of message transmission
+ * time, clockdiff sends a sequence of messages. Yet, measures between
+ * two `distant' hosts can be affected by a small error. The error can,
+ * however, be reduced by increasing the number of messages sent in each
+ * measurement.
+ */
+void
+clockdiff(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int measure_status;
+ extern int measure(u_long, u_long, char *, struct sockaddr_in*, int);
+ register int avg_cnt;
+ register long avg;
+ struct servent *sp;
+
+ if (argc < 2) {
+ printf("usage: timedc clockdiff host ...\n");
+ return;
+ }
+
+ if (gethostname(myname, sizeof(myname) - 1) < 0)
+ err(1, "gethostname");
+
+ /* get the address for the date ready */
+ sp = getservbyname(DATE_PORT, DATE_PROTO);
+ if (!sp) {
+ warnx("%s/%s is an unknown service", DATE_PORT, DATE_PROTO);
+ dayaddr.sin_port = 0;
+ } else {
+ dayaddr.sin_port = sp->s_port;
+ }
+
+ while (argc > 1) {
+ argc--; argv++;
+ hp = gethostbyname(*argv);
+ if (hp == NULL) {
+ warnx("%s: %s", *argv, hstrerror(h_errno));
+ continue;
+ }
+
+ server.sin_family = hp->h_addrtype;
+ bcopy(hp->h_addr, &server.sin_addr.s_addr, hp->h_length);
+ for (avg_cnt = 0, avg = 0; avg_cnt < 16; avg_cnt++) {
+ measure_status = measure(10000,100, *argv, &server, 1);
+ if (measure_status != GOOD)
+ break;
+ avg += measure_delta;
+ }
+ if (measure_status == GOOD)
+ measure_delta = avg/avg_cnt;
+
+ switch (measure_status) {
+ case HOSTDOWN:
+ printf("%s is down\n", hp->h_name);
+ continue;
+ case NONSTDTIME:
+ printf("%s transmitts a non-standard time format\n",
+ hp->h_name);
+ continue;
+ case UNREACHABLE:
+ printf("%s is unreachable\n", hp->h_name);
+ continue;
+ }
+
+ /*
+ * Try to get the date only after using ICMP timestamps to
+ * get the time. This is because the date protocol
+ * is optional.
+ */
+ if (dayaddr.sin_port != 0) {
+ dayaddr.sin_family = hp->h_addrtype;
+ bcopy(hp->h_addr, &dayaddr.sin_addr.s_addr,
+ hp->h_length);
+ avg = daydiff(*argv);
+ if (avg > SECDAY) {
+ printf("time on %s is %ld days ahead %s\n",
+ hp->h_name, avg/SECDAY, myname);
+ continue;
+ } else if (avg < -SECDAY) {
+ printf("time on %s is %ld days behind %s\n",
+ hp->h_name, -avg/SECDAY, myname);
+ continue;
+ }
+ }
+
+ if (measure_delta > 0) {
+ printf("time on %s is %d ms. ahead of time on %s\n",
+ hp->h_name, measure_delta, myname);
+ } else if (measure_delta == 0) {
+ printf("%s and %s have the same time\n",
+ hp->h_name, myname);
+ } else {
+ printf("time on %s is %d ms. behind time on %s\n",
+ hp->h_name, -measure_delta, myname);
+ }
+ }
+ return;
+}
+
+
+/*
+ * finds location of master timedaemon
+ */
+void
+msite(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int cc;
+ fd_set ready;
+ struct sockaddr_in dest;
+ int i, length;
+ struct sockaddr from;
+ struct timeval tout;
+ struct tsp msg;
+ struct servent *srvp;
+ char *tgtname;
+
+ if (argc < 1) {
+ printf("usage: timedc msite [host ...]\n");
+ return;
+ }
+
+ srvp = getservbyname("timed", "udp");
+ if (srvp == 0) {
+ warnx("udp/timed: unknown service");
+ return;
+ }
+ dest.sin_port = srvp->s_port;
+ dest.sin_family = AF_INET;
+
+ if (gethostname(myname, sizeof(myname) - 1) < 0)
+ err(1, "gethostname");
+ i = 1;
+ do {
+ tgtname = (i >= argc) ? myname : argv[i];
+ hp = gethostbyname(tgtname);
+ if (hp == 0) {
+ warnx("%s: %s", tgtname, hstrerror(h_errno));
+ continue;
+ }
+ bcopy(hp->h_addr, &dest.sin_addr.s_addr, hp->h_length);
+
+ (void)strcpy(msg.tsp_name, myname);
+ msg.tsp_type = TSP_MSITE;
+ msg.tsp_vers = TSPVERSION;
+ bytenetorder(&msg);
+ if (sendto(sock, &msg, sizeof(struct tsp), 0,
+ (struct sockaddr*)&dest,
+ sizeof(struct sockaddr)) < 0) {
+ warn("sendto");
+ continue;
+ }
+
+ tout.tv_sec = 15;
+ tout.tv_usec = 0;
+ FD_ZERO(&ready);
+ FD_SET(sock, &ready);
+ if (select(FD_SETSIZE, &ready, (fd_set *)0, (fd_set *)0,
+ &tout)) {
+ length = sizeof(struct sockaddr);
+ cc = recvfrom(sock, &msg, sizeof(struct tsp), 0,
+ &from, &length);
+ if (cc < 0) {
+ warn("recvfrom");
+ continue;
+ }
+ bytehostorder(&msg);
+ if (msg.tsp_type == TSP_ACK) {
+ printf("master timedaemon at %s is %s\n",
+ tgtname, msg.tsp_name);
+ } else {
+ printf("received wrong ack: %s\n",
+ tsptype[msg.tsp_type]);
+ }
+ } else {
+ printf("communication error with %s\n", tgtname);
+ }
+ } while (++i < argc);
+}
+
+/*
+ * quits timedc
+ */
+void
+quit()
+{
+ exit(0);
+}
+
+
+/*
+ * Causes the election timer to expire on the selected hosts
+ * It sends just one udp message per machine, relying on
+ * reliability of communication channel.
+ */
+void
+testing(argc, argv)
+ int argc;
+ char *argv[];
+{
+ struct servent *srvp;
+ struct sockaddr_in sin;
+ struct tsp msg;
+
+ if (argc < 2) {
+ printf("usage: timedc election host1 [host2 ...]\n");
+ return;
+ }
+
+ srvp = getservbyname("timed", "udp");
+ if (srvp == 0) {
+ warnx("udp/timed: unknown service");
+ return;
+ }
+
+ while (argc > 1) {
+ argc--; argv++;
+ hp = gethostbyname(*argv);
+ if (hp == NULL) {
+ warnx("%s: %s", *argv, hstrerror(h_errno));
+ argc--; argv++;
+ continue;
+ }
+ sin.sin_port = srvp->s_port;
+ sin.sin_family = hp->h_addrtype;
+ bcopy(hp->h_addr, &sin.sin_addr.s_addr, hp->h_length);
+
+ msg.tsp_type = TSP_TEST;
+ msg.tsp_vers = TSPVERSION;
+ if (gethostname(myname, sizeof(myname) - 1) < 0)
+ err(1, "gethostname");
+ (void)strcpy(msg.tsp_name, myname);
+ bytenetorder(&msg);
+ if (sendto(sock, &msg, sizeof(struct tsp), 0,
+ (struct sockaddr*)&sin,
+ sizeof(struct sockaddr)) < 0) {
+ warn("sendto");
+ }
+ }
+}
+
+
+/*
+ * Enables or disables tracing on local timedaemon
+ */
+void
+tracing(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int onflag;
+ int length;
+ int cc;
+ fd_set ready;
+ struct sockaddr_in dest;
+ struct sockaddr from;
+ struct timeval tout;
+ struct tsp msg;
+ struct servent *srvp;
+
+ if (argc != 2) {
+ printf("usage: timedc trace { on | off }\n");
+ return;
+ }
+
+ srvp = getservbyname("timed", "udp");
+ if (srvp == 0) {
+ warnx("udp/timed: unknown service");
+ return;
+ }
+ dest.sin_port = srvp->s_port;
+ dest.sin_family = AF_INET;
+
+ if (gethostname(myname, sizeof(myname) - 1) < 0)
+ err(1, "gethostname");
+ hp = gethostbyname(myname);
+ bcopy(hp->h_addr, &dest.sin_addr.s_addr, hp->h_length);
+
+ if (strcmp(argv[1], "on") == 0) {
+ msg.tsp_type = TSP_TRACEON;
+ onflag = ON;
+ } else {
+ msg.tsp_type = TSP_TRACEOFF;
+ onflag = OFF;
+ }
+
+ (void)strcpy(msg.tsp_name, myname);
+ msg.tsp_vers = TSPVERSION;
+ bytenetorder(&msg);
+ if (sendto(sock, &msg, sizeof(struct tsp), 0,
+ (struct sockaddr*)&dest, sizeof(struct sockaddr)) < 0) {
+ warn("sendto");
+ return;
+ }
+
+ tout.tv_sec = 5;
+ tout.tv_usec = 0;
+ FD_ZERO(&ready);
+ FD_SET(sock, &ready);
+ if (select(FD_SETSIZE, &ready, (fd_set *)0, (fd_set *)0, &tout)) {
+ length = sizeof(struct sockaddr);
+ cc = recvfrom(sock, &msg, sizeof(struct tsp), 0,
+ &from, &length);
+ if (cc < 0) {
+ warn("recvfrom");
+ return;
+ }
+ bytehostorder(&msg);
+ if (msg.tsp_type == TSP_ACK)
+ if (onflag)
+ printf("timed tracing enabled\n");
+ else
+ printf("timed tracing disabled\n");
+ else
+ printf("wrong ack received: %s\n",
+ tsptype[msg.tsp_type]);
+ } else
+ printf("communication error\n");
+}
+
+int
+priv_resources()
+{
+ int port;
+ struct sockaddr_in sin;
+
+ sock = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sock < 0) {
+ warn("opening socket");
+ return(-1);
+ }
+
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = 0;
+ for (port = IPPORT_RESERVED - 1; port > IPPORT_RESERVED / 2; port--) {
+ sin.sin_port = htons((u_short)port);
+ if (bind(sock, (struct sockaddr*)&sin, sizeof (sin)) >= 0)
+ break;
+ if (errno != EADDRINUSE && errno != EADDRNOTAVAIL) {
+ warn("bind");
+ (void) close(sock);
+ return(-1);
+ }
+ }
+ if (port == IPPORT_RESERVED / 2) {
+ warnx("all reserved ports in use");
+ (void) close(sock);
+ return(-1);
+ }
+
+ sock_raw = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
+ if (sock_raw < 0) {
+ warn("opening raw socket");
+ (void) close(sock);
+ return(-1);
+ }
+ return(1);
+}
diff --git a/usr.sbin/timed/timedc/cmdtab.c b/usr.sbin/timed/timedc/cmdtab.c
new file mode 100644
index 0000000..85b4bee
--- /dev/null
+++ b/usr.sbin/timed/timedc/cmdtab.c
@@ -0,0 +1,61 @@
+/*
+ * Copyright (c) 1983, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)cmdtab.c 8.1 (Berkeley) 6/6/93";
+#endif
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#include "timedc.h"
+
+char clockdiffhelp[] = "measures clock differences between machines";
+char helphelp[] = "gets help on commands";
+char msitehelp[] = "finds location of master";
+char quithelp[] = "exits timedc";
+char testinghelp[] = "causes election timers to expire";
+char tracinghelp[] = "turns tracing on or off";
+
+struct cmd cmdtab[] = {
+ { "clockdiff", clockdiffhelp, clockdiff, 0 },
+ { "election", testinghelp, testing, 1 },
+ { "help", helphelp, help, 0 },
+ { "msite", msitehelp, msite, 0 },
+ { "quit", quithelp, quit, 0 },
+ { "trace", tracinghelp, tracing, 1 },
+ { "?", helphelp, help, 0 },
+};
+
+int NCMDS = sizeof (cmdtab) / sizeof (cmdtab[0]);
diff --git a/usr.sbin/timed/timedc/extern.h b/usr.sbin/timed/timedc/extern.h
new file mode 100644
index 0000000..7f33362
--- /dev/null
+++ b/usr.sbin/timed/timedc/extern.h
@@ -0,0 +1,52 @@
+/*-
+ * Copyright (c) 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)extern.h 8.1 (Berkeley) 6/6/93
+ */
+
+#if __STDC__
+struct tsp;
+#endif
+
+extern struct cmd cmdtab[];
+
+void bytehostorder __P((struct tsp *));
+void bytenetorder __P((struct tsp *));
+void clockdiff __P((int, char *[]));
+void help __P((int, char *[]));
+void intr __P((int));
+void makeargv __P((void));
+void msite __P((int, char *[]));
+int priv_resources __P((void));
+void quit __P((void));
+void testing __P((int, char *[]));
+void tracing __P((int, char *[]));
diff --git a/usr.sbin/timed/timedc/timedc.8 b/usr.sbin/timed/timedc/timedc.8
new file mode 100644
index 0000000..7647e08
--- /dev/null
+++ b/usr.sbin/timed/timedc/timedc.8
@@ -0,0 +1,145 @@
+.\" Copyright (c) 1980, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)timedc.8 8.1 (Berkeley) 6/6/93
+.\"
+.Dd June 6, 1993
+.Dt TIMEDC 8
+.Os BSD 4.3
+.ad
+.Sh NAME
+.Nm timedc
+.Nd timed control program
+.Sh SYNOPSIS
+.Nm timedc
+.Oo Ar command\ \&
+.Op Ar argument ...
+.Oc
+.Sh DESCRIPTION
+.Nm Timedc
+is used to control the operation of the
+.Xr timed 8
+program.
+It may be used to:
+.Bl -bullet
+.It
+Measure the differences between machines' clocks,
+.It
+Find the location where the master time server is running,
+.It
+Enable or disable tracing of messages received by
+.Xr timed 8 ,
+and
+.It
+Perform various debugging actions.
+.El
+.Pp
+Without any arguments,
+.Nm
+will prompt for commands from the standard input.
+If arguments are supplied,
+.Nm
+interprets the first argument as a command and the remaining
+arguments as parameters to the command. The standard input
+may be redirected causing
+.Nm
+to read commands from a file.
+Commands may be abbreviated;
+recognized commands are:
+.Pp
+.Bl -tag -width Ds -compact
+.It Ic \&? Op Ar command ...
+.Pp
+.It Ic help Op Ar command ...
+Print a short description of each command specified in the argument list,
+or, if no arguments are given, a list of the recognized commands.
+.Pp
+.It Ic clockdiff Ar host ...
+Compute the differences between the clock of the host machine
+and the clocks of the machines given as arguments.
+.Pp
+.It Ic msite Op Ar host ...
+Show the master time server for specified host(s).
+.Pp
+.It Xo
+.Ic trace
+.Li \&{ Ar on Li \&|
+.Ar off \&}
+.Xc
+Enable or disable the tracing of incoming messages to
+.Xr timed
+in the file
+.Pa /var/log/timed.log .
+.Pp
+.It Ic election Ar host1 Op Ar host2 ...
+Asks the daemon
+on the target host to reset its "election" timers and to ensure that
+a time master has been elected.
+.Pp
+.It Ic quit
+Exit from timedc.
+.El
+.Pp
+Other commands may be included for use in testing and debugging
+.Xr timed 8 ;
+the help command and
+the program source may be consulted for details.
+.Sh FILES
+.Bl -tag -width /var/log/timed.masterlog -compact
+.It Pa /var/log/timed.log
+tracing file for timed
+.It Pa /var/log/timed.masterlog
+log file for master timed
+.El
+.Sh SEE ALSO
+.Xr date 1 ,
+.Xr adjtime 2 ,
+.Xr icmp 4 ,
+.Xr timed 8
+.Rs
+.%T "TSP: The Time Synchronization Protocol for UNIX 4.3BSD"
+.%A R. Gusella
+.%A S. Zatti
+.Re
+.Sh DIAGNOSTICS
+.Bl -tag -width Ds -compact
+.It ?Ambiguous command
+abbreviation matches more than one command
+.It ?Invalid command
+no match found
+.It ?Privileged command
+command can be executed by root only
+.El
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 4.3 .
diff --git a/usr.sbin/timed/timedc/timedc.c b/usr.sbin/timed/timedc/timedc.c
new file mode 100644
index 0000000..4187f61
--- /dev/null
+++ b/usr.sbin/timed/timedc/timedc.c
@@ -0,0 +1,260 @@
+/*-
+ * Copyright (c) 1985, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 1985, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)timedc.c 8.1 (Berkeley) 6/6/93";
+#endif
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#include "timedc.h"
+#include <ctype.h>
+#include <err.h>
+#include <setjmp.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <strings.h>
+#include <syslog.h>
+#include <unistd.h>
+
+int trace = 0;
+FILE *fd = 0;
+int margc;
+int fromatty;
+char *margv[20];
+char cmdline[200];
+jmp_buf toplevel;
+static struct cmd *getcmd __P((char *));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ register struct cmd *c;
+
+ openlog("timedc", LOG_ODELAY, LOG_AUTH);
+
+ /*
+ * security dictates!
+ */
+ if (priv_resources() < 0)
+ errx(1, "could not get privileged resources");
+ (void) setuid(getuid());
+
+ if (--argc > 0) {
+ c = getcmd(*++argv);
+ if (c == (struct cmd *)-1) {
+ printf("?Ambiguous command\n");
+ exit(1);
+ }
+ if (c == 0) {
+ printf("?Invalid command\n");
+ exit(1);
+ }
+ if (c->c_priv && getuid()) {
+ printf("?Privileged command\n");
+ exit(1);
+ }
+ (*c->c_handler)(argc, argv);
+ exit(0);
+ }
+
+ fromatty = isatty(fileno(stdin));
+ if (setjmp(toplevel))
+ putchar('\n');
+ (void) signal(SIGINT, intr);
+ for (;;) {
+ if (fromatty) {
+ printf("timedc> ");
+ (void) fflush(stdout);
+ }
+ if (fgets(cmdline, sizeof(cmdline), stdin) == 0)
+ quit();
+ if (cmdline[0] == 0)
+ break;
+ makeargv();
+ if (margv[0] == 0)
+ continue;
+ c = getcmd(margv[0]);
+ if (c == (struct cmd *)-1) {
+ printf("?Ambiguous command\n");
+ continue;
+ }
+ if (c == 0) {
+ printf("?Invalid command\n");
+ continue;
+ }
+ if (c->c_priv && getuid()) {
+ printf("?Privileged command\n");
+ continue;
+ }
+ (*c->c_handler)(margc, margv);
+ }
+ return 0;
+}
+
+void
+intr(signo)
+ int signo;
+{
+ if (!fromatty)
+ exit(0);
+ longjmp(toplevel, 1);
+}
+
+
+static struct cmd *
+getcmd(name)
+ char *name;
+{
+ register char *p, *q;
+ register struct cmd *c, *found;
+ register int nmatches, longest;
+ extern int NCMDS;
+
+ longest = 0;
+ nmatches = 0;
+ found = 0;
+ for (c = cmdtab; c < &cmdtab[NCMDS]; c++) {
+ p = c->c_name;
+ for (q = name; *q == *p++; q++)
+ if (*q == 0) /* exact match? */
+ return(c);
+ if (!*q) { /* the name was a prefix */
+ if (q - name > longest) {
+ longest = q - name;
+ nmatches = 1;
+ found = c;
+ } else if (q - name == longest)
+ nmatches++;
+ }
+ }
+ if (nmatches > 1)
+ return((struct cmd *)-1);
+ return(found);
+}
+
+/*
+ * Slice a string up into argc/argv.
+ */
+void
+makeargv()
+{
+ register char *cp;
+ register char **argp = margv;
+
+ margc = 0;
+ for (cp = cmdline; *cp;) {
+ while (isspace(*cp))
+ cp++;
+ if (*cp == '\0')
+ break;
+ *argp++ = cp;
+ margc += 1;
+ while (*cp != '\0' && !isspace(*cp))
+ cp++;
+ if (*cp == '\0')
+ break;
+ *cp++ = '\0';
+ }
+ *argp++ = 0;
+}
+
+#define HELPINDENT (sizeof ("directory"))
+
+/*
+ * Help command.
+ */
+void
+help(argc, argv)
+ int argc;
+ char *argv[];
+{
+ register struct cmd *c;
+
+ if (argc == 1) {
+ register int i, j, w;
+ int columns, width = 0, lines;
+ extern int NCMDS;
+
+ printf("Commands may be abbreviated. Commands are:\n\n");
+ for (c = cmdtab; c < &cmdtab[NCMDS]; c++) {
+ int len = strlen(c->c_name);
+
+ if (len > width)
+ width = len;
+ }
+ width = (width + 8) &~ 7;
+ columns = 80 / width;
+ if (columns == 0)
+ columns = 1;
+ lines = (NCMDS + columns - 1) / columns;
+ for (i = 0; i < lines; i++) {
+ for (j = 0; j < columns; j++) {
+ c = cmdtab + j * lines + i;
+ printf("%s", c->c_name);
+ if (c + lines >= &cmdtab[NCMDS]) {
+ printf("\n");
+ break;
+ }
+ w = strlen(c->c_name);
+ while (w < width) {
+ w = (w + 8) &~ 7;
+ putchar('\t');
+ }
+ }
+ }
+ return;
+ }
+ while (--argc > 0) {
+ register char *arg;
+ arg = *++argv;
+ c = getcmd(arg);
+ if (c == (struct cmd *)-1)
+ printf("?Ambiguous help command %s\n", arg);
+ else if (c == (struct cmd *)0)
+ printf("?Invalid help command %s\n", arg);
+ else
+ printf("%-*s\t%s\n", (int)HELPINDENT,
+ c->c_name, c->c_help);
+ }
+}
diff --git a/usr.sbin/timed/timedc/timedc.h b/usr.sbin/timed/timedc/timedc.h
new file mode 100644
index 0000000..dfdc2f5
--- /dev/null
+++ b/usr.sbin/timed/timedc/timedc.h
@@ -0,0 +1,64 @@
+/*-
+ * Copyright (c) 1985, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)timedc.h 8.1 (Berkeley) 6/6/93
+ */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#ifdef sgi
+#include <sys/uio.h>
+#endif
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include <errno.h>
+#include <netdb.h>
+#include <stdio.h>
+
+#define ON 1
+#define OFF 0
+
+#define GOOD 1
+#define UNREACHABLE 2
+#define NONSTDTIME 3
+#define HOSTDOWN 0x7fffffff
+
+struct cmd {
+ char *c_name; /* command name */
+ char *c_help; /* help message */
+ void (*c_handler)(); /* routine to do the work */
+ int c_priv; /* privileged command */
+};
+
+#include "extern.h"
diff --git a/usr.sbin/traceroute/Makefile b/usr.sbin/traceroute/Makefile
new file mode 100644
index 0000000..0176c0d
--- /dev/null
+++ b/usr.sbin/traceroute/Makefile
@@ -0,0 +1,25 @@
+# $Id$
+
+PROG= traceroute
+MAN8= traceroute.8
+BINOWN= root
+BINMODE=4555
+CFLAGS+=-DHAVE_SYS_SELECT_H=1 -DHAVE_SETLINEBUF=1 -DHAVE_RAW_OPTIONS=1 \
+ -DSTDC_HEADERS=1
+# RTT Jitter on the internet these days means printing 3 decimal places on
+# > 1000ms times is plain useless. Uncomment this to enable variable precision
+# reporting, ie: print a variable precision from 0.001ms through 1000ms
+# CFLAGS+=-DSANE_PRECISION
+
+SRCS= version.c traceroute.c
+CLEANFILES+= version.c
+
+TRACEROUTE_DISTDIR?= ${.CURDIR}/../../contrib/traceroute
+CFLAGS+= -I${TRACEROUTE_DISTDIR}/lbl
+.PATH: ${TRACEROUTE_DISTDIR}
+
+version.c: ${TRACEROUTE_DISTDIR}/VERSION
+ @rm -f $@
+ sed -e 's/.*/char version[] = "&";/' ${TRACEROUTE_DISTDIR}/VERSION > $@
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/trpt/Makefile b/usr.sbin/trpt/Makefile
new file mode 100644
index 0000000..19b41da
--- /dev/null
+++ b/usr.sbin/trpt/Makefile
@@ -0,0 +1,8 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= trpt
+MAN8= trpt.8
+BINGRP= kmem
+BINMODE=2555
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/trpt/trpt.8 b/usr.sbin/trpt/trpt.8
new file mode 100644
index 0000000..fbed94f
--- /dev/null
+++ b/usr.sbin/trpt/trpt.8
@@ -0,0 +1,152 @@
+.\" Copyright (c) 1983, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)trpt.8 8.2 (Berkeley) 12/11/93
+.\"
+.Dd December 11, 1993
+.Dt TRPT 8
+.Os BSD 4.2
+.Sh NAME
+.Nm trpt
+.Nd transliterate protocol trace
+.Sh SYNOPSIS
+.Nm trpt
+.Op Fl a
+.Op Fl f
+.Op Fl j
+.Op Fl p Ar hex-address
+.Op Fl s
+.Op Fl t
+.Oo
+.Ar system Op Ar core
+.Oc
+.Sh DESCRIPTION
+.Nm Trpt
+interrogates the buffer of
+.Tn TCP
+trace records created
+when a socket is marked for
+.Dq debugging
+(see
+.Xr setsockopt 2 ) ,
+and prints a readable description of these records.
+When no options are supplied,
+.Nm
+prints all the trace records found in the system
+grouped according to
+.Tn TCP
+connection protocol control
+block
+.Pq Tn PCB .
+The following options may be used to
+alter this behavior.
+.Bl -tag -width indent
+.It Fl a
+In addition to the normal output,
+print the values of the source and destination
+addresses for each packet recorded.
+.It Fl f
+Follow the trace as it occurs, waiting a short time for additional records
+each time the end of the log is reached.
+.It Fl j
+Just give a list of the protocol control block
+addresses for which there are trace records.
+.It Fl p
+Show only trace records associated with the protocol
+control block at the given address
+.Ar hex-address .
+.It Fl s
+In addition to the normal output,
+print a detailed description of the packet
+sequencing information.
+.It Fl t
+In addition to the normal output,
+print the values for all timers at each
+point in the trace.
+.El
+.Pp
+The recommended use of
+.Nm
+is as follows.
+Isolate the problem and enable debugging on the
+socket(s) involved in the connection.
+Find the address of the protocol control blocks
+associated with the sockets using the
+.Fl A
+option to
+.Xr netstat 1 .
+Then run
+.Nm
+with the
+.Fl p
+option, supplying the associated
+protocol control block addresses.
+The
+.Fl f
+option can be used to follow the trace log once the trace is located.
+If there are
+many sockets using the debugging option, the
+.Fl j
+option may be useful in checking to see if
+any trace records are present for the socket in
+question.
+.Pp
+If debugging is being performed on a system or
+core file other than the default, the last two
+arguments may be used to supplant the defaults.
+.Sh FILES
+.Bl -tag -width /dev/kmem -compact
+.It Pa /kernel
+.It Pa /dev/kmem
+.El
+.Sh SEE ALSO
+.Xr netstat 1 ,
+.Xr setsockopt 2
+.\".Xr setsockopt 2 ,
+.\".Xr trsp 8
+.Sh DIAGNOSTICS
+.Bl -tag -width Ds
+.It Sy no namelist
+When the system image doesn't
+contain the proper symbols to find the trace buffer;
+others which should be self explanatory.
+.El
+.Sh BUGS
+Should also print the data for each input or output,
+but this is not saved in the trace record.
+.Pp
+The output format is inscrutable and should be described
+here.
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 4.2 .
diff --git a/usr.sbin/trpt/trpt.c b/usr.sbin/trpt/trpt.c
new file mode 100644
index 0000000..2d604af
--- /dev/null
+++ b/usr.sbin/trpt/trpt.c
@@ -0,0 +1,380 @@
+/*
+ * Copyright (c) 1983, 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 1983, 1988, 1993\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)trpt.c 8.1 (Berkeley) 6/6/93";
+#endif
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/queue.h>
+#include <sys/socket.h>
+#include <sys/socketvar.h>
+#define PRUREQUESTS
+#include <sys/protosw.h>
+#include <sys/file.h>
+#include <sys/time.h>
+
+#include <net/route.h>
+#include <net/if.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+#include <netinet/ip_var.h>
+#include <netinet/tcp.h>
+#define TCPSTATES
+#include <netinet/tcp_fsm.h>
+#include <netinet/tcp_seq.h>
+#define TCPTIMERS
+#include <netinet/tcp_timer.h>
+#include <netinet/tcp_var.h>
+#include <netinet/tcpip.h>
+#define TANAMES
+#include <netinet/tcp_debug.h>
+
+#include <arpa/inet.h>
+
+#include <err.h>
+#include <nlist.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+struct nlist nl[] = {
+#define N_TCP_DEBUG 0
+ { "_tcp_debug" },
+#define N_TCP_DEBX 1
+ { "_tcp_debx" },
+ { "" },
+};
+
+static caddr_t tcp_pcbs[TCP_NDEBUG];
+static n_time ntime;
+static int aflag, kflag, memf, follow, sflag, tflag;
+
+void dotrace __P((caddr_t));
+void klseek __P((int, off_t, int));
+int numeric __P((caddr_t *, caddr_t *));
+void tcp_trace __P((short, short, struct tcpcb *, struct tcpcb *,
+ struct tcpiphdr *, int));
+static void usage __P((void));
+
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ int ch, i, jflag, npcbs;
+ char *system, *core;
+
+ jflag = npcbs = 0;
+ while ((ch = getopt(argc, argv, "afjp:st")) != -1)
+ switch (ch) {
+ case 'a':
+ ++aflag;
+ break;
+ case 'f':
+ ++follow;
+ setlinebuf(stdout);
+ break;
+ case 'j':
+ ++jflag;
+ break;
+ case 'p':
+ if (npcbs >= TCP_NDEBUG)
+ errx(1, "too many pcb's specified");
+ (void)sscanf(optarg, "%x", (int *)&tcp_pcbs[npcbs++]);
+ break;
+ case 's':
+ ++sflag;
+ break;
+ case 't':
+ ++tflag;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+ argc -= optind;
+ argv += optind;
+
+ core = _PATH_KMEM;
+ if (argc > 0) {
+ system = *argv;
+ argc--, argv++;
+ if (argc > 0) {
+ core = *argv;
+ argc--, argv++;
+ ++kflag;
+ }
+ /*
+ * Discard setgid privileges if not the running kernel so that
+ * bad guys can't print interesting stuff from kernel memory.
+ */
+ setgid(getgid());
+ }
+ else
+ system = (char *)getbootfile();
+
+ if (nlist(system, nl) < 0 || !nl[0].n_value)
+ errx(1, "%s: no namelist", system);
+ if ((memf = open(core, O_RDONLY)) < 0)
+ err(2, "%s", core);
+ if (kflag)
+ errx(1, "can't do core files yet");
+ (void)klseek(memf, (off_t)nl[N_TCP_DEBX].n_value, L_SET);
+ if (read(memf, (char *)&tcp_debx, sizeof(tcp_debx)) !=
+ sizeof(tcp_debx))
+ err(3, "tcp_debx");
+ (void)klseek(memf, (off_t)nl[N_TCP_DEBUG].n_value, L_SET);
+ if (read(memf, (char *)tcp_debug, sizeof(tcp_debug)) !=
+ sizeof(tcp_debug))
+ err(3, "tcp_debug");
+ /*
+ * If no control blocks have been specified, figure
+ * out how many distinct one we have and summarize
+ * them in tcp_pcbs for sorting the trace records
+ * below.
+ */
+ if (!npcbs) {
+ for (i = 0; i < TCP_NDEBUG; i++) {
+ register struct tcp_debug *td = &tcp_debug[i];
+ register int j;
+
+ if (td->td_tcb == 0)
+ continue;
+ for (j = 0; j < npcbs; j++)
+ if (tcp_pcbs[j] == td->td_tcb)
+ break;
+ if (j >= npcbs)
+ tcp_pcbs[npcbs++] = td->td_tcb;
+ }
+ if (!npcbs)
+ exit(0);
+ }
+ qsort(tcp_pcbs, npcbs, sizeof(caddr_t), numeric);
+ if (jflag) {
+ for (i = 0;;) {
+ printf("%x", (int)tcp_pcbs[i]);
+ if (++i == npcbs)
+ break;
+ fputs(", ", stdout);
+ }
+ putchar('\n');
+ }
+ else for (i = 0; i < npcbs; i++) {
+ printf("\n%x:\n", (int)tcp_pcbs[i]);
+ dotrace(tcp_pcbs[i]);
+ }
+ exit(0);
+}
+
+static void
+usage()
+{
+ (void)fprintf(stderr,
+ "usage: trpt [-afjst] [-p hex-address] [system [core]]\n");
+ exit(1);
+}
+
+void
+dotrace(tcpcb)
+ register caddr_t tcpcb;
+{
+ register struct tcp_debug *td;
+ register int i;
+ int prev_debx = tcp_debx;
+
+again: if (--tcp_debx < 0)
+ tcp_debx = TCP_NDEBUG - 1;
+ for (i = prev_debx % TCP_NDEBUG; i < TCP_NDEBUG; i++) {
+ td = &tcp_debug[i];
+ if (tcpcb && td->td_tcb != tcpcb)
+ continue;
+ ntime = ntohl(td->td_time);
+ tcp_trace(td->td_act, td->td_ostate, td->td_tcb, &td->td_cb,
+ &td->td_ti, td->td_req);
+ if (i == tcp_debx)
+ goto done;
+ }
+ for (i = 0; i <= tcp_debx % TCP_NDEBUG; i++) {
+ td = &tcp_debug[i];
+ if (tcpcb && td->td_tcb != tcpcb)
+ continue;
+ ntime = ntohl(td->td_time);
+ tcp_trace(td->td_act, td->td_ostate, td->td_tcb, &td->td_cb,
+ &td->td_ti, td->td_req);
+ }
+done: if (follow) {
+ prev_debx = tcp_debx + 1;
+ if (prev_debx >= TCP_NDEBUG)
+ prev_debx = 0;
+ do {
+ sleep(1);
+ (void)klseek(memf, (off_t)nl[N_TCP_DEBX].n_value, L_SET);
+ if (read(memf, (char *)&tcp_debx, sizeof(tcp_debx)) !=
+ sizeof(tcp_debx))
+ err(3, "tcp_debx");
+ } while (tcp_debx == prev_debx);
+ (void)klseek(memf, (off_t)nl[N_TCP_DEBUG].n_value, L_SET);
+ if (read(memf, (char *)tcp_debug, sizeof(tcp_debug)) !=
+ sizeof(tcp_debug))
+ err(3, "tcp_debug");
+ goto again;
+ }
+}
+
+/*
+ * Tcp debug routines
+ */
+/*ARGSUSED*/
+void
+tcp_trace(act, ostate, atp, tp, ti, req)
+ short act, ostate;
+ struct tcpcb *atp, *tp;
+ struct tcpiphdr *ti;
+ int req;
+{
+ tcp_seq seq, ack;
+ int flags, len, win, timer;
+
+ printf("%03ld %s:%s ",(ntime/10) % 1000, tcpstates[ostate],
+ tanames[act]);
+ switch (act) {
+ case TA_INPUT:
+ case TA_OUTPUT:
+ case TA_DROP:
+ if (aflag) {
+ printf("(src=%s,%u, ",
+ inet_ntoa(ti->ti_src), ntohs(ti->ti_sport));
+ printf("dst=%s,%u)",
+ inet_ntoa(ti->ti_dst), ntohs(ti->ti_dport));
+ }
+ seq = ti->ti_seq;
+ ack = ti->ti_ack;
+ len = ti->ti_len;
+ win = ti->ti_win;
+ if (act == TA_OUTPUT) {
+ seq = ntohl(seq);
+ ack = ntohl(ack);
+ len = ntohs(len);
+ win = ntohs(win);
+ }
+ if (act == TA_OUTPUT)
+ len -= sizeof(struct tcphdr);
+ if (len)
+ printf("[%lx..%lx)", seq, seq + len);
+ else
+ printf("%lx", seq);
+ printf("@%lx", ack);
+ if (win)
+ printf("(win=%x)", win);
+ flags = ti->ti_flags;
+ if (flags) {
+ register char *cp = "<";
+#define pf(flag, string) { \
+ if (ti->ti_flags&flag) { \
+ (void)printf("%s%s", cp, string); \
+ cp = ","; \
+ } \
+}
+ pf(TH_SYN, "SYN");
+ pf(TH_ACK, "ACK");
+ pf(TH_FIN, "FIN");
+ pf(TH_RST, "RST");
+ pf(TH_PUSH, "PUSH");
+ pf(TH_URG, "URG");
+ printf(">");
+ }
+ break;
+ case TA_USER:
+ timer = req >> 8;
+ req &= 0xff;
+ printf("%s", prurequests[req]);
+ if (req == PRU_SLOWTIMO || req == PRU_FASTTIMO)
+ printf("<%s>", tcptimers[timer]);
+ break;
+ }
+ printf(" -> %s", tcpstates[tp->t_state]);
+ /* print out internal state of tp !?! */
+ printf("\n");
+ if (sflag) {
+ printf("\trcv_nxt %lx rcv_wnd %x snd_una %lx snd_nxt %lx snd_max %lx\n",
+ tp->rcv_nxt, tp->rcv_wnd, tp->snd_una, tp->snd_nxt,
+ tp->snd_max);
+ printf("\tsnd_wl1 %lx snd_wl2 %lx snd_wnd %x\n", tp->snd_wl1,
+ tp->snd_wl2, tp->snd_wnd);
+ }
+ /* print out timers? */
+ if (tflag) {
+ register char *cp = "\t";
+ register int i;
+
+ for (i = 0; i < TCPT_NTIMERS; i++) {
+ if (tp->t_timer[i] == 0)
+ continue;
+ printf("%s%s=%d", cp, tcptimers[i], tp->t_timer[i]);
+ if (i == TCPT_REXMT)
+ printf(" (t_rxtshft=%d)", tp->t_rxtshift);
+ cp = ", ";
+ }
+ if (*cp != '\t')
+ putchar('\n');
+ }
+}
+
+int
+numeric(c1, c2)
+ caddr_t *c1, *c2;
+{
+ return(*c1 - *c2);
+}
+
+void
+klseek(fd, base, off)
+ int fd, off;
+ off_t base;
+{
+ (void)lseek(fd, base, off);
+}
diff --git a/usr.sbin/tzsetup/Makefile b/usr.sbin/tzsetup/Makefile
new file mode 100644
index 0000000..5501792
--- /dev/null
+++ b/usr.sbin/tzsetup/Makefile
@@ -0,0 +1,9 @@
+# $Id$
+
+PROG= tzsetup
+MAN8= tzsetup.8
+CFLAGS+= -I${.CURDIR}
+LDADD+= -ldialog -lncurses -lmytinfo
+DPADD+= ${LIBDIALOG} ${LIBNCURSES} ${LIBMYTINFO}
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/tzsetup/paths.h b/usr.sbin/tzsetup/paths.h
new file mode 100644
index 0000000..10a6812
--- /dev/null
+++ b/usr.sbin/tzsetup/paths.h
@@ -0,0 +1,4 @@
+#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"
diff --git a/usr.sbin/tzsetup/tzsetup.8 b/usr.sbin/tzsetup/tzsetup.8
new file mode 100644
index 0000000..8841959
--- /dev/null
+++ b/usr.sbin/tzsetup/tzsetup.8
@@ -0,0 +1,74 @@
+.\" Copyright (c) 1996 Wolfram Schneider <wosch@FreeBSD.org>. Berlin.
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $Id: tzsetup.8,v 1.7 1997/06/23 04:52:11 steve Exp $
+
+.Dd January 24, 1996
+.Dt TZSETUP 8
+.Os FreeBSD
+
+.Sh NAME
+.Nm tzsetup
+.Nd set local timezone
+
+.Sh SYNOPSIS
+.Nm tzsetup
+.Op Fl n
+
+.Sh DESCRIPTION
+.Nm Tzsetup
+is a menu based program to set your local time zone. Pick the continent
+and a city (e.g. your capital) that best describes your location and
+.Nm
+do all the work for you.
+.Pp
+The following option is available:
+.Bl -tag -width indent
+.It Fl n
+Do not really do anything.
+.El
+
+.Sh BUGS
+You need to reboot the machine for changes to take effect.
+
+.Sh FILES
+.Bl -tag -width /etc/wall_cmos_clock -compact
+.It Pa /etc/localtime
+current time zone file
+.It Pa /usr/share/zoneinfo
+directory for zoneinfo files
+.It Pa /etc/wall_cmos_clock
+see
+.Xr adjkerntz 8 .
+.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 .
diff --git a/usr.sbin/tzsetup/tzsetup.c b/usr.sbin/tzsetup/tzsetup.c
new file mode 100644
index 0000000..d3bd449
--- /dev/null
+++ b/usr.sbin/tzsetup/tzsetup.c
@@ -0,0 +1,671 @@
+/*
+ * Copyright 1996 Massachusetts Institute of Technology
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby
+ * granted, provided that both the above copyright notice and this
+ * permission notice appear in all copies, that both the above
+ * copyright notice and this permission notice appear in all
+ * supporting documentation, and that the name of M.I.T. not be used
+ * in advertising or publicity pertaining to distribution of the
+ * software without specific, written prior permission. M.I.T. makes
+ * no representations about the suitability of this software for any
+ * purpose. It is provided "as is" without express or implied
+ * warranty.
+ *
+ * THIS SOFTWARE IS PROVIDED BY M.I.T. ``AS IS''. M.I.T. DISCLAIMS
+ * ALL EXPRESS OR IMPLIED WARRANTIES WITH REGARD TO THIS SOFTWARE,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT
+ * SHALL M.I.T. BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * Second attempt at a `tzmenu' program, using the separate description
+ * files provided in newer tzdata releases.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id: tzsetup.c,v 1.8 1997/10/27 07:49:47 charnier Exp $";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <dialog.h>
+#include <err.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <sys/fcntl.h>
+#include <sys/queue.h>
+#include <sys/stat.h>
+
+#include "paths.h"
+
+static int reallydoit = 1;
+
+static int continent_country_menu(dialogMenuItem *);
+static int set_zone_multi(dialogMenuItem *);
+static int set_zone_whole_country(dialogMenuItem *);
+static int set_zone_menu(dialogMenuItem *);
+
+struct continent {
+ dialogMenuItem *menu;
+ int nitems;
+ int ch;
+ int sc;
+};
+
+static struct continent africa, america, antarctica, arctic, asia, atlantic;
+static struct continent australia, europe, indian, pacific;
+
+static struct continent_names {
+ char *name;
+ struct continent *continent;
+} continent_names[] = {
+ { "Africa", &africa }, { "America", &america },
+ { "Antarctica", &antarctica }, { "Arctic", &arctic },
+ { "Asia", &asia },
+ { "Atlantic", &atlantic }, { "Australia", &australia },
+ { "Europe", &europe }, { "Indian", &indian }, { "Pacific", &pacific }
+};
+
+static dialogMenuItem continents[] = {
+ { "1", "Africa", 0, continent_country_menu, 0, &africa },
+ { "2", "America -- North and South", 0, continent_country_menu, 0,
+ &america },
+ { "3", "Antarctica", 0, continent_country_menu, 0, &antarctica },
+ { "4", "Arctic Ocean", 0, continent_country_menu, 0, &arctic },
+ { "5", "Asia", 0, continent_country_menu, 0, &asia },
+ { "6", "Atlantic Ocean", 0, continent_country_menu, 0, &atlantic },
+ { "7", "Australia", 0, continent_country_menu, 0, &australia },
+ { "8", "Europe", 0, continent_country_menu, 0, &europe },
+ { "9", "Indian Ocean", 0, continent_country_menu, 0, &indian },
+ { "0", "Pacific Ocean", 0, continent_country_menu, 0, &pacific }
+};
+#define NCONTINENTS ((sizeof continents)/(sizeof continents[0]))
+#define OCEANP(x) ((x) == 3 || (x) == 5 || (x) == 8 || (x) == 9)
+
+static int
+continent_country_menu(dialogMenuItem *continent)
+{
+ int rv;
+ struct continent *contp = continent->data;
+ char title[256];
+ int isocean = OCEANP(continent - continents);
+ int menulen;
+
+ /* Short cut -- if there's only one country, don't post a menu. */
+ if (contp->nitems == 1) {
+ return set_zone_menu(&contp->menu[0]);
+ }
+
+ /* It's amazing how much good grammar really matters... */
+ if (!isocean)
+ snprintf(title, sizeof title, "Countries in %s",
+ continent->title);
+ else
+ snprintf(title, sizeof title, "Islands and groups in the %s",
+ continent->title);
+
+ menulen = contp->nitems < 16 ? contp->nitems : 16;
+ rv = dialog_menu(title, (isocean ? "Select an island or group"
+ : "Select a country"), -1, -1, menulen,
+ -contp->nitems, contp->menu, 0, &contp->ch,
+ &contp->sc);
+ if (rv == 0)
+ return DITEM_LEAVE_MENU;
+ return DITEM_RECREATE;
+}
+
+static struct continent *
+find_continent(const char *name)
+{
+ int i;
+
+ for (i = 0; i < NCONTINENTS; i++) {
+ if (strcmp(name, continent_names[i].name) == 0)
+ return continent_names[i].continent;
+ }
+ return 0;
+}
+
+struct country {
+ char *name;
+ char *tlc;
+ int nzones;
+ char *filename; /* use iff nzones < 0 */
+ struct continent *continent; /* use iff nzones < 0 */
+ TAILQ_HEAD(, zone) zones; /* use iff nzones > 0 */
+ dialogMenuItem *submenu; /* use iff nzones > 0 */
+};
+
+struct zone {
+ TAILQ_ENTRY(zone) link;
+ char *descr;
+ char *filename;
+ struct continent *continent;
+};
+
+/*
+ * This is the easiest organization... we use ISO 3166 country codes,
+ * of the two-letter variety, so we just size this array to suit.
+ * Beats worrying about dynamic allocation.
+ */
+#define NCOUNTRIES (26*26)
+static struct country countries[NCOUNTRIES];
+#define CODE2INT(s) ((s[0] - 'A') * 26 + (s[1] - 'A'))
+
+/*
+ * Read the ISO 3166 country code database in _PATH_ISO3166
+ * (/usr/share/misc/iso3166). On error, exit via err(3).
+ */
+static void
+read_iso3166_table(void)
+{
+ FILE *fp;
+ char *s, *t, *name;
+ size_t len;
+ int lineno;
+ struct country *cp;
+
+ fp = fopen(_PATH_ISO3166, "r");
+ if (!fp)
+ err(1, _PATH_ISO3166);
+ lineno = 0;
+
+ while ((s = fgetln(fp, &len)) != 0) {
+ lineno++;
+ if (s[len - 1] != '\n')
+ errx(1, _PATH_ISO3166 ":%d: invalid format", lineno);
+ s[len - 1] = '\0';
+ if (s[0] == '#' || strspn(s, " \t") == len - 1)
+ continue;
+
+ /* Isolate the two-letter code. */
+ t = strsep(&s, "\t");
+ if (t == 0 || strlen(t) != 2)
+ errx(1, _PATH_ISO3166 ":%d: invalid format", lineno);
+ if (t[0] < 'A' || t[0] > 'Z' || t[1] < 'A' || t[1] > 'Z')
+ errx(1, _PATH_ISO3166 ":%d: invalid code `%s'",
+ lineno, t);
+
+ /* Now skip past the three-letter and numeric codes. */
+ name = strsep(&s, "\t"); /* 3-let */
+ if (name == 0 || strlen(name) != 3)
+ errx(1, _PATH_ISO3166 ":%d: invalid format", lineno);
+ name = strsep(&s, "\t"); /* numeric */
+ if (name == 0 || strlen(name) != 3)
+ errx(1, _PATH_ISO3166 ":%d: invalid format", lineno);
+
+ name = s;
+
+ cp = &countries[CODE2INT(t)];
+ if (cp->name)
+ errx(1, _PATH_ISO3166
+ ":%d: country code `%s' multiply defined: %s",
+ lineno, t, cp->name);
+ cp->name = strdup(name);
+ cp->tlc = strdup(t);
+ }
+
+ fclose(fp);
+}
+
+static void
+add_zone_to_country(int lineno, const char *tlc, const char *descr,
+ const char *file, struct continent *cont)
+{
+ struct zone *zp;
+ struct country *cp;
+
+ if (tlc[0] < 'A' || tlc[0] > 'Z' || tlc[1] < 'A' || tlc[1] > 'Z')
+ errx(1, _PATH_ZONETAB ":%d: country code `%s' invalid",
+ lineno, tlc);
+
+ cp = &countries[CODE2INT(tlc)];
+ if (cp->name == 0)
+ errx(1, _PATH_ZONETAB ":%d: country code `%s' unknown",
+ lineno, tlc);
+
+ if (descr) {
+ if (cp->nzones < 0)
+ errx(1, _PATH_ZONETAB
+ ":%d: conflicting zone definition", lineno);
+
+ zp = malloc(sizeof *zp);
+ if (zp == 0)
+ err(1, "malloc(%lu)", (unsigned long)sizeof *zp);
+
+ if (cp->nzones == 0)
+ TAILQ_INIT(&cp->zones);
+
+ zp->descr = strdup(descr);
+ zp->filename = strdup(file);
+ zp->continent = cont;
+ TAILQ_INSERT_TAIL(&cp->zones, zp, link);
+ cp->nzones++;
+ } else {
+ if (cp->nzones > 0)
+ errx(1, _PATH_ZONETAB
+ ":%d: zone must have description", lineno);
+ if (cp->nzones < 0)
+ errx(1, _PATH_ZONETAB
+ ":%d: zone multiply defined", lineno);
+ cp->nzones = -1;
+ cp->filename = strdup(file);
+ cp->continent = cont;
+ }
+}
+
+/*
+ * This comparison function intentionally sorts all of the null-named
+ * ``countries''---i.e., the codes that don't correspond to a real
+ * country---to the end. Everything else is lexical by country name.
+ */
+static int
+compare_countries(const void *xa, const void *xb)
+{
+ const struct country *a = xa, *b = xb;
+
+ if (a->name == 0 && b->name == 0)
+ return 0;
+ if (a->name == 0 && b->name != 0)
+ return 1;
+ if (b->name == 0)
+ return -1;
+
+ return strcmp(a->name, b->name);
+}
+
+/*
+ * This must be done AFTER all zone descriptions are read, since it breaks
+ * CODE2INT().
+ */
+static void
+sort_countries(void)
+{
+ qsort(countries, NCOUNTRIES, sizeof countries[0], compare_countries);
+}
+
+static void
+read_zones(void)
+{
+ FILE *fp;
+ char *line;
+ size_t len;
+ int lineno;
+ char *tlc, *coord, *file, *descr, *p;
+ char contbuf[16];
+ struct continent *cont;
+
+ fp = fopen(_PATH_ZONETAB, "r");
+ if (!fp)
+ err(1, _PATH_ZONETAB);
+ lineno = 0;
+
+ while ((line = fgetln(fp, &len)) != 0) {
+ lineno++;
+ if (line[len - 1] != '\n')
+ errx(1, _PATH_ZONETAB ":%d: invalid format", lineno);
+ line[len - 1] = '\0';
+ if (line[0] == '#')
+ continue;
+
+ tlc = strsep(&line, "\t");
+ if (strlen(tlc) != 2)
+ errx(1, _PATH_ZONETAB ":%d: invalid country code `%s'",
+ lineno, tlc);
+ coord = strsep(&line, "\t");
+ file = strsep(&line, "\t");
+ p = strchr(file, '/');
+ if (p == 0)
+ errx(1, _PATH_ZONETAB ":%d: invalid zone name `%s'",
+ lineno, file);
+ contbuf[0] = '\0';
+ strncat(contbuf, file, p - file);
+ cont = find_continent(contbuf);
+ if (!cont)
+ errx(1, _PATH_ZONETAB ":%d: invalid region `%s'",
+ lineno, contbuf);
+
+ descr = (line && *line) ? line : 0;
+
+ add_zone_to_country(lineno, tlc, descr, file, cont);
+ }
+ fclose(fp);
+}
+
+static void
+make_menus(void)
+{
+ struct country *cp;
+ struct zone *zp, *zp2;
+ struct continent *cont;
+ dialogMenuItem *dmi;
+ int i;
+
+ /*
+ * First, count up all the countries in each continent/ocean.
+ * Be careful to count those countries which have multiple zones
+ * only once for each. NB: some countries are in multiple
+ * continents/oceans.
+ */
+ for (cp = countries; cp->name; cp++) {
+ if (cp->nzones == 0)
+ continue;
+ if (cp->nzones < 0) {
+ cp->continent->nitems++;
+ } else {
+ for (zp = cp->zones.tqh_first; zp;
+ zp = zp->link.tqe_next) {
+ cont = zp->continent;
+ for (zp2 = cp->zones.tqh_first;
+ zp2->continent != cont;
+ zp2 = zp2->link.tqe_next)
+ ;
+ if (zp2 == zp)
+ zp->continent->nitems++;
+ }
+ }
+ }
+
+ /*
+ * Now allocate memory for the country menus. We set
+ * nitems back to zero so that we can use it for counting
+ * again when we actually build the menus.
+ */
+ for (i = 0; i < NCONTINENTS; i++) {
+ continent_names[i].continent->menu =
+ malloc(sizeof(dialogMenuItem) *
+ continent_names[i].continent->nitems);
+ if (continent_names[i].continent->menu == 0)
+ err(1, "malloc for continent menu");
+ continent_names[i].continent->nitems = 0;
+ }
+
+ /*
+ * Now that memory is allocated, create the menu items for
+ * each continent. For multiple-zone countries, also create
+ * the country's zone submenu.
+ */
+ for (cp = countries; cp->name; cp++) {
+ if (cp->nzones == 0)
+ continue;
+ if (cp->nzones < 0) {
+ dmi = &cp->continent->menu[cp->continent->nitems];
+ memset(dmi, 0, sizeof *dmi);
+ asprintf(&dmi->prompt, "%d",
+ ++cp->continent->nitems);
+ dmi->title = cp->name;
+ dmi->checked = 0;
+ dmi->fire = set_zone_whole_country;
+ dmi->selected = 0;
+ dmi->data = cp;
+ } else {
+ cp->submenu = malloc(cp->nzones * sizeof *dmi);
+ if (cp->submenu == 0)
+ err(1, "malloc for submenu");
+ cp->nzones = 0;
+ for (zp = cp->zones.tqh_first; zp;
+ zp = zp->link.tqe_next) {
+ cont = zp->continent;
+ dmi = &cp->submenu[cp->nzones];
+ memset(dmi, 0, sizeof *dmi);
+ asprintf(&dmi->prompt, "%d",
+ ++cp->nzones);
+ dmi->title = zp->descr;
+ dmi->checked = 0;
+ dmi->fire = set_zone_multi;
+ dmi->selected = 0;
+ dmi->data = zp;
+
+ for (zp2 = cp->zones.tqh_first;
+ zp2->continent != cont;
+ zp2 = zp2->link.tqe_next)
+ ;
+ if (zp2 != zp)
+ continue;
+
+ dmi = &cont->menu[cont->nitems];
+ memset(dmi, 0, sizeof *dmi);
+ asprintf(&dmi->prompt, "%d", ++cont->nitems);
+ dmi->title = cp->name;
+ dmi->checked = 0;
+ dmi->fire = set_zone_menu;
+ dmi->selected = 0;
+ dmi->data = cp;
+ }
+ }
+ }
+}
+
+static int
+set_zone_menu(dialogMenuItem *dmi)
+{
+ int rv;
+ char buf[256];
+ struct country *cp = dmi->data;
+ int menulen;
+
+ snprintf(buf, sizeof buf, "%s Time Zones", cp->name);
+ menulen = cp->nzones < 16 ? cp->nzones : 16;
+ rv = dialog_menu(buf, "Select a zone which observes the same time as "
+ "your locality.", -1, -1, menulen, -cp->nzones,
+ cp->submenu, 0, 0, 0);
+ if (rv != 0)
+ return DITEM_RECREATE;
+ return DITEM_LEAVE_MENU;
+}
+
+static int
+install_zone_file(const char *filename)
+{
+ struct stat sb;
+ int fd1, fd2;
+ int copymode;
+ char *msg;
+ ssize_t len;
+ char buf[1024];
+
+ if (lstat(_PATH_LOCALTIME, &sb) < 0)
+ /* Nothing there yet... */
+ copymode = 1;
+ else if(S_ISLNK(sb.st_mode))
+ copymode = 0;
+ else
+ copymode = 1;
+
+#ifdef VERBOSE
+ if (copymode)
+ asprintf(&msg, "Copying %s to " _PATH_LOCALTIME, filename);
+ else
+ asprintf(&msg, "Creating symbolic link " _PATH_LOCALTIME
+ " to %s", filename);
+
+ dialog_notify(msg);
+ free(msg);
+#endif
+
+ if (reallydoit) {
+ if (copymode) {
+ fd1 = open(filename, O_RDONLY, 0);
+ if (fd1 < 0) {
+ asprintf(&msg, "Could not open %s: %s",
+ filename, strerror(errno));
+ dialog_mesgbox("Error", msg, 8, 72);
+ free(msg);
+ return DITEM_FAILURE | DITEM_RECREATE;
+ }
+
+ unlink(_PATH_LOCALTIME);
+ fd2 = open(_PATH_LOCALTIME,
+ O_CREAT | O_EXCL | O_WRONLY,
+ 0444);
+ if (fd2 < 0) {
+ asprintf(&msg, "Could not open "
+ _PATH_LOCALTIME ": %s",
+ strerror(errno));
+ dialog_mesgbox("Error", msg, 8, 72);
+ free(msg);
+ return DITEM_FAILURE | DITEM_RECREATE;
+ }
+
+ while ((len = read(fd1, buf, sizeof buf)) > 0)
+ len = write(fd2, buf, len);
+
+ if (len == -1) {
+ asprintf(&msg, "Error copying %s to "
+ _PATH_LOCALTIME ": %s",
+ strerror(errno));
+ dialog_mesgbox("Error", msg, 8, 72);
+ free(msg);
+ /* Better to leave none than a corrupt one. */
+ unlink(_PATH_LOCALTIME);
+ return DITEM_FAILURE | DITEM_RECREATE;
+ }
+ close(fd1);
+ close(fd2);
+ } else {
+ if (access(filename, R_OK) != 0) {
+ asprintf(&msg, "Cannot access %s: %s",
+ filename, strerror(errno));
+ dialog_mesgbox("Error", msg, 8, 72);
+ free(msg);
+ return DITEM_FAILURE | DITEM_RECREATE;
+ }
+ unlink(_PATH_LOCALTIME);
+ if (symlink(filename, _PATH_LOCALTIME) < 0) {
+ asprintf(&msg, "Cannot create symbolic link "
+ _PATH_LOCALTIME " to %s: %s",
+ filename, strerror(errno));
+ dialog_mesgbox("Error", msg, 8, 72);
+ free(msg);
+ return DITEM_FAILURE | DITEM_RECREATE;
+ }
+ }
+ }
+
+#ifdef VERBOSE
+ if (copymode)
+ asprintf(&msg, "Copied timezone file from %s to "
+ _PATH_LOCALTIME, filename);
+ else
+ asprintf(&msg, "Created symbolic link from " _PATH_LOCALTIME
+ " to %s", filename);
+
+ dialog_mesgbox("Done", msg, 8, 72);
+ free(msg);
+#endif
+ return DITEM_LEAVE_MENU;
+}
+
+static int
+confirm_zone(const char *filename)
+{
+ char *msg;
+ struct tm *tm;
+ time_t t = time(0);
+ int rv;
+
+ setenv("TZ", filename, 1);
+ tzset();
+ tm = localtime(&t);
+
+ asprintf(&msg, "Does the abbreviation `%s' look reasonable?",
+ tm->tm_zone);
+ rv = !dialog_yesno("Confirmation", msg, 4, 72);
+ free(msg);
+ return rv;
+}
+
+static int
+set_zone_multi(dialogMenuItem *dmi)
+{
+ char *fn;
+ struct zone *zp = dmi->data;
+ int rv;
+
+ if (!confirm_zone(zp->filename))
+ return DITEM_FAILURE | DITEM_RECREATE;
+
+ asprintf(&fn, "%s/%s", _PATH_ZONEINFO, zp->filename);
+ rv = install_zone_file(fn);
+ free(fn);
+ return rv;
+}
+
+static int
+set_zone_whole_country(dialogMenuItem *dmi)
+{
+ char *fn;
+ struct country *cp = dmi->data;
+ int rv;
+
+ if (!confirm_zone(cp->filename))
+ return DITEM_FAILURE | DITEM_RECREATE;
+
+ asprintf(&fn, "%s/%s", _PATH_ZONEINFO, cp->filename);
+ rv = install_zone_file(fn);
+ free(fn);
+ return rv;
+}
+
+static void
+usage()
+{
+ fprintf(stderr, "usage: tzsetup [-n]\n");
+ exit(1);
+}
+
+int
+main(int argc, char **argv)
+{
+ int c;
+
+ while ((c = getopt(argc, argv, "n")) != -1) {
+ switch(c) {
+ case 'n':
+ reallydoit = 0;
+ break;
+
+ default:
+ usage();
+ }
+ }
+
+ if (optind != argc)
+ usage();
+
+ read_iso3166_table();
+ read_zones();
+ sort_countries();
+ make_menus();
+
+ init_dialog();
+ if (!dialog_yesno("Select local or UTC (Greenwich Mean Time) clock",
+ "Is this machine's CMOS clock set to UTC? If it is set to local time,\n"
+ "please choose NO here!", 7, 72))
+ if (reallydoit)
+ system("rm -f /etc/wall_cmos_clock");
+ else
+ if (reallydoit)
+ system("touch /etc/wall_cmos_clock");
+ 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/vidcontrol/Makefile b/usr.sbin/vidcontrol/Makefile
new file mode 100644
index 0000000..0200812
--- /dev/null
+++ b/usr.sbin/vidcontrol/Makefile
@@ -0,0 +1,6 @@
+PROG= vidcontrol
+SRCS= vidcontrol.c decode.c
+
+CFLAGS += -Wall
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/vidcontrol/decode.c b/usr.sbin/vidcontrol/decode.c
new file mode 100644
index 0000000..fd24e76
--- /dev/null
+++ b/usr.sbin/vidcontrol/decode.c
@@ -0,0 +1,80 @@
+/*-
+ * Copyright (c) 1994 Søren Schmidt
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * in this position and unchanged.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software withough specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#include <stdio.h>
+#include <string.h>
+#include "decode.h"
+
+int decode(FILE *fd, char *buffer)
+{
+ int n, pos = 0;
+ char *p;
+ char temp[128];
+
+#define DEC(c) (((c) - ' ') & 0x3f)
+
+ do {
+ if (!fgets(temp, sizeof(temp), fd))
+ return(0);
+ } while (strncmp(temp, "begin ", 6));
+ sscanf(temp, "begin %o %s", &n, temp);
+ for (;;) {
+ if (!fgets(p = temp, sizeof(temp), fd))
+ return(0);
+ if ((n = DEC(*p)) <= 0)
+ break;
+ for (++p; n > 0; p += 4, n -= 3)
+ if (n >= 3) {
+ buffer[pos++] = DEC(p[0])<<2 | DEC(p[1])>>4;
+ buffer[pos++] = DEC(p[1])<<4 | DEC(p[2])>>2;
+ buffer[pos++] = DEC(p[2])<<6 | DEC(p[3]);
+ }
+ else {
+ if (n >= 1) {
+ buffer[pos++] =
+ DEC(p[0])<<2 | DEC(p[1])>>4;
+ }
+ if (n >= 2) {
+ buffer[pos++] =
+ DEC(p[1])<<4 | DEC(p[2])>>2;
+ }
+ if (n >= 3) {
+ buffer[pos++] =
+ DEC(p[2])<<6 | DEC(p[3]);
+ }
+ }
+ }
+ if (!fgets(temp, sizeof(temp), fd) || strcmp(temp, "end\n"))
+ return(0);
+ return(pos);
+}
diff --git a/usr.sbin/vidcontrol/decode.h b/usr.sbin/vidcontrol/decode.h
new file mode 100644
index 0000000..b939af4
--- /dev/null
+++ b/usr.sbin/vidcontrol/decode.h
@@ -0,0 +1 @@
+int decode(FILE *fd, char *buffer);
diff --git a/usr.sbin/vidcontrol/path.h b/usr.sbin/vidcontrol/path.h
new file mode 100644
index 0000000..709acbc
--- /dev/null
+++ b/usr.sbin/vidcontrol/path.h
@@ -0,0 +1,4 @@
+#define KEYMAP_PATH "/usr/share/syscons/keymaps/"
+#define FONT_PATH "/usr/share/syscons/fonts/"
+#define SCRNMAP_PATH "/usr/share/syscons/scrnmaps/"
+
diff --git a/usr.sbin/vidcontrol/vidcontrol.1 b/usr.sbin/vidcontrol/vidcontrol.1
new file mode 100644
index 0000000..aea5467
--- /dev/null
+++ b/usr.sbin/vidcontrol/vidcontrol.1
@@ -0,0 +1,123 @@
+.\"
+.\" vidcontrol - a utility for manipulating the syscons video driver
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" @(#)vidcontrol.1
+.\" $Id: vidcontrol.1,v 1.11 1997/07/24 23:46:35 wosch Exp $
+.\"
+.Dd May 22, 1994
+.Dt VIDCONTROL 1
+.Os
+.Sh NAME
+.Nm vidcontrol
+.Nd a utility for manipulating the syscons video driver.
+.Sh SYNOPSIS
+.Nm
+.Op Fl r Ar fg Ar bg
+.Op Fl b Ar color
+.Op Fl c Ar appearance
+.Op Fl d
+.Op Fl l Ar scrmap
+.Op Fl L
+.Op Fl m Ar on|off
+.Op Fl f Ar size Ar file
+.Op Fl s Ar number
+.Op Fl t Ar N|off
+.Op Fl x
+.Op mode
+.Op fgcol Op bgcol
+.Op show
+.Sh DESCRIPTION
+The
+.Nm
+command is used to set various options for the syscons video driver,
+such as video mode, colors, cursors, scrnmaps, font and screensaver timeout.
+.Pp
+The following command line options are supported:
+.Bl -tag -width indent
+.It mode
+Select a new video mode. The modes currently supported are:
+.Ar VGA_40x25 ,
+.Ar VGA_80x25 ,
+.Ar VGA_80x50 ,
+.Ar EGA_80x25 ,
+.Ar EGA_80x43 .
+On some laptops the modes
+.Ar VGA_80x30
+and
+.Ar VGA_80x60
+can be used.
+The graphic mode
+.Ar VGA_320x200
+can also be chosen.
+.It fgcol Op bgcol
+Change colors when displaying text. Specify the foreground color
+(e.g. "vidcontrol white"), or both a foreground & background color
+(e.g. "vidcontrol yellow blue").
+.It show
+See the supported colors on a given platform.
+.It Fl r Ar foreground Ar background
+Change reverse mode colors to
+.Ar foreground
+and
+.Ar background .
+.It Fl b Ar color
+Set border color to
+.Ar color
+(only supported on VGA hardware).
+.It Fl c Ar normal|blink|destructive
+Change the cursor appearance. The cursor is either an inverting block
+(normal) that eventually can "blink". Or it can be like the old hardware cursor
+(destructive). The latter is actually a simulation.
+.It Fl d
+Print out current screen output map.
+.It Fl l Ar scrmap
+Install screen output map file from
+.Ar scrmap
+.It Fl L
+Install default screen output map.
+.It Fl m Ar on|off
+Switch the mousepointer
+.Ar on
+or
+.Ar off .
+Used together with the moused
+daemon for textmode cut & paste functionality.
+.It Fl f Ar size Ar file
+Load font
+.Ar file
+for
+.Ar size
+(currently, only 8x8, 8x14 or 8x16).
+The font file can be either uuencoded or in raw binary format.
+.It Fl s Ar number
+Set the current vty to
+.Ar number .
+.It Fl t Ar N|off
+Set the screensaver timeout to
+.Ar N
+seconds, or turns it
+.Ar off .
+.It Fl x
+Use hexadecimal digits for output.
+.El
+.Sh FILES
+.Bl -tag -width /usr/share/syscons/scrnmaps -compact
+.It Pa /usr/share/syscons/fonts
+.It Pa /usr/share/syscons/scrnmaps
+.El
+.Sh SEE ALSO
+.Xr kbdcontrol 1 ,
+.Xr keyboard 4 ,
+.Xr screen 4 ,
+.Xr moused 8
+.Sh AUTHORS
+.An Søren Schmidt Aq sos@FreeBSD.org
diff --git a/usr.sbin/vidcontrol/vidcontrol.c b/usr.sbin/vidcontrol/vidcontrol.c
new file mode 100644
index 0000000..5c3133c
--- /dev/null
+++ b/usr.sbin/vidcontrol/vidcontrol.c
@@ -0,0 +1,453 @@
+/*-
+ * Copyright (c) 1994-1996 Søren Schmidt
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer,
+ * in this position and unchanged.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software withough specific prior written permission
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#include <ctype.h>
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <machine/console.h>
+#include <sys/errno.h>
+#include "path.h"
+#include "decode.h"
+
+char legal_colors[16][16] = {
+ "black", "blue", "green", "cyan",
+ "red", "magenta", "brown", "white",
+ "grey", "lightblue", "lightgreen", "lightcyan",
+ "lightred", "lightmagenta", "yellow", "lightwhite"
+};
+int hex = 0;
+int number;
+char letter;
+struct vid_info info;
+
+
+static void
+usage()
+{
+ fprintf(stderr, "%s\n%s\n%s\n",
+"usage: vidcontrol [-r fg bg] [-b color] [-c appearance] [-d] [-l scrmap]",
+" [-L] [-m on|off] [-f size file] [-s number] [-t N|off]",
+" [-x] [mode] [fgcol [bgcol]] [show]");
+ exit(1);
+}
+
+char *
+nextarg(int ac, char **av, int *indp, int oc)
+{
+ if (*indp < ac)
+ return(av[(*indp)++]);
+ errx(1, "option requires two arguments -- %c", oc);
+ return("");
+}
+
+char *
+mkfullname(const char *s1, const char *s2, const char *s3)
+{
+ static char *buf = NULL;
+ static int bufl = 0;
+ int f;
+
+ f = strlen(s1) + strlen(s2) + strlen(s3) + 1;
+ if (f > bufl)
+ if (buf)
+ buf = (char *)realloc(buf, f);
+ else
+ buf = (char *)malloc(f);
+ if (!buf) {
+ bufl = 0;
+ return(NULL);
+ }
+
+ bufl = f;
+ strcpy(buf, s1);
+ strcat(buf, s2);
+ strcat(buf, s3);
+ return(buf);
+}
+
+void
+load_scrnmap(char *filename)
+{
+ FILE *fd = 0;
+ int i, size;
+ char *name;
+ scrmap_t scrnmap;
+ char *prefix[] = {"", "", SCRNMAP_PATH, SCRNMAP_PATH, NULL};
+ char *postfix[] = {"", ".scm", "", ".scm"};
+
+ for (i=0; prefix[i]; i++) {
+ name = mkfullname(prefix[i], filename, postfix[i]);
+ fd = fopen(name, "r");
+ if (fd)
+ break;
+ }
+ if (fd == NULL) {
+ warn("screenmap file not found");
+ return;
+ }
+ size = sizeof(scrnmap);
+ if (decode(fd, (char *)&scrnmap) != size) {
+ rewind(fd);
+ if (fread(&scrnmap, 1, size, fd) != size) {
+ warnx("bad screenmap file");
+ fclose(fd);
+ return;
+ }
+ }
+ if (ioctl(0, PIO_SCRNMAP, &scrnmap) < 0)
+ warn("can't load screenmap");
+ fclose(fd);
+}
+
+void
+load_default_scrnmap()
+{
+ scrmap_t scrnmap;
+ int i;
+
+ for (i=0; i<256; i++)
+ *((char*)&scrnmap + i) = i;
+ if (ioctl(0, PIO_SCRNMAP, &scrnmap) < 0)
+ warn("can't load default screenmap");
+}
+
+void
+print_scrnmap()
+{
+ unsigned char map[256];
+ int i;
+
+ if (ioctl(0, GIO_SCRNMAP, &map) < 0) {
+ warn("getting screenmap");
+ return;
+ }
+ for (i=0; i<sizeof(map); i++) {
+ if (i > 0 && i % 16 == 0)
+ fprintf(stdout, "\n");
+ if (hex)
+ fprintf(stdout, " %02x", map[i]);
+ else
+ fprintf(stdout, " %03d", map[i]);
+ }
+ fprintf(stdout, "\n");
+
+}
+
+void
+load_font(char *type, char *filename)
+{
+ FILE *fd = 0;
+ int i, io, size;
+ char *name, *fontmap;
+ char *prefix[] = {"", "", FONT_PATH, FONT_PATH, NULL};
+ char *postfix[] = {"", ".fnt", "", ".fnt"};
+
+ for (i=0; prefix[i]; i++) {
+ name = mkfullname(prefix[i], filename, postfix[i]);
+ fd = fopen(name, "r");
+ if (fd)
+ break;
+ }
+ if (fd == NULL) {
+ warn("font file not found");
+ return;
+ }
+ if (!strcmp(type, "8x8")) {
+ size = 8*256;
+ io = PIO_FONT8x8;
+ }
+ else if (!strcmp(type, "8x14")) {
+ size = 14*256;
+ io = PIO_FONT8x14;
+ }
+ else if (!strcmp(type, "8x16")) {
+ size = 16*256;
+ io = PIO_FONT8x16;
+ }
+ else {
+ warn("bad font size specification");
+ fclose(fd);
+ return;
+ }
+ fontmap = (char*) malloc(size);
+ if (decode(fd, fontmap) != size) {
+ rewind(fd);
+ if (fread(fontmap, 1, size, fd) != size) {
+ warnx("bad font file");
+ fclose(fd);
+ free(fontmap);
+ return;
+ }
+ }
+ if (ioctl(0, io, fontmap) < 0)
+ warn("can't load font");
+ fclose(fd);
+ free(fontmap);
+}
+
+void
+set_screensaver_timeout(char *arg)
+{
+ int nsec;
+
+ if (!strcmp(arg, "off"))
+ nsec = 0;
+ else {
+ nsec = atoi(arg);
+ if ((*arg == '\0') || (nsec < 1)) {
+ warnx("argument must be a positive number");
+ return;
+ }
+ }
+ if (ioctl(0, CONS_BLANKTIME, &nsec) == -1)
+ warn("setting screensaver period");
+}
+
+void
+set_cursor_type(char *appearence)
+{
+ int type;
+
+ if (!strcmp(appearence, "normal"))
+ type = 0;
+ else if (!strcmp(appearence, "blink"))
+ type = 1;
+ else if (!strcmp(appearence, "destructive"))
+ type = 3;
+ else {
+ warnx("argument to -c must be normal, blink or destructive");
+ return;
+ }
+ ioctl(0, CONS_CURSORTYPE, &type);
+}
+
+void
+video_mode(int argc, char **argv, int *index)
+{
+ int mode;
+
+ if (*index < argc) {
+ if (!strcmp(argv[*index], "VGA_40x25"))
+ mode = SW_VGA_C40x25;
+ else if (!strcmp(argv[*index], "VGA_80x25"))
+ mode = SW_VGA_C80x25;
+ else if (!strcmp(argv[*index], "VGA_80x30"))
+ mode = SW_VGA_C80x30;
+ else if (!strcmp(argv[*index], "VGA_80x50"))
+ mode = SW_VGA_C80x50;
+ else if (!strcmp(argv[*index], "VGA_80x60"))
+ mode = SW_VGA_C80x60;
+ else if (!strcmp(argv[*index], "VGA_320x200"))
+ mode = SW_VGA_CG320;
+ else if (!strcmp(argv[*index], "EGA_80x25"))
+ mode = SW_ENH_C80x25;
+ else if (!strcmp(argv[*index], "EGA_80x43"))
+ mode = SW_ENH_C80x43;
+ else
+ return;
+ if (ioctl(0, mode, NULL) < 0)
+ warn("cannot set videomode");
+ (*index)++;
+ }
+ return;
+}
+
+int
+get_color_number(char *color)
+{
+ int i;
+
+ for (i=0; i<16; i++)
+ if (!strcmp(color, legal_colors[i]))
+ return i;
+ return -1;
+}
+
+void
+set_normal_colors(int argc, char **argv, int *index)
+{
+ int color;
+
+ if (*index < argc && (color = get_color_number(argv[*index])) != -1) {
+ (*index)++;
+ fprintf(stderr, "[=%dF", color);
+ if (*index < argc
+ && (color = get_color_number(argv[*index])) != -1
+ && color < 8) {
+ (*index)++;
+ fprintf(stderr, "[=%dG", color);
+ }
+ }
+}
+
+void
+set_reverse_colors(int argc, char **argv, int *index)
+{
+ int color;
+
+ if ((color = get_color_number(argv[*(index)-1])) != -1) {
+ fprintf(stderr, "[=%dH", color);
+ if (*index < argc
+ && (color = get_color_number(argv[*index])) != -1
+ && color < 8) {
+ (*index)++;
+ fprintf(stderr, "[=%dI", color);
+ }
+ }
+}
+
+void
+set_console(char *arg)
+{
+ int n;
+
+ if( !arg || strspn(arg,"0123456789") != strlen(arg)) {
+ warnx("bad console number");
+ return;
+ }
+
+ n = atoi(arg);
+ if (n < 1 || n > 12) {
+ warnx("console number out of range");
+ } else if (ioctl(0,VT_ACTIVATE,(char *)n) == -1)
+ warn("ioctl(VT_ACTIVATE)");
+}
+
+void
+set_border_color(char *arg)
+{
+ int color;
+
+ if ((color = get_color_number(arg)) != -1) {
+ fprintf(stderr, "[=%dA", color);
+ }
+ else
+ usage();
+}
+
+void
+set_mouse(char *arg)
+{
+ struct mouse_info mouse;
+
+ if (!strcmp(arg, "on"))
+ mouse.operation = MOUSE_SHOW;
+ else if (!strcmp(arg, "off"))
+ mouse.operation = MOUSE_HIDE;
+ else {
+ warnx("argument to -m must either on or off");
+ return;
+ }
+ ioctl(0, CONS_MOUSECTL, &mouse);
+}
+
+void
+test_frame()
+{
+ int i;
+
+ fprintf(stdout, "[=0G\n\n");
+ for (i=0; i<8; i++) {
+ fprintf(stdout, "[=15F[=0G %2d [=%dF%-16s"
+ "[=15F[=0G %2d [=%dF%-16s "
+ "[=15F %2d [=%dGBACKGROUND[=0G\n",
+ i, i, legal_colors[i], i+8, i+8,
+ legal_colors[i+8], i, i);
+ }
+ fprintf(stdout, "[=%dF[=%dG[=%dH[=%dI\n",
+ info.mv_norm.fore, info.mv_norm.back,
+ info.mv_rev.fore, info.mv_rev.back);
+}
+
+int
+main(int argc, char **argv)
+{
+ int opt;
+
+
+ info.size = sizeof(info);
+ if (ioctl(0, CONS_GETINFO, &info) < 0)
+ err(1, "must be on a virtual console");
+ while((opt = getopt(argc, argv, "b:c:df:l:Lm:r:s:t:x")) != -1)
+ switch(opt) {
+ case 'b':
+ set_border_color(optarg);
+ break;
+ case 'c':
+ set_cursor_type(optarg);
+ break;
+ case 'd':
+ print_scrnmap();
+ break;
+ case 'f':
+ load_font(optarg,
+ nextarg(argc, argv, &optind, 'f'));
+ break;
+ case 'l':
+ load_scrnmap(optarg);
+ break;
+ case 'L':
+ load_default_scrnmap();
+ break;
+ case 'm':
+ set_mouse(optarg);
+ break;
+ case 'r':
+ set_reverse_colors(argc, argv, &optind);
+ break;
+ case 's':
+ set_console(optarg);
+ break;
+ case 't':
+ set_screensaver_timeout(optarg);
+ break;
+ case 'x':
+ hex = 1;
+ break;
+ default:
+ usage();
+ }
+ video_mode(argc, argv, &optind);
+ set_normal_colors(argc, argv, &optind);
+ if (optind < argc && !strcmp(argv[optind], "show")) {
+ test_frame();
+ optind++;
+ }
+ if ((optind != argc) || (argc == 1))
+ usage();
+ return 0;
+}
+
diff --git a/usr.sbin/vipw/Makefile b/usr.sbin/vipw/Makefile
new file mode 100644
index 0000000..e1c3978
--- /dev/null
+++ b/usr.sbin/vipw/Makefile
@@ -0,0 +1,7 @@
+# @(#)Makefile 8.1 (Berkeley) 6/6/93
+
+PROG= vipw
+SRCS= pw_util.c vipw.c
+MAN8= vipw.8
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/vipw/pw_util.c b/usr.sbin/vipw/pw_util.c
new file mode 100644
index 0000000..b6f6719
--- /dev/null
+++ b/usr.sbin/vipw/pw_util.c
@@ -0,0 +1,237 @@
+/*-
+ * Copyright (c) 1990, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+#if 0
+static const char sccsid[] = "@(#)pw_util.c 8.3 (Berkeley) 4/2/94";
+#endif
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+/*
+ * This file is used by all the "password" programs; vipw(8), chpass(1),
+ * and passwd(1).
+ */
+
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
+#include <sys/wait.h>
+
+#include <err.h>
+#include <fcntl.h>
+#include <paths.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "pw_util.h"
+
+extern char *tempname;
+static pid_t editpid = -1;
+static int lockfd;
+
+void
+pw_cont(sig)
+ int sig;
+{
+
+ if (editpid != -1)
+ kill(editpid, sig);
+}
+
+void
+pw_init()
+{
+ struct rlimit rlim;
+
+ /* Unlimited resource limits. */
+ rlim.rlim_cur = rlim.rlim_max = RLIM_INFINITY;
+ (void)setrlimit(RLIMIT_CPU, &rlim);
+ (void)setrlimit(RLIMIT_FSIZE, &rlim);
+ (void)setrlimit(RLIMIT_STACK, &rlim);
+ (void)setrlimit(RLIMIT_DATA, &rlim);
+ (void)setrlimit(RLIMIT_RSS, &rlim);
+
+ /* Don't drop core (not really necessary, but GP's). */
+ rlim.rlim_cur = rlim.rlim_max = 0;
+ (void)setrlimit(RLIMIT_CORE, &rlim);
+
+ /* Turn off signals. */
+ (void)signal(SIGALRM, SIG_IGN);
+ (void)signal(SIGHUP, SIG_IGN);
+ (void)signal(SIGINT, SIG_IGN);
+ (void)signal(SIGPIPE, SIG_IGN);
+ (void)signal(SIGQUIT, SIG_IGN);
+ (void)signal(SIGTERM, SIG_IGN);
+ (void)signal(SIGCONT, pw_cont);
+
+ /* Create with exact permissions. */
+ (void)umask(0);
+}
+
+int
+pw_lock()
+{
+ /*
+ * If the master password file doesn't exist, the system is hosed.
+ * Might as well try to build one. Set the close-on-exec bit so
+ * that users can't get at the encrypted passwords while editing.
+ * Open should allow flock'ing the file; see 4.4BSD. XXX
+ */
+ lockfd = open(_PATH_MASTERPASSWD, O_RDONLY, 0);
+ if (lockfd < 0 || fcntl(lockfd, F_SETFD, 1) == -1)
+ err(1, "%s", _PATH_MASTERPASSWD);
+ if (flock(lockfd, LOCK_EX|LOCK_NB))
+ errx(1, "the password db file is busy");
+ return (lockfd);
+}
+
+int
+pw_tmp()
+{
+ static char path[MAXPATHLEN] = _PATH_MASTERPASSWD;
+ int fd;
+ char *p;
+
+ if ((p = strrchr(path, '/')))
+ ++p;
+ else
+ p = path;
+ strcpy(p, "pw.XXXXXX");
+ if ((fd = mkstemp(path)) == -1)
+ err(1, "%s", path);
+ tempname = path;
+ return (fd);
+}
+
+int
+pw_mkdb(username)
+char *username;
+{
+ int pstat;
+ pid_t pid;
+
+ (void)fflush(stderr);
+ if (!(pid = vfork())) {
+ if(!username) {
+ warnx("rebuilding the database...");
+ execl(_PATH_PWD_MKDB, "pwd_mkdb", "-p", tempname, NULL);
+ } else {
+ warnx("updating the database...");
+ execl(_PATH_PWD_MKDB, "pwd_mkdb", "-p", "-u",
+ username, tempname, NULL);
+ }
+ pw_error(_PATH_PWD_MKDB, 1, 1);
+ }
+ pid = waitpid(pid, &pstat, 0);
+ if (pid == -1 || !WIFEXITED(pstat) || WEXITSTATUS(pstat) != 0)
+ return (0);
+ warnx("done");
+ return (1);
+}
+
+void
+pw_edit(notsetuid)
+ int notsetuid;
+{
+ int pstat;
+ char *p, *editor;
+
+ if (!(editor = getenv("EDITOR")))
+ editor = _PATH_VI;
+ if ((p = strrchr(editor, '/')))
+ ++p;
+ else
+ p = editor;
+
+ if (!(editpid = vfork())) {
+ if (notsetuid) {
+ (void)setgid(getgid());
+ (void)setuid(getuid());
+ }
+ execlp(editor, p, tempname, NULL);
+ _exit(1);
+ }
+ for (;;) {
+ editpid = waitpid(editpid, (int *)&pstat, WUNTRACED);
+ if (editpid == -1)
+ pw_error(editor, 1, 1);
+ else if (WIFSTOPPED(pstat))
+ raise(WSTOPSIG(pstat));
+ else if (WIFEXITED(pstat) && WEXITSTATUS(pstat) == 0)
+ break;
+ else
+ pw_error(editor, 1, 1);
+ }
+ editpid = -1;
+}
+
+void
+pw_prompt()
+{
+ int c, first;
+
+ (void)printf("re-edit the password file? [y]: ");
+ (void)fflush(stdout);
+ first = c = getchar();
+ while (c != '\n' && c != EOF)
+ c = getchar();
+ if (first == 'n')
+ pw_error(NULL, 0, 0);
+}
+
+void
+pw_error(name, err, eval)
+ char *name;
+ int err, eval;
+{
+#ifdef YP
+ extern int _use_yp;
+#endif /* YP */
+ if (err)
+ warn(name);
+#ifdef YP
+ if (_use_yp)
+ warnx("NIS information unchanged");
+ else
+#endif /* YP */
+ warnx("%s: unchanged", _PATH_MASTERPASSWD);
+ (void)unlink(tempname);
+ exit(eval);
+}
diff --git a/usr.sbin/vipw/pw_util.h b/usr.sbin/vipw/pw_util.h
new file mode 100644
index 0000000..c6aab8e
--- /dev/null
+++ b/usr.sbin/vipw/pw_util.h
@@ -0,0 +1,42 @@
+/*-
+ * Copyright (c) 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)pw_util.h 8.2 (Berkeley) 4/1/94
+ */
+
+void pw_edit __P((int));
+void pw_error __P((char *, int, int));
+void pw_init __P((void));
+int pw_lock __P((void));
+int pw_mkdb __P((char *));
+void pw_prompt __P((void));
+int pw_tmp __P((void));
diff --git a/usr.sbin/vipw/vipw.8 b/usr.sbin/vipw/vipw.8
new file mode 100644
index 0000000..ca70b5e
--- /dev/null
+++ b/usr.sbin/vipw/vipw.8
@@ -0,0 +1,93 @@
+.\" Copyright (c) 1983, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)vipw.8 8.1 (Berkeley) 6/6/93
+.\"
+.Dd June 6, 1993
+.Dt VIPW 8
+.Os BSD 4
+.Sh NAME
+.Nm vipw
+.Nd edit the password file
+.Sh SYNOPSIS
+.Nm vipw
+.Sh DESCRIPTION
+.Nm Vipw
+edits the password file after setting the appropriate locks,
+and does any necessary processing after the password file is unlocked.
+If the password file is already locked for editing by another user,
+.Nm
+will ask you
+to try again later. The default editor for
+.Nm
+is
+.Xr vi 1 .
+.Pp
+.Nm Vipw
+performs a number of consistency checks on the password entries,
+and will not allow a password file with a
+.Dq mangled
+entry to be
+installed.
+If
+.Nm
+rejects the new password file, the user is prompted to re-enter
+the edit session.
+.Pp
+Once the information has been verified,
+.Nm
+uses
+.Xr pwd_mkdb 8
+to update the user database. This is run in the background, and,
+at very large sites could take several minutes. Until this update
+is completed, the password file is unavailable for other updates
+and the new information is not available to programs.
+.Sh ENVIRONMENT
+If the following environment variable exists it will be utilized by
+.Nm Ns :
+.Bl -tag -width EDITOR
+.It Ev EDITOR
+The editor specified by the string
+.Ev EDITOR
+will be invoked instead of the default editor
+.Xr vi 1 .
+.El
+.Sh SEE ALSO
+.Xr chpass 1 ,
+.Xr passwd 1 ,
+.Xr passwd 5 ,
+.Xr adduser 8 ,
+.Xr pwd_mkdb 8
+.Sh HISTORY
+The
+.Nm
+command appeared in
+.Bx 4.0 .
diff --git a/usr.sbin/vipw/vipw.c b/usr.sbin/vipw/vipw.c
new file mode 100644
index 0000000..30b789e
--- /dev/null
+++ b/usr.sbin/vipw/vipw.c
@@ -0,0 +1,131 @@
+/*
+ * Copyright (c) 1987, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char copyright[] =
+"@(#) Copyright (c) 1987, 1993, 1994\n\
+ The Regents of the University of California. All rights reserved.\n";
+#endif /* not lint */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)vipw.c 8.3 (Berkeley) 4/2/94";
+#endif
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <err.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "pw_util.h"
+
+char *tempname;
+
+void copyfile __P((int, int));
+static void usage __P((void));
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int pfd, tfd;
+ struct stat begin, end;
+ int ch;
+
+ while ((ch = getopt(argc, argv, "")) != -1)
+ switch (ch) {
+ case '?':
+ default:
+ usage();
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (argc != 0)
+ usage();
+
+ pw_init();
+ pfd = pw_lock();
+ tfd = pw_tmp();
+ copyfile(pfd, tfd);
+ (void)close(tfd);
+
+ for (;;) {
+ if (stat(tempname, &begin))
+ pw_error(tempname, 1, 1);
+ pw_edit(0);
+ if (stat(tempname, &end))
+ pw_error(tempname, 1, 1);
+ if (begin.st_mtime == end.st_mtime) {
+ warnx("no changes made");
+ pw_error((char *)NULL, 0, 0);
+ }
+ if (pw_mkdb((char *)NULL))
+ break;
+ pw_prompt();
+ }
+ exit(0);
+}
+
+void
+copyfile(from, to)
+ int from, to;
+{
+ int nr, nw, off;
+ char buf[8*1024];
+
+ while ((nr = read(from, buf, sizeof(buf))) > 0)
+ for (off = 0; off < nr; nr -= nw, off += nw)
+ if ((nw = write(to, buf + off, nr)) < 0)
+ pw_error(tempname, 1, 1);
+ if (nr < 0)
+ pw_error(_PATH_MASTERPASSWD, 1, 1);
+}
+
+static void
+usage()
+{
+
+ (void)fprintf(stderr, "usage: vipw\n");
+ exit(1);
+}
diff --git a/usr.sbin/vnconfig/Makefile b/usr.sbin/vnconfig/Makefile
new file mode 100644
index 0000000..eba48cb
--- /dev/null
+++ b/usr.sbin/vnconfig/Makefile
@@ -0,0 +1,7 @@
+# $Id$
+
+PROG= vnconfig
+MAN8= vnconfig.8
+MLINKS= vnconfig.8 swapfile.8
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/vnconfig/vnconfig.8 b/usr.sbin/vnconfig/vnconfig.8
new file mode 100644
index 0000000..6b296f8
--- /dev/null
+++ b/usr.sbin/vnconfig/vnconfig.8
@@ -0,0 +1,180 @@
+.\" Copyright (c) 1993 University of Utah.
+.\" Copyright (c) 1980, 1989, 1991, 1993
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" This code is derived from software contributed to Berkeley by
+.\" the Systems Programming Group of the University of Utah Computer
+.\" Science Department.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" @(#)vnconfig.8 8.1 (Berkeley) 6/5/93
+.\"
+.Dd July 8, 1993
+.Dt VNCONFIG 8
+.Os BSD 4
+.Sh NAME
+.Nm vnconfig
+.Nd configure and enable vnode disks
+.Sh SYNOPSIS
+.Nm vnconfig
+.Op Fl cdeguv
+.Op Fl s Ar option
+.Op Fl r Ar option
+.Ar special_file Ar regular_file
+.Op Ar feature
+.Nm vnconfig
+.Fl a
+.Op Fl cdeguv
+.Op Fl s Ar option
+.Op Fl r Ar option
+.Op Fl f Ar config_file
+.Sh DESCRIPTION
+The
+.Nm
+command configures and enables vnode pseudo disk devices.
+The first form of the command will associate the special file
+.Ar special_file
+with the regular file
+.Ar regular_file
+allowing the latter to be accessed as though it were a disk.
+Hence a regular file within the filesystem can be used for swapping
+or can contain a filesystem that is mounted in the name space.
+.Pp
+Options indicate an action to be performed:
+.Bl -tag -width indent
+.It Fl a
+Read a command file and performs the
+specified actions for each device/file pair.
+.It Fl c
+Configure the device.
+If successful, references to
+.Ar special_file
+will access the contents of
+.Ar regular_file .
+.It Fl d
+Disable (if possible) the specified feature.
+.It Fl e
+Configure the device and enables any
+.Ar feature
+that was specified.
+If no feature was specified,
+.Fl e
+is the same as
+.Fl c .
+.It Fl f Ar config_file
+Use
+.Ar config_file
+as an alternate config file.
+.It Fl g
+Fiddle global options.
+.It Fl r Ar option
+Reset
+.Ar option .
+.Ar Option
+is one of labels, follow, debug, io, all, none.
+.It Fl s Ar option
+Set
+.Ar option .
+.It Fl u
+Disable and ``unconfigure'' the device.
+.It Fl v
+Print messages to stdout describing actions taken.
+.El
+.Pp
+If no action option is given,
+.Fl c
+is assumed.
+.Pp
+The
+.Ar feature
+argument specifies a feature that can be enabled via the
+.Fl e
+option:
+.Bl -tag -width indent
+.It Dv swap
+Swapping is enabled on the special file.
+See
+.Xr swapon 2 .
+.It Dv Pf mountro= Pa mount_point
+The special file is mounted read-only on
+.Ar mount_point .
+See
+.Xr mount 2 .
+.It Dv Pf mountrw= Pa mount_point
+The special file is mounted read-write on
+.Ar mount_point .
+See
+.Xr mount 2 .
+.It Dv Pf mount= Pa mount_point
+Same as ``mountrw=''.
+.El
+.Pp
+A configuration file contains one line per device/file pair in the form:
+.Bd -literal
+ special_file regular_file [ feature ]
+.Ed
+.Pp
+where fields are seperated by white space.
+The previously described action options serve to configure, enable,
+disable or unconfigure all devices in the configuration file.
+.Sh FILES
+.Bl -tag -width /etc/vntab -compact
+.It Pa /etc/vntab
+default configuration file for
+.Fl a
+option.
+.El
+.Sh EXAMPLES
+.Pp
+.Dl vnconfig /dev/vn0c /tmp/diskimage
+.Pp
+Configures the vnode disk
+.Pa vn0c .
+.Pp
+.Dl vnconfig -e /dev/vn0c /var/swapfile swap
+.Pp
+Configures
+.Pa vn0c
+and enables swapping on it.
+.Pp
+.Dl vnconfig -d /dev/vn0c myfilesystem mount=/mnt
+.Pp
+Unmounts (disables)
+.Pa vn0c .
+.Pp
+.Dl vnconfig -ae
+.Pp
+Configures and enables all devices specified in
+.Pa /etc/vntab .
+.Sh SEE ALSO
+.Xr mount 2 ,
+.Xr swapon 2 ,
+.Xr unmount 2 ,
+.Xr vn 4 .
diff --git a/usr.sbin/vnconfig/vnconfig.c b/usr.sbin/vnconfig/vnconfig.c
new file mode 100644
index 0000000..de84988
--- /dev/null
+++ b/usr.sbin/vnconfig/vnconfig.c
@@ -0,0 +1,449 @@
+/*
+ * Copyright (c) 1993 University of Utah.
+ * Copyright (c) 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * the Systems Programming Group of the University of Utah Computer
+ * Science Department.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * from: Utah $Hdr: vnconfig.c 1.1 93/12/15$
+ */
+
+#ifndef lint
+#if 0
+static char sccsid[] = "@(#)vnconfig.c 8.1 (Berkeley) 12/15/93";
+#endif
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#include <err.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/param.h>
+#include <sys/ioctl.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+#include <sys/vnioctl.h>
+#include <ufs/ufs/ufsmount.h>
+
+#define MAXVNDISK 16
+#define LINESIZE 1024
+
+struct vndisk {
+ char *dev;
+ char *file;
+ int flags;
+ char *oarg;
+} vndisks[MAXVNDISK];
+
+#define VN_CONFIG 0x01
+#define VN_UNCONFIG 0x02
+#define VN_ENABLE 0x04
+#define VN_DISABLE 0x08
+#define VN_SWAP 0x10
+#define VN_MOUNTRO 0x20
+#define VN_MOUNTRW 0x40
+#define VN_IGNORE 0x80
+#define VN_SET 0x100
+#define VN_RESET 0x200
+
+int nvndisks;
+
+int all = 0;
+int verbose = 0;
+int global = 0;
+u_long setopt = 0;
+u_long resetopt = 0;
+char *configfile;
+
+int config __P((struct vndisk *));
+void getoptions __P((struct vndisk *, char *));
+char *rawdevice __P((char *));
+void readconfig __P((int));
+static void usage __P((void));
+int what_opt __P((char *, u_long *));
+
+int
+main(argc, argv)
+ char **argv;
+{
+ register int i, rv;
+ int flags = 0;
+
+ configfile = _PATH_VNTAB;
+ while ((i = getopt(argc, argv, "acdef:gr:s:uv")) != -1)
+ switch (i) {
+
+ /* all -- use config file */
+ case 'a':
+ all++;
+ break;
+
+ /* configure */
+ case 'c':
+ flags |= VN_CONFIG;
+ flags &= ~VN_UNCONFIG;
+ break;
+
+ /* disable */
+ case 'd':
+ flags |= VN_DISABLE;
+ flags &= ~VN_ENABLE;
+ break;
+
+ /* enable */
+ case 'e':
+ flags |= (VN_ENABLE|VN_CONFIG);
+ flags &= ~(VN_DISABLE|VN_UNCONFIG);
+ break;
+
+ /* alternate config file */
+ case 'f':
+ configfile = optarg;
+ break;
+
+ /* fiddle global options */
+ case 'g':
+ global = 1 - global;
+ break;
+
+ /* reset options */
+ case 'r':
+ if (what_opt(optarg,&resetopt))
+ errx(1, "invalid options '%s'", optarg);
+ flags |= VN_RESET;
+ break;
+
+ /* set options */
+ case 's':
+ if (what_opt(optarg,&setopt))
+ errx(1, "invalid options '%s'", optarg);
+ flags |= VN_SET;
+ break;
+
+ /* unconfigure */
+ case 'u':
+ flags |= (VN_DISABLE|VN_UNCONFIG);
+ flags &= ~(VN_ENABLE|VN_CONFIG);
+ break;
+
+ /* verbose */
+ case 'v':
+ verbose++;
+ break;
+
+ default:
+ usage();
+ }
+
+ if (flags == 0)
+ flags = VN_CONFIG;
+ if (all)
+ readconfig(flags);
+ else {
+ if (argc < optind + 1)
+ usage();
+ vndisks[0].dev = argv[optind++];
+ vndisks[0].file = argv[optind++];
+ vndisks[0].flags = flags;
+ if (optind < argc)
+ getoptions(&vndisks[0], argv[optind]);
+ nvndisks = 1;
+ }
+ rv = 0;
+ for (i = 0; i < nvndisks; i++)
+ rv += config(&vndisks[i]);
+ exit(rv);
+}
+
+int
+what_opt(str,p)
+ char *str;
+ u_long *p;
+{
+ if (!strcmp(str,"labels")) { *p |= VN_LABELS; return 0; }
+ if (!strcmp(str,"follow")) { *p |= VN_FOLLOW; return 0; }
+ if (!strcmp(str,"debug")) { *p |= VN_DEBUG; return 0; }
+ if (!strcmp(str,"io")) { *p |= VN_IO; return 0; }
+ if (!strcmp(str,"all")) { *p |= ~0; return 0; }
+ if (!strcmp(str,"none")) { *p |= 0; return 0; }
+ return 1;
+}
+
+int
+config(vnp)
+ struct vndisk *vnp;
+{
+ char *dev, *file, *oarg;
+ int flags;
+ struct vn_ioctl vnio;
+ register int rv;
+ char *rdev;
+ FILE *f;
+ u_long l;
+
+ dev = vnp->dev;
+ file = vnp->file;
+ flags = vnp->flags;
+ oarg = vnp->oarg;
+
+ if (flags & VN_IGNORE)
+ return(0);
+
+ rdev = rawdevice(dev);
+ f = fopen(rdev, "rw");
+ if (f == NULL) {
+ warn("open");
+ return(1);
+ }
+ vnio.vn_file = file;
+
+ /*
+ * Disable the device
+ */
+ if (flags & VN_DISABLE) {
+ if (flags & (VN_MOUNTRO|VN_MOUNTRW)) {
+ rv = unmount(oarg, 0);
+ if (rv) {
+ if (errno == EBUSY)
+ flags &= ~VN_UNCONFIG;
+ if ((flags & VN_UNCONFIG) == 0)
+ warn("umount");
+ } else if (verbose)
+ printf("%s: unmounted\n", dev);
+ }
+ }
+ /*
+ * Clear (un-configure) the device
+ */
+ if (flags & VN_UNCONFIG) {
+ rv = ioctl(fileno(f), VNIOCDETACH, &vnio);
+ if (rv) {
+ if (errno == ENODEV) {
+ if (verbose)
+ printf("%s: not configured\n", dev);
+ rv = 0;
+ } else
+ warn("VNIOCDETACH");
+ } else if (verbose)
+ printf("%s: cleared\n", dev);
+ }
+ /*
+ * Configure the device
+ */
+ if (flags & VN_CONFIG) {
+ rv = ioctl(fileno(f), VNIOCATTACH, &vnio);
+ if (rv) {
+ warn("VNIOCATTACH");
+ flags &= ~VN_ENABLE;
+ } else if (verbose)
+ printf("%s: %d bytes on %s\n",
+ dev, vnio.vn_size, file);
+ }
+ /*
+ * Set an option
+ */
+ if (flags & VN_SET) {
+ l = setopt;
+ if (global)
+ rv = ioctl(fileno(f), VNIOCGSET, &l);
+ else
+ rv = ioctl(fileno(f), VNIOCUSET, &l);
+ if (rv) {
+ warn("VNIO[GU]SET");
+ } else if (verbose)
+ printf("%s: flags now=%08x\n",dev,l);
+ }
+ /*
+ * Reset an option
+ */
+ if (flags & VN_RESET) {
+ l = resetopt;
+ if (global)
+ rv = ioctl(fileno(f), VNIOCGCLEAR, &l);
+ else
+ rv = ioctl(fileno(f), VNIOCUCLEAR, &l);
+ if (rv) {
+ warn("VNIO[GU]CLEAR");
+ } else if (verbose)
+ printf("%s: flags now=%08x\n",dev,l);
+ }
+
+ /*
+ * Enable special functions on the device
+ */
+ if (flags & VN_ENABLE) {
+ if (flags & VN_SWAP) {
+ rv = swapon(dev);
+ if (rv)
+ warn("swapon");
+ else if (verbose)
+ printf("%s: swapping enabled\n", dev);
+ }
+ if (flags & (VN_MOUNTRO|VN_MOUNTRW)) {
+ struct ufs_args args;
+ int mflags;
+
+ args.fspec = dev;
+ mflags = (flags & VN_MOUNTRO) ? MNT_RDONLY : 0;
+ rv = mount("ufs", oarg, mflags, &args);
+ if (rv)
+ warn("mount");
+ else if (verbose)
+ printf("%s: mounted on %s\n", dev, oarg);
+ }
+ }
+/* done: */
+ fclose(f);
+ fflush(stdout);
+ return(rv < 0);
+}
+
+#define EOL(c) ((c) == '\0' || (c) == '\n')
+#define WHITE(c) ((c) == ' ' || (c) == '\t' || (c) == '\n')
+
+void
+readconfig(flags)
+ int flags;
+{
+ char buf[LINESIZE];
+ FILE *f;
+ register char *cp, *sp;
+ register int ix;
+
+ f = fopen(configfile, "r");
+ if (f == NULL)
+ err(1, "%s", configfile);
+ ix = 0;
+ while (fgets(buf, LINESIZE, f) != NULL) {
+ cp = buf;
+ if (*cp == '#')
+ continue;
+ while (!EOL(*cp) && WHITE(*cp))
+ cp++;
+ if (EOL(*cp))
+ continue;
+ sp = cp;
+ while (!EOL(*cp) && !WHITE(*cp))
+ cp++;
+ if (EOL(*cp))
+ continue;
+ *cp++ = '\0';
+ vndisks[ix].dev = malloc(cp - sp);
+ strcpy(vndisks[ix].dev, sp);
+ while (!EOL(*cp) && WHITE(*cp))
+ cp++;
+ if (EOL(*cp))
+ continue;
+ sp = cp;
+ while (!EOL(*cp) && !WHITE(*cp))
+ cp++;
+ *cp++ = '\0';
+ vndisks[ix].file = malloc(cp - sp);
+ strcpy(vndisks[ix].file, sp);
+ while (!EOL(*cp) && WHITE(*cp))
+ cp++;
+ vndisks[ix].flags = flags;
+ if (!EOL(*cp)) {
+ sp = cp;
+ while (!EOL(*cp) && !WHITE(*cp))
+ cp++;
+ *cp++ = '\0';
+ getoptions(&vndisks[ix], sp);
+ }
+ nvndisks++;
+ ix++;
+ }
+}
+
+void
+getoptions(vnp, fstr)
+ struct vndisk *vnp;
+ char *fstr;
+{
+ int flags = 0;
+ char *oarg = NULL;
+
+ if (strcmp(fstr, "swap") == 0)
+ flags |= VN_SWAP;
+ else if (strncmp(fstr, "mount=", 6) == 0) {
+ flags |= VN_MOUNTRW;
+ oarg = &fstr[6];
+ } else if (strncmp(fstr, "mountrw=", 8) == 0) {
+ flags |= VN_MOUNTRW;
+ oarg = &fstr[8];
+ } else if (strncmp(fstr, "mountro=", 8) == 0) {
+ flags |= VN_MOUNTRO;
+ oarg = &fstr[8];
+ } else if (strcmp(fstr, "ignore") == 0)
+ flags |= VN_IGNORE;
+ vnp->flags |= flags;
+ if (oarg) {
+ vnp->oarg = malloc(strlen(oarg) + 1);
+ strcpy(vnp->oarg, oarg);
+ } else
+ vnp->oarg = NULL;
+}
+
+char *
+rawdevice(dev)
+ char *dev;
+{
+ register char *rawbuf, *dp, *ep;
+ struct stat sb;
+ int len;
+
+ len = strlen(dev);
+ rawbuf = malloc(len + 2);
+ strcpy(rawbuf, dev);
+ if (stat(rawbuf, &sb) != 0 || !S_ISCHR(sb.st_mode)) {
+ dp = rindex(rawbuf, '/');
+ if (dp) {
+ for (ep = &rawbuf[len]; ep > dp; --ep)
+ *(ep+1) = *ep;
+ *++ep = 'r';
+ }
+ }
+ return (rawbuf);
+}
+
+static void
+usage()
+{
+ fprintf(stderr,
+"usage: vnconfig [-acdefguv] [-s option] [-r option] [special-device file]\n");
+ exit(1);
+}
diff --git a/usr.sbin/watch/Makefile b/usr.sbin/watch/Makefile
new file mode 100644
index 0000000..12e44fc
--- /dev/null
+++ b/usr.sbin/watch/Makefile
@@ -0,0 +1,9 @@
+# $Id$
+
+PROG= watch
+MAN8= watch.8
+BINMODE=500
+LDADD+= -ltermcap
+DPADD+= ${LIBTERMCAP}
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/watch/watch.8 b/usr.sbin/watch/watch.8
new file mode 100644
index 0000000..ae7ba5c6
--- /dev/null
+++ b/usr.sbin/watch/watch.8
@@ -0,0 +1,86 @@
+.\"
+.\" @(#)watch.8 1.1 (FreeBSD) 2/17/95
+.\"
+.Dd February 17, 1995
+.Dt WATCH 8
+.Os
+.Sh NAME
+.Nm watch
+.Nd snoop on another tty line
+.Sh SYNOPSIS
+.Nm watch
+.Op Fl ciotnW
+.Ar tty
+.\" watch [-ciotnW] [<tty name>]
+.Sh DESCRIPTION
+.Nm Watch
+allows the superuser to examine all data coming through a specified tty.
+.Nm Watch
+writes to standard output.
+.Pp
+The options are as follows:
+.Bl -tag -width indent
+.It Fl c
+Reconnect on close. If the tty observed by
+.Nm
+is closed, automatically reattach to the same tty.
+If this option is not specified,
+.Nm
+will request a new tty if running in interactive mode or exit if running
+without a controlling tty.
+.It Fl i
+Force interactive mode.
+Interactive mode is a default if
+.Nm
+is started from a tty.
+If output is redirected to a file, interactive mode can still be requested
+by specifying this option.
+.It Fl o
+Reconnect on overflow.
+The behavior of
+.Nm
+if the observed tty overflows is similar to the behavior if the observed tty
+is closed.
+For more info see
+.Xr snp 4 .
+.It Fl t
+Print the date and time when observation of a given tty is started.
+.It Fl n
+Disable the ability to switch the watched tty interactively. This disables
+both change requests made with <control-X> as well as automatic prompting
+when the current tty is closed or overflows. In all cases where a prompt
+would be displayed, watch will exit. The reconnect flags are unaffected by
+this option.
+.It Fl W
+Allow write access to observed tty.
+.It Ar tty
+Tty may be specified as an tty-style device, such as a pseudo tty device,
+a virtual console, or a serial line, etc.
+Names may be preceded by "/dev/".
+.Sh OPERATION
+While running in interactive mode, all user input is discarded except for:
+.Pp
+.Bl -tag -width "XXXX" -compact
+.It Sy "<control-G>"
+Exit
+.Nm Ns .
+.It Sy "<control-W>"
+Clear screen.
+.It Sy "<control-X>"
+Change attached tty.
+.Sh RESTRICTIONS
+Only the superuser can run
+.Nm Ns .
+.Sh SEE ALSO
+.Xr pty 4 ,
+.Xr sio 4 ,
+.Xr snp 4
+.Sh BUGS
+No terminal emulation is performed.
+All user output is reproduced as-is.
+.Sh AUTHOR
+.An Ugen J.S. Antsilevich Aq ugen@NetVision.net.il
+.Sh HISTORY
+.Nm Watch
+first appeared in
+.Fx 2.1 .
diff --git a/usr.sbin/watch/watch.c b/usr.sbin/watch/watch.c
new file mode 100644
index 0000000..9277efb
--- /dev/null
+++ b/usr.sbin/watch/watch.c
@@ -0,0 +1,430 @@
+/*
+ * Copyright (c) 1995 Ugen J.S.Antsilevich
+ *
+ * Redistribution and use in source forms, with and without modification,
+ * are permitted provided that this entire comment appears intact.
+ *
+ * Redistribution in binary form may occur without any restrictions.
+ * Obviously, it would be nice if you gave credit where credit is due
+ * but requiring it would be too onerous.
+ *
+ * This software is provided ``AS IS'' without any warranties of any kind.
+ *
+ * Snoop stuff.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#include <sys/types.h>
+#include <sys/fcntl.h>
+#include <sys/filio.h>
+#include <sys/snoop.h>
+#include <sys/stat.h>
+
+#include <err.h>
+#include <locale.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <termcap.h>
+#include <termios.h>
+#include <unistd.h>
+
+#define MSG_INIT "Snoop started."
+#define MSG_OFLOW "Snoop stopped due to overflow. Reconnecting."
+#define MSG_CLOSED "Snoop stopped due to tty close. Reconnecting."
+#define MSG_CHANGE "Snoop device change by user request."
+#define MSG_NOWRITE "Snoop device change due to write failure."
+
+
+#define DEV_NAME_LEN 1024 /* for /dev/ttyXX++ */
+#define MIN_SIZE 256
+
+#define CHR_SWITCH 24 /* Ctrl+X */
+#define CHR_CLEAR 23 /* Ctrl+V */
+
+
+int opt_reconn_close = 0;
+int opt_reconn_oflow = 0;
+int opt_interactive = 1;
+int opt_timestamp = 0;
+int opt_write = 0;
+int opt_no_switch = 0;
+
+char dev_name[DEV_NAME_LEN];
+int snp_io;
+dev_t snp_tty;
+int std_in = 0, std_out = 1;
+
+
+int clear_ok = 0;
+struct termios otty;
+char tbuf[1024], buf[1024];
+
+
+void
+clear()
+{
+ if (clear_ok)
+ tputs(buf, 1, putchar);
+ fflush(stdout);
+}
+
+void
+timestamp(buf)
+ char *buf;
+{
+ time_t t;
+ char btmp[1024];
+ clear();
+ printf("\n---------------------------------------------\n");
+ t = time(NULL);
+ strftime(btmp, 1024, "Time: %d %b %H:%M", localtime(&t));
+ printf("%s\n", btmp);
+ printf("%s\n", buf);
+ printf("---------------------------------------------\n");
+ fflush(stdout);
+}
+
+void
+set_tty()
+{
+ struct termios ntty;
+
+ tcgetattr (std_in, &otty);
+ ntty = otty;
+ ntty.c_lflag &= ~ICANON; /* disable canonical operation */
+ ntty.c_lflag &= ~ECHO;
+#ifdef FLUSHO
+ ntty.c_lflag &= ~FLUSHO;
+#endif
+#ifdef PENDIN
+ ntty.c_lflag &= ~PENDIN;
+#endif
+#ifdef IEXTEN
+ ntty.c_lflag &= ~IEXTEN;
+#endif
+ ntty.c_cc[VMIN] = 1; /* minimum of one character */
+ ntty.c_cc[VTIME] = 0; /* timeout value */
+
+ ntty.c_cc[VINTR] = 07; /* ^G */
+ ntty.c_cc[VQUIT] = 07; /* ^G */
+ tcsetattr (std_in, TCSANOW, &ntty);
+}
+
+void
+unset_tty()
+{
+ tcsetattr (std_in, TCSANOW, &otty);
+}
+
+
+void
+fatal(err, buf)
+ unsigned int err;
+ char *buf;
+{
+ unset_tty();
+ if (buf)
+ errx(err, "fatal: %s", buf);
+ else
+ exit(err);
+}
+
+int
+open_snp()
+{
+ char snp[] = {"/dev/snpX"};
+ char c;
+ int f, mode;
+
+ if (opt_write)
+ mode = O_RDWR;
+ else
+ mode = O_RDONLY;
+
+ for (c = '0'; c <= '9'; c++) {
+ snp[8] = c;
+ if ((f = open(snp, mode)) < 0)
+ continue;
+ return f;
+ }
+ fatal(EX_OSFILE, "cannot open snoop device");
+ return (0);
+}
+
+
+void
+cleanup()
+{
+ if (opt_timestamp)
+ timestamp("Logging Exited.");
+ close(snp_io);
+ unset_tty();
+ exit(EX_OK);
+}
+
+
+static void
+usage()
+{
+ fprintf(stderr, "usage: watch [-ciotnW] [tty name]\n");
+ exit(EX_USAGE);
+}
+
+void
+setup_scr()
+{
+ char *cbuf = buf, *term;
+ if (!opt_interactive)
+ return;
+ if ((term = getenv("TERM")))
+ if (tgetent(tbuf, term) == 1)
+ if (tgetstr("cl", &cbuf))
+ clear_ok = 1;
+ set_tty();
+ clear();
+}
+
+
+int
+ctoh(c)
+ char c;
+{
+ if (c >= '0' && c <= '9')
+ return (int) (c - '0');
+
+ if (c >= 'a' && c <= 'f')
+ return (int) (c - 'a' + 10);
+
+ fatal(EX_DATAERR, "bad tty number");
+ return (0);
+}
+
+
+void
+detach_snp()
+{
+ dev_t dev;
+
+ dev = -1;
+ ioctl(snp_io, SNPSTTY, &dev);
+}
+
+void
+attach_snp()
+{
+ if (ioctl(snp_io, SNPSTTY, &snp_tty) != 0)
+ fatal(EX_UNAVAILABLE, "cannot attach to tty");
+ if (opt_timestamp)
+ timestamp("Logging Started.");
+}
+
+
+void
+set_dev(name)
+ char *name;
+{
+ char buf[DEV_NAME_LEN];
+ struct stat sb;
+
+ if (strlen(name) > 5 && !strncmp(name, "/dev/", 5)) {
+ snprintf(buf, sizeof buf, "%s", name);
+ }
+ else {
+ if (strlen(name) == 2)
+ sprintf(buf, "/dev/tty%s", name);
+ else
+ sprintf(buf, "/dev/%s", name);
+ }
+
+ if (*name == '\0' || stat(buf, &sb) < 0)
+ fatal(EX_DATAERR, "bad device name");
+
+ if ((sb.st_mode & S_IFMT) != S_IFCHR)
+ fatal(EX_DATAERR, "must be a character device");
+
+ snp_tty = sb.st_rdev;
+ attach_snp();
+}
+
+void
+ask_dev(dev_name, msg)
+ char *dev_name, *msg;
+{
+ char buf[DEV_NAME_LEN];
+ int len;
+
+ clear();
+ unset_tty();
+
+ if (msg)
+ printf("%s\n", msg);
+ if (dev_name)
+ printf("Enter device name [%s]:", dev_name);
+ else
+ printf("Enter device name:");
+
+ if (fgets(buf, DEV_NAME_LEN - 1, stdin)) {
+ len = strlen(buf);
+ if (buf[len - 1] == '\n')
+ buf[len - 1] = '\0';
+ if (buf[0] != '\0' && buf[0] != ' ')
+ strcpy(dev_name, buf);
+ }
+ set_tty();
+}
+
+#define READB_LEN 5
+
+void
+main(ac, av)
+ int ac;
+ char **av;
+{
+ int res, nread, b_size = MIN_SIZE;
+ extern int optind;
+ char ch, *buf, chb[READB_LEN];
+ fd_set fd_s;
+
+ (void) setlocale(LC_TIME, "");
+
+ if (isatty(std_out))
+ opt_interactive = 1;
+ else
+ opt_interactive = 0;
+
+
+ while ((ch = getopt(ac, av, "Wciotn")) != -1)
+ switch (ch) {
+ case 'W':
+ opt_write = 1;
+ break;
+ case 'c':
+ opt_reconn_close = 1;
+ break;
+ case 'i':
+ opt_interactive = 1;
+ break;
+ case 'o':
+ opt_reconn_oflow = 1;
+ break;
+ case 't':
+ opt_timestamp = 1;
+ break;
+ case 'n':
+ opt_no_switch = 1;
+ break;
+ case '?':
+ default:
+ usage();
+ }
+
+ signal(SIGINT, cleanup);
+
+ setup_scr();
+ snp_io = open_snp();
+
+ if (*(av += optind) == NULL) {
+ if (opt_interactive && !opt_no_switch)
+ ask_dev(dev_name, MSG_INIT);
+ else
+ fatal(EX_DATAERR, "no device name given");
+ } else
+ strncpy(dev_name, *av, DEV_NAME_LEN);
+
+ set_dev(dev_name);
+
+ if (!(buf = (char *) malloc(b_size)))
+ fatal(EX_UNAVAILABLE, "malloc failed");
+
+ FD_ZERO(&fd_s);
+
+ while (1) {
+ if (opt_interactive)
+ FD_SET(std_in, &fd_s);
+ FD_SET(snp_io, &fd_s);
+ res = select(snp_io + 1, &fd_s, NULL, NULL, NULL);
+ if (opt_interactive && FD_ISSET(std_in, &fd_s)) {
+
+ if ((res = ioctl(std_in, FIONREAD, &nread)) != 0)
+ fatal(EX_OSERR, "ioctl(FIONREAD)");
+ if (nread > READB_LEN)
+ nread = READB_LEN;
+ if (read(std_in,chb,nread)!=nread)
+ fatal(EX_IOERR, "read (stdin) failed");
+
+ switch (chb[0]) {
+ case CHR_CLEAR:
+ clear();
+ break;
+ case CHR_SWITCH:
+ if (opt_no_switch)
+ break;
+ detach_snp();
+ ask_dev(dev_name, MSG_CHANGE);
+ set_dev(dev_name);
+ break;
+ default:
+ if (opt_write) {
+ if (write(snp_io,chb,nread) != nread) {
+ detach_snp();
+ if (opt_no_switch)
+ fatal(EX_IOERR, "write failed");
+ ask_dev(dev_name, MSG_NOWRITE);
+ set_dev(dev_name);
+ }
+ }
+
+ }
+ }
+ if (!FD_ISSET(snp_io, &fd_s))
+ continue;
+
+ if ((res = ioctl(snp_io, FIONREAD, &nread)) != 0)
+ fatal(EX_OSERR, "ioctl(FIONREAD)");
+
+ switch (nread) {
+ case SNP_OFLOW:
+ if (opt_reconn_oflow)
+ attach_snp();
+ else if (opt_interactive && !opt_no_switch) {
+ ask_dev(dev_name, MSG_OFLOW);
+ set_dev(dev_name);
+ } else
+ cleanup();
+ case SNP_DETACH:
+ case SNP_TTYCLOSE:
+ if (opt_reconn_close)
+ attach_snp();
+ else if (opt_interactive && !opt_no_switch) {
+ ask_dev(dev_name, MSG_CLOSED);
+ set_dev(dev_name);
+ } else
+ cleanup();
+ default:
+ if (nread < (b_size / 2) && (b_size / 2) > MIN_SIZE) {
+ free(buf);
+ if (!(buf = (char *) malloc(b_size / 2)))
+ fatal(EX_UNAVAILABLE, "malloc failed");
+ b_size = b_size / 2;
+ }
+ if (nread > b_size) {
+ b_size = (nread % 2) ? (nread + 1) : (nread);
+ free(buf);
+ if (!(buf = (char *) malloc(b_size)))
+ fatal(EX_UNAVAILABLE, "malloc failed");
+ }
+ if (read(snp_io, buf, nread) < nread)
+ fatal(EX_IOERR, "read failed");
+ if (write(std_out, buf, nread) < nread)
+ fatal(EX_IOERR, "write failed");
+ }
+ } /* While */
+}
+
diff --git a/usr.sbin/wlconfig/Makefile b/usr.sbin/wlconfig/Makefile
new file mode 100644
index 0000000..8a50cc8
--- /dev/null
+++ b/usr.sbin/wlconfig/Makefile
@@ -0,0 +1,7 @@
+# $Id: Makefile,v 1.1.1.1 1997/05/22 08:58:18 msmith Exp $
+PROG= wlconfig
+SRCS= wlconfig.c
+CFLAGS+= -Wall
+MAN8= 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..21b68a0
--- /dev/null
+++ b/usr.sbin/wlconfig/wlconfig.8
@@ -0,0 +1,133 @@
+.Dd December 26 1996
+.Os
+.Dt WLCONFIG 8
+.Sh NAME
+.Nm wlconfig
+.Nd read/write wavelan config parameters
+.Sh SYNOPSIS
+.Nm wlconfig
+.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 nonvolatile Parameter
+Storage Area (PSA) on the card can be modified with this program, which
+obviates the need for the DOS-based
+.Nm instconf.exe
+program. 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 -compat -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
+independantly whilse 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-volative memory
+(called the PSA or programmable storage area), and are loaded by
+software into the radio modem when the driver is
+initialized. This sets the default NWID loaded at startup.
+.It curnwid
+This sets the current operating NWID (but does not save it to the
+PSA).
+.It cache
+The driver may maintain a per interface fixed size cache of signal strength,
+silence, and quality levels, which are indexed by sender MAC addresses.
+Input packets are stored in the cache, and when received, the values
+stored in the radio modem are interrogated and stored.
+There are also two sysctl values (iponly and multicast only) which
+can be used for filtering out some input packets. By default, the
+cache mechanism stores only non-unicast IP packets, but this can
+be changed with sysctl(8). Each non-filtered
+input packet causes a cache update, hence one can monitor
+the antennae signal strength to a remote system.
+There are three commands that can be given as values:
+.Sq raw ,
+which prints out the raw signal strength data as found in the radio
+modem hardware value,
+.Sq scale ,
+which scales the raw hardware values to 0..100%, and
+.Sq zero
+which clears out the cache in case you want to store new samples.
+.El
+.Pp
+Note that if the IRQ on the Wavelan card is incorrect, the interface
+will be configured, but will not function. The
+.Nm
+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 compatability : 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
+.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..6d252b5
--- /dev/null
+++ b/usr.sbin/wlconfig/wlconfig.c
@@ -0,0 +1,419 @@
+/*
+ * 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[] =
+ "$Id$";
+#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 <net/if_var.h>
+#include <netinet/in.h>
+#include <netinet/if_ether.h>
+extern struct ether_addr *ether_aton(char *a);
+
+#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 compatability : ");
+ 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/wormcontrol/Makefile b/usr.sbin/wormcontrol/Makefile
new file mode 100644
index 0000000..c94d349
--- /dev/null
+++ b/usr.sbin/wormcontrol/Makefile
@@ -0,0 +1,4 @@
+PROG= wormcontrol
+MAN8= wormcontrol.8
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/wormcontrol/wormcontrol.8 b/usr.sbin/wormcontrol/wormcontrol.8
new file mode 100644
index 0000000..5b007de
--- /dev/null
+++ b/usr.sbin/wormcontrol/wormcontrol.8
@@ -0,0 +1,175 @@
+.\"
+.\" Copyright (C) 1996
+.\" interface business GmbH
+.\" Tolkewitzer Strasse 49
+.\" D-01277 Dresden
+.\" F.R. Germany
+.\"
+.\" All rights reserved.
+.\"
+.\" Written by Joerg Wunsch <joerg_wunsch@interface-business.de>
+.\"
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY
+.\" EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+.\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+.\" OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+.\" BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+.\" LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+.\" USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+.\" DAMAGE.
+.\"
+.\" $Id: wormcontrol.8,v 1.9 1997/06/23 04:52:12 steve Exp $
+.\"
+.\" arrgh, hilit19 needs this" :-(
+.Dd January 27, 1996
+.Os
+.Dt WORMCONTROL 8
+.Sh NAME
+.Nm wormcontrol
+.Nd control the CD-R driver
+.Sh SYNOPSIS
+.Nm wormcontrol
+.Op Fl f Ar device
+.Ar command
+.Op Ar params...
+.Sh DESCRIPTION
+The
+.Nm
+utility is used to control the behaviour of the
+.Xr worm 4
+driver in order to adjust various parameters of a recordable CD
+.Pq CD-R .
+.Pp
+Unlike many other devices, CD-R's require a very strict handling order.
+Prior to writing data, the speed of the drive must be selected, and
+the drive can also be turned into a
+.Ql dummy
+mode, where every action is only performed with the laser turned off.
+This way, it's possible to test whether the environment provides a
+sufficiently high data flow rate in order to actually burn the CD-R,
+without destroying the medium in case of a catastrophic failure.
+.Pp
+In order to write a new track, the drive must be told whether the new
+track will become an audio or a data track. Audio tracks are written
+with a block size of 2352 bytes, while data tracks have 2048 bytes per
+block. For audio tracks, the driver does further need to know
+whether the data are recorded with a preemphasis.
+.Pp
+Once all tracks of a session have been written, the disk must be
+.Em fixated .
+This writes the table of contents and leadout information to the disk.
+The disk won't be usable without doing this.
+.Pp
+The following options are available:
+.Bl -tag -width ident
+.It Fl f Ar device
+Use
+.Ar device
+instead of the default
+.Pa /dev/rworm0 .
+.It prepdisk Ar single \&| double Op Ar dummy
+Prepare the disk for recording. This must be done before any tracks
+can be prepared, and remains in effect until the session has been
+fixated. Either single speed
+.Pq for audio data
+or double speed
+.Pq for CD-ROM data
+must be selected, and optionally, the argument
+.Ar dummy
+can be used to tell the drive to keep the laser turned off, for testing.
+.It track Ar audio \&| data Op Ar preemp
+Inform the driver about the format of the next track. Either
+.Ar audio
+or
+.Ar data
+.Pq CD-ROM
+must be selected, with an optional argument
+.Ar preemp
+that must be specified for an audio track where data records with
+preemphasis are being used. Once this command has been successfully
+specified, the track is ready for being written.
+.It fixate Ar toc-type Op Ar onp
+Once all tracks have been written, this closes the current session.
+The argument
+.Ar toc-type
+is a single digit between 0 and 4, with the following meaning:
+.Bl -item
+.It
+0 CD audio
+.It
+1 CD-ROM
+.It
+2 CD-ROM with first track in mode 1
+.It
+3 CD-ROM with first track in mode 2
+.It
+4 CDI
+.El
+.Pp
+The optional argument
+.Ar onp
+stands for
+.Dq open next program area ,
+which means that the next session on the CD-R will be opened and can
+be recorded in the future. Otherwise, the CD-R will be closed and
+remains unchangeable.
+.El
+.Sh DIAGNOSTICS
+Error codes for the underlying
+.Xr ioctl 2
+commands are printed by the
+.Xr err 3
+facility.
+.Sh EXAMPLES
+The typical sequence of burning a data CD-R looks like:
+.Bd -literal
+# wormcontrol prepdisk double
+# wormcontrol track data
+# rtprio 5 team -v 1m 5 < cdrom.image | rtprio 5 dd of=/dev/rworm0 obs=20k
+# wormcontrol fixate 1
+.Ed
+.Pp
+Note that the
+.Xr dd 1
+command above is mainly used in order to
+.Dq slice
+the data stream. It's particularly useful when recording audio data
+with their rather unusual blocksize. Since the underlying device is a
+.Em raw
+device, the blocksize used in that command must be an integral multiple
+of the CD-R blocksize.
+.Pp
+The mentioned command
+.Xr team 1
+is not part of the base system, but comes extremely handy in order to
+pipe a constant data stream into the CD recorder.
+.Sh SEE ALSO
+.Xr dd 1 ,
+.Xr team 1 ,
+.Xr ioctl 2 ,
+.Xr err 3 ,
+.Xr worm 4
+.Pp
+.Pa /usr/share/examples/worm/*
+.Sh HISTORY
+.Nm Wormcontrol
+is currently under development.
+.Sh AUTHOR
+The program has been contributed by
+.ie t J\(:org Wunsch,
+.el Joerg Wunsch,
+Dresden.
diff --git a/usr.sbin/wormcontrol/wormcontrol.c b/usr.sbin/wormcontrol/wormcontrol.c
new file mode 100644
index 0000000..5f7aff2
--- /dev/null
+++ b/usr.sbin/wormcontrol/wormcontrol.c
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 1996
+ * interface business GmbH
+ * Tolkewitzer Strasse 49
+ * D-01277 Dresden
+ * F.R. Germany
+ *
+ * All rights reserved.
+ *
+ * Written by Joerg Wunsch <joerg_wunsch@interface-business.de>
+ *
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
+ * USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
+ * DAMAGE.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#include <err.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sysexits.h>
+#include <unistd.h>
+
+#include <sys/ioctl.h>
+#include <sys/wormio.h>
+
+static void
+usage()
+{
+ fprintf(stderr,"usage: wormcontrol [-f device] command [args]\n");
+ exit(EX_USAGE);
+}
+
+#define eq(a, b) (strcmp(a, b) == 0)
+
+int
+main(int argc, char **argv)
+{
+ int fd, c, i;
+ int errs = 0;
+ const char *devname = "/dev/rworm0";
+
+ while ((c = getopt(argc, argv, "f:")) != -1)
+ switch(c) {
+ case 'f':
+ devname = optarg;
+ break;
+
+ case '?':
+ default:
+ errs++;
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (errs || argc < 1)
+ usage();
+
+ if ((fd = open(devname, O_RDONLY | O_NONBLOCK, 0)) == -1)
+ err(EX_NOINPUT, "open(%s)", devname);
+
+ if (eq(argv[0], "prepdisk")) {
+ struct wormio_prepare_disk d;
+ d.dummy = 0;
+ d.speed = -1;
+ for (i = 1; i < argc; i++) {
+ if (eq(argv[i], "dummy"))
+ d.dummy = 1;
+ else if (eq(argv[i], "single"))
+ d.speed = 1;
+ else if (eq(argv[i], "double"))
+ d.speed = 2;
+ else
+ errx(EX_USAGE,
+ "wrong param for \"prepdisk\": %s",
+ argv[i]);
+ }
+ if (d.speed == -1)
+ errx(EX_USAGE, "missing speed parameter");
+ if (ioctl(fd, WORMIOCPREPDISK, &d) == -1)
+ err(EX_IOERR, "ioctl(WORMIOCPREPDISK)");
+ }
+ else if (eq(argv[0], "track")) {
+ struct wormio_prepare_track t;
+ bzero(&t, sizeof (t));
+ t.audio = -1;
+ t.preemp = 0;
+ for (i = 1; i < argc; i++) {
+ if (eq(argv[i], "audio"))
+ t.audio = 1;
+ else if (eq(argv[i], "data")) {
+ t.audio = 0;
+ t.track_type = BLOCK_MODE_1;
+ } else if (eq(argv[i], "preemp"))
+ t.preemp = 1;
+ else
+ errx(EX_USAGE,
+ "wrong param for \"track\": %s",
+ argv[i]);
+ }
+ if (t.audio == -1)
+ errx(EX_USAGE, "missing track type parameter");
+ if (t.audio == 0 && t.preemp == 1)
+ errx(EX_USAGE, "\"preemp\" attempted on data track");
+ if (ioctl(fd, WORMIOCPREPTRACK, &t) == -1)
+ err(EX_IOERR, "ioctl(WORMIOCPREPTRACK)");
+ }
+ else if (eq(argv[0], "fixate")) {
+ struct wormio_fixation f;
+ f.toc_type = -1;
+ f.onp = 0;
+ for (i = 1; i < argc; i++) {
+ if (eq(argv[i], "onp"))
+ f.onp = 1;
+ else if (argv[i][0] >= '0' && argv[i][0] <= '4' &&
+ argv[i][1] == '\0')
+ f.toc_type = argv[i][0] - '0';
+ else
+ errx(EX_USAGE,
+ "wrong param for \"fixate\": %s",
+ argv[i]);
+ }
+ if (f.toc_type == -1)
+ errx(EX_USAGE, "missing TOC type parameter");
+ if (ioctl(fd, WORMIOCFIXATION, &f) == -1)
+ err(EX_IOERR, "ioctl(WORMIOFIXATION)");
+ }
+ else
+ errx(EX_USAGE, "unknown command: %s", argv[0]);
+
+ return EX_OK;
+}
diff --git a/usr.sbin/xntpd/COPYRIGHT b/usr.sbin/xntpd/COPYRIGHT
new file mode 100644
index 0000000..b5d6282
--- /dev/null
+++ b/usr.sbin/xntpd/COPYRIGHT
@@ -0,0 +1,62 @@
+/******************************************************************************
+ * *
+ * Copyright (c) David L. Mills 1992, 1993, 1994 *
+ * *
+ * Permission to use, copy, modify, and distribute this software and its *
+ * documentation for any purpose and without fee is hereby granted, provided *
+ * that the above copyright notice appears in all copies and that both the *
+ * copyright notice and this permission notice appear in supporting *
+ * documentation, and that the name University of Delaware not be used in *
+ * advertising or publicity pertaining to distribution of the software *
+ * without specific, written prior permission. The University of Delaware *
+ * makes no representations about the suitability this software for any *
+ * purpose. It is provided "as is" without express or implied warranty. *
+ * *
+ ******************************************************************************/
+
+/*
+ * For all files included in this distribution and not specifically marked
+ * otherwise, the above copyright information applies.
+ *
+ * Authors
+ *
+ * Dennis Ferguson <dennis@mrbill.canet.ca> (foundation code for NTP
+ * Version 2 as specified in RFC-1119)
+ * Lars H. Mathiesen <thorinn@diku.dk> (adaptation of foundation code for
+ * Version 3 as specified in RFC-1305)
+ * Louis A. Mamakos <louie@ni.umd.edu> (support for md5-based
+ * authentication)
+ * Craig Leres <leres@ee.lbl.gov> (port to 4.4BSD operating system,
+ * ppsclock, Maganavox GPS clock driver)
+ * Nick Sayer <mrapple@quack.kfu.com> (SunOS streams modules)
+ * Frank Kardel <Frank.Kardel@informatik.uni-erlangen.de>(PARSE (GENERIC)
+ * driver, STREAMS module for PARSE, support scripts, reference clock
+ * configuration scripts, Makefile cleanup)
+ * Rainer Pruy <Rainer.Pruy@informatik.uni-erlangen.de> (monitoring/trap
+ * scripts, statistics file handling)
+ * Glenn Hollinger <glenn@herald.usask.ca> (GOES clock driver)
+ * Kenneth Stone <ken@sdd.hp.com> (port to HPUX operating system)
+ * Dave Katz <dkatz@cisco.com> (port to RS/6000 AIX operating system)
+ * William L. Jones <jones@hermes.chpc.utexas.edu> (RS/6000 AIX
+ * modifications, HPUX modifications)
+ * John A. Dundas III <dundas@salt.jpl.nasa.gov> (Apple A/UX port)
+ * David L. Mills <mills@udel.edu> (Spectractom WWVB, Austron GPS,
+ * Heath, ATOM, ACTS, KSI/Odetics IRIG-B clock drivers; pps support)
+ * Jeffrey Mogul <mogul@pa.dec.com> (ntptrace utility)
+ * Steve Clift (clift@ml.csiro.au) OMEGA clock driver)
+ * Mike Iglesias (iglesias@uci.edu) (DEC Alpha changes)
+ * Mark Andrews <marka@syd.dms.csiro.au> (Leitch atomic clock controller)
+ * George Lindholm <lindholm@ucs.ubc.ca> (port to SunOS 5.1 operating system)
+ * Jeff Johnson <jbj@chatham.usdesign.com> (massive prototyping overhaul)
+ * Tom Moore <tmoore@fievel.daytonoh.ncr.com> (port to i386 svr4)
+ * Piete Brooks <Piete.Brooks@cl.cam.ac.uk> (MSF clock driver, Trimble PARSE
+ * support)
+ * Karl Berry <karl@owl.HQ.ileaf.com> (syslog to file option)
+ * Torsten Duwe <duwe@immd4.informatik.uni-erlangen.de> (Linux Port)
+ * Paul A Vixie <vixie@vix.com> (TrueTime GPS driver)
+ * Jim Jagielski <jim@jagubox.gsfc.nasa.gov> (A/UX port)
+ * Ray Schnitzler <schnitz@unipress.com> (First pass at a Unixware1 port.)
+ * Ajit Thyagarajan <ajit@ee.udel.edu> (IP multicast support)
+ * Jeff Steinman <jss@pebbles.jpl.nasa.gov> (Datum PTS clock driver)
+ * Tomoaki TSURUOKA <tsuruoka@nc.fukuoka-u.ac.jp> (TRAK clock driver)
+ */
diff --git a/usr.sbin/xntpd/Makefile b/usr.sbin/xntpd/Makefile
new file mode 100644
index 0000000..8f71fa9
--- /dev/null
+++ b/usr.sbin/xntpd/Makefile
@@ -0,0 +1,7 @@
+#
+# Makefile for xntpd.
+# $Id$
+#
+SUBDIR= lib parse xntpd xntpdc ntpq ntpdate ntptrace authstuff util
+
+.include <bsd.subdir.mk>
diff --git a/usr.sbin/xntpd/Makefile.inc b/usr.sbin/xntpd/Makefile.inc
new file mode 100644
index 0000000..906b5c6
--- /dev/null
+++ b/usr.sbin/xntpd/Makefile.inc
@@ -0,0 +1,17 @@
+.if !defined(BEEN_HERE)
+BEEN_HERE=1
+
+DEFS_LOCAL=-DREFCLOCK -DPARSE -DUDP_WILDCARD_DELIVERY
+NTPDEFS= -DSYS_FREEBSD -DSYS_44BSD
+AUTHDEFS= -DMD5
+CLOCKDEFS= -DLOCAL_CLOCK -DPST -DWWVB -DAS2201 -DGOES -DGPSTM -DOMEGA \
+ -DLEITCH -DTRAK -DACTS -DATOM -DDATUM -DHEATH -DMSFEES \
+ -DMX4200 -DNMEA -DBOEDER
+CFLAGS+= ${NTPDEFS} ${DEFS_LOCAL} ${AUTHDEFS} ${CLOCKDEFS} ${COPTS}
+BINDIR?= /usr/sbin
+
+.if !defined(NOCRYPT) && exists(${.CURDIR}/../../../secure/usr.sbin/xntpd/lib)
+AUTHDEFS+= -DDES
+.endif
+
+.endif
diff --git a/usr.sbin/xntpd/PORTING b/usr.sbin/xntpd/PORTING
new file mode 100644
index 0000000..7f23642
--- /dev/null
+++ b/usr.sbin/xntpd/PORTING
@@ -0,0 +1,37 @@
+These are the rules so that older bsd systems and the POSIX standard
+system can coexist togather.
+
+ 1) If you use select then include "ntp_select.h"
+ select is not standard, since it is very system depenedent as to where
+ select is defined. The logic to include the right system dependent
+ include file is in "ntp_select.h".
+ 2) Always use POSIX defintion of strings. Inlcude "ntp_string.h" instaed
+ of <string.h>.
+ 3) Always include "ntp_malloc.h" if you use malloc.
+ 4) Always include "ntp_io.h" instead of <sys/file.h> or <fnctl.h> to
+ get O_* flags.
+ 5) Always include "ntp_if.h" instead of <net/if.h>.
+ 6) Always include "ntp_stdlib.h" instead of <stdlib.h>.
+ 7) Always define a system identifier for any new system added to the
+ machines directory. The identifier should always start with SYS_!
+ 8) Define any special defines needed for a system in
+ ./include/ntp_machine.h based on system identifier. This file is
+ included by the "ntp_types.h" file and should always be placed
+ first after the <> defines.
+ 9) Define any special library prototypes left over from the system
+ library and include files in the "l_stdlib.h" file. This file is
+ included by the "ntp_stdlib.h" file and should ordinarily be
+ placed last in the includes list.
+ 10) Don't define a include file by the same name as a system include file.
+
+
+"l_stdlib.h" can contain any extra definitions that are needed so that
+gcc will shut up. They should be controlled by a system identifier and
+there should be a seperate section for each system. Really this will
+make it easier to maintain.
+
+See include/ntp_machines.h for the verious compile time options.
+
+Good luck.
+
+Bill Jones, with amendments by Dave Mills
diff --git a/usr.sbin/xntpd/README b/usr.sbin/xntpd/README
new file mode 100644
index 0000000..43780d6
--- /dev/null
+++ b/usr.sbin/xntpd/README
@@ -0,0 +1,156 @@
+The xntp3 Distribution
+
+This directory and its subdirectories contain the Network Time Protocol
+Version 3 (NTP) distribution for Unix systems. It contains source code
+for the daemon, together with related auxiliary programs, documentation
+and strange stuff. You are welcome to the lot, with due consideration of
+the COPYRIGHT files stashed in the distributions. You are also invited
+to contribute bugfixes and drivers for new and exotic radios, telephones
+and sundials. This distribution is normally available by anonymous ftp
+as the compressed tar archive xntp-<version>.tar.Z in the pub/ntp directory
+on louie.udel.edu.
+
+The base directory contains the distributions and related stuff. The files
+marked with a "*" are not distributed, but generated. Most of
+the subdirectories contain README files describing their contents. The
+base directory ./ includes:
+
+COPYRIGHT file specifying copyright conditions, together with a
+ list of major authors and electric addresses.
+
+Config * configuration file built by the configuration script
+ "make makeconfig" and used to build the makefiles in the
+ various subdirectories. Do not edit.
+
+Config.local * Unless you have a reference clock (besides the local
+ computer clock) or want to change the default installation
+ directory (/usr/local/bin) not action is needed. For
+ configuring a reference clock a "make refconf" should
+ suffice. Diehards can still use an editor on this file.
+
+Config.local.dist file used to generate a plausible Config.local by commands
+ such as "make Config.local.green".
+
+Config.sed * sed script used to build makefiles from the
+ configuration file. Do not edit.
+
+Makefile this is the root of the makefile tree. Do not edit.
+ (Contents under pressure - qualified personel only 8-)
+
+PORTING contains useful information for porting to unexplored
+ new systems.
+
+RELNOTES instructions for compiling and installing the daemon and
+ supporting programs.
+
+README this file.
+
+TODO our current problems where we could need help.
+
+adjtime directory containing the sources for the adjtime daemon
+ for HP/UX systems.
+
+authstuff directory containing sources for miscellaneous programs
+ to test, calibrate and certify the cryptographic
+ mechanisms for DES and MD5 based authentication. These
+ programs do not include the cryptographic routines
+ themselves, so are free of U.S. export restrictions.
+
+clockstuff directory containing sources for miscellaneous programs
+ to test certain auxilliary programs used with some
+ kernel configurations, together with a program to
+ calculate propagation delays for use with radio clocks
+ and national time dissemination services such as
+ WWV/WWVH, WWVB and CHU.
+
+compilers directory containing configuration scripts for various
+ compilers and operating systems.
+
+conf directory containing a motley collection of
+ configuration files for various systems. For example
+ only.
+
+doc directory containing miscellaneous man pages and memos
+ useful for installation and subnet management.
+
+gadget directory containing instructions and construction data
+ for a mysterious little box used as a CHU radio
+ demodulator and/or a level converter-pulse generator for
+ a precision 1-pps signal.
+
+include directory containing include header files used by most
+ programs in the distribution.
+
+hints directory containing files with hints on particular
+ topics like installation on specific OS variants or
+ general information.
+
+kernel directory containing sources for kernel programs such as
+ line disciplines and STREAMS modules used with the CHU
+ decoder and precision 1-pps signals.
+
+lib directory containing sources for the library programs
+ used by most programs in the distribution.
+
+machines directory containing configuration scripts for various
+ operating systems.
+
+ntpdate directory containing sources for a program to set the
+ local machine time from one or more remote machines
+ running NTP. Operates like rdate, but much more
+ accurate.
+
+ntpq directory containing sources for a utility program to
+ query local and remote NTP peers for state variables and
+ related timekeeping information. This program conforms
+ to Appendix A of the NTP Version 3 Specification RFC
+ 1305.
+
+ntptrace directory containing sources for a utility program that
+ can be used to reveal the chain of NTP peers from a
+ designated peer to the primary server at the root of the
+ timekeeping subnet.
+
+parse directory containing file belonging to the generic parse
+ reference clock driver. For reasonably simple clocks it
+ is possible to get away with about 3-4Kb of code.
+ additionally the SunOS 4.x streams module for parse is
+ residing here.
+
+parse/util some goodies for testing parse processing of DCF77 information.
+ (primarily for use on Suns, although others may work
+ also - possibly with a little porting.)
+ one little gem is dcfd.c - DCF77 decoder with ntp loopfilter
+ code for standalone DCF77 synchronisation without the full
+ works of NTP.
+ Dcfd.c has been ported to FreeBSD for the Boeder DCF77 receiver
+ by Vincenzo Capuano.
+
+ppsclock directory containing sources for modifications to the
+ kernel asynchronous serial driver plus a STREAMS module
+ to capture a precision 1-pps signal. Useful on SunOS
+ 4.1.X systems only.
+
+refclocks directory containing reference clock configuration support
+
+scripts directory containing scripts to build the configuration
+ file "config" in this directory and then the makefiles
+ used in various dependent directories.
+ the subdirectories monitoring and support hold various
+ perl and shell scripts for visualising synchronisation
+ and daemon startup.
+
+util directory containing sources for various utility and
+ testing programs.
+
+xntpd directory containing sources for the NTP Version 3 daemon.
+
+xntpdc directory containing sources for a utility program to
+ query local and remote NTP peers for state variables and
+ related timekeeping information. This program is
+ specific to this implmentation of NTP Version 3 and does
+ not conform to Appendix A of the NTP Version 3
+ Specification RFC 1305.
+
+xntpres directory containing sources for a name-resolution
+ program used in some configurations of NTP Version 3.
diff --git a/usr.sbin/xntpd/README.FreeBSD b/usr.sbin/xntpd/README.FreeBSD
new file mode 100644
index 0000000..523d84c
--- /dev/null
+++ b/usr.sbin/xntpd/README.FreeBSD
@@ -0,0 +1,15 @@
+ $Id$
+
+This version of NTP was converted to the BSD-style Makefile system by
+Garrett Wollman (wollman@FreeBSD.org); it is based on version
+3.4e (beta) from the University of Delaware.
+
+Besides the Makefile changes, the DES code has been completely removed
+in order to make this code exportable. If you have a legal copy of
+`authdes.c', you can just add it to the lib/ directory and add `-DDES'
+to the AUTHDEFS in Makefile.inc.
+
+You can change CLOCKDEFS in the same file to add other reference clocks.
+
+This port should work under either FreeBSD 1.1 or FreeBSD 2.0. For
+1.1, change the `-DSYS_44BSD' in Makefile.inc to `-DSYS_386BSD'.
diff --git a/usr.sbin/xntpd/RELNOTES b/usr.sbin/xntpd/RELNOTES
new file mode 100644
index 0000000..ab3aebe
--- /dev/null
+++ b/usr.sbin/xntpd/RELNOTES
@@ -0,0 +1,216 @@
+For special hints on setup/compilation/installation and other general
+topics you may persue the files in the hints directory.
+
+This file contains the usual instructions to compile and install the programs in
+this distribution. To make these programs:
+
+(0) Make sure that you have all necessary tools for building executables.
+ These tools include cc/gcc, make, awk, sed, tr, sh, grep, egrep and
+ a few others. Not all of these tools exist in the standard distribution
+ of todays UNIX versions (compilers are likely to be an extra product).
+ For a successful build all of these tools should be accessible via the
+ current path.
+
+(1) By default, if there is no Config.local, the system will generate one
+ to support a local ref clock (i.e. run off the system clock).
+ Greenhorns can skip on to (2).
+
+ HACKers can create a Config.local and choose the compilation options,
+ install destination directory and clock drivers.
+ A template for Config.local can be found in Config.local.dist.
+ There are two Configurations that can be auto-generated:
+ make Config.local.local # network configuration plus local
+ # reference clock (the default)
+ make Config.local.NO.clock # network only configuration
+
+ To set up for a radio clock, type "make refconf" and answer the questions
+ about PLL, PPS and radio clock type.
+ If this is the first use of the ref clock, don't forget to make suitable
+ files in /dev/.
+
+ For custom tailored configuration copying Config.local.dist to Config.local
+ and editing Config.local to suit the local needs is neccessary (at most
+ 3 lines to change), or use one of the make's above and then tweak it.
+ Config.local can also be used to override common settings from the
+ machines/* files like the AUTHDEFS= to select very specific configurations.
+ Please use this feature with care and don't be disappointed if it doesn't
+ work the way you expect.
+
+(2) Type "make" to compile everything of general interest. Expect few or
+ no warnings using cc and a moderate level of warnings using gcc.
+ Note: On some Unix platforms the use of gcc can result in quite a few
+ complaints about system header files and type problems within xntp
+ code. This is usually the case when the OS header files are not up
+ up to ANSI standards or GCCISMs. (There may, however, be still some
+ inconsistencies in the code)
+
+ Other known problems stem from bugs/features/... in utility programs
+ of some vendors.
+
+ See section "build problems" for known problems and possible work-
+ arounds.
+
+ Each time you change the configuration a script that pokes your hard- and
+ software will be run to build the actual configuration files.
+ If the script fails, it will give you a list of machines it knows about.
+ You can override the automatic choice by cd to the ../machines directory
+ and typing "make makeconfig OS=<machine>", where <machine> is one of the
+ file names in the ../machine directory.
+
+ The shell script will attempt to find the gcc compiler and, if
+ found, will use it instead of the cc compiler. You can override
+ this automatic choice by cd to the ../machines directory and typing
+ "make makeconfig COMP=<compiler>", where <compiler> is one of the file
+ names in the ../compilers directory. This can be combined with
+ the OS argument above.
+
+ The configuration step can be separatly invoked by "make makeconfig".
+
+ Note that any reconfiguration will result in cleaning the old
+ program and object files.
+
+(3) Assuming you have write permission on the install destination directory,
+ type "make install" to install the binaries in the destination directory.
+ At the time of writing this includes
+ the programs xntpd (the daemon), xntpdc (an xntpd-dependent query
+ program), ntpq (a standard query program), ntpdate (an rdate
+ replacement for boot time date setting and sloppy time keeping)
+ and xntpres (a program which provides name resolver support for
+ some xntpd configurations).
+
+(4) You are now ready to configure the daemon and start it. At this
+ point it might be useful to format and print the file doc/notes.me
+ and read a little bit. The sections on configuration and on the
+ tickadj program will be immediately useful.
+
+Additional "make" target you might find useful are:
+
+clean cleans out object files, programs and temporary files
+
+dist makes a new distribution file (also cleans current binaries)
+ All usual scratch and backup files (*.rej, *.orig, *.o, *~
+ core, lint*.errs, executables, tags, Makefile.bak, make.log)
+ will be removed. The distribution is created in a tar file
+ (file name: <prefix><version>.tar.<compression suffix> - with
+ the prefix usually being ../xntp- and a compression suffix
+ of .Z (compress))
+ Note: the file Config.local will never be included in the
+ distribution tar file. For configuration hints to propagate
+ in in distribution changes must be made to Config.local.dist.
+
+depend possible maker of hazardous waste
+
+refconf a target to interactively configure reference clock support.
+ This should work for you, but has not yet been tested on
+ the more exotic Unix ports (mostly the supercomputer ones).
+
+Bug reports of a general nature can be sent to David Mills (mills@udel.edu).
+Reports concerning specific hardware or software systems mentioned in the
+COPYRIGHT file should be sent to the author, with copy to David Mills for
+archive.
+
+The distribution has been compiled and run on at least the following
+machines, operating systems and compilers. In all known cases, if
+the gcc compiler eats it with some success, the cc compiler also enjoys
+the meal. The converse is not always true. See the conf directory for
+test suites known to compile with various radio clocks; however, not all
+the combinations that compile have been tested.
+
+ VAX-11/785 4.3 tahoe cc no REFCLOCK (dm 93/11/20)
+ Sun3 SunOS 4.1.1 gcc no REFCLOCK (pb 93/10/25)
+ Sun4 SunOS 4.1.1 gcc all REFCLOCK drivers (dm 93/10/25)
+ Sun4 SunOS 4.1.3 gcc all REFLCOCK drivers
+ Sun4 SunOS 5.1 gcc no REFCLOCK (pb 93/10/25)
+ Sun4 SunOS 5.2 gcc no REFCLOCK (dm 93/11/20)
+ Sun4 SunOS 5.2 gcc PARSE REFCLOCK (kd 93/11/10)
+ Sun4 SunOS 5.3 gcc local (pb 93/11/10)
+ HP700 HPUX 9.0 cc no REFCLOCK
+ hp7xx HPUX 9.01 cc local + PARSE (kd 93/10/26)
+ HP3xx HPUX 9.01 cc no REFCLOCK (pb 93/10/25)
+ HP3xx HPUX 8.0 cc no REFCLOCK (pb 93/10/25)
+ MIPS Ultrix 4.3a gcc WWVB clock (dm 93/11/20)
+ MIPS Ultrix 3a gcc green (pb 93/10/26)
+ ALPHA OSF 1.2a gcc no REFCLOCK (dm 93/11/20)
+ ALPHA OSF 1.3 gcc no REFCLOCK (pb 93/10/25)
+ ALPHA OSF1 1.3 gcc green (pb 93/10/26)
+ Convex Convex OS 10.1 ? ?
+ SGI IRIX 4.0.5F gcc no REFCLOCK (pb 93/11/10)
+ AIX 3.2 ? ?
+ A/UX 2.0.1, 3.x.x gcc LOCAL_CLOCK (jmj (94/01/26 see hints)
+ RS6000 AIX 3.2 gcc no REFCLOCK
+ MX500 Sinix-m V5.40 cc PARSE REFCLOCK
+ S2000 Sequent PTX 1.4 cc LOCAL_CLOCK (kd 93/11/10)
+ S2000 Sequent PTX 1.4 gcc LOCAL_CLOCK (kd 93/11/10)
+ PC FreeBSD gcc LOCAL_CLOCK see "build problems"
+ PC NetBSD? gcc LOCAL_CLOCK possibly see "build problems"
+ PC BSD/386 1.0 gcc LOCAL_CLOCK possibly see "build problems"
+ PC Linux (pl14) gcc LOCAL_CLOCK (dw 93/10/30)
+ PC Dell SVR4 v2.2 gcc ? (tl 93/12/30)
+ PC Unixware1/SVR4 cc no tickadj, ? (ras 93/04/11)
+ NCR3445 NCR SVR4 cc LOCAL_CLOCK (tm 93/11/29)
+
+ pb: Piete Brooks
+ kd: Frank Kardel
+ dw: Torsten Duwe (duwe@informatik.uni-erlangen.de)
+ dm: David Mills (mills@udel.edu)
+ tl: Tony Lill <ajlill@tlill.hookup.net>
+ tm: Tom Moore <Tom.Moore@DaytonOH.NCR.COM>
+ jmj: Jim Jagielski <jim@jagubox.gsfc.nasa.gov>
+ ras: Ray Schnitzler <schnitz@unipress.com>
+
+Build Problems (and workaround):
+
+During testing/porting we have found some
+of "make" and "sh" and "awk" features in different implementations.
+If you have problems other tha the one listed below please check for
+usualy things like the latest sh compatible pd shell in your own
+environment. Things like this are known to hinder compilation if
+they are not fully compatible with sh or are buggy.
+
+Current build problem on (Mac) NetBSD, possibly BSDI and 386BSD:
+ pmake (e. g. NetBSD on MAC, possible other BNR2+pmake systems)
+ Following Makefile construction fails for no
+ apparent reason (at least to me)
+ doit:
+ $(MAKE) MAKE=\"$(MAKE)\" all
+
+ all:
+ @echo all done.
+
+ for the "make MAKE=make" call but not for "make" or
+ "make -e MAKE=make". Use the last form if you suffer
+ from that kind of make problems. (Easily detected
+ by failure to build with the message:
+ "don't know how to make make".
+
+ On BSD/386 the solution is to get GNU make and run build as:
+ % gnumake MAKE=gnumake
+ Note that BSD/386 1.0's "sed" goes into an infinite loop if
+ you try to make the "refconf" target -- so edit Config.local
+ by hand if you have a reference clock. (BSD/386 1.1 will fix
+ this "sed" bug.)
+
+ The NetBSD people claim that this problem goes away
+ when you compile make with POSIX compilation options.
+
+The known sh and some make pecularities have already been taken care of.
+
+Usually the vendor should fix these bugs in vital utilities.
+We try to circumvent these bugs in a hopefully portable way.
+If you can reproduce these bugs on your system please bug your
+vendor/developer group to fix them. We are not trying anything fancy
+in here (except for starting sub-makes) and we are shocked that even
+the most common tools fail so miserably. By the time you get this
+code the above utilities may already have been fixed. Hopefully one
+day we do not have to cope with this kind of broken utilities.
+ Frank Kardel
+
+William L. Jones <jones@chpc.utexas.edu>
+Dennis Ferguson (Advanced Network Systems) <dennis@ans.net>
+Lars Mathiesen (University of Copenhagen) <thorinn@diku.dk>
+David Mills <mills@udel.edu>
+Frank Kardel <Frank.Kardel@informatik.uni-erlangen.de>
+Piete Brooks <Piete.Brooks@cl.cam.ac.uk>
+
+-- and a cast of thousands -- see the COPYRIGHT file
+16 November 1993
diff --git a/usr.sbin/xntpd/TODO b/usr.sbin/xntpd/TODO
new file mode 100644
index 0000000..e4bbe47
--- /dev/null
+++ b/usr.sbin/xntpd/TODO
@@ -0,0 +1,26 @@
+#
+# TODO,v 3.5 1994/01/25 19:03:55 kardel Exp
+#
+This file contains problems known to the authors that still need to be done.
+We would appreciate if you could spare some of your time to look through
+these topics and help us with some open questions. Most of the topics
+pertain to specific architectures where we have no direct access or not
+the time or expertise to currently track down the problem further.
+If you don't know what we are talking about in the topics don't bother
+with finding out - somebody else will probably solve that problem.
+
+Before you try to send a solution to mills@udel.edu please check whether
+this problem still exists in the distribution on louie.udel.edu.
+
+Thank you for your help !
+ Dave Mills
+ Frank Kardel
+ Piete Brooks
+
+Open issues:
+
+Apollo:
+ - terminal affiliation
+ Check whether thing are still correct in respect to breaking
+ terminal affiliation - horrible stories are told in the code.
+ File affected: xntpd/ntpd.c
diff --git a/usr.sbin/xntpd/VERSION b/usr.sbin/xntpd/VERSION
new file mode 100644
index 0000000..a51bf774
--- /dev/null
+++ b/usr.sbin/xntpd/VERSION
@@ -0,0 +1 @@
+version=3.4e (beta multicast)
diff --git a/usr.sbin/xntpd/authstuff/Makefile b/usr.sbin/xntpd/authstuff/Makefile
new file mode 100644
index 0000000..db0b5eb
--- /dev/null
+++ b/usr.sbin/xntpd/authstuff/Makefile
@@ -0,0 +1,27 @@
+#
+# $Id$
+#
+# Most of the programs in this directory are completely useless for the
+# NTP configuration that we provide by default.
+# We provide the `md5' program as a public service.
+
+CFLAGS+= -I${.CURDIR}/../include
+
+.if exists(${.OBJDIR}/../lib)
+LDADD+= -L${.OBJDIR}/../lib
+DPADD+= ${.OBJDIR}/../lib/libntp.a
+.else
+LDADD+= -L${.CURDIR}/../lib
+DPADD+= ${.CURDIR}/../lib/libntp.a
+.endif
+
+LDADD+= -lntp
+
+PROG= md5
+
+SRCS= md5driver.c
+NOMAN=
+
+install:
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/xntpd/authstuff/README b/usr.sbin/xntpd/authstuff/README
new file mode 100644
index 0000000..2985751
--- /dev/null
+++ b/usr.sbin/xntpd/authstuff/README
@@ -0,0 +1,13 @@
+README file for directory ./authstuff of the NTP Version 3 distribution
+
+This directory contains the sources for miscellaneous programs to test,
+validate and calibreate cryptographic routines used by NTP. These include
+
+authcert.c used to certify the DES and MD5 message digest algorithms
+ work properly. See the source for directions for use.
+
+authspeed.c used to determing the running time for DES and MD5
+ messge digest algorithms. See the source for directions
+ for use.
+
+For other programs, see the source files.
diff --git a/usr.sbin/xntpd/authstuff/auth.samplekeys b/usr.sbin/xntpd/authstuff/auth.samplekeys
new file mode 100644
index 0000000..761c7c2
--- /dev/null
+++ b/usr.sbin/xntpd/authstuff/auth.samplekeys
@@ -0,0 +1,44 @@
+#
+# Sample key file, also used for testing.
+#
+# Note that there are three formats for keys. Standard format is a
+# hex format with the low order bit of each byte being a parity
+# bit, a la the NBS standard. NTP format is also hex, but uses the
+# high order bit of each byte for parity. Ascii format simply encodes
+# a 1-8 character ascii string as a key. Note that because of the
+# simple tokenizing routine, the characters ' ', '#', '\t', '\n' and
+# '\0' can't be used in an ascii key. Everything else is fair game, though.
+#
+
+1 S 0101010101010101 # odd parity 0 key
+2 N 8080808080808080 # and again
+3 A ugosnod
+4 A BigbOObs
+5 S f1f1f1f1f1f1f1f1
+6 N f8f8f8f8f8f8f8f8 # same as key 5
+7 S f8f8f8f8f8f8f8f8 # not same as key 6
+8 A a # short ascii keys are zero padded
+9 A &^%$@!*(
+10 S 01020407080bf1f1
+11 N 4040404040404040
+12 A more
+13 A random
+14 A keys
+15 A password # 15 used as password by runtime configuration
+#
+16 M password # MD5 key
+17 M secret
+18 M key1
+19 M key2
+20 M foobar
+21 M tick
+22 M tock
+23 M key23
+24 M key24
+25 M key25
+26 M a
+27 M few
+28 M more
+29 M random
+30 M md5
+31 M keys!
diff --git a/usr.sbin/xntpd/authstuff/auth.speed b/usr.sbin/xntpd/authstuff/auth.speed
new file mode 100644
index 0000000..ccf8993
--- /dev/null
+++ b/usr.sbin/xntpd/authstuff/auth.speed
@@ -0,0 +1,33 @@
+Authentication delays (us) DES MD5
+-------------------------------------------
+IBM RS6000/990 10 28
+IBM RS6000/590 10 29
+HP 9000/735 hpux9.03 snavely 10 33
+DEC 3000/400 OSF/1 bunnylou 14 35
+HP 9000/730 hpux8.07(+OV) 16 55
+SGI Indigo R4000 19 48
+HP 9000/720 hpux8.07 21 66
+IBM RS6000/250 20 39
+IBM RS6000/370 21 43
+IBM RS6000/580 22 43
+SGI 4/35 38 110
+DECstation 5000/240 cowbird 39 81
+IBM RS6000/530H 40 83
+Sun4c/75 SS2 43 96
+Sun4c/50 IPX malarky 47 94
+DECstation 5000/33 sundeck 49 106
+IBM RS6000/530 51 107
+SGI Indigo 54 115
+DECstation 5000/125 herald 63 136
+IBM RS6000/320 69 139
+Sun4c/65 SS1+ pogo 72 159
+Sun4c/40 IPC grundoon 73 163
+Sun4c/60 SS1 albert 95 199
+Sun4c/20 SLC 95 203
+DECstation 3100 sheol 98 214
+DECstation 2100 circus 126 278
+VAX 780 985 ?
+
+Updated 26 April 1994
+David L. Mills
+
diff --git a/usr.sbin/xntpd/authstuff/authcert.c b/usr.sbin/xntpd/authstuff/authcert.c
new file mode 100644
index 0000000..19ade8c
--- /dev/null
+++ b/usr.sbin/xntpd/authstuff/authcert.c
@@ -0,0 +1,95 @@
+/*
+ * This file, and the certdata file, shamelessly stolen
+ * from Phil Karn's DES implementation.
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+#define DES
+#include "ntp_stdlib.h"
+
+u_char ekeys[128];
+u_char dkeys[128];
+
+static void get8 P((U_LONG *));
+static void put8 P((U_LONG *));
+
+void
+main()
+{
+ U_LONG key[2], plain[2], cipher[2], answer[2], temp;
+ int i;
+ int test;
+ int fail;
+
+ for(test = 0; !feof(stdin); test++){
+ get8(key);
+ DESauth_subkeys(key, ekeys, dkeys);
+ printf(" K: "); put8(key);
+ get8(plain);
+ printf(" P: "); put8(plain);
+ get8(answer);
+ printf(" C: "); put8(answer);
+ for (i = 0; i < 2; i++)
+ cipher[i] = htonl(plain[i]);
+ DESauth_des(cipher, ekeys);
+ for (i = 0; i < 2; i++) {
+ temp = ntohl(cipher[i]);
+ if (temp != answer[i])
+ break;
+ }
+
+ fail = 0;
+ if (i != 2) {
+ printf(" Encrypt FAIL");
+ fail++;
+ }
+ DESauth_des(cipher, dkeys);
+ for (i = 0; i < 2; i++) {
+ temp = ntohl(cipher[i]);
+ if (temp != plain[i])
+ break;
+ }
+ if (i != 2) {
+ printf(" Decrypt FAIL");
+ fail++;
+ }
+ if (fail == 0)
+ printf(" OK");
+ printf("\n");
+ }
+}
+
+static void
+get8(lp)
+ U_LONG *lp;
+{
+ int t;
+ U_LONG l[2];
+ int i;
+
+ l[0] = l[1] = 0;
+ for (i = 0; i < 8; i++) {
+ scanf("%2x", &t);
+ if (feof(stdin))
+ exit(0);
+ l[i / 4] <<= 8;
+ l[i / 4] |= (U_LONG)(t & 0xff);
+ }
+ *lp = l[0];
+ *(lp + 1) = l[1];
+}
+
+static void
+put8(lp)
+ U_LONG *lp;
+{
+ int i;
+
+
+ for(i = 0; i < 2; i++)
+ printf("%08lx", (u_long)(*lp++));
+}
diff --git a/usr.sbin/xntpd/authstuff/authspeed.c b/usr.sbin/xntpd/authstuff/authspeed.c
new file mode 100644
index 0000000..93965d2
--- /dev/null
+++ b/usr.sbin/xntpd/authstuff/authspeed.c
@@ -0,0 +1,315 @@
+/*
+ * authspeed - figure out how long it takes to do an NTP encryption
+ */
+
+#if defined(SYS_HPUX) || defined(SYS_AUX3) || defined(SYS_AUX2) || defined(SOLARIS) || defined(SYS_SVR4) || defined(SYS_PTX) || defined(SYS_UNIXWARE1)
+#define FAKE_RUSAGE
+#endif
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#ifdef FAKE_RUSAGE
+#include <sys/param.h>
+#include <sys/times.h>
+#endif
+
+#include "ntp_fp.h"
+#include "ntp_stdlib.h"
+
+#define STREQ(a, b) (*(a) == *(b) && strcmp((a), (b)) == 0)
+
+#define DEFLOOPS -1
+
+#define DEFDELAYLOOPS 20000
+#define DEFCOSTLOOPS 2000
+
+char *progname;
+int debug;
+
+struct timeval tstart, tend;
+#ifdef FAKE_RUSAGE
+struct tms rstart, rend;
+#define getrusage(foo, t) times(t)
+#define RUSAGE_SELF 0
+#else
+struct rusage rstart, rend;
+#endif
+
+l_fp dummy1, dummy2;
+u_long dummy3;
+
+U_LONG pkt[15];
+
+int totalcost = 0;
+double rtime;
+double vtime;
+
+int domd5 = 0;
+
+static void dodelay P((int));
+static void docheap P((int));
+static void docost P((int));
+static void subtime P((struct timeval *, struct timeval *, double *));
+
+/*
+ * main - parse arguments and handle options
+ */
+void
+main(argc, argv)
+int argc;
+char *argv[];
+{
+ int c;
+ int loops;
+ int i;
+ int errflg = 0;
+ extern int ntp_optind;
+ extern char *ntp_optarg;
+
+ progname = argv[0];
+ loops = DEFLOOPS;
+ while ((c = ntp_getopt(argc, argv, "cdmn:")) != EOF)
+ switch (c) {
+ case 'c':
+ totalcost++;
+ break;
+ case 'd':
+ ++debug;
+ break;
+ case 'm':
+ domd5 = 16; /* offset into list of keys */
+ break;
+ case 'n':
+ loops = atoi(ntp_optarg);
+ if (loops <= 0) {
+ (void) fprintf(stderr,
+ "%s: %s is unlikely to be a useful number of loops\n",
+ progname, ntp_optarg);
+ errflg++;
+ }
+ break;
+ default:
+ errflg++;
+ break;
+ }
+ if (errflg || ntp_optind == argc) {
+ (void) fprintf(stderr,
+ "usage: %s [-d] [-n loops] [ -c ] auth.samplekeys\n",
+ progname);
+ exit(2);
+ }
+ printf("Compute timing for ");
+ if (domd5)
+ printf("MD5");
+ else
+ printf("DES");
+ printf(" based authentication.\n");
+
+ init_auth();
+ authreadkeys(argv[ntp_optind]);
+ for (i = 0; i < 16; i++) {
+ if (!auth_havekey(i + domd5)) {
+ errflg++;
+ (void) fprintf(stderr, "%s: key %d missing\n",
+ progname, i + domd5);
+ }
+ }
+
+ if (errflg) {
+ (void) fprintf(stderr,
+ "%s: check syslog for errors, or use file with complete set of keys\n",
+ progname);
+ exit(1);
+ }
+
+ if (loops == DEFLOOPS) {
+ if (totalcost)
+ loops = DEFCOSTLOOPS;
+ else
+ loops = DEFDELAYLOOPS;
+ }
+
+ dummy1.l_ui = 0x80808080;
+ dummy1.l_uf = 0xffffff00;
+ dummy3 = 0x0aaaaaaa;
+
+ for (i = 0; i < 12; i++)
+ pkt[i] = i * 0x22222;
+
+ if (totalcost) {
+ if (totalcost > 1)
+ docheap(loops);
+ else
+ docost(loops);
+ } else {
+ dodelay(loops);
+ }
+
+ printf("total real time: %.3f\n", rtime);
+ printf("total CPU time: %.3f\n", vtime);
+ if (totalcost) {
+ printf("real cost (in seconds): %.6f\n",
+ rtime/(double)loops);
+ printf("CPU cost (in seconds): %.6f\n",
+ vtime/(double)loops);
+ printf("\nThis includes the cost of a decryption plus the\n");
+ printf("the cost of an encryption, i.e. the cost to process\n");
+ printf("a single authenticated packet.\n");
+ } else {
+ printf("authdelay in the configuration file\n");
+ printf("real authentication delay: %.6f\n",
+ rtime/(double)loops);
+ printf("authentication delay in CPU time: %.6f\n",
+ vtime/(double)loops);
+ printf("\nThe CPU delay is probably the best bet for\n");
+ printf("authdelay in the configuration file\n");
+ }
+ exit(0);
+}
+
+
+/*
+ * dodelay - do the delay measurement
+ */
+static void
+dodelay(loops)
+ int loops;
+{
+ double vtime1, rtime1, vtime2, rtime2;
+ register int loopcount;
+ /*
+ * If we're attempting to compute the cost of an auth2crypt()
+ * for first compute the total cost, then compute the
+ * cost of only doing the first step, auth1crypt(). What
+ * remains is the cost of auth2crypt.
+ */
+ loopcount = loops;
+ (void) gettimeofday(&tstart, (struct timezone *)0);
+ (void) getrusage(RUSAGE_SELF, &rstart);
+
+ while (loopcount-- > 0) {
+ auth1crypt((loops & 0xf) + domd5, pkt, 48);
+ L_ADDUF(&dummy1, dummy3);
+ auth2crypt((loops & 0xf) + domd5, pkt, 48);
+ }
+
+ (void) getrusage(RUSAGE_SELF, &rend);
+ (void) gettimeofday(&tend, (struct timezone *)0);
+
+ subtime(&tstart, &tend, &rtime1);
+#ifdef FAKE_RUSAGE
+ vtime1 = (rend.tms_utime - rstart.tms_utime) * 1.0 / HZ;
+#else
+ subtime(&rstart.ru_utime, &rend.ru_utime, &vtime1);
+#endif
+printf("Time for full encryptions is %f rusage %f real\n", vtime1, rtime1);
+ loopcount = loops;
+ (void) gettimeofday(&tstart, (struct timezone *)0);
+ (void) getrusage(RUSAGE_SELF, &rstart);
+
+ while (loopcount-- > 0) {
+ auth1crypt((loops & 0xf) + domd5, pkt, 48);
+ }
+
+ (void) getrusage(RUSAGE_SELF, &rend);
+ (void) gettimeofday(&tend, (struct timezone *)0);
+
+ subtime(&tstart, &tend, &rtime2);
+#ifdef FAKE_RUSAGE
+ vtime2 = (rend.tms_utime - rstart.tms_utime) * 1.0 / HZ;
+#else
+ subtime(&rstart.ru_utime, &rend.ru_utime, &vtime2);
+#endif
+
+printf("Time for auth1crypt is %f rusage %f real\n", vtime2, rtime2);
+ vtime = vtime1 - vtime2;
+ rtime = rtime1 - rtime2;
+}
+
+
+/*
+ * docheap - do the cost measurement the cheap way
+ */
+static void
+docheap(loops)
+ register int loops;
+{
+
+ (void) authhavekey(3 + domd5);
+
+ (void) gettimeofday(&tstart, (struct timezone *)0);
+ (void) getrusage(RUSAGE_SELF, &rstart);
+
+ while (loops-- > 0) {
+ auth1crypt(3 + domd5, pkt, 48);
+ L_ADDUF(&dummy1, dummy3);
+ auth2crypt(3 + domd5, pkt, 48);
+ (void) authdecrypt(3 + domd5, pkt, 48);
+ }
+
+ (void) getrusage(RUSAGE_SELF, &rend);
+ (void) gettimeofday(&tend, (struct timezone *)0);
+
+ subtime(&tstart, &tend, &rtime);
+#ifdef FAKE_RUSAGE
+ vtime = (rend.tms_utime - rstart.tms_utime) * 1.0 / HZ;
+#else
+ subtime(&rstart.ru_utime, &rend.ru_utime, &vtime);
+#endif
+}
+
+
+/*
+ * docost - do the cost measurement
+ */
+static void
+docost(loops)
+ register int loops;
+{
+
+ (void) gettimeofday(&tstart, (struct timezone *)0);
+ (void) getrusage(RUSAGE_SELF, &rstart);
+
+ while (loops-- > 0) {
+ auth1crypt((loops & 0xf) + domd5, pkt, 48);
+ L_ADDUF(&dummy1, dummy3);
+ auth2crypt((loops & 0xf) + domd5, pkt, 48);
+ (void) authdecrypt(((loops+1) & 0xf) + domd5, pkt, 48);
+ }
+
+ (void) getrusage(RUSAGE_SELF, &rend);
+ (void) gettimeofday(&tend, (struct timezone *)0);
+
+ subtime(&tstart, &tend, &rtime);
+#ifdef FAKE_RUSAGE
+ vtime = (rend.tms_utime - rstart.tms_utime) * 1.0 / HZ;
+#else
+ subtime(&rstart.ru_utime, &rend.ru_utime, &vtime);
+#endif
+}
+
+
+/*
+ * subtime - subtract two struct timevals, return double result
+ */
+static void
+subtime(tvs, tve, res)
+ struct timeval *tvs, *tve;
+ double *res;
+{
+ long sec;
+ long usec;
+
+ sec = tve->tv_sec - tvs->tv_sec;
+ usec = tve->tv_usec - tvs->tv_usec;
+
+ if (usec < 0) {
+ usec += 1000000;
+ sec--;
+ }
+
+ *res = (double)sec + (double)usec/1000000.;
+ return;
+}
diff --git a/usr.sbin/xntpd/authstuff/certdata b/usr.sbin/xntpd/authstuff/certdata
new file mode 100644
index 0000000..f9a818e
--- /dev/null
+++ b/usr.sbin/xntpd/authstuff/certdata
@@ -0,0 +1,34 @@
+0000000000000000 0000000000000000 8CA64DE9C1B123A7
+FFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFF 7359B2163E4EDC58
+3000000000000000 1000000000000001 958E6E627A05557B
+1111111111111111 1111111111111111 F40379AB9E0EC533
+0123456789ABCDEF 1111111111111111 17668DFC7292532D
+1111111111111111 0123456789ABCDEF 8A5AE1F81AB8F2DD
+0000000000000000 0000000000000000 8CA64DE9C1B123A7
+FEDCBA9876543210 0123456789ABCDEF ED39D950FA74BCC4
+7CA110454A1A6E57 01A1D6D039776742 690F5B0D9A26939B
+0131D9619DC1376E 5CD54CA83DEF57DA 7A389D10354BD271
+07A1133E4A0B2686 0248D43806F67172 868EBB51CAB4599A
+3849674C2602319E 51454B582DDF440A 7178876E01F19B2A
+04B915BA43FEB5B6 42FD443059577FA2 AF37FB421F8C4095
+0113B970FD34F2CE 059B5E0851CF143A 86A560F10EC6D85B
+0170F175468FB5E6 0756D8E0774761D2 0CD3DA020021DC09
+43297FAD38E373FE 762514B829BF486A EA676B2CB7DB2B7A
+07A7137045DA2A16 3BDD119049372802 DFD64A815CAF1A0F
+04689104C2FD3B2F 26955F6835AF609A 5C513C9C4886C088
+37D06BB516CB7546 164D5E404F275232 0A2AEEAE3FF4AB77
+1F08260D1AC2465E 6B056E18759F5CCA EF1BF03E5DFA575A
+584023641ABA6176 004BD6EF09176062 88BF0DB6D70DEE56
+025816164629B007 480D39006EE762F2 A1F9915541020B56
+49793EBC79B3258F 437540C8698F3CFA 6FBF1CAFCFFD0556
+4FB05E1515AB73A7 072D43A077075292 2F22E49BAB7CA1AC
+49E95D6D4CA229BF 02FE55778117F12A 5A6B612CC26CCE4A
+018310DC409B26D6 1D9D5C5018F728C2 5F4C038ED12B2E41
+1C587F1C13924FEF 305532286D6F295A 63FAC0D034D9F793
+0101010101010101 0123456789ABCDEF 617B3A0CE8F07100
+1F1F1F1F0E0E0E0E 0123456789ABCDEF DB958605F8C8C606
+E0FEE0FEF1FEF1FE 0123456789ABCDEF EDBFD1C66C29CCC7
+0000000000000000 FFFFFFFFFFFFFFFF 355550B2150E2451
+FFFFFFFFFFFFFFFF 0000000000000000 CAAAAF4DEAF1DBAE
+0123456789ABCDEF 0000000000000000 D5D44FF720683D0D
+FEDCBA9876543210 FFFFFFFFFFFFFFFF 2A2BB008DF97C2F2
diff --git a/usr.sbin/xntpd/authstuff/keyparity.c b/usr.sbin/xntpd/authstuff/keyparity.c
new file mode 100644
index 0000000..8a12c13
--- /dev/null
+++ b/usr.sbin/xntpd/authstuff/keyparity.c
@@ -0,0 +1,279 @@
+/*
+ * keyparity - add parity bits to key and/or change an ascii key to binary
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <ctype.h>
+
+#include "ntp_string.h"
+#include "ntp_stdlib.h"
+
+#define STREQ(a, b) (*(a) == *(b) && strcmp((a), (b)) == 0)
+
+/*
+ * Types of ascii representations for keys. "Standard" means a 64 bit
+ * hex number in NBS format, i.e. with the low order bit of each byte
+ * a parity bit. "NTP" means a 64 bit key in NTP format, with the
+ * high order bit of each byte a parity bit. "Ascii" means a 1-to-8
+ * character string whose ascii representation is used as the key.
+ */
+#define KEY_TYPE_STD 1
+#define KEY_TYPE_NTP 2
+#define KEY_TYPE_ASCII 3
+
+#define STD_PARITY_BITS 0x01010101
+
+char *progname;
+int debug;
+
+int ntpflag = 0;
+int stdflag = 0;
+int asciiflag = 0;
+int ntpoutflag = 0;
+int gotoopt = 0;
+
+static int parity P((u_long *));
+static int decodekey P((int, char *, u_long *));
+static void output P((u_long *, int));
+
+/*
+ * main - parse arguments and handle options
+ */
+void
+main(argc, argv)
+int argc;
+char *argv[];
+{
+ int c;
+ int errflg = 0;
+ int keytype;
+ u_long key[2];
+ extern int ntp_optind;
+ extern char *ntp_optarg;
+
+ progname = argv[0];
+ while ((c = ntp_getopt(argc, argv, "adno:s")) != EOF)
+ switch (c) {
+ case 'a':
+ asciiflag = 1;
+ break;
+ case 'd':
+ ++debug;
+ break;
+ case 'n':
+ ntpflag = 1;
+ break;
+ case 's':
+ stdflag = 1;
+ break;
+ case 'o':
+ if (*ntp_optarg == 'n') {
+ ntpoutflag = 1;
+ gotoopt = 1;
+ } else if (*ntp_optarg == 's') {
+ ntpoutflag = 0;
+ gotoopt = 1;
+ } else {
+ (void) fprintf(stderr,
+ "%s: output format must be `n' or `s'\n",
+ progname);
+ errflg++;
+ }
+ break;
+ default:
+ errflg++;
+ break;
+ }
+ if (errflg || ntp_optind == argc) {
+ (void) fprintf(stderr,
+ "usage: %s -n|-s [-a] [-o n|s] key [...]\n",
+ progname);
+ exit(2);
+ }
+
+ if (!ntpflag && !stdflag) {
+ (void) fprintf(stderr,
+ "%s: one of either the -n or -s flags must be specified\n",
+ progname);
+ exit(2);
+ }
+
+ if (ntpflag && stdflag) {
+ (void) fprintf(stderr,
+ "%s: only one of the -n and -s flags may be specified\n",
+ progname);
+ exit(2);
+ }
+
+ if (!gotoopt) {
+ if (ntpflag)
+ ntpoutflag = 1;
+ }
+
+ if (asciiflag)
+ keytype = KEY_TYPE_ASCII;
+ else if (ntpflag)
+ keytype = KEY_TYPE_NTP;
+ else
+ keytype = KEY_TYPE_STD;
+
+ for (; ntp_optind < argc; ntp_optind++) {
+ if (!decodekey(keytype, argv[ntp_optind], key)) {
+ (void) fprintf(stderr,
+ "%s: format of key %s invalid\n",
+ progname, argv[ntp_optind]);
+ exit(1);
+ }
+ (void) parity(key);
+ output(key, ntpoutflag);
+ }
+ exit(0);
+}
+
+
+
+/*
+ * parity - set parity on a key/check for odd parity
+ */
+static int
+parity(key)
+ u_long *key;
+{
+ u_long mask;
+ int parity_err;
+ int bitcount;
+ int half;
+ int byte;
+ int i;
+
+ /*
+ * Go through counting bits in each byte. Check to see if
+ * each parity bit was set correctly. If not, note the error
+ * and set it right.
+ */
+ parity_err = 0;
+ for (half = 0; half < 2; half++) { /* two halves of key */
+ mask = 0x80000000;
+ for (byte = 0; byte < 4; byte++) { /* 4 bytes per half */
+ bitcount = 0;
+ for (i = 0; i < 7; i++) { /* 7 data bits / byte */
+ if (key[half] & mask)
+ bitcount++;
+ mask >>= 1;
+ }
+
+ /*
+ * If bitcount is even, parity must be set. If
+ * bitcount is odd, parity must be clear.
+ */
+ if ((bitcount & 0x1) == 0) {
+ if (!(key[half] & mask)) {
+ parity_err++;
+ key[half] |= mask;
+ }
+ } else {
+ if (key[half] & mask) {
+ parity_err++;
+ key[half] &= ~mask;
+ }
+ }
+ mask >>= 1;
+ }
+ }
+
+ /*
+ * Return the result of the parity check.
+ */
+ return (parity_err == 0);
+}
+
+
+static int
+decodekey(keytype, str, key)
+ int keytype;
+ char *str;
+ u_long *key;
+{
+ u_char keybytes[8];
+ char *cp;
+ char *xdigit;
+ int len;
+ int i;
+ static char *hex = "0123456789abcdef";
+
+ cp = str;
+ len = strlen(cp);
+ if (len == 0)
+ return 0;
+
+ switch(keytype) {
+ case KEY_TYPE_STD:
+ case KEY_TYPE_NTP:
+ if (len != 16) /* Lazy. Should define constant */
+ return 0;
+ /*
+ * Decode hex key.
+ */
+ key[0] = 0;
+ key[1] = 0;
+ for (i = 0; i < 16; i++) {
+ if (!isascii(*cp))
+ return 0;
+ xdigit = strchr(hex, isupper(*cp) ? tolower(*cp) : *cp);
+ cp++;
+ if (xdigit == 0)
+ return 0;
+ key[i>>3] <<= 4;
+ key[i>>3] |= (u_long)(xdigit - hex) & 0xf;
+ }
+
+ /*
+ * If this is an NTP format key, put it into NBS format
+ */
+ if (keytype == KEY_TYPE_NTP) {
+ for (i = 0; i < 2; i++)
+ key[i] = ((key[i] << 1) & ~STD_PARITY_BITS)
+ | ((key[i] >> 7) & STD_PARITY_BITS);
+ }
+ break;
+
+ case KEY_TYPE_ASCII:
+ /*
+ * Make up key from ascii representation
+ */
+ memset(keybytes, 0, sizeof(keybytes));
+ for (i = 0; i < 8 && i < len; i++)
+ keybytes[i] = *cp++ << 1;
+ key[0] = keybytes[0] << 24 | keybytes[1] << 16
+ | keybytes[2] << 8 | keybytes[3];
+ key[1] = keybytes[4] << 24 | keybytes[5] << 16
+ | keybytes[6] << 8 | keybytes[7];
+ break;
+
+ default:
+ /* Oh, well */
+ return 0;
+ }
+
+ return 1;
+}
+
+
+/*
+ * output - print a hex key on the standard output
+ */
+static void
+output(key, ntpformat)
+ u_long *key;
+ int ntpformat;
+{
+ int i;
+
+ if (ntpformat) {
+ for (i = 0; i < 2; i++)
+ key[i] = ((key[i] & ~STD_PARITY_BITS) >> 1)
+ | ((key[i] & STD_PARITY_BITS) << 7);
+ }
+ (void) printf("%08x%08x\n", key[0], key[1]);
+}
diff --git a/usr.sbin/xntpd/authstuff/makeIPFP.c b/usr.sbin/xntpd/authstuff/makeIPFP.c
new file mode 100644
index 0000000..8fabdb7
--- /dev/null
+++ b/usr.sbin/xntpd/authstuff/makeIPFP.c
@@ -0,0 +1,345 @@
+/*
+ * makeIPFP - make fast DES IP and FP tables
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+
+#include "ntp_stdlib.h"
+
+#define STREQ(a, b) (*(a) == *(b) && strcmp((a), (b)) == 0)
+
+u_long IPL[256];
+u_long FPL[256];
+
+char *progname;
+int debug;
+
+static void perm P((u_char *, u_char *, u_long *, u_long *));
+static void doit P((void));
+
+/*
+ * main - parse arguments and handle options
+ */
+void
+main(argc, argv)
+int argc;
+char *argv[];
+{
+ int c;
+ int errflg = 0;
+ extern int ntp_optind;
+ extern char *ntp_optarg;
+
+ progname = argv[0];
+ while ((c = ntp_getopt(argc, argv, "d")) != EOF)
+ switch (c) {
+ case 'd':
+ ++debug;
+ break;
+ default:
+ errflg++;
+ break;
+ }
+ if (errflg) {
+ (void) fprintf(stderr, "usage: %s [-d]\n", progname);
+ exit(2);
+ }
+ doit();
+ exit(0);
+}
+
+
+/*
+ * Initial permutation table
+ */
+u_char IP[64] = {
+ 58, 50, 42, 34, 26, 18, 10, 2,
+ 60, 52, 44, 36, 28, 20, 12, 4,
+ 62, 54, 46, 38, 30, 22, 14, 6,
+ 64, 56, 48, 40, 32, 24, 16, 8,
+ 57, 49, 41, 33, 25, 17, 9, 1,
+ 59, 51, 43, 35, 27, 19, 11, 3,
+ 61, 53, 45, 37, 29, 21, 13, 5,
+ 63, 55, 47, 39, 31, 23, 15, 7
+};
+
+/*
+ * Inverse initial permutation table
+ */
+u_char FP[64] = {
+ 40, 8, 48, 16, 56, 24, 64, 32,
+ 39, 7, 47, 15, 55, 23, 63, 31,
+ 38, 6, 46, 14, 54, 22, 62, 30,
+ 37, 5, 45, 13, 53, 21, 61, 29,
+ 36, 4, 44, 12, 52, 20, 60, 28,
+ 35, 3, 43, 11, 51, 19, 59, 27,
+ 34, 2, 42, 10, 50, 18, 58, 26,
+ 33, 1, 41, 9, 49, 17, 57, 25
+};
+
+
+/*
+ * Bit order after the operation
+ *
+ * ((left & 0x55555555) << 1) | (right & 0x55555555)
+ */
+u_char IPLbits[32] = {
+ 2, 34, 4, 36, 6, 38, 8, 40,
+ 10, 42, 12, 44, 14, 46, 16, 48,
+ 18, 50, 20, 52, 22, 54, 24, 56,
+ 26, 58, 28, 60, 30, 62, 32, 64
+};
+
+
+/*
+ * Bit order after the operation
+ *
+ * (left & 0xaaaaaaaa) | ((right & 0xaaaaaaaa) >> 1)
+ */
+u_char IPRbits[32] = {
+ 1, 33, 3, 35, 5, 37, 7, 39,
+ 9, 41, 11, 43, 13, 45, 15, 47,
+ 17, 49, 19, 51, 21, 53, 23, 55,
+ 25, 57, 27, 59, 29, 61, 31, 63
+};
+
+
+/*
+ * Bit order after the operation
+ *
+ * ((left & 0x0f0f0f0f) << 4) | (right & 0x0f0f0f0f)
+ */
+u_char FPLbits[32] = {
+ 5, 6, 7, 8, 37, 38, 39, 40,
+ 13, 14, 15, 16, 45, 46, 47, 48,
+ 21, 22, 23, 24, 53, 54, 55, 56,
+ 29, 30, 31, 32, 61, 62, 63, 64
+};
+
+
+/*
+ * Bit order after the operation
+ *
+ * (left & 0xf0f0f0f0) | ((right & 0xf0f0f0f0) >> 4)
+ */
+u_char FPRbits[32] = {
+ 1, 2, 3, 4, 33, 34, 35, 36,
+ 9, 10, 11, 12, 41, 42, 43, 44,
+ 17, 18, 19, 20, 49, 50, 51, 52,
+ 25, 26, 27, 28, 57, 58, 59, 60
+};
+
+
+/*
+ * perm - do a permutation with the given table
+ */
+static void
+perm(databits, permtab, leftp, rightp)
+ u_char *databits;
+ u_char *permtab;
+ u_long *leftp;
+ u_long *rightp;
+{
+ register u_long left;
+ register u_long right;
+ register u_char *PT;
+ register u_char *bits;
+ register int i;
+
+ left = right = 0;
+ PT = permtab;
+ bits = databits;
+
+ for (i = 0; i < 32; i++) {
+ left <<= 1;
+ if (bits[PT[i]-1])
+ left |= 1;
+ }
+
+ for (i = 32; i < 64; i++) {
+ right <<= 1;
+ if (bits[PT[i]-1])
+ right |= 1;
+ }
+
+ *leftp = left;
+ *rightp = right;
+}
+
+
+/*
+ * doit - make up the tables
+ */
+static void
+doit()
+{
+ u_char bits[64];
+ u_long left;
+ u_long right;
+ int tabno;
+ int i;
+ int ind0, ind1, ind2, ind3;
+ int ind4, ind5, ind6, ind7;
+ int octbits;
+
+ memset((char *)bits, 0, sizeof bits);
+
+ /*
+ * Do the rounds for the IP table. We save the results of
+ * this as well as printing them. Note that this is the
+ * left-half table, the right half table will be identical.
+ */
+ printf("static u_long IP[256] = {");
+ for (tabno = 0; tabno < 4; tabno++) {
+ i = tabno * 8;
+ ind7 = IPLbits[i] - 1;
+ ind6 = IPLbits[i+1] - 1;
+ ind5 = IPLbits[i+2] - 1;
+ ind4 = IPLbits[i+3] - 1;
+ ind3 = IPLbits[i+4] - 1;
+ ind2 = IPLbits[i+5] - 1;
+ ind1 = IPLbits[i+6] - 1;
+ ind0 = IPLbits[i+7] - 1;
+ for (octbits = 0; octbits < 256; octbits++) {
+ if (octbits & (1 << 7))
+ bits[ind7] = 1;
+ if (octbits & (1 << 6))
+ bits[ind6] = 1;
+ if (octbits & (1 << 5))
+ bits[ind5] = 1;
+ if (octbits & (1 << 4))
+ bits[ind4] = 1;
+ if (octbits & (1 << 3))
+ bits[ind3] = 1;
+ if (octbits & (1 << 2))
+ bits[ind2] = 1;
+ if (octbits & (1 << 1))
+ bits[ind1] = 1;
+ if (octbits & 1)
+ bits[ind0] = 1;
+ perm(bits, IP, &left, &right);
+ bits[ind7] = 0;
+ bits[ind6] = 0;
+ bits[ind5] = 0;
+ bits[ind4] = 0;
+ bits[ind3] = 0;
+ bits[ind2] = 0;
+ bits[ind1] = 0;
+ bits[ind0] = 0;
+ if (right != 0) {
+ fprintf(stderr,
+ "IP tabno %d oct %d right not zero\n",
+ tabno, octbits);
+ exit(1);
+ }
+ if (tabno > 0) {
+ if ((IPL[octbits] << tabno) != left) {
+ fprintf(stderr,
+ "IP tabno %d oct %d IP %d left %d, IP != left\n",
+ tabno, octbits, IPL[octbits], left);
+ exit (1);
+ }
+ } else {
+ IPL[octbits] = left;
+ if (octbits == 255) {
+ printf(" 0x%08x", left);
+ } else if (octbits & 0x3) {
+ printf(" 0x%08x,", left);
+ } else {
+ printf("\n\t0x%08x,", left);
+ }
+ }
+ }
+ if (tabno == 0)
+ printf("\n};\n\n");
+ }
+
+ /*
+ * Next is the FP table, in big endian order
+ */
+ printf("#if BYTE_ORDER == LITTLE_ENDIAN\nstatic u_long FP[256] = {");
+ for (tabno = 3; tabno >= 0; tabno--) {
+ i = tabno * 8;
+ ind7 = FPLbits[i] - 1;
+ ind6 = FPLbits[i+1] - 1;
+ ind5 = FPLbits[i+2] - 1;
+ ind4 = FPLbits[i+3] - 1;
+ ind3 = FPLbits[i+4] - 1;
+ ind2 = FPLbits[i+5] - 1;
+ ind1 = FPLbits[i+6] - 1;
+ ind0 = FPLbits[i+7] - 1;
+ for (octbits = 0; octbits < 256; octbits++) {
+ if (octbits & (1 << 7))
+ bits[ind7] = 1;
+ if (octbits & (1 << 6))
+ bits[ind6] = 1;
+ if (octbits & (1 << 5))
+ bits[ind5] = 1;
+ if (octbits & (1 << 4))
+ bits[ind4] = 1;
+ if (octbits & (1 << 3))
+ bits[ind3] = 1;
+ if (octbits & (1 << 2))
+ bits[ind2] = 1;
+ if (octbits & (1 << 1))
+ bits[ind1] = 1;
+ if (octbits & 1)
+ bits[ind0] = 1;
+ perm(bits, FP, &left, &right);
+ bits[ind7] = 0;
+ bits[ind6] = 0;
+ bits[ind5] = 0;
+ bits[ind4] = 0;
+ bits[ind3] = 0;
+ bits[ind2] = 0;
+ bits[ind1] = 0;
+ bits[ind0] = 0;
+ if (right != 0) {
+ fprintf(stderr,
+ "FP tabno %d oct %d right not zero\n",
+ tabno, octbits);
+ exit(1);
+ }
+ if (tabno != 3) {
+ if ((FPL[octbits] << ((3-tabno)<<1)) != left) {
+ fprintf(stderr,
+ "FP tabno %d oct %d FP %x left %x, FP != left\n",
+ tabno, octbits, FPL[octbits], left);
+ exit (1);
+ }
+ } else {
+ FPL[octbits] = left;
+ if (octbits == 255) {
+ printf(" 0x%08x", left);
+ } else if (octbits & 0x3) {
+ printf(" 0x%08x,", left);
+ } else {
+ printf("\n\t0x%08x,", left);
+ }
+ }
+ }
+ if (tabno == 3)
+ printf("\n};\n");
+ }
+
+ /*
+ * Now reouput the FP table in order appropriate for little
+ * endian machines
+ */
+ printf("#else\nstatic u_long FP[256] = {");
+ for (octbits = 0; octbits < 256; octbits++) {
+ left = ((FPL[octbits] >> 24) & 0x000000ff)
+ | ((FPL[octbits] >> 8) & 0x0000ff00)
+ | ((FPL[octbits] << 8) & 0x00ff0000)
+ | ((FPL[octbits] << 24) & 0xff000000);
+ if (octbits == 255) {
+ printf(" 0x%08x", left);
+ } else if (octbits & 0x3) {
+ printf(" 0x%08x,", left);
+ } else {
+ printf("\n\t0x%08x,", left);
+ }
+ }
+ printf("\n};\n#endif\n");
+}
diff --git a/usr.sbin/xntpd/authstuff/makePC1.c b/usr.sbin/xntpd/authstuff/makePC1.c
new file mode 100644
index 0000000..8a49ec9
--- /dev/null
+++ b/usr.sbin/xntpd/authstuff/makePC1.c
@@ -0,0 +1,286 @@
+/*
+ * makePC1 - build custom permutted choice 1 tables
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+
+#include "ntp_stdlib.h"
+
+#define STREQ(a, b) (*(a) == *(b) && strcmp((a), (b)) == 0)
+
+char *progname;
+int debug;
+
+static void permute P((u_char *, u_long *, u_long *));
+static void doit P((void));
+
+/*
+ * main - parse arguments and handle options
+ */
+void
+main(argc, argv)
+int argc;
+char *argv[];
+{
+ int c;
+ int errflg = 0;
+ extern int ntp_optind;
+ extern char *ntp_optarg;
+
+ progname = argv[0];
+ while ((c = ntp_getopt(argc, argv, "d")) != EOF)
+ switch (c) {
+ case 'd':
+ ++debug;
+ break;
+ default:
+ errflg++;
+ break;
+ }
+ if (errflg) {
+ (void) fprintf(stderr, "usage: %s [-d]\n", progname);
+ exit(2);
+ }
+ doit();
+ exit(0);
+}
+
+/*
+ * Permuted choice 1 table, to produce the initial C. This table
+ * has had 1 subtracted from it to give it a zero base.
+ */
+static u_char PC1_C[28] = {
+ 56, 48, 40, 32, 24, 16, 8,
+ 0, 57, 49, 41, 33, 25, 17,
+ 9, 1, 58, 50, 42, 34, 26,
+ 18, 10, 2, 59, 51, 43, 35
+};
+
+/*
+ * Permuted choice 1 table, to produce the initial D. Again, 1 has
+ * been subtracted to match C language zero base arrays.
+ */
+static u_char PC1_D[28] = {
+ 62, 54, 46, 38, 30, 22, 14,
+ 6, 61, 53, 45, 37, 29, 21,
+ 13, 5, 60, 52, 44, 36, 28,
+ 20, 12, 4, 27, 19, 11, 3
+};
+
+/*
+ * permute - produce c and d for the given bits
+ */
+static void
+permute(bits, cp, dp)
+ u_char *bits;
+ u_long *cp;
+ u_long *dp;
+{
+ register int i;
+ register u_long mask;
+ u_char c[28];
+ u_char d[28];
+
+ memset((char *)c, 0, sizeof c);
+ memset((char *)d, 0, sizeof d);
+
+ for (i = 0; i < 28; i++) {
+ c[i] = bits[PC1_C[i]];
+ d[i] = bits[PC1_D[i]];
+ }
+
+ mask = 0x10000000;
+ *cp = *dp = 0;
+ for (i = 0; i < 28; i++) {
+ mask >>= 1;
+ if (c[i])
+ *cp |= mask;
+ if (d[i])
+ *dp |= mask;
+ }
+}
+
+
+/*
+ * bits from the left part of the key used to form the C subkey
+ */
+static int lc3[4] = { 0, 8, 16, 24 };
+
+/*
+ * bits from the left part of the key used to form the D subkey
+ */
+static int ld4[4] = { 3, 11, 19, 27 };
+
+/*
+ * bits from the right part of the key used to form the C subkey
+ */
+static int rc4[4] = { 32, 40, 48, 56 };
+
+/*
+ * bits from the right part of the key used to form the D subkey
+ */
+static int rd3[4] = { 36, 44, 52, 60 };
+
+static u_long PC_CL[8];
+static u_long PC_DL[16];
+static u_long PC_CR[16];
+static u_long PC_DR[8];
+
+
+/*
+ * doit - compute and print the four PC1 tables
+ */
+static void
+doit()
+{
+ int i;
+ int comb;
+ u_long c;
+ u_long d;
+ u_char bits[64];
+
+ memset((char *)bits, 0, sizeof bits);
+
+ printf("static u_long PC1_CL[8] = {");
+ for (i = 0; i < 4; i++) {
+ for (comb = 0; comb < 8; comb++) {
+ if (comb & 0x4)
+ bits[lc3[i]] = 1;
+ if (comb & 0x2)
+ bits[lc3[i]+1] = 1;
+ if (comb & 0x1)
+ bits[lc3[i]+2] = 1;
+ permute(bits, &c, &d);
+ bits[lc3[i]] = 0;
+ bits[lc3[i]+1] = 0;
+ bits[lc3[i]+2] = 0;
+ if (d != 0) {
+ (void) fprintf(stderr,
+ "Error PC_CL i %d comb %d\n", i, comb);
+ }
+ if (i == 0) {
+ PC_CL[comb] = c;
+ if ((comb & 0x3) == 0)
+ printf("\n\t0x%08x,", c);
+ else if (comb == 7)
+ printf(" 0x%08x\n};\n\n", c);
+ else
+ printf(" 0x%08x,", c);
+ } else {
+ if (c != PC_CL[comb] << i)
+ (void) fprintf(stderr,
+ "Error PC_CL 0x%08x c 0x%08x\n",
+ PC_CL[comb], c);
+ }
+ }
+ }
+
+ printf("static u_long PC1_DL[16] = {");
+ for (i = 0; i < 4; i++) {
+ for (comb = 0; comb < 16; comb++) {
+ if (comb & 0x8)
+ bits[ld4[i]] = 1;
+ if (comb & 0x4)
+ bits[ld4[i]+1] = 1;
+ if (comb & 0x2)
+ bits[ld4[i]+2] = 1;
+ if (comb & 0x1)
+ bits[ld4[i]+3] = 1;
+ permute(bits, &c, &d);
+ bits[ld4[i]] = 0;
+ bits[ld4[i]+1] = 0;
+ bits[ld4[i]+2] = 0;
+ bits[ld4[i]+3] = 0;
+ if (c != 0) {
+ (void) fprintf(stderr,
+ "Error PC_DL i %d comb %d\n", i, comb);
+ }
+ if (i == 0) {
+ PC_DL[comb] = d;
+ if ((comb & 0x3) == 0)
+ printf("\n\t0x%08x,", d);
+ else if (comb == 15)
+ printf(" 0x%08x\n};\n\n", d);
+ else
+ printf(" 0x%08x,", d);
+ } else {
+ if (d != PC_DL[comb] << i)
+ (void) fprintf(stderr,
+ "Error PC_DL 0x%08x c 0x%08x\n",
+ PC_DL[comb], d);
+ }
+ }
+ }
+
+ printf("static u_long PC1_CR[16] = {");
+ for (i = 0; i < 4; i++) {
+ for (comb = 0; comb < 16; comb++) {
+ if (comb & 0x8)
+ bits[rc4[i]] = 1;
+ if (comb & 0x4)
+ bits[rc4[i]+1] = 1;
+ if (comb & 0x2)
+ bits[rc4[i]+2] = 1;
+ if (comb & 0x1)
+ bits[rc4[i]+3] = 1;
+ permute(bits, &c, &d);
+ bits[rc4[i]] = 0;
+ bits[rc4[i]+1] = 0;
+ bits[rc4[i]+2] = 0;
+ bits[rc4[i]+3] = 0;
+ if (d != 0) {
+ (void) fprintf(stderr,
+ "Error PC_CR i %d comb %d\n", i, comb);
+ }
+ if (i == 0) {
+ PC_CR[comb] = c;
+ if ((comb & 0x3) == 0)
+ printf("\n\t0x%08x,", c);
+ else if (comb == 15)
+ printf(" 0x%08x\n};\n\n", c);
+ else
+ printf(" 0x%08x,", c);
+ } else {
+ if (c != PC_CR[comb] << i)
+ (void) fprintf(stderr,
+ "Error PC_CR 0x%08x c 0x%08x\n",
+ PC_CR[comb], c);
+ }
+ }
+ }
+
+ printf("static u_long PC1_DR[8] = {");
+ for (i = 0; i < 4; i++) {
+ for (comb = 0; comb < 8; comb++) {
+ if (comb & 0x4)
+ bits[rd3[i]] = 1;
+ if (comb & 0x2)
+ bits[rd3[i]+1] = 1;
+ if (comb & 0x1)
+ bits[rd3[i]+2] = 1;
+ permute(bits, &c, &d);
+ bits[rd3[i]] = 0;
+ bits[rd3[i]+1] = 0;
+ bits[rd3[i]+2] = 0;
+ if (c != 0) {
+ (void) fprintf(stderr,
+ "Error PC_DR i %d comb %d\n", i, comb);
+ }
+ if (i == 0) {
+ PC_DR[comb] = d;
+ if ((comb & 0x3) == 0)
+ printf("\n\t0x%08x,", d);
+ else if (comb == 7)
+ printf(" 0x%08x\n};\n\n", d);
+ else
+ printf(" 0x%08x,", d);
+ } else {
+ if (d != PC_DR[comb] << i)
+ (void) fprintf(stderr,
+ "Error PC_DR 0x%08x c 0x%08x\n",
+ PC_DR[comb], d);
+ }
+ }
+ }
+}
diff --git a/usr.sbin/xntpd/authstuff/makePC2.c b/usr.sbin/xntpd/authstuff/makePC2.c
new file mode 100644
index 0000000..e5121fd
--- /dev/null
+++ b/usr.sbin/xntpd/authstuff/makePC2.c
@@ -0,0 +1,238 @@
+/*
+ * makePC2 - build custom permutted choice 2 tables
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+
+#include "ntp_stdlib.h"
+
+#define STREQ(a, b) (*(a) == *(b) && strcmp((a), (b)) == 0)
+
+char *progname;
+int debug;
+
+static void permc P((u_char *, u_long *));
+static void permd P((u_char *, u_long *));
+static void doit P((void));
+
+/*
+ * main - parse arguments and handle options
+ */
+void
+main(argc, argv)
+int argc;
+char *argv[];
+{
+ int c;
+ int errflg = 0;
+ extern int ntp_optind;
+ extern char *ntp_optarg;
+
+ progname = argv[0];
+ while ((c = ntp_getopt(argc, argv, "d")) != EOF)
+ switch (c) {
+ case 'd':
+ ++debug;
+ break;
+ default:
+ errflg++;
+ break;
+ }
+ if (errflg) {
+ (void) fprintf(stderr, "usage: %s [-d]\n", progname);
+ exit(2);
+ }
+ doit();
+ exit(0);
+}
+
+/*
+ * Permuted choice 2 table. This actually produces the low order 24
+ * bits of the subkey Ki from the 28 bit value of Ci. This has had
+ * 1 subtracted from it to give a zero base.
+ */
+static u_char PC2_C[24] = {
+ 13, 16, 10, 23, 0, 4,
+ 2, 27, 14, 5, 20, 9,
+ 22, 18, 11, 3, 25, 7,
+ 15, 6, 26, 19, 12, 1
+};
+
+/*
+ * Permuted choice 2 table, operating on the 28 Di bits to produce the
+ * high order 24 bits of subkey Ki. This has had 29 subtracted from
+ * it to give it a zero base into our D bit array.
+ */
+static u_char PC2_D[24] = {
+ 12, 23, 2, 8, 18, 26,
+ 1, 11, 22, 16, 4, 19,
+ 15, 20, 10, 27, 5, 24,
+ 17, 13, 21, 7, 0, 3
+};
+
+u_long masks[4] = { 0x40000000, 0x400000, 0x4000, 0x40 };
+
+
+/*
+ * permc - permute C, producing a four byte result
+ */
+static void
+permc(bits, resp)
+ u_char *bits;
+ u_long *resp;
+{
+ register int part;
+ register int i;
+ register u_long mask;
+ u_char res[24];
+
+ memset((char *)res, 0, sizeof res);
+
+ for (i = 0; i < 24; i++) {
+ res[i] = bits[PC2_C[i]];
+ }
+
+ *resp = 0;
+ for (part = 0; part < 4; part++) {
+ mask = masks[part];
+ for (i = part*6; i < (part+1)*6; i++) {
+ mask >>= 1;
+ if (res[i])
+ *resp |= mask;
+ }
+ }
+}
+
+/*
+ * permd - permute D, producing a four byte result
+ */
+static void
+permd(bits, resp)
+ u_char *bits;
+ u_long *resp;
+{
+ register int part;
+ register int i;
+ register u_long mask;
+ u_char res[24];
+
+ memset((char *)res, 0, sizeof res);
+
+ for (i = 0; i < 24; i++) {
+ res[i] = bits[PC2_D[i]];
+ }
+
+ *resp = 0;
+ for (part = 0; part < 4; part++) {
+ mask = masks[part];
+ for (i = part*6; i < (part+1)*6; i++) {
+ mask >>= 1;
+ if (res[i])
+ *resp |= mask;
+ }
+ }
+}
+
+
+/*
+ * bits used for each round in C
+ */
+static int cbits[4][6] = {
+ 0, 1, 2, 3, 4, 5,
+ 6, 7, 9, 10, 11, 12,
+ 13, 14, 15, 16, 22, 23,
+ 18, 19, 20, 25, 26, 27
+};
+
+
+/*
+ * bits used for each round in D
+ */
+static int dbits[4][6] = {
+ 0, 1, 2, 3, 4, 5,
+ 7, 8, 10, 11, 12, 13,
+ 15, 16, 17, 18, 19, 20,
+ 21, 22, 23, 24, 26, 27
+};
+
+
+/*
+ * doit - compute and print the four PC1 tables
+ */
+static void
+doit()
+{
+ int i;
+ int comb;
+ u_long res;
+ u_char bits[28];
+
+ memset((char *)bits, 0, sizeof bits);
+
+ printf("static u_long PC2_C[4][64] = {");
+ for (i = 0; i < 4; i++) {
+ for (comb = 0; comb < 64; comb++) {
+ if (comb & 0x20)
+ bits[cbits[i][0]] = 1;
+ if (comb & 0x10)
+ bits[cbits[i][1]] = 1;
+ if (comb & 0x8)
+ bits[cbits[i][2]] = 1;
+ if (comb & 0x4)
+ bits[cbits[i][3]] = 1;
+ if (comb & 0x2)
+ bits[cbits[i][4]] = 1;
+ if (comb & 0x1)
+ bits[cbits[i][5]] = 1;
+ permc(bits, &res);
+ bits[cbits[i][0]] = 0;
+ bits[cbits[i][1]] = 0;
+ bits[cbits[i][2]] = 0;
+ bits[cbits[i][3]] = 0;
+ bits[cbits[i][4]] = 0;
+ bits[cbits[i][5]] = 0;
+ if ((comb & 0x3) == 0)
+ printf("\n\t0x%08x,", res);
+ else if (comb == 63 && i == 3)
+ printf(" 0x%08x\n};\n\n", res);
+ else if (comb == 63)
+ printf(" 0x%08x,\n", res);
+ else
+ printf(" 0x%08x,", res);
+ }
+ }
+
+ printf("static u_long PC2_D[4][64] = {");
+ for (i = 0; i < 4; i++) {
+ for (comb = 0; comb < 64; comb++) {
+ if (comb & 0x20)
+ bits[dbits[i][0]] = 1;
+ if (comb & 0x10)
+ bits[dbits[i][1]] = 1;
+ if (comb & 0x8)
+ bits[dbits[i][2]] = 1;
+ if (comb & 0x4)
+ bits[dbits[i][3]] = 1;
+ if (comb & 0x2)
+ bits[dbits[i][4]] = 1;
+ if (comb & 0x1)
+ bits[dbits[i][5]] = 1;
+ permd(bits, &res);
+ bits[dbits[i][0]] = 0;
+ bits[dbits[i][1]] = 0;
+ bits[dbits[i][2]] = 0;
+ bits[dbits[i][3]] = 0;
+ bits[dbits[i][4]] = 0;
+ bits[dbits[i][5]] = 0;
+ if ((comb & 0x3) == 0)
+ printf("\n\t0x%08x,", res);
+ else if (comb == 63 && i == 3)
+ printf(" 0x%08x\n};\n\n", res);
+ else if (comb == 63)
+ printf(" 0x%08x,\n", res);
+ else
+ printf(" 0x%08x,", res);
+ }
+ }
+}
diff --git a/usr.sbin/xntpd/authstuff/makeSP.c b/usr.sbin/xntpd/authstuff/makeSP.c
new file mode 100644
index 0000000..7daefa6
--- /dev/null
+++ b/usr.sbin/xntpd/authstuff/makeSP.c
@@ -0,0 +1,183 @@
+/*
+ * makeSP - build combination S and P tables for quick DES computation
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+
+#include "ntp_stdlib.h"
+
+#define STREQ(a, b) (*(a) == *(b) && strcmp((a), (b)) == 0)
+
+char *progname;
+int debug;
+
+static void selperm P((int, int, u_long *));
+static void doit P((void));
+
+/*
+ * main - parse arguments and handle options
+ */
+void
+main(argc, argv)
+int argc;
+char *argv[];
+{
+ int c;
+ int errflg = 0;
+ extern int ntp_optind;
+ extern char *ntp_optarg;
+
+ progname = argv[0];
+ while ((c = ntp_getopt(argc, argv, "d")) != EOF)
+ switch (c) {
+ case 'd':
+ ++debug;
+ break;
+ default:
+ errflg++;
+ break;
+ }
+ if (errflg) {
+ (void) fprintf(stderr, "usage: %s [-d]\n", progname);
+ exit(2);
+ }
+ doit();
+ exit(0);
+}
+
+
+/*
+ * The cipher selection function tables. These turn 6 bit data back
+ * into 4 bit data.
+ */
+u_char S[8][64] = {
+ 14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7,
+ 0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8,
+ 4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0,
+ 15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13,
+
+ 15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10,
+ 3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5,
+ 0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15,
+ 13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9,
+
+ 10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8,
+ 13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1,
+ 13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7,
+ 1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12,
+
+ 7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15,
+ 13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9,
+ 10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4,
+ 3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14,
+
+ 2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9,
+ 14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6,
+ 4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14,
+ 11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3,
+
+ 12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11,
+ 10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8,
+ 9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6,
+ 4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13,
+
+ 4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1,
+ 13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6,
+ 1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2,
+ 6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12,
+
+ 13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7,
+ 1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2,
+ 7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8,
+ 2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11
+};
+
+/*
+ * Cipher function permutation table
+ */
+u_char PT[32] = {
+ 16, 7, 20, 21,
+ 29, 12, 28, 17,
+ 1, 15, 23, 26,
+ 5, 18, 31, 10,
+ 2, 8, 24, 14,
+ 32, 27, 3, 9,
+ 19, 13, 30, 6,
+ 22, 11, 4, 25
+};
+
+
+/*
+ * Bits array. We keep this zeroed.
+ */
+u_char bits[32];
+
+
+/*
+ * selperm - run six bit data through the given selection table, then
+ * through the PT table to produce a long output.
+ */
+static void
+selperm(selnumber, sixbits, resp)
+ int selnumber;
+ int sixbits;
+ u_long *resp;
+{
+ register u_long res;
+ register int selno;
+ register int i;
+ register int ind;
+
+ selno = selnumber;
+ i = sixbits;
+ ind = (i & 0x20) | ((i >> 1) & 0xf) | ((i & 0x1) << 4);
+ i = S[selno][ind];
+
+ for (ind = 0; ind < 4; ind++) {
+ if (i & 0x8)
+ bits[4*selno + ind] = 1;
+ i <<= 1;
+ }
+
+ res = 0;
+ for (i = 0; i < 32; i++) {
+ res <<= 1;
+ if (bits[PT[i]-1])
+ res |= 1;
+ }
+
+ *resp = res;
+ bits[4*selno] = 0;
+ bits[4*selno + 1] = 0;
+ bits[4*selno + 2] = 0;
+ bits[4*selno + 3] = 0;
+}
+
+
+/*
+ * doit - compute and print the 8 SP tables
+ */
+static void
+doit()
+{
+ int selno;
+ u_long result;
+ int sixbits;
+
+ memset((char *)bits, 0, sizeof bits);
+ printf("static u_long SP[8][64] = {");
+ for (selno = 0; selno < 8; selno++) {
+ for (sixbits = 0; sixbits < 64; sixbits++) {
+ selperm(selno, sixbits, &result);
+ if ((sixbits & 0x3) == 0)
+ printf("\n\t0x%08x,", result);
+ else if (sixbits == 63 && selno == 7)
+ printf(" 0x%08x\n};\n", result);
+ else if (sixbits == 63)
+ printf(" 0x%08x,\n", result);
+ else
+ printf(" 0x%08x,", result);
+ }
+ }
+}
diff --git a/usr.sbin/xntpd/authstuff/md5_sample_output b/usr.sbin/xntpd/authstuff/md5_sample_output
new file mode 100644
index 0000000..a7d4282
--- /dev/null
+++ b/usr.sbin/xntpd/authstuff/md5_sample_output
@@ -0,0 +1,8 @@
+MD5 test suite results:
+d41d8cd98f00b204e9800998ecf8427e ""
+0cc175b9c0f1b6a831c399e269772661 "a"
+900150983cd24fb0d6963f7d28e17f72 "abc"
+f96b697d7cb7938d525a2f31aaf161d0 "message digest"
+c3fcd3d76192e4007dfb496cca67e13b "abcdefghijklmnopqrstuvwxyz"
+d174ab98d277d9f5a5611c2c9f419d9f "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
+57edf4a22be3c955ac49da2e2107b67a "12345678901234567890123456789012345678901234567890123456789012345678901234567890"
diff --git a/usr.sbin/xntpd/authstuff/md5driver.c b/usr.sbin/xntpd/authstuff/md5driver.c
new file mode 100644
index 0000000..0d7e132
--- /dev/null
+++ b/usr.sbin/xntpd/authstuff/md5driver.c
@@ -0,0 +1,211 @@
+/*
+ ***********************************************************************
+ ** md5driver.c -- sample test routines **
+ ** RSA Data Security, Inc. MD5 Message-Digest Algorithm **
+ ** Created: 2/16/90 RLR **
+ ** Updated: 1/91 SRD **
+ ** Updated: 7/91 SRD Removed file "foo" from test suite **
+ ***********************************************************************
+ */
+
+/*
+ ***********************************************************************
+ ** Copyright (C) 1990, RSA Data Security, Inc. All rights reserved. **
+ ** **
+ ** RSA Data Security, Inc. makes no representations concerning **
+ ** either the merchantability of this software or the suitability **
+ ** of this software for any particular purpose. It is provided "as **
+ ** is" without express or implied warranty of any kind. **
+ ** **
+ ** These notices must be retained in any copies of any part of this **
+ ** documentation and/or software. **
+ ***********************************************************************
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <time.h>
+#if defined(SYS_BSDI) || defined(SYS_44BSD)
+#include <sys/time.h>
+#endif /* SYS_BSDI */
+#include "md5.h"
+
+#ifndef MD5
+#define MD5
+#endif
+#include "ntp_string.h"
+#include "ntp_stdlib.h"
+
+/* Prints message digest buffer in mdContext as 32 hexadecimal digits.
+ Order is from low-order byte to high-order byte of digest.
+ Each byte is printed with high-order hexadecimal digit first.
+ */
+static void
+MDPrint (mdContext)
+MD5_CTX *mdContext;
+{
+ int i;
+
+ for (i = 0; i < 16; i++)
+ printf ("%02x", mdContext->digest[i]);
+}
+
+/* size of test block */
+#define TEST_BLOCK_SIZE 1000
+
+/* number of blocks to process */
+#define TEST_BLOCKS 10000
+
+/* number of test bytes = TEST_BLOCK_SIZE * TEST_BLOCKS */
+static long TEST_BYTES = (long)TEST_BLOCK_SIZE * (long)TEST_BLOCKS;
+
+/* A time trial routine, to measure the speed of MD5.
+ Measures wall time required to digest TEST_BLOCKS * TEST_BLOCK_SIZE
+ characters.
+ */
+static void
+MDTimeTrial ()
+{
+ MD5_CTX mdContext;
+ time_t endTime, startTime;
+ unsigned char data[TEST_BLOCK_SIZE];
+ unsigned int i;
+
+ /* initialize test data */
+ for (i = 0; i < TEST_BLOCK_SIZE; i++)
+ data[i] = (unsigned char)(i & 0xFF);
+
+ /* start timer */
+ printf ("MD5 time trial. Processing %ld characters...\n", (long)TEST_BYTES);
+ time (&startTime);
+
+ /* digest data in TEST_BLOCK_SIZE byte blocks */
+ MD5Init (&mdContext);
+ for (i = TEST_BLOCKS; i > 0; i--)
+ MD5Update (&mdContext, data, TEST_BLOCK_SIZE);
+ MD5Final (&mdContext);
+
+ /* stop timer, get time difference */
+ time (&endTime);
+ MDPrint (&mdContext);
+ printf (" is digest of test input.\n");
+ printf
+ ("Seconds to process test input: %ld\n", (long)endTime-startTime);
+ printf
+ ("Characters processed per second: %ld\n",
+ (long)(TEST_BYTES/(endTime-startTime)));
+}
+
+/* Computes the message digest for string inString.
+ Prints out message digest, a space, the string (in quotes) and a
+ carriage return.
+ */
+static void
+MDString (inString)
+char *inString;
+{
+ MD5_CTX mdContext;
+ unsigned int len = strlen (inString);
+
+ MD5Init (&mdContext);
+ MD5Update (&mdContext, inString, len);
+ MD5Final (&mdContext);
+ MDPrint (&mdContext);
+ printf (" \"%s\"\n", inString);
+}
+
+/* Computes the message digest for a specified file.
+ Prints out message digest, a space, the file name, and a carriage
+ return.
+ */
+static void
+MDFile (filename)
+char *filename;
+{
+ FILE *inFile = fopen (filename, "rb");
+ MD5_CTX mdContext;
+ int bytes;
+ unsigned char data[1024];
+
+ if (inFile == NULL) {
+ printf ("%s can't be opened.\n", filename);
+ return;
+ }
+
+ MD5Init (&mdContext);
+ while ((bytes = fread (data, 1, 1024, inFile)) != 0)
+ MD5Update (&mdContext, data, bytes);
+ MD5Final (&mdContext);
+ MDPrint (&mdContext);
+ printf (" %s\n", filename);
+ fclose (inFile);
+}
+
+/* Writes the message digest of the data from stdin onto stdout,
+ followed by a carriage return.
+ */
+static void
+MDFilter ()
+{
+ MD5_CTX mdContext;
+ int bytes;
+ unsigned char data[16];
+
+ MD5Init (&mdContext);
+ while ((bytes = fread (data, 1, 16, stdin)) != 0)
+ MD5Update (&mdContext, data, bytes);
+ MD5Final (&mdContext);
+ MDPrint (&mdContext);
+ printf ("\n");
+}
+
+/* Runs a standard suite of test data.
+ */
+static void
+MDTestSuite ()
+{
+ printf ("MD5 test suite results:\n");
+ MDString ("");
+ MDString ("a");
+ MDString ("abc");
+ MDString ("message digest");
+ MDString ("abcdefghijklmnopqrstuvwxyz");
+ MDString
+ ("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789");
+ MDString
+ ("12345678901234567890123456789012345678901234567890123456789012345678901234567890");
+}
+
+void
+main (argc, argv)
+int argc;
+char *argv[];
+{
+ int i;
+
+ /* For each command line argument in turn:
+ ** filename -- prints message digest and name of file
+ ** -sstring -- prints message digest and contents of string
+ ** -t -- prints time trial statistics for 10M
+ characters
+ ** -x -- execute a standard suite of test data
+ ** (no args) -- writes messages digest of stdin onto stdout
+ */
+ if (argc == 1)
+ MDFilter ();
+ else
+ for (i = 1; i < argc; i++)
+ if (argv[i][0] == '-' && argv[i][1] == 's')
+ MDString (argv[i] + 2);
+ else if (strcmp (argv[i], "-t") == 0)
+ MDTimeTrial ();
+ else if (strcmp (argv[i], "-x") == 0)
+ MDTestSuite ();
+ else MDFile (argv[i]);
+}
+
+/*
+ ***********************************************************************
+ ** End of md5driver.c **
+ ******************************** (cut) ********************************
+ */
diff --git a/usr.sbin/xntpd/authstuff/mkrandkeys.c b/usr.sbin/xntpd/authstuff/mkrandkeys.c
new file mode 100644
index 0000000..0edf733
--- /dev/null
+++ b/usr.sbin/xntpd/authstuff/mkrandkeys.c
@@ -0,0 +1,167 @@
+/*
+ * mkrandkeys - make a key file for xntpd with some quite random keys
+ */
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include "ntp_stdlib.h"
+
+#define STREQ(a, b) (*(a) == *(b) && strcmp((a), (b)) == 0)
+
+char *progname;
+int debug;
+
+u_long keydata[2];
+
+int std = 1; /* DES standard key format */
+u_char dokey[16] = { 0 };
+
+static void rand_data P((u_long *));
+
+/*
+ * main - parse arguments and handle options
+ */
+void
+main(argc, argv)
+int argc;
+char *argv[];
+{
+ int c;
+ int i;
+ int j;
+ int errflg = 0;
+ int numkeys;
+ u_long tmp;
+ char *passwd;
+ extern int ntp_optind;
+ extern char *ntp_optarg;
+ extern char *getpass();
+
+ numkeys = 0;
+ progname = argv[0];
+ passwd = NULL;
+ while ((c = ntp_getopt(argc, argv, "dnp:s")) != EOF)
+ switch (c) {
+ case 'd':
+ ++debug;
+ break;
+ case 'n':
+ std = 0;
+ break;
+ case 'p':
+ passwd = ntp_optarg;
+ break;
+ case 's':
+ std = 1;
+ break;
+ default:
+ errflg++;
+ break;
+ }
+
+ numkeys = 0;
+ for (; !errflg && ntp_optind < argc; ntp_optind++) {
+ c = atoi(argv[ntp_optind]);
+ if (c <= 0 || c > 15) {
+ (void) fprintf(stderr, "%s: invalid key number `%s'\n",
+ progname, argv[ntp_optind]);
+ exit(2);
+ }
+ dokey[c] = 1;
+ numkeys++;
+ }
+
+ if (errflg || numkeys == 0) {
+ (void) fprintf(stderr,
+ "usage: %s [-ns] [-p seed] key# [key# ...]\n",
+ progname);
+ exit(2);
+ }
+
+ while (passwd == 0 || *passwd == '\0') {
+ passwd = getpass("Seed: ");
+ if (*passwd == '\0') {
+ (void) fprintf(stderr,
+ "better use a better seed than that\n");
+ }
+ }
+
+ keydata[0] = keydata[1] = 0;
+ for (i = 0; i < 8 && *passwd != '\0'; i++) {
+ keydata[i/4] |= ((((u_long)(*passwd))&0xff)<<(1+((3-(i%4))*8)));
+ passwd++;
+ }
+
+ for (i = 1; i <= 15; i++) {
+ if (dokey[i]) {
+ for (c = 0, tmp = 0; c < 32; c += 4)
+ tmp |= (i << c);
+ keydata[0] ^= tmp;
+ keydata[1] ^= tmp;
+ rand_data(keydata);
+ DESauth_parity(keydata);
+
+ if (std) {
+ (void)printf("%-2d S\t%08x%08x\n",
+ i, keydata[0], keydata[1]);
+ } else {
+ for (j = 0; j < 2; j++) {
+ keydata[j]
+ = ((keydata[j] & 0xfefefefe) >> 1)
+ | ((keydata[j] & 0x01010101) << 7);
+ }
+ (void)printf("%-2d N\t%08x%08x\n",
+ i, keydata[0], keydata[1]);
+ }
+ }
+ }
+ exit(0);
+}
+
+char *volatile_file[] = {
+ "/bin/echo",
+ "/bin/sh",
+ "/bin/cat",
+ "/bin/ls",
+ "/bin/stty",
+ "/bin/date",
+ "/bin/cat",
+ "/bin/cc",
+ "/etc/motd",
+ "/etc/utmp",
+ "/dev/kmem",
+ "/dev/null",
+ "",
+};
+
+#define NEXT(X) (0x1e1f2f2d*(X) + 0x361962e9)
+
+static void
+rand_data(data)
+ u_long *data;
+{
+ register i;
+ struct stat buf;
+ extern long time();
+ char ekeys[128], dkeys[128];
+
+ *data ^= 0x9662f394;
+ *(data+1) ^= 0x9f17c55f;
+ DESauth_subkeys(data, ekeys, dkeys);
+ *data ^= NEXT(getpid() + (getuid() << 16));
+ *(data+1) ^= NEXT(time((long *)0));
+ DESauth_des(data, ekeys);
+ for (i = 0; strlen(volatile_file[i]); i++) {
+ if (stat(volatile_file[i], &buf) == -1)
+ continue;
+ if (i & 1) {
+ *data ^= NEXT(buf.st_atime);
+ *(data+1) ^= NEXT(buf.st_mtime);
+ } else {
+ *data ^= NEXT(buf.st_mtime);
+ *(data+1) ^= NEXT(buf.st_atime);
+ }
+ DESauth_des(data, ekeys);
+ }
+}
diff --git a/usr.sbin/xntpd/authstuff/omakeIPFP.c b/usr.sbin/xntpd/authstuff/omakeIPFP.c
new file mode 100644
index 0000000..92d87be
--- /dev/null
+++ b/usr.sbin/xntpd/authstuff/omakeIPFP.c
@@ -0,0 +1,361 @@
+/*
+ * makeIPFP - make fast DES IP and FP tables
+ *
+ * This is an older version which generated tables half the size of
+ * the current version, but which took about double the CPU time to
+ * compute permutations from these tables. Since the CPU spent on the
+ * permutations is small compared to the CPU spent in the cipher code,
+ * I may go back to the smaller tables to save the space some day.
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+
+#include "ntp_stdlib.h"
+
+#define STREQ(a, b) (*(a) == *(b) && strcmp((a), (b)) == 0)
+
+u_long IPL[8][16];
+u_long FPL[8][16];
+
+char *progname;
+int debug;
+
+static void perm P((u_char *, u_char *, u_long *, u_long *));
+static void doit P((void));
+
+/*
+ * main - parse arguments and handle options
+ */
+void
+main(argc, argv)
+int argc;
+char *argv[];
+{
+ int c;
+ int errflg = 0;
+ extern int ntp_optind;
+ extern char *ntp_optarg;
+
+ progname = argv[0];
+ while ((c = ntp_getopt(argc, argv, "d")) != EOF)
+ switch (c) {
+ case 'd':
+ ++debug;
+ break;
+ default:
+ errflg++;
+ break;
+ }
+ if (errflg) {
+ (void) fprintf(stderr, "usage: %s [-d]\n", progname);
+ exit(2);
+ }
+ doit();
+ exit(0);
+}
+
+
+/*
+ * Initial permutation table
+ */
+u_char IP[64] = {
+ 58, 50, 42, 34, 26, 18, 10, 2,
+ 60, 52, 44, 36, 28, 20, 12, 4,
+ 62, 54, 46, 38, 30, 22, 14, 6,
+ 64, 56, 48, 40, 32, 24, 16, 8,
+ 57, 49, 41, 33, 25, 17, 9, 1,
+ 59, 51, 43, 35, 27, 19, 11, 3,
+ 61, 53, 45, 37, 29, 21, 13, 5,
+ 63, 55, 47, 39, 31, 23, 15, 7
+};
+
+/*
+ * Inverse initial permutation table
+ */
+u_char FP[64] = {
+ 40, 8, 48, 16, 56, 24, 64, 32,
+ 39, 7, 47, 15, 55, 23, 63, 31,
+ 38, 6, 46, 14, 54, 22, 62, 30,
+ 37, 5, 45, 13, 53, 21, 61, 29,
+ 36, 4, 44, 12, 52, 20, 60, 28,
+ 35, 3, 43, 11, 51, 19, 59, 27,
+ 34, 2, 42, 10, 50, 18, 58, 26,
+ 33, 1, 41, 9, 49, 17, 57, 25
+};
+
+
+/*
+ * Bit order after the operation
+ *
+ * ((left & 0x55555555) << 1) | (right & 0x55555555)
+ */
+u_char IPLbits[32] = {
+ 2, 34, 4, 36, 6, 38, 8, 40,
+ 10, 42, 12, 44, 14, 46, 16, 48,
+ 18, 50, 20, 52, 22, 54, 24, 56,
+ 26, 58, 28, 60, 30, 62, 32, 64
+};
+
+
+/*
+ * Bit order after the operation
+ *
+ * (left & 0xaaaaaaaa) | ((right & 0xaaaaaaaa) >> 1)
+ */
+u_char IPRbits[32] = {
+ 1, 33, 3, 35, 5, 37, 7, 39,
+ 9, 41, 11, 43, 13, 45, 15, 47,
+ 17, 49, 19, 51, 21, 53, 23, 55,
+ 25, 57, 27, 59, 29, 61, 31, 63
+};
+
+
+/*
+ * Bit order after the operation
+ *
+ * ((left & 0x0f0f0f0f) << 4) | (right & 0x0f0f0f0f)
+ */
+u_char FPLbits[32] = {
+ 5, 6, 7, 8, 37, 38, 39, 40,
+ 13, 14, 15, 16, 45, 46, 47, 48,
+ 21, 22, 23, 24, 53, 54, 55, 56,
+ 29, 30, 31, 32, 61, 62, 63, 64
+};
+
+
+/*
+ * Bit order after the operation
+ *
+ * (left & 0xf0f0f0f0) | ((right & 0xf0f0f0f0) >> 4)
+ */
+u_char FPRbits[32] = {
+ 1, 2, 3, 4, 33, 34, 35, 36,
+ 9, 10, 11, 12, 41, 42, 43, 44,
+ 17, 18, 19, 20, 49, 50, 51, 52,
+ 25, 26, 27, 28, 57, 58, 59, 60
+};
+
+
+/*
+ * perm - do a permutation with the given table
+ */
+static void
+perm(databits, permtab, leftp, rightp)
+ u_char *databits;
+ u_char *permtab;
+ u_long *leftp;
+ u_long *rightp;
+{
+ register u_long left;
+ register u_long right;
+ register u_char *PT;
+ register u_char *bits;
+ register int i;
+
+ left = right = 0;
+ PT = permtab;
+ bits = databits;
+
+ for (i = 0; i < 32; i++) {
+ left <<= 1;
+ if (bits[PT[i]-1])
+ left |= 1;
+ }
+
+ for (i = 32; i < 64; i++) {
+ right <<= 1;
+ if (bits[PT[i]-1])
+ right |= 1;
+ }
+
+ *leftp = left;
+ *rightp = right;
+}
+
+
+/*
+ * doit - make up the tables
+ */
+static void
+doit()
+{
+ u_char bits[64];
+ u_long left;
+ u_long right;
+ int tabno;
+ int i;
+ int ind0, ind1, ind2, ind3;
+ int quadbits;
+
+ memset((char *)bits, 0, sizeof bits);
+
+ /*
+ * Do the rounds for the IPL table. We save the results of
+ * this as well as printing them. Note that this is the
+ * left-half table.
+ */
+ printf("static u_long IP[8][16] = {");
+ for (tabno = 0; tabno < 8; tabno++) {
+ i = tabno * 4;
+ ind3 = IPLbits[i] - 1;
+ ind2 = IPLbits[i+1] - 1;
+ ind1 = IPLbits[i+2] - 1;
+ ind0 = IPLbits[i+3] - 1;
+ for (quadbits = 0; quadbits < 16; quadbits++) {
+ if (quadbits & (1 << 3))
+ bits[ind3] = 1;
+ if (quadbits & (1 << 2))
+ bits[ind2] = 1;
+ if (quadbits & (1 << 1))
+ bits[ind1] = 1;
+ if (quadbits & 1)
+ bits[ind0] = 1;
+ perm(bits, IP, &left, &right);
+ bits[ind3] = 0;
+ bits[ind2] = 0;
+ bits[ind1] = 0;
+ bits[ind0] = 0;
+ if (right != 0) {
+ fprintf(stderr,
+ "IPL tabno %d quad %d right not zero\n",
+ tabno, quadbits);
+ exit(1);
+ }
+ IPL[tabno][quadbits] = left;
+ if (quadbits == 15 && tabno == 7) {
+ printf(" 0x%08x", left);
+ } else if (quadbits & 0x3) {
+ printf(" 0x%08x,", left);
+ } else {
+ printf("\n\t0x%08x,", left);
+ }
+ }
+ if (tabno == 7)
+ printf("\n};\n");
+ printf("\n");
+ }
+
+ /*
+ * Compute the right half of the same table. I noticed this table
+ * was the same as the previous one, just by luck, so we don't
+ * actually have to do this. Do it anyway just for a check.
+ */
+ for (tabno = 0; tabno < 8; tabno++) {
+ i = tabno * 4;
+ ind3 = IPRbits[i] - 1;
+ ind2 = IPRbits[i+1] - 1;
+ ind1 = IPRbits[i+2] - 1;
+ ind0 = IPRbits[i+3] - 1;
+ for (quadbits = 0; quadbits < 16; quadbits++) {
+ if (quadbits & (1 << 3))
+ bits[ind3] = 1;
+ if (quadbits & (1 << 2))
+ bits[ind2] = 1;
+ if (quadbits & (1 << 1))
+ bits[ind1] = 1;
+ if (quadbits & 1)
+ bits[ind0] = 1;
+ perm(bits, IP, &left, &right);
+ bits[ind3] = 0;
+ bits[ind2] = 0;
+ bits[ind1] = 0;
+ bits[ind0] = 0;
+ if (left != 0) {
+ fprintf(stderr,
+ "IPR tabno %d quad %d left not zero\n",
+ tabno, quadbits);
+ exit(1);
+ }
+ if (right != IPL[tabno][quadbits]) {
+ fprintf(stderr,
+ "IPR tabno %d quad %d: 0x%08x not same as 0x%08x\n",
+ tabno, quadbits, right,IPL[tabno][quadbits]);
+ exit(1);
+ }
+ }
+ }
+
+ /*
+ * Next are the FP tables
+ */
+ printf("static u_long FP[8][16] = {");
+ for (tabno = 0; tabno < 8; tabno++) {
+ i = tabno * 4;
+ ind3 = FPLbits[i] - 1;
+ ind2 = FPLbits[i+1] - 1;
+ ind1 = FPLbits[i+2] - 1;
+ ind0 = FPLbits[i+3] - 1;
+ for (quadbits = 0; quadbits < 16; quadbits++) {
+ if (quadbits & (1 << 3))
+ bits[ind3] = 1;
+ if (quadbits & (1 << 2))
+ bits[ind2] = 1;
+ if (quadbits & (1 << 1))
+ bits[ind1] = 1;
+ if (quadbits & 1)
+ bits[ind0] = 1;
+ perm(bits, FP, &left, &right);
+ bits[ind3] = 0;
+ bits[ind2] = 0;
+ bits[ind1] = 0;
+ bits[ind0] = 0;
+ if (right != 0) {
+ fprintf(stderr,
+ "FPL tabno %d quad %d right not zero\n",
+ tabno, quadbits);
+ exit(1);
+ }
+ FPL[tabno][quadbits] = left;
+ if (quadbits == 15 && tabno == 7) {
+ printf(" 0x%08x", left);
+ } else if (quadbits & 0x3) {
+ printf(" 0x%08x,", left);
+ } else {
+ printf("\n\t0x%08x,", left);
+ }
+ }
+ if (tabno == 7)
+ printf("\n};");
+ printf("\n");
+ }
+
+ /*
+ * Right half of same set of tables. This was symmetric too.
+ * Amazing!
+ */
+ for (tabno = 0; tabno < 8; tabno++) {
+ i = tabno * 4;
+ ind3 = FPRbits[i] - 1;
+ ind2 = FPRbits[i+1] - 1;
+ ind1 = FPRbits[i+2] - 1;
+ ind0 = FPRbits[i+3] - 1;
+ for (quadbits = 0; quadbits < 16; quadbits++) {
+ if (quadbits & (1 << 3))
+ bits[ind3] = 1;
+ if (quadbits & (1 << 2))
+ bits[ind2] = 1;
+ if (quadbits & (1 << 1))
+ bits[ind1] = 1;
+ if (quadbits & 1)
+ bits[ind0] = 1;
+ perm(bits, FP, &left, &right);
+ bits[ind3] = 0;
+ bits[ind2] = 0;
+ bits[ind1] = 0;
+ bits[ind0] = 0;
+ if (left != 0) {
+ fprintf(stderr,
+ "FPR tabno %d quad %d left not zero\n",
+ tabno, quadbits);
+ exit(1);
+ }
+ if (right != FPL[tabno][quadbits]) {
+ fprintf(stderr,
+ "FPR tabno %d quad %d: 0x%08x not same as 0x%08x\n",
+ tabno, quadbits, right,FPL[tabno][quadbits]);
+ exit(1);
+ }
+ }
+ }
+}
diff --git a/usr.sbin/xntpd/authstuff/results b/usr.sbin/xntpd/authstuff/results
new file mode 100644
index 0000000..305a179
--- /dev/null
+++ b/usr.sbin/xntpd/authstuff/results
@@ -0,0 +1,2 @@
+odin/1000000: 0.000145
+idavolde/1000000: 0.000451
diff --git a/usr.sbin/xntpd/authstuff/unixcert.c b/usr.sbin/xntpd/authstuff/unixcert.c
new file mode 100644
index 0000000..9ec2c98
--- /dev/null
+++ b/usr.sbin/xntpd/authstuff/unixcert.c
@@ -0,0 +1,156 @@
+/*
+ * This file, and the certdata file, shamelessly stolen
+ * from Phil Karn's DES implementation.
+ *
+ * This version uses the standard Unix setkey() and encrypt()
+ * routines to do the encryption.
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+
+#include "ntp_stdlib.h"
+
+static void get8 P((U_LONG *));
+static void put8 P((U_LONG *));
+static void do_setkey P((U_LONG *));
+static void do_crypt P((U_LONG *, int));
+
+void
+main()
+{
+ U_LONG key[2], plain[2], cipher[2], answer[2];
+ int i;
+ int test;
+ int fail;
+
+ for(test=0;!feof(stdin);test++){
+ get8(key);
+ do_setkey(key);
+ printf(" K: "); put8(key);
+
+ get8(plain);
+ printf(" P: "); put8(plain);
+
+ get8(answer);
+ printf(" C: "); put8(answer);
+
+
+ for(i=0;i<2;i++)
+ cipher[i] = plain[i];
+ do_crypt(cipher, 0);
+
+ for(i=0;i<2;i++)
+ if(cipher[i] != answer[i])
+ break;
+ fail = 0;
+ if(i != 2){
+ printf(" Encrypt FAIL");
+ fail++;
+ }
+ do_crypt(cipher, 1);
+ for(i=0;i<2;i++)
+ if(cipher[i] != plain[i])
+ break;
+ if(i != 2){
+ printf(" Decrypt FAIL");
+ fail++;
+ }
+ if(fail == 0)
+ printf(" OK");
+ printf("\n");
+ }
+}
+
+static void
+get8(lp)
+U_LONG *lp;
+{
+ int t;
+ U_LONG l[2];
+ int i;
+
+ l[0] = l[1] = 0L;
+ for(i=0;i<8;i++){
+ scanf("%2x",&t);
+ if(feof(stdin))
+ exit(0);
+ l[i/4] <<= 8;
+ l[i/4] |= (U_LONG)(t & 0xff);
+ }
+ *lp = l[0];
+ *(lp+1) = l[1];
+}
+
+static void
+put8(lp)
+U_LONG *lp;
+{
+ int i;
+
+
+ for(i=0;i<2;i++){
+ printf("%08x",*lp++);
+ }
+}
+
+static void
+do_setkey(key)
+ U_LONG *key;
+{
+ int j;
+ register int i;
+ register char *kb;
+ register U_LONG *kp;
+ char keybits[64];
+
+ kb = keybits;
+ kp = key;
+ for (j = 0; j < 2; j++) {
+ for (i = 0; i < 32; i++) {
+ if (*kp & (1<<(31-i)))
+ *kb++ = 1;
+ else
+ *kb++ = 0;
+ }
+ kp++;
+ }
+ setkey(keybits);
+}
+
+static void
+do_crypt(data, edflag)
+ U_LONG *data;
+ int edflag;
+{
+ int j;
+ register int i;
+ register char *bp;
+ register U_LONG *dp;
+ char block[64];
+
+ bp = block;
+ dp = data;
+ for (j = 0; j < 2; j++) {
+ for (i = 0; i < 32; i++) {
+ if (*dp & (1<<(31-i)))
+ *bp++ = 1;
+ else
+ *bp++ = 0;
+ }
+ dp++;
+ }
+
+ encrypt(block, edflag);
+
+ bp = block;
+ dp = data;
+ for (j = 0; j < 2; j++) {
+ *dp = 0;
+ for (i = 0; i < 32; i++) {
+ if (*bp++)
+ *dp |= 1<<(31-i);
+ }
+ dp++;
+ }
+}
diff --git a/usr.sbin/xntpd/clockstuff/Makefile b/usr.sbin/xntpd/clockstuff/Makefile
new file mode 100644
index 0000000..a41a791
--- /dev/null
+++ b/usr.sbin/xntpd/clockstuff/Makefile
@@ -0,0 +1,16 @@
+#
+# $Id$
+#
+
+PROG= propdelay
+LDADD+= -L${.CURDIR}/../lib -lntp -lm
+DPADD+= ${.CURDIR}/../lib/libntp.a ${LIBM}
+
+SRCS= propdelay.c
+NOMAN=
+
+install:
+
+CLEANFILES+= chutest clktest chutest.o clktest.o
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/xntpd/clockstuff/README b/usr.sbin/xntpd/clockstuff/README
new file mode 100644
index 0000000..3714ab3
--- /dev/null
+++ b/usr.sbin/xntpd/clockstuff/README
@@ -0,0 +1,31 @@
+README file for directory ./clockstuff of the NTP Version 3 distribution
+
+This directory contains the sources for utility programs designed to
+support radio clocks. The chutest.c and clktest.c are desgined to
+test the chu_clk and tty_clk line disciplines and STREAMS modules in
+the ../kernel directory.
+
+These files have been modified to work with either the line disciplines
+or the STREAMS modules. Be sure to define -DSTREAM if appropriate.
+
+These are random bits of things written to help with clocks. You can
+make things in here by typing one or more of:
+
+ make propdelay (or `make')
+ make chutest
+ make clktest
+
+Propdelay computes high frequency propagation delays, given the
+longitude and latitude of the transmitter and receiver. Use
+this for WWV/H and CHU. Don't use it for WWVB (the computation
+is easier for that).
+
+Chutest can be used to input and process data from a CHU modem
+attached to a serial port. It will use the CHU line discipline
+(if installed), or raw mode otherwise. This was used to test
+out the initial reduction algorithms, and may not be up to date.
+
+Clktest can be used to test the clock line discipline (CLKLDISC,
+it must be available), and to take a look at radio clocks attached to a
+serial port.
+
diff --git a/usr.sbin/xntpd/clockstuff/chutest.c b/usr.sbin/xntpd/clockstuff/chutest.c
new file mode 100644
index 0000000..d8b804a
--- /dev/null
+++ b/usr.sbin/xntpd/clockstuff/chutest.c
@@ -0,0 +1,798 @@
+/* chutest.c,v 3.1 1993/07/06 01:05:21 jbj Exp
+ * chutest - test the CHU clock
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <sys/file.h>
+#include <sgtty.h>
+
+#include "../include/ntp_fp.h"
+#include "../include/ntp.h"
+#include "../include/ntp_unixtime.h"
+
+#ifdef STREAM
+#include <sys/chudefs.h>
+#include <stropts.h>
+#endif
+
+#ifdef CHULDISC
+#include <sys/chudefs.h>
+#endif
+
+#ifndef CHULDISC
+#ifndef STREAM
+#define NCHUCHARS (10)
+
+struct chucode {
+ u_char codechars[NCHUCHARS]; /* code characters */
+ u_char ncodechars; /* number of code characters */
+ u_char chustatus; /* not used currently */
+ struct timeval codetimes[NCHUCHARS]; /* arrival times */
+};
+#endif
+#endif
+
+#define STREQ(a, b) (*(a) == *(b) && strcmp((a), (b)) == 0)
+
+char *progname;
+int debug;
+
+int dofilter = 0; /* set to 1 when we should run filter algorithm */
+int showtimes = 0; /* set to 1 when we should show char arrival times */
+int doprocess = 0; /* set to 1 when we do processing analogous to driver */
+#ifdef CHULDISC
+int usechuldisc = 0; /* set to 1 when CHU line discipline should be used */
+#endif
+#ifdef STREAM
+int usechuldisc = 0; /* set to 1 when CHU line discipline should be used */
+#endif
+
+struct timeval lasttv;
+struct chucode chudata;
+
+extern u_long ustotslo[];
+extern u_long ustotsmid[];
+extern u_long ustotshi[];
+
+/*
+ * main - parse arguments and handle options
+ */
+main(argc, argv)
+int argc;
+char *argv[];
+{
+ int c;
+ int errflg = 0;
+ extern int ntp_optind;
+ extern char *ntp_optarg;
+ void init_chu();
+
+ progname = argv[0];
+ while ((c = ntp_getopt(argc, argv, "cdfpt")) != EOF)
+ switch (c) {
+ case 'c':
+#ifdef STREAM
+ usechuldisc = 1;
+ break;
+#endif
+#ifdef CHULDISC
+ usechuldisc = 1;
+ break;
+#endif
+#ifndef STREAM
+#ifndef CHULDISC
+ (void) fprintf(stderr,
+ "%s: CHU line discipline not available on this machine\n",
+ progname);
+ exit(2);
+#endif
+#endif
+ case 'd':
+ ++debug;
+ break;
+ case 'f':
+ dofilter = 1;
+ break;
+ case 'p':
+ doprocess = 1;
+ case 't':
+ showtimes = 1;
+ break;
+ default:
+ errflg++;
+ break;
+ }
+ if (errflg || ntp_optind+1 != argc) {
+#ifdef STREAM
+ (void) fprintf(stderr, "usage: %s [-dft] tty_device\n",
+ progname);
+#endif
+#ifdef CHULDISC
+ (void) fprintf(stderr, "usage: %s [-dft] tty_device\n",
+ progname);
+#endif
+#ifndef STREAM
+#ifndef CHULDISC
+ (void) fprintf(stderr, "usage: %s [-cdft] tty_device\n",
+ progname);
+#endif
+#endif
+ exit(2);
+ }
+
+ (void) gettimeofday(&lasttv, (struct timezone *)0);
+ c = openterm(argv[ntp_optind]);
+ init_chu();
+#ifdef STREAM
+ if (usechuldisc)
+ process_ldisc(c);
+ else
+#endif
+#ifdef CHULDISC
+ if (usechuldisc)
+ process_ldisc(c);
+ else
+#endif
+ process_raw(c);
+ /*NOTREACHED*/
+}
+
+
+/*
+ * openterm - open a port to the CHU clock
+ */
+int
+openterm(dev)
+ char *dev;
+{
+ int s;
+ struct sgttyb ttyb;
+
+ if (debug)
+ (void) fprintf(stderr, "Doing open...");
+ if ((s = open(dev, O_RDONLY, 0777)) < 0)
+ error("open(%s)", dev, "");
+ if (debug)
+ (void) fprintf(stderr, "open okay\n");
+
+ if (debug)
+ (void) fprintf(stderr, "Setting exclusive use...");
+ if (ioctl(s, TIOCEXCL, (char *)0) < 0)
+ error("ioctl(TIOCEXCL)", "", "");
+ if (debug)
+ (void) fprintf(stderr, "done\n");
+
+ ttyb.sg_ispeed = ttyb.sg_ospeed = B300;
+ ttyb.sg_erase = ttyb.sg_kill = 0;
+ ttyb.sg_flags = EVENP|ODDP|RAW;
+ if (debug)
+ (void) fprintf(stderr, "Setting baud rate et al...");
+ if (ioctl(s, TIOCSETP, (char *)&ttyb) < 0)
+ error("ioctl(TIOCSETP, raw)", "", "");
+ if (debug)
+ (void) fprintf(stderr, "done\n");
+
+#ifdef CHULDISC
+ if (usechuldisc) {
+ int ldisc;
+
+ if (debug)
+ (void) fprintf(stderr, "Switching to CHU ldisc...");
+ ldisc = CHULDISC;
+ if (ioctl(s, TIOCSETD, (char *)&ldisc) < 0)
+ error("ioctl(TIOCSETD, CHULDISC)", "", "");
+ if (debug)
+ (void) fprintf(stderr, "okay\n");
+ }
+#endif
+#ifdef STREAM
+ if (usechuldisc) {
+
+ if (debug)
+ (void) fprintf(stderr, "Poping off streams...");
+ while (ioctl(s, I_POP, 0) >=0) ;
+ if (debug)
+ (void) fprintf(stderr, "okay\n");
+ if (debug)
+ (void) fprintf(stderr, "Pushing CHU stream...");
+ if (ioctl(s, I_PUSH, "chu") < 0)
+ error("ioctl(I_PUSH, \"chu\")", "", "");
+ if (debug)
+ (void) fprintf(stderr, "okay\n");
+ }
+#endif
+ return s;
+}
+
+
+/*
+ * process_raw - process characters in raw mode
+ */
+process_raw(s)
+ int s;
+{
+ u_char c;
+ int n;
+ struct timeval tv;
+ struct timeval difftv;
+
+ while ((n = read(s, &c, sizeof(char))) > 0) {
+ (void) gettimeofday(&tv, (struct timezone *)0);
+ if (dofilter)
+ raw_filter((unsigned int)c, &tv);
+ else {
+ difftv.tv_sec = tv.tv_sec - lasttv.tv_sec;
+ difftv.tv_usec = tv.tv_usec - lasttv.tv_usec;
+ if (difftv.tv_usec < 0) {
+ difftv.tv_sec--;
+ difftv.tv_usec += 1000000;
+ }
+ (void) printf("%02x\t%lu.%06lu\t%lu.%06lu\n",
+ c, tv.tv_sec, tv.tv_usec, difftv.tv_sec,
+ difftv.tv_usec);
+ lasttv = tv;
+ }
+ }
+
+ if (n == 0) {
+ (void) fprintf(stderr, "%s: zero returned on read\n", progname);
+ exit(1);
+ } else
+ error("read()", "", "");
+}
+
+
+/*
+ * raw_filter - run the line discipline filter over raw data
+ */
+raw_filter(c, tv)
+ unsigned int c;
+ struct timeval *tv;
+{
+ static struct timeval diffs[10] = { 0 };
+ struct timeval diff;
+ l_fp ts;
+ void chufilter();
+
+ if ((c & 0xf) > 9 || ((c>>4)&0xf) > 9) {
+ if (debug)
+ (void) fprintf(stderr,
+ "character %02x failed BCD test\n");
+ chudata.ncodechars = 0;
+ return;
+ }
+
+ if (chudata.ncodechars > 0) {
+ diff.tv_sec = tv->tv_sec
+ - chudata.codetimes[chudata.ncodechars].tv_sec;
+ diff.tv_usec = tv->tv_usec
+ - chudata.codetimes[chudata.ncodechars].tv_usec;
+ if (diff.tv_usec < 0) {
+ diff.tv_sec--;
+ diff.tv_usec += 1000000;
+ } /*
+ if (diff.tv_sec != 0 || diff.tv_usec > 900000) {
+ if (debug)
+ (void) fprintf(stderr,
+ "character %02x failed time test\n");
+ chudata.ncodechars = 0;
+ return;
+ } */
+ }
+
+ chudata.codechars[chudata.ncodechars] = c;
+ chudata.codetimes[chudata.ncodechars] = *tv;
+ if (chudata.ncodechars > 0)
+ diffs[chudata.ncodechars] = diff;
+ if (++chudata.ncodechars == 10) {
+ if (doprocess) {
+ TVTOTS(&chudata.codetimes[NCHUCHARS-1], &ts);
+ ts.l_ui += JAN_1970;
+ chufilter(&chudata, &chudata.codetimes[NCHUCHARS-1]);
+ } else {
+ register int i;
+
+ for (i = 0; i < chudata.ncodechars; i++) {
+ (void) printf("%x%x\t%lu.%06lu\t%lu.%06lu\n",
+ chudata.codechars[i] & 0xf,
+ (chudata.codechars[i] >>4 ) & 0xf,
+ chudata.codetimes[i].tv_sec,
+ chudata.codetimes[i].tv_usec,
+ diffs[i].tv_sec, diffs[i].tv_usec);
+ }
+ }
+ chudata.ncodechars = 0;
+ }
+}
+
+
+/* #ifdef CHULDISC*/
+/*
+ * process_ldisc - process line discipline
+ */
+process_ldisc(s)
+ int s;
+{
+ struct chucode chu;
+ int n;
+ register int i;
+ struct timeval diff;
+ l_fp ts;
+ void chufilter();
+
+ while ((n = read(s, (char *)&chu, sizeof chu)) > 0) {
+ if (n != sizeof chu) {
+ (void) fprintf(stderr, "Expected %d, got %d\n",
+ sizeof chu, n);
+ continue;
+ }
+
+ if (doprocess) {
+ TVTOTS(&chu.codetimes[NCHUCHARS-1], &ts);
+ ts.l_ui += JAN_1970;
+ chufilter(&chu, &ts);
+ } else {
+ for (i = 0; i < NCHUCHARS; i++) {
+ if (i == 0)
+ diff.tv_sec = diff.tv_usec = 0;
+ else {
+ diff.tv_sec = chu.codetimes[i].tv_sec
+ - chu.codetimes[i-1].tv_sec;
+ diff.tv_usec = chu.codetimes[i].tv_usec
+ - chu.codetimes[i-1].tv_usec;
+ if (diff.tv_usec < 0) {
+ diff.tv_sec--;
+ diff.tv_usec += 1000000;
+ }
+ }
+ (void) printf("%x%x\t%lu.%06lu\t%lu.%06lu\n",
+ chu.codechars[i] & 0xf, (chu.codechars[i]>>4)&0xf,
+ chu.codetimes[i].tv_sec, chu.codetimes[i].tv_usec,
+ diff.tv_sec, diff.tv_usec);
+ }
+ }
+ }
+ if (n == 0) {
+ (void) fprintf(stderr, "%s: zero returned on read\n", progname);
+ exit(1);
+ } else
+ error("read()", "", "");
+}
+/*#endif*/
+
+
+/*
+ * error - print an error message
+ */
+error(fmt, s1, s2)
+ char *fmt;
+ char *s1;
+ char *s2;
+{
+ (void) fprintf(stderr, "%s: ", progname);
+ (void) fprintf(stderr, fmt, s1, s2);
+ (void) fprintf(stderr, ": ");
+ perror("");
+ exit(1);
+}
+
+/*
+ * Definitions
+ */
+#define MAXUNITS 4 /* maximum number of CHU units permitted */
+#define CHUDEV "/dev/chu%d" /* device we open. %d is unit number */
+#define NCHUCODES 9 /* expect 9 CHU codes per minute */
+
+/*
+ * When CHU is operating optimally we want the primary clock distance
+ * to come out at 300 ms. Thus, peer.distance in the CHU peer structure
+ * is set to 290 ms and we compute delays which are at least 10 ms long.
+ * The following are 290 ms and 10 ms expressed in u_fp format
+ */
+#define CHUDISTANCE 0x00004a3d
+#define CHUBASEDELAY 0x0000028f
+
+/*
+ * To compute a quality for the estimate (a pseudo delay) we add a
+ * fixed 10 ms for each missing code in the minute and add to this
+ * the sum of the differences between the remaining offsets and the
+ * estimated sample offset.
+ */
+#define CHUDELAYPENALTY 0x0000028f
+
+/*
+ * Other constant stuff
+ */
+#define CHUPRECISION (-9) /* what the heck */
+#define CHUREFID "CHU\0"
+
+/*
+ * Default fudge factors
+ */
+#define DEFPROPDELAY 0x00624dd3 /* 0.0015 seconds, 1.5 ms */
+#define DEFFILTFUDGE 0x000d1b71 /* 0.0002 seconds, 200 us */
+
+/*
+ * Hacks to avoid excercising the multiplier. I have no pride.
+ */
+#define MULBY10(x) (((x)<<3) + ((x)<<1))
+#define MULBY60(x) (((x)<<6) - ((x)<<2)) /* watch overflow */
+#define MULBY24(x) (((x)<<4) + ((x)<<3))
+
+/*
+ * Constants for use when multiplying by 0.1. ZEROPTONE is 0.1
+ * as an l_fp fraction, NZPOBITS is the number of significant bits
+ * in ZEROPTONE.
+ */
+#define ZEROPTONE 0x1999999a
+#define NZPOBITS 29
+
+/*
+ * The CHU table. This gives the expected time of arrival of each
+ * character after the on-time second and is computed as follows:
+ * The CHU time code is sent at 300 bps. Your average UART will
+ * synchronize at the edge of the start bit and will consider the
+ * character complete at the center of the first stop bit, i.e.
+ * 0.031667 ms later. Thus the expected time of each interrupt
+ * is the start bit time plus 0.031667 seconds. These times are
+ * in chutable[]. To this we add such things as propagation delay
+ * and delay fudge factor.
+ */
+#define CHARDELAY 0x081b4e80
+
+static u_long chutable[NCHUCHARS] = {
+ 0x2147ae14 + CHARDELAY, /* 0.130 (exactly) */
+ 0x2ac08312 + CHARDELAY, /* 0.167 (exactly) */
+ 0x34395810 + CHARDELAY, /* 0.204 (exactly) */
+ 0x3db22d0e + CHARDELAY, /* 0.241 (exactly) */
+ 0x472b020c + CHARDELAY, /* 0.278 (exactly) */
+ 0x50a3d70a + CHARDELAY, /* 0.315 (exactly) */
+ 0x5a1cac08 + CHARDELAY, /* 0.352 (exactly) */
+ 0x63958106 + CHARDELAY, /* 0.389 (exactly) */
+ 0x6d0e5604 + CHARDELAY, /* 0.426 (exactly) */
+ 0x76872b02 + CHARDELAY, /* 0.463 (exactly) */
+};
+
+/*
+ * Keep the fudge factors separately so they can be set even
+ * when no clock is configured.
+ */
+static l_fp propagation_delay;
+static l_fp fudgefactor;
+static l_fp offset_fudge;
+
+/*
+ * We keep track of the start of the year, watching for changes.
+ * We also keep track of whether the year is a leap year or not.
+ * All because stupid CHU doesn't include the year in the time code.
+ */
+static u_long yearstart;
+
+/*
+ * Imported from the timer module
+ */
+extern u_long current_time;
+extern struct event timerqueue[];
+
+/*
+ * Time conversion tables imported from the library
+ */
+extern u_long ustotslo[];
+extern u_long ustotsmid[];
+extern u_long ustotshi[];
+
+
+/*
+ * init_chu - initialize internal chu driver data
+ */
+void
+init_chu()
+{
+
+ /*
+ * Initialize fudge factors to default.
+ */
+ propagation_delay.l_ui = 0;
+ propagation_delay.l_uf = DEFPROPDELAY;
+ fudgefactor.l_ui = 0;
+ fudgefactor.l_uf = DEFFILTFUDGE;
+ offset_fudge = propagation_delay;
+ L_ADD(&offset_fudge, &fudgefactor);
+
+ yearstart = 0;
+}
+
+
+void
+chufilter(chuc, rtime)
+ struct chucode *chuc;
+ l_fp *rtime;
+{
+ register int i;
+ register u_long date_ui;
+ register u_long tmp;
+ register u_char *code;
+ int isneg;
+ int imin;
+ int imax;
+ u_long reftime;
+ l_fp off[NCHUCHARS];
+ l_fp ts;
+ int day, hour, minute, second;
+ static u_char lastcode[NCHUCHARS];
+ extern u_long calyearstart();
+ extern char *mfptoa();
+ void chu_process();
+ extern char *prettydate();
+
+ /*
+ * We'll skip the checks made in the kernel, but assume they've
+ * been done. This means that all characters are BCD and
+ * the intercharacter spacing isn't unreasonable.
+ */
+
+ /*
+ * print the code
+ */
+ for (i = 0; i < NCHUCHARS; i++)
+ printf("%c%c", (chuc->codechars[i] & 0xf) + '0',
+ ((chuc->codechars[i]>>4) & 0xf) + '0');
+ printf("\n");
+
+ /*
+ * Format check. Make sure the two halves match.
+ */
+ for (i = 0; i < NCHUCHARS/2; i++)
+ if (chuc->codechars[i] != chuc->codechars[i+(NCHUCHARS/2)]) {
+ (void) printf("Bad format, halves don't match\n");
+ return;
+ }
+
+ /*
+ * Break out the code into the BCD nibbles. Only need to fiddle
+ * with the first half since both are identical. Note the first
+ * BCD character is the low order nibble, the second the high order.
+ */
+ code = lastcode;
+ for (i = 0; i < NCHUCHARS/2; i++) {
+ *code++ = chuc->codechars[i] & 0xf;
+ *code++ = (chuc->codechars[i] >> 4) & 0xf;
+ }
+
+ /*
+ * If the first nibble isn't a 6, we're up the creek
+ */
+ code = lastcode;
+ if (*code++ != 6) {
+ (void) printf("Bad format, no 6 at start\n");
+ return;
+ }
+
+ /*
+ * Collect the day, the hour, the minute and the second.
+ */
+ day = *code++;
+ day = MULBY10(day) + *code++;
+ day = MULBY10(day) + *code++;
+ hour = *code++;
+ hour = MULBY10(hour) + *code++;
+ minute = *code++;
+ minute = MULBY10(minute) + *code++;
+ second = *code++;
+ second = MULBY10(second) + *code++;
+
+ /*
+ * Sanity check the day and time. Note that this
+ * only occurs on the 31st through the 39th second
+ * of the minute.
+ */
+ if (day < 1 || day > 366
+ || hour > 23 || minute > 59
+ || second < 31 || second > 39) {
+ (void) printf("Failed date sanity check: %d %d %d %d\n",
+ day, hour, minute, second);
+ return;
+ }
+
+ /*
+ * Compute seconds into the year.
+ */
+ tmp = (u_long)(MULBY24((day-1)) + hour); /* hours */
+ tmp = MULBY60(tmp) + (u_long)minute; /* minutes */
+ tmp = MULBY60(tmp) + (u_long)second; /* seconds */
+
+ /*
+ * Now the fun begins. We demand that the received time code
+ * be within CLOCK_WAYTOOBIG of the receive timestamp, but
+ * there is uncertainty about the year the timestamp is in.
+ * Use the current year start for the first check, this should
+ * work most of the time.
+ */
+ date_ui = tmp + yearstart;
+ if (date_ui < (rtime->l_ui + CLOCK_WAYTOOBIG)
+ && date_ui > (rtime->l_ui - CLOCK_WAYTOOBIG))
+ goto codeokay; /* looks good */
+
+ /*
+ * Trouble. Next check is to see if the year rolled over and, if
+ * so, try again with the new year's start.
+ */
+ date_ui = calyearstart(rtime->l_ui);
+ if (date_ui != yearstart) {
+ yearstart = date_ui;
+ date_ui += tmp;
+ (void) printf("time %u, code %u, difference %d\n",
+ date_ui, rtime->l_ui, (long)date_ui-(long)rtime->l_ui);
+ if (date_ui < (rtime->l_ui + CLOCK_WAYTOOBIG)
+ && date_ui > (rtime->l_ui - CLOCK_WAYTOOBIG))
+ goto codeokay; /* okay this time */
+ }
+
+ ts.l_uf = 0;
+ ts.l_ui = yearstart;
+ printf("yearstart %s\n", prettydate(&ts));
+ printf("received %s\n", prettydate(rtime));
+ ts.l_ui = date_ui;
+ printf("date_ui %s\n", prettydate(&ts));
+
+ /*
+ * Here we know the year start matches the current system
+ * time. One remaining possibility is that the time code
+ * is in the year previous to that of the system time. This
+ * is only worth checking if the receive timestamp is less
+ * than CLOCK_WAYTOOBIG seconds into the new year.
+ */
+ if ((rtime->l_ui - yearstart) < CLOCK_WAYTOOBIG) {
+ date_ui = tmp + calyearstart(yearstart - CLOCK_WAYTOOBIG);
+ if ((rtime->l_ui - date_ui) < CLOCK_WAYTOOBIG)
+ goto codeokay;
+ }
+
+ /*
+ * One last possibility is that the time stamp is in the year
+ * following the year the system is in. Try this one before
+ * giving up.
+ */
+ date_ui = tmp + calyearstart(yearstart + (400*24*60*60)); /* 400 days */
+ if ((date_ui - rtime->l_ui) >= CLOCK_WAYTOOBIG) {
+ printf("Date hopelessly off\n");
+ return; /* hopeless, let it sync to other peers */
+ }
+
+codeokay:
+ reftime = date_ui;
+ /*
+ * We've now got the integral seconds part of the time code (we hope).
+ * The fractional part comes from the table. We next compute
+ * the offsets for each character.
+ */
+ for (i = 0; i < NCHUCHARS; i++) {
+ register u_long tmp2;
+
+ off[i].l_ui = date_ui;
+ off[i].l_uf = chutable[i];
+ tmp = chuc->codetimes[i].tv_sec + JAN_1970;
+ TVUTOTSF(chuc->codetimes[i].tv_usec, tmp2);
+ M_SUB(off[i].l_ui, off[i].l_uf, tmp, tmp2);
+ }
+
+ /*
+ * Here is a *big* problem. What one would normally
+ * do here on a machine with lots of clock bits (say
+ * a Vax or the gizmo board) is pick the most positive
+ * offset and the estimate, since this is the one that
+ * is most likely suffered the smallest interrupt delay.
+ * The trouble is that the low order clock bit on an IBM
+ * RT, which is the machine I had in mind when doing this,
+ * ticks at just under the millisecond mark. This isn't
+ * precise enough. What we can do to improve this is to
+ * average all 10 samples and rely on the second level
+ * filtering to pick the least delayed estimate. Trouble
+ * is, this means we have to divide a 64 bit fixed point
+ * number by 10, a procedure which really sucks. Oh, well.
+ * First compute the sum.
+ */
+ date_ui = 0;
+ tmp = 0;
+ for (i = 0; i < NCHUCHARS; i++)
+ M_ADD(date_ui, tmp, off[i].l_ui, off[i].l_uf);
+ if (M_ISNEG(date_ui, tmp))
+ isneg = 1;
+ else
+ isneg = 0;
+
+ /*
+ * Here is a multiply-by-0.1 optimization that should apply
+ * just about everywhere. If the magnitude of the sum
+ * is less than 9 we don't have to worry about overflow
+ * out of a 64 bit product, even after rounding.
+ */
+ if (date_ui < 9 || date_ui > 0xfffffff7) {
+ register u_long prod_ui;
+ register u_long prod_uf;
+
+ prod_ui = prod_uf = 0;
+ /*
+ * This code knows the low order bit in 0.1 is zero
+ */
+ for (i = 1; i < NZPOBITS; i++) {
+ M_LSHIFT(date_ui, tmp);
+ if (ZEROPTONE & (1<<i))
+ M_ADD(prod_ui, prod_uf, date_ui, tmp);
+ }
+
+ /*
+ * Done, round it correctly. Prod_ui contains the
+ * fraction.
+ */
+ if (prod_uf & 0x80000000)
+ prod_ui++;
+ if (isneg)
+ date_ui = 0xffffffff;
+ else
+ date_ui = 0;
+ tmp = prod_ui;
+ /*
+ * date_ui is integral part, tmp is fraction.
+ */
+ } else {
+ register u_long prod_ovr;
+ register u_long prod_ui;
+ register u_long prod_uf;
+ register u_long highbits;
+
+ prod_ovr = prod_ui = prod_uf = 0;
+ if (isneg)
+ highbits = 0xffffffff; /* sign extend */
+ else
+ highbits = 0;
+ /*
+ * This code knows the low order bit in 0.1 is zero
+ */
+ for (i = 1; i < NZPOBITS; i++) {
+ M_LSHIFT3(highbits, date_ui, tmp);
+ if (ZEROPTONE & (1<<i))
+ M_ADD3(prod_ovr, prod_uf, prod_ui,
+ highbits, date_ui, tmp);
+ }
+
+ if (prod_uf & 0x80000000)
+ M_ADDUF(prod_ovr, prod_ui, (u_long)1);
+ date_ui = prod_ovr;
+ tmp = prod_ui;
+ }
+
+ /*
+ * At this point we have the mean offset, with the integral
+ * part in date_ui and the fractional part in tmp. Store
+ * it in the structure.
+ */
+ /*
+ * Add in fudge factor.
+ */
+ M_ADD(date_ui, tmp, offset_fudge.l_ui, offset_fudge.l_uf);
+
+ /*
+ * Find the minimun and maximum offset
+ */
+ imin = imax = 0;
+ for (i = 1; i < NCHUCHARS; i++) {
+ if (L_ISGEQ(&off[i], &off[imax])) {
+ imax = i;
+ } else if (L_ISGEQ(&off[imin], &off[i])) {
+ imin = i;
+ }
+ }
+
+ L_ADD(&off[imin], &offset_fudge);
+ if (imin != imax)
+ L_ADD(&off[imax], &offset_fudge);
+ (void) printf("mean %s, min %s, max %s\n",
+ mfptoa(date_ui, tmp, 8), lfptoa(&off[imin], 8),
+ lfptoa(&off[imax], 8));
+}
diff --git a/usr.sbin/xntpd/clockstuff/clktest.c b/usr.sbin/xntpd/clockstuff/clktest.c
new file mode 100644
index 0000000..b540485
--- /dev/null
+++ b/usr.sbin/xntpd/clockstuff/clktest.c
@@ -0,0 +1,511 @@
+/* clktest.c,v 3.1 1993/07/06 01:05:23 jbj Exp
+ * clktest - test the clock line discipline
+ *
+ * usage: clktest -b bps -f -t timeo -s cmd -c char1 -a char2 /dev/whatever
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <signal.h>
+#include <netinet/in.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <sys/file.h>
+#include <sgtty.h>
+
+#include "../include/ntp_fp.h"
+#include "../include/ntp.h"
+#include "../include/ntp_unixtime.h"
+
+#define STREQ(a, b) (*(a) == *(b) && strcmp((a), (b)) == 0)
+
+#if defined(ULT_2_0_SUCKS)
+#ifndef sigmask
+#define sigmask(m) (1<<(m))
+#endif
+#endif
+
+#ifndef STREAM
+#ifndef CLKLDISC
+ CLOCK_LINE_DISCIPLINE_NEEDED_BY_THIS_PROGRAM;
+#endif
+#endif
+
+/*
+ * Mask for blocking SIGIO and SIGALRM
+ */
+#define BLOCKSIGMASK (sigmask(SIGIO)|sigmask(SIGALRM))
+
+/*
+ * speed table
+ */
+struct speeds {
+ int bps;
+ int rate;
+} speedtab[] = {
+ { 300, B300 },
+ { 1200, B1200 },
+ { 2400, B2400 },
+ { 4800, B4800 },
+ { 9600, B9600 },
+ { 19200, EXTA },
+ { 38400, EXTB },
+ { 0, 0 }
+};
+
+char *progname;
+int debug;
+
+#ifdef CLKLDISC
+#define DEFMAGIC '\r'
+#endif
+
+#ifdef STREAM
+#include <stropts.h>
+#include <sys/clkdefs.h>
+#define DEFMAGIC "\r"
+#endif
+
+struct timeval timeout = { 0 };
+char *cmd = NULL;
+int cmdlen;
+int docmd = 0;
+#ifdef CLKLDISC
+u_long magic1 = DEFMAGIC;
+u_long magic2 = DEFMAGIC;
+#endif
+#ifdef STREAM
+char magic[32];
+#endif
+int speed = B9600;
+int ttflags = RAW|EVENP|ODDP;
+
+int wasalarmed;
+int iosig;
+
+struct timeval lasttv;
+
+extern u_long ustotslo[];
+extern u_long ustotsmid[];
+extern u_long ustotshi[];
+
+/*
+ * main - parse arguments and handle options
+ */
+main(argc, argv)
+int argc;
+char *argv[];
+{
+ int c;
+ int errflg = 0;
+ struct speeds *spd;
+ u_long tmp;
+ int fd;
+ struct sgttyb ttyb;
+ struct itimerval itimer;
+ extern int ntp_optind;
+ extern char *ntp_optarg;
+ int alarming();
+ int ioready();
+
+ progname = argv[0];
+#ifdef STREAM
+ magic[0] = 0;
+#endif
+ while ((c = ntp_getopt(argc, argv, "a:b:c:dfs:t:")) != EOF)
+ switch (c) {
+#ifdef CLKLDISC
+ case 'a':
+#endif
+ case 'c':
+ if (!atouint(ntp_optarg, &tmp)) {
+ (void) fprintf(stderr,
+ "%s: argument for -%c must be integer\n",
+ progname, c);
+ errflg++;
+ break;
+ }
+#ifdef CLKLDISC
+ if (c == 'c')
+ magic1 = tmp;
+ else
+ magic2 = tmp;
+#endif
+#ifdef STREAM
+ magic[strlen(magic)+1] = '\0';
+ magic[strlen(magic)] = tmp;
+#endif
+ break;
+ case 'b':
+ if (!atouint(ntp_optarg, &tmp)) {
+ errflg++;
+ break;
+ }
+ spd = speedtab;
+ while (spd->bps != 0)
+ if ((int)tmp == spd->bps)
+ break;
+ if (spd->bps == 0) {
+ (void) fprintf(stderr,
+ "%s: speed %lu is unsupported\n",
+ progname, tmp);
+ errflg++;
+ } else {
+ speed = spd->rate;
+ }
+ break;
+ case 'd':
+ ++debug;
+ break;
+ case 'f':
+ ttflags |= CRMOD;
+ break;
+ case 's':
+ cmdlen = strlen(ntp_optarg);
+ if (cmdlen == 0)
+ errflg++;
+ else
+ cmd = ntp_optarg;
+ break;
+ case 't':
+ if (!atouint(ntp_optarg, &tmp))
+ errflg++;
+ else {
+ timeout.tv_sec = (long)tmp;
+ docmd = 1;
+ }
+ break;
+ default:
+ errflg++;
+ break;
+ }
+ if (errflg || ntp_optind+1 != argc) {
+ (void) fprintf(stderr,
+#ifdef CLKLDISC
+"usage: %s [-b bps] [-c magic1] [-a magic2] [-f] [-s cmd] [-t timeo] tty_device\n",
+#endif
+#ifdef STREAM
+"usage: %s [-b bps] [-c magic1] [-c magic2]... [-f] [-s cmd] [-t timeo] tty_device\n",
+#endif
+ progname);
+ exit(2);
+ }
+
+#ifdef STREAM
+ if (!strlen(magic))
+ strcpy(magic,DEFMAGIC);
+#endif
+
+ if (docmd)
+ fd = open(argv[ntp_optind], O_RDWR, 0777);
+ else
+ fd = open(argv[ntp_optind], O_RDONLY, 0777);
+ if (fd == -1) {
+ (void) fprintf(stderr, "%s: open(%s): ", progname,
+ argv[ntp_optind]);
+ perror("");
+ exit(1);
+ }
+
+ if (ioctl(fd, TIOCEXCL, (char *)0) < 0) {
+ (void) fprintf(stderr, "%s: ioctl(TIOCEXCL): ", progname);
+ perror("");
+ exit(1);
+ }
+
+ /*
+ * If we have the clock discipline, set the port to raw. Otherwise
+ * we run cooked.
+ */
+ ttyb.sg_ispeed = ttyb.sg_ospeed = speed;
+#ifdef CLKLDISC
+ ttyb.sg_erase = (char)magic1;
+ ttyb.sg_kill = (char)magic2;
+#endif
+ ttyb.sg_flags = (short)ttflags;
+ if (ioctl(fd, TIOCSETP, (char *)&ttyb) < 0) {
+ (void) fprintf(stderr, "%s: ioctl(TIOCSETP): ", progname);
+ perror("");
+ exit(1);
+ }
+
+ if (fcntl(fd, F_SETOWN, getpid()) == -1) {
+ (void) fprintf(stderr, "%s: fcntl(F_SETOWN): ", progname);
+ perror("");
+ exit(1);
+ }
+
+#ifdef CLKLDISC
+ {
+ int ldisc;
+ ldisc = CLKLDISC;
+ if (ioctl(fd, TIOCSETD, (char *)&ldisc) < 0) {
+ (void) fprintf(stderr, "%s: ioctl(TIOCSETD): ", progname);
+ perror("");
+ exit(1);
+ }
+ }
+#endif
+#ifdef STREAM
+ if (ioctl(fd, I_POP, 0) >=0 ) ;
+ if (ioctl(fd, I_PUSH, "clk") < 0) {
+ (void) fprintf(stderr, "%s: ioctl(I_PUSH): ", progname);
+ perror("");
+ exit(1);
+ }
+ if (ioctl(fd, CLK_SETSTR, magic) < 0) {
+ (void) fprintf(stderr, "%s: ioctl(CLK_SETSTR): ", progname);
+ perror("");
+ exit(1);
+ }
+#endif
+
+
+ (void) gettimeofday(&lasttv, (struct timezone *)0);
+ if (docmd) {
+ /*
+ * set non-blocking, async I/O on the descriptor
+ */
+ iosig = 0;
+ (void) signal(SIGIO, ioready);
+ if (fcntl(fd, F_SETFL, FNDELAY|FASYNC) < 0) {
+ (void) fprintf(stderr, "%s: fcntl(F_SETFL): ",
+ progname);
+ perror("");
+ exit(1);
+ }
+
+ /*
+ * Set up the alarm interrupt.
+ */
+ wasalarmed = 0;
+ (void) signal(SIGALRM, alarming);
+ itimer.it_interval = itimer.it_value = timeout;
+ setitimer(ITIMER_REAL, &itimer, (struct itimerval *)0);
+ doboth(fd);
+ }
+ doioonly(fd);
+}
+
+
+/*
+ * doboth - handle both I/O and alarms via SIGIO
+ */
+doboth(fd)
+ int fd;
+{
+ int n;
+ int sawalarm;
+ int sawiosig;
+ int omask;
+ fd_set fds;
+ struct timeval tvzero;
+
+ sawalarm = 0;
+ sawiosig = 0;
+ FD_ZERO(&fds);
+ for (;;) {
+ omask = sigblock(BLOCKSIGMASK);
+ if (wasalarmed) { /* alarmed? */
+ sawalarm = 1;
+ wasalarmed = 0;
+ }
+ if (iosig) {
+ sawiosig = 1;
+ iosig = 0;
+ }
+
+ if (!sawalarm && !sawiosig) {
+ /*
+ * Nothing to do. Wait for something.
+ */
+ sigpause(omask);
+ if (wasalarmed) { /* alarmed? */
+ sawalarm = 1;
+ wasalarmed = 0;
+ }
+ if (iosig) {
+ sawiosig = 1;
+ iosig = 0;
+ }
+ }
+ (void)sigsetmask(omask);
+
+ if (sawiosig) {
+
+ do {
+ tvzero.tv_sec = tvzero.tv_usec = 0;
+ FD_SET(fd, &fds);
+ n = select(fd+1, &fds, (fd_set *)0,
+ (fd_set *)0, &tvzero);
+ if (n > 0)
+ doio(fd);
+ } while (n > 0);
+
+ if (n == -1) {
+ (void) fprintf(stderr, "%s: select: ",
+ progname);
+ perror("");
+ exit(1);
+ }
+ sawiosig = 0;
+ }
+ if (sawalarm) {
+ doalarm(fd);
+ sawalarm = 0;
+ }
+ }
+}
+
+
+/*
+ * doioonly - do I/O. This avoids the use of signals
+ */
+doioonly(fd)
+ int fd;
+{
+ int n;
+ fd_set fds;
+
+ FD_ZERO(&fds);
+ for (;;) {
+ FD_SET(fd, &fds);
+ n = select(fd+1, &fds, (fd_set *)0, (fd_set *)0,
+ (struct timeval *)0);
+ if (n > 0)
+ doio(fd);
+ }
+}
+
+
+/*
+ * doio - read a buffer full of stuff and print it out
+ */
+doio(fd)
+ int fd;
+{
+ register char *rp, *rpend;
+ register char *cp;
+ register int i;
+ char raw[512];
+ struct timeval tv, tvd;
+ int rlen;
+ int ind;
+ char cooked[2049];
+ static char *digits = "0123456789abcdef";
+
+ rlen = read(fd, raw, sizeof(raw));
+ if (rlen < 0) {
+ (void) fprintf(stderr, "%s: read(): ", progname);
+ perror("");
+ return;
+ }
+ if (rlen == 0) {
+ (void) printf("Zero length read\n");
+ return;
+ }
+
+ cp = cooked;
+ rp = raw;
+ rpend = &raw[rlen];
+ ind = 0;
+
+ while (rp < rpend) {
+ ind = 1;
+ if (isprint(*rp))
+ *cp++ = *rp;
+ else {
+ *cp++ = '<';
+ *cp++ = digits[((*rp)>>4) & 0xf];
+ *cp++ = digits[*rp & 0xf];
+ *cp++ = '>';
+ }
+#ifdef CLKLDISC
+ if (*rp == (char)magic1 || *rp == (char)magic2) {
+#else
+ if ( strchr( magic, *rp) != NULL ) {
+#endif
+ rp++;
+ ind = 0;
+ *cp = '\0';
+ if ((rpend - rp) < sizeof(struct timeval)) {
+ (void)printf(
+ "Too little data (%d): %s\n",
+ rpend-rp, cooked);
+ return;
+ }
+
+ tv.tv_sec = 0;
+ for (i = 0; i < 4; i++) {
+ tv.tv_sec <<= 8;
+ tv.tv_sec |= ((long)*rp++) & 0xff;
+ }
+ tv.tv_usec = 0;
+ for (i = 0; i < 4; i++) {
+ tv.tv_usec <<= 8;
+ tv.tv_usec |= ((long)*rp++) & 0xff;
+ }
+
+ tvd.tv_sec = tv.tv_sec - lasttv.tv_sec;
+ tvd.tv_usec = tv.tv_usec - lasttv.tv_usec;
+ if (tvd.tv_usec < 0) {
+ tvd.tv_usec += 1000000;
+ tvd.tv_sec--;
+ }
+
+ (void)printf("%lu.%06lu %lu.%06lu %s\n",
+ tv.tv_sec, tv.tv_usec, tvd.tv_sec, tvd.tv_usec,
+ cooked);
+ lasttv = tv;
+ } else {
+ rp++;
+ }
+ }
+
+ if (ind) {
+ *cp = '\0';
+ (void)printf("Incomplete data: %s\n", cooked);
+ }
+}
+
+
+/*
+ * doalarm - send a string out the port, if we have one.
+ */
+doalarm(fd)
+ int fd;
+{
+ int n;
+
+ if (cmd == NULL || cmdlen <= 0)
+ return;
+
+ n = write(fd, cmd, cmdlen);
+
+ if (n < 0) {
+ (void) fprintf(stderr, "%s: write(): ", progname);
+ perror("");
+ } else if (n < cmdlen) {
+ (void) printf("Short write (%d bytes, should be %d)\n",
+ n, cmdlen);
+ }
+}
+
+
+/*
+ * alarming - receive alarm interupt
+ */
+alarming()
+{
+ wasalarmed = 1;
+}
+
+/*
+ * ioready - handle SIGIO interrupt
+ */
+ioready()
+{
+ iosig = 1;
+}
diff --git a/usr.sbin/xntpd/clockstuff/propdelay.c b/usr.sbin/xntpd/clockstuff/propdelay.c
new file mode 100644
index 0000000..507bc08
--- /dev/null
+++ b/usr.sbin/xntpd/clockstuff/propdelay.c
@@ -0,0 +1,536 @@
+/* propdelay.c,v 3.1 1993/07/06 01:05:24 jbj Exp
+ * propdelay - compute propagation delays
+ *
+ * cc -o propdelay propdelay.c -lm
+ *
+ * "Time and Frequency Users' Manual", NBS Technical Note 695 (1977).
+ */
+
+/*
+ * This can be used to get a rough idea of the HF propagation delay
+ * between two points (usually between you and the radio station).
+ * The usage is
+ *
+ * propdelay latitudeA longitudeA latitudeB longitudeB
+ *
+ * where points A and B are the locations in question. You obviously
+ * need to know the latitude and longitude of each of the places.
+ * The program expects the latitude to be preceded by an 'n' or 's'
+ * and the longitude to be preceded by an 'e' or 'w'. It understands
+ * either decimal degrees or degrees:minutes:seconds. Thus to compute
+ * the delay between the WWVH (21:59:26N, 159:46:00W) and WWV (40:40:49N,
+ * 105:02:27W) you could use:
+ *
+ * propdelay n21:59:26 w159:46 n40:40:49 w105:02:27
+ *
+ * By default it prints out a summer (F2 average virtual height 350 km) and
+ * winter (F2 average virtual height 250 km) number. The results will be
+ * quite approximate but are about as good as you can do with HF time anyway.
+ * You might pick a number between the values to use, or use the summer
+ * value in the summer and switch to the winter value when the static
+ * above 10 MHz starts to drop off in the fall. You can also use the
+ * -h switch if you want to specify your own virtual height.
+ *
+ * You can also do a
+ *
+ * propdelay -W n45:17:47 w75:45:22
+ *
+ * to find the propagation delays to WWV and WWVH (from CHU in this
+ * case), a
+ *
+ * propdelay -C n40:40:49 w105:02:27
+ *
+ * to find the delays to CHU, and a
+ *
+ * propdelay -G n52:03:17 w98:34:18
+ *
+ * to find delays to GOES via each of the three satellites.
+ */
+
+#include <stdio.h>
+#include <string.h>
+
+#include "ntp_stdlib.h"
+
+extern double sin P((double));
+extern double cos P((double));
+extern double acos P((double));
+extern double tan P((double));
+extern double atan P((double));
+extern double sqrt P((double));
+
+#define STREQ(a, b) (*(a) == *(b) && strcmp((a), (b)) == 0)
+
+/*
+ * Program constants
+ */
+#define EARTHRADIUS (6370.0) /* raduis of earth (km) */
+#define LIGHTSPEED (299800.0) /* speed of light, km/s */
+#define PI (3.1415926536)
+#define RADPERDEG (PI/180.0) /* radians per degree */
+#define MILE (1.609344) /* km in a mile */
+
+#define SUMMERHEIGHT (350.0) /* summer height in km */
+#define WINTERHEIGHT (250.0) /* winter height in km */
+
+#define SATHEIGHT (6.6110 * 6378.0) /* geosync satellite height in km
+ from centre of earth */
+
+#define WWVLAT "n40:40:49"
+#define WWVLONG "w105:02:27"
+
+#define WWVHLAT "n21:59:26"
+#define WWVHLONG "w159:46:00"
+
+#define CHULAT "n45:17:47"
+#define CHULONG "w75:45:22"
+
+#define GOES_UP_LAT "n37:52:00"
+#define GOES_UP_LONG "w75:27:00"
+#define GOES_EAST_LONG "w75:00:00"
+#define GOES_STBY_LONG "w105:00:00"
+#define GOES_WEST_LONG "w135:00:00"
+#define GOES_SAT_LAT "n00:00:00"
+
+char *wwvlat = WWVLAT;
+char *wwvlong = WWVLONG;
+
+char *wwvhlat = WWVHLAT;
+char *wwvhlong = WWVHLONG;
+
+char *chulat = CHULAT;
+char *chulong = CHULONG;
+
+char *goes_up_lat = GOES_UP_LAT;
+char *goes_up_long = GOES_UP_LONG;
+char *goes_east_long = GOES_EAST_LONG;
+char *goes_stby_long = GOES_STBY_LONG;
+char *goes_west_long = GOES_WEST_LONG;
+char *goes_sat_lat = GOES_SAT_LAT;
+
+int hflag = 0;
+int Wflag = 0;
+int Cflag = 0;
+int Gflag = 0;
+int height;
+
+char *progname;
+int debug;
+
+static void doit P((double, double, double, double, double, char *));
+static double latlong P((char *, int));
+static double greatcircle P((double, double, double, double));
+static double waveangle P((double, double, int));
+static double propdelay P((double, double, int));
+static int finddelay P((double, double, double, double, double, double *));
+static void satdoit P((double, double, double, double, double, double, char *));
+static void satfinddelay P((double, double, double, double, double *));
+static double satpropdelay P((double));
+
+/*
+ * main - parse arguments and handle options
+ */
+void
+main(argc, argv)
+int argc;
+char *argv[];
+{
+ int c;
+ int errflg = 0;
+ double lat1, long1;
+ double lat2, long2;
+ double lat3, long3;
+ extern int ntp_optind;
+ extern char *ntp_optarg;
+
+ progname = argv[0];
+ while ((c = ntp_getopt(argc, argv, "dh:CWG")) != EOF)
+ switch (c) {
+ case 'd':
+ ++debug;
+ break;
+ case 'h':
+ hflag++;
+ height = atof(ntp_optarg);
+ if (height <= 0.0) {
+ (void) fprintf(stderr, "height %s unlikely\n",
+ ntp_optarg);
+ errflg++;
+ }
+ break;
+ case 'C':
+ Cflag++;
+ break;
+ case 'W':
+ Wflag++;
+ break;
+ case 'G':
+ Gflag++;
+ break;
+ default:
+ errflg++;
+ break;
+ }
+ if (errflg || (!(Cflag || Wflag || Gflag) && ntp_optind+4 != argc) ||
+ ((Cflag || Wflag || Gflag) && ntp_optind+2 != argc)) {
+ (void) fprintf(stderr,
+ "usage: %s [-d] [-h height] lat1 long1 lat2 long2\n",
+ progname);
+ (void) fprintf(stderr," - or -\n");
+ (void) fprintf(stderr,
+ "usage: %s -CWG [-d] lat long\n",
+ progname);
+ exit(2);
+ }
+
+
+ if (!(Cflag || Wflag || Gflag)) {
+ lat1 = latlong(argv[ntp_optind], 1);
+ long1 = latlong(argv[ntp_optind + 1], 0);
+ lat2 = latlong(argv[ntp_optind + 2], 1);
+ long2 = latlong(argv[ntp_optind + 3], 0);
+ if (hflag) {
+ doit(lat1, long1, lat2, long2, height, "");
+ } else {
+ doit(lat1, long1, lat2, long2, (double)SUMMERHEIGHT,
+ "summer propagation, ");
+ doit(lat1, long1, lat2, long2, (double)WINTERHEIGHT,
+ "winter propagation, ");
+ }
+ } else if (Wflag) {
+ /*
+ * Compute delay from WWV
+ */
+ lat1 = latlong(argv[ntp_optind], 1);
+ long1 = latlong(argv[ntp_optind + 1], 0);
+ lat2 = latlong(wwvlat, 1);
+ long2 = latlong(wwvlong, 0);
+ if (hflag) {
+ doit(lat1, long1, lat2, long2, height, "WWV ");
+ } else {
+ doit(lat1, long1, lat2, long2, (double)SUMMERHEIGHT,
+ "WWV summer propagation, ");
+ doit(lat1, long1, lat2, long2, (double)WINTERHEIGHT,
+ "WWV winter propagation, ");
+ }
+
+ /*
+ * Compute delay from WWVH
+ */
+ lat2 = latlong(wwvhlat, 1);
+ long2 = latlong(wwvhlong, 0);
+ if (hflag) {
+ doit(lat1, long1, lat2, long2, height, "WWVH ");
+ } else {
+ doit(lat1, long1, lat2, long2, (double)SUMMERHEIGHT,
+ "WWVH summer propagation, ");
+ doit(lat1, long1, lat2, long2, (double)WINTERHEIGHT,
+ "WWVH winter propagation, ");
+ }
+ } else if (Cflag) {
+ lat1 = latlong(argv[ntp_optind], 1);
+ long1 = latlong(argv[ntp_optind + 1], 0);
+ lat2 = latlong(chulat, 1);
+ long2 = latlong(chulong, 0);
+ if (hflag) {
+ doit(lat1, long1, lat2, long2, height, "CHU ");
+ } else {
+ doit(lat1, long1, lat2, long2, (double)SUMMERHEIGHT,
+ "CHU summer propagation, ");
+ doit(lat1, long1, lat2, long2, (double)WINTERHEIGHT,
+ "CHU winter propagation, ");
+ }
+ } else if (Gflag) {
+ lat1 = latlong(goes_up_lat, 1);
+ long1 = latlong(goes_up_long, 0);
+ lat3 = latlong(argv[ntp_optind], 1);
+ long3 = latlong(argv[ntp_optind + 1], 0);
+
+ lat2 = latlong(goes_sat_lat, 1);
+
+ long2 = latlong(goes_west_long, 0);
+ satdoit(lat1, long1, lat2, long2, lat3, long3,
+ "GOES Delay via WEST");
+
+ long2 = latlong(goes_stby_long, 0);
+ satdoit(lat1, long1, lat2, long2, lat3, long3,
+ "GOES Delay via STBY");
+
+ long2 = latlong(goes_east_long, 0);
+ satdoit(lat1, long1, lat2, long2, lat3, long3,
+ "GOES Delay via EAST");
+
+ }
+ exit(0);
+}
+
+
+/*
+ * doit - compute a delay and print it
+ */
+static void
+doit(lat1, long1, lat2, long2, h, str)
+ double lat1;
+ double long1;
+ double lat2;
+ double long2;
+ double h;
+ char *str;
+{
+ int hops;
+ double delay;
+
+ hops = finddelay(lat1, long1, lat2, long2, h, &delay);
+ printf("%sheight %g km, hops %d, delay %g seconds\n",
+ str, h, hops, delay);
+}
+
+
+/*
+ * latlong - decode a latitude/longitude value
+ */
+static double
+latlong(str, islat)
+ char *str;
+ int islat;
+{
+ register char *cp;
+ register char *bp;
+ double arg;
+ double div;
+ int isneg;
+ char buf[32];
+ char *colon;
+
+ if (islat) {
+ /*
+ * Must be north or south
+ */
+ if (*str == 'N' || *str == 'n')
+ isneg = 0;
+ else if (*str == 'S' || *str == 's')
+ isneg = 1;
+ else
+ isneg = -1;
+ } else {
+ /*
+ * East is positive, west is negative
+ */
+ if (*str == 'E' || *str == 'e')
+ isneg = 0;
+ else if (*str == 'W' || *str == 'w')
+ isneg = 1;
+ else
+ isneg = -1;
+ }
+
+ if (isneg >= 0)
+ str++;
+
+ colon = strchr(str, ':');
+ if (colon != NULL) {
+ /*
+ * in hhh:mm:ss form
+ */
+ cp = str;
+ bp = buf;
+ while (cp < colon)
+ *bp++ = *cp++;
+ *bp = '\0';
+ cp++;
+ arg = atof(buf);
+ div = 60.0;
+ colon = strchr(cp, ':');
+ if (colon != NULL) {
+ bp = buf;
+ while (cp < colon)
+ *bp++ = *cp++;
+ *bp = '\0';
+ cp++;
+ arg += atof(buf) / div;
+ div = 3600.0;
+ }
+ if (*cp != '\0')
+ arg += atof(cp) / div;
+ } else {
+ arg = atof(str);
+ }
+
+ if (isneg == 1)
+ arg = -arg;
+
+ if (debug > 2)
+ (void) printf("latitude/longitude %s = %g\n", str, arg);
+
+ return arg;
+}
+
+
+/*
+ * greatcircle - compute the great circle distance in kilometers
+ */
+static double
+greatcircle(lat1, long1, lat2, long2)
+ double lat1;
+ double long1;
+ double lat2;
+ double long2;
+{
+ double dg;
+ double l1r, l2r;
+
+ l1r = lat1 * RADPERDEG;
+ l2r = lat2 * RADPERDEG;
+ dg = EARTHRADIUS * acos(
+ (cos(l1r) * cos(l2r) * cos((long2-long1)*RADPERDEG))
+ + (sin(l1r) * sin(l2r)));
+ if (debug >= 2)
+ printf(
+ "greatcircle lat1 %g long1 %g lat2 %g long2 %g dist %g\n",
+ lat1, long1, lat2, long2, dg);
+ return dg;
+}
+
+
+/*
+ * waveangle - compute the wave angle for the given distance, virtual
+ * height and number of hops.
+ */
+static double
+waveangle(dg, h, n)
+ double dg;
+ double h;
+ int n;
+{
+ double theta;
+ double delta;
+
+ theta = dg / (EARTHRADIUS * (double)(2 * n));
+ delta = atan((h / (EARTHRADIUS * sin(theta))) + tan(theta/2)) - theta;
+ if (debug >= 2)
+ printf("waveangle dist %g height %g hops %d angle %g\n",
+ dg, h, n, delta / RADPERDEG);
+ return delta;
+}
+
+
+/*
+ * propdelay - compute the propagation delay
+ */
+static double
+propdelay(dg, h, n)
+ double dg;
+ double h;
+ int n;
+{
+ double phi;
+ double theta;
+ double td;
+
+ theta = dg / (EARTHRADIUS * (double)(2 * n));
+ phi = (PI/2.0) - atan((h / (EARTHRADIUS * sin(theta))) + tan(theta/2));
+ td = dg / (LIGHTSPEED * sin(phi));
+ if (debug >= 2)
+ printf("propdelay dist %g height %g hops %d time %g\n",
+ dg, h, n, td);
+ return td;
+}
+
+
+/*
+ * finddelay - find the propagation delay
+ */
+static int
+finddelay(lat1, long1, lat2, long2, h, delay)
+ double lat1;
+ double long1;
+ double lat2;
+ double long2;
+ double h;
+ double *delay;
+{
+ double dg; /* great circle distance */
+ double delta; /* wave angle */
+ int n; /* number of hops */
+
+ dg = greatcircle(lat1, long1, lat2, long2);
+ if (debug)
+ printf("great circle distance %g km %g miles\n", dg, dg/MILE);
+
+ n = 1;
+ while ((delta = waveangle(dg, h, n)) < 0.0) {
+ if (debug)
+ printf("tried %d hop%s, no good\n", n, n>1?"s":"");
+ n++;
+ }
+ if (debug)
+ printf("%d hop%s okay, wave angle is %g\n", n, n>1?"s":"",
+ delta / RADPERDEG);
+
+ *delay = propdelay(dg, h, n);
+ return n;
+}
+
+/*
+ * satdoit - compute a delay and print it
+ */
+static void
+satdoit(lat1, long1, lat2, long2, lat3, long3, str)
+ double lat1;
+ double long1;
+ double lat2;
+ double long2;
+ double lat3;
+ double long3;
+ char *str;
+{
+ double up_delay,down_delay;
+
+ satfinddelay(lat1, long1, lat2, long2, &up_delay);
+ satfinddelay(lat3, long3, lat2, long2, &down_delay);
+
+ printf("%s, delay %g seconds\n", str, up_delay + down_delay);
+}
+
+/*
+ * satfinddelay - calculate the one-way delay time between a ground station
+ * and a satellite
+ */
+static void
+satfinddelay(lat1, long1, lat2, long2, delay)
+ double lat1;
+ double long1;
+ double lat2;
+ double long2;
+ double *delay;
+{
+ double dg; /* great circle distance */
+
+ dg = greatcircle(lat1, long1, lat2, long2);
+
+ *delay = satpropdelay(dg);
+}
+
+/*
+ * satpropdelay - calculate the one-way delay time between a ground station
+ * and a satellite
+ */
+static double
+satpropdelay(dg)
+ double dg;
+{
+ double k1, k2, dist;
+ double theta;
+ double td;
+
+ theta = dg / (EARTHRADIUS);
+ k1 = EARTHRADIUS * sin(theta);
+ k2 = SATHEIGHT - (EARTHRADIUS * cos(theta));
+ if (debug >= 2)
+ printf("Theta %g k1 %g k2 %g\n", theta, k1, k2);
+ dist = sqrt(k1*k1 + k2*k2);
+ td = dist / LIGHTSPEED;
+ if (debug >= 2)
+ printf("propdelay dist %g height %g time %g\n", dg, dist, td);
+ return td;
+}
diff --git a/usr.sbin/xntpd/conf/Config.CHATHAM b/usr.sbin/xntpd/conf/Config.CHATHAM
new file mode 100644
index 0000000..b1f980b
--- /dev/null
+++ b/usr.sbin/xntpd/conf/Config.CHATHAM
@@ -0,0 +1,211 @@
+# Edit this file to reflect information specific to your installation.
+# Then run 'make makeconfig' to propagate the information to all the makefiles,
+# Config.CHATHAM,v 3.1 1993/07/06 01:03:42 jbj Exp
+
+#
+# Definitions for the library:
+#
+# You must define one of -DXNTP_BIG_ENDIAN, -DXNTP_LITTLE_ENDIAN
+# or -DXNTP_AUTO_ENDIAN depending on which way your machine's
+# bytes go for the benefit of the DES routine. Most things
+# sold by DEC, the NS32x32 and the 80386 deserve a
+# -DXNTP_LITTLE_ENDIAN. Most of the rest of the world does
+# it the other way. If in doubt, pick one, compile
+# everything and run authstuff/authcert < authstuff/certdata.
+# If everything fails, do it the other way.
+#
+# Under BSD, you may define -DXNTP_NETINET_ENDIAN to use
+# netinet/in.h to determine which of -DXNTP_BIG_ENDIAN and
+# XNTP_LITTLE_ENDIAN should be used.
+#
+LIBDEFS= -DWORDS_BIGENDIAN
+
+#
+# Library loading:
+#
+# If you don't want your library ranlib'ed, chose the second line
+#
+RANLIB= ranlib
+#RANLIB= : # ar does the work of ranlib under System V
+
+#
+# Definitions for programs:
+#
+# If your compiler doesn't understand the declaration `signed char',
+# add -DNO_SIGNED_CHAR_DECL. Your `char' data type had better be
+# signed. If you don't know what the compiler knows, try it
+# without the flag. If you get a syntax error on line 13 of
+# ntp.h, add it. Note that `signed char' is an ANSIism. Most
+# older, pcc-derived compilers will need this flag.
+#
+# If your library already has 's_char' defined, add -DS_CHAR_DEFINED.
+#
+# For SunOS 3.x, add -DSUN_3_3_STINKS (otherwise it will complain
+# about broadaddr and will hang if you run without a -d flag
+# on the command line. I actually can't believe the latter
+# bug. If it hangs on your system with the flag defined, peruse
+# xntpd/ntp_io.c for some rude comments about SunOS 3.5 and try it
+# the other way). This flag affects xntpd only.
+#
+# For Ultrix 2.0, add -DULT_2_0_SUCKS. This OS has the same hanging
+# bug as SunOS 3.5 (is this an original 4.2 bug?) and in addition
+# has some strangeness concerning signal masks. Ultrix 2.3 doesn't
+# have these problems. If you're running something in between
+# you're on your own. This flag affects xntpd only.
+#
+# For SunOS 4.x, add -DDOSYNCTODR_SUCKS to include the code in ntp_util.c
+# that sets the battery clock at the same time that it updates
+# the driftfile. It does this by revving up the niceness, then
+# sets the time of day to the current time of day. Ordinarily,
+# you would need this only on non-networked machines.
+#
+# For some machines, settimeofday does not set the sub-second component
+# of the time correctly. For these machines add -DSETTIMEOFDAY_BROKEN.
+# If xntpd keeps STEPPING the clock by small amounts, then it is
+# possible that you are suffering from this problem.
+#
+# There are three ways to pry loose the kernel variables tick and tickadj
+# needed by ntp_unixclock.c. One reads kmem and and is enabled
+# with -DREADKMEM. One uses Sun's libkvm and is enabled with
+# -DUSELIBKVM. The last one uses builtin defaults and is enabled
+# with -DNOKMEM. Therefore, one of -DUSELIBKVM, -DREADKMEM or
+# -DNOKMEM must be defined. Suns and recent BSD should use
+# -DUSELIBKVM; others should use -DREADKMEM. If -DUSELIBKVM, use
+# the DAEMONLIBS below to get the kernel routines.
+#
+# If your gethostbyname() routine isn't based on the DNS resolver (and,
+# in particular, h_errno doesn't exist) add a -DNODNS. There
+# doesn't seem to be a good way to detect this automatically which
+# works in all cases. This flag affects xntpres only.
+#
+# Adding -DLOCK_PROCESS to the compilation flags will prevent
+# xntpd from being swapped out on systems where the plock(3) call
+# is available.
+#
+# The flag -DDEBUG includes some debugging code.
+#
+# The flag -DREFCLOCK causes the basic reference clock support to be
+# compiled into the daemon. If you set this you will also want
+# to configure the particular clock drivers you want in the
+# CLOCKDEFS= line below. This flag affects xntpd only.
+#
+# To change the location of the configuration file, use a
+# -DCONFIG_FILE=\\"/local/etc/ntp.conf\\" or something similar.
+#
+# Under HP-UX, you must use either -Dhpux70 or -Dhpux80 as,
+# well as -DNOKMEM
+#
+# If your library doesn't include the vsprintf() routine, define
+# NEED_VSPRINTF.
+#
+# There are three ways to utilize external 1-pps signals. Define -DPPS to
+# include just the pps routine, such as used by the DCF77 reference clock
+# driver. Define -DPPSDEV ito include a serial device driver. This
+# requires a serial port and either a line discipline or STREAMS module.
+# Define -DPPSCD to include the driver and a special kernal hack
+# (for SunOS 4.1.1) that intercepts carrier-detect transitions
+# generated by the pps signal. Only one of these flags should be defined.
+#
+DEFS= -DUSELIBKVM -DDEBUG -DSTREAM -DREFCLOCK -DNO_SIGNED_CHAR_DECL -DPPS -DPPSDEV -DXNTP_RETROFIT_STDLIB -DNTP_POSIX_SOURCE
+
+#
+# Authentication types supported. Choose from DES and MD5. If you
+# have a 680x0 type CPU and GNU-C, also choose -DFASTMD5
+#
+AUTHDEFS=-DDES -DMD5
+
+#
+# Clock support definitions (these only make sense if -DREFCLOCK used):
+#
+# Define -DLOCAL_CLOCK to include local pseudo-clock support
+#
+# Define -DPST to include support for the PST 1020 WWV/H receiver.
+#
+# Define -DWWVB to include support for the Spectracom 8170 WWVB receiver.
+# Define -DWWVBPPS for PPS support via the WWVB receiver; also,
+# define -DPPSCD in the DEFS above. This requires the ppsclock
+# streams module under SunOS 4.2.
+#
+# Define -DCHU to include support for a driver to receive the CHU
+# timecode. Note that to compile in CHU support you must
+# previously have installed the CHU serial line discipline in
+# the kernel of the machine you are doing the compile on.
+#
+# Define -DDCF to include support for the DCF77 receiver. This code
+# requires a special STREAMS module found in the kernel directory.
+# Define -DDCFPPS for PPS support via the DCF77 receiver; also,
+# devine -DPPS in the DEFS above.
+#
+# Define -DMX4200 to support a Magnavox 4200 GPS receiver. Define -DPPSCD
+# in the DEFS above for PPS support via this receiver. This requires
+# the ppsclock streams module under SunOS 4.2.
+#
+# Define -DAS2201 to include support for the Austron 2201 GPS Timing
+# Receiver. Define -DPPSCD in the DEFS above for PPS support via this
+# receiver. This requires the ppsclock streams module under SunOS 4.2.
+#
+# Define -DGOES to support a Kinemetrics TrueTime 468-DC GOES receiver. This
+# driver may work with other True-Time products as well.
+#
+# Define -DOMEGA to support a Kinemetrics TrueTime OM-DC OMEGA receiver.
+#
+# Define -DTPRO to support a KSI/Odetics TPRO-S IRIG-B timecode reader. This
+# requires the Sun interface driver available from KSI.
+#
+# Define -DLEITCH to support a Leitch CSD 5300 Master Clock System Driver
+# for the HP 5061B Cesium Clock.
+#
+CLOCKDEFS= -DLOCAL_CLOCK -DPST -DWWVB -DWWVBPPS -DCHU -DDCF -DMX4200 -DAS2201 -DGOES -DOMEGA -DTPRO -DLEITCH -DIRIG
+
+#
+# For MIPS 4.3BSD or RISCos 4.0, include a -lmld to get the nlist() routine.
+# If USELIBKVM is defined above, include a -lkvm to get the kernel
+# routines.
+#
+#DAEMONLIBS= -lmld
+DAEMONLIBS= -lkvm
+#DAEMONLIBS=
+
+#
+# Name resolver library. Included when loading xntpres, which calls
+# gethostbyname(). Define this if you would rather use a different
+# version of the routine than the one in libc.a
+#
+#RESLIB= -lresolv
+RESLIB=
+
+#
+# Option flags for the C compiler. A -g if you are uncomfortable
+#
+COPTS= -O
+
+#
+# C compiler to use. gcc will work, but avoid the -fstrength-reduce option
+# if the version is 1.35 or earlier (using this option caused incorrect
+# code to be generated in the DES key permutation code, and perhaps
+# elsewhere).
+#
+COMPILER= gcc -pipe -Wall -g -O2 -finline-functions -fdelayed-branch -fomit-frame-pointer
+#COMPILER= cc -pipe
+
+#
+# Directory into which binaries should be installed
+#
+BINDIR= /usr/local/bin
+
+#
+# Special library for adjtime emulation. Used under HP-UX
+# (remember to run make in the adjtime directory)
+#
+#ADJLIB= ../adjtime/libadjtime.a
+ADJLIB=
+
+#
+# BSD emulation library. In theory, this fixes signal semantics under
+# HP-UX, but it doesn't work with 8.0 on a 9000s340, so there is now
+# a work-around in the code (compiled when hpux80 is defined). In other
+# words, use this for HP-UX prior to 8.0.
+#
+#COMPAT= -lBSD
+COMPAT=
+
diff --git a/usr.sbin/xntpd/conf/Config.HP-UX b/usr.sbin/xntpd/conf/Config.HP-UX
new file mode 100644
index 0000000..ef4fa30
--- /dev/null
+++ b/usr.sbin/xntpd/conf/Config.HP-UX
@@ -0,0 +1,7 @@
+#
+# Test suite for HPUX 9 (no multicast, kernel mods, disciplines, modem control)
+#
+DEFS_OPT=-DDEBUG
+DEFS_LOCAL= $(DEFS_OPT) -DREFCLOCK
+CLOCKDEFS= -DATOM -DAS2201 -DCHU -DGOES -DGPSTM -DIRIG -DLEITCH -DLOCAL_CLOCK -DMSFEES -DMX4200 -DNMEA -DOMEGA -DPST -DTPRO -DTRAK -DWWVB
+BINDIR= /usr/local/bin
diff --git a/usr.sbin/xntpd/conf/Config.MONOMOY b/usr.sbin/xntpd/conf/Config.MONOMOY
new file mode 100644
index 0000000..18dddff
--- /dev/null
+++ b/usr.sbin/xntpd/conf/Config.MONOMOY
@@ -0,0 +1,186 @@
+# Edit this file to reflect information specific to your installation.
+# Then run 'make makeconfig' to propagate the information to all the makefiles,
+# Config.MONOMOY,v 3.1 1993/07/06 01:03:43 jbj Exp
+
+# Config.bsdi by Bdale Garbee, N3EUA, bdale@gag.com
+#
+# Tested with the BSDI BSD/386 0.9.3 "gamma 4" revision. It should
+# work fine with this or later revs of BSD/386.
+#
+# Definitions for the library:
+#
+# You must define one of -DXNTP_BIG_ENDIAN, -DXNTP_LITTLE_ENDIAN
+# or -DXNTP_AUTO_ENDIAN depending on which way your machine's
+# bytes go for the benefit of the DES routine. Most things
+# sold by DEC, the NS32x32 and the 80386 deserve a
+# -DXNTP_LITTLE_ENDIAN. Most of the rest of the world does
+# it the other way. If in doubt, pick one, compile
+# everything and run authstuff/authcert < authstuff/certdata.
+# If everything fails, do it the other way.
+#
+# Under BSD, you may define -DXNTP_NETINET_ENDIAN to use
+# netinet/in.h to determine which of -DXNTP_BIG_ENDIAN and
+# XNTP_LITTLE_ENDIAN should be used.
+#
+LIBDEFS= -DXNTP_LITTLE_ENDIAN
+
+#
+# Library loading:
+#
+# If you don't want your library ranlib'ed, chose the second line
+#
+RANLIB= ranlib
+#RANLIB= : # ar does the work of ranlib under System V
+
+#
+# Definitions for programs:
+#
+# If your compiler doesn't understand the declaration `signed char',
+# add -DNO_SIGNED_CHAR_DECL. Your `char' data type had better be
+# signed. If you don't know what the compiler knows, try it
+# without the flag. If you get a syntax error on line 13 of
+# ntp.h, add it. Note that `signed char' is an ANSIism. Most
+# older, pcc-derived compilers will need this flag.
+#
+# If your library already has 's_char' defined, add -DS_CHAR_DEFINED.
+#
+# For SunOS 3.x, add -DSUN_3_3_STINKS (otherwise it will complain
+# about broadaddr and will hang if you run without a -d flag
+# on the command line. I actually can't believe the latter
+# bug. If it hangs on your system with the flag defined, peruse
+# xntpd/ntp_io.c for some rude comments about SunOS 3.5 and try it
+# the other way). This flag affects xntpd only.
+#
+# For Ultrix 2.0, add -DULT_2_0_SUCKS. This OS has the same hanging
+# bug as SunOS 3.5 (is this an original 4.2 bug?) and in addition
+# has some strangeness concerning signal masks. Ultrix 2.3 doesn't
+# have these problems. If you're running something in between
+# you're on your own. This flag affects xntpd only.
+#
+# For SunOS 4.x, add -DDOSYNCTODR_SUCKS to include the code in ntp_util.c
+# that sets the battery clock at the same time that it updates
+# the driftfile. It does this by revving up the niceness, then
+# sets the time of day to the current time of day. Ordinarily,
+# you would need this only on non-networked machines.
+#
+# There are three ways to pry loose the kernel variables tick and tickadj
+# needed by ntp_unixclock.c. One reads kmem and and is enabled
+# with -DREADKMEM. One uses Sun's libkvm and is enabled with
+# -DUSELIBKVM. The last one uses builtin defaults and is enabled
+# with -DNOKMEM. Therefore, one of -DUSELIBKVM, -DREADKMEM or
+# -DNOKMEM must be defined. Suns and recent BSD should use
+# -DUSELIBKVM; others should use -DREADKMEM. If -DUSELIBKVM, use
+# the DAEMONLIBS below to get the kernel routines.
+#
+# If your gethostbyname() routine isn't based on the DNS resolver (and,
+# in particular, h_errno doesn't exist) add a -DNODNS. There
+# doesn't seem to be a good way to detect this automatically which
+# works in all cases. This flag affects xntpres only.
+#
+# The flag -DDEBUG includes some debugging code.
+#
+# The flag -DREFCLOCK causes the basic reference clock support to be
+# compiled into the daemon. If you set this you will also want
+# to configure the particular clock drivers you want in the
+# CLOCKDEFS= line below. This flag affects xntpd only.
+#
+# To change the location of the configuration file, use a
+# -DCONFIG_FILE=\\"/local/etc/ntp.conf\\" or something similar.
+#
+# Under HP-UX, you must use either -Dhpux70 or -Dhpux80 as,
+# well as -DNOKMEM
+#
+# If your library doesn't include the vsprintf() routine, define
+# NEED_VSPRINTF.
+#
+# Define -DPPS to include support for a 1-pps signal. Define -DPPSDEV
+# to include a device driver for it. The latter requires a
+# serial port and either a line discipline or STREAMS module.
+# The PPS signal may also be generated via a reference clock
+# module like DCF77. In that case a special define is required for
+# the reference clock module (only one source of PPS signal should
+# be used)
+#
+DEFS= -DBSDI -DUSELIBKVM -DDEBUG -DREFCLOCK -DPPS -DCONFIG_FILE=\\"/usr/local/etc/xntp.conf\\" -DNTP_POSIX_SOURCE
+
+#
+# Authentication types supported. Choose from DES and MD5. If you
+# have a 680x0 type CPU and GNU-C, also choose -DFASTMD5
+#
+AUTHDEFS=-DDES -DMD5
+
+#
+# Clock support definitions (these only make sense if -DREFCLOCK used):
+#
+# Define -DLOCAL_CLOCK to include local pseudo-clock support
+#
+# Define -DPST to include support for the PST 1020 WWV/H receiver.
+#
+# Define -DWWVB to include support for the Spectracom 8170 WWVB receiver.
+#
+# Define -DCHU to include support for a driver to receive the CHU
+# timecode. Note that to compile in CHU support you must
+# previously have installed the CHU serial line discipline in
+# the kernel of the machine you are doing the compile on.
+#
+# Define -DDCF to include support for the DCF77 receiver. This code
+# requires a special STREAMS module found in the kernel directory.
+# Define -DDCFPPS for PPS support via the DCF77 receiver
+# (see also: -DPPS)
+#
+# Define -DGOES to support a Kinemetrics TrueTime 468-DC GOES receiver.
+#
+CLOCKDEFS= -DLOCAL_CLOCK -DPST -DWWVB -DCHU -DGOES # -DMX4200 -DAS2201
+
+#
+# For MIPS 4.3BSD or RISCos 4.0, include a -lmld to get the nlist() routine.
+# If USELIBKVM is defined above, include a -lkvm to get the kernel
+# routines.
+#
+#DAEMONLIBS= -lmld
+DAEMONLIBS= -lkvm
+#DAEMONLIBS=
+
+#
+# Name resolver library. Included when loading xntpres, which calls
+# gethostbyname(). Define this if you would rather use a different
+# version of the routine than the one in libc.a
+#
+#RESLIB= -lresolv
+RESLIB=
+
+#
+# Option flags for the C compiler. A -g if you are uncomfortable
+#
+COPTS= -O
+
+#
+# C compiler to use. gcc will work, but avoid the -fstrength-reduce option
+# if the version is 1.35 or earlier (using this option caused incorrect
+# code to be generated in the DES key permutation code, and perhaps
+# elsewhere).
+#
+COMPILER= gcc -pipe -Wall -g -O -finline-functions -fdelayed-branch -fomit-frame-pointer
+#COMPILER= cc -pipe -g
+
+#
+# Directory into which binaries should be installed
+#
+BINDIR= /usr/local/bin
+
+#
+# Special library for adjtime emulation. Used under HP-UX
+# (remember to run make in the adjtime directory)
+#
+#ADJLIB= ../adjtime/libadjtime.a
+ADJLIB=
+
+#
+# BSD emulation library. In theory, this fixes signal semantics under
+# HP-UX, but it doesn't work with 8.0 on a 9000s340, so there is now
+# a work-around in the code (compiled when hpux80 is defined). In other
+# words, use this for HP-UX prior to 8.0.
+#
+#COMPAT= -lBSD
+COMPAT=
+
diff --git a/usr.sbin/xntpd/conf/Config.OSF1 b/usr.sbin/xntpd/conf/Config.OSF1
new file mode 100644
index 0000000..f460e9f
--- /dev/null
+++ b/usr.sbin/xntpd/conf/Config.OSF1
@@ -0,0 +1,7 @@
+#
+# Test suite for DEC OSF/1 V1.x (no disciplines)
+#
+DEFS_OPT=-DDEBUG
+DEFS_LOCAL= $(DEFS_OPT) -DREFCLOCK -DKERNEL_PLL -DMCAST
+CLOCKDEFS= -DACTS -DATOM -DAS2201 -DCHU -DDATUM -DGOES -DGPSTM -DHEATH -DIRIG -DLEITCH -DLOCAL_CLOCK -DMSFEES -DMX4200 -DNMEA -DOMEGA -DPST -DTPRO -DTRAK -DWWVB
+BINDIR= /usr/local/bin
diff --git a/usr.sbin/xntpd/conf/Config.SunOS b/usr.sbin/xntpd/conf/Config.SunOS
new file mode 100644
index 0000000..42fd1a5
--- /dev/null
+++ b/usr.sbin/xntpd/conf/Config.SunOS
@@ -0,0 +1,7 @@
+#
+# Test suite for SunOS 4.1.x (kitchen sink)
+#
+DEFS_OPT=-DDEBUG
+DEFS_LOCAL= $(DEFS_OPT) -DREFCLOCK -DMCAST -DKERNEL_PLL -DCLK -DCHU -DPPS
+CLOCKDEFS= -DACTS -DATOM -DAS2201 -DCHU -DDATUM -DGOES -DGPSTM -DHEATH -DIRIG -DLEITCH -DLOCAL_CLOCK -DMSFEES -DMX4200 -DNMEA -DOMEGA -DPST -DTPRO -DTRAK -DWWVB
+BINDIR= /usr/local/bin
diff --git a/usr.sbin/xntpd/conf/Config.TIGER b/usr.sbin/xntpd/conf/Config.TIGER
new file mode 100644
index 0000000..29c6cbd
--- /dev/null
+++ b/usr.sbin/xntpd/conf/Config.TIGER
@@ -0,0 +1,182 @@
+# Edit this file to reflect information specific to your installation.
+# Then run 'make makeconfig' to propagate the information to all the makefiles,
+# Config.TIGER,v 3.1 1993/07/06 01:03:45 jbj Exp
+
+#
+# Definitions for the library:
+#
+# You must define one of -DXNTP_BIG_ENDIAN, -DXNTP_LITTLE_ENDIAN
+# or -DXNTP_AUTO_ENDIAN depending on which way your machine's
+# bytes go for the benefit of the DES routine. Most things
+# sold by DEC, the NS32x32 and the 80386 deserve a
+# -DXNTP_LITTLE_ENDIAN. Most of the rest of the world does
+# it the other way. If in doubt, pick one, compile
+# everything and run authstuff/authcert < authstuff/certdata.
+# If everything fails, do it the other way.
+#
+# Under BSD, you may define -DXNTP_NETINET_ENDIAN to use
+# netinet/in.h to determine which of -DXNTP_BIG_ENDIAN and
+# XNTP_LITTLE_ENDIAN should be used.
+#
+LIBDEFS= -DXNTP_LITTLE_ENDIAN
+
+#
+# Library loading:
+#
+# If you don't want your library ranlib'ed, chose the second line
+#
+RANLIB= ranlib
+#RANLIB= : # ar does the work of ranlib under System V
+
+#
+# Definitions for programs:
+#
+# If your compiler doesn't understand the declaration `signed char',
+# add -DNO_SIGNED_CHAR_DECL. Your `char' data type had better be
+# signed. If you don't know what the compiler knows, try it
+# without the flag. If you get a syntax error on line 13 of
+# ntp.h, add it. Note that `signed char' is an ANSIism. Most
+# older, pcc-derived compilers will need this flag.
+#
+# If your library already has 's_char' defined, add -DS_CHAR_DEFINED.
+#
+# For SunOS 3.x, add -DSUN_3_3_STINKS (otherwise it will complain
+# about broadaddr and will hang if you run without a -d flag
+# on the command line. I actually can't believe the latter
+# bug. If it hangs on your system with the flag defined, peruse
+# xntpd/ntp_io.c for some rude comments about SunOS 3.5 and try it
+# the other way). This flag affects xntpd only.
+#
+# For Ultrix 2.0, add -DULT_2_0_SUCKS. This OS has the same hanging
+# bug as SunOS 3.5 (is this an original 4.2 bug?) and in addition
+# has some strangeness concerning signal masks. Ultrix 2.3 doesn't
+# have these problems. If you're running something in between
+# you're on your own. This flag affects xntpd only.
+#
+# For SunOS 4.x, add -DDOSYNCTODR_SUCKS to include the code in ntp_util.c
+# that sets the battery clock at the same time that it updates
+# the driftfile. It does this by revving up the niceness, then
+# sets the time of day to the current time of day. Ordinarily,
+# you would need this only on non-networked machines.
+#
+# There are three ways to pry loose the kernel variables tick and tickadj
+# needed by ntp_unixclock.c. One reads kmem and and is enabled
+# with -DREADKMEM. One uses Sun's libkvm and is enabled with
+# -DUSELIBKVM. The last one uses builtin defaults and is enabled
+# with -DNOKMEM. Therefore, one of -DUSELIBKVM, -DREADKMEM or
+# -DNOKMEM must be defined. Suns and recent BSD should use
+# -DUSELIBKVM; others should use -DREADKMEM. If -DUSELIBKVM, use
+# the DAEMONLIBS below to get the kernel routines.
+#
+# If your gethostbyname() routine isn't based on the DNS resolver (and,
+# in particular, h_errno doesn't exist) add a -DNODNS. There
+# doesn't seem to be a good way to detect this automatically which
+# works in all cases. This flag affects xntpres only.
+#
+# The flag -DDEBUG includes some debugging code.
+#
+# The flag -DREFCLOCK causes the basic reference clock support to be
+# compiled into the daemon. If you set this you will also want
+# to configure the particular clock drivers you want in the
+# CLOCKDEFS= line below. This flag affects xntpd only.
+#
+# To change the location of the configuration file, use a
+# -DCONFIG_FILE=\\"/local/etc/ntp.conf\\" or something similar.
+#
+# Under HP-UX, you must use either -Dhpux70 or -Dhpux80 as,
+# well as -DNOKMEM
+#
+# If your library doesn't include the vsprintf() routine, define
+# NEED_VSPRINTF.
+#
+# Define -DPPS to include support for a 1-pps signal. Define -DPPSDEV
+# to include a device driver for it. The latter requires a
+# serial port and either a line discipline or STREAMS module.
+# The PPS signal may also be generated via a reference clock
+# module like DCF77. In that case a special define is required for
+# the reference clock module (only one source of PPS signal should
+# be used)
+#
+DEFS= -DREFCLOCK -DS_CHAR_DEFINED -DREADKMEM -DDEBUG -DPLL -DXNTP_RETROFIT_STDLIB -DNTP_POSIX_SOURCE
+
+#
+# Authentication types supported. Choose from DES and MD5. If you
+# have a 680x0 type CPU and GNU-C, also choose -DFASTMD5
+#
+AUTHDEFS=-DDES -DMD5
+
+#
+# Clock support definitions (these only make sense if -DREFCLOCK used):
+#
+# Define -DLOCAL_CLOCK to include local pseudo-clock support
+#
+# Define -DPST to include support for the PST 1020 WWV/H receiver.
+#
+# Define -DWWVB to include support for the Spectracom 8170 WWVB receiver.
+#
+# Define -DCHU to include support for a driver to receive the CHU
+# timecode. Note that to compile in CHU support you must
+# previously have installed the CHU serial line discipline in
+# the kernel of the machine you are doing the compile on.
+#
+# Define -DDCF to include support for the DCF77 receiver. This code
+# requires a special STREAMS module found in the kernel directory.
+# Define -DDCFPPS for PPS support via the DCF77 receiver
+# (see also: -DPPS)
+#
+# Define -DGOES to support a Kinemetrics TrueTime 468-DC GOES receiver.
+#
+CLOCKDEFS= -DLOCAL_CLOCK -DPST -DWWVB -DGOES -DCHU
+
+#
+# For MIPS 4.3BSD or RISCos 4.0, include a -lmld to get the nlist() routine.
+# If USELIBKVM is defined above, include a -lkvm to get the kernel
+# routines.
+#
+#DAEMONLIBS= -lmld
+#DAEMONLIBS= -lkvm
+DAEMONLIBS=
+
+#
+# Name resolver library. Included when loading xntpres, which calls
+# gethostbyname(). Define this if you would rather use a different
+# version of the routine than the one in libc.a
+#
+#RESLIB= -lresolv
+RESLIB=
+
+#
+# Option flags for the C compiler. A -g if you are uncomfortable
+#
+COPTS= -O
+
+#
+# C compiler to use. gcc will work, but avoid the -fstrength-reduce option
+# if the version is 1.35 or earlier (using this option caused incorrect
+# code to be generated in the DES key permutation code, and perhaps
+# elsewhere).
+#
+COMPILER= gcc -Wall -g -O2 -finline-functions -fdelayed-branch -fomit-frame-pointer
+#COMPILER= cc
+
+#
+# Directory into which binaries should be installed
+#
+BINDIR= /usr/local/bin
+
+#
+# Special library for adjtime emulation. Used under HP-UX
+# (remember to run make in the adjtime directory)
+#
+#ADJLIB= ../adjtime/libadjtime.a
+ADJLIB=
+
+#
+# BSD emulation library. In theory, this fixes signal semantics under
+# HP-UX, but it doesn't work with 8.0 on a 9000s340, so there is now
+# a work-around in the code (compiled when hpux80 is defined). In other
+# words, use this for HP-UX prior to 8.0.
+#
+#COMPAT= -lBSD
+COMPAT=
+
diff --git a/usr.sbin/xntpd/conf/Config.TRURO b/usr.sbin/xntpd/conf/Config.TRURO
new file mode 100644
index 0000000..2fc2580
--- /dev/null
+++ b/usr.sbin/xntpd/conf/Config.TRURO
@@ -0,0 +1,202 @@
+# Edit this file to reflect information specific to your installation.
+# Then run 'make makeconfig' to propagate the information to all the makefiles,
+# Config.TRURO,v 3.1 1993/07/06 01:03:46 jbj Exp
+
+#
+# Definitions for the library:
+#
+# You must define one of -DXNTP_BIG_ENDIAN, -DXNTP_LITTLE_ENDIAN
+# or -DXNTP_AUTO_ENDIAN depending on which way your machine's
+# bytes go for the benefit of the DES routine. Most things
+# sold by DEC, the NS32x32 and the 80386 deserve a
+# -DXNTP_LITTLE_ENDIAN. Most of the rest of the world does
+# it the other way. If in doubt, pick one, compile
+# everything and run authstuff/authcert < authstuff/certdata.
+# If everything fails, do it the other way.
+#
+# Under BSD, you may define -DXNTP_NETINET_ENDIAN to use
+# netinet/in.h to determine which of -DXNTP_BIG_ENDIAN and
+# XNTP_LITTLE_ENDIAN should be used.
+#
+LIBDEFS= -DWORDS_BIGENDIAN
+
+#
+# Library loading:
+#
+# If you don't want your library ranlib'ed, chose the second line
+#
+RANLIB= : # ar does the work of ranlib under System V
+
+#
+# Definitions for programs:
+#
+# If your compiler doesn't understand the declaration `signed char',
+# add -DNO_SIGNED_CHAR_DECL. Your `char' data type had better be
+# signed. If you don't know what the compiler knows, try it
+# without the flag. If you get a syntax error on line 13 of
+# ntp.h, add it. Note that `signed char' is an ANSIism. Most
+# older, pcc-derived compilers will need this flag.
+#
+# If your library already has 's_char' defined, add -DS_CHAR_DEFINED.
+#
+# For SunOS 3.x, add -DSUN_3_3_STINKS (otherwise it will complain
+# about broadaddr and will hang if you run without a -d flag
+# on the command line. I actually can't believe the latter
+# bug. If it hangs on your system with the flag defined, peruse
+# xntpd/ntp_io.c for some rude comments about SunOS 3.5 and try it
+# the other way). This flag affects xntpd only.
+#
+# For Ultrix 2.0, add -DULT_2_0_SUCKS. This OS has the same hanging
+# bug as SunOS 3.5 (is this an original 4.2 bug?) and in addition
+# has some strangeness concerning signal masks. Ultrix 2.3 doesn't
+# have these problems. If you're running something in between
+# you're on your own. This flag affects xntpd only.
+#
+# For SunOS 4.x, add -DDOSYNCTODR_SUCKS to include the code in ntp_util.c
+# that sets the battery clock at the same time that it updates
+# the driftfile. It does this by revving up the niceness, then
+# sets the time of day to the current time of day. Ordinarily,
+# you would need this only on non-networked machines.
+#
+# For some machines, settimeofday does not set the sub-second component
+# of the time correctly. For these machines add -DSETTIMEOFDAY_BROKEN.
+# If xntpd keeps STEPPING the clock by small amounts, then it is
+# possible that you are suffering from this problem.
+#
+# There are four ways to pry loose the kernel variables tick and tickadj
+# needed by ntp_unixclock.c. One reads kmem and and is enabled
+# with -DREADKMEM. One uses Sun's libkvm and is enabled with
+# -DUSELIBKVM. The last one uses builtin defaults and is enabled
+# with -DNOKMEM. Therefore, one of -DUSELIBKVM, -DREADKMEM or
+# -DNOKMEM must be defined. Suns, if they are not running Solaris,
+# and recent BSD should use -DUSELIBKVM; others should use
+# -DREADKMEM. Soalris 2.1 should use -DSOLARIS.
+# If -DUSELIBKVM, use the DAEMONLIBS below to get the
+# kernel routines.
+#
+# If your gethostbyname() routine isn't based on the DNS resolver (and,
+# in particular, h_errno doesn't exist) add a -DNODNS. There
+# doesn't seem to be a good way to detect this automatically which
+# works in all cases. This flag affects xntpres only.
+#
+# The flag -DDEBUG includes some debugging code.
+#
+# The flag -DREFCLOCK causes the basic reference clock support to be
+# compiled into the daemon. If you set this you will also want
+# to configure the particular clock drivers you want in the
+# CLOCKDEFS= line below. This flag affects xntpd only.
+#
+# To change the location of the configuration file, use a
+# -DCONFIG_FILE=\\"/local/etc/ntp.conf\\" or something similar.
+#
+# Under HP-UX, you must use either -Dhpux70 or -Dhpux80 as,
+# well as -DNOKMEM
+#
+# Under Solaris 2.1, you must use -DSOLARIS and -DSLEWALWAYS.
+# Don't define USELIBKVM, NOKMEM or READKMEM.
+#
+# If your library doesn't include the vsprintf() routine, define
+# NEED_VSPRINTF.
+#
+# There are three ways to utilize external 1-pps signals. Define -DPPS to
+# include just the pps routine, such as used by the DCF77 reference clock
+# driver. Define -DPPSDEV ito include a serial device driver. This
+# requires a serial port and either a line discipline or STREAMS module.
+# Define -DPPSCD to include the driver and a special kernal hack
+# (for SunOS 4.1.1) that intercepts carrier-detect transitions
+# generated by the pps signal. Only one of these flags should be defined.
+#
+DEFS= -DDEBUG -DSTREAM -DREFCLOCK -DNO_SIGNED_CHAR_DECL -DSLEWALWAYS -DSOLARIS -DPPS -DSTUPID_SIGNAL -DXNTP_RETROFIT_STDLIB -DNTP_POSIX_SOURCE
+
+#
+# Authentication types supported. Choose from DES and MD5. If you
+# have a 680x0 type CPU and GNU-C, also choose -DFASTMD5
+#
+AUTHDEFS=-DDES -DMD5
+
+#
+# Clock support definitions (these only make sense if -DREFCLOCK used):
+#
+# Define -DLOCAL_CLOCK to include local pseudo-clock support
+#
+# Define -DPST to include support for the PST 1020 WWV/H receiver.
+#
+# Define -DWWVB to include support for the Spectracom 8170 WWVB receiver.
+# Define -DWWVBPPS for PPS support via the WWVB receiver; also,
+# define -DPPSCD in the DEFS above. This requires the ppsclock
+# streams module under SunOS 4.2.
+#
+# Define -DCHU to include support for a driver to receive the CHU
+# timecode. Note that to compile in CHU support you must
+# previously have installed the CHU serial line discipline in
+# the kernel of the machine you are doing the compile on.
+#
+# Define -DDCF to include support for the DCF77 receiver. This code
+# requires a special STREAMS module found in the kernel directory.
+# Define -DDCFPPS for PPS support via the DCF77 receiver; also,
+# devine -DPPS in the DEFS above.
+#
+# Define -DMX4200 to support a Magnavox 4200 GPS receiver. Define -DPPSCD
+# in the DEFS above for PPS support via this receiver. This requires
+# the ppsclock streams module under SunOS 4.2.
+#
+# Define -DAS2201 to include support for the Austron 2201 GPS Timing
+# Receiver. Define -DPPSCD in the DEFS above for PPS support via this
+# receiver. This requires the ppsclock streams module under SunOS 4.2.
+#
+# Define -DGOES to support a Kinemetrics TrueTime 468-DC GOES receiver. This
+# driver may work with other True-Time products as well.
+#
+CLOCKDEFS= -DLOCAL_CLOCK -DPST -DWWVB -DWWVBPPS -DGOES -DCHU -DMX4200 -DAS2201 -DOMEGA -DTPRO -DLEITCH -DIRIG
+
+#
+# For MIPS 4.3BSD or RISCos 4.0, include a -lmld to get the nlist() routine.
+# If USELIBKVM is defined above, include a -lkvm to get the kernel
+# routines.
+#
+#DAEMONLIBS= -lmld
+DAEMONLIBS=
+
+#
+# Name resolver library. Included when loading xntpres, which calls
+# gethostbyname(). Define this if you would rather use a different
+# version of the routine than the one in libc.a
+#
+#RESLIB= -lresolv
+RESLIB= -lsocket -lnsl -lelf
+
+#
+# Option flags for the C compiler. A -g if you are uncomfortable
+#
+COPTS= -O
+
+#
+# C compiler to use. gcc will work, but avoid the -fstrength-reduce option
+# if the version is 1.35 or earlier (using this option caused incorrect
+# code to be generated in the DES key permutation code, and perhaps
+# elsewhere).
+#
+#COMPILER= gcc -traditional
+COMPILER= gcc -pipe -Wall -g -O2 -finline-functions -fdelayed-branch -fomit-frame-pointer
+
+#
+# Directory into which binaries should be installed
+#
+BINDIR= /usr/local/bin
+
+#
+# Special library for adjtime emulation. Used under HP-UX
+# (remember to run make in the adjtime directory)
+#
+#ADJLIB= ../adjtime/libadjtime.a
+ADJLIB=
+
+#
+# BSD emulation library. In theory, this fixes signal semantics under
+# HP-UX, but it doesn't work with 8.0 on a 9000s340, so there is now
+# a work-around in the code (compiled when hpux80 is defined). In other
+# words, use this for HP-UX prior to 8.0.
+#
+#COMPAT= -lBSD
+COMPAT=
+
diff --git a/usr.sbin/xntpd/conf/Config.ULTRIX b/usr.sbin/xntpd/conf/Config.ULTRIX
new file mode 100644
index 0000000..4ead1be
--- /dev/null
+++ b/usr.sbin/xntpd/conf/Config.ULTRIX
@@ -0,0 +1,7 @@
+#
+# Test suite for Ultrix 4.x (no disciplines)
+#
+DEFS_OPT=-DDEBUG
+DEFS_LOCAL= $(DEFS_OPT) -DREFCLOCK -DKERNEL_PLL -DMCAST
+CLOCKDEFS= -DACTS -DATOM -DAS2201 -DCHU -DDATUM -DGOES -DGPSTM -DHEATH -DIRIG -DLEITCH -DLOCAL_CLOCK -DMSFEES -DMX4200 -DNMEA -DOMEGA -DPST -DTPRO -DTRAK -DWWVB
+BINDIR= /usr/local/bin
diff --git a/usr.sbin/xntpd/conf/Config.VAX b/usr.sbin/xntpd/conf/Config.VAX
new file mode 100644
index 0000000..66b9f91
--- /dev/null
+++ b/usr.sbin/xntpd/conf/Config.VAX
@@ -0,0 +1,7 @@
+#
+# Test suite for 4.3bsd VAX tahoe (no multicast, kernel mods, disciplines)
+#
+DEFS_OPT=-DDEBUG
+DEFS_LOCAL= $(DEFS_OPT) -DREFCLOCK
+CLOCKDEFS= -DACTS -DATOM -DAS2201 -DCHU -DDATUM -DGOES -DGPSTM -DHEATH -DIRIG -DLEITCH -DLOCAL_CLOCK -DMSFEES -DMX4200 -DNMEA -DOMEGA -DPST -DTPRO -DTRAK -DWWVB
+BINDIR= /usr/local/bin
diff --git a/usr.sbin/xntpd/conf/Config.dartnet b/usr.sbin/xntpd/conf/Config.dartnet
new file mode 100644
index 0000000..b591db3
--- /dev/null
+++ b/usr.sbin/xntpd/conf/Config.dartnet
@@ -0,0 +1,187 @@
+# This is the local configure file (distribution version).
+# You must modify it to fit your particular configuration
+# and name it Config.local
+# The following configuratiions can be auto-generated:
+#
+# make Config.local.green
+# make a Config.local that supports a local clock
+# (i.e. allow fallback to use of the CPU's own clock)
+# make Config.local.NO.clock
+# make a Config.local that supports no clocks
+#
+#
+# NOTE TO GREENHORNS
+#
+# For plug-'n-play and no radios or other complicated gadgetry,
+# use "make Config.local.green" or "make Config.local.local" as above.
+#
+# Following defines can be set in the DEFS_OPT= define:
+#
+# The flag -DDEBUG includes some debugging code. To use this, include
+# the define and start the daemon with one or more -d flags, depending
+# on your calibration of pearannoya. The daemon will not detach your
+# terminal in this case. Judicious use of grep will reduce the speaker
+# volume to bearable levels.
+#
+# To change the location of the configuration file, use a
+# -DCONFIG_FILE=\\"/local/etc/ntp.conf\\" or something similar.
+#
+# The -DSYSLOG_FILE defines allows logging messages that are normally
+# reported via syslof() in a file. The file name can be configured using
+# the configuration line "logfile <filename>" in CONFIG_FILE.
+#
+# There are three serial port system software interfaces, each of
+# which is peculiar to one or more Unix versions. Define
+# -DHAVE_SYSV_TTYS for basic System V compatibility; define -DSTREAM
+# for POSIX compatibility including System V Streams, and
+# HAVE_BSD_TTYS for 4.3bsd compatibility. Only one of these three
+# should be defined. If none are defined, HAVE_BSD_TTYS is assumed.
+# Usually these defines are already set correctly.
+#
+DEFS_OPT=-DDEBUG
+#
+# The DEFS_LOCAL define picks up all flags from DEFS_OPT (do not delete that)
+# and one of the following:
+#
+# The flag -DREFCLOCK causes the basic reference clock support to be
+# compiled into the daemon. If you set this you may also want to
+# configure the particular clock drivers you want in the CLOCKDEFS= line
+# below. This flag affects xntpd only. This define is included by
+# default when using the "make makeconfig" script.
+#
+# The next two sets of defines are meaningful only when radio clock
+# drivers or special 1-pps signals are to be used. For systems without
+# these features, these delicious complexities can be avoided. Ordinarily,
+# the "make makeconfig" script figures out which ones to use, but your
+# mileage may vary.
+#
+# There are three ways to utilize external 1-pps signals. Define
+# -DPPS to include just the pps routine, such as used by the DCF77(PARSE)
+# clock driver. Define -DPPSCLK to include a serial device driver
+# which avoids much of the jitter due to upper level port
+# processing. This requires a dedicated serial port and either the
+# tty_clock line discipline or tty_clk_streams module, both of
+# which are in the ./kernel directory. Define -DPPSCD to include a
+# special driver which intercepts carrier-detect transitions
+# generated by the pps signal. This requires a nondedicated serial
+# port and the ppsclock streams module in the ./kernel directory.
+# Only one of these three flags should be defined.
+#
+# The flag KERNEL_PLL causes code to be compiled for a special feature of
+# the kernel that (a) implements the phase-lock loop and (b) provides
+# a user interface to learn time, maximum error and estimated error.
+# See the file README.kern in the doc directory for further info.
+# This code is activated only if the relevant kernel features have
+# been configured; it does not affect operation of unmodified kernels.
+# To compile it, however, requires a few header files from the
+# special distribution.
+#
+# Note: following line must always start with DEFS_LOCAL= $(DEFS_OPT)
+DEFS_LOCAL= $(DEFS_OPT) -DPPSPPS -DREFCLOCK -DKERNEL_PLL
+#
+# Radio clock support definitions (these only make sense if -DREFCLOCK
+# used), which is normally the case. Note that a configuration can include
+# no clocks, more than one type of clock and even multiple clocks of the
+# same type.
+#
+# For most radio clocks operating with serial ports, accuracy can
+# be considerably improved through use of the tty_clk line
+# discipline or tty_clk_STREAMS streams module found in the
+# ./kernel directory. These gizmos capture a timestamp upon
+# occurrence of an intercept character and stuff it in the data
+# stream for the clock driver to munch. To select this mode,
+# postfix the driver name with the string CLK; that is, WWVB
+# becomes WWVBCLK. If more than one clock is in use, the CLK
+# postfix can be used with any or all of them.
+#
+# Alternatively, for the best accuracy, use the ppsclock streams
+# module in the ./ppsclock directory to steal the carrier-detect
+# transition and capture a precision timestamp. At present this
+# works only with SunOS 4.1.1 or later. To select this mode,
+# postfix the driver name with the string PPS; that is, AS2201
+# becomes AS2201PPS. If more than one clock is in use, the PPS
+# postfix should be used with only one of them. If any PPS
+# postfix is defined, the -DPPSPPS define should be used on the
+# DEFS above.
+#
+# Define -DLOCAL_CLOCK for a local pseudo-clock to masquerade as a
+# reference clock for those subnets without access to the real thing.
+# Works in all systems and requires no hardware support. This is defined
+# by default when using the "make makeconfig" script and greenhorn
+# configuraiton.
+#
+# Define -DPST for a PST/Traconex 1020 WWV/H receiver. The driver
+# supports both the CLK and PPS modes. It should work in all systems
+# with a serial port.
+#
+# Define -DWWVB for a Spectracom 8170 or Netclock/2 WWVB receiver. It
+# should work in all systems with a serial port. The driver supports
+# both the CLK and PPS modes if the requisite kernel support is installed.
+#
+# Define -DCHU for a special CHU receiver using an ordinary shortwave
+# radio. This requires the chu_clk line discipline or chu_clk_STREAMS
+# module in the ./kernel directory. At present, this driver works only
+# on SunOS4.1.x; operation in other systems has not been confirmed.
+# Construction details for a suitable modem can be found in the ./gadget
+# directory. The driver supports # neither the CLK nor PPS modes.
+#
+# Define -DPARSE for a DCF77/GPS(GENERIC) receiver. For best performance
+# this requires a special parsestreams STREAMS (SunOS 4.x) module in the
+# ./parse directory. Define -DPARSEPPS for PPS support via the
+# DCF77/GPS (GENERIC) receiver; also, define -DPPS in the DEFS above.
+# Define: -DCLOCK_MEINBERG for Meinberg clocks
+# -DCLOCK_SCHMID for Schmid receivers
+# -DCLOCK_DCF7000 for ELV DCF7000
+# -DCLOCK_RAWDCF for simple receivers (100/200ms pulses on Rx)
+# -DCLOCK_TRIMSV6 for Trimble SV6 GPS receiver
+#
+# Define -DMX4200PPS for a Magnavox 4200 GPS receiver. At present, this
+# driver works only on SunOS4.1.x with CPU serial ports only. The PPS
+# mode is required.
+#
+# Define -DAS2201 for an Austron 2200A or 2201A GPS receiver. It should
+# work in all systems with a serial port. The driver does not support the
+# CLK mode, but does support the PPS mode. If the radio is connected to
+# more than one machine, the PPS mode is required.
+#
+# Define -DGOES for a Kinemetrics/TrueTime 468-DC GOES receiver. This
+# driver is known to work with some other TrueTime products as well,
+# including the GPS-DC GPS receiver. It should work in all systems with
+# a serial port. The driver does not support the CLK mode, but does
+# support the PPS mode.
+#
+# Define -DOMEGA for a Kinemetrics/TrueTime OM-DC OMEGA receiver. It
+# should work in all systems with a serial port. The driver does not
+# support the CLK mode, but does support the PPS mode.
+#
+# Define -DTPRO for a KSI/Odetics TPRO-S IRIG-B timecode reader. This
+# requires the SunOS interface driver available from KSI. The driver
+# supports neither the CLK nor PPS modes.
+#
+# Define -DLEITCH for a Leitch CSD 5300 Master Clock System Driver for
+# the HP 5061B Cesium Clock. It should work in all systems with a serial
+# port. The driver does not support the CLK mode, but does support the
+# PPS mode.
+#
+# Define -DMSFEESPPS for an EES M201 MSF receiver. It currently only works
+# under SunOS 4.x with the PPSCD (ppsclock) STREAMS module, but the RCS
+# files on cl.cam.ac.uk still has support for CLK and CBREAK modes.
+#
+# Define -DIRIG for a IRIG-B timecode timecode using the audio codec of
+# the Sun SPARCstations. This requires a modified BSD audio driver and
+# exclusive access to the audio port. A memo describing how it works and
+# how to install the driver is in the README.irig file in the ./doc
+# directory.
+#
+# Note: The following defines result in compilation of all the above radio
+# clocks. This works on a Sun 4.1.x system which has tty_clk, chu_clk and
+# ppsclock STREAMS modules installed. If the trailing CLK and PPS suffixes
+# are removed and the IRIG, PARSE* and CLOCK* deleted, all of the rest compile
+# under Ultrix 4.2a/3. If the MX4200 is removed, all the rest compile on a DEC
+# OSF/1 Alpha.
+#
+CLOCKDEFS=-DAS2201PPS -DCHU -DGOES -DIRIG -DLEITCH -DLOCAL_CLOCK -DMX4200PPS -DOMEGA -DPSTCLK -DTPRO -DWWVBCLK
+#
+# Directory into which binaries should be installed (default /usr/local)
+#
+BINDIR= /usr/local/bin
diff --git a/usr.sbin/xntpd/conf/Config.local b/usr.sbin/xntpd/conf/Config.local
new file mode 100644
index 0000000..22c12a3
--- /dev/null
+++ b/usr.sbin/xntpd/conf/Config.local
@@ -0,0 +1,190 @@
+# This is the local configure file (distribution version).
+# You must modify it to fit your particular configuration
+# and name it Config.local
+# The following configuratiions can be auto-generated:
+#
+# make Config.local.green
+# make a Config.local that supports a local clock
+# (i.e. allow fallback to use of the CPU's own clock)
+# make Config.local.NO.clock
+# make a Config.local that supports no clocks
+#
+#
+# NOTE TO GREENHORNS
+#
+# For plug-'n-play and no radios or other complicated gadgetry,
+# use "make Config.local.green" as above.
+#
+# Following defines can be set in the DEFS_OPT= define:
+#
+# The flag -DDEBUG includes some debugging code. To use this, include
+# the define and start the daemon with one or more -d flags, depending
+# on your calibration of pearannoya. The daemon will not detach your
+# terminal in this case. Judicious use of grep will reduce the speaker
+# volume to bearable levels.
+#
+# To change the location of the configuration file, use a
+# -DCONFIG_FILE=\\"/local/etc/ntp.conf\\" or something similar.
+#
+# The -DSYSLOG_FILE defines allows logging messages that are normally
+# reported via syslof() in a file. The file name can be configured using
+# the configuration line "logfile <filename>" in CONFIG_FILE.
+#
+# There are three serial port system software interfaces, each of
+# which is peculiar to one or more Unix versions. Define
+# -DHAVE_SYSV_TTYS for basic System V compatibility; define -DSTREAM
+# for POSIX compatibility including System V Streams, and
+# HAVE_BSD_TTYS for 4.3bsd compatibility. Only one of these three
+# should be defined. If none are defined, HAVE_BSD_TTYS is assumed.
+# Usually these defines are already set correctly.
+#
+DEFS_OPT=-DDEBUG
+
+#
+# The DEFS_LOCAL define picks up all flags from DEFS_OPT (do not delete that)
+# and one of the following:
+#
+# The flag -DREFCLOCK causes the basic reference clock support to be
+# compiled into the daemon. If you set this you may also want to
+# configure the particular clock drivers you want in the CLOCKDEFS= line
+# below. This flag affects xntpd only. This define is included by
+# default when using the "make makeconfig" script.
+#
+# The next two sets of defines are meaningful only when radio clock
+# drivers or special 1-pps signals are to be used. For systems without
+# these features, these delicious complexities can be avoided. Ordinarily,
+# the "make makeconfig" script figures out which ones to use, but your
+# mileage may vary.
+#
+# There are three ways to utilize external 1-pps signals. Define
+# -DPPS to include just the pps routine, such as used by the DCF77(PARSE)
+# clock driver. Define -DPPSCLK to include a serial device driver
+# which avoids much of the jitter due to upper level port
+# processing. This requires a dedicated serial port and either the
+# tty_clock line discipline or tty_clk_streams module, both of
+# which are in the ./kernel directory. Define -DPPSCD to include a
+# special driver which intercepts carrier-detect transitions
+# generated by the pps signal. This requires a nondedicated serial
+# port and the ppsclock streams module in the ./kernel directory.
+# Only one of these three flags should be defined.
+#
+# The flag KERNEL_PLL causes code to be compiled for a special feature of
+# the kernel that (a) implements the phase-lock loop and (b) provides
+# a user interface to learn time, maximum error and estimated error.
+# See the file README.kern in the doc directory for further info.
+# This code is activated only if the relevant kernel features have
+# been configured; it does not affect operation of unmodified kernels.
+# To compile it, however, requires a few header files from the
+# special distribution.
+#
+# Note: following line must always start with DEFS_LOCAL= $(DEFS_OPT)
+DEFS_LOCAL= $(DEFS_OPT) -DREFCLOCK -DKERNEL_PLL -DMCAST
+
+#
+# Radio clock support definitions (these only make sense if -DREFCLOCK
+# used), which is normally the case. Note that a configuration can include
+# no clocks, more than one type of clock and even multiple clocks of the
+# same type.
+#
+# For most radio clocks operating with serial ports, accuracy can
+# be considerably improved through use of the tty_clk line
+# discipline or tty_clk_STREAMS streams module found in the
+# ./kernel directory. These gizmos capture a timestamp upon
+# occurrence of an intercept character and stuff it in the data
+# stream for the clock driver to munch. To select this mode,
+# postfix the driver name with the string CLK; that is, WWVB
+# becomes WWVBCLK. If more than one clock is in use, the CLK
+# postfix can be used with any or all of them.
+#
+# Alternatively, for the best accuracy, use the ppsclock streams
+# module in the ./ppsclock directory to steal the carrier-detect
+# transition and capture a precision timestamp. At present this
+# works only with SunOS 4.1.1 or later. To select this mode,
+# postfix the driver name with the string PPS; that is, AS2201
+# becomes AS2201PPS. If more than one clock is in use, the PPS
+# postfix should be used with only one of them. If any PPS
+# postfix is defined, the -DPPSPPS define should be used on the
+# DEFS above.
+#
+# Define -DLOCAL_CLOCK for a local pseudo-clock to masquerade as a
+# reference clock for those subnets without access to the real thing.
+# Works in all systems and requires no hardware support. This is defined
+# by default when using the "make makeconfig" script and greenhorn
+# configuraiton.
+#
+# Define -DPST for a PST/Traconex 1020 WWV/H receiver. The driver
+# supports both the CLK and PPS modes. It should work in all systems
+# with a serial port.
+#
+# Define -DWWVB for a Spectracom 8170 or Netclock/2 WWVB receiver. It
+# should work in all systems with a serial port. The driver supports
+# both the CLK and PPS modes if the requisite kernel support is installed.
+#
+# Define -DCHU for a special CHU receiver using an ordinary shortwave
+# radio. This requires the chu_clk line discipline or chu_clk_STREAMS
+# module in the ./kernel directory. At present, this driver works only
+# on SunOS4.1.x; operation in other systems has not been confirmed.
+# Construction details for a suitable modem can be found in the ./gadget
+# directory. The driver supports # neither the CLK nor PPS modes.
+#
+# Define -DPARSE for a DCF77/GPS(GENERIC) receiver. For best performance
+# this requires a special parsestreams STREAMS (SunOS 4.x) module in the
+# ./parse directory. Define -DPARSEPPS for PPS support via the
+# DCF77/GPS (GENERIC) receiver; also, define -DPPS in the DEFS above.
+# Define: -DCLOCK_MEINBERG for Meinberg clocks
+# -DCLOCK_SCHMID for Schmid receivers
+# -DCLOCK_DCF7000 for ELV DCF7000
+# -DCLOCK_RAWDCF for simple receivers (100/200ms pulses on Rx)
+# -DCLOCK_TRIMSV6 for Trimble SV6 GPS receiver
+#
+# Define -DMX4200PPS for a Magnavox 4200 GPS receiver. At present, this
+# driver works only on SunOS4.1.x with CPU serial ports only. The PPS
+# mode is required.
+#
+# Define -DAS2201 for an Austron 2200A or 2201A GPS receiver. It should
+# work in all systems with a serial port. The driver does not support the
+# CLK mode, but does support the PPS mode. If the radio is connected to
+# more than one machine, the PPS mode is required.
+#
+# Define -DGOES for a Kinemetrics/TrueTime 468-DC GOES receiver. This
+# driver is known to work with some other TrueTime products as well,
+# including the GPS-DC GPS receiver. It should work in all systems with
+# a serial port. The driver does not support the CLK mode, but does
+# support the PPS mode.
+#
+# Define -DOMEGA for a Kinemetrics/TrueTime OM-DC OMEGA receiver. It
+# should work in all systems with a serial port. The driver does not
+# support the CLK mode, but does support the PPS mode.
+#
+# Define -DTPRO for a KSI/Odetics TPRO-S IRIG-B timecode reader. This
+# requires the SunOS interface driver available from KSI. The driver
+# supports neither the CLK nor PPS modes.
+#
+# Define -DLEITCH for a Leitch CSD 5300 Master Clock System Driver for
+# the HP 5061B Cesium Clock. It should work in all systems with a serial
+# port. The driver does not support the CLK mode, but does support the
+# PPS mode.
+#
+# Define -DMSFEESPPS for an EES M201 MSF receiver. It currently only works
+# under SunOS 4.x with the PPSCD (ppsclock) STREAMS module, but the RCS
+# files on cl.cam.ac.uk still has support for CLK and CBREAK modes.
+#
+# Define -DIRIG for a IRIG-B timecode timecode using the audio codec of
+# the Sun SPARCstations. This requires a modified BSD audio driver and
+# exclusive access to the audio port. A memo describing how it works and
+# how to install the driver is in the README.irig file in the ./doc
+# directory.
+#
+# Note: The following defines result in compilation of all the above radio
+# clocks. This works on a Sun 4.1.x system which has tty_clk, chu_clk and
+# ppsclock STREAMS modules installed. If the trailing CLK and PPS suffixes
+# are removed and the IRIG, PARSE* and CLOCK* deleted, all of the rest compile
+# under Ultrix 4.2a/3. If the MX4200 is removed, all the rest compile on a DEC
+# OSF/1 Alpha.
+#
+CLOCKDEFS= -DLOCAL_CLOCK -DAS2201PPS -DCHUPPS -DGOES -DIRIG -DMX4200PPS -DOMEGA -DPSTCLK -DTPRO -DWWVBCLK -DLEITCH
+
+#
+# Directory into which binaries should be installed (default /usr/local)
+#
+BINDIR= /usr/local/bin
diff --git a/usr.sbin/xntpd/conf/Config.plain b/usr.sbin/xntpd/conf/Config.plain
new file mode 100644
index 0000000..67dd70a
--- /dev/null
+++ b/usr.sbin/xntpd/conf/Config.plain
@@ -0,0 +1,190 @@
+# This is the local configure file (distribution version).
+# You must modify it to fit your particular configuration
+# and name it Config.local
+# The following configuratiions can be auto-generated:
+#
+# make Config.local.green
+# make a Config.local that supports a local clock
+# (i.e. allow fallback to use of the CPU's own clock)
+# make Config.local.NO.clock
+# make a Config.local that supports no clocks
+#
+#
+# NOTE TO GREENHORNS
+#
+# For plug-'n-play and no radios or other complicated gadgetry,
+# use "make Config.local.green" as above.
+#
+# Following defines can be set in the DEFS_OPT= define:
+#
+# The flag -DDEBUG includes some debugging code. To use this, include
+# the define and start the daemon with one or more -d flags, depending
+# on your calibration of pearannoya. The daemon will not detach your
+# terminal in this case. Judicious use of grep will reduce the speaker
+# volume to bearable levels.
+#
+# To change the location of the configuration file, use a
+# -DCONFIG_FILE=\\"/local/etc/ntp.conf\\" or something similar.
+#
+# The -DSYSLOG_FILE defines allows logging messages that are normally
+# reported via syslof() in a file. The file name can be configured using
+# the configuration line "logfile <filename>" in CONFIG_FILE.
+#
+# There are three serial port system software interfaces, each of
+# which is peculiar to one or more Unix versions. Define
+# -DHAVE_SYSV_TTYS for basic System V compatibility; define -DSTREAM
+# for POSIX compatibility including System V Streams, and
+# HAVE_BSD_TTYS for 4.3bsd compatibility. Only one of these three
+# should be defined. If none are defined, HAVE_BSD_TTYS is assumed.
+# Usually these defines are already set correctly.
+#
+DEFS_OPT=-DDEBUG
+
+#
+# The DEFS_LOCAL define picks up all flags from DEFS_OPT (do not delete that)
+# and one of the following:
+#
+# The flag -DREFCLOCK causes the basic reference clock support to be
+# compiled into the daemon. If you set this you may also want to
+# configure the particular clock drivers you want in the CLOCKDEFS= line
+# below. This flag affects xntpd only. This define is included by
+# default when using the "make makeconfig" script.
+#
+# The next two sets of defines are meaningful only when radio clock
+# drivers or special 1-pps signals are to be used. For systems without
+# these features, these delicious complexities can be avoided. Ordinarily,
+# the "make makeconfig" script figures out which ones to use, but your
+# mileage may vary.
+#
+# There are three ways to utilize external 1-pps signals. Define
+# -DPPS to include just the pps routine, such as used by the DCF77(PARSE)
+# clock driver. Define -DPPSCLK to include a serial device driver
+# which avoids much of the jitter due to upper level port
+# processing. This requires a dedicated serial port and either the
+# tty_clock line discipline or tty_clk_streams module, both of
+# which are in the ./kernel directory. Define -DPPSCD to include a
+# special driver which intercepts carrier-detect transitions
+# generated by the pps signal. This requires a nondedicated serial
+# port and the ppsclock streams module in the ./kernel directory.
+# Only one of these three flags should be defined.
+#
+# The flag KERNEL_PLL causes code to be compiled for a special feature of
+# the kernel that (a) implements the phase-lock loop and (b) provides
+# a user interface to learn time, maximum error and estimated error.
+# See the file README.kern in the doc directory for further info.
+# This code is activated only if the relevant kernel features have
+# been configured; it does not affect operation of unmodified kernels.
+# To compile it, however, requires a few header files from the
+# special distribution.
+#
+# Note: following line must always start with DEFS_LOCAL= $(DEFS_OPT)
+DEFS_LOCAL= $(DEFS_OPT) -DREFCLOCK -DKERNEL_PLL -DMCAST
+
+#
+# Radio clock support definitions (these only make sense if -DREFCLOCK
+# used), which is normally the case. Note that a configuration can include
+# no clocks, more than one type of clock and even multiple clocks of the
+# same type.
+#
+# For most radio clocks operating with serial ports, accuracy can
+# be considerably improved through use of the tty_clk line
+# discipline or tty_clk_STREAMS streams module found in the
+# ./kernel directory. These gizmos capture a timestamp upon
+# occurrence of an intercept character and stuff it in the data
+# stream for the clock driver to munch. To select this mode,
+# postfix the driver name with the string CLK; that is, WWVB
+# becomes WWVBCLK. If more than one clock is in use, the CLK
+# postfix can be used with any or all of them.
+#
+# Alternatively, for the best accuracy, use the ppsclock streams
+# module in the ./ppsclock directory to steal the carrier-detect
+# transition and capture a precision timestamp. At present this
+# works only with SunOS 4.1.1 or later. To select this mode,
+# postfix the driver name with the string PPS; that is, AS2201
+# becomes AS2201PPS. If more than one clock is in use, the PPS
+# postfix should be used with only one of them. If any PPS
+# postfix is defined, the -DPPSPPS define should be used on the
+# DEFS above.
+#
+# Define -DLOCAL_CLOCK for a local pseudo-clock to masquerade as a
+# reference clock for those subnets without access to the real thing.
+# Works in all systems and requires no hardware support. This is defined
+# by default when using the "make makeconfig" script and greenhorn
+# configuraiton.
+#
+# Define -DPST for a PST/Traconex 1020 WWV/H receiver. The driver
+# supports both the CLK and PPS modes. It should work in all systems
+# with a serial port.
+#
+# Define -DWWVB for a Spectracom 8170 or Netclock/2 WWVB receiver. It
+# should work in all systems with a serial port. The driver supports
+# both the CLK and PPS modes if the requisite kernel support is installed.
+#
+# Define -DCHU for a special CHU receiver using an ordinary shortwave
+# radio. This requires the chu_clk line discipline or chu_clk_STREAMS
+# module in the ./kernel directory. At present, this driver works only
+# on SunOS4.1.x; operation in other systems has not been confirmed.
+# Construction details for a suitable modem can be found in the ./gadget
+# directory. The driver supports # neither the CLK nor PPS modes.
+#
+# Define -DPARSE for a DCF77/GPS(GENERIC) receiver. For best performance
+# this requires a special parsestreams STREAMS (SunOS 4.x) module in the
+# ./parse directory. Define -DPARSEPPS for PPS support via the
+# DCF77/GPS (GENERIC) receiver; also, define -DPPS in the DEFS above.
+# Define: -DCLOCK_MEINBERG for Meinberg clocks
+# -DCLOCK_SCHMID for Schmid receivers
+# -DCLOCK_DCF7000 for ELV DCF7000
+# -DCLOCK_RAWDCF for simple receivers (100/200ms pulses on Rx)
+# -DCLOCK_TRIMSV6 for Trimble SV6 GPS receiver
+#
+# Define -DMX4200PPS for a Magnavox 4200 GPS receiver. At present, this
+# driver works only on SunOS4.1.x with CPU serial ports only. The PPS
+# mode is required.
+#
+# Define -DAS2201 for an Austron 2200A or 2201A GPS receiver. It should
+# work in all systems with a serial port. The driver does not support the
+# CLK mode, but does support the PPS mode. If the radio is connected to
+# more than one machine, the PPS mode is required.
+#
+# Define -DGOES for a Kinemetrics/TrueTime 468-DC GOES receiver. This
+# driver is known to work with some other TrueTime products as well,
+# including the GPS-DC GPS receiver. It should work in all systems with
+# a serial port. The driver does not support the CLK mode, but does
+# support the PPS mode.
+#
+# Define -DOMEGA for a Kinemetrics/TrueTime OM-DC OMEGA receiver. It
+# should work in all systems with a serial port. The driver does not
+# support the CLK mode, but does support the PPS mode.
+#
+# Define -DTPRO for a KSI/Odetics TPRO-S IRIG-B timecode reader. This
+# requires the SunOS interface driver available from KSI. The driver
+# supports neither the CLK nor PPS modes.
+#
+# Define -DLEITCH for a Leitch CSD 5300 Master Clock System Driver for
+# the HP 5061B Cesium Clock. It should work in all systems with a serial
+# port. The driver does not support the CLK mode, but does support the
+# PPS mode.
+#
+# Define -DMSFEESPPS for an EES M201 MSF receiver. It currently only works
+# under SunOS 4.x with the PPSCD (ppsclock) STREAMS module, but the RCS
+# files on cl.cam.ac.uk still has support for CLK and CBREAK modes.
+#
+# Define -DIRIG for a IRIG-B timecode timecode using the audio codec of
+# the Sun SPARCstations. This requires a modified BSD audio driver and
+# exclusive access to the audio port. A memo describing how it works and
+# how to install the driver is in the README.irig file in the ./doc
+# directory.
+#
+# Note: The following defines result in compilation of all the above radio
+# clocks. This works on a Sun 4.1.x system which has tty_clk, chu_clk and
+# ppsclock STREAMS modules installed. If the trailing CLK and PPS suffixes
+# are removed and the IRIG, PARSE* and CLOCK* deleted, all of the rest compile
+# under Ultrix 4.2a/3. If the MX4200 is removed, all the rest compile on a DEC
+# OSF/1 Alpha.
+#
+CLOCKDEFS= -DLOCAL_CLOCK -DCHU -DGOES -DOMEGA -DPST -DWWVB -DLEITCH
+
+#
+# Directory into which binaries should be installed (default /usr/local)
+#
+BINDIR= /usr/local/bin
diff --git a/usr.sbin/xntpd/conf/Config.solaris b/usr.sbin/xntpd/conf/Config.solaris
new file mode 100644
index 0000000..5db3cd0
--- /dev/null
+++ b/usr.sbin/xntpd/conf/Config.solaris
@@ -0,0 +1,7 @@
+#
+# Test suite for SunOS 5.x (no kernel mods, disciplines)
+#
+DEFS_OPT=-DDEBUG
+DEFS_LOCAL= $(DEFS_OPT) -DREFCLOCK -DMCAST
+CLOCKDEFS= -DACTS -DATOM -DAS2201 -DCHU -DDATUM -DGOES -DGPSTM -DHEATH -DIRIG -DLEITCH -DLOCAL_CLOCK -DMSFEES -DMX4200 -DNMEA -DOMEGA -DPST -DTPRO -DTRAK -DWWVB
+BINDIR= /usr/local/bin
diff --git a/usr.sbin/xntpd/conf/Config.svr4 b/usr.sbin/xntpd/conf/Config.svr4
new file mode 100644
index 0000000..d6d0661
--- /dev/null
+++ b/usr.sbin/xntpd/conf/Config.svr4
@@ -0,0 +1,167 @@
+#
+# This is the local configure file. Modify it to fit your particular
+# configuration.
+#
+# NOTE TO GREENHORNS
+#
+# For plug-'n-play and no radios or other complicated gadgetry, set the
+# alternate defines as shown.
+#
+# The flag -DDEBUG includes some debugging code. To use this, include
+# the define and start the daemon with one or more -d flags, depending
+# on your calibration of pearannoya. The daemon will not detach your
+# terminal in this case. Judicious use of grep will reduce the speaker
+# volume to bearable levels.
+#
+# To change the location of the configuration file, use a
+# -DCONFIG_FILE=\\"/local/etc/ntp.conf\\" or something similar.
+#
+# The flag -DREFCLOCK causes the basic reference clock support to be
+# compiled into the daemon. If you set this you may also want to
+# configure the particular clock drivers you want in the CLOCKDEFS= line
+# below. This flag affects xntpd only. This define is included by
+# default when using the "make makeconfig" script.
+#
+# The next two sets of defines are meaningful only when radio clock
+# drivers or special 1-pps signals are to be used. For systems without
+# these features, these delicious complexities can be avoided. Ordinarily,
+# the "make makeconfig" script figures out which ones to use, but your
+# mileage may vary.
+#
+# There are three ways to utilize external 1-pps signals. Define
+# -DPPS to include just the pps routine, such as used by the DCF77
+# clock driver. Define -DPPSCLK to include a serial device driver
+# which avoids much of the jitter due to upper level port
+# processing. This requires a dedicated serial port and either the
+# tty_clock line discipline or tty_clk_streams module, both of
+# which are in the ./kernel directory. Define -DPPSCD to include a
+# special driver which intercepts carrier-detect transitions
+# generated by the pps signal. This requires a nondedicated serial
+# port and the ppsclock streams module in the ./kernel directory.
+# Only one of these three flags should be defined.
+#
+# There are three serial port system software interfaces, each of
+# which is peculiar to one or more Unix versions. Define
+# -DHAVE_SYSV_TTYS for basic System V compatibility; define -DSTREAM
+# for POSIX compatibility including System V Streams, and
+# HAVE_BSD_TTYS for 4.3bsd compatibility. Only one of these three
+# should be defined. If none are defined, HAVE_BSD_TTYS is assumed.
+# Ordinarily, the correct define is sniffed by the "make makeconfig"
+# script and automatically included.
+#
+# The flag KERNEL_PLL is a temporary hack to use when the phase-lock loop
+# is implmented in the kernel. Do not use unless you have modified
+# kernel routines (see doc/README.kern).
+#
+#DEFS_LOCAL= -DDEBUG -DPPSPPS -DKERNEL_PLL
+DEFS_LOCAL= -DDEBUG
+#DEFS_LOCAL= # for greenhorns
+#
+# Radio clock support definitions (these only make sense if -DREFCLOCK
+# used), which is normally the case. Note that a configuration can include
+# no clocks, more than one type of clock and even multiple clocks of the
+# same type.
+#
+# For most radio clocks operating with serial ports, accuracy can
+# be considerably improved through use of the tty_clk line
+# discipline or tty_clk_STREAMS streams module found in the
+# ./kernel directory. These gizmos capture a timestamp upon
+# occurrence of an intercept character and stuff it in the data
+# stream for the clock driver to munch. To select this mode,
+# postfix the driver name with the string CLK; that is, WWVB
+# becomes WWVBCLK. If more than one clock is in use, the CLK
+# postfix can be used with any or all of them.
+#
+# Alternatively, for the best accuracy, use the ppsclock streams
+# module in the ./ppsclock directory to steal the carrier-detect
+# transition and capture a precision timestamp. At present this
+# works only with SunOS 4.1.1 or later. To select this mode,
+# postfix the driver name with the string PPS; that is, AS2201
+# becomes AS2201PPS. If more than one clock is in use, the PPS
+# postfix should be used with only one of them. If any PPS
+# postfix is defined, the -DPPSPPS define should be used on the
+# DEFS above.
+#
+# Define -DLOCAL_CLOCK for a local pseudo-clock to masquerade as a
+# reference clock for those subnets without access to the real thing.
+# Works in all systems and requires no hardware support. This is defined
+# by default when using the "make makeconfig" script.
+#
+# Define -DPST for a PST/Traconex 1020 WWV/H receiver. The driver
+# supports both the CLK and PPS modes. It should work in all systems
+# with a serial port.
+#
+# Define -DWWVB for a Spectracom 8170 or Netclock/2 WWVB receiver. It
+# should work in all systems with a serial port. The driver supports
+# both the CLK and PPS modes if the requisite kernel support is installed.
+#
+# Define -DCHU for a special CHU receiver using an ordinary shortwave
+# radio. This requires the chu_clk line discipline or chu_clk_STREAMS
+# module in the ./kernel directory. At present, this driver works only
+# on SunOS4.1.x; operation in other systems has not been confirmed.
+# Construction details for a suitable modem can be found in the ./gadget
+# directory. The driver supports # neither the CLK nor PPS modes.
+#
+# Define -DPARSE for a DCF77/GPS(GENERIC) receiver. For best performance
+# this requires a special parsestreams STREAMS (SunOS 4.x) module in the
+# ./kernel directory. Define -DPARSEPPS for PPS support via the
+# DCF77/GPS (GENERIC) receiver; also, define -DPPS in the DEFS above.
+# Define PARSESTREAM for utilising the STREAMS module for improved
+# precision (currently only SunOS4.x)
+#
+# Define: -DCLOCK_MEINBERG for Meinberg clocks
+# -DCLOCK_SCHMID for Schmid receivers
+# -DCLOCK_DCF7000 for ELV DCF7000
+# -DCLOCK_RAWDCF for simple receivers (100/200ms pulses on Rx)
+#
+# Define -DMX4200PPS for a Magnavox 4200 GPS receiver. At present, this
+# driver works only on SunOS4.1.x with CPU serial ports only. The PPS
+# mode is required.
+#
+# Define -DAS2201 for an Austron 2200A or 2201A GPS receiver. It should
+# work in all systems with a serial port. The driver does not support the
+# CLK mode, but does support the PPS mode. If the radio is connected to
+# more than one machine, the PPS mode is required.
+#
+# Define -DGOES for a Kinemetrics/TrueTime 468-DC GOES receiver. This
+# driver is known to work with some other TrueTime products as well,
+# including the GPS-DC GPS receiver. It should work in all systems with
+# a serial port. The driver does not support the CLK mode, but does
+# support the PPS mode.
+#
+# Define -DOMEGA for a Kinemetrics/TrueTime OM-DC OMEGA receiver. It
+# should work in all systems with a serial port. The driver does not
+# support the CLK mode, but does support the PPS mode.
+#
+# Define -DTPRO for a KSI/Odetics TPRO-S IRIG-B timecode reader. This
+# requires the SunOS interface driver available from KSI. The driver
+# supports neither the CLK nor PPS modes.
+#
+# Define -DLEITCH for a Leitch CSD 5300 Master Clock System Driver for
+# the HP 5061B Cesium Clock. It should work in all systems with a serial
+# port. The driver does not support the CLK mode, but does support the
+# PPS mode.
+#
+# Define -DMSF for a EES M201 MSF receiver. It should work in all systems
+# with a serial port. The driver does not support the CLK mode, but does
+# support the # PPS mode.
+#
+# Define -DIRIG for a IRIG-B timecode timecode using the audio codec of
+# the Sun SPARCstations. This requires a modified BSD audio driver and
+# exclusive access to the audio port. A memo describing how it works and
+# how to install the driver is in the README.irig file in the ./doc
+# directory.
+#
+# Note: The following defines result in compilation of all the above radio
+# clocks. This works on a Sun 4.1.x system which has tty_clk, chu_clk and
+# ppsclock STREAMS modules installed. If the trailing CLK and PPS suffixes
+# are removed and the IRIG deleted, all of the rest compile under
+# Ultrix 4.2a/3. If the MX4200 is removed, all the rest compile on a DEC
+# OSF/1 Alpha.
+#
+#CLOCKDEFS= -DAS2201PPS -DCHU -DGOES -DIRIG -DMX4200PPS -DOMEGA -DPST -DPSTCLK -DTPRO -DWWVBCLK
+CLOCKDEFS= # for greenhorns
+#
+# Directory into which binaries should be installed
+#
+BINDIR= /usr/etc
diff --git a/usr.sbin/xntpd/conf/README b/usr.sbin/xntpd/conf/README
new file mode 100644
index 0000000..8d07591
--- /dev/null
+++ b/usr.sbin/xntpd/conf/README
@@ -0,0 +1,11 @@
+README file for directory ./conf of the NTP Version 3 distribution
+
+This directory contains example run-time configuration files for the
+NTP Version 3 daemon xntpd. These files illustrate some of the more
+obtuse configurations you may run into. They are not likely to do
+anything good if run on machines other than their native spot, so don't
+just blindly copy something and put it up. Additional information can
+be found in the ./doc directory of the base directory.
+
+See the Config.local.dist file in the base directory for an explanation
+of the defines used.
diff --git a/usr.sbin/xntpd/conf/baldwin.conf b/usr.sbin/xntpd/conf/baldwin.conf
new file mode 100644
index 0000000..baaac95
--- /dev/null
+++ b/usr.sbin/xntpd/conf/baldwin.conf
@@ -0,0 +1,40 @@
+#
+# NTP configuration file (ntp.conf)
+# baldwin.udel.edu (128.4.1.24)
+#
+# This illustrates the use of an external clock with the local clock
+# driver, as well as a multicast server. The prefer keyword on the
+# local clock driver declares an external clock and that the time of
+# this server should not be wiggled by an NTP peer, unless the
+# external clock comes unstuck. Note the use of the multicast group
+# ID assigned to NTP, 224.0.1.1, which identifies this as a multicast
+# server rather than a broadcast one. The other NTP peers are known
+# stratum-1 chimes intended as backup should the external clock croak.
+#
+peer 127.127.1.0 prefer # KSI/Odetics TPRO IRIG interface
+fudge 127.127.1.0 stratum 0 refid GPS
+broadcast 224.0.1.1 key 6 ttl 127
+peer 128.4.1.1 # rackety.udel.edu (Sun4c/40 IPC)
+peer 128.4.1.4 # barnstable.udel.edu (Sun4c/65 SS1+)
+peer 128.4.1.2 # mizbeaver.udel.edu (Bancomm bc700LAN)
+peer 128.4.1.20 # pogo.udel.edu (Sun4c/65 SS1+)
+
+#
+# Miscellaneous stuff
+#
+enable auth monitor # enable the good stuff
+driftfile /etc/ntp.drift # path for drift file
+statsdir /baldwin/ntpstats/ # directory for statistics files
+filegen peerstats file peerstats type day enable
+filegen loopstats file loopstats type day enable
+filegen clockstats file clockstats type day enable
+
+#
+# Authentication stuff
+#
+keys /usr/local/bin/ntp.keys # path for keys file
+trustedkey 3 4 5 6 14 15 # define trusted keys
+requestkey 15 # key (7) for accessing server variables
+controlkey 15 # key (6) for accessing server variables
+authdelay 0.000163 # authentication delay (SPARC4c/40 IPC MD5)
+
diff --git a/usr.sbin/xntpd/conf/dewey.conf b/usr.sbin/xntpd/conf/dewey.conf
new file mode 100644
index 0000000..2a7548c
--- /dev/null
+++ b/usr.sbin/xntpd/conf/dewey.conf
@@ -0,0 +1,46 @@
+#
+# NTP configuration file (ntp.conf)
+#
+# Generic configuration file for UDel NTP stratum-2 time servers. Don't
+# forget each server should have a /etc/ntp.drift and /etc/ntp.keys file.
+#
+# Stratum-1 peers. Each server should chime two different stratum-1
+# servers from the following list. Each stratum-1 server should be used
+# only once.
+#
+#peer 128.8.10.1 # umd1.umd.edu
+#peer 18.72.0.3 version 2 # bitsy.mit.edu
+peer 132.249.16.1 # fuzz.sdsc.edu
+peer 128.118.46.3 version 2 # otc1.psu.edu
+#peer 128.9.2.129 # wwvb.isi.edu
+#peer 130.43.2.2 version 2 # apple.com
+#peer 16.1.0.22 # clepsydra.dec.com
+#peer 130.105.1.156 version 2 # clock.osf.orga
+#peer 128.96.60.5 version 2 # pi.bellcore.com
+#peer 128.4.1.1 # rackety.udel.edu
+#peer 129.116.3.5 # shorty.chpc.utexas.edu
+#
+# Stratum-2 peers. Each server should chime all of the others in this
+# list except itself.
+#
+peer 128.175.1.1 # huey.udel.edu (VAX)
+#peer 128.175.1.2 # dewey.udel.edu (VAX)
+peer 128.175.1.3 # louie.udel.edu (SPARC)
+peer 128.175.2.15 # snow-white.ee.udel.edu (SPARC)
+peer 128.175.7.4 # sol.cis.udel.edu (SPARC)
+#
+# Miscellaneous stuff
+#
+enable auth monitor # enable the good stuff
+driftfile /etc/ntp.drift # path for drift file
+#
+# Authentication stuff. Note the different authentication delay on
+# VAX and SPARC.
+#
+authenticate yes # enable authentication
+keys /etc/ntp.keys # path for key file
+trustedkey 1 2 15 # define trusted keys
+requestkey 15 # key (7) for accessing server variables
+controlkey 15 # key (6) for accessing server variables
+authdelay 0.001501 # authentication delay (VAX)
+#authdelay 0.000073 # authentication delay (SPARC)
diff --git a/usr.sbin/xntpd/conf/grundoon.conf b/usr.sbin/xntpd/conf/grundoon.conf
new file mode 100644
index 0000000..16003d5
--- /dev/null
+++ b/usr.sbin/xntpd/conf/grundoon.conf
@@ -0,0 +1,157 @@
+#
+# NTP configuration file (ntp.conf)
+# grundoon.udel.edu (128.4.2.7)
+#
+# This machine can best be described as the kitchen sink. It has, in
+# addition to the baseboard tty ports ttya and ttyb, an 8-line
+# Serial/Parallel Interface (SPIF) with ports ttyz00 through ttyz07. The
+# configuration includes the following drivers, clock addresses and Unix
+# device names.
+#
+# Local Clock 127.127.1.0 /dev/audio
+# PST 1020 WWV/WWVH Receiver 127.127.3.1 /dev/pst1
+# Spectracom 8170 WWVB Receiver 127.127.4.1 /dev/wwvb1
+# IRIG Audio Decoder 127.127.6.0 /dev/audio
+# Scratchbuilt CHU Receiver 127.127.7.1 /dev/chu1
+# NIST ACTS modem 127.127.18.1 /dev/acts1
+# Heath GC-1000 WWV Receiver 127.127.19.1 /dev/pst1
+# PPS Clock 127.127.22.1 none
+#
+# This machine has the kernel modifications described in the README.kern
+# file, as well as the tty_clk, tty_chu and ppsclock streams modules.
+#
+# Spectracom 8170/Netclock-2 WWVB receiver. This receiver is equipped
+# with a 1-pps and IRIG outputs. The 1-pps signal is connected via the
+# ppsclock streams module and the carrier detect line of the CHU
+# receiver below (ttyb). The IRIG signal is connected via an attenuator
+# to the audio port (/dev/audio). The propagation delay computed from
+# geographical coordinates is 8.8 ms, while the receiver delay
+# calibrated at the factory is 17.3 ms, for a total delay of 26.1 ms.
+# This is confirmed within 0.1 ms at the 1-pps signal output using a
+# portable cesium clock. We add a fudge time1 of 3.5 ms so the driver
+# time agrees with the 1-pps signal to within 1 ms. The fudge flag4 is
+# set to cause the receiver to dump the quality table once each day to
+# the clockstats file.
+
+#
+#server 127.127.4.1 # /dev/wwvb1 -> /dev/ttyz03
+#fudge 127.127.4.1 time1 0.0035 flag4 1
+#
+# IRIG Audio Decoder. The IRGI signal of the Spectracom WWVB receiver is
+# connected to the audio codec via a resistor attenuator. We add a fudge
+# time1 of 3.5 ms so the driver agrees with the calibrated 1-pps signal
+# to within 0.1 ms. We also specify a reference ID of WWVB to indicate
+# the signal origin. Note the prefer keyword in the server line, which
+# favors this driver over all others that survive the clock selection
+# algorithm. See README.refclock for further insight on this feature.
+#
+server 127.127.6.0 prefer # /dev/audio
+fudge 127.127.6.0 time1 0.0005 refid WWVB
+
+#
+# PST/Traconex 1020 WWV/WWVH Receier. The internal DIPswitches are set
+# as near as possible to the delays to WWV (8.8 ms) and WWVH (28.1 ms),
+# as computed from geographical coordinates. We add a fudge time1 of 5.9
+# ms so the driver time agrees with the 1-pps signal to within 1 ms for
+# WWV. We also set the stratum to 1, so this receiver will not normally
+# be selected, unless the primary WWVB receiver comes unstuck.
+#
+server 127.127.3.1 # /dev/pst1 -> ttyz05
+fudge 127.127.3.1 time1 0.0059 stratum 1
+
+#
+# Scratchbuilt CHU Receiver. The audio signal from a computer controlled
+# CHU receiver is connected to a gadget box, which contains a 103A modem
+# chip and level converter operating at 300 bps. The propagation delay
+# computed from geographical coordinates is 3.0 ms, which is the value
+# of the fudge time1 parameter. We add a fudge time2 of 9.9 ms so that
+# the driver time agrees with the 1-pps signal to within a few ms,
+# ordinarily the best possible with this receiver. The fudge flag3 is
+# set because the 1-pps signal happens to be connected vit the carrier
+# detect line on this port (ttyb). We also set the stratum to 1, so this
+# receiver will not normally be selected, unless the primary WWVB
+# receiver comes unstuck.
+#
+server 127.127.7.1 # /dev/chu1 -> /dev/ttyb
+fudge 127.127.7.1 time1 0.0030 time2 0.0099 flag3 1 stratum 1
+
+#
+# NIST Automated Computer Time Service. This driver calls a special
+# telephone number in Boulder, CO, to fetch the time directly from the
+# NIST cesium farm. The details of the complicated calling program are
+# in the README.refclock file. The Practical Peripherals 9600SA modem
+# does not work correctly with the ACTS echo-delay scheme for
+# automatically calculating the propagation delay, so the fudge flag2 is
+# set to disable the feature. Instead, we add a fudge time1 of 65.0 ms
+# so that the driver time agrees with th e1-pps signal to within 1 ms.
+# The phone command specifies three alternate telephone numbers,
+# including AT modem command prefix, which will be tried one after the
+# other at each measurement attempt. In this case, a cron job is used to
+# set fudge flag1, causing a measurement attempt, every six hours.
+#
+server 127.127.18.1 # /dev/acts1 -> /dev/ttyz00
+fudge 127.127.18.1 time1 0.0650 flag2 1
+phone atdt13034944774 atdt13034944785 atdt13034944774
+
+#
+# Heath GC-1000 Most Accurate Clock. This is a WWV receiver with a
+# claimed accuracy better than 100 ms under "hi spec" conditions, but
+# such conditions are not frequent. The propagation delay DIPswitchs are
+# set to 9 ms, as close as possible to the 8.8 ms computed from
+# geographical coordinates. We add a fudge time2 of 40.0 ms so that the
+# driver time agrees with the 1-pps signal to within 50 ms, ordinarily
+# the best possible with this receiver. We also set the stratum to 1, so
+# this receiver will not normally be selected, unless the primary WWVB
+# receiver comes unstuck.
+#
+server 127.127.19.1 # /dev/heath1 -> ttyz07
+fudge 127.127.19.1 time1 0.040 stratum 1
+
+#
+# Undisciplined Local Clock. This is a fake driver intended for backup
+# and when no outside source of synchronized time is available. The
+# default stratum is usually 3, but in this case we elect to use stratum
+# 0. Since the server line does not have the prefer keyword, this driver
+# is never used for synchronization, unless no other other
+# synchronization source is available. In case the local host is
+# controlled by some external source, such as an external oscillator or
+# another protocol, the prefer keyword would cause the local host to
+# disregard all other synchronization sources, unless the kernel
+# modifications are in use and declare an unsynchronized condition.
+#
+server 127.127.1.0 # local clock
+fudge 127.127.1.0 stratum 0
+
+#
+# PPS Clock. This driver is used to capture a 1-pps signal when the PPS
+# kernel modifications are not in use. It can be configured for the
+# tty_clk or ppsclock streams module or no module at all, assuming the
+# RS232 connector is properly wired. Normally, the 1-pps signal is
+# generated by a radio clock, in this cast the Spectracom clock
+# 127.127.4.1 also configured for this host. When used this way, the
+# associated radio clock normally has the prefer keyword in the serve
+# command line. The PPS driver then will be selected only if the prefer
+# peer is operating within nominal error bounds. See the README.refclock
+# file for further details.
+#
+#server 127.127.22.1 # pps clock
+
+#
+# Miscellaneous stuff. We enable authentication in order to prevent
+#
+enable auth monitor # enable the good stuff
+driftfile /etc/ntp.drift # path for drift file
+statsdir /grundoon/ntpstats/ # directory for statistics files
+filegen peerstats file peerstats type day enable
+filegen loopstats file loopstats type day enable
+filegen clockstats file clockstats type day enable
+
+#
+# Authentication stuff
+#
+keys /usr/local/ntp.keys # path for keys file
+trustedkey 1 2 3 4 5 6 14 15 # define trusted keys
+requestkey 15 # key (7) for accessing server variables
+controlkey 15 # key (6) for accessing server variables
+authdelay 0.000163 # authentication delay (SPARC4c/40 IPC MD5)
+
diff --git a/usr.sbin/xntpd/conf/maccarony.conf b/usr.sbin/xntpd/conf/maccarony.conf
new file mode 100644
index 0000000..6bd25c7
--- /dev/null
+++ b/usr.sbin/xntpd/conf/maccarony.conf
@@ -0,0 +1,33 @@
+#
+# NTP configuration file (ntp.conf)
+#
+# This illustrates a multicast client. All that is really needed
+# here is the multicastclient command and the authentication stuff.
+# If the monitoring option and filgen statistics were not needed,
+# this could be done without a configuration file by including the
+# following snip in the rc.local startup file or equivalent:
+#
+#if [ -f /usr/local/bin/xntpd ]; then
+# /usr/local/bin/xntpd -m -a -k/usr/local/bin/ntp.keys -t3
+#fi
+#
+multicastclient # listen on default 224.0.1.1
+#
+# Miscellaneous stuff
+#
+enable auth monitor # enable the good stuff
+driftfile /etc/ntp.drift # path for drift file
+statsdir /malarky/ntpstats/ # directory for statistics files
+filegen peerstats file peerstats type day enable
+filegen loopstats file loopstats type day enable
+filegen clockstats file clockstats type day enable
+
+#
+# Authentication stuff
+#
+keys /usr/local/bin/ntp.keys # path for key file
+trustedkey 3 4 5 6 14 15 # define trusted keys
+requestkey 15 # key (7) for accessing server variables
+controlkey 15 # key (6) for accessing server variables
+authdelay 0.000094 # authentication delay (Sun4c/50 IPX MD5)
+
diff --git a/usr.sbin/xntpd/conf/malarky.conf b/usr.sbin/xntpd/conf/malarky.conf
new file mode 100644
index 0000000..9b1d7b2
--- /dev/null
+++ b/usr.sbin/xntpd/conf/malarky.conf
@@ -0,0 +1,27 @@
+#
+# NTP configuration file (ntp.conf)
+#
+# This is for a broadcast/multicast client. Except for the statistics
+# stuff, this can be done with only a commmand line of the form
+#
+# /usr/local/bin/xntpd -a -k /usr/local/bin/ntp.keys -m -t 3
+#
+multicastclient # listen on default 224.0.1.1
+#
+# Miscellaneous stuff
+#
+enable auth monitor # enable the good stuff
+driftfile /etc/ntp.drift # path for drift file
+statsdir /malarky/ntpstats/ # directory for statistics files
+filegen peerstats file peerstats type day enable
+filegen loopstats file loopstats type day enable
+filegen clockstats file clockstats type day enable
+
+#
+# Authentication stuff
+#
+keys /usr/local/bin/ntp.keys # path for key file
+trustedkey 3 4 5 6 14 # define trusted keys
+requestkey 14 # key (7) for accessing server variables
+controlkey 14 # key (6) for accessing server variables
+authdelay 0.000094 # authentication delay (Sun4c/50 IPX MD5)
diff --git a/usr.sbin/xntpd/conf/ntp.conf.dcf77 b/usr.sbin/xntpd/conf/ntp.conf.dcf77
new file mode 100644
index 0000000..678d719
--- /dev/null
+++ b/usr.sbin/xntpd/conf/ntp.conf.dcf77
@@ -0,0 +1,19 @@
+#
+# XNTP configuration file (/etc/ntp.conf)
+#
+
+#
+# Server is a Boeder DCF77 receiver
+#
+# Use:
+# 127.127.8.40 for /dev/refclock-0 (/dev/ttyd0)
+# 127.127.8.41 for /dev/refclock-1 (/dev/ttyd1)
+# 127.127.8.42 for /dev/refclock-2 (/dev/ttyd2)
+# 127.127.8.43 for /dev/refclock-3 (/dev/ttyd3)
+#
+server 127.127.8.40
+
+#
+# drift file
+#
+driftfile /etc/ntp.drift
diff --git a/usr.sbin/xntpd/conf/ntp.conf.gw b/usr.sbin/xntpd/conf/ntp.conf.gw
new file mode 100644
index 0000000..bd56878
--- /dev/null
+++ b/usr.sbin/xntpd/conf/ntp.conf.gw
@@ -0,0 +1,34 @@
+#
+# peers for gw.ccie.utoronto.ca (128.100.63.2, 128.100.49.104, 128.100.224.224)
+#
+peer 128.4.0.1 key 1 # dcn1.udel.edu
+peer 128.8.10.1 key 2 # umd1.umd.edu
+peer 128.116.64.3 key 3 # ncarfuzz.ucar.edu
+peer 128.9.2.129 key 4 # wwvb.isi.edu
+#peer 128.4.0.6 key 1 # dcn6.udel.edu
+#
+# Don't configure associations with the other secondaries. This is
+# the only one in a machine room and will hold itself pretty stable
+# when all else fails
+#
+monitor yes # keep track of traffic
+
+#
+# drift file
+#
+driftfile /etc/ntp.drift
+
+#
+# authentication stuff. We're running authenticated, tell it
+# where the keys are and which to trust.
+#
+authenticate yes
+authdelay 0.000323 # seconds, about right for an RT model 125
+trustedkey 1 2 3 4 21 22 23 24
+keys /etc/ntp.keys
+
+#
+# allow run time reconfiguration using key 65535
+#
+requestkey 65535
+controlkey 65535
diff --git a/usr.sbin/xntpd/conf/ntp.conf.ipl b/usr.sbin/xntpd/conf/ntp.conf.ipl
new file mode 100644
index 0000000..1fd5b7d
--- /dev/null
+++ b/usr.sbin/xntpd/conf/ntp.conf.ipl
@@ -0,0 +1,32 @@
+#
+# peers for ipl.utcs.utoronto.ca (128.100.102.7)
+#
+peer 128.4.0.5 key 1 # dcn5.udel.edu
+peer 128.8.10.1 key 2 # umd1.umd.edu
+peer 192.12.207.1 key 3 # fuzz.sdsc.edu
+peer 128.9.2.129 key 4 # wwvb.isi.edu
+peer 128.100.63.2 key 21 # gw.ccie
+peer 128.100.49.105 key 22 # suzuki.ccie
+peer 128.100.102.4 key 23 # shiningtree.utcs
+#
+monitor yes # keep track of traffic
+
+#
+# drift file
+#
+driftfile /etc/ntp.drift
+
+#
+# authentication stuff. We're running authenticated, tell it
+# where the keys are and which to trust.
+#
+authenticate yes
+authdelay 0.000323 # seconds, about right for an RT model 125
+trustedkey 1 2 3 4 21 22 23
+keys /etc/ntp.keys
+
+#
+# allow run time reconfiguration using key 65535
+#
+requestkey 65535
+controlkey 65535
diff --git a/usr.sbin/xntpd/conf/ntp.conf.nsf b/usr.sbin/xntpd/conf/ntp.conf.nsf
new file mode 100644
index 0000000..298bb7a
--- /dev/null
+++ b/usr.sbin/xntpd/conf/ntp.conf.nsf
@@ -0,0 +1,156 @@
+#
+# Maybe an alternate xntpd configuration for NSS#17
+#
+
+#
+# precision is supported, but you don't really need it. The code
+# will determine a precision from the kernel's value of _hz which
+# is fine. Note you shouldn't claim too good a precision on a
+# Unix machine even if the clock carries a lot of bits, since
+# precision also depends on things like I/O delays and scheduling
+# latencies, which Unix machines control poorly. If you claim better
+# than -6 or -7 it will make the anti-hop aperture tighter than is
+# reasonable for a Unix machine.
+#
+#precision -7
+
+#
+# peers are ncarfuzz.ucar.edu umd1.umd.edu dcn5.udel.edu fuzz.sdsc.edu
+# syntax is peer addr [ key 1-15 ] [ version 1_or_2 ]
+#
+
+peer 128.116.64.3 # ncarfuzz.ucar.edu
+peer 128.8.10.1 # umd1.umd.edu
+peer 128.4.0.5 # dcn5.udel.edu
+peer 192.12.207.1 # fuzz.sdsc.edu
+
+#
+# Drift file. Put this in a directory which the daemon can write to.
+# No symbolic links allowed, either, since the daemon updates the file
+# by creating a temporary in the same directory and then rename()'ing
+# it to the file.
+#
+# This is a nice feature. Once you've got the drift computed it hardly
+# ever takes more than an hour or so to resync after a restart.
+#
+driftfile /etc/ntp.drift
+
+#
+# The server statement causes polling to be done in client mode rather
+# than symmetric active. It is an alternative to the peer command
+# above. Which you use depends on what you want to achieve. Usually
+# it doesn't matter. Syntax is:
+#
+#server 128.100.49.1 key 4 version 1
+
+#
+# The broadcast statement tells it to start broadcasting time out one
+# of its interfaces. Syntax is
+#
+#broadcast 128.100.49.255 # [ key n ] [ version n ]
+
+#
+# broadcastclient tells the daemon whether it should attempt to sync
+# to broadcasts or not. Defaults to `no'.
+#
+#broadcastclient yes # or no
+
+#
+# broadcastdelay configures in a default round-trip delay to use for
+# broadcast time. It may poll to improve this estimate.
+#
+#broadcastdelay 0.0095 # in seconds
+
+#
+# authenticate configures us into strict authentication mode (or not).
+#
+#authenticate yes # or no. Default is no
+
+#
+# authdelay is the time it takes to do an NTP encryption on this host.
+# The current routine is pretty fast.
+#
+#authdelay 0.000340 # in seconds
+
+#
+# trustedkey are used when authenticate is on. We only trust (and sync to)
+# peers who know these keys.
+#
+#trustedkey 1 3 4 8
+
+#
+# monitor turns on the monitoring facility. See xntpdc's monlist command.
+# This shows a lot of neat stuff, but I'm not fussy about the implementation.
+# Uses up to 20Kb of memory at run time. You could try this.
+#
+#monitor yes # or no. Default is no
+
+#
+# keys points at the file which holds the authentication keys.
+#
+#keys /etc/ntp.keys
+
+#
+# requestkey indicates which key is to be used for validating
+# runtime reconfiguration requests. If this isn't defined, or the
+# key isn't in the keys file, you can't do runtime reconfiguration.
+# controlkey indicates which key is to be used for validating
+# mode 6 write variables commands. If this isn't defined you can't
+# do it. The only thing the latter is used for is to set leap second
+# warnings on machines with radio clocks.
+#
+#requestkey 65535
+#controlkey 65534
+
+#
+# restrict places restrictions on the punters. This is implemented as
+# a sorted address-and-mask list, with each entry including a set of
+# flags which define what a host matching the entry *can't* do (the sort
+# also saves CPU time searching the table since it needn't be searched
+# to the end). The last match in the table defines what the host does.
+# The default entry, which everyone matches, is first, most specific
+# matches are later in the table. The flags are:
+#
+# ignore - ignore all traffic from host
+# noserve - don't give host any time (but let him make queries?)
+# notrust - give host time, let him make queries, but don't sync to him
+# noquery - host can have time, but not make queries
+# nomodify - allow the host to make queries except those which are
+# actually run-time configuration commands.
+# notrap - don't allow matching hosts to set traps. If noquery is
+# set this isn't needed
+# lowpriotrap - if this guy sets a trap make it easy to delete
+# ntpport - a different kind of flag. Makes matches for this entry
+# possible only if the source port is 123.
+#
+# To understand this better, take a look at xntpdc's reslist command when the
+# server is running. This usually prints in the sorted order.
+#
+# This should match the NSS 17 stuff. Default mask is all ones.
+
+restrict default ignore # ignore almost everyone
+
+#
+# These guys can be served time and make non-modifying queries
+#
+restrict 129.140.0.0 mask 255.255.0.0 notrust nomodify
+restrict 35.1.1.42 notrust nomodify
+
+#
+# Rest of 35.1.1 gets to look but not touch
+#
+restrict 35.1.1.0 mask 255.255.255.0 noserve nomodify
+
+#
+# modifications can be made from local NSS only
+#
+restrict 129.140.17.0 mask 255.255.255.0 notrust
+restrict 127.0.0.1 notrust
+
+#
+# take time from the following peers, but don't let them peek or modify
+#
+restrict 128.116.64.3 noquery
+restrict 128.8.10.1 noquery
+restrict 128.4.0.5 noquery
+restrict 192.12.207.1 noquery
diff --git a/usr.sbin/xntpd/conf/ntp.conf.shiningtree b/usr.sbin/xntpd/conf/ntp.conf.shiningtree
new file mode 100644
index 0000000..1576ebb
--- /dev/null
+++ b/usr.sbin/xntpd/conf/ntp.conf.shiningtree
@@ -0,0 +1,32 @@
+#
+# peers for shiningtree.utcs.utoronto.ca (128.100.102.4)
+#
+peer 128.4.0.1 key 1 # dcn1.udel.edu
+peer 130.126.174.40 key 2 # truechimer.cso.uiuc.edu
+peer 192.12.207.1 key 3 # fuzz.sdsc.edu
+peer 128.116.64.3 key 4 # ncarfuzz.ucar.edu
+peer 128.100.63.2 key 21 # gw.ccie
+peer 128.100.49.105 key 22 # suzuki.ccie
+peer 128.100.102.7 key 23 # ipl.utcs
+#
+monitor yes # keep track of traffic
+
+#
+# drift file
+#
+driftfile /etc/ntp.drift
+
+#
+# authentication stuff. We're running authenticated, tell it
+# where the keys are and which to trust.
+#
+authenticate yes
+authdelay 0.000323 # seconds, about right for an RT model 125
+trustedkey 1 2 3 4 21 22 23
+keys /etc/ntp.keys
+
+#
+# allow run time reconfiguration using key 65535
+#
+requestkey 65535
+controlkey 65535
diff --git a/usr.sbin/xntpd/conf/ntp.conf.suzuki b/usr.sbin/xntpd/conf/ntp.conf.suzuki
new file mode 100644
index 0000000..ee32e7a
--- /dev/null
+++ b/usr.sbin/xntpd/conf/ntp.conf.suzuki
@@ -0,0 +1,43 @@
+#
+# peers for suzuki.ccie.utoronto.ca (128.100.49.105, 128.100.224.225)
+#
+
+#
+# the reference clock, /dev/chu1
+#
+server 127.127.7.1 key 4
+# Propagation delay 2.5 ms, sloppy clock flag on
+fudge 127.127.7.1 time1 0.0025 flag1 1
+
+peer 128.4.0.5 key 1 # dcn5.udel.edu
+peer 128.8.10.1 key 2 # umd1.umd.edu
+peer 128.116.64.34 key 3 # ncarfuzz.ucar.edu
+peer 130.126.174.40 key 4 # truechimer.cso.uiuc.edu
+peer 128.100.49.104 key 24 # gw.ccie
+peer 128.100.102.4 key 22 # shiningtree.utcs
+peer 128.100.102.7 key 22 # ipl.utcs
+
+peer 128.4.0.6 key 1 # dcn6.udel.edu
+
+#
+monitor yes # keep track of traffic
+
+#
+# drift file
+#
+driftfile /etc/ntp.drift
+
+#
+# authentication stuff. We're running authenticated, tell it
+# where the keys are and which to trust.
+#
+authenticate yes
+authdelay 0.000323 # seconds, about right for an RT model 125
+trustedkey 1 2 3 4 21 22 23 24
+keys /etc/ntp.keys
+
+#
+# allow run time reconfiguration using key 65535
+#
+requestkey 65535
+controlkey 65535
diff --git a/usr.sbin/xntpd/conf/pogo.conf b/usr.sbin/xntpd/conf/pogo.conf
new file mode 100644
index 0000000..e557e44
--- /dev/null
+++ b/usr.sbin/xntpd/conf/pogo.conf
@@ -0,0 +1,34 @@
+#
+# NTP configuration file (ntp.conf)
+# pogo.udel.edu (128.4.1.20)
+#
+server 127.127.10.1 prefer # austron 2201A gps receiver
+peer 128.4.1.1 # rackety.udel.edu (Sun4c/40 IPC)
+peer 128.4.1.2 # mizbeaver.udel.edu (Bancomm bc700LAN)
+peer 128.4.1.4 # barnstable.udel.edu (Sun4c/65 SS1+)
+peer 128.4.1.5 maxpoll 8 # churchy.udel.edu (cisco IGS router)
+peer 132.163.135.130 maxpoll 8 # time_A.timefreq.bldrdoc.gov (Cesium)
+peer 131.188.1.40 maxpoll 8 # ntps1-0.uni-erlangen.de (DCF77)
+peer 129.132.2.21 maxpoll 8 # swisstime.ethz.ch (DCF77)
+peer 130.155.98.13 maxpoll 8 # terss.ml.csiro.au (Cesium)
+peer 192.36.143.150 maxpoll 8 # Time1.Stupi.SE (Cesium)
+
+#
+# Miscellaneous stuff
+#
+enable auth monitor # enable the good stuff
+precision -18 # clock reading precision (usec)
+driftfile /etc/ntp.drift # path for drift file
+statsdir /pogo/ntpstats/ # directory for statistics files
+filegen peerstats file peerstats type day enable
+filegen loopstats file loopstats type day enable
+filegen clockstats file clockstats type day enable
+
+#
+# Authentication stuff
+#
+keys /usr/local/bin/ntp.keys # path for keys file
+trustedkey 3 4 5 6 14 # define trusted keys
+requestkey 15 # key (7) for accessing server variables
+controlkey 15 # key (6) for accessing server variables
+authdelay 0.000159 # authentication delay (SPARC4c/65 SS1+ MD5)
diff --git a/usr.sbin/xntpd/conf/rackety.conf b/usr.sbin/xntpd/conf/rackety.conf
new file mode 100644
index 0000000..48389dc
--- /dev/null
+++ b/usr.sbin/xntpd/conf/rackety.conf
@@ -0,0 +1,69 @@
+#
+# NTP configuration file (ntp.conf)
+# rackety (128.4.1.1)
+#
+server 127.127.10.1 prefer # austron 2201A gps receiver
+fudge 127.127.10.1 flag4 1 # enable statistics
+server 127.127.4.1 # spectracom 8170/netclock-2 wwvb receiver
+# propagation delay: wwvb 0.0088, receiver delay 0.0173, os delay .0035
+fudge 127.127.4.1 time1 0.0035 stratum 1 flag4 1
+
+#
+# ee vaxen
+#
+peer 128.175.1.1 # huey.udel.edu
+peer 128.175.1.2 # louie.udel.edu
+peer 128.175.1.3 # dewey.udel.edu
+
+#
+# munchkins (stratum-1 only)
+#
+broadcast 224.0.1.1 key 5 ttl 127 # multicast
+broadcast 128.4.1.0 key 3 # local broadcast
+peer 128.4.1.2 # mizbeaver.udel.edu
+peer 128.4.1.4 # barnstable.udel.edu
+peer 128.4.1.20 # pogo.udel.edu
+
+#
+# dartnet
+#
+peer 140.173.112.2 # ames.dart.net
+peer 140.173.128.1 # la.dart.net
+peer 140.173.64.1 # dc.dart.net
+peer 140.173.144.2 # parc.dart.net
+peer 140.173.80.1 # sri.dart.net
+peer 140.173.96.1 # lbl.dart.net
+peer 140.173.128.2 # isi.dart.net
+peer 140.173.16.1 # udel.dart.net
+peer 140.173.32.1 # bbn.dart.net
+peer 140.173.48.2 # mit.dart.net
+
+#
+# nsfnet t3 backbone
+#
+server 140.222.134.1 version 2 # enss134 (cambridge - mit)
+server 140.222.135.1 version 2 # enss135 (san diego - sdsc)
+peer 140.222.136.1 version 2 # enss136 (college park - sura)
+server 140.222.141.1 version 2 # enss141 (boulder - ncar)
+server 140.222.144.1 version 2 # enss144 (sunnyvale - nasa ames)
+
+#
+# Miscellaneous stuff
+#
+enable auth monitor # enable the good stuff
+precision -18 # clock reading precision (usec)
+driftfile /etc/ntp.drift # path for drift file
+statsdir /rackety/ntpstats/ # directory for statistics files
+filegen peerstats file peerstats type day enable
+filegen loopstats file loopstats type day enable
+filegen clockstats file clockstats type day enable
+
+#
+# Authentication stuff
+#
+keys /usr/local/bin/ntp.keys # path for keys file
+trustedkey 3 4 5 6 14 # define trusted keys
+requestkey 14 # key (7) for accessing server variables
+controlkey 14 # key (6) for accessing server variables
+authdelay 0.000163 # authentication delay (SPARC4c/40 IPC MD5)
+
diff --git a/usr.sbin/xntpd/conf/snow-white.conf b/usr.sbin/xntpd/conf/snow-white.conf
new file mode 100644
index 0000000..a86cb4b
--- /dev/null
+++ b/usr.sbin/xntpd/conf/snow-white.conf
@@ -0,0 +1,33 @@
+#
+# NTP configuration file (ntp.conf)
+# snow-white.udel.edu (128.175.2.15)
+#
+# Stratum-2 peers
+#
+peer 128.175.1.1 # huey.udel.edu
+peer 128.175.1.2 # dewey.udel.edu
+#peer 128.175.1.3 # louie.udel.edu
+peer 128.175.2.33 # louie.udel.edu
+#peer 128.175.7.39 # louie.udel.edu
+#
+# Stratum-3 peers
+#
+peer 128.175.7.4 # sol.cis.udel.edu
+peer 128.175.7.18 # ra.cis.udel.edu
+#peer 128.175.2.15 # snow-white.ee.udel.edu
+peer 128.175.2.21 # opus.ee.udel.edu
+#
+# Miscellaneous stuff
+#
+monitor yes # enable monitoring
+precision -18 # clock reading precision (1 usec)
+driftfile /etc/ntp.drift # path for drift file
+#
+# Authentication stuff
+#
+authenticate yes # enable authentication
+keys /etc/ntp.keys # path for key file
+trustedkey 1 2 15 # define trusted keys
+requestkey 15 # key (7) for accessing server variables
+controlkey 15 # key (6) for accessing server variables
+authdelay 0.000077 # authentication delay (SPARC IPC)
diff --git a/usr.sbin/xntpd/doc/README.irig b/usr.sbin/xntpd/doc/README.irig
new file mode 100644
index 0000000..f293f4c
--- /dev/null
+++ b/usr.sbin/xntpd/doc/README.irig
@@ -0,0 +1,306 @@
+ Audio IRIG Receiver for Precision Timekeeping
+
+ Revised 20 September 1993
+
+Note: This information file is included in both the BSD audio driver
+distribution (bsd_audio.tar.Z) and NTP Version 3 distribution
+(xntp3.tar.Z) as the file README.irig. Both distributions can be
+obtained via anonymous ftp from louie.udel.edu in the directory pub/ntp.
+
+1. Introduction
+
+This software distribution includes modifications to the BSD audio
+driver for the Sun SPARCstation written by Van Jacobson and
+collaborators at Lawrence Berkeley National Laboratory. The
+modifications provide for the connection of a standard Inter-Range
+Instrumentation Group (IRIG) timecode signal generator and the decoding
+of the signal to produce data sufficient to synchronize a host clock to
+the IRIG signal. There are several timing receivers now on the market
+that can produce IRIG signals, including those made by Austron,
+TrueTime, Odetics and Spectracom, among others. These data can be used
+to precisely synchronize the host computer clock to within a few
+microseconds without requiring level converters or pulse generators
+necessary with the one-pulse-per-second signals also produced by these
+receivers. The current implementation of the Network Time Protocol
+Version 3 supports the modified BSD driver when installed in the SunOS
+4.1.x kernel.
+
+The specific IRIG signal format supported by the driver is designated
+IRIG-B. It consists of an amplitude-modulated 1000-Hz sinewave, where
+each symbol is encoded as ten full carrier cycles, or 10 ms in duration.
+The symbols are distinguished using a pulse-width code, where 2 ms
+corresponds to logic zero, 5 ms to logic one and 8 ms to a position
+identifier used for symbol synchronization. The complete IRIG-B message
+consists of a frame of ten fields, each field consisting of a nine
+information symbols followed by a position identifier for a total frame
+duration of one second. The first symbol in the frame is also a position
+identifier to facilitate frame synchronization.
+
+The IRIG-B signal encodes the day of year and time of day in binary-
+coded decimal (BCD) format, together with a set of control functions,
+which are not used by the driver, but included in the raw binary
+timecode. Either the BCD timecode or the combined raw timecode and BCD
+timecode can be returned in response to a read() system call. The BCD
+timecode is in handy ASCII format: "ddd hh:mm:ss*" for convenience in
+client programs. In this format the "*" status character is " " when the
+driver is operating normally and "?" when errors may be present (see
+below). In order to reduce residual errors to the greatest extent
+possible, the driver computes a timestamp based on the value of the
+kernel clock at the on-time epoch of the IRIG-B signal. In addition, the
+driver automatically adjusts for slowly varying amplitude levels of the
+IRIG-B signal and suppresses noise transients.
+
+In operation the IRIG driver interprets the IRIG-B signal in real time,
+synchronizes to the signal, demodulates the data bits and prepares the
+data to be read later. At the on-time epoch a timestamp is captured from
+the kernel clock and adjusted for the phase of the IRIG carrier signal
+relative to the 8-kHz codec sample clock. When a client program issues a
+read() request, the most recent timecode data, including a status byte
+and the corrected timestamp, are stored in a structure and returned to
+the caller. Depending on the frequency with which the driver is called,
+this may result in old data or duplicate data or even invalid data,
+should the driver be called before it has computed its first timestamp.
+
+In practice, the resulting ambiguity causes few problems. The caller
+converts the ASCII timecode returned by a read() system call to Unix
+timeval format and subtracts it from the kernel timestamp provided by
+the driver. The result is an adjustment that can be subtracted from the
+kernel time, as returned in a gettimeofday() call, for example, to
+correct for the deviation between IRIG time and kernel time. The result
+can always be relied on to within plus/minus 128 microseconds, the audio
+codec sampling interval, and ordinarily to within a few microseconds, as
+determined by the interpolation algorithm.
+
+2. Programming Interface
+
+The IRIG driver modifications are integrated in the BSD audio driver
+bsd_audio.c without affecting its usual functions in transmitting and
+receiving ordinary speech, except when enabled by specific ioctl()
+system calls. However, the driver cannot be used for both speech and
+IRIG signals at the same time. Once activated by a designated ioctl()
+call, the driver remains active until it is explicitly deactivated by
+another ioctl() call. This allows applications to configure the audio
+device and pass the pre-configured driver to other applications. Since
+the driver is currently only a receiver, it does not affect the
+operation of the BSD audio output driver.
+
+Data are read using the standard read() system call. Since the output
+formats have constant lengths, the application receives the data into a
+fixed-length buffer or structure. The read() call never blocks; it
+simply returns the most recent IRIG data received during the last
+second. It may happen that, due to unavoidable race conditions in the
+kernel, data for other than the most recent second are returned. The
+driver's internal data structure is updated as an atomic unit; thus, the
+entire structure is valid, even if it contains old data. This should
+cause no problems, since in the intended application the driver is
+called at regular intervals by a time-synchronization daemon such as
+NTP. The daemon can determine the validity of the time indication by
+checking the timecode or status byte returned with the data.
+
+The header file bsd_audioirig.h defines the irig_time structure and
+ioctl() codes used by the driver. Following are those codes specific to
+the IRIG function of the driver. Unless indicated otherwise, the (third)
+argument of the ioctl() system call points to an integer or string.
+
+AUDIO_IRIG_OPEN
+
+ This command activates the IRIG receiver. The audio driver must be
+ opened with this command before other commands can be issued. The
+ argument is ignored. When the IRIG receiver is initialized, all
+ internal data are purged and any buffered data are lost.
+
+AUDIO_IRIG_CLOSE
+
+ This command deactivates the IRIG receiver. The argument is
+ ignored. The buffers are purged and any buffered time data are
+ lost. The original BSD audio driver functions are enabled and it
+ resumes operating normally.
+
+AUDIO_IRIG_SETFORMAT
+
+ The argument is a pointer to an integer designating the output
+ format for the IRIG data. There are currently two formats defined,
+ 0 (default) and 1. If an invalid format is selected, the default
+ format is used.
+
+The data returned by a read() system call in format 0 is a character
+string in the format "ddd hh:mm:ss*\n", which consists of 13 ASCII
+characters followed by a newline terminator for a total of 14
+characters. The "*" status character is an ASCII space " " if the status
+byte determined by the driver is zero and "?" if not. This format is
+intended to be used with simple user programs that care only about the
+time to the nearest second.
+The data returned by a read() system call in format 1 is a structure
+defined in the bsd_audioirig.h header file:
+
+ struct irig_time {
+ struct timeval stamp; /* timestamp */
+ u_char bits[13]; /* 100 irig data bits */
+ u_char status; /* status byte */
+ char time[14]; /* time string */
+ };
+
+The irig-time.stamp is a pair of 32-bit longwords in Unix timeval
+format, as defined in the sys/time.h header file. The first word is the
+number of seconds since 1 January 1970, while the second is the number
+of microseconds in the current second. The timestamp is captured at the
+most recent on-time instant of the IRIG timecode and applies to all
+other values returned in the irig_time structure.
+
+The irig_time.bits[13] is a vector of 13 bytes to hold the 100-bit,
+zero-padded raw binary timecode, packed 8 symbols per byte. The symbol
+encoding maps IRIG one to 1 and both IRIG zero and IRIG position
+identifier to 0. The order of encoding is illustrated by the following
+diagram (the padding bits are represented by xxxx, which are set to
+zero):
+
+IRIG symbol number 00000000001111111111 . . . 8888889999999999xxxx
+ 01234567890123456789 . . . 4567890123456789xxxx
+ -----------------------------------------------
+bits byte number <--00--><--01--><---- ----><--11--><--12-->
+bits bit in byte 01234567012345670123 . . . 45670123456701234567
+
+The irig_time.status is a single byte with bits defined in the
+bsd_audioirig.h header file. In ordinary operation all bits of the
+status byte are zero and the " " status character is set in the ASCII
+timecode. If any of these bits are nonzero, the "?" status character is
+set in the ASCII timecode.
+
+AUDIO_IRIG_BADSIGNAL
+
+ The signal amplitude is outside tolerance limits, either in
+ amplitude or modulation depth. The indicated time may or may not be
+ in error. If the signal is too high, it may be clipped by the
+ codec, so that the pulse width cannot be reliably determined. If
+ too low, it may be obscured by noise. The nominal expectation is
+ that the peak amplitude of the signal be maintained by the codec
+ AGC at about 10 dB below the clipping level and that the modulation
+ index be at least 0.5 (6 dB).
+
+AUDIO_IRIG_BADDATA
+
+ An invalid hex code (A through F) has been found where BCD data is
+ expected. The ASCII representation of the invalid code is set to
+ "?". Errors of this type are most likely due to noise on the IRIG
+ signal due to ground loops, coupling to other noise sources, etc.
+
+AUDIO_IRIG_BADSYNC
+
+ A code element has been found where a position identifier should be
+ or a position identifier has been found where a code element should
+ be. The time is meaningless and should be disregarded. Errors of
+ this type can be due to severe noise on the IRIG signal due to
+ ground loops, coupling to other noise sources, etc., or during
+ initial acquisition of the signal.
+
+AUDIO_IRIG_BADCLOCK
+
+ Some IRIG timecode generators can indicate whether or not the
+ generator is operating correctly or synchronized to its source of
+ standard time using a designated field in the raw binary timecode.
+ Where such information is available and the IRIG decoder can detect
+ it, this bit is set when the generator reports anything except
+ normal operating conditions.
+
+AUDIO_IRIG_OLDDATA
+
+ The IRIG time has not changed since the last time it was returned
+ in a read() call. This is not normally considered an error, unless
+ it persists for longer than a few seconds, in which case it
+ probably indicates a hardware problem.
+
+The irig_time.time[14] vector is a character string in the format "ddd
+hh:mm:ss*\0", which consists of 13 ASCII characters followed by a zero
+terminator. The "*" status character is an ASCII space " " if the status
+byte is zero and "?" if not. This format is identical to format 0,
+except that in format 1 the time string is null-terminated.
+
+2.1. Programming Example
+
+The following pseudo-code demonstrates how the IRIG receiver may be used
+by a simple user program. Of course, real code should include error
+checking after each call to ensure the driver is communicating properly.
+It should also verify that the correct fields in the structure are being
+filled by the read() call.
+
+ include "bsd_audioirig.h"
+
+ int format = 1;
+ struct irig_time it;
+
+ Audio_fd = open("/dev/audio", O_RDONLY);
+ ioctl(Audio_fd, AUDIO_IRIG_OPEN, NULL);
+ ioctl(Audio_fd, AUDIO_IRIG_SETFORMAT,&format);
+ while (condition)
+ read(Audio_fd, &it, sizeof(it);
+ printf("%s\n", it.time);
+ ioctl(Audio_fd, AUDIO_IRIG_CLOSE, NULL);
+ close(Audio_fd);
+
+3. Implementation and Configuration Notes
+
+The signal level produced by most IRIG-equipped radios is on the order
+of a few volts peak-peak, which is far larger than the audio codec can
+accept; therefore, an attenuator in the form of a voltage divider is
+needed. The codec can handle IRIG signals at the microphone input from
+4.2mV to 230mV peak-peak. A suitable attenuator conists of a series-
+connected 100K-Ohm resistor at the input and a parallel-connected 1K-Ohm
+resistor at the output, both contained along with suitable connectors in
+a small aluminum box. The exact values of these resistors are not
+critical, since the IRIG driver includes an automatic level-adjustment
+capability.
+
+For the most accurate time using the IRIG signal and a particular radio,
+it may be necessary to adjust the time1 parameter of the fudge command
+to compensate for the codec delay and any additional delay due to IRIG
+processing in the radio itself. Since the codec samples at an 8-kHz
+rate, the average delay is about 62 usec; however, the delays due to the
+radios and IRIG signals themselves can vary. For instance, in the
+Austron recievers the IRIG delay is essentially zero, while in the
+Spectracom receivers the delay is about 240 usec relative to the 1-pps
+signal. In addition, the poll interval can be reduced from the usual 64
+seconds to 16 seconds to reduce wander of the local hardware clock.
+Finally, the prefer parameter can be used to bias the clock-selection
+algorithm to favor the IRIG time, which is ordinarily the best time
+available. For example, the following two lines in the NTP configuration
+file ntp.conf are appropriate for the Spectracom Netclock/1 WWVB
+Synchronized Clock with IRIG Option:
+
+server 127.127.6.0 prefer minpoll 4 maxpoll 4 # irig audio decoder
+fudge 127.127.6.0 time1 0.0005
+
+The time1 value of .0005 s (500 usec) was determined by actual
+measurement. Since the IRIG delay in Austron receivers is essentially
+zero, the fudge command is not necessary with these receivers. The
+correct value in case of other radios may have to be determined by
+actual measurement. A convenient way of doing this is to configure the
+PPSPPS feature in the NTP Version 3 distribution and adjust time1 until
+the 1-pps signal and IRIG signal both show the same offset.
+
+The modified BSD driver includes both the modified driver itself
+bsd_audio.c and the IRIG header file bsd_audioirig.h, as well as
+modified header files bsd_audiovar.h and bsd_audioio.h. The driver is
+installed in the same way as described in the BSD driver documentation,
+with the addition of the following define in the kernel configuration
+file:
+
+options AUDIO_IRIG # IRIG driver
+
+This causes the IRIG code to be included in the BSD driver, as well as a
+C-coded codec interrupt routine which replaces the assembly-coded
+routine and provides the IRIG functionality. While the C-coded routine
+is somewhat slower than the assembly-coded routine, the extra overhead
+is not expected to be significant. Note that the IRIG driver calls the
+kernel routine microtime() as included in the ppsclock directory of the
+NTP Version 3 distribution xntp3. It is highly recommended that this
+routine be installed in the kernel configuration as well. The
+instructions for doing this are contained in the ppsclock directory of
+the xntp3 distribution.
+
+Roy LeCates <lecates@udel.edu> and David Mills <mills@udel.edu>
+Electrical Engineering Department
+University of Delaware
+Newark, DE 19716
+302 831 8247 fax 302 831 4316
+
+24 August 1993
diff --git a/usr.sbin/xntpd/doc/README.kern b/usr.sbin/xntpd/doc/README.kern
new file mode 100644
index 0000000..aac26fa
--- /dev/null
+++ b/usr.sbin/xntpd/doc/README.kern
@@ -0,0 +1,1374 @@
+ A Kernel Model for Precision Timekeeping
+
+ Revised 3 April 1994
+
+Note: This memorandum is a substantial revision of RFC-1589, "A Kernel
+Model for Precision Timekeeping," March, 1994. It includes several
+changes to the daemon and user interfaces, as well as a new feature
+which disciplines the CPU clock oscillator in both time and frequency to
+a source of precision time signals. This memorandum is included in the
+distributions for the SunOS, Ultrix and OSF/1 kernels and in the NTP
+Version 3 distribution (xntp3.v.tar.Z) as the file README.kern, where v
+is the version identifier. Availability of the kernel distributions,
+which involve licensed code, will be announced separately. The NTP
+Version 3 distribution can be obtained via anonymous ftp from
+louie.udel.edu in the directory pub/ntp. In order to utilize all
+features of this distribution, the NTP version identifier should be 3q
+or later.
+
+Overview
+
+This memorandum describes an engineering model which implements a
+precision time-of-day function for a generic operating system. The model
+is based on the principles of disciplined oscillators and phase-lock
+loops (PLL) and frequency-lock loops (FLL) often found in the
+engineering literature. It has been implemented in the Unix kernels for
+several workstations, including those made by Sun Microsystems and
+Digital Equipment. The model changes the way the system clock is
+adjusted in time and frequency, as well as provides mechanisms to
+discipline its frequency to an external precision timing source. The
+model incorporates a generic system-call interface for use with the
+Network Time Protocol (NTP) or similar time synchronization protocol.
+The NTP Version 3 daemon xntpd operates with this model to provide
+synchronization limited in principle only by the accuracy and stability
+of the external timing source.
+
+This memorandum does not obsolete or update any RFC. It does not propose
+a standard protocol, specification or algorithm. It is intended to
+provoke comment, refinement and implementations for kernels not
+considered herein. While a working knowledge of NTP is not required for
+an understanding of the design principles or implementation of the
+model, it may be helpful in understanding how the model behaves in a
+fully functional timekeeping system. The architecture and design of NTP
+is described in [MIL91], while the current NTP Version 3 protocol
+specification is given in RFC-1305 [MIL92a] and a subset of the
+protocol, the Simple Network Time Protocol (SNTP), is given in RFC-1361
+[MIL92c].
+
+The model has been implemented in the Unix kernels for three Sun
+Microsystems and Digital Equipment workstations. In addition, for the
+Digital machines the model provides improved precision to one
+microsecond (us). Since these specific implementations involve
+modifications to licensed code, they cannot be provided directly.
+Inquiries should be directed to the manufacturer's representatives.
+However, the engineering model for these implementations, including a
+simulator with code segments almost identical to the implementations,
+but not involving licensed code, is available via anonymous FTP from
+host louie.udel.edu in the directory pub/ntp and compressed tar archive
+kernel.tar.Z. The NTP Version 3 distribution can be obtained via
+anonymous ftp from the same host and directory in the compressed tar
+archive xntp3.3q.tar.Z, where the version number shown as 3.3q may be
+adjusted for new versions as they occur.
+
+1. Introduction
+
+This memorandum describes a model and programming interface for generic
+operating system software that manages the system clock and timer
+functions. The model provides improved accuracy and stability for most
+computers using the Network Time Protocol (NTP) or similar time
+synchronization protocol. This memorandum describes the design
+principles and implementations of the model, while related technical
+reports discuss the design approach, engineering analysis and
+performance evaluation of the model as implemented in Unix kernels for
+modern workstations. The NTP Version 3 daemon xntpd operates with these
+implementations to provide improved accuracy and stability, together
+with diminished overhead in the operating system and network. In
+addition, the model supports the use of external timing sources, such as
+precision pulse-per-second (PPS) signals and the industry standard IRIG
+timing signals. The NTP daemon automatically detects the presence of the
+new features and utilizes them when available.
+
+There are three prototype implementations of the model presented in this
+memorandum, one each for the Sun Microsystems SPARCstation with the
+SunOS 4.1.x kernel, Digital Equipment DECstation 5000 with the Ultrix
+4.x kernel and Digital Equipment 3000 AXP Alpha with the OSF/1 V1.x
+kernel. In addition, for the DECstation 5000/240 and 3000 AXP Alpha
+machines, a special feature provides improved precision to 1 us (stock
+Sun kernels already do provide this precision). Other than improving the
+system clock accuracy, stability and precision, these implementations do
+not change the operation of existing Unix system calls which manage the
+system clock, such as gettimeofday(), settimeofday() and adjtime();
+however, if the new features are in use, the operations of
+gettimeofday() and adjtime() can be controlled instead by new system
+calls ntp_gettime() and ntp_adjtime() as described below.
+
+A detailed description of the variables and algorithms that operate upon
+them is given in the hope that similar functionality can be incorporated
+in Unix kernels for other machines. The algorithms involve only minor
+changes to the system clock and interval timer routines and include
+interfaces for application programs to learn the system clock status and
+certain statistics of the time synchronization process. Detailed
+installation instructions are given in a specific README files included
+in the kernel distributions.
+
+In this memorandum, NTP Version 3 and the Unix implementation xntp3 are
+used as an example application of the new system calls for use by a
+synchronization daemon. In principle, these system calls can be used by
+other protocols and implementations as well. Even in cases where the
+local time is maintained by periodic exchanges of messages at relatively
+long intervals, such as using the NIST Automated Computer Time Service
+[LEV89], the ability to precisely adjust the system clock frequency
+simplifies the synchronization procedures and allows the telephone call
+frequency to be considerably reduced.
+
+2. Design Approach
+
+While not strictly necessary for an understanding or implementation of
+the model, it may be helpful to briefly describe how NTP operates to
+control the system clock in a client computer. As described in [MIL91],
+the NTP protocol exchanges timestamps with one or more peers sharing a
+synchronization subnet to calculate the time offsets between peer clocks
+and the local clock. These offsets are processed by several algorithms
+which refine and combine the offsets to produce an ensemble average,
+which is then used to adjust the local clock time and frequency. The
+manner in which the local clock is adjusted represents the main topic of
+this memorandum. The goal in the enterprise is the most accurate and
+stable system clock possible with the available computer hardware and
+kernel software.
+
+In order to understand how the new model works, it is useful to review
+how most Unix kernels maintain the system clock. In the Unix design a
+hardware counter interrupts the kernel at a fixed rate: 100 Hz in the
+SunOS kernel, 256 Hz in the Ultrix kernel and 1024 Hz in the OSF/1
+kernel. Since the Ultrix timer interval (reciprocal of the rate) does
+not evenly divide one second in microseconds, the kernel adds 64 us once
+each second, so the timescale consists of 255 advances of 3906 us plus
+one of 3970 us. Similarly, the OSF/1 kernel adds 576 us once each
+second, so its timescale consists of 1023 advances of 976 us plus one of
+1552 us.
+
+2.1. Mechanisms to Adjust Time and Frequency
+
+In most Unix kernels it is possible to slew the system clock to a new
+offset relative to the current time by using the adjtime() system call.
+To do this the clock frequency is changed by adding or subtracting a
+fixed amount (tickadj) at each timer interrupt (tick) for a calculated
+number of timer interrupts. Since this calculation involves dividing the
+requested offset by tickadj, it is possible to slew to a new offset with
+a precision only of tickadj, which is usually in the neighborhood of 5
+us, but sometimes much larger. This results in a roundoff error which
+can accumulate to an unacceptable degree, so that special provisions
+must be made in the clock adjustment procedures of the synchronization
+daemon.
+
+In order to implement a frequency discipline function, it is necessary
+to provide time offset adjustments to the kernel at regular adjustment
+intervals using the adjtime() system call. In order to reduce the system
+clock jitter to the regime consistent with the model, it is necessary
+that the adjustment interval be relatively small, in the neighborhood of
+1 s. However, the Unix adjtime() implementation requires each offset
+adjustment to complete before another one can be begun, which means that
+large adjustments must be amortized over possibly many adjustment
+intervals. The requirement to implement the adjustment interval and
+compensate for roundoff error considerably complicates the synchronizing
+daemon implementation.
+
+In the new model this scheme is replaced by another that represents the
+system clock as a multiple-word, precision-time variable in order to
+provide very precise clock adjustments. At each timer interrupt a
+precisely calibrated quantity is added to the kernel time variable and
+overflows propagated as required. The quantity is computed as in the NTP
+local clock model described in [MIL92b], which operates as an adaptive-
+parameter, first-order, type-II phase-lock loop (PLL). In principle,
+this PLL design can provide precision control of the system clock
+oscillator within 1 us and frequency to within parts in 10^11. While
+precisions of this order are surely well beyond the capabilities of the
+CPU clock oscillator used in typical workstations, they are appropriate
+using precision external oscillators, as described below.
+
+The PLL design is identical to the one originally implemented in NTP and
+described in [MIL92b]. In the original design the software daemon
+simulates the PLL using the adjtime() system call; however, the daemon
+implementation is considerably complicated by the considerations
+described above. The modified kernel routines implement the PLL in the
+kernel using precision time and frequency representations, so that these
+complications are avoided. A new system call ntp_adjtime() is called
+only as each new time update is determined, which in NTP occurs at
+intervals of from 16 s to 1024 s. In addition, doing frequency
+compensation in the kernel means that the system clock runs true even if
+the daemon were to cease operation or the network paths to the primary
+synchronization source fail.
+
+In the new model the new ntp_adjtime() operates in a way similar to the
+original adjtime() system call, but does so independently of adjtime(),
+which continues to operate in its traditional fashion. When used with
+NTP, it is the design intent that settimeofday() or adjtime() be used
+only for system clock adjustments greater than +-128 ms, although the
+dynamic range of the new model is much larger at +-512 ms. It has been
+the Internet experience that the need to change the system clock in
+increments greater than +-128 ms is extremely rare and is usually
+associated with a hardware or software malfunction or system reboot.
+
+The easiest way to set the time is with the settimeofday() system call;
+however, this can under some conditions cause the clock to jump
+backwards. If this cannot be tolerated, adjtime() can be used to slew
+the clock to the new value without running backward or affecting the
+frequency discipline process. Once the system clock has been set within
++-128 ms, the ntp_adjtime() system call is used to provide periodic
+updates including the time offset, maximum error, estimated error and
+PLL time constant. With NTP the update interval and time constant depend
+on the measured delay and dispersion; however, the scheme is quite
+forgiving and neither moderate loss of updates nor variations in the
+update interval are serious.
+
+2.2 Daemon and Application Interface
+
+Unix application programs can read the system clock using the
+gettimeofday() system call, which returns only the system time and
+timezone data. For some applications it is useful to know the maximum
+error of the reported time due to all causes, including clock reading
+errors, oscillator frequency errors and accumulated latencies on the
+path to the primary synchronization source. However, in the new model
+the PLL adjusts the system clock to compensate for its intrinsic
+frequency error, so that the time error expected in normal operation
+will usually be much less than the maximum error. The programming
+interface includes a new system call ntp_gettime(), which returns the
+system time, as well as the maximum error and estimated error. This
+interface is intended to support applications that need such things,
+including distributed file systems, multimedia teleconferencing and
+other real-time applications. The programming interface also includes a
+new system call ntp_adjtime(), which can be used to read and write
+kernel variables for time and frequency adjustment, PLL time constant,
+leap-second warning and related data.
+
+In addition, the kernel adjusts the indicated maximum error to grow by
+an amount equal to the maximum oscillator frequency tolerance times the
+elapsed time since the last update. The default engineering parameters
+have been optimized for update intervals in the order of 64 s. As shown
+in [MIL93], this is near the optimum interval for NTP used with ordinary
+room-temperature quartz oscillators. For other intervals the PLL time
+constant can be adjusted to optimize the dynamic response over intervals
+of 16-1024 s. Normally, this is automatically done by NTP. In any case,
+if updates are suspended, the PLL coasts at the frequency last
+determined, which usually results in errors increasing only to a few
+tens of milliseconds over a day using typical modern workstations.
+
+While any synchronization daemon can in principle be modified to use the
+new system calls, the most likely will be users of the NTP Version 3
+daemon xntpd. The xntpd code determines whether the new system calls are
+implemented and automatically reconfigures as required. When
+implemented, the daemon reads the frequency offset from a system file
+and provides it and the initial time constant via ntp_adjtime(). In
+subsequent calls to ntp_adjtime(), only the time offset and time
+constant are affected. The daemon reads the frequency from the kernel
+using ntp_adjtime() at intervals of about one hour and writes it to a
+system file. This information is recovered when the daemon is restarted
+after reboot, for example, so the sometimes extensive training period to
+learn the frequency separately for each oscillator can be avoided.
+
+2.3. Precision Clocks for DECstation 5000/240 and 3000 AXP Alpha
+
+The stock microtime() routine in the Ultrix kernel for Digital Equipment
+MIPS-based workstations returns system time to the precision of the
+timer interrupt interval, which is in the 1-4 ms range. However, in the
+DECstation 5000/240 and possibly other machines of that family, there is
+an undocumented IOASIC hardware register that counts system bus cycles
+at a rate of 25 MHz. The new microtime() routine for the Ultrix kernel
+uses this register to interpolate system time between timer interrupts.
+This results in a precision of 1 us for all time values obtained via the
+gettimeofday() and ntp_gettime() system calls. For the Digital Equipment
+3000 AXP Alpha, the architecture provides a hardware Process Cycle
+Counter and a machine instruction (rpcc) to read it. This counter
+operates at the fundamental frequency of the CPU clock or some
+submultiple of it, 133.333 MHz for the 3000/400 for example. The new
+microtime() routine for the OSF/1 kernel uses this counter in the same
+fashion as the Ultrix routine. Support for this feature is conditionally
+compiled in the kernel only if the MICRO option is used in the kernel
+configuration file.
+
+In both the Ultrix and OSF/1 kernels the gettimeofday() and
+ntp_gettime() system call use the new microtime() routine, which returns
+the interpolated value to 1-us resolution, but does not change the
+kernel time variable. Therefore, other routines that access the kernel
+time variable directly and do not call either gettimeofday(),
+ntp_gettime() or microtime() will continue their present behavior. The
+microtime() feature is independent of other features described here and
+is operative even if the kernel PLL or new system calls have not been
+implemented.
+
+The SunOS kernel already includes a system clock with 1-us resolution;
+so, in principle, no microtime() routine is necessary. An existing
+kernel routine uniqtime() implements this function, but it is coded in
+the C language and is rather slow at 42-85 us per call on a SPARCstation
+IPC. A replacement microtime() routine coded in assembler language is
+available in the NTP Version 3 distribution and is much faster at about
+3 us per call. Note that, as explained later, this routine should be
+called at an interrupt priority level not greater than that of the timer
+interrupt routine. Otherwise, it is possible to miss a tick increment,
+with result the time returned can be late by one tick. This is always
+true in the case of gettimeofday() and ntp_gettime(), but might not be
+true in other cases, such as when using the PPS signal described later
+in this memorandum.
+
+2.4. External Time and Frequency Discipline
+
+The overall accuracy of a time synchronization subnet with respect to
+Coordinated Universal Time (UTC) depends on the accuracy and stability
+of the primary synchronization source, usually a radio or satellite
+receiver, and the CPU clock oscillator of the primary server. As
+discussed in [MIL93], the traditional interface using a ASCII serial
+timecode and RS232 port precludes the full accuracy of most radio
+clocks. In addition, the poor frequency stability of typical CPU clock
+oscillators limits the accuracy, whether or not precision time sources
+are available. There are, however, several ways in which the system
+clock accuracy and stability can be improved to the degree limited only
+by the accuracy and stability of the synchronization source and the
+jitter of the interface and operating system.
+
+Many radio clocks produce special signals that can be used by external
+equipment to precisely synchronize time and frequency. Most produce a
+pulse-per-second (PPS) signal that can be read via a modem-control lead
+of a serial port and some produce a special IRIG signal that can be read
+directly by a bus peripheral, such as the KSI/Odetics TPRO IRIG SBus
+interface, or indirectly via the audio codec of some workstations, as
+described in [MIL93]. In the NTP Version 3 daemon xntpd, the PPS signal
+can be used to augment the less precise ASCII serial timecode to improve
+accuracy to the order of a few tens of microseconds. Support is also
+included in the NTP distribution for the TPRO interface, as well as the
+audio codec; however, the latter requires a modified kernel audio driver
+contained in the compressed tar archive bsd_audio.tar.Z in the same host
+and directory as the NTP Version 3 distribution mentioned previously.
+2.4.1. PPS Signal
+
+The most convenient way to interface a PPS signal to a computer is
+usually with a serial port and RS232-compatible signal; however, the PPS
+signal produced by most radio clocks and laboratory instruments is
+usually a TTL pulse signal. Therefore, some kind of level
+converter/pulse generator is necessary to adapt the PPS signal to a
+serial port. An example design, including schematic and printed-circuit
+board artwork, is in the compressed tar archive gadget.tar.Z in the same
+host and directory as the NTP Version 3 distribution mentioned
+previously. There are several ways the PPS signal can be used in
+conjunction with the NTP Version 3 daemon xntpd, as described in [MIL93]
+and in the documentation included in the distribution.
+
+The NTP Version 3 distribution includes a special ppsclock module for
+the SunOS 4.1.x kernel that captures the PPS signal presented via a
+modem-control lead of a serial port. Normally, the ppsclock module
+produces a timestamp at each transition of the PPS signal and provides
+it to the synchronization daemon for integration with the serial ASCII
+timecode, also produced by the radio clock. With the conventional PLL
+implementation in either the daemon or the kernel as described in
+[MIL93], the accuracy of this scheme is limited by the intrinsic
+stability of the CPU clock oscillator to a millisecond or two, depending
+on environmental temperature variations.
+
+The ppsclock module has been modified to in addition call a new kernel
+routine hardpps() once each second. In addition, the Ultrix 4.3 kernel
+has been modified to provide a similar functionality. The hardpps()
+routine compares the timestamp with a sample of the CPU clock oscillator
+in order to discipline the oscillator to the time and frequency of the
+PPS signal. Using this method, the time accuracy is improved to
+typically 20 us or less and frequency stability a few parts in 10^8,
+which is about two orders of magnitude better than the undisciplined
+oscillator. The new feature is conditionally compiled in the code
+described below only if the PPS_SYNC option is used in the kernel
+configuration file.
+
+When using the PPS signal to adjust the time, there is a problem with
+some kernels which is very difficult to fix. The serial port interrupt
+routine often operates at an interrupt priority level above the timer
+interrupt routine. Thus, as explained below, it is possible that a tick
+increment can be missed and the time returned late by one tick. It may
+happen that, if the CPU clock oscillator frequency is close to the PPS
+oscillator frequency (less than a few ppm), this condition can persist
+for two or more successive PPS interrupts. A useful workaround in the
+code is to use a glitch detector and median filter to process the PPS
+sample offsets. The glitch detector suppresses offset bursts greater
+than half the tick interval and which last less than 30 successive PPS
+interrupts. The median filter ranks the offsets in a moving window of
+three samples and uses the median as the output and the difference
+between the other two as a dispersion measure.
+
+2.4.2. External Clocks
+
+It is possible to replace the system clock function with an external bus
+peripheral. The TPRO device mentioned previously can be used to provide
+IRIG-synchronized time with a precision of 1 us. A driver for this
+device tprotime.c and header file tpro.h are included in the
+kernel.tar.Z distribution mentioned previously. Using this device, the
+system clock is read directly from the interface; however, the device
+does not record the year, so special provisions have been made to obtain
+the year from the kernel time variable and initialize the driver
+accordingly. Support for this feature is conditionally compiled in the
+kernel only if the EXT_CLOCK and TPRO options are used in the kernel
+configuration file.
+
+While the system clock function is provided directly by the microtime()
+routine in the driver, the kernel time variable must be disciplined as
+well, since not all system timing functions use the microtime() routine.
+This is done by measuring the time difference between the microtime()
+clock and kernel time variable and using it to adjust the kernel PLL as
+if the adjustment were provided by an external peer and NTP.
+
+A good deal of error checking is done in the TPRO driver, since the
+system clock is vulnerable to a misbehaving radio clock, IRIG signal
+source, interface cables and TPRO device itself. Unfortunately, there is
+no practical way to utilize the extensive diversity and redundancy
+capabilities available in the NTP synchronization daemon. In order to
+avoid disruptions that might occur if the TPRO time is far different
+from the kernel time variable, the latter is used instead of the former
+if the difference between the two exceeds 1000 s; presumably in that
+case operator intervention is required.
+
+2.4.2. External Oscillators
+
+Even if a source of PPS or IRIG signals is not available, it is still
+possible to improve the stability of the system clock through the use of
+a specialized bus peripheral. In order to explore the benefits of such
+an approach, a special SBus peripheral called HIGHBALL has been
+constructed. The device includes a pair of 32-bit hardware counters in
+Unix timeval format, together with a precision, oven-controlled quartz
+oscillator with a stability of a few parts in 10^9. A driver for this
+device hightime.c and header file high.h are included in the
+kernel.tar.Z distribution mentioned previously. Support for this feature
+is conditionally compiled in the kernel only if the EXT_CLOCK and
+HIGHBALL options are used in the kernel configuration file.
+
+Unlike the external clock case, where the system clock function is
+provided directly by the microtime() routine in the driver, the HIGHBALL
+counter offsets with respect to UTC must be provided first. This is done
+using the ordinary kernel PLL, but controlling the counter offsets
+directly, rather than the kernel time variable. At first, this might
+seem to defeat the purpose of the design, since the jitter and wander of
+the synchronization source will affect the counter offsets and thus the
+accuracy of the time. However, the jitter is much reduced by the PLL and
+the wander is small, especially if using a radio clock or another
+primary server disciplined in the same way. In practice, the scheme
+works to reduce the incidental wander to a few parts in 10^8, or about
+the same as using the PPS signal.
+
+As in the previous case, the kernel time variable must be disciplined as
+well, since not all system timing functions use the microtime() routine.
+However, the kernel PLL cannot be used for this, since it is already in
+use providing offsets for the HIGHBALL counters. Therefore, a special
+correction is calculated from the difference between the microtime()
+clock and the kernel time variable and used to adjust the kernel time
+variable at the next timer interrupt. This somewhat roundabout approach
+is necessary in order that the adjustment does not cause the kernel time
+variable to jump backwards and possibly lose or duplicate a timer event.
+
+2.5 Other Features
+
+It is a design feature of the NTP architecture that the system clocks in
+a synchronization subnet are to read the same or nearly the same values
+before during and after a leap-second event, as declared by national
+standards bodies. The new model is designed to implement the leap event
+upon command by an ntp_adjtime() argument. The intricate and sometimes
+arcane details of the model and implementation are discussed in [MIL92b]
+and [MIL93]. Further details are given in the technical summary later in
+this memorandum.
+3. Technical Summary
+
+In order to more fully understand the workings of the model, a stand-
+alone simulator kern.c and header file timex.h are included in the
+kernel.tar.Z distribution mentioned previously. In addition, an example
+kernel module kern_ntptime.c which implements the ntp_gettime() and
+ntp_adjtime() system calls is included. Neither of these programs
+incorporate licensed code. Since the distribution is somewhat large, due
+to copious comments and ornamentation, it is impractical to include a
+listing of these programs in this memorandum. In any case, implementors
+may choose to snip portions of the simulator for use in new kernel
+designs; but, due to formatting conventions, this would be difficult if
+included in this memorandum.
+
+The kern.c program is an implementation of an adaptive-parameter, first-
+order, type-II phase-lock loop. The system clock is implemented using a
+set of variables and algorithms defined in the simulator and driven by
+explicit offsets generated by the main() routine in the program. The
+algorithms include code fragments almost identical to those in the
+machine-specific kernel implementations and operate in the same way, but
+the operations can be understood separately from any licensed source
+code into which these fragments may be integrated. The code fragments
+themselves are not derived from any licensed code. The following
+discussion assumes that the simulator code is available for inspection.
+
+3.1. PLL Simulation
+
+The simulator operates in conformance with the analytical model
+described in [MIL92b]. The main() program operates as a driver for the
+fragments hardupdate(), hardclock(), second_overflow(), hardpps() and
+microtime(), although not all functions implemented in these fragments
+are simulated. The program simulates the PLL at each timer interrupt and
+prints a summary of critical program variables at each time update.
+
+There are three defined options in the kernel configuration file
+specific to each implementation. The PPS_SYNC option provides support
+for a pulse-per-second (PPS) signal, which is used to discipline the
+frequency of the CPU clock oscillator. The EXT_CLOCK option provides
+support for an external kernel-readable clock, such as the KSI/Odetics
+TPRO IRIG interface or HIGHBALL precision oscillator, both for the SBus.
+The TPRO option provides support for the former, while the HIGHBALL
+option provides support for the latter. External clocks are implemented
+as the microtime() clock driver, with the specific source code selected
+by the kernel configuration file.
+
+The PPS signal is carefully monitored for error conditions which can
+affect accuracy, stability and reliability. The time_status kernel
+variable contains bits that both control the use of the PPS signal and
+reveal its operational status. The function of each bit is described in
+a later section of this memo.
+
+3.1.1. The hardupdate() Fragment
+
+The hardupdate() fragment is called by ntp_adjtime() as each update is
+computed to adjust the system clock phase and frequency. Note that the
+time constant is in units of powers of two, so that multiplies can be
+done by simple shifts. The phase variable is computed as the offset
+divided by the time constant, but clamped to a maximum (for robustness).
+Then, the time since the last update is computed and clamped to a
+maximum and to zero if initializing. The offset is multiplied (sorry
+about the ugly multiply) by the result and divided by the square of the
+time constant and then added to the frequency variable. Note that all
+shifts are assumed to be positive and that a shift of a signed quantity
+to the right requires a little dance.
+
+The STA_PLL and STA_PPSTIME status bits, which are set by the
+ntp_adjtime() system call, serve to enable or inhibit the kernel PLL and
+PPS time-discipline functions. The STA_PPSSIGNAL status bit is set by
+the hardpps() code fragment when the PPS signal is present and operating
+within nominal bounds. Time discipline from the PPS signal operates only
+if both the STA_PPSTIME and STA_PPSSIGNAL bits are set; otherwise, the
+discipline operates from the offset given in the ntp_adjtime() system
+call. In the intended mode of operation, the synchronization daemon sets
+STA_PLL to enable the PLL when first initialized, then sets STA_PPSTIME
+when reliable synchronization to within +-128 ms has been achieved with
+either a radio clock or external peer. The daemon can detect and
+indicate this condition for monitoring purposes by noting that both
+STA_PPSTIME and STA_PPSSIGNAL are set.
+
+With the defines given in the program and header files, the maximum time
+offset is determined by the size in bits of the long type (32 or 64)
+less the SHIFT_UPDATE scale factor (12) or at least 20 bits (signed).
+The scale factor is chosen so that there is no loss of significance in
+later steps, which may involve a right shift up to SHIFT_UPDATE bits.
+This results in a time adjustment range over +-512 ms. Since
+time_constant must be greater than or equal to zero, the maximum
+frequency offset is determined by the SHIFT_USEC scale factor (16) or at
+least 16 bits (signed). This results in a frequency adjustment range
+over +-31,500 ppm.
+
+In the addition step, the value of offset * mtemp is not greater than
+MAXPHASE * MAXSEC = 31 bits (signed), which will not overflow a long add
+on a 32-bit machine. There could be a loss of precision due to the right
+shift of up to 12 bits, since time_constant is bounded at 6. This
+results in a net worst-case frequency resolution of about .063 ppm,
+which is not significant for most quartz oscillators. The worst case
+could be realized only if the NTP peer misbehaves according to the
+protocol specification.
+
+The time_offset value is clamped upon entry. The time_phase variable is
+an accumulator, so is clamped to the tolerance on every call. This helps
+to damp transients before the oscillator frequency has been stabilized,
+as well as to satisfy the correctness assertions if the time
+synchronization protocol or implementation misbehaves.
+
+3.1.2. The hardclock() Fragment
+
+The hardclock() fragment is inserted in the hardware timer interrupt
+routine at the point the system clock is to be incremented by the value
+of tick. Previous to this fragment the time_update variable has been
+initialized to the tick increment plus the value computed by the
+adjtime() system call in the stock Unix kernel, normally plus/minus the
+tickadj value, which is usually in the order of 5 us. The time_phase
+variable, which represents the instantaneous phase of the system clock,
+is advanced by time_adj, which is calculated in the second_overflow()
+fragment described below. If the value of time_phase exceeds 1 us in
+scaled units, time_update is increased by the (signed) excess and
+time_phase retains the residue.
+
+In those cases where a PPS signal is connected by a serial port
+operating at an interrupt priority level greater than the timer
+interrupt, special consideration should be given the location of the
+hardclock() fragment in the timer interrupt routine. The system clock
+should be advanced as early in the routine as possible, preferably
+before the hardware timer interrupt flag is cleared. This reduces or
+eliminates the possibility that the microtime() routine may latch the
+time after the flag is cleared, but before the system clock is advanced,
+which results in a returned time late by one tick.
+
+Except in the case of an external oscillator such as the HIGHBALL
+interface, the hardclock() fragment advances the system clock by the
+value of tick plus time_update. However, in the case of an external
+oscillator, the system clock is obtained directly from the interface and
+time_update used to discipline that interface instead. However, the
+system clock must still be disciplined as explained previously, so the
+value of clock_cpu computed by the second_overflow() fragment is used
+instead.
+
+3.1.3. The second_overflow() Fragment
+
+The second_overflow() fragment is inserted at the point where the
+microseconds field of the system time variable is being checked for
+overflow. Upon overflow the maximum error time_maxerror is increased by
+time_tolerance to reflect the maximum time offset due to oscillator
+frequency error. Then, the increment time_adj to advance the kernel time
+variable is calculated from the (scaled) time_offset and time_freq
+variables updated at the last call to the hardclock() fragment.
+
+The phase adjustment is calculated as a (signed) fraction of the
+time_offset remaining, where the fraction is added to time_adj, then
+subtracted from time_offset. This technique provides a rapid convergence
+when offsets are high, together with good resolution when offsets are
+low. The frequency adjustment is the sum of the (scaled) time_freq
+variable, an adjustment necessary when the tick interval does not evenly
+divide one second fixtick and PPS frequency adjustment pps_freq (if
+configured).
+
+The scheme of approximating exact multiply/divide operations with shifts
+produces good results, except when an exact calculation is required,
+such as when the PPS signal is being used to discipline the CPU clock
+oscillator frequency as described below. As long as the actual
+oscillator frequency is a power of two in Hz, no correction is required.
+However, in the SunOS kernel the clock frequency is 100 Hz, which
+results in an error factor of 0.78. In this case the code increases
+time_adj by a factor of 1.25, which results in an overall error less
+than three percent.
+
+On rollover of the day, the leap-second state machine described below
+determines whether a second is to be inserted or deleted in the
+timescale. The microtime() routine insures that the reported time is
+always monotonically increasing.
+
+3.1.4. The hardpps() Fragment
+
+The hardpps() fragment is operative only if the PPS_SYNC option is
+specified in the kernel configuration file. It is called from the serial
+port driver or equivalent interface at the on-time transition of the PPS
+signal. The code operates as a first-order, type-I, frequency-lock loop
+(FLL) controlled by the difference between the frequency represented by
+the pps_freq variable and the frequency of the hardware clock
+oscillator. It also provides offsets to the hardupdate() fragment in
+order to discipline the system clock time.
+
+In order to avoid calling the microtime() routine more than once for
+each PPS transition, the interface requires the calling program to
+capture the system time and hardware counter contents at the on-time
+transition of the PPS signal and provide a pointer to the timestamp
+(Unix timeval) and counter contents as arguments to the hardpps() call.
+The hardware counter contents are determined by saving the microseconds
+field of the system time, calling the microtime() routine, and
+subtracting the saved value. If a microseconds overflow has occurred
+during the process, the resulting microseconds value will be negative,
+in which case the caller adds 1000000 to normalize the microseconds
+field.
+
+In order to avoid large jitter when the PPS interrupt occurs during the
+timer interrupt routine before the system clock is advanced, a glitch
+detector is used. The detector latches when an offset exceeds a
+threshold tick/2 and stays latched until either a subsequent offset is
+less than the threshold or a specified interval MAXGLITCH (30 s) has
+elapsed. As long as the detector remains latched, it outputs the offset
+immediately preceding the latch, rather than the one received.
+
+A three-stage median filter is used to suppress jitter less than the
+glitch threshold. The median sample drives the PLL, while the difference
+between the other two samples represents the time dispersion. Time
+dispersion samples are averaged and used as a jitter estimate. If this
+estimate exceeds a threshold MAXTIME/2 (100 us), an error bit
+STA_PPSJITTER is raised in the status word.
+
+The frequency of the hardware oscillator is determined from the
+difference in hardware counter readings at the beginning and end of the
+calibration interval divided by the duration of the interval. However,
+the oscillator frequency tolerance, as much as 100 ppm, may cause the
+difference to exceed the tick value, creating an ambiguity. In order to
+avoid this ambiguity, the hardware counter value at the beginning of the
+interval is increased by the current pps_freq value once each second,
+but computed modulo the tick value. At the end of the interval, the
+difference between this value and the value computed from the hardware
+counter is the control signal for the FLL.
+
+Control signal samples which exceed the frequency tolerance MAXFREQ (100
+ppm) are discarded, as well as samples resulting from excessive interval
+duration jitter. In these cases an error bit STA_PPSERROR is raised in
+the status word. Surviving samples are then processed by a three-stage
+median filter. The median sample drives the FLL, while the difference
+between the other two samples represents the frequency dispersion.
+Frequency dispersion samples are averaged and used as a stabiity
+estimate. If this estimate is below a threshold MAXFREQ/4 (25 ppm), the
+median sample is used to correct the oscillator frequency pps_freq with
+a weight expressed as a shift PPS_AVG (2).
+
+Initially, an approximate value for the oscillator frequency is not
+known, so the duration of the calibration interval must be kept small to
+avoid overflowing the tick. The time difference at the end of the
+calibration interval is measured. If greater than tick/4, the interval
+is reduced by half. If less than this fraction for four successive
+calibration intervals, the interval is doubled. This design
+automatically adapts to nominal jitter in the PPS signal, as well as the
+value of tick. The duration of the calibration interval is set by the
+pps_shift variable as a shift in powers of two. The minimum value
+PPS_SHIFT (2) is chosen so that with the highest CPU oscillator
+frequency 1024 Hz and frequency tolerance 100 ppm the tick will not
+overflow. The maximum value PPS_SHIFTMAX (8) is chosen such that the
+maximum averaging time is about 1000 s as determined by measurements of
+Allan variance [MIL93].
+
+Should the PPS signal fail, the current frequency estimate pps_freq
+continues to be used, so the nominal frequency remains correct subject
+only to the instability of the undisciplined oscillator. The procedure
+to save and restore the frequency estimate works as follows. When
+setting the frequency from a file, the time_freq value is set as the
+file value minus the pps_freq value; when retrieving the frequency, the
+two values are added before saving in the file. This scheme provides a
+seamless interface should the PPS signal fail or the kernel
+configuration change. Note that the frequency discipline is active
+whether or not the synchronization daemon is active. Since all Unix
+systems take some time after reboot to build a running system, usually
+by that time the discipline process has already settled down and the
+initial transients due to frequency discipline have damped out.
+3.1.4. External Clock Interface
+
+The external clock driver interface is implemented with two routines,
+microtime(), which returns the current clock time, and clock_set(),
+which furnishes the apparent system time derived from the kernel time
+variable. The latter routine is called only when the clock is set using
+the settimeofday() system call, but can be called from within the
+driver, such as when the year rolls over, for example.
+
+In the stock SunOS kernel and modified Ultrix and OSF/1 kernels, the
+microtime() routine returns the kernel time variable plus an
+interpolation between timer interrupts based on the contents of a
+hardware counter. In the case of an external clock, such as described
+above, the system clock is read directly from the hardware clock
+registers. Examples of external clock drivers are in the tprotime.c and
+hightime.c routines included in the kernel.tar.Z distribution.
+
+The external clock routines return a status code which indicates whether
+the clock is operating correctly and the nature of the problem, if not.
+The return code is interpreted by the ntp_gettime() system call, which
+transitions the status state machine to the TIME_ERR state if an error
+code is returned. This is the only error checking implemented for the
+external clock in the present version of the code.
+
+The simulator has been used to check the PLL operation over the design
+envelope of +-512 ms in time error and +-100 ppm in frequency error.
+This confirms that no overflows occur and that the loop initially
+converges in about 15 minutes for timer interrupt rates from 50 Hz to
+1024 Hz. The loop has a normal overshoot of a few percent and a final
+convergence time of several hours, depending on the initial time and
+frequency error.
+
+3.2. Leap Seconds
+
+It does not seem generally useful in the user application interface to
+provide additional details private to the kernel and synchronization
+protocol, such as stratum, reference identifier, reference timestamp and
+so forth. It would in principle be possible for the application to
+independently evaluate the quality of time and project into the future
+how long this time might be "valid." However, to do that properly would
+duplicate the functionality of the synchronization protocol and require
+knowledge of many mundane details of the platform architecture, such as
+the subnet configuration, reachability status and related variables. For
+the curious, the ntp_adjtime() system call can be used to reveal some of
+these mysteries.
+
+However, the user application may need to know whether a leap second is
+scheduled, since this might affect interval calculations spanning the
+event. A leap-warning condition is determined by the synchronization
+protocol (if remotely synchronized), by the timecode receiver (if
+available), or by the operator (if awake). This condition is set by the
+synchronization daemon on the day the leap second is to occur (30 June
+or 31 December, as announced) by specifying in a ntp_adjtime() system
+call a status bit of either STA_DEL, if a second is to be deleted, or
+STA_INS, if a second is to be inserted. Note that, on all occasions
+since the inception of the leap-second scheme, there has never been a
+deletion, nor is there likely to be one in future. If the bit is
+STA_DEL, the kernel adds one second to the system time immediately
+following second 23:59:58 and resets the clock state to TIME_WAIT. If
+the bit is STA_INS, the kernel subtracts one second from the system time
+immediately following second 23:59:59 and resets the clock stateto
+TIME_OOP, in effect causing system time to repeat second 59. Immediately
+following the repeated second, the kernel resets the clock status to
+TIME_WAIT.
+
+Following the leap operations, the clock remains in the TIME_WAIT state
+until both the STA_DEL and STA_INS status bits are reset. This provides
+both an unambiguous indication that a leap recently occured, as well as
+time for the daemon or operator to clear the warning condition.
+
+Depending upon the system call implementation, the reported time during
+a leap second may repeat (with the TIME_OOP return code set to advertise
+that fact) or be monotonically adjusted until system time "catches up"
+to reported time. With the latter scheme the reported time will be
+correct before and shortly after the leap second (depending on the
+number of microtime() calls during the leap second), but freeze or
+slowly advance during the leap second itself. However, Most programs
+will probably use the ctime() library routine to convert from timeval
+(seconds, microseconds) format to tm format (seconds, minutes,...). If
+this routine is modified to use the ntp_gettime() system call and
+inspect the return code, it could simply report the leap second as
+second 60.
+
+3.3. Clock Status State Machine
+
+The various options possible with the system clock model described in
+this memorandum require a careful examination of the state transitions,
+status indications and recovery procedures should a crucial signal or
+interface fail. In this section is presented a prototype state machine
+designed to support leap second insertion and deletion, as well as
+reveal various kinds of errors in the synchronization process. The
+states of this machine are decoded as follows:
+
+ TIME_OK If a PPS signal or external clock is present, it is
+ working properly and the system clock is derived
+ from it. If not, the synchronization daemon is
+ working properly and the system clock is
+ synchronized to a radio clock or one or more peers.
+
+ TIME_INS An insertion of one second in the system clock has
+ been declared following the last second of the
+ current day, but has not yet been executed.
+
+ TIME_DEL A deletion of the last second of the current day has
+ been declared, but not yet executed.
+
+ TIME_OOP An insertion of one second in the system clock has
+ been declared following the last second of the
+ current day. The second is in progress, but not yet
+ completed. Library conversion routines should
+ interpret this second as 23:59:60.
+
+ TIME_WAIT The scheduled leap event has occurred, but the
+ STA_DEL and STA_INS status bits have not yet been
+ cleared.
+
+ TIME_ERROR Either (a) the synchronization daemon has declared
+ the protocol is not working properly, (b) all
+ sources of outside synchronization have been lost or
+ (c) a PPS signal or external clock is present, but
+ not working properly.
+
+In all states the system clock is derived from either a PPS signal or
+external clock, if present, or the kernel time variable, if not. If a
+PPS error condition is recognized, the PPS signal is disabled and
+ntp_adjtime() updates are used instead. If an external clock error
+condition is recognized, the external clock is disabled and the kernel
+time variable is used instead.
+
+The state machine makes a transition once each second at an instant
+where the microseconds field of the kernel time variable overflows and
+one second is added to the seconds field. However, this condition is
+checked when the timer overflows, which may not coincide with the actual
+seconds increment. This may lead to some interesting anomalies, such as
+a status indication of a leap second in progress (TIME_OOP) when the
+leap second has already expired. This ambiguity is unavoidable, unless
+the timer interrupt is made synchronous with the system clock.
+
+The following state transitions are executed automatically by the kernel
+at rollover of the microseconds field:
+
+ any state -> TIME_ERROR This transition occurs when an error
+ condition is recognized and continues as long
+ as the condition persists. The error indication
+ overrides the normal state indication, but does
+ not affect the actual clock state. Therefore,
+ when the condition is cleared, the normal state
+ indication resumes.
+
+ TIME_OK->TIME_DEL This transition occurs if the STA_DEL bit is
+ set in the status word.
+
+ TIME_OK->TIME_INS This transition occurs if the STA_INS bit is
+ set in the status word.
+
+ TIME_INS->TIME_OOP This transition occurs immediately following
+ second 86,400 of the current day when an
+ insert-second event has been declared.
+
+ TIME_OOP->TIME_WAIT This transition occurs immediately following
+ second 86,401 of the current day; that is, one
+ second after entry to the TIME_OOP state.
+
+ TIME_DEL->TIME_WAIT This transition occurs immediately following
+ second 86,399 of the current day when a delete-
+ second event has been declared.
+
+ TIME_WAIT->TIME_OK This transition occurs when the STA_DEL and
+ STA_INS bits are cleared by an ntp_adjtime()
+ call.
+
+The following table summarizes the actions just before, during and just
+after a leap-second event. Each line in the table shows the UTC and NTP
+times at the beginning of the second. The left column shows the behavior
+when no leap event is to occur. In the middle column the state machine
+is in TIME_INS at the end of UTC second 23:59:59 and the NTP time has
+just reached 400. The NTP time is set back one second to 399 and the
+machine enters TIME_OOP. At the end of the repeated second the machine
+enters TIME_OK and the UTC and NTP times are again in correspondence. In
+the right column the state machine is in TIME_DEL at the end of UTC
+second 23:59:58 and the NTP time has just reached 399. The NTP time is
+incremented, the machine enters TIME_OK and both UTC and NTP times are
+again in correspondence.
+
+ No Leap Leap Insert Leap Delete
+ UTC NTP UTC NTP UTC NTP
+ ---------------------------------------------
+ 23:59:58|398 23:59:58|398 23:59:58|398
+ | | |
+ 23:59:59|399 23:59:59|399 00:00:00|400
+ | | |
+ 00:00:00|400 23:59:60|399 00:00:01|401
+ | | |
+ 00:00:01|401 00:00:00|400 00:00:02|402
+ | | |
+ 00:00:02|402 00:00:01|401 00:00:03|403
+ | | |
+To determine local midnight without fuss, the kernel code simply finds
+the residue of the time.tv_sec (or time.tv_sec + 1) value mod 86,400,
+but this requires a messy divide. Probably a better way to do this is to
+initialize an auxiliary counter in the settimeofday() routine using an
+ugly divide and increment the counter at the same time the time.tv_sec
+is incremented in the timer interrupt routine. For future embellishment.
+
+4. Programming Model and Interfaces
+
+This section describes the programming model for the synchronization
+daemon and user application programs. The ideas are based on suggestions
+from Jeff Mogul and Philip Gladstone and a similar interface designed by
+the latter. It is important to point out that the functionality of the
+original Unix adjtime() system call is preserved, so that the modified
+kernel will work as the unmodified one, should the new features not be
+in use. In this case the ntp_adjtime() system call can still be used to
+read and write kernel variables that might be used by a synchronization
+daemon other than NTP, for example.
+
+The kernel routines use the clock state variable time_state, which
+records whether the clock is synchronized, waiting for a leap second,
+etc. The value of this variable is returned as the result code by both
+the ntp_gettime() and ntp_adjtime() system calls. It is set implicitly
+by the STA_DEL and STA_INS status bits, as described previously. Values
+presently defined in the timex.h header file are as follows:
+
+ TIME_OK 0 no leap second warning
+ TIME_INS 1 insert leap second warning
+ TIME_DEL 2 delete leap second warning
+ TIME_OOP 3 leap second in progress
+ TIME_WAIT 4 leap second has occured
+ TIME_ERROR 5 clock not synchronized
+
+In case of a negative result code, the kernel has intercepted an invalid
+address or (in case of the ntp_adjtime() system call), a superuser
+violation.
+
+4.1. The ntp_gettime() System Call
+
+The syntax and semantics of the ntp_gettime() call are given in the
+following fragment of the timex.h header file. This file is identical,
+except for the SHIFT_HZ define, in the SunOS, Ultrix and OSF/1 kernel
+distributions. (The SHIFT_HZ define represents the logarithm to the base
+2 of the clock oscillator frequency specific to each system type.) Note
+that the timex.h file calls the syscall.h system header file, which must
+be modified to define the SYS_ntp_gettime system call specific to each
+system type. The kernel distributions include directions on how to do
+this.
+
+ /*
+ * This header file defines the Network Time Protocol (NTP)
+ * interfaces for user and daemon application programs. These are
+ * implemented using private system calls and data structures and
+ * require specific kernel support.
+ *
+ * NAME
+ * ntp_gettime - NTP user application interface
+ *
+ * SYNOPSIS
+ * #include <sys/timex.h>
+ *
+ * int system call(SYS_ntp_gettime, tptr)
+ *
+ * int SYS_ntp_gettime defined in syscall.h header file
+ * struct ntptimeval *tptr pointer to ntptimeval structure
+ *
+ * NTP user interface - used to read kernel clock values
+ * Note: maximum error = NTP synch distance = dispersion + delay /
+ * 2
+ * estimated error = NTP dispersion.
+ */
+ struct ntptimeval {
+ struct timeval time; /* current time (ro) */
+ long maxerror; /* maximum error (us) (ro) */
+ long esterror; /* estimated error (us) (ro) */
+ };
+
+The ntp_gettime() system call returns three read-only (ro) values in the
+ntptimeval structure: the current time in unix timeval format plus the
+maximum and estimated errors in microseconds. While the 32-bit long data
+type limits the error quantities to something more than an hour, in
+practice this is not significant, since the protocol itself will declare
+an unsynchronized condition well below that limit. In the NTP Version 3
+specification, if the protocol computes either of these values in excess
+of 16 seconds, they are clamped to that value and the system clock
+declared unsynchronized.
+
+Following is a detailed description of the ntptimeval structure members.
+
+struct timeval time (ro)
+
+ This member is the current system time expressed as a Unix timeval
+ structure. The timeval structure consists of two 32-bit words; the
+ first is the number of seconds past 1 January 1970 assuming no
+ intervening leap-second insertions or deletions, while the second
+ is the number of microseconds within the second.
+
+long maxerror (ro)
+
+ This member is the value of the time_maxerror kernel variable,
+ which represents the maximum error of the indicated time relative
+ to the primary synchronization source, in microseconds. For NTP,
+ the value is initialized by a ntp_adjtime() call to the
+ synchronization distance, which is equal to the root dispersion
+ plus one-half the root delay. It is increased by a small amount
+ (time_tolerance) each second to reflect the maximum clock frequency
+ error. This variable is provided bu a ntp-adjtime() system call and
+ modified by the kernel, but is otherwise not used by the kernel.
+
+long esterror (ro)
+
+ This member is the value of the time_esterror kernel variable,
+ which represents the expected error of the indicated time relative
+ to the primary synchronization source, in microseconds. For NTP,
+ the value is determined as the root dispersion, which represents
+ the best estimate of the actual error of the system clock based on
+ its past behavior, together with observations of multiple clocks
+ within the peer group. This variable is provided bu a ntp-adjtime()
+ system call, but is otherwise not used by the kernel.
+
+4.2. The ntp_adjtime() System Call
+
+The syntax and semantics of the ntp_adjtime() call are given in the
+following fragment of the timex.h header file. Note that, as in the
+ntp_gettime() system call, the syscall.h system header file must be
+modified to define the SYS_ntp_adjtime system call specific to each
+system type. In the fragment, rw = read/write, ro = read-only, wo =
+write-only.
+
+ /*
+ * NAME
+ * ntp_adjtime - NTP daemon application interface
+ *
+ * SYNOPSIS
+ * #include <sys/timex.h>
+ *
+ * int system call(SYS_ntp_adjtime, mode, tptr)
+ *
+ * int SYS_ntp_adjtime defined in syscall.h header file
+ * struct timex *tptr pointer to timex structure
+ *
+ * NTP daemon interface - used to discipline kernel clock
+ * oscillator
+ */
+ struct timex {
+ unsigned int mode; /* mode selector (wo) */
+ long offset; /* time offset (us) (rw) */
+ long frequency; /* frequency offset (scaled ppm) (rw)
+ */
+ long maxerror; /* maximum error (us) (rw) */
+ long esterror; /* estimated error (us) (rw) */
+ int status; /* clock status bits (rw) */
+ long constant; /* pll time constant (rw) */
+ long precision; /* clock precision (us) (ro) */
+ long tolerance; /* clock frequency tolerance (scaled
+ * ppm) (ro) */
+ /*
+ * The following read-only structure members are implemented
+ * only if the PPS signal discipline is configured in the
+ * kernel.
+ */
+ long ppsfreq; /* pps frequency (scaled ppm) (ro) */
+ long jitter; /* pps jitter (us) (ro) */
+ int shift; /* interval duration (s) (shift) (ro)
+ */
+ long stabil; /* pps stability (scaled ppm) (ro) */
+ long jitcnt; /* jitter limit exceeded (ro) */
+ long calcnt; /* calibration intervals (ro) */
+ long errcnt; /* calibration errors (ro) */
+ long stbcnt; /* stability limit exceeded (ro) */
+ };
+
+The ntp_adjtime() system call is used to read and write certain time-
+related kernel variables summarized below. Writing these variables can
+only be done in superuser mode. To write a variable, the mode structure
+member is set with one or more bits, one of which is assigned each of
+the following variables in turn. The current values for all variables
+are returned in any case; therefore, a mode argument of zero means to
+return these values without changing anything.
+
+Following is a description of the timex structure members.
+
+mode (wo)
+
+ This is a bit-coded variable selecting one or more structure
+ members, with one bit assigned each member. If a bit is set, the
+ value of the associated member variable is copied to the
+ corresponding kernel variable; if not, the member is ignored. The
+ bits are assigned as given in the following, with the variable name
+ indicated in parens. Note that the precision, tolerance and PPS
+ variables are determined by the kernel and cannot be changed by
+ ntp_adjtime().
+
+ MOD_OFFSET 0x0001 time offset (offset)
+ MOD_FREQUENCY 0x0002 frequency offset (frequency)
+ MOD_MAXERROR 0x0004 maximum time error (maxerror)
+ MOD_ESTERROR 0x0008 estimated time error (esterror)
+ MOD_STATUS 0x0010 clock status (status)
+ MOD_TIMECONST 0x0020 pll time constant (constant)
+ MOD_CLKB 0x4000 set clock B
+ MOD_CLKA 0x8000 set clock A
+
+ Note that the MOD_CLK0 and MOD_CLK1 bits are intended for those
+ systems where more than one hardware clock is available for backup,
+ such as in Tandem Non-Stop computers. Presumably, in such cases
+ each clock would have its own oscillator and require a separate PLL
+ for each. Refinements to this model are for further study. The
+ interpretation of these bits is as follows:
+
+offset (rw)
+
+ If selected, this member specifies the time adjustment, in
+ microseconds. The absolute value must be less than MAXPHASE
+ (128000) microseconds defined in the timex.h header file. On
+ return, this member contains the residual offset remaining between
+ a previously specified offset and the current system time, in
+ microseconds.
+
+frequency (rw)
+
+ If selected, this member replaces the value of the time_frequency
+ kernel variable. The value is in ppm, with the integer part in the
+ high order 16 bits and fraction in the low order 16 bits. The
+ absolute value must be in the range less than MAXFREQ (100) ppm
+ defined in the timex.h header file.
+
+ The time_freq variable represents the frequency offset of the CPU
+ clock oscillator. It is recalculated as each update to the system
+ clock is determined by the offset member of the timex structure. It
+ is usually set from a value stored in a file when the
+ synchronization daemon is first started. The current value is
+ usually retrieved via this member and written to the file about
+ once per hour.
+
+maxerror (rw)
+
+ If selected, this member replaces the value of the time_maxerror
+ kernel variable, in microseconds. This is the same variable as in
+ the ntp_getime() system call.
+
+esterror (rw)
+
+ If selected, this member replaces the value of the time_esterror
+ kernel variable, in microseconds. This is the same variable as in
+ the ntp_getime() system call.
+
+int status (rw)
+
+ If selected, this member replaces the value of the time_status
+ kernel variable. This variable controls the state machine used to
+ insert or delete leap seconds and shows the status of the
+ timekeeping system, PPS signal and external oscillator, if
+ configured.
+
+ STA_PLL 0x0001 enable PLL updates (r/w)
+ STA_PPSFREQ 0x0002 enable PPS freq discipline (r/w)
+ STA_PPSTIME 0x0004 enable PPS time discipline (r/w)
+ STA_INS 0x0010 insert leap (r/w)
+ STA_DEL 0x0020 delete leap (r/w)
+ STA_UNSYNC 0x0040 clock unsynchronized (r/w)
+ STA_PPSSIGNAL 0x0100 PPS signal present (r)
+ STA_PPSJITTER 0x0200 PPS signal jitter exceeded (r)
+ STA_PPSWANDER 0x0400 PPS signal wander exceeded (r)
+ STA_PPSERROR 0x0800 PPS signal calibration error (r)
+ STA_CLOCKERR 0x1000 clock hardware fault (r)
+
+ The interpretation of these bits is as follows:
+
+ STA_PLL set/cleared by the caller to enable PLL updates
+
+ STA_PPSFREQ set/cleared by the caller to enable PPS frequency
+ discipline
+
+ STA_PPSTIME set/cleared by the caller to enable PPS time
+ discipline
+
+ STA_INS set by the caller to insert a leap second at the end
+ of the current day; cleared by the caller after the
+ event
+
+ STA_DEL set by the caller to delete a leap second at the end
+ of the current day; cleared by the caller after the
+ event
+
+ STA_UNSYNC set/cleared by the caller to indicate clock
+ unsynchronized (e.g., when no peers are reachable)
+
+ STA_PPSSIGNAL set/cleared by the hardpps() fragment to indicate
+ PPS signal present
+
+ STA_PPSJITTER set/cleared by the hardpps() fragment to indicates
+ PPS signal jitter exceeded
+
+ STA_PPSWANDER set/cleared by the hardpps() fragment to indicates
+ PPS signal wander exceeded
+
+ STA_PPSERROR set/cleared by the hardpps() fragment to indicates
+ PPS signal calibration error
+
+ STA_CLOCKERR set/cleared by the external hardware clock driver to
+ indicate hardware fault
+
+ An error condition is raised when (a) either STA_UNSYNC or
+ STA_CLOCKERR is set (loss of synchronization), (b) STA_PPSFREQ or
+ STA_PPSTIME is set and STA_PPSSIGNAL is clear (loss of PPS signal),
+ (c) STA_PPSTIME and STA_PPSJITTER are both set (jitter exceeded),
+ (d) STA_PPSFREQ is set and either STA_PPSWANDER or STA_PPSERROR is
+ set (wander exceeded). An error condition results in a system call
+ return code of TIME_ERROR.
+
+constant (rw)
+
+ If selected, this member replaces the value of the time_constant
+ kernel variable. The value must be between zero and MAXTC (6)
+ defined in the timex.h header file.
+
+ The time_constant variable determines the bandwidth or "stiffness"
+ of the PLL. The value is used as a shift between zero and MAXTC
+ (6), with the effective PLL time constant equal to a multiple of (1
+ << time_constant), in seconds. For room-temperature quartz
+ oscillators, the recommended default value is 2, which corresponds
+ to a PLL time constant of about 900 s and a maximum update interval
+ of about 64 s. The maximum update interval scales directly with the
+ time constant, so that at the maximum time constant of 6, the
+ update interval can be as large as 1024 s.
+
+ Values of time_constant between zero and 2 can be used if quick
+ convergence is necessary; values between 2 and 6 can be used to
+ reduce network load, but at a modest cost in accuracy. Values above
+ 6 are appropriate only if an precision external oscillator is
+ present.
+
+precision (ro)
+
+ This is the current value of the time_precision kernel variable in
+ microseconds.
+
+ The time_precision variable represents the maximum error in reading
+ the system clock, in microseconds. It is usually based on the
+ number of microseconds between timer interrupts (tick), 10000 us
+ for the SunOS kernel, 3906 us for the Ultrix kernel, 976 us for the
+ OSF/1 kernel. However, in cases where the time can be interpolated
+ between timer interrupts with microsecond resolution, such as in
+ the stock SunOS kernel and modified Ultrix and OSF/1 kernels, the
+ precision is specified as 1 us. In cases where a PPS signal or
+ external oscillator is available, the precision can depend on the
+ operating condition of the signal or oscillator. This variable is
+ determined by the kernel for use by the synchronization daemon, but
+ is otherwise not used by the kernel.
+
+tolerance (ro)
+
+ This is the current value of the time_tolerance kernel variable.
+ The value is in ppm, with the integer part in the high order 16
+ bits and fraction in the low order 16 bits.
+
+ The time_tolerance variable represents the maximum frequency error
+ in ppm of the particular CPU clock oscillator and is a property of
+ the hardware; however, in principle it could change as result of
+ the presence of external discipline signals, for instance.
+
+ The recommended value for time_tolerance MAXFREQ (200) ppm is
+ appropriate for room-temperature quartz oscillators used in typical
+ workstations. However, it can change due to the operating condition
+ of the PPS signal and/or external oscillator. With either the PPS
+ signal or external oscillator, the recommended value for MAXFREQ is
+ 100 ppm.
+
+The following members are defined only if the PPS_SYNC option is
+specified in the kernel configuration file. These members are useful
+primarily as a monitoring and evalutation tool. These variables can be
+written only by the kernel.
+
+ppsfreq (ro)
+
+ This is the current value of the pps_freq kernel variable, which is
+ the CPU clock oscillator frequency offset relative to the PPS
+ discipline signal. The value is in ppm, with the integer part in
+ the high order 16 bits and fraction in the low order 16 bits.
+
+jitter (ro)
+
+ This is the current value of the pps_jitter kernel variable, which
+ is the average PPS time dispersion measured by the time-offset
+ median filter, in microseconds.
+
+shift (ro)
+
+ This is the current value of the pps_shift kernel variable, which
+ determines the duration of the calibration interval as the value of
+ 1 << pps_shift, in seconds.
+stabil (ro)
+
+ This is the current value of the pps_stabil kernel variable, which
+ is the average PPS frequency dispersion measured by the frequency-
+ offset median filter. The value is in ppm, with the integer part in
+ the high order 16 bits and fraction in the low order 16 bits.
+
+jitcnt (ro)
+
+ This is the current value of the pps_jitcnt kernel variable, counts
+ the number of PPS signals where the average jitter exceeds the
+ threshold MAXTIME (200 us).
+
+calcnt (ro)
+
+ This is the current value of the pps_calcnt kernel variable, which
+ counts the number of frequency calibration intervals. The duration
+ of these intervals can range from 4 to 256 seconds, as determined
+ by the pps_shift kernel variable.
+
+errcnt (ro)
+
+ This is the current value of the pps_errcnt kernel variable, which
+ counts the number of frequency calibration cycles where (a) the
+ apparent frequency offset is greater than MAXFREQ (100 ppm) or (b)
+ the interval jitter exceeds tick * 2.
+
+stbcnt (ro)
+
+ This is the current value of the pps_discnt kernel variable, which
+ counts the number of calibration intervals where the average
+ stability exceeds the threshold MAXFREQ / 4 (25 ppm).
+
+7. References
+
+[MIL91] Mills, D.L. Internet time synchronization: the Network Time
+Protocol, IEEE Trans. Communications COM-39, 10 (October 1991),
+1482-1493. Also in: Yang, Z., and T.A. Marsland (Eds.). Global
+States and Time in Distributed Systems, IEEE Press, Los Alamitos,
+CA, 91-102.
+
+[MIL92a] Mills, D.L. Network Time Protocol (Version 3) specification,
+implementation and analysis, RFC 1305, University of Delaware, March
+1992, 113 pp.
+
+[MIL92b] Mills, D.L. Modelling and analysis of computer network clocks,
+Electrical Engineering Department Report 92-5-2, University of Delaware,
+May 1992, 29 pp.
+
+[MIL92c] Mills, D.L. Simple Network Time Protocol (SNTP), RFC 1361,
+University of Delaware, August 1992, 10 pp.
+
+[MIL93] Mills, D.L. Precision synchronizatin of computer network clocks,
+Electrical Engineering Department Report 93-11-1, University of
+Delaware, November 1993, 66 pp.
+
+[LEV89] Levine, J., M. Weiss, D. Davis, D. Allan, and D. Sullivan. The
+NIST automated computer time service. J. Research National Institute of
+Standards and Technology 94, 5 (September-October 1989), 311-321.
+
+David L. Mills <mills@udel.edu>
+Electrical Engineering Department
+University of Delaware
+Newark, DE 19716
+302 831 8247 fax 302 831 4316
+3 April 1994
diff --git a/usr.sbin/xntpd/doc/README.magic b/usr.sbin/xntpd/doc/README.magic
new file mode 100644
index 0000000..f473a92
--- /dev/null
+++ b/usr.sbin/xntpd/doc/README.magic
@@ -0,0 +1,346 @@
+ Magic Tricks for Precision Timekeeping
+
+ Revised 19 September 1993
+
+Note: This information file is included in the NTP Version 3
+distribution (xntp3.tar.Z) as the file README.magic. This distribution
+can be obtained via anonymous ftp from louie.udel.edu in the directory
+pub/ntp.
+
+1. Introduction
+
+It most cases it is possible using NTP to synchronize a number of hosts
+on an Ethernet or moderately loaded T1 network to a radio clock within a
+few tens of milliseconds with no particular care in selecting the radio
+clock or configuring the servers on the network. This may be adequate
+for the majority of applications; however, modern workstations and high
+speed networks can do much better than that, generally to within some
+fraction of a millisecond, by using special care in the design of the
+hardware and software interfaces.
+
+The timekeeping accuracy of a NTP-synchronized host depends on two
+quantities: the delay due to hardware and software processing and the
+accumulated jitter due to such things as clock reading precision and
+varying latencies in hardware and software queuing. Processing delays
+directly affect the timekeeping accuracy, unless minimized by systematic
+analysis and adjustment. Jitter, on the other hand, can be essentially
+removed, as long as the statistical properties are unbiased, by the low-
+pass filtering of the phase-lock loop incorporated in the NTP local
+clock model.
+
+This note discusses issues in the connection of external time sources
+such as radio clocks and related timing signals to a primary (stratum-1)
+NTP time server. Of principal concern are various techniques that can be
+utilized to improve the accuracy and precision of the time accuracy and
+frequency stability. Radio clocks are most often connected to a time
+server using a serial asynchronous port. Much of the discussion in this
+memorandum has to do with ways in which the delay incurred in this type
+of connection can be controlled and ways in which the jitter due to
+various causes can be minimized.
+
+However, there are ways other than serial ports to connect a radio
+clock, including special purpose hardware devices for some
+architectures, and even unusual applications of existing interface
+devices, such as the audio codec provided in some systems. Many of these
+methods can yield accuracies as good as any attainable with a serial
+port. For those radio clocks equipped with an IRIG-B signal output, for
+example, a hardware device is available for the Sun SPARCstation; see
+the xntpd.8 manual page in the doc directory of the NTP Version 3
+distribution for further information. In addition, it is possible to
+decode the IRIG-B signal using the audio codec included in the Sun
+SPARCstation and a special kernel driver described in the irig.txt file
+in the doc directory of the NTP Version 3 distribution. These devices
+will not be discussed further in this memorandum.
+
+2. Connection via Serial Port
+
+Most radio clocks produce an ASCII timecode with a precision only to the
+millisecond. This results in a maximum peak-to-peak (p-p) jitter in the
+clock readings of one millisecond. However, assuming the read requests
+are statistically independent of the clock update times, the reading
+error is uniformly distributed over the millisecond, so that the average
+over a large number of readings will make the clock appear 0.5 ms late.
+To compensate for this, it is only necessary to add 0.5 ms to its
+reading before further processing by the NTP algorithms.
+
+Radio clocks are usually connected to the host computer using a serial
+port operating at a typical speed of 9600 baud. The on-time reference
+epoch for the timecode is usually the start bit of a designated
+character, usually <CR>, which is part of the timecode. The UART chip
+implementing the serial port most often has a sample clock of eight to
+16 times the basic baud rate. Assuming the sample clock starts midway in
+the start bit and continues to midway in the first stop bit, this
+creates a processing delay of 10.5 baud times, or about 1.1 ms, relative
+to the start bit of the character. The jitter contribution is usually no
+more than a couple of sample-clock periods, or about 26 usec p-p. This
+is small compared to the clock reading jitter and can be ignored. Thus,
+the UART delay can be considered constant, so the hardware contribution
+to the total mean delay budget is 0.5 + 1.1 = 1.6 ms.
+
+In some kernel serial port drivers, in particular, the Sun zs driver,
+an intentional delay is introduce in input character processing when the
+first character is received after an idle period. A batch of characters
+is passed to the calling program when either (a) a timeout in the
+neighborhood of 10 ms expires or (b) an input buffer fills up. The
+intent in this design is to reduce the interrupt load on the processor
+by batching the characters where possible. Obviously, this can cause
+severe problems for precision timekeeping. It is possible to patch the
+zs driver to eliminate the jitter due to this cause; contact the author
+for further details. However, there is a better solution which will be
+described later in this note. The problem does not appear to be present
+in the Serial/Parallel Controller (SPC) for the SBus, which contains
+eight serial asynchronous ports along with a parallel port. The
+measurements referred to below were made using this controller.
+
+Good timekeeping depends strongly on the means available to capture an
+accurate sample of the local clock or timestamp at the instant the stop
+bit of the on-time character is found; therefore, the code path delay
+between the character interrupt routine and the first place a timestamp
+can be captured is very important, since on some systems such as Sun
+SPARCstations, this path can be astonishingly long. The Sun scheduling
+mechanisms involve both a hardware interrupt queue and a software
+interrupt queue. Entries are made on the hardware queue as the interrupt
+is signalled and generally with the lowest latency, estimated at 20-30
+microseconds (usec) for a SPARC 4/65 IPC. Then, after minimal
+processing, an entry is made on the software queue for later processing
+in order of software interrupt priority. Finally, the software interrupt
+unblocks the NTP daemon which calculates the current local clock offset
+and introduces corrections as required.
+
+Opportunities exist to capture timestamps at the hardware interrupt
+time, software interrupt time and at the time the NTP daemon is
+activated, but these involve various degrees of kernel trespass and
+hardware gimmicks. To gain some idea of the severity of the errors
+introduced at each of these stages, measurements were made using a Sun
+4/65 IPC and a test setup that results in an error between the host
+clock and a precision time source (calibrated cesium clock) no greater
+than 0.1 ms. The total delay from the on-time epoch to when the NTP
+daemon is activated was measured at 8.3 ms in an otherwise idle system,
+but increased on rare occasion to over 25 ms under load, even when the
+NTP daemon was operated at the highest available software priority
+level. Since 1.6 ms of the total delay is due to the hardware, the
+remaining 6.7 ms represents the total code path delay accounting for all
+software processing from the hardware interrupt to the NTP daemon.
+
+It is commonly observed that the latency variations (jitter) in typical
+real-time applications scale as the processing delay. In the case above,
+the ratio of the maximum observed delay (25 ms) to the baseline code
+path delay (8.3 ms) is about three. It is natural to expect that this
+ratio remain the same or less as the code path between the hardware
+interrupt and where the timestamp is captured is reduced. However, in
+general this requires trespass on kernel facilities and/or making use of
+features not common to all or even most Unix implementations. In order
+to assess the cost and benefits of increasingly more aggressive insult
+to the hardware and software of the system, it is useful to construct a
+budget of the code path delay at each of the timestamp opportunity
+times. For instance, on Unix systems which include support for the SIGIO
+facility, it is possible to intervene at the time the software interrupt
+is serviced. The NTP daemon code uses this facility, when available, to
+capture a timestamp and save it along with the data in a buffer for
+later processing. This reduces the total code path delay from 6.7 ms to
+3.5 ms on an otherwise idle system. This reduction applies to all input
+processing, including network interfaces and serial ports.
+
+3. The CLK Mode
+
+By far the best place to capture the timestamp is right in the kernel
+interrupt routine, but this gerally requires intruding in the code
+itself, which can be intricate and architecture dependent. The next best
+place is in some routine close to the interrupt routine on the code
+path. There are two ways to do this, depending on the ancestry of the
+Unix operating system variant. Older systems based primarily on the
+original Unix 4.3bsd support what is called a line discipline module,
+which is a hunk of code with more-or-less well defined interface
+specifications that can get in the way, so to speak, of the code path
+between the interrupt routine and the remainder of the serial port
+processing. Newer systems based on System V STREAMS can do the same
+thing using what is called a streams module. Both approaches are
+supported in the NTP Version 3 distribution, as described in the README
+files in the kernel directory of the distribution. In either case,
+header and source files have to be copied to the kernel build tree and
+certain tables in the kernel have to be modified. In neither case,
+however, are kernel sources required. In order to take advantage of
+this, the clock driver must include code to activate the feature and
+extract the timestamp. At present, this support is included in the clock
+drivers for the Spectracom WWVB clock (WWVB define), the PSTI/Traconex
+WWV/WWVH clock (PST define) and a special one-pulse-per-second (pps)
+signal (PPSCLK define) described later. If justified, support can be
+easily added to most other clock drivers as well. For future reference,
+these modules operating with supported drivers will be called the CLK
+support.
+
+The CLK line discipline and STREAMS modules operate in the same way.
+They look for a designated character, usually <CR>, and stuff a Unix
+timestamp in the data stream following that character whenever it is
+found. Eventually, the data arrive at the particular clock driver
+configured in the NTP Version 3 distribution. The driver then uses the
+timestamp as a precise reference epoch, subject to the earlier
+processing delays and jitter budget, for future reference. In order to
+gain some insight as to the effectiveness of this approach, measurements
+were made using the same test setup described above. The total delay
+from the on-time epoch to the instant when the timestamp is captured was
+measured at 3.5 ms. Thus, the code path delay is this value less the
+hardware delay 3.5 - 1.6 = 1.9 ms.
+
+While the improvement in accuracy in the baseline case is significant,
+there is another factor, at least in Sun systems, that makes it even
+more worthwhile. When processing the code path up to the CLK module, the
+priority is apparently higher than for processing beyond it. In case of
+heavy CPU activity, this can lead to relatively long tails in the
+processing delays for the driver, which of course are avoided by
+capturing the timestamp early in the code path.
+
+4. The PPSCLK Mode
+
+Many timing receivers can produce a 1-pps signal of considerably better
+precision than the ASCII timecode. Using this signal, it is possible to
+avoid the 1-ms p-p jitter and 1.6 ms hardware timecode adjustment
+entirely. However, a device is required to interface this signal to the
+hardware and operating system. In general, this requires some sort of
+level converter and pulse generator that can turn the 1-pps signal on-
+time transition into a valid character. An example of such a device is
+described in the gadget directory of the NTP Version 3 distribution.
+Although many different circuit designs could be used as well, this
+particular device generates a single 26-usec start bit for each 1-pps
+signal on-time transition. This appears to the UART operating at 38.4K
+baud as an ASCII DEL (hex FF).
+
+Now, assuming a serial port can be dedicated to this purpose, a source
+of 1-pps character interrupts is available and can be used to provide a
+precision reference. The NTP Version 3 daemon can be configured to
+utilize this feature by specifying the PPSCLK define, which requires the
+CLK module and gadget box described above. The character resulting from
+each 1-pps signal on-time transition is intercepted by the CLK module
+and a timestamp is inserted in the data stream. An interrupt is created
+for the device driver, which reads the timestamp and discards the DEL
+character. Since the timestamp is captured at the on-time transition,
+the seconds-fraction portion is the offset between the local clock and
+the on-time epoch less the UART delay of 273 usec at 38.4K baud. If the
+local clock is within +-0.5 second of this epoch, as determined by other
+means, the local clock correction is taken as the offset itself, if
+between zero and 0.5 s, and the offset minus one second, if between 0.5
+and 1.0 s. In the NTP daemon the resulting correction is first processed
+by a multi-stage median/trimmed mean filter to remove residual jitter
+and then processed by the usual NTP algorithms.
+
+The baseline delay between the on-time transition and the timestamp
+capture was measured at 400+-10 usec on an otherwise idle test system.
+As the UART delay at 38.4K baud is about 270 usec, the difference, 130
+usec, must be due to the hardware interrupt latency plus the time to
+call the microtime() routine which actually reads the system clock and
+microsecond counter. For these measurements the assembly-coded version
+of this routine described in the ppsclock directory of the NTP Version 3
+distribution was used. This routine reduces the time to read the system
+clock from 42-85 usec with the native Sun C-coded routine to about 3
+usec using the microtime() assembly-coded routine and can be ignored.
+Thus, the 130 usec must be accounted for in interrupt service, register
+window, context switching, streams operations and measurement
+uncertainty, which is probably not unreasonable. The reason for the
+difference between the this figure and the previously calculated value
+of 1.9 ms for the CLK module and serial ASCII timecode is probably due
+to the fact that all STREAMS modules other than the CLK module were
+removed, since the serial port is not used for ordinary ASCII data.
+
+An interesting feature of this approach is that the 1-pps signal is not
+necessarily associated with any particular radio clock and, indeed,
+there may be no such clock at all. Some precision timekeeping equipment,
+such as cesium clocks, VLF receivers and LORAN-C timing receivers
+produce only a precision 1-pps signal and rely on other mechanisms to
+resolve the second of the day and day of the year. It is possible for an
+NTP-synchronized host to derive the latter information using other NTP
+peers, presumably properly synchronized within +-0.5 second, and to
+remove residual jitter using the 1-pps signal. This makes it quite
+practical to deliver precision time to local clients when the subnet
+paths to remote primary servers are heavily congested. In extreme cases
+like this, it has been found useful to increase the tracking aperture
+from +-128 ms to as high as +-512 ms.
+
+In the current implementation the radio timecode and 1-pps signal are
+separately processed. The timecode capture and CLK support, if provided
+by the radio driver, operate the same way whether or not the PPSCLK
+support is enabled. If the local clock is reliably synchronized within
++-0.5 s and the 1-pps signal has been valid for some number of seconds,
+its offset rather than whatever synchronization source has been selected
+is used instead. However, while a this procedure delivers a new offset
+estimate every second, the local clock is updated only as each valid
+update is computed for the peer selected as the source of
+synchronization.
+
+However, there is a hazard to the use of the 1-pps signal in this way if
+the radio generating the 1-pps signal misbehaves or loses
+synchronization with its transmitter. In such a case the radio might
+indicate the error, but the system has no way to associate the error
+with the 1-pps signal. To deal with this problem the prefer parameter
+described in the xntpd.8 man page in the doc directory of the NTP
+Version 3 distribution can be used both to cause the clock selection
+algorithm to choose a preferred peer, all other things being equal, as
+well as associate the error indications in such a way that the 1-pps
+signal will be disregarded if the peer stops providing valid updates,
+such as would occur in an error condition. The prefer parameter can be
+used in other situations as well when preference is to be given a
+particular source of synchronization.
+
+5. The PPS Mode
+
+For the ultimate accuracy and lowest jitter, it would be best to
+eliminate the UART and capture the 1-pps on-time transition directly
+using an appropriate interface. This is in fact possible using a
+modified serial port driver and data lead in the serial port interface
+cable. In this scheme, described in detail in the ppsclock directory of
+the NTP Version 3 distribution, the 1-pps source is connected via the
+previously described gadget box to the carrier-detect lead of a serial
+port. Happily, this can be the same port used for a radio clock, for
+example, or another unrelated serial device. The scheme, referred to
+subsequently as the PPS mode, is specific to the SunOS 4.1.x kernel and
+requires a special STREAMS module. Instructions on how to build the
+kernel are also included in that directory.
+
+Except for special-purpose interface modules, such as the KSI/Odetics
+TPRO IRIG-B decoder and the modified audio driver for the IRIG-B signal
+mentioned previously, the PPS mode provides the most accurate and
+precise timestamp available. There is essentially no latency and the
+timestamp is captured within 20-30 usec of the on-time epoch.
+
+The PPS mode requires the PPSPPS define and one of the radio clock
+serial ports to be selected as the PPS interface. This is the port which
+handles the 1-pps signal; however, the signal path has nothing to do
+with the ordinary serial data path; the two signals are not related,
+other than by the need to activate the PPS mode and pass the file
+descriptor to a common processing routine. Thus, for the port to be
+selected for the PPS function, the define for the associated radio clock
+needs to have a PPS suffix. In case of multiple radio clocks on a single
+time server, the PPS suffix is necessary on only one of them; more than
+one PPS suffix would be an error.
+
+The PPS mode works just like the CLK mode in the treatment of the prefer
+parameter and indicated peer errors. As in the CLK mode, only the offset
+within the second is used and only when the offset is less than +-0.5 s.
+However, the precision of the clock adjustments is usually so fine that
+the error budget is dominated by the inherent short-term stability of
+typical computer local clock oscillators. Therefore, it is advisable to
+reduce the poll interval for the preferred peer from the default 64 s to
+something less, like 16 s. This is done using the minpoll and maxpoll
+parameters of the peer or server command associated with the clock.
+These parameters take as arguments a power of 2, in seconds, which
+becomes the poll interval and, indirectly, affects the bandwidth of the
+tracking loop.
+
+6. Results and Conclusions
+
+It is clear from the above that substantial improvements in timekeeping
+accuracy are possible with varying degrees of hardware and software
+intrusion. While the ultimate accuracy depends on the jitter and wander
+characteristics of the computer local oscillator, it is possible to
+reduce jitter to a negligible degree simply by processing with the NTP
+phase-lock loop and local clock algorithms. The residual jitter using
+the PPS mode on a Sun4 IPC is typically in the 40-100 usec range, while
+the wander is rarely more than twice that under typical environmental
+room conditions.
+
+David L. Mills <mills@udel.edu>
+Electrical Engineering Department
+University of Delaware
+Newark, DE 19716
+302 831 8247 fax 302 831 4316
+
+25 August 1993
diff --git a/usr.sbin/xntpd/doc/README.refclock b/usr.sbin/xntpd/doc/README.refclock
new file mode 100644
index 0000000..8c6c228
--- /dev/null
+++ b/usr.sbin/xntpd/doc/README.refclock
@@ -0,0 +1,1421 @@
+ Information on Reference Clock Drivers
+
+ Revised 5 August 1994
+
+Support for most of the commonly available radio clocks is included in
+the default configuration of xntpd. Individual clocks can be activated
+by configuration file commands, specifically the server and fudge
+commands described in the xntpd.8 man page. This file contains
+information useful in configuring and operating these clocks. Note that
+the man pages and documentation files mentioned in this note can be
+found in the ./doc directory of the xntp3 distribution.
+
+Radio clocks by convention have addresses in the form 127.127.t.u, where
+"t" is the clock type and "u" is a unit number in the range 0-3 used to
+distinguish multiple instances of clocks of the same type. Most of these
+clocks require support in the form of a serial port or special bus
+peripheral. The particular device is normally specified by adding a soft
+link /dev/device%d to the particular hardware device involved. The name
+"device" is compiled in the driver according to the table below. The
+table shows the type number, device name, short description used in some
+displays, and long description used in other displays.
+
+Type Device Name Description
+-------------------------------------------------------
+1 (none) LOCAL Undisciplined Local Clock
+2 trak GPS_TRAK TRAK 8820 GPS Receiver
+3 pst WWV_PST PSTI/Traconex WWV/WWVH Receiver
+4 wwvb WWVB_SPEC Spectracom WWVB Receiver
+5 goes GPS_GOES_TRUE TrueTime GPS/GOES Receivers
+6 irig IRIG_AUDIO IRIG Audio Decoder
+7 chu CHU Scratchbuilt CHU Receiver
+8 refclock- GENERIC Generic Reference Clock Driver
+9 gps GPS_MX4200 Magnavox MX4200 GPS Receiver
+10 gps GPS_AS2201 Austron 2201A GPS Receiver
+11 omega OMEGA_TRUE TrueTime OM-DC OMEGA Receiver
+12 tpro IRIG_TPRO KSI/Odetics TPRO/S IRIG Interface
+13 leitch ATOM_LEITCH Leitch CSD 5300 Master Clock Controller
+14 ees MSF_EES EES M201 MSF Receiver
+15 gpstm GPS_TRUE TrueTime GPS/TM-TMD Receiver
+16 * GPS_BANC Bancomm GPS/IRIG Receiver
+17 datum GPS_DATUM Datum Precision Time System
+18 acts NIST_ACTS NIST Automated Computer Time Service
+19 heath WWV_HEATH Heath WWV/WWVH Receiver
+20 nmea GPS_NMEA Generic NMEA GPS Receiver
+21 * GPS_MOTO Motorola Six Gun GPS Receiver
+22 pps ATOM_PPS PPS Clock Discipline
+
+* Not yet available.
+
+A radio clock is specified in the configuration using the server command
+
+ server 127.127.t.u [ prefer ] [ mode m ]
+
+where t is the type number above and u can be in the range 0-3,
+conventionally 1. Ordinarily, this is the only command necessary to
+configure a radio clock. The optional prefer keyword can be used to
+modify the clock selection algorithm as described in Appendix B. For
+those clock drivers that support multiple modes of operation, the
+optional mode parameter selects which one. This parameter affects the
+operation of each driver as described in Appendix A.
+
+In rare cases a fudge command is necessary to specify additional
+details. This command has the following syntax
+
+fudge 127.127.t.u [stratum s] [refid r] [time1 t1] [time2 t2] [flags]
+and must follow the corresponding server command in the configuration
+file. The optional fields following the clock address are interpreted as
+follows:
+
+stratum s The s, a decimal number in the range 0-15, overrides the
+ default stratum assigned by the driver.
+
+refid r The r, a 4-character, null-terminated ASCII string, overrides
+ the reference identifier assigned by the driver.
+
+time1 t1 The t1, a fixed-point decimal number in seconds, specifies a
+ constant to be added to the time offset produced by the
+ driver. This provides a way to correct a systematic error or
+ bias on the part of the particular clock.
+
+time2 t2 The t2, a fixed-point decimal number, is interpreted in a
+ driver-dependent way. See the descriptions of specific clock
+ drivers in Appendix A.
+
+There are four optional flags named flag1, flag2, flag3 and flag4. A
+flag is specified in the form <keyword> <value>, where <keyword> is one
+of the flag names and <value> is either 0 or 1, as appropriate. Two of
+the flags are generic and are interpreted by all applicable drivers, and
+two are driver dependent. The generic ones are as follows:
+
+flag4 This flag is used to enable detailed status monitoring and
+ event recording. The data collected are written to the
+ clockstats file maintained by the filegen utility (See the
+ xntpd.8 man page). This file is normally processed by a cron
+ job run once per day to produce summary statistics and
+ performance data. The ./scripts/stats directory contains a
+ number of shell and awk scripts for this, as well as S-
+ language programs that produce PostScript plots of performance
+ data.
+
+flag3 This flag is used with Sun SPARCstation baseboard serial ports
+ to assign the ppsclock streams driver for a 1-pps signal
+ produced by some radio clocks and timekeeping devices. See the
+ dscription of the PPS Clock Discipline Driver (type 22) in
+ Appendix A for further information.
+
+Note that the fudge factors above can be changed at run time using the
+xntpdc program (see the xntpdc.8 man page). This program does not have
+to be run on the server machine itself, since it communicates with the
+xntpd daemon using cryptographically authenticated messages.
+
+The PPS Signal
+
+Some radio clocks and related timekeeping gear have a pulse-per-second
+(PPS) signal that can be used to discipline the local clock oscillator
+to a high degree of precision, typically to the order less than 50 us in
+time and 0.1 ppm in frequency. The PPS signal can be connected in either
+of two ways, either via the data leads of a serial port or via the modem
+control leads. Either way requires conversion of the PPS signal, usually
+at TTL levels, to RS232 levels, which can be done using a circuit such
+as described in the ./gadget directory of the xntp3 distribution.
+
+The data leads interface requires regenerating the PPS pulse and
+converting to RS232 signal levels, so that the pulse looks like a
+legitimate ASCII character. The tty_clk module in the ./kernel directory
+inserts a timestamp following this character in the input data stream.
+The driver uses this timestamp to determine the time of arrival of the
+PPS pulse to within 26 us at 38.4 kbps while eliminating error due to
+operating system queues and service times. In order to use the tty_clk
+module, the xntp3 distribution must be compiled with CLK defined.
+The modem control leads interface requires converting to RS232 levels
+and connecting to the carrier detect (CD) lead of a serial port. The
+ppsclock streams module in the ./ppsclock directory is used to capture a
+timestamp upon transition of the PPS signal. The driver reads the latest
+timestamp with a designated ioctl() system call to determine the time of
+arrival of the PPS pulse to within a few tens of microseconds. In order
+to use the ppsclock module, the xntp3 distribution must be compiled with
+PPS defined.
+
+Both of these mechanisms are supported by the ATOM_PPS reference clock
+driver described in Appendix A. This driver is ordinarily used in
+conjunction with another clock driver that supports the radio clock that
+produces the PPS pulse. This driver furnishes the coarse timecode used
+to disambiguate the seconds numbering of the PPS pulse itself. The NTP
+daemon mitigates between the radio clock driver and ATOM_PPS driver as
+described in Appendix B in order to provide the most accurate time,
+while respecting the various types of equipment failures that could
+happen.
+
+For the utmost time quality, a number of Unix system kernel
+modifications can be made as described in the README.magic and
+README.kernel files. Specifically, the ppsclock module can be used to
+interface the PPS signal directly to the kernel for use as discipline
+sources for both time and frequency. These sources can be separately
+enabled and monitored using the ntp_adjtime() system call described in
+README.kernel and the ./include/sys/timex.h header file in the xntp3
+distribution. In order to use the kernel PPS signal, the xntp3
+distribution must be compiled with KERNEL_PLL defined.
+
+In some configurations may have multiple radio clocks, each with PPS
+outputs, as well as a kernel modified to use the PPS signal. In order to
+provide the highest degree of redundancy and survivability, the kernel
+PPS discipline, tty_clk module and ppsclock module may all be in use at
+the same time, each backing up the other. The sometimes complicated
+mitigation rules are described in Appendix B.
+
+Debugging Hints
+
+The ntpq and xntpdc utility programs can be used to debug reference
+clocks, either on the server itself or from another machine elsewhere in
+the network. The server is compiled, installed and started using the
+command-line switches described in the xntpd.8 man page. The first thing
+to look for are error messages on the system log. If none occur, the
+daemon has started, opened the devices specified and waiting for peers
+and radios to come up.
+
+The next step is to be sure the RS232 messages, if used, are getting to
+and from the clock. The most reliable way to do this is with an RS232
+tester and to look for data flashes as the driver polls the clock and/or
+as data arrive from the clock. Our experience is that the overwhelming
+fraction of problems occurring during installation are due to problems
+such as miswired connectors or improperly configured radio clocks at
+this stage.
+
+If RS232 messages are getting to and from the clock, the variables of
+interest can be inspected using the ntpq program and various commands
+described in the ntpq.8 man page. First, use the pe and as commands to
+display a pair of billboards showing the peer configuration and
+association IDs for all peers, including the radio clock peers. The
+assigned clock address should appear in the pe billboard and the
+association ID for it at the same relative line position in the as
+billboard. If things are operating correctly, after a minute or two
+samples should show up in the pe display line for the clock.
+
+Additional information is available with the rv and clockvar commands,
+which take as argument the association ID shown in the as billboard. The
+rv command with no argument shows the system variables, while the rv
+command with argument shows the peer variables for the clock, as well as
+any other peers of interest. The clockvar command with argument shows
+the peer variables specific to reference clock peers, including the
+clock status, device name, last received timecode (if relevant), and
+various event counters. In addition, a subset of the fudge parameters is
+included.
+
+The xntpdc utility program can be used for detailed inspection of the
+clock driver status. The most useful are the clockstat and clkbug
+commands described in the xntpdc.8 man page. While these commands permit
+getting quite personal with the particular driver involved, their use is
+seldom necessary, unless an implementation bug shows up.
+
+Most drivers write a message to the clockstats file as each timecode or
+surrogate is received from the radio clock. By convention, this is the
+last ASCII timecode (or ASCII gloss of a binary-coded one) received from
+the radio. This file is managed by the filegen facility described in the
+xntpd.8 man page and requires specific commands in the configuration
+file. This forms a highly useful record to discover anomalies during
+regular operation of the clock. The scripts included in the
+./scripts/stats directory can be run from a cron job to collect and
+summarize these data on a daily or weekly basis. The summary files have
+proven invaluable to detect infrequent misbehavior due to clock
+implementation bugs in some radios.
+
+Appendix A. Individual Driver Descriptions
+
+Following are detailed descriptions of the clock drivers, together with
+configuration data useful for special circumstances.
+
+Type 1: Undisciplined Local Clock
+
+ This is a hack to allow a machine to use its own system clock as a
+ reference clock, i.e., to free-run using no outside clock
+ discipline source. This is useful if you want to use NTP in an
+ isolated environment with no radio clock or NIST modem available.
+ Pick a machine that you figure has a good clock oscillator and
+ configure it with this driver. Set the clock using the best means
+ available, like eyeball-and-wristwatch. Then, point all the other
+ machines at this one or use broadcast (not multicast) mode to
+ distribute time.
+
+ Another application for this driver is if you want to use a
+ particular server's clock as the clock of last resort when all
+ other normal synchronization sources have gone away. This is
+ especially useful if that server has an ovenized oscillator. For
+ this you would configure this driver at a higher stratum (say 3 or
+ 4) to prevent the server's stratum from falling below that.
+
+ A third application for this driver is when an external discipline
+ source is available, such as the NIST "lockclock" program, which
+ synchronizes the local clock via a telephone modem and the NIST
+ Automated Computer Time Service (ACTS), or the Digital Time
+ Synchronization Service (DTSS), which runs on DCE machines. In this
+ case the stratum should be set at zero, indicating a bona fide
+ stratum-1 source. Exercise some caution with this, since there is
+ no easy way to telegraph via NTP that something might be wrong in
+ the discipline source itself. In the case of DTSS, the local clock
+ can have a rather large jitter, depending on the interval between
+ corrections and the intrinsic frequency error of the clock
+ oscillator. In extreme cases, this can cause clients to exceed the
+ 128-ms slew window and drop off the NTP subnet.
+
+ In the default mode the behavior of the clock selection algorithm
+ is modified when this driver is in use. The algorithm is designed
+ so that this driver will never be selected unless no other
+ discipline source is available. This can be overridden with the
+ prefer keyword of the server configuration command, in which case
+ only this driver will be selected for synchronization and all other
+ discipline sources will be ignored. This behavior is intended for
+ use when an external discipline source controls the system clock.
+
+ Fudge Factors
+
+ The stratum for this driver LCLSTRATUM is set at 3 by default, but
+ can be changed by the fudge command and/or the xntpdc utility. The
+ reference ID is "LCL" by default, but can be changed using the same
+ mechanisms. *NEVER* configure this driver to operate at a stratum
+ which might possibly disrupt a client with access to a bona fide
+ primary server, unless the local clock oscillator is reliably
+ disciplined by another source. *NEVER NEVER* configure a server
+ which might devolve to an undisciplined local clock to use
+ multicast mode.
+
+ This driver provides a mechanism to trim the local clock in both
+ time and frequency, as well as a way to manipulate the leap bits.
+ The fudge time1 parameter adjusts the time, in seconds, and the
+ fudge time2 parameter adjusts the frequency, in ppm. Both
+ parameters are additive; that is, they add increments in time or
+ frequency to the present values. (Note: The frequency cannot be
+ changed when the kernel modifications are in use - see the
+ README.kern file). The fudge flag1 and fudge flag2 bits set the
+ corresponding leap bits; for example, setting flag1 causes a leap
+ second to be added at the end of the UTC day. These bits are not
+ reset automatically when the leap takes place; they must be turned
+ off manually after the leap event.
+
+Type 2: TRAK 8820 GPS Receiver
+
+ This driver supports the TRAK 8820 GPS Station Clock. The claimed
+ accuracy at the 1-pps output is 200-300 ns relative to the
+ broadcast signal; however, in most cases the actual accuracy is
+ limited by the precision of the timecode and the latencies of the
+ serial interface and operating system.
+
+ For best accuracy, this radio requires the LDISC_ACTS line
+ discipline, which captures a timestamp at the '*' on-time character
+ of the timecode. Using this discipline the jitter is in the order
+ of 1 ms and systematic error about 0.5 ms. If unavailable, the
+ buffer timestamp is used, which is captured at the \r ending the
+ timecode message. This introduces a systematic error of 23
+ character times, or about 24 ms at 9600 bps, together with a jitter
+ well over 8 ms on Sun IPC-class machines.
+
+ Using the menus, the radio should be set for 9600 bps, one stop bit
+ and no parity. It should be set to operate in computer (no echo)
+ mode. The timecode format includes neither the year nor leap-second
+ warning. No provisions are included in this preliminary version of
+ the driver to read and record detailed internal radio status.
+
+ In operation, this driver sends a RQTS\r request to the radio at
+ initialization in order to put it in continuous time output mode.
+ The radio then sends the following message once each second:
+
+ *RQTS U,ddd:hh:mm:ss.0,q<cr><lf>
+
+ on-time = '*'
+ ddd = day of year
+ hh:mm:ss = hours, minutes, seconds
+ q = quality indicator (phase error), 0-6:
+ 0 > 20 us
+ 6 > 10 us
+ 5 > 1 us
+ 4 > 100 ns
+ 3 > 10 ns
+ 2 < 10 ns
+
+ The alarm condition is indicated by '0' at Q, which means the radio
+ has a phase error than 20 usec relative to the broadcast time. The
+ absence of year, DST and leap-second warning in this format is also
+ alarming.
+
+ The continuous time mode is disabled using the RQTX<cr> request,
+ following which the radio sends a RQTX DONE<cr><lf> response. In
+ the normal mode, other control and status requests are effective,
+ including the leap-second status request RQLS<cr>. The radio
+ responds with RQLS yy,mm,dd<cr><lf>, where yy,mm,dd are the year,
+ month and day. Presumably, this gives the epoch of the next leap
+ second, RQLS 00,00,00 if none is specified in the GPS message.
+ Specified in this form, the information is generally useless and is
+ ignored by the driver.
+
+ Fudge Factors
+
+ There are no special fudge factors other than the generic.
+
+Type 3: PSTI/Traconex WWV/WWVH Receiver
+
+ This driver supports the PSTI 1010 and Traconex 1020 WWV/WWVH
+ Receivers. No specific claim of accuracy is made for these
+ receiver, but actual experience suggests that 10 ms would be a
+ conservative assumption.
+
+ The DIPswitches should be set for 9600 bps line speed, 24-hour day-
+ of-year format and UTC time zone. Automatic correction for DST
+ should be disabled. It is very important that the year be set
+ correctly in the DIPswitches; otherwise, the day of year will be
+ incorrect after 28 April of a normal or leap year. The propagation
+ delay DIPswitches should be set according to the distance from the
+ transmitter for both WWV and WWVH, as described in the
+ instructions. While the delay can be set only to within 11 ms, the
+ fudge time1 parameter can be used for vernier corrections.
+
+ Using the poll sequence QTQDQM, the response timecode is in three
+ sections totalling 50 ASCII printing characters, as concatenated by
+ the driver, in the following format:
+
+ ahh:mm:ss.fffs<cr> yy/dd/mm/ddd<cr> frdzycchhSSFTttttuuxx<cr>
+
+ on-time = first <cr>
+ hh:mm:ss.fff = hours, minutes, seconds, milliseconds
+ a = AM/PM indicator (' ' for 24-hour mode)
+ yy = year (from DIPswitches)
+ dd/mm/ddd = day of month, month, day of year
+ s = daylight-saving indicator (' ' for 24-hour mode)
+ f = frequency enable (O = all frequencies enabled)
+ r = baud rate (3 = 1200, 6 = 9600)
+ d = features indicator (@ = month/day display enabled)
+ z = time zone (0 = UTC)
+ y = year (5 = 91)
+ cc = WWV propagation delay (52 = 22 ms)
+ hh = WWVH propagation delay (81 = 33 ms)
+ SS = status (80 or 82 = operating correctly)
+ F = current receive frequency (4 = 15 MHz)
+ T = transmitter (C = WWV, H = WWVH)
+ tttt = time since last update (0000 = minutes)
+ uu = flush character (03 = ^c)
+ xx = 94 (unknown)
+
+ The alarm condition is indicated by other than '8' at A, which
+ occurs during initial synchronization and when received signal is
+ lost for an extended period; unlock condition is indicated by other
+ than "0000" in the tttt subfield at Q.
+
+ Fudge Factors
+
+ There are no special fudge factors other than the generic.
+
+Type 4: Spectracom WWVB Receiver
+
+ This driver supports the Spectracom Model 8170 and Netclock/2 WWVB
+ Synchronized Clock. This clock has proven a reliable source of
+ time, except in some cases of high ambient conductive RF
+ interference. The claimed accuracy of the clock is 100 usec
+ relative to the broadcast signal; however, in most cases the actual
+ accuracy is limited by the precision of the timecode and the
+ latencies of the serial interface and operating system.
+
+ The DIPswitches on this clock should be set to 24-hour display,
+ AUTO DST off, time zone 0 (UTC), data format 0 or 2 (see below) and
+ baud rate 9600. If this clock is to used as the source for the IRIG
+ Audio Decoder (refclock_irig.c in this distribution), set the
+ DIPswitches for AM IRIG output and IRIG format 1 (IRIG B with
+ signature control).
+
+ There are two timecode formats used by these clocks. Format 0,
+ which is available with both the Netclock/2 and 8170, and format 2,
+ which is available only with the Netclock/2 and specially modified
+ 8170.
+
+ Format 0 (22 ASCII printing characters):
+
+ <cr><lf>i ddd hh:mm:ss TZ=zz<cr><lf>
+
+ on-time = first <cr>
+ hh:mm:ss = hours, minutes, seconds
+ i = synchronization flag (' ' = in synch, '?' = out synch)
+
+ The alarm condition is indicated by other than ' ' at A, which
+ occurs during initial synchronization and when received signal is
+ lost for about ten hours.
+
+ Format 2 (24 ASCII printing characters):
+
+ <cr><lf>iqyy ddd hh:mm:ss.fff ld
+
+ on-time = <cr>
+ i = synchronization flag (' ' = in synch, '?' = out synch)
+ q = quality indicator (' ' = locked, 'A'...'D' = unlocked)
+ yy = year (as broadcast)
+ ddd = day of year
+ hh:mm:ss.fff = hours, minutes, seconds, milliseconds
+
+ The alarm condition is indicated by other than ' ' at A, which
+ occurs during initial synchronization and when received signal is
+ lost for about ten hours. The unlock condition is indicated by
+ other than ' ' at Q.
+
+ The Q is normally ' ' when the time error is less than 1 ms and a
+ character in the set 'A'...'D' when the time error is less than 10,
+ 100, 500 and greater than 500 ms respectively. The L is normally '
+ ', but is set to 'L' early in the month of an upcoming UTC leap
+ second and reset to ' ' on the first day of the following month.
+ The D is set to 'S' for standard time 'I' on the day preceding a
+ switch to daylight time, 'D' for daylight time and 'O' on the day
+ preceding a switch to standard time. The start bit of the first
+ <cr> is synchronized to the indicated time as returned.
+
+ This driver does not need to be told which format is in use - it
+ figures out which one from the length of the message. A three-stage
+ median filter is used to reduce jitter and provide a dispersion
+ measure. The driver makes no attempt to correct for the intrinsic
+ jitter of the radio itself, which is a known problem with the older
+ radios.
+
+ Fudge Factors
+
+ This driver can retrieve a table of quality data maintained
+ internally by the Netclock/2 receiver. If flag4 of the fudge
+ configuration command is set to 1, the driver will retrieve this
+ table and write it to the clockstats file on when the first
+ timecode message of a new day is received.
+
+Type 5: TrueTime GPS/GOES Receivers
+
+ This driver supports at least two models of Kinemetrics/TrueTime
+ Timing Receivers, the 468-DC MK III GOES Synchronized Clock and
+ GPS-DC MK III GPS Synchronized Clock and very likely others in the
+ same model family that use the same timecode formats. The clocks
+ are connected via a serial port. Up to four units, with unit
+ numbers in the range 0 through 3, can be configured. The driver
+ assumes the serial port device name is /dev/goes%d (i.e., unit 1 at
+ 127.127.5.1 opens the clock at /dev/goes1) and that the clock is
+ configured for 9600-baud operation.
+
+Type 6: IRIG Audio Decoder
+
+ This driver supports the Inter-Range Instrumentation Group standard
+ time-distribution signal IRIG-B using the audio codec native to the
+ Sun SPARCstation. This signal is generated by several radio clocks,
+ including those made by Austron, TrueTime, Odetics and Spectracom,
+ among others, although it is generally an add-on option. The signal
+ is connected via an attenuator box and cable to the audio codec
+ input on a Sun SPARCstation and requires a specially modified
+ kernel audio driver. Details are in the README.irig file.
+
+ Timing jitter using the decoder and a Sun IPC is in the order of a
+ few microseconds, although the overall timing accuracy is limited
+ by the wander of the CPU oscillator used for timing purposes to a
+ few hundred microseconds. These figures are comparable with what
+ can be achieved using the 1-pps discipline as describe elsewhere in
+ this note.
+
+ Fudge Factors
+
+ There are no special fudge factors other than the generic. The
+ flag3 and flag4 flags are not applicable to this driver.
+
+Type 7: Scratchbuilt CHU Receiver
+
+ This driver supports a shortwave receiver and special modem
+ circuitry described in the ./gadget directory of the xntp3
+ distribution. It requires the chu-clk line discipline or chu_clk
+ STREAMS module described in the ./kernel directory of that
+ distribution. It is connected via a serial port operating at 300
+ baud.
+ Unlike the NIST time services, whose timecode requires quite
+ specialized hardware to interpret, the CHU timecode can be received
+ directly via a serial port after demodulation. While there are
+ currently no known commercial CHU receivers, the hardware required
+ to receive the CHU timecode is fairly simple to build. While it is
+ possible to configure several CHU units simultaneously, this is in
+ general not useful.
+
+ Fudge Factors
+
+ The time1 option can be used to set the CHU propagation delay,
+ compensate for inherent latencies in the serial port hardware and
+ operating system. The default value is 0.0025 seconds, which is
+ about right for Toronto. Values for other locations can be
+ calculated using the propdelay program in the util directory of the
+ xntp3 distribution or equivalent means.
+
+ The time2 option can be used to compensate for inherent latencies
+ in the serial port hardware and operating system. The value, which
+ defaults to zero, is in addition to the propagation delay provided
+ by the time1 option. The default value is 0.0002 seconds, which is
+ about right for typical telephone modem chips.
+
+ The flag1 option can be used to modify the averaging algorithm used
+ to smooth the clock indications. Ordinarily, the algorithm picks
+ the median of a set of samples, which is appropriate under
+ conditions of poor to fair radio propagation conditions. If the
+ clock is located relatively close to the WWV or WWVH transmitters,
+ setting this flag will cause the algorithm to average the set of
+ samples, which can reduce the residual jitter and improve accuracy.
+
+Type 8: Generic Reference Clock Driver
+
+ The timecode of these receivers is sampled via a STREAMS module in
+ the kernel (The STREAMS module has been designed for use with SUN
+ Systems under SunOS 4.1.x. It can be linked directly into the
+ kernel or loaded via the loadable driver mechanism). This STREAMS
+ module can be adapted to be able to convert different time code
+ formats. If the daemon is
+
+ compiled without the STREAM definition synchronization will work
+ without the Sun streams module, though accuracy is significantly
+ degraded.
+
+ The actual receiver status is mapped into various synchronization
+ states generally used by receivers. The STREAMS module is
+ configured to interpret the time codes of DCF U/A 31, PZF535,
+ GPS166, Trimble SV6 GPS, ELV DCF7000, Schmid and low cost receivers
+ (see list below).
+
+ The reference clock support in xntp contains the necessary
+ configuration tables for those receivers. In addition to supporting
+ up to 32 different clock types and 4 devices, the generation a a
+ PPS signal is also provided as an configuration option. The PPS
+ configuration option uses the receiver generated time stamps for
+ feeding the PPS loopfilter control for much finer clock
+ synchronization.
+
+ CAUTION: The PPS configuration option is different from the
+ hardware PPS signal, which is also supported (see below), as it
+ controls the way xntpd is synchronized to the reference clock,
+ while the hardware PPS signal controls the way time offsets are
+ determined.
+
+ The use of the PPS option requires receivers with an accuracy of
+ better than 1ms.
+ Fudge factors
+
+ Only two fudge factors are utilized. The time1 fudge factor defines
+ the phase offset of the synchronization character to the actual
+ time. On the availability of PPS information the time2 fudge factor
+ defines the skew between the PPS time stamp and the receiver
+ timestamp of the PPS signal. This parameter is usually zero, as
+ usually the PPS signal is believed in time and OS delays should be
+ corrected in the machine specific section of the kernel driver.
+ time2 needs only be set when the actual PPS signal is delayed for
+ some reason. The flag0 enables input filtering. This a median
+ filter with continuous sampling. The flag1 selects averaging of the
+ samples remaining after the filtering. Leap secondhandling is
+ controlled with the flag2. When set a leap second will be deleted
+ on receipt of a leap second indication from the receiver. Otherwise
+ the leap second will be added, (which is the default).
+
+ ntpq (8)
+
+ timecode variable
+
+ The ntpq program can read clock variables command list several
+ variables. These hold the following information: refclock_time is
+ the local time with the offset to UTC (format HHMM). The currently
+ active receiver flags are listed in refclock_status. Additional
+ feature flags of the receiver are optionally listed in parentheses.
+ The actual time code is listed in timecode. A qualification of the
+ decoded time code format is following in refclock_format. The last
+ piece of information is the overall running time and the
+ accumulated times for the clock event states in refclock_states.
+ When PPS information is present additional variable are available.
+ refclock_ppstime lists then the PPS timestamp and refclock_ppsskew
+ lists the difference between RS232 derived timestamp and the PPS
+ timestamp.
+
+ Unit encoding
+
+ The unit field <u> encodes the device, clock type and the PPS
+ generation option. There are 4 possible units, which are encoded in
+ the lower two bits of the <u> field. The devices are named
+ /dev/refclock-0 through /dev/refclock-3. Bits 2 thru 6 encode the
+ clock type. The fudge factors of the clock type are taken from a
+ table clockinfo in refclock_parse.c. The generation of PPS
+ information for disciplining the local NTP clock is encoded in bit
+ 7 of <u>.
+
+ Currently, nine clock types (devices /dev/refclock-0 -
+ /dev/refclock-3) are supported.
+
+ 127.127.8.0-3 16
+
+ Meinberg PZF535 receiver (FM demodulation/TCXO / 50us)
+
+ 127.127.8.4-7 16
+
+ Meinberg PZF535 receiver (FM demodulation/OCXO / 50us)
+
+ 127.127.8.8-11 16
+
+ Meinberg DCF U/A 31 receiver (AM demodulation / 4ms)
+
+ 127.127.8.12-15 16
+
+ ELV DCF7000 (sloppy AM demodulation / 50ms)
+
+ 127.127.8.16-19 16
+
+ Walter Schmid DCF receiver Kit (AM demodulation / 1ms)
+
+ 127.127.8.20-23 16
+
+ RAW DCF77 100/200ms pulses (Conrad DCF77 receiver module / 5ms)
+
+ 127.127.8.24-27 16
+
+ RAW DCF77 100/200ms pulses (TimeBrick DCF77 receiver module / 5ms)
+
+ 127.127.8.28-31 16
+
+ Meinberg GPS166 receiver (GPS / <<1us)
+
+ 127.127.8.32-35 16
+
+ Trimble SV6 GPS receiver (GPS / <<1us)
+
+ 127.127.8.40-43 16
+
+ RAW DCF77 100/200ms pulses (Boeder DCF77 receiver / 5ms)
+
+ The reference clock support carefully monitors the state
+ transitions of the receiver. All state changes and exceptional
+ events such as loss of time code transmission are logged via the
+ syslog facility. Every hour a summary of the accumulated times for
+ the clock states is listed via syslog.
+
+ PPS support is only available when the receiver is completely
+ synchronized. The receiver is believed to deliver correct time for
+ an additional period of time after losing synchronizations, unless
+ a disruption in time code transmission is detected (possible power
+ loss). The trust period is dependent on the receiver oscillator and
+ thus a function of clock type. This is one of the parameters in the
+ clockinfo field of the reference clock implementation. This
+ parameter cannot be configured by xntpdc.
+
+ In addition to the PPS loopfilter control a true PPS hardware
+ signal can be applied on Sun Sparc stations via the CPU serial
+ ports on the CD pin. This signal is automatically detected and will
+ be used for offset calculation. The input signal must be the time
+ mark for the following time code. (The edge sensitivity can be
+ selected - look into the appropriate kernel/parsestreams.c for
+ details). Meinberg receivers can be connected by feeding the PPS
+ pulse of the receiver via a 1488 level converter to Pin 8 (CD) of a
+ Sun serial zs\-port.
+
+ There exists a special firmware release for the PZF535 Meinberg
+ receivers. This release (PZFUERL 4.6 (or higher - The UERL is
+ important)) is absolutely recommended for XNTP use, as it provides
+ LEAP warning, time code time zone information and alternate antenna
+ indication. Please check with Meinberg for this firmware release.
+ For the Meinberg GPS166 receiver is also a special firmware release
+ available (Uni-Erlangen). This release must be used for proper
+ operation.
+
+ The raw DCF77 pulses can be fed via a level converter directly into
+ Pin 3 (Rx) of the Sun. The telegrams will be decoded an used for
+ synchronization. AM DCF77 receivers are running as low as $25. The
+ accuracy is dependent on the receiver and is somewhere between 2ms
+ (expensive) to 10ms (cheap). Upon bad signal reception of DCF77
+ synchronizations will cease as no backup oscillator is available as
+ usually found in other reference clock receivers. So it is
+ important to have a good place for the DCF77 antenna. For
+ transmitter shutdowns you are out of luck unless you have other NTP
+ servers with alternate time sources available.
+
+Type 9: Magnavox MX4200 GPS Receiver
+
+ This driver supports the Magnavox MX4200 Navigation Receiver
+ adapted to precision timing applications. This requires an
+ interface box described in the ./ppsclock directory of the xntp3
+ distribution. It is connected via a serial port and requires the
+ ppsclock STREAMS module described in the same directory.
+
+ Fudge Factors
+
+ There are no special fudge factors other than the generic.
+
+Type 10: Austron 2201A GPS Receiver
+
+ This driver supports the Austron 2200A/2201A GPS/LORAN Synchronized
+ Clock and Timing Receiver connected via a serial port. It supports
+ several special features of the clock, including the Input Buffer
+ Module, Output Buffer Module, IRIG-B Interface Module and LORAN
+ Assist Module. It requires the RS232 Serial Interface module for
+ communication with the driver.
+
+ This receiver is capable of a comprehensive and large volume of
+ statistics and operational data. The specific data collection
+ commands and attributes are embedded in the driver source code;
+ however, the collection process can be enabled or disabled using
+ the flag4 flag. If set, collection is enabled; if not, which is the
+ default, it is disabled. A comprehensive suite of data reduction
+ and summary scripts is in the ./scripts/stats directory of the
+ xntp3 distribution.
+
+ To achieve the high accuracy this device provides, it is necessary
+ to use the ppsclock feature of the xntp3 program distribution or,
+ alternatively, to install the kernel modifications described in the
+ README.kern. The clock can be wired to provide time to a single CPU
+ or bussed in parallel to several CPUs, with one CPU controlling the
+ receiver and the others just listening. Fair accuracy can be
+ achieved in the single-CPU configuration without use of the 1-pps
+ signal, but in multiple CPU configurations accuracy is severely
+ degraded without it.
+
+ Fudge Factors
+
+ There are no special fudge factors other than the generic.
+
+Type 11: TrueTime OM-DC OMEGA Receiver
+
+ This driver supports the Kinemetrics/TrueTime OMEGA-DC OMEGA
+ Synchronized Clock connected via a serial port. This clock is
+ sufficiently different from other Kinemetrics/TrueTime models that
+ a separate driver is required. Up to four units, with unit numbers
+ in the range 0 through 3, can be configured. The driver assumes the
+ serial port device name is /dev/omega%d (i.e., unit 1 at
+ 127.127.11.1 opens the clock at /dev/omega1) and that the clock is
+ configured for 9600-baud operation.
+
+ Fudge Factors
+
+ There are no special fudge factors other than the generic.
+
+Type 12: KSI/Odetics TPRO/S IRIG Interface
+
+ This driver supports the KSI/Odetics TPRO and TPRO-SAT IRIG-B
+ Decoder, which is a module connected directly to the SBus of a Sun
+ workstation. The module works with the IRIG-B signal generated by
+ several radio clocks, including those made by Austron, TrueTime,
+ Odetics and Spectracom, among others, although it is generally an
+ add-on option. In the case of the TPRO-SAT, the module is an
+ integral part of a GPS receiver, which serves as the primary timing
+ source.
+
+ Using the TPRO interface as a NTP reference clock provides
+ precision time only to xntpd and its clients. With suitable kernel
+ modifications, it is possible to use the TPRO as the CPU system
+ clock, avoiding errors introduced by the CPU clock oscillator
+ wander. See the README.kernel and kern.c files for further details.
+
+Type 13: Leitch CSD 5300 Master Clock Controller
+
+ Information not available.
+
+Type 14: EES M201 MSF Receiver
+
+ This driver supports the EES M201 MSF receiver connected to a Sun
+ SPARCstation running SunOS 4.x with the "ppsclock" STREAMS module.
+
+ Fudge Factors
+
+ If flag1 is set, then the system clock is assumed to be sloppy
+ (e.g. Sun4 with 20ms clock), so samples are averaged. If flag2 is
+ set, then leaphold is set. If flag3 is set, then the sample
+ information is dumped. If flag4 is set, then the input data is
+ smoothed, and all data points are used.
+
+Type 15: TrueTime GPS/TM-TMD Receiver
+
+ Information not available.
+
+Type 16: Bancomm GPS/IRIG Receiver
+
+ Information not available.
+
+Type 17: Datum Precision Time System
+
+ Information not available.
+
+Type 18: NIST Automated Computer Time Service
+
+ This driver supports the NIST Automated Computer Time Service
+ (ACTS). It periodically dials a prespecified telephone number,
+ receives the NIST timecode data and calculates the local clock
+ correction. It designed primarily for use when neither a radio
+ clock nor connectivity to Internet time servers is available. For
+ the best accuracy, the individual telephone line/modem delay needs
+ to be calibrated using outside sources.
+
+ The ACTS is located at NIST Boulder, CO, telephone 303 494 4774. A
+ toll call from Newark, DE, costs between three and four cents,
+ although it is not clear what carrier and time of day discounts
+ apply. The modem dial string will differ depending on local
+ telephone configuration, etc., and is specified by the phone
+ command in the configuration file. The argument to this command is
+ an AT command for a Hayes compatible modem.
+
+ The accuracy produced by this driver should be in the range of a
+ millisecond or two, but may need correction due to the delay
+ characteristics of the individual modem involved. For undetermined
+ reasons, some modems work with the ACTS echo-delay measurement
+ scheme and some don't. This driver tries to do the best it can with
+ what it gets. Initial experiments with a Practical Peripherals
+ 9600SA modem here in Delaware suggest an accuracy of a millisecond
+ or two can be achieved without the scheme by using a fudge time1
+ value of 65.0 ms. In either case, the dispersion for a single call
+ involving ten samples is about 1.3 ms.
+
+ The driver can operate in either of three modes, as determined by
+ the mode parameter in the server configuration command. In mode 0
+ (automatic) the driver operates continuously at intervals depending
+ on the prediction error, as measured by the driver, usually in the
+ order of several hours. In mode 1 (backup) the driver is enabled in
+ automatic mode only when no other source of synchronization is
+ available and when more than MAXOUTAGE (3600 s) have elapsed since
+ last synchronized by other sources. In mode 2 (manual) the driver
+ operates only when enabled using a fudge flags switch, as described
+ below.
+
+ For reliable call management, this driver requires a 1200-bps modem
+ with a Hayes-compatible command set and control over the modem data
+ terminal ready (DTR) control line. Present restrictions require the
+ use of a POSIX-compatible programming interface, although other
+ interfaces may work as well. The ACTS telephone number and modem
+ setup string are hard-coded in the driver and may require changes
+ for nonstandard modems or special circumstances.
+
+ Fudge Factors
+
+ Ordinarily, the propagation time correction is computed
+ automatically by ACTS and the driver. When this is not possible or
+ erratic due to individual modem characteristics, the fudge flag2
+ switch should be set to disable the ACTS echo-delay scheme. In any
+ case, the fudge time1 parameter can be used to adjust the
+ propagation delay as required.
+
+ The ACTS call interval is determined in one of three ways. In
+ manual mode a call is initiated by setting fudge flag1 using
+ xntpdc, either manually or via a cron job. In automatic mode this
+ flag is set by the peer timer, which is controlled by the sys_poll
+ variable in response to measured errors. In backup mode the driver
+ is ordinarily asleep, but awakes (in automatic mode) if all other
+ synchronization sources are lost. In either automatic or backup
+ modes, the call interval increases as long as the measured errors
+ do not exceed the value of the fudge time2 parameter.
+
+ When the fudge flag1 is set, the ACTS calling program is activated.
+ This program dials each number listed in the phones command of the
+ configuration file in turn. If a call attempt fails, the next
+ number in the list is dialed. The fudge flag1 and counter are reset
+ and the calling program terminated if (a) a valid clock update has
+ been determined, (b) no more numbers remain in the list, (c) a
+ device fault or timeout occurs, or (d) fudge flag1 is reset
+ manually using xntpdc.
+
+ The NIST timecode message is transmitted at 1200 bps in the
+ following format (see the driver source for more information):
+
+ jjjjj yy-mm-dd hh:mm:ss tt l uuu mmmmm UTC(NIST) *
+
+ jjjjj = modified Julian day
+ yy-mm-dd = year, month, day
+ hh:mm:ss = hours, minutes, seconds
+ tt = DST indicator (see driver listing)
+ l = leap-second warning (see driver listing)
+ uuu = DUT1 correction (see driver listing)
+ mmmmm = modem calibration (see driver listing)
+ on-time = '*'
+
+ The timecode message is transmitted continuously after a signon
+ banner, which this driver ignores. The driver also ignores all but
+ the yy-mm-dd, hh:mm:ss and on-time character '*' fields, although
+ it checks the format of all fields of the message. A timestamp is
+ captured at the '*' character, as required by the ACTS
+ specification, and used as the reference time of the timecode. If a
+ message with an on-time character of '#' is received, the driver
+ updates the propagation delay. The driver disconnects when (a) ten
+ valid messages have been received, (b) no message has been received
+ for 15 s, (c) an on-time character of '#' is received. These
+ messages are processed by a trimmed-mean filter to reduce timing
+ noise and then by the usual NTP algorithms to develop the clock
+ correction.
+
+ The behavior of the clock selection algorithm is modified when this
+ driver is in use. The algorithm is designed so that this driver
+ will never be selected unless no other discipline source is
+ available. This can be overridden with the prefer keyword of the
+ server configuration command, in which case only this driver will
+ be selected for synchronization and all other discipline sources
+ will be ignored. Ordinarily, the prefer keyword would be used only
+ in automatic mode ehen primary time is to be obtained via ACTS and
+ backup NTP peers used only when ACTS fails.
+
+ Call Management
+
+ Since ACTS will be a toll call in most areas of the country, it is
+ necessary to carefully manage the calling interval. The ACTS call
+ program is initiated by setting fudge flag1. This flag can be set
+ manually using xntpdc, by a cron job that calls xntpdc, or
+ automatically by the driver itself. The fudge flag1 is reset when
+ the program terminates after a time determination is comlete or
+ when no more numbers remain in the alternate path list, a device
+ fault or timeout has occured, or the fudge flag1 has been reset
+ using xntpdc.
+
+ In automatic and backup modes, the driver determines the call
+ interval using a procedure depending on the measured prediction
+ error and the fudge time2 parameter. If the error exceeds time2 for
+ a number of times depending on the current interval, the interval
+ is decreased, but not less than about 1000 s. If the error is less
+ than time2 for some number of times, the interval is increased, but
+ not more than about 18 h. With the default value of zero for fudge
+ time2, the interval will increase from 1000 s to the 4000-8000-s
+ range, in which the expected accuracy should be in the 1-2-ms
+ range. Setting fudge time2 to a large value, like 0.1 s, may result
+ in errors of that order, but increase the call interval to the
+ maximum. The exact value for each configuration will depend on the
+ modem and operating system involved, so some experimentation may be
+ necessary.
+
+ Manual call attempts can be made at any time by setting fudge flag1
+ using xntpdc. For example, the xntpdc command
+
+ fudge 127.127.18.1 flags 1
+
+ will ask for a key identifier and password and, if authenticated by
+ the server, will set flag1. There may be a short delay until the
+ expiration of the current poll timeout.
+
+ The flag1 can be set from a cron job in the following way.
+ Construct a file with contents
+
+ keyid 11
+ passwd dialup
+ fudge 127.127.18.1 flags 1
+ quit
+
+ Then, run the following program at specified times as required.
+
+ /usr/local/bin/xntpdc <file
+
+Type 19: Heath WWV/WWVH Receiver
+
+ This driver supports the Heath GC-1000 Most Accurate Clock, with
+ RS232C Output Accessory. This is a WWV/WWVH receiver somewhat less
+ robust than other supported receivers. Its claimed accuracy is 100
+ ms when actually synchronized to the broadcast signal, but this
+ doesn't happen even most of the time, due to propagation
+ conditions, ambient noise sources, etc. When not synchronized, the
+ accuracy is at the whim of the internal clock oscillator, which can
+ wander into the sunset without warning. Since the indicated
+ precision is 100 ms, expect a host synchronized only to this thing
+ to wander to and fro, occasionally being rudely stepped when the
+ offset exceeds the default CLOCK_MAX of 128 ms.
+
+ The internal DIPswitches should be set to operate at 1200 baud in
+ MANUAL mode and the current year. The external DIPswitches should
+ be set to GMT and 24-hour format. It is very important that the
+ year be set correctly in the DIPswitches. Otherwise, the day of
+ year will be incorrect after 28 April of a normal or leap year.
+
+ In MANUAL mode the clock responds to a rising edge of the request
+ to send (RTS) modem control line by sending the timecode.
+ Therefore, it is necessary that the operating system implement the
+ TIOCMBIC and TIOCMBIS ioctl system calls and TIOCM_RTS control bit.
+ Present restrictions require the use of a POSIX-compatible
+ programming interface, although other interfaces may work as well.
+
+ The clock message consists of 23 ASCII printing characters in the
+ following format:
+
+ hh:mm:ss.f dd/mm/yr<cr>
+
+ hh:mm:ss.f = hours, minutes, seconds
+ f = deciseconds ('?' when out of spec)
+ dd/mm/yr = day, month, year
+
+ The alarm condition is indicated by '?', rather than a digit, at A.
+ Note that 0?:??:??.? is displayed before synchronization is first
+ established and hh:mm:ss.? once synchronization is established and
+ then lost again for about a day.
+
+ Fudge Factors
+
+ There are no special fudge factors other than the generic. A fudge
+ time1 value of .07 s appears to center the clock offset residuals.
+
+Type 20: Generic NMEA GPS Receiver
+
+ Information not available.
+
+Type 21: Motorola Six Gun GPS Receiver
+
+ Information not available.
+
+Type 22: PPS Clock Discipline
+
+ This driver furnishes an interface for pulse-per-second (PPS)
+ signals produced by a cesium clock, timing receiver or related
+ equipment. It can be used to remove accumulated jitter and retime a
+ secondary server when synchronized to a primary server over a
+ congested, wide-area network and before redistributing the time to
+ local clients. Note that this driver does not control the system
+ clock if the kernel modifications described in the README.kernel
+ file have been installed, but it can be useful as a monitoring
+ tool.
+
+ In order for this driver to work, the local clock must be set to
+ within +-500 ms by another means, such as a radio clock or NTP
+ itself. The 1-pps signal is connected via a serial port and gadget
+ box consisting of a one-shot and RS232 level converter. When
+ operated at 38.4 kbps with a SPARCstation IPC, this arrangement has
+ a worst-case jitter less than 26 us.
+
+ There are three ways in which this driver can be used. The first
+ way uses the LDISC_PPS line discipline and works only for the
+ baseboard serial ports of the Sun SPARCstation. The PPS signal is
+ connected via a gadget box to the carrier detect (CD) line of a
+ serial port and flag3 of the driver configured for that port is
+ set. This causes the ppsclock streams module to be configured for
+ that port and capture a timestamp at the on-time transition of the
+ PPS signal. This driver then reads the timestamp directly by a
+ designated ioctl() system call. This provides the most accurate
+ time and least jitter of any other scheme. There is no need to
+ configure a dedicated device for this purpose, which ordinarily is
+ the device used for the associated radio clock.
+
+ The second way uses the LDISC_CLKPPS line discipline and works for
+ any architecture supporting a serial port. If after a few seconds
+ this driver finds no ppsclock module configured, it attempts to
+ open a serial port device /dev/pps%d, where %d is the unit number,
+ and assign the LDISC_CLKPPS line discipline to it. If the line
+ discipline fails, no harm is done except the accuracy is reduced
+ somewhat. The pulse generator in the gadget box is adjusted to
+ produce a start bit of length 26 us at 38400 bps (or 104 us at 9600
+ bps). Used with the LDISC_CLKPPS line discipline, this produces an
+ ASCII DEL character ('\377') followed by a timestamp at each
+ seconds epoch.
+
+ The third way involves an auxiliary radio clock driver which calls
+ the PPS driver with a timestamp captured by that driver. This use
+ is documented in the source code for the driver(s) involved.
+
+ Fudge Factors
+
+ There are no special fudge factors other than the generic and those
+ explicitly defined above. The fudge time1 parameter can be used to
+ compensate for miscellaneous UART and OS delays. Allow about 247 us
+ for uart delays at 38400 bps and about 1 ms for SunOS streams
+ nonsense.
+
+Appendix B. Mitigation Rules
+
+In order to provide robust backup sources, stratum-1 peers are usually
+operated in a diversity configuration, in which the local server
+operates with a number of remote peers in addition with one or more
+radio clocks operating also as local peers. In these configurations the
+suite of algorithms used in NTP to refine the data from each peer
+separately and to select and weight the data from a number of peers can
+be used with the entire ensemble of remote peers and local radios.
+However, Because of small but significant systematic time offsets
+between the peers, it is in general not possible to achieve the lowest
+jitter and highest stability in these configurations. In addition, there
+are a number of special configurations involving auxiliary radio clock
+outputs, telephone backup services and other special cases, so that a
+set of mitigation rules becomes necessary.
+
+The mitigation rules are based on a set of special characteristics of
+the various reference clock drivers configured on the server. For
+instance, it is possible to designate a peer as "preferred," in which
+case, all other things being equal, this peer will be selected for
+synchronization over all other eligible candidates in the clock
+selection procedures. The precise characterization of the prefer peer is
+described below. In addition, when a pulse-per-second (PPS) signal is
+connected via the PPS Clock Discipline Driver (type 22), the
+corresponding peer is called the PPS peer. The manner in which this peer
+operates is described below. When the Undisciplined Local Clock Driver
+(type 1) is configured in the server, this becomes the local-clock peer.
+When the Automated Computer Time Service Driver (type 18) is configured
+in the server, this becomes the ACTS peer. Both the local-clock and ACTS
+peers operates in the manner described in Appendix A. Finally, where
+support is available, the PPS signal may be processed directly by the
+kernel. In the following this will be called the kernel discipline.
+
+The mitigation rules apply in the clock selection procedures following
+the sanity checks, intersection algorithm and clustering algorithm. The
+survivors at this point represent the subset of all peers which can
+provide the most accurate, stable time. In the general case, with no
+designated prefer peer, PPS peer or local-clock peer, the mitigation
+rules require all survivors be averaged according to a weight depending
+on the reciprocal of the dispersion, as provided in the NTP
+specification.
+
+The mitigation rules establish the choice of system peer, which
+determine the stratum, reference identifier and several other system
+variables which are visible to clients of the local server. In addition,
+they establish which source or combination of sources control the local
+clock. In detail, these rules operate as follows:
+
+1. If there is a prefer peer and it is the local-clock peer or the
+ ACTS peer; or, if there is a prefer peer and the kernel discipline
+ is active, choose the prefer peer as the system peer.
+
+2. If the above is not the case and there is a PPS peer, then choose
+ it as the system peer and its offset as the system clock offset.
+
+3. If the above is not the case and there is a prefer peer (not the
+ local-clock or ACTS peer in this case), then choose it as the
+ system peer and its offset as the system clock offset.
+
+4. If the above is not the case and the peer previously chosen as the
+ system peer is in the surviving population, then choose it as the
+ system peer and average its offset along with the other survivors
+ to determine the system clock offset. This behavior is designed to
+ avoid excess jitter due to "clockhopping," when switching the
+ system peer would not materially improve the time accuracy.
+
+5. If the above is not the case, then choose the first candidate in
+ the list of survivors ranked in order of synchronization distance
+ and average its offset along with the other survivors to determine
+ the system clock offset. This is the default case and the only case
+ considered in the current NTP specification.
+
+The specific interpretation of the prefer peer and PPS peer require some
+explanation, which is given in following sections.
+
+B.1. Using the prefer Keyword
+
+For the reasons stated previously, a scheme has been implemented in NTP
+to provide an intelligent mitigation between various classes of peers,
+one designed to provide the best quality time without compromising the
+normal operation of the NTP algorithms. This scheme in its present form
+is not an integral component of the NTP specification. but is likely to
+be included in future versions of the specification. The scheme is based
+on the "preferred peer," which is specified by including the prefer
+keyword with the associated server or peer command in the configuration
+file. This keyword can be used with any peer or server, but is most
+commonly used with a radio clock server.
+
+The prefer scheme works on the set of peers that have survived the
+sanity and intersection algorithms of the clock select procedures.
+Ordinarily, the members of this set can be considered truechimers and
+any one of them could in principle provide correct time; however, due to
+various error contributions, not all can provide the most stable time.
+The job of the clustering algorithm, which is invoked at this point, is
+to select the best subset of the survivors providing the least variance
+in the combined ensemble compared to the variance in each member of the
+subset. The detailed operation of the clustering algorithm, which are
+given in the specification, are not important here, other than to point
+out it operates in rounds, where a survivor, presumably the worst of the
+lot, is discarded in each round until one of several termination
+conditions is met.
+
+In the prefer scheme the clustering algorithm is modified so that the
+prefer peer is never discarded; on the contrary, its potential removal
+becomes a termination condition. If the original algorithm were about to
+toss out the prefer peer, the algorithm terminates right there. The
+prefer peer can still be discarded by the sanity and intersection
+algorithms, of course, but it will always survive the clustering
+algorithm. A preferred peer retains that designation as long as it
+survives the intersection algorithm. If for some reason the prefer peer
+fails to survive the intersection algorithm, either because it was
+declared a falseticker or became unreachable, it loses that designation
+and the clock selection remitigates as described above.
+
+Along with this behavior, the clock select procedures are modified so
+that the combining algorithm is not used when a prefer (or PPS) peer is
+present. Instead, the offset of the prefer (or PPS) peer is used
+exclusively as the synchronization source. In the usual case involving a
+radio clock and a flock of remote stratum-1 peers, and with the radio
+clock designated a prefer peer, the result is that the high quality
+radio time disciplines the server clock as long as the radio itself
+remains operational and with valid time, as determined from the remote
+peers, sanity algorithm and intersection algorithm.
+
+While the model does not forbid it, it does not seem useful to designate
+more than one peer as preferred, since the additional complexities to
+mitigate among them do not seem justified from on the air experience.
+Note that the prefer peer interacts with the PPS peer discussed in
+Appendix C. It also interacts with the Undisciplined Local Clock Driver
+(type 1), as described in Appendix A. See the main text for the
+mitigation rules applying to the general case.
+
+B.2. Using the Pulse-per-Second (PPS) Signal
+
+Most radio clocks are connected using a serial port operating at speeds
+of 9600 bps or lower. The accuracy using typical timecode formats, where
+the on-time epoch is indicated by a designated ASCII character, like
+carriage-return <cr>, is limited to a millisecond at best and a few
+milliseconds in typical cases. However, some radios produce a precision
+pulse-per-second (PPS) signal which can be used to improve the accuracy
+in typical workstation servers to the order of a few tens of
+microseconds. The details of how this can be accomplished are discussed
+in the README.magic file; the following discusses how this signal is
+implemented and configured in a typical working server.
+
+First, it should be pointed out that the PPS signal is inherently
+ambiguous, in that it provides a precise seconds epoch, but does not
+provide a way to number the seconds. In principle and most commonly,
+another source of synchronization, either the timecode from an
+associated radio clock, or even a set of remote peers, is available to
+perform that function. In all cases a specific, configured peer or
+server must be designated as associated with the PPS signal. This is
+done by including the prefer keyword with the associated server or peer
+command in the configuration file. This PPS signal can be associated in
+this way any peer or server, but is most commonly used with the radio
+clock generating the PPS signal.
+
+The PPS signal is processed by a special PPS Clock Discipline Driver
+(type 22) described in Appendix A. That description specifies the
+hardware configurations in which this signal can be connected to the
+server. This driver replaces the former scheme based on conditional
+compilation and the PPS, CLK and PPSCLK compile-time switches.
+Regardless of method, the driver, like all other drivers, is mitigated
+in the manner described for the prefer peer in Appendix B. However, in
+the case of the PPS peer, the behavior is slightly more complex.
+
+First, in order for the PPS peer to be considered at all, its associated
+prefer peer must have survived the sanity and intersection algorithms
+and have been designated the prefer peer. This insures that the radio
+clock hardware is operating correctly and that, presumably, the PPS
+signal is operating correctly as well. Second, the absolute time offset
+from that peer must be less than CLOCK_MAX, the gradual-adjustment
+range, which is ordinarily set at 128 ms, or well within the +-0.5-s
+unambiguous range of the PPS signal itself. Finally, the time offsets
+generated by the PPS peer are propagated via the clock filter to the
+clock selection procedures just like any other peer. Should these pass
+the sanity and intersection algorithms, they will show up along with the
+offsets of the prefer peer itself. Note that, unlike the prefer peer,
+the PPS peer samples are not protected from discard by the clustering
+algorithm. These complicated procedures insure that the PPS offsets
+developed in this way are the most accurate, reliable available for
+synchronization.
+
+A PPS peer retains that designation as long as it survives the
+intersection algorithm; however, like any other clock driver, it runs a
+reachability algortihm on the PPS signal itself. If for some reason the
+signal fails or displays gross errors, the PPS peer will either become
+unreachable or stray out of the survivor population. In this case the
+clock selection remitigates as described above.
+
+Finally, the mitigation procedures described above for the prefer peer
+are modified so that, if the PPS peer survives the clustering algorithm,
+its offset is mitigated over the prefer peer offset; in other words in
+case of ties, the PPS offset wins. See the main text for the mitigation
+rules applying to the general case.
+
+B.3. Using the Kernel Discipline
+
+Code to implement the kernel discipline is a special feature that can be
+incorporated in the kernel of some workstations as described in the
+README.kernel file. The discipline provides for the control of the local
+clock oscillator time and/or frequency by means of an external PPS
+signal interfaced via a modem control lead. As the PPS signal is derived
+from external equipment, cables, etc., which sometimes fail, a good deal
+of error checking is done in the kernel to detect signal failure and
+excessive noise.
+
+In order to operate, the kernel discipline must be enabled and the
+signal must be present and within nominal jitter and wander error
+tolerances. In the NTP daemon the kernel is enabled only when the prefer
+peer is among the survivors of the clustering algorithm, as described
+above. Then, the PPS peer is designated the prefer peer as long as the
+PPS signal is present and operating within tolerances. Under these
+conditions the kernel disregards updates produced by the NTP daemon and
+uses its internal PPS source instead. The kernel maintains a watchdog
+timer for the PPS signal; if the signal has not been heard or is out of
+tolerance for more than some interval, currently two minutes, the kernel
+discipline is declared inoperable and operation continues as if it were
+not present.
+Appendix C. NTP Local Clock Discipline
+
+Implementation of the ACTS driver caused somewhat of a shakeup in the
+NTP local clock model and implementation. The model described in the
+specification RFC-1305 is based on a phase-lock loop (PLL) design, which
+is optimum or near optimum for the update intervals used for NTP peers
+and radio clocks, ordinarily in the range 64-1024 s. However, the ACTS
+driver must operate with update intervals in the range well above 1024
+s, where the performance of the PLL model deteriorates. As suggested by
+Judah Levine of NIST and used in his "lockclock" algorithm, a hybrid
+frequency-lock loop (FLL) gives better performance at the longer update
+intervals up to a maximum depending on the acceptable error bound.
+
+In a series of experiments and simulations, it was verified that the PLL
+model provides better performance in the regime less than about 1000 s,
+while the FLL model provides better performance above that. The
+parameters of each model were optimized by simulation for the lowest
+time and frequency error using data collected on an undisciplined
+computer clock oscillator over a period of about two weeks. The PLL/FLL
+hybrid loop has been implemented in NTP, along with certain other
+refinements described below. While it was designed primarily with ACTS
+in mind, it can be used with any NTP peer or radio clock, should that
+prove useful.
+
+To take advantage of this feature for other than the ACTS driver, where
+it is automatic, note that the default minimum poll interval is 64 s and
+default maximum poll interval 1024 s (for the ACTS driver the default
+minimum is 1024 s and default maximum 16384 s). However, using the
+minpoll and/or maxpoll parameters of the server or peer commands in the
+configuration file, it is possible to set the minimum poll interval as
+low as 16 s and the maximum poll interval as high as 16384 s. Poll
+intervals less than 64 s are useful if an exceptionally quick lock is
+required, like in real-time or portable systems. Poll intervals above
+1024 s, other than ACTS, may be useful to reduce traffic in some
+situations, such as when charges are made on a per-packet basis.
+
+Another modification to the stock NTP local clock discipline is to avoid
+errors due to old data. From a study of the stability characteristics of
+typical computer clock oscillators using both experiment and simulation,
+it was determined that data used to discipline the PLL are not generally
+useful if older than about 1000 s. This corresponds roughly to the knee
+in the Allan variance characteristic measured for a typical workstation
+oscillator. The NTP clock filter algorithm was modified to adjust the
+effective length of the shift register so that samples older than about
+1000 s are not used to determine the filtered offset, delay and
+dispersion values. This design has the useful byproduct that the time to
+acquire lock when first coming up and to declare unreachability is
+independent of the poll interval.
+
+A problem which has recurred on every occasion a leap second has been
+inserted in the UTC timescale is that not all radio clocks detect and
+implement the leap event. As a result, some radios sail right through
+the leap, become confused for periods up to 15 minutes, then reacquire
+lock. In order to cope with this, as well as other occasions where
+atypically large offsets occur, the NTP clock discipline has been
+modified to disregard offsets over 128 ms, unless (a) first coming up,
+(b) first returning to service after a period when unsynchronized, or
+(c) an interval of about 15 minutes has elapsed since the last update
+less than 128 ms was received. In addition, the discipline has been
+modified so that, if the first offset received after coming up is less
+than 128 ms, the local clock is immediately reset to that offset without
+affecting the PLL variables.
+
+It has been the experience of some users that, when first installed in a
+system, the NTP clock discipline fails to reliably lock to other peers
+and servers as configured. The indications are that the daemon locks for
+some period of time, but is unable to stabilize the frequency estimate.
+As the result, the time offsets eventually climb above 128 ms and the
+discipline unlocks again. After the 15-minute timeout, the daemon locks
+again and the cycle repeats. The problem here is that the intrinsic
+frequency error of the local clock exceeds the design capture range of
+the PLL, 100 ppm. This particular limit was selected as a compromise
+between useful maximum error indications and the tolerances found in
+typical computer clock oscillators.
+
+In spite of the tolerance assumed in the NTP specification of 100 ppm,
+the NTP daemon for Unix can operate with an intrinsic frequency error of
+over 380 ppm, depending on the values of tick and tickadj selected by
+the tickadj program. However, with errors that large, the PLL will not
+reliably lock, and the behavior noted above can occur. Formerly, the
+only remedial in cases where this happens waas a somewhat painful manual
+process where the nominal oscillator frequency is measured by some other
+means, such as eyeball-and-wristwatch, and a specific drift file
+(ntp.drift) crafted.
+
+In order to avoid the above problem, the NTP clock discipline has been
+modified to measure the frequency during periods when not locked to
+another server or radio clock. Such periods occur when the time offset
+wanders through and beyond the 128-ms window as described above. When
+synchronization is reestablished, the working frequency used by NTP is
+initialized with the measured value. Since a precise frequency
+determination is not always possible under these chaotic conditions, it
+may take more than one cycle of this type to get the residual error
+below 100 ppm and reliable lock established.
+
+David L. Mills <mills@udel.edu>
+Electrical Engineering Department
+University of Delaware
+Newark, DE 19716
+302 831 8247 fax 302 831 4316
+
+3 July 1994
diff --git a/usr.sbin/xntpd/doc/UofT b/usr.sbin/xntpd/doc/UofT
new file mode 100644
index 0000000..54420d5
--- /dev/null
+++ b/usr.sbin/xntpd/doc/UofT
@@ -0,0 +1,146 @@
+This file is the original README, and is a little out of date. It
+is also very specific to UofT, since there was a time when the daemon
+was only run here.
+
+To run this:
+
+(1) Fix your kernel's value of tickadj. Tickadj sets both the
+ precision with which time slews can be performed and the amount
+ of slew you can do in a given interval. Xntpd operates by making
+ a bunch of little adjustments. Make tickadj too large (the default
+ value almost always is) and xntpd will perform poorly since the
+ slews will disappear in the roundoff. Make tickadj too small
+ and large slews won't complete before the next adjustment is
+ ready.
+
+ To determine a good value of tickadj to use, first determine your
+ kernel's value of hz (50 on a Sun 3, 100 on Sun 4's and vaxes).
+ Divide that number into 500 (i.e. compute 500/hz) and use an
+ integer near there as tickadj (say, 10 on Sun 3's, 5 on Sun 4's
+ and vaxes). Then adb your kernel and write the new value. You
+ should probably do both the running kernel and the disk image.
+
+ If your machine doesn't come with adb, or if the kernel is of a
+ non-Berkeley flavour, take a look at the util directory, particularly
+ util/tickadj.
+
+(2) Edit the Config file in this directory. You *must* tell it whether
+ your machine uses big endian or little endian byte order. Also,
+ Suns running SunOS 3.x require special consideration, as well as Vaxes
+ running Ultrix 2.0 and compilers which don't understand `signed char'
+ declarations. When you've got all this worked out, type `make makefiles'
+ to distribute configuration information to Makefiles for individual
+ programs, followed by `make' to compile everything.
+
+(2a) Note that, among other things, two programs were made in the authstuff
+ directory, authcert and authspeed. The last two are utilities for
+ checking the authentication code. Type `authcert < certdata'. If
+ this provokes a massive failure you probably got the byte order wrong
+ in the Config file. Type `authspeed -n 10000 auth.samplekeys', or
+ something, a couple of times to get a value of authdelay to stick in
+ the configuration file. The numbers for machines I've tried look like:
+
+ uVax II 0.001450
+ Sun 3/180 0.000620
+ uVax III 0.000515
+ Sun 3/60 0.000455
+ IBM RT Mdl 125 0.000323
+ Sun 3/280 0.000302
+ Sun 4/280 0.000110
+ MIPS M/1000 0.000100
+
+(3) Typing `make install' will nstall xntpd, xntpdc, ntpdate and ntpq. Watch
+ the install location in the Config file.
+
+(4) If you will be running xntpd (see 4a below for the alternative),
+ configure it (configuration is necessary for all machines now, though
+ this restriction will go away when I get broadcast time fully tested).
+ xntpd reads its configuration from /etc/ntp.conf (by default) and
+ you must tell it which machines it is to get its time from in
+ here.
+
+ Note that NTP operates in a hierarchy. Machines with radio clocks
+ (which are stratum 1 servers) are at the top of the heap, in that
+ all time originates with them. The situation with servers locally
+ is in a state of flux. We currently have one semi-reliable stratum 1
+ server on campus (suzuki.ccie), and maintain three other stratum 2
+ servers which (gently) access other people's off-campus stratum 1
+ servers. All of these machines are lightly loaded and have good
+ quality clocks, and so will probably do until we get some more stratum 1
+ weight.
+
+ Thus you are probably faced with choosing whether your hosts should
+ be stratum 2 or stratum 3 (or stratum 3 or 4 when suzuki's clock is down).
+ The rule of thumb is to make your best clocks and/or your file servers
+ stratum 2 (or 3) by peering them with the four campus servers, and make
+ lesser clocks and clients stratum 3 (or 4) by peering them with near
+ by servers which are synchonized to the campus servers. The second rule
+ of thumb is that more servers are better. It is quite possible to
+ synchronize with just a single server, but if you do your xtnpd daemon
+ won't have any cross checks to tell it when the server has gone
+ wonky. 3 or 4 lower stratum peers is about right. Note that while
+ you can also peer with same-stratum peers, you shouldn't do this
+ unless the same-stratum peer is exchanging time with a lower stratum
+ peer you don't talk to directly.
+
+ Anyway, for your stratum 2 servers you can probably use ntp.conf
+ from the conf directory directly. You will have to handcraft the
+ peer assocations for your stratum 3 servers.
+
+ Oh, and a note about the drift file (see ntp.conf). One of the
+ things xntpd does is accumulate a correction for the frequency of
+ the crystal in your computer. It usually takes a day or so of
+ running to figure this out, after which the value will usually remain
+ pretty stable, especially if the computer is in a machine room. The
+ value is printed in your syslog file (once a minute, currently, though
+ this will change), and can be obtained from the daemon using xntpdc.
+
+ To avoid having to wait a day after restarts before the computer
+ synchronizes really well, xntpd will optionally write its current
+ value of the frequency correction into a file, once an hour. When
+ it is killed and restarted, xntpd reinitializes itself to this
+ value on start up. This is an advantageous feature, so a driftfile
+ line should always be included in the configuration file.
+
+(4a) Xntpd is a daemon. It will keep your time exquisitely precise under
+ normal conditions (it is quite capable of keeping a good clock within
+ a millisecond of a good server. Our servers aren't normally this
+ good, yet, but may become so when we get a few more stable local
+ stratum 1 peers). Even when cut off entirely from its servers xntpd
+ will prevent your clock from drifting seriously by continuing to apply
+ its accumulated frequency correction. The cost of this is that xntpd
+ will permanently consume memory while it is running, and real memory
+ at that since xntpd is unlikely to ever swap out. This cost is
+ currently over 100 kb.
+
+ If you aren't too worried about millisecond timing and feel religious
+ about keeping memory consumption at a minimum (perhaps on memory-poor
+ workstations), a passable alternative might be to run ntpdate instead.
+ Ntpdate is the NTP equivalent of rdate, a one shot date setting
+ program, and implements the same multiple sample/multiple server
+ filter algorithms as xntpd. Ntpdate was explicitly designed to be
+ run repeatly from cron, though it also makes a good boot time date
+ setter. Running ntpdate from cron on an hourly basis will keep all
+ but seriously broken clocks within 100 ms of on-time, and for most
+ clocks will probably do better than 50 ms. If this is an attractive
+ alternative see the manual page. You should choose ntpdate's servers
+ as you would the peer associations for a stratum 3 xntpd server.
+
+(5) Once everything is configured, start the daemon(s). ntpq can be
+ used to see what xntpd is doing. It runs both interactive and from
+ the command line, type ? to see the interactive commands and ? command
+ to see what a command does. The `peers' command is a good one. ntpq
+ can also be used to see what other peoples' servers are doing, in
+ particular the fuzzball primary servers.
+
+(6) If you want to use the authentication facility (this might be useful
+ if, for example, you were running Kerberos since this prevents people
+ from setting your time back and doing replay attacks on the server),
+ you might find a couple of useful programs in the auth_stuff directory.
+ mkrandkeys will generate some very random keys to use. keyparity
+ generates odd parity bits for keys (needed for the key file) and will
+ convert between key formats.
+
+All bug reports gratefully received.
+
+Dennis
diff --git a/usr.sbin/xntpd/doc/acts.c b/usr.sbin/xntpd/doc/acts.c
new file mode 100644
index 0000000..7d3733b
--- /dev/null
+++ b/usr.sbin/xntpd/doc/acts.c
@@ -0,0 +1,878 @@
+/*
+ * refclock_acts - clock driver for the NIST Automated Computer Time
+ * Service aka Amalgamated Containerized Trash Service (ACTS)
+ */
+#if defined(REFCLOCK) && defined(ACTS)
+
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/time.h>
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_unixtime.h"
+#include "ntp_refclock.h"
+#include "ntp_stdlib.h"
+
+/*
+ * This driver supports the NIST Automated Computer Time Service (ACTS).
+ * It periodically dials a prespecified telephone number, receives the
+ * NIST timecode data and calculates the local clock correction. It is
+ * designed primarily for use as a backup when neither a radio clock nor
+ * connectivity to Internet time servers is available. For the best
+ * accuracy, the individual telephone line/modem delay needs to be
+ * calibrated using outside sources.
+ *
+ * The ACTS is located at NIST Boulder, CO, telephone 303 494 4774. A
+ * toll call from a residence telephone in Newark, DE, costs between 14
+ * and 27 cents, depending on time of day, and from a campus telephone
+ * between 3 and 4 cents, although it is not clear what carrier and time
+ * of day discounts apply in this case. The modem dial string will
+ * differ depending on local telephone configuration, etc., and is
+ * specified by the phone command in the configuration file. The
+ * argument to this command is an AT command for a Hayes compatible
+ * modem.
+ *
+ * The accuracy produced by this driver should be in the range of a
+ * millisecond or two, but may need correction due to the delay
+ * characteristics of the individual modem involved. For undetermined
+ * reasons, some modems work with the ACTS echo-delay measurement scheme
+ * and some don't. This driver tries to do the best it can with what it
+ * gets. Initial experiments with a Practical Peripherals 9600SA modem
+ * here in Delaware suggest an accuracy of a millisecond or two can be
+ * achieved without the scheme by using a fudge time1 value of 65.0 ms.
+ * In either case, the dispersion for a single call involving ten
+ * samples is about 1.3 ms.
+ *
+ * The driver can operate in either of two modes, as determined by the
+ * mode parameter in the server configuration command. In mode 0 the
+ * driver operates continuously at intervals determined by the fudge
+ * time1 parameter, as described above. In mode 1 the driver is enabled
+ * only when no other sources of synchronization are available and when
+ * we have gone more than MAXOUTAGE (3600 s) since last synchronized by
+ * other sources of synchronization.
+ *
+ * For reliable call management, this driver requires a 1200-bps modem
+ * with a Hayes-compatible command set and control over the modem data
+ * terminal ready (DTR) control line. Present restrictions require the
+ * use of a POSIX-compatible programming interface, although other
+ * interfaces may work as well. The modem setup string is hard-coded in
+ * the driver and may require changes for nonstandard modems or special
+ * circumstances.
+ *
+ * Further information can be found in the README.refclock file in the
+ * xntp3 distribution.
+ *
+ * Fudge Factors
+ *
+ * Ordinarily, the propagation time correction is computed automatically
+ * by ACTS and the driver. When this is not possible or erratic due to
+ * individual modem characteristics, the fudge flag2 switch should be
+ * set to disable the ACTS echo-delay scheme. In any case, the fudge
+ * time1 parameter can be used to adjust the propagation delay as
+ * required.
+ *
+ * The ACTS call interval is determined in one of three ways. In MANUAL
+ * mode a call is initiated by setting fudge flag1 using xntpdc, either
+ * manually or via a cron job. In AUTO mode this flag is set by the peer
+ * timer, which is controlled by the sys_poll variable in response to
+ * measured errors. In BACKUP mode the driver is ordinarily asleep, but
+ * awakes (in AUTO mode) if all other synchronization sources are lost.
+ * In either AUTO or BACKUP modes, the call interval increases as long
+ * as the measured errors do not exceed the value of the fudge time2
+ * parameter.
+ *
+ * When the fudge flag1 is set, the ACTS calling program is activated.
+ * This program dials each number listed in the phones command of the
+ * configuration file in turn. If a call attempt fails, the next number
+ * in the list is dialed. The fudge flag1 and counter are reset and the
+ * calling program terminated if (a) a valid clock update has been
+ * determined, (b) no more numbers remain in the list, (c) a device
+ * fault or timeout occurs or (d) fudge flag1 is reset manually using
+ * xntpdc.
+ */
+
+/*
+ * DESCRIPTION OF THE AUTOMATED COMPUTER TELEPHONE SERVICE (ACTS)
+ * (reformatted from ACTS on-line computer help information)
+ *
+ * The following is transmitted (at 1200 baud) following completion of
+ * the telephone connection.
+ *
+ * National Institute of Standards and Technology
+ * Telephone Time Service, Generator 3B
+ * Enter question mark "?" for HELP
+ * D L D
+ * MJD YR MO DA H M S ST S UT1 msADV <OTM>
+ * 47999 90-04-18 21:39:15 50 0 +.1 045.0 UTC(NIST) *
+ * 47999 90-04-18 21:39:16 50 0 +.1 045.0 UTC(NIST) *
+ * 47999 90-04-18 21:39:17 50 0 +.1 045.0 UTC(NIST) *
+ * 47999 90-04-18 21:39:18 50 0 +.1 045.0 UTC(NIST) *
+ * 47999 90-04-18 21:39:19 50 0 +.1 037.6 UTC(NIST) #
+ * 47999 90-04-18 21:39:20 50 0 +.1 037.6 UTC(NIST) #
+ * etc..etc...etc.......
+ *
+ * UTC = Universal Time Coordinated, the official world time referred to
+ * the zero meridian.
+ *
+ * DST Daylight savings time characters, valid for the continental
+ * U.S., are set as follows:
+ *
+ * 00 We are on standard time (ST).
+ * 01-49 Now on DST, go to ST when your local time is 2:00 am and
+ * the count is 01. The count is decremented daily at 00
+ * (UTC).
+ * 50 We are on DST.
+ * 51-99 Now on ST, go to DST when your local time is 2:00 am and
+ * the count is 51. The count is decremented daily at 00
+ * (UTC).
+ *
+ * The two DST characters provide up to 48 days advance notice of a
+ * change in time. The count remains at 00 or 50 at other times.
+ *
+ * LS Leap second flag is set to "1" to indicate that a leap second is
+ * to be added as 23:59:60 (UTC) on the last day of the current UTC
+ * month. The LS flag will be reset to "0" starting with 23:59:60
+ * (UTC). The flag will remain on for the entire month before the
+ * second is added. Leap seconds are added as needed at the end of
+ * any month. Usually June and/or December are chosen.
+ *
+ * The leap second flag will be set to a "2" to indicate that a
+ * leap second is to be deleted at 23:59:58--00:00:00 on the last
+ * day of the current month. (This latter provision is included per
+ * international recommendation, however it is not likely to be
+ * required in the near future.)
+ *
+ * DUT1 Approximate difference between earth rotation time (UT1) and
+ * UTC, in steps of 0.1 second: DUT1 = UT1 - UTC.
+ *
+ * MJD Modified Julian Date, often used to tag certain scientific data.
+ *
+ * The full time format is sent at 1200 baud, 8 bit, 1 stop, no parity.
+ * The format at 300 Baud is also 8 bit, 1 stop, no parity. At 300 Baud
+ * the MJD and DUT1 values are deleted and the time is transmitted only
+ * on even seconds.
+ *
+ * Maximum on line time will be 56 seconds. If all lines are busy at any
+ * time, the oldest call will be terminated if it has been on line more
+ * than 28 seconds, otherwise, the call that first reaches 28 seconds
+ * will be terminated.
+ *
+ * Current time is valid at the "on-time" marker (OTM), either "*" or
+ * "#". The nominal on-time marker (*) will be transmitted 45 ms early
+ * to account for the 8 ms required to send 1 character at 1200 Baud,
+ * plus an additional 7 ms for delay from NIST to the user, and
+ * approximately 30 ms "scrambler" delay inherent in 1200 Baud modems.
+ * If the caller echoes all characters, NIST will measure the round trip
+ * delay and advance the on-time marker so that the midpoint of the stop
+ * bit arrives at the user on time. The amount of msADV will reflect the
+ * actual required advance in milliseconds and the OTM will be a "#".
+ *
+ * (The NIST system requires 4 or 5 consecutive delay measurements which
+ * are consistent before switching from "*" to "#". If the user has a
+ * 1200 Baud modem with the same internal delay as that used by NIST,
+ * then the "#" OTM should arrive at the user within +-2 ms of the
+ * correct time.
+ *
+ * However, NIST has studied different brands of 1200 Baud modems and
+ * found internal delays from 24 ms to 40 ms and offsets of the "#" OTM
+ * of +-10 ms. For many computer users, +-10 ms accuracy should be more
+ * than adequate since many computer internal clocks can only be set
+ * with granularity of 20 to 50 ms. In any case, the repeatability of
+ * the offset for the "#" OTM should be within +-2 ms, if the dial-up
+ * path is reciprocal and the user doesn't change the brand or model of
+ * modem used.
+ *
+ * This should be true even if the dial-up path on one day is a land-
+ * line of less than 40 ms (one way) and on the next day is a satellite
+ * link of 260 to 300 ms. In the rare event that the path is one way by
+ * satellite and the other way by land line with a round trip
+ * measurement in the range of 90 to 260 ms, the OTM will remain a "*"
+ * indicating 45 ms advance.
+ *
+ * For user comments write:
+ * NIST-ACTS
+ * Time and Frequency Division
+ * Mail Stop 847
+ * 325 Broadway
+ * Boulder, CO 80303
+ *
+ * Software for setting (PC)DOS compatable machines is available on a
+ * 360-kbyte diskette for $35.00 from: NIST Office of Standard Reference
+ * Materials B311-Chemistry Bldg, NIST, Gaithersburg, MD, 20899, (301)
+ * 975-6776
+ */
+
+/*
+ * Interface definitions
+ */
+#define DEVICE "/dev/acts%d" /* device name and unit */
+#define SPEED232 B1200 /* uart speed (1200 cowardly baud) */
+#define PRECISION (-10) /* precision assumed (about 1 ms) */
+#define REFID "ACTS" /* reference ID */
+#define DESCRIPTION "NIST Automated Computer Time Service" /* WRU */
+
+#define MODE_AUTO 0 /* automatic mode */
+#define MODE_BACKUP 1 /* backup mode */
+#define MODE_MANUAL 2 /* manual mode */
+
+#define NSAMPLES 3 /* stages of median filter */
+#define MSGCNT 10 /* we need this many ACTS messages */
+#define SMAX 80 /* max token string length */
+#define LENCODE 50 /* length of valid timecode string */
+#define ACTS_MINPOLL 10 /* log2 min poll interval (1024 s) */
+#define ACTS_MAXPOLL 14 /* log2 max poll interval (16384 s) */
+#define MAXOUTAGE 3600 /* max outage before ACTS kicks in (s) */
+
+/*
+ * Modem control strings. These may have to be changed for some modems.
+ *
+ * AT command prefix
+ * B1 initiate call negotiation using Bell 212A
+ * &C1 enable carrier detect
+ * &D2 hang up and return to command mode on DTR transition
+ * E0 modem command echo disabled
+ * l1 set modem speaker volume to low level
+ * M1 speaker enabled untill carrier detect
+ * Q0 return result codes
+ * V1 return result codes as English words
+ */
+#define MODEM_SETUP "ATB1&C1&D2E0L1M1Q0V1" /* modem setup */
+#define MODEM_HANGUP "ATH" /* modem disconnect */
+
+/*
+ * Timeouts
+ */
+#define IDLE 60 /* idle timeout (s) */
+#define WAIT 2 /* wait timeout (s) */
+#define ANSWER 30 /* answer timeout (s) */
+#define CONNECT 10 /* connect timeout (s) */
+#define TIMECODE 15 /* timecode timeout (s) */
+
+/*
+ * Imported from ntp_timer module
+ */
+extern u_long current_time; /* current time (s) */
+extern u_long last_time; /* last clock update time (s) */
+extern struct event timerqueue[]; /* inner space */
+
+/*
+ * Imported from ntpd module
+ */
+extern int debug; /* global debug flag */
+
+/*
+ * Imported from ntp_config module
+ */
+extern char sys_phone[][MAXDIAL]; /* modem dial strings */
+
+/*
+ * Imported from ntp_proto module
+ */
+extern struct peer *sys_peer; /* who is running the show */
+extern u_char sys_poll; /* log2 of system poll interval */
+extern struct peer *sys_peer; /* system peer structure pointer */
+
+/*
+ * Tables to compute the ddd of year form icky dd/mm timecode. Viva la
+ * leap.
+ */
+static day1tab[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
+static day2tab[] = {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
+
+/*
+ * Unit control structure
+ */
+struct actsunit {
+ struct event timer; /* timeout timer */
+ int pollcnt; /* poll message counter */
+
+ int state; /* the first one was Delaware */
+ int run; /* call program run switch */
+ int msgcnt; /* count of ACTS messages received */
+ long redial; /* interval to next automatic call */
+ double msADV; /* millisecond advance of last message */
+};
+
+/*
+ * Function prototypes
+ */
+static int acts_start P((int, struct peer *));
+static void acts_shutdown P((int, struct peer *));
+static void acts_receive P((struct recvbuf *));
+static void acts_poll P((int, struct peer *));
+static void acts_timeout P((struct peer *));
+static void acts_disc P((struct peer *));
+static int acts_write P((struct peer *, char *));
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_acts = {
+ acts_start, /* start up driver */
+ acts_shutdown, /* shut down driver */
+ acts_poll, /* transmit poll message */
+ noentry, /* not used (old acts_control) */
+ noentry, /* not used (old acts_init) */
+ noentry, /* not used (old acts_buginfo) */
+ NOFLAGS /* not used */
+};
+
+
+/*
+ * acts_start - open the devices and initialize data for processing
+ */
+static int
+acts_start(unit, peer)
+ int unit;
+ struct peer *peer;
+{
+ register struct actsunit *up;
+ struct refclockproc *pp;
+ int fd;
+ char device[20];
+ int dtr = TIOCM_DTR;
+
+ /*
+ * Open serial port. Use ACTS line discipline, if available. It
+ * pumps a timestamp into the data stream at every on-time
+ * character '*' found. Note: the port must have modem control
+ * or deep pockets for the phone bill. HP-UX 9.03 users should
+ * have very deep pockets.
+ */
+ (void)sprintf(device, DEVICE, unit);
+ if (!(fd = refclock_open(device, SPEED232, LDISC_ACTS)))
+ return (0);
+ if (ioctl(fd, TIOCMBIC, (char *)&dtr) < 0) {
+ syslog(LOG_ERR, "clock %s ACTS no modem control",
+ ntoa(&peer->srcadr));
+ return (0);
+ }
+
+ /*
+ * Allocate and initialize unit structure
+ */
+ if (!(up = (struct actsunit *)
+ emalloc(sizeof(struct actsunit)))) {
+ (void) close(fd);
+ return (0);
+ }
+ memset((char *)up, 0, sizeof(struct actsunit));
+ pp = peer->procptr;
+ pp->io.clock_recv = acts_receive;
+ pp->io.srcclock = (caddr_t)peer;
+ pp->io.datalen = 0;
+ pp->io.fd = fd;
+ if (!io_addclock(&pp->io)) {
+ (void) close(fd);
+ free(up);
+ return (0);
+ }
+ pp->unitptr = (caddr_t)up;
+
+ /*
+ * Initialize miscellaneous variables
+ */
+ peer->precision = PRECISION;
+ pp->clockdesc = DESCRIPTION;
+ memcpy((char *)&pp->refid, REFID, 4);
+ peer->minpoll = ACTS_MINPOLL;
+ peer->maxpoll = ACTS_MAXPOLL;
+
+ /*
+ * Initialize modem and kill DTR. We skedaddle if this comes
+ * bum.
+ */
+ if (!acts_write(peer, MODEM_SETUP)) {
+ (void) close(fd);
+ free(up);
+ return (0);
+ }
+
+ /*
+ * Set up the driver timeout
+ */
+ up->timer.peer = (struct peer *)peer;
+ up->timer.event_handler = acts_timeout;
+ up->timer.event_time = current_time + WAIT;
+ TIMER_INSERT(timerqueue, &up->timer);
+ return (1);
+}
+
+
+/*
+ * acts_shutdown - shut down the clock
+ */
+static void
+acts_shutdown(unit, peer)
+ int unit;
+ struct peer *peer;
+{
+ register struct actsunit *up;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ up = (struct actsunit *)pp->unitptr;
+ TIMER_DEQUEUE(&up->timer);
+ io_closeclock(&pp->io);
+ free(up);
+}
+
+
+/*
+ * acts_receive - receive data from the serial interface
+ */
+static void
+acts_receive(rbufp)
+ struct recvbuf *rbufp;
+{
+ register struct actsunit *up;
+ struct refclockproc *pp;
+ struct peer *peer;
+ char str[SMAX];
+ int i;
+ l_fp tstmp;
+ u_fp disp;
+ char hangup = '%'; /* ACTS hangup */
+ int day; /* day of the month */
+ int month; /* month of the year */
+ u_long mjd; /* Modified Julian Day */
+ u_int dst; /* daylight/standard time indicator */
+ u_int leap; /* leap-second indicator */
+ double dut1; /* DUT adjustment */
+ double msADV; /* ACTS transmit advance (ms) */
+ char utc[10]; /* this is NIST and you're not */
+ char flag; /* calibration flag */
+
+ /*
+ * Initialize pointers and read the timecode and timestamp. If
+ * the OK modem status code, leave it where folks can find it.
+ */
+ peer = (struct peer *)rbufp->recv_srcclock;
+ pp = peer->procptr;
+ up = (struct actsunit *)pp->unitptr;
+ pp->lencode = refclock_gtlin(rbufp, pp->lastcode, BMAX,
+ &pp->lastrec);
+ if (pp->lencode == 0) {
+ if (strcmp(pp->lastcode, "OK") == 0)
+ pp->lencode = 2;
+ return;
+ }
+#ifdef DEBUG
+ if (debug)
+ printf("acts: timecode %d %s\n", pp->lencode,
+ pp->lastcode);
+#endif
+
+ switch (up->state) {
+
+ case 0:
+
+ /*
+ * State 0. We are not expecting anything. Probably
+ * modem disconnect noise. Go back to sleep.
+ */
+ return;
+
+ case 1:
+
+ /*
+ * State 1. We are waiting for the call to be answered.
+ * All we care about here is CONNECT as the first token
+ * in the string. If the modem signals BUSY, ERROR, NO
+ * ANSWER, NO CARRIER or NO DIALTONE, we immediately
+ * hang up the phone. If CONNECT doesn't happen after
+ * ANSWER seconds, hang up the phone. If everything is
+ * okay, start the connect timeout and slide into state
+ * 2.
+ */
+ (void)strncpy(str, strtok(pp->lastcode, " "), SMAX);
+ if (strcmp(str, "BUSY") == 0 || strcmp(str, "ERROR") ==
+ 0 || strcmp(str, "NO") == 0) {
+ TIMER_DEQUEUE(&up->timer);
+ syslog(LOG_NOTICE,
+ "clock %s ACTS modem status %s",
+ ntoa(&peer->srcadr), pp->lastcode);
+ acts_disc(peer);
+ } else if (strcmp(str, "CONNECT") == 0) {
+ TIMER_DEQUEUE(&up->timer);
+ up->timer.event_time = current_time + CONNECT;
+ TIMER_INSERT(timerqueue, &up->timer);
+ up->msgcnt = 0;
+ up->state++;
+ }
+ return;
+
+ case 2:
+
+ /*
+ * State 2. The call has been answered and we are
+ * waiting for the first ACTS message. If this doesn't
+ * happen within the timecode timeout, hang up the
+ * phone. We probably got a wrong number or ACTS is
+ * down.
+ */
+ TIMER_DEQUEUE(&up->timer);
+ up->timer.event_time = current_time + TIMECODE;
+ TIMER_INSERT(timerqueue, &up->timer);
+ up->state++;
+ }
+
+ /*
+ * Real yucky things here. Ignore everything except timecode
+ * messages, as determined by the message length. We told the
+ * terminal routines to end the line with '*' and the line
+ * discipline to strike a timestamp on that character. However,
+ * when the ACTS echo-delay scheme works, the '*' eventually
+ * becomes a '#'. In this case the message is ended by the <CR>
+ * that comes about 200 ms after the '#' and the '#' cannot be
+ * echoed at the proper time. But, this may not be a lose, since
+ * we already have good data from prior messages and only need
+ * the millisecond advance calculated by ACTS. So, if the
+ * message is long enough and has an on-time character at the
+ * right place, we consider the message (but not neccesarily the
+ * timestmap) to be valid.
+ */
+ if (pp->lencode != LENCODE)
+ return;
+
+ /*
+ * We apparently have a valid timecode message, so dismember it
+ * with sscan(). This routine does a good job in spotting syntax
+ * errors without becoming overly pedantic.
+ *
+ * D L D
+ * MJD YR MO DA H M S ST S UT1 msADV OTM
+ * 47222 88-03-02 21:39:15 83 0 +.3 045.0 UTC(NBS) *
+ */
+ if (sscanf(pp->lastcode,
+ "%5ld %2d-%2d-%2d %2d:%2d:%2d %2d %1d %3lf %5lf %s %c",
+ &mjd, &pp->year, &month, &day, &pp->hour, &pp->minute,
+ &pp->second, &dst, &leap, &dut1, &msADV, utc, &flag) != 13) {
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+
+ /*
+ * Some modems can't be trusted (the Practical Peripherals
+ * 9600SA comes to mind) and, even if they manage to unstick
+ * ACTS, the millisecond advance is wrong, so we use CLK_FLAG2
+ * to disable echoes, if neccessary.
+ */
+ if ((flag == '*' || flag == '#') && !(pp->sloppyclockflag &
+ CLK_FLAG2))
+ (void)write(pp->io.fd, &flag, 1);
+
+ /*
+ * Yes, I know this code incorrectly thinks that 2000 is a leap
+ * year. The ACTS timecode format croaks then anyway. Life is
+ * short. Would only the timecode mavens resist the urge to
+ * express months of the year and days of the month in favor of
+ * days of the year.
+ */
+ if (month < 1 || month > 12 || day < 1) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ if (pp->year % 4) {
+ if (day > day1tab[month - 1]) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ for (i = 0; i < month - 1; i++)
+ day += day1tab[i];
+ } else {
+ if (day > day2tab[month - 1]) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ for (i = 0; i < month - 1; i++)
+ day += day2tab[i];
+ }
+ pp->day = day;
+ if (leap == 1)
+ pp->leap = LEAP_ADDSECOND;
+ else if (pp->leap == 2)
+ pp->leap = LEAP_DELSECOND;
+ else
+ pp->leap = 0;
+ pp->lasttime = current_time;
+
+ /*
+ * Colossal hack here. We process each sample in a trimmed-mean
+ * filter and determine the reference clock offset and
+ * dispersion. The fudge time1 value is added to each sample as
+ * received. If we collect MSGCNT samples before the '#' on-time
+ * character, we use the results of the filter as is. If the '#'
+ * is found before that, the adjusted msADV is used to correct
+ * the propagation delay.
+ */
+ up->msgcnt++;
+ if (flag == '#') {
+ L_CLR(&tstmp);
+ TVUTOTSF((long)((msADV - up->msADV) * 1000.),
+ tstmp.l_uf);
+ L_ADD(&pp->offset, &tstmp);
+ } else {
+ up->msADV = msADV;
+ if (!refclock_process(pp, up->msgcnt, up->msgcnt -
+ up->msgcnt / 3)) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ } else if (up->msgcnt < MSGCNT)
+ return;
+ }
+
+ /*
+ * We have a filtered sample offset ready for peer processing.
+ * We use lastrec as both the reference time and receive time in
+ * order to avoid being cute, like setting the reference time
+ * later than the receive time, which may cause a paranoid
+ * protocol module to chuck out the data. Finaly, we unhook the
+ * timeout, arm for the next call, fold the tent and go home.
+ * The little dance with the '%' character is an undocumented
+ * ACTS feature that hangs up the phone real quick without
+ * waiting for carrier loss or long-space disconnect, but we do
+ * these clumsy things anyway.
+ */
+ disp = LFPTOFP(&pp->fudgetime2);
+ record_clock_stats(&peer->srcadr, pp->lastcode);
+ refclock_receive(peer, &pp->offset, 0, pp->dispersion +
+ (u_fp)disp, &pp->lastrec, &pp->lastrec, pp->leap);
+ pp->sloppyclockflag &= ~CLK_FLAG1;
+ up->pollcnt = 0;
+ TIMER_DEQUEUE(&up->timer);
+ (void)write(pp->io.fd, &hangup, 1);
+ up->state = 0;
+ acts_disc(peer);
+}
+
+
+/*
+ * acts_poll - called by the transmit routine
+ */
+static void
+acts_poll(unit, peer)
+ int unit;
+ struct peer *peer;
+{
+ register struct actsunit *up;
+ struct refclockproc *pp;
+
+ /*
+ * If the driver is running, we set the enable flag (fudge
+ * flag1), which causes the driver timeout routine to initiate a
+ * call to ACTS. If not, the enable flag can be set using
+ * xntpdc. If this is the sustem peer, then follow the system
+ * poll interval.
+ */
+ pp = peer->procptr;
+ up = (struct actsunit *)pp->unitptr;
+ if (up->run) {
+ pp->sloppyclockflag |= CLK_FLAG1;
+ if (peer == sys_peer)
+ peer->hpoll = sys_poll;
+ else
+ peer->hpoll = peer->minpoll;
+ }
+}
+
+
+/*
+ * acts_timeout - called by the timer interrupt
+ */
+static void
+acts_timeout(peer)
+ struct peer *peer;
+{
+ register struct actsunit *up;
+ struct refclockproc *pp;
+ int dtr = TIOCM_DTR;
+
+ /*
+ * If a timeout occurs in other than state 0, the call has
+ * failed. If in state 0, we just see if there is other work to
+ * do.
+ */
+ pp = peer->procptr;
+ up = (struct actsunit *)pp->unitptr;
+ if (up->state) {
+ acts_disc(peer);
+ return;
+ }
+ switch (peer->ttl) {
+
+ /*
+ * In manual mode the ACTS calling program is activated
+ * by the xntpdc program using the enable flag (fudge
+ * flag1), either manually or by a cron job.
+ */
+ case MODE_MANUAL:
+ up->run = 0;
+ break;
+
+ /*
+ * In automatic mode the ACTS calling program runs
+ * continuously at intervals determined by the sys_poll
+ * variable.
+ */
+ case MODE_AUTO:
+ if (!up->run)
+ pp->sloppyclockflag |= CLK_FLAG1;
+ up->run = 1;
+ break;
+
+ /*
+ * In backup mode the ACTS calling program is disabled,
+ * unless no system peer has been selected for MAXOUTAGE
+ * (3600 s). Once enabled, it runs until some other NTP
+ * peer shows up.
+ */
+ case MODE_BACKUP:
+ if (!up->run && sys_peer == 0) {
+ if (current_time - last_time > MAXOUTAGE) {
+ up->run = 1;
+ peer->hpoll = peer->minpoll;
+ syslog(LOG_NOTICE,
+ "clock %s ACTS backup started ",
+ ntoa(&peer->srcadr));
+ }
+ } else if (up->run && sys_peer->refclktype !=
+ REFCLK_NIST_ACTS) {
+ peer->hpoll = peer->minpoll;
+ up->run = 0;
+ syslog(LOG_NOTICE,
+ "clock %s ACTS backup stopped",
+ ntoa(&peer->srcadr));
+ }
+ break;
+
+ default:
+ syslog(LOG_NOTICE,
+ "clock %s ACTS invalid mode", ntoa(&peer->srcadr));
+
+ }
+
+ /*
+ * The fudge flag1 is used as an enable/disable; if set either
+ * by the code or via xntpdc, the ACTS calling program is
+ * started; if reset, the phones stop ringing.
+ */
+ if (!(pp->sloppyclockflag & CLK_FLAG1)) {
+ up->pollcnt = 0;
+ up->timer.event_time = current_time + IDLE;
+ TIMER_INSERT(timerqueue, &up->timer);
+ return;
+ }
+
+ /*
+ * Initiate a call to the ACTS service. If we wind up here in
+ * other than state 0, a successful call could not be completed
+ * within minpoll seconds. We advance to the next modem dial
+ * string. If none are left, we log a notice and clear the
+ * enable flag. For future enhancement: call the site RP and
+ * leave an obscene message in his voicemail.
+ */
+ if (sys_phone[up->pollcnt][0] == '\0') {
+ refclock_report(peer, CEVNT_TIMEOUT);
+ syslog(LOG_NOTICE,
+ "clock %s ACTS calling program terminated",
+ ntoa(&peer->srcadr));
+ pp->sloppyclockflag &= ~CLK_FLAG1;
+#ifdef DEBUG
+ if (debug)
+ printf("acts: calling program terminated\n");
+#endif
+ up->pollcnt = 0;
+ up->timer.event_time = current_time + IDLE;
+ TIMER_INSERT(timerqueue, &up->timer);
+ return;
+ }
+
+ /*
+ * Raise DTR, call ACTS and start the answer timeout. We think
+ * it strange if the OK status has not been received from the
+ * modem, but plow ahead anyway.
+ */
+ if (strcmp(pp->lastcode, "OK") != 0)
+ syslog(LOG_NOTICE, "clock %s ACTS no modem status",
+ ntoa(&peer->srcadr));
+ (void)ioctl(pp->io.fd, TIOCMBIS, (char *)&dtr);
+ (void)acts_write(peer, sys_phone[up->pollcnt]);
+ syslog(LOG_NOTICE, "clock %s ACTS calling %s\n",
+ ntoa(&peer->srcadr), sys_phone[up->pollcnt]);
+ up->state = 1;
+ up->pollcnt++;
+ pp->polls++;
+ up->timer.event_time = current_time + ANSWER;
+ TIMER_INSERT(timerqueue, &up->timer);
+}
+
+
+/*
+ * acts_disc - disconnect the call and wait for the ruckus to cool
+ */
+static void
+acts_disc(peer)
+ struct peer *peer;
+{
+ register struct actsunit *up;
+ struct refclockproc *pp;
+ int dtr = TIOCM_DTR;
+
+ /*
+ * We should never get here other than in state 0, unless a call
+ * has timed out. We drop DTR, which will reliably get the modem
+ * off the air, even while ACTS is hammering away full tilt.
+ */
+ pp = peer->procptr;
+ up = (struct actsunit *)pp->unitptr;
+ (void)ioctl(pp->io.fd, TIOCMBIC, (char *)&dtr);
+ if (up->state > 0) {
+ up->state = 0;
+ syslog(LOG_NOTICE, "clock %s ACTS call failed %d",
+ ntoa(&peer->srcadr), up->state);
+#ifdef DEBUG
+ if (debug)
+ printf("acts: call failed %d\n", up->state);
+#endif
+ }
+ up->timer.event_time = current_time + WAIT;
+ TIMER_INSERT(timerqueue, &up->timer);
+}
+
+
+/*
+ * acts_write - write a message to the serial port
+ */
+int
+acts_write(peer, str)
+ struct peer *peer;
+ char *str;
+{
+ register struct actsunit *up;
+ struct refclockproc *pp;
+ int len;
+ int code;
+ char cr = '\r';
+
+ /*
+ * Not much to do here, other than send the message, handle
+ * debug and report faults.
+ */
+ pp = peer->procptr;
+ up = (struct actsunit *)pp->unitptr;
+ len = strlen(str);
+#ifdef DEBUG
+ if (debug)
+ printf("acts: state %d send %d %s\n", up->state, len,
+ str);
+#endif
+ code = write(pp->io.fd, str, len) == len;
+ code |= write(pp->io.fd, &cr, 1) == 1;
+ if (!code)
+ refclock_report(peer, CEVNT_FAULT);
+ return (code);
+}
+
+#endif
diff --git a/usr.sbin/xntpd/doc/notes.txt b/usr.sbin/xntpd/doc/notes.txt
new file mode 100644
index 0000000..1dd59f2
--- /dev/null
+++ b/usr.sbin/xntpd/doc/notes.txt
@@ -0,0 +1,1258 @@
+ Notes on Xntpd Configuration
+
+ David L. Mills (mills@udel.edu)
+ University of Delaware
+ 14 January 1993
+
+Introduction
+
+This document is a collection of notes concerning the use of xntpd and
+related programs, and on coping with the Network Time Protocol (NTP) in
+general. It is a major rewrite and update of an earlier document written
+by Dennis Ferguson of the University of Toronto dated 5 November 1989.
+It includes many changes and additions resulting from the NTP Version 3
+specification and new implementation features. It supersedes the earlier
+document, which should no longer be used for new configurations.
+
+Xntpd is a complete implementation of the NTP Version 3 specification as
+defined in RFC 1305. It also retains compatibility with both NTP Version
+2, as defined in RFC 1119, and NTP Version 1, as defined in RFC 1059,
+although this compatibility is sometimes strained and only
+semiautomatic. In order to support in principle the ultimate precision
+of about 232 picoseconds in the NTP specification, xntpd does no
+floating-point arithmetic and instead manipulates the 64-bit NTP
+timestamps as unsigned 64-bit integers. Xntpd fully implements NTP
+Versions 2 and 3 authentication and a mode-6 control-message facility.
+As extensions to the specification, a flexible address-and-mask
+restriction facility has been included, along with a private mode-7
+control-message facility used to remotely reconfigure the system and
+monitor a considerable amount of internal detail.
+
+The code is biased towards the needs of a busy time server with
+numerous, possibly hundreds, of clients and other servers. Tables are
+hashed to allow efficient handling of many associations, though at the
+expense of additional overhead when the number of associations is small.
+Many fancy features have been included to permit efficient management
+and monitoring of a busy primary server, features which are simply
+excess baggage for a server on a high stratum client. The code was
+written with near demonic attention to details which can affect
+precision and as a consequence should be able to make good use of high
+performance, special purpose hardware such as precision oscillators and
+radio clocks. The present code supports a number of radio clocks,
+including those for the WWV, CHU, WWVB, DCF77, GOES and GPS radio and
+satellite services. The server methodically avoids the use of Unix-
+specific library routines where possible by implementing local versions,
+in order to aid in porting the code to perverse Unix and non-Unix
+platforms.
+
+While this implementation slavishly obeys the NTP specification RFC
+1305, it has been specifically tuned to achieve the highest accuracy
+possible on whatever hardware and operating-system platform is
+available. In general, its precision is limited only by that of the
+onboard time-of-day clock maintained by the hardware and operating
+system, while its stability is limited only by that of the onboard
+frequency source, usually an uncompensated crystal oscillator. On modern
+RISC-based processors connected directly to radio clocks via serial-
+asynchronous interfaces, the accuracy is usually limited by that of the
+radio clock and interface to the order of a few milliseconds. The code
+includes special features to support a one-pulse-per-second (1-pps)
+signal generated by some radio clocks. When used in conjunction with a
+suitable hardware level converter, the accuracy can be improved to the
+order of 100 microseconds. Further improvement is possible using an
+outboard, stabilized frequency source, in which the accuracy and
+stability are limited only by the characteristics of that source.
+
+The xntp3 distribution includes, in addition to the daemon itself
+(xntpd), several utility programs, including two remote-monitoring
+programs (ntpq, xntpdc), a remote clock-setting program similar to the
+Unix rdate program (ntpdate), a traceback utility useful to discover
+suitable synchronization sources (ntptrace), and various programs used
+to configure the local platform and calibrate the intrinsic errors. NTP
+has been ported to a large number of platforms, including most RISC and
+CISC workstations and mainframes manufactured today. Example
+configuration files for many models of these machines are included in
+the xntp3 distribution. While in most cases the standard version of the
+implementation runs with no hardware or operating-system modifications,
+not all features of the distribution are available on all platforms. For
+instance, a special feature allowing Sun 4s to achieve accuracies in the
+order of 100 microseconds requires some minor changes and additions to
+the kernel and input/output support.
+
+There are, however, several drawbacks to all of this. Xntpd is very,
+very fat. This is rotten if your intended platform for the daemon is
+memory-limited. Xntpd uses SIGIO for all input, a facility which appears
+to not enjoy universal support and whose use seems to exercise the parts
+of your vendors' kernels which are most likely to have been done poorly.
+The code is unforgiving in the face of kernel problems which affect
+performance, and generally requires that you repair the problems in
+order to achieve acceptable performance. The code has a distinctly
+experimental flavour and contains features which could charitably be
+termed failed experiments, but which have not been hacked out yet. There
+is code which has not been thoroughly tested (e.g. leap-second support)
+due to the inconvenience of setting up tests. Much was learned from the
+addition of support for a variety of radio clocks, with the result that
+this support could use some rewriting.
+
+How NTP Works
+
+The approach used by NTP to achieve reliable time synchronization from a
+set of possibly unreliable remote time servers is somewhat different
+than other such protocols. In particular, NTP does not attempt to
+synchronize clocks to each other. Rather, each server attempts to
+synchronize to UTC (i.e., Universal Coordinated Time) using the best
+available source and available transmission paths to that source. This
+is a fine point which is worth understanding. A group of NTP-
+synchronized clocks may be close to each other in time, but this is not
+a consequence of the clocks in the group having synchronized to each
+other, but rather because each clock has synchronized closely to UTC via
+the best source it has access to. As such, trying to synchronize a set
+of clocks to a set of servers whose time is not in mutual agreement may
+not result in any sort of useful synchronization of the clocks, even if
+you don't care about UTC. NTP operates on the premise that there is one
+true standard time, and that if several servers which claim
+synchronization to standard time disagree about what that time is, then
+one or more of them must be broken. There is no attempt to resolve
+differences more gracefully since the premise is that substantial
+differences cannot exist. In essence, NTP expects that the time being
+distributed from the root of the synchronization subnet will be derived
+from some external source of UTC (e.g. a radio clock). This makes it
+somewhat inconvenient (though not impossible) to synchronize hosts
+together without a reliable source of UTC to synchronize them to. If
+your network is isolated and you cannot access other people's servers
+across the Internet, a radio clock may make a good investment.
+
+Time is distributed through a hierarchy of NTP servers, with each server
+adopting a "stratum" which indicates how far away from an external
+source of UTC it is operating at. Stratum-1 servers, which are at the
+top of the pile (or bottom, depending on your point of view), have
+access to some external time source, usually a radio clock synchronized
+to time signal broadcasts from radio stations which explicitly provide a
+standard time service. A stratum-2 server is one which is currently
+obtaining time from a stratum-1 server, a stratum-3 server gets its time
+from a stratum-2 server, and so on. To avoid long lived synchronization
+loops the number of strata is limited to 15.
+
+Each client in the synchronization subnet (which may also be a server
+for other, higher stratum clients) chooses exactly one of the available
+servers to synchronize to, usually from among the lowest stratum servers
+it has access to. It is thus possible to construct a synchronization
+subnet where each server has exactly one source of lower stratum time to
+synchronize to. This is, however, not an optimal configuration, for
+indeed NTP operates under another premise as well, that each server's
+time should be viewed with a certain amount of distrust. NTP really
+prefers to have access to several sources of lower stratum time (at
+least three) since it can then apply an agreement algorithm to detect
+insanity on the part of any one of these. Normally, when all servers are
+in agreement, NTP will choose the best of these, where "best" is defined
+in terms of lowest stratum, closest (in terms of network delay) and
+claimed precision, along with several other considerations. The
+implication is that, while one should aim to provide each client with
+three or more sources of lower stratum time, several of these will only
+be providing backup service and may be of lesser quality in terms of
+network delay and stratum (i.e. a same-stratum peer which receives time
+from lower stratum sources the local server doesn't access directly can
+also provide good backup service).
+
+Finally, there is the issue of association modes. There are a number of
+modes in which NTP servers can associate with each other, with the mode
+of each server in the pair indicating the behaviour the other server can
+expect from it. In particular, when configuring a server to obtain time
+from other servers, there is a choice of two modes which may be
+alternatively used. Configuring an association in symmetric-active mode
+(usually indicated by a "peer" declaration in configuration files)
+indicates to the remote server that one wishes to obtain time from the
+remote server and that one is also willing to supply time to the remote
+server if need be. This mode is appropriate in configurations involving
+a number of redundant time servers interconnected via diverse network
+paths, which is presently the case for most stratum-1 and stratum-2
+servers on the Internet today. Configuring an association in client mode
+(usually indicated by a "server" declaration in configuration files)
+indicates that one wishes to obtain time from the remote server, but that
+one is not willing to provide time to the remote server. This mode is
+appropriate for file-server and workstation clients that do not provide
+synchronization to other local clients. Client mode is also useful for
+boot-date-setting programs and the like, which really have no time to
+provide and which don't retain state about associations over the longer
+term.
+
+Configuring Your Subnet
+
+At startup time the xntpd daemon running on a host reads the initial
+configuration information from a file, usually /etc/ntp.conf, unless a
+different name has been specified at compile time. Putting something in
+this file which will enable the host to obtain time from somewhere else
+is usually the first big hurdle after installation of the software
+itself, which is described in other documents included in the xntp3
+distribution. At its simplest, what you need to do in the configuration
+file is declare the servers that the daemon should poll for time
+synchronization. In principle, no such list is needed if some other time
+server explicitly mentions the host and is willing to provide
+synchronization; however, this is considered dangerous, unless the
+access control or authentication features (described later) are in use.
+
+In the case of a workstation operating in an enterprise network for a
+public or private organization, there is often an administrative
+department that coordinates network services, including NTP. Where
+available, the addresses of appropriate servers can be provided by that
+department. However, if this infrastructure is not available, it is
+necessary to explore some portion of the existing NTP subnet now running
+in the Internet. There are at present many thousands of time servers
+running NTP in the Internet, a significant number of which are willing
+to provide a public time-synchronization service. Some of these are
+listed in a file maintained on the Internet host louie.udel.edu
+(128.175.1.3) on the path pub/ntp/doc/clock.txt. This file is updated on
+a regular basis using information provided voluntarily by various site
+administrators. There are other ways to explore the nearby subnet using
+the ntptrace and ntpq programs. See the man pages for further
+information on these programs.
+
+It is vital to carefully consider the issues of robustness and
+reliability when selecting the sources of synchronization. Normally, not
+less than three sources should be available, preferably selected to
+avoid common points of failure. It is usually better to choose sources
+which are likely to be "close" to you in terms of network topology,
+though you shouldn't worry overly about this if you are unable to
+determine who is close and who isn't. Normally, it is much more serious
+when a server becomes faulty and delivers incorrect time than when it
+simply stops operating, since an NTP-synchronized host normally can
+coast for hours or even days without its clock accumulating serious
+error over one second, for instance. Selecting at least three sources
+from different operating administrations, where possible, is the minimum
+recommended, although a lesser number could provide acceptable service
+with a degraded degree of robustness.
+
+Normally, it is not considered good practice for a single workstation to
+request synchronization from a primary (stratum-1) time server. At
+present, these servers provide synchronization for hundreds of clients
+in many cases and could, along with the network access paths, become
+seriously overloaded if large numbers of workstation clients requested
+synchronization directly. Therefore, workstations located in sparsely
+populated administrative domains with no local synchronization
+infrastructure should request synchronization from nearby stratum-2
+servers instead. In most cases the keepers of those servers listed in
+the clock.txt file provide unrestricted access without prior permission;
+however, in all cases it is considered polite to notify the
+administrator listed in the file upon commencement of regular service.
+In all cases the access mode and notification requirements listed in the
+file must be respected.
+
+In the case of a gateway or file server providing service to a
+significant number of workstations or file servers in an enterprise
+network it is even more important to provide multiple, redundant sources
+of synchronization and multiple, diversity-routed, network access paths.
+The preferred configuration is at least three administratively
+coordinated time servers providing service throughout the administrative
+domain including campus networks and subnetworks. Each of these should
+obtain service from at least two different outside sources of
+synchronization, preferably via different gateways and access paths.
+These sources should all operate at the same stratum level, which is one
+less than the stratum level to be used by the local time servers
+themselves. In addition, each of these time servers should peer with all
+of the other time servers in the local administrative domain at the
+stratum level used by the local time servers, as well as at least one
+(different) outside source at this level. This configuration results in
+the use of six outside sources at a lower stratum level (toward the
+primary source of synchronization, usually a radio clock), plus three
+outside sources at the same stratum level, for a total of nine outside
+sources of synchronization. While this may seem excessive, the actual
+load on network resources is minimal, since the interval between polling
+messages exchanged between peers usually ratchets back to no more than
+one message every 17 minutes.
+
+The stratum level to be used by the local time servers is an engineering
+choice. As a matter of policy, and in order to reduce the load on the
+primary servers, it is desirable to use the highest stratum consistent
+with reliable, accurate time synchronization throughout the
+administrative domain. In the case of enterprise networks serving
+hundreds or thousands of client file servers and workstations,
+conventional practice is to obtain service from stratum-1 primary
+servers such as listed in the clock.txt file. When choosing sources away
+from the primary sources, the particular synchronization path in use at
+any time can be verified using the ntptrace program included in the
+xntp3 distribution. It is important to avoid loops and possible common
+points of failure when selecting these sources. Note that, while NTP
+detects and rejects loops involving neighboring servers, it does not
+detect loops involving intervening servers. In the unlikely case that
+all primary sources of synchronization are lost throughout the subnet,
+the remaining servers on that subnet can form temporary loops and, if
+the loss continues for an interval of many hours, the servers will drop
+off the subnet and free-run with respect to their internal (disciplined)
+timing sources.
+
+In many cases the purchase of one or more radio clocks is justified, in
+which cases good engineering practice is to use the configurations
+described above and connect the radio clock to one of the local servers.
+This server is then encouraged to participate in a special primary-
+server subnetwork in which each radio-equipped server peers with several
+other similarly equipped servers. In this way the radio-equipped server
+may provide synchronization, as well as receive synchronization, should
+the local or remote radio clock(s) fail or become faulty. Xntpd treats
+attached radio clock(s) in the same way as other servers and applies the
+same criteria and algorithms to the time indications, so can detect when
+the radio fails or becomes faulty and switch to alternate sources of
+synchronization. It is strongly advised, and in practice for most
+primary servers today, to employ the authentication or access-control
+features of the xntp3 distribution in order to protect against hostile
+penetration and possible destabilization of the time service.
+
+Using this or similar strategies, the remaining hosts in the same
+administrative domain can be synchronized to the three (or more)
+selected time servers. Assuming these servers are synchronized directly
+to stratum-1 sources and operate normally as stratum-2, the next level
+away from the primary source of synchronization, for instance various
+campus file servers, will operate at stratum 3 and dependent
+workstations at stratum 4. Engineered correctly, such a subnet will
+survive all but the most exotic failures or even hostile penetrations of
+the various, distributed timekeeping resources.
+
+The above arrangement should provide very good, robust time service with
+a minimum of traffic to distant servers and with manageable loads on the
+local servers. While it is theoretically possible to extend the
+synchronization subnet to even higher strata, this is seldom justified
+and can make the maintenance of configuration files unmanageable.
+Serving time to a higher stratum peer is very inexpensive in terms of
+the load on the lower stratum server if the latter is located on the
+same concatenated LAN. When justified by the accuracy expectations, NTP
+can be operated in broadcast mode, so that clients need only listen for
+periodic broadcasts and do not need to send anything.
+
+When planning your network you might, beyond this, keep in mind a few
+generic don'ts, in particular:
+
+1. Don't synchronize a local time server to another peer at the same
+ stratum, unless the latter is receiving time from lower stratum
+ sources the former doesn't talk to directly. This minimizes the
+ occurance of common points of failure, but does not eliminate them
+ in cases where the usual chain of associations to the primary
+ sources of synchronization are disrupted due to failures.
+2. Don't configure peer associations with higher stratum servers. Let
+ the higher strata configure lower stratum servers, but not the
+ reverse. This greatly simplifies configuration file maintenance,
+ since there is usually much greater configuration churn in the high
+ stratum clients such as personal workstations.
+
+3. Don't synchronize more than one time server in a particular
+ administrative domain to the same time server outside that domain.
+ Such a practice invites common points of failure, as well as raises
+ the possibility of massive abuse, should the configuration file be
+ automatically distributed do a large number of clients.
+
+There are many useful exceptions to these rules. When in doubt, however,
+follow them.
+
+Dennis Ferguson writes: Note that mention was made of machines with
+"good" clocks versus machines with "bad" ones. There are two things that
+make a clock good, the precision of the clock (e.g. how many low order
+bits in a time value are actually significant) and the frequency of
+occurance (or lack thereof) of such things as lost clock interrupts.
+Among the most common computers I have observed there to be a fairly
+simple algorithm for determining the goodness of its clock. If the
+machine is a Vax, it probably has a good clock (the low order bit in the
+time is in the microseconds and most of these seem to manage to get
+along without losing clock interrupts). If the machine is a Sun 3 it
+probably doesn't (the low order clock bit is at the 10 or 20 millisecond
+mark and Sun 3s like to lose clock interrupts, particularly if they have
+a screen and particularly if they run SunOS 4.0.x). If you have IBM RTs
+running AOS 4.3, they have fair clocks (low order clock bit at about a
+millisecond and they don't lose clock interrupts, though they do have
+trouble with clock rollovers while reading the low order clock bits) but
+I recommend them as low stratum NTP servers anyway since they aren't
+much use as anything else. Sun 4s running SunOS 4.1.1 make very good
+time servers, once some native foolishness mentioned below is
+surmounted. [However, it is very important to avoid using the keyboard
+firmware, which can cause severe interrupt latencies, in favor of the
+software drivers ordinarily used in conjunction with a windowing system.
+- DLM] For other machines you are on your own since I don't have enough
+data points to venture an opinion. In any event, if at all possible you
+should try to use machines with good clocks for the lower strata.
+
+Configuring Your Server or Client
+
+As mentioned previously, the configuration file is usually called
+/etc/ntp.conf. This is an ASCII file conforming to the usual comment and
+whitespace conventions. A working configuration file might look like (In
+this and other examples, do not copy this directly.):
+
+ # peer configuration for 128.100.100.7
+ # (expected to operate at stratum 2)
+
+ server 128.4.1.1 # rackety.udel.edu
+ server 128.8.10.1 # umd1.umd.edu
+ server 192.35.82.50 # lilben.tn.cornell.edu
+ driftfile /etc/ntp.drift
+
+This particular host is expected to operate as a client at stratum 2 by
+virtue of the "server" keyward and the fact that two of the three
+servers declared (the first two, actually) have radio clocks and usually
+run at stratum 1. The third server in the list has no radio clock, but
+is known to maintain associations with a number of stratum 1 peers and
+usually operates at stratum 2. Of particular importance with the last
+host is that it maintains associations with peers besides the two
+stratum 1 peers mentioned. This can be verified using the ntpq program
+included in the xntp3 distribution. When configured using the "server"
+keyword, this host can receive synchronization from any of the listed
+servers, but can never provide synchronization to them.
+
+Unless restricted using facilities described later, this host can
+provide synchronization to dependent clients, which do not have to be
+listed in the configuration file. Associations maintained for these
+clients are transitory and result in no persistent state in the host.
+These clients are normally not visible using the ntpq program included
+in the xntp3 distribution; however, xntpd includes a monitoring feature
+(described later) which caches a minimal amount of client information
+useful for debugging administrative purposes.
+
+A time server expected to both receive synchronization from another
+server, as well as to provide synchronization to it, is delared using
+the "peer" keyword instead of the "server" keyword. In all other aspects
+the server operates the same in either mode and can provide
+synchronization to dependent clients or other peers. It is considered
+good engineering practice to declare time servers outside the
+administrative domain as "peer" and those inside as "server" in order to
+provide redundancy in the global Internet, while minimizing the
+possibility of instability within the domain itself. A time server in
+one domain can in principle heal another domain temporarily isolated
+from all other sources of synchronization. However, it is probably
+unwise for a casual workstation to bridge fragments of the local domain
+which have become temporarily isolated.
+
+Note the inclusion of a "driftfile" declaration. One of the things the
+NTP daemon does when it is first started is to compute the error in the
+intrinsic frequency of the clock on the computer it is running on. It
+usually takes about a day or so after the daemon is started to compute a
+good estimate of this (and it needs a good estimate to synchronize
+closely to its server). Once the initial value is computed, it will
+change only by relatively small amounts during the course of continued
+operation. The "driftfile" declaration indicates to the daemon the name
+of a file where it may store the current value of the frequency error so
+that, if the daemon is stopped and restarted, it can reinitialize itself
+to the previous estimate and avoid the day's worth of time it will take
+to recompute the frequency estimate. Since this is a desireable feature,
+a "driftfile" declaration should always be included in the configuration
+file.
+
+An implication in the above is that, should xntpd be stopped for some
+reason, the local platform time will diverge from UTC by an amount that
+depends on the intrinsic error of the clock oscillator and the time
+since last synchronized. In view of the length of time necessary to
+refine the frequency estimate, every effort should be made to operate
+the daemon on a continuous basis and minimize the intervals when for
+some reason it is not running.
+
+Xntpd3 Versus Previous Versions
+
+There are several items of note when dealing with a mixture of xntp3 and
+and previous distributions of xntp (NTP Version 2 xntpd) and ntp3.4 (NTP
+Version 1 ntpd). The xntp3 implementation of xntpd is an NTP Version 3
+implementation. As such, by default when no additional information is
+available concerning the preferences of the peer, xntpd claims to be
+version 3 in the packets that it sends.
+
+An NTP implementation conforming to a previous version specification
+ordinarily discards packets from a later version. However, in most
+respects documented in RFC 1305, the previous version is compatible with
+the version-3 algorithms and protocol. Ntpd, while implementing most of
+the version-2 algorithms, still believes itself to be a version-1
+implementation. The sticky part here is that, when either xntpd version
+2 or ntpd version 1 receives a packet claiming to be from a version-3
+server, it discards it without further processing. Hence there is a
+danger that in some situations synchronization with previous versions
+will fail.
+
+Xntpd is aware of this problem. In particular, when xntpd is polled
+first by a host claiming to be a previous version 1 or version 2
+implementation, xntpd claims to be a version 1 or 2 implementation,
+respectively, in packets returned to the poller. This allows xntpd to
+serve previous version clients transparently. The trouble occurs when an
+previous version is to be included in an xntpd configuration file. With
+no further indication, xntpd will send packets claiming to be version 3
+when it polls. To get around this, xntpd allows a qualifier to be added
+to configuration entries to indicate which version to use when polling.
+Hence the entry
+
+ # specify NTP version 1
+
+ peer 130.43.2.2 version 1 # apple.com (running ntpd version 1)
+ peer 130.43.2.2 version 2 # apple.com (running xntpd version 2)
+
+will cause version 1 packets to be sent to the host address 130.43.2.2.
+If you are testing xntpd against previous version servers you will need
+to be careful about this. Note that, as indicated in the RFC 1305
+specification, there is no longer support for the original NTP
+specification, popularly called NTP Version 0.
+
+There are a few other items to watch when converting an ntpd
+configuration file for use with xntpd. The first is to reconsider the
+precision entry from the configuration file, if there is one. There was
+a time when the precision claimed by a server was mostly commentary,
+with no particularly useful purpose. This is no longer the case,
+however, and so changing the precision a server claims should only be
+done with some consideration as to how this alters the performance of
+the server. The default precision claimed by xntpd will be right for
+most situations. A section later on will deal with when and how it is
+appropriate to change a server's precision without doing things you
+don't intend.
+
+Second, note that in the example configuration file above numeric
+addresses are used in the peer and server declarations. It is also
+possible to use names requiring resolution instead, but only if some
+additional configuration is done (xntpd doesn't include the resolver
+routines itself, and requires that a second program be used to do name
+resolution). If you find numeric addresses offensive, see below.
+
+Finally, "passive" and "client" entries in an ntpd configuration file
+have no useful equivalent semantics for xntpd and should be deleted.
+Xntpd won't reset the kernel variable tickadj when it starts, so you can
+remove anything dealing with this in the configuration file. The
+configuration of radio clock peers is done using different language in
+xntpd configuration files, so you will need to delete these entries from
+your ntpd configuration file and see below for the equivalent language.
+
+Traffic Monitoring
+
+Xntpd handles peers whose stratum is higher than the stratum of the
+local server and pollers using client mode by a fast path which
+minimizes the work done in responding to their polls, and normally
+retains no memory of these pollers. Sometimes, however, it is
+interesting to be able to determine who is polling the server, and how
+often, as well as who has been sending other types of queries to the
+server.
+
+To allow this, xntpd implements a traffic monitoring facility which
+records the source address and a minimal amount of other information
+from each packet which is received by the server. This can be enabled by
+adding the following line to the server's configuration file:
+
+ # enable monitoring feature
+
+ monitor yes
+
+The recorded information can be displayed using the xntpdc query
+program, described briefly below.
+
+Address-and-Mask Restrictions
+
+The address-and-mask configuration facility supported by xntpd is quite
+flexible and general, but is not an integral part of the NTP Version 3
+specification. The major drawback is that, while the internal
+implementation is very nice, the user interface sucks. For this reason
+it is probably worth doing an example here. Briefly, the facility works
+as follows. There is an internal list, each entry of which holds an
+address, a mask and a set of flags. On receipt of a packet, the source
+address of the packet is compared to each entry in the list, with a
+match being posted when the following is true:
+
+ (source_addr & mask) == (address & mask)
+
+A particular source address may match several list entries. In this case
+the entry with the most one bits in the mask is chosen. The flags
+associated with this entry are used to control the access.
+
+In the current implementation the flags always add restrictions. In
+effect, an entry with no flags set leaves matching hosts unrestricted.
+An entry can be added to the internal list using a "restrict"
+declaration. The flags associated with the entry are specified
+textually. For example, the "notrust" flag indicates that hosts matching
+this entry, while treated normally in other respects, shouldn't be
+trusted to provide synchronization even if otherwise so enabled. The
+"nomodify" flag indicates that hosts matching this entry should not be
+allowed to do run time configuration. There are many more flags, see the
+xntpd.8 man page.
+
+Now the example. Suppose you are running the server on a host whose
+address is 128.100.100.7. You would like to ensure that run time
+reconfiguration requests can only be made from the local host and that
+the server only ever synchronizes to one of a pair of off-campus servers
+or, failing that, a time source on net 128.100. The following entries in
+the configuration file would implement this policy:
+
+ # by default, don't trust and don't allow modifications
+
+ restrict default notrust nomodify
+
+ # these guys are trusted for time, but no modifications allowed
+
+ restrict 128.100.0.0 mask 255.255.0.0 nomodify
+ restrict 128.8.10.1 nomodify
+ restrict 192.35.82.50 nomodify
+
+ # the local addresses are unrestricted
+
+ restrict 128.100.100.7
+ restrict 127.0.0.1
+
+The first entry is the default entry, which all hosts match and hence
+which provides the default set of flags. The next three entries indicate
+that matching hosts will only have the nomodify flag set and hence will
+be trusted for time. If the mask isn't specified in the restrict
+keyward, it defaults to 255.255.255.255. Note that the address
+128.100.100.7 matches three entries in the table, the default entry
+(mask 0.0.0.0), the entry for net 128.100 (mask 255.255.0.0) and the
+entry for the host itself (mask 255.255.255.255). As expected, the flags
+for the host are derived from the last entry since the mask has the most
+bits set.
+
+The only other thing worth mentioning is that the restrict declarations
+apply to packets from all hosts, including those that are configured
+elsewhere in the configuration file and even including your clock
+pseudopeer(s), in any. Hence, if you specify a default set of
+restrictions which you don't wish to be applied to your configured
+peers, you must remove those restrictions for the configured peers with
+additional restrict declarations mentioning each peer separately.
+
+Authentication
+
+Xntpd supports the optional authentication procedure specified in the
+NTP Version 2 and 3 specifications. Briefly, when an association runs in
+authenticated mode, each packet transmitted has appended to it a 32-bit
+key ID and a 64-bit crypto checksum of the contents of the packet
+computed using either the Data Encryption Standard (DES) or Message
+Digest (MD5) algorithms. Note that while either of these algorithms
+provide sufficient protection from message-modification attacks,
+distribution of the former algorithm implementation is restricted to the
+U.S. and Canada, while the latter presently is free from such
+restrictions. With either algorithm the receiving peer recomputes the
+checksum and compares it with the one included in the packet. For this
+to work, the peers must share at least one encryption key and,
+furthermore, must associate the shared key with the same key ID.
+
+This facility requires some minor modifications to the basic packet
+processing procedures, as required by the specification. These
+modifications are enabled by the "authenticate" configuration
+declaration. In particular, in authenticated mode, peers which send
+unauthenticated packets, peers which send authenticated packets which
+the local server is unable to decrypt and peers which send authenticated
+packets encrypted using a key we don't trust are all marked
+untrustworthy and unsuitable for synchronization. Note that, while the
+server may know many keys (identified by many key IDs), it is possible
+to declare only a subset of these as trusted. This allows the server to
+share keys with a client which requires authenticated time and which
+trusts the server but which is not trusted by the server. Also, some
+additional configuration language is required to specify the key ID to
+be used to authenticate each configured peer association. Hence, for a
+server running in authenticated mode, the configuration file might look
+similar to the following:
+
+ # peer configuration for 128.100.100.7
+ # (expected to operate at stratum 2)
+ # fully authenticated this time
+
+ peer 128.100.49.105 key 22 # suzuki.ccie.utoronto.ca
+ peer 128.8.10.1 key 4 # umd1.umd.edu
+ peer 192.35.82.50 key 6 # lilben.tn.cornell.edu
+ authenticate yes # enable authentication
+ keys /usr/local/bin/ntp.keys # path for key file
+ trustedkey 1 2 14 15 # define trusted keys
+ requestkey 15 # key (7) for accessing server variables
+ controlkey 15 # key (6) for accessing server variables
+
+ #authdelay 0.000047 # authentication delay (Sun4c/50 IPX DES)
+ authdelay 0.000094 # authentication delay (Sun4c/50 IPX MD5)
+
+There are a couple of previously unmentioned things in here. The
+"authenticate yes" line enables authentication processing, while the
+"keys /usr/local/bin/ntp.keys" specifies the path to the keys file (see
+below and the xntpd.8 man page for detaiils of the file format). The
+"trustedkey" declaration identifies those keys that are known to be
+uncompromised; the remainder presumably represent the expired or
+possibly compromised keys. Both sets of keys must be declared by key
+identifier in the ntp.keys file described below. This provides a way to
+retire old keys while minimrequestkey 15izing the frequency of delicate
+key-distribution procedures. The "requestkey 15" line establishes the
+key to be used for mode-6 control messages as specified in RFC 1305 and
+used by the ntpq utility program, while the "controlkey 15" establishes
+the key to be used for mode-7 private control messages used by the
+xntpdc utility program these keys are used to prevent unauthorized
+modification of daemon variables.
+
+The "authdelay" declaration is an estimate of the amount of processing
+time taken between the freezing of a transmit timestamp and the actual
+transmission of the packet when authentication is enabled (i.e. more or
+less the time it takes for the DES or MD5 routine to encrypt a single
+block), and is used as a correction for the transmit timestamp. This can
+be computed for your CPU by the authspeed program included in the
+authstuff directory in the xntp3 distribution. The usage is illustrated
+to the following:
+
+ # for DES keys
+
+ authspeed -n 30000 auth.samplekeys
+
+ # for MD5 keys
+
+ authspeed -nd 30000 auth.samplekeys
+
+Additional utility programs included in the authstuff directory can be
+used to generate random keys, certify implementation correctness and
+display sample keys. As a general rule, keys should be chosen randomly,
+except possibly the request and control keys, which must be entered by
+the user as a password.
+
+The ntp.keys file contains the list of keys and associated key IDs the
+server knows about (for obvious reasons this file is better left
+unreadable by anyone except the server). The contents of this file might
+look like:
+
+ # ntp keys file (ntp.keys)
+
+ 1 N 29233E0461ECD6AE # des key in NTP format
+ 2 M RIrop8KPPvQvYotM # md5 key as an ASCII random string
+ 14 M sundial # md5 key as an ASCII string
+ 15 A sundial # des key as an ASCII string
+
+ # the following 3 keys are identical
+
+ 10 A SeCReT
+ 10 N d3e54352e5548080
+ 10 S a7cb86a4cba80101
+
+In the keys file the first token on each line indicates the key ID, the
+second token the format of the key and the third the key itself. There
+are four key formats. An "A" indicates a DES key written as a 1-to-8
+character string in 7-bit ASCII representation, with each character
+standing for a key octet (like a Unix password). An "S" indicates a DES
+key written as a hex number in the DES standard format, with the low
+order bit (LSB) of each octet being the (odd) parity bit. An "N"
+indicates a DES key again written as a hex number, but in NTP standard
+format with the high order bit of each octet being the (odd) parity bit
+(confusing enough?). An "M" indicates an MD5 key written as a 1-to-31
+character ASCII string in the "A" format. Note that, because of the
+simple tokenizing routine, the characters ' ', '#', '\t', '\n' and '\0'
+can't be used in either a DES or MD5 ASCII key. Everything else is fair
+game, though. Key 0 (zero) is used for special purposes and should not
+appear in this file.
+
+The big trouble with the authentication facility is the keys file. It is
+a maintenance headache and a security problem. This should be fixed some
+day. Presumably, this whole bag of worms goes away if/when a generic
+security regime for the Internet is established.
+
+Query Programs
+
+Three utility query programs are included with the xntp3 distribution,
+ntpq, ntptrace and xntpdc. Ntpq is a rather handy program which sends
+queries and receives responses using NTP standard mode-6 control
+messages. Since it uses the standard control protocol specified in RFC
+1305, it may be used with NTP Version 2 and Version 3 implementations
+for both Unix and Fuzzball, but not Version 1 implementations. It is
+most useful to query remote NTP implementations to assess timekeeping
+accuracy and expose bugs in configuration or operation.
+
+Ntptrace can be used to display the current synchronization path from a
+selected host through possibly intervening servers to the primary source
+of synchronization, usually a radio clock. It works with both version 2
+and version 3 servers, but not version 1.
+
+Xnptdc is a horrid program which uses NTP private mode-7 control
+messages to query local or remote servers. The format and and contents
+of these messages are specific to xntpd. The program does allow
+inspection of a wide variety of internal counters and other state data,
+and hence does make a pretty good debugging tool, even if it is
+frustrating to use. The other thing of note about xntpdc is that it
+provides a user interface to the run time reconfiguration facility.
+
+See the respective man pages for details on the use of these programs.
+The primary reason for mentioning them here is to point out an
+inconsistancy which can be awfully annoying if it catches you, and which
+is worth keeping firmly in mind. Both xntpdc and xntpd demand that
+anything which has dimensions of time be specified in units of seconds,
+both in the configuration file and when doing run time reconfiguration.
+Both programs also print the values in seconds. Ntpq on the other hand,
+obeys the standard by printing all time values in milliseconds. This
+makes the process of looking at values with ntpq and then changing them
+in the configuration file or with xntpdc very prone to errors (by three
+orders of magnitude). I wish this problem didn't exist, but xntpd and
+its love of seconds predate the mode-6 protocol and the latter's
+(Fuzzball-inspired) millisecond orientation, making the inconsistancy
+irresolvable without considerable work.
+
+Run Time Reconfiguration
+
+Xntpd was written specifically to allow its configuration to be fully
+modifiable at run time. Indeed, the only way to configure the server is
+at run time. The configuration file is read only after the rest of the
+server has been initialized into a running, but default unconfigured,
+state. This facility was included not so much for the benefit of Unix,
+where it is handy but not strictly essential, but rather for dedicated
+platforms where the feature is more important for maintenance.
+Nevertheless, run time configuration works very nicely for Unix servers
+as well.
+
+Nearly all of the things it is possible to configure in the
+configuration file may be altered via NTP mode-7 messages using the
+xntpdc program. Mode-6 messages may also provide some limited
+configuration functionality (though the only thing you can currently do
+with mode-6 messages is set the leap-second warning bits) and the ntpq
+program provides generic support for the latter. The leap bits that can be
+set in the leap_warning variable (up to one month ahead) and in the
+leap_indication variable have a slightly different encoding than the
+usual interpretation:
+
+ Value Action
+ 00 The daemon passes the leap bits of its
+ synchronisation source (usual mode of operation)
+ 01/10 A leap second is added/deleted
+ 11 Leap information from the sychronisation source
+ is ignored (thus LEAP_NOWARNING is passed on)
+
+Mode-6 and mode-7 messages which would modify the configuration of the
+server are required to be authenticated using standard NTP
+authentication. To enable the facilities one must, in addition to
+specifying the location of a keys file, indicate in the configuration
+file the key IDs to be used for authenticating reconfiguration commands.
+Hence the following fragment might be added to a configuration file to
+enable the mode-6 (ntpq) and mode-7 (xntpdc) facilities in the daemon:
+
+ # specify mode-6 and mode-7 trusted keys
+
+ requestkey 65535 # for mode-7 requests
+ controlkey 65534 # for mode-6 requests
+
+If the "requestkey" and/or the "controlkey" configuration declarations
+are omitted from the configuration file, the corresponding run time
+reconfiguration facility is disabled.
+
+The query programs require the user to specify a key ID and a key to use
+for authenticating requests to be sent. The key ID provided should be
+the same as the one mentioned in the configuration file, while the key
+should match that corresponding to the key ID in the keys file. As the
+query programs prompt for the key as a password, it is useful to make
+the request and control authentication keys typable (in ASCII format)
+from the keyboard.
+
+Name Resolution
+
+Xntpd includes the cability to specify host names requiring resolution
+in "peer" and "server" declarations in the configuration file. There are
+several reasons why this was not permitted in the past. Chief among
+these is the fact that name service is unreliable and the interface to
+the Unix resolver routines is synchronous. The hangups and delays
+resulting from name-resolver clanking can be unacceptable once the NTP
+server is running (and remember it is up and running before the
+configuration file is read). However, it is advantageous to resolve time
+server names, since their addresses are occasionally changed.
+
+Instead of running the resolver itself the daemon can defer this task to
+a separate program, xntpres. When the daemon comes across a "peer" or
+"server" entry with a non-numeric host address it records the relevant
+information in a temporary file and continues on. When the end of the
+configuration file has been reached and one or more entries requiring
+name resolution have been found, the server runs an instance of xntpres
+with the temporary file as an argument. The server then continues on
+normally but with the offending peers/servers omitted from its
+configuration.
+
+When xntpres successfully resolves a name from this file, it configures
+the associated entry into the server using the same mode-7 run time
+reconfiguration facility that xntpdc uses. If temporary resolver
+failures occur, xntpres will periodically retry the offending requests
+until a definite response is received. The program will continue to run
+until all entries have been resolved.
+There are several configuration requirements if xntpres is to be used.
+The path to the xntpres program must be made known to the daemon via a
+"resolver" configuration entry, and mode-7 run time reconfiguration must
+be enabled. The following fragment might be used to accomplish this:
+
+ # specify host name resolver data
+
+ resolver /local/etc/xntpres
+ keys /etc/ntp.keys
+ requestkey 65535
+
+Note that xntpres sends packets to the server with a source address of
+127.0.0.1. You should obviously avoid "restrict" modification requests
+from this address or xntpres will fail.
+
+Dealing with Frequency Tolerance Violations (Tickadj and Friends)
+
+The NTP Version 3 specification RFC 1305 calls for a maximum oscillator
+frequency tolerance of +-100 parts-per-million (ppm), which is
+representative of those components suitable for use in relatively
+inexpensive workstation platforms. For those platforms meeting this
+tolerance, NTP will automatically compensate for the frequency errors of
+the individual oscillator and no further adjustments are required,
+either to the configuration file or to various kernel variables.
+
+However, in the case of certain notorious platforms, in particular Sun
+4s, the 100-ppm tolerance is routinely violated. In such cases it may be
+necessary to adjust the values of certain kernel variables; in
+particular, "tick" and "tickadj". The variable tick is the increment in
+microseconds added to the system time on each interval-timer interrupt,
+while the variable tickadj is used by the time adjustment code as a slew
+rate. When the time is being adjusted via a call to the system routine
+adjtime(), the kernel increases or reduces tick by tickadj microseconds
+until the specified adjustment has been completed. Unfortunately, in
+most Unix implementations the tick increment must be either zero or
+plus/minus exactly tickadj microseconds, meaning that adjustments are
+truncated to be an integral multiple of tickadj (this latter behaviour
+is a misfeature, and is the only reason the xntpd code needs to concern
+itself with the internal implementation of adjtime() at all). In
+addition, the stock Unix implementation considers it an error to request
+another adjustment before a prior one has completed.
+
+Thus, to make very sure it avoids problems related to the roundoff, the
+xntpd daemon reads the values of tick and tickadj from /dev/kmem when it
+starts. It then ensures that all adjustments given to adjtime() are an
+even multiple of tickadj microseconds and computes the largest
+adjustment that can be completed in the adjustment interval (using both
+the value of tickadj and the value of tick) so it can avoid exceeding
+this limit.
+
+Unfortunately, the value of tickadj set by default is almost always too
+large for xntpd. NTP operates by continuously making small adjustments
+to the clock, usually at one-second intervals. If tickadj is set too
+large, the adjustments will disappear in the roundoff; while, if tickadj
+is too small, NTP will have difficulty if it needs to make an occasional
+large adjustment. While the daemon itself will read the kernel's values
+of tick and tickadj, it will not change the values, even if they are
+unsuitable. You must do this yourself before the daemon is started,
+either with adb or, in the running kernel only, with the tickadj program
+included in the util directory of the xntp3 distribution. Note that the
+latter program will also computes an optimal value of tickadj for NTP
+use based on the kernel's value of tick.
+
+The tickadj program can reset several other kernel variables if asked.
+It can also change the value of tick if asked, this being necessary on a
+few machines with very broken clocks, like Sun 4s. With these machines
+it should also set the value of the kernel dosynctodr variable to zero.
+This variable controls whether to synchronize the system clock to the
+time-of-day clock, something you really don't want to be happen when
+xntpd is trying to keep it under control.
+
+In order to maintain reasonable correctness bounds, as well as
+reasonably good accuracy with acceptable polling intervals, xntpd will
+complain if the frequency error is greater than 100 ppm. For machines
+with a value of tick in the 10-ms range, a change of one in the value of
+tick will change the frequency by about 100 ppm. In order to determine
+the value of tick for a particular CPU, disconnect the machine from all
+sources of time (dosynctodr = 0) and record its actual time compared to
+an outside source (eyeball-and-wristwatch will do) over a day or more.
+Multiply the time change over the day by 0.116 and add or subtract the
+result to tick, depending on whether the CPU is fast or slow. An example
+call to tickadj useful on Sun 4s is:
+
+ tickadj -t 9999 -a 5 -s
+
+which sets tick 100 ppm fast, tickadj to 5 microseconds and turns off
+the clock/calendar chip fiddle. This line can be added to the rc.local
+configuration file to automatically set the kernel variables at boot
+time.
+
+All this stuff about diddling kernel variables so the NTP daemon will
+work is really silly. If vendors would ship machines with clocks that
+kept reasonable time and would make their adjtime() system call apply
+the slew it is given exactly, independent of the value of tickadj, all
+this could go away.
+
+Tuning Your Subnet
+
+There are several parameters available for tuning the NTP subnet for
+maximum accuracy and minimum jitter. Two important parameters are the
+the "precision" and "prefer" configuration declarations. The precision
+declaration specifies the number of significant bits of the system clock
+representation relative to one second. For instance, the default value
+of -6 corresponds to 1/64 second or about 16 milliseconds.
+
+The NTP protocol makes use of the precision parameter in several places.
+It is included in packets sent to peers and is used by them to calculate
+the maximum absolute error and maximum statistical error. When faced
+with selecting one of several servers of the same stratum and about the
+same network path delay for synchronization purposes, clients will
+usually prefer to synchronize to those servers claiming the smallest
+(most negative) precision, since this maximizes the accuracy and
+minimizes the jitter apparent to application programs running on the
+client platform. Therefore, when the maximum attainable accuracy is
+required, it is important that every platform configure an accurate
+value for the precision variable. This can be done using the optional
+"precision" declaration in the configuration file:
+
+ # precision declaration
+
+ precision -18 # for microsecond clocks (Sun 4s, DEC 5000/240)
+
+When more than one eligible server exists, the NTP clock-selection and
+combining algorithms act to winnow out all except the "best" set of
+servers using several criteria based on differences between the readings
+of different servers and between successive readings of the same server.
+The result is usually a set of surviving servers that are apparently
+statistically equivalent in accuracy, jitter and stability. The
+population of survivors remaining in this set depends on the individual
+server characteristics measured during the selection process and may
+vary from time to time as the result of normal statistical variations.
+In LANs with high speed RISC-based time servers, the population can
+become somewhat unstable, with individual servers popping in and out of
+the surviving population, generally resulting in a regime called
+clockhopping.
+
+When only the smallest residual jitter can be tolerated, it may be
+convenient to elect one of the servers at each stratum level as the
+preferred one using the keyword "prefer" on the configuration
+declaration for the selected server:
+
+ # prefered server declaration
+
+ peer 128.4.1.1 prefer # preferred server
+
+The preferred server will always be included in the surviving
+population, regardless of its characteristics and as long as it survives
+preliminary sanity checks and validation procedures.
+
+The most useful application of the prefer keyword is in high speed LANs
+equipped with precision radio clocks, such as a GPS receiver. In order
+to insure robustness, the hosts need to include outside peers as well as
+the GPS-equipped server; however, as long as that server is running, the
+synchronization preference should be that server. The keyword should
+normally be used in all cases in order to prefer an attached radio
+clock. It is probably inadvisable to use this keyword for peers outside
+the LAN, since it interferes with the carefully crafted judgement of the
+selection and combining algorithms.
+
+Provisions for Leap Seconds and Accuracy Metrics
+
+Xntpd understands leap seconds and will attempt to take appropriate
+action when one occurs. In principle, every host running xntpd will
+insert a leap second in the local timescale in precise synchronization
+with UTC. This requires that the leap-warning bits be manually activated
+some time prior to the occurance of a leap second at the primary
+(stratum 1) servers. Subsequently, these bits are propagated throughout
+the subnet depending on these servers by the NTP protocol itself and
+automatically implemented by xntpd and the time-conversion routines of
+each host. The implementation is independent of the idiosyncracies of
+the particular radio clock, which vary widely among the various devices,
+as long as the idiosyncratic behavior does not last for more than about
+20 minutes following the leap. Provisions are included to modify the
+behavior in cases where this cannot be guaranteed.
+
+While provisions for leap seconds have been carefully crafted so that
+correct timekeeping immediately before, during and after the occurance
+of a leap second is scrupulously correct, stock Unix systems are mostly
+inept in responding to the available information. This caveat goes also
+for the maximum-error and statistical-error bounds carefully calculated
+for all clients and servers, which could be very useful for application
+programs needing to calibrate the delays and offsets to achieve a near-
+simulataneous commit procedure, for example. While this information is
+maintained in the xntpd data structures, there is at present no way for
+application programs to access it. This may be a topic for further
+development.
+
+Clock Support Overview
+
+Xntpd was designed to support radio (and other external) clocks and does
+some parts of this function with utmost care. Clocks are treated by the
+protocol as ordinary NTP peers, even to the point of referring to them
+with an (invalid) IP host address. Clock addresses are of the form
+127.127.t.u, where t specifies the particular type of clock (i.e. refers
+to a particular clock driver) and u is a unit number whose
+interpretation is clock-driver dependent. This is analogous to the use
+of major and minor device numbers by Unix and permits multiple
+instantiations of clocks of the same type on the same server, should
+such magnificant redundancy be required.
+
+Because clocks look much like peers, both configuration file syntax and
+run time reconfiguration commands can be be used to control clocks in
+the same way as ordinary peers. Clocks are configured via "server"
+declarations in the configuration file, can be started and stopped using
+xntpdc and are subject to address-and-mask restrictions much like a
+normal peer, should this stretch of imagination ever be useful. As a
+concession to the need to sometimes transmit additional information to
+clock drivers, an additional configuration file is available: the
+"fudge" statement. This enables one to specify the values two time
+quantities, two integral values and two flags, the use of which is
+dependent on the particular clock driver. For example, to configure a
+PST radio clock which can be accessed through the serial device
+/dev/pst1, with propagation delays to WWV and WWVH of 7.5 and 26.5
+milliseconds, respectively, on a machine with an imprecise system clock
+and with the driver set to disbelieve the radio clock once it has gone
+30 minutes without an update, one might use the following configuration
+file entries:
+
+ # radio clock fudge fiddles
+
+ server 127.127.3.1
+ fudge 127.127.3.1 time1 0.0075 time2 0.0265
+ fudge 127.127.3.1 value2 30 flag1 1
+
+Additional information on the interpretation of these data with respect
+to various radio clock drivers is given in the xntpd.8 man page.
+
+Towards the Ultimate Tick
+
+This section consideres issues in providing precision time
+synchronization in NTP subnets which need the highest quality time
+available in the present technology. These issues are important in
+subnets supporting real-time services such as distributed multimedia
+conferencing and wide-are experiment control and monitoring.
+
+In the Internet of today synchronization paths often span continents and
+oceans with moderate to high variations in delay due to traffic spasms.
+NTP is specifically designed to minimize timekeeping jitter due to delay
+variations using intricately crafted filtering and selection algorithms;
+however, in cases where these variations are as much as a second or
+more, the residual jitter following these algorithms may still be
+excessive. Sometimes, as in the case of some isolated NTP subnets where
+a local source of precision time is available, such as a 1-pps signal
+produced by a calibrated cesium clock, it is possible to remove the
+jitter and retime the local clock oscillator of the NTP server. This has
+turned out to be a useful feature to improve the synchronization quality
+of time distributed in remote places where radio clocks are not
+available. In these cases special features of the xntp3 distribution are
+used together with the 1-pps signal to provide a jitter-free timing
+signal, while NTP itself is used to provide the coarse timing and
+resolve the seconds numbering.
+
+Most available radio clocks can provide time to an accuracy in the order
+of milliseconds, depending on propagation conditions, local noise levels
+and so forth. However, as a practical matter, all clocks can
+occasionally display errors significantly exceeding nominal
+specifications. Usually, the algorithms used by NTP for ordinary network
+peers, as well as radio clock "peers" will detect and discard these
+errors as discrepancies between the disciplined local clock oscillator
+and the decoded time message produced by the radio clock. Some radio
+clocks can produce a special 1-pps signal which can be interfaced to the
+server platform in a number of ways and used to substantially improve
+the (disciplined) clock oscillator jitter and wander characteristics by
+at least an order of magnitude. Using these features it is possible to
+achieve accuracies in the order of 100 microseconds with a fast RISC-
+based platform.
+
+There are three ways to implement 1-pps support, depending on the radio
+clock model, platform model and serial line interface. Each of these
+requires circuitry to convert the TTL signal produced by most clocks to
+the the EIA levels used by most serial interfaces. An example of a
+device designed to do this is presented in the gadget subdirectory
+included in the xntp3 distribtuion. Besides being useful for this
+purpose, this device includes an inexpensive modem designed for use with
+the Canadian CHU time/frequency radio station.
+
+In order to select the appropriate implementation, it is important to
+understand the underlying 1-pps mechanism used by xntpd. The 1-pps
+suport depends on a continuous source of 1-pps pulses used to calculate
+an offset within +-500 milliseconds relative to the local clock. The
+serial timecode produced by the radio or the time determined by NTP in
+absence of the radio is used to adjust the local clock within +-128
+milliseconds of the actual time. As long as the local clock is within
+this interval the 1-pps support is used to discipline the local clock
+and the timecode used only to verify that the local clock is in fact
+within the interval. Outside this interval the 1-pps support is disabled
+and the timecode used directly to control the local clock.
+
+The first method of implementation uses a dedicated serial port and
+either the bsd line discipline or System V streams module, which can be
+found in the kernel directory of the xntp3 distribution. This method can
+be used with any radio clock or in the absence of any clock. The line
+discipline and streams modules take receive timestamps in the kernel,
+specifically the interrupt routine of the serial port hardware driver.
+Using this method the port is dedicated to serve the 1-pps signal and
+cannot be used for other purposes. Instructions for implementing the
+feature, which requires rebuilding the kernel, are included in the
+modules themselves. Note that xndpd must be compiled with the -DPPSDEV
+compiler switch in this case. There is an inherent error in this method
+due to the latency of the interrupt system and remaining serial-line
+protocol modules in the order of a millisecond with Sun 4s. While the
+jitter in this latency is unavoidable, the systematic component can be
+calibrated out using a special configuration declaration:
+
+ # pps delay and baud rate
+
+ pps delay .0017 baud 19200 # pps delay (ms) and baud rate
+
+Note that the delay defaults to zero and the baud to 38400.
+
+The second method uses mechanisms embedded in the radio clock driver,
+which call the 1-pps support directly and do not require a dedicated
+serial port. Currently, only the DCF77 (German radio time service)
+driver uses this method. Instructions for implementing this are given in
+README files in the xntp3 distribution.
+
+The third method and the most accurate and intrusive of all uses the
+carrier-detect modem-control lead monitored by the serial port driver.
+This method can be used with any radio clock and 1-pps interface
+mentioned above. It requires in addition to a special streams module,
+replacement of the kernel high resolution time-of-day clock routine.
+This method is applicable only to Sun 4 platforms running SunOS 4.1.1
+and then only with either of the two onboard serial ports. It does not
+work with other platforms, operating systems or external (SBus) serial
+multiplexors.
+
+Swatting Bugs
+
+Let's say you have compiled and installed the code and put up an
+apparently relevant configuration file. In many Unix systems the xntpd
+daemon and utility programs (ntpq, ntptrace and xntpdc) are usually
+installed in the /usr/local directory along with the key file
+(ntp.keys), while the configuration file (ntp.conf) and drift file
+(ntp.drift) are installed in the /etc directory. The daemon can is
+usually started from the rc.local shell script at system boot time, but
+could be started (and stopped) at other times for debugging, etc. How do
+you verify that the daemon can form associations with remote peers and
+verify correct synchronization? For this you need the ntpq utility
+described in the ntpq.8 man page.
+
+After starting the daemon, run the ntpq program using the -n switch,
+which will avoid possible distractions due to name resolutions. Use the
+peer command to display a billboard showing the status of configured
+peers and possibly other clients poking the daemon. After operating for
+a few minutes, the display should be something like:
+
+ remote refid st when poll reach delay offset disp
+========================================================================
++128.4.2.6 132.249.16.1 2 131 256 373 9.89 16.28 23.25
+*128.4.1.20 .WWVB. 1 137 256 377 280.62 21.74 20.23
+-128.8.2.88 128.8.10.1 2 49 128 376 294.14 5.94 17.47
++128.4.2.17 .WWVB. 1 173 256 377 279.95 20.56 16.40
+
+The hosts shown in the "remote" column should agree with the entries in
+the configuration file, plus any peers not mentioned in the file at the
+same or lower than your stratum that happen to be configured to peer
+with you. The "refid" entry shows the current source of synchronization
+for that peer, while the "st" reveals its stratum and the "poll" entry
+the polling interval, in seconds. The "when" entry shows the time since
+the peer was last heard, in seconds, while the "reach" entry shows the
+status of the reachability register (see specification), which is in
+octal format. The remaining entries show the latest delay, offset and
+dispersion computed for the peer, in milliseconds.
+
+*** This section incomplete. Soon.
+
+status=0664 leap_none, sync_ntp, 6 events, event_peer/strat_chg
+system="UNIX", leap=00, stratum=2, rootdelay=280.62,
+rootdispersion=45.26, peer=11673, refid=128.4.1.20,
+reftime=af00bb42.56111000 Fri, Jan 15 1993 4:25:38.336, poll=8,
+clock=af00bbcd.8a5de000 Fri, Jan 15 1993 4:27:57.540, phase=21.147,
+freq=13319.46, compliance=2
+
+status=7414 reach, auth, sel_sync, 1 event, event_reach
+srcadr=128.4.2.6, srcport=123, dstadr=128.4.2.7, dstport=123, keyid=1,
+stratum=2, precision=-10, rootdelay=362.00, rootdispersion=21.99,
+refid=132.249.16.1,
+reftime=af00bb44.849b0000 Fri, Jan 15 1993 4:25:40.517,
+delay= 9.89, offset= 16.28, dispersion=23.25, reach=373, valid=8,
+hmode=2, pmode=1, hpoll=8, ppoll=10, leap=00, flash=0x0,
+org=af00bb48.31a90000 Fri, Jan 15 1993 4:25:44.193,
+rec=af00bb48.305e3000 Fri, Jan 15 1993 4:25:44.188,
+xmt=af00bb1e.16689000 Fri, Jan 15 1993 4:25:02.087,
+filtdelay= 16.40 9.89 140.08 9.63 9.72 9.22 10.79 122.99,
+filtoffset= 13.24 16.28 -49.19 16.04 16.83 16.49 16.95 -39.43,
+filterror= 16.27 20.17 27.98 31.89 35.80 39.70 43.61 47.52
+
+ind assID status conf reach auth condition last_event cnt
+===========================================================
+ 1 11670 7414 no yes ok synchr. reachable 1
+ 2 11673 7614 no yes ok sys.peer reachable 1
+ 3 11833 7314 no yes ok outlyer reachable 1
+ 4 11868 7414 no yes ok synchr. reachable 1
+
+Parting Shots
+
+There are several undocumented programs which are useful if you are
+trying to set up a clock. They can be found in the clockstuff directory
+of the xntp3 distribution. The most useful of these is the propdelay
+program, which can compute high frequency radio propagation delays
+between any two points whose latitude and longitude are known. The
+program understands something about the phenomena which allow high
+frequency radio propagation to occur, and will generally provide a
+better estimate than a calculation based on the great circle distance.
+The other two programs in the directory are clktest, which allows one to
+exercise the generic clock line discipline, and chutest, which runs the
+basic reduction algorithms used by the daemon on data received from a
+serial port.
diff --git a/usr.sbin/xntpd/doc/ntpdate.8 b/usr.sbin/xntpd/doc/ntpdate.8
new file mode 100644
index 0000000..7afa79a
--- /dev/null
+++ b/usr.sbin/xntpd/doc/ntpdate.8
@@ -0,0 +1,189 @@
+''' $Header
+'''
+.de Sh
+.br
+.ne 5
+.PP
+\fB\\$1\fR
+.PP
+..
+.de Sp
+.if t .sp .5v
+.if n .sp
+..
+.de Ip
+.br
+.ie \\n.$>=3 .ne \\$3
+.el .ne 3
+.IP "\\$1" \\$2
+..
+'''
+''' Set up \*(-- to give an unbreakable dash;
+''' string Tr holds user defined translation string.
+''' Greek uppercase omega is used as a dummy character.
+'''
+.tr \(*W-|\(bv\*(Tr
+.ie n \{\
+.ds -- \(*W-
+.if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch
+.if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch
+.ds L" ""
+.ds R" ""
+.ds L' '
+.ds R' '
+'br\}
+.el\{\
+.ds -- \(em\|
+.tr \*(Tr
+.ds L" ``
+.ds R" ''
+.ds L' `
+.ds R' '
+'br\}
+.TH NTPDATE 8 LOCAL
+.SH NAME
+ntpdate - set the date and time via NTP
+.SH SYNOPSIS
+.B ntpdate
+[
+.B -bds
+] [
+.B -o
+.I version
+] [
+.B -a
+.I key#
+] [
+.B -e
+.I authdelay
+] [
+.B -k
+.I keyfile
+] [
+.B -p
+.I samples
+] [
+.B -t
+.I timeout
+]
+server ...
+.SH DESCRIPTION
+.I Ntpdate
+sets the local date and time by polling the Network Time Protocol
+server(s) on the host(s) given as arguments to determine
+the correct time. It must be run as root on the local host. A number
+of samples are obtained from each of the servers specified and the
+standard NTP clock filter and selection algorithms are applied to select
+the best of these. Typically,
+.I ntpdate
+can be inserted in the
+.I /etc/rc.local
+startup up script to set the time of day at boot time and/or can be run
+from time\-to\-time via
+.IR cron (8).
+Note that
+.IR ntpdate 's
+reliability and precision will improve dramatically with greater numbers
+of servers. While a single server may be used, better performance and
+greater resistance to insanity on the part of any one server
+will be obtained by providing at least three or four servers, if not more.
+.PP
+Time adjustments are made by
+.I ntpdate
+in one of two ways. If
+.I ntpdate
+determines your clock is off by more than 0.5 seconds it will simply
+step the time by calling
+.IR settimeofday (2).
+If the error is less than 0.5 seconds, however, it will by default slew
+the clock's time via a call to
+.IR adjtime (2)
+with the offset. The latter technique is less disruptive and more
+accurate when the offset is small, and works quite well when
+.I ntpdate
+is run by
+.I cron (8)
+every hour or two. The adjustment made in the latter
+case is actually 50% larger than the measured offset since this will
+tend to keep a badly drifting clock more accurate (at some expense to
+stability, though this tradeoff is usually advantageous). At boot time,
+however, it is usually better to always step the time. This can be forced
+in all cases by specifying the
+.B -b
+switch on the command line. The
+.B -s
+switch tells
+.I ntpdate
+to log its actions via the
+.IR syslog (3)
+facility rather than to the standard output, a useful option when
+running the program from
+.IR cron (8).
+.PP
+The
+.B -d
+flag may be used to determine what
+.I ntpdate
+will do without it actually doing it. Information useful for general
+debugging will also be printed. By default
+.I ntpdate
+claims to be an NTP version 3 implementation in its outgoing packets. As
+some older software will decline to respond to version 3 queries, the
+.B -o
+.I version
+switch can be used to force the program to poll as a version 2 or 1
+implementation instead.
+.PP
+The number of samples
+.I ntpdate
+acquires from each server can be set to between 1 and 8 inclusive
+using the
+.B -p
+switch. The default is 4. The time it will spend waiting for a
+response can be set using the
+.B -t
+switch, and will be rounded to a multiple of 0.2 seconds. The default
+is 1 second, a value suitable for polling across a LAN.
+.PP
+.I Ntpdate
+will authenticate its transactions if need be. The
+.B -a
+switch specifies that all packets should be authenticated using the
+key number indicated. The
+.B -k
+switch allows the name of the file from which the keys may be read
+to be modified from the default of
+.I /etc/ntp.keys.
+This file should be in the format described in
+.IR xntpd (8).
+The
+.B -e
+option allows the specification of an authentication processing delay,
+in seconds (see
+.IR xntpd (8)
+for details). This number is usually small enough to be negligible for
+.IR ntpdate 's
+purposes, though specifying a value may improve timekeeping on very slow
+CPU's.
+.PP
+.I Ntpdate
+will decline to set the date if an NTP server daemon (e.g.
+.IR xntpd (8))
+is running on the same host. When running
+.I ntpdate
+on a regular basis from
+.IR cron (8)
+as an alternative to running a daemon, doing so once every hour or two
+will result in precise enough timekeeping to avoid stepping the clock.
+.SH FILES
+.nf
+/etc/ntp.keys\0\0contains the encryption keys used by \fIntpdate\fP.
+.fi
+.SH SEE ALSO
+xntpd(8)
+.SH HISTORY
+Written by Dennis Ferguson at the University of Toronto
+.SH BUGS
+The technique used for improving accuracy by compensating for clock
+oscillator errors sucks, but doing better would require the program
+to save state from previous runs.
diff --git a/usr.sbin/xntpd/doc/ntpq.8 b/usr.sbin/xntpd/doc/ntpq.8
new file mode 100644
index 0000000..e34e478
--- /dev/null
+++ b/usr.sbin/xntpd/doc/ntpq.8
@@ -0,0 +1,563 @@
+''' $Header
+'''
+.de Sh
+.br
+.ne 5
+.PP
+\fB\\$1\fR
+.PP
+..
+.de Sp
+.if t .sp .5v
+.if n .sp
+..
+.de Ip
+.br
+.ie \\n.$>=3 .ne \\$3
+.el .ne 3
+.IP "\\$1" \\$2
+..
+'''
+''' Set up \*(-- to give an unbreakable dash;
+''' string Tr holds user defined translation string.
+''' Greek uppercase omega is used as a dummy character.
+'''
+.tr \(*W-|\(bv\*(Tr
+.ie n \{\
+.ds -- \(*W-
+.if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch
+.if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch
+.ds L" ""
+.ds R" ""
+.ds L' '
+.ds R' '
+'br\}
+.el\{\
+.ds -- \(em\|
+.tr \*(Tr
+.ds L" ``
+.ds R" ''
+.ds L' `
+.ds R' '
+'br\}
+.TH NTPQ 8 LOCAL
+.SH NAME
+ntpq - standard Network Time Protocol query program
+.SH SYNOPSIS
+.B ntpq
+[
+.B -inp
+] [
+.B -c
+.I command
+] [
+.I host
+] [
+.I ...
+]
+.SH DESCRIPTION
+.I Ntpq
+is used to query NTP servers which implement the recommended NTP mode 6
+control message format about current state and to request changes in
+that state. The program may be run either in interactive mode or
+controlled using command line arguments. Requests to read and write
+arbitrary variables can be assembled, with raw and pretty\-printed
+output options being available.
+.I Ntpq
+can also obtain and print a list of peers in a common format by sending
+multiple queries to the server.
+.PP
+If one or more request options is included on the command line when
+.I ntpq
+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
+.I localhost
+by default. If no request options are given,
+.I ntpq
+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
+.I localhost
+when no other host is specified.
+.I Ntpq
+will prompt for commands if the standard input is a terminal device.
+.PP
+.I Ntpq
+uses NTP mode 6 packets to communicate with the NTP server, and hence
+can be used to query any compatable server on the network which permits
+it. Note that since NTP is a UDP protocol this communication will be
+somewhat unreliable, especially over large distances in terms of network
+topology.
+.I Ntpq
+makes one attempt to retransmit requests, and will time requests out if
+the remote host is not heard from within a suitable time out time.
+.PP
+Command line options are described following. Specifying a command line
+option other than
+.B -i
+or
+.B -n
+will cause the specified query (queries) to be sent to the indicated
+host(s) immediately. Otherwise,
+.I ntpq
+will attempt to read interactive format commands from the standard
+input.
+.Ip -c 8
+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
+.B -c
+options may be given.
+.Ip -i 8
+Force
+.I ntpq
+to operate in interactive mode. Prompts will be written to the standard
+output and commands read from the standard input.
+.Ip -n 8
+Output all host addresses in dotted\-quad numeric format rather than
+converting to the canonical host names.
+.Ip -p 8
+Print a list of the peers known to the server as well as a summary of
+their state. This is equivalent to the \*(L"peers\*(R" interactive
+command.
+.SH INTERNAL COMMANDS
+.PP
+Interactive format commands consist of a keyword followed by zero to
+four arguments. Only enough characters of the full keyword to uniquely
+identify the command need be typed. The output of a command is normally
+sent to the standard output, but optionally the output of individual
+commands may be sent to a file by appending a \*(L">\*(R", followed by a
+file name, to the command line.
+.PP
+A number of interactive format commands are executed entirely within the
+.I ntpq
+program itself and do not result in NTP mode 6 requests being sent to a
+server. These are described following.
+.PP
+.B ?
+[
+.I command_keyword
+]
+.PP
+A \*(L"?\*(R" by itself will print a list of all the command keywords
+known to this incarnation of
+.IR ntpq .
+A \*(L"?\*(R" followed by a command keyword will print function and
+usage information about the command. This command is probably a better
+source of information about
+.I ntpq
+than this manual page.
+.PP
+.B timeout
+.I millseconds
+.PP
+Specify a time out period for responses to server queries. The default
+is about 5000 milliseconds. Note that since
+.I ntpq
+retries each query once after a time out the total waiting time for a
+time out will be twice the time out value set.
+.PP
+.B delay
+.I milliseconds
+.PP
+Specify a time interval to be added to timestamps included in requests
+which require authentication. This is used to enable (unreliable) server
+reconfiguration over long delay network paths or between machines whose
+clocks are unsynchronized. Actually the server does not now require time
+stamps in authenticated requests, so this command may be obsolete.
+.PP
+.B host
+.I hostname
+.PP
+Set the host to which future queries will be sent.
+.I Hostname
+may be either a host name or a numeric
+address.
+.PP
+.B poll
+[
+.I #
+] [
+.B verbose
+]
+.PP
+Poll the current server in client mode. The first argument is the number
+of times to poll (default is 1) while the second argument may be given
+to obtain a more detailed output of the results. This command is
+currently just wishful thinking.
+.PP
+.B keyid
+.I #
+.PP
+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.
+.PP
+.B passwd
+.PP
+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.
+.PP
+.B "hostnames yes|no"
+.PP
+If \*(L"yes\*(R" is specified, host names are printed in information
+displays. If \*(L"no\*(R" is given, numeric addresses are printed
+instead. The default is \*(L"yes\*(R" unless modified using the command
+line
+.B -n
+switch.
+.PP
+.B raw
+.PP
+Causes all output from query commands is printed as received from the
+remote server. The only formating/intepretation done on the data is to
+transform nonascii data into a printable (but barely understandable)
+form.
+.PP
+.B cooked
+.PP
+Causes output from query commands to be \*(L"cooked\*(R". Variables
+which are recognized by the server will have their values reformatted
+for human consumption. Variables which
+.I ntpq
+thinks should have a decodeable value but didn't are marked with a
+trailing \*(L"?\*(R".
+.PP
+.B ntpversion
+.B 1|2|3
+.PP
+Sets the NTP version number which
+.I ntpq
+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.
+.PP
+.B authenticate
+.B yes|no
+.PP
+Normally
+.I ntpq
+does not authenticate requests unless they are write requests. The
+command
+.B authenticate yes
+causes
+.I ntpq
+to send authentication with all requests it makes. Authenticated
+requests causes some servers to handle requests slightly differently,
+and can occasionally melt the CPU in fuzzballs if you turn
+authentication on before doing a peer display.
+.PP
+.B addvars
+.IR <variable_name>[=<value>] [,...]
+.B rmvars
+.IR <variable_name> [,...]
+.B clearvars
+.PP
+The data carried by NTP mode 6 messages consists of a list of items of
+the form
+.IP "" 8
+<variable_name>=<value>
+.PP
+where the \*(L"=<value>\*(R" is ignored, and can be omitted, in requests
+to the server to read variables.
+.I Ntpq
+maintains an internal list in which data to be included in control
+messages can be assembled, and sent using the
+.B readlist
+and
+.B writelist
+commands described below. The
+.B 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
+.B rmvars
+command can be used to remove individual variables from the list, while
+the
+.B clearlist
+command removes all variables from the list.
+.PP
+.B debug
+.I more|less|off
+.PP
+Turns internal query program debugging on and off.
+.PP
+.B quit
+.PP
+Exit
+.IR ntpq .
+.SH CONTROL MESSAGE COMMANDS
+.PP
+Each peer known to an NTP server has a 16 bit integer
+.I association
+.I 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
+.B peers
+command, which will send a preprogrammed series of messages to obtain
+the data it needs, and the
+.B mreadlist
+and
+.B mreadvar
+commands, which will iterate over a range of associations.
+.PP
+.B associations
+.PP
+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. Note that the data returned by the \*(L"associations\*(R"
+command is cached internally in
+.IR ntpq .
+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
+.I &index
+may be used as an alternative.
+.PP
+.B lassocations
+.PP
+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 \*(L"associations\*(R" 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 \*(L"associations\*(R" command is used, but are included in the
+output of \*(L"lassociations\*(R".
+.PP
+.B passociations
+.PP
+Prints association data concerning in\-spec peers from the internally
+cached list of associations. This command performs identically to the
+\*(L"associations\*(R" except that it displays the internally stored
+data rather than making a new query.
+.PP
+.B lpassociations
+.PP
+Print data for all associations, including out\-of\-spec client
+associations, from the internally cached list of associations. This
+command differs from \*(L"passociations\*(R" only when dealing with
+fuzzballs.
+.PP
+.B pstatus
+.I assocID
+.PP
+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 pidgin English.
+.PP
+.B readvar
+[
+.I assocID
+] [
+.IR <variable_name>[=<value>] [,...]
+]
+.PP
+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.
+.PP
+.B rv
+[
+.I assocID
+] [
+.IR <variable_name>[=<value>] [,...]
+]
+.PP
+An easy\-to\-type short form for the
+.B readvar
+command.
+.PP
+.B writevar
+.I assocID
+.IR <variable_name>=<value> [,...]
+.PP
+Like the
+.B readvar
+request, except the specified variables are written instead of read.
+.PP
+.B readlist
+[
+.I assocID
+]
+.PP
+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.
+.PP
+.B rl
+[
+.I assocID
+]
+.PP
+An easy\-to\-type short form of the
+.B readlist
+command.
+.PP
+.B writelist
+[
+.I assocID
+]
+.PP
+Like the
+.B readlist
+request, except the internal list variables are written instead of read.
+.PP
+.B mreadvar
+.I assocID
+.I assocID
+[
+.IR <variable_name>[=<value>] [,...]
+]
+.PP
+Like the
+.B 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
+.B associations
+command.
+.PP
+.B mrv
+.I assocID
+.I assocID
+[
+.IR <variable_name>[=<value>] [,...]
+]
+.PP
+An easy\-to\-type short form of the
+.B mreadvar
+command.
+.PP
+.B mreadlist
+.I assocID
+.I assocID
+.PP
+Like the
+.B 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
+.B associations
+command.
+.PP
+.B mrl
+.I assocID
+.I assocID
+.PP
+An easy\-to\-type short form of the
+.B mreadlist
+command.
+.PP
+.B clockvar
+[
+.I assocID
+]
+[
+.IR <variable_name>[=<value>] [,...]
+]
+.PP
+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 \*(L"system clock\*(R" 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.
+.PP
+.B cv
+[
+.I assocID
+]
+[
+.IR <variable_name>[=<value>] [,...]
+]
+.PP
+An easy\-to\-type short form of the
+.B clockvar
+command.
+.PP
+.B peers
+.PP
+Obtains a list of in\-spec peers of the server, along with a summary of
+each peer's state. Summary information includes the address of the
+remote peer, the reference ID (0.0.0.0 if the refID is unknown), the
+stratum of the remote peer, the type of the peer (local, unicast,
+multicast or broadcast), when the last packet was received, the polling
+interval, in seconds, the reachability register, in octal, and the
+current estimated delay, offset and dispersion of the peer, all in
+seconds.
+.PP
+The character in the left margin indicates the fate of this peer in the
+clock selection process. The codes mean: <sp> discarded due to high
+stratum and/or failed sanity checks; \*(L"x\*(R" designated falsticker
+by the intersection algorithm; \*(L".\*(R" culled from the end of the
+candidate list; \*(L"-\*(R" discarded by the clustering algorithm;
+\*(L"+\*(R" included in the final selection set; \*(L"#\*(R" selected
+for synchronization but distance exceeds maximum; \*(L"*\*(R" selected
+for synchronization; and \*(L"o\*(R" selected for synchronization, pps
+signal in use.
+.PP
+Note that since the
+.B peers
+command depends on the ability to parse the values in the responses it
+gets it may fail to work from time to time with servers which poorly
+control the data formats.
+.PP
+The contents of the host field may be one of four forms. It may be a
+host name, an IP address, a reference clock implementation name with its
+parameter or \*(L"REFCLK(<implementation number>, <parameter>)\*(R". On
+\*(L"hostnames no\*(R" only IP\-addresses will be displayed.
+.PP
+.B lpeers
+.PP
+Like
+.BR 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.
+.PP
+.B opeers
+.PP
+An old form of the \*(L"peers\*(R" command with the reference ID
+replaced by the local interface address.
+.SH HISTORY
+.PP
+Written by Dennis Ferguson at the University of Toronto.
+.SH BUGS
+.PP
+The
+.B peers
+command is non\-atomic and may occasionally result in spurious error
+messages about invalid associations occurring and terminating the
+command.
+.PP
+The timeout time is a fixed constant, which means you wait a long time
+for time outs since it assumes sort of a worst case. The program should
+improve the time out estimate as it sends queries to a particular host,
+but doesn't.
diff --git a/usr.sbin/xntpd/doc/ntptrace.8 b/usr.sbin/xntpd/doc/ntptrace.8
new file mode 100644
index 0000000..a79a679
--- /dev/null
+++ b/usr.sbin/xntpd/doc/ntptrace.8
@@ -0,0 +1,104 @@
+''' $Header
+'''
+.de Sh
+.br
+.ne 5
+.PP
+\fB\\$1\fR
+.PP
+..
+.de Sp
+.if t .sp .5v
+.if n .sp
+..
+.de Ip
+.br
+.ie \\n.$>=3 .ne \\$3
+.el .ne 3
+.IP "\\$1" \\$2
+..
+'''
+''' Set up \*(-- to give an unbreakable dash;
+''' string Tr holds user defined translation string.
+''' Greek uppercase omega is used as a dummy character.
+'''
+.tr \(*W-|\(bv\*(Tr
+.ie n \{\
+.ds -- \(*W-
+.if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch
+.if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch
+.ds L" ""
+.ds R" ""
+.ds L' '
+.ds R' '
+'br\}
+.el\{\
+.ds -- \(em\|
+.tr \*(Tr
+.ds L" ``
+.ds R" ''
+.ds L' `
+.ds R' '
+'br\}
+.TH NTPTRACE 8 LOCAL
+.SH NAME
+ntptrace - trace a chain of NTP hosts back to their master time source
+.SH SYNOPSIS
+.B ntptrace
+[
+.B -vdn
+] [
+.B -r
+.I retries
+] [
+.B -t
+.I timeout
+] [
+.I server
+]
+.SH DESCRIPTION
+.I Ntptrace
+determines where a given Network Time Protocol (NTP) server gets
+its time from, and follows the chain of NTP servers back to their
+master time source.
+If given no arguments, it starts with ``localhost.''
+.PP
+Here is an example of the output from
+.IR ntptrace :
+.RS 2
+.nf
+
+% ntptrace
+localhost: stratum 4, offset 0.0019529, synch distance 0.144135
+server2.bozo.com: stratum 2, offset 0.0124263, synch distance 0.115784
+usndh.edu: stratum 1, offset 0.0019298, synch distance 0.011993, refid 'WWVB'
+
+.fi
+.RE
+On each line, the fields are (left to right): the host name, the
+host's stratum,
+the time offset between that host and the local host
+(as measured by
+.IR ntptrace ;
+this is why it is not always zero for ``localhost''),
+the host's ``synchronization distance,''
+and (only for stratum-1 servers) the reference clock ID. All times
+are given in seconds. (Synchronization distance is a measure of the
+goodness of the clock's time.)
+.SH OPTIONS
+.IP "\-d" 5
+Turns on some debugging output.
+.IP "\-n" 5
+Turns off the printing of host names; instead, host IP addresses
+are given. This may be necessary if a nameserver is down.
+.IP "\-r retries" 5
+Sets the number of retransmission attempts for each host; default = 5.
+.IP "\-t timeout" 5
+Sets the retransmission timeout (in seconds); default = 2.
+.IP "\-v" 5
+Prints verbose information about the NTP servers.
+.SH SEE ALSO
+xntpd(8), xntpdc(8)
+.SH BUGS
+This program makes no attempt to improve accuracy by doing multiple
+samples.
diff --git a/usr.sbin/xntpd/doc/tickadj.8 b/usr.sbin/xntpd/doc/tickadj.8
new file mode 100644
index 0000000..4ee458f
--- /dev/null
+++ b/usr.sbin/xntpd/doc/tickadj.8
@@ -0,0 +1,143 @@
+''' $Id$
+'''
+.de Sh
+.br
+.ne 5
+.PP
+\fB\\$1\fR
+.PP
+..
+.de Sp
+.if t .sp .5v
+.if n .sp
+..
+.de Ip
+.br
+.ie \\n.$>=3 .ne \\$3
+.el .ne 3
+.IP "\\$1" \\$2
+..
+'''
+''' Set up \*(-- to give an unbreakable dash;
+''' string Tr holds user defined translation string.
+''' Greek uppercase omega is used as a dummy character.
+'''
+.tr \(*W-|\(bv\*(Tr
+.ie n \{\
+.ds -- \(*W-
+.if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch
+.if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch
+.ds L" ""
+.ds R" ""
+.ds L' '
+.ds R' '
+'br\}
+.el\{\
+.ds -- \(em\|
+.tr \*(Tr
+.ds L" ``
+.ds R" ''
+.ds L' `
+.ds R' '
+'br\}
+.TH TICKADJ 8 LOCAL
+.SH NAME
+tickadj - fiddle time\-related variables in the kernel
+.SH SYNOPSIS
+.B tickadj
+[
+.B -Aqs
+] [
+.B -a
+.I new_tickadj
+] [
+.B -t
+.I new_tick
+]
+.SH DESCRIPTION
+The
+.I tickadj
+program reads, and optionally modifies, several time\-keeping\-related
+variables in the running kernel, via
+.IR /dev/kmem .
+The particular variables it is concerned with are
+.IR tick ,
+which is the number of microseconds added to the system time during a
+clock interrupt,
+.IR tickadj ,
+which sets the slew rate and resolution used by the
+.IR adjtime (2)
+system call, and
+.IR dosynctodr ,
+which indicates to the kernels on some machines whether they should internally
+adjust the system clock to keep it in line with time\-of\-day clock
+or not.
+.PP
+By default, with no arguments,
+.I tickadj
+reads the variables of interest in the kernel and prints them. At the
+same time it determines an \*(L"optimal\*(R" value for the value of the
+.I tickadj
+variable if the intent is to run the
+.IR xntpd (8)
+Network Time Protocol daemon, and prints this as well. Since the operation
+of
+.I tickadj
+when reading the kernel mimics the operation of similar parts of the
+.IR xntpd (8)
+program fairly closely, this is useful for doing debugging of problems
+with
+.IR xntpd (8).
+.PP
+Various flags may be specified to change the variables of interest in
+the running kernel. The
+.B -a
+flag allows one to set the variable
+.I tickadj
+to the value specified as an argument. The
+.B -A
+flag will also cause
+.I tickadj
+to be modified, but instead will set it to the internally computed
+\*(L"optimal\*(R" value. The
+.B -t
+flag may be used to reset the kernel's value of
+.IR tick ,
+a capability which is useful on machines with very broken clocks. The
+.B -s
+flag tells the program to set the value of the variable
+.I dosynctodr
+to zero, a prerequisite for running the
+.IR xntpd (8)
+daemon under SunOS 4.0. Normally
+.I tickadj
+is quite verbose about what it is doing. The
+.B -q
+flag tells it to shut up about everything except errors.
+.PP
+Note that
+.I tickadj
+should be run with some caution when being used for the first time on
+different types of machines. The operations which
+.I tickadj
+trys to perform are not guaranteed to work on all Unix machines.
+.SH FILES
+.nf
+/vmunix
+/unix
+/dev/kmem
+.fi
+.SH SEE ALSO
+xntpd(8)
+.SH HISTORY
+Written by Dennis Ferguson at the University of Toronto
+.SH BUGS
+Fiddling with kernel variables at run time as a part of ordinary
+operations is a hideous practice which is only necessary to make
+up for deficiencies in the implementation of
+.IR adjtime (8)
+in many kernels and/or brokenness of the system clock in some
+vendors' kernels. It would be much better if the kernels were fixed
+and the
+.I tickadj
+program went away.
diff --git a/usr.sbin/xntpd/doc/xntpd.8 b/usr.sbin/xntpd/doc/xntpd.8
new file mode 100644
index 0000000..5f3d33a
--- /dev/null
+++ b/usr.sbin/xntpd/doc/xntpd.8
@@ -0,0 +1,1105 @@
+''' $Header
+'''
+.de Sh
+.br
+.ne 5
+.PP
+\fB\\$1\fR
+.PP
+..
+.de Sp
+.if t .sp .5v
+.if n .sp
+..
+.de Ip
+.br
+.ie \\n.$>=3 .ne \\$3
+.el .ne 3
+.IP "\\$1" \\$2
+..
+'''
+''' Set up \*(-- to give an unbreakable dash;
+''' string Tr holds user defined translation string.
+''' Greek uppercase omega is used as a dummy character.
+'''
+.tr \(*W-|\(bv\*(Tr
+.ie n \{\
+.ds -- \(*W-
+.if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch
+.if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch
+.ds L" ""
+.ds R" ""
+.ds L' '
+.ds R' '
+'br\}
+.el\{\
+.ds -- \(em\|
+.tr \*(Tr
+.ds L" ``
+.ds R" ''
+.ds L' `
+.ds R' '
+'br\}
+.TH XNTPD 8 LOCAL
+.SH NAME
+xntpd - Network Time Protocol daemon
+.SH SYNOPSIS
+.B xntpd
+[
+.B -abdm
+] [
+.B -c
+.I conffile
+] [
+.B -e
+.I authdelay
+] [
+.B -f
+.I driftfile
+] [
+.B -k
+.I keyfile
+] [
+.B -p
+.I pidfile
+] [
+.B -r
+.I broadcastdelay
+] [
+.B -s
+.I statsdir
+] [
+.B -t
+.I trustedkey
+] [
+.B -v
+.I variable
+] [
+.B -V
+.I variable
+]
+.SH DESCRIPTION
+.I xntpd
+is a daemon which sets and maintains a Unix system time\-of\-day in
+agreement with Internet standard time servers.
+.I xntpd
+is a complete implementation of the Network Time Protocol (NTP) version
+3 standard, as defined by RFC 1305, but also retains compatibility with
+version 1 and 2 servers as defined by RFC 1059 and RFC 1119,
+respectively.
+.I xntpd
+does all computations in fixed point arithmetic and requires no floating
+point code. The computations done in the protocol and clock adjustment
+code are carried out with high precision and with attention to the
+details which might introduce systematic bias into the computations, to
+try to maintain an accuracy suitable for synchronizing with even the
+most precise external time source.
+.PP
+Ordinarily,
+.I xntpd
+reads its configuration from a configuration file at startup time. The
+default configuration file name is
+.IR /etc/ntp.conf,
+although this may be overridden from the command line. It is also
+possible to specify a working, although limited,
+.I xntpd
+configuration entirely on the command line, obviating the need for a
+configuration file. This may be particularly appropriate when
+.I xntpd
+is to be configured as a broadcast or multicast client, with all peers
+being determined by listening to broadcasts at run time. Various
+internal
+.I xntpd
+variables can be displayed and configuration options altered while the
+daemon is running through use of the
+.IR ntpq (8)
+and
+.IR xntpdc (8)
+programs.
+.PP
+The daemon can operate in any of several modes, including symmetric
+active/passive, client/server and broadcast/multicast. A
+broadcast/multicast client can automatically discover remote servers,
+compute one-way delay correction factors and configure itself
+automatically. This makes it possible to deploy a fleet of workstations
+without specifying a configuration file or configuration details
+specific to its environment.
+.PP
+The following command line arguments are understood by
+.I xntpd
+(see the configuration file description for a more complete functional
+description):
+.Ip -a 8
+run in \*(L"authenticate\*(R" mode
+.Ip -b 8
+listen for broadcast NTP and sync to this if available
+.Ip -c 8
+specify an alternate configuration file
+.Ip -d 8
+specify debugging mode. This flag may occur multiple times, with each
+occurance indicating greater detail of display.
+.Ip -e 8
+specify the time (in seconds) it takes to compute the NTP encryption
+field on this computer
+.Ip -f driftfile 8
+specify the location of the drift file
+.Ip -k 8
+specify the location of the file which contains the NTP authentication
+keys
+.Ip -m 8
+listen for multicast messages and synchronize to them if available
+(requires multicast kernel)
+.Ip -p 8
+specify the name of the file to record the daemon's process id
+.Ip -r 8
+ordinarily, the daemon automatically compensates for the network delay
+between the broadcast/multicast server and the client; if the
+calibration procedure fails, use the specified the default delay (in
+seconds)
+.Ip -s 8
+specify the directory to be used for creating statistics files
+.Ip -t trustedkey 8
+add a key number to the trusted key list
+.Ip -v 8
+add a system variable
+.Ip -V 8
+add a system variable listed by default
+.SH "CONFIGURATION OPTIONS"
+.I xntpd 's
+configuration file format is similar to other Unix configuration files.
+Comments begin with a \*(L"#\*(R" character and extend to the end of the
+line. Blank lines are ignored. Configuration commands consist of an
+initial keyword followed by a list of arguments, some of which may be
+optional, separated by whitespace. These commands may not be continued
+over multiple lines. Arguments may be host names, host addresses written
+in numeric, dotted\-quad form, integers, floating point numbers (when
+specifying times in seconds) and text strings. Optional arguments are
+delimited by \*(L"[]\*(R" in the following descriptions, while
+alternatives are separated by \*(L"|\*(R".
+.PP
+.B peer
+.I host_address
+[
+.B key
+.I #
+] [
+.B version
+.I #
+] [
+.B prefer
+]
+.br
+.B server
+.I host_address
+[
+.B key
+.I #
+] [
+.B version
+.I #
+] [
+.B prefer
+]
+.br
+.B broadcast
+.I host_address
+[
+.B key
+.I #
+] [
+.B version
+.I #
+] [
+.B ttl
+.I #
+]
+.PP
+These three commands specify various time servers to be used and/or time
+services to be provided. The
+.B peer
+command specifies that the local server is to operate in \*(L"symmetric
+active\*(R" mode with the remote server
+.I host_address
+named in the command. In this mode the local server can be synchronized
+to the remote server and, in addition, the remote server can be
+synchronized by the local server. This is useful in a network of servers
+where, depending on various failure scenarios, either the local or
+remote server host may be the better source of time. The
+.B server
+command specifies that the local server is to operate in
+\*(L"client\*(R" mode with the remote server named in the command. In
+this mode the local server can be synchronized to the remote server, but
+the remote server can never be synchronized to the local server. The
+.B broadcast
+command specifies that the local server is to operate in
+\*(L"broadcast\*(R" mode where the local server sends periodic broadcast
+messages to a client population at the broadcast/multicast address named
+in the command. Ordinarily, this specification applies only to the local
+server operating as a transmitter; for operation as a broadcast client,
+see the
+.B broadcastclient
+or
+.B multicastclient
+commands elsewhere in this document. In this mode the
+.I host_address
+is usually the broadcast address on [one of] the local network[s] or a
+multicast address assigned to NTP. The Numbers Czar has assigned the
+address 224.0.1.1 to NTP; this is presently the only number that should
+be used. Note that the use of multicast features requires a multicast
+kernel, which is not yet ubiquitous in vendor products.
+.PP
+The
+.B key
+option, when included, indicates that all packets sent to the address
+are to include authentication fields encrypted using the specified key
+number (the range of which is that of an unsigned 32 bit integer). The
+default is to not include an encryption field. The
+.B version
+option allows one to specify the version number to be used for outgoing
+NTP packets. Versions 1, 2, and 3 are the choices, version 3 is the
+default. The
+.B prefer
+option marks the host as a preferred host. All other things being equal,
+this host will be chosen for synchronization among a set of correctly
+operating hosts. The
+.B ttl
+option is used only with the broadcast mode. It specifies the time-to-
+live (TTL) to use on multicast packets. Selection of the proper value,
+which defaults to 127, is something of a black art and must be
+coordinated with the network admistrator(s).
+.PP
+.B broadcastclient
+.PP
+This directs the local server to listen for broadcast messages on the
+local network, in order to discover other servers on the same subnet.
+Upon hearing a broadcast message for the first time, the local server
+measures the nominal network delay using a brief client/server exchange
+with the remote server, then enters the \*(L"broadcastclient\*(R" mode,
+in which it listens for and synchronizes to succeeding broadcast
+messages. Note that, in order to avoid accidental or malicious
+disruption in this mode, both the local and remote servers must operate
+using authentication and the same trusted key and key identifier.
+.PP
+.B multicastclient
+[
+.I IP address ...
+]
+.PP
+This command is used in the same way as the
+.IR broadcastclient
+command, but operates using IP multicasting. Support for this function
+requires a multicast kernel and the use of authentication. If one or
+more IP addresses are given, the server joins the respective multicast
+group(s). If none are given, the IP address assigned to NTP (224.0.1.1)
+is assumed.
+.PP
+.B driftfile
+.I filename
+.PP
+This command specifies the name of the file used to record the frequency
+offset of the local clock oscillator. If the file exists, it is read at
+startup in order to set the initial frequency offset and then updated
+once per hour with the current offset computed by the daemon. If the
+file does not exist or this command is not given, the initial frequency
+offset is assumed zero. In this case, it may take some hours for the
+frequency to stabilize and the residual timing errors to subside. The
+file contains a single floating point value equal to the offset in
+parts-per-million (ppm). Note that the file is updated by first writing
+the current drift value into a temporary file and then using
+.IR rename (3)
+to replace the old version. This implies that
+.I xntpd
+must have write permission for the directory the drift file is located
+in, and that file system links, symbolic or otherwise, should probably
+be avoided.
+.PP
+.B enable auth|bclient|pll|monitor|stats
+[
+.I ...
+]
+.PP
+Provides a way to enable various server options. Flags not mentioned are
+unaffected. The \*(L"auth\*(R" flag causes the server to synchronize
+with unconfigured peers only if the peer has been correctly
+authenticated using a trusted key and key identifier. The default for
+this flag is disable (off). The \*(L"bclient\*(R" flag causes the server
+to listen for a message from a broadcast or multicast server, following
+which an association is automatically instantiated for that server. The
+default for this flag is disable (off). The \*(L"pll\*(R" flag enables
+the server to adjust its local clock, with default enable (on). If not
+set, the local clock free-runs at its intrinsic time and frequency
+offset. This flag is useful in case the local clock is controlled by
+some other device or protocol and NTP is used only to provide
+synchronization to other clients. The \*(L"monitor\*(R" flag enables the
+monitoring facility (see elsewhere), with default enable (on). The
+\*(L"stats\*(R" flag enables statistics facility filegen (see
+description elsewhere.), with default enable (on).
+.PP
+.B disable auth|bclient|pll|monitor|stats
+[
+.I ...
+]
+.PP
+Provides a way to disable various server options. Flags not mentioned
+are unaffected. The flags presently available are described under the
+enable command.
+
+.SH "AUTHENTICATION OPTIONS"
+.PP
+.B keys
+.I filename
+.PP
+This command specifies the name of a file which contains the encryption
+keys and key identifiers used by
+.I xntpd
+when operating in authenticated mode. The format of this file is
+described later in this document.
+.PP
+.B trustedkey
+.I #
+[
+.I "# ..."
+]
+.PP
+This command is used to specify the encryption key identifiers which are
+trusted for the purposes of authenticating peers suitable for
+sychonization. The authentication procedures require that both the local
+and remote servers share the same key and key identifier for this
+purpose, although different keys can be used with different servers. The
+arguments are 32 bit unsigned integers. Note, however, that NTP key 0 is
+fixed and globally known. If meaningful authentication is to be
+performed the 0 key should not be trusted.
+.PP
+.B requestkey
+.I #
+.PP
+This command specifies the key identifier to use with the
+.IR xntpdc (8)
+program, which is useful to diagnose and repair problems that affect
+.IR xntpd (8)
+operation. The operation of the
+.I xntpdc
+program are specific to this particular implementation of xntpd and can
+be expected to work only with this and previous versions of the daemon.
+Requests from a remote xntpdc program which affect the state of the
+local server must be authenticated, which requires bot the remote
+program and local server share a common key and key identifier. The
+argument to this command is a 32 bit unsigned integer. If no
+.B requestkey
+command is included in the configuration file, or if the keys don't
+match, such requests will be ignored.
+.PP
+.B controlkey
+.I #
+.PP
+This command specifies the key identifier to use with the
+.IR ntpq (8)
+program, which is useful to diagnose and repair problems that affect
+.IR xntpd (8)
+operation. The operation of the
+.IR ntpq
+program and
+.I xntpd
+conform to those specified in RFC 1305. Requests from a remote
+.I ntpq
+program which affect the state of the local server must be
+authenticated, which requires bot the remote program and local server
+share a common key and key identifier. The argument to this command is a
+32 bit unsigned integer. If no
+.B requestkey
+command is included in the configuration file, or if the keys don't
+match, such requests will be ignored.
+.PP
+.B authdelay
+.I seconds
+.PP
+Indicates the amount of time it takes to encrypt an NTP authentication
+field on the local computer. This value is used to correct transmit
+timestamps when the authentication is used on outgoing packets. The
+value usually lies somewhere in the range 0.0001 seconds to 0.003
+seconds, though it is very dependent on the CPU speed of the host
+computer. The value is usually computed using the
+.I authspeed
+program included with the distribution.
+.SH "ACCESS CONTROL OPTIONS"
+.B restrict
+.I address
+[
+.B mask
+.I numeric_mask
+] [
+.I flag
+] [
+.I ...
+]
+.PP
+.I xntpd
+implements a general purpose address\-and\-mask based restriction list.
+The list is sorted by address and by mask, and the list is searched in
+this order for matches, with the last match found defining the
+restriction flags associated with the incoming packets. The source
+address of incoming packets is used for the match, with the 32 bit
+address being and'ed with the mask associated with the restriction entry
+and then compared with the entry's address (which has also been and'ed
+with the mask) to look for a match. The \*(L"mask\*(R" argument defaults
+to 255.255.255.255, meaning that the \*(L"address\*(R" 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 \*(L"address\*(R" is normally
+given in dotted\-quad format, the text string \*(L"default\*(R", with no
+mask option, may be used to indicate the default entry.
+.PP
+In the current implementation, flags always restrict access, i.e. an
+entry with no flags indicates that free access to the server is to be
+given. The flags are not orthogonal, in that more restrictive flags will
+often make less restrictive ones redundant. The flags can generally be
+classed into two catagories, those which restrict time service and those
+which restrict informational queries and attempts to do run time
+reconfiguration of the server. One or more of the following flags may be
+specified:
+.Ip ignore 10
+Ignore all packets from hosts which match this entry. If this flag is
+specified neither queries nor time server polls will be responded to.
+.Ip noquery 10
+Ignore all NTP mode 6 and 7 packets (i.e. information queries and
+configuration requests) from the source. Time service is not affected.
+.Ip nomodify 10
+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.
+.Ip notrap 10
+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.
+.Ip lowpriotrap 10
+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.
+.Ip noserve 10
+Ignore NTP packets whose mode is other than 6 or 7. In effect, time
+service is denied, though queries may still be permitted.
+.Ip nopeer 10
+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.
+.Ip notrust 10
+Treat these hosts normally in other respects, but never use them as
+synchronization sources.
+.Ip limited 10
+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 \*(L"client_limit\*(R" hosts that have
+shown up at the server and that have been active during the last
+\*(L"client_limit_period\*(R" seconds are accepted. Requests from other
+clients from the same net are rejected. Only time request packets are
+taken into account. \*(L"Private\*(R", \*(L"control\*(R", and
+\*(L"broadcast\*(R" packets are not subject to client limitation and
+therefore are not contributing to client count. History of clients is
+kept using the monitoring capability of
+.I xntpd .
+Thus, monitoring is active as long as there is a restriction entry with
+the \*(L"limited\*(R" flag. The default value for \*(L"client_limit\*(R"
+is 3. The default value for \*(L"client_limit_period\*(R" is 3600
+seconds.
+.Ip ntpport 10
+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
+\*(L"ntpport\*(R" and non\-\*(L"ntpport\*(R" may be specified. The
+\*(L"ntpport\*(R" is considered more specific and is sorted later in the
+list.
+.PP
+Default restriction list entries, with the flags \*(L"ignore,
+ntpport\*(R", for each of the local host's interface addresses are
+inserted into the table at startup to prevent the server from attempting
+to synchronize to its own time. A default entry is also always present,
+though if it is otherwise unconfigured no flags are associated with the
+default entry (i.e. everything besides your own NTP server is
+unrestricted).
+.PP
+The restriction facility was added to allow the current access policies
+of the time servers running on the NSFnet backbone to be implemented
+with
+.I xntpd
+as well. While this facility may be otherwise useful for keeping
+unwanted or broken remote time servers from affecting your own, it
+should not be considered an alternative to the standard NTP
+authentication facility. Source address based restrictions are easily
+circumvented by a determined cracker.
+.PP
+.B clientlimit
+.I limit
+.PP
+Sets \*(L"client_limit\*(R" to \*(L"limit\*(R", allows configuration of
+client limitation policy. This variable defines the number of clients
+from the same network that are allowed to use the server.
+.PP
+.B clientperiod
+.I period
+.PP
+Sets \*(L"client_limit_period\*(R", allows configuration of client
+limitation policy. This variable specifies the number of seconds after
+which a client is considered inactive and thus no longer is counted for
+client limit restriction.
+.SH "MONITORING OPTIONS"
+.PP
+.B statsdir
+.I /directory path/
+.PP
+Indicates the full path of a directory where statistics files should be
+created (see below). This keyword allows the (otherwise constant)
+filegen filename prefix to be modified for file generation sets used for
+handling statistics logs (see
+.B filegen
+statement below).
+.PP
+.B statistics
+.IR name \.\.\.
+.PP
+Enables writing of statistics records. Currently, three kinds of
+statistics are supported.
+.Ip loopstats 10
+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 \*(L"loopstats\*(R":
+.PP
+.RS 5
+48773 10847.650 0.0001307 17.3478 2
+.RE
+
+.RS 10
+The first two fields show the date (Modified Julian Day) and time
+(seconds and fraction past UTC midnight). The next three fields show
+time offset in seconds, frequency offset in parts-per-million and time
+constant of the clock-discipline algorithm at each update of the clock.
+.RE
+.Ip peerstats 10
+enables recording of peer statistics information. This includes
+statistics records of all peers of a NTP server and of the 1-pps signal,
+where present and configured. Each valid update appends a line of the
+following form to the current element of a file generation set named
+\*(L"peerstats\*(R":
+.PP
+.RS 5
+48773 10847.650 127.127.4.1 9714 -0.001605 0.00000 0.00142
+.RE
+
+.RS 10
+The first two fields show the date (Modified Julian Day) and time
+(seconds and fraction past UTC midnight). The next two fields show the
+peer address in dotted-quad notation and status, respectively. The
+status field is encoded in hex in the format described in Appendix A of
+the NTP specification RFC 1305. The final three fields show the offset,
+delay and dispersion, all in seconds.
+.RE
+.Ip clockstats 10
+enables recording of clock driver statistics information. Each update
+received from a clock driver outputs a line of the following form to the
+file generation set named \*(L"clockstats\*(R":
+.PP
+.RS 5
+49213 525.624 127.127.4.1 93 226 00:08:29.606 D
+.RE
+
+.RS 10
+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.
+.RE
+.PP
+Statistic files are managed using file generation sets (see
+.B filegen
+below). The information obtained by enabling statistics recording allows
+analysis of temporal properties of a
+.I xntpd
+server. It is usually only useful to primary servers or maybe main
+campus servers.
+.PP
+.B filegen
+.I name
+[
+.B file
+.I filename
+] [
+.B type
+.I typename
+] [
+.B flag
+.I flagval
+] [
+.BR link \| nolink
+] [
+.BR enable \| disable
+]
+.PP
+Configures setting of generation file set
+.IR name .
+Generation file sets provide a means for handling files that are
+continously growing during the lifetime of a server. Server statistics
+are a typical example for such files. Generation file sets provide
+access to a set of files used to store the actual data. At any time at
+most one element of the set is being written to. The
+.I type
+given specifies when and how data will be directed to a new element of
+the set. This way, information stored in elements of a file set that are
+currently unused are available for administrational operations without
+the risc of desturbing the operation of
+.I xntpd .
+(Most important: they can be removed to free space for new data
+produced.) Filenames of set members are built from three elements.
+.Ip prefix 10
+This is a constant filename path. It is not subject to modifications via
+the
+.B filegen
+statement. It is defined by the server, usually specified as a compile
+time constant. It may, however, be configurable for individual file
+generation sets via other commands. For example, the prefix used with
+"loopstats" and "peerstats" filegens can be configured using the
+.B statsdir
+statement explained above.
+.Ip filename 10
+This string is directly concatenated to the
+.I prefix
+mentioned above (no intervening \*(L'/\*(R' (slash)). This can be
+modified using the \*(L"file\*(R" argument to the \*(L"filegen\*(R"
+statement. No \*(L"..\*(R" elements are allowed in this component to
+prevent filenames referring to parts outside the filesystem hierarchy
+denoted by \*(L"prefix\*(R".
+.Ip suffix 10
+This part is reflects individual elements of a file set. It is generated
+according to the
+.I type
+of a file set as explained below.
+.PP
+A file generation set is characterized by its type. The following types
+are supported:
+.Ip none 10
+The file set is actually a single plain file.
+.Ip pid 10
+One element of file set is used per incarnation of a
+.I xntpd
+server. This type does not perform any changes to file set members
+during runtime, however it provides an easy way of seperating files
+belonging to different
+.I xntpd
+server incarnations. The set member filename is built by appending a dot
+(\*(L'.\*(R') to concatentated \*(L"prefix\*(R" and \*(L"filename\*(R"
+strings, and appending the decimal representation of the process id of
+the
+.I xntpd
+server process.
+.Ip day 10
+One file generation set element is created per day. The term
+.I day
+is based on
+.IR UTC .
+A day is defined as the period between 00:00 and 24:00 UTC. The file set
+member suffix consists of a dot \*(L".\*(R" and a day specification in
+the form
+.RI < YYYYMMDD >.
+.I YYYY
+is a 4 digit year number (e.g. 1992).
+.I MM
+is a two digit month number.
+.I DD
+is a two digit day number. Thus, all information written at December
+10th, 1992 would end up in a file named
+\*(L"<prefix><filename>.19921210\*(R".
+.Ip week 10
+Any file set member contains data related to a certain week of a year.
+The term
+.I week
+is definied by computing \*(L"day of year\*(R" modulo 7. Elements of
+such a file generation set are distinguished by appending the following
+suffix to the file set filename base: A dot, a four digit year number,
+the letter \*(L"W\*(R", and a two digit week number. For example,
+information from Jamuary, 10th 1992 would end up in a file with suffix
+\*(L".1992W1\*(R".
+.Ip month 10
+One generation file set element is generated per month. The file name
+suffix consists of a dot, a four digit year number, and a two digit
+month.
+.Ip year 10
+One generation file elment is generated per year. The filename suffix
+consists of a dot and a 4 digit year number.
+.Ip age 10
+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 \*(L"a\*(R", and an eight digit number. This number is
+taken to be the number of seconds the server is running at the start of
+the corresponding 24 hour period.
+.PP
+Information is only written to a file generation set when this set is
+\*(L"enabled\*(R". Output is prevented by specifying \*(L"disabled\*(R".
+.PP
+It is convenient to be able to access the
+.I current
+element of a file generation set by a fixed name. This feature is
+enabled by specifying \*(L"link\*(R" and disabled using
+\*(L"nolink\*(R". If \*(L"link\*(R" 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 \*(L"C\*(R", and the pid
+of the
+.I xntpd
+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.
+.SH "MISCELLANEOUS OPTIONS"
+.PP
+.B precision
+.I #
+.PP
+This command specifies the nominal precision of the local clock. The
+value is an integer approximately equal to the base 2 logarithm of the
+local timekeeping precision in seconds. Normally, the daemon determines
+the precision automatically at startup, so this command is necessary
+only in special cases when the precision cannot be determined
+automatically.
+.PP
+.B broadcastdelay
+.I seconds
+.PP
+The broadcast and multicast modes require a special calibration to
+determine the network delay between the local and remote servers.
+Ordinarily, this is done automatically by the initial protocol exchanges
+between the local and remote servers. In some cases, the calibration
+procedure may fail due to network or server access controls, for
+example. This command specifies the default delay to be used under these
+circumstances. Typically (for Ethernet), a number between 0.003 and
+0.007 seconds is appropriate. The default when this command is not used
+is 0.004 seconds.
+.PP
+.B trap
+.I host_address
+[
+.B port
+.I port_number
+] [
+.B interface
+.I interface_addess
+]
+.PP
+This command configures a trap receiver at the given host address and
+port number for sending messages with the specified local interface
+address. If the port number is unspecified a value of 18447 is used. If
+the interface address is not specified the message is sent with a source
+address which is that of the local interface the message is sent
+through. Note that on a multihomed host the interface used may vary from
+time to time with routing changes.
+.PP
+The trap receiver will generally log event messages and other
+information from the server in a log file. While such monitor programs
+may also request their own trap dynamically, configuring a trap receiver
+will ensure that no messages are lost when the server is started.
+.PP
+.B setvar
+.I variable
+.I [default]
+.PP
+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 from <name>=<value> is followed by the
+.I default
+keyword the variable will be listed as part of the default system
+variables (
+.I ntpq rv
+command). These additional variables serve informational purposes only.
+They are not related to the protocol other that they can be listed. The
+known protocol variables will always overide any variables defined via
+the
+.I setvar
+mechanism.
+.PP
+There are three special variables that contain the names of all variable
+of the same group. The
+.I sys_var_list
+holds the names of all system variables. The
+.I peer_var_list
+holds the names of all peer variables and the
+.I clock_var_list
+hold the names of the reference clock variables.
+.PP
+.B monitor yes|no
+.B authenticate yes|no
+.PP
+These commands have been superseded by the
+.B enable
+and
+.B disable
+commands. They are listed here for historical purposes.
+.SH "AUTHENTICATION KEY FILE FORMAT"
+.PP
+The NTP standard specifies an extension allowing verification of the
+authenticity of received NTP packets, and to provide an indication of
+authenticity in outgoing packets. This is implemented in
+.I xntpd
+using the DES or MD5 algorithms to compute a digital signature, or
+message-digest. The specification allows any one of possibly 4 billion
+keys, numbered with 32 bit key identifiers, to be used to authenticate
+an association. The servers involved in an association must agree on the
+key and key identifier used to authenticate their data, though they must
+each learn the key and key identifer independently. In the case of DES,
+the keys are 56 bits long with, depending on type, a parity check on
+each byte. In the case of MD5, the keys are 64 bits (8 bytes).
+.I xntpd
+reads its keys from a file specified using the
+.B -k
+command line option or the
+.B 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
+.Ip "" 5
+.I "keyno type key"
+.PP
+where \*(L"keyno\*(R" is a positive integer, \*(L"type\*(R" is a single
+character which defines the format the key is given in, and
+\*(L"key\*(R" is the key itself.
+.PP
+The key may be given in one of three different formats, controlled by
+the \*(L"type\*(R" character. The three key types, and corresponding
+formats, are listed following.
+.Ip "S" 5
+The \*(L"key\*(R" is a 64 bit hexadecimal number in the format specified
+in the DES document, that is the high order 7 bits of each octet are
+used to form the 56 bit key while the low order bit of each octet is
+given a value such that odd parity is maintained for the octet. Leading
+zeroes must be specified (i.e. the key must be exactly 16 hex digits
+long) and odd parity must be maintained. Hence a zero key, in standard
+format, would be given as
+.I 0101010101010101 .
+.Ip "N" 5
+The \*(L"key\*(R" 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
+.I 8080808080808080
+.Ip "A" 5
+The \*(L"key\*(R" is a 1\-to\-8 character ASCII string. A key is formed
+from this by using the lower order 7 bits of the ASCII representation of
+each character in the string, with zeroes being added on the right when
+necessary to form a full width 56 bit key, in the same way that
+encryption keys are formed from Unix passwords.
+.Ip "M" 5
+The \*(L"key\*(R" 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.
+.PP
+One of the keys may be chosen,
+by way of the configuration file
+.B requestkey
+statement, to authenticate run time configuration requests made using
+the
+.IR xntpdc (8)
+program. The latter program obtains the key from the terminal as a
+password, so it is generally appropriate to specify the key chosen to be
+used for this purpose in ASCII format.
+.SH PRIMARY CLOCK SUPPORT
+.I xntpd
+can be optionally compiled to include support for a number of types of
+reference clocks. A reference clock will generally (though not always)
+be a radio timecode receiver which is synchronized to a source of
+standard time such as the services offered by the NRC in Canada and NIST
+in the U.S. The interface between the computer and the timecode receiver
+is device dependent and will vary, but is often a serial port.
+.PP
+Support for the various reference clock drivers is conditionally
+compiled using the compiler define codes described elsewhere. An attempt
+to configure a reference clock when specific support is not available or
+the hardware port has not been appropriately configured results in a
+scolding remark to the system log file, but is otherwise non hazardous.
+.PP
+For the purposes of configuration,
+.I xntpd
+treats reference clocks in a manner analogous to normal NTP peers as
+much as possible. Reference clocks are referred to by address, much as a
+normal peer is, though an invalid IP address is used to distinguish them
+from normal peers. Reference clock addresses are of the form
+.I 127.127.t.u
+where
+.I t
+is an integer denoting the clock type and
+.I u
+indicates the type\-specific unit number. Reference clocks are
+configured using a
+.B server
+statement in the configuration file where the
+.I host_address
+is the clock address. The
+.I key,
+.I version
+and
+.I ttl
+options are not used for reference clock support; however, the
+.I prefer
+option can be useful to persuade the server to cherish a reference clock
+with somewhat more enthusiasm than other reference clocks or peers, if
+this is advisable. Clock addresses may generally be used anywhere in the
+configuration file a normal IP address can be used, for example, in
+.B restrict
+statements, although such use would normally be considered strange.
+.PP
+Reference clock support provides the
+.B fudge
+command, which can be used to configure reference clocks in special
+ways. Following is the generic format that applies to this command
+.PP
+.B fudge
+.I 127.127.t.u
+[
+.B time1
+.I secs
+] [
+.B time2
+.I secs
+] [
+.B stratum
+.I int
+] [
+.B refid
+.I int
+] [
+.B flag1
+.I 0|1
+] [
+.B flag2
+.I 0|1
+] [
+.B flag3
+.I 0|1
+] [
+.B flag4
+.I 0|1
+]
+.PP
+The
+.I time1
+and
+.B time2
+options are specified in fixed point seconds and used in some clock
+drivers as calibration constants. By convention, and unless indicated
+otherwise,
+.B time1
+is used as a calibration constant to adjust the nominal time offset of a
+particular clock to agree with an external standard, such as a precision
+PPS signal. The specified offset is in addition to the propagation delay
+provided by other means, such as internal DIPswitches. The
+.B stratum
+option is a number in the range zero to 15 and is used to assign a
+nonstandard operating stratum to the clock. The
+.B refid
+option is an ASCII string in the range one to four characters and is
+used to assign a nonstandard reference identifier to the clock. Finally,
+the four binary flags
+.B flag1,
+.B flag2,
+.B flag3
+and
+.B flag4
+are used for customizing the clock driver. The interpretation of these
+values, and whether they are used at all, is a function of the needs of
+the particular clock driver. However, by convention, and unless
+indicated otherwise,
+.B flag3
+is used to attach the ppsclock streams module to the configured driver,
+while
+.B flag4
+is used to enable recording verbose monitoring data to the clockstats
+file configured with the
+.I filegen
+command. Further information on the ppsclock streams module is in the
+README file in the ./kernel directory in the current xntp3 program
+distribution. Further information on this feature is available in the
+./scripts/stats directory in the same distribution.
+.PP
+Ordinarily, the stratum of a reference clock is by default zero. Since
+the
+.I xntpd
+daemon adds one to the stratum of each peer, a primary server ordinarily
+displays stratum one. In order to provide engineered backups, it is
+often useful to specify the reference clock stratum as greater than
+zero. The
+.B stratum
+option is used for this purpose. Also, in cases involving both a
+reference clock and a 1-pps discipline signal, it is useful to specify
+the reference clock identifier as other than the default, depending on
+the driver. The
+.I refid
+option is used for this purpose. Except where noted, these options apply
+to all clock drivers.
+.PP
+.I xntpd
+on Unix machines currently supports several different types of clock
+hardware plus a special pseudo\-clock used for backup or when no other
+clock source is available. In the case of most of the clock drivers,
+support for a 1-pps precision timing signal is available as described in
+the README file in the ./doc directory of the xntp3 progam distribution.
+The clock drivers, and the addresses used to configure them, are
+described in the README.refclocks in the doc directory of the current
+program distribution.
+.PP
+.SH VARIABLES
+Most variables used by the NTP protocol can be examined with the
+.I xntpdc
+(mode 7 messages) and the
+.I ntpq
+(mode 6 messages). Currently very few variables can be modified via mode
+6 messages. These variables are either created with the
+.I setvar
+directive or the leap warning variables. The leap warning bits that can
+be set in the
+.B leapwarning
+variable (up to one month ahead). Both, the
+.B leapwarning and in the
+.B leapindication
+variable, have a slightly different encoding than the usual
+.B leap
+bits interpretation:
+.P
+.Ip 00 8
+The daemon passes the leap bits of its synchronisation source (usual
+mode of operation).
+.Ip 01/10 8
+A leap second is added/deleted (operator forced leap second).
+.Ip 11 8
+Leap information from the sychronisation source is ignored (thus
+LEAP_NOWARNING is passed on).
+.PP
+.SH FILES
+.Ip /etc/ntp.conf 20
+the default name of the configuration file
+.Ip /etc/ntp.drift 20
+the conventional name of the drift file
+.Ip /etc/ntp.keys 20
+the conventional name of the key file
+.SH SEE ALSO
+.PP
+.IR xntpdc (8),
+.IR ntpq (8),
+.IR ntpdate (8)
+.SH HISTORY
+.PP
+Written by Dennis Ferguson at the University of Toronto. Text amended by
+David Mills at the University of Delaware.
+.SH BUGS
+.PP
+.I xntpd
+has gotten rather fat. While not huge, it has gotten larger than might
+be desireable for an elevated\-priority daemon running on a workstation,
+particularly since many of the fancy features which consume the space
+were designed more with a busy primary server, rather than a high
+stratum workstation, in mind.
diff --git a/usr.sbin/xntpd/doc/xntpdc.8 b/usr.sbin/xntpd/doc/xntpdc.8
new file mode 100644
index 0000000..e7ec93b
--- /dev/null
+++ b/usr.sbin/xntpd/doc/xntpdc.8
@@ -0,0 +1,733 @@
+''' $Header
+'''
+.de Sh
+.br
+.ne 5
+.PP
+\fB\\$1\fR
+.PP
+..
+.de Sp
+.if t .sp .5v
+.if n .sp
+..
+.de Ip
+.br
+.ie \\n.$>=3 .ne \\$3
+.el .ne 3
+.IP "\\$1" \\$2
+..
+'''
+''' Set up \*(-- to give an unbreakable dash;
+''' string Tr holds user defined translation string.
+''' Greek uppercase omega is used as a dummy character.
+'''
+.tr \(*W-|\(bv\*(Tr
+.ie n \{\
+.ds -- \(*W-
+.if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch
+.if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch
+.ds L" ""
+.ds R" ""
+.ds L' '
+.ds R' '
+'br\}
+.el\{\
+.ds -- \(em\|
+.tr \*(Tr
+.ds L" ``
+.ds R" ''
+.ds L' `
+.ds R' '
+'br\}
+.TH XNTPDC 8 LOCAL
+.SH NAME
+xntpdc - query/control program for the Network Time Protocol daemon
+.SH SYNOPSIS
+.B xntpdc
+[
+.B -ilnps
+] [
+.B -c
+.I command
+] [
+.I host
+] [
+.I ...
+]
+.SH DESCRIPTION
+.I Xntpdc
+is used to query the
+.IR xntpd (8)
+daemon about its current state and to request changes in that state. The
+program may be run either in interactive mode or controlled using
+command line arguments. Extensive state and statistics information is
+available through the
+.I xntpdc
+interface. In addition, nearly all the configuration options which can
+be specified at start up using
+.IR xntpd 's
+configuration file may also be specified at run time using
+.IR xntpdc .
+.PP
+If one or more request options is included on the command line when
+.I xntpdc
+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
+.I localhost
+by default. If no request options are given,
+.I xntpdc
+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
+.I localhost
+when no other host is specified.
+.I Xntpdc
+will prompt for commands if the standard input is a terminal device.
+.PP
+.I Xntpdc
+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.
+.I Xntpdc
+makes no attempt to retransmit requests, and will time requests out if
+the remote host is not heard from within a suitable time out time.
+.PP
+Command line options are described following. Specifying a command line
+option other than
+.B -i
+or
+.B -n
+will cause the specified query (queries) to be sent to the indicated
+host(s) immediately. Otherwise,
+.I xntpdc
+will attempt to read interactive format commands from the standard
+input.
+.Ip -c 8
+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
+.B -c
+options may be given.
+.Ip -i 8
+Force
+.I xntpdc
+to operate in interactive mode. Prompts will be written to the standard
+output and commands read from the standard input.
+.Ip -l 8
+Obtain a list of peers which are known to the server(s). This switch is
+equivalent to \*(L"-c listpeers\*(R".
+.Ip -n 8
+Output all host addresses in dotted\-quad numeric format rather than
+converting to the canonical host names.
+.Ip -p 8
+Print a list of the peers known to the server as well as a summary of
+their state. This is equivalent to \*(L"-c peers\*(R".
+.Ip -s 8
+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
+.B -p
+switch. This is equivalent to \*(L"-c dmpeers\*(R".
+.SH INTERNAL COMMANDS
+.PP
+Interactive format commands consist of a keyword followed by zero to
+four arguments. Only enough characters of the full keyword to uniquely
+identify the command need be typed. The output of a command is normally
+sent to the standard output, but optionally the output of individual
+commands may be sent to a file by appending a \*(L">\*(R", followed by a
+file name, to the command line.
+.PP
+A number of interactive format commands are executed entirely within the
+.I xntpdc
+program itself and do not result in NTP mode 7 requests being sent to a
+server. These are described following.
+.PP
+.B ?
+[
+.I command_keyword
+]
+.PP
+A \*(L"?\*(R" by itself will print a list of all the command keywords
+known to this incarnation of
+.IR xntpdc .
+A \*(L"?\*(R" followed by a command keyword will print funcation and
+usage information about the command. This command is probably a better
+source of information about
+.I xntpdc
+than this manual page.
+.PP
+.B help
+[
+.I command_keyword
+]
+.PP
+A synonym for the
+.B ?
+command.
+.PP
+.B timeout
+.I millseconds
+.PP
+Specify a time out period for responses to server queries. The default
+is about 8000 milliseconds.
+.PP
+.B delay
+.I milliseconds
+.PP
+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.
+.PP
+.B host
+.I hostname
+.PP
+Set the host to which future queries will be sent.
+.I Hostname
+may be either a host name or a numeric (dotted quad) dmaddress.
+.PP
+.B keyid
+.I #
+.PP
+This command allows the specification of a key number to be used to
+authenticate configuration requests. This must correspond to the key
+number the server has been configured to use for this purpose.
+.PP
+.B passwd
+.PP
+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.
+.PP
+.B "hostnames yes|no"
+.PP
+If \*(L"yes\*(R" is specified, host names are printed in information
+displays. If \*(L"no\*(R" is given, numeric addresses are printed
+instead. The default is \*(L"yes\*(R" unless modified using the command
+line
+.B -n
+switch.
+.PP
+.B quit
+.PP
+Exit
+.IR xntpdc .
+.SH QUERY COMMANDS
+.PP
+Query commands result in NTP mode 7 packets containing requests for
+information being sent to the server. These are \*(L"read\-only\*(R"
+commands in that they make no modification of the server configuration
+state.
+.PP
+.B listpeers
+.PP
+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.
+.PP
+.B peers
+.PP
+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. In addition, the character in the left margin indicates the
+mode this peer entry is operating in. A \*(L"+\*(R" denotes symmetric
+active, a \*(L"-\*(R" indicates symmetric passive, a \*(L"=\*(R" means
+the remote server is being polled in client mode, a \*(L"^\*(R"
+indicates that the server is broadcasting to this address, a \*(L"~\*(R"
+denotes that the remote peer is sending broadcasts and a \*(L"*\*(R"
+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 \*(L"REFCLK(<implementation number>, <parameter>)\*(R". On
+\*(L"hostnames no\*(R" only IP\-addresses will be displayed.
+.PP
+.B dmpeers
+.PP
+A slightly different peer summary list. Identical to the output of the
+.B 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 \*(L".\*(R" indicates that this peer was cast off
+in the falseticker detection, while a \*(L"+\*(R" indicates that the
+peer made it through. A \*(L"*\*(R" denotes the peer the server is
+currently synchronizing with.
+.PP
+.B showpeer
+.I peer_address
+[
+.I addr2
+] [
+.I addr3
+] [
+.I addr4
+]
+.PP
+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.
+.PP
+.B pstats
+.I peer_address
+[
+.I addr2
+] [
+.I addr3
+] [
+.I addr4
+]
+.PP
+Show per\-peer statistic counters associated with the specified peer(s).
+.PP
+.B clockinfo
+.I clock_peer_address
+[
+.I addr2
+] [
+.I addr3
+] [
+.I addr4
+]
+.PP
+Obtain and print information concerning a peer clock. The values
+obtained provide information on the setting of fudge factors and other
+clock performance information.
+.PP
+.B kerninfo
+.PP
+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.
+.PP
+.B loopinfo
+[
+.B oneline|multiline
+]
+.PP
+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
+\*(L"offset\*(R" is the last offset given to the loop filter by the
+packet processing code. The \*(L"frequency\*(R" is the frequency error
+of the local clock in parts-per-million (ppm). The \*(L"time_const\*(R"
+controls the "stiffness" of the phase-lock loop and thus the speed at
+which it can adapt to oscillator drift. The \*(L"watchdog timer\*(R"
+value is the number of seconds which have elapsed since the last sample
+offset was given to the loop filter. The \*(L"oneline\*(R" and
+\*(L"multiline\*(R" options specify the format in which this information
+is to be printed, with \*(L"multiline\*(R" as the default.
+.PP
+.B sysinfo
+.PP
+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. The \*(L"system flags\*(R" show
+various system flags, some of which can be set and cleared by the
+\*(L"enable\*(R" and \*(L"disable\*(R" configuration commands,
+respectively. The \*(L"stability\*(R" 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 \*(L"tick\*(R" may be incorrect. The
+\*(L"broadcastdelay\*(R" shows the default broadcast delay, as set by
+the \*(L"broadcastdelay\*(R" configuration command, while the
+\*(L"authdelay\*(R" shows the default authentication delay, as set by
+the \*(L"authdelay\*(R" configuration command.
+.PP
+.B sysstats
+.PP
+Print statistics counters maintained in the protocol module.
+.PP
+.B memstats
+.PP
+Print statistics counters related to memory allocation
+code.
+.PP
+.B iostats
+.PP
+Print statistics counters maintained in the input\-output module.
+.PP
+.B timerstats
+.PP
+Print statistics counters maintained in the timer/event queue support
+code.
+.PP
+.B reslist
+.PP
+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.
+.PP
+.B monlist
+[
+.I version
+]
+.PP
+Obtain and print traffic counts collected and maintained by the monitor
+facility. The version number should not normally need to be specified.
+.PP
+.B clkbug
+.I clock_peer_address
+[
+.I addr2
+] [
+.I addr3
+] [
+.I addr4
+]
+.PP
+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.
+.SH RUNTIME CONFIGURATION REQUESTS
+.PP
+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
+.IR xtnpdc .
+This can be done using the
+.B keyid
+and
+.B 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.
+.PP
+.B addpeer
+.I peer_address
+[
+.I keyid
+] [
+.I version#
+] [
+.B prefer
+]
+.PP
+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 \*(L"keyid\*(R" 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 \*(L"version#\*(R" can be 1, 2 or 3 and defaults to 3. The
+\*(L"prefer\*(R" 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.
+.PP
+.B addserver
+.I peer_address
+[
+.I keyid
+] [
+.I version#
+] [
+.B prefer
+]
+.PP
+Identical to the
+.B addpeer
+command, except that the operating mode is client.
+.PP
+.B broadcast
+.I peer_address
+[
+.I keyid
+] [
+.I version#
+]
+.PP
+Identical to the
+.B addpeer
+command, except that the operating mode is broadcast. In this case a
+valid key identifier and key are required. The \*(L"peer_address\*(R"
+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.
+.PP
+.B unconfig
+.I peer_address
+[
+.I addr2
+] [
+.I addr3
+] [
+.I addr4
+]
+.PP
+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.
+.PP
+.B fudge
+.I peer_address
+[
+.I time1
+] [
+.I time2
+] [
+.I stratum
+] [
+.I refid
+]
+.PP
+This command provides a way to set certain data for a reference clock.
+See the source listing for further information.
+.PP
+.B enable auth|bclient|pll|monitor|stats
+[
+.I ...
+]
+.PP
+Provides a way to enable various server options. Flags not mentioned are
+unaffected. The \*(L"auth\*(R" flag causes the server to synchronize
+with unconfigured peers only if the peer has been correctly
+authenticated using a trusted key and key identifier. The default for
+this flag is disable (off). The \*(L"bclient\*(R" flag causes the server
+to listen for a message from a broadcast or multicast server, following
+which an association is automatically instantiated for that server. The
+default for this flag is disable (off). The \*(L"pll\*(R" flag enables
+the server to adjust its local clock, with default enable (on). If not
+set, the local clock free-runs at its intrinsic time and frequency
+offset. This flag is useful in case the local clock is controlled by
+some other device or protocol and NTP is used only to provide
+synchronization to other clients. The \*(L"monitor\*(R" flag enables the
+monitoring facility (see elsewhere), with default disable (off). The
+\*(L"stats\*(R" flag enables statistics facility filegen (see
+description elsewhere.), with default enable (on).
+.PP
+.B disable auth|bclient|pll|monitor|stats
+[
+.I ...
+]
+.PP
+Provides a way to disable various server options. Flags not mentioned
+are unaffected. The flags presently available are described under the
+enable command.
+.PP
+.B restrict
+.I address
+.I mask
+.I flag
+[
+.I flag
+]
+.PP
+Causes flag(s) to be added to an existing restrict list entry, or adds a
+new entry to the list with the specified flag(s). The possible choices
+for the flags arguments are given in the following list:
+.Ip ignore 10
+Ignore all packets from hosts which match this entry. If this flag is
+specified neither queries nor time server polls will be responded to.
+.Ip noquery 10
+Ignore all NTP mode 7 packets (i.e. information queries and
+configuration requests) from the source. Time service is not affected.
+.Ip nomodify 10
+Ignore all NTP mode 7 packets which attempt to modify the state of the
+server (i.e. run time reconfiguration). Queries which return information
+are permitted.
+.Ip notrap 10
+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.
+.Ip lowpriotrap 10
+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.
+.Ip noserve 10
+Ignore NTP packets whose mode is other than 7. In effect, time service
+is denied, though queries may still be permitted.
+.Ip nopeer 10
+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.
+.Ip notrust 10
+Treat these hosts normally in other respects, but never use them as
+synchronization sources.
+.Ip limited 10
+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 \*(L"client_limit\*(R" hosts that have
+shown up at the server and that have been active during the last
+\*(L"client_limit_period\*(R" seconds are accepted. Requests from other
+clients from the same net are rejected. Only time request packets are
+taken into account. \*(L"Private\*(R", \*(L"control\*(R", and
+\*(L"broadcast\*(R" packets are not subject to client limitation and
+therefore are not contributing to client count. History of clients is
+kept using the monitoring capability of
+.IR xntpd.
+Thus, monitoring is active as long as there is a restriction entry with
+the \*(L"limited\*(R" flag. The default value for \*(L"client_limit\*(R"
+is 3. The default value for \*(L"client_limit_period\*(R" is 3600
+seconds. Currently both variables are not runtime configurable.
+.Ip ntpport 10
+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
+\*(L"ntpport\*(R" and non\-\*(L"ntpport\*(R" may be specified. The
+\*(L"ntpport\*(R" is considered more specific and is sorted later in the
+list.
+.PP
+.B unrestrict
+.I address
+.I mask
+.I flag
+[
+.I flag
+]
+.PP
+Remove the specified flag(s) from the restrict list entry indicated
+by the
+.I address
+and
+.I mask
+arguments.
+.PP
+.B delrestrict
+.I address
+.I mask
+[
+.B ntpport
+]
+.PP
+Delete the matching entry from the restrict list.
+.PP
+.B "monitor yes|no"
+.PP
+Enable or disable the monitoring facility. Note that a
+.B "monitor no"
+command followed by a
+.B "monitor yes"
+command is a good way of resetting the packet counts.
+.PP
+.B readkeys
+.PP
+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
+.I xntpd
+configuration file). This allows encryption keys to be changed without
+restarting the server.
+.PP
+.B trustkey
+.I keyid
+[
+.I keyid
+] [
+.I keyid
+] [
+.I keyid
+]
+.PP
+Adds one or more keys to the trusted key list. When authentication is
+enabled, peers whose time is to be trusted must be authenticated using a
+trusted key.
+.PP
+.B untrustkey
+.I keyid
+[
+.I keyid
+] [
+.I keyid
+] [
+.I keyid
+]
+.PP
+Removes one or more keys from the trusted key list.
+.PP
+.B authinfo
+.PP
+Returns information concerning the authentication module, including
+known keys and counts of encryptions and decryptions which have been
+done.
+.PP
+.B setprecision
+.I precision_value
+.PP
+Sets the precision which the server advertises to the specified value.
+This should be a negative integer in the range -4 through -20.
+.PP
+.B traps
+.PP
+Display the traps set in the server. See the source listing for further
+information.
+.PP
+.B addtrap
+.I address
+[
+.I port
+] [
+.I interface
+]
+.PP
+Set a trap for asynchronous messages. See the source listing for further
+information.
+.PP
+.B clrtrap
+.I address
+[
+.I port
+] [
+.I interface
+]
+.PP
+Clear a trap for asynchronous messages. See the source listing for
+further information.
+.PP
+.B reset ...
+.PP
+Clear the statistics counters in various modules of the server. See the
+source listing for further information.
+.SH SEE ALSO
+.PP
+.IR xntpd (8)
+.SH HISTORY
+.PP
+Written by Dennis Ferguson at the University of Toronto.
+.SH BUGS
+.PP
+.I Xntpdc
+is a crude hack. Much of the information it shows is deadly boring and
+could only be loved by its implementer. The program was designed so that
+new (and temporary) features were easy to hack in, at great expense to
+the program's ease of use. Despite this, the program is occasionally
+useful.
diff --git a/usr.sbin/xntpd/include/README b/usr.sbin/xntpd/include/README
new file mode 100644
index 0000000..73d0620
--- /dev/null
+++ b/usr.sbin/xntpd/include/README
@@ -0,0 +1,21 @@
+README file for directory ./include of the NTP Version 3 distribution
+
+This directory contains the include files used by most programs in this
+distribution. The ./sys directory in this directory contains system
+header files used by the clock discipline and STREAMS modules in the
+../kernel directory.
+
+Note that multicast support (MCAST define) requires the header file
+/usr/include/netinet/in.h for the particular architecture to be in place.
+This file is constructed during the installation process in older systems;
+it is already in place for those machines that support multicast ex box.
+The file in.h included in this distribution is for Suns; the files for
+other systems can be found in the multicast distribtutions for each
+architecture separately.
+
+If the precision-time kernel (KERNEL_PLL define) is configured, the
+installation process requires the header file /usr/include/sys/timex.h
+for the particular architecture to be in place. The file timex.h included
+in this distribution is for Suns; the files for other systems can be
+found in the kernel distributions available from the manufacturer's
+representatives.
diff --git a/usr.sbin/xntpd/include/in.h b/usr.sbin/xntpd/include/in.h
new file mode 100644
index 0000000..c18d569
--- /dev/null
+++ b/usr.sbin/xntpd/include/in.h
@@ -0,0 +1,256 @@
+/* @(#)in.h 1.19 90/07/27 SMI; from UCB 7.5 2/22/88 */
+
+/*
+ * Copyright (c) 1982, 1986 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that this notice is preserved and that due credit is given
+ * to the University of California at Berkeley. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission. This software
+ * is provided ``as is'' without express or implied warranty.
+ */
+
+/*
+ * Constants and structures defined by the internet system,
+ * Per RFC 790, September 1981.
+ */
+
+#ifndef _netinet_in_h
+#define _netinet_in_h
+
+/*
+ * Protocols
+ */
+#define IPPROTO_IP 0 /* dummy for IP */
+#define IPPROTO_ICMP 1 /* control message protocol */
+#define IPPROTO_IGMP 2 /* group control protocol */
+#define IPPROTO_GGP 3 /* gateway^2 (deprecated) */
+#define IPPROTO_ST 5 /* st */
+#define IPPROTO_TCP 6 /* tcp */
+#define IPPROTO_EGP 8 /* exterior gateway protocol */
+#define IPPROTO_PUP 12 /* pup */
+#define IPPROTO_UDP 17 /* user datagram protocol */
+#define IPPROTO_IDP 22 /* xns idp */
+#define IPPROTO_HELLO 63 /* "hello" routing protocol */
+#define IPPROTO_ND 77 /* UNOFFICIAL net disk proto */
+#define IPPROTO_OSPF 89 /* Open SPF IGP */
+
+#define IPPROTO_RAW 255 /* raw IP packet */
+#define IPPROTO_MAX 256
+
+/*
+ * Port/socket numbers: network standard functions
+ */
+#define IPPORT_ECHO 7
+#define IPPORT_DISCARD 9
+#define IPPORT_SYSTAT 11
+#define IPPORT_DAYTIME 13
+#define IPPORT_NETSTAT 15
+#define IPPORT_FTP 21
+#define IPPORT_TELNET 23
+#define IPPORT_SMTP 25
+#define IPPORT_TIMESERVER 37
+#define IPPORT_NAMESERVER 42
+#define IPPORT_WHOIS 43
+#define IPPORT_MTP 57
+
+/*
+ * Port/socket numbers: host specific functions
+ */
+#define IPPORT_TFTP 69
+#define IPPORT_RJE 77
+#define IPPORT_FINGER 79
+#define IPPORT_TTYLINK 87
+#define IPPORT_SUPDUP 95
+
+/*
+ * UNIX TCP sockets
+ */
+#define IPPORT_EXECSERVER 512
+#define IPPORT_LOGINSERVER 513
+#define IPPORT_CMDSERVER 514
+#define IPPORT_EFSSERVER 520
+
+/*
+ * UNIX UDP sockets
+ */
+#define IPPORT_BIFFUDP 512
+#define IPPORT_WHOSERVER 513
+#define IPPORT_ROUTESERVER 520 /* 520+1 also used */
+
+/*
+ * Ports < IPPORT_RESERVED are reserved for
+ * privileged processes (e.g. root).
+ * Ports > IPPORT_USERRESERVED are reserved
+ * for servers, not necessarily privileged.
+ */
+#define IPPORT_RESERVED 1024
+#define IPPORT_USERRESERVED 5000
+
+/*
+ * Link numbers
+ */
+#define IMPLINK_IP 155
+#define IMPLINK_LOWEXPER 156
+#define IMPLINK_HIGHEXPER 158
+
+/*
+ * Internet address
+ * This definition contains obsolete fields for compatibility
+ * with SunOS 3.x and 4.2bsd. The presence of subnets renders
+ * divisions into fixed fields misleading at best. New code
+ * should use only the s_addr field.
+ */
+struct in_addr {
+ union {
+ struct { u_char s_b1,s_b2,s_b3,s_b4; } S_un_b;
+ struct { u_short s_w1,s_w2; } S_un_w;
+ u_long S_addr;
+ } S_un;
+#define s_addr S_un.S_addr /* should be used for all code */
+#define s_host S_un.S_un_b.s_b2 /* OBSOLETE: host on imp */
+#define s_net S_un.S_un_b.s_b1 /* OBSOLETE: network */
+#define s_imp S_un.S_un_w.s_w2 /* OBSOLETE: imp */
+#define s_impno S_un.S_un_b.s_b4 /* OBSOLETE: imp # */
+#define s_lh S_un.S_un_b.s_b3 /* OBSOLETE: logical host */
+};
+
+/*
+ * Definitions of bits in internet address integers.
+ * On subnets, the decomposition of addresses to host and net parts
+ * is done according to subnet mask, not the masks here.
+ */
+#define IN_CLASSA(i) (((long)(i) & 0x80000000) == 0)
+#define IN_CLASSA_NET 0xff000000
+#define IN_CLASSA_NSHIFT 24
+#define IN_CLASSA_HOST 0x00ffffff
+#define IN_CLASSA_MAX 128
+
+#define IN_CLASSB(i) (((long)(i) & 0xc0000000) == 0x80000000)
+#define IN_CLASSB_NET 0xffff0000
+#define IN_CLASSB_NSHIFT 16
+#define IN_CLASSB_HOST 0x0000ffff
+#define IN_CLASSB_MAX 65536
+
+#define IN_CLASSC(i) (((long)(i) & 0xe0000000) == 0xc0000000)
+#define IN_CLASSC_NET 0xffffff00
+#define IN_CLASSC_NSHIFT 8
+#define IN_CLASSC_HOST 0x000000ff
+
+#define IN_CLASSD(i) (((long)(i) & 0xf0000000) == 0xe0000000)
+#define IN_CLASSD_NET 0xf0000000 /* These ones aren't really */
+#define IN_CLASSD_NSHIFT 28 /* net and host fields, but */
+#define IN_CLASSD_HOST 0x0fffffff /* routing needn't know. */
+#define IN_MULTICAST(i) IN_CLASSD(i)
+
+#define IN_EXPERIMENTAL(i) (((long)(i) & 0xe0000000) == 0xe0000000)
+#define IN_BADCLASS(i) (((long)(i) & 0xf0000000) == 0xf0000000)
+
+#define INADDR_ANY (u_long)0x00000000
+#define INADDR_LOOPBACK (u_long)0x7F000001
+#define INADDR_BROADCAST (u_long)0xffffffff /* must be masked */
+
+#define INADDR_UNSPEC_GROUP (u_long)0xe0000000 /* 224.0.0.0 */
+#define INADDR_ALLHOSTS_GROUP (u_long)0xe0000001 /* 224.0.0.1 */
+#define INADDR_MAX_LOCAL_GROUP (u_long)0xe00000ff /* 224.0.0.255 */
+
+#define IN_LOOPBACKNET 127 /* official! */
+
+/*
+ * Define a macro to stuff the loopback address into an Internet address
+ */
+#define IN_SET_LOOPBACK_ADDR(a) {(a)->sin_addr.s_addr = htonl(INADDR_LOOPBACK); \
+ (a)->sin_family = AF_INET;}
+
+/*
+ * Socket address, internet style.
+ */
+struct sockaddr_in {
+ short sin_family;
+ u_short sin_port;
+ struct in_addr sin_addr;
+ char sin_zero[8];
+};
+
+/*
+ * Options for use with [gs]etsockopt at the IP level.
+ */
+#define IP_OPTIONS 1 /* set/get IP per-packet options */
+#define IP_MULTICAST_IF 2 /* set/get IP multicast interface */
+#define IP_MULTICAST_TTL 3 /* set/get IP multicast timetolive */
+#define IP_MULTICAST_LOOP 4 /* set/get IP multicast loopback */
+#define IP_ADD_MEMBERSHIP 5 /* add an IP group membership */
+#define IP_DROP_MEMBERSHIP 6 /* drop an IP group membership */
+
+#define IP_DEFAULT_MULTICAST_TTL 1 /* normally limit m'casts to 1 hop */
+#define IP_DEFAULT_MULTICAST_LOOP 1 /* normally hear sends if a member */
+#define IP_MAX_MEMBERSHIPS 20 /* per socket; must fit in one mbuf */
+
+/*
+ * Argument structure for IP_ADD_MEMBERSHIP and IP_DROP_MEMBERSHIP.
+ */
+struct ip_mreq {
+ struct in_addr imr_multiaddr; /* IP multicast address of group */
+ struct in_addr imr_interface; /* local IP address of interface */
+};
+
+#if !defined(vax) && !defined(ntohl) && !defined(i386)
+/*
+ * Macros for number representation conversion.
+ */
+#define ntohl(x) (x)
+#define ntohs(x) (x)
+#define htonl(x) (x)
+#define htons(x) (x)
+#endif
+
+#if !defined(ntohl) && (defined(vax) || defined(i386))
+u_short ntohs(), htons();
+u_long ntohl(), htonl();
+#endif
+
+#ifdef KERNEL
+extern struct domain inetdomain;
+extern struct protosw inetsw[];
+struct in_addr in_makeaddr();
+u_long in_netof(), in_lnaof();
+#endif
+
+#ifndef BYTE_ORDER
+/*
+ * Definitions for byte order,
+ * according to byte significance from low address to high.
+ */
+#define LITTLE_ENDIAN 1234 /* least-significant byte first (vax) */
+#define BIG_ENDIAN 4321 /* most-significant byte first (IBM, net) */
+#define PDP_ENDIAN 3412 /* LSB first in word, MSW first in long (pdp) */
+
+#if defined(vax) || defined(i386)
+#define BYTE_ORDER LITTLE_ENDIAN
+#else
+#define BYTE_ORDER BIG_ENDIAN /* mc68000, tahoe, most others */
+#endif
+#endif BYTE_ORDER
+
+/*
+ * Macros for number representation conversion.
+ */
+#if BYTE_ORDER==LITTLE_ENDIAN
+#define NTOHL(d) ((d) = ntohl((d)))
+#define NTOHS(d) ((d) = ntohs((d)))
+#define HTONL(d) ((d) = htonl((d)))
+#define HTONS(d) ((d) = htons((d)))
+#else
+#define ntohl(x) (x)
+#define ntohs(x) (x)
+#define htonl(x) (x)
+#define htons(x) (x)
+#define NTOHL(d)
+#define NTOHS(d)
+#define HTONL(d)
+#define HTONS(d)
+#endif
+
+#endif /*!_netinet_in_h*/
diff --git a/usr.sbin/xntpd/include/l_stdlib.h b/usr.sbin/xntpd/include/l_stdlib.h
new file mode 100644
index 0000000..221682e
--- /dev/null
+++ b/usr.sbin/xntpd/include/l_stdlib.h
@@ -0,0 +1,284 @@
+/*
+ * Proto types for machines that are not ANSI and POSIX compliant.
+ * This is optionaly
+ */
+
+#ifndef _l_stdlib_h
+#define _l_stdlib_h
+
+#if defined(NTP_POSIX_SOURCE)
+#include <stdlib.h>
+#endif
+
+#ifndef P
+#if defined(__STDC__) || defined(USE_PROTOTYPES)
+#define P(x) x
+#else
+#define P(x) ()
+#if !defined(const)
+#define const
+#endif
+#endif
+#endif
+
+/*
+ * Unprottyped library functions for SunOS 4.x.x
+ */
+#ifdef SYS_SUNOS4
+extern void closelog P((void));
+extern void openlog P((char *, int, int));
+extern void syslog P((int, char *, ...));
+extern int setlogmask P((int));
+
+extern char * getpass P((char *));
+
+extern int setpriority P((int ,int ,int));
+
+extern long strtol P((char *, char **, int));
+
+#if !defined(NTP_POSIX_SOURCE)
+extern int atoi P((char *));
+extern int dup2 P((int, int));
+extern int execve P((char *, char **,char **));
+extern int fork P((void));
+extern int getdtablesize P((void));
+extern int qsort P((void *, int , int,
+ int (*compar)(void *, void *)));
+extern int rand P((void));
+extern int setpgrp P((int, int));
+extern void srand P((unsigned int));
+extern void bcopy P((char *, char *, int));
+#endif
+
+#ifndef bzero /* XXX macro prototyping clash */
+extern void bzero P((char *, int));
+extern int bcmp P((char *, char *, int));
+extern void bcopy P((char *, char *, int));
+#endif
+extern char *mktemp P((char *));
+
+extern int tolower P((int));
+
+extern int isatty P((int));
+
+extern unsigned sleep P((unsigned ));
+extern unsigned int alarm P((unsigned int));
+extern int pause P((void));
+
+extern int getpid P((void));
+extern int getppid P((void));
+
+extern int close P((int));
+extern int ioctl P((int, int, char *));
+extern int read P((int, void *, unsigned));
+extern int rename P((char *, char *));
+extern int write P((int, const void *, unsigned));
+extern int unlink P((const char *));
+extern int link P((const char *, const char *));
+
+#ifdef FILE
+extern int fclose P((FILE *));
+extern int fflush P((FILE *));
+extern int fprintf P((FILE *, char *, ...));
+extern int fscanf P((FILE *, char *, ...));
+extern int fputs P((char *, FILE *));
+extern int fputc P((char, FILE *));
+extern int fread P((char *, int, int, FILE *));
+extern int printf P((char *, ...));
+extern int setbuf P((FILE *, char *));
+extern int setvbuf P((FILE *, char *, int, int));
+extern int scanf P((char *, ...));
+extern int sscanf P((char *, char *, ...));
+extern int vsprintf P((char *, char *, ...));
+extern int _flsbuf P((int, FILE *));
+extern int _filbuf P((FILE *));
+extern void perror P((char *));
+#ifndef NTP_POSIX_SOURCE
+extern int setlinebuf P((FILE *));
+#endif
+#endif
+
+#ifdef _ntp_string_h
+#ifdef NTP_POSIX_SOURCE /* these are builtins */
+#ifndef NTP_NEED_BOPS /* but may be emulated by bops */
+extern char *memcpy();
+extern char *memset();
+extern int memcmp();
+#endif
+#endif
+#endif
+
+#ifdef _sys_socket_h
+extern int bind P((int, struct sockaddr *, int));
+extern int connect P((int, struct sockaddr *, int));
+extern int sendto P((int, char *, int, int, struct sockaddr *, int));
+extern int setsockopt P((int, int, int, char *, int));
+extern int socket P((int, int, int));
+extern int recvfrom P((int, char *, int, int, struct sockaddr *, int *));
+#endif /* _sys_socket_h */
+
+#ifdef _ntp_select_h
+extern int select P((int, fd_set *, fd_set *, fd_set *, struct timeval *));
+#endif
+
+#ifdef _sys_time_h
+extern int adjtime P((struct timeval *, struct timeval *));
+extern int setitimer P((int , struct itimerval *, struct itimerval *));
+#ifdef SYSV_TIMEOFDAY
+extern int gettimeofday P((struct timeval *));
+extern int settimeofday P((struct timeval *));
+#else /* ! SYSV_TIMEOFDAY */
+extern int gettimeofday P((struct timeval *, struct timezone *));
+extern int settimeofday P((struct timeval *, struct timezone *));
+#endif /* SYSV_TIMEOFDAY */
+#endif /* _sys_time_h */
+
+#ifdef __time_h
+extern time_t time P((time_t *));
+#endif
+
+#ifdef __setjmp_h
+extern int setjmp P((jmp_buf));
+extern void longjmp P((jmp_buf, int));
+#endif
+
+#ifdef _sys_resource_h
+extern int getrusage P((int, struct rusage *));
+#endif
+
+#ifdef _nlist_h
+extern int nlist P((char *, struct nlist *));
+#endif
+
+#endif /* SYS_SUNOS4 */
+
+/*
+ * Unprototyped library functions for DEC OSF/1
+ */
+#ifdef SYS_DECOSF1
+#ifndef _MACHINE_ENDIAN_H_
+#define _MACHINE_ENDIAN_H_
+extern u_short htons P((u_short));
+extern u_short ntohs P((u_short));
+extern U_LONG htonl P((U_LONG));
+extern U_LONG ntohl P((U_LONG));
+#endif /* _MACHINE_ENDIAN_H_ */
+
+/*
+extern char * getpass P((char *));
+*/
+extern char * mktemp P((char *));
+#ifndef SYS_IX86OSF1
+extern int ioctl P((int, u_long, char *));
+extern void bzero P((char *, int));
+#endif
+
+#ifdef SOCK_DGRAM
+extern int bind P((int, const struct sockaddr *, int));
+extern int connect P((int, const struct sockaddr *, int));
+extern int socket P((int, int, int));
+extern int sendto P((int, const void *, int, int, const struct sockaddr *, int));
+extern int setsockopt P((int, int, int, const void *, int));
+extern int recvfrom P((int, void *, int, int, struct sockaddr *, int *));
+#endif /* SOCK_STREAM */
+
+#ifdef _ntp_select_h
+extern int select P((int, fd_set *, fd_set *, fd_set *, struct timeval *));
+#endif
+
+#endif /* DECOSF1 */
+
+/*
+ * Unprototyped library functions for Ultrix
+ */
+#ifdef SYS_ULTRIX
+extern int close P((int));
+extern char * getpass P((char *));
+extern int getpid P((void));
+extern int ioctl P((int, int, char *));
+extern char *mktemp P((char *));
+extern int unlink P((const char *));
+extern int link P((const char *, const char *));
+
+extern void closelog P((void));
+extern void syslog P((int, char *, ...));
+#ifndef LOG_DAEMON
+extern void openlog P((char *, int));
+#else
+extern void openlog P((char *, int, int));
+#endif
+
+extern int setpriority P((int ,int ,int ));
+
+#ifdef SOCK_DGRAM
+extern int bind P((int, struct sockaddr *, int));
+extern int connect P((int, struct sockaddr *, int));
+extern int socket P((int, int, int));
+extern int sendto P((int, char *, int, int, struct sockaddr *, int));
+extern int setsockopt P((int, int, int, char *, int));
+extern int recvfrom P((int, char *, int, int, struct sockaddr *, int *));
+#endif /* SOCK_STREAM */
+
+#ifdef _TIME_H_
+extern int gettimeofday P((struct timeval *, struct timezone *));
+extern int settimeofday P((struct timeval *, struct timezone *));
+extern int adjtime P((struct timeval *, struct timeval *));
+extern int select P((int, fd_set *, fd_set *, fd_set *, struct timeval *));
+extern int setitimer P((int , struct itimerval *, struct itimerval *));
+#endif /* _TIME_H_ */
+
+#ifdef N_UNDF
+extern int nlist P((char *, struct nlist *));
+#endif
+
+#ifndef bzero /* XXX macro prototyping clash */
+extern void bzero P((char *, int));
+extern int bcmp P((char *, char *, int));
+extern void bcopy P((char *, char *, int));
+#endif
+
+#ifndef NTP_POSIX_SOURCE
+extern int atoi P((char *));
+extern void bzero P((char *, int));
+extern int bcmp P((char *, char *, int));
+extern void bcopy P((char *, char *, int));
+extern int execve P((char *, char **,char **));
+extern int fork P((void));
+extern int getdtablesize P((void));
+extern int ran P((void));
+extern int rand P((void));
+extern void srand P((unsigned int));
+#ifdef _TIME_H_
+extern int gettimeofday P((struct timeval *, struct timezone *));
+extern int settimeofday P((struct timeval *, struct timezone *));
+#endif
+#endif
+
+#ifdef _RESOURCE_H_
+extern int getrusage P((int, struct rusage *));
+#endif
+
+#endif /* SYS_ULTRIX */
+
+#if defined(__convex__)
+extern char * getpass P((char *));
+#endif
+
+#ifdef SYS_IRIX4
+extern char * getpass P((char *));
+#endif /* IRIX4 */
+
+#ifdef SYS_VAX
+extern char * getpass P((char *));
+#endif /* VAX */
+
+#ifdef SYS_DOMAINOS
+extern char * getpass P((char *));
+#endif /* SYS_DOMAINOS */
+
+#ifdef SYS_BSD
+#define IN_CLASSD(i) (((long)(i) & 0xf0000000) == 0xe0000000)
+#endif
+
+#endif /* l_stdlib_h */
+
diff --git a/usr.sbin/xntpd/include/md5.h b/usr.sbin/xntpd/include/md5.h
new file mode 100644
index 0000000..82b43e1
--- /dev/null
+++ b/usr.sbin/xntpd/include/md5.h
@@ -0,0 +1,56 @@
+/*
+ ***********************************************************************
+ ** md5.h -- header file for implementation of MD5 **
+ ** RSA Data Security, Inc. MD5 Message-Digest Algorithm **
+ ** Created: 2/17/90 RLR **
+ ** Revised: 12/27/90 SRD,AJ,BSK,JT Reference C version **
+ ** Revised (for MD5): RLR 4/27/91 **
+ ***********************************************************************
+ */
+
+/*
+ ***********************************************************************
+ ** Copyright (C) 1990, RSA Data Security, Inc. All rights reserved. **
+ ** **
+ ** License to copy and use this software is granted provided that **
+ ** it is identified as the "RSA Data Security, Inc. MD5 Message- **
+ ** Digest Algorithm" in all material mentioning or referencing this **
+ ** software or this function. **
+ ** **
+ ** License is also granted to make and use derivative works **
+ ** provided that such works are identified as "derived from the RSA **
+ ** Data Security, Inc. MD5 Message-Digest Algorithm" in all **
+ ** material mentioning or referencing the derived work. **
+ ** **
+ ** RSA Data Security, Inc. makes no representations concerning **
+ ** either the merchantability of this software or the suitability **
+ ** of this software for any particular purpose. It is provided "as **
+ ** is" without express or implied warranty of any kind. **
+ ** **
+ ** These notices must be retained in any copies of any part of this **
+ ** documentation and/or software. **
+ ***********************************************************************
+ */
+
+#include "ntp_types.h"
+
+/* typedef a 32-bit type */
+typedef unsigned LONG UINT4;
+
+/* Data structure for MD5 (Message-Digest) computation */
+typedef struct {
+ UINT4 i[2]; /* number of _bits_ handled mod 2^64 */
+ UINT4 buf[4]; /* scratch buffer */
+ unsigned char in[64]; /* input buffer */
+ unsigned char digest[16]; /* actual digest after MD5Final call */
+} MD5_CTX;
+
+void MD5Init ();
+void MD5Update ();
+void MD5Final ();
+
+/*
+ ***********************************************************************
+ ** End of md5.h **
+ ******************************** (cut) ********************************
+ */
diff --git a/usr.sbin/xntpd/include/mx4200.h b/usr.sbin/xntpd/include/mx4200.h
new file mode 100644
index 0000000..13058de
--- /dev/null
+++ b/usr.sbin/xntpd/include/mx4200.h
@@ -0,0 +1,40 @@
+
+/* records transmitted from extern CDU to MX 4200 */
+#define PMVXG_S_INITMODEA 0 /* initialization/mode part A */
+#define PMVXG_S_INITMODEB 1 /* initialization/mode part B*/
+#define PMVXG_S_SATHEALTH 2 /* satellite health control */
+#define PMVXG_S_DIFFNAV 3 /* differential navigation control */
+#define PMVXG_S_PORTCONF 7 /* control port configuration */
+#define PMVXG_S_GETSELFTEST 3 /* self test (request results) */
+#define PMVXG_S_RTCMCONF 16 /* RTCM port configuration */
+#define PMVXG_S_PASSTHRU 17 /* equipment port pass-thru config */
+#define PMVXG_S_RESTART 18 /* restart control */
+#define PMVXG_S_OSCPARAM 19 /* oscillator parameter */
+#define PMVXG_S_DOSELFTEST 20 /* self test (activate a test) */
+#define PMVXG_S_TRECOVCONF 23 /* time recovery configuration */
+#define PMVXG_S_RAWDATASEL 24 /* raw data port data selection */
+#define PMVXG_S_EQUIPCONF 26 /* equipment port configuration */
+#define PMVXG_S_RAWDATACONF 27 /* raw data port configuration */
+
+/* records transmitted from MX 4200 to external CDU */
+#define PMVXG_D_STATUS 0 /* status */
+#define PMVXG_D_POSITION 1 /* position */
+#define PMVXG_D_OPDOPS 3 /* (optimum) DOPs */
+#define PMVXG_D_MODEDATA 4 /* mode data */
+#define PMVXG_D_SATPRED 5 /* satellite predictions */
+#define PMVXG_D_SATHEALTH 6 /* satellite health status */
+#define PMVXG_D_UNRECOG 7 /* unrecognized request response */
+#define PMVXG_D_SIGSTRLOC 8 /* sig strength & location (sats 1-4) */
+#define PMVXG_D_SPEEDHEAD 11 /* speed/heading data */
+#define PMVXG_D_OSELFTEST 12 /* (old) self-test results */
+#define PMVXG_D_SIGSTRLOC2 18 /* sig strength & location (sats 5-8) */
+#define PMVXG_D_OSCPARAM 19 /* oscillator parameter */
+#define PMVXG_D_SELFTEST 20 /* self test results */
+#define PMVXG_D_PHV 21 /* position, height & velocity */
+#define PMVXG_D_DOPS 22 /* DOPs */
+#define PMVXG_D_SOFTCONF 30 /* software configuration */
+#define PMVXG_D_DIFFGPSMODE 503 /* differential gps moding */
+#define PMVXG_D_TRECOVUSEAGE 523 /* time recovery usage */
+#define PMVXG_D_RAWDATAOUT 524 /* raw data port data output */
+#define PMVXG_D_TRECOVRESULT 828 /* time recovery results */
+#define PMVXG_D_TRECOVOUT 830 /* time recovery output message */
diff --git a/usr.sbin/xntpd/include/ntp.h b/usr.sbin/xntpd/include/ntp.h
new file mode 100644
index 0000000..5eca0e8
--- /dev/null
+++ b/usr.sbin/xntpd/include/ntp.h
@@ -0,0 +1,706 @@
+/*
+ * ntp.h - NTP definitions for the masses
+ */
+
+#include "ntp_types.h"
+
+/*
+ * How to get signed characters. On machines where signed char works,
+ * use it. On machines where signed char doesn't work, char had better
+ * be signed.
+ */
+#if !defined(S_CHAR_DEFINED)
+#if defined(NO_SIGNED_CHAR_DECL)
+typedef char s_char;
+#else
+typedef signed char s_char;
+#endif
+#ifdef sequent
+#undef SO_RCVBUF
+#undef SO_SNDBUF
+#endif
+#endif
+
+/*
+ * NTP protocol parameters. See section 3.2.6 of the specification.
+ */
+#define NTP_VERSION ((u_char)3) /* current version number */
+#define NTP_OLDVERSION ((u_char)1) /* oldest credible version */
+#define NTP_PORT 123 /* included for sake of non-unix machines */
+#define NTP_MAXSTRATUM ((u_char)15) /* max stratum, infinity a la Bellman-Ford */
+#define NTP_MAXAGE 86400 /* one day in seconds */
+#define NTP_MAXSKEW 1 /* 1 sec, skew after NTP_MAXAGE w/o updates */
+#define NTP_SKEWINC 49170 /* skew increment for clock updates (l_f) */
+#define NTP_SKEWFACTOR 16 /* approximation of factor for peer calcs */
+#define NTP_MAXDISTANCE (1 * FP_SECOND) /* max. rootdelay for synchr. */
+#define NTP_MINDPOLL 6 /* log2 default min poll interval (64 s) */
+#define NTP_MAXDPOLL 10 /* log2 default max poll interval (~17 m) */
+#define NTP_MINPOLL 4 /* log2 min poll interval (16 s) */
+#define NTP_MAXPOLL 14 /* log2 max poll interval (~4.5 h) */
+#define NTP_MINCLOCK 3 /* minimum for outlyer detection */
+#define NTP_MAXCLOCK 10 /* maximum select list size */
+#define NTP_MINDISPERSE (FP_SECOND / 100) /* min dispersion (u_fp 10 ms) */
+#define NTP_MAXDISPERSE (FP_SECOND * 16) /* max dispersion (u_fp 16 s) */
+#define NTP_DISPFACTOR 20 /* MAXDISPERSE as a shift (u_fp 16 s) */
+#define NTP_WINDOW 8 /* reachability register size */
+#define NTP_SHIFT 8 /* 8 suitable for crystal time base */
+#define NTP_MAXKEY 65535 /* maximum authentication key number */
+#define NTP_MAXD 3 /* log2 estimated error averaging factor */
+
+/*
+ * Loop filter parameters. See section 5.1 of the specification.
+ *
+ * Note that these are appropriate for a crystal time base. If your
+ * system clock is line frequency controlled you should read the
+ * specification for appropriate modifications. Note that the
+ * loop filter code will have to change if you change CLOCK_MAX
+ * to be greater than or equal to 500 ms.
+ *
+ * Note these parameters have been rescaled for a time constant range from
+ * 0 through 10, with 2 corresoponding to the old time constant of 0.
+ */
+#define CLOCK_MINSTEP 900 /* step timeout (sec) */
+#define CLOCK_ADJ 0 /* log2 adjustment interval (1 sec) */
+#define CLOCK_DSCALE 20 /* skew reg. scale: unit is 2**-20 ~= 1 ppm */
+#define CLOCK_FREQ 16 /* log2 frequency weight (65536) */
+#define CLOCK_PHASE 6 /* log2 phase weight (64) */
+#define CLOCK_LIMIT 30 /* time constant adjust threshold */
+#define CLOCK_G 2 /* log2 frequency averaging factor */
+#define CLOCK_MAXSEC 800 /* max update interval for pll */
+
+#define CLOCK_MAX_FP 0x000020c5 /* max clock offset (s_fp 128 ms) */
+#define CLOCK_MAX_F 0x20c49ba6 /* max clock offset (l_fp 128 ms) */
+#define CLOCK_MAX_I 0x00000000 /* both fractional and integral parts */
+
+#define CLOCK_WAYTOOBIG 1000 /* if clock 1000 sec off, forget it */
+
+/*
+ * Event timers are actually implemented as a sorted queue of expiry
+ * times. The queue is slotted, with each slot holding timers which
+ * expire in a 2**(NTP_MINPOLL-1) (8) second period. The timers in
+ * each slot are sorted by increasing expiry time. The number of
+ * slots is 2**(NTP_MAXPOLL-(NTP_MINPOLL-1)), or 512, to cover a time
+ * period of 2**NTP_MAXPOLL (16384) seconds into the future before
+ * wrapping.
+ */
+#define EVENT_TIMEOUT CLOCK_ADJ
+
+struct event {
+ struct event *next; /* next in chain */
+ struct event *prev; /* previous in chain */
+ struct peer *peer; /* peer this counter belongs to */
+ void (*event_handler)(); /* routine to call to handle event */
+ u_long event_time; /* expiry time of counter */
+};
+
+#define TIMER_SLOTTIME (1<<(NTP_MINPOLL-1))
+#define TIMER_NSLOTS (1<<(NTP_MAXPOLL-(NTP_MINPOLL-1)))
+#define TIMER_SLOT(t) (((t) >> (NTP_MINPOLL-1)) & (TIMER_NSLOTS-1))
+
+/*
+ * TIMER_ENQUEUE() puts stuff on the timer queue. It takes as
+ * arguments (ea), an array of event slots, and (iev), the event
+ * to be inserted. This one searches the hash bucket from the
+ * end, and is about optimum for the timing requirements of
+ * NTP peers.
+ */
+#define TIMER_ENQUEUE(ea, iev) \
+ do { \
+ register struct event *ev; \
+ \
+ ev = (ea)[TIMER_SLOT((iev)->event_time)].prev; \
+ while (ev->event_time > (iev)->event_time) \
+ ev = ev->prev; \
+ (iev)->prev = ev; \
+ (iev)->next = ev->next; \
+ (ev)->next->prev = (iev); \
+ (ev)->next = (iev); \
+ } while(0)
+
+/*
+ * TIMER_INSERT() also puts stuff on the timer queue, but searches the
+ * bucket from the top. This is better for things that do very short
+ * time outs, like clock support.
+ */
+#define TIMER_INSERT(ea, iev) \
+ do { \
+ register struct event *ev; \
+ \
+ ev = (ea)[TIMER_SLOT((iev)->event_time)].next; \
+ while (ev->event_time != 0 && \
+ ev->event_time < (iev)->event_time) \
+ ev = ev->next; \
+ (iev)->next = ev; \
+ (iev)->prev = ev->prev; \
+ (ev)->prev->next = (iev); \
+ (ev)->prev = (iev); \
+ } while(0)
+
+/*
+ * Remove an event from the queue.
+ */
+#define TIMER_DEQUEUE(ev) \
+ do { \
+ if ((ev)->next != 0) { \
+ (ev)->next->prev = (ev)->prev; \
+ (ev)->prev->next = (ev)->next; \
+ (ev)->next = (ev)->prev = 0; \
+ } \
+ } while (0)
+
+/*
+ * The interface structure is used to hold the addresses and socket
+ * numbers of each of the interfaces we are using.
+ */
+struct interface {
+ int fd; /* socket this is opened on */
+ int bfd; /* socket for receiving broadcasts */
+ struct sockaddr_in sin; /* interface address */
+ struct sockaddr_in bcast; /* broadcast address */
+ struct sockaddr_in mask; /* interface mask */
+ char name[8]; /* name of interface */
+ int flags; /* interface flags */
+ int last_ttl; /* last TTL specified */
+ long received; /* number of incoming packets */
+ long sent; /* number of outgoing packets */
+ long notsent; /* number of send failures */
+};
+
+/*
+ * Flags for interfaces
+ */
+#define INT_BROADCAST 1 /* can broadcast out this interface */
+#define INT_BCASTOPEN 2 /* broadcast socket is open */
+#define INT_LOOPBACK 4 /* the loopback interface */
+#define INT_MULTICAST 8 /* multicasting enabled */
+
+/*
+ * Define flasher bits (tests 1 through 8 in packet procedure)
+ * These reveal the state at the last grumble from the peer and are
+ * most handy for diagnosing problems, even if not strictly a state
+ * variable in the spec. These are recorded in the peer structure.
+ */
+#define TEST1 0x01 /* duplicate packet received */
+#define TEST2 0x02 /* bogus packet received */
+#define TEST3 0x04 /* protocol unsynchronized */
+#define TEST4 0x08 /* peer delay/dispersion bounds check */
+#define TEST5 0x10 /* peer authentication failed */
+#define TEST6 0x20 /* peer clock unsynchronized */
+#define TEST7 0x40 /* peer stratum out of bounds */
+#define TEST8 0x80 /* root delay/dispersion bounds check */
+
+/*
+ * The peer structure. Holds state information relating to the guys
+ * we are peering with. Most of this stuff is from section 3.2 of the
+ * spec.
+ */
+struct peer {
+ struct peer *next;
+ struct peer *ass_next; /* link pointer in associd hash */
+ struct sockaddr_in srcadr; /* address of remote host */
+ struct interface *dstadr; /* pointer to address on local host */
+ struct refclockproc *procptr; /* pointer to reference clock sutuff */
+ u_char leap; /* leap indicator */
+ u_char hmode; /* association mode with this peer */
+ u_char pmode; /* peer's association mode */
+ u_char stratum; /* stratum of remote peer */
+ s_char precision; /* peer's clock precision */
+ u_char ppoll; /* peer poll interval */
+ u_char hpoll; /* local host poll interval */
+ u_char minpoll; /* min local host poll interval */
+ u_char maxpoll; /* max local host poll interval */
+ u_char version; /* version number */
+ u_char flags; /* peer flags */
+ u_char cast_flags; /* flags MDF_?CAST */
+ u_char flash; /* peer flashers (for maint) */
+ u_char refclktype; /* reference clock type */
+ u_char refclkunit; /* reference clock unit number */
+ u_char sstclktype; /* clock type for system status word */
+ s_fp rootdelay; /* distance from primary clock */
+ u_fp rootdispersion; /* peer clock dispersion */
+ U_LONG refid; /* peer reference ID */
+ l_fp reftime; /* time of peer's last update */
+ struct event event_timer; /* event queue entry */
+ U_LONG keyid; /* encription key ID */
+ U_LONG pkeyid; /* keyid used to encrypt last message */
+ u_short associd; /* association ID, a unique integer */
+ u_char ttl; /* time to live (multicast) */
+/* **Start of clear-to-zero area.*** */
+/* Everything that is cleared to zero goes below here */
+ u_char valid; /* valid counter */
+#define clear_to_zero valid
+ u_char reach; /* reachability, NTP_WINDOW bits */
+ u_char unreach; /* unreachable count */
+ u_short filter_nextpt; /* index into filter shift register */
+ s_fp filter_delay[NTP_SHIFT]; /* delay part of shift register */
+ l_fp filter_offset[NTP_SHIFT]; /* offset part of shift register */
+ s_fp filter_soffset[NTP_SHIFT]; /* offset in s_fp format, for disp */
+ l_fp org; /* originate time stamp */
+ l_fp rec; /* receive time stamp */
+ l_fp xmt; /* transmit time stamp */
+/* ***End of clear-to-zero area.*** */
+/* Everything that is cleared to zero goes above here */
+ u_char filter_order[NTP_SHIFT]; /* we keep the filter sorted here */
+#define end_clear_to_zero filter_order[0]
+ u_fp filter_error[NTP_SHIFT]; /* error part of shift register */
+ long update; /* base sys_clock for skew calc.s */
+ s_fp delay; /* filter estimated delay */
+ u_fp dispersion; /* filter estimated dispersion */
+ l_fp offset; /* filter estimated clock offset */
+ s_fp soffset; /* fp version of above */
+ s_fp synch; /* synch distance from above */
+ u_fp selectdisp; /* select dispersion */
+ s_fp estbdelay; /* broadcast offset */
+
+ /*
+ * statistic counters
+ */
+ u_long timereset; /* time stat counters were reset */
+ u_long sent; /* number of updates sent */
+ u_long received; /* number of frames received */
+ u_long timereceived; /* last time a frame received */
+ u_long timereachable; /* last reachable/unreachable event */
+ u_long processed; /* processed by the protocol */
+ u_long badauth; /* bad credentials detected */
+ u_long bogusorg; /* rejected due to bogus origin */
+ u_long oldpkt; /* rejected as duplicate packet */
+ u_long seldisptoolarge; /* too much dispersion for selection */
+ u_long selbroken; /* broken NTP detected in selection */
+ u_long seltooold; /* too long since sync in selection */
+ u_char candidate; /* position after candidate selection */
+ u_char select; /* position at end of falseticker sel */
+ u_char was_sane; /* set to 1 if it passed sanity check */
+ u_char correct; /* set to 1 if it passed correctness check */
+ u_char last_event; /* set to code for last peer error */
+ u_char num_events; /* num. of events which have occurred */
+};
+
+/*
+ * Values for peer.leap, sys_leap
+ */
+#define LEAP_NOWARNING 0x0 /* normal, no leap second warning */
+#define LEAP_ADDSECOND 0x1 /* last minute of day has 61 seconds */
+#define LEAP_DELSECOND 0x2 /* last minute of day has 59 seconds */
+#define LEAP_NOTINSYNC 0x3 /* overload, clock is free running */
+
+/*
+ * Values for peer.mode
+ */
+#define MODE_UNSPEC 0 /* unspecified (probably old NTP version) */
+#define MODE_ACTIVE 1 /* symmetric active */
+#define MODE_PASSIVE 2 /* symmetric passive */
+#define MODE_CLIENT 3 /* client mode */
+#define MODE_SERVER 4 /* server mode */
+#define MODE_BROADCAST 5 /* broadcast mode */
+#define MODE_CONTROL 6 /* control mode packet */
+#define MODE_PRIVATE 7 /* implementation defined function */
+
+#define MODE_BCLIENT 8 /* a pseudo mode, used internally */
+#define MODE_MCLIENT 9 /* multicast mode, used internally */
+
+/*
+ * Values for peer.stratum, sys_stratum
+ */
+#define STRATUM_REFCLOCK ((u_char)0) /* stratum claimed by primary clock */
+#define STRATUM_PRIMARY ((u_char)1) /* host has a primary clock */
+#define STRATUM_INFIN ((u_char)NTP_MAXSTRATUM) /* infinity a la Bellman-Ford */
+/* A stratum of 0 in the packet is mapped to 16 internally */
+#define STRATUM_PKT_UNSPEC ((u_char)0) /* unspecified in packet */
+#define STRATUM_UNSPEC ((u_char)(NTP_MAXSTRATUM+(u_char)1)) /* unspecified */
+
+/*
+ * Values for peer.flags
+ */
+#define FLAG_CONFIG 0x1 /* association was configured */
+#define FLAG_AUTHENABLE 0x2 /* this guy needs authentication */
+#define FLAG_MCAST1 0x4 /* multicast client/server mode */
+#define FLAG_MCAST2 0x8 /* multicast client mode */
+#define FLAG_AUTHENTIC 0x10 /* last message was authentic */
+#define FLAG_REFCLOCK 0x20 /* this is actually a reference clock */
+#define FLAG_SYSPEER 0x40 /* this is one of the selected peers */
+#define FLAG_PREFER 0x80 /* this is the preferred peer */
+
+/*
+ * Definitions for the clear() routine. We use memset() to clear
+ * the parts of the peer structure which go to zero. These are
+ * used to calculate the start address and length of the area.
+ */
+#define CLEAR_TO_ZERO(p) ((char *)&((p)->clear_to_zero))
+#define END_CLEAR_TO_ZERO(p) ((char *)&((p)->end_clear_to_zero))
+#define LEN_CLEAR_TO_ZERO (END_CLEAR_TO_ZERO((struct peer *)0) \
+ - CLEAR_TO_ZERO((struct peer *)0))
+/*
+ * Reference clock identifiers (for pps signal)
+ */
+#define PPSREFID (U_LONG)"PPS " /* used when pps controls stratum > 1 */
+
+/*
+ * Reference clock types. Added as necessary.
+ */
+#define REFCLK_NONE 0 /* unknown or missing */
+#define REFCLK_LOCALCLOCK 1 /* external (e.g., lockclock) */
+#define REFCLK_GPS_TRAK 2 /* TRAK 8810 GPS Receiver */
+#define REFCLK_WWV_PST 3 /* PST/Traconex 1020 WWV/H */
+#define REFCLK_WWVB_SPECTRACOM 4 /* Spectracom 8170/Netclock WWVB */
+#define REFCLK_GOES_TRUETIME 5 /* TrueTime 468-DC GOES */
+#define REFCLK_IRIG_AUDIO 6 /* IRIG-B audio decoder */
+#define REFCLK_CHU 7 /* scratchbuilt CHU (Canada) */
+#define REFCLK_PARSE 8 /* generic driver (usually DCF77,GPS) */
+#define REFCLK_GPS_MX4200 9 /* Magnavox MX4200 GPS */
+#define REFCLK_GPS_AS2201 10 /* Austron 2201A GPS */
+#define REFCLK_OMEGA_TRUETIME 11 /* TrueTime OM-DC OMEGA */
+#define REFCLK_IRIG_TPRO 12 /* KSI/Odetics TPRO-S IRIG */
+#define REFCLK_ATOM_LEITCH 13 /* Leitch CSD 5300 Master Clock */
+#define REFCLK_MSF_EES 14 /* EES M201 MSF Receiver */
+#define REFCLK_GPSTM_TRUETIME 15 /* TrueTime GPS/TM-TMD Receiver */
+#define REFCLK_IRIG_BANCOMM 16 /* Bancomm GPS/IRIG Interface */
+#define REFCLK_GPS_DATUM 17 /* Datum Programmable Time System */
+#define REFCLK_NIST_ACTS 18 /* NIST Auto Computer Time Service */
+#define REFCLK_WWV_HEATH 19 /* Heath GC1000 WWV/WWVH Receiver */
+#define REFCLK_GPS_NMEA 20 /* NMEA based GPS clock */
+#define REFCLK_GPS_MOTO 21 /* Motorola GPS clock */
+#define REFCLK_ATOM_PPS 22 /* 1-PPS Clock Discipline */
+#define REFCLK_MAX 24 /* maximum index (room to expand) */
+
+/*
+ * We tell reference clocks from real peers by giving the reference
+ * clocks an address of the form 127.127.t.u, where t is the type and
+ * u is the unit number. We define some of this here since we will need
+ * some sanity checks to make sure this address isn't interpretted as
+ * that of a normal peer.
+ */
+#define REFCLOCK_ADDR 0x7f7f0000 /* 127.127.0.0 */
+#define REFCLOCK_MASK 0xffff0000 /* 255.255.0.0 */
+
+#define ISREFCLOCKADR(srcadr) ((SRCADR(srcadr) & REFCLOCK_MASK) \
+ == REFCLOCK_ADDR)
+
+/*
+ * Macro for checking for invalid addresses. This is really, really
+ * gross, but is needed so no one configures a host on net 127 now that
+ * we're encouraging it the the configuration file.
+ */
+#define LOOPBACKADR 0x7f000001
+#define LOOPNETMASK 0xff000000
+
+#define ISBADADR(srcadr) (((SRCADR(srcadr) & LOOPNETMASK) \
+ == (LOOPBACKADR & LOOPNETMASK)) \
+ && (SRCADR(srcadr) != LOOPBACKADR))
+
+/*
+ * Utilities for manipulating addresses and port numbers
+ */
+#define NSRCADR(src) ((src)->sin_addr.s_addr) /* address in net byte order */
+#define NSRCPORT(src) ((src)->sin_port) /* port in net byte order */
+#define SRCADR(src) (ntohl(NSRCADR((src)))) /* address in host byte order */
+#define SRCPORT(src) (ntohs(NSRCPORT((src)))) /* host port */
+
+/*
+ * NTP packet format. The mac field is optional. It isn't really
+ * an l_fp either, but for now declaring it that way is convenient.
+ * See Appendix A in the specification.
+ *
+ * Note that all u_fp and l_fp values arrive in network byte order
+ * and must be converted (except the mac, which isn't, really).
+ */
+struct pkt {
+ u_char li_vn_mode; /* contains leap indicator, version and mode */
+ u_char stratum; /* peer's stratum */
+ u_char ppoll; /* the peer polling interval */
+ s_char precision; /* peer clock precision */
+ s_fp rootdelay; /* distance to primary clock */
+ u_fp rootdispersion; /* clock dispersion */
+ U_LONG refid; /* reference clock ID */
+ l_fp reftime; /* time peer clock was last updated */
+ l_fp org; /* originate time stamp */
+ l_fp rec; /* receive time stamp */
+ l_fp xmt; /* transmit time stamp */
+
+#define MIN_MAC_LEN (sizeof(U_LONG) + 8) /* DES */
+#define MAX_MAC_LEN (sizeof(U_LONG) + 16) /* MD5 */
+
+ U_LONG keyid; /* key identification */
+ u_char mac[MAX_MAC_LEN-sizeof(U_LONG)];/* message-authentication code */
+ /*l_fp mac;*/
+};
+
+/*
+ * Packets can come in two flavours, one with a mac and one without.
+ */
+#define LEN_PKT_NOMAC (sizeof(struct pkt) - MAX_MAC_LEN)
+
+/*
+ * Minimum size of packet with a MAC: has to include at least a key number.
+ */
+#define LEN_PKT_MAC (LEN_PKT_NOMAC + sizeof(U_LONG))
+
+/*
+ * Stuff for extracting things from li_vn_mode
+ */
+#define PKT_MODE(li_vn_mode) ((u_char)((li_vn_mode) & 0x7))
+#define PKT_VERSION(li_vn_mode) ((u_char)(((li_vn_mode) >> 3) & 0x7))
+#define PKT_LEAP(li_vn_mode) ((u_char)(((li_vn_mode) >> 6) & 0x3))
+
+/*
+ * Stuff for putting things back into li_vn_mode
+ */
+#define PKT_LI_VN_MODE(li, vn, md) \
+ ((u_char)((((li) << 6) & 0xc0) | (((vn) << 3) & 0x38) | ((md) & 0x7)))
+
+
+/*
+ * Dealing with stratum. 0 gets mapped to 16 incoming, and back to 0
+ * on output.
+ */
+#define PKT_TO_STRATUM(s) ((u_char)(((s) == (STRATUM_PKT_UNSPEC)) ?\
+ (STRATUM_UNSPEC) : (s)))
+
+#define STRATUM_TO_PKT(s) ((u_char)(((s) == (STRATUM_UNSPEC)) ?\
+ (STRATUM_PKT_UNSPEC) : (s)))
+
+/*
+ * Format of a recvbuf. These are used by the asynchronous receive
+ * routine to store incoming packets and related information.
+ */
+
+/*
+ * the maximum length NTP packet is a full length NTP control message with
+ * the maximum length message authenticator. I hate to hard-code 468 and 12,
+ * but only a few modules include ntp_control.h...
+ */
+#define RX_BUFF_SIZE (468+12+MAX_MAC_LEN)
+
+struct recvbuf {
+ struct recvbuf *next; /* next buffer in chain */
+ union {
+ struct sockaddr_in X_recv_srcadr;
+ caddr_t X_recv_srcclock;
+ } X_from_where;
+#define recv_srcadr X_from_where.X_recv_srcadr
+#define recv_srcclock X_from_where.X_recv_srcclock
+ struct sockaddr_in srcadr; /* where packet came from */
+ struct interface *dstadr; /* interface datagram arrived thru */
+ int fd; /* fd on which it was received */
+ l_fp recv_time; /* time of arrival */
+ void (*receiver)(); /* routine to receive buffer */
+ int recv_length; /* number of octets received */
+ union {
+ struct pkt X_recv_pkt;
+ char X_recv_buffer[RX_BUFF_SIZE];
+ } recv_space;
+#define recv_pkt recv_space.X_recv_pkt
+#define recv_buffer recv_space.X_recv_buffer
+};
+
+
+/*
+ * Event codes. Used for reporting errors/events to the control module
+ */
+#define PEER_EVENT 0x80 /* this is a peer event */
+
+#define EVNT_UNSPEC 0
+#define EVNT_SYSRESTART 1
+#define EVNT_SYSFAULT 2
+#define EVNT_SYNCCHG 3
+#define EVNT_PEERSTCHG 4
+#define EVNT_CLOCKRESET 5
+#define EVNT_BADDATETIM 6
+#define EVNT_CLOCKEXCPT 7
+
+#define EVNT_PEERIPERR (1|PEER_EVENT)
+#define EVNT_PEERAUTH (2|PEER_EVENT)
+#define EVNT_UNREACH (3|PEER_EVENT)
+#define EVNT_REACH (4|PEER_EVENT)
+#define EVNT_PEERCLOCK (5|PEER_EVENT)
+
+/*
+ * Clock event codes
+ */
+#define CEVNT_NOMINAL 0
+#define CEVNT_TIMEOUT 1
+#define CEVNT_BADREPLY 2
+#define CEVNT_FAULT 3
+#define CEVNT_PROP 4
+#define CEVNT_BADDATE 5
+#define CEVNT_BADTIME 6
+#define CEVNT_MAX CEVNT_BADTIME
+
+/*
+ * Very misplaced value. Default port through which we send traps.
+ */
+#define TRAPPORT 18447
+
+
+/*
+ * To speed lookups, peers are hashed by the low order bits of the remote
+ * IP address. These definitions relate to that.
+ */
+#define HASH_SIZE 32
+#define HASH_MASK (HASH_SIZE-1)
+#define HASH_ADDR(src) ((SRCADR((src))^(SRCADR((src))>>8)) & HASH_MASK)
+
+
+/*
+ * The poll update procedure takes an extra argument which controls
+ * how a random perturbation is applied to peer.timer. The choice is
+ * to not randomize at all, to randomize only if we're going to update
+ * peer.timer, and to randomize no matter what (almost, the algorithm
+ * is that we apply the random value if it is less than the current
+ * timer count).
+ */
+#define POLL_NOTRANDOM 0 /* don't randomize */
+#define POLL_RANDOMCHANGE 1 /* if you change, change randomly */
+#define POLL_MAKERANDOM 2 /* randomize next interval */
+
+
+/*
+ * How we randomize polls. The poll interval is a power of two.
+ * We chose a random value which is between 1/4 and 3/4 of the
+ * poll interval we would normally use and which is an even multiple
+ * of the EVENT_TIMEOUT. The random number routine, given an argument
+ * spread value of n, returns an integer between 0 and (1<<n)-1. This
+ * is shifted by EVENT_TIMEOUT and added to the base value.
+ */
+#define RANDOM_SPREAD(poll) ((poll) - (EVENT_TIMEOUT+1))
+#define RANDOM_POLL(poll, rval) ((((rval)+1)<<EVENT_TIMEOUT) + (1<<((poll)-2)))
+
+/*
+ * min, min3 and max. Makes it easier to transliterate the spec without
+ * thinking about it.
+ */
+#define min(a,b) (((a) < (b)) ? (a) : (b))
+#define max(a,b) (((a) > (b)) ? (a) : (b))
+#define min3(a,b,c) min(min((a),(b)), (c))
+
+
+/*
+ * Configuration items. These are for the protocol module (proto_config())
+ */
+#define PROTO_BROADCLIENT 1
+#define PROTO_PRECISION 2
+#define PROTO_AUTHENTICATE 3
+#define PROTO_BROADDELAY 4
+#define PROTO_AUTHDELAY 5
+#define PROTO_MULTICAST_ADD 6
+#define PROTO_MULTICAST_DEL 7
+#define PROTO_PLL 8
+#define PROTO_PPS 9
+#define PROTO_MONITOR 10
+#define PROTO_FILEGEN 11
+
+/*
+ * Configuration items for the loop filter
+ */
+#define LOOP_DRIFTCOMP 1 /* set frequency offset */
+#define LOOP_PPSDELAY 2 /* set pps delay */
+#define LOOP_PPSBAUD 3 /* set pps baud rate */
+
+/*
+ * Configuration items for the stats printer
+ */
+#define STATS_FREQ_FILE 1 /* configure drift file */
+#define STATS_STATSDIR 2 /* directory prefix for stats files */
+#define STATS_PID_FILE 3 /* configure xntpd PID file */
+
+#define MJD_1970 40587 /* MJD for 1 Jan 1970 */
+
+/*
+ * Default parameters. We use these in the absense of something better.
+ */
+#define DEFPRECISION (-7) /* default precision (~10 ms) */
+#define DEFBROADDELAY 0x00000100 /* default broadcast offset */
+ /* (~4 ms as s_fp) */
+#define DEFAUTHDELAY 0x00080000 /* default authentcation delay */
+ /* (~100 us as l_fp.u_f) */
+#define INADDR_NTP 0xe0000101 /* NTP multicast address 224.0.1.1 */
+/*
+ * Structure used optionally for monitoring when this is turned on.
+ */
+struct mon_data {
+ struct mon_data *hash_next; /* next structure in hash list */
+ struct mon_data *mru_next; /* next structure in MRU list */
+ struct mon_data *mru_prev; /* previous structure in MRU list */
+ struct mon_data *fifo_next; /* next structure in FIFO list */
+ struct mon_data *fifo_prev; /* previous structure in FIFO list */
+ u_long lastdrop; /* last time dropped due to RES_LIMIT*/
+ u_long lasttime; /* last time data updated */
+ u_long firsttime; /* time structure initialized */
+ u_long count; /* count we have seen */
+ U_LONG rmtadr; /* address of remote host */
+ struct interface *interface; /* interface on which this arrived */
+ u_short rmtport; /* remote port last came from */
+ u_char mode; /* mode of incoming packet */
+ u_char version; /* version of incoming packet */
+ u_char cast_flags; /* flags MDF_?CAST */
+};
+
+#define MDF_UCAST 1 /* unicast packet */
+#define MDF_MCAST 2 /* multicast packet */
+#define MDF_BCAST 4 /* broadcast packet */
+#define MDF_LCAST 8 /* local packet */
+
+/*
+ * Values used with mon_enabled to indicate reason for enabling monitoring
+ */
+#define MON_OFF 0x00 /* no monitoring */
+#define MON_ON 0x01 /* monitoring explicitly enabled */
+#define MON_RES 0x02 /* implicit monitoring for RES_LIMITED */
+/*
+ * Structure used for restrictlist entries
+ */
+struct restrictlist {
+ struct restrictlist *next; /* link to next entry */
+ U_LONG addr; /* host address (host byte order) */
+ U_LONG mask; /* mask for address (host byte order) */
+ u_long count; /* number of packets matched */
+ u_short flags; /* accesslist flags */
+ u_short mflags; /* match flags */
+};
+
+/*
+ * Access flags
+ */
+#define RES_IGNORE 0x1 /* ignore if matched */
+#define RES_DONTSERVE 0x2 /* don't give him any time */
+#define RES_DONTTRUST 0x4 /* don't trust if matched */
+#define RES_NOQUERY 0x8 /* don't allow queries if matched */
+#define RES_NOMODIFY 0x10 /* don't allow him to modify server */
+#define RES_NOPEER 0x20 /* don't allocate memory resources */
+#define RES_NOTRAP 0x40 /* don't allow him to set traps */
+#define RES_LPTRAP 0x80 /* traps set by him are low priority */
+#define RES_LIMITED 0x100 /* limit per net number of clients */
+
+#define RES_ALLFLAGS \
+ (RES_IGNORE|RES_DONTSERVE|RES_DONTTRUST|RES_NOQUERY\
+ |RES_NOMODIFY|RES_NOPEER|RES_NOTRAP|RES_LPTRAP|RES_LIMITED)
+
+/*
+ * Match flags
+ */
+#define RESM_INTERFACE 0x1 /* this is an interface */
+#define RESM_NTPONLY 0x2 /* match ntp port only */
+
+/*
+ * Restriction configuration ops
+ */
+#define RESTRICT_FLAGS 1 /* add flags to restrict entry */
+#define RESTRICT_UNFLAG 2 /* remove flags from restrict entry */
+#define RESTRICT_REMOVE 3 /* remove a restrict entry */
+
+
+/*
+ * Experimental alternate selection algorithm identifiers
+ */
+#define SELECT_1 1
+#define SELECT_2 2
+#define SELECT_3 3
+#define SELECT_4 4
+#define SELECT_5 5
+
+/*
+ * Endpoint structure for the select algorithm
+ */
+struct endpoint {
+ s_fp val; /* offset of endpoint */
+ int type; /* interval entry/exit */
+};
diff --git a/usr.sbin/xntpd/include/ntp_calendar.h b/usr.sbin/xntpd/include/ntp_calendar.h
new file mode 100644
index 0000000..461aee4
--- /dev/null
+++ b/usr.sbin/xntpd/include/ntp_calendar.h
@@ -0,0 +1,80 @@
+/*
+ * ntp_calendar.h - definitions for the calendar time-of-day routine
+ */
+
+#include "ntp_types.h"
+
+struct calendar {
+ u_short year; /* year (A.D.) */
+ u_short yearday; /* day of year, 1 = January 1 */
+ u_char month; /* month, 1 = January */
+ u_char monthday; /* day of month */
+ u_char hour; /* hour of day, midnight = 0 */
+ u_char minute; /* minute of hour */
+ u_char second; /* second of minute */
+};
+
+/*
+ * Days in each month. 30 days hath September...
+ */
+#define JAN 31
+#define FEB 28
+#define FEBLEAP 29
+#define MAR 31
+#define APR 30
+#define MAY 31
+#define JUN 30
+#define JUL 31
+#define AUG 31
+#define SEP 30
+#define OCT 31
+#define NOV 30
+#define DEC 31
+
+/*
+ * We deal in a 4 year cycle starting at March 1, 1900. We assume
+ * we will only want to deal with dates since then, and not to exceed
+ * the rollover day in 2036.
+ */
+#define SECSPERMIN (60) /* seconds per minute */
+#define MINSPERHR (60) /* minutes per hour */
+#define HRSPERDAY (24) /* hours per day */
+#define DAYSPERYEAR (365) /* days per year */
+
+#define SECSPERDAY (SECSPERMIN*MINSPERHR*HRSPERDAY)
+#define SECSPERYEAR (365 * SECSPERDAY) /* regular year */
+#define SECSPERLEAPYEAR (366 * SECSPERDAY) /* leap year */
+
+#define MAR1900 ((JAN+FEB) * SECSPERDAY) /* no leap year in 1900 */
+#define DAYSPERCYCLE (365+365+365+366) /* 3 normal years plus leap */
+#define SECSPERCYCLE (DAYSPERCYCLE*SECSPERDAY)
+#define YEARSPERCYCLE 4
+
+/*
+ * Gross hacks. I have illicit knowlege that there won't be overflows
+ * here, the compiler often can't tell this.
+ */
+#define TIMES60(val) ((((val)<<4) - (val))<<2) /* *(16 - 1) * 4 */
+#define TIMES24(val) (((val)<<4) + ((val)<<3)) /* *16 + *8 */
+#define TIMES7(val) (((val)<<3) - (val)) /* *8 - *1 */
+#define TIMESDPERC(val) (((val)<<10) + ((val)<<8) \
+ + ((val)<<7) + ((val)<<5) \
+ + ((val)<<4) + ((val)<<2) + (val)) /* *big* hack */
+
+/*
+ * Another big hack. Cycle 22 started on March 1, 1988. This is
+ * STARTCYCLE22 seconds after the start of cycle 0.
+ */
+#define CYCLE22 (22)
+#define STARTCYCLE22 (u_long)(0xa586b500) /* 2777068800 */
+#define MAR1988 (u_long)(STARTCYCLE22 + (u_long)MAR1900)
+
+/*
+ * The length of January + February in leap and non-leap years.
+ */
+#define JANFEBNOLEAP ((JAN+FEB) * SECSPERDAY)
+#define JANFEBLEAP ((JAN+FEBLEAP) * SECSPERDAY)
+
+extern void caljulian P((u_long, struct calendar *));
+extern u_long caltontp P((const struct calendar *));
+
diff --git a/usr.sbin/xntpd/include/ntp_control.h b/usr.sbin/xntpd/include/ntp_control.h
new file mode 100644
index 0000000..1124bb0
--- /dev/null
+++ b/usr.sbin/xntpd/include/ntp_control.h
@@ -0,0 +1,253 @@
+/*
+ * ntp_control.h - definitions related to NTP mode 6 control messages
+ */
+
+#include "ntp_types.h"
+
+struct ntp_control {
+ u_char li_vn_mode; /* leap, version, mode */
+ u_char r_m_e_op; /* response, more, error, opcode */
+ u_short sequence; /* sequence number of request */
+ u_short status; /* status word for association */
+ u_short associd; /* association ID */
+ u_short offset; /* offset of this batch of data */
+ u_short count; /* count of data in this packet */
+ u_char data[(480 + MAX_MAC_LEN)]; /* data + auth */
+};
+
+/*
+ * Length of the control header, in octets
+ */
+#define CTL_HEADER_LEN 12
+#define CTL_MAX_DATA_LEN 468
+
+
+/*
+ * Limits and things
+ */
+#define CTL_MAXTRAPS 3 /* maximum number of traps we allow */
+#define CTL_TRAPTIME (60*60) /* time out traps in 1 hour */
+#define CTL_MAXAUTHSIZE 64 /* maximum size of an authen'ed req */
+
+/*
+ * Decoding for the r_m_e_op field
+ */
+#define CTL_RESPONSE 0x80
+#define CTL_ERROR 0x40
+#define CTL_MORE 0x20
+#define CTL_OP_MASK 0x1f
+
+#define CTL_ISRESPONSE(r_m_e_op) (((r_m_e_op) & 0x80) != 0)
+#define CTL_ISMORE(r_m_e_op) (((r_m_e_op) & 0x20) != 0)
+#define CTL_ISERROR(r_m_e_op) (((r_m_e_op) & 0x40) != 0)
+#define CTL_OP(r_m_e_op) ((r_m_e_op) & CTL_OP_MASK)
+
+/*
+ * Opcodes
+ */
+#define CTL_OP_UNSPEC 0
+#define CTL_OP_READSTAT 1
+#define CTL_OP_READVAR 2
+#define CTL_OP_WRITEVAR 3
+#define CTL_OP_READCLOCK 4
+#define CTL_OP_WRITECLOCK 5
+#define CTL_OP_SETTRAP 6
+#define CTL_OP_ASYNCMSG 7
+#define CTL_OP_UNSETTRAP 31
+
+/*
+ * {En,De}coding of the system status word
+ */
+#define CTL_SST_TS_UNSPEC 0 /* time source unspecified */
+#define CTL_SST_TS_ATOM 1 /* time source calibrated atomic */
+#define CTL_SST_TS_LF 2 /* time source VLF or LF radio */
+#define CTL_SST_TS_HF 3 /* time source HF radio */
+#define CTL_SST_TS_UHF 4 /* time source UHF radio */
+#define CTL_SST_TS_LOCAL 5 /* time source LOCAL */
+#define CTL_SST_TS_NTP 6 /* time source NTP */
+#define CTL_SST_TS_UDPTIME 7 /* time source UDP/TIME */
+#define CTL_SST_TS_WRSTWTCH 8 /* time source is wristwatch */
+#define CTL_SST_TS_TELEPHONE 9 /* time source is telephone modem */
+#define CTL_SST_TS_PPS 0x20 /* time source is PPS signal */
+
+#define CTL_SYS_MAXEVENTS 15
+
+#define CTL_SYS_STATUS(li, source, nevnt, evnt) \
+ (((((unsigned short)(li))<< 14)&0xc000) | \
+ (((source)<<8)&0x3f00) | \
+ (((nevnt)<<4)&0x00f0) | \
+ ((evnt)&0x000f))
+
+#define CTL_SYS_LI(status) (((status)>>14) & 0x3)
+#define CTL_SYS_SOURCE(status) (((status)>>8) & 0x3f)
+#define CTL_SYS_NEVNT(status) (((status)>>4) & 0xf)
+#define CTL_SYS_EVENT(status) ((status) & 0xf)
+
+/*
+ * {En,De}coding of the peer status word
+ */
+#define CTL_PST_CONFIG 0x80
+#define CTL_PST_AUTHENABLE 0x40
+#define CTL_PST_AUTHENTIC 0x20
+#define CTL_PST_REACH 0x10
+#define CTL_PST_UNSPEC 0x08
+
+#define CTL_PST_SEL_REJECT 0 /* rejected */
+#define CTL_PST_SEL_SANE 1 /* passed sanity checks */
+#define CTL_PST_SEL_CORRECT 2 /* passed correctness checks */
+#define CTL_PST_SEL_SELCAND 3 /* passed candidate checks */
+#define CTL_PST_SEL_SYNCCAND 4 /* passed outlyer checks */
+#define CTL_PST_SEL_DISTSYSPEER 5 /* selected, distance exceeded */
+#define CTL_PST_SEL_SYSPEER 6 /* selected */
+#define CTL_PST_SEL_PPS 7 /* selected, pps signal override */
+
+#define CTL_PEER_MAXEVENTS 15
+
+#define CTL_PEER_STATUS(status, nevnt, evnt) \
+ ((((status)<<8) & 0xff00) | \
+ (((nevnt)<<4) & 0x00f0) | \
+ ((evnt) & 0x000f))
+
+#define CTL_PEER_STATVAL(status)(((status)>>8) & 0xff)
+#define CTL_PEER_NEVNT(status) (((status)>>4) & 0xf)
+#define CTL_PEER_EVENT(status) ((status) & 0xf)
+
+/*
+ * {En,De}coding of the clock status word
+ */
+#define CTL_CLK_OKAY 0
+#define CTL_CLK_NOREPLY 1
+#define CTL_CLK_BADFORMAT 2
+#define CTL_CLK_FAULT 3
+#define CTL_CLK_PROPAGATION 4
+#define CTL_CLK_BADDATE 5
+#define CTL_CLK_BADTIME 6
+
+#define CTL_CLK_STATUS(status, event) \
+ ((((status)<<8) & 0xff00) | \
+ ((event) & 0x00ff))
+
+/*
+ * Error code responses returned when the E bit is set.
+ */
+#define CERR_UNSPEC 0
+#define CERR_PERMISSION 1
+#define CERR_BADFMT 2
+#define CERR_BADOP 3
+#define CERR_BADASSOC 4
+#define CERR_UNKNOWNVAR 5
+#define CERR_BADVALUE 6
+#define CERR_RESTRICT 7
+
+#define CERR_NORESOURCE CERR_PERMISSION /* wish there was a different code */
+
+
+/*
+ * System variables we understand
+ */
+#define CS_LEAP 1
+#define CS_STRATUM 2
+#define CS_PRECISION 3
+#define CS_ROOTDELAY 4
+#define CS_ROOTDISPERSION 5
+#define CS_REFID 6
+#define CS_REFTIME 7
+#define CS_POLL 8
+#define CS_PEERID 9
+#define CS_OFFSET 10
+#define CS_DRIFT 11
+#define CS_COMPLIANCE 12
+#define CS_CLOCK 13
+#define CS_LEAPIND 14
+#define CS_LEAPWARNING 15
+#define CS_PROCESSOR 16
+#define CS_SYSTEM 17
+#define CS_KEYID 18
+#define CS_REFSKEW 19
+#define CS_VARLIST 20
+
+#define CS_MAXCODE CS_VARLIST
+
+/*
+ * Peer variables we understand
+ */
+#define CP_CONFIG 1
+#define CP_AUTHENABLE 2
+#define CP_AUTHENTIC 3
+#define CP_SRCADR 4
+#define CP_SRCPORT 5
+#define CP_DSTADR 6
+#define CP_DSTPORT 7
+#define CP_LEAP 8
+#define CP_HMODE 9
+#define CP_STRATUM 10
+#define CP_PPOLL 11
+#define CP_HPOLL 12
+#define CP_PRECISION 13
+#define CP_ROOTDELAY 14
+#define CP_ROOTDISPERSION 15
+#define CP_REFID 16
+#define CP_REFTIME 17
+#define CP_ORG 18
+#define CP_REC 19
+#define CP_XMT 20
+#define CP_REACH 21
+#define CP_VALID 22
+#define CP_TIMER 23
+#define CP_DELAY 24
+#define CP_OFFSET 25
+#define CP_DISPERSION 26
+#define CP_KEYID 27
+#define CP_FILTDELAY 28
+#define CP_FILTOFFSET 29
+#define CP_PMODE 30
+#define CP_RECEIVED 31
+#define CP_SENT 32
+#define CP_FILTERROR 33
+#define CP_FLASH 34
+#define CP_DISP 35
+#define CP_VARLIST 36
+
+#define CP_MAXCODE CP_VARLIST
+
+/*
+ * Clock variables we understand
+ */
+#define CC_TYPE 1
+#define CC_TIMECODE 2
+#define CC_POLL 3
+#define CC_NOREPLY 4
+#define CC_BADFORMAT 5
+#define CC_BADDATA 6
+#define CC_FUDGETIME1 7
+#define CC_FUDGETIME2 8
+#define CC_FUDGEVAL1 9
+#define CC_FUDGEVAL2 10
+#define CC_FLAGS 11
+#define CC_DEVICE 12
+#define CC_VARLIST 13
+
+#define CC_MAXCODE CC_VARLIST
+
+/*
+ * Definition of the structure used internally to hold trap information.
+ * ntp_request.c wants to see this.
+ */
+struct ctl_trap {
+ struct sockaddr_in tr_addr; /* address of trap recipient */
+ struct interface *tr_localaddr; /* interface to send this through */
+ u_long tr_settime; /* time trap was set */
+ u_long tr_count; /* async messages sent to this guy */
+ u_long tr_origtime; /* time trap was originally set */
+ u_long tr_resets; /* count of resets for this trap */
+ u_short tr_sequence; /* trap sequence id */
+ u_char tr_flags; /* trap flags */
+ u_char tr_version; /* version number of trapper */
+};
+
+/*
+ * Flag bits
+ */
+#define TRAP_INUSE 0x1 /* this trap is active */
+#define TRAP_NONPRIO 0x2 /* this trap is non-priority */
+#define TRAP_CONFIGURED 0x4 /* this trap was configured */
diff --git a/usr.sbin/xntpd/include/ntp_datum.h b/usr.sbin/xntpd/include/ntp_datum.h
new file mode 100644
index 0000000..2aa2cb7
--- /dev/null
+++ b/usr.sbin/xntpd/include/ntp_datum.h
@@ -0,0 +1,30 @@
+struct btfp_time /* Structure for reading 5 time words */
+ /* in one ioctl(2) operation. */
+{
+ unsigned short btfp_time[5]; /* Time words 0,1,2,3, and 4. (16bit)*/
+};
+
+/***** Simple ioctl commands *****/
+
+#define RUNLOCK _IO('X',19) /* Release Capture Lockout */
+#define RCR0 _IOR('X',22,unsigned int) /* Read control register */
+#define WCR0 _IOW('X',23,unsigned int) /* Write control register */
+
+/***** Compound ioctl commands *****/
+
+/* Read all 5 time words in one call. */
+#define READTIME _IOR('X',32,struct btfp_time)
+#define VMEFD "/dev/btfp0"
+
+ struct vmedate { /* structure returned by get_vmetime.c */
+ unsigned short year;
+ unsigned short doy;
+ unsigned short hr;
+ unsigned short mn;
+ unsigned short sec;
+ unsigned long frac;
+ unsigned short status;
+ };
+
+#define PRIO 120 /* set the realtime priority */
+#define NREGS 7 /* number of registers we will use */
diff --git a/usr.sbin/xntpd/include/ntp_filegen.h b/usr.sbin/xntpd/include/ntp_filegen.h
new file mode 100644
index 0000000..9e75bc6
--- /dev/null
+++ b/usr.sbin/xntpd/include/ntp_filegen.h
@@ -0,0 +1,51 @@
+/*
+ * ntp_filegen.h,v 3.8 1994/05/30 09:48:53 kardel Exp
+ *
+ * definitions for NTP file generations support
+ *
+ *
+ * Copyright (c) 1992
+ * Rainer Pruy Friedrich-Alexander Unuiversitaet Erlangen-Nuernberg
+ *
+ * This code may be modified and used freely
+ * provided the credits remain intact.
+ */
+
+#include "ntp_types.h"
+
+/*
+ * supported file generation types
+ */
+
+#define FILEGEN_NONE 255 /* no generations - use plain file name */
+#define FILEGEN_PID 1 /* one filegen per process incarnation */
+#define FILEGEN_DAY 2 /* one filegen per day */
+#define FILEGEN_WEEK 3 /* one filegen per week */
+#define FILEGEN_MONTH 4 /* one filegen per month */
+#define FILEGEN_YEAR 5 /* one filegen per year */
+#define FILEGEN_AGE 6 /* change filegen each FG_AGE_SECS */
+
+/*
+ * supported file generation flags
+ */
+
+#define FGEN_FLAG_LINK 0x01 /* make a link to base name */
+
+#define FGEN_FLAG_ENABLED 0x80 /* set this to really create files */
+ /* without this, open is suppressed */
+
+typedef struct FILEGEN
+ {
+ FILE *fp; /* file referring to current generation */
+ char *prefix; /* filename prefix and basename to be used*/
+ char *basename; /* for constructing filename of generation file */
+ /* WARNING: must be malloced !!! will be fed to free()*/
+ u_long id; /* id of current generation */
+ u_char type; /* type of file generation */
+ u_char flag; /* flags modifying processing of file generation */
+ } FILEGEN;
+
+extern void filegen_setup P((FILEGEN *, u_long));
+extern void filegen_config P((FILEGEN *, char *, u_int, u_int));
+extern FILEGEN *filegen_get P((char *));
+extern void filegen_register P((char *, FILEGEN *));
diff --git a/usr.sbin/xntpd/include/ntp_fp.h b/usr.sbin/xntpd/include/ntp_fp.h
new file mode 100644
index 0000000..ec657f7
--- /dev/null
+++ b/usr.sbin/xntpd/include/ntp_fp.h
@@ -0,0 +1,316 @@
+/*
+ * ntp_fp.h - definitions for NTP fixed point arithmetic
+ */
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+#include "ntp_types.h"
+
+/*
+ * NTP uses two fixed point formats. The first (l_fp) is the "long" format
+ * and is 64 bits long with the decimal between bits 31 and 32. This
+ * is used for time stamps in the NTP packet header (in network byte
+ * order) and for internal computations of offsets (in local host byte
+ * order). We use the same structure for both signed and unsigned values,
+ * which is a big hack but saves rewriting all the operators twice. Just
+ * to confuse this, we also sometimes just carry the fractional part in
+ * calculations, in both signed and unsigned forms. Anyway, an l_fp looks
+ * like:
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Integral Part |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Fractional Part |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ */
+typedef struct {
+ union {
+ U_LONG Xl_ui;
+ LONG Xl_i;
+ } Ul_i;
+ union {
+ U_LONG Xl_uf;
+ LONG Xl_f;
+ } Ul_f;
+} l_fp;
+
+#define l_ui Ul_i.Xl_ui /* unsigned integral part */
+#define l_i Ul_i.Xl_i /* signed integral part */
+#define l_uf Ul_f.Xl_uf /* unsigned fractional part */
+#define l_f Ul_f.Xl_f /* signed fractional part */
+
+/*
+ * Fractional precision (of an l_fp) is actually the number of
+ * bits in a long.
+ */
+#define FRACTION_PREC (32)
+
+
+/*
+ * The second fixed point format is 32 bits, with the decimal between
+ * bits 15 and 16. There is a signed version (s_fp) and an unsigned
+ * version (u_fp). This is used to represent synchronizing distance
+ * and synchronizing dispersion in the NTP packet header (again, in
+ * network byte order) and internally to hold both distance and
+ * dispersion values (in local byte order). In network byte order
+ * it looks like:
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Integer Part | Fraction Part |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ */
+typedef LONG s_fp;
+typedef U_LONG u_fp;
+
+/*
+ * A unit second in fp format. Actually 2**(half_the_bits_in_a_long)
+ */
+#define FP_SECOND (0x10000)
+
+/*
+ * Byte order conversions
+ */
+#define HTONS_FP(x) (htonl(x))
+#define HTONL_FP(h, n) do { (n)->l_ui = htonl((h)->l_ui); \
+ (n)->l_uf = htonl((h)->l_uf); } while (0)
+#define NTOHS_FP(x) (ntohl(x))
+#define NTOHL_FP(n, h) do { (h)->l_ui = ntohl((n)->l_ui); \
+ (h)->l_uf = ntohl((n)->l_uf); } while (0)
+#define NTOHL_MFP(ni, nf, hi, hf) \
+ do { (hi) = ntohl(ni); (hf) = ntohl(nf); } while (0)
+#define HTONL_MFP(hi, hf, ni, nf) \
+ do { (ni) = ntohl(hi); (nf) = ntohl(hf); } while (0)
+
+/* funny ones. Converts ts fractions to net order ts */
+#define HTONL_UF(uf, nts) \
+ do { (nts)->l_ui = 0; (nts)->l_uf = htonl(uf); } while (0)
+#define HTONL_F(f, nts) do { (nts)->l_uf = htonl(f); \
+ if ((f) & 0x80000000) \
+ (nts)->l_i = -1; \
+ else \
+ (nts)->l_i = 0; \
+ } while (0)
+
+/*
+ * Conversions between the two fixed point types
+ */
+#define MFPTOFP(x_i, x_f) (((x_i)<<16) | (((x_f)>>16)&0xffff))
+#define LFPTOFP(v) MFPTOFP((v)->l_i, (v)->l_f)
+
+#define UFPTOLFP(x, v) ((v)->l_ui = (u_fp)(x)>>16, (v)->l_uf = (x)<<16)
+#define FPTOLFP(x, v) (UFPTOLFP((x), (v)), (x) < 0 ? (v)->l_ui -= 0x10000 : 0)
+
+/*
+ * Primitive operations on long fixed point values. If these are
+ * reminiscent of assembler op codes it's only because some may
+ * be replaced by inline assembler for particular machines someday.
+ * These are the (kind of inefficient) run-anywhere versions.
+ */
+#define M_NEG(v_i, v_f) /* v = -v */ \
+ do { \
+ if ((v_f) == 0) \
+ (v_i) = -(v_i); \
+ else { \
+ (v_f) = -(v_f); \
+ (v_i) = ~(v_i); \
+ } \
+ } while(0)
+
+#define M_NEGM(r_i, r_f, a_i, a_f) /* r = -a */ \
+ do { \
+ if ((a_f) == 0) { \
+ (r_f) = 0; \
+ (r_i) = -(a_i); \
+ } else { \
+ (r_f) = -(a_f); \
+ (r_i) = ~(a_i); \
+ } \
+ } while(0)
+
+#define M_ADD(r_i, r_f, a_i, a_f) /* r += a */ \
+ do { \
+ register U_LONG lo_tmp; \
+ register U_LONG hi_tmp; \
+ \
+ lo_tmp = ((r_f) & 0xffff) + ((a_f) & 0xffff); \
+ hi_tmp = (((r_f) >> 16) & 0xffff) + (((a_f) >> 16) & 0xffff); \
+ if (lo_tmp & 0x10000) \
+ hi_tmp++; \
+ (r_f) = ((hi_tmp & 0xffff) << 16) | (lo_tmp & 0xffff); \
+ \
+ (r_i) += (a_i); \
+ if (hi_tmp & 0x10000) \
+ (r_i)++; \
+ } while (0)
+
+#define M_ADD3(r_ovr, r_i, r_f, a_ovr, a_i, a_f) /* r += a, three word */ \
+ do { \
+ register U_LONG lo_tmp; \
+ register U_LONG hi_tmp; \
+ \
+ lo_tmp = ((r_f) & 0xffff) + ((a_f) & 0xffff); \
+ hi_tmp = (((r_f) >> 16) & 0xffff) + (((a_f) >> 16) & 0xffff); \
+ if (lo_tmp & 0x10000) \
+ hi_tmp++; \
+ (r_f) = ((hi_tmp & 0xffff) << 16) | (lo_tmp & 0xffff); \
+ \
+ lo_tmp = ((r_i) & 0xffff) + ((a_i) & 0xffff); \
+ if (hi_tmp & 0x10000) \
+ lo_tmp++; \
+ hi_tmp = (((r_i) >> 16) & 0xffff) + (((a_i) >> 16) & 0xffff); \
+ if (lo_tmp & 0x10000) \
+ hi_tmp++; \
+ (r_i) = ((hi_tmp & 0xffff) << 16) | (lo_tmp & 0xffff); \
+ \
+ (r_ovr) += (a_ovr); \
+ if (hi_tmp & 0x10000) \
+ (r_ovr)++; \
+ } while (0)
+
+#define M_SUB(r_i, r_f, a_i, a_f) /* r -= a */ \
+ do { \
+ register U_LONG lo_tmp; \
+ register U_LONG hi_tmp; \
+ \
+ if ((a_f) == 0) { \
+ (r_i) -= (a_i); \
+ } else { \
+ lo_tmp = ((r_f) & 0xffff) + ((-(a_f)) & 0xffff); \
+ hi_tmp = (((r_f) >> 16) & 0xffff) \
+ + (((-(a_f)) >> 16) & 0xffff); \
+ if (lo_tmp & 0x10000) \
+ hi_tmp++; \
+ (r_f) = ((hi_tmp & 0xffff) << 16) | (lo_tmp & 0xffff); \
+ \
+ (r_i) += ~(a_i); \
+ if (hi_tmp & 0x10000) \
+ (r_i)++; \
+ } \
+ } while (0)
+
+#define M_RSHIFTU(v_i, v_f) /* v >>= 1, v is unsigned */ \
+ do { \
+ (v_f) = (U_LONG)(v_f) >> 1; \
+ if ((v_i) & 01) \
+ (v_f) |= 0x80000000; \
+ (v_i) = (U_LONG)(v_i) >> 1; \
+ } while (0)
+
+#define M_RSHIFT(v_i, v_f) /* v >>= 1, v is signed */ \
+ do { \
+ (v_f) = (U_LONG)(v_f) >> 1; \
+ if ((v_i) & 01) \
+ (v_f) |= 0x80000000; \
+ if ((v_i) & 0x80000000) \
+ (v_i) = ((v_i) >> 1) | 0x80000000; \
+ else \
+ (v_i) = (v_i) >> 1; \
+ } while (0)
+
+#define M_LSHIFT(v_i, v_f) /* v <<= 1 */ \
+ do { \
+ (v_i) <<= 1; \
+ if ((v_f) & 0x80000000) \
+ (v_i) |= 0x1; \
+ (v_f) <<= 1; \
+ } while (0)
+
+#define M_LSHIFT3(v_ovr, v_i, v_f) /* v <<= 1, with overflow */ \
+ do { \
+ (v_ovr) <<= 1; \
+ if ((v_i) & 0x80000000) \
+ (v_ovr) |= 0x1; \
+ (v_i) <<= 1; \
+ if ((v_f) & 0x80000000) \
+ (v_i) |= 0x1; \
+ (v_f) <<= 1; \
+ } while (0)
+
+#define M_ADDUF(r_i, r_f, uf) /* r += uf, uf is U_LONG fraction */ \
+ M_ADD((r_i), (r_f), 0, (uf)) /* let optimizer worry about it */
+
+#define M_SUBUF(r_i, r_f, uf) /* r -= uf, uf is U_LONG fraction */ \
+ M_SUB((r_i), (r_f), 0, (uf)) /* let optimizer worry about it */
+
+#define M_ADDF(r_i, r_f, f) /* r += f, f is a LONG fraction */ \
+ do { \
+ if ((f) > 0) \
+ M_ADD((r_i), (r_f), 0, (f)); \
+ else if ((f) < 0) \
+ M_ADD((r_i), (r_f), (-1), (f));\
+ } while(0)
+
+#define M_ISNEG(v_i, v_f) /* v < 0 */ \
+ (((v_i) & 0x80000000) != 0)
+
+#define M_ISHIS(a_i, a_f, b_i, b_f) /* a >= b unsigned */ \
+ (((U_LONG)(a_i)) > ((U_LONG)(b_i)) || \
+ ((a_i) == (b_i) && ((U_LONG)(a_f)) >= ((U_LONG)(b_f))))
+
+#define M_ISGEQ(a_i, a_f, b_i, b_f) /* a >= b signed */ \
+ (((LONG)(a_i)) > ((LONG)(b_i)) || \
+ ((a_i) == (b_i) && ((U_LONG)(a_f)) >= ((U_LONG)(b_f))))
+
+#define M_ISEQU(a_i, a_f, b_i, b_f) /* a == b unsigned */ \
+ ((a_i) == (b_i) && (a_f) == (b_f))
+
+/*
+ * Operations on the long fp format
+ */
+#define L_ADD(r, a) M_ADD((r)->l_ui, (r)->l_uf, (a)->l_ui, (a)->l_uf)
+#define L_SUB(r, a) M_SUB((r)->l_ui, (r)->l_uf, (a)->l_ui, (a)->l_uf)
+#define L_NEG(v) M_NEG((v)->l_ui, (v)->l_uf)
+#define L_ADDUF(r, uf) M_ADDUF((r)->l_ui, (r)->l_uf, (uf))
+#define L_SUBUF(r, uf) M_SUBUF((r)->l_ui, (r)->l_uf, (uf))
+#define L_ADDF(r, f) M_ADDF((r)->l_ui, (r)->l_uf, (f))
+#define L_RSHIFT(v) M_RSHIFT((v)->l_i, (v)->l_uf)
+#define L_RSHIFTU(v) M_RSHIFT((v)->l_ui, (v)->l_uf)
+#define L_LSHIFT(v) M_LSHIFT((v)->l_ui, (v)->l_uf)
+#define L_CLR(v) ((v)->l_ui = (v)->l_uf = 0)
+
+#define L_ISNEG(v) (((v)->l_ui & 0x80000000) != 0)
+#define L_ISZERO(v) ((v)->l_ui == 0 && (v)->l_uf == 0)
+#define L_ISHIS(a, b) ((a)->l_ui > (b)->l_ui || \
+ ((a)->l_ui == (b)->l_ui && (a)->l_uf >= (b)->l_uf))
+#define L_ISGEQ(a, b) ((a)->l_i > (b)->l_i || \
+ ((a)->l_i == (b)->l_i && (a)->l_uf >= (b)->l_uf))
+#define L_ISEQU(a, b) M_ISEQU((a)->l_ui, (a)->l_uf, (b)->l_ui, (b)->l_uf)
+
+extern char * dofptoa P((u_fp, int, int, int));
+extern char * dolfptoa P((u_long, u_long, int, int, int));
+
+extern int atolfp P((const char *, l_fp *));
+extern int buftvtots P((const char *, l_fp *));
+extern void gettstamp P((l_fp *));
+extern char * fptoa P((s_fp, int));
+extern char * fptoms P((s_fp, int));
+extern char * fptoms P((s_fp, int));
+extern int hextolfp P((const char *, l_fp *));
+extern int mstolfp P((const char *, l_fp *));
+extern char * prettydate P((l_fp *));
+extern char * uglydate P((l_fp *));
+
+extern void get_systime P((l_fp *));
+extern int step_systime P((l_fp *));
+extern int step_systime_real P((l_fp *));
+extern int adj_systime P((l_fp *));
+
+#define lfptoa(_fpv, _ndec) mfptoa((_fpv)->l_ui, (_fpv)->l_uf, (_ndec))
+#define lfptoms(_fpv, _ndec) mfptoms((_fpv)->l_ui, (_fpv)->l_uf, (_ndec))
+
+#define ntoa(_sin) numtoa((_sin)->sin_addr.s_addr)
+#define ntohost(_sin) numtohost((_sin)->sin_addr.s_addr)
+
+#define ufptoa(_fpv, _ndec) dofptoa((_fpv), 0, (_ndec), 0)
+#define ufptoms(_fpv, _ndec) dofptoa((_fpv), 0, (_ndec), 1)
+#define ulfptoa(_fpv, _ndec) dolfptoa((_fpv)->l_ui, (_fpv)->l_uf, 0, (_ndec), 0)
+#define ulfptoms(_fpv, _ndec) dolfptoa((_fpv)->l_ui, (_fpv)->l_uf, 0, (_ndec), 1)
+#define umfptoa(_fpi, _fpf, _ndec) dolfptoa((_fpi), (_fpf), 0, (_ndec), 0)
diff --git a/usr.sbin/xntpd/include/ntp_if.h b/usr.sbin/xntpd/include/ntp_if.h
new file mode 100644
index 0000000..340481a
--- /dev/null
+++ b/usr.sbin/xntpd/include/ntp_if.h
@@ -0,0 +1,51 @@
+/*
+ * Sockets are not standard.
+ * So hide uglyness in include file.
+ */
+#if defined(SYS_CONVEXOS9)
+#include "/sys/sync/queue.h"
+#include "/sys/sync/sema.h"
+#endif
+
+#if defined(SYS_AIX)
+#include <sys/time.h>
+#include <time.h>
+#endif
+
+#if (defined(SOLARIS) && !defined(bsd)) || defined(SYS_SUNOS4)
+#include <sys/sockio.h>
+#endif
+
+#if defined(SYS_UNIXWARE1)
+#include <sys/sockio.h>
+#endif
+
+#if defined(SYS_PTX) || defined(SYS_SINIXM)
+#include <sys/stream.h>
+#include <sys/stropts.h>
+#endif
+
+#if defined(SYS_SVR4)
+#if !defined(USE_STREAMS_DEVICE_FOR_IF_CONFIG)
+#include <sys/sockio.h>
+#else /* USE_STREAMS_DEVICE_FOR_IF_CONFIG */
+#include <netinet/ip.h>
+#undef SIOCGIFCONF
+#undef SIOCGIFFLAGS
+#undef SIOCGIFADDR
+#undef SIOCGIFBRDADDR
+#undef SIOCGIFNETMASK
+#define SIOCGIFCONF IPIOC_GETIFCONF
+#define SIOCGIFFLAGS IPIOC_GETIFFLAGS
+#define SIOCGIFADDR IPIOC_GETIFADDR
+#define SIOCGIFBRDADDR IPIOC_GETIFBRDADDR
+#define SIOCGIFNETMASK IPIOC_GETIFNETMASK
+#endif /* USE_STREAMS_DEVICE_FOR_IF_CONFIG */
+
+#endif /* SYS_SVR4 */
+
+#if defined(SYS_FREEBSD)
+#include <sys/time.h>
+#endif
+
+#include <net/if.h>
diff --git a/usr.sbin/xntpd/include/ntp_in.h b/usr.sbin/xntpd/include/ntp_in.h
new file mode 100755
index 0000000..494051f
--- /dev/null
+++ b/usr.sbin/xntpd/include/ntp_in.h
@@ -0,0 +1,259 @@
+/* @(#)in.h 1.19 90/07/27 SMI; from UCB 7.5 2/22/88 */
+
+/*
+ * Copyright (c) 1982, 1986 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that this notice is preserved and that due credit is given
+ * to the University of California at Berkeley. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission. This software
+ * is provided ``as is'' without express or implied warranty.
+ */
+
+/*
+ * Constants and structures defined by the internet system,
+ * Per RFC 790, September 1981.
+ */
+
+#ifndef _netinet_in_h
+#define _netinet_in_h
+#define _NETINET_IN_H_
+#define _SYS_IN_INCLUDED
+#define __IN_HEADER
+
+/*
+ * Protocols
+ */
+#define IPPROTO_IP 0 /* dummy for IP */
+#define IPPROTO_ICMP 1 /* control message protocol */
+#define IPPROTO_IGMP 2 /* group control protocol */
+#define IPPROTO_GGP 3 /* gateway^2 (deprecated) */
+#define IPPROTO_ST 5 /* st */
+#define IPPROTO_TCP 6 /* tcp */
+#define IPPROTO_EGP 8 /* exterior gateway protocol */
+#define IPPROTO_PUP 12 /* pup */
+#define IPPROTO_UDP 17 /* user datagram protocol */
+#define IPPROTO_IDP 22 /* xns idp */
+#define IPPROTO_HELLO 63 /* "hello" routing protocol */
+#define IPPROTO_ND 77 /* UNOFFICIAL net disk proto */
+#define IPPROTO_OSPF 89 /* Open SPF IGP */
+
+#define IPPROTO_RAW 255 /* raw IP packet */
+#define IPPROTO_MAX 256
+
+/*
+ * Port/socket numbers: network standard functions
+ */
+#define IPPORT_ECHO 7
+#define IPPORT_DISCARD 9
+#define IPPORT_SYSTAT 11
+#define IPPORT_DAYTIME 13
+#define IPPORT_NETSTAT 15
+#define IPPORT_FTP 21
+#define IPPORT_TELNET 23
+#define IPPORT_SMTP 25
+#define IPPORT_TIMESERVER 37
+#define IPPORT_NAMESERVER 42
+#define IPPORT_WHOIS 43
+#define IPPORT_MTP 57
+
+/*
+ * Port/socket numbers: host specific functions
+ */
+#define IPPORT_TFTP 69
+#define IPPORT_RJE 77
+#define IPPORT_FINGER 79
+#define IPPORT_TTYLINK 87
+#define IPPORT_SUPDUP 95
+
+/*
+ * UNIX TCP sockets
+ */
+#define IPPORT_EXECSERVER 512
+#define IPPORT_LOGINSERVER 513
+#define IPPORT_CMDSERVER 514
+#define IPPORT_EFSSERVER 520
+
+/*
+ * UNIX UDP sockets
+ */
+#define IPPORT_BIFFUDP 512
+#define IPPORT_WHOSERVER 513
+#define IPPORT_ROUTESERVER 520 /* 520+1 also used */
+
+/*
+ * Ports < IPPORT_RESERVED are reserved for
+ * privileged processes (e.g. root).
+ * Ports > IPPORT_USERRESERVED are reserved
+ * for servers, not necessarily privileged.
+ */
+#define IPPORT_RESERVED 1024
+#define IPPORT_USERRESERVED 5000
+
+/*
+ * Link numbers
+ */
+#define IMPLINK_IP 155
+#define IMPLINK_LOWEXPER 156
+#define IMPLINK_HIGHEXPER 158
+
+/*
+ * Internet address
+ * This definition contains obsolete fields for compatibility
+ * with SunOS 3.x and 4.2bsd. The presence of subnets renders
+ * divisions into fixed fields misleading at best. New code
+ * should use only the s_addr field.
+ */
+struct in_addr {
+ union {
+ struct { u_char s_b1,s_b2,s_b3,s_b4; } S_un_b;
+ struct { u_short s_w1,s_w2; } S_un_w;
+ u_long S_addr;
+ } S_un;
+#define s_addr S_un.S_addr /* should be used for all code */
+#define s_host S_un.S_un_b.s_b2 /* OBSOLETE: host on imp */
+#define s_net S_un.S_un_b.s_b1 /* OBSOLETE: network */
+#define s_imp S_un.S_un_w.s_w2 /* OBSOLETE: imp */
+#define s_impno S_un.S_un_b.s_b4 /* OBSOLETE: imp # */
+#define s_lh S_un.S_un_b.s_b3 /* OBSOLETE: logical host */
+};
+
+/*
+ * Definitions of bits in internet address integers.
+ * On subnets, the decomposition of addresses to host and net parts
+ * is done according to subnet mask, not the masks here.
+ */
+#define IN_CLASSA(i) (((long)(i) & 0x80000000) == 0)
+#define IN_CLASSA_NET 0xff000000
+#define IN_CLASSA_NSHIFT 24
+#define IN_CLASSA_HOST 0x00ffffff
+#define IN_CLASSA_MAX 128
+
+#define IN_CLASSB(i) (((long)(i) & 0xc0000000) == 0x80000000)
+#define IN_CLASSB_NET 0xffff0000
+#define IN_CLASSB_NSHIFT 16
+#define IN_CLASSB_HOST 0x0000ffff
+#define IN_CLASSB_MAX 65536
+
+#define IN_CLASSC(i) (((long)(i) & 0xe0000000) == 0xc0000000)
+#define IN_CLASSC_NET 0xffffff00
+#define IN_CLASSC_NSHIFT 8
+#define IN_CLASSC_HOST 0x000000ff
+
+#define IN_CLASSD(i) (((long)(i) & 0xf0000000) == 0xe0000000)
+#define IN_CLASSD_NET 0xf0000000 /* These ones aren't really */
+#define IN_CLASSD_NSHIFT 28 /* net and host fields, but */
+#define IN_CLASSD_HOST 0x0fffffff /* routing needn't know. */
+#define IN_MULTICAST(i) IN_CLASSD(i)
+
+#define IN_EXPERIMENTAL(i) (((long)(i) & 0xe0000000) == 0xe0000000)
+#define IN_BADCLASS(i) (((long)(i) & 0xf0000000) == 0xf0000000)
+
+#define INADDR_ANY (u_long)0x00000000
+#define INADDR_LOOPBACK (u_long)0x7F000001
+#define INADDR_BROADCAST (u_long)0xffffffff /* must be masked */
+
+#define INADDR_UNSPEC_GROUP (u_long)0xe0000000 /* 224.0.0.0 */
+#define INADDR_ALLHOSTS_GROUP (u_long)0xe0000001 /* 224.0.0.1 */
+#define INADDR_MAX_LOCAL_GROUP (u_long)0xe00000ff /* 224.0.0.255 */
+
+#define IN_LOOPBACKNET 127 /* official! */
+
+/*
+ * Define a macro to stuff the loopback address into an Internet address
+ */
+#define IN_SET_LOOPBACK_ADDR(a) {(a)->sin_addr.s_addr = htonl(INADDR_LOOPBACK); \
+ (a)->sin_family = AF_INET;}
+
+/*
+ * Socket address, internet style.
+ */
+struct sockaddr_in {
+ short sin_family;
+ u_short sin_port;
+ struct in_addr sin_addr;
+ char sin_zero[8];
+};
+
+/*
+ * Options for use with [gs]etsockopt at the IP level.
+ */
+#define IP_OPTIONS 1 /* set/get IP per-packet options */
+#define IP_MULTICAST_IF 2 /* set/get IP multicast interface */
+#define IP_MULTICAST_TTL 3 /* set/get IP multicast timetolive */
+#define IP_MULTICAST_LOOP 4 /* set/get IP multicast loopback */
+#define IP_ADD_MEMBERSHIP 5 /* add an IP group membership */
+#define IP_DROP_MEMBERSHIP 6 /* drop an IP group membership */
+
+#define IP_DEFAULT_MULTICAST_TTL 1 /* normally limit m'casts to 1 hop */
+#define IP_DEFAULT_MULTICAST_LOOP 1 /* normally hear sends if a member */
+#define IP_MAX_MEMBERSHIPS 20 /* per socket; must fit in one mbuf */
+
+/*
+ * Argument structure for IP_ADD_MEMBERSHIP and IP_DROP_MEMBERSHIP.
+ */
+struct ip_mreq {
+ struct in_addr imr_multiaddr; /* IP multicast address of group */
+ struct in_addr imr_interface; /* local IP address of interface */
+};
+
+#if !defined(vax) && !defined(ntohl) && !defined(i386)
+/*
+ * Macros for number representation conversion.
+ */
+#define ntohl(x) (x)
+#define ntohs(x) (x)
+#define htonl(x) (x)
+#define htons(x) (x)
+#endif
+
+#if !defined(ntohl) && (defined(vax) || defined(i386))
+u_short ntohs(), htons();
+u_long ntohl(), htonl();
+#endif
+
+#ifdef KERNEL
+extern struct domain inetdomain;
+extern struct protosw inetsw[];
+struct in_addr in_makeaddr();
+u_long in_netof(), in_lnaof();
+#endif
+
+#ifndef BYTE_ORDER
+/*
+ * Definitions for byte order,
+ * according to byte significance from low address to high.
+ */
+#define LITTLE_ENDIAN 1234 /* least-significant byte first (vax) */
+#define BIG_ENDIAN 4321 /* most-significant byte first (IBM, net) */
+#define PDP_ENDIAN 3412 /* LSB first in word, MSW first in long (pdp) */
+
+#if defined(vax) || defined(i386)
+#define BYTE_ORDER LITTLE_ENDIAN
+#else
+#define BYTE_ORDER BIG_ENDIAN /* mc68000, tahoe, most others */
+#endif
+#endif BYTE_ORDER
+
+/*
+ * Macros for number representation conversion.
+ */
+#if BYTE_ORDER==LITTLE_ENDIAN
+#define NTOHL(d) ((d) = ntohl((d)))
+#define NTOHS(d) ((d) = ntohs((d)))
+#define HTONL(d) ((d) = htonl((d)))
+#define HTONS(d) ((d) = htons((d)))
+#else
+#define ntohl(x) (x)
+#define ntohs(x) (x)
+#define htonl(x) (x)
+#define htons(x) (x)
+#define NTOHL(d)
+#define NTOHS(d)
+#define HTONL(d)
+#define HTONS(d)
+#endif
+
+#endif /*!_netinet_in_h*/
diff --git a/usr.sbin/xntpd/include/ntp_io.h b/usr.sbin/xntpd/include/ntp_io.h
new file mode 100644
index 0000000..ebe20b4
--- /dev/null
+++ b/usr.sbin/xntpd/include/ntp_io.h
@@ -0,0 +1,25 @@
+/*
+ * POSIX says use <fnct.h> to get O_* symbols and
+ * SEEK_SET symbol form <untisd.h>.
+ */
+#if defined(NTP_POSIX_SOURCE)
+
+/*
+ * POSIX way
+ */
+#include <stdio.h>
+#if defined(HAVE_SIGNALED_IO) && (defined(SYS_AUX2) || defined(SYS_AUX3) || defined(SYS_PTX))
+#include <sys/file.h>
+#endif
+#include <unistd.h>
+#include <fcntl.h>
+#else
+/*
+ * BSD way
+ */
+#include <sys/file.h>
+#include <fcntl.h>
+#if !defined(SEEK_SET) && defined(L_SET)
+#define SEEK_SET L_SET
+#endif
+#endif
diff --git a/usr.sbin/xntpd/include/ntp_machine.h b/usr.sbin/xntpd/include/ntp_machine.h
new file mode 100644
index 0000000..25de021
--- /dev/null
+++ b/usr.sbin/xntpd/include/ntp_machine.h
@@ -0,0 +1,741 @@
+/*
+ * Collect all machine dependent idiosyncrasies in one place.
+ */
+
+#ifndef __ntp_machine
+#define __ntp_machine
+
+/*
+ Various options.
+ They can defined with the DEFS directive in the Config file if they
+ are not defined here.
+
+WHICH NICE
+
+ HAVE_ATT_NICE - Use att nice(priority_change)
+ HAVE_BSD_NICE - Use bsd setprioirty(which, who, priority)
+ HAVE_NO_NICE - Don't have (or use) either
+
+KERNEL MUCKING - If you porting to a new system see xntpd/ntp_unixclock.c and
+ util/tickadj.c to see what these do. This is very system
+ dependent stuff!!!
+
+ HAVE_LIBKVM - Use libkvm to read kernal memory
+ HAVE_READKMEM - Use read to read kernal memory
+ NOKMEM - Don't read kmem
+ HAVE_N_UN - Have u_nn nlist struct.
+
+WHICH SETPGRP TO USE - Not needed if NTP_POSIX_SOURCE is defined since you
+ better of setsid!
+
+ HAVE_ATT_SETPGRP - setpgrp(void) instead of setpgrp(int, int)
+
+
+Signaled IO - Signled IO defines.
+
+ HAVE_SIGNALED_IO - Enable signaled io. Assumes you are going to use SIGIO
+ for tty and udp io.
+ USE_UDP_SIGPOLL - Use SIGPOLL on socket io. This assumes that the
+ sockets routines are defined on top of streams.
+ USE_TTY_SIGPOLL - Use SIGPOLL on tty io. This assumes streams.
+ UDP_BACKWARDS_SETOWN - SunOS 3.5 or Ultirx 2.0 system.
+
+
+WHICH TERMINAL MODEL TO USE - I would assume HAVE_TERMIOS if
+ NTP_POSIX_SOURCE was set but can't. The
+ posix tty driver is too restrictive on most systems.
+ It is defined if you define STREAMS.
+
+ We do not put these defines in the ntp_machine.h as some systems
+ offer multiple interfaces and refclock configuration likes to
+ peek into the configuration defines for tty model restrictions.
+ Thus all tty definitions should be in the files in the machines directory.
+
+ HAVE_TERMIOS - Use POSIX termios.h
+ HAVE_SYSV_TTYS - Use SYSV termio.h
+ HAVE_BSD_TTYS - Use BSD stty.h
+
+THIS MAKES PORTS TO NEW SYSTEMS EASY - You only have to wory about
+ kernel mucking.
+
+ NTP_POSIX_SOURCE - Use POSIX functions over bsd functions and att functions.
+ This is NOT the same as _POSIX_SOURCE.
+ It is much weaker!
+
+
+STEP SLEW OR TWO STEP - The Default is to step.
+
+ SLEWALWAYS - setttimeofday can not be used to set the time of day at
+ all.
+ STEP_SLEW - setttimeofday can not set the seconds part of time
+ time use setttimeofday to set the seconds part of the
+ time and the slew the seconds.
+ FORCE_NTPDATE_STEP - even if SLEWALWAYS is defined, force a step of
+ of the systemtime (via settimeofday()). Only takes
+ affect if STEP_SLEW isn't defined.
+
+WHICH TIMEOFDAY()
+
+ SYSV_TIMEOFDAY - [sg]ettimeofday(struct timeval *) as opposed to BSD
+ [sg]ettimeofday(struct timeval *, struct timezone *)
+
+INFO ON NEW KERNEL PLL SYS CALLS
+
+ NTP_SYSCALLS_STD - use the "normal" ones
+ NTP_SYSCALL_GET - SYS_ntp_gettime id
+ NTP_SYSCALL_ADJ - SYS_ntp_adjtime id
+ NTP_SYSCALLS_LIBC - ntp_adjtime() and ntp_gettime() are in libc.
+
+HOW TO GET IP INTERFACE INFORMATION
+
+ Some UNIX V.4 machines implement a sockets library on top of
+ streams. For these systems, you must use send the SIOCGIFCONF down
+ the stream in an I_STR ioctl. This ususally also implies
+ USE_STREAMS_DEVICE FOR IF_CONFIG. Dell UNIX is a notable exception.
+
+ STREAMS_TLI - use ioctl(I_STR) to implement ioctl(SIOCGIFCONF)
+
+WHAT DOES IOCTL(SIOCGIFCONF) RETURN IN THE BUFFER
+
+ UNIX V.4 machines implement a sockets library on top of streams.
+ When requesting the IP interface configuration with an ioctl(2) calll,
+ an array of ifreq structures are placed in the provided buffer. Some
+ implementations also place the length of the buffer information in
+ the first integer position of the buffer.
+
+ SIZE_RETURNED_IN_BUFFER - size integer is in the buffer
+
+WILL IOCTL(SIOCGIFCONF) WORK ON A SOCKET
+
+ Some UNIX V.4 machines do not appear to support ioctl() requests for the
+ IP interface configuration on a socket. They appear to require the use
+ of the streams device instead.
+
+ USE_STREAMS_DEVICE_FOR_IF_CONFIG - use the /dev/ip device for configuration
+
+MISC
+
+ USE_PROTOTYPES - Prototype functions
+ DOSYNCTODR - Resync TODR clock every hour.
+ RETSIGTYPE - Define signal function type.
+ NO_SIGNED_CHAR_DECL - No "signed char" see include/ntp.h
+ LOCK_PROCESS - Have plock.
+ UDP_WILDCARD_DELIVERY
+ - these systems deliver broadcast packets to the wildcard
+ port instead to a port bound to the interface bound
+ to the correct broadcast address - are these
+ implementations broken or did the spec change ?
+
+DEFINITIONS FOR SYSTEM && PROCESSOR
+ STR_SYSTEM - value of system variable
+ STR_PROCESSOR - value of processor variable
+
+You could just put the defines on the DEFS line in machines/<os> file.
+I don't since there are lots of different types of compilers that a system might
+have, some that can do proto typing and others that cannot on the same system.
+I get a chance to twiddle some of the configuration parameters at compile
+time based on compiler/machine combinations by using this include file.
+See convex, aix and sun configurations see how complex it get.
+
+Note that it _is_ considered reasonable to add some system-specific defines
+to the machine/<os> file if it would be too inconvenient to puzzle them out
+in this file.
+
+*/
+
+
+/*
+ * RS6000 running AIX.
+ */
+#if defined(SYS_AIX)
+#define HAVE_SIGNALED_IO
+#ifndef _BSD
+#define NTP_STDC
+#define NTP_POSIX_SOURCE
+/*
+ * Keep USE_PROTOTYPES and _NO_PROTO in step.
+ */
+#if defined(_NO_PROTO) && defined(USE_PROTOTYPES)
+#undef USE_PROTOTYPES
+#endif
+#if !defined(_NO_PROTO) && !defined(USE_PROTOTYPES)
+#define USE_PROTOTYPES
+#endif
+#endif /*_BSD */
+#define HAVE_BSD_NICE
+#ifndef STR_SYSTEM
+#define STR_SYSTEM "UNIX/AIX"
+#endif
+#endif /* RS6000 */
+
+/*
+ * SunOS 4.X.X
+ * Note: posix version has NTP_POSIX_SOURCE and HAVE_SIGNALED_IO
+ */
+#if defined(SYS_SUNOS4)
+#define NTP_NEED_BOPS
+#define NO_SIGNED_CHAR_DECL
+#define HAVE_LIBKVM
+#define HAVE_MALLOC_H
+#define HAVE_BSD_NICE
+#define RETSIGTYPE void
+#define NTP_SYSCALL_GET 132
+#define NTP_SYSCALL_ADJ 147
+#define HAVE_MODEM_CONTROL
+#ifndef STR_SYSTEM
+#define STR_SYSTEM "UNIX/SunOS 4.x"
+#endif
+#endif
+
+/*
+ * Sinix-M
+ */
+#if defined(SYS_SINIXM)
+#undef HAVE_SIGNALED_IO
+#undef USE_TTY_SIGPOLL
+#undef USE_UDP_SIGPOLL
+#define STREAMS_TLI
+#define NO_SIGNED_CHAR_DECL
+#define STEP_SLEW /* TWO step */
+#define RETSIGTYPE void
+#define NTP_POSIX_SOURCE
+#define HAVE_ATT_SETPGRP
+#define HAVE_ATT_NICE
+#ifndef STR_SYSTEM
+#define STR_SYSTEM "UNIX/SINIX-M"
+#endif
+#endif
+
+/*
+ * SunOS 5.1 or SunOS 5.2 or Solaris 2.1 or Solaris 2.2
+ */
+#if defined(SYS_SOLARIS)
+#define HAVE_SIGNALED_IO
+#define USE_TTY_SIGPOLL
+#define USE_UDP_SIGPOLL
+#define NO_SIGNED_CHAR_DECL
+#define STEP_SLEW /* TWO step */
+#define RETSIGTYPE void
+#define NTP_POSIX_SOURCE
+#define HAVE_ATT_SETPGRP
+#define HAVE_ATT_NICE
+#define UDP_WILDCARD_DELIVERY
+#define HAVE_MODEM_CONTROL
+#ifndef STR_SYSTEM
+#define STR_SYSTEM "UNIX/Solaris 2.x"
+#endif
+#endif
+
+/*
+ * Convex
+ */
+#if defined(SYS_CONVEXOS10) || defined(SYS_CONVEXOS9)
+#define HAVE_SIGNALED_IO
+#define HAVE_N_UN
+#define HAVE_READKMEM
+#define HAVE_BSD_NICE
+#if defined(convex)
+#define RETSIGTYPE int
+#define NO_SIGNED_CHAR_DECL
+#else
+#if defined(__stdc__) && !defined(USE_PROTOTYPES)
+#define USE_PROTOTYPES
+#endif
+#if !defined(__stdc__) && defined(USE_PROTOTYPES)
+#undef USE_PROTOTYPES
+#endif
+#define NTP_POSIX_SOURCE
+#define HAVE_ATT_SETPGRP
+#endif
+#ifndef STR_SYSTEM
+#define STR_SYSTEM "UNIX/ConvexOS"
+#endif
+#endif
+
+/*
+ * IRIX 4.X and IRIX 5.x
+ */
+#if defined(SYS_IRIX4)||defined(SYS_IRIX5)
+#define HAVE_SIGNALED_IO
+#define USE_TTY_SIGPOLL
+#define ADJTIME_IS_ACCURATE
+#define LOCK_PROCESS
+#define USE_PROTOTYPES
+#define HAVE_ATT_SETPGRP
+#define HAVE_BSD_NICE
+#define NTP_POSIX_SOURCE
+#ifndef STR_SYSTEM
+#define STR_SYSTEM "UNIX/IRIX"
+#endif
+#endif
+
+/*
+ * Ultrix
+ * Note: posix version has NTP_POSIX_SOURCE and HAVE_SIGNALED_IO
+ */
+#if defined(SYS_ULTRIX)
+#define S_CHAR_DEFINED
+#define HAVE_READKMEM
+#define HAVE_BSD_NICE
+#define RETSIGTYPE void
+#define NTP_SYSCALLS_STD
+#define HAVE_MODEM_CONTROL
+#ifndef STR_SYSTEM
+#define STR_SYSTEM "UNIX/Ultrix"
+#endif
+#endif
+
+/*
+ * AUX
+ */
+#if defined(SYS_AUX2) || defined(SYS_AUX3)
+#define NO_SIGNED_CHAR_DECL
+#define HAVE_READKMEM
+#define HAVE_ATT_NICE
+#define LOCK_PROCESS
+#define NTP_POSIX_SOURCE
+/*
+ * This requires that _POSIX_SOURCE be forced on the
+ * compiler command flag. We can't do it here since this
+ * file is included _after_ the system header files and we
+ * need to let _them_ know we're POSIX. We do this in
+ * compilers/aux3.gcc...
+ */
+#define SLEWALWAYS
+#define FORCE_NTPDATE_STEP
+#define RETSIGTYPE void
+#define HAVE_ATT_SETPGRP
+#define LOG_NTP LOG_LOCAL1
+#define HAVE_SIGNALED_IO
+#define NTP_NEED_BOPS
+#ifndef STR_SYSTEM
+#define STR_SYSTEM "UNIX/AUX"
+#endif
+#endif
+
+/*
+ * Next
+ */
+#if defined(SYS_NEXT)
+#define RETSIGTYPE void
+#define DOSYNCTODR
+#define HAVE_READKMEM
+#define HAVE_BSD_NICE
+#define HAVE_N_UN
+#undef NTP_POSIX_SOURCE
+#ifndef STR_SYSTEM
+#define STR_SYSTEM "UNIX/Next"
+#endif
+#endif
+
+/*
+ * HPUX
+ */
+#if defined(SYS_HPUX)
+#define NTP_POSIX_SOURCE
+#define HAVE_SIGNALED_IO
+#define getdtablesize() sysconf(_SC_OPEN_MAX)
+#define setlinebuf(f) setvbuf(f, NULL, _IOLBF, 0)
+#define NO_SIGNED_CHAR_DECL
+#define LOCK_PROCESS
+#define RETSIGTYPE void
+#if (SYS_HPUX < 9)
+#define HAVE_NO_NICE /* HPUX uses rtprio instead */
+#else
+#define HAVE_BSD_NICE /* new at 9.X */
+#endif
+#if (SYS_HPUX < 10)
+#define NOKMEM
+#else
+#define HAVE_READKMEM
+#endif
+#ifndef STR_SYSTEM
+#define STR_SYSTEM "UNIX/HPUX"
+#endif
+#endif
+
+/*
+ * bsdi
+ */
+#if defined(SYS_BSDI)
+#define HAVE_SIGNALED_IO
+#define HAVE_LIBKVM
+#define NTP_POSIX_SOURCE
+#define HAVE_BSD_NICE
+#ifndef STR_SYSTEM
+#define STR_SYSTEM "UNIX/BSDI"
+#endif
+#endif
+
+/*
+ * 4.4 bsd
+ */
+#if defined(SYS_44BSD)
+#define HAVE_SIGNALED_IO
+#define HAVE_LIBKVM
+#define NTP_POSIX_SOURCE
+#define HAVE_BSD_NICE
+#define USE_PROTOTYPES
+#ifndef STR_SYSTEM
+#define STR_SYSTEM "UNIX/4.4BSD"
+#endif
+#define MCAST
+#ifdef SYS_FREEBSD
+#ifdef HAVE_SIGNALED_IO
+#undef HAVE_SIGNALED_IO
+#endif
+#define HAVE_TERMIOS
+#define HAVE_UNAME
+#define HAVE_SYS_TIMEX_H
+#define HAVE_GETBOOTFILE
+#define NTP_SYSCALLS_LIBC
+#define KERNEL_PLL
+#endif
+#endif
+
+/*
+ * Linux
+ */
+#if defined(SYS_LINUX)
+#undef HAVE_SIGNALED_IO
+#define RETSIGTYPE void
+#define NTP_POSIX_SOURCE
+#define ADJTIME_IS_ACCURATE
+#define HAVE_SYS_TIMEX_H
+/* hope there will be a standard interface
+ * along with a standard name one day ! */
+#define ntp_adjtime __adjtimex
+#define HAVE_BSD_NICE
+#ifndef STR_SYSTEM
+#define STR_SYSTEM "UNIX/Linux"
+#endif
+#endif
+
+/*
+ * 386BSD and any variants 8-) - should really have only ONE define
+ * for this bunch.
+ */
+#if defined(SYS_386BSD) || defined(SYS_NETBSD)
+#define HAVE_SIGNALED_IO
+#define HAVE_READKMEM
+#define NTP_POSIX_SOURCE
+#define HAVE_BSD_NICE
+#ifndef STR_SYSTEM
+#define STR_SYSTEM "UNIX/\052BSD"
+#endif
+#ifdef SYS_FREEBSD
+#define HAVE_TERMIOS
+#define HAVE_UNAME
+#define HAVE_SYS_TIMEX_H
+#define NTP_SYSCALLS_LIBC
+#define KERNEL_PLL
+#endif
+#endif
+
+/*
+ * DEC AXP OSF/1
+ */
+#if defined(SYS_DECOSF1)
+#define HAVE_SIGNALED_IO
+#define HAVE_READKMEM
+#define NTP_POSIX_SOURCE
+#define NTP_SYSCALLS_STD
+#define HAVE_BSD_NICE
+#define HAVE_MODEM_CONTROL
+#ifndef STR_SYSTEM
+#define STR_SYSTEM "UNIX/DECOSF1"
+#endif
+#endif
+
+/*
+ * Intel x86 OSF/1
+ */
+#if defined(SYS_IX86OSF1)
+#define HAVE_SIGNALED_IO
+#define HAVE_READKMEM
+#define NTP_POSIX_SOURCE
+#define NTP_SYSCALLS_STD
+#define HAVE_BSD_NICE
+#define HAVE_MODEM_CONTROL
+#define SYS_DECOSF1
+#ifndef STR_SYSTEM
+#define STR_SYSTEM "UNIX/IX86OSF1"
+#endif
+#endif
+
+/*
+ * ISI
+ */
+#if defined(SYS_BSD)
+#define NO_SIGNED_CHAR_DECL
+#define HAVE_BSD_NICE
+#define HAVE_BSD_TTYS
+#define HAVE_READKMEM
+#define HAVE_SIGNALED_IO
+#define NEED_VSPRINTF
+#undef NTP_POSIX_SOURCE
+#endif
+
+/*
+ * I386
+ * XXX - what OS?
+ */
+#if defined(SYS_I386)
+#define HAVE_READKMEM
+#define S_CHAR_DEFINED
+#define HAVE_BSD_NICE
+#ifndef STR_SYSTEM
+#define STR_SYSTEM "UNIX/I386"
+#endif
+#endif
+
+/*
+ * Mips
+ */
+#if defined(SYS_MIPS)
+#define NOKMEM
+#define HAVE_BSD_NICE
+#ifndef STR_SYSTEM
+#define STR_SYSTEM "UNIX/Mips"
+#endif
+#endif
+
+/*
+ * SEQUENT
+ */
+#if defined(SYS_SEQUENT)
+#define HAVE_BSD_NICE
+#ifndef STR_SYSTEM
+#define STR_SYSTEM "UNIX/Sequent Dynix 3"
+#endif
+#endif
+
+/*
+ * PTX
+ */
+#if defined(SYS_PTX)
+#define NO_SIGNED_CHAR_DECL
+#define STREAMS_TLI
+#define HAVE_ATT_SETPGRP
+#define HAVE_SIGNALED_IO
+#define USE_UDP_SIGPOLL
+#define USE_TTY_SIGPOLL
+#undef ADJTIME_IS_ACCURATE /* not checked yet */
+#define LOCK_PROCESS
+#define HAVE_ATT_SETPGRP
+#define HAVE_ATT_NICE
+#define STEP_SLEW /* TWO step */
+#define SYSV_GETTIMEOFDAY
+#define HAVE_READKMEM
+#define UDP_WILDCARD_DELIVERY
+#define NTP_POSIX_SOURCE
+#define memmove(x, y, z) memcpy(x, y, z)
+struct timezone { int __0; }; /* unused placebo */
+/*
+ * no comment !@!
+ */
+typedef unsigned int u_int;
+#ifndef _NETINET_IN_SYSTM_INCLUDED /* i am about to comment... */
+typedef unsigned char u_char;
+typedef unsigned short u_short;
+typedef unsigned long u_long;
+#endif
+#ifndef STR_SYSTEM
+#define STR_SYSTEM "UNIX/Sequent PTX"
+#endif
+#endif
+
+
+/*
+ * Sony NEWS
+ */
+#if defined(SYS_SONY)
+#define NO_SIGNED_CHAR_DECL
+#define HAVE_READKMEM
+#define HAVE_BSD_NICE
+#ifndef STR_SYSTEM
+#define STR_SYSTEM "UNIX/Sony"
+#endif
+#endif
+
+/*
+ * VAX
+ * XXX - VMS?
+ */
+#if defined(SYS_VAX)
+#define NO_SIGNED_CHAR_DECL
+#define HAVE_READKMEM
+#define HAVE_BSD_NICE
+#ifndef STR_SYSTEM
+#define STR_SYSTEM "UNIX/VAX"
+#endif
+#endif
+
+/*
+ * UNIX V.4 on and NCR 3000
+ */
+#if defined(SYS_SVR4)
+#define HAVE_ATT_SETPGRP
+#define USE_PROTOTYPES
+#define NTP_POSIX_SOURCE
+#define HAVE_ATT_NICE
+#define HAVE_READKMEM
+#define USE_TTY_SIGPOLL
+#define USE_UDP_SIGPOLL
+#define STREAM
+#define STEP_SLEW /* TWO step */
+#define LOCK_PROCESS
+#define SYSV_TIMEOFDAY
+#define SIZE_RETURNED_IN_BUFFER
+#ifndef STR_SYSTEM
+#define STR_SYSTEM "UNIX/SysVR4"
+#endif
+#endif
+
+/*
+ * (Univel/Novell) Unixware1 SVR4 on intel x86 processor
+ */
+#if defined(SYS_UNIXWARE1)
+/* #define _POSIX_SOURCE */
+#undef HAVE_ATT_SETPGRP
+#define USE_PROTOTYPES
+#define NTP_POSIX_SOURCE
+#define HAVE_ATT_NICE
+#define HAVE_READKMEM
+#define USE_TTY_SIGPOLL
+#define USE_UDP_SIGPOLL
+#define UDP_WILDCARD_DELIVERY
+#undef HAVE_SIGNALED_IO
+#define STREAM
+#define STREAMS
+#ifndef STREAMS_TLI
+/*#define STREAMS_TLI*/
+#endif
+/* #define USE_STREAMS_DEVICE_FOR_IF_CONFIG */
+#undef STEP_SLEW /* TWO step */
+#define LOCK_PROCESS
+#define NO_SIGNED_CHAR_DECL
+#undef SYSV_TIMEOFDAY
+#define SIZE_RETURNED_IN_BUFFER
+#define RETSIGTYPE void
+#include <sys/sockio.h>
+#include <sys/types.h>
+#include <netinet/in_systm.h>
+#ifndef STR_SYSTEM
+#define STR_SYSTEM "UNIX/Unixware1"
+#endif
+#endif
+
+/*
+ * DomainOS
+ */
+#if defined(SYS_DOMAINOS)
+#define HAVE_BSD_NICE
+#define NOKMEM
+#define HAVE_SIGNALED_IO
+#define NTP_SYSCALLS_STD
+#define USE_PROTOTYPES
+#define UDP_WILDCARD_DELIVERY
+#ifndef STR_SYSTEM
+#define STR_SYSTEM "UNIX/DOMAINOS"
+#endif
+#endif
+
+#ifdef STREAM /* STREAM implies TERMIOS */
+#ifndef HAVE_TERMIOS
+#define HAVE_TERMIOS
+#endif
+#endif
+
+#ifndef RETSIGTYPE
+#if defined(NTP_POSIX_SOURCE)
+#define RETSIGTYPE void
+#else
+#define RETSIGTYPE int
+#endif
+#endif
+
+#ifdef NTP_SYSCALLS_STD
+#ifndef NTP_SYSCALL_GET
+#define NTP_SYSCALL_GET 235
+#endif
+#ifndef NTP_SYSCALL_ADJ
+#define NTP_SYSCALL_ADJ 236
+#endif
+#endif /* NTP_SYSCALLS_STD */
+
+#if !defined(HAVE_ATT_NICE) \
+ && !defined(HAVE_BSD_NICE) \
+ && !defined(HAVE_NO_NICE)
+ ERROR You_must_define_one_of_the_HAVE_xx_NICE_defines
+#endif
+
+/*
+ * use only one tty model - no use in initialising
+ * a tty in three ways
+ * HAVE_TERMIOS is preferred over HAVE_SYSV_TTYS over HAVE_BSD_TTYS
+ */
+#ifdef HAVE_TERMIOS
+#undef HAVE_BSD_TTYS
+#undef HAVE_SYSV_TTYS
+#endif
+
+#ifdef HAVE_SYSV_TTYS
+#undef HAVE_BSD_TTYS
+#endif
+
+#if !defined(HAVE_SYSV_TTYS) \
+ && !defined(HAVE_BSD_TTYS) \
+ && !defined(HAVE_TERMIOS)
+ ERROR no_tty_type_defined
+#endif
+
+
+#if !defined(XNTP_BIG_ENDIAN) && !defined(XNTP_LITTLE_ENDIAN)
+
+# if defined(XNTP_AUTO_ENDIAN)
+# include <netinet/in.h>
+
+# if BYTE_ORDER == BIG_ENDIAN
+# define XNTP_BIG_ENDIAN
+# endif
+# if BYTE_ORDER == LITTLE_ENDIAN
+# define XNTP_LITTLE_ENDIAN
+# endif
+
+# else /* AUTO */
+
+# ifdef WORDS_BIGENDIAN
+# define XNTP_BIG_ENDIAN 1
+# else
+# define XNTP_LITTLE_ENDIAN 1
+# endif
+
+# endif /* AUTO */
+
+#endif /* !BIG && !LITTLE */
+
+/*
+ * Byte order woes. The DES code is sensitive to byte order. This
+ * used to be resolved by calling ntohl() and htonl() to swap things
+ * around, but this turned out to be quite costly on Vaxes where those
+ * things are actual functions. The code now straightens out byte
+ * order troubles on its own, with no performance penalty for little
+ * end first machines, but at great expense to cleanliness.
+ */
+#if !defined(XNTP_BIG_ENDIAN) && !defined(XNTP_LITTLE_ENDIAN)
+ /*
+ * Pick one or the other.
+ */
+ BYTE_ORDER_NOT_DEFINED_FOR_AUTHENTICATION
+#endif
+
+#if defined(XNTP_BIG_ENDIAN) && defined(XNTP_LITTLE_ENDIAN)
+ /*
+ * Pick one or the other.
+ */
+ BYTE_ORDER_NOT_DEFINED_FOR_AUTHENTICATION
+#endif
+
+
+#endif /* __ntp_machine */
diff --git a/usr.sbin/xntpd/include/ntp_malloc.h b/usr.sbin/xntpd/include/ntp_malloc.h
new file mode 100644
index 0000000..0079cb7
--- /dev/null
+++ b/usr.sbin/xntpd/include/ntp_malloc.h
@@ -0,0 +1,15 @@
+/*
+ * Define malloc and friends.
+ */
+#ifndef _ntp_malloc_h
+
+#define _ntp_malloc_h
+#ifdef NTP_POSIX_SOURCE
+#include <stdlib.h>
+#else /* NTP_POSIX_SOURCE */
+#ifdef HAVE_MALLOC_H
+#include <malloc.h>
+#endif
+#endif /* NTP_POSIX_SOURCE */
+
+#endif /* _ntp_malloc_h */
diff --git a/usr.sbin/xntpd/include/ntp_refclock.h b/usr.sbin/xntpd/include/ntp_refclock.h
new file mode 100644
index 0000000..fcc1200
--- /dev/null
+++ b/usr.sbin/xntpd/include/ntp_refclock.h
@@ -0,0 +1,224 @@
+/*
+ * ntp_refclock.h - definitions for reference clock support
+ */
+
+#include "ntp_types.h"
+
+#if defined(HAVE_BSD_TTYS)
+#include <sgtty.h>
+#endif /* HAVE_BSD_TTYS */
+
+#if defined(HAVE_SYSV_TTYS)
+#include <termio.h>
+#endif /* HAVE_SYSV_TTYS */
+
+#if defined(HAVE_TERMIOS)
+#include <termios.h>
+#endif
+
+#if defined(STREAM)
+#include <stropts.h>
+#if defined(CLK)
+#include <sys/clkdefs.h>
+#endif /* CLK */
+#endif /* STREAM */
+
+#if !defined(SYSV_TTYS) && !defined(STREAM) & !defined(BSD_TTYS)
+#define BSD_TTYS
+#endif /* SYSV_TTYS STREAM BSD_TTYS */
+
+/*
+ * Macros to determine the clock type and unit numbers from a
+ * 127.127.t.u address
+ */
+#define REFCLOCKTYPE(srcadr) ((SRCADR(srcadr) >> 8) & 0xff)
+#define REFCLOCKUNIT(srcadr) (SRCADR(srcadr) & 0xff)
+
+/*
+ * List of reference clock names and descriptions. These must agree with
+ * lib/clocktypes.c and xntpd/refclock_conf.c.
+ */
+struct clktype {
+ int code; /* driver "major" number */
+ char *clocktype; /* long description */
+ char *abbrev; /* short description */
+};
+
+/*
+ * Configuration flag values
+ */
+#define CLK_HAVETIME1 0x1
+#define CLK_HAVETIME2 0x2
+#define CLK_HAVEVAL1 0x4
+#define CLK_HAVEVAL2 0x8
+
+#define CLK_FLAG1 0x1
+#define CLK_FLAG2 0x2
+#define CLK_FLAG3 0x4
+#define CLK_FLAG4 0x8
+
+#define CLK_HAVEFLAG1 0x10
+#define CLK_HAVEFLAG2 0x20
+#define CLK_HAVEFLAG3 0x40
+#define CLK_HAVEFLAG4 0x80
+
+/*
+ * Structure for returning clock status
+ */
+struct refclockstat {
+ u_char type; /* clock type */
+ u_char flags; /* clock flags */
+ u_char haveflags; /* bit array of valid flags */
+ u_char lencode; /* length of last timecode */
+ char *lastcode; /* last timecode received */
+ U_LONG polls; /* transmit polls */
+ U_LONG noresponse; /* no response to poll */
+ U_LONG badformat; /* bad format timecode received */
+ U_LONG baddata; /* invalid data timecode received */
+ U_LONG timereset; /* driver resets */
+ char *clockdesc; /* ASCII description */
+ l_fp fudgetime1; /* configure fudge time1 */
+ l_fp fudgetime2; /* configure fudge time2 */
+ LONG fudgeval1; /* configure fudge value1 */
+ LONG fudgeval2; /* configure fudge value2 */
+ u_char currentstatus; /* clock status */
+ u_char lastevent; /* last exception event */
+ u_char unused; /* spare */
+ struct ctl_var *kv_list; /* additional variables */
+};
+
+/*
+ * Reference clock I/O structure. Used to provide an interface between
+ * the reference clock drivers and the I/O module.
+ */
+struct refclockio {
+ struct refclockio *next; /* link to next structure */
+ void (*clock_recv)();/* completion routine */
+ caddr_t srcclock; /* pointer to clock structure */
+ int datalen; /* lenth of data */
+ int fd; /* file descriptor */
+ u_long recvcount; /* count of receive completions */
+};
+
+/*
+ * Structure for returning debugging info
+ */
+#define NCLKBUGVALUES 16
+#define NCLKBUGTIMES 32
+
+struct refclockbug {
+ u_char nvalues; /* values following */
+ u_char ntimes; /* times following */
+ u_short svalues; /* values format sign array */
+ U_LONG stimes; /* times format sign array */
+ U_LONG values[NCLKBUGVALUES]; /* real values */
+ l_fp times[NCLKBUGTIMES]; /* real times */
+};
+
+/*
+ * Structure interface between the reference clock support
+ * ntp_refclock.c and the driver utility routines
+ */
+#define MAXSTAGE 64 /* max stages in shift register */
+#define BMAX 128 /* max timecode length */
+#define GMT 0 /* I hope nobody sees this */
+#define MAXDIAL 20 /* max length of modem dial strings */
+
+/*
+ * Line discipline flags. These require line discipline or streams
+ * modules to be installed/loaded in the kernel. If specified, but not
+ * installed, the code runs as if unspecified.
+ */
+#define LDISC_STD 0x0 /* standard */
+#define LDISC_CLK 0x1 /* tty_clk \n intercept */
+#define LDISC_CLKPPS 0x2 /* tty_clk \377 intercept */
+#define LDISC_ACTS 0x4 /* tty_clk #* intercept */
+#define LDISC_CHU 0x8 /* tty_chu */
+#define LDISC_PPS 0x10 /* ppsclock */
+
+struct refclockproc {
+ struct refclockio io; /* I/O handler structure */
+ caddr_t unitptr; /* pointer to unit structure */
+ u_long lasttime; /* last clock update time */
+ u_char leap; /* leap/synchronization code */
+ u_char currentstatus; /* clock status */
+ u_char lastevent; /* last exception event */
+ u_char type; /* clock type */
+ char *clockdesc; /* clock description */
+ char lastcode[BMAX]; /* last timecode received */
+ u_char lencode; /* length of last timecode */
+
+ u_int year; /* year of eternity */
+ u_int day; /* day of year */
+ u_int hour; /* hour of day */
+ u_int minute; /* minute of hour */
+ u_int second; /* second of minute */
+ u_int msec; /* millisecond of second */
+ u_long usec; /* microsecond of second (alt) */
+ u_int nstages; /* median filter stages */
+ u_long yearstart; /* beginning of year */
+ u_long coderecv; /* sample counter */
+ l_fp lastref; /* last reference timestamp */
+ l_fp lastrec; /* last local timestamp */
+ l_fp offset; /* median offset */
+ u_fp dispersion; /* sample dispersion */
+ l_fp filter[MAXSTAGE]; /* median filter */
+
+ /*
+ * Configuration data
+ */
+ l_fp fudgetime1; /* fudge time1 */
+ l_fp fudgetime2; /* fudge time2 */
+ u_long refid; /* reference identifier */
+ u_long sloppyclockflag; /* fudge flags */
+
+ /*
+ * Status tallies
+ */
+ u_long timestarted; /* time we started this */
+ u_long polls; /* polls sent */
+ u_long noreply; /* no replies to polls */
+ u_long badformat; /* bad format reply */
+ u_long baddata; /* bad data reply */
+};
+
+/*
+ * Structure interface between the reference clock support
+ * ntp_refclock.c and particular clock drivers. This must agree with the
+ * structure defined in the driver.
+ */
+#define noentry 0 /* flag for null routine */
+#define NOFLAGS 0 /* flag for null flags */
+
+struct refclock {
+ int (*clock_start) P((int, struct peer *));
+ void (*clock_shutdown) P((int, struct peer *));
+ void (*clock_poll) P((int, struct peer *));
+ void (*clock_control) P((int, struct refclockstat *,
+ struct refclockstat *));
+ void (*clock_init) P((void));
+ void (*clock_buginfo) P((int, struct refclockbug *));
+ u_long clock_flags;
+};
+
+/*
+ * Function prototypes
+ */
+extern int io_addclock_simple P((struct refclockio *));
+extern int io_addclock P((struct refclockio *));
+extern void io_closeclock P((struct refclockio *));
+
+#ifdef REFCLOCK
+extern void refclock_buginfo P((struct sockaddr_in *,
+ struct refclockbug *));
+extern void refclock_control P((struct sockaddr_in *,
+ struct refclockstat *,
+ struct refclockstat *));
+extern int refclock_open P((char *, int, int));
+extern void refclock_transmit P((struct peer *));
+extern int refclock_ioctl P((int, int));
+extern int refclock_process P((struct refclockproc *, int, int));
+extern void refclock_report P((struct peer *, u_char));
+extern int refclock_gtlin P((struct recvbuf *, char *, int,
+ l_fp *));
+#endif /* REFCLOCK */
diff --git a/usr.sbin/xntpd/include/ntp_request.h b/usr.sbin/xntpd/include/ntp_request.h
new file mode 100644
index 0000000..ea2d769
--- /dev/null
+++ b/usr.sbin/xntpd/include/ntp_request.h
@@ -0,0 +1,808 @@
+/*
+ * ntp_request.h - definitions for the xntpd remote query facility
+ */
+
+#include "ntp_types.h"
+
+/*
+ * A mode 7 packet is used exchanging data between an NTP server
+ * and a client for purposes other than time synchronization, e.g.
+ * monitoring, statistics gathering and configuration. A mode 7
+ * packet has the following format:
+ *
+ * 0 1 2 3
+ * 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * |R|M| VN | Mode|A| Sequence | Implementation| Req Code |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Err | Number of data items | MBZ | Size of data item |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | |
+ * | Data (Minimum 0 octets, maximum 500 octets) |
+ * | |
+ * [...]
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | Encryption Keyid (when A bit set) |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ * | |
+ * | Message Authentication Code (when A bit set) |
+ * | |
+ * +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ *
+ * where the fields are (note that the client sends requests, the server
+ * responses):
+ *
+ * Response Bit: This packet is a response (if clear, packet is a request).
+ *
+ * More Bit: Set for all packets but the last in a response which
+ * requires more than one packet.
+ *
+ * Version Number: 2 for current version
+ *
+ * Mode: Always 7
+ *
+ * Authenticated bit: If set, this packet is authenticated.
+ *
+ * Sequence number: For a multipacket response, contains the sequence
+ * number of this packet. 0 is the first in the sequence,
+ * 127 (or less) is the last. The More Bit must be set in
+ * all packets but the last.
+ *
+ * Implementation number: The number of the implementation this request code
+ * is defined by. An implementation number of zero is used
+ * for requst codes/data formats which all implementations
+ * agree on. Implementation number 255 is reserved (for
+ * extensions, in case we run out).
+ *
+ * Request code: An implementation-specific code which specifies the
+ * operation to be (which has been) performed and/or the
+ * format and semantics of the data included in the packet.
+ *
+ * Err: Must be 0 for a request. For a response, holds an error
+ * code relating to the request. If nonzero, the operation
+ * requested wasn't performed.
+ *
+ * 0 - no error
+ * 1 - incompatable implementation number
+ * 2 - unimplemented request code
+ * 3 - format error (wrong data items, data size, packet size etc.)
+ * 4 - no data available (e.g. request for details on unknown peer)
+ * 5-6 I don't know
+ * 7 - authentication failure (i.e. permission denied)
+ *
+ * Number of data items: number of data items in packet. 0 to 500
+ *
+ * MBZ: A reserved data field, must be zero in requests and responses.
+ *
+ * Size of data item: size of each data item in packet. 0 to 500
+ *
+ * Data: Variable sized area containing request/response data. For
+ * requests and responses the size in octets must be greater
+ * than or equal to the product of the number of data items
+ * and the size of a data item. For requests the data area
+ * must be exactly 40 octets in length. For responses the
+ * data area may be any length between 0 and 500 octets
+ * inclusive.
+ *
+ * Message Authentication Code: Same as NTP spec, in definition and function.
+ * May optionally be included in requests which require
+ * authentication, is never included in responses.
+ *
+ * The version number, mode and keyid have the same function and are
+ * in the same location as a standard NTP packet. The request packet
+ * is the same size as a standard NTP packet to ease receive buffer
+ * management, and to allow the same encryption procedure to be used
+ * both on mode 7 and standard NTP packets. The mac is included when
+ * it is required that a request be authenticated, the keyid should be
+ * zero in requests in which the mac is not included.
+ *
+ * The data format depends on the implementation number/request code pair
+ * and whether the packet is a request or a response. The only requirement
+ * is that data items start in the octet immediately following the size
+ * word and that data items be concatenated without padding between (i.e.
+ * if the data area is larger than data_items*size, all padding is at
+ * the end). Padding is ignored, other than for encryption purposes.
+ * Implementations using encryption might want to include a time stamp
+ * or other data in the request packet padding. The key used for requests
+ * is implementation defined, but key 15 is suggested as a default.
+ */
+
+/*
+ * A request packet. These are almost a fixed length.
+ */
+struct req_pkt {
+ u_char rm_vn_mode; /* response, more, version, mode */
+ u_char auth_seq; /* key, sequence number */
+ u_char implementation; /* implementation number */
+ u_char request; /* request number */
+ u_short err_nitems; /* error code/number of data items */
+ u_short mbz_itemsize; /* item size */
+ char data[32]; /* data area */
+ l_fp tstamp; /* time stamp, for authentication */
+ U_LONG keyid; /* encryption key */
+ char mac[MAX_MAC_LEN-sizeof(U_LONG)]; /* (optional) 8 byte auth code */
+};
+
+/*
+ * Input packet lengths. One with the mac, one without.
+ */
+#define REQ_LEN_MAC (sizeof(struct req_pkt))
+#define REQ_LEN_NOMAC (sizeof(struct req_pkt) - MAX_MAC_LEN)
+
+/*
+ * A response packet. The length here is variable, this is a
+ * maximally sized one. Note that this implementation doesn't
+ * authenticate responses.
+ */
+#define RESP_HEADER_SIZE (8)
+#define RESP_DATA_SIZE (500)
+
+struct resp_pkt {
+ u_char rm_vn_mode; /* response, more, version, mode */
+ u_char auth_seq; /* key, sequence number */
+ u_char implementation; /* implementation number */
+ u_char request; /* request number */
+ u_short err_nitems; /* error code/number of data items */
+ u_short mbz_itemsize; /* item size */
+ char data[RESP_DATA_SIZE]; /* data area */
+};
+
+
+/*
+ * Information error codes
+ */
+#define INFO_OKAY 0
+#define INFO_ERR_IMPL 1 /* incompatable implementation */
+#define INFO_ERR_REQ 2 /* unknown request code */
+#define INFO_ERR_FMT 3 /* format error */
+#define INFO_ERR_NODATA 4 /* no data for this request */
+#define INFO_ERR_AUTH 7 /* authentication failure */
+
+/*
+ * Maximum sequence number.
+ */
+#define MAXSEQ 127
+
+
+/*
+ * Bit setting macros for multifield items.
+ */
+#define RESP_BIT 0x80
+#define MORE_BIT 0x40
+
+#define ISRESPONSE(rm_vn_mode) (((rm_vn_mode)&RESP_BIT)!=0)
+#define ISMORE(rm_vn_mode) (((rm_vn_mode)&MORE_BIT)!=0)
+#define INFO_VERSION(rm_vn_mode) ((u_char)(((rm_vn_mode)>>3)&0x7))
+#define INFO_MODE(rm_vn_mode) ((rm_vn_mode)&0x7)
+
+#define RM_VN_MODE(resp, more) ((u_char)(((resp)?RESP_BIT:0)\
+ |((more)?MORE_BIT:0)\
+ |((NTP_VERSION)<<3)\
+ |(MODE_PRIVATE)))
+
+#define INFO_IS_AUTH(auth_seq) (((auth_seq) & 0x80) != 0)
+#define INFO_SEQ(auth_seq) ((auth_seq)&0x7f)
+#define AUTH_SEQ(auth, seq) ((u_char)((((auth)!=0)?0x80:0)|((seq)&0x7f)))
+
+#define INFO_ERR(err_nitems) ((u_short)((ntohs(err_nitems)>>12)&0xf))
+#define INFO_NITEMS(err_nitems) ((u_short)(ntohs(err_nitems)&0xfff))
+#define ERR_NITEMS(err, nitems) (htons((((u_short)(err)<<12)&0xf000)\
+ |((u_short)(nitems)&0xfff)))
+
+#define INFO_MBZ(mbz_itemsize) ((ntohs(mbz_itemsize)>>12)&0xf)
+#define INFO_ITEMSIZE(mbz_itemsize) (ntohs(mbz_itemsize)&0xfff)
+#define MBZ_ITEMSIZE(itemsize) (htons((u_short)(itemsize)))
+
+
+/*
+ * Implementation numbers. One for universal use and one for xntpd.
+ */
+#define IMPL_UNIV 0
+#define IMPL_XNTPD 2
+
+/*
+ * Some limits related to authentication. Frames which are
+ * authenticated must include a time stamp which differs from
+ * the receive time stamp by no more than 10 seconds.
+ */
+#define INFO_TS_MAXSKEW_UI 10
+
+/*
+ * Universal request codes go here. There aren't any.
+ */
+
+/*
+ * XNTPD request codes go here.
+ */
+#define REQ_PEER_LIST 0 /* return list of peers */
+#define REQ_PEER_LIST_SUM 1 /* return summary info for all peers */
+#define REQ_PEER_INFO 2 /* get standard information on peer */
+#define REQ_PEER_STATS 3 /* get statistics for peer */
+#define REQ_SYS_INFO 4 /* get system information */
+#define REQ_SYS_STATS 5 /* get system stats */
+#define REQ_IO_STATS 6 /* get I/O stats */
+#define REQ_MEM_STATS 7 /* stats related to peer list maint */
+#define REQ_LOOP_INFO 8 /* info from the loop filter */
+#define REQ_TIMER_STATS 9 /* get timer stats */
+#define REQ_CONFIG 10 /* configure a new peer */
+#define REQ_UNCONFIG 11 /* unconfigure an existing peer */
+#define REQ_SET_SYS_FLAG 12 /* set system flags */
+#define REQ_CLR_SYS_FLAG 13 /* clear system flags */
+#define REQ_MONITOR 14 /* monitor clients */
+#define REQ_NOMONITOR 15 /* stop monitoring clients */
+#define REQ_GET_RESTRICT 16 /* return restrict list */
+#define REQ_RESADDFLAGS 17 /* add flags to restrict list */
+#define REQ_RESSUBFLAGS 18 /* remove flags from restrict list */
+#define REQ_UNRESTRICT 19 /* remove entry from restrict list */
+#define REQ_MON_GETLIST 20 /* return data collected by monitor */
+#define REQ_RESET_STATS 21 /* reset stat counters */
+#define REQ_RESET_PEER 22 /* reset peer stat counters */
+#define REQ_REREAD_KEYS 23 /* reread the encryption key file */
+#define REQ_DO_DIRTY_HACK 24 /* historical interest */
+#define REQ_DONT_DIRTY_HACK 25 /* Ibid. */
+#define REQ_TRUSTKEY 26 /* add a trusted key */
+#define REQ_UNTRUSTKEY 27 /* remove a trusted key */
+#define REQ_AUTHINFO 28 /* return authentication info */
+#define REQ_TRAPS 29 /* return currently set traps */
+#define REQ_ADD_TRAP 30 /* add a trap */
+#define REQ_CLR_TRAP 31 /* clear a trap */
+#define REQ_REQUEST_KEY 32 /* define a new request keyid */
+#define REQ_CONTROL_KEY 33 /* define a new control keyid */
+#define REQ_GET_CTLSTATS 34 /* get stats from the control module */
+#define REQ_GET_LEAPINFO 35 /* get leap information */
+#define REQ_GET_CLOCKINFO 36 /* get clock information */
+#define REQ_SET_CLKFUDGE 37 /* set clock fudge factors */
+#define REQ_GET_KERNEL 38 /* get kernel pll/pps information */
+#define REQ_GET_CLKBUGINFO 39 /* get clock debugging info */
+#define REQ_SET_PRECISION 41 /* set clock precision */
+#define REQ_MON_GETLIST_1 42 /* return data collected by monitor v1*/
+
+/*
+ * Flags in the peer information returns
+ */
+#define INFO_FLAG_CONFIG 0x1
+#define INFO_FLAG_SYSPEER 0x2
+#define INFO_FLAG_UNUSED 0x4
+#define INFO_FLAG_REFCLOCK 0x8
+#define INFO_FLAG_PREFER 0x10
+#define INFO_FLAG_AUTHENABLE 0x20
+#define INFO_FLAG_SEL_CANDIDATE 0x40
+#define INFO_FLAG_SHORTLIST 0x80
+
+/*
+ * Flags in the system information returns
+ */
+#define INFO_FLAG_BCLIENT 0x1
+#define INFO_FLAG_AUTHENTICATE 0x2
+#define INFO_FLAG_PLL 0x4
+#define INFO_FLAG_PPS 0x8 /* unused */
+#define INFO_FLAG_PLL_SYNC 0x10
+#define INFO_FLAG_PPS_SYNC 0x20
+#define INFO_FLAG_MONITOR 0x40
+#define INFO_FLAG_FILEGEN 0x80
+
+/*
+ * Peer list structure. Used to return raw lists of peers. It goes
+ * without saying that everything returned is in network byte order.
+ */
+struct info_peer_list {
+ U_LONG address; /* address of peer */
+ u_short port; /* port number of peer */
+ u_char hmode; /* mode for this peer */
+ u_char flags; /* flags (from above) */
+};
+
+
+/*
+ * Peer summary structure. Sort of the info that ntpdc returns by default.
+ */
+struct info_peer_summary {
+ U_LONG dstadr; /* local address (zero for undetermined) */
+ U_LONG srcadr; /* source address */
+ u_short srcport; /* source port */
+ u_char stratum; /* stratum of peer */
+ s_char hpoll; /* host polling interval */
+ s_char ppoll; /* peer polling interval */
+ u_char reach; /* reachability register */
+ u_char flags; /* flags, from above */
+ u_char hmode; /* peer mode */
+ s_fp delay; /* peer.estdelay */
+ l_fp offset; /* peer.estoffset */
+ u_fp dispersion; /* peer.estdisp */
+};
+
+
+/*
+ * Peer information structure.
+ */
+struct info_peer {
+ U_LONG dstadr; /* local address */
+ U_LONG srcadr; /* remote address */
+ u_short srcport; /* remote port */
+ u_char flags; /* peer flags */
+ u_char leap; /* peer.leap */
+ u_char hmode; /* peer.hmode */
+ u_char pmode; /* peer.pmode */
+ u_char stratum; /* peer.stratum */
+ u_char ppoll; /* peer.ppoll */
+ u_char hpoll; /* peer.hpoll */
+ s_char precision; /* peer.precision */
+ u_char version; /* peer.version */
+ u_char valid; /* peer.valid */
+ u_char reach; /* peer.reach */
+ u_char unreach; /* peer.unreach */
+ u_char flash; /* peer.flash */
+ u_char ttl; /* peer.ttl */
+ u_char unused8; /* (obsolete) */
+ u_char unused9;
+ u_short associd; /* association ID */
+ U_LONG keyid; /* auth key in use */
+ U_LONG pkeyid; /* peer.pkeyid */
+ U_LONG refid; /* peer.refid */
+ U_LONG timer; /* peer.timer */
+ s_fp rootdelay; /* peer.distance */
+ u_fp rootdispersion; /* peer.dispersion */
+ l_fp reftime; /* peer.reftime */
+ l_fp org; /* peer.org */
+ l_fp rec; /* peer.rec */
+ l_fp xmt; /* peer.xmt */
+ s_fp filtdelay[NTP_SHIFT]; /* delay shift register */
+ l_fp filtoffset[NTP_SHIFT]; /* offset shift register */
+ u_char order[NTP_SHIFT]; /* order of peers from last filter */
+ s_fp delay; /* peer.estdelay */
+ u_fp dispersion; /* peer.estdisp */
+ l_fp offset; /* peer.estoffset */
+ u_fp selectdisp; /* peer select dispersion */
+ LONG unused1; /* (obsolete) */
+ LONG unused2;
+ LONG unused3;
+ LONG unused4;
+ LONG unused5;
+ LONG unused6;
+ LONG unused7;
+ s_fp estbdelay; /* broadcast offset */
+};
+
+
+/*
+ * Peer statistics structure
+ */
+struct info_peer_stats {
+ U_LONG dstadr; /* local address */
+ U_LONG srcadr; /* remote address */
+ u_short srcport; /* remote port */
+ u_short flags; /* peer flags */
+ U_LONG timereset; /* time counters were reset */
+ U_LONG timereceived; /* time since a packet received */
+ U_LONG timetosend; /* time until a packet sent */
+ U_LONG timereachable; /* time peer has been reachable */
+ U_LONG sent; /* number sent */
+ U_LONG unused1; /* (unused) */
+ U_LONG processed; /* number processed */
+ U_LONG unused2; /* (unused) */
+ U_LONG badauth; /* bad authentication */
+ U_LONG bogusorg; /* bogus origin */
+ U_LONG oldpkt; /* duplicate */
+ U_LONG unused3; /* (unused) */
+ U_LONG unused4; /* (unused) */
+ U_LONG seldisp; /* bad dispersion */
+ U_LONG selbroken; /* bad reference time */
+ U_LONG unused5; /* (unused) */
+ u_char candidate; /* select order */
+ u_char unused6; /* (unused) */
+ u_char unused7; /* (unused) */
+ u_char unused8; /* (unused) */
+};
+
+
+/*
+ * Loop filter variables
+ */
+struct info_loop {
+ l_fp last_offset;
+ l_fp drift_comp;
+ U_LONG compliance;
+ U_LONG watchdog_timer;
+};
+
+
+/*
+ * System info. Mostly the sys.* variables, plus a few unique to
+ * the implementation.
+ */
+struct info_sys {
+ U_LONG peer; /* system peer address */
+ u_char peer_mode; /* mode we are syncing to peer in */
+ u_char leap; /* system leap bits */
+ u_char stratum; /* our stratum */
+ s_char precision; /* local clock precision */
+ s_fp rootdelay; /* distance from sync source */
+ u_fp rootdispersion; /* dispersion from sync source */
+ U_LONG refid; /* reference ID of sync source */
+ l_fp reftime; /* system reference time */
+ U_LONG poll; /* system poll interval */
+ u_char flags; /* system flags */
+ u_char unused1; /* unused */
+ u_char unused2; /* unused */
+ u_char unused3; /* unused */
+ s_fp bdelay; /* default broadcast offset */
+ s_fp frequency; /* frequency residual (scaled ppm) */
+ l_fp authdelay; /* default authentication delay */
+ u_fp stability; /* clock stability (scaled ppm) */
+};
+
+
+/*
+ * System stats. These are collected in the protocol module
+ */
+struct info_sys_stats {
+ U_LONG timeup; /* time we have been up and running */
+ U_LONG timereset; /* time since these were last cleared */
+ U_LONG badstratum; /* packets claiming an invalid stratum */
+ U_LONG oldversionpkt; /* old version packets received */
+ U_LONG newversionpkt; /* new version packets received */
+ U_LONG unknownversion; /* don't know version packets */
+ U_LONG badlength; /* packets with bad length */
+ U_LONG processed; /* packets processed */
+ U_LONG badauth; /* packets dropped because of authorization */
+ U_LONG wanderhold; /* (obsolete) */
+ U_LONG limitrejected; /* rejected because of client limitation */
+};
+
+
+/*
+ * System stats - old version
+ */
+struct old_info_sys_stats {
+ U_LONG timeup; /* time we have been up and running */
+ U_LONG timereset; /* time since these were last cleared */
+ U_LONG badstratum; /* packets claiming an invalid stratum */
+ U_LONG oldversionpkt; /* old version packets received */
+ U_LONG newversionpkt; /* new version packets received */
+ U_LONG unknownversion; /* don't know version packets */
+ U_LONG badlength; /* packets with bad length */
+ U_LONG processed; /* packets processed */
+ U_LONG badauth; /* packets dropped because of authorization */
+ U_LONG wanderhold;
+};
+
+
+/*
+ * Peer memory statistics. Collected in the peer module.
+ */
+struct info_mem_stats {
+ U_LONG timereset; /* time since reset */
+ u_short totalpeermem;
+ u_short freepeermem;
+ U_LONG findpeer_calls;
+ U_LONG allocations;
+ U_LONG demobilizations;
+ u_char hashcount[HASH_SIZE];
+};
+
+
+/*
+ * I/O statistics. Collected in the I/O module
+ */
+struct info_io_stats {
+ U_LONG timereset; /* time since reset */
+ u_short totalrecvbufs; /* total receive bufs */
+ u_short freerecvbufs; /* free buffers */
+ u_short fullrecvbufs; /* full buffers */
+ u_short lowwater; /* number of times we've added buffers */
+ U_LONG dropped; /* dropped packets */
+ U_LONG ignored; /* ignored packets */
+ U_LONG received; /* received packets */
+ U_LONG sent; /* packets sent */
+ U_LONG notsent; /* packets not sent */
+ U_LONG interrupts; /* interrupts we've handled */
+ U_LONG int_received; /* received by interrupt handler */
+};
+
+
+/*
+ * Timer stats. Guess where from.
+ */
+struct info_timer_stats {
+ U_LONG timereset; /* time since reset */
+ U_LONG alarms; /* alarms we've handled */
+ U_LONG overflows; /* timer overflows */
+ U_LONG xmtcalls; /* calls to xmit */
+};
+
+
+/*
+ * Structure for passing peer configuration information
+ */
+struct conf_peer {
+ U_LONG peeraddr; /* address to poll */
+ u_char hmode; /* mode, either broadcast, active or client */
+ u_char version; /* version number to poll with */
+ u_char minpoll; /* min host poll interval */
+ u_char maxpoll; /* max host poll interval */
+ u_char flags; /* flags for this request */
+ u_char ttl; /* time to live (multicast) */
+ u_short unused; /* unused */
+ U_LONG keyid; /* key to use for this association */
+};
+
+#define CONF_FLAG_AUTHENABLE 0x1
+#define CONF_FLAG_PREFER 0x2
+
+/*
+ * Structure for passing peer deletion information. Currently
+ * we only pass the address and delete all configured peers with
+ * this addess.
+ */
+struct conf_unpeer {
+ U_LONG peeraddr; /* address of peer */
+};
+
+/*
+ * Structure for carrying system flags.
+ */
+struct conf_sys_flags {
+ U_LONG flags;
+};
+
+/*
+ * System flags we can set/clear
+ */
+#define SYS_FLAG_BCLIENT 0x1
+#define SYS_FLAG_AUTHENTICATE 0x2
+#define SYS_FLAG_PLL 0x4
+#define SYS_FLAG_PPS 0x8
+#define SYS_FLAG_MONITOR 0x10
+#define SYS_FLAG_FILEGEN 0x20
+
+/*
+ * Structure used for returning restrict entries
+ */
+struct info_restrict {
+ U_LONG addr; /* match address */
+ U_LONG mask; /* match mask */
+ U_LONG count; /* number of packets matched */
+ u_short flags; /* restrict flags */
+ u_short mflags; /* match flags */
+};
+
+
+/*
+ * Structure used for specifying restrict entries
+ */
+struct conf_restrict {
+ U_LONG addr; /* match address */
+ U_LONG mask; /* match mask */
+ u_short flags; /* restrict flags */
+ u_short mflags; /* match flags */
+};
+
+
+/*
+ * Structure used for returning monitor data
+ */
+struct info_monitor_1 {
+ U_LONG lasttime; /* last packet from this host */
+ U_LONG firsttime; /* first time we received a packet */
+ U_LONG lastdrop; /* last time we rejected a packet due to client limitation policy */
+ U_LONG count; /* count of packets received */
+ U_LONG addr; /* host address */
+ U_LONG daddr; /* destination host address */
+ U_LONG flags; /* flags about destination */
+ u_short port; /* port number of last reception */
+ u_char mode; /* mode of last packet */
+ u_char version; /* version number of last packet */
+};
+
+
+/*
+ * Structure used for returning monitor data
+ */
+struct info_monitor {
+ U_LONG lasttime; /* last packet from this host */
+ U_LONG firsttime; /* first time we received a packet */
+ U_LONG lastdrop; /* last time we rejected a packet due to client limitation policy */
+ U_LONG count; /* count of packets received */
+ U_LONG addr; /* host address */
+ u_short port; /* port number of last reception */
+ u_char mode; /* mode of last packet */
+ u_char version; /* version number of last packet */
+};
+
+/*
+ * Structure used for returning monitor data (old format
+ */
+struct old_info_monitor {
+ U_LONG lasttime; /* last packet from this host */
+ U_LONG firsttime; /* first time we received a packet */
+ U_LONG count; /* count of packets received */
+ U_LONG addr; /* host address */
+ u_short port; /* port number of last reception */
+ u_char mode; /* mode of last packet */
+ u_char version; /* version number of last packet */
+};
+
+/*
+ * Structure used for passing indication of flags to clear
+ */
+struct reset_flags {
+ U_LONG flags;
+};
+
+#define RESET_FLAG_ALLPEERS 0x01
+#define RESET_FLAG_IO 0x02
+#define RESET_FLAG_SYS 0x04
+#define RESET_FLAG_MEM 0x08
+#define RESET_FLAG_TIMER 0x10
+#define RESET_FLAG_AUTH 0x20
+#define RESET_FLAG_CTL 0x40
+
+#define RESET_ALLFLAGS \
+ (RESET_FLAG_ALLPEERS|RESET_FLAG_IO|RESET_FLAG_SYS \
+ |RESET_FLAG_MEM|RESET_FLAG_TIMER|RESET_FLAG_AUTH|RESET_FLAG_CTL)
+
+/*
+ * Structure used to return information concerning the authentication
+ * module.
+ */
+struct info_auth {
+ U_LONG timereset; /* time counters were reset */
+ U_LONG numkeys; /* number of keys we know */
+ U_LONG numfreekeys; /* number of free keys */
+ U_LONG keylookups; /* calls to authhavekey() */
+ U_LONG keynotfound; /* requested key unknown */
+ U_LONG encryptions; /* number of encryptions */
+ U_LONG decryptions; /* number of decryptions */
+ U_LONG unused; /* (unused) */
+ U_LONG keyuncached; /* calls to encrypt/decrypt with uncached key */
+};
+
+
+/*
+ * Structure used to pass trap information to the client
+ */
+struct info_trap {
+ U_LONG local_address; /* local interface address */
+ U_LONG trap_address; /* remote client's address */
+ u_short trap_port; /* remote port number */
+ u_short sequence; /* sequence number */
+ U_LONG settime; /* time trap last set */
+ U_LONG origtime; /* time trap originally set */
+ U_LONG resets; /* number of resets on this trap */
+ U_LONG flags; /* trap flags, as defined in ntp_control.h */
+};
+
+/*
+ * Structure used to pass add/clear trap information to the client
+ */
+struct conf_trap {
+ U_LONG local_address; /* local interface address */
+ U_LONG trap_address; /* remote client's address */
+ u_short trap_port; /* remote client's port */
+ u_short unused;
+};
+
+
+/*
+ * Structure used to return statistics from the control module
+ */
+struct info_control {
+ U_LONG ctltimereset;
+ U_LONG numctlreq; /* number of requests we've received */
+ U_LONG numctlbadpkts; /* number of bad control packets */
+ U_LONG numctlresponses; /* # resp packets sent */
+ U_LONG numctlfrags; /* # of fragments sent */
+ U_LONG numctlerrors; /* number of error responses sent */
+ U_LONG numctltooshort; /* number of too short input packets */
+ U_LONG numctlinputresp; /* number of responses on input */
+ U_LONG numctlinputfrag; /* number of fragments on input */
+ U_LONG numctlinputerr; /* # input pkts with err bit set */
+ U_LONG numctlbadoffset; /* # input pkts with nonzero offset */
+ U_LONG numctlbadversion; /* # input pkts with unknown version */
+ U_LONG numctldatatooshort; /* data too short for count */
+ U_LONG numctlbadop; /* bad op code found in packet */
+ U_LONG numasyncmsgs; /* # async messages we've sent */
+};
+
+
+/*
+ * Structure used to return leap information.
+ */
+struct info_leap {
+ u_char sys_leap; /* current sys_leap */
+ u_char leap_indicator; /* current leap indicator */
+ u_char leap_warning; /* current leap warning */
+ u_char leap_bits; /* leap flags */
+ U_LONG leap_timer; /* seconds to next interrupt */
+ U_LONG leap_processcalls; /* calls to the leap process */
+ U_LONG leap_notclose; /* found leap was not close */
+ U_LONG leap_monthofleap; /* in month of leap */
+ U_LONG leap_dayofleap; /* in day of leap */
+ U_LONG leap_hoursfromleap; /* leap within two hours */
+ U_LONG leap_happened; /* leap second happened */
+};
+
+#define INFO_LEAP_MASK 0x3 /* flag for leap_bits */
+#define INFO_LEAP_SEENSTRATUM1 0x4 /* server has seen stratum 1 */
+#define INFO_LEAP_OVERRIDE 0x8 /* server will override the leap information */
+
+/*
+ * Structure used to return clock information
+ */
+struct info_clock {
+ U_LONG clockadr;
+ u_char type;
+ u_char flags;
+ u_char lastevent;
+ u_char currentstatus;
+ U_LONG polls;
+ U_LONG noresponse;
+ U_LONG badformat;
+ U_LONG baddata;
+ U_LONG timestarted;
+ l_fp fudgetime1;
+ l_fp fudgetime2;
+ LONG fudgeval1;
+ LONG fudgeval2;
+};
+
+
+/*
+ * Structure used for setting clock fudge factors
+ */
+struct conf_fudge {
+ U_LONG clockadr;
+ U_LONG which;
+ l_fp fudgetime;
+ LONG fudgeval_flags;
+};
+
+#define FUDGE_TIME1 1
+#define FUDGE_TIME2 2
+#define FUDGE_VAL1 3
+#define FUDGE_VAL2 4
+#define FUDGE_FLAGS 5
+
+
+/*
+ * Structure used for returning clock debugging info
+ */
+#define NUMCBUGVALUES 16
+#define NUMCBUGTIMES 32
+
+struct info_clkbug {
+ U_LONG clockadr;
+ u_char nvalues;
+ u_char ntimes;
+ u_short svalues;
+ U_LONG stimes;
+ U_LONG values[NUMCBUGVALUES];
+ l_fp times[NUMCBUGTIMES];
+};
+
+/*
+ * Structure used for returning kernel pll/PPS information
+ */
+struct info_kernel {
+ LONG offset;
+ LONG freq;
+ LONG maxerror;
+ LONG esterror;
+ u_short status;
+ u_short shift;
+ LONG constant;
+ LONG precision;
+ LONG tolerance;
+
+/*
+ * Variables used only if PPS signal discipline is implemented
+ */
+ LONG ppsfreq;
+ LONG jitter;
+ LONG stabil;
+ LONG jitcnt;
+ LONG calcnt;
+ LONG errcnt;
+ LONG stbcnt;
+};
diff --git a/usr.sbin/xntpd/include/ntp_select.h b/usr.sbin/xntpd/include/ntp_select.h
new file mode 100644
index 0000000..14e498d
--- /dev/null
+++ b/usr.sbin/xntpd/include/ntp_select.h
@@ -0,0 +1,20 @@
+/*
+ * Not all machines define FD_SET in sys/types.h
+ */
+#ifndef _ntp_select_h
+#define _ntp_select_h
+
+#if (defined(RS6000)||defined(SYS_PTX))&&!defined(_BSD)
+#include <sys/select.h>
+#endif
+
+#ifndef FD_SET
+#define NFDBITS 32
+#define FD_SETSIZE 32
+#define FD_SET(n, p) ((p)->fds_bits[(n)/NFDBITS] |= (1 << ((n) % NFDBITS)))
+#define FD_CLR(n, p) ((p)->fds_bits[(n)/NFDBITS] &= ~(1 << ((n) % NFDBITS)))
+#define FD_ISSET(n, p) ((p)->fds_bits[(n)/NFDBITS] & (1 << ((n) % NFDBITS)))
+#define FD_ZERO(p) memset((char *)(p), 0, sizeof(*(p)))
+#endif
+
+#endif /* _ntp_select_h */
diff --git a/usr.sbin/xntpd/include/ntp_stdlib.h b/usr.sbin/xntpd/include/ntp_stdlib.h
new file mode 100644
index 0000000..646ce60
--- /dev/null
+++ b/usr.sbin/xntpd/include/ntp_stdlib.h
@@ -0,0 +1,93 @@
+/*
+ * ntp_stdlib.h - Prototypes for XNTP lib.
+ */
+#include <sys/types.h>
+
+#include "ntp_types.h"
+#include "ntp_string.h"
+#include "l_stdlib.h"
+
+#ifndef P
+#if defined(__STDC__) || defined(USE_PROTOTYPES)
+#define P(x) x
+#else
+#define P(x) ()
+#if !defined(const)
+#define const
+#endif
+#endif
+#endif
+
+#if defined(__STDC__)
+extern void msyslog P((int, char *, ...));
+#else
+extern void msyslog P(());
+#endif
+
+extern void auth_des P((u_long *, u_char *));
+extern void auth_delkeys P((void));
+extern int auth_havekey P((u_long));
+extern int auth_parity P((u_long *));
+extern void auth_setkey P((u_long, u_long *));
+extern void auth_subkeys P((u_long *, u_char *, u_char *));
+extern int authistrusted P((u_long));
+extern int authusekey P((u_long, int, const char *));
+
+extern void auth_delkeys P((void));
+
+extern void auth1crypt P((u_long, U_LONG *, int));
+extern int auth2crypt P((u_long, U_LONG *, int));
+extern int authdecrypt P((u_long, U_LONG *, int));
+extern int authencrypt P((u_long, U_LONG *, int));
+extern int authhavekey P((u_long));
+extern int authreadkeys P((const char *));
+extern void authtrust P((u_long, int));
+extern void calleapwhen P((u_long, u_long *, u_long *));
+extern u_long calyearstart P((u_long));
+extern const char *clockname P((int));
+extern int clocktime P((int, int, int, int, int, u_long, u_long *, U_LONG *));
+extern char * emalloc P((u_int));
+extern int ntp_getopt P((int, char **, char *));
+extern void init_auth P((void));
+extern void init_lib P((void));
+extern void init_random P((void));
+
+#ifdef DES
+extern void DESauth1crypt P((u_long, U_LONG *, int));
+extern int DESauth2crypt P((u_long, U_LONG *, int));
+extern int DESauthdecrypt P((u_long, const U_LONG *, int));
+extern int DESauthencrypt P((u_long, U_LONG *, int));
+extern void DESauth_setkey P((u_long, const U_LONG *));
+extern void DESauth_subkeys P((const U_LONG *, u_char *, u_char *));
+extern void DESauth_des P((U_LONG *, u_char *));
+extern int DESauth_parity P((U_LONG *));
+#endif /* DES */
+
+#ifdef MD5
+extern void MD5auth1crypt P((u_long, U_LONG *, int));
+extern int MD5auth2crypt P((u_long, U_LONG *, int));
+extern int MD5authdecrypt P((u_long, const U_LONG *, int));
+extern int MD5authencrypt P((u_long, U_LONG *, int));
+extern void MD5auth_setkey P((u_long, const U_LONG *));
+#endif /* MD5 */
+
+extern int atoint P((const char *, long *));
+extern int atouint P((const char *, u_long *));
+extern int hextoint P((const char *, u_long *));
+extern char * humandate P((u_long));
+extern char * inttoa P((long));
+extern char * mfptoa P((u_long, u_long, int));
+extern char * mfptoms P((u_long, u_long, int));
+extern char * modetoa P((int));
+extern u_long netof P((u_long));
+extern char * numtoa P((u_long));
+extern char * numtohost P((u_long));
+extern int octtoint P((const char *, u_long *));
+extern u_long ranp2 P((int));
+extern char * refnumtoa P((u_long));
+extern int tsftomsu P((u_long, int));
+extern char * uinttoa P((u_long));
+
+extern int decodenetnum P((const char *, u_long *));
+
+extern RETSIGTYPE signal_no_reset P((int, RETSIGTYPE (*func)()));
diff --git a/usr.sbin/xntpd/include/ntp_string.h b/usr.sbin/xntpd/include/ntp_string.h
new file mode 100644
index 0000000..90a29da
--- /dev/null
+++ b/usr.sbin/xntpd/include/ntp_string.h
@@ -0,0 +1,35 @@
+/*
+ * Define string ops: strchr strrchr memcmp memmove memset
+ */
+
+#ifndef _ntp_string_h
+#define _ntp_string_h
+
+#if defined(NTP_POSIX_SOURCE)
+
+# if defined(HAVE_MEMORY_H)
+# include <memory.h>
+# endif
+
+# include <string.h>
+
+#else
+
+# include <strings.h>
+# define strchr(s,c) index(s,c)
+# define strrchr(s,c) rindex(s,c)
+# ifndef NTP_NEED_BOPS
+# define NTP_NEED_BOPS
+# endif
+#endif /* NTP_POSIX_SOURCE */
+
+#ifdef NTP_NEED_BOPS
+
+# define memcmp(a,b,c) bcmp(a,b,c)
+# define memmove(t,f,c) bcopy(f,t,c)
+# define memset(a,x,c) if (x == 0x00) bzero(a,c); else ntp_memset((char*)a,x,c)
+void ntp_memset P((char *, int, int));
+
+#endif /* NTP_NEED_BOPS */
+
+#endif /* _ntp_string_h */
diff --git a/usr.sbin/xntpd/include/ntp_syslog.h b/usr.sbin/xntpd/include/ntp_syslog.h
new file mode 100644
index 0000000..38b847b
--- /dev/null
+++ b/usr.sbin/xntpd/include/ntp_syslog.h
@@ -0,0 +1,15 @@
+/*
+ * A hack for platforms which require specially built syslog facilities
+ */
+#ifdef GIZMO
+#include "gizmo_syslog.h"
+#else /* !GIZMO */
+#include <syslog.h>
+#ifdef SYSLOG_FILE
+#include <stdio.h>
+#endif
+#endif /* GIZMO */
+#ifdef SYSLOG_FILE
+extern FILE *syslog_file;
+#define syslog msyslog
+#endif
diff --git a/usr.sbin/xntpd/include/ntp_timex.h b/usr.sbin/xntpd/include/ntp_timex.h
new file mode 100644
index 0000000..cb8396a
--- /dev/null
+++ b/usr.sbin/xntpd/include/ntp_timex.h
@@ -0,0 +1,273 @@
+/******************************************************************************
+ * *
+ * Copyright (c) David L. Mills 1993, 1994 *
+ * *
+ * Permission to use, copy, modify, and distribute this software and its *
+ * documentation for any purpose and without fee is hereby granted, provided *
+ * that the above copyright notice appears in all copies and that both the *
+ * copyright notice and this permission notice appear in supporting *
+ * documentation, and that the name University of Delaware not be used in *
+ * advertising or publicity pertaining to distribution of the software *
+ * without specific, written prior permission. The University of Delaware *
+ * makes no representations about the suitability this software for any *
+ * purpose. It is provided "as is" without express or implied warranty. *
+ * *
+ ******************************************************************************/
+
+/*
+ * Modification history timex.h
+ *
+ * 19 Mar 94 David L. Mills
+ * Moved defines from kernel routines to header file and added new
+ * defines for PPS phase-lock loop.
+ *
+ * 20 Feb 94 David L. Mills
+ * Revised status codes and structures for external clock and PPS
+ * signal discipline.
+ *
+ * 28 Nov 93 David L. Mills
+ * Adjusted parameters to improve stability and increase poll
+ * interval.
+ *
+ * 17 Sep 93 David L. Mills
+ * Created file
+ */
+/*
+ * This header file defines the Network Time Protocol (NTP) interfaces
+ * for user and daemon application programs. These are implemented using
+ * private syscalls and data structures and require specific kernel
+ * support.
+ *
+ * NAME
+ * ntp_gettime - NTP user application interface
+ *
+ * SYNOPSIS
+ * #include <sys/timex.h>
+ *
+ * int syscall(SYS_ntp_gettime, tptr)
+ *
+ * int SYS_ntp_gettime defined in syscall.h header file
+ * struct ntptimeval *tptr pointer to ntptimeval structure
+ *
+ * NAME
+ * ntp_adjtime - NTP daemon application interface
+ *
+ * SYNOPSIS
+ * #include <sys/timex.h>
+ *
+ * int syscall(SYS_ntp_adjtime, mode, tptr)
+ *
+ * int SYS_ntp_adjtime defined in syscall.h header file
+ * struct timex *tptr pointer to timex structure
+ *
+ */
+#ifndef MSDOS /* Microsoft specific */
+#include <sys/syscall.h>
+#endif /* MSDOS */
+
+/*
+ * The following defines establish the engineering parameters of the
+ * phase-lock loop (PLL) model used in the kernel implementation. These
+ * parameters have been carefully chosen by analysis for good stability
+ * and wide dynamic range.
+ *
+ * The hz variable is defined in the kernel build environment. It
+ * establishes the timer interrupt frequency, 100 Hz for the SunOS
+ * kernel, 256 Hz for the Ultrix kernel and 1024 Hz for the OSF/1
+ * kernel. SHIFT_HZ expresses the same value as the nearest power of two
+ * in order to avoid hardware multiply operations.
+ *
+ * SHIFT_KG and SHIFT_KF establish the damping of the PLL and are chosen
+ * for a slightly underdamped convergence characteristic.
+ *
+ * MAXTC establishes the maximum time constant of the PLL. With the
+ * SHIFT_KG and SHIFT_KF values given and a time constant range from
+ * zero to MAXTC, the PLL will converge in 15 minutes to 16 hours,
+ * respectively.
+ */
+#define SHIFT_HZ 7 /* log2(hz) */
+#define SHIFT_KG 6 /* phase factor (shift) */
+#define SHIFT_KF 16 /* frequency factor (shift) */
+#define MAXTC 6 /* maximum time constant (shift) */
+
+/*
+ * The following defines establish the scaling of the various variables
+ * used by the PLL. They are chosen to allow the greatest precision
+ * possible without overflow of a 32-bit word.
+ *
+ * SHIFT_SCALE defines the scaling (shift) of the time_phase variable,
+ * which serves as a an extension to the low-order bits of the system
+ * clock variable time.tv_usec.
+ *
+ * SHIFT_UPDATE defines the scaling (shift) of the time_offset variable,
+ * which represents the current time offset with respect to standard
+ * time.
+ *
+ * SHIFT_USEC defines the scaling (shift) of the time_freq and
+ * time_tolerance variables, which represent the current frequency
+ * offset and maximum frequency tolerance.
+ *
+ * FINEUSEC is 1 us in SHIFT_UPDATE units of the time_phase variable.
+ */
+#define SHIFT_SCALE 23 /* phase scale (shift) */
+#define SHIFT_UPDATE (SHIFT_KG + MAXTC) /* time offset scale (shift) */
+#define SHIFT_USEC 16 /* frequency offset scale (shift) */
+#define FINEUSEC (1L << SHIFT_SCALE) /* 1 us in phase units */
+
+/*
+ * The following defines establish the performance envelope of the PLL.
+ * They insure it operates within predefined limits, in order to satisfy
+ * correctness assertions. An excursion which exceeds these bounds is
+ * clamped to the bound and operation proceeds accordingly. In practice,
+ * this can occur only if something has failed or is operating out of
+ * tolerance, but otherwise the PLL continues to operate in a stable
+ * mode.
+ *
+ * MAXPHASE must be set greater than or equal to CLOCK.MAX (128 ms), as
+ * defined in the NTP specification. CLOCK.MAX establishes the maximum
+ * time offset allowed before the system time is reset, rather than
+ * incrementally adjusted. Here, the maximum offset is clamped to
+ * MAXPHASE only in order to prevent overflow errors due to defective
+ * protocol implementations.
+ *
+ * MAXFREQ is the maximum frequency tolerance of the CPU clock
+ * oscillator plus the maximum slew rate allowed by the protocol. It
+ * should be set to at least the frequency tolerance of the oscillator
+ * plus 100 ppm for vernier frequency adjustments. If the kernel
+ * PPS discipline code is configured (PPS_SYNC), the oscillator time and
+ * frequency are disciplined to an external source, presumably with
+ * negligible time and frequency error relative to UTC, and MAXFREQ can
+ * be reduced.
+ *
+ * MAXTIME is the maximum jitter tolerance of the PPS signal if the
+ * kernel PPS discipline code is configured (PPS_SYNC).
+ *
+ * MINSEC and MAXSEC define the lower and upper bounds on the interval
+ * between protocol updates.
+ */
+#define MAXPHASE 128000L /* max phase error (us) */
+#ifdef PPS_SYNC
+#define MAXFREQ (100L << SHIFT_USEC) /* max freq error (100 ppm) */
+#define MAXTIME (200L << PPS_AVG) /* max PPS error (jitter) (200 us) */
+#else
+#define MAXFREQ (200L << SHIFT_USEC) /* max freq error (200 ppm) */
+#endif /* PPS_SYNC */
+#define MINSEC 16L /* min interval between updates (s) */
+#define MAXSEC 1200L /* max interval between updates (s) */
+
+#ifdef PPS_SYNC
+/*
+ * The following defines are used only if a pulse-per-second (PPS)
+ * signal is available and connected via a modem control lead, such as
+ * produced by the optional ppsclock feature incorporated in the Sun
+ * asynch driver. They establish the design parameters of the frequency-
+ * lock loop used to discipline the CPU clock oscillator to the PPS
+ * signal.
+ *
+ * PPS_AVG is the averaging factor for the frequency loop, as well as
+ * the time and frequency dispersion.
+ *
+ * PPS_SHIFT and PPS_SHIFTMAX specify the minimum and maximum
+ * calibration intervals, respectively, in seconds as a power of two.
+ *
+ * PPS_VALID is the maximum interval before the PPS signal is considered
+ * invalid and protocol updates used directly instead.
+ *
+ * MAXGLITCH is the maximum interval before a time offset of more than
+ * MAXTIME is believed.
+ */
+#define PPS_AVG 2 /* pps averaging constant (shift) */
+#define PPS_SHIFT 2 /* min interval duration (s) (shift) */
+#define PPS_SHIFTMAX 8 /* max interval duration (s) (shift) */
+#define PPS_VALID 120 /* pps signal watchdog max (s) */
+#define MAXGLITCH 30 /* pps signal glitch max (s) */
+#endif /* PPS_SYNC */
+
+/*
+ * The following defines and structures define the user interface for
+ * the ntp_gettime() and ntp_adjtime() system calls.
+ *
+ * Control mode codes (timex.modes)
+ */
+#define MOD_OFFSET 0x0001 /* set time offset */
+#define MOD_FREQUENCY 0x0002 /* set frequency offset */
+#define MOD_MAXERROR 0x0004 /* set maximum time error */
+#define MOD_ESTERROR 0x0008 /* set estimated time error */
+#define MOD_STATUS 0x0010 /* set clock status bits */
+#define MOD_TIMECONST 0x0020 /* set pll time constant */
+#define MOD_CLKB 0x4000 /* set clock B */
+#define MOD_CLKA 0x8000 /* set clock A */
+
+/*
+ * Status codes (timex.status)
+ */
+#define STA_PLL 0x0001 /* enable PLL updates (rw) */
+#define STA_PPSFREQ 0x0002 /* enable PPS freq discipline (rw) */
+#define STA_PPSTIME 0x0004 /* enable PPS time discipline (rw) */
+
+#define STA_INS 0x0010 /* insert leap (rw) */
+#define STA_DEL 0x0020 /* delete leap (rw) */
+#define STA_UNSYNC 0x0040 /* clock unsynchronized (rw) */
+
+#define STA_PPSSIGNAL 0x0100 /* PPS signal present (ro) */
+#define STA_PPSJITTER 0x0200 /* PPS signal jitter exceeded (ro) */
+#define STA_PPSWANDER 0x0400 /* PPS signal wander exceeded (ro) */
+#define STA_PPSERROR 0x0800 /* PPS signal calibration error (ro) */
+
+#define STA_CLOCKERR 0x1000 /* clock hardware fault (ro) */
+
+#define STA_RONLY (STA_PPSSIGNAL | STA_PPSJITTER | STA_PPSWANDER | \
+ STA_PPSERROR | STA_CLOCKERR) /* read-only bits */
+
+/*
+ * Clock states (time_state)
+ */
+#define TIME_OK 0 /* no leap second warning */
+#define TIME_INS 1 /* insert leap second warning */
+#define TIME_DEL 2 /* delete leap second warning */
+#define TIME_OOP 3 /* leap second in progress */
+#define TIME_WAIT 4 /* leap second has occured */
+#define TIME_ERROR 5 /* clock not synchronized */
+
+/*
+ * NTP user interface (ntp_gettime()) - used to read kernel clock values
+ *
+ * Note: maximum error = NTP synch distance = dispersion + delay / 2;
+ * estimated error = NTP dispersion.
+ */
+struct ntptimeval {
+ struct timeval time; /* current time (ro) */
+ LONG maxerror; /* maximum error (us) (ro) */
+ LONG esterror; /* estimated error (us) (ro) */
+};
+
+/*
+ * NTP daemon interface - (ntp_adjtime()) used to discipline CPU clock
+ * oscillator
+ */
+struct timex {
+ unsigned int modes; /* clock mode bits (wo) */
+ LONG offset; /* time offset (us) (rw) */
+ LONG freq; /* frequency offset (scaled ppm) (rw) */
+ LONG maxerror; /* maximum error (us) (rw) */
+ LONG esterror; /* estimated error (us) (rw) */
+ int status; /* clock status bits (rw) */
+ LONG constant; /* pll time constant (rw) */
+ LONG precision; /* clock precision (us) (ro) */
+ LONG tolerance; /* clock frequency tolerance (scaled
+ * ppm) (ro) */
+ /*
+ * The following read-only structure members are implemented
+ * only if the PPS signal discipline is configured in the
+ * kernel.
+ */
+ LONG ppsfreq; /* pps frequency (scaled ppm) (ro) */
+ LONG jitter; /* pps jitter (us) (ro) */
+ int shift; /* interval duration (s) (shift) (ro) */
+ LONG stabil; /* pps stability (scaled ppm) (ro) */
+ LONG jitcnt; /* jitter limit exceeded (ro) */
+ LONG calcnt; /* calibration intervals (ro) */
+ LONG errcnt; /* calibration errors (ro) */
+ LONG stbcnt; /* stability limit exceeded (ro) */
+
+};
diff --git a/usr.sbin/xntpd/include/ntp_types.h b/usr.sbin/xntpd/include/ntp_types.h
new file mode 100644
index 0000000..bbcea1b
--- /dev/null
+++ b/usr.sbin/xntpd/include/ntp_types.h
@@ -0,0 +1,60 @@
+/*
+ * ntp_types.h - defines how LONG and U_LONG are treated. For 64 bit systems
+ * like the DEC Alpha, they has to be defined as int and u_int. for 32 bit
+ * systems, define them as long and u_long
+ */
+#include "ntp_machine.h"
+
+#ifndef _NTP_TYPES_
+#define _NTP_TYPES_
+
+/*
+ * This is another naming conflict.
+ * On NetBSD for MAC the macro "mac" is defined as 1
+ * this is fun for a as a paket structure contains an
+ * optional "mac" member - severe confusion results 8-)
+ * As we hopefully do not have to rely on that macro we
+ * just undefine that.
+ */
+#ifdef mac
+#undef mac
+#endif
+
+/*
+ * Set up for prototyping
+ */
+#ifndef P
+#if defined(__STDC__) || defined(USE_PROTOTYPES)
+#define P(x) x
+#else /* __STDC__ USE_PROTOTYPES */
+#define P(x) ()
+#if !defined(const)
+#define const
+#endif /* const */
+#endif /* __STDC__ USE_PROTOTYPES */
+#endif /* P */
+
+/*
+ * DEC Alpha systems need LONG and U_LONG defined as int and u_int
+ */
+#ifdef __alpha
+#ifndef LONG
+#define LONG int
+#endif /* LONG */
+#ifndef U_LONG
+#define U_LONG u_int
+#endif /* U_LONG */
+/*
+ * All other systems fall into this part
+ */
+#else /* __alpha */
+#ifndef LONG
+#define LONG long
+#endif /* LONG */
+#ifndef U_LONG
+#define U_LONG u_long
+#endif /* U_LONG */
+#endif /* __ alplha */
+
+#endif /* _NTP_TYPES_ */
+
diff --git a/usr.sbin/xntpd/include/ntp_unixtime.h b/usr.sbin/xntpd/include/ntp_unixtime.h
new file mode 100644
index 0000000..8007a06
--- /dev/null
+++ b/usr.sbin/xntpd/include/ntp_unixtime.h
@@ -0,0 +1,119 @@
+/*
+ * ntp_unixtime.h - contains constants and macros for converting between
+ * NTP time stamps (l_fp) and Unix times (struct timeval)
+ */
+
+#include "ntp_types.h"
+#include <sys/time.h>
+
+/* gettimeofday() takes two args in BSD and only one in SYSV */
+#ifdef SYSV_TIMEOFDAY
+# define GETTIMEOFDAY(a, b) (gettimeofday(a))
+# define SETTIMEOFDAY(a, b) (settimeofday(a))
+#else /* ! SYSV_TIMEOFDAY */
+# define GETTIMEOFDAY(a, b) (gettimeofday(a, b))
+# define SETTIMEOFDAY(a, b) (settimeofday(a, b))
+#endif /* SYSV_TIMEOFDAY */
+
+/*
+ * Time of day conversion constant. Ntp's time scale starts in 1900,
+ * Unix in 1970.
+ */
+#define JAN_1970 0x83aa7e80 /* 2208988800 1970 - 1900 in seconds */
+
+/*
+ * These constants are used to round the time stamps computed from
+ * a struct timeval to the microsecond (more or less). This keeps
+ * things neat.
+ */
+#define TS_MASK 0xfffff000 /* mask to usec, for time stamps */
+#define TS_ROUNDBIT 0x00000800 /* round at this bit */
+
+
+/*
+ * Convert usec to a time stamp fraction. If you use this the program
+ * must include the following declarations:
+ */
+extern u_long ustotslo[];
+extern u_long ustotsmid[];
+extern u_long ustotshi[];
+
+#define TVUTOTSF(tvu, tsf) \
+ (tsf) = ustotslo[(tvu) & 0xff] \
+ + ustotsmid[((tvu) >> 8) & 0xff] \
+ + ustotshi[((tvu) >> 16) & 0xf]
+
+/*
+ * Convert a struct timeval to a time stamp.
+ */
+#define TVTOTS(tv, ts) \
+ do { \
+ (ts)->l_ui = (u_long)(tv)->tv_sec; \
+ TVUTOTSF((tv)->tv_usec, (ts)->l_uf); \
+ } while(0)
+
+#define sTVTOTS(tv, ts) \
+ do { \
+ int isneg = 0; \
+ long usec; \
+ (ts)->l_ui = (tv)->tv_sec; \
+ usec = (tv)->tv_usec; \
+ if (((tv)->tv_sec < 0) || ((tv)->tv_usec < 0)) { \
+ usec = -usec; \
+ (ts)->l_ui = -(ts)->l_ui; \
+ isneg = 1; \
+ } \
+ TVUTOTSF(usec, (ts)->l_uf); \
+ if (isneg) { \
+ L_NEG((ts)); \
+ } \
+ } while(0)
+
+/*
+ * TV_SHIFT is used to turn the table result into a usec value. To round,
+ * add in TV_ROUNDBIT before shifting
+ */
+#define TV_SHIFT 3
+#define TV_ROUNDBIT 0x4
+
+
+/*
+ * Convert a time stamp fraction to microseconds. The time stamp
+ * fraction is assumed to be unsigned. To use this in a program, declare:
+ */
+extern long tstouslo[];
+extern long tstousmid[];
+extern long tstoushi[];
+
+#define TSFTOTVU(tsf, tvu) \
+ (tvu) = (tstoushi[((tsf) >> 24) & 0xff] \
+ + tstousmid[((tsf) >> 16) & 0xff] \
+ + tstouslo[((tsf) >> 9) & 0x7f] \
+ + TV_ROUNDBIT) >> TV_SHIFT
+/*
+ * Convert a time stamp to a struct timeval. The time stamp
+ * has to be positive.
+ */
+#define TSTOTV(ts, tv) \
+ do { \
+ (tv)->tv_sec = (ts)->l_ui; \
+ TSFTOTVU((ts)->l_uf, (tv)->tv_usec); \
+ if ((tv)->tv_usec == 1000000) { \
+ (tv)->tv_sec++; \
+ (tv)->tv_usec = 0; \
+ } \
+ } while (0)
+
+/*
+ * Convert milliseconds to a time stamp fraction. This shouldn't be
+ * here, but it is convenient since the guys who use the definition will
+ * often be including this file anyway.
+ */
+extern u_long msutotsflo[];
+extern u_long msutotsfhi[];
+
+#define MSUTOTSF(msu, tsf) \
+ (tsf) = msutotsfhi[((msu) >> 5) & 0x1f] + msutotsflo[(msu) & 0x1f]
+
+extern char * tvtoa P((const struct timeval *));
+extern char * utvtoa P((const struct timeval *));
diff --git a/usr.sbin/xntpd/include/ntpd.h b/usr.sbin/xntpd/include/ntpd.h
new file mode 100644
index 0000000..53e3a91
--- /dev/null
+++ b/usr.sbin/xntpd/include/ntpd.h
@@ -0,0 +1,175 @@
+/*
+ * ntpd.h - Prototypes for xntpd.
+ */
+
+#include "ntp_syslog.h"
+#include "ntp_fp.h"
+#include "ntp.h"
+#include "ntp_malloc.h"
+
+/* ntp_config.c */
+extern void getstartup P((int, char **));
+extern void getconfig P((int, char **));
+
+/* ntp_config.c */
+extern void ctl_clr_stats P((void));
+extern int ctlclrtrap P((struct sockaddr_in *, struct interface *, int));
+extern u_short ctlpeerstatus P((struct peer *));
+extern int ctlsettrap P((struct sockaddr_in *, struct interface *, int, int));
+extern u_short ctlsysstatus P((void));
+extern void init_control P((void));
+extern void process_control P((struct recvbuf *, int));
+extern void report_event P((int, struct peer *));
+
+/* ntp_control.c */
+/*
+ * Structure for translation tables between internal system
+ * variable indices and text format.
+ */
+struct ctl_var {
+ u_short code;
+ u_short flags;
+ char *text;
+};
+/*
+ * Flag values
+ */
+#define CAN_READ 0x01
+#define CAN_WRITE 0x02
+
+#define DEF 0x20
+#define PADDING 0x40
+#define EOV 0x80
+
+#define RO (CAN_READ)
+#define WO (CAN_WRITE)
+#define RW (CAN_READ|CAN_WRITE)
+
+extern char * add_var P((struct ctl_var **, unsigned long, int));
+extern void free_varlist P((struct ctl_var *));
+extern void set_var P((struct ctl_var **, char *, unsigned long, int));
+extern void set_sys_var P((char *, unsigned long, int));
+
+/* ntp_intres.c */
+extern void ntp_intres P((void));
+
+/* ntp_io.c */
+extern struct interface *findbcastinter P((struct sockaddr_in *));
+extern struct interface *findinterface P((struct sockaddr_in *));
+extern void freerecvbuf P((struct recvbuf *));
+extern struct recvbuf *getrecvbufs P((void));
+extern void init_io P((void));
+extern void input_handler P((l_fp *));
+extern void io_clr_stats P((void));
+extern void io_setbclient P((void));
+extern void io_unsetbclient P((void));
+extern void io_multicast_add P((u_long));
+extern void io_multicast_del P((u_long));
+
+extern void sendpkt P((struct sockaddr_in *, struct interface *, int, struct pkt *, int));
+#ifdef HAVE_SIGNALED_IO
+extern void wait_for_signal P((void));
+extern void unblock_io_and_alarm P((void));
+extern void block_io_and_alarm P((void));
+#endif
+
+/* ntp_leap.c */
+extern void init_leap P((void));
+extern void leap_process P((void));
+extern int leap_setleap P((int, int));
+/*
+ * there seems to be a bug in the IRIX 4 compiler which prevents
+ * u_char from beeing used in prototyped functions.
+ * This is also true AIX compiler.
+ * So give up and define it to be int. WLJ
+ */
+extern int leap_actual P((int));
+
+/* ntp_loopfilter.c */
+extern void init_loopfilter P((void));
+extern int local_clock P((l_fp *, struct peer *));
+extern void adj_frequency P((s_fp));
+extern void adj_host_clock P((void));
+extern void loop_config P((int, l_fp *, int));
+#if defined(PPS) || defined(PPSPPS) || defined(PPSCD)
+extern int pps_sample P((l_fp *));
+#endif /* PPS || PPSDEV || PPSCD */
+
+/* ntp_monitor.c */
+extern void init_mon P((void));
+extern void mon_start P((int));
+extern void mon_stop P((int));
+extern void monitor P((struct recvbuf *));
+
+/* ntp_peer.c */
+extern void init_peer P((void));
+extern struct peer *findexistingpeer P((struct sockaddr_in *, struct peer *));
+extern struct peer *findpeer P((struct sockaddr_in *, struct interface *, int));
+extern struct peer *findpeerbyassoc P((int));
+extern struct peer *newpeer P((struct sockaddr_in *, struct interface *, int, int, int, int, int, u_long));
+extern void peer_all_reset P((void));
+extern void peer_clr_stats P((void));
+extern struct peer *peer_config P((struct sockaddr_in *, struct interface *, int, int, int, int, int, int, u_long));
+extern void peer_reset P((struct peer *));
+extern int peer_unconfig P((struct sockaddr_in *, struct interface *));
+extern void unpeer P((struct peer *));
+
+/* ntp_proto.c */
+extern void transmit P((struct peer *));
+extern void receive P((struct recvbuf *));
+extern void peer_clear P((struct peer *));
+extern int process_packet P((struct peer *, struct pkt *, l_fp *, int, int));
+extern void clock_update P((struct peer *));
+
+/*
+ * there seems to be a bug in the IRIX 4 compiler which prevents
+ * u_char from beeing used in prototyped functions.
+ * This is also true AIX compiler.
+ * So give up and define it to be int. WLJ
+ */
+extern void poll_update P((struct peer *, unsigned int, int));
+
+extern void clear P((struct peer *));
+extern void clock_filter P((struct peer *, l_fp *, s_fp, u_fp));
+extern void clock_select P((void));
+extern void clock_combine P((struct peer **, int));
+extern void fast_xmit P((struct recvbuf *, int, int));
+extern void init_proto P((void));
+extern void proto_config P((int, u_long));
+extern void proto_clr_stats P((void));
+
+#ifdef REFCLOCK
+/* ntp_refclock.c */
+extern int refclock_newpeer P((struct peer *));
+extern void refclock_unpeer P((struct peer *));
+extern void refclock_receive P((struct peer *, l_fp *, s_fp, u_fp, l_fp *, l_fp *, int));
+extern void refclock_leap P((void));
+extern void init_refclock P((void));
+#endif /* REFCLOCK */
+
+/* ntp_request.c */
+extern void init_request P((void));
+extern void process_private P((struct recvbuf *, int));
+
+/* ntp_restrict.c */
+extern void init_restrict P((void));
+extern int restrictions P((struct sockaddr_in *));
+extern void restrict P((int, struct sockaddr_in *, struct sockaddr_in *, int, int));
+
+/* ntp_timer.c */
+extern void init_timer P((void));
+extern void timer P((void));
+extern void timer_clr_stats P((void));
+
+/* ntp_unixclock.c */
+extern void init_systime P((void));
+
+/* ntp_util.c */
+extern void init_util P((void));
+extern void hourly_stats P((void));
+extern void stats_config P((int, char *));
+extern void record_peer_stats P((struct sockaddr_in *, int, l_fp *, s_fp, u_fp));
+extern void record_loop_stats P((l_fp *, s_fp, int));
+extern void record_clock_stats P((struct sockaddr_in *, char *));
+extern void getauthkeys P((char *));
+extern void rereadkeys P((void));
diff --git a/usr.sbin/xntpd/include/parse.h b/usr.sbin/xntpd/include/parse.h
new file mode 100644
index 0000000..774cf5e
--- /dev/null
+++ b/usr.sbin/xntpd/include/parse.h
@@ -0,0 +1,459 @@
+/*
+ * /src/NTP/REPOSITORY/v3/include/parse.h,v 3.21 1994/05/30 20:58:34 kardel Exp
+ *
+ * parse.h,v 3.21 1994/05/30 20:58:34 kardel Exp
+ *
+ * Copyright (c) 1989,1990,1991,1992,1993,1994
+ * Frank Kardel Friedrich-Alexander Universitaet Erlangen-Nuernberg
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ */
+
+#ifndef __PARSE_H__
+#define __PARSE_H__
+#if !(defined(lint) || defined(__GNUC__))
+ static char parsehrcsid[]="parse.h,v 3.21 1994/05/30 20:58:34 kardel Exp";
+#endif
+
+#include "ntp_types.h"
+
+#include "parse_conf.h"
+
+/*
+ * we use the following datastructures in two modes
+ * either in the NTP itself where we use NTP time stamps at some places
+ * or in the kernel, where only struct timeval will be used.
+ */
+#undef PARSEKERNEL
+#if defined(KERNEL) || defined(_KERNEL)
+#ifndef PARSESTREAM
+#define PARSESTREAM
+#endif
+#endif
+#if defined(PARSESTREAM) && defined(STREAM)
+#define PARSEKERNEL
+#endif
+#ifdef PARSEKERNEL
+#ifndef _KERNEL
+extern caddr_t kmem_alloc P((unsigned int));
+extern caddr_t kmem_free P((caddr_t, unsigned int));
+extern int splx();
+extern int splhigh();
+#define MALLOC(_X_) (char *)kmem_alloc(_X_)
+#define FREE(_X_, _Y_) kmem_free((caddr_t)_X_, _Y_)
+#else
+#include <sys/kmem.h>
+#define MALLOC(_X_) (char *)kmem_alloc(_X_, KM_SLEEP)
+#define FREE(_X_, _Y_) kmem_free((caddr_t)_X_, _Y_)
+#endif
+#else
+#define MALLOC(_X_) malloc(_X_)
+#define FREE(_X_, _Y_) free(_X_)
+#endif
+
+#if defined(PARSESTREAM) && defined(STREAM)
+#include "sys/stream.h"
+#include "sys/stropts.h"
+#ifndef _KERNEL
+extern int printf();
+#endif
+#else /* STREAM */
+#include <stdio.h>
+#include "ntp_syslog.h"
+#ifdef DEBUG
+extern int debug;
+#define DD_PARSE 5
+#define DD_RAWDCF 4
+#define parseprintf(LEVEL, ARGS) if (debug > LEVEL) printf ARGS
+#else /* DEBUG */
+#define parseprintf(LEVEL, ARGS)
+#endif /* DEBUG */
+#endif /* PARSESTREAM */
+
+#if defined(timercmp) && defined(__GNUC__)
+#undef timercmp
+#define timercmp(tvp, uvp, cmp) \
+ ((tvp)->tv_sec cmp (uvp)->tv_sec || \
+ ((tvp)->tv_sec == (uvp)->tv_sec && (tvp)->tv_usec cmp (uvp)->tv_usec))
+#endif
+
+#ifndef TIMES10
+#define TIMES10(_X_) (((_X_) << 3) + ((_X_) << 1))
+#endif
+
+/*
+ * state flags
+ */
+#define PARSEB_POWERUP 0x00000001 /* no synchronisation */
+#define PARSEB_NOSYNC 0x00000002 /* timecode currently not confirmed */
+
+/*
+ * time zone information
+ */
+#define PARSEB_ANNOUNCE 0x00000010 /* switch time zone warning (DST switch) */
+#define PARSEB_DST 0x00000020 /* DST in effect */
+#define PARSEB_UTC 0x00000040 /* UTC time */
+
+/*
+ * leap information
+ */
+#define PARSEB_LEAPDEL 0x00000100 /* LEAP deletion warning */
+#define PARSEB_LEAPADD 0x00000200 /* LEAP addition warning */
+#define PARSEB_LEAPS 0x00000300 /* LEAP warnings */
+#define PARSEB_LEAPSECOND 0x00000400 /* actual leap second */
+/*
+ * optional status information
+ */
+#define PARSEB_ALTERNATE 0x00001000 /* alternate antenna used */
+#define PARSEB_POSITION 0x00002000 /* position available */
+
+/*
+ * feature information
+ */
+#define PARSEB_S_LEAP 0x00010000 /* supports LEAP */
+#define PARSEB_S_ANTENNA 0x00020000 /* supports antenna information */
+#define PARSEB_S_PPS 0x00040000 /* supports PPS time stamping */
+#define PARSEB_S_POSITION 0x00080000 /* supports position information (GPS) */
+
+/*
+ * time stamp availality
+ */
+#define PARSEB_TIMECODE 0x10000000 /* valid time code sample */
+#define PARSEB_PPS 0x20000000 /* valid PPS sample */
+
+#define PARSE_TCINFO (PARSEB_ANNOUNCE|PARSEB_POWERUP|PARSEB_NOSYNC|PARSEB_DST|\
+ PARSEB_UTC|PARSEB_LEAPS|PARSEB_ALTERNATE|PARSEB_S_LEAP|\
+ PARSEB_S_LOCATION|PARSEB_TIMECODE)
+
+#define PARSE_POWERUP(x) ((x) & PARSEB_POWERUP)
+#define PARSE_NOSYNC(x) (((x) & (PARSEB_POWERUP|PARSEB_NOSYNC)) == PARSEB_NOSYNC)
+#define PARSE_SYNC(x) (((x) & (PARSEB_POWERUP|PARSEB_NOSYNC)) == 0)
+#define PARSE_ANNOUNCE(x) ((x) & PARSEB_ANNOUNCE)
+#define PARSE_DST(x) ((x) & PARSEB_DST)
+#define PARSE_UTC(x) ((x) & PARSEB_UTC)
+#define PARSE_LEAPADD(x) (PARSE_SYNC(x) && (((x) & PARSEB_LEAPS) == PARSEB_LEAPADD))
+#define PARSE_LEAPDEL(x) (PARSE_SYNC(x) && (((x) & PARSEB_LEAPS) == PARSEB_LEAPDEL))
+#define PARSE_ALTERNATE(x) ((x) & PARSEB_ALTERNATE)
+#define PARSE_LEAPSECOND(x) (PARSE_SYNC(x) && ((x) & PARSEB_LEAP_SECOND))
+
+#define PARSE_S_LEAP(x) ((x) & PARSEB_S_LEAP)
+#define PARSE_S_ANTENNA(x) ((x) & PARSEB_S_ANTENNA)
+#define PARSE_S_PPS(x) ((x) & PARSEB_S_PPS)
+#define PARSE_S_POSITION(x) ((x) & PARSEB_S_POSITION)
+
+#define PARSE_TIMECODE(x) ((x) & PARSEB_TIMECODE)
+#define PARSE_PPS(x) ((x) & PARSEB_PPS)
+#define PARSE_POSITION(x) ((x) & PARSEB_POSITION)
+
+/*
+ * operation flags - some are also fudge flags
+ */
+#define PARSE_STAT_FLAGS 0x03 /* interpreted by io module */
+#define PARSE_STAT_FILTER 0x01 /* filter incoming data */
+#define PARSE_STAT_AVG 0x02 /* 1:median average / 0: median point */
+#define PARSE_LEAP_DELETE 0x04 /* delete leap */
+#define PARSE_STATISTICS 0x08 /* enable statistics */
+#define PARSE_FIXED_FMT 0x10 /* fixed format */
+#define PARSE_PPSCLOCK 0x20 /* try to get PPS time stamp via ppsclock ioctl */
+
+typedef union timestamp
+{
+ struct timeval tv; /* timeval - usually kernel view */
+ l_fp fp; /* fixed point - xntp view */
+} timestamp_t;
+
+/*
+ * standard time stamp structure
+ */
+struct parsetime
+{
+ u_long parse_status; /* data status - CVT_OK, CVT_NONE, CVT_FAIL ... */
+ timestamp_t parse_time; /* PARSE timestamp */
+ timestamp_t parse_stime; /* telegram sample timestamp */
+ timestamp_t parse_ptime; /* PPS time stamp */
+ long parse_usecerror; /* sampled/filtered usec error */
+ long parse_usecdisp; /* sampled usecdispersion */
+ u_long parse_state; /* current receiver state */
+ unsigned short parse_format; /* format code */
+};
+
+typedef struct parsetime parsetime_t;
+
+/*---------- STREAMS interface ----------*/
+
+#ifdef STREAM
+/*
+ * ioctls
+ */
+#define PARSEIOC_ENABLE (('D'<<8) + 'E')
+#define PARSEIOC_DISABLE (('D'<<8) + 'D')
+#define PARSEIOC_SETSTAT (('D'<<8) + 'S')
+#define PARSEIOC_GETSTAT (('D'<<8) + 'G')
+#define PARSEIOC_SETFMT (('D'<<8) + 'f')
+#define PARSEIOC_GETFMT (('D'<<8) + 'F')
+#define PARSEIOC_SETCS (('D'<<8) + 'C')
+#define PARSEIOC_TIMECODE (('D'<<8) + 'T')
+
+#endif
+
+/*------ IO handling flags (sorry) ------*/
+
+#define PARSE_IO_CSIZE 0x00000003
+#define PARSE_IO_CS5 0x00000000
+#define PARSE_IO_CS6 0x00000001
+#define PARSE_IO_CS7 0x00000002
+#define PARSE_IO_CS8 0x00000003
+
+/*
+ * sizes
+ */
+#define PARSE_TCMAX 128
+
+/*
+ * ioctl structure
+ */
+union parsectl
+{
+ struct parsestatus
+ {
+ u_long flags; /* new/old flags */
+ } parsestatus;
+
+ struct parsegettc
+ {
+ u_long parse_state; /* last state */
+ u_long parse_badformat; /* number of bad packets since last query */
+ unsigned short parse_format;/* last decoded format */
+ unsigned short parse_count; /* count of valid time code bytes */
+ char parse_buffer[PARSE_TCMAX+1]; /* timecode buffer */
+ } parsegettc;
+
+ struct parseformat
+ {
+ unsigned short parse_format;/* number of examined format */
+ unsigned short parse_count; /* count of valid string bytes */
+ char parse_buffer[PARSE_TCMAX+1]; /* format code string */
+ } parseformat;
+
+ struct parsesetcs
+ {
+ u_long parse_cs; /* character size (needed for stripping) */
+ } parsesetcs;
+};
+
+typedef union parsectl parsectl_t;
+
+/*------ for conversion routines --------*/
+
+#define PARSE_DELTA 16
+
+struct parse /* parse module local data */
+{
+ int parse_flags; /* operation and current status flags */
+
+ int parse_ioflags; /* io handling flags (5-8 Bit control currently) */
+ int parse_syncflags; /* possible sync events (START/END/character) */
+ /*
+ * RS232 input parser information
+ */
+ unsigned char parse_startsym[32]; /* possible start packet values */
+ unsigned char parse_endsym[32]; /* possible end packet values */
+ unsigned char parse_syncsym[32]; /* sync characters */
+ struct timeval parse_timeout; /* max gap between characters (us) */
+
+ /*
+ * PPS 'input' buffer
+ */
+ struct timeval parse_lastone; /* time stamp of last PPS 1 transition */
+ struct timeval parse_lastzero; /* time stamp of last PPS 0 transition */
+
+ /*
+ * character input buffer
+ */
+ timestamp_t parse_lastchar; /* time stamp of last received character */
+
+ /*
+ * private data - fixed format only
+ */
+ unsigned short parse_plen; /* length of private data */
+ void *parse_pdata; /* private data pointer */
+
+ /*
+ * time code input buffer (from RS232 or PPS)
+ */
+ unsigned short parse_index; /* current buffer index */
+ char *parse_data; /* data buffer */
+ unsigned short parse_dsize; /* size of data buffer */
+ unsigned short parse_lformat; /* last format used */
+ u_long parse_lstate; /* last state code */
+ char *parse_ldata; /* last data buffer */
+ unsigned short parse_ldsize; /* last data buffer length */
+ u_long parse_badformat; /* number of unparsable pakets */
+
+ /*
+ * time stamp filtering
+ */
+ long parse_delta[PARSE_DELTA]; /* delta buffer */
+ int parse_dindex;
+
+ parsetime_t parse_dtime; /* external data prototype */
+};
+
+typedef struct parse parse_t;
+
+struct clocktime /* clock time broken up from time code */
+{
+ long day;
+ long month;
+ long year;
+ long hour;
+ long minute;
+ long second;
+ long usecond;
+ long utcoffset; /* in seconds */
+ time_t utctime; /* the actual time - alternative to date/time */
+ long flags; /* current clock status */
+};
+
+typedef struct clocktime clocktime_t;
+
+/*
+ * clock formats specify routines to be called to
+ * convert the buffer into a struct clock.
+ * functions are called
+ * fn(buffer, data, clock) -> CVT_NONE, CVT_FAIL, CVT_OK
+ *
+ * the private data pointer can be used to
+ * distingush between different formats of a common
+ * base type
+ */
+#define F_START 0x00000001 /* start packet delimiter */
+#define F_END 0x00000002 /* end packet delimiter */
+#define SYNC_TIMEOUT 0x00000004 /* packet restart after timeout */
+#define SYNC_START 0x00000008 /* packet start is sync event */
+#define SYNC_END 0x00000010 /* packet end is sync event */
+#define SYNC_CHAR 0x00000020 /* special character is sync event */
+#define SYNC_ONE 0x00000040 /* PPS synchronize on 'ONE' transition */
+#define SYNC_ZERO 0x00000080 /* PPS synchronize on 'ZERO' transition */
+#define SYNC_SYNTHESIZE 0x00000100 /* generate intermediate time stamps */
+#define CVT_FIXEDONLY 0x00010000 /* convert only in fixed configuration */
+
+/*
+ * parser related return/error codes
+ */
+#define CVT_MASK 0x0000000F /* conversion exit code */
+#define CVT_NONE 0x00000001 /* format not applicable */
+#define CVT_FAIL 0x00000002 /* conversion failed - error code returned */
+#define CVT_OK 0x00000004 /* conversion succeeded */
+#define CVT_SKIP 0x00000008 /* conversion succeeded */
+#define CVT_BADFMT 0x00000010 /* general format error - (unparsable) */
+#define CVT_BADDATE 0x00000020 /* date field incorrect */
+#define CVT_BADTIME 0x00000040 /* time field incorrect */
+
+struct clockformat
+{
+ u_long (*input)(); /* special input protocol - implies fixed format */
+ u_long (*convert)(); /* conversion routine */
+ void (*syncevt)(); /* routine for handling RS232 sync events (time stamps) */
+ u_long (*syncpps)(); /* PPS input routine */
+ u_long (*synth)(); /* time code synthesizer */
+ void *data; /* local parameters */
+ char *name; /* clock format name */
+ unsigned short length; /* maximum length of data packet */
+ u_long flags; /* valid start symbols etc. */
+ unsigned short plen; /* length of private data - implies fixed format */
+ struct timeval timeout; /* buffer restart after timeout (us) */
+ unsigned char startsym; /* start symbol */
+ unsigned char endsym; /* end symbol */
+ unsigned char syncsym; /* sync symbol */
+};
+
+typedef struct clockformat clockformat_t;
+
+/*
+ * parse interface
+ */
+extern int parse_ioinit P((parse_t *));
+extern void parse_ioend P((parse_t *));
+extern int parse_ioread P((parse_t *, unsigned char, timestamp_t *));
+extern int parse_iopps P((parse_t *, int, timestamp_t *));
+extern void parse_iodone P((parse_t *));
+
+extern int parse_getstat P((parsectl_t *, parse_t *));
+extern int parse_setstat P((parsectl_t *, parse_t *));
+extern int parse_timecode P((parsectl_t *, parse_t *));
+extern int parse_getfmt P((parsectl_t *, parse_t *));
+extern int parse_setfmt P((parsectl_t *, parse_t *));
+extern int parse_setcs P((parsectl_t *, parse_t *));
+
+extern int Strok P((char *, char *));
+extern int Stoi P((char *, long *, int));
+
+extern time_t parse_to_unixtime P((clocktime_t *, u_long *));
+extern u_long updatetimeinfo P((parse_t *, time_t, u_long, u_long));
+extern void syn_simple P((parse_t *, timestamp_t *, struct format *, u_long));
+extern u_long pps_simple P((parse_t *, int, timestamp_t *));
+#endif
+
+/*
+ * History:
+ *
+ * parse.h,v
+ * Revision 3.21 1994/05/30 20:58:34 kardel
+ * fix prototypes
+ *
+ * Revision 3.20 1994/05/30 10:19:44 kardel
+ * LONG cleanup
+ *
+ * Revision 3.19 1994/05/15 11:30:33 kardel
+ * documented flag4 as statistics enable flag
+ *
+ * Revision 3.18 1994/05/12 12:40:34 kardel
+ * shut up gcc about broken Sun/BSD code
+ *
+ * Revision 3.17 1994/03/03 09:27:20 kardel
+ * rcs ids fixed
+ *
+ * Revision 3.13 1994/01/25 19:04:21 kardel
+ * 94/01/23 reconcilation
+ *
+ * Revision 3.12 1994/01/23 17:23:05 kardel
+ * 1994 reconcilation
+ *
+ * Revision 3.11 1993/11/11 11:20:18 kardel
+ * declaration fixes
+ *
+ * Revision 3.10 1993/11/01 19:59:48 kardel
+ * parse Solaris support (initial version)
+ *
+ * Revision 3.9 1993/10/06 00:14:57 kardel
+ * include fixes
+ *
+ * Revision 3.8 1993/10/05 23:15:41 kardel
+ * more STREAM protection
+ *
+ * Revision 3.7 1993/10/05 22:56:10 kardel
+ * STREAM must be defined for PARSESTREAMS
+ *
+ * Revision 3.6 1993/10/03 19:10:28 kardel
+ * restructured I/O handling
+ *
+ * Revision 3.5 1993/09/26 23:41:13 kardel
+ * new parse driver logic
+ *
+ * Revision 3.4 1993/09/01 21:46:31 kardel
+ * conditional cleanup
+ *
+ * Revision 3.3 1993/08/27 00:29:29 kardel
+ * compilation cleanup
+ *
+ * Revision 3.2 1993/07/09 11:37:05 kardel
+ * Initial restructured version + GPS support
+ *
+ * Revision 3.1 1993/07/06 09:59:12 kardel
+ * DCF77 driver goes generic...
+ *
+ */
diff --git a/usr.sbin/xntpd/include/parse_conf.h b/usr.sbin/xntpd/include/parse_conf.h
new file mode 100644
index 0000000..3c512aa
--- /dev/null
+++ b/usr.sbin/xntpd/include/parse_conf.h
@@ -0,0 +1,54 @@
+/*
+ * /src/NTP/REPOSITORY/v3/include/parse_conf.h,v 3.6 1994/05/30 10:19:49 kardel Exp
+ *
+ * parse_conf.h,v 3.6 1994/05/30 10:19:49 kardel Exp
+ *
+ * Copyright (c) 1993,1994
+ * Frank Kardel Friedrich-Alexander Universitaet Erlangen-Nuernberg
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ */
+
+#ifndef __PARSE_CONF_H__
+#define __PARSE_CONF_H__
+#if !(defined(lint) || defined(__GNUC__))
+ static char dcfhrcsid[]="parse_conf.h,v 3.6 1994/05/30 10:19:49 kardel Exp FAU";
+#endif
+
+/*
+ * field location structure (Meinberg clocks/simple format)
+ */
+#define O_DAY 0
+#define O_MONTH 1
+#define O_YEAR 2
+#define O_HOUR 3
+#define O_MIN 4
+#define O_SEC 5
+#define O_WDAY 6
+#define O_FLAGS 7
+#define O_ZONE 8
+#define O_UTCHOFFSET 9
+#define O_UTCMOFFSET 10
+#define O_UTCSOFFSET 11
+#define O_COUNT (O_UTCSOFFSET+1)
+
+#define MBG_EXTENDED 0x00000001
+
+/*
+ * see below for field offsets
+ */
+
+struct format
+{
+ struct foff
+ {
+ char offset; /* offset into buffer */
+ char length; /* length of field */
+ } field_offsets[O_COUNT];
+ char *fixed_string; /* string with must be chars (blanks = wildcards) */
+ u_long flags;
+};
+#endif
diff --git a/usr.sbin/xntpd/include/sys/bsd_audioirig.h b/usr.sbin/xntpd/include/sys/bsd_audioirig.h
new file mode 100644
index 0000000..6a23260
--- /dev/null
+++ b/usr.sbin/xntpd/include/sys/bsd_audioirig.h
@@ -0,0 +1,101 @@
+/*
+ * $Header: bsd_audioirig.h,v 1.0 93/08/02 12:42:00
+ */
+
+#ifndef _BSD_AUDIOIRIG_H_
+#define _BSD_AUDIOIRIG_H_
+
+#include <sys/time.h>
+
+/********************************************************************/
+/* user interface */
+
+/*
+ * irig ioctls
+ */
+#if defined(__STDC__) || !(defined(ibm032) && !defined(__GNUC))
+#define AUDIO_IRIG_OPEN _IO('A', 50)
+#define AUDIO_IRIG_CLOSE _IO('A', 51)
+#define AUDIO_IRIG_SETFORMAT _IOWR('A', 52, int)
+#else
+#define AUDIO_IRIG_OPEN _IO(A, 50)
+#define AUDIO_IRIG_CLOSE _IO(A, 51)
+#define AUDIO_IRIG_SETFORMAT _IOWR(A, 52, int)
+#endif
+
+/*
+ * irig error codes
+ */
+#define AUDIO_IRIG_BADSIGNAL 0x01
+#define AUDIO_IRIG_BADDATA 0x02
+#define AUDIO_IRIG_BADSYNC 0x04
+#define AUDIO_IRIG_BADCLOCK 0x08
+#define AUDIO_IRIG_OLDDATA 0x10
+
+/********************************************************************/
+
+/*
+ * auib definitions
+ */
+#define AUIB_SIZE (0x0040)
+#define AUIB_INC (0x0008)
+#define AUIB_MOD(k) ((k) & 0x0038)
+#define AUIB_INIT(ib) ((ib)->ib_head = (ib)->ib_tail = (ib)->ib_lock = \
+ (ib)->phase = (ib)->shi = (ib)->slo = (ib)->high = \
+ (ib)->level0 = (ib)->level1 = \
+ (ib)->shift[0] = (ib)->shift[1] = (ib)->shift[2] = \
+ (ib)->shift[3] = (ib)->sdata[0] = (ib)->sdata[1] = \
+ (ib)->sdata[2] = (ib)->sdata[3] = (ib)->err = 0)
+#define AUIB_EMPTY(ib) ((ib)->ib_head == (ib)->ib_tail)
+#define AUIB_LEN(ib) (AUIB_MOD((ib)->ib_tail - (ib)->ib_head))
+#define AUIB_LEFT(ib) (AUIB_MOD((ib)->ib_head - (ib)->ib_tail - 1))
+#define IRIGDELAY 3
+#define IRIGLEVEL 1355
+
+#ifndef LOCORE
+/*
+ * irig_time holds IRIG data for one second
+ */
+struct irig_time {
+ struct timeval stamp; /* timestamp */
+ u_char bits[13]; /* 100 irig data bits */
+ u_char status; /* status byte */
+ char time[14]; /* time string */
+};
+
+/*
+ * auib's are used for IRIG data communication between the trap
+ * handler and the software interrupt.
+ */
+struct auib {
+ /* driver variables */
+ u_short active; /* 0=inactive, else=active */
+ u_short format; /* time output format */
+ struct irig_time timestr; /* time structure */
+ char buffer[14]; /* output formation buffer */
+
+ /* hardware interrupt variables */
+ struct timeval tv1,tv2,tv3; /* time stamps (median filter) */
+ int level0,level1; /* lo/hi input levels */
+ int level; /* decision level */
+ int high; /* recent largest sample */
+ int sl0,sl1; /* recent sample levels */
+ int lasts; /* last sample value */
+ u_short scount; /* sample count */
+ u_long eacc; /* 10-bit element accumulator */
+ u_long ebit; /* current bit in element */
+ u_char r_level,mmr1; /* recording level 0-255 */
+ int shi,slo,phase; /* AGC variables */
+ u_long err; /* error status bits */
+ int ecount; /* count of elements this second */
+ long shift[4]; /* shift register of pos ident */
+ long sdata[4]; /* shift register of symbols */
+
+ int ib_head; /* queue head */
+ int ib_tail; /* queue tail */
+ u_short ib_lock; /* queue head lock */
+ u_long ib_data[AUIB_SIZE]; /* data buffer */
+};
+#endif
+
+#endif /* _BSD_AUDIOIRIG_H_ */
diff --git a/usr.sbin/xntpd/include/sys/chudefs.h b/usr.sbin/xntpd/include/sys/chudefs.h
new file mode 100644
index 0000000..f5549f5
--- /dev/null
+++ b/usr.sbin/xntpd/include/sys/chudefs.h
@@ -0,0 +1,22 @@
+/*
+ * Definitions for the CHU line discipline v2.0
+ */
+
+/*
+ * The CHU time code consists of 10 BCD digits and is repeated
+ * twice for a total of 10 characters. A time is taken after
+ * the arrival of each character. The following structure is
+ * used to return this stuff.
+ */
+#define NCHUCHARS (10)
+
+struct chucode {
+ u_char codechars[NCHUCHARS]; /* code characters */
+ u_char ncodechars; /* number of code characters */
+ u_char chutype; /* packet type */
+ struct timeval codetimes[NCHUCHARS]; /* arrival times */
+};
+
+#define CHU_TIME 0 /* second half is equal to first half */
+#define CHU_YEAR 1 /* second half is one's complement */
+
diff --git a/usr.sbin/xntpd/include/sys/clkdefs.h b/usr.sbin/xntpd/include/sys/clkdefs.h
new file mode 100644
index 0000000..afbc77a
--- /dev/null
+++ b/usr.sbin/xntpd/include/sys/clkdefs.h
@@ -0,0 +1,38 @@
+/*
+ * Defines for the "clk" timestamping STREAMS module
+ */
+
+#if defined(sun)
+#include <sys/ioccom.h>
+#else
+#include <sys/ioctl.h>
+#endif
+
+/*
+ * First, we need to define the maximum size of the set of
+ * characters to timestamp. 32 is MORE than enough.
+ */
+
+#define CLK_MAXSTRSIZE 32
+struct clk_tstamp_charset { /* XXX to use _IOW not _IOWN */
+ char val[CLK_MAXSTRSIZE];
+};
+
+/*
+ * ioctl(fd, CLK_SETSTR, (char*)c );
+ *
+ * will tell the driver that any char in the null-terminated
+ * string c should be timestamped. It is possible, though
+ * unlikely that this ioctl number could collide with an
+ * existing one on your system. If so, change the 'K'
+ * to some other letter. However, once you've compiled
+ * the kernel with this include file, you should NOT
+ * change this file.
+ */
+
+#if defined(__STDC__) /* XXX avoid __STDC__=0 on SOLARIS */
+#define CLK_SETSTR _IOW('K', 01, struct clk_tstamp_charset)
+#else
+#define CLK_SETSTR _IOW(K, 01, struct clk_tstamp_charset)
+#endif
+
diff --git a/usr.sbin/xntpd/include/sys/parsestreams.h b/usr.sbin/xntpd/include/sys/parsestreams.h
new file mode 100644
index 0000000..ac66f42
--- /dev/null
+++ b/usr.sbin/xntpd/include/sys/parsestreams.h
@@ -0,0 +1,66 @@
+/*
+ * /src/NTP/REPOSITORY/v3/include/sys/parsestreams.h,v 3.12 1994/06/01 08:21:08 kardel Exp
+ *
+ * parsestreams.h,v 3.12 1994/06/01 08:21:08 kardel Exp
+ *
+ * Copyright (c) 1989,1990,1991,1992,1993,1994
+ * Frank Kardel Friedrich-Alexander Universitaet Erlangen-Nuernberg
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ */
+
+#if !(defined(lint) || defined(__GNUC__))
+ static char parse77hrcsid[]="parsestreams.h,v 3.12 1994/06/01 08:21:08 kardel Exp";
+#endif
+
+#undef PARSEKERNEL
+#if defined(KERNEL) || defined(_KERNEL)
+#ifndef PARSESTREAM
+#define PARSESTREAM
+#endif
+#endif
+#if defined(PARSESTREAM) && defined(STREAM)
+#define PARSEKERNEL
+#include <sys/ppsclock.h>
+
+struct parsestream /* parse module local data */
+{
+ queue_t *parse_queue; /* read stream for this channel */
+ queue_t *parse_dqueue; /* driver queue entry (PPS support) */
+ unsigned long parse_status; /* operation flags */
+ void *parse_data; /* local data space (PPS support) */
+ parse_t parse_io; /* io structure */
+ struct ppsclockev parse_ppsclockev; /* copy of last pps event */
+};
+
+typedef struct parsestream parsestream_t;
+
+#define PARSE_ENABLE 0x0001
+
+/*--------------- debugging support ---------------------------------*/
+
+#ifdef DEBUG_PARSE
+
+extern int parsedebug;
+
+#define DD_OPEN 0x00000001
+#define DD_CLOSE 0x00000002
+#define DD_RPUT 0x00000004
+#define DD_WPUT 0x00000008
+#define DD_RSVC 0x00000010
+#define DD_PARSE 0x00000020
+#define DD_INSTALL 0x00000040
+#define DD_ISR 0x00000080
+#define DD_RAWDCF 0x00000100
+
+#define parseprintf(X, Y) if ((X) & parsedebug) printf Y
+
+#else
+
+#define parseprintf(X, Y)
+
+#endif
+#endif
diff --git a/usr.sbin/xntpd/include/sys/ppsclock.h b/usr.sbin/xntpd/include/sys/ppsclock.h
new file mode 100644
index 0000000..edf28aa
--- /dev/null
+++ b/usr.sbin/xntpd/include/sys/ppsclock.h
@@ -0,0 +1,58 @@
+/*
+ * This software was developed by the Computer Systems Engineering group
+ * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66.
+ *
+ * Copyright (c) 1992 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Lawrence Berkeley Laboratory.
+ * 4. The name of the University may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#define PPSCLOCKSTR "ppsclock"
+
+struct ppsclockev {
+ struct timeval tv;
+ u_int serial;
+};
+
+#if defined(__STDC__) || defined(SYS_HPUX)
+#ifdef _IOR
+#define CIOGETEV _IOR('C', 0, struct ppsclockev) /* get last pps event */
+#else /* XXX SOLARIS is different */
+#define CIO ('C'<<8)
+#define CIOGETEV (CIO|0) /* get last pps event */
+#endif /* _IOR */
+#else /* __STDC__ */
+#ifdef _IOR
+#define CIOGETEV _IOR(C, 0, struct ppsclockev) /* get last pps event */
+#else /* XXX SOLARIS is different */
+#define CIO ('C'<<8)
+#define CIOGETEV (CIO|0) /* get last pps event */
+#endif /* _IOR */
+#endif /* __STDC__ */
diff --git a/usr.sbin/xntpd/include/sys/timex.h b/usr.sbin/xntpd/include/sys/timex.h
new file mode 100644
index 0000000..bc2d634
--- /dev/null
+++ b/usr.sbin/xntpd/include/sys/timex.h
@@ -0,0 +1,290 @@
+/******************************************************************************
+ * *
+ * Copyright (c) David L. Mills 1993, 1994 *
+ * *
+ * Permission to use, copy, modify, and distribute this software and its *
+ * documentation for any purpose and without fee is hereby granted, provided *
+ * that the above copyright notice appears in all copies and that both the *
+ * copyright notice and this permission notice appear in supporting *
+ * documentation, and that the name University of Delaware not be used in *
+ * advertising or publicity pertaining to distribution of the software *
+ * without specific, written prior permission. The University of Delaware *
+ * makes no representations about the suitability this software for any *
+ * purpose. It is provided "as is" without express or implied warranty. *
+ * *
+ ******************************************************************************/
+
+/*
+ * Modification history timex.h
+ *
+ * 19 Mar 94 David L. Mills
+ * Moved defines from kernel routines to header file and added new
+ * defines for PPS phase-lock loop.
+ *
+ * 20 Feb 94 David L. Mills
+ * Revised status codes and structures for external clock and PPS
+ * signal discipline.
+ *
+ * 28 Nov 93 David L. Mills
+ * Adjusted parameters to improve stability and increase poll
+ * interval.
+ *
+ * 17 Sep 93 David L. Mills
+ * Created file
+ */
+/*
+ * This header file defines the Network Time Protocol (NTP) interfaces
+ * for user and daemon application programs. These are implemented using
+ * private syscalls and data structures and require specific kernel
+ * support.
+ *
+ * NAME
+ * ntp_gettime - NTP user application interface
+ *
+ * SYNOPSIS
+ * #include <sys/timex.h>
+ *
+ * int syscall(SYS_ntp_gettime, tptr)
+ *
+ * int SYS_ntp_gettime defined in syscall.h header file
+ * struct ntptimeval *tptr pointer to ntptimeval structure
+ *
+ * NAME
+ * ntp_adjtime - NTP daemon application interface
+ *
+ * SYNOPSIS
+ * #include <sys/timex.h>
+ *
+ * int syscall(SYS_ntp_adjtime, mode, tptr)
+ *
+ * int SYS_ntp_adjtime defined in syscall.h header file
+ * struct timex *tptr pointer to timex structure
+ *
+ */
+#ifndef _SYS_TIMEX_H_
+#define _SYS_TIMEX_H_ 1
+
+#ifndef MSDOS /* Microsoft specific */
+#include <sys/syscall.h>
+#endif /* MSDOS */
+
+/*
+ * The following defines establish the engineering parameters of the
+ * phase-lock loop (PLL) model used in the kernel implementation. These
+ * parameters have been carefully chosen by analysis for good stability
+ * and wide dynamic range.
+ *
+ * The hz variable is defined in the kernel build environment. It
+ * establishes the timer interrupt frequency, 100 Hz for the SunOS
+ * kernel, 256 Hz for the Ultrix kernel and 1024 Hz for the OSF/1
+ * kernel. SHIFT_HZ expresses the same value as the nearest power of two
+ * in order to avoid hardware multiply operations.
+ *
+ * SHIFT_KG and SHIFT_KF establish the damping of the PLL and are chosen
+ * for a slightly underdamped convergence characteristic.
+ *
+ * MAXTC establishes the maximum time constant of the PLL. With the
+ * SHIFT_KG and SHIFT_KF values given and a time constant range from
+ * zero to MAXTC, the PLL will converge in 15 minutes to 16 hours,
+ * respectively.
+ */
+#define SHIFT_HZ 7 /* log2(hz) */
+#define SHIFT_KG 6 /* phase factor (shift) */
+#define SHIFT_KF 16 /* frequency factor (shift) */
+#define MAXTC 6 /* maximum time constant (shift) */
+
+/*
+ * The following defines establish the scaling of the various variables
+ * used by the PLL. They are chosen to allow the greatest precision
+ * possible without overflow of a 32-bit word.
+ *
+ * SHIFT_SCALE defines the scaling (shift) of the time_phase variable,
+ * which serves as a an extension to the low-order bits of the system
+ * clock variable time.tv_usec.
+ *
+ * SHIFT_UPDATE defines the scaling (shift) of the time_offset variable,
+ * which represents the current time offset with respect to standard
+ * time.
+ *
+ * SHIFT_USEC defines the scaling (shift) of the time_freq and
+ * time_tolerance variables, which represent the current frequency
+ * offset and maximum frequency tolerance.
+ *
+ * FINEUSEC is 1 us in SHIFT_UPDATE units of the time_phase variable.
+ */
+#define SHIFT_SCALE 23 /* phase scale (shift) */
+#define SHIFT_UPDATE (SHIFT_KG + MAXTC) /* time offset scale (shift) */
+#define SHIFT_USEC 16 /* frequency offset scale (shift) */
+#define FINEUSEC (1L << SHIFT_SCALE) /* 1 us in phase units */
+
+/*
+ * The following defines establish the performance envelope of the PLL.
+ * They insure it operates within predefined limits, in order to satisfy
+ * correctness assertions. An excursion which exceeds these bounds is
+ * clamped to the bound and operation proceeds accordingly. In practice,
+ * this can occur only if something has failed or is operating out of
+ * tolerance, but otherwise the PLL continues to operate in a stable
+ * mode.
+ *
+ * MAXPHASE must be set greater than or equal to CLOCK.MAX (128 ms), as
+ * defined in the NTP specification. CLOCK.MAX establishes the maximum
+ * time offset allowed before the system time is reset, rather than
+ * incrementally adjusted. Here, the maximum offset is clamped to
+ * MAXPHASE only in order to prevent overflow errors due to defective
+ * protocol implementations.
+ *
+ * MAXFREQ is the maximum frequency tolerance of the CPU clock
+ * oscillator plus the maximum slew rate allowed by the protocol. It
+ * should be set to at least the frequency tolerance of the oscillator
+ * plus 100 ppm for vernier frequency adjustments. If the kernel
+ * PPS discipline code is configured (PPS_SYNC), the oscillator time and
+ * frequency are disciplined to an external source, presumably with
+ * negligible time and frequency error relative to UTC, and MAXFREQ can
+ * be reduced.
+ *
+ * MAXTIME is the maximum jitter tolerance of the PPS signal if the
+ * kernel PPS discipline code is configured (PPS_SYNC).
+ *
+ * MINSEC and MAXSEC define the lower and upper bounds on the interval
+ * between protocol updates.
+ */
+#define MAXPHASE 128000L /* max phase error (us) */
+#ifdef PPS_SYNC
+#define MAXFREQ (100L << SHIFT_USEC) /* max freq error (100 ppm) */
+#define MAXTIME (200L << PPS_AVG) /* max PPS error (jitter) (200 us) */
+#else
+#define MAXFREQ (200L << SHIFT_USEC) /* max freq error (200 ppm) */
+#endif /* PPS_SYNC */
+#define MINSEC 16L /* min interval between updates (s) */
+#define MAXSEC 1200L /* max interval between updates (s) */
+
+#ifdef PPS_SYNC
+/*
+ * The following defines are used only if a pulse-per-second (PPS)
+ * signal is available and connected via a modem control lead, such as
+ * produced by the optional ppsclock feature incorporated in the Sun
+ * asynch driver. They establish the design parameters of the frequency-
+ * lock loop used to discipline the CPU clock oscillator to the PPS
+ * signal.
+ *
+ * PPS_AVG is the averaging factor for the frequency loop, as well as
+ * the time and frequency dispersion.
+ *
+ * PPS_SHIFT and PPS_SHIFTMAX specify the minimum and maximum
+ * calibration intervals, respectively, in seconds as a power of two.
+ *
+ * PPS_VALID is the maximum interval before the PPS signal is considered
+ * invalid and protocol updates used directly instead.
+ *
+ * MAXGLITCH is the maximum interval before a time offset of more than
+ * MAXTIME is believed.
+ */
+#define PPS_AVG 2 /* pps averaging constant (shift) */
+#define PPS_SHIFT 2 /* min interval duration (s) (shift) */
+#define PPS_SHIFTMAX 8 /* max interval duration (s) (shift) */
+#define PPS_VALID 120 /* pps signal watchdog max (s) */
+#define MAXGLITCH 30 /* pps signal glitch max (s) */
+#endif /* PPS_SYNC */
+
+/*
+ * The following defines and structures define the user interface for
+ * the ntp_gettime() and ntp_adjtime() system calls.
+ *
+ * Control mode codes (timex.modes)
+ */
+#define MOD_OFFSET 0x0001 /* set time offset */
+#define MOD_FREQUENCY 0x0002 /* set frequency offset */
+#define MOD_MAXERROR 0x0004 /* set maximum time error */
+#define MOD_ESTERROR 0x0008 /* set estimated time error */
+#define MOD_STATUS 0x0010 /* set clock status bits */
+#define MOD_TIMECONST 0x0020 /* set pll time constant */
+#define MOD_CLKB 0x4000 /* set clock B */
+#define MOD_CLKA 0x8000 /* set clock A */
+
+/*
+ * Status codes (timex.status)
+ */
+#define STA_PLL 0x0001 /* enable PLL updates (rw) */
+#define STA_PPSFREQ 0x0002 /* enable PPS freq discipline (rw) */
+#define STA_PPSTIME 0x0004 /* enable PPS time discipline (rw) */
+
+#define STA_INS 0x0010 /* insert leap (rw) */
+#define STA_DEL 0x0020 /* delete leap (rw) */
+#define STA_UNSYNC 0x0040 /* clock unsynchronized (rw) */
+
+#define STA_PPSSIGNAL 0x0100 /* PPS signal present (ro) */
+#define STA_PPSJITTER 0x0200 /* PPS signal jitter exceeded (ro) */
+#define STA_PPSWANDER 0x0400 /* PPS signal wander exceeded (ro) */
+#define STA_PPSERROR 0x0800 /* PPS signal calibration error (ro) */
+
+#define STA_CLOCKERR 0x1000 /* clock hardware fault (ro) */
+
+#define STA_RONLY (STA_PPSSIGNAL | STA_PPSJITTER | STA_PPSWANDER | \
+ STA_PPSERROR | STA_CLOCKERR) /* read-only bits */
+
+/*
+ * Clock states (time_state)
+ */
+#define TIME_OK 0 /* no leap second warning */
+#define TIME_INS 1 /* insert leap second warning */
+#define TIME_DEL 2 /* delete leap second warning */
+#define TIME_OOP 3 /* leap second in progress */
+#define TIME_WAIT 4 /* leap second has occured */
+#define TIME_ERROR 5 /* clock not synchronized */
+
+/*
+ * NTP user interface (ntp_gettime()) - used to read kernel clock values
+ *
+ * Note: maximum error = NTP synch distance = dispersion + delay / 2;
+ * estimated error = NTP dispersion.
+ */
+struct ntptimeval {
+ struct timeval time; /* current time (ro) */
+ long maxerror; /* maximum error (us) (ro) */
+ long esterror; /* estimated error (us) (ro) */
+};
+
+/*
+ * NTP daemon interface - (ntp_adjtime()) used to discipline CPU clock
+ * oscillator
+ */
+struct timex {
+ unsigned int modes; /* clock mode bits (wo) */
+ long offset; /* time offset (us) (rw) */
+ long freq; /* frequency offset (scaled ppm) (rw) */
+ long maxerror; /* maximum error (us) (rw) */
+ long esterror; /* estimated error (us) (rw) */
+ int status; /* clock status bits (rw) */
+ long constant; /* pll time constant (rw) */
+ long precision; /* clock precision (us) (ro) */
+ long tolerance; /* clock frequency tolerance (scaled
+ * ppm) (ro) */
+ /*
+ * The following read-only structure members are implemented
+ * only if the PPS signal discipline is configured in the
+ * kernel.
+ */
+ long ppsfreq; /* pps frequency (scaled ppm) (ro) */
+ long jitter; /* pps jitter (us) (ro) */
+ int shift; /* interval duration (s) (shift) (ro) */
+ long stabil; /* pps stability (scaled ppm) (ro) */
+ long jitcnt; /* jitter limit exceeded (ro) */
+ long calcnt; /* calibration intervals (ro) */
+ long errcnt; /* calibration errors (ro) */
+ long stbcnt; /* stability limit exceeded (ro) */
+
+};
+#ifdef __FreeBSD__
+
+#ifndef KERNEL
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+extern int ntp_gettime __P((struct ntptimeval *));
+extern int ntp_adjtime __P((struct timex *));
+__END_DECLS
+
+#endif /* not KERNEL */
+
+#endif /* __FreeBSD__ */
+#endif /* _SYS_TIMEX_H_ */
diff --git a/usr.sbin/xntpd/include/sys/tpro.h b/usr.sbin/xntpd/include/sys/tpro.h
new file mode 100644
index 0000000..f276f81
--- /dev/null
+++ b/usr.sbin/xntpd/include/sys/tpro.h
@@ -0,0 +1,34 @@
+/*
+ * Structure for the KSI/Odetics TPRO-S data returned in reponse to a
+ * read() call. Note that these are driver-specific and not dependent on
+ * 32/64-bit architecture.
+ */
+struct tproval {
+ u_short day100; /* days * 100 */
+ u_short day10; /* days * 10 */
+ u_short day1; /* days * 1 */
+ u_short hour10; /* hours * 10 */
+ u_short hour1; /* hours * 1 */
+ u_short min10; /* minutes * 10 */
+ u_short min1; /* minutes * 1 */
+ u_short sec10; /* seconds * 10 */
+ u_short sec1; /* seconds * 1*/
+ u_short ms100; /* milliseconds * 100 */
+ u_short ms10; /* milliseconds * 10 */
+ u_short ms1; /* milliseconds * 1 */
+ u_short usec100; /* microseconds * 100 */
+ u_short usec10; /* microseconds * 10 */
+ u_short usec1; /* microseconds * 1 */
+ long tv_sec; /* seconds */
+ long tv_usec; /* microseconds */
+ u_short status; /* status register */
+};
+
+/*
+ * Status register bits
+ */
+#define TIMEAVAIL 0x0001 /* time available */
+#define NOSIGNAL 0x0002 /* insufficient IRIG-B signal */
+#define NOSYNC 0x0004 /* local oscillator not synchronized */
+
+/* end of tpro.h */
diff --git a/usr.sbin/xntpd/kernel/README.kern b/usr.sbin/xntpd/kernel/README.kern
new file mode 100644
index 0000000..64ba9c5
--- /dev/null
+++ b/usr.sbin/xntpd/kernel/README.kern
@@ -0,0 +1,596 @@
+Precision Time and Frequency Synchronization Using Modified Kernels
+
+1. Introduction
+
+This memo describes replacements for certain SunOS and Ultrix kernel
+routines that manage the system clock and timer functions. They provide
+improved accuracy and stability through the use of a disciplined clock
+interface for use with the Network Time Protocol (NTP) or similar time-
+synchronization protocol. In addition, for certain models of the
+DECstation 5000 product line, the new routines provide improved
+precision to +-1 microsecond (us) (SunOS 4.1.1 already does provide
+precision to +-1 us). The current public NTP distribution cooperates
+with these kernel routines to provide synchronization in principle to
+within a microsecond, but in practice this is limited by the short-term
+stability of the oscillator that drives the timer interrupt.
+
+This memo describes the principles behind the design and operation of
+the software. There are two versions of the software, one that operates
+with the SunOS 4.1.1 kernel and the other that operates with the Ultrix
+4.2a kernel (and probably the 4.3 kernel, although this has not been
+tested). A detailed description of the variables and algorithms is given
+in the hope that similar improvements can be incorporated in Unix
+kernels for other machines. The software itself is not included in this
+memo, since it involves licensed code. Detailed instructions on where to
+obtain it for either SunOS or Ultrix will be given separately.
+
+The principle function added to the SunOS and Ultrix kernels is to
+change the way the system clock is controlled, in order to provide
+precision time and frequency adjustments. Another function utilizes an
+undocumented counter in the DECstation hardware to provide precise time
+to the microsecond. This function can be used only with the DECstation
+5000/240 and possibly others that use the same input/output chipset.
+
+2. Design Principles
+
+In order to understand how these routines work, it is useful to consider
+how most Unix systems maintain the system clock. In the original design
+a hardware timer interrupts the kernel at some fixed rate, such as 100
+Hz in the SunOS kernel and 256 Hz in the Ultrix kernel. Since 256 does
+not evenly divide the second in microseconds, the kernel inserts 64 us
+once each second so that the system clock stays in step with real time.
+The time returned by the gettimeofday() routine is thus characterized by
+255 advances of 3906 us plus one of 3970 us.
+
+Also in the original design it is possible to slew the system clock to a
+new offset using the adjtime() system call. To do this the clock
+frequency is changed by adding or subtracting a fixed amount (tickadj)
+at each timer interrupt (tick) for a calculated number of ticks. Since
+this calculation involves dividing the requested offset by tickadj, it
+is possible to slew to a new offset with a precision only of tickadj,
+which is usually in the neighborhood of 5 us, but sometimes much higher.
+
+In order to maintain the system clock within specified bounds with this
+scheme, it is necessary to call adjtime() on a regular basis. For
+instance, let the bound be set at 100 us, which is a reasonable value
+for NTP-synchronized hosts on a local network, and let the onboard
+oscillator tolerance be 100 ppm, which is a reasonably conservative
+assumption. This requires that adjtime() be called at intervals not
+exceeding 1 second (s), which is in fact what the unmodified NTP
+software daemon does.
+
+In the modified kernel routines this scheme is replaced by another that
+extends the low-order bits of the system clock to provide very precise
+clock adjustments. At each timer interrupt a precisely calibrated time
+adjustment is added to the composite time value and overflows handled as
+required. The quantity to add is computed from the adjtime() call and,
+in addition a frequency adjustment, which is automatically calculated
+from previous time adjustments. This implementation operates as an
+adaptive-parameter, first-order, type-II, phase-lock loop (PLL), which
+in principle provides precision control of the system clock phase to
+within +-1 us and frequency to within +-5 nanoseconds (ns) per day.
+
+This PLL model is identical to the one implemented in NTP, except that
+in NTP the software daemon has to simulate the PLL using only the
+original adjtime() system call. The daemon is considerably complicated
+by the need to parcel time adjustments at frequent intervals in order to
+maintain the accuracy to specified bounds. The kernel routines do this
+directly, allowing vast gobs of ugly daemon code to be avoided at the
+expense of only a small amount of new code in the kernel. In fact, the
+amount of code added to the kernel for the new scheme is about the
+amount removed for the old scheme. The new adjtime() routine needs to be
+called only as each new time update is determined, which in NTP occurs
+at intervals of from 64 s to 1024 s. In addition, doing the frequency
+correction in the kernel means that the system time runs true even if
+the daemon were to cease operation or the network paths to the primary
+reference source fail.
+
+Note that the degree to which the adjtime() adjustment can be made is
+limited to a specific maximum value, presently +-128 milliseconds (ms),
+in order to achieve microsecond resolution. It is the intent in the
+design that settimeofday() be used for changes in system time greater
+than +-128 ms. It has been the Internet experience that the need to
+change the system time in increments greater than +-128 milliseconds is
+extremely rare and is usually associated with a hardware or software
+malfunction. Nevertheless, the limit applies to each adjtime() call and
+it is possible, but not recommended, that this routine is called at
+intervals smaller than 64 seconds, which is the NTP lower limit.
+
+For the most accurate and stable operation, adjtime() should be called
+at specified intervals; however, the PLL is quite forgiving and neither
+moderate loss of updates nor variations in the length of the interval is
+serious. The current engineering parameters have been optimized for
+intervals not greater than about 64 s. For larger intervals the PLL time
+constant can be adjusted to optimize the dynamic response up to
+intervals of 1024 s. Normally, this is automatically done by NTP. In any
+case, if updates are suspended, the PLL coasts at the frequency last
+determinated, which usually results in errors increasing only to a few
+tens of milliseconds over a day.
+
+The new code needs to know the initial frequency offset and time
+constant for the PLL, and the daemon needs to know the current frequency
+offset computed by the kernel for monitoring purposes. This is provided
+by a small change in the second argument of the kernel adjtime() calling
+sequence, which is documented later in this memo. Ordinarily, only the
+daemon will call the adjtime() routine, so the modified calling sequence
+is easily accommodated. Other than this change, the operation of
+adjtime() is transparent to the original.
+
+In the DECstation 5000/240 and possibly other models there happens to be
+an undocumented hardware register that counts system bus cycles at a
+rate of 25 MHz. The new kernel routines test for the CPU type and, in
+the case of the '240, use this register to interpolate system time
+between hardware timer interrupts. This results in a precision of +-1 us
+for all time values obtained via the gettimeofday() system call. This
+routine calls the kernel routine microtime(), which returns the actual
+interpolated value, but does not change the kernel time variable.
+Therefore, other kernel routines that access the kernel time variable
+directly and do not call either gettimeofday() or microtime() will
+continue their present behavior.
+
+The new kernel routines include provisions for error statistics (maximum
+error and estimated error), leap seconds and system clock status. These
+are intended to support applications that need such things; however,
+there are no applications other than the time-synchronization daemon
+itself that presently use them. At issue is the manner in which these
+data can be provided to application clients, such as new system calls
+and data interfaces. While a proposed interface is described later in
+this memo, it has not yet been implemented. This is an area for further
+study.
+
+While any time-synchronization daemon can in principle be modified to
+use the new code, the most likely will be users of the xntp3
+distribution of NTP. The code in the xntp3 distribution determines
+whether the new kernel code is in use and automatically reconfigures as
+required. When the new code is in use, the daemon reads the frequency
+offset from a file and provides it and the initial time constant via
+adjtime(). In subsequent calls to adjtime(), only the time adjustment
+and time constant are affected. The daemon reads the frequency from the
+kernel (returned as the second argument of adjtime()) at intervals of
+one hour and writes it to the file.
+
+3. Technical Description
+
+Following is a technical description of how the new scheme works in
+terms of the variables and algorithms involved. These components are
+discussed as a distinct entity and do not involve coding details
+specific to the Ultrix kernel. The algorithms involve only minor changes
+to the system clock and interval timer routines, but do not in
+themselves provide a conduit for application programs to learn the
+system clock status or statistics of the time-synchronization process.
+In a later section a number of new system calls are proposed to do this,
+along with an interface specification.
+
+The new scheme works like the companion simulator called kern.c and
+included in this directory. This stand-alone simulator includes code
+fragments identical to those in the modified kernel routines and
+operates in the same way. The system clock is implemented in the kernel
+using a set of variables and algorithms defined below and in the
+simulator. The algorithms are driven by explicit calls from the
+synchronization protocol as each time update is computed. The clock is
+read and set using the gettimeofday() and settimeofday() system calls,
+which operate in the same way as the originals, but return a status word
+describing the state of the system clock.
+
+Once the system clock has been set, the adjtime() system call is used to
+provide periodic updates including the time offset and possibly
+frequency offset and time constant. With NTP this occurs at intervals of
+from 64 s to 1024 s, deending on the time constant value. The kernel
+implements an adaptive-parameter, first-order, type-II, phase-lock loop
+(PLL) in order to integrate this offset into the phase and frequency of
+the system clock. The kernel keeps track of the time of the last update
+and adjusts the maximum error to grow by an amount equal to the
+oscillator frequency tolerance times the elapsed time since the last
+update.
+
+Occasionally, it is necessary to adjust the PLL parameters in response
+to environmental conditions, such as leap-second warning and oscillator
+stability observations. While the interface to do this has not yet been
+implemented, proposals to to that are included in a later section. A
+system call (setloop()) is used on such occasions to communicate these
+data. In addition, a system call (getloop())) is used to extract these
+data from the kernel for monitoring purposes.
+
+All programs utilize the system clock status variable time_status, which
+records whether the clock is synchronized, waiting for a leap second,
+etc. The value of this variable is returned by each system call. It can
+be set explicitly by the setloop() system call and implicitly by the
+settimeofday() system call and in the timer-interrupt routine. Values
+presently defined in the header file timex.h are as follows:
+
+int time_status = TIME_BAD; /* clock synchronization status */
+
+#define TIME_UNS 0 /* unspecified or unknown */
+#define TIME_OK 1 /* operation succeeded */
+#define TIME_INS 1 /* insert leap second at end of current day */
+#define TIME_DEL 2 /* delete leap second at end of current day */
+#define TIME_OOP 3 /* leap second in progress */
+#define TIME_BAD 4 /* system clock is not synchronized */
+#define TIME_ADR -1 /* operation failed: invalid address */
+#define TIME_VAL -2 /* operation failed: invalid argument */
+#define TIME_PRV -3 /* operation failed: priviledged operation */
+
+In case of a negative result code, the operation has failed; however,
+some variables may have been modified before the error was detected.
+Note that the new system calls never return a value of zero, so it is
+possible to determine whether the old routines or the new ones are in
+use. The syntax of the modified adjtime() is as follows:
+
+/*
+ * adjtime - adjuts system time
+ */
+#include <sys/timex.h>
+
+int gettimexofday(tp, fiddle)
+
+struct timeval *tp; /* system time adjustment*/
+struct timeval *fiddle; /* sneak path */
+
+On entry the "timeval" sneak path is coded:
+
+struct timeval {
+ long tv_sec = time_constant; /* time constant */
+ long tv_usec = time_freq; /* new frequency offset */
+}
+
+However, the sneak is ignored if fiddle is the null pointer and the new
+frequency offset is ignored if zero.
+
+The value returned on exit is the system clock status defined above. The
+"timeval" sneak path is modified as follows:
+
+struct timeval {
+ long tv_sec = time_precision; /* system clock precision */
+ long tv_usec = time_freq; /* current frequency offset */
+}
+
+3.1. Kernel Variables
+
+The following variables are used by the new code:
+
+long time_offset = 0; /* time adjustment (us) */
+
+This variable is used by the PLL to adjust the system time in small
+increments. It is scaled by (1 << SHIFT_UPDATE) in binary microseconds.
+The maximum value that can be represented is about +-130 ms and the
+minimum value or precision is about one nanosecond.
+
+long time_constant = SHIFT_TAU; /* pll time constant */
+
+This variable determines the bandwidth or "stiffness" of the PLL. It is
+used as a shift, with the effective value in positive powers of two. The
+optimum value for this variable is equal to 1/64 times the update
+interval. The default value SHIFT_TAU (0) corresponds to a PLL time
+constant of about one hour or an update interval of about one minute,
+which is appropriate for typical uncompensated quartz oscillators used
+in most computing equipment. Values larger than four are not useful,
+unless the local clock timebase is derived from a precision oscillator.
+
+long time_tolerance = MAXFREQ; /* frequency tolerance (ppm) */
+
+This variable represents the maximum frequency error or tolerance of the
+particular platform and is a property of the architecture. It is
+expressed as a positive number greater than zero in parts-per-million
+(ppm). The default MAXFREQ (100) is appropriate for conventional
+workstations.
+
+long time_precision = 1000000 / HZ; /* clock precision (us) */
+
+This variable represents the maximum error in reading the system clock.
+It is expressed as a positive number greater than zero in microseconds
+and is usually based on the number of microseconds between timer
+interrupts, in the case of the Ultrix kernel, 3906. However, in cases
+where the time can be interpolated between timer interrupts with
+microsecond resolution, the precision is specified as 1. This variable
+is computed by the kernel for use by the time-synchronization daemon,
+but is otherwise not used by the kernel.
+
+struct timeval time_maxerror; /* maximum error */
+
+This variable represents the maximum error, expressed as a Unix timeval,
+of the system clock. For NTP, it is computed as the synchronization
+distance, which is equal to one-half the root delay plus the root
+dispersion. It is increased by a small amount (time_tolerance) each
+second to reflect the clock frequency tolerance. This variable is
+computed by the time-synchronization daemon and the kernel for use by
+the application program, but is otherwise not used by the kernel.
+
+struct timeval time_esterror; /* estimated error */
+
+This variable represents the best estimate of the actual error,
+expressed as a Unix timeval, of the system clock based on its past
+behavior, together with observations of multiple clocks within the peer
+group. This variable is computed by the time-synchronization daemon for
+use by the application program, but is otherwise not used by the kernel.
+
+The PLL itself is controlled by the following variables:
+
+long time_phase = 0; /* phase offset (scaled us) */
+long time_freq = 0; /* frequency offset (scaled ppm) */long
+time_adj = 0; /* tick adjust (scaled 1 / HZ) */
+
+These variables control the phase increment and the frequency increment
+of the system clock at each tick of the clock. The time_phase variable
+is scaled by (1 << SHIFT_SCALE) in binary microseconds, giving a minimum
+value (time resolution) of 9.3e-10 us. The time_freq variable is scaled
+by (1 << SHIFT_KF) in parts-per-million (ppm), giving it a maximum value
+of about +-130 ppm and a minimum value (frequency resolution) of 6e-8
+ppm. The time_adj variable is the actual phase increment in scaled
+microseconds to add to time_phase once each tick. It is computed from
+time_phase and time_freq once per second.
+
+long time_reftime = 0; /* time at last adjustment (s) */
+
+This variable is the second's portion of the system time on the last
+call to adjtime(). It is used to adjust the time_freq variable as the
+time since the last update increases.
+
+The HZ define establishes the timer interrupt frequency, 256 Hz for the
+Ultrix kernel and 100 Hz for the SunOS kernel. The SHIFT_HZ define
+expresses the same value as the nearest power of two in order to avoid
+hardware multiply operations. These are the only parameters that need to
+be changed for different timer interrupt rates.
+
+#define HZ 256 /* timer interrupt frequency (Hz) */
+#define SHIFT_HZ 8 /* log2(HZ) */
+
+The following defines establish the engineering parameters of the PLL
+model. They are chosen for an initial convergence time of about an hour,
+an overshoot of about seven percent and a final convergence time of
+several hours, depending on initial frequency error.
+
+#define SHIFT_KG 10 /* shift for phase increment */
+#define SHIFT_KF 24 /* shift for frequency increment */
+#define SHIFT_TAU 0 /* default time constant (shift) */
+
+The SHIFT_SCALE define establishes the decimal point on the time_phase
+variable which serves as a an extension to the low-order bits of the
+system clock variable. The SHIFT_UPDATE define establishes the decimal
+point of the phase portion of the adjtime() update. The FINEUSEC define
+represents 1 us in scaled units.
+
+#define SHIFT_SCALE 28 /* shift for scale factor */
+#define SHIFT_UPDATE 14 /* shift for offset scale factor */
+#define FINEUSEC (1 << SHIFT_SCALE) /* 1 us in scaled units */
+
+The FINETUNE define represents the residual, in ppm, to be added to the
+system clock variable in addition to the integral 1-us value given by
+tick. This allows a systematic frequency offset in cases where the timer
+interrupt frequency does not exactly divide the second in microseconds.
+
+#define FINETUNE (1000000 - (1000000 / HZ) * HZ) /* frequency adjustment
+ * for non-isochronous HZ (ppm) */
+
+The following four defines establish the performance envelope of the
+PLL, one to bound the maximum phase error, another to bound the maximum
+frequency error and the last two to bound the minimum and maximum time
+between updates. The intent of these bounds is to force the PLL to
+operate within predefined limits in order to conform to the correctness
+models assumed by time-synchronization protocols like NTP and DTSS. An
+excursion which exceeds these bounds is clamped to the bound and
+operation proceeds accordingly. In practice, this can occur only if
+something has failed or is operating out of tolerance, but otherwise the
+PLL continues to operate in a stable mode. Note that the MAXPHASE define
+conforms to the maximum offset allowed in NTP before the system time is
+reset, rather than incrementally adjusted.
+
+#define MAXPHASE 128000 /* max phase error (us) */
+#define MINSEC 64 /* min interval between updates (s) */
+#define MAXFREQ 100 /* max frequency error (ppm) */
+#define MAXSEC 1024 /* max interval between updates (s) */
+
+3.2. Code Segments
+
+The code segments illustrated in the simulator should make clear the
+operations at various points in the code. These segments are not derived
+from any licensed code. The hardupdate() fragment is called by adjtime()
+to update the system clock phase and frequency. This is an
+implementation of an adaptive-parameter, first-order, type-II phase-lock
+loop. Note that the time constant is in units of powers of two, so that
+multiplies can be done by simple shifts. The phase variable is computed
+as the offset multiplied by the time constant. Then, the time since the
+last update is computed and clamped to a maximum (for robustness) and to
+zero if initializing. The offset is multiplied (sorry about the ugly
+multiply) by the result and by the square of the time constant and then
+added to the frequency variable. Finally, the frequency variable is
+clamped not to exceed the tolerance. Note that all shifts are assumed to
+be positive and that a shift of a signed quantity to the right requires
+a litle dance.
+
+With the defines given, the maximum time offset is determined by the
+size in bits of the long type (32) less the SHIFT_UPDATE (14) scale
+factor or 18 bits (signed). The scale factor is chosen so that there is
+no loss of significance in later steps, which may involve a right shift
+up to 14 bits. This results in a maximum offset of about +-130 ms. Since
+the time_constant must be greater than or equal to zero, the maximum
+frequency offset is determined by the SHIFT_KF (24) scale factor, or
+about +-130 ppm. In the addition step the value of offset * mtemp is
+represented in 18 + 10 = 28 bits, which will not overflow a long add.
+There could be a loss of precision due to the right shift of up to eight
+bits, since time_constant is bounded at four. This results in a net
+worst-case frequency error of about 2^-16 us or well down into the
+oscillator phase noise. While the time_offset value is assumed checked
+before entry, the time_phase variable is an accumulator, so is clamped
+to the tolerance on every call. This helps to damp transients before the
+oscillator frequency has been determined, as well as to satisfy the
+correctness assertions if the time-synchronization protocol comes
+unstuck.
+
+The hardclock() fragment is inserted in the hardware timer interrupt
+routine at the point the system clock is to be incremented. The phase
+adjustment (time_adj) is added to the clock phase (time_phase) and
+tested for overflow of the microsecond. If an overflow occurs, the
+microsecond (tick) in incremented or decremented.
+
+The second_overflow() fragment is inserted at the point where the
+microseconds field of the system time variable is being checked for
+overflow. On rollover of the second the maximum error is increased by
+the tolerance. The time offset is divided by the phase weight (SHIFT_KG)
+and time constant. The time offset is then reduced by the result and the
+result is scaled and becomes the value of the phase adjustment. The
+phase adjustment is then corrected for the calculated frequency offset
+and a fixed offset FINETUNE which is a property of the architecture. On
+rollover of the day the leap-warning indicator is checked and the
+apparent time adjusted +-1 s accordingly. The gettimeofday() routine
+insures that the reported time is always monotonically increasing.
+
+The simulator can be used to check the loop operation over the design
+range of +-128 ms in time error and +-100 ppm in frequency error. This
+confirms that no overflows occur and that the loop initially converges
+in about 50-60 minutes for timer interrupt rates from 50 Hz to 1024 Hz.
+The loop has a normal overshoot of about seven percent and a final
+convergence time of several hours, depending on the initional frequency
+error.
+
+3.3. Leap Seconds
+
+The leap-warning condition is determined by the synchronization protocol
+(if remotely synchronized), by the timecode receiver (if available), or
+by the operator (if awake). The time_status value must be set on the day
+the leap event is to occur (30 June or 31 December) and is automatically
+reset after the event. If the value is TIME_DEL, the kernel adds one
+second to the system time immediately following second 23:59:58 and
+resets time_status to TIME_OK. If the value is TIME_INS, the kernel
+subtracts one second from the system time immediately following second
+23:59:59 and resets time_status to TIME_OOP, in effect causing system
+time to repeat second 59. Immediately following the repeated second, the
+kernel resets time_status to TIME_OK.
+
+Depending upon the system call implementation, the reported time during
+a leap second may repeat (with a return code set to advertise that fact)
+or be monotonically adjusted until system time "catches up" to reported
+time. With the latter scheme the reported time will be correct before
+and after the leap second, but freeze or slowly advance during the leap
+second itself. However, Most programs will probably use the ctime()
+library routine to convert from timeval (seconds, microseconds) format
+to tm format (seconds, minutes,...). If this routine is modified to
+inspect the return code of the gettimeofday() routine, it could simply
+report the leap second as second 60.
+
+To determine local midnight without fuss, the kernel simply finds the
+residue of the time.tv_sec value mod 86,400, but this requires a messy
+divide. Probably a better way to do this is to initialize an auxiliary
+counter in the settimeofday() routine using an ugly divide and increment
+the counter at the same time the time.tv_sec is incremented in the timer
+interrupt routine. For future embellishment.
+
+4. Proposed Application Program Interface
+
+Most programs read the system clock using the gettimeofday() system
+call, which returns the system time and time-zone data. In the modified
+5000/240 kernel, the gettimeofday() routine calls the microtime()
+routine, which interpolates between hardware timer interrupts to a
+precision of +-1 microsecond. However, the synchronization protocol
+provides additional information that will be of interest in many
+applications. For some applications it is necessary to know the maximum
+error of the reported time due to all causes, including those due to the
+system clock reading error, oscillator frequency error and accumulated
+errors due to intervening time servers on the path to a primary
+reference source. However, for those protocols that adjust the system
+clock frequency as well as the time offset, the errors expected in
+actual use will almost always be much less than the maximum error.
+Therefore, it is useful to report the estimated error, as well as the
+maximum error.
+
+It does not seem useful to provide additional details private to the
+kernel and synchronization protocol, such as stratum, reference
+identifier, reference timestamp and so forth. It would in principle be
+possible for the application to independently evaluate the quality of
+time and project into the future how long this time might be "valid."
+However, to do that properly would duplicate the functionality of the
+synchronization protocol and require knowledge of many mundane details
+of the platform architecture, such as the tick value, reachability
+status and related variables. Therefore, the application interface does
+not reveal anything except the time, timezone and error data.
+
+With respect to NTP, the data maintained by the protocol include the
+roundtrip delay and total dispersion to the source of synchronization.
+In terms of the above, the maximum error is computed as half the delay
+plus the dispersion, while the estimated error is equal to the
+dispersion. These are reported in timeval structures. A new system call
+is proposed that includes all the data in the gettimeofday() plus the
+two new timeval structures.
+
+The proposed interface involves modifications to the gettimeofday(),
+settimeofday() and adjtime() system calls, as well as new system calls
+to get and set various system parameters. In order to minimize
+confusion, by convention the new system calls are named with an "x"
+following the "time"; e.g., adjtime() becomes adjtimex(). The operation
+of the modified gettimexofday(), settimexofday() and adjtimex() system
+calls is identical to that of their prototypes, except for the error
+quantities and certain other side effects, as documented below. By
+convention, a NULL pointer can be used in place of any argument, in
+which case the argument is ignored.
+
+The synchronization protocol daemon needs to set and adjust the system
+clock and certain other kernel variables. It needs to read these
+variables for monitoring purposes as well. The present list of these
+include a subset of the variables defined previously:
+
+long time_precision
+long time_timeconstant
+long time_tolerance
+long time_freq
+long time_status
+
+/*
+ * gettimexofday, settimexofday - get/set date and time
+ */
+#include <sys/timex.h>
+
+int gettimexofday(tp, tzp, tmaxp, testp)
+
+struct timeval *tp; /* system time */
+struct timezone *tzp; /* timezone */
+struct timeval *tmaxp; /* maximum error */
+struct timeval *testp; /* estimated error */
+
+The settimeofday() syntax is identical. Note that a call to
+settimexofday() automatically results in the system being declared
+unsynchronized (TIME_BAD return code), since the synchronization
+condition can only be achieved by the synchronization daemon using an
+internal or external primary reference source and the adjtimex() system
+call.
+
+/*
+ * adjtimex - adjust system time
+ */
+#include <sys/timex.h>
+
+int adjtimex(tp, tzp, freq, tc)
+
+struct timeval *tp; /* system time */
+struct timezone *tzp; /* timezone */
+long freq; /* frequency adjustment */
+long tc; /* time constant */
+
+/*
+ * getloop, setloop - get/set kernel time variables
+ */
+#include <sys/timex.h>
+
+int getloop(code, argp)
+
+int code; /* operation code */
+long *argp; /* argument pointer */
+
+The paticular kernal variables affected by these routines are selected
+by the operation code. Values presently defined in the header file
+timex.h are as follows:
+
+#define TIME_PREC 1 /* precision (log2(sec)) */
+#define TIME_TCON 2 /* time constant (log2(sec) */
+#define TIME_FREQ 3 /* frequency tolerance */
+#define TIME_FREQ 4 /* frequency offset (scaled) */
+#define TIME_STAT 5 /* status (see return codes) */
+
+The getloop() syntax is identical.
+
+Comments welcome, but very little support is available:
+
+David L. Mills
+Electrical Engineering Department
+University of Delaware
+Newark, DE 19716
+302 831 8247 fax 302 831 4316
+mills@udel.edu
diff --git a/usr.sbin/xntpd/kernel/chuinit.c b/usr.sbin/xntpd/kernel/chuinit.c
new file mode 100644
index 0000000..80b36a9
--- /dev/null
+++ b/usr.sbin/xntpd/kernel/chuinit.c
@@ -0,0 +1,76 @@
+/*
+** dynamically loadable chu driver
+**
+** $Header: /home/ncvs/src/usr.sbin/xntpd/kernel/chuinit.c,v 1.1.1.1 1994/09/29 23:02:42 wollman Exp $
+**
+** william robertson <rob@agate.berkeley.edu>
+*/
+
+#include <sys/types.h>
+#include <sys/conf.h>
+#include <sys/errno.h>
+#include <sys/stream.h>
+#include <sys/syslog.h>
+
+#include <sun/openprom.h>
+#include <sun/vddrv.h>
+
+extern int findmod(); /* os/str_io.c */
+
+extern struct streamtab chuinfo;
+
+struct vdldrv vd = {
+ VDMAGIC_USER,
+ "chu"
+ };
+
+
+int
+xxxinit(function_code, vdp, vdi, vds)
+unsigned int function_code;
+struct vddrv *vdp;
+addr_t vdi;
+struct vdstat *vds;
+{
+ register int i = 0;
+ register int j;
+
+ switch (function_code) {
+ case VDLOAD:
+
+ if (findmod("chu") >= 0) {
+ log(LOG_ERR, "chu stream module already loaded\n");
+ return (EADDRINUSE);
+ }
+
+ i = findmod("\0");
+
+ if (i == -1 || fmodsw[i].f_name[0] != '\0')
+ return(-1);
+
+ for (j = 0; vd.Drv_name[j] != '\0'; j++) /* XXX check bounds */
+ fmodsw[i].f_name[j] = vd.Drv_name[j];
+
+ fmodsw[i].f_name[j] = '\0';
+ fmodsw[i].f_str = &chuinfo;
+
+ vdp->vdd_vdtab = (struct vdlinkage *) &vd;
+
+ return(0);
+
+ case VDUNLOAD:
+ if ((i = findmod(vd.Drv_name)) == -1)
+ return(-1);
+
+ fmodsw[i].f_name[0] = '\0';
+ fmodsw[i].f_str = 0;
+
+ return(0);
+
+ case VDSTAT:
+ return(0);
+
+ default:
+ return(EIO);
+ }
+}
diff --git a/usr.sbin/xntpd/kernel/clkinit.c b/usr.sbin/xntpd/kernel/clkinit.c
new file mode 100644
index 0000000..b8fe456
--- /dev/null
+++ b/usr.sbin/xntpd/kernel/clkinit.c
@@ -0,0 +1,76 @@
+/*
+** dynamically loadable clk driver
+**
+** $Header: /home/ncvs/src/usr.sbin/xntpd/kernel/clkinit.c,v 1.1.1.1 1994/09/29 23:02:47 wollman Exp $
+**
+** william robertson <rob@agate.berkeley.edu>
+*/
+
+#include <sys/types.h>
+#include <sys/conf.h>
+#include <sys/errno.h>
+#include <sys/stream.h>
+#include <sys/syslog.h>
+
+#include <sun/openprom.h>
+#include <sun/vddrv.h>
+
+extern int findmod(); /* os/str_io.c */
+
+extern struct streamtab clkinfo;
+
+struct vdldrv vd = {
+ VDMAGIC_USER,
+ "clk"
+ };
+
+
+int
+xxxinit(function_code, vdp, vdi, vds)
+unsigned int function_code;
+struct vddrv *vdp;
+addr_t vdi;
+struct vdstat *vds;
+{
+ register int i = 0;
+ register int j;
+
+ switch (function_code) {
+ case VDLOAD:
+
+ if (findmod("clk") >= 0) {
+ log(LOG_ERR, "clk stream module already loaded\n");
+ return (EADDRINUSE);
+ }
+
+ i = findmod("\0");
+
+ if (i == -1 || fmodsw[i].f_name[0] != '\0')
+ return(-1);
+
+ for (j = 0; vd.Drv_name[j] != '\0'; j++) /* XXX check bounds */
+ fmodsw[i].f_name[j] = vd.Drv_name[j];
+
+ fmodsw[i].f_name[j] = '\0';
+ fmodsw[i].f_str = &clkinfo;
+
+ vdp->vdd_vdtab = (struct vdlinkage *) &vd;
+
+ return(0);
+
+ case VDUNLOAD:
+ if ((i = findmod(vd.Drv_name)) == -1)
+ return(-1);
+
+ fmodsw[i].f_name[0] = '\0';
+ fmodsw[i].f_str = 0;
+
+ return(0);
+
+ case VDSTAT:
+ return(0);
+
+ default:
+ return(EIO);
+ }
+}
diff --git a/usr.sbin/xntpd/lib/Makefile b/usr.sbin/xntpd/lib/Makefile
new file mode 100644
index 0000000..948e66d
--- /dev/null
+++ b/usr.sbin/xntpd/lib/Makefile
@@ -0,0 +1,35 @@
+#
+# $Id$
+#
+
+CFLAGS+= -I${.CURDIR}/../include
+
+SRCS= atoint.c atolfp.c atouint.c auth12crypt.c authdecrypt.c authdes.c \
+ authencrypt.c authkeys.c authparity.c authreadkeys.c authusekey.c \
+ buftvtots.c caljulian.c calleapwhen.c caltontp.c calyearstart.c \
+ clocktime.c dofptoa.c dolfptoa.c emalloc.c fptoa.c fptoms.c \
+ gettstamp.c hextoint.c hextolfp.c humandate.c inttoa.c \
+ lib_strbuf.c mfptoa.c mfptoms.c modetoa.c mstolfp.c \
+ msutotsf.c netof.c numtoa.c refnumtoa.c numtohost.c octtoint.c \
+ prettydate.c ranny.c tsftomsu.c tstotv.c tvtoa.c tvtots.c \
+ uglydate.c uinttoa.c utvtoa.c clocktypes.c \
+ md5.c a_md5encrypt.c a_md5decrypt.c \
+ a_md512crypt.c decodenetnum.c systime.c msyslog.c syssignal.c \
+ findconfig.c getopt.c
+
+NOMAN=
+NOPROFILE=
+LIB= ntp
+CLEANFILES+=authdes.c
+
+.if !defined(NOCRYPT) && exists(${.CURDIR}/../../../secure/usr.sbin/xntpd/lib)
+.PATH: ${.CURDIR}/../../../secure/usr.sbin/xntpd/lib
+.else
+authdes.c: authdes.c.export
+ cp ${.CURDIR}/authdes.c.export authdes.c
+.endif
+
+# don't install this anywhere
+install:
+
+.include <bsd.lib.mk>
diff --git a/usr.sbin/xntpd/lib/README b/usr.sbin/xntpd/lib/README
new file mode 100644
index 0000000..c2b65d9
--- /dev/null
+++ b/usr.sbin/xntpd/lib/README
@@ -0,0 +1,5 @@
+README file for directory ./lib of the NTP Version 3 distribution
+
+This directory contains the sources for the NTP library used by most
+programs in this distribution. See the README and RELNOTES files in the
+parent directory for directions on how to make this library.
diff --git a/usr.sbin/xntpd/lib/a_md512crypt.c b/usr.sbin/xntpd/lib/a_md512crypt.c
new file mode 100644
index 0000000..515d83b
--- /dev/null
+++ b/usr.sbin/xntpd/lib/a_md512crypt.c
@@ -0,0 +1,86 @@
+/*
+ * md5crypt - MD5 based authentication routines
+ */
+
+#include "ntp_types.h"
+#include "ntp_string.h"
+#include "md5.h"
+#include "ntp_stdlib.h"
+
+extern u_long cache_keyid;
+extern char *cache_key;
+extern int cache_keylen;
+
+/*
+ * Stat counters, imported from data base module
+ */
+extern U_LONG authencryptions;
+extern U_LONG authdecryptions;
+extern U_LONG authkeyuncached;
+extern U_LONG authnokey;
+
+/*
+ * For our purposes an NTP packet looks like:
+ *
+ * a variable amount of encrypted data, multiple of 8 bytes, followed by:
+ * NOCRYPT_OCTETS worth of unencrypted data, followed by:
+ * BLOCK_OCTETS worth of ciphered checksum.
+ */
+#define NOCRYPT_OCTETS 4
+#define BLOCK_OCTETS 16
+
+#define NOCRYPT_LONGS ((NOCRYPT_OCTETS)/sizeof(U_LONG))
+#define BLOCK_LONGS ((BLOCK_OCTETS)/sizeof(U_LONG))
+
+static MD5_CTX ctx;
+
+/*
+ * Do first stage of a two stage authenticator generation.
+ */
+
+void
+MD5auth1crypt(keyno, pkt, length)
+ u_long keyno;
+ U_LONG *pkt;
+ int length; /* length of all encrypted data */
+{
+
+ authencryptions++;
+
+ if (keyno != cache_keyid) {
+ authkeyuncached++;
+ if (!authhavekey(keyno)) {
+ authnokey++;
+ return;
+ }
+ }
+
+ MD5Init(&ctx);
+ MD5Update(&ctx, cache_key, cache_keylen);
+ MD5Update(&ctx, (char *)pkt, length - 8);
+ /* just leave the partially computed value in the static MD5_CTX */
+}
+
+/*
+ * Do second state of a two stage authenticator generation.
+ */
+int
+MD5auth2crypt(keyno, pkt, length)
+ u_long keyno;
+ U_LONG *pkt;
+ int length; /* total length of encrypted area */
+{
+ /*
+ * Don't bother checking the keys. The first stage would have
+ * handled that. Finish up the generation by also including the
+ * last 8 bytes of the data area.
+ */
+
+ MD5Update(&ctx, (char *)(pkt) + length - 8, 8);
+ MD5Final(&ctx);
+
+ memmove((char *) &pkt[NOCRYPT_LONGS + length/sizeof(U_LONG)],
+ (char *) ctx.digest,
+ BLOCK_OCTETS);
+ return (4 + BLOCK_OCTETS);
+}
diff --git a/usr.sbin/xntpd/lib/a_md5decrypt.c b/usr.sbin/xntpd/lib/a_md5decrypt.c
new file mode 100644
index 0000000..958c21f
--- /dev/null
+++ b/usr.sbin/xntpd/lib/a_md5decrypt.c
@@ -0,0 +1,59 @@
+/*
+ * md5crypt - MD5 based authentication routines
+ */
+
+#include "ntp_types.h"
+#include "ntp_string.h"
+#include "md5.h"
+#include "ntp_stdlib.h"
+
+extern u_long cache_keyid;
+extern char *cache_key;
+extern int cache_keylen;
+
+/*
+ * Stat counters, imported from data base module
+ */
+extern U_LONG authencryptions;
+extern U_LONG authdecryptions;
+extern U_LONG authkeyuncached;
+extern U_LONG authnokey;
+
+/*
+ * For our purposes an NTP packet looks like:
+ *
+ * a variable amount of encrypted data, multiple of 8 bytes, followed by:
+ * NOCRYPT_OCTETS worth of unencrypted data, followed by:
+ * BLOCK_OCTETS worth of ciphered checksum.
+ */
+#define NOCRYPT_OCTETS 4
+#define BLOCK_OCTETS 16
+
+#define NOCRYPT_LONGS ((NOCRYPT_OCTETS)/sizeof(U_LONG))
+#define BLOCK_LONGS ((BLOCK_OCTETS)/sizeof(U_LONG))
+
+int
+MD5authdecrypt(keyno, pkt, length)
+ u_long keyno;
+ const U_LONG *pkt;
+ int length; /* length of variable data in octets */
+{
+ MD5_CTX ctx;
+
+ authdecryptions++;
+
+ if (keyno != cache_keyid) {
+ authkeyuncached++;
+ if (!authhavekey(keyno))
+ return 0;
+ }
+
+ MD5Init(&ctx);
+ MD5Update(&ctx, cache_key, cache_keylen);
+ MD5Update(&ctx, (char *)pkt, length);
+ MD5Final(&ctx);
+
+ return (!memcmp((char *)ctx.digest,
+ (char *)pkt + length + 4,
+ BLOCK_OCTETS));
+}
diff --git a/usr.sbin/xntpd/lib/a_md5encrypt.c b/usr.sbin/xntpd/lib/a_md5encrypt.c
new file mode 100644
index 0000000..6fae6fb
--- /dev/null
+++ b/usr.sbin/xntpd/lib/a_md5encrypt.c
@@ -0,0 +1,69 @@
+/*
+ * md5crypt - MD5 based authentication routines
+ */
+
+#include "ntp_types.h"
+#include "ntp_string.h"
+#include "md5.h"
+#include "ntp_stdlib.h"
+
+extern u_long cache_keyid;
+extern char *cache_key;
+extern int cache_keylen;
+
+/*
+ * Stat counters, imported from data base module
+ */
+extern U_LONG authencryptions;
+extern U_LONG authdecryptions;
+extern U_LONG authkeyuncached;
+extern U_LONG authnokey;
+
+/*
+ * For our purposes an NTP packet looks like:
+ *
+ * a variable amount of encrypted data, multiple of 8 bytes, followed by:
+ * NOCRYPT_OCTETS worth of unencrypted data, followed by:
+ * BLOCK_OCTETS worth of ciphered checksum.
+ */
+#define NOCRYPT_OCTETS 4
+#define BLOCK_OCTETS 16
+
+#define NOCRYPT_LONGS ((NOCRYPT_OCTETS)/sizeof(U_LONG))
+#define BLOCK_LONGS ((BLOCK_OCTETS)/sizeof(U_LONG))
+
+
+int
+MD5authencrypt(keyno, pkt, length)
+ u_long keyno;
+ U_LONG *pkt;
+ int length; /* length of encrypted portion of packet */
+{
+ MD5_CTX ctx;
+ int len; /* in 4 byte quantities */
+
+ authencryptions++;
+
+ if (keyno != cache_keyid) {
+ authkeyuncached++;
+ if (!authhavekey(keyno)) {
+ authnokey++;
+ return 0;
+ }
+ }
+
+ len = length / sizeof(U_LONG);
+
+ /*
+ * Generate the authenticator.
+ */
+ MD5Init(&ctx);
+ MD5Update(&ctx, cache_key, cache_keylen);
+ MD5Update(&ctx, (char *)pkt, length);
+ MD5Final(&ctx);
+
+ memmove((char *)&pkt[NOCRYPT_LONGS + len],
+ (char *)ctx.digest,
+ BLOCK_OCTETS);
+ return (4 + BLOCK_OCTETS); /* return size of key and MAC */
+}
diff --git a/usr.sbin/xntpd/lib/adjtimex.c b/usr.sbin/xntpd/lib/adjtimex.c
new file mode 100644
index 0000000..03e9d79
--- /dev/null
+++ b/usr.sbin/xntpd/lib/adjtimex.c
@@ -0,0 +1,15 @@
+/*
+ * Copyright (c) 1983 Regents of the University of California.
+ * All rights reserved. The Berkeley software License Agreement
+ * specifies the terms and conditions for redistribution.
+ */
+
+#ifndef lint
+_sccsid:.asciz "11/19/91 ULTRIX @(#)adjtime.c 6.1"
+#endif not lint
+
+#include "SYS.h"
+
+SYSCALL(adjtimex)
+ ret
+
diff --git a/usr.sbin/xntpd/lib/atoint.c b/usr.sbin/xntpd/lib/atoint.c
new file mode 100644
index 0000000..57abd44
--- /dev/null
+++ b/usr.sbin/xntpd/lib/atoint.c
@@ -0,0 +1,48 @@
+/*
+ * atoint - convert an ascii string to a signed long, with error checking
+ */
+#include <sys/types.h>
+#include <ctype.h>
+
+#include "ntp_types.h"
+
+int
+atoint(str, ival)
+ const char *str;
+ long *ival;
+{
+ register long u;
+ register const char *cp;
+ register int isneg;
+ register int oflow_digit;
+
+ cp = str;
+
+ if (*cp == '-') {
+ cp++;
+ isneg = 1;
+ oflow_digit = '8';
+ } else {
+ isneg = 0;
+ oflow_digit = '7';
+ }
+
+ if (*cp == '\0')
+ return 0;
+
+ u = 0;
+ while (*cp != '\0') {
+ if (!isdigit(*cp))
+ return 0;
+ if (u > 214748364 || (u == 214748364 && *cp > oflow_digit))
+ return 0; /* overflow */
+ u = (u << 3) + (u << 1);
+ u += *cp++ - '0'; /* ascii dependent */
+ }
+
+ if (isneg)
+ *ival = -u;
+ else
+ *ival = u;
+ return 1;
+}
diff --git a/usr.sbin/xntpd/lib/atolfp.c b/usr.sbin/xntpd/lib/atolfp.c
new file mode 100644
index 0000000..644a38c
--- /dev/null
+++ b/usr.sbin/xntpd/lib/atolfp.c
@@ -0,0 +1,117 @@
+/*
+ * atolfp - convert an ascii string to an l_fp number
+ */
+#include <stdio.h>
+#include <ctype.h>
+
+#include "ntp_fp.h"
+#include "ntp_string.h"
+
+/*
+ * Powers of 10
+ */
+static u_long ten_to_the_n[10] = {
+ 0,
+ 10,
+ 100,
+ 1000,
+ 10000,
+ 100000,
+ 1000000,
+ 10000000,
+ 100000000,
+ 1000000000,
+};
+
+
+int
+atolfp(str, lfp)
+ const char *str;
+ l_fp *lfp;
+{
+ register const char *cp;
+ register u_long dec_i;
+ register u_long dec_f;
+ char *ind;
+ int ndec;
+ int isneg;
+ static char *digits = "0123456789";
+
+ isneg = 0;
+ dec_i = dec_f = 0;
+ ndec = 0;
+ cp = str;
+
+ /*
+ * We understand numbers of the form:
+ *
+ * [spaces][-|+][digits][.][digits][spaces|\n|\0]
+ */
+ while (isspace(*cp))
+ cp++;
+
+ if (*cp == '-') {
+ cp++;
+ isneg = 1;
+ }
+
+ if (*cp == '+')
+ cp++;
+
+ if (*cp != '.' && !isdigit(*cp))
+ return 0;
+
+ while (*cp != '\0' && (ind = strchr(digits, *cp)) != NULL) {
+ dec_i = (dec_i << 3) + (dec_i << 1); /* multiply by 10 */
+ dec_i += (ind - digits);
+ cp++;
+ }
+
+ if (*cp != '\0' && !isspace(*cp)) {
+ if (*cp++ != '.')
+ return 0;
+
+ while (ndec < 9 && *cp != '\0'
+ && (ind = strchr(digits, *cp)) != NULL) {
+ ndec++;
+ dec_f = (dec_f << 3) + (dec_f << 1); /* *10 */
+ dec_f += (ind - digits);
+ cp++;
+ }
+
+ while (isdigit(*cp))
+ cp++;
+
+ if (*cp != '\0' && !isspace(*cp))
+ return 0;
+ }
+
+ if (ndec > 0) {
+ register u_long tmp;
+ register u_long bit;
+ register u_long ten_fact;
+
+ ten_fact = ten_to_the_n[ndec];
+
+ tmp = 0;
+ bit = 0x80000000;
+ while (bit != 0) {
+ dec_f <<= 1;
+ if (dec_f >= ten_fact) {
+ tmp |= bit;
+ dec_f -= ten_fact;
+ }
+ bit >>= 1;
+ }
+ if ((dec_f << 1) > ten_fact)
+ tmp++;
+ dec_f = tmp;
+ }
+
+ if (isneg)
+ M_NEG(dec_i, dec_f);
+
+ lfp->l_ui = dec_i;
+ lfp->l_uf = dec_f;
+ return 1;
+}
diff --git a/usr.sbin/xntpd/lib/atouint.c b/usr.sbin/xntpd/lib/atouint.c
new file mode 100644
index 0000000..d826bb4
--- /dev/null
+++ b/usr.sbin/xntpd/lib/atouint.c
@@ -0,0 +1,33 @@
+/*
+ * atouint - convert an ascii string to an unsigned long, with error checking
+ */
+#include <sys/types.h>
+#include <ctype.h>
+
+#include "ntp_types.h"
+
+int
+atouint(str, uval)
+ const char *str;
+ u_long *uval;
+{
+ register u_long u;
+ register const char *cp;
+
+ cp = str;
+ if (*cp == '\0')
+ return 0;
+
+ u = 0;
+ while (*cp != '\0') {
+ if (!isdigit(*cp))
+ return 0;
+ if (u > 429496729 || (u == 429496729 && *cp >= '6'))
+ return 0; /* overflow */
+ u = (u << 3) + (u << 1);
+ u += *cp++ - '0'; /* ascii dependent */
+ }
+
+ *uval = u;
+ return 1;
+}
diff --git a/usr.sbin/xntpd/lib/auth12crypt.c b/usr.sbin/xntpd/lib/auth12crypt.c
new file mode 100644
index 0000000..7d69122
--- /dev/null
+++ b/usr.sbin/xntpd/lib/auth12crypt.c
@@ -0,0 +1,125 @@
+/*
+ * auth12crypt.c - routines to support two stage NTP encryption
+ */
+#include "ntp_stdlib.h"
+
+/*
+ * For our purposes an NTP packet looks like:
+ *
+ * a variable amount of encrypted data, multiple of 8 bytes, which
+ * is encrypted in pass 1, followed by:
+ * an 8 byte chunk of data which is encrypted in pass 2
+ * NOCRYPT_OCTETS worth of unencrypted data, followed by:
+ * BLOCK_OCTETS worth of ciphered checksum.
+ */
+#define NOCRYPT_OCTETS 4
+#define BLOCK_OCTETS 8
+
+#define NOCRYPT_LONGS ((NOCRYPT_OCTETS)/sizeof(U_LONG))
+#define BLOCK_LONGS ((BLOCK_OCTETS)/sizeof(U_LONG))
+
+/*
+ * Imported from the key data base module
+ */
+extern u_long cache_keyid; /* cached key ID */
+extern u_char DEScache_ekeys[]; /* cached decryption keys */
+extern u_char DESzeroekeys[]; /* zero key decryption keys */
+
+/*
+ * Stat counters, from the database module
+ */
+extern U_LONG authencryptions;
+extern U_LONG authkeyuncached;
+extern U_LONG authnokey;
+
+
+/*
+ * auth1crypt - do the first stage of a two stage encryption
+ */
+void
+DESauth1crypt(keyno, pkt, length)
+ u_long keyno;
+ U_LONG *pkt;
+ int length; /* length of all encrypted data */
+{
+ register U_LONG *pd;
+ register int i;
+ register u_char *keys;
+ U_LONG work[2];
+
+ authencryptions++;
+
+ if (keyno == 0) {
+ keys = DESzeroekeys;
+ } else {
+ if (keyno != cache_keyid) {
+ authkeyuncached++;
+ if (!authhavekey(keyno)) {
+ authnokey++;
+ return;
+ }
+ }
+ keys = DEScache_ekeys;
+ }
+
+ /*
+ * Do the first five encryptions. Stick the intermediate result
+ * in the mac field. The sixth encryption must wait until the
+ * caller freezes a transmit time stamp, and will be done in stage 2.
+ */
+ pd = pkt;
+ work[0] = work[1] = 0;
+
+ for (i = (length/BLOCK_OCTETS - 1); i > 0; i--) {
+ work[0] ^= *pd++;
+ work[1] ^= *pd++;
+ DESauth_des(work, keys);
+ }
+
+ /*
+ * Space to the end of the packet and stick the intermediate
+ * result in the mac field.
+ */
+ pd += BLOCK_LONGS + NOCRYPT_LONGS;
+ *pd++ = work[0];
+ *pd = work[1];
+}
+
+
+/*
+ * auth2crypt - do the second stage of a two stage encryption
+ */
+int
+DESauth2crypt(keyno, pkt, length)
+ u_long keyno;
+ U_LONG *pkt;
+ int length; /* total length of encrypted area */
+{
+ register U_LONG *pd;
+ register u_char *keys;
+
+ /*
+ * Skip the key check. The call to the first stage should
+ * have got it.
+ */
+ if (keyno == 0)
+ keys = DESzeroekeys;
+ else
+ keys = DEScache_ekeys;
+
+ /*
+ * The mac currently should hold the results of the first `n'
+ * encryptions. We xor in the last block in data section and
+ * do the final encryption in place.
+ *
+ * Get a pointer to the MAC block. XOR in the last two words of
+ * the data area. Call the encryption routine.
+ */
+ pd = pkt + (length/sizeof(U_LONG)) + NOCRYPT_LONGS;
+
+ *pd ^= *(pd - NOCRYPT_LONGS - 2);
+ *(pd + 1) ^= *(pd - NOCRYPT_LONGS - 1);
+ DESauth_des(pd, keys);
+
+ return 4 + 8; /* return size of key number and MAC */
+}
diff --git a/usr.sbin/xntpd/lib/authdecrypt.c b/usr.sbin/xntpd/lib/authdecrypt.c
new file mode 100644
index 0000000..6ad3aeb
--- /dev/null
+++ b/usr.sbin/xntpd/lib/authdecrypt.c
@@ -0,0 +1,82 @@
+/*
+ * authdecrypt - routine to decrypt a packet to see if this guy knows our key.
+ */
+#include "ntp_stdlib.h"
+
+/*
+ * For our purposes an NTP packet looks like:
+ *
+ * a variable amount of unencrypted data, multiple of 8 bytes, followed by:
+ * NOCRYPT_OCTETS worth of unencrypted data, followed by:
+ * BLOCK_OCTETS worth of ciphered checksum.
+ */
+#define NOCRYPT_OCTETS 4
+#define BLOCK_OCTETS 8
+
+#define NOCRYPT_LONGS ((NOCRYPT_OCTETS)/sizeof(U_LONG))
+#define BLOCK_LONGS ((BLOCK_OCTETS)/sizeof(U_LONG))
+
+/*
+ * Imported from the key data base module
+ */
+extern u_long cache_keyid; /* cached key ID */
+extern u_char DEScache_dkeys[]; /* cached decryption keys */
+extern u_char DESzerodkeys[]; /* zero key decryption keys */
+
+/*
+ * Stat counters, imported from data base module
+ */
+extern U_LONG authdecryptions;
+extern U_LONG authkeyuncached;
+
+int
+DESauthdecrypt(keyno, pkt, length)
+ u_long keyno;
+ const U_LONG *pkt;
+ int length; /* length of variable data in octets */
+{
+ register const U_LONG *pd;
+ register int i;
+ register u_char *keys;
+ register int longlen;
+ U_LONG work[2];
+
+ authdecryptions++;
+
+ if (keyno == 0)
+ keys = DESzerodkeys;
+ else {
+ if (keyno != cache_keyid) {
+ authkeyuncached++;
+ if (!authhavekey(keyno))
+ return 0;
+ }
+ keys = DEScache_dkeys;
+ }
+
+ /*
+ * Get encryption block data in host byte order and decrypt it.
+ */
+ longlen = length / sizeof(U_LONG);
+ pd = pkt + longlen; /* points at NOCRYPT area */
+ work[0] = *(pd + NOCRYPT_LONGS);
+ work[1] = *(pd + NOCRYPT_LONGS + 1);
+
+ if (longlen & 0x1) {
+ DESauth_des(work, keys);
+ work[0] ^= *(--pd);
+ }
+
+ for (i = longlen/2; i > 0; i--) {
+ DESauth_des(work, keys);
+ work[1] ^= *(--pd);
+ work[0] ^= *(--pd);
+ }
+
+ /*
+ * Success if the encryption data is zero
+ */
+ if ((work[0] == 0) && (work[1] == 0))
+ return 1;
+ return 0;
+}
diff --git a/usr.sbin/xntpd/lib/authdes.c.export b/usr.sbin/xntpd/lib/authdes.c.export
new file mode 100644
index 0000000..a22fc83
--- /dev/null
+++ b/usr.sbin/xntpd/lib/authdes.c.export
@@ -0,0 +1,41 @@
+/*
+ * authdes.c - dummy encryption routines for destinations outside the USA.
+ *
+ * Sorry, folks; I hate this, too. Send me your e-mail address in an
+ * envelope bearing a US postmark and I'll send you the decryption key
+ * for the des program normally distributed with Unix in the USA. Outside
+ * the USA you are on your own; however, you should be able quickly to
+ * obtain the source from lots of places, homegrown or otherwise.
+ *
+ * to decrypt the des routine, mumble the following:
+ *
+ * des -d -k key authdes.c.des authdes.c
+ *
+ * , where key is as above, and rebuild. To restore the distribution
+ * to its exportable state, copy this file to authdes.c .
+ */
+#include <sys/types.h>
+#include "ntp_stdlib.h"
+
+/*
+ * This routine is normally called to compute the key schedule.
+ */
+void
+DESauth_subkeys(key, encryptkeys, decryptkeys)
+ const U_LONG *key;
+ u_char *encryptkeys;
+ u_char *decryptkeys;
+{
+};
+
+/*
+ * This routine is normally called to encrypt and decrypt the data. This
+ * is done in-place using the Digital Encryption Standard (DES) Cipher-
+ * Block Chaining (CBC) method as described in the NTP specification.
+ */
+void
+DESauth_des(data, subkeys)
+ U_LONG *data;
+ u_char *subkeys;
+{
+};
diff --git a/usr.sbin/xntpd/lib/authencrypt.c b/usr.sbin/xntpd/lib/authencrypt.c
new file mode 100644
index 0000000..f84d10f
--- /dev/null
+++ b/usr.sbin/xntpd/lib/authencrypt.c
@@ -0,0 +1,88 @@
+/*
+ * authencrypt - compute and encrypt the mac field in an NTP packet
+ */
+#include "ntp_stdlib.h"
+
+/*
+ * For our purposes an NTP packet looks like:
+ *
+ * a variable amount of encrypted data, multiple of 8 bytes, followed by:
+ * NOCRYPT_OCTETS worth of unencrypted data, followed by:
+ * BLOCK_OCTETS worth of ciphered checksum.
+ */
+#define NOCRYPT_OCTETS 4
+#define BLOCK_OCTETS 8
+
+#define NOCRYPT_LONGS ((NOCRYPT_OCTETS)/sizeof(U_LONG))
+#define BLOCK_LONGS ((BLOCK_OCTETS)/sizeof(U_LONG))
+
+/*
+ * Imported from the key data base module
+ */
+extern u_long cache_keyid; /* cached key ID */
+extern u_char DEScache_ekeys[]; /* cached decryption keys */
+extern u_char DESzeroekeys[]; /* zero key decryption keys */
+
+/*
+ * Stat counters from the database module
+ */
+extern U_LONG authencryptions;
+extern U_LONG authkeyuncached;
+extern U_LONG authnokey;
+
+int
+DESauthencrypt(keyno, pkt, length)
+ u_long keyno;
+ U_LONG *pkt;
+ int length; /* length of encrypted portion of packet */
+{
+ register U_LONG *pd;
+ register int i;
+ register u_char *keys;
+ register int len;
+ U_LONG work[2];
+
+ authencryptions++;
+
+ if (keyno == 0) {
+ keys = DESzeroekeys;
+ } else {
+ if (keyno != cache_keyid) {
+ authkeyuncached++;
+ if (!authhavekey(keyno)) {
+ authnokey++;
+ return 0;
+ }
+ }
+ keys = DEScache_ekeys;
+ }
+
+ /*
+ * Do the encryption. Work our way forward in the packet, eight
+ * bytes at a time, encrypting as we go. Note that the byte order
+ * issues are handled by the DES routine itself
+ */
+ pd = pkt;
+ work[0] = work[1] = 0;
+ len = length / sizeof(U_LONG);
+
+ for (i = (len/2); i > 0; i--) {
+ work[0] ^= *pd++;
+ work[1] ^= *pd++;
+ DESauth_des(work, keys);
+ }
+
+ if (len & 0x1) {
+ work[0] ^= *pd++;
+ DESauth_des(work, keys);
+ }
+
+ /*
+ * Space past the keyid and stick the result back in the mac field
+ */
+ pd += NOCRYPT_LONGS;
+ *pd++ = work[0];
+ *pd = work[1];
+
+ return 4 + BLOCK_OCTETS; /* return size of key and MAC */
+}
diff --git a/usr.sbin/xntpd/lib/authkeys.c b/usr.sbin/xntpd/lib/authkeys.c
new file mode 100644
index 0000000..9fce020
--- /dev/null
+++ b/usr.sbin/xntpd/lib/authkeys.c
@@ -0,0 +1,601 @@
+/*
+ * authkeys.c - routines to manage the storage of authentication keys
+ */
+#include <stdio.h>
+
+#include "ntp_types.h"
+#include "ntp_string.h"
+#include "ntp_malloc.h"
+#include "ntp_stdlib.h"
+
+/*
+ * Structure to store keys in in the hash table.
+ */
+struct savekey {
+ struct savekey *next;
+ union {
+#ifdef DES
+ U_LONG DES_key[2];
+#endif
+#ifdef MD5
+ char MD5_key[32];
+#endif
+ } k;
+ u_long keyid;
+ u_short flags;
+#ifdef MD5
+ int keylen;
+#endif
+};
+
+#define KEY_TRUSTED 0x1 /* this key is trusted */
+#define KEY_KNOWN 0x2 /* this key is known */
+
+#ifdef DES
+#define KEY_DES 0x100 /* this is a DES type key */
+#endif
+
+#ifdef MD5
+#define KEY_MD5 0x200 /* this is a MD5 type key */
+#endif
+
+/*
+ * The hash table. This is indexed by the low order bits of the
+ * keyid. We make this fairly big for potentially busy servers.
+ */
+#define HASHSIZE 64
+#define HASHMASK ((HASHSIZE)-1)
+#define KEYHASH(keyid) ((keyid) & HASHMASK)
+
+struct savekey *key_hash[HASHSIZE];
+
+U_LONG authkeynotfound;
+U_LONG authkeylookups;
+U_LONG authnumkeys;
+U_LONG authuncached;
+U_LONG authkeyuncached;
+U_LONG authnokey; /* calls to encrypt with no key */
+U_LONG authencryptions;
+U_LONG authdecryptions;
+
+/*
+ * Storage for free key structures. We malloc() such things but
+ * never free them.
+ */
+struct savekey *authfreekeys;
+int authnumfreekeys;
+
+#define MEMINC 12 /* number of new free ones to get at once */
+
+
+#ifdef DES
+/*
+ * Size of the key schedule
+ */
+#define KEY_SCHED_SIZE 128 /* number of octets to store key schedule */
+
+/*
+ * The zero key, which we always have. Store the permutted key
+ * zero in here.
+ */
+#define ZEROKEY_L 0x01010101 /* odd parity zero key */
+#define ZEROKEY_R 0x01010101 /* right half of same */
+u_char DESzeroekeys[KEY_SCHED_SIZE];
+u_char DESzerodkeys[KEY_SCHED_SIZE];
+u_char DEScache_ekeys[KEY_SCHED_SIZE];
+u_char DEScache_dkeys[KEY_SCHED_SIZE];
+#endif
+
+/*
+ * The key cache. We cache the last key we looked at here.
+ */
+u_long cache_keyid;
+u_short cache_flags;
+
+#ifdef MD5
+int cache_keylen;
+char *cache_key;
+#endif
+
+/*
+ * init_auth - initialize internal data
+ */
+void
+init_auth()
+{
+ U_LONG zerokey[2];
+
+ /*
+ * Initialize hash table and free list
+ */
+ memset((char *)key_hash, 0, sizeof key_hash);
+ cache_flags = cache_keyid = 0;
+
+ authnumfreekeys = authkeynotfound = authkeylookups = 0;
+ authnumkeys = authuncached = authkeyuncached = authnokey = 0;
+ authencryptions = authdecryptions = 0;
+
+#ifdef DES
+ /*
+ * Initialize the zero key
+ */
+ zerokey[0] = ZEROKEY_L;
+ zerokey[1] = ZEROKEY_R;
+ /* could just zero all */
+ DESauth_subkeys(zerokey, DESzeroekeys, DESzerodkeys);
+#endif
+}
+
+
+/*
+ * auth_findkey - find a key in the hash table
+ */
+struct savekey *
+auth_findkey(keyno)
+ u_long keyno;
+{
+ register struct savekey *sk;
+
+ sk = key_hash[KEYHASH(keyno)];
+ while (sk != 0) {
+ if (keyno == sk->keyid)
+ return sk;
+ sk = sk->next;
+ }
+ return 0;
+}
+
+
+/*
+ * auth_havekey - return whether a key is known
+ */
+int
+auth_havekey(keyno)
+ u_long keyno;
+{
+ register struct savekey *sk;
+
+ if (keyno == 0 || (keyno == cache_keyid))
+ return 1;
+
+ sk = key_hash[KEYHASH(keyno)];
+ while (sk != 0) {
+ if (keyno == sk->keyid) {
+ if (sk->flags & KEY_KNOWN)
+ return 1;
+ else {
+ authkeynotfound++;
+ return 0;
+ }
+ }
+ sk = sk->next;
+ }
+ authkeynotfound++;
+ return 0;
+}
+
+
+/*
+ * authhavekey - return whether a key is known. Permute and cache
+ * the key as a side effect.
+ */
+int
+authhavekey(keyno)
+ u_long keyno;
+{
+ register struct savekey *sk;
+
+ authkeylookups++;
+ if (keyno == 0 || keyno == cache_keyid)
+ return 1;
+
+ sk = key_hash[KEYHASH(keyno)];
+ while (sk != 0) {
+ if (keyno == sk->keyid)
+ break;
+ sk = sk->next;
+ }
+
+ if (sk == 0 || !(sk->flags & KEY_KNOWN)) {
+ authkeynotfound++;
+ return 0;
+ }
+
+ cache_keyid = sk->keyid;
+ cache_flags = sk->flags;
+#ifdef MD5
+ if (sk->flags & KEY_MD5) {
+ cache_keylen = sk->keylen;
+ cache_key = (char *) sk->k.MD5_key; /* XXX */
+ return 1;
+ }
+#endif
+
+#ifdef DES
+ if (sk->flags & KEY_DES) {
+ DESauth_subkeys(sk->k.DES_key, DEScache_ekeys, DEScache_dkeys);
+ return 1;
+ }
+#endif
+ return 0;
+}
+
+
+/*
+ * auth_moremem - get some more free key structures
+ */
+int
+auth_moremem()
+{
+ register struct savekey *sk;
+ register int i;
+
+ sk = (struct savekey *)malloc(MEMINC * sizeof(struct savekey));
+ if (sk == 0)
+ return 0;
+
+ for (i = MEMINC; i > 0; i--) {
+ sk->next = authfreekeys;
+ authfreekeys = sk++;
+ }
+ authnumfreekeys += MEMINC;
+ return authnumfreekeys;
+}
+
+
+/*
+ * authtrust - declare a key to be trusted/untrusted
+ */
+void
+authtrust(keyno, trust)
+ u_long keyno;
+ int trust;
+{
+ register struct savekey *sk;
+
+ sk = key_hash[KEYHASH(keyno)];
+ while (sk != 0) {
+ if (keyno == sk->keyid)
+ break;
+ sk = sk->next;
+ }
+
+ if (sk == 0 && !trust)
+ return;
+
+ if (sk != 0) {
+ if (cache_keyid == keyno)
+ cache_flags = cache_keyid = 0;
+
+ if (trust) {
+ sk->flags |= KEY_TRUSTED;
+ return;
+ }
+
+ sk->flags &= ~KEY_TRUSTED;
+ if (!(sk->flags & KEY_KNOWN)) {
+ register struct savekey *skp;
+
+ skp = key_hash[KEYHASH(keyno)];
+ if (skp == sk) {
+ key_hash[KEYHASH(keyno)] = sk->next;
+ } else {
+ while (skp->next != sk)
+ skp = skp->next;
+ skp->next = sk->next;
+ }
+ authnumkeys--;
+
+ sk->next = authfreekeys;
+ authfreekeys = sk;
+ authnumfreekeys++;
+ }
+ return;
+ }
+
+ if (authnumfreekeys == 0)
+ if (auth_moremem() == 0)
+ return;
+
+ sk = authfreekeys;
+ authfreekeys = sk->next;
+ authnumfreekeys--;
+
+ sk->keyid = keyno;
+ sk->flags = KEY_TRUSTED;
+ sk->next = key_hash[KEYHASH(keyno)];
+ key_hash[KEYHASH(keyno)] = sk;
+ authnumkeys++;
+ return;
+}
+
+
+/*
+ * authistrusted - determine whether a key is trusted
+ */
+int
+authistrusted(keyno)
+ u_long keyno;
+{
+ register struct savekey *sk;
+
+ if (keyno == cache_keyid)
+ return ((cache_flags & KEY_TRUSTED) != 0);
+
+ authkeyuncached++;
+
+ sk = key_hash[KEYHASH(keyno)];
+ while (sk != 0) {
+ if (keyno == sk->keyid)
+ break;
+ sk = sk->next;
+ }
+
+ if (sk == 0 || !(sk->flags & KEY_TRUSTED))
+ return 0;
+ return 1;
+}
+
+
+
+#ifdef DES
+/*
+ * DESauth_setkey - set a key into the key array
+ */
+void
+DESauth_setkey(keyno, key)
+ u_long keyno;
+ const U_LONG *key;
+{
+ register struct savekey *sk;
+
+ /*
+ * See if we already have the key. If so just stick in the
+ * new value.
+ */
+ sk = key_hash[KEYHASH(keyno)];
+ while (sk != 0) {
+ if (keyno == sk->keyid) {
+ sk->k.DES_key[0] = key[0];
+ sk->k.DES_key[1] = key[1];
+ sk->flags |= KEY_KNOWN | KEY_DES;
+ if (cache_keyid == keyno)
+ cache_flags = cache_keyid = 0;
+ return;
+ }
+ sk = sk->next;
+ }
+
+ /*
+ * Need to allocate new structure. Do it.
+ */
+ if (authnumfreekeys == 0) {
+ if (auth_moremem() == 0)
+ return;
+ }
+
+ sk = authfreekeys;
+ authfreekeys = sk->next;
+ authnumfreekeys--;
+
+ sk->k.DES_key[0] = key[0];
+ sk->k.DES_key[1] = key[1];
+ sk->keyid = keyno;
+ sk->flags = KEY_KNOWN | KEY_DES;
+ sk->next = key_hash[KEYHASH(keyno)];
+ key_hash[KEYHASH(keyno)] = sk;
+ authnumkeys++;
+ return;
+}
+#endif
+
+#ifdef MD5
+void
+MD5auth_setkey(keyno, key)
+ u_long keyno;
+ const U_LONG *key;
+{
+ register struct savekey *sk;
+
+ /*
+ * See if we already have the key. If so just stick in the
+ * new value.
+ */
+ sk = key_hash[KEYHASH(keyno)];
+ while (sk != 0) {
+ if (keyno == sk->keyid) {
+ strncpy(sk->k.MD5_key, (char *)key, sizeof(sk->k.MD5_key));
+ if ((sk->keylen = strlen((char *)key)) >
+ sizeof(sk->k.MD5_key))
+ sk->keylen = sizeof(sk->k.MD5_key);
+
+ sk->flags |= KEY_KNOWN | KEY_MD5;
+ if (cache_keyid == keyno)
+ cache_flags = cache_keyid = 0;
+ return;
+ }
+ sk = sk->next;
+ }
+
+ /*
+ * Need to allocate new structure. Do it.
+ */
+ if (authnumfreekeys == 0) {
+ if (auth_moremem() == 0)
+ return;
+ }
+
+ sk = authfreekeys;
+ authfreekeys = sk->next;
+ authnumfreekeys--;
+
+ strncpy(sk->k.MD5_key, (char *)key, sizeof(sk->k.MD5_key));
+ if ((sk->keylen = strlen((char *)key)) > sizeof(sk->k.MD5_key))
+ sk->keylen = sizeof(sk->k.MD5_key);
+
+ sk->keyid = keyno;
+ sk->flags = KEY_KNOWN | KEY_MD5;
+ sk->next = key_hash[KEYHASH(keyno)];
+ key_hash[KEYHASH(keyno)] = sk;
+ authnumkeys++;
+ return;
+}
+#endif
+
+/*
+ * auth_delkeys - delete all known keys, in preparation for rereading
+ * the keys file (presumably)
+ */
+void
+auth_delkeys()
+{
+ register struct savekey *sk;
+ register struct savekey **skp;
+ register int i;
+
+ for (i = 0; i < HASHSIZE; i++) {
+ skp = &(key_hash[i]);
+ sk = key_hash[i];
+ while (sk != 0) {
+ sk->flags &= ~(KEY_KNOWN
+#ifdef MD5
+ | KEY_MD5
+#endif
+#ifdef DES
+ | KEY_DES
+#endif
+ );
+ if (sk->flags == 0) {
+ *skp = sk->next;
+ authnumkeys--;
+ sk->next = authfreekeys;
+ authfreekeys = sk;
+ authnumfreekeys++;
+ sk = *skp;
+ } else {
+ skp = &(sk->next);
+ sk = sk->next;
+ }
+ }
+ }
+}
+
+
+/*
+ * auth1crypt - support for two stage encryption, part 1.
+ */
+void
+auth1crypt(keyno, pkt, length)
+ u_long keyno;
+ U_LONG *pkt;
+ int length; /* length of all encrypted data */
+{
+ if (keyno && keyno != cache_keyid) {
+ authkeyuncached++;
+ if (!authhavekey(keyno)) {
+ authnokey++;
+ return;
+ }
+ }
+
+#ifdef DES
+ if (!keyno || (cache_flags & KEY_DES)) {
+ DESauth1crypt(keyno, pkt, length);
+ return;
+ }
+#endif
+
+#ifdef MD5
+ if (cache_flags & KEY_MD5) {
+ MD5auth1crypt(keyno, pkt, length);
+ return;
+ }
+#endif
+}
+
+
+/*
+ * auth1crypt - support for two stage encryption, part 1.
+ */
+int
+auth2crypt(keyno, pkt, length)
+ u_long keyno;
+ U_LONG *pkt;
+ int length; /* total length of encrypted area */
+{
+ if (keyno && keyno != cache_keyid) {
+ authkeyuncached++;
+ if (!authhavekey(keyno)) {
+ authnokey++;
+ return 0;
+ }
+ }
+
+#ifdef DES
+ if (!keyno || (cache_flags & KEY_DES))
+ return DESauth2crypt(keyno, pkt, length);
+#endif
+
+#ifdef MD5
+ if (cache_flags & KEY_MD5)
+ return MD5auth2crypt(keyno, pkt, length);
+#endif
+
+ return 0;
+}
+
+int
+authencrypt(keyno, pkt, length)
+ u_long keyno;
+ U_LONG *pkt;
+ int length; /* length of encrypted portion of packet */
+{
+ int sendlength = 0;
+
+ if (keyno && keyno != cache_keyid) {
+ authkeyuncached++;
+ if (!authhavekey(keyno)) {
+ authnokey++;
+ return 0;
+ }
+ }
+
+#ifdef DES
+ if (!keyno || (cache_flags & KEY_DES))
+ return sendlength = DESauthencrypt(keyno, pkt, length);
+#endif
+
+#ifdef MD5
+ if (cache_flags & KEY_MD5)
+ return MD5authencrypt(keyno, pkt, length);
+#endif
+ return 0;
+}
+
+
+int
+authdecrypt(keyno, pkt, length)
+ u_long keyno;
+ U_LONG *pkt;
+ int length; /* length of variable data in octets */
+{
+ if (keyno && (keyno != cache_keyid)) {
+ authkeyuncached++;
+ if (!authhavekey(keyno)) {
+ authnokey++;
+ return 0;
+ }
+ }
+
+#ifdef DES
+ if (!keyno || (cache_flags & KEY_DES))
+ return DESauthdecrypt(keyno, pkt, length);
+#endif
+
+#ifdef MD5
+ if (cache_flags & KEY_MD5)
+ return MD5authdecrypt(keyno, pkt, length);
+#endif
+
+ return 0;
+}
diff --git a/usr.sbin/xntpd/lib/authparity.c b/usr.sbin/xntpd/lib/authparity.c
new file mode 100644
index 0000000..a5b59e3
--- /dev/null
+++ b/usr.sbin/xntpd/lib/authparity.c
@@ -0,0 +1,58 @@
+/*
+ * auth_parity - set parity on a key/check for odd parity
+ */
+#include "ntp_stdlib.h"
+
+int
+DESauth_parity(key)
+ U_LONG *key;
+{
+ U_LONG mask;
+ int parity_err;
+ int bitcount;
+ int half;
+ int byte;
+ int i;
+
+ /*
+ * Go through counting bits in each byte. Check to see if
+ * each parity bit was set correctly. If not, note the error
+ * and set it right.
+ */
+ parity_err = 0;
+ for (half = 0; half < 2; half++) { /* two halves of key */
+ mask = 0x80000000;
+ for (byte = 0; byte < 4; byte++) { /* 4 bytes per half */
+ bitcount = 0;
+ for (i = 0; i < 7; i++) { /* 7 data bits / byte */
+ if (key[half] & mask)
+ bitcount++;
+ mask >>= 1;
+ }
+
+ /*
+ * If bitcount is even, parity must be set. If
+ * bitcount is odd, parity must be clear.
+ */
+ if ((bitcount & 0x1) == 0) {
+ if (!(key[half] & mask)) {
+ parity_err++;
+ key[half] |= mask;
+ }
+ } else {
+ if (key[half] & mask) {
+ parity_err++;
+ key[half] &= ~mask;
+ }
+ }
+ mask >>= 1;
+ }
+ }
+
+ /*
+ * Return the result of the parity check.
+ */
+ return (parity_err == 0);
+}
+
+
diff --git a/usr.sbin/xntpd/lib/authreadkeys.c b/usr.sbin/xntpd/lib/authreadkeys.c
new file mode 100644
index 0000000..ee13441
--- /dev/null
+++ b/usr.sbin/xntpd/lib/authreadkeys.c
@@ -0,0 +1,191 @@
+/*
+ * authreadkeys.c - routines to support the reading of the key file
+ */
+#include <stdio.h>
+#include <ctype.h>
+
+#include "ntp_stdlib.h"
+#include "ntp_syslog.h"
+
+#ifdef DES
+/*
+ * Types of ascii representations for keys. "Standard" means a 64 bit
+ * hex number in NBS format, i.e. with the low order bit of each byte
+ * a parity bit. "NTP" means a 64 bit key in NTP format, with the
+ * high order bit of each byte a parity bit. "Ascii" means a 1-to-8
+ * character string whose ascii representation is used as the key.
+ */
+#define KEY_TYPE_STD 1
+#define KEY_TYPE_NTP 2
+#define KEY_TYPE_ASCII 3
+#endif
+
+#ifdef MD5
+/*
+ * Arbitrary LONG string of ASCII characters.
+ */
+#define KEY_TYPE_MD5 4
+#endif
+
+/*
+ * nexttok - basic internal tokenizing routine
+ */
+static char *
+nexttok(str)
+ char **str;
+{
+ register char *cp;
+ char *starttok;
+
+ cp = *str;
+
+ /*
+ * Space past white space
+ */
+ while (*cp == ' ' || *cp == '\t')
+ cp++;
+
+ /*
+ * Save this and space to end of token
+ */
+ starttok = cp;
+ while (*cp != '\0' && *cp != '\n' && *cp != ' '
+ && *cp != '\t' && *cp != '#')
+ cp++;
+
+ /*
+ * If token length is zero return an error, else set end of
+ * token to zero and return start.
+ */
+ if (starttok == cp)
+ return 0;
+
+ if (*cp == ' ' || *cp == '\t')
+ *cp++ = '\0';
+ else
+ *cp = '\0';
+
+ *str = cp;
+ return starttok;
+}
+
+
+/*
+ * authreadkeys - (re)read keys from a file.
+ */
+int
+authreadkeys(file)
+ const char *file;
+{
+ FILE *fp;
+ char *line;
+ char *token;
+ u_long keyno;
+ int keytype;
+ char buf[512]; /* lots of room for line? */
+extern FILE * fopen P((const char *filename, const char *type));
+extern int fclose P((FILE *stream));
+
+ /*
+ * Open file. Complain and return if it can't be opened.
+ */
+ fp = fopen(file, "r");
+ if (fp == NULL) {
+ syslog(LOG_ERR, "can't open key file %s: %m", file);
+ return 0;
+ }
+
+ /*
+ * Remove all existing keys
+ */
+ auth_delkeys();
+
+ /*
+ * Now read lines from the file, looking for key entries
+ */
+ while ((line = fgets(buf, sizeof buf, fp)) != NULL) {
+ token = nexttok(&line);
+ if (token == 0)
+ continue;
+
+ /*
+ * First is key number. See if it is okay.
+ */
+ keyno = atoi(token);
+ if (keyno == 0) {
+ syslog(LOG_ERR,
+ "cannot change keyid 0, key entry `%s' ignored",
+ token);
+ continue;
+ }
+
+ /*
+ * Next is keytype. See if that is all right.
+ */
+ token = nexttok(&line);
+ if (token == 0) {
+ syslog(LOG_ERR,
+ "no key type for key number %ld, entry ignored",
+ keyno);
+ continue;
+ }
+ switch (*token) {
+#ifdef DES
+ case 'S':
+ case 's':
+ keytype = KEY_TYPE_STD; break;
+
+ case 'N':
+ case 'n':
+ keytype = KEY_TYPE_NTP; break;
+
+ case 'A':
+ case 'a':
+ keytype = KEY_TYPE_ASCII; break;
+#endif
+#ifdef MD5
+ case 'M':
+ case 'm':
+ keytype = KEY_TYPE_MD5; break;
+#endif
+ default:
+ syslog(LOG_ERR,
+ "invalid key type for key number %ld, entry ignored",
+ keyno);
+ continue;
+ }
+
+ /*
+ * Finally, get key and insert it
+ */
+ token = nexttok(&line);
+ if (token == 0) {
+ syslog(LOG_ERR,
+ "no key for number %ld entry, entry ignored",
+ keyno);
+ } else {
+ switch(keytype) {
+#ifdef DES
+ case KEY_TYPE_STD:
+ case KEY_TYPE_NTP:
+ case KEY_TYPE_ASCII:
+ if (!authusekey(keyno, keytype, token))
+ syslog(LOG_ERR,
+ "format/parity error for DES key %ld, not used",
+ keyno);
+ break;
+#endif
+#ifdef MD5
+ case KEY_TYPE_MD5:
+ if (!authusekey(keyno, keytype, token))
+ syslog(LOG_ERR,
+ "format/parity error for MD5 key %ld, not used",
+ keyno);
+ break;
+#endif
+ }
+ }
+ }
+ (void) fclose(fp);
+ return 1;
+}
diff --git a/usr.sbin/xntpd/lib/authusekey.c b/usr.sbin/xntpd/lib/authusekey.c
new file mode 100644
index 0000000..05fc0d7
--- /dev/null
+++ b/usr.sbin/xntpd/lib/authusekey.c
@@ -0,0 +1,132 @@
+/*
+ * authusekey - decode a key from ascii and use it
+ */
+#include <stdio.h>
+#include <ctype.h>
+
+#include "ntp_types.h"
+#include "ntp_string.h"
+#include "ntp_stdlib.h"
+
+/*
+ * Types of ascii representations for keys. "Standard" means a 64 bit
+ * hex number in NBS format, i.e. with the low order bit of each byte
+ * a parity bit. "NTP" means a 64 bit key in NTP format, with the
+ * high order bit of each byte a parity bit. "Ascii" means a 1-to-8
+ * character string whose ascii representation is used as the key.
+ */
+#ifdef DES
+#define KEY_TYPE_STD 1
+#define KEY_TYPE_NTP 2
+#define KEY_TYPE_ASCII 3
+
+#define STD_PARITY_BITS 0x01010101
+
+#endif
+
+#ifdef MD5
+#define KEY_TYPE_MD5 4
+#endif
+
+int
+authusekey(keyno, keytype, str)
+ u_long keyno;
+ int keytype;
+ const char *str;
+{
+ U_LONG key[2];
+ u_char keybytes[8];
+ const char *cp;
+ char *xdigit;
+ int len;
+ int i;
+ static char *hex = "0123456789abcdef";
+
+ cp = str;
+ len = strlen(cp);
+ if (len == 0)
+ return 0;
+
+ switch(keytype) {
+#ifdef DES
+ case KEY_TYPE_STD:
+ case KEY_TYPE_NTP:
+ if (len != 16) /* Lazy. Should define constant */
+ return 0;
+ /*
+ * Decode hex key.
+ */
+ key[0] = 0;
+ key[1] = 0;
+ for (i = 0; i < 16; i++) {
+ if (!isascii(*cp))
+ return 0;
+ xdigit = strchr(hex, isupper(*cp) ? tolower(*cp) : *cp);
+ cp++;
+ if (xdigit == 0)
+ return 0;
+ key[i>>3] <<= 4;
+ key[i>>3] |= (U_LONG)(xdigit - hex) & 0xf;
+ }
+
+ /*
+ * If this is an NTP format key, put it into NBS format
+ */
+ if (keytype == KEY_TYPE_NTP) {
+ for (i = 0; i < 2; i++)
+ key[i] = ((key[i] << 1) & ~STD_PARITY_BITS)
+ | ((key[i] >> 7) & STD_PARITY_BITS);
+ }
+
+ /*
+ * Check the parity, reject the key if the check fails
+ */
+ if (!DESauth_parity(key)) {
+ return 0;
+ }
+
+ /*
+ * We can't find a good reason not to use this key.
+ * So use it.
+ */
+ DESauth_setkey(keyno, key);
+ break;
+
+ case KEY_TYPE_ASCII:
+ /*
+ * Make up key from ascii representation
+ */
+ memset((char *) keybytes, 0, sizeof(keybytes));
+ for (i = 0; i < 8 && i < len; i++)
+ keybytes[i] = *cp++ << 1;
+ key[0] = (U_LONG)keybytes[0] << 24 | (U_LONG)keybytes[1] << 16
+ | (U_LONG)keybytes[2] << 8 | (U_LONG)keybytes[3];
+ key[1] = (U_LONG)keybytes[4] << 24 | (U_LONG)keybytes[5] << 16
+ | (U_LONG)keybytes[6] << 8 | (U_LONG)keybytes[7];
+
+ /*
+ * Set parity on key
+ */
+ (void)DESauth_parity(key);
+
+ /*
+ * Now set key in.
+ */
+ DESauth_setkey(keyno, key);
+ break;
+#endif
+
+#ifdef MD5
+ case KEY_TYPE_MD5:
+ /* XXX FIXME: MD5auth_setkey() casts arg2 back to (char *) */
+ MD5auth_setkey(keyno, (U_LONG *)str);
+ break;
+#endif
+
+ default:
+ /* Oh, well */
+ return 0;
+ }
+
+ return 1;
+}
diff --git a/usr.sbin/xntpd/lib/buftvtots.c b/usr.sbin/xntpd/lib/buftvtots.c
new file mode 100644
index 0000000..7e1ec63
--- /dev/null
+++ b/usr.sbin/xntpd/lib/buftvtots.c
@@ -0,0 +1,61 @@
+/*
+ * buftvtots - pull a Unix-format (struct timeval) time stamp out of
+ * an octet stream and convert it to a l_fp time stamp.
+ * This is useful when using the clock line discipline.
+ */
+#include "ntp_fp.h"
+#include "ntp_unixtime.h"
+
+int
+buftvtots(bufp, ts)
+ const char *bufp;
+ l_fp *ts;
+{
+ register const u_char *bp;
+ register u_long sec;
+ register u_long usec;
+
+#ifdef XNTP_BIG_ENDIAN
+ bp = (u_char *)bufp;
+
+ sec = (u_long)*bp++ & 0xff;
+ sec <<= 8;
+ sec += (u_long)*bp++ & 0xff;
+ sec <<= 8;
+ sec += (u_long)*bp++ & 0xff;
+ sec <<= 8;
+ sec += (u_long)*bp++ & 0xff;
+
+ usec = (u_long)*bp++ & 0xff;
+ usec <<= 8;
+ usec += (u_long)*bp++ & 0xff;
+ usec <<= 8;
+ usec += (u_long)*bp++ & 0xff;
+ usec <<= 8;
+ usec += (u_long)*bp & 0xff;
+#else
+ bp = (u_char *)bufp + 7;
+
+ usec = (u_long)*bp-- & 0xff;
+ usec <<= 8;
+ usec += (u_long)*bp-- & 0xff;
+ usec <<= 8;
+ usec += (u_long)*bp-- & 0xff;
+ usec <<= 8;
+ usec += (u_long)*bp-- & 0xff;
+
+ sec = (u_long)*bp-- & 0xff;
+ sec <<= 8;
+ sec += (u_long)*bp-- & 0xff;
+ sec <<= 8;
+ sec += (u_long)*bp-- & 0xff;
+ sec <<= 8;
+ sec += (u_long)*bp & 0xff;
+#endif
+ if (usec > 999999)
+ return 0;
+
+ ts->l_ui = sec + (u_long)JAN_1970;
+ TVUTOTSF(usec, ts->l_uf);
+ return 1;
+}
diff --git a/usr.sbin/xntpd/lib/caljulian.c b/usr.sbin/xntpd/lib/caljulian.c
new file mode 100644
index 0000000..64efd1e
--- /dev/null
+++ b/usr.sbin/xntpd/lib/caljulian.c
@@ -0,0 +1,105 @@
+/*
+ * caljulian - determine the Julian date from an NTP time.
+ */
+#include <sys/types.h>
+
+#include "ntp_types.h"
+#include "ntp_calendar.h"
+#include "ntp_stdlib.h"
+
+/*
+ * calmonthtab - month start offsets from the beginning of a cycle.
+ */
+static u_short calmonthtab[12] = {
+ 0, /* March */
+ MAR, /* April */
+ (MAR+APR), /* May */
+ (MAR+APR+MAY), /* June */
+ (MAR+APR+MAY+JUN), /* July */
+ (MAR+APR+MAY+JUN+JUL), /* August */
+ (MAR+APR+MAY+JUN+JUL+AUG), /* September */
+ (MAR+APR+MAY+JUN+JUL+AUG+SEP), /* October */
+ (MAR+APR+MAY+JUN+JUL+AUG+SEP+OCT), /* November */
+ (MAR+APR+MAY+JUN+JUL+AUG+SEP+OCT+NOV), /* December */
+ (MAR+APR+MAY+JUN+JUL+AUG+SEP+OCT+NOV+DEC), /* January */
+ (MAR+APR+MAY+JUN+JUL+AUG+SEP+OCT+NOV+DEC+JAN), /* February */
+};
+
+/*
+ * caldaytab - calendar year start day offsets
+ */
+static u_short caldaytab[YEARSPERCYCLE] = {
+ (DAYSPERYEAR - (JAN + FEB)),
+ ((DAYSPERYEAR * 2) - (JAN + FEB)),
+ ((DAYSPERYEAR * 3) - (JAN + FEB)),
+ ((DAYSPERYEAR * 4) - (JAN + FEB)),
+};
+
+void
+caljulian(ntptime, jt)
+ u_long ntptime;
+ register struct calendar *jt;
+{
+ register int i;
+ register u_long nt;
+ register u_short snt;
+ register int cyear;
+
+ /*
+ * Find the start of the cycle this is in.
+ */
+ nt = ntptime;
+ if (nt >= MAR1988) {
+ cyear = CYCLE22;
+ nt -= MAR1988;
+ } else {
+ cyear = 0;
+ nt -= MAR1900;
+ }
+ while (nt >= SECSPERCYCLE) {
+ nt -= SECSPERCYCLE;
+ cyear++;
+ }
+
+ /*
+ * Seconds, minutes and hours are too hard to do without
+ * divides, so we don't.
+ */
+ jt->second = nt % SECSPERMIN;
+ nt /= SECSPERMIN; /* nt in minutes */
+ jt->minute = nt % MINSPERHR;
+ snt = nt / MINSPERHR; /* snt in hours */
+ jt->hour = snt % HRSPERDAY;
+ snt /= HRSPERDAY; /* nt in days */
+
+ /*
+ * snt is now the number of days into the cycle, from 0 to 1460.
+ */
+ cyear <<= 2;
+ if (snt < caldaytab[0]) {
+ jt->yearday = snt + JAN + FEBLEAP + 1; /* first year is leap */
+ } else {
+ for (i = 1; i < YEARSPERCYCLE; i++)
+ if (snt < caldaytab[i])
+ break;
+ jt->yearday = snt - caldaytab[i-1] + 1;
+ cyear += i;
+ }
+ jt->year = cyear + 1900;
+
+ /*
+ * One last task, to compute the month and day. Normalize snt to
+ * a day within a cycle year.
+ */
+ while (snt >= DAYSPERYEAR)
+ snt -= DAYSPERYEAR;
+ for (i = 0; i < 11; i++)
+ if (snt < calmonthtab[i+1])
+ break;
+
+ if (i > 9)
+ jt->month = i - 9; /* January or February */
+ else
+ jt->month = i + 3; /* March through December */
+ jt->monthday = snt - calmonthtab[i] + 1;
+}
diff --git a/usr.sbin/xntpd/lib/calleapwhen.c b/usr.sbin/xntpd/lib/calleapwhen.c
new file mode 100644
index 0000000..d68bbe9
--- /dev/null
+++ b/usr.sbin/xntpd/lib/calleapwhen.c
@@ -0,0 +1,61 @@
+/*
+ * calleapwhen - determine the number of seconds to the next possible
+ * leap occurance and the last one.
+ */
+#include <sys/types.h>
+
+#include "ntp_types.h"
+#include "ntp_calendar.h"
+#include "ntp_stdlib.h"
+
+/*
+ * calleaptab - leaps occur at the end of December and June
+ */
+long calleaptab[10] = {
+ -(JAN+FEBLEAP)*SECSPERDAY, /* leap previous to cycle */
+ (MAR+APR+MAY+JUN)*SECSPERDAY, /* end of June */
+ (MAR+APR+MAY+JUN+JUL+AUG+SEP+OCT+NOV+DEC)*SECSPERDAY, /* end of Dec */
+ (MAR+APR+MAY+JUN)*SECSPERDAY + SECSPERYEAR,
+ (MAR+APR+MAY+JUN+JUL+AUG+SEP+OCT+NOV+DEC)*SECSPERDAY + SECSPERYEAR,
+ (MAR+APR+MAY+JUN)*SECSPERDAY + 2*SECSPERYEAR,
+ (MAR+APR+MAY+JUN+JUL+AUG+SEP+OCT+NOV+DEC)*SECSPERDAY + 2*SECSPERYEAR,
+ (MAR+APR+MAY+JUN)*SECSPERDAY + 3*SECSPERYEAR,
+ (MAR+APR+MAY+JUN+JUL+AUG+SEP+OCT+NOV+DEC)*SECSPERDAY + 3*SECSPERYEAR,
+ (MAR+APR+MAY+JUN+JUL+AUG+SEP+OCT+NOV+DEC+JAN+FEBLEAP+MAR+APR+MAY+JUN)
+ *SECSPERDAY + 3*SECSPERYEAR, /* next after current cycle */
+};
+
+void
+calleapwhen(ntpdate, leaplast, leapnext)
+ u_long ntpdate;
+ u_long *leaplast;
+ u_long *leapnext;
+{
+ register u_long dateincycle;
+ register int i;
+
+ /*
+ * Find the offset from the start of the cycle
+ */
+ dateincycle = ntpdate;
+ if (dateincycle >= MAR1988)
+ dateincycle -= MAR1988;
+ else
+ dateincycle -= MAR1900;
+
+ while (dateincycle >= SECSPERCYCLE)
+ dateincycle -= SECSPERCYCLE;
+
+ /*
+ * Find where we are with respect to the leap events.
+ */
+ for (i = 1; i < 9; i++)
+ if (dateincycle < (u_long)calleaptab[i])
+ break;
+
+ /*
+ * i points at the next leap. Compute the last and the next.
+ */
+ *leaplast = (u_long)((long)dateincycle - calleaptab[i-1]);
+ *leapnext = (u_long)(calleaptab[i] - (long)dateincycle);
+}
diff --git a/usr.sbin/xntpd/lib/caltontp.c b/usr.sbin/xntpd/lib/caltontp.c
new file mode 100644
index 0000000..a0c8f61
--- /dev/null
+++ b/usr.sbin/xntpd/lib/caltontp.c
@@ -0,0 +1,90 @@
+/*
+ * caltontp - convert a julian date to an NTP time
+ */
+#include <sys/types.h>
+
+#include "ntp_types.h"
+#include "ntp_calendar.h"
+#include "ntp_stdlib.h"
+
+/*
+ * calmonthtab - month start offsets from the beginning of a cycle.
+ */
+static u_short calmonthtab[12] = {
+ 0, /* March */
+ MAR, /* April */
+ (MAR+APR), /* May */
+ (MAR+APR+MAY), /* June */
+ (MAR+APR+MAY+JUN), /* July */
+ (MAR+APR+MAY+JUN+JUL), /* August */
+ (MAR+APR+MAY+JUN+JUL+AUG), /* September */
+ (MAR+APR+MAY+JUN+JUL+AUG+SEP), /* October */
+ (MAR+APR+MAY+JUN+JUL+AUG+SEP+OCT), /* November */
+ (MAR+APR+MAY+JUN+JUL+AUG+SEP+OCT+NOV), /* December */
+ (MAR+APR+MAY+JUN+JUL+AUG+SEP+OCT+NOV+DEC), /* January */
+ (MAR+APR+MAY+JUN+JUL+AUG+SEP+OCT+NOV+DEC+JAN), /* February */
+};
+
+u_long
+caltontp(jt)
+ register const struct calendar *jt;
+{
+ register int cyear;
+ register int resyear;
+ register u_long nt;
+ register int yearday;
+
+ /*
+ * Find the start of the cycle this is in.
+ */
+ cyear = (int)(jt->year - 1900) >> 2;
+ resyear = (jt->year - 1900) - (cyear << 2);
+ yearday = 0;
+ if (resyear == 0) {
+ if (jt->yearday == 0) {
+ if (jt->month == 1 || jt->month == 2) {
+ cyear--;
+ resyear = 3;
+ }
+ } else {
+ if (jt->yearday <= (u_short)(JAN+FEBLEAP)) {
+ cyear--;
+ resyear = 3;
+ yearday = calmonthtab[10] + jt->yearday;
+ } else {
+ yearday = jt->yearday - (JAN+FEBLEAP);
+ }
+ }
+ } else {
+ if (jt->yearday == 0) {
+ if (jt->month == 1 || jt->month == 2)
+ resyear--;
+ } else {
+ if (jt->yearday <= (u_short)(JAN+FEB)) {
+ resyear--;
+ yearday = calmonthtab[10] + jt->yearday;
+ } else {
+ yearday = jt->yearday - (JAN+FEB);
+ }
+ }
+ }
+
+ if (yearday == 0) {
+ if (jt->month >= 3) {
+ yearday = calmonthtab[jt->month - 3] + jt->monthday;
+ } else {
+ yearday = calmonthtab[jt->month + 9] + jt->monthday;
+ }
+ }
+
+ nt = TIMESDPERC((u_long)cyear);
+ while (resyear-- > 0)
+ nt += DAYSPERYEAR;
+ nt += (u_long) (yearday - 1);
+
+ nt = TIMES24(nt) + (u_long)jt->hour;
+ nt = TIMES60(nt) + (u_long)jt->minute;
+ nt = TIMES60(nt) + (u_long)jt->second;
+
+ return nt + MAR1900;
+}
diff --git a/usr.sbin/xntpd/lib/calyearstart.c b/usr.sbin/xntpd/lib/calyearstart.c
new file mode 100644
index 0000000..e78c058
--- /dev/null
+++ b/usr.sbin/xntpd/lib/calyearstart.c
@@ -0,0 +1,62 @@
+/*
+ * calyearstart - determine the NTP time at midnight of January 1 in
+ * the year of the given date.
+ */
+#include <sys/types.h>
+
+#include "ntp_types.h"
+#include "ntp_calendar.h"
+#include "ntp_stdlib.h"
+
+/*
+ * calyeartab - year start offsets from the beginning of a cycle
+ */
+u_long calyeartab[YEARSPERCYCLE] = {
+ (SECSPERLEAPYEAR-JANFEBLEAP),
+ (SECSPERLEAPYEAR-JANFEBLEAP) + SECSPERYEAR,
+ (SECSPERLEAPYEAR-JANFEBLEAP) + 2*SECSPERYEAR,
+ (SECSPERLEAPYEAR-JANFEBLEAP) + 3*SECSPERYEAR
+};
+
+u_long
+calyearstart(dateinyear)
+ register u_long dateinyear;
+{
+ register u_long cyclestart;
+ register u_long nextyear, lastyear;
+ register int i;
+
+ /*
+ * Find the start of the cycle this is in.
+ */
+ if (dateinyear >= MAR1988)
+ cyclestart = MAR1988;
+ else
+ cyclestart = MAR1900;
+ while ((cyclestart + SECSPERCYCLE) <= dateinyear)
+ cyclestart += SECSPERCYCLE;
+
+ /*
+ * If we're in the first year of the cycle, January 1 is
+ * two months back from the cyclestart and the year is
+ * a leap year.
+ */
+ lastyear = cyclestart + calyeartab[0];
+ if (dateinyear < lastyear)
+ return (cyclestart - JANFEBLEAP);
+
+ /*
+ * Look for an intermediate year
+ */
+ for (i = 1; i < YEARSPERCYCLE; i++) {
+ nextyear = cyclestart + calyeartab[i];
+ if (dateinyear < nextyear)
+ return lastyear;
+ lastyear = nextyear;
+ }
+
+ /*
+ * Not found, must be in last two months of cycle
+ */
+ return nextyear;
+}
diff --git a/usr.sbin/xntpd/lib/clocktime.c b/usr.sbin/xntpd/lib/clocktime.c
new file mode 100644
index 0000000..0217f2b
--- /dev/null
+++ b/usr.sbin/xntpd/lib/clocktime.c
@@ -0,0 +1,131 @@
+/*
+ * clocktime - compute the NTP date from a day of year, hour, minute
+ * and second.
+ */
+#include "ntp_fp.h"
+#include "ntp_unixtime.h"
+#include "ntp_stdlib.h"
+
+/*
+ * Hacks to avoid excercising the multiplier. I have no pride.
+ */
+#define MULBY10(x) (((x)<<3) + ((x)<<1))
+#define MULBY60(x) (((x)<<6) - ((x)<<2)) /* watch overflow */
+#define MULBY24(x) (((x)<<4) + ((x)<<3))
+
+/*
+ * Two days, in seconds.
+ */
+#define TWODAYS (2*24*60*60)
+
+/*
+ * We demand that the time be within CLOSETIME seconds of the receive
+ * time stamp. This is about 4 hours, which hopefully should be
+ * wide enough to collect most data, while close enough to keep things
+ * from getting confused.
+ */
+#define CLOSETIME (4*60*60)
+
+
+int
+clocktime(yday, hour, minute, second, tzoff, rec_ui, yearstart, ts_ui)
+ int yday;
+ int hour;
+ int minute;
+ int second;
+ int tzoff;
+ u_long rec_ui;
+ u_long *yearstart;
+ U_LONG *ts_ui;
+{
+ register long tmp;
+ register u_long date;
+ register u_long yst;
+
+ /*
+ * Compute the offset into the year in seconds. Note that
+ * this could come out to be a negative number.
+ */
+ tmp = (long)(MULBY24((yday-1)) + hour + tzoff);
+ tmp = MULBY60(tmp) + (long)minute;
+ tmp = MULBY60(tmp) + (long)second;
+
+ /*
+ * Initialize yearstart, if necessary.
+ */
+ yst = *yearstart;
+ if (yst == 0) {
+ yst = calyearstart(rec_ui);
+ *yearstart = yst;
+ }
+
+ /*
+ * Now the fun begins. We demand that the received clock time
+ * be within CLOSETIME of the receive timestamp, but
+ * there is uncertainty about the year the timestamp is in.
+ * Use the current year start for the first check, this should
+ * work most of the time.
+ */
+ date = (u_long)(tmp + (long)yst);
+ if (date < (rec_ui + CLOSETIME) &&
+ date > (rec_ui - CLOSETIME)) {
+ *ts_ui = date;
+ return 1;
+ }
+
+ /*
+ * Trouble. Next check is to see if the year rolled over and, if
+ * so, try again with the new year's start.
+ */
+ yst = calyearstart(rec_ui);
+ if (yst != *yearstart) {
+ date = (u_long)((long)yst + tmp);
+ *ts_ui = date;
+ if (date < (rec_ui + CLOSETIME) &&
+ date > (rec_ui - CLOSETIME)) {
+ *yearstart = yst;
+ return 1;
+ }
+ }
+
+ /*
+ * Here we know the year start matches the current system
+ * time. One remaining possibility is that the time code
+ * is in the year previous to that of the system time. This
+ * is only worth checking if the receive timestamp is less
+ * than a couple of days into the new year.
+ */
+ if ((rec_ui - yst) < TWODAYS) {
+ yst = calyearstart(yst - TWODAYS);
+ if (yst != *yearstart) {
+ date = (u_long)(tmp + (long)yst);
+ if (date < (rec_ui + CLOSETIME) &&
+ date > (rec_ui - CLOSETIME)) {
+ *yearstart = yst;
+ *ts_ui = date;
+ return 1;
+ }
+ }
+ }
+
+ /*
+ * One last possibility is that the time stamp is in the year
+ * following the year the system is in. Try this one before
+ * giving up.
+ */
+ yst = calyearstart(rec_ui + TWODAYS);
+ if (yst != *yearstart) {
+ date = (u_long)((long)yst + tmp);
+ if (date < (rec_ui + CLOSETIME) &&
+ date > (rec_ui - CLOSETIME)) {
+ *yearstart = yst;
+ *ts_ui = date;
+ return 1;
+ }
+ }
+
+ /*
+ * Give it up.
+ */
+ return 0;
+}
diff --git a/usr.sbin/xntpd/lib/clocktypes.c b/usr.sbin/xntpd/lib/clocktypes.c
new file mode 100644
index 0000000..4c5f437
--- /dev/null
+++ b/usr.sbin/xntpd/lib/clocktypes.c
@@ -0,0 +1,72 @@
+/*
+ * Data for pretty printing clock types
+ */
+#include <stdio.h>
+
+#include "ntp_fp.h"
+#include "ntp.h"
+#include "lib_strbuf.h"
+#include "ntp_refclock.h"
+
+struct clktype clktypes[] = {
+ { REFCLK_NONE, "unspecified type (0)",
+ "UNKNOWN" },
+ { REFCLK_LOCALCLOCK, "Undisciplined local clock (1)",
+ "LOCAL" },
+ { REFCLK_GPS_TRAK, "TRAK 8810 GPS Receiver (2)",
+ "GPS_TRAK" },
+ { REFCLK_WWV_PST, "PSTI/Traconex WWV/WWVH Receiver (3)",
+ "WWV_PST" },
+ { REFCLK_WWVB_SPECTRACOM, "Spectracom WWVB Receiver (4)",
+ "WWVB_SPEC" },
+ { REFCLK_GOES_TRUETIME, "TrueTime GPS/GOES Receivers (5)",
+ "GPS_GOES_TRUE" },
+ { REFCLK_IRIG_AUDIO, "IRIG Audio Decoder (6)",
+ "IRIG_AUDIO" },
+ { REFCLK_CHU, "Scratchbuilt CHU Receiver (7)",
+ "CHU" },
+ { REFCLK_PARSE, "Generic reference clock driver (8)",
+ "GENERIC" },
+ { REFCLK_GPS_MX4200, "Magnavox MX4200 GPS Receiver (9)",
+ "GPS_MX4200" },
+ { REFCLK_GPS_AS2201, "Austron 2201A GPS Receiver (10)",
+ "GPS_AS2201" },
+ { REFCLK_OMEGA_TRUETIME, "TrueTime OM-DC OMEGA Receiver (11)",
+ "OMEGA_TRUE" },
+ { REFCLK_IRIG_TPRO, "KSI/Odetics TPRO/S IRIG Interface (12)",
+ "IRIG_TPRO" },
+ { REFCLK_ATOM_LEITCH, "Leitch CSD 5300 Master Clock Controller (13)",
+ "ATOM_LEITCH" },
+ { REFCLK_MSF_EES, "EES M201 MSF Receiver (14)",
+ "MSF_EES" },
+ { REFCLK_GPSTM_TRUETIME, "TrueTime GPS/TM-TMD Receiver (15)",
+ "GPS_TRUE" },
+ { REFCLK_IRIG_BANCOMM, "Bancomm GPS/IRIG Receiver (16)",
+ "GPS_BANC" },
+ { REFCLK_GPS_DATUM, "Datum Precision Time System (17)",
+ "GPS_DATUM" },
+ { REFCLK_NIST_ACTS, "NIST Automated Computer Time Service (18)",
+ "NIST_ACTS" },
+ { REFCLK_WWV_HEATH, "Heath WWV/WWVH Receiver (19)",
+ "WWV_HEATH" },
+ { REFCLK_GPS_NMEA, "Generic NMEA GPS Receiver (20)",
+ "GPS_NMEA" },
+ { REFCLK_GPS_MOTO, "Motorola Six Gun GPS Receiver (21)",
+ "GPS_MOTO" },
+ { REFCLK_ATOM_PPS, "PPS Clock Discipline (22)",
+ "ATOM_PPS" },
+ { -1, "", "" }
+};
+
+const char *
+clockname(num)
+ int num;
+{
+ register struct clktype *clk;
+
+ for (clk = clktypes; clk->code != -1; clk++) {
+ if (num == clk->code)
+ return (clk->abbrev);
+ }
+ return (NULL);
+}
diff --git a/usr.sbin/xntpd/lib/decodenetnum.c b/usr.sbin/xntpd/lib/decodenetnum.c
new file mode 100644
index 0000000..f19b137
--- /dev/null
+++ b/usr.sbin/xntpd/lib/decodenetnum.c
@@ -0,0 +1,58 @@
+/*
+ * decodenetnum - return a net number (this is crude, but careful)
+ */
+#include <sys/types.h>
+#include <ctype.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+#include "ntp_stdlib.h"
+
+int
+decodenetnum(num, netnum)
+ const char *num;
+ u_long *netnum;
+{
+ register const char *cp;
+ register char *bp;
+ register int i;
+ register int temp;
+ register int eos;
+ char buf[80]; /* will core dump on really stupid stuff */
+
+ cp = num;
+ *netnum = 0;
+
+ if (*cp == '[') {
+ eos = ']';
+ cp++;
+ } else {
+ eos = '\0';
+ }
+
+ for (i = 0; i < 4; i++) {
+ bp = buf;
+ while (isdigit(*cp))
+ *bp++ = *cp++;
+ if (bp == buf)
+ break;
+
+ if (i < 3) {
+ if (*cp++ != '.')
+ break;
+ } else if (*cp != eos)
+ break;
+
+ *bp = '\0';
+ temp = atoi(buf);
+ if (temp > 255)
+ break;
+ *netnum <<= 8;
+ *netnum += temp;
+ }
+
+ if (i < 4)
+ return 0;
+ *netnum = htonl(*netnum);
+ return 1;
+}
diff --git a/usr.sbin/xntpd/lib/dofptoa.c b/usr.sbin/xntpd/lib/dofptoa.c
new file mode 100644
index 0000000..bfa9c71
--- /dev/null
+++ b/usr.sbin/xntpd/lib/dofptoa.c
@@ -0,0 +1,117 @@
+/*
+ * dofptoa - do the grunge work to convert an fp number to ascii
+ */
+#include <stdio.h>
+
+#include "ntp_fp.h"
+#include "lib_strbuf.h"
+#include "ntp_string.h"
+#include "ntp_stdlib.h"
+
+char *
+dofptoa(fpv, neg, ndec, msec)
+ u_fp fpv;
+ int neg;
+ int ndec;
+ int msec;
+{
+ register u_char *cp, *cpend;
+ register u_long val;
+ register short dec;
+ u_char cbuf[12];
+ u_char *cpdec;
+ char *buf;
+ char *bp;
+
+ /*
+ * Get a string buffer before starting
+ */
+ LIB_GETBUF(buf);
+
+ /*
+ * Zero out the buffer
+ */
+ memset((char *)cbuf, 0, sizeof cbuf);
+
+ /*
+ * Set the pointers to point at the first
+ * decimal place. Get a local copy of the value.
+ */
+ cp = cpend = &cbuf[5];
+ val = fpv;
+
+ /*
+ * If we have to, decode the integral part
+ */
+ if (!(val & 0xffff0000))
+ cp--;
+ else {
+ register u_short sv = (u_short)(val >> 16);
+ register u_short tmp;
+ register u_short ten = 10;
+
+ do {
+ tmp = sv;
+ sv /= ten;
+ *(--cp) = tmp - ((sv<<3) + (sv<<1));
+ } while (sv != 0);
+ }
+
+ /*
+ * Figure out how much of the fraction to do
+ */
+ if (msec) {
+ dec = ndec + 3;
+ if (dec < 3)
+ dec = 3;
+ cpdec = &cbuf[8];
+ } else {
+ dec = ndec;
+ cpdec = cpend;
+ }
+
+ if (dec > 6)
+ dec = 6;
+
+ if (dec > 0) {
+ do {
+ val &= 0xffff;
+ val = (val << 3) + (val << 1);
+ *cpend++ = (u_char)(val >> 16);
+ } while (--dec > 0);
+ }
+
+ if (val & 0x8000) {
+ register u_char *tp;
+ /*
+ * Round it. Ick.
+ */
+ tp = cpend;
+ *(--tp) += 1;
+ while (*tp >= 10) {
+ *tp = 0;
+ *(--tp) += 1;
+ }
+ }
+
+ /*
+ * Remove leading zeroes if necessary
+ */
+ while (cp < (cpdec -1) && *cp == 0)
+ cp++;
+
+ /*
+ * Copy it into the buffer, asciizing as we go.
+ */
+ bp = buf;
+ if (neg)
+ *bp++ = '-';
+
+ while (cp < cpend) {
+ if (cp == cpdec)
+ *bp++ = '.';
+ *bp++ = (char)(*cp++ + '0');
+ }
+ *bp = '\0';
+ return buf;
+}
diff --git a/usr.sbin/xntpd/lib/dolfptoa.c b/usr.sbin/xntpd/lib/dolfptoa.c
new file mode 100644
index 0000000..24fc213
--- /dev/null
+++ b/usr.sbin/xntpd/lib/dolfptoa.c
@@ -0,0 +1,161 @@
+/*
+ * dolfptoa - do the grunge work of converting an l_fp number to decimal
+ */
+#include <stdio.h>
+
+#include "ntp_fp.h"
+#include "lib_strbuf.h"
+#include "ntp_string.h"
+#include "ntp_stdlib.h"
+
+char *
+dolfptoa(fpi, fpv, neg, ndec, msec)
+ u_long fpi;
+ u_long fpv;
+ int neg;
+ int ndec;
+ int msec;
+{
+ register u_char *cp, *cpend;
+ register u_long lwork;
+ register int dec;
+ u_char cbuf[24];
+ u_char *cpdec;
+ char *buf;
+ char *bp;
+
+ /*
+ * Get a string buffer before starting
+ */
+ LIB_GETBUF(buf);
+
+ /*
+ * Zero the character buffer
+ */
+ memset((char *) cbuf, 0, sizeof(cbuf));
+
+ /*
+ * Work on the integral part. This is biased by what I know
+ * compiles fairly well for a 68000.
+ */
+ cp = cpend = &cbuf[10];
+ lwork = fpi;
+ if (lwork & 0xffff0000) {
+ register u_long lten = 10;
+ register u_long ltmp;
+
+ do {
+ ltmp = lwork;
+ lwork /= lten;
+ ltmp -= (lwork << 3) + (lwork << 1);
+ *--cp = (u_char)ltmp;
+ } while (lwork & 0xffff0000);
+ }
+ if (lwork != 0) {
+ register u_short sten = 10;
+ register u_short stmp;
+ register u_short swork = (u_short)lwork;
+
+ do {
+ stmp = swork;
+ swork /= sten;
+ stmp -= (swork<<3) + (swork<<1);
+ *--cp = (u_char)stmp;
+ } while (swork != 0);
+ }
+
+ /*
+ * Done that, now deal with the problem of the fraction. First
+ * determine the number of decimal places.
+ */
+ if (msec) {
+ dec = ndec + 3;
+ if (dec < 3)
+ dec = 3;
+ cpdec = &cbuf[13];
+ } else {
+ dec = ndec;
+ if (dec < 0)
+ dec = 0;
+ cpdec = &cbuf[10];
+ }
+ if (dec > 12)
+ dec = 12;
+
+ /*
+ * If there's a fraction to deal with, do so.
+ */
+ if (fpv != 0) {
+ l_fp work;
+
+ work.l_ui = 0;
+ work.l_uf = fpv;
+ while (dec > 0) {
+ l_fp ftmp;
+
+ dec--;
+ /*
+ * The scheme here is to multiply the
+ * fraction (0.1234...) by ten. This moves
+ * a junk of BCD into the units part.
+ * record that and iterate.
+ */
+ work.l_ui = 0;
+ L_LSHIFT(&work);
+ ftmp = work;
+ L_LSHIFT(&work);
+ L_LSHIFT(&work);
+ L_ADD(&work, &ftmp);
+ *cpend++ = (u_char)work.l_ui;
+ if (work.l_uf == 0)
+ break;
+ }
+
+ /*
+ * Rounding is rotten
+ */
+ if (work.l_uf & 0x80000000) {
+ register u_char *tp = cpend;
+
+ *(--tp) += 1;
+ while (*tp >= 10) {
+ *tp = 0;
+ *(--tp) += 1;
+ };
+ if (tp < cp)
+ cp = tp;
+ }
+ }
+ cpend += dec;
+
+
+ /*
+ * We've now got the fraction in cbuf[], with cp pointing at
+ * the first character, cpend pointing past the last, and
+ * cpdec pointing at the first character past the decimal.
+ * Remove leading zeros, then format the number into the
+ * buffer.
+ */
+ while (cp < cpdec) {
+ if (*cp != 0)
+ break;
+ cp++;
+ }
+ if (cp == cpdec)
+ --cp;
+
+ bp = buf;
+ if (neg)
+ *bp++ = '-';
+ while (cp < cpend) {
+ if (cp == cpdec)
+ *bp++ = '.';
+ *bp++ = (char)(*cp++ + '0'); /* ascii dependent? */
+ }
+ *bp = '\0';
+
+ /*
+ * Done!
+ */
+ return buf;
+}
diff --git a/usr.sbin/xntpd/lib/emalloc.c b/usr.sbin/xntpd/lib/emalloc.c
new file mode 100644
index 0000000..61d4cb6
--- /dev/null
+++ b/usr.sbin/xntpd/lib/emalloc.c
@@ -0,0 +1,20 @@
+/*
+ * emalloc - return new memory obtained from the system. Belch if none.
+ */
+#include "ntp_types.h"
+#include "ntp_malloc.h"
+#include "ntp_stdlib.h"
+#include "ntp_syslog.h"
+
+char *
+emalloc(size)
+ u_int size;
+{
+ char *mem;
+
+ if ((mem = (char *)malloc(size)) == 0) {
+ syslog(LOG_ERR, "No more memory!");
+ exit(1);
+ }
+ return mem;
+}
diff --git a/usr.sbin/xntpd/lib/findconfig.c b/usr.sbin/xntpd/lib/findconfig.c
new file mode 100755
index 0000000..a65f32c
--- /dev/null
+++ b/usr.sbin/xntpd/lib/findconfig.c
@@ -0,0 +1,62 @@
+#ifdef SYS_HPUX
+#include <string.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/utsname.h>
+
+char *
+FindConfig(base)
+ char *base;
+{
+ static char result[BUFSIZ];
+ char hostname[BUFSIZ], *cp;
+ struct stat sbuf;
+ struct utsname unamebuf;
+
+ /* All keyed by initial target being a directory */
+ (void) strcpy(result, base);
+ if (stat(result, &sbuf) == 0) {
+ if (S_ISDIR(sbuf.st_mode)) {
+
+ /* First choice is my hostname */
+ if (gethostname(hostname, BUFSIZ) >= 0) {
+ (void) sprintf(result, "%s/%s", base, hostname);
+ if (stat(result, &sbuf) == 0) {
+ goto outahere;
+ } else {
+
+ /* Second choice is of form default.835 */
+ (void) uname(&unamebuf);
+ if (strncmp(unamebuf.machine, "9000/", 5) == 0)
+ cp = unamebuf.machine + 5;
+ else
+ cp = unamebuf.machine;
+ (void) sprintf(result, "%s/default.%s", base, cp);
+ if (stat(result, &sbuf) == 0) {
+ goto outahere;
+ } else {
+
+ /* Last choice is just default */
+ (void) sprintf(result, "%s/default", base);
+ if (stat(result, &sbuf) == 0) {
+ goto outahere;
+ } else {
+ (void) strcpy(result, "/not/found");
+ }
+ }
+ }
+ }
+ }
+ }
+outahere:
+ return(result);
+}
+#else
+char *
+FindConfig(base)
+ char *base;
+{
+ return base;
+}
+#endif
diff --git a/usr.sbin/xntpd/lib/fptoa.c b/usr.sbin/xntpd/lib/fptoa.c
new file mode 100644
index 0000000..b0d7a14
--- /dev/null
+++ b/usr.sbin/xntpd/lib/fptoa.c
@@ -0,0 +1,24 @@
+/*
+ * fptoa - return an asciized representation of an s_fp number
+ */
+#include "ntp_fp.h"
+#include "ntp_stdlib.h"
+
+char *
+fptoa(fpv, ndec)
+ s_fp fpv;
+ int ndec;
+{
+ u_fp plusfp;
+ int neg;
+
+ if (fpv < 0) {
+ plusfp = (u_fp)(-fpv);
+ neg = 1;
+ } else {
+ plusfp = (u_fp)fpv;
+ neg = 0;
+ }
+
+ return dofptoa(plusfp, neg, ndec, 0);
+}
diff --git a/usr.sbin/xntpd/lib/fptoms.c b/usr.sbin/xntpd/lib/fptoms.c
new file mode 100644
index 0000000..f8ae6ba
--- /dev/null
+++ b/usr.sbin/xntpd/lib/fptoms.c
@@ -0,0 +1,23 @@
+/*
+ * fptoms - return an asciized s_fp number in milliseconds
+ */
+#include "ntp_fp.h"
+
+char *
+fptoms(fpv, ndec)
+ s_fp fpv;
+ int ndec;
+{
+ u_fp plusfp;
+ int neg;
+
+ if (fpv < 0) {
+ plusfp = (u_fp)(-fpv);
+ neg = 1;
+ } else {
+ plusfp = (u_fp)fpv;
+ neg = 0;
+ }
+
+ return dofptoa(plusfp, neg, ndec, 1);
+}
diff --git a/usr.sbin/xntpd/lib/getopt.c b/usr.sbin/xntpd/lib/getopt.c
new file mode 100644
index 0000000..b5164c60
--- /dev/null
+++ b/usr.sbin/xntpd/lib/getopt.c
@@ -0,0 +1,105 @@
+/*
+ * getopt - get option letter from argv
+ *
+ * This is a version of the public domain getopt() implementation by
+ * Henry Spencer, changed for 4.3BSD compatibility (in addition to System V).
+ * It allows rescanning of an option list by setting optind to 0 before
+ * calling, which is why we use it even if the system has its own (in fact,
+ * this one has a unique name so as not to conflict with the system's).
+ * Thanks to Dennis Ferguson for the appropriate modifications.
+ *
+ * This file is in the Public Domain.
+ */
+
+/*LINTLIBRARY*/
+
+#include <stdio.h>
+
+#include "ntp_stdlib.h"
+
+#ifdef lint
+#undef putc
+#define putc fputc
+#endif /* lint */
+
+char *ntp_optarg; /* Global argument pointer. */
+int ntp_optind = 0; /* Global argv index. */
+int ntp_opterr = 1; /* for compatibility, should error be printed? */
+int ntp_optopt; /* for compatibility, option character checked */
+
+static char *scan = NULL; /* Private scan pointer. */
+static char *prog = "amnesia";
+
+/*
+ * Print message about a bad option.
+ */
+static int
+badopt(mess, ch)
+ char *mess;
+ int ch;
+{
+ if (ntp_opterr) {
+ fputs(prog, stderr);
+ fputs(mess, stderr);
+ (void) putc(ch, stderr);
+ (void) putc('\n', stderr);
+ }
+ return ('?');
+}
+
+int
+ntp_getopt(argc, argv, optstring)
+ int argc;
+ char *argv[];
+ char *optstring;
+{
+ register char c;
+ register char *place;
+
+ prog = argv[0];
+ ntp_optarg = NULL;
+
+ if (ntp_optind == 0) {
+ scan = NULL;
+ ntp_optind++;
+ }
+
+ if (scan == NULL || *scan == '\0') {
+ if (ntp_optind >= argc
+ || argv[ntp_optind][0] != '-'
+ || argv[ntp_optind][1] == '\0') {
+ return (EOF);
+ }
+ if (argv[ntp_optind][1] == '-'
+ && argv[ntp_optind][2] == '\0') {
+ ntp_optind++;
+ return (EOF);
+ }
+
+ scan = argv[ntp_optind++]+1;
+ }
+
+ c = *scan++;
+ ntp_optopt = c & 0377;
+ for (place = optstring; place != NULL && *place != '\0'; ++place)
+ if (*place == c)
+ break;
+
+ if (place == NULL || *place == '\0' || c == ':' || c == '?') {
+ return (badopt(": unknown option -", c));
+ }
+
+ place++;
+ if (*place == ':') {
+ if (*scan != '\0') {
+ ntp_optarg = scan;
+ scan = NULL;
+ } else if (ntp_optind >= argc) {
+ return (badopt(": option requires argument -", c));
+ } else {
+ ntp_optarg = argv[ntp_optind++];
+ }
+ }
+
+ return (c & 0377);
+}
diff --git a/usr.sbin/xntpd/lib/gettstamp.c b/usr.sbin/xntpd/lib/gettstamp.c
new file mode 100644
index 0000000..53b124a
--- /dev/null
+++ b/usr.sbin/xntpd/lib/gettstamp.c
@@ -0,0 +1,29 @@
+/*
+ * gettstamp - return the system time in timestamp format
+ */
+#include <stdio.h>
+#include <sys/time.h>
+
+#include "ntp_fp.h"
+#include "ntp_unixtime.h"
+#include "ntp_stdlib.h"
+
+void
+gettstamp(ts)
+ l_fp *ts;
+{
+ struct timeval tv;
+
+ /*
+ * Quickly get the time of day and convert it
+ */
+ (void) GETTIMEOFDAY(&tv, (struct timezone *)NULL);
+ if (tv.tv_usec >= 1000000) { /* bum solaris */
+ tv.tv_usec -= 1000000;
+ tv.tv_sec++;
+ }
+ TVTOTS(&tv, ts);
+ ts->l_uf += TS_ROUNDBIT; /* guaranteed not to overflow */
+ ts->l_ui += JAN_1970;
+ ts->l_uf &= TS_MASK;
+}
diff --git a/usr.sbin/xntpd/lib/hextoint.c b/usr.sbin/xntpd/lib/hextoint.c
new file mode 100644
index 0000000..23f8c08
--- /dev/null
+++ b/usr.sbin/xntpd/lib/hextoint.c
@@ -0,0 +1,38 @@
+/*
+ * hextoint - convert an ascii string in hex to an unsigned
+ * long, with error checking
+ */
+#include <ctype.h>
+
+#include "ntp_stdlib.h"
+
+int
+hextoint(str, ival)
+ const char *str;
+ u_long *ival;
+{
+ register u_long u;
+ register const char *cp;
+
+ cp = str;
+
+ if (*cp == '\0')
+ return 0;
+
+ u = 0;
+ while (*cp != '\0') {
+ if (!isxdigit(*cp))
+ return 0;
+ if (u >= 0x10000000)
+ return 0; /* overflow */
+ u <<= 4;
+ if (*cp <= '9') /* very ascii dependent */
+ u += *cp++ - '0';
+ else if (*cp >= 'a')
+ u += *cp++ - 'a' + 10;
+ else
+ u += *cp++ - 'A' + 10;
+ }
+ *ival = u;
+ return 1;
+}
diff --git a/usr.sbin/xntpd/lib/hextolfp.c b/usr.sbin/xntpd/lib/hextolfp.c
new file mode 100644
index 0000000..104446c
--- /dev/null
+++ b/usr.sbin/xntpd/lib/hextolfp.c
@@ -0,0 +1,66 @@
+/*
+ * hextolfp - convert an ascii hex string to an l_fp number
+ */
+#include <stdio.h>
+#include <ctype.h>
+
+#include "ntp_fp.h"
+#include "ntp_string.h"
+#include "ntp_stdlib.h"
+
+int
+hextolfp(str, lfp)
+ const char *str;
+ l_fp *lfp;
+{
+ register const char *cp;
+ register const char *cpstart;
+ register u_long dec_i;
+ register u_long dec_f;
+ char *ind = NULL;
+ static char *digits = "0123456789abcdefABCDEF";
+
+ dec_i = dec_f = 0;
+ cp = str;
+
+ /*
+ * We understand numbers of the form:
+ *
+ * [spaces]8_hex_digits[.]8_hex_digits[spaces|\n|\0]
+ */
+ while (isspace(*cp))
+ cp++;
+
+ cpstart = cp;
+ while (*cp != '\0' && (cp - cpstart) < 8 &&
+ (ind = strchr(digits, *cp)) != NULL) {
+ dec_i = dec_i << 4; /* multiply by 16 */
+ dec_i += ((ind - digits) > 15) ? (ind - digits) - 6
+ : (ind - digits);
+ cp++;
+ }
+
+ if ((cp - cpstart) < 8 || ind == NULL)
+ return 0;
+ if (*cp == '.')
+ cp++;
+
+ cpstart = cp;
+ while (*cp != '\0' && (cp - cpstart) < 8 &&
+ (ind = strchr(digits, *cp)) != NULL) {
+ dec_f = dec_f << 4; /* multiply by 16 */
+ dec_f += ((ind - digits) > 15) ? (ind - digits) - 6
+ : (ind - digits);
+ cp++;
+ }
+
+ if ((cp - cpstart) < 8 || ind == NULL)
+ return 0;
+
+ if (*cp != '\0' && !isspace(*cp))
+ return 0;
+
+ lfp->l_ui = dec_i;
+ lfp->l_uf = dec_f;
+ return 1;
+}
diff --git a/usr.sbin/xntpd/lib/humandate.c b/usr.sbin/xntpd/lib/humandate.c
new file mode 100644
index 0000000..b7b89bc
--- /dev/null
+++ b/usr.sbin/xntpd/lib/humandate.c
@@ -0,0 +1,61 @@
+/*
+ * humandate - convert an NTP (or the current) time to something readable
+ */
+#include <stdio.h>
+
+#include "ntp_fp.h"
+#include "ntp_unixtime.h"
+#include "lib_strbuf.h"
+#include "ntp_stdlib.h"
+
+#ifdef NTP_POSIX_SOURCE
+#include <time.h>
+#endif
+
+static char *months[] = {
+ "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
+};
+static char *days[] = {
+ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
+};
+
+char *
+humandate(ntptime)
+ u_long ntptime;
+{
+ char *bp;
+ struct tm *tm;
+ time_t sec;
+
+ LIB_GETBUF(bp);
+
+ sec = ntptime - JAN_1970;
+ tm = localtime(&sec);
+
+ (void) sprintf(bp, "%s, %s %2d %4d %2d:%02d:%02d",
+ days[tm->tm_wday], months[tm->tm_mon], tm->tm_mday,
+ 1900+tm->tm_year, tm->tm_hour, tm->tm_min, tm->tm_sec);
+
+ return bp;
+}
+
+
+/* This is used in msyslog.c; we don't want to clutter up the log with
+ the year and day of the week, etc.; just the minimal date and time. */
+
+char *
+humanlogtime()
+{
+ char *bp;
+ time_t cursec = time((time_t *) 0);
+ struct tm *tm = localtime(&cursec);
+
+ LIB_GETBUF(bp);
+
+ (void) sprintf(bp, "%2d %s %02d:%02d:%02d",
+ tm->tm_mday, months[tm->tm_mon],
+ tm->tm_hour, tm->tm_min, tm->tm_sec);
+
+ return bp;
+}
diff --git a/usr.sbin/xntpd/lib/inttoa.c b/usr.sbin/xntpd/lib/inttoa.c
new file mode 100644
index 0000000..ff886f5
--- /dev/null
+++ b/usr.sbin/xntpd/lib/inttoa.c
@@ -0,0 +1,19 @@
+/*
+ * inttoa - return an asciized signed integer
+ */
+#include <stdio.h>
+
+#include "lib_strbuf.h"
+#include "ntp_stdlib.h"
+
+char *
+inttoa(ival)
+ long ival;
+{
+ register char *buf;
+
+ LIB_GETBUF(buf);
+
+ (void) sprintf(buf, "%ld", (long)ival);
+ return buf;
+}
diff --git a/usr.sbin/xntpd/lib/lib_strbuf.c b/usr.sbin/xntpd/lib/lib_strbuf.c
new file mode 100644
index 0000000..47df66d
--- /dev/null
+++ b/usr.sbin/xntpd/lib/lib_strbuf.c
@@ -0,0 +1,21 @@
+/*
+ * lib_strbuf - library string storage
+ */
+
+#include "lib_strbuf.h"
+
+/*
+ * Storage declarations
+ */
+char lib_stringbuf[LIB_NUMBUFS][LIB_BUFLENGTH];
+int lib_nextbuf;
+
+
+/*
+ * initialization routine. Might be needed if the code is ROMized.
+ */
+void
+init_lib()
+{
+ lib_nextbuf = 0;
+}
diff --git a/usr.sbin/xntpd/lib/lib_strbuf.h b/usr.sbin/xntpd/lib/lib_strbuf.h
new file mode 100644
index 0000000..699b978
--- /dev/null
+++ b/usr.sbin/xntpd/lib/lib_strbuf.h
@@ -0,0 +1,22 @@
+/*
+ * lib_strbuf.h - definitions for routines which use the common string buffers
+ */
+
+/*
+ * Sizes of things
+ */
+#define LIB_NUMBUFS 20
+#define LIB_BUFLENGTH 80
+
+/*
+ * Macro to get a pointer to the next buffer
+ */
+#define LIB_GETBUF(buf) \
+ do { \
+ buf = &lib_stringbuf[lib_nextbuf][0]; \
+ if (++lib_nextbuf >= LIB_NUMBUFS) \
+ lib_nextbuf = 0; \
+ } while (0)
+
+extern char lib_stringbuf[LIB_NUMBUFS][LIB_BUFLENGTH];
+extern int lib_nextbuf;
diff --git a/usr.sbin/xntpd/lib/machines.c b/usr.sbin/xntpd/lib/machines.c
new file mode 100644
index 0000000..2fc7785
--- /dev/null
+++ b/usr.sbin/xntpd/lib/machines.c
@@ -0,0 +1,61 @@
+/* machines.c - provide special support for peculiar architectures
+ *
+ * Real bummers unite !
+ *
+ * $Id$
+ */
+
+#include "ntp_stdlib.h"
+
+#ifdef SYS_PTX
+#include <sys/types.h>
+#include <sys/procstats.h>
+
+int
+settimeofday(tvp)
+ struct timeval *tvp;
+{
+ return (stime(&tvp->tv_sec)); /* lie as bad as SysVR4 */
+}
+
+int
+gettimeofday(tvp)
+ struct timeval *tvp;
+{
+ /*
+ * hi, this is Sequents sneak path to get to a clock
+ * this is also the most logical syscall for such a function
+ */
+ return (get_process_stats(tvp, PS_SELF, (struct procstats *) 0,
+ (struct procstats *) 0));
+}
+#endif
+
+#if !defined(NTP_POSIX_SOURCE) || defined(NTP_NEED_BOPS)
+void
+ntp_memset(a, x, c)
+ char *a;
+ int x, c;
+{
+ while (c-- > 0)
+ *a++ = x;
+}
+#endif /*POSIX*/
+
+#if defined(USE_CLOCK_SETTIME)
+
+#include <time.h>
+
+int
+settimeofday(tvp)
+ struct timeval *tvp;
+{
+ struct timespec ts;
+
+ /* Convert timeval to timespec */
+ ts.tv_sec = tvp->tv_sec;
+ ts.tv_nsec = 1000 * tvp->tv_usec;
+
+ return clock_settime(CLOCK_REALTIME, &ts);
+}
+#endif /* USE_CLOCK_SETTIME */
diff --git a/usr.sbin/xntpd/lib/md5.c b/usr.sbin/xntpd/lib/md5.c
new file mode 100644
index 0000000..a5aca7a
--- /dev/null
+++ b/usr.sbin/xntpd/lib/md5.c
@@ -0,0 +1,322 @@
+/*
+ ***********************************************************************
+ ** md5.c -- the source code for MD5 routines **
+ ** RSA Data Security, Inc. MD5 Message-Digest Algorithm **
+ ** Created: 2/17/90 RLR **
+ ** Revised: 1/91 SRD,AJ,BSK,JT Reference C Version **
+ ** Revised (for MD5): RLR 4/27/91 **
+ ** -- G modified to have y&~z instead of y&z **
+ ** -- FF, GG, HH modified to add in last register done **
+ ** -- Access pattern: round 2 works mod 5, round 3 works mod 3 **
+ ** -- distinct additive constant for each step **
+ ** -- round 4 added, working mod 7 **
+ ***********************************************************************
+ */
+
+/*
+ ***********************************************************************
+ ** Copyright (C) 1990, RSA Data Security, Inc. All rights reserved. **
+ ** **
+ ** License to copy and use this software is granted provided that **
+ ** it is identified as the "RSA Data Security, Inc. MD5 Message- **
+ ** Digest Algorithm" in all material mentioning or referencing this **
+ ** software or this function. **
+ ** **
+ ** License is also granted to make and use derivative works **
+ ** provided that such works are identified as "derived from the RSA **
+ ** Data Security, Inc. MD5 Message-Digest Algorithm" in all **
+ ** material mentioning or referencing the derived work. **
+ ** **
+ ** RSA Data Security, Inc. makes no representations concerning **
+ ** either the merchantability of this software or the suitability **
+ ** of this software for any particular purpose. It is provided "as **
+ ** is" without express or implied warranty of any kind. **
+ ** **
+ ** These notices must be retained in any copies of any part of this **
+ ** documentation and/or software. **
+ ***********************************************************************
+ */
+
+#include "md5.h"
+
+/*
+ ***********************************************************************
+ ** Message-digest routines: **
+ ** To form the message digest for a message M **
+ ** (1) Initialize a context buffer mdContext using MD5Init **
+ ** (2) Call MD5Update on mdContext and M **
+ ** (3) Call MD5Final on mdContext **
+ ** The message digest is now in mdContext->digest[0...15] **
+ ***********************************************************************
+ */
+
+/* forward declaration */
+static void Transform ();
+
+#ifdef __STDC__
+static const
+#else
+static
+#endif
+unsigned char PADDING[64] = {
+ 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+/* F, G, H and I are basic MD5 functions */
+#define F(x, y, z) (((x) & (y)) | ((~x) & (z)))
+#define G(x, y, z) (((x) & (z)) | ((y) & (~z)))
+#define H(x, y, z) ((x) ^ (y) ^ (z))
+#define I(x, y, z) ((y) ^ ((x) | (~z)))
+
+/* ROTATE_LEFT rotates x left n bits */
+#if defined(FAST_MD5) && defined(__GNUC__) && defined(mc68000)
+/*
+ * If we're on a 68000 based CPU and using a GNU C compiler with
+ * inline assembly code, we can speed this up a bit.
+ */
+inline UINT4 ROTATE_LEFT(UINT4 x, int n)
+{
+ asm("roll %2,%0" : "=d" (x) : "0" (x), "Ir" (n));
+ return x;
+}
+#else
+#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n))))
+#endif
+
+
+/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4 */
+/* Rotation is separate from addition to prevent recomputation */
+#define FF(a, b, c, d, x, s, ac) \
+ {(a) += F ((b), (c), (d)) + (x) + (UINT4)(ac); \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ (a) += (b); \
+ }
+#define GG(a, b, c, d, x, s, ac) \
+ {(a) += G ((b), (c), (d)) + (x) + (UINT4)(ac); \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ (a) += (b); \
+ }
+#define HH(a, b, c, d, x, s, ac) \
+ {(a) += H ((b), (c), (d)) + (x) + (UINT4)(ac); \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ (a) += (b); \
+ }
+#define II(a, b, c, d, x, s, ac) \
+ {(a) += I ((b), (c), (d)) + (x) + (UINT4)(ac); \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ (a) += (b); \
+ }
+
+/* The routine MD5Init initializes the message-digest context
+ mdContext. All fields are set to zero.
+ */
+void MD5Init (mdContext)
+MD5_CTX *mdContext;
+{
+ mdContext->i[0] = mdContext->i[1] = (UINT4)0;
+
+ /* Load magic initialization constants.
+ */
+ mdContext->buf[0] = (UINT4)0x67452301;
+ mdContext->buf[1] = (UINT4)0xefcdab89;
+ mdContext->buf[2] = (UINT4)0x98badcfe;
+ mdContext->buf[3] = (UINT4)0x10325476;
+}
+
+/* The routine MD5Update updates the message-digest context to
+ account for the presence of each of the characters inBuf[0..inLen-1]
+ in the message whose digest is being computed.
+ */
+void MD5Update (mdContext, inBuf, inLen)
+MD5_CTX *mdContext;
+unsigned char *inBuf;
+unsigned int inLen;
+{
+ UINT4 in[16];
+ int mdi;
+ unsigned int i, ii;
+
+ /* compute number of bytes mod 64 */
+ mdi = (int)((mdContext->i[0] >> 3) & 0x3F);
+
+ /* update number of bits */
+ if ((mdContext->i[0] + ((UINT4)inLen << 3)) < mdContext->i[0])
+ mdContext->i[1]++;
+ mdContext->i[0] += ((UINT4)inLen << 3);
+ mdContext->i[1] += ((UINT4)inLen >> 29);
+
+ while (inLen--) {
+ /* add new character to buffer, increment mdi */
+ mdContext->in[mdi++] = *inBuf++;
+
+ /* transform if necessary */
+ if (mdi == 0x40) {
+ for (i = 0, ii = 0; i < 16; i++, ii += 4)
+ in[i] = (((UINT4)mdContext->in[ii+3]) << 24) |
+ (((UINT4)mdContext->in[ii+2]) << 16) |
+ (((UINT4)mdContext->in[ii+1]) << 8) |
+ ((UINT4)mdContext->in[ii]);
+ Transform (mdContext->buf, in);
+ mdi = 0;
+ }
+ }
+}
+
+/* The routine MD5Final terminates the message-digest computation and
+ ends with the desired message digest in mdContext->digest[0...15].
+ */
+
+void MD5Final (mdContext)
+MD5_CTX *mdContext;
+{
+ UINT4 in[16];
+ int mdi;
+ unsigned int i, ii;
+ unsigned int padLen;
+
+ /* save number of bits */
+ in[14] = mdContext->i[0];
+ in[15] = mdContext->i[1];
+
+ /* compute number of bytes mod 64 */
+ mdi = (int)((mdContext->i[0] >> 3) & 0x3F);
+
+ /* pad out to 56 mod 64 */
+ padLen = (mdi < 56) ? (56 - mdi) : (120 - mdi);
+ MD5Update (mdContext, PADDING, padLen);
+
+ /* append length in bits and transform */
+ for (i = 0, ii = 0; i < 14; i++, ii += 4)
+ in[i] = (((UINT4)mdContext->in[ii+3]) << 24) |
+ (((UINT4)mdContext->in[ii+2]) << 16) |
+ (((UINT4)mdContext->in[ii+1]) << 8) |
+ ((UINT4)mdContext->in[ii]);
+ Transform (mdContext->buf, in);
+
+ /* store buffer in digest */
+ for (i = 0, ii = 0; i < 4; i++, ii += 4) {
+ mdContext->digest[ii] = (unsigned char)(mdContext->buf[i] & 0xFF);
+ mdContext->digest[ii+1] =
+ (unsigned char)((mdContext->buf[i] >> 8) & 0xFF);
+ mdContext->digest[ii+2] =
+ (unsigned char)((mdContext->buf[i] >> 16) & 0xFF);
+ mdContext->digest[ii+3] =
+ (unsigned char)((mdContext->buf[i] >> 24) & 0xFF);
+ }
+}
+
+/* Basic MD5 step. Transforms buf based on in.
+ */
+static void Transform (buf, in)
+UINT4 *buf;
+UINT4 *in;
+{
+ UINT4 a = buf[0], b = buf[1], c = buf[2], d = buf[3];
+
+ /* Round 1 */
+#define S11 7
+#define S12 12
+#define S13 17
+#define S14 22
+
+ FF ( a, b, c, d, in[ 0], S11, 0xd76aa478); /* 1 */
+ FF ( d, a, b, c, in[ 1], S12, 0xe8c7b756); /* 2 */
+ FF ( c, d, a, b, in[ 2], S13, 0x242070db); /* 3 */
+ FF ( b, c, d, a, in[ 3], S14, 0xc1bdceee); /* 4 */
+ FF ( a, b, c, d, in[ 4], S11, 0xf57c0faf); /* 5 */
+ FF ( d, a, b, c, in[ 5], S12, 0x4787c62a); /* 6 */
+ FF ( c, d, a, b, in[ 6], S13, 0xa8304613); /* 7 */
+ FF ( b, c, d, a, in[ 7], S14, 0xfd469501); /* 8 */
+ FF ( a, b, c, d, in[ 8], S11, 0x698098d8); /* 9 */
+ FF ( d, a, b, c, in[ 9], S12, 0x8b44f7af); /* 10 */
+ FF ( c, d, a, b, in[10], S13, 0xffff5bb1); /* 11 */
+ FF ( b, c, d, a, in[11], S14, 0x895cd7be); /* 12 */
+ FF ( a, b, c, d, in[12], S11, 0x6b901122); /* 13 */
+ FF ( d, a, b, c, in[13], S12, 0xfd987193); /* 14 */
+ FF ( c, d, a, b, in[14], S13, 0xa679438e); /* 15 */
+ FF ( b, c, d, a, in[15], S14, 0x49b40821); /* 16 */
+
+ /* Round 2 */
+#define S21 5
+#define S22 9
+#define S23 14
+#define S24 20
+ GG ( a, b, c, d, in[ 1], S21, 0xf61e2562); /* 17 */
+ GG ( d, a, b, c, in[ 6], S22, 0xc040b340); /* 18 */
+ GG ( c, d, a, b, in[11], S23, 0x265e5a51); /* 19 */
+ GG ( b, c, d, a, in[ 0], S24, 0xe9b6c7aa); /* 20 */
+ GG ( a, b, c, d, in[ 5], S21, 0xd62f105d); /* 21 */
+ GG ( d, a, b, c, in[10], S22, 0x2441453); /* 22 */
+ GG ( c, d, a, b, in[15], S23, 0xd8a1e681); /* 23 */
+ GG ( b, c, d, a, in[ 4], S24, 0xe7d3fbc8); /* 24 */
+ GG ( a, b, c, d, in[ 9], S21, 0x21e1cde6); /* 25 */
+ GG ( d, a, b, c, in[14], S22, 0xc33707d6); /* 26 */
+ GG ( c, d, a, b, in[ 3], S23, 0xf4d50d87); /* 27 */
+ GG ( b, c, d, a, in[ 8], S24, 0x455a14ed); /* 28 */
+ GG ( a, b, c, d, in[13], S21, 0xa9e3e905); /* 29 */
+ GG ( d, a, b, c, in[ 2], S22, 0xfcefa3f8); /* 30 */
+ GG ( c, d, a, b, in[ 7], S23, 0x676f02d9); /* 31 */
+ GG ( b, c, d, a, in[12], S24, 0x8d2a4c8a); /* 32 */
+
+ /* Round 3 */
+#define S31 4
+#define S32 11
+#define S33 16
+#define S34 23
+ HH ( a, b, c, d, in[ 5], S31, 0xfffa3942); /* 33 */
+ HH ( d, a, b, c, in[ 8], S32, 0x8771f681); /* 34 */
+ HH ( c, d, a, b, in[11], S33, 0x6d9d6122); /* 35 */
+ HH ( b, c, d, a, in[14], S34, 0xfde5380c); /* 36 */
+ HH ( a, b, c, d, in[ 1], S31, 0xa4beea44); /* 37 */
+ HH ( d, a, b, c, in[ 4], S32, 0x4bdecfa9); /* 38 */
+ HH ( c, d, a, b, in[ 7], S33, 0xf6bb4b60); /* 39 */
+ HH ( b, c, d, a, in[10], S34, 0xbebfbc70); /* 40 */
+ HH ( a, b, c, d, in[13], S31, 0x289b7ec6); /* 41 */
+ HH ( d, a, b, c, in[ 0], S32, 0xeaa127fa); /* 42 */
+ HH ( c, d, a, b, in[ 3], S33, 0xd4ef3085); /* 43 */
+ HH ( b, c, d, a, in[ 6], S34, 0x4881d05); /* 44 */
+ HH ( a, b, c, d, in[ 9], S31, 0xd9d4d039); /* 45 */
+ HH ( d, a, b, c, in[12], S32, 0xe6db99e5); /* 46 */
+ HH ( c, d, a, b, in[15], S33, 0x1fa27cf8); /* 47 */
+ HH ( b, c, d, a, in[ 2], S34, 0xc4ac5665); /* 48 */
+
+ /* Round 4 */
+#define S41 6
+#define S42 10
+#define S43 15
+#define S44 21
+ II ( a, b, c, d, in[ 0], S41, 0xf4292244); /* 49 */
+ II ( d, a, b, c, in[ 7], S42, 0x432aff97); /* 50 */
+ II ( c, d, a, b, in[14], S43, 0xab9423a7); /* 51 */
+ II ( b, c, d, a, in[ 5], S44, 0xfc93a039); /* 52 */
+ II ( a, b, c, d, in[12], S41, 0x655b59c3); /* 53 */
+ II ( d, a, b, c, in[ 3], S42, 0x8f0ccc92); /* 54 */
+ II ( c, d, a, b, in[10], S43, 0xffeff47d); /* 55 */
+ II ( b, c, d, a, in[ 1], S44, 0x85845dd1); /* 56 */
+ II ( a, b, c, d, in[ 8], S41, 0x6fa87e4f); /* 57 */
+ II ( d, a, b, c, in[15], S42, 0xfe2ce6e0); /* 58 */
+ II ( c, d, a, b, in[ 6], S43, 0xa3014314); /* 59 */
+ II ( b, c, d, a, in[13], S44, 0x4e0811a1); /* 60 */
+ II ( a, b, c, d, in[ 4], S41, 0xf7537e82); /* 61 */
+ II ( d, a, b, c, in[11], S42, 0xbd3af235); /* 62 */
+ II ( c, d, a, b, in[ 2], S43, 0x2ad7d2bb); /* 63 */
+ II ( b, c, d, a, in[ 9], S44, 0xeb86d391); /* 64 */
+
+ buf[0] += a;
+ buf[1] += b;
+ buf[2] += c;
+ buf[3] += d;
+}
+
+/*
+ ***********************************************************************
+ ** End of md5.c **
+ ******************************** (cut) ********************************
+ */
diff --git a/usr.sbin/xntpd/lib/mfptoa.c b/usr.sbin/xntpd/lib/mfptoa.c
new file mode 100644
index 0000000..7de3b6e
--- /dev/null
+++ b/usr.sbin/xntpd/lib/mfptoa.c
@@ -0,0 +1,22 @@
+/*
+ * mfptoa - Return an asciized representation of a signed long fp number
+ */
+#include "ntp_fp.h"
+#include "ntp_stdlib.h"
+
+char *
+mfptoa(fpi, fpf, ndec)
+ u_long fpi;
+ u_long fpf;
+ int ndec;
+{
+ int isneg;
+
+ if (M_ISNEG(fpi, fpf)) {
+ isneg = 1;
+ M_NEG(fpi, fpf);
+ } else
+ isneg = 0;
+
+ return dolfptoa(fpi, fpf, isneg, ndec, 0);
+}
diff --git a/usr.sbin/xntpd/lib/mfptoms.c b/usr.sbin/xntpd/lib/mfptoms.c
new file mode 100644
index 0000000..01a6f09
--- /dev/null
+++ b/usr.sbin/xntpd/lib/mfptoms.c
@@ -0,0 +1,22 @@
+/*
+ * mfptoms - Return an asciized signed long fp number in milliseconds
+ */
+#include "ntp_fp.h"
+#include "ntp_stdlib.h"
+
+char *
+mfptoms(fpi, fpf, ndec)
+ u_long fpi;
+ u_long fpf;
+ int ndec;
+{
+ int isneg;
+
+ if (M_ISNEG(fpi, fpf)) {
+ isneg = 1;
+ M_NEG(fpi, fpf);
+ } else
+ isneg = 0;
+
+ return dolfptoa(fpi, fpf, isneg, ndec, 1);
+}
diff --git a/usr.sbin/xntpd/lib/modetoa.c b/usr.sbin/xntpd/lib/modetoa.c
new file mode 100644
index 0000000..e8292b3
--- /dev/null
+++ b/usr.sbin/xntpd/lib/modetoa.c
@@ -0,0 +1,33 @@
+/*
+ * modetoa - return an asciized mode
+ */
+#include <stdio.h>
+
+#include "lib_strbuf.h"
+#include "ntp_stdlib.h"
+
+char *
+modetoa(mode)
+ int mode;
+{
+ char *bp;
+ static char *modestrings[] = {
+ "unspec",
+ "sym_active",
+ "sym_passive",
+ "client",
+ "server",
+ "broadcast",
+ "control",
+ "private",
+ "bclient",
+ };
+
+ if (mode < 0 || mode >= (sizeof modestrings)/sizeof(char *)) {
+ LIB_GETBUF(bp);
+ (void)sprintf(bp, "mode#%d", mode);
+ return bp;
+ }
+
+ return modestrings[mode];
+}
diff --git a/usr.sbin/xntpd/lib/mstolfp.c b/usr.sbin/xntpd/lib/mstolfp.c
new file mode 100644
index 0000000..e4c4d46
--- /dev/null
+++ b/usr.sbin/xntpd/lib/mstolfp.c
@@ -0,0 +1,99 @@
+/*
+ * mstolfp - convert an ascii string in milliseconds to an l_fp number
+ */
+#include <stdio.h>
+#include <ctype.h>
+
+#include "ntp_fp.h"
+#include "ntp_stdlib.h"
+
+int
+mstolfp(str, lfp)
+ const char *str;
+ l_fp *lfp;
+{
+ register const char *cp;
+ register char *bp;
+ register const char *cpdec;
+ char buf[100];
+
+ /*
+ * We understand numbers of the form:
+ *
+ * [spaces][-][digits][.][digits][spaces|\n|\0]
+ *
+ * This is one enormous hack. Since I didn't feel like
+ * rewriting the decoding routine for milliseconds, what
+ * is essentially done here is to make a copy of the string
+ * with the decimal moved over three places so the seconds
+ * decoding routine can be used.
+ */
+ bp = buf;
+ cp = str;
+ while (isspace(*cp))
+ cp++;
+
+ if (*cp == '-') {
+ *bp++ = '-';
+ cp++;
+ }
+
+ if (*cp != '.' && !isdigit(*cp))
+ return 0;
+
+
+ /*
+ * Search forward for the decimal point or the end of the string.
+ */
+ cpdec = cp;
+ while (isdigit(*cpdec))
+ cpdec++;
+
+ /*
+ * Found something. If we have more than three digits copy the
+ * excess over, else insert a leading 0.
+ */
+ if ((cpdec - cp) > 3) {
+ do {
+ *bp++ = (char)*cp++;
+ } while ((cpdec - cp) > 3);
+ } else {
+ *bp++ = '0';
+ }
+
+ /*
+ * Stick the decimal in. If we've got less than three digits in
+ * front of the millisecond decimal we insert the appropriate number
+ * of zeros.
+ */
+ *bp++ = '.';
+ if ((cpdec - cp) < 3) {
+ register int i = 3 - (cpdec - cp);
+
+ do {
+ *bp++ = '0';
+ } while (--i > 0);
+ }
+
+ /*
+ * Copy the remainder up to the millisecond decimal. If cpdec
+ * is pointing at a decimal point, copy in the trailing number too.
+ */
+ while (cp < cpdec)
+ *bp++ = (char)*cp++;
+
+ if (*cp == '.') {
+ cp++;
+ while (isdigit(*cp))
+ *bp++ = (char)*cp++;
+ }
+ *bp = '\0';
+
+ /*
+ * Check to make sure the string is properly terminated. If
+ * so, give the buffer to the decoding routine.
+ */
+ if (*cp != '\0' && !isspace(*cp))
+ return 0;
+ return atolfp(buf, lfp);
+}
diff --git a/usr.sbin/xntpd/lib/msutotsf.c b/usr.sbin/xntpd/lib/msutotsf.c
new file mode 100644
index 0000000..eb3babe
--- /dev/null
+++ b/usr.sbin/xntpd/lib/msutotsf.c
@@ -0,0 +1,35 @@
+/*
+ * msutotsf - tables for converting from a subsecond millisecond value
+ * to a time stamp fraction.
+ */
+#include <sys/types.h>
+
+#include "ntp_types.h"
+
+/*
+ * Index each of these tables with five bits of the (less than) 10
+ * bit millisecond value. Note that the tables are rounded (not
+ * truncated). The error in the result will thus be +-1 low order
+ * bit in the time stamp fraction.
+ */
+u_long msutotsflo[32] = {
+ 0x00000000, 0x00418937, 0x0083126f, 0x00c49ba6,
+ 0x010624dd, 0x0147ae14, 0x0189374c, 0x01cac083,
+ 0x020c49ba, 0x024dd2f2, 0x028f5c29, 0x02d0e560,
+ 0x03126e98, 0x0353f7cf, 0x03958106, 0x03d70a3d,
+ 0x04189375, 0x045a1cac, 0x049ba5e3, 0x04dd2f1b,
+ 0x051eb852, 0x05604189, 0x05a1cac1, 0x05e353f8,
+ 0x0624dd2f, 0x06666666, 0x06a7ef9e, 0x06e978d5,
+ 0x072b020c, 0x076c8b44, 0x07ae147b, 0x07ef9db2
+};
+
+u_long msutotsfhi[32] = {
+ 0x00000000, 0x083126e9, 0x10624dd3, 0x189374bc,
+ 0x20c49ba6, 0x28f5c28f, 0x3126e979, 0x39581062,
+ 0x4189374c, 0x49ba5e35, 0x51eb851f, 0x5a1cac08,
+ 0x624dd2f2, 0x6a7ef9db, 0x72b020c5, 0x7ae147ae,
+ 0x83126e98, 0x8b439581, 0x9374bc6a, 0x9ba5e354,
+ 0xa3d70a3d, 0xac083127, 0xb4395810, 0xbc6a7efa,
+ 0xc49ba5e3, 0xcccccccd, 0xd4fdf3b6, 0xdd2f1aa0,
+ 0xe5604189, 0xed916873, 0xf5c28f5c, 0xfdf3b646
+};
diff --git a/usr.sbin/xntpd/lib/msyslog.c b/usr.sbin/xntpd/lib/msyslog.c
new file mode 100644
index 0000000..d71b075
--- /dev/null
+++ b/usr.sbin/xntpd/lib/msyslog.c
@@ -0,0 +1,111 @@
+/*
+ * msyslog - either send a message to the terminal or print it on
+ * the standard output.
+ *
+ * Converted to use varargs, much better ... jks
+ */
+#include <stdio.h>
+#include <errno.h>
+
+/* alternative, as Solaris 2.x defines __STDC__ as 0 in a largely standard
+ conforming environment
+ #if __STDC__ || (defined(SOLARIS) && defined(__STDC__))
+*/
+#ifdef __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+#include "ntp_types.h"
+#include "ntp_string.h"
+#include "ntp_syslog.h"
+#include "ntp_stdlib.h"
+
+#undef syslog
+
+int syslogit = 1;
+FILE *syslog_file = NULL;
+
+extern int errno;
+extern char *progname;
+
+#if defined(__STDC__)
+void msyslog(int level, char *fmt, ...)
+#else
+/*VARARGS*/
+void msyslog(va_alist)
+ va_dcl
+#endif
+{
+#ifndef __STDC__
+ int level;
+ char *fmt;
+#endif
+ va_list ap;
+ char buf[1025], nfmt[256], xerr[50];
+ const char *err;
+ register int c, l;
+ register char *n, *f, *prog;
+#ifndef SYS_44BSD
+ extern int sys_nerr;
+ extern char *sys_errlist[];
+#endif
+ int olderrno;
+
+#ifdef __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+
+ level = va_arg(ap, int);
+ fmt = va_arg(ap, char *);
+#endif
+
+ olderrno = errno;
+ n = nfmt;
+ f = fmt;
+ while ((c = *f++) != '\0' && c != '\n' && n < &nfmt[252]) {
+ if (c != '%') {
+ *n++ = c;
+ continue;
+ }
+ if ((c = *f++) != 'm') {
+ *n++ = '%';
+ *n++ = c;
+ continue;
+ }
+ if ((unsigned)olderrno > sys_nerr)
+ sprintf((char *)(err = xerr), "error %d", olderrno);
+ else
+ err = sys_errlist[olderrno];
+ if (n + (l = strlen(err)) < &nfmt[254]) {
+ strcpy(n, err);
+ n += strlen(err);
+ }
+ }
+ if (!syslogit)
+ *n++ = '\n';
+ *n = '\0';
+
+ vsprintf(buf, nfmt, ap);
+ if (syslogit)
+ syslog(level, buf);
+ else {
+ extern char * humanlogtime P((void));
+
+ FILE *out_file = syslog_file ? syslog_file
+ : level <= LOG_ERR ? stderr : stdout;
+ /* syslog() provides the timestamp, so if we're not using
+ syslog, we must provide it. */
+ prog = strrchr(progname, '/');
+ if (prog == NULL)
+ prog = progname;
+ else
+ prog++;
+ (void) fprintf(out_file, "%s ", humanlogtime ());
+ (void) fprintf(out_file, "%s: %s", prog, buf);
+ fflush (out_file);
+ }
+ va_end(ap);
+}
diff --git a/usr.sbin/xntpd/lib/netof.c b/usr.sbin/xntpd/lib/netof.c
new file mode 100644
index 0000000..1823fb2
--- /dev/null
+++ b/usr.sbin/xntpd/lib/netof.c
@@ -0,0 +1,24 @@
+/*
+ * netof - return the net address part of an ip address
+ * (zero out host part)
+ */
+#include <stdio.h>
+
+#include "ntp_fp.h"
+#include "ntp_stdlib.h"
+
+u_long
+netof(num)
+ u_long num;
+{
+ register u_long netnum;
+
+ netnum = num;
+ if(IN_CLASSC(netnum))
+ netnum &= IN_CLASSC_NET;
+ else if (IN_CLASSB(netnum))
+ netnum &= IN_CLASSB_NET;
+ else /* treat all other like class A */
+ netnum &= IN_CLASSA_NET;
+ return netnum;
+}
diff --git a/usr.sbin/xntpd/lib/numtoa.c b/usr.sbin/xntpd/lib/numtoa.c
new file mode 100644
index 0000000..ef291c8
--- /dev/null
+++ b/usr.sbin/xntpd/lib/numtoa.c
@@ -0,0 +1,22 @@
+/*
+ * numtoa - return asciized network numbers store in local array space
+ */
+#include <stdio.h>
+
+#include "ntp_fp.h"
+#include "lib_strbuf.h"
+#include "ntp_stdlib.h"
+
+char *
+numtoa(num)
+ u_long num;
+{
+ register u_long netnum;
+ register char *buf;
+
+ netnum = ntohl(num);
+ LIB_GETBUF(buf);
+ (void) sprintf(buf, "%lu.%lu.%lu.%lu", (netnum >> 24) & 0xff,
+ (netnum >> 16) & 0xff, (netnum >> 8) & 0xff, netnum & 0xff);
+ return buf;
+}
diff --git a/usr.sbin/xntpd/lib/numtohost.c b/usr.sbin/xntpd/lib/numtohost.c
new file mode 100644
index 0000000..9d83584
--- /dev/null
+++ b/usr.sbin/xntpd/lib/numtohost.c
@@ -0,0 +1,38 @@
+/*
+ * numtohost - convert network number to host name.
+ */
+#include <netdb.h>
+
+#include "ntp_fp.h"
+#include "ntp_stdlib.h"
+#include "lib_strbuf.h"
+
+#define LOOPBACKNET 0x7f000000
+#define LOOPBACKHOST 0x7f000001
+#define LOOPBACKNETMASK 0xff000000
+
+char *
+numtohost(netnum)
+ u_long netnum;
+{
+ char *bp;
+ struct hostent *hp;
+
+ /*
+ * This is really gross, but saves lots of hanging looking for
+ * hostnames for the radio clocks. Don't bother looking up
+ * addresses on the loopback network except for the loopback
+ * host itself.
+ */
+ if ((((ntohl(netnum) & LOOPBACKNETMASK) == LOOPBACKNET)
+ && (ntohl(netnum) != LOOPBACKHOST))
+ || ((hp = gethostbyaddr((char *)&netnum, sizeof netnum, AF_INET))
+ == 0))
+ return numtoa(netnum);
+
+ LIB_GETBUF(bp);
+
+ bp[LIB_BUFLENGTH-1] = '\0';
+ (void) strncpy(bp, hp->h_name, LIB_BUFLENGTH-1);
+ return bp;
+}
diff --git a/usr.sbin/xntpd/lib/octtoint.c b/usr.sbin/xntpd/lib/octtoint.c
new file mode 100644
index 0000000..7598d72
--- /dev/null
+++ b/usr.sbin/xntpd/lib/octtoint.c
@@ -0,0 +1,34 @@
+/*
+ * octtoint - convert an ascii string in octal to an unsigned
+ * long, with error checking
+ */
+#include <stdio.h>
+#include <ctype.h>
+
+#include "ntp_stdlib.h"
+
+int
+octtoint(str, ival)
+ const char *str;
+ u_long *ival;
+{
+ register u_long u;
+ register const char *cp;
+
+ cp = str;
+
+ if (*cp == '\0')
+ return 0;
+
+ u = 0;
+ while (*cp != '\0') {
+ if (!isdigit(*cp) || *cp == '8' || *cp == '9')
+ return 0;
+ if (u >= 0x20000000)
+ return 0; /* overflow */
+ u <<= 3;
+ u += *cp++ - '0'; /* ascii dependent */
+ }
+ *ival = u;
+ return 1;
+}
diff --git a/usr.sbin/xntpd/lib/prettydate.c b/usr.sbin/xntpd/lib/prettydate.c
new file mode 100644
index 0000000..fd4a8b1
--- /dev/null
+++ b/usr.sbin/xntpd/lib/prettydate.c
@@ -0,0 +1,44 @@
+/*
+ * prettydate - convert a time stamp to something readable
+ */
+#include <stdio.h>
+
+#include "ntp_fp.h"
+#include "ntp_unixtime.h"
+#include "lib_strbuf.h"
+#include "ntp_stdlib.h"
+
+#ifdef NTP_POSIX_SOURCE
+#include <time.h>
+#endif
+
+char *
+prettydate(ts)
+ l_fp *ts;
+{
+ char *bp;
+ struct tm *tm;
+ time_t sec;
+ u_long msec;
+ static char *months[] = {
+ "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
+ };
+ static char *days[] = {
+ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
+ };
+
+ LIB_GETBUF(bp);
+
+ sec = ts->l_ui - JAN_1970;
+ msec = ts->l_uf / 4294967; /* fract / (2 ** 32 / 1000) */
+
+ tm = localtime(&sec);
+
+ (void) sprintf(bp, "%08lx.%08lx %s, %s %2d %4d %2d:%02d:%02d.%03lu",
+ (u_long)ts->l_ui, (u_long)ts->l_uf, days[tm->tm_wday],
+ months[tm->tm_mon], tm->tm_mday, 1900 + tm->tm_year,
+ tm->tm_hour,tm->tm_min, tm->tm_sec, msec);
+
+ return bp;
+}
diff --git a/usr.sbin/xntpd/lib/ranny.c b/usr.sbin/xntpd/lib/ranny.c
new file mode 100644
index 0000000..3fe7b22
--- /dev/null
+++ b/usr.sbin/xntpd/lib/ranny.c
@@ -0,0 +1,81 @@
+/*
+ * Random number generator is:
+ *
+ * Copyright 1988 by Rayan S. Zachariassen, all rights reserved.
+ * This will be free software, but only when it is finished.
+ *
+ * Used in xntp by permission of the author. If copyright is
+ * annoying to you, read no further. Instead, look up the reference,
+ * write me an equivalent to this and send it back to me.
+ */
+
+/*
+ * Random number generator; see Knuth Vol 2. 2nd ed. p.27 (section 3.2.2)
+ */
+#include "ntp_stdlib.h"
+
+extern time_t time P((time_t *loc));
+
+/*
+ * 55 random numbers, not all even. Note we don't initialize ran_y
+ * directly since I have had thoughts of putting this in an EPROM
+ */
+static time_t ran_y[55];
+
+static time_t init_ran_y[55] = {
+ 1860909544, 231033423, 437666411, 1349655137, 2014584962,
+ 504613712, 656256107, 1246027206, 573713775, 643466871,
+ 540235388, 1630565153, 443649364, 729302839, 1933991552,
+ 944681982, 949111118, 406212522, 1065063137, 1712954727,
+ 73280612, 787623973, 1874130997, 801658492, 73395958,
+ 739165367, 596047144, 490055249, 1131094323, 662727104,
+ 483614097, 844520219, 893760527, 921280508, 46691708,
+ 760861842, 1425894220, 702947816, 2006889048, 1999607995,
+ 1346414687, 399640789, 1482689501, 1790064052, 1128943628,
+ 1269197405, 587262386, 2078054746, 1675409928, 1652325524,
+ 1643525825, 1748690540, 292465849, 1370173174, 402865384
+};
+
+static int ran_j;
+static int ran_k;
+
+
+/*
+ * ranp2 - return a random integer in the range 0 .. (1 << m) - 1
+ */
+u_long
+ranp2(m)
+ int m;
+{
+ time_t r;
+
+ ran_y[ran_k] += ran_y[ran_j]; /* overflow does a mod */
+ r = ran_y[ran_k];
+ if (ran_k-- == 0)
+ ran_k = 54;
+ if (ran_j-- == 0)
+ ran_j = 54;
+ return (u_long)(r & ((1 << m ) - 1));
+}
+
+/*
+ * init_random - do initialization of random number routine
+ */
+void
+init_random()
+{
+ register int i;
+ register time_t now;
+
+ ran_j = 23;
+ ran_k = 54;
+
+ /*
+ * Randomize the seed array some more. The time of day
+ * should be initialized by now.
+ */
+ now = time((time_t *)0) | 01;
+
+ for (i = 0; i < 55; ++i)
+ ran_y[i] = now * init_ran_y[i]; /* overflow does a mod */
+}
diff --git a/usr.sbin/xntpd/lib/refnumtoa.c b/usr.sbin/xntpd/lib/refnumtoa.c
new file mode 100644
index 0000000..8bb7d38
--- /dev/null
+++ b/usr.sbin/xntpd/lib/refnumtoa.c
@@ -0,0 +1,30 @@
+/*
+ * refnumtoa - return asciized refclock addresses stored in local array space
+ */
+#include <stdio.h>
+
+#include "ntp_fp.h"
+#include "lib_strbuf.h"
+#include "ntp_stdlib.h"
+
+char *
+refnumtoa(num)
+ u_long num;
+{
+ register u_long netnum;
+ register char *buf;
+ register const char *rclock;
+
+ netnum = ntohl(num);
+
+ LIB_GETBUF(buf);
+
+ rclock = clockname((int)((netnum >> 8) & 0xff));
+
+ if (rclock != NULL)
+ (void)sprintf(buf, "%s(%lu)", rclock, netnum & 0xff);
+ else
+ (void)sprintf(buf, "REFCLK(%lu,%lu)",
+ (netnum >> 8) & 0xff, netnum&0xff);
+ return buf;
+}
diff --git a/usr.sbin/xntpd/lib/syssignal.c b/usr.sbin/xntpd/lib/syssignal.c
new file mode 100644
index 0000000..f8abfdd
--- /dev/null
+++ b/usr.sbin/xntpd/lib/syssignal.c
@@ -0,0 +1,45 @@
+#include <stdio.h>
+#include <sys/types.h>
+#include <signal.h>
+
+#include "ntp_stdlib.h"
+
+#if defined(NTP_POSIX_SOURCE)
+#include <errno.h>
+
+extern int errno;
+
+void
+signal_no_reset(sig, func)
+int sig;
+void (*func)();
+{
+ int n;
+ struct sigaction vec;
+
+ vec.sa_handler = func;
+ sigemptyset(&vec.sa_mask);
+ vec.sa_flags = 0;
+
+ while (1) {
+ n = sigaction(sig, &vec, NULL);
+ if (n == -1 && errno == EINTR) continue;
+ break;
+ }
+ if (n == -1) {
+ perror("sigaction");
+ exit(1);
+ }
+}
+
+#else
+RETSIGTYPE
+signal_no_reset(sig, func)
+int sig;
+RETSIGTYPE (*func)();
+{
+ signal(sig, func);
+
+}
+#endif
+
diff --git a/usr.sbin/xntpd/lib/systime.c b/usr.sbin/xntpd/lib/systime.c
new file mode 100644
index 0000000..53f0e67
--- /dev/null
+++ b/usr.sbin/xntpd/lib/systime.c
@@ -0,0 +1,376 @@
+/*
+ * systime -- routines to fiddle a UNIX clock.
+ */
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#if defined(SYS_HPUX) || defined(sgi) || defined(SYS_BSDI) || defined(SYS_44BSD)
+#include <sys/param.h>
+#include <utmp.h>
+#endif
+
+#ifdef SYS_LINUX
+#include "sys/timex.h"
+#endif
+
+#include "ntp_fp.h"
+#include "ntp_syslog.h"
+#include "ntp_unixtime.h"
+#include "ntp_stdlib.h"
+
+#if defined(STEP_SLEW)
+#define SLEWALWAYS
+#endif
+
+extern int debug;
+
+/*
+ * These routines (init_systime, get_systime, step_systime, adj_systime)
+ * implement an interface between the (more or less) system independent
+ * bits of NTP and the peculiarities of dealing with the Unix system
+ * clock. These routines will run with good precision fairly independently
+ * of your kernel's value of tickadj. I couldn't tell the difference
+ * between tickadj==40 and tickadj==5 on a microvax, though I prefer
+ * to set tickadj == 500/hz when in doubt. At your option you
+ * may compile this so that your system's clock is always slewed to the
+ * correct time even for large corrections. Of course, all of this takes
+ * a lot of code which wouldn't be needed with a reasonable tickadj and
+ * a willingness to let the clock be stepped occasionally. Oh well.
+ */
+
+/*
+ * Clock variables. We round calls to adjtime() to adj_precision
+ * microseconds, and limit the adjustment to tvu_maxslew microseconds
+ * (tsf_maxslew fractional sec) in one adjustment interval. As we are
+ * thus limited in the speed and precision with which we can adjust the
+ * clock, we compensate by keeping the known "error" in the system time
+ * in sys_clock_offset. This is added to timestamps returned by get_systime().
+ * We also remember the clock precision we computed from the kernel in
+ * case someone asks us.
+ */
+ long sys_clock;
+
+ long adj_precision; /* adj precision in usec (tickadj) */
+ long tvu_maxslew; /* maximum adjust doable in 1<<CLOCK_ADJ sec (usec) */
+
+ u_long tsf_maxslew; /* same as above, as long format */
+
+ l_fp sys_clock_offset; /* correction for current system time */
+
+/*
+ * get_systime - return the system time in timestamp format
+ * As a side effect, update sys_clock.
+ */
+void
+get_systime(ts)
+ l_fp *ts;
+{
+ struct timeval tv;
+
+#if !defined(SLEWALWAYS)
+ /*
+ * Quickly get the time of day and convert it
+ */
+ (void) GETTIMEOFDAY(&tv, (struct timezone *)0);
+ TVTOTS(&tv, ts);
+ ts->l_uf += TS_ROUNDBIT; /* guaranteed not to overflow */
+#else
+ /*
+ * Get the time of day, convert to time stamp format
+ * and add in the current time offset. Then round
+ * appropriately.
+ */
+ (void) GETTIMEOFDAY(&tv, (struct timezone *)0);
+ TVTOTS(&tv, ts);
+ L_ADD(ts, &sys_clock_offset);
+ if (ts->l_uf & TS_ROUNDBIT)
+ L_ADDUF(ts, TS_ROUNDBIT);
+#endif /* !defined(SLEWALWAYS) */
+ ts->l_ui += JAN_1970;
+ ts->l_uf &= TS_MASK;
+
+ sys_clock = ts->l_ui;
+}
+
+/*
+ * step_systime - do a step adjustment in the system time (at least from
+ * NTP's point of view.
+ */
+int
+step_systime(ts)
+ l_fp *ts;
+{
+#ifdef SLEWALWAYS
+#ifdef STEP_SLEW
+ register u_long tmp_ui;
+ register u_long tmp_uf;
+ int isneg;
+ int n;
+
+ /*
+ * Take the absolute value of the offset
+ */
+ tmp_ui = ts->l_ui;
+ tmp_uf = ts->l_uf;
+ if (M_ISNEG(tmp_ui, tmp_uf)) {
+ M_NEG(tmp_ui, tmp_uf);
+ isneg = 1;
+ } else
+ isneg = 0;
+
+ if (tmp_ui >= 3) { /* Step it and slew we might win */
+ n = step_systime_real(ts);
+ if (!n) return n;
+ if (isneg)
+ ts->l_ui = ~0;
+ else
+ ts->l_ui = ~0;
+ }
+#endif
+ /*
+ * Just add adjustment into the current offset. The update
+ * routine will take care of bringing the system clock into
+ * line.
+ */
+ L_ADD(&sys_clock_offset, ts);
+ return 1;
+#else /* SLEWALWAYS */
+ return step_systime_real(ts);
+#endif /* SLEWALWAYS */
+}
+
+int max_no_complete = 20;
+
+/*
+ * adj_systime - called once every 1<<CLOCK_ADJ seconds to make system time
+ * adjustments.
+ */
+int
+adj_systime(ts)
+ l_fp *ts;
+{
+ register u_long offset_i, offset_f;
+ register long temp;
+ register u_long residual;
+ register int isneg = 0;
+ struct timeval adjtv, oadjtv;
+ l_fp oadjts;
+ long adj = ts->l_f;
+ int rval;
+
+ adjtv.tv_sec = adjtv.tv_usec = 0;
+
+ /*
+ * Move the current offset into the registers
+ */
+ offset_i = sys_clock_offset.l_ui;
+ offset_f = sys_clock_offset.l_uf;
+
+ /*
+ * Add the new adjustment into the system offset. Adjust the
+ * system clock to minimize this.
+ */
+ M_ADDF(offset_i, offset_f, adj);
+ if (M_ISNEG(offset_i, offset_f)) {
+ isneg = 1;
+ M_NEG(offset_i, offset_f);
+ }
+#ifdef DEBUG
+ if (debug > 4)
+ syslog(LOG_DEBUG, "adj_systime(%s): offset = %s%s\n",
+ mfptoa((adj<0?-1:0), adj, 9), isneg?"-":"",
+ umfptoa(offset_i, offset_f, 9));
+#endif
+
+ adjtv.tv_sec = 0;
+ if (offset_i > 0 || offset_f >= tsf_maxslew) {
+ /*
+ * Slew is bigger than we can complete in
+ * the adjustment interval. Make a maximum
+ * sized slew and reduce sys_clock_offset by this
+ * much.
+ */
+ M_SUBUF(offset_i, offset_f, tsf_maxslew);
+ if (!isneg) {
+ adjtv.tv_usec = tvu_maxslew;
+ } else {
+ adjtv.tv_usec = -tvu_maxslew;
+ M_NEG(offset_i, offset_f);
+ }
+
+#ifdef DEBUG
+ if (debug > 4)
+ printf("systime: maximum slew: %s%s, remainder = %s\n",
+ isneg?"-":"", umfptoa(0, tsf_maxslew, 9),
+ mfptoa(offset_i, offset_f, 9));
+#endif
+ } else {
+ /*
+ * We can do this slew in the time period. Do our
+ * best approximation (rounded), save residual for
+ * next adjustment.
+ *
+ * Note that offset_i is guaranteed to be 0 here.
+ */
+ TSFTOTVU(offset_f, temp);
+#ifndef ADJTIME_IS_ACCURATE
+ /*
+ * Round value to be an even multiple of adj_precision
+ */
+ residual = temp % adj_precision;
+ temp -= residual;
+ if (residual << 1 >= adj_precision)
+ temp += adj_precision;
+#endif /* ADJTIME_IS_ACCURATE */
+ TVUTOTSF(temp, residual);
+ M_SUBUF(offset_i, offset_f, residual);
+ if (isneg) {
+ adjtv.tv_usec = -temp;
+ M_NEG(offset_i, offset_f);
+ } else {
+ adjtv.tv_usec = temp;
+ }
+#ifdef DEBUG
+ if (debug > 4)
+ printf(
+ "systime: adjtv = %s, adjts = %s, sys_clock_offset = %s\n",
+ tvtoa(&adjtv), umfptoa(0, residual, 9),
+ mfptoa(offset_i, offset_f, 9));
+#endif
+ }
+
+ if (adjtime(&adjtv, &oadjtv) < 0) {
+ syslog(LOG_ERR, "Can't do time adjustment: %m");
+ rval = 0;
+ } else {
+ sys_clock_offset.l_ui = offset_i;
+ sys_clock_offset.l_uf = offset_f;
+ rval = 1;
+
+#ifdef DEBUGRS6000
+ syslog(LOG_ERR, "adj_systime(%s): offset = %s%s\n",
+ mfptoa((adj<0?-1:0), adj, 9), isneg?"-":"",
+ umfptoa(offset_i, offset_f, 9));
+ syslog(LOG_ERR, "%d %d %d %d\n", (int) adjtv.tv_sec,
+ (int) adjtv.tv_usec, (int) oadjtv.tv_sec, (int)
+ oadjtv.tv_usec);
+#endif /* DEBUGRS6000 */
+
+ if (oadjtv.tv_sec != 0 || oadjtv.tv_usec != 0) {
+ sTVTOTS(&oadjtv, &oadjts);
+ L_ADD(&sys_clock_offset, &oadjts);
+ if (max_no_complete > 0) {
+ syslog(LOG_WARNING,
+ "Previous time adjustment didn't complete");
+#ifdef DEBUG
+ if (debug > 4)
+ syslog(LOG_DEBUG,
+ "Previous adjtime() incomplete, residual = %s\n",
+ tvtoa(&oadjtv));
+#endif
+ if (--max_no_complete == 0)
+ syslog(LOG_WARNING,
+ "*** No more 'Prev time adj didn't complete'");
+ }
+ }
+ }
+ return(rval);
+}
+
+
+/*
+ * This is used by ntpdate even when xntpd does not use it! WLJ
+ */
+int
+step_systime_real(ts)
+ l_fp *ts;
+{
+ struct timeval timetv, adjtv;
+ int isneg = 0;
+#if defined(SYS_HPUX)
+ struct utmp ut;
+ time_t oldtime;
+#endif
+
+ /*
+ * We can afford to be sloppy here since if this is called
+ * the time is really screwed and everything is being reset.
+ */
+ L_ADD(&sys_clock_offset, ts);
+
+ if (L_ISNEG(&sys_clock_offset)) {
+ isneg = 1;
+ L_NEG(&sys_clock_offset);
+ }
+ TSTOTV(&sys_clock_offset, &adjtv);
+
+ (void) GETTIMEOFDAY(&timetv, (struct timezone *)0);
+#if defined(SYS_HPUX)
+ oldtime = timetv.tv_sec;
+#endif
+#ifdef DEBUG
+ if (debug > 3)
+ syslog(LOG_DEBUG, "step: %s, sys_clock_offset = %s, adjtv = %s, timetv = %s\n",
+ lfptoa(ts, 9), lfptoa(&sys_clock_offset, 9), tvtoa(&adjtv),
+ utvtoa(&timetv));
+#endif
+ if (isneg) {
+ timetv.tv_sec -= adjtv.tv_sec;
+ timetv.tv_usec -= adjtv.tv_usec;
+ if (timetv.tv_usec < 0) {
+ timetv.tv_sec--;
+ timetv.tv_usec += 1000000;
+ }
+ } else {
+ timetv.tv_sec += adjtv.tv_sec;
+ timetv.tv_usec += adjtv.tv_usec;
+ if (timetv.tv_usec >= 1000000) {
+ timetv.tv_sec++;
+ timetv.tv_usec -= 1000000;
+ }
+ }
+ if (SETTIMEOFDAY(&timetv, (struct timezone *)0) != 0) {
+ syslog(LOG_ERR, "Can't set time of day: %m");
+ return 0;
+ }
+#ifdef DEBUG
+ if (debug > 3)
+ syslog(LOG_DEBUG, "step: new timetv = %s\n", utvtoa(&timetv));
+#endif
+ sys_clock_offset.l_ui = sys_clock_offset.l_uf = 0;
+#if defined(SYS_HPUX)
+#if (SYS_HPUX < 10)
+ /*
+ * CHECKME: is this correct when called by ntpdate?????
+ */
+ _clear_adjtime();
+#endif
+ /*
+ * Write old and new time entries in utmp and wtmp if step adjustment
+ * is greater than one second.
+ */
+ if (oldtime != timetv.tv_sec) {
+ memset((char *)&ut, 0, sizeof(ut));
+ ut.ut_type = OLD_TIME;
+ ut.ut_time = oldtime;
+ (void)strcpy(ut.ut_line, OTIME_MSG);
+ pututline(&ut);
+ setutent();
+ ut.ut_type = NEW_TIME;
+ ut.ut_time = timetv.tv_sec;
+ (void)strcpy(ut.ut_line, NTIME_MSG);
+ pututline(&ut);
+ utmpname(WTMP_FILE);
+ ut.ut_type = OLD_TIME;
+ ut.ut_time = oldtime;
+ (void)strcpy(ut.ut_line, OTIME_MSG);
+ pututline(&ut);
+ ut.ut_type = NEW_TIME;
+ ut.ut_time = timetv.tv_sec;
+ (void)strcpy(ut.ut_line, NTIME_MSG);
+ pututline(&ut);
+ endutent();
+ }
+#endif
+ return 1;
+}
diff --git a/usr.sbin/xntpd/lib/tsftomsu.c b/usr.sbin/xntpd/lib/tsftomsu.c
new file mode 100644
index 0000000..9904b4f
--- /dev/null
+++ b/usr.sbin/xntpd/lib/tsftomsu.c
@@ -0,0 +1,37 @@
+/*
+ * tsftomsu - convert from a time stamp fraction to milliseconds
+ */
+#include "ntp_fp.h"
+#include "ntp_stdlib.h"
+
+int
+tsftomsu(tsf, round)
+ u_long tsf;
+ int round;
+{
+ register long val_ui, val_uf;
+ register long tmp_ui, tmp_uf;
+ register int i;
+
+ /*
+ * Essentially, multiply by 10 three times in l_fp form.
+ * The integral part is the milliseconds.
+ */
+ val_ui = 0;
+ val_uf = tsf;
+ for (i = 3; i > 0; i--) {
+ M_LSHIFT(val_ui, val_uf);
+ tmp_ui = val_ui;
+ tmp_uf = val_uf;
+ M_LSHIFT(val_ui, val_uf);
+ M_LSHIFT(val_ui, val_uf);
+ M_ADD(val_ui, val_uf, tmp_ui, tmp_uf);
+ }
+
+ /*
+ * Round the value if need be, then return it.
+ */
+ if (round && (val_uf & 0x80000000))
+ val_ui++;
+ return (int)val_ui;
+}
diff --git a/usr.sbin/xntpd/lib/tstotod.c b/usr.sbin/xntpd/lib/tstotod.c
new file mode 100644
index 0000000..a78aea1
--- /dev/null
+++ b/usr.sbin/xntpd/lib/tstotod.c
@@ -0,0 +1,21 @@
+#ifdef ELIMINATE
+/* tstotod.c,v 3.1 1993/07/06 01:08:48 jbj Exp
+ * tstotod - compute calendar time given an NTP timestamp
+ */
+#include <stdio.h>
+
+#include "ntp_fp.h"
+#include "ntp_calendar.h"
+#include "ntp_stdlib.h"
+
+void
+tstotod(ts, tod)
+ l_fp *ts;
+ struct calendar *tod;
+{
+ register U_LONG cyclesecs;
+
+ cyclesecs = ts.l_ui - MAR_1900; /* bump forward to March 1900 */
+
+}
+#endif /* ELIMINATE */
diff --git a/usr.sbin/xntpd/lib/tstotv.c b/usr.sbin/xntpd/lib/tstotv.c
new file mode 100644
index 0000000..be4bdd4
--- /dev/null
+++ b/usr.sbin/xntpd/lib/tstotv.c
@@ -0,0 +1,135 @@
+/*
+ * tstotv - tables for converting from NTP time stamps to struct timeval
+ */
+
+#include "ntp_types.h"
+
+/*
+ * Tables to convert from a time stamp fraction to usecs. Note that
+ * the units of these tables are actually (usec<<3). We carry three
+ * guard bits so that the result can be properly truncated (or rounded)
+ * to be correct to the least significant bit.
+ *
+ * These tables are rounded.
+ */
+
+long tstoushi[256] = {
+ 0x000000, 0x007a12, 0x00f424, 0x016e36,
+ 0x01e848, 0x02625a, 0x02dc6c, 0x03567e,
+ 0x03d090, 0x044aa2, 0x04c4b4, 0x053ec6,
+ 0x05b8d8, 0x0632ea, 0x06acfc, 0x07270e,
+ 0x07a120, 0x081b32, 0x089544, 0x090f56,
+ 0x098968, 0x0a037a, 0x0a7d8c, 0x0af79e,
+ 0x0b71b0, 0x0bebc2, 0x0c65d4, 0x0cdfe6,
+ 0x0d59f8, 0x0dd40a, 0x0e4e1c, 0x0ec82e,
+ 0x0f4240, 0x0fbc52, 0x103664, 0x10b076,
+ 0x112a88, 0x11a49a, 0x121eac, 0x1298be,
+ 0x1312d0, 0x138ce2, 0x1406f4, 0x148106,
+ 0x14fb18, 0x15752a, 0x15ef3c, 0x16694e,
+ 0x16e360, 0x175d72, 0x17d784, 0x185196,
+ 0x18cba8, 0x1945ba, 0x19bfcc, 0x1a39de,
+ 0x1ab3f0, 0x1b2e02, 0x1ba814, 0x1c2226,
+ 0x1c9c38, 0x1d164a, 0x1d905c, 0x1e0a6e,
+ 0x1e8480, 0x1efe92, 0x1f78a4, 0x1ff2b6,
+ 0x206cc8, 0x20e6da, 0x2160ec, 0x21dafe,
+ 0x225510, 0x22cf22, 0x234934, 0x23c346,
+ 0x243d58, 0x24b76a, 0x25317c, 0x25ab8e,
+ 0x2625a0, 0x269fb2, 0x2719c4, 0x2793d6,
+ 0x280de8, 0x2887fa, 0x29020c, 0x297c1e,
+ 0x29f630, 0x2a7042, 0x2aea54, 0x2b6466,
+ 0x2bde78, 0x2c588a, 0x2cd29c, 0x2d4cae,
+ 0x2dc6c0, 0x2e40d2, 0x2ebae4, 0x2f34f6,
+ 0x2faf08, 0x30291a, 0x30a32c, 0x311d3e,
+ 0x319750, 0x321162, 0x328b74, 0x330586,
+ 0x337f98, 0x33f9aa, 0x3473bc, 0x34edce,
+ 0x3567e0, 0x35e1f2, 0x365c04, 0x36d616,
+ 0x375028, 0x37ca3a, 0x38444c, 0x38be5e,
+ 0x393870, 0x39b282, 0x3a2c94, 0x3aa6a6,
+ 0x3b20b8, 0x3b9aca, 0x3c14dc, 0x3c8eee,
+ 0x3d0900, 0x3d8312, 0x3dfd24, 0x3e7736,
+ 0x3ef148, 0x3f6b5a, 0x3fe56c, 0x405f7e,
+ 0x40d990, 0x4153a2, 0x41cdb4, 0x4247c6,
+ 0x42c1d8, 0x433bea, 0x43b5fc, 0x44300e,
+ 0x44aa20, 0x452432, 0x459e44, 0x461856,
+ 0x469268, 0x470c7a, 0x47868c, 0x48009e,
+ 0x487ab0, 0x48f4c2, 0x496ed4, 0x49e8e6,
+ 0x4a62f8, 0x4add0a, 0x4b571c, 0x4bd12e,
+ 0x4c4b40, 0x4cc552, 0x4d3f64, 0x4db976,
+ 0x4e3388, 0x4ead9a, 0x4f27ac, 0x4fa1be,
+ 0x501bd0, 0x5095e2, 0x510ff4, 0x518a06,
+ 0x520418, 0x527e2a, 0x52f83c, 0x53724e,
+ 0x53ec60, 0x546672, 0x54e084, 0x555a96,
+ 0x55d4a8, 0x564eba, 0x56c8cc, 0x5742de,
+ 0x57bcf0, 0x583702, 0x58b114, 0x592b26,
+ 0x59a538, 0x5a1f4a, 0x5a995c, 0x5b136e,
+ 0x5b8d80, 0x5c0792, 0x5c81a4, 0x5cfbb6,
+ 0x5d75c8, 0x5defda, 0x5e69ec, 0x5ee3fe,
+ 0x5f5e10, 0x5fd822, 0x605234, 0x60cc46,
+ 0x614658, 0x61c06a, 0x623a7c, 0x62b48e,
+ 0x632ea0, 0x63a8b2, 0x6422c4, 0x649cd6,
+ 0x6516e8, 0x6590fa, 0x660b0c, 0x66851e,
+ 0x66ff30, 0x677942, 0x67f354, 0x686d66,
+ 0x68e778, 0x69618a, 0x69db9c, 0x6a55ae,
+ 0x6acfc0, 0x6b49d2, 0x6bc3e4, 0x6c3df6,
+ 0x6cb808, 0x6d321a, 0x6dac2c, 0x6e263e,
+ 0x6ea050, 0x6f1a62, 0x6f9474, 0x700e86,
+ 0x708898, 0x7102aa, 0x717cbc, 0x71f6ce,
+ 0x7270e0, 0x72eaf2, 0x736504, 0x73df16,
+ 0x745928, 0x74d33a, 0x754d4c, 0x75c75e,
+ 0x764170, 0x76bb82, 0x773594, 0x77afa6,
+ 0x7829b8, 0x78a3ca, 0x791ddc, 0x7997ee
+};
+
+long tstousmid[256] = {
+ 0x0000, 0x007a, 0x00f4, 0x016e, 0x01e8, 0x0262, 0x02dc, 0x0356,
+ 0x03d1, 0x044b, 0x04c5, 0x053f, 0x05b9, 0x0633, 0x06ad, 0x0727,
+ 0x07a1, 0x081b, 0x0895, 0x090f, 0x0989, 0x0a03, 0x0a7e, 0x0af8,
+ 0x0b72, 0x0bec, 0x0c66, 0x0ce0, 0x0d5a, 0x0dd4, 0x0e4e, 0x0ec8,
+ 0x0f42, 0x0fbc, 0x1036, 0x10b0, 0x112b, 0x11a5, 0x121f, 0x1299,
+ 0x1313, 0x138d, 0x1407, 0x1481, 0x14fb, 0x1575, 0x15ef, 0x1669,
+ 0x16e3, 0x175d, 0x17d8, 0x1852, 0x18cc, 0x1946, 0x19c0, 0x1a3a,
+ 0x1ab4, 0x1b2e, 0x1ba8, 0x1c22, 0x1c9c, 0x1d16, 0x1d90, 0x1e0a,
+ 0x1e84, 0x1eff, 0x1f79, 0x1ff3, 0x206d, 0x20e7, 0x2161, 0x21db,
+ 0x2255, 0x22cf, 0x2349, 0x23c3, 0x243d, 0x24b7, 0x2531, 0x25ac,
+ 0x2626, 0x26a0, 0x271a, 0x2794, 0x280e, 0x2888, 0x2902, 0x297c,
+ 0x29f6, 0x2a70, 0x2aea, 0x2b64, 0x2bde, 0x2c59, 0x2cd3, 0x2d4d,
+ 0x2dc7, 0x2e41, 0x2ebb, 0x2f35, 0x2faf, 0x3029, 0x30a3, 0x311d,
+ 0x3197, 0x3211, 0x328b, 0x3306, 0x3380, 0x33fa, 0x3474, 0x34ee,
+ 0x3568, 0x35e2, 0x365c, 0x36d6, 0x3750, 0x37ca, 0x3844, 0x38be,
+ 0x3938, 0x39b3, 0x3a2d, 0x3aa7, 0x3b21, 0x3b9b, 0x3c15, 0x3c8f,
+ 0x3d09, 0x3d83, 0x3dfd, 0x3e77, 0x3ef1, 0x3f6b, 0x3fe5, 0x405f,
+ 0x40da, 0x4154, 0x41ce, 0x4248, 0x42c2, 0x433c, 0x43b6, 0x4430,
+ 0x44aa, 0x4524, 0x459e, 0x4618, 0x4692, 0x470c, 0x4787, 0x4801,
+ 0x487b, 0x48f5, 0x496f, 0x49e9, 0x4a63, 0x4add, 0x4b57, 0x4bd1,
+ 0x4c4b, 0x4cc5, 0x4d3f, 0x4db9, 0x4e34, 0x4eae, 0x4f28, 0x4fa2,
+ 0x501c, 0x5096, 0x5110, 0x518a, 0x5204, 0x527e, 0x52f8, 0x5372,
+ 0x53ec, 0x5466, 0x54e1, 0x555b, 0x55d5, 0x564f, 0x56c9, 0x5743,
+ 0x57bd, 0x5837, 0x58b1, 0x592b, 0x59a5, 0x5a1f, 0x5a99, 0x5b13,
+ 0x5b8d, 0x5c08, 0x5c82, 0x5cfc, 0x5d76, 0x5df0, 0x5e6a, 0x5ee4,
+ 0x5f5e, 0x5fd8, 0x6052, 0x60cc, 0x6146, 0x61c0, 0x623a, 0x62b5,
+ 0x632f, 0x63a9, 0x6423, 0x649d, 0x6517, 0x6591, 0x660b, 0x6685,
+ 0x66ff, 0x6779, 0x67f3, 0x686d, 0x68e7, 0x6962, 0x69dc, 0x6a56,
+ 0x6ad0, 0x6b4a, 0x6bc4, 0x6c3e, 0x6cb8, 0x6d32, 0x6dac, 0x6e26,
+ 0x6ea0, 0x6f1a, 0x6f94, 0x700f, 0x7089, 0x7103, 0x717d, 0x71f7,
+ 0x7271, 0x72eb, 0x7365, 0x73df, 0x7459, 0x74d3, 0x754d, 0x75c7,
+ 0x7641, 0x76bc, 0x7736, 0x77b0, 0x782a, 0x78a4, 0x791e, 0x7998
+};
+
+long tstouslo[128] = {
+ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
+ 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
+ 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e,
+ 0x1f, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25,
+ 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d,
+ 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33, 0x33, 0x34,
+ 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c,
+ 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44,
+ 0x45, 0x46, 0x47, 0x48, 0x48, 0x49, 0x4a, 0x4b,
+ 0x4c, 0x4d, 0x4e, 0x4f, 0x50, 0x51, 0x52, 0x53,
+ 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5a, 0x5b,
+ 0x5c, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62,
+ 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6a,
+ 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x71,
+ 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79
+};
diff --git a/usr.sbin/xntpd/lib/tvtoa.c b/usr.sbin/xntpd/lib/tvtoa.c
new file mode 100644
index 0000000..dd9ee7e
--- /dev/null
+++ b/usr.sbin/xntpd/lib/tvtoa.c
@@ -0,0 +1,33 @@
+/*
+ * tvtoa - return an asciized representation of a struct timeval
+ */
+#include <stdio.h>
+#include <sys/time.h>
+
+#include "lib_strbuf.h"
+#include "ntp_stdlib.h"
+
+char *
+tvtoa(tv)
+ struct timeval *tv;
+{
+ register char *buf;
+ register u_long sec;
+ register u_long usec;
+ register int isneg;
+
+ if (tv->tv_sec < 0 || tv->tv_usec < 0) {
+ sec = -tv->tv_sec;
+ usec = -tv->tv_usec;
+ isneg = 1;
+ } else {
+ sec = tv->tv_sec;
+ usec = tv->tv_usec;
+ isneg = 0;
+ }
+
+ LIB_GETBUF(buf);
+
+ (void) sprintf(buf, "%s%lu.%06lu", (isneg?"-":""), sec, usec);
+ return buf;
+}
diff --git a/usr.sbin/xntpd/lib/tvtots.c b/usr.sbin/xntpd/lib/tvtots.c
new file mode 100644
index 0000000..0bd2b69
--- /dev/null
+++ b/usr.sbin/xntpd/lib/tvtots.c
@@ -0,0 +1,159 @@
+/*
+ * tvtots - tables for converting from Unix struct timeval's to
+ * NTP time stamp format.
+ */
+#include <sys/types.h>
+
+#include "ntp_types.h"
+
+/*
+ * Tables to calculate time stamp fractions from usecs. The entries
+ * in these tables are offset into using each of the two low order
+ * bytes plus the next 4 bits in a usec value (from a struct timeval).
+ * These are summed to produce the time stamp fraction.
+ *
+ * Note that these tables are rounded (not truncated) to the nearest
+ * low order bit in the fraction. The timestamp computed should be
+ * +- 1.5 low order bits.
+ */
+
+u_long ustotslo[256] = {
+ 0x00000000, 0x000010c7, 0x0000218e, 0x00003255,
+ 0x0000431c, 0x000053e3, 0x000064aa, 0x00007571,
+ 0x00008638, 0x000096ff, 0x0000a7c6, 0x0000b88d,
+ 0x0000c954, 0x0000da1b, 0x0000eae2, 0x0000fba9,
+ 0x00010c6f, 0x00011d36, 0x00012dfd, 0x00013ec4,
+ 0x00014f8b, 0x00016052, 0x00017119, 0x000181e0,
+ 0x000192a7, 0x0001a36e, 0x0001b435, 0x0001c4fc,
+ 0x0001d5c3, 0x0001e68a, 0x0001f751, 0x00020818,
+ 0x000218df, 0x000229a6, 0x00023a6d, 0x00024b34,
+ 0x00025bfb, 0x00026cc2, 0x00027d89, 0x00028e50,
+ 0x00029f17, 0x0002afde, 0x0002c0a5, 0x0002d16c,
+ 0x0002e233, 0x0002f2fa, 0x000303c0, 0x00031487,
+ 0x0003254e, 0x00033615, 0x000346dc, 0x000357a3,
+ 0x0003686a, 0x00037931, 0x000389f8, 0x00039abf,
+ 0x0003ab86, 0x0003bc4d, 0x0003cd14, 0x0003dddb,
+ 0x0003eea2, 0x0003ff69, 0x00041030, 0x000420f7,
+ 0x000431be, 0x00044285, 0x0004534c, 0x00046413,
+ 0x000474da, 0x000485a1, 0x00049668, 0x0004a72f,
+ 0x0004b7f6, 0x0004c8bd, 0x0004d984, 0x0004ea4b,
+ 0x0004fb12, 0x00050bd8, 0x00051c9f, 0x00052d66,
+ 0x00053e2d, 0x00054ef4, 0x00055fbb, 0x00057082,
+ 0x00058149, 0x00059210, 0x0005a2d7, 0x0005b39e,
+ 0x0005c465, 0x0005d52c, 0x0005e5f3, 0x0005f6ba,
+ 0x00060781, 0x00061848, 0x0006290f, 0x000639d6,
+ 0x00064a9d, 0x00065b64, 0x00066c2b, 0x00067cf2,
+ 0x00068db9, 0x00069e80, 0x0006af47, 0x0006c00e,
+ 0x0006d0d5, 0x0006e19c, 0x0006f263, 0x00070329,
+ 0x000713f0, 0x000724b7, 0x0007357e, 0x00074645,
+ 0x0007570c, 0x000767d3, 0x0007789a, 0x00078961,
+ 0x00079a28, 0x0007aaef, 0x0007bbb6, 0x0007cc7d,
+ 0x0007dd44, 0x0007ee0b, 0x0007fed2, 0x00080f99,
+ 0x00082060, 0x00083127, 0x000841ee, 0x000852b5,
+ 0x0008637c, 0x00087443, 0x0008850a, 0x000895d1,
+ 0x0008a698, 0x0008b75f, 0x0008c826, 0x0008d8ed,
+ 0x0008e9b4, 0x0008fa7b, 0x00090b41, 0x00091c08,
+ 0x00092ccf, 0x00093d96, 0x00094e5d, 0x00095f24,
+ 0x00096feb, 0x000980b2, 0x00099179, 0x0009a240,
+ 0x0009b307, 0x0009c3ce, 0x0009d495, 0x0009e55c,
+ 0x0009f623, 0x000a06ea, 0x000a17b1, 0x000a2878,
+ 0x000a393f, 0x000a4a06, 0x000a5acd, 0x000a6b94,
+ 0x000a7c5b, 0x000a8d22, 0x000a9de9, 0x000aaeb0,
+ 0x000abf77, 0x000ad03e, 0x000ae105, 0x000af1cc,
+ 0x000b0292, 0x000b1359, 0x000b2420, 0x000b34e7,
+ 0x000b45ae, 0x000b5675, 0x000b673c, 0x000b7803,
+ 0x000b88ca, 0x000b9991, 0x000baa58, 0x000bbb1f,
+ 0x000bcbe6, 0x000bdcad, 0x000bed74, 0x000bfe3b,
+ 0x000c0f02, 0x000c1fc9, 0x000c3090, 0x000c4157,
+ 0x000c521e, 0x000c62e5, 0x000c73ac, 0x000c8473,
+ 0x000c953a, 0x000ca601, 0x000cb6c8, 0x000cc78f,
+ 0x000cd856, 0x000ce91d, 0x000cf9e4, 0x000d0aaa,
+ 0x000d1b71, 0x000d2c38, 0x000d3cff, 0x000d4dc6,
+ 0x000d5e8d, 0x000d6f54, 0x000d801b, 0x000d90e2,
+ 0x000da1a9, 0x000db270, 0x000dc337, 0x000dd3fe,
+ 0x000de4c5, 0x000df58c, 0x000e0653, 0x000e171a,
+ 0x000e27e1, 0x000e38a8, 0x000e496f, 0x000e5a36,
+ 0x000e6afd, 0x000e7bc4, 0x000e8c8b, 0x000e9d52,
+ 0x000eae19, 0x000ebee0, 0x000ecfa7, 0x000ee06e,
+ 0x000ef135, 0x000f01fb, 0x000f12c2, 0x000f2389,
+ 0x000f3450, 0x000f4517, 0x000f55de, 0x000f66a5,
+ 0x000f776c, 0x000f8833, 0x000f98fa, 0x000fa9c1,
+ 0x000fba88, 0x000fcb4f, 0x000fdc16, 0x000fecdd,
+ 0x000ffda4, 0x00100e6b, 0x00101f32, 0x00102ff9,
+ 0x001040c0, 0x00105187, 0x0010624e, 0x00107315,
+ 0x001083dc, 0x001094a3, 0x0010a56a, 0x0010b631,
+};
+
+u_long ustotsmid[256] = {
+ 0x00000000, 0x0010c6f8, 0x00218def, 0x003254e7,
+ 0x00431bde, 0x0053e2d6, 0x0064a9ce, 0x007570c5,
+ 0x008637bd, 0x0096feb4, 0x00a7c5ac, 0x00b88ca4,
+ 0x00c9539b, 0x00da1a93, 0x00eae18a, 0x00fba882,
+ 0x010c6f7a, 0x011d3671, 0x012dfd69, 0x013ec460,
+ 0x014f8b58, 0x01605250, 0x01711947, 0x0181e03f,
+ 0x0192a736, 0x01a36e2e, 0x01b43526, 0x01c4fc1d,
+ 0x01d5c315, 0x01e68a0c, 0x01f75104, 0x020817fc,
+ 0x0218def3, 0x0229a5eb, 0x023a6ce3, 0x024b33da,
+ 0x025bfad2, 0x026cc1c9, 0x027d88c1, 0x028e4fb9,
+ 0x029f16b0, 0x02afdda8, 0x02c0a49f, 0x02d16b97,
+ 0x02e2328f, 0x02f2f986, 0x0303c07e, 0x03148775,
+ 0x03254e6d, 0x03361565, 0x0346dc5c, 0x0357a354,
+ 0x03686a4b, 0x03793143, 0x0389f83b, 0x039abf32,
+ 0x03ab862a, 0x03bc4d21, 0x03cd1419, 0x03dddb11,
+ 0x03eea208, 0x03ff6900, 0x04102ff7, 0x0420f6ef,
+ 0x0431bde7, 0x044284de, 0x04534bd6, 0x046412cd,
+ 0x0474d9c5, 0x0485a0bd, 0x049667b4, 0x04a72eac,
+ 0x04b7f5a3, 0x04c8bc9b, 0x04d98393, 0x04ea4a8a,
+ 0x04fb1182, 0x050bd879, 0x051c9f71, 0x052d6669,
+ 0x053e2d60, 0x054ef458, 0x055fbb4f, 0x05708247,
+ 0x0581493f, 0x05921036, 0x05a2d72e, 0x05b39e25,
+ 0x05c4651d, 0x05d52c15, 0x05e5f30c, 0x05f6ba04,
+ 0x060780fb, 0x061847f3, 0x06290eeb, 0x0639d5e2,
+ 0x064a9cda, 0x065b63d2, 0x066c2ac9, 0x067cf1c1,
+ 0x068db8b8, 0x069e7fb0, 0x06af46a8, 0x06c00d9f,
+ 0x06d0d497, 0x06e19b8e, 0x06f26286, 0x0703297e,
+ 0x0713f075, 0x0724b76d, 0x07357e64, 0x0746455c,
+ 0x07570c54, 0x0767d34b, 0x07789a43, 0x0789613a,
+ 0x079a2832, 0x07aaef2a, 0x07bbb621, 0x07cc7d19,
+ 0x07dd4410, 0x07ee0b08, 0x07fed200, 0x080f98f7,
+ 0x08205fef, 0x083126e6, 0x0841edde, 0x0852b4d6,
+ 0x08637bcd, 0x087442c5, 0x088509bc, 0x0895d0b4,
+ 0x08a697ac, 0x08b75ea3, 0x08c8259b, 0x08d8ec92,
+ 0x08e9b38a, 0x08fa7a82, 0x090b4179, 0x091c0871,
+ 0x092ccf68, 0x093d9660, 0x094e5d58, 0x095f244f,
+ 0x096feb47, 0x0980b23e, 0x09917936, 0x09a2402e,
+ 0x09b30725, 0x09c3ce1d, 0x09d49514, 0x09e55c0c,
+ 0x09f62304, 0x0a06e9fb, 0x0a17b0f3, 0x0a2877ea,
+ 0x0a393ee2, 0x0a4a05da, 0x0a5accd1, 0x0a6b93c9,
+ 0x0a7c5ac1, 0x0a8d21b8, 0x0a9de8b0, 0x0aaeafa7,
+ 0x0abf769f, 0x0ad03d97, 0x0ae1048e, 0x0af1cb86,
+ 0x0b02927d, 0x0b135975, 0x0b24206d, 0x0b34e764,
+ 0x0b45ae5c, 0x0b567553, 0x0b673c4b, 0x0b780343,
+ 0x0b88ca3a, 0x0b999132, 0x0baa5829, 0x0bbb1f21,
+ 0x0bcbe619, 0x0bdcad10, 0x0bed7408, 0x0bfe3aff,
+ 0x0c0f01f7, 0x0c1fc8ef, 0x0c308fe6, 0x0c4156de,
+ 0x0c521dd5, 0x0c62e4cd, 0x0c73abc5, 0x0c8472bc,
+ 0x0c9539b4, 0x0ca600ab, 0x0cb6c7a3, 0x0cc78e9b,
+ 0x0cd85592, 0x0ce91c8a, 0x0cf9e381, 0x0d0aaa79,
+ 0x0d1b7171, 0x0d2c3868, 0x0d3cff60, 0x0d4dc657,
+ 0x0d5e8d4f, 0x0d6f5447, 0x0d801b3e, 0x0d90e236,
+ 0x0da1a92d, 0x0db27025, 0x0dc3371d, 0x0dd3fe14,
+ 0x0de4c50c, 0x0df58c03, 0x0e0652fb, 0x0e1719f3,
+ 0x0e27e0ea, 0x0e38a7e2, 0x0e496ed9, 0x0e5a35d1,
+ 0x0e6afcc9, 0x0e7bc3c0, 0x0e8c8ab8, 0x0e9d51b0,
+ 0x0eae18a7, 0x0ebedf9f, 0x0ecfa696, 0x0ee06d8e,
+ 0x0ef13486, 0x0f01fb7d, 0x0f12c275, 0x0f23896c,
+ 0x0f345064, 0x0f45175c, 0x0f55de53, 0x0f66a54b,
+ 0x0f776c42, 0x0f88333a, 0x0f98fa32, 0x0fa9c129,
+ 0x0fba8821, 0x0fcb4f18, 0x0fdc1610, 0x0fecdd08,
+ 0x0ffda3ff, 0x100e6af7, 0x101f31ee, 0x102ff8e6,
+ 0x1040bfde, 0x105186d5, 0x10624dcd, 0x107314c4,
+ 0x1083dbbc, 0x1094a2b4, 0x10a569ab, 0x10b630a3,
+};
+
+u_long ustotshi[16] = {
+ 0x00000000, 0x10c6f79a, 0x218def35, 0x3254e6cf,
+ 0x431bde6a, 0x53e2d604, 0x64a9cd9f, 0x7570c539,
+ 0x8637bcd3, 0x96feb46e, 0xa7c5ac08, 0xb88ca3a3,
+ 0xc9539b3d, 0xda1a92d7, 0xeae18a72, 0xfba8820c,
+};
diff --git a/usr.sbin/xntpd/lib/uglydate.c b/usr.sbin/xntpd/lib/uglydate.c
new file mode 100644
index 0000000..25a8e56
--- /dev/null
+++ b/usr.sbin/xntpd/lib/uglydate.c
@@ -0,0 +1,49 @@
+/*
+ * uglydate - convert a time stamp to something barely readable
+ * The string returned is 37 characters long.
+ */
+#include <stdio.h>
+
+#include "ntp_fp.h"
+#include "ntp_unixtime.h"
+#include "lib_strbuf.h"
+#include "ntp_stdlib.h"
+#ifdef NTP_POSIX_SOURCE
+#include <time.h>
+#endif
+
+char *
+uglydate(ts)
+ l_fp *ts;
+{
+ char *bp;
+ char *timep;
+ struct tm *tm;
+ time_t sec;
+ long msec;
+ int year;
+
+ timep = ulfptoa(ts, 6); /* returns max 17 characters */
+ LIB_GETBUF(bp);
+ sec = ts->l_ui - JAN_1970;
+ msec = ts->l_uf / 4294967; /* fract / (2**32/1000) */
+ tm = gmtime(&sec);
+ if (ts->l_ui == 0) {
+ /*
+ * Probably not a real good thing to do. Oh, well.
+ */
+ year = 0;
+ tm->tm_yday = 0;
+ tm->tm_hour = 0;
+ tm->tm_min = 0;
+ tm->tm_sec = 0;
+ } else {
+ year = tm->tm_year;
+ while (year >= 100)
+ year -= 100;
+ }
+ (void) sprintf(bp, "%17s %02d:%03d:%02d:%02d:%02d.%03ld",
+ timep, year, tm->tm_yday, tm->tm_hour, tm->tm_min,
+ tm->tm_sec, msec);
+ return bp;
+}
diff --git a/usr.sbin/xntpd/lib/uinttoa.c b/usr.sbin/xntpd/lib/uinttoa.c
new file mode 100644
index 0000000..7c406dd
--- /dev/null
+++ b/usr.sbin/xntpd/lib/uinttoa.c
@@ -0,0 +1,19 @@
+/*
+ * uinttoa - return an asciized unsigned integer
+ */
+#include <stdio.h>
+
+#include "lib_strbuf.h"
+#include "ntp_stdlib.h"
+
+char *
+uinttoa(uval)
+ u_long uval;
+{
+ register char *buf;
+
+ LIB_GETBUF(buf);
+
+ (void) sprintf(buf, "%lu", (u_long)uval);
+ return buf;
+}
diff --git a/usr.sbin/xntpd/lib/utvtoa.c b/usr.sbin/xntpd/lib/utvtoa.c
new file mode 100644
index 0000000..5a192e1
--- /dev/null
+++ b/usr.sbin/xntpd/lib/utvtoa.c
@@ -0,0 +1,21 @@
+/*
+ * utvtoa - return an asciized representation of an unsigned struct timeval
+ */
+#include <stdio.h>
+#include <sys/time.h>
+
+#include "lib_strbuf.h"
+#include "ntp_stdlib.h"
+
+char *
+utvtoa(tv)
+ struct timeval *tv;
+{
+ register char *buf;
+
+ LIB_GETBUF(buf);
+
+ (void) sprintf(buf, "%lu.%06lu", (u_long)tv->tv_sec,
+ (u_long)tv->tv_usec);
+ return buf;
+}
diff --git a/usr.sbin/xntpd/ntpdate/Makefile b/usr.sbin/xntpd/ntpdate/Makefile
new file mode 100644
index 0000000..ac333ca
--- /dev/null
+++ b/usr.sbin/xntpd/ntpdate/Makefile
@@ -0,0 +1,28 @@
+#
+# $Id$
+#
+
+CFLAGS+= -I${.CURDIR}/../include
+
+.if exists(${.OBJDIR}/../lib)
+LDADD+= -L${.OBJDIR}/../lib
+DPADD+= ${.OBJDIR}/../lib/libntp.a
+.else
+LDADD+= -L${.CURDIR}/../lib
+DPADD+= ${.CURDIR}/../lib/libntp.a
+.endif
+
+LDADD+= -lntp
+
+PROG= ntpdate
+MAN8= ${.CURDIR}/../doc/ntpdate.8
+CLEANFILES+= .version version.c
+
+SRCS= ntpdate.c version.c
+
+beforedepend: version.c
+
+version.c: ${.CURDIR}/../VERSION
+ sh -e ${.CURDIR}/../scripts/mkversion ntpdate
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/xntpd/ntpdate/README b/usr.sbin/xntpd/ntpdate/README
new file mode 100644
index 0000000..fd2dbe2
--- /dev/null
+++ b/usr.sbin/xntpd/ntpdate/README
@@ -0,0 +1,7 @@
+README file for directory ./ntpdate of the NTP Version 3 distribution
+
+This directory contains the sources for the ntpdate utility program. See
+the README and RELNOTES files in the parent directory for directions on
+how to make and install this program. The current version number of this
+program is in the version.c file.
+
diff --git a/usr.sbin/xntpd/ntpdate/ntpdate.c b/usr.sbin/xntpd/ntpdate/ntpdate.c
new file mode 100644
index 0000000..bb5ede5
--- /dev/null
+++ b/usr.sbin/xntpd/ntpdate/ntpdate.c
@@ -0,0 +1,1586 @@
+/*
+ * ntpdate - set the time of day by polling one or more NTP servers
+ */
+#include <stdio.h>
+#include <signal.h>
+#include <ctype.h>
+#include <errno.h>
+#include <netdb.h>
+#include <sys/types.h>
+#include <sys/signal.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+
+#ifdef __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+#if defined(SYS_HPUX)
+#include <utmp.h>
+#endif
+
+#ifdef SYS_LINUX
+#include <sys/timex.h>
+#endif
+
+#ifndef SYSLOG_FILE
+#define SYSLOG_FILE /* we want to go through the syslog/printf/file code */
+#endif
+
+#include "ntp_select.h"
+#include "ntp_fp.h"
+#include "ntp.h"
+#include "ntp_io.h"
+#include "ntp_unixtime.h"
+#include "ntpdate.h"
+#include "ntp_string.h"
+#include "ntp_stdlib.h"
+#include "ntp_syslog.h"
+
+/*
+ * Scheduling priority we run at
+ */
+#define NTPDATE_PRIO (-12)
+
+/*
+ * Compatibility stuff for Version 2
+ */
+#define NTP_MAXSKW 0x28f /* 0.01 sec in fp format */
+#define NTP_MINDIST 0x51f /* 0.02 sec in fp format */
+#define PEER_MAXDISP (64*FP_SECOND) /* maximum dispersion (fp 64) */
+#define NTP_INFIN 15 /* max stratum, infinity a la Bellman-Ford */
+#define NTP_MAXWGT (8*FP_SECOND) /* maximum select weight 8 seconds */
+#define NTP_MAXLIST 5 /* maximum select list size */
+#define PEER_SHIFT 8 /* 8 suitable for crystal time base */
+
+/*
+ * Debugging flag
+ */
+int debug = 0;
+
+/*
+ * File descriptor masks etc. for call to select
+ */
+int fd;
+fd_set fdmask;
+
+/*
+ * Initializing flag. All async routines watch this and only do their
+ * thing when it is clear.
+ */
+int initializing = 1;
+
+/*
+ * Alarm flag. Set when an alarm occurs
+ */
+int alarm_flag = 0;
+
+/*
+ * Simple query flag.
+ */
+int simple_query = 0;
+
+/*
+ * Program name.
+ */
+char *progname;
+
+/*
+ * Systemwide parameters and flags
+ */
+int sys_samples = DEFSAMPLES; /* number of samples/server */
+u_long sys_timeout = DEFTIMEOUT; /* timeout time, in TIMER_HZ units */
+struct server **sys_servers; /* the server list */
+int sys_numservers = 0; /* number of servers to poll */
+int sys_maxservers = 0; /* max number of servers to deal with */
+int sys_authenticate = 0; /* true when authenticating */
+u_long sys_authkey = 0; /* set to authentication key in use */
+u_long sys_authdelay = 0; /* authentication delay */
+int sys_version = NTP_VERSION; /* version to poll with */
+
+/*
+ * The current internal time
+ */
+u_long current_time = 0;
+
+/*
+ * Counter for keeping track of completed servers
+ */
+int complete_servers = 0;
+
+/*
+ * File of encryption keys
+ */
+#ifndef KEYFILE
+#define KEYFILE "/etc/ntp.keys"
+#endif /* KEYFILE */
+
+char *key_file = KEYFILE;
+
+/*
+ * Miscellaneous flags
+ */
+extern int syslogit;
+int verbose = 0;
+int always_step = 0;
+
+extern int errno;
+
+static void transmit P((struct server *));
+static void receive P((struct recvbuf *));
+static void server_data P((struct server *, s_fp, l_fp *, u_fp));
+static void clock_filter P((struct server *));
+static struct server *clock_select P((void));
+static int clock_adjust P((void));
+static void addserver P((char *));
+static struct server *findserver P((struct sockaddr_in *));
+static void timer P((void));
+static void init_alarm P((void));
+static RETSIGTYPE alarming P((int));
+static void init_io P((void));
+static struct recvbuf *getrecvbufs P((void));
+static void freerecvbuf P((struct recvbuf *));
+static void sendpkt P((struct sockaddr_in *, struct pkt *, int));
+static void input_handler P((void));
+
+static int l_adj_systime P((l_fp *));
+static int l_step_systime P((l_fp *));
+
+static int getnetnum P((char *, u_long *));
+static void printserver P((struct server *, FILE *));
+
+/*
+ * Main program. Initialize us and loop waiting for I/O and/or
+ * timer expiries.
+ */
+void
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ int was_alarmed;
+ struct recvbuf *rbuflist;
+ struct recvbuf *rbuf;
+ l_fp tmp;
+ int errflg;
+ int c;
+ extern char *ntp_optarg;
+ extern int ntp_optind;
+ extern char *Version;
+
+ errflg = 0;
+ progname = argv[0];
+ syslogit = 0;
+
+ /*
+ * Decode argument list
+ */
+ while ((c = ntp_getopt(argc, argv, "a:bde:k:o:p:qst:v")) != EOF)
+ switch (c) {
+ case 'a':
+ c = atoi(ntp_optarg);
+ sys_authenticate = 1;
+ sys_authkey = c;
+ break;
+ case 'b':
+ always_step++;
+ break;
+ case 'd':
+ ++debug;
+ break;
+ case 'e':
+ if (!atolfp(ntp_optarg, &tmp)
+ || tmp.l_ui != 0) {
+ (void) fprintf(stderr,
+ "%s: encryption delay %s is unlikely\n",
+ progname, ntp_optarg);
+ errflg++;
+ } else {
+ sys_authdelay = tmp.l_uf;
+ }
+ break;
+ case 'k':
+ key_file = ntp_optarg;
+ break;
+ case 'o':
+ sys_version = atoi(ntp_optarg);
+ break;
+ case 'p':
+ c = atoi(ntp_optarg);
+ if (c <= 0 || c > NTP_SHIFT) {
+ (void) fprintf(stderr,
+ "%s: number of samples (%d) is invalid\n",
+ progname, c);
+ errflg++;
+ } else {
+ sys_samples = c;
+ }
+ break;
+ case 'q':
+ simple_query = 1;
+ break;
+ case 's':
+ syslogit = 1;
+ break;
+ case 't':
+ if (!atolfp(ntp_optarg, &tmp)) {
+ (void) fprintf(stderr,
+ "%s: timeout %s is undecodeable\n",
+ progname, ntp_optarg);
+ errflg++;
+ } else {
+ sys_timeout = ((LFPTOFP(&tmp) * TIMER_HZ)
+ + 0x8000) >> 16;
+ if (sys_timeout == 0)
+ sys_timeout = 1;
+ }
+ break;
+ case 'v':
+ verbose = 1;
+ break;
+ case '?':
+ ++errflg;
+ break;
+ default:
+ break;
+ }
+
+ sys_maxservers = argc - ntp_optind;
+ if (errflg || sys_maxservers == 0) {
+ (void) fprintf(stderr,
+"usage: %s [-bqs] [-a key#] [-k file] [-p samples] [-t timeo] server ...\n",
+ progname);
+ exit(2);
+ }
+
+ sys_servers = (struct server **)
+ emalloc(sys_maxservers * sizeof(struct server *));
+
+ if (debug || simple_query) {
+#ifdef NTP_POSIX_SOURCE
+ static char buf[BUFSIZ];
+ setvbuf(stdout, buf, _IOLBF, BUFSIZ);
+#else
+ setlinebuf(stdout);
+#endif
+ }
+
+ /*
+ * Logging. Open the syslog if we have to
+ */
+ if (syslogit) {
+#ifndef LOG_DAEMON
+ openlog("ntpdate", LOG_PID);
+#else
+
+#ifndef LOG_NTP
+#define LOG_NTP LOG_DAEMON
+#endif
+ openlog("ntpdate", LOG_PID | LOG_NDELAY, LOG_NTP);
+ if (debug)
+ setlogmask(LOG_UPTO(LOG_DEBUG));
+ else
+ setlogmask(LOG_UPTO(LOG_INFO));
+#endif /* LOG_DAEMON */
+ }
+
+ if (debug || verbose)
+ syslog(LOG_NOTICE, "%s", Version);
+
+ /*
+ * Add servers we are going to be polling
+ */
+ for ( ; ntp_optind < argc; ntp_optind++)
+ addserver(argv[ntp_optind]);
+
+ if (sys_numservers == 0) {
+ syslog(LOG_ERR, "no servers can be used, exiting");
+ exit(1);
+ }
+
+ /*
+ * Initialize the time of day routines and the I/O subsystem
+ */
+ if (sys_authenticate) {
+ init_auth();
+ if (!authreadkeys(key_file)) {
+ syslog(LOG_ERR, "no key file, exitting");
+ exit(1);
+ }
+ if (!authhavekey(sys_authkey)) {
+ char buf[10];
+
+ (void) sprintf(buf, "%lu", (unsigned long)sys_authkey);
+ syslog(LOG_ERR, "authentication key %s unknown", buf);
+ exit(1);
+ }
+ }
+ init_io();
+ init_alarm();
+
+ /*
+ * Set the priority.
+ */
+#if defined(HAVE_ATT_NICE)
+ nice (NTPDATE_PRIO);
+#endif
+#if defined(HAVE_BSD_NICE)
+ (void) setpriority(PRIO_PROCESS, 0, NTPDATE_PRIO);
+#endif
+
+ initializing = 0;
+
+ was_alarmed = 0;
+ rbuflist = (struct recvbuf *)0;
+ while (complete_servers < sys_numservers) {
+ fd_set rdfdes;
+ int nfound;
+
+ if (alarm_flag) { /* alarmed? */
+ was_alarmed = 1;
+ alarm_flag = 0;
+ }
+ rbuflist = getrecvbufs(); /* get received buffers */
+
+ if (!was_alarmed && rbuflist == (struct recvbuf *)0) {
+ /*
+ * Nothing to do. Wait for something.
+ */
+ rdfdes = fdmask;
+ nfound = select(fd+1, &rdfdes, (fd_set *)0,
+ (fd_set *)0, (struct timeval *)0);
+ if (nfound > 0)
+ input_handler();
+
+ else if (nfound == -1 && errno != EINTR) {
+ syslog(LOG_ERR, "select() error: %m");
+ }
+ if (alarm_flag) { /* alarmed? */
+ was_alarmed = 1;
+ alarm_flag = 0;
+ }
+ rbuflist = getrecvbufs(); /* get received buffers */
+
+ }
+
+ /*
+ * Out here, signals are unblocked. Call receive
+ * procedure for each incoming packet.
+ */
+ while (rbuflist != (struct recvbuf *)0) {
+ rbuf = rbuflist;
+ rbuflist = rbuf->next;
+ receive(rbuf);
+ freerecvbuf(rbuf);
+ }
+
+ /*
+ * Call timer to process any timeouts
+ */
+ if (was_alarmed) {
+ timer();
+ was_alarmed = 0;
+ }
+
+ /*
+ * Go around again
+ */
+ }
+
+ /*
+ * When we get here we've completed the polling of all servers.
+ * Adjust the clock, then exit.
+ */
+ exit(clock_adjust());
+}
+
+
+/*
+ * transmit - transmit a packet to the given server, or mark it completed.
+ * This is called by the timeout routine and by the receive
+ * procedure.
+ */
+static void
+transmit(server)
+ register struct server *server;
+{
+ struct pkt xpkt;
+
+ if (debug)
+ printf("transmit(%s)\n", ntoa(&server->srcadr));
+
+ if (server->filter_nextpt < server->xmtcnt) {
+ l_fp ts;
+ /*
+ * Last message to this server timed out. Shift
+ * zeros into the filter.
+ */
+ L_CLR(&ts);
+ server_data(server, 0, &ts, 0);
+ }
+
+ if ((int)server->filter_nextpt >= sys_samples) {
+ /*
+ * Got all the data we need. Mark this guy
+ * completed and return.
+ */
+ server->event_time = 0;
+ complete_servers++;
+ return;
+ }
+
+ /*
+ * If we're here, send another message to the server. Fill in
+ * the packet and let 'er rip.
+ */
+ xpkt.li_vn_mode = PKT_LI_VN_MODE(LEAP_NOTINSYNC,
+ sys_version, MODE_CLIENT);
+ xpkt.stratum = STRATUM_TO_PKT(STRATUM_UNSPEC);
+ xpkt.ppoll = NTP_MINPOLL;
+ xpkt.precision = NTPDATE_PRECISION;
+ xpkt.rootdelay = htonl(NTPDATE_DISTANCE);
+ xpkt.rootdispersion = htonl(NTPDATE_DISP);
+ xpkt.refid = htonl(NTPDATE_REFID);
+ L_CLR(&xpkt.reftime);
+ L_CLR(&xpkt.org);
+ L_CLR(&xpkt.rec);
+
+ /*
+ * Determine whether to authenticate or not. If so,
+ * fill in the extended part of the packet and do it.
+ * If not, just timestamp it and send it away.
+ */
+ if (sys_authenticate) {
+ int len;
+
+ xpkt.keyid = htonl(sys_authkey);
+ auth1crypt(sys_authkey, (U_LONG *)&xpkt, LEN_PKT_NOMAC);
+ get_systime(&server->xmt);
+ L_ADDUF(&server->xmt, sys_authdelay);
+ HTONL_FP(&server->xmt, &xpkt.xmt);
+ len = auth2crypt(sys_authkey, (U_LONG *)&xpkt, LEN_PKT_NOMAC);
+ sendpkt(&(server->srcadr), &xpkt, LEN_PKT_NOMAC + len);
+
+ if (debug > 1)
+ printf("transmit auth to %s\n",
+ ntoa(&(server->srcadr)));
+ } else {
+ get_systime(&(server->xmt));
+ HTONL_FP(&server->xmt, &xpkt.xmt);
+ sendpkt(&(server->srcadr), &xpkt, LEN_PKT_NOMAC);
+
+ if (debug > 1)
+ printf("transmit to %s\n", ntoa(&(server->srcadr)));
+ }
+
+ /*
+ * Update the server timeout and transmit count
+ */
+ server->event_time = current_time + sys_timeout;
+ server->xmtcnt++;
+}
+
+
+/*
+ * receive - receive and process an incoming frame
+ */
+static void
+receive(rbufp)
+ struct recvbuf *rbufp;
+{
+ register struct pkt *rpkt;
+ register struct server *server;
+ register s_fp di;
+ l_fp t10, t23;
+ l_fp org;
+ l_fp rec;
+ l_fp ci;
+ int has_mac;
+ int is_authentic;
+
+ if (debug)
+ printf("receive(%s)\n", ntoa(&rbufp->srcadr));
+ /*
+ * Check to see if the packet basically looks like something
+ * intended for us.
+ */
+ if (rbufp->recv_length == LEN_PKT_NOMAC)
+ has_mac = 0;
+ else if (rbufp->recv_length >= LEN_PKT_NOMAC)
+ has_mac = 1;
+ else {
+ if (debug)
+ printf("receive: packet length %d\n",
+ rbufp->recv_length);
+ return; /* funny length packet */
+ }
+
+ rpkt = &(rbufp->recv_pkt);
+ if (PKT_VERSION(rpkt->li_vn_mode) < NTP_OLDVERSION ||
+ PKT_VERSION(rpkt->li_vn_mode) > NTP_VERSION) {
+ return;
+ }
+
+ if ((PKT_MODE(rpkt->li_vn_mode) != MODE_SERVER
+ && PKT_MODE(rpkt->li_vn_mode) != MODE_PASSIVE)
+ || rpkt->stratum > NTP_MAXSTRATUM) {
+ if (debug)
+ printf("receive: mode %d stratum %d\n",
+ PKT_MODE(rpkt->li_vn_mode), rpkt->stratum);
+ return;
+ }
+
+ /*
+ * So far, so good. See if this is from a server we know.
+ */
+ server = findserver(&(rbufp->srcadr));
+ if (server == NULL) {
+ if (debug)
+ printf("receive: server not found\n");
+ return;
+ }
+
+ /*
+ * Decode the org timestamp and make sure we're getting a response
+ * to our last request.
+ */
+ NTOHL_FP(&rpkt->org, &org);
+ if (!L_ISEQU(&org, &server->xmt)) {
+ if (debug)
+ printf("receive: pkt.org and peer.xmt differ\n");
+ return;
+ }
+
+ /*
+ * Check out the authenticity if we're doing that.
+ */
+ if (!sys_authenticate)
+ is_authentic = 1;
+ else {
+ is_authentic = 0;
+
+ if (debug > 3)
+ printf("receive: rpkt keyid=%ld sys_authkey=%ld decrypt=%ld\n",
+ (long int)ntohl(rpkt->keyid), (long int)sys_authkey,
+ (long int)authdecrypt(sys_authkey, (U_LONG *)rpkt,
+ LEN_PKT_NOMAC));
+
+ if (has_mac && ntohl(rpkt->keyid) == sys_authkey &&
+ authdecrypt(sys_authkey, (U_LONG *)rpkt, LEN_PKT_NOMAC))
+ is_authentic = 1;
+ if (debug)
+ printf("receive: authentication %s\n",
+ is_authentic ? "passed" : "failed");
+ }
+ server->trust <<= 1;
+ if (!is_authentic)
+ server->trust |= 1;
+
+ /*
+ * Looks good. Record info from the packet.
+ */
+ server->leap = PKT_LEAP(rpkt->li_vn_mode);
+ server->stratum = PKT_TO_STRATUM(rpkt->stratum);
+ server->precision = rpkt->precision;
+ server->rootdelay = ntohl(rpkt->rootdelay);
+ server->rootdispersion = ntohl(rpkt->rootdispersion);
+ server->refid = rpkt->refid;
+ NTOHL_FP(&rpkt->reftime, &server->reftime);
+ NTOHL_FP(&rpkt->rec, &rec);
+ NTOHL_FP(&rpkt->xmt, &server->org);
+
+ /*
+ * Make sure the server is at least somewhat sane. If not, try
+ * again.
+ */
+ if (L_ISZERO(&rec) || !L_ISHIS(&server->org, &rec)) {
+ transmit(server);
+ return;
+ }
+
+ /*
+ * Calculate the round trip delay (di) and the clock offset (ci).
+ * We use the equations (reordered from those in the spec):
+ *
+ * d = (t2 - t3) - (t1 - t0)
+ * c = ((t2 - t3) + (t1 - t0)) / 2
+ */
+ t10 = server->org; /* pkt.xmt == t1 */
+ L_SUB(&t10, &rbufp->recv_time); /* recv_time == t0*/
+
+ t23 = rec; /* pkt.rec == t2 */
+ L_SUB(&t23, &org); /* pkt->org == t3 */
+
+ /* now have (t2 - t3) and (t0 - t1). Calculate (ci) and (di) */
+ ci = t10;
+ L_ADD(&ci, &t23);
+ L_RSHIFT(&ci);
+
+ /*
+ * Calculate di in t23 in full precision, then truncate
+ * to an s_fp.
+ */
+ L_SUB(&t23, &t10);
+ di = LFPTOFP(&t23);
+
+ if (debug > 3)
+ printf("offset: %s, delay %s\n", lfptoa(&ci, 6), fptoa(di, 5));
+
+ di += (FP_SECOND >> (-(int)NTPDATE_PRECISION))
+ + (FP_SECOND >> (-(int)server->precision)) + NTP_MAXSKW;
+
+ if (di <= 0) { /* value still too raunchy to use? */
+ L_CLR(&ci);
+ di = 0;
+ } else {
+ di = max(di, NTP_MINDIST);
+ }
+
+ /*
+ * Shift this data in, then transmit again.
+ */
+ server_data(server, (u_fp) di, &ci, 0);
+ transmit(server);
+}
+
+
+/*
+ * server_data - add a sample to the server's filter registers
+ */
+static void
+server_data(server, d, c, e)
+ register struct server *server;
+ s_fp d;
+ l_fp *c;
+ u_fp e;
+{
+ register int i;
+
+ i = server->filter_nextpt;
+ if (i < NTP_SHIFT) {
+ server->filter_delay[i] = d;
+ server->filter_offset[i] = *c;
+ server->filter_soffset[i] = LFPTOFP(c);
+ server->filter_error[i] = e;
+ server->filter_nextpt = i + 1;
+ }
+}
+
+
+/*
+ * clock_filter - determine a server's delay, dispersion and offset
+ */
+static void
+clock_filter(server)
+ register struct server *server;
+{
+ register int i, j;
+ int ord[NTP_SHIFT];
+
+ /*
+ * Sort indices into increasing delay order
+ */
+ for (i = 0; i < sys_samples; i++)
+ ord[i] = i;
+
+ for (i = 0; i < (sys_samples-1); i++) {
+ for (j = i+1; j < sys_samples; j++) {
+ if (server->filter_delay[ord[j]] == 0)
+ continue;
+ if (server->filter_delay[ord[i]] == 0
+ || (server->filter_delay[ord[i]]
+ > server->filter_delay[ord[j]])) {
+ register int tmp;
+
+ tmp = ord[i];
+ ord[i] = ord[j];
+ ord[j] = tmp;
+ }
+ }
+ }
+
+ /*
+ * Now compute the dispersion, and assign values to delay and
+ * offset. If there are no samples in the register, delay and
+ * offset go to zero and dispersion is set to the maximum.
+ */
+ if (server->filter_delay[ord[0]] == 0) {
+ server->delay = 0;
+ L_CLR(&server->offset);
+ server->soffset = 0;
+ server->dispersion = PEER_MAXDISP;
+ } else {
+ register s_fp d;
+
+ server->delay = server->filter_delay[ord[0]];
+ server->offset = server->filter_offset[ord[0]];
+ server->soffset = LFPTOFP(&server->offset);
+ server->dispersion = 0;
+ for (i = 1; i < sys_samples; i++) {
+ if (server->filter_delay[ord[i]] == 0)
+ d = PEER_MAXDISP;
+ else {
+ d = server->filter_soffset[ord[i]]
+ - server->filter_soffset[ord[0]];
+ if (d < 0)
+ d = -d;
+ if (d > PEER_MAXDISP)
+ d = PEER_MAXDISP;
+ }
+ /*
+ * XXX This *knows* PEER_FILTER is 1/2
+ */
+ server->dispersion += (u_fp)(d) >> i;
+ }
+ }
+ /*
+ * We're done
+ */
+}
+
+
+/*
+ * clock_select - select the pick-of-the-litter clock from the samples
+ * we've got.
+ */
+static struct server *
+clock_select()
+{
+ register struct server *server;
+ register int i;
+ register int nlist;
+ register s_fp d;
+ register int j;
+ register int n;
+ s_fp local_threshold;
+ struct server *server_list[NTP_MAXCLOCK];
+ u_fp server_badness[NTP_MAXCLOCK];
+ struct server *sys_server;
+
+ /*
+ * This first chunk of code is supposed to go through all
+ * servers we know about to find the NTP_MAXLIST servers which
+ * are most likely to succeed. We run through the list
+ * doing the sanity checks and trying to insert anyone who
+ * looks okay. We are at all times aware that we should
+ * only keep samples from the top two strata and we only need
+ * NTP_MAXLIST of them.
+ */
+ nlist = 0; /* none yet */
+ for (n = 0; n < sys_numservers; n++) {
+ server = sys_servers[n];
+ if (server->delay == 0)
+ continue; /* no data */
+ if (server->stratum > NTP_INFIN)
+ continue; /* stratum no good */
+ if (server->delay > NTP_MAXWGT) {
+ continue; /* too far away */
+ }
+ if (server->leap == LEAP_NOTINSYNC)
+ continue; /* he's in trouble */
+ if (!L_ISHIS(&server->org, &server->reftime)) {
+ continue; /* very broken host */
+ }
+ if ((server->org.l_ui - server->reftime.l_ui)
+ >= NTP_MAXAGE) {
+ continue; /* too long without sync */
+ }
+ if (server->trust != 0) {
+ continue;
+ }
+
+ /*
+ * This one seems sane. Find where he belongs
+ * on the list.
+ */
+ d = server->dispersion + server->dispersion;
+ for (i = 0; i < nlist; i++)
+ if (server->stratum <= server_list[i]->stratum)
+ break;
+ for ( ; i < nlist; i++) {
+ if (server->stratum < server_list[i]->stratum)
+ break;
+ if (d < server_badness[i])
+ break;
+ }
+
+ /*
+ * If i points past the end of the list, this
+ * guy is a loser, else stick him in.
+ */
+ if (i >= NTP_MAXLIST)
+ continue;
+ for (j = nlist; j > i; j--)
+ if (j < NTP_MAXLIST) {
+ server_list[j] = server_list[j-1];
+ server_badness[j]
+ = server_badness[j-1];
+ }
+
+ server_list[i] = server;
+ server_badness[i] = d;
+ if (nlist < NTP_MAXLIST)
+ nlist++;
+ }
+
+ /*
+ * Got the five-or-less best. Cut the list where the number of
+ * strata exceeds two.
+ */
+ j = 0;
+ for (i = 1; i < nlist; i++)
+ if (server_list[i]->stratum > server_list[i-1]->stratum)
+ if (++j == 2) {
+ nlist = i;
+ break;
+ }
+
+ /*
+ * Whew! What we should have by now is 0 to 5 candidates for
+ * the job of syncing us. If we have none, we're out of luck.
+ * If we have one, he's a winner. If we have more, do falseticker
+ * detection.
+ */
+
+ if (nlist == 0)
+ sys_server = 0;
+ else if (nlist == 1) {
+ sys_server = server_list[0];
+ } else {
+ /*
+ * Re-sort by stratum, bdelay estimate quality and
+ * server.delay.
+ */
+ for (i = 0; i < nlist-1; i++)
+ for (j = i+1; j < nlist; j++) {
+ if (server_list[i]->stratum
+ < server_list[j]->stratum)
+ break; /* already sorted by stratum */
+ if (server_list[i]->delay
+ < server_list[j]->delay)
+ continue;
+ server = server_list[i];
+ server_list[i] = server_list[j];
+ server_list[j] = server;
+ }
+
+ /*
+ * Calculate the fixed part of the dispersion limit
+ */
+ local_threshold = (FP_SECOND >> (-(int)NTPDATE_PRECISION))
+ + NTP_MAXSKW;
+
+ /*
+ * Now drop samples until we're down to one.
+ */
+ while (nlist > 1) {
+ for (n = 0; n < nlist; n++) {
+ server_badness[n] = 0;
+ for (j = 0; j < nlist; j++) {
+ if (j == n) /* with self? */
+ continue;
+ d = server_list[j]->soffset
+ - server_list[n]->soffset;
+ if (d < 0) /* absolute value */
+ d = -d;
+ /*
+ * XXX This code *knows* that
+ * NTP_SELECT is 3/4
+ */
+ for (i = 0; i < j; i++)
+ d = (d>>1) + (d>>2);
+ server_badness[n] += d;
+ }
+ }
+
+ /*
+ * We now have an array of nlist badness
+ * coefficients. Find the badest. Find
+ * the minimum precision while we're at
+ * it.
+ */
+ i = 0;
+ n = server_list[0]->precision;;
+ for (j = 1; j < nlist; j++) {
+ if (server_badness[j] >= server_badness[i])
+ i = j;
+ if (n > server_list[j]->precision)
+ n = server_list[j]->precision;
+ }
+
+ /*
+ * i is the index of the server with the worst
+ * dispersion. If his dispersion is less than
+ * the threshold, stop now, else delete him and
+ * continue around again.
+ */
+ if (server_badness[i] < (local_threshold
+ + (FP_SECOND >> (-n))))
+ break;
+ for (j = i + 1; j < nlist; j++)
+ server_list[j-1] = server_list[j];
+ nlist--;
+ }
+
+ /*
+ * What remains is a list of less than 5 servers. Take
+ * the best.
+ */
+ sys_server = server_list[0];
+ }
+
+ /*
+ * That's it. Return our server.
+ */
+ return sys_server;
+}
+
+
+/*
+ * clock_adjust - process what we've received, and adjust the time
+ * if we got anything decent.
+ */
+static int
+clock_adjust()
+{
+ register int i;
+ register struct server *server;
+ s_fp absoffset;
+ int dostep;
+
+ for (i = 0; i < sys_numservers; i++)
+ clock_filter(sys_servers[i]);
+ server = clock_select();
+
+ if (debug || simple_query) {
+ for (i = 0; i < sys_numservers; i++)
+ printserver(sys_servers[i], stdout);
+ }
+
+ if (server == 0) {
+ syslog(LOG_ERR,
+ "no server suitable for synchronization found");
+ return(1);
+ }
+
+ dostep = 1;
+ if (!always_step) {
+ absoffset = server->soffset;
+ if (absoffset < 0)
+ absoffset = -absoffset;
+ if (absoffset < NTPDATE_THRESHOLD)
+ dostep = 0;
+ }
+
+ if (dostep) {
+ if (simple_query || l_step_systime(&server->offset)) {
+ syslog(LOG_NOTICE, "step time server %s offset %s",
+ ntoa(&server->srcadr),
+ lfptoa(&server->offset, 6));
+ }
+ } else {
+ if (simple_query || l_adj_systime(&server->offset)) {
+ syslog(LOG_NOTICE, "adjust time server %s offset %s",
+ ntoa(&server->srcadr),
+ lfptoa(&server->offset, 6));
+ }
+ }
+ return(0);
+}
+
+
+/* XXX ELIMINATE: merge BIG slew into adj_systime in lib/systime.c */
+/*
+ * addserver - determine a server's address and allocate a new structure
+ * for it.
+ */
+static void
+addserver(serv)
+ char *serv;
+{
+ register struct server *server;
+ u_long netnum;
+ static int toomany = 0;
+
+ if (sys_numservers >= sys_maxservers) {
+ if (!toomany) {
+ /*
+ * This is actually a `can't happen' now. Leave
+ * the error message in anyway, though
+ */
+ toomany = 1;
+ syslog(LOG_ERR,
+ "too many servers (> %d) specified, remainder not used",
+ sys_maxservers);
+ }
+ return;
+ }
+
+ if (!getnetnum(serv, &netnum)) {
+ syslog(LOG_ERR, "can't find host %s\n", serv);
+ return;
+ }
+
+ server = (struct server *)emalloc(sizeof(struct server));
+ memset((char *)server, 0, sizeof(struct server));
+
+ server->srcadr.sin_family = AF_INET;
+ server->srcadr.sin_addr.s_addr = netnum;
+ server->srcadr.sin_port = htons(NTP_PORT);
+
+ sys_servers[sys_numservers++] = server;
+ server->event_time = sys_numservers;
+}
+
+
+/*
+ * findserver - find a server in the list given its address
+ */
+static struct server *
+findserver(addr)
+ struct sockaddr_in *addr;
+{
+ register int i;
+ register u_long netnum;
+
+ if (htons(addr->sin_port) != NTP_PORT)
+ return 0;
+ netnum = addr->sin_addr.s_addr;
+
+ for (i = 0; i < sys_numservers; i++) {
+ if (netnum == sys_servers[i]->srcadr.sin_addr.s_addr)
+ return sys_servers[i];
+ }
+ return 0;
+}
+
+
+/*
+ * timer - process a timer interrupt
+ */
+static void
+timer()
+{
+ register int i;
+
+ /*
+ * Bump the current idea of the time
+ */
+ current_time++;
+
+ /*
+ * Search through the server list looking for guys
+ * who's event timers have expired. Give these to
+ * the transmit routine.
+ */
+ for (i = 0; i < sys_numservers; i++) {
+ if (sys_servers[i]->event_time != 0
+ && sys_servers[i]->event_time <= current_time)
+ transmit(sys_servers[i]);
+ }
+}
+
+
+
+/*
+ * init_alarm - set up the timer interrupt
+ */
+static void
+init_alarm()
+{
+ struct itimerval itimer;
+
+ alarm_flag = 0;
+
+ /*
+ * Set up the alarm interrupt. The first comes 1/(2*TIMER_HZ)
+ * seconds from now and they continue on every 1/TIMER_HZ seconds.
+ */
+ (void) signal_no_reset(SIGALRM, alarming);
+ itimer.it_interval.tv_sec = itimer.it_value.tv_sec = 0;
+ itimer.it_interval.tv_usec = 1000000/TIMER_HZ;
+ itimer.it_value.tv_usec = 1000000/(TIMER_HZ<<1);
+ setitimer(ITIMER_REAL, &itimer, (struct itimerval *)0);
+}
+
+
+/*
+ * alarming - record the occurance of an alarm interrupt
+ */
+static RETSIGTYPE
+alarming(sig)
+int sig;
+{
+ alarm_flag++;
+}
+
+
+/*
+ * We do asynchronous input using the SIGIO facility. A number of
+ * recvbuf buffers are preallocated for input. In the signal
+ * handler we poll to see if the socket is ready and read the
+ * packets from it into the recvbuf's along with a time stamp and
+ * an indication of the source host and the interface it was received
+ * through. This allows us to get as accurate receive time stamps
+ * as possible independent of other processing going on.
+ *
+ * We allocate a number of recvbufs equal to the number of servers
+ * plus 2. This should be plenty.
+ */
+
+/*
+ * recvbuf lists
+ */
+struct recvbuf *freelist; /* free buffers */
+struct recvbuf *fulllist; /* buffers with data */
+
+int full_recvbufs; /* number of full ones */
+int free_recvbufs;
+
+
+/*
+ * init_io - initialize I/O data and open socket
+ */
+static void
+init_io()
+{
+ register int i;
+ register struct recvbuf *rb;
+
+ /*
+ * Init buffer free list and stat counters
+ */
+ rb = (struct recvbuf *)
+ emalloc((sys_numservers + 2) * sizeof(struct recvbuf));
+ freelist = 0;
+ for (i = sys_numservers + 2; i > 0; i--) {
+ rb->next = freelist;
+ freelist = rb;
+ rb++;
+ }
+
+ fulllist = 0;
+ full_recvbufs = 0;
+ free_recvbufs = sys_numservers + 2;
+
+ /*
+ * Open the socket
+ */
+
+ /* create a datagram (UDP) socket */
+ if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+ syslog(LOG_ERR, "socket() failed: %m");
+ exit(1);
+ /*NOTREACHED*/
+ }
+
+ /*
+ * bind the socket to the NTP port
+ */
+ if (!debug && !simple_query) {
+ struct sockaddr_in addr;
+
+ memset((char *)&addr, 0, sizeof addr);
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(NTP_PORT);
+ addr.sin_addr.s_addr = htonl(INADDR_ANY);
+ if (bind(fd, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
+ if (errno == EADDRINUSE)
+ syslog(LOG_ERR,
+ "the NTP socket is in use, exiting");
+ else
+ syslog(LOG_ERR, "bind() fails: %m");
+ exit(1);
+ }
+ }
+
+ FD_ZERO(&fdmask);
+ FD_SET(fd, &fdmask);
+
+ /*
+ * set non-blocking,
+ */
+#if defined(O_NONBLOCK)
+ if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) {
+ syslog(LOG_ERR, "fcntl(FNDELAY|FASYNC) fails: %m");
+ exit(1);
+ /*NOTREACHED*/
+ }
+#else /* O_NONBLOCK */
+#if defined(FNDELAY)
+ if (fcntl(fd, F_SETFL, FNDELAY) < 0) {
+ syslog(LOG_ERR, "fcntl(FNDELAY|FASYNC) fails: %m");
+ exit(1);
+ /*NOTREACHED*/
+ }
+#else /* FNDELAY */
+Need non blocking I/O
+#endif /* FNDELAY */
+#endif /* O_NONBLOCK */
+}
+
+
+/* XXX ELIMINATE getrecvbufs (almost) identical to ntpdate.c, ntptrace.c, ntp_io.c */
+/*
+ * getrecvbufs - get receive buffers which have data in them
+ *
+ * ***N.B. must be called with SIGIO blocked***
+ */
+static struct recvbuf *
+getrecvbufs()
+{
+ struct recvbuf *rb;
+
+ if (full_recvbufs == 0) {
+ return (struct recvbuf *)0; /* nothing has arrived */
+ }
+
+ /*
+ * Get the fulllist chain and mark it empty
+ */
+ rb = fulllist;
+ fulllist = 0;
+ full_recvbufs = 0;
+
+ /*
+ * Return the chain
+ */
+ return rb;
+}
+
+
+/* XXX ELIMINATE freerecvbuf (almost) identical to ntpdate.c, ntptrace.c, ntp_io.c */
+/*
+ * freerecvbuf - make a single recvbuf available for reuse
+ */
+static void
+freerecvbuf(rb)
+ struct recvbuf *rb;
+{
+
+ rb->next = freelist;
+ freelist = rb;
+ free_recvbufs++;
+}
+
+
+/*
+ * sendpkt - send a packet to the specified destination
+ */
+static void
+sendpkt(dest, pkt, len)
+ struct sockaddr_in *dest;
+ struct pkt *pkt;
+ int len;
+{
+ int cc;
+
+ cc = sendto(fd, (char *)pkt, len, 0, (struct sockaddr *)dest,
+ sizeof(struct sockaddr_in));
+ if (cc == -1) {
+ if (errno != EWOULDBLOCK && errno != ENOBUFS)
+ syslog(LOG_ERR, "sendto(%s): %m", ntoa(dest));
+ }
+}
+
+
+/*
+ * input_handler - receive packets asynchronously
+ */
+static void
+input_handler()
+{
+ register int n;
+ register struct recvbuf *rb;
+ struct timeval tvzero;
+ int fromlen;
+ l_fp ts;
+ fd_set fds;
+
+ /*
+ * Do a poll to see if we have data
+ */
+ for (;;) {
+ fds = fdmask;
+ tvzero.tv_sec = tvzero.tv_usec = 0;
+ n = select(fd+1, &fds, (fd_set *)0, (fd_set *)0, &tvzero);
+
+ /*
+ * If nothing to do, just return. If an error occurred,
+ * complain and return. If we've got some, freeze a
+ * timestamp.
+ */
+ if (n == 0)
+ return;
+ else if (n == -1) {
+ syslog(LOG_ERR, "select() error: %m");
+ return;
+ }
+ get_systime(&ts);
+
+ /*
+ * Get a buffer and read the frame. If we
+ * haven't got a buffer, or this is received
+ * on the wild card socket, just dump the packet.
+ */
+ if (initializing || free_recvbufs == 0) {
+ char buf[100];
+
+ (void) read(fd, buf, sizeof buf);
+ continue;
+ }
+
+ rb = freelist;
+ freelist = rb->next;
+ free_recvbufs--;
+
+ fromlen = sizeof(struct sockaddr_in);
+ rb->recv_length = recvfrom(fd, (char *)&rb->recv_pkt,
+ sizeof(rb->recv_pkt), 0,
+ (struct sockaddr *)&rb->srcadr, &fromlen);
+ if (rb->recv_length == -1) {
+ rb->next = freelist;
+ freelist = rb;
+ free_recvbufs++;
+ continue;
+ }
+
+ /*
+ * Got one. Mark how and when it got here,
+ * put it on the full list.
+ */
+ rb->recv_time = ts;
+ rb->next = fulllist;
+ fulllist = rb;
+ full_recvbufs++;
+ }
+}
+
+
+/*
+ * adj_systime - do a big long slew of the system time
+ */
+static int
+l_adj_systime(ts)
+ l_fp *ts;
+{
+ struct timeval adjtv, oadjtv;
+ int isneg = 0;
+ l_fp offset;
+ l_fp overshoot;
+
+ /*
+ * Take the absolute value of the offset
+ */
+ offset = *ts;
+ if (L_ISNEG(&offset)) {
+ isneg = 1;
+ L_NEG(&offset);
+ }
+
+#ifndef STEP_SLEW
+ /*
+ * Calculate the overshoot. XXX N.B. This code *knows*
+ * ADJ_OVERSHOOT is 1/2.
+ */
+ overshoot = offset;
+ L_RSHIFTU(&overshoot);
+ if (overshoot.l_ui != 0 || (overshoot.l_uf > ADJ_MAXOVERSHOOT)) {
+ overshoot.l_ui = 0;
+ overshoot.l_uf = ADJ_MAXOVERSHOOT;
+ }
+ L_ADD(&offset, &overshoot);
+#endif
+ TSTOTV(&offset, &adjtv);
+
+ if (isneg) {
+ adjtv.tv_sec = -adjtv.tv_sec;
+ adjtv.tv_usec = -adjtv.tv_usec;
+ }
+
+ if (adjtv.tv_usec != 0 && !debug) {
+ if (adjtime(&adjtv, &oadjtv) < 0) {
+ syslog(LOG_ERR, "Can't adjust the time of day: %m");
+ return 0;
+ }
+ }
+ return 1;
+}
+
+
+/*
+ * This fuction is not the same as lib/systime step_systime!!!
+ */
+static int
+l_step_systime(ts)
+ l_fp *ts;
+{
+#ifdef SLEWALWAYS
+#ifdef STEP_SLEW
+ l_fp ftmp;
+ int isneg;
+ int n;
+
+ if (debug) return 1;
+ /*
+ * Take the absolute value of the offset
+ */
+ ftmp = ts;
+ if (L_ISNEG(&ftmp)) {
+ L_NEG(&tmp);
+ isneg = 1;
+ } else
+ isneg = 0;
+
+ if (tmp_ui >= 3) { /* Step it and slew - we might win */
+ n = step_systime_real(ts);
+ if (!n)
+ return n;
+ if (isneg)
+ ts->l_ui = ~0;
+ else
+ ts->l_ui = ~0;
+ }
+ /*
+ * Just add adjustment into the current offset. The update
+ * routine will take care of bringing the system clock into
+ * line.
+ */
+#endif
+ if (debug)
+ return 1;
+#ifdef FORCE_NTPDATE_STEP
+ return step_systime_real(ts);
+#else
+ l_adj_systime(ts);
+ return 1;
+#endif
+#else /* SLEWALWAYS */
+ if (debug)
+ return 1;
+ return step_systime_real(ts);
+#endif /* SLEWALWAYS */
+}
+
+/*
+ * getnetnum - given a host name, return its net number
+ */
+static int
+getnetnum(host, num)
+ char *host;
+ u_long *num;
+{
+ struct hostent *hp;
+
+ if (decodenetnum(host, num)) {
+ return 1;
+ } else if ((hp = gethostbyname(host)) != 0) {
+ memmove((char *)num, hp->h_addr, sizeof(U_LONG));
+ return (1);
+ }
+ return (0);
+}
+
+/* XXX ELIMINATE printserver similar in ntptrace.c, ntpdate.c */
+/*
+ * printserver - print detail information for a server
+ */
+static void
+printserver(pp, fp)
+ register struct server *pp;
+ FILE *fp;
+{
+ register int i;
+ char junk[5];
+ char *str;
+
+ if (!debug) {
+ (void) fprintf(fp, "server %s, stratum %d, offset %s, delay %s\n",
+ ntoa(&pp->srcadr), pp->stratum,
+ lfptoa(&pp->offset, 6), fptoa(pp->delay, 5));
+ return;
+ }
+
+ (void) fprintf(fp, "server %s, port %d\n",
+ ntoa(&pp->srcadr), ntohs(pp->srcadr.sin_port));
+
+ (void) fprintf(fp, "stratum %d, precision %d, leap %c%c, trust %03o\n",
+ pp->stratum, pp->precision,
+ pp->leap & 0x2 ? '1' : '0',
+ pp->leap & 0x1 ? '1' : '0',
+ pp->trust);
+
+ if (pp->stratum == 1) {
+ junk[4] = 0;
+ memmove(junk, (char *)&pp->refid, 4);
+ str = junk;
+ } else {
+ str = numtoa(pp->refid);
+ }
+ (void) fprintf(fp,
+ "refid [%s], delay %s, dispersion %s\n",
+ str, fptoa(pp->delay, 5),
+ ufptoa(pp->dispersion, 5));
+
+ (void) fprintf(fp, "transmitted %d, in filter %d\n",
+ pp->xmtcnt, pp->filter_nextpt);
+
+ (void) fprintf(fp, "reference time: %s\n",
+ prettydate(&pp->reftime));
+ (void) fprintf(fp, "originate timestamp: %s\n",
+ prettydate(&pp->org));
+ (void) fprintf(fp, "transmit timestamp: %s\n",
+ prettydate(&pp->xmt));
+
+ (void) fprintf(fp, "filter delay: ");
+ for (i = 0; i < NTP_SHIFT; i++) {
+ (void) fprintf(fp, " %-8.8s", fptoa(pp->filter_delay[i], 5));
+ if (i == (NTP_SHIFT>>1)-1)
+ (void) fprintf(fp, "\n ");
+ }
+ (void) fprintf(fp, "\n");
+
+ (void) fprintf(fp, "filter offset:");
+ for (i = 0; i < PEER_SHIFT; i++) {
+ (void) fprintf(fp, " %-8.8s", lfptoa(&pp->filter_offset[i], 6));
+ if (i == (PEER_SHIFT>>1)-1)
+ (void) fprintf(fp, "\n ");
+ }
+ (void) fprintf(fp, "\n");
+
+ (void) fprintf(fp, "delay %s, dispersion %s\n",
+ fptoa(pp->delay, 5), ufptoa(pp->dispersion, 5));
+
+ (void) fprintf(fp, "offset %s\n\n",
+ lfptoa(&pp->offset, 6));
+}
+
+#if defined(NEED_VSPRINTF)
+/*
+ * This nugget for pre-tahoe 4.3bsd systems
+ */
+#if !defined(__STDC__) || !__STDC__
+#define const
+#endif
+
+int
+vsprintf(str, fmt, ap)
+ char *str;
+ const char *fmt;
+ va_list ap;
+{
+ FILE f;
+ int len;
+
+ f._flag = _IOWRT+_IOSTRG;
+ f._ptr = str;
+ f._cnt = 32767;
+ len = _doprnt(fmt, ap, &f);
+ *f._ptr = 0;
+ return (len);
+}
+#endif
+
diff --git a/usr.sbin/xntpd/ntpdate/ntpdate.h b/usr.sbin/xntpd/ntpdate/ntpdate.h
new file mode 100644
index 0000000..cfbe90a
--- /dev/null
+++ b/usr.sbin/xntpd/ntpdate/ntpdate.h
@@ -0,0 +1,89 @@
+/*
+ * ntpdate.h - declarations for the ntpdate program
+ */
+
+#include "ntp_malloc.h"
+
+/*
+ * The server structure is a much simplified version of the
+ * peer structure, for ntpdate's use. Since we always send
+ * in client mode and expect to receive in server mode, this
+ * leaves only a very limited number of things we need to
+ * remember about the server.
+ */
+struct server {
+ struct sockaddr_in srcadr; /* address of remote host */
+ u_char leap; /* leap indicator */
+ u_char stratum; /* stratum of remote server */
+ s_char precision; /* server's clock precision */
+ u_char trust; /* trustability of the filtered data */
+ u_fp rootdelay; /* distance from primary clock */
+ u_fp rootdispersion; /* peer clock dispersion */
+ U_LONG refid; /* peer reference ID */
+ l_fp reftime; /* time of peer's last update */
+ u_long event_time; /* time for next timeout */
+ u_short xmtcnt; /* number of packets transmitted */
+ u_short filter_nextpt; /* index into filter shift register */
+ s_fp filter_delay[NTP_SHIFT]; /* delay part of shift register */
+ l_fp filter_offset[NTP_SHIFT]; /* offset part of shift register */
+ s_fp filter_soffset[NTP_SHIFT]; /* offset in s_fp format, for disp */
+ u_fp filter_error[NTP_SHIFT]; /* error part of shift register */
+ l_fp org; /* peer's originate time stamp */
+ l_fp xmt; /* transmit time stamp */
+ u_fp delay; /* filter estimated delay */
+ u_fp dispersion; /* filter estimated dispersion */
+ l_fp offset; /* filter estimated clock offset */
+ s_fp soffset; /* fp version of above */
+};
+
+
+/*
+ * ntpdate runs everything on a simple, short timeout. It sends a
+ * packet and sets the timeout (by default, to a small value suitable
+ * for a LAN). If it receives a response it sends another request.
+ * If it times out it shifts zeroes into the filter and sends another
+ * request.
+ *
+ * The timer routine is run often (once every 1/5 second currently)
+ * so that time outs are done with reasonable precision.
+ */
+#define TIMER_HZ (5) /* 5 per second */
+
+/*
+ * ntpdate will make a long adjustment using adjtime() if the times
+ * are close, or step the time if the times are farther apart. The
+ * following defines what is "close".
+ */
+#ifdef linux
+#define NTPDATE_THRESHOLD (FP_SECOND / 8) /* 1/8 second */
+#else
+#define NTPDATE_THRESHOLD (FP_SECOND >> 1) /* 1/2 second */
+#endif
+
+/*
+ * When doing adjustments, ntpdate actually overadjusts (currently
+ * by 50%, though this may change). While this will make it take longer
+ * to reach a steady state condition, it will typically result in
+ * the clock keeping more accurate time, on average. The amount of
+ * overshoot is limited.
+ */
+#ifdef NOTNOW
+#define ADJ_OVERSHOOT 1/2 /* this is hard coded */
+#endif /* NOTNOW */
+#define ADJ_MAXOVERSHOOT 0x10000000 /* 50 ms as a ts fraction */
+
+/*
+ * Since ntpdate isn't aware of some of the things that normally get
+ * put in an NTP packet, we fix some values.
+ */
+#define NTPDATE_PRECISION (-6) /* use this precision */
+#define NTPDATE_DISTANCE FP_SECOND /* distance is 1 sec */
+#define NTPDATE_DISP FP_SECOND /* so is the dispersion */
+#define NTPDATE_REFID (0) /* reference ID to use */
+
+
+/*
+ * Some defaults
+ */
+#define DEFTIMEOUT 5 /* 5 timer increments */
+#define DEFSAMPLES 4 /* get 4 samples per server */
diff --git a/usr.sbin/xntpd/ntpq/Makefile b/usr.sbin/xntpd/ntpq/Makefile
new file mode 100644
index 0000000..ea643d2
--- /dev/null
+++ b/usr.sbin/xntpd/ntpq/Makefile
@@ -0,0 +1,29 @@
+#
+# $Id$
+#
+
+CFLAGS+= -I${.CURDIR}/../include
+
+.if exists(${.OBJDIR}/../lib)
+LDADD+= -L${.OBJDIR}/../lib
+DPADD+= ${.OBJDIR}/../lib/libntp.a
+.else
+LDADD+= -L${.CURDIR}/../lib
+DPADD+= ${.CURDIR}/../lib/libntp.a
+.endif
+
+LDADD+= -lntp
+
+PROG= ntpq
+MAN8= ${.CURDIR}/../doc/ntpq.8
+CLEANFILES+= .version version.c
+BINDIR= /usr/bin
+
+SRCS= ntpq.c ntpq_ops.c version.c
+
+beforedepend: version.c
+
+version.c: ${.CURDIR}/../VERSION
+ sh -e ${.CURDIR}/../scripts/mkversion ntpq
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/xntpd/ntpq/README b/usr.sbin/xntpd/ntpq/README
new file mode 100644
index 0000000..117c66c
--- /dev/null
+++ b/usr.sbin/xntpd/ntpq/README
@@ -0,0 +1,6 @@
+README file for directory ./ntpq of the NTP Version 3 distribution
+
+This directory contains the sources for the ntpq utility program. See
+the README and RELNOTES files in the parent directory for directions on
+how to make and install this program. The current version number of this
+program is in the version.c file.
diff --git a/usr.sbin/xntpd/ntpq/ntpq.c b/usr.sbin/xntpd/ntpq/ntpq.c
new file mode 100644
index 0000000..2c5eacf
--- /dev/null
+++ b/usr.sbin/xntpd/ntpq/ntpq.c
@@ -0,0 +1,3091 @@
+/*
+ * ntpq - query an NTP server using mode 6 commands
+ */
+#include <stdio.h>
+#include <ctype.h>
+#include <signal.h>
+#include <setjmp.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <netdb.h>
+
+#include "ntp_select.h"
+#include "ntpq.h"
+#include "ntp_unixtime.h"
+#include "ntp_calendar.h"
+#include "ntp_io.h"
+#include "ntp_stdlib.h"
+
+/*
+ * Because we potentially understand a lot of commands we will run
+ * interactive if connected to a terminal.
+ */
+int interactive = 0; /* set to 1 when we should prompt */
+char *prompt = "ntpq> "; /* prompt to ask him about */
+
+
+/*
+ * Keyid used for authenticated requests. Obtained on the fly.
+ */
+u_long info_auth_keyid = -1;
+
+/*
+ * Type of key md5 or des
+ */
+#define KEY_TYPE_DES 3
+#define KEY_TYPE_MD5 4
+
+int info_auth_keytype = KEY_TYPE_DES; /* DES */
+
+/*
+ * Flag which indicates we should always send authenticated requests
+ */
+int always_auth = 0;
+
+/*
+ * Flag which indicates raw mode output.
+ */
+int rawmode = 0;
+
+/*
+ * Packet version number we use
+ */
+u_char pktversion = NTP_VERSION;
+
+/*
+ * Don't jump if no set jmp.
+ */
+int jump = 0;
+
+/*
+ * Format values
+ */
+#define PADDING 0
+#define TS 1 /* time stamp */
+#define FL 2 /* l_fp type value */
+#define FU 3 /* u_fp type value */
+#define FS 4 /* s_fp type value */
+#define UI 5 /* unsigned integer value */
+#define IN 6 /* signed integer value */
+#define HA 7 /* host address */
+#define NA 8 /* network address */
+#define ST 9 /* string value */
+#define RF 10 /* refid (sometimes string, sometimes not) */
+#define LP 11 /* leap (print in binary) */
+#define OC 12 /* integer, print in octal */
+#define MD 13 /* mode */
+#define AR 14 /* array of times */
+#define TST 15 /* test flags */
+#define EOV 255 /* end of table */
+
+
+/*
+ * System variable values. The array can be indexed by
+ * the variable index to find the textual name.
+ */
+struct ctl_var sys_var[] = {
+ { 0, PADDING, "" }, /* 0 */
+ { CS_LEAP, LP, "leap" }, /* 1 */
+ { CS_STRATUM, UI, "stratum" }, /* 2 */
+ { CS_PRECISION, IN, "precision" }, /* 3 */
+ { CS_ROOTDELAY, FS, "rootdelay" }, /* 4 */
+ { CS_ROOTDISPERSION, FU, "rootdispersion" }, /* 5 */
+ { CS_REFID, RF, "refid" }, /* 6 */
+ { CS_REFTIME, TS, "reftime" }, /* 7 */
+ { CS_POLL, UI, "poll" }, /* 8 */
+ { CS_PEERID, UI, "peer" }, /* 9 */
+ { CS_OFFSET, FL, "phase" }, /* 10 */
+ { CS_DRIFT, FS, "freq" }, /* 11 */
+ { CS_COMPLIANCE, FU, "error" }, /* 12 */
+ { CS_CLOCK, TS, "clock" }, /* 13 */
+ { CS_LEAPIND, LP, "leapindicator" }, /* 14 */
+ { CS_LEAPWARNING, LP, "leapwarning" }, /* 15 */
+ { CS_PROCESSOR, ST, "processor" }, /* 16 */
+ { CS_SYSTEM, ST, "system" }, /* 17 */
+ { CS_KEYID, UI, "keyid" }, /* 18 */
+ { CS_REFSKEW, FL, "refskew" }, /* 19 */
+ { 0, EOV, "" }
+};
+
+
+/*
+ * Peer variable list
+ */
+struct ctl_var peer_var[] = {
+ { 0, PADDING, "" }, /* 0 */
+ { CP_CONFIG, UI, "config" }, /* 1 */
+ { CP_AUTHENABLE, UI, "authenable" }, /* 2 */
+ { CP_AUTHENTIC, UI, "authentic" }, /* 3 */
+ { CP_SRCADR, HA, "srcadr" }, /* 4 */
+ { CP_SRCPORT, UI, "srcport" }, /* 5 */
+ { CP_DSTADR, NA, "dstadr" }, /* 6 */
+ { CP_DSTPORT, UI, "dstport" }, /* 7 */
+ { CP_LEAP, LP, "leap" }, /* 8 */
+ { CP_HMODE, MD, "hmode" }, /* 9 */
+ { CP_STRATUM, UI, "stratum" }, /* 10 */
+ { CP_PPOLL, UI, "ppoll" }, /* 11 */
+ { CP_HPOLL, UI, "hpoll" }, /* 12 */
+ { CP_PRECISION, IN, "precision" }, /* 13 */
+ { CP_ROOTDELAY, FS, "rootdelay" }, /* 14 */
+ { CP_ROOTDISPERSION, FU, "rootdispersion" }, /* 15 */
+ { CP_REFID, RF, "refid" }, /* 16 */
+ { CP_REFTIME, TS, "reftime" }, /* 17 */
+ { CP_ORG, TS, "org" }, /* 18 */
+ { CP_REC, TS, "rec" }, /* 19 */
+ { CP_XMT, TS, "xmt" }, /* 20 */
+ { CP_REACH, OC, "reach" }, /* 21 */
+ { CP_VALID, UI, "valid" }, /* 22 */
+ { CP_TIMER, UI, "timer" }, /* 23 */
+ { CP_DELAY, AR, "delay" }, /* 24 */
+ { CP_OFFSET, AR, "offset" }, /* 25 */
+ { CP_DISPERSION, FU, "dispersion" }, /* 26 */
+ { CP_KEYID, UI, "keyid" }, /* 27 */
+ { CP_FILTDELAY, AR, "filtdelay" }, /* 28 */
+ { CP_FILTOFFSET, AR, "filtoffset" }, /* 29 */
+ { CP_PMODE, ST, "pmode" }, /* 30 */
+ { CP_RECEIVED, UI, "received" }, /* 31 */
+ { CP_SENT, UI, "sent" }, /* 32 */
+ { CP_FILTERROR, AR, "filterror" }, /* 33 */
+ { CP_FLASH, TST, "flash"}, /* 34 */
+ { CP_DISP, AR, "disp" }, /* 35 */
+ /*
+ * These are duplicate entries so that we can
+ * process deviant version of the xntp protocal.
+ */
+ { CP_SRCADR, HA, "peeraddr" }, /* 4 */
+ { CP_SRCPORT, UI, "peerport" }, /* 5 */
+ { CP_PPOLL, UI, "peerpoll" }, /* 11 */
+ { CP_HPOLL, UI, "hostpoll" }, /* 12 */
+ { 0, EOV, "" }
+};
+
+
+/*
+ * Clock variable list
+ */
+struct ctl_var clock_var[] = {
+ { 0, PADDING, "" }, /* 0 */
+ { CC_TYPE, UI, "type" }, /* 1 */
+ { CC_TIMECODE, ST, "timecode" }, /* 2 */
+ { CC_POLL, UI, "poll" }, /* 3 */
+ { CC_NOREPLY, UI, "noreply" }, /* 4 */
+ { CC_BADFORMAT, UI, "badformat" }, /* 5 */
+ { CC_BADDATA, UI, "baddata" }, /* 6 */
+ { CC_FUDGETIME1, FL, "fudgetime1" }, /* 7 */
+ { CC_FUDGETIME2, FL, "fudgetime2" }, /* 8 */
+ { CC_FUDGEVAL1, UI, "stratum" }, /* 9 */
+ { CC_FUDGEVAL2, RF, "refid" }, /* 10 */
+ { CC_FLAGS, UI, "flags" }, /* 11 */
+ { CC_DEVICE, ST, "device" }, /* 12 */
+ { 0, EOV, "" }
+};
+
+
+/*
+ * Structure for turning various constants into a readable string.
+ */
+struct codestring {
+ int code;
+ char *string;
+};
+
+/*
+ * flasher bits
+ */
+static char *tstflagnames[] = {
+ "DUPLICATE PKT",
+ "BOGUS PKT",
+ "PROTO UNSYNC",
+ "PEER BOUNDS",
+ "BAD AUTH",
+ "PEER CLOCK UNSYNC",
+ "BAD STRATUM",
+ "ROOT BOUNDS"
+};
+
+/*
+ * Leap values
+ */
+struct codestring leap_codes[] = {
+ { 0, "leap_none" },
+ { 1, "leap_add_sec" },
+ { 2, "leap_del_sec" },
+ { 3, "sync_alarm" },
+ { -1, "leap" }
+};
+
+
+/*
+ * Clock source
+ */
+struct codestring sync_codes[] = {
+ { CTL_SST_TS_UNSPEC, "sync_unspec" },
+ { CTL_SST_TS_ATOM, "sync_atomic" },
+ { CTL_SST_TS_LF, "sync_lf_clock" },
+ { CTL_SST_TS_HF, "sync_hf_clock" },
+ { CTL_SST_TS_UHF, "sync_uhf_clock" },
+ { CTL_SST_TS_LOCAL, "sync_local_proto" },
+ { CTL_SST_TS_NTP, "sync_ntp" },
+ { CTL_SST_TS_UDPTIME, "sync_udp/time" },
+ { CTL_SST_TS_WRSTWTCH, "sync_wristwatch" },
+ { CTL_SST_TS_TELEPHONE, "sync_telephone" },
+ { -1, "sync" }
+};
+
+
+/*
+ * Peer selection
+ */
+struct codestring select_codes[] = {
+ { CTL_PST_SEL_REJECT, "sel_reject" },
+ { CTL_PST_SEL_SANE, "sel_sane" },
+ { CTL_PST_SEL_CORRECT, "sel_correct" },
+ { CTL_PST_SEL_SELCAND, "sel_candidate" },
+ { CTL_PST_SEL_SYNCCAND, "sel_sync" },
+ { CTL_PST_SEL_DISTSYSPEER, "sel_sys.peer, hi_dist" },
+ { CTL_PST_SEL_SYSPEER, "sel_sys.peer" },
+ { -1, "sel" }
+};
+
+
+/*
+ * Clock status
+ */
+struct codestring clock_codes[] = {
+ { CTL_CLK_OKAY, "clk_okay" },
+ { CTL_CLK_NOREPLY, "clk_noreply" },
+ { CTL_CLK_BADFORMAT, "clk_badformat" },
+ { CTL_CLK_FAULT, "clk_fault" },
+ { CTL_CLK_PROPAGATION, "clk_propagation" },
+ { CTL_CLK_BADDATE, "clk_baddate" },
+ { CTL_CLK_BADTIME, "clk_badtime" },
+ { -1, "clk" }
+};
+
+
+/*
+ * System Events
+ */
+struct codestring sys_codes[] = {
+ { EVNT_UNSPEC, "event_unspec" },
+ { EVNT_SYSRESTART, "event_restart" },
+ { EVNT_SYSFAULT, "event_fault" },
+ { EVNT_SYNCCHG, "event_sync_chg" },
+ { EVNT_PEERSTCHG, "event_peer/strat_chg" },
+ { EVNT_CLOCKRESET, "event_clock_reset" },
+ { EVNT_BADDATETIM, "event_bad_date" },
+ { EVNT_CLOCKEXCPT, "event_clock_excptn" },
+ { -1, "event" }
+};
+
+/*
+ * Peer Events
+ */
+struct codestring peer_codes[] = {
+ { EVNT_UNSPEC, "event_unspec" },
+ { EVNT_PEERIPERR & ~PEER_EVENT, "event_ip_err" },
+ { EVNT_PEERAUTH & ~PEER_EVENT, "event_authen" },
+ { EVNT_UNREACH & ~PEER_EVENT, "event_unreach" },
+ { EVNT_REACH & ~PEER_EVENT, "event_reach" },
+#if 0
+ { EVNT_PEERSTRAT & ~PEER_EVENT, "event_stratum_chg" },
+#endif
+ { -1, "event" }
+};
+
+
+/*
+ * Built in command handler declarations
+ */
+static int openhost P((char *));
+static int sendpkt P((char *, int));
+static int getresponse P((int, int, u_short *, int *, char **, int));
+static int sendrequest P((int, int, int, int, char *));
+static void getcmds P((void));
+static RETSIGTYPE abortcmd P((int));
+static void docmd P((char *));
+static void tokenize P((char *, char **, int *));
+static int findcmd P((char *, struct xcmd *, struct xcmd *, struct xcmd **));
+static int getarg P((char *, int, arg_v *));
+static int rtdatetolfp P((char *, l_fp *));
+static int decodearr P((char *, int *, l_fp *));
+static char * getcode P((int, struct codestring *));
+static void help P((struct parse *, FILE *));
+#if defined(sgi) || defined(SYS_BSDI) || defined(__STDC__)
+static int helpsort P((const void *, const void *));
+#else
+static int helpsort P((char **, char **));
+#endif /* sgi */
+static void printusage P((struct xcmd *, FILE *));
+static void timeout P((struct parse *, FILE *));
+static void delay P((struct parse *, FILE *));
+static void host P((struct parse *, FILE *));
+static void ntp_poll P((struct parse *, FILE *));
+static void keyid P((struct parse *, FILE *));
+static void keytype P((struct parse *, FILE *));
+static void passwd P((struct parse *, FILE *));
+static void hostnames P((struct parse *, FILE *));
+static void setdebug P((struct parse *, FILE *));
+static void quit P((struct parse *, FILE *));
+static void version P((struct parse *, FILE *));
+static void raw P((struct parse *, FILE *));
+static void cooked P((struct parse *, FILE *));
+static void authenticate P((struct parse *, FILE *));
+static void ntpversion P((struct parse *, FILE *));
+static void warning P((char *, char *, char *));
+static void error P((char *, char *, char *));
+static u_long getkeyid P((char *));
+static void atoascii P((int, char *, char *));
+static void makeascii P((int, char *, FILE *));
+static char * getevents P((int));
+static char * statustoa P((int, int));
+static void rawprint P((int, int, char *, int, FILE *));
+static void startoutput P((void));
+static void output P((FILE *, char *, char *));
+static void endoutput P((FILE *));
+static void outputarr P((FILE *, char *, int, l_fp *));
+static void cookedprint P((int, int, char *, int, FILE *));
+#if defined(sgi) || defined(SYS_BSDI) || defined(__STDC__)
+static int assoccmp P((const void *, const void *));
+#else
+static int assoccmp P((struct association *, struct association *));
+#endif /* sgi || bsdi */
+
+
+/*
+ * Built-in commands we understand
+ */
+struct xcmd builtins[] = {
+ { "?", help, { OPT|STR, NO, NO, NO },
+ { "command", "", "", "" },
+ "tell the use and syntax of commands" },
+ { "help", help, { OPT|STR, NO, NO, NO },
+ { "command", "", "", "" },
+ "tell the use and syntax of commands" },
+ { "timeout", timeout, { OPT|UINT, NO, NO, NO },
+ { "msec", "", "", "" },
+ "set the primary receive time out" },
+ { "delay", delay, { OPT|INT, NO, NO, NO },
+ { "msec", "", "", "" },
+ "set the delay added to encryption time stamps" },
+ { "host", host, { OPT|STR, NO, NO, NO },
+ { "hostname", "", "", "" },
+ "specify the host whose NTP server we talk to" },
+ { "poll", ntp_poll, { OPT|UINT, OPT|STR, NO, NO },
+ { "n", "verbose", "", "" },
+ "poll an NTP server in client mode `n' times" },
+ { "passwd", passwd, { NO, NO, NO, NO },
+ { "", "", "", "" },
+ "specify a password to use for authenticated requests"},
+ { "hostnames", hostnames, { OPT|STR, NO, NO, NO },
+ { "yes|no", "", "", "" },
+ "specify whether hostnames or net numbers are printed"},
+ { "debug", setdebug, { OPT|STR, NO, NO, NO },
+ { "no|more|less", "", "", "" },
+ "set/change debugging level" },
+ { "quit", quit, { NO, NO, NO, NO },
+ { "", "", "", "" },
+ "exit ntpq" },
+ { "keyid", keyid, { OPT|UINT, NO, NO, NO },
+ { "key#", "", "", "" },
+ "set keyid to use for authenticated requests" },
+ { "version", version, { NO, NO, NO, NO },
+ { "", "", "", "" },
+ "print version number" },
+ { "raw", raw, { NO, NO, NO, NO },
+ { "", "", "", "" },
+ "do raw mode variable output" },
+ { "cooked", cooked, { NO, NO, NO, NO },
+ { "", "", "", "" },
+ "do cooked mode variable output" },
+ { "authenticate", authenticate, { OPT|STR, NO, NO, NO },
+ { "yes|no", "", "", "" },
+ "always authenticate requests to this server" },
+ { "ntpversion", ntpversion, { OPT|UINT, NO, NO, NO },
+ { "version number", "", "", "" },
+ "set the NTP version number to use for requests" },
+ { "keytype", keytype, { STR, NO, NO, NO },
+ { "key type (md5|des)", "", "", "" },
+ "set key type to use for authenticated requests (des|md5)" },
+ { 0, 0, { NO, NO, NO, NO },
+ { "", "", "", "" }, "" }
+};
+
+
+/*
+ * Default values we use.
+ */
+#define DEFTIMEOUT (5) /* 5 second time out */
+#define DEFSTIMEOUT (2) /* 2 second time out after first */
+#define DEFDELAY 0x51EB852 /* 20 milliseconds, l_fp fraction */
+#define DEFHOST "localhost" /* default host name */
+#define LENHOSTNAME 256 /* host name is 256 characters long */
+#define MAXCMDS 100 /* maximum commands on cmd line */
+#define MAXHOSTS 100 /* maximum hosts on cmd line */
+#define MAXLINE 512 /* maximum line length */
+#define MAXTOKENS (1+MAXARGS+2) /* maximum number of usable tokens */
+#define MAXVARLEN 256 /* maximum length of a variable name */
+#define MAXVALLEN 256 /* maximum length of a variable value */
+#define MAXOUTLINE 72 /* maximum length of an output line */
+
+/*
+ * Some variables used and manipulated locally
+ */
+struct timeval tvout = { DEFTIMEOUT, 0 }; /* time out for reads */
+struct timeval tvsout = { DEFSTIMEOUT, 0 }; /* secondary time out */
+l_fp delay_time; /* delay time */
+char currenthost[LENHOSTNAME]; /* current host name */
+struct sockaddr_in hostaddr = { 0 }; /* host address */
+int showhostnames = 1; /* show host names by default */
+
+int sockfd; /* fd socket is openned on */
+int havehost = 0; /* set to 1 when host open */
+struct servent *server_entry = NULL; /* server entry for ntp */
+
+/*
+ * Sequence number used for requests. It is incremented before
+ * it is used.
+ */
+u_short sequence;
+
+/*
+ * Holds data returned from queries. Declare buffer long to be sure of
+ * alignment.
+ */
+#define MAXFRAGS 24 /* maximum number of fragments */
+#define DATASIZE (MAXFRAGS*480) /* maximum amount of data */
+long pktdata[DATASIZE/sizeof(long)];
+
+/*
+ * Holds association data for use with the &n operator.
+ */
+struct association assoc_cache[MAXASSOC];
+int numassoc = 0; /* number of cached associations */
+
+/*
+ * For commands typed on the command line (with the -c option)
+ */
+int numcmds = 0;
+char *ccmds[MAXCMDS];
+#define ADDCMD(cp) if (numcmds < MAXCMDS) ccmds[numcmds++] = (cp)
+
+/*
+ * When multiple hosts are specified.
+ */
+int numhosts = 0;
+char *chosts[MAXHOSTS];
+#define ADDHOST(cp) if (numhosts < MAXHOSTS) chosts[numhosts++] = (cp)
+
+/*
+ * Error codes for internal use
+ */
+#define ERR_UNSPEC 256
+#define ERR_INCOMPLETE 257
+#define ERR_TIMEOUT 258
+#define ERR_TOOMUCH 259
+
+/*
+ * Macro definitions we use
+ */
+#define ISSPACE(c) ((c) == ' ' || (c) == '\t')
+#define ISEOL(c) ((c) == '\n' || (c) == '\r' || (c) == '\0')
+#define STREQ(a, b) (*(a) == *(b) && strcmp((a), (b)) == 0)
+
+/*
+ * Jump buffer for longjumping back to the command level
+ */
+jmp_buf interrupt_buf;
+
+/*
+ * Points at file being currently printed into
+ */
+FILE *current_output;
+
+/*
+ * Command table imported from ntpdc_ops.c
+ */
+extern struct xcmd opcmds[];
+
+char *progname;
+int debug;
+
+/*
+ * main - parse arguments and handle options
+ */
+void
+main(argc, argv)
+int argc;
+char *argv[];
+{
+ int c;
+ int errflg = 0;
+ extern int ntp_optind;
+ extern char *ntp_optarg;
+
+ delay_time.l_ui = 0;
+ delay_time.l_uf = DEFDELAY;
+
+ progname = argv[0];
+ while ((c = ntp_getopt(argc, argv, "c:dinp")) != EOF)
+ switch (c) {
+ case 'c':
+ ADDCMD(ntp_optarg);
+ break;
+ case 'd':
+ ++debug;
+ break;
+ case 'i':
+ interactive = 1;
+ break;
+ case 'n':
+ showhostnames = 0;
+ break;
+ case 'p':
+ ADDCMD("peers");
+ break;
+ default:
+ errflg++;
+ break;
+ }
+ if (errflg) {
+ (void) fprintf(stderr,
+ "usage: %s [-dinp] [-c cmd] host ...\n",
+ progname);
+ exit(2);
+ }
+ if (ntp_optind == argc) {
+ ADDHOST(DEFHOST);
+ } else {
+ for (; ntp_optind < argc; ntp_optind++)
+ ADDHOST(argv[ntp_optind]);
+ }
+
+ if (numcmds == 0 && interactive == 0
+ && isatty(fileno(stdin)) && isatty(fileno(stderr))) {
+ interactive = 1;
+ }
+
+ if (interactive)
+ (void) signal_no_reset(SIGINT, abortcmd);
+
+ if (numcmds == 0) {
+ (void) openhost(chosts[0]);
+ getcmds();
+ } else {
+ int ihost;
+ int icmd;
+
+ for (ihost = 0; ihost < numhosts; ihost++) {
+ if (openhost(chosts[ihost]))
+ for (icmd = 0; icmd < numcmds; icmd++)
+ docmd(ccmds[icmd]);
+ }
+ }
+ exit(0);
+}
+
+
+/*
+ * openhost - open a socket to a host
+ */
+static int
+openhost(hname)
+ char *hname;
+{
+ u_long netnum;
+ char temphost[LENHOSTNAME];
+
+ if (server_entry == NULL) {
+ server_entry = getservbyname("ntp", "udp");
+ if (server_entry == NULL) {
+ (void) fprintf(stderr, "%s: ntp/udp: unknown service\n",
+ progname);
+ exit(1);
+ }
+ if (debug > 2)
+ printf("Got ntp/udp service entry\n");
+ }
+
+ if (!getnetnum(hname, &netnum, temphost))
+ return 0;
+
+ if (debug > 2)
+ printf("Opening host %s\n", temphost);
+
+ if (havehost == 1) {
+ if (debug > 2)
+ printf("Closing old host %s\n", currenthost);
+ (void) close(sockfd);
+ havehost = 0;
+ }
+ (void) strcpy(currenthost, temphost);
+
+ hostaddr.sin_family = AF_INET;
+ hostaddr.sin_port = server_entry->s_port;
+ hostaddr.sin_addr.s_addr = netnum;
+
+ sockfd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sockfd == -1)
+ error("socket", "", "");
+
+#if defined(SYS_HPUX) && (SYS_HPUX < 8)
+#ifdef SO_RCVBUF
+ { int rbufsize = DATASIZE + 2048; /* 2K for slop */
+ if (setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF,
+ &rbufsize, sizeof(int)) == -1)
+ error("setsockopt", "", "");
+ }
+#endif
+#endif
+
+ if (connect(sockfd, (struct sockaddr *)&hostaddr,
+ sizeof(hostaddr)) == -1)
+ error("connect", "", "");
+
+ havehost = 1;
+ return 1;
+}
+
+
+/* XXX ELIMINATE sendpkt similar in ntpq.c, ntpdc.c, ntp_io.c, ntptrace.c */
+/*
+ * sendpkt - send a packet to the remote host
+ */
+static int
+sendpkt(xdata, xdatalen)
+ char *xdata;
+ int xdatalen;
+{
+ if (debug >= 3)
+ printf("Sending %d octets\n", xdatalen);
+
+ if (write(sockfd, xdata, xdatalen) == -1) {
+ warning("write to %s failed", currenthost, "");
+ return -1;
+ }
+
+ if (debug >= 4) {
+ int first = 8;
+ printf("Packet data:\n");
+ while (xdatalen-- > 0) {
+ if (first-- == 0) {
+ printf("\n");
+ first = 7;
+ }
+ printf(" %02x", *xdata++ & 0xff);
+ }
+ printf("\n");
+ }
+ return 0;
+}
+
+
+
+/*
+ * getresponse - get a (series of) response packet(s) and return the data
+ */
+static int
+getresponse(opcode, associd, rstatus, rsize, rdata, timeo)
+ int opcode;
+ int associd;
+ u_short *rstatus;
+ int *rsize;
+ char **rdata;
+ int timeo;
+{
+ struct ntp_control rpkt;
+ struct timeval tvo;
+ u_short offsets[MAXFRAGS+1];
+ u_short counts[MAXFRAGS+1];
+ u_short offset;
+ u_short count;
+ int numfrags;
+ int seenlastfrag;
+ fd_set fds;
+ int n;
+
+ /*
+ * This is pretty tricky. We may get between 1 and MAXFRAG packets
+ * back in response to the request. We peel the data out of
+ * each packet and collect it in one long block. When the last
+ * packet in the sequence is received we'll know how much data we
+ * should have had. Note we use one long time out, should reconsider.
+ */
+ *rsize = 0;
+ if (rstatus)
+ *rstatus = 0;
+ *rdata = (char *)pktdata;
+
+ numfrags = 0;
+ seenlastfrag = 0;
+
+ FD_ZERO(&fds);
+
+again:
+ if (numfrags == 0)
+ tvo = tvout;
+ else
+ tvo = tvsout;
+
+ FD_SET(sockfd, &fds);
+ n = select(sockfd+1, &fds, (fd_set *)0, (fd_set *)0, &tvo);
+
+ if (debug >= 1)
+ printf("select() returns %d\n", n);
+
+ if (n == -1) {
+ warning("select fails", "", "");
+ return -1;
+ }
+ if (n == 0) {
+ /*
+ * Timed out. Return what we have
+ */
+ if (numfrags == 0) {
+ if (timeo)
+ (void) fprintf(stderr,
+ "%s: timed out, nothing received\n",
+ currenthost);
+ return ERR_TIMEOUT;
+ } else {
+ if (timeo)
+ (void) fprintf(stderr,
+ "%s: timed out with incomplete data\n",
+ currenthost);
+ if (debug) {
+ printf("Received fragments:\n");
+ for (n = 0; n < numfrags; n++)
+ printf("%4d %d\n", offsets[n],
+ counts[n]);
+ if (seenlastfrag)
+ printf("last fragment received\n");
+ else
+ printf("last fragment not received\n");
+ }
+ return ERR_INCOMPLETE;
+ }
+ }
+
+ n = read(sockfd, (char *)&rpkt, sizeof(rpkt));
+ if (n == -1) {
+ warning("read", "", "");
+ return -1;
+ }
+
+ if (debug >= 4) {
+ int len = n, first = 8;
+ char *data = (char *)&rpkt;
+
+ printf("Packet data:\n");
+ while (len-- > 0) {
+ if (first-- == 0) {
+ printf("\n");
+ first = 7;
+ }
+ printf(" %02x", *data++ & 0xff);
+ }
+ printf("\n");
+ }
+
+ /*
+ * Check for format errors. Bug proofing.
+ */
+ if (n < CTL_HEADER_LEN) {
+ if (debug)
+ printf("Short (%d byte) packet received\n", n);
+ goto again;
+ }
+ if (PKT_VERSION(rpkt.li_vn_mode) > NTP_VERSION
+ || PKT_VERSION(rpkt.li_vn_mode) <= NTP_OLDVERSION) {
+ if (debug)
+ printf("Packet received with version %d\n",
+ PKT_VERSION(rpkt.li_vn_mode));
+ goto again;
+ }
+ if (PKT_MODE(rpkt.li_vn_mode) != MODE_CONTROL) {
+ if (debug)
+ printf("Packet received with mode %d\n",
+ PKT_MODE(rpkt.li_vn_mode));
+ goto again;
+ }
+ if (!CTL_ISRESPONSE(rpkt.r_m_e_op)) {
+ if (debug)
+ printf("Received request packet, wanted response\n");
+ goto again;
+ }
+
+ /*
+ * Check opcode and sequence number for a match.
+ * Could be old data getting to us.
+ */
+ if (ntohs(rpkt.sequence) != sequence) {
+ if (debug)
+ printf(
+ "Received sequnce number %d, wanted %d\n",
+ ntohs(rpkt.sequence), sequence);
+ goto again;
+ }
+ if (CTL_OP(rpkt.r_m_e_op) != opcode) {
+ if (debug)
+ printf(
+ "Received opcode %d, wanted %d (sequence number okay)\n",
+ CTL_OP(rpkt.r_m_e_op), opcode);
+ goto again;
+ }
+
+ /*
+ * Check the error code. If non-zero, return it.
+ */
+ if (CTL_ISERROR(rpkt.r_m_e_op)) {
+ int errcode;
+
+ errcode = (ntohs(rpkt.status) >> 8) & 0xff;
+ if (debug && CTL_ISMORE(rpkt.r_m_e_op)) {
+ printf("Error code %d received on not-final packet\n",
+ errcode);
+ }
+ if (errcode == CERR_UNSPEC)
+ return ERR_UNSPEC;
+ return errcode;
+ }
+
+ /*
+ * Check the association ID to make sure it matches what
+ * we sent.
+ */
+ if (ntohs(rpkt.associd) != associd) {
+ if (debug)
+ printf("Association ID %d doesn't match expected %d\n",
+ ntohs(rpkt.associd), associd);
+ /*
+ * Hack for silly fuzzballs which, at the time of writing,
+ * return an assID of sys.peer when queried for system variables.
+ */
+#ifdef notdef
+ goto again;
+#endif
+ }
+
+ /*
+ * Collect offset and count. Make sure they make sense.
+ */
+ offset = ntohs(rpkt.offset);
+ count = ntohs(rpkt.count);
+
+ if (debug >= 3) {
+ int shouldbesize;
+ u_long key;
+ u_long *lpkt;
+ int maclen;
+
+ /*
+ * Usually we ignore authentication, but for debugging purposes
+ * we watch it here.
+ */
+ shouldbesize = CTL_HEADER_LEN + count;
+
+ /* round to 8 octet boundary */
+ shouldbesize = (shouldbesize + 7) & ~7;
+
+ if (n & 0x3) {
+ printf("Packet not padded, size = %d\n", n);
+ } if ((maclen = n - shouldbesize) >= MIN_MAC_LEN) {
+ printf(
+"Packet shows signs of authentication (total %d, data %d, mac %d)\n",
+ n, shouldbesize, maclen);
+ lpkt = (u_long *)&rpkt;
+ printf("%08lx %08lx %08lx %08lx %08lx %08lx\n",
+ (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_long) - 3]),
+ (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_long) - 2]),
+ (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_long) - 1]),
+ (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_long)]),
+ (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_long) + 1]),
+ (u_long)ntohl(lpkt[(n - maclen)/sizeof(u_long) + 2]));
+ key = ntohl(lpkt[(n - maclen) / sizeof(u_long)]);
+ printf("Authenticated with keyid %lu\n", key);
+ if (key != 0 && key != info_auth_keyid) {
+ printf("We don't know that key\n");
+ } else {
+ if (authdecrypt(key, (U_LONG *)&rpkt,
+ (n - maclen))) {
+ printf("Auth okay!\n");
+ } else {
+ printf("Auth failed!\n");
+ }
+ }
+ }
+ }
+
+ if (debug >= 2)
+ printf("Got packet, size = %d\n", n);
+ if (count > (u_short)(n-CTL_HEADER_LEN)) {
+ if (debug)
+ printf(
+ "Received count of %d octets, data in packet is %d\n",
+ count, n-CTL_HEADER_LEN);
+ goto again;
+ }
+ if (count == 0 && CTL_ISMORE(rpkt.r_m_e_op)) {
+ if (debug)
+ printf("Received count of 0 in non-final fragment\n");
+ goto again;
+ }
+ if (offset + count > sizeof(pktdata)) {
+ if (debug)
+ printf("Offset %d, count %d, too big for buffer\n",
+ offset, count);
+ return ERR_TOOMUCH;
+ }
+ if (seenlastfrag && !CTL_ISMORE(rpkt.r_m_e_op)) {
+ if (debug)
+ printf("Received second last fragment packet\n");
+ goto again;
+ }
+
+ /*
+ * So far, so good. Record this fragment, making sure it doesn't
+ * overlap anything.
+ */
+ if (debug >= 2)
+ printf("Packet okay\n");;
+
+ if (numfrags == MAXFRAGS) {
+ if (debug)
+ printf("Number of fragments exceeds maximum\n");
+ return ERR_TOOMUCH;
+ }
+
+ for (n = 0; n < numfrags; n++) {
+ if (offset == offsets[n])
+ goto again; /* duplicate */
+ if (offset < offsets[n])
+ break;
+ }
+
+ if ((u_short)(n > 0 && offsets[n-1] + counts[n-1]) > offset)
+ goto overlap;
+ if (n < numfrags && (u_short)(offset + count) > offsets[n])
+ goto overlap;
+
+ {
+ register int i;
+
+ for (i = numfrags; i > n; i--) {
+ offsets[i] = offsets[i-1];
+ counts[i] = counts[i-1];
+ }
+ }
+ offsets[n] = offset;
+ counts[n] = count;
+ numfrags++;
+
+ /*
+ * Got that stuffed in right. Figure out if this was the last.
+ * Record status info out of the last packet.
+ */
+ if (!CTL_ISMORE(rpkt.r_m_e_op)) {
+ seenlastfrag = 1;
+ if (rstatus != 0)
+ *rstatus = ntohs(rpkt.status);
+ }
+
+ /*
+ * Copy the data into the data buffer.
+ */
+ memmove((char *)pktdata + offset, (char *)rpkt.data, count);
+
+ /*
+ * If we've seen the last fragment, look for holes in the sequence.
+ * If there aren't any, we're done.
+ */
+ if (seenlastfrag && offsets[0] == 0) {
+ for (n = 1; n < numfrags; n++) {
+ if (offsets[n-1] + counts[n-1] != offsets[n])
+ break;
+ }
+ if (n == numfrags) {
+ *rsize = offsets[numfrags-1] + counts[numfrags-1];
+ return 0;
+ }
+ }
+ goto again;
+
+overlap:
+ /*
+ * Print debugging message about overlapping fragments
+ */
+ if (debug)
+ printf("Overlapping fragments returned in response\n");
+ goto again;
+}
+
+
+/*
+ * sendrequest - format and send a request packet
+ */
+static int
+sendrequest(opcode, associd, auth, qsize, qdata)
+ int opcode;
+ int associd;
+ int auth;
+ int qsize;
+ char *qdata;
+{
+ struct ntp_control qpkt;
+ int pktsize;
+
+ /*
+ * Check to make sure the data will fit in one packet
+ */
+ if (qsize > CTL_MAX_DATA_LEN) {
+ (void) fprintf(stderr,
+ "***Internal error! qsize (%d) too large\n",
+ qsize);
+ return 1;
+ }
+
+ /*
+ * Fill in the packet
+ */
+ qpkt.li_vn_mode = PKT_LI_VN_MODE(0, pktversion, MODE_CONTROL);
+ qpkt.r_m_e_op = (u_char)opcode & CTL_OP_MASK;
+ qpkt.sequence = htons(sequence);
+ qpkt.status = 0;
+ qpkt.associd = htons((u_short)associd);
+ qpkt.offset = 0;
+ qpkt.count = htons((u_short)qsize);
+
+ /*
+ * If we have data, copy it in and pad it out to a 64
+ * bit boundary.
+ */
+ if (qsize > 0) {
+ memmove((char *)qpkt.data, qdata, qsize);
+ pktsize = qsize + CTL_HEADER_LEN;
+ while (pktsize & (sizeof(u_long) - 1)) {
+ qpkt.data[qsize++] = 0;
+ pktsize++;
+ }
+ } else {
+ pktsize = CTL_HEADER_LEN;
+ }
+
+ /*
+ * If it isn't authenticated we can just send it. Otherwise
+ * we're going to have to think about it a little.
+ */
+ if (!auth && !always_auth) {
+ return sendpkt((char *)&qpkt, pktsize);
+ } else {
+ char *pass;
+
+ /*
+ * Pad out packet to a multiple of 8 octets to be sure
+ * receiver can handle it.
+ */
+ while (pktsize & 7) {
+ qpkt.data[qsize++] = 0;
+ pktsize++;
+ }
+
+ /*
+ * Get the keyid and the password if we don't have one.
+ */
+ if (info_auth_keyid == -1) {
+ info_auth_keyid = getkeyid("Keyid: ");
+ if (info_auth_keyid == -1) {
+ (void) fprintf(stderr,
+ "Keyid must be defined, request not sent\n");
+ return 1;
+ }
+ }
+ if (!auth_havekey(info_auth_keyid)) {
+ pass = getpass("Password: ");
+ if (*pass != '\0')
+ authusekey(info_auth_keyid,
+ info_auth_keytype, pass);
+ }
+ if (auth_havekey(info_auth_keyid)) {
+ int maclen;
+
+ /*
+ * Stick the keyid in the packet where
+ * cp currently points. Cp should be aligned
+ * properly. Then do the encryptions.
+ */
+ *(u_long *)(&qpkt.data[qsize]) = htonl(info_auth_keyid);
+ maclen = authencrypt(info_auth_keyid, (U_LONG *)&qpkt,
+ pktsize);
+ return sendpkt((char *)&qpkt, pktsize + maclen);
+ } else {
+ (void) fprintf(stderr,
+ "No password, request not sent\n");
+ return 1;
+ }
+ }
+ /*NOTREACHED*/
+}
+
+
+/*
+ * doquery - send a request and process the response
+ */
+int
+doquery(opcode, associd, auth, qsize, qdata, rstatus, rsize, rdata)
+ int opcode;
+ int associd;
+ int auth;
+ int qsize;
+ char *qdata;
+ u_short *rstatus;
+ int *rsize;
+ char **rdata;
+{
+ int res;
+ int done;
+
+ /*
+ * Check to make sure host is open
+ */
+ if (!havehost) {
+ (void) fprintf(stderr, "***No host open, use `host' command\n");
+ return -1;
+ }
+
+ done = 0;
+ sequence++;
+
+again:
+ /*
+ * send a request
+ */
+ res = sendrequest(opcode, associd, auth, qsize, qdata);
+ if (res != 0)
+ return res;
+
+ /*
+ * Get the response. If we got a standard error, print a message
+ */
+ res = getresponse(opcode, associd, rstatus, rsize, rdata, done);
+
+ if (res > 0) {
+ if (!done && (res == ERR_TIMEOUT || res == ERR_INCOMPLETE)) {
+ if (res == ERR_INCOMPLETE) {
+ /*
+ * better bump the sequence so we don't
+ * get confused about differing fragments.
+ */
+ sequence++;
+ }
+ done = 1;
+ goto again;
+ }
+ switch(res) {
+ case CERR_BADFMT:
+ (void) fprintf(stderr,
+ "***Server reports a bad format request packet\n");
+ break;
+ case CERR_PERMISSION:
+ (void) fprintf(stderr,
+ "***Server disallowed request (authentication?)\n");
+ break;
+ case CERR_BADOP:
+ (void) fprintf(stderr,
+ "***Server reports a bad opcode in request\n");
+ break;
+ case CERR_BADASSOC:
+ (void) fprintf(stderr,
+ "***Association ID %d unknown to server\n",associd);
+ break;
+ case CERR_UNKNOWNVAR:
+ (void) fprintf(stderr,
+ "***A request variable was unknown to the server\n");
+ break;
+ case CERR_BADVALUE:
+ (void) fprintf(stderr,
+ "***Server indicates a request variable was bad\n");
+ break;
+ case ERR_UNSPEC:
+ (void) fprintf(stderr,
+ "***Server returned an unspecified error\n");
+ break;
+ case ERR_TIMEOUT:
+ (void) fprintf(stderr, "***Request timed out\n");
+ break;
+ case ERR_INCOMPLETE:
+ (void) fprintf(stderr,
+ "***Response from server was incomplete\n");
+ break;
+ case ERR_TOOMUCH:
+ (void) fprintf(stderr,
+ "***Buffer size exceeded for returned data\n");
+ break;
+ default:
+ (void) fprintf(stderr,
+ "***Server returns unknown error code %d\n", res);
+ break;
+ }
+ }
+ return res;
+}
+
+
+/*
+ * getcmds - read commands from the standard input and execute them
+ */
+static void
+getcmds()
+{
+ char line[MAXLINE];
+
+ for (;;) {
+ if (interactive) {
+ (void) fputs(prompt, stderr);
+ (void) fflush(stderr);
+ }
+
+ if (fgets(line, sizeof line, stdin) == NULL)
+ return;
+
+ docmd(line);
+ }
+}
+
+
+/*
+ * abortcmd - catch interrupts and abort the current command
+ */
+static RETSIGTYPE
+abortcmd(sig)
+int sig;
+{
+ if (current_output == stdout)
+ (void) fflush(stdout);
+ putc('\n', stderr);
+ (void) fflush(stderr);
+ if (jump) longjmp(interrupt_buf, 1);
+}
+
+
+/*
+ * docmd - decode the command line and execute a command
+ */
+static void
+docmd(cmdline)
+ char *cmdline;
+{
+ char *tokens[1+MAXARGS+2];
+ struct parse pcmd;
+ int ntok;
+ static int i;
+ struct xcmd *xcmd;
+
+ /*
+ * Tokenize the command line. If nothing on it, return.
+ */
+ tokenize(cmdline, tokens, &ntok);
+ if (ntok == 0)
+ return;
+
+ /*
+ * Find the appropriate command description.
+ */
+ i = findcmd(tokens[0], builtins, opcmds, &xcmd);
+ if (i == 0) {
+ (void) fprintf(stderr, "***Command `%s' unknown\n",
+ tokens[0]);
+ return;
+ } else if (i >= 2) {
+ (void) fprintf(stderr, "***Command `%s' ambiguous\n",
+ tokens[0]);
+ return;
+ }
+
+ /*
+ * Save the keyword, then walk through the arguments, interpreting
+ * as we go.
+ */
+ pcmd.keyword = tokens[0];
+ pcmd.nargs = 0;
+ for (i = 0; i < MAXARGS && xcmd->arg[i] != NO; i++) {
+ if ((i+1) >= ntok) {
+ if (!(xcmd->arg[i] & OPT)) {
+ printusage(xcmd, stderr);
+ return;
+ }
+ break;
+ }
+ if ((xcmd->arg[i] & OPT) && (*tokens[i+1] == '>'))
+ break;
+ if (!getarg(tokens[i+1], (int)xcmd->arg[i], &pcmd.argval[i]))
+ return;
+ pcmd.nargs++;
+ }
+
+ i++;
+ if (i < ntok && *tokens[i] == '>') {
+ char *fname;
+
+ if (*(tokens[i]+1) != '\0')
+ fname = tokens[i]+1;
+ else if ((i+1) < ntok)
+ fname = tokens[i+1];
+ else {
+ (void) fprintf(stderr, "***No file for redirect\n");
+ return;
+ }
+
+ current_output = fopen(fname, "w");
+ if (current_output == NULL) {
+ (void) fprintf(stderr, "***Error opening %s: ", fname);
+ perror("");
+ return;
+ }
+ i = 1; /* flag we need a close */
+ } else {
+ current_output = stdout;
+ i = 0; /* flag no close */
+ }
+
+ if (interactive && setjmp(interrupt_buf)) {
+ return;
+ } else {
+ jump++;
+ (xcmd->handler)(&pcmd, current_output);
+ if (i) (void) fclose(current_output);
+ }
+}
+
+
+/*
+ * tokenize - turn a command line into tokens
+ */
+static void
+tokenize(line, tokens, ntok)
+ char *line;
+ char **tokens;
+ int *ntok;
+{
+ register char *cp;
+ register char *sp;
+ static char tspace[MAXLINE];
+
+ sp = tspace;
+ cp = line;
+ for (*ntok = 0; *ntok < MAXTOKENS; (*ntok)++) {
+ tokens[*ntok] = sp;
+ while (ISSPACE(*cp))
+ cp++;
+ if (ISEOL(*cp))
+ break;
+ do {
+ *sp++ = *cp++;
+ } while (!ISSPACE(*cp) && !ISEOL(*cp));
+
+ *sp++ = '\0';
+ }
+}
+
+
+
+/*
+ * findcmd - find a command in a command description table
+ */
+static int
+findcmd(str, clist1, clist2, cmd)
+ register char *str;
+ struct xcmd *clist1;
+ struct xcmd *clist2;
+ struct xcmd **cmd;
+{
+ register struct xcmd *cl;
+ register int clen;
+ int nmatch;
+ struct xcmd *nearmatch = NULL;
+ struct xcmd *clist;
+
+ clen = strlen(str);
+ nmatch = 0;
+ if (clist1 != 0)
+ clist = clist1;
+ else if (clist2 != 0)
+ clist = clist2;
+ else
+ return 0;
+
+again:
+ for (cl = clist; cl->keyword != 0; cl++) {
+ /* do a first character check, for efficiency */
+ if (*str != *(cl->keyword))
+ continue;
+ if (strncmp(str, cl->keyword, clen) == 0) {
+ /*
+ * Could be extact match, could be approximate.
+ * Is exact if the length of the keyword is the
+ * same as the str.
+ */
+ if (*((cl->keyword) + clen) == '\0') {
+ *cmd = cl;
+ return 1;
+ }
+ nmatch++;
+ nearmatch = cl;
+ }
+ }
+
+ /*
+ * See if there is more to do. If so, go again. Sorry about the
+ * goto, too much looking at BSD sources...
+ */
+ if (clist == clist1 && clist2 != 0) {
+ clist = clist2;
+ goto again;
+ }
+
+ /*
+ * If we got extactly 1 near match, use it, else return number
+ * of matches.
+ */
+ if (nmatch == 1) {
+ *cmd = nearmatch;
+ return 1;
+ }
+ return nmatch;
+}
+
+
+/*
+ * getarg - interpret an argument token
+ */
+static int
+getarg(str, code, argp)
+ char *str;
+ int code;
+ arg_v *argp;
+{
+ int isneg;
+ char *cp, *np;
+ static char *digits = "0123456789";
+
+ switch (code & ~OPT) {
+ case STR:
+ argp->string = str;
+ break;
+ case ADD:
+ if (!getnetnum(str, &(argp->netnum), (char *)0)) {
+ return 0;
+ }
+ break;
+ case INT:
+ case UINT:
+ isneg = 0;
+ np = str;
+ if (*np == '&') {
+ np++;
+ isneg = atoi(np);
+ if (isneg <= 0) {
+ (void) fprintf(stderr,
+ "***Association value `%s' invalid/undecodable\n", str);
+ return 0;
+ }
+ if (isneg > numassoc) {
+ (void) fprintf(stderr,
+ "***Association for `%s' unknown (max &%d)\n",
+ str, numassoc);
+ return 0;
+ }
+ argp->uval = assoc_cache[isneg-1].assid;
+ break;
+ }
+
+ if (*np == '-') {
+ np++;
+ isneg = 1;
+ }
+
+ argp->uval = 0;
+ do {
+ cp = strchr(digits, *np);
+ if (cp == NULL) {
+ (void) fprintf(stderr,
+ "***Illegal integer value %s\n", str);
+ return 0;
+ }
+ argp->uval *= 10;
+ argp->uval += (cp - digits);
+ } while (*(++np) != '\0');
+
+ if (isneg) {
+ if ((code & ~OPT) == UINT) {
+ (void) fprintf(stderr,
+ "***Value %s should be unsigned\n", str);
+ return 0;
+ }
+ argp->ival = -argp->ival;
+ }
+ break;
+ }
+
+ return 1;
+}
+
+
+/*
+ * getnetnum - given a host name, return its net number
+ * and (optional) full name
+ */
+int
+getnetnum(host, num, fullhost)
+ char *host;
+ u_long *num;
+ char *fullhost;
+{
+ struct hostent *hp;
+
+ if (decodenetnum(host, num)) {
+ if (fullhost != 0) {
+ (void) sprintf(fullhost, "%lu.%lu.%lu.%lu",
+ (u_long)((htonl(*num) >> 24) & 0xff),
+ (u_long)((htonl(*num) >> 16) & 0xff),
+ (u_long)((htonl(*num) >> 8) & 0xff),
+ (u_long)(htonl(*num) & 0xff));
+ }
+ return 1;
+ } else if ((hp = gethostbyname(host)) != 0) {
+ memmove((char *)num, hp->h_addr, sizeof(u_long));
+ if (fullhost != 0)
+ (void) strcpy(fullhost, hp->h_name);
+ return 1;
+ } else {
+ (void) fprintf(stderr, "***Can't find host %s\n", host);
+ return 0;
+ }
+ /*NOTREACHED*/
+}
+
+/*
+ * nntohost - convert network number to host name. This routine enforces
+ * the showhostnames setting.
+ */
+char *
+nntohost(netnum)
+ u_long netnum;
+{
+ if (!showhostnames)
+ return numtoa(netnum);
+ if ((ntohl(netnum) & REFCLOCK_MASK) == REFCLOCK_ADDR)
+ return refnumtoa(netnum);
+ return numtohost(netnum);
+}
+
+
+/*
+ * rtdatetolfp - decode an RT-11 date into an l_fp
+ */
+static int
+rtdatetolfp(str, lfp)
+ char *str;
+ l_fp *lfp;
+{
+ register char *cp;
+ register int i;
+ struct calendar cal;
+ char buf[4];
+ static char *months[12] = {
+ "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
+ };
+
+ cal.yearday = 0;
+
+ /*
+ * An RT-11 date looks like:
+ *
+ * d[d]-Mth-y[y] hh:mm:ss
+ */
+ cp = str;
+ if (!isdigit(*cp)) {
+ if (*cp == '-') {
+ /*
+ * Catch special case
+ */
+ L_CLR(lfp);
+ return 1;
+ }
+ return 0;
+ }
+
+ cal.monthday = *cp++ - '0'; /* ascii dependent */
+ if (isdigit(*cp)) {
+ cal.monthday = (cal.monthday << 3) + (cal.monthday << 1);
+ cal.monthday += *cp++ - '0';
+ }
+
+ if (*cp++ != '-')
+ return 0;
+
+ for (i = 0; i < 3; i++)
+ buf[i] = *cp++;
+ buf[3] = '\0';
+
+ for (i = 0; i < 12; i++)
+ if (STREQ(buf, months[i]))
+ break;
+ if (i == 12)
+ return 0;
+ cal.month = i + 1;
+
+ if (*cp++ != '-')
+ return 0;
+
+ if (!isdigit(*cp))
+ return 0;
+ cal.year = *cp++ - '0';
+ if (isdigit(*cp)) {
+ cal.year = (cal.year << 3) + (cal.year << 1);
+ cal.year += *cp++ - '0';
+ }
+
+ /*
+ * Catch special case. If cal.year == 0 this is a zero timestamp.
+ */
+ if (cal.year == 0) {
+ L_CLR(lfp);
+ return 1;
+ }
+
+ if (*cp++ != ' ' || !isdigit(*cp))
+ return 0;
+ cal.hour = *cp++ - '0';
+ if (isdigit(*cp)) {
+ cal.hour = (cal.hour << 3) + (cal.hour << 1);
+ cal.hour += *cp++ - '0';
+ }
+
+ if (*cp++ != ':' || !isdigit(*cp))
+ return 0;
+ cal.minute = *cp++ - '0';
+ if (isdigit(*cp)) {
+ cal.minute = (cal.minute << 3) + (cal.minute << 1);
+ cal.minute += *cp++ - '0';
+ }
+
+ if (*cp++ != ':' || !isdigit(*cp))
+ return 0;
+ cal.second = *cp++ - '0';
+ if (isdigit(*cp)) {
+ cal.second = (cal.second << 3) + (cal.second << 1);
+ cal.second += *cp++ - '0';
+ }
+
+ cal.year += 1900;
+ lfp->l_ui = caltontp(&cal);
+ lfp->l_uf = 0;
+ return 1;
+}
+
+
+/*
+ * decodets - decode a timestamp into an l_fp format number, with
+ * consideration of fuzzball formats.
+ */
+int
+decodets(str, lfp)
+ char *str;
+ l_fp *lfp;
+{
+ /*
+ * If it starts with a 0x, decode as hex.
+ */
+ if (*str == '0' && (*(str+1) == 'x' || *(str+1) == 'X'))
+ return hextolfp(str+2, lfp);
+
+ /*
+ * If it starts with a '"', try it as an RT-11 date.
+ */
+ if (*str == '"') {
+ register char *cp = str+1;
+ register char *bp;
+ char buf[30];
+
+ bp = buf;
+ while (*cp != '"' && *cp != '\0' && bp < &buf[29])
+ *bp++ = *cp++;
+ *bp = '\0';
+ return rtdatetolfp(buf, lfp);
+ }
+
+ /*
+ * Might still be hex. Check out the first character. Talk
+ * about heuristics!
+ */
+ if ((*str >= 'A' && *str <= 'F') || (*str >= 'a' && *str <= 'f'))
+ return hextolfp(str, lfp);
+
+ /*
+ * Try it as a decimal. If this fails, try as an unquoted
+ * RT-11 date. This code should go away eventually.
+ */
+ if (atolfp(str, lfp))
+ return 1;
+ return rtdatetolfp(str, lfp);
+}
+
+
+/*
+ * decodetime - decode a time value. It should be in milliseconds
+ */
+int
+decodetime(str, lfp)
+ char *str;
+ l_fp *lfp;
+{
+ return mstolfp(str, lfp);
+}
+
+
+/*
+ * decodeint - decode an integer
+ */
+int
+decodeint(str, val)
+ char *str;
+ long *val;
+{
+ if (*str == '0') {
+ if (*(str+1) == 'x' || *(str+1) == 'X')
+ return hextoint(str+2, (u_long *)&val);
+ return octtoint(str, (u_long *)&val);
+ }
+ return atoint(str, val);
+}
+
+
+/*
+ * decodeuint - decode an unsigned integer
+ */
+int
+decodeuint(str, val)
+ char *str;
+ u_long *val;
+{
+ if (*str == '0') {
+ if (*(str+1) == 'x' || *(str+1) == 'X')
+ return hextoint(str+2, val);
+ return octtoint(str, val);
+ }
+ return atouint(str, val);
+}
+
+
+/*
+ * decodearr - decode an array of time values
+ */
+static int
+decodearr(str, narr, lfparr)
+ char *str;
+ int *narr;
+ l_fp *lfparr;
+{
+ register char *cp, *bp;
+ register l_fp *lfp;
+ char buf[60];
+
+ lfp = lfparr;
+ cp = str;
+ *narr = 0;
+
+ while (*narr < 8) {
+ while (isspace(*cp))
+ cp++;
+ if (*cp == '\0')
+ break;
+
+ bp = buf;
+ while (!isspace(*cp) && *cp != '\0')
+ *bp++ = *cp++;
+ *bp++ = '\0';
+
+ if (!decodetime(buf, lfp))
+ return 0;
+ (*narr)++;
+ lfp++;
+ }
+ return 1;
+}
+
+
+
+
+/*
+ * getcode - return string corresponding to code
+ */
+static char *
+getcode(code, codetab)
+ int code;
+ struct codestring *codetab;
+{
+ static char buf[30];
+
+ while (codetab->code != -1) {
+ if (codetab->code == code)
+ return codetab->string;
+ codetab++;
+ }
+ (void) sprintf(buf, "%s_%d", codetab->string, code);
+ return buf;
+}
+
+
+/*
+ * Finally, the built in command handlers
+ */
+
+/*
+ * help - tell about commands, or details of a particular command
+ */
+static void
+help(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ int i;
+ int n;
+ struct xcmd *xcp;
+ char *cmd;
+ char *cmdsort[100];
+ int length[100];
+ int maxlength;
+ int numperline;
+ static char *spaces = " "; /* 20 spaces */
+
+ if (pcmd->nargs == 0) {
+ n = 0;
+ for (xcp = builtins; xcp->keyword != 0; xcp++) {
+ if (*(xcp->keyword) != '?')
+ cmdsort[n++] = xcp->keyword;
+ }
+ for (xcp = opcmds; xcp->keyword != 0; xcp++)
+ cmdsort[n++] = xcp->keyword;
+
+#if defined(sgi) || defined(SYS_BSDI) || defined(__STDC__)
+ qsort((void *)cmdsort, n, sizeof(char *), helpsort);
+#else
+ qsort((char *)cmdsort, n, sizeof(char *), helpsort);
+#endif /* sgi || bsdi */
+
+ maxlength = 0;
+ for (i = 0; i < n; i++) {
+ length[i] = strlen(cmdsort[i]);
+ if (length[i] > maxlength)
+ maxlength = length[i];
+ }
+ maxlength++;
+ numperline = 76 / maxlength;
+
+ (void) fprintf(fp, "Commands available:\n");
+ for (i = 0; i < n; i++) {
+ if ((i % numperline) == (numperline-1)
+ || i == (n-1))
+ (void) fprintf(fp, "%s\n", cmdsort[i]);
+ else
+ (void) fprintf(fp, "%s%s", cmdsort[i],
+ spaces+20-maxlength+length[i]);
+ }
+ } else {
+ cmd = pcmd->argval[0].string;
+ n = findcmd(cmd, builtins, opcmds, &xcp);
+ if (n == 0) {
+ (void) fprintf(stderr,
+ "Command `%s' is unknown\n", cmd);
+ return;
+ } else if (n >= 2) {
+ (void) fprintf(stderr,
+ "Command `%s' is ambiguous\n", cmd);
+ return;
+ }
+ (void) fprintf(fp, "function: %s\n", xcp->comment);
+ printusage(xcp, fp);
+ }
+}
+
+
+/*
+ * helpsort - do hostname qsort comparisons
+ */
+static int
+#if defined(sgi) || defined(SYS_BSDI) || defined(__STDC__)
+helpsort(t1, t2)
+ const void *t1;
+ const void *t2;
+{
+ const char **name1 = (const char **)t1;
+ const char **name2 = (const char **)t2;
+#else
+helpsort(name1, name2)
+ char **name1;
+ char **name2;
+{
+#endif /* sgi || bsdi */
+ return strcmp(*name1, *name2);
+}
+
+
+/*
+ * printusage - print usage information for a command
+ */
+static void
+printusage(xcp, fp)
+ struct xcmd *xcp;
+ FILE *fp;
+{
+ register int i;
+
+ (void) fprintf(fp, "usage: %s", xcp->keyword);
+ for (i = 0; i < MAXARGS && xcp->arg[i] != NO; i++) {
+ if (xcp->arg[i] & OPT)
+ (void) fprintf(fp, " [ %s ]", xcp->desc[i]);
+ else
+ (void) fprintf(fp, " %s", xcp->desc[i]);
+ }
+ (void) fprintf(fp, "\n");
+}
+
+
+/*
+ * timeout - set time out time
+ */
+static void
+timeout(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ int val;
+
+ if (pcmd->nargs == 0) {
+ val = tvout.tv_sec * 1000 + tvout.tv_usec / 1000;
+ (void) fprintf(fp, "primary timeout %d ms\n", val);
+ } else {
+ tvout.tv_sec = pcmd->argval[0].uval / 1000;
+ tvout.tv_usec = (pcmd->argval[0].uval - (tvout.tv_sec * 1000))
+ * 1000;
+ }
+}
+
+
+/*
+ * delay - set delay for auth requests
+ */
+static void
+delay(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ int isneg;
+ u_long val;
+
+ if (pcmd->nargs == 0) {
+ val = delay_time.l_ui * 1000 + delay_time.l_uf / 4294967;
+ (void) fprintf(fp, "delay %lu ms\n", val);
+ } else {
+ if (pcmd->argval[0].ival < 0) {
+ isneg = 1;
+ val = (u_long)(-pcmd->argval[0].ival);
+ } else {
+ isneg = 0;
+ val = (u_long)pcmd->argval[0].ival;
+ }
+
+ delay_time.l_ui = val / 1000;
+ val %= 1000;
+ delay_time.l_uf = val * 4294967; /* 2**32/1000 */
+
+ if (isneg)
+ L_NEG(&delay_time);
+ }
+}
+
+
+/*
+ * host - set the host we are dealing with.
+ */
+static void
+host(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ if (pcmd->nargs == 0) {
+ if (havehost)
+ (void) fprintf(fp, "current host is %s\n", currenthost);
+ else
+ (void) fprintf(fp, "no current host\n");
+ } else if (openhost(pcmd->argval[0].string)) {
+ (void) fprintf(fp, "current host set to %s\n", currenthost);
+ numassoc = 0;
+ } else {
+ if (havehost)
+ (void) fprintf(fp,
+ "current host remains %s\n", currenthost);
+ else
+ (void) fprintf(fp, "still no current host\n");
+ }
+}
+
+
+/*
+ * poll - do one (or more) polls of the host via NTP
+ */
+/*ARGSUSED*/
+static void
+ntp_poll(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ (void) fprintf(fp, "poll not implemented yet\n");
+}
+
+
+/*
+ * keyid - get a keyid to use for authenticating requests
+ */
+static void
+keyid(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ if (pcmd->nargs == 0) {
+ if (info_auth_keyid == -1)
+ (void) fprintf(fp, "no keyid defined\n");
+ else
+ (void) fprintf(fp, "keyid is %lu\n", info_auth_keyid);
+ } else {
+ info_auth_keyid = pcmd->argval[0].uval;
+ }
+}
+
+/*
+ * keytype - get type of key to use for authenticating requests
+ */
+static void
+keytype(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ if (pcmd->nargs == 0)
+ fprintf(fp, "keytype is %s",
+ (info_auth_keytype == KEY_TYPE_MD5) ? "md5" : "des");
+ else
+ switch (*(pcmd->argval[0].string)) {
+ case 'm':
+ case 'M':
+ info_auth_keytype = KEY_TYPE_MD5;
+ break;
+
+ case 'd':
+ case 'D':
+ info_auth_keytype = KEY_TYPE_DES;
+ break;
+
+ default:
+ fprintf(fp, "keytype must be 'md5' or 'des'\n");
+ }
+}
+
+
+
+/*
+ * passwd - get an authentication key
+ */
+/*ARGSUSED*/
+static void
+passwd(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ char *pass;
+
+ if (info_auth_keyid == -1) {
+ info_auth_keyid = getkeyid("Keyid: ");
+ if (info_auth_keyid == -1) {
+ (void)fprintf(fp, "Keyid must be defined\n");
+ return;
+ }
+ }
+ pass = getpass("Password: ");
+ if (*pass == '\0')
+ (void) fprintf(fp, "Password unchanged\n");
+ else
+ authusekey(info_auth_keyid, info_auth_keytype, pass);
+}
+
+
+/*
+ * hostnames - set the showhostnames flag
+ */
+static void
+hostnames(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ if (pcmd->nargs == 0) {
+ if (showhostnames)
+ (void) fprintf(fp, "hostnames being shown\n");
+ else
+ (void) fprintf(fp, "hostnames not being shown\n");
+ } else {
+ if (STREQ(pcmd->argval[0].string, "yes"))
+ showhostnames = 1;
+ else if (STREQ(pcmd->argval[0].string, "no"))
+ showhostnames = 0;
+ else
+ (void)fprintf(stderr, "What?\n");
+ }
+}
+
+
+
+/*
+ * setdebug - set/change debugging level
+ */
+static void
+setdebug(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ if (pcmd->nargs == 0) {
+ (void) fprintf(fp, "debug level is %d\n", debug);
+ return;
+ } else if (STREQ(pcmd->argval[0].string, "no")) {
+ debug = 0;
+ } else if (STREQ(pcmd->argval[0].string, "more")) {
+ debug++;
+ } else if (STREQ(pcmd->argval[0].string, "less")) {
+ debug--;
+ } else {
+ (void) fprintf(fp, "What?\n");
+ return;
+ }
+ (void) fprintf(fp, "debug level set to %d\n", debug);
+}
+
+
+/*
+ * quit - stop this nonsense
+ */
+/*ARGSUSED*/
+static void
+quit(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ if (havehost)
+ (void) close(sockfd); /* cleanliness next to godliness */
+ exit(0);
+}
+
+
+/*
+ * version - print the current version number
+ */
+/*ARGSUSED*/
+static void
+version(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ extern char *Version;
+
+ (void) fprintf(fp, "%s\n", Version);
+}
+
+
+/*
+ * raw - set raw mode output
+ */
+/*ARGSUSED*/
+static void
+raw(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ rawmode = 1;
+ (void) fprintf(fp, "Output set to raw\n");
+}
+
+
+/*
+ * cooked - set cooked mode output
+ */
+/*ARGSUSED*/
+static void
+cooked(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ rawmode = 0;
+ (void) fprintf(fp, "Output set to cooked\n");
+ return;
+}
+
+
+/*
+ * authenticate - always authenticate requests to this host
+ */
+static void
+authenticate(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ if (pcmd->nargs == 0) {
+ if (always_auth) {
+ (void) fprintf(fp,
+ "authenticated requests being sent\n");
+ } else
+ (void) fprintf(fp,
+ "unauthenticated requests being sent\n");
+ } else {
+ if (STREQ(pcmd->argval[0].string, "yes")) {
+ always_auth = 1;
+ } else if (STREQ(pcmd->argval[0].string, "no")) {
+ always_auth = 0;
+ } else
+ (void)fprintf(stderr, "What?\n");
+ }
+}
+
+
+/*
+ * ntpversion - choose the NTP version to use
+ */
+static void
+ntpversion(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ if (pcmd->nargs == 0) {
+ (void) fprintf(fp,
+ "NTP version being claimed is %d\n", pktversion);
+ } else {
+ if (pcmd->argval[0].uval <= NTP_OLDVERSION
+ || pcmd->argval[0].uval > NTP_VERSION) {
+ (void) fprintf(stderr, "versions %d to %d, please\n",
+ NTP_OLDVERSION+1, NTP_VERSION);
+ } else {
+ pktversion = pcmd->argval[0].uval;
+ }
+ }
+}
+
+
+/*
+ * warning - print a warning message
+ */
+static void
+warning(fmt, st1, st2)
+ char *fmt;
+ char *st1;
+ char *st2;
+{
+ (void) fprintf(stderr, "%s: ", progname);
+ (void) fprintf(stderr, fmt, st1, st2);
+ (void) fprintf(stderr, ": ");
+ perror("");
+}
+
+
+/*
+ * error - print a message and exit
+ */
+static void
+error(fmt, st1, st2)
+ char *fmt;
+ char *st1;
+ char *st2;
+{
+ warning(fmt, st1, st2);
+ exit(1);
+}
+
+/*
+ * getkeyid - prompt the user for a keyid to use
+ */
+static u_long
+getkeyid(prompt)
+char *prompt;
+{
+ register char *p;
+ register c;
+ FILE *fi;
+ char pbuf[20];
+
+ if ((fi = fdopen(open("/dev/tty", 2), "r")) == NULL)
+ fi = stdin;
+ else
+ setbuf(fi, (char *)NULL);
+ fprintf(stderr, "%s", prompt); fflush(stderr);
+ for (p=pbuf; (c = getc(fi))!='\n' && c!=EOF;) {
+ if (p < &pbuf[18])
+ *p++ = c;
+ }
+ *p = '\0';
+ if (fi != stdin)
+ fclose(fi);
+ if (strcmp(pbuf, "0") == 0)
+ return 0;
+
+ return (u_long) atoi(pbuf);
+}
+
+
+/*
+ * atoascii - printable-ize possibly ascii data using the character
+ * transformations cat -v uses.
+ */
+static void
+atoascii(length, data, outdata)
+ int length;
+ char *data;
+ char *outdata;
+{
+ register u_char *cp;
+ register u_char *ocp;
+ register u_char c;
+
+ if (!data)
+ {
+ *outdata = '\0';
+ return;
+ }
+
+ ocp = (u_char *)outdata;
+ for (cp = (u_char *)data; cp < (u_char *)data + length; cp++) {
+ c = *cp;
+ if (c == '\0')
+ break;
+ if (c == '\0')
+ break;
+ if (c > 0177) {
+ *ocp++ = 'M';
+ *ocp++ = '-';
+ c &= 0177;
+ }
+
+ if (c < ' ') {
+ *ocp++ = '^';
+ *ocp++ = c + '@';
+ } else if (c == 0177) {
+ *ocp++ = '^';
+ *ocp++ = '?';
+ } else {
+ *ocp++ = c;
+ }
+ if (ocp >= ((u_char *)outdata + length - 4))
+ break;
+ }
+ *ocp++ = '\0';
+}
+
+
+
+/*
+ * makeascii - print possibly ascii data using the character
+ * transformations that cat -v uses.
+ */
+static void
+makeascii(length, data, fp)
+ int length;
+ char *data;
+ FILE *fp;
+{
+ register u_char *cp;
+ register int c;
+
+ for (cp = (u_char *)data; cp < (u_char *)data + length; cp++) {
+ c = (int)*cp;
+ if (c > 0177) {
+ putc('M', fp);
+ putc('-', fp);
+ c &= 0177;
+ }
+
+ if (c < ' ') {
+ putc('^', fp);
+ putc(c+'@', fp);
+ } else if (c == 0177) {
+ putc('^', fp);
+ putc('?', fp);
+ } else {
+ putc(c, fp);
+ }
+ }
+}
+
+
+/*
+ * asciize - same thing as makeascii except add a newline
+ */
+void
+asciize(length, data, fp)
+ int length;
+ char *data;
+ FILE *fp;
+{
+ makeascii(length, data, fp);
+ putc('\n', fp);
+}
+
+
+/*
+ * Some circular buffer space
+ */
+#define CBLEN 80
+#define NUMCB 6
+
+char circ_buf[NUMCB][CBLEN];
+int nextcb = 0;
+
+
+/*
+ * getevents - return a descriptive string for the event count
+ */
+static char *
+getevents(cnt)
+ int cnt;
+{
+ static char buf[20];
+
+ if (cnt == 0)
+ return "no events";
+ (void) sprintf(buf, "%d event%s", cnt, (cnt==1) ? "" : "s");
+ return buf;
+}
+
+
+/*
+ * statustoa - return a descriptive string for a peer status
+ */
+static char *
+statustoa(type, st)
+ int type;
+ int st;
+{
+ char *cb;
+ u_char pst;
+
+ cb = &circ_buf[nextcb][0];
+ if (++nextcb >= NUMCB)
+ nextcb = 0;
+
+ switch (type) {
+ case TYPE_SYS:
+ (void)strcpy(cb, getcode(CTL_SYS_LI(st), leap_codes));
+ (void)strcat(cb, ", ");
+ (void)strcat(cb, getcode(CTL_SYS_SOURCE(st) & ~CTL_SST_TS_PPS, sync_codes));
+ if (CTL_SYS_SOURCE(st) & CTL_SST_TS_PPS)
+ (void)strcat(cb, "/PPS");
+ (void)strcat(cb, ", ");
+ (void)strcat(cb, getevents(CTL_SYS_NEVNT(st)));
+ (void)strcat(cb, ", ");
+ (void)strcat(cb, getcode(CTL_SYS_EVENT(st), sys_codes));
+ break;
+
+ case TYPE_PEER:
+ /*
+ * Handcraft the bits
+ */
+ pst = CTL_PEER_STATVAL(st);
+ if (!(pst & CTL_PST_REACH)) {
+ (void)strcpy(cb, "unreach");
+ } else {
+ (void)strcpy(cb, "reach");
+#if 0
+ if (!(pst & CTL_PST_DISP)) {
+ (void)strcat(cb, ", hi_disp");
+ } else {
+ if (pst & CTL_PST_SANE) {
+ if ((pst & 0x3) == CTL_PST_SEL_REJECT)
+ (void)strcat(cb, ", sane");
+ } else {
+ (void)strcat(cb, ", insane");
+ }
+ }
+#endif
+ }
+ if (pst & CTL_PST_CONFIG)
+ (void)strcat(cb, ", conf");
+ if (pst & CTL_PST_AUTHENABLE) {
+ if (!(pst & CTL_PST_REACH) || (pst & CTL_PST_AUTHENTIC))
+ (void)strcat(cb, ", auth");
+ else
+ (void)strcat(cb, ", unauth");
+ }
+
+ /*
+ * Now the codes
+ */
+ if ((pst & 0x7) != CTL_PST_SEL_REJECT) {
+ (void)strcat(cb, ", ");
+ (void)strcat(cb, getcode(pst & 0x7, select_codes));
+ }
+ (void)strcat(cb, ", ");
+ (void)strcat(cb, getevents(CTL_PEER_NEVNT(st)));
+ if (CTL_PEER_EVENT(st) != EVNT_UNSPEC) {
+ (void)strcat(cb, ", ");
+ (void)strcat(cb, getcode(CTL_PEER_EVENT(st),
+ peer_codes));
+ }
+ break;
+
+ case TYPE_CLOCK:
+ (void)strcpy(cb, getcode(((st)>>8) & 0xff, clock_codes));
+ (void)strcat(cb, ", last_");
+ (void)strcat(cb, getcode((st) & 0xff, clock_codes));
+ break;
+ }
+ return cb;
+}
+
+
+/*
+ * nextvar - find the next variable in the buffer
+ */
+int
+nextvar(datalen, datap, vname, vvalue)
+ int *datalen;
+ char **datap;
+ char **vname;
+ char **vvalue;
+{
+ register char *cp;
+ register char *np;
+ register char *cpend;
+ int quoted = 0;
+ static char name[MAXVARLEN];
+ static char value[MAXVALLEN];
+
+ cp = *datap;
+ cpend = cp + *datalen;
+
+ /*
+ * Space past commas and white space
+ */
+ while (cp < cpend && (*cp == ',' || isspace(*cp)))
+ cp++;
+ if (cp == cpend)
+ return 0;
+
+ /*
+ * Copy name until we hit a ',', an '=', a '\r' or a '\n'. Backspace
+ * over any white space and terminate it.
+ */
+ np = name;
+ while (cp < cpend && *cp != ',' && *cp != '='
+ && *cp != '\r' && *cp != '\n')
+ *np++ = *cp++;
+ while (isspace(*(np-1)))
+ np--;
+ *np = '\0';
+ *vname = name;
+
+ /*
+ * Check if we hit the end of the buffer or a ','. If so we are done.
+ */
+ if (cp == cpend || *cp == ',' || *cp == '\r' || *cp == '\n') {
+ if (cp != cpend)
+ cp++;
+ *datap = cp;
+ *datalen = cpend - cp;
+ *vvalue = (char *)0;
+ return 1;
+ }
+
+ /*
+ * So far, so good. Copy out the value
+ */
+ cp++; /* past '=' */
+ while (cp < cpend && (isspace(*cp) && *cp != '\r' && *cp != '\n'))
+ cp++;
+ np = value;
+ while (cp < cpend && ((*cp != ',') || quoted))
+ {
+ quoted ^= ((*np++ = *cp++) == '"');
+ }
+
+ while (np > value && isspace(*(np-1)))
+ np--;
+ *np = '\0';
+
+ /*
+ * Return this. All done.
+ */
+ if (cp != cpend)
+ cp++;
+ *datap = cp;
+ *datalen = cpend - cp;
+ *vvalue = value;
+ return 1;
+}
+
+
+/*
+ * findvar - see if this variable is known to us
+ */
+int
+findvar(varname, varlist)
+ char *varname;
+ struct ctl_var *varlist;
+{
+ register char *np;
+ register struct ctl_var *vl;
+
+ vl = varlist;
+ np = varname;
+ while (vl->fmt != EOV) {
+ if (vl->fmt != PADDING && STREQ(np, vl->text))
+ return vl->code;
+ vl++;
+ }
+ return 0;
+}
+
+
+
+/*
+ * printvars - print variables returned in response packet
+ */
+void
+printvars(length, data, status, sttype, fp)
+ int length;
+ char *data;
+ int status;
+ int sttype;
+ FILE *fp;
+{
+ if (rawmode)
+ rawprint(sttype, length, data, status, fp);
+ else
+ cookedprint(sttype, length, data, status, fp);
+}
+
+
+/*
+ * rawprint - do a printout of the data in raw mode
+ */
+static void
+rawprint(datatype, length, data, status, fp)
+ int datatype;
+ int length;
+ char *data;
+ int status;
+ FILE *fp;
+{
+ register char *cp;
+ register char *cpend;
+
+ /*
+ * Essentially print the data as is. We reformat unprintables, though.
+ */
+ cp = data;
+ cpend = data + length;
+
+ (void) fprintf(fp, "status=%04x %s\n", status,
+ statustoa(datatype, status));
+
+ while (cp < cpend) {
+ if (*cp == '\r') {
+ /*
+ * If this is a \r and the next character is a
+ * \n, supress this, else pretty print it. Otherwise
+ * just output the character.
+ */
+ if (cp == (cpend-1) || *(cp+1) != '\n')
+ makeascii(1, cp, fp);
+ } else if (isspace(*cp) || isprint(*cp)) {
+ putc(*cp, fp);
+ } else {
+ makeascii(1, cp, fp);
+ }
+ cp++;
+ }
+}
+
+
+/*
+ * Global data used by the cooked output routines
+ */
+int out_chars; /* number of characters output */
+int out_linecount; /* number of characters output on this line */
+
+
+/*
+ * startoutput - get ready to do cooked output
+ */
+static void
+startoutput()
+{
+ out_chars = 0;
+ out_linecount = 0;
+}
+
+
+/*
+ * output - output a variable=value combination
+ */
+static void
+output(fp, name, value)
+ FILE *fp;
+ char *name;
+ char *value;
+{
+ int lenname;
+ int lenvalue;
+
+ lenname = strlen(name);
+ lenvalue = strlen(value);
+
+ if (out_chars != 0) {
+ putc(',', fp);
+ out_chars++;
+ out_linecount++;
+ if ((out_linecount + lenname + lenvalue + 3) > MAXOUTLINE) {
+ putc('\n', fp);
+ out_chars++;
+ out_linecount = 0;
+ } else {
+ putc(' ', fp);
+ out_chars++;
+ out_linecount++;
+ }
+ }
+
+ fputs(name, fp);
+ putc('=', fp);
+ fputs(value, fp);
+ out_chars += lenname + 1 + lenvalue;
+ out_linecount += lenname + 1 + lenvalue;
+}
+
+
+/*
+ * endoutput - terminate a block of cooked output
+ */
+static void
+endoutput(fp)
+ FILE *fp;
+{
+ if (out_chars != 0)
+ putc('\n', fp);
+}
+
+
+/*
+ * outputarr - output an array of values
+ */
+static void
+outputarr(fp, name, narr, lfp)
+ FILE *fp;
+ char *name;
+ int narr;
+ l_fp *lfp;
+{
+ register char *bp;
+ register char *cp;
+ register int i;
+ register int len;
+ char buf[256];
+
+ bp = buf;
+ /*
+ * Hack to align delay and offset values
+ */
+ if ((int)strlen(name) < 10)
+ *bp++ = ' ';
+
+ for (i = narr; i > 0; i--) {
+ if (i != narr)
+ *bp++ = ' ';
+ cp = lfptoms(lfp, 2);
+ len = strlen(cp);
+ while (len < 7) {
+ *bp++ = ' ';
+ len++;
+ }
+ while (*cp != '\0')
+ *bp++ = *cp++;
+ lfp++;
+ }
+ *bp = '\0';
+ output(fp, name, buf);
+}
+
+static char *
+tstflags(val)
+ u_long val;
+{
+ register char *cb, *s;
+ register int i;
+ register char *sep;
+
+ sep = "";
+ i = 0;
+ s = cb = &circ_buf[nextcb][0];
+ if (++nextcb >= NUMCB)
+ nextcb = 0;
+
+ sprintf(cb, "0x%lx", val);
+ cb += strlen(cb);
+ if (val <= ((1<<8)-1)) {
+ if (!val) {
+ strcat(cb, "<OK>");
+ cb += strlen(cb);
+ } else {
+ *cb++ = '<';
+ while (val) {
+ if (val & 0x1) {
+ sprintf(cb, "%s%s", sep, tstflagnames[i]);
+ sep = ";";
+ cb += strlen(cb);
+ }
+ i++;
+ val >>= 1;
+ }
+ *cb++ = '>';
+ }
+ } else {
+ *cb++ = '?';
+ }
+ *cb = '\0';
+ return s;
+}
+
+/*
+ * cookedprint - output variables in cooked mode
+ */
+static void
+cookedprint(datatype, length, data, status, fp)
+ int datatype;
+ int length;
+ char *data;
+ int status;
+ FILE *fp;
+{
+ register int varid;
+ char *name;
+ char *value;
+ int output_raw;
+ int fmt;
+ struct ctl_var *varlist;
+ l_fp lfp;
+ long ival;
+ u_long hval;
+ u_long uval;
+ l_fp lfparr[8];
+ int narr;
+
+ switch (datatype) {
+ case TYPE_PEER:
+ varlist = peer_var;
+ break;
+ case TYPE_SYS:
+ varlist = sys_var;
+ break;
+ case TYPE_CLOCK:
+ varlist = clock_var;
+ break;
+ default:
+ (void) fprintf(stderr, "Unknown datatype(0x%x) in cookedprint\n", datatype);
+ return;
+ break;
+ }
+
+ (void) fprintf(fp, "status=%04x %s\n", status,
+ statustoa(datatype, status));
+
+ startoutput();
+ while (nextvar(&length, &data, &name, &value)) {
+ varid = findvar(name, varlist);
+ if (varid == 0) {
+ output_raw = '*';
+ } else {
+ output_raw = 0;
+ switch((fmt = varlist[varid].fmt)) {
+ case TS:
+ if (!decodets(value, &lfp))
+ output_raw = '?';
+ else
+ output(fp, name, prettydate(&lfp));
+ break;
+ case FL:
+ case FU:
+ case FS:
+ if (!decodetime(value, &lfp))
+ output_raw = '?';
+ else {
+ switch (fmt) {
+ case FL:
+ output(fp, name,
+ lfptoms(&lfp, 3));
+ break;
+ case FU:
+ output(fp, name,
+ ulfptoms(&lfp, 2));
+ break;
+ case FS:
+ output(fp, name,
+ lfptoms(&lfp, 2));
+ break;
+ }
+ }
+ break;
+
+ case UI:
+ if (!decodeuint(value, &uval))
+ output_raw = '?';
+ else
+ output(fp, name, uinttoa(uval));
+ break;
+
+ case IN:
+ if (!decodeint(value, &ival))
+ output_raw = '?';
+ else
+ output(fp, name, inttoa(ival));
+ break;
+
+ case HA:
+ case NA:
+ if (!decodenetnum(value, &hval))
+ output_raw = '?';
+ else if (fmt == HA)
+ output(fp, name, nntohost(hval));
+ else
+ output(fp, name, numtoa(hval));
+ break;
+
+ case ST:
+ output_raw = '*';
+ break;
+
+ case RF:
+ if (decodenetnum(value, &hval))
+ output(fp, name, nntohost(hval));
+ else if ((int)strlen(value) <= 4)
+ output(fp, name, value);
+ else
+ output_raw = '?';
+ break;
+
+ case LP:
+ if (!decodeuint(value, &uval) || uval > 3)
+ output_raw = '?';
+ else {
+ char b[3];
+ b[0] = b[1] = '0';
+ if (uval & 0x2)
+ b[0] = '1';
+ if (uval & 0x1)
+ b[1] = '1';
+ b[2] = '\0';
+ output(fp, name, b);
+ }
+ break;
+
+ case OC:
+ if (!decodeuint(value, &uval))
+ output_raw = '?';
+ else {
+ char b[10];
+
+ (void) sprintf(b, "%03lo", uval);
+ output(fp, name, b);
+ }
+ break;
+
+ case MD:
+ if (!decodeuint(value, &uval))
+ output_raw = '?';
+ else
+ output(fp, name, uinttoa(uval));
+ break;
+
+ case AR:
+ if (!decodearr(value, &narr, lfparr))
+ output_raw = '?';
+ else
+ outputarr(fp, name, narr, lfparr);
+ break;
+
+ case TST:
+ if (!decodeuint(value, &uval))
+ output_raw = '?';
+ else
+ output(fp, name, tstflags(uval));
+ break;
+
+ default:
+ (void) fprintf(stderr,
+ "Internal error in cookedprint, %s=%s, fmt %d\n",
+ name, value, fmt);
+ break;
+ }
+
+ }
+ if (output_raw != 0) {
+ char bn[401];
+ char bv[401];
+ int len;
+
+ atoascii(400, name, bn);
+ atoascii(400, value, bv);
+ if (output_raw != '*') {
+ len = strlen(bv);
+ bv[len] = output_raw;
+ bv[len+1] = '\0';
+ }
+ output(fp, bn, bv);
+ }
+ }
+ endoutput(fp);
+}
+
+
+/*
+ * sortassoc - sort associations in the cache into ascending order
+ */
+void
+sortassoc()
+{
+ if (numassoc > 1)
+#if defined(sgi) || defined(SYS_BSDI) || defined(__STDC__)
+ qsort((void *)assoc_cache, numassoc,
+ sizeof(struct association), assoccmp);
+#else
+ qsort((char *)assoc_cache, numassoc,
+ sizeof(struct association), assoccmp);
+#endif /* sgi || bsdi */
+}
+
+
+/*
+ * assoccmp - compare two associations
+ */
+static int
+#if defined(sgi) || defined(SYS_BSDI) || defined(__STDC__)
+assoccmp(t1, t2)
+ const void *t1;
+ const void *t2;
+{
+ const struct association *ass1 = (const struct association *)t1;
+ const struct association *ass2 = (const struct association *)t2;
+#else
+assoccmp(ass1, ass2)
+ struct association *ass1;
+ struct association *ass2;
+{
+#endif /* sgi || bsdi */
+ if (ass1->assid < ass2->assid)
+ return -1;
+ if (ass1->assid > ass2->assid)
+ return 1;
+ return 0;
+}
diff --git a/usr.sbin/xntpd/ntpq/ntpq.h b/usr.sbin/xntpd/ntpq/ntpq.h
new file mode 100644
index 0000000..c233b24
--- /dev/null
+++ b/usr.sbin/xntpd/ntpq/ntpq.h
@@ -0,0 +1,97 @@
+/*
+ * ntpq.h - definitions of interest to ntpq
+ */
+#include "ntp_fp.h"
+#include "ntp.h"
+#include "ntp_control.h"
+#include "ntp_string.h"
+#include "ntp_malloc.h"
+
+/*
+ * Maximum number of arguments
+ */
+#define MAXARGS 4
+
+/*
+ * Flags for forming descriptors.
+ */
+#define OPT 0x80 /* this argument is optional, or'd with type */
+
+#define NO 0x0
+#define STR 0x1 /* string argument */
+#define UINT 0x2 /* unsigned integer */
+#define INT 0x3 /* signed integer */
+#define ADD 0x4 /* IP network address */
+
+/*
+ * Arguments are returned in a union
+ */
+typedef union {
+ char *string;
+ long ival;
+ u_long uval;
+ u_long netnum;
+} arg_v;
+
+/*
+ * Structure for passing parsed command line
+ */
+struct parse {
+ char *keyword;
+ arg_v argval[MAXARGS];
+ int nargs;
+};
+
+/*
+ * xntpdc includes a command parser which could charitably be called
+ * crude. The following structure is used to define the command
+ * syntax.
+ */
+struct xcmd {
+ char *keyword; /* command key word */
+ void (*handler) P((struct parse *, FILE *)); /* command handler */
+ u_char arg[MAXARGS]; /* descriptors for arguments */
+ char *desc[MAXARGS]; /* descriptions for arguments */
+ char *comment;
+};
+
+/*
+ * Types of things we may deal with
+ */
+#define TYPE_SYS 1
+#define TYPE_PEER 2
+#define TYPE_CLOCK 3
+
+
+/*
+ * Structure to hold association data
+ */
+struct association {
+ u_short assid;
+ u_short status;
+};
+
+#define MAXASSOC 1024
+
+/*
+ * Structure for translation tables between text format
+ * variable indices and text format.
+ */
+struct ctl_var {
+ u_short code;
+ u_short fmt;
+ char *text;
+};
+
+extern void asciize P((int, char *, FILE *));
+extern int getnetnum P((char *, u_long *, char *));
+extern void sortassoc P((void));
+extern int doquery P((int, int, int, int, char *, u_short *, int *, char **));
+extern char * nntohost P((u_long));
+extern int decodets P((char *, l_fp *));
+extern int decodeuint P((char *, u_long *));
+extern int nextvar P((int *, char **, char **, char **));
+extern int decodetime P((char *, l_fp *));
+extern void printvars P((int, char *, int, int, FILE *));
+extern int decodeint P((char *, long *));
+extern int findvar P((char *, struct ctl_var *));
diff --git a/usr.sbin/xntpd/ntpq/ntpq_ops.c b/usr.sbin/xntpd/ntpq/ntpq_ops.c
new file mode 100644
index 0000000..8e59a71
--- /dev/null
+++ b/usr.sbin/xntpd/ntpq/ntpq_ops.c
@@ -0,0 +1,1610 @@
+/*
+ * ntpdc_ops.c - subroutines which are called to perform operations by xntpdc
+ */
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <netdb.h>
+
+#include "ntpq.h"
+#include "ntp_stdlib.h"
+
+extern char * chosts[];
+extern char currenthost[];
+extern int numhosts;
+int maxhostlen;
+
+/*
+ * Declarations for command handlers in here
+ */
+static int checkassocid P((u_long));
+static char * strsave P((char *));
+static struct varlist *findlistvar P((struct varlist *, char *));
+static void doaddvlist P((struct varlist *, char *));
+static void dormvlist P((struct varlist *, char *));
+static void doclearvlist P((struct varlist *));
+static void makequerydata P((struct varlist *, int *, char *));
+static int doquerylist P((struct varlist *, int, int, int, u_short *, int *, char **));
+static void doprintvlist P((struct varlist *, FILE *));
+static void addvars P((struct parse *, FILE *));
+static void rmvars P((struct parse *, FILE *));
+static void clearvars P((struct parse *, FILE *));
+static void showvars P((struct parse *, FILE *));
+static int dolist P((struct varlist *, int, int, int, FILE *));
+static void readlist P((struct parse *, FILE *));
+static void writelist P((struct parse *, FILE *));
+static void readvar P((struct parse *, FILE *));
+static void writevar P((struct parse *, FILE *));
+static void clocklist P((struct parse *, FILE *));
+static void clockvar P((struct parse *, FILE *));
+static int findassidrange P((u_long, u_long, int *, int *));
+static void mreadlist P((struct parse *, FILE *));
+static void mreadvar P((struct parse *, FILE *));
+static int dogetassoc P((FILE *));
+static void printassoc P((int, FILE *));
+static void associations P((struct parse *, FILE *));
+static void lassociations P((struct parse *, FILE *));
+static void passociations P((struct parse *, FILE *));
+static void lpassociations P((struct parse *, FILE *));
+
+#ifdef UNUSED
+static void radiostatus P((struct parse *, FILE *));
+#endif /* UNUSED */
+
+static void pstatus P((struct parse *, FILE *));
+static char * fixup P((int, char *));
+static char * when P((l_fp *, l_fp *, l_fp *));
+static int doprintpeers P((struct varlist *, int, int, int, char *, FILE *));
+static int dogetpeers P((struct varlist *, int, FILE *));
+static void dopeers P((int, FILE *));
+static void peers P((struct parse *, FILE *));
+static void lpeers P((struct parse *, FILE *));
+static void doopeers P((int, FILE *));
+static void opeers P((struct parse *, FILE *));
+static void lopeers P((struct parse *, FILE *));
+
+
+/*
+ * Commands we understand. Ntpdc imports this.
+ */
+struct xcmd opcmds[] = {
+ { "associations", associations, { NO, NO, NO, NO },
+ { "", "", "", "" },
+ "print list of association ID's and statuses for the server's peers" },
+ { "passociations", passociations, { NO, NO, NO, NO },
+ { "", "", "", "" },
+ "print list of associations returned by last associations command" },
+ { "lassociations", lassociations, { NO, NO, NO, NO },
+ { "", "", "", "" },
+ "print list of associations including all client information" },
+ { "lpassociations", lpassociations, { NO, NO, NO, NO },
+ { "", "", "", "" },
+"print last obtained list of associations, including client information" },
+ { "addvars", addvars, { STR, NO, NO, NO },
+ { "name[=value][,...]", "", "", "" },
+ "add variables to the variable list or change their values" },
+ { "rmvars", rmvars, { STR, NO, NO, NO },
+ { "name[,...]", "", "", "" },
+ "remove variables from the variable list" },
+ { "clearvars", clearvars, { NO, NO, NO, NO },
+ { "", "", "", "" },
+ "remove all variables from the variable list" },
+ { "showvars", showvars, { NO, NO, NO, NO },
+ { "", "", "", "" },
+ "print variables on the variable list" },
+ { "readlist", readlist, { OPT|UINT, NO, NO, NO },
+ { "assocID", "", "", "" },
+ "read the system or peer variables included in the variable list" },
+ { "rl", readlist, { OPT|UINT, NO, NO, NO },
+ { "assocID", "", "", "" },
+ "read the system or peer variables included in the variable list" },
+ { "writelist", writelist, { OPT|UINT, NO, NO, NO },
+ { "assocID", "", "", "" },
+ "write the system or peer variables included in the variable list" },
+ { "readvar", readvar, { OPT|UINT, OPT|STR, NO, NO },
+ { "assocID", "name=value[,...]", "", "" },
+ "read system or peer variables" },
+ { "rv", readvar, { OPT|UINT, OPT|STR, NO, NO },
+ { "assocID", "name=value[,...]", "", "" },
+ "read system or peer variables" },
+ { "writevar", writevar, { UINT, STR, NO, NO },
+ { "assocID", "name=value,[...]", "", "" },
+ "write system or peer variables" },
+ { "mreadlist", mreadlist, { UINT, UINT, NO, NO },
+ { "assocID", "assocID", "", "" },
+ "read the peer variables in the variable list for multiple peers" },
+ { "mrl", mreadlist, { UINT, UINT, NO, NO },
+ { "assocID", "assocID", "", "" },
+ "read the peer variables in the variable list for multiple peers" },
+ { "mreadvar", mreadvar, { UINT, UINT, OPT|STR, NO },
+ { "assocID", "assocID", "name=value[,...]", "" },
+ "read peer variables from multiple peers" },
+ { "mrv", mreadvar, { UINT, UINT, OPT|STR, NO },
+ { "assocID", "assocID", "name=value[,...]", "" },
+ "read peer variables from multiple peers" },
+ { "clocklist", clocklist, { OPT|UINT, NO, NO, NO },
+ { "assocID", "", "", "" },
+ "read the clock variables included in the variable list" },
+ { "cl", clocklist, { OPT|UINT, NO, NO, NO },
+ { "assocID", "", "", "" },
+ "read the clock variables included in the variable list" },
+ { "clockvar", clockvar, { OPT|UINT, OPT|STR, NO, NO },
+ { "assocID", "name=value[,...]", "", "" },
+ "read clock variables" },
+ { "cv", clockvar, { OPT|UINT, OPT|STR, NO, NO },
+ { "assocID", "name=value[,...]", "", "" },
+ "read clock variables" },
+ { "pstatus", pstatus, { UINT, NO, NO, NO },
+ { "assocID", "", "", "" },
+ "print status information returned for a peer" },
+ { "peers", peers, { NO, NO, NO, NO },
+ { "", "", "", "" },
+ "obtain and print a list of the server's peers" },
+ { "lpeers", lpeers, { NO, NO, NO, NO },
+ { "", "", "", "" },
+ "obtain and print a list of all peers and clients" },
+ { "opeers", opeers, { NO, NO, NO, NO },
+ { "", "", "", "" },
+ "print peer list the old way, with dstadr shown rather than refid" },
+ { "lopeers", lopeers, { NO, NO, NO, NO },
+ { "", "", "", "" },
+ "obtain and print a list of all peers and clients showing dstadr" },
+ { 0, 0, { NO, NO, NO, NO },
+ { "", "", "", "" }, "" }
+};
+
+
+/*
+ * Variable list data space
+ */
+#define MAXLIST 64 /* maximum number of variables in list */
+#define LENHOSTNAME 256 /* host name is 256 characters long */
+/*
+ * Old CTL_PST defines for version 2.
+ */
+#define OLD_CTL_PST_CONFIG 0x80
+#define OLD_CTL_PST_AUTHENABLE 0x40
+#define OLD_CTL_PST_AUTHENTIC 0x20
+#define OLD_CTL_PST_REACH 0x10
+#define OLD_CTL_PST_SANE 0x08
+#define OLD_CTL_PST_DISP 0x04
+#define OLD_CTL_PST_SEL_REJECT 0
+#define OLD_CTL_PST_SEL_SELCAND 1
+#define OLD_CTL_PST_SEL_SYNCCAND 2
+#define OLD_CTL_PST_SEL_SYSPEER 3
+
+
+char flash2[] = " .+* "; /* flash decode for version 2 */
+char flash3[] = " x.-+#*o"; /* flash decode for peer status version 3 */
+
+struct varlist {
+ char *name;
+ char *value;
+} varlist[MAXLIST] = { { 0, 0 } };
+
+/*
+ * Imported from ntpq.c
+ */
+extern int showhostnames;
+extern int rawmode;
+extern int debug;
+extern struct servent *server_entry;
+extern struct association assoc_cache[];
+extern int numassoc;
+extern u_char pktversion;
+
+/*
+ * For quick string comparisons
+ */
+#define STREQ(a, b) (*(a) == *(b) && strcmp((a), (b)) == 0)
+
+
+/*
+ * checkassocid - return the association ID, checking to see if it is valid
+ */
+static int
+checkassocid(value)
+ u_long value;
+{
+ if (value == 0 || value >= 65536) {
+ (void) fprintf(stderr, "***Invalid association ID specified\n");
+ return 0;
+ }
+ return (int)value;
+}
+
+
+/*
+ * strsave - save a string
+ * XXX - should be in libntp.a
+ */
+static char *
+strsave(str)
+ char *str;
+{
+ char *cp;
+ u_int len;
+
+ len = strlen(str) + 1;
+ if ((cp = (char *)malloc(len)) == NULL) {
+ (void) fprintf(stderr, "Malloc failed!!\n");
+ exit(1);
+ }
+
+ memmove(cp, str, len);
+ return (cp);
+}
+
+
+/*
+ * findlistvar - look for the named variable in a list and return if found
+ */
+static struct varlist *
+findlistvar(list, name)
+ struct varlist *list;
+ char *name;
+{
+ register struct varlist *vl;
+
+ for (vl = list; vl < list + MAXLIST && vl->name != 0; vl++)
+ if (STREQ(name, vl->name))
+ return vl;
+ if (vl < list + MAXLIST)
+ return vl;
+ return (struct varlist *)0;
+}
+
+
+/*
+ * doaddvlist - add variable(s) to the variable list
+ */
+static void
+doaddvlist(vlist, vars)
+ struct varlist *vlist;
+ char *vars;
+{
+ register struct varlist *vl;
+ int len;
+ char *name;
+ char *value;
+
+ len = strlen(vars);
+ while (nextvar(&len, &vars, &name, &value)) {
+ vl = findlistvar(vlist, name);
+ if (vl == 0) {
+ (void) fprintf(stderr, "Variable list full\n");
+ return;
+ }
+
+ if (vl->name == 0) {
+ vl->name = strsave(name);
+ } else if (vl->value != 0) {
+ (void) free(vl->value);
+ vl->value = 0;
+ }
+
+ if (value != 0)
+ vl->value = strsave(value);
+ }
+}
+
+
+/*
+ * dormvlist - remove variable(s) from the variable list
+ */
+static void
+dormvlist(vlist, vars)
+ struct varlist *vlist;
+ char *vars;
+{
+ register struct varlist *vl;
+ int len;
+ char *name;
+ char *value;
+
+ len = strlen(vars);
+ while (nextvar(&len, &vars, &name, &value)) {
+ vl = findlistvar(vlist, name);
+ if (vl == 0 || vl->name == 0) {
+ (void) fprintf(stderr, "Variable `%s' not found\n",
+ name);
+ } else {
+ (void) free(vl->name);
+ if (vl->value != 0)
+ (void) free(vl->value);
+ for ( ; (vl+1) < (varlist+MAXLIST)
+ && (vl+1)->name != 0; vl++) {
+ vl->name = (vl+1)->name;
+ vl->value = (vl+1)->value;
+ }
+ vl->name = vl->value = 0;
+ }
+ }
+}
+
+
+/*
+ * doclearvlist - clear a variable list
+ */
+static void
+doclearvlist(vlist)
+ struct varlist *vlist;
+{
+ register struct varlist *vl;
+
+ for (vl = vlist; vl < vlist + MAXLIST && vl->name != 0; vl++) {
+ (void) free(vl->name);
+ vl->name = 0;
+ if (vl->value != 0) {
+ (void) free(vl->value);
+ vl->value = 0;
+ }
+ }
+}
+
+
+/*
+ * makequerydata - form a data buffer to be included with a query
+ */
+static void
+makequerydata(vlist, datalen, data)
+ struct varlist *vlist;
+ int *datalen;
+ char *data;
+{
+ register struct varlist *vl;
+ register char *cp, *cpend;
+ register int namelen, valuelen;
+ register int totallen;
+
+ cp = data;
+ cpend = data + *datalen;
+
+ for (vl = vlist; vl < vlist + MAXLIST && vl->name != 0; vl++) {
+ namelen = strlen(vl->name);
+ if (vl->value == 0)
+ valuelen = 0;
+ else
+ valuelen = strlen(vl->value);
+ totallen = namelen + valuelen + (valuelen != 0) + (cp != data);
+ if (cp + totallen > cpend)
+ break;
+
+ if (cp != data)
+ *cp++ = ',';
+ memmove(cp, vl->name, namelen);
+ cp += namelen;
+ if (valuelen != 0) {
+ *cp++ = '=';
+ memmove(cp, vl->value, valuelen);
+ cp += valuelen;
+ }
+ }
+ *datalen = cp - data;
+}
+
+
+/*
+ * doquerylist - send a message including variables in a list
+ */
+static int
+doquerylist(vlist, op, associd, auth, rstatus, dsize, datap)
+ struct varlist *vlist;
+ int op;
+ int associd;
+ int auth;
+ u_short *rstatus;
+ int *dsize;
+ char **datap;
+{
+ char data[CTL_MAX_DATA_LEN];
+ int datalen;
+
+ datalen = sizeof(data);
+ makequerydata(vlist, &datalen, data);
+
+ return doquery(op, associd, auth, datalen, data, rstatus,
+ dsize, datap);
+}
+
+
+/*
+ * doprintvlist - print the variables on a list
+ */
+static void
+doprintvlist(vlist, fp)
+ struct varlist *vlist;
+ FILE *fp;
+{
+ register struct varlist *vl;
+
+ if (vlist->name == 0) {
+ (void) fprintf(fp, "No variables on list\n");
+ } else {
+ for (vl = vlist; vl < vlist + MAXLIST && vl->name != 0; vl++) {
+ if (vl->value == 0) {
+ (void) fprintf(fp, "%s\n", vl->name);
+ } else {
+ (void) fprintf(fp, "%s=%s\n",
+ vl->name, vl->value);
+ }
+ }
+ }
+}
+
+
+/*
+ * addvars - add variables to the variable list
+ */
+/*ARGSUSED*/
+static void
+addvars(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ doaddvlist(varlist, pcmd->argval[0].string);
+}
+
+
+/*
+ * rmvars - remove variables from the variable list
+ */
+/*ARGSUSED*/
+static void
+rmvars(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ dormvlist(varlist, pcmd->argval[0].string);
+}
+
+
+/*
+ * clearvars - clear the variable list
+ */
+/*ARGSUSED*/
+static void
+clearvars(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ doclearvlist(varlist);
+}
+
+
+/*
+ * showvars - show variables on the variable list
+ */
+/*ARGSUSED*/
+static void
+showvars(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ doprintvlist(varlist, fp);
+}
+
+
+/*
+ * dolist - send a request with the given list of variables
+ */
+static int
+dolist(vlist, associd, op, type, fp)
+ struct varlist *vlist;
+ int associd;
+ int op;
+ int type;
+ FILE *fp;
+{
+ char *datap;
+ int res;
+ int dsize;
+ u_short rstatus;
+
+ res = doquerylist(vlist, op, associd, 0, &rstatus, &dsize, &datap);
+
+ if (res != 0)
+ return 0;
+
+ if (dsize == 0) {
+ if (associd == 0)
+ (void) fprintf(fp, "No system%s variables returned\n",
+ (type == TYPE_CLOCK) ? " clock" : "");
+ else
+ (void) fprintf(fp,
+ "No information returned for%s association %u\n",
+ (type == TYPE_CLOCK) ? " clock" : "", associd);
+ return 1;
+ }
+
+ printvars(dsize, datap, (int)rstatus, type, fp);
+ return 1;
+}
+
+
+/*
+ * readlist - send a read variables request with the variables on the list
+ */
+static void
+readlist(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ int associd;
+
+ if (pcmd->nargs == 0) {
+ associd = 0;
+ } else {
+ if (pcmd->argval[0].uval == 0)
+ associd = 0;
+ else if ((associd = checkassocid(pcmd->argval[0].uval)) == 0)
+ return;
+ }
+
+ (void) dolist(varlist, associd, CTL_OP_READVAR,
+ (associd == 0) ? TYPE_SYS : TYPE_PEER, fp);
+}
+
+
+/*
+ * writelist - send a write variables request with the variables on the list
+ */
+static void
+writelist(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ char *datap;
+ int res;
+ int associd;
+ int dsize;
+ u_short rstatus;
+
+ if (pcmd->nargs == 0) {
+ associd = 0;
+ } else {
+ if (pcmd->argval[0].uval == 0)
+ associd = 0;
+ else if ((associd = checkassocid(pcmd->argval[0].uval)) == 0)
+ return;
+ }
+
+ res = doquerylist(varlist, CTL_OP_WRITEVAR, associd, 0, &rstatus,
+ &dsize, &datap);
+
+ if (res != 0)
+ return;
+
+ if (dsize == 0)
+ (void) fprintf(fp, "done! (no data returned)\n");
+ else
+ printvars(dsize, datap, (int)rstatus,
+ (associd != 0) ? TYPE_PEER : TYPE_SYS, fp);
+ return;
+}
+
+
+/*
+ * readvar - send a read variables request with the specified variables
+ */
+static void
+readvar(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ int associd;
+ struct varlist tmplist[MAXLIST];
+
+ if (pcmd->nargs == 0 || pcmd->argval[0].uval == 0)
+ associd = 0;
+ else if ((associd = checkassocid(pcmd->argval[0].uval)) == 0)
+ return;
+
+ memset((char *)tmplist, 0, sizeof(tmplist));
+ if (pcmd->nargs >= 2)
+ doaddvlist(tmplist, pcmd->argval[1].string);
+
+ (void) dolist(tmplist, associd, CTL_OP_READVAR,
+ (associd == 0) ? TYPE_SYS : TYPE_PEER, fp);
+
+ doclearvlist(tmplist);
+}
+
+
+/*
+ * writevar - send a write variables request with the specified variables
+ */
+static void
+writevar(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ char *datap;
+ int res;
+ int associd;
+ int dsize;
+ u_short rstatus;
+ struct varlist tmplist[MAXLIST];
+
+ if (pcmd->argval[0].uval == 0)
+ associd = 0;
+ else if ((associd = checkassocid(pcmd->argval[0].uval)) == 0)
+ return;
+
+ memset((char *)tmplist, 0, sizeof(tmplist));
+ doaddvlist(tmplist, pcmd->argval[1].string);
+
+ res = doquerylist(tmplist, CTL_OP_WRITEVAR, associd, 0, &rstatus,
+ &dsize, &datap);
+
+ doclearvlist(tmplist);
+
+ if (res != 0)
+ return;
+
+ if (dsize == 0)
+ (void) fprintf(fp, "done! (no data returned)\n");
+ else
+ printvars(dsize, datap, (int)rstatus,
+ (associd != 0) ? TYPE_PEER : TYPE_SYS, fp);
+ return;
+}
+
+
+/*
+ * clocklist - send a clock variables request with the variables on the list
+ */
+static void
+clocklist(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ int associd;
+
+ if (pcmd->nargs == 0) {
+ associd = 0;
+ } else {
+ if (pcmd->argval[0].uval == 0)
+ associd = 0;
+ else if ((associd = checkassocid(pcmd->argval[0].uval)) == 0)
+ return;
+ }
+
+ (void) dolist(varlist, associd, CTL_OP_READCLOCK, TYPE_CLOCK, fp);
+}
+
+
+/*
+ * clockvar - send a clock variables request with the specified variables
+ */
+static void
+clockvar(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ int associd;
+ struct varlist tmplist[MAXLIST];
+
+ if (pcmd->nargs == 0 || pcmd->argval[0].uval == 0)
+ associd = 0;
+ else if ((associd = checkassocid(pcmd->argval[0].uval)) == 0)
+ return;
+
+ memset((char *)tmplist, 0, sizeof(tmplist));
+ if (pcmd->nargs >= 2)
+ doaddvlist(tmplist, pcmd->argval[1].string);
+
+ (void) dolist(tmplist, associd, CTL_OP_READCLOCK, TYPE_CLOCK, fp);
+
+ doclearvlist(tmplist);
+}
+
+
+/*
+ * findassidrange - verify a range of association ID's
+ */
+static int
+findassidrange(assid1, assid2, from, to)
+ u_long assid1;
+ u_long assid2;
+ int *from;
+ int *to;
+{
+ register int i;
+ int f, t;
+
+ if (assid1 == 0 || assid1 > 65535) {
+ (void) fprintf(stderr,
+ "***Invalid association ID %lu specified\n", (u_long)assid1);
+ return 0;
+ }
+
+ if (assid2 == 0 || assid2 > 65535) {
+ (void) fprintf(stderr,
+ "***Invalid association ID %lu specified\n", (u_long)assid2);
+ return 0;
+ }
+
+ f = t = -1;
+ for (i = 0; i < numassoc; i++) {
+ if (assoc_cache[i].assid == assid1) {
+ f = i;
+ if (t != -1)
+ break;
+ }
+ if (assoc_cache[i].assid == assid2) {
+ t = i;
+ if (f != -1)
+ break;
+ }
+ }
+
+ if (f == -1 || t == -1) {
+ (void) fprintf(stderr,
+ "***Association ID %lu not found in list\n",
+ (f == -1) ? (u_long)assid1 : (u_long)assid2);
+ return 0;
+ }
+
+ if (f < t) {
+ *from = f;
+ *to = t;
+ } else {
+ *from = t;
+ *to = f;
+ }
+ return 1;
+}
+
+
+
+/*
+ * mreadlist - send a read variables request for multiple associations
+ */
+static void
+mreadlist(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ int i;
+ int from;
+ int to;
+
+ if (!findassidrange(pcmd->argval[0].uval, pcmd->argval[1].uval,
+ &from, &to))
+ return;
+
+ for (i = from; i <= to; i++) {
+ if (i != from)
+ (void) fprintf(fp, "\n");
+ if (!dolist(varlist, (int)assoc_cache[i].assid,
+ CTL_OP_READVAR, TYPE_PEER, fp))
+ return;
+ }
+ return;
+}
+
+
+/*
+ * mreadvar - send a read variables request for multiple associations
+ */
+static void
+mreadvar(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ int i;
+ int from;
+ int to;
+ struct varlist tmplist[MAXLIST];
+
+ if (!findassidrange(pcmd->argval[0].uval, pcmd->argval[1].uval,
+ &from, &to))
+ return;
+
+ memset((char *)tmplist, 0, sizeof(tmplist));
+ if (pcmd->nargs >= 3)
+ doaddvlist(tmplist, pcmd->argval[2].string);
+
+ for (i = from; i <= to; i++) {
+ if (i != from)
+ (void) fprintf(fp, "\n");
+ if (!dolist(varlist, (int)assoc_cache[i].assid,
+ CTL_OP_READVAR, TYPE_PEER, fp))
+ break;
+ }
+ doclearvlist(tmplist);
+ return;
+}
+
+
+/*
+ * dogetassoc - query the host for its list of associations
+ */
+static int
+dogetassoc(fp)
+ FILE *fp;
+{
+ u_short *datap;
+ int res;
+ int dsize;
+ u_short rstatus;
+
+ res = doquery(CTL_OP_READSTAT, 0, 0, 0, (char *)0, &rstatus,
+ &dsize, (char **)&datap);
+
+ if (res != 0)
+ return 0;
+
+ if (dsize == 0) {
+ (void) fprintf(fp, "No association ID's returned\n");
+ return 0;
+ }
+
+ if (dsize & 0x3) {
+ (void) fprintf(stderr,
+ "***Server returned %d octets, should be multiple of 4\n",
+ dsize);
+ return 0;
+ }
+
+ numassoc = 0;
+ while (dsize > 0) {
+ assoc_cache[numassoc].assid = ntohs(*datap);
+ datap++;
+ assoc_cache[numassoc].status = ntohs(*datap);
+ datap++;
+ if (++numassoc >= MAXASSOC)
+ break;
+ dsize -= sizeof(u_short) + sizeof(u_short);
+ }
+ sortassoc();
+ return 1;
+}
+
+
+/*
+ * printassoc - print the current list of associations
+ */
+static void
+printassoc(showall, fp)
+ int showall;
+ FILE *fp;
+{
+ register char *bp;
+ int i;
+ u_char statval;
+ int event;
+ u_long event_count;
+ char *conf;
+ char *reach;
+ char *auth;
+ char *condition = "";
+ char *last_event;
+ char *cnt;
+ char buf[128];
+
+ if (numassoc == 0) {
+ (void) fprintf(fp, "No association ID's in list\n");
+ return;
+ }
+
+ /*
+ * Output a header
+ */
+ (void) fprintf(fp,
+ "ind assID status conf reach auth condition last_event cnt\n");
+ (void) fprintf(fp,
+ "===========================================================\n");
+ for (i = 0; i < numassoc; i++) {
+ statval = CTL_PEER_STATVAL(assoc_cache[i].status);
+ if (!showall && !(statval & (CTL_PST_CONFIG|CTL_PST_REACH)))
+ continue;
+ event = CTL_PEER_EVENT(assoc_cache[i].status);
+ event_count = CTL_PEER_NEVNT(assoc_cache[i].status);
+ if (statval & CTL_PST_CONFIG)
+ conf = "yes";
+ else
+ conf = "no";
+ if (statval & CTL_PST_REACH) {
+ reach = "yes";
+ if (statval & CTL_PST_AUTHENABLE) {
+ if (statval & CTL_PST_AUTHENTIC)
+ auth = "ok ";
+ else
+ auth = "bad";
+ } else
+ auth = "none";
+
+ if (pktversion == NTP_VERSION)
+ switch (statval & 0x7) {
+ case CTL_PST_SEL_REJECT:
+ condition = "insane";
+ break;
+ case CTL_PST_SEL_SANE:
+ condition = "falsetick";
+ break;
+ case CTL_PST_SEL_CORRECT:
+ condition = "eliminate";
+ break;
+ case CTL_PST_SEL_SELCAND:
+ condition = "outlyer";
+ break;
+ case CTL_PST_SEL_SYNCCAND:
+ condition = "synchr.";
+ break;
+ case CTL_PST_SEL_DISTSYSPEER:
+ condition = "dist.peer";
+ break;
+ case CTL_PST_SEL_SYSPEER:
+ condition = "sys.peer";
+ break;
+ case CTL_PST_SEL_PPS:
+ condition = "pps.peer";
+ break;
+ }
+ else
+ switch (statval & 0x3) {
+ case OLD_CTL_PST_SEL_REJECT:
+ if (!(statval & OLD_CTL_PST_SANE))
+ condition = "insane";
+ else if (!(statval & OLD_CTL_PST_DISP))
+ condition = "hi_disp";
+ else
+ condition = "";
+ break;
+ case OLD_CTL_PST_SEL_SELCAND:
+ condition = "sel_cand";
+ break;
+ case OLD_CTL_PST_SEL_SYNCCAND:
+ condition = "sync_cand";
+ break;
+ case OLD_CTL_PST_SEL_SYSPEER:
+ condition = "sys.peer";
+ break;
+ }
+
+ } else {
+ reach = "no";
+ auth = condition = "";
+ }
+
+ switch (PEER_EVENT|event) {
+ case EVNT_PEERIPERR:
+ last_event = "IP error";
+ break;
+ case EVNT_PEERAUTH:
+ last_event = "auth fail";
+ break;
+ case EVNT_UNREACH:
+ last_event = "lost reach";
+ break;
+ case EVNT_REACH:
+ last_event = "reachable";
+ break;
+ case EVNT_PEERCLOCK:
+ last_event = "clock expt";
+ break;
+#if 0
+ case EVNT_PEERSTRAT:
+ last_event = "stratum chg";
+ break;
+#endif
+ default:
+ last_event = "";
+ break;
+ }
+
+ if (event_count != 0)
+ cnt = uinttoa(event_count);
+ else
+ cnt = "";
+ (void) sprintf(buf,
+ "%3d %5u %04x %3.3s %4s %4.4s %9.9s %11s %2s",
+ i+1, assoc_cache[i].assid, assoc_cache[i].status,
+ conf, reach, auth, condition, last_event, cnt);
+ bp = &buf[strlen(buf)];
+ while (bp > buf && *(bp-1) == ' ')
+ *(--bp) = '\0';
+ (void) fprintf(fp, "%s\n", buf);
+ }
+}
+
+
+
+/*
+ * associations - get, record and print a list of associations
+ */
+/*ARGSUSED*/
+static void
+associations(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ if (dogetassoc(fp))
+ printassoc(0, fp);
+}
+
+
+/*
+ * lassociations - get, record and print a long list of associations
+ */
+/*ARGSUSED*/
+static void
+lassociations(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ if (dogetassoc(fp))
+ printassoc(1, fp);
+}
+
+
+/*
+ * passociations - print the association list
+ */
+/*ARGSUSED*/
+static void
+passociations(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ printassoc(0, fp);
+}
+
+
+/*
+ * lpassociations - print the long association list
+ */
+/*ARGSUSED*/
+static void
+lpassociations(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ printassoc(1, fp);
+}
+
+
+#ifdef UNUSED
+/*
+ * radiostatus - print the radio status returned by the server
+ */
+/*ARGSUSED*/
+static void
+radiostatus(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ char *datap;
+ int res;
+ int dsize;
+ u_short rstatus;
+
+ res = doquery(CTL_OP_READCLOCK, 0, 0, 0, (char *)0, &rstatus,
+ &dsize, &datap);
+
+ if (res != 0)
+ return;
+
+ if (dsize == 0) {
+ (void) fprintf(fp, "No radio status string returned\n");
+ return;
+ }
+
+ asciize(dsize, datap, fp);
+}
+#endif /* UNUSED */
+
+/*
+ * pstatus - print peer status returned by the server
+ */
+static void
+pstatus(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ char *datap;
+ int res;
+ int associd;
+ int dsize;
+ u_short rstatus;
+
+ if ((associd = checkassocid(pcmd->argval[0].uval)) == 0)
+ return;
+
+ res = doquery(CTL_OP_READSTAT, associd, 0, 0, (char *)0, &rstatus,
+ &dsize, &datap);
+
+ if (res != 0)
+ return;
+
+ if (dsize == 0) {
+ (void) fprintf(fp,
+ "No information returned for association %u\n",
+ associd);
+ return;
+ }
+
+ printvars(dsize, datap, (int)rstatus, TYPE_PEER, fp);
+}
+
+
+/*
+ * fixup - fix up a string so we don't get a hanging decimal after it
+ */
+static char *
+fixup(width, str)
+ int width;
+ char *str;
+{
+ if (str[width-1] == '.')
+ str[width-1] = '\0';
+ return str;
+}
+
+
+/*
+ * when - print how long its been since his last packet arrived
+ */
+static char *
+when(ts, rec, reftime)
+ l_fp *ts;
+ l_fp *rec;
+ l_fp *reftime;
+{
+ long diff;
+ l_fp *lasttime;
+ static char buf[20];
+
+ if (rec->l_ui != 0)
+ lasttime = rec;
+ else if (reftime->l_ui != 0)
+ lasttime = reftime;
+ else
+ return "-";
+
+ diff = (long)(ts->l_ui - lasttime->l_ui);
+ if (diff <= 0) {
+ /*
+ * Time warp?
+ */
+ diff = 1;
+ }
+
+ if (diff <= 2048) {
+ (void) sprintf(buf, "%ld", (long int)diff);
+ return buf;
+ }
+
+ diff = (diff + 29) / 60;
+ if (diff <= 300) {
+ (void) sprintf(buf, "%ldm", (long int)diff);
+ return buf;
+ }
+
+ diff = (diff + 29) / 60;
+ if (diff <= 96) {
+ (void) sprintf(buf, "%ldh", (long int)diff);
+ return buf;
+ }
+
+ diff = (diff + 11) / 24;
+ (void) sprintf(buf, "%ldd", (long int)diff);
+ return buf;
+}
+
+
+
+/*
+ * A list of variables required by the peers command
+ */
+struct varlist opeervarlist[] = {
+ { "srcadr", 0 }, /* 0 */
+ { "dstadr", 0 }, /* 1 */
+ { "stratum", 0 }, /* 2 */
+ { "hpoll", 0 }, /* 3 */
+ { "ppoll", 0 }, /* 4 */
+ { "reach", 0 }, /* 5 */
+ { "delay", 0 }, /* 6 */
+ { "offset", 0 }, /* 7 */
+ { "dispersion", 0 }, /* 8 */
+ { "rec", 0 }, /* 9 */
+ { "reftime", 0 }, /* 10 */
+ { "srcport", 0 }, /* 11 */
+ { 0, 0 }
+};
+
+struct varlist peervarlist[] = {
+ { "srcadr", 0 }, /* 0 */
+ { "refid", 0 }, /* 1 */
+ { "stratum", 0 }, /* 2 */
+ { "hpoll", 0 }, /* 3 */
+ { "ppoll", 0 }, /* 4 */
+ { "reach", 0 }, /* 5 */
+ { "delay", 0 }, /* 6 */
+ { "offset", 0 }, /* 7 */
+ { "dispersion", 0 }, /* 8 */
+ { "rec", 0 }, /* 9 */
+ { "reftime", 0 }, /* 10 */
+ { "srcport", 0 }, /* 11 */
+ { 0, 0 }
+};
+
+#define HAVE_SRCADR 0
+#define HAVE_DSTADR 1
+#define HAVE_REFID 1
+#define HAVE_STRATUM 2
+#define HAVE_HPOLL 3
+#define HAVE_PPOLL 4
+#define HAVE_REACH 5
+#define HAVE_DELAY 6
+#define HAVE_OFFSET 7
+#define HAVE_DISPERSION 8
+#define HAVE_REC 9
+#define HAVE_REFTIME 10
+#define HAVE_SRCPORT 11
+#define MAXHAVE 12
+
+/*
+ * Decode an incoming data buffer and print a line in the peer list
+ */
+static int
+doprintpeers(pvl, associd, rstatus, datalen, data, fp)
+ struct varlist *pvl;
+ int associd;
+ int rstatus;
+ int datalen;
+ char *data;
+ FILE *fp;
+{
+ char *name;
+ char *value;
+ int i;
+ int c;
+
+ u_long srcadr;
+ u_long dstadr;
+ u_long srcport;
+ char *dstadr_refid = "0.0.0.0";
+ u_long stratum;
+ long ppoll;
+ long hpoll;
+ u_long reach;
+ l_fp estdelay;
+ l_fp estoffset;
+ l_fp estdisp;
+ l_fp rec;
+ l_fp reftime;
+ l_fp ts;
+ u_char havevar[MAXHAVE];
+ u_long poll;
+ char type = '?';
+ char refid_string[10];
+ extern struct ctl_var peer_var[];
+
+ memset((char *)havevar, 0, sizeof(havevar));
+ gettstamp(&ts);
+
+ while (nextvar(&datalen, &data, &name, &value)) {
+ u_long dummy;
+ i = findvar(name, peer_var);
+ if (i == 0)
+ continue; /* don't know this one */
+ switch (i) {
+ case CP_SRCADR:
+ if (decodenetnum(value, &srcadr))
+ havevar[HAVE_SRCADR] = 1;
+ break;
+ case CP_DSTADR:
+ if (decodenetnum(value, &dummy)) {
+ dummy = ntohl(dummy);
+ type = ((dummy&0xf0000000)==0xe0000000) ? 'm' :
+ ((dummy&0x000000ff)==0x000000ff) ? 'b' :
+ ((dummy&0xffffffff)==0x7f000001) ? 'l' :
+ ((dummy&0xffffffe0)==0x00000000) ? '-' :
+ 'u';
+ }
+ if (pvl == opeervarlist) {
+ if (decodenetnum(value, &dstadr)) {
+ havevar[HAVE_DSTADR] = 1;
+ dstadr_refid = numtoa(dstadr);
+ }
+ }
+ break;
+ case CP_REFID:
+ if (pvl == peervarlist) {
+ havevar[HAVE_REFID] = 1;
+ if (*value == '\0') {
+ dstadr_refid = "0.0.0.0";
+ } else if (decodenetnum(value, &dstadr)) {
+ if (dstadr == 0)
+ dstadr_refid = "0.0.0.0";
+ else
+ dstadr_refid = nntohost(dstadr);
+ } else if ((int)strlen(value) <= 4) {
+ refid_string[0] = '.';
+ (void) strcpy(&refid_string[1], value);
+ i = strlen(refid_string);
+ refid_string[i] = '.';
+ refid_string[i+1] = '\0';
+ dstadr_refid = refid_string;
+ } else {
+ havevar[HAVE_REFID] = 0;
+ }
+ }
+ break;
+ case CP_STRATUM:
+ if (decodeuint(value, &stratum))
+ havevar[HAVE_STRATUM] = 1;
+ break;
+ case CP_HPOLL:
+ if (decodeint(value, &hpoll)) {
+ havevar[HAVE_HPOLL] = 1;
+ if (hpoll < 0)
+ hpoll = NTP_MINPOLL;
+ }
+ break;
+ case CP_PPOLL:
+ if (decodeint(value, &ppoll)) {
+ havevar[HAVE_PPOLL] = 1;
+ if (ppoll < 0)
+ ppoll = NTP_MINPOLL;
+ }
+ break;
+ case CP_REACH:
+ if (decodeuint(value, &reach))
+ havevar[HAVE_REACH] = 1;
+ break;
+ case CP_DELAY:
+ if (decodetime(value, &estdelay))
+ havevar[HAVE_DELAY] = 1;
+ break;
+ case CP_OFFSET:
+ if (decodetime(value, &estoffset))
+ havevar[HAVE_OFFSET] = 1;
+ break;
+ case CP_DISPERSION:
+ if (decodetime(value, &estdisp))
+ havevar[HAVE_DISPERSION] = 1;
+ break;
+ case CP_REC:
+ if (decodets(value, &rec))
+ havevar[HAVE_REC] = 1;
+ break;
+ case CP_SRCPORT:
+ if (decodeuint(value, &srcport))
+ havevar[HAVE_SRCPORT] = 1;
+ break;
+ case CP_REFTIME:
+ havevar[HAVE_REFTIME] = 1;
+ if (!decodets(value, &reftime))
+ L_CLR(&reftime);
+ break;
+ default:
+ break;
+ }
+ }
+
+ /*
+ * Check to see if the srcport is NTP's port. If not this probably
+ * isn't a valid peer association.
+ */
+ if (havevar[HAVE_SRCPORT] && srcport != NTP_PORT)
+ return 1;
+
+ /*
+ * Check to see if we got all of them. If not, return an
+ * error.
+ */
+ for (i = 0; i < MAXHAVE; i++)
+ if (!havevar[i]) {
+ (void) fprintf(stderr,
+ "***Remote host didn't return peer.%s for association %d\n",
+ pvl[i].name, associd);
+ return 0;
+ }
+
+
+ /*
+ * Got everything, format the line
+ */
+ poll = 1<<max(min3(ppoll, hpoll, NTP_MAXPOLL), NTP_MINPOLL);
+ if (pktversion == NTP_VERSION)
+ c = flash3[CTL_PEER_STATVAL(rstatus) & 0x7];
+ else
+ c = flash2[CTL_PEER_STATVAL(rstatus) & 0x3];
+ if (numhosts > 1)
+ (void) fprintf(fp, "%-*s ", maxhostlen, currenthost);
+ (void) fprintf(fp,
+ "%c%-15.15s %-15.15s %2ld %c %4.4s %4ld %3lo %7.7s %8.7s %7.7s\n",
+ c, nntohost(srcadr), dstadr_refid, stratum, type,
+ when(&ts, &rec, &reftime), poll, reach,
+ fixup(7, lfptoms(&estdelay, 2)), fixup(8, lfptoms(&estoffset, 3)),
+ fixup(7, lfptoms(&estdisp, 2)));
+ return 1;
+}
+
+#undef HAVE_SRCADR
+#undef HAVE_DSTADR
+#undef HAVE_STRATUM
+#undef HAVE_PPOLL
+#undef HAVE_HPOLL
+#undef HAVE_REACH
+#undef HAVE_ESTDELAY
+#undef HAVE_ESTOFFSET
+#undef HAVE_ESTDISP
+#undef HAVE_REFID
+#undef HAVE_REC
+#undef HAVE_SRCPORT
+#undef HAVE_REFTIME
+#undef MAXHAVE
+
+
+/*
+ * dogetpeers - given an association ID, read and print the spreadsheet
+ * peer variables.
+ */
+static int
+dogetpeers(pvl, associd, fp)
+ struct varlist *pvl;
+ int associd;
+ FILE *fp;
+{
+ char *datap;
+ int res;
+ int dsize;
+ u_short rstatus;
+
+#ifdef notdef
+ res = doquerylist(pvl, CTL_OP_READVAR, associd, 0, &rstatus,
+ &dsize, &datap);
+#else
+ /*
+ * Damn fuzzballs
+ */
+ res = doquery(CTL_OP_READVAR, associd, 0, 0, (char *)0, &rstatus,
+ &dsize, &datap);
+#endif
+
+ if (res != 0)
+ return 0;
+
+ if (dsize == 0) {
+ (void) fprintf(stderr,
+ "***No information returned for association %d\n",
+ associd);
+ return 0;
+ }
+
+
+ return doprintpeers(pvl, associd, (int)rstatus, dsize, datap, fp);
+}
+
+
+/*
+ * peers - print a peer spreadsheet
+ */
+static void
+dopeers(showall, fp)
+ int showall;
+ FILE *fp;
+{
+ register int i;
+ char fullname[LENHOSTNAME];
+ u_long netnum;
+
+
+ if (!dogetassoc(fp))
+ return;
+
+ for (i = 0; i < numhosts; ++i)
+ { if(getnetnum(chosts[i],&netnum,fullname))
+ if ((int)strlen(fullname) > maxhostlen)
+ maxhostlen = strlen(fullname);
+ }
+ if (numhosts > 1)
+ (void) fprintf(fp, "%-*.*s ", maxhostlen, maxhostlen, "host");
+ (void) fprintf(fp,
+" remote refid st t when poll reach delay offset disp\n");
+ if (numhosts > 1)
+ for (i = 0; i <= maxhostlen; ++i)
+ (void) fprintf(fp, "=");
+ (void) fprintf(fp,
+"==============================================================================\n");
+
+ for (i = 0; i < numassoc; i++) {
+ if (!showall &&
+ !(CTL_PEER_STATVAL(assoc_cache[i].status)
+ & (CTL_PST_CONFIG|CTL_PST_REACH)))
+ continue;
+ if (!dogetpeers(peervarlist, (int)assoc_cache[i].assid, fp)) {
+ return;
+ }
+ }
+ return;
+}
+
+
+/*
+ * peers - print a peer spreadsheet
+ */
+/*ARGSUSED*/
+static void
+peers(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ dopeers(0, fp);
+}
+
+
+/*
+ * lpeers - print a peer spreadsheet including all fuzzball peers
+ */
+/*ARGSUSED*/
+static void
+lpeers(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ dopeers(1, fp);
+}
+
+
+/*
+ * opeers - print a peer spreadsheet
+ */
+static void
+doopeers(showall, fp)
+ int showall;
+ FILE *fp;
+{
+ register int i;
+
+ if (!dogetassoc(fp))
+ return;
+
+ (void) fprintf(fp,
+" remote local st t when poll reach delay offset disp\n");
+ (void) fprintf(fp,
+"===========================================================================\n");
+
+ for (i = 0; i < numassoc; i++) {
+ if (!showall &&
+ !(CTL_PEER_STATVAL(assoc_cache[i].status)
+ & (CTL_PST_CONFIG|CTL_PST_REACH)))
+ continue;
+ if (!dogetpeers(opeervarlist, (int)assoc_cache[i].assid, fp)) {
+ return;
+ }
+ }
+ return;
+}
+
+
+/*
+ * opeers - print a peer spreadsheet the old way
+ */
+/*ARGSUSED*/
+static void
+opeers(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ doopeers(0, fp);
+}
+
+
+/*
+ * lopeers - print a peer spreadsheet including all fuzzball peers
+ */
+/*ARGSUSED*/
+static void
+lopeers(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ doopeers(1, fp);
+}
diff --git a/usr.sbin/xntpd/ntptrace/Makefile b/usr.sbin/xntpd/ntptrace/Makefile
new file mode 100644
index 0000000..e1e2bd0
--- /dev/null
+++ b/usr.sbin/xntpd/ntptrace/Makefile
@@ -0,0 +1,28 @@
+#
+# $Id$
+#
+
+CFLAGS+= -I${.CURDIR}/../include
+
+.if exists(${.OBJDIR}/../lib)
+LDADD+= -L${.OBJDIR}/../lib
+DPADD+= ${.OBJDIR}/../lib/libntp.a
+.else
+LDADD+= -L${.CURDIR}/../lib
+DPADD+= ${.CURDIR}/../lib/libntp.a
+.endif
+
+LDADD+= -lntp
+
+PROG= ntptrace
+MAN8= ${.CURDIR}/../doc/ntptrace.8
+CLEANFILES+= .version version.c
+
+SRCS= ntptrace.c version.c
+
+beforedepend: version.c
+
+version.c: ${.CURDIR}/../VERSION
+ sh -e ${.CURDIR}/../scripts/mkversion ntptrace
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/xntpd/ntptrace/README b/usr.sbin/xntpd/ntptrace/README
new file mode 100644
index 0000000..b976cfd
--- /dev/null
+++ b/usr.sbin/xntpd/ntptrace/README
@@ -0,0 +1,7 @@
+README file for directory ./ntptrace of the NTP Version 3 distribution
+
+This directory contains the sources for the ntptrace utility program. See
+the README and RELNOTES files in the parent directory for directions on
+how to make and install this program. The current version number of this
+program is in the version.c file.
+
diff --git a/usr.sbin/xntpd/ntptrace/ntptrace.c b/usr.sbin/xntpd/ntptrace/ntptrace.c
new file mode 100644
index 0000000..206c738
--- /dev/null
+++ b/usr.sbin/xntpd/ntptrace/ntptrace.c
@@ -0,0 +1,772 @@
+/*
+ * ntptrace - show the chain from an NTP host leading back to
+ * its source of time
+ *
+ * Jeffrey Mogul DECWRL 13 January 1993
+ *
+ * Inspired by a script written by Glenn Trewitt
+ *
+ * Large portions stolen from ntpdate.c
+ */
+#include <stdio.h>
+#include <signal.h>
+#include <ctype.h>
+#include <errno.h>
+#include <netdb.h>
+#include <sys/types.h>
+#include <sys/signal.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+
+
+#ifdef __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+#if defined(SYS_HPUX)
+#include <utmp.h>
+#endif
+
+#include "ntp_select.h"
+#include "ntp_fp.h"
+#include "ntp.h"
+#include "ntp_io.h"
+#include "ntp_unixtime.h"
+#include "ntptrace.h"
+#include "ntp_string.h"
+#include "ntp_stdlib.h"
+#include "ntp_syslog.h"
+
+/*
+ * Debugging flag
+ */
+int debug = 0;
+
+int nonames = 0; /* if set, don't print hostnames */
+
+/*
+ * Program name.
+ */
+char *progname;
+
+/*
+ * Systemwide parameters and flags
+ */
+int sys_retries = 5; /* # of retry attempts per server */
+int sys_timeout = 2; /* timeout time, in seconds */
+struct server **sys_servers; /* the server list */
+int sys_numservers = 0; /* number of servers to poll */
+int sys_maxservers = NTP_MAXSTRATUM+1; /* max number of servers to deal with */
+int sys_version = NTP_OLDVERSION; /* version to poll with */
+
+/*
+ * recvbuf lists
+ */
+struct recvbuf *freelist; /* free buffers */
+struct recvbuf *fulllist; /* buffers with data */
+
+int full_recvbufs; /* number of full ones */
+int free_recvbufs;
+
+/*
+ * File descriptor masks etc. for call to select
+ */
+int fd;
+fd_set fdmask;
+
+/*
+ * Miscellaneous flags
+ */
+int verbose = 0;
+int always_step = 0;
+
+extern int errno;
+
+static void DoTrace P((struct server *));
+static void DoTransmit P((struct server *));
+static int DoReceive P((struct server *));
+static int ReceiveBuf P((struct server *, struct recvbuf *));
+static struct server *addserver P((struct in_addr *));
+static struct server *addservbyname P((char *));
+static void setup_io P((void));
+static void freerecvbuf P((struct recvbuf *));
+static void sendpkt P((struct sockaddr_in *, struct pkt *, int));
+static int getipaddr P((char *, long *));
+static int decodeipaddr P((char *, long *));
+static void printserver P((struct server *, FILE *));
+static void printrefid P((FILE *, struct server *));
+
+/*
+ * Main program. Initialize us and loop waiting for I/O and/or
+ * timer expiries.
+ */
+void
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ struct server *firstserver;
+ int errflg;
+ int c;
+ extern char *ntp_optarg;
+ extern int ntp_optind;
+ extern char *Version;
+
+ errflg = 0;
+ progname = argv[0];
+
+ /*
+ * Decode argument list
+ */
+ while ((c = ntp_getopt(argc, argv, "do:nr:t:v")) != EOF)
+ switch (c) {
+ case 'd':
+ ++debug;
+ break;
+ case 'n':
+ nonames = 1;
+ break;
+ case 'o':
+ sys_version = atoi(ntp_optarg);
+ break;
+ case 'r':
+ sys_retries = atoi(ntp_optarg);
+ if (sys_retries < 1) {
+ (void)fprintf(stderr,
+ "%s: retries (%d) too small\n",
+ progname, sys_retries);
+ errflg++;
+ }
+ break;
+ case 't':
+ sys_timeout = atoi(ntp_optarg);
+ if (sys_timeout < 1) {
+ (void)fprintf(stderr,
+ "%s: timeout (%d) too short\n",
+ progname, sys_timeout);
+ errflg++;
+ }
+ break;
+ case 'v':
+ verbose = 1;
+ break;
+ case '?':
+ ++errflg;
+ break;
+ default:
+ break;
+ }
+
+ if (errflg || (argc - ntp_optind) > 1) {
+ (void) fprintf(stderr,
+ "usage: %s [-vnd] [-r retries] [-t timeout] [server]\n",
+ progname);
+ exit(2);
+ }
+
+ sys_servers = (struct server **)
+ emalloc(sys_maxservers * sizeof(struct server *));
+
+ if (debug) {
+#ifdef NTP_POSIX_SOURCE
+ static char buf[BUFSIZ];
+ setvbuf(stdout, buf, _IOLBF, BUFSIZ);
+#else
+ setlinebuf(stdout);
+#endif
+ }
+
+ if (debug || verbose)
+ syslog(LOG_NOTICE, "%s", Version);
+
+ if ((argc - ntp_optind) == 1)
+ firstserver = addservbyname(argv[ntp_optind]);
+ else
+ firstserver = addservbyname("localhost");
+
+ if (firstserver == NULL) {
+ /* a message has already been printed */
+ exit(2);
+ }
+
+ /*
+ * Initialize the time of day routines and the I/O subsystem
+ */
+ setup_io();
+
+ DoTrace(firstserver);
+
+ exit(0);
+}
+
+static void
+DoTrace(server)
+register struct server *server;
+{
+ int retries = sys_retries;
+
+ if (!verbose) {
+ if (nonames)
+ printf("%s: ", ntoa(&server->srcadr));
+ else
+ printf("%s: ", ntohost(&server->srcadr));
+ fflush(stdout);
+ }
+ while (retries-- > 0) {
+ DoTransmit(server);
+ if (DoReceive(server))
+ return;
+ }
+ if (verbose) {
+ if (nonames)
+ printf("%s:\t*Timeout*\n", ntoa(&server->srcadr));
+ else
+ printf("%s:\t*Timeout*\n", ntohost(&server->srcadr));
+ }
+ else
+ printf("\t*Timeout*\n");
+}
+
+/*
+ * Dotransmit - transmit a packet to the given server
+ */
+static void
+DoTransmit(server)
+register struct server *server;
+{
+ struct pkt xpkt;
+
+ if (debug)
+ printf("DoTransmit(%s)\n", ntoa(&server->srcadr));
+
+ /*
+ * Fill in the packet and let 'er rip.
+ */
+ xpkt.li_vn_mode = PKT_LI_VN_MODE(LEAP_NOTINSYNC,
+ sys_version, MODE_CLIENT);
+ xpkt.stratum = STRATUM_TO_PKT(STRATUM_UNSPEC);
+ xpkt.ppoll = NTP_MINPOLL;
+ xpkt.precision = NTPTRACE_PRECISION;
+ xpkt.rootdelay = htonl(NTPTRACE_DISTANCE);
+ xpkt.rootdispersion = htonl(NTPTRACE_DISP);
+ xpkt.refid = htonl(NTPTRACE_REFID);
+ L_CLR(&xpkt.reftime);
+ L_CLR(&xpkt.org);
+ L_CLR(&xpkt.rec);
+
+ /*
+ * just timestamp packet and send it away.
+ */
+ get_systime(&(server->xmt));
+ HTONL_FP(&server->xmt, &xpkt.xmt);
+ sendpkt(&(server->srcadr), &xpkt, LEN_PKT_NOMAC);
+
+ if (debug)
+ printf("DoTransmit to %s\n", ntoa(&(server->srcadr)));
+}
+
+/*
+ * DoReceive - attempt to receive a packet from a specific server
+ */
+static int
+DoReceive(server)
+register struct server *server;
+{
+ register int n;
+ fd_set fds;
+ struct timeval timeout;
+ l_fp ts;
+ register struct recvbuf *rb;
+ int fromlen;
+ int status;
+
+ /*
+ * Loop until we see the packet we want or until we time out
+ */
+ for (;;) {
+ fds = fdmask;
+ timeout.tv_sec = sys_timeout;
+ timeout.tv_usec = 0;
+ n = select(fd+1, &fds, (fd_set *)0, (fd_set *)0, &timeout);
+
+ if (n == 0) { /* timed out */
+ if (debug)
+ printf("timeout\n");
+ return(0);
+ }
+ else if (n == -1) {
+ syslog(LOG_ERR, "select() error: %m");
+ return(0);
+ }
+ get_systime(&ts);
+
+ if (free_recvbufs == 0) {
+ syslog(LOG_ERR, "no buffers");
+ exit(1);
+ }
+
+ rb = freelist;
+ freelist = rb->next;
+ free_recvbufs--;
+
+ fromlen = sizeof(struct sockaddr_in);
+ rb->recv_length = recvfrom(fd, (char *)&rb->recv_pkt,
+ sizeof(rb->recv_pkt), 0,
+ (struct sockaddr *)&rb->srcadr, &fromlen);
+ if (rb->recv_length == -1) {
+ rb->next = freelist;
+ freelist = rb;
+ free_recvbufs++;
+ continue;
+ }
+
+ /*
+ * Got one. Mark how and when it got here,
+ * put it on the full list.
+ */
+ rb->recv_time = ts;
+ rb->next = fulllist;
+ fulllist = rb;
+ full_recvbufs++;
+
+ status = ReceiveBuf(server, rb);
+
+ freerecvbuf(rb);
+
+ return(status);
+ }
+}
+
+/*
+ * receive - receive and process an incoming frame
+ * Return 1 on success, 0 on failure
+ */
+static int
+ReceiveBuf(server, rbufp)
+ struct server *server;
+ struct recvbuf *rbufp;
+{
+ register struct pkt *rpkt;
+ register s_fp di;
+ l_fp t10, t23;
+ l_fp org;
+ l_fp rec;
+ l_fp ci;
+ struct server *nextserver;
+ struct in_addr nextia;
+
+
+ if (debug) {
+ printf("ReceiveBuf(%s, ", ntoa(&server->srcadr));
+ printf("%s)\n", ntoa(&rbufp->srcadr));
+ }
+
+ /*
+ * Check to see if the packet basically looks like something
+ * intended for us.
+ */
+ if (rbufp->recv_length < LEN_PKT_NOMAC) {
+ if (debug)
+ printf("receive: packet length %d\n",
+ rbufp->recv_length);
+ return(0); /* funny length packet */
+ }
+ if (rbufp->srcadr.sin_addr.s_addr != server->srcadr.sin_addr.s_addr) {
+ if (debug)
+ printf("receive: wrong server\n");
+ return(0); /* funny length packet */
+ }
+
+ rpkt = &(rbufp->recv_pkt);
+
+ if (PKT_VERSION(rpkt->li_vn_mode) < NTP_OLDVERSION) {
+ if (debug)
+ printf("receive: version %d\n", PKT_VERSION(rpkt->li_vn_mode));
+ return(0);
+ }
+ if (PKT_VERSION(rpkt->li_vn_mode) > NTP_VERSION) {
+ if (debug)
+ printf("receive: version %d\n", PKT_VERSION(rpkt->li_vn_mode));
+ return(0);
+ }
+
+ if ((PKT_MODE(rpkt->li_vn_mode) != MODE_SERVER
+ && PKT_MODE(rpkt->li_vn_mode) != MODE_PASSIVE)
+ || rpkt->stratum > NTP_MAXSTRATUM) {
+ if (debug)
+ printf("receive: mode %d stratum %d\n",
+ PKT_MODE(rpkt->li_vn_mode), rpkt->stratum);
+ return(0);
+ }
+
+ /*
+ * Decode the org timestamp and make sure we're getting a response
+ * to our last request.
+ */
+ NTOHL_FP(&rpkt->org, &org);
+ if (!L_ISEQU(&org, &server->xmt)) {
+ if (debug)
+ printf("receive: pkt.org and peer.xmt differ\n");
+ return(0);
+ }
+
+ /*
+ * Looks good. Record info from the packet.
+ */
+ server->leap = PKT_LEAP(rpkt->li_vn_mode);
+ server->stratum = PKT_TO_STRATUM(rpkt->stratum);
+ server->precision = rpkt->precision;
+ server->rootdelay = ntohl(rpkt->rootdelay);
+ server->rootdispersion = ntohl(rpkt->rootdispersion);
+ server->refid = rpkt->refid;
+ NTOHL_FP(&rpkt->reftime, &server->reftime);
+ NTOHL_FP(&rpkt->rec, &rec);
+ NTOHL_FP(&rpkt->xmt, &server->org);
+
+ /*
+ * Make sure the server is at least somewhat sane. If not, try
+ * again.
+ */
+ if (L_ISZERO(&rec) || !L_ISHIS(&server->org, &rec)) {
+ return(0);
+ }
+
+ /*
+ * Calculate the round trip delay (di) and the clock offset (ci).
+ * We use the equations (reordered from those in the spec):
+ *
+ * d = (t2 - t3) - (t1 - t0)
+ * c = ((t2 - t3) + (t1 - t0)) / 2
+ */
+ t10 = server->org; /* pkt.xmt == t1 */
+ L_SUB(&t10, &rbufp->recv_time); /* recv_time == t0*/
+
+ t23 = rec; /* pkt.rec == t2 */
+ L_SUB(&t23, &org); /* pkt->org == t3 */
+
+ /* now have (t2 - t3) and (t0 - t1). Calculate (ci) and (di) */
+ ci = t10;
+ L_ADD(&ci, &t23);
+ L_RSHIFT(&ci);
+
+ /*
+ * Calculate di in t23 in full precision, then truncate
+ * to an s_fp.
+ */
+ L_SUB(&t23, &t10);
+ di = LFPTOFP(&t23);
+
+ server->offset = ci;
+ server->delay = di;
+
+ printserver(server, stdout);
+
+ /* End of recursion if we reach stratum 1 */
+ if (server->stratum <= 1)
+ return(1);
+
+ nextia.s_addr = server->refid;
+ nextserver = addserver(&nextia);
+ DoTrace(nextserver);
+ return(1);
+}
+
+/* XXX ELIMINATE addserver (almost) identical to ntpdate.c, ntptrace.c */
+/*
+ * addserver - Allocate a new structure for server.
+ * Returns a pointer to that structure.
+ */
+static struct server *
+addserver(iap)
+struct in_addr *iap;
+{
+ register struct server *server;
+ static int toomany = 0;
+
+ if (sys_numservers >= sys_maxservers) {
+ if (!toomany) {
+ toomany = 1;
+ syslog(LOG_ERR,
+ "too many servers (> %d) specified, remainder not used",
+ sys_maxservers);
+ }
+ return(NULL);
+ }
+
+ server = (struct server *)emalloc(sizeof(struct server));
+ memset((char *)server, 0, sizeof(struct server));
+
+ server->srcadr.sin_family = AF_INET;
+ server->srcadr.sin_addr = *iap;
+ server->srcadr.sin_port = htons(NTP_PORT);
+
+ sys_servers[sys_numservers++] = server;
+
+ return(server);
+}
+/*
+ * addservbyname - determine a server's address and allocate a new structure
+ * for it. Returns a pointer to that structure.
+ */
+static struct server *
+addservbyname(serv)
+ char *serv;
+{
+ long ipaddr;
+ struct in_addr ia;
+
+ if (!getipaddr(serv, &ipaddr)) {
+ syslog(LOG_ERR, "can't find host %s\n", serv);
+ return(NULL);
+ }
+
+ ia.s_addr = ipaddr;
+ return(addserver(&ia));
+}
+
+/* XXX ELIMINATE getrecvbufs (almost) identical to ntpdate.c, ntptrace.c, ntp_io.c */
+/*
+ * setup_io - initialize I/O data and open socket
+ */
+static void
+setup_io()
+{
+ register int i;
+ register struct recvbuf *rb;
+
+ /*
+ * Init buffer free list and stat counters
+ */
+ rb = (struct recvbuf *)
+ emalloc((sys_maxservers + 2) * sizeof(struct recvbuf));
+ freelist = 0;
+ for (i = sys_maxservers + 2; i > 0; i--) {
+ rb->next = freelist;
+ freelist = rb;
+ rb++;
+ }
+
+ fulllist = 0;
+ full_recvbufs = 0;
+ free_recvbufs = sys_maxservers + 2;
+
+ /* create a datagram (UDP) socket */
+ if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+ syslog(LOG_ERR, "socket() failed: %m");
+ exit(1);
+ /*NOTREACHED*/
+ }
+
+ FD_ZERO(&fdmask);
+ FD_SET(fd, &fdmask);
+}
+
+/* XXX ELIMINATE freerecvbuf (almost) identical to ntpdate.c, ntptrace.c, ntp_io.c */
+/*
+ * freerecvbuf - make a single recvbuf available for reuse
+ */
+static void
+freerecvbuf(rb)
+ struct recvbuf *rb;
+{
+ rb->next = freelist;
+ freelist = rb;
+ free_recvbufs++;
+}
+
+
+/* XXX ELIMINATE sendpkt similar in ntpq.c, ntpdc.c, ntp_io.c, ntptrace.c */
+/*
+ * sendpkt - send a packet to the specified destination
+ */
+static void
+sendpkt(dest, pkt, len)
+ struct sockaddr_in *dest;
+ struct pkt *pkt;
+ int len;
+{
+ int cc;
+
+ cc = sendto(fd, (char *)pkt, len, 0, (struct sockaddr *)dest,
+ sizeof(struct sockaddr_in));
+ if (cc == -1) {
+ if (errno != EWOULDBLOCK && errno != ENOBUFS)
+ syslog(LOG_ERR, "sendto(%s): %m", ntoa(dest));
+ }
+}
+
+/*
+ * getipaddr - given a host name, return its host address
+ */
+static int
+getipaddr(host, num)
+ char *host;
+ long *num;
+{
+ struct hostent *hp;
+
+ if (decodeipaddr(host, num)) {
+ return 1;
+ } else if ((hp = gethostbyname(host)) != 0) {
+ memmove((char *)num, hp->h_addr, sizeof(long));
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * decodeipaddr - return a host address (this is crude, but careful)
+ */
+static int
+decodeipaddr(num, ipaddr)
+ char *num;
+ long *ipaddr;
+{
+ register char *cp;
+ register char *bp;
+ register int i;
+ register int temp;
+ char buf[80]; /* will core dump on really stupid stuff */
+
+ cp = num;
+ *ipaddr = 0;
+ for (i = 0; i < 4; i++) {
+ bp = buf;
+ while (isdigit(*cp))
+ *bp++ = *cp++;
+ if (bp == buf)
+ break;
+
+ if (i < 3) {
+ if (*cp++ != '.')
+ break;
+ } else if (*cp != '\0')
+ break;
+
+ *bp = '\0';
+ temp = atoi(buf);
+ if (temp > 255)
+ break;
+ *ipaddr <<= 8;
+ *ipaddr += temp;
+ }
+
+ if (i < 4)
+ return 0;
+ *ipaddr = htonl(*ipaddr);
+ return 1;
+}
+
+
+/* XXX ELIMINATE printserver similar in ntptrace.c, ntpdate.c */
+/*
+ * printserver - print detail information for a server
+ */
+static void
+printserver(pp, fp)
+ register struct server *pp;
+ FILE *fp;
+{
+ u_fp synchdist;
+
+ synchdist = pp->rootdispersion + (pp->rootdelay/2);
+
+ if (!verbose) {
+ (void) fprintf(fp, "stratum %d, offset %s, synch distance %s",
+ pp->stratum,
+ lfptoa(&pp->offset, 6),
+ ufptoa(synchdist, 5));
+ if (pp->stratum == 1) {
+ (void) fprintf(fp, ", refid ");
+ printrefid(fp, pp);
+ }
+ (void) fprintf(fp, "\n");
+ return;
+ }
+
+ (void) fprintf(fp, "server %s, port %d\n",
+ ntoa(&pp->srcadr), ntohs(pp->srcadr.sin_port));
+
+ (void) fprintf(fp, "stratum %d, precision %d, leap %c%c\n",
+ pp->stratum, pp->precision,
+ pp->leap & 0x2 ? '1' : '0',
+ pp->leap & 0x1 ? '1' : '0');
+
+ (void) fprintf(fp, "refid ");
+ printrefid(fp, pp);
+
+ (void) fprintf(fp,
+ " delay %s, dispersion %s ",
+ fptoa(pp->delay, 5),
+ ufptoa(pp->dispersion, 5));
+ (void) fprintf(fp, "offset %s\n",
+ lfptoa(&pp->offset, 6));
+ (void) fprintf(fp, "rootdelay %s, rootdispersion %s",
+ fptoa(pp->rootdelay, 5), ufptoa(pp->rootdispersion, 5));
+ (void) fprintf(fp, ", synch dist %s\n",
+ ufptoa(synchdist, 5));
+
+ (void) fprintf(fp, "reference time: %s\n",
+ prettydate(&pp->reftime));
+ (void) fprintf(fp, "originate timestamp: %s\n",
+ prettydate(&pp->org));
+ (void) fprintf(fp, "transmit timestamp: %s\n",
+ prettydate(&pp->xmt));
+
+ (void) fprintf(fp, "\n");
+
+}
+
+static void
+printrefid(fp, pp)
+FILE *fp;
+struct server *pp;
+{
+ char junk[5];
+ char *str;
+
+ if (pp->stratum == 1) {
+ junk[4] = 0;
+ memmove(junk, (char *)&pp->refid, 4);
+ str = junk;
+ (void) fprintf(fp, "'%s'", str);
+ } else {
+ if (nonames) {
+ str = numtoa(pp->refid);
+ (void) fprintf(fp, "[%s]", str);
+ }
+ else {
+ str = numtohost(pp->refid);
+ (void) fprintf(fp, "%s", str);
+ }
+ }
+}
+
+#if defined(NEED_VSPRINTF)
+/*
+ * This nugget for pre-tahoe 4.3bsd systems
+ */
+#if !defined(__STDC__) || !__STDC__
+#define const
+#endif
+
+int
+vsprintf(str, fmt, ap)
+ char *str;
+ const char *fmt;
+ va_list ap;
+{
+ FILE f;
+ int len;
+
+ f._flag = _IOWRT+_IOSTRG;
+ f._ptr = str;
+ f._cnt = 32767;
+ len = _doprnt(fmt, ap, &f);
+ *f._ptr = 0;
+ return (len);
+}
+#endif
diff --git a/usr.sbin/xntpd/ntptrace/ntptrace.h b/usr.sbin/xntpd/ntptrace/ntptrace.h
new file mode 100644
index 0000000..65b72fb
--- /dev/null
+++ b/usr.sbin/xntpd/ntptrace/ntptrace.h
@@ -0,0 +1,36 @@
+/*
+ * ntptrace.h - declarations for the ntptrace program
+ */
+
+/*
+ * The server structure is a much simplified version of the
+ * peer structure, for ntptrace's use. Since we always send
+ * in client mode and expect to receive in server mode, this
+ * leaves only a very limited number of things we need to
+ * remember about the server.
+ */
+struct server {
+ struct sockaddr_in srcadr; /* address of remote host */
+ u_char leap; /* leap indicator */
+ u_char stratum; /* stratum of remote server */
+ s_char precision; /* server's clock precision */
+ u_fp rootdelay; /* distance from primary clock */
+ u_fp rootdispersion; /* peer clock dispersion */
+ U_LONG refid; /* peer reference ID */
+ l_fp reftime; /* time of peer's last update */
+ l_fp org; /* peer's originate time stamp */
+ l_fp xmt; /* transmit time stamp */
+ u_fp delay; /* filter estimated delay */
+ u_fp dispersion; /* filter estimated dispersion */
+ l_fp offset; /* filter estimated clock offset */
+};
+
+
+/*
+ * Since ntptrace isn't aware of some of the things that normally get
+ * put in an NTP packet, we fix some values.
+ */
+#define NTPTRACE_PRECISION (-6) /* use this precision */
+#define NTPTRACE_DISTANCE FP_SECOND /* distance is 1 sec */
+#define NTPTRACE_DISP FP_SECOND /* so is the dispersion */
+#define NTPTRACE_REFID (0) /* reference ID to use */
diff --git a/usr.sbin/xntpd/parse/Makefile b/usr.sbin/xntpd/parse/Makefile
new file mode 100644
index 0000000..8162377
--- /dev/null
+++ b/usr.sbin/xntpd/parse/Makefile
@@ -0,0 +1,19 @@
+#
+# $Id$
+#
+
+PARSEFLAGS= -DCLOCK_SCHMID -DCLOCK_DCF7000 -DCLOCK_MEINBERG \
+ -DCLOCK_RAWDCF -DCLOCK_TRIMSV6
+
+CFLAGS+= -I${.CURDIR}/../include ${PARSEFLAGS}
+
+SRCS= parse.c parse_conf.c clk_meinberg.c clk_schmid.c clk_rawdcf.c \
+ clk_dcf7000.c clk_trimble.c
+
+NOMAN=
+NOPROFILE=
+LIB= parse
+
+install:
+
+.include <bsd.lib.mk>
diff --git a/usr.sbin/xntpd/parse/README b/usr.sbin/xntpd/parse/README
new file mode 100644
index 0000000..aa83aa7
--- /dev/null
+++ b/usr.sbin/xntpd/parse/README
@@ -0,0 +1,100 @@
+PARSE reference clock driver:
+
+This directory contains the files making up the parser for
+the parse refclock driver. For reasonably sane clocks this refclock
+drivers allows a refclock implementation by just providing a
+conversion routine and the appropriate NTP parameters. Refclock
+support can run as low a 3k code with the parse refclock driver.
+
+The modules in here are designed to live in two worlds. In userlevel
+as part of the xntp daemon and in kernel land as part of a STREAMS module
+or, if someone gets to it, as part of a line discipline. Currently only
+SunOS4.x/SunOS5.x STREAMS are supported (volunteers for other vendors like HP?).
+This structure means, that refclock_parse can work with or without kernel
+support. Kernelsupport increases accuracy tremendingly. The current restriction
+of the parse driver is that it only supports SYSV type ttys and that kernel
+support is only available for Suns right now.
+
+Three kernel modules are part of this directory. These work only on
+SunOS (SunOS4 and SunOS5 (not fully tested!)).
+
+ SunOS4 (aka Solaris 1.x):
+ parsestreams.o - standard parse module for SunOS 4
+ mparsestreams.o - parse module for SunOS 4 with better
+ clock reading code (assembler)
+
+ Both modules can be loaded via modload <modulename>.
+
+ SunOS5 (aka Solaris 2.x):
+ parse - auto loadable streams module
+ (not fully tested - don't kill me if
+ it kills you machine)
+
+ To install just drop "parse" into /kernel/strmod and
+ start the daemon (SunOS5 will do the rest).
+
+The structure of the parse reference clock driver is as follows:
+
+ xntpd - contains NTP implementation and calls a reference clock
+ 127.127.8.x which is implemented by
+ refclock_parse.c
+ - which contains several refclock decriptions. These are
+ selected by the x part of the refclock address.
+ The lower two bits specify the device to use. Thus the
+ value (x % 4) determines the device to open
+ (/dev/refclock-0 - /dev/refclock-3).
+
+ The kind of clock is selected by bits 2-6. This parameter
+ selects the clock type which deterimines how I/O is done,
+ the tty parameters and the NTP parameters.
+
+ refclock_parse operates on an abstract reference clock
+ that delivers time stamps and stati. Offsets and sychron-
+ isation information is derived from this data and passed
+ on to refclock_receive of xntp which uses that data for
+ syncronisation.
+
+ The abstract reference clock is generated by the parse*
+ routines. They parse the incoming data stream from the
+ clock and convert it to the appropriate time stamps.
+ The data is also mapped int the abstract clock states
+ POWERUP - clock has no valid phase and time code
+ information
+
+ NOSYNC - Time code is not confirmed, phase is probably
+ ok.
+ SYNC - Time code and phase are correct.
+
+ A clock is trusted for a certain time (type parameter) when
+ it leaves the SYNC state. This is derived from the
+ observation that quite a few clocks can still generate good
+ time code information when losing contact to their
+ synchronisation source. When the clock does not reagain
+ synchronisation in that trust period it will be deemed
+ unsynchronised until it regains synchronisation. The same
+ will happen if xntp sees the clock unsynchronised at
+ startup.
+
+ The upper bit of x specifies that all samples delivered
+ from the clock should be used to discipline the NTP
+ loppfilter. For clock with accurate once a second time
+ information this means big improvements for time keeping.
+ A prerequisite for passing on the time stamps to
+ the loopfilter is, that the clock is in synchronised state.
+
+ parse.c These are the general routines to parse the incoming data
+ stream. Usually these routines should not require
+ modification.
+
+ clk_*.c These files hole the conversion code for the time stamps
+ and the description how the time code can be parsed and
+ where the time stamps are to be taken.
+ If you want to add a new clock type this is the file
+ you need to write in addition to mention it in
+ parse_conf.c and setting up the NTP and TTY parameters
+ in refclock_parse.c.
+
+Further information can be found in parse/README.parse and the various source
+files.
+
+Frank Kardel
diff --git a/usr.sbin/xntpd/parse/README.new_clocks b/usr.sbin/xntpd/parse/README.new_clocks
new file mode 100644
index 0000000..5b2d29e
--- /dev/null
+++ b/usr.sbin/xntpd/parse/README.new_clocks
@@ -0,0 +1,212 @@
+Here is an attempt to sketch out what you need to do in order to
+add another clock to the parse driver:
+
+Prerequisites:
+- Does the system you want the clock connect to have
+ termio.h or termios.h ? (You need that for the parse driver)
+
+What to do:
+
+Make a conversion module (parse/clk_*.c)
+
+- What ist the time code format ?
+ - find year, month, day, hour, minute, second, status (synchronised or
+ not), possibly time zone information (you need to give the offset to UTC)
+ You will have to convert the data from a string into a struct clocktime:
+ struct clocktime /* clock time broken up from time code */
+ {
+ long day;
+ long month;
+ long year;
+ long hour;
+ long minute;
+ long second;
+ long usecond;
+ long utcoffset; /* in seconds */
+ time_t utcoffset; /* true utc time instead of date/time */
+ long flags; /* current clock status */
+ };
+
+ Conversion is usually simple and straight forward. For the flags following
+ values can be OR'ed together:
+
+ PARSEB_ANNOUNCE switch time zone warning (informational only)
+ PARSEB_POWERUP no synchronisation - clock confused (must set then)
+ PARSEB_NOSYNC timecode currently not confirmed (must set then)
+ usually on reception error when there is still a
+ chance the the generated time is still ok.
+
+ PARSEB_DST DST in effect (informational only)
+ PARSEB_UTC timecode contains UTC time (informational only)
+ PARSEB_LEAPADD LEAP addition warning (prior to leap happening - must set when imminent)
+ also used for time code that do not encode the
+ direction (as this is currently the default).
+ PARSEB_LEAPDEL LEAP deletion warning (prior to leap happening - must set when imminent)
+ PARSEB_ALTERNATE backup transmitter (informational only)
+ PARSEB_POSITION geographic position available (informational only)
+ PARSEB_LEAPSECOND actual leap second (this time code is the leap
+ second - informational only)
+
+ These are feature flags denoting items that are supported by the clock:
+ PARSEB_S_LEAP supports LEAP - might set PARSEB_LEAP
+ PARSEB_S_ANTENNA supports ANTENNA - might set PARSEB_ALTERNATE
+ PARSEB_S_PPS supports PPS time stamping
+ PARSEB_S_POSITION supports position information (GPS)
+
+ If the utctime field is non zero this value will be take as
+ time code value. This allows for conversion routines that
+ already have the utc time value. The utctime field gives the seconds
+ since Jan 1st 1970, 0:00:00. The useconds field gives the respective
+ usec value. The fields for date and time (down to second resolution)
+ will be ignored.
+
+ Conversion is done in the cvt_* routine in parse/clk_*.c files. look in
+ them for examples. The basic structure is:
+
+ struct clockformat <yourclock>_format = {
+ lots of fields for you to fill out (see below)
+ };
+
+ static cvt_<yourclock>()
+ ...
+ {
+ if (<I do not recognize my time code>) {
+ return CVT_NONE;
+ } else {
+ if (<conversion into clockformat is ok>) {
+ <set all necessary flags>;
+ return CVT_OK;
+ } else {
+ return CVT_FAIL|CVT_BADFMT;
+ }
+ }
+
+ The struct clockformat is the interface to the rest of the parse
+ driver - it holds all information necessary for finding the
+ clock message and doing the appropriate time stamping.
+
+struct clockformat
+{
+ u_long (*convert)();
+ /* conversion routine - your routine - cvt_<yourclock> */
+ void (*syncevt)();
+ /* routine for handling RS232 sync events (time stamps) - usually sync_simple */
+ u_long (*syncpps)();
+ /* PPS input routine - usually pps_simple */
+ u_long (*synth)();
+ /* time code synthesizer - usually not used - (long (*)())0 */
+ void *data;
+ /* local parameters - any parameters/data/configuration info your conversion
+ routine might need */
+ char *name;
+ /* clock format name - Name of the time code */
+ unsigned short length;
+ /* maximum length of data packet for your clock format */
+ u_long flags;
+ /* information for the parser what to look for */
+ struct timeval timeout;
+ /* buffer restart after timeout (us) - some clocks preceede new data by
+ a longer period of silence - unsually not used */
+ unsigned char startsym;
+ /* start symbol - character at the beginning of the clock data */
+ unsigned char endsym;
+ /* end symbol - character at the end of the clock data */
+ unsigned char syncsym;
+ /* sync symbol - character that is "on time" - where the time stamp should be taken */
+};
+
+ The flags:
+ F_START use startsym to find the beginning of the clock data
+ F_END use endsym to find the end of the clock data
+ SYNC_TIMEOUT packet restart after timeout in timeout field
+ SYNC_START packet start is sync event (time stamp at paket start)
+ SYNC_END packet end is sync event (time stamp at paket end)
+ SYNC_CHAR special character (syncsym) is sync event
+ SYNC_ONE PPS synchronize on 'ONE' transition
+ SYNC_ZERO PPS synchronize on 'ZERO' transition
+ SYNC_SYNTHESIZE generate intermediate time stamps (very special case!)
+ CVT_FIXEDONLY convert only in fixed configuration - (data format not
+ suitable for auto-configuration)
+
+
+ The above should have given you some hints on how to build a clk_*.c
+ file with the time code conversion. See the examples and pick a clock
+ closest to yours and tweak the code to match your clock.
+
+ In order to make your clk_*.c file usable a reference to the clockformat
+ structure must be put into parse_conf.c.
+
+TTY setup and initialisation/configuration will be done in
+xntpd/refclock_parse.c
+
+- Find out the exact tty settings for your clock (baud rate, parity,
+ stop bits, character size, ...) and note them in terms of
+ termio*.h c_cflag macros.
+
+- in xntpd/refclock_parse.c fill out a new the struct clockinfo element
+ (that allocates a new "IP" address - see comments)
+ (see all the other clocks for example)
+ struct clockinfo
+ {
+ u_long cl_flags; /* operation flags (io modes) */
+ PARSE_F_NOPOLLONLY always do async io - read whenever input comes
+ PARSE_F_POLLONLY never do async io - only read when expecting data
+ PARSE_F_PPSPPS use loopfilter PPS code (CIOGETEV)
+ PARSE_F_PPSONSECOND PPS pulses are on second
+ usually flags stay 0 as they are used only for special setups
+
+ void (*cl_poll)(); /* active poll routine */
+ The routine to call when the clock needs data sent to it in order to
+ get a time code from the clock (e.g. Trimble clock)
+ int (*cl_init)(); /* active poll init routine */
+ The routine to call for very special initializations.
+ void (*cl_end)(); /* active poll end routine */
+ The routine to call to undo any special initialisation (free memory/timers)
+ void *cl_data; /* local data area for "poll" mechanism */
+ local data for polling routines
+ u_fp cl_rootdelay; /* rootdelay */
+ NTP rottdelay estimate (usually 0)
+ u_long cl_basedelay; /* current offset - unsigned l_fp fractional par
+ time (fraction) by which the RS232 time code is delayed from the actual time.
+ t */
+ u_long cl_ppsdelay; /* current PPS offset - unsigned l_fp fractional
+ time (fraction) by which the PPS time stamp is delayed (usually 0)
+ part */
+ char *cl_id; /* ID code (usually "DCF") */
+ Refclock id - (max 4 chars)
+ char *cl_description; /* device name */
+ Name of this device.
+ char *cl_format; /* fixed format */
+ If the data format cann not ne detected automatically this is the name
+ as in clk_*.c clockformat.
+ u_char cl_type; /* clock type (ntp control) */
+ Type if clock as in clock status word (ntp control messages) - usually 0
+ u_long cl_maxunsync; /* time to trust oscillator after loosing synch
+ */
+ seconds a clock can be trusted after loosing synchronisation.
+
+ u_long cl_cflag; /* terminal io flags */
+ u_long cl_iflag; /* terminal io flags */
+ u_long cl_oflag; /* terminal io flags */
+ u_long cl_lflag; /* terminal io flags */
+ termio*.h tty modes.
+ } clockinfo[] = {
+ ...,<other clocks>,...
+ { < your parameters> },
+ };
+
+
+Well, this is very sketchy, i know. But I hope it helps a little bit.
+The best way is to look which clock comes closest to your and tweak that
+code.
+Two sorts of clocks are used with parse. Clocks that automatically send
+their time code (once a second) do not need entries in the poll routines because
+they send the data all the time. The second sort are the clocks that need a
+command sent to them in order to reply with a time code (like the Trimble
+clock).
+
+For questions: kardel@informatik.uni-erlangen.de. Please include
+an exact description on how your clock works. (initialisation,
+TTY modes, strings to be sent to it, responses received from the clock).
+
+Frank Kardel
diff --git a/usr.sbin/xntpd/parse/README.parse b/usr.sbin/xntpd/parse/README.parse
new file mode 100644
index 0000000..660973a
--- /dev/null
+++ b/usr.sbin/xntpd/parse/README.parse
@@ -0,0 +1,142 @@
+MINI INFO:
+The following info pertains mainly to SunOS4.x in respect to installation.
+Installation for SunOS5.x (Solaris 2.x) is very simple - just drop the parse
+module into /kernel/strmod.
+All others notes about the software structure refer to both environments.
+
+#ifdef ENGLISH
+Installation of a Streams module requires knowledge in kernel generation
+and possession of "superuser" rights.
+
+This directory contains the STREAMS module code for the supported DCF/GPS
+receivers of the "parse" driver.
+The dataformat should be easy to adept for other clocks.
+
+A suitable kernel module can be generated in two ways:
+ 1) loadable driver
+ 2) linking into the kernel
+
+Solution 1 has the advantage that the kernel module is present right at system startup,
+while solution 2 avoids reconfigurating the kernel (except for VDDRV).
+
+Loadable Driver: (Kernel must be configured with VDDRV option like e.g. GENERIC)
+ make -f Makefile.kernel
+
+# make one module for each kernel architecture you intend to use this module for
+
+ make -f Makefile.kernel mparsestreams.o
+# use the above command for a version with increased time stamp precision
+# (available only for sun4c and sun4m architectures (thanks Craig Leres)
+
+Integration into kernel (refer to the Manual for complete instructions)
+ Still possible, but not recommended
+
+if you run into trouble: time@informatik.uni-erlangen.de
+
+Porting to different clock formats:
+The streams module is designed to be able to parse different time code
+packets. The parser is very simple and expects at least a start or end of packet
+character. In order to be able to distinguish time code packets a list
+of several start/end pairs and conversion routines can be defined in the
+clockformats structure. Whenever a packet delimited by any start/end pair is
+detected the conversion routines are called in a RR fashion for converting the
+time code into a clocktime structure. A return code of CVT_OK indicates a
+correct conversion.
+(This routine will be called first on the next conversion attempt). CVT_FAIL
+indicates the the packet format was detected, but the actual conversion failed
+(e.g. illegal time codes). A CVT_NONE indicates that this conversion routine
+did not recognize the packet format.
+See the simpleformat conversion routines for Meinberg clocks for examples.
+It might be possible to parse other periodically sent time codes with a fixed
+format with these simple conversion routines.
+The parser can be found in parse/*.c
+
+The actual STREAMS module is parsestreams.c. It contains some fudge factors.
+These are needed if a PPS hardware signal is sampled via the serial CD input.
+There are some emperically determined valued for sun4c type machine in there.
+Measurements have shown, that for full precision these values have to be
+determined in the actual environment, as line lengths and capacities DO matter.
+So for absolute precision you need a good oscilloscope and the license for
+hardware work.
+WARNING: DO NOT ATTEMPT TO MEASURE IF YOU ARE NOT ABSOLUTELY CERTAIN WHAT YOU
+ARE DOING.
+
+This instructions are distributed in the hope that they will be
+useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+
+I will not be in any case responsible for any damage cause by this
+Software/Instruction.
+
+USE AT YOUR OWN RISK.
+
+#else
+
+Die folgenden Hinweise zur Uebersetzung und Installation besiehen sich auf
+SunOS 4.x (Solaris1.x). Die Installation auf SunOS5.x (Solaris 2.x) gestaltet
+sich erheblich einfacher. Man muss nur die Daten "parse" in dem Verzeichnis
+/kernel/strmod ablegen.
+Alle anderen Hinweise zur Softwarestruktur sind fuer beide Umgebungen gueltig.
+
+Installation eines STREAMS Moduls setzt Kenntnisse in der Kerngenerierung
+und "Superuser"-Rechte vorraus.
+
+Dieses Inhaltsverzeichnis enthaelt das aktuelle Streams Modul fuer Sun.
+
+Man kann dieses Modul auf zwei Weisen in den Kern integrieren:
+ 1) direkt durch Einbinden (neuer Kern)
+ 2) als ladbarer Treiber
+
+Loesung 1 hat den Vorteil, dass das Modul gleich nach Systemstart zur
+Verfuegung steht.
+Loesung 2 besticht dadurch, das man das Modul nachtraeglich laden und
+auch debuggen kann, ohne einen neuen Kern zu booten.
+
+Fuer ein ladbares Modul muss der Kern mit der VDDRV option konfiguriert sein und das
+parsestreams.c muss mit -DVDDRV uebersetzt werden.
+
+Uebersetzung fuer ladbaren Treiber (Kern muss mit VDDRV konfiguriert sein):
+ make -f Makefile.kernel
+ bitte einmal fuer jede Kernelarchitektur, fuer die dieses Modul
+ benoetigt wird durchfuehren.
+
+ make -f Makefile.kernel mparsestreams.o
+ Das obige make erstellt eine Version, die die Rechneruhr besser
+ als SunOS abliest. Nur fur sun4c und sun4m Architekturen verfuegbar
+
+Uebersetzung als .o Modul oder vorherige Einbindung in die Kernbauumgebung:
+ Immer noch moeglich, wird aber nicht mehr empfohlen.
+
+Anpassung an andere Datenformate:
+Das Streamsmodul ist in der Lage verschiedene Datenformate zu erkennen und
+umzusetzen. Der Parser ist einfach gehalten und kann Datenpakete anhand von
+Start und Endekennzeichen unterscheiden. Jedes so erkannte Paket wird einer
+Liste von Konvertierroutinen vorgelegt (clockformats Struktur). Die
+Konvertierroutinen koennen mit drei verschiedenen Rueckgabewerten angeben,
+wie die Konvertierung verlaufen ist. CVT_OK heisst, dass die Konvertierung
+in die clocktime Struktur erfolgreich verlaufen ist. Beim naechsten
+Umsetzungsversuch wird diese Routine als erstes wieder befragt werden
+(Optimierung). CVT_FAIL bedeutet, dass zwar das Format erkannt wurde, aber
+die eigentliche Konvertierung fehlgeschlagen ist (z. B. illegale Feldwerte).
+CVT_NONE heisst, dass das Format dieser Konvertierroutine nicht erkannt wurde.
+Die simpleformat Routinen fuer Meinberg Uhren koennen als Vorlage fuer eigene
+Anpassungen an Uhren mit periodischem Zeittelegramm und festem Format genommen werden.
+Der Parser ist in parse/*.c zu finden.
+
+Das eigentliche STREAMSmodul ist parsestreams.c. Es enthaelt einige
+Korrekturfaktoren, die beim Einsatz von Hardware-PPS Signalen benoetigt werden.
+Einige empirische Werte fuer sun4c Maschinen sind schon vorgegeben. Bei exterm
+hohen Genauigkeitsanforderungen muessen diese Werte aber in der aktuellen
+Installation NEU ermittelt werden, weil die Zeiten unter anderem von
+Leitunglaengen der PPS Leitung abhaengen. Wenn Sie diese Abstimmung
+durchfuehren, benoetigen Sie ein gutes Oszilloskop und die Lizenz fuer
+Hardwarearbeiten.
+
+ACHTUNG: VERSUCHEN SIE NICHT DIESE MESSUNGEN ZU MACHEN, WENN IHNEN DIE
+VORAUSSETZUNGEN DAFUER FEHLEN !
+
+WIR GEBEN KEINE GARANTIEN
+
+Bei Schwierigkeiten email an: time@informatik.uni-erlangen.de
+
+#endif
diff --git a/usr.sbin/xntpd/parse/README.parse_clocks b/usr.sbin/xntpd/parse/README.parse_clocks
new file mode 100644
index 0000000..a3c5a80
--- /dev/null
+++ b/usr.sbin/xntpd/parse/README.parse_clocks
@@ -0,0 +1,264 @@
+The parse driver currently supports several clocks with different
+query mechanisms. In order for you to find a sample that might be
+similar to a clock you might want to integrate into parse i'll sum
+up the major features of the clocks (this information is distributed
+in the parse/clk_*.c and xntpd/refclock_parse.c files).
+
+---
+ Meinberg: 127.127.8. 0- 3 (PZF535TCXO)
+ 127.127.8. 4- 7 (PZF535OCXO)
+ 127.127.8. 8-11 (DCFUA31)
+ 127.127.8.28-31 (GPS166)
+ Meinberg: start=<STX>, end=<ETX>, sync on start
+ pattern="\2D: . . ;T: ;U: . . ; \3"
+ pattern="\2 . . ; ; : : ; \3"
+ pattern="\2 . . ; ; : : ; : ; ; . . "
+
+ Meinberg is a german manufacturer of time code receivers. Those clocks
+ have a pretty common output format in the stock version. In order to
+ support NTP Meinberg was so kind to produce some special versions of
+ the firmware for the use with NTP. So, if you are going to use a
+ Meinberg clock please ask whether there is a special Uni Erlangen
+ version.
+
+ General characteristics:
+ Meinberg clocks primarily output pulse per second and a describing
+ ASCII string. This string can be produced in two modes. either upon
+ the reception of a question mark or every second. NTP uses the latter
+ mechanism. The DCF77 variants have a pretty good relationship between
+ RS232 time code and the PPS signal while the GPS receiver has no fixed
+ timeing between the datagram and the pulse (you need to use PPS with
+ GPS!) on DCF77 you might get away without the PPS signal.
+
+ The preferred tty setting for Meinberg is:
+ CFLAG (B9600|CS7|PARENB|CREAD|HUPCL)
+ IFLAG (IGNBRK|IGNPAR|ISTRIP)
+ OFLAG 0
+ LFLAG 0
+
+ The clock is run at datagram once per second.
+ Stock dataformat is:
+
+ <STX>D:<dd>.<mm>.<yy>;T:<w>;U:<hh>:<mm>:<ss>;<S><F><D><A><ETX>
+ pos: 0 00 00 0 00 0 11 111 1 111 12 2 22 2 22 2 2 2 3 3 3
+ 1 23 45 6 78 9 01 234 5 678 90 1 23 4 56 7 8 9 0 1 2
+
+ <STX> = '\002' ASCII start of text
+ <ETX> = '\003' ASCII end of text
+ <dd>,<mm>,<yy> = day, month, year(2 digits!!)
+ <w> = day of week (sunday= 0)
+ <hh>,<mm>,<ss> = hour, minute, second
+ <S> = '#' if never synced since powerup else ' ' for DCF U/A 31
+ '#' if not PZF sychronisation available else ' ' for PZF 535
+ <F> = '*' if time comes from internal quartz else ' '
+ <D> = 'S' if daylight saving time is active else ' '
+ <A> = '!' during the hour preceeding an daylight saving time
+ start/end change
+
+ For the university of Erlangen a special format was implemented to support
+ LEAP announcement and anouncement of alternate antenna.
+
+ Version for UNI-ERLANGEN Software is: PZFUERL V4.6 (Meinberg)
+
+ The use of this software release (or higher) is *ABSOLUTELY*
+ recommended (ask for PZFUERL version as some minor HW fixes have
+ been introduced) due to the LEAP second support and UTC indication.
+ The standard timecode does not indicate when the timecode is in
+ UTC (by front panel configuration) thus we have no chance to find
+ the correct utc offset. For the standard format do not ever use
+ UTC display as this is not detectable in the time code !!!
+
+ <STX><dd>.<mm>.<yy>; <w>; <hh>:<mm>:<ss>; <U><S><F><D><A><L><R><ETX>
+ pos: 0 00 0 00 0 00 11 1 11 11 1 11 2 22 22 2 2 2 2 2 3 3 3
+ 1 23 4 56 7 89 01 2 34 56 7 89 0 12 34 5 6 7 8 9 0 1 2
+ <STX> = '\002' ASCII start of text
+ <ETX> = '\003' ASCII end of text
+ <dd>,<mm>,<yy> = day, month, year(2 digits!!)
+ <w> = day of week (sunday= 0)
+ <hh>,<mm>,<ss> = hour, minute, second
+ <U> = 'U' UTC time display
+ <S> = '#' if never synced since powerup else ' ' for DCF U/A 31
+ '#' if not PZF sychronisation available else ' ' for PZF 535
+ <F> = '*' if time comes from internal quartz else ' '
+ <D> = 'S' if daylight saving time is active else ' '
+ <A> = '!' during the hour preceeding an daylight saving time
+ start/end change
+ <L> = 'A' LEAP second announcement
+ <R> = 'R' alternate antenna
+
+ Meinberg GPS166 receiver
+
+ You must get the Uni-Erlangen firmware for the GPS receiver support
+ to work to full satisfaction !
+
+ <STX><dd>.<mm>.<yy>; <w>; <hh>:<mm>:<ss>; <+/-><00:00>; <U><S><F><D><A><L><R><L>; <position...><ETX>
+ *
+ 000000000111111111122222222223333333333444444444455555555556666666
+ 123456789012345678901234567890123456789012345678901234567890123456
+ \x0209.07.93; 5; 08:48:26; +00:00; ; 49.5736N 11.0280E 373m\x03
+ *
+
+ <STX> = '\002' ASCII start of text
+ <ETX> = '\003' ASCII end of text
+ <dd>,<mm>,<yy> = day, month, year(2 digits!!)
+ <w> = day of week (sunday= 0)
+ <hh>,<mm>,<ss> = hour, minute, second
+ <+/->,<00:00> = offset to UTC
+ <S> = '#' if never synced since powerup else ' ' for DCF U/A 31
+ '#' if not PZF sychronisation available else ' ' for PZF 535
+ <U> = 'U' UTC time display
+ <F> = '*' if time comes from internal quartz else ' '
+ <D> = 'S' if daylight saving time is active else ' '
+ <A> = '!' during the hour preceeding an daylight saving time
+ start/end change
+ <L> = 'A' LEAP second announcement
+ <R> = 'R' alternate antenna (reminiscent of PZF535) usually ' '
+ <L> = 'L' on 23:59:60
+
+
+ For the Meinberg parse look into clock_meinberg.c
+
+---
+ RAWDCF: 127.127.8.20-23 (Conrad receiver module - delay 258ms)
+ 127.127.8.24-27 (FAU receiver - delay 210ms)
+ 127.127.8.40-43 (Boeder receiver - delay 258ms)
+ RAWDCF: end=TIMEOUT>1.5s, sync each char (any char), generate psuedo time
+ codes, fixed format
+
+ direct DCF77 code input
+ In Europe it is relatively easy/cheap the receive the german time code
+ transmitter DCF77. The simplest version to process its signal is to
+ feed the 100/200ms pulse of the demodulated AM signal via a level
+ converter to an RS232 port at 50Baud. parse/clk_rawdcf.c holds all
+ necessary decoding logic for the time code which is transmitted each
+ minute for one minute. A bit of the time code is sent once a second.
+
+ The preferred tty setting is:
+ CFLAG (B50|CS8|CREAD|CLOCAL)
+ IFLAG 0
+ OFLAG 0
+ LFLAG 0
+
+ DCF77 raw time code
+
+ From "Zur Zeit", Physikalisch-Technische Bundesanstalt (PTB), Braunschweig
+ und Berlin, Maerz 1989
+
+ Timecode transmission:
+ AM:
+ time marks are send every second except for the second before the
+ next minute mark
+ time marks consist of a reduction of transmitter power to 25%
+ of the nominal level
+ the falling edge is the time indication (on time)
+ time marks of a 100ms duration constitute a logical 0
+ time marks of a 200ms duration constitute a logical 1
+ FM:
+ see the spec. (basically a (non-)inverted psuedo random phase shift)
+
+ Encoding:
+ Second Contents
+ 0 - 10 AM: free, FM: 0
+ 11 - 14 free
+ 15 R - alternate antenna
+ 16 A1 - expect zone change (1 hour before)
+ 17 - 18 Z1,Z2 - time zone
+ 0 0 illegal
+ 0 1 MEZ (MET)
+ 1 0 MESZ (MED, MET DST)
+ 1 1 illegal
+ 19 A2 - expect leap insertion/deletion (1 hour before)
+ 20 S - start of time code (1)
+ 21 - 24 M1 - BCD (lsb first) Minutes
+ 25 - 27 M10 - BCD (lsb first) 10 Minutes
+ 28 P1 - Minute Parity (even)
+ 29 - 32 H1 - BCD (lsb first) Hours
+ 33 - 34 H10 - BCD (lsb first) 10 Hours
+ 35 P2 - Hour Parity (even)
+ 36 - 39 D1 - BCD (lsb first) Days
+ 40 - 41 D10 - BCD (lsb first) 10 Days
+ 42 - 44 DW - BCD (lsb first) day of week (1: Monday -> 7: Sunday)
+ 45 - 49 MO - BCD (lsb first) Month
+ 50 MO0 - 10 Months
+ 51 - 53 Y1 - BCD (lsb first) Years
+ 54 - 57 Y10 - BCD (lsb first) 10 Years
+ 58 P3 - Date Parity (even)
+ 59 - usually missing (minute indication), except for leap insertion
+
+---
+ Schmid clock: 127.127.8.16-19
+ Schmid clock: needs poll, binary input, end='\xFC', sync start
+
+ The Schmid clock is a DCF77 receiver that sends a binary
+ time code at the reception of a flag byte. The contents
+ if the flag byte determined the time code format. The
+ binary time code is delimited by the byte 0xFC.
+
+ TTY setup is:
+ CFLAG (B1200|CS8|CREAD|CLOCAL)
+ IFLAG 0
+ OFLAG 0
+ LFLAG 0
+
+ The command to Schmid's DCF77 clock is a single byte; each bit
+ allows the user to select some part of the time string, as follows (the
+ output for the lsb is sent first).
+
+ Bit 0: time in MEZ, 4 bytes *binary, not BCD*; hh.mm.ss.tenths
+ Bit 1: date 3 bytes *binary, not BCD: dd.mm.yy
+ Bit 2: week day, 1 byte (unused here)
+ Bit 3: time zone, 1 byte, 0=MET, 1=MEST. (unused here)
+ Bit 4: clock status, 1 byte, 0=time invalid,
+ 1=time from crystal backup,
+ 3=time from DCF77
+ Bit 5: transmitter status, 1 byte,
+ bit 0: backup antenna
+ bit 1: time zone change within 1h
+ bit 3,2: TZ 01=MEST, 10=MET
+ bit 4: leap second will be
+ added within one hour
+ bits 5-7: Zero
+ Bit 6: time in backup mode, units of 5 minutes (unused here)
+
+
+---
+ Trimble SV6: 127.127.8.32-35
+ Trimble SV6: needs poll, ascii timecode, start='>', end='<',
+ query='>QTM<', eol='<'
+
+ Trimble SV6 is a GPS receiver with PPS output. It needs to be polled.
+ It also need a special tty mode setup (EOL='<').
+
+ TTY setup is:
+ CFLAG (B4800|CS8|CREAD)
+ IFLAG (BRKINT|IGNPAR|ISTRIP|ICRNL|IXON)
+ OFLAG (OPOST|ONLCR)
+ LFLAG (ICANON|ECHOK)
+
+ Special flags are:
+ PARSE_F_PPSPPS - use CIOGETEV for PPS time stamping
+ PARSE_F_PPSONSECOND - the time code is not related to
+ the PPS pulse (so use the time code
+ only for the second epoch)
+
+ Timecode
+ 0000000000111111111122222222223333333 / char
+ 0123456789012345678901234567890123456 \ posn
+ >RTMhhmmssdddDDMMYYYYoodnnvrrrrr;*xx< Actual
+ ----33445566600112222BB7__-_____--99- Parse
+ >RTM 1 ;* <", Check
+
+---
+ ELV DCF7000: 127.127.8.12-15
+ ELV DCF7000: end='\r', pattern=" - - - - - - - \r"
+
+ The ELV DCF7000 is a cheap DCF77 receiver sending each second
+ a time code (though not very precise!) delimited by '`r'
+
+ Timecode
+ YY-MM-DD-HH-MM-SS-FF\r
+
+ FF&0x1 - DST
+ FF&0x2 - DST switch warning
+ FF&0x4 - unsynchronised
+
diff --git a/usr.sbin/xntpd/parse/clk_dcf7000.c b/usr.sbin/xntpd/parse/clk_dcf7000.c
new file mode 100644
index 0000000..82e7915
--- /dev/null
+++ b/usr.sbin/xntpd/parse/clk_dcf7000.c
@@ -0,0 +1,150 @@
+#if defined(REFCLOCK) && (defined(PARSE) || defined(PARSEPPS)) && defined(CLOCK_DCF7000)
+/*
+ * /src/NTP/REPOSITORY/v3/parse/clk_dcf7000.c,v 3.12 1994/05/30 10:19:57 kardel Exp
+ *
+ * clk_dcf7000.c,v 3.12 1994/05/30 10:19:57 kardel Exp
+ *
+ * ELV DCF7000 module
+ *
+ * Copyright (c) 1992,1993,1994
+ * Frank Kardel Friedrich-Alexander Universitaet Erlangen-Nuernberg
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ */
+
+#include "sys/types.h"
+#include "sys/time.h"
+#include "sys/errno.h"
+#include "ntp_fp.h"
+#include "ntp_unixtime.h"
+#include "ntp_calendar.h"
+
+#include "parse.h"
+
+static struct format dcf7000_fmt =
+{ /* ELV DCF7000 */
+ {
+ { 6, 2}, { 3, 2}, { 0, 2},
+ { 12, 2}, { 15, 2}, { 18, 2},
+ { 9, 2}, { 21, 2},
+ },
+ " - - - - - - - \r",
+ 0
+};
+
+static u_long cvt_dcf7000();
+
+clockformat_t clock_dcf7000 =
+{
+ (unsigned LONG (*)())0, /* no input handling */
+ cvt_dcf7000, /* ELV DCF77 conversion */
+ syn_simple, /* easy time stamps */
+ (u_long (*)())0, /* no direct PPS monitoring */
+ (u_long (*)())0, /* no time code synthesizer monitoring */
+ (void *)&dcf7000_fmt, /* conversion configuration */
+ "ELV DCF7000", /* ELV clock */
+ 24, /* string buffer */
+ F_END|SYNC_END, /* END packet delimiter / synchronisation */
+ 0, /* no private data (complete pakets) */
+ { 0, 0},
+ '\0',
+ '\r',
+ '\0'
+};
+
+/*
+ * cvt_dcf7000
+ *
+ * convert dcf7000 type format
+ */
+static u_long
+cvt_dcf7000(buffer, size, format, clock)
+ register char *buffer;
+ register int size;
+ register struct format *format;
+ register clocktime_t *clock;
+{
+ if (!Strok(buffer, format->fixed_string))
+ {
+ return CVT_NONE;
+ }
+ else
+ {
+ if (Stoi(&buffer[format->field_offsets[O_DAY].offset], &clock->day,
+ format->field_offsets[O_DAY].length) ||
+ Stoi(&buffer[format->field_offsets[O_MONTH].offset], &clock->month,
+ format->field_offsets[O_MONTH].length) ||
+ Stoi(&buffer[format->field_offsets[O_YEAR].offset], &clock->year,
+ format->field_offsets[O_YEAR].length) ||
+ Stoi(&buffer[format->field_offsets[O_HOUR].offset], &clock->hour,
+ format->field_offsets[O_HOUR].length) ||
+ Stoi(&buffer[format->field_offsets[O_MIN].offset], &clock->minute,
+ format->field_offsets[O_MIN].length) ||
+ Stoi(&buffer[format->field_offsets[O_SEC].offset], &clock->second,
+ format->field_offsets[O_SEC].length))
+ {
+ return CVT_FAIL|CVT_BADFMT;
+ }
+ else
+ {
+ char *f = &buffer[format->field_offsets[O_FLAGS].offset];
+ long flags;
+
+ clock->flags = 0;
+ clock->usecond = 0;
+
+ if (Stoi(f, &flags, format->field_offsets[O_FLAGS].length))
+ {
+ return CVT_FAIL|CVT_BADFMT;
+ }
+ else
+ {
+ if (flags & 0x1)
+ clock->utcoffset = -2*60*60;
+ else
+ clock->utcoffset = -1*60*60;
+
+ if (flags & 0x2)
+ clock->flags |= PARSEB_ANNOUNCE;
+
+ if (flags & 0x4)
+ clock->flags |= PARSEB_NOSYNC;
+ }
+ return CVT_OK;
+ }
+ }
+}
+#endif /* defined(PARSE) && defined(CLOCK_DCF7000) */
+
+/*
+ * History:
+ *
+ * clk_dcf7000.c,v
+ * Revision 3.12 1994/05/30 10:19:57 kardel
+ * LONG cleanup
+ *
+ * Revision 3.11 1994/02/02 17:45:14 kardel
+ * rcs ids fixed
+ *
+ * Revision 3.6 1993/10/09 15:01:27 kardel
+ * file structure unified
+ *
+ * Revision 3.5 1993/10/03 19:10:41 kardel
+ * restructured I/O handling
+ *
+ * Revision 3.4 1993/09/27 21:08:02 kardel
+ * utcoffset now in seconds
+ *
+ * Revision 3.3 1993/09/26 23:40:20 kardel
+ * new parse driver logic
+ *
+ * Revision 3.2 1993/07/09 11:37:15 kardel
+ * Initial restructured version + GPS support
+ *
+ * Revision 3.1 1993/07/06 10:00:14 kardel
+ * DCF77 driver goes generic...
+ *
+ */
diff --git a/usr.sbin/xntpd/parse/clk_meinberg.c b/usr.sbin/xntpd/parse/clk_meinberg.c
new file mode 100644
index 0000000..10389cf
--- /dev/null
+++ b/usr.sbin/xntpd/parse/clk_meinberg.c
@@ -0,0 +1,473 @@
+#if defined(REFCLOCK) && (defined(PARSE) || defined(PARSEPPS)) && defined(CLOCK_MEINBERG)
+/*
+ * /src/NTP/REPOSITORY/v3/parse/clk_meinberg.c,v 3.15 1994/05/30 10:19:59 kardel Exp
+ *
+ * clk_meinberg.c,v 3.15 1994/05/30 10:19:59 kardel Exp
+ *
+ * Meinberg clock support
+ *
+ * Copyright (c) 1992,1993,1994
+ * Frank Kardel Friedrich-Alexander Universitaet Erlangen-Nuernberg
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ */
+
+#include "sys/types.h"
+#include "sys/time.h"
+#include "sys/errno.h"
+#include "ntp_fp.h"
+#include "ntp_unixtime.h"
+#include "ntp_calendar.h"
+
+#include "parse.h"
+
+/*
+ * The Meinberg receiver every second sends a datagram of the following form
+ * (Standard Format)
+ *
+ * <STX>D:<dd>.<mm>.<yy>;T:<w>;U:<hh>:<mm>:<ss>;<S><F><D><A><ETX>
+ * pos: 0 00 00 0 00 0 11 111 1 111 12 2 22 2 22 2 2 2 3 3 3
+ * 1 23 45 6 78 9 01 234 5 678 90 1 23 4 56 7 8 9 0 1 2
+ * <STX> = '\002' ASCII start of text
+ * <ETX> = '\003' ASCII end of text
+ * <dd>,<mm>,<yy> = day, month, year(2 digits!!)
+ * <w> = day of week (sunday= 0)
+ * <hh>,<mm>,<ss> = hour, minute, second
+ * <S> = '#' if never synced since powerup else ' ' for DCF U/A 31
+ * '#' if not PZF sychronisation available else ' ' for PZF 535
+ * <F> = '*' if time comes from internal quartz else ' '
+ * <D> = 'S' if daylight saving time is active else ' '
+ * <A> = '!' during the hour preceeding an daylight saving time
+ * start/end change
+ *
+ * For the university of Erlangen a special format was implemented to support
+ * LEAP announcement and anouncement of alternate antenna.
+ *
+ * Version for UNI-ERLANGEN Software is: PZFUERL V4.6 (Meinberg)
+ *
+ * The use of this software release (or higher) is *ABSOLUTELY*
+ * recommended (ask for PZFUERL version as some minor HW fixes have
+ * been introduced) due to the LEAP second support and UTC indication.
+ * The standard timecode does not indicate when the timecode is in
+ * UTC (by front panel configuration) thus we have no chance to find
+ * the correct utc offset. For the standard format do not ever use
+ * UTC display as this is not detectable in the time code !!!
+ *
+ * <STX><dd>.<mm>.<yy>; <w>; <hh>:<mm>:<ss>; <U><S><F><D><A><L><R><ETX>
+ * pos: 0 00 0 00 0 00 11 1 11 11 1 11 2 22 22 2 2 2 2 2 3 3 3
+ * 1 23 4 56 7 89 01 2 34 56 7 89 0 12 34 5 6 7 8 9 0 1 2
+ * <STX> = '\002' ASCII start of text
+ * <ETX> = '\003' ASCII end of text
+ * <dd>,<mm>,<yy> = day, month, year(2 digits!!)
+ * <w> = day of week (sunday= 0)
+ * <hh>,<mm>,<ss> = hour, minute, second
+ * <U> = 'U' UTC time display
+ * <S> = '#' if never synced since powerup else ' ' for DCF U/A 31
+ * '#' if not PZF sychronisation available else ' ' for PZF 535
+ * <F> = '*' if time comes from internal quartz else ' '
+ * <D> = 'S' if daylight saving time is active else ' '
+ * <A> = '!' during the hour preceeding an daylight saving time
+ * start/end change
+ * <L> = 'A' LEAP second announcement
+ * <R> = 'R' alternate antenna
+ *
+ * Meinberg GPS166 receiver
+ *
+ * You must get the Uni-Erlangen firmware for the GPS receiver support
+ * to work to full satisfaction !
+ *
+ * <STX><dd>.<mm>.<yy>; <w>; <hh>:<mm>:<ss>; <+/-><00:00>; <U><S><F><D><A><L><R><L>; <position...><ETX>
+ *
+ * 000000000111111111122222222223333333333444444444455555555556666666
+ * 123456789012345678901234567890123456789012345678901234567890123456
+ * \x0209.07.93; 5; 08:48:26; +00:00; ; 49.5736N 11.0280E 373m\x03
+ *
+ *
+ * <STX> = '\002' ASCII start of text
+ * <ETX> = '\003' ASCII end of text
+ * <dd>,<mm>,<yy> = day, month, year(2 digits!!)
+ * <w> = day of week (sunday= 0)
+ * <hh>,<mm>,<ss> = hour, minute, second
+ * <+/->,<00:00> = offset to UTC
+ * <S> = '#' if never synced since powerup else ' ' for DCF U/A 31
+ * '#' if not PZF sychronisation available else ' ' for PZF 535
+ * <U> = 'U' UTC time display
+ * <F> = '*' if time comes from internal quartz else ' '
+ * <D> = 'S' if daylight saving time is active else ' '
+ * <A> = '!' during the hour preceeding an daylight saving time
+ * start/end change
+ * <L> = 'A' LEAP second announcement
+ * <R> = 'R' alternate antenna (reminiscent of PZF535) usually ' '
+ * <L> = 'L' on 23:59:60
+ */
+
+static struct format meinberg_fmt[] =
+{
+ {
+ {
+ { 3, 2}, { 6, 2}, { 9, 2},
+ { 18, 2}, { 21, 2}, { 24, 2},
+ { 14, 1}, { 27, 4}, { 29, 1},
+ },
+ "\2D: . . ;T: ;U: . . ; \3",
+ 0
+ },
+ { /* special extended FAU Erlangen extended format */
+ {
+ { 1, 2}, { 4, 2}, { 7, 2},
+ { 14, 2}, { 17, 2}, { 20, 2},
+ { 11, 1}, { 25, 4}, { 27, 1},
+ },
+ "\2 . . ; ; : : ; \3",
+ MBG_EXTENDED
+ },
+ { /* special extended FAU Erlangen GPS format */
+ {
+ { 1, 2}, { 4, 2}, { 7, 2},
+ { 14, 2}, { 17, 2}, { 20, 2},
+ { 11, 1}, { 32, 8}, { 35, 1},
+ { 25, 2}, { 28, 2}, { 24, 1}
+ },
+ "\2 . . ; ; : : ; : ; ; . . ",
+ 0
+ }
+};
+
+static u_long cvt_meinberg();
+static u_long cvt_mgps();
+
+clockformat_t clock_meinberg[] =
+{
+ {
+ (unsigned LONG (*)())0, /* no input handling */
+ cvt_meinberg, /* Meinberg conversion */
+ syn_simple, /* easy time stamps for RS232 (fallback) */
+ pps_simple, /* easy PPS monitoring */
+ (u_long (*)())0, /* no time code synthesizer monitoring */
+ (void *)&meinberg_fmt[0], /* conversion configuration */
+ "Meinberg Standard", /* Meinberg simple format - beware */
+ 32, /* string buffer */
+ F_START|F_END|SYNC_START|SYNC_ONE, /* paket START/END delimiter, START synchronisation, PPS ONE sampling */
+ 0, /* no private data (complete pakets) */
+ { 0, 0},
+ '\2',
+ '\3',
+ '\0'
+ },
+ {
+ (unsigned LONG (*)())0, /* no input handling */
+ cvt_meinberg, /* Meinberg conversion */
+ syn_simple, /* easy time stamps for RS232 (fallback) */
+ pps_simple, /* easy PPS monitoring */
+ (u_long (*)())0, /* no time code synthesizer monitoring */
+ (void *)&meinberg_fmt[1], /* conversion configuration */
+ "Meinberg Extended", /* Meinberg enhanced format */
+ 32, /* string buffer */
+ F_START|F_END|SYNC_START|SYNC_ONE, /* paket START/END delimiter, START synchronisation, PPS ONE sampling */
+ 0, /* no private data (complete pakets) */
+ { 0, 0},
+ '\2',
+ '\3',
+ '\0'
+ },
+ {
+ (unsigned LONG (*)())0, /* no input handling */
+ cvt_mgps, /* Meinberg GPS166 conversion */
+ syn_simple, /* easy time stamps for RS232 (fallback) */
+ pps_simple, /* easy PPS monitoring */
+ (u_long (*)())0, /* no time code synthesizer monitoring */
+ (void *)&meinberg_fmt[2], /* conversion configuration */
+ "Meinberg GPS Extended", /* Meinberg FAU GPS format */
+ 70, /* string buffer */
+ F_START|F_END|SYNC_START|SYNC_ONE, /* paket START/END delimiter, START synchronisation, PPS ONE sampling */
+ 0, /* no private data (complete pakets) */
+ { 0, 0},
+ '\2',
+ '\3',
+ '\0'
+ }
+};
+
+/*
+ * cvt_meinberg
+ *
+ * convert simple type format
+ */
+static u_long
+cvt_meinberg(buffer, size, format, clock)
+ register char *buffer;
+ register int size;
+ register struct format *format;
+ register clocktime_t *clock;
+{
+ if (!Strok(buffer, format->fixed_string))
+ {
+ return CVT_NONE;
+ }
+ else
+ {
+ if (Stoi(&buffer[format->field_offsets[O_DAY].offset], &clock->day,
+ format->field_offsets[O_DAY].length) ||
+ Stoi(&buffer[format->field_offsets[O_MONTH].offset], &clock->month,
+ format->field_offsets[O_MONTH].length) ||
+ Stoi(&buffer[format->field_offsets[O_YEAR].offset], &clock->year,
+ format->field_offsets[O_YEAR].length) ||
+ Stoi(&buffer[format->field_offsets[O_HOUR].offset], &clock->hour,
+ format->field_offsets[O_HOUR].length) ||
+ Stoi(&buffer[format->field_offsets[O_MIN].offset], &clock->minute,
+ format->field_offsets[O_MIN].length) ||
+ Stoi(&buffer[format->field_offsets[O_SEC].offset], &clock->second,
+ format->field_offsets[O_SEC].length))
+ {
+ return CVT_FAIL|CVT_BADFMT;
+ }
+ else
+ {
+ char *f = &buffer[format->field_offsets[O_FLAGS].offset];
+
+ clock->flags = 0;
+ clock->usecond = 0;
+
+ /*
+ * in the extended timecode format we have also the
+ * indication that the timecode is in UTC
+ * for compatibilty reasons we start at the USUAL
+ * offset (POWERUP flag) and know that the UTC indication
+ * is the character before the powerup flag
+ */
+ if ((format->flags & MBG_EXTENDED) && (f[-1] == 'U'))
+ {
+ /*
+ * timecode is in UTC
+ */
+ clock->utcoffset = 0; /* UTC */
+ clock->flags |= PARSEB_UTC;
+ }
+ else
+ {
+ /*
+ * only calculate UTC offset if MET/MED is in time code
+ * or we have the old time code format, where we do not
+ * know whether it is UTC time or MET/MED
+ * pray that nobody switches to UTC in the standard time code
+ * ROMS !!!!
+ */
+ switch (buffer[format->field_offsets[O_ZONE].offset])
+ {
+ case ' ':
+ clock->utcoffset = -1*60*60; /* MET */
+ break;
+
+ case 'S':
+ clock->utcoffset = -2*60*60; /* MED */
+ break;
+
+ default:
+ return CVT_FAIL|CVT_BADFMT;
+ }
+ }
+
+ /*
+ * gather status flags
+ */
+ if (buffer[format->field_offsets[O_ZONE].offset] == 'S')
+ clock->flags |= PARSEB_DST;
+
+ if (f[0] == '#')
+ clock->flags |= PARSEB_POWERUP;
+
+ if (f[1] == '*')
+ clock->flags |= PARSEB_NOSYNC;
+
+ if (f[3] == '!')
+ clock->flags |= PARSEB_ANNOUNCE;
+
+ if (format->flags & MBG_EXTENDED)
+ {
+ clock->flags |= PARSEB_S_LEAP;
+ clock->flags |= PARSEB_S_ANTENNA;
+
+ /*
+ * DCF77 does not encode the direction -
+ * so we take the current default -
+ * earth slowing down
+ */
+ if (f[4] == 'A')
+ clock->flags |= PARSEB_LEAPADD;
+
+ if (f[5] == 'R')
+ clock->flags |= PARSEB_ALTERNATE;
+ }
+ return CVT_OK;
+ }
+ }
+}
+
+/*
+ * cvt_mgps
+ *
+ * convert Meinberg GPS format
+ */
+static u_long
+cvt_mgps(buffer, size, format, clock)
+ register char *buffer;
+ register int size;
+ register struct format *format;
+ register clocktime_t *clock;
+{
+ if (!Strok(buffer, format->fixed_string))
+ {
+ return CVT_NONE;
+ }
+ else
+ {
+ if (Stoi(&buffer[format->field_offsets[O_DAY].offset], &clock->day,
+ format->field_offsets[O_DAY].length) ||
+ Stoi(&buffer[format->field_offsets[O_MONTH].offset], &clock->month,
+ format->field_offsets[O_MONTH].length) ||
+ Stoi(&buffer[format->field_offsets[O_YEAR].offset], &clock->year,
+ format->field_offsets[O_YEAR].length) ||
+ Stoi(&buffer[format->field_offsets[O_HOUR].offset], &clock->hour,
+ format->field_offsets[O_HOUR].length) ||
+ Stoi(&buffer[format->field_offsets[O_MIN].offset], &clock->minute,
+ format->field_offsets[O_MIN].length) ||
+ Stoi(&buffer[format->field_offsets[O_SEC].offset], &clock->second,
+ format->field_offsets[O_SEC].length))
+ {
+ return CVT_FAIL|CVT_BADFMT;
+ }
+ else
+ {
+ long h;
+ char *f = &buffer[format->field_offsets[O_FLAGS].offset];
+
+ clock->flags = PARSEB_S_LEAP|PARSEB_S_POSITION;
+
+ clock->usecond = 0;
+
+ /*
+ * calculate UTC offset
+ */
+ if (Stoi(&buffer[format->field_offsets[O_UTCHOFFSET].offset], &h,
+ format->field_offsets[O_UTCHOFFSET].length))
+ {
+ return CVT_FAIL|CVT_BADFMT;
+ }
+ else
+ {
+ if (Stoi(&buffer[format->field_offsets[O_UTCMOFFSET].offset], &clock->utcoffset,
+ format->field_offsets[O_UTCMOFFSET].length))
+ {
+ return CVT_FAIL|CVT_BADFMT;
+ }
+
+ clock->utcoffset += TIMES60(h);
+
+ if (buffer[format->field_offsets[O_UTCSOFFSET].offset] != '-')
+ {
+ clock->utcoffset = -clock->utcoffset;
+ }
+ }
+
+ /*
+ * gather status flags
+ */
+ if (buffer[format->field_offsets[O_ZONE].offset] == 'S')
+ clock->flags |= PARSEB_DST;
+
+ if ((f[0] == 'U') ||
+ (clock->utcoffset == 0))
+ clock->flags |= PARSEB_UTC;
+
+ /*
+ * no sv's seen - no time & position
+ */
+ if (f[1] == '#')
+ clock->flags |= PARSEB_POWERUP;
+
+ /*
+ * at least one sv seen - time (for last position)
+ */
+ if (f[2] == '*')
+ clock->flags |= PARSEB_NOSYNC;
+ else
+ if (!(clock->flags & PARSEB_POWERUP))
+ clock->flags |= PARSEB_POSITION;
+
+ /*
+ * oncoming zone switch
+ */
+ if (f[4] == '!')
+ clock->flags |= PARSEB_ANNOUNCE;
+
+ /*
+ * oncoming leap second
+ * data format does not (yet) specify whether
+ * to add or to delete a second - thus we
+ * pick the current default
+ */
+ if (f[5] == 'A')
+ clock->flags |= PARSEB_LEAPADD;
+
+ /*
+ * this is the leap second
+ */
+ if (f[7] == 'L')
+ clock->flags |= PARSEB_LEAPSECOND;
+
+ return CVT_OK;
+ }
+ }
+}
+#endif /* defined(PARSE) && defined(CLOCK_MEINBERG) */
+
+/*
+ * History:
+ *
+ * clk_meinberg.c,v
+ * Revision 3.15 1994/05/30 10:19:59 kardel
+ * LONG cleanup
+ *
+ * Revision 3.14 1994/02/20 13:04:37 kardel
+ * parse add/delete second support
+ *
+ * Revision 3.13 1994/02/02 17:45:21 kardel
+ * rcs ids fixed
+ *
+ * Revision 3.11 1994/01/25 19:05:10 kardel
+ * 94/01/23 reconcilation
+ *
+ * Revision 3.10 1994/01/23 17:21:54 kardel
+ * 1994 reconcilation
+ *
+ * Revision 3.9 1993/10/30 09:44:38 kardel
+ * conditional compilation flag cleanup
+ *
+ * Revision 3.8 1993/10/22 14:27:48 kardel
+ * Oct. 22nd 1993 reconcilation
+ *
+ * Revision 3.7 1993/10/09 15:01:30 kardel
+ * file structure unified
+ *
+ * Revision 3.6 1993/10/03 19:10:43 kardel
+ * restructured I/O handling
+ *
+ * Revision 3.5 1993/09/27 21:08:04 kardel
+ * utcoffset now in seconds
+ *
+ * Revision 3.4 1993/09/26 23:40:22 kardel
+ * new parse driver logic
+ *
+ * Revision 3.3 1993/08/18 09:29:32 kardel
+ * GPS format is somewhat variable length - variable length part holds position
+ *
+ * Revision 3.2 1993/07/09 11:37:16 kardel
+ * Initial restructured version + GPS support
+ *
+ * Revision 3.1 1993/07/06 10:00:17 kardel
+ * DCF77 driver goes generic...
+ *
+ */
diff --git a/usr.sbin/xntpd/parse/clk_rawdcf.c b/usr.sbin/xntpd/parse/clk_rawdcf.c
new file mode 100644
index 0000000..042e5d3
--- /dev/null
+++ b/usr.sbin/xntpd/parse/clk_rawdcf.c
@@ -0,0 +1,580 @@
+#if defined(REFCLOCK) && (defined(PARSE) || defined(PARSEPPS)) && defined(CLOCK_RAWDCF)
+/*
+ * /src/NTP/REPOSITORY/v3/parse/clk_rawdcf.c,v 3.16 1994/05/31 20:02:40 kardel Exp
+ *
+ * clk_rawdcf.c,v 3.16 1994/05/31 20:02:40 kardel Exp
+ *
+ * Raw DCF77 pulse clock support
+ *
+ * Copyright (c) 1992,1993,1994
+ * Frank Kardel Friedrich-Alexander Universitaet Erlangen-Nuernberg
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ */
+
+#include "sys/types.h"
+#include "sys/time.h"
+#include "sys/errno.h"
+#include "ntp_fp.h"
+#include "ntp_unixtime.h"
+#include "ntp_calendar.h"
+
+#include "parse.h"
+#ifdef PARSESTREAM
+#include "sys/parsestreams.h"
+#endif
+
+#ifndef PARSEKERNEL
+#include "ntp_stdlib.h"
+#endif
+
+/*
+ * DCF77 raw time code
+ *
+ * From "Zur Zeit", Physikalisch-Technische Bundesanstalt (PTB), Braunschweig
+ * und Berlin, Maerz 1989
+ *
+ * Timecode transmission:
+ * AM:
+ * time marks are send every second except for the second before the
+ * next minute mark
+ * time marks consist of a reduction of transmitter power to 25%
+ * of the nominal level
+ * the falling edge is the time indication (on time)
+ * time marks of a 100ms duration constitute a logical 0
+ * time marks of a 200ms duration constitute a logical 1
+ * FM:
+ * see the spec. (basically a (non-)inverted psuedo random phase shift)
+ *
+ * Encoding:
+ * Second Contents
+ * 0 - 10 AM: free, FM: 0
+ * 11 - 14 free
+ * 15 R - alternate antenna
+ * 16 A1 - expect zone change (1 hour before)
+ * 17 - 18 Z1,Z2 - time zone
+ * 0 0 illegal
+ * 0 1 MEZ (MET)
+ * 1 0 MESZ (MED, MET DST)
+ * 1 1 illegal
+ * 19 A2 - expect leap insertion/deletion (1 hour before)
+ * 20 S - start of time code (1)
+ * 21 - 24 M1 - BCD (lsb first) Minutes
+ * 25 - 27 M10 - BCD (lsb first) 10 Minutes
+ * 28 P1 - Minute Parity (even)
+ * 29 - 32 H1 - BCD (lsb first) Hours
+ * 33 - 34 H10 - BCD (lsb first) 10 Hours
+ * 35 P2 - Hour Parity (even)
+ * 36 - 39 D1 - BCD (lsb first) Days
+ * 40 - 41 D10 - BCD (lsb first) 10 Days
+ * 42 - 44 DW - BCD (lsb first) day of week (1: Monday -> 7: Sunday)
+ * 45 - 49 MO - BCD (lsb first) Month
+ * 50 MO0 - 10 Months
+ * 51 - 53 Y1 - BCD (lsb first) Years
+ * 54 - 57 Y10 - BCD (lsb first) 10 Years
+ * 58 P3 - Date Parity (even)
+ * 59 - usually missing (minute indication), except for leap insertion
+ */
+
+static u_long cvt_rawdcf();
+static u_long pps_rawdcf();
+static u_long snt_rawdcf();
+
+clockformat_t clock_rawdcf =
+{
+ (unsigned LONG (*)())0, /* no input handling */
+ cvt_rawdcf, /* raw dcf input conversion */
+ (void (*)())0, /* no character bound synchronisation */
+ pps_rawdcf, /* examining PPS information */
+ snt_rawdcf, /* synthesize time code from input */
+ (void *)0, /* buffer bit representation */
+ "RAW DCF77 Timecode", /* direct decoding / time synthesis */
+ 61, /* bit buffer */
+ SYNC_ONE|SYNC_ZERO|SYNC_TIMEOUT|SYNC_SYNTHESIZE|CVT_FIXEDONLY,
+ /* catch all transitions, buffer restart on timeout, fixed configuration only */
+ 0, /* no private data (currently in input buffer) */
+ { 1, 500000}, /* restart after 1.5 seconds */
+ '\0',
+ '\0',
+ '\0'
+};
+
+static struct dcfparam
+{
+ unsigned char onebits[60];
+ unsigned char zerobits[60];
+} dcfparam =
+{
+ "###############RADMLS1248124P124812P1248121241248112481248P", /* 'ONE' representation */
+ "--------------------s-------p------p----------------------p" /* 'ZERO' representation */
+};
+
+static struct rawdcfcode
+{
+ char offset; /* start bit */
+} rawdcfcode[] =
+{
+ { 0 }, { 15 }, { 16 }, { 17 }, { 19 }, { 20 }, { 21 }, { 25 }, { 28 }, { 29 },
+ { 33 }, { 35 }, { 36 }, { 40 }, { 42 }, { 45 }, { 49 }, { 50 }, { 54 }, { 58 }, { 59 }
+};
+
+#define DCF_M 0
+#define DCF_R 1
+#define DCF_A1 2
+#define DCF_Z 3
+#define DCF_A2 4
+#define DCF_S 5
+#define DCF_M1 6
+#define DCF_M10 7
+#define DCF_P1 8
+#define DCF_H1 9
+#define DCF_H10 10
+#define DCF_P2 11
+#define DCF_D1 12
+#define DCF_D10 13
+#define DCF_DW 14
+#define DCF_MO 15
+#define DCF_MO0 16
+#define DCF_Y1 17
+#define DCF_Y10 18
+#define DCF_P3 19
+
+static struct partab
+{
+ char offset; /* start bit of parity field */
+} partab[] =
+{
+ { 21 }, { 29 }, { 36 }, { 59 }
+};
+
+#define DCF_P_P1 0
+#define DCF_P_P2 1
+#define DCF_P_P3 2
+
+#define DCF_Z_MET 0x2
+#define DCF_Z_MED 0x1
+
+static u_long ext_bf(buf, idx, zero)
+ register char *buf;
+ register int idx;
+ register char *zero;
+{
+ register u_long sum = 0;
+ register int i, first;
+
+ first = rawdcfcode[idx].offset;
+
+ for (i = rawdcfcode[idx+1].offset - 1; i >= first; i--)
+ {
+ sum <<= 1;
+ sum |= (buf[i] != zero[i]);
+ }
+ return sum;
+}
+
+static unsigned pcheck(buf, idx, zero)
+ register char *buf;
+ register int idx;
+ register char *zero;
+{
+ register int i,last;
+ register unsigned psum = 1;
+
+ last = partab[idx+1].offset;
+
+ for (i = partab[idx].offset; i < last; i++)
+ psum ^= (buf[i] != zero[i]);
+
+ return psum;
+}
+
+static u_long convert_rawdcf(buffer, size, dcfparam, clock)
+ register unsigned char *buffer;
+ register int size;
+ register struct dcfparam *dcfparam;
+ register clocktime_t *clock;
+{
+ register unsigned char *s = buffer;
+ register unsigned char *b = dcfparam->onebits;
+ register unsigned char *c = dcfparam->zerobits;
+ register int i;
+
+ parseprintf(DD_RAWDCF,("parse: convert_rawdcf: \"%s\"\n", buffer));
+
+ if (size < 57)
+ {
+#ifdef PARSEKERNEL
+ printf("parse: convert_rawdcf: INCOMPLETE DATA - time code only has %d bits\n", size);
+#else
+ syslog(LOG_ERR, "parse: convert_rawdcf: INCOMPLETE DATA - time code only has %d bits\n", size);
+#endif
+ return CVT_NONE;
+ }
+
+ for (i = 0; i < 58; i++)
+ {
+ if ((*s != *b) && (*s != *c))
+ {
+ /*
+ * we only have two types of bytes (ones and zeros)
+ */
+#ifdef PARSEKERNEL
+ printf("parse: convert_rawdcf: BAD DATA - no conversion for \"%s\"\n", buffer);
+#else
+ syslog(LOG_ERR, "parse: convert_rawdcf: BAD DATA - no conversion for \"%s\"\n", buffer);
+#endif
+ return CVT_NONE;
+ }
+ b++;
+ c++;
+ s++;
+ }
+
+ /*
+ * check Start and Parity bits
+ */
+ if ((ext_bf(buffer, DCF_S, dcfparam->zerobits) == 1) &&
+ pcheck(buffer, DCF_P_P1, dcfparam->zerobits) &&
+ pcheck(buffer, DCF_P_P2, dcfparam->zerobits) &&
+ pcheck(buffer, DCF_P_P3, dcfparam->zerobits))
+ {
+ /*
+ * buffer OK
+ */
+ parseprintf(DD_RAWDCF,("parse: convert_rawdcf: parity check passed\n"));
+
+ clock->flags = PARSEB_S_ANTENNA|PARSEB_S_LEAP;
+ clock->utctime= 0;
+ clock->usecond= 0;
+ clock->second = 0;
+ clock->minute = ext_bf(buffer, DCF_M10, dcfparam->zerobits);
+ clock->minute = TIMES10(clock->minute) + ext_bf(buffer, DCF_M1, dcfparam->zerobits);
+ clock->hour = ext_bf(buffer, DCF_H10, dcfparam->zerobits);
+ clock->hour = TIMES10(clock->hour) + ext_bf(buffer, DCF_H1, dcfparam->zerobits);
+ clock->day = ext_bf(buffer, DCF_D10, dcfparam->zerobits);
+ clock->day = TIMES10(clock->day) + ext_bf(buffer, DCF_D1, dcfparam->zerobits);
+ clock->month = ext_bf(buffer, DCF_MO0, dcfparam->zerobits);
+ clock->month = TIMES10(clock->month) + ext_bf(buffer, DCF_MO, dcfparam->zerobits);
+ clock->year = ext_bf(buffer, DCF_Y10, dcfparam->zerobits);
+ clock->year = TIMES10(clock->year) + ext_bf(buffer, DCF_Y1, dcfparam->zerobits);
+
+ switch (ext_bf(buffer, DCF_Z, dcfparam->zerobits))
+ {
+ case DCF_Z_MET:
+ clock->utcoffset = -1*60*60;
+ break;
+
+ case DCF_Z_MED:
+ clock->flags |= PARSEB_DST;
+ clock->utcoffset = -2*60*60;
+ break;
+
+ default:
+ parseprintf(DD_RAWDCF,("parse: convert_rawdcf: BAD TIME ZONE\n"));
+ return CVT_FAIL|CVT_BADFMT;
+ }
+
+ if (ext_bf(buffer, DCF_A1, dcfparam->zerobits))
+ clock->flags |= PARSEB_ANNOUNCE;
+
+ if (ext_bf(buffer, DCF_A2, dcfparam->zerobits))
+ clock->flags |= PARSEB_LEAPADD; /* default: DCF77 data format deficiency */
+
+ if (ext_bf(buffer, DCF_R, dcfparam->zerobits))
+ clock->flags |= PARSEB_ALTERNATE;
+
+ parseprintf(DD_RAWDCF,("parse: convert_rawdcf: TIME CODE OK: %d:%d, %d.%d.%d, flags 0x%lx\n",
+ (int)clock->hour, (int)clock->minute, (int)clock->day, (int)clock->month,(int) clock->year,
+ (u_long)clock->flags));
+ return CVT_OK;
+ }
+ else
+ {
+ /*
+ * bad format - not for us
+ */
+#ifdef PARSEKERNEL
+ printf("parse: convert_rawdcf: parity check FAILED for \"%s\"\n", buffer);
+#else
+ syslog(LOG_ERR, "parse: convert_rawdcf: parity check FAILED for \"%s\"\n", buffer);
+#endif
+ return CVT_FAIL|CVT_BADFMT;
+ }
+}
+
+/*
+ * raw dcf input routine - needs to fix up 50 baud
+ * characters for 1/0 decision
+ */
+static u_long cvt_rawdcf(buffer, size, param, clock)
+ register unsigned char *buffer;
+ register int size;
+ register void *param;
+ register clocktime_t *clock;
+{
+ register unsigned char *s = buffer;
+ register unsigned char *e = buffer + size;
+ register unsigned char *b = dcfparam.onebits;
+ register unsigned char *c = dcfparam.zerobits;
+ register unsigned rtc = CVT_NONE;
+ register unsigned int i, lowmax, highmax, cutoff, span;
+#define BITS 9
+ unsigned char histbuf[BITS];
+ /*
+ * the input buffer contains characters with runs of consecutive
+ * bits set. These set bits are an indication of the DCF77 pulse
+ * length. We assume that we receive the pulse at 50 Baud. Thus
+ * a 100ms pulse would generate a 4 bit train (20ms per bit and
+ * start bit)
+ * a 200ms pulse would create all zeroes (and probably a frame error)
+ */
+
+ for (i = 0; i < BITS; i++)
+ {
+ histbuf[i] = 0;
+ }
+
+ cutoff = 0;
+ lowmax = 0;
+
+ while (s < e)
+ {
+ register unsigned int ch = *s ^ 0xFF;
+ /*
+ * these lines are left as an excercise to the reader 8-)
+ */
+ if (!((ch+1) & ch) || !*s)
+ {
+
+ for (i = 0; ch; i++)
+ {
+ ch >>= 1;
+ }
+
+ *s = i;
+ histbuf[i]++;
+ cutoff += i;
+ lowmax++;
+ }
+ else
+ {
+ parseprintf(DD_RAWDCF,("parse: cvt_rawdcf: character check for 0x%x@%d FAILED\n", *s, s - buffer));
+ *s = ~0;
+ rtc = CVT_FAIL|CVT_BADFMT;
+ }
+ s++;
+ }
+
+ if (lowmax)
+ {
+ cutoff /= lowmax;
+ }
+ else
+ {
+ cutoff = 4; /* doesn't really matter - it'll fail anyway, but gives error output */
+ }
+
+ parseprintf(DD_RAWDCF,("parse: cvt_rawdcf: average bit count: %d\n", cutoff));
+
+ lowmax = 0;
+ highmax = 0;
+
+ parseprintf(DD_RAWDCF,("parse: cvt_rawdcf: histogram:"));
+ for (i = 0; i <= cutoff; i++)
+ {
+ lowmax+=histbuf[i] * i;
+ highmax += histbuf[i];
+ parseprintf(DD_RAWDCF,(" %d", histbuf[i]));
+ }
+ parseprintf(DD_RAWDCF, (" <M>"));
+
+ lowmax += highmax / 2;
+
+ if (highmax)
+ {
+ lowmax /= highmax;
+ }
+ else
+ {
+ lowmax = 0;
+ }
+
+ highmax = 0;
+ cutoff = 0;
+
+ for (; i < BITS; i++)
+ {
+ highmax+=histbuf[i] * i;
+ cutoff +=histbuf[i];
+ parseprintf(DD_RAWDCF,(" %d", histbuf[i]));
+ }
+ parseprintf(DD_RAWDCF,("\n"));
+
+ if (cutoff)
+ {
+ highmax /= cutoff;
+ }
+ else
+ {
+ highmax = BITS-1;
+ }
+
+ span = cutoff = lowmax;
+ for (i = lowmax; i <= highmax; i++)
+ {
+ if (histbuf[cutoff] > histbuf[i])
+ {
+ cutoff = i;
+ span = i;
+ }
+ else
+ if (histbuf[cutoff] == histbuf[i])
+ {
+ span = i;
+ }
+ }
+
+ cutoff = (cutoff + span) / 2;
+
+ parseprintf(DD_RAWDCF,("parse: cvt_rawdcf: lower maximum %d, higher maximum %d, cutoff %d\n", lowmax, highmax, cutoff));
+
+ s = buffer;
+ while ((s < e) && *c && *b)
+ {
+ if (*s == (unsigned char)~0)
+ {
+ *s = '?';
+ }
+ else
+ {
+ *s = (*s >= cutoff) ? *b : *c;
+ }
+ s++;
+ b++;
+ c++;
+ }
+
+ return (rtc == CVT_NONE) ? convert_rawdcf(buffer, size, &dcfparam, clock) : rtc;
+}
+
+/*
+ * pps_rawdcf
+ *
+ * currently a very stupid version - should be extended to decode
+ * also ones and zeros (which is easy)
+ */
+/*ARGSUSED*/
+static u_long pps_rawdcf(parseio, status, ptime)
+ register parse_t *parseio;
+ register int status;
+ register timestamp_t *ptime;
+{
+ if (status)
+ {
+ parseio->parse_dtime.parse_ptime = *ptime;
+ parseio->parse_dtime.parse_state |= PARSEB_PPS|PARSEB_S_PPS;
+ }
+
+ return CVT_NONE;
+}
+
+/*ARGSUSED*/
+static u_long snt_rawdcf(parseio, ptime)
+ register parse_t *parseio;
+ register timestamp_t *ptime;
+{
+ clocktime_t clock;
+ u_long cvtrtc;
+ time_t t;
+
+ /*
+ * start at last sample and add second index - gross, may have to be much more careful
+ */
+ if (convert_rawdcf(parseio->parse_ldata, parseio->parse_ldsize - 1, &dcfparam, &clock) == CVT_OK)
+ {
+ if ((t = parse_to_unixtime(&clock, &cvtrtc)) == -1)
+ {
+ parseprintf(DD_RAWDCF,("parse: snt_rawdcf: time conversion FAILED\n"));
+ return CVT_FAIL|cvtrtc;
+ }
+ }
+ else
+ {
+ parseprintf(DD_RAWDCF,("parse: snt_rawdcf: data conversion FAILED\n"));
+ return CVT_NONE;
+ }
+
+ parseio->parse_dtime.parse_stime = *ptime;
+
+ t += parseio->parse_index - 1;
+
+ /*
+ * time stamp
+ */
+#ifdef PARSEKERNEL
+ parseio->parse_dtime.parse_time.tv.tv_sec = t;
+ parseio->parse_dtime.parse_time.tv.tv_usec = clock.usecond;
+#else
+ parseio->parse_dtime.parse_time.fp.l_ui = t + JAN_1970;
+ TVUTOTSF(clock.usecond, parseio->parse_dtime.parse_time.fp.l_uf);
+#endif
+
+ parseprintf(DD_RAWDCF,("parse: snt_rawdcf: time stamp synthesized offset %d seconds\n", parseio->parse_index - 1));
+
+ return updatetimeinfo(parseio, t, clock.usecond, clock.flags);
+}
+#endif /* defined(PARSE) && defined(CLOCK_RAWDCF) */
+
+/*
+ * History:
+ *
+ * clk_rawdcf.c,v $
+ * Revision 3.16 1994/05/31 20:02:40 kardel
+ * sync on ONE transition
+ *
+ * Revision 3.15 1994/05/30 10:20:01 kardel
+ * LONG cleanup
+ *
+ * Revision 3.14 1994/05/12 12:49:09 kardel
+ * printf fmt/arg cleanup
+ *
+ * Revision 3.13 1994/03/10 19:00:43 kardel
+ * clear utctime field to avoid confusion on synthesize time stamps
+ *
+ * Revision 3.12 1994/02/20 13:04:39 kardel
+ * parse add/delete second support
+ *
+ * Revision 3.11 1994/02/02 17:45:23 kardel
+ * rcs ids fixed
+ *
+ * Revision 3.9 1994/01/25 19:05:12 kardel
+ * 94/01/23 reconcilation
+ *
+ * Revision 3.8 1994/01/22 11:24:11 kardel
+ * fixed PPS handling
+ *
+ * Revision 3.7 1993/10/30 09:44:41 kardel
+ * conditional compilation flag cleanup
+ *
+ * Revision 3.6 1993/10/03 19:10:45 kardel
+ * restructured I/O handling
+ *
+ * Revision 3.5 1993/09/27 21:08:07 kardel
+ * utcoffset now in seconds
+ *
+ * Revision 3.4 1993/09/26 23:40:25 kardel
+ * new parse driver logic
+ *
+ * Revision 3.3 1993/09/01 21:44:54 kardel
+ * conditional cleanup
+ *
+ * Revision 3.2 1993/07/09 11:37:18 kardel
+ * Initial restructured version + GPS support
+ *
+ * Revision 3.1 1993/07/06 10:00:19 kardel
+ * DCF77 driver goes generic...
+ *
+ */
diff --git a/usr.sbin/xntpd/parse/clk_schmid.c b/usr.sbin/xntpd/parse/clk_schmid.c
new file mode 100644
index 0000000..457a659
--- /dev/null
+++ b/usr.sbin/xntpd/parse/clk_schmid.c
@@ -0,0 +1,217 @@
+#if defined(REFCLOCK) && (defined(PARSE) || defined(PARSEPPS)) && defined(CLOCK_SCHMID)
+/*
+ * /src/NTP/REPOSITORY/v3/parse/clk_schmid.c,v 3.16 1994/05/30 10:20:03 kardel Exp
+ *
+ * clk_schmid.c,v 3.16 1994/05/30 10:20:03 kardel Exp
+ *
+ * Schmid clock support
+ *
+ * Copyright (c) 1992,1993,1994
+ * Frank Kardel Friedrich-Alexander Universitaet Erlangen-Nuernberg
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ */
+
+#include "sys/types.h"
+#include "sys/time.h"
+#include "sys/errno.h"
+#include "ntp_fp.h"
+#include "ntp_unixtime.h"
+#include "ntp_calendar.h"
+
+#include "parse.h"
+
+/*
+ * Description courtesy of Adam W. Feigin et. al (Swisstime iis.ethz.ch)
+ *
+ * The command to Schmid's DCF77 clock is a single byte; each bit
+ * allows the user to select some part of the time string, as follows (the
+ * output for the lsb is sent first).
+ *
+ * Bit 0: time in MEZ, 4 bytes *binary, not BCD*; hh.mm.ss.tenths
+ * Bit 1: date 3 bytes *binary, not BCD: dd.mm.yy
+ * Bit 2: week day, 1 byte (unused here)
+ * Bit 3: time zone, 1 byte, 0=MET, 1=MEST. (unused here)
+ * Bit 4: clock status, 1 byte, 0=time invalid,
+ * 1=time from crystal backup,
+ * 3=time from DCF77
+ * Bit 5: transmitter status, 1 byte,
+ * bit 0: backup antenna
+ * bit 1: time zone change within 1h
+ * bit 3,2: TZ 01=MEST, 10=MET
+ * bit 4: leap second will be
+ * added within one hour
+ * bits 5-7: Zero
+ * Bit 6: time in backup mode, units of 5 minutes (unused here)
+ *
+ */
+#define WS_TIME 0x01
+#define WS_SIGNAL 0x02
+
+#define WS_ALTERNATE 0x01
+#define WS_ANNOUNCE 0x02
+#define WS_TZ 0x0c
+#define WS_MET 0x08
+#define WS_MEST 0x04
+#define WS_LEAP 0x10
+
+static u_long cvt_schmid();
+
+clockformat_t clock_schmid =
+{
+ (unsigned LONG (*)())0, /* no input handling */
+ cvt_schmid, /* Schmid conversion */
+ syn_simple, /* easy time stamps */
+ (u_long (*)())0, /* not direct PPS monitoring */
+ (u_long (*)())0, /* no time code synthesizer monitoring */
+ (void *)0, /* conversion configuration */
+ "Schmid", /* Schmid receiver */
+ 12, /* binary data buffer */
+ F_END|SYNC_START, /* END packet delimiter / synchronisation */
+ 0, /* no private data (complete messages) */
+ { 0, 0},
+ '\0',
+ (unsigned char)'\375',
+ '\0'
+};
+
+
+static u_long
+cvt_schmid(buffer, size, format, clock)
+ register unsigned char *buffer;
+ register int size;
+ register struct format *format;
+ register clocktime_t *clock;
+{
+ if ((size != 11) || (buffer[10] != (unsigned char)'\375'))
+ {
+ return CVT_NONE;
+ }
+ else
+ {
+ if (buffer[0] > 23 || buffer[1] > 59 || buffer[2] > 59 || buffer[3] > 9) /* Time */
+ {
+ return CVT_FAIL|CVT_BADTIME;
+ }
+ else
+ if (buffer[4] < 1 || buffer[4] > 31 || buffer[5] < 1 || buffer[5] > 12
+ || buffer[6] > 99)
+ {
+ return CVT_FAIL|CVT_BADDATE;
+ }
+ else
+ {
+ clock->hour = buffer[0];
+ clock->minute = buffer[1];
+ clock->second = buffer[2];
+ clock->usecond = buffer[3] * 100000;
+ clock->day = buffer[4];
+ clock->month = buffer[5];
+ clock->year = buffer[6];
+
+ clock->flags = 0;
+
+ switch (buffer[8] & WS_TZ)
+ {
+ case WS_MET:
+ clock->utcoffset = -1*60*60;
+ break;
+
+ case WS_MEST:
+ clock->utcoffset = -2*60*60;
+ clock->flags |= PARSEB_DST;
+ break;
+
+ default:
+ return CVT_FAIL|CVT_BADFMT;
+ }
+
+ if (!(buffer[7] & WS_TIME))
+ {
+ clock->flags |= PARSEB_POWERUP;
+ }
+
+ if (!(buffer[7] & WS_SIGNAL))
+ {
+ clock->flags |= PARSEB_NOSYNC;
+ }
+
+ if (buffer[7] & WS_SIGNAL)
+ {
+ if (buffer[8] & WS_ALTERNATE)
+ {
+ clock->flags |= PARSEB_ALTERNATE;
+ }
+
+ if (buffer[8] & WS_ANNOUNCE)
+ {
+ clock->flags |= PARSEB_ANNOUNCE;
+ }
+
+ if (buffer[8] & WS_LEAP)
+ {
+ clock->flags |= PARSEB_LEAPADD; /* default: DCF77 data format deficiency */
+ }
+ }
+
+ clock->flags |= PARSEB_S_LEAP|PARSEB_S_ANTENNA;
+
+ return CVT_OK;
+ }
+ }
+}
+#endif /* defined(PARSE) && defined(CLOCK_SCHMID) */
+
+/*
+ * History:
+ *
+ * clk_schmid.c,v
+ * Revision 3.16 1994/05/30 10:20:03 kardel
+ * LONG cleanup
+ *
+ * Revision 3.15 1994/05/12 12:34:48 kardel
+ * data type cleanup
+ *
+ * Revision 3.14 1994/04/12 14:56:31 kardel
+ * fix declaration
+ *
+ * Revision 3.13 1994/02/20 13:04:41 kardel
+ * parse add/delete second support
+ *
+ * Revision 3.12 1994/02/02 17:45:25 kardel
+ * rcs ids fixed
+ *
+ * Revision 3.10 1994/01/25 19:05:15 kardel
+ * 94/01/23 reconcilation
+ *
+ * Revision 3.9 1994/01/23 17:21:56 kardel
+ * 1994 reconcilation
+ *
+ * Revision 3.8 1993/11/01 20:00:18 kardel
+ * parse Solaris support (initial version)
+ *
+ * Revision 3.7 1993/10/30 09:44:43 kardel
+ * conditional compilation flag cleanup
+ *
+ * Revision 3.6 1993/10/09 15:01:32 kardel
+ * file structure unified
+ *
+ * Revision 3.5 1993/10/03 19:10:47 kardel
+ * restructured I/O handling
+ *
+ * Revision 3.4 1993/09/27 21:08:09 kardel
+ * utcoffset now in seconds
+ *
+ * Revision 3.3 1993/09/26 23:40:27 kardel
+ * new parse driver logic
+ *
+ * Revision 3.2 1993/07/09 11:37:19 kardel
+ * Initial restructured version + GPS support
+ *
+ * Revision 3.1 1993/07/06 10:00:22 kardel
+ * DCF77 driver goes generic...
+ *
+ */
diff --git a/usr.sbin/xntpd/parse/clk_trimble.c b/usr.sbin/xntpd/parse/clk_trimble.c
new file mode 100644
index 0000000..4cb96e2
--- /dev/null
+++ b/usr.sbin/xntpd/parse/clk_trimble.c
@@ -0,0 +1,140 @@
+#if defined(REFCLOCK) && (defined(PARSE) || defined(PARSEPPS)) && defined(CLOCK_TRIMSV6)
+/*
+ * /src/NTP/REPOSITORY/v3/parse/clk_trimble.c,v 3.9 1994/02/02 17:45:27 kardel Exp
+ *
+ * Trimble SV6 clock support
+ */
+
+#include "sys/types.h"
+#include "sys/time.h"
+#include "sys/errno.h"
+#include "ntp_fp.h"
+#include "ntp_unixtime.h"
+#include "ntp_calendar.h"
+
+#include "parse.h"
+
+/* 0000000000111111111122222222223333333 / char
+ * 0123456789012345678901234567890123456 \ posn
+ * >RTMhhmmssdddDDMMYYYYoodnnvrrrrr;*xx< Actual
+ * ----33445566600112222BB7__-_____--99- Parse
+ * >RTM 1 ;* <", Check
+ */
+
+#define hexval(x) (('0' <= (x) && (x) <= '9') ? (x) - '0' : \
+ ('a' <= (x) && (x) <= 'f') ? (x) - 'a' + 10 : \
+ ('A' <= (x) && (x) <= 'F') ? (x) - 'A' + 10 : \
+ -1)
+#define O_USEC O_WDAY
+#define O_GPSFIX O_FLAGS
+#define O_CHKSUM O_UTCHOFFSET
+static struct format trimsv6_fmt =
+{ { { 13, 2 }, {15, 2}, { 17, 4}, /* Day, Month, Year */
+ { 4, 2 }, { 6, 2}, { 8, 2}, /* Hour, Minute, Second */
+ { 10, 3 }, {23, 1}, { 0, 0}, /* uSec, FIXes (WeekDAY, FLAGS, ZONE) */
+ { 34, 2 }, { 0, 0}, { 21, 2}, /* cksum, -, utcS (UTC[HMS]OFFSET) */
+ },
+ ">RTM 1 ;* <",
+ 0
+};
+
+static unsigned LONG cvt_trimsv6();
+
+clockformat_t clock_trimsv6 =
+{
+ (unsigned LONG (*)())0, /* XXX?: no input handling */
+ cvt_trimsv6, /* Trimble conversion */
+ syn_simple, /* easy time stamps for RS232 (fallback) */
+ pps_simple, /* easy PPS monitoring */
+ (unsigned LONG (*)())0, /* no time code synthesizer monitoring */
+ (void *)&trimsv6_fmt, /* conversion configuration */
+ "Trimble SV6",
+ 37, /* string buffer */
+ F_START|F_END|SYNC_START|SYNC_ONE, /* paket START/END delimiter, START synchronisation, PPS ONE sampling */
+ 0, /* XXX?: no private data (complete messages) */
+ { 0, 0},
+ '>',
+ '<',
+ '\0'
+};
+
+static unsigned LONG
+cvt_trimsv6(buffer, size, format, clock)
+ register char *buffer;
+ register int size;
+ register struct format *format;
+ register clocktime_t *clock;
+{
+ LONG gpsfix;
+ u_char calc_csum = 0;
+ long recv_csum;
+ int i;
+
+ if (!Strok(buffer, format->fixed_string)) return CVT_NONE;
+#define OFFS(x) format->field_offsets[(x)].offset
+#define STOI(x, y) \
+ Stoi(&buffer[OFFS(x)], y, \
+ format->field_offsets[(x)].length)
+ if ( STOI(O_DAY, &clock->day) ||
+ STOI(O_MONTH, &clock->month) ||
+ STOI(O_YEAR, &clock->year) ||
+ STOI(O_HOUR, &clock->hour) ||
+ STOI(O_MIN, &clock->minute) ||
+ STOI(O_SEC, &clock->second) ||
+ STOI(O_USEC, &clock->usecond)||
+ STOI(O_GPSFIX, &gpsfix)
+ ) return CVT_FAIL|CVT_BADFMT;
+
+ clock->usecond *= 1000;
+ /* Check that the checksum is right */
+ for (i=OFFS(O_CHKSUM)-1; i >= 0; i--) calc_csum ^= buffer[i];
+ recv_csum = (hexval(buffer[OFFS(O_CHKSUM)]) << 4) |
+ hexval(buffer[OFFS(O_CHKSUM)+1]);
+ if (recv_csum < 0) return CVT_FAIL|CVT_BADTIME;
+ if (((u_char) recv_csum) != calc_csum) return CVT_FAIL|CVT_BADTIME;
+
+ clock->utcoffset = 0;
+
+ /* What should flags be set to ? */
+ clock->flags = PARSEB_UTC;
+
+ /* if the current GPS fix is 9 (unknown), reject */
+ if (0 > gpsfix || gpsfix > 9) clock->flags |= PARSEB_POWERUP;
+
+ return CVT_OK;
+}
+#endif /* defined(PARSE) && defined(CLOCK_TRIMSV6) */
+
+/*
+ * History:
+ *
+ * clk_trimble.c,v
+ * Revision 3.9 1994/02/02 17:45:27 kardel
+ * rcs ids fixed
+ *
+ * Revision 3.7 1994/01/25 19:05:17 kardel
+ * 94/01/23 reconcilation
+ *
+ * Revision 3.6 1993/10/30 09:44:45 kardel
+ * conditional compilation flag cleanup
+ *
+ * Revision 3.5 1993/10/09 15:01:35 kardel
+ * file structure unified
+ *
+ * revision 3.4
+ * date: 1993/10/08 14:44:51; author: kardel;
+ * trimble - initial working version
+ *
+ * revision 3.3
+ * date: 1993/10/03 19:10:50; author: kardel;
+ * restructured I/O handling
+ *
+ * revision 3.2
+ * date: 1993/09/27 21:07:17; author: kardel;
+ * Trimble alpha integration
+ *
+ * revision 3.1
+ * date: 1993/09/26 23:40:29; author: kardel;
+ * new parse driver logic
+ *
+ */
diff --git a/usr.sbin/xntpd/parse/clk_trimtaip.c b/usr.sbin/xntpd/parse/clk_trimtaip.c
new file mode 100644
index 0000000..87538f1
--- /dev/null
+++ b/usr.sbin/xntpd/parse/clk_trimtaip.c
@@ -0,0 +1,140 @@
+#if defined(REFCLOCK) && (defined(PARSE) || defined(PARSEPPS)) && defined(CLOCK_TRIMTAIP)
+/*
+ * $Header: /src/NTP/REPOSITORY/v3/parse/clk_trimble.c,v 3.9 1994/02/02 17:45:27 kardel Exp $
+ *
+ * Trimble SV6 clock support
+ */
+
+#include "sys/types.h"
+#include "sys/time.h"
+#include "sys/errno.h"
+#include "ntp_fp.h"
+#include "ntp_unixtime.h"
+#include "ntp_calendar.h"
+
+#include "parse.h"
+
+/* 0000000000111111111122222222223333333 / char
+ * 0123456789012345678901234567890123456 \ posn
+ * >RTMhhmmssdddDDMMYYYYoodnnvrrrrr;*xx< Actual
+ * ----33445566600112222BB7__-_____--99- Parse
+ * >RTM 1 ;* <", Check
+ */
+
+#define hexval(x) (('0' <= (x) && (x) <= '9') ? (x) - '0' : \
+ ('a' <= (x) && (x) <= 'f') ? (x) - 'a' + 10 : \
+ ('A' <= (x) && (x) <= 'F') ? (x) - 'A' + 10 : \
+ -1)
+#define O_USEC O_WDAY
+#define O_GPSFIX O_FLAGS
+#define O_CHKSUM O_UTCHOFFSET
+static struct format trimsv6_fmt =
+{ { { 13, 2 }, {15, 2}, { 17, 4}, /* Day, Month, Year */
+ { 4, 2 }, { 6, 2}, { 8, 2}, /* Hour, Minute, Second */
+ { 10, 3 }, {23, 1}, { 0, 0}, /* uSec, FIXes (WeekDAY, FLAGS, ZONE) */
+ { 34, 2 }, { 0, 0}, { 21, 2}, /* cksum, -, utcS (UTC[HMS]OFFSET) */
+ },
+ ">RTM 1 ;* <",
+ 0
+};
+
+static unsigned LONG cvt_trimtaip();
+
+clockformat_t clock_trimtaip =
+{
+ (unsigned LONG (*)())0, /* no input handling */
+ cvt_trimtaip, /* Trimble conversion */
+ syn_simple, /* easy time stamps for RS232 (fallback) */
+ pps_simple, /* easy PPS monitoring */
+ (unsigned LONG (*)())0, /* no time code synthesizer monitoring */
+ (void *)&trimsv6_fmt, /* conversion configuration */
+ "Trimble SV6/TAIP",
+ 37, /* string buffer */
+ F_START|F_END|SYNC_START|SYNC_ONE, /* paket START/END delimiter, START synchronisation, PPS ONE sampling */
+ 0, /* no private data */
+ { 0, 0},
+ '>',
+ '<',
+ '\0'
+};
+
+static unsigned LONG
+cvt_trimtaip(buffer, size, format, clock)
+ register char *buffer;
+ register int size;
+ register struct format *format;
+ register clocktime_t *clock;
+{
+ LONG gpsfix;
+ u_char calc_csum = 0;
+ long recv_csum;
+ int i;
+
+ if (!Strok(buffer, format->fixed_string)) return CVT_NONE;
+#define OFFS(x) format->field_offsets[(x)].offset
+#define STOI(x, y) \
+ Stoi(&buffer[OFFS(x)], y, \
+ format->field_offsets[(x)].length)
+ if ( STOI(O_DAY, &clock->day) ||
+ STOI(O_MONTH, &clock->month) ||
+ STOI(O_YEAR, &clock->year) ||
+ STOI(O_HOUR, &clock->hour) ||
+ STOI(O_MIN, &clock->minute) ||
+ STOI(O_SEC, &clock->second) ||
+ STOI(O_USEC, &clock->usecond)||
+ STOI(O_GPSFIX, &gpsfix)
+ ) return CVT_FAIL|CVT_BADFMT;
+
+ clock->usecond *= 1000;
+ /* Check that the checksum is right */
+ for (i=OFFS(O_CHKSUM)-1; i >= 0; i--) calc_csum ^= buffer[i];
+ recv_csum = (hexval(buffer[OFFS(O_CHKSUM)]) << 4) |
+ hexval(buffer[OFFS(O_CHKSUM)+1]);
+ if (recv_csum < 0) return CVT_FAIL|CVT_BADTIME;
+ if (((u_char) recv_csum) != calc_csum) return CVT_FAIL|CVT_BADTIME;
+
+ clock->utcoffset = 0;
+
+ /* What should flags be set to ? */
+ clock->flags = PARSEB_UTC;
+
+ /* if the current GPS fix is 9 (unknown), reject */
+ if (0 > gpsfix || gpsfix > 9) clock->flags |= PARSEB_POWERUP;
+
+ return CVT_OK;
+}
+#endif /* defined(PARSE) && defined(CLOCK_TRIMTAIP) */
+
+/*
+ * History:
+ *
+ * $Log: clk_trimble.c,v $
+ * Revision 3.9 1994/02/02 17:45:27 kardel
+ * rcs ids fixed
+ *
+ * Revision 3.7 1994/01/25 19:05:17 kardel
+ * 94/01/23 reconcilation
+ *
+ * Revision 3.6 1993/10/30 09:44:45 kardel
+ * conditional compilation flag cleanup
+ *
+ * Revision 3.5 1993/10/09 15:01:35 kardel
+ * file structure unified
+ *
+ * revision 3.4
+ * date: 1993/10/08 14:44:51; author: kardel;
+ * trimble - initial working version
+ *
+ * revision 3.3
+ * date: 1993/10/03 19:10:50; author: kardel;
+ * restructured I/O handling
+ *
+ * revision 3.2
+ * date: 1993/09/27 21:07:17; author: kardel;
+ * Trimble alpha integration
+ *
+ * revision 3.1
+ * date: 1993/09/26 23:40:29; author: kardel;
+ * new parse driver logic
+ *
+ */
diff --git a/usr.sbin/xntpd/parse/clk_trimtsip.c b/usr.sbin/xntpd/parse/clk_trimtsip.c
new file mode 100644
index 0000000..422fac1
--- /dev/null
+++ b/usr.sbin/xntpd/parse/clk_trimtsip.c
@@ -0,0 +1,474 @@
+#if defined(REFCLOCK) && (defined(PARSE) || defined(PARSEPPS)) && defined(CLOCK_TRIMTSIP)
+/*
+ * $Header: /home/ncvs/src/usr.sbin/xntpd/parse/clk_trimtsip.c,v 1.1.1.1 1994/09/29 23:01:31 wollman Exp $
+ *
+ * Trimble TSIP support - CURRENTLY VERY MUCH UNDER CONSTRUCTION
+ */
+
+#include "sys/types.h"
+#include "sys/time.h"
+#include "sys/errno.h"
+
+#include "ntp_syslog.h"
+#include "ntp_types.h"
+#include "ntp_fp.h"
+#include "ntp_unixtime.h"
+#include "ntp_calendar.h"
+
+#include "parse.h"
+
+/*
+ * Trimble TSIP parser
+ *
+ * The receiver uses a serial message protocol called Trimble Standard
+ * Interface Protocol (it can support others but this driver only supports
+ * TSIP). Messages in this protocol have the following form:
+ *
+ * <DLE><id> ... <data> ... <DLE><ETX>
+ *
+ * Any bytes within the <data> portion of value 10 hex (<DLE>) are doubled
+ * on transmission and compressed back to one on reception. Otherwise
+ * the values of data bytes can be anything. The serial interface is RS-422
+ * asynchronous using 9600 baud, 8 data bits with odd party (**note** 9 bits
+ * in total!), and 1 stop bit. The protocol supports byte, integer, single,
+ * and double datatypes. Integers are two bytes, sent most significant first.
+ * Singles are IEEE754 single precision floating point numbers (4 byte) sent
+ * sign & exponent first. Doubles are IEEE754 double precision floating point
+ * numbers (8 byte) sent sign & exponent first.
+ * The receiver supports a large set of messages, only a small subset of
+ * which are used here. From driver to receiver the following are used:
+ *
+ * ID Description
+ *
+ * 21 Request current time
+ * 22 Mode Select
+ * 2C Set/Request operating parameters
+ * 2F Request UTC info
+ * 35 Set/Request I/O options
+ *
+ * From receiver to driver the following are recognised:
+ *
+ * ID Description
+ *
+ * 41 GPS Time
+ * 44 Satellite selection, PDOP, mode
+ * 46 Receiver health
+ * 4B Machine code/status
+ * 4C Report operating parameters (debug only)
+ * 4F UTC correction data (used to get leap second warnings)
+ * 55 I/O options (debug only)
+ *
+ * All others are accepted but ignored.
+ *
+ */
+
+extern int debug;
+
+struct trimble
+{
+ u_char t_in_pkt; /* first DLE received */
+ u_char t_dle; /* subsequent DLE received */
+ u_char t_status; /* last status */
+ u_char t_error; /* last error */
+ u_short t_week; /* GPS week */
+ u_short t_weekleap; /* GPS week of next/last week */
+ u_short t_dayleap; /* day in week */
+ u_short t_gpsutc; /* GPS - UTC offset */
+ u_short t_gpsutcleap; /* offset at next/last leap */
+ u_char t_operable; /* receiver feels OK */
+ u_char t_leap; /* possible leap warning */
+};
+
+static unsigned LONG inp_tsip();
+static unsigned LONG cvt_trimtsip();
+
+struct clockformat clock_trimtsip =
+{
+ inp_tsip, /* Trimble TSIP input handler */
+ cvt_trimtsip, /* Trimble TSIP conversion */
+ syn_simple, /* easy time stamps for RS232 (fallback) */
+ pps_simple, /* easy PPS monitoring */
+ (unsigned LONG (*)())0, /* no time code synthesizer monitoring */
+ (void *)0, /* no configuration data */
+ "Trimble SV6/TSIP",
+ 128, /* input buffer */
+ CVT_FIXEDONLY, /* we do our own input handling */
+ sizeof(struct trimble), /* no private data */
+ { 0, 0},
+ '\0',
+ '\0',
+ '\0'
+};
+
+#define ADDSECOND 0x01
+#define DELSECOND 0x02
+
+#define DLE 0x10
+#define ETX 0x03
+
+static unsigned LONG inp_tsip(parseio, ch, ctime)
+ register parse_t *parseio;
+ register unsigned char ch;
+ register timestamp_t *ctime;
+{
+ register struct trimble *t = (struct trimble *)parseio->parse_pdata;
+
+ if (!t)
+ return 0; /* local data not allocated - sigh! */
+
+ if (!t->t_in_pkt && ch != DLE) {
+ /* wait for start of packet */
+#ifdef DEBUG
+ if (debug > 2)
+ printf("sv6+ discarding %2.2x\n", ch);
+#endif
+ return 0;
+ }
+
+ switch (ch) {
+ case DLE:
+ if (!t->t_in_pkt) {
+ t->t_dle = 0;
+ t->t_in_pkt = 1;
+ parseio->parse_index = 0;
+ parseio->parse_data[parseio->parse_index++] = ch;
+ parseio->parse_dtime.parse_stime = *ctime; /* pick up time stamp at packet start */
+ } else if (t->t_dle) {
+ /* Double DLE -> insert a DLE */
+ t->t_dle = 0;
+ parseio->parse_data[parseio->parse_index++] = DLE;
+ } else
+ t->t_dle = 1;
+ break;
+ case ETX:
+ if (t->t_dle) {
+ /* DLE,ETX -> end of packet */
+ parseio->parse_data[parseio->parse_index++] = DLE;
+ parseio->parse_data[parseio->parse_index++] = ch;
+ parseio->parse_data[parseio->parse_index] = '\0';
+ parseio->parse_ldsize = parseio->parse_index+1;
+ bcopy(parseio->parse_data, parseio->parse_ldata, parseio->parse_ldsize);
+ t->t_in_pkt = t->t_dle = 0;
+ return 1;
+ }
+ /* fall into ... */
+ default:
+ t->t_dle = 0;
+ parseio->parse_data[parseio->parse_index++] = ch;
+ }
+
+ return (parseio->parse_index == parseio->parse_dsize-1); /* buffer full - attempt to parse (likely to fail) */
+}
+
+#define GPSORIGIN 2524953600 /* NTP origin - GPS origin in seconds */
+#define SECSPERWEEK 604800 /* seconds per week - GPS tells us about weeks */
+#define L_UF_SCALE 4294967296.0 /* scale a float fraction to l_uf units */
+
+/*
+ * mapping union for ints, floats, doubles for both input & output to the
+ * receiver
+ *
+ * CAVEAT: must disappear - non portable
+ */
+
+union {
+ u_char bd[8];
+ int iv;
+ float fv;
+ double dv;
+} uval;
+
+static float getflt P((u_char *));
+static double getdbl P((u_char *));
+static int getint P((u_char *));
+
+/*
+ * cvt_trimtsip
+ *
+ * convert TSIP type format
+ */
+static unsigned LONG
+cvt_trimtsip(buffer, size, format, clock, t)
+ register char *buffer;
+ register int size;
+ register struct format *format;
+ register clocktime_t *clock;
+ register struct trimble *t;
+{
+#define mb(_X_) (buffer[2+(_X_)]) /* shortcut for buffer access */
+ register u_char cmd;
+
+ if (!t) {
+#ifdef DEBUG
+ if (debug) printf("sv6+ BAD call (t=0)\n");
+#endif
+ return CVT_NONE; /* local data not allocated - sigh! */
+ }
+
+ if ((size < 4) ||
+ (buffer[0] != DLE) ||
+ (buffer[size-1] != ETX) ||
+ (buffer[size-2] != DLE))
+ {
+#ifdef DEBUG
+ if (debug > 2) {
+ int i;
+
+ printf("sv6+ BAD packet, size %d:\n ", size);
+ for (i = 0; i < size; i++) {
+ printf ("%2.2x, ", buffer[i]&0xff);
+ if (i%16 == 15) printf("\n\t");
+ }
+ printf("\n");
+ }
+#endif
+ return CVT_NONE;
+ }
+ else
+ {
+ cmd = buffer[1];
+
+#ifdef DEBUG
+ if (debug > 1)
+ switch(cmd)
+ {
+ case 0x41:
+ printf("sv6+ gps time: %f, %d, %f\n",
+ getflt(&mb(0)), getint(&mb(4)), getflt(&mb(6)));
+ break;
+
+ case 0x44:
+ printf("sv6+ sats: %2x, %2d %2d %2d %2d, %.2f\n",
+ mb(0), mb(1), mb(2), mb(3), mb(4), getflt(&mb(5)));
+ break;
+
+ case 0x45:
+ printf("sv6+ software: %d.%d (19%d/%d/%d)\n",
+ mb(0)&0xff, mb(1)&0xff, mb(4)&0xff, mb(2)&0xff, mb(3)&0xff);
+ break;
+
+ case 0x46:
+ printf("sv6+ health: %2x %2x\n",
+ mb(0), mb(1));
+ break;
+
+ case 0x48:
+ printf("sv6+ gps message: '%.22s'\n", &mb(0));
+ break;
+
+ case 0x4b:
+ printf("sv6+ status: %2d %2x\n",
+ mb(0), mb(1));
+ break;
+
+ case 0x4c:
+ printf("sv6+ op params: %2x %.1f %.1f %.1f %.1f\n",
+ mb(0), getflt(&mb(1)), getflt(&mb(5)),
+ getflt(&mb(9)), getflt(&mb(13)));
+ break;
+
+ case 0x4f:
+ printf("sv6+ utc data: %.3e %.3e %d %d %d %d %d\n",
+ getdbl(&mb(0)), getflt(&mb(8)), getint(&mb(18)),
+ getint(&mb(12)), getint(&mb(20)), getint(&mb(22)), getint(&mb(24)));
+ break;
+
+ case 0x54:
+ /*printf("sv6+ bias and rate: %.1fm %.2fm/s at %.1fs\n",
+ getflt(&mb(0)), getflt(&mb(4)), getflt(&mb(8))); ignore it*/
+ break;
+
+ case 0x55:
+ printf("sv6+ io opts: %2x %2x %2x %2x\n",
+ mb(0), mb(1), mb(2), mb(3));
+ break;
+
+ case 0x8f:
+ {
+#define RTOD (180.0 / 3.1415926535898)
+ double lat = getdbl(&mb(2));
+ double lng = getdbl(&mb(10));
+ printf("sv6+ last fix: %2.2x %d lat %f %c, long %f %c, alt %.2fm\n",
+ mb(1)&0xff, mb(40)&0xff,
+ ((lat < 0) ? (-lat) : (lat))*RTOD, (lat < 0 ? 'S' : 'N'),
+ ((lng < 0) ? (-lng) : (lng))*RTOD, (lng < 0 ? 'W' : 'E'),
+ getdbl(&mb(18)));
+ }
+ break;
+
+ case 0x40:
+ case 0x5b:
+ case 0x6d:
+ /* Ignore */
+ break;
+
+ default:
+ printf("sv6+ cmd ignored: %2x, length: %d\n",
+ cmd, size);
+ break;
+ }
+#endif /* DEBUG */
+
+ switch(cmd)
+ {
+ case 0x41:
+ { /* GPS time */
+ float secs = getflt(&mb(0));
+ int week = getint(&mb(4));
+ int secint;
+ float secfrac;
+ l_fp gpstime, off;
+
+ if (secs <= 0)
+ {
+#ifdef DEBUG
+ if (debug)
+ printf("sv6+ seconds <= 0 (%e), setting POWERUP\n");
+#endif
+ clock->flags = PARSEB_POWERUP;
+ return CVT_OK;
+ }
+
+ /* time OK */
+ secint = secs; /* integer part, hopefully */
+ secfrac = secs - secint; /* 0.0 <= secfrac < 1.0 */
+ secint -= getflt(&mb(6)); /* UTC offset */
+ gpstime.l_ui = week*SECSPERWEEK + secint + GPSORIGIN; /* convert to NTP time */
+ gpstime.l_uf = secfrac*L_UF_SCALE;
+
+ clock->utctime = gpstime.l_ui - JAN_1970;
+ TSFTOTVU(gpstime.l_uf, clock->usecond);
+
+ if (t->t_leap == ADDSECOND)
+ clock->flags |= PARSEB_LEAPADD;
+
+ if (t->t_leap == DELSECOND)
+ clock->flags |= PARSEB_LEAPDEL;
+
+ if (t->t_operable)
+ clock->flags &= ~(PARSEB_NOSYNC|PARSEB_POWERUP);
+ else
+ clock->flags |= PARSEB_NOSYNC;
+ return CVT_OK;
+
+ } /* case 0x41 */
+ break;
+
+ case 0x46:
+ {
+ /* sv6+ health */
+ u_char status = t->t_status = mb(0);
+ u_char error = t->t_error = mb(1);
+
+ if (status == 0 || status == 9 || status == 10 || status == 11)
+ {
+ if (!t->t_operable)
+ syslog(LOG_ERR, "Trimble clock synced");
+ t->t_operable = 1;
+ }
+ else
+ {
+ if (t->t_operable)
+ syslog(LOG_ERR, "Trimble clock unsynced");
+ t->t_operable = 0;
+ }
+ }
+ break;
+
+ case 0x4f:
+ {
+ /* UTC correction data - derive a leap warning */
+ int tls = t->t_gpsutc = getint(&mb(12)); /* current leap correction (GPS-UTC) */
+ int wnlsf = t->t_weekleap = getint(&mb(20)); /* week no of leap correction */
+ int dn = t->t_dayleap = getint(&mb(22)); /* day in week of leap correction */
+ int tlsf = t->t_gpsutcleap = getint(&mb(24)); /* new leap correction */
+ U_LONG now, leaptime;
+
+ t->t_week = getint(&mb(18)); /* current week no */
+
+ /* this stuff hasn't been tested yet... */
+ now = clock->utctime + JAN_1970; /* now in GPS seconds */
+ leaptime = (wnlsf*7 + dn)*86400; /* time of leap in GPS seconds */
+ if ((leaptime > now) && ((leaptime-now) < 86400*28))
+ {
+ /* generate a leap warning */
+ if (tlsf > tls)
+ t->t_leap = ADDSECOND;
+ else
+ t->t_leap = DELSECOND;
+ }
+ else
+ {
+ t->t_leap = 0;
+ }
+ }
+ break;
+
+ default:
+ /* it's validly formed, but we don't care about it! */
+ break;
+ }
+ }
+ return CVT_SKIP;
+}
+
+/*
+ * getflt, getdbl, getint convert fields in the incoming data into the
+ * appropriate type of item
+ *
+ * CAVEAT: these routines are currently definitely byte order dependent
+ * and assume Representation(float) == IEEE754
+ * These functions MUST be converted to portable versions (especially
+ * converting the float representation into ntp_fp formats in order
+ * to avoid floating point operations at all!
+ */
+
+static float
+getflt(bp)
+ u_char *bp;
+{
+ uval.bd[0] = *bp++;
+ uval.bd[1] = *bp++;
+ uval.bd[2] = *bp++;
+ uval.bd[3] = *bp;
+ return uval.fv;
+}
+
+static double
+getdbl(bp)
+ u_char *bp;
+{
+ uval.bd[0] = *bp++;
+ uval.bd[1] = *bp++;
+ uval.bd[2] = *bp++;
+ uval.bd[3] = *bp++;
+ uval.bd[4] = *bp++;
+ uval.bd[5] = *bp++;
+ uval.bd[6] = *bp++;
+ uval.bd[7] = *bp;
+ return uval.dv;
+}
+
+static int
+getint(bp)
+ u_char *bp;
+{
+ uval.bd[2] = *bp++;
+ uval.bd[3] = *bp;
+ if (uval.bd[2] & 0x80) /* sign-extend */
+ uval.bd[0] = uval.bd[1] = 0xff;
+ else
+ uval.bd[0] = uval.bd[1] = 0;
+ return uval.iv;
+}
+
+#endif /* defined(PARSE) && defined(CLOCK_TRIMTSIP) */
+
+/*
+ * History:
+ *
+ * $Log: clk_trimtsip.c,v $
+ * Revision 1.1.1.1 1994/09/29 23:01:31 wollman
+ * xntp 3.4e from Dave Mills @ UDel
+ *
+ */
diff --git a/usr.sbin/xntpd/parse/empty.c b/usr.sbin/xntpd/parse/empty.c
new file mode 100644
index 0000000..91b777a
--- /dev/null
+++ b/usr.sbin/xntpd/parse/empty.c
@@ -0,0 +1,7 @@
+/*
+ * Well, some ranlibs, ar's or compilers react funny
+ * if asked to do nothing but build empty valid files
+ * I would have preferred to a no or at least a static
+ * symbol here...
+ */
+char * _____empty__ = "empty .o file";
diff --git a/usr.sbin/xntpd/parse/parse.c b/usr.sbin/xntpd/parse/parse.c
new file mode 100644
index 0000000..11dacd9
--- /dev/null
+++ b/usr.sbin/xntpd/parse/parse.c
@@ -0,0 +1,1309 @@
+#if defined(REFCLOCK) && (defined(PARSE) || defined(PARSEPPS))
+/*
+ * /src/NTP/REPOSITORY/v3/parse/parse.c,v 3.27 1994/06/01 08:18:33 kardel Exp
+ *
+ * parse.c,v 3.27 1994/06/01 08:18:33 kardel Exp
+ *
+ * Parser module for reference clock
+ *
+ * PARSEKERNEL define switches between two personalities of the module
+ * if PARSEKERNEL is defined this module can be used with dcf77sync.c as
+ * a PARSEKERNEL kernel module. In this case the time stamps will be
+ * a struct timeval.
+ * when PARSEKERNEL is not defined NTP time stamps will be used.
+ *
+ * Copyright (c) 1992,1993,1994
+ * Frank Kardel Friedrich-Alexander Universitaet Erlangen-Nuernberg
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ */
+
+#if !(defined(lint) || defined(__GNUC__))
+static char rcsid[] = "parse.c,v 3.19 1994/01/25 19:05:20 kardel Exp";
+#endif
+
+#include "sys/types.h"
+#include "sys/time.h"
+#include "sys/errno.h"
+
+#include "ntp_fp.h"
+#include "ntp_unixtime.h"
+#include "ntp_calendar.h"
+
+#include "ntp_machine.h"
+
+#if defined(PARSESTREAM) && (defined(SYS_SUNOS4) || defined(SYS_SOLARIS)) && defined(STREAM)
+/*
+ * Sorry, but in SunOS 4.x AND Solaris 2.x kernels there are no
+ * mem* operations. I don't want them - bcopy, bzero
+ * are fine in the kernel
+ */
+#ifndef NTP_NEED_BOPS
+#define NTP_NEED_BOPS
+#endif
+#else
+#ifndef NTP_NEED_BOPS
+#ifndef bzero
+#define bzero(_X_, _Y_) memset(_X_, 0, _Y_)
+#define bcopy(_X_, _Y_, _Z_) memmove(_Y_, _X_, _Z_)
+#endif
+#endif
+#endif
+
+#include "parse.h"
+
+#include "ntp_stdlib.h"
+
+#ifdef PARSESTREAM
+#include "sys/parsestreams.h"
+#endif
+
+extern clockformat_t *clockformats[];
+extern unsigned short nformats;
+
+static u_long timepacket();
+
+/*
+ * strings support usually not in kernel - duplicated, but what the heck
+ */
+static int
+Strlen(s)
+ register char *s;
+{
+ register int c;
+
+ c = 0;
+ if (s)
+ {
+ while (*s++)
+ {
+ c++;
+ }
+ }
+ return c;
+}
+
+static int
+Strcmp(s, t)
+ register char *s;
+ register char *t;
+{
+ register int c = 0;
+
+ if (!s || !t || (s == t))
+ {
+ return 0;
+ }
+
+ while (!(c = *s++ - *t++) && *s && *t)
+ /* empty loop */;
+
+ return c;
+}
+
+static int
+timedout(parseio, ctime)
+ register parse_t *parseio;
+ register timestamp_t *ctime;
+{
+ struct timeval delta;
+
+#ifdef PARSEKERNEL
+ delta.tv_sec = ctime->tv.tv_sec - parseio->parse_lastchar.tv.tv_sec;
+ delta.tv_usec = ctime->tv.tv_usec - parseio->parse_lastchar.tv.tv_usec;
+ if (delta.tv_usec < 0)
+ {
+ delta.tv_sec -= 1;
+ delta.tv_usec += 1000000;
+ }
+#else
+ extern long tstouslo[];
+ extern long tstousmid[];
+ extern long tstoushi[];
+
+ l_fp delt;
+
+ delt = ctime->fp;
+ L_SUB(&delt, &parseio->parse_lastchar.fp);
+ TSTOTV(&delt, &delta);
+#endif
+
+ if (timercmp(&delta, &parseio->parse_timeout, >))
+ {
+ parseprintf(DD_PARSE, ("parse: timedout: TRUE\n"));
+ return 1;
+ }
+ else
+ {
+ parseprintf(DD_PARSE, ("parse: timedout: FALSE\n"));
+ return 0;
+ }
+}
+
+/*
+ * setup_bitmaps
+ * WARNING: NOT TO BE CALLED CONCURRENTLY WITH
+ * parse_ioread, parse_ioend, parse_ioinit
+ */
+static int
+setup_bitmaps(parseio, low, high)
+ register parse_t *parseio;
+ register unsigned short low;
+ register unsigned short high;
+{
+ register unsigned short i;
+ register int f = 0;
+ register clockformat_t *fmt;
+ register unsigned short index, mask, plen;
+
+ if ((low >= high) ||
+ (high > nformats))
+ {
+ parseprintf(DD_PARSE, ("setup_bitmaps: failed: bounds error (low=%d, high=%d, nformats=%d)\n", low, high, nformats));
+ return 0;
+ }
+
+ bzero(parseio->parse_startsym, sizeof (parseio->parse_startsym));
+ bzero(parseio->parse_endsym, sizeof (parseio->parse_endsym));
+ bzero(parseio->parse_syncsym, sizeof (parseio->parse_syncsym));
+
+ plen = 0;
+
+ parseio->parse_syncflags = 0;
+ parseio->parse_timeout.tv_sec = 0;
+ parseio->parse_timeout.tv_usec = 0;
+
+ /*
+ * gather bitmaps of possible start and end values
+ */
+ for (i=low; i < high; i++)
+ {
+ fmt = clockformats[i];
+
+ if (!(parseio->parse_flags & PARSE_FIXED_FMT) &&
+ (fmt->flags & CVT_FIXEDONLY)) {
+ if (parseio->parse_dsize < fmt->length)
+ parseio->parse_dsize = fmt->length;
+ continue;
+ }
+
+ if (fmt->flags & F_START)
+ {
+ index = fmt->startsym >> 3;
+ mask = 1 << (fmt->startsym & 0x7);
+
+ if (parseio->parse_endsym[index] & mask)
+ {
+#ifdef PARSEKERNEL
+ printf("parse: setup_bitmaps: failed: START symbol collides with END symbol (format %d)\n", i);
+#else
+ syslog(LOG_ERR, "parse: setup_bitmaps: failed: START symbol collides with END symbol (format %d)\n", i);
+#endif
+ return 0;
+ }
+ else
+ {
+ parseio->parse_startsym[index] |= mask;
+ f = 1;
+ }
+ }
+
+ if (fmt->flags & F_END)
+ {
+ index = fmt->endsym >> 3;
+ mask = 1 << (fmt->endsym & 0x7);
+
+ if (parseio->parse_startsym[index] & mask)
+ {
+#ifdef PARSEKERNEL
+ printf("parse: setup_bitmaps: failed: END symbol collides with START symbol (format %d)\n", i);
+#else
+ syslog(LOG_ERR, "parse: setup_bitmaps: failed: END symbol collides with START symbol (format %d)\n", i);
+#endif
+ return 0;
+ }
+ else
+ {
+ parseio->parse_endsym[index] |= mask;
+ f = 1;
+ }
+ }
+
+ if (fmt->flags & SYNC_CHAR)
+ {
+ parseio->parse_syncsym[fmt->syncsym >> 3] |= (1 << (fmt->syncsym & 0x7));
+ }
+
+ parseio->parse_syncflags |= fmt->flags & (SYNC_START|SYNC_END|SYNC_CHAR|SYNC_ONE|SYNC_ZERO|SYNC_TIMEOUT|SYNC_SYNTHESIZE);
+
+ if (((fmt->flags & (SYNC_TIMEOUT|CVT_FIXEDONLY)) == (SYNC_TIMEOUT|CVT_FIXEDONLY)) &&
+ ((parseio->parse_timeout.tv_sec || parseio->parse_timeout.tv_usec) ? timercmp(&parseio->parse_timeout, &fmt->timeout, >) : 1))
+ {
+ parseio->parse_timeout = fmt->timeout;
+ }
+
+ if (parseio->parse_dsize < fmt->length)
+ parseio->parse_dsize = fmt->length;
+ }
+
+ if (parseio->parse_pdata)
+ {
+ FREE(parseio->parse_pdata, parseio->parse_plen);
+ parseio->parse_plen = 0;
+ parseio->parse_pdata = (void *)0;
+ }
+
+ if (!f && ((int)(high - low) > 1))
+ {
+ /*
+ * need at least one start or end symbol
+ */
+#ifdef PARSEKERNEL
+ printf("parse: setup_bitmaps: failed: neither START nor END symbol defined\n");
+#else
+ syslog(LOG_ERR, "parse: setup_bitmaps: failed: neither START nor END symbol defined\n");
+#endif
+ return 0;
+ }
+
+ if ((high - low == 1) && (clockformats[low]->flags & CVT_FIXEDONLY) &&
+ (clockformats[low]->plen))
+ {
+ parseio->parse_plen = clockformats[low]->plen;
+ parseio->parse_pdata = (void *)MALLOC(parseio->parse_plen);
+
+ if (!parseio->parse_pdata)
+ {
+ /*
+ * no memory
+ */
+#ifdef PARSEKERNEL
+ printf("parse: setup_bitmaps: failed: no memory for private data\n");
+#else
+ syslog(LOG_ERR, "parse: setup_bitmaps: failed: no memory for private data\n");
+#endif
+ return 0;
+ }
+ bzero((char *)parseio->parse_pdata, parseio->parse_plen);
+ }
+
+ return 1;
+}
+
+/*ARGSUSED*/
+int
+parse_ioinit(parseio)
+ register parse_t *parseio;
+{
+ parseprintf(DD_PARSE, ("parse_iostart\n"));
+
+ parseio->parse_plen = 0;
+ parseio->parse_pdata = (void *)0;
+
+ if (!setup_bitmaps(parseio, 0, nformats))
+ return 0;
+
+ parseio->parse_data = MALLOC(parseio->parse_dsize * 2 + 2);
+ if (!parseio->parse_data)
+ {
+ parseprintf(DD_PARSE, ("init failed: malloc for data area failed\n"));
+ return 0;
+ }
+
+ /*
+ * leave room for '\0'
+ */
+ parseio->parse_ldata = parseio->parse_data + parseio->parse_dsize + 1;
+ parseio->parse_lformat = 0;
+ parseio->parse_badformat = 0;
+ parseio->parse_ioflags = PARSE_IO_CS7; /* usual unix default */
+ parseio->parse_flags = 0; /* true samples */
+ parseio->parse_index = 0;
+ parseio->parse_ldsize = 0;
+
+ return 1;
+}
+
+/*ARGSUSED*/
+void
+parse_ioend(parseio)
+ register parse_t *parseio;
+{
+ parseprintf(DD_PARSE, ("parse_ioend\n"));
+
+ if (parseio->parse_pdata)
+ FREE(parseio->parse_pdata, parseio->parse_plen);
+
+ if (parseio->parse_data)
+ FREE(parseio->parse_data, parseio->parse_dsize * 2 + 2);
+}
+
+/*ARGSUSED*/
+int
+parse_ioread(parseio, ch, ctime)
+ register parse_t *parseio;
+ register unsigned char ch;
+ register timestamp_t *ctime;
+{
+ register unsigned updated = CVT_NONE;
+ register unsigned short low, high;
+ register unsigned index, mask;
+
+ /*
+ * within STREAMS CSx (x < 8) chars still have the upper bits set
+ * so we normalize the characters by masking unecessary bits off.
+ */
+ switch (parseio->parse_ioflags & PARSE_IO_CSIZE)
+ {
+ case PARSE_IO_CS5:
+ ch &= 0x1F;
+ break;
+
+ case PARSE_IO_CS6:
+ ch &= 0x3F;
+ break;
+
+ case PARSE_IO_CS7:
+ ch &= 0x7F;
+ break;
+
+ case PARSE_IO_CS8:
+ break;
+ }
+
+ parseprintf(DD_PARSE, ("parse_ioread(0x%x, char=0x%x, ..., ...)\n", (unsigned int)parseio, ch & 0xFF));
+
+ if (parseio->parse_flags & PARSE_FIXED_FMT)
+ {
+ if (!clockformats[parseio->parse_lformat]->convert)
+ {
+ parseprintf(DD_PARSE, ("parse_ioread: input dropped.\n"));
+ return CVT_NONE;
+ }
+
+ if (clockformats[parseio->parse_lformat]->input)
+ {
+ if (clockformats[parseio->parse_lformat]->input(parseio, ch, ctime))
+ updated = timepacket(parseio); /* got a datagram - process */
+
+ low = high = 0; /* all done - just post processing */
+ }
+ else
+ {
+ low = parseio->parse_lformat;
+ high = low + 1; /* scan just one format */
+ }
+ }
+ else
+ {
+ low = 0;
+ high = nformats; /* scan all non fixed formats */
+ }
+
+ if (low != high)
+ {
+ index = ch >> 3;
+ mask = 1 << (ch & 0x7);
+
+ if ((parseio->parse_syncflags & SYNC_CHAR) &&
+ (parseio->parse_syncsym[index] & mask))
+ {
+ register clockformat_t *fmt;
+ register unsigned short i;
+ /*
+ * got a sync event - call sync routine
+ */
+
+ for (i = low; i < high; i++)
+ {
+ fmt = clockformats[i];
+
+ if ((fmt->flags & SYNC_CHAR) &&
+ (fmt->syncsym == ch))
+ {
+ parseprintf(DD_PARSE, ("parse_ioread: SYNC_CHAR event\n"));
+ if (fmt->syncevt)
+ fmt->syncevt(parseio, ctime, fmt->data, SYNC_CHAR);
+ }
+ }
+ }
+
+ if ((((parseio->parse_syncflags & SYNC_START) &&
+ (parseio->parse_startsym[index] & mask)) ||
+ (parseio->parse_index == 0)) ||
+ ((parseio->parse_syncflags & SYNC_TIMEOUT) &&
+ timedout(parseio, ctime)))
+ {
+ register unsigned short i;
+ /*
+ * packet start - re-fill buffer
+ */
+ if (parseio->parse_index)
+ {
+ /*
+ * filled buffer - thus not end character found
+ * do processing now
+ */
+ parseio->parse_data[parseio->parse_index] = '\0';
+
+ updated = timepacket(parseio);
+ bcopy(parseio->parse_data, parseio->parse_ldata, parseio->parse_index+1);
+ parseio->parse_ldsize = parseio->parse_index+1;
+ if (parseio->parse_syncflags & SYNC_TIMEOUT)
+ parseio->parse_dtime.parse_stime = *ctime;
+ }
+
+ /*
+ * could be a sync event - call sync routine if needed
+ */
+ if (parseio->parse_syncflags & SYNC_START)
+ for (i = low; i < high; i++)
+ {
+ register clockformat_t *fmt = clockformats[i];
+
+ if ((parseio->parse_index == 0) ||
+ ((fmt->flags & SYNC_START) && (fmt->startsym == ch)))
+ {
+ parseprintf(DD_PARSE, ("parse_ioread: SYNC_START event\n"));
+ if (fmt->syncevt)
+ fmt->syncevt(parseio, ctime, fmt->data, SYNC_START);
+ }
+ }
+ parseio->parse_index = 1;
+ parseio->parse_data[0] = ch;
+ parseprintf(DD_PARSE, ("parse: parse_ioread: buffer start\n"));
+ }
+ else
+ {
+ register unsigned short i;
+
+ if (parseio->parse_index < parseio->parse_dsize)
+ {
+ /*
+ * collect into buffer
+ */
+ parseprintf(DD_PARSE, ("parse: parse_ioread: buffer[%d] = 0x%x\n", parseio->parse_index, ch));
+ parseio->parse_data[parseio->parse_index++] = ch;
+ }
+
+ if ((parseio->parse_endsym[index] & mask) ||
+ (parseio->parse_index >= parseio->parse_dsize))
+ {
+ /*
+ * packet end - process buffer
+ */
+ if (parseio->parse_syncflags & SYNC_END)
+ for (i = low; i < high; i++)
+ {
+ register clockformat_t *fmt = clockformats[i];
+
+ if ((fmt->flags & SYNC_END) && (fmt->endsym == ch))
+ {
+ parseprintf(DD_PARSE, ("parse_ioread: SYNC_END event\n"));
+ if (fmt->syncevt)
+ fmt->syncevt(parseio, ctime, fmt->data, SYNC_END);
+ }
+ }
+ parseio->parse_data[parseio->parse_index] = '\0';
+ updated = timepacket(parseio);
+ bcopy(parseio->parse_data, parseio->parse_ldata, parseio->parse_index+1);
+ parseio->parse_ldsize = parseio->parse_index+1;
+ parseio->parse_index = 0;
+ parseprintf(DD_PARSE, ("parse: parse_ioread: buffer end\n"));
+ }
+ }
+ }
+
+ if ((updated == CVT_NONE) &&
+ (parseio->parse_flags & PARSE_FIXED_FMT) &&
+ (parseio->parse_syncflags & SYNC_SYNTHESIZE) &&
+ ((parseio->parse_dtime.parse_status & CVT_MASK) == CVT_OK) &&
+ clockformats[parseio->parse_lformat]->synth)
+ {
+ updated = clockformats[parseio->parse_lformat]->synth(parseio, ctime);
+ }
+
+ /*
+ * remember last character time
+ */
+ parseio->parse_lastchar = *ctime;
+
+#ifdef DEBUG
+ if ((updated & CVT_MASK) != CVT_NONE)
+ {
+ parseprintf(DD_PARSE, ("parse_ioread: time sample accumulated (status=0x%x)\n", updated));
+ }
+#endif
+
+ parseio->parse_dtime.parse_status = updated;
+
+ return (updated & CVT_MASK) != CVT_NONE;
+}
+
+/*
+ * parse_iopps
+ *
+ * take status line indication and derive synchronisation information
+ * from it.
+ * It can also be used to decode a serial serial data format (such as the
+ * ONE, ZERO, MINUTE sync data stream from DCF77)
+ */
+/*ARGSUSED*/
+int
+parse_iopps(parseio, status, ptime)
+ register parse_t *parseio;
+ register int status;
+ register timestamp_t *ptime;
+{
+ register unsigned updated = CVT_NONE;
+
+ /*
+ * PPS pulse information will only be delivered to ONE clock format
+ * this is either the last successful conversion module with a ppssync
+ * routine, or a fixed format with a ppssync routine
+ */
+ parseprintf(DD_PARSE, ("parse_iopps: STATUS %s\n", (status == SYNC_ONE) ? "ONE" : "ZERO"));
+
+ if (((parseio->parse_flags & PARSE_FIXED_FMT) ||
+ ((parseio->parse_dtime.parse_status & CVT_MASK) == CVT_OK)) &&
+ clockformats[parseio->parse_lformat]->syncpps &&
+ (status & clockformats[parseio->parse_lformat]->flags))
+ {
+ updated = clockformats[parseio->parse_lformat]->syncpps(parseio, status == SYNC_ONE, ptime);
+ parseprintf(DD_PARSE, ("parse_iopps: updated = 0x%x\n", updated));
+ }
+ else
+ {
+ parseprintf(DD_PARSE, ("parse_iopps: STATUS dropped\n"));
+ }
+
+ return (updated & CVT_MASK) != CVT_NONE;
+}
+
+/*
+ * parse_iodone
+ *
+ * clean up internal status for new round
+ */
+/*ARGSUSED*/
+void
+parse_iodone(parseio)
+ register parse_t *parseio;
+{
+ /*
+ * we need to clean up certain flags for the next round
+ */
+ parseprintf(DD_PARSE, ("parse_iodone: DONE\n"));
+ parseio->parse_dtime.parse_state = 0; /* no problems with ISRs */
+}
+
+/*---------- conversion implementation --------------------*/
+
+/*
+ * convert a struct clock to UTC since Jan, 1st 1970 0:00 (the UNIX EPOCH)
+ */
+#define dysize(x) (((x) % 4) ? 365 : \
+ (((x) % 100) ? 366 : \
+ (((x) % 400) ? 365 : 366)))
+
+time_t
+parse_to_unixtime(clock, cvtrtc)
+ register clocktime_t *clock;
+ register u_long *cvtrtc;
+{
+#define SETRTC(_X_) { if (cvtrtc) *cvtrtc = (_X_); }
+ static int days_of_month[] =
+ {
+ 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
+ };
+ register int i;
+ time_t t;
+
+ if (clock->utctime)
+ return clock->utctime; /* if the conversion routine gets it right away - why not */
+
+ if (clock->year < 100)
+ clock->year += 1900;
+
+ if (clock->year < 1970)
+ clock->year += 100; /* XXX this will do it till <2070 */
+
+ if (clock->year < 0)
+ {
+ SETRTC(CVT_FAIL|CVT_BADDATE);
+ return -1;
+ }
+
+ /*
+ * sorry, slow section here - but it's not time critical anyway
+ */
+ t = (clock->year - 1970) * 365;
+ t += (clock->year >> 2) - (1970 >> 2);
+ t -= clock->year / 100 - 1970 / 100;
+ t += clock->year / 400 - 1970 / 400;
+
+ /* month */
+ if (clock->month <= 0 || clock->month > 12)
+ {
+ SETRTC(CVT_FAIL|CVT_BADDATE);
+ return -1; /* bad month */
+ }
+ /* adjust leap year */
+ if (clock->month < 3 && dysize(clock->year) == 366)
+ t--;
+
+ for (i = 1; i < clock->month; i++)
+ {
+ t += days_of_month[i];
+ }
+ /* day */
+ if (clock->day < 1 || ((clock->month == 2 && dysize(clock->year) == 366) ?
+ clock->day > 29 : clock->day > days_of_month[clock->month]))
+ {
+ SETRTC(CVT_FAIL|CVT_BADDATE);
+ return -1; /* bad day */
+ }
+
+ t += clock->day - 1;
+ /* hour */
+ if (clock->hour < 0 || clock->hour >= 24)
+ {
+ SETRTC(CVT_FAIL|CVT_BADTIME);
+ return -1; /* bad hour */
+ }
+
+ t = TIMES24(t) + clock->hour;
+
+ /* min */
+ if (clock->minute < 0 || clock->minute > 59)
+ {
+ SETRTC(CVT_FAIL|CVT_BADTIME);
+ return -1; /* bad min */
+ }
+
+ t = TIMES60(t) + clock->minute;
+ /* sec */
+
+ if (clock->second < 0 || clock->second > 60) /* allow for LEAPs */
+ {
+ SETRTC(CVT_FAIL|CVT_BADTIME);
+ return -1; /* bad sec */
+ }
+
+ t = TIMES60(t) + clock->second;
+
+ t += clock->utcoffset; /* warp to UTC */
+
+ /* done */
+
+ clock->utctime = t; /* documentray only */
+
+ return t;
+}
+
+/*--------------- format conversion -----------------------------------*/
+
+int
+Stoi(s, zp, cnt)
+ char *s;
+ long *zp;
+ int cnt;
+{
+ char *b = s;
+ int f,z,v;
+ char c;
+
+ f=z=v=0;
+
+ while(*s == ' ')
+ s++;
+
+ if (*s == '-')
+ {
+ s++;
+ v = 1;
+ }
+ else
+ if (*s == '+')
+ s++;
+
+ for(;;)
+ {
+ c = *s++;
+ if (c == '\0' || c < '0' || c > '9' || (cnt && ((s-b) > cnt)))
+ {
+ if (f == 0)
+ {
+ return(-1);
+ }
+ if (v)
+ z = -z;
+ *zp = z;
+ return(0);
+ }
+ z = (z << 3) + (z << 1) + ( c - '0' );
+ f=1;
+ }
+}
+
+
+int
+Strok(s, m)
+ char *s;
+ char *m;
+{
+ if (!s || !m)
+ return 0;
+
+ while(*s && *m)
+ {
+ if ((*m == ' ') ? 1 : (*s == *m))
+ {
+ s++;
+ m++;
+ }
+ else
+ {
+ return 0;
+ }
+ }
+ return !*m;
+}
+
+u_long
+updatetimeinfo(parseio, t, usec, flags)
+ register parse_t *parseio;
+ register time_t t;
+ register u_long usec;
+ register u_long flags;
+{
+ register long usecoff;
+ register long mean;
+ long delta[PARSE_DELTA];
+
+#ifdef PARSEKERNEL
+ usecoff = (t - parseio->parse_dtime.parse_stime.tv.tv_sec) * 1000000
+ - parseio->parse_dtime.parse_stime.tv.tv_usec + usec;
+#else
+ extern long tstouslo[];
+ extern long tstousmid[];
+ extern long tstoushi[];
+
+ TSFTOTVU(parseio->parse_dtime.parse_stime.fp.l_uf, usecoff);
+ usecoff = -usecoff;
+ usecoff += (t - parseio->parse_dtime.parse_stime.fp.l_ui + JAN_1970) * 1000000
+ + usec;
+#endif
+
+ /*
+ * filtering (median) if requested
+ */
+ if (parseio->parse_flags & PARSE_STAT_FILTER)
+ {
+ register int n, i, s, k;
+
+ parseio->parse_delta[parseio->parse_dindex] = usecoff;
+
+ parseio->parse_dindex = (parseio->parse_dindex + 1) % PARSE_DELTA;
+
+ /*
+ * sort always - thus every sample gets its data
+ */
+ bcopy((caddr_t)parseio->parse_delta, (caddr_t)delta, sizeof(delta));
+
+ for (s = 0; s < PARSE_DELTA; s++)
+ for (k = s+1; k < PARSE_DELTA; k++)
+ { /* Yes - it's slow sort */
+ if (delta[s] > delta[k])
+ {
+ register long tmp;
+
+ tmp = delta[k];
+ delta[k] = delta[s];
+ delta[s] = tmp;
+ }
+ }
+
+ i = 0;
+ n = PARSE_DELTA;
+
+ /*
+ * you know this median loop if you have read the other code
+ */
+ while ((n - i) > 8)
+ {
+ register long top = delta[n-1];
+ register long mid = delta[(n+i)>>1];
+ register long low = delta[i];
+
+ if ((top - mid) > (mid - low))
+ {
+ /*
+ * cut off high end
+ */
+ n--;
+ }
+ else
+ {
+ /*
+ * cut off low end
+ */
+ i++;
+ }
+ }
+
+ parseio->parse_dtime.parse_usecdisp = delta[n-1] - delta[i];
+
+ if (parseio->parse_flags & PARSE_STAT_AVG)
+ {
+ /*
+ * take the average of the median samples as this clock
+ * is a little bumpy
+ */
+ mean = 0;
+
+ while (i < n)
+ {
+ mean += delta[i++];
+ }
+
+ mean >>= 3;
+ }
+ else
+ {
+ mean = delta[(n+i)>>1];
+ }
+
+ parseio->parse_dtime.parse_usecerror = mean;
+ }
+ else
+ {
+ parseio->parse_dtime.parse_usecerror = usecoff;
+ parseio->parse_dtime.parse_usecdisp = 0;
+ }
+
+ parseprintf(DD_PARSE,("parse: updatetimeinfo: T=%x+%d usec, useccoff=%d, usecerror=%d, usecdisp=%d\n",
+ (int)t, (int)usec, (int)usecoff, (int)parseio->parse_dtime.parse_usecerror,
+ (int)parseio->parse_dtime.parse_usecdisp));
+
+
+#ifdef PARSEKERNEL
+ {
+ int s = splhigh();
+#endif
+
+ parseio->parse_lstate = parseio->parse_dtime.parse_state | flags | PARSEB_TIMECODE;
+
+ parseio->parse_dtime.parse_state = parseio->parse_lstate;
+
+#ifdef PARSEKERNEL
+ (void)splx(s);
+ }
+#endif
+
+ return CVT_OK; /* everything fine and dandy... */
+}
+
+
+/*
+ * syn_simple
+ *
+ * handle a sync time stamp
+ */
+/*ARGSUSED*/
+void
+syn_simple(parseio, ts, format, why)
+ register parse_t *parseio;
+ register timestamp_t *ts;
+ register struct format *format;
+ register u_long why;
+{
+ parseio->parse_dtime.parse_stime = *ts;
+}
+
+/*
+ * pps_simple
+ *
+ * handle a pps time stamp
+ */
+/*ARGSUSED*/
+u_long
+pps_simple(parseio, status, ptime)
+ register parse_t *parseio;
+ register int status;
+ register timestamp_t *ptime;
+{
+ parseio->parse_dtime.parse_ptime = *ptime;
+ parseio->parse_dtime.parse_state |= PARSEB_PPS|PARSEB_S_PPS;
+
+ return CVT_NONE;
+}
+
+/*
+ * timepacket
+ *
+ * process a data packet
+ */
+static u_long
+timepacket(parseio)
+ register parse_t *parseio;
+{
+ register int k;
+ register unsigned short format;
+ register time_t t;
+ register u_long cvtsum = 0;/* accumulated CVT_FAIL errors */
+ u_long cvtrtc; /* current conversion result */
+ clocktime_t clock;
+
+ bzero(&clock, sizeof clock);
+ format = parseio->parse_lformat;
+
+ k = 0;
+
+ if (parseio->parse_flags & PARSE_FIXED_FMT)
+ {
+ switch ((cvtrtc = clockformats[format]->convert ? clockformats[format]->convert(parseio->parse_data, parseio->parse_index, clockformats[format]->data, &clock, parseio->parse_pdata) : CVT_NONE) & CVT_MASK)
+ {
+ case CVT_FAIL:
+ parseio->parse_badformat++;
+ cvtsum = cvtrtc & ~CVT_MASK;
+
+ /*
+ * may be too often ... but is nice to know when it happens
+ */
+#ifdef PARSEKERNEL
+ printf("parse: \"%s\" failed to convert\n", clockformats[format]->name);
+#else
+ syslog(LOG_WARNING, "parse: \"%s\" failed to convert\n", clockformats[format]->name);
+#endif
+ break;
+
+ case CVT_NONE:
+ /*
+ * too bad - pretend bad format
+ */
+ parseio->parse_badformat++;
+ cvtsum = CVT_BADFMT;
+ break;
+
+ case CVT_OK:
+ k = 1;
+ break;
+
+ case CVT_SKIP:
+ k = 2;
+ break;
+
+ default:
+ /* shouldn't happen */
+#ifdef PARSEKERNEL
+ printf("parse: INTERNAL error: bad return code of convert routine \"%s\"\n", clockformats[format]->name);
+#else
+ syslog(LOG_WARNING, "parse: INTERNAL error: bad return code of convert routine \"%s\"\n", clockformats[format]->name);
+#endif
+ return CVT_FAIL|cvtrtc;
+ }
+ }
+ else
+ {
+ /*
+ * find correct conversion routine
+ * and convert time packet
+ * RR search starting at last successful conversion routine
+ */
+
+ if (nformats) /* very careful ... */
+ {
+ do
+ {
+ switch ((cvtrtc = (clockformats[format]->convert && !(clockformats[format]->flags & CVT_FIXEDONLY)) ?
+ clockformats[format]->convert(parseio->parse_data, parseio->parse_index, clockformats[format]->data, &clock, parseio->parse_pdata) :
+ CVT_NONE) & CVT_MASK)
+ {
+ case CVT_FAIL:
+ parseio->parse_badformat++;
+ cvtsum |= cvtrtc & ~CVT_MASK;
+
+ /*
+ * may be too often ... but is nice to know when it happens
+ */
+#ifdef PARSEKERNEL
+ printf("parse: \"%s\" failed to convert\n", clockformats[format]->name);
+#else
+ syslog(LOG_WARNING, "parse: \"%s\" failed to convert\n", clockformats[format]->name);
+#endif
+ /*FALLTHROUGH*/
+ case CVT_NONE:
+ format++;
+ break;
+
+ case CVT_OK:
+ k = 1;
+ break;
+
+ default:
+ /* shouldn't happen */
+#ifdef PARSEKERNEL
+ printf("parse: INTERNAL error: bad return code of convert routine \"%s\"\n", clockformats[format]->name);
+#else
+ syslog(LOG_WARNING, "parse: INTERNAL error: bad return code of convert routine \"%s\"\n", clockformats[format]->name);
+#endif
+ return CVT_BADFMT;
+ }
+ if (format >= nformats)
+ format = 0;
+ }
+ while (!k && (format != parseio->parse_lformat));
+ }
+ }
+
+ if (!k)
+ {
+#ifdef PARSEKERNEL
+ printf("parse: time format \"%s\" not convertable\n", parseio->parse_data);
+#else
+ syslog(LOG_WARNING, "parse: time format \"%s\" not convertable\n", parseio->parse_data);
+#endif
+ return CVT_FAIL|cvtsum;
+ }
+
+ if (k == 2) return CVT_OK;
+
+ if ((t = parse_to_unixtime(&clock, &cvtrtc)) == -1)
+ {
+#ifdef PARSEKERNEL
+ printf("parse: bad time format \"%s\"\n", parseio->parse_data);
+#else
+ syslog(LOG_WARNING,"parse: bad time format \"%s\"\n", parseio->parse_data);
+#endif
+ return CVT_FAIL|cvtrtc;
+ }
+
+ parseio->parse_lformat = format;
+
+ /*
+ * time stamp
+ */
+#ifdef PARSEKERNEL
+ parseio->parse_dtime.parse_time.tv.tv_sec = t;
+ parseio->parse_dtime.parse_time.tv.tv_usec = clock.usecond;
+#else
+ parseio->parse_dtime.parse_time.fp.l_ui = t + JAN_1970;
+ TVUTOTSF(clock.usecond, parseio->parse_dtime.parse_time.fp.l_uf);
+#endif
+
+ parseio->parse_dtime.parse_format = format;
+
+ return updatetimeinfo(parseio, t, clock.usecond, clock.flags);
+}
+
+
+/*
+ * control operations
+ */
+/*ARGSUSED*/
+int
+parse_getstat(dct, parse)
+ parsectl_t *dct;
+ parse_t *parse;
+{
+ dct->parsestatus.flags = parse->parse_flags & PARSE_STAT_FLAGS;
+ return 1;
+}
+
+
+/*ARGSUSED*/
+int
+parse_setstat(dct, parse)
+ parsectl_t *dct;
+ parse_t *parse;
+{
+ parse->parse_flags = (parse->parse_flags & ~PARSE_STAT_FLAGS) | dct->parsestatus.flags;
+ return 1;
+}
+
+
+/*ARGSUSED*/
+int
+parse_timecode(dct, parse)
+ parsectl_t *dct;
+ parse_t *parse;
+{
+ dct->parsegettc.parse_state = parse->parse_lstate;
+ dct->parsegettc.parse_format = parse->parse_lformat;
+ /*
+ * move out current bad packet count
+ * user program is expected to sum these up
+ * this is not a problem, as "parse" module are
+ * exclusive open only
+ */
+ dct->parsegettc.parse_badformat = parse->parse_badformat;
+ parse->parse_badformat = 0;
+
+ if (parse->parse_ldsize <= PARSE_TCMAX)
+ {
+ dct->parsegettc.parse_count = parse->parse_ldsize;
+ bcopy(parse->parse_ldata, dct->parsegettc.parse_buffer, dct->parsegettc.parse_count);
+ return 1;
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+
+/*ARGSUSED*/
+int
+parse_setfmt(dct, parse)
+ parsectl_t *dct;
+ parse_t *parse;
+{
+ if (dct->parseformat.parse_count <= PARSE_TCMAX)
+ {
+ if (dct->parseformat.parse_count)
+ {
+ register unsigned short i;
+
+ for (i = 0; i < nformats; i++)
+ {
+ if (!Strcmp(dct->parseformat.parse_buffer, clockformats[i]->name))
+ {
+ parse->parse_lformat = i;
+ parse->parse_flags |= PARSE_FIXED_FMT; /* set fixed format indication */
+ return setup_bitmaps(parse, i, i+1);
+ }
+ }
+
+ return 0;
+ }
+ else
+ {
+ parse->parse_flags &= ~PARSE_FIXED_FMT; /* clear fixed format indication */
+ return setup_bitmaps(parse, 0, nformats);
+ }
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+/*ARGSUSED*/
+int
+parse_getfmt(dct, parse)
+ parsectl_t *dct;
+ parse_t *parse;
+{
+ if (dct->parseformat.parse_format < nformats &&
+ Strlen(clockformats[dct->parseformat.parse_format]->name) <= PARSE_TCMAX)
+ {
+ dct->parseformat.parse_count = Strlen(clockformats[dct->parseformat.parse_format]->name)+1;
+ bcopy(clockformats[dct->parseformat.parse_format]->name, dct->parseformat.parse_buffer, dct->parseformat.parse_count);
+ return 1;
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+/*ARGSUSED*/
+int
+parse_setcs(dct, parse)
+ parsectl_t *dct;
+ parse_t *parse;
+{
+ parse->parse_ioflags &= ~PARSE_IO_CSIZE;
+ parse->parse_ioflags |= dct->parsesetcs.parse_cs & PARSE_IO_CSIZE;
+ return 1;
+}
+
+#endif /* defined(REFCLOCK) && defined(PARSE) */
+
+/*
+ * History:
+ *
+ * parse.c,v
+ * Revision 3.27 1994/06/01 08:18:33 kardel
+ * more debug info
+ *
+ * Revision 3.26 1994/05/30 10:20:07 kardel
+ * LONG cleanup
+ *
+ * Revision 3.25 1994/05/12 12:49:12 kardel
+ * printf fmt/arg cleanup
+ *
+ * Revision 3.24 1994/03/27 15:01:36 kardel
+ * reorder include file to cope with PTX
+ *
+ * Revision 3.23 1994/03/25 13:09:02 kardel
+ * considering FIXEDONLY entries only in FIXEDONLY mode
+ *
+ * Revision 3.22 1994/02/25 12:34:49 kardel
+ * allow for converter generated utc times
+ *
+ * Revision 3.21 1994/02/02 17:45:30 kardel
+ * rcs ids fixed
+ *
+ * Revision 3.19 1994/01/25 19:05:20 kardel
+ * 94/01/23 reconcilation
+ *
+ * Revision 3.18 1994/01/23 17:21:59 kardel
+ * 1994 reconcilation
+ *
+ * Revision 3.17 1993/11/11 11:20:29 kardel
+ * declaration fixes
+ *
+ * Revision 3.16 1993/11/06 22:26:07 duwe
+ * Linux cleanup after config change
+ *
+ * Revision 3.15 1993/11/04 11:14:18 kardel
+ * ansi/K&R traps
+ *
+ * Revision 3.14 1993/11/04 10:03:28 kardel
+ * disarmed ansiism
+ *
+ * Revision 3.13 1993/11/01 20:14:13 kardel
+ * useless comparision removed
+ *
+ * Revision 3.12 1993/11/01 20:00:22 kardel
+ * parse Solaris support (initial version)
+ *
+ * Revision 3.11 1993/10/30 09:41:25 kardel
+ * minor optimizations
+ *
+ * Revision 3.10 1993/10/22 14:27:51 kardel
+ * Oct. 22nd 1993 reconcilation
+ *
+ * Revision 3.9 1993/10/05 23:15:09 kardel
+ * more STREAM protection
+ *
+ * Revision 3.8 1993/09/27 21:08:00 kardel
+ * utcoffset now in seconds
+ *
+ * Revision 3.7 1993/09/26 23:40:16 kardel
+ * new parse driver logic
+ *
+ * Revision 3.6 1993/09/07 10:12:46 kardel
+ * September 7th reconcilation - 3.2 (alpha)
+ *
+ * Revision 3.5 1993/09/01 21:44:48 kardel
+ * conditional cleanup
+ *
+ * Revision 3.4 1993/08/27 00:29:39 kardel
+ * compilation cleanup
+ *
+ * Revision 3.3 1993/08/24 22:27:13 kardel
+ * cleaned up AUTOCONF DCF77 mess 8-) - wasn't too bad
+ *
+ * Revision 3.2 1993/07/09 11:37:11 kardel
+ * Initial restructured version + GPS support
+ *
+ * Revision 3.1 1993/07/06 10:00:08 kardel
+ * DCF77 driver goes generic...
+ *
+ */
diff --git a/usr.sbin/xntpd/parse/parse_conf.c b/usr.sbin/xntpd/parse/parse_conf.c
new file mode 100644
index 0000000..cc931e3
--- /dev/null
+++ b/usr.sbin/xntpd/parse/parse_conf.c
@@ -0,0 +1,133 @@
+#if defined(REFCLOCK) && (defined(PARSE) || defined(PARSEPPS))
+/*
+ * /src/NTP/REPOSITORY/v3/parse/parse_conf.c,v 3.15 1994/02/02 17:45:32 kardel Exp
+ *
+ * parse_conf.c,v 3.15 1994/02/02 17:45:32 kardel Exp
+ *
+ * Parser configuration module for reference clocks
+ *
+ * STREAM define switches between two personalities of the module
+ * if STREAM is defined this module can be used with dcf77sync.c as
+ * a STREAMS kernel module. In this case the time stamps will be
+ * a struct timeval.
+ * when STREAM is not defined NTP time stamps will be used.
+ *
+ * Copyright (c) 1992,1993,1994
+ * Frank Kardel Friedrich-Alexander Universitaet Erlangen-Nuernberg
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ */
+
+#include "sys/types.h"
+#include "sys/time.h"
+#include "sys/errno.h"
+#include "ntp_fp.h"
+#include "ntp_unixtime.h"
+#include "ntp_calendar.h"
+
+#include "parse.h"
+
+#ifdef CLOCK_SCHMID
+extern clockformat_t clock_schmid;
+#endif
+
+#ifdef CLOCK_DCF7000
+extern clockformat_t clock_dcf7000;
+#endif
+
+#ifdef CLOCK_MEINBERG
+extern clockformat_t clock_meinberg[];
+#endif
+
+#ifdef CLOCK_RAWDCF
+extern clockformat_t clock_rawdcf;
+#endif
+
+#ifdef CLOCK_TRIMTAIP
+extern clockformat_t clock_trimtaip;
+#endif
+
+#ifdef CLOCK_TRIMTSIP
+extern clockformat_t clock_trimtsip;
+#endif
+
+/*
+ * format definitions
+ */
+clockformat_t *clockformats[] =
+{
+#ifdef CLOCK_MEINBERG
+ &clock_meinberg[0],
+ &clock_meinberg[1],
+ &clock_meinberg[2],
+#endif
+#ifdef CLOCK_DCF7000
+ &clock_dcf7000,
+#endif
+#ifdef CLOCK_SCHMID
+ &clock_schmid,
+#endif
+#ifdef CLOCK_RAWDCF
+ &clock_rawdcf,
+#endif
+#ifdef CLOCK_TRIMTAIP
+ &clock_trimtaip,
+#endif
+#ifdef CLOCK_TRIMTSIP
+ &clock_trimtsip,
+#endif
+0};
+
+unsigned short nformats = sizeof(clockformats) / sizeof(clockformats[0]) - 1;
+#endif /* REFCLOCK PARSE */
+
+/*
+ * History:
+ *
+ * parse_conf.c,v
+ * Revision 3.15 1994/02/02 17:45:32 kardel
+ * rcs ids fixed
+ *
+ * Revision 3.13 1994/01/25 19:05:23 kardel
+ * 94/01/23 reconcilation
+ *
+ * Revision 3.12 1994/01/23 17:22:02 kardel
+ * 1994 reconcilation
+ *
+ * Revision 3.11 1993/11/01 20:00:24 kardel
+ * parse Solaris support (initial version)
+ *
+ * Revision 3.10 1993/10/09 15:01:37 kardel
+ * file structure unified
+ *
+ * Revision 3.9 1993/09/26 23:40:19 kardel
+ * new parse driver logic
+ *
+ * Revision 3.8 1993/09/02 23:20:57 kardel
+ * dragon extiction
+ *
+ * Revision 3.7 1993/09/01 21:44:52 kardel
+ * conditional cleanup
+ *
+ * Revision 3.6 1993/09/01 11:25:09 kardel
+ * patch accident 8-(
+ *
+ * Revision 3.5 1993/08/31 22:31:14 kardel
+ * SINIX-M SysVR4 integration
+ *
+ * Revision 3.4 1993/08/27 00:29:42 kardel
+ * compilation cleanup
+ *
+ * Revision 3.3 1993/07/14 09:04:45 kardel
+ * only when REFCLOCK && PARSE is defined
+ *
+ * Revision 3.2 1993/07/09 11:37:13 kardel
+ * Initial restructured version + GPS support
+ *
+ * Revision 3.1 1993/07/06 10:00:11 kardel
+ * DCF77 driver goes generic...
+ *
+ */
diff --git a/usr.sbin/xntpd/parse/parsesolaris.c b/usr.sbin/xntpd/parse/parsesolaris.c
new file mode 100644
index 0000000..a67f0bf
--- /dev/null
+++ b/usr.sbin/xntpd/parse/parsesolaris.c
@@ -0,0 +1,1249 @@
+/*
+ * /src/NTP/REPOSITORY/v3/parse/parsesolaris.c,v 3.16 1994/05/30 09:57:40 kardel Exp
+ *
+ * parsesolaris.c,v 3.16 1994/05/30 09:57:40 kardel Exp
+ *
+ * STREAMS module for reference clocks
+ * (SunOS5.x - not fully tested - buyer beware ! - OS KILLERS may still be
+ * lurking in the code!)
+ *
+ * Copyright (c) 1993,1994
+ * derived work from parsestreams.c ((c) 1991-1993, Frank Kardel) and
+ * dcf77sync.c((c) Frank Kardel)
+ * Frank Kardel, Friedrich-Alexander Universitaet Erlangen-Nuernberg
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ */
+
+#ifndef lint
+static char rcsid[] = "parsesolaris.c,v 3.16 1994/05/30 09:57:40 kardel Exp";
+#endif
+
+/*
+ * Well, the man spec says we have to do this junk - the
+ * header files tell a different story (i like that one more)
+ */
+#define SAFE_WR(q) (((q)->q_flag & QREADR) ? WR((q)) : (q))
+#define SAFE_RD(q) (((q)->q_flag & QREADR) ? (q) : RD((q)))
+
+/*
+ * needed to cope with Solaris 2.3 header file chaos
+ */
+#include <sys/types.h>
+/*
+ * the Solaris 2.2 include list
+ */
+#include <sys/conf.h>
+#include <sys/errno.h>
+#include <sys/time.h>
+#include <sys/termios.h>
+#include <sys/stream.h>
+#include <sys/strtty.h>
+#include <sys/stropts.h>
+#include <sys/modctl.h>
+#include <sys/ddi.h>
+#include <sys/sunddi.h>
+#include <sys/cpu.h>
+
+#define STREAM /* that's what we are here for */
+
+#define HAVE_NO_NICE /* for the NTP headerfiles */
+#include "ntp_fp.h"
+#include "parse.h"
+#include "sys/parsestreams.h"
+
+static unsigned int parsebusy = 0;
+
+/*--------------- loadable driver section -----------------------------*/
+
+static struct streamtab parseinfo;
+
+static struct fmodsw fmod_templ =
+{
+ "parse", /* module name */
+ &parseinfo, /* module information */
+ D_NEW|D_MP|D_MTQPAIR, /* exclusive for q pair */
+ /* lock ptr */
+};
+
+extern struct mod_ops mod_strmodops;
+
+static struct modlstrmod modlstrmod =
+{
+ &mod_strmodops, /* a STREAMS module */
+ "PARSE - NTP reference", /* name this baby - keep room for revision number */
+ &fmod_templ
+};
+
+static struct modlinkage modlinkage =
+{
+ MODREV_1,
+ &modlstrmod,
+ NULL
+};
+
+/*
+ * strings support usually not in kernel
+ */
+static int Strlen(s)
+ register char *s;
+{
+ register int c;
+
+ c = 0;
+ if (s)
+ {
+ while (*s++)
+ {
+ c++;
+ }
+ }
+ return c;
+}
+
+static void Strncpy(t, s, c)
+ register char *t;
+ register char *s;
+ register int c;
+{
+ if (s && t)
+ {
+ while ((c-- > 0) && (*t++ = *s++))
+ ;
+ }
+}
+
+int Strcmp(s, t)
+ register char *s;
+ register char *t;
+{
+ register int c = 0;
+
+ if (!s || !t || (s == t))
+ {
+ return 0;
+ }
+
+ while (!(c = *s++ - *t++) && *s && *t)
+ /* empty loop */;
+
+ return c;
+}
+
+/*
+ * module management routines
+ */
+/*ARGSUSED*/
+int _init(void)
+{
+ static char revision[] = "3.16";
+ char *s, *S, *t;
+
+ /*
+ * copy RCS revision into Drv_name
+ *
+ * are we forcing RCS here to do things it was not built for ?
+ */
+ s = revision;
+ if (*s == '$')
+ {
+ /*
+ * skip "$Revision: "
+ * if present. - not necessary on a -kv co (cvs export)
+ */
+ while (*s && (*s != ' '))
+ {
+ s++;
+ }
+ if (*s == ' ') s++;
+ }
+
+ t = modlstrmod.strmod_linkinfo;
+ while (*t && (*t != ' '))
+ {
+ t++;
+ }
+ if (*t == ' ') t++;
+
+ S = s;
+ while (*S && (((*S >= '0') && (*S <= '9')) || (*S == '.')))
+ {
+ S++;
+ }
+
+ if (*s && *t && (S > s))
+ {
+ if (Strlen(t) >= (S - s))
+ {
+ (void) Strncpy(t, s, S - s);
+ }
+ }
+ return (mod_install(&modlinkage));
+}
+
+/*ARGSUSED*/
+int _info(struct modinfo *modinfop)
+{
+ return (mod_info(&modlinkage, modinfop));
+}
+
+/*ARGSUSED*/
+int _fini(void)
+{
+ if (parsebusy > 0)
+ {
+ printf("_fini[%s]: STREAMS module has still %d instances active.\n", modlstrmod.strmod_linkinfo, parsebusy);
+ return (EBUSY);
+ }
+ else
+ return (mod_remove(&modlinkage));
+}
+
+/*--------------- stream module definition ----------------------------*/
+
+static int parseopen(), parseclose(), parsewput(), parserput(), parsersvc();
+
+static struct module_info driverinfo =
+{
+ 0, /* module ID number */
+ fmod_templ.f_name, /* module name - why repeated here ? compat ?*/
+ 0, /* minimum accepted packet size */
+ INFPSZ, /* maximum accepted packet size */
+ 1, /* high water mark - flow control */
+ 0 /* low water mark - flow control */
+};
+
+static struct qinit rinit = /* read queue definition */
+{
+ parserput, /* put procedure */
+ parsersvc, /* service procedure */
+ parseopen, /* open procedure */
+ parseclose, /* close procedure */
+ NULL, /* admin procedure - NOT USED FOR NOW */
+ &driverinfo, /* information structure */
+ NULL /* statistics */
+};
+
+static struct qinit winit = /* write queue definition */
+{
+ parsewput, /* put procedure */
+ NULL, /* service procedure */
+ NULL, /* open procedure */
+ NULL, /* close procedure */
+ NULL, /* admin procedure - NOT USED FOR NOW */
+ &driverinfo, /* information structure */
+ NULL /* statistics */
+};
+
+static struct streamtab parseinfo = /* stream info element for parse driver */
+{
+ &rinit, /* read queue */
+ &winit, /* write queue */
+ NULL, /* read mux */
+ NULL /* write mux */
+};
+
+/*--------------- driver data structures ----------------------------*/
+
+/*
+ * we usually have an inverted signal - but you
+ * can change this to suit your needs
+ */
+int cd_invert = 1; /* invert status of CD line - PPS support via CD input */
+
+int parsedebug = ~0;
+
+extern void uniqtime();
+
+/*--------------- module implementation -----------------------------*/
+
+#define TIMEVAL_USADD(_X_, _US_) do {\
+ (_X_)->tv_usec += (_US_);\
+ if ((_X_)->tv_usec >= 1000000)\
+ {\
+ (_X_)->tv_sec++;\
+ (_X_)->tv_usec -= 1000000;\
+ }\
+ } while (0)
+
+#if defined(sun4c) && defined(DEBUG_CD)
+#include <sun4c/cpu.h>
+#include <sun4c/auxio.h>
+#define SET_LED(_X_) (((cpu & CPU_ARCH) == SUN4C_ARCH) ? *(u_char *)AUXIO_REG = AUX_MBO|AUX_EJECT|((_X_)?AUX_LED:0) : 0)
+#else
+#define SET_LED(_X_)
+#endif
+
+static int init_linemon();
+static void close_linemon();
+
+/*
+ * keep here MACHINE AND OS AND ENVIRONMENT DEPENDENT
+ * timing constants
+ *
+ * FOR ABSOLUTE PRECISION YOU NEED TO MEASURE THE TIMING
+ * SKEW BETWEEN THE HW-PPS SIGNAL AND KERNEL uniqtime()
+ * YOURSELF.
+ *
+ * YOU MUST BE QUALIFIED APPROPRIATELY FOR THESE TYPE
+ * OF HW MANIPULATION !
+ *
+ * you need an oscilloscope and the permission for HW work
+ * in order to figure out these timing constants/variables
+ */
+
+static unsigned long xsdelay = 10; /* assume an SS2 */
+static unsigned long stdelay = 350;
+
+struct delays
+{
+ unsigned char mask; /* what to check for */
+ unsigned char type; /* what to match */
+ unsigned long xsdelay; /* external status direct delay in us */
+ unsigned long stdelay; /* STREAMS message delay (M_[UN]HANGUP) */
+} isr_delays[] =
+{
+ /*
+ * WARNING: must still be measured - currently taken from Craig Leres ppsdev
+ */
+#ifdef sun4c
+ {CPU_ARCH|CPU_MACH, CPU_SUN4C_50, 10, 350},
+ {CPU_ARCH|CPU_MACH, CPU_SUN4C_65, 15, 700},
+ {CPU_ARCH|CPU_MACH, CPU_SUN4C_75, 10, 350},
+#endif
+#ifdef sun4m
+ {CPU_ARCH|CPU_MACH, CPU_SUN4M_50, 8, 250},
+ {CPU_ARCH|CPU_MACH, CPU_SUN4M_690, 8, 250},
+#endif
+ {0,}
+};
+
+void setup_delays()
+{
+ register int i;
+
+ if (cputype & OBP_ARCH)
+ {
+ printf("parse: WARNING: PPS kernel fudge factors no yet determinable (no dev tree walk yet) - assuming SS2 (Sun4/75)\n", cputype);
+ return;
+ }
+
+ for (i = 0; isr_delays[i].mask; i++)
+ {
+ if ((cputype & isr_delays[i].mask) == isr_delays[i].type)
+ {
+ xsdelay = isr_delays[i].xsdelay;
+ stdelay = isr_delays[i].stdelay;
+ return;
+ }
+ }
+ printf("parse: WARNING: PPS kernel fudge factors unknown for this machine (Type 0x%x) - assuming SS2 (Sun4/75)\n", cputype);
+}
+
+#define M_PARSE 0x0001
+#define M_NOPARSE 0x0002
+
+static int
+setup_stream(queue_t *q, int mode)
+{
+ register mblk_t *mp;
+
+ parseprintf(DD_OPEN,("parse: SETUP_STREAM - setting up stream for q=%x\n", q));
+
+ mp = allocb(sizeof(struct stroptions), BPRI_MED);
+ if (mp)
+ {
+ struct stroptions *str = (struct stroptions *)mp->b_wptr;
+
+ str->so_flags = SO_READOPT|SO_HIWAT|SO_LOWAT;
+ str->so_readopt = (mode == M_PARSE) ? RMSGD : RNORM;
+ str->so_hiwat = (mode == M_PARSE) ? sizeof(parsetime_t) : 256;
+ str->so_lowat = 0;
+ mp->b_datap->db_type = M_SETOPTS;
+ mp->b_wptr += sizeof(struct stroptions);
+ if (!q)
+ panic("NULL q - strange");
+ putnext(q, mp);
+ return putctl1(SAFE_WR(q)->q_next, M_CTL, (mode == M_PARSE) ? MC_SERVICEIMM :
+ MC_SERVICEDEF);
+ }
+ else
+ {
+ parseprintf(DD_OPEN,("parse: setup_stream - FAILED - no MEMORY for allocb\n"));
+ return 0;
+ }
+}
+
+/*ARGSUSED*/
+static int parseopen(queue_t *q, dev_t *dev, int flag, int sflag, cred_t *credp)
+{
+ register mblk_t *mp;
+ register parsestream_t *parse;
+ static int notice = 0;
+
+ parseprintf(DD_OPEN,("parse: OPEN - q=%x\n", q));
+
+ if (sflag != MODOPEN)
+ { /* open only for modules */
+ parseprintf(DD_OPEN,("parse: OPEN - FAILED - not MODOPEN\n"));
+ return EIO;
+ }
+
+ if (q->q_ptr != (caddr_t)NULL)
+ {
+ parseprintf(DD_OPEN,("parse: OPEN - FAILED - EXCLUSIVE ONLY\n"));
+ return EBUSY;
+ }
+
+ parsebusy++;
+
+ q->q_ptr = (caddr_t)kmem_alloc(sizeof(parsestream_t), KM_SLEEP);
+ if (q->q_ptr == (caddr_t)0)
+ {
+ return ENOMEM;
+ }
+
+ parseprintf(DD_OPEN,("parse: OPEN - parse area q=%x, q->q_ptr=%x\n", q, q->q_ptr));
+ SAFE_WR(q)->q_ptr = q->q_ptr;
+ parseprintf(DD_OPEN,("parse: OPEN - WQ parse area q=%x, q->q_ptr=%x\n", SAFE_WR(q), SAFE_WR(q)->q_ptr));
+
+ parse = (parsestream_t *) q->q_ptr;
+ bzero((caddr_t)parse, sizeof(*parse));
+ parse->parse_queue = q;
+ parse->parse_status = PARSE_ENABLE;
+ parse->parse_ppsclockev.tv.tv_sec = 0;
+ parse->parse_ppsclockev.tv.tv_usec = 0;
+ parse->parse_ppsclockev.serial = 0;
+
+ qprocson(q);
+
+ parseprintf(DD_OPEN,("parse: OPEN - initializing io subsystem q=%x\n", q));
+
+ if (!parse_ioinit(&parse->parse_io))
+ {
+ /*
+ * ok guys - beat it
+ */
+ qprocsoff(q);
+
+ kmem_free((caddr_t)parse, sizeof(parsestream_t));
+
+ parsebusy--;
+
+ return EIO;
+ }
+
+ parseprintf(DD_OPEN,("parse: OPEN - initializing stream q=%x\n", q));
+
+ if (setup_stream(q, M_PARSE))
+ {
+ (void) init_linemon(q); /* hook up PPS ISR routines if possible */
+ setup_delays();
+ parseprintf(DD_OPEN,("parse: OPEN - SUCCEEDED\n"));
+
+ /*
+ * I know that you know the delete key, but you didn't write this
+ * code, did you ? - So, keep the message in here.
+ */
+ if (!notice)
+ {
+ printf("%s: Copyright (c) 1991-1994, Frank Kardel\n", modlstrmod.strmod_linkinfo);
+ notice = 1;
+ }
+
+ return 0;
+ }
+ else
+ {
+ qprocsoff(q);
+
+ kmem_free((caddr_t)parse, sizeof(parsestream_t));
+
+ parsebusy--;
+
+ return EIO;
+ }
+}
+
+/*ARGSUSED*/
+static int parseclose(queue_t *q, int flags)
+{
+ register parsestream_t *parse = (parsestream_t *)q->q_ptr;
+ register unsigned long s;
+
+ parseprintf(DD_CLOSE,("parse: CLOSE\n"));
+
+ qprocsoff(q);
+
+ s = splhigh();
+
+ if (parse->parse_dqueue)
+ close_linemon(parse->parse_dqueue, q);
+ parse->parse_dqueue = (queue_t *)0;
+
+ (void) splx(s);
+
+ parse_ioend(&parse->parse_io);
+
+ kmem_free((caddr_t)parse, sizeof(parsestream_t));
+
+ q->q_ptr = (caddr_t)NULL;
+ SAFE_WR(q)->q_ptr = (caddr_t)NULL;
+
+ parsebusy--;
+}
+
+/*
+ * move unrecognized stuff upward
+ */
+static parsersvc(queue_t *q)
+{
+ mblk_t *mp;
+
+ while (mp = getq(q))
+ {
+ if (canput(q->q_next) || (mp->b_datap->db_type > QPCTL))
+ {
+ putnext(q, mp);
+ parseprintf(DD_RSVC,("parse: RSVC - putnext\n"));
+ }
+ else
+ {
+ putbq(q, mp);
+ parseprintf(DD_RSVC,("parse: RSVC - flow control wait\n"));
+ break;
+ }
+ }
+}
+
+/*
+ * do ioctls and
+ * send stuff down - dont care about
+ * flow control
+ */
+static int parsewput(queue_t *q, mblk_t *mp)
+{
+ register int ok = 1;
+ register mblk_t *datap;
+ register struct iocblk *iocp;
+ parsestream_t *parse = (parsestream_t *)q->q_ptr;
+
+ parseprintf(DD_WPUT,("parse: parsewput\n"));
+
+ switch (mp->b_datap->db_type)
+ {
+ default:
+ putnext(q, mp);
+ break;
+
+ case M_IOCTL:
+ iocp = (struct iocblk *)mp->b_rptr;
+ switch (iocp->ioc_cmd)
+ {
+ default:
+ parseprintf(DD_WPUT,("parse: parsewput - forward M_IOCTL\n"));
+ putnext(q, mp);
+ break;
+
+ case CIOGETEV:
+ /*
+ * taken from Craig Leres ppsclock module (and modified)
+ */
+ datap = allocb(sizeof(struct ppsclockev), BPRI_MED);
+ if (datap == NULL || mp->b_cont)
+ {
+ mp->b_datap->db_type = M_IOCNAK;
+ iocp->ioc_error = (datap == NULL) ? ENOMEM : EINVAL;
+ if (datap != NULL)
+ freeb(datap);
+ qreply(q, mp);
+ break;
+ }
+
+ mp->b_cont = datap;
+ *(struct ppsclockev *)datap->b_wptr = parse->parse_ppsclockev;
+ datap->b_wptr +=
+ sizeof(struct ppsclockev) / sizeof(*datap->b_wptr);
+ mp->b_datap->db_type = M_IOCACK;
+ iocp->ioc_count = sizeof(struct ppsclockev);
+ qreply(q, mp);
+ break;
+
+ case PARSEIOC_ENABLE:
+ case PARSEIOC_DISABLE:
+ {
+ parse->parse_status = (parse->parse_status & ~PARSE_ENABLE) |
+ (iocp->ioc_cmd == PARSEIOC_ENABLE) ?
+ PARSE_ENABLE : 0;
+ if (!setup_stream(SAFE_RD(q), (parse->parse_status & PARSE_ENABLE) ?
+ M_PARSE : M_NOPARSE))
+ {
+ mp->b_datap->db_type = M_IOCNAK;
+ }
+ else
+ {
+ mp->b_datap->db_type = M_IOCACK;
+ }
+ qreply(q, mp);
+ break;
+ }
+
+ case PARSEIOC_SETSTAT:
+ case PARSEIOC_GETSTAT:
+ case PARSEIOC_TIMECODE:
+ case PARSEIOC_SETFMT:
+ case PARSEIOC_GETFMT:
+ case PARSEIOC_SETCS:
+ if (iocp->ioc_count == sizeof(parsectl_t))
+ {
+ parsectl_t *dct = (parsectl_t *)mp->b_cont->b_rptr;
+
+ switch (iocp->ioc_cmd)
+ {
+ case PARSEIOC_GETSTAT:
+ parseprintf(DD_WPUT,("parse: parsewput - PARSEIOC_GETSTAT\n"));
+ ok = parse_getstat(dct, &parse->parse_io);
+ break;
+
+ case PARSEIOC_SETSTAT:
+ parseprintf(DD_WPUT,("parse: parsewput - PARSEIOC_SETSTAT\n"));
+ ok = parse_setstat(dct, &parse->parse_io);
+ break;
+
+ case PARSEIOC_TIMECODE:
+ parseprintf(DD_WPUT,("parse: parsewput - PARSEIOC_TIMECODE\n"));
+ ok = parse_timecode(dct, &parse->parse_io);
+ break;
+
+ case PARSEIOC_SETFMT:
+ parseprintf(DD_WPUT,("parse: parsewput - PARSEIOC_SETFMT\n"));
+ ok = parse_setfmt(dct, &parse->parse_io);
+ break;
+
+ case PARSEIOC_GETFMT:
+ parseprintf(DD_WPUT,("parse: parsewput - PARSEIOC_GETFMT\n"));
+ ok = parse_getfmt(dct, &parse->parse_io);
+ break;
+
+ case PARSEIOC_SETCS:
+ parseprintf(DD_WPUT,("parse: parsewput - PARSEIOC_SETCS\n"));
+ ok = parse_setcs(dct, &parse->parse_io);
+ break;
+ }
+ mp->b_datap->db_type = ok ? M_IOCACK : M_IOCNAK;
+ }
+ else
+ {
+ mp->b_datap->db_type = M_IOCNAK;
+ }
+ parseprintf(DD_WPUT,("parse: parsewput qreply - %s\n", (mp->b_datap->db_type == M_IOCNAK) ? "M_IOCNAK" : "M_IOCACK"));
+ qreply(q, mp);
+ break;
+ }
+ }
+}
+
+/*
+ * read characters from streams buffers
+ */
+static unsigned long rdchar(mblk_t **mp)
+{
+ while (*mp != (mblk_t *)NULL)
+ {
+ if ((*mp)->b_wptr - (*mp)->b_rptr)
+ {
+ return (unsigned long)(*(unsigned char *)((*mp)->b_rptr++));
+ }
+ else
+ {
+ register mblk_t *mmp = *mp;
+
+ *mp = (*mp)->b_cont;
+ freeb(mmp);
+ }
+ }
+ return ~0;
+}
+
+/*
+ * convert incoming data
+ */
+static int parserput(queue_t *q, mblk_t *imp)
+{
+ register unsigned char type;
+ mblk_t *mp = imp;
+
+ switch (type = mp->b_datap->db_type)
+ {
+ default:
+ /*
+ * anything we don't know will be put on queue
+ * the service routine will move it to the next one
+ */
+ parseprintf(DD_RPUT,("parse: parserput - forward type 0x%x\n", type));
+
+ if (canput(q->q_next) || (mp->b_datap->db_type > QPCTL))
+ {
+ putnext(q, mp);
+ }
+ else
+ putq(q, mp);
+ break;
+
+ case M_BREAK:
+ case M_DATA:
+ {
+ register parsestream_t * parse = (parsestream_t *)q->q_ptr;
+ register mblk_t *nmp;
+ register unsigned long ch;
+ timestamp_t ctime;
+
+ /*
+ * get time on packet delivery
+ */
+ uniqtime(&ctime.tv);
+
+ if (!(parse->parse_status & PARSE_ENABLE))
+ {
+ parseprintf(DD_RPUT,("parse: parserput - parser disabled - forward type 0x%x\n", type));
+ if (canput(q->q_next) || (mp->b_datap->db_type > QPCTL))
+ {
+ putnext(q, mp);
+ }
+ else
+ putq(q, mp);
+ }
+ else
+ {
+#if 0
+ parseprintf(DD_RPUT,("parse: parserput - M_%s\n", (type == M_DATA) ? "DATA" : "BREAK"));
+#endif
+ if (type == M_DATA)
+ {
+ /*
+ * parse packet looking for start an end characters
+ */
+ while (mp != (mblk_t *)NULL)
+ {
+ ch = rdchar(&mp);
+ if (ch != ~0 && parse_ioread(&parse->parse_io, (char)ch, &ctime))
+ {
+ /*
+ * up up and away (hopefully ...)
+ * don't press it if resources are tight or nobody wants it
+ */
+ nmp = (mblk_t *)NULL;
+ if (canput(parse->parse_queue->q_next) && (nmp = allocb(sizeof(parsetime_t), BPRI_MED)))
+ {
+ bcopy((caddr_t)&parse->parse_io.parse_dtime, (caddr_t)nmp->b_rptr, sizeof(parsetime_t));
+ nmp->b_wptr += sizeof(parsetime_t);
+ putnext(parse->parse_queue, nmp);
+ }
+ else
+ if (nmp) freemsg(nmp);
+ parse_iodone(&parse->parse_io);
+ }
+ }
+ }
+ else
+ {
+ if (parse_ioread(&parse->parse_io, (char)0, &ctime))
+ {
+ /*
+ * up up and away (hopefully ...)
+ * don't press it if resources are tight or nobody wants it
+ */
+ nmp = (mblk_t *)NULL;
+ if (canput(parse->parse_queue->q_next) && (nmp = allocb(sizeof(parsetime_t), BPRI_MED)))
+ {
+ bcopy((caddr_t)&parse->parse_io.parse_dtime, (caddr_t)nmp->b_rptr, sizeof(parsetime_t));
+ nmp->b_wptr += sizeof(parsetime_t);
+ putnext(parse->parse_queue, nmp);
+ }
+ else
+ if (nmp) freemsg(nmp);
+ parse_iodone(&parse->parse_io);
+ }
+ freemsg(mp);
+ }
+ break;
+ }
+ }
+
+ /*
+ * CD PPS support for non direct ISR hack
+ */
+ case M_HANGUP:
+ case M_UNHANGUP:
+ {
+ register parsestream_t * parse = (parsestream_t *)q->q_ptr;
+ timestamp_t ctime;
+ register mblk_t *nmp;
+ register int status = cd_invert ^ (type == M_HANGUP);
+
+ SET_LED(status);
+
+ uniqtime(&ctime.tv);
+
+ TIMEVAL_USADD(&ctime.tv, stdelay);
+
+ parseprintf(DD_RPUT,("parse: parserput - M_%sHANGUP\n", (type == M_HANGUP) ? "" : "UN"));
+
+ if ((parse->parse_status & PARSE_ENABLE) &&
+ parse_iopps(&parse->parse_io, status ? SYNC_ONE : SYNC_ZERO, &ctime))
+ {
+ nmp = (mblk_t *)NULL;
+ if (canput(parse->parse_queue->q_next) && (nmp = allocb(sizeof(parsetime_t), BPRI_MED)))
+ {
+ bcopy((caddr_t)&parse->parse_io.parse_dtime, (caddr_t)nmp->b_rptr, sizeof(parsetime_t));
+ nmp->b_wptr += sizeof(parsetime_t);
+ putnext(parse->parse_queue, nmp);
+ }
+ else
+ if (nmp) freemsg(nmp);
+ parse_iodone(&parse->parse_io);
+ freemsg(mp);
+ }
+ else
+ if (canput(q->q_next) || (mp->b_datap->db_type > QPCTL))
+ {
+ putnext(q, mp);
+ }
+ else
+ putq(q, mp);
+
+ if (status)
+ {
+ parse->parse_ppsclockev.tv = ctime.tv;
+ ++(parse->parse_ppsclockev.serial);
+ }
+ }
+ }
+}
+
+static int init_zs_linemon(); /* handle line monitor for "zs" driver */
+static void close_zs_linemon();
+static void zs_xsisr(); /* zs external status interupt handler */
+
+/*-------------------- CD isr status monitor ---------------*/
+
+static int init_linemon(queue_t *q)
+{
+ register queue_t *dq;
+
+ dq = SAFE_WR(q);
+ /*
+ * we ARE doing very bad things down here (basically stealing ISR
+ * hooks)
+ *
+ * so we chase down the STREAMS stack searching for the driver
+ * and if this is a known driver we insert our ISR routine for
+ * status changes in to the ExternalStatus handling hook
+ */
+ while (dq->q_next)
+ {
+ dq = dq->q_next; /* skip down to driver */
+ }
+
+ /*
+ * find appropriate driver dependent routine
+ */
+ if (dq->q_qinfo && dq->q_qinfo->qi_minfo)
+ {
+ register char *dname = dq->q_qinfo->qi_minfo->mi_idname;
+
+ parseprintf(DD_INSTALL, ("init_linemon: driver is \"%s\"\n", dname));
+
+#ifdef sun
+ if (dname && !Strcmp(dname, "zs"))
+ {
+ return init_zs_linemon(dq, q);
+ }
+ else
+#endif
+ {
+ parseprintf(DD_INSTALL, ("init_linemon: driver \"%s\" not suitable for CD monitoring\n", dname));
+ return 0;
+ }
+ }
+ parseprintf(DD_INSTALL, ("init_linemon: cannot find driver\n"));
+ return 0;
+}
+
+static void close_linemon(queue_t *q, queue_t *my_q)
+{
+ /*
+ * find appropriate driver dependent routine
+ */
+ if (q->q_qinfo && q->q_qinfo->qi_minfo)
+ {
+ register char *dname = q->q_qinfo->qi_minfo->mi_idname;
+
+#ifdef sun
+ if (dname && !Strcmp(dname, "zs"))
+ {
+ close_zs_linemon(q, my_q);
+ return;
+ }
+ parseprintf(DD_INSTALL, ("close_linemon: cannot find driver close routine for \"%s\"\n", dname));
+#endif
+ }
+ parseprintf(DD_INSTALL, ("close_linemon: cannot find driver name\n"));
+}
+
+#ifdef sun
+#include <sys/tty.h>
+#include <sys/zsdev.h>
+#include <sys/ser_async.h>
+#include <sys/ser_zscc.h>
+
+/*
+ * there should be some docs telling how to get to
+ * sz:zs_usec_delay and zs:initzsops()
+ */
+#define zs_usec_delay 5
+
+struct savedzsops
+{
+ struct zsops zsops;
+ struct zsops *oldzsops;
+};
+
+static struct zsops *emergencyzs;
+
+static int init_zs_linemon(queue_t *q, queue_t *my_q)
+{
+ register struct zscom *zs;
+ register struct savedzsops *szs;
+ register parsestream_t *parsestream = (parsestream_t *)my_q->q_ptr;
+ /*
+ * we expect the zsaline pointer in the q_data pointer
+ * from there on we insert our on EXTERNAL/STATUS ISR routine
+ * into the interrupt path, before the standard handler
+ */
+ zs = ((struct asyncline *)q->q_ptr)->za_common;
+ if (!zs)
+ {
+ /*
+ * well - not found on startup - just say no (shouldn't happen though)
+ */
+ return 0;
+ }
+ else
+ {
+ unsigned long s;
+
+ /*
+ * we do a direct replacement, in case others fiddle also
+ * if somebody else grabs our hook and we disconnect
+ * we are in DEEP trouble - panic is likely to be next, sorry
+ */
+ szs = (struct savedzsops *) kmem_alloc(sizeof(struct savedzsops), KM_SLEEP);
+
+ if (szs == (struct savedzsops *)0)
+ {
+ parseprintf(DD_INSTALL, ("init_zs_linemon: CD monitor NOT installed - no memory\n"));
+
+ return 0;
+ }
+ else
+ {
+ parsestream->parse_data = (void *)szs;
+
+ mutex_enter(zs->zs_excl);
+
+ parsestream->parse_dqueue = q; /* remember driver */
+
+ szs->zsops = *zs->zs_ops;
+ szs->zsops.zsop_xsint = (void (*)())zs_xsisr; /* place our bastard */
+ szs->oldzsops = zs->zs_ops;
+ emergencyzs = zs->zs_ops;
+
+ zs->zs_ops = &szs->zsops; /* hook it up */
+ /*
+ * XXX: this is usually done via zsopinit()
+ * - have yet to find a way to call that routine
+ */
+ zs->zs_xsint = (void (*)())zs_xsisr;
+
+ mutex_exit(zs->zs_excl);
+
+ parseprintf(DD_INSTALL, ("init_zs_linemon: CD monitor installed\n"));
+
+ return 1;
+ }
+ }
+}
+
+/*
+ * unregister our ISR routine - must call under splhigh()
+ */
+static void close_zs_linemon(queue_t *q, queue_t *my_q)
+{
+ register struct zscom *zs;
+ register parsestream_t *parsestream = (parsestream_t *)my_q->q_ptr;
+
+ zs = ((struct asyncline *)q->q_ptr)->za_common;
+ if (!zs)
+ {
+ /*
+ * well - not found on startup - just say no (shouldn't happen though)
+ */
+ return;
+ }
+ else
+ {
+ register struct savedzsops *szs = (struct savedzsops *)parsestream->parse_data;
+
+ mutex_enter(zs->zs_excl);
+
+ zs->zs_ops = szs->oldzsops; /* reset to previous handler functions */
+ /*
+ * XXX: revert xsint (usually done via zsopinit() - have still to find
+ * a way to call that bugger
+ */
+ zs->zs_xsint = zs->zs_ops->zsop_xsint;
+
+ mutex_exit(zs->zs_excl);
+
+ kmem_free((caddr_t)szs, sizeof (struct savedzsops));
+
+ parseprintf(DD_INSTALL, ("close_zs_linemon: CD monitor deleted\n"));
+ return;
+ }
+}
+
+#define ZSRR0_IGNORE (ZSRR0_CD|ZSRR0_SYNC|ZSRR0_CTS)
+
+#define MAXDEPTH 50 /* maximum allowed stream crawl */
+
+/*
+ * take external status interrupt (only CD interests us)
+ */
+static void zs_xsisr(struct zscom *zs)
+{
+ register struct asyncline *za = (struct asyncline *)zs->zs_priv;
+ register queue_t *q;
+ register unsigned char zsstatus;
+ register int loopcheck;
+ register unsigned char cdstate;
+ register char *dname;
+
+ /*
+ * pick up current state
+ */
+ zsstatus = SCC_READ0();
+
+ if (za->za_rr0 ^ (cdstate = zsstatus & ZSRR0_CD))
+ {
+ timestamp_t cdevent;
+ register int status;
+
+ /*
+ * CONDITIONAL external measurement support
+ */
+ SET_LED(cdstate); /*
+ * inconsistent with upper SET_LED, but this
+ * is for oscilloscope business anyway and we
+ * are just interested in edge delays in the
+ * lower us range
+ */
+
+ /*
+ * time stamp
+ */
+ uniqtime(&cdevent.tv);
+
+ TIMEVAL_USADD(&cdevent.tv, xsdelay);
+
+ q = za->za_ttycommon.t_readq;
+
+ /*
+ * logical state
+ */
+ status = cd_invert ? cdstate == 0 : cdstate != 0;
+
+ /*
+ * ok - now the hard part - find ourself
+ */
+ loopcheck = MAXDEPTH;
+
+ while (q)
+ {
+ if (q->q_qinfo && q->q_qinfo->qi_minfo)
+ {
+ dname = q->q_qinfo->qi_minfo->mi_idname;
+
+ if (!Strcmp(dname, parseinfo.st_rdinit->qi_minfo->mi_idname))
+ {
+ /*
+ * back home - phew (hopping along stream queues might
+ * prove dangerous to your health)
+ */
+
+ if ((((parsestream_t *)q->q_ptr)->parse_status & PARSE_ENABLE) &&
+ parse_iopps(&((parsestream_t *)q->q_ptr)->parse_io, status ? SYNC_ONE : SYNC_ZERO, &cdevent))
+ {
+ /*
+ * XXX - currently we do not pass up the message, as
+ * we should.
+ * for a correct behaviour wee need to block out
+ * processing until parse_iodone has been posted via
+ * a softcall-ed routine which does the message pass-up
+ * right now PPS information relies on input being
+ * received
+ */
+ parse_iodone(&((parsestream_t *)q->q_ptr)->parse_io);
+ }
+
+ if (status)
+ {
+ ((parsestream_t *)q->q_ptr)->parse_ppsclockev.tv = cdevent.tv;
+ ++(((parsestream_t *)q->q_ptr)->parse_ppsclockev.serial);
+ }
+
+ parseprintf(DD_ISR, ("zs_xsisr: CD event %s has been posted for \"%s\"\n", status ? "ONE" : "ZERO", dname));
+ break;
+ }
+ }
+
+ q = q->q_next;
+
+ if (!loopcheck--)
+ {
+ panic("zs_xsisr: STREAMS Queue corrupted - CD event");
+ }
+ }
+
+ /*
+ * only pretend that CD and ignored transistion (SYNC,CTS)
+ * have been handled
+ */
+ za->za_rr0 = (za->za_rr0 & ~ZSRR0_IGNORE) | (zsstatus & ZSRR0_IGNORE);
+
+ if (((za->za_rr0 ^ zsstatus) & ~ZSRR0_IGNORE) == 0)
+ {
+ /*
+ * all done - kill status indication and return
+ */
+ SCC_WRITE0(ZSWR0_RESET_STATUS); /* might kill other conditions here */
+ return;
+ }
+ }
+
+ parseprintf(DD_ISR, ("zs_xsisr: non CD event 0x%x for \"%s\"\n",
+ (za->za_rr0 ^ zsstatus) & ~ZSRR0_CD,dname));
+ /*
+ * we are now gathered here to process some unusual external status
+ * interrupts.
+ * any CD events have also been handled and shouldn't be processed
+ * by the original routine (unless we have a VERY busy port pin)
+ * some initializations are done here, which could have been done before for
+ * both code paths but have been avioded for minimum path length to
+ * the uniq_time routine
+ */
+ dname = (char *) 0;
+ q = za->za_ttycommon.t_readq;
+
+ loopcheck = MAXDEPTH;
+
+ /*
+ * the real thing for everything else ...
+ */
+ while (q)
+ {
+ if (q->q_qinfo && q->q_qinfo->qi_minfo)
+ {
+ dname = q->q_qinfo->qi_minfo->mi_idname;
+ if (!Strcmp(dname, parseinfo.st_rdinit->qi_minfo->mi_idname))
+ {
+ register void (*zsisr)();
+
+ /*
+ * back home - phew (hopping along stream queues might
+ * prove dangerous to your health)
+ */
+ if (zsisr = ((struct savedzsops *)((parsestream_t *)q->q_ptr)->parse_data)->oldzsops->zsop_xsint)
+ zsisr(zs);
+ else
+ panic("zs_xsisr: unable to locate original ISR");
+
+ parseprintf(DD_ISR, ("zs_xsisr: non CD event was processed for \"%s\"\n", dname));
+ /*
+ * now back to our program ...
+ */
+ return;
+ }
+ }
+
+ q = q->q_next;
+
+ if (!loopcheck--)
+ {
+ panic("zs_xsisr: STREAMS Queue corrupted - non CD event");
+ }
+ }
+
+ /*
+ * last resort - shouldn't even come here as it indicates
+ * corrupted TTY structures
+ */
+ printf("zs_zsisr: looking for \"%s\" - found \"%s\" - taking EMERGENCY path\n", parseinfo.st_rdinit->qi_minfo->mi_idname, dname ? dname : "-NIL-");
+
+ if (emergencyzs && emergencyzs->zsop_xsint)
+ emergencyzs->zsop_xsint(zs);
+ else
+ panic("zs_xsisr: no emergency ISR handler");
+}
+#endif /* sun */
+
+/*
+ * History:
+ *
+ * parsesolaris.c,v
+ * Revision 3.16 1994/05/30 09:57:40 kardel
+ * kmem_alloc checking
+ *
+ * Revision 3.15 1994/02/15 22:20:51 kardel
+ * rcsid fixed
+ *
+ * Revision 3.14 1994/02/15 22:06:04 kardel
+ * added qprocsx & flags for MT capability
+ *
+ * Revision 3.13 1994/02/13 19:16:47 kardel
+ * updated verbose Copyright message
+ *
+ * Revision 3.12 1994/02/02 17:45:35 kardel
+ * rcs ids fixed
+ *
+ * Revision 3.9 1994/01/25 19:05:26 kardel
+ * 94/01/23 reconcilation
+ *
+ * Revision 3.8 1994/01/23 17:22:04 kardel
+ * 1994 reconcilation
+ *
+ * Revision 3.7 1993/12/15 18:24:41 kardel
+ * Now also ignoring state changes on ZSRR0_{SYNC,CTS} to avoid zs driver bugs (Solaris 2.3)
+ *
+ * Revision 3.6 1993/12/15 12:48:53 kardel
+ * fixed message loss on M_*HANHUP messages
+ *
+ * Revision 3.5 1993/12/14 21:05:12 kardel
+ * PPS working now for SunOS 5.x zs external status hook
+ *
+ * Revision 3.4 1993/11/13 11:13:17 kardel
+ * Solaris 2.3 additional includes
+ *
+ * Revision 3.3 1993/11/11 11:20:33 kardel
+ * declaration fixes
+ *
+ * Revision 3.2 1993/11/05 15:40:25 kardel
+ * shut up nice feature detection
+ *
+ * Revision 3.1 1993/11/01 20:00:29 kardel
+ * parse Solaris support (initial version)
+ *
+ */
diff --git a/usr.sbin/xntpd/parse/parsestreams.c b/usr.sbin/xntpd/parse/parsestreams.c
new file mode 100644
index 0000000..990f2b3
--- /dev/null
+++ b/usr.sbin/xntpd/parse/parsestreams.c
@@ -0,0 +1,1363 @@
+/*
+ * /src/NTP/REPOSITORY/v3/parse/parsestreams.c,v 3.22 1994/06/01 10:41:16 kardel Exp
+ *
+ * parsestreams.c,v 3.22 1994/06/01 10:41:16 kardel Exp
+ *
+ * STREAMS module for reference clocks
+ * (SunOS4.x)
+ *
+ * Copyright (c) 1989,1990,1991,1992,1993,1994
+ * Frank Kardel Friedrich-Alexander Universitaet Erlangen-Nuernberg
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ */
+
+#ifndef lint
+static char rcsid[] = "parsestreams.c,v 3.22 1994/06/01 10:41:16 kardel Exp";
+#endif
+
+#include "sys/types.h"
+#include "sys/conf.h"
+#include "sys/buf.h"
+#include "sys/param.h"
+#include "sys/sysmacros.h"
+#include "sys/errno.h"
+#include "sys/time.h"
+#include "sundev/mbvar.h"
+#include "sun/autoconf.h"
+#include "sys/stream.h"
+#include "sys/stropts.h"
+#include "sys/dir.h"
+#include "sys/signal.h"
+#include "sys/termios.h"
+#include "sys/termio.h"
+#include "sys/ttold.h"
+#include "sys/user.h"
+#include "sys/errno.h"
+#include "sys/tty.h"
+#include "machine/cpu.h"
+
+#ifdef VDDRV
+#include "sun/vddrv.h"
+#endif
+
+/*
+ * no protypes here !
+ */
+#define P(x) ()
+
+/*
+ * use microtime instead of uniqtime if advised to
+ */
+#ifdef MICROTIME
+#define uniqtime microtime
+#endif
+
+#define HAVE_NO_NICE /* for the NTP headerfiles */
+#include "ntp_fp.h"
+#include "parse.h"
+#include "sys/parsestreams.h"
+
+#ifdef VDDRV
+static unsigned int parsebusy = 0;
+
+/*--------------- loadable driver section -----------------------------*/
+
+extern struct streamtab parseinfo;
+
+struct vdldrv parsesync_vd =
+{
+ VDMAGIC_PSEUDO, /* nothing like a real driver - a STREAMS module */
+ "PARSE ", /* name this baby - keep room for revision number */
+};
+
+/*
+ * strings support usually not in kernel
+ */
+static int strlen(s)
+ register char *s;
+{
+ register int c;
+
+ c = 0;
+ if (s)
+ {
+ while (*s++)
+ {
+ c++;
+ }
+ }
+ return c;
+}
+
+static void strncpy(t, s, c)
+ register char *t;
+ register char *s;
+ register int c;
+{
+ if (s && t)
+ {
+ while ((c-- > 0) && (*t++ = *s++))
+ ;
+ }
+}
+
+static int strcmp(s, t)
+ register char *s;
+ register char *t;
+{
+ register int c = 0;
+
+ if (!s || !t || (s == t))
+ {
+ return 0;
+ }
+
+ while (!(c = *s++ - *t++) && *s && *t)
+ /* empty loop */;
+
+ return c;
+}
+
+static int strncmp(s, t, n)
+ register char *s;
+ register char *t;
+ register int n;
+{
+ register int c = 0;
+
+ if (!s || !t || (s == t))
+ {
+ return 0;
+ }
+
+ while (n-- && !(c = *s++ - *t++) && *s && *t)
+ /* empty loop */;
+
+ return c;
+}
+
+/*
+ * driver init routine
+ * since no mechanism gets us into and out of the fmodsw, we have to
+ * do it ourselves
+ */
+/*ARGSUSED*/
+int xxxinit(fc, vdp, vdi, vds)
+ unsigned int fc;
+ struct vddrv *vdp;
+ addr_t vdi;
+ struct vdstat *vds;
+{
+ extern struct fmodsw fmodsw[];
+ extern int fmodcnt;
+
+ struct fmodsw *fm = fmodsw;
+ struct fmodsw *fmend = &fmodsw[fmodcnt];
+ struct fmodsw *ifm = (struct fmodsw *)0;
+ char *mname = parseinfo.st_rdinit->qi_minfo->mi_idname;
+
+ switch (fc)
+ {
+ case VDLOAD:
+ vdp->vdd_vdtab = (struct vdlinkage *)&parsesync_vd;
+ /*
+ * now, jog along fmodsw scanning for an empty slot
+ * and deposit our name there
+ */
+ while (fm <= fmend)
+ {
+ if (!strncmp(fm->f_name, mname, FMNAMESZ))
+ {
+ printf("vddrinit[%s]: STREAMS module already loaded.\n", mname);
+ return(EBUSY);
+ }
+ else
+ if ((ifm == (struct fmodsw *)0) &&
+ (fm->f_name[0] == '\0') && (fm->f_str == (struct streamtab *)0))
+ {
+ /*
+ * got one - so move in
+ */
+ ifm = fm;
+ break;
+ }
+ fm++;
+ }
+
+ if (ifm == (struct fmodsw *)0)
+ {
+ printf("vddrinit[%s]: no slot free for STREAMS module\n", mname);
+ return (ENOSPC);
+ }
+ else
+ {
+ static char revision[] = "3.22";
+ char *s, *S, *t;
+
+ strncpy(ifm->f_name, mname, FMNAMESZ);
+ ifm->f_name[FMNAMESZ] = '\0';
+ ifm->f_str = &parseinfo;
+ /*
+ * copy RCS revision into Drv_name
+ *
+ * are we forcing RCS here to do things it was not built for ?
+ */
+ s = revision;
+ if (*s == '$')
+ {
+ /*
+ * skip "$Revision: "
+ * if present. - not necessary on a -kv co (cvs export)
+ */
+ while (*s && (*s != ' '))
+ {
+ s++;
+ }
+ if (*s == ' ') s++;
+ }
+
+ t = parsesync_vd.Drv_name;
+ while (*t && (*t != ' '))
+ {
+ t++;
+ }
+ if (*t == ' ') t++;
+
+ S = s;
+ while (*S && (((*S >= '0') && (*S <= '9')) || (*S == '.')))
+ {
+ S++;
+ }
+
+ if (*s && *t && (S > s))
+ {
+ if (strlen(t) >= (S - s))
+ {
+ (void) strncpy(t, s, S - s);
+ }
+ }
+ return (0);
+ }
+ break;
+
+ case VDUNLOAD:
+ if (parsebusy > 0)
+ {
+ printf("vddrinit[%s]: STREAMS module has still %d instances active.\n", mname, parsebusy);
+ return (EBUSY);
+ }
+ else
+ {
+ while (fm <= fmend)
+ {
+ if (!strncmp(fm->f_name, mname, FMNAMESZ))
+ {
+ /*
+ * got it - kill entry
+ */
+ fm->f_name[0] = '\0';
+ fm->f_str = (struct streamtab *)0;
+ fm++;
+
+ break;
+ }
+ fm++;
+ }
+ if (fm > fmend)
+ {
+ printf("vddrinit[%s]: cannot find entry for STREAMS module\n", mname);
+ return (ENXIO);
+ }
+ else
+ return (0);
+ }
+
+
+ case VDSTAT:
+ return (0);
+
+ default:
+ return (EIO);
+
+ }
+ return EIO;
+}
+
+#endif
+
+/*--------------- stream module definition ----------------------------*/
+
+static int parseopen(), parseclose(), parsewput(), parserput(), parsersvc();
+
+static struct module_info driverinfo =
+{
+ 0, /* module ID number */
+ "parse", /* module name */
+ 0, /* minimum accepted packet size */
+ INFPSZ, /* maximum accepted packet size */
+ 1, /* high water mark - flow control */
+ 0 /* low water mark - flow control */
+};
+
+static struct qinit rinit = /* read queue definition */
+{
+ parserput, /* put procedure */
+ parsersvc, /* service procedure */
+ parseopen, /* open procedure */
+ parseclose, /* close procedure */
+ NULL, /* admin procedure - NOT USED FOR NOW */
+ &driverinfo, /* information structure */
+ NULL /* statistics */
+};
+
+static struct qinit winit = /* write queue definition */
+{
+ parsewput, /* put procedure */
+ NULL, /* service procedure */
+ NULL, /* open procedure */
+ NULL, /* close procedure */
+ NULL, /* admin procedure - NOT USED FOR NOW */
+ &driverinfo, /* information structure */
+ NULL /* statistics */
+};
+
+struct streamtab parseinfo = /* stream info element for dpr driver */
+{
+ &rinit, /* read queue */
+ &winit, /* write queue */
+ NULL, /* read mux */
+ NULL, /* write mux */
+ NULL /* module auto push */
+};
+
+/*--------------- driver data structures ----------------------------*/
+
+/*
+ * we usually have an inverted signal - but you
+ * can change this to suit your needs
+ */
+int cd_invert = 1; /* invert status of CD line - PPS support via CD input */
+
+int parsedebug = ~0;
+
+extern void uniqtime();
+
+/*--------------- module implementation -----------------------------*/
+
+#define TIMEVAL_USADD(_X_, _US_) {\
+ (_X_)->tv_usec += (_US_);\
+ if ((_X_)->tv_usec >= 1000000)\
+ {\
+ (_X_)->tv_sec++;\
+ (_X_)->tv_usec -= 1000000;\
+ }\
+ } while (0)
+
+#if defined(sun4c) && defined(DEBUG_CD)
+#include <sun4c/cpu.h>
+#include <sun4c/auxio.h>
+#define SET_LED(_X_) (((cpu & CPU_ARCH) == SUN4C_ARCH) ? *(u_char *)AUXIO_REG = AUX_MBO|AUX_EJECT|((_X_)?AUX_LED:0) : 0)
+#else
+#define SET_LED(_X_)
+#endif
+
+static int init_linemon();
+static void close_linemon();
+
+/*
+ * keep here MACHINE AND OS AND ENVIRONMENT DEPENDENT
+ * timing constants
+ *
+ * FOR ABSOLUTE PRECISION YOU NEED TO MEASURE THE TIMING
+ * SKEW BETWEEN THE HW-PPS SIGNAL AND KERNEL uniqtime()
+ * YOURSELF.
+ *
+ * YOU MUST BE QUALIFIED APPROPRIATELY FOR THESE TYPE
+ * OF HW MANIPULATION !
+ *
+ * you need an oscilloscope and the permission for HW work
+ * in order to figure out these timing constants/variables
+ */
+#ifdef sun
+static unsigned long xsdelay = 10; /* assume an SS2 */
+static unsigned long stdelay = 350;
+
+struct delays
+{
+ unsigned char mask; /* what to check for */
+ unsigned char type; /* what to match */
+ unsigned long xsdelay; /* external status direct delay in us */
+ unsigned long stdelay; /* STREAMS message delay (M_[UN]HANGUP) */
+} isr_delays[] =
+{
+ /*
+ * WARNING: must still be measured - currently taken from Craig Leres ppsdev
+ */
+#ifdef sun4c
+ {CPU_ARCH|CPU_MACH, CPU_SUN4C_50, 10, 350},
+ {CPU_ARCH|CPU_MACH, CPU_SUN4C_65, 15, 700},
+ {CPU_ARCH|CPU_MACH, CPU_SUN4C_75, 10, 350},
+#endif
+#ifdef sun4m
+ {CPU_ARCH|CPU_MACH, CPU_SUN4M_50, 8, 250},
+ {CPU_ARCH|CPU_MACH, CPU_SUN4M_690, 8, 250},
+#endif
+ {0,}
+};
+
+void setup_delays()
+{
+ register int i;
+
+ for (i = 0; isr_delays[i].mask; i++)
+ {
+ if ((cpu & isr_delays[i].mask) == isr_delays[i].type)
+ {
+ xsdelay = isr_delays[i].xsdelay;
+ stdelay = isr_delays[i].stdelay;
+ return;
+ }
+ }
+ printf("parse: WARNING: PPS kernel fudge factors unknown for this machine (Type 0x%x) - assuming SS2 (Sun4/75)\n", cpu);
+}
+#else
+#define setup_delays() /* empty - no need for clobbering kernel with this */
+static unsigned long xsdelay = 0; /* assume nothing */
+static unsigned long stdelay = 0;
+#endif
+
+#define M_PARSE 0x0001
+#define M_NOPARSE 0x0002
+
+static int
+setup_stream(q, mode)
+ queue_t *q;
+ int mode;
+{
+ mblk_t *mp;
+
+ mp = allocb(sizeof(struct stroptions), BPRI_MED);
+ if (mp)
+ {
+ struct stroptions *str = (struct stroptions *)mp->b_rptr;
+
+ str->so_flags = SO_READOPT|SO_HIWAT|SO_LOWAT;
+ str->so_readopt = (mode == M_PARSE) ? RMSGD : RNORM;
+ str->so_hiwat = (mode == M_PARSE) ? sizeof(parsetime_t) : 256;
+ str->so_lowat = 0;
+ mp->b_datap->db_type = M_SETOPTS;
+ mp->b_wptr += sizeof(struct stroptions);
+ putnext(q, mp);
+ return putctl1(WR(q)->q_next, M_CTL, (mode == M_PARSE) ? MC_SERVICEIMM :
+ MC_SERVICEDEF);
+ }
+ else
+ {
+ parseprintf(DD_OPEN,("parse: setup_stream - FAILED - no MEMORY for allocb\n"));
+ return 0;
+ }
+}
+
+/*ARGSUSED*/
+static int parseopen(q, dev, flag, sflag)
+ queue_t *q;
+ dev_t dev;
+ int flag;
+ int sflag;
+{
+ register mblk_t *mp;
+ register parsestream_t *parse;
+ static int notice = 0;
+
+ parseprintf(DD_OPEN,("parse: OPEN\n"));
+
+ if (sflag != MODOPEN)
+ { /* open only for modules */
+ parseprintf(DD_OPEN,("parse: OPEN - FAILED - not MODOPEN\n"));
+ return OPENFAIL;
+ }
+
+ if (q->q_ptr != (caddr_t)NULL)
+ {
+ u.u_error = EBUSY;
+ parseprintf(DD_OPEN,("parse: OPEN - FAILED - EXCLUSIVE ONLY\n"));
+ return OPENFAIL;
+ }
+
+#ifdef VDDRV
+ parsebusy++;
+#endif
+
+ q->q_ptr = (caddr_t)kmem_alloc(sizeof(parsestream_t));
+ if (q->q_ptr == (caddr_t)0)
+ {
+ parseprintf(DD_OPEN,("parse: OPEN - FAILED - no memory\n"));
+ return OPENFAIL;
+ }
+ WR(q)->q_ptr = q->q_ptr;
+
+ parse = (parsestream_t *) q->q_ptr;
+ bzero((caddr_t)parse, sizeof(*parse));
+ parse->parse_queue = q;
+ parse->parse_status = PARSE_ENABLE;
+ parse->parse_ppsclockev.tv.tv_sec = 0;
+ parse->parse_ppsclockev.tv.tv_usec = 0;
+ parse->parse_ppsclockev.serial = 0;
+
+ if (!parse_ioinit(&parse->parse_io))
+ {
+ /*
+ * ok guys - beat it
+ */
+ kmem_free((caddr_t)parse, sizeof(parsestream_t));
+#ifdef VDDRV
+ parsebusy--;
+#endif
+ return OPENFAIL;
+ }
+
+ if (setup_stream(q, M_PARSE))
+ {
+ (void) init_linemon(q); /* hook up PPS ISR routines if possible */
+ setup_delays();
+ parseprintf(DD_OPEN,("parse: OPEN - SUCCEEDED\n"));
+
+ /*
+ * I know that you know the delete key, but you didn't write this
+ * code, did you ? - So, keep the message in here.
+ */
+ if (!notice)
+ {
+ printf("%s: Copyright (c) 1991-1994, Frank Kardel\n", parsesync_vd.Drv_name);
+ notice = 1;
+ }
+
+ return 1;
+ }
+ else
+ {
+ kmem_free((caddr_t)parse, sizeof(parsestream_t));
+
+#ifdef VDDRV
+ parsebusy--;
+#endif
+ return OPENFAIL;
+ }
+}
+
+/*ARGSUSED*/
+static int parseclose(q, flags)
+ queue_t *q;
+ int flags;
+{
+ register parsestream_t *parse = (parsestream_t *)q->q_ptr;
+ register unsigned long s;
+
+ parseprintf(DD_CLOSE,("parse: CLOSE\n"));
+
+ s = splhigh();
+
+ if (parse->parse_dqueue)
+ close_linemon(parse->parse_dqueue, q);
+ parse->parse_dqueue = (queue_t *)0;
+
+ (void) splx(s);
+
+ parse_ioend(&parse->parse_io);
+
+ kmem_free((caddr_t)parse, sizeof(parsestream_t));
+
+ q->q_ptr = (caddr_t)NULL;
+ WR(q)->q_ptr = (caddr_t)NULL;
+
+#ifdef VDDRV
+ parsebusy--;
+#endif
+}
+
+/*
+ * move unrecognized stuff upward
+ */
+static parsersvc(q)
+ queue_t *q;
+{
+ mblk_t *mp;
+
+ while (mp = getq(q))
+ {
+ if (canput(q->q_next) || (mp->b_datap->db_type > QPCTL))
+ {
+ putnext(q, mp);
+ parseprintf(DD_RSVC,("parse: RSVC - putnext\n"));
+ }
+ else
+ {
+ putbq(q, mp);
+ parseprintf(DD_RSVC,("parse: RSVC - flow control wait\n"));
+ break;
+ }
+ }
+}
+
+/*
+ * do ioctls and
+ * send stuff down - dont care about
+ * flow control
+ */
+static int parsewput(q, mp)
+ queue_t *q;
+ register mblk_t *mp;
+{
+ register int ok = 1;
+ register mblk_t *datap;
+ register struct iocblk *iocp;
+ parsestream_t *parse = (parsestream_t *)q->q_ptr;
+
+ parseprintf(DD_WPUT,("parse: parsewput\n"));
+
+ switch (mp->b_datap->db_type)
+ {
+ default:
+ putnext(q, mp);
+ break;
+
+ case M_IOCTL:
+ iocp = (struct iocblk *)mp->b_rptr;
+ switch (iocp->ioc_cmd)
+ {
+ default:
+ parseprintf(DD_WPUT,("parse: parsewput - forward M_IOCTL\n"));
+ putnext(q, mp);
+ break;
+
+ case CIOGETEV:
+ /*
+ * taken from Craig Leres ppsclock module (and modified)
+ */
+ datap = allocb(sizeof(struct ppsclockev), BPRI_MED);
+ if (datap == NULL || mp->b_cont)
+ {
+ mp->b_datap->db_type = M_IOCNAK;
+ iocp->ioc_error = (datap == NULL) ? ENOMEM : EINVAL;
+ if (datap != NULL)
+ freeb(datap);
+ qreply(q, mp);
+ break;
+ }
+
+ mp->b_cont = datap;
+ *(struct ppsclockev *)datap->b_wptr = parse->parse_ppsclockev;
+ datap->b_wptr +=
+ sizeof(struct ppsclockev) / sizeof(*datap->b_wptr);
+ mp->b_datap->db_type = M_IOCACK;
+ iocp->ioc_count = sizeof(struct ppsclockev);
+ qreply(q, mp);
+ break;
+
+ case PARSEIOC_ENABLE:
+ case PARSEIOC_DISABLE:
+ {
+ parse->parse_status = (parse->parse_status & ~PARSE_ENABLE) |
+ (iocp->ioc_cmd == PARSEIOC_ENABLE) ?
+ PARSE_ENABLE : 0;
+ if (!setup_stream(RD(q), (parse->parse_status & PARSE_ENABLE) ?
+ M_PARSE : M_NOPARSE))
+ {
+ mp->b_datap->db_type = M_IOCNAK;
+ }
+ else
+ {
+ mp->b_datap->db_type = M_IOCACK;
+ }
+ qreply(q, mp);
+ break;
+ }
+
+ case PARSEIOC_SETSTAT:
+ case PARSEIOC_GETSTAT:
+ case PARSEIOC_TIMECODE:
+ case PARSEIOC_SETFMT:
+ case PARSEIOC_GETFMT:
+ case PARSEIOC_SETCS:
+ if (iocp->ioc_count == sizeof(parsectl_t))
+ {
+ parsectl_t *dct = (parsectl_t *)mp->b_cont->b_rptr;
+
+ switch (iocp->ioc_cmd)
+ {
+ case PARSEIOC_GETSTAT:
+ parseprintf(DD_WPUT,("parse: parsewput - PARSEIOC_GETSTAT\n"));
+ ok = parse_getstat(dct, &parse->parse_io);
+ break;
+
+ case PARSEIOC_SETSTAT:
+ parseprintf(DD_WPUT,("parse: parsewput - PARSEIOC_SETSTAT\n"));
+ ok = parse_setstat(dct, &parse->parse_io);
+ break;
+
+ case PARSEIOC_TIMECODE:
+ parseprintf(DD_WPUT,("parse: parsewput - PARSEIOC_TIMECODE\n"));
+ ok = parse_timecode(dct, &parse->parse_io);
+ break;
+
+ case PARSEIOC_SETFMT:
+ parseprintf(DD_WPUT,("parse: parsewput - PARSEIOC_SETFMT\n"));
+ ok = parse_setfmt(dct, &parse->parse_io);
+ break;
+
+ case PARSEIOC_GETFMT:
+ parseprintf(DD_WPUT,("parse: parsewput - PARSEIOC_GETFMT\n"));
+ ok = parse_getfmt(dct, &parse->parse_io);
+ break;
+
+ case PARSEIOC_SETCS:
+ parseprintf(DD_WPUT,("parse: parsewput - PARSEIOC_SETCS\n"));
+ ok = parse_setcs(dct, &parse->parse_io);
+ break;
+ }
+ mp->b_datap->db_type = ok ? M_IOCACK : M_IOCNAK;
+ }
+ else
+ {
+ mp->b_datap->db_type = M_IOCNAK;
+ }
+ parseprintf(DD_WPUT,("parse: parsewput qreply - %s\n", (mp->b_datap->db_type == M_IOCNAK) ? "M_IOCNAK" : "M_IOCACK"));
+ qreply(q, mp);
+ break;
+ }
+ }
+}
+
+/*
+ * read characters from streams buffers
+ */
+static unsigned long rdchar(mp)
+ register mblk_t **mp;
+{
+ while (*mp != (mblk_t *)NULL)
+ {
+ if ((*mp)->b_wptr - (*mp)->b_rptr)
+ {
+ return (unsigned long)(*(unsigned char *)((*mp)->b_rptr++));
+ }
+ else
+ {
+ register mblk_t *mmp = *mp;
+
+ *mp = (*mp)->b_cont;
+ freeb(mmp);
+ }
+ }
+ return ~0;
+}
+
+/*
+ * convert incoming data
+ */
+static int parserput(q, mp)
+ queue_t *q;
+ mblk_t *mp;
+{
+ unsigned char type;
+
+ switch (type = mp->b_datap->db_type)
+ {
+ default:
+ /*
+ * anything we don't know will be put on queue
+ * the service routine will move it to the next one
+ */
+ parseprintf(DD_RPUT,("parse: parserput - forward type 0x%x\n", type));
+ if (canput(q->q_next) || (mp->b_datap->db_type > QPCTL))
+ {
+ putnext(q, mp);
+ }
+ else
+ putq(q, mp);
+ break;
+
+ case M_BREAK:
+ case M_DATA:
+ {
+ register parsestream_t * parse = (parsestream_t *)q->q_ptr;
+ register mblk_t *nmp;
+ register unsigned long ch;
+ timestamp_t ctime;
+
+ /*
+ * get time on packet delivery
+ */
+ uniqtime(&ctime.tv);
+
+ if (!(parse->parse_status & PARSE_ENABLE))
+ {
+ parseprintf(DD_RPUT,("parse: parserput - parser disabled - forward type 0x%x\n", type));
+ if (canput(q->q_next) || (mp->b_datap->db_type > QPCTL))
+ {
+ putnext(q, mp);
+ }
+ else
+ putq(q, mp);
+ }
+ else
+ {
+ parseprintf(DD_RPUT,("parse: parserput - M_%s\n", (type == M_DATA) ? "DATA" : "BREAK"));
+
+ if (type == M_DATA)
+ {
+ /*
+ * parse packet looking for start an end characters
+ */
+ while (mp != (mblk_t *)NULL)
+ {
+ ch = rdchar(&mp);
+ if (ch != ~0 && parse_ioread(&parse->parse_io, (char)ch, &ctime))
+ {
+ /*
+ * up up and away (hopefully ...)
+ * don't press it if resources are tight or nobody wants it
+ */
+ nmp = (mblk_t *)NULL;
+ if (canput(parse->parse_queue->q_next) && (nmp = allocb(sizeof(parsetime_t), BPRI_MED)))
+ {
+ bcopy((caddr_t)&parse->parse_io.parse_dtime, (caddr_t)nmp->b_rptr, sizeof(parsetime_t));
+ nmp->b_wptr += sizeof(parsetime_t);
+ putnext(parse->parse_queue, nmp);
+ }
+ else
+ if (nmp) freemsg(nmp);
+ parse_iodone(&parse->parse_io);
+ }
+ }
+ }
+ else
+ {
+ if (parse_ioread(&parse->parse_io, (char)0, &ctime))
+ {
+ /*
+ * up up and away (hopefully ...)
+ * don't press it if resources are tight or nobody wants it
+ */
+ nmp = (mblk_t *)NULL;
+ if (canput(parse->parse_queue->q_next) && (nmp = allocb(sizeof(parsetime_t), BPRI_MED)))
+ {
+ bcopy((caddr_t)&parse->parse_io.parse_dtime, (caddr_t)nmp->b_rptr, sizeof(parsetime_t));
+ nmp->b_wptr += sizeof(parsetime_t);
+ putnext(parse->parse_queue, nmp);
+ }
+ else
+ if (nmp) freemsg(nmp);
+ parse_iodone(&parse->parse_io);
+ }
+ freemsg(mp);
+ }
+ break;
+ }
+ }
+
+ /*
+ * CD PPS support for non direct ISR hack
+ */
+ case M_HANGUP:
+ case M_UNHANGUP:
+ {
+ register parsestream_t * parse = (parsestream_t *)q->q_ptr;
+ timestamp_t ctime;
+ register mblk_t *nmp;
+ register int status = cd_invert ^ (type == M_HANGUP);
+
+ SET_LED(status);
+
+ uniqtime(&ctime.tv);
+
+ TIMEVAL_USADD(&ctime.tv, stdelay);
+
+ parseprintf(DD_RPUT,("parse: parserput - M_%sHANGUP\n", (type == M_HANGUP) ? "" : "UN"));
+
+ if ((parse->parse_status & PARSE_ENABLE) &&
+ parse_iopps(&parse->parse_io, status ? SYNC_ONE : SYNC_ZERO, &ctime))
+ {
+ nmp = (mblk_t *)NULL;
+ if (canput(parse->parse_queue->q_next) && (nmp = allocb(sizeof(parsetime_t), BPRI_MED)))
+ {
+ bcopy((caddr_t)&parse->parse_io.parse_dtime, (caddr_t)nmp->b_rptr, sizeof(parsetime_t));
+ nmp->b_wptr += sizeof(parsetime_t);
+ putnext(parse->parse_queue, nmp);
+ }
+ else
+ if (nmp) freemsg(nmp);
+ parse_iodone(&parse->parse_io);
+ freemsg(mp);
+ }
+ else
+ if (canput(q->q_next) || (mp->b_datap->db_type > QPCTL))
+ {
+ putnext(q, mp);
+ }
+ else
+ putq(q, mp);
+
+ if (status)
+ {
+ parse->parse_ppsclockev.tv = ctime.tv;
+ ++(parse->parse_ppsclockev.serial);
+ }
+ }
+ }
+}
+
+static int init_zs_linemon(); /* handle line monitor for "zs" driver */
+static void close_zs_linemon();
+static void zs_xsisr(); /* zs external status interupt handler */
+
+/*-------------------- CD isr status monitor ---------------*/
+
+static int init_linemon(q)
+ register queue_t *q;
+{
+ register queue_t *dq;
+
+ dq = WR(q);
+ /*
+ * we ARE doing very bad things down here (basically stealing ISR
+ * hooks)
+ *
+ * so we chase down the STREAMS stack searching for the driver
+ * and if this is a known driver we insert our ISR routine for
+ * status changes in to the ExternalStatus handling hook
+ */
+ while (dq->q_next)
+ {
+ dq = dq->q_next; /* skip down to driver */
+ }
+
+ /*
+ * find appropriate driver dependent routine
+ */
+ if (dq->q_qinfo && dq->q_qinfo->qi_minfo)
+ {
+ register char *dname = dq->q_qinfo->qi_minfo->mi_idname;
+
+ parseprintf(DD_INSTALL, ("init_linemon: driver is \"%s\"\n", dname));
+
+#ifdef sun
+ if (dname && !strcmp(dname, "zs"))
+ {
+ return init_zs_linemon(dq, q);
+ }
+ else
+#endif
+ {
+ parseprintf(DD_INSTALL, ("init_linemon: driver \"%s\" not suitable for CD monitoring\n", dname));
+ return 0;
+ }
+ }
+ parseprintf(DD_INSTALL, ("init_linemon: cannot find driver\n"));
+ return 0;
+}
+
+static void close_linemon(q, my_q)
+ register queue_t *q;
+ register queue_t *my_q;
+{
+ /*
+ * find appropriate driver dependent routine
+ */
+ if (q->q_qinfo && q->q_qinfo->qi_minfo)
+ {
+ register char *dname = q->q_qinfo->qi_minfo->mi_idname;
+
+#ifdef sun
+ if (dname && !strcmp(dname, "zs"))
+ {
+ close_zs_linemon(q, my_q);
+ return;
+ }
+ parseprintf(DD_INSTALL, ("close_linemon: cannot find driver close routine for \"%s\"\n", dname));
+#endif
+ }
+ parseprintf(DD_INSTALL, ("close_linemon: cannot find driver name\n"));
+}
+
+#ifdef sun
+#include <sundev/zsreg.h>
+#include <sundev/zscom.h>
+#include <sundev/zsvar.h>
+
+struct savedzsops
+{
+ struct zsops zsops;
+ struct zsops *oldzsops;
+};
+
+struct zsops *emergencyzs;
+
+static int init_zs_linemon(q, my_q)
+ register queue_t *q;
+ register queue_t *my_q;
+{
+ register struct zscom *zs;
+ register struct savedzsops *szs;
+ register parsestream_t *parsestream = (parsestream_t *)my_q->q_ptr;
+ /*
+ * we expect the zsaline pointer in the q_data pointer
+ * from there on we insert our on EXTERNAL/STATUS ISR routine
+ * into the interrupt path, before the standard handler
+ */
+ zs = ((struct zsaline *)q->q_ptr)->za_common;
+ if (!zs)
+ {
+ /*
+ * well - not found on startup - just say no (shouldn't happen though)
+ */
+ return 0;
+ }
+ else
+ {
+ unsigned long s;
+
+ /*
+ * we do a direct replacement, in case others fiddle also
+ * if somebody else grabs our hook and we disconnect
+ * we are in DEEP trouble - panic is likely to be next, sorry
+ */
+ szs = (struct savedzsops *) kmem_alloc(sizeof(struct savedzsops));
+
+ if (szs == (struct savedzsops *)0)
+ {
+ parseprintf(DD_INSTALL, ("init_zs_linemon: CD monitor NOT installed - no memory\n"));
+
+ return 0;
+ }
+ else
+ {
+ parsestream->parse_data = (void *)szs;
+
+ s = splhigh();
+
+ parsestream->parse_dqueue = q; /* remember driver */
+
+ szs->zsops = *zs->zs_ops;
+ szs->zsops.zsop_xsint = (int (*)())zs_xsisr; /* place our bastard */
+ szs->oldzsops = zs->zs_ops;
+ emergencyzs = zs->zs_ops;
+
+ zsopinit(zs, &szs->zsops); /* hook it up */
+
+ (void) splx(s);
+
+ parseprintf(DD_INSTALL, ("init_zs_linemon: CD monitor installed\n"));
+
+ return 1;
+ }
+ }
+}
+
+/*
+ * unregister our ISR routine - must call under splhigh()
+ */
+static void close_zs_linemon(q, my_q)
+ register queue_t *q;
+ register queue_t *my_q;
+{
+ register struct zscom *zs;
+ register parsestream_t *parsestream = (parsestream_t *)my_q->q_ptr;
+
+ zs = ((struct zsaline *)q->q_ptr)->za_common;
+ if (!zs)
+ {
+ /*
+ * well - not found on startup - just say no (shouldn't happen though)
+ */
+ return;
+ }
+ else
+ {
+ register struct savedzsops *szs = (struct savedzsops *)parsestream->parse_data;
+
+ zsopinit(zs, szs->oldzsops); /* reset to previous handler functions */
+
+ kmem_free((caddr_t)szs, sizeof (struct savedzsops));
+
+ parseprintf(DD_INSTALL, ("close_zs_linemon: CD monitor deleted\n"));
+ return;
+ }
+}
+
+#define MAXDEPTH 50 /* maximum allowed stream crawl */
+
+#ifdef PPS_SYNC
+extern hardpps();
+extern struct timeval time;
+#endif
+
+/*
+ * take external status interrupt (only CD interests us)
+ */
+static void zs_xsisr(zs)
+ register struct zscom *zs;
+{
+ register struct zsaline *za = (struct zsaline *)zs->zs_priv;
+ register struct zscc_device *zsaddr = zs->zs_addr;
+ register queue_t *q;
+ register unsigned char zsstatus;
+ register int loopcheck;
+ register char *dname;
+#ifdef PPS_SYNC
+ register int s;
+ register long usec;
+#endif
+
+ /*
+ * pick up current state
+ */
+ zsstatus = zsaddr->zscc_control;
+
+ if ((za->za_rr0 ^ zsstatus) & (ZSRR0_CD|ZSRR0_SYNC))
+ {
+ timestamp_t cdevent;
+ register int status;
+
+ /*
+ * CONDITIONAL external measurement support
+ */
+ SET_LED(zsstatus & (ZSRR0_CD|ZSRR0_SYNC)); /*
+ * inconsistent with upper SET_LED, but this
+ * is for oscilloscope business anyway and we
+ * are just interested in edge delays in the
+ * lower us range
+ */
+#ifdef PPS_SYNC
+ s = splclock();
+ usec = time.tv_usec;
+#endif
+ /*
+ * time stamp
+ */
+ uniqtime(&cdevent.tv);
+
+#ifdef PPS_SYNC
+ splx(s);
+#endif
+
+ /*
+ * logical state
+ */
+ status = cd_invert ? (zsstatus & ZSRR0_SYNC) == 0 : (zsstatus & ZSRR0_SYNC) != 0;
+
+#ifdef PPS_SYNC
+ if (status)
+ {
+ usec = cdevent.tv.tv_usec - usec;
+ if (usec < 0)
+ usec += 1000000;
+
+ hardpps(&cdevent.tv, usec);
+ }
+#endif
+
+ TIMEVAL_USADD(&cdevent.tv, xsdelay);
+
+ q = za->za_ttycommon.t_readq;
+
+ /*
+ * ok - now the hard part - find ourself
+ */
+ loopcheck = MAXDEPTH;
+
+ while (q)
+ {
+ if (q->q_qinfo && q->q_qinfo->qi_minfo)
+ {
+ dname = q->q_qinfo->qi_minfo->mi_idname;
+
+ if (!strcmp(dname, parseinfo.st_rdinit->qi_minfo->mi_idname))
+ {
+ /*
+ * back home - phew (hopping along stream queues might
+ * prove dangerous to your health)
+ */
+
+ if ((((parsestream_t *)q->q_ptr)->parse_status & PARSE_ENABLE) &&
+ parse_iopps(&((parsestream_t *)q->q_ptr)->parse_io, status ? SYNC_ONE : SYNC_ZERO, &cdevent))
+ {
+ /*
+ * XXX - currently we do not pass up the message, as
+ * we should.
+ * for a correct behaviour wee need to block out
+ * processing until parse_iodone has been posted via
+ * a softcall-ed routine which does the message pass-up
+ * right now PPS information relies on input being
+ * received
+ */
+ parse_iodone(&((parsestream_t *)q->q_ptr)->parse_io);
+ }
+
+ if (status)
+ {
+ ((parsestream_t *)q->q_ptr)->parse_ppsclockev.tv = cdevent.tv;
+ ++(((parsestream_t *)q->q_ptr)->parse_ppsclockev.serial);
+ }
+
+ parseprintf(DD_ISR, ("zs_xsisr: CD event %s has been posted for \"%s\"\n", status ? "ONE" : "ZERO", dname));
+ break;
+ }
+ }
+
+ q = q->q_next;
+
+ if (!loopcheck--)
+ {
+ panic("zs_xsisr: STREAMS Queue corrupted - CD event");
+ }
+ }
+
+ /*
+ * only pretend that CD has been handled
+ */
+ za->za_rr0 = za->za_rr0 & ~(ZSRR0_CD|ZSRR0_SYNC) | zsstatus & (ZSRR0_CD|ZSRR0_SYNC);
+ ZSDELAY(2);
+
+ if (!((za->za_rr0 ^ zsstatus) & ~(ZSRR0_CD|ZSRR0_SYNC)))
+ {
+ /*
+ * all done - kill status indication and return
+ */
+ zsaddr->zscc_control = ZSWR0_RESET_STATUS; /* might kill other conditions here */
+ return;
+ }
+ }
+
+ /*
+ * we are now gathered here to process some unusual external status
+ * interrupts.
+ * any CD events have also been handled and shouldn't be processed
+ * by the original routine (unless we have a VERY busy port pin)
+ * some initializations are done here, which could have been done before for
+ * both code paths but have been avioded for minimum path length to
+ * the uniq_time routine
+ */
+ dname = (char *) 0;
+ q = za->za_ttycommon.t_readq;
+
+ loopcheck = MAXDEPTH;
+
+ /*
+ * the real thing for everything else ...
+ */
+ while (q)
+ {
+ if (q->q_qinfo && q->q_qinfo->qi_minfo)
+ {
+ dname = q->q_qinfo->qi_minfo->mi_idname;
+ if (!strcmp(dname, parseinfo.st_rdinit->qi_minfo->mi_idname))
+ {
+ register int (*zsisr)();
+
+ /*
+ * back home - phew (hopping along stream queues might
+ * prove dangerous to your health)
+ */
+ if (zsisr = ((struct savedzsops *)((parsestream_t *)q->q_ptr)->parse_data)->oldzsops->zsop_xsint)
+ (void)zsisr(zs);
+ else
+ panic("zs_xsisr: unable to locate original ISR");
+
+ parseprintf(DD_ISR, ("zs_xsisr: non CD event was processed for \"%s\"\n", dname));
+ /*
+ * now back to our program ...
+ */
+ return;
+ }
+ }
+
+ q = q->q_next;
+
+ if (!loopcheck--)
+ {
+ panic("zs_xsisr: STREAMS Queue corrupted - non CD event");
+ }
+ }
+
+ /*
+ * last resort - shouldn't even come here as it indicates
+ * corrupted TTY structures
+ */
+ printf("zs_zsisr: looking for \"%s\" - found \"%s\" - taking EMERGENCY path\n", parseinfo.st_rdinit->qi_minfo->mi_idname, dname ? dname : "-NIL-");
+
+ if (emergencyzs && emergencyzs->zsop_xsint)
+ emergencyzs->zsop_xsint(zs);
+ else
+ panic("zs_xsisr: no emergency ISR handler");
+}
+#endif /* sun */
+
+/*
+ * History:
+ *
+ * parsestreams.c,v
+ * Revision 3.22 1994/06/01 10:41:16 kardel
+ * CD seems to happen on ZSRR0_SYNC
+ *
+ * Revision 3.21 1994/06/01 08:18:57 kardel
+ * look at CD only
+ *
+ * Revision 3.20 1994/05/30 09:57:43 kardel
+ * kmem_alloc checking
+ *
+ * Revision 3.19 1994/02/24 16:33:54 kardel
+ * CD events can olso be posted on sync flag
+ *
+ * Revision 3.18 1994/02/24 14:12:58 kardel
+ * initial PPS_SYNC support version
+ *
+ * Revision 3.17 1994/02/20 15:18:02 kardel
+ * rcs id cleanup
+ *
+ * Revision 3.16 1994/02/15 22:39:50 kardel
+ * memory leak on open failure closed
+ *
+ * Revision 3.15 1994/02/13 19:16:50 kardel
+ * updated verbose Copyright message
+ *
+ * Revision 3.14 1994/02/02 17:45:38 kardel
+ * rcs ids fixed
+ *
+ * Revision 3.12 1994/01/25 19:05:30 kardel
+ * 94/01/23 reconcilation
+ *
+ * Revision 3.11 1994/01/23 17:22:07 kardel
+ * 1994 reconcilation
+ *
+ * Revision 3.10 1993/12/15 12:48:58 kardel
+ * fixed message loss on M_*HANHUP messages
+ *
+ * Revision 3.9 1993/11/05 15:34:55 kardel
+ * shut up nice feature detection
+ *
+ * Revision 3.8 1993/10/22 14:27:56 kardel
+ * Oct. 22nd 1993 reconcilation
+ *
+ * Revision 3.7 1993/10/10 18:13:53 kardel
+ * Makefile reorganisation, file relocation
+ *
+ * Revision 3.6 1993/10/09 15:01:18 kardel
+ * file structure unified
+ *
+ * Revision 3.5 1993/10/04 07:59:31 kardel
+ * Well, at least we should know that a the tv_usec field should be in the range 0..999999
+ *
+ * Revision 3.4 1993/09/26 23:41:33 kardel
+ * new parse driver logic
+ *
+ * Revision 3.3 1993/09/11 00:38:34 kardel
+ * LINEMON must also cover M_[UN]HANGUP handling
+ *
+ * Revision 3.2 1993/07/06 10:02:56 kardel
+ * DCF77 driver goes generic...
+ *
+ */
diff --git a/usr.sbin/xntpd/parse/util/Makefile b/usr.sbin/xntpd/parse/util/Makefile
new file mode 100644
index 0000000..851e8df
--- /dev/null
+++ b/usr.sbin/xntpd/parse/util/Makefile
@@ -0,0 +1,24 @@
+#
+# $Id$
+#
+
+CFLAGS+= -I${.CURDIR}/../../include
+CFLAGS+= -DNTP_POSIX_SOURCE -DUSE_PROTOTYPES
+CFLAGS+= -DSYS_FREEBSD -DBOEDER -DHAVE_TERMIOS -DHAVE_BSD_NICE
+
+.if exists(${.OBJDIR}/../../lib)
+LDADD+= -L${.OBJDIR}/../../lib
+DPADD+= -L${.OBJDIR}/../../lib/libntp.a
+.else
+LDADD+= -L${.CURDIR}/../../lib
+DPADD+= -L${.CURDIR}/../../lib/libntp.a
+.endif
+
+LDADD+= -lntp
+BINDIR= /usr/sbin
+PROG= dcfd
+
+SRCS= dcfd.c
+NOMAN=
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/xntpd/parse/util/Makefile.tmpl b/usr.sbin/xntpd/parse/util/Makefile.tmpl
new file mode 100644
index 0000000..aa0b262
--- /dev/null
+++ b/usr.sbin/xntpd/parse/util/Makefile.tmpl
@@ -0,0 +1,49 @@
+#
+# /src/NTP/REPOSITORY/v3/parse/util/Makefile.tmpl,v 3.12 1994/01/25 19:05:39 kardel Exp
+#
+COMPILER= cc
+DEFS=
+DEFS_OPT=
+DEFS_LOCAL=
+CLOCKDEFS=
+INCL=
+COPTS= -O
+INSTALL= install
+BINDIR=
+#
+CFLAGS= $(COPTS) $(DEFS) $(DEFS_LOCAL) $(INCL) -I../../include
+CC= $(COMPILER)
+TOP=../../
+#
+EXECS=parsetest testdcf dcfd
+
+all:
+ @echo $(DEFS) $(DEFS_LOCAL) $(CLOCKDEFS) | \
+ awk '/-DSTREAM/ && /-DPARSE/ && /-DCLOCK_RAWDCF/ && ( /-DSYS_SUNOS/ || /-DSYS_SOLARIS/ ) { makeit = 1 } \
+ END { if (makeit) \
+ { print "$(MAKE) $(MFLAGS) MFLAGS=\"$(MFLAGS)\" parsetest"; } \
+ }' | \
+ sh
+ @echo $(DEFS) $(DEFS_LOCAL) $(CLOCKDEFS) | \
+ awk '/-DPARSE/ && /-DCLOCK_RAWDCF/ && ( /-DSYS_SUNOS/ || /-DSYS_SOLARIS/ ) { makeit = 1 } \
+ END { if (makeit) \
+ { print "$(MAKE) $(MFLAGS) MFLAGS=\"$(MFLAGS)\" testdcf"; } \
+ }' | \
+ sh
+ @echo $(DEFS) $(DEFS_LOCAL) $(CLOCKDEFS) | \
+ awk '/-DPARSE/ && /-DCLOCK_RAWDCF/ && ( /-DSYS_SUNOS/ || /-DSYS_SOLARIS/ ) { makeit = 1 } \
+ END { if (makeit) \
+ { print "$(MAKE) $(MFLAGS) MFLAGS=\"$(MFLAGS)\" dcfd"; } \
+ }' | \
+ sh
+
+clean:
+ -@rm -f $(EXECS) *.o
+
+distclean: clean
+ -@rm -f *.orig *.rej .version Makefile
+
+install:
+ @echo "--- DCF77 utilities should be installed manually"
+ @#[ -f testdcf ] && $(INSTALL) -c -m 0755 testdcf $(BINDIR) || true
+ @#[ -f dcfd ] && $(INSTALL) -c -m 0755 dcfd $(BINDIR) || true
diff --git a/usr.sbin/xntpd/parse/util/README b/usr.sbin/xntpd/parse/util/README
new file mode 100644
index 0000000..e1c80d4
--- /dev/null
+++ b/usr.sbin/xntpd/parse/util/README
@@ -0,0 +1,19 @@
+This directory contains some DCF77 related programs.
+They have not yet fully been ported to other architectures then Sun with
+SunOS 4.x. So if you want to try them you are on your own - a little
+porting may be necessary.
+
+parsetest: simple parse streams module test
+testdcf: simple DCF77 raw impulse test program via 50Baud RS232
+dcfd: simple DCF77 raw impulse receiver with NTP loopfilter
+ mechanics for synchronisation (allows DCF77 synchronisation
+ without network code in a nutshell)
+
+Frank Kardel
+
+----------------
+
+1995-03-20 Dcfd has been ported to FreeBSD 2.0, it works with a
+ Boeder Receiver connected to a 50Baud RS232.
+
+Vincenzo Capuano
diff --git a/usr.sbin/xntpd/parse/util/dcfd.c b/usr.sbin/xntpd/parse/util/dcfd.c
new file mode 100644
index 0000000..3898071
--- /dev/null
+++ b/usr.sbin/xntpd/parse/util/dcfd.c
@@ -0,0 +1,1641 @@
+/*
+ * /src/NTP/REPOSITORY/v3/parse/util/dcfd.c,v 3.18 1994/05/12 12:49:23 kardel Exp
+ *
+ * dcfd.c,v 3.18 1994/05/12 12:49:23 kardel Exp
+ *
+ * Ported to FreeBSD 2.0 1995/03/20 by Vincenzo Capuano
+ *
+ * DCF77 100/200ms pulse synchronisation daemon program (via 50Baud serial line)
+ *
+ * Features:
+ * DCF77 decoding
+ * NTP loopfilter logic for local clock
+ * interactive display for debugging
+ *
+ * Lacks:
+ * Leap second handling (at that level you should switch to xntp3 - really!)
+ *
+ * Copyright (c) 1993,1994
+ * Frank Kardel, Friedrich-Alexander Universitaet Erlangen-Nuernberg
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * This program may not be sold or used for profit without prior
+ * written consent of the author.
+ */
+
+#include <unistd.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/signal.h>
+#include <sys/errno.h>
+#include <syslog.h>
+
+/*
+ * NTP compilation environment
+ */
+#ifdef USE_PROTOTYPES
+#include "ntp_stdlib.h"
+#include <signal.h>
+#include <stdio.h>
+#endif
+
+#ifdef SYS_LINUX
+#include "ntp_timex.h"
+#endif
+
+/*
+ * select which terminal handling to use (currently only SysV variants)
+ */
+#if defined(HAVE_TERMIOS) || defined(STREAM)
+#include <termios.h>
+#define TTY_GETATTR(_FD_, _ARG_) tcgetattr((_FD_), (_ARG_))
+#define TTY_SETATTR(_FD_, _ARG_) tcsetattr((_FD_), TCSANOW, (_ARG_))
+#endif
+
+#if defined(HAVE_TERMIO) || defined(HAVE_SYSV_TTYS)
+#include <termio.h>
+#define TTY_GETATTR(_FD_, _ARG_) ioctl((_FD_), TCGETA, (_ARG_))
+#define TTY_SETATTR(_FD_, _ARG_) ioctl((_FD_), TCSETAW, (_ARG_))
+#endif
+
+#ifndef TTY_GETATTR
+MUST DEFINE ONE OF "HAVE_TERMIOS" or "HAVE_TERMIO"
+#endif
+
+#ifndef dysize
+#define dysize(_x_) (((_x_) % 4) ? 365 : (((_x_) % 400) ? 365 : 366))
+#endif
+
+#define timernormalize(_a_) \
+ if ((_a_)->tv_usec >= 1000000) \
+ { \
+ (_a_)->tv_sec += (_a_)->tv_usec / 1000000; \
+ (_a_)->tv_usec = (_a_)->tv_usec % 1000000; \
+ } \
+ if ((_a_)->tv_usec < 0) \
+ { \
+ (_a_)->tv_sec -= 1 + -(_a_)->tv_usec / 1000000; \
+ (_a_)->tv_usec = 1000000 - (-(_a_)->tv_usec % 1000000); \
+ }
+
+#define timeradd(_a_, _b_) \
+ (_a_)->tv_sec += (_b_)->tv_sec; \
+ (_a_)->tv_usec += (_b_)->tv_usec; \
+ timernormalize((_a_))
+
+#define timersub(_a_, _b_) \
+ (_a_)->tv_sec -= (_b_)->tv_sec; \
+ (_a_)->tv_usec -= (_b_)->tv_usec; \
+ timernormalize((_a_))
+
+/*
+ * debug macros
+ */
+#define PRINTF if (interactive) printf
+#define LPRINTF if (interactive && loop_filter_debug) printf
+
+#ifdef DEBUG
+#define dprintf(_x_) PRINTF _x_
+#else
+#define dprintf(_x_)
+#endif
+
+extern int errno;
+
+/*
+ * display received data (avoids also detaching from tty)
+ */
+static int interactive = 0;
+
+/*
+ * display loopfilter (clock control) variables
+ */
+static int loop_filter_debug = 0;
+
+/*
+ * do not set/adjust system time
+ */
+static int no_set = 0;
+
+/*
+ * time that passes between start of DCF impulse and time stamping (fine
+ * adjustment) in microseconds (receiver/OS dependent)
+ */
+#define DEFAULT_DELAY 230000 /* rough estimate */
+
+/*
+ * The two states we can be in - eithe we receive nothing
+ * usable or we have the correct time
+ */
+#define NO_SYNC 0x01
+#define SYNC 0x02
+
+static int sync_state = NO_SYNC;
+static time_t last_sync;
+
+static unsigned long ticks = 0;
+
+static char pat[] = "-\\|/";
+
+#define LINES (24-2) /* error lines after which the two headlines are repeated */
+
+#define MAX_UNSYNC (10*60) /* allow synchronisation loss for 10 minutes */
+#define NOTICE_INTERVAL (20*60) /* mention missing synchronisation every 20 minutes */
+
+/*
+ * clock adjustment PLL - see NTP protocol spec (RFC1305) for details
+ */
+
+#define USECSCALE 10
+#define TIMECONSTANT 2
+#define ADJINTERVAL 0
+#define FREQ_WEIGHT 18
+#define PHASE_WEIGHT 7
+#define MAX_DRIFT 0x3FFFFFFF
+
+#define R_SHIFT(_X_, _Y_) (((_X_) < 0) ? -(-(_X_) >> (_Y_)) : ((_X_) >> (_Y_)))
+
+static struct timeval max_adj_offset = { 0, 128000 };
+
+static long clock_adjust = 0; /* current adjustment value (usec * 2^USECSCALE) */
+static long drift_comp = 0; /* accumulated drift value (usec / ADJINTERVAL) */
+static long adjustments = 0;
+static char skip_adjust = 1; /* discard first adjustment (bad samples) */
+
+/*
+ * DCF77 state flags
+ */
+#define DCFB_ANNOUNCE 0x0001 /* switch time zone warning (DST switch) */
+#define DCFB_DST 0x0002 /* DST in effect */
+#define DCFB_LEAP 0x0004 /* LEAP warning (1 hour prior to occurence) */
+#define DCFB_ALTERNATE 0x0008 /* alternate antenna used */
+
+struct clocktime /* clock time broken up from time code */
+{
+ long wday; /* Day of week: 1: Monday - 7: Sunday */
+ long day;
+ long month;
+ long year;
+ long hour;
+ long minute;
+ long second;
+ long usecond;
+ long utcoffset; /* in minutes */
+ long flags; /* current clock status (DCF77 state flags) */
+};
+
+typedef struct clocktime clocktime_t;
+
+/*
+ * (usually) quick constant multiplications
+ */
+#define TIMES10(_X_) (((_X_) << 3) + ((_X_) << 1)) /* *8 + *2 */
+#define TIMES24(_X_) (((_X_) << 4) + ((_X_) << 3)) /* *16 + *8 */
+#define TIMES60(_X_) ((((_X_) << 4) - (_X_)) << 2) /* *(16 - 1) *4 */
+/*
+ * generic abs() function
+ */
+#define abs(_x_) (((_x_) < 0) ? -(_x_) : (_x_))
+
+/*
+ * conversion related return/error codes
+ */
+#define CVT_MASK 0x0000000F /* conversion exit code */
+#define CVT_NONE 0x00000001 /* format not applicable */
+#define CVT_FAIL 0x00000002 /* conversion failed - error code returned */
+#define CVT_OK 0x00000004 /* conversion succeeded */
+#define CVT_BADFMT 0x00000010 /* general format error - (unparsable) */
+#define CVT_BADDATE 0x00000020 /* invalid date */
+#define CVT_BADTIME 0x00000040 /* invalid time */
+
+/*
+ * DCF77 raw time code
+ *
+ * From "Zur Zeit", Physikalisch-Technische Bundesanstalt (PTB), Braunschweig
+ * und Berlin, Maerz 1989
+ *
+ * Timecode transmission:
+ * AM:
+ * time marks are send every second except for the second before the
+ * next minute mark
+ * time marks consist of a reduction of transmitter power to 25%
+ * of the nominal level
+ * the falling edge is the time indication (on time)
+ * time marks of a 100ms duration constitute a logical 0
+ * time marks of a 200ms duration constitute a logical 1
+ * FM:
+ * see the spec. (basically a (non-)inverted psuedo random phase shift)
+ *
+ * Encoding:
+ * Second Contents
+ * 0 - 10 AM: free, FM: 0
+ * 11 - 14 free
+ * 15 R - alternate antenna
+ * 16 A1 - expect zone change (1 hour before)
+ * 17 - 18 Z1,Z2 - time zone
+ * 0 0 illegal
+ * 0 1 MEZ (MET)
+ * 1 0 MESZ (MED, MET DST)
+ * 1 1 illegal
+ * 19 A2 - expect leap insertion/deletion (1 hour before)
+ * 20 S - start of time code (1)
+ * 21 - 24 M1 - BCD (lsb first) Minutes
+ * 25 - 27 M10 - BCD (lsb first) 10 Minutes
+ * 28 P1 - Minute Parity (even)
+ * 29 - 32 H1 - BCD (lsb first) Hours
+ * 33 - 34 H10 - BCD (lsb first) 10 Hours
+ * 35 P2 - Hour Parity (even)
+ * 36 - 39 D1 - BCD (lsb first) Days
+ * 40 - 41 D10 - BCD (lsb first) 10 Days
+ * 42 - 44 DW - BCD (lsb first) day of week (1: Monday -> 7: Sunday)
+ * 45 - 49 MO - BCD (lsb first) Month
+ * 50 MO0 - 10 Months
+ * 51 - 53 Y1 - BCD (lsb first) Years
+ * 54 - 57 Y10 - BCD (lsb first) 10 Years
+ * 58 P3 - Date Parity (even)
+ * 59 - usually missing (minute indication), except for leap insertion
+ */
+
+/*-----------------------------------------------------------------------
+ * conversion table to map DCF77 bit stream into data fields.
+ * Encoding:
+ * Each field of the DCF77 code is described with two adjacent entries in
+ * this table. The first entry specifies the offset into the DCF77 data stream
+ * while the length is given as the difference between the start index and
+ * the start index of the following field.
+ */
+static struct rawdcfcode
+{
+ char offset; /* start bit */
+} rawdcfcode[] =
+{
+ { 0 }, { 15 }, { 16 }, { 17 }, { 19 }, { 20 }, { 21 }, { 25 }, { 28 }, { 29 },
+ { 33 }, { 35 }, { 36 }, { 40 }, { 42 }, { 45 }, { 49 }, { 50 }, { 54 }, { 58 }, { 59 }
+};
+
+/*-----------------------------------------------------------------------
+ * symbolic names for the fields of DCF77 describes in "rawdcfcode".
+ * see comment above for the structure of the DCF77 data
+ */
+#define DCF_M 0
+#define DCF_R 1
+#define DCF_A1 2
+#define DCF_Z 3
+#define DCF_A2 4
+#define DCF_S 5
+#define DCF_M1 6
+#define DCF_M10 7
+#define DCF_P1 8
+#define DCF_H1 9
+#define DCF_H10 10
+#define DCF_P2 11
+#define DCF_D1 12
+#define DCF_D10 13
+#define DCF_DW 14
+#define DCF_MO 15
+#define DCF_MO0 16
+#define DCF_Y1 17
+#define DCF_Y10 18
+#define DCF_P3 19
+
+/*-----------------------------------------------------------------------
+ * parity field table (same encoding as rawdcfcode)
+ * This table describes the sections of the DCF77 code that are
+ * parity protected
+ */
+static struct partab
+{
+ char offset; /* start bit of parity field */
+} partab[] =
+{
+ { 21 }, { 29 }, { 36 }, { 59 }
+};
+
+/*-----------------------------------------------------------------------
+ * offsets for parity field descriptions
+ */
+#define DCF_P_P1 0
+#define DCF_P_P2 1
+#define DCF_P_P3 2
+
+/*-----------------------------------------------------------------------
+ * legal values for time zone information
+ */
+#define DCF_Z_MET 0x2
+#define DCF_Z_MED 0x1
+
+/*-----------------------------------------------------------------------
+ * symbolic representation if the DCF77 data stream
+ */
+static struct dcfparam
+{
+ unsigned char onebits[60];
+ unsigned char zerobits[60];
+} dcfparam =
+{
+ "###############RADMLS1248124P124812P1248121241248112481248P", /* 'ONE' representation */
+ "--------------------s-------p------p----------------------p" /* 'ZERO' representation */
+};
+
+/*-----------------------------------------------------------------------
+ * extract a bitfield from DCF77 datastream
+ * All numeric field are LSB first.
+ * buf holds a pointer to a DCF77 data buffer in symbolic
+ * representation
+ * idx holds the index to the field description in rawdcfcode
+ */
+static unsigned long ext_bf(buf, idx)
+ register unsigned char *buf;
+ register int idx;
+{
+ register unsigned long sum = 0;
+ register int i, first;
+
+ first = rawdcfcode[idx].offset;
+
+ for (i = rawdcfcode[idx+1].offset - 1; i >= first; i--)
+ {
+ sum <<= 1;
+ sum |= (buf[i] != dcfparam.zerobits[i]);
+ }
+ return sum;
+}
+
+/*-----------------------------------------------------------------------
+ * check even parity integrity for a bitfield
+ *
+ * buf holds a pointer to a DCF77 data buffer in symbolic
+ * representation
+ * idx holds the index to the field description in partab
+ */
+static unsigned pcheck(buf, idx)
+ register unsigned char *buf;
+ register int idx;
+{
+ register int i,last;
+ register unsigned psum = 1;
+
+ last = partab[idx+1].offset;
+
+ for (i = partab[idx].offset; i < last; i++)
+ psum ^= (buf[i] != dcfparam.zerobits[i]);
+
+ return psum;
+}
+
+/*-----------------------------------------------------------------------
+ * convert a DCF77 data buffer into wall clock time + flags
+ *
+ * buffer holds a pointer to a DCF77 data buffer in symbolic
+ * representation
+ * size describes the length of DCF77 information in bits (represented
+ * as chars in symbolic notation
+ * clock points to a wall clock time description of the DCF77 data (result)
+ */
+static unsigned long convert_rawdcf(buffer, size, clock)
+ register unsigned char *buffer;
+ register int size;
+ register clocktime_t *clock;
+{
+ if (size < 57)
+ {
+ PRINTF("%-30s", "*** INCOMPLETE");
+ return CVT_NONE;
+ }
+
+ /*
+ * check Start and Parity bits
+ */
+ if ((ext_bf(buffer, DCF_S) == 1) &&
+ pcheck(buffer, DCF_P_P1) &&
+ pcheck(buffer, DCF_P_P2) &&
+ pcheck(buffer, DCF_P_P3))
+ {
+ /*
+ * buffer OK - extract all fields and build wall clock time from them
+ */
+
+ clock->flags = 0;
+ clock->usecond= 0;
+ clock->second = 0;
+ clock->minute = ext_bf(buffer, DCF_M10);
+ clock->minute = TIMES10(clock->minute) + ext_bf(buffer, DCF_M1);
+ clock->hour = ext_bf(buffer, DCF_H10);
+ clock->hour = TIMES10(clock->hour) + ext_bf(buffer, DCF_H1);
+ clock->day = ext_bf(buffer, DCF_D10);
+ clock->day = TIMES10(clock->day) + ext_bf(buffer, DCF_D1);
+ clock->month = ext_bf(buffer, DCF_MO0);
+ clock->month = TIMES10(clock->month) + ext_bf(buffer, DCF_MO);
+ clock->year = ext_bf(buffer, DCF_Y10);
+ clock->year = TIMES10(clock->year) + ext_bf(buffer, DCF_Y1);
+ clock->wday = ext_bf(buffer, DCF_DW);
+
+ /*
+ * determine offset to UTC by examining the time zone
+ */
+ switch (ext_bf(buffer, DCF_Z))
+ {
+ case DCF_Z_MET:
+ clock->utcoffset = -60;
+ break;
+
+ case DCF_Z_MED:
+ clock->flags |= DCFB_DST;
+ clock->utcoffset = -120;
+ break;
+
+ default:
+ PRINTF("%-30s", "*** BAD TIME ZONE");
+ return CVT_FAIL|CVT_BADFMT;
+ }
+
+ /*
+ * extract various warnings from DCF77
+ */
+ if (ext_bf(buffer, DCF_A1))
+ clock->flags |= DCFB_ANNOUNCE;
+
+ if (ext_bf(buffer, DCF_A2))
+ clock->flags |= DCFB_LEAP;
+
+ if (ext_bf(buffer, DCF_R))
+ clock->flags |= DCFB_ALTERNATE;
+
+ return CVT_OK;
+ }
+ else
+ {
+ /*
+ * bad format - not for us
+ */
+ PRINTF("%-30s", "*** BAD FORMAT (invalid/parity)");
+ return CVT_FAIL|CVT_BADFMT;
+ }
+}
+
+/*-----------------------------------------------------------------------
+ * raw dcf input routine - fix up 50 baud
+ * characters for 1/0 decision
+ */
+static unsigned long cvt_rawdcf(buffer, size, clock)
+ register unsigned char *buffer;
+ register int size;
+ register clocktime_t *clock;
+{
+ register unsigned char *s = buffer;
+ register unsigned char *e = buffer + size;
+ register unsigned char *b = dcfparam.onebits;
+ register unsigned char *c = dcfparam.zerobits;
+ register unsigned rtc = CVT_NONE;
+ register unsigned int i, lowmax, highmax, cutoff, span;
+#define BITS 9
+ unsigned char histbuf[BITS];
+ /*
+ * the input buffer contains characters with runs of consecutive
+ * bits set. These set bits are an indication of the DCF77 pulse
+ * length. We assume that we receive the pulse at 50 Baud. Thus
+ * a 100ms pulse would generate a 4 bit train (20ms per bit and
+ * start bit)
+ * a 200ms pulse would create all zeroes (and probably a frame error)
+ *
+ * The basic idea is that on corret reception we must have two
+ * maxima in the pulse length distribution histogram. (one for
+ * the zero representing pulses and one for the one representing
+ * pulses)
+ * There will always be ones in the datastream, thus we have to see
+ * two maxima.
+ * The best point to cut for a 1/0 decision is the minimum between those
+ * between the maxima. The following code tries to find this cutoff point.
+ */
+
+ /*
+ * clear histogram buffer
+ */
+ for (i = 0; i < BITS; i++)
+ {
+ histbuf[i] = 0;
+ }
+
+ cutoff = 0;
+ lowmax = 0;
+
+ /*
+ * convert sequences of set bits into bits counts updating
+ * the histogram alongway
+ */
+ while (s < e)
+ {
+ register unsigned int ch = *s ^ 0xFF;
+ /*
+ * check integrity and update histogramm
+ */
+ if (!((ch+1) & ch) || !*s)
+ {
+ /*
+ * character ok
+ */
+ for (i = 0; ch; i++)
+ {
+ ch >>= 1;
+ }
+
+ *s = i;
+ histbuf[i]++;
+ cutoff += i;
+ lowmax++;
+ }
+ else
+ {
+ /*
+ * invalid character (no consecutive bit sequence)
+ */
+ dprintf(("parse: cvt_rawdcf: character check for 0x%x@%d FAILED\n", *s, s - buffer));
+ *s = ~0;
+ rtc = CVT_FAIL|CVT_BADFMT;
+ }
+ s++;
+ }
+
+ /*
+ * first cutoff estimate (average bit count - must be between both
+ * maxima)
+ */
+ if (lowmax)
+ {
+ cutoff /= lowmax;
+ }
+ else
+ {
+ cutoff = 4; /* doesn't really matter - it'll fail anyway, but gives error output */
+ }
+
+ dprintf(("parse: cvt_rawdcf: average bit count: %d\n", cutoff));
+
+ lowmax = 0; /* weighted sum */
+ highmax = 0; /* bitcount */
+
+ /*
+ * collect weighted sum of lower bits (left of initial guess)
+ */
+ dprintf(("parse: cvt_rawdcf: histogram:"));
+ for (i = 0; i <= cutoff; i++)
+ {
+ lowmax += histbuf[i] * i;
+ highmax += histbuf[i];
+ dprintf((" %d", histbuf[i]));
+ }
+ dprintf((" <M>"));
+
+ /*
+ * round up
+ */
+ lowmax += highmax / 2;
+
+ /*
+ * calculate lower bit maximum (weighted sum / bit count)
+ *
+ * avoid divide by zero
+ */
+ if (highmax)
+ {
+ lowmax /= highmax;
+ }
+ else
+ {
+ lowmax = 0;
+ }
+
+ highmax = 0; /* weighted sum of upper bits counts */
+ cutoff = 0; /* bitcount */
+
+ /*
+ * collect weighted sum of lower bits (right of initial guess)
+ */
+ for (; i < BITS; i++)
+ {
+ highmax+=histbuf[i] * i;
+ cutoff +=histbuf[i];
+ dprintf((" %d", histbuf[i]));
+ }
+ dprintf(("\n"));
+
+ /*
+ * determine upper maximum (weighted sum / bit count)
+ */
+ if (cutoff)
+ {
+ highmax /= cutoff;
+ }
+ else
+ {
+ highmax = BITS-1;
+ }
+
+ /*
+ * following now holds:
+ * lowmax <= cutoff(initial guess) <= highmax
+ * best cutoff is the minimum nearest to higher bits
+ */
+
+ /*
+ * find the minimum between lowmax and highmax (detecting
+ * possibly a minimum span)
+ */
+ span = cutoff = lowmax;
+ for (i = lowmax; i <= highmax; i++)
+ {
+ if (histbuf[cutoff] > histbuf[i])
+ {
+ /*
+ * got a new minimum move beginning of minimum (cutoff) and
+ * end of minimum (span) there
+ */
+ cutoff = span = i;
+ }
+ else
+ if (histbuf[cutoff] == histbuf[i])
+ {
+ /*
+ * minimum not better yet - but it spans more than
+ * one bit value - follow it
+ */
+ span = i;
+ }
+ }
+
+ /*
+ * cutoff point for 1/0 decision is the middle of the minimum section
+ * in the histogram
+ */
+ cutoff = (cutoff + span) / 2;
+
+ dprintf(("parse: cvt_rawdcf: lower maximum %d, higher maximum %d, cutoff %d\n", lowmax, highmax, cutoff));
+
+ /*
+ * convert the bit counts to symbolic 1/0 information for data conversion
+ */
+ s = buffer;
+ while ((s < e) && *c && *b)
+ {
+ if (*s == (unsigned char)~0)
+ {
+ /*
+ * invalid character
+ */
+ *s = '?';
+ }
+ else
+ {
+ /*
+ * symbolic 1/0 representation
+ */
+ *s = (*s >= cutoff) ? *b : *c;
+ }
+ s++;
+ b++;
+ c++;
+ }
+
+ /*
+ * if everything went well so far return the result of the symbolic
+ * conversion routine else just the accumulated errors
+ */
+ if (rtc != CVT_NONE)
+ {
+ PRINTF("%-30s", "*** BAD DATA");
+ }
+
+ return (rtc == CVT_NONE) ? convert_rawdcf(buffer, size, clock) : rtc;
+}
+
+/*-----------------------------------------------------------------------
+ * convert a wall clock time description of DCF77 to a Unix time (seconds
+ * since 1.1. 1970 UTC)
+ */
+time_t
+dcf_to_unixtime(clock, cvtrtc)
+ register clocktime_t *clock;
+ register unsigned long *cvtrtc;
+{
+#define SETRTC(_X_) { if (cvtrtc) *cvtrtc = (_X_); }
+ static int days_of_month[] =
+ {
+ 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
+ };
+ register int i;
+ time_t t;
+
+ /*
+ * map 2 digit years to 19xx (DCF77 is a 20th century item)
+ */
+ if (clock->year < 100)
+ clock->year += 1900;
+
+ /*
+ * assume that we convert timecode within the unix/UTC epoch -
+ * prolonges validity of 2 digit years
+ */
+ if (clock->year < 1994)
+ clock->year += 100; /* XXX this will do it till <2094 */
+
+ /*
+ * must have been a really negative year code - drop it
+ */
+ if (clock->year < 0)
+ {
+ SETRTC(CVT_FAIL|CVT_BADDATE);
+ return -1;
+ }
+
+ /*
+ * sorry, slow section here - but it's not time critical anyway
+ */
+
+ /*
+ * calculate days since 1970 (watching leap years)
+ */
+ t = (clock->year - 1970) * 365;
+ t += (clock->year >> 2) - (1970 >> 2);
+ t -= clock->year / 400 - 1970 / 400;
+
+ /* month */
+ if (clock->month <= 0 || clock->month > 12)
+ {
+ SETRTC(CVT_FAIL|CVT_BADDATE);
+ return -1; /* bad month */
+ }
+ /* adjust current leap year */
+ if (clock->month >= 3 && dysize(clock->year) == 366)
+ t++;
+
+ /*
+ * collect days from months excluding the current one
+ */
+ for (i = 1; i < clock->month; i++)
+ {
+ t += days_of_month[i];
+ }
+ /* day */
+ if (clock->day < 1 || ((clock->month == 2 && dysize(clock->year) == 366) ?
+ clock->day > 29 : clock->day > days_of_month[clock->month]))
+ {
+ SETRTC(CVT_FAIL|CVT_BADDATE);
+ return -1; /* bad day */
+ }
+
+ /*
+ * collect days from date excluding the current one
+ */
+ t += clock->day - 1;
+
+ /* hour */
+ if (clock->hour < 0 || clock->hour >= 24)
+ {
+ SETRTC(CVT_FAIL|CVT_BADTIME);
+ return -1; /* bad hour */
+ }
+
+ /*
+ * calculate hours from 1. 1. 1970
+ */
+ t = TIMES24(t) + clock->hour;
+
+ /* min */
+ if (clock->minute < 0 || clock->minute > 59)
+ {
+ SETRTC(CVT_FAIL|CVT_BADTIME);
+ return -1; /* bad min */
+ }
+
+ /*
+ * calculate minutes from 1. 1. 1970
+ */
+ t = TIMES60(t) + clock->minute;
+ /* sec */
+
+ /*
+ * calculate UTC in minutes
+ */
+ t += clock->utcoffset;
+
+ if (clock->second < 0 || clock->second > 60) /* allow for LEAPs */
+ {
+ SETRTC(CVT_FAIL|CVT_BADTIME);
+ return -1; /* bad sec */
+ }
+
+ /*
+ * calculate UTC in seconds - phew !
+ */
+ t = TIMES60(t) + clock->second;
+ /* done */
+ return t;
+}
+
+/*-----------------------------------------------------------------------
+ * cheap half baked 1/0 decision - for interactive operation only
+ */
+static char type(c)
+unsigned char c;
+{
+ c ^= 0xFF;
+ return (c > 0xF);
+}
+
+/*-----------------------------------------------------------------------
+ * week day representation
+ */
+static char *wday[8] =
+{
+ "??",
+ "Mo",
+ "Tu",
+ "We",
+ "Th",
+ "Fr",
+ "Sa",
+ "Su"
+};
+
+/*-----------------------------------------------------------------------
+ * generate a string representation for a timeval
+ */
+static char * pr_timeval(val)
+ struct timeval *val;
+{
+ static char buf[20];
+
+ if (val->tv_sec == 0)
+ sprintf(buf, "%c0.%06ld", (val->tv_usec < 0) ? '-' : '+', (long int)abs(val->tv_usec));
+ else
+ sprintf(buf, "%ld.%06ld", (long int)val->tv_sec, (long int)abs(val->tv_usec));
+ return buf;
+}
+
+/*-----------------------------------------------------------------------
+ * correct the current time by an offset by setting the time rigorously
+ */
+static void set_time(offset)
+ struct timeval *offset;
+{
+ struct timeval the_time;
+
+ if (no_set)
+ return;
+
+ LPRINTF("set_time: %s ", pr_timeval(offset));
+ syslog(LOG_NOTICE, "setting time (offset %s)", pr_timeval(offset));
+
+ if (gettimeofday(&the_time, 0L) == -1)
+ {
+ perror("gettimeofday()");
+ }
+ else
+ {
+ timeradd(&the_time, offset);
+ if (settimeofday(&the_time, 0L) == -1)
+ {
+ perror("settimeofday()");
+ }
+ }
+}
+
+/*-----------------------------------------------------------------------
+ * slew the time by a given offset
+ */
+static void adj_time(offset)
+ register long offset;
+{
+ struct timeval time_offset;
+
+ if (no_set)
+ return;
+
+ time_offset.tv_sec = offset / 1000000;
+ time_offset.tv_usec = offset % 1000000;
+
+ LPRINTF("adj_time: %ld us ", (long int)offset);
+ if (adjtime(&time_offset, 0L) == -1)
+ perror("adjtime()");
+}
+
+/*-----------------------------------------------------------------------
+ * read in a possibly previously written drift value
+ */
+static void read_drift(drift_file)
+ char *drift_file;
+{
+ FILE *df;
+
+ df = fopen(drift_file, "r");
+ if (df != NULL)
+ {
+ int idrift, fdrift;
+
+ fscanf(df, "%4d.%03d", &idrift, &fdrift);
+ fclose(df);
+ LPRINTF("read_drift: %d.%03d ppm ", idrift, fdrift);
+
+ drift_comp = idrift << USECSCALE;
+ fdrift = (fdrift << USECSCALE) / 1000;
+ drift_comp += fdrift & (1<<USECSCALE);
+ LPRINTF("read_drift: drift_comp %ld ", (long int)drift_comp);
+ }
+}
+
+/*-----------------------------------------------------------------------
+ * write out the current drift value
+ */
+static void update_drift(drift_file, offset, reftime)
+ char *drift_file;
+ long offset;
+ time_t reftime;
+{
+ FILE *df;
+
+ df = fopen(drift_file, "w");
+ if (df != NULL)
+ {
+ int idrift = R_SHIFT(drift_comp, USECSCALE);
+ int fdrift = drift_comp & ((1<<USECSCALE)-1);
+
+ LPRINTF("update_drift: drift_comp %ld ", (long int)drift_comp);
+ fdrift = (fdrift * 1000) / (1<<USECSCALE);
+ fprintf(df, "%4d.%03d %c%ld.%06ld %.24s\n", idrift, fdrift,
+ (offset < 0) ? '-' : '+', (long int)(abs(offset) / 1000000),
+ (long int)(abs(offset) % 1000000), asctime(localtime(&reftime)));
+ fclose(df);
+ LPRINTF("update_drift: %d.%03d ppm ", idrift, fdrift);
+ }
+}
+
+/*-----------------------------------------------------------------------
+ * process adjustments derived from the DCF77 observation
+ * (controls clock PLL)
+ */
+static void adjust_clock(offset, drift_file, reftime)
+ struct timeval *offset;
+ char *drift_file;
+ time_t reftime;
+{
+ struct timeval toffset;
+ register long usecoffset;
+ int tmp;
+
+ if (no_set)
+ return;
+
+ if (skip_adjust)
+ {
+ skip_adjust = 0;
+ return;
+ }
+
+ toffset = *offset;
+ toffset.tv_sec = abs(toffset.tv_sec);
+ toffset.tv_usec = abs(toffset.tv_usec);
+ if (timercmp(&toffset, &max_adj_offset, >))
+ {
+ /*
+ * hopeless - set the clock - and clear the timing
+ */
+ set_time(offset);
+ clock_adjust = 0;
+ skip_adjust = 1;
+ return;
+ }
+
+ usecoffset = offset->tv_sec * 1000000 + offset->tv_usec;
+
+ clock_adjust = R_SHIFT(usecoffset, TIMECONSTANT); /* adjustment to make for next period */
+
+ tmp = 0;
+ while (adjustments > (1 << tmp))
+ tmp++;
+ adjustments = 0;
+ if (tmp > FREQ_WEIGHT)
+ tmp = FREQ_WEIGHT;
+
+ drift_comp += R_SHIFT(usecoffset << USECSCALE, TIMECONSTANT+TIMECONSTANT+FREQ_WEIGHT-tmp);
+
+ if (drift_comp > MAX_DRIFT) /* clamp into interval */
+ drift_comp = MAX_DRIFT;
+ else
+ if (drift_comp < -MAX_DRIFT)
+ drift_comp = -MAX_DRIFT;
+
+ update_drift(drift_file, usecoffset, reftime);
+ LPRINTF("clock_adjust: %s, clock_adjust %ld, drift_comp %ld(%ld) ",
+ pr_timeval(offset),(long int) R_SHIFT(clock_adjust, USECSCALE),
+ (long int)R_SHIFT(drift_comp, USECSCALE), (long int)drift_comp);
+}
+
+/*-----------------------------------------------------------------------
+ * adjust the clock by a small mount to simulate frequency correction
+ */
+static void periodic_adjust()
+{
+ register long adjustment;
+
+ adjustments++;
+
+ adjustment = R_SHIFT(clock_adjust, PHASE_WEIGHT);
+
+ clock_adjust -= adjustment;
+
+ adjustment += R_SHIFT(drift_comp, USECSCALE+ADJINTERVAL);
+
+ adj_time(adjustment);
+}
+
+/*-----------------------------------------------------------------------
+ * control synchronisation status (warnings) and do periodic adjusts
+ * (frequency control simulation)
+ */
+static void tick()
+{
+ static unsigned long last_notice = 0;
+
+#ifndef SV_ONSTACK
+ (void)signal(SIGALRM, tick);
+#endif
+
+ periodic_adjust();
+
+ ticks += 1<<ADJINTERVAL;
+
+ if ((ticks - last_sync) > MAX_UNSYNC)
+ {
+ /*
+ * not getting time for a while
+ */
+ if (sync_state == SYNC)
+ {
+ /*
+ * completely lost information
+ */
+ sync_state = NO_SYNC;
+ syslog(LOG_INFO, "DCF77 reception lost (timeout)");
+ last_notice = ticks;
+ }
+ else
+ /*
+ * in NO_SYNC state - look whether its time to speak up again
+ */
+ if ((ticks - last_notice) > NOTICE_INTERVAL)
+ {
+ syslog(LOG_NOTICE, "still not synchronized to DCF77 - check receiver/signal");
+ last_notice = ticks;
+ }
+ }
+
+#ifndef ITIMER_REAL
+ (void) alarm(1<<ADJINTERVAL);
+#endif
+}
+
+/*-----------------------------------------------------------------------
+ * break association from terminal to avoid catching terminal
+ * or process group related signals (-> daemon operation)
+ */
+static void detach()
+{
+ int s;
+
+ if (fork())
+ exit(0);
+
+ for (s = 0; s < 3; s++)
+ (void) close(s);
+ (void) open("/", 0);
+ (void) dup2(0, 1);
+ (void) dup2(0, 2);
+
+#if defined(NTP_POSIX_SOURCE) || defined(_POSIX_)
+ (void) setsid();
+#else /* _POSIX_ */
+#ifndef BSD
+ (void) setpgrp();
+#else /* BSD */
+ (void) setpgrp(0, getpid());
+#endif /* BSD */
+#endif /* _POSIX_ */
+#if defined(hpux)
+ if (fork())
+ exit(0);
+#endif /* hpux */
+}
+
+/*-----------------------------------------------------------------------
+ * list possible arguments and options
+ */
+static void usage(program)
+ char *program;
+{
+ fprintf(stderr, "usage: %s [-f] [-l] [-t] [-i] [-o] [-d <drift_file>] <device>\n", program);
+ fprintf(stderr, "\t-n do not change time\n");
+ fprintf(stderr, "\t-i interactive\n");
+ fprintf(stderr, "\t-t trace (print all datagrams)\n");
+ fprintf(stderr, "\t-f print all databits (includes PTB private data)\n");
+ fprintf(stderr, "\t-l print loop filter debug information\n");
+ fprintf(stderr, "\t-o print offet average for current minute\n");
+ fprintf(stderr, "\t-d <drift_file> specify alternate drift file\n");
+ fprintf(stderr, "\t-D <input delay>specify delay from input edge to processing in micro seconds\n");
+}
+
+/*-----------------------------------------------------------------------
+ * main loop - argument interpreter / setup / main loop
+ */
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ unsigned char c;
+ char **a = argv;
+ int ac = argc;
+ char *file = NULL;
+ char *drift_file = "/etc/dcfd.drift";
+ int fd;
+ int offset = 15;
+ int offsets = 0;
+ int delay = DEFAULT_DELAY; /* average delay from input edge to time stamping */
+ int trace = 0;
+ int errs = 0;
+
+ /*
+ * process arguments
+ */
+ while (--ac)
+ {
+ char *arg = *++a;
+ if (*arg == '-')
+ while ((c = *++arg))
+ switch (c)
+ {
+ case 't':
+ trace = 1;
+ interactive = 1;
+ break;
+
+ case 'f':
+ offset = 0;
+ interactive = 1;
+ break;
+
+ case 'l':
+ loop_filter_debug = 1;
+ offsets = 1;
+ interactive = 1;
+ break;
+
+ case 'n':
+ no_set = 1;
+ break;
+
+ case 'o':
+ offsets = 1;
+ interactive = 1;
+ break;
+
+ case 'i':
+ interactive = 1;
+ break;
+
+ case 'D':
+ if (ac > 1)
+ {
+ delay = atoi(*++a);
+ ac--;
+ }
+ else
+ {
+ fprintf(stderr, "%s: -D requires integer argument\n", argv[0]);
+ errs=1;
+ }
+ break;
+
+ case 'd':
+ if (ac > 1)
+ {
+ drift_file = *++a;
+ ac--;
+ }
+ else
+ {
+ fprintf(stderr, "%s: -d requires file name argument\n", argv[0]);
+ errs=1;
+ }
+ break;
+
+ default:
+ fprintf(stderr, "%s: unknown option -%c\n", argv[0], c);
+ errs=1;
+ break;
+ }
+ else
+ if (file == NULL)
+ file = arg;
+ else
+ {
+ fprintf(stderr, "%s: device specified twice\n", argv[0]);
+ errs=1;
+ }
+ }
+
+ if (errs)
+ {
+ usage(argv[0]);
+ exit(1);
+ }
+ else
+ if (file == NULL)
+ {
+ fprintf(stderr, "%s: device not specified\n", argv[0]);
+ usage(argv[0]);
+ exit(1);
+ }
+
+ errs = LINES+1;
+
+ /*
+ * get access to DCF77 tty port
+ */
+#if defined(SYS_FREEBSD) && defined(BOEDER)
+ fd = open(file, O_RDONLY | O_NONBLOCK);
+#else
+ fd = open(file, O_RDONLY);
+#endif
+ if (fd == -1)
+ {
+ perror(file);
+ exit(1);
+ }
+ else
+ {
+ int i, rrc;
+ struct timeval t, tt, tlast;
+ struct timeval timeout;
+ struct timeval phase;
+ struct timeval time_offset;
+ char pbuf[61]; /* printable version */
+ char buf[61]; /* raw data */
+ clocktime_t clock; /* wall clock time */
+ time_t utc_time = 0;
+ time_t last_utc_time = 0;
+ long usecerror = 0;
+ long lasterror = 0;
+#if defined(HAVE_TERMIOS) || defined(STREAM)
+ struct termios term;
+#endif
+#if defined(HAVE_TERMIO) || defined(HAVE_SYSV_TTYS)
+ struct termio term;
+#endif
+ int rtc = CVT_NONE;
+
+ timeout.tv_sec = 1;
+ timeout.tv_usec = 500000;
+
+ phase.tv_sec = 0;
+ phase.tv_usec = delay;
+
+ /*
+ * setup TTY (50 Baud, Read, 8Bit, No Hangup, 1 character IO)
+ */
+ if (TTY_GETATTR(fd, &term) == -1)
+ {
+ perror("tcgetattr");
+ exit(1);
+ }
+
+ memset(term.c_cc, 0, sizeof(term.c_cc));
+ term.c_cc[VMIN] = 1;
+#if defined(SYS_FREEBSD)
+ term.c_cflag = CS8|CREAD|CLOCAL|PARENB;
+ term.c_iflag = 0;
+#else
+ term.c_cflag = B50|CS8|CREAD|CLOCAL|PARENB;
+ term.c_iflag = IGNPAR;
+#endif
+ term.c_oflag = 0;
+ term.c_lflag = 0;
+
+#if defined(SYS_FREEBSD)
+ if (cfsetspeed(&term, B50) == -1)
+ perror("cfsetspeed");
+#endif
+ if (TTY_SETATTR(fd, &term) == -1)
+ {
+ perror("tcsetattr");
+ exit(1);
+ }
+
+ /*
+ * loose terminal if in daemon operation
+ */
+ if (!interactive)
+ detach();
+
+ /*
+ * get syslog() initialized
+ */
+#ifdef LOG_DAEMON
+ openlog("dcfd", LOG_PID, LOG_DAEMON);
+#else
+ openlog("dcfd", LOG_PID);
+#endif
+
+ /*
+ * setup periodic operations (state control / frequency control)
+ */
+#ifdef SV_ONSTACK
+ {
+ struct sigvec vec;
+
+ vec.sv_handler = tick;
+ vec.sv_mask = 0;
+ vec.sv_flags = 0;
+
+ if (sigvec(SIGALRM, &vec, (struct sigvec *)0) == -1)
+ {
+ syslog(LOG_ERR, "sigvec(SIGALRM): %m");
+ exit(1);
+ }
+ }
+#else
+ (void) signal(SIGALRM, tick);
+#endif
+
+#ifdef ITIMER_REAL
+ {
+ struct itimerval it;
+
+ it.it_interval.tv_sec = 1<<ADJINTERVAL;
+ it.it_interval.tv_usec = 0;
+ it.it_value.tv_sec = 1<<ADJINTERVAL;
+ it.it_value.tv_usec = 0;
+
+ if (setitimer(ITIMER_REAL, &it, (struct itimerval *)0) == -1)
+ {
+ syslog(LOG_ERR, "setitimer: %m");
+ exit(1);
+ }
+ }
+#else
+ (void) alarm(1<<ADJINTERVAL);
+#endif
+#if defined(SYS_FREEBSD) && defined(CONRAD)
+ if (fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) & ~O_NONBLOCK) == -1)
+ perror("F_SETFL");
+
+ {
+ int i;
+
+ if (ioctl(fd, TIOCMGET, &i) == -1)
+ perror("TIOCMGET");
+ i |= TIOCM_DTR;
+ i &= ~TIOCM_RTS;
+ if (ioctl(fd, TIOCMSET, &i) == -1)
+ perror("TIOCMSET");
+ }
+#endif
+#if defined(SYS_FREEBSD) && defined(BOEDER)
+ if (fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) & ~O_NONBLOCK) == -1)
+ perror("F_SETFL");
+
+ if (ioctl(fd, TIOCCDTR, 0) == -1)
+ perror("TIOCCDTR");
+#endif
+
+ PRINTF(" DCF77 monitor - Copyright 1993,1994, Frank Kardel\n\n");
+ syslog(LOG_NOTICE, "Starting on %s", file);
+
+ pbuf[60] = '\0';
+ for ( i = 0; i < 60; i++)
+ pbuf[i] = '.';
+
+ read_drift(drift_file);
+
+ /*
+ * what time is it now (for interval measurement)
+ */
+ gettimeofday(&tlast, 0L);
+ i = 0;
+ /*
+ * loop until input trouble ...
+ */
+ do
+ {
+ /*
+ * get an impulse
+ */
+ while ((rrc = read(fd, &c, 1)) == 1)
+ {
+ gettimeofday(&t, 0L);
+ tt = t;
+ timersub(&t, &tlast);
+
+ if (errs > LINES)
+ {
+ PRINTF(" %s", &"PTB private....RADMLSMin....PHour..PMDay..DayMonthYear....P\n"[offset]);
+ PRINTF(" %s", &"---------------RADMLS1248124P124812P1248121241248112481248P\n"[offset]);
+ errs = 0;
+ }
+
+ /*
+ * timeout -> possible minute mark -> interpretation
+ */
+ if (timercmp(&t, &timeout, >))
+ {
+ PRINTF("%c %.*s ", pat[i % (sizeof(pat)-1)], 59 - offset, &pbuf[offset]);
+
+ if ((rtc = cvt_rawdcf(buf, i, &clock)) != CVT_OK)
+ {
+ /*
+ * this data was bad - well - forget synchronisation for now
+ */
+ PRINTF("\n");
+ if (sync_state == SYNC)
+ {
+ sync_state = NO_SYNC;
+ syslog(LOG_INFO, "DCF77 reception lost (bad data)");
+ }
+ errs++;
+ }
+ else
+ if (trace)
+ {
+ PRINTF("\r %.*s ", 59 - offset, &buf[offset]);
+ }
+
+
+ buf[0] = c;
+
+ /*
+ * collect first character
+ */
+ if (((c^0xFF)+1) & (c^0xFF))
+ pbuf[0] = '?';
+ else
+ pbuf[0] = type(c) ? '#' : '-';
+
+ for ( i = 1; i < 60; i++)
+ pbuf[i] = '.';
+
+ i = 0;
+ }
+ else
+ {
+ /*
+ * collect character
+ */
+ buf[i] = c;
+
+ /*
+ * initial guess (usually correct)
+ */
+ if (((c^0xFF)+1) & (c^0xFF))
+ pbuf[i] = '?';
+ else
+ pbuf[i] = type(c) ? '#' : '-';
+
+ PRINTF("%c %.*s ", pat[i % (sizeof(pat)-1)], 59 - offset, &pbuf[offset]);
+ }
+
+ if (i == 0 && rtc == CVT_OK)
+ {
+ /*
+ * we got a good time code here - try to convert it to
+ * UTC
+ */
+ if ((utc_time = dcf_to_unixtime(&clock, &rtc)) == -1)
+ {
+ PRINTF("*** BAD CONVERSION\n");
+ }
+
+ if (utc_time != (last_utc_time + 60))
+ {
+ /*
+ * well, two successive sucessful telegrams are not 60 seconds
+ * apart
+ */
+ PRINTF("*** NO MINUTE INC\n");
+ if (sync_state == SYNC)
+ {
+ sync_state = NO_SYNC;
+ syslog(LOG_INFO, "DCF77 reception lost (data mismatch)");
+ }
+ errs++;
+ rtc = CVT_FAIL|CVT_BADTIME|CVT_BADDATE;
+ }
+ else
+ usecerror = 0;
+
+ last_utc_time = utc_time;
+ }
+
+ if (rtc == CVT_OK)
+ {
+ if (i == 0)
+ {
+ /*
+ * valid time code - determine offset and
+ * note regained reception
+ */
+ last_sync = ticks;
+ if (sync_state == NO_SYNC)
+ {
+ syslog(LOG_INFO, "receiving DCF77");
+ }
+ else
+ {
+ /*
+ * we had at least one minute SYNC - thus
+ * last error is valid
+ */
+#if defined(BOEDER)
+ if (abs(utc_time - tt.tv_sec) > 10)
+ {
+ time_offset.tv_sec = utc_time - tt.tv_sec;
+ time_offset.tv_usec = 0;
+ }
+ else
+ {
+ time_offset.tv_sec = lasterror / 1000000;
+ time_offset.tv_usec = lasterror % 1000000;
+ }
+#else
+ time_offset.tv_sec = lasterror / 1000000;
+ time_offset.tv_usec = lasterror % 1000000;
+#endif
+ adjust_clock(&time_offset, drift_file, utc_time);
+ }
+ sync_state = SYNC;
+ }
+
+ time_offset.tv_sec = utc_time + i;
+ time_offset.tv_usec = 0;
+
+ timeradd(&time_offset, &phase);
+
+ usecerror += (time_offset.tv_sec - tt.tv_sec) * 1000000 + time_offset.tv_usec
+ -tt.tv_usec;
+
+ /*
+ * output interpreted DCF77 data
+ */
+ PRINTF(offsets ? "%s, %2d:%02d:%02d, %d.%02d.%02d, <%s%s%s%s> (%c%d.%06ds)" :
+ "%s, %2d:%02d:%02d, %d.%02d.%02d, <%s%s%s%s>",
+ wday[clock.wday],
+ clock.hour, clock.minute, i, clock.day, clock.month,
+ clock.year,
+ (clock.flags & DCFB_ALTERNATE) ? "R" : "_",
+ (clock.flags & DCFB_ANNOUNCE) ? "A" : "_",
+ (clock.flags & DCFB_DST) ? "D" : "_",
+ (clock.flags & DCFB_LEAP) ? "L" : "_",
+ (lasterror < 0) ? '-' : '+', abs(lasterror) / 1000000, abs(lasterror) % 1000000
+ );
+
+ if (trace && (i == 0))
+ {
+ PRINTF("\n");
+ errs++;
+ }
+ lasterror = usecerror / (i+1);
+ }
+ else
+ {
+ lasterror = 0; /* we cannot calculate phase errors on bad reception */
+ }
+
+ PRINTF("\r");
+
+ if (i < 60)
+ {
+ i++;
+ }
+
+ tlast = tt;
+
+ if (interactive)
+ fflush(stdout);
+ }
+ } while ((rrc == -1) && (errno == EINTR));
+
+ /*
+ * lost IO - sorry guys
+ */
+ syslog(LOG_ERR, "TERMINATING - cannot read from device %s (%m)", file);
+
+ (void)close(fd);
+ }
+
+ closelog();
+
+ return 0;
+}
diff --git a/usr.sbin/xntpd/parse/util/parsetest.c b/usr.sbin/xntpd/parse/util/parsetest.c
new file mode 100644
index 0000000..33f3d9c
--- /dev/null
+++ b/usr.sbin/xntpd/parse/util/parsetest.c
@@ -0,0 +1,275 @@
+/*
+ * /src/NTP/REPOSITORY/v3/parse/util/parsetest.c,v 3.14 1994/05/12 12:49:27 kardel Exp
+ *
+ * parsetest.c,v 3.14 1994/05/12 12:49:27 kardel Exp
+ *
+ * Copyright (c) 1989,1990,1991,1992,1993,1994
+ * Frank Kardel Friedrich-Alexander Universitaet Erlangen-Nuernberg
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Log: parsetest.c,v $
+ * Revision 1.1.1.4 1994/09/29 23:01:34 wollman
+ * xntp 3.4e from Dave Mills @ UDel
+ *
+ * Revision 3.14 1994/05/12 12:49:27 kardel
+ * printf fmt/arg cleanup
+ *
+ * Revision 3.14 1994/05/11 09:25:43 kardel
+ * 3.3r + printf fmt/arg fixes
+ *
+ * Revision 3.13 1994/02/20 13:04:46 kardel
+ * parse add/delete second support
+ *
+ * Revision 3.12 1994/02/02 17:45:51 kardel
+ * rcs ids fixed
+ *
+ */
+
+#ifndef STREAM
+ONLY STREAM OPERATION SUPPORTED
+#endif
+
+#define PARSESTREAM /* there is no other choice - TEST HACK */
+
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <sys/time.h>
+#include <sys/stream.h>
+#include <sys/stropts.h>
+#include <sys/errno.h>
+#include <fcntl.h>
+
+#define P(X) ()
+
+#include "ntp_fp.h"
+#ifdef USE_PROTOTYPES
+#include "ntp_stdlib.h"
+#endif
+#include "parse.h"
+
+static char *strstatus(buffer, state)
+ char *buffer;
+ unsigned LONG state;
+{
+ static struct bits
+ {
+ unsigned LONG bit;
+ char *name;
+ } flagstrings[] =
+ {
+ { PARSEB_ANNOUNCE, "DST SWITCH WARNING" },
+ { PARSEB_POWERUP, "NOT SYNCHRONIZED" },
+ { PARSEB_NOSYNC, "TIME CODE NOT CONFIRMED" },
+ { PARSEB_DST, "DST" },
+ { PARSEB_UTC, "UTC DISPLAY" },
+ { PARSEB_LEAPADD, "LEAP ADDITION WARNING" },
+ { PARSEB_LEAPDEL, "LEAP DELETION WARNING" },
+ { PARSEB_LEAPSECOND, "LEAP SECOND" },
+ { PARSEB_ALTERNATE,"ALTERNATE ANTENNA" },
+ { PARSEB_TIMECODE, "TIME CODE" },
+ { PARSEB_PPS, "PPS" },
+ { PARSEB_POSITION, "POSITION" },
+ { 0 }
+ };
+
+ static struct sbits
+ {
+ unsigned LONG bit;
+ char *name;
+ } sflagstrings[] =
+ {
+ { PARSEB_S_LEAP, "LEAP INDICATION" },
+ { PARSEB_S_PPS, "PPS SIGNAL" },
+ { PARSEB_S_ANTENNA, "ANTENNA" },
+ { PARSEB_S_POSITION, "POSITION" },
+ { 0 }
+ };
+ int i;
+
+ *buffer = '\0';
+
+ i = 0;
+ while (flagstrings[i].bit)
+ {
+ if (flagstrings[i].bit & state)
+ {
+ if (buffer[0])
+ strcat(buffer, "; ");
+ strcat(buffer, flagstrings[i].name);
+ }
+ i++;
+ }
+
+ if (state & (PARSEB_S_LEAP|PARSEB_S_ANTENNA|PARSEB_S_PPS|PARSEB_S_POSITION))
+ {
+ register char *s, *t;
+
+ if (buffer[0])
+ strcat(buffer, "; ");
+
+ strcat(buffer, "(");
+
+ t = s = buffer + strlen(buffer);
+
+ i = 0;
+ while (sflagstrings[i].bit)
+ {
+ if (sflagstrings[i].bit & state)
+ {
+ if (t != s)
+ {
+ strcpy(t, "; ");
+ t += 2;
+ }
+
+ strcpy(t, sflagstrings[i].name);
+ t += strlen(t);
+ }
+ i++;
+ }
+ strcpy(t, ")");
+ }
+ return buffer;
+}
+
+/*--------------------------------------------------
+ * convert a status flag field to a string
+ */
+static char *parsestatus(state, buffer)
+ unsigned LONG state;
+ char *buffer;
+{
+ static struct bits
+ {
+ unsigned LONG bit;
+ char *name;
+ } flagstrings[] =
+ {
+ { CVT_OK, "CONVERSION SUCCESSFUL" },
+ { CVT_NONE, "NO CONVERSION" },
+ { CVT_FAIL, "CONVERSION FAILED" },
+ { CVT_BADFMT, "ILLEGAL FORMAT" },
+ { CVT_BADDATE, "DATE ILLEGAL" },
+ { CVT_BADTIME, "TIME ILLEGAL" },
+ { 0 }
+ };
+ int i;
+
+ *buffer = '\0';
+
+ i = 0;
+ while (flagstrings[i].bit)
+ {
+ if (flagstrings[i].bit & state)
+ {
+ if (buffer[0])
+ strcat(buffer, "; ");
+ strcat(buffer, flagstrings[i].name);
+ }
+ i++;
+ }
+
+ return buffer;
+}
+
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ if (argc != 2)
+ {
+ fprintf(stderr,"usage: %s <parse-device>\n", argv[0]);
+ exit(1);
+ }
+ else
+ {
+ int fd;
+
+ fd = open(argv[1], O_RDWR);
+ if (fd == -1)
+ {
+ perror(argv[1]);
+ exit(1);
+ }
+ else
+ {
+ parsectl_t dct;
+ parsetime_t parsetime;
+ struct strioctl strioc;
+
+ printf("parsetest.c,v 3.11 1994/01/23 19:00:01 kardel Exp\n");
+
+ while (ioctl(fd, I_POP, 0) == 0)
+ ;
+
+ if (ioctl(fd, I_PUSH, "parse") == -1)
+ {
+ perror("ioctl(I_PUSH,\"parse\")");
+ exit(1);
+ }
+
+ strioc.ic_cmd = PARSEIOC_GETSTAT;
+ strioc.ic_timout = 0;
+ strioc.ic_dp = (char *)&dct;
+ strioc.ic_len = sizeof(parsectl_t);
+
+ if (ioctl(fd, I_STR, &strioc) == -1)
+ {
+ perror("ioctl(fd, I_STR(PARSEIOC_GETSTAT))");
+ exit(1);
+ }
+ printf("parse status: %04lx\n", (unsigned long)dct.parsestatus.flags);
+
+ dct.parsestatus.flags |= PARSE_STAT_FILTER;
+ strioc.ic_cmd = PARSEIOC_SETSTAT;
+
+ if (ioctl(fd, I_STR, &strioc) == -1)
+ {
+ perror("ioctl(fd, I_STR(PARSEIOC_SETSTAT))");
+ exit(1);
+ }
+ printf("PARSE clock FILTERMODE\n");
+
+ if (ioctl(fd, I_STR, &strioc) == -1)
+ {
+ perror("ioctl(fd, I_STR(PARSEIOC_GETSTAT))");
+ exit(1);
+ }
+ printf("parse status: %04lx\n", (unsigned long)dct.parsestatus.flags);
+
+ while (read(fd, &parsetime, sizeof(parsetime)) == sizeof(parsetime))
+ {
+ char tmp[200], tmp1[200], tmp2[60];
+
+ strncpy(tmp, asctime(localtime(&parsetime.parse_time.tv.tv_sec)), 30);
+ strncpy(tmp1,asctime(localtime(&parsetime.parse_stime.tv.tv_sec)), 30);
+ strncpy(tmp2,asctime(localtime(&parsetime.parse_ptime.tv.tv_sec)), 30);
+ tmp[24] = '\0';
+ tmp1[24] = '\0';
+ tmp2[24] = '\0';
+
+ printf("%s (+%06ldus) %s PPS: %s (+%06ldus), ", tmp1, (long int)parsetime.parse_stime.tv.tv_usec, tmp, tmp2,
+ (long int)parsetime.parse_ptime.tv.tv_usec);
+
+ strstatus(tmp, parsetime.parse_state);
+ printf("state: 0x%lx (%s) error: %ldus, dispersion: %ldus, Status: 0x%lx (%s)\n",
+ (unsigned long)parsetime.parse_state,
+ tmp,
+ (long)parsetime.parse_usecerror,
+ (long)parsetime.parse_usecdisp,
+ (unsigned long)parsetime.parse_status,
+ parsestatus(parsetime.parse_status, tmp1));
+ }
+
+ close(fd);
+ }
+ }
+ return 0;
+}
diff --git a/usr.sbin/xntpd/parse/util/testdcf.c b/usr.sbin/xntpd/parse/util/testdcf.c
new file mode 100644
index 0000000..219b7c3
--- /dev/null
+++ b/usr.sbin/xntpd/parse/util/testdcf.c
@@ -0,0 +1,485 @@
+/*
+ * /src/NTP/REPOSITORY/v3/parse/util/testdcf.c,v 3.13 1994/05/12 12:49:31 kardel Exp
+ *
+ * testdcf.c,v 3.13 1994/05/12 12:49:31 kardel Exp
+ *
+ * simple DCF77 100/200ms pulse test program (via 50Baud serial line)
+ *
+ * Copyright (c) 1993,1994
+ * Frank Kardel, Friedrich-Alexander Universitaet Erlangen-Nuernberg
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * This program may not be sold or used for profit without prior
+ * written consent of the author.
+ */
+
+#include <unistd.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <termios.h>
+#include <sys/types.h>
+#ifdef STREAM
+#include <sys/stream.h>
+#include <sys/stropts.h>
+#endif
+#include <sys/time.h>
+
+#include "ntp_stdlib.h"
+
+/*
+ * state flags
+ */
+#define DCFB_ANNOUNCE 0x0001 /* switch time zone warning (DST switch) */
+#define DCFB_DST 0x0002 /* DST in effect */
+#define DCFB_LEAP 0x0004 /* LEAP warning (1 hour prior to occurence) */
+#define DCFB_ALTERNATE 0x0008 /* alternate antenna used */
+
+struct clocktime /* clock time broken up from time code */
+{
+ long wday;
+ long day;
+ long month;
+ long year;
+ long hour;
+ long minute;
+ long second;
+ long usecond;
+ long utcoffset; /* in minutes */
+ long flags; /* current clock status */
+};
+
+typedef struct clocktime clocktime_t;
+
+#define TIMES10(_X_) (((_X_) << 3) + ((_X_) << 1))
+
+/*
+ * parser related return/error codes
+ */
+#define CVT_MASK 0x0000000F /* conversion exit code */
+#define CVT_NONE 0x00000001 /* format not applicable */
+#define CVT_FAIL 0x00000002 /* conversion failed - error code returned */
+#define CVT_OK 0x00000004 /* conversion succeeded */
+#define CVT_BADFMT 0x00000010 /* general format error - (unparsable) */
+
+/*
+ * DCF77 raw time code
+ *
+ * From "Zur Zeit", Physikalisch-Technische Bundesanstalt (PTB), Braunschweig
+ * und Berlin, Maerz 1989
+ *
+ * Timecode transmission:
+ * AM:
+ * time marks are send every second except for the second before the
+ * next minute mark
+ * time marks consist of a reduction of transmitter power to 25%
+ * of the nominal level
+ * the falling edge is the time indication (on time)
+ * time marks of a 100ms duration constitute a logical 0
+ * time marks of a 200ms duration constitute a logical 1
+ * FM:
+ * see the spec. (basically a (non-)inverted psuedo random phase shift)
+ *
+ * Encoding:
+ * Second Contents
+ * 0 - 10 AM: free, FM: 0
+ * 11 - 14 free
+ * 15 R - alternate antenna
+ * 16 A1 - expect zone change (1 hour before)
+ * 17 - 18 Z1,Z2 - time zone
+ * 0 0 illegal
+ * 0 1 MEZ (MET)
+ * 1 0 MESZ (MED, MET DST)
+ * 1 1 illegal
+ * 19 A2 - expect leap insertion/deletion (1 hour before)
+ * 20 S - start of time code (1)
+ * 21 - 24 M1 - BCD (lsb first) Minutes
+ * 25 - 27 M10 - BCD (lsb first) 10 Minutes
+ * 28 P1 - Minute Parity (even)
+ * 29 - 32 H1 - BCD (lsb first) Hours
+ * 33 - 34 H10 - BCD (lsb first) 10 Hours
+ * 35 P2 - Hour Parity (even)
+ * 36 - 39 D1 - BCD (lsb first) Days
+ * 40 - 41 D10 - BCD (lsb first) 10 Days
+ * 42 - 44 DW - BCD (lsb first) day of week (1: Monday -> 7: Sunday)
+ * 45 - 49 MO - BCD (lsb first) Month
+ * 50 MO0 - 10 Months
+ * 51 - 53 Y1 - BCD (lsb first) Years
+ * 54 - 57 Y10 - BCD (lsb first) 10 Years
+ * 58 P3 - Date Parity (even)
+ * 59 - usually missing (minute indication), except for leap insertion
+ */
+
+static struct rawdcfcode
+{
+ char offset; /* start bit */
+} rawdcfcode[] =
+{
+ { 0 }, { 15 }, { 16 }, { 17 }, { 19 }, { 20 }, { 21 }, { 25 }, { 28 }, { 29 },
+ { 33 }, { 35 }, { 36 }, { 40 }, { 42 }, { 45 }, { 49 }, { 50 }, { 54 }, { 58 }, { 59 }
+};
+
+#define DCF_M 0
+#define DCF_R 1
+#define DCF_A1 2
+#define DCF_Z 3
+#define DCF_A2 4
+#define DCF_S 5
+#define DCF_M1 6
+#define DCF_M10 7
+#define DCF_P1 8
+#define DCF_H1 9
+#define DCF_H10 10
+#define DCF_P2 11
+#define DCF_D1 12
+#define DCF_D10 13
+#define DCF_DW 14
+#define DCF_MO 15
+#define DCF_MO0 16
+#define DCF_Y1 17
+#define DCF_Y10 18
+#define DCF_P3 19
+
+static struct partab
+{
+ char offset; /* start bit of parity field */
+} partab[] =
+{
+ { 21 }, { 29 }, { 36 }, { 59 }
+};
+
+#define DCF_P_P1 0
+#define DCF_P_P2 1
+#define DCF_P_P3 2
+
+#define DCF_Z_MET 0x2
+#define DCF_Z_MED 0x1
+
+static unsigned long ext_bf(buf, idx)
+ register char *buf;
+ register int idx;
+{
+ register unsigned long sum = 0;
+ register int i, first;
+
+ first = rawdcfcode[idx].offset;
+
+ for (i = rawdcfcode[idx+1].offset - 1; i >= first; i--)
+ {
+ sum <<= 1;
+ sum |= (buf[i] != '-');
+ }
+ return sum;
+}
+
+static unsigned pcheck(buf, idx)
+ register char *buf;
+ register int idx;
+{
+ register int i,last;
+ register unsigned psum = 1;
+
+ last = partab[idx+1].offset;
+
+ for (i = partab[idx].offset; i < last; i++)
+ psum ^= (buf[i] != '-');
+
+ return psum;
+}
+
+static unsigned long convert_rawdcf(buffer, size, clock)
+ register unsigned char *buffer;
+ register int size;
+ register clocktime_t *clock;
+{
+ if (size < 57)
+ {
+ printf("%-30s", "*** INCOMPLETE");
+ return CVT_NONE;
+ }
+
+ /*
+ * check Start and Parity bits
+ */
+ if ((ext_bf(buffer, DCF_S) == 1) &&
+ pcheck(buffer, DCF_P_P1) &&
+ pcheck(buffer, DCF_P_P2) &&
+ pcheck(buffer, DCF_P_P3))
+ {
+ /*
+ * buffer OK
+ */
+
+ clock->flags = 0;
+ clock->usecond= 0;
+ clock->second = 0;
+ clock->minute = ext_bf(buffer, DCF_M10);
+ clock->minute = TIMES10(clock->minute) + ext_bf(buffer, DCF_M1);
+ clock->hour = ext_bf(buffer, DCF_H10);
+ clock->hour = TIMES10(clock->hour) + ext_bf(buffer, DCF_H1);
+ clock->day = ext_bf(buffer, DCF_D10);
+ clock->day = TIMES10(clock->day) + ext_bf(buffer, DCF_D1);
+ clock->month = ext_bf(buffer, DCF_MO0);
+ clock->month = TIMES10(clock->month) + ext_bf(buffer, DCF_MO);
+ clock->year = ext_bf(buffer, DCF_Y10);
+ clock->year = TIMES10(clock->year) + ext_bf(buffer, DCF_Y1);
+ clock->wday = ext_bf(buffer, DCF_DW);
+
+ switch (ext_bf(buffer, DCF_Z))
+ {
+ case DCF_Z_MET:
+ clock->utcoffset = -60;
+ break;
+
+ case DCF_Z_MED:
+ clock->flags |= DCFB_DST;
+ clock->utcoffset = -120;
+ break;
+
+ default:
+ printf("%-30s", "*** BAD TIME ZONE");
+ return CVT_FAIL|CVT_BADFMT;
+ }
+
+ if (ext_bf(buffer, DCF_A1))
+ clock->flags |= DCFB_ANNOUNCE;
+
+ if (ext_bf(buffer, DCF_A2))
+ clock->flags |= DCFB_LEAP;
+
+ if (ext_bf(buffer, DCF_R))
+ clock->flags |= DCFB_ALTERNATE;
+
+ return CVT_OK;
+ }
+ else
+ {
+ /*
+ * bad format - not for us
+ */
+ printf("%-30s", "*** BAD FORMAT (invalid/parity)");
+ return CVT_FAIL|CVT_BADFMT;
+ }
+}
+
+char type(c)
+unsigned char c;
+{
+ c ^= 0xFF;
+ return (c > 0xF);
+}
+
+static char *wday[8] =
+{
+ "??",
+ "Mo",
+ "Tu",
+ "We",
+ "Th",
+ "Fr",
+ "Sa",
+ "Su"
+};
+
+static char pat[] = "-\\|/";
+
+#define LINES (24-2) /* error lines after which the two headlines are repeated */
+
+int
+main(argc, argv)
+ int argc;
+ char **argv;
+{
+ if ((argc != 2) && (argc != 3))
+ {
+ fprintf(stderr, "usage: %s [-f|-t|-ft|-tf] <device>\n", argv[0]);
+ exit(1);
+ }
+ else
+ {
+ unsigned char c;
+ char *file;
+ int fd;
+ int offset = 15;
+ int trace = 0;
+ int errs = LINES+1;
+
+ /*
+ * SIMPLE(!) argument "parser"
+ */
+ if (argc == 3)
+ {
+ if (strcmp(argv[1], "-f") == 0)
+ offset = 0;
+ if (strcmp(argv[1], "-t") == 0)
+ trace = 1;
+ if ((strcmp(argv[1], "-ft") == 0) ||
+ (strcmp(argv[1], "-tf") == 0))
+ {
+ offset = 0;
+ trace = 1;
+ }
+ file = argv[2];
+ }
+ else
+ {
+ file = argv[1];
+ }
+
+ fd = open(file, O_RDONLY);
+ if (fd == -1)
+ {
+ perror(file);
+ exit(1);
+ }
+ else
+ {
+ int i;
+#ifdef TIOCM_RTS
+ int on = TIOCM_RTS;
+#endif
+ struct timeval t, tt, tlast;
+ char buf[61];
+ clocktime_t clock;
+ struct termios term;
+ int rtc = CVT_NONE;
+
+ if (tcgetattr(fd, &term) == -1)
+ {
+ perror("tcgetattr");
+ exit(1);
+ }
+
+ memset(term.c_cc, 0, sizeof(term.c_cc));
+ term.c_cc[VMIN] = 1;
+ term.c_cflag = B50|CS8|CREAD|CLOCAL|PARENB;
+ term.c_iflag = IGNPAR;
+ term.c_oflag = 0;
+ term.c_lflag = 0;
+
+ if (tcsetattr(fd, TCSANOW, &term) == -1)
+ {
+ perror("tcsetattr");
+ exit(1);
+ }
+
+#ifdef I_POP
+ while (ioctl(fd, I_POP, 0) == 0)
+ ;
+#endif
+#if defined(TIOCMBIC) && defined(TIOCM_RTS)
+ if (ioctl(fd, TIOCMBIC, (caddr_t)&on) == -1)
+ {
+ perror("TIOCM_RTS");
+ }
+#endif
+
+ printf(" DCF77 monitor - Copyright 1993, Frank Kardel\n\n");
+
+ clock.hour = 0;
+ clock.minute = 0;
+ clock.day = 0;
+ clock.wday = 0;
+ clock.month = 0;
+ clock.year = 0;
+ clock.flags = 0;
+ buf[60] = '\0';
+ for ( i = 0; i < 60; i++)
+ buf[i] = '.';
+
+ gettimeofday(&tlast, 0L);
+ i = 0;
+ while (read(fd, &c, 1) == 1)
+ {
+ gettimeofday(&t, 0L);
+ tt = t;
+ t.tv_sec -= tlast.tv_sec;
+ t.tv_usec -= tlast.tv_usec;
+ if (t.tv_usec < 0)
+ {
+ t.tv_usec += 1000000;
+ t.tv_sec -= 1;
+ }
+
+ if (errs > LINES)
+ {
+ printf(" %s", &"PTB private....RADMLSMin....PHour..PMDay..DayMonthYear....P\n"[offset]);
+ printf(" %s", &"---------------RADMLS1248124P124812P1248121241248112481248P\n"[offset]);
+ errs = 0;
+ }
+
+ if (t.tv_sec > 1 ||
+ (t.tv_sec == 1 &&
+ t.tv_usec > 500000))
+ {
+ printf("%c %.*s ", pat[i % (sizeof(pat)-1)], 59 - offset, &buf[offset]);
+
+ if ((rtc = convert_rawdcf(buf, i, &clock)) != CVT_OK)
+ {
+ printf("\n");
+ clock.hour = 0;
+ clock.minute = 0;
+ clock.day = 0;
+ clock.wday = 0;
+ clock.month = 0;
+ clock.year = 0;
+ clock.flags = 0;
+ errs++;
+ }
+
+ if (((c^0xFF)+1) & (c^0xFF))
+ buf[0] = '?';
+ else
+ buf[0] = type(c) ? '#' : '-';
+
+ for ( i = 1; i < 60; i++)
+ buf[i] = '.';
+
+ i = 0;
+ }
+ else
+ {
+ if (((c^0xFF)+1) & (c^0xFF))
+ buf[i] = '?';
+ else
+ buf[i] = type(c) ? '#' : '-';
+
+ printf("%c %.*s ", pat[i % (sizeof(pat)-1)], 59 - offset, &buf[offset]);
+ }
+
+ if (rtc == CVT_OK)
+ {
+ printf("%s, %2d:%02d:%02d, %d.%02d.%02d, <%s%s%s%s>",
+ wday[clock.wday],
+ (int)clock.hour, (int)clock.minute, (int)i, (int)clock.day, (int)clock.month,
+ (int)clock.year,
+ (clock.flags & DCFB_ALTERNATE) ? "R" : "_",
+ (clock.flags & DCFB_ANNOUNCE) ? "A" : "_",
+ (clock.flags & DCFB_DST) ? "D" : "_",
+ (clock.flags & DCFB_LEAP) ? "L" : "_"
+ );
+ if (trace && (i == 0))
+ {
+ printf("\n");
+ errs++;
+ }
+ }
+
+ printf("\r");
+
+ if (i < 60)
+ {
+ i++;
+ }
+
+ tlast = tt;
+
+ fflush(stdout);
+ }
+ close(fd);
+ }
+ }
+ return 0;
+}
diff --git a/usr.sbin/xntpd/refclocks/Dependencies b/usr.sbin/xntpd/refclocks/Dependencies
new file mode 100644
index 0000000..2829476
--- /dev/null
+++ b/usr.sbin/xntpd/refclocks/Dependencies
@@ -0,0 +1,30 @@
+Clock Defines Restrictions AddDef Kernel
+PPS PPS
+ PPSPPS /STREAM/ STREAM(ppsclock|ppsclocd)
+ PPSCLK /LD/||/STREAM/ LD||STREAM(tty_clock||tty_clk_streams)
+ PPSCD /STREAM/ STREAM(ppsclock|ppsclocd)
+LOCAL LOCAL_CLOCK
+PST PST
+ PSTCLK /LD/||/STREAM/ LD||STREAM(tty_clock||tty_clk_streams)
+ PSTPPS /PPSPPS/
+WWVB WWVB
+ WWVBCLK /LD/||/STREAM/ PPSPPS LD||STREAM(tty_clock||tty_clk_streams)
+ WWVBPPS /PPSPPS/
+CHU CHU /SUNOS4/ none
+PARSE PARSE /SYSV_TTYS/||/STREAM/||/TERMIOS/ PPS any||STREAM(parse||ppsclock||ppsclocd)
+ PARSEPPS /SYSV_TTYS/||/STREAM/||/TERMIOS/ PPS any||STREAM(parse||ppsclock||ppsclocd)
+ CLOCK_*
+MX4200 MX4200PPS /PPSPPS/
+AS2201 AS2201
+ AS2201PPS /PPSPPS/
+GOES GOES
+ GOESPPS /PPSPPS/
+GPSTM GPSTM
+ GPSTTMPPS /PPSPPS/
+OMEGA OMEGA
+ OMEGAPPS /PPSPPS/
+TPRO TPRO /SUNOS/
+LEITCH LEITCH
+ LEITCHPPS /PPSPPS/
+MSFEES MSFEESPPS /PPSPPS/
+IRIG IRIG /SUNOS/ BSDAUDIO
diff --git a/usr.sbin/xntpd/refclocks/README b/usr.sbin/xntpd/refclocks/README
new file mode 100644
index 0000000..b27b006
--- /dev/null
+++ b/usr.sbin/xntpd/refclocks/README
@@ -0,0 +1,4 @@
+This directory contains shell scripts that should allow an interactive
+refclock configuration.
+
+Frank Kardel
diff --git a/usr.sbin/xntpd/refclocks/check b/usr.sbin/xntpd/refclocks/check
new file mode 100755
index 0000000..d1e8b19
--- /dev/null
+++ b/usr.sbin/xntpd/refclocks/check
@@ -0,0 +1,2 @@
+#!/bin/sh
+`echo "$1" | awk '{ if ( '"$2"' ) { print ":"; } else { print "false"; } exit; }'`;
diff --git a/usr.sbin/xntpd/refclocks/echon b/usr.sbin/xntpd/refclocks/echon
new file mode 100755
index 0000000..8750ae8
--- /dev/null
+++ b/usr.sbin/xntpd/refclocks/echon
@@ -0,0 +1,2 @@
+#!/bin/sh
+echo -n "$@";
diff --git a/usr.sbin/xntpd/refclocks/query b/usr.sbin/xntpd/refclocks/query
new file mode 100755
index 0000000..07e11cc
--- /dev/null
+++ b/usr.sbin/xntpd/refclocks/query
@@ -0,0 +1,11 @@
+#!/bin/sh
+echon "$1 (y/n) [$2] ? "
+X=""
+read X
+if [ "$X" = "" ]; then
+ X="$2"
+fi
+case "$X" in
+ [yY]*) exit 0;;
+ *) exit 1;;
+esac
diff --git a/usr.sbin/xntpd/refclocks/rclk.AS2201 b/usr.sbin/xntpd/refclocks/rclk.AS2201
new file mode 100644
index 0000000..9af89af
--- /dev/null
+++ b/usr.sbin/xntpd/refclocks/rclk.AS2201
@@ -0,0 +1,29 @@
+#!/bin/sh -
+CMD="$1"
+shift;
+
+. refclocks/setup
+
+case "$CMD" in
+ info)
+ echo " AS2201 - Austron 2200A or 2201A GPS receiver"
+ ;;
+ check)
+ if check "$RCONFIG" '$0 ~ /AS2201/'; then
+ echo "AS2201 - Austron 2200A or 2201A GPS receiver"
+ fi
+ ;;
+ config)
+ if check "$REFCONF" '$0 ~ /AS2201/' ||
+ ( [ ! "$REFCONF" ] && query "Include Austron 2200A or 2201A GPS receiver (AS2201)" n); then
+ if check "$PPSFEATURES" '$0 ~ /CD/' &&
+ [ "$PPSOK" -eq 1 ] &&
+ (check "$REFCONF" '$0 ~ /AS2201PPS/' ||
+ ( [ ! "$REFCONF" ] && query " Use AS2201 for PPS" n)); then
+ echo "-DAS2201PPS" >> $RCONFIG
+ else
+ echo "-DAS2201" >> $RCONFIG
+ fi
+ fi
+ ;;
+esac
diff --git a/usr.sbin/xntpd/refclocks/rclk.CHU b/usr.sbin/xntpd/refclocks/rclk.CHU
new file mode 100644
index 0000000..fedd55c
--- /dev/null
+++ b/usr.sbin/xntpd/refclocks/rclk.CHU
@@ -0,0 +1,24 @@
+#!/bin/sh -
+CMD="$1"
+shift;
+
+. refclocks/setup
+
+if check "$DEFS" '$0 ~ /SYS_SUNOS4/'; then
+ case "$CMD" in
+ info)
+ echo " CHU - CHU via shortwave radio"
+ ;;
+ check)
+ if check "$RCONFIG" '$0 ~ /CHU/'; then
+ echo "CHU - CHU via shortwave radio"
+ fi
+ ;;
+ config)
+ if check "$REFCONF" '$0 ~ /CHU/' ||
+ ( [ ! "$REFCONF" ] && query "Include CHU via shortwave radio (CHU)" n); then
+ echo "-DCHU" >> $RCONFIG
+ fi
+ ;;
+esac
+fi
diff --git a/usr.sbin/xntpd/refclocks/rclk.DATUM b/usr.sbin/xntpd/refclocks/rclk.DATUM
new file mode 100644
index 0000000..3639e4a
--- /dev/null
+++ b/usr.sbin/xntpd/refclocks/rclk.DATUM
@@ -0,0 +1,22 @@
+#!/bin/sh -
+CMD="$1"
+shift;
+
+. refclocks/setup
+
+case "$CMD" in
+ info)
+ echo " DATUM - use Datum Programmable Time System"
+ ;;
+ check)
+ if check "$RCONFIG" '$0 ~ /DATUM/'; then
+ echo "DATUM - Datum Programmable Time System"
+ fi
+ ;;
+ config)
+ if check "$REFCONF" '$0 ~ /DATUM/' ||
+ ( [ ! "$REFCONF" ] && query "Include DATUM reference support (DATUM)" n); then
+ echo "-DDATUM" >> $RCONFIG
+ fi
+ ;;
+esac
diff --git a/usr.sbin/xntpd/refclocks/rclk.GOES b/usr.sbin/xntpd/refclocks/rclk.GOES
new file mode 100644
index 0000000..cb87c63
--- /dev/null
+++ b/usr.sbin/xntpd/refclocks/rclk.GOES
@@ -0,0 +1,32 @@
+#!/bin/sh -
+CMD="$1"
+shift;
+
+. refclocks/setup
+
+case "$CMD" in
+ info)
+ echo " GOES - Kinemetrics/TrueTime 468-DC GOES receiver"
+ ;;
+ check)
+ if check "$RCONFIG" '$0 ~ /GOES/'; then
+ echo "GOES - Kinemetrics/TrueTime 468-DC GOES receiver"
+ fi
+ ;;
+ config)
+ if check "$REFCONF" '$0 ~ /GOES/' ||
+ ( [ ! "$REFCONF" ] && query "Include Kinemetrics/TrueTime 468-DC GOES receiver (GOES)" n); then
+ if check "$PPSFEATURES" '$0 ~ /CD/'; then
+ if [ "$PPSOK" -eq 1 ] &&
+ (check "$REFCONF" '$0 ~ /GOESPPS/' ||
+ ( [ ! "$REFCONF" ] && query " Use GOES for PPS" n)); then
+ echo "-DGOESPPS" >> $RCONFIG
+ else
+ echo "-DGOES" >> $RCONFIG
+ fi
+ else
+ echo "-DGOES" >> $RCONFIG
+ fi
+ fi
+ ;;
+esac
diff --git a/usr.sbin/xntpd/refclocks/rclk.GPSTM b/usr.sbin/xntpd/refclocks/rclk.GPSTM
new file mode 100644
index 0000000..552747a
--- /dev/null
+++ b/usr.sbin/xntpd/refclocks/rclk.GPSTM
@@ -0,0 +1,33 @@
+#!/bin/sh -
+CMD="$1"
+shift;
+
+. refclocks/setup
+
+case "$CMD" in
+ info)
+ echo " GPSTM - Kinemetrics/TrueTime GPS-TM/TMD receiver"
+ ;;
+ check)
+ if check "$RCONFIG" '$0 ~ /GPSTM/'; then
+ echo "GPSTM - Kinemetrics/TrueTime GPS-TM/TMD receiver"
+ fi
+ ;;
+ config)
+ if check "$REFCONF" '$0 ~ /GPSTM/' ||
+ ( [ ! "$REFCONF" ] &&
+ query "Include Kinemetrics/TrueTime GPS-TM/TMD receiver (GPSTM)" n); then
+ if check "$PPSFEATURES" '$0 ~ /CD/'; then
+ if [ "$PPSOK" -eq 1 ] &&
+ (check "$REFCONF" '$0 ~ /GPSTMPPS/' ||
+ ( [ ! "$REFCONF" ] && query " Use GPSTM for PPS" n)); then
+ echo "-DGPSTMPPS" >> $RCONFIG
+ else
+ echo "-DGPSTM" >> $RCONFIG
+ fi
+ else
+ echo "-DGPSTM" >> $RCONFIG
+ fi
+ fi
+ ;;
+esac
diff --git a/usr.sbin/xntpd/refclocks/rclk.IRIG b/usr.sbin/xntpd/refclocks/rclk.IRIG
new file mode 100644
index 0000000..b480e90
--- /dev/null
+++ b/usr.sbin/xntpd/refclocks/rclk.IRIG
@@ -0,0 +1,24 @@
+#!/bin/sh -
+CMD="$1"
+shift;
+
+. refclocks/setup
+
+if check "$DEFS" '$0 ~ /SYS_SUNOS4/'; then
+ case "$CMD" in
+ info)
+ echo " IRIG - IRIG-B timecode timecode using the audio codec"
+ ;;
+ check)
+ if check "$RCONFIG" '$0 ~ /IRIG/'; then
+ echo "IRIG - IRIG-B timecode timecode using the audio codec"
+ fi
+ ;;
+ config)
+ if check "$REFCONF" '$0 ~ /IRIG/' ||
+ ( [ ! "$REFCONF" ] && query "Include IRIG-B timecode timecode using the audio codec (IRIG)" n); then
+ echo "-DIRIG" >> $RCONFIG
+ fi
+ ;;
+ esac
+fi
diff --git a/usr.sbin/xntpd/refclocks/rclk.LEITCH b/usr.sbin/xntpd/refclocks/rclk.LEITCH
new file mode 100644
index 0000000..1bd5fb8
--- /dev/null
+++ b/usr.sbin/xntpd/refclocks/rclk.LEITCH
@@ -0,0 +1,29 @@
+#!/bin/sh -
+CMD="$1"
+shift;
+
+. refclocks/setup
+
+case "$CMD" in
+ info)
+ echo " LEITCH - Leitch CSD 5300 Master Clock System Driver"
+ ;;
+ check)
+ if check "$RCONFIG" '$0 ~ /LEITCH/'; then
+ echo "LEITCH - Leitch CSD 5300 Master Clock System Driver"
+ fi
+ ;;
+ config)
+ if check "$REFCONF" '$0 ~ /LEITCH/' ||
+ ( [ ! "$REFCONF" ] && query "Include Leitch CSD 5300 Master Clock System Driver (LEITCH)" n); then
+ if check "$PPSFEATURES" '$0 ~ /CD/' &&
+ [ "$PPSOK" -eq 1 ] &&
+ (check "$REFCONF" '$0 ~ /LEITCHPPS/' ||
+ ( [ ! "$REFCONF" ] && query " Use LEITCH for PPS" n)); then
+ echo "-DLEITCHPPS" >> $RCONFIG
+ else
+ echo "-DLEITCH" >> $RCONFIG
+ fi
+ fi
+ ;;
+esac
diff --git a/usr.sbin/xntpd/refclocks/rclk.LOCAL_CLOCK b/usr.sbin/xntpd/refclocks/rclk.LOCAL_CLOCK
new file mode 100644
index 0000000..e2f9e1a
--- /dev/null
+++ b/usr.sbin/xntpd/refclocks/rclk.LOCAL_CLOCK
@@ -0,0 +1,22 @@
+#!/bin/sh -
+CMD="$1"
+shift;
+
+. refclocks/setup
+
+case "$CMD" in
+ info)
+ echo " LOCAL_CLOCK - use local clock as reference"
+ ;;
+ check)
+ if check "$RCONFIG" '$0 ~ /LOCAL_CLOCK/'; then
+ echo "LOCAL_CLOCK - local clock"
+ fi
+ ;;
+ config)
+ if check "$REFCONF" '$0 ~ /LOCAL_CLOCK/' ||
+ ( [ ! "$REFCONF" ] && query "Include local clock reference support (LOCAL_CLOCK)" y); then
+ echo "-DLOCAL_CLOCK" >> $RCONFIG
+ fi
+ ;;
+esac
diff --git a/usr.sbin/xntpd/refclocks/rclk.MOTO b/usr.sbin/xntpd/refclocks/rclk.MOTO
new file mode 100644
index 0000000..d6e11f8
--- /dev/null
+++ b/usr.sbin/xntpd/refclocks/rclk.MOTO
@@ -0,0 +1,29 @@
+#!/bin/sh -
+CMD="$1"
+shift;
+
+. refclocks/setup
+
+case "$CMD" in
+ info)
+ echo " MOTO - Motorola GPS clock"
+ ;;
+ check)
+ if check "$RCONFIG" '$0 ~ /MOTO/'; then
+ echo "MOTO - Motorola GPS clock"
+ fi
+ ;;
+ config)
+ if check "$REFCONF" '$0 ~ /MOTO/' ||
+ ( [ ! "$REFCONF" ] && query "Include Motorola clock (MOTO)" n); then
+ if check "$PPSFEATURES" '$0 ~ /CD/' &&
+ [ "$PPSOK" -eq 1 ] &&
+ (check "$REFCONF" '$0 ~ /MOTOPPS/' ||
+ ( [ ! "$REFCONF" ] && query " Use Motorola for PPS" n)); then
+ echo "-DMOTOPPS" >> $RCONFIG
+ else
+ echo "-DMOTO" >> $RCONFIG
+ fi
+ fi
+ ;;
+esac
diff --git a/usr.sbin/xntpd/refclocks/rclk.MSFEES b/usr.sbin/xntpd/refclocks/rclk.MSFEES
new file mode 100644
index 0000000..aeb988a
--- /dev/null
+++ b/usr.sbin/xntpd/refclocks/rclk.MSFEES
@@ -0,0 +1,26 @@
+#!/bin/sh -
+CMD="$1"
+shift;
+
+. refclocks/setup
+
+case "$CMD" in
+ info)
+ if check "$DEFS" '$0 ~ /STREAM/'; then
+ echo " MSFEESPPS - EES M201 MSF receiver"
+ fi
+ ;;
+ check)
+ if check "$RCONFIG" '$0 ~ /MSFEESPPS/'; then
+ echo "MSFEESPPS - EES M201 MSF receiver"
+ fi
+ ;;
+ config)
+ if check "$PPSFEATURES" '$0 ~ /CD/' &&
+ [ "$PPSOK" -eq 1 ] &&
+ (check "$REFCONF" '$0 ~ /MSFEESPPS/' ||
+ ( [ ! "$REFCONF" ] && query "Include EES M201 MSF receiver (MSFEESPPS)" n)); then
+ echo "-DMSFEESPPS" >> $RCONFIG
+ fi
+ ;;
+esac
diff --git a/usr.sbin/xntpd/refclocks/rclk.MX4200 b/usr.sbin/xntpd/refclocks/rclk.MX4200
new file mode 100644
index 0000000..5d10712
--- /dev/null
+++ b/usr.sbin/xntpd/refclocks/rclk.MX4200
@@ -0,0 +1,27 @@
+#!/bin/sh -
+CMD="$1"
+shift;
+
+. refclocks/setup
+
+case "$CMD" in
+ info)
+ if check "$DEFS" '$0 ~ /STREAM/'; then
+ echo " MX4200 - Magnavox 4200 GPS receiver"
+ fi
+ ;;
+ check)
+ if check "$RCONFIG" '$0 ~ /MX4200PPS/'; then
+ echo "MX4200 - Magnavox 4200 GPS receiver"
+ fi
+ ;;
+ config)
+ if check "$PPSFEATURES" '$0 ~ /CD/' &&
+ [ "$PPSOK" -eq 1 ] &&
+ (check "$REFCONF" '$0 ~ /MX4200PPS/' ||
+ ( [ ! "$REFCONF" ] && query "Include Magnavox 4200 GPS receiver (MX4200PPS)" n)); then
+ echo "-DMX4200PPS" >> $RCONFIG
+ fi
+ ;;
+esac
+
diff --git a/usr.sbin/xntpd/refclocks/rclk.NMEA b/usr.sbin/xntpd/refclocks/rclk.NMEA
new file mode 100644
index 0000000..f505465
--- /dev/null
+++ b/usr.sbin/xntpd/refclocks/rclk.NMEA
@@ -0,0 +1,23 @@
+case "$CMD" in
+ info)
+ echo " NMEA - NMEA GPS station clock"
+ ;;
+ check)
+ if check "$RCONFIG" '$0 ~ /NMEA/'; then
+ echo "NMEA - NMEA GPS station clock"
+ fi
+ ;;
+ config)
+ if check "$REFCONF" '$0 ~ /NMEA/' ||
+ ( [ ! "$REFCONF" ] && query "Include NMEA GPS station clock (NMEA)" n); then
+ if check "$PPSFEATURES" '$0 ~ /CD/' &&
+ [ "$PPSOK" -eq 1 ] &&
+ (check "$REFCONF" '$0 ~ /NMEAPPS/' ||
+ ( [ ! "$REFCONF" ] && query " Use NMEA for PPS" n)); then
+ echo "-DNMEAPPS" >> $RCONFIG
+ else
+ echo "-DNMEA" >> $RCONFIG
+ fi
+ fi
+ ;;
+esac
diff --git a/usr.sbin/xntpd/refclocks/rclk.OMEGA b/usr.sbin/xntpd/refclocks/rclk.OMEGA
new file mode 100644
index 0000000..62094e4
--- /dev/null
+++ b/usr.sbin/xntpd/refclocks/rclk.OMEGA
@@ -0,0 +1,29 @@
+#!/bin/sh -
+CMD="$1"
+shift;
+
+. refclocks/setup
+
+case "$CMD" in
+ info)
+ echo " OMEGA - Kinemetrics/TrueTime OM-DC OMEGA receiver"
+ ;;
+ check)
+ if check "$RCONFIG" '$0 ~ /OMEGA/'; then
+ echo "OMEGA - Kinemetrics/TrueTime OM-DC OMEGA receiver"
+ fi
+ ;;
+ config)
+ if check "$REFCONF" '$0 ~ /OMEGA/' ||
+ ( [ ! "$REFCONF" ] && query "Include Kinemetrics/TrueTime OM-DC OMEGA receiver (OMEGA)" n); then
+ if check "$PPSFEATURES" '$0 ~ /CD/' &&
+ [ "$PPSOK" -eq 1 ] &&
+ (check "$REFCONF" '$0 ~ /OMEGAPPS/' ||
+ ( [ ! "$REFCONF" ] && query " Use OMEGA for PPS" n)); then
+ echo "-DOMEGAPPS" >> $RCONFIG
+ else
+ echo "-DOMEGA" >> $RCONFIG
+ fi
+ fi
+ ;;
+esac
diff --git a/usr.sbin/xntpd/refclocks/rclk.PARSE b/usr.sbin/xntpd/refclocks/rclk.PARSE
new file mode 100644
index 0000000..5211ccc
--- /dev/null
+++ b/usr.sbin/xntpd/refclocks/rclk.PARSE
@@ -0,0 +1,55 @@
+#!/bin/sh
+CMD="$1"
+shift;
+
+. refclocks/setup
+
+if check "$DEFS" '$0 ~ /HAVE_SYSV_TTYS|STREAM|HAVE_TERMIOS/'; then
+ case "$CMD" in
+ info)
+ echo " PARSE - GENERIC refence clock driver"
+ echo " DCF77:"
+ echo " Meinberg DCF U/A 31, PZF 535"
+ echo " Schmid DCF77 receiver"
+ echo " ELV DCF7000"
+ echo " Raw DCF77 signal (100/200ms pulses)"
+ echo " GPS:"
+ echo " Meinberg GPS 166"
+ echo " Trimble GPS (TAIP Protocol)"
+ echo " Trimble GPS (TSIP Protocol)"
+ ;;
+ check)
+ if check "$RCONFIG" '$0 ~ /PARSE/ && $0 ~ /CLOCK/'; then
+ echon "PARSE - GENERIC"
+ if check "$RCONFIG" '$0 ~ /PARSEPPS/'; then echon "/PPS"; fi
+ if check "$RCONFIG" '$0 ~ /CLOCK_MEINBERG/'; then echon ",MEINBERG"; fi
+ if check "$RCONFIG" '$0 ~ /CLOCK_SCHMID/'; then echon ",SCHMID"; fi
+ if check "$RCONFIG" '$0 ~ /CLOCK_DCF7000/'; then echon ",DCF7000"; fi
+ if check "$RCONFIG" '$0 ~ /CLOCK_TRIMTAIP/'; then echon ",Trimble GPS (TAIP)"; fi
+ if check "$RCONFIG" '$0 ~ /CLOCK_TRIMTSIP/'; then echon ",Trimble GPS (TSIP)"; fi
+ if check "$RCONFIG" '$0 ~ /CLOCK_RAWDCF/'; then echon ",Raw DCF77 pulses"; fi
+ echo
+ fi
+ ;;
+ config)
+ if check "$REFCONF" '$0 ~ /PARSE/' ||
+ ( [ ! "$REFCONF" ] && query "Include PARSE generic driver support" n); then
+ if check "$REFCONF" '$0 ~ /PARSEPPS/' ||
+ ( [ ! "$REFCONF" ] && query " Include support for PPS simulation" y); then echo "-DPARSEPPS" >> $RCONFIG; else echo "-DPARSE" >> $RCONFIG; fi
+ if check "$REFCONF" '$0 ~ /MEINBERG/' ||
+ ( [ ! "$REFCONF" ] && query " Include support for Meinberg clocks" y); then echo "-DCLOCK_MEINBERG" >> $RCONFIG; fi
+ if check "$REFCONF" '$0 ~ /SCHMID/' ||
+ ( [ ! "$REFCONF" ] && query " Include support for Schmid DCF77 clock" y); then echo "-DCLOCK_SCHMID" >> $RCONFIG; fi
+ if check "$REFCONF" '$0 ~ /DCF7000|ELV/' ||
+ ( [ ! "$REFCONF" ] && query " Include support for ELV/DCF7000 clock" y); then echo "-DCLOCK_DCF7000" >> $RCONFIG; fi
+ if check "$REFCONF" '$0 ~ /TRIMTAIP/' ||
+ ( [ ! "$REFCONF" ] && query " Include support for Trimble GPS receiver (TAIP Protocol)" y); then echo "-DCLOCK_TRIMTAIP" >> $RCONFIG; fi
+ if check "$REFCONF" '$0 ~ /TRIMTSIP/' ||
+ ( [ ! "$REFCONF" ] && query " Include support for Trimble GPS receiver (TSIP Protocol)" y); then echo "-DCLOCK_TRIMTSIP" >> $RCONFIG; fi
+ if check "$REFCONF" '$0 ~ /RAWDCF/' ||
+ ( [ ! "$REFCONF" ] && query " Include support for raw DCF77 time code" y); then echo "-DCLOCK_RAWDCF" >> $RCONFIG; fi
+ fi
+ ;;
+ esac
+fi
+
diff --git a/usr.sbin/xntpd/refclocks/rclk.PST b/usr.sbin/xntpd/refclocks/rclk.PST
new file mode 100644
index 0000000..4f93c8e
--- /dev/null
+++ b/usr.sbin/xntpd/refclocks/rclk.PST
@@ -0,0 +1,37 @@
+#!/bin/sh -
+CMD="$1"
+shift;
+
+. refclocks/setup
+
+case "$CMD" in
+ info)
+ echo " PST - PST/Traconex 1020 WWV/H receiver"
+ ;;
+ check)
+ if check "$RCONFIG" '$0 ~ /PST/'; then
+ echo "PST - PST/Traconex 1020 WWV/H receiver"
+ fi
+ ;;
+ config)
+ if check "$REFCONF" '$0 ~ /PST/' ||
+ ( [ ! "$REFCONF" ] && query "Include PST/Traconex 1020 WWV/H receiver (PST)" n); then
+ _PST=0
+ if check "$PPSFEATURES" '$0 ~ /CLK/'; then
+ if (check "$REFCONF" '$0 ~ /PSTCLK/' ||
+ ( [ ! "$REFCONF" ] && query " Support PPS via clk" y)); then
+ echo " -DPSTCLK" >> $RCONFIG
+ _PST=1
+ fi
+ fi
+ if check "$PPSFEATURES" '$0 ~ /CD/' &&
+ [ "$PPSOK" -eq 1 ] &&
+ (check "$REFCONF" '$0 ~ /PSTPPS/' ||
+ ( [ ! "$REFCONF" ] && query " Use PST for PPS" n)); then
+ echo " -DPSTPPS" >> $RCONFIG
+ else
+ [ "$_PST" -eq 0 ] && echo "-DPST" >> $RCONFIG
+ fi
+ fi
+ ;;
+esac
diff --git a/usr.sbin/xntpd/refclocks/rclk.TPRO b/usr.sbin/xntpd/refclocks/rclk.TPRO
new file mode 100644
index 0000000..74e5545
--- /dev/null
+++ b/usr.sbin/xntpd/refclocks/rclk.TPRO
@@ -0,0 +1,24 @@
+#!/bin/sh -
+CMD="$1"
+shift;
+
+. refclocks/setup
+
+if check "$DEFS" '$0 ~ /SYS_SUNOS/'; then
+ case "$CMD" in
+ info)
+ echo " TPRO - KSI/Odetics TPRO-S IRIG-B timecode reader"
+ ;;
+ check)
+ if check "$RCONFIG" '$0 ~ /TPRO/'; then
+ echo "TPRO - KSI/Odetics TPRO-S IRIG-B timecode reader"
+ fi
+ ;;
+ config)
+ if check "$REFCONF" '$0 ~ /TPRO/' ||
+ ( [ ! "$REFCONF" ] && query "Include KSI/Odetics TPRO-S IRIG-B timecode reader (TPRO)" n); then
+ echo "-DTPRO" >> $RCONFIG
+ fi
+ ;;
+ esac
+fi
diff --git a/usr.sbin/xntpd/refclocks/rclk.TRAK b/usr.sbin/xntpd/refclocks/rclk.TRAK
new file mode 100644
index 0000000..188ffd4
--- /dev/null
+++ b/usr.sbin/xntpd/refclocks/rclk.TRAK
@@ -0,0 +1,29 @@
+#!/bin/sh -
+CMD="$1"
+shift;
+
+. refclocks/setup
+
+case "$CMD" in
+ info)
+ echo " TRAK - TRAK 8810 GPS station clock"
+ ;;
+ check)
+ if check "$RCONFIG" '$0 ~ /TRAK/'; then
+ echo "TRAK - TRAK 8810 GPS station clock"
+ fi
+ ;;
+ config)
+ if check "$REFCONF" '$0 ~ /TRAK/' ||
+ ( [ ! "$REFCONF" ] && query "Include TRAK 8810 GPS station clock (TRAK)" n); then
+ if check "$PPSFEATURES" '$0 ~ /CD/' &&
+ [ "$PPSOK" -eq 1 ] &&
+ (check "$REFCONF" '$0 ~ /TRAKPPS/' ||
+ ( [ ! "$REFCONF" ] && query " Use TRAK for PPS" n)); then
+ echo "-DTRAKPPS" >> $RCONFIG
+ else
+ echo "-DTRAK" >> $RCONFIG
+ fi
+ fi
+ ;;
+esac
diff --git a/usr.sbin/xntpd/refclocks/rclk.WWVB b/usr.sbin/xntpd/refclocks/rclk.WWVB
new file mode 100644
index 0000000..2801ac7
--- /dev/null
+++ b/usr.sbin/xntpd/refclocks/rclk.WWVB
@@ -0,0 +1,38 @@
+#!/bin/sh -
+CMD="$1"
+shift;
+
+. refclocks/setup
+
+case "$CMD" in
+ info)
+ echo " WWVB - Spectracom 8170 or Netclock/2 WWVB receiver"
+ ;;
+ check)
+ if check "$RCONFIG" '$0 ~ /WWVB/'; then
+ echo "WWVB - Spectracom 8170 or Netclock/2 WWVB receiver"
+ fi
+ ;;
+ config)
+ if check "$REFCONF" '$0 ~ /WWVB/' ||
+ ( [ ! "$REFCONF" ] && query "Include Spectracom 8170 or Netclock/2 WWVB receiver (WWVB)" n); then
+ _WWV=0
+ if check "$PPSFEATURES" '$0 ~ /CLK/'; then
+ if check "$REFCONF" '$0 ~ /WWVBCLK/' ||
+ ( [ ! "$REFCONF" ] && query " Support PPS via clk" y); then
+ echo " -DWWVBCLK" >> $RCONFIG
+ _WWV=1
+ fi
+ fi
+ if check "$PPSFEATURES" '$0 ~ /CD/'; then
+ if [ "$PPSOK" -eq 1 ] &&
+ (check "$REFCONF" '$0 ~ /WWVBPPS/' ||
+ ( [ ! "$REFCONF" ] && query " Use WWVB for PPS" n)); then
+ echo " -DWWVBPPS" >> $RCONFIG
+ fi
+ else
+ [ "$_WWV" -eq 0 ] && echo "-DWWVB" >> $RCONFIG
+ fi
+ fi
+ ;;
+esac
diff --git a/usr.sbin/xntpd/refclocks/rconfig b/usr.sbin/xntpd/refclocks/rconfig
new file mode 100644
index 0000000..e49c559
--- /dev/null
+++ b/usr.sbin/xntpd/refclocks/rconfig
@@ -0,0 +1,130 @@
+#!/bin/sh -
+#
+# Refclock configuration script
+#
+# batch configuration options (optional arg 1)
+# pps related
+# PPS - general PPS support
+# CLK - CLK line discipline or streams module
+# CD - ppsclock or ppsclockd streams module
+# LINE - dedicated line
+PATH=refclocks:${PATH}
+export PATH
+RCONFIG=rconf
+DLOCAL=dlocal
+REFCONF=${1-""}
+
+. refclocks/setup
+
+rcfg="`echo refclocks/rclk.*`"
+
+if [ "$rcfg" = "refclocks/rclk.*" ]; then
+ echo "no reference clock configuration information available"
+else
+ config="`egrep '^[ ]*CLOCKDEFS[ ]*=' Config.local | sed 's/\([^#]*\)#.*$/\1/g; s/[ ]*CLOCKDEFS[ ]*=//g; s/-D//g; s/[ ][ ]*/ /g; s/^ *//g; s/ *$//g;'`"
+ DEFS="`egrep '^[ ]*DEFS[ ]*=' Config | sed 's/\([^#]*\)#.*$/\1/g; s/[ ]*DEFS[ ]*=//g; s/-D//g; s/[ ][ ]*/ /g; s/^ *//g; s/ *$//g;'`"
+ DEFSLOCAL="`egrep '^[ ]*DEFS_LOCAL[ ]*=' Config.local | sed 's/\([^#]*\)#.*$/\1/g; s/[ ]*DEFS_LOCAL[ ]*=//g; s/-D//g; s/[ ][ ]*/ /g; s/^ *//g; s/ *$//g;'`"
+ if [ ! "$REFCONF" ]; then
+ echo
+ echo "Current configuration"
+ echo
+ if check "$DEFSLOCAL" '$0 ~ /MCAST/'; then
+ echo "==> MULTICAST SUPPORT (if available)"
+ echo
+ fi
+ for i in $rcfg
+ do
+ sh $i check "$config" "" "" "$DEFS" "$REFCONF"
+ done
+ echo
+ fi
+ if [ "$REFCONF" ] || query "Change Configuration" n; then
+ if [ ! "$REFCONF" ]; then
+ echo
+ echo 'Available reference clock drivers'
+ for i in $rcfg
+ do
+ sh $i info "" "" "" "$DEFS" "$REFCONF"
+ done
+ echo
+ fi
+ :>"$RCONFIG"
+ PPS=""
+ PPSFEATURES=""
+ PPSOK=0
+ if check "$REFCONF" '$0 ~ /PLL/' ||
+ ( [ ! "$REFCONF" ] && query "Include support for Kernel PLL" n); then
+ PPS="-DKERNEL_PLL $PPS"
+ fi
+ if check "$REFCONF" '$0 ~ /[^A-Za-z]PPS/' ||
+ ( [ ! "$REFCONF" ] && query "Do you have a PPS (pulse per second) signal" n); then
+ if check "$DEFS" '$0 ~ /HAVE_BSD_TTYS|STREAM/' &&
+ (check "$REFCONF" '$0 ~ /CLK/' ||
+ ( [ ! "$REFCONF" ] && query "Is the clk line discipline available" n)); then
+ PPSFEATURES="CLK"
+ else
+ if check "$DEFS" '$0 ~ /STREAM/' &&
+ (check "$REFCONF" '$0 ~ /CD/' ||
+ ( [ ! "$REFCONF" ] && query "Is the ppsclock or ppsclocd STREAMS module available" n)); then
+ PPSFEATURES="CD $PPSFEATURES"
+ fi
+ fi
+ if check "$PPSFEATURES" '$0 ~ /CLK|CD/' &&
+ (check "$REFCONF" '$0 ~ /LINE/' ||
+ ( [ ! "$REFCONF" ] && query "Do you want to use a dedicated serial port for PPS signal" n)); then
+ if check "$PPSFEATURES" '$0 ~ /CLK/'; then
+ PPS="-DPPSCLK $PPS"
+ fi
+ if check "$PPSFEATURES" '$0 ~ /CD/'; then
+ PPS="-DPPSCD $PPS"
+ fi
+ else
+ PPSOK=1
+ PPS="-DPPS $PPS"
+ fi
+ fi
+ if (check "$REFCONF" '$0 ~ /MCAST/' ||
+ ( [ ! "$REFCONF" ] && query "Do you want allow for multicast support (if available) ?" y)); then
+ MCAST="-DMCAST"
+ else
+ MCAST=""
+ fi
+ for i in $rcfg
+ do
+ sh $i config "$RCONFIG" "$PPSFEATURES" "$PPSOK" "$DEFS" "$REFCONF"
+ if [ "$PPSOK" -eq 1 ] && egrep -e '-D..*PPS' "$RCONFIG" >/dev/null 2>&1; then
+ PPSOK=0
+ fi
+ done
+ if egrep -e '-D..*PPS' "$RCONFIG" >/dev/null 2>&1; then
+ PPS="-DPPSPPS $PPS"
+ fi
+ CLOCKDEFS="`tr '\012' ' ' < $RCONFIG`"
+ if check "$CLOCKDEFS" '$0 !~ /^[ ]*$/'; then
+ PPS="-DREFCLOCK $PPS"
+ if [ ! "$REFCONF" ]; then
+ echo
+ echo "Do not forget to set up the appropriate device links in the /dev directory"
+ echo
+ fi
+ fi
+ if sed -e 's/^[ ]*CLOCKDEFS[ ]*=.*$/CLOCKDEFS='"$CLOCKDEFS"'/;' \
+ -e 's/^[ ]*DEFS_LOCAL[ ]*=.*$/DEFS_LOCAL= $(DEFS_OPT) '"$PPS $MCAST"'/;' \
+ Config.local > Config.local.new; then
+ mv Config.local Config.local.old &&
+ mv Config.local.new Config.local &&
+ rm -f Config.local.old
+ echo
+ echo "New configuration defines:"
+ echo " CLOCKDEFS=$CLOCKDEFS"
+ echo " DEFS_LOCAL="'$(DEFS_OPT)'" $PPS $MCAST"
+ echo
+ echo "Configuration updated"
+ else
+ echo "Configuration update FAILED"
+ fi
+ rm -f "$RCONFIG"
+ else
+ :;
+ fi
+fi
diff --git a/usr.sbin/xntpd/refclocks/setup b/usr.sbin/xntpd/refclocks/setup
new file mode 100644
index 0000000..4611178
--- /dev/null
+++ b/usr.sbin/xntpd/refclocks/setup
@@ -0,0 +1,16 @@
+#
+# gobble possible parameters
+#
+if [ $# -eq 5 ]; then
+ RCONFIG="$1"
+ PPSFEATURES="$2"
+ PPSOK="$3"
+ DEFS="$4"
+ REFCONF="$5"
+fi
+#
+# shell dumbness detection
+#
+if (eval "_x () { :; }") >/dev/null 2>&1 ; then
+ . refclocks/setupfn
+fi
diff --git a/usr.sbin/xntpd/refclocks/setupfn b/usr.sbin/xntpd/refclocks/setupfn
new file mode 100644
index 0000000..5724dcb
--- /dev/null
+++ b/usr.sbin/xntpd/refclocks/setupfn
@@ -0,0 +1,27 @@
+#
+# sh io functions
+#
+if [ "`echo -n`" = "-n" ]; then
+ echon () { echo "$@\\c"; }
+else
+ echon () { echo -n "$@"; }
+fi
+
+query() {
+ _Q="$1"
+ _A="$2"
+ echon "$_Q (y/n) [$_A] ? "
+ X=""
+ read X
+ if [ "$X" = "" ]; then
+ X="$_A"
+ fi
+ case "$X" in
+ [yY]*) return 0;;
+ *) return 1;;
+ esac
+}
+
+check () {
+ `echo "$1" | awk '{ if ( '"$2"' ) { print ":"; } else { print "false"; } exit; }'`;
+}
diff --git a/usr.sbin/xntpd/scripts/Guess.sh b/usr.sbin/xntpd/scripts/Guess.sh
new file mode 100755
index 0000000..9b3180b
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/Guess.sh
@@ -0,0 +1,130 @@
+#! /bin/sh
+
+if [ -f /bin/uname -o -f /usr/bin/uname ]; then
+ set `uname -a | tr '[A-Z]' '[a-z]'`
+# set `cat test | tr '[A-Z]' '[a-z]'`
+ case "$1" in
+ convexos) case "$4" in
+ 10.*) guess="convexos10" ;;
+ esac
+ ;;
+ aix) case "$4" in
+ 3) case "$3" in
+ 1) guess="aix3.1" ;;
+ 2) guess="aix3.2" ;;
+ esac
+ ;;
+ esac
+ ;;
+ sinix-m)
+ guess=sinix-m
+ ;;
+ sunos|solaris)
+ case "$3" in
+ 4.1*) guess="sunos4" ;;
+ 5.1) guess="sunos5.1" ;;
+ 5.2) guess="sunos5.2" ;;
+ 5.*) guess="sunos5.3" ;;
+ esac
+ ;;
+ irix) case "$3" in
+ 4.*) guess="irix4" ;;
+ 5.*) guess="irix5" ;;
+ esac
+ ;;
+ "a/ux") case "$3" in
+ 2.*) guess="aux2" ;;
+ 3.*) guess="aux3" ;;
+ esac
+ ;;
+ ultrix)
+ guess="ultrix"
+ ;;
+ hp-ux) case "$3" in
+ *.10.*) guess="hpux-adj" ;;
+ *.09.03) case "$5" in
+ 9000/3*) guess="hpux-adj" ;;
+ *) guess="hpux" ;;
+ esac ;;
+ *) guess="hpux" ;;
+ esac
+ ;;
+ linux) guess="linux" ;;
+
+ osf1) case "$5" in
+ alpha) guess="decosf1" ;;
+ esac
+ ;;
+ "bsd/386")
+ guess="bsdi"
+ ;;
+ "freebsd")
+ guess="freebsd"
+ ;;
+ "netbsd")
+ guess="netbsd"
+ ;;
+ "4.4bsd")
+ guess="4.4bsd"
+ ;;
+ # now the fun starts - there are vendors that
+ # do not really identify their OS in uname.
+ # Fine - now I look at our version and hope
+ # that nobody else had this marvellous idea.
+ # I am not willing to mention the vendor explicitly
+ *) # Great ! - We are dealing with an industry standard !
+ if [ -f /unix ]; then
+ #
+ # looks like this thing has the license
+ # to call itself Unix
+ #
+ case "$3" in
+ 3.2.*)
+ case "$4" in
+ v*)
+ (i386) >/dev/null 2>&1 && [ -f /usr/lib/libseq.a ] && guess=ptx;;
+ esac
+ esac
+ fi
+ ;;
+ esac
+fi
+
+if [ "0$guess" != "0" ]; then
+ echo $guess
+ exit 0
+fi
+
+if [ -f /bin/machine ]; then
+ echo `/bin/machine`
+ exit 0
+fi
+
+if [ -f /usr/convex/vers ]; then
+ set `/usr/convex/vers /vmunix`
+ case "$2" in
+ 9.0) echo "convexos9"
+ exit 0 ;;
+ esac
+fi
+
+if [ -d /usr/lib/NextStep ]; then
+ echo next
+ exit 0
+fi
+
+if [ -f /netbsd ]; then
+ echo netbsd
+ exit 0
+fi
+
+if [ -f /lib/clib -a -f /lib/libc ]; then
+ echo domainos
+ exit 0
+fi
+
+case "$guess" in
+ '') guess="none"
+esac
+
+echo $guess
diff --git a/usr.sbin/xntpd/scripts/README b/usr.sbin/xntpd/scripts/README
new file mode 100644
index 0000000..7439c6c
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/README
@@ -0,0 +1,41 @@
+README file for directory ./scripts of the NTP Version 3 distribution
+
+This directory contains shell and perl script files for the configuration,
+monitoring and support of NTP installations. See the README and RELNOTES
+files in the parent directory for directions on how to use these files.
+
+Guess.sh script to figure out what machine and operating system
+ is running this thing. Prizes awarded for new machines
+ added to the list.
+
+autoconf awesome script swiped from Jeff Johnson (who may have
+ swiped it from GNU) which delves deep into the system
+ files to reveal dark secrets necessary to port NTP to
+ everything except sewing machines. Unfinished work.
+
+makeconfig.sh shell script that calles Guess.sh and then figures out
+ what compiler is available, then builds the
+ configuration file in the base directory. Finally, it
+ launches the sed script Config.sed in the base directory
+ to make the makefiles used by most programs.
+
+mklinks script useful to create directories for multiple
+ architecture maintenance
+
+mkversion script useful to create new version numbers for all
+ sources
+
+monitoring directory containing perl scripts useful for monitoring
+ operations
+
+ntp-groper script useful for reaching out and rattling the cages of
+ NTP peers to see if animals are inside the bars
+
+ntp-restart script useful for killing and restarting the NTP daemon
+
+stats directory containing awk ans shell scripts useful for
+ maintaining statistics summaries of clockstats, loopstats
+ and peerstats files
+
+support directory containing shell and perl scripts useful for
+ configuration and monitoring of NTP subnets
diff --git a/usr.sbin/xntpd/scripts/autoconf b/usr.sbin/xntpd/scripts/autoconf
new file mode 100755
index 0000000..b661910
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/autoconf
@@ -0,0 +1,885 @@
+#!/bin/sh
+# Guess values for system-dependent variables and create Makefiles.
+# Generated automatically using autoconf.
+# Copyright (C) 1991, 1992, 1993 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2, or (at your option)
+# any later version.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+# Usage: configure [--srcdir=DIR] [--host=HOST] [--gas] [--nfp] [--no-create]
+# [--prefix=PREFIX] [--exec-prefix=PREFIX] [--with-PACKAGE] [TARGET]
+# Ignores all args except --srcdir, --prefix, --exec-prefix, --no-create, and
+# --with-PACKAGE unless this script has special code to handle it.
+
+
+for arg
+do
+ # Handle --exec-prefix with a space before the argument.
+ if test x$next_exec_prefix = xyes; then exec_prefix=$arg; next_exec_prefix=
+ # Handle --host with a space before the argument.
+ elif test x$next_host = xyes; then next_host=
+ # Handle --prefix with a space before the argument.
+ elif test x$next_prefix = xyes; then prefix=$arg; next_prefix=
+ # Handle --srcdir with a space before the argument.
+ elif test x$next_srcdir = xyes; then srcdir=$arg; next_srcdir=
+ else
+ case $arg in
+ # For backward compatibility, also recognize exact --exec_prefix.
+ -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* | --exec=* | --exe=* | --ex=* | --e=*)
+ exec_prefix=`echo $arg | sed 's/[-a-z_]*=//'` ;;
+ -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- | --exec | --exe | --ex | --e)
+ next_exec_prefix=yes ;;
+
+ -gas | --gas | --ga | --g) ;;
+
+ -host=* | --host=* | --hos=* | --ho=* | --h=*) ;;
+ -host | --host | --hos | --ho | --h)
+ next_host=yes ;;
+
+ -nfp | --nfp | --nf) ;;
+
+ -no-create | --no-create | --no-creat | --no-crea | --no-cre | --no-cr | --no-c | --no- | --no)
+ no_create=1 ;;
+
+ -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*)
+ prefix=`echo $arg | sed 's/[-a-z_]*=//'` ;;
+ -prefix | --prefix | --prefi | --pref | --pre | --pr | --p)
+ next_prefix=yes ;;
+
+ -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=* | --s=*)
+ srcdir=`echo $arg | sed 's/[-a-z_]*=//'` ;;
+ -srcdir | --srcdir | --srcdi | --srcd | --src | --sr | --s)
+ next_srcdir=yes ;;
+
+ -with-* | --with-*)
+ package=`echo $arg|sed 's/-*with-//'`
+ # Delete all the valid chars; see if any are left.
+ if test -n "`echo $package|sed 's/[-a-zA-Z0-9_]*//g'`"; then
+ echo "configure: $package: invalid package name" >&2; exit 1
+ fi
+ eval "with_`echo $package|sed s/-/_/g`=1" ;;
+
+ -v | -verbose | --verbose | --verbos | --verbo | --verb | --ver | --ve | --v)
+ verbose=yes ;;
+
+ *) ;;
+ esac
+ fi
+done
+
+trap 'rm -f conftest* core; exit 1' 1 3 15
+
+# Needed for some versions of `tr' so that character classes in `[]' work.
+if test "${LANG+set}" = "set" ; then
+ LANG=C
+fi
+
+rm -f conftest*
+compile='${CC-cc} $CFLAGS $DEFS conftest.c -o conftest $LIBS >/dev/null 2>&1'
+
+# A filename unique to this package, relative to the directory that
+# configure is in, which we can look for to find out if srcdir is correct.
+unique_file=lib/msyslog.c
+
+# Find the source files, if location was not specified.
+if test -z "$srcdir"; then
+ srcdirdefaulted=yes
+ # Try the directory containing this script, then `..'.
+ prog=$0
+ confdir=`echo $prog|sed 's%/[^/][^/]*$%%'`
+ test "X$confdir" = "X$prog" && confdir=.
+ srcdir=$confdir
+ if test ! -r $srcdir/$unique_file; then
+ srcdir=..
+ fi
+fi
+if test ! -r $srcdir/$unique_file; then
+ if test x$srcdirdefaulted = xyes; then
+ echo "configure: Can not find sources in \`${confdir}' or \`..'." 1>&2
+ else
+ echo "configure: Can not find sources in \`${srcdir}'." 1>&2
+ fi
+ exit 1
+fi
+# Preserve a srcdir of `.' to avoid automounter screwups with pwd.
+# But we can't avoid them for `..', to make subdirectories work.
+case $srcdir in
+ .|/*|~*) ;;
+ *) srcdir=`cd $srcdir; pwd` ;; # Make relative path absolute.
+esac
+
+
+useresolv=""
+
+echo CONFIGURE -- initializing DEFS
+test -z "$DEFS" && DEFS="-DDEBUG -DREFCLOCK"
+echo CONFIGURE -- initializing AUTHDEFS
+test -z "$AUTHDEFS" && AUTHDEFS="-DDES -DMD5"
+echo CONFIGURE -- initializing CLOCKDEFS
+test -z "$CLOCKDEFS" && CLOCKDEFS="-DAS2201 -DCHU -DGOES -DLEITCH -DLOCAL_CLOCK -DOMEGA -DPST -DWWVB"
+echo CONFIGURE -- initializing COPTS
+test -z "$COPTS" && COPTS="-O"
+
+test -z "$INCLUDE" && INCLUDE="-I../include"
+test -z "$LIB" && LIB="../lib/libntp.a"
+
+test "`uname`" = "SunOS" && {
+ DEFS="$DEFS -DXNTP_RETROFIT_STDLIB"
+}
+test "`uname`" = "ULTRIX" && {
+ DEFS="$DEFS -DXNTP_RETROFIT_STDLIB -DPLL -DREADKMEM"
+}
+test "`uname`" = "BSD/386" && {
+ DEFS="$DEFS -D__bsdi__"
+}
+test -d /usr/lib/NextStep && {
+ DEFS="$DEFS -DREADKMEM -DSYNCTODR_SUCKS"
+}
+
+SOLARIS=""
+test -d /kernel && test -d /opt && isSOLARIS="1"
+
+echo TODO -- checking for HPUX
+
+# We want these before the checks, so the checks can modify their values.
+test -z "$CFLAGS" && CFLAGS=-g auto_cflags=1
+test -z "$LDFLAGS" && LDFLAGS=-g
+
+#==========================================================================
+if test -z "$CC"; then
+ # Extract the first word of `gcc', so it can be a program name with args.
+ set dummy gcc; word=$2
+ echo checking for $word
+ IFS="${IFS= }"; saveifs="$IFS"; IFS="${IFS}:"
+ for dir in $PATH; do
+ test -z "$dir" && dir=.
+ if test -f $dir/$word; then
+ CC="gcc"
+ break
+ fi
+ done
+ IFS="$saveifs"
+fi
+test -z "$CC" && CC="cc"
+test -n "$CC" -a -n "$verbose" && echo " setting CC to $CC"
+
+# Find out if we are using GNU C, under whatever name.
+cat > conftest.c <<EOF
+#ifdef __GNUC__
+ yes
+#endif
+EOF
+${CC-cc} -E conftest.c > conftest.out 2>&1
+if egrep yes conftest.out >/dev/null 2>&1; then
+ GCC=1 # For later tests.
+fi
+rm -f conftest*
+
+
+# If we're using gcc and the user hasn't specified CFLAGS, add -O to CFLAGS.
+test -n "$GCC" && test -n "$auto_cflags" && CFLAGS="$CFLAGS -O"
+
+
+echo checking how to run the C preprocessor
+if test -z "$CPP"; then
+ CPP='${CC-cc} -E'
+ cat > conftest.c <<EOF
+#include <stdio.h>
+EOF
+err=`eval "($CPP \$DEFS conftest.c >/dev/null) 2>&1"`
+if test -z "$err"; then
+ :
+else
+ CPP=/lib/cpp
+fi
+rm -f conftest*
+fi
+
+if test -n "$GCC"; then
+ echo checking whether -traditional is needed
+ pattern="Autoconf.*'x'"
+ prog='#include <sgtty.h>
+Autoconf TIOCGETP'
+ cat > conftest.c <<EOF
+$prog
+EOF
+eval "$CPP \$DEFS conftest.c > conftest.out 2>&1"
+if egrep "$pattern" conftest.out >/dev/null 2>&1; then
+ need_trad=1
+fi
+rm -f conftest*
+
+
+ if test -z "$need_trad"; then
+ prog='#include <termio.h>
+Autoconf TCGETA'
+ cat > conftest.c <<EOF
+$prog
+EOF
+eval "$CPP \$DEFS conftest.c > conftest.out 2>&1"
+if egrep "$pattern" conftest.out >/dev/null 2>&1; then
+ need_trad=1
+fi
+rm -f conftest*
+
+ fi
+ test -n "$need_trad" && CC="$CC -traditional"
+fi
+
+if test -z "$RANLIB"; then
+ # Extract the first word of `ranlib', so it can be a program name with args.
+ set dummy ranlib; word=$2
+ echo checking for $word
+ IFS="${IFS= }"; saveifs="$IFS"; IFS="${IFS}:"
+ for dir in $PATH; do
+ test -z "$dir" && dir=.
+ if test -f $dir/$word; then
+ RANLIB="ranlib"
+ break
+ fi
+ done
+ IFS="$saveifs"
+fi
+test -z "$RANLIB" && RANLIB=":"
+test -n "$RANLIB" -a -n "$verbose" && echo " setting RANLIB to $RANLIB"
+
+
+# aC_MEMORY_H
+echo checking for ANSI C header files
+cat > conftest.c <<EOF
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <float.h>
+EOF
+err=`eval "($CPP \$DEFS conftest.c >/dev/null) 2>&1"`
+if test -z "$err"; then
+ # SunOS 4.x string.h does not declare mem*, contrary to ANSI.
+echo '#include <string.h>' > conftest.c
+eval "$CPP \$DEFS conftest.c > conftest.out 2>&1"
+if egrep "memchr" conftest.out >/dev/null 2>&1; then
+ # SGI's /bin/cc from Irix-4.0.5 gets non-ANSI ctype macros unless using -ansi.
+cat > conftest.c <<EOF
+#include <ctype.h>
+#define ISLOWER(c) ('a' <= (c) && (c) <= 'z')
+#define TOUPPER(c) (ISLOWER(c) ? 'A' + ((c) - 'a') : (c))
+#define XOR(e,f) (((e) && !(f)) || (!(e) && (f)))
+int main () { int i; for (i = 0; i < 256; i++)
+if (XOR (islower (i), ISLOWER (i)) || toupper (i) != TOUPPER (i)) exit(2);
+exit (0); }
+
+EOF
+eval $compile
+if test -s conftest && (./conftest; exit) 2>/dev/null; then
+ {
+test -n "$verbose" && \
+echo ' defining' STDC_HEADERS
+DEFS="$DEFS -DSTDC_HEADERS=1"
+}
+
+fi
+rm -f conftest*
+fi
+rm -f conftest*
+
+fi
+rm -f conftest*
+
+for hdr in string.h memory.h
+do
+trhdr=HAVE_`echo $hdr | tr '[a-z]./' '[A-Z]__'`
+echo checking for ${hdr}
+cat > conftest.c <<EOF
+#include <${hdr}>
+EOF
+err=`eval "($CPP \$DEFS conftest.c >/dev/null) 2>&1"`
+if test -z "$err"; then
+ {
+test -n "$verbose" && \
+echo ' defining' ${trhdr}
+DEFS="$DEFS -D${trhdr}=1"
+}
+
+fi
+rm -f conftest*
+done
+
+echo checking for unistd.h
+cat > conftest.c <<EOF
+#include <unistd.h>
+EOF
+err=`eval "($CPP \$DEFS conftest.c >/dev/null) 2>&1"`
+if test -z "$err"; then
+ {
+test -n "$verbose" && \
+echo ' defining' NTP_POSIX_SOURCE
+DEFS="$DEFS -DNTP_POSIX_SOURCE=1"
+}
+
+fi
+rm -f conftest*
+
+
+echo checking for mode_t in sys/types.h
+echo '#include <sys/types.h>' > conftest.c
+eval "$CPP \$DEFS conftest.c > conftest.out 2>&1"
+if egrep "mode_t" conftest.out >/dev/null 2>&1; then
+ :
+else
+ {
+test -n "$verbose" && \
+echo ' defining' mode_t to be 'int'
+DEFS="$DEFS -Dmode_t=int"
+}
+
+fi
+rm -f conftest*
+
+echo checking for pid_t in sys/types.h
+echo '#include <sys/types.h>' > conftest.c
+eval "$CPP \$DEFS conftest.c > conftest.out 2>&1"
+if egrep "pid_t" conftest.out >/dev/null 2>&1; then
+ :
+else
+ {
+test -n "$verbose" && \
+echo ' defining' pid_t to be 'int'
+DEFS="$DEFS -Dpid_t=int"
+}
+
+fi
+rm -f conftest*
+
+echo checking for return type of signal handlers
+cat > conftest.c <<EOF
+#include <sys/types.h>
+#include <signal.h>
+#ifdef signal
+#undef signal
+#endif
+extern void (*signal ()) ();
+int main() { exit(0); }
+int t() { int i; }
+EOF
+if eval $compile; then
+ {
+test -n "$verbose" && \
+echo ' defining' RETSIGTYPE to be 'void'
+DEFS="$DEFS -DRETSIGTYPE=void"
+}
+
+else
+ {
+test -n "$verbose" && \
+echo ' defining' RETSIGTYPE to be 'int'
+DEFS="$DEFS -DRETSIGTYPE=int"
+}
+
+fi
+rm -f conftest*
+
+
+echo checking for size_t in sys/types.h
+echo '#include <sys/types.h>' > conftest.c
+eval "$CPP \$DEFS conftest.c > conftest.out 2>&1"
+if egrep "size_t" conftest.out >/dev/null 2>&1; then
+ :
+else
+ {
+test -n "$verbose" && \
+echo ' defining' size_t to be 'unsigned'
+DEFS="$DEFS -Dsize_t=unsigned"
+}
+
+fi
+rm -f conftest*
+
+
+# aC_VPRINTF
+
+# aC_TIME_WITH_SYS_TIME
+# aC_STRUCT_TM
+
+# aC_CHAR_UNSIGNED
+echo checking for signed char declaration
+cat > conftest.c <<EOF
+main() { signed char c; }
+EOF
+eval $compile
+if test -s conftest && (./conftest; exit) 2>/dev/null; then
+ :
+else
+ {
+test -n "$verbose" && \
+echo ' defining' NO_SIGNED_CHAR_DECL
+DEFS="$DEFS -DNO_SIGNED_CHAR_DECL=1"
+}
+
+fi
+rm -f conftest*
+echo checking for s_char in sys/types.h
+echo '#include <sys/types.h>' > conftest.c
+eval "$CPP \$DEFS conftest.c > conftest.out 2>&1"
+if egrep "s_char" conftest.out >/dev/null 2>&1; then
+ {
+test -n "$verbose" && \
+echo ' defining' S_CHAR_DEFINED
+DEFS="$DEFS -DS_CHAR_DEFINED=1"
+}
+
+fi
+rm -f conftest*
+
+
+prog='/* Ultrix mips cc rejects this. */
+typedef int charset[2]; const charset x;
+/* SunOS 4.1.1 cc rejects this. */
+char const *const *ccp;
+char **p;
+/* AIX XL C 1.02.0.0 rejects this.
+ It does not let you subtract one const X* pointer from another in an arm
+ of an if-expression whose if-part is not a constant expression */
+const char *g = "string";
+p = &g + (g ? g-g : 0);
+/* HPUX 7.0 cc rejects these. */
+++ccp;
+p = (char**) ccp;
+ccp = (char const *const *) p;
+{ /* SCO 3.2v4 cc rejects this. */
+ char *t;
+ char const *s = 0 ? (char *) 0 : (char const *) 0;
+
+ *t++ = 0;
+}
+{ /* Someone thinks the Sun supposedly-ANSI compiler will reject this. */
+ int x[] = {25,17};
+ const int *foo = &x[0];
+ ++foo;
+}
+{ /* Sun SC1.0 ANSI compiler rejects this -- but not the above. */
+ typedef const int *iptr;
+ iptr p = 0;
+ ++p;
+}
+{ /* AIX XL C 1.02.0.0 rejects this saying
+ "k.c", line 2.27: 1506-025 (S) Operand must be a modifiable lvalue. */
+ struct s { int j; const int *ap[3]; };
+ struct s *b; b->j = 5;
+}'
+echo checking for working const
+cat > conftest.c <<EOF
+
+int main() { exit(0); }
+int t() { $prog }
+EOF
+if eval $compile; then
+ :
+else
+ {
+test -n "$verbose" && \
+echo ' defining' const to be 'empty'
+DEFS="$DEFS -Dconst="
+}
+
+fi
+rm -f conftest*
+
+if test -n "$GCC"; then
+echo checking for inline
+cat > conftest.c <<EOF
+
+int main() { exit(0); }
+int t() { } inline foo() { }
+EOF
+if eval $compile; then
+ :
+else
+ {
+test -n "$verbose" && \
+echo ' defining' inline to be '__inline'
+DEFS="$DEFS -Dinline=__inline"
+}
+
+fi
+rm -f conftest*
+
+fi
+
+
+# aC_INT_16_BITS
+# DEC Alpha running OSF/1
+echo checking integer size
+cat > conftest.c <<EOF
+main() { exit(!(sizeof(long) > sizeof(int))); }
+EOF
+eval $compile
+if test -s conftest && (./conftest; exit) 2>/dev/null; then
+ {
+test -n "$verbose" && \
+echo ' defining' LONG to be 'int'
+DEFS="$DEFS -DLONG=int"
+}
+ {
+test -n "$verbose" && \
+echo ' defining' U_LONG to be 'u_int'
+DEFS="$DEFS -DU_LONG=u_int"
+}
+
+fi
+rm -f conftest*
+
+if test -n "$GCC"; then
+{
+test -n "$verbose" && \
+echo ' defining' HAVE_LONG_DOUBLE
+DEFS="$DEFS -DHAVE_LONG_DOUBLE=1"
+}
+
+else
+echo checking for long double
+cat > conftest.c <<EOF
+
+int main() { exit(0); }
+int t() { } long double foo() { }
+EOF
+if eval $compile; then
+ {
+test -n "$verbose" && \
+echo ' defining' HAVE_LONG_DOUBLE
+DEFS="$DEFS -DHAVE_LONG_DOUBLE=1"
+}
+
+fi
+rm -f conftest*
+
+fi
+
+echo checking byte ordering
+cat > conftest.c <<EOF
+main () {
+ /* Are we little or big endian? From Harbison&Steele. */
+ union
+ {
+ long l;
+ char c[sizeof (long)];
+ } u;
+ u.l = 1;
+ exit (u.c[sizeof (long) - 1] == 1);
+}
+EOF
+eval $compile
+if test -s conftest && (./conftest; exit) 2>/dev/null; then
+ :
+else
+ {
+test -n "$verbose" && \
+echo ' defining' WORDS_BIGENDIAN
+DEFS="$DEFS -DWORDS_BIGENDIAN=1"
+}
+
+fi
+rm -f conftest*
+
+
+echo checking for restartable system calls
+cat > conftest.c <<EOF
+/* Exit 0 (true) if wait returns something other than -1,
+ i.e. the pid of the child, which means that wait was restarted
+ after getting the signal. */
+#include <sys/types.h>
+#include <signal.h>
+ucatch (isig) { }
+main () {
+ int i = fork (), status;
+ if (i == 0) { sleep (3); kill (getppid (), SIGINT); sleep (3); exit (0); }
+ signal (SIGINT, ucatch);
+ status = wait(&i);
+ if (status == -1) wait(&i);
+ exit (status == -1);
+}
+
+EOF
+eval $compile
+if test -s conftest && (./conftest; exit) 2>/dev/null; then
+ {
+test -n "$verbose" && \
+echo ' defining' HAVE_RESTARTABLE_SYSCALLS
+DEFS="$DEFS -DHAVE_RESTARTABLE_SYSCALLS=1"
+}
+
+fi
+rm -f conftest*
+
+
+havestreams=""
+echo checking for sys/stream.h
+cat > conftest.c <<EOF
+#include <sys/stream.h>
+EOF
+err=`eval "($CPP \$DEFS conftest.c >/dev/null) 2>&1"`
+if test -z "$err"; then
+ {
+test -n "$verbose" && \
+echo ' defining' STREAM
+DEFS="$DEFS -DSTREAM=1"
+}
+ havestreams="1"
+fi
+rm -f conftest*
+
+echo checking clock options
+
+if test -f /dev/pps ; then
+ echo found ppsclk
+ DEFS="$DEFS -DPPSCLK"
+fi
+if test -f /dev/tpro ; then
+ echo found tpro
+ CLOCKDEFS="$CLOCKDEFS -DTPRO"
+fi
+if test -f /dev/irig ; then
+ echo found irig
+ CLOCKDEFS="$CLOCKDEFS -DIRIG"
+fi
+
+echo TODO -- checking for adjtime/libadjtimed.a
+
+test -n "$useresolv" && {
+ LIBS_save="${LIBS}"
+LIBS="${LIBS} -lresolv"
+have_lib=""
+echo checking for -lresolv
+cat > conftest.c <<EOF
+
+int main() { exit(0); }
+int t() { main(); }
+EOF
+if eval $compile; then
+ have_lib="1"
+fi
+rm -f conftest*
+LIBS="${LIBS_save}"
+if test -n "${have_lib}"; then
+ :; RESLIB="$RESLIB -lresolv"
+else
+ :;
+fi
+
+}
+test -n "$isSOLARIS" && {
+ {
+test -n "$verbose" && \
+echo ' defining' SOLARIS
+DEFS="$DEFS -DSOLARIS=1"
+}
+
+ {
+test -n "$verbose" && \
+echo ' defining' SLEWALWAYS
+DEFS="$DEFS -DSLEWALWAYS=1"
+}
+
+ {
+test -n "$verbose" && \
+echo ' defining' STUPID_SIGNAL
+DEFS="$DEFS -DSTUPID_SIGNAL=1"
+}
+
+ LIBS_save="${LIBS}"
+LIBS="${LIBS} -lsocket"
+have_lib=""
+echo checking for -lsocket
+cat > conftest.c <<EOF
+
+int main() { exit(0); }
+int t() { main(); }
+EOF
+if eval $compile; then
+ have_lib="1"
+fi
+rm -f conftest*
+LIBS="${LIBS_save}"
+if test -n "${have_lib}"; then
+ :; RESLIB="$RESLIB -lsocket"
+else
+ :;
+fi
+
+ LIBS_save="${LIBS}"
+LIBS="${LIBS} -lnsl"
+have_lib=""
+echo checking for -lnsl
+cat > conftest.c <<EOF
+
+int main() { exit(0); }
+int t() { main(); }
+EOF
+if eval $compile; then
+ have_lib="1"
+fi
+rm -f conftest*
+LIBS="${LIBS_save}"
+if test -n "${have_lib}"; then
+ :; RESLIB="$RESLIB -lnsl"
+else
+ :;
+fi
+
+ LIBS_save="${LIBS}"
+LIBS="${LIBS} -lelf"
+have_lib=""
+echo checking for -lelf
+cat > conftest.c <<EOF
+
+int main() { exit(0); }
+int t() { main(); }
+EOF
+if eval $compile; then
+ have_lib="1"
+fi
+rm -f conftest*
+LIBS="${LIBS_save}"
+if test -n "${have_lib}"; then
+ :; RESLIB="$RESLIB -lelf"
+else
+ :;
+fi
+
+}
+
+LIBS_save="${LIBS}"
+LIBS="${LIBS} -lBSD"
+have_lib=""
+echo checking for -lBSD
+cat > conftest.c <<EOF
+
+int main() { exit(0); }
+int t() { main(); }
+EOF
+if eval $compile; then
+ have_lib="1"
+fi
+rm -f conftest*
+LIBS="${LIBS_save}"
+if test -n "${have_lib}"; then
+ :; LIBS="$LIBS -lBSD"
+else
+ :;
+fi
+
+test -n "$have_lib" && {
+test -n "$verbose" && \
+echo ' defining' COMPAT to be '"-lBSD"'
+DEFS="$DEFS -DCOMPAT=\"-lBSD\""
+}
+
+
+test -z "$isSOLARIS" && {
+ LIBS_save="${LIBS}"
+LIBS="${LIBS} -lkvm"
+have_lib=""
+echo checking for -lkvm
+cat > conftest.c <<EOF
+
+int main() { exit(0); }
+int t() { main(); }
+EOF
+if eval $compile; then
+ have_lib="1"
+fi
+rm -f conftest*
+LIBS="${LIBS_save}"
+if test -n "${have_lib}"; then
+ :; RESLIB="$RESLIB -lkvm"
+else
+ :;
+fi
+
+ test -n "$have_lib" && {
+test -n "$verbose" && \
+echo ' defining' USELIBKVM
+DEFS="$DEFS -DUSELIBKVM=1"
+}
+
+}
+
+LIBS_save="${LIBS}"
+LIBS="${LIBS} -lmld"
+have_lib=""
+echo checking for -lmld
+cat > conftest.c <<EOF
+
+int main() { exit(0); }
+int t() { main(); }
+EOF
+if eval $compile; then
+ have_lib="1"
+fi
+rm -f conftest*
+LIBS="${LIBS_save}"
+if test -n "${have_lib}"; then
+ :; RESLIB="$RESLIB -lmld"
+else
+ :;
+fi
+
+
+prefix=/usr/local
+bindir=/usr/local/bin
+
+if test -n "$prefix"; then
+ test -z "$exec_prefix" && exec_prefix='${prefix}'
+ prsub="s%^prefix\\([ ]*\\)=\\([ ]*\\).*$%prefix\\1=\\2$prefix%"
+fi
+if test -n "$exec_prefix"; then
+ prsub="$prsub
+s%^exec_prefix\\([ ]*\\)=\\([ ]*\\).*$%\
+exec_prefix\\1=\\2$exec_prefix%"
+fi
+DEFS="`echo \"$DEFS\" | sed 's%[&\\\]%\\\&%g'`"
+
+trap 'rm -f config.status; exit 1' 1 3 15
+echo creating config.status
+rm -f config.status
+cat > config.status <<EOF
+#!/bin/sh
+# Generated automatically by autoconf.
+# Run this file to recreate the current configuration.
+# This directory was configured as follows,
+# on host `(hostname || uname -n) 2>/dev/null | sed 1q`:
+#
+# $0 $*
+PROGS='$PROGS'
+CC='$CC'
+CFLAGS='$CFLAGS'
+LDFLAGS='$LDFLAGS'
+CPP='$CPP'
+RANLIB='$RANLIB'
+bindir='$bindir'
+AUTHDEFS='$AUTHDEFS'
+CLOCKDEFS='$CLOCKDEFS'
+COPTS='$COPTS'
+INCLUDE='$INCLUDE'
+LIB='$LIB'
+ADJLIB='$ADJLIB'
+RESLIB='$RESLIB'
+COMPAT='$COMPAT'
+LIBS='$LIBS'
+srcdir='$srcdir'
+DEFS='$DEFS'
+prefix='$prefix'
+exec_prefix='$exec_prefix'
+prsub='$prsub'
+EOF
+
diff --git a/usr.sbin/xntpd/scripts/install.sh b/usr.sbin/xntpd/scripts/install.sh
new file mode 100755
index 0000000..1bc4989
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/install.sh
@@ -0,0 +1,100 @@
+#!/bin/sh
+#
+# Emulate the BSD install command with cpset for System V
+# Tom Moore - NCR Corporation
+#
+PATH=/bin:/etc:/usr/bin:/usr/ucb
+export PATH
+
+# Default values
+mode=0755
+owner=bin
+group=bin
+strip=FALSE
+remove=TRUE
+
+USAGE="install [-s] [-c] [-m mode] [-o owner] [-g group] source file|directory"
+set -- `getopt scm:o:g: $*` || {
+ echo $USAGE >&2
+ exit 2
+}
+for option in $*
+do
+ case $option in
+ -s) # Strip the installed file
+ strip=TRUE
+ shift
+ ;;
+ -c) # Copy the source file rather than move it
+ remove=FALSE
+ shift
+ ;;
+ -m) # File mode
+ mode=$2
+ shift 2
+ ;;
+ -o) # File owner
+ owner=$2
+ shift 2
+ ;;
+ -g) # File group
+ group=$2
+ shift 2
+ ;;
+ --) # End of options
+ shift
+ break
+ ;;
+ esac
+done
+
+case $# in
+0) echo "install: no file or destination specified" >&2
+ exit 2
+ ;;
+1) echo "install: no destination specified" >&2
+ exit 2
+ ;;
+2) source=$1
+ destination=$2
+ ;;
+*) echo "install: too many files specified" >&2
+ exit 2
+ ;;
+esac
+
+[ $source = $destination -o $destination = . ] && {
+ echo "install: can't move $source onto itself" >&2
+ exit 1
+}
+
+[ -f $source ] || {
+ echo "install: can't open $source" >&2
+ exit 1
+}
+
+if [ -d $destination ]
+then
+ file=`basename $source`
+ OLDdestination=$destination/OLD$file
+ destination=$destination/$file
+else
+ dir=`dirname $destination`
+ file=`basename $destination`
+ OLDdestination=$dir/OLD$file
+fi
+
+(cp $source $destination &&
+ chmod $mode $destination &&
+ chown $owner $destination &&
+ chgrp $group $destination) || true # exit 1
+
+# /bin/rm -f $OLDdestination
+
+[ $strip = TRUE ] &&
+ strip $destination
+
+[ $remove = TRUE ] &&
+ rm -f $source
+
+exit 0
diff --git a/usr.sbin/xntpd/scripts/makeconfig.sh b/usr.sbin/xntpd/scripts/makeconfig.sh
new file mode 100755
index 0000000..8842a86
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/makeconfig.sh
@@ -0,0 +1,85 @@
+#!/bin/sh
+
+MACHINE=${1-${OS}}
+COMPILER=${2-${CC}}
+
+#
+# Figure out which compiler to use. Stolen from Jeff Johnson.
+#
+if [ "0$COMPILER" = "0" ]; then
+ COMPILER="cc"
+ set dummy gcc; word=$2
+ IFS="${IFS= }"; saveifs="$IFS"; IFS="${IFS}:"
+ for dir in $PATH; do
+ test -z "$dir" && dir=.
+ if test -f $dir/$word; then
+ COMPILER="gcc"
+ break
+ fi
+ done
+ IFS="$saveifs"
+fi
+
+#
+# Figure out the byte order and word size.
+#
+if (cd util && rm -f longsize && $COMPILER -o longsize longsize.c ); then
+ if util/longsize >/dev/null 2>&1; then
+ LONG=`util/longsize`
+ else
+ echo "TROUBLE: executables built by your compiler don't work - bug your vendor"
+ exit 1
+ fi
+else
+ echo "TROUBLE: could not compile !"
+ echo "TROUBLE: either your compiler does not work / is not present"
+ echo "TROUBLE: or you have mangled the file tree"
+ exit 1
+fi
+(cd util && rm -f byteorder && $COMPILER -o byteorder byteorder.c $LONG )
+BYTE=`util/byteorder `
+if [ "0$BYTE" = "0" ]; then
+ BYTE="XNTP_BIG_ENDIAN"
+fi
+(cd util && rm -f byteorder longsize)
+
+#
+# Figure out which machine we have.
+#
+if [ "0$MACHINE" = "0" ]; then
+ GUESS=`scripts/Guess.sh`
+ if [ "0$GUESS" = "0none" ]; then
+ echo ' '
+ echo "I don't know your system!"
+ echo "I do know about the following systems:"
+ (cd machines && ls -C *)
+ echo "Choose a system and type \"make OS=<system>\""
+ exit 1
+ else
+ if [ -f machines/$GUESS ]; then
+ MACHINE=$GUESS
+ else
+ if [ -f machines/$GUESS.posix ]; then
+ MACHINE="$GUESS.posix"
+ else
+ MACHINE="$GUESS.bsd"
+ fi
+ fi
+ fi
+fi
+
+echo "Configuring machines/$MACHINE compilers/$MACHINE.$COMPILER"
+
+if [ -f machines/$MACHINE ]; then
+ cat machines/$MACHINE >Config ;
+ if [ -f compilers/$MACHINE.$COMPILER ]; then
+ cat compilers/$MACHINE.$COMPILER >>Config
+ else
+ echo "COMPILER= $COMPILER" >>Config
+ fi
+ echo "LIBDEFS= -D$BYTE" >>Config
+ cat Config.local >>Config
+else
+ echo "Don't know how to build xntpd for machine $MACHINE " ;
+ exit 1
+fi
diff --git a/usr.sbin/xntpd/scripts/mklinks b/usr.sbin/xntpd/scripts/mklinks
new file mode 100755
index 0000000..8565d1c
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/mklinks
@@ -0,0 +1,9 @@
+#!/bin/sh
+# call from the source root as 'mklinks ../sun4 ../src'
+find . -type d -print | sort | sed "s-^\.-mkdir $1-" | sh
+root=`echo $2 | sed "s-^\.\./--"`
+find . ! -type d -a ! -name Config -print | sed "s-^\./--" | while read file
+ do
+ down=`echo $file | sed -e "s-[^/]*-..-g"`
+ ln -s $down/$root/$file $1/$file
+ done
diff --git a/usr.sbin/xntpd/scripts/mkversion b/usr.sbin/xntpd/scripts/mkversion
new file mode 100755
index 0000000..fe043e0
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/mkversion
@@ -0,0 +1,36 @@
+#!/bin/sh -
+PROG=${1-UNKNOWN}
+if [ ! -f .version ]; then
+ echo 0 > .version
+fi
+
+RUN="`cat .version`"
+RUN="`expr $RUN + 1`"
+echo $RUN > .version
+
+DATE="`LC_TIME=C date`"
+TOPDIR=`echo $0 | sed -e 's;mkversion;..;'`
+
+if [ -r VERSION ]; then
+ VERSION=VERSION
+else if [ -r ${TOPDIR}/VERSION ]; then
+ VERSION=${TOPDIR}/VERSION
+else
+ VERSION=../VERSION
+fi; fi
+
+if [ -f "$VERSION" ]; then
+ FLAGS="`egrep '^[0-9a-zA-Z_]+=' "$VERSION" | tr '\012' ';'` "
+else
+ FLAGS=""
+fi
+
+echo "Version $PROG ${FLAGS}${DATE} (${RUN})";
+
+rm -f version.c
+cat > version.c << -EoF-
+/*
+ * version file for $PROG
+ */
+char * Version = "$PROG ${FLAGS}${DATE} (${RUN})";
+-EoF-
diff --git a/usr.sbin/xntpd/scripts/monitoring/README b/usr.sbin/xntpd/scripts/monitoring/README
new file mode 100644
index 0000000..fa8ad8b
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/monitoring/README
@@ -0,0 +1,154 @@
+This directory contains support for monitoring the local clock of xntp daemons.
+
+WARNING: The scripts and routines contained in this directory are bete realease!
+ Do not depend on their correct operation. They are, however, in regular
+ use at University of Erlangen-Nuernberg. No severe problems are known
+ for this code.
+
+!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+PLEASE THINK TWICE BEFORE STARTING MONITORING REMOTE XNTP DEAMONS !!!!
+MONITORING MAY INCREASE THE LOAD OF THE DEAMON MONITORED AND MAY
+INCREASE THE NETWORK LOAD SIGNIFICANTLY
+!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+
+
+Files are:
+
+README:
+ This file
+
+ntptrap:
+ perl script to log ntp mode 6 trap messages.
+
+ It sends a set_trap request to each server given and dumps the
+ trap messages received. It handles refresh of set_trap.
+ Currently it handles only NTP V2, however the NTP V3 servers
+ also accept v2 requests. It will not interpret v3 system and peer
+ stati correctly.
+
+ usage:
+ ntptrap [-n] [-p <port>] [-l <debug-output>] servers...
+
+ -n: do not send set_trap requests
+
+ port: port to listen for responses
+ useful if you have a configured trap
+
+ debug-output: file to write trace output to (for debugging)
+
+ This script convinced me that ntp trap messages are only of
+ little use.
+
+ntploopstat:
+ perl script to gather loop info statistics from xntpd via mode 7
+ LOOP_INFO requests.
+
+ This script collects data to allow monitoring of remote xntp servers
+ where it is not possible to directly access the loopstats file
+ produced by xntpd itself. Of course, it can be used to sample
+ a local server if it is not configured to produce a loopstats file.
+
+ Please note, this program poses a high load on the server as
+ a communication takes place every delay seconds ! USE WITH CARE !
+
+ usage:
+ ntploopstat [-d<delay>] [-t<timeout>] [-l <logfile>] [-v] [ntpserver]
+
+ delay: number of seconds to wait between samples
+ default: 60 seconds
+ timeout: number of seconds to wait for reply
+ default 12 seconds
+ logfile: file to log samples to
+ default: loopstats:<ntpserver>:
+ (note the trailing colon)
+ This name actually is a prefix.
+ The file name is dynamically derived by appending
+ the name of the month the sample belongs to.
+ Thus all samples of a month end up in the same file.
+
+ the format of the files generated is identical to the format used by
+ xntpd with the loopstats file:
+ MJD <seconds since midnight UTC> offset frequency compliance
+
+ if a timeout occurs the next sample is tried after delay/2 seconds
+
+ The script will terminate after MAX_FAIL (currently 60) consecutive errors.
+ Errors are counted for:
+ - error on send call
+ - error on select call
+ - error on recv call
+ - short packet received
+ - bad packet
+ - error on open for logfile
+
+ntploopwatch:
+ perl script to display loop filter statistics collected by ntploopstat
+ or dumped directly by xntpd.
+
+ Gnuplot is used to produce a graphical representation of the sample
+ values, that have been preprocessed and analysed by this script.
+
+ It can either be called to produce a printout of specific data set or
+ used to continously monitor the values. Monitoring is achieved by
+ periodically reprocessing the logfiles, which are updated regularly
+ either by a running ntploopstat process or by the running xntpd.
+
+ usage:
+ to watch statistics permanently:
+ ntploopwatch [-v[<level>]] [-c <config-file>] [-d <working-dir>]
+
+ to get a single print out specify also
+ -P<printer> [-s<samples>]
+ [-S <start-time>] [-E <end-time>]
+ [-O <MaxOffs>] [-o <MinOffs>]
+
+ level: level of verbosity for debugging
+ config-file: file to read configurable settings from
+ On each iteration it is checked and reread
+ if it has been changed
+ default: loopwatch.config
+ working-dir: specify working directory for process, affects
+ interpretation of relative file names
+
+ All other flags are only useful with printing plots, as otherwise
+ command line values would be replaced by settings from the config file.
+
+ printer: specify printer to print plot
+ BSD print systems semantics apply; if printer is omitted
+ the name "ps" is used; plots are prepared using
+ PostScript, thus the printer should best accept
+ postscript input
+
+ For the following see also the comments in loopwatch.config.SAMPLE
+
+ samples: use last # samples from input data
+ start-time: ignore input samples before this date
+ end-time: ignore input samples after this date
+ if both start-time and end-time are specified
+ a given samples value is ignored
+ MaxOffs:
+ MinOffs: restrict value range
+
+loopwatch.config.SAMPLE:
+ sample config file for ntploopwatch
+ each configurable option is explained there
+
+lr.pl:
+ linear regression package used by ntploopwatch to compute
+ linear approximations for frequency and offset values
+ within display range
+
+timelocal.pl:
+ used during conversion of ISO_DATE_TIME values specified in loopwatch
+ config files to unix epoch values (seconds since 1970-01-01_00:00_00 UTC)
+
+ A version of this file is distributed with perl-4.x, however,
+ it has a bug related to dates crossing 1970, causing endless loops..
+ The version contained here has been fixed.
+
+ntp.pl:
+ perl support for ntp v2 mode 6 message handling
+ WARNING: This code is beta level - it triggers a memory leak;
+ as for now it is not quite clear, wether this is caused by a
+ bug in perl or by bad usage of perl within this script.
+
diff --git a/usr.sbin/xntpd/scripts/monitoring/loopwatch.config.SAMPLE b/usr.sbin/xntpd/scripts/monitoring/loopwatch.config.SAMPLE
new file mode 100644
index 0000000..8cefea3
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/monitoring/loopwatch.config.SAMPLE
@@ -0,0 +1,89 @@
+# sample configuration and control file for ntploowatch
+#
+# delay: sampling interval in seconds
+delay=60
+# samples: use only last # samples
+samples=600
+# DO NOT USE srcprefix in shared config files
+# srcprefix: name of file to read samples from
+# current time suffix (month name) is appended
+# defaults to "./var@$STATHOST/loopstats."
+# The string "$STATHOST"is replaced by the name of the host
+# being monitored
+#srcprefix=./var@$STATHOST/loopstats.
+#
+# showoffs: yes/no control display of offset values
+showoffs=yes
+#
+# showfreq: yes/no control display of frequency values
+showfreq=yes
+#
+# showcmpl: yes/no control display of compliance values
+showcmpl=no
+#
+# showoreg: yes/no control display of linear regression of offset values
+showoreg=no
+#
+# showfreg: yes/no control display of linear regression of frequency values
+showfreg=no
+#
+# timebase: dynamic/ISO_DATE_TIME point of zero for linear regression
+# ISO_DATE_TIME: yyyy-mm-dd_hh:mm:ss.ms
+# values are interpreted using local time zone
+# parts omitted from front default to current date/time
+# parts omitted from end default to lowest permitted values
+# to get aa:bb being interpreted as minutes:seconds use aa:bb.0
+# for dynamic '00:00:00.0 of current day' is used
+timebase=dynamic
+#
+# freqbase: dynamic/<baseval>
+# if a number is given, subtract this from sampling values for display
+# if dynamic is selected, freqbase is adjusted to fit into the range of
+# offset values
+freqbase=dynamic
+#
+# cmplscale: dynamic/<scaling>
+# if a number is given, the sampling values are divided by this number
+# if dynamic is selected, cmplscale is adjusted to fit into the range of
+# offset values
+cmplscale=dynamic
+#
+# DumbScale: 0/1
+# 0 enables dynamic adjust of value ranges for freqbase and cmplscale
+# timescale is labeled with human readable times
+# 1 only uses explicit scaling for numbers
+# timescale is labeled with hours relative to timebase
+DumbScale=0
+#
+# StartTime: none/ISO_DATE_TIME
+# ignore any samples before the specified date
+StartTime=none
+#
+# EndTime: none/ISO_DATE_TIME
+# ignore any samples after the specified date
+#
+# if both StartTime and EndTime are specified
+# the value specified for samples is ignored
+EndTime=none
+#
+# MaxOffs: none/<number>
+# limit display (y-axis) to values not larger than <number>
+MaxOffset=none
+#
+# MinOffs: none/<number>
+# limit display (y-axis) to values not smaller than <number>
+MinOffset=none
+
+#
+# verbose: <number>
+# specify level for debugging
+# default is 0 for printing and 1 for monitoring
+# level 1 will just print a timestamp for any display update
+# (this is every delay seconds)
+verbose=1
+#
+# deltaT: <seconds>
+# mark `holes' in the sample data grater than <seconds>
+# by a break in the plot
+# default: 512 seconds
+deltaT=512
diff --git a/usr.sbin/xntpd/scripts/monitoring/lr.pl b/usr.sbin/xntpd/scripts/monitoring/lr.pl
new file mode 100755
index 0000000..02c7550
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/monitoring/lr.pl
@@ -0,0 +1,145 @@
+;#
+;# lr.pl,v 3.1 1993/07/06 01:09:08 jbj Exp
+;#
+;#
+;# Linear Regression Package for perl
+;# to be 'required' from perl
+;#
+;# Copyright (c) 1992
+;# Frank Kardel, Rainer Pruy
+;# Friedrich-Alexander Universitaet Erlangen-Nuernberg
+;#
+;#
+;#############################################################
+
+##
+## y = A + Bx
+##
+## B = (n * Sum(xy) - Sum(x) * Sum(y)) / (n * Sum(x^2) - Sum(x)^2)
+##
+## A = (Sum(y) - B * Sum(x)) / n
+##
+
+##
+## interface
+##
+*lr_init = *lr'lr_init; #';# &lr_init(tag); initialize data set for tag
+*lr_sample = *lr'lr_sample; #';# &lr_sample(x,y,tag); enter sample
+*lr_Y = *lr'lr_Y; #';# &lr_Y(x,tag); compute y for given x
+*lr_X = *lr'lr_X; #';# &lr_X(y,tag); compute x for given y
+*lr_r = *lr'lr_r; #';# &lr_r(tag); regression coeffizient
+*lr_cov = *lr'lr_cov; #';# &lr_cov(tag); covariance
+*lr_A = *lr'lr_A; #';# &lr_A(tag);
+*lr_B = *lr'lr_B; #';# &lr_B(tag);
+*lr_sigma = *lr'lr_sigma; #';# &lr_sigma(tag); standard deviation
+*lr_mean = *lr'lr_mean; #';# &lr_mean(tag);
+#########################
+
+package lr;
+
+sub tagify
+{
+ local($tag) = @_;
+ if (defined($tag))
+ {
+ *lr_n = eval "*${tag}_n";
+ *lr_sx = eval "*${tag}_sx";
+ *lr_sx2 = eval "*${tag}_sx2";
+ *lr_sxy = eval "*${tag}_sxy";
+ *lr_sy = eval "*${tag}_sy";
+ *lr_sy2 = eval "*${tag}_sy2";
+ }
+}
+
+sub lr_init
+{
+ &tagify($_[$[]) if defined($_[$[]);
+
+ $lr_n = 0;
+ $lr_sx = 0.0;
+ $lr_sx2 = 0.0;
+ $lr_sxy = 0.0;
+ $lr_sy = 0.0;
+ $lr_sy2 = 0.0;
+}
+
+sub lr_sample
+{
+ local($_x, $_y) = @_;
+
+ &tagify($_[$[+2]) if defined($_[$[+2]);
+
+ $lr_n++;
+ $lr_sx += $_x;
+ $lr_sy += $_y;
+ $lr_sxy += $_x * $_y;
+ $lr_sx2 += $_x**2;
+ $lr_sy2 += $_y**2;
+}
+
+sub lr_B
+{
+ &tagify($_[$[]) if defined($_[$[]);
+
+ return 1 unless ($lr_n * $lr_sx2 - $lr_sx**2);
+ return ($lr_n * $lr_sxy - $lr_sx * $lr_sy) / ($lr_n * $lr_sx2 - $lr_sx**2);
+}
+
+sub lr_A
+{
+ &tagify($_[$[]) if defined($_[$[]);
+
+ return ($lr_sy - &lr_B * $lr_sx) / $lr_n;
+}
+
+sub lr_Y
+{
+ &tagify($_[$[]) if defined($_[$[]);
+
+ return &lr_A + &lr_B * $_[$[];
+}
+
+sub lr_X
+{
+ &tagify($_[$[]) if defined($_[$[]);
+
+ return ($_[$[] - &lr_A) / &lr_B;
+}
+
+sub lr_r
+{
+ &tagify($_[$[]) if defined($_[$[]);
+
+ local($s) = ($lr_n * $lr_sx2 - $lr_sx**2) * ($lr_n * $lr_sy2 - $lr_sy**2);
+
+ return 1 unless $s;
+
+ return ($lr_n * $lr_sxy - $lr_sx * $lr_sy) / sqrt($s);
+}
+
+sub lr_cov
+{
+ &tagify($_[$[]) if defined($_[$[]);
+
+ return ($lr_sxy - $lr_sx * $lr_sy / $lr_n) / ($lr_n - 1);
+}
+
+sub lr_sigma
+{
+ &tagify($_[$[]) if defined($_[$[]);
+
+ return 0 if $lr_n <= 1;
+ return sqrt(($lr_sy2 - ($lr_sy * $lr_sy) / $lr_n) / ($lr_n));
+}
+
+sub lr_mean
+{
+ &tagify($_[$[]) if defined($_[$[]);
+
+ return 0 if $lr_n <= 0;
+ return $lr_sy / $lr_n;
+}
+
+&lr_init();
+
+1;
diff --git a/usr.sbin/xntpd/scripts/monitoring/ntp.pl b/usr.sbin/xntpd/scripts/monitoring/ntp.pl
new file mode 100755
index 0000000..f3bfd2b
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/monitoring/ntp.pl
@@ -0,0 +1,477 @@
+#!/local/bin/perl
+;#
+;# ntp.pl,v 3.1 1993/07/06 01:09:09 jbj Exp
+;#
+;# process loop filter statistics file and either
+;# - show statistics periodically using gnuplot
+;# - or print a single plot
+;#
+;# Copyright (c) 1992
+;# Rainer Pruy Friedrich-Alexander Universitaet Erlangen-Nuernberg
+;#
+;#
+;#############################################################
+
+package ntp;
+
+$NTP_version = 2;
+$ctrl_mode=6;
+
+$byte1 = (($NTP_version & 0x7)<< 3) & 0x34 | ($ctrl_mode & 0x7);
+$MAX_DATA = 468;
+
+$sequence = 0; # initial sequence number incred before used
+$pad=4;
+$do_auth=0; # no possibility today
+$keyid=0;
+;#list if known keys (passwords)
+%KEYS = ( 0, "\200\200\200\200\200\200\200\200",
+ );
+
+;#-----------------------------------------------------------------------------
+;# access routines for ntp control packet
+ ;# NTP control message format
+ ;# C LI|VN|MODE LI 2bit=00 VN 3bit=2(3) MODE 3bit=6 : $byte1
+ ;# C R|E|M|Op R response E error M more Op opcode
+ ;# n sequence
+ ;# n status
+ ;# n associd
+ ;# n offset
+ ;# n count
+ ;# a+ data (+ padding)
+ ;# optional authentication data
+ ;# N key
+ ;# N2 checksum
+
+;# first bye of packet
+sub pkt_LI { return ($_[$[] >> 6) & 0x3; }
+sub pkt_VN { return ($_[$[] >> 3) & 0x7; }
+sub pkt_MODE { return ($_[$[] ) & 0x7; }
+
+;# second byte of packet
+sub pkt_R { return ($_[$[] & 0x80) == 0x80; }
+sub pkt_E { return ($_[$[] & 0x40) == 0x40; }
+sub pkt_M { return ($_[$[] & 0x20) == 0x20; }
+sub pkt_OP { return $_[$[] & 0x1f; }
+
+;#-----------------------------------------------------------------------------
+
+sub setkey
+{
+ local($id,$key) = @_;
+
+ $KEYS{$id} = $key if (defined($key));
+ if (! defined($KEYS{$id}))
+ {
+ warn "Key $id not yet specified - key not changed\n";
+ return undef;
+ }
+ return ($keyid,$keyid = $id)[$[];
+}
+
+;#-----------------------------------------------------------------------------
+sub numerical { $a <=> $b; }
+
+;#-----------------------------------------------------------------------------
+
+sub send #'
+{
+ local($fh,$opcode, $associd, $data,$address) = @_;
+ $fh = caller(0)."'$fh";
+
+ local($junksize,$junk,$packet,$offset,$ret);
+ $offset = 0;
+
+ $sequence++;
+ while(1)
+ {
+ $junksize = length($data);
+ $junksize = $MAX_DATA if $junksize > $MAX_DATA;
+
+ ($junk,$data) = $data =~ /^(.{$junksize})(.*)$/;
+ $packet
+ = pack("C2n5a".(($junk eq "") ? 0 : &pad($junksize+12,$pad)-12),
+ $byte1,
+ ($opcode & 0x1f) | ($data ? 0x20 : 0),
+ $sequence,
+ 0, $associd,
+ $offset, $junksize, $junk);
+ if ($do_auth)
+ {
+ ;# not yet
+ }
+ $offset += $junksize;
+
+ if (defined($address))
+ {
+ $ret = send($fh, $packet, 0, $address);
+ }
+ else
+ {
+ $ret = send($fh, $packet, 0);
+ }
+
+ if (! defined($ret))
+ {
+ warn "send failed: $!\n";
+ return undef;
+ }
+ elsif ($ret != length($packet))
+ {
+ warn "send failed: sent only $ret from ".length($packet). "bytes\n";
+ return undef;
+ }
+ return $sequence unless $data;
+ }
+}
+
+;#-----------------------------------------------------------------------------
+;# status interpretation
+;#
+sub getval
+{
+ local($val,*list) = @_;
+
+ return $list{$val} if defined($list{$val});
+ return sprintf("%s#%d",$list{"-"},$val) if defined($list{"-"});
+ return "unknown-$val";
+}
+
+;#---------------------------------
+;# system status
+;#
+;# format: |LI|CS|SECnt|SECode| LI=2bit CS=6bit SECnt=4bit SECode=4bit
+sub ssw_LI { return ($_[$[] >> 14) & 0x3; }
+sub ssw_CS { return ($_[$[] >> 8) & 0x3f; }
+sub ssw_SECnt { return ($_[$[] >> 4) & 0xf; }
+sub ssw_SECode { return $_[$[] & 0xf; }
+
+%LI = ( 0, "leap_none", 1, "leap_add_sec", 2, "leap_del_sec", 3, "sync_alarm", "-", "leap");
+%ClockSource = (0, "sync_unspec",
+ 1, "sync_lf_clock",
+ 2, "sync_uhf_clock",
+ 3, "sync_hf_clock",
+ 4, "sync_local_proto",
+ 5, "sync_ntp",
+ 6, "sync_udp/time",
+ 7, "sync_wristwatch",
+ "-", "ClockSource",
+ );
+
+%SystemEvent = (0, "event_unspec",
+ 1, "event_restart",
+ 2, "event_fault",
+ 3, "event_sync_chg",
+ 4, "event_sync/strat_chg",
+ 5, "event_clock_reset",
+ 6, "event_bad_date",
+ 7, "event_clock_excptn",
+ "-", "event",
+ );
+sub LI
+{
+ &getval(&ssw_LI($_[$[]),*LI);
+}
+sub ClockSource
+{
+ &getval(&ssw_CS($_[$[]),*ClockSource);
+}
+
+sub SystemEvent
+{
+ &getval(&ssw_SECode($_[$[]),*SystemEvent);
+}
+
+sub system_status
+{
+ return sprintf("%s, %s, %d event%s, %s", &LI($_[$[]), &ClockSource($_[$[]),
+ &ssw_SECnt($_[$[]), ((&ssw_SECnt($_[$[])==1) ? "" : "s"),
+ &SystemEvent($_[$[]));
+}
+;#---------------------------------
+;# peer status
+;#
+;# format: |PStat|PSel|PCnt|PCode| Pstat=6bit PSel=2bit PCnt=4bit PCode=4bit
+sub psw_PStat_config { return ($_[$[] & 0x8000) == 0x8000; }
+sub psw_PStat_authenable { return ($_[$[] & 0x4000) == 0x4000; }
+sub psw_PStat_authentic { return ($_[$[] & 0x2000) == 0x2000; }
+sub psw_PStat_reach { return ($_[$[] & 0x1000) == 0x1000; }
+sub psw_PStat_sane { return ($_[$[] & 0x0800) == 0x0800; }
+sub psw_PStat_dispok { return ($_[$[] & 0x0400) == 0x0400; }
+sub psw_PStat { return ($_[$[] >> 10) & 0x3f; }
+sub psw_PSel { return ($_[$[] >> 8) & 0x3; }
+sub psw_PCnt { return ($_[$[] >> 4) & 0xf; }
+sub psw_PCode { return $_[$[] & 0xf; }
+
+%PeerSelection = (0, "sel_reject",
+ 1, "sel_candidate",
+ 2, "sel_selcand",
+ 3, "sel_sys.peer",
+ "-", "PeerSel",
+ );
+%PeerEvent = (0, "event_unspec",
+ 1, "event_ip_err",
+ 2, "event_authen",
+ 3, "event_unreach",
+ 4, "event_reach",
+ 5, "event_clock_excptn",
+ 6, "event_stratum_chg",
+ "-", "event",
+ );
+
+sub PeerSelection
+{
+ &getval(&psw_PSel($_[$[]),*PeerSelection);
+}
+sub PeerEvent
+{
+ &getval(&psw_PCode($_[$[]),*PeerEvent);
+}
+
+sub peer_status
+{
+ local($x) = ("");
+ $x .= "config," if &psw_PStat_config($_[$[]);
+ $x .= "authenable," if &psw_PStat_authenable($_[$[]);
+ $x .= "authentic," if &psw_PStat_authentic($_[$[]);
+ $x .= "reach," if &psw_PStat_reach($_[$[]);
+ $x .= &psw_PStat_sane($_[$[]) ? "sane," : "insane,";
+ $x .= "hi_disp," unless &psw_PStat_dispok($_[$[]);
+
+ $x .= sprintf(" %s, %d event%s, %s", &PeerSelection($_[$[]),
+ &psw_PCnt($_[$[]), ((&psw_PCnt($_[$[]) == 1) ? "" : "s"),
+ &PeerEvent($_[$[]));
+ return $x;
+}
+
+;#---------------------------------
+;# clock status
+;#
+;# format: |CStat|CEvnt| CStat=8bit CEvnt=8bit
+sub csw_CStat { return ($_[$[] >> 8) & 0xff; }
+sub csw_CEvnt { return $_[$[] & 0xff; }
+
+%ClockStatus = (0, "clk_nominal",
+ 1, "clk_timeout",
+ 2, "clk_badreply",
+ 3, "clk_fault",
+ 4, "clk_prop",
+ 5, "clk_baddate",
+ 6, "clk_badtime",
+ "-", "clk",
+ );
+
+sub clock_status
+{
+ return sprintf("%s, last %s",
+ &getval(&csw_CStat($_[$[]),*ClockStatus),
+ &getval(&csw_CEvnt($_[$[]),*ClockStatus));
+}
+
+;#---------------------------------
+;# error status
+;#
+;# format: |Err|reserved| Err=8bit
+;#
+sub esw_Err { return ($_[$[] >> 8) & 0xff; }
+
+%ErrorStatus = (0, "err_unspec",
+ 1, "err_auth_fail",
+ 2, "err_invalid_fmt",
+ 3, "err_invalid_opcode",
+ 4, "err_unknown_assoc",
+ 5, "err_unknown_var",
+ 6, "err_invalid_value",
+ 7, "err_adm_prohibit",
+ );
+
+sub error_status
+{
+ return sprintf("%s", &getval(&esw_Err($_[$[]),*ErrorStatus));
+}
+
+;#-----------------------------------------------------------------------------
+;#
+;# cntrl op name translation
+
+%CntrlOpName = (1, "read_status",
+ 2, "read_variables",
+ 3, "write_variables",
+ 4, "read_clock_variables",
+ 5, "write_clock_variables",
+ 6, "set_trap",
+ 7, "trap_response",
+ 31, "unset_trap", # !!! unofficial !!!
+ "-", "cntrlop",
+ );
+
+sub cntrlop_name
+{
+ return &getval($_[$[],*CntrlOpName);
+}
+
+;#-----------------------------------------------------------------------------
+
+$STAT_short_pkt = 0;
+$STAT_pkt = 0;
+
+;# process a NTP control message (response) packet
+;# returns a list ($ret,$data,$status,$associd,$op,$seq,$auth_keyid)
+;# $ret: undef --> not yet complete
+;# "" --> complete packet received
+;# "ERROR" --> error during receive, bad packet, ...
+;# else --> error packet - list may contain useful info
+
+
+sub handle_packet
+{
+ local($pkt,$from) = @_; # parameters
+ local($len_pkt) = (length($pkt));
+;# local(*FRAGS,*lastseen);
+ local($li_vn_mode,$r_e_m_op,$seq,$status,$associd,$offset,$count,$data);
+ local($autch_keyid,$auth_cksum);
+
+ $STAT_pkt++;
+ if ($len_pkt < 12)
+ {
+ $STAT_short_pkt++;
+ return ("ERROR","short packet received");
+ }
+
+ ;# now break packet apart
+ ($li_vn_mode,$r_e_m_op,$seq,$status,$associd,$offset,$count,$data) =
+ unpack("C2n5a".($len_pkt-12),$pkt);
+ $data=substr($data,$[,$count);
+ if ((($len_pkt - 12) - &pad($count,4)) >= 12)
+ {
+ ;# looks like an authenticator
+ ($auth_keyid,$auth_cksum) =
+ unpack("Na8",substr($pkt,$len_pkt-12+$[,12));
+ $STAT_auth++;
+ ;# no checking of auth_cksum (yet ?)
+ }
+
+ if (&pkt_VN($li_vn_mode) != $NTP_version)
+ {
+ $STAT_bad_version++;
+ return ("ERROR","version ".&pkt_VN($li_vn_mode)."packet ignored");
+ }
+
+ if (&pkt_MODE($li_vn_mode) != $ctrl_mode)
+ {
+ $STAT_bad_mode++;
+ return ("ERROR", "mode ".&pkt_MODE($li_vn_mode)." packet ignored");
+ }
+
+ ;# handle single fragment fast
+ if ($offset == 0 && &pkt_M($r_e_m_op) == 0)
+ {
+ $STAT_single_frag++;
+ if (&pkt_E($r_e_m_op))
+ {
+ $STAT_err_pkt++;
+ return (&error_status($status),
+ $data,$status,$associd,&pkt_OP($r_e_m_op),$seq,
+ $auth_keyid);
+ }
+ else
+ {
+ return ("",
+ $data,$status,$associd,&pkt_OP($r_e_m_op),$seq,
+ $auth_keyid);
+ }
+ }
+ else
+ {
+ ;# fragment - set up local name space
+ $id = "$from$seq".&pkt_OP($r_e_m_op);
+ $ID{$id} = 1;
+ *FRAGS = "$id FRAGS";
+ *lastseen = "$id lastseen";
+
+ $STAT_frag++;
+
+ $lastseen = 1 if !&pkt_M($r_e_m_op);
+ if (!defined(%FRAGS))
+ {
+ (&pkt_M($r_e_m_op) ? " more" : "")."\n";
+ $FRAGS{$offset} = $data;
+ ;# save other info
+ @FRAGS = ($status,$associd,&pkt_OP($r_e_m_op),$seq,$auth_keyid,$r_e_m_op);
+ }
+ else
+ {
+ (&pkt_M($r_e_m_op) ? " more" : "")."\n";
+ ;# add frag to previous - combine on the fly
+ if (defined($FRAGS{$offset}))
+ {
+ $STAT_dup_frag++;
+ return ("ERROR","duplicate fragment at $offset seq=$seq");
+ }
+
+ $FRAGS{$offset} = $data;
+
+ undef($loff);
+ foreach $off (sort numerical keys(%FRAGS))
+ {
+ next unless defined($FRAGS{$off});
+ if (defined($loff) &&
+ ($loff + length($FRAGS{$loff})) == $off)
+ {
+ $FRAGS{$loff} .= $FRAGS{$off};
+ delete $FRAGS{$off};
+ last;
+ }
+ $loff = $off;
+ }
+
+ ;# return packet if all frags arrived
+ ;# at most two frags with possible padding ???
+ if ($lastseen && defined($FRAGS{0}) &&
+ scalar(@x=sort numerical keys(%FRAGS)) <= 2 &&
+ (length($FRAGS{0}) + 8) > $x[$[+1])
+ {
+ @x=((&pkt_E($r_e_m_op) ? &error_status($status) : ""),
+ $FRAGS{0},@FRAGS);
+ &pkt_E($r_e_m_op) ? $STAT_err_frag++ : $STAT_frag_all++;
+ undef(%FRAGS);
+ undef(@FRAGS);
+ undef($lastseen);
+ delete $ID{$id};
+ &main'clear_timeout($id);
+ return @x;
+ }
+ else
+ {
+ &main'set_timeout($id,time+$timeout,"&ntp'handle_packet_timeout(\"".unpack("H*",$id)."\");"); #'";
+ }
+ }
+ return (undef);
+ }
+}
+
+sub handle_packet_timeout
+{
+ local($id) = @_;
+ local($r_e_m_op,*FRAGS,*lastseen,@x) = (@FRAGS[$[+5]);
+
+ *FRAGS = "$id FRAGS";
+ *lastseen = "$id lastseen";
+
+ @x=((&pkt_E($r_e_m_op) ? &error_status($status) : "TIMEOUT"),
+ $FRAGS{0},@FRAGS[$[ .. $[+4]);
+ $STAT_frag_timeout++;
+ undef(%FRAGS);
+ undef(@FRAGS);
+ undef($lastseen);
+ delete $ID{$id};
+ return @x;
+}
+
+
+sub pad
+{
+ return $_[$[+1] * int(($_[$[] + $_[$[+1] - 1) / $_[$[+1]);
+}
+
+1;
diff --git a/usr.sbin/xntpd/scripts/monitoring/ntploopstat b/usr.sbin/xntpd/scripts/monitoring/ntploopstat
new file mode 100755
index 0000000..75cdff2
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/monitoring/ntploopstat
@@ -0,0 +1,457 @@
+#!/local/bin/perl -w--*-perl-*-
+;#
+;# ntploopstat,v 3.1 1993/07/06 01:09:11 jbj Exp
+;#
+;# Poll NTP server using NTP mode 7 loopinfo request.
+;# Log info and timestamp to file for processing by ntploopwatch.
+;#
+;#
+;# Copyright (c) 1992
+;# Rainer Pruy Friedrich-Alexander Universitaet Erlangen-Nuernberg
+;#
+;#################################################################
+;#
+;# The format written to the logfile is the same as used by xntpd
+;# for the loopstats file.
+;# This script however allows to gather loop filter statistics from
+;# remote servers where you do not have access to the loopstats logfile.
+;#
+;# Please note: Communication delays affect the accuracy of the
+;# timestamps recorded. Effects from these delays will probably
+;# not show up, as timestamps are recorded to the second only.
+;# (Should have implemented &gettimeofday()..)
+;#
+
+$0 =~ s!^.*/([^/]+)$!\1!; # beautify script name
+
+$ntpserver = 'localhost'; # default host to poll
+$delay = 60; # default sampling rate
+ ;# keep it shorter than minpoll (=64)
+ ;# to get all values
+
+require "ctime.pl";
+;# handle bug in early ctime distributions
+$ENV{'TZ'} = 'MET' unless defined($ENV{'TZ'}) || $] > 4.010;
+
+if (defined(@ctime'MoY))
+{
+ *MonthName = *ctime'MoY;
+}
+else
+{
+ @MonthName = ('Jan','Feb','Mar','Apr','May','Jun',
+ 'Jul','Aug','Sep','Oct','Nov','Dec');
+}
+
+;# this routine can be redefined to point to syslog if necessary
+sub msg
+{
+ return unless $verbose;
+
+ print STDERR "$0: ";
+ printf STDERR @_;
+}
+
+;#############################################################
+;#
+;# process command line
+$usage = <<"E-O-S";
+
+usage:
+ $0 [-d<delay>] [-t<timeout>] [-l <logfile>] [-v] [ntpserver]
+E-O-S
+
+while($_ = shift)
+{
+ /^-v(\d*)$/ && ($verbose=($1 eq '') ? 1 : $1,1) && next;
+ /^-d(\d*)$/ &&
+ do {
+ ($1 ne '') && ($delay = $1,1) && next;
+ @ARGV || die("$0: delay value missing after -d\n$usage");
+ $delay = shift;
+ ($delay >= 0) || die("$0: bad delay value \"$delay\"\n$usage");
+ next;
+ };
+ /^-l$/ &&
+ do {
+ @ARGV || die("$0: logfile missing after -l\n$usage");
+ $logfile = shift;
+ next;
+ };
+ /^-t(\d*(\.\d*)?)$/ &&
+ do {
+ ($1 ne '') && ($timeout = $1,1) && next;
+ @ARGV || die("$0: timeout value missing after -t\n$usage\n");
+ $timeout = shift;
+ ($timeout > 0) ||
+ die("$0: bad timeout value \"$timeout\"\n$usage");
+ next;
+ };
+
+ /^-/ && die("$0: unknown option \"$_\"\n$usage");
+
+ ;# any other argument is server to poll
+ $ntpserver = $_;
+ last;
+}
+
+if (@ARGV)
+{
+ warn("unexpected arguments: ".join(" ",@ARGV).".\n");
+ die("$0: too many servers specified\n$usage");
+}
+
+;# logfile defaults to include server name
+;# The name of the current month is appended and
+;# the file is opened and closed for each sample.
+;#
+$logfile = "loopstats:$ntpserver." unless defined($logfile);
+$timeout = 12.0 unless defined($timeout); # wait $timeout seconds for reply
+
+$MAX_FAIL = 60; # give up after $MAX_FAIL failed polls
+
+
+$MJD_1970 = 40587;
+
+if (eval 'require "syscall.ph";')
+{
+ if (defined(&SYS_gettimeofday))
+ {
+ ;# assume standard
+ ;# gettimeofday(struct timeval *tp,struct timezone *tzp)
+ ;# syntax for gettimeofday syscall
+ ;# tzp = NULL -> undef
+ ;# tp = (long,long)
+ eval 'sub time { local($tz) = pack("LL",0,0);
+ (&msg("gettimeofday failed: $!\n"),
+ return (time))
+ unless syscall(&SYS_gettimeofday,$tz,undef) == 0;
+ local($s,$us) = unpack("LL",$tz);
+ return $s + $us/1000000; }';
+ local($t1,$t2,$t3);
+ $t1 = time;
+ eval '$t2 = &time;';
+ $t3 = time;
+ die("$0: gettimeofday failed: $@.\n") if defined($@) && $@;
+ die("$0: gettimeofday inconsistency time=$t1,gettimeofday=$t2,time=$t2\n")
+ if (int($t1) != int($t2) && int($t3) != int($t2));
+ &msg("Using gettimeofday for timestamps\n");
+ }
+ else
+ {
+ warn("No gettimeofday syscall found - using time builtin for timestamps\n");
+ eval 'sub time { return time; }';
+ }
+}
+else
+{
+ warn("No syscall.ph file found - using time builtin for timestamps\n");
+ eval 'sub time { return time; }';
+}
+
+
+;#------------------+
+;# from ntp_request.h
+;#------------------+
+
+;# NTP mode 7 packet format:
+;# Byte 1: ResponseBit MoreBit Version(3bit) Mode(3bit)==7
+;# Byte 2: AuthBit Sequence # - 0 - 127 see MoreBit
+;# Byte 3: Implementation #
+;# Byte 4: Request Code
+;#
+;# Short 1: Err(3bit) NumItems(12bit)
+;# Short 2: MBZ(3bit)=0 DataItemSize(12bit)
+;# 0 - 500 byte Data
+;# if AuthBit is set:
+;# Long: KeyId
+;# 2xLong: AuthCode
+
+;#
+$IMPL_XNTPD = 2;
+$REQ_LOOP_INFO = 8;
+
+
+;# request packet for REQ_LOOP_INFO:
+;# B1: RB=0 MB=0 V=2 M=7
+;# B2: S# = 0
+;# B3: I# = IMPL_XNTPD
+;# B4: RC = REQ_LOOP_INFO
+;# S1: E=0 NI=0
+;# S2: MBZ=0 DIS=0
+;# data: 32 byte 0 padding
+;# 8byte timestamp if encryption, 0 padding otherwise
+$loopinfo_reqpkt =
+ pack("CCCC nn x32 x8", 0x17, 0, $IMPL_XNTPD, $REQ_LOOP_INFO, 0, 0);
+
+;# ignore any auth data in packets
+$loopinfo_response_size =
+ 1+1+1+1+2+2 # header size like request pkt
+ + 8 # l_fp last_offset
+ + 8 # l_fp drift_comp
+ + 4 # u_long compliance
+ + 4 # u_long watchdog_timer
+ ;
+$loopinfo_response_fmt = "C4n2N2N2NN";
+$loopinfo_response_fmt_v2 = "C4n2N2N2N2N";
+
+;#
+;# prepare connection to server
+;#
+
+;# workaround for broken socket.ph on dynix_ptx
+eval 'sub INTEL {1;}' unless defined(&INTEL);
+eval 'sub ATT {1;}' unless defined(&ATT);
+
+require "sys/socket.ph";
+
+require 'netinet/in.ph';
+
+;# if you do not have netinet/in.ph enable the following lines
+;#eval 'sub INADDR_ANY { 0x00000000; }' unless defined(&INADDR_ANY);
+;#eval 'sub IPPRORO_UDP { 17; }' unless defined(&IPPROTO_UDP);
+
+if ($ntpserver =~ /^((0x?)?\w+)\.((0x?)?\w+)\.((0x?)?\w+)\.((0x?)?\w+)$/)
+{
+ local($a,$b,$c,$d) = ($1,$3,$5,$7);
+ $a = oct($a) if defined($2);
+ $b = oct($b) if defined($4);
+ $c = oct($c) if defined($6);
+ $d = oct($d) if defined($8);
+ $server_addr = pack("C4", $a,$b,$c,$d);
+
+ $server_mainname
+ = (gethostbyaddr($server_addr,&AF_INET))[$[] || $ntpserver;
+}
+else
+{
+ ($server_mainname,$server_addr)
+ = (gethostbyname($ntpserver))[$[,$[+4];
+
+ die("$0: host \"$ntpserver\" is unknown\n")
+ unless defined($server_addr);
+}
+&msg ("Address of server \"$ntpserver\" is \"%d.%d.%d.%d\"\n",
+ unpack("C4",$server_addr));
+
+$proto_udp = (getprotobyname('udp'))[$[+2] || &IPPROTO_UDP;
+
+$ntp_port =
+ (getservbyname('ntp','udp'))[$[+2] ||
+ (warn "Could not get port number for service \"ntp/udp\" using 123\n"),
+ ($ntp_port=123);
+
+;#
+0 && &SOCK_DGRAM; # satisfy perl -w ...
+socket(S, &AF_INET, &SOCK_DGRAM, $proto_udp) ||
+ die("Cannot open socket: $!\n");
+
+bind(S, pack("S n N x8", &AF_INET, 0, &INADDR_ANY)) ||
+ die("Cannot bind: $!\n");
+
+($my_port, $my_addr) = (unpack("S n a4 x8",getsockname(S)))[$[+1,$[+2];
+
+&msg("Listening at address %d.%d.%d.%d port %d\n",
+ unpack("C4",$my_addr), $my_port);
+
+$server_inaddr = pack("Sna4x8", &AF_INET, $ntp_port, $server_addr);
+
+;############################################################
+;#
+;# the main loop:
+;# send request
+;# get reply
+;# wait til next sample time
+
+undef($lasttime);
+$lostpacket = 0;
+
+while(1)
+{
+ $stime = &time;
+
+ &msg("Sending request $stime...\n");
+
+ $ret = send(S,$loopinfo_reqpkt,0,$server_inaddr);
+
+ if (! defined($ret) || $ret < length($loopinfo_reqpkt))
+ {
+ warn("$0: send failed ret=($ret): $!\n");
+ $fail++;
+ next;
+ }
+
+ &msg("Waiting for reply...\n");
+
+ $mask = ""; vec($mask,fileno(S),1) = 1;
+ $ret = select($mask,undef,undef,$timeout);
+
+ if (! defined($ret))
+ {
+ warn("$0: select failed: $!\n");
+ $fail++;
+ next;
+ }
+ elsif ($ret == 0)
+ {
+ warn("$0: request to $ntpserver timed out ($timeout seconds)\n");
+ ;# do not count this event as failure
+ ;# it usually this happens due to dropped udp packets on noisy and
+ ;# havily loaded lines, so just try again;
+ $lostpacket = 1;
+ next;
+ }
+
+ &msg("Receiving reply...\n");
+
+ $len = 520; # max size of a mode 7 packet
+ $reply = ""; # just make it defined for -w
+ $ret = recv(S,$reply,$len,0);
+
+ if (!defined($ret))
+ {
+ warn("$0: recv failed: $!\n");
+ $fail++;
+ next;
+ }
+
+ $etime = &time;
+ &msg("Received at\t$etime\n");
+
+ ;#$time = ($stime + $etime) / 2; # symmetric delay assumed
+ $time = $etime; # the above assumption breaks for X25
+ ;# so taking etime makes timestamps be a
+ ;# little late, but keeps them increasing
+ ;# monotonously
+
+ &msg(sprintf("Reply from %d.%d.%d.%d took %f seconds\n",
+ (unpack("SnC4",$ret))[$[+2 .. $[+5], ($etime - $stime)));
+
+ if ($len < $loopinfo_response_size)
+ {
+ warn("$0: short packet ($len bytes) received ($loopinfo_response_size bytes expected\n");
+ $fail++;
+ next;
+ }
+
+ ($b1,$b2,$b3,$b4,$s1,$s2,
+ $offset_i,$offset_f,$drift_i,$drift_f,$compl,$watchdog)
+ = unpack($loopinfo_response_fmt,$reply);
+
+ ;# check reply
+ if (($s1 >> 12) != 0) # error !
+ {
+ die("$0: got error reply ".($s1>>12)."\n");
+ }
+ if (($b1 != 0x97 && $b1 != 0x9f) || # Reply NotMore V=2 M=7
+ ($b2 != 0 && $b2 != 0x80) || # S=0 Auth no/yes
+ $b3 != $IMPL_XNTPD || # ! IMPL_XNTPD
+ $b4 != $REQ_LOOP_INFO || # Ehh.. not loopinfo reply ?
+ $s1 != 1 || # ????
+ ($s2 != 24 && $s2 != 28) #
+ )
+ {
+ warn("$0: Bad/unexpected reply from server:\n");
+ warn(" \"".unpack("H*",$reply)."\"\n");
+ warn(" ".sprintf("b1=%x b2=%x b3=%x b4=%x s1=%d s2=%d\n",
+ $b1,$b2,$b3,$b4,$s1,$s2));
+ $fail++;
+ next;
+ }
+ elsif ($s2 == 28)
+ {
+ ;# seems to be a version 2 xntpd
+ ($b1,$b2,$b3,$b4,$s1,$s2,
+ $offset_i,$offset_f,$drift_i,$drift_f,$compl_i,$compl_f,$watchdog)
+ = unpack($loopinfo_response_fmt_v2,$reply);
+ $compl = &lfptoa($compl_i, $compl_f);
+ }
+
+ $time -= $watchdog;
+
+ $offset = &lfptoa($offset_i, $offset_f);
+ $drift = &lfptoa($drift_i, $drift_f);
+
+ &log($time,$offset,$drift,$compl) && ($fail = 0);;
+}
+continue
+{
+ die("$0: Too many failures - terminating\n") if $fail > $MAX_FAIL;
+ &msg("Sleeping " . ($lostpacket ? ($delay / 2) : $delay) . " seconds...\n");
+
+ sleep($lostpacket ? ($delay / 2) : $delay);
+ $lostpacket = 0;
+}
+
+sub log
+{
+ local($time,$offs,$freq,$cmpl) = @_;
+ local($y,$m,$d);
+ local($fname,$suff) = ($logfile);
+
+
+ ;# silently drop sample if distance to last sample is too low
+ if (defined($lasttime) && ($lasttime + 2) >= $time)
+ {
+ &msg("Dropped packet - old sample\n");
+ return 1;
+ }
+
+ ;# $suff determines which samples end up in the same file
+ ;# could have used $year (;-) or WeekOfYear, DayOfYear,....
+ ;# Change it to your suit...
+
+ ($d,$m,$y) = (localtime($time))[$[+3 .. $[+5];
+ $suff = sprintf("%04d%02d%02d",$y+1900,$m+1,$d);
+ $fname .= $suff;
+ if (!open(LOG,">>$fname"))
+ {
+ warn("$0: open($fname) failed: $!\n");
+ $fail++;
+ return 0;
+ }
+ else
+ {
+ ;# file format
+ ;# MJD seconds offset drift compliance
+ printf LOG ("%d %.3lf %.8lf %.7lf %d\n",
+ int($time/86400)+$MJD_1970,
+ $time - int($time/86400) * 86400,
+ $offs,$freq,$cmpl);
+ close(LOG);
+ $lasttime = $time;
+ }
+ return 1;
+}
+
+;# see ntp_fp.h to understand this
+sub lfptoa
+{
+ local($i,$f) = @_;
+ local($sign) = 1;
+
+
+ if ($i & 0x80000000)
+ {
+ if ($f == 0)
+ {
+ $i = -$i;
+ }
+ else
+ {
+ $f = -$f;
+ $i = ~$i;
+ $i += 1; # 2s complement
+ }
+ $sign = -1;
+ ;#print "NEG: $i $f\n";
+ }
+ else
+ {
+ ;#print "POS: $i $f\n";
+ }
+ ;# unlike xntpd I have perl do the dirty work.
+ ;# Using floats here may affect precision, but
+ ;# currently these bits aren't significant anyway
+ return $sign * ($i + $f/2**32);
+}
diff --git a/usr.sbin/xntpd/scripts/monitoring/ntploopwatch b/usr.sbin/xntpd/scripts/monitoring/ntploopwatch
new file mode 100755
index 0000000..655ed71
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/monitoring/ntploopwatch
@@ -0,0 +1,1631 @@
+#!/local/bin/perl -w--*-perl-*-
+;#
+;# ntploopwatch,v 3.1 1993/07/06 01:09:13 jbj Exp
+;#
+;# process loop filter statistics file and either
+;# - show statistics periodically using gnuplot
+;# - or print a single plot
+;#
+;# Copyright (c) 1992
+;# Rainer Pruy Friedrich-Alexander Universitaet Erlangen-Nuernberg
+;#
+;#
+;#############################################################
+$0 =~ s!^.*/([^/]+)$!\1!;
+$F = ' ' x length($0);
+$|=1;
+
+$ENV{'SHELL'} = '/bin/sh'; # use bourne shell
+
+undef($config);
+undef($workdir);
+undef($PrintIt);
+undef($samples);
+undef($StartTime);
+undef($EndTime);
+($a,$b) if 0; # keep -w happy
+$usage = <<"E-O-P";
+usage:
+ to watch statistics permanently:
+ $0 [-v[<level>]] [-c <config-file>] [-d <working-dir>]
+ $F [-h <hostname>]
+
+ to get a single print out specify also
+ $F -P[<printer>] [-s<samples>]
+ $F [-S <start-time>] [-E <end-time>]
+ $F [-Y <MaxOffs>] [-y <MinOffs>]
+
+If You like long option names, You can use:
+ -help
+ -c +config
+ -d +directory
+ -h +host
+ -v +verbose[=<level>]
+ -P +printer[=<printer>]
+ -s +samples[=<samples>]
+ -S +starttime
+ -E +endtime
+ -Y +maxy
+ -y +miny
+
+If <printer> contains a '/' (slash character) output is directed to
+a file of this name instead of delivered to a printer.
+E-O-P
+
+;# add directory to look for lr.pl and timelocal.pl (in front of current list)
+unshift(@INC,"/src/NTP/v3/xntp/monitoring");
+
+require "lr.pl"; # linear regresion routines
+
+$MJD_1970 = 40587; # from ntp.h (V3)
+$RecordSize = 48; # usually a line fits into 42 bytes
+$MinClip = 0.12; # clip Y scales with greater range than this
+
+;# largest extension of Y scale from mean value, factor for standart deviation
+$FuzzLow = 2; # for side closer to zero
+$FuzzBig = 1; # for side farther from zero
+
+require "ctime.pl";
+require "timelocal.pl";
+;# early distributions of ctime.pl had a bug
+$ENV{'TZ'} = 'MET' unless defined $ENV{'TZ'} || $[ > 4.010;
+if (defined(@ctime'MoY))
+{
+ *Month=*ctime'MoY;
+ *Day=*ctime'DoW;
+}
+else
+{
+ @Month = ('Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec');
+ @Day = ('Sun','Mon','Tue','Wed','Thu','Fri','Sat');
+}
+;# max number of days per month
+@MaxNumDaysPerMonth = (31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31);
+
+;# config settable parameters
+$delay = 60;
+$srcprefix = "./var\@\$STATHOST/loopstats.";
+$showoffs = 1;
+$showfreq = 1;
+$showcmpl = 0;
+$showoreg = 0;
+$showfreg = 0;
+undef($timebase);
+undef($freqbase);
+undef($cmplscale);
+undef($MaxY);
+undef($MinY);
+$deltaT = 512; # indicate sample data gaps greater than $deltaT seconds
+$verbose = 1;
+
+while($_ = shift(@ARGV))
+{
+ (/^[+-]help$/) && die($usage);
+
+ (/^-c$/ || /^\+config$/) &&
+ (@ARGV || die($usage), $config = shift(@ARGV), next);
+
+ (/^-d$/ || /^\+directory$/) &&
+ (@ARGV || die($usage), $workdir = shift(@ARGV), next);
+
+ (/^-h$/ || /^\+host$/) &&
+ (@ARGV || die($usage), $STATHOST = shift, next);
+
+ (/^-v(\d*)$/ || /^\+verbose=?(\d*)$/) &&
+ ($verbose=($1 eq "") ? 1 : $1, next);
+
+ (/^-P(\S*)$/ || /^\+[Pp]rinter=?(\S*)$/) &&
+ ($PrintIt = $1, $verbose==1 && ($verbose = 0), next);
+
+ (/^-s(\d*)$/ || /^\+samples=?(\d*)$/) &&
+ (($samples = ($1 eq "") ? (shift || die($usage)): $1), next);
+
+ (/^-S$/ || /^\+[Ss]tart[Tt]ime$/) &&
+ (@ARGV || die($usage), $StartTime=&date_time_spec2seconds(shift),next);
+
+ (/^-E$/ || /^\+[Ee]nd[Tt]ime$/) &&
+ (@ARGV || die($usage), $EndTime = &date_time_spec2seconds(shift),next);
+
+ (/^-Y$/ || /^\+[Mm]ax[Yy]$/) &&
+ (@ARGV || die($usage), $MaxY = shift, next);
+
+ (/^-y$/ || /^\+[Mm]in[Yy]$/) &&
+ (@ARGV || die($usage), $MinY = shift, next);
+
+ die("$0: unexpected argument \"$_\"\n$usage");
+}
+
+if (defined($workdir))
+{
+ chdir($workdir) ||
+ die("$0: failed to change working dir to \"$workdir\": $!\n");
+}
+
+$PrintIt = "ps" if defined($PrintIt) && $PrintIt eq "";
+
+if (!defined($PrintIt))
+{
+ defined($samples) &&
+ print "WARNING: your samples value may be shadowed by config file settings\n";
+ defined($StartTime) &&
+ print "WARNING: your StartTime value may be shadowed by config file settings\n";
+ defined($EndTime) &&
+ print "WARNING: your EndTime value may be shadowed by config file settings\n";
+ defined($MaxY) &&
+ print "WARNING: your MaxY value may be shadowed by config file settings\n";
+ defined($MinY) &&
+ print "WARNING: your MinY value may be shadowed by config file settings\n";
+
+ ;# check operating environment
+ ;#
+ ;# gnuplot usually has X support
+ ;# I vaguely remember there was one with sunview support
+ ;#
+ ;# If Your plotcmd can display graphics using some other method
+ ;# (Tek window,..) fix the following test
+ ;# (or may be, just disable it)
+ ;#
+ !(defined($ENV{'DISPLAY'}) || defined($ENV{'WINDOW_PARENT'})) &&
+ die("Need window system to monitor statistics\n");
+}
+
+;# configuration file
+$config = "loopwatch.config" unless defined($config);
+($STATHOST = $config) =~ s!.*loopwatch\.config.([^/\.]*)$!\1!
+ unless defined($STATHOST);
+($STATTAG = $STATHOST) =~ s/^([^\.\*\s]+)\..*$/\1/;
+
+$srcprefix =~ s/\$STATHOST/$STATHOST/g;
+
+;# plot command
+@plotcmd=("gnuplot",
+ '-title', "Ntp loop filter statistics $STATHOST",
+ '-name', "NtpLoopWatch_$STATTAG");
+$tmpfile = "/tmp/ntpstat.$$";
+
+;# other variables
+$doplot = ""; # assembled command for @plotcmd to display plot
+undef($laststat);
+
+;# plot value ranges
+undef($mintime);
+undef($maxtime);
+undef($minoffs);
+undef($maxoffs);
+undef($minfreq);
+undef($maxfreq);
+undef($mincmpl);
+undef($maxcmpl);
+undef($miny);
+undef($maxy);
+
+;# stop operation if plot command dies
+sub sigchld
+{
+ local($pid) = wait;
+ unlink($tmpfile);
+ warn(sprintf("%s: %s died: exit status: %d signal %d\n",
+ $0,
+ (defined($Plotpid) && $Plotpid == $pid)
+ ? "plotcmd" : "unknown child $pid",
+ $?>>8,$? & 0xff)) if $?;
+ exit(1) if $? && defined($Plotpid) && $pid == $Plotpid;
+}
+&sigchld if 0;
+$SIG{'CHLD'} = "sigchld";
+$SIG{'CLD'} = "sigchld";
+
+sub abort
+{
+ unlink($tmpfile);
+ defined($Plotpid) && kill('TERM',$Plotpid);
+ die("$0: received signal SIG$_[$[] - exiting\n");
+}
+&abort if 0; # make -w happy - &abort IS used
+$SIG{'INT'} = $SIG{'HUP'} = $SIG{'QUIT'} = $SIG{'TERM'} = $SIG{'PIPE'} = "abort";
+
+;#
+sub abs
+{
+ ($_[$[] < 0) ? -($_[$[]) : $_[$[];
+}
+
+;#####################
+;# start of real work
+
+print "starting plot command (" . join(" ",@plotcmd) . ")\n" if $verbose > 1;
+
+$Plotpid = open(PLOT,"|-");
+select((select(PLOT),$|=1)[$[]); # make PLOT line bufferd
+
+defined($Plotpid) ||
+ die("$0: failed to start plot command: $!\n");
+
+unless ($Plotpid)
+{
+ ;# child == plot command
+ close(STDOUT);
+ open(STDOUT,">&STDERR") ||
+ die("$0: failed to redirect STDOUT of plot command: $!\n");
+
+ print STDOUT "plot command running as $$\n";
+
+ exec @plotcmd;
+ die("$0: failed to exec (@plotcmd): $!\n");
+ exit(1); # in case ...
+}
+
+sub read_config
+{
+ local($at) = (stat($config))[$[+9];
+ local($_,$c,$v);
+
+ (undef($laststat),(print("stat $config failed: $!\n")),return) if ! defined($at);
+ return if (defined($laststat) && ($laststat == $at));
+ $laststat = $at;
+
+ print "reading configuration from \"$config\"\n" if $verbose;
+
+ open(CF,"<$config") ||
+ (warn("$0: failed to read \"$config\" - using old settings ($!)\n"),
+ return);
+ while(<CF>)
+ {
+ chop;
+ s/^([^\#]*[^\#\s]?)\s*\#.*$//;
+ next if /^\s*$/;
+
+ s/^\s*([^=\s]*)\s*=\s*(.*\S)\s*$/\1=\2/;
+
+ ($c,$v) = split(/=/,$_,2);
+ print "processing \"$c=$v\"\n" if $verbose > 3;
+ ($c eq "delay") && ($delay = $v,1) && next;
+ ($c eq 'samples') && (!defined($PrintIt) || !defined($samples)) &&
+ ($samples = $v,1) && next;
+ ($c eq 'srcprefix') && (($srcprefix=$v)=~s/\$STATHOST/$STATHOST/g,1)
+ && next;
+ ($c eq 'showoffs') &&
+ ($showoffs = ($v eq 'yes' || $v eq 'y' || $v != 0),1) && next;
+ ($c eq 'showfreq') &&
+ ($showfreq = ($v eq 'yes' || $v eq 'y' || $v != 0),1) && next;
+ ($c eq 'showcmpl') &&
+ ($showcmpl = ($v eq 'yes' || $v eq 'y' || $v != 0),1) && next;
+ ($c eq 'showoreg') &&
+ ($showoreg = ($v eq 'yes' || $v eq 'y' || $v != 0),1) && next;
+ ($c eq 'showfreg') &&
+ ($showfreg = ($v eq 'yes' || $v eq 'y' || $v != 0),1) && next;
+
+ ($c eq 'exit') && (unlink($tmpfile),die("$0: exit by config request\n"));
+
+ ($c eq 'freqbase' ||
+ $c eq 'cmplscale') &&
+ do {
+ if (! defined($v) || $v eq "" || $v eq 'dynamic')
+ {
+ eval "undef(\$$c);";
+ }
+ else
+ {
+ eval "\$$c = \$v;";
+ }
+ next;
+ };
+ ($c eq 'timebase') &&
+ do {
+ if (! defined($v) || $v eq "" || $v eq "dynamic")
+ {
+ undef($timebase);
+ }
+ else
+ {
+ $timebase=&date_time_spec2seconds($v);
+ }
+ };
+ ($c eq 'EndTime') &&
+ do {
+ next if defined($EndTime) && defined($PrintIt);
+ if (! defined($v) || $v eq "" || $v eq "none")
+ {
+ undef($EndTime);
+ }
+ else
+ {
+ $EndTime=&date_time_spec2seconds($v);
+ }
+ };
+ ($c eq 'StartTime') &&
+ do {
+ next if defined($StartTime) && defined($PrintIt);
+ if (! defined($v) || $v eq "" || $v eq "none")
+ {
+ undef($StartTime);
+ }
+ else
+ {
+ $StartTime=&date_time_spec2seconds($v);
+ }
+ };
+
+ ($c eq 'MaxY') &&
+ do {
+ next if defined($MaxY) && defined($PrintIt);
+ if (! defined($v) || $v eq "" || $v eq "none")
+ {
+ undef($MaxY);
+ }
+ else
+ {
+ $MaxY=$v;
+ }
+ };
+
+ ($c eq 'MinY') &&
+ do {
+ next if defined($MinY) && defined($PrintIt);
+ if (! defined($v) || $v eq "" || $v eq "none")
+ {
+ undef($MinY);
+ }
+ else
+ {
+ $MinY=$v;
+ }
+ };
+
+ ($c eq 'deltaT') &&
+ do {
+ if (!defined($v) || $v eq "")
+ {
+ undef($deltaT);
+ }
+ else
+ {
+ $deltaT = $v;
+ }
+ next;
+ };
+ ($c eq 'verbose') && ! defined($PrintIt) &&
+ do {
+ if (!defined($v) || $v == 0)
+ {
+ $verbose = 0;
+ }
+ else
+ {
+ $verbose = $v;
+ }
+ next;
+ };
+ ;# otherwise: silently ignore unrecognized config line
+ }
+ close(CF);
+ ;# set show defaults when nothing selected
+ $showoffs = $showfreq = $showcmpl = 1
+ unless $showoffs || $showfreq || $showcmpl;
+ if ($verbose > 3)
+ {
+ print "new configuration:\n";
+ print " delay\t= $delay\n";
+ print " samples\t= $samples\n";
+ print " srcprefix\t= $srcprefix\n";
+ print " showoffs\t= $showoffs\n";
+ print " showfreq\t= $showfreq\n";
+ print " showcmpl\t= $showcmpl\n";
+ print " showoreg\t= $showoreg\n";
+ print " showfreg\t= $showfreg\n";
+ printf " timebase\t= %s",defined($timebase)?&ctime($timebase):"dynamic\n";
+ printf " freqbase\t= %s\n",defined($freqbase) ?"$freqbase":"dynamic";
+ printf " cmplscale\t= %s\n",defined($cmplscale)?"$cmplscale":"dynamic";
+ printf " StartTime\t= %s",defined($StartTime)?&ctime($StartTime):"none\n";
+ printf " EndTime\t= %s", defined($EndTime) ? &ctime($EndTime):"none\n";
+ printf " MaxY\t= %s",defined($MaxY)? $MaxY :"none\n";
+ printf " MinY\t= %s",defined($MinY)? $MinY :"none\n";
+ print " verbose\t= $verbose\n";
+ }
+print "configuration file read\n" if $verbose > 2;
+}
+
+sub make_doplot
+{
+ local($c) = ("");
+ local($fmt)
+ = ("%s \"%s\" using 1:%d title '%s <%lf %lf> %6s' with lines");
+ local($regfmt)
+ = ("%s ((%lf * x) + %lf) title 'lin. approx. %s (%f t[h]) %s %f <%f> %6s' with lines");
+
+ $doplot = " set title 'NTP loopfilter statistics for $STATHOST " .
+ "(last $LastCnt samples from $srcprefix*)'\n";
+
+ local($xts,$xte,$i,$t);
+
+ local($s,$c) = ("");
+
+ ;# number of integral seconds to get at least 12 tic marks on x axis
+ $t = int(($maxtime - $mintime) / 12 + 0.5);
+ $t = 1 unless $t; # prevent $t to be zero
+ foreach $i (30,
+ 60,5*60,15*60,30*60,
+ 60*60,2*60*60,6*60*60,12*60*60,
+ 24*60*60,48*60*60)
+ {
+ last if $t < $i;
+ $t = $t - ($t % $i);
+ }
+ print "time label resolution: $t seconds\n" if $verbose > 1;
+
+ ;# make gnuplot use wall clock time labels instead of NTP seconds
+ for ($c="", $i = $mintime - ($mintime % $t);
+ $i <= $maxtime + $t;
+ $i += $t, $c=",")
+ {
+ $s .= $c;
+ ((int($i / $t) % 2) &&
+ ($s .= sprintf("'' %lf",($i - $LastTimeBase)/3600))) ||
+ (($t <= 60) &&
+ ($s .= sprintf("'%d:%02d:%02d' %lf",
+ (localtime($i))[$[+2,$[+1,$[+0],
+ ($i - $LastTimeBase)/3600)))
+ || (($t <= 2*60*60) &&
+ ($s .= sprintf("'%d:%02d' %lf",
+ (localtime($i))[$[+2,$[+1],
+ ($i - $LastTimeBase)/3600)))
+ || (($t <= 12*60*60) &&
+ ($s .= sprintf("'%s %d:00' %lf",
+ $Day[(localtime($i))[$[+6]],
+ (localtime($i))[$[+2],
+ ($i - $LastTimeBase)/3600)))
+ || ($s .= sprintf("'%d.%d-%d:00' %lf",
+ (localtime($i))[$[+3,$[+4,$[+2],
+ ($i - $LastTimeBase)/3600));
+ }
+ $doplot .= "set xtics ($s)\n";
+
+ chop($xts = &ctime($mintime));
+ chop($xte = &ctime($maxtime));
+ $doplot .= "set xlabel 'Start: $xts -- Time Scale -- End: $xte'\n";
+ $doplot .= "set yrange [" ;
+ $doplot .= defined($MinY) ? sprintf("%lf", $MinY) : $miny;
+ $doplot .= ':';
+ $doplot .= defined($MaxY) ? sprintf("%lf", $MaxY) : $maxy;
+ $doplot .= "]\n";
+
+ $doplot .= " plot";
+ $c = "";
+ $showoffs &&
+ ($doplot .= sprintf($fmt,$c,$tmpfile,2,
+ "offset",
+ $minoffs,$maxoffs,
+ "[ms]"),
+ $c = ",");
+ $showcmpl &&
+ ($doplot .= sprintf($fmt,$c,$tmpfile,4,
+ "compliance" .
+ (&abs($LastCmplScale) > 1
+ ? " / $LastCmplScale"
+ : (&abs($LastCmplScale) == 1 ? "" : " * ".(1/$LastCmplScale))),
+ $mincmpl/$LastCmplScale,$maxcmpl/$LastCmplScale,
+ ""),
+ $c = ",");
+ $showfreq &&
+ ($doplot .= sprintf($fmt,$c,$tmpfile,3,
+ "frequency" .
+ ($LastFreqBase > 0
+ ? " - $LastFreqBaseString"
+ : ($LastFreqBase == 0 ? "" : " + $LastFreqBaseString")),
+ $minfreq * $FreqScale - $LastFreqBase,
+ $maxfreq * $FreqScale - $LastFreqBase,
+ "[${FreqScaleInv}ppm]"),
+ $c = ",");
+ $showoreg && $showoffs &&
+ ($doplot .= sprintf($regfmt, $c,
+ &lr_B('offs'),&lr_A('offs'),
+ "offset ",
+ &lr_B('offs'),
+ ((&lr_A('offs')) < 0 ? '-' : '+'),
+ &abs(&lr_A('offs')), &lr_r('offs'),
+ "[ms]"),
+ $c = ",");
+ $showfreg && $showfreq &&
+ ($doplot .= sprintf($regfmt, $c,
+ &lr_B('freq') * $FreqScale,
+ (&lr_A('freq') + $minfreq) * $FreqScale - $LastFreqBase,
+ "frequency",
+ &lr_B('freq') * $FreqScale,
+ ((&lr_A('freq') + $minfreq) * $FreqScale - $LastFreqBase) < 0 ? '-' : '+',
+ &abs((&lr_A('freq') + $minfreq) * $FreqScale - $LastFreqBase),
+ &lr_r('freq'),
+ "[${FreqScaleInv}ppm]"),
+ $c = ",");
+ $doplot .= "\n";
+}
+
+%F_key = ();
+%F_name = ();
+%F_size = ();
+%F_mtime = ();
+%F_first = ();
+%F_last = ();
+
+sub genfile
+{
+ local($cnt,$in,$out,@fpos) = @_;
+
+ local(@F,@t,$t,$lastT) = ();
+ local(@break,@time,@offs,@freq,@cmpl,@loffset,@filekey) = ();
+ local($lm,$l,@f);
+
+ local($sdir,$sname);
+
+ ;# allocate some storage for the tables
+ ;# otherwise realloc may get into troubles
+ if (defined($StartTime) && defined($EndTime))
+ {
+ $l = ($EndTime-$StartTime) -$[+1 +1; # worst case: 1 sample per second
+ }
+ else
+ {
+ $l = $cnt + 10;
+ }
+ print "preextending arrays to $l entries\n" if $verbose > 2;
+ $#break = $l; for ($i=$[; $i<=$l;$i++) { $break[$i] = 0; }
+ $#time = $l; for ($i=$[; $i<=$l;$i++) { $time[$i] = 0; }
+ $#offs = $l; for ($i=$[; $i<=$l;$i++) { $offs[$i] = 0; }
+ $#freq = $l; for ($i=$[; $i<=$l;$i++) { $freq[$i] = 0; }
+ $#cmpl = $l; for ($i=$[; $i<=$l;$i++) { $cmpl[$i] = 0; }
+ $#loffset = $l; for ($i=$[; $i<=$l;$i++) { $loffset[$i] = 0; }
+ $#filekey = $l; for ($i=$[; $i<=$l;$i++) { $filekey[$i] = 0; }
+ ;# now reduce size again
+ $#break = $[ - 1;
+ $#time = $[ - 1;
+ $#offs = $[ - 1;
+ $#freq = $[ - 1;
+ $#cmpl = $[ - 1;
+ $#loffset = $[ - 1;
+ $#filekey = $[ - 1;
+ print "memory allocation ready\n" if $verbose > 2;
+ sleep(3) if $verbose > 1;
+
+ if (index($in,"/") < $[)
+ {
+ $sdir = ".";
+ $sname = $in;
+ }
+ else
+ {
+ ($sdir,$sname) = ($in =~ m!^(.*)/([^/]*)!);
+ $sname = "" unless defined($sname);
+ }
+
+ if (!defined($Lsdir) || $Lsdir ne $sdir || $Ltime != (stat($sdir))[$[+9] ||
+ grep($F_mtime{$_} != (stat($F_name{$_}))[$[+9], @F_files))
+
+ {
+ print "rescanning directory \"$sdir\" for files \"$sname*\"\n"
+ if $verbose > 1;
+
+ ;# rescan directory on changes
+ $Lsdir = $sdir;
+ $Ltime = (stat($sdir))[$[+9];
+ </X{> if 0; # dummy line - calm down my formatter
+ local(@newfiles) = < ${in}*[0-9] >;
+ local($st_dev,$st_ino,$st_mtime,$st_size,$name,$key,$modified);
+
+ foreach $name (@newfiles)
+ {
+ ($st_dev,$st_ino,$st_size,$st_mtime) =
+ (stat($name))[$[,$[+1,$[+7,$[+9];
+ $modified = 0;
+ $key = sprintf("%lx|%lu", $st_dev, $st_ino);
+
+ print "candidate file \"$name\"",
+ (defined($st_dev) ? "" : " failed: $!"),"\n"
+ if $verbose > 2;
+
+ if (! defined($F_key{$name}) || $F_key{$name} ne $key)
+ {
+ $F_key{$name} = $key;
+ $modified++;
+ }
+ if (!defined($F_name{$key}) || $F_name{$key} != $name)
+ {
+ $F_name{$key} = $name;
+ $modified++;
+ }
+ if (!defined($F_size{$key}) || $F_size{$key} != $st_size)
+ {
+ $F_size{$key} = $st_size;
+ $modified++;
+ }
+ if (!defined($F_mtime{$key}) || $F_mtime{$key} != $st_mtime)
+ {
+ $F_mtime{$key} = $st_mtime;
+ $modified++;
+ }
+ if ($modified)
+ {
+ print "new data \"$name\" key: $key;\n" if $verbose > 1;
+ print " size: $st_size; mtime: $st_mtime;\n"
+ if $verbose > 1;
+ $F_last{$key} = $F_first{$key} = $st_mtime;
+ $F_first{$key}--; # prevent zero divide later on
+ ;# now compute derivated attributes
+ open(IN, "<$name") ||
+ do {
+ warn "$0: failed to open \"$name\": $!";
+ next;
+ };
+
+ while(<IN>)
+ {
+ @F = split;
+ next if @F < 5;
+ next if $F[$[] eq "";
+ $t = ($F[$[] - $MJD_1970) * 24 * 60 * 60;
+ $t += $F[$[+1];
+ $F_first{$key} = $t;
+ print "\tfound first entry: $t ",&ctime($t)
+ if $verbose > 4;
+ last;
+ }
+ seek(IN,
+ ($st_size > 4*$RecordSize) ? $st_size - 4*$RecordSize : 0,
+ 0);
+ while(<IN>)
+ {
+ @F = split;
+ next if @F < 5;
+ next if $F[$[] eq "";
+ $t = ($F[$[] - $MJD_1970) * 24 * 60 * 60;
+ $t += $F[$[+1];
+ $F_last{$key} = $t;
+ $_ = <IN>;
+ print "\tfound last entry: $t ", &ctime($t)
+ if $verbose > 4 && ! defined($_);
+ last unless defined($_);
+ redo;
+ ;# Ok, calm down...
+ ;# using $_ = <IN> in conjunction with redo
+ ;# is semantically equivalent to the while loop, but
+ ;# I needed a one line look ahead and this solution
+ ;# was what I thought of first
+ ;# and.. If you do not like it dont look
+ }
+ close(IN);
+ print(" first: ",$F_first{$key},
+ " last: ",$F_last{$key},"\n") if $verbose > 1;
+ }
+ }
+ ;# now reclaim memory used for files no longer referenced ...
+ local(%Names);
+ grep($Names{$_} = 1,@newfiles);
+ foreach (keys %F_key)
+ {
+ next if defined($Names{$_});
+ delete $F_key{$_};
+ $verbose > 2 && print "no longer referenced: \"$_\"\n";
+ }
+ %Names = ();
+
+ grep($Names{$_} = 1,values(%F_key));
+ foreach (keys %F_name)
+ {
+ next if defined($Names{$_});
+ delete $F_name{$_};
+ $verbose > 2 && print "unref name($_)= $F_name{$_}\n";
+ }
+ foreach (keys %F_size)
+ {
+ next if defined($Names{$_});
+ delete $F_size{$_};
+ $verbose > 2 && print "unref size($_)\n";
+ }
+ foreach (keys %F_mtime)
+ {
+ next if defined($Names{$_});
+ delete $F_mtime{$_};
+ $verbose > 2 && print "unref mtime($_)\n";
+ }
+ foreach (keys %F_first)
+ {
+ next if defined($Names{$_});
+ delete $F_first{$_};
+ $verbose > 2 && print "unref first($_)\n";
+ }
+ foreach (keys %F_last)
+ {
+ next if defined($Names{$_});
+ delete $F_last{$_};
+ $verbose > 2 && print "unref last($_)\n";
+ }
+ ;# create list sorted by time
+ @F_files = sort {$F_first{$a} <=> $F_first{$b}; } keys(%F_name);
+ if ($verbose > 1)
+ {
+ print "Resulting file list:\n";
+ foreach (@F_files)
+ {
+ print "\t$_\t$F_name{$_}\n";
+ }
+ }
+ }
+
+ printf("processing %s; output \"$out\" (%d input files)\n",
+ ((defined($StartTime) && defined($EndTime))
+ ? "time range"
+ : (defined($StartTime) ? "$cnt samples from StartTime" :
+ (defined($EndTime) ? "$cnt samples to EndTime" :
+ "last $cnt samples"))),
+ scalar(@F_files))
+ if $verbose > 1;
+
+ ;# open output file - will be input for plotcmd
+ open(OUT,">$out") ||
+ do {
+ warn("$0: cannot create \"$out\": $!\n");
+ };
+
+ @f = @F_files;
+ if (defined($StartTime))
+ {
+ while (@f && ($F_last{$f[$[]} < $StartTime))
+ {
+ print("shifting ", $F_name{$f[$[]},
+ " last: ", $F_last{$f[$[]},
+ " < StartTime: $StartTime\n")
+ if $verbose > 3;
+ shift(@f);
+ }
+
+
+ }
+ if (defined($EndTime))
+ {
+ while (@f && ($F_first{$f[$#f]} > $EndTime))
+ {
+ print("popping ", $F_name{$f[$#f]},
+ " first: ", $F_first{$f[$#f]},
+ " > EndTime: $EndTime\n")
+ if $verbose > 3;
+ pop(@f);
+ }
+ }
+
+ if (@f)
+ {
+ if (defined($StartTime))
+ {
+ print "guess start according to StartTime ($StartTime)\n"
+ if $verbose > 3;
+
+ if ($fpos[$[] eq 'start')
+ {
+ if (grep($_ eq $fpos[$[+1],@f))
+ {
+ shift(@f) while @f && $f[$[] ne $fpos[$[+1];
+ }
+ else
+ {
+ @fpos = ('start', $f[$[], undef);
+ }
+ }
+ else
+ {
+ @fpos = ('start' , $f[$[], undef);
+ }
+
+ if (!defined($fpos[$[+2]))
+ {
+ if ($StartTime <= $F_first{$f[$[]})
+ {
+ $fpos[$[+2] = 0;
+ }
+ else
+ {
+ $fpos[$[+2] =
+ int($F_size{$f[$[]} *
+ (($StartTime - $F_first{$f[$[]})/
+ ($F_last{$f[$[]} - $F_first{$f[$[]})));
+ $fpos[$[+2] = ($fpos[$[+2] <= 2 * $RecordSize)
+ ? 0 : $fpos[$[+2] - 2 * $RecordSize;
+ ;# anyway as the data may contain "time holes"
+ ;# our heuristics may baldly fail
+ ;# so just start at 0
+ $fpos[$[+2] = 0;
+ }
+ }
+ }
+ elsif (defined($EndTime))
+ {
+ print "guess starting point according to EndTime ($EndTime)\n"
+ if $verbose > 3;
+
+ if ($fpos[$[] eq 'end')
+ {
+ if (grep($_ eq $fpos[$[+1],@f))
+ {
+ shift(@f) while @f && $f[$[] ne $fpos[$[+1];
+ }
+ else
+ {
+ @fpos = ('end', $f[$[], undef);
+ }
+ }
+ else
+ {
+ @fpos = ('end', $f[$[], undef);
+ }
+
+ if (!defined($fpos[$[+2]))
+ {
+ local(@x) = reverse(@f);
+ local($s,$c) = (0,$cnt);
+ if ($EndTime < $F_last{$x[$[]})
+ {
+ ;# last file will only be used partially
+ $s = int($F_size{$x[$[]} *
+ (($EndTime - $F_first{$x[$[]}) /
+ ($F_last{$x[$[]} - $F_first{$x[$[]})));
+ $s = int($s/$RecordSize);
+ $c -= $s - 1;
+ if ($c <= 0)
+ {
+ ;# start is in the same file
+ $fpos[$[+1] = $x[$[];
+ $fpos[$[+2] = ($c >=-2) ? 0 : (-$c - 2) * $RecordSize;
+ shift(@f) while @f && ($f[$[] ne $x[$[]);
+ }
+ else
+ {
+ shift(@x);
+ }
+ }
+
+ if (!defined($fpos[$[+2]))
+ {
+ local($_);
+ while($_ = shift(@x))
+ {
+ $s = int($F_size{$_}/$RecordSize);
+ $c -= $s - 1;
+ if ($c <= 0)
+ {
+ $fpos[$[+1] = $_;
+ $fpos[$[+2] = ($c>-2) ? 0 : (-$c - 2) * $RecordSize;
+ shift(@f) while @f && ($f[$[] ne $_);
+ last;
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ print "guessing starting point according to count ($cnt)\n"
+ if $verbose > 3;
+ ;# guess offset to get last available $cnt samples
+ if ($fpos[$[] eq 'cnt')
+ {
+ if (grep($_ eq $fpos[$[+1],@f))
+ {
+ print "old positioning applies\n" if $verbose > 3;
+ shift(@f) while @f && $f[$[] ne $fpos[$[+1];
+ }
+ else
+ {
+ @fpos = ('cnt', $f[$[], undef);
+ }
+ }
+ else
+ {
+ @fpos = ('cnt', $f[$[], undef);
+ }
+
+ if (!defined($fpos[$[+2]))
+ {
+ local(@x) = reverse(@f);
+ local($s,$c) = (0,$cnt);
+
+ local($_);
+ while($_ = shift(@x))
+ {
+ print "examing \"$_\" $c samples still needed\n"
+ if $verbose > 4;
+ $s = int($F_size{$_}/$RecordSize);
+ $c -= $s - 1;
+ if ($c <= 0)
+ {
+ $fpos[$[+1] = $_;
+ $fpos[$[+2] = ($c>-2) ? 0 : (-$c - 2) * $RecordSize;
+ shift(@f) while @f && ($f[$[] ne $_);
+ last;
+ }
+ }
+ if (!defined($fpos[$[+2]))
+ {
+ print "no starting point yet - using start of data\n"
+ if $verbose > 2;
+ $fpos[$[+2] = 0;
+ }
+ }
+ }
+ }
+ print "Ooops, no suitable input file ??\n"
+ if $verbose > 1 && @f <= 0;
+
+ printf("Starting at (%s) \"%s\" offset %ld using %d files\n",
+ $fpos[$[+1],
+ $F_name{$fpos[$[+1]},
+ $fpos[$[+2],
+ scalar(@f))
+ if $verbose > 2;
+
+ $lm = 1;
+ $l = 0;
+ foreach $key (@f)
+ {
+ $file = $F_name{$key};
+ print "processing file \"$file\"\n" if $verbose > 2;
+
+ open(IN,"<$file") ||
+ (warn("$0: cannot read \"$file\": $!\n"), next);
+
+ ;# try to seek to a position nearer to the start of the interesting lines
+ ;# should always affect only first item in @f
+ ($key eq $fpos[$[+1]) &&
+ (($verbose > 1) &&
+ print("Seeking to offset $fpos[$[+2]\n"),
+ seek(IN,$fpos[$[+2],0) ||
+ warn("$0: seek(\"$F_name{$key}\" failed: $|\n"));
+
+ while(<IN>)
+ {
+ $l++;
+ ($verbose > 3) &&
+ (($l % $lm) == 0 && print("\t$l lines read\n") &&
+ (($l == 2) && ($lm = 10) ||
+ ($l == 100) && ($lm = 100) ||
+ ($l == 500) && ($lm = 500) ||
+ ($l == 1000) && ($lm = 1000) ||
+ ($l == 5000) && ($lm = 5000) ||
+ ($l == 10000) && ($lm = 10000)));
+
+ @F = split;
+
+ next if @F < 5; # no valid input line is this short
+ next if $F[$[] eq "";
+ ($F[$[] !~ /^\d+$/) && # A 'never should have happend' error
+ die("$0: unexpected input line: $_\n");
+
+ ;# modified Julian to UNIX epoch
+ $t = ($F[$[] - $MJD_1970) * 24 * 60 * 60;
+ $t += $F[$[+1]; # add seconds + fraction
+
+ ;# multiply offset by 1000 to get ms - try to avoid float op
+ (($F[$[+2] =~ s/(\d*)\.(\d{3})(\d*)/\1\2.\3/) &&
+ $F[$[+2] =~ s/0+([\d\.])/($1 eq '.') ? '0.' : $1/e) # strip leading zeros
+ || $F[$[+2] *= 1000;
+
+
+ ;# skip samples out of specified time range
+ next if (defined($StartTime) && $StartTime > $t);
+ next if (defined($EndTime) && $EndTime < $t);
+
+ next if defined($lastT) && $t < $lastT; # backward in time ??
+
+ push(@offs,$F[$[+2]);
+ push(@freq,$F[$[+3] * (2**20/10**6));
+ push(@cmpl,$F[$[+4]);
+
+ push(@break, (defined($lastT) && ($t - $lastT > $deltaT)));
+ $lastT = $t;
+ push(@time,$t);
+ push(@loffset, tell(IN) - length($_));
+ push(@filekey, $key);
+
+ shift(@break),shift(@time),shift(@offs),
+ shift(@freq), shift(@cmpl),shift(@loffset),
+ shift(@filekey)
+ if @time > $cnt &&
+ ! (defined($StartTime) && defined($EndTime));
+
+ last if @time >= $cnt && defined($StartTime) && !defined($EndTime);
+ }
+ close(IN);
+ last if @time >= $cnt && defined($StartTime) && !defined($EndTime);
+ }
+ print "input scanned ($l lines/",scalar(@time)," samples)\n"
+ if $verbose > 1;
+
+ &lr_init('offs');
+ &lr_init('freq');
+
+ if (@time)
+ {
+ local($_,@F);
+
+ local($timebase) unless defined($timebase);
+ local($freqbase) unless defined($freqbase);
+ local($cmplscale) unless defined($cmplscale);
+
+ undef($mintime,$maxtime,$minoffs,$maxoffs,
+ $minfreq,$maxfreq,$mincmpl,$maxcmpl,
+ $miny,$maxy);
+
+ print "computing ranges\n" if $verbose > 2;
+
+ $LastCnt = @time;
+
+ ;# @time is in ascending order (;-)
+ $mintime = @time[$[];
+ $maxtime = @time[$#time];
+ unless (defined($timebase))
+ {
+ local($time,@X) = (time);
+ @X = localtime($time);
+
+ ;# compute today 00:00:00
+ $timebase = $time - ((($X[$[+2]*60)+$X[$[+1])*60+$X[$[]);
+
+ }
+ $LastTimeBase = $timebase;
+
+ if ($showoffs)
+ {
+ local($i,$m,$f);
+
+ $minoffs = &min(@offs);
+ $maxoffs = &max(@offs);
+
+ ;# I know, it is not perl style using indices to access arrays,
+ ;# but I have to proccess two arrays in sync, non-destructively
+ ;# (otherwise a (shift(@a1),shift(a2)) would do),
+ ;# I dont like to make copies of these arrays as they may be huge
+ $i = $[;
+ &lr_sample(($time[$i]-$timebase)/3600,$offs[$i],'offs'),$i++
+ while $i <= $#time;
+
+ ($minoffs == $maxoffs) && ($minoffs -= 0.1,$maxoffs += 0.1);
+
+ $i = &lr_sigma('offs');
+ $m = &lr_mean('offs');
+
+ print "mean offset: $m sigma: $i\n" if $verbose > 2;
+
+ if (($maxoffs - $minoffs) > $MinClip)
+ {
+ $f = (&abs($minoffs) < &abs($maxoffs)) ? $FuzzLow : $FuzzBig;
+ $miny = (($m - $minoffs) <= ($f * $i))
+ ? $minoffs : ($m - $f * $i);
+ $f = ($f == $FuzzLow) ? $FuzzBig : $FuzzLow;
+ $maxy = (($maxoffs - $m) <= ($f * $i))
+ ? $maxoffs : ($m + $f * $i);
+ }
+ else
+ {
+ $miny = $minoffs;
+ $maxy = $maxoffs;
+ }
+ ($maxy-$miny) == 0 &&
+ (($maxy,$miny)
+ = (($maxoffs - $minoffs) > 0)
+ ? ($maxoffs,$minoffs) : ($MinClip,-$MinClip));
+
+ $maxy = $MaxY if defined($MaxY) && $MaxY < $maxy;
+ $miny = $MinY if defined($MinY) && $MinY > $miny;
+
+ print "offset min clipped from $minoffs to $miny\n"
+ if $verbose > 2 && $minoffs != $miny;
+ print "offset max clipped from $maxoffs to $maxy\n"
+ if $verbose > 2 && $maxoffs != $maxy;
+ }
+
+ if ($showfreq)
+ {
+ local($i,$m);
+
+ $minfreq = &min(@freq);
+ $maxfreq = &max(@freq);
+
+ $i = $[;
+ &lr_sample(($time[$i]-$timebase)/3600,$freq[$i]-$minfreq,'freq'),
+ $i++
+ while $i <= $#time;
+
+ $i = &lr_sigma('freq');
+ $m = &lr_mean('freq') + $minfreq;
+
+ print "mean frequency: $m sigma: $i\n" if $verbose > 2;
+
+ if (defined($maxy))
+ {
+ local($s) =
+ ($maxfreq - $minfreq)
+ ? ($maxy - $miny) / ($maxfreq - $minfreq) : 1;
+
+ if (defined($freqbase))
+ {
+ $FreqScale = 1;
+ $FreqScaleInv = "";
+ }
+ else
+ {
+ $FreqScale = 1;
+ $FreqScale = 10 ** int(log($s)/log(10) - 0.8);
+ $FreqScaleInv =
+ ("$FreqScale" =~ /^10(0*)$/) ? "0.${1}1" :
+ ($FreqScale == 1 ? "" : (1/$FreqScale));
+
+ $freqbase = $m * $FreqScale;
+ $freqbase -= &lr_mean('offs');
+
+ ;# round resulting freqbase
+ ;# to precision of min max difference
+ $s = int(log(($maxfreq-$minfreq)*$FreqScale)/log(10))-1;
+ $s = 10 ** $s;
+ $freqbase = int($freqbase / $s) * $s;
+ }
+ }
+ else
+ {
+ $FreqScale = 1;
+ $FreqScaleInv = "";
+ $freqbase = $m unless defined($freqbase);
+ if (($maxfreq - $minfreq) > $MinClip)
+ {
+ $f = (&abs($minfreq) < &abs($maxfreq))
+ ? $FuzzLow : $FuzzBig;
+ $miny = (($freqbase - $minfreq) <= ($f * $i))
+ ? ($minfreq-$freqbase) : (- $f * $i);
+ $f = ($f == $FuzzLow) ? $FuzzBig : $FuzzLow;
+ $maxy = (($maxfreq - $freqbase) <= ($f * $i))
+ ? ($maxfreq-$freqbase) : ($f * $i);
+ }
+ else
+ {
+ $miny = $minfreq - $freqbase;
+ $maxy = $maxfreq - $freqbase;
+ }
+ ($maxy - $miny) == 0 &&
+ (($maxy,$miny) =
+ (($maxfreq - $minfreq) > 0)
+ ? ($maxfreq-$freqbase,$minfreq-$freqbase) : (0.5,-0.5));
+
+ $maxy = $MaxY if defined($MaxY) && $MaxY < $maxy;
+ $miny = $MinY if defined($MinY) && $MinY > $miny;
+
+ print("frequency min clipped from ",$minfreq-$freqbase,
+ " to $miny\n")
+ if $verbose > 2 && $miny != ($minfreq - $freqbase);
+ print("frequency max clipped from ",$maxfreq-$freqbase,
+ " to $maxy\n")
+ if $verbose > 2 && $maxy != ($maxfreq - $freqbase);
+ }
+ $LastFreqBaseString =
+ sprintf("%g",$freqbase >= 0 ? $freqbase : -$freqbase);
+ $LastFreqBase = $freqbase;
+ print "LastFreqBaseString now \"$LastFreqBaseString\"\n"
+ if $verbose > 5;
+ }
+ else
+ {
+ $FreqScale = 1;
+ $FreqScaleInv = "";
+ $LastFreqBase = 0;
+ $LastFreqBaseString = "";
+ }
+
+ if ($showcmpl)
+ {
+ $mincmpl = &min(@cmpl);
+ $maxcmpl = &max(@cmpl);
+
+ if (!defined($cmplscale))
+ {
+ if (defined($maxy))
+ {
+ local($cmp)
+ = (&abs($miny) > &abs($maxy)) ? &abs($miny) : $maxy;
+ $cmplscale = $cmp == $maxy ? 1 : -1;
+
+ foreach (0.01, 0.02, 0.05,
+ 0.1, 0.2, 0.25, 0.4, 0.5,
+ 1, 2, 4, 5,
+ 10, 20, 25, 50,
+ 100, 200, 250, 500, 1000)
+ {
+ $cmplscale *= $_, last if $maxcmpl/$_ <= $cmp;
+ }
+ }
+ else
+ {
+ $cmplscale = 1;
+ $miny = $mincmpl ? 0 : -$MinClip;
+ $maxy = $maxcmpl+$MinClip;
+ }
+ }
+ $LastCmplScale = $cmplscale;
+ }
+ else
+ {
+ $LastCmplScale = 1;
+ }
+
+ print "creating plot command input file\n" if $verbose > 2;
+
+
+ print OUT ("# preprocessed NTP statistics file for $STATHOST\n");
+ print OUT ("# timebase is: ",&ctime($LastTimeBase))
+ if defined($LastTimeBase);
+ print OUT ("# frequency is offset by ",
+ ($LastFreqBase >= 0 ? "+" : "-"),
+ "$LastFreqBaseString [${FreqScaleInv}ppm]\n");
+ print OUT ("# compliance is scaled by $LastCmplScale\n");
+ print OUT ("# time [h]\toffset [ms]\tfrequency [${FreqScaleInv}ppm]\tcompliance\n");
+
+ printf OUT ("%s%lf\t%lf\t%lf\t%lf\n",
+ (shift(@break) ? "\n" : ""),
+ (shift(@time) - $LastTimeBase)/3600,
+ shift(@offs),
+ shift(@freq) * $FreqScale - $LastFreqBase,
+ shift(@cmpl) / $LastCmplScale)
+ while(@time);
+ }
+ else
+ {
+ ;# prevent plotcmd from processing empty file
+ print "Creating plot command dummy...\n" if $verbose > 2;
+ print OUT "# dummy samples\n0 1 2 3\n1 1 2 3\n";
+ &lr_sample(0,1,'offs');
+ &lr_sample(1,1,'offs');
+ &lr_sample(0,2,'freq');
+ &lr_sample(1,2,'freq');
+ @time = (0, 1); $maxtime = 1; $mintime = 0;
+ @offs = (1, 1); $maxoffs = 1; $minoffs = 1;
+ @freq = (2, 2); $maxfreq = 2; $minfreq = 2;
+ @cmpl = (3, 3); $maxcmpl = 3; $mincmpl = 3;
+ $LastCnt = 2;
+ $LastFreqBase = 0;
+ $LastCmplScale = 1;
+ $LastTimeBase = 0;
+ $miny = -$MinClip;
+ $maxy = 3 + $MinClip;
+ }
+ close(OUT);
+
+ print "plot command input file created\n"
+ if $verbose > 2;
+
+ if (($fpos[$[] eq 'cnt' && @loffset >= $cnt) ||
+ ($fpos[$[] eq 'start' && $time[$[] <= $StartTime) ||
+ ($fpos[$[] eq 'end'))
+ {
+ return ($fpos[$[],$filekey[$[],$loffset[$[]);
+ }
+ else # found to few lines - next time start search earlier in file
+ {
+ if ($fpos[$[] eq 'start')
+ {
+ ;# the timestamps we got for F_first and F_last guaranteed
+ ;# that no file is left out
+ ;# the only thing that could happen is:
+ ;# we guessed the starting point wrong
+ ;# compute a new guess from the first record found
+ ;# if this equals our last guess use data of first record
+ ;# otherwise try new guess
+
+ if ($fpos[$[+1] eq $filekey[$[] && $loffset[$[] > $fpos[$[+2])
+ {
+ local($noff);
+ $noff = $loffset[$[] - ($cnt - @loffset + 1) * $RecordSize;
+ $noff = 0 if $noff < 0;
+
+ return (@fpos[$[,$[+1], ($noff == $fpos[$[+2]) ? $loffset[$[] : $noff);
+ }
+ return ($fpos[$[],$filekey[$[],$loffset[$[]);
+ }
+ elsif ($fpos[$[] eq 'end' || $fpos[$[] eq 'cnt')
+ {
+ ;# try to start earlier in file
+ ;# if we already started at the beginning
+ ;# try to use previous file
+ ;# this assumes distance to better starting point is at most one file
+ ;# the primary guess at top of genfile() should usually allow this
+ ;# assumption
+ ;# if the offset of the first sample used is within
+ ;# a different file than we guessed it must have occured later
+ ;# in the sequence of files
+ ;# this only can happen if our starting file did not contain
+ ;# a valid sample from the starting point we guessed
+ ;# however this does not invalidate our assumption, no check needed
+ local($noff,$key);
+ if ($fpos[$[+2] > 0)
+ {
+ $noff = $fpos[$[+2] - $RecordSize * ($cnt - @loffset + 1);
+ $noff = 0 if $noff < 0;
+ return (@fpos[$[,$[+1],$noff);
+ }
+ else
+ {
+ if ($fpos[$[+1] eq $F_files[$[])
+ {
+ ;# first file - and not enough samples
+ ;# use data of first sample
+ return ($fpos[$[], $filekey[$[], $loffset[$[]);
+ }
+ else
+ {
+ ;# search key of previous file
+ $key = $F_files[$[];
+ @F = reverse(@F_files);
+ while ($_ = shift(@F))
+ {
+ if ($_ eq $fpos[$[+1])
+ {
+ $key = shift(@F) if @F;
+ last;
+ }
+ }
+ $noff = int($F_size{$key} / $RecordSize);
+ $noff -= $cnt - @loffset;
+ $noff = 0 if $noff < 0;
+ $noff *= $RecordSize;
+ return ($fpos[$[], $key, $noff);
+ }
+ }
+ }
+ else
+ {
+ return ();
+ }
+
+ return 0 if @loffset <= 1 || ($loffset[$#loffset] - $loffset[$[]) <= 1;
+
+ ;# EOF - 1.1 * avg(line) * $cnt
+ local($val) = $loffset[$#loffset]
+ - $cnt * 11 * (($loffset[$#loffset] - $loffset[$[]) / @loffset) / 10;
+ return ($val < 0) ? 0 : $val;
+ }
+}
+
+;# initial setup of plot
+print "initialize plotting\n" if $verbose;
+if (defined($PrintIt))
+{
+ if ($PrintIt =~ m,/,)
+ {
+ print "Saving plot to file $PrintIt\n";
+ print PLOT "set output '$PrintIt'\n";
+ }
+ else
+ {
+ print "Printing plot on printer $PrintIt\n";
+ print PLOT "set output '| lpr -P$PrintIt -h'\n";
+ }
+ print PLOT "set terminal postscript landscape color solid 'Helvetica' 10\n";
+}
+print PLOT "set grid\n";
+print PLOT "set tics out\n";
+print PLOT "set format y '%g '\n";
+printf PLOT "set time 47\n" unless defined($PrintIt);
+
+@filepos =();
+while(1)
+{
+ print &ctime(time) if $verbose;
+
+ ;# update diplay characteristics
+ &read_config;# unless defined($PrintIt);
+
+ unlink($tmpfile);
+ @filepos = &genfile($samples,$srcprefix,$tmpfile,@filepos);
+
+ ;# make plotcmd display samples
+ &make_doplot;
+ print "Displaying plot...\n" if $verbose > 1;
+ print "command for plot sub process:\n$doplot----\n" if $verbose > 3;
+ print PLOT $doplot;
+}
+continue
+{
+ if (defined($PrintIt))
+ {
+ delete $SIG{'CHLD'};
+ print PLOT "quit\n";
+ close(PLOT);
+ if ($PrintIt =~ m,/,)
+ {
+ print "Plot saved to file $PrintIt\n";
+ }
+ else
+ {
+ print "Plot spooled to printer $PrintIt\n";
+ }
+ unlink($tmpfile);
+ exit(0);
+ }
+ ;# wait $delay seconds
+ print "waiting $delay seconds ..." if $verbose > 2;
+ sleep($delay);
+ print " continuing\n" if $verbose > 2;
+ undef($LastFreqBaseString);
+}
+
+
+sub date_time_spec2seconds
+{
+ local($_) = @_;
+ ;# a date_time_spec consistes of:
+ ;# YYYY-MM-DD_HH:MM:SS.ms
+ ;# values can be omitted from the beginning and default than to
+ ;# values of current date
+ ;# values omitted from the end default to lowest possible values
+
+ local($time) = time;
+ local($sec,$min,$hour,$mday,$mon,$year)
+ = localtime($time);
+
+ local($last) = ();
+
+ s/^\D*(.*\d)\D*/\1/; # strip off garbage
+
+ PARSE:
+ {
+ if (s/^(\d{4})(-|$)//)
+ {
+ if ($1 < 1970)
+ {
+ warn("$0: can not handle years before 1970 - year $1 ignored\n");
+ return undef;
+ }
+ elsif ( $1 >= 2070)
+ {
+ warn("$0: can not handle years past 2070 - year $1 ignored\n");
+ return undef;
+ }
+ else
+ {
+ $year = $1 % 100; # 0<= $year < 100
+ ;# - interpreted 70 .. 99,00 .. 69
+ }
+ $last = $[ + 5;
+ last PARSE if $_ eq '';
+ warn("$0: bad date_time_spec: \"$_\" found after YEAR\n"),
+ return(undef)
+ if $2 eq '';
+ }
+
+ if (s/^(\d{1,2})(-|$)//)
+ {
+ warn("$0: implausible month $1\n"),return(undef)
+ if $1 < 1 || $1 > 12;
+ $mon = $1 - 1;
+ $last = $[ + 4;
+ last PARSE if $_ eq '';
+ warn("$0: bad date_time_spec: \"$_\" found after MONTH\n"),
+ return(undef)
+ if $2 eq '';
+ }
+ else
+ {
+ warn("$0: bad date_time_spec \"$_\"\n"),return(undef)
+ if defined($last);
+
+ }
+
+ if (s/^(\d{1,2})([_ ]|$)//)
+ {
+ warn("$0: implausible month day $1 for month ".($mon+1)." (".
+ $MaxNumDaysPerMonth[$mon].")$mon\n"),
+ return(undef)
+ if $1 < 1 || $1 > $MaxNumDaysPerMonth[$mon];
+ $mday = $1;
+ $last = $[ + 3;
+ last PARSE if $_ eq '';
+ warn("$0: bad date_time_spec \"$_\" found after MDAY\n"),
+ return(undef)
+ if $2 eq '';
+ }
+ else
+ {
+ warn("$0: bad date_time_spec \"$_\"\n"), return undef
+ if defined($last);
+ }
+
+ ;# now we face a problem:
+ ;# if ! defined($last) a prefix of "07:"
+ ;# can be either 07:MM or 07:ss
+ ;# to get the second interpretation make the user add
+ ;# a msec fraction part and check for this special case
+ if (! defined($last) && s/^(\d{1,2}):(\d{1,2}\.\d+)//)
+ {
+ warn("$0: implausible minute $1\n"), return undef
+ if $1 < 0 || $1 >= 60;
+ warn("$0: implausible second $1\n"), return undef
+ if $2 < 0 || $2 >= 60;
+ $min = $1;
+ $sec = $2;
+ $last = $[ + 1;
+ last PARSE if $_ eq '';
+ warn("$0: bad date_time_spec \"$_\" after SECONDS\n");
+ return undef;
+ }
+
+ if (s/^(\d{1,2})(:|$)//)
+ {
+ warn("$0: implausible hour $1\n"), return undef
+ if $1 < 0 || $1 > 24;
+ $hour = $1;
+ $last = $[ + 2;
+ last PARSE if $_ eq '';
+ warn("$0: bad date_time_spec found \"$_\" after HOUR\n"),
+ return undef
+ if $2 eq '';
+ }
+ else
+ {
+ warn("$0: bad date_time_spec \"$_\"\n"), return undef
+ if defined($last);
+ }
+
+ if (s/^(\d{1,2})(:|$)//)
+ {
+ warn("$0: implausible minute $1\n"), return undef
+ if $1 < 0 || $1 >=60;
+ $min = $1;
+ $last = $[ + 1;
+ last PARSE if $_ eq '';
+ warn("$0: bad date_time_spec found \"$_\" after MINUTE\n"),
+ return undef
+ if $2 eq '';
+ }
+ else
+ {
+ warn("$0: bad date_time_spec \"$_\"\n"), return undef
+ if defined($last);
+ }
+
+ if (s/^(\d{1,2}(\.\d+)?)//)
+ {
+ warn("$0: implausible second $1\n"), return undef
+ if $1 < 0 || $1 >=60;
+ $sec = $1;
+ $last = $[;
+ last PARSE if $_ eq '';
+ warn("$0: bad date_time_spec found \"$_\" after SECOND\n");
+ return undef;
+ }
+ }
+
+ return $time unless defined($last);
+
+ $sec = 0 if $last > $[;
+ $min = 0 if $last > $[ + 1;
+ $hour = 0 if $last > $[ + 2;
+ $mday = 1 if $last > $[ + 3;
+ $mon = 0 if $last > $[ + 4;
+ local($rtime) = &timelocal($sec,$min,$hour,$mday,$mon,$year, 0,0, 0);
+
+ ;# $rtime may be off if daylight savings time is in effect at given date
+ return $rtime + ($sec - int($sec))
+ if $hour == (localtime($rtime))[$[+2];
+ return
+ &timelocal($sec,$min,$hour,$mday,$mon,$year, 0,0, 1)
+ + ($sec - int($sec));
+}
+
+
+sub min
+{
+ local($m) = shift;
+
+ grep((($m > $_) && ($m = $_),0),@_);
+ $m;
+}
+
+sub max
+{
+ local($m) = shift;
+
+ grep((($m < $_) && ($m = $_),0),@_);
+ $m;
+}
diff --git a/usr.sbin/xntpd/scripts/monitoring/ntptrap b/usr.sbin/xntpd/scripts/monitoring/ntptrap
new file mode 100755
index 0000000..69c6660
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/monitoring/ntptrap
@@ -0,0 +1,453 @@
+#!/local/bin/perl --*-perl-*-
+;#
+;# ntptrap,v 3.1 1993/07/06 01:09:15 jbj Exp
+;#
+;# a client for the xntp mode 6 trap mechanism
+;#
+;# Copyright (c) 1992
+;# Rainer Pruy Friedrich-Alexander Universitaet Erlangen-Nuernberg
+;#
+;#
+;#############################################################
+$0 =~ s!^.*/([^/]+)$!\1!; # strip to filename
+;# enforce STDOUT and STDERR to be line buffered
+$| = 1;
+select((select(STDERR),$|=1)[$[]);
+
+;#######################################
+;# load utility routines and definitions
+;#
+require('ntp.pl'); # implementation of the NTP protocol
+eval { require('sys/socket.ph'); require('netinet/in.ph') unless defined(&INADDR_ANY); } ||
+do {
+ die("$0: $@") unless $[ == index($@, "Can't locate ");
+ warn "$0: $@";
+ warn "$0: supplying some default definitions\n";
+ eval 'sub INADDR_ANY { 0; } sub AF_INET {2;} sub SOCK_DGRAM {2;} 1;' || die "$0: $@";
+};
+require('getopts.pl'); # option parsing
+require('ctime.pl'); # date/time formatting
+
+;######################################
+;# define some global constants
+;#
+$BASE_TIMEOUT=10;
+$FRAG_TIMEOUT=10;
+$MAX_TRY = 5;
+$REFRESH_TIME=60*15; # 15 minutes (server uses 1 hour)
+$ntp'timeout = $FRAG_TIMEOUT; #';
+
+;######################################
+;# now process options
+;#
+sub usage
+{
+ die("usage: $0 [-n] [-p <port>] [-l <logfile>] [host] ...\n");
+}
+
+$opt_l = "/dev/null"; # where to write debug messages to
+$opt_p = 0; # port to use locally - (0 does mean: will be choosen by kernel)
+
+&usage unless &Getopts('l:p:');
+&Getopts if 0; # make -w happy
+
+@Hosts = ($#ARGV < $[) ? ("localhost") : @ARGV;
+
+;# setup for debug output
+$DEBUGFILE=$opt_l;
+$DEBUGFILE="&STDERR" if $DEBUGFILE eq '-';
+
+open(DEBUG,">>$DEBUGFILE") || die("Cannot open \"$DEBUGFILE\": $!\n");
+select((select(DEBUG),$|=1)[$[]);
+
+;# &log prints a single trap record (adding a (local) time stamp)
+sub log
+{
+ chop($date=&ctime(time));
+ print "$date ",@_,"\n";
+}
+
+sub debug
+{
+ print DEBUG @_,"\n";
+}
+;#
+$proto_udp = (getprotobyname('udp'))[$[+2] ||
+ (warn("$0: Could not get protocoll number for 'udp' using 17"), 17);
+
+$ntp_port = (getservbyname('ntp','udp'))[$[+2] ||
+ (warn("$0: Could not get port number for service ntp/udp using 123"), 123);
+
+;#
+socket(S, &AF_INET, &SOCK_DGRAM, $proto_udp) || die("Cannot open socket: $!\n");
+
+;#
+bind(S, pack("S n N x8", &AF_INET, $opt_p, &INADDR_ANY)) ||
+ die("Cannot bind: $!\n");
+
+($my_port, $my_addr) = (unpack("S n a4 x8",getsockname(S)))[$[+1,$[+2];
+&log(sprintf("Listening at address %d.%d.%d.%d port %d",
+ unpack("C4",$my_addr), $my_port));
+
+;# disregister with all servers in case of termination
+sub cleanup
+{
+ &log("Aborted by signal \"$_[$[]\"") if defined($_[$[]);
+
+ foreach (@Hosts)
+ {
+ &ntp'send(S,31,0,"",pack("Sna4x8",&AF_INET,$ntp_port,$Hosts{$_})); #';
+ }
+ close(S);
+ exit(2);
+}
+
+$SIG{'HUP'} = 'cleanup';
+$SIG{'INT'} = 'cleanup';
+$SIG{'QUIT'} = 'cleanup';
+$SIG{'TERM'} = 'cleanup';
+
+0 && $a && $b;
+sub timeouts # sort timeout id array
+{
+ $TIMEOUTS{$a} <=> $TIMEOUTS{$b};
+}
+
+;# a Request element looks like: pack("a4SC",addr,associd,op)
+@Requests= ();
+
+;# compute requests for set trap control msgs to each host given
+{
+ local($name,$addr);
+
+ foreach (@Hosts)
+ {
+ if (/^(\d+)\.(\d+)\.(\d+)\.(\d+)$/)
+ {
+ ($name,$addr) =
+ (gethostbyaddr(pack("C4",$1,$2,$3,$4),&AF_INET))[$[,$[+4];
+ unless (defined($name))
+ {
+ $name = sprintf("[[%d.%d.%d.%d]]",$1,$2,$3,$4);
+ $addr = pack("C4",$1,$2,$3,$4);
+ }
+ }
+ else
+ {
+ ($name,$addr) = (gethostbyname($_))[$[,$[+4];
+ unless (defined($name))
+ {
+ warn "$0: unknown host \"$_\" - ignored\n";
+ next;
+ }
+ }
+ next if defined($Host{$name});
+ $Host{$name} = $addr;
+ push(@Requests,pack("a4SC",$addr,0,6)); # schedule a set trap request for $name
+ }
+}
+
+sub hostname
+{
+ local($addr) = @_;
+ return $HostName{$addr} if defined($HostName{$addr});
+ local($name) = gethostbyaddr($addr,&AF_INET);
+ &debug(sprintf("hostname(%d.%d.%d.%d) = \"%s\"",unpack("C4",$addr),$name))
+ if defined($name);
+ defined($name) && ($HostName{$addr} = $name) && (return $name);
+ &debug(sprintf("Failed to get name for %d.%d.%d.%d",unpack("C4",$addr)));
+ return sprintf("[%d.%d.%d.%d]",unpack("C4",$addr));
+}
+
+;# when no hosts were given on the commandline no requests have been scheduled
+&usage unless (@Requests);
+
+&debug(sprintf("%d request(s) scheduled",scalar(@Requests)));
+grep(&debug(" - ".$_),keys(%Host));
+
+;# allocate variables;
+$addr="";
+$assoc=0;
+$op = 0;
+$timeout = 0;
+$ret="";
+%TIMEOUTS = ();
+%TIMEOUT_PROCS = ();
+@TIMEOUTS = ();
+
+$len = 512;
+$buf = " " x $len;
+
+while (1)
+{
+ if (@Requests || @TIMEOUTS) # if there is some work pending
+ {
+ if (@Requests)
+ {
+ ($addr,$assoc,$op) = unpack("a4SC",($req = shift(@Requests)));
+ &debug(sprintf("Request: %s: %s(%d)",&hostname($addr), &ntp'cntrlop_name($op), $assoc)); #';))
+ $ret = &ntp'send(S,$op,$assoc,"", #'(
+ pack("Sna4x8",&AF_INET,$ntp_port,$addr));
+ &set_timeout("retry-".unpack("H*",$req),time+$BASE_TIMEOUT,
+ sprintf("&retry(\"%s\");",unpack("H*",$req)));
+
+ last unless (defined($ret)); # warn called by ntp'send();
+
+ ;# if there are more requests just have a quick look for new messages
+ ;# otherwise grant server time for a response
+ $timeout = @Requests ? 0 : $BASE_TIMEOUT;
+ }
+ if ($timeout && @TIMEOUTS)
+ {
+ ;# ensure not to miss a timeout
+ if ($timeout + time > $TIMEOUTS{$TIMEOUTS[$[]})
+ {
+ $timeout = $TIMEOUTS{$TIMEOUTS[$[]} - time;
+ $timeout = 0 if $timeout < 0;
+ }
+ }
+ }
+ else
+ {
+ ;# no work yet - wait for some messages dropping in
+ ;# usually this will not hapen as the refresh semantic will
+ ;# always have a pending timeout
+ undef($timeout);
+ }
+
+ vec($mask="",fileno(S),1) = 1;
+ $ret = select($mask,undef,undef,$timeout);
+
+ warn("$0: select: $!\n"),last if $ret < 0; # give up on error return from select
+
+ if ($ret == 0)
+ {
+ ;# timeout
+ if (@TIMEOUTS && time > $TIMEOUTS{$TIMEOUTS[$[]})
+ {
+ ;# handle timeout
+ $timeout_proc =
+ (delete $TIMEOUT_PROCS{$TIMEOUTS[$[]},
+ delete $TIMEOUTS{shift(@TIMEOUTS)})[$[];
+ eval $timeout_proc;
+ die "timeout eval (\"$timeout_proc\"): $@\n" if $@;
+ }
+ ;# else: there may be something to be sent
+ }
+ else
+ {
+ ;# data avail
+ $from = recv(S,$buf,$len,0);
+ ;# give up on error return from recv
+ warn("$0: recv: $!\n"), last unless (defined($from));
+
+ $from = (unpack("Sna4",$from))[$[+2]; # keep host addr only
+ ;# could check for ntp_port - but who cares
+ &debug("-Packet from ",&hostname($from));
+
+ ;# stuff packet into ntp mode 6 receive machinery
+ ($ret,$data,$status,$associd,$op,$seq,$auth_keyid) =
+ &ntp'handle_packet($buf,$from); # ';
+ &debug(sprintf("%s uses auth_keyid %d",&hostname($from),$auth_keyid)) if defined($auth_keyid);
+ next unless defined($ret);
+
+ if ($ret eq "")
+ {
+ ;# handle packet
+ ;# simple trap response messages have neither timeout nor retries
+ &clear_timeout("retry-".unpack("H*",pack("a4SC",$from,$associd,$op))) unless $op == 7;
+ delete $RETRY{pack("a4SC",$from,$associd,$op)} unless $op == 7;
+
+ &process_response($from,$ret,$data,$status,$associd,$op,$seq,$auth_keyid);
+ }
+ else
+ {
+ ;# some kind of error
+ &log(sprintf("%50s: %s: %s",(gethostbyaddr($from,&AF_INET))[$[],$ret,$data));
+ if ($ret ne "TIMEOUT" && $ret ne "ERROR")
+ {
+ &clear_timeout("retry-".unpack("H*",pack("a4SC",$from,$associd,$op)));
+ }
+ }
+ }
+
+}
+
+warn("$0: terminating\n");
+&cleanup;
+exit 0;
+
+;##################################################
+;# timeout support
+;#
+sub set_timeout
+{
+ local($id,$time,$proc) = @_;
+
+ $TIMEOUTS{$id} = $time;
+ $TIMEOUT_PROCS{$id} = $proc;
+ @TIMEOUTS = sort timeouts keys(%TIMEOUTS);
+ chop($date=&ctime($time));
+ &debug(sprintf("Schedule timeout \"%s\" for %s", $id, $date));
+}
+
+sub clear_timeout
+{
+ local($id) = @_;
+ delete $TIMEOUTS{$id};
+ delete $TIMEOUT_PROCS{$id};
+ @TIMEOUTS = sort timeouts keys(%TIMEOUTS);
+ &debug("Clear timeout \"$id\"");
+}
+
+0 && &refresh;
+sub refresh
+{
+ local($addr) = @_;
+ $addr = pack("H*",$addr);
+ &debug(sprintf("Refreshing trap for %s", &hostname($addr)));
+ push(@Requests,pack("a4SC",$addr,0,6));
+}
+
+0 && &retry;
+sub retry
+{
+ local($tag) = @_;
+ $tag = pack("H*",$tag);
+ $RETRY{$tag} = 0 if (!defined($RETRY{$tag}));
+
+ if (++$RETRY{$tag} > $MAX_TRY)
+ {
+ &debug(sprintf("Retry failed: %s assoc %5d op %d",
+ &hostname(substr($tag,$[,4)),
+ unpack("x4SC",$tag)));
+ return;
+ }
+ &debug(sprintf("Retrying: %s assoc %5d op %d",
+ &hostname(substr($tag,$[,4)),
+ unpack("x4SC",$tag)));
+ push(@Requests,$tag);
+}
+
+sub process_response
+{
+ local($from,$ret,$data,$status,$associd,$op,$seq,$auth_keyid) = @_;
+
+ $msg="";
+ if ($op == 7) # trap response
+ {
+ $msg .= sprintf("%40s trap#%-5d",
+ &hostname($from),$seq);
+ &debug (sprintf("\nTrap %d associd %d:\n%s\n===============\n",$seq,$associd,$data));
+ if ($associd == 0) # system event
+ {
+ $msg .= " SYSTEM ";
+ $evnt = &ntp'SystemEvent($status); #';
+ $msg .= "$evnt ";
+ ;# for special cases add additional info
+ ($stratum) = ($data =~ /stratum=(\d+)/);
+ ($refid) = ($data =~ /refid=([\w\.]+)/);
+ $msg .= "stratum=$stratum refid=$refid";
+ if ($refid =~ /\[?(\d+)\.(\d+)\.(\d+)\.(\d+)/)
+ {
+ $msg .= " " . (gethostbyaddr(pack("C4",$1,$2,$3,$4),&AF_INET))[$[];
+ }
+ if ($evnt eq "event_sync_chg")
+ {
+ $msg .= sprintf("%s %s ",
+ &ntp'LI($status), #',
+ &ntp'ClockSource($status) #'
+ );
+ }
+ elsif ($evnt eq "event_sync/strat_chg")
+ {
+ ($peer) = ($data =~ /peer=([0-9]+)/);
+ $msg .= " peer=$peer";
+ }
+ elsif ($evnt eq "event_clock_excptn")
+ {
+ if (($device) = ($data =~ /device=\"([^\"]+)\"/))
+ {
+ ($cstatus) = ($data =~ /refclockstatus=0?x?([\da-fA-F]+)/);
+ $Cstatus = hex($cstatus);
+ $msg .= sprintf("- %-32s",&ntp'clock_status($Cstatus)); #');
+ ($timecode) = ($data =~ /timecode=\"([^\"]+)\"/);
+ $msg .= " \"$device\" \"$timecode\"";
+ }
+ else
+ {
+ push(@Requests,pack("a4SC",$from, $associd, 4));
+ }
+ }
+ }
+ else # peer event
+ {
+ $msg .= sprintf("peer %5d ",$associd);
+ ($srcadr) = ($data =~ /srcadr=\[?([\d\.]+)/);
+ $msg .= sprintf("%-18s %40s ", "[$srcadr]",
+ &hostname(pack("C4",split(/\./,$srcadr))));
+ $evnt = &ntp'PeerEvent($status); #';
+ $msg .= "$evnt ";
+ ;# for special cases include additional info
+ if ($evnt eq "event_clock_excptn")
+ {
+ if (($device) = ($data =~ /device=\"([^\"]+)\"/))
+ {
+ ;#&debug("----\n$data\n====\n");
+ ($cstatus) = ($data =~ /refclockstatus=0?x?([\da-fA-F]+)/);
+ $Cstatus = hex($cstatus);
+ $msg .= sprintf("- %-32s",&ntp'clock_status($Cstatus)); #');
+ ($timecode) = ($data =~ /timecode=\"([^\"]+)\"/);
+ $msg .= " \"$device\" \"$timecode\"";
+ }
+ else
+ {
+ ;# no clockvars included - post a cv request
+ push(@Requests,pack("a4SC",$from, $associd, 4));
+ }
+ }
+ elsif ($evnt eq "event_stratum_chg")
+ {
+ ($stratum) = ($data =~ /stratum=(\d+)/);
+ $msg .= "new stratum $stratum";
+ }
+ }
+ }
+ elsif ($op == 6) # set trap resonse
+ {
+ &debug("Set trap ok from ",&hostname($from));
+ &set_timeout("refresh-".unpack("H*",$from),time+$REFRESH_TIME,
+ sprintf("&refresh(\"%s\");",unpack("H*",$from)));
+ return;
+ }
+ elsif ($op == 4) # read clock variables response
+ {
+ ;# status of clock
+ $msg .= sprintf(" %40s ", &hostname($from));
+ if ($associd == 0)
+ {
+ $msg .= "system clock status: ";
+ }
+ else
+ {
+ $msg .= sprintf("peer %5d clock",$associd);
+ }
+ $msg .= sprintf("%-32s",&ntp'clock_status($status)); #');
+ ($device) = ($data =~ /device=\"([^\"]+)\"/);
+ ($timecode) = ($data =~ /timecode=\"([^\"]+)\"/);
+ $msg .= " \"$device\" \"$timecode\"";
+ }
+ elsif ($op == 31) # unset trap response (UNOFFICIAL op)
+ {
+ ;# clear timeout
+ &debug("Clear Trap ok from ",&hostname($from));
+ &clear_timeout("refresh-".unpack("H*",$from));
+ return;
+ }
+ else # unexpected response
+ {
+ $msg .= "unexpected response to op $op assoc=$associd";
+ $msg .= sprintf(" status=%04x",$status);
+ }
+ &log($msg);
+}
diff --git a/usr.sbin/xntpd/scripts/monitoring/timelocal.pl b/usr.sbin/xntpd/scripts/monitoring/timelocal.pl
new file mode 100755
index 0000000..061f925
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/monitoring/timelocal.pl
@@ -0,0 +1,78 @@
+;# timelocal.pl
+;#
+;# Usage:
+;# $time = timelocal($sec,$min,$hours,$mday,$mon,$year,$junk,$junk,$isdst);
+;# $time = timegm($sec,$min,$hours,$mday,$mon,$year);
+
+;# These routines are quite efficient and yet are always guaranteed to agree
+;# with localtime() and gmtime(). We manage this by caching the start times
+;# of any months we've seen before. If we know the start time of the month,
+;# we can always calculate any time within the month. The start times
+;# themselves are guessed by successive approximation starting at the
+;# current time, since most dates seen in practice are close to the
+;# current date. Unlike algorithms that do a binary search (calling gmtime
+;# once for each bit of the time value, resulting in 32 calls), this algorithm
+;# calls it at most 6 times, and usually only once or twice. If you hit
+;# the month cache, of course, it doesn't call it at all.
+
+;# timelocal is implemented using the same cache. We just assume that we're
+;# translating a GMT time, and then fudge it when we're done for the timezone
+;# and daylight savings arguments. The timezone is determined by examining
+;# the result of localtime(0) when the package is initialized. The daylight
+;# savings offset is currently assumed to be one hour.
+
+CONFIG: {
+ package timelocal;
+
+ @epoch = localtime(0);
+ $tzmin = $epoch[2] * 60 + $epoch[1]; # minutes east of GMT
+ if ($tzmin > 0) {
+ $tzmin = 24 * 60 - $tzmin; # minutes west of GMT
+ $tzmin -= 24 * 60 if $epoch[5] == 70; # account for the date line
+ }
+
+ $SEC = 1;
+ $MIN = 60 * $SEC;
+ $HR = 60 * $MIN;
+ $DAYS = 24 * $HR;
+ $YearFix = ((gmtime(946684800))[5] == 100) ? 100 : 0;
+ 1;
+}
+
+sub timegm {
+ package timelocal;
+
+ $ym = pack(C2, @_[5,4]);
+ $cheat = $cheat{$ym} || &cheat;
+ $cheat + $_[0] * $SEC + $_[1] * $MIN + $_[2] * $HR + ($_[3]-1) * $DAYS;
+}
+
+sub timelocal {
+ package timelocal;
+
+ $ym = pack(C2, @_[5,4]);
+ $cheat = $cheat{$ym} || &cheat;
+ $cheat + $_[0] * $SEC + $_[1] * $MIN + $_[2] * $HR + ($_[3]-1) * $DAYS
+ + $tzmin * $MIN - 60 * 60 * ($_[8] != 0);
+}
+
+package timelocal;
+
+sub cheat {
+ $year = $_[5];
+ $month = $_[4];
+ $guess = $^T;
+ @g = gmtime($guess);
+ $year += $YearFix if $year < $epoch[5];
+ while ($diff = $year - $g[5]) {
+ $guess += $diff * (364 * $DAYS);
+ @g = gmtime($guess);
+ }
+ while ($diff = $month - $g[4]) {
+ $guess += $diff * (28 * $DAYS);
+ @g = gmtime($guess);
+ }
+ $g[3]--;
+ $guess -= $g[0] * $SEC + $g[1] * $MIN + $g[2] * $HR + $g[3] * $DAYS;
+ $cheat{$ym} = $guess;
+}
diff --git a/usr.sbin/xntpd/scripts/ntp-groper b/usr.sbin/xntpd/scripts/ntp-groper
new file mode 100755
index 0000000..1fd0cfe
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/ntp-groper
@@ -0,0 +1,95 @@
+#!/bin/sh
+#
+# ntpgroper host ...
+#
+# This script checks each hostname given as an argument to see if
+# it is running NTP. It reports one of the following messages (assume
+# the host is named "dumbo.hp.com":
+#
+# dumbo.hp.com not registered in DNS
+# dumbo.hp.com not responding to ping
+# dumbo.hp.com refused ntpq connection
+# dumbo.hp.com not responding to NTP
+# dumbo.hp.com answers NTP version 2, stratum: 3, ref: telford.nsa.hp.com
+# dumbo.hp.com answers NTP version 3, stratum: 3, ref: telford.nsa.hp.com
+#
+# It ain't pretty, but it is kinda useful.
+#
+# Walter Underwood, 11 Feb 1993, wunder@hpl.hp.com
+#
+# converted to /bin/sh from /bin/ksh by scott@ee.udel.edu 24 Mar 1993
+
+PATH="/usr/local/etc:$PATH" export PATH
+
+verbose=1
+logfile=/tmp/cntp-log$$
+ntpqlog=/tmp/cntp-ntpq$$
+
+# I wrap the whole thing in parens so that it is possible to redirect
+# all the output somewhere, if desired.
+(
+for host in $*
+do
+ # echo "Trying $host."
+
+ gethost $host > /dev/null 2>&1
+ if [ $? -ne 0 ]
+ then
+ echo "$host not registered in DNS"
+ continue
+ fi
+
+ ping $host 64 1 > /dev/null 2>&1
+ if [ $? -ne 0 ]
+ then
+ echo "$host not responding to ping"
+ continue
+ fi
+
+ # Attempt to contact with version 3 ntp, then try version 2.
+ for version in 3 2
+ do
+
+ ntpq -c "ntpversion $version" -p $host > $ntpqlog 2>&1
+
+ if fgrep -s 'Connection refused' $ntpqlog
+ then
+ echo "$host refused ntpq connection"
+ break
+ fi
+
+ responding=1
+ fgrep -s 'timed out, nothing received' $ntpqlog > /dev/null && responding=0
+
+ if [ $responding -eq 1 ]
+ then
+ ntpq -c "ntpversion $version" -c rl $host > $ntpqlog
+
+ # First we extract the reference ID (usually a host or a clock)
+ synchost=`fgrep "refid=" $ntpqlog`
+ #synchost=${synchost##*refid=} # strip off the beginning of the line
+ #synchost=${synchost%%,*} # strip off the end
+ synchost=`expr "$synchost" : '.*refid=\([^,]*\),.*'`
+
+ # Next, we get the stratum
+ stratum=`fgrep "stratum=" $ntpqlog`
+ #stratum=${stratum##*stratum=}
+ #stratum=${stratum%%,*}
+ stratum=`expr "$stratum" : '.*stratum=\([^,]*\),.*'`
+
+ echo "$host answers NTP version $version, stratum: $stratum, ref: $synchost"
+ break;
+ fi
+
+ if [ $version -eq 2 -a $responding -eq 0 ]
+ then
+ echo "$host not responding to NTP"
+ fi
+ done
+done
+)
+# ) >> $logfile
+
+if [ -f $ntpqlog ]; then
+ rm $ntpqlog
+fi
diff --git a/usr.sbin/xntpd/scripts/ntp-restart b/usr.sbin/xntpd/scripts/ntp-restart
new file mode 100755
index 0000000..d2023f0
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/ntp-restart
@@ -0,0 +1,9 @@
+#!/bin/sh
+#
+# This script can be used to kill and restart the NTP daemon. Edit the
+# /usr/local/bin/xntpd line to fit.
+#
+kill -INT `ps -ax | egrep "xntpd" | egrep -v "egrep" | sed 's/^\([ 0-9]*\) .*/\1'/`
+sleep 10
+/usr/local/bin/xntpd
+exit 0
diff --git a/usr.sbin/xntpd/scripts/stats/README b/usr.sbin/xntpd/scripts/stats/README
new file mode 100644
index 0000000..6808963
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/stats/README
@@ -0,0 +1,39 @@
+Statistics processing scripts (README)
+
+This directory contains a number of scripts for use with the filegen
+facility. Those files ending in .awk are for the Unix awk utility, while
+those ending in .sh are for the csh utility. Normally, the summary.sh
+script is called from a cron job once per day. This script processes the
+daily loopstats, peerstats and clockstats files produced by the daemon,
+updates the loop_summary, peer_summary and clock_summary archive files,
+and deletes the daily files.
+
+In the case of the Austron 2201A GPS receiver, the clockstats file
+contains a wealth of additional monitoring data. These data are summarized
+and writted to the clock_summary file, then a series of special files are
+constructed for later processing by the S utility.
+
+The summary.sh script invokes a number of awk scripts to actually produce
+the data. This may result in multiple scans of the same input file.
+The input file is deleted after processing. In fact, the shell scripts will
+process all input files found of the correct type in chronological order,
+deleting each one as it is scanned, except the current day file.
+
+The summary.sh script can produce input files for the S utility, if it
+is found on the search path. This utility makes PostScript graphs of the
+loopstats data for each day, as well as various statistics produced by
+the Austorn 220aA GPS receiver. The S utility is automatically run
+as a background job. Its control files have the .S extension.
+
+The psummary.awk script can be used to scan the peer_summary file and
+construct an historical reprise of the daily summaries.
+
+The file formats are documented in the README.stats file and in the
+scripts themselves. Further detail on the radio clock ASCII timecode
+formats and related data are in the README.timecode file.
+
+David L. Mills
+University of Delaware
+mills@udel.edu
+1 November 1993
+Revised 12 April 1994
diff --git a/usr.sbin/xntpd/scripts/stats/README.stats b/usr.sbin/xntpd/scripts/stats/README.stats
new file mode 100644
index 0000000..aa8e77f
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/stats/README.stats
@@ -0,0 +1,246 @@
+Statistics file formats (README.stats)
+
+The xntp3 daemon can produce a variety of statistics files which are
+useful for maintenance, evaluation and retrospective calibration
+purposes. See the xntpd.8 man page for instructions on how to configure
+this feature. Since these files can become rather large and cumbersome,
+they are ordinarily reduced to summary form by running the summary.sh
+shell script once per day, week or month, as appropriate. There are
+three file collections presently defined: peerstats, loopstats and
+clockstats, each of which is described in this note.
+
+peerstats
+
+The following data are collected in the peerstats files. The files are
+reduced to summary data using the peer.sh shell script. See the peer.awk
+script for further information. A line in the file is produced upon
+reception of each valid update from a configured peer.
+
+ 49236 30.756 140.173.96.1 9474 0.000603 0.37532
+
+ 49236 modified Julian day number
+ 30.756 time of day (s) past midnight UTC
+ 140.173.96.1 peer identifier (IP address or receiver identifier)
+ 9474 peer status word (hex) (see NTP specification)
+ 0.000603 offset (s)
+ 0.08929 delay (s)
+ 0.37532 dispersion (s)
+
+loopstats
+
+The following data are collected in the loopstats files. The files are
+reduced to summary data using the loop.sh shell script. See the loop.awk
+script for further information. A line in the file is produced at each
+valid update of the local clock.
+
+ 49236 11.897 -0.000004 -35.9384 0
+
+ 49236 modified Julian day number
+ 11.897 time of day (s) past midnight UTC
+ -0.000004 time offset (s)
+ -35.9384 frequency offset (ppm)
+ 0 phase-lock loop time constant
+
+clockstats
+
+The following data are collected in the clockstats files. The files are
+reduced to summary data using the clock.sh shell script, which also
+updates the ensemble, etf, itf and tdata data files as well. See the
+clock.awk, ensemble.awk, etf.awk, itf.awk and tdta.awk scripts for
+further information. A line in the file is produced at each valid update
+received from a configured radio clock. Data are at present recorded for
+several radios. The first part of each data line is similar for all
+radios, e.g.:
+
+ 49234 60517.826 127.127.4.1 93 247 16:48:21.814
+
+ 49234 modified Julian day number
+ 60517.826 time of day (s) past midnight UTC
+ 127.127.4.1 receiver identifier (Spectracom 8170/Netclock-2)
+ 93 247 16:48:21.814 timecode (format varies)
+
+In the case of the Austron GPS receiver, a good deal of additional
+information is extracted from the radio, as described below. The formats
+shown consist of one line with all the fields shown in order. The
+timecode formats specific to each radio follow. See the file
+README.timecodes for detailed information on the timecode formats used
+by these radios.
+
+Spectracom 8170/Netclock-2 WWVB receiver
+
+ 49234 60517.826 127.127.4.1 ?A93 247 16:48:21.814
+
+ The '?' and 'A' characters are present only when the receiver is
+ unsynchronized; otherwise, they are replaced by space ' ' characters.
+
+IRIG audio decoder
+
+ 49234 60517.826 127.127.6.0 247 16:48:21?
+
+ The '?' character is present only when the receiver is unsynchronized.
+
+Austron 2200A/2201A GPS receiver
+
+ 49234 60580.843 127.127.10.1 93:247:16:49:24.814?
+
+ The '?' character is present only when the receiver is unsynchronized.
+
+Depending on the installed options, the Austron 2200A/2201A recognizes a
+number of special commands that report various data items. See the
+refclock_as2201.c source module for a list of the commands used. These
+data are collected only if the following line is included in the
+configuration file ntp.conf:
+
+ fudge 127.127.10.1 flag4 1 # enable extended statistics collection
+
+The format of each data line returned is summarized in the following
+list.
+
+External time/frequency data (requires input buffer option IN)
+
+These data determine the deviations of external time/frequency inputs
+relative to receiver oscillator time. The following data are typical
+using an external cesium oscillator PPS and 5-MHz outputs.
+
+ 49234 60580.843 127.127.10.1 93:247:16:49:24.814 ETF
+
+ -85.9 time interval (ns)
+ -89.0 average time interval (ns)
+ 4.0 time interval sigma (ns)
+ +1.510E-11 time interval rate
+ -4.500E-11 deltaf/f
+ +1.592E-11 average deltaf/f
+ 5.297E-13 sigma deltaf/f
+ 500 number of samples
+
+Model and option identifiers
+
+These data show the receiver model number and option configuration.
+
+ 49234 60708.848 127.127.10.1 93:247:16:51:32.817 ID;OPT;VER
+
+ GPS 2201A model ident (must be "GPS 2200A" or "GPS 2201A")
+ TTY1 rs232 option present (required)
+ TC1 IRIG option present (optional)
+ LORAN LORAN assist option present (optional)
+ IN input buffer option present (optional)
+ OUT1 output buffer option present (required)
+ B.00 data processor software version ("B.00" or later)
+ B.00 signal processor software version ("B.00" or later)
+ 28-Apr-93 software version date ("28-Apr-93" or later)
+
+Internal time/frequency data
+
+These data determine the deviations of the receiver oscillator with
+respect to satellite time.
+
+ 49234 60564.846 127.127.10.1 93:247:16:49:08.816 ITF
+
+ COCO current mode (must be "COCO")
+ 0 code coast mode (must be zero)
+ +6.6152E-08 code sigma (s)
+ -3.5053E-08 code delta t (s)
+ -4.0361E-11 deltat/t
+ -6.4746E-11 oscillator ageing rate
+ 500.00 loop time constant
+ 4.984072 electrical tuning (V)
+
+GPS/LORAN ensemble data (requires LORAN assist option LORAN)
+
+These data determine the deviations and weights to calculate ensemble
+time from GPS and LORAN data.
+
+ 49234 60596.852 127.127.10.1 93:247:16:49:40.812 LORAN ENSEMBLE
+
+ +9.06E-08 GPS t (s)
+ +3.53E-08 GPS sigma (s)
+ .532 GPS weight
+ +3.71E-08 LORAN t (s)
+ +3.76E-08 LORAN sigma (s)
+ .468 LORAN weight
+ +6.56E-08 ensemble t
+ +6.94E-08 ensemble sigma (s)
+
+LORAN stationkeeping data (requires LORAN assist option LORAN)
+
+These data determine which stations of the LORAN chain are being
+tracked, together with individual signal/noise ratios, deviations and
+weights.
+
+ 49234 60532.850 127.127.10.1 93:247:16:48:36.820 LORAN TDATA
+
+ M station identifier; data follows
+ OK status (must be "OK" for tracking)
+ 0 cw flag
+ 0 sw flag
+ 1162.17 time of arrival
+ -4.6 snr (-30.0 if not "OK" status)
+ 1.67E-07 2-sample phase-time deviation
+ .507 weight (included only if "OK" status)
+ W AQ 0 0 3387.80 -31.0 station identifier and data
+ X OK 0 0 1740.27 -11.2 2.20E-07 .294 station identifier and data
+ Y OK 0 0 2180.71 -4.6 2.68E-07 .198 station identifier and data
+ Z CV 0 0 3392.94 -30.0 station identifier and data
+
+Oscillator status and environment
+
+These data determine the receiver oscillator type, mode, status and
+environment. Nominal operating conditions are shown below.
+
+ 49234 60628.847 127.127.10.1 93:247:16:50:12.817 OSC;ET;TEMP
+
+ 1121 Software Control oscillator model and mode (must be
+ "Software Control")
+ Locked status (must be "Locked")
+ 4.979905 electrical tuning (V)
+ 44.81 oscillator cavity temperature
+
+Receiver position, status and offsets
+
+These data determine the receiver position and elevation, together with
+programmable delay corrections for the antenna cable and receiver.
+
+ 49234 60788.847 127.127.10.1 93:247:16:52:52.817 POS;PPS;PPSOFF
+
+ +39:40:48.425 receiver latitude (N)
+ -075:45:02.392 receiver longitude (E)
+ +74.09 receiver elevation (m)
+ Stored position status (must be "Stored")
+ UTC PPS/PPM alignment (must be "UTC")
+ 0 receiver delay (ns) (should be zero for calibrated
+ receiver)
+ 200 cable delay (ns)
+ 0 user time bias (ns) (must be zero)
+
+Satellite tracking status
+
+These data determine how many satellites are being tracked. At the
+present state of constellation development, there should be at least
+three visible satellites in view. Much of the time the maximum of
+seven are being tracked; rarely this number drops to two.
+
+ 49234 60612.850 127.127.10.1 93:247:16:49:56.820 TRSTAT
+
+ 24 T satellite prn and status (T = track, A = acquire)
+ 16 A 13 T 20 T 18 T 07 T 12 T list continued
+
+UTC leap-second information
+
+These data determine when the next leap second is to occur. The exact
+method to use is obscure.
+
+ 49234 60548.847 127.127.10.1 93:247:16:48:52.818 UTC
+
+ -1.2107E-08 A0 term (s)
+ -1.2790E-13 A1 term (s)
+ +9.0000E+00 current leap seconds (s)
+ +2.0480E+05 time for leap seconds (s)
+ +2.0100E+02 week number for delta leap (weeks)
+ +1.9100E+02 week number for future leap (weeks)
+ +4.0000E+00 day number for future leap (days)
+ +9.0000E+00 future leap seconds (s)
+
+David L. Mills
+University of Delaware
+mills@udel.edu
+23 October 1993
diff --git a/usr.sbin/xntpd/scripts/stats/README.timecodes b/usr.sbin/xntpd/scripts/stats/README.timecodes
new file mode 100644
index 0000000..00b5ba5
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/stats/README.timecodes
@@ -0,0 +1,149 @@
+Radio Timecode Formats (README.timecodes)
+
+Following are examples of the serial timecode formats used by various
+timecode receivers as given in the instruction manuals. These examples
+are intended only for illustration and not as the basis of system
+design. The following symbols are used to identify the timecode
+character that begins a subfield. The values given after this symbol
+represent the character offset from the beginning of the timecode string
+as edited to remove control characters.
+
+C on-time character (start bit)
+Y year of century
+T time of day
+D day of year or month/day
+A alarm indicator (format specific)
+Q quality indicator (format specific)
+<LF> ASCII line feed (hex 0a)
+<CR> ASCII carriage return (hex 0d)
+<SP> ASCII space (hex 20)
+
+In order to promote uniform behavior in the various implementations, it
+is useful to have a common interpretation of alarm conditions and signal
+quality. When the alarm indicator it on, the receiver is not operating
+correctly or has never synchronized to the broadcast signal. When the
+alarm indicator is off and the quality indicator is on, the receiver has
+synchronized to the broadcast signal, then lost the signal and is
+coasting on its internal oscillator.
+
+In the following uppercase letters, punctuation marks and spaces <SP>
+stand for themselves; lowercase letters stand for fields as described.
+Special characters other than <LF>, <CR> and <SP> are preceded by ^.
+
+Spectracom 8170 and Netclock/2 WWV Synchonized Clock (format 0)
+
+"<CR><LF>i ddd hh:mm:ss TZ=zz<CR><LF>"
+ C A D T
+
+ poll: ?; offsets: Y = none, D = 3, T = 7, A = 0, Q = none
+ i = synchronization flag (<SP> = in synch, ? = out synch)
+ ddd = day of year
+ hh:mm:ss = hours, minutes, seconds
+ zz = timezone offset (hours from UTC)
+
+ Note: alarm condition is indicated by other than <SP> at A, which
+ occurs during initial synchronization and when received signal has
+ been lost for about ten hours
+
+ example: " 216 15:36:43 TZ=0"
+ A D T
+
+Netclock/2 WWV Synchonized Clock (format 2)
+
+"<CR><LF>iqyy ddd hh:mm:ss.fff ld"
+ C AQY D T
+
+ poll: ?; offsets: Y = 2, D = 5, T = 9, A = 0, Q = 1
+ i = synchronization flag (<SP> = in synch, ? = out synch)
+ q = quality indicator (<SP> < 1ms, A < 10 ms, B < 100 ms, C < 500
+ ms, D > 500 ms)
+ yy = year (as broadcast)
+ ddd = day of year
+ hh:mm:ss.fff = hours, minutes, seconds, milliseconds of day
+ l = leap-second warning (L indicates leap at end of month)
+ d = standard/daylight time indicator (<SP> standard, D daylight)
+
+ Note: alarm condition is indicated by other than <SP> at A, which
+ occurs during initial synchronization and when received signal has
+ been lost for about ten hours; unlock condition is indicated by
+ other than <SP> at Q, with time since last lock indicated by the
+ letter code A < 13 min, B < 1.5 hr, C < 7 hr, D > 7 hr.
+
+ example: " 92 216 15:36:43.640 D"
+ AQ D T
+
+TrueTime 468-DC Satellite Synchronized Clock (and other TrueTime
+receivers)
+
+"<CR><LF><^A>ddd:hh:mm:ssq<CR>"
+ D T QC
+
+ poll: none; offsets: Y = none, D = 0, T = 4, A = 12, Q = 12
+ hh:mm:ss = hours, minutes, seconds
+ q = quality/alarm indicator (<SP> = locked, ? = alarm)
+
+ Note: alarm condition is indicated by ? at A, which occurs during
+ initial synchronization and when received signal is lost for an
+ extended period; unlock condition is indicated by other than <SP>
+ at Q
+
+ example: "216:15:36:43 "
+ D T Q
+
+Heath GC-1000 Most Accurate Clock (WWV/H)
+
+"<CR>hh:mm:ss.f dd/mm/yy<CR>"
+ C T A D
+
+ poll: none; offsets: Y = none, D = 15, T = 0, A = 9, Q = none
+ hh:mm:ss = hours, minutes, seconds
+ f = deciseconds (? when out of spec)
+ dd/mm = day, month
+ yy = year of century (from DIPswitches)
+
+ Note: 0?:??:??.? is displayed before synch is first established and
+ hh:mm:ss.? once synch is established and then lost again for about
+ a day.
+
+ example: "15:36:43.6 04/08/91"
+ T A D Y
+
+PST/Traconex 1020 Time Source (WWV/H) (firmware revision V4.01)
+
+"frdzycchhSSFTttttuuxx<CR>" "ahh:mm:ss.fffs<CR>" "yy/dd/mm/ddd<CR>"
+ A Q T Y D
+
+ poll: "QMQDQT"; offsets: Y = 0, D = 3 T = 1,, A = 11, Q = 13
+ f = frequency enable (O = all frequencies enabled)
+ r = baud rate (3 = 1200, 6 = 9600)
+ d = features indicator (@ = month/day display enabled)
+ z = time zone (0 = UTC)
+ y = year (5 = 1991)
+ cc = WWV propagation delay (52 = 22 ms)
+ hh = WWVH propagation delay (81 = 33 ms)
+ SS = status (80 or 82 = operating correctly)
+ F = current receive frequency (1-5 = 2.5, 5, 10, 15, 20 MHz)
+ T = transmitter (C = WWV, H = WWVH)
+ tttt = time since last update (minutes)
+ uu = flush character (03 = ^C)
+ xx = 94 (unknown) (firmware revision X4.01.999 only)
+
+ a = AM/PM indicator (A = AM, P = PM, <SP> - 24-hour format)
+ hh:mm:ss.fff = hours, minutes, seconds, milliseconds of day
+ s = daylight-saving indicator (<SP> standard, D daylight)
+
+ yy = year of century (from DIPswitches)
+ dd/mm/ddd = day of month, month of year, day of year
+
+ Note: The alarm condition is indicated by other than ? at A, which
+ occurs during initial synchronization and when received signal is
+ lost for an extended period. A receiver unlock condition is
+ indicated by other than "0000" in the tttt subfield at Q.
+
+ example: "O3@055281824C00000394 91/08/04/216 15:36:43.640"
+ T Y D T
+
+David L. Mills
+University of Delaware
+mills@udel.edu
+23 October 1993
diff --git a/usr.sbin/xntpd/scripts/stats/clock.awk b/usr.sbin/xntpd/scripts/stats/clock.awk
new file mode 100644
index 0000000..c9d1455
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/stats/clock.awk
@@ -0,0 +1,341 @@
+# awk program to scan clockstat files and report errors/statistics
+#
+# usage: awk -f check.awk clockstats
+#
+# This program works for Spectracom 8170/Netclock-2 receiver, Austron
+# 2200A/2201A receiver and IRIG audio decoder. It is easily adapted to
+# other receivers as required. See README.austron file for additional
+# information on Austron receiver.
+#
+BEGIN {
+ etf_min = osc_vmin = osc_tmin = 1e9
+ etf_max = osc_vmax = osc_tmax = -1e9
+}
+#
+# scan all records in file
+#
+{
+ #
+ # select WWVB records
+ # see summary for decode
+ #
+ if (NF >= 4 && $3 == "127.127.4.1") {
+ if ($4 == "SIGNAL" || NF > 7)
+ printf "%s\n", $0
+ else {
+ wwvb_count++
+ if ($4 ~ /\?/)
+ wwvb_x++
+ else if ($4 ~ /A/)
+ wwvb_a++
+ else if ($4 ~ /B/)
+ wwvb_b++
+ else if ($4 ~ /C/)
+ wwvb_c++
+ else if ($4 ~ /D/)
+ wwvb_d++
+ }
+ continue
+ }
+ #
+ # select IRIG records
+ # see summary for decode
+ #
+ if (NF >= 4 && $3 == "127.127.6.0") {
+ irig_count++
+ if ($5 ~ /\?/)
+ irig_error++
+ continue
+ }
+ #
+ # select LORAN ENSEMBLE records
+ # see summary for decode
+ #
+ else if (NF >= 13 && $6 == "ENSEMBLE") {
+ ensemble_count++
+ if ($9 <= 0)
+ ensemble_badgps++
+ else if ($12 <= 0)
+ ensemble_badloran++
+ else {
+ if ($13 > 200e-9 || $13 < -200e-9)
+ ensemble_200++
+ else if ($13 > 100e-9 || $13 < -100e-9)
+ ensemble_100++
+ ensemble_mean += $13
+ ensemble_rms += $13 * $13
+ }
+ continue
+ }
+ #
+ # select LORAN TDATA records
+ # see summary for decode; note that signal quality log is simply
+ # copied to output
+ #
+ else if (NF >= 7 && $6 == "TDATA") {
+ tdata_count++
+ for (i = 7; i < NF; i++) {
+ if ($i == "M" && $(i+1) == "OK") {
+ i += 5
+ m += $i
+ tdata_m++
+ }
+ else if ($i == "W" && $(i+1) == "OK") {
+ i += 5
+ w += $i
+ tdata_w++
+ }
+ else if ($i == "X" && $(i+1) == "OK") {
+ i += 5
+ x += $i
+ tdata_x++
+ }
+ else if ($i == "Y" && $(i+1) == "OK") {
+ i += 5
+ y += $i
+ tdata_y++
+ }
+ else if ($i == "Z" && $(i+1) == "OK") {
+ i += 5
+ z += $i
+ tdata_z++
+ }
+ }
+ continue
+ }
+ #
+ # select ITF records
+ # see summary for decode
+ #
+ else if (NF >= 13 && $5 == "ITF" && $12 >= 100) {
+ itf_count++
+ if ($9 > 200e-9 || $9 < -200e-9)
+ itf_200++
+ else if ($9 > 100e-9 || $9 < -100e-9)
+ itf_100++
+ itf_mean += $9
+ itf_rms += $9 * $9
+ itf_var += $10 * $10
+ continue
+ }
+ #
+ # select ETF records
+ # see summary for decode
+ #
+ else if (NF >= 13 && $5 == "ETF" && $13 >= 100) {
+ etf_count++
+ if ($6 > etf_max)
+ etf_max = $6
+ else if ($6 < etf_min)
+ etf_min = $6
+ etf_mean += $6
+ etf_rms += $6 * $6
+ etf_var += $9 * $9
+ continue
+ }
+ #
+ # select TRSTAT records
+ # see summary for decode
+ #
+ else if (NF >= 5 && $5 == "TRSTAT") {
+ trstat_count++
+ j = 0
+ for (i = 6; i <= NF; i++)
+ if ($i == "T")
+ j++
+ trstat_sat[j]++
+ continue
+ }
+ #
+ # select ID;OPT;VER records
+ #
+ # config GPS 2201A TTY1 TC1 LORAN IN OUT1 B.00 B.00 28-Apr-93
+ #
+ # GPS 2201A receiver model
+ # TTY1 rs232 moduel
+ # TC1 IRIG module
+ # LORAN LORAN assist module
+ # IN input module
+ # OUT1 output module
+ # B.00 B.00 firmware revision
+ # 28-Apr-9 firmware date3
+ #
+ else if (NF >= 5 && $5 == "ID;OPT;VER") {
+ id_count++
+ id_temp = ""
+ for (i = 6; i <= NF; i++)
+ id_temp = id_temp " " $i
+ if (id_string != id_temp)
+ printf "config%s\n", id_temp
+ id_string = id_temp
+ continue
+ }
+ #
+ # select POS;PPS;PPSOFF records
+ #
+ # position +39:40:48.425 -075:45:02.392 +74.09 Stored UTC 0 200 0
+ #
+ # +39:40:48.425 position north latitude
+ # -075:45:02.392 position east longitude
+ # +74.09 elevation (meters)
+ # Stored position is stored
+ # UTC time is relative to UTC
+ # 0 200 0 PPS offsets
+ #
+ else if (NF >= 5 && $5 == "POS;PPS;PPSOFF") {
+ pos_count++
+ pos_temp = ""
+ for (i = 6; i <= NF; i++)
+ pos_temp = pos_temp " " $i
+ if (pos_string != pos_temp)
+ printf "position%s\n", pos_temp
+ pos_string = pos_temp
+ continue
+ }
+ #
+ # select OSC;ET;TEMP records
+ #
+ # loop 1121 Software Control Locked
+ #
+ # 1121 oscillator type
+ # Software Control loop is under software control
+ # Locked loop is locked
+ #
+ else if (NF >= 5 && $5 == "OSC;ET;TEMP") {
+ osc_count++
+ osc_temp = $6 " " $7 " " $8 " " $9
+ if (osc_status != osc_temp)
+ printf "loop %s\n", osc_temp
+ osc_status = osc_temp
+ if ($10 > osc_vmax)
+ osc_vmax = $10
+ if ($10 < osc_vmin)
+ osc_vmin = $10
+ if ($11 > osc_tmax)
+ osc_tmax = $11
+ if ($11 < osc_tmin)
+ osc_tmin = $11
+ continue
+ }
+ #
+ # select UTC records
+ # these ain't ready yet
+ #
+ else if (NF >= 5 && $5 == "UTC") {
+ utc_count++
+ utc_temp = ""
+ for (i = 6; i <= NF; i++)
+ utc_temp = utc_temp " " $i
+ if (utc_string != utc_temp)
+# printf "utc%s\n", utc_temp
+ utc_string = utc_temp
+ continue
+ }
+} END {
+#
+# ensemble summary data
+#
+# ensemble record count
+# badgps gps data unavailable
+# badloran loran data unavailable
+# rms ensemble rms error (ns)
+# >200 ensemble error >200 ns
+# >100 100 ns < ensemble error < 200 ns
+#
+ if (ensemble_count > 0) {
+ ensemble_mean /= ensemble_count
+ ensemble_rms = sqrt(ensemble_rms / ensemble_count - ensemble_mean * ensemble_mean) * 1e9
+ printf "ensemble %d, badgps %d, badloran %d, rms %.1f, >200 %d, >100 %d\n", ensemble_count, ensemble_badgps, ensemble_badloran, ensemble_rms, ensemble_200, ensemble_100
+ }
+#
+# wwvb summary data
+#
+# wwvb record count
+# ? unsynchronized
+# >1 error > 1 ms
+# >10 error > 10 ms
+# >100 error > 100 ms
+# >500 error > 500 ms
+#
+ if (wwvb_count > 0)
+ printf "wwvb %d, ? %d, >1 %d, >10 %d, >100 %d, >500 %d\n", wwvb_count, wwvb_x, wwvb_a, wwvb_b, wwvb_c, wwvb_d
+#
+# irig summary data
+#
+# irig record count
+# err error count
+#
+ if (irig_count > 0)
+ printf "irig %d, err %d\n", irig_count, irig_error
+#
+# tdata summary data
+#
+# tdata record count
+# m M master OK-count, mean level (dB)
+# w W slave OK-count, mean level (dB)
+# x X slave OK-count, mean level (dB)
+# y Y slave OK-count, mean level (dB)
+# z Z slave OK-count, mean level (dB)
+#
+ if (tdata_count > 0 ) {
+ if (tdata_m > 0)
+ m /= tdata_count
+ if (tdata_x > 0)
+ w /= tdata_count
+ if (tdata_x > 0)
+ x /= tdata_count
+ if (tdata_y > 0)
+ y /= tdata_count
+ if (tdata_z > 0)
+ z /= tdata_count
+ printf "tdata %d, m %d %.1f, w %d %.1f, x %d %.1f, y %d %.1f, z %d %.1f\n", tdata_count, tdata_m, m, tdata_w, w, tdata_x, x, tdata_y, y, tdata_z, z
+ }
+#
+# itf summary data
+#
+# itf record count
+# rms itf rms error (ns)
+# >200 itf error > 200 ns
+# >100 itf error > 100 ns
+# var Allan variance
+#
+ if (itf_count > 1) {
+ itf_mean /= itf_count
+ itf_rms = sqrt(itf_rms / itf_count - itf_mean * itf_mean) * 1e9
+ itf_var = sqrt(itf_var / (2 * (itf_count - 1)))
+ printf "itf %d, rms %.1f, >200 %d, >100 %d, var %.2e\n", itf_count, itf_rms, itf_200, itf_100, itf_var
+ }
+#
+# etf summary data
+#
+# etf record count
+# mean etf mean (ns)
+# rms etf rms error (ns)
+# max etf maximum (ns)
+# min etf minimum (ns)
+# var Allan variance
+#
+ if (etf_count > 0) {
+ etf_mean /= etf_count
+ etf_rms = sqrt(etf_rms / etf_count - etf_mean * etf_mean)
+ etf_var = sqrt(etf_var / (2 * (etf_count - 1)))
+ printf "etf %d, mean %.1f, rms %.1f, max %d, min %d, var %.2e\n", etf_count, etf_mean, etf_rms, etf_max, etf_min, etf_var
+ }
+#
+# trstat summary data
+#
+# trstat record count
+# sat histogram of tracked satellites (0 - 7)
+#
+ if (trstat_count > 0)
+ printf "trstat %d, sat %d %d %d %d %d %d %d %d\n", trstat_count, trstat_sat[0], trstat_sat[1], trstat_sat[2], trstat_sat[2], trstat_sat[3], trstat_sat[4], trstat_sat[5], trstat_sat[6], trstat_sat[7]
+#
+# osc summary data
+#
+# osc record count
+# control control midrange (V) +/- deviation (mV)
+# temp oven temperature midrange +/- deviation (deg C)
+#
+ if (osc_count > 0)
+ printf "osc %d, control %.3f+/-%.3f, temp %.1f+/-%.2f\n", osc_count, (osc_vmax + osc_vmin) / 2, (osc_vmax - osc_vmin) / 2 * 1e3, (osc_tmax + osc_tmin) / 2, (osc_tmax - osc_tmin) / 2
+}
diff --git a/usr.sbin/xntpd/scripts/stats/clock.sh b/usr.sbin/xntpd/scripts/stats/clock.sh
new file mode 100755
index 0000000..1866d55
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/stats/clock.sh
@@ -0,0 +1,17 @@
+#!/bin/csh
+#
+# Script to summarize clockstats files
+#
+set x = `ls clockstats.*`
+foreach dayfile ( $x )
+ if ($dayfile == $x[$#x]) continue
+ echo " "
+ echo $dayfile
+ awk -f clock.awk $dayfile
+ awk -f itf.awk $dayfile >>itf
+ awk -f etf.awk $dayfile >>etf
+ awk -f ensemble.awk $dayfile >>ensemble
+ awk -f tdata.awk $dayfile >>tdata
+ rm -f $dayfile
+end
+
diff --git a/usr.sbin/xntpd/scripts/stats/dupe.awk b/usr.sbin/xntpd/scripts/stats/dupe.awk
new file mode 100644
index 0000000..317c2a4
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/stats/dupe.awk
@@ -0,0 +1,8 @@
+#
+# delete duplicate lines
+#
+{
+ if (old != $0)
+ printf "%s\n", $0
+ old = $0
+}
diff --git a/usr.sbin/xntpd/scripts/stats/ensemble.S b/usr.sbin/xntpd/scripts/stats/ensemble.S
new file mode 100644
index 0000000..32a4dba
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/stats/ensemble.S
@@ -0,0 +1,5 @@
+ensemble <- scan(file1, list(day=0, sec=0, gps=0, gpsw=0, loran=0, loranw=0, ensemble=0, std=0))
+str <- paste("eps/", file1, ".eps", sep="")
+postscript(str, , , , 5, pointsize=18)
+par(mgp=c(1, 0, 0), tck = 0.03, mar = c(2, 2, 1, 1))
+plot(ensemble$sec, ensemble$ensemble, type="l", xlab=paste("MJD", ensemble$day, "Time (s)"), ylab="Ensemble Offset (ns)", ylim=c(-400, 400))
diff --git a/usr.sbin/xntpd/scripts/stats/ensemble.awk b/usr.sbin/xntpd/scripts/stats/ensemble.awk
new file mode 100644
index 0000000..136b33d
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/stats/ensemble.awk
@@ -0,0 +1,17 @@
+# program to produce loran ensemble statistics from clockstats files
+#
+# usage: awk -f ensemble.awk clockstats
+#
+# format of input record (time values in seconds)
+# 49165 8.628 127.127.10.1 93:178:00:00:07.241 LORAN ENSEMBLE
+# -6.43E-08 +5.02E-08 .091 +5.98E-08 +1.59E-08 .909 +4.85E-08 +3.52E-08
+#
+# format of output record (time values in nanoseconds)
+# MJD sec GPS wgt LORAN wgt avg sigma
+# 49165 8.628 -64.3 0.091 59.8 0.909 48.5 35.2
+#
+# select LORAN ENSEMBLE records with valid format and weights
+{
+ if (NF >= 14 && $6 == "ENSEMBLE" && $9 > 0 && $12 > 0)
+ printf "%5s %9.3f %7.1f %6.3f %7.1f %6.3f %7.1f %7.1f\n", $1, $2, $7*1e9, $9, $10*1e9, $12, $13*1e9, $14*1e9
+}
diff --git a/usr.sbin/xntpd/scripts/stats/etf.S b/usr.sbin/xntpd/scripts/stats/etf.S
new file mode 100644
index 0000000..9b9c68b
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/stats/etf.S
@@ -0,0 +1,15 @@
+options(digits=4)
+file2 <- "etf_summary"
+etf <- scan(file1, list(day=0, sec=0, offset=0, stab=0))
+r <- lsfit(etf$sec, etf$offset)
+count<-length(etf$sec)
+mean<-r$coef[[1]]
+std<-sqrt(var(r$residuals))
+slope<-r$coef[[2]] * 1000
+cat("\n", file=file2 , append=TRUE, fill=FALSE, sep="")
+cat(file1, "\n", file=file2, append=TRUE, fill=FALSE, sep="")
+cat("etf1 ", count, ", T ", mean, " ns, R ", slope, " ps/s, std ", std, " us\n", file=file2, append=TRUE, fill=FALSE, sep="")
+str <- paste("eps/", file1, ".eps", sep="")
+postscript(str, , , , 5, pointsize=18)
+par(mgp=c(1, 0, 0), tck=0.03, mar=c(2, 2, 1, 1))
+plot(etf$sec, etf$offset, type="l", xlab=paste("MJD", etf$day, "Time (s)"), ylab="External Offset (ns)", ylim=c(-400, 400))
diff --git a/usr.sbin/xntpd/scripts/stats/etf.awk b/usr.sbin/xntpd/scripts/stats/etf.awk
new file mode 100644
index 0000000..8e6e334
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/stats/etf.awk
@@ -0,0 +1,19 @@
+# program to produce external time/frequence statistics from clockstats files
+#
+# usage: awk -f etf.awk clockstats
+#
+# format of input record
+# 49165 40.473 127.127.10.1 93:178:00:00:39.238 ETF
+# +175.0 +176.8 2.0 +3.729E-11 +1.000E-10 +3.511E-11 4.005E-13 500
+#
+# format of output record (time values in nanoseconds)
+# MJD sec time freq
+# 49165 40.473 175.0 3.729e-11
+#
+# select ETF records with valid format
+{
+ if (NF >= 9 && $5 == "ETF") {
+ printf "%5s %9.3f %7.1f %10.3e\n", $1, $2, $6, $9
+ }
+}
+
diff --git a/usr.sbin/xntpd/scripts/stats/itf.S b/usr.sbin/xntpd/scripts/stats/itf.S
new file mode 100644
index 0000000..56c8c8d
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/stats/itf.S
@@ -0,0 +1,5 @@
+itf <- scan(file1, list(day=0, sec=0, offset=0, stab=0))
+str <- paste("eps/", file1, ".eps", sep="")
+postscript(str, , , , 5, pointsize=18)
+par(mgp=c(1, 0, 0), tck=0.03, mar=c(2, 2, 1, 1))
+plot(itf$sec, itf$offset, type="l", xlab=paste("MJD", itf$day, "Time (s)"), ylab="Internal Offset (ns)", ylim=c(-400, 400))
diff --git a/usr.sbin/xntpd/scripts/stats/itf.awk b/usr.sbin/xntpd/scripts/stats/itf.awk
new file mode 100644
index 0000000..2b21c5b
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/stats/itf.awk
@@ -0,0 +1,19 @@
+# program to produce intewrnal time/frequence statistics from clockstats files
+#
+# usage: awk -f itf.awk clockstats
+#
+# format of input record
+# 49227 67.846 127.127.10.1 93:240:00:00:51.816 ITF
+# COCO 0 +2.0579E-07 -3.1037E-08 -7.7723E-11 +6.5455E-10 500.00 4.962819
+#
+# format of output record (time values in nanoseconds)
+# MJD sec time freq
+# 49227 67.846 +2.0579E-07 -7.7723E-11
+#
+# select ITF records with valid format
+{
+ if (NF >= 10 && $5 == "ITF") {
+ printf "%5s %9.3f %7.1f %10.3e\n", $1, $2, $8 * 1e9, $10
+ }
+}
+
diff --git a/usr.sbin/xntpd/scripts/stats/loop.S b/usr.sbin/xntpd/scripts/stats/loop.S
new file mode 100644
index 0000000..8e564b6
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/stats/loop.S
@@ -0,0 +1,7 @@
+options(digits=4)
+loop <- scan(file1, list(day=0, sec=0, offset=0, freq=0, tc=0))
+loop$offset <- loop$offset * 1e6
+str <- paste("eps/", file1, ".eps", sep="")
+postscript(str, , , , 5, pointsize=18)
+par(mgp=c(1, 0, 0), tck=0.03, mar=c(2, 2, 1, 1))
+plot(loop$sec, loop$offset, type="l", xlab=paste("MJD", loop$day, "Time (s)"), ylab="PLL Offset (us)", ylim=c(-400, 400))
diff --git a/usr.sbin/xntpd/scripts/stats/loop.awk b/usr.sbin/xntpd/scripts/stats/loop.awk
new file mode 100644
index 0000000..470b27c
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/stats/loop.awk
@@ -0,0 +1,41 @@
+# awk program to scan loopstats files and report errors/statistics
+#
+# usage: awk -f loop.awk loopstats
+#
+# format of loopstats record
+# MJD sec time (s) freq (ppm) tc
+# 49235 3.943 0.000016 22.4716 0
+#
+BEGIN {
+ loop_tmax = loop_fmax = -1e9
+ loop_tmin = loop_fmin = 1e9
+}
+#
+# scan all records in file
+#
+{
+ if (NF >= 5) {
+ loop_count++
+ if ($3 > loop_tmax)
+ loop_tmax = $3
+ if ($3 < loop_tmin)
+ loop_tmin = $3
+ if ($4 > loop_fmax)
+ loop_fmax = $4
+ if ($4 < loop_fmin)
+ loop_fmin = $4
+ loop_time += $3
+ loop_time_rms += $3 * $3
+ loop_freq += $4
+ loop_freq_rms += $4 * $4
+ }
+} END {
+ if (loop_count > 0) {
+ loop_time /= loop_count
+ loop_time_rms = sqrt(loop_time_rms / loop_count - loop_time * loop_time)
+ loop_freq /= loop_count
+ loop_freq_rms = sqrt(loop_freq_rms / loop_count - loop_freq * loop_freq)
+ printf "loop %d, %.0f+/-%.1f, rms %.1f, freq %.2f+/-%0.3f, var %.3f\n", loop_count, (loop_tmax + loop_tmin) / 2 * 1e6, (loop_tmax - loop_tmin) / 2 * 1e6, loop_time_rms * 1e6, (loop_fmax + loop_fmin) / 2, (loop_fmax - loop_fmin) / 2, loop_freq_rms
+ }
+}
+
diff --git a/usr.sbin/xntpd/scripts/stats/loop.sh b/usr.sbin/xntpd/scripts/stats/loop.sh
new file mode 100755
index 0000000..619eeb8
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/stats/loop.sh
@@ -0,0 +1,13 @@
+#!/bin/csh
+#
+# Script to summarize loopstats files
+#
+set x = `ls loopstats.*`
+foreach dayfile ( $x )
+ if ($dayfile == $x[$#x]) continue
+ echo " "
+ echo $dayfile
+ awk -f loop.awk $dayfile
+ rm -f $dayfile
+end
+
diff --git a/usr.sbin/xntpd/scripts/stats/peer.awk b/usr.sbin/xntpd/scripts/stats/peer.awk
new file mode 100644
index 0000000..4cb48cd
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/stats/peer.awk
@@ -0,0 +1,57 @@
+# awk program to scan peerstats files and report errors/statistics
+#
+# usage: awk -f peer.awk peerstats
+#
+# format of peerstats record
+# MJD sec ident stat offset (s) delay (s) disp (s)
+# 49235 11.632 128.4.2.7 f414 -0.000041 0.21910 0.00084
+#
+BEGIN {
+ n = 0
+ MAXDISTANCE = 1.0
+}
+#
+# scan all records in file
+#
+{
+ if (NF >= 7 && ($7 + $6 / 2) < MAXDISTANCE) {
+ i = n
+ for (j = 0; j < n; j++) {
+ if ($3 == peer_ident[j])
+ i = j
+ }
+ if (i == n) {
+ peer_ident[i] = $3
+ peer_tmax[i] = peer_dist[i] = -1e9
+ peer_tmin[i] = 1e9
+ n++
+ }
+ peer_count[i]++
+ if ($5 > peer_tmax[i])
+ peer_tmax[i] = $5
+ if ($5 < peer_tmin[i])
+ peer_tmin[i] = $5
+ dist = $7 + $6 / 2
+ if (dist > peer_dist[i])
+ peer_dist[i] = dist
+ peer_time[i] += $5
+ peer_time_rms[i] += $5 * $5
+ peer_delay[i] += $6
+ peer_disp[i] += $7
+ }
+} END {
+ printf " ident cnt mean rms max delay dist disp\n"
+ printf "==========================================================================\n"
+ for (i = 0; i < n; i++) {
+ peer_time[i] /= peer_count[i]
+ peer_time_rms[i] = sqrt(peer_time_rms[i] / peer_count[i] - peer_time[i] * peer_time[i])
+ peer_delay[i] /= peer_count[i]
+ peer_disp[i] /= peer_count[i]
+ peer_tmax[i] = peer_tmax[i] - peer_time[i]
+ peer_tmin[i] = peer_time[i] - peer_tmin[i]
+ if (peer_tmin[i] > peer_tmax[i])
+ peer_tmax[i] = peer_tmin[i]
+ printf "%15s%5d%9.3f%9.3f%9.3f%9.3f%9.3f%9.3f\n", peer_ident[i], peer_count[i], peer_time[i] * 1e3, peer_time_rms[i] * 1e3, peer_tmax[i] * 1e3, peer_delay[i] * 1e3, peer_dist[i] * 1e3, peer_disp[i] * 1e3
+ }
+}
+
diff --git a/usr.sbin/xntpd/scripts/stats/peer.sh b/usr.sbin/xntpd/scripts/stats/peer.sh
new file mode 100755
index 0000000..b5d8d29
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/stats/peer.sh
@@ -0,0 +1,13 @@
+#!/bin/csh
+#
+# Script to summarize peerstats files
+#
+set x = `ls peerstats.*`
+foreach dayfile ( $x )
+ if ($dayfile == $x[$#x]) continue
+ echo " "
+ echo $dayfile
+ awk -f peer.awk $dayfile
+ rm -f $dayfile
+end
+
diff --git a/usr.sbin/xntpd/scripts/stats/psummary.awk b/usr.sbin/xntpd/scripts/stats/psummary.awk
new file mode 100644
index 0000000..5ef8d8e
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/stats/psummary.awk
@@ -0,0 +1,43 @@
+# program to scan peer_summary file and produce summary of daily summaries
+#
+# usage: awk -f psummary.awk peer_summary
+#
+{
+ if (NF < 8 || $1 == "ident")
+ continue
+ i = n
+ for (j = 0; j < n; j++) {
+ if ($1 == peer_ident[j])
+ i = j
+ }
+ if (i == n) {
+ peer_ident[i] = $1
+ n++
+ }
+ peer_count[i]++
+ if (($7 - $6 / 2) < 400) {
+ peer_count[i]++
+ peer_mean[i] += $3
+ peer_var[i] += $4 * $4
+ if ($5 > peer_max[i])
+ peer_max[i] = $5
+ if ($5 > 1)
+ peer_1[i]++
+ if ($5 > 5)
+ peer_2[i]++
+ if ($5 > 10)
+ peer_3[i]++
+ if ($5 > 50)
+ peer_4[i]++
+ }
+} END {
+ printf " host cnt mean rms max >1 >5 >10 >50\n"
+ printf "=================================================================\n"
+ for (i = 0; i < n; i++) {
+ if (peer_count[i] <= 0)
+ continue
+ peer_mean[i] /= peer_count[i]
+ peer_var[i] = sqrt(peer_var[i] / peer_count[i])
+ printf "%15s%4d%10.3f%10.3f%10.3f%4d%4d%4d%4d\n", peer_ident[i], peer_count[i], peer_mean[i], peer_var[i], peer_max[i], peer_1[i], peer_2[i], peer_3[i], peer_4[i]
+ }
+}
diff --git a/usr.sbin/xntpd/scripts/stats/rms.awk b/usr.sbin/xntpd/scripts/stats/rms.awk
new file mode 100644
index 0000000..34d612a
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/stats/rms.awk
@@ -0,0 +1,41 @@
+# program to scan peer_summary file
+#
+{
+ if (NF < 8 || $1 == "ident")
+ continue
+ i = n
+ for (j = 0; j < n; j++) {
+ if ($1 == peer_ident[j])
+ i = j
+ }
+ if (i == n) {
+ peer_ident[i] = $1
+ n++
+ }
+ peer_count[i]++
+ if (($7 - $6 / 2) < 400) {
+ peer_count[i]++
+ peer_mean[i] += $3
+ peer_var[i] += $4 * $4
+ if ($5 > peer_max[i])
+ peer_max[i] = $5
+ if ($5 > 1)
+ peer_1[i]++
+ if ($5 > 5)
+ peer_2[i]++
+ if ($5 > 10)
+ peer_3[i]++
+ if ($5 > 50)
+ peer_4[i]++
+ }
+} END {
+ printf " host cnt mean sd max >1 >5 >10 >50\n"
+ printf "=================================================================\n"
+ for (i = 0; i < n; i++) {
+ if (peer_count[i] <= 0)
+ continue
+ peer_mean[i] /= peer_count[i]
+ peer_var[i] = sqrt(peer_var[i] / peer_count[i])
+ printf "%15s%4d%10.3f%10.3f%10.3f%4d%4d%4d%4d\n", peer_ident[i], peer_count[i], peer_mean[i], peer_var[i], peer_max[i], peer_1[i], peer_2[i], peer_3[i], peer_4[i]
+ }
+}
diff --git a/usr.sbin/xntpd/scripts/stats/summary.sh b/usr.sbin/xntpd/scripts/stats/summary.sh
new file mode 100755
index 0000000..19b64a0
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/stats/summary.sh
@@ -0,0 +1,88 @@
+#!/bin/sh
+#
+# Script to summarize ipeerstats, loopstats and clockstats files
+#
+# This script can be run from a cron job once per day, week or month. It
+# runs the file-specific summary script and appends the summary data to
+# designated files.
+#
+DATE=`date +%Y%m%d`
+SIN=S.in
+SOUT=S.out
+LOOP=loop_summary
+PEER=peer_summary
+CLOCK=clock_summary
+
+rm -f $SIN $SOUT
+S=0
+if [ -f `which S | cut -f1 -d" "` ]; then
+ S=1
+fi
+#
+# Summarize loopstats files
+#
+for f in loopstats.????????; do
+ d=`echo $f | cut -f2 -d.`
+ if [ $DATE != $d ]; then
+ echo " " >>$LOOP
+ echo $f >>$LOOP
+ awk -f loop.awk $f >>$LOOP
+ if [ $S ]; then
+ echo "file1<-"\"${f}\" >>$SIN
+ echo "source("\""loop.S"\"")" >>$SIN
+ fi
+ rm -f $f
+ fi
+done
+
+#
+# Summarize peerstats files
+#
+for f in peerstats.????????; do
+ d=`echo $f | cut -f2 -d.`
+ if [ $DATE != $d ]; then
+ echo " " >>$PEER
+ echo $f >>$PEER
+ awk -f peer.awk $f >>$PEER
+ rm -f $f
+ fi
+done
+
+#
+# Summarize clockstats files
+#
+for f in clockstats.????????; do
+ d=`echo $f | cut -f2 -d.`
+ if [ $DATE != $d ]; then
+ echo " " >>$CLOCK
+ echo $f >>$CLOCK
+ awk -f clock.awk $f >>$CLOCK
+ if [ -f /dev/gps* ]; then
+ awk -f itf.awk $f >itf.$d
+ awk -f etf.awk $f >etf.$d
+ awk -f ensemble.awk $f >ensemble.$d
+ awk -f tdata.awk $f >tdata.$d
+ fi
+ rm -f $f
+ fi
+done
+
+#
+# Process clockstat files with S and generate PostScript plots
+#
+for f in itf etf ensemble tdata; do
+ for d in ${f}.????????; do
+ if [ -f $d ]; then
+ if [ $S ]; then
+ echo "file1<-"\"${d}\" >>$SIN
+ echo "source("\"${f}.S\"")" >>$SIN
+ echo "unix("\""rm ${d}"\"")" >>$SIN
+ else
+ rm -f $d
+ fi
+ fi
+ done
+done
+if [ -f $SIN ]; then
+ S BATCH $SIN $SOUT
+fi
diff --git a/usr.sbin/xntpd/scripts/stats/tdata.S b/usr.sbin/xntpd/scripts/stats/tdata.S
new file mode 100644
index 0000000..f360a24
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/stats/tdata.S
@@ -0,0 +1,5 @@
+tdata <- scan(file1, list(day=0, sec=0, m=0, w=0, x=0, y=0, z=0))
+str <- paste("eps/", file1, ".eps", sep="")
+postscript(str, , , , 5, pointsize=18)
+par(mgp=c(1, 0, 0), tck=0.03, mar=c(2, 2, 1, 1))
+plot(tdata$sec, tdata$m, type="l", xlab=paste("MJD", tdata$day, "Time (s)"), ylab="LORAN-M SNR (dB)")
diff --git a/usr.sbin/xntpd/scripts/stats/tdata.awk b/usr.sbin/xntpd/scripts/stats/tdata.awk
new file mode 100644
index 0000000..04d7e6a
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/stats/tdata.awk
@@ -0,0 +1,45 @@
+# program to produce loran tdata statistics from clockstats files
+#
+# usage: awk -f tdata.awk clockstats
+#
+# format of input record (missing replaced by -40.0)
+# 49228 36.852 127.127.10.1 93:241:00:00:20.812 LORAN TDATA
+# M OK 0 0 1169.14 -7.4 3.16E-07 .424
+# W CV 0 0 3329.30 -16.4 1.81E-06
+# X OK 0 0 1737.19 -10.5 3.44E-07 .358
+# Y OK 0 0 2182.07 -9.0 4.41E-07 .218
+#
+# format of output record (signal values are in dB)
+# MJD sec time M W X Y Z
+# 49228 36.852 175.0 -7.4 -16.4 -10.5 -9.0
+#
+# select LORAN TDATA records with valid format
+{
+ if (NF >= 7 && $6 == "TDATA") {
+ m = w = x = y = z = -40.0
+ for (i = 7; i < NF - 5; i++) {
+ if ($i == "M" && $(i+1) == "OK") {
+ i += 5
+ m = $i
+ }
+ else if ($i == "W" && $(i+1) == "OK") {
+ i += 5
+ w = $i
+ }
+ else if ($i == "X" && $(i+1) == "OK") {
+ i += 5
+ x = $i
+ }
+ else if ($i == "Y" && $(i+1) == "OK") {
+ i += 5
+ y = $i
+ }
+ else if ($i == "Z" && $(i+1) == "OK") {
+ i += 5
+ z = $i
+ }
+ }
+ printf "%5s %9.3f %6.1f %6.1f %6.1f %6.1f %6.1f\n", $1, $2, m, w, x, y, z
+ }
+}
+
diff --git a/usr.sbin/xntpd/scripts/support/README b/usr.sbin/xntpd/scripts/support/README
new file mode 100644
index 0000000..812965b
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/support/README
@@ -0,0 +1,73 @@
+The bin and etc directories contain several scripts (sh and perl) that
+should ease startup and configuration of NTP sites.
+
+ bin/monl is a monitoring script that prints out new, current and
+ old customers of an NTP timeserver when monitoring is
+ in effect.
+ monl has following options:
+ -i <regexp> (regular expression matchin IP addres to be ignored
+ -d <directory> where the current state is kept (default /tmp)
+ -v debug output
+ -n do not translate IP addresses into hostnames
+ <host> host to be analyzed
+
+ monl uses xntpdc for information gathering and is thus
+ limited to the NTP version xntpdc is compiled for.
+
+ bin/mvstats moves compresses and removes statistics files (useful mainly
+ for reference servers
+
+ etc/install creates the locally needed directories for NTP (if not residung in /etc)
+
+ etc/rc starts up daemon with configuration file and key file
+ etc/cron cron called monitor statistic (uses bin/monl)
+ etc/crontab crontab prototype for reference time servers
+ etc/setup sh script sourced by the other scripts for variable setup
+
+YOU MUST EDIT THESE FILES TO REFLECT YOUR LOCAL SETUP !
+
+READ THIS BEFORE USING THE STARTUP SCRIPTS
+
+The startupscript etc/rc has been written for Suns and HPs. They are not
+guaranteed to work elsewhere. Following assumptions have been made:
+
+ All NTP related files reside in ONE directory having following structure:
+
+ bin/* - all executables (daemon, control, date, scripts)
+ etc/* - startup scripts and cron scripts
+ conf/* - NTP configuration files
+
+The variable NTPROOT (etc/rc, etc/install) must be edited to reflect
+the NTP directory (e.g. /usr/local/NTP)
+
+NTP config files are located via Suns arch command and have the name
+conf/`arch`.`arch -k`.
+These are the default configurations (usually clients). If a file with the name
+conf/`arch`.`arch -k`.`hostname` is present this file will be preferred (Reference host,
+gateway). If the arch command is not available no-arch is used. The arch command
+is usually a shell script which echoes a string unique the the current machine
+architecture.
+
+The tickadj command has its own conf/tickconf file which is used to set host
+specific tickadj values. The line with DEFAULT specifies the default tickadj
+parameters, all other lines consists of <hostname> <hostid>
+<tickadj parameters>. These lines need only be entered if the specified host
+needs parameters different from the default parameters.
+
+Reference clock support is provided for DCF77. If you need to initialize
+certain things for reference clock support (e.g. loading STREAMS modules),
+you need to edit etc/rc.
+
+The current config files of Erlangen are included in the conf directory.
+They are just for reference, but might help you a bit in setting up a
+synchronisation network.
+
+The advantage of keeping all config files centralized is the easier
+administration.
+
+We replicate the NTP directory via NFS and rdist.
+
+When you have set up the local config files (YOUR OWN!) you can call
+<NTPROOT>/etc/rc for daemon startup.
+
+For more information: time@informatik.uni-erlangen.de
diff --git a/usr.sbin/xntpd/scripts/support/bin/monl b/usr.sbin/xntpd/scripts/support/bin/monl
new file mode 100755
index 0000000..f0c48db
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/support/bin/monl
@@ -0,0 +1,213 @@
+#!/local/bin/perl
+
+%service = ( 0, "unspec",
+ 1, "Active",
+ 2, "Passive",
+ 3, "Client",
+ 4, "Server",
+ 5, "Broadcast",
+ 6, "Control",
+ 7, "Private" );
+%nc = ();
+@ignpat = ();
+$noname = 0;
+$verbose = 0;
+$retries = 5;
+$lastkey = 0;
+
+sub timedelta {
+ local($tm, $days, $h, $m, $s);
+
+ $tm = @_[$[];
+ $days = 0;
+ $days = sprintf("%dd+", $days) if $days = int($tm / (60*60*24));
+ $days = "" unless $days;
+ $tm = $tm % (60*60*24);
+ $h = int($tm / (60*60));
+ $tm = $tm % (60*60);
+ $m = int($tm / 60);
+ $s = $tm % 60;
+
+ return sprintf("%s%02d:%02d:%02d", $days, $h, $m, $s);
+}
+
+sub listentry {
+ local($host, $mode) = split("$;" , @_[$[]);
+ local($count, $version, $firsttime) = split("$;" , $_[$[+1]);
+ local($name);
+
+ if (grep($host =~ m/$_/, @ignpat))
+ {
+ print "ignored $host ...\n" if $verbose;
+ return;
+ }
+
+ return if ! $count;
+
+ if (defined($nc{$host}))
+ {
+ $name = $nc{$host};
+ }
+ else
+ {
+ if ($noname)
+ {
+ $nc{$host} = $name = $host;
+ }
+ else
+ {
+ $name = (gethostbyaddr(pack("C4", split(/\./, $host)), 2))[$[];
+ $nc{$host} = $name = $host if ! defined($name);
+ }
+ }
+
+ printf ($fmt, ($lastkey eq $host) ? "" : $name, $service{$mode}, $count, $version, &timedelta($firsttime), $firsttime / $count);
+
+ if (@_[$[+2])
+ {
+ $hostcnt++ if $lastkey ne $host;
+ $packcnt += $count;
+ $maxtime = $firsttime if $firsttime > $maxtime;
+ }
+
+ $lastkey = $host;
+}
+
+while ($ARGV[$[] =~ /^-[nvid]$/)
+ {
+ if ($ARGV[$[] eq "-i")
+ {
+ shift;
+ push(@ignpat, shift) unless ! defined($ARGV[$[]);
+ }
+ elsif ($ARGV[$[] eq "-d")
+ {
+ shift;
+ $dir = shift unless ! defined($ARGV[$[]);
+ }
+ elsif ($ARGV[$[] eq "-n")
+ {
+ shift;
+ $noname = 1;
+ }
+ elsif ($ARGV[$[] eq "-v")
+ {
+ shift;
+ $verbose = 1;
+ }
+ }
+
+$dir = "/tmp" unless defined($dir);
+$gone = 60*60*48;
+$fmt = "%48s %10s %7d %7d %13s %14.3f\n";
+$sfmt = "%48s %10s %7s %7s %13s %14s\n";
+@lbl = ("Host", "Mode", "Count", "Version", "Time active", "Packetinterval");
+
+if (!defined($ARGV[$[]))
+ {
+ $hostname = `hostname`;
+ chop($hostname);
+ unshift(@ARGV, $hostname);
+ }
+
+foreach $hostname (@ARGV)
+ {
+ $dbmfile = $dir . "/monlstats-" . $hostname;
+ $monl = "xntpdc -c 'hostnames no' -c monl $hostname | tail +3 |";
+ $hostcnt = 0;
+ $packcnt = 0;
+ $maxtime = 0;
+ %Seen = ();
+ %New = ();
+ %Old = ();
+
+ print "Monitor Status of $hostname\n\n";
+
+ $cnt = $retries;
+ do
+ {
+ open(MONL, $monl) || die("$monl failed $!");
+ @monlout = <MONL>;
+ close(MONL);
+ } while (! @monlout && $cnt--);
+
+ if (! @monlout)
+ {
+ print "not available.\n";
+ next;
+ }
+
+ dbmopen(Clients, $dbmfile, 0644) || die("dbmopen(.., $dbmfile, ...): $!");
+
+ foreach (@monlout)
+ {
+ chop;
+ split;
+ ($host, $count, $mode, $version, $lasttime, $firsttime) =
+ (@_[$[, $[+2 .. $[+4, $#_-1,$#_]);
+
+ $Seen{$host, $mode} = 1;
+
+ if (!defined($Clients{$host, $mode}))
+ {
+ if ($lasttime <= $gone)
+ {
+ ## got a new one
+ $Clients{$host, $mode} = $New{$host, $mode} = join("$;", $count, $version, $firsttime, $lasttime);
+ }
+ }
+ else
+ {
+ ## throw out the old ones
+ if ($lasttime > $gone)
+ {
+ $Old{$host, $mode} = $Clients{$host, $mode};
+ delete $Clients{$host, $mode};
+ }
+ else
+ {
+ $Clients{$host, $mode} = join("$;", $count, $version, $firsttime, $lasttime);
+ }
+ }
+ }
+
+ grep(($Seen{$_} || ($Old{$_} = delete $Clients{$_})), keys(%Clients));
+
+ if (grep(($tmp = $_ , !grep($tmp =~ m/$_/, @ignpat)), keys(%New)))
+ {
+ print "New customers\n";
+ print "-------------\n";
+ printf $sfmt, @lbl;
+ grep( &listentry($_, $New{$_}, 1), sort(keys(%New)) );
+ print "\n";
+ }
+
+
+ if (grep((!defined($New{$_}) && ($tmp = $_, !grep($tmp =~ m/$_/, @ignpat))), keys(%Clients)))
+ {
+ print "Current customers\n";
+ print "-----------------\n";
+ printf $sfmt, @lbl;
+ grep( defined($New{$_}) || &listentry($_, $Clients{$_}, 1) , sort(keys(%Clients)) );
+ print "\n";
+ }
+
+ if (grep(($tmp = $_, !grep($tmp =~ m/$_/, @ignpat)), keys(%Old)))
+ {
+ print "Discarded customers\n";
+ print "-------------------\n";
+ printf $sfmt, @lbl;
+ grep( &listentry($_, $Old{$_}, 0) , sort(keys(%Old)) );
+ print "\n";
+ }
+
+ dbmclose(Clients);
+
+ print "\nSummary:\n";
+ print "--------\n";
+ printf("Elapsed time: %13s\n", &timedelta($maxtime));
+ printf(" Hosts: %13d\n", $hostcnt);
+ printf(" Packets: %13d\n", $packcnt);
+ printf(" Rate: %13.2f\n", $packcnt / $maxtime) if $maxtime;
+ print "\n";
+ }
diff --git a/usr.sbin/xntpd/scripts/support/bin/mvstats b/usr.sbin/xntpd/scripts/support/bin/mvstats
new file mode 100755
index 0000000..e33dc792
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/support/bin/mvstats
@@ -0,0 +1,23 @@
+#!/bin/sh
+#
+# mvstats,v 3.1 1993/07/06 01:10:24 jbj Exp
+#
+# mvstats is called by cron for keeping the log files together
+# usually only used on reference hosts
+#
+# Files reside in /var/NTP
+# Files older than 2 days will be compressed,
+# Files older than 64 days will be removed.
+#
+# mvstats,v
+# Revision 3.1 1993/07/06 01:10:24 jbj
+# XNTP release 3.1
+#
+#
+# Revision 1.1 1992/12/10 12:58:24 kardel
+# Prerelease NTP V3 / DCF
+#
+#
+cd /var/NTP
+find . ! -name '*.Z' -mtime +2 -exec compress -f {} \;
+find . -mtime +64 -exec rm -f {} \;
diff --git a/usr.sbin/xntpd/scripts/support/conf/hp300.hp300 b/usr.sbin/xntpd/scripts/support/conf/hp300.hp300
new file mode 100644
index 0000000..7b18758
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/support/conf/hp300.hp300
@@ -0,0 +1,70 @@
+#
+# FAU NTP client configuration file
+#
+# hp300.hp300,v 3.1 1993/07/06 01:10:27 jbj Exp
+#
+# hp300.hp300,v
+# Revision 3.1 1993/07/06 01:10:27 jbj
+# XNTP release 3.1
+#
+#
+# Revision 1.1 1992/12/10 12:58:29 kardel
+# Prerelease NTP V3 / DCF
+#
+# Revision 1.2 1992/09/24 06:10:46 kardel
+# authdelay adjust
+#
+# Revision 1.1 1992/09/24 06:09:23 kardel
+# Initial revision
+#
+# Revision 1.2 1992/01/14 14:01:35 kardel
+# update for joined INF4/INF1 nets
+#
+#
+# Local fall back clock
+#
+precision -7
+#
+# Local clock
+#
+peer 127.127.1.13
+#
+broadcastclient yes
+# broadcastdelay must be figured out
+
+#
+# peers - local synch setup
+#
+#server ntps1-0 version 3
+#server ntps1-1 version 2
+#server ntps2-0 version 3
+#
+# files
+#
+driftfile /+private/local/NTP/xntp.drift
+#
+# authentication stuff
+#
+authdelay 0.000436 # hp300
+controlkey 1006
+requestkey 1007
+#
+# restrictions
+#
+# provide cheap services to the world/ prevent modifications from there
+#
+restrict default notrust lowpriotrap nomodify
+#
+# hosts on the local networks are allowed unrestricted access
+#
+restrict 131.188.0.0 mask 255.255.0.0 nomodify notrust # allow local hosts
+restrict 131.188.1.0 mask 255.255.255.0 # allow local hosts
+restrict 131.188.31.0 mask 255.255.255.0 # allow local hosts
+restrict 131.188.34.0 mask 255.255.255.0 # allow local hosts
+restrict 131.188.44.0 mask 255.255.255.0 # allow local hosts
+restrict 131.188.40.0 mask 255.255.255.0 # allow local hosts
+restrict 131.188.60.0 mask 255.255.255.0 # allow local hosts
+restrict 131.188.54.0 mask 255.255.255.0 # allow local hosts
+#
+restrict 127.0.0.1 mask 255.255.255.255 # local config
+restrict 127.127.0.0 mask 255.255.0.0 # local clocks
diff --git a/usr.sbin/xntpd/scripts/support/conf/hp700.hp700 b/usr.sbin/xntpd/scripts/support/conf/hp700.hp700
new file mode 100644
index 0000000..911ff10
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/support/conf/hp700.hp700
@@ -0,0 +1,67 @@
+#
+# FAU NTP client configuration file
+#
+# hp700.hp700,v 3.1 1993/07/06 01:10:29 jbj Exp
+#
+# hp700.hp700,v
+# Revision 3.1 1993/07/06 01:10:29 jbj
+# XNTP release 3.1
+#
+#
+# Revision 1.1 1992/12/10 12:58:31 kardel
+# Prerelease NTP V3 / DCF
+#
+# Revision 1.1 1992/09/24 06:09:02 kardel
+# Initial revision
+#
+# Revision 1.2 1992/01/14 14:01:35 kardel
+# update for joined INF4/INF1 nets
+#
+#
+# Local fall back clock
+#
+precision -7
+#
+# Local clock
+#
+peer 127.127.1.13
+#
+broadcastclient yes
+# broadcastdelay must be figured out
+
+#
+# peers - local synch setup
+#
+#server ntps1-0 version 3
+#server ntps1-1 version 2
+#server ntps2-0 version 3
+#
+# files
+#
+driftfile /+private/local/NTP/xntp.drift
+#
+# authentication stuff
+#
+authdelay 0.000016 # hp700
+controlkey 1006
+requestkey 1007
+#
+# restrictions
+#
+# provide cheap services to the world/ prevent modifications from there
+#
+restrict default notrust lowpriotrap nomodify
+#
+# hosts on the local networks are allowed unrestricted access
+#
+restrict 131.188.0.0 mask 255.255.0.0 nomodify notrust # allow local hosts
+restrict 131.188.1.0 mask 255.255.255.0 # allow local hosts
+restrict 131.188.31.0 mask 255.255.255.0 # allow local hosts
+restrict 131.188.34.0 mask 255.255.255.0 # allow local hosts
+restrict 131.188.44.0 mask 255.255.255.0 # allow local hosts
+restrict 131.188.40.0 mask 255.255.255.0 # allow local hosts
+restrict 131.188.60.0 mask 255.255.255.0 # allow local hosts
+restrict 131.188.54.0 mask 255.255.255.0 # allow local hosts
+#
+restrict 127.0.0.1 mask 255.255.255.255 # local config
+restrict 127.127.0.0 mask 255.255.0.0 # local clocks
diff --git a/usr.sbin/xntpd/scripts/support/conf/hp700.hp700.faui47 b/usr.sbin/xntpd/scripts/support/conf/hp700.hp700.faui47
new file mode 100644
index 0000000..80c72a6
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/support/conf/hp700.hp700.faui47
@@ -0,0 +1,71 @@
+#
+# FAU NTP client configuration file
+#
+# hp700.hp700.faui47,v 3.1 1993/07/06 01:10:30 jbj Exp
+#
+# hp700.hp700.faui47,v
+# Revision 3.1 1993/07/06 01:10:30 jbj
+# XNTP release 3.1
+#
+#
+# Revision 1.1 1992/12/10 12:58:33 kardel
+# Prerelease NTP V3 / DCF
+#
+# Revision 1.1 1992/09/24 14:53:10 kirschni
+# Initial revision
+#
+# Revision 1.1 1992/09/24 06:09:02 kardel
+# Initial revision
+#
+# Revision 1.2 1992/01/14 14:01:35 kardel
+# update for joined INF4/INF1 nets
+#
+#
+# Local fall back clock
+#
+precision -7
+#
+# Local clock
+#
+peer 127.127.1.13
+#
+broadcastclient yes
+# broadcastdelay must be figured out
+broadcast 131.188.54.255
+
+#
+# peers - local synch setup
+#
+#server ntps1-0 version 3
+#server ntps1-1 version 2
+#server ntps2-0 version 3
+#
+# files
+#
+driftfile /+private/local/NTP/xntp.drift
+#
+# authentication stuff
+#
+authdelay 0.000016 # hp700
+controlkey 1006
+requestkey 1007
+#
+# restrictions
+#
+# provide cheap services to the world/ prevent modifications from there
+#
+restrict default notrust lowpriotrap nomodify
+#
+# hosts on the local networks are allowed unrestricted access
+#
+restrict 131.188.0.0 mask 255.255.0.0 nomodify notrust # allow local hosts
+restrict 131.188.1.0 mask 255.255.255.0 # allow local hosts
+restrict 131.188.31.0 mask 255.255.255.0 # allow local hosts
+restrict 131.188.34.0 mask 255.255.255.0 # allow local hosts
+restrict 131.188.44.0 mask 255.255.255.0 # allow local hosts
+restrict 131.188.40.0 mask 255.255.255.0 # allow local hosts
+restrict 131.188.60.0 mask 255.255.255.0 # allow local hosts
+restrict 131.188.54.0 mask 255.255.255.0 # allow local hosts
+#
+restrict 127.0.0.1 mask 255.255.255.255 # local config
+restrict 127.127.0.0 mask 255.255.0.0 # local clocks
diff --git a/usr.sbin/xntpd/scripts/support/conf/hp800.hp800 b/usr.sbin/xntpd/scripts/support/conf/hp800.hp800
new file mode 100644
index 0000000..58f4706
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/support/conf/hp800.hp800
@@ -0,0 +1,70 @@
+#
+# FAU NTP client configuration file
+#
+# hp800.hp800,v 3.1 1993/07/06 01:10:31 jbj Exp
+#
+# hp800.hp800,v
+# Revision 3.1 1993/07/06 01:10:31 jbj
+# XNTP release 3.1
+#
+#
+# Revision 1.1 1992/12/10 12:58:35 kardel
+# Prerelease NTP V3 / DCF
+#
+# Revision 1.2 1992/09/24 06:10:46 kardel
+# authdelay adjust
+#
+# Revision 1.1 1992/09/24 06:09:23 kardel
+# Initial revision
+#
+# Revision 1.2 1992/01/14 14:01:35 kardel
+# update for joined INF4/INF1 nets
+#
+#
+# Local fall back clock
+#
+precision -7
+#
+# Local clock
+#
+peer 127.127.1.13
+#
+broadcastclient yes
+# broadcastdelay must be figured out
+
+#
+# peers - local synch setup
+#
+#server ntps1-0 version 3
+#server ntps1-1 version 2
+#server ntps2-0 version 3
+#
+# files
+#
+driftfile /+private/local/NTP/xntp.drift
+#
+# authentication stuff
+#
+authdelay 0.000088 # hp800
+controlkey 1006
+requestkey 1007
+#
+# restrictions
+#
+# provide cheap services to the world/ prevent modifications from there
+#
+restrict default notrust lowpriotrap nomodify
+#
+# hosts on the local networks are allowed unrestricted access
+#
+restrict 131.188.0.0 mask 255.255.0.0 nomodify notrust # allow local hosts
+restrict 131.188.1.0 mask 255.255.255.0 # allow local hosts
+restrict 131.188.31.0 mask 255.255.255.0 # allow local hosts
+restrict 131.188.34.0 mask 255.255.255.0 # allow local hosts
+restrict 131.188.44.0 mask 255.255.255.0 # allow local hosts
+restrict 131.188.40.0 mask 255.255.255.0 # allow local hosts
+restrict 131.188.60.0 mask 255.255.255.0 # allow local hosts
+restrict 131.188.54.0 mask 255.255.255.0 # allow local hosts
+#
+restrict 127.0.0.1 mask 255.255.255.255 # local config
+restrict 127.127.0.0 mask 255.255.0.0 # local clocks
diff --git a/usr.sbin/xntpd/scripts/support/conf/ntp.conf b/usr.sbin/xntpd/scripts/support/conf/ntp.conf
new file mode 100644
index 0000000..06f5482
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/support/conf/ntp.conf
@@ -0,0 +1,36 @@
+#
+# peers - local synch setup
+#
+#server ntps1-0 key 0 version 2
+#server ntps1-1 key 0 version 2
+#server ntps2-0 key 0 version 2
+#server ntps2-1 key 0 version 2
+broadcastclient yes
+#broadcastdelay # use default, until we measure something
+#
+# files
+#
+driftfile /+private/local/NTP/xntp.drift
+resolver /local/NTP/bin/xntpres
+#
+# authentication stuff
+#
+authdelay 0.000629
+requestkey 65634
+controlkey 65635
+#
+# restrictions
+#
+# provide cheap services to the world/ prevent modifications from there
+restrict default notrust lowpriotrap nopeer nomodify
+#
+# hosts on the local networks are allowed unrestricted access
+#
+restrict 131.188.1.0 mask 255.255.255.0 # allow local hosts
+restrict 131.188.34.0 mask 255.255.255.0 # allow local hosts
+restrict 131.188.44.0 mask 255.255.255.0 # allow local hosts
+#
+restrict 127.127.0.0 mask 255.255.0.0 # allow refclocks
+restrict 127.0.0.1 mask 255.255.255.255 # allow local config
+#
+restrict 131.188.0.0 mask 255.255.0.0 nomodify# allow local hosts
diff --git a/usr.sbin/xntpd/scripts/support/conf/ntp.keys b/usr.sbin/xntpd/scripts/support/conf/ntp.keys
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/support/conf/ntp.keys
diff --git a/usr.sbin/xntpd/scripts/support/conf/ntp.keys.dumb b/usr.sbin/xntpd/scripts/support/conf/ntp.keys.dumb
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/support/conf/ntp.keys.dumb
diff --git a/usr.sbin/xntpd/scripts/support/conf/sun3.sun3 b/usr.sbin/xntpd/scripts/support/conf/sun3.sun3
new file mode 100644
index 0000000..06f5482
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/support/conf/sun3.sun3
@@ -0,0 +1,36 @@
+#
+# peers - local synch setup
+#
+#server ntps1-0 key 0 version 2
+#server ntps1-1 key 0 version 2
+#server ntps2-0 key 0 version 2
+#server ntps2-1 key 0 version 2
+broadcastclient yes
+#broadcastdelay # use default, until we measure something
+#
+# files
+#
+driftfile /+private/local/NTP/xntp.drift
+resolver /local/NTP/bin/xntpres
+#
+# authentication stuff
+#
+authdelay 0.000629
+requestkey 65634
+controlkey 65635
+#
+# restrictions
+#
+# provide cheap services to the world/ prevent modifications from there
+restrict default notrust lowpriotrap nopeer nomodify
+#
+# hosts on the local networks are allowed unrestricted access
+#
+restrict 131.188.1.0 mask 255.255.255.0 # allow local hosts
+restrict 131.188.34.0 mask 255.255.255.0 # allow local hosts
+restrict 131.188.44.0 mask 255.255.255.0 # allow local hosts
+#
+restrict 127.127.0.0 mask 255.255.0.0 # allow refclocks
+restrict 127.0.0.1 mask 255.255.255.255 # allow local config
+#
+restrict 131.188.0.0 mask 255.255.0.0 nomodify# allow local hosts
diff --git a/usr.sbin/xntpd/scripts/support/conf/sun4.sun4.faui01 b/usr.sbin/xntpd/scripts/support/conf/sun4.sun4.faui01
new file mode 100644
index 0000000..8927535
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/support/conf/sun4.sun4.faui01
@@ -0,0 +1,83 @@
+#
+# NTP v3 configuration file for faui01
+#
+# sun4.sun4.faui01,v 3.1 1993/07/06 01:10:37 jbj Exp
+#
+# sun4.sun4.faui01,v
+# Revision 3.1 1993/07/06 01:10:37 jbj
+# XNTP release 3.1
+#
+#
+# Revision 1.1 1992/12/10 12:58:44 kardel
+# Prerelease NTP V3 / DCF
+#
+# Revision 1.3 1992/10/15 10:56:01 kardel
+# -60 has 0 broadcasts now
+#
+# Revision 1.2 1992/09/17 12:46:53 kardel
+# CIP network broadcasts
+#
+# Revision 1.1 1992/06/09 13:40:44 kardel
+# Initial revision
+#
+#
+
+#
+# Local clock definitions
+#
+precision -14 # kernel fix - HIREZ timer
+
+#
+# Local clock
+#
+peer 127.127.1.6 # Fall back stratum 6
+
+#
+# get time from local network - hope this is reasonably stable
+#
+broadcastclient yes
+
+#
+# files / programs
+#
+driftfile /+private/local/NTP/xntp.drift
+
+#
+# authentication stuff
+#
+authdelay 0.000076
+requestkey 1007
+controlkey 1006
+
+#
+# service
+#
+broadcast 131.188.54.255 key 0 version 3
+broadcast 131.188.60.0 key 0 version 3
+broadcast 131.188.61.0 version 3 # inf1-net.revue (still on 2)
+broadcast 131.188.62.0 version 3 # inf4-net1.revue (still on 2)
+
+#
+# Statistics
+#
+monitor yes
+#statfile /var/NTP/statistics
+
+#
+# restrictions
+#
+# provide cheap services to the world/ prevent modifications from there
+restrict default notrust lowpriotrap nomodify
+
+#
+# hosts on the local networks are allowed unrestricted access
+#
+restrict 131.188.0.0 mask 255.255.0.0 nomodify notrust # allow local hosts
+restrict 131.188.1.0 mask 255.255.255.0 notrust # allow local hosts
+#
+restrict 127.127.0.0 mask 255.255.0.0 # local clocks
+restrict 127.0.0.1 # localhost does it too
+#
+restrict 131.188.1.45 mask 255.255.255.255 # ntps1-0
+restrict 131.188.34.45 mask 255.255.255.255 # ntps1-0
+restrict 131.188.44.45 mask 255.255.255.255 # ntps1-0
diff --git a/usr.sbin/xntpd/scripts/support/conf/sun4.sun4.faui10 b/usr.sbin/xntpd/scripts/support/conf/sun4.sun4.faui10
new file mode 100644
index 0000000..3be93a9
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/support/conf/sun4.sun4.faui10
@@ -0,0 +1,176 @@
+#
+# NTP v3 configuration file for faui45
+#
+# sun4.sun4.faui10,v 3.1 1993/07/06 01:10:38 jbj Exp
+#
+# sun4.sun4.faui10,v
+# Revision 3.1 1993/07/06 01:10:38 jbj
+# XNTP release 3.1
+#
+#
+# Revision 1.2 1993/01/19 09:32:31 kardel
+# Release 1993/01/19 DCF77/PPS
+#
+# Revision 1.1 1992/12/10 12:58:46 kardel
+# Prerelease NTP V3 / DCF
+#
+# Revision 1.11 1992/10/28 07:38:09 kardel
+# bear.zoo.bt.co.uk now also peer
+#
+# Revision 1.10 1992/09/17 12:56:22 kardel
+# 61 and 62 have ZEROBROADCASTS
+#
+# Revision 1.9 1992/09/17 12:46:53 kardel
+# CIP network broadcasts
+#
+# Revision 1.8 1992/08/14 21:51:04 kardel
+# local clock is now preferred peer
+#
+# Revision 1.7 1992/07/19 14:19:26 kardel
+# fixed broadcasts
+#
+# Revision 1.6 1992/07/17 17:12:43 kardel
+# new statistics support
+#
+# Revision 1.5 1992/07/10 07:46:03 kardel
+# added loopstats statistic file
+#
+# Revision 1.4 1992/06/26 07:30:32 kardel
+# update for reference clock support
+#
+# Revision 1.3 1992/05/18 13:51:04 kardel
+# precision fix
+#
+# Revision 1.2 1992/03/30 11:16:07 kardel
+# ntps1-1 version 3
+#
+# Revision 1.1 1992/01/14 12:30:21 kardel
+# Initial revision
+#
+#
+
+#
+# Local clock definitions
+#
+precision -18 # us resolution
+
+# DCF77 - 0 - REFERENCE CLOCK / Meinberg PZF 535/OCXO
+#
+# Supported clock types Base
+# Meinberg DCF PZF535 TCXO 0
+# Meinberg DCF PZF535 OCXO 16
+# Meinberg DCF U/A 31 32
+#
+# Option PPS support (CLOCKDEFS=-DDCF -DDCFPPS)
+# PPS 128
+#
+# The device to be used is added to the base (16 devices possible
+# /dev/dcf77-0 - /dev/dcf77-15)
+#
+# If PPS support is to be used 128 has to be added to the base
+# thus a DCF77 U/A 31 without PPS would be 127.127.8.32 (device 0 - /dev/dcf77-0)
+# a DCF77 PZF535/TCXO with PPS would be 127.127.8.129 (device 1 - /dev/dcf77-1)
+# a DCF77 PZF535/OCXO with PPS would be 127.127.8.146 (device 2 - /dev/dcf77-2)
+#
+peer 127.127.8.144 prefer # PZF 535/OCXO / PPS support
+#
+# We want to provide timed service too, thus (startup script magic)
+# TIMED
+
+#
+# Local clock
+#
+peer 127.127.1.6 # Fall back stratum 6
+
+#
+# peers - local synch setup
+#
+peer ntps1-0 key 0 version 3
+peer ntps2-0 key 0 version 3
+
+#
+# European servers
+#
+peer sunmanager.lrz-muenchen.de key 0 version 2
+peer rustime01.rus.uni-stuttgart.de version 2
+peer mailszrz.zrz.tu-berlin.de version 2
+
+#
+# UK servers
+#
+peer bear.zoo.bt.co.uk version 3
+
+# US Servers
+#
+server truechimer.cso.uiuc.edu version 2
+
+#
+# files / programs
+#
+driftfile /+private/local/NTP/xntp.drift
+resolver /local/NTP/bin/xntpres
+
+#
+# authentication stuff
+#
+authdelay 0.000076
+requestkey 1007
+controlkey 1006
+
+#
+# service
+#
+broadcast 131.188.31.0 version 3 # inf1-net.revue
+broadcast 131.188.34.0 version 3 # inf4-net1.revue
+broadcast 131.188.44.0 version 3 # inf4-net2.revue
+broadcast 131.188.1.255 version 3 # revue.revue
+broadcast 131.188.54.255 key 0 version 3
+broadcast 131.188.60.255 key 0 version 3
+broadcast 131.188.61.0 key 0 version 3
+broadcast 131.188.62.0 key 0 version 3
+
+#
+# Statistics
+#
+monitor yes
+
+#
+# file name prefix
+#
+statsdir /var/NTP/
+#
+# <stat type> file <file name> type <modifier> enable|disable|link|nolink
+filegen peerstats file peerstats type day link # generate <statsdir><file>.<YYYYMMDD> and link generic file name (without extension)
+filegen loopstats file loopstats type day link
+statistics peerstats loopstats # enable statistics
+
+#
+# restrictions
+#
+# provide cheap services to the world/ prevent modifications from there
+restrict default notrust lowpriotrap nomodify
+
+#
+# hosts on the local networks are allowed unrestricted access
+#
+restrict 131.188.0.0 mask 255.255.0.0 nomodify notrust # allow local hosts
+restrict 131.188.31.0 mask 255.255.255.0 notrust # allow local hosts
+restrict 131.188.34.0 mask 255.255.255.0 notrust # allow local hosts
+restrict 131.188.44.0 mask 255.255.255.0 notrust # allow local hosts
+restrict 131.188.40.0 mask 255.255.255.0 notrust # allow local hosts
+#
+restrict 127.127.0.0 mask 255.255.0.0 # local clocks
+restrict 127.0.0.1 # localhost does it too
+#
+restrict 131.188.1.45 mask 255.255.255.255 # ntps1-0
+restrict 131.188.34.45 mask 255.255.255.255 # ntps1-0
+restrict 131.188.44.45 mask 255.255.255.255 # ntps1-0
+restrict 131.188.30.1 mask 255.255.255.255 # ntps1-1
+#
+# external trust
+#
+restrict 130.126.174.40 mask 255.255.255.255 nomodify # truechimer.cso.uiuc.edu
+restrict 129.69.1.153 mask 255.255.255.255 nomodify # rustime01.rus.uni-stuttgart.de
+restrict 129.187.10.32 mask 255.255.255.255 nomodify # sunmanager.lrz-muenchen.de
+restrict 130.149.4.11 mask 255.255.255.255 nomodify # mailszrz.zrz.tu-berlin.de
+restrict 132.146.40.28 mask 255.255.255.255 nomodify # bear.zoo.bt.co.uk
diff --git a/usr.sbin/xntpd/scripts/support/conf/sun4.sun4.faui45 b/usr.sbin/xntpd/scripts/support/conf/sun4.sun4.faui45
new file mode 100644
index 0000000..57e77f2
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/support/conf/sun4.sun4.faui45
@@ -0,0 +1,228 @@
+#
+# NTP v3 configuration file for faui45
+#
+# sun4.sun4.faui45,v 3.1 1993/07/06 01:10:39 jbj Exp
+#
+# sun4.sun4.faui45,v
+# Revision 3.1 1993/07/06 01:10:39 jbj
+# XNTP release 3.1
+#
+#
+# Revision 1.2 1993/01/19 09:32:33 kardel
+# Release 1993/01/19 DCF77/PPS
+#
+# Revision 1.1 1992/12/10 12:58:48 kardel
+# Prerelease NTP V3 / DCF
+#
+# Revision 1.28 1992/10/28 07:38:09 kardel
+# bear.zoo.bt.co.uk now also peer
+#
+# Revision 1.27 1992/09/17 12:56:22 kardel
+# 61 and 62 have ZEROBROADCASTS
+#
+# Revision 1.26 1992/09/17 12:46:53 kardel
+# CIP network broadcasts
+#
+# Revision 1.25 1992/09/04 12:48:44 kardel
+# dcn1 -> churchy
+#
+# Revision 1.24 1992/08/14 21:42:20 kardel
+# local clock is now preferred peer
+#
+# Revision 1.23 1992/07/17 17:11:51 kardel
+# new statistics support
+#
+# Revision 1.22 1992/07/05 22:41:18 root
+# using default module settings now
+#
+# Revision 1.21 1992/07/02 11:47:26 root
+# loop statistics added
+#
+# Revision 1.20 1992/06/26 07:30:51 kardel
+# corrected examples
+#
+# Revision 1.19 1992/06/18 16:56:05 kardel
+# running timed too (startup script magic)
+#
+# Revision 1.18 1992/06/18 13:58:45 kardel
+# precision adjusted (us resolution)
+# clock definition explanation
+#
+# Revision 1.17 1992/06/13 12:49:35 root
+# allowed ntps1-1
+#
+# Revision 1.16 1992/06/07 11:44:41 kardel
+# switch to PPS support for dcf77-0
+#
+# Revision 1.15 1992/06/03 14:02:58 kardel
+# new version (fausup notrust)
+#
+# Revision 1.14 1992/05/18 13:49:45 kardel
+# first precision update due to kernel patch
+#
+# Revision 1.13 1992/05/18 13:48:36 kardel
+# more updates
+#
+# Revision 1.12 1992/03/24 08:43:49 kardel
+# now trusting netserv.rz.uni-karlsruhe.de
+#
+# Revision 1.11 1992/03/23 15:03:43 kardel
+# sunmanager.lrz-muenchen.de is a peer
+#
+# Revision 1.10 1992/03/12 22:49:53 kardel
+# well, got to switch fudge too
+#
+# Revision 1.9 1992/03/12 22:47:07 kardel
+# adjust for next xntpv3 alpha release
+#
+# Revision 1.8 1992/02/07 11:07:35 kardel
+# switched to Meinberg PZF 535/OCXO
+#
+# Revision 1.7 1992/01/21 15:11:38 kardel
+# netserv & sunmanager must be configured server (botch on other side)
+#
+# Revision 1.6 1992/01/17 17:54:34 kardel
+# added ntps2-0, ntps2-1 to unrestricted list
+#
+# Revision 1.5 1992/01/10 10:49:03 kardel
+# Authentication correction
+#
+# Revision 1.4 1992/01/10 08:08:06 kardel
+# peer apple.com added
+# ntps1-1 added to restrictionlist
+#
+# Revision 1.3 1991/12/19 10:23:56 kardel
+# peers on STRATUM 1
+# add mailszrz
+#
+# Revision 1.2 1991/12/19 09:57:29 kardel
+# upgrade NTP V3
+#
+#
+
+#
+# Local clock definitions
+#
+precision -18 # us resolution
+
+# DCF77 - 0 - REFERENCE CLOCK / Meinberg PZF 535/OCXO
+#
+# Supported clock types Base
+# Meinberg DCF PZF535 TCXO 0
+# Meinberg DCF PZF535 OCXO 16
+# Meinberg DCF U/A 31 32
+#
+# Option PPS support (CLOCKDEFS=-DDCF -DDCFPPS)
+# PPS 128
+#
+# The device to be used is added to the base (16 devices possible
+# /dev/dcf77-0 - /dev/dcf77-15)
+#
+# If PPS support is to be used 128 has to be added to the base
+# thus a DCF77 U/A 31 without PPS would be 127.127.8.32 (device 0 - /dev/dcf77-0)
+# a DCF77 PZF535/TCXO with PPS would be 127.127.8.129 (device 1 - /dev/dcf77-1)
+# a DCF77 PZF535/OCXO with PPS would be 127.127.8.146 (device 2 - /dev/dcf77-2)
+#
+peer 127.127.8.144 prefer # PZF 535/OCXO / PPS support
+#
+# We want to provide timed service too, thus (startup script magic)
+# TIMED
+
+#
+# Local clock
+#
+peer 127.127.1.6 # Fall back stratum 6
+
+#
+# peers - local synch setup
+#
+peer ntps1-1 key 0 version 2 # to be upgrade to version 3
+peer ntps2-0 key 0 version 2 # to be upgrade to version 3
+
+#
+# European servers
+#
+peer sunmanager.lrz-muenchen.de key 0 version 2
+peer iis.ethz.ch version 3
+server netserv.rz.uni-karlsruhe.de version 2 # sorry configuration error on other side
+peer rustime01.rus.uni-stuttgart.de version 2
+peer mailszrz.zrz.tu-berlin.de version 2
+
+#
+# UK servers
+#
+peer bear.zoo.bt.co.uk version 3
+
+#
+# US Servers
+#
+peer apple.com version 2
+server churchy.udel.edu key 0 version 3
+
+#
+# files / programs
+#
+driftfile /+private/local/NTP/xntp.drift
+resolver /local/NTP/bin/xntpres
+
+#
+# authentication stuff
+#
+authdelay 0.000076
+requestkey 1007
+controlkey 1006
+
+#
+# service
+#
+broadcast 131.188.1.255 key 0 version 2 # revue.revue (still on 2)
+broadcast 131.188.34.0 key 0 version 2 # inf4-net1.revue (still on 2)
+broadcast 131.188.44.0 key 0 version 2 # inf4-net2.revue (still on 2)
+broadcast 131.188.54.255 key 0 version 3
+broadcast 131.188.60.255 key 0 version 3
+broadcast 131.188.61.0 key 0 version 3
+broadcast 131.188.62.0 key 0 version 3
+
+#
+# Statistics
+#
+monitor yes
+#
+# file name prefix
+#
+statsdir /var/NTP/
+#
+# <stat type> file <file name> type <modifier> enable|disable|link|nolink
+filegen peerstats file peerstats type day link # generate <statsdir><file>.<YYYYMMDD> and link generic file name (without extension)
+filegen loopstats file loopstats type day link
+statistics peerstats loopstats # enable statistics
+
+#
+# restrictions
+#
+# provide cheap services to the world/ prevent modifications from there
+restrict default notrust lowpriotrap nomodify
+
+#
+# hosts on the local networks are allowed unrestricted access
+#
+restrict 131.188.0.0 mask 255.255.0.0 nomodify notrust # allow local hosts
+restrict 131.188.34.0 mask 255.255.255.0 notrust # allow local hosts
+restrict 131.188.44.0 mask 255.255.255.0 notrust # allow local hosts
+#
+restrict 127.127.0.0 mask 255.255.0.0 # local clocks
+restrict 127.0.0.1 # localhost does it too
+#
+restrict 131.188.1.41 mask 255.255.255.255 # ntps1-1
+restrict 131.188.31.1 mask 255.255.255.255 # ntps2-0, ntps2-1
+#
+# external trust
+#
+restrict 130.43.2.2 mask 255.255.255.255 nomodify # apple.com
+restrict 129.132.2.60 mask 255.255.255.255 nomodify # iis.ethz.ch
+restrict 128.4.1.5 mask 255.255.255.255 nomodify # churchy.udel.edu
+restrict 129.13.64.5 mask 255.255.255.255 nomodify # netserv.rz.uni-karlsruhe.de
+restrict 129.69.1.153 mask 255.255.255.255 nomodify # rustime01.rus.uni-stuttgart.de
+restrict 129.187.10.32 mask 255.255.255.255 nomodify # sunmanager.lrz-muenchen.de
+restrict 132.146.40.28 mask 255.255.255.255 nomodify # bear.zoo.bt.co.uk
+restrict 130.149.4.11 mask 255.255.255.255 nomodify # mailszrz.zrz.tu-berlin.de
diff --git a/usr.sbin/xntpd/scripts/support/conf/sun4.sun4c b/usr.sbin/xntpd/scripts/support/conf/sun4.sun4c
new file mode 100644
index 0000000..e1ff902
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/support/conf/sun4.sun4c
@@ -0,0 +1,63 @@
+#
+# FAU NTP client configuration file
+#
+# sun4.sun4c,v 3.1 1993/07/06 01:10:41 jbj Exp
+#
+# sun4.sun4c,v
+# Revision 3.1 1993/07/06 01:10:41 jbj
+# XNTP release 3.1
+#
+#
+# Revision 1.1 1992/12/10 12:58:50 kardel
+# Prerelease NTP V3 / DCF
+#
+# Revision 1.2 1992/01/14 14:01:35 kardel
+# update for joined INF4/INF1 nets
+#
+#
+# Local fall back clock
+#
+precision -7
+#
+# Local clock
+#
+peer 127.127.1.13
+#
+broadcastclient yes
+# broadcastdelay must be figured out
+
+#
+# peers - local synch setup
+#
+#server ntps1-0 version 3
+#server ntps1-1 version 2
+#server ntps2-0 version 3
+#
+# files
+#
+driftfile /+private/local/NTP/xntp.drift
+resolver /local/NTP/bin/xntpres
+#
+# authentication stuff
+#
+authdelay 0.000144 # sun4c
+controlkey 1006
+requestkey 1007
+#
+# restrictions
+#
+# provide cheap services to the world/ prevent modifications from there
+#
+restrict default notrust lowpriotrap nomodify
+#
+# hosts on the local networks are allowed unrestricted access
+#
+restrict 131.188.0.0 mask 255.255.0.0 nomodify notrust # allow local hosts
+restrict 131.188.1.0 mask 255.255.255.0 # allow local hosts
+restrict 131.188.31.0 mask 255.255.255.0 # allow local hosts
+restrict 131.188.34.0 mask 255.255.255.0 # allow local hosts
+restrict 131.188.44.0 mask 255.255.255.0 # allow local hosts
+restrict 131.188.40.0 mask 255.255.255.0 # allow local hosts
+#
+restrict 127.0.0.1 mask 255.255.255.255 # local config
+restrict 127.127.0.0 mask 255.255.0.0 # local clocks
diff --git a/usr.sbin/xntpd/scripts/support/conf/sun4.sun4c.Lucifer b/usr.sbin/xntpd/scripts/support/conf/sun4.sun4c.Lucifer
new file mode 100644
index 0000000..78d3ea8
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/support/conf/sun4.sun4c.Lucifer
@@ -0,0 +1,174 @@
+#
+# NTP v3 configuration file for Lucifer
+#
+# sun4.sun4c.Lucifer,v 3.1 1993/07/06 01:10:42 jbj Exp
+#
+# sun4.sun4c.Lucifer,v
+# Revision 3.1 1993/07/06 01:10:42 jbj
+# XNTP release 3.1
+#
+#
+# Revision 1.2 1993/01/19 09:32:35 kardel
+# Release 1993/01/19 DCF77/PPS
+#
+# Revision 1.1 1992/12/10 12:58:52 kardel
+# Prerelease NTP V3 / DCF
+#
+# Revision 1.8 1992/10/28 07:38:09 kardel
+# bear.zoo.bt.co.uk now also peer
+#
+# Revision 1.7 1992/09/17 12:56:22 kardel
+# 61 and 62 have ZEROBROADCASTS
+#
+# Revision 1.6 1992/09/17 12:46:53 kardel
+# CIP network broadcasts
+#
+# Revision 1.5 1992/08/14 21:52:02 kardel
+# local clock is now preferred peer
+#
+# Revision 1.4 1992/07/17 17:15:06 kardel
+# adedd new statistics support
+#
+# Revision 1.3 1992/07/12 16:50:16 kardel
+# new peers, restrictions, statistics, no timed
+#
+# Revision 1.2 1992/07/10 07:01:44 kardel
+# authdelay fixed
+#
+# Revision 1.1 1992/07/10 07:00:30 kardel
+# Initial revision
+#
+#
+#
+# Local clock definitions
+#
+precision -18 # us resolution
+
+# DCF77 - 0 - REFERENCE CLOCK / Meinberg PZF 535/OCXO
+#
+# Supported clock types Base
+# Meinberg DCF PZF535 TCXO 0
+# Meinberg DCF PZF535 OCXO 16
+# Meinberg DCF U/A 31 32
+# ELV DCF7000 48
+
+#
+# Option PPS support (CLOCKDEFS=-DDCF -DDCFPPS)
+# PPS 128
+#
+# The device to be used is added to the base (16 devices possible
+# /dev/dcf77-0 - /dev/dcf77-15)
+#
+# If PPS support is to be used 128 has to be added to the base
+# thus a DCF77 U/A 31 without PPS would be 127.127.8.32 (device 0 - /dev/dcf77-0)
+# a DCF77 PZF535/TCXO with PPS would be 127.127.8.129 (device 1 - /dev/dcf77-1)
+# a DCF77 PZF535/OCXO with PPS would be 127.127.8.146 (device 2 - /dev/dcf77-2)
+#
+peer 127.127.8.144 prefer # PZF 535/OCXO / PPS support
+#
+
+#
+# Local clock
+#
+peer 127.127.1.6 # Fall back stratum 6
+
+#
+# peers - local synch setup
+#
+peer ntps1-1 key 0 version 3
+peer ntps1-2 key 0 version 3
+peer ntps2-0 key 0 version 3
+
+#
+# UK servers
+#
+peer bear.zoo.bt.co.uk version 3
+
+#
+# European servers
+#
+peer sunmanager.lrz-muenchen.de key 0 version 2
+peer iis.ethz.ch version 3
+server netserv.rz.uni-karlsruhe.de version 2 # sorry configuration error on other side
+peer rustime01.rus.uni-stuttgart.de version 2
+peer mailszrz.zrz.tu-berlin.de version 2
+
+#
+# US Servers
+#
+peer apple.com version 2
+server dcn1.udel.edu key 0 version 3
+
+#
+# files / programs
+#
+driftfile /+private/local/NTP/xntp.drift
+resolver /local/NTP/bin/xntpres
+
+#
+# authentication stuff
+#
+authdelay 0.000144 # sun4c
+requestkey 1007
+controlkey 1006
+
+#
+# service
+#
+broadcast 131.188.1.255 key 0 version 3 # revue.revue (still on 2)
+broadcast 131.188.34.0 key 0 version 3 # inf4-net1.revue (still on 2)
+broadcast 131.188.44.0 key 0 version 3 # inf4-net2.revue (still on 2)
+broadcast 131.188.54.255 key 0 version 3
+broadcast 131.188.60.255 key 0 version 3
+broadcast 131.188.61.0 key 0 version 3
+broadcast 131.188.62.0 key 0 version 3
+
+#
+# Statistics
+#
+monitor yes
+
+#
+# file name prefix
+#
+statsdir /var/NTP/
+#
+# <stat type> file <file name> type <modifier> enable|disable|link|nolink
+filegen peerstats file peerstats type day link # generate <statsdir><file>.<YYYYMMDD> and link generic file name (without extension)
+filegen loopstats file loopstats type day link
+statistics peerstats loopstats # enable statistics
+
+#
+# restrictions
+#
+# provide cheap services to the world/ prevent modifications from there
+restrict default notrust lowpriotrap nomodify
+
+#
+# hosts on the local networks are allowed unrestricted access
+#
+restrict 131.188.0.0 mask 255.255.0.0 nomodify notrust # allow local hosts
+restrict 131.188.34.0 mask 255.255.255.0 notrust # allow local hosts
+restrict 131.188.44.0 mask 255.255.255.0 notrust # allow local hosts
+#
+restrict 127.127.0.0 mask 255.255.0.0 # local clocks
+restrict 127.0.0.1 # localhost does it too
+#
+restrict 131.188.1.45 mask 255.255.255.255 # ntps1-1
+restrict 131.188.34.45 mask 255.255.255.255 # ntps1-1
+restrict 131.188.44.45 mask 255.255.255.255 # ntps1-1
+restrict 131.188.1.31 mask 255.255.255.255 # ntps1-2, ntps2-0, ntps2-1
+restrict 131.188.34.31 mask 255.255.255.255 # ntps1-2, ntps2-0, ntps2-1
+restrict 131.188.44.31 mask 255.255.255.255 # ntps1-2, ntps2-0, ntps2-1
+restrict 131.188.31.1 mask 255.255.255.255 # ntps1-2, ntps2-0, ntps2-1
+#
+# external trust
+#
+restrict 130.43.2.2 mask 255.255.255.255 nomodify # apple.com
+restrict 129.132.2.60 mask 255.255.255.255 nomodify # iis.ethz.ch
+restrict 128.4.0.1 mask 255.255.255.255 nomodify # dcn1.umd.edu
+restrict 129.13.64.5 mask 255.255.255.255 nomodify # netserv.rz.uni-karlsruhe.de
+restrict 129.69.1.153 mask 255.255.255.255 nomodify # rustime01.rus.uni-stuttgart.de
+restrict 129.187.10.32 mask 255.255.255.255 nomodify # sunmanager.lrz-muenchen.de
+restrict 132.146.40.28 mask 255.255.255.255 nomodify # bear.zoo.bt.co.uk
+restrict 130.149.4.11 mask 255.255.255.255 nomodify # mailszrz.zrz.tu-berlin.de
diff --git a/usr.sbin/xntpd/scripts/support/conf/sun4.sun4m b/usr.sbin/xntpd/scripts/support/conf/sun4.sun4m
new file mode 100644
index 0000000..cf1e283
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/support/conf/sun4.sun4m
@@ -0,0 +1,69 @@
+#
+# FAU NTP client configuration file
+#
+# sun4.sun4m,v 3.1 1993/07/06 01:10:43 jbj Exp
+#
+# sun4.sun4m,v
+# Revision 3.1 1993/07/06 01:10:43 jbj
+# XNTP release 3.1
+#
+#
+# Revision 1.1 1992/12/10 12:58:55 kardel
+# Prerelease NTP V3 / DCF
+#
+# Revision 1.2 1992/10/05 12:48:44 kardel
+# sun4m authdelay
+#
+# Revision 1.1 1992/10/05 12:48:07 kardel
+# Initial revision
+#
+# Revision 1.2 1992/01/14 14:01:35 kardel
+# update for joined INF4/INF1 nets
+#
+#
+# Local fall back clock
+#
+precision -7
+#
+# Local clock
+#
+peer 127.127.1.13
+#
+broadcastclient yes
+# broadcastdelay must be figured out
+
+#
+# peers - local synch setup
+#
+#server ntps1-0 version 3
+#server ntps1-1 version 2
+#server ntps2-0 version 3
+#
+# files
+#
+driftfile /+private/local/NTP/xntp.drift
+resolver /local/NTP/bin/xntpres
+#
+# authentication stuff
+#
+authdelay 0.000033 # sun4c
+controlkey 1006
+requestkey 1007
+#
+# restrictions
+#
+# provide cheap services to the world/ prevent modifications from there
+#
+restrict default notrust lowpriotrap nomodify
+#
+# hosts on the local networks are allowed unrestricted access
+#
+restrict 131.188.0.0 mask 255.255.0.0 nomodify notrust # allow local hosts
+restrict 131.188.1.0 mask 255.255.255.0 # allow local hosts
+restrict 131.188.31.0 mask 255.255.255.0 # allow local hosts
+restrict 131.188.34.0 mask 255.255.255.0 # allow local hosts
+restrict 131.188.44.0 mask 255.255.255.0 # allow local hosts
+restrict 131.188.40.0 mask 255.255.255.0 # allow local hosts
+#
+restrict 127.0.0.1 mask 255.255.255.255 # local config
+restrict 127.127.0.0 mask 255.255.0.0 # local clocks
diff --git a/usr.sbin/xntpd/scripts/support/conf/sun4.sun4m.faui42 b/usr.sbin/xntpd/scripts/support/conf/sun4.sun4m.faui42
new file mode 100644
index 0000000..acc919c
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/support/conf/sun4.sun4m.faui42
@@ -0,0 +1,152 @@
+#
+# NTP v3 configuration file for faui42
+#
+# sun4.sun4m.faui42,v 3.1 1993/07/06 01:10:44 jbj Exp
+#
+# sun4.sun4m.faui42,v
+# Revision 3.1 1993/07/06 01:10:44 jbj
+# XNTP release 3.1
+#
+#
+# Revision 1.2 1993/01/19 09:32:36 kardel
+# Release 1993/01/19 DCF77/PPS
+#
+# Revision 1.1 1992/12/10 12:58:57 kardel
+# Prerelease NTP V3 / DCF
+#
+# Revision 1.6 1992/09/15 16:19:10 kardel
+# preferrred peer
+#
+# Revision 1.5 1992/09/15 15:57:36 kardel
+# Stratum 1 again (may the Patches be with us...)
+#
+# Revision 1.4 1992/06/30 08:52:38 kardel
+# sun4m machine don't have a clock (SunOS4.1.2)
+# soory - just Stratum 2
+#
+# Revision 1.3 1992/06/18 13:58:45 kardel
+# precision adjusted (us resolution)
+# clock definition explanation
+#
+# Revision 1.2 1992/06/13 11:42:49 kardel
+# restrictions changed
+#
+# Revision 1.1 1992/06/13 11:27:11 kardel
+# Initial revision
+#
+#
+
+#
+# Local clock definitions
+#
+precision -18 # us resolution
+
+#
+# DCF77 - 0 - REFERENCE CLOCK / Meinberg PZF 535/OCXO
+#
+# Supported clock types Base
+# Meinberg DCF PZF535 TCXO 0
+# Meinberg DCF PZF535 OCXO 16
+# Meinberg DCF U/A 31 32
+#
+# Option PPS support (CLOCKDEFS=-DDCF -DDCFPPS)
+# PPS 128
+#
+# The device to be used is added to the base (16 devices possible
+# /dev/dcf77-0 - /dev/dcf77-15)
+#
+# If PPS support is to be used 128 has to be added to the base
+# thus a DCF77 U/A 31 without PPS would be 127.127.8.32 (device 0 - /dev/dcf77-0)
+# a DCF77 PZF535/TCXO with PPS would be 127.127.8.129 (device 1 - /dev/dcf77-1)
+# a DCF77 PZF535/OCXO with PPS would be 127.127.8.146 (device 2 - /dev/dcf77-2)
+#
+peer 127.127.8.144 prefer # PZF 535/OCXO / PPS support
+#
+# Local clock
+#
+peer 127.127.1.6 # Fall back stratum 6
+
+#
+# peers - local synch setup
+#
+peer ntps1-0 key 0 version 2 # to be upgrade to version 3
+peer ntps2-0 key 0 version 2 # to be upgrade to version 3
+
+#
+# European servers
+#
+peer sunmanager.lrz-muenchen.de key 0 version 2
+peer iis.ethz.ch version 3
+#server netserv.rz.uni-karlsruhe.de version 2 # sorry configuration error on other side
+peer rustime01.rus.uni-stuttgart.de version 2
+#peer mailszrz.zrz.tu-berlin.de version 2
+
+#
+# US Servers
+#
+#peer apple.com version 2
+#server dcn1.udel.edu key 0 version 3
+
+#
+# files / programs
+#
+driftfile /+private/local/NTP/xntp.drift
+resolver /local/NTP/bin/xntpres
+
+#
+# authentication stuff
+#
+authdelay 0.000047
+requestkey 1007
+controlkey 1006
+
+#
+# service
+#
+broadcast 131.188.1.255 key 0 version 3 # revue.revue (still on 2)
+broadcast 131.188.40.0 key 0 version 3 # inf4-net2.revue (still on 2)
+
+#
+# Statistics
+#
+monitor yes
+
+#
+# file name prefix
+#
+statsdir /var/NTP/
+#
+# <stat type> file <file name> type <modifier> enable|disable|link|nolink
+filegen peerstats file peerstats type day link # generate <statsdir><file>.<YYYYMMDD> and link generic file name (without extension)
+filegen loopstats file loopstats type day link
+statistics peerstats loopstats # enable statistics
+
+#
+# restrictions
+#
+# provide cheap services to the world/ prevent modifications from there
+restrict default notrust lowpriotrap nomodify
+
+#
+# hosts on the local networks are allowed unrestricted access
+#
+restrict 131.188.0.0 mask 255.255.0.0 nomodify notrust # allow local hosts
+restrict 131.188.40.0 mask 255.255.255.0 nomodify # allow local hosts
+restrict 131.188.34.0 mask 255.255.255.0 nomodify # allow local hosts
+restrict 131.188.44.0 mask 255.255.255.0 nomodify # allow local hosts
+restrict 131.188.31.0 mask 255.255.255.0 nomodify # allow local hosts
+restrict 131.188.1.0 mask 255.255.255.0 nomodify # allow local hosts
+#
+restrict 127.127.0.0 mask 255.255.0.0 # local clocks
+restrict 127.0.0.1 # localhost does it too
+#
+#
+# external trust
+#
+restrict 130.43.2.2 mask 255.255.255.255 nomodify # apple.com
+restrict 129.132.2.60 mask 255.255.255.255 nomodify # iis.ethz.ch
+restrict 128.4.0.1 mask 255.255.255.255 nomodify # dcn1.umd.edu
+restrict 129.13.64.5 mask 255.255.255.255 nomodify # netserv.rz.uni-karlsruhe.de
+restrict 129.69.1.153 mask 255.255.255.255 nomodify # rustime01.rus.uni-stuttgart.de
+restrict 129.187.10.32 mask 255.255.255.255 nomodify # sunmanager.lrz-muenchen.de
+restrict 130.149.4.11 mask 255.255.255.255 nomodify # mailszrz.zrz.tu-berlin.de
diff --git a/usr.sbin/xntpd/scripts/support/conf/sun4.sun4m.faui45m b/usr.sbin/xntpd/scripts/support/conf/sun4.sun4m.faui45m
new file mode 100644
index 0000000..2c75f67
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/support/conf/sun4.sun4m.faui45m
@@ -0,0 +1,165 @@
+#
+# NTP v3 configuration file for Lucifer
+#
+# sun4.sun4m.faui45m,v 3.1 1993/07/06 01:10:45 jbj Exp
+#
+# sun4.sun4m.faui45m,v
+# Revision 3.1 1993/07/06 01:10:45 jbj
+# XNTP release 3.1
+#
+#
+# Revision 1.2 1993/01/19 09:32:38 kardel
+# Release 1993/01/19 DCF77/PPS
+#
+# Revision 1.1 1992/12/10 12:58:59 kardel
+# Prerelease NTP V3 / DCF
+#
+# Revision 1.7 1992/09/17 12:56:22 kardel
+# 61 and 62 have ZEROBROADCASTS
+#
+# Revision 1.6 1992/09/17 12:46:53 kardel
+# CIP network broadcasts
+#
+# Revision 1.5 1992/08/14 21:52:02 kardel
+# local clock is now preferred peer
+#
+# Revision 1.4 1992/07/17 17:15:06 kardel
+# adedd new statistics support
+#
+# Revision 1.3 1992/07/12 16:50:16 kardel
+# new peers, restrictions, statistics, no timed
+#
+# Revision 1.2 1992/07/10 07:01:44 kardel
+# authdelay fixed
+#
+# Revision 1.1 1992/07/10 07:00:30 kardel
+# Initial revision
+#
+#
+#
+# Local clock definitions
+#
+precision -18 # us resolution
+
+# DCF77 - 0 - REFERENCE CLOCK / Meinberg PZF 535/OCXO
+#
+# Supported clock types Base
+# Meinberg DCF PZF535 TCXO 0
+# Meinberg DCF PZF535 OCXO 16
+# Meinberg DCF U/A 31 32
+# ELV DCF7000 48
+
+#
+# Option PPS support (CLOCKDEFS=-DDCF -DDCFPPS)
+# PPS 128
+#
+# The device to be used is added to the base (16 devices possible
+# /dev/dcf77-0 - /dev/dcf77-15)
+#
+# If PPS support is to be used 128 has to be added to the base
+# thus a DCF77 U/A 31 without PPS would be 127.127.8.32 (device 0 - /dev/dcf77-0)
+# a DCF77 PZF535/TCXO with PPS would be 127.127.8.129 (device 1 - /dev/dcf77-1)
+# a DCF77 PZF535/OCXO with PPS would be 127.127.8.146 (device 2 - /dev/dcf77-2)
+#
+peer 127.127.8.144 prefer # PZF 535/OCXO / PPS support
+#
+
+#
+# Local clock
+#
+peer 127.127.1.6 # Fall back stratum 6
+
+#
+# peers - local synch setup
+#
+peer ntps1-1 key 0 version 3
+peer ntps1-2 key 0 version 3
+peer ntps2-0 key 0 version 3
+
+#
+# European servers
+#
+peer sunmanager.lrz-muenchen.de key 0 version 2
+peer iis.ethz.ch version 3
+server netserv.rz.uni-karlsruhe.de version 2 # sorry configuration error on other side
+peer rustime01.rus.uni-stuttgart.de version 2
+peer mailszrz.zrz.tu-berlin.de version 2
+
+#
+# US Servers
+#
+peer apple.com version 2
+server dcn1.udel.edu key 0 version 3
+
+#
+# files / programs
+#
+driftfile /+private/local/NTP/xntp.drift
+resolver /local/NTP/bin/xntpres
+
+#
+# authentication stuff
+#
+authdelay 0.000033 # sun4m
+requestkey 1007
+controlkey 1006
+
+#
+# service
+#
+broadcast 131.188.1.255 key 0 version 3 # revue.revue (still on 2)
+broadcast 131.188.34.0 key 0 version 3 # inf4-net1.revue (still on 2)
+broadcast 131.188.44.0 key 0 version 3 # inf4-net2.revue (still on 2)
+broadcast 131.188.54.255 key 0 version 3
+broadcast 131.188.60.255 key 0 version 3
+broadcast 131.188.61.0 key 0 version 3
+broadcast 131.188.62.0 key 0 version 3
+
+#
+# Statistics
+#
+monitor yes
+
+#
+# file name prefix
+#
+statsdir /var/NTP/
+#
+# <stat type> file <file name> type <modifier> enable|disable|link|nolink
+filegen peerstats file peerstats type day link # generate <statsdir><file>.<YYYYMMDD> and link generic file name (without extension)
+filegen loopstats file loopstats type day link
+statistics peerstats loopstats # enable statistics
+
+#
+# restrictions
+#
+# provide cheap services to the world/ prevent modifications from there
+restrict default notrust lowpriotrap nomodify
+
+#
+# hosts on the local networks are allowed unrestricted access
+#
+restrict 131.188.0.0 mask 255.255.0.0 nomodify notrust # allow local hosts
+restrict 131.188.34.0 mask 255.255.255.0 notrust # allow local hosts
+restrict 131.188.44.0 mask 255.255.255.0 notrust # allow local hosts
+#
+restrict 127.127.0.0 mask 255.255.0.0 # local clocks
+restrict 127.0.0.1 # localhost does it too
+#
+restrict 131.188.1.45 mask 255.255.255.255 # ntps1-1
+restrict 131.188.34.45 mask 255.255.255.255 # ntps1-1
+restrict 131.188.44.45 mask 255.255.255.255 # ntps1-1
+restrict 131.188.1.31 mask 255.255.255.255 # ntps1-2, ntps2-0, ntps2-1
+restrict 131.188.34.31 mask 255.255.255.255 # ntps1-2, ntps2-0, ntps2-1
+restrict 131.188.44.31 mask 255.255.255.255 # ntps1-2, ntps2-0, ntps2-1
+restrict 131.188.31.1 mask 255.255.255.255 # ntps1-2, ntps2-0, ntps2-1
+#
+# external trust
+#
+restrict 130.43.2.2 mask 255.255.255.255 nomodify # apple.com
+restrict 129.132.2.60 mask 255.255.255.255 nomodify # iis.ethz.ch
+restrict 128.4.0.1 mask 255.255.255.255 nomodify # dcn1.umd.edu
+restrict 129.13.64.5 mask 255.255.255.255 nomodify # netserv.rz.uni-karlsruhe.de
+restrict 129.69.1.153 mask 255.255.255.255 nomodify # rustime01.rus.uni-stuttgart.de
+restrict 129.187.10.32 mask 255.255.255.255 nomodify # sunmanager.lrz-muenchen.de
+restrict 130.149.4.11 mask 255.255.255.255 nomodify # mailszrz.zrz.tu-berlin.de
diff --git a/usr.sbin/xntpd/scripts/support/conf/tickconf b/usr.sbin/xntpd/scripts/support/conf/tickconf
new file mode 100644
index 0000000..b17dbe8
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/support/conf/tickconf
@@ -0,0 +1,19 @@
+DEFAULT -A -p -s -q
+Lucifer 55406cfa -a 1 -p -s -q -t 10001
+faui45 24000f9b -a 1 -p -s -q
+faui10 2440213c -a 1 -p -s -q
+faui1b 54001418 -A -p -s -q -t 10001
+faui4p 5100344d -A -p -s -q -t 9999
+faui02g 1200be20 -A -p -s -q -t 9999
+faui02e 1200bbab -A -p -s -q -t 9999
+faui02f 1200bedb -A -p -s -q -t 9999
+faui03b 1200b92b -A -p -s -q -t 9999
+faui45m 726001ac -A -p -s -q -t 10001
+faui45o 72600272 -A -p -s -q -t 10001
+faui45p 7260028f -A -p -s -q -t 10001
+faui45r 72400cc7 -A -p -s -q -t 10001
+faui45s 726045be -A -p -s -q -t 10001
+faui45v 72604487 -A -p -s -q -t 10001
+faui45x 726044eb -A -p -s -q -t 10001
+faui45y 7260476d -A -p -s -q -t 10001
+faui45z 726045a1 -A -p -s -q -t 10001
diff --git a/usr.sbin/xntpd/scripts/support/etc/cron b/usr.sbin/xntpd/scripts/support/etc/cron
new file mode 100755
index 0000000..07ed189
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/support/etc/cron
@@ -0,0 +1,18 @@
+#!/bin/sh
+#
+# cron,v 3.1 1993/07/06 01:10:50 jbj Exp
+#
+# called by cron for statistics gathering
+#
+# cron,v
+# Revision 3.1 1993/07/06 01:10:50 jbj
+# XNTP release 3.1
+#
+#
+# Revision 1.1 1992/12/10 12:59:18 kardel
+# Prerelease NTP V3 / DCF
+#
+#
+PATH="${PATH}:/local/NTP/bin"
+export PATH
+monl -d /local/NTP/monitor -i '127\.0\.0\.1' faui10 faui45 lucifer rackety.udel.edu
diff --git a/usr.sbin/xntpd/scripts/support/etc/crontab b/usr.sbin/xntpd/scripts/support/etc/crontab
new file mode 100644
index 0000000..2b2d19c
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/support/etc/crontab
@@ -0,0 +1,8 @@
+#
+# NTP statistics periodic cleanup - REFERENCE SERVER ONLY
+#
+#55 23 * * * sh /local/NTP/etc/mvstats
+#
+# gather NTP client statistics - REFERENCE SERVER ONLY
+#
+0 8,18 * * * /local/NTP/etc/cron 2>/dev/null | /usr/ucb/mail -s "NTP statistics" time@informatik.uni-erlangen.de
diff --git a/usr.sbin/xntpd/scripts/support/etc/install b/usr.sbin/xntpd/scripts/support/etc/install
new file mode 100755
index 0000000..169a7e5
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/support/etc/install
@@ -0,0 +1,67 @@
+#!/bin/sh
+#
+# install,v 3.1 1993/07/06 01:10:53 jbj Exp
+#
+# install,v
+# Revision 3.1 1993/07/06 01:10:53 jbj
+# XNTP release 3.1
+#
+#
+# Revision 1.1 1992/12/10 12:59:21 kardel
+# Prerelease NTP V3 / DCF
+#
+# Revision 1.1 1992/06/18 14:50:08 kardel
+# Initial revision
+#
+#
+NTPROOT=/local/NTP # SITE SPECIFIC: where NTP resides
+#
+# where the local NTP state files reside (xntp.drift) ussualle /etc
+# this directory must not be shared as machine dependent data ist stored there
+#
+NTPDIR="/+private/local/NTP"
+#
+# get the initial setup
+#
+if [ ! -r $NTPROOT/etc/setup ]; then
+ echo "ERROR: $NTPROOT/etc/setup missing - incorrect installation."
+ exit 1
+else
+ . $NTPROOT/etc/setup
+fi
+
+umask 022 # SITE SPECIFIC: local policy - watch out for NFS and "root" rights
+
+Mkdir() {
+ p=""
+ IFS="/"
+ set -- $@
+ IFS='
+'
+ for pnc do
+ if [ ! -d "$p/$pnc" ]; then
+ ECHO -n "creating directory $p/$pnc"
+ if mkdir "$p/$pnc"; then
+ ECHO ""
+ else
+ ECHO " - FAILED"
+ break;
+ fi
+ fi
+ p="$p/$pnc"
+ done
+}
+
+if [ ! -d "$NTPDIR" ]; then
+ ECHO "installing NTP private data area ($NTPDIR)"
+ if Mkdir "$NTPDIR"; then
+ chmod 755 "$NTPDIR"
+ ECHO "$NTPDIR created."
+ fi
+else
+ ECHO "NTP already installed."
+ if [ -f "$NTPDIR/xntp.drift" ]; then
+ ECHO "currently saved drift value:" `cat "$NTPDIR/xntp.drift"`
+ fi
+fi
+
diff --git a/usr.sbin/xntpd/scripts/support/etc/rc b/usr.sbin/xntpd/scripts/support/etc/rc
new file mode 100755
index 0000000..ef8834a
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/support/etc/rc
@@ -0,0 +1,198 @@
+#!/bin/sh
+# NTP time synchronisation
+#
+# /src/NTP/REPOSITORY/v3/supportscripts/etc/rc,v 1.11 1993/07/09 13:17:00 kardel Exp
+#
+# rc,v
+# Revision 1.11 1993/07/09 13:17:00 kardel
+# local NTPROOT
+#
+# Revision 1.10 1993/07/09 11:37:29 kardel
+# Initial restructured version + GPS support
+#
+# Revision 1.9 1993/06/23 14:10:36 kardel
+# June 21st reconcilation
+#
+# Revision 1.7 1993/06/02 12:04:43 kardel
+# May 28th reconcilation & clenaup
+#
+#
+# non reference clock hosts will try to do an ntpdate on NTPSERVERS
+#
+NTPSERVERS="ntps1-0 ntps1-1 ntps2-0 ntps2-1"
+NTPROOT=/local/NTP
+
+#
+# get the initial setup
+#
+if [ ! -r $NTPROOT/etc/setup ]; then
+ echo "ERROR: $NTPROOT/etc/setup missing - incorrect installation."
+ exit 1
+else
+ . $NTPROOT/etc/setup
+fi
+
+umask 022 # SITE SPECIFIC: local policy - watch out for NFS and "root" rights
+
+msg=""
+#
+# default configuration files are named $NTPROOT/conf/<ARCH>.<KARCH>
+#
+CF=$NTPROOT/conf/$ARCH.$KARCH # default configuration file
+#
+# Host specific config file (reference clocks) have the hostname tagged on
+#
+CFH="$CF"."$HOSTNAME" # specific configuration file
+#
+# where to find the tickadj command
+#
+KFIX=$NTPROOT/bin/tickadj # kernel variable fix
+#
+# where to find special tickadj parameters
+#
+TC=$NTPROOT/conf/tickconf # special tickadj parameters
+#
+# where to find the keys file (if not found $KEY.dumb will be used)
+#
+KEY=$NTPROOT/conf/ntp.keys # private key file
+#
+# the daemon
+#
+XD=$NTPROOT/bin/xntpd # NTP daemon
+#
+# HP adjtimed
+#
+ADJTIMED=$NTPROOT/bin/adjtimed # HP special (adjtime() emulation)
+#
+# ntpdate command
+#
+NTPDATE=$NTPROOT/bin/ntpdate
+
+#
+# secondary timed support
+# The word "TIMED" must be in the config file for timed to start
+# Note that this times is a special version which does not ever set or
+# adjust the time. Ask time@informatik.uni-erlangen.de for patches
+#
+TIMED=$NTPROOT/bin/timed # timed (Berkeley) secondary time service
+ # here used in a *HARMLESS* version
+ # to provide time to "inferior" systems
+#
+# ISREFHOST is a command that returns exit status 0 for a reference host
+# Site specific: sample for dcf77 is given
+#
+ISREFHOST="[ -f $NTPROOT/.karch.$KARCH/sys/OBJ/parsestreams.o -a -f /dev/refclock-0 ]"
+#
+# SETUP_REFCLOCK
+#
+# what to do in order to set up a local reference clock
+# usually this will load a STREAMS module or initialize other things
+# needed
+#
+SETUP_REFCLOCK() {
+ if modstat | grep -s 'PARSE'; then
+ ECHO "loadable PARSER STREAMS module already loaded."
+ else
+ ECHO "attempting to load PARSER STREAMS module..."
+ MDLFILE="/tmp/mdl.$$"
+ if modload $NTPROOT/.karch.$KARCH/sys/OBJ/parsestreams.o -o $MDLFILE 2>&1; then
+ modstat
+ else
+ echo WARNING: load FAILED
+ fi | LOG
+ rm -f $MDLFILE
+ unset MDLFILE
+ fi
+}
+
+kargs() {
+ MATCH=NO
+ HOSTID="`(hostid) 2>/dev/null || echo 000000`"
+ if [ -r "$TC" ]; then
+ exec 0< "$TC"
+ while [ "$MATCH" != "YES" ] && read HOST ID PARAM; do
+ if [ "$HOST" = "DEFAULT" ]; then
+ DEFAULT="$ID $PARAM"
+ else
+ if [ "$ID" = "$HOSTID" -o "$HOST" = "$HOSTNAME" ]; then
+ echo "$PARAM"
+ MATCH=YES
+ fi
+ fi
+ done
+ if [ "$MATCH" != "YES" ]; then
+ if [ -z "$DEFAULT" ]; then
+ echo "-A -p -s -q";
+ else
+ echo "$DEFAULT";
+ fi
+ fi
+ else
+ echo "-A -p -s -q";
+ fi
+}
+
+if [ -x $XD ]; then
+ if [ -x "$ADJTIMED" ]; then
+ $ADJTIMED && ECHO "adjusttimesupport: adjtimed."
+ fi
+ #
+ # WARNING: check ps command first, or you might kill things you don't want to
+ #
+ PID="`(ps -efa 2>/dev/null || ps auxww 2>/dev/null || echo "") | grep xntp | grep -v grep | awk '{ print $2 }'`"
+
+ if [ ! -z "$PID" ]; then
+ ECHO "killing old NTP daemon (PID=$PID)"
+ #
+ # enable this after checking for correctness
+ # kill $PID
+ ECHO "should do a kill $PID, if this is the right PID - check rc script"
+ fi
+ #
+ # try an ntpdate when timeservers are configured
+ #
+ if [ ! -z "$NTPSERVERS" -a -x $NTPDATE ]; then
+ ECHO "NTP initial time setting"
+ $NTPDATE -v $NTPSERVERS | LOG
+ fi
+ #
+ # look for reference clock equipment
+ #
+ if $ISREFHOST; then
+ ECHO "REFERENCE CLOCK SUPPORT (initializing...)"
+ SETUP_REFCLOCK
+ fi
+
+ if [ -r "$CFH" ]; then
+ CF="$CFH"
+ else
+ if [ ! -r "$KEY" ]; then
+ KEY="$KEY.dumb"
+ fi
+ fi
+
+ ECHO "NTP configuration file: $CF"
+ ECHO -n "time daemon startup:"
+
+ if [ -r "$CF" ]; then
+ if [ -x "$KFIX" ]; then
+ KARGS="`kargs`"
+ if [ ! -z "$KARGS" ]; then
+ $KFIX $KARGS && ECHO -n "tickadj $KARGS"
+ fi
+ fi
+ $XD -c "$CF" -k "$KEY" && ECHO -n ' xntpd'
+ if [ -x "$TIMED" ] && grep -s TIMED "$CF"; then
+ $TIMED -M -N && ECHO -n ' timed'
+ fi
+ else
+ msg="configuration file ($CF) not present."
+ fi
+else
+ msg="daemon binary ($XD) not present."
+fi
+ECHO "."
+
+if [ "$msg" ]; then
+ NLECHO "WARNING: NO NTP time sychronisation: $msg"
+fi
diff --git a/usr.sbin/xntpd/scripts/support/etc/setup b/usr.sbin/xntpd/scripts/support/etc/setup
new file mode 100755
index 0000000..d4ea75e
--- /dev/null
+++ b/usr.sbin/xntpd/scripts/support/etc/setup
@@ -0,0 +1,72 @@
+#
+# setup,v 3.1 1993/07/06 01:10:55 jbj Exp
+#
+# /bin/sh sourced file for environment setup
+# expects NTPROOT variable initialized
+#
+# if not set it will be initialized to /usr/local/NTP
+#
+# setup,v
+# Revision 3.1 1993/07/06 01:10:55 jbj
+# XNTP release 3.1
+#
+#
+# Revision 1.1 1992/12/10 12:59:25 kardel
+# Prerelease NTP V3 / DCF
+#
+# Revision 1.1 1992/12/10 10:14:46 kardel
+# Initial revision
+#
+#
+NTPROOT=${NTPROOT-/usr/local/NTP}
+
+#
+# we so use our own echos, as we somes want to substitute them with a
+# file logging version durin the /etc/rc.local phase
+#
+set `type ECHO`
+
+PATH="${PATH}:$NTPROOT/bin"
+export PATH
+
+if [ "$2" = "is" ]; then
+ :
+else
+ #
+ # find out the way echos work (Rest of rc thinks BSD echo)
+ #
+ ECHOREP="`echo -n x`"
+ if [ "$ECHOREP" = "-n x" ]; then
+ ECHO () {
+ if [ "$1" = "-n" ]; then
+ shift
+ echo "$@\c"
+ else
+ echo "$@"
+ fi
+ }
+ #ECHO "System V style echo"
+ else
+ ECHO () {
+ echo "$@"
+ }
+ #ECHO "BSD style echo"
+ fi
+
+ NLECHO () {
+ echo "$@"
+ }
+
+ LOG () {
+ while read _line; do
+ ECHO "$_line"
+ done
+ }
+ #
+ # carefully find out some configuration Variables
+ #
+ ARCH="`(arch) 2>/dev/null || ((uname) > /dev/null && uname -a | awk '{ print $6; }') 2>/dev/null || echo 'no-arch'`"
+ KARCH="`(arch -k) 2>/dev/null || ((uname) > /dev/null && uname -a | awk '{ print $5 }') || echo 'no-arch'`"
+ HOSTNAME="`(hostname) 2>/dev/null || uname -n`"
+fi
+
diff --git a/usr.sbin/xntpd/util/Makefile b/usr.sbin/xntpd/util/Makefile
new file mode 100644
index 0000000..e14a1c0
--- /dev/null
+++ b/usr.sbin/xntpd/util/Makefile
@@ -0,0 +1,28 @@
+#
+# $Id$
+#
+
+CFLAGS+= -I${.CURDIR}/../include
+
+.if exists(${.OBJDIR}/../lib)
+LDADD+= -L${.OBJDIR}/../lib
+DPADD+= ${.OBJDIR}/../lib/libntp.a
+.else
+LDADD+= -L${.CURDIR}/../lib
+DPADD+= ${.CURDIR}/../lib/libntp.a
+.endif
+
+LDADD+= -lntp
+
+PROG= tickadj
+MAN8= ${.CURDIR}/../doc/tickadj.8
+CLEANFILES+= .version version.c
+
+SRCS= tickadj.c version.c
+
+beforedepend: version.c
+
+version.c: ${.CURDIR}/../VERSION
+ sh -e ${.CURDIR}/../scripts/mkversion tickadj
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/xntpd/util/README b/usr.sbin/xntpd/util/README
new file mode 100644
index 0000000..2aedb00
--- /dev/null
+++ b/usr.sbin/xntpd/util/README
@@ -0,0 +1,67 @@
+README file for directory ./util of the NTP Version 3 distribution
+
+This directory contains the sources for the various utility programs. See
+the README and RELNOTES files in the parent directory for directions on
+how to make and install these programs.
+
+The ntptime.c program checks the kernel configuration for the NTP user
+interface syscalls ntp_gettime() and ntp_adjtime(). If present, the
+current timekeeping data are displayed. If not, a dissapointment is
+displayed. Do "make ntptime" in this directory to make the thing,
+but be advised that, unless you have installed the kernel support,
+there will probably be missing vital header files. See the README.kern
+file in the doc directory of this distribution for further details.
+
+The jitter.c program can be used to determine the timing jitter due to
+the operating system in a gettimeofday() call. For most systems the
+dominant contribution to the jitter budget is the period of the hardware
+interrupt, usually in the range 1-10 ms. For those systems with microsecond
+counters, such as recent Sun and certain Ultrix systems, the jitter is
+dominated only by the operating system.
+
+The timetrim.c program can be used with SGI machines to implement a
+scheme to discipline the hardware clock frequency. See the source code
+for further information.
+
+The byteorder.c and longsize.c programs are used during the configuration
+process to determine the byte order (little or big endian) and longword
+size (32 or 64 bits). See the ../scripts/makefile.sh script for further
+details.
+
+The testrs6000.c program is used for testing purposes with the IBM
+RS/6000 AIX machines. Bill Jones <jones@chpc.utexas.edu> reports:
+"I could not get a tickadj of less then 40 us to work on a RS6000.
+If you set it less then 40 us do so at your own risk!"
+
+The tickadj.c program can be used to read and set various kernel
+parameters affecting NTP operations. Comes now the rationale for its use.
+
+Then daemon's clock adjustment algorithms depend (too) strongly
+on the internals of the kernel adjtime() call, and expect it to
+match that which comes with Berkeley-flavour operating systems.
+The daemon actually reads a couple of values from your kernel
+using /dev/kmem (ugh!), the value of `tick' and the value of `tickadj'.
+`tick' is expected to be the number of microseconds which are
+added to the system time on timer interrupts when the clock isn't
+being slewed. `tickadj' is the number of microseconds which are
+added or subtracted from tick when the clock is being slewed.
+
+The program tickadj mimics the daemon's handling of these variables.
+If you run it (as root) and it fails or produces bizarre looking
+values you may have to torque ntp_unixclock.c in the daemon code.
+
+You can also use tickadj -a to set tickadj in the running kernel.
+In addition, tickadj -A will compute the value to set based on the
+kernel's value of tick, while the -t flag allows one to set the
+value of tick and the -s flag will set the value of dosynctodr
+to zero. This is an alternative for people who can't change the
+values in the kernel's disk image.
+
+In addition, the -p flag will set the noprintf variable. This will
+suppress any kernel messages. Kernel message can then only be seen via
+syslog(3). This inhibits clockhopping due to kernel printf's.
+
+The target "ntptime" can only be compiled on systems with kernel PLL
+support. This is currently only possible for SunOS4, Ultrix and DECOSF1.
+You need the propriatary header files for that. So there is no need to
+attempt to compile ntptime unless you have the above configuration.
diff --git a/usr.sbin/xntpd/util/byteorder.c b/usr.sbin/xntpd/util/byteorder.c
new file mode 100644
index 0000000..ff7d239
--- /dev/null
+++ b/usr.sbin/xntpd/util/byteorder.c
@@ -0,0 +1,52 @@
+/*
+ * This works on:
+ * Crays
+ * Conven
+ * sparc's
+ * Dec mip machines
+ * Dec alpha machines
+ * RS6000
+ * SGI's
+ */
+
+#include <stdio.h>
+main()
+{
+ int i;
+ int big;
+ union {
+ unsigned long l;
+ char c[sizeof(long)];
+ } u;
+
+#if defined(LONG8)
+ u.l = (((long)0x08070605) << 32) | (long)0x04030201;
+#else
+ u.l = 0x04030201;
+#endif
+ if (sizeof(long) > 4) {
+ if (u.c[0] == 0x08) big = 1;
+ else big = 0;
+ } else {
+ if (u.c[0] == 0x04) big = 1;
+ else big = 0;
+ }
+ for (i=0; i< sizeof(long); i++) {
+ if (big == 1 && (u.c[i] == (sizeof(long) - i))) {
+ continue;
+ } else if (big == 0 && (u.c[i] == (i+1))) {
+ continue;
+ } else {
+ big = -1;
+ break;
+ }
+ }
+
+ if (big == 1) {
+ printf("XNTP_BIG_ENDIAN\n");
+ } else if (big == 0) {
+ printf("XNTP_LITTLE_ENDIAN\n");
+ }
+ exit(0);
+}
+
diff --git a/usr.sbin/xntpd/util/jitter.c b/usr.sbin/xntpd/util/jitter.c
new file mode 100644
index 0000000..7201e87
--- /dev/null
+++ b/usr.sbin/xntpd/util/jitter.c
@@ -0,0 +1,73 @@
+/*
+ * This program can be used to calibrate the clock reading jitter of a
+ * particular CPU and operating system. It first tickles every element
+ * of an array, in order to force pages into memory, then repeatedly calls
+ * gettimeofday() and, finally, writes out the time values for later
+ * analysis. From this you can determine the jitter and if the clock ever
+ * runs backwards.
+ */
+#include <sys/time.h>
+#include <stdio.h>
+
+#define NBUF 10001
+
+main()
+{
+ struct timeval tp, ts, tr;
+ struct timezone tzp;
+ long temp, j, i, gtod[NBUF];
+
+ gettimeofday(&ts, &tzp);
+ ts.tv_usec = 0;
+
+ /*
+ * Force pages into memory
+ */
+ for (i = 0; i < NBUF; i ++)
+ gtod[i] = 0;
+
+ /*
+ * Construct gtod array
+ */
+ for (i = 0; i < NBUF; i ++) {
+ gettimeofday(&tp, &tzp);
+ tr = tp;
+ tr.tv_sec -= ts.tv_sec;
+ tr.tv_usec -= ts.tv_usec;
+ if (tr.tv_usec < 0) {
+ tr.tv_usec += 1000000;
+ tr.tv_sec--;
+ }
+ gtod[i] = tr.tv_sec * 1000000 + tr.tv_usec;
+ }
+
+ /*
+ * Write out gtod array for later processing with S
+ */
+ for (i = 0; i < NBUF - 1; i++) {
+/*
+ printf("%lu\n", gtod[i]);
+*/
+ gtod[i] = gtod[i + 1] - gtod[i];
+ printf("%lu\n", gtod[i]);
+ }
+
+ /*
+ * Sort the gtod array and display deciles
+ */
+ for (i = 0; i < NBUF - 1; i++) {
+ for (j = 0; j <= i; j++) {
+ if (gtod[j] > gtod[i]) {
+ temp = gtod[j];
+ gtod[j] = gtod[i];
+ gtod[i] = temp;
+ }
+ }
+ }
+ fprintf(stderr, "First rank\n");
+ for (i = 0; i < 10; i++)
+ fprintf(stderr, "%10ld%10ld\n", i, gtod[i]);
+ fprintf(stderr, "Last rank\n");
+ for (i = NBUF - 11; i < NBUF - 1; i++)
+ fprintf(stderr, "%10ld%10ld\n", i, gtod[i]);
+}
diff --git a/usr.sbin/xntpd/util/kern.c b/usr.sbin/xntpd/util/kern.c
new file mode 100644
index 0000000..a2a6672
--- /dev/null
+++ b/usr.sbin/xntpd/util/kern.c
@@ -0,0 +1,210 @@
+/*
+ * This program simulates a first-order, type-II phase-lock loop using
+ * actual code segments from modified kernel distributions for SunOS,
+ * Ultrix and OSF/1 kernels. These segments do not use any licensed code.
+ */
+#include <stdio.h>
+#include <ctype.h>
+#include <math.h>
+#include <sys/time.h>
+
+#include "timex.h"
+
+/*
+ * Phase-lock loop definitions
+ */
+#define HZ 100 /* timer interrupt frequency (Hz) */
+#define MAXPHASE 512000 /* max phase error (us) */
+#define MAXFREQ 200 /* max frequency error (ppm) */
+#define TAU 2 /* time constant (shift 0 - 6) */
+#define POLL 16 /* interval between updates (s) */
+#define MAXSEC 1200 /* max interval between updates (s) */
+
+/*
+ * Function declarations
+ */
+void hardupdate();
+void hardclock();
+void second_overflow();
+
+/*
+ * Kernel variables
+ */
+int tick; /* timer interrupt period (us) */
+int fixtick; /* amortization constant (ppm) */
+struct timeval timex; /* ripoff of kernel time variable */
+
+/*
+ * Phase-lock loop variables
+ */
+int time_status = TIME_BAD; /* clock synchronization status */
+long time_offset = 0; /* time adjustment (us) */
+long time_constant = 0; /* pll time constant */
+long time_tolerance = MAXFREQ; /* frequency tolerance (ppm) */
+long time_precision = 1000000 / HZ; /* clock precision (us) */
+long time_maxerror = MAXPHASE; /* maximum error (us) */
+long time_esterror = MAXPHASE; /* estimated error (us) */
+long time_phase = 0; /* phase offset (scaled us) */
+long time_freq = 0; /* frequency offset (scaled ppm) */
+long time_adj = 0; /* tick adjust (scaled 1 / HZ) */
+long time_reftime = 0; /* time at last adjustment (s) */
+
+/*
+ * Simulation variables
+ */
+double timey = 0; /* simulation time (us) */
+long timez = 0; /* current error (us) */
+long poll_interval = 0; /* poll counter */
+
+/*
+ * Simulation test program
+ */
+void main()
+{
+ tick = 1000000 / HZ;
+ fixtick = 1000000 % HZ;
+ timex.tv_sec = 0;
+ timex.tv_usec = MAXPHASE;
+ time_freq = 0;
+ time_constant = TAU;
+ printf("tick %d us, fixtick %d us\n", tick, fixtick);
+ printf(" time offset freq _offset _freq _adj\n");
+
+ /*
+ * Grind the loop until ^C
+ */
+ while (1) {
+ timey += (double)(1000000) / HZ;
+ if (timey >= 1000000)
+ timey -= 1000000;
+ hardclock();
+ if (timex.tv_usec >= 1000000) {
+ timex.tv_usec -= 1000000;
+ timex.tv_sec++;
+ second_overflow();
+ poll_interval++;
+ if (!(poll_interval % POLL)) {
+ timez = (long)timey - timex.tv_usec;
+ if (timez > 500000)
+ timez -= 1000000;
+ if (timez < -500000)
+ timez += 1000000;
+ hardupdate(timez);
+ printf("%10li%10li%10.2f %08lx %08lx %08lx\n",
+ timex.tv_sec, timez,
+ (double)time_freq / (1 << SHIFT_KF),
+ time_offset, time_freq, time_adj);
+ }
+ }
+ }
+}
+
+/*
+ * This routine simulates the ntp_adjtime() call
+ *
+ * For default SHIFT_UPDATE = 12, offset is limited to +-512 ms, the
+ * maximum interval between updates is 4096 s and the maximum frequency
+ * offset is +-31.25 ms/s.
+ */
+void hardupdate(offset)
+long offset;
+{
+ long ltemp, mtemp;
+
+ time_offset = offset << SHIFT_UPDATE;
+ mtemp = timex.tv_sec - time_reftime;
+ time_reftime = timex.tv_sec;
+ if (mtemp > MAXSEC)
+ mtemp = 0;
+
+ /* ugly multiply should be replaced */
+ if (offset < 0)
+ time_freq -= (-offset * mtemp) >>
+ (time_constant + time_constant);
+ else
+ time_freq += (offset * mtemp) >>
+ (time_constant + time_constant);
+ ltemp = time_tolerance << SHIFT_KF;
+ if (time_freq > ltemp)
+ time_freq = ltemp;
+ else if (time_freq < -ltemp)
+ time_freq = -ltemp;
+ if (time_status == TIME_BAD)
+ time_status = TIME_OK;
+}
+
+/*
+ * This routine simulates the timer interrupt
+ */
+void hardclock()
+{
+ int ltemp, time_update;
+
+ time_update = tick; /* computed by adjtime() */
+ time_phase += time_adj;
+ if (time_phase < -FINEUSEC) {
+ ltemp = -time_phase >> SHIFT_SCALE;
+ time_phase += ltemp << SHIFT_SCALE;
+ time_update -= ltemp;
+ }
+ else if (time_phase > FINEUSEC) {
+ ltemp = time_phase >> SHIFT_SCALE;
+ time_phase -= ltemp << SHIFT_SCALE;
+ time_update += ltemp;
+ }
+ timex.tv_usec += time_update;
+}
+
+/*
+ * This routine simulates the overflow of the microsecond field
+ *
+ * With SHIFT_SCALE = 23, the maximum frequency adjustment is +-256 us
+ * per tick, or 25.6 ms/s at a clock frequency of 100 Hz. The time
+ * contribution is shifted right a minimum of two bits, while the frequency
+ * contribution is a right shift. Thus, overflow is prevented if the
+ * frequency contribution is limited to half the maximum or 15.625 ms/s.
+ */
+void second_overflow()
+{
+ int ltemp;
+
+ time_maxerror += time_tolerance;
+ if (time_offset < 0) {
+ ltemp = -time_offset >>
+ (SHIFT_KG + time_constant);
+ time_offset += ltemp;
+ time_adj = -(ltemp <<
+ (SHIFT_SCALE - SHIFT_HZ - SHIFT_UPDATE));
+ } else {
+ ltemp = time_offset >>
+ (SHIFT_KG + time_constant);
+ time_offset -= ltemp;
+ time_adj = ltemp <<
+ (SHIFT_SCALE - SHIFT_HZ - SHIFT_UPDATE);
+ }
+ if (time_freq < 0)
+ time_adj -= -time_freq >> (SHIFT_KF + SHIFT_HZ - SHIFT_SCALE);
+ else
+ time_adj += time_freq >> (SHIFT_KF + SHIFT_HZ - SHIFT_SCALE);
+ time_adj += fixtick << (SHIFT_SCALE - SHIFT_HZ);
+
+ /* ugly divide should be replaced */
+ if (timex.tv_sec % 86400 == 0) {
+ switch (time_status) {
+
+ case TIME_INS:
+ timex.tv_sec--; /* !! */
+ time_status = TIME_OOP;
+ break;
+
+ case TIME_DEL:
+ timex.tv_sec++;
+ time_status = TIME_OK;
+ break;
+
+ case TIME_OOP:
+ time_status = TIME_OK;
+ break;
+ }
+ }
+}
diff --git a/usr.sbin/xntpd/util/longsize.c b/usr.sbin/xntpd/util/longsize.c
new file mode 100644
index 0000000..bb884ba
--- /dev/null
+++ b/usr.sbin/xntpd/util/longsize.c
@@ -0,0 +1,11 @@
+#include <stdio.h>
+
+main()
+{
+ if (sizeof(long) == 8) {
+ printf("-DLONG8\n");
+ } else if (sizeof(long) == 4) {
+ printf("-DLONG4\n");
+ }
+ exit(0);
+}
diff --git a/usr.sbin/xntpd/util/ntptime.c b/usr.sbin/xntpd/util/ntptime.c
new file mode 100644
index 0000000..38d9a4a
--- /dev/null
+++ b/usr.sbin/xntpd/util/ntptime.c
@@ -0,0 +1,236 @@
+/*
+ * NTP test program
+ *
+ * This program tests to see if the NTP user interface routines
+ * ntp_gettime() and ntp_adjtime() have been implemented in the kernel.
+ * If so, each of these routines is called to display current timekeeping
+ * data.
+ *
+ * For more information, see the README.kern file in the doc directory
+ * of the xntp3 distribution.
+ */
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/time.h>
+#include <signal.h>
+#include <errno.h>
+
+#include "ntp_fp.h"
+#include "ntp_unixtime.h"
+#include "sys/timex.h"
+#include "ntp_stdlib.h"
+
+#ifndef SYS_DECOSF1
+#define BADCALL -1 /* this is supposed to be a bad syscall */
+#endif /* SYS_DECOSF1 */
+
+#ifdef KERNEL_PLL
+#define ntp_gettime(t) syscall(SYS_ntp_gettime, (t))
+#define ntp_adjtime(t) syscall(SYS_ntp_adjtime, (t))
+#else /* KERNEL_PLL */
+#define SYS_ntp_adjtime NTP_SYSCALL_ADJ
+#define SYS_ntp_gettime NTP_SYSCALL_GET
+#endif /* KERNEL_PLL */
+
+/*
+ * Function prototypes
+ */
+extern int sigvec P((int, struct sigvec *, struct sigvec *));
+extern int syscall P((int, void *, ...));
+void pll_trap P((void));
+
+static struct sigvec newsigsys; /* new sigvec status */
+static struct sigvec sigsys; /* current sigvec status */
+static int pll_control; /* (0) daemon, (1) kernel loop */
+
+static char* progname;
+static char optargs[] = "ce:f:hm:o:rs:t:";
+
+void
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ extern int ntp_optind;
+ extern char *ntp_optarg;
+ int status;
+ struct ntptimeval ntv;
+ struct timex ntx, _ntx;
+ int times[20];
+ double ftemp, gtemp;
+ l_fp ts;
+ int c;
+ int errflg = 0;
+ int cost = 0;
+ int rawtime = 0;
+
+ memset((char *)&ntx, 0, sizeof(ntx));
+ progname = argv[0];
+ while ((c = ntp_getopt(argc, argv, optargs)) != EOF) switch (c) {
+ case 'c':
+ cost++;
+ break;
+ case 'e':
+ ntx.modes |= MOD_ESTERROR;
+ ntx.esterror = atoi(ntp_optarg);
+ break;
+ case 'f':
+ ntx.modes |= MOD_FREQUENCY;
+ ntx.freq = (int) (atof(ntp_optarg) *
+ (1 << SHIFT_USEC));
+ if (ntx.freq < (-100 << SHIFT_USEC)
+ || ntx.freq > ( 100 << SHIFT_USEC)) errflg++;
+ break;
+ case 'm':
+ ntx.modes |= MOD_MAXERROR;
+ ntx.maxerror = atoi(ntp_optarg);
+ break;
+ case 'o':
+ ntx.modes |= MOD_OFFSET;
+ ntx.offset = atoi(ntp_optarg);
+ break;
+ case 'r':
+ rawtime++;
+ break;
+ case 's':
+ ntx.modes |= MOD_STATUS;
+ ntx.status = atoi(ntp_optarg);
+ if (ntx.status < 0 || ntx.status > 4) errflg++;
+ break;
+ case 't':
+ ntx.modes |= MOD_TIMECONST;
+ ntx.constant = atoi(ntp_optarg);
+ if (ntx.constant < 0 || ntx.constant > MAXTC)
+ errflg++;
+ break;
+ default:
+ errflg++;
+ }
+ if (errflg || (ntp_optind != argc)) {
+ (void) fprintf(stderr,
+ "usage: %s [-%s]\n\n\
+ -c display the time taken to call ntp_gettime (us)\n\
+ -e esterror estimate of the error (us)\n\
+ -f frequency Frequency error (-100 .. 100) (ppm)\n\
+ -h display this help info\n\
+ -m maxerror max possible error (us)\n\
+ -o offset current offset (ms)\n\
+ -r print the unix and NTP time raw\n\
+ -l leap Set the leap bits\n\
+ -t timeconstant log2 of PLL time constant (0 .. %d)\n",
+ progname, optargs, MAXTC);
+ exit(2);
+ }
+
+
+ /*
+ * Test to make sure the sigvec() works in case of invalid
+ * syscall codes.
+ */
+ newsigsys.sv_handler = pll_trap;
+ newsigsys.sv_mask = 0;
+ newsigsys.sv_flags = 0;
+ if (sigvec(SIGSYS, &newsigsys, &sigsys)) {
+ perror("sigvec() fails to save SIGSYS trap");
+ exit(1);
+ }
+
+#ifdef BADCALL
+ /*
+ * Make sure the trapcatcher works.
+ */
+ pll_control = 1;
+ (void)syscall(BADCALL, &ntv); /* dummy parameter f. ANSI compilers */
+ if (pll_control)
+ printf("sigvec() failed to catch an invalid syscall\n");
+#endif
+
+ if (cost) {
+ for (c = 0; c < sizeof times / sizeof times[0]; c++) {
+ (void)ntp_gettime(&ntv);
+ if (pll_control < 0)
+ break;
+ times[c] = ntv.time.tv_usec;
+ }
+ if (pll_control >= 0) {
+ printf("[ us %06d:", times[0]);
+ for (c = 1; c < sizeof times / sizeof times[0]; c++)
+ printf(" %d", times[c] - times[c - 1]);
+ printf(" ]\n");
+ }
+ }
+ (void)ntp_gettime(&ntv);
+ _ntx.modes = 0; /* Ensure nothing is set */
+ (void)ntp_adjtime(&_ntx);
+ if (pll_control < 0) {
+ printf("NTP user interface routines are not configured in this kernel.\n");
+ goto lexit;
+ }
+
+ /*
+ * Fetch timekeeping data and display.
+ */
+ status = ntp_gettime(&ntv);
+ if (status < 0)
+ perror("ntp_gettime() call fails");
+ else {
+ printf("ntp_gettime() returns code %d\n", status);
+ TVTOTS(&ntv.time, &ts);
+ ts.l_uf += TS_ROUNDBIT; /* guaranteed not to overflow */
+ ts.l_ui += JAN_1970;
+ ts.l_uf &= TS_MASK;
+ printf(" time %s, (.%06d),\n",
+ prettydate(&ts), ntv.time.tv_usec);
+ printf(" maximum error %ld us, estimated error %ld us.\n",
+ ntv.maxerror, ntv.esterror);
+ if (rawtime) printf(" ntptime=%x.%x unixtime=%x.%06d %s",
+ ts.l_ui, ts.l_uf, ntv.time.tv_sec, ntv.time.tv_usec,
+ ctime(&ntv.time.tv_sec));
+ }
+ status = ntp_adjtime(&ntx);
+ if (status < 0)
+ perror((errno == EPERM) ?
+ "Must be root to set kernel values\nntp_adjtime() call fails" :
+ "ntp_adjtime() call fails");
+ else {
+ printf("ntp_adjtime() returns code %d\n", status);
+ ftemp = ntx.freq;
+ ftemp /= (1 << SHIFT_USEC);
+ printf(" modes %04x, offset %ld us, frequency %.3f ppm, interval %d s,\n",
+ ntx.modes, ntx.offset, ftemp, 1 << ntx.shift);
+ printf(" maximum error %ld us, estimated error %ld us,\n",
+ ntx.maxerror, ntx.esterror);
+ ftemp = ntx.tolerance;
+ ftemp /= (1 << SHIFT_USEC);
+ printf(" status %04x, time constant %ld, precision %ld us, tolerance %.0f ppm,\n",
+ ntx.status, ntx.constant, ntx.precision, ftemp);
+ if (ntx.shift == 0)
+ return;
+ ftemp = ntx.ppsfreq;
+ ftemp /= (1 << SHIFT_USEC);
+ gtemp = ntx.stabil;
+ gtemp /= (1 << SHIFT_USEC);
+ printf(" pps frequency %.3f ppm, stability %.3f ppm, jitter %ld us,\n",
+ ftemp, gtemp, ntx.jitter);
+ printf(" intervals %ld, jitter exceeded %ld, stability exceeded %ld, errors %ld.\n",
+ ntx.calcnt, ntx.jitcnt, ntx.stbcnt, ntx.errcnt);
+ }
+
+ /*
+ * Put things back together the way we found them.
+ */
+lexit: if (sigvec(SIGSYS, &sigsys, (struct sigvec *)NULL)) {
+ perror("sigvec() fails to restore SIGSYS trap");
+ exit(1);
+ }
+ exit(0);
+}
+
+/*
+ * pll1_trap - trap processor for undefined syscalls
+ */
+void
+pll_trap()
+{
+ pll_control--;
+}
diff --git a/usr.sbin/xntpd/util/precision.c b/usr.sbin/xntpd/util/precision.c
new file mode 100644
index 0000000..64fe336
--- /dev/null
+++ b/usr.sbin/xntpd/util/precision.c
@@ -0,0 +1,150 @@
+#include <sys/types.h>
+#include <sys/time.h>
+#include "ntp_unixtime.h"
+
+#define DEFAULT_SYS_PRECISION -99
+
+int default_get_resolution();
+int default_get_precision();
+
+int
+main() {
+ printf("log2(resolution) = %d, log2(precision) = %d\n",
+ default_get_resolution(),
+ default_get_precision());
+ return 0;
+}
+
+/* Find the resolution of the system clock by watching how the current time
+ * changes as we read it repeatedly.
+ *
+ * struct timeval is only good to 1us, which may cause problems as machines
+ * get faster, but until then the logic goes:
+ *
+ * If a machine has resolution (i.e. accurate timing info) > 1us, then it will
+ * probably use the "unused" low order bits as a counter (to force time to be
+ * a strictly increaing variable), incrementing it each time any process
+ * requests the time [[ or maybe time will stand still ? ]].
+ *
+ * SO: the logic goes:
+ *
+ * IF the difference from the last time is "small" (< MINSTEP)
+ * THEN this machine is "counting" with the low order bits
+ * ELIF this is not the first time round the loop
+ * THEN this machine *WAS* counting, and has now stepped
+ * ELSE this machine has resolution < time to read clock
+ *
+ * SO: if it exits on the first loop, assume "full accuracy" (1us)
+ * otherwise, take the log2(observered difference, rounded UP)
+ *
+ * MINLOOPS > 1 ensures that even if there is a STEP between the initial call
+ * and the first loop, it doesn't stop too early.
+ * Making it even greater allows MINSTEP to be reduced, assuming that the
+ * chance of MINSTEP-1 other processes getting in and calling gettimeofday
+ * between this processes's calls.
+ * Reducing MINSTEP may be necessary as this sets an upper bound for the time
+ * to actually call gettimeofday.
+ */
+
+#define DUSECS 1000000
+#define HUSECS (1024 * 1024)
+#define MINSTEP 5 /* some systems increment uS on each call */
+ /* Don't use "1" as some *other* process may read too*/
+ /*We assume no system actually *ANSWERS* in this time*/
+#define MAXSTEP 20000 /* maximum clock increment (us) */
+#define MINLOOPS 5 /* minimum number of step samples */
+#define MAXLOOPS HUSECS /* Assume precision < .1s ! */
+
+int default_get_resolution()
+{
+ struct timeval tp;
+ struct timezone tzp;
+ long last;
+ int i;
+ long diff;
+ long val;
+ int minsteps = MINLOOPS; /* need at least this many steps */
+
+ gettimeofday(&tp, &tzp);
+ last = tp.tv_usec;
+ for (i = - --minsteps; i< MAXLOOPS; i++) {
+ gettimeofday(&tp, &tzp);
+ diff = tp.tv_usec - last;
+ if (diff < 0) diff += DUSECS;
+ if (diff > MINSTEP) if (minsteps-- <= 0) break;
+ last = tp.tv_usec;
+ }
+
+ printf("resolution = %ld usec after %d loop%s\n",
+ diff, i, (i==1) ? "" : "s");
+
+ diff = (diff *3)/2;
+ if (i >= MAXLOOPS) {
+ printf(
+ " (Boy this machine is fast ! %d loops without a step)\n",
+ MAXLOOPS);
+ diff = 1; /* No STEP, so FAST machine */
+ }
+ if (i == 0) {
+ printf(
+" (The resolution is less than the time to read the clock -- Assume 1us)\n");
+ diff = 1; /* time to read clock >= resolution */
+ }
+ for (i=0, val=HUSECS; val>0; i--, val >>= 1) if (diff >= val) return i;
+ printf(" (Oh dear -- that wasn't expected ! I'll guess !)\n");
+ return DEFAULT_SYS_PRECISION /* Something's BUST, so lie ! */;
+}
+
+/* ===== Rest of this code lifted straight from xntpd/ntp_proto.c ! ===== */
+
+/*
+ * This routine calculates the differences between successive calls to
+ * gettimeofday(). If a difference is less than zero, the us field
+ * has rolled over to the next second, so we add a second in us. If
+ * the difference is greater than zero and less than MINSTEP, the
+ * clock has been advanced by a small amount to avoid standing still.
+ * If the clock has advanced by a greater amount, then a timer interrupt
+ * has occurred and this amount represents the precision of the clock.
+ * In order to guard against spurious values, which could occur if we
+ * happen to hit a fat interrupt, we do this for MINLOOPS times and
+ * keep the minimum value obtained.
+ */
+int default_get_precision()
+{
+ struct timeval tp;
+ struct timezone tzp;
+ long last;
+ int i;
+ long diff;
+ long val;
+ long usec;
+
+ usec = 0;
+ val = MAXSTEP;
+ GETTIMEOFDAY(&tp, &tzp);
+ last = tp.tv_usec;
+ for (i = 0; i < MINLOOPS && usec < HUSECS;) {
+ GETTIMEOFDAY(&tp, &tzp);
+ diff = tp.tv_usec - last;
+ last = tp.tv_usec;
+ if (diff < 0)
+ diff += DUSECS;
+ usec += diff;
+ if (diff > MINSTEP) {
+ i++;
+ if (diff < val)
+ val = diff;
+ }
+ }
+ printf("precision = %ld usec after %d loop%s\n",
+ val, i, (i == 1) ? "" : "s");
+ if (usec >= HUSECS) {
+ printf(" (Boy this machine is fast ! usec was %ld)\n",
+ usec);
+ val = MINSTEP; /* val <= MINSTEP; fast machine */
+ }
+ diff = HUSECS;
+ for (i = 0; diff > val; i--)
+ diff >>= 1;
+ return (i);
+}
diff --git a/usr.sbin/xntpd/util/testrs6000.c b/usr.sbin/xntpd/util/testrs6000.c
new file mode 100644
index 0000000..0d4bf4f
--- /dev/null
+++ b/usr.sbin/xntpd/util/testrs6000.c
@@ -0,0 +1,44 @@
+/* Checks for the RS/6000 AIX adjtime() bug, in which if a negative
+ * offset is given, the system gets messed up and never completes the
+ * adjustment. If the problem is fixed, this program will print the
+ * time, sit there for 10 seconds, and exit. If the problem isn't fixed,
+ * the program will print an occasional "result=nnnnnn" (the residual
+ * slew from adjtime()).
+ *
+ * Compile this with bsdcc and run it as root!
+ */
+#include <signal.h>
+#include <sys/time.h>
+#include <time.h>
+#include <stdio.h>
+int timeout();
+struct timeval adjustment, result;
+main () {
+ struct itimerval value, oldvalue;
+ int i;
+ time_t curtime;
+ curtime = time(0);
+ printf("Starting: %s", ctime(&curtime));
+ value.it_interval.tv_sec = value.it_value.tv_sec = 1;
+ value.it_interval.tv_usec = value.it_value.tv_usec = 0;
+ adjustment.tv_sec = 0;
+ adjustment.tv_usec = -2000;
+ signal(SIGALRM, timeout);
+ setitimer(ITIMER_REAL, &value, &oldvalue);
+ for (i=0; i<10; i++) {
+ pause();
+ }
+}
+
+int timeout(sig, code, scp)
+int sig,code;
+struct sigcontext *scp;
+{
+ signal (SIGALRM, timeout);
+ if (adjtime(&adjustment, &result))
+ printf("adjtime call failed\n");
+ if (result.tv_sec != 0 || result.tv_usec != 0) {
+ printf("result.u = %d.%06.6d ", (int) result.tv_sec,
+ (int) result.tv_usec);
+ }
+}
diff --git a/usr.sbin/xntpd/util/tickadj.c b/usr.sbin/xntpd/util/tickadj.c
new file mode 100644
index 0000000..d3781c5
--- /dev/null
+++ b/usr.sbin/xntpd/util/tickadj.c
@@ -0,0 +1,584 @@
+/*
+ * tickadj - read, and possibly modify, the kernel `tick' and
+ * `tickadj' variables, as well as `dosynctodr'. Note that
+ * this operates on the running kernel only. I'd like to be
+ * able to read and write the binary as well, but haven't
+ * mastered this yet.
+ */
+#include <stdio.h>
+
+#if !defined(SYS_VAX) && !defined(SYS_BSD)
+#include <unistd.h>
+#endif /* SYS_VAX */
+
+#ifdef SYS_LINUX
+#include "sys/timex.h"
+
+struct timex txc;
+
+int
+main(int argc, char ** argv)
+{
+ if (argc > 2)
+ {
+ fprintf(stderr, "Usage: %s [tick_value]\n", argv[0]);
+ exit(-1);
+ }
+ else if (argc == 2)
+ {
+ if ( (txc.tick = atoi(argv[1])) < 1 )
+ {
+ fprintf(stderr, "Silly value for tick: %s\n", argv[1]);
+ exit(-1);
+ }
+ txc.mode = ADJ_TICK;
+ }
+ else
+ txc.mode = 0;
+
+ if (__adjtimex(&txc) < 0)
+ perror("adjtimex");
+ else
+ printf("tick = %d\n", txc.tick);
+
+ return(0);
+}
+#else /* not Linux... kmem tweaking: */
+
+#include <sys/types.h>
+#ifndef SYS_BSD
+#include <sys/file.h>
+#endif
+#include <sys/stat.h>
+
+#if defined(SYS_AUX3) || defined(SYS_AUX2)
+#include <sys/param.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/file.h>
+#include <a.out.h>
+#include <sys/var.h>
+#else
+#include <nlist.h>
+#endif
+
+#include "ntp_io.h"
+#include "ntp_stdlib.h"
+
+#if defined(HAVE_GETBOOTFILE)
+#include <paths.h>
+#endif
+
+#ifdef RS6000
+#undef hz
+#endif /* RS6000 */
+
+#if defined(SOLARIS)||defined(RS6000)||defined(SYS_SINIXM)
+#if !defined(_SC_CLK_TCK)
+#include <unistd.h>
+#endif
+#endif
+
+#if defined(SYS_PTX) || defined(SYS_IX86OSF1)
+#define L_SET SEEK_SET
+#endif
+
+#define KMEM "/dev/kmem"
+#define STREQ(a, b) (*(a) == *(b) && strcmp((a), (b)) == 0)
+
+char *progname;
+int debug;
+
+int dokmem = 1;
+int writetickadj = 0;
+int writeopttickadj = 0;
+int unsetdosync = 0;
+int writetick = 0;
+int quiet = 0;
+int setnoprintf = 0;
+
+char *kmem = KMEM;
+char *kernel = NULL;
+char *file = NULL;
+int fd = -1;
+
+static char * getoffsets P((char *, unsigned long *, unsigned long *, unsigned long *, unsigned long *));
+static int openfile P((char *, int));
+static void writevar P((int, unsigned long, int));
+static void readvar P((int, unsigned long, int *));
+
+/*
+ * main - parse arguments and handle options
+ */
+void
+main(argc, argv)
+int argc;
+char *argv[];
+{
+ int c;
+ int errflg = 0;
+ extern int ntp_optind;
+ extern char *ntp_optarg;
+ unsigned long tickadj_offset;
+ unsigned long tick_offset;
+ unsigned long dosync_offset;
+ unsigned long noprintf_offset;
+ int tickadj;
+ int tick;
+ int dosynctodr;
+ int noprintf;
+ int hz, hz_hundredths;
+ int recommend_tickadj;
+ long tmp;
+ int openfile();
+ char *getoffsets();
+ void readvar();
+ void writevar();
+
+ progname = argv[0];
+ while ((c = ntp_getopt(argc, argv, "a:Adkqpst:")) != EOF)
+ switch (c) {
+ case 'd':
+ ++debug;
+ break;
+ case 'k':
+ dokmem = 1;
+ break;
+ case 'p':
+ setnoprintf = 1;
+ break;
+ case 'q':
+ quiet = 1;
+ break;
+ case 'a':
+ writetickadj = atoi(ntp_optarg);
+ if (writetickadj <= 0) {
+ (void) fprintf(stderr,
+ "%s: unlikely value for tickadj: %s\n",
+ progname, ntp_optarg);
+ errflg++;
+ }
+ break;
+ case 'A':
+ writeopttickadj = 1;
+ break;
+ case 's':
+ unsetdosync = 1;
+ break;
+ case 't':
+ writetick = atoi(ntp_optarg);
+ if (writetick <= 0) {
+ (void) fprintf(stderr,
+ "%s: unlikely value for tick: %s\n",
+ progname, ntp_optarg);
+ errflg++;
+ }
+ break;
+ default:
+ errflg++;
+ break;
+ }
+ if (errflg || ntp_optind != argc) {
+ (void) fprintf(stderr,
+ "usage: %s [-Aqsp] [-a newadj] [-t newtick]\n", progname);
+ exit(2);
+ }
+ kernel = getoffsets(kernel, &tick_offset,
+ &tickadj_offset, &dosync_offset, &noprintf_offset);
+
+ if (debug) {
+ (void) printf("tick offset = %lu\n", tick_offset);
+ (void) printf("tickadj offset = %lu\n", tickadj_offset);
+ (void) printf("dosynctodr offset = %lu\n", dosync_offset);
+ (void) printf("noprintf offset = %lu\n", noprintf_offset);
+ }
+
+ if (setnoprintf && (noprintf_offset == 0)) {
+ (void) fprintf(stderr,
+ "No noprintf kernal variable\n");
+ errflg++;
+ }
+
+ if (unsetdosync && (dosync_offset == 0)) {
+ (void) fprintf(stderr,
+ "No dosynctodr kernal variable\n");
+ errflg++;
+ }
+
+ if (writeopttickadj && (tickadj_offset == 0)) {
+ (void) fprintf(stderr,
+ "No tickadj kernal variable\n");
+ errflg++;
+ }
+
+ if (writetick && (tick_offset == 0)) {
+ (void) fprintf(stderr,
+ "No tick kernal variable\n");
+ errflg++;
+ }
+
+
+ if (tickadj_offset != 0)
+ readvar(fd, tickadj_offset, &tickadj);
+
+#if defined(SOLARIS)||defined(RS6000)||defined(SYS_SINIXM)
+ tick = 1000000/sysconf(_SC_CLK_TCK);
+#else
+ readvar(fd, tick_offset, &tick);
+#endif
+
+ if (dosync_offset != 0)
+ readvar(fd, dosync_offset, &dosynctodr);
+ if (noprintf_offset != 0)
+ readvar(fd, noprintf_offset, &noprintf);
+ (void) close(fd);
+
+ if (unsetdosync && dosync_offset == 0) {
+ (void) fprintf(stderr,
+ "%s: can't find dosynctodr in namelist\n", progname);
+ exit(1);
+ }
+
+ if (!quiet) {
+ (void) printf("tick = %d us",tick);
+ if (tickadj_offset != 0)
+ (void) printf(", tickadj = %d us", tickadj);
+ if (dosync_offset != 0)
+ (void) printf(", dosynctodr is %s", dosynctodr ? "on" : "off");
+ (void) printf("\n");
+ if (noprintf_offset != 0)
+ (void) printf("kernel level printf's: %s\n", noprintf ? "off" : "on");
+ }
+
+ if (tick <= 0) {
+ (void) fprintf(stderr, "%s: the value of tick is silly!\n",
+ progname);
+ exit(1);
+ }
+
+ hz = (int)(1000000L / (long)tick);
+ hz_hundredths = (int)((100000000L / (long)tick) - ((long)hz * 100L));
+ if (!quiet)
+ (void) printf("calculated hz = %d.%02d Hz\n", hz,
+ hz_hundredths);
+ tmp = (long) tick * 500L;
+ recommend_tickadj = (int)(tmp / 1000000L);
+ if (tmp % 1000000L > 0)
+ recommend_tickadj++;
+
+#if defined(RS6000)
+ if (recommend_tickadj < 40) recommend_tickadj = 40;
+#endif
+
+ if ((!quiet) && (tickadj_offset != 0))
+ (void) printf("recommended value of tickadj = %d us\n",
+ recommend_tickadj);
+
+ if (writetickadj == 0 && !writeopttickadj &&
+ !unsetdosync && writetick == 0 && !setnoprintf)
+ exit(errflg ? 1 : 0);
+
+ if (writetickadj == 0 && writeopttickadj)
+ writetickadj = recommend_tickadj;
+
+ fd = openfile(file, O_WRONLY);
+
+ if (setnoprintf && (dosync_offset != 0)) {
+ if (!quiet) {
+ (void) fprintf(stderr, "setting noprintf: ");
+ (void) fflush(stderr);
+ }
+ writevar(fd, noprintf_offset, 1);
+ if (!quiet)
+ (void) fprintf(stderr, "done!\n");
+ }
+
+ if ((writetick > 0) && (tick_offset != 0)) {
+ if (!quiet) {
+ (void) fprintf(stderr, "writing tick, value %d: ",
+ writetick);
+ (void) fflush(stderr);
+ }
+ writevar(fd, tick_offset, writetick);
+ if (!quiet)
+ (void) fprintf(stderr, "done!\n");
+ }
+
+ if ((writetickadj > 0) && (tickadj_offset != 0)) {
+ if (!quiet) {
+ (void) fprintf(stderr, "writing tickadj, value %d: ",
+ writetickadj);
+ (void) fflush(stderr);
+ }
+ writevar(fd, tickadj_offset, writetickadj);
+ if (!quiet)
+ (void) fprintf(stderr, "done!\n");
+ }
+
+ if (unsetdosync && (dosync_offset != 0)) {
+ if (!quiet) {
+ (void) fprintf(stderr, "zeroing dosynctodr: ");
+ (void) fflush(stderr);
+ }
+ writevar(fd, dosync_offset, 0);
+ if (!quiet)
+ (void) fprintf(stderr, "done!\n");
+ }
+ (void) close(fd);
+ exit(errflg ? 1 : 0);
+}
+
+/*
+ * getoffsets - read the magic offsets from the specified file
+ */
+static char *
+getoffsets(filex, tick_off, tickadj_off, dosync_off, noprintf_off)
+ char *filex;
+ unsigned long *tick_off;
+ unsigned long *tickadj_off;
+ unsigned long *dosync_off;
+ unsigned long *noprintf_off;
+{
+ char **kname, *knm;
+
+#if defined(SYS_AUX3) || defined(SYS_AUX2)
+#define X_TICKADJ 0
+#define X_TICK 1
+#define X_DEF
+ static struct nlist nl[] =
+ { {"tickadj"},
+ {"tick"},
+ {""},
+ };
+#endif
+
+#ifdef NeXT
+#define X_TICKADJ 0
+#define X_TICK 1
+#define X_DOSYNC 2
+#define X_NOPRINTF 3
+#define X_DEF
+ static struct nlist nl[] =
+ { {{"_tickadj"}},
+ {{"_tick"}},
+ {{"_dosynctodr"}},
+ {{"_noprintf"}},
+ {{""}},
+ };
+#endif
+
+#if defined(SYS_SVR4) || defined(SYS_PTX)
+#define X_TICKADJ 0
+#define X_TICK 1
+#define X_DOSYNC 2
+#define X_NOPRINTF 3
+#define X_DEF
+ static struct nlist nl[] =
+ { {{"tickadj"}},
+ {{"tick"}},
+ {{"doresettodr"}},
+ {{"noprintf"}},
+ {{""}},
+ };
+#endif /* SYS_SVR4 */
+
+#if defined(SOLARIS)||defined(RS6000)||defined(SYS_SINIXM)
+#ifndef SOLARIS_HRTIME
+#define X_TICKADJ 0
+#endif
+#define X_DOSYNC 1
+#define X_NOPRINTF 2
+#define X_DEF
+ static struct nlist nl[] =
+ { {"tickadj"},
+ {"dosynctodr"},
+ {"noprintf"},
+ {""},
+ };
+
+#if defined(RS6000)
+ int i;
+#endif
+#endif
+
+#if defined(SYS_HPUX)
+#define X_TICKADJ 0
+#define X_TICK 1
+#define X_DEF
+ static struct nlist nl[] =
+#ifdef hp9000s300
+ { {"_tickadj"},
+ {"_old_tick"},
+#else
+ { {"tickadj"},
+ {"old_tick"},
+#endif
+ {""},
+ };
+#endif
+
+#if !defined(X_DEF)
+#define X_TICKADJ 0
+#define X_TICK 1
+#define X_DOSYNC 2
+#define X_NOPRINTF 3
+ static struct nlist nl[] =
+ { {"_tickadj"},
+ {"_tick"},
+ {"_dosynctodr"},
+ {"_noprintf"},
+ {""},
+ };
+#endif
+#ifndef HAVE_GETBOOTFILE
+ static char *kernels[] = {
+ "/kernel",
+ "/vmunix",
+ "/unix",
+ "/mach",
+ "/kernel/unix",
+ "/386bsd",
+ "/netbsd",
+ NULL
+ };
+#endif
+ struct stat stbuf;
+
+#ifdef HAVE_GETBOOTFILE
+ /* XXX bogus cast to avoid `const' poisoning. */
+ kname = &knm;
+ *kname = (char *)getbootfile();
+ if (stat(*kname, &stbuf) == -1 || nlist(*kname, nl) == -1)
+ *kname = NULL;
+#else
+ for (kname = kernels; *kname != NULL; kname++) {
+ if (stat(*kname, &stbuf) == -1)
+ continue;
+ if (nlist(*kname, nl) >= 0)
+ break;
+ }
+#endif
+ if (*kname == NULL) {
+ (void) fprintf(stderr,
+ "%s: nlist fails: can't find/read /vmunix or /unix\n",
+ progname);
+ exit(1);
+ }
+
+ if (dokmem)
+ file = kmem;
+ else
+ file = kernel;
+
+ fd = openfile(file, O_RDONLY);
+#if defined(RS6000)
+ /*
+ * Go one more round of indirection.
+ */
+ for (i=0; i<(sizeof(nl)/sizeof(struct nlist)); i++) {
+ if (nl[i].n_value) {
+ readvar(fd, nl[i].n_value, &nl[i].n_value);
+ }
+ }
+#endif
+ *tickadj_off = 0;
+ *tick_off = 0;
+ *dosync_off = 0;
+ *noprintf_off = 0;
+
+#if defined(X_TICKADJ)
+ *tickadj_off = nl[X_TICKADJ].n_value;
+#endif
+
+#if defined(X_TICK)
+ *tick_off = nl[X_TICK].n_value;
+#endif
+
+#if defined(X_DOSYNC)
+ *dosync_off = nl[X_DOSYNC].n_value;
+#endif
+
+#if defined(X_NOPRINTF)
+ *noprintf_off = nl[X_NOPRINTF].n_value;
+#endif
+ return *kname;
+}
+
+#undef X_TICKADJ
+#undef X_TICK
+#undef X_DOSYNC
+#undef X_NOPRINTF
+
+
+/*
+ * openfile - open the file, check for errors
+ */
+static int
+openfile(name, mode)
+ char *name;
+ int mode;
+{
+ int fd;
+
+ fd = open(name, mode);
+ if (fd < 0) {
+ (void) fprintf(stderr, "%s: open %s: ", progname, name);
+ perror("");
+ exit(1);
+ }
+ return fd;
+}
+
+
+/*
+ * writevar - write a variable into the file
+ */
+static void
+writevar(fd, off, var)
+ int fd;
+ unsigned long off;
+ int var;
+{
+
+ if (lseek(fd, off, L_SET) == -1) {
+ (void) fprintf(stderr, "%s: lseek fails: ", progname);
+ perror("");
+ exit(1);
+ }
+ if (write(fd, (char *)&var, sizeof(int)) != sizeof(int)) {
+ (void) fprintf(stderr, "%s: write fails: ", progname);
+ perror("");
+ exit(1);
+ }
+}
+
+
+/*
+ * readvar - read a variable from the file
+ */
+static void
+readvar(fd, off, var)
+ int fd;
+ unsigned long off;
+ int *var;
+{
+ int i;
+
+ if (lseek(fd, off, L_SET) == -1) {
+ (void) fprintf(stderr, "%s: lseek fails: ", progname);
+ perror("");
+ exit(1);
+ }
+ i = read(fd, (char *)var, sizeof(int));
+ if (i < 0) {
+ (void) fprintf(stderr, "%s: read fails: ", progname);
+ perror("");
+ exit(1);
+ }
+ if (i != sizeof(int)) {
+ (void) fprintf(stderr, "%s: read expected %d, got %d\n",
+ progname, (int)sizeof(int), i);
+ exit(1);
+ }
+}
+#endif /* not Linux */
diff --git a/usr.sbin/xntpd/util/timetrim.c b/usr.sbin/xntpd/util/timetrim.c
new file mode 100644
index 0000000..7b9413f
--- /dev/null
+++ b/usr.sbin/xntpd/util/timetrim.c
@@ -0,0 +1,85 @@
+/*
+ * timetrim.c
+ *
+ * "timetrim" allows setting and adjustment of the system clock frequency
+ * trim parameter on Silicon Graphics machines. The trim value native
+ * units are nanoseconds per second (10**-9), so a trim value of 1 makes
+ * the system clock step ahead 1 nanosecond more per second than a value
+ * of zero. Xntpd currently uses units of 2**-20 secs for its frequency
+ * offset (drift) values; to convert to a timetrim value, multiply by
+ * 1E9 / 2**20 (about 954).
+ *
+ * "timetrim" with no arguments just prints out the current kernel value.
+ * With a numeric argument, the kernel value is set to the supplied value.
+ * The "-i" flag causes the supplied value to be added to the kernel value.
+ * The "-n" option causes all input and output to be in xntpd units rather
+ * than timetrim native units.
+ *
+ * Note that there is a limit of +-3000000 (0.3%) on the timetrim value
+ * which is (silently?) enforced by the kernel.
+ *
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/syssgi.h>
+
+#define abs(X) (((X) < 0) ? -(X) : (X))
+#define USAGE "usage: timetrim [-n] [[-i] value]\n"
+#define SGITONTP(X) ((double)(X) * 1048576.0/1.0e9)
+#define NTPTOSGI(X) ((LONG)((X) * 1.0e9/1048576.0))
+
+main(argc, argv)
+int argc;
+char **argv;
+{
+ char *rem;
+ int c, incremental = 0, ntpunits = 0;
+ LONG timetrim;
+ double value, strtod();
+
+ while (--argc && **++argv == '-' && isalpha(argv[0][1])) {
+ switch (argv[0][1]) {
+ case 'i':
+ incremental++;
+ break;
+ case 'n':
+ ntpunits++;
+ break;
+ default:
+ fprintf(stderr, USAGE);
+ exit(1);
+ }
+ }
+
+ if (syssgi(SGI_GETTIMETRIM, &timetrim) < 0) {
+ perror("syssgi");
+ exit(2);
+ }
+
+ if (argc == 0) {
+ if (ntpunits)
+ fprintf(stdout, "%0.5lf\n", SGITONTP(timetrim));
+ else
+ fprintf(stdout, "%ld\n", timetrim);
+ } else if (argc != 1) {
+ fprintf(stderr, USAGE);
+ exit(1);
+ } else {
+ value = strtod(argv[0], &rem);
+ if (*rem != '\0') {
+ fprintf(stderr, USAGE);
+ exit(1);
+ }
+ if (ntpunits)
+ value = NTPTOSGI(value);
+ if (incremental)
+ timetrim += value;
+ else
+ timetrim = value;
+ if (syssgi(SGI_SETTIMETRIM, timetrim) < 0) {
+ perror("syssgi");
+ exit(2);
+ }
+ }
+}
diff --git a/usr.sbin/xntpd/xntpd/Makefile b/usr.sbin/xntpd/xntpd/Makefile
new file mode 100644
index 0000000..aee14ea
--- /dev/null
+++ b/usr.sbin/xntpd/xntpd/Makefile
@@ -0,0 +1,48 @@
+#
+# $Id$
+#
+
+CFLAGS+= -I${.CURDIR}/../include
+
+.if exists(${.OBJDIR}/../lib)
+LDADD+= -L${.OBJDIR}/../lib
+DPADD+= ${.OBJDIR}/../lib/libntp.a
+.else
+LDADD+= -L${.CURDIR}/../lib
+DPADD+= ${.CURDIR}/../lib/libntp.a
+.endif
+
+.if exists(${.OBJDIR}/../parse)
+LDADD+= -L${.OBJDIR}/../parse
+DPADD+= ${.OBJDIR}/../parse/libparse.a
+.else
+LDADD+= -L${.CURDIR}/../parse
+DPADD+= ${.CURDIR}/../parse/libparse.a
+.endif
+
+LDADD+= -lntp -lparse -lkvm
+DPADD+= ${LIBKVM}
+
+PROG= xntpd
+MAN8= ${.CURDIR}/../doc/xntpd.8
+CLEANFILES+= .version version.c
+
+SRCS= ntp_config.c ntp_control.c ntp_io.c ntp_leap.c \
+ ntp_loopfilter.c ntp_monitor.c ntp_peer.c ntp_proto.c \
+ ntp_refclock.c ntp_request.c ntp_restrict.c ntp_timer.c \
+ ntp_unixclock.c ntp_util.c ntpd.c ntp_intres.c \
+ ntp_filegen.c version.c
+
+# refclocks
+SRCS+= refclock_acts.c refclock_as2201.c refclock_atom.c refclock_chu.c \
+ refclock_conf.c refclock_datum.c refclock_goes.c refclock_gpstm.c \
+ refclock_heath.c refclock_leitch.c refclock_local.c refclock_moto.c \
+ refclock_msfees.c refclock_mx4200.c refclock_nmea.c refclock_omega.c \
+ refclock_parse.c refclock_pst.c refclock_trak.c refclock_wwvb.c
+
+beforedepend: version.c
+
+version.c: ${.CURDIR}/../VERSION
+ sh -e ${.CURDIR}/../scripts/mkversion xntpd
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/xntpd/xntpd/README b/usr.sbin/xntpd/xntpd/README
new file mode 100644
index 0000000..4551276
--- /dev/null
+++ b/usr.sbin/xntpd/xntpd/README
@@ -0,0 +1,6 @@
+README file for directory ./xntpd of the NTP Version 3 distribution
+
+This directory contains the sources for the xntpd daemon for Unix. See
+the README and RELNOTES files in the parent directory for directions on
+how to make and install this program. The current version number of this
+program is in the version.c file.
diff --git a/usr.sbin/xntpd/xntpd/minpoll b/usr.sbin/xntpd/xntpd/minpoll
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/usr.sbin/xntpd/xntpd/minpoll
diff --git a/usr.sbin/xntpd/xntpd/ntp_config.c b/usr.sbin/xntpd/xntpd/ntp_config.c
new file mode 100644
index 0000000..bc88b5a
--- /dev/null
+++ b/usr.sbin/xntpd/xntpd/ntp_config.c
@@ -0,0 +1,1717 @@
+/*
+ k ntp_config.c - read and apply configuration information
+ */
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <signal.h>
+#include <sys/wait.h>
+#include <sys/time.h>
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_unixtime.h"
+#include "ntp_refclock.h"
+#include "ntp_filegen.h"
+#include "ntp_stdlib.h"
+
+/*
+ * These routines are used to read the configuration file at
+ * startup time. An entry in the file must fit on a single line.
+ * Entries are processed as multiple tokens separated by white space
+ * Lines are considered terminated when a '#' is encountered. Blank
+ * lines are ignored.
+ */
+
+/*
+ * Configuration file name
+ */
+#ifndef CONFIG_FILE
+#if defined(__bsdi__)
+#define CONFIG_FILE "/usr/local/etc/xntp.conf"
+#else
+#define CONFIG_FILE "/etc/ntp.conf"
+#endif
+#endif /* CONFIG_FILE */
+
+/*
+ * We understand the following configuration entries and defaults.
+ *
+ * peer [ addr ] [ version 3 ] [ key 0 ] [ minpoll 6 ] [ maxpoll 10 ]
+ * server [ addr ] [ version 3 ] [ key 0 ] [ minpoll 6 ] [ maxpoll 10 ]
+ * precision -7
+ * broadcast [ addr ] [ version 3 ] [ key 0 ] [ ttl 1 ]
+ * broadcastclient
+ * multicastclient [224.0.1.1]
+ * broadcastdelay 0.0102
+ * authenticate yes|no XXX depredated
+ * monitor yes|no XXX depredated
+ * authdelay 0.00842
+ * restrict [ addr ] [ mask 255.255.255.0 ] ignore|noserve|notrust|noquery
+ * driftfile file_name
+ * keys file_name
+ * statsdir /var/NTP/
+ * filegen peerstats [ file peerstats ] [ type day ] [ link ]
+ * clientlimit [ n ]
+ * clientperiod [ 3600 ]
+ * trustedkey [ key ]
+ * requestkey [ key]
+ * controlkey [ key ]
+ * trap [ addr ]
+ * fudge [ addr ] [ stratum ] [ refid ] ...
+ * pidfile [ ]
+ * setvar [ ]
+ * enable auth|bclient|pll|pps|monitor|stats
+ * disable auth|bclient|pll|pps|monitor|stats
+ * phone ...
+ */
+
+/*
+ * Types of entries we understand.
+ */
+#define CONFIG_UNKNOWN 0
+
+#define CONFIG_PEER 1
+#define CONFIG_SERVER 2
+#define CONFIG_PRECISION 3
+#define CONFIG_DRIFTFILE 4
+#define CONFIG_BROADCAST 5
+#define CONFIG_BROADCASTCLIENT 6
+#define CONFIG_AUTHENTICATE 7
+#define CONFIG_KEYS 8
+#define CONFIG_MONITOR 9
+#define CONFIG_AUTHDELAY 10
+#define CONFIG_RESTRICT 11
+#define CONFIG_BDELAY 12
+#define CONFIG_TRUSTEDKEY 13
+#define CONFIG_REQUESTKEY 14
+#define CONFIG_CONTROLKEY 15
+#define CONFIG_TRAP 16
+#define CONFIG_FUDGE 17
+#define CONFIG_RESOLVER 18
+#define CONFIG_STATSDIR 19
+#define CONFIG_FILEGEN 20
+#define CONFIG_STATISTICS 21
+#define CONFIG_PIDFILE 22
+#define CONFIG_SETVAR 23
+#define CONFIG_CLIENTLIMIT 24
+#define CONFIG_CLIENTPERIOD 25
+#define CONFIG_MULTICASTCLIENT 26
+#define CONFIG_ENABLE 27
+#define CONFIG_DISABLE 28
+#define CONFIG_PHONE 29
+
+#define CONF_MOD_VERSION 1
+#define CONF_MOD_KEY 2
+#define CONF_MOD_MINPOLL 3
+#define CONF_MOD_MAXPOLL 4
+#define CONF_MOD_PREFER 5
+#define CONF_MOD_TTL 6
+#define CONF_MOD_MODE 7
+
+#define CONF_RES_MASK 1
+#define CONF_RES_IGNORE 2
+#define CONF_RES_NOSERVE 3
+#define CONF_RES_NOTRUST 4
+#define CONF_RES_NOQUERY 5
+#define CONF_RES_NOMODIFY 6
+#define CONF_RES_NOPEER 7
+#define CONF_RES_NOTRAP 8
+#define CONF_RES_LPTRAP 9
+#define CONF_RES_NTPPORT 10
+#define CONF_RES_LIMITED 11
+
+#define CONF_TRAP_PORT 1
+#define CONF_TRAP_INTERFACE 2
+
+#define CONF_FDG_TIME1 1
+#define CONF_FDG_TIME2 2
+#define CONF_FDG_STRATUM 3
+#define CONF_FDG_REFID 4
+#define CONF_FDG_FLAG1 5
+#define CONF_FDG_FLAG2 6
+#define CONF_FDG_FLAG3 7
+#define CONF_FDG_FLAG4 8
+
+#define CONF_FGEN_FILE 1
+#define CONF_FGEN_TYPE 2
+#define CONF_FGEN_FLAG_LINK 3
+#define CONF_FGEN_FLAG_NOLINK 4
+#define CONF_FGEN_FLAG_ENABLE 5
+#define CONF_FGEN_FLAG_DISABLE 6
+
+/*
+ * Translation table - keywords to function index
+ */
+struct keyword {
+ char *text;
+ int keytype;
+};
+
+/*
+ * Command keywords
+ */
+static struct keyword keywords[] = {
+ { "peer", CONFIG_PEER },
+ { "server", CONFIG_SERVER },
+ { "precision", CONFIG_PRECISION },
+ { "driftfile", CONFIG_DRIFTFILE },
+ { "broadcast", CONFIG_BROADCAST },
+ { "broadcastclient", CONFIG_BROADCASTCLIENT },
+ { "multicastclient", CONFIG_MULTICASTCLIENT },
+ { "authenticate", CONFIG_AUTHENTICATE },
+ { "keys", CONFIG_KEYS },
+ { "monitor", CONFIG_MONITOR },
+ { "authdelay", CONFIG_AUTHDELAY },
+ { "restrict", CONFIG_RESTRICT },
+ { "broadcastdelay", CONFIG_BDELAY },
+ { "trustedkey", CONFIG_TRUSTEDKEY },
+ { "requestkey", CONFIG_REQUESTKEY },
+ { "controlkey", CONFIG_CONTROLKEY },
+ { "trap", CONFIG_TRAP },
+ { "fudge", CONFIG_FUDGE },
+ { "statsdir", CONFIG_STATSDIR },
+ { "filegen", CONFIG_FILEGEN },
+ { "statistics", CONFIG_STATISTICS },
+ { "pidfile", CONFIG_PIDFILE },
+ { "setvar", CONFIG_SETVAR },
+ { "clientlimit", CONFIG_CLIENTLIMIT },
+ { "clientperiod", CONFIG_CLIENTPERIOD },
+ { "enable", CONFIG_ENABLE },
+ { "disable", CONFIG_DISABLE },
+ { "phone", CONFIG_PHONE },
+ { "", CONFIG_UNKNOWN }
+};
+
+/*
+ * "peer", "server", "broadcast" modifier keywords
+ */
+static struct keyword mod_keywords[] = {
+ { "version", CONF_MOD_VERSION },
+ { "key", CONF_MOD_KEY },
+ { "minpoll", CONF_MOD_MINPOLL },
+ { "maxpoll", CONF_MOD_MAXPOLL },
+ { "prefer", CONF_MOD_PREFER },
+ { "mode", CONF_MOD_MODE }, /* reference clocks */
+ { "ttl", CONF_MOD_TTL }, /* NTP peers */
+ { "", CONFIG_UNKNOWN }
+};
+
+/*
+ * "restrict" modifier keywords
+ */
+static struct keyword res_keywords[] = {
+ { "mask", CONF_RES_MASK },
+ { "ignore", CONF_RES_IGNORE },
+ { "noserve", CONF_RES_NOSERVE },
+ { "notrust", CONF_RES_NOTRUST },
+ { "noquery", CONF_RES_NOQUERY },
+ { "nomodify", CONF_RES_NOMODIFY },
+ { "nopeer", CONF_RES_NOPEER },
+ { "notrap", CONF_RES_NOTRAP },
+ { "lowpriotrap", CONF_RES_LPTRAP },
+ { "ntpport", CONF_RES_NTPPORT },
+ { "limited", CONF_RES_LIMITED },
+ { "", CONFIG_UNKNOWN }
+};
+
+/*
+ * "trap" modifier keywords
+ */
+static struct keyword trap_keywords[] = {
+ { "port", CONF_TRAP_PORT },
+ { "interface", CONF_TRAP_INTERFACE },
+ { "", CONFIG_UNKNOWN }
+};
+
+
+/*
+ * "fudge" modifier keywords
+ */
+static struct keyword fudge_keywords[] = {
+ { "time1", CONF_FDG_TIME1 },
+ { "time2", CONF_FDG_TIME2 },
+ { "stratum", CONF_FDG_STRATUM },
+ { "refid", CONF_FDG_REFID },
+ { "flag1", CONF_FDG_FLAG1 },
+ { "flag2", CONF_FDG_FLAG2 },
+ { "flag3", CONF_FDG_FLAG3 },
+ { "flag4", CONF_FDG_FLAG4 },
+ { "", CONFIG_UNKNOWN }
+};
+
+
+/*
+ * "filegen" modifier keywords
+ */
+static struct keyword filegen_keywords[] = {
+ { "file", CONF_FGEN_FILE },
+ { "type", CONF_FGEN_TYPE },
+ { "link", CONF_FGEN_FLAG_LINK },
+ { "nolink", CONF_FGEN_FLAG_NOLINK },
+ { "enable", CONF_FGEN_FLAG_ENABLE },
+ { "disable", CONF_FGEN_FLAG_DISABLE },
+ { "", CONFIG_UNKNOWN }
+};
+
+/*
+ * "type" modifier keywords
+ */
+static struct keyword fgen_types[] = {
+ { "none", FILEGEN_NONE },
+ { "pid", FILEGEN_PID },
+ { "day", FILEGEN_DAY },
+ { "week", FILEGEN_WEEK },
+ { "month", FILEGEN_MONTH },
+ { "year", FILEGEN_YEAR },
+ { "age", FILEGEN_AGE },
+ { "", CONFIG_UNKNOWN}
+};
+
+/*
+ * "enable", "disable" modifier keywords
+ */
+static struct keyword flags_keywords[] = {
+ { "auth", PROTO_AUTHENTICATE },
+ { "bclient", PROTO_BROADCLIENT },
+ { "pll", PROTO_PLL },
+ { "pps", PROTO_PPS },
+ { "monitor", PROTO_MONITOR },
+ { "stats", PROTO_FILEGEN },
+ { "", CONFIG_UNKNOWN }
+};
+
+/*
+ * Limits on things
+ */
+#define MAXTOKENS 20 /* 20 tokens on line */
+#define MAXLINE 1024 /* maximum length of line */
+#define MAXPHONE 5 /* maximum number of phone strings */
+#define MAXFILENAME 128 /* maximum length of a file name (alloca()?) */
+
+
+/*
+ * Miscellaneous macros
+ */
+#define STRSAME(s1, s2) (*(s1) == *(s2) && strcmp((s1), (s2)) == 0)
+#define ISEOL(c) ((c) == '#' || (c) == '\n' || (c) == '\0')
+#define ISSPACE(c) ((c) == ' ' || (c) == '\t')
+#define STREQ(a, b) (*(a) == *(b) && strcmp((a), (b)) == 0)
+
+/*
+ * File descriptor used by the resolver save routines, and temporary file
+ * name.
+ */
+static FILE *res_fp;
+static char res_file[20]; /* enough for /tmp/xntpXXXXXX\0 */
+#define RES_TEMPFILE "/tmp/xntpXXXXXX"
+
+/*
+ * Definitions of things either imported from or exported to outside
+ */
+#ifdef DEBUG
+extern int debug;
+#endif
+extern char *FindConfig();
+ char *progname;
+ char sys_phone[MAXPHONE][MAXDIAL]; /* ACTS phone numbers */
+static char *xntp_options = "abc:dD:e:f:k:l:mp:r:s:t:v:V:";
+
+/*
+ * Function prototypes
+ */
+static int gettokens P((FILE *, char *, char **, int *));
+static int matchkey P((char *, struct keyword *));
+static int getnetnum P((char *, struct sockaddr_in *, int));
+static void save_resolve P((char *, int, int, int, int, int, int, u_long));
+static void do_resolve_internal P((void));
+static void abort_resolve P((void));
+static RETSIGTYPE catchchild P((int));
+
+/*
+ * getstartup - search through the options looking for a debugging flag
+ */
+void
+getstartup(argc, argv)
+ int argc;
+ char *argv[];
+{
+#ifdef DEBUG
+ int errflg;
+ int c;
+ extern int ntp_optind;
+ extern char *ntp_optarg;
+
+ debug = 0; /* no debugging by default */
+
+ /*
+ * This is a big hack. We don't really want to read command line
+ * configuration until everything else is initialized, since
+ * the ability to configure the system may depend on storage
+ * and the like having been initialized. Except that we also
+ * don't want to initialize anything until after detaching from
+ * the terminal, but we won't know to do that until we've
+ * parsed the command line. Do that now, crudely, and do it
+ * again later. Our ntp_getopt() is explicitly reusable, by the
+ * way. Your own mileage may vary.
+ */
+ errflg = 0;
+ progname = argv[0];
+
+ /*
+ * Decode argument list
+ */
+ while ((c = ntp_getopt(argc, argv, xntp_options)) != EOF)
+ switch (c) {
+ case 'd':
+ ++debug;
+ break;
+ case 'D':
+ debug = strtol(ntp_optarg, 0, 0);
+ printf("Debug1: %s -> %x = %d\n", ntp_optarg, debug, debug);
+ break;
+ case '?':
+ ++errflg;
+ break;
+ default:
+ break;
+ }
+
+ if (errflg || ntp_optind != argc) {
+ (void) fprintf(stderr, "usage: %s [ -abd ] [ -c config_file ] [ -e encryption delay ]\n", progname);
+ (void) fprintf(stderr, "\t\t[ -f frequency file ] [ -k key file ] [ -l log file ]\n");
+ (void) fprintf(stderr, "\t\t[ -p pid file ] [ -r broadcast delay ] [ -s status directory ]\n");
+ (void) fprintf(stderr, "\t\t[ -t trusted key ] [ -v sys variable ] [ -V default sys variable ]\n");
+ exit(2);
+ }
+ ntp_optind = 0; /* reset ntp_optind to restart ntp_getopt */
+
+ if (debug) {
+#ifdef NTP_POSIX_SOURCE
+ static char buf[BUFSIZ];
+ setvbuf(stdout, buf, _IOLBF, BUFSIZ);
+#else
+ setlinebuf(stdout);
+#endif
+ }
+
+#endif /* DEBUG */
+}
+
+/*
+ * getconfig - get command line options and read the configuration file
+ */
+void
+getconfig(argc, argv)
+ int argc;
+ char *argv[];
+{
+ register int i;
+ int c;
+ int errflg;
+ int peerversion;
+ int minpoll;
+ int maxpoll;
+ int ttl;
+ u_long peerkey;
+ int peerflags;
+ int hmode;
+ struct sockaddr_in peeraddr;
+ struct sockaddr_in maskaddr;
+ FILE *fp;
+ char line[MAXLINE];
+ char *(tokens[MAXTOKENS]);
+ int ntokens;
+ int tok;
+ struct interface *localaddr;
+ char *config_file;
+ struct refclockstat clock;
+ int have_keyfile;
+ char keyfile[MAXFILENAME];
+ extern int ntp_optind;
+ extern char *ntp_optarg;
+ extern char *Version;
+ extern u_long info_auth_keyid;
+ FILEGEN *filegen;
+
+ /*
+ * Initialize, initialize
+ */
+ errflg = 0;
+#ifdef DEBUG
+ debug = 0;
+#endif /* DEBUG */
+ config_file = CONFIG_FILE;
+ progname = argv[0];
+ res_fp = NULL;
+ have_keyfile = 0;
+ memset((char *)sys_phone, 0, sizeof(sys_phone));
+
+ /*
+ * install a non default variable with this daemon version
+ */
+ (void) sprintf(line, "daemon_version=\"%s\"", Version);
+ set_sys_var(line, strlen(line)+1, RO);
+
+ /*
+ * Decode argument list
+ */
+ while ((c = ntp_getopt(argc, argv, xntp_options)) != EOF) {
+ switch (c) {
+ case 'a':
+ proto_config(PROTO_AUTHENTICATE, 1);
+ break;
+
+ case 'b':
+ proto_config(PROTO_BROADCLIENT, 1);
+ break;
+
+ case 'c':
+ config_file = ntp_optarg;
+ break;
+
+ case 'd':
+#ifdef DEBUG
+ debug++;
+#else
+ errflg++;
+#endif /* DEBUG */
+ break;
+ case 'D':
+#ifdef DEBUG
+ debug = strtol(ntp_optarg, 0, 0);
+ printf("Debug2: %s -> %x = %d\n", ntp_optarg, debug, debug);
+#endif /* DEBUG */
+ break;
+
+ case 'e':
+ do {
+ l_fp tmp;
+
+ if (!atolfp(ntp_optarg, &tmp)) {
+ syslog(LOG_ERR,
+ "command line encryption delay value %s undecodable",
+ ntp_optarg);
+ errflg++;
+ } else if (tmp.l_ui != 0) {
+ syslog(LOG_ERR,
+ "command line encryption delay value %s is unlikely",
+ ntp_optarg);
+ errflg++;
+ } else {
+ proto_config(PROTO_AUTHDELAY, tmp.l_f);
+ }
+ } while (0);
+ break;
+
+ case 'f':
+ stats_config(STATS_FREQ_FILE, ntp_optarg);
+ break;
+
+ case 'k':
+ getauthkeys(ntp_optarg);
+ if ((int)strlen(ntp_optarg) >= MAXFILENAME) {
+ syslog(LOG_ERR,
+ "key file name too long (>%d, sigh), no name resolution possible",
+ MAXFILENAME);
+ } else {
+ have_keyfile = 1;
+ (void)strcpy(keyfile, ntp_optarg);
+ }
+ break;
+
+ case 'm':
+ proto_config(PROTO_MULTICAST_ADD, htonl(INADDR_NTP));
+ break;
+
+ case 'p':
+ stats_config(STATS_PID_FILE, ntp_optarg);
+ break;
+
+ case 'r':
+ do {
+ l_fp tmp;
+
+ if (!atolfp(ntp_optarg, &tmp)) {
+ syslog(LOG_ERR,
+ "command line broadcast delay value %s undecodable",
+ ntp_optarg);
+ } else if (tmp.l_ui != 0) {
+ syslog(LOG_ERR,
+ "command line broadcast delay value %s is unlikely",
+ ntp_optarg);
+ } else {
+ proto_config(PROTO_BROADDELAY, tmp.l_f);
+ }
+ } while (0);
+ break;
+
+ case 's':
+ stats_config(STATS_STATSDIR, ntp_optarg);
+ break;
+
+ case 't':
+ do {
+ u_long tkey;
+
+ tkey = atol(ntp_optarg);
+ if (tkey <= 0 || tkey > NTP_MAXKEY) {
+ syslog(LOG_ERR,
+ "command line trusted key %s is unlikely",
+ ntp_optarg);
+ } else {
+ authtrust(tkey, 1);
+ }
+ } while (0);
+ break;
+
+ case 'v':
+ case 'V':
+ set_sys_var(ntp_optarg, strlen(ntp_optarg)+1,
+ RW | ((c == 'V') ? DEF : 0));
+ break;
+
+ default:
+ errflg++;
+ break;
+ }
+ }
+
+ if (errflg || ntp_optind != argc) {
+ (void) fprintf(stderr,
+ "usage: %s [ -bd ] [ -c config_file ]\n", progname);
+ exit(2);
+ }
+
+ if ((fp = fopen(FindConfig(config_file), "r")) == NULL) {
+ /*
+ * Broadcast clients can sometimes run without
+ * a configuration file.
+ */
+ return;
+ }
+
+ while ((tok = gettokens(fp, line, tokens, &ntokens))
+ != CONFIG_UNKNOWN) {
+ switch(tok) {
+ case CONFIG_PEER:
+ case CONFIG_SERVER:
+ case CONFIG_BROADCAST:
+ if (tok == CONFIG_PEER)
+ hmode = MODE_ACTIVE;
+ else if (tok == CONFIG_SERVER)
+ hmode = MODE_CLIENT;
+ else
+ hmode = MODE_BROADCAST;
+
+ if (ntokens < 2) {
+ syslog(LOG_ERR,
+ "No address for %s, line ignored",
+ tokens[0]);
+ break;
+ }
+
+ if (!getnetnum(tokens[1], &peeraddr, 0)) {
+ errflg = -1;
+ } else {
+ errflg = 0;
+
+ if (
+#ifdef REFCLOCK
+ !ISREFCLOCKADR(&peeraddr) &&
+#endif
+ ISBADADR(&peeraddr)) {
+ syslog(LOG_ERR,
+ "attempt to configure invalid address %s",
+ ntoa(&peeraddr));
+ break;
+ }
+ }
+
+ peerversion = NTP_VERSION;
+ minpoll = NTP_MINDPOLL;
+ maxpoll = NTP_MAXDPOLL;
+ peerkey = 0;
+ peerflags = 0;
+ ttl = 0;
+ for (i = 2; i < ntokens; i++)
+ switch (matchkey(tokens[i], mod_keywords)) {
+ case CONF_MOD_VERSION:
+ if (i >= ntokens-1) {
+ syslog(LOG_ERR,
+ "peer/server version requires an argument");
+ errflg = 1;
+ break;
+ }
+ peerversion = atoi(tokens[++i]);
+ if ((u_char)peerversion > NTP_VERSION
+ || (u_char)peerversion < NTP_OLDVERSION) {
+ syslog(LOG_ERR,
+ "inappropriate version number %s, line ignored",
+ tokens[i]);
+ errflg = 1;
+ }
+ break;
+
+ case CONF_MOD_KEY:
+ if (i >= ntokens-1) {
+ syslog(LOG_ERR,
+ "key: argument required");
+ errflg = 1;
+ break;
+ }
+ peerkey = atol(tokens[++i]);
+ peerflags |= FLAG_AUTHENABLE;
+ break;
+
+ case CONF_MOD_MINPOLL:
+ if (i >= ntokens-1) {
+ syslog(LOG_ERR,
+ "minpoll: argument required");
+ errflg = 1;
+ break;
+ }
+ minpoll = atoi(tokens[++i]);
+ if (minpoll < NTP_MINPOLL)
+ minpoll = NTP_MINPOLL;
+ break;
+
+ case CONF_MOD_MAXPOLL:
+ if (i >= ntokens-1) {
+ syslog(LOG_ERR,
+ "maxpoll: argument required"
+);
+ errflg = 1;
+ break;
+ }
+ maxpoll = atoi(tokens[++i]);
+ if (maxpoll > NTP_MAXPOLL)
+ maxpoll = NTP_MAXPOLL;
+ break;
+
+ case CONF_MOD_PREFER:
+ peerflags |= FLAG_PREFER;
+ break;
+
+ case CONF_MOD_TTL:
+ if (i >= ntokens-1) {
+ syslog(LOG_ERR,
+ "ttl: argument required");
+ errflg = 1;
+ break;
+ }
+ ttl = atoi(tokens[++i]);
+ break;
+
+ case CONF_MOD_MODE:
+ if (i >= ntokens-1) {
+ syslog(LOG_ERR,
+ "mode: argument required");
+ errflg = 1;
+ break;
+ }
+ ttl = atoi(tokens[++i]);
+ break;
+
+ case CONFIG_UNKNOWN:
+ errflg = 1;
+ break;
+ }
+ if (minpoll > maxpoll) {
+ syslog(LOG_ERR, "config error: minpoll > maxpoll");
+ errflg = 1;
+ }
+ if (errflg == 0) {
+ if (peer_config(&peeraddr,
+ (struct interface *)0, hmode, peerversion,
+ minpoll, maxpoll, peerflags, ttl, peerkey)
+ == 0) {
+ syslog(LOG_ERR,
+ "configuration of %s failed",
+ ntoa(&peeraddr));
+ }
+ } else if (errflg == -1) {
+ save_resolve(tokens[1], hmode, peerversion,
+ minpoll, maxpoll, peerflags, ttl, peerkey);
+ }
+ break;
+
+ case CONFIG_PRECISION:
+ if (ntokens >= 2) {
+ i = atoi(tokens[1]);
+ if (i >= 0 || i < -25)
+ syslog(LOG_ERR,
+ "unlikely precision %s, line ignored",
+ tokens[1]);
+ else
+ proto_config(PROTO_PRECISION, i);
+ }
+ break;
+
+ case CONFIG_DRIFTFILE:
+ if (ntokens >= 2)
+ stats_config(STATS_FREQ_FILE, tokens[1]);
+ else
+ stats_config(STATS_FREQ_FILE, (char *)0);
+ break;
+
+ case CONFIG_PIDFILE:
+ if (ntokens >= 2)
+ stats_config(STATS_PID_FILE, tokens[1]);
+ else
+ stats_config(STATS_PID_FILE, (char *)0);
+ break;
+
+ case CONFIG_BROADCASTCLIENT:
+ proto_config(PROTO_BROADCLIENT, 1);
+ break;
+
+ case CONFIG_MULTICASTCLIENT:
+ if (ntokens > 1) {
+ for (i = 1; i < ntokens; i++) {
+ if (getnetnum(tokens[i], &peeraddr, 1))
+ proto_config(PROTO_MULTICAST_ADD,
+ peeraddr.sin_addr.s_addr);
+ }
+ } else
+ proto_config(PROTO_MULTICAST_ADD, htonl(INADDR_NTP));
+ break;
+
+ case CONFIG_AUTHENTICATE:
+ errflg = 0;
+ if (ntokens >= 2) {
+ if (STREQ(tokens[1], "yes"))
+ proto_config(PROTO_AUTHENTICATE, 1);
+ else if (STREQ(tokens[1], "no"))
+ proto_config(PROTO_AUTHENTICATE, 0);
+ else
+ errflg++;
+ } else {
+ errflg++;
+ }
+
+ if (errflg)
+ syslog(LOG_ERR,
+ "should be `authenticate yes|no'");
+ break;
+
+ case CONFIG_KEYS:
+ if (ntokens >= 2) {
+ getauthkeys(tokens[1]);
+ if ((int)strlen(tokens[1]) >= MAXFILENAME) {
+ syslog(LOG_ERR,
+ "key file name too long (>%d, sigh), no name resolution possible",
+ MAXFILENAME);
+ } else {
+ have_keyfile = 1;
+ (void)strcpy(keyfile, tokens[1]);
+ }
+ }
+ break;
+
+ case CONFIG_MONITOR:
+ errflg = 0;
+ if (ntokens >= 2) {
+ if (STREQ(tokens[1], "yes"))
+ mon_start(MON_ON);
+ else if (STREQ(tokens[1], "no"))
+ mon_stop(MON_ON);
+ else
+ errflg++;
+ } else {
+ errflg++;
+ }
+
+ if (errflg)
+ syslog(LOG_ERR,
+ "should be `monitor yes|no'");
+ break;
+
+ case CONFIG_AUTHDELAY:
+ if (ntokens >= 2) {
+ l_fp tmp;
+
+ if (!atolfp(tokens[1], &tmp)) {
+ syslog(LOG_ERR,
+ "authdelay value %s undecodable",
+ tokens[1]);
+ } else if (tmp.l_ui != 0) {
+ syslog(LOG_ERR,
+ "authdelay value %s is unlikely",
+ tokens[1]);
+ } else {
+ proto_config(PROTO_AUTHDELAY, tmp.l_f);
+ }
+ }
+ break;
+
+ case CONFIG_RESTRICT:
+ if (ntokens < 2) {
+ syslog(LOG_ERR, "restrict requires an address");
+ break;
+ }
+ if (STREQ(tokens[1], "default"))
+ peeraddr.sin_addr.s_addr = htonl(INADDR_ANY);
+ else if (!getnetnum(tokens[1], &peeraddr, 1))
+ break;
+
+ /*
+ * Use peerversion as flags, peerkey as mflags. Ick.
+ */
+ peerversion = 0;
+ peerkey = 0;
+ errflg = 0;
+ maskaddr.sin_addr.s_addr = ~0;
+ for (i = 2; i < ntokens; i++) {
+ switch (matchkey(tokens[i], res_keywords)) {
+ case CONF_RES_MASK:
+ if (i >= ntokens-1) {
+ syslog(LOG_ERR,
+ "mask keyword needs argument");
+ errflg++;
+ break;
+ }
+ i++;
+ if (!getnetnum(tokens[i], &maskaddr, 1))
+ errflg++;
+ break;
+
+ case CONF_RES_IGNORE:
+ peerversion |= RES_IGNORE;
+ break;
+
+ case CONF_RES_NOSERVE:
+ peerversion |= RES_DONTSERVE;
+ break;
+
+ case CONF_RES_NOTRUST:
+ peerversion |= RES_DONTTRUST;
+ break;
+
+ case CONF_RES_NOQUERY:
+ peerversion |= RES_NOQUERY;
+ break;
+
+ case CONF_RES_NOMODIFY:
+ peerversion |= RES_NOMODIFY;
+ break;
+
+ case CONF_RES_NOPEER:
+ peerversion |= RES_NOPEER;
+ break;
+
+ case CONF_RES_NOTRAP:
+ peerversion |= RES_NOTRAP;
+ break;
+
+ case CONF_RES_LPTRAP:
+ peerversion |= RES_LPTRAP;
+ break;
+
+ case CONF_RES_NTPPORT:
+ peerkey |= RESM_NTPONLY;
+ break;
+
+ case CONF_RES_LIMITED:
+ peerversion |= RES_LIMITED;
+ break;
+
+ case CONFIG_UNKNOWN:
+ errflg++;
+ break;
+ }
+ }
+ if (SRCADR(&peeraddr) == htonl(INADDR_ANY))
+ maskaddr.sin_addr.s_addr = 0;
+ if (!errflg)
+ restrict(RESTRICT_FLAGS, &peeraddr, &maskaddr,
+ (int)peerkey, peerversion);
+ break;
+
+ case CONFIG_BDELAY:
+ if (ntokens >= 2) {
+ l_fp tmp;
+
+ if (!atolfp(tokens[1], &tmp)) {
+ syslog(LOG_ERR,
+ "broadcastdelay value %s undecodable",
+ tokens[1]);
+ } else if (tmp.l_ui != 0) {
+ syslog(LOG_ERR,
+ "broadcastdelay value %s is unlikely",
+ tokens[1]);
+ } else {
+ proto_config(PROTO_BROADDELAY, tmp.l_f);
+ }
+ }
+ break;
+
+ case CONFIG_TRUSTEDKEY:
+ for (i = 1; i < ntokens; i++) {
+ u_long tkey;
+
+ tkey = atol(tokens[i]);
+ if (tkey == 0) {
+ syslog(LOG_ERR,
+ "trusted key %s unlikely",
+ tokens[i]);
+ } else {
+ authtrust(tkey, 1);
+ }
+ }
+ break;
+
+ case CONFIG_REQUESTKEY:
+ if (ntokens >= 2) {
+ u_long rkey;
+
+ if (!atouint(tokens[1], &rkey)) {
+ syslog(LOG_ERR,
+ "%s is undecodeable as request key",
+ tokens[1]);
+ } else if (rkey == 0) {
+ syslog(LOG_ERR,
+ "%s makes a poor request keyid",
+ tokens[1]);
+ } else {
+#ifdef DEBUG
+ if (debug > 3)
+ printf(
+ "set info_auth_key to %lu\n", rkey);
+#endif
+ info_auth_keyid = rkey;
+ }
+ }
+ break;
+
+ case CONFIG_CONTROLKEY:
+ if (ntokens >= 2) {
+ u_long ckey;
+ extern u_long ctl_auth_keyid;
+
+ ckey = atol(tokens[1]);
+ if (ckey == 0) {
+ syslog(LOG_ERR,
+ "%s makes a poor control keyid",
+ tokens[1]);
+ } else {
+ ctl_auth_keyid = ckey;
+ }
+ }
+ break;
+
+ case CONFIG_TRAP:
+ if (ntokens < 2) {
+ syslog(LOG_ERR,
+ "no address for trap command, line ignored");
+ break;
+ }
+ if (!getnetnum(tokens[1], &peeraddr, 1))
+ break;
+
+ /*
+ * Use peerversion for port number. Barf.
+ */
+ errflg = 0;
+ peerversion = 0;
+ localaddr = 0;
+ for (i = 2; i < ntokens-1; i++)
+ switch (matchkey(tokens[i], trap_keywords)) {
+ case CONF_TRAP_PORT:
+ if (i >= ntokens-1) {
+ syslog(LOG_ERR,
+ "trap port requires an argument");
+ errflg = 1;
+ break;
+ }
+ peerversion = atoi(tokens[++i]);
+ if (peerversion <= 0
+ || peerversion > 32767) {
+ syslog(LOG_ERR,
+ "invalid port number %s, trap ignored",
+ tokens[i]);
+ errflg = 1;
+ }
+ break;
+
+ case CONF_TRAP_INTERFACE:
+ if (i >= ntokens-1) {
+ syslog(LOG_ERR,
+ "trap interface requires an argument");
+ errflg = 1;
+ break;
+ }
+
+ if (!getnetnum(tokens[++i],
+ &maskaddr, 1)) {
+ errflg = 1;
+ break;
+ }
+
+ localaddr = findinterface(&maskaddr);
+ if (localaddr == NULL) {
+ syslog(LOG_ERR,
+ "can't find interface with address %s",
+ ntoa(&maskaddr));
+ errflg = 1;
+ }
+ break;
+
+ case CONFIG_UNKNOWN:
+ errflg++;
+ break;
+ }
+
+ if (!errflg) {
+ extern struct interface *any_interface;
+
+ if (peerversion != 0)
+ peeraddr.sin_port = htons(peerversion);
+ else
+ peeraddr.sin_port = htons(TRAPPORT);
+ if (localaddr == NULL)
+ localaddr = any_interface;
+ if (!ctlsettrap(&peeraddr, localaddr, 0,
+ NTP_VERSION))
+ syslog(LOG_ERR,
+ "can't set trap for %s, no resources",
+ ntoa(&peeraddr));
+ }
+ break;
+
+ case CONFIG_FUDGE:
+ if (ntokens < 2) {
+ syslog(LOG_ERR,
+ "no address for fudge command, line ignored");
+ break;
+ }
+ if (!getnetnum(tokens[1], &peeraddr, 1))
+ break;
+
+ if (!ISREFCLOCKADR(&peeraddr)) {
+ syslog(LOG_ERR,
+ "%s is inappropriate address for the fudge command, line ignored",
+ ntoa(&peeraddr));
+ break;
+ }
+
+ memset((char *)&clock, 0, sizeof clock);
+ errflg = 0;
+ for (i = 2; i < ntokens-1; i++) {
+ switch (c = matchkey(tokens[i],
+ fudge_keywords)) {
+ case CONF_FDG_TIME1:
+ if (!atolfp(tokens[++i],
+ &clock.fudgetime1)) {
+ syslog(LOG_ERR,
+ "fudge %s time1 value in error",
+ ntoa(&peeraddr));
+ errflg = i;
+ break;
+ }
+ clock.haveflags |= CLK_HAVETIME1;
+ break;
+
+ case CONF_FDG_TIME2:
+ if (!atolfp(tokens[++i],
+ &clock.fudgetime2)) {
+ syslog(LOG_ERR,
+ "fudge %s time2 value in error",
+ ntoa(&peeraddr));
+ errflg = i;
+ break;
+ }
+ clock.haveflags |= CLK_HAVETIME2;
+ break;
+
+ case CONF_FDG_STRATUM:
+ if (!atoint(tokens[++i],
+ (long *)&clock.fudgeval1)) {
+ syslog(LOG_ERR,
+ "fudge %s stratum value in error",
+ ntoa(&peeraddr));
+ errflg = i;
+ break;
+ }
+ clock.haveflags |= CLK_HAVEVAL1;
+ break;
+
+ case CONF_FDG_REFID:
+ strncpy((char *)&clock.fudgeval2,
+ tokens[++i], 4);
+ clock.haveflags |= CLK_HAVEVAL2;
+ break;
+
+ case CONF_FDG_FLAG1:
+ case CONF_FDG_FLAG2:
+ case CONF_FDG_FLAG3:
+ case CONF_FDG_FLAG4:
+ if (!atouint(tokens[++i], &peerkey)
+ || peerkey > 1) {
+ syslog(LOG_ERR,
+ "fudge %s flag value in error",
+ ntoa(&peeraddr));
+ errflg = i;
+ break;
+ }
+ switch(c) {
+ case CONF_FDG_FLAG1:
+ c = CLK_FLAG1;
+ clock.haveflags|=CLK_HAVEFLAG1;
+ break;
+ case CONF_FDG_FLAG2:
+ c = CLK_FLAG2;
+ clock.haveflags|=CLK_HAVEFLAG2;
+ break;
+ case CONF_FDG_FLAG3:
+ c = CLK_FLAG3;
+ clock.haveflags|=CLK_HAVEFLAG3;
+ break;
+ case CONF_FDG_FLAG4:
+ c = CLK_FLAG4;
+ clock.haveflags|=CLK_HAVEFLAG4;
+ break;
+ }
+ if (peerkey == 0)
+ clock.flags &= ~c;
+ else
+ clock.flags |= c;
+ break;
+
+ case CONFIG_UNKNOWN:
+ errflg = -1;
+ break;
+ }
+ }
+
+#ifdef REFCLOCK
+ /*
+ * If reference clock support isn't defined the
+ * fudge line will still be accepted and syntax
+ * checked, but will essentially do nothing.
+ */
+ if (!errflg) {
+ refclock_control(&peeraddr, &clock,
+ (struct refclockstat *)0);
+ }
+#endif
+ break;
+
+ case CONFIG_STATSDIR:
+ if (ntokens >= 2) {
+ stats_config(STATS_STATSDIR,tokens[1]);
+ }
+ break;
+
+ case CONFIG_STATISTICS:
+ for (i = 1; i < ntokens; i++) {
+ filegen = filegen_get(tokens[i]);
+
+ if (filegen == NULL) {
+ syslog(LOG_ERR,
+ "no statistics named %s available",
+ tokens[i]);
+ continue;
+ }
+#ifdef DEBUG
+ if (debug > 3)
+ printf("enabling filegen for %s statistics \"%s%s\"\n",
+ tokens[i], filegen->prefix, filegen->basename);
+#endif
+ filegen->flag |= FGEN_FLAG_ENABLED;
+ }
+ break;
+
+ case CONFIG_FILEGEN:
+ if (ntokens < 2) {
+ syslog(LOG_ERR,
+ "no id for filegen command, line ignored");
+ break;
+ }
+
+ filegen = filegen_get(tokens[1]);
+ if (filegen == NULL) {
+ syslog(LOG_ERR,
+ "unknown filegen \"%s\" ignored",
+ tokens[1]);
+ break;
+ }
+ /*
+ * peerversion is (ab)used for filegen file (index)
+ * peerkey is (ab)used for filegen type
+ * peerflags is (ab)used for filegen flags
+ */
+ peerversion = 0;
+ peerkey = filegen->type;
+ peerflags = filegen->flag;
+ errflg = 0;
+
+ for (i = 2; i < ntokens; i++) {
+ switch (matchkey(tokens[i], filegen_keywords)) {
+ case CONF_FGEN_FILE:
+ if (i >= ntokens - 1) {
+ syslog(LOG_ERR,
+ "filegen %s file requires argument",
+ tokens[1]);
+ errflg = i;
+ break;
+ }
+ peerversion = ++i;
+ break;
+ case CONF_FGEN_TYPE:
+ if (i >= ntokens -1) {
+ syslog(LOG_ERR,
+ "filegen %s type requires argument",
+ tokens[1]);
+ errflg = i;
+ break;
+ }
+ peerkey = matchkey(tokens[++i], fgen_types);
+ if (peerkey == CONFIG_UNKNOWN) {
+ syslog(LOG_ERR,
+ "filegen %s unknown type \"%s\"",
+ tokens[1], tokens[i]);
+ errflg = i;
+ break;
+ }
+ break;
+
+ case CONF_FGEN_FLAG_LINK:
+ peerflags |= FGEN_FLAG_LINK;
+ break;
+
+ case CONF_FGEN_FLAG_NOLINK:
+ peerflags &= ~FGEN_FLAG_LINK;
+ break;
+
+ case CONF_FGEN_FLAG_ENABLE:
+ peerflags |= FGEN_FLAG_ENABLED;
+ break;
+
+ case CONF_FGEN_FLAG_DISABLE:
+ peerflags &= ~FGEN_FLAG_ENABLED;
+ break;
+ }
+ }
+ if (!errflg) {
+ filegen_config(filegen, tokens[peerversion],
+ (u_char)peerkey, (u_char)peerflags);
+ }
+ break;
+
+ case CONFIG_SETVAR:
+ if (ntokens < 2) {
+ syslog(LOG_ERR,
+ "no value for setvar command - line ignored");
+ } else {
+ set_sys_var(tokens[1], strlen(tokens[1])+1, RW |
+ ((((ntokens > 2) && !strcmp(tokens[2],
+ "default"))) ? DEF : 0));
+ }
+ break;
+
+ case CONFIG_CLIENTLIMIT:
+ if (ntokens < 2) {
+ syslog(LOG_ERR,
+ "no value for clientlimit command - line ignored");
+ } else {
+ u_long i;
+ if (!atouint(tokens[1], &i) || !i) {
+ syslog(LOG_ERR,
+ "illegal value for clientlimit command - line ignored");
+ } else {
+ extern u_long client_limit;
+ char bp[80];
+
+#ifdef DEBUG
+ if (debug)
+ sprintf(bp, "client_limit=%lu", i);
+#endif
+ set_sys_var(bp, strlen(bp)+1, RO);
+ client_limit = i;
+ }
+ }
+ break;
+
+ case CONFIG_CLIENTPERIOD:
+ if (ntokens < 2) {
+ syslog(LOG_ERR,
+ "no value for clientperiod command - line ignored");
+ } else {
+ u_long i;
+
+ if (!atouint(tokens[1], &i) || i < 64) {
+ syslog(LOG_ERR,
+ "illegal value for clientperiod command - line ignored");
+ } else {
+ extern u_long client_limit_period;
+ char bp[80];
+
+ sprintf(bp, "client_limit_period=%ld", i);
+ set_sys_var(bp, strlen(bp)+1, RO);
+ client_limit_period = i;
+ }
+ }
+ break;
+
+ case CONFIG_ENABLE:
+ for (i = 1; i < ntokens; i++) {
+ int flag;
+
+ flag = matchkey(tokens[i], flags_keywords);
+ if (flag == CONFIG_UNKNOWN) {
+ syslog(LOG_ERR,
+ "enable unknown flag %s",
+ tokens[i]);
+ errflg = 1;
+ break;
+ }
+ proto_config(flag, 1L);
+ }
+ break;
+
+ case CONFIG_DISABLE:
+ for (i = 1; i < ntokens; i++) {
+ int flag;
+
+ flag = matchkey(tokens[i], flags_keywords);
+ if (flag == CONFIG_UNKNOWN) {
+ syslog(LOG_ERR,
+ "disable unknown flag %s",
+ tokens[i]);
+ errflg = 1;
+ break;
+ }
+ proto_config(flag, 0L);
+ }
+ break;
+
+ case CONFIG_PHONE:
+ for (i = 1; i < ntokens && i < MAXPHONE; i++) {
+ (void)strncpy(sys_phone[i - 1],
+ tokens[i], MAXDIAL);
+ }
+ sys_phone[i - 1][0] = '\0';
+ break;
+ }
+ }
+ (void) fclose(fp);
+
+ if (res_fp != NULL) {
+ /*
+ * Need name resolution
+ */
+ do_resolve_internal();
+ }
+}
+
+
+
+/*
+ * gettokens - read a line and return tokens
+ */
+static int
+gettokens(fp, line, tokenlist, ntokens)
+ FILE *fp;
+ char *line;
+ char **tokenlist;
+ int *ntokens;
+{
+ register char *cp;
+ register int eol;
+ register int ntok;
+ register int quoted = 0;
+
+ /*
+ * Find start of first token
+ */
+again:
+ while ((cp = fgets(line, MAXLINE, fp)) != NULL) {
+ cp = line;
+ while (ISSPACE(*cp))
+ cp++;
+ if (!ISEOL(*cp))
+ break;
+ }
+ if (cp == NULL) {
+ *ntokens = 0;
+ return CONFIG_UNKNOWN; /* hack. Is recognized as EOF */
+ }
+
+ /*
+ * Now separate out the tokens
+ */
+ eol = 0;
+ ntok = 0;
+ while (!eol) {
+ tokenlist[ntok++] = cp;
+ while (!ISEOL(*cp) && (!ISSPACE(*cp) || quoted))
+ quoted ^= (*cp++ == '"');
+
+ if (ISEOL(*cp)) {
+ *cp = '\0';
+ eol = 1;
+ } else { /* must be space */
+ *cp++ = '\0';
+ while (ISSPACE(*cp))
+ cp++;
+ if (ISEOL(*cp))
+ eol = 1;
+ }
+ if (ntok == MAXTOKENS)
+ eol = 1;
+ }
+
+ /*
+ * Return the match
+ */
+ *ntokens = ntok;
+ ntok = matchkey(tokenlist[0], keywords);
+ if (ntok == CONFIG_UNKNOWN)
+ goto again;
+ return ntok;
+}
+
+
+
+/*
+ * matchkey - match a keyword to a list
+ */
+static int
+matchkey(word, keys)
+ register char *word;
+ register struct keyword *keys;
+{
+ for (;;) {
+ if (keys->keytype == CONFIG_UNKNOWN) {
+ syslog(LOG_ERR,
+ "configure: keyword \"%s\" unknown, line ignored",
+ word);
+ return CONFIG_UNKNOWN;
+ }
+ if (STRSAME(word, keys->text))
+ return keys->keytype;
+ keys++;
+ }
+}
+
+
+/*
+ * getnetnum - return a net number (this is crude, but careful)
+ */
+static int
+getnetnum(num, addr, complain)
+ char *num;
+ struct sockaddr_in *addr;
+ int complain;
+{
+ register char *cp;
+ register char *bp;
+ register int i;
+ register int temp;
+ char buf[80]; /* will core dump on really stupid stuff */
+ u_long netnum;
+
+/* XXX ELIMINATE replace with decodenetnum */
+ cp = num;
+ netnum = 0;
+ for (i = 0; i < 4; i++) {
+ bp = buf;
+ while (isdigit(*cp))
+ *bp++ = *cp++;
+ if (bp == buf)
+ break;
+
+ if (i < 3) {
+ if (*cp++ != '.')
+ break;
+ } else if (*cp != '\0')
+ break;
+
+ *bp = '\0';
+ temp = atoi(buf);
+ if (temp > 255)
+ break;
+ netnum <<= 8;
+ netnum += temp;
+#ifdef DEBUG
+ if (debug > 3)
+ printf("getnetnum %s step %d buf %s temp %d netnum %lu\n",
+ num, i, buf, temp, netnum);
+#endif
+ }
+
+ if (i < 4) {
+ if (complain)
+ syslog(LOG_ERR,
+ "configure: \"%s\" not valid host number, line ignored",
+ num);
+#ifdef DEBUG
+ if (debug > 3)
+ printf(
+ "configure: \"%s\" not valid host number, line ignored\n",
+ num);
+#endif
+ return 0;
+ }
+
+ /*
+ * make up socket address. Clear it out for neatness.
+ */
+ memset((char *)addr, 0, sizeof(struct sockaddr_in));
+ addr->sin_family = AF_INET;
+ addr->sin_port = htons(NTP_PORT);
+ addr->sin_addr.s_addr = htonl(netnum);
+#ifdef DEBUG
+ if (debug > 1)
+ printf("getnetnum given %s, got %s (%lx)\n",
+ num, ntoa(addr), netnum);
+#endif
+ return 1;
+}
+
+
+/*
+ * catchchild - receive the resolver's exit status
+ */
+static RETSIGTYPE
+catchchild(sig)
+int sig;
+{
+ /*
+ * We only start up one child, and if we're here
+ * it should have already exited. Hence the following
+ * shouldn't hang. If it does, please tell me.
+ */
+ (void) wait(0);
+}
+
+
+/*
+ * save_resolve - save configuration info into a file for later name resolution
+ */
+static void
+save_resolve(name, mode, version, minpoll, maxpoll, flags, ttl, keyid)
+ char *name;
+ int mode;
+ int version;
+ int minpoll;
+ int maxpoll;
+ int flags;
+ int ttl;
+ u_long keyid;
+{
+ if (res_fp == NULL) {
+ (void) strcpy(res_file, RES_TEMPFILE);
+ (void) mktemp(res_file);
+ res_fp = fopen(res_file, "w");
+ if (res_fp == NULL) {
+ syslog(LOG_ERR, "open failed for %s: %m", res_file);
+ return;
+ }
+ }
+
+#ifdef DEBUG
+ if (debug) {
+ printf("resolving %s\n", name);
+ }
+#endif
+
+ (void) fprintf(res_fp, "%s %d %d %d %d %d %d %lu\n", name, mode,
+ version, minpoll, maxpoll, flags, ttl, keyid);
+}
+
+
+/*
+ * abort_resolve - terminate the resolver stuff and delete the file
+ */
+static void
+abort_resolve()
+{
+ /*
+ * In an ideal world we would might reread the file and
+ * log the hosts which aren't getting configured. Since
+ * this is too much work, however, just close and delete
+ * the temp file.
+ */
+ if (res_fp != NULL)
+ (void) fclose(res_fp);
+ res_fp = NULL;
+
+ (void) unlink(res_file);
+}
+
+
+#define KEY_TYPE_MD5 4
+
+/*
+ * do_resolve_internal - start up the resolver function (not program)
+ */
+static void
+do_resolve_internal()
+{
+ int i;
+
+ extern u_long req_keyid; /* request keyid */
+ extern char *req_file; /* name of the file with config info */
+ extern u_long info_auth_keyid;
+
+ if (res_fp == NULL) {
+ /* belch */
+ syslog(LOG_ERR,
+ "internal error in do_resolve_internal: res_fp == NULL");
+ exit(1);
+ }
+
+ /* we are done with this now */
+ (void) fclose(res_fp);
+ res_fp = NULL;
+
+ /* find a keyid */
+ if (info_auth_keyid == 0)
+ req_keyid = 65535;
+ else
+ req_keyid = info_auth_keyid;
+
+ /* if doesn't exist, make up one at random */
+ if (!authhavekey(req_keyid)) {
+ char rankey[9];
+ struct timeval now;
+
+ /* generate random key */
+ GETTIMEOFDAY(&now, (struct timezone *)0);
+ srand(now.tv_sec * now.tv_usec);
+ for (i = 0; i < 8; i++)
+ rankey[i] = (rand() % 255) + 1;
+ rankey[8] = 0;
+ authusekey(req_keyid, KEY_TYPE_MD5, rankey);
+ }
+
+ /* save keyid so we will accept config requests with it */
+ info_auth_keyid = req_keyid;
+ req_file = res_file; /* set up pointer to res file */
+ (void) signal_no_reset(SIGCHLD, catchchild);
+
+ i = fork();
+ if (i == 0) {
+ /*
+ * this used to close everything
+ * I don't think this is necessary
+ */
+ (void) signal_no_reset(SIGCHLD, SIG_DFL);
+ ntp_intres();
+
+ /*
+ * If we got here, the intres code screwed up.
+ * Print something so we don't die without complaint
+ */
+ syslog(LOG_ERR, "call to ntp_intres lost");
+ abort_resolve();
+ exit(1);
+ }
+ if (i == -1) {
+ syslog(LOG_ERR, "fork() failed, can't start ntp_intres");
+ (void) signal_no_reset(SIGCHLD, SIG_DFL);
+ abort_resolve();
+ }
+}
diff --git a/usr.sbin/xntpd/xntpd/ntp_control.c b/usr.sbin/xntpd/xntpd/ntp_control.c
new file mode 100644
index 0000000..af4365c
--- /dev/null
+++ b/usr.sbin/xntpd/xntpd/ntp_control.c
@@ -0,0 +1,2690 @@
+/*
+ * ntp_control.c - respond to control messages and send async traps
+ */
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <signal.h>
+#include <sys/time.h>
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_control.h"
+#include "ntp_stdlib.h"
+
+/*
+ * Structure to hold request procedure information
+ */
+#define NOAUTH 0
+#define AUTH 1
+
+#define NO_REQUEST (-1)
+
+struct ctl_proc {
+ short control_code; /* defined request code */
+ u_short flags; /* flags word */
+ void (*handler)(); /* routine to handle request */
+};
+
+/*
+ * Only one flag. Authentication required or not.
+ */
+#define NOAUTH 0
+#define AUTH 1
+
+/*
+ * Request processing routines
+ */
+static void ctl_error P((int));
+static u_short ctlclkstatus P((struct refclockstat *));
+static void ctl_flushpkt P((int));
+static void ctl_putdata P((char *, int, int));
+static void ctl_putstr P((char *, char *, int));
+static void ctl_putlfp P((char *, l_fp *));
+static void ctl_putfp P((char *, s_fp));
+static void ctl_putufp P((char *, u_fp));
+static void ctl_putuint P((char *, u_long));
+static void ctl_puthex P((char *, u_long));
+static void ctl_putint P((char *, long));
+static void ctl_putts P((char *, l_fp *));
+static void ctl_putadr P((char *, u_long));
+static void ctl_putid P((char *, char *));
+static void ctl_putarray P((char *, s_fp *, int));
+static void ctl_putsys P((int));
+static void ctl_putpeer P((int, struct peer *));
+#ifdef REFCLOCK
+static void ctl_putclock P((int, struct refclockstat *, int));
+#endif /* REFCLOCK */
+static struct ctl_var *ctl_getitem P((struct ctl_var *, char **));
+static u_long count_var P((struct ctl_var *));
+static void control_unspec P((struct recvbuf *, int));
+static void read_status P((struct recvbuf *, int));
+static void read_variables P((struct recvbuf *, int));
+static void write_variables P((struct recvbuf *, int));
+static void read_clock_status P((struct recvbuf *, int));
+static void write_clock_status P((struct recvbuf *, int));
+static void set_trap P((struct recvbuf *, int));
+static void unset_trap P((struct recvbuf *, int));
+static struct ctl_trap *ctlfindtrap P((struct sockaddr_in *, struct interface *));
+
+static struct ctl_proc control_codes[] = {
+ { CTL_OP_UNSPEC, NOAUTH, control_unspec },
+ { CTL_OP_READSTAT, NOAUTH, read_status },
+ { CTL_OP_READVAR, NOAUTH, read_variables },
+ { CTL_OP_WRITEVAR, AUTH, write_variables },
+ { CTL_OP_READCLOCK, NOAUTH, read_clock_status },
+ { CTL_OP_WRITECLOCK, NOAUTH, write_clock_status },
+ { CTL_OP_SETTRAP, NOAUTH, set_trap },
+ { CTL_OP_UNSETTRAP, NOAUTH, unset_trap },
+ { NO_REQUEST, 0 }
+};
+
+/*
+ * System variable values. The array can be indexed by
+ * the variable index to find the textual name.
+ */
+static struct ctl_var sys_var[] = {
+ { 0, PADDING, "" }, /* 0 */
+ { CS_LEAP, RW, "leap" }, /* 1 */
+ { CS_STRATUM, RO, "stratum" }, /* 2 */
+ { CS_PRECISION, RO, "precision" }, /* 3 */
+ { CS_ROOTDELAY, RO, "rootdelay" }, /* 4 */
+ { CS_ROOTDISPERSION, RO, "rootdispersion" }, /* 5 */
+ { CS_REFID, RO, "refid" }, /* 6 */
+ { CS_REFTIME, RO, "reftime" }, /* 7 */
+ { CS_POLL, RO, "poll" }, /* 8 */
+ { CS_PEERID, RO, "peer" }, /* 9 */
+ { CS_OFFSET, RO, "phase" }, /* 10 */
+ { CS_DRIFT, RO, "freq" }, /* 11 */
+ { CS_COMPLIANCE, RO, "error" }, /* 12 */
+ { CS_CLOCK, RO, "clock" }, /* 13 */
+ { CS_LEAPIND, RW, "leapindicator" }, /* 14 */
+ { CS_LEAPWARNING, RW, "leapwarning" }, /* 15 */
+ { CS_PROCESSOR, RO, "processor" }, /* 16 */
+ { CS_SYSTEM, RO, "system" }, /* 17 */
+ { CS_KEYID, RO, "keyid" }, /* 18 */
+ { CS_REFSKEW, RO, "refskew" }, /* 19 */
+ { CS_VARLIST, RO, "sys_var_list" },/* 20 */
+ { 0, EOV, "" }
+};
+
+static struct ctl_var *ext_sys_var = (struct ctl_var *)0;
+
+/*
+ * System variables we print by default (in fuzzball order, more-or-less)
+ */
+static u_char def_sys_var[] = {
+ CS_SYSTEM,
+ CS_LEAP,
+ CS_STRATUM,
+ CS_ROOTDELAY,
+ CS_ROOTDISPERSION,
+ CS_PEERID,
+ CS_REFID,
+ CS_REFTIME,
+ CS_POLL,
+ CS_CLOCK,
+ CS_OFFSET,
+ CS_DRIFT,
+ CS_COMPLIANCE,
+ 0
+};
+
+
+/*
+ * Peer variable list
+ */
+static struct ctl_var peer_var[] = {
+ { 0, PADDING, "" }, /* 0 */
+ { CP_CONFIG, RO, "config" }, /* 1 */
+ { CP_AUTHENABLE, RO, "authenable" }, /* 2 */
+ { CP_AUTHENTIC, RO, "authentic" }, /* 3 */
+ { CP_SRCADR, RO, "srcadr" }, /* 4 */
+ { CP_SRCPORT, RO, "srcport" }, /* 5 */
+ { CP_DSTADR, RO, "dstadr" }, /* 6 */
+ { CP_DSTPORT, RO, "dstport" }, /* 7 */
+ { CP_LEAP, RO, "leap" }, /* 8 */
+ { CP_HMODE, RO, "hmode" }, /* 9 */
+ { CP_STRATUM, RO, "stratum" }, /* 10 */
+ { CP_PPOLL, RO, "ppoll" }, /* 11 */
+ { CP_HPOLL, RO, "hpoll" }, /* 12 */
+ { CP_PRECISION, RO, "precision" }, /* 13 */
+ { CP_ROOTDELAY, RO, "rootdelay" }, /* 14 */
+ { CP_ROOTDISPERSION, RO, "rootdispersion" }, /* 15 */
+ { CP_REFID, RO, "refid" }, /* 16 */
+ { CP_REFTIME, RO, "reftime" }, /* 17 */
+ { CP_ORG, RO, "org" }, /* 18 */
+ { CP_REC, RO, "rec" }, /* 19 */
+ { CP_XMT, RO, "xmt" }, /* 20 */
+ { CP_REACH, RO, "reach" }, /* 21 */
+ { CP_VALID, RO, "valid" }, /* 22 */
+ { CP_TIMER, RO, "timer" }, /* 23 */
+ { CP_DELAY, RO, "delay" }, /* 24 */
+ { CP_OFFSET, RO, "offset" }, /* 25 */
+ { CP_DISPERSION,RO, "dispersion" }, /* 26 */
+ { CP_KEYID, RO, "keyid" }, /* 27 */
+ { CP_FILTDELAY, RO, "filtdelay" }, /* 28 */
+ { CP_FILTOFFSET, RO, "filtoffset" }, /* 29 */
+ { CP_PMODE, RO, "pmode" }, /* 30 */
+ { CP_RECEIVED, RO, "received" }, /* 31 */
+ { CP_SENT, RO, "sent" }, /* 32 */
+ { CP_FILTERROR, RO, "filterror" }, /* 33 */
+ { CP_FLASH, RO, "flash" }, /* 34 */
+ { CP_DISP, PADDING,"" }, /* 35 */
+ { CP_VARLIST, RO, "peer_var_list" }, /* 36 */
+ { 0, EOV, "" }
+};
+
+
+/*
+ * Peer variables we print by default
+ */
+static u_char def_peer_var[] = {
+ CP_SRCADR,
+ CP_SRCPORT,
+ CP_DSTADR,
+ CP_DSTPORT,
+ CP_KEYID,
+ CP_STRATUM,
+ CP_PRECISION,
+ CP_ROOTDELAY,
+ CP_ROOTDISPERSION,
+ CP_REFID,
+ CP_REFTIME,
+ CP_DELAY,
+ CP_OFFSET,
+ CP_DISPERSION,
+ CP_REACH,
+ CP_VALID,
+ CP_HMODE,
+ CP_PMODE,
+ CP_HPOLL,
+ CP_PPOLL,
+ CP_LEAP,
+ CP_FLASH,
+ CP_ORG,
+ CP_REC,
+ CP_XMT,
+ CP_FILTDELAY,
+ CP_FILTOFFSET,
+ CP_FILTERROR,
+ 0
+};
+
+
+#ifdef REFCLOCK
+/*
+ * Clock variable list
+ */
+static struct ctl_var clock_var[] = {
+ { 0, PADDING, "" }, /* 0 */
+ { CC_TYPE, RO, "type" }, /* 1 */
+ { CC_TIMECODE, RO, "timecode" }, /* 2 */
+ { CC_POLL, RO, "poll" }, /* 3 */
+ { CC_NOREPLY, RO, "noreply" }, /* 4 */
+ { CC_BADFORMAT, RO, "badformat" }, /* 5 */
+ { CC_BADDATA, RO, "baddata" }, /* 6 */
+ { CC_FUDGETIME1, RO, "fudgetime1" }, /* 7 */
+ { CC_FUDGETIME2, RO, "fudgetime2" }, /* 8 */
+ { CC_FUDGEVAL1, RO, "stratum" }, /* 9 */
+ { CC_FUDGEVAL2, RO, "refid" }, /* 10 */
+ { CC_FLAGS, RO, "flags" }, /* 11 */
+ { CC_DEVICE, RO, "device" }, /* 12 */
+ { CC_VARLIST, RO, "clock_var_list" },/* 13 */
+ { 0, EOV, "" }
+};
+
+
+/*
+ * Clock variables printed by default
+ */
+static u_char def_clock_var[] = {
+ CC_DEVICE,
+ CC_TYPE, /* won't be output if device= known */
+ CC_TIMECODE,
+ CC_POLL,
+ CC_NOREPLY,
+ CC_BADFORMAT,
+ CC_BADDATA,
+ CC_FUDGETIME1,
+ CC_FUDGETIME2,
+ CC_FUDGEVAL1,
+ CC_FUDGEVAL2,
+ CC_FLAGS,
+ 0
+};
+#endif
+
+
+/*
+ * System and processor definitions. These will change for the gizmo board.
+ */
+#ifndef HAVE_UNAME
+#ifndef STR_SYSTEM
+#define STR_SYSTEM "UNIX"
+#endif
+#ifndef STR_PROCESSOR
+#define STR_PROCESSOR "unknown"
+#endif
+
+static char str_system[] = STR_SYSTEM;
+static char str_processor[] = STR_PROCESSOR;
+#else
+#include <sys/utsname.h>
+static struct utsname utsname;
+#endif /* HAVE_UNAME */
+
+/*
+ * Trap structures. We only allow a few of these, and send
+ * a copy of each async message to each live one. Traps time
+ * out after an hour, it is up to the trap receipient to
+ * keep resetting it to avoid being timed out.
+ */
+/* ntp_request.c */
+ struct ctl_trap ctl_trap[CTL_MAXTRAPS];
+ int num_ctl_traps;
+
+/*
+ * Type bits, for ctlsettrap() call.
+ */
+#define TRAP_TYPE_CONFIG 0 /* used by configuration code */
+#define TRAP_TYPE_PRIO 1 /* priority trap */
+#define TRAP_TYPE_NONPRIO 2 /* nonpriority trap */
+
+
+/*
+ * List relating reference clock types to control message time sources.
+ * Index by the reference clock type.
+ * This list will only be used iff the reference clock driver doesn't
+ * set peer->sstclktype to something different than CTL_SST_TS_UNSPEC.
+ */
+static u_char clocktypes[] = {
+ CTL_SST_TS_NTP, /* REFCLK_NONE (0) */
+ CTL_SST_TS_LOCAL, /* REFCLK_LOCALCLOCK (1) */
+ CTL_SST_TS_UHF, /* REFCLK_GPS_TRAK (2) */
+ CTL_SST_TS_HF, /* REFCLK_WWV_PST (3) */
+ CTL_SST_TS_LF, /* REFCLK_WWVB_SPECTRACOM (4) */
+ CTL_SST_TS_UHF, /* REFCLK_GOES_TRUETIME (5) */
+ CTL_SST_TS_UHF, /* REFCLK_GOES_TRAK (6) */
+ CTL_SST_TS_HF, /* REFCLK_CHU (7) */
+ CTL_SST_TS_LF, /* REFCLOCK_PARSE - default value - driver supplies actual value in peer->sstclktype */
+ CTL_SST_TS_LF, /* REFCLK_GPS_MX4200 (9) */
+ CTL_SST_TS_UHF, /* REFCLK_GPS_AS2201 (10) */
+ CTL_SST_TS_LF, /* REFCLK_OMEGA_TRUETIME (11) */
+ CTL_SST_TS_UHF, /* REFCLK_IRIG_TPRO (12) */
+ CTL_SST_TS_ATOM, /* REFCLK_ATOM_LEITCH (13) */
+ CTL_SST_TS_LF, /* REFCLK_MSF_EES (14) */
+ CTL_SST_TS_UHF, /* REFCLK_GPSTM_TRUETIME (15) */
+ CTL_SST_TS_UHF, /* REFCLK_IRIG_BANCOMM (16) */
+ CTL_SST_TS_UHF, /* REFCLK_GPS_DATU (17) */
+ CTL_SST_TS_TELEPHONE, /* REFCLK_NIST_ACT (18) */
+ CTL_SST_TS_HF, /* REFCLK_WWV_HEATH (19) */
+ CTL_SST_TS_UHF, /* REFCLK_GPS_NMEA (20) */
+ CTL_SST_TS_UHF, /* REFCLK_GPS_MOTO (21) */
+ CTL_SST_TS_ATOM /* REFCLK_ATOM_PPS (22) */
+};
+
+
+/*
+ * Keyid used for authenticating write requests.
+ */
+u_long ctl_auth_keyid;
+
+/*
+ * We keep track of the last error reported by the system internally
+ */
+static u_char ctl_sys_last_event;
+static u_char ctl_sys_num_events;
+
+
+/*
+ * Statistic counters to keep track of requests and responses.
+ */
+u_long ctltimereset; /* time stats reset */
+u_long numctlreq; /* number of requests we've received */
+u_long numctlbadpkts; /* number of bad control packets */
+u_long numctlresponses; /* number of resp packets sent with data */
+u_long numctlfrags; /* number of fragments sent */
+u_long numctlerrors; /* number of error responses sent */
+u_long numctltooshort; /* number of too short input packets */
+u_long numctlinputresp; /* number of responses on input */
+u_long numctlinputfrag; /* number of fragments on input */
+u_long numctlinputerr; /* number of input pkts with err bit set */
+u_long numctlbadoffset; /* number of input pkts with nonzero offset */
+u_long numctlbadversion; /* number of input pkts with unknown version */
+u_long numctldatatooshort; /* data too short for count */
+u_long numctlbadop; /* bad op code found in packet */
+u_long numasyncmsgs; /* number of async messages we've sent */
+
+/*
+ * Imported from the I/O module
+ */
+extern struct interface *any_interface;
+
+/*
+ * Imported from the main routines
+ */
+extern int debug;
+
+/*
+ * Imported from the timer module
+ */
+extern u_long current_time;
+
+extern struct peer *assoc_hash[];
+extern int pps_control; /* flag for 1-pps signal present */
+/*
+ * Importations from the protocol module
+ */
+extern u_char sys_leap;
+extern u_char sys_stratum;
+extern s_char sys_precision;
+extern s_fp sys_rootdelay;
+extern u_fp sys_rootdispersion;
+extern u_long sys_refid;
+extern l_fp sys_reftime;
+extern l_fp sys_refskew;
+extern u_char sys_poll;
+extern struct peer *sys_peer;
+/*
+ * Imported from the loop filter module
+ */
+extern l_fp last_offset;
+extern s_fp drift_comp;
+extern u_fp sys_maxd;
+extern int pll_control;
+/*
+ * Imported from the leap module
+ */
+extern u_char leap_indicator;
+extern u_char leap_warning;
+
+/*
+ * Response packet used by these routines. Also some state information
+ * so that we can handle packet formatting within a common set of
+ * subroutines. Note we try to enter data in place whenever possible,
+ * but the need to set the more bit correctly means we occasionally
+ * use the extra buffer and copy.
+ */
+static struct ntp_control rpkt;
+static u_char res_version;
+static u_char res_opcode;
+static u_short res_associd;
+static int res_offset;
+static u_char * datapt;
+static u_char * dataend;
+static int datalinelen;
+static int datanotbinflag;
+static struct sockaddr_in *rmt_addr;
+static struct interface *lcl_inter;
+
+static u_char res_authenticate;
+static u_char res_authokay;
+static u_long res_keyid;
+
+#define MAXDATALINELEN (72)
+
+static u_char res_async; /* set to 1 if this is async trap response */
+
+/*
+ * Pointers for saving state when decoding request packets
+ */
+static char *reqpt;
+static char *reqend;
+
+/*
+ * init_control - initialize request data
+ */
+void
+init_control()
+{
+ int i;
+
+#ifdef HAVE_UNAME
+ uname(&utsname);
+#endif /* HAVE_UNAME */
+
+ ctl_clr_stats();
+
+ ctl_auth_keyid = 0;
+ ctl_sys_last_event = EVNT_UNSPEC;
+ ctl_sys_num_events = 0;
+
+ num_ctl_traps = 0;
+ for (i = 0; i < CTL_MAXTRAPS; i++)
+ ctl_trap[i].tr_flags = 0;
+}
+
+
+/*
+ * ctl_error - send an error response for the current request
+ */
+static void
+ctl_error(errcode)
+ int errcode;
+{
+#ifdef DEBUG
+ if (debug >= 4)
+ printf("sending control error %d\n", errcode);
+#endif
+ /*
+ * fill in the fields. We assume rpkt.sequence and rpkt.associd
+ * have already been filled in.
+ */
+ rpkt.r_m_e_op = CTL_RESPONSE|CTL_ERROR|(res_opcode & CTL_OP_MASK);
+ rpkt.status = htons((errcode<<8) & 0xff00);
+ rpkt.count = 0;
+
+ /*
+ * send packet and bump counters
+ */
+ if (res_authenticate) {
+ int maclen;
+
+ *(u_long *)((u_char *)&rpkt + CTL_HEADER_LEN)
+ = htonl(res_keyid);
+ maclen =
+ authencrypt(res_keyid, (U_LONG *)&rpkt, CTL_HEADER_LEN);
+ sendpkt(rmt_addr, lcl_inter, -2, (struct pkt *)&rpkt,
+ CTL_HEADER_LEN + maclen);
+ } else {
+ sendpkt(rmt_addr, lcl_inter, -3, (struct pkt *)&rpkt,
+ CTL_HEADER_LEN);
+ }
+ numctlerrors++;
+}
+
+
+/*
+ * process_control - process an incoming control message
+ */
+void
+process_control(rbufp, restrict)
+ struct recvbuf *rbufp;
+ int restrict;
+{
+ register struct ntp_control *pkt;
+ register int req_count;
+ register int req_data;
+ register struct ctl_proc *cc;
+ int properlen;
+ int maclen;
+
+#ifdef DEBUG
+ if (debug)
+ printf("in process_control()\n");
+#endif
+
+ /*
+ * Save the addresses for error responses
+ */
+ numctlreq++;
+ rmt_addr = &rbufp->recv_srcadr;
+ lcl_inter = rbufp->dstadr;
+ pkt = (struct ntp_control *)&rbufp->recv_pkt;
+
+ /*
+ * If the length is less than required for the header, or
+ * it is a response or a fragment, ignore this.
+ */
+ if (rbufp->recv_length < CTL_HEADER_LEN
+ || pkt->r_m_e_op & (CTL_RESPONSE|CTL_MORE|CTL_ERROR)
+ || pkt->offset != 0) {
+#ifdef DEBUG
+ if (debug)
+ printf("invalid format in control packet\n");
+#endif
+ if (rbufp->recv_length < CTL_HEADER_LEN)
+ numctltooshort++;
+ if (pkt->r_m_e_op & CTL_RESPONSE)
+ numctlinputresp++;
+ if (pkt->r_m_e_op & CTL_MORE)
+ numctlinputfrag++;
+ if (pkt->r_m_e_op & CTL_ERROR)
+ numctlinputerr++;
+ if (pkt->offset != 0)
+ numctlbadoffset++;
+ return;
+ }
+ res_version = PKT_VERSION(pkt->li_vn_mode);
+ if (res_version > NTP_VERSION || res_version < NTP_OLDVERSION) {
+#ifdef DEBUG
+ if (debug)
+ printf("unknown version %d in control packet\n",
+ res_version);
+#endif
+ numctlbadversion++;
+ return;
+ }
+
+ /*
+ * Pull enough data from the packet to make intelligent responses
+ */
+ rpkt.li_vn_mode = PKT_LI_VN_MODE(sys_leap, res_version, MODE_CONTROL);
+ res_opcode = pkt->r_m_e_op;
+ rpkt.sequence = pkt->sequence;
+ rpkt.associd = pkt->associd;
+ rpkt.status = 0;
+ res_offset = 0;
+ res_associd = htons(pkt->associd);
+ res_async = 0;
+ res_authenticate = 0;
+ res_keyid = 0;
+ res_authokay = 0;
+ req_count = (int)htons(pkt->count);
+ datanotbinflag = 0;
+ datalinelen = 0;
+ datapt = rpkt.data;
+ dataend = &(rpkt.data[CTL_MAX_DATA_LEN]);
+
+ /*
+ * We're set up now. Make sure we've got at least
+ * enough incoming data space to match the count.
+ */
+ req_data = rbufp->recv_length - CTL_HEADER_LEN;
+ if (req_data < req_count || rbufp->recv_length & 0x3) {
+ ctl_error(CERR_BADFMT);
+ numctldatatooshort++;
+ return;
+ }
+
+ properlen = req_count + CTL_HEADER_LEN;
+#ifdef DEBUG
+ if (debug >= 2 && (rbufp->recv_length & 0x3) != 0)
+ printf("Packet length %d unrounded\n", rbufp->recv_length);
+#endif
+ /* round up proper len to a 8 octet boundary */
+
+ properlen = (properlen + 7) & ~7;
+
+ if ((rbufp->recv_length & (sizeof(u_long)-1)) == 0
+ && (maclen = (rbufp->recv_length - properlen)) >= MIN_MAC_LEN
+ && maclen <= MAX_MAC_LEN) {
+
+ res_authenticate = 1;
+ res_keyid = ntohl(*(u_long *)((u_char *)pkt + properlen));
+
+#ifdef DEBUG
+ if (debug >= 3)
+ printf(
+ "recv_len %d, properlen %d, wants auth with keyid %ld, MAC length=%d\n",
+ rbufp->recv_length, properlen, res_keyid, maclen);
+#endif
+ if (!authhavekey(res_keyid)) {
+#ifdef DEBUG
+ if (debug >= 2)
+ printf("keyid %lu unknown\n", res_keyid);
+#endif
+ } else if (authdecrypt(res_keyid, (U_LONG *)pkt,
+ rbufp->recv_length - maclen)) {
+#ifdef DEBUG
+ if (debug >= 3)
+ printf("authenticated okay\n");
+#endif
+ res_authokay = 1;
+ } else {
+#ifdef DEBUG
+ if (debug >= 3)
+ printf("authentication failed\n");
+#endif
+ res_keyid = 0;
+ }
+ }
+
+ /*
+ * Set up translate pointers
+ */
+ reqpt = (char *)pkt->data;
+ reqend = reqpt + req_count;
+
+ /*
+ * Look for the opcode processor
+ */
+ for (cc = control_codes; cc->control_code != NO_REQUEST; cc++) {
+ if (cc->control_code == res_opcode) {
+#ifdef DEBUG
+ if (debug >= 2)
+ printf("opcode %d, found command handler\n",
+ res_opcode);
+#endif
+ if (cc->flags == AUTH && (!res_authokay
+ || res_keyid != ctl_auth_keyid)) {
+ ctl_error(CERR_PERMISSION);
+ return;
+ }
+ (cc->handler)(rbufp, restrict);
+ return;
+ }
+ }
+
+ /*
+ * Can't find this one, return an error.
+ */
+ numctlbadop++;
+ ctl_error(CERR_BADOP);
+ return;
+}
+
+
+/*
+ * ctlpeerstatus - return a status word for this peer
+ */
+u_short
+ctlpeerstatus(peer)
+ register struct peer *peer;
+{
+ register u_short status;
+
+ status = CTL_PST_SEL_REJECT;
+ if (peer->was_sane != 0)
+ status = CTL_PST_SEL_SANE;
+ if (peer->correct != 0)
+ status = CTL_PST_SEL_CORRECT;
+ if (peer->candidate != 0)
+ status = CTL_PST_SEL_SELCAND;
+ if (peer->select != 0)
+ status = CTL_PST_SEL_SYNCCAND;
+ if (peer == sys_peer) {
+ status = CTL_PST_SEL_DISTSYSPEER;
+ if (peer->synch < NTP_MAXDISTANCE) {
+ status = CTL_PST_SEL_SYSPEER;
+ if (pps_control)
+ status = CTL_PST_SEL_PPS;
+ }
+ }
+ if (peer->flags & FLAG_CONFIG)
+ status |= CTL_PST_CONFIG;
+ if (peer->flags & FLAG_AUTHENABLE) {
+ status |= CTL_PST_AUTHENABLE;
+ if (peer->flags & FLAG_AUTHENTIC)
+ status |= CTL_PST_AUTHENTIC;
+ }
+ if (peer->reach != 0)
+ status |= CTL_PST_REACH;
+
+ return (u_short)CTL_PEER_STATUS(status, peer->num_events,
+ peer->last_event);
+}
+
+
+/*
+ * ctlclkstatus - return a status word for this clock
+ */
+static u_short
+ctlclkstatus(clock)
+ struct refclockstat *clock;
+{
+ return ((u_short)(clock->currentstatus) << 8)
+ | (u_short)(clock->lastevent);
+}
+
+
+
+/*
+ * ctlsysstatus - return the system status word
+ */
+u_short
+ctlsysstatus()
+{
+ register u_char clock;
+
+ clock = CTL_SST_TS_UNSPEC;
+ if (sys_peer != 0) {
+ if (sys_peer->sstclktype != CTL_SST_TS_UNSPEC) {
+ clock = sys_peer->sstclktype;
+ if (pps_control)
+ clock |= CTL_SST_TS_PPS;
+ } else {
+ if (sys_peer->refclktype < sizeof(clocktypes))
+ clock = clocktypes[sys_peer->refclktype];
+ if (pps_control)
+ clock |= CTL_SST_TS_PPS;
+ }
+ }
+ return (u_short)CTL_SYS_STATUS(sys_leap, clock,
+ ctl_sys_num_events, ctl_sys_last_event);
+}
+
+
+
+/*
+ * ctl_flushpkt - write out the current packet and prepare
+ * another if necessary.
+ */
+static void
+ctl_flushpkt(more)
+ int more;
+{
+ int dlen;
+ int sendlen;
+
+ if (!more && datanotbinflag) {
+ /*
+ * Big hack, output a trailing \r\n
+ */
+ *datapt++ = '\r';
+ *datapt++ = '\n';
+ }
+ dlen = datapt - (u_char *)rpkt.data;
+ sendlen = dlen + CTL_HEADER_LEN;
+
+ /*
+ * Pad to a multiple of 32 bits
+ */
+ while (sendlen & 0x3) {
+ *datapt++ = '\0';
+ sendlen++;
+ }
+
+ /*
+ * Fill in the packet with the current info
+ */
+ rpkt.r_m_e_op = CTL_RESPONSE|more|(res_opcode & CTL_OP_MASK);
+ rpkt.count = htons((u_short)dlen);
+ rpkt.offset = htons(res_offset);
+ if (res_async) {
+ register int i;
+
+ for (i = 0; i < CTL_MAXTRAPS; i++) {
+ if (ctl_trap[i].tr_flags & TRAP_INUSE) {
+ rpkt.li_vn_mode = PKT_LI_VN_MODE(sys_leap,
+ ctl_trap[i].tr_version, MODE_CONTROL);
+ rpkt.sequence = htons(ctl_trap[i].tr_sequence);
+ sendpkt(&ctl_trap[i].tr_addr,
+ ctl_trap[i].tr_localaddr,
+ -4,
+ (struct pkt *)&rpkt, sendlen);
+ if (!more)
+ ctl_trap[i].tr_sequence++;
+ numasyncmsgs++;
+ }
+ }
+ } else {
+ if (res_authenticate) {
+ int maclen;
+ int totlen = sendlen;
+
+ /*
+ * If we are going to authenticate, then there is
+ * an additional requirement that the MAC begin on
+ * a 64 bit boundary.
+ */
+ while (totlen & 7) {
+ *datapt++ = '\0';
+ totlen++;
+ }
+ *(u_long *)datapt = htonl(res_keyid);
+ maclen =
+ authencrypt(res_keyid, (U_LONG *)&rpkt, totlen);
+
+ sendpkt(rmt_addr, lcl_inter, -5, (struct pkt *)&rpkt,
+ totlen + maclen);
+ } else {
+ sendpkt(rmt_addr, lcl_inter, -6, (struct pkt *)&rpkt,
+ sendlen);
+ }
+ if (more)
+ numctlfrags++;
+ else
+ numctlresponses++;
+ }
+
+ /*
+ * Set us up for another go around.
+ */
+ res_offset += dlen;
+ datapt = (u_char *)rpkt.data;
+}
+
+
+/*
+ * ctl_putdata - write data into the packet, fragmenting and
+ * starting another if this one is full.
+ */
+static void
+ctl_putdata(dp, dlen, bin)
+ char *dp;
+ int dlen;
+ int bin; /* set to 1 when data is binary */
+{
+ int overhead;
+
+ overhead = 0;
+ if (!bin) {
+ datanotbinflag = 1;
+ overhead = 3;
+ if (datapt != rpkt.data) {
+ *datapt++ = ',';
+ datalinelen++;
+ if ((dlen + datalinelen + 1) >= MAXDATALINELEN) {
+ *datapt++ = '\r';
+ *datapt++ = '\n';
+ datalinelen = 0;
+ } else {
+ *datapt++ = ' ';
+ datalinelen++;
+ }
+ }
+ }
+
+ /*
+ * Save room for trailing junk
+ */
+ if (dlen + overhead + datapt > dataend) {
+ /*
+ * Not enough room in this one, flush it out.
+ */
+ ctl_flushpkt(CTL_MORE);
+ }
+
+ memmove((char *)datapt, dp, dlen);
+ datapt += dlen;
+ datalinelen += dlen;
+}
+
+
+/*
+ * ctl_putstr - write a tagged string into the response packet
+ */
+static void
+ctl_putstr(tag, data, len)
+ char *tag;
+ char *data;
+ int len;
+{
+ register char *cp, *cq;
+ char buffer[400];
+
+ cp = buffer;
+ cq = tag;
+ while (*cq != '\0')
+ *cp++ = *cq++;
+
+ if (len > 0) {
+ *cp++ = '=';
+ *cp++ = '"';
+ if (len > (sizeof(buffer) - (cp - buffer) - 1))
+ len = sizeof(buffer) - (cp - buffer) - 1;
+ memmove(cp, data, len);
+ cp += len;
+ *cp++ = '"';
+ }
+
+ ctl_putdata(buffer, cp - buffer, 0);
+}
+
+
+
+/*
+ * ctl_putlfp - write a tagged, signed l_fp into the response packet
+ */
+static void
+ctl_putlfp(tag, ts)
+ char *tag;
+ l_fp *ts;
+{
+ register char *cp, *cq;
+ char buffer[200];
+
+ cp = buffer;
+ cq = tag;
+ while (*cq != '\0')
+ *cp++ = *cq++;
+
+ *cp++ = '=';
+ cq = lfptoms(ts, 3);
+ while (*cq != '\0')
+ *cp++ = *cq++;
+
+ ctl_putdata(buffer, cp - buffer, 0);
+}
+
+
+/*
+ * ctl_putfp - write a tagged s_fp number into the response
+ */
+static void
+ctl_putfp(tag, fp)
+ char *tag;
+ s_fp fp;
+{
+ register char *cp, *cq;
+ char buffer[200];
+
+ cp = buffer;
+ cq = tag;
+ while (*cq != '\0')
+ *cp++ = *cq++;
+
+ *cp++ = '=';
+ cq = fptoms(fp, 2);
+ while (*cq != '\0')
+ *cp++ = *cq++;
+
+ ctl_putdata(buffer, cp - buffer, 0);
+}
+
+
+/*
+ * ctl_putufp - write a tagged u_fp number into the response
+ */
+static void
+ctl_putufp(tag, ufp)
+ char *tag;
+ u_fp ufp;
+{
+ register char *cp, *cq;
+ char buffer[200];
+
+ cp = buffer;
+ cq = tag;
+ while (*cq != '\0')
+ *cp++ = *cq++;
+
+ *cp++ = '=';
+ cq = ufptoms(ufp, 2);
+ while (*cq != '\0')
+ *cp++ = *cq++;
+
+ ctl_putdata(buffer, cp - buffer, 0);
+}
+
+
+/*
+ * ctl_putuint - write a tagged unsigned integer into the response
+ */
+static void
+ctl_putuint(tag, uval)
+ char *tag;
+ u_long uval;
+{
+ register char *cp, *cq;
+ char buffer[200];
+
+ cp = buffer;
+ cq = tag;
+ while (*cq != '\0')
+ *cp++ = *cq++;
+
+ *cp++ = '=';
+ (void) sprintf(cp, "%lu", uval);
+ while (*cp != '\0')
+ cp++;
+
+ ctl_putdata(buffer, cp - buffer, 0);
+}
+
+
+/*
+ * ctl_puthex - write a tagged unsigned integer, in hex, into the response
+ */
+static void
+ctl_puthex(tag, uval)
+ char *tag;
+ u_long uval;
+{
+ register char *cp, *cq;
+ char buffer[200];
+
+ cp = buffer;
+ cq = tag;
+ while (*cq != '\0')
+ *cp++ = *cq++;
+
+ *cp++ = '=';
+ (void) sprintf(cp, "0x%lx", uval);
+ while (*cp != '\0')
+ cp++;
+
+ ctl_putdata(buffer, cp - buffer, 0);
+}
+
+
+/*
+ * ctl_putint - write a tagged signed integer into the response
+ */
+static void
+ctl_putint(tag, ival)
+ char *tag;
+ long ival;
+{
+ register char *cp, *cq;
+ char buffer[200];
+
+ cp = buffer;
+ cq = tag;
+ while (*cq != '\0')
+ *cp++ = *cq++;
+
+ *cp++ = '=';
+ (void) sprintf(cp, "%ld", ival);
+ while (*cp != '\0')
+ cp++;
+
+ ctl_putdata(buffer, cp - buffer, 0);
+}
+
+
+/*
+ * ctl_putts - write a tagged timestamp, in hex, into the response
+ */
+static void
+ctl_putts(tag, ts)
+ char *tag;
+ l_fp *ts;
+{
+ register char *cp, *cq;
+ char buffer[200];
+
+ cp = buffer;
+ cq = tag;
+ while (*cq != '\0')
+ *cp++ = *cq++;
+
+ *cp++ = '=';
+ (void) sprintf(cp, "0x%08lx.%08lx", ts->l_ui & 0xffffffffL,
+ ts->l_uf & 0xffffffffL);
+ while (*cp != '\0')
+ cp++;
+
+ ctl_putdata(buffer, cp - buffer, 0);
+}
+
+
+/*
+ * ctl_putadr - write a dotted quad IP address into the response
+ */
+static void
+ctl_putadr(tag, addr)
+ char *tag;
+ u_long addr;
+{
+ register char *cp, *cq;
+ char buffer[200];
+
+ cp = buffer;
+ cq = tag;
+ while (*cq != '\0')
+ *cp++ = *cq++;
+
+ *cp++ = '=';
+ cq = numtoa(addr);
+ while (*cq != '\0')
+ *cp++ = *cq++;
+
+ ctl_putdata(buffer, cp - buffer, 0);
+}
+
+
+/*
+ * ctl_putid - write a tagged clock ID into the response
+ */
+static void
+ctl_putid(tag, id)
+ char *tag;
+ char *id;
+{
+ register char *cp, *cq;
+ char buffer[200];
+
+ cp = buffer;
+ cq = tag;
+ while (*cq != '\0')
+ *cp++ = *cq++;
+
+ *cp++ = '=';
+ cq = id;
+ while (*cq != '\0' && (cq - id) < 4)
+ *cp++ = *cq++;
+
+ ctl_putdata(buffer, cp - buffer, 0);
+}
+
+
+/*
+ * ctl_putarray - write a tagged eight element s_fp array into the response
+ */
+static void
+ctl_putarray(tag, arr, start)
+ char *tag;
+ s_fp *arr;
+ int start;
+{
+ register char *cp, *cq;
+ char buffer[200];
+ int i, ind;
+ int len;
+
+ cp = buffer;
+ cq = tag;
+ while (*cq != '\0')
+ *cp++ = *cq++;
+ *cp++ = '=';
+ /*
+ * Hack. We know the tag is either filtdelay, filtoffset,
+ * or filterror. Space over the shorter words one space.
+ */
+ if ((cp - buffer) < 11)
+ *cp++ = ' ';
+
+ i = start;
+ ind = 0;
+ do {
+ if (i == 0)
+ i = NTP_SHIFT;
+ i--;
+ if (ind) {
+ *cp++ = ' ';
+ } else {
+ ind = 1;
+ }
+ cq = fptoms(arr[i], 2);
+ len = strlen(cq);
+ while (len < 7) {
+ *cp++ = ' ';
+ len++;
+ }
+ while (*cq != '\0')
+ *cp++ = *cq++;
+ } while(i != start);
+
+ ctl_putdata(buffer, cp - buffer, 0);
+}
+
+
+/*
+ * ctl_putsys - output a system variable
+ */
+static void
+ctl_putsys(varid)
+ int varid;
+{
+ l_fp tmp;
+
+ switch (varid) {
+ case CS_LEAP:
+ ctl_putuint(sys_var[CS_LEAP].text, sys_leap);
+ break;
+ case CS_STRATUM:
+ ctl_putuint(sys_var[CS_STRATUM].text, sys_stratum);
+ break;
+ case CS_PRECISION:
+ ctl_putint(sys_var[CS_PRECISION].text, sys_precision);
+ break;
+ case CS_ROOTDELAY:
+ ctl_putfp(sys_var[CS_ROOTDELAY].text, sys_rootdelay);
+ break;
+ case CS_ROOTDISPERSION:
+ ctl_putufp(sys_var[CS_ROOTDISPERSION].text,
+ sys_rootdispersion);
+ break;
+ case CS_REFID:
+ if (sys_stratum > 1)
+ ctl_putadr(sys_var[CS_REFID].text, sys_refid);
+ else
+ ctl_putid(sys_var[CS_REFID].text, (char *)&sys_refid);
+ break;
+ case CS_REFTIME:
+ ctl_putts(sys_var[CS_REFTIME].text, &sys_reftime);
+ break;
+ case CS_POLL:
+ ctl_putuint(sys_var[CS_POLL].text, sys_poll);
+ break;
+ case CS_PEERID:
+ if (sys_peer == NULL)
+ ctl_putuint(sys_var[CS_PEERID].text, 0);
+ else
+ ctl_putuint(sys_var[CS_PEERID].text,
+ sys_peer->associd);
+ break;
+ case CS_OFFSET:
+ ctl_putlfp(sys_var[CS_OFFSET].text, &last_offset);
+ break;
+ case CS_DRIFT:
+ ctl_putfp(sys_var[CS_DRIFT].text, drift_comp);
+ break;
+ case CS_COMPLIANCE:
+ ctl_putufp(sys_var[CS_COMPLIANCE].text, sys_maxd);
+ break;
+ case CS_CLOCK:
+ get_systime(&tmp);
+ ctl_putts(sys_var[CS_CLOCK].text, &tmp);
+ break;
+ case CS_LEAPIND:
+ ctl_putuint(sys_var[CS_LEAPIND].text, leap_indicator);
+ break;
+ case CS_LEAPWARNING:
+ ctl_putuint(sys_var[CS_LEAPWARNING].text, leap_warning);
+ break;
+ case CS_PROCESSOR:
+#ifndef HAVE_UNAME
+ ctl_putstr(sys_var[CS_PROCESSOR].text, str_processor,
+ sizeof(str_processor) - 1);
+#else
+ ctl_putstr(sys_var[CS_PROCESSOR].text, utsname.machine,
+ strlen(utsname.machine));
+#endif /* HAVE_UNAME */
+ break;
+ case CS_SYSTEM:
+#ifndef HAVE_UNAME
+ ctl_putstr(sys_var[CS_SYSTEM].text, str_system,
+ sizeof(str_system) - 1);
+#else
+ ctl_putstr(sys_var[CS_SYSTEM].text, utsname.sysname,
+ strlen(utsname.sysname));
+#endif /* HAVE_UNAME */
+ break;
+ case CS_KEYID:
+ ctl_putuint(sys_var[CS_KEYID].text, 0);
+ break;
+ case CS_REFSKEW:
+ ctl_putlfp(sys_var[CS_REFSKEW].text, &sys_refskew);
+ break;
+ case CS_VARLIST:
+ {
+ char buf[CTL_MAX_DATA_LEN];
+ register char *s, *ss, *t, *be;
+ register int i;
+ register struct ctl_var *k;
+
+ s = buf;
+ be = buf + sizeof(buf) - strlen(sys_var[CS_VARLIST].text) - 4;
+ if (s > be)
+ break; /* really long var name 8-( - Killer */
+
+ strcpy(s, sys_var[CS_VARLIST].text);
+ strcat(s, "=\"");
+ s += strlen(s);
+ t = s;
+
+ for (k = sys_var; !(k->flags &EOV); k++)
+ {
+ if (k->flags & PADDING)
+ continue;
+
+ i = strlen(k->text);
+ if (s+i+1 >= be)
+ break;
+ if (s != t)
+ *s++ = ',';
+ strcpy(s, k->text);
+ s += i;
+ }
+
+ for (k = ext_sys_var; k && !(k->flags &EOV); k++)
+ {
+ if (k->flags & PADDING)
+ continue;
+
+ ss = k->text;
+ if (!ss)
+ continue;
+
+ while (*ss && *ss != '=')
+ ss++;
+
+ i = ss - k->text;
+ if (s+i+1 >= be)
+ break;
+ if (s != t)
+ *s++ = ',';
+ strncpy(s, k->text, i);
+ s += i;
+ }
+
+ if (s+2 >= be)
+ break;
+
+ *s++ = '"';
+ *s = '\0';
+
+ ctl_putdata(buf, s - buf, 0);
+ }
+ break;
+ }
+}
+
+
+/*
+ * ctl_putpeer - output a peer variable
+ */
+static void
+ctl_putpeer(varid, peer)
+ int varid;
+ struct peer *peer;
+{
+ switch (varid) {
+ case CP_CONFIG:
+ ctl_putuint(peer_var[CP_CONFIG].text,
+ ((peer->flags & FLAG_CONFIG) != 0));
+ break;
+ case CP_AUTHENABLE:
+ ctl_putuint(peer_var[CP_AUTHENABLE].text,
+ ((peer->flags & FLAG_AUTHENABLE) != 0));
+ break;
+ case CP_AUTHENTIC:
+ ctl_putuint(peer_var[CP_AUTHENTIC].text,
+ ((peer->flags & FLAG_AUTHENTIC) != 0));
+ break;
+ case CP_SRCADR:
+ ctl_putadr(peer_var[CP_SRCADR].text,
+ peer->srcadr.sin_addr.s_addr);
+ break;
+ case CP_SRCPORT:
+ ctl_putuint(peer_var[CP_SRCPORT].text,
+ ntohs(peer->srcadr.sin_port));
+ break;
+ case CP_DSTADR:
+ ctl_putadr(peer_var[CP_DSTADR].text,
+ peer->processed ?
+ peer->cast_flags & MDF_BCAST ?
+ peer->dstadr->bcast.sin_addr.s_addr:
+ peer->cast_flags ?
+ peer->dstadr->sin.sin_addr.s_addr ?
+ peer->dstadr->sin.sin_addr.s_addr:
+ peer->dstadr->bcast.sin_addr.s_addr:
+ 8 : 12);
+ break;
+ case CP_DSTPORT:
+ ctl_putuint(peer_var[CP_DSTPORT].text,
+ ntohs(peer->dstadr->sin.sin_port));
+ break;
+ case CP_LEAP:
+ ctl_putuint(peer_var[CP_LEAP].text, peer->leap);
+ break;
+ case CP_HMODE:
+ ctl_putuint(peer_var[CP_HMODE].text, peer->hmode);
+ break;
+ case CP_STRATUM:
+ ctl_putuint(peer_var[CP_STRATUM].text, peer->stratum);
+ break;
+ case CP_PPOLL:
+ ctl_putuint(peer_var[CP_PPOLL].text, peer->ppoll);
+ break;
+ case CP_HPOLL:
+ ctl_putuint(peer_var[CP_HPOLL].text, peer->hpoll);
+ break;
+ case CP_PRECISION:
+ ctl_putint(peer_var[CP_PRECISION].text, peer->precision);
+ break;
+ case CP_ROOTDELAY:
+ ctl_putfp(peer_var[CP_ROOTDELAY].text, peer->rootdelay);
+ break;
+ case CP_ROOTDISPERSION:
+ ctl_putufp(peer_var[CP_ROOTDISPERSION].text,
+ peer->rootdispersion);
+ break;
+ case CP_REFID:
+ if (peer->stratum > 1)
+ if (peer->flags & FLAG_REFCLOCK)
+ ctl_putadr(peer_var[CP_REFID].text,
+ peer->srcadr.sin_addr.s_addr);
+ else
+ ctl_putadr(peer_var[CP_REFID].text,
+ peer->refid);
+ else
+ ctl_putid(peer_var[CP_REFID].text,
+ (char *)&peer->refid);
+ break;
+ case CP_REFTIME:
+ ctl_putts(peer_var[CP_REFTIME].text, &peer->reftime);
+ break;
+ case CP_ORG:
+ ctl_putts(peer_var[CP_ORG].text, &peer->org);
+ break;
+ case CP_REC:
+ ctl_putts(peer_var[CP_REC].text, &peer->rec);
+ break;
+ case CP_XMT:
+ ctl_putts(peer_var[CP_XMT].text, &peer->xmt);
+ break;
+ case CP_REACH:
+ ctl_puthex(peer_var[CP_REACH].text, peer->reach);
+ break;
+ case CP_FLASH:
+ ctl_puthex(peer_var[CP_FLASH].text, peer->flash);
+ break;
+ case CP_VALID:
+ ctl_putuint(peer_var[CP_VALID].text, peer->valid);
+ break;
+ case CP_TIMER:
+ ctl_putuint(peer_var[CP_TIMER].text,
+ peer->event_timer.event_time - current_time);
+ break;
+ case CP_DELAY:
+ ctl_putfp(peer_var[CP_DELAY].text, peer->delay);
+ break;
+ case CP_OFFSET:
+ ctl_putlfp(peer_var[CP_OFFSET].text, &peer->offset);
+ break;
+ case CP_DISPERSION:
+ ctl_putufp(peer_var[CP_DISPERSION].text, peer->dispersion);
+ break;
+ case CP_KEYID:
+ ctl_putuint(peer_var[CP_KEYID].text, peer->keyid);
+ break;
+ case CP_FILTDELAY:
+ ctl_putarray(peer_var[CP_FILTDELAY].text,
+ peer->filter_delay, (int)peer->filter_nextpt);
+ break;
+ case CP_FILTOFFSET:
+ ctl_putarray(peer_var[CP_FILTOFFSET].text,
+ peer->filter_soffset, (int)peer->filter_nextpt);
+ break;
+ case CP_FILTERROR:
+ ctl_putarray(peer_var[CP_FILTERROR].text,
+ (s_fp *)peer->filter_error, (int)peer->filter_nextpt);
+ break;
+ case CP_PMODE:
+ ctl_putuint(peer_var[CP_PMODE].text, peer->pmode);
+ break;
+ case CP_RECEIVED:
+ ctl_putuint(peer_var[CP_RECEIVED].text, peer->received);
+ break;
+ case CP_SENT:
+ ctl_putuint(peer_var[CP_SENT].text, peer->sent);
+ break;
+ case CP_VARLIST:
+ {
+ char buf[CTL_MAX_DATA_LEN];
+ register char *s, *t, *be;
+ register int i;
+ register struct ctl_var *k;
+
+ s = buf;
+ be = buf + sizeof(buf) - strlen(peer_var[CP_VARLIST].text) - 4;
+ if (s > be)
+ break; /* really long var name 8-( - Killer */
+
+ strcpy(s, peer_var[CP_VARLIST].text);
+ strcat(s, "=\"");
+ s += strlen(s);
+ t = s;
+
+ for (k = peer_var; !(k->flags &EOV); k++)
+ {
+ if (k->flags & PADDING)
+ continue;
+
+ i = strlen(k->text);
+ if (s+i+1 >= be)
+ break;
+ if (s != t)
+ *s++ = ',';
+ strcpy(s, k->text);
+ s += i;
+ }
+
+ if (s+2 >= be)
+ break;
+
+ *s++ = '"';
+ *s = '\0';
+
+ ctl_putdata(buf, s - buf, 0);
+ }
+ break;
+ }
+}
+
+
+#ifdef REFCLOCK
+/*
+ * ctl_putclock - output clock variables
+ */
+static void
+ctl_putclock(varid, clock, mustput)
+ int varid;
+ struct refclockstat *clock;
+ int mustput;
+{
+ switch(varid) {
+ case CC_TYPE:
+ if (mustput || clock->clockdesc == NULL
+ || *(clock->clockdesc) == '\0') {
+ ctl_putuint(clock_var[CC_TYPE].text, clock->type);
+ }
+ break;
+ case CC_TIMECODE:
+ ctl_putstr(clock_var[CC_TIMECODE].text, clock->lastcode,
+ (int)clock->lencode);
+ break;
+ case CC_POLL:
+ ctl_putuint(clock_var[CC_POLL].text, clock->polls);
+ break;
+ case CC_NOREPLY:
+ ctl_putuint(clock_var[CC_NOREPLY].text, clock->noresponse);
+ break;
+ case CC_BADFORMAT:
+ ctl_putuint(clock_var[CC_BADFORMAT].text, clock->badformat);
+ break;
+ case CC_BADDATA:
+ ctl_putuint(clock_var[CC_BADDATA].text, clock->baddata);
+ break;
+ case CC_FUDGETIME1:
+ if (mustput || (clock->haveflags & CLK_HAVETIME1))
+ ctl_putlfp(clock_var[CC_FUDGETIME1].text,
+ &clock->fudgetime1);
+ break;
+ case CC_FUDGETIME2:
+ if (mustput || (clock->haveflags & CLK_HAVETIME2))
+ ctl_putlfp(clock_var[CC_FUDGETIME2].text,
+ &clock->fudgetime2);
+ break;
+ case CC_FUDGEVAL1:
+ if (mustput || (clock->haveflags & CLK_HAVEVAL1))
+ ctl_putint(clock_var[CC_FUDGEVAL1].text,
+ clock->fudgeval1);
+ break;
+ case CC_FUDGEVAL2:
+ if (mustput || (clock->haveflags & CLK_HAVEVAL2))
+ if (clock->fudgeval1 > 1)
+ ctl_putadr(clock_var[CC_FUDGEVAL2].text,
+ clock->fudgeval2);
+ else
+ ctl_putid(clock_var[CC_FUDGEVAL2].text,
+ (char *)&clock->fudgeval2);
+ break;
+ case CC_FLAGS:
+ if (mustput || (clock->haveflags &
+ (CLK_HAVEFLAG1|CLK_HAVEFLAG2|CLK_HAVEFLAG3|CLK_HAVEFLAG4)))
+ ctl_putuint(clock_var[CC_FLAGS].text, clock->flags);
+ break;
+ case CC_DEVICE:
+ if (clock->clockdesc == NULL || *(clock->clockdesc) == '\0') {
+ if (mustput)
+ ctl_putstr(clock_var[CC_DEVICE].text, "", 0);
+ } else {
+ ctl_putstr(clock_var[CC_DEVICE].text, clock->clockdesc,
+ strlen(clock->clockdesc));
+ }
+ break;
+ case CC_VARLIST:
+ {
+ char buf[CTL_MAX_DATA_LEN];
+ register char *s, *ss, *t, *be;
+ register int i;
+ register struct ctl_var *k;
+
+ s = buf;
+ be = buf + sizeof(buf) - strlen(clock_var[CC_VARLIST].text) - 4;
+ if (s > be)
+ break; /* really long var name 8-( - Killer */
+
+ strcpy(s, clock_var[CC_VARLIST].text);
+ strcat(s, "=\"");
+ s += strlen(s);
+ t = s;
+
+ for (k = clock_var; !(k->flags &EOV); k++)
+ {
+ if (k->flags & PADDING)
+ continue;
+
+ i = strlen(k->text);
+ if (s+i+1 >= be)
+ break;
+ if (s != t)
+ *s++ = ',';
+ strcpy(s, k->text);
+ s += i;
+ }
+
+ for (k = clock->kv_list; k && !(k->flags &EOV); k++)
+ {
+ if (k->flags & PADDING)
+ continue;
+
+ ss = k->text;
+ if (!ss)
+ continue;
+
+ while (*ss && *ss != '=')
+ ss++;
+
+ i = ss - k->text;
+ if (s+i+1 >= be)
+ break;
+ if (s != t)
+ *s++ = ',';
+ strncpy(s, k->text, i);
+ s += i;
+ *s = '\0';
+ }
+
+ if (s+2 >= be)
+ break;
+
+ *s++ = '"';
+ *s = '\0';
+
+ ctl_putdata(buf, s - buf, 0);
+ }
+ break;
+ }
+}
+#endif
+
+
+
+/*
+ * ctl_getitem - get the next data item from the incoming packet
+ */
+static struct ctl_var *
+ctl_getitem(var_list, data)
+ struct ctl_var *var_list;
+ char **data;
+{
+ register struct ctl_var *v;
+ register char *cp, *tp;
+ static struct ctl_var eol = { 0, EOV, };
+ static char buf[128];
+
+ /*
+ * Delete leading commas and white space
+ */
+ while (reqpt < reqend && (*reqpt == ',' || isspace(*reqpt))) {
+ reqpt++;
+ }
+
+ if (reqpt >= reqend)
+ return 0;
+
+ if (var_list == (struct ctl_var *)0)
+ return &eol;
+
+ /*
+ * Look for a first character match on the tag. If we find
+ * one, see if it is a full match.
+ */
+ v = var_list;
+ cp = reqpt;
+ while (!(v->flags & EOV)) {
+ if (!(v->flags & PADDING) && *cp == *(v->text)) {
+ tp = v->text;
+ while (*tp != '\0' && *tp != '=' && cp < reqend && *cp == *tp) {
+ cp++;
+ tp++;
+ }
+ if ((*tp == '\0') || (*tp == '=')) {
+ while (cp < reqend && isspace(*cp))
+ cp++;
+ if (cp == reqend || *cp == ',') {
+ buf[0] = '\0';
+ *data = buf;
+ if (cp < reqend)
+ cp++;
+ reqpt = cp;
+ return v;
+ }
+ if (*cp == '=') {
+ cp++;
+ tp = buf;
+ while (cp < reqend && isspace(*cp))
+ cp++;
+ while (cp < reqend && *cp != ',')
+ *tp++ = *cp++;
+ if (cp < reqend)
+ cp++;
+ *tp = '\0';
+ while (isspace(*(tp-1)))
+ *(--tp) = '\0';
+ reqpt = cp;
+ *data = buf;
+ return v;
+ }
+ }
+ cp = reqpt;
+ }
+ v++;
+ }
+ return v;
+}
+
+
+/*
+ * control_unspec - response to an unspecified op-code
+ */
+/*ARGSUSED*/
+static void
+control_unspec(rbufp, restrict)
+ struct recvbuf *rbufp;
+ int restrict;
+{
+ struct peer *peer;
+
+ /*
+ * What is an appropriate response to an unspecified op-code?
+ * I return no errors and no data, unless a specified assocation
+ * doesn't exist.
+ */
+ if (res_associd != 0) {
+ if ((peer = findpeerbyassoc((int)res_associd)) == 0) {
+ ctl_error(CERR_BADASSOC);
+ return;
+ }
+ rpkt.status = htons(ctlpeerstatus(peer));
+ } else {
+ rpkt.status = htons(ctlsysstatus());
+ }
+ ctl_flushpkt(0);
+}
+
+
+/*
+ * read_status - return either a list of associd's, or a particular
+ * peer's status.
+ */
+/*ARGSUSED*/
+static void
+read_status(rbufp, restrict)
+ struct recvbuf *rbufp;
+ int restrict;
+{
+ register int i;
+ register struct peer *peer;
+ u_short ass_stat[CTL_MAX_DATA_LEN/sizeof(u_short)];
+
+#ifdef DEBUG
+ if (debug >= 2)
+ printf("read_status: ID %d\n", res_associd);
+#endif
+ /*
+ * Two choices here. If the specified association ID is
+ * zero we return all known assocation ID's. Otherwise
+ * we return a bunch of stuff about the particular peer.
+ */
+ if (res_associd == 0) {
+ register int n;
+
+ n = 0;
+ rpkt.status = htons(ctlsysstatus());
+ for (i = 0; i < HASH_SIZE; i++) {
+ for (peer = assoc_hash[i]; peer != 0;
+ peer = peer->ass_next) {
+ ass_stat[n++] = htons(peer->associd);
+ ass_stat[n++] = htons(ctlpeerstatus(peer));
+ if (n == CTL_MAX_DATA_LEN/sizeof(u_short)) {
+ ctl_putdata((char *)ass_stat,
+ n * sizeof(u_short), 1);
+ n = 0;
+ }
+ }
+ }
+
+ if (n != 0)
+ ctl_putdata((char *)ass_stat, n * sizeof(u_short), 1);
+ ctl_flushpkt(0);
+ } else {
+ peer = findpeerbyassoc((int)res_associd);
+ if (peer == 0) {
+ ctl_error(CERR_BADASSOC);
+ } else {
+ register u_char *cp;
+
+ rpkt.status = htons(ctlpeerstatus(peer));
+ if (res_authokay)
+ peer->num_events = 0;
+ /*
+ * For now, output everything we know about the peer.
+ * May be more selective later.
+ */
+ for (cp = def_peer_var; *cp != 0; cp++)
+ ctl_putpeer((int)*cp, peer);
+ ctl_flushpkt(0);
+ }
+ }
+}
+
+
+/*
+ * read_variables - return the variables the caller asks for
+ */
+/*ARGSUSED*/
+static void
+read_variables(rbufp, restrict)
+ struct recvbuf *rbufp;
+ int restrict;
+{
+ register struct ctl_var *v;
+ register int i;
+ char *valuep;
+ u_char *wants;
+ int gotvar = (CS_MAXCODE>CP_MAXCODE) ? (CS_MAXCODE+1) : (CP_MAXCODE+1);
+
+ if (res_associd == 0) {
+ /*
+ * Wants system variables. Figure out which he wants
+ * and give them to him.
+ */
+ rpkt.status = htons(ctlsysstatus());
+ if (res_authokay)
+ ctl_sys_num_events = 0;
+ gotvar += count_var(ext_sys_var);
+ wants = (u_char *)emalloc(gotvar);
+ memset((char *)wants, 0, gotvar);
+ gotvar = 0;
+ while ((v = ctl_getitem(sys_var, &valuep)) != 0) {
+ if (v->flags & EOV) {
+ if ((v = ctl_getitem(ext_sys_var, &valuep)) != 0) {
+ if (v->flags & EOV) {
+ ctl_error(CERR_UNKNOWNVAR);
+ free((char *)wants);
+ return;
+ }
+ wants[CS_MAXCODE+1+v->code] = 1;
+ gotvar = 1;
+ continue;
+ } else {
+ break; /* shouldn't happen ! */
+ }
+ }
+ wants[v->code] = 1;
+ gotvar = 1;
+ }
+ if (gotvar) {
+ for (i = 1; i <= CS_MAXCODE; i++)
+ if (wants[i])
+ ctl_putsys(i);
+ for (i = 0; ext_sys_var && !(ext_sys_var[i].flags & EOV); i++)
+ if (wants[i+CS_MAXCODE+1])
+ ctl_putdata(ext_sys_var[i].text,
+ strlen(ext_sys_var[i].text), 0);
+ } else {
+ register u_char *cs;
+ register struct ctl_var *kv;
+
+ for (cs = def_sys_var; *cs != 0; cs++)
+ ctl_putsys((int)*cs);
+ for (kv = ext_sys_var; kv && !(kv->flags & EOV); kv++)
+ if (kv->flags & DEF)
+ ctl_putdata(kv->text, strlen(kv->text), 0);
+ }
+ free((char *)wants);
+ } else {
+ register struct peer *peer;
+
+ /*
+ * Wants info for a particular peer. See if we know
+ * the guy.
+ */
+ peer = findpeerbyassoc((int)res_associd);
+ if (peer == 0) {
+ ctl_error(CERR_BADASSOC);
+ return;
+ }
+
+ rpkt.status = htons(ctlpeerstatus(peer));
+ if (res_authokay)
+ peer->num_events = 0;
+ wants = (u_char *)emalloc(gotvar);
+ memset((char*)wants, 0, gotvar);
+ gotvar = 0;
+ while ((v = ctl_getitem(peer_var, &valuep)) != 0) {
+ if (v->flags & EOV) {
+ ctl_error(CERR_UNKNOWNVAR);
+ free((char *)wants);
+ return;
+ }
+ wants[v->code] = 1;
+ gotvar = 1;
+ }
+ if (gotvar) {
+ for (i = 1; i <= CP_MAXCODE; i++)
+ if (wants[i])
+ ctl_putpeer(i, peer);
+ } else {
+ register u_char *cp;
+
+ for (cp = def_peer_var; *cp != 0; cp++)
+ ctl_putpeer((int)*cp, peer);
+ }
+ free((char *)wants);
+ }
+ ctl_flushpkt(0);
+}
+
+
+/*
+ * write_variables - write into variables. We only allow leap bit writing
+ * this way.
+ */
+/*ARGSUSED*/
+static void
+write_variables(rbufp, restrict)
+ struct recvbuf *rbufp;
+ int restrict;
+{
+ register struct ctl_var *v;
+ register int ext_var;
+ char *valuep;
+ long val;
+ int leapind, leapwarn;
+
+ /*
+ * If he's trying to write into a peer tell him no way
+ */
+ if (res_associd != 0) {
+ ctl_error(CERR_PERMISSION);
+ return;
+ }
+
+ /*
+ * Set status
+ */
+ rpkt.status = htons(ctlsysstatus());
+
+ /*
+ * Set flags to not-in-sync so we can tell when we get something.
+ */
+ leapind = ~0;
+ leapwarn = ~0;
+
+ /*
+ * Look through the variables. Dump out at the first sign of trouble.
+ */
+ while ((v = ctl_getitem(sys_var, &valuep)) != 0) {
+ ext_var = 0;
+ if (v->flags & EOV) {
+ if ((v = ctl_getitem(ext_sys_var, &valuep)) != 0) {
+ if (v->flags & EOV) {
+ ctl_error(CERR_UNKNOWNVAR);
+ return;
+ }
+ ext_var = 1;
+ } else {
+ break;
+ }
+ }
+ if (!(v->flags & CAN_WRITE)) {
+ ctl_error(CERR_PERMISSION);
+ return;
+ }
+ if (!ext_var && (*valuep == '\0' || !atoint(valuep, &val))) {
+ ctl_error(CERR_BADFMT);
+ return;
+ }
+ if (!ext_var && (val & ~LEAP_NOTINSYNC) != 0) {
+ ctl_error(CERR_BADVALUE);
+ return;
+ }
+
+ if (ext_var) {
+ char *s = emalloc(strlen(v->text)+strlen(valuep)+2);
+ char *t, *tt = s;
+
+ t = v->text;
+ while (*t && *t != '=')
+ *tt++ = *t++;
+
+ *tt++ = '=';
+ strcat(tt, valuep);
+
+ set_sys_var(s, strlen(s)+1, v->flags);
+ free(s);
+ } else {
+ /*
+ * This one seems sane. Save it.
+ */
+ switch(v->code) {
+ case CS_LEAP:
+ case CS_LEAPIND:
+ leapind = val;
+ break;
+ case CS_LEAPWARNING:
+ leapwarn = val;
+ break;
+ default:
+ ctl_error(CERR_UNSPEC); /* our fault, really */
+ return;
+ }
+ }
+ }
+
+ /*
+ * If we got anything, do it.
+ */
+ if (leapind != ~0 || leapwarn != ~0) {
+ if (!leap_setleap((int)leapind, (int)leapwarn)) {
+ ctl_error(CERR_PERMISSION);
+ return;
+ }
+ }
+ ctl_flushpkt(0);
+}
+
+
+/*
+ * read_clock_status - return clock radio status
+ */
+/*ARGSUSED*/
+static void
+read_clock_status(rbufp, restrict)
+ struct recvbuf *rbufp;
+ int restrict;
+{
+#ifndef REFCLOCK
+ /*
+ * If no refclock support, no data to return
+ */
+ ctl_error(CERR_BADASSOC);
+#else
+ register struct ctl_var *v;
+ register int i;
+ register struct peer *peer;
+ char *valuep;
+ u_char *wants;
+ int gotvar;
+ struct refclockstat clock;
+
+ if (res_associd == 0) {
+ /*
+ * Find a clock for this jerk. If the system peer
+ * is a clock use it, else search the hash tables
+ * for one.
+ */
+ if (sys_peer != 0 && (sys_peer->flags & FLAG_REFCLOCK)) {
+ peer = sys_peer;
+ } else {
+ peer = 0;
+ for (i = 0; peer == 0 && i < HASH_SIZE; i++) {
+ for (peer = assoc_hash[i]; peer != 0;
+ peer = peer->ass_next) {
+ if (peer->flags & FLAG_REFCLOCK)
+ break;
+ }
+ }
+ if (peer == 0) {
+ ctl_error(CERR_BADASSOC);
+ return;
+ }
+ }
+ } else {
+ peer = findpeerbyassoc((int)res_associd);
+ if (peer == 0 || !(peer->flags & FLAG_REFCLOCK)) {
+ ctl_error(CERR_BADASSOC);
+ return;
+ }
+ }
+
+ /*
+ * If we got here we have a peer which is a clock. Get his status.
+ */
+ clock.kv_list = (struct ctl_var *)0;
+
+ refclock_control(&peer->srcadr, (struct refclockstat *)0, &clock);
+
+ /*
+ * Look for variables in the packet.
+ */
+ rpkt.status = htons(ctlclkstatus(&clock));
+ gotvar = CC_MAXCODE+1+count_var(clock.kv_list);
+ wants = (u_char *)emalloc(gotvar);
+ memset((char*)wants, 0, gotvar);
+ gotvar = 0;
+ while ((v = ctl_getitem(clock_var, &valuep)) != 0) {
+ if (v->flags & EOV) {
+ if ((v = ctl_getitem(clock.kv_list, &valuep)) != 0) {
+ if (v->flags & EOV) {
+ ctl_error(CERR_UNKNOWNVAR);
+ free((char*)wants);
+ free_varlist(clock.kv_list);
+ return;
+ }
+ wants[CC_MAXCODE+1+v->code] = 1;
+ gotvar = 1;
+ continue;
+ } else {
+ break; /* shouldn't happen ! */
+ }
+ }
+ wants[v->code] = 1;
+ gotvar = 1;
+ }
+
+ if (gotvar) {
+ for (i = 1; i <= CC_MAXCODE; i++)
+ if (wants[i])
+ ctl_putclock(i, &clock, 1);
+ for (i = 0; clock.kv_list && !(clock.kv_list[i].flags & EOV); i++)
+ if (wants[i+CC_MAXCODE+1])
+ ctl_putdata(clock.kv_list[i].text,
+ strlen(clock.kv_list[i].text), 0);
+ } else {
+ register u_char *cc;
+ register struct ctl_var *kv;
+
+ for (cc = def_clock_var; *cc != 0; cc++)
+ ctl_putclock((int)*cc, &clock, 0);
+ for (kv = clock.kv_list; kv && !(kv->flags & EOV); kv++)
+ if (kv->flags & DEF)
+ ctl_putdata(kv->text, strlen(kv->text), 0);
+ }
+
+ free((char*)wants);
+ free_varlist(clock.kv_list);
+
+ ctl_flushpkt(0);
+#endif
+}
+
+
+/*
+ * write_clock_status - we don't do this
+ */
+/*ARGSUSED*/
+static void
+write_clock_status(rbufp, restrict)
+ struct recvbuf *rbufp;
+ int restrict;
+{
+ ctl_error(CERR_PERMISSION);
+}
+
+/*
+ * Trap support from here on down. We send async trap messages when the
+ * upper levels report trouble. Traps can by set either by control
+ * messages or by configuration.
+ */
+
+/*
+ * set_trap - set a trap in response to a control message
+ */
+static void
+set_trap(rbufp, restrict)
+ struct recvbuf *rbufp;
+ int restrict;
+{
+ int traptype;
+
+ /*
+ * See if this guy is allowed
+ */
+ if (restrict & RES_NOTRAP) {
+ ctl_error(CERR_PERMISSION);
+ return;
+ }
+
+ /*
+ * Determine his allowed trap type.
+ */
+ traptype = TRAP_TYPE_PRIO;
+ if (restrict & RES_LPTRAP)
+ traptype = TRAP_TYPE_NONPRIO;
+
+ /*
+ * Call ctlsettrap() to do the work. Return
+ * an error if it can't assign the trap.
+ */
+ if (!ctlsettrap(&rbufp->recv_srcadr, rbufp->dstadr, traptype,
+ (int)res_version))
+ ctl_error(CERR_NORESOURCE);
+ ctl_flushpkt(0);
+}
+
+
+/*
+ * unset_trap - unset a trap in response to a control message
+ */
+static void
+unset_trap(rbufp, restrict)
+ struct recvbuf *rbufp;
+ int restrict;
+{
+ int traptype;
+
+ /*
+ * We don't prevent anyone from removing his own
+ * trap unless the trap is configured. Note we also
+ * must be aware of the possibility that restriction
+ * flags were changed since this guy last set his trap.
+ * Set the trap type based on this.
+ */
+ traptype = TRAP_TYPE_PRIO;
+ if (restrict & RES_LPTRAP)
+ traptype = TRAP_TYPE_NONPRIO;
+
+ /*
+ * Call ctlclrtrap() to clear this out.
+ */
+ if (!ctlclrtrap(&rbufp->recv_srcadr, rbufp->dstadr, traptype))
+ ctl_error(CERR_BADASSOC);
+ ctl_flushpkt(0);
+}
+
+
+/*
+ * ctlsettrap - called to set a trap
+ */
+int
+ctlsettrap(raddr, linter, traptype, version)
+ struct sockaddr_in *raddr;
+ struct interface *linter;
+ int traptype;
+ int version;
+{
+ register struct ctl_trap *tp;
+ register struct ctl_trap *tptouse;
+
+ /*
+ * See if we can find this trap. If so, we only need update
+ * the flags and the time.
+ */
+ if ((tp = ctlfindtrap(raddr, linter)) != NULL) {
+ switch (traptype) {
+ case TRAP_TYPE_CONFIG:
+ tp->tr_flags = TRAP_INUSE|TRAP_CONFIGURED;
+ break;
+ case TRAP_TYPE_PRIO:
+ if (tp->tr_flags & TRAP_CONFIGURED)
+ return 1; /* don't change anything */
+ tp->tr_flags = TRAP_INUSE;
+ break;
+ case TRAP_TYPE_NONPRIO:
+ if (tp->tr_flags & TRAP_CONFIGURED)
+ return 1; /* don't change anything */
+ tp->tr_flags = TRAP_INUSE|TRAP_NONPRIO;
+ break;
+ }
+ tp->tr_settime = current_time;
+ tp->tr_resets++;
+ return 1;
+ }
+
+ /*
+ * First we heard of this guy. Try to find a trap structure
+ * for him to use, clearing out lesser priority guys if we
+ * have to. Clear out anyone who's expired while we're at it.
+ */
+ tptouse = NULL;
+ for (tp = ctl_trap; tp < &ctl_trap[CTL_MAXTRAPS]; tp++) {
+ if ((tp->tr_flags & TRAP_INUSE) &&
+ !(tp->tr_flags & TRAP_CONFIGURED) &&
+ ((tp->tr_settime + CTL_TRAPTIME) > current_time)) {
+ tp->tr_flags = 0;
+ num_ctl_traps--;
+ }
+
+ if (!(tp->tr_flags & TRAP_INUSE)) {
+ tptouse = tp;
+ } else if (!(tp->tr_flags & TRAP_CONFIGURED)) {
+ switch (traptype) {
+ case TRAP_TYPE_CONFIG:
+ if (tptouse == NULL) {
+ tptouse = tp;
+ break;
+ }
+ if (tptouse->tr_flags & TRAP_NONPRIO
+ && !(tp->tr_flags & TRAP_NONPRIO))
+ break;
+ if (!(tptouse->tr_flags & TRAP_NONPRIO)
+ && tp->tr_flags & TRAP_NONPRIO) {
+ tptouse = tp;
+ break;
+ }
+ if (tptouse->tr_origtime < tp->tr_origtime)
+ tptouse = tp;
+ break;
+ case TRAP_TYPE_PRIO:
+ if (tp->tr_flags & TRAP_NONPRIO) {
+ if (tptouse == NULL ||
+ (tptouse->tr_flags & TRAP_INUSE
+ && tptouse->tr_origtime
+ < tp->tr_origtime))
+ tptouse = tp;
+ }
+ break;
+ case TRAP_TYPE_NONPRIO:
+ break;
+ }
+ }
+ }
+
+ /*
+ * If we don't have room for him return an error.
+ */
+ if (tptouse == NULL)
+ return 0;
+
+ /*
+ * Set up this structure for him.
+ */
+ tptouse->tr_settime = tptouse->tr_origtime = current_time;
+ tptouse->tr_count = tptouse->tr_resets = 0;
+ tptouse->tr_sequence = 1;
+ tptouse->tr_addr = *raddr;
+ tptouse->tr_localaddr = linter;
+ tptouse->tr_version = version;
+
+ tptouse->tr_flags = TRAP_INUSE;
+ if (traptype == TRAP_TYPE_CONFIG)
+ tptouse->tr_flags |= TRAP_CONFIGURED;
+ else if (traptype == TRAP_TYPE_NONPRIO)
+ tptouse->tr_flags |= TRAP_NONPRIO;
+ num_ctl_traps++;
+ return 1;
+}
+
+
+/*
+ * ctlclrtrap - called to clr a trap
+ */
+int
+ctlclrtrap(raddr, linter, traptype)
+ struct sockaddr_in *raddr;
+ struct interface *linter;
+ int traptype;
+{
+ register struct ctl_trap *tp;
+
+ if ((tp = ctlfindtrap(raddr, linter)) == NULL)
+ return 0;
+
+ if (tp->tr_flags & TRAP_CONFIGURED
+ && traptype != TRAP_TYPE_CONFIG)
+ return 0;
+
+ tp->tr_flags = 0;
+ num_ctl_traps--;
+ return 1;
+}
+
+
+/*
+ * ctlfindtrap - find a trap given the remote and local addresses
+ */
+static struct ctl_trap *
+ctlfindtrap(raddr, linter)
+ struct sockaddr_in *raddr;
+ struct interface *linter;
+{
+ register struct ctl_trap *tp;
+
+ for (tp = ctl_trap; tp < &ctl_trap[CTL_MAXTRAPS]; tp++) {
+ if (tp->tr_flags & TRAP_INUSE
+ && NSRCADR(raddr) == NSRCADR(&tp->tr_addr)
+ && NSRCPORT(raddr) == NSRCPORT(&tp->tr_addr)
+ && linter == tp->tr_localaddr)
+ return tp;
+ }
+ return (struct ctl_trap *)NULL;
+}
+
+
+/*
+ * report_event - report an event to the trappers
+ */
+void
+report_event(err, peer)
+ int err;
+ struct peer *peer;
+{
+ register int i;
+
+ /*
+ * Record error code in proper spots, but have mercy on the
+ * log file.
+ */
+ if (!(err & PEER_EVENT)) {
+ if (ctl_sys_num_events < CTL_SYS_MAXEVENTS)
+ ctl_sys_num_events++;
+ if (ctl_sys_last_event != (u_char)err) {
+ syslog(LOG_INFO, "system event %x status %x",
+ err, ctlsysstatus());
+#ifdef DEBUG
+ if (debug)
+ printf("report_event: system event %x status %x\n",
+ err, ctlsysstatus());
+#endif
+ ctl_sys_last_event = (u_char)err;
+ }
+ } else if (peer != 0) {
+ peer->last_event = (u_char)(err & ~PEER_EVENT);
+ if (peer->num_events < CTL_PEER_MAXEVENTS)
+ peer->num_events++;
+ syslog(LOG_INFO, "peer %s event %x status %x",
+ ntoa(&peer->srcadr), err, ctlpeerstatus(peer));
+#ifdef DEBUG
+ if (debug)
+ printf("peer %s event %x status %x\n",
+ ntoa(&peer->srcadr), err, ctlpeerstatus(peer));
+#endif
+ } else {
+ syslog(LOG_ERR, "report_event: err %x, no peer", err);
+#ifdef DEBUG
+ printf("report_event: err %x, no peer\n", err);
+#endif
+ return;
+ }
+
+ /*
+ * If no trappers, return.
+ */
+ if (num_ctl_traps <= 0)
+ return;
+
+ /*
+ * Set up the outgoing packet variables
+ */
+ res_opcode = CTL_OP_ASYNCMSG;
+ res_offset = 0;
+ res_async = 1;
+ res_authenticate = 0;
+ datapt = rpkt.data;
+ dataend = &(rpkt.data[CTL_MAX_DATA_LEN]);
+
+ if (!(err & PEER_EVENT)) {
+ rpkt.associd = 0;
+ rpkt.status = htons(ctlsysstatus());
+
+ /*
+ * For now, put everything we know about system
+ * variables. Maybe more selective later
+ */
+ for (i = 1; i <= CS_MAXCODE; i++)
+ ctl_putsys(i);
+#ifdef REFCLOCK
+ /*
+ * for clock exception events:
+ * add clock variables to reflect info on exception
+ */
+ if (err == EVNT_CLOCKEXCPT) {
+ struct refclockstat clock;
+ struct ctl_var *kv;
+
+ clock.kv_list = (struct ctl_var *)0;
+
+ refclock_control(&peer->srcadr,
+ (struct refclockstat *)0, &clock);
+ ctl_puthex("refclockstatus", ctlclkstatus(&clock));
+
+ for (i = 1; i <= CC_MAXCODE; i++)
+ ctl_putclock(i, &clock, 0);
+ for (kv = clock.kv_list; kv && !(kv->flags & EOV); kv++)
+ if (kv->flags & DEF)
+ ctl_putdata(kv->text, strlen(kv->text), 0);
+
+ free_varlist(clock.kv_list);
+ }
+#endif /*REFCLOCK*/
+ } else {
+ rpkt.associd = htons(peer->associd);
+ rpkt.status = htons(ctlpeerstatus(peer));
+
+ /*
+ * Dump it all. Later, maybe less.
+ */
+ for (i = 1; i <= CP_MAXCODE; i++)
+ ctl_putpeer(i, peer);
+#ifdef REFCLOCK
+ /*
+ * for clock exception events:
+ * add clock variables to reflect info on exception
+ */
+ if (err == EVNT_PEERCLOCK) {
+ struct refclockstat clock;
+ struct ctl_var *kv;
+
+ clock.kv_list = (struct ctl_var *)0;
+
+ refclock_control(&peer->srcadr,
+ (struct refclockstat *)0,
+ &clock);
+
+ ctl_puthex("refclockstatus",
+ ctlclkstatus(&clock));
+
+ for (i = 1; i <= CC_MAXCODE; i++)
+ ctl_putclock(i, &clock, 0);
+ for (kv = clock.kv_list; kv && !(kv->flags & EOV); kv++)
+ if (kv->flags & DEF)
+ ctl_putdata(kv->text, strlen(kv->text), 0);
+
+ free_varlist(clock.kv_list);
+ }
+#endif /*REFCLOCK*/
+ }
+
+ /*
+ * We're done, return.
+ */
+ ctl_flushpkt(0);
+}
+
+
+/*
+ * ctl_clr_stats - clear stat counters
+ */
+void
+ctl_clr_stats()
+{
+ ctltimereset = current_time;
+ numctlreq = 0;
+ numctlbadpkts = 0;
+ numctlresponses = 0;
+ numctlfrags = 0;
+ numctlerrors = 0;
+ numctlfrags = 0;
+ numctltooshort = 0;
+ numctlinputresp = 0;
+ numctlinputfrag = 0;
+ numctlinputerr = 0;
+ numctlbadoffset = 0;
+ numctlbadversion = 0;
+ numctldatatooshort = 0;
+ numctlbadop = 0;
+ numasyncmsgs = 0;
+}
+
+static u_long
+count_var(k)
+ struct ctl_var *k;
+{
+ register u_long c;
+
+ c = 0;
+ while (k && !(k++->flags & EOV))
+ c++;
+
+ return c;
+}
+
+char *
+add_var(kv, size, def)
+ struct ctl_var **kv;
+ u_long size;
+ int def;
+{
+ register u_long c;
+ register struct ctl_var *k;
+
+ c = count_var(*kv);
+
+ k = *kv;
+ *kv = (struct ctl_var *)emalloc((c+2)*sizeof(struct ctl_var));
+ if (k)
+ {
+ memmove((char *)*kv, (char *)k, sizeof(struct ctl_var)*c);
+ free((char *)k);
+ }
+
+ (*kv)[c].code = c;
+ (*kv)[c].text = (char *)emalloc(size);
+ (*kv)[c].flags = def;
+ (*kv)[c+1].code = 0;
+ (*kv)[c+1].text = (char *)0;
+ (*kv)[c+1].flags = EOV;
+ return (*kv)[c].text;
+}
+
+void
+set_var(kv, data, size, def)
+ struct ctl_var **kv;
+ char *data;
+ u_long size;
+ int def;
+{
+ register struct ctl_var *k;
+ register char *s, *t;
+
+ if (!data || !size)
+ return;
+
+ if ((k = *kv))
+ {
+ while (!(k->flags & EOV))
+ {
+ s = data;
+ t = k->text;
+ if (t)
+ {
+ while (*t != '=' && *s - *t == 0)
+ {
+ s++;
+ t++;
+ }
+ if (*s == *t && ((*t == '=') || !*t))
+ {
+ free(k->text);
+ k->text = (char *)emalloc(size);
+ memmove(k->text, data, size);
+ k->flags = def;
+ return;
+ }
+ }
+ else
+ {
+ k->text = (char *)emalloc(size);
+ memmove(k->text, data, size);
+ k->flags = def;
+ return;
+ }
+ k++;
+ }
+ }
+ t = add_var(kv, size, def);
+ memmove(t, data, size);
+}
+
+void
+set_sys_var(data, size, def)
+ char *data;
+ u_long size;
+ int def;
+{
+ set_var(&ext_sys_var, data, size, def);
+}
+
+void
+free_varlist(kv)
+ struct ctl_var *kv;
+{
+ struct ctl_var *k;
+ if (kv)
+ {
+ for (k = kv; !(k->flags & EOV); k++)
+ free(k->text);
+ free((char *)kv);
+ }
+}
diff --git a/usr.sbin/xntpd/xntpd/ntp_filegen.c b/usr.sbin/xntpd/xntpd/ntp_filegen.c
new file mode 100644
index 0000000..cc4882b
--- /dev/null
+++ b/usr.sbin/xntpd/xntpd/ntp_filegen.c
@@ -0,0 +1,538 @@
+/*
+ * ntp_filegen.c,v 3.12 1994/01/25 19:06:11 kardel Exp
+ *
+ * implements file generations support for NTP
+ * logfiles and statistic files
+ *
+ *
+ * Copyright (c) 1992
+ * Rainer Pruy Friedrich-Alexander Unuiversitaet Erlangen-Nuernberg
+ *
+ * This code may be modified and used freely
+ * provided credits remain intact.
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <errno.h>
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_string.h"
+#include "ntp_calendar.h"
+#include "ntp_filegen.h"
+#include "ntp_stdlib.h"
+
+/*
+ * NTP is intended to run long periods of time without restart.
+ * Thus log and statistic files generated by NTP will grow large.
+ *
+ * this set of routines provides a central interface
+ * to generating files using file generations
+ *
+ * the generation of a file is changed according to file generation type
+ */
+
+
+/*
+ * to check reason on open failure
+ */
+extern int errno;
+
+/*
+ * imported from timer
+ */
+extern u_long current_time;
+
+/*
+ * redefine this if your system dislikes filename suffixes like
+ * X.19910101 or X.1992W50 or ....
+ */
+#define SUFFIX_SEP '.'
+
+/*
+ * other constants
+ */
+#define FGEN_AGE_SECS (24*60*60) /* life time of FILEGEN_AGE in seconds */
+
+#ifdef DEBUG
+extern int debug;
+#endif
+
+static void filegen_open P((FILEGEN *, u_long));
+static int valid_fileref P((char *, char *));
+#ifdef UNUSED
+static FILEGEN *filegen_unregister P((char *));
+#endif /* UNUSED */
+
+/*
+ * open a file generation according to the current settings of gen
+ * will also provide a link to basename if requested to do so
+ */
+
+static void
+filegen_open(gen, newid)
+ FILEGEN *gen;
+ u_long newid;
+{
+ char *filename;
+ char *basename;
+ u_int len;
+ FILE *fp;
+ struct calendar cal;
+
+ len = strlen(gen->prefix) + strlen(gen->basename) + 1;
+ basename = emalloc(len);
+ sprintf(basename, "%s%s", gen->prefix, gen->basename);
+
+ switch(gen->type) {
+ default:
+ syslog(LOG_ERR, "unsupported file generations type %d for \"%s\" - reverting to FILEGEN_NONE",
+ gen->type, basename);
+ gen->type = FILEGEN_NONE;
+
+ /*FALLTHROUGH*/
+ case FILEGEN_NONE:
+ filename = emalloc(len);
+ sprintf(filename,"%s", basename);
+ break;
+
+ case FILEGEN_PID:
+ filename = emalloc(len + 1 + 1 + 10);
+ sprintf(filename,"%s%c#%ld", basename, SUFFIX_SEP, newid);
+ break;
+
+ case FILEGEN_DAY:
+ /* You can argue here in favor of using MJD, but
+ * I would assume it to be easier for humans to interpret dates
+ * in a format they are used to in everyday life.
+ */
+ caljulian(newid,&cal);
+ filename = emalloc(len + 1 + 4 + 2 + 2);
+ sprintf(filename, "%s%c%04d%02d%02d",
+ basename, SUFFIX_SEP, cal.year, cal.month, cal.monthday);
+ break;
+
+ case FILEGEN_WEEK:
+ /*
+ * This is still a hack
+ * - the term week is not correlated to week as it is used
+ * normally - it just refers to a period of 7 days
+ * starting at Jan 1 - 'weeks' are counted starting from zero
+ */
+ caljulian(newid,&cal);
+ filename = emalloc(len + 1 + 4 + 1 + 2);
+ sprintf(filename, "%s%c%04dw%02d",
+ basename, SUFFIX_SEP, cal.year, cal.yearday / 7);
+ break;
+
+ case FILEGEN_MONTH:
+ caljulian(newid,&cal);
+ filename = emalloc(len + 1 + 4 + 2);
+ sprintf(filename, "%s%c%04d%02d",
+ basename, SUFFIX_SEP, cal.year, cal.month);
+ break;
+
+ case FILEGEN_YEAR:
+ caljulian(newid,&cal);
+ filename = emalloc(len + 1 + 4);
+ sprintf(filename, "%s%c%04d", basename, SUFFIX_SEP, cal.year);
+ break;
+
+ case FILEGEN_AGE:
+ filename = emalloc(len + 1 + 2 + 10);
+ sprintf(filename, "%s%ca%08ld", basename, SUFFIX_SEP, newid);
+ break;
+ }
+
+ if (gen->type != FILEGEN_NONE) {
+ /*
+ * check for existence of a file with name 'basename'
+ * as we disallow such a file
+ * if FGEN_FLAG_LINK is set create a link
+ */
+ struct stat stats;
+ /*
+ * try to resolve name collisions
+ */
+ static u_long conflicts = 0;
+
+#ifndef S_ISREG
+#define S_ISREG(mode) (((mode) & S_IFREG) == S_IFREG)
+#endif
+ if (stat(basename, &stats) == 0) {
+ /* Hm, file exists... */
+ if (S_ISREG(stats.st_mode)) {
+ if (stats.st_nlink <= 1) {
+ /*
+ * Oh, it is not linked - try to save it
+ */
+ char *savename = emalloc(len + 1 + 1 + 10 + 10);
+ sprintf(savename, "%s%c%dC%lu",
+ basename, SUFFIX_SEP, getpid(),
+ (u_long)conflicts++);
+ if (rename(basename, savename) != 0)
+ syslog(LOG_ERR," couldn't save %s: %m", basename);
+ free(savename);
+ } else {
+ /*
+ * there is at least a second link tpo this file
+ * just remove the conflicting one
+ */
+ if (unlink(basename) != 0)
+ syslog(LOG_ERR, "couldn't unlink %s: %m", basename);
+ }
+ } else {
+ /*
+ * Ehh? Not a regular file ?? strange !!!!
+ */
+ syslog(LOG_ERR, "expected regular file for %s (found mode 0%o)",
+ basename, stats.st_mode);
+ }
+ } else {
+ /*
+ * stat(..) failed, but it is absolutely correct for
+ * 'basename' not to exist
+ */
+ if (errno != ENOENT)
+ syslog(LOG_ERR,"stat(%s) failed: %m", basename);
+ }
+ }
+
+ /*
+ * now, try to open new file generation...
+ */
+ fp = fopen(filename, "a");
+
+#ifdef DEBUG
+ if (debug > 3)
+ printf("opening filegen (type=%d/id=%lu) \"%s\"\n",
+ gen->type, (u_long)newid, filename);
+#endif
+
+ if (fp == NULL) {
+ /* open failed -- keep previous state
+ *
+ * If the file was open before keep the previous generation.
+ * This will cause output to end up in the 'wrong' file,
+ * but I think this is still better than loosing output
+ *
+ * ignore errors due to missing directories
+ */
+
+ if (errno != ENOENT)
+ syslog(LOG_ERR, "can't open %s: %m", filename);
+ } else {
+ if (gen->fp != NULL) {
+ fclose(gen->fp);
+ }
+ gen->fp = fp;
+ gen->id = newid;
+
+ if (gen->flag & FGEN_FLAG_LINK) {
+ /*
+ * need to link file to basename
+ * have to use hardlink for now as I want to allow
+ * gen->basename spanning directory levels
+ * this would make it more complex to get the correct filename
+ * for symlink
+ *
+ * Ok, it would just mean taking the part following the last '/'
+ * in the name.... Should add it later....
+ */
+
+ if (link(filename, basename) != 0) {
+ if (errno != EEXIST)
+ syslog(LOG_ERR, "can't link(%s, %s): %m", filename, basename);
+ }
+
+ } /*flags & FGEN_FLAG_LINK*/
+ } /*else fp == NULL*/
+
+ free(basename);
+ free(filename);
+ return;
+}
+
+/*
+ * this function sets up gen->fp to point to the correct
+ * generation of the file for the time specified by 'now'
+ *
+ * 'now' usually is interpreted as second part of a l_fp as is in the cal...
+ * library routines
+ */
+
+void
+filegen_setup(gen,now)
+ FILEGEN *gen;
+ u_long now;
+{
+ u_long new_gen = ~0;
+ struct calendar cal;
+
+ if (!(gen->flag & FGEN_FLAG_ENABLED)) {
+ if (gen->fp != NULL)
+ fclose(gen->fp);
+ return;
+ }
+
+ switch (gen->type) {
+ case FILEGEN_NONE:
+ if (gen->fp != NULL) return; /* file already open */
+ break;
+
+ case FILEGEN_PID:
+ new_gen = getpid();
+ break;
+
+ case FILEGEN_DAY:
+ caljulian(now, &cal);
+ cal.hour = cal.minute = cal.second = 0;
+ new_gen = caltontp(&cal);
+ break;
+
+ case FILEGEN_WEEK:
+ /* Would be nice to have a calweekstart() routine */
+ /* so just use a hack ... */
+ /* just round time to integral 7 days period for actual year */
+ new_gen = now - (now - calyearstart(now)) % TIMES7(SECSPERDAY)
+ + 60;
+ /*
+ * just to be sure -
+ * the computation above would fail in the presence of leap seconds
+ * so at least carry the date to the next day (+60 (seconds))
+ * and go back to the start of the day via calendar computations
+ */
+ caljulian(new_gen, &cal);
+ cal.hour = cal.minute = cal.second = 0;
+ new_gen = caltontp(&cal);
+ break;
+
+ case FILEGEN_MONTH:
+ caljulian(now, &cal);
+ cal.yearday -= cal.monthday - 1;
+ cal.monthday = 1;
+ cal.hour = cal.minute = cal.second = 0;
+ new_gen = caltontp(&cal);
+ break;
+
+ case FILEGEN_YEAR:
+ new_gen = calyearstart(now);
+ break;
+
+ case FILEGEN_AGE:
+ new_gen = current_time - (current_time % FGEN_AGE_SECS);
+ break;
+ }
+ /*
+ * try to open file if not yet open
+ * reopen new file generation file on change of generation id
+ */
+ if (gen->fp == NULL || gen->id != new_gen) {
+ filegen_open(gen, new_gen);
+ }
+}
+
+
+/*
+ * change settings for filegen files
+ */
+void
+filegen_config(gen,basename,type,flag)
+ FILEGEN *gen;
+ char *basename;
+ u_int type;
+ u_int flag;
+{
+ /*
+ * if nothing would be changed...
+ */
+ if ((basename == gen->basename || strcmp(basename,gen->basename) == 0) &&
+ type == gen->type &&
+ flag == gen->flag)
+ return;
+
+ /*
+ * validate parameters
+ */
+ if (!valid_fileref(gen->prefix,basename))
+ return;
+
+ if (gen->fp != NULL)
+ fclose(gen->fp);
+
+#ifdef DEBUG
+ if (debug > 2)
+ printf("configuring filegen:\n\tprefix:\t%s\n\tbasename:\t%s -> %s\n\ttype:\t%d -> %d\n\tflag: %x -> %x\n",
+ gen->prefix, gen->basename, basename, gen->type, type, gen->flag, flag);
+#endif
+ if (gen->basename != basename || strcmp(gen->basename, basename) != 0) {
+ free(gen->basename);
+ gen->basename = emalloc(strlen(basename) + 1);
+ strcpy(gen->basename, basename);
+ }
+ gen->type = type;
+ gen->flag = flag;
+
+ /*
+ * make filegen use the new settings
+ * special action is only required when a generation file
+ * is currently open
+ * otherwise the new settings will be used anyway at the next open
+ */
+ if (gen->fp != NULL) {
+ l_fp now;
+
+ gettstamp(&now);
+ filegen_setup(gen, now.l_ui);
+ }
+}
+
+
+/*
+ * check whether concatenating prefix and basename
+ * yields a legal filename
+ */
+static int
+valid_fileref(prefix,basename)
+ char *prefix, *basename;
+{
+ /*
+ * prefix cannot be changed dynamically
+ * (within the context of filegen)
+ * so just reject basenames containing '..'
+ *
+ * ASSUMPTION:
+ * file system parts 'below' prefix may be
+ * specified without infringement of security
+ *
+ * restricing prefix to legal values
+ * has to be ensured by other means
+ * (however, it would be possible to perform some checks here...)
+ */
+ register char *p = basename;
+
+ /*
+ * Just to catch, dumb errors opening up the world...
+ */
+ if (prefix == NULL || *prefix == '\0')
+ return 0;
+
+ if (basename == NULL)
+ return 0;
+
+ for (p = basename; p; p = strchr(p, '/')) {
+ if (*p == '.' && *(p+1) == '.' && (*(p+2) == '\0' || *(p+2) == '/'))
+ return 0;
+ }
+
+ return 1;
+}
+
+
+/*
+ * filegen registry
+ */
+
+
+static struct filegen_entry {
+ char *name;
+ FILEGEN *filegen;
+ struct filegen_entry *next;
+} *filegen_registry = NULL;
+
+
+FILEGEN *
+filegen_get(name)
+ char *name;
+{
+ struct filegen_entry *f = filegen_registry;
+
+ while(f) {
+ if (f->name == name || strcmp(name, f->name) == 0) {
+#ifdef XXX /* this gives the Alpha compiler fits */
+ if (debug > 3)
+ printf("filegen_get(\"%s\") = %x\n", name,
+ (u_int)f->filegen);
+#endif
+ return f->filegen;
+ }
+ f = f->next;
+ }
+#ifdef DEBUG
+ if (debug > 3)
+ printf("filegen_get(\"%s\") = NULL\n", name);
+#endif
+ return NULL;
+}
+
+void
+filegen_register(name, filegen)
+ char *name;
+ FILEGEN *filegen;
+{
+ struct filegen_entry **f = &filegen_registry;
+
+#ifdef XXX /* this gives the Alpha compiler fits */
+ if (debug > 3)
+ printf("filegen_register(\"%s\",%x)\n", name, (u_int)filegen);
+#endif
+ while (*f) {
+ if ((*f)->name == name || strcmp(name, (*f)->name) == 0) {
+#ifdef XXX /* this gives the Alpha compiler fits */
+ if (debug > 4) {
+ printf("replacing filegen %x\n", (u_int)(*f)->filegen);
+ }
+#endif
+ (*f)->filegen = filegen;
+ return;
+ }
+ f = &((*f)->next);
+ }
+
+ *f = (struct filegen_entry *) emalloc(sizeof(struct filegen_entry));
+ if (*f) {
+ (*f)->next = NULL;
+ (*f)->name = emalloc(strlen(name) + 1);
+ strcpy((*f)->name, name);
+ (*f)->filegen = filegen;
+#ifdef DEBUG
+ if (debug > 5) {
+ printf("adding new filegen\n");
+ }
+#endif
+ }
+
+ return;
+}
+
+#ifdef UNUSED
+static FILEGEN *
+filegen_unregister(name)
+ char *name;
+{
+ struct filegen_entry **f = &filegen_registry;
+
+#ifdef DEBUG
+ if (debug > 3)
+ printf("filegen_unregister(\"%s\")\n", name);
+#endif
+
+ while (*f) {
+ if (strcmp((*f)->name,name) == 0) {
+ struct filegen_entry *ff = *f;
+ FILEGEN *fg;
+
+ *f = (*f)->next;
+ fg = ff->filegen;
+ free(ff->name);
+ free(ff);
+ return fg;
+ }
+ f = &((*f)->next);
+ }
+ return NULL;
+}
+#endif /* UNUSED */
+
diff --git a/usr.sbin/xntpd/xntpd/ntp_intres.c b/usr.sbin/xntpd/xntpd/ntp_intres.c
new file mode 100644
index 0000000..62d3792
--- /dev/null
+++ b/usr.sbin/xntpd/xntpd/ntp_intres.c
@@ -0,0 +1,799 @@
+/*
+ * ripped off from ../xnptres/xntpres.c by Greg Troxel 4/2/92
+ * routine callable from xntpd, rather than separate program
+ * also, key info passed in via a global, so no key file needed.
+ */
+
+/*
+ * xntpres - process configuration entries which require use of the resolver
+ *
+ * This is meant to be run by xntpd on the fly. It is not guaranteed
+ * to work properly if run by hand. This is actually a quick hack to
+ * stave off violence from people who hate using numbers in the
+ * configuration file (at least I hope the rest of the daemon is
+ * better than this). Also might provide some ideas about how one
+ * might go about autoconfiguring an NTP distribution network.
+ *
+ */
+
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <netdb.h>
+#include <signal.h>
+#include <errno.h>
+
+#include "ntpd.h"
+#include "ntp_select.h"
+#include "ntp_io.h"
+#include "ntp_request.h"
+#include "ntp_stdlib.h"
+#include "ntp_syslog.h"
+
+#define STREQ(a, b) (*(a) == *(b) && strcmp((a), (b)) == 0)
+
+/*
+ * Each item we are to resolve and configure gets one of these
+ * structures defined for it.
+ */
+struct conf_entry {
+ struct conf_entry *ce_next;
+ char *ce_name; /* name we are trying to resolve */
+ struct conf_peer ce_config; /* configuration info for peer */
+};
+#define ce_peeraddr ce_config.peeraddr
+#define ce_hmode ce_config.hmode
+#define ce_version ce_config.version
+#define ce_minpoll ce_config.minpoll
+#define ce_maxpoll ce_config.maxpoll
+#define ce_flags ce_config.flags
+#define ce_ttl ce_config.ttl
+#define ce_keyid ce_config.keyid
+
+/*
+ * confentries is a pointer to the list of configuration entries
+ * we have left to do.
+ */
+struct conf_entry *confentries = NULL;
+
+/*
+ * We take an interrupt every thirty seconds, at which time we decrement
+ * config_timer and resolve_timer. The former is set to 2, so we retry
+ * unsucessful reconfigurations every minute. The latter is set to
+ * an exponentially increasing value which starts at 2 and increases to
+ * 32. When this expires we retry failed name resolutions.
+ *
+ * We sleep SLEEPTIME seconds before doing anything, to give the server
+ * time to arrange itself.
+ */
+#define MINRESOLVE 2
+#define MAXRESOLVE 32
+#define CONFIG_TIME 2
+#define ALARM_TIME 30
+
+#define SLEEPTIME 2
+
+static int config_timer = 0;
+static int resolve_timer = 0;
+
+static int resolve_value; /* next value of resolve timer */
+
+/*
+ * Big hack attack
+ */
+#define LOCALHOST 0x7f000001 /* 127.0.0.1, in hex, of course */
+#define SKEWTIME 0x08000000 /* 0.03125 seconds as a l_fp fraction */
+
+/*
+ * Select time out. Set to 2 seconds. The server is on the local machine,
+ * after all.
+ */
+#define TIMEOUT_SEC 2
+#define TIMEOUT_USEC 0
+
+
+/*
+ * Input processing. The data on each line in the configuration file
+ * is supposed to consist of entries in the following order
+ */
+#define TOK_HOSTNAME 0
+#define TOK_HMODE 1
+#define TOK_VERSION 2
+#define TOK_MINPOLL 3
+#define TOK_MAXPOLL 4
+#define TOK_FLAGS 5
+#define TOK_TTL 6
+#define TOK_KEYID 7
+#define NUMTOK 8
+
+#define MAXLINESIZE 512
+
+
+/*
+ * File descriptor for ntp request code.
+ */
+static int sockfd = -1;
+
+
+/* stuff to be filled in by caller */
+
+u_long req_keyid; /* request keyid */
+char *req_file; /* name of the file with configuration info */
+
+/* end stuff to be filled in */
+
+
+extern int debug; /* use global debug flag */
+extern int errno;
+
+static RETSIGTYPE bong P((int));
+static void checkparent P((void));
+static void removeentry P((struct conf_entry *));
+static void addentry P((char *, int, int, int, int, int, int, u_long));
+static int findhostaddr P((struct conf_entry *));
+static void openntp P((void));
+static int request P((struct conf_peer *));
+static char * nexttoken P((char **));
+static void readconf P((FILE *, char *));
+static void doconfigure P((int));
+
+/*
+ * assumes: req_key, req_keyid, conffile valid
+ * syslog still open
+ */
+void
+ntp_intres()
+{
+ FILE *in;
+
+#ifdef DEBUG
+ if ( debug )
+ syslog(LOG_INFO, "ntp_intres running");
+#endif
+
+ /* check out auth stuff */
+ if (!authhavekey(req_keyid)) {
+ syslog(LOG_ERR, "request keyid %lu not found",
+ req_keyid );
+ exit(1);
+ }
+
+ /*
+ * Read the configuration info
+ * {this is bogus, since we are forked, but it is easier
+ * to keep this code - gdt}
+ */
+ if ((in = fopen(req_file, "r")) == NULL) {
+ syslog(LOG_ERR, "can't open configuration file %s: %m",
+ req_file);
+ exit(1);
+ }
+ readconf(in, req_file);
+ (void) fclose(in);
+
+ if (!debug )
+ (void) unlink(req_file);
+
+ /*
+ * Sleep a little to make sure the server is completely up
+ */
+ sleep(SLEEPTIME);
+
+ /*
+ * Make a first cut at resolving the bunch
+ */
+ doconfigure(1);
+ if (confentries == NULL)
+ exit(0); /* done that quick */
+
+ /*
+ * Here we've got some problem children. Set up the timer
+ * and wait for it.
+ */
+ resolve_value = resolve_timer = MINRESOLVE;
+ config_timer = CONFIG_TIME;
+ (void) signal_no_reset(SIGALRM, bong);
+ alarm(ALARM_TIME);
+
+ for (;;) {
+ if (confentries == NULL)
+ exit(0);
+ checkparent();
+ if (resolve_timer == 0) {
+ if (resolve_value < MAXRESOLVE)
+ resolve_value <<= 1;
+ resolve_timer = resolve_value;
+ config_timer = CONFIG_TIME;
+ doconfigure(1);
+ continue;
+ } else if (config_timer == 0) {
+ config_timer = CONFIG_TIME;
+ doconfigure(0);
+ continue;
+ }
+ /*
+ * There is a race in here. Is okay, though, since
+ * all it does is delay things by 30 seconds.
+ */
+ (void) pause();
+ }
+}
+
+
+/*
+ * bong - service and reschedule an alarm() interrupt
+ */
+static RETSIGTYPE
+bong(sig)
+int sig;
+{
+ if (config_timer > 0)
+ config_timer--;
+ if (resolve_timer > 0)
+ resolve_timer--;
+ alarm(ALARM_TIME);
+}
+
+
+/*
+ * checkparent - see if our parent process is still running
+ */
+static void
+checkparent()
+{
+ /*
+ * If our parent (the server) has died we will have been
+ * inherited by init. If so, exit.
+ */
+ if (getppid() == 1) {
+ syslog(LOG_INFO, "parent died before we finished, exiting");
+ exit(0);
+ }
+}
+
+
+/*
+ * removeentry - we are done with an entry, remove it from the list
+ */
+static void
+removeentry(entry)
+ struct conf_entry *entry;
+{
+ register struct conf_entry *ce;
+
+ ce = confentries;
+ if (ce == entry) {
+ confentries = ce->ce_next;
+ return;
+ }
+
+ while (ce != NULL) {
+ if (ce->ce_next == entry) {
+ ce->ce_next = entry->ce_next;
+ return;
+ }
+ ce = ce->ce_next;
+ }
+}
+
+
+/*
+ * addentry - add an entry to the configuration list
+ */
+static void
+addentry(name, mode, version, minpoll, maxpoll, flags, ttl, keyid)
+ char *name;
+ int mode;
+ int version;
+ int minpoll;
+ int maxpoll;
+ int flags;
+ int ttl;
+ u_long keyid;
+{
+ register char *cp;
+ register struct conf_entry *ce;
+ int len;
+
+ len = strlen(name) + 1;
+ cp = emalloc((unsigned)len);
+ memmove(cp, name, len);
+
+ ce = (struct conf_entry *)emalloc(sizeof(struct conf_entry));
+ ce->ce_name = cp;
+ ce->ce_peeraddr = 0;
+ ce->ce_hmode = (u_char)mode;
+ ce->ce_version = (u_char)version;
+ ce->ce_minpoll = (u_char)minpoll;
+ ce->ce_maxpoll = (u_char)maxpoll;
+ ce->ce_flags = (u_char)flags;
+ ce->ce_ttl = (u_char)ttl;
+ ce->ce_keyid = keyid;
+ ce->ce_next = NULL;
+
+ if (confentries == NULL) {
+ confentries = ce;
+ } else {
+ register struct conf_entry *cep;
+
+ for (cep = confentries; cep->ce_next != NULL;
+ cep = cep->ce_next)
+ /* nothing */;
+ cep->ce_next = ce;
+ }
+}
+
+
+/*
+ * findhostaddr - resolve a host name into an address
+ *
+ * The routine sticks the address into the entry's ce_peeraddr if it
+ * gets one. It returns 1 for "success" and 0 for an uncorrectable
+ * failure. Note that "success" includes try again errors. You can
+ * tell that you got a try again since ce_peeraddr will still be zero.
+ */
+static int
+findhostaddr(entry)
+ struct conf_entry *entry;
+{
+ struct hostent *hp;
+
+ checkparent(); /* make sure our guy is still running */
+
+ hp = gethostbyname(entry->ce_name);
+
+ if (hp == NULL) {
+#ifndef NODNS
+ /*
+ * If the resolver is in use, see if the failure is
+ * temporary. If so, return success.
+ */
+ extern int h_errno;
+
+ if (h_errno == TRY_AGAIN)
+ return (1);
+#endif
+ return (0);
+ }
+
+ /*
+ * Use the first address. We don't have any way to
+ * tell preferences and older gethostbyname() implementations
+ * only return one.
+ */
+ memmove((char *)&(entry->ce_peeraddr),
+ (char *)hp->h_addr,
+ sizeof(struct in_addr));
+ return (1);
+}
+
+
+/*
+ * openntp - open a socket to the ntp server
+ */
+static void
+openntp()
+{
+ struct sockaddr_in saddr;
+
+ if (sockfd >= 0)
+ return;
+
+ sockfd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sockfd == -1) {
+ syslog(LOG_ERR, "socket() failed: %m");
+ exit(1);
+ }
+
+ memset((char *)&saddr, 0, sizeof(saddr));
+ saddr.sin_family = AF_INET;
+ saddr.sin_port = htons(NTP_PORT); /* trash */
+ saddr.sin_addr.s_addr = htonl(LOCALHOST); /* garbage */
+
+ /*
+ * Make the socket non-blocking. We'll wait with select()
+ */
+#if defined(O_NONBLOCK)
+ if (fcntl(sockfd, F_SETFL, O_NONBLOCK) == -1) {
+ syslog(LOG_ERR, "fcntl(O_NONBLOCK) failed: %m");
+ exit(1);
+ }
+#else
+#if defined(FNDELAY)
+ if (fcntl(sockfd, F_SETFL, FNDELAY) == -1) {
+ syslog(LOG_ERR, "fcntl(FNDELAY) failed: %m");
+ exit(1);
+ }
+#else
+NEED NON BLOCKING IO
+#endif
+#endif
+
+
+ if (connect(sockfd, (struct sockaddr *)&saddr, sizeof(saddr)) == -1) {
+ syslog(LOG_ERR, "connect() failed: %m");
+ exit(1);
+ }
+}
+
+
+/*
+ * request - send a configuration request to the server, wait for a response
+ */
+static int
+request(conf)
+ struct conf_peer *conf;
+{
+ fd_set fdset;
+ struct timeval tvout;
+ struct req_pkt reqpkt;
+ l_fp ts;
+ int n;
+
+ checkparent(); /* make sure our guy is still running */
+
+ if (sockfd < 0)
+ openntp();
+
+ /*
+ * Try to clear out any previously received traffic so it
+ * doesn't fool us. Note the socket is nonblocking.
+ */
+ tvout.tv_sec = 0;
+ tvout.tv_usec = 0;
+ FD_ZERO(&fdset);
+ FD_SET(sockfd, &fdset);
+ while (select(sockfd + 1, &fdset, (fd_set *)0, (fd_set *)0, &tvout) >
+ 0) {
+ read(sockfd, (char *)&reqpkt, REQ_LEN_MAC);
+ FD_ZERO(&fdset);
+ FD_SET(sockfd, &fdset);
+ }
+
+ /*
+ * Make up a request packet with the configuration info
+ */
+ memset((char *)&reqpkt, 0, sizeof(reqpkt));
+
+ reqpkt.rm_vn_mode = RM_VN_MODE(0, 0);
+ reqpkt.auth_seq = AUTH_SEQ(1, 0); /* authenticated, no seq */
+ reqpkt.implementation = IMPL_XNTPD; /* local implementation */
+ reqpkt.request = REQ_CONFIG; /* configure a new peer */
+ reqpkt.err_nitems = ERR_NITEMS(0, 1); /* one item */
+ reqpkt.mbz_itemsize = MBZ_ITEMSIZE(sizeof(struct conf_peer));
+ memmove(reqpkt.data, (char *)conf, sizeof(struct conf_peer));
+ reqpkt.keyid = htonl(req_keyid);
+
+ auth1crypt(req_keyid, (U_LONG *)&reqpkt, REQ_LEN_NOMAC);
+ gettstamp(&ts);
+ L_ADDUF(&ts, SKEWTIME);
+ HTONL_FP(&ts, &reqpkt.tstamp);
+ n = auth2crypt(req_keyid, (U_LONG *)&reqpkt, REQ_LEN_NOMAC);
+
+ /*
+ * Done. Send it.
+ */
+ n = write(sockfd, (char *)&reqpkt, REQ_LEN_NOMAC + n);
+ if (n < 0) {
+ syslog(LOG_ERR, "send to NTP server failed: %m");
+ return 0; /* maybe should exit */
+ }
+
+ /*
+ * Wait for a response. A weakness of the mode 7 protocol used
+ * is that there is no way to associate a response with a
+ * particular request, i.e. the response to this configuration
+ * request is indistinguishable from that to any other. I should
+ * fix this some day. In any event, the time out is fairly
+ * pessimistic to make sure that if an answer is coming back
+ * at all, we get it.
+ */
+ for (;;) {
+ FD_ZERO(&fdset);
+ FD_SET(sockfd, &fdset);
+ tvout.tv_sec = TIMEOUT_SEC;
+ tvout.tv_usec = TIMEOUT_USEC;
+
+ n = select(sockfd + 1, &fdset, (fd_set *)0,
+ (fd_set *)0, &tvout);
+
+ if (n <= 0) {
+ if (n < 0)
+ syslog(LOG_ERR, "select() fails: %m");
+ return 0;
+ }
+
+ n = read(sockfd, (char *)&reqpkt, REQ_LEN_MAC);
+ if (n <= 0) {
+ if (n < 0) {
+ syslog(LOG_ERR, "read() fails: %m");
+ return 0;
+ }
+ continue;
+ }
+
+ /*
+ * Got one. Check through to make sure it is what
+ * we expect.
+ */
+ if (n < RESP_HEADER_SIZE) {
+ syslog(LOG_ERR, "received runt response (%d octets)",
+ n);
+ continue;
+ }
+
+ if (!ISRESPONSE(reqpkt.rm_vn_mode)) {
+#ifdef DEBUG
+ if (debug > 1)
+ printf("received non-response packet\n");
+#endif
+ continue;
+ }
+
+ if (ISMORE(reqpkt.rm_vn_mode)) {
+#ifdef DEBUG
+ if (debug > 1)
+ printf("received fragmented packet\n");
+#endif
+ continue;
+ }
+
+ if (INFO_VERSION(reqpkt.rm_vn_mode) != NTP_VERSION
+ || INFO_MODE(reqpkt.rm_vn_mode) != MODE_PRIVATE) {
+#ifdef DEBUG
+ if (debug > 1)
+ printf("version (%d) or mode (%d) incorrect\n",
+ INFO_VERSION(reqpkt.rm_vn_mode),
+ INFO_MODE(reqpkt.rm_vn_mode));
+#endif
+ continue;
+ }
+
+ if (INFO_SEQ(reqpkt.auth_seq) != 0) {
+#ifdef DEBUG
+ if (debug > 1)
+ printf("nonzero sequence number (%d)\n",
+ INFO_SEQ(reqpkt.auth_seq));
+#endif
+ continue;
+ }
+
+ if (reqpkt.implementation != IMPL_XNTPD ||
+ reqpkt.request != REQ_CONFIG) {
+#ifdef DEBUG
+ if (debug > 1)
+ printf(
+ "implementation (%d) or request (%d) incorrect\n",
+ reqpkt.implementation, reqpkt.request);
+#endif
+ continue;
+ }
+
+ if (INFO_NITEMS(reqpkt.err_nitems) != 0 ||
+ INFO_MBZ(reqpkt.mbz_itemsize) != 0 ||
+ INFO_ITEMSIZE(reqpkt.mbz_itemsize != 0)) {
+#ifdef DEBUG
+ if (debug > 1)
+ printf(
+ "nitems (%d) mbz (%d) or itemsize (%d) nonzero\n",
+ INFO_NITEMS(reqpkt.err_nitems),
+ INFO_MBZ(reqpkt.mbz_itemsize),
+ INFO_ITEMSIZE(reqpkt.mbz_itemsize));
+#endif
+ continue;
+ }
+
+ n = INFO_ERR(reqpkt.err_nitems);
+ switch (n) {
+ case INFO_OKAY:
+ /* success */
+ return 1;
+
+ case INFO_ERR_IMPL:
+ syslog(LOG_ERR,
+ "server reports implementation mismatch!!");
+ return 0;
+
+ case INFO_ERR_REQ:
+ syslog(LOG_ERR,
+ "server claims configuration request is unknown");
+ return 0;
+
+ case INFO_ERR_FMT:
+ syslog(LOG_ERR,
+ "server indicates a format error occured(!!)");
+ return 0;
+
+ case INFO_ERR_NODATA:
+ syslog(LOG_ERR,
+ "server indicates no data available (shouldn't happen)");
+ return 0;
+
+ case INFO_ERR_AUTH:
+ syslog(LOG_ERR,
+ "server returns a permission denied error");
+ return 0;
+
+ default:
+ syslog(LOG_ERR,
+ "server returns unknown error code %d", n);
+ return 0;
+ }
+ }
+}
+
+
+/*
+ * nexttoken - return the next token from a line
+ */
+static char *
+nexttoken(lptr)
+ char **lptr;
+{
+ register char *cp;
+ register char *tstart;
+
+ cp = *lptr;
+
+ /*
+ * Skip leading white space
+ */
+ while (*cp == ' ' || *cp == '\t')
+ cp++;
+
+ /*
+ * If this is the end of the line, return nothing.
+ */
+ if (*cp == '\n' || *cp == '\0') {
+ *lptr = cp;
+ return NULL;
+ }
+
+ /*
+ * Must be the start of a token. Record the pointer and look
+ * for the end.
+ */
+ tstart = cp++;
+ while (*cp != ' ' && *cp != '\t' && *cp != '\n' && *cp != '\0')
+ cp++;
+
+ /*
+ * Terminate the token with a \0. If this isn't the end of the
+ * line, space to the next character.
+ */
+ if (*cp == '\n' || *cp == '\0')
+ *cp = '\0';
+ else
+ *cp++ = '\0';
+
+ *lptr = cp;
+ return tstart;
+}
+
+
+/*
+ * readconf - read the configuration information out of the file we
+ * were passed. Note that since the file is supposed to be
+ * machine generated, we bail out at the first sign of trouble.
+ */
+static void
+readconf(fp, name)
+ FILE *fp;
+ char *name;
+{
+ register int i;
+ char *token[NUMTOK];
+ u_long intval[NUMTOK];
+ int flags;
+ char buf[MAXLINESIZE];
+ char *bp;
+
+ while (fgets(buf, MAXLINESIZE, fp) != NULL) {
+
+ bp = buf;
+ for (i = 0; i < NUMTOK; i++) {
+ if ((token[i] = nexttoken(&bp)) == NULL) {
+ syslog(LOG_ERR,
+ "tokenizing error in file `%s', quitting",
+ name);
+ exit(1);
+ }
+ }
+
+ for (i = 1; i < NUMTOK; i++) {
+ if (!atouint(token[i], &intval[i])) {
+ syslog(LOG_ERR,
+ "format error for integer token `%s', file `%s', quitting",
+ token[i], name);
+ exit(1);
+ }
+ }
+
+ if (intval[TOK_HMODE] != MODE_ACTIVE &&
+ intval[TOK_HMODE] != MODE_CLIENT &&
+ intval[TOK_HMODE] != MODE_BROADCAST) {
+ syslog(LOG_ERR, "invalid mode (%d) in file %s",
+ intval[TOK_HMODE], name);
+ exit(1);
+ }
+
+ if (intval[TOK_VERSION] > NTP_VERSION ||
+ intval[TOK_VERSION] < NTP_OLDVERSION) {
+ syslog(LOG_ERR, "invalid version (%d) in file %s",
+ intval[TOK_VERSION], name);
+ exit(1);
+ }
+ if (intval[TOK_MINPOLL] < NTP_MINPOLL ||
+ intval[TOK_MINPOLL] > NTP_MAXPOLL) {
+ syslog(LOG_ERR, "invalid MINPOLL value (%d) in file %s",
+ intval[TOK_MINPOLL], name);
+ exit(1);
+ }
+
+ if (intval[TOK_MAXPOLL] < NTP_MINPOLL ||
+ intval[TOK_MAXPOLL] > NTP_MAXPOLL) {
+ syslog(LOG_ERR, "invalid MAXPOLL value (%d) in file %s",
+ intval[TOK_MAXPOLL], name);
+ exit(1);
+ }
+
+ if ((intval[TOK_FLAGS] & ~(FLAG_AUTHENABLE|FLAG_PREFER))
+ != 0) {
+ syslog(LOG_ERR, "invalid flags (%d) in file %s",
+ intval[TOK_FLAGS], name);
+ exit(1);
+ }
+
+ flags = 0;
+ if (intval[TOK_FLAGS] & FLAG_AUTHENABLE)
+ flags |= CONF_FLAG_AUTHENABLE;
+ if (intval[TOK_FLAGS] & FLAG_PREFER)
+ flags |= CONF_FLAG_PREFER;
+
+ /*
+ * This is as good as we can check it. Add it in.
+ */
+ addentry(token[TOK_HOSTNAME], (int)intval[TOK_HMODE],
+ (int)intval[TOK_VERSION], (int)intval[TOK_MINPOLL],
+ (int)intval[TOK_MAXPOLL], flags, (int)intval[TOK_TTL],
+ intval[TOK_KEYID]);
+ }
+}
+
+
+/*
+ * doconfigure - attempt to resolve names and configure the server
+ */
+static void
+doconfigure(dores)
+ int dores;
+{
+ register struct conf_entry *ce;
+ register struct conf_entry *ceremove;
+
+ ce = confentries;
+ while (ce != NULL) {
+ if (dores && ce->ce_peeraddr == 0) {
+ if (!findhostaddr(ce)) {
+ syslog(LOG_ERR,
+ "couldn't resolve `%s', giving up on it",
+ ce->ce_name);
+ ceremove = ce;
+ ce = ceremove->ce_next;
+ removeentry(ceremove);
+ continue;
+ }
+ }
+
+ if (ce->ce_peeraddr != 0) {
+ if (request(&ce->ce_config)) {
+ ceremove = ce;
+ ce = ceremove->ce_next;
+ removeentry(ceremove);
+ continue;
+ }
+ }
+ ce = ce->ce_next;
+ }
+}
diff --git a/usr.sbin/xntpd/xntpd/ntp_io.c b/usr.sbin/xntpd/xntpd/ntp_io.c
new file mode 100644
index 0000000..bcf50f9
--- /dev/null
+++ b/usr.sbin/xntpd/xntpd/ntp_io.c
@@ -0,0 +1,1809 @@
+/*
+ * xntp_io.c - input/output routines for xntpd. The socket-opening code
+ * was shamelessly stolen from ntpd.
+ */
+#include <stdio.h>
+#include <signal.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/time.h>
+#ifndef __bsdi__
+#include <netinet/in.h>
+#endif
+#if defined(__bsdi__) || defined(SYS_NETBSD) || defined(SYS_FREEBSD) || defined(SYS_AIX)
+#include <sys/ioctl.h>
+#endif
+
+#include "ntpd.h"
+#include "ntp_select.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_if.h"
+#include "ntp_stdlib.h"
+
+#if defined(MCAST) && !defined(IP_ADD_MEMBERSHIP)
+#undef MCAST
+#endif
+
+#if defined(BSD)&&!defined(sun)&&!defined(SYS_SINIXM)
+#if BSD >= 199006
+#define HAVE_VARIABLE_IFR_LENGTH
+#endif
+#endif
+
+#if !defined(HAVE_VARIABLE_IFR_LENGTH) && defined(AF_LINK) && (defined(_SOCKADR_LEN) || !defined(SYS_DECOSF1))
+#define HAVE_VARIABLE_IFR_LENGTH
+#endif
+
+#if defined(USE_TTY_SIGPOLL)||defined(USE_UDP_SIGPOLL)
+#if defined(SYS_AIX)&&defined(_IO)
+#undef _IO
+#endif
+#include <stropts.h>
+#endif
+
+/*
+ * We do asynchronous input using the SIGIO facility. A number of
+ * recvbuf buffers are preallocated for input. In the signal
+ * handler we poll to see which sockets are ready and read the
+ * packets from them into the recvbuf's along with a time stamp and
+ * an indication of the source host and the interface it was received
+ * through. This allows us to get as accurate receive time stamps
+ * as possible independent of other processing going on.
+ *
+ * We watch the number of recvbufs available to the signal handler
+ * and allocate more when this number drops below the low water
+ * mark. If the signal handler should run out of buffers in the
+ * interim it will drop incoming frames, the idea being that it is
+ * better to drop a packet than to be inaccurate.
+ */
+
+/*
+ * Block the interrupt, for critical sections.
+ */
+#if defined(HAVE_SIGNALED_IO)
+#define BLOCKIO() ((void) block_sigio())
+#define UNBLOCKIO() ((void) unblock_sigio())
+#else
+#define BLOCKIO()
+#define UNBLOCKIO()
+#endif
+
+/*
+ * recvbuf memory management
+ */
+#define RECV_INIT 10 /* 10 buffers initially */
+#define RECV_LOWAT 3 /* when we're down to three buffers get more */
+#define RECV_INC 5 /* get 5 more at a time */
+#define RECV_TOOMANY 30 /* this is way too many buffers */
+
+/*
+ * Memory allocation
+ */
+u_long full_recvbufs; /* number of recvbufs on fulllist */
+u_long free_recvbufs; /* number of recvbufs on freelist */
+
+static struct recvbuf *freelist; /* free buffers */
+static struct recvbuf *fulllist; /* lifo buffers with data */
+static struct recvbuf *beginlist; /* fifo buffers with data */
+
+u_long total_recvbufs; /* total recvbufs currently in use */
+u_long lowater_additions; /* number of times we have added memory */
+
+static struct recvbuf initial_bufs[RECV_INIT]; /* initial allocation */
+
+
+/*
+ * Other statistics of possible interest
+ */
+u_long packets_dropped; /* total number of packets dropped on reception */
+u_long packets_ignored; /* packets received on wild card interface */
+u_long packets_received; /* total number of packets received */
+u_long packets_sent; /* total number of packets sent */
+u_long packets_notsent; /* total number of packets which couldn't be sent */
+
+u_long handler_calls; /* number of calls to interrupt handler */
+u_long handler_pkts; /* number of pkts received by handler */
+u_long io_timereset; /* time counters were reset */
+
+/*
+ * Interface stuff
+ */
+#define MAXINTERFACES 192 /* much better for big gateways with IP/X.25 and more ... */
+struct interface *any_interface; /* pointer to default interface */
+struct interface *loopback_interface; /* point to loopback interface */
+static struct interface inter_list[MAXINTERFACES];
+static int ninterfaces;
+
+#ifdef REFCLOCK
+/*
+ * Refclock stuff. We keep a chain of structures with data concerning
+ * the guys we are doing I/O for.
+ */
+static struct refclockio *refio;
+#endif
+
+/*
+ * File descriptor masks etc. for call to select
+ */
+fd_set activefds;
+int maxactivefd;
+
+/*
+ * Imported from ntp_timer.c
+ */
+extern u_long current_time;
+
+extern int errno;
+extern int debug;
+
+static int create_sockets P((u_int));
+static int open_socket P((struct sockaddr_in *, int));
+static void close_socket P((int));
+#ifdef HAVE_SIGNALED_IO
+static int init_clock_sig P(());
+static void init_socket_sig P((int));
+static void set_signal P(());
+static RETSIGTYPE sigio_handler P((int));
+static void block_sigio P((void));
+static void unblock_sigio P(());
+#endif
+#ifndef STREAMS_TLI
+extern char *inet_ntoa P((struct in_addr));
+#endif /* STREAMS_TLI */
+
+/*
+ * init_io - initialize I/O data structures and call socket creation routine
+ */
+void
+init_io()
+{
+ register int i;
+
+ /*
+ * Init buffer free list and stat counters
+ */
+ freelist = 0;
+ for (i = 0; i < RECV_INIT; i++) {
+ initial_bufs[i].next = freelist;
+ freelist = &initial_bufs[i];
+ }
+
+ fulllist = 0;
+ free_recvbufs = total_recvbufs = RECV_INIT;
+ full_recvbufs = lowater_additions = 0;
+ packets_dropped = packets_received = 0;
+ packets_ignored = 0;
+ packets_sent = packets_notsent = 0;
+ handler_calls = handler_pkts = 0;
+ io_timereset = 0;
+ loopback_interface = 0;
+
+#ifdef REFCLOCK
+ refio = 0;
+#endif
+
+#if defined(HAVE_SIGNALED_IO)
+ (void) set_signal();
+#endif
+
+ /*
+ * Create the sockets
+ */
+ BLOCKIO();
+ (void) create_sockets(htons(NTP_PORT));
+ UNBLOCKIO();
+
+#ifdef DEBUG
+ if (debug)
+ printf("init_io: maxactivefd %d\n", maxactivefd);
+#endif
+}
+
+/*
+ * create_sockets - create a socket for each interface plus a default
+ * socket for when we don't know where to send
+ */
+static int
+create_sockets(port)
+ u_int port;
+{
+#ifdef STREAMS_TLI
+ struct strioctl ioc;
+#endif /* STREAMS_TLI */
+ char buf[MAXINTERFACES*sizeof(struct ifreq)];
+ struct ifconf ifc;
+ struct ifreq ifreq, *ifr;
+ int n, i, j, vs, size;
+ struct sockaddr_in resmask;
+
+#ifdef DEBUG
+ if (debug)
+ printf("create_sockets(%d)\n", ntohs(port));
+#endif
+
+ /*
+ * create pseudo-interface with wildcard address
+ */
+ inter_list[0].sin.sin_family = AF_INET;
+ inter_list[0].sin.sin_port = port;
+ inter_list[0].sin.sin_addr.s_addr = htonl(INADDR_ANY);
+ (void) strncpy(inter_list[0].name, "wildcard",
+ sizeof(inter_list[0].name));
+ inter_list[0].mask.sin_addr.s_addr = htonl(~0);
+ inter_list[0].received = 0;
+ inter_list[0].sent = 0;
+ inter_list[0].notsent = 0;
+ inter_list[0].flags = INT_BROADCAST;
+
+#ifdef USE_STREAMS_DEVICE_FOR_IF_CONFIG
+ if ((vs = open("/dev/ip", O_RDONLY)) < 0) {
+#else /* ! USE_STREAMS_DEVICE_FOR_IF_CONFIG */
+ if ((vs = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+#endif /* USE_STREAMS_DEVICE_FOR_IF_CONFIG */
+ syslog(LOG_ERR, "vs=socket(AF_INET, SOCK_DGRAM) %m");
+ exit(1);
+ }
+
+ i = 1;
+
+ ifc.ifc_len = sizeof(buf);
+#ifdef STREAMS_TLI
+ ioc.ic_cmd = SIOCGIFCONF;
+ ioc.ic_timout = 0;
+ ioc.ic_dp = (caddr_t)buf;
+ ioc.ic_len = sizeof(buf);
+ if(ioctl(vs, I_STR, &ioc) < 0 ||
+ ioc.ic_len < sizeof(struct ifreq)) {
+ syslog(LOG_ERR, "get interface configuration: %m");
+ exit(1);
+ }
+#ifdef SIZE_RETURNED_IN_BUFFER
+ ifc.ifc_len = ioc.ic_len - sizeof(int);
+ ifc.ifc_buf = buf + sizeof(int);
+#else /* ! SIZE_RETURNED_IN_BUFFER */
+ ifc.ifc_len = ioc.ic_len;
+ ifc.ifc_buf = buf;
+#endif /* SIZE_RETURNED_IN_BUFFER */
+
+#else /* ! STREAMS_TLI */
+ ifc.ifc_len = sizeof(buf);
+ ifc.ifc_buf = buf;
+ if (ioctl(vs, SIOCGIFCONF, (char *)&ifc) < 0) {
+ syslog(LOG_ERR, "get interface configuration: %m");
+ exit(1);
+ }
+#endif /* STREAMS_TLI */
+
+ for(n = ifc.ifc_len, ifr = ifc.ifc_req; n > 0;
+ ifr = (struct ifreq *)((char *)ifr + size)) {
+ size = sizeof(*ifr);
+
+#ifdef HAVE_VARIABLE_IFR_LENGTH
+ if (ifr->ifr_addr.sa_len > sizeof(ifr->ifr_addr))
+ size += ifr->ifr_addr.sa_len - sizeof(struct sockaddr);
+#endif
+ n -= size;
+ if (ifr->ifr_addr.sa_family != AF_INET)
+ continue;
+ ifreq = *ifr;
+#ifdef STREAMS_TLI
+ ioc.ic_cmd = SIOCGIFFLAGS;
+ ioc.ic_timout = 0;
+ ioc.ic_dp = (caddr_t)&ifreq;
+ ioc.ic_len = sizeof(struct ifreq);
+ if(ioctl(vs, I_STR, &ioc)) {
+#else /* ! STREAMS_TLI */
+ if (ioctl(vs, SIOCGIFFLAGS, (char *)&ifreq) < 0) {
+#endif /* STREAMS_TLI */
+ syslog(LOG_ERR, "get interface flags: %m");
+ continue;
+ }
+ if ((ifreq.ifr_flags & IFF_UP) == 0)
+ continue;
+ inter_list[i].flags = 0;
+ if (ifreq.ifr_flags & IFF_BROADCAST)
+ inter_list[i].flags |= INT_BROADCAST;
+#if !defined(SUN_3_3_STINKS)
+#if defined(SYS_HPUX) && (SYS_HPUX < 8)
+ if (ifreq.ifr_flags & IFF_LOCAL_LOOPBACK)
+#else
+ if (ifreq.ifr_flags & IFF_LOOPBACK)
+#endif
+ {
+ inter_list[i].flags |= INT_LOOPBACK;
+ if (loopback_interface == 0)
+ loopback_interface = &inter_list[i];
+ }
+#endif
+
+#ifdef STREAMS_TLI
+ ioc.ic_cmd = SIOCGIFADDR;
+ ioc.ic_timout = 0;
+ ioc.ic_dp = (caddr_t)&ifreq;
+ ioc.ic_len = sizeof(struct ifreq);
+ if(ioctl(vs, I_STR, &ioc)) {
+#else /* ! STREAMS_TLI */
+ if (ioctl(vs, SIOCGIFADDR, (char *)&ifreq) < 0) {
+#endif /* STREAMS_TLI */
+ syslog(LOG_ERR, "get interface addr: %m");
+ continue;
+ }
+
+ (void)strncpy(inter_list[i].name, ifreq.ifr_name,
+ sizeof(inter_list[i].name));
+ inter_list[i].sin = *(struct sockaddr_in *)&ifreq.ifr_addr;
+ inter_list[i].sin.sin_family = AF_INET;
+ inter_list[i].sin.sin_port = port;
+
+#if defined(SUN_3_3_STINKS)
+ /*
+ * Oh, barf! I'm too disgusted to even explain this
+ */
+ if (SRCADR(&inter_list[i].sin) == 0x7f000001) {
+ inter_list[i].flags |= INT_LOOPBACK;
+ if (loopback_interface == 0)
+ loopback_interface = &inter_list[i];
+ }
+#endif
+ if (inter_list[i].flags & INT_BROADCAST) {
+#ifdef STREAMS_TLI
+ ioc.ic_cmd = SIOCGIFBRDADDR;
+ ioc.ic_timout = 0;
+ ioc.ic_dp = (caddr_t)&ifreq;
+ ioc.ic_len = sizeof(struct ifreq);
+ if(ioctl(vs, I_STR, &ioc)) {
+#else /* ! STREAMS_TLI */
+ if (ioctl(vs, SIOCGIFBRDADDR, (char *)&ifreq) < 0) {
+#endif /* STREAMS_TLI */
+ syslog(LOG_ERR, "SIOCGIFBRDADDR fails");
+ exit(1);
+ }
+#ifndef ifr_broadaddr
+ inter_list[i].bcast =
+ *(struct sockaddr_in *)&ifreq.ifr_addr;
+#else
+ inter_list[i].bcast =
+ *(struct sockaddr_in *)&ifreq.ifr_broadaddr;
+#endif
+ inter_list[i].bcast.sin_family = AF_INET;
+ inter_list[i].bcast.sin_port = port;
+ }
+#ifdef STREAMS_TLI
+ ioc.ic_cmd = SIOCGIFNETMASK;
+ ioc.ic_timout = 0;
+ ioc.ic_dp = (caddr_t)&ifreq;
+ ioc.ic_len = sizeof(struct ifreq);
+ if(ioctl(vs, I_STR, &ioc)) {
+#else /* ! STREAMS_TLI */
+ if (ioctl(vs, SIOCGIFNETMASK, (char *)&ifreq) < 0) {
+#endif /* STREAMS_TLI */
+ syslog(LOG_ERR, "SIOCGIFNETMASK fails");
+ exit(1);
+ }
+ inter_list[i].mask = *(struct sockaddr_in *)&ifreq.ifr_addr;
+
+ /*
+ * look for an already existing source interface address. If
+ * the machine has multiple point to point interfaces, then
+ * the local address may appear more than once.
+ */
+ for (j=0; j < i; j++)
+ if (inter_list[j].sin.sin_addr.s_addr ==
+ inter_list[i].sin.sin_addr.s_addr) {
+ break;
+ }
+ if (j == i)
+ i++;
+ }
+ close(vs);
+ ninterfaces = i;
+
+ maxactivefd = 0;
+ FD_ZERO(&activefds);
+
+ for (i = 0; i < ninterfaces; i++) {
+ inter_list[i].fd = open_socket(&inter_list[i].sin,
+ inter_list[i].flags & INT_BROADCAST);
+ }
+
+#if defined(MCAST) && !defined(sun) && !defined(SYS_BSDI) && !defined(SYS_DECOSF1) && !defined(SYS_44BSD)
+ /*
+ * enable possible multicast reception on the broadcast socket
+ */
+ inter_list[0].bcast.sin_addr.s_addr = htonl(INADDR_ANY);
+ inter_list[0].bcast.sin_family = AF_INET;
+ inter_list[0].bcast.sin_port = port;
+#endif /* MCAST */
+
+ /*
+ * Blacklist all bound interface addresses
+ */
+ resmask.sin_addr.s_addr = ~0L;
+ for (i = 1; i < ninterfaces; i++)
+ restrict(RESTRICT_FLAGS, &inter_list[i].sin, &resmask,
+ RESM_NTPONLY|RESM_INTERFACE, RES_IGNORE);
+
+ any_interface = &inter_list[0];
+#ifdef DEBUG
+ if (debug > 2) {
+ printf("create_sockets: ninterfaces=%d\n", ninterfaces);
+ for (i = 0; i < ninterfaces; i++) {
+ printf("interface %d: fd=%d, bfd=%d, name=%.8s, flags=0x%x\n",
+ i,
+ inter_list[i].fd,
+ inter_list[i].bfd,
+ inter_list[i].name,
+ inter_list[i].flags);
+ /* Leave these as three printf calls. */
+ printf(" sin=%s",
+ inet_ntoa((inter_list[i].sin.sin_addr)));
+ if(inter_list[i].flags & INT_BROADCAST)
+ printf(" bcast=%s,",
+ inet_ntoa((inter_list[i].bcast.sin_addr)));
+ printf(" mask=%s\n",
+ inet_ntoa((inter_list[i].mask.sin_addr)));
+ }
+ }
+#endif
+ return ninterfaces;
+}
+
+
+/*
+ * io_setbclient - open the broadcast client sockets
+ */
+void
+io_setbclient()
+{
+ int i;
+
+ for (i = 1; i < ninterfaces; i++) {
+ if (!(inter_list[i].flags & INT_BROADCAST))
+ continue;
+ if (inter_list[i].flags & INT_BCASTOPEN)
+ continue;
+#ifdef SOLARIS
+ inter_list[i].bcast.sin_addr.s_addr = htonl(INADDR_ANY);
+#endif
+#ifndef SYS_DOMAINOS
+ inter_list[i].bfd = open_socket(&inter_list[i].bcast, 0);
+ inter_list[i].flags |= INT_BCASTOPEN;
+#endif
+ }
+}
+
+
+/*
+ * io_multicast_add() - add multicast group address
+ */
+void
+io_multicast_add(addr)
+ u_long addr;
+{
+#ifdef MCAST
+ struct ip_mreq mreq;
+ int i = ninterfaces; /* Use the next interface */
+ u_long haddr = ntohl(addr);
+ struct in_addr iaddr;
+ int s;
+ struct sockaddr_in *sinp;
+
+ iaddr.s_addr = addr;
+
+ if (!IN_CLASSD(haddr))
+ { syslog(LOG_ERR,
+ "cannot add multicast address %s as it is not class D",
+ inet_ntoa(iaddr));
+ return;
+ }
+
+ for (i=0; i<ninterfaces; i++) {
+ /* Already have this address */
+ if (inter_list[i].sin.sin_addr.s_addr == addr) return;
+ /* found a free slot */
+ if (inter_list[i].sin.sin_addr.s_addr == 0 &&
+ inter_list[i].fd <= 0 && inter_list[i].bfd <= 0 &&
+ inter_list[i].flags == 0) break;
+ }
+ sinp = &(inter_list[i].sin);
+
+ memset((char *)&mreq, 0, sizeof(mreq));
+ memset((char *)&inter_list[i], 0, sizeof inter_list[0]);
+ sinp->sin_family = AF_INET;
+ sinp->sin_addr = iaddr;
+ sinp->sin_port = htons(123);
+
+ s = open_socket(sinp, 0);
+ /* Try opening a socket for the specified class D address */
+ /* This works under SunOS 4.x, but not OSF1 .. :-( */
+ if (s < 0) {
+ memset((char *)&inter_list[i], 0, sizeof inter_list[0]);
+ i = 0;
+ /* HACK ! -- stuff in an address */
+ inter_list[i].bcast.sin_addr.s_addr = addr;
+ syslog(LOG_ERR, "...multicast address %s using wildcard socket",
+ inet_ntoa(iaddr));
+ }
+ else {
+ inter_list[i].fd = s;
+ inter_list[i].bfd = -1;
+ (void) strncpy(inter_list[i].name, "multicast",
+ sizeof(inter_list[i].name));
+ inter_list[i].mask.sin_addr.s_addr = htonl(~0);
+ }
+
+ /*
+ * enable reception of multicast packets
+ */
+ mreq.imr_multiaddr = iaddr;
+ mreq.imr_interface.s_addr = htonl(INADDR_ANY);
+ if (setsockopt(inter_list[i].fd, IPPROTO_IP, IP_ADD_MEMBERSHIP,
+ (char *)&mreq, sizeof(mreq)) == -1)
+ syslog(LOG_ERR,
+ "setsockopt IP_ADD_MEMBERSHIP fails: %m for %x / %x (%s)",
+ mreq.imr_multiaddr, mreq.imr_interface.s_addr,
+ inet_ntoa(iaddr));
+ inter_list[i].flags |= INT_MULTICAST;
+ if (i >= ninterfaces) ninterfaces = i+1;
+#else /* MCAST */
+ struct in_addr iaddr;
+ iaddr.s_addr = addr;
+ syslog(LOG_ERR, "cannot add multicast address %s as no MCAST support",
+ inet_ntoa(iaddr));
+#endif /* MCAST */
+}
+
+/*
+ * io_unsetbclient - close the broadcast client sockets
+ */
+void
+io_unsetbclient()
+{
+ int i;
+
+ for (i = 1; i < ninterfaces; i++) {
+ if (!(inter_list[i].flags & INT_BCASTOPEN))
+ continue;
+ close_socket(inter_list[i].bfd);
+ inter_list[i].flags &= ~INT_BCASTOPEN;
+ }
+}
+
+
+/*
+ * io_multicast_del() - delete multicast group address
+ */
+void
+io_multicast_del(addr)
+ u_long addr;
+{
+#ifdef MCAST
+ int i;
+ struct ip_mreq mreq;
+ struct sockaddr_in sinaddr;
+
+ if (!IN_CLASSD(addr)) {
+ sinaddr.sin_addr.s_addr = addr;
+ syslog(LOG_ERR,
+ "invalid multicast address %s", ntoa(&sinaddr));
+ return;
+ }
+
+ /*
+ * Disable reception of multicast packets
+ */
+ mreq.imr_multiaddr.s_addr = addr;
+ mreq.imr_interface.s_addr = htonl(INADDR_ANY);
+ for (i = 0; i < ninterfaces; i++) {
+ if (!(inter_list[i].flags & INT_MULTICAST))
+ continue;
+ if (!(inter_list[i].fd < 0))
+ continue;
+ if (addr != inter_list[i].sin.sin_addr.s_addr)
+ continue;
+ if (i != 0) {
+ /* we have an explicit fd, so we can slose it */
+ close_socket(inter_list[i].fd);
+ memset((char *)&inter_list[i], 0, sizeof inter_list[0]);
+ inter_list[i].fd = -1;
+ inter_list[i].bfd = -1;
+ } else {
+ /* We are sharing "any address" port :-( Don't close it! */
+ if (setsockopt(inter_list[i].fd, IPPROTO_IP, IP_DROP_MEMBERSHIP,
+ (char *)&mreq, sizeof(mreq)) == -1)
+ syslog(LOG_ERR, "setsockopt IP_DROP_MEMBERSHIP fails: %m");
+ /* This is **WRONG** -- there may be others ! */
+ /* There should be a count of users ... */
+ inter_list[i].flags &= ~INT_MULTICAST;
+ }
+ }
+#else /* MCAST */
+ syslog(LOG_ERR, "this function requires multicast kernel");
+#endif /* MCAST */
+}
+
+
+/*
+ * open_socket - open a socket, returning the file descriptor
+ */
+static int
+open_socket(addr, flags)
+ struct sockaddr_in *addr;
+ int flags;
+{
+ int fd;
+ int on = 1, off = 0;
+
+ /* create a datagram (UDP) socket */
+ if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
+ syslog(LOG_ERR, "socket(AF_INET, SOCK_DGRAM, 0) failed: %m");
+ exit(1);
+ /*NOTREACHED*/
+ }
+
+ /* set SO_REUSEADDR since we will be binding the same port
+ number on each interface */
+ if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
+ (char *)&on, sizeof(on))) {
+ syslog(LOG_ERR, "setsockopt SO_REUSEADDR on fails: %m");
+ }
+
+ /*
+ * bind the local address.
+ */
+ if (bind(fd, (struct sockaddr *)addr, sizeof(*addr)) < 0) {
+ char buff[160];
+ sprintf(buff,
+ "bind() fd %d, family %d, port %d, addr %08lx, in_classd=%d flags=%d fails: %%m",
+ fd, addr->sin_family, (int)ntohs(addr->sin_port),
+ (u_long)ntohl(addr->sin_addr.s_addr),
+ IN_CLASSD(ntohl(addr->sin_addr.s_addr)), flags);
+ syslog(LOG_ERR, buff);
+ close(fd);
+
+ /*
+ * soft fail if opening a class D address
+ */
+ if (IN_CLASSD(ntohl(addr->sin_addr.s_addr)))
+ return -1;
+ exit(1);
+ }
+#ifdef DEBUG
+ if (debug)
+ printf("bind() fd %d, family %d, port %d, addr %08lx, flags=%d\n",
+ fd,
+ addr->sin_family,
+ (int)ntohs(addr->sin_port),
+ (u_long)ntohl(addr->sin_addr.s_addr),
+ flags);
+#endif
+ if (fd > maxactivefd)
+ maxactivefd = fd;
+ FD_SET(fd, &activefds);
+
+#ifdef HAVE_SIGNALED_IO
+ init_socket_sig(fd);
+#else /* HAVE_SIGNALED_IO */
+
+ /*
+ * set non-blocking,
+ */
+#if defined(O_NONBLOCK)
+ if (fcntl(fd, F_SETFL, O_NONBLOCK) < 0) {
+ syslog(LOG_ERR, "fcntl(O_NONBLOCK) fails: %m");
+ exit(1);
+ /*NOTREACHED*/
+ }
+#else /* O_NONBLOCK */
+#if defined(FNDELAY)
+ if (fcntl(fd, F_SETFL, FNDELAY) < 0) {
+ syslog(LOG_ERR, "fcntl(FNDELAY) fails: %m");
+ exit(1);
+ /*NOTREACHED*/
+ }
+#else /* FNDELAY */
+Need non blocking I/O
+#endif /* FNDELAY */
+#endif /* O_NONBLOCK */
+#endif /* HAVE_SIGNALED_IO */
+
+ /*
+ * Turn off the SO_REUSEADDR socket option. It apparently
+ * causes heartburn on systems with multicast IP installed.
+ * On normal systems it only gets looked at when the address
+ * is being bound anyway..
+ */
+ if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
+ (char *)&off, sizeof(off))) {
+ syslog(LOG_ERR, "setsockopt SO_REUSEADDR off fails: %m");
+ }
+
+#ifdef SO_BROADCAST
+ /* if this interface can support broadcast, set SO_BROADCAST */
+ if (flags & INT_BROADCAST) {
+ if (setsockopt(fd, SOL_SOCKET, SO_BROADCAST,
+ (char *)&on, sizeof(on))) {
+ syslog(LOG_ERR, "setsockopt(SO_BROADCAST): %m");
+ }
+ }
+#endif /* SO_BROADCAST */
+
+#ifdef DEBUG
+ if (debug > 1)
+ printf("flags for fd %d: 0%o\n", fd,
+ fcntl(fd, F_GETFL, 0));
+#endif
+
+ return fd;
+}
+
+
+/*
+ * closesocket - close a socket and remove from the activefd list
+ */
+static void
+close_socket(fd)
+ int fd;
+{
+ int i, newmax;
+
+ (void) close(fd);
+ FD_CLR(fd, &activefds);
+
+ if (fd >= maxactivefd) {
+ newmax = 0;
+ for (i = 0; i < maxactivefd; i++)
+ if (FD_ISSET(i, &activefds))
+ newmax = i;
+ maxactivefd = newmax;
+ }
+}
+
+
+
+/*
+ * findbcastinter - find broadcast interface corresponding to address
+ */
+struct interface *
+findbcastinter(addr)
+ struct sockaddr_in *addr;
+{
+#ifdef SIOCGIFCONF
+ register int i;
+ register u_long netnum;
+
+ netnum = NSRCADR(addr);
+ for (i = 1; i < ninterfaces; i++) {
+ if (!(inter_list[i].flags & INT_BROADCAST))
+ continue;
+ if (NSRCADR(&inter_list[i].bcast) == netnum)
+ return &inter_list[i];
+ if ((NSRCADR(&inter_list[i].sin) & NSRCADR(&inter_list[i].mask))
+ == (netnum & NSRCADR(&inter_list[i].mask)))
+ return &inter_list[i];
+ }
+#endif /* SIOCGIFCONF */
+ return any_interface;
+}
+
+
+/* XXX ELIMINATE getrecvbufs (almost) identical to ntpdate.c, ntptrace.c, ntp_io.c */
+/*
+ * getrecvbufs - get receive buffers which have data in them
+ *
+ * ***N.B. must be called with SIGIO blocked***
+ */
+struct recvbuf *
+getrecvbufs()
+{
+ struct recvbuf *rb;
+
+#ifdef DEBUG
+ if (debug > 4)
+ printf("getrecvbufs: %ld handler interrupts, %ld frames\n",
+ handler_calls, handler_pkts);
+#endif
+
+ if (full_recvbufs == 0) {
+#ifdef DEBUG
+ if (debug > 4)
+ printf("getrecvbufs called, no action here\n");
+#endif
+ return (struct recvbuf *)0; /* nothing has arrived */
+ }
+
+ /*
+ * Get the fulllist chain and mark it empty
+ */
+#ifdef DEBUG
+ if (debug > 4)
+ printf("getrecvbufs returning %ld buffers\n", full_recvbufs);
+#endif
+ rb = beginlist;
+ fulllist = 0;
+ full_recvbufs = 0;
+
+ /*
+ * Check to see if we're below the low water mark.
+ */
+ if (free_recvbufs <= RECV_LOWAT) {
+ register struct recvbuf *buf;
+ register int i;
+
+ if (total_recvbufs >= RECV_TOOMANY)
+ syslog(LOG_ERR, "too many recvbufs allocated (%d)",
+ total_recvbufs);
+ else {
+ buf = (struct recvbuf *)
+ emalloc(RECV_INC*sizeof(struct recvbuf));
+ for (i = 0; i < RECV_INC; i++) {
+ buf->next = freelist;
+ freelist = buf;
+ buf++;
+ }
+
+ free_recvbufs += RECV_INC;
+ total_recvbufs += RECV_INC;
+ lowater_additions++;
+ }
+ }
+
+ /*
+ * Return the chain
+ */
+ return rb;
+}
+
+
+/* XXX ELIMINATE freerecvbuf (almost) identical to ntpdate.c, ntptrace.c, ntp_io.c */
+/*
+ * freerecvbuf - make a single recvbuf available for reuse
+ */
+void
+freerecvbuf(rb)
+ struct recvbuf *rb;
+{
+ BLOCKIO();
+ rb->next = freelist;
+ freelist = rb;
+ free_recvbufs++;
+ UNBLOCKIO();
+}
+
+
+/* XXX ELIMINATE sendpkt similar in ntpq.c, ntpdc.c, ntp_io.c, ntptrace.c */
+/*
+ * sendpkt - send a packet to the specified destination. Maintain a
+ * send error cache so that only the first consecutive error for a
+ * destination is logged.
+ */
+void
+sendpkt(dest, inter, ttl, pkt, len)
+ struct sockaddr_in *dest;
+ struct interface *inter;
+ int ttl;
+ struct pkt *pkt;
+ int len;
+{
+ int cc, slot;
+ /*
+ * Send error cache. Empty slots have port == 0
+ * Set ERRORCACHESIZE to 0 to disable
+ */
+ struct cache {
+ u_short port;
+ struct in_addr addr;
+ };
+
+#ifndef ERRORCACHESIZE
+#define ERRORCACHESIZE 8
+#endif
+#if ERRORCACHESIZE > 0
+ static struct cache badaddrs[ERRORCACHESIZE];
+#else
+#define badaddrs ((struct cache *)0) /* Only used in empty loops! */
+#endif
+
+#ifdef DEBUG
+ if (debug)
+ printf("%ssendpkt(fd=%d %s, %s, ttl=%d, %d)\n",
+ (ttl >= 0) ? "\tMCAST\t*****" : "",
+ inter->fd, ntoa(dest),
+ ntoa(&inter->sin), ttl, len);
+#endif
+
+#ifdef MCAST
+ /* for the moment we use the bcast option to set multicast ttl */
+ if (ttl >= 0 && ttl != inter->last_ttl) {
+ u_char mttl = ttl;
+
+ /* set the multicast ttl for outgoing packets */
+ if (setsockopt(inter->fd, IPPROTO_IP, IP_MULTICAST_TTL,
+ &mttl, sizeof(mttl)) == -1) {
+ syslog(LOG_ERR, "setsockopt IP_MULTICAST_TTL fails: %m");
+ }
+ else inter->last_ttl = ttl;
+ }
+#endif /* MCAST */
+
+ for (slot = ERRORCACHESIZE; --slot >= 0; )
+ if (badaddrs[slot].port == dest->sin_port &&
+ badaddrs[slot].addr.s_addr == dest->sin_addr.s_addr)
+ break;
+
+ cc = sendto(inter->fd, (char *)pkt, len, 0, (struct sockaddr *)dest,
+ sizeof(struct sockaddr_in));
+ if (cc == -1) {
+ inter->notsent++;
+ packets_notsent++;
+ if (errno != EWOULDBLOCK && errno != ENOBUFS && slot < 0) {
+ /*
+ * Remember this, if there's an empty slot
+ */
+ for (slot = ERRORCACHESIZE; --slot >= 0; )
+ if (badaddrs[slot].port == 0) {
+ badaddrs[slot].port = dest->sin_port;
+ badaddrs[slot].addr = dest->sin_addr;
+ break;
+ }
+ syslog(LOG_ERR, "sendto(%s): %m", ntoa(dest));
+ }
+ } else {
+ inter->sent++;
+ packets_sent++;
+ /*
+ * He's not bad any more
+ */
+ if (slot >= 0)
+ badaddrs[slot].port = 0;
+ }
+}
+
+
+/*
+ * input_handler - receive packets asynchronously
+ */
+void
+input_handler(cts)
+ l_fp *cts;
+{
+ register int i, n;
+ register struct recvbuf *rb;
+ register int doing;
+ register int fd;
+ struct timeval tvzero;
+ int fromlen;
+ l_fp ts;
+ fd_set fds;
+ int first = 1;
+
+ handler_calls++;
+ ts = *cts;
+
+ /*
+ * Do a poll to see who has data
+ */
+again:
+ fds = activefds;
+ tvzero.tv_sec = tvzero.tv_usec = 0;
+ n = select(maxactivefd+1, &fds, (fd_set *)0, (fd_set *)0, &tvzero);
+
+ /*
+ * If nothing to do, just return. If an error occurred, complain
+ * and return. If we've got some, freeze a timestamp.
+ */
+ if (n == 0)
+ return;
+ else if (n == -1) {
+ syslog(LOG_ERR, "select() error: %m");
+ return;
+ }
+ if (!first)get_systime(&ts);
+ first = 0;
+ handler_pkts += n;
+
+#ifdef REFCLOCK
+ /*
+ * Check out the reference clocks first, if any
+ */
+ if (refio != 0) {
+ register struct refclockio *rp;
+
+ for (rp = refio; rp != 0 && n > 0; rp = rp->next) {
+ fd = rp->fd;
+ if (FD_ISSET(fd, &fds)) {
+ n--;
+ if (free_recvbufs == 0) {
+ char buf[RX_BUFF_SIZE];
+
+ (void) read(fd, buf, sizeof buf);
+ packets_dropped++;
+ continue;
+ }
+
+ rb = freelist;
+ freelist = rb->next;
+ free_recvbufs--;
+
+ i = (rp->datalen == 0
+ || rp->datalen > sizeof(rb->recv_space))
+ ? sizeof(rb->recv_space) : rp->datalen;
+
+ rb->recv_length =
+ read(fd, (char *)&rb->recv_space, i);
+
+ if (rb->recv_length == -1) {
+ syslog(LOG_ERR, "clock read fd %d: %m", fd);
+ rb->next = freelist;
+ freelist = rb;
+ free_recvbufs++;
+ continue;
+ }
+
+ /*
+ * Got one. Mark how and when it got here,
+ * put it on the full list and do bookkeeping.
+ */
+ rb->recv_srcclock = rp->srcclock;
+ rb->dstadr = 0;
+ rb->fd = fd;
+ rb->recv_time = ts;
+ rb->receiver = rp->clock_recv;
+
+ if (fulllist == 0) {
+ beginlist = rb;
+ rb->next = 0;
+ } else {
+ rb->next = fulllist->next;
+ fulllist->next = rb;
+ }
+ fulllist = rb;
+ full_recvbufs++;
+
+ rp->recvcount++;
+ packets_received++;
+ }
+ }
+ }
+#endif
+
+ /*
+ * Loop through the interfaces looking for data to read.
+ */
+ for (i = ninterfaces-1; i >= 0 && n > 0; i--) {
+ for (doing = 0; doing < 2 && n > 0; doing++) {
+ if (doing == 0) {
+ fd = inter_list[i].fd;
+ } else {
+ if (!(inter_list[i].flags & INT_BCASTOPEN))
+ break;
+ fd = inter_list[i].bfd;
+ }
+ if (fd < 0) continue;
+ if (FD_ISSET(fd, &fds)) {
+ n--;
+
+ /*
+ * Get a buffer and read the frame. If we
+ * haven't got a buffer, or this is received
+ * on the wild card socket, just dump the
+ * packet.
+ */
+ if (!(free_recvbufs && i == 0 &&
+ inter_list[i].flags & INT_MULTICAST)) {
+#ifdef UDP_WILDCARD_DELIVERY
+ /*
+ * these guys manage to put properly addressed
+ * packets into the wildcard queue
+ */
+ if (free_recvbufs == 0) {
+#else
+ if (i == 0 || free_recvbufs == 0) {
+#endif
+ char buf[RX_BUFF_SIZE];
+ struct sockaddr from;
+ fromlen = sizeof from;
+ (void) recvfrom(fd, buf,
+ sizeof(buf), 0,
+ &from, &fromlen);
+#ifdef DEBUG
+ if (debug)
+ printf("ignore/drop on %d(%lu) fd=%d from %s\n",
+ i, free_recvbufs, fd,
+ inet_ntoa(((struct sockaddr_in *) &from)->sin_addr));
+#endif
+ if (i == 0)
+ packets_ignored++;
+ else
+ packets_dropped++;
+ continue;
+ }
+ }
+
+ rb = freelist;
+ freelist = rb->next;
+ free_recvbufs--;
+
+ fromlen = sizeof(struct sockaddr_in);
+ rb->recv_length = recvfrom(fd,
+ (char *)&rb->recv_space,
+ sizeof(rb->recv_space), 0,
+ (struct sockaddr *)&rb->recv_srcadr,
+ &fromlen);
+ if (rb->recv_length == -1) {
+ syslog(LOG_ERR, "recvfrom: %m");
+ rb->next = freelist;
+ freelist = rb;
+ free_recvbufs++;
+#ifdef DEBUG
+ if (debug)
+ printf("input_handler: fd=%d dropped (bad recvfrom)\n", fd);
+#endif
+ continue;
+ }
+#ifdef DEBUG
+ if (debug)
+ printf("input_handler: fd=%d length %d from %08lx %s\n",
+ fd, rb->recv_length,
+ (u_long)ntohl(rb->recv_srcadr.sin_addr.s_addr) &
+ 0x00000000ffffffff, inet_ntoa(rb->recv_srcadr.sin_addr));
+#endif
+
+ /*
+ * Got one. Mark how and when it got here,
+ * put it on the full list and do bookkeeping.
+ */
+ rb->dstadr = &inter_list[i];
+ rb->fd = fd;
+ rb->recv_time = ts;
+ rb->receiver = receive;
+
+
+ if (fulllist == 0) {
+ beginlist = rb;
+ rb->next = 0;
+ } else {
+ rb->next = fulllist->next;
+ fulllist->next = rb;
+ }
+ fulllist = rb;
+ full_recvbufs++;
+
+ inter_list[i].received++;
+ packets_received++;
+ }
+ }
+ }
+ /*
+ * Done everything from that select. Poll again.
+ */
+ goto again;
+}
+
+
+/*
+ * findinterface - utility used by other modules to find an interface
+ * given an address.
+ */
+struct interface *
+findinterface(addr)
+ struct sockaddr_in *addr;
+{
+ register int i;
+ register u_long saddr;
+
+ /*
+ * Just match the address portion.
+ */
+ saddr = addr->sin_addr.s_addr;
+ for (i = 0; i < ninterfaces; i++) {
+ if (inter_list[i].sin.sin_addr.s_addr == saddr)
+ return &inter_list[i];
+ }
+ return (struct interface *)0;
+}
+
+
+/*
+ * io_clr_stats - clear I/O module statistics
+ */
+void
+io_clr_stats()
+{
+ packets_dropped = 0;
+ packets_ignored = 0;
+ packets_received = 0;
+ packets_sent = 0;
+ packets_notsent = 0;
+
+ handler_calls = 0;
+ handler_pkts = 0;
+ io_timereset = current_time;
+}
+
+
+#ifdef REFCLOCK
+/*
+ * This is a hack so that I don't have to fool with these ioctls in the
+ * pps driver ... we are already non-blocking and turn on SIGIO thru
+ * another mechanisim
+ */
+int
+io_addclock_simple(rio)
+ struct refclockio *rio;
+{
+ BLOCKIO();
+ /*
+ * Stuff the I/O structure in the list and mark the descriptor
+ * in use. There is a harmless (I hope) race condition here.
+ */
+ rio->next = refio;
+ refio = rio;
+
+ if (rio->fd > maxactivefd)
+ maxactivefd = rio->fd;
+ FD_SET(rio->fd, &activefds);
+ UNBLOCKIO();
+ return 1;
+}
+
+/*
+ * io_addclock - add a reference clock to the list and arrange that we
+ * get SIGIO interrupts from it.
+ */
+int
+io_addclock(rio)
+ struct refclockio *rio;
+{
+ BLOCKIO();
+ /*
+ * Stuff the I/O structure in the list and mark the descriptor
+ * in use. There is a harmless (I hope) race condition here.
+ */
+ rio->next = refio;
+ refio = rio;
+
+#ifdef HAVE_SIGNALED_IO
+ if (init_clock_sig(rio)) {
+ UNBLOCKIO();
+ return 0;
+ }
+#endif
+
+ if (rio->fd > maxactivefd)
+ maxactivefd = rio->fd;
+ FD_SET(rio->fd, &activefds);
+
+ UNBLOCKIO();
+ return 1;
+}
+
+/*
+ * io_closeclock - close the clock in the I/O structure given
+ */
+void
+io_closeclock(rio)
+ struct refclockio *rio;
+{
+ /*
+ * Remove structure from the list
+ */
+ if (refio == rio) {
+ refio = rio->next;
+ } else {
+ register struct refclockio *rp;
+
+ for (rp = refio; rp != 0; rp = rp->next)
+ if (rp->next == rio) {
+ rp->next = rio->next;
+ break;
+ }
+
+ if (rp == 0) {
+ /*
+ * Internal error. Report it.
+ */
+ syslog(LOG_ERR,
+ "internal error: refclockio structure not found");
+ return;
+ }
+ }
+
+ /*
+ * Close the descriptor. close_socket does the right thing despite
+ * the misnomer.
+ */
+ close_socket(rio->fd);
+}
+#endif /* REFCLOCK */
+
+/*
+ * SIGPOLL and SIGIO ROUTINES.
+ */
+#ifdef HAVE_SIGNALED_IO
+/*
+ * Some systems (MOST) define SIGPOLL==SIGIO others SIGIO==SIGPOLL a few
+ * have seperate SIGIO and SIGPOLL signals. This code checks for the
+ * SIGIO==SIGPOLL case at compile time.
+ * Do not defined USE_SIGPOLL or USE_SIGIO.
+ * these are interal only to ntp_io.c!
+ */
+#if defined(USE_SIGPOLL)
+#undef USE_SIGPOLL
+#endif
+#if defined(USE_SIGIO)
+#undef USE_SIGIO
+#endif
+
+#if defined(USE_TTY_SIGPOLL)||defined(USE_UDP_SIGPOLL)
+#define USE_SIGPOLL
+#endif
+
+#if !defined(USE_TTY_SIGPOLL)||!defined(USE_UDP_SIGPOLL)
+#define USE_SIGIO
+#endif
+
+#if defined(USE_SIGIO)&&defined(USE_SIGPOLL)
+#if SIGIO==SIGPOLL
+#define USE_SIGIO
+#undef USE_SIGPOLL
+#endif /* SIGIO==SIGPOLL */
+#endif /* USE_SIGIO && USE_SIGIO */
+
+
+/*
+ * TTY instialzation routeins.
+ */
+#ifndef USE_TTY_SIGPOLL
+/*
+ * Spical cases first!
+ */
+#if defined(SYS_HPUX)
+#define CLOCK_DONE
+static int
+init_clock_sig(rio)
+ struct refclockio *rio;
+{
+ int pgrp, on = 1;
+
+ pgrp = getpid();
+ if (ioctl(rio->fd, FIOSSAIOOWN, (char *)&pgrp) == -1) {
+ syslog(LOG_ERR, "ioctl(FIOSSAIOOWN) fails for clock I/O: %m");
+ exit(1);
+ /*NOTREACHED*/
+ }
+
+ /*
+ * set non-blocking, async I/O on the descriptor
+ */
+ if (ioctl(rio->fd, FIOSNBIO, (char *)&on) == -1) {
+ syslog(LOG_ERR, "ioctl(FIOSNBIO) fails for clock I/O: %m");
+ exit(1);
+ /*NOTREACHED*/
+ }
+
+ if (ioctl(rio->fd, FIOSSAIOSTAT, (char *)&on) == -1) {
+ syslog(LOG_ERR, "ioctl(FIOSSAIOSTAT) fails for clock I/O: %m");
+ exit(1);
+ /*NOTREACHED*/
+ }
+ return 0;
+}
+#endif /* SYS_HPUX */
+#if defined(SYS_AIX)&&!defined(_BSD)
+/*
+ * SYSV compatibility mode under AIX.
+ */
+#define CLOCK_DONE
+static int
+init_clock_sig(rio)
+ struct refclockio *rio;
+{
+ int pgrp, on = 1;
+
+ if (ioctl(rio->fd, FIOASYNC, (char *)&on) == -1) {
+ syslog(LOG_ERR, "ioctl(FIOASYNC) fails for clock I/O: %m");
+ return 1;
+ }
+ pgrp = -getpid();
+ if (ioctl(rio->fd, FIOSETOWN, (char*)&pgrp) == -1) {
+ syslog(LOG_ERR, "ioctl(FIOSETOWN) fails for clock I/O: %m");
+ return 1;
+ }
+
+ if (fcntl(rio->fd, F_SETFL, FNDELAY|FASYNC) < 0) {
+ syslog(LOG_ERR, "fcntl(FNDELAY|FASYNC) fails for clock I/O: %m");
+ return 1;
+ }
+ return 0;
+}
+#endif /* AIX && !BSD */
+#ifndef CLOCK_DONE
+static int
+init_clock_sig(rio)
+ struct refclockio *rio;
+{
+ if (fcntl(rio->fd, F_SETOWN, getpid()) == -1) {
+ syslog(LOG_ERR, "fcntl(F_SETOWN) fails for clock I/O: %m");
+ return 1;
+ }
+
+ if (fcntl(rio->fd, F_SETFL, FNDELAY|FASYNC) < 0) {
+ syslog(LOG_ERR,
+ "fcntl(FNDELAY|FASYNC) fails for clock I/O: %m");
+ return 1;
+ }
+ return 0;
+}
+#endif /* CLOCK_DONE */
+#else /* !USE_TTY_SIGPOLL */
+int
+static init_clock_sig(rio)
+ struct refclockio *rio;
+{
+ if (ioctl(rio->fd, I_SETSIG, S_INPUT) < 0) {
+ syslog(LOG_ERR,
+ "ioctl(I_SETSIG, S_INPUT) fails for clock I/O: %m");
+ return 1;
+ }
+ return 0;
+}
+#endif /* !USE_TTY_SIGPOLL */
+
+
+
+#ifndef USE_UDP_SIGPOLL
+/*
+ * Socket SIGPOLL initialization routines.
+ * Special cases first!
+ */
+#if defined(SYS_HPUX) || defined(SYS_LINUX)
+#define SOCKET_DONE
+static void
+init_socket_sig(fd)
+ int fd;
+{
+ int pgrp, on = 1;
+
+ /*
+ * Big difference here for HP-UX ... why can't life be easy ?
+ */
+ if (ioctl(fd, FIOSNBIO, (char *)&on) == -1) {
+ syslog(LOG_ERR, "ioctl(FIOSNBIO) fails: %m");
+ exit(1);
+ /*NOTREACHED*/
+ }
+
+ if (ioctl(fd, FIOASYNC, (char *)&on) == -1) {
+ syslog(LOG_ERR, "ioctl(FIOASYNC) fails: %m");
+ exit(1);
+ /*NOTREACHED*/
+ }
+
+#if (SYS_HPUX > 7)
+ pgrp = getpid();
+#else
+ pgrp = -getpid();
+#endif
+ if (ioctl(fd, SIOCSPGRP, (char *)&pgrp) == -1) {
+ syslog(LOG_ERR, "ioctl(SIOCSPGRP) fails: %m");
+ exit(1);
+ /*NOTREACHED*/
+ }
+}
+#endif /* SYS_HPUX */
+#if defined(SYS_AIX)&&!defined(_BSD)
+/*
+ * SYSV compatibility mod under AIX
+ */
+#define SOCKET_DONE
+static void
+init_socket_sig(fd)
+ int fd;
+{
+ int pgrp, on = 1;
+
+ if (ioctl(fd, FIOASYNC, (char *)&on) == -1) {
+ syslog(LOG_ERR, "ioctl(FIOASYNC) fails: %m");
+ exit(1);
+ /*NOTREACHED*/
+ }
+ pgrp = -getpid();
+ if (ioctl(fd, FIOSETOWN, (char*)&pgrp) == -1) {
+ syslog(LOG_ERR, "ioctl(FIOSETOWN) fails: %m");
+ exit(1);
+ /*NOTREACHED*/
+ }
+
+ if (fcntl(fd, F_SETFL, FNDELAY|FASYNC) < 0) {
+ syslog(LOG_ERR, "fcntl(FNDELAY|FASYNC) fails: %m");
+ exit(1);
+ /*NOTREACHED*/
+ }
+}
+#endif /* AIX && !BSD */
+#if defined(UDP_BACKWARDS_SETOWN)
+/*
+ * SunOS 3.5 and Ultirx 2.0
+ */
+#define SOCKET_DONE
+static void
+init_socket_sig(fd)
+ int fd;
+{
+ /*
+ * The way Sun did it as recently as SunOS 3.5. Only
+ * in the case of sockets, of course, just to confuse
+ * the issue. Don't they even bother to test the stuff
+ * they send out? Ibid for Ultrix 2.0
+ */
+ if (fcntl(fd, F_SETOWN, -getpid()) == -1)
+ {
+ syslog(LOG_ERR, "fcntl(F_SETOWN) fails: %m");
+ exit(1);
+ }
+ /*
+ * set non-blocking, async I/O on the descriptor
+ */
+ if (fcntl(fd, F_SETFL, FNDELAY|FASYNC) < 0) {
+ syslog(LOG_ERR, "fcntl(FNDELAY|FASYNC) fails: %m");
+ exit(1);
+ /*NOTREACHED*/
+ }
+}
+#endif /* UDP_BACKWARDS_SETOWN */
+#ifndef SOCKET_DONE
+static void
+init_socket_sig(fd)
+ int fd;
+{
+ if (fcntl(fd, F_SETOWN, getpid()) == -1)
+ {
+ syslog(LOG_ERR, "fcntl(F_SETOWN) fails: %m");
+ exit(1);
+ }
+ /*
+ * set non-blocking, async I/O on the descriptor
+ */
+ if (fcntl(fd, F_SETFL, FNDELAY|FASYNC) < 0) {
+ syslog(LOG_ERR, "fcntl(FNDELAY|FASYNC) fails: %m");
+ exit(1);
+ /*NOTREACHED*/
+ }
+}
+#endif /* SOCKET_DONE */
+#else /* !USE_UDP_SIGPOLL */
+static void
+init_socket_sig(fd)
+ int fd;
+{
+ if (ioctl(fd, I_SETSIG, S_INPUT) < 0) {
+ syslog(LOG_ERR,
+ "ioctl(I_SETSIG, S_INPUT) fails for socket I/O: %m");
+ exit(1);
+ }
+}
+#endif /* USE_UDP_SIGPOLL */
+
+static RETSIGTYPE
+sigio_handler(sig)
+int sig;
+{
+ l_fp ts;
+
+#ifdef SYS_SVR4
+ /* This should not be necessary for a signal previously set with
+ * sigset().
+ */
+# if defined(USE_SIGIO)
+ (void) sigset(SIGIO, sigio_handler);
+# endif
+# if defined(USE_SIGPOLL)
+ (void) sigset(SIGPOLL, sigio_handler);
+# endif
+#endif /* SYS_SVR4 */
+
+ get_systime(&ts);
+ (void)input_handler(&ts);
+}
+
+/*
+ * Signal support routines.
+ */
+#ifdef NTP_POSIX_SOURCE
+static void
+set_signal()
+{
+ int n;
+ struct sigaction vec;
+
+ sigemptyset(&vec.sa_mask);
+
+#ifdef USE_SIGIO
+ sigaddset(&vec.sa_mask, SIGIO);
+#endif
+#ifdef USE_SIGPOLL
+ sigaddset(&vec.sa_mask, SIGPOLL);
+#endif
+ vec.sa_flags = 0;
+
+#if defined(USE_SIGIO)
+ vec.sa_handler = sigio_handler;
+
+ while (1) {
+ n = sigaction(SIGIO, &vec, NULL);
+ if (n == -1 && errno == EINTR) continue;
+ break;
+ }
+
+ if (n == -1) {
+ perror("sigaction");
+ exit(1);
+ }
+#endif
+#if defined(USE_SIGPOLL)
+ vec.sa_handler = sigio_handler;
+
+ while (1) {
+ n = sigaction(SIGPOLL, &vec, NULL);
+ if (n == -1 && errno == EINTR) continue;
+ break;
+ }
+
+ if (n == -1) {
+ perror("sigaction");
+ exit(1);
+ }
+#endif
+}
+
+void
+block_io_and_alarm()
+{
+ sigset_t set;
+
+ sigemptyset(&set);
+#if defined(USE_SIGIO)
+ sigaddset(&set, SIGIO);
+#endif
+#if defined(USE_SIGPOLL)
+ sigaddset(&set, SIGPOLL);
+#endif
+ sigprocmask(SIG_BLOCK, &set, NULL);
+}
+
+static void
+block_sigio()
+{
+ sigset_t set;
+
+ sigemptyset(&set);
+#if defined(USE_SIGIO)
+ sigaddset(&set, SIGIO);
+#endif
+#if defined(USE_SIGPOLL)
+ sigaddset(&set, SIGPOLL);
+#endif
+ sigaddset(&set, SIGALRM);
+ sigprocmask(SIG_BLOCK, &set, NULL);
+}
+
+void
+unblock_io_and_alarm()
+{
+ sigset_t unset;
+
+ sigemptyset(&unset);
+
+#if defined(USE_SIGIO)
+ sigaddset(&unset, SIGIO);
+#endif
+#if defined(USE_SIGPOLL)
+ sigaddset(&unset, SIGPOLL);
+#endif
+ sigaddset(&unset, SIGALRM);
+ sigprocmask(SIG_UNBLOCK, &unset, NULL);
+}
+
+static
+void
+unblock_sigio()
+{
+ sigset_t unset;
+
+ sigemptyset(&unset);
+
+#if defined(USE_SIGIO)
+ sigaddset(&unset, SIGIO);
+#endif
+#if defined(USE_SIGPOLL)
+ sigaddset(&unset, SIGPOLL);
+#endif
+ sigprocmask(SIG_UNBLOCK, &unset, NULL);
+}
+
+void
+wait_for_signal()
+{
+ sigset_t old;
+
+ sigprocmask(SIG_UNBLOCK, NULL, &old);
+
+#if defined(USE_SIGIO)
+ sigdelset(&old, SIGIO);
+#endif
+#if defined(USE_SIGPOLL)
+ sigdelset(&old, SIGPOLL);
+#endif
+ sigdelset(&old, SIGALRM);
+
+ sigsuspend(&old);
+}
+
+#else
+/*
+ * Must be an old bsd system.
+ * We assume there is no SIGPOLL.
+ */
+
+void
+block_io_and_alarm()
+{
+ int mask;
+
+ mask = sigmask(SIGIO)|sigmask(SIGALRM);
+ (void)sigblock(mask);
+}
+
+void
+block_sigio()
+{
+ int mask;
+
+ mask = sigmask(SIGIO);
+ (void)sigblock(mask);
+}
+
+static void
+set_signal()
+{
+ (void) signal_no_reset(SIGIO, sigio_handler);
+}
+
+void
+unblock_io_and_alarm()
+{
+ int mask, omask;
+
+ mask = sigmask(SIGIO)|sigmask(SIGALRM);
+ omask = sigblock(0);
+ omask &= ~mask;
+ (void)sigsetmask(omask);
+}
+
+void
+unblock_sigio()
+{
+ int mask, omask;
+
+ mask = sigmask(SIGIO);
+ omask = sigblock(0);
+ omask &= ~mask;
+ (void)sigsetmask(omask);
+}
+
+void
+wait_for_signal()
+{
+ int mask, omask;
+
+ mask = sigmask(SIGIO)|sigmask(SIGALRM);
+ omask = sigblock(0);
+ omask &= ~mask;
+ sigpause(omask);
+}
+#endif /* NTP_POSIX_SOURCE */
+#endif /* HAVE_SIGNALED_IO */
+
diff --git a/usr.sbin/xntpd/xntpd/ntp_leap.c b/usr.sbin/xntpd/xntpd/ntp_leap.c
new file mode 100644
index 0000000..5e7eca7
--- /dev/null
+++ b/usr.sbin/xntpd/xntpd/ntp_leap.c
@@ -0,0 +1,315 @@
+/*
+ * ntp_leap - maintain leap bits and take action when a leap occurs
+ */
+#include <stdio.h>
+
+#include "ntpd.h"
+#include "ntp_stdlib.h"
+
+/*
+ * This module is devoted to maintaining the leap bits and taking
+ * action when a leap second occurs. It probably has bugs since
+ * a leap second has never occurred to excercise the code.
+ *
+ * The code does two things when a leap second occurs. It first
+ * steps the clock one second in the appropriate direction. It
+ * then informs the reference clock code, if compiled in, that the
+ * leap second has occured so that any clocks which need to disable
+ * themselves can do so. This is done within the first few seconds
+ * after midnight, UTC.
+ *
+ * The code maintains two variables which may be written externally,
+ * leap_warning and leap_indicator. Leap_warning can be written
+ * any time in the month preceeding a leap second. 24 hours before
+ * the leap is to occur, leap_warning's contents are copied to
+ * leap_indicator. The latter is used by reference clocks to set
+ * their leap bits.
+ *
+ * The module normally maintains a timer which is arranged to expire
+ * just after 0000Z one day before the leap. On the day a leap might
+ * occur the interrupt is aimed at 2200Z and every 5 minutes thereafter
+ * until 1200Z to see if the leap bits appear.
+ */
+
+/*
+ * The leap indicator and leap warning flags. Set by control messages
+ */
+u_char leap_indicator;
+u_char leap_warning;
+u_char leap_mask; /* set on day before a potential leap */
+/*
+ * Timer. The timer code imports this so it can call us prior to
+ * calling out any pending transmits.
+ */
+u_long leap_timer;
+
+/*
+ * We don't want to do anything drastic if the leap function is handled
+ * by the kernel.
+ */
+extern int pll_control; /* set nonzero if kernel pll in uss */
+
+/*
+ * Internal leap bits. If we see leap bits set during the last
+ * hour we set these.
+ */
+u_char leapbits;
+
+/*
+ * Constants.
+ */
+#define OKAYTOSETWARNING (31*24*60*60)
+#define DAYBEFORE (24*60*60)
+#define TWOHOURSBEFORE (2*60*60)
+#define FIVEMINUTES (5*60)
+#define ONEMINUTE (60)
+
+/*
+ * Imported from the timer module.
+ */
+extern u_long current_time;
+
+
+/*
+ * Some statistics counters
+ */
+u_long leap_processcalls; /* calls to leap_process */
+u_long leap_notclose; /* leap found to be a long time from now */
+u_long leap_monthofleap; /* in the month of a leap */
+u_long leap_dayofleap; /* This is the day of the leap */
+u_long leap_hoursfromleap; /* only 2 hours from leap */
+u_long leap_happened; /* leap process saw the leap */
+
+/*
+ * Imported from the main module
+ */
+extern int debug;
+
+
+static void setnexttimeout P((u_long));
+
+/*
+ * init_leap - initialize the leap module's data.
+ */
+void
+init_leap()
+{
+ /*
+ * Zero the indicators. Schedule an event for just after
+ * initialization so we can sort things out.
+ */
+ leap_indicator = leap_warning = leap_mask = 0;
+ leap_timer = 1<<EVENT_TIMEOUT;
+ leapbits = 0;
+
+ leap_processcalls = leap_notclose = 0;
+ leap_monthofleap = leap_dayofleap = 0;
+ leap_hoursfromleap = leap_happened = 0;
+}
+
+
+/*
+ * leap_process - process a leap event expiry and/or a system time step
+ */
+void
+leap_process()
+{
+ u_long leapnext;
+ u_long leaplast;
+ l_fp ts;
+ u_char bits;
+ extern u_char sys_leap;
+
+ leap_processcalls++;
+ get_systime(&ts);
+ calleapwhen((u_long)ts.l_ui, &leaplast, &leapnext);
+
+ /*
+ * Figure out what to do based on how long to the next leap.
+ */
+ if (leapnext > OKAYTOSETWARNING) {
+ if (leaplast < ONEMINUTE) {
+ /*
+ * The golden moment! See if there's anything
+ * to do.
+ */
+ leap_happened++;
+ bits = 0;
+ leap_mask = 0;
+ if (leap_indicator != 0)
+ bits = leap_indicator;
+ else if (leapbits != 0)
+ bits = leapbits;
+
+ if (bits != 0 && !pll_control) {
+ l_fp tmp;
+
+ /*
+ * Step the clock 1 second in the proper
+ * direction.
+ */
+ if (bits == LEAP_DELSECOND)
+ tmp.l_i = 1;
+ else
+ tmp.l_i = -1;
+ tmp.l_uf = 0;
+
+ step_systime(&tmp);
+#ifdef SLEWALWAYS
+ syslog(LOG_NOTICE,
+ "leap second occured, slewed time %s 1 second",
+ tmp.l_i > 0 ? "forward" : "back");
+#else
+ syslog(LOG_NOTICE,
+ "leap second occured, stepped time %s 1 second",
+ tmp.l_i > 0 ? "forward" : "back");
+#endif
+ }
+ } else {
+ leap_notclose++;
+ }
+ leap_warning = 0;
+ } else {
+ if (leapnext > DAYBEFORE)
+ leap_monthofleap++;
+ else if (leapnext > TWOHOURSBEFORE)
+ leap_dayofleap++;
+ else
+ leap_hoursfromleap++;
+ }
+
+ if (leapnext > DAYBEFORE) {
+ leap_indicator = 0;
+ leapbits = 0;
+ /*
+ * Berkeley's setitimer call does result in alarm
+ * signal drift despite rumours to the contrary.
+ * Schedule an event no more than 24 hours into
+ * the future to allow the event time to be
+ * recomputed.
+ */
+ if ((leapnext - DAYBEFORE) >= DAYBEFORE)
+ setnexttimeout((u_long)DAYBEFORE);
+ else
+ setnexttimeout(leapnext - DAYBEFORE);
+ return;
+ }
+
+ /*
+ * Here we're in the day of the leap. Set the leap indicator
+ * bits from the warning, if necessary.
+ */
+ if (leap_indicator == 0 && leap_warning != 0)
+ leap_indicator = leap_warning;
+ leap_mask = LEAP_NOTINSYNC;
+ if (leapnext > TWOHOURSBEFORE) {
+ leapbits = 0;
+ setnexttimeout(leapnext - TWOHOURSBEFORE);
+ return;
+ }
+
+ /*
+ * Here we're in the final 2 hours. If sys_leap is set, set
+ * leapbits to it.
+ */
+ if (sys_leap == LEAP_ADDSECOND || sys_leap == LEAP_DELSECOND)
+ leapbits = sys_leap;
+ setnexttimeout((leapnext > FIVEMINUTES) ? FIVEMINUTES : leapnext);
+}
+
+
+/*
+ * setnexttimeout - set the next leap alarm
+ */
+static void
+setnexttimeout(secs)
+ u_long secs;
+{
+ /*
+ * We try to aim the time out at between 1 and 1+(1<<EVENT_TIMEOUT)
+ * seconds after the desired time.
+ */
+ leap_timer = (secs + 1 + (1<<EVENT_TIMEOUT) + current_time)
+ & ~((1<<EVENT_TIMEOUT)-1);
+}
+
+
+/*
+ * leap_setleap - set leap_indicator and/or leap_warning. Return failure
+ * if we don't want to do it.
+ */
+int
+leap_setleap(indicator, warning)
+ int indicator;
+ int warning;
+{
+ u_long leapnext;
+ u_long leaplast;
+ l_fp ts;
+ int i;
+
+ get_systime(&ts);
+ calleapwhen((u_long)ts.l_ui, &leaplast, &leapnext);
+
+ i = 0;
+ if (warning != ~0)
+ if (leapnext > OKAYTOSETWARNING)
+ i = 1;
+
+ if (indicator != ~0)
+ if (leapnext > DAYBEFORE)
+ i = 1;
+
+ if (i) {
+ syslog(LOG_ERR,
+ "attempt to set leap bits at unlikely time of month");
+ return 0;
+ }
+
+ if (warning != ~0)
+ leap_warning = warning;
+
+ if (indicator != ~0) {
+ if (indicator == LEAP_NOWARNING) {
+ leap_warning = LEAP_NOWARNING;
+ }
+ leap_indicator = indicator;
+ }
+ return 1;
+}
+
+/*
+ * leap_actual
+ *
+ * calculate leap value - pass arg through of no local
+ * configuration. Otherwise ise local configuration
+ * (only used to cope with broken time servers and
+ * broken refclocks)
+ *
+ * Mapping of leap_indicator:
+ * LEAP_NOWARNING
+ * pass peer value to sys_leap - usual operation
+ * LEAP_ADD/DEL_SECOND
+ * pass LEAP_ADD/DEL_SECOND to sys_leap
+ * LEAP_NOTINSYNC
+ * pass LEAP_NOWARNING to sys_leap - effectively ignores leap
+ */
+/* there seems to be a bug in the IRIX 4 compiler which prevents
+ u_char from beeing used in prototyped functions
+ AIX also suffers from this.
+ So give up and define it terms of int.
+*/
+int
+leap_actual(l)
+ int l ;
+{
+ if (leap_indicator != LEAP_NOWARNING) {
+ if (leap_indicator == LEAP_NOTINSYNC)
+ return LEAP_NOWARNING;
+ else
+ return leap_indicator;
+ } else {
+ return l;
+ }
+}
+
diff --git a/usr.sbin/xntpd/xntpd/ntp_loopfilter.c b/usr.sbin/xntpd/xntpd/ntp_loopfilter.c
new file mode 100644
index 0000000..f2866d5
--- /dev/null
+++ b/usr.sbin/xntpd/xntpd/ntp_loopfilter.c
@@ -0,0 +1,721 @@
+/*
+ * ntp_loopfilter.c - implements the NTP loop filter algorithm
+ */
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/time.h>
+#include <signal.h>
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_unixtime.h"
+#include "ntp_stdlib.h"
+
+#ifdef KERNEL_PLL
+#ifdef HAVE_SYS_TIMEX_H
+#include <sys/timex.h>
+#else
+#include "sys/timex.h"
+#endif /* not HAVE_SYS_TIMEX_H */
+
+#ifndef NTP_SYSCALLS_LIBC
+#define ntp_gettime(t) syscall(SYS_ntp_gettime, (t))
+#define ntp_adjtime(t) syscall(SYS_ntp_adjtime, (t))
+#endif /* not NTP_SYSCALLS_LIBC */
+#endif /* KERNEL_PLL */
+
+/*
+ * The loop filter is implemented in slavish adherence to the
+ * specification (Section 5), except that for consistency we
+ * mostly carry the quantities in the same units as appendix G.
+ *
+ * Note that the long values below are the fractional portion of
+ * a long fixed-point value. This limits these values to +-0.5
+ * seconds. When adjustments are capped inside this range (see
+ * CLOCK_MAX_{I,F}) both the clock_adjust and the compliance
+ * registers should be fine. (When the compliance is above 16, it
+ * will at most accumulate 2 ** CLOCK_MULT times the maximum offset,
+ * which means it fits in a s_fp.)
+ *
+ * The skew compensation is a special case. In version 2, it was
+ * kept in ms / 4s (i.e., CLOCK_FREQ was 10). In version 3 (Section 5)
+ * it seems to be 2 ** -16ms / 4s in a s_fp for a maximum of +-125ppm
+ * (stated maximum 100ppm). Since this seems about to change to a
+ * larger range, it will be kept in units of 2 ** -20 (CLOCK_DSCALE)
+ * in an s_fp (mainly because that's nearly the same as parts per
+ * million). Note that this is ``seconds per second'', whereas a
+ * clock adjustment is a 32-bit fraction of a second to be applied
+ * every 2 ** CLOCK_ADJ seconds; to find it, shift the drift right by
+ * (CLOCK_DSCALE - 16 - CLOCK_ADJ). When updating the drift, on the
+ * other hand, the CLOCK_FREQ factor from the spec assumes the value to
+ * be in ``seconds per 4 seconds''; to get our units, CLOCK_ADJ must be
+ * added to the shift.
+ *
+ * Kernel PLL/PPS state machine
+ *
+ * The following state machine is used when the kernel PLL modifications
+ * described in the README.kernel file are present. The initial
+ * configuration routine loop_config() sets up the initial frequency
+ * estimate and tests if the kernel modifications are present. If so and
+ * the PLL mode bit 1 (STA_PLL) of the mode word in the drift file
+ * (ntp.drift) is set, pll_control is set true and the kernel pll is
+ * enabled. If the kernel modifications are present and the PLL mode bit
+ * 2 (STA_PPSFREQ) is set, the kernel PPS frequency discipline is
+ * enabled.
+ *
+ * Each update to a prefer peer sets pps_update true if it survives the
+ * intersection algorithm and its time is within range. The PPS time
+ * discipline is enabled (STA_PPSTIME bit set in the status word) when
+ * pps_update is true and the PPS frequency discipline is enabled. If
+ * the PPS time discipline is enabled and the kernel reports a PPS
+ * signal is present, the pps_control variable is set to the current
+ * time. If the current time is later than pps_control by PPS_MAXAGE
+ * (120 s), this variable is set to zero.
+ *
+ * The pll_enable switch can be set both at configuration time and at
+ * run time using xntpdc. If true, the kernel modifications are active
+ * as described above; if false, the kernel is bypassed entirely (except
+ * for the PPS frequency update, if enabled) and the daemon PLL used
+ * instead.
+ */
+#define RSH_DRIFT_TO_FRAC (CLOCK_DSCALE - 16)
+#define RSH_DRIFT_TO_ADJ (RSH_DRIFT_TO_FRAC - CLOCK_ADJ)
+#define RSH_FRAC_TO_FREQ (CLOCK_FREQ + CLOCK_ADJ - RSH_DRIFT_TO_FRAC)
+#define PPS_MAXAGE 120 /* kernel pps signal timeout (s) */
+
+/*
+ * Program variables
+ */
+ l_fp last_offset; /* last clock offset */
+ u_long last_time; /* time of last clock update (s) */
+ u_fp clock_stability; /* clock stability (ppm) */
+ s_fp clock_frequency; /* clock frequency error (ppm) */
+ s_fp drift_comp; /* pll frequency (ppm) */
+static long clock_adjust; /* clock adjust (fraction only) */
+static s_fp max_comp; /* max frequency offset (ppm) */
+ int tc_counter; /* poll-adjust counter */
+ int pll_status; /* status bits for kernel pll */
+ int pll_control; /* true if working kernel pll */
+ int pll_enable; /* true if pll enabled */
+ u_long pps_control; /* last pps sample time */
+ int pps_update; /* pps update valid */
+ int fdpps = -1; /* pps file descriptor */
+
+/*
+ * Imported from the ntp_proto module
+ */
+extern s_fp sys_rootdelay; /* root delay */
+extern u_fp sys_rootdispersion; /* root dispersion */
+extern struct peer *sys_peer; /* system peer pointer */
+extern u_char sys_poll; /* log2 of system poll interval */
+extern u_char sys_leap; /* system leap bits */
+extern l_fp sys_refskew; /* accumulated skew since last update */
+extern u_fp sys_maxd; /* max dispersion of survivor list */
+
+/*
+ * Imported from ntp_io.c
+ */
+extern struct interface *loopback_interface;
+
+/*
+ * Imported from ntpd module
+ */
+extern int debug; /* global debug flag */
+
+/*
+ * Imported from timer module
+ */
+extern u_long current_time; /* like it says, in seconds */
+
+/*
+ * Imported from leap module
+ */
+extern u_char leapbits; /* sanitized leap bits */
+
+#if defined(KERNEL_PLL)
+#define MOD_BITS (MOD_OFFSET | MOD_MAXERROR | MOD_ESTERROR | \
+ MOD_STATUS | MOD_TIMECONST)
+extern int sigvec P((int, struct sigvec *, struct sigvec *));
+#ifndef NTP_SYSCALLS_LIBC
+extern int syscall P((int, void *, ...));
+#endif /* no NTP syscalls in libc */
+void pll_trap P((int));
+
+static struct sigvec sigsys; /* current sigvec status */
+static struct sigvec newsigsys; /* new sigvec status */
+#endif /* KERNEL_PLL */
+
+/*
+ * init_loopfilter - initialize loop filter data
+ */
+void
+init_loopfilter()
+{
+ extern u_long tsf_maxslew;
+ u_long tsf_limit;
+
+ /*
+ * Limit for drift_comp, minimum of two values. The first is to
+ * avoid signed overflow, the second to keep within 75% of the
+ * maximum adjustment possible in adj_systime().
+ */
+ max_comp = 0x7fff0000;
+ tsf_limit = ((tsf_maxslew >> 1) + (tsf_maxslew >> 2));
+ if ((max_comp >> RSH_DRIFT_TO_ADJ) > tsf_limit)
+ max_comp = tsf_limit << RSH_DRIFT_TO_ADJ;
+
+ /*
+ * Reset clockworks
+ */
+ drift_comp = 0;
+ clock_adjust = 0;
+ tc_counter = 0;
+ sys_poll = NTP_MINPOLL;
+
+ L_CLR(&last_offset);
+ last_time = 0;
+ clock_frequency = 0;
+ clock_stability = 0;
+ pps_update = pps_control = 0;
+}
+
+/*
+ * local_clock - the NTP logical clock loop filter. Returns 1 if the
+ * clock was stepped, 0 if it was slewed and -1 if it is hopeless.
+ */
+int
+local_clock(fp_offset, peer)
+ l_fp *fp_offset; /* best offset estimate */
+ struct peer *peer; /* from peer - for messages */
+{
+ long offset;
+ long tmp;
+ int return_code;
+ l_fp ftmp;
+ s_fp stmp;
+ u_fp smax;
+ long allan;
+ long interval;
+#if defined(KERNEL_PLL)
+ struct timex ntv;
+#endif /* KERNEL_PLL */
+
+ /*
+ * Initialize estimated measurement error and Allan variance
+ * intercept point. The measurement error is assumed the sum of
+ * the peer dispersion plus select dispersion, which seems
+ * reasonable. The Allan variance intercept point is assumed
+ * at MAXSEC for reference clocks and twice that for peer
+ * clocks, which seems cowardly.
+ */
+ if (peer->refclktype)
+ allan = CLOCK_MAXSEC;
+ else
+ allan = CLOCK_MAXSEC << 1;
+
+ if (!last_time)
+ last_time = current_time;
+ interval = (long)(current_time - last_time);
+ clock_adjust = 0;
+ offset = fp_offset->l_f;
+ smax = peer->dispersion + peer->selectdisp;
+ return_code = 0;
+
+#ifdef DEBUG
+ if (debug > 1)
+ printf(
+ "local_clock: offset %s peer %s watch %ld)\n",
+ lfptoa(fp_offset, 6), ntoa(&peer->srcadr),
+ interval);
+#endif
+
+ /*
+ * If the clock is way off, don't tempt fate by correcting it.
+ */
+ ftmp = *fp_offset;
+ if (L_ISNEG(&ftmp))
+ L_NEG(&ftmp);
+ if (ftmp.l_ui >= CLOCK_WAYTOOBIG) {
+ syslog(LOG_ERR,
+ "time error %s is way too large (set clock manually)",
+ lfptoa(fp_offset, 6));
+#ifndef BIGTIMESTEP
+ return (-1);
+#endif /* BIGTIMESTEP */
+
+ /*
+ * If the magnitude of the offset is greater than CLOCK.MAX
+ * (128 ms), reset the time and frequency. We are quite
+ * aggresive here, since the intrinsic clock oscillator
+ * frequency error can be quite large, sometimes over +-300 ppm.
+ * With something this large and a noisy peer, the casual time
+ * updates wander right through the acceptable range, causing
+ * this section to trigger.
+ */
+ } else if (ftmp.l_ui > CLOCK_MAX_I || (ftmp.l_ui == CLOCK_MAX_I
+ && ftmp.l_uf >= CLOCK_MAX_F)) {
+ tc_counter = 0;
+ sys_poll = peer->minpoll;
+
+ /*
+ * Either we are not in synchronization, or we have gone
+ * CLOCK_MINSTEP (900 s) since the last acceptable
+ * update. We step the clock and leave the frequency
+ * alone. Since the clock filter has been reset, the
+ * dispersions will be high upon recovery and the quick-
+ * march code below will trigger to keep the clock in
+ * bounds.
+ */
+ if (sys_leap == LEAP_NOTINSYNC || interval >
+ CLOCK_MINSTEP) {
+ step_systime(fp_offset);
+ syslog(LOG_NOTICE,
+
+ "time reset (%s) %s s",
+#ifdef SLEWALWAYS
+ "slew",
+#else
+ "step",
+#endif
+ lfptoa(fp_offset, 6));
+ return_code = 1;
+
+ /*
+ * The local clock is out of range, but we haven't
+ * allowed enough time for the peer (usually a radio
+ * clock) to recover after a leap second. Pretend we wuz
+ * never here.
+ */
+ } else
+ return (return_code);
+
+ /*
+ * This code segment works when the clock-adjustment code is
+ * implemented in the kernel, which at present is only in the
+ * (modified) SunOS 4.1, Ultrix 4.3 and OSF/1 kernels. In the
+ * case of the DECstation 5000/240 and Alpha AXP, additional
+ * kernel modifications provide a true microsecond clock. We
+ * know the scaling of the frequency variable (s_fp) is the same
+ * as the kernel variable (1 << SHIFT_USEC = 16).
+ */
+#if defined(KERNEL_PLL)
+ } else if (pll_control && pll_enable) {
+ l_fp pps_offset;
+ u_fp pps_dispersion;
+
+ /*
+ * We initialize the structure for the ntp_adjtime()
+ * system call. We have to convert everything to
+ * microseconds first. Afterwards, remember the
+ * frequency offset for the drift file.
+ */
+ memset((char *)&ntv, 0, sizeof ntv);
+ ntv.modes = MOD_BITS;
+ if (offset >= 0) {
+ TSFTOTVU(offset, ntv.offset);
+ } else {
+ TSFTOTVU(-offset, ntv.offset);
+ ntv.offset = -ntv.offset;
+ }
+ TSFTOTVU(sys_rootdispersion + sys_rootdelay / 2,
+ ntv.maxerror);
+ TSFTOTVU(sys_rootdispersion, ntv.esterror);
+ ntv.status = pll_status & (STA_PLL | STA_PPSFREQ);
+ if (pps_update && pll_status & STA_PPSFREQ)
+ ntv.status |= STA_PPSTIME;
+ if (sys_leap & LEAP_ADDSECOND &&
+ sys_leap & LEAP_DELSECOND)
+ ntv.status |= STA_UNSYNC;
+ else if (sys_leap & LEAP_ADDSECOND)
+ ntv.status |= STA_INS;
+ else if (sys_leap & LEAP_DELSECOND)
+ ntv.status |= STA_DEL;
+ ntv.constant = min(peer->ppoll, sys_poll) - NTP_MINPOLL;
+ (void)ntp_adjtime(&ntv);
+ drift_comp = ntv.freq;
+ pll_status = ntv.status;
+
+ /*
+ * If the kernel pps discipline is working, monitor its
+ * performance.
+ */
+ if (pll_status & STA_PPSTIME && pll_status &
+ STA_PPSSIGNAL && ntv.shift) {
+ if (ntv.offset >= 0)
+ TVUTOTSF(ntv.offset, offset);
+ else {
+ TVUTOTSF(-ntv.offset, offset);
+ offset = -offset;
+ }
+ L_CLR(&pps_offset);
+ L_ADDF(&pps_offset, offset);
+ TVUTOTSF(ntv.jitter, tmp);
+ pps_dispersion = (tmp >> 16) & 0xffff;
+ if (!pps_control)
+ syslog(LOG_INFO,
+ "kernel pps sync enabled");
+ pps_control = current_time;
+ record_peer_stats(&loopback_interface->sin,
+ ctlsysstatus(), fp_offset, 0,
+ pps_dispersion);
+ }
+#endif /* KERNEL_PLL */
+
+ /*
+ * If the dispersion exceeds 128 ms, we need to quick-march it
+ * to nominal zero offset and wait for the next update. This is
+ * necessary when the intrinsic frequency error is large and the
+ * clock has drifted during the interval the clock filter was
+ * stabilizing. Note that, if unsynchronized, the dispersion is
+ * always greater than 128 ms, so we don't need a check for
+ * that.
+ */
+ } else if (smax > CLOCK_MAX_FP) {
+ clock_adjust = offset;
+
+ /*
+ * If the dispersion has increased substantially over the
+ * previous value, we have a spike which probably should be
+ * suppressed. A factor of eight has been found reasonable by
+ * simulation.
+ */
+ } else if (smax > sys_maxd << 3) {
+ return (0);
+
+ /*
+ * If the interval between corrections is less than the Allan
+ * variance intercept point, we use a phase-lock loop to compute
+ * new values of time and frequency. The bandwidth is controlled
+ * by the time constant, which is adjusted in response to the
+ * phase error and dispersion. Note the frequency is not changed
+ * if the local clock driver is in control.
+ */
+ } else if (interval < allan) {
+ int time_constant = min(peer->ppoll, sys_poll) -
+ NTP_MINPOLL;
+ int ltmp = interval;
+
+ if (offset < 0)
+ clock_adjust = -(-offset >> time_constant);
+ else
+ clock_adjust = offset >> time_constant;
+ if (interval && !(peer->refclktype ==
+ REFCLK_LOCALCLOCK)) {
+ tmp = peer->maxpoll;
+ while (ltmp < (1 << peer->maxpoll)) {
+ tmp--;
+ ltmp <<= 1;
+ }
+ tmp = (RSH_FRAC_TO_FREQ - tmp) + time_constant +
+ time_constant;
+ if (offset < 0)
+ tmp = -(-offset >> tmp);
+ else
+ tmp = offset >> tmp;
+ drift_comp += tmp;
+ }
+
+ /*
+ * If the interval between corrections is greater than the Allan
+ * variance intercept point, we use a hybrid frequency-lock loop
+ * to compute new values of phase and frequency. The following
+ * code is based on ideas suggested by Judah Levine of NIST and
+ * used in his "lockclock" implementation of ACTS. The magic
+ * factor of 4 in the left shift is to convert from s_fp to ppm.
+ */
+ } else {
+ clock_adjust = offset;
+ stmp = (offset / interval) << 4;
+ if (stmp < 0)
+ drift_comp -= -stmp >> CLOCK_G;
+ else
+ drift_comp += stmp >> CLOCK_G;
+ }
+
+ /*
+ * As a sanity check, we clamp the frequency not to exceed the
+ * slew rate of the stock Unix adjtime() system call. Finally,
+ * do a little housekeeping.
+ */
+ if (drift_comp > max_comp)
+ drift_comp = max_comp;
+ else if (drift_comp < -max_comp)
+ drift_comp = -max_comp;
+ if (interval > (1 << (peer->minpoll - 1))) {
+
+ /*
+ * Determine when to adjust the poll interval. We do
+ * this regardless of what source controls the loop,
+ * since we might flap back and forth between sources.
+ */
+ stmp = LFPTOFP(fp_offset);
+ if (stmp < 0)
+ stmp = -stmp;
+ if (stmp > smax) {
+ tc_counter -= (int)sys_poll << 1;
+ if (tc_counter < -CLOCK_LIMIT) {
+ tc_counter = -CLOCK_LIMIT;
+ if (sys_poll > peer->minpoll) {
+ sys_poll--;
+ tc_counter = 0;
+ }
+ }
+ } else {
+ tc_counter += (int)sys_poll;
+ if (tc_counter > CLOCK_LIMIT) {
+ tc_counter = CLOCK_LIMIT;
+ if (sys_poll < peer->maxpoll) {
+ sys_poll++;
+ tc_counter = 0;
+ }
+ }
+ }
+
+ /*
+ * Calculate the frequency offset and frequency
+ * stability. These are useful for performance
+ * monitoring, but do not affect the loop variables. The
+ * results are scaled as a s_fp in ppm, because we know
+ * more than we should.
+ */
+ ftmp = *fp_offset;
+ L_SUB(&ftmp, &last_offset);
+ clock_frequency = (LFPTOFP(&ftmp) / interval) << 20;
+ if (clock_frequency < -max_comp)
+ clock_frequency = -max_comp;
+ else if (clock_frequency > max_comp)
+ clock_frequency = max_comp;
+ stmp = clock_frequency;
+ if (stmp < 0)
+ stmp = -stmp;
+ stmp -= clock_stability;
+ if (stmp < 0)
+ clock_stability -= -stmp >> NTP_MAXD;
+ else
+ clock_stability += stmp >> NTP_MAXD;
+ }
+ last_offset = *fp_offset;
+ last_time = current_time;
+
+#ifdef DEBUG
+ if (debug > 1)
+ printf(
+ "local_clock: phase %s freq %s err %s allan %ld poll %d\n",
+ mfptoa((clock_adjust < 0 ? -1 : 0), clock_adjust,
+ 6), fptoa(drift_comp, 3), fptoa(smax, 6), allan,
+ sys_poll);
+#endif /* DEBUG */
+
+ (void) record_loop_stats(fp_offset, drift_comp, sys_poll);
+
+ /*
+ * Whew. I've had enough.
+ */
+ return (return_code);
+}
+
+
+/*
+ * adj_host_clock - Called every 1 << CLOCK_ADJ seconds to update host
+ * clock
+ */
+void
+adj_host_clock()
+{
+ register long adjustment;
+ l_fp offset;
+
+ /*
+ * Update the dispersion since the last update. Don't allow
+ * frequency measurements over periods longer than NTP_MAXAGE
+ * (86400 s = one day).
+ */
+ if (current_time - last_time > NTP_MAXAGE)
+ last_time = 0;
+ L_ADDUF(&sys_refskew, NTP_SKEWINC);
+
+ /*
+ * Declare PPS kernel unsync if the pps signal has been heard
+ * during the last few minutes.
+ */
+ if (pps_control && current_time - pps_control > PPS_MAXAGE) {
+ if (pps_control)
+ syslog(LOG_INFO, "kernel pps sync disabled");
+ pps_control = 0;
+ }
+
+ /*
+ * If the phase-lock loop is not implemented in the kernel, we
+ * do it the hard way using incremental adjustments and the
+ * adjtime() system call.
+ */
+ if (pll_control && pll_enable)
+ return;
+ adjustment = clock_adjust;
+ if (adjustment < 0)
+ adjustment = -(-adjustment >> CLOCK_PHASE);
+ else
+ adjustment >>= CLOCK_PHASE;
+
+ clock_adjust -= adjustment;
+ if (drift_comp < 0)
+ adjustment -= -drift_comp >> RSH_DRIFT_TO_ADJ;
+ else
+ adjustment += drift_comp >> RSH_DRIFT_TO_ADJ;
+
+ /*
+ * Intricate wrinkle. If the local clock driver is in use and
+ * selected for synchronization, somebody else may be tinker the
+ * adjtime() syscall. In this case we have to avoid calling
+ * adjtime(), since that may truncate the other guy's requests.
+ * That means the local clock fudge time and frequency
+ * adjustments don't work in that case. Caveat empty.
+ */
+ if (sys_peer) {
+ if (sys_peer->refclktype == REFCLK_LOCALCLOCK &&
+ sys_peer->flags & FLAG_PREFER)
+ return;
+ }
+ L_CLR(&offset);
+ L_ADDF(&offset, adjustment);
+ adj_systime(&offset);
+}
+
+
+/*
+ * adj_frequency - adjust local clock frequency
+ */
+void
+adj_frequency(freq)
+ s_fp freq; /* frequency (ppm) */
+{
+#if defined(KERNEL_PLL)
+ struct timex ntv;
+#endif /* KERNEL_PLL */
+
+ /*
+ * This routine adjusts the frequency offset. It is used by the
+ * local clock driver to adjust frequency when no external
+ * discipline source is available and by the acts driver when
+ * the interval between updates is greater than 1 <<
+ * NTP_MAXPOLL. Note that the maximum offset is limited by
+ * max_comp when the daemon pll is used, but the maximum may be
+ * different when the kernel pll is used.
+ */
+ drift_comp += freq;
+ if (drift_comp > max_comp)
+ drift_comp = max_comp;
+ else if (drift_comp < -max_comp)
+ drift_comp = -max_comp;
+#if defined(KERNEL_PLL)
+ /*
+ * If the phase-lock code is implemented in the kernel, set the
+ * kernel frequency as well, but be sure to set drift_comp to
+ * the actual frequency.
+ */
+ if (!(pll_control && pll_enable))
+ return;
+ memset((char *)&ntv, 0, sizeof ntv);
+ ntv.modes = MOD_FREQUENCY;
+ ntv.freq = freq + drift_comp;
+ (void)ntp_adjtime(&ntv);
+ drift_comp = ntv.freq;
+#endif /* KERNEL_PLL */
+}
+
+
+/*
+ * loop_config - configure the loop filter
+ */
+void
+loop_config(item, lfp_value, int_value)
+ int item;
+ l_fp *lfp_value;
+ int int_value;
+{
+#if defined(KERNEL_PLL)
+ struct timex ntv;
+#endif /* KERNEL_PLL */
+
+#ifdef DEBUG
+ if (debug) {
+ printf("loop_config %d %s %x\n", item,
+ lfptoa(lfp_value, 3), int_value);
+ }
+#endif
+ switch (item) {
+
+ case LOOP_DRIFTCOMP:
+ drift_comp = LFPTOFP(lfp_value);
+ if (drift_comp > max_comp)
+ drift_comp = max_comp;
+ if (drift_comp < -max_comp)
+ drift_comp = -max_comp;
+
+#if defined(KERNEL_PLL)
+ /*
+ * If the phase-lock code is implemented in the kernel,
+ * give the time_constant and saved frequency offset to
+ * the kernel. If not, no harm is done. We do this
+ * whether or not the use of the kernel mods is
+ * requested, in order to clear out the trash from
+ * possible prior customers.
+ */
+ memset((char *)&ntv, 0, sizeof ntv);
+ pll_status = int_value & (STA_PLL | STA_PPSFREQ);
+ if (pll_status & STA_PLL)
+ pll_control = 1;
+ else
+ pll_control = 0;
+ ntv.modes = MOD_BITS | MOD_FREQUENCY;
+ if (pll_status) {
+ ntv.freq = drift_comp;
+ ntv.maxerror = NTP_MAXDISPERSE;
+ ntv.esterror = NTP_MAXDISPERSE;
+ ntv.status = pll_status | STA_UNSYNC;
+ ntv.constant = sys_poll - NTP_MINPOLL;
+ }
+ newsigsys.sv_handler = pll_trap;
+ newsigsys.sv_mask = 0;
+ newsigsys.sv_flags = 0;
+ if ((sigvec(SIGSYS, &newsigsys, &sigsys)))
+ syslog(LOG_ERR,
+ "sigvec() fails to save SIGSYS trap: %m");
+ (void)ntp_adjtime(&ntv);
+ if ((sigvec(SIGSYS, &sigsys,
+ (struct sigvec *)NULL)))
+ syslog(LOG_ERR,
+ "sigvec() fails to restore SIGSYS trap: %m");
+ if (pll_control)
+ syslog(LOG_NOTICE,
+ "using kernel phase-lock loop %04x",
+ ntv.status);
+ else
+ syslog(LOG_NOTICE,
+ "using xntpd phase-lock loop");
+#endif /* KERNEL_PLL */
+ break;
+
+ default:
+ /* sigh */
+ break;
+ }
+}
+
+
+#if defined(KERNEL_PLL)
+/*
+ * _trap - trap processor for undefined syscalls
+ *
+ * This nugget is called by the kernel when the SYS_ntp_adjtime()
+ * syscall bombs because the silly thing has not been implemented in
+ * the kernel. In this case the phase-lock loop is emulated by
+ * the stock adjtime() syscall and a lot of indelicate abuse.
+ */
+RETSIGTYPE
+pll_trap(int sig)
+{
+ pll_control = 0;
+}
+#endif /* KERNEL_PLL */
+
diff --git a/usr.sbin/xntpd/xntpd/ntp_monitor.c b/usr.sbin/xntpd/xntpd/ntp_monitor.c
new file mode 100644
index 0000000..e6a1c4f
--- /dev/null
+++ b/usr.sbin/xntpd/xntpd/ntp_monitor.c
@@ -0,0 +1,349 @@
+/*
+ * ntp_monitor.c - monitor who is using the xntpd server
+ */
+#include <stdio.h>
+#include <sys/types.h>
+#include <signal.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_if.h"
+#include "ntp_stdlib.h"
+
+/*
+ * I'm still not sure I like what I've done here. It certainly consumes
+ * memory like it is going out of style, and also may not be as low
+ * overhead as I'd imagined.
+ *
+ * Anyway, we record statistics based on source address, mode and version
+ * (for now, anyway. Check the code). The receive procedure calls us with
+ * the incoming rbufp before it does anything else.
+ *
+ * Each entry is doubly linked into two lists, a hash table and a
+ * most-recently-used list. When a packet arrives it is looked up
+ * in the hash table. If found, the statistics are updated and the
+ * entry relinked at the head of the MRU list. If not found, a new
+ * entry is allocated, initialized and linked into both the hash
+ * table and at the head of the MRU list.
+ *
+ * Memory is usually allocated by grabbing a big chunk of new memory
+ * and cutting it up into littler pieces. The exception to this when we
+ * hit the memory limit. Then we free memory by grabbing entries off
+ * the tail for the MRU list, unlinking from the hash table, and
+ * reinitializing.
+ *
+ * trimmed back memory consumption ... jdg 8/94
+ */
+
+/*
+ * Limits on the number of structures allocated. This limit is picked
+ * with the illicit knowlege that we can only return somewhat less
+ * than 8K bytes in a mode 7 response packet, and that each structure
+ * will require about 20 bytes of space in the response.
+ *
+ * ... I don't believe the above is true anymore ... jdg
+ */
+#ifndef MAXMONMEM
+#define MAXMONMEM 600 /* we allocate up to 400 structures */
+#endif
+#ifndef MONMEMINC
+#define MONMEMINC 40 /* allocate them 40 at a time */
+#endif
+
+/*
+ * Hashing stuff
+ */
+#define MON_HASH_SIZE 128
+#define MON_HASH_MASK (MON_HASH_SIZE-1)
+#define MON_HASH(addr) ((int)(ntohl((addr)) & MON_HASH_MASK))
+
+/*
+ * Pointers to the hash table, the MRU list and the count table. Memory
+ * for the hash and count tables is only allocated if monitoring is turned on.
+ */
+static struct mon_data *mon_hash[MON_HASH_SIZE]; /* array of list ptrs */
+ struct mon_data mon_mru_list;
+ struct mon_data mon_fifo_list;
+/*
+ * List of free structures structures, and counters of free and total
+ * structures. The free structures are linked with the hash_next field.
+ */
+static struct mon_data *mon_free; /* the free list or null if none */
+
+static int mon_total_mem; /* total number of structures allocated */
+static int mon_mem_increments; /* number of times we've called malloc() */
+
+/*
+ * Initialization state. We may be monitoring, we may not. If
+ * we aren't, we may not even have allocated any memory yet.
+ */
+ int mon_enabled;
+static int mon_have_memory;
+
+/*
+ * Imported from the timer module
+ */
+extern u_long current_time;
+
+static void mon_getmoremem P((void));
+static void remove_from_hash P((struct mon_data *));
+
+/*
+ * init_mon - initialize monitoring global data
+ */
+void
+init_mon()
+{
+ /*
+ * Don't do much of anything here. We don't allocate memory
+ * until someone explicitly starts us.
+ */
+ mon_enabled = MON_OFF;
+ mon_have_memory = 0;
+
+ mon_total_mem = 0;
+ mon_mem_increments = 0;
+ mon_free = NULL;
+ memset((char *)&mon_hash[0], 0, sizeof mon_hash);
+ memset((char *)&mon_mru_list, 0, sizeof mon_mru_list);
+ memset((char *)&mon_fifo_list, 0, sizeof mon_fifo_list);
+}
+
+
+/*
+ * mon_start - start up the monitoring software
+ */
+void
+mon_start(mode)
+ int mode;
+{
+
+ if (mon_enabled != MON_OFF) {
+ mon_enabled |= mode;
+ return;
+ }
+ if (mode == MON_OFF)
+ return; /* Ooops.. */
+
+ if (!mon_have_memory) {
+ mon_total_mem = 0;
+ mon_mem_increments = 0;
+ mon_free = NULL;
+ mon_getmoremem();
+ mon_have_memory = 1;
+ }
+
+ mon_mru_list.mru_next = &mon_mru_list;
+ mon_mru_list.mru_prev = &mon_mru_list;
+
+ mon_fifo_list.fifo_next = &mon_fifo_list;
+ mon_fifo_list.fifo_prev = &mon_fifo_list;
+
+ mon_enabled = mode;
+}
+
+
+/*
+ * mon_stop - stop the monitoring software
+ */
+void
+mon_stop(mode)
+ int mode;
+{
+ register struct mon_data *md, *md_next;
+ register int i;
+
+ if (mon_enabled == MON_OFF)
+ return;
+ if ((mon_enabled & mode) == 0 || mode == MON_OFF)
+ return;
+
+ mon_enabled &= ~mode;
+ if (mon_enabled != MON_OFF)
+ return;
+
+ /*
+ * Put everything back on the free list
+ */
+ for (i = 0; i < MON_HASH_SIZE; i++) {
+ md = mon_hash[i]; /* get next list */
+ mon_hash[i] = NULL; /* zero the list head */
+ while (md != NULL) {
+ md_next = md->hash_next;
+ md->hash_next = mon_free;
+ mon_free = md;
+ md = md_next;
+ }
+ }
+
+ mon_mru_list.mru_next = &mon_mru_list;
+ mon_mru_list.mru_prev = &mon_mru_list;
+
+ mon_fifo_list.fifo_next = &mon_fifo_list;
+ mon_fifo_list.fifo_prev = &mon_fifo_list;
+}
+
+
+/*
+ * monitor - record stats about this packet
+ */
+void
+monitor(rbufp)
+ struct recvbuf *rbufp;
+{
+ register struct pkt *pkt;
+ register struct mon_data *md;
+ register u_long netnum;
+ register int hash;
+ register int mode;
+
+ if (mon_enabled == MON_OFF)
+ return;
+
+ pkt = &rbufp->recv_pkt;
+ netnum = NSRCADR(&rbufp->recv_srcadr);
+ hash = MON_HASH(netnum);
+ mode = PKT_MODE(pkt->li_vn_mode);
+
+ md = mon_hash[hash];
+ while (md != NULL) {
+ if (md->rmtadr == netnum &&
+ /* ?? md->interface == rbufp->dstadr && ?? */
+ md->mode == (u_char)mode) {
+ md->lasttime = current_time;
+ md->count++;
+ md->version = PKT_VERSION(pkt->li_vn_mode);
+ md->rmtport = NSRCPORT(&rbufp->recv_srcadr);
+
+ /*
+ * Shuffle him to the head of the
+ * mru list. What a crock.
+ */
+ md->mru_next->mru_prev = md->mru_prev;
+ md->mru_prev->mru_next = md->mru_next;
+ md->mru_next = mon_mru_list.mru_next;
+ md->mru_prev = &mon_mru_list;
+ mon_mru_list.mru_next->mru_prev = md;
+ mon_mru_list.mru_next = md;
+
+ return;
+ }
+ md = md->hash_next;
+ }
+
+ /*
+ * If we got here, this is the first we've heard of this
+ * guy. Get him some memory, either from the free list
+ * or from the tail of the MRU list.
+ */
+ if (mon_free == NULL && mon_total_mem >= MAXMONMEM) {
+ /*
+ * Get it from MRU list
+ */
+ md = mon_mru_list.mru_prev;
+ md->mru_prev->mru_next = &mon_mru_list;
+ mon_mru_list.mru_prev = md->mru_prev;
+
+ remove_from_hash(md);
+
+ /*
+ * Get it from FIFO list
+ */
+ md->fifo_prev->fifo_next = md->fifo_next;
+ md->fifo_next->fifo_prev = md->fifo_prev;
+
+ } else {
+ if (mon_free == NULL) /* if free list empty */
+ mon_getmoremem(); /* then get more */
+ md = mon_free;
+ mon_free = md->hash_next;
+ }
+
+ /*
+ * Got one, initialize it
+ */
+ md->lasttime = md->firsttime = current_time;
+ md->lastdrop = 0;
+ md->count = 1;
+ md->rmtadr = netnum;
+ md->rmtport = NSRCPORT(&rbufp->recv_srcadr);
+ md->mode = (u_char) mode;
+ md->version = PKT_VERSION(pkt->li_vn_mode);
+ md->interface = rbufp->dstadr;
+ md->cast_flags = ((rbufp->dstadr->flags & INT_MULTICAST) &&
+ rbufp->fd == md->interface->fd) ? MDF_MCAST: rbufp->fd ==
+ md->interface->bfd ? MDF_BCAST : MDF_UCAST;
+
+ /*
+ * Drop him into front of the hash table.
+ * Also put him on top of the MRU list
+ * and at bottom of FIFO list
+ */
+
+ md->hash_next = mon_hash[hash];
+ mon_hash[hash] = md;
+
+ md->mru_next = mon_mru_list.mru_next;
+ md->mru_prev = &mon_mru_list;
+ mon_mru_list.mru_next->mru_prev = md;
+ mon_mru_list.mru_next = md;
+
+ md->fifo_prev = mon_fifo_list.fifo_prev;
+ md->fifo_next = &mon_fifo_list;
+ mon_fifo_list.fifo_prev->fifo_next = md;
+ mon_fifo_list.fifo_prev = md;
+}
+
+
+/*
+ * mon_getmoremem - get more memory and put it on the free list
+ */
+static void
+mon_getmoremem()
+{
+ register struct mon_data *md;
+ register int i;
+ struct mon_data *freedata; /* 'old' free list (null) */
+
+ md = (struct mon_data *)emalloc(MONMEMINC * sizeof(struct mon_data));
+ freedata = mon_free;
+ mon_free = md;
+
+ for (i = 0; i < (MONMEMINC-1); i++) {
+ md->hash_next = (md + 1);
+ md++;
+ }
+
+ /*
+ * md now points at the last. Link in the rest of the chain.
+ */
+ md->hash_next = freedata;
+
+ mon_total_mem += MONMEMINC;
+ mon_mem_increments++;
+}
+
+static void
+remove_from_hash(md)
+struct mon_data *md;
+{ register int hash;
+ register struct mon_data *md_prev;
+
+ hash = MON_HASH(md->rmtadr);
+ if (mon_hash[hash] == md) {
+ mon_hash[hash] = md->hash_next;
+ } else {
+ md_prev = mon_hash[hash];
+ while (md_prev->hash_next != md) {
+ md_prev = md_prev->hash_next;
+ if (md_prev == NULL) {
+ /* logic error */
+ return;
+ }
+ }
+ md_prev->hash_next = md->hash_next;
+ }
+}
diff --git a/usr.sbin/xntpd/xntpd/ntp_peer.c b/usr.sbin/xntpd/xntpd/ntp_peer.c
new file mode 100644
index 0000000..f777734
--- /dev/null
+++ b/usr.sbin/xntpd/xntpd/ntp_peer.c
@@ -0,0 +1,667 @@
+/*
+ * ntp_peer.c - management of data maintained for peer associations
+ */
+#include <stdio.h>
+#include <sys/types.h>
+
+#include "ntpd.h"
+#include "ntp_stdlib.h"
+
+/*
+ * These routines manage the allocation of memory to peer structures
+ * and the maintenance of the peer hash table. The two main entry
+ * points are findpeer(), which looks for corresponding peer data
+ * in the peer list, newpeer(), which allocates a new peer structure
+ * and adds it to the list, and unpeer(), which demobilizes the association
+ * and deallocates the structure.
+ */
+
+/*
+ * The peer hash table (imported by the protocol module).
+ */
+struct peer *peer_hash[HASH_SIZE];
+int peer_hash_count[HASH_SIZE]; /* count of peers in each bucket */
+
+/*
+ * The association ID hash table. Used for lookups by association ID
+ */
+struct peer *assoc_hash[HASH_SIZE];
+int assoc_hash_count[HASH_SIZE];
+
+/*
+ * The free list. Clean structures only, please.
+ */
+struct peer *peer_free;
+int peer_free_count;
+
+/*
+ * Association ID. We initialize this value randomly, the assign a new
+ * value every time the peer structure is incremented.
+ */
+u_short current_association_ID;
+
+/*
+ * Memory allocation watermarks.
+ */
+#define INIT_PEER_ALLOC 15 /* initialize space for 15 peers */
+#define INC_PEER_ALLOC 5 /* when we run out, add 5 more */
+
+/*
+ * Miscellaneous statistic counters which may be queried.
+ */
+u_long peer_timereset; /* time stat counters were zeroed */
+u_long findpeer_calls; /* number of calls to findpeer */
+u_long assocpeer_calls; /* number of calls to findpeerbyassoc */
+u_long peer_allocations; /* number of allocations from the free list */
+u_long peer_demobilizations; /* number of structs freed to free list */
+int total_peer_structs; /* number of peer structs in circulation */
+
+/*
+ * default interface. Imported from the io module.
+ */
+extern struct interface *any_interface;
+
+/*
+ * Timer queue and current time. Imported from the timer module.
+ */
+extern u_long current_time;
+extern struct event timerqueue[];
+
+/*
+ * Our initial allocation of peer space
+ */
+static struct peer init_peer_alloc[INIT_PEER_ALLOC];
+
+/*
+ * Initialization data. When configuring peers at initialization time,
+ * we try to get their poll update timers initialized to different values
+ * to prevent us from sending big clumps of data all at once.
+ */
+u_long init_peer_starttime;
+extern int initializing;
+extern int debug;
+
+static void getmorepeermem P((void));
+
+/*
+ * init_peer - initialize peer data structures and counters
+ *
+ * N.B. We use the random number routine in here. It had better be
+ * initialized prior to getting here.
+ */
+void
+init_peer()
+{
+ register int i;
+
+ /*
+ * Clear hash table and counters.
+ */
+ for (i = 0; i < HASH_SIZE; i++) {
+ peer_hash[i] = 0;
+ peer_hash_count[i] = 0;
+ assoc_hash[i] = 0;
+ assoc_hash_count[i] = 0;
+ }
+
+ /*
+ * Clear stat counters
+ */
+ findpeer_calls = peer_allocations = 0;
+ assocpeer_calls = peer_demobilizations = 0;
+
+ /*
+ * Initialization counter.
+ */
+ init_peer_starttime = 0;
+
+ /*
+ * Initialize peer memory.
+ */
+ peer_free = 0;
+ for (i = 0; i < INIT_PEER_ALLOC; i++) {
+ init_peer_alloc[i].next = peer_free;
+ peer_free = &init_peer_alloc[i];
+ }
+ total_peer_structs = INIT_PEER_ALLOC;
+ peer_free_count = INIT_PEER_ALLOC;
+
+ /*
+ * Initialize our first association ID
+ */
+ current_association_ID = (u_short)ranp2(16);
+ if (current_association_ID == 0)
+ current_association_ID = 1;
+}
+
+
+
+/*
+ * getmorepeermem - add more peer structures to the free list
+ */
+static void
+getmorepeermem()
+{
+ register int i;
+ register struct peer *peer;
+
+ peer = (struct peer *)emalloc(INC_PEER_ALLOC*sizeof(struct peer));
+ for (i = 0; i < INC_PEER_ALLOC; i++) {
+ peer->next = peer_free;
+ peer_free = peer;
+ peer++;
+ }
+
+ total_peer_structs += INC_PEER_ALLOC;
+ peer_free_count += INC_PEER_ALLOC;
+}
+
+
+
+/*
+ * findexistingpeer - return a pointer to a peer in the hash table
+ */
+struct peer *
+findexistingpeer(addr, start_peer)
+ struct sockaddr_in *addr;
+ struct peer *start_peer;
+{
+ register struct peer *peer;
+
+ /*
+ * start_peer is included so we can locate instances of the
+ * same peer through different interfaces in the hash table.
+ */
+ if (start_peer == 0)
+ peer = peer_hash[HASH_ADDR(addr)];
+ else
+ peer = start_peer->next;
+
+ while (peer != 0) {
+ if (NSRCADR(addr) == NSRCADR(&peer->srcadr)
+ && NSRCPORT(addr) == NSRCPORT(&peer->srcadr))
+ return peer;
+ peer = peer->next;
+ }
+
+ return (struct peer *)0;
+}
+
+
+/*
+ * findpeer - find and return a peer in the hash table.
+ */
+struct peer *
+findpeer(srcadr, dstadr, fd)
+ struct sockaddr_in *srcadr;
+ struct interface *dstadr;
+ int fd;
+{
+ register struct peer *any_inter_peer;
+ register struct peer *peer;
+ register struct peer *best = (struct peer *) 0;
+ int hash;
+
+ findpeer_calls++;
+
+ any_inter_peer = 0;
+ hash = HASH_ADDR(srcadr);
+ for (peer = peer_hash[hash]; peer != 0; peer = peer->next) {
+ if (NSRCADR(srcadr) == NSRCADR(&peer->srcadr)
+ && NSRCPORT(srcadr) == NSRCPORT(&peer->srcadr)) {
+ if (peer->dstadr == dstadr) {
+ int rfd = (peer->cast_flags & MDF_BCAST) ?
+ dstadr->bfd : dstadr->fd;
+
+ if (rfd == fd)
+ return peer; /* got it! */
+ best = peer;
+ }
+ if (peer->dstadr == any_interface) {
+
+ /*
+ * We shouldn't have more than one
+ * instance of the peer in the table,
+ * but I don't trust this. Save this
+ * one for later and continue search.
+ */
+ if (any_inter_peer == 0)
+ any_inter_peer = peer;
+ else
+ syslog(LOG_ERR,
+ "two instances of default interface for %s in hash table",
+ ntoa(srcadr));
+ }
+
+ /*
+ * Multicast hacks to determine peer when a
+ * packet arrives and there exists an assoc.
+ * with src in client/server mode
+ */
+ if (((dstadr == any_interface) || (peer->cast_flags &
+ MDF_MCAST)) && peer->flags & FLAG_MCAST2)
+ return peer;
+ }
+ }
+
+ if(best) {
+ return best;
+ }
+
+ /*
+ * If we didn't find the specific peer but found a wild card,
+ * modify the interface and return him.
+ */
+ if (any_inter_peer != 0) {
+ any_inter_peer->dstadr = dstadr;
+ return any_inter_peer;
+ }
+
+ /*
+ * Out of luck. Return 0.
+ */
+ return (struct peer *)0;
+}
+
+/*
+ * findpeerbyassocid - find and return a peer using his association ID
+ */
+struct peer *
+findpeerbyassoc(assoc)
+ int assoc;
+{
+ register struct peer *peer;
+ int hash;
+
+ assocpeer_calls++;
+
+ hash = assoc & HASH_MASK;
+ for (peer = assoc_hash[hash]; peer != 0; peer = peer->ass_next) {
+ if ((u_short)assoc == peer->associd)
+ return peer; /* got it! */
+ }
+
+ /*
+ * Out of luck. Return 0.
+ */
+ return (struct peer *)0;
+}
+
+/*
+ * unpeer - remove peer structure from hash table and free structure
+ */
+void
+unpeer(peer_to_remove)
+ struct peer *peer_to_remove;
+{
+ int hash;
+
+ hash = HASH_ADDR(&peer_to_remove->srcadr);
+ peer_hash_count[hash]--;
+ peer_demobilizations++;
+
+#ifdef REFCLOCK
+ /*
+ * If this peer is actually a clock, shut it down first
+ */
+ if (peer_to_remove->flags & FLAG_REFCLOCK)
+ refclock_unpeer(peer_to_remove);
+#endif
+
+ if (peer_hash[hash] == peer_to_remove)
+ peer_hash[hash] = peer_to_remove->next;
+ else {
+ register struct peer *peer;
+
+ peer = peer_hash[hash];
+ while (peer != 0 && peer->next != peer_to_remove)
+ peer = peer->next;
+
+ if (peer == 0) {
+ peer_hash_count[hash]++;
+ syslog(LOG_ERR, "peer struct for %s not in table!",
+ ntoa(&peer->srcadr));
+ } else {
+ peer->next = peer_to_remove->next;
+ }
+ }
+
+ /*
+ * Remove him from the association hash as well.
+ */
+ hash = peer_to_remove->associd & HASH_MASK;
+ assoc_hash_count[hash]--;
+ if (assoc_hash[hash] == peer_to_remove)
+ assoc_hash[hash] = peer_to_remove->ass_next;
+ else {
+ register struct peer *peer;
+
+ peer = assoc_hash[hash];
+ while (peer != 0 && peer->ass_next != peer_to_remove)
+ peer = peer->ass_next;
+
+ if (peer == 0) {
+ assoc_hash_count[hash]++;
+ syslog(LOG_ERR,
+ "peer struct for %s not in association table!",
+ ntoa(&peer->srcadr));
+ } else {
+ peer->ass_next = peer_to_remove->ass_next;
+ }
+ }
+
+ TIMER_DEQUEUE(&peer_to_remove->event_timer);
+
+ peer_to_remove->next = peer_free;
+ peer_free = peer_to_remove;
+ peer_free_count++;
+}
+
+
+/*
+ * peer_config - configure a new peer
+ */
+struct peer *
+peer_config(srcadr, dstadr, hmode, version, minpoll, maxpoll, flags, ttl, key)
+ struct sockaddr_in *srcadr;
+ struct interface *dstadr;
+ int hmode;
+ int version;
+ int minpoll;
+ int maxpoll;
+ int flags;
+ int ttl;
+ u_long key;
+{
+ register struct peer *peer;
+
+#ifdef DEBUG
+ if (debug)
+ printf("peer_config: addr %s mode %d version %d minpoll %d maxpoll %d flags %d ttl %d key %lu\n",
+ ntoa(srcadr), hmode, version, minpoll, maxpoll, flags,
+ ttl, key);
+#endif
+ /*
+ * See if we have this guy in the tables already. If
+ * so just mark him configured.
+ */
+ peer = findexistingpeer(srcadr, (struct peer *)0);
+ if (dstadr != 0) {
+ while (peer != 0) {
+ if (peer->dstadr == dstadr)
+ break;
+ peer = findexistingpeer(srcadr, peer);
+ }
+ }
+
+ /*
+ * Torque the flags to make sure they're valid
+ */
+ flags &= (FLAG_AUTHENABLE|FLAG_PREFER);
+
+ /*
+ * If we found one, just change his mode and mark him configured.
+ */
+ if (peer != 0) {
+ peer->hmode = (u_char)hmode;
+ peer->version = (u_char)version;
+ peer->minpoll = (u_char)minpoll;
+ peer->maxpoll = (u_char)maxpoll;
+ peer->hpoll = peer->minpoll;
+ peer->ppoll = peer->minpoll;
+ peer->flags = ((u_char)(flags | FLAG_CONFIG)) |
+ (peer->flags & FLAG_REFCLOCK);
+ peer->cast_flags = (hmode == MODE_BROADCAST) ?
+ IN_CLASSD(ntohl(srcadr->sin_addr.s_addr)) ? MDF_MCAST : MDF_BCAST : MDF_UCAST;
+ peer->ttl = (u_char)ttl;
+ peer->keyid = key;
+ return peer;
+ }
+
+ /*
+ * If we're here this guy is unknown to us. Make a new peer
+ * structure for him.
+ */
+ peer = newpeer(srcadr, dstadr, hmode, version, minpoll, maxpoll,
+ ttl, key);
+ if (peer != 0)
+ peer->flags |= (u_char)(flags|FLAG_CONFIG);
+ return peer;
+}
+
+
+/*
+ * newpeer - initialize a new peer association
+ */
+struct peer *
+newpeer(srcadr, dstadr, hmode, version, minpoll, maxpoll, ttl, key)
+ struct sockaddr_in *srcadr;
+ struct interface *dstadr;
+ int hmode;
+ int version;
+ int minpoll;
+ int maxpoll;
+ int ttl;
+ u_long key;
+{
+ register struct peer *peer;
+ register int i;
+
+ /*
+ * Some dirt here. Some of the initialization requires
+ * knowlege of our system state.
+ */
+ extern s_fp sys_bdelay;
+ extern long sys_clock;
+
+ if (peer_free_count == 0)
+ getmorepeermem();
+
+ peer = peer_free;
+ peer_free = peer->next;
+ peer_free_count--;
+
+ /*
+ * Initialize the structure. This stuff is sort of part of
+ * the receive procedure and part of the clear procedure rolled
+ * into one.
+ *
+ * Zero the whole thing for now. We might be pickier later.
+ */
+ memset((char *)peer, 0, sizeof(struct peer));
+
+ peer->srcadr = *srcadr;
+ if (dstadr != 0)
+ peer->dstadr = dstadr;
+ else if (hmode == MODE_BROADCAST)
+ peer->dstadr = findbcastinter(srcadr);
+ else
+ peer->dstadr = any_interface;
+ peer->cast_flags = (hmode == MODE_BROADCAST) ?
+ (IN_CLASSD(ntohl(srcadr->sin_addr.s_addr))) ? MDF_MCAST : MDF_BCAST :
+ (hmode == MODE_BCLIENT || hmode == MODE_MCLIENT) ?
+ (peer->dstadr->flags & INT_MULTICAST) ? MDF_MCAST : MDF_BCAST :
+ MDF_UCAST;
+ peer->hmode = (u_char)hmode;
+ peer->version = (u_char)version;
+ peer->minpoll = (u_char)minpoll;
+ peer->maxpoll = (u_char)maxpoll;
+ peer->hpoll = peer->minpoll;
+ peer->ppoll = peer->minpoll;
+ peer->ttl = ttl;
+ peer->keyid = key;
+ peer->estbdelay = sys_bdelay;
+ peer->leap = LEAP_NOTINSYNC;
+ peer->precision = DEFPRECISION;
+ peer->dispersion = NTP_MAXDISPERSE;
+ peer->stratum = STRATUM_UNSPEC;
+ peer->update = sys_clock;
+
+ for (i = 0; i < NTP_SHIFT; i++) {
+ peer->filter_order[i] = i;
+ peer->filter_error[i] = NTP_MAXDISPERSE;
+ }
+
+ /*
+ * Assign him an association ID and increment the system variable
+ */
+ peer->associd = current_association_ID;
+ if (++current_association_ID == 0)
+ ++current_association_ID;
+
+ /*
+ * Note time on statistics timers.
+ */
+ peer->timereset = current_time;
+ peer->timereachable = current_time;
+ peer->timereceived = current_time;
+
+#ifdef REFCLOCK
+ if (ISREFCLOCKADR(&peer->srcadr)) {
+ /*
+ * We let the reference clock support do clock
+ * dependent initialization. This includes setting
+ * the peer timer, since the clock may have requirements
+ * for this.
+ */
+ if (!refclock_newpeer(peer)) {
+ /*
+ * Dump it, something screwed up
+ */
+ peer->next = peer_free;
+ peer_free = peer;
+ peer_free_count++;
+ return 0;
+ }
+ } else {
+#endif
+ /*
+ * Set up timer. If initializing, just make sure we start polling
+ * in different 4 second intervals.
+ */
+ peer->event_timer.peer = peer;
+ peer->event_timer.event_handler = transmit;
+
+ if (initializing) {
+ init_peer_starttime += (1 << EVENT_TIMEOUT);
+ if (init_peer_starttime >= (1 << peer->minpoll))
+ init_peer_starttime = (1 << EVENT_TIMEOUT);
+ peer->event_timer.event_time = init_peer_starttime;
+ } else {
+ /*
+ * First expiry is set to eight seconds from now.
+ */
+ peer->event_timer.event_time
+ = (1 << (peer->minpoll - 1)) + current_time;
+ }
+ TIMER_ENQUEUE(timerqueue, &peer->event_timer);
+#ifdef REFCLOCK
+ }
+#endif
+
+ /*
+ * Put him in the hash tables.
+ */
+ i = HASH_ADDR(&peer->srcadr);
+ peer->next = peer_hash[i];
+ peer_hash[i] = peer;
+ peer_hash_count[i]++;
+
+ i = peer->associd & HASH_MASK;
+ peer->ass_next = assoc_hash[i];
+ assoc_hash[i] = peer;
+ assoc_hash_count[i]++;
+
+ return peer;
+}
+
+
+/*
+ * peer_unconfig - remove the configuration bit from a peer
+ */
+int
+peer_unconfig(srcadr, dstadr)
+ struct sockaddr_in *srcadr;
+ struct interface *dstadr;
+{
+ register struct peer *peer;
+ int num_found;
+
+ num_found = 0;
+ peer = findexistingpeer(srcadr, (struct peer *)0);
+ while (peer != 0) {
+ if (peer->flags & FLAG_CONFIG
+ && (dstadr == 0 || peer->dstadr == dstadr)) {
+ num_found++;
+ /*
+ * Tricky stuff here. If the peer is polling us
+ * in active mode, turn off the configuration bit
+ * and make the mode passive. This allows us to
+ * avoid dumping a lot of history for peers we
+ * might choose to keep track of in passive mode.
+ * The protocol will eventually terminate undesirables
+ * on its own.
+ */
+ if (peer->hmode == MODE_ACTIVE
+ && peer->pmode == MODE_ACTIVE) {
+ peer->hmode = MODE_PASSIVE;
+ peer->flags &= ~FLAG_CONFIG;
+ } else {
+ unpeer(peer);
+ peer = 0;
+ }
+ }
+ peer = findexistingpeer(srcadr, peer);
+ }
+ return num_found;
+}
+
+
+/*
+ * peer_clr_stats - clear peer module stat counters
+ */
+void
+peer_clr_stats()
+{
+ findpeer_calls = 0;
+ assocpeer_calls = 0;
+ peer_allocations = 0;
+ peer_demobilizations = 0;
+ peer_timereset = current_time;
+}
+
+/*
+ * peer_reset - reset stat counters in a peer structure
+ */
+void
+peer_reset(peer)
+ struct peer *peer;
+{
+ if (peer == 0)
+ return;
+ peer->sent = 0;
+ peer->received = 0;
+ peer->processed = 0;
+ peer->badauth = 0;
+ peer->bogusorg = 0;
+ peer->oldpkt = 0;
+ peer->seldisptoolarge = 0;
+ peer->selbroken = 0;
+ peer->seltooold = 0;
+ peer->timereset = current_time;
+}
+
+
+/*
+ * peer_all_reset - reset all peer stat counters
+ */
+void
+peer_all_reset()
+{
+ struct peer *peer;
+ int hash;
+
+ for (hash = 0; hash < HASH_SIZE; hash++)
+ for (peer = peer_hash[hash]; peer != 0; peer = peer->next)
+ peer_reset(peer);
+}
diff --git a/usr.sbin/xntpd/xntpd/ntp_proto.c b/usr.sbin/xntpd/xntpd/ntp_proto.c
new file mode 100644
index 0000000..fb81206
--- /dev/null
+++ b/usr.sbin/xntpd/xntpd/ntp_proto.c
@@ -0,0 +1,2268 @@
+/*
+ * ntp_proto.c - NTP version 3 protocol machinery
+ */
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/time.h>
+
+#include "ntpd.h"
+#include "ntp_stdlib.h"
+#include "ntp_unixtime.h"
+
+/*
+ * System variables are declared here. See Section 3.2 of the
+ * specification.
+ */
+u_char sys_leap; /* system leap indicator */
+u_char sys_stratum; /* stratum of system */
+s_char sys_precision; /* local clock precision */
+s_fp sys_rootdelay; /* distance to current sync source */
+u_fp sys_rootdispersion; /* dispersion of system clock */
+u_long sys_refid; /* reference source for local clock */
+l_fp sys_offset; /* combined offset from clock_select */
+u_fp sys_maxd; /* dispersion of selected peer */
+l_fp sys_reftime; /* time we were last updated */
+l_fp sys_refskew; /* accumulated skew since last update */
+struct peer *sys_peer; /* our current peer */
+u_char sys_poll; /* log2 of system poll interval */
+extern long sys_clock; /* second part of current time */
+long sys_lastselect; /* sys_clock at last synch update */
+
+/*
+ * Nonspecified system state variables.
+ */
+int sys_bclient; /* we set our time to broadcasts */
+s_fp sys_bdelay; /* broadcast client default delay */
+int sys_authenticate; /* authenticate time used for syncing */
+u_char consensus_leap; /* mitigated leap bits */
+u_long sys_authdelay; /* encryption time (l_fp fraction) */
+u_char leap_consensus; /* consensus of survivor leap bits */
+
+/*
+ * Statistics counters
+ */
+u_long sys_stattime; /* time when we started recording */
+u_long sys_badstratum; /* packets with invalid stratum */
+u_long sys_oldversionpkt; /* old version packets received */
+u_long sys_newversionpkt; /* new version packets received */
+u_long sys_unknownversion; /* don't know version packets */
+u_long sys_badlength; /* packets with bad length */
+u_long sys_processed; /* packets processed */
+u_long sys_badauth; /* packets dropped because of auth */
+u_long sys_limitrejected; /* pkts rejected due toclient count per net */
+
+/*
+ * Imported from ntp_timer.c
+ */
+extern u_long current_time;
+extern struct event timerqueue[];
+
+/*
+ * Imported from ntp_io.c
+ */
+extern struct interface *any_interface;
+
+/*
+ * Imported from ntp_loopfilter.c
+ */
+extern int pll_enable;
+extern int pps_update;
+
+/*
+ * Imported from ntp_util.c
+ */
+extern int stats_control;
+
+/*
+ * The peer hash table. Imported from ntp_peer.c
+ */
+extern struct peer *peer_hash[];
+extern int peer_hash_count[];
+
+/*
+ * debug flag
+ */
+extern int debug;
+
+static void clear_all P((void));
+
+/*
+ * transmit - Transmit Procedure. See Section 3.4.1 of the
+ * specification.
+ */
+void
+transmit(peer)
+ register struct peer *peer;
+{
+ struct pkt xpkt; /* packet to send */
+ u_long peer_timer;
+ u_fp precision;
+ int bool;
+
+ /*
+ * We need to be very careful about honking uncivilized time. If
+ * not operating in broadcast mode, honk in all except broadcast
+ * client mode. If operating in broadcast mode and synchronized
+ * to a real source, honk except when the peer is the local-
+ * clock driver and the prefer flag is not set. In other words,
+ * in broadcast mode we never honk unless known to be
+ * synchronized to real time.
+ */
+ bool = 0;
+ if (peer->hmode != MODE_BROADCAST) {
+ if (peer->hmode != MODE_BCLIENT)
+ bool = 1;
+ } else if (sys_peer != 0 && sys_leap != LEAP_NOTINSYNC) {
+ if (!(sys_peer->refclktype == REFCLK_LOCALCLOCK &&
+ !(sys_peer->flags & FLAG_PREFER)))
+ bool = 1;
+ }
+ if (bool) {
+ u_long xkeyid;
+ int find_rtt = (peer->cast_flags & MDF_MCAST) &&
+ peer->hmode != MODE_BROADCAST;
+
+ /*
+ * Figure out which keyid to include in the packet
+ */
+ if ((peer->flags & FLAG_AUTHENABLE)
+ && (peer->flags & (FLAG_CONFIG|FLAG_AUTHENTIC))
+ && authhavekey(peer->keyid)) {
+ xkeyid = peer->keyid;
+ } else {
+ xkeyid = 0;
+ }
+
+ /*
+ * Make up a packet to send.
+ */
+ xpkt.li_vn_mode = PKT_LI_VN_MODE(sys_leap,
+ peer->version, peer->hmode);
+ xpkt.stratum = STRATUM_TO_PKT(sys_stratum);
+ xpkt.ppoll = peer->hpoll;
+ xpkt.precision = sys_precision;
+ xpkt.rootdelay = HTONS_FP(sys_rootdelay);
+ precision = FP_SECOND >> -(int)sys_precision;
+ if (precision == 0)
+ precision = 1;
+ xpkt.rootdispersion = HTONS_FP(sys_rootdispersion +
+ precision + LFPTOFP(&sys_refskew));
+ xpkt.refid = sys_refid;
+ HTONL_FP(&sys_reftime, &xpkt.reftime);
+ HTONL_FP(&peer->org, &xpkt.org);
+ HTONL_FP(&peer->rec, &xpkt.rec);
+
+ /*
+ * Decide whether to authenticate or not. If so, call
+ * encrypt() to fill in the rest of the frame. If not,
+ * just add in the xmt timestamp and send it quick.
+ */
+ if (peer->flags & FLAG_AUTHENABLE) {
+ int sendlen;
+
+ xpkt.keyid = htonl(xkeyid);
+ auth1crypt(xkeyid, (U_LONG *)&xpkt,
+ LEN_PKT_NOMAC);
+ get_systime(&peer->xmt);
+ L_ADDUF(&peer->xmt, sys_authdelay);
+ HTONL_FP(&peer->xmt, &xpkt.xmt);
+ sendlen = auth2crypt(xkeyid, (U_LONG *)&xpkt,
+ LEN_PKT_NOMAC);
+ sendpkt(&peer->srcadr, find_rtt ?
+ any_interface : peer->dstadr,
+ ((peer->cast_flags & MDF_MCAST) && !find_rtt) ?
+ peer->ttl : -7, &xpkt, sendlen +
+ LEN_PKT_NOMAC);
+#ifdef DEBUG
+ if (debug > 1)
+ printf("transmit auth to %s\n",
+ ntoa(&(peer->srcadr)));
+#endif
+ peer->sent++;
+ } else {
+ /*
+ * Get xmt timestamp, then send it without mac
+ * field
+ */
+ int find_rtt = (peer->cast_flags & MDF_MCAST) &&
+ peer->dstadr != any_interface;
+ get_systime(&(peer->xmt));
+ HTONL_FP(&peer->xmt, &xpkt.xmt);
+ sendpkt(&(peer->srcadr), find_rtt ?
+ any_interface : peer->dstadr,
+ ((peer->cast_flags & MDF_MCAST) && !find_rtt) ?
+ peer->ttl : -8, &xpkt, LEN_PKT_NOMAC);
+#ifdef DEBUG
+ if (debug > 1)
+ printf("transmit to %s\n",
+ ntoa(&(peer->srcadr)));
+#endif
+ peer->sent++;
+ }
+ }
+
+ if (peer->hmode != MODE_BROADCAST) {
+ u_char opeer_reach;
+ /*
+ * Determine reachability and diddle things if we
+ * haven't heard from the host for a while. If we are
+ * about to become unreachable and are a
+ * broadcast/multicast client, the server has refused to
+ * boogie in client/server mode, so we switch to
+ * MODE_BCLIENT anyway and wait for subsequent
+ * broadcasts.
+ */
+ opeer_reach = peer->reach;
+ if (opeer_reach & 0x80 && peer->flags & FLAG_MCAST2) {
+ peer->hmode = MODE_BCLIENT;
+ }
+ peer->reach <<= 1;
+ if (peer->reach == 0) {
+ if (opeer_reach != 0)
+ report_event(EVNT_UNREACH, peer);
+ /*
+ * Clear this guy out. No need to redo clock
+ * selection since by now this guy won't be a
+ * player
+ */
+ if (peer->flags & FLAG_CONFIG) {
+ if (opeer_reach != 0) {
+ peer_clear(peer);
+ peer->timereachable =
+ current_time;
+ }
+ }
+
+ /*
+ * While we have a chance, if our system peer is
+ * zero or his stratum is greater than the last
+ * known stratum of this guy, make sure hpoll is
+ * clamped to the minimum before resetting the
+ * timer. If the peer has been unreachable for a
+ * while and we have a system peer who is at
+ * least his equal, we may want to ramp his
+ * polling interval up to avoid the useless
+ * traffic.
+ */
+ if (sys_peer == 0) {
+ peer->hpoll = peer->minpoll;
+ peer->unreach = 0;
+ } else if (sys_peer->stratum > peer->stratum) {
+ peer->hpoll = peer->minpoll;
+ peer->unreach = 0;
+ } else {
+ if (peer->unreach < 16) {
+ peer->unreach++;
+ peer->hpoll = peer->minpoll;
+ } else if (peer->hpoll < peer->maxpoll) {
+ peer->hpoll++;
+ peer->ppoll = peer->hpoll;
+ }
+ }
+
+ /*
+ * Update reachability and poll variables
+ */
+ } else if ((opeer_reach & 3) == 0) {
+
+ l_fp off;
+
+ if (peer->valid > 0)
+ peer->valid--;
+ if (peer->hpoll > peer->minpoll)
+ peer->hpoll--;
+ L_CLR(&off);
+ clock_filter(peer, &off, (s_fp)0,
+ (u_fp)NTP_MAXDISPERSE);
+ if (peer->flags & FLAG_SYSPEER)
+ clock_select();
+ } else {
+ if (peer->valid < NTP_SHIFT) {
+ peer->valid++;
+ } else {
+ if (peer->hpoll < peer->maxpoll)
+ peer->hpoll++;
+ }
+ }
+ }
+
+ /*
+ * Finally, adjust the hpoll variable for special conditions. If
+ * we are a broadcast/multicast client, we use the server poll
+ * interval if listening for broadcasts and one-eighth this
+ * interval if in client/server mode. The following clamp
+ * prevents madness. If this is the system poll, sys_poll
+ * controls hpoll.
+ */
+ if (peer->flags & FLAG_MCAST2) {
+ if (peer->hmode == MODE_BCLIENT)
+ peer->hpoll = peer->ppoll;
+ else
+ peer->hpoll = peer->ppoll - 3;
+ } else if (peer->flags & FLAG_SYSPEER)
+ peer->hpoll = sys_poll;
+ if (peer->hpoll < peer->minpoll)
+ peer->hpoll = peer->minpoll;
+
+ /*
+ * Arrange for our next timeout. hpoll will be less than maxpoll
+ * for sure.
+ */
+ if (peer->event_timer.next != 0)
+ /*
+ * Oops, someone did already.
+ */
+ TIMER_DEQUEUE(&peer->event_timer);
+ peer_timer = 1 << (int)max((u_char)min(peer->ppoll,
+ peer->hpoll), peer->minpoll);
+ peer->event_timer.event_time = current_time + peer_timer;
+ TIMER_ENQUEUE(timerqueue, &peer->event_timer);
+}
+
+/*
+ * receive - Receive Procedure. See section 3.4.2 in the specification.
+ */
+void
+receive(rbufp)
+ struct recvbuf *rbufp;
+{
+ register struct peer *peer;
+ register struct pkt *pkt;
+ register u_char hismode;
+ int restrict;
+ int has_mac;
+ int trustable;
+ int is_authentic;
+ u_long hiskeyid;
+ struct peer *peer2;
+
+#ifdef DEBUG
+ if (debug > 1)
+ printf("receive from %s\n", ntoa(&rbufp->recv_srcadr));
+#endif
+
+ /*
+ * Let the monitoring software take a look at this first.
+ */
+ monitor(rbufp);
+
+ /*
+ * Get the restrictions on this guy. If we're to ignore him,
+ * go no further.
+ */
+ restrict = restrictions(&rbufp->recv_srcadr);
+ if (restrict & RES_IGNORE)
+ return;
+
+ /*
+ * Get a pointer to the packet.
+ */
+ pkt = &rbufp->recv_pkt;
+
+ /*
+ * Catch packets whose version number we can't deal with
+ */
+ if (PKT_VERSION(pkt->li_vn_mode) >= NTP_VERSION) {
+ sys_newversionpkt++;
+ } else if (PKT_VERSION(pkt->li_vn_mode) >= NTP_OLDVERSION) {
+ sys_oldversionpkt++;
+ } else {
+ sys_unknownversion++;
+ return;
+ }
+
+ /*
+ * Catch private mode packets. Dump it if queries not allowed.
+ */
+ if (PKT_MODE(pkt->li_vn_mode) == MODE_PRIVATE) {
+ if (restrict & RES_NOQUERY)
+ return;
+ process_private(rbufp, ((restrict&RES_NOMODIFY) == 0));
+ return;
+ }
+
+ /*
+ * Same with control mode packets.
+ */
+ if (PKT_MODE(pkt->li_vn_mode) == MODE_CONTROL) {
+ if (restrict & RES_NOQUERY)
+ return;
+ process_control(rbufp, restrict);
+ return;
+ }
+
+ /*
+ * See if we're allowed to serve this guy time. If not, ignore
+ * him.
+ */
+ if (restrict & RES_DONTSERVE)
+ return;
+
+ /*
+ * See if we only accept limited number of clients from the net
+ * this guy is from. Note: the flag is determined dynamically
+ * within restrictions()
+ */
+ if (restrict & RES_LIMITED) {
+ extern u_long client_limit;
+
+ sys_limitrejected++;
+ syslog(LOG_NOTICE,
+ "rejected mode %d request from %s - per net client limit (%d) exceeded",
+ PKT_MODE(pkt->li_vn_mode),
+ ntoa(&rbufp->recv_srcadr), client_limit);
+ return;
+ }
+ /*
+ * Dump anything with a putrid stratum. These will most likely
+ * come from someone trying to poll us with ntpdc.
+ */
+ if (pkt->stratum > NTP_MAXSTRATUM) {
+ sys_badstratum++;
+ return;
+ }
+
+ /*
+ * Find the peer. This will return a null if this guy isn't in
+ * the database.
+ */
+ peer = findpeer(&rbufp->recv_srcadr, rbufp->dstadr, rbufp->fd);
+
+ /*
+ * Check the length for validity, drop the packet if it is
+ * not as expected. If this is a client mode poll, go no
+ * further. Send back his time and drop it.
+ *
+ * The scheme we use for authentication is this. If we are
+ * running in non-authenticated mode, we accept both frames
+ * which are authenticated and frames which aren't, but don't
+ * authenticate. We do record whether the frame had a mac field
+ * or not so we know what to do on output.
+ *
+ * If we are running in authenticated mode, we only trust frames
+ * which have authentication attached, which are validated and
+ * which are using one of our trusted keys. We respond to all
+ * other pollers without saving any state. If a host we are
+ * passively peering with changes his key from a trusted one to
+ * an untrusted one, we immediately unpeer with him, reselect
+ * the clock and treat him as an unmemorable client (this is
+ * a small denial-of-service hole I'll have to think about).
+ * If a similar event occurs with a configured peer we drop the
+ * frame and hope he'll revert to our key again. If we get a
+ * frame which can't be authenticated with the given key, we
+ * drop it. Either we disagree on the keys or someone is trying
+ * some funny stuff.
+ */
+
+ /*
+ * here we assume that any packet with an authenticator is at
+ * least LEN_PKT_MAC bytes long, which means at least 96 bits
+ */
+ if (rbufp->recv_length >= LEN_PKT_MAC) {
+ has_mac = rbufp->recv_length - LEN_PKT_NOMAC;
+ hiskeyid = ntohl(pkt->keyid);
+#ifdef DEBUG
+ if (debug > 2)
+ printf(
+ "receive: pkt is %d octets, mac %d octets long, keyid %ld\n",
+ rbufp->recv_length, has_mac, hiskeyid);
+#endif
+ } else if (rbufp->recv_length == LEN_PKT_NOMAC) {
+ hiskeyid = 0;
+ has_mac = 0;
+ } else {
+#ifdef DEBUG
+ if (debug > 2)
+ printf("receive: bad length %d %ld\n",
+ rbufp->recv_length, sizeof(struct pkt));
+#endif
+ sys_badlength++;
+ return;
+ }
+
+
+ /*
+ * Figure out his mode and validate it.
+ */
+ hismode = PKT_MODE(pkt->li_vn_mode);
+#ifdef DEBUG
+ if (debug > 2)
+ printf("receive: his mode %d\n", hismode);
+#endif
+ if (PKT_VERSION(pkt->li_vn_mode) == NTP_OLDVERSION && hismode ==
+ 0) {
+ /*
+ * Easy. If it is from the NTP port it is
+ * a sym act, else client.
+ */
+ if (SRCPORT(&rbufp->recv_srcadr) == NTP_PORT)
+ hismode = MODE_ACTIVE;
+ else
+ hismode = MODE_CLIENT;
+ } else {
+ if (hismode != MODE_ACTIVE && hismode != MODE_PASSIVE &&
+ hismode != MODE_SERVER && hismode != MODE_CLIENT &&
+ hismode != MODE_BROADCAST) {
+ syslog(LOG_ERR, "bad mode %d received from %s",
+ PKT_MODE(pkt->li_vn_mode),
+ ntoa(&rbufp->recv_srcadr));
+ return;
+ }
+ }
+
+ /*
+ * If he included a mac field, decrypt it to see if it is
+ * authentic.
+ */
+ is_authentic = 0;
+ if (has_mac) {
+ if (authhavekey(hiskeyid)) {
+ if (!authistrusted(hiskeyid)) {
+ sys_badauth++;
+#ifdef DEBUG
+ if (debug > 3)
+ printf("receive: untrusted keyid\n");
+#endif
+ return;
+ }
+ if (authdecrypt(hiskeyid, (U_LONG *)pkt,
+ LEN_PKT_NOMAC)) {
+ is_authentic = 1;
+#ifdef DEBUG
+ if (debug > 3)
+ printf("receive: authdecrypt succeeds\n");
+#endif
+ } else {
+ sys_badauth++;
+#ifdef DEBUG
+ if (debug > 3)
+ printf("receive: authdecrypt fails\n");
+#endif
+ }
+ }
+ }
+
+ /*
+ * If this is someone we don't remember from a previous
+ * association, dispatch him now. Either we send something back
+ * quick, we ignore him, or we allocate some memory for him and
+ * let him continue.
+ */
+ if (peer == 0) {
+ int mymode;
+
+ mymode = MODE_PASSIVE;
+ switch(hismode) {
+ case MODE_ACTIVE:
+ /*
+ * See if this guy qualifies as being the least
+ * bit memorable. If so we keep him around for
+ * later. If not, send his time quick.
+ */
+ if (restrict & RES_NOPEER) {
+ fast_xmit(rbufp, (int)hismode,
+ is_authentic);
+ return;
+ }
+ break;
+
+ case MODE_PASSIVE:
+ case MODE_SERVER:
+ /*
+ * These are obvious errors. Ignore.
+ */
+ return;
+
+ case MODE_CLIENT:
+ /*
+ * Send it back quick and go home.
+ */
+ fast_xmit(rbufp, (int)hismode, is_authentic);
+ return;
+
+ case MODE_BROADCAST:
+ /*
+ * Sort of a repeat of the above...
+ */
+ if ((restrict & RES_NOPEER) || !sys_bclient)
+ return;
+ mymode = MODE_MCLIENT;
+ break;
+ }
+
+ /*
+ * Okay, we're going to keep him around. Allocate him
+ * some memory.
+ */
+ peer = newpeer(&rbufp->recv_srcadr,
+ rbufp->dstadr, mymode, PKT_VERSION(pkt->li_vn_mode),
+ NTP_MINDPOLL, NTP_MAXDPOLL, 0, hiskeyid);
+
+ if (peer == 0) {
+ /*
+ * The only way this can happen is if the
+ * source address looks like a reference
+ * clock. Since this is an illegal address
+ * this is one of those "can't happen" things.
+ */
+ syslog(LOG_ERR,
+ "receive() failed to peer with %s, mode %d",
+ ntoa(&rbufp->recv_srcadr), mymode);
+ return;
+ }
+ }
+
+ /*
+ * Mark the time of reception
+ */
+ peer->timereceived = current_time;
+
+ /*
+ * If the peer isn't configured, set his keyid and authenable
+ * status based on the packet.
+ */
+ if (!(peer->flags & FLAG_CONFIG)) {
+ if (has_mac) {
+ if (!(peer->reach && peer->keyid != hiskeyid)) {
+ peer->keyid = hiskeyid;
+ peer->flags |= FLAG_AUTHENABLE;
+ }
+ } else {
+ peer->keyid = 0;
+ peer->flags &= ~FLAG_AUTHENABLE;
+ }
+ }
+
+
+ /*
+ * If this message was authenticated properly, note this
+ * in the flags.
+ */
+ if (is_authentic) {
+ peer->flags |= FLAG_AUTHENTIC;
+ } else {
+ /*
+ * If this guy is authenable, and has been authenticated
+ * in the past, but just failed the authentic test,
+ * report the event.
+ */
+ if (peer->flags & FLAG_AUTHENABLE
+ && peer->flags & FLAG_AUTHENTIC)
+ report_event(EVNT_PEERAUTH, peer);
+ peer->flags &= ~FLAG_AUTHENTIC;
+ }
+
+ /*
+ * Determine if this guy is basically trustable.
+ */
+ if (restrict & RES_DONTTRUST)
+ trustable = 0;
+ else
+ trustable = 1;
+
+ if (sys_authenticate && trustable) {
+ if (!(peer->flags & FLAG_CONFIG) ||
+ (peer->flags & FLAG_AUTHENABLE)) {
+ if (has_mac && is_authentic)
+ trustable = 1;
+ else
+ trustable = 0;
+ }
+ }
+
+ /*
+ * Dispose of the packet based on our respective modes. We
+ * don't drive this with a table, though we probably could.
+ */
+ switch (peer->hmode) {
+ case MODE_ACTIVE:
+ case MODE_CLIENT:
+ /*
+ * Active mode associations are configured. If the data
+ * isn't trustable, ignore it and hope this guy
+ * brightens up. Else accept any data we get and process
+ * it.
+ */
+ switch (hismode) {
+ case MODE_ACTIVE:
+ case MODE_PASSIVE:
+ case MODE_SERVER:
+ case MODE_BROADCAST:
+ process_packet(peer, pkt, &(rbufp->recv_time),
+ has_mac, trustable);
+ break;
+
+ case MODE_CLIENT:
+ if (peer->hmode == MODE_ACTIVE)
+ fast_xmit(rbufp, hismode, is_authentic);
+ return;
+ }
+ break;
+
+ case MODE_PASSIVE:
+ /*
+ * Passive mode associations are (in the current
+ * implementation) always dynamic. If we get an invalid
+ * header, break the connection. I hate doing this since
+ * it seems like a waste. Oh, well.
+ */
+ switch (hismode) {
+ case MODE_ACTIVE:
+ if (process_packet(peer, pkt,
+ &(rbufp->recv_time),
+ has_mac, trustable) == 0) {
+ unpeer(peer);
+ clock_select();
+ fast_xmit(rbufp, (int)hismode, is_authentic);
+ }
+ break;
+
+ case MODE_PASSIVE:
+ case MODE_SERVER:
+ case MODE_BROADCAST:
+ /*
+ * These are errors. Just ignore the packet.
+ * If he doesn't straighten himself out this
+ * association will eventually be disolved.
+ */
+ break;
+
+ case MODE_CLIENT:
+ fast_xmit(rbufp, hismode, is_authentic);
+ return;
+ }
+ break;
+
+
+ case MODE_BCLIENT:
+ /*
+ * Broadcast client pseudo-mode. We accept both server
+ * and broadcast data. Passive mode data is an error.
+ */
+ switch (hismode) {
+ case MODE_ACTIVE:
+ /*
+ * This guy wants to give us real time when
+ * we've been existing on lousy broadcasts!
+ * Create a passive mode association and do it
+ * that way, but keep the old one in case the
+ * packet turns out to be bad.
+ */
+ peer2 = newpeer(&rbufp->recv_srcadr,
+ rbufp->dstadr, MODE_PASSIVE,
+ PKT_VERSION(pkt->li_vn_mode),
+ NTP_MINDPOLL, NTP_MAXPOLL, 0, hiskeyid);
+ if (process_packet(peer2, pkt,
+ &rbufp->recv_time, has_mac, trustable) == 0) {
+ /*
+ * Strange situation. We've been
+ * receiving broadcasts from him which
+ * we liked, but we don't like his
+ * active mode stuff. Keep his old peer
+ * structure and send him some time
+ * quickly, we'll figure it out later.
+ */
+ unpeer(peer2);
+ fast_xmit(rbufp, (int)hismode,
+ is_authentic);
+ } else
+ /*
+ * Drop the old association
+ */
+ unpeer(peer);
+ break;
+
+ case MODE_PASSIVE:
+ break;
+
+ case MODE_SERVER:
+ case MODE_BROADCAST:
+ process_packet(peer, pkt, &rbufp->recv_time,
+ has_mac, trustable);
+ /*
+ * We don't test for invalid headers.
+ * Let him time out.
+ */
+ break;
+ }
+ break;
+
+ case MODE_MCLIENT:
+ /*
+ * This mode is temporary and does not appear outside
+ * this routine. It lasts only from the time the
+ * broadcast/multicast is recognized until the
+ * association is instantiated. Note that we start up in
+ * client/server mode to initially synchronize the
+ * clock.
+ */
+ switch (hismode) {
+ case MODE_BROADCAST:
+ peer->flags |= FLAG_MCAST1 | FLAG_MCAST2;
+ peer->hmode = MODE_CLIENT;
+ process_packet(peer, pkt, &rbufp->recv_time,
+ has_mac, trustable);
+ break;
+
+ case MODE_SERVER:
+ case MODE_PASSIVE:
+ case MODE_ACTIVE:
+ case MODE_CLIENT:
+ break;
+ }
+ }
+}
+
+
+/*
+ * process_packet - Packet Procedure, a la Section 3.4.3 of the
+ * specification. Or almost, at least. If we're in here we have a
+ * reasonable expectation that we will be having a long term
+ * relationship with this host.
+ */
+int
+process_packet(peer, pkt, recv_ts, has_mac, trustable)
+ register struct peer *peer;
+ register struct pkt *pkt;
+ l_fp *recv_ts;
+ int has_mac;
+ int trustable; /* used as "valid header" */
+{
+ l_fp t10, t23;
+ s_fp di, ei, p_dist, p_disp;
+ l_fp ci, p_rec, p_xmt, p_org;
+ int randomize;
+ u_char ostratum, oreach;
+ U_LONG temp;
+ u_fp precision;
+
+ sys_processed++;
+ peer->processed++;
+ p_dist = NTOHS_FP(pkt->rootdelay);
+ p_disp = NTOHS_FP(pkt->rootdispersion);
+ NTOHL_FP(&pkt->rec, &p_rec);
+ NTOHL_FP(&pkt->xmt, &p_xmt);
+ if (PKT_MODE(pkt->li_vn_mode) != MODE_BROADCAST)
+ NTOHL_FP(&pkt->org, &p_org);
+ else
+ p_org = peer->rec;
+ peer->rec = *recv_ts;
+ peer->flash = 0;
+ randomize = POLL_RANDOMCHANGE;
+
+ /*
+ * Test for old or duplicate packets (tests 1 through 3).
+ */
+ if (L_ISHIS(&peer->org, &p_xmt)) /* count old packets */
+ peer->oldpkt++;
+ if (L_ISEQU(&peer->org, &p_xmt)) /* test 1 */
+ peer->flash |= TEST1; /* duplicate packet */
+ if (PKT_MODE(pkt->li_vn_mode) != MODE_BROADCAST) {
+ if (!L_ISEQU(&peer->xmt, &p_org)) { /* test 2 */
+ randomize = POLL_MAKERANDOM;
+ peer->bogusorg++;
+ peer->flash |= TEST2; /* bogus packet */
+ }
+ if (L_ISZERO(&p_rec) || L_ISZERO(&p_org))
+ peer->flash |= TEST3; /* unsynchronized */
+ } else {
+ if (L_ISZERO(&p_org))
+ peer->flash |= TEST3; /* unsynchronized */
+ }
+ peer->org = p_xmt; /* reuse byte-swapped pkt->xmt */
+ peer->ppoll = pkt->ppoll;
+
+ /*
+ * Call poll_update(). This will either start us, if the
+ * association is new, or drop the polling interval if the
+ * association is existing and ppoll has been reduced.
+ */
+ poll_update(peer, peer->hpoll, randomize);
+
+
+ /*
+ * Test for valid header (tests 5 through 8)
+ */
+ if (trustable == 0) /* test 5 */
+ peer->flash |= TEST5; /* authentication failed */
+ temp = ntohl(pkt->reftime.l_ui);
+ if (PKT_LEAP(pkt->li_vn_mode) == LEAP_NOTINSYNC || /* test 6 */
+ p_xmt.l_ui < temp || p_xmt.l_ui >= temp + NTP_MAXAGE)
+ peer->flash |= TEST6; /* peer clock unsynchronized */
+ if (!(peer->flags & FLAG_CONFIG) && /* test 7 */
+ (PKT_TO_STRATUM(pkt->stratum) >= NTP_MAXSTRATUM ||
+ PKT_TO_STRATUM(pkt->stratum) > sys_stratum))
+ peer->flash |= TEST7; /* peer stratum out of bounds */
+ if (p_dist >= NTP_MAXDISPERSE /* test 8 */
+ || p_dist <= (-NTP_MAXDISPERSE)
+ || p_disp >= NTP_MAXDISPERSE)
+ peer->flash |= TEST8; /* delay/dispersion too big */
+
+ /*
+ * If the packet header is invalid (tests 5 through 8), exit
+ */
+ if (peer->flash & (TEST5 | TEST6 | TEST7 | TEST8)) {
+
+#ifdef DEBUG
+ if (debug > 1)
+ printf("invalid packet header %s %02x\n",
+ ntoa(&peer->srcadr), peer->flash);
+#endif
+
+ return(0);
+ }
+
+ /*
+ * Valid header; update our state.
+ */
+ peer->leap = PKT_LEAP(pkt->li_vn_mode);
+ peer->pmode = PKT_MODE(pkt->li_vn_mode);
+ if (has_mac)
+ peer->pkeyid = ntohl(pkt->keyid);
+ else
+ peer->pkeyid = 0;
+ ostratum = peer->stratum;
+ peer->stratum = PKT_TO_STRATUM(pkt->stratum);
+ peer->precision = pkt->precision;
+ peer->rootdelay = p_dist;
+ peer->rootdispersion = p_disp;
+ peer->refid = pkt->refid;
+ NTOHL_FP(&pkt->reftime, &peer->reftime);
+ oreach = peer->reach;
+ if (peer->reach == 0) {
+ peer->timereachable = current_time;
+ /*
+ * If this guy was previously unreachable, set his
+ * polling interval to the minimum and reset the
+ * unreach counter.
+ */
+ peer->unreach = 0;
+ peer->hpoll = peer->minpoll;
+ }
+ peer->reach |= 1;
+
+ /*
+ * If running in a client/server association, calculate the
+ * clock offset c, roundtrip delay d and dispersion e. We use
+ * the equations (reordered from those in the spec). Note that,
+ * in a broadcast association, org has been set to the time of
+ * last reception. Note the computation of dispersion includes
+ * the system precision plus that due to the frequency error
+ * since the originate time.
+ *
+ * c = ((t2 - t3) + (t1 - t0)) / 2
+ * d = (t2 - t3) - (t1 - t0)
+ * e = (org - rec) (seconds only)
+ */
+ t10 = p_xmt; /* compute t1 - t0 */
+ L_SUB(&t10, &peer->rec);
+ t23 = p_rec; /* compute t2 - t3 */
+ L_SUB(&t23, &p_org);
+ ci = t10;
+ precision = FP_SECOND >> -(int)sys_precision;
+ if (precision == 0)
+ precision = 1;
+ ei = precision + peer->rec.l_ui - p_org.l_ui;
+
+ /*
+ * If running in a broacast association, the clock offset is (t1
+ * - t0) corrected by the one-way delay, but we can't measure
+ * that directly; therefore, we start up in client/server mode,
+ * calculate the clock offset, using the engineered refinement
+ * algorithms, while also receiving broadcasts. When a broadcast
+ * is received in client/server mode, we calculate a correction
+ * factor to use after switching back to broadcast mode. We know
+ * NTP_SKEWFACTOR == 16, which accounts for the simplified ei
+ * calculation.
+ *
+ * If FLAG_MCAST2 is set, we are a broadcast/multicast client.
+ * If FLAG_MCAST1 is set, we haven't calculated the propagation
+ * delay. If hmode is MODE_CLIENT, we haven't set the local
+ * clock in client/server mode. Initially, we come up
+ * MODE_CLIENT. When the clock is first updated and FLAG_MCAST2
+ * is set, we switch from MODE_CLIENT to MODE_BCLIENT.
+ */
+ if (peer->pmode == MODE_BROADCAST) {
+ if (peer->flags & FLAG_MCAST1) {
+ if (peer->hmode == MODE_BCLIENT)
+ peer->flags &= ~FLAG_MCAST1;
+ L_SUB(&ci, &peer->offset);
+ L_NEG(&ci);
+ peer->estbdelay = LFPTOFP(&ci);
+ return (1);
+
+ }
+ FPTOLFP(peer->estbdelay, &t10);
+ L_ADD(&ci, &t10);
+ di = peer->delay;
+
+ } else {
+ L_ADD(&ci, &t23);
+ L_RSHIFT(&ci);
+ L_SUB(&t23, &t10);
+ di = LFPTOFP(&t23);
+ }
+
+#ifdef DEBUG
+ if (debug > 3)
+ printf("offset: %s, delay %s, error %s\n",
+ lfptoa(&ci, 6), fptoa(di, 5), fptoa(ei, 5));
+#endif
+ if (di >= NTP_MAXDISPERSE || di <= (-NTP_MAXDISPERSE)
+ || ei >= NTP_MAXDISPERSE) /* test 4 */
+ peer->flash |= TEST4; /* delay/dispersion too big */
+
+ /*
+ * If the packet data is invalid (tests 1 through 4), exit.
+ */
+ if (peer->flash) {
+
+#ifdef DEBUG
+ if (debug)
+ printf("invalid packet data %s %02x\n",
+ ntoa(&peer->srcadr), peer->flash);
+#endif
+
+ /*
+ * If there was a reachability change report it even
+ * though the packet was bogus.
+ */
+ if (oreach == 0)
+ report_event(EVNT_REACH, peer);
+ return(1);
+ }
+
+ /*
+ * This one is valid. Mark it so, give it to clock_filter().
+ */
+ clock_filter(peer, &ci, di, (u_fp)ei);
+
+ /*
+ * If this guy was previously unreachable, report him reachable.
+ * Note we do this here so that the peer values we return are
+ * the updated ones.
+ */
+ if (oreach == 0)
+ report_event(EVNT_REACH, peer);
+
+ /*
+ * Now update the clock. If we have found a system peer and this
+ * is a broadcast/multicast client, switch to listen mode.
+ */
+ clock_update(peer);
+ if (sys_peer && peer->flags & FLAG_MCAST2)
+ peer->hmode = MODE_BCLIENT;
+ return(1);
+}
+
+
+/*
+ * clock_update - Clock-update procedure, see section 3.4.5.
+ */
+void
+clock_update(peer)
+ struct peer *peer;
+{
+ u_char oleap;
+ u_char ostratum;
+ s_fp d;
+ extern u_char leap_mask;
+
+#ifdef DEBUG
+ if (debug)
+ printf("clock_update(%s)\n", ntoa(&peer->srcadr));
+#endif
+
+ record_peer_stats(&peer->srcadr, ctlpeerstatus(peer),
+ &peer->offset, peer->delay, peer->dispersion);
+
+ /*
+ * Call the clock selection algorithm to see if this update
+ * causes the peer to change. If this is not the system peer,
+ * quit now.
+ */
+ clock_select();
+ if (peer != sys_peer)
+ return;
+
+ /*
+ * Update the system state. This updates the system stratum,
+ * leap bits, root delay, root dispersion, reference ID and
+ * reference time. We also update select dispersion and max
+ * frequency error.
+ */
+ oleap = sys_leap;
+ ostratum = sys_stratum;
+ sys_stratum = peer->stratum + 1;
+ if (sys_stratum == 1)
+ sys_refid = peer->refid;
+ else
+ sys_refid = peer->srcadr.sin_addr.s_addr;
+ sys_reftime = peer->rec;
+ d = peer->delay;
+ if (d < 0)
+ d = -d;
+ sys_rootdelay = peer->rootdelay + d;
+ d = peer->soffset;
+ if (d < 0)
+ d = -d;
+ d += peer->dispersion + peer->selectdisp;
+ if (!peer->flags & FLAG_REFCLOCK && d < NTP_MINDISPERSE)
+ d = NTP_MINDISPERSE;
+ sys_rootdispersion = peer->rootdispersion + d;
+
+ /*
+ * Reset/adjust the system clock. Watch for timewarps here.
+ */
+ switch (local_clock(&sys_offset, peer)) {
+ case -1:
+
+ /*
+ * Clock is too screwed up. Just exit for now.
+ */
+ report_event(EVNT_SYSFAULT, (struct peer *)0);
+ exit(1);
+ /*NOTREACHED*/
+ case 0:
+
+ /*
+ * Clock was slewed. Continue on normally.
+ */
+ sys_leap = leap_consensus & leap_mask;
+ L_CLR(&sys_refskew);
+ break;
+
+ case 1:
+
+ /*
+ * Clock was stepped. Clear filter registers
+ * of all peers.
+ */
+ clear_all();
+ leap_process(); /* reset the leap interrupt */
+ sys_leap = LEAP_NOTINSYNC;
+ sys_refskew.l_i = NTP_MAXSKEW; sys_refskew.l_f = 0;
+ report_event(EVNT_CLOCKRESET, (struct peer *)0);
+ break;
+ }
+ sys_maxd = peer->dispersion + peer->selectdisp;
+ if (oleap != sys_leap)
+ report_event(EVNT_SYNCCHG, (struct peer *)0);
+ if (ostratum != sys_stratum)
+ report_event(EVNT_PEERSTCHG, (struct peer *)0);
+}
+
+
+/*
+ * poll_update - update peer poll interval. See Section 3.4.8 of the
+ * spec.
+ */
+void
+poll_update(peer, new_hpoll, randomize)
+ struct peer *peer;
+ unsigned int new_hpoll;
+ int randomize;
+{
+ register struct event *evp;
+ register u_long new_timer;
+ u_char newpoll, oldpoll;
+
+#ifdef DEBUG
+ if (debug > 1)
+ printf("poll_update(%s, %d, %d)\n", ntoa(&peer->srcadr),
+ new_hpoll, randomize);
+#endif
+ /*
+ * Catch reference clocks here. The polling interval for a
+ * reference clock is fixed and needn't be maintained by us.
+ */
+ if (peer->flags & FLAG_REFCLOCK || peer->hmode ==
+ MODE_BROADCAST)
+ return;
+
+ /*
+ * This routine * will randomly perturb the new peer.timer if
+ * requested, to try to prevent synchronization with the remote
+ * peer from occuring. There are three options, based on the
+ * value of randomize:
+ *
+ * POLL_NOTRANDOM - essentially the spec algorithm. If
+ * peer.timer is greater than the new polling interval,
+ * drop it to the new interval.
+ *
+ * POLL_RANDOMCHANGE - make changes randomly. If peer.timer
+ * must be changed, based on the comparison about, randomly
+ * perturb the new value of peer.timer.
+ *
+ * POLL_MAKERANDOM - make next interval random. Calculate
+ * a randomly perturbed poll interval. If this value is
+ * less that peer.timer, update peer.timer.
+ */
+ oldpoll = peer->hpoll;
+ if (peer->hmode == MODE_BCLIENT)
+ peer->hpoll = peer->ppoll;
+ else if ((peer->flags & FLAG_SYSPEER) && new_hpoll > sys_poll)
+ peer->hpoll = max(peer->minpoll, sys_poll);
+ else {
+ if (new_hpoll > peer->maxpoll)
+ peer->hpoll = peer->maxpoll;
+ else if (new_hpoll < peer->minpoll)
+ peer->hpoll = peer->minpoll;
+ else
+ peer->hpoll = new_hpoll;
+ }
+
+ /* hpoll <= maxpoll for sure */
+ newpoll = max((u_char)min(peer->ppoll, peer->hpoll),
+ peer->minpoll);
+ if (randomize == POLL_MAKERANDOM || (randomize ==
+ POLL_RANDOMCHANGE && newpoll != oldpoll))
+ new_timer = (1 << (newpoll - 1))
+ + ranp2(newpoll - 1) + current_time;
+ else
+ new_timer = (1 << newpoll) + current_time;
+ evp = &(peer->event_timer);
+ if (evp->next == 0 || evp->event_time > new_timer) {
+ TIMER_DEQUEUE(evp);
+ evp->event_time = new_timer;
+ TIMER_ENQUEUE(timerqueue, evp);
+ }
+}
+
+/*
+ * clear_all - clear all peer filter registers. This is done after
+ * a step change in the time.
+ */
+static void
+clear_all()
+{
+ register int i;
+ register struct peer *peer;
+
+ for (i = 0; i < HASH_SIZE; i++)
+ for (peer = peer_hash[i]; peer != 0; peer = peer->next) {
+ peer_clear(peer);
+ }
+
+ /*
+ * Clear sys_peer. We'll sync to one later.
+ */
+ sys_peer = 0;
+ sys_stratum = STRATUM_UNSPEC;
+}
+
+
+/*
+ * clear - clear peer filter registers. See Section 3.4.7 of the spec.
+ */
+void
+peer_clear(peer)
+ register struct peer *peer;
+{
+ register int i;
+
+#ifdef DEBUG
+ if (debug)
+ printf("clear(%s)\n", ntoa(&peer->srcadr));
+#endif
+ memset(CLEAR_TO_ZERO(peer), 0, LEN_CLEAR_TO_ZERO);
+ peer->hpoll = peer->minpoll;
+ peer->dispersion = NTP_MAXDISPERSE;
+ for (i = 0; i < NTP_SHIFT; i++)
+ peer->filter_error[i] = NTP_MAXDISPERSE;
+ poll_update(peer, peer->minpoll, POLL_RANDOMCHANGE);
+ clock_select();
+
+ /*
+ * Clear out the selection counters
+ */
+ peer->candidate = 0;
+ peer->select = 0;
+ peer->correct = 0;
+ peer->was_sane = 0;
+
+ /*
+ * Since we have a chance to correct possible funniness in
+ * our selection of interfaces on a multihomed host, do so
+ * by setting us to no particular interface.
+ */
+ peer->dstadr = any_interface;
+}
+
+
+/*
+ * clock_filter - add incoming clock sample to filter register and run
+ * the filter procedure to find the best sample.
+ */
+void
+clock_filter(peer, sample_offset, sample_delay, sample_error)
+ register struct peer *peer;
+ l_fp *sample_offset;
+ s_fp sample_delay;
+ u_fp sample_error;
+{
+ register int i, j, k, n;
+ register u_char *ord;
+ s_fp distance[NTP_SHIFT];
+ long skew, skewmax;
+
+#ifdef DEBUG
+ if (debug)
+ printf("clock_filter(%s, %s, %s, %s)\n",
+ ntoa(&peer->srcadr), lfptoa(sample_offset, 6),
+ fptoa(sample_delay, 5), ufptoa(sample_error, 5));
+#endif
+
+ /*
+ * Update sample errors and calculate distances. Also initialize
+ * sort index vector. We know NTP_SKEWFACTOR == 16
+ */
+ skew = sys_clock - peer->update;
+ peer->update = sys_clock;
+ ord = peer->filter_order;
+ j = peer->filter_nextpt;
+ for (i = 0; i < NTP_SHIFT; i++) {
+ peer->filter_error[j] += (u_fp)skew;
+ if (peer->filter_error[j] > NTP_MAXDISPERSE)
+ peer->filter_error[j] = NTP_MAXDISPERSE;
+ distance[i] = peer->filter_error[j] +
+ (peer->filter_delay[j] >> 1);
+ ord[i] = j;
+ if (--j < 0)
+ j += NTP_SHIFT;
+ }
+
+ /*
+ * Insert the new sample at the beginning of the register.
+ */
+ peer->filter_delay[peer->filter_nextpt] = sample_delay;
+ peer->filter_offset[peer->filter_nextpt] = *sample_offset;
+ peer->filter_soffset[peer->filter_nextpt] =
+ LFPTOFP(sample_offset);
+ peer->filter_error[peer->filter_nextpt] = sample_error;
+ distance[0] = sample_error + (sample_delay >> 1);
+
+ /*
+ * Sort the samples in the register by distance. The winning
+ * sample will be in ord[0]. Sort the samples only if the
+ * samples are not too old and the delay is meaningful.
+ */
+ skewmax = 0;
+ for (n = 0; n < NTP_SHIFT && sample_delay; n++) {
+ for (j = 0; j < n && skewmax <
+ CLOCK_MAXSEC; j++) {
+ if (distance[j] > distance[n]) {
+ s_fp ftmp;
+
+ ftmp = distance[n];
+ k = ord[n];
+ distance[n] = distance[j];
+ ord[n] = ord[j];
+ distance[j] = ftmp;
+ ord[j] = k;
+ }
+ }
+ skewmax += (1 << peer->hpoll);
+ }
+ peer->filter_nextpt++;
+ if (peer->filter_nextpt >= NTP_SHIFT)
+ peer->filter_nextpt = 0;
+
+ /*
+ * We compute the dispersion as per the spec. Note that, to make
+ * things simple, both the l_fp and s_fp offsets are retained
+ * and that the s_fp could be nonsense if the l_fp is greater
+ * than about 32000 s. However, the sanity checks in
+ * ntp_loopfilter() require the l_fp offset to be less than 1000
+ * s anyway, so not to worry.
+ */
+ if (peer->filter_error[ord[0]] >= NTP_MAXDISPERSE) {
+ peer->dispersion = NTP_MAXDISPERSE;
+ } else {
+ s_fp d;
+ u_fp y;
+
+ peer->delay = peer->filter_delay[ord[0]];
+ peer->offset = peer->filter_offset[ord[0]];
+ peer->soffset = LFPTOFP(&peer->offset);
+ peer->dispersion = peer->filter_error[ord[0]];
+
+ y = 0;
+ for (i = NTP_SHIFT - 1; i > 0; i--) {
+ if (peer->filter_error[ord[i]] >=
+ NTP_MAXDISPERSE)
+ d = NTP_MAXDISPERSE;
+ else {
+ d = peer->filter_soffset[ord[i]] -
+ peer->filter_soffset[ord[0]];
+ if (d < 0)
+ d = -d;
+ if (d > NTP_MAXDISPERSE)
+ d = NTP_MAXDISPERSE;
+ }
+ /*
+ * XXX This *knows* NTP_FILTER is 1/2
+ */
+ y = ((u_fp)d + y) >> 1;
+ }
+ peer->dispersion += y;
+
+ /*
+ * Calculate synchronization distance backdated to
+ * sys_lastselect (clock_select will fix it). We know
+ * NTP_SKEWFACTOR == 16.
+ */
+ d = peer->delay;
+ if (d < 0)
+ d = -d;
+ d += peer->rootdelay;
+ peer->synch = (d >> 1) + peer->rootdispersion +
+ peer->dispersion - (sys_clock - sys_lastselect);
+ }
+}
+
+
+/*
+ * clock_select - find the pick-of-the-litter clock
+ */
+void
+clock_select()
+{
+ register struct peer *peer;
+ register int i;
+ register int nlist, nl3;
+ register s_fp d, e;
+ register int j;
+ register int n;
+ register int allow, found, k;
+ s_fp low = 0x7fffffff;
+ s_fp high = -0x7ffffff;
+ u_fp synch[NTP_MAXCLOCK], error[NTP_MAXCLOCK];
+ struct peer *osys_peer;
+ struct peer *typeacts = 0;
+ struct peer *typelocal = 0;
+ struct peer *typepps = 0;
+ struct peer *typeprefer = 0;
+ struct peer *typesystem = 0;
+
+ static int list_alloc = 0;
+ static struct endpoint *endpoint;
+ static int *index;
+ static struct peer **peer_list;
+ static int endpoint_size = 0, index_size = 0, peer_list_size = 0;
+
+#ifdef DEBUG
+ if (debug > 1)
+ printf("clock_select()\n");
+#endif
+
+ /*
+ * Initizialize. If a prefer peer does not survive this thing,
+ * the pps_update switch will remain zero.
+ */
+ pps_update = 0;
+ nlist = 0;
+ for (n = 0; n < HASH_SIZE; n++)
+ nlist += peer_hash_count[n];
+ if (nlist > list_alloc) {
+ if (list_alloc > 0) {
+ free(endpoint);
+ free(index);
+ free(peer_list);
+ }
+ while (list_alloc < nlist) {
+ list_alloc += 5;
+ endpoint_size += 5 * 3 * sizeof *endpoint;
+ index_size += 5 * 3 * sizeof *index;
+ peer_list_size += 5 * sizeof *peer_list;
+ }
+ endpoint = (struct endpoint *)emalloc(endpoint_size);
+ index = (int *)emalloc(index_size);
+ peer_list = (struct peer **)emalloc(peer_list_size);
+ }
+
+ /*
+ * This first chunk of code is supposed to go through all
+ * peers we know about to find the NTP_MAXLIST peers which
+ * are most likely to succeed. We run through the list
+ * doing the sanity checks and trying to insert anyone who
+ * looks okay. We are at all times aware that we should
+ * only keep samples from the top two strata and we only need
+ * NTP_MAXLIST of them.
+ */
+ nlist = nl3 = 0; /* none yet */
+ for (n = 0; n < HASH_SIZE; n++) {
+ for (peer = peer_hash[n]; peer != 0; peer = peer->next) {
+ /*
+ * Clear peer selection stats
+ */
+ peer->was_sane = 0;
+ peer->correct = 0;
+ peer->candidate = 0;
+ peer->select = 0;
+
+ peer->flags &= ~FLAG_SYSPEER;
+ /*
+ * Update synch distance (NTP_SKEWFACTOR == 16).
+ * Note synch distance check instead of spec
+ * dispersion check. Naughty.
+ */
+ peer->synch += (sys_clock - sys_lastselect);
+
+ if (peer->reach == 0)
+ continue; /* unreachable */
+ if (peer->stratum > 1 && peer->refid ==
+ peer->dstadr->sin.sin_addr.s_addr)
+ continue; /* sync loop */
+ if (peer->stratum >= NTP_MAXSTRATUM ||
+ peer->stratum > sys_stratum)
+ continue; /* bad stratum */
+
+ if (peer->dispersion >= NTP_MAXDISTANCE) {
+ peer->seldisptoolarge++;
+ continue; /* too noisy or broken */
+ }
+ if (peer->org.l_ui < peer->reftime.l_ui) {
+ peer->selbroken++;
+ continue; /* very broken host */
+ }
+
+ /*
+ * Don't allow the local-clock or acts drivers
+ * in the kitchen at this point, unless the
+ * prefer peer. Do that later, but only if
+ * nobody else is around.
+ */
+ if (peer->refclktype == REFCLK_LOCALCLOCK) {
+ typelocal = peer;
+ if (!(peer->flags & FLAG_PREFER))
+ continue; /* no local clock */
+ }
+ if (peer->refclktype == REFCLK_NIST_ACTS) {
+ typeacts = peer;
+ if (!(peer->flags & FLAG_PREFER))
+ continue; /* no acts */
+ }
+
+ /*
+ * If we get this far, we assume the peer is
+ * acceptable.
+ */
+ peer->was_sane = 1;
+ peer_list[nlist++] = peer;
+
+ /*
+ * Insert each interval endpoint on the sorted
+ * list.
+ */
+ e = peer->soffset + peer->synch; /* Upper end */
+ for (i = nl3 - 1; i >= 0; i--) {
+ if (e >= endpoint[index[i]].val)
+ break;
+ index[i + 3] = index[i];
+ }
+ index[i + 3] = nl3;
+ endpoint[nl3].type = 1;
+ endpoint[nl3++].val = e;
+
+ e -= peer->synch; /* Center point */
+ for ( ; i >= 0; i--) {
+ if (e >= endpoint[index[i]].val)
+ break;
+ index[i + 2] = index[i];
+ }
+ index[i + 2] = nl3;
+ endpoint[nl3].type = 0;
+ endpoint[nl3++].val = e;
+
+ e -= peer->synch; /* Lower end */
+ for ( ; i >= 0; i--) {
+ if (e >= endpoint[index[i]].val)
+ break;
+ index[i + 1] = index[i];
+ }
+ index[i + 1] = nl3;
+ endpoint[nl3].type = -1;
+ endpoint[nl3++].val = e;
+ }
+ }
+ sys_lastselect = sys_clock;
+
+#ifdef DEBUG
+ if (debug > 2)
+ for (i = 0; i < nl3; i++)
+ printf("select: endpoint %2d %s\n",
+ endpoint[index[i]].type,
+ fptoa(endpoint[index[i]].val, 6));
+#endif
+
+ i = 0;
+ j = nl3 - 1;
+ allow = nlist; /* falsetickers assumed */
+ found = 0;
+ while (allow > 0) {
+ allow--;
+ for (n = 0; i <= j; i++) {
+ n += endpoint[index[i]].type;
+ if (n < 0)
+ break;
+ if (endpoint[index[i]].type == 0)
+ found++;
+ }
+ for (n = 0; i <= j; j--) {
+ n += endpoint[index[j]].type;
+ if (n > 0)
+ break;
+ if (endpoint[index[j]].type == 0)
+ found++;
+ }
+ if (found > allow)
+ break;
+ low = endpoint[index[i++]].val;
+ high = endpoint[index[j--]].val;
+ }
+
+ /*
+ * If no survivors remain at this point, check if the acts or
+ * local clock drivers have been found. If so, nominate one of
+ * them as the only survivor. Otherwise, give up and declare us
+ * unsynchronized.
+ */
+ if ((allow << 1) >= nlist) {
+ if (typeacts != 0) {
+ typeacts->was_sane = 1;
+ peer_list[0] = typeacts;
+ nlist = 1;
+ } else if (typelocal != 0) {
+ typelocal->was_sane = 1;
+ peer_list[0] = typelocal;
+ nlist = 1;
+ } else {
+ if (sys_peer != 0)
+ report_event(EVNT_PEERSTCHG,
+ (struct peer *)0);
+ sys_peer = 0;
+ sys_stratum = STRATUM_UNSPEC;
+ return;
+ }
+ }
+
+#ifdef DEBUG
+ if (debug > 2)
+ printf("select: low %s high %s\n", fptoa(low, 6),
+ fptoa(high, 6));
+#endif
+
+ /*
+ * Clustering algorithm. Process intersection list to discard
+ * outlyers. Construct candidate list in cluster order
+ * determined by the sum of peer synchronization distance plus
+ * scaled stratum. We must find at least one peer.
+ */
+ j = 0;
+ for (i = 0; i < nlist; i++) {
+ peer = peer_list[i];
+ if (nlist > 1 && (peer->soffset < low || high <
+ peer->soffset))
+ continue;
+ peer->correct = 1;
+ d = peer->synch + ((u_long)peer->stratum <<
+ NTP_DISPFACTOR);
+ if (j >= NTP_MAXCLOCK) {
+ if (d >= synch[j - 1])
+ continue;
+ else
+ j--;
+ }
+ for (k = j; k > 0; k--) {
+ if (d >= synch[k - 1])
+ break;
+ synch[k] = synch[k - 1];
+ peer_list[k] = peer_list[k - 1];
+ }
+ peer_list[k] = peer;
+ synch[k] = d;
+ j++;
+ }
+ nlist = j;
+
+#ifdef DEBUG
+ if (debug > 2)
+ for (i = 0; i < nlist; i++)
+ printf("select: candidate %s cdist %s\n",
+ ntoa(&peer_list[i]->srcadr),
+ fptoa(synch[i], 6));
+#endif
+
+ /*
+ * Now, prune outlyers by root dispersion. Continue as long as
+ * there are more than NTP_MINCLOCK survivors and the minimum
+ * select dispersion is greater than the maximum peer
+ * dispersion. Stop if we are about to discard a preferred peer.
+ */
+ for (i = 0; i < nlist; i++) {
+ peer = peer_list[i];
+ peer->candidate = i + 1;
+ error[i] = peer_list[i]->rootdispersion +
+ peer_list[i]->dispersion +
+ (sys_clock - peer_list[i]->update);
+ }
+ while (1) {
+ u_fp maxd = 0;
+ e = error[0];
+ for (k = i = nlist - 1; i >= 0; i--) {
+ u_fp sdisp = 0;
+
+ for (j = nlist - 1; j > 0; j--) {
+ d = peer_list[i]->soffset
+ - peer_list[j]->soffset;
+ if (d < 0)
+ d = -d;
+ sdisp += d;
+ sdisp = ((sdisp >> 1) + sdisp) >> 1;
+ }
+ peer_list[i]->selectdisp = sdisp;
+ if (sdisp > maxd) {
+ maxd = sdisp;
+ k = i;
+ }
+ if (error[i] < e)
+ e = error[i];
+ }
+ if (nlist <= NTP_MINCLOCK || maxd <= e ||
+ peer_list[k]->flags & FLAG_PREFER)
+ break;
+ for (j = k + 1; j < nlist; j++) {
+ peer_list[j - 1] = peer_list[j];
+ error[j - 1] = error[j];
+ }
+ nlist--;
+ }
+
+#ifdef DEBUG
+ if (debug > 1) {
+ for (i = 0; i < nlist; i++)
+ printf("select: survivor %s offset %s, cdist %s\n",
+ ntoa(&peer_list[i]->srcadr),
+ lfptoa(&peer_list[i]->offset, 6),
+ fptoa(synch[i], 5));
+ }
+#endif
+
+ /*
+ * What remains is a list of not greater than NTP_MINCLOCK
+ * peers. We want only a peer at the lowest stratum to become
+ * the system peer, although all survivors are eligible for the
+ * combining algorithm. First record their order, diddle the
+ * flags and clamp the poll intervals. Then, consider the peers
+ * at the lowest stratum. Of these, OR the leap bits on the
+ * assumption that, if some of them honk nonzero bits, they must
+ * know what they are doing. Also, check for prefer and pps
+ * peers. If a prefer peer is found within CLOCK_MAX, update the
+ * pps switch. Of the other peers not at the lowest stratum,
+ * check if the system peer is among them and, if found, zap
+ * him. We note that the head of the list is at the lowest
+ * stratum and that unsynchronized peers cannot survive this
+ * far.
+ */
+ leap_consensus = 0;
+ for (i = nlist - 1; i >= 0; i--) {
+ peer_list[i]->select = i + 1;
+ peer_list[i]->flags |= FLAG_SYSPEER;
+ poll_update(peer_list[i], peer_list[i]->hpoll,
+ POLL_RANDOMCHANGE);
+ if (peer_list[i]->stratum == peer_list[0]->stratum) {
+ leap_consensus |= peer_list[i]->leap;
+ if (peer_list[i]->refclktype == REFCLK_ATOM_PPS)
+ typepps = peer_list[i];
+ if (peer_list[i] == sys_peer)
+ typesystem = peer_list[i];
+ if (peer_list[i]->flags & FLAG_PREFER) {
+ typeprefer = peer_list[i];
+ if (typeprefer->soffset >= -CLOCK_MAX_FP &&
+ typeprefer->soffset < CLOCK_MAX_FP)
+ pps_update = 1;
+ }
+ } else {
+ if (peer_list[i] == sys_peer)
+ sys_peer = 0;
+ }
+ }
+
+ /*
+ * Mitigation rules of the game. There are several types of
+ * peers that make a difference here: (1) prefer local peers
+ * (type REFCLK_LOCALCLOCK with FLAG_PREFER) or prefer acts
+ * peers (type REFCLK_NIST_ATOM with FLAG_PREFER), (2) pps peers
+ * (type REFCLK_ATOM_PPS), (3) remaining prefer peers (flag
+ * FLAG_PREFER), (4) the existing system peer, if any, (5) the
+ * head of the survivor list. Note that only one peer can be
+ * declared prefer. The order of preference is in the order
+ * stated. Note that all of these must be at the lowest stratum,
+ * i.e., the stratum of the head of the survivor list.
+ */
+ osys_peer = sys_peer;
+ if (typeprefer && (typeprefer == typelocal || typeprefer ==
+ typeacts || !typepps)) {
+ sys_peer = typeprefer;
+ sys_peer->selectdisp = 0;
+ sys_offset = sys_peer->offset;
+#ifdef DEBUG
+ if (debug)
+ printf("select: prefer offset %s\n",
+ lfptoa(&sys_offset, 6));
+#endif
+ } else if (typepps) {
+ sys_peer = typepps;
+ sys_peer->selectdisp = 0;
+ sys_offset = sys_peer->offset;
+#ifdef DEBUG
+ if (debug)
+ printf("select: pps offset %s\n",
+ lfptoa(&sys_offset, 6));
+#endif
+ } else {
+ if (!typesystem)
+ sys_peer = peer_list[0];
+ clock_combine(peer_list, nlist);
+#ifdef DEBUG
+ if (debug)
+ printf("select: combine offset %s\n",
+ lfptoa(&sys_offset, 6));
+#endif
+ }
+
+ /*
+ * If we got a new system peer from all of this, report the
+ * event and clamp the system poll interval.
+ */
+ if (osys_peer != sys_peer) {
+ sys_poll = sys_peer->minpoll;
+ report_event(EVNT_PEERSTCHG, (struct peer *)0);
+ }
+}
+
+/*
+ * clock_combine - combine offsets from selected peers
+ *
+ * Note: this routine uses only those peers at the lowest stratum.
+ * Strictly speaking, this is at variance with the spec.
+ */
+void
+clock_combine(peers, npeers)
+ struct peer **peers;
+ int npeers;
+{
+ register int i, j, k;
+ register u_fp a, b, d;
+ u_fp synch[NTP_MAXCLOCK];
+ l_fp coffset[NTP_MAXCLOCK];
+ l_fp diff;
+
+ /*
+ * Sort the offsets by synch distance.
+ */
+ k = 0;
+ for (i = 0; i < npeers; i++) {
+ if (peers[i]->stratum > sys_peer->stratum)
+ continue;
+ d = peers[i]->synch;
+ for (j = k; j > 0; j--) {
+ if (synch[j - 1] <= d)
+ break;
+ synch[j] = synch[j - 1];
+ coffset[j] = coffset[j - 1];
+ }
+ synch[j] = d;
+ coffset[j] = peers[i]->offset;
+ k++;
+ }
+
+ /*
+ * Succesively combine the two offsets with the highest
+ * distance and enter the result into the sorted list.
+ */
+ for (i = k - 2; i >= 0; i--) {
+ /*
+ * The possible weights for the most distant offset
+ * are 1/2, 1/4, 1/8 and zero. We combine the synch
+ * distances as if they were variances of the offsets;
+ * the given weights allow us to stay within 16/15 of
+ * the optimum combined variance at each step, and
+ * within 8/7 on any series.
+ *
+ * The breakeven points for the weigths are found
+ * where the smaller distance is 3/8, 3/16 and 1/16
+ * of the sum, respectively.
+ */
+ d = synch[i];
+ a = (d + synch[i + 1]) >> 2; /* (d1+d2)/4 */
+ b = a>>1; /* (d1+d2)/8 */
+ if (d <= (b>>1)) /* d1 <= (d1+d2)/16 */
+ /*
+ * Below 1/16, no combination is done,
+ * we just drop the distant offset.
+ */
+ continue;
+
+ /*
+ * The offsets are combined by shifting their
+ * difference the appropriate number of times and
+ * adding it back in.
+ */
+ diff = coffset[i + 1];
+ L_SUB(&diff, &coffset[i]);
+ L_RSHIFT(&diff);
+ if (d >= a + b) { /* d1 >= 3(d1+d2)/8 */
+ /*
+ * Above 3/8, the weight is 1/2, and the
+ * combined distance is (d1+d2)/4
+ */
+ d = a;
+ } else {
+ a >>= 2; /* (d1+d2)/16 */
+ L_RSHIFT(&diff);
+ if (d >= a + b) { /* d1 >= 3(d1+d2)/16 */
+ /*
+ * Between 3/16 and 3/8, the weight
+ * is 1/4, and the combined distance
+ * is (9d1+d2)/16 = d1/2 + (d1+d2)/16
+ */
+ d = (d>>1) + a;
+ } else {
+ /*
+ * Between 1/16 and 3/16, the weight
+ * is 1/8, and the combined distance
+ * is (49d1+d2)/64 = 3d1/4+(d1+d2)/64
+ * (We know d > a, so the shift is safe).
+ */
+ L_RSHIFT(&diff);
+ d -= (d - a)>>2;
+ }
+ }
+ /*
+ * Now we can make the combined offset and insert it
+ * in the list.
+ */
+ L_ADD(&diff, &coffset[i]);
+ for (j = i; j > 0; j--) {
+ if (d >= synch[j - 1])
+ break;
+ synch[j] = synch[j - 1];
+ coffset[j] = coffset[j - 1];
+ }
+ synch[j] = d;
+ coffset[j] = diff;
+ }
+
+ /*
+ * The result is put where clock_update() can find it.
+ */
+ sys_offset = coffset[0];
+}
+
+
+/*
+ * fast_xmit - fast path send for stateless (non-)associations
+ */
+void
+fast_xmit(rbufp, rmode, authentic)
+ struct recvbuf *rbufp;
+ int rmode;
+ int authentic;
+{
+ struct pkt xpkt;
+ register struct pkt *rpkt;
+ u_char xmode;
+ u_short xkey = 0;
+ int docrypt = 0;
+ l_fp xmt_ts;
+ u_fp precision;
+
+#ifdef DEBUG
+ if (debug > 1)
+ printf("fast_xmit(%s, %d)\n", ntoa(&rbufp->recv_srcadr), rmode);
+#endif
+
+ /*
+ * Make up new packet and send it quick
+ */
+ rpkt = &rbufp->recv_pkt;
+ if (rmode == MODE_ACTIVE)
+ xmode = MODE_PASSIVE;
+ else
+ xmode = MODE_SERVER;
+
+ if (rbufp->recv_length >= LEN_PKT_MAC) {
+ docrypt = rbufp->recv_length - LEN_PKT_NOMAC;
+ if (authentic)
+ xkey = ntohl(rpkt->keyid);
+ }
+
+ xpkt.li_vn_mode = PKT_LI_VN_MODE(sys_leap,
+ PKT_VERSION(rpkt->li_vn_mode), xmode);
+ xpkt.stratum = STRATUM_TO_PKT(sys_stratum);
+ xpkt.ppoll = max(NTP_MINPOLL, rpkt->ppoll);
+ xpkt.precision = sys_precision;
+ xpkt.rootdelay = HTONS_FP(sys_rootdelay);
+ precision = FP_SECOND >> -(int)sys_precision;
+ if (precision == 0)
+ precision = 1;
+ xpkt.rootdispersion = HTONS_FP(sys_rootdispersion +
+ precision + LFPTOFP(&sys_refskew));
+ xpkt.refid = sys_refid;
+ HTONL_FP(&sys_reftime, &xpkt.reftime);
+ xpkt.org = rpkt->xmt;
+ HTONL_FP(&rbufp->recv_time, &xpkt.rec);
+
+ /*
+ * If we are encrypting, do it. Else don't. Easy.
+ */
+ if (docrypt) {
+ int maclen;
+
+ xpkt.keyid = htonl(xkey);
+ auth1crypt(xkey, (U_LONG *)&xpkt, LEN_PKT_NOMAC);
+ get_systime(&xmt_ts);
+ L_ADDUF(&xmt_ts, sys_authdelay);
+ HTONL_FP(&xmt_ts, &xpkt.xmt);
+ maclen = auth2crypt(xkey, (U_LONG *)&xpkt, LEN_PKT_NOMAC);
+ sendpkt(&rbufp->recv_srcadr, rbufp->dstadr, -9, &xpkt,
+ LEN_PKT_NOMAC + maclen);
+ } else {
+ /*
+ * Get xmt timestamp, then send it without mac field
+ */
+ get_systime(&xmt_ts);
+ HTONL_FP(&xmt_ts, &xpkt.xmt);
+ sendpkt(&rbufp->recv_srcadr, rbufp->dstadr, -10, &xpkt,
+ LEN_PKT_NOMAC);
+ }
+}
+
+/*
+ * Find the precision of this particular machine
+ */
+#define DUSECS 1000000 /* us in a s */
+#define HUSECS (1 << 20) /* approx DUSECS for shifting etc */
+#define MINSTEP 5 /* minimum clock increment (ys) */
+#define MAXSTEP 20000 /* maximum clock increment (us) */
+#define MINLOOPS 5 /* minimum number of step samples */
+
+/*
+ * This routine calculates the differences between successive calls to
+ * gettimeofday(). If a difference is less than zero, the us field
+ * has rolled over to the next second, so we add a second in us. If
+ * the difference is greater than zero and less than MINSTEP, the
+ * clock has been advanced by a small amount to avoid standing still.
+ * If the clock has advanced by a greater amount, then a timer interrupt
+ * has occurred and this amount represents the precision of the clock.
+ * In order to guard against spurious values, which could occur if we
+ * happen to hit a fat interrupt, we do this for MINLOOPS times and
+ * keep the minimum value obtained.
+ */
+int default_get_precision()
+{
+ struct timeval tp;
+ struct timezone tzp;
+ long last;
+ int i;
+ long diff;
+ long val;
+ long usec;
+
+ usec = 0;
+ val = MAXSTEP;
+ GETTIMEOFDAY(&tp, &tzp);
+ last = tp.tv_usec;
+ for (i = 0; i < MINLOOPS && usec < HUSECS;) {
+ GETTIMEOFDAY(&tp, &tzp);
+ diff = tp.tv_usec - last;
+ last = tp.tv_usec;
+ if (diff < 0)
+ diff += DUSECS;
+ usec += diff;
+ if (diff > MINSTEP) {
+ i++;
+ if (diff < val)
+ val = diff;
+ }
+ }
+ syslog(LOG_INFO, "precision = %d usec", val);
+ if (usec >= HUSECS)
+ val = MINSTEP; /* val <= MINSTEP; fast machine */
+ diff = HUSECS;
+ for (i = 0; diff > val; i--)
+ diff >>= 1;
+ return (i);
+}
+
+/*
+ * init_proto - initialize the protocol module's data
+ */
+void
+init_proto()
+{
+ l_fp dummy;
+
+ /*
+ * Fill in the sys_* stuff. Default is don't listen to
+ * broadcasting, don't authenticate.
+ */
+ sys_leap = LEAP_NOTINSYNC;
+ sys_stratum = STRATUM_UNSPEC;
+ sys_precision = (s_char)default_get_precision();
+ sys_rootdelay = 0;
+ sys_rootdispersion = 0;
+ sys_refid = 0;
+ L_CLR(&sys_reftime);
+ sys_refskew.l_i = NTP_MAXSKEW; sys_refskew.l_f = 0;
+ sys_peer = 0;
+ sys_poll = NTP_MINPOLL;
+ get_systime(&dummy);
+ sys_lastselect = sys_clock;
+
+ sys_bclient = 0;
+ sys_bdelay = DEFBROADDELAY;
+ sys_authenticate = 0;
+ sys_authdelay = DEFAUTHDELAY;
+
+ sys_stattime = 0;
+ sys_badstratum = 0;
+ sys_oldversionpkt = 0;
+ sys_newversionpkt = 0;
+ sys_badlength = 0;
+ sys_unknownversion = 0;
+ sys_processed = 0;
+ sys_badauth = 0;
+
+ /*
+ * Default these to enable
+ */
+ pll_enable = 1;
+ stats_control = 1;
+}
+
+
+/*
+ * proto_config - configure the protocol module
+ */
+void
+proto_config(item, value)
+ int item;
+ u_long value;
+{
+ /*
+ * Figure out what he wants to change, then do it
+ */
+ switch (item) {
+ case PROTO_PLL:
+ /*
+ * Turn on/off pll clock correction
+ */
+ pll_enable = (int)value;
+ break;
+
+ case PROTO_MONITOR:
+ /*
+ * Turn on/off monitoring
+ */
+ if (value)
+ mon_start(MON_ON);
+ else
+ mon_stop(MON_ON);
+ break;
+
+ case PROTO_FILEGEN:
+ /*
+ * Turn on/off statistics
+ */
+ stats_control = (int)value;
+ break;
+
+ case PROTO_BROADCLIENT:
+ /*
+ * Turn on/off facility to listen to broadcasts
+ */
+ sys_bclient = (int)value;
+ if (value)
+ io_setbclient();
+ else
+ io_unsetbclient();
+ break;
+
+ case PROTO_MULTICAST_ADD:
+ /*
+ * Add muliticast group address
+ */
+ sys_bclient = 1;
+ io_multicast_add(value);
+ break;
+
+ case PROTO_MULTICAST_DEL:
+ /*
+ * Delete multicast group address
+ */
+ sys_bclient = 1;
+ io_multicast_del(value);
+ break;
+
+ case PROTO_PRECISION:
+ /*
+ * Set system precision
+ */
+ sys_precision = (s_char)value;
+ break;
+
+ case PROTO_BROADDELAY:
+ /*
+ * Set default broadcast delay (s_fp)
+ */
+ if (sys_bdelay < 0)
+ sys_bdelay = -(-value >> 16);
+ else
+ sys_bdelay = value >> 16;
+ break;
+
+ case PROTO_AUTHENTICATE:
+ /*
+ * Specify the use of authenticated data
+ */
+ sys_authenticate = (int)value;
+ break;
+
+
+ case PROTO_AUTHDELAY:
+ /*
+ * Set authentication delay (l_fp fraction)
+ */
+ sys_authdelay = value;
+ break;
+
+ default:
+ /*
+ * Log this error
+ */
+ syslog(LOG_ERR, "proto_config: illegal item %d, value %ld",
+ item, value);
+ break;
+ }
+}
+
+
+/*
+ * proto_clr_stats - clear protocol stat counters
+ */
+void
+proto_clr_stats()
+{
+ sys_badstratum = 0;
+ sys_oldversionpkt = 0;
+ sys_newversionpkt = 0;
+ sys_unknownversion = 0;
+ sys_badlength = 0;
+ sys_processed = 0;
+ sys_badauth = 0;
+ sys_stattime = current_time;
+ sys_limitrejected = 0;
+}
diff --git a/usr.sbin/xntpd/xntpd/ntp_refclock.c b/usr.sbin/xntpd/xntpd/ntp_refclock.c
new file mode 100644
index 0000000..29c80d9
--- /dev/null
+++ b/usr.sbin/xntpd/xntpd/ntp_refclock.c
@@ -0,0 +1,1286 @@
+/*
+ * ntp_refclock - processing support for reference clocks
+ */
+#ifdef REFCLOCK
+
+#include <stdio.h>
+#include <sys/types.h>
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_unixtime.h"
+#include "ntp_refclock.h"
+#include "ntp_stdlib.h"
+
+#ifdef PPS
+#include <sys/ppsclock.h>
+#endif /* PPS */
+
+/*
+ * Reference clock support is provided here by maintaining the fiction
+ * that the clock is actually a peer. As no packets are exchanged with a
+ * reference clock, however, we replace the transmit, receive and packet
+ * procedures with separate code to simulate them. Routines
+ * refclock_transmit() and refclock_receive() maintain the peer
+ * variables in a state analogous to an actual peer and pass reference
+ * clock data on through the filters. Routines refclock_peer() and
+ * refclock_unpeer() are called to initialize and terminate reference
+ * clock associations. A set of utility routines is included to open
+ * serial devices, process sample data, edit input lines to extract
+ * embedded timestamps and to peform various debugging functions.
+ *
+ * The main interface used by these routines is the refclockproc
+ * structure, which contains for most drivers the decimal equivalants of
+ * the year, day, month, hour, second and millisecond/microsecond
+ * decoded from the ASCII timecode. Additional information includes the
+ * receive timestamp, exception report, statistics tallies, etc. In
+ * addition, there may be a driver-specific unit structure used for
+ * local control of the device.
+ *
+ * The support routines are passed a pointer to the peer structure,
+ * which is used for all peer-specific processing and contains a pointer
+ * to the refclockproc structure, which in turn containes a pointer to
+ * the unit structure, if used. In addition, some routines expect an
+ * address in the dotted quad form 127.127.t.u, where t is the clock
+ * type and u the unit. A table typeunit[type][unit] contains the peer
+ * structure pointer for each configured clock type and unit.
+ *
+ * Most drivers support the 1-pps signal provided by some radios and
+ * connected via a level converted described in the gadget directory.
+ * The signal is captured using a separate, dedicated serial port and
+ * the tty_clk line discipline/streams modules described in the kernel
+ * directory. For the highest precision, the signal is captured using
+ * the carrier-detect line of the same serial port using the ppsclock
+ * streams module described in the ppsclock directory.
+ */
+#define REFCLOCKMAXDISPERSE (FP_SECOND/4) /* max sample dispersion */
+#define MAXUNIT 44 /* max units */
+#ifndef CLKLDISC
+#define CLKLDISC 10 /* XXX temp tty_clk line discipline */
+#endif
+#ifndef CHULDISC
+#define CHULDISC 10 /* XXX temp tty_chu line discipline */
+#endif
+
+/*
+ * The refclock configuration table. Imported from refclock_conf
+ */
+extern struct refclock *refclock_conf[];
+extern u_char num_refclock_conf;
+
+/*
+ * Imported from the I/O module
+ */
+extern struct interface *any_interface;
+extern struct interface *loopback_interface;
+
+/*
+ * Imported from ntp_loopfilter module
+ */
+extern int fdpps; /* pps file descriptor */
+
+/*
+ * Imported from the timer module
+ */
+extern u_long current_time;
+extern struct event timerqueue[];
+
+/*
+ * Imported from the main and peer modules. We use the same algorithm
+ * for spacing out timers at configuration time that the peer module
+ * does.
+ */
+extern u_long init_peer_starttime;
+extern int initializing;
+extern int debug;
+
+/*
+ * Type/unit peer index. Used to find the peer structure for control and
+ * debugging. When all clock drivers have been converted to new style,
+ * this dissapears.
+ */
+static struct peer *typeunit[REFCLK_MAX + 1][MAXUNIT];
+
+
+/*
+ * refclock_report - note the occurance of an event
+ *
+ * This routine presently just remembers the report and logs it, but
+ * does nothing heroic for the trap handler. It tries to be a good
+ * citizen and bothers the system log only if things change.
+ */
+void
+refclock_report(peer, code)
+ struct peer *peer;
+ u_char code;
+{
+ struct refclockproc *pp;
+
+ if (!(pp = peer->procptr))
+ return;
+ if (code == CEVNT_BADREPLY)
+ pp->badformat++;
+ if (code == CEVNT_BADTIME)
+ pp->baddata++;
+ if (code == CEVNT_TIMEOUT)
+ pp->noreply++;
+ if (pp->currentstatus != code) {
+ pp->currentstatus = code;
+ if (code == CEVNT_NOMINAL)
+ return;
+ pp->lastevent = code;
+ if (code == CEVNT_FAULT)
+ syslog(LOG_ERR,
+ "clock %s fault %x", ntoa(&peer->srcadr), code);
+ else {
+ syslog(LOG_INFO,
+ "clock %s event %x", ntoa(&peer->srcadr), code);
+ }
+ }
+}
+
+
+/*
+ * init_refclock - initialize the reference clock drivers
+ *
+ * This routine calls each of the drivers in turn to initialize internal
+ * variables, if necessary. Most drivers have nothing to say at this
+ * point.
+ */
+void
+init_refclock()
+{
+ int i, j;
+
+ for (i = 0; i < num_refclock_conf; i++) {
+ if (refclock_conf[i]->clock_init != noentry)
+ (refclock_conf[i]->clock_init)();
+ for (j = 0; j < MAXUNIT; j++)
+ typeunit[i][j] = 0;
+ }
+}
+
+
+/*
+ * refclock_newpeer - initialize and start a reference clock
+ *
+ * This routine allocates and initializes the interface structure which
+ * supports a reference clock in the form of an ordinary NTP peer. A
+ * driver-specific support routine completes the initialization, if
+ * used. Default peer variables which identify the clock and establish
+ * its reference ID and stratum are set here. It returns one if success
+ * and zero if the clock address is invalid or already running,
+ * insufficient resources are available or the driver declares a bum
+ * rap.
+ */
+int
+refclock_newpeer(peer)
+ struct peer *peer; /* peer structure pointer */
+{
+ struct refclockproc *pp;
+ u_char clktype;
+ int unit;
+
+ /*
+ * Check for valid clock address. If already running, shut it * down first.
+ */
+ if (!ISREFCLOCKADR(&peer->srcadr)) {
+ syslog(LOG_ERR,
+ "refclock_newpeer: clock address %s invalid",
+ ntoa(&peer->srcadr));
+ return (0);
+ }
+ clktype = REFCLOCKTYPE(&peer->srcadr);
+ unit = REFCLOCKUNIT(&peer->srcadr);
+ if (clktype >= num_refclock_conf || unit > MAXUNIT ||
+ refclock_conf[clktype]->clock_start == noentry) {
+ syslog(LOG_ERR,
+ "refclock_newpeer: clock type %d invalid\n",
+ clktype);
+ return (0);
+ }
+ refclock_unpeer(peer);
+
+ /*
+ * Allocate and initialize interface structure
+ */
+ if (!(pp = (struct refclockproc *)
+ emalloc(sizeof(struct refclockproc))))
+ return (0);
+ memset((char *)pp, 0, sizeof(struct refclockproc));
+ typeunit[clktype][unit] = peer;
+ peer->procptr = pp;
+
+ /*
+ * Initialize structures
+ */
+ peer->refclktype = clktype;
+ peer->refclkunit = unit;
+ peer->flags |= FLAG_REFCLOCK;
+ peer->event_timer.peer = peer;
+ peer->event_timer.event_handler = refclock_transmit;
+ pp->type = clktype;
+ pp->timestarted = current_time;
+ peer->stratum = STRATUM_REFCLOCK;
+ peer->refid = peer->srcadr.sin_addr.s_addr;
+ peer->maxpoll = peer->minpoll;
+
+ /*
+ * Do driver dependent initialization
+ */
+ if (!((refclock_conf[clktype]->clock_start)(unit, peer))) {
+ free(pp);
+ return (0);
+ }
+ peer->hpoll = peer->minpoll;
+ peer->ppoll = peer->maxpoll;
+ if (peer->stratum <= 1)
+ peer->refid = pp->refid;
+ else
+ peer->refid = peer->srcadr.sin_addr.s_addr;
+
+ /*
+ * Set up the timeout for polling and reachability determination
+ */
+ if (initializing) {
+ init_peer_starttime += (1 << EVENT_TIMEOUT);
+ if (init_peer_starttime >= (1 << peer->minpoll))
+ init_peer_starttime = (1 << EVENT_TIMEOUT);
+ peer->event_timer.event_time = init_peer_starttime;
+ } else {
+ peer->event_timer.event_time = current_time +
+ (1 << peer->hpoll);
+ }
+ TIMER_ENQUEUE(timerqueue, &peer->event_timer);
+ return (1);
+}
+
+
+/*
+ * refclock_unpeer - shut down a clock
+ */
+void
+refclock_unpeer(peer)
+ struct peer *peer; /* peer structure pointer */
+{
+ u_char clktype;
+ int unit;
+
+ /*
+ * Wiggle the driver to release its resources, then give back
+ * the interface structure.
+ */
+ if (!peer->procptr)
+ return;
+ clktype = peer->refclktype;
+ unit = peer->refclkunit;
+ if (refclock_conf[clktype]->clock_shutdown != noentry)
+ (refclock_conf[clktype]->clock_shutdown)(unit, peer);
+ free(peer->procptr);
+ peer->procptr = 0;
+}
+
+
+/*
+ * refclock_transmit - simulate the transmit procedure
+ *
+ * This routine implements the NTP transmit procedure for a reference
+ * clock. This provides a mechanism to call the driver at the NTP poll
+ * interval, as well as provides a reachability mechanism to detect a
+ * broken radio or other madness.
+ */
+void
+refclock_transmit(peer)
+ struct peer *peer; /* peer structure pointer */
+{
+ struct refclockproc *pp;
+ u_char clktype;
+ int unit;
+ u_char opeer_reach;
+
+ pp = peer->procptr;
+ clktype = peer->refclktype;
+ unit = peer->refclkunit;
+ peer->sent++;
+
+ /*
+ * The transmit procedure is supposed to freeze a timestamp.
+ * Get one just for fun, and to tell when we last were here.
+ */
+ get_systime(&peer->xmt);
+
+ /*
+ * Fiddle reachability.
+ */
+ opeer_reach = peer->reach;
+ peer->reach <<= 1;
+ if (peer->reach == 0) {
+ /*
+ * Clear this one out. No need to redo selection since
+ * this fellow will definitely be suffering from
+ * dispersion madness.
+ */
+ if (opeer_reach != 0) {
+ peer_clear(peer);
+ peer->timereachable = current_time;
+ report_event(EVNT_UNREACH, peer);
+ }
+
+ /*
+ * Update reachability and poll variables
+ */
+ } else if ((opeer_reach & 3) == 0) {
+ l_fp off;
+
+ if (peer->valid > 0)
+ peer->valid--;
+ L_CLR(&off);
+ clock_filter(peer, &off, 0, NTP_MAXDISPERSE);
+ if (peer->flags & FLAG_SYSPEER)
+ clock_select();
+ } else if (peer->valid < NTP_SHIFT)
+ peer->valid++;
+
+ /*
+ * If he wants to be polled, do it. New style drivers do not use
+ * the unit argument, since the fudge stuff is in the
+ * refclockproc structure.
+ */
+ if (refclock_conf[clktype]->clock_poll != noentry)
+ (refclock_conf[clktype]->clock_poll)(unit, peer);
+
+ /*
+ * Finally, reset the timer
+ */
+ peer->event_timer.event_time += (1 << peer->hpoll);
+ TIMER_ENQUEUE(timerqueue, &peer->event_timer);
+}
+
+
+/*
+ * Compare two l_fp's - used with qsort()
+ */
+static int
+refclock_cmpl_fp(p1, p2)
+ register void *p1, *p2; /* l_fp to compare */
+{
+
+ if (!L_ISGEQ((l_fp *)p1, (l_fp *)p2))
+ return (-1);
+ if (L_ISEQU((l_fp *)p1, (l_fp *)p2))
+ return (0);
+ return (1);
+}
+
+
+/*
+ * refclock_process - process a pile of samples from the clock
+ *
+ * This routine converts the timecode in the form days, hours, miinutes,
+ * seconds, milliseconds/microseconds to internal timestamp format. It
+ * then calculates the difference from the receive timestamp and
+ * assembles the samples in a shift register. It implements a recursive
+ * median filter to suppress spikes in the data, as well as determine a
+ * rough dispersion estimate. A configuration constant time adjustment
+ * fudgetime1 can be added to the final offset to compensate for various
+ * systematic errors. The routine returns one if success and zero if
+ * failure due to invalid timecode data or very noisy offsets.
+ */
+int
+refclock_process(pp, nstart, nskeep)
+ struct refclockproc *pp; /* peer structure pointer */
+ int nstart; /* stages of median filter */
+ int nskeep; /* stages after outlyer trim */
+{
+ int i, n;
+ l_fp offset, median, lftmp;
+ l_fp off[MAXSTAGE];
+ u_fp disp;
+
+ /*
+ * Compute the timecode timestamp from the days, hours, minutes,
+ * seconds and milliseconds/microseconds of the timecode. Use
+ * clocktime() for the aggregate seconds and the msec/usec for
+ * the fraction, when present. Note that this code relies on the
+ * filesystem time for the years and does not use the years of
+ * the timecode.
+ */
+ pp->nstages = nstart;
+ if (!clocktime(pp->day, pp->hour, pp->minute, pp->second, GMT,
+ pp->lastrec.l_ui, &pp->yearstart, &pp->lastref.l_ui))
+ return (0);
+ if (pp->usec) {
+ TVUTOTSF(pp->usec, pp->lastref.l_uf);
+ } else {
+ MSUTOTSF(pp->msec, pp->lastref.l_uf);
+ }
+
+ /*
+ * Subtract the receive timestamp from the timecode timestamp
+ * to form the raw offset. Insert in the median filter shift
+ * register.
+ */
+ i = ((int)(pp->coderecv)) % pp->nstages;
+ offset = pp->lastref;
+ L_SUB(&offset, &pp->lastrec);
+ pp->filter[i] = offset;
+ if (pp->coderecv == 0)
+ for (i = 1; i < pp->nstages; i++)
+ pp->filter[i] = pp->filter[0];
+ pp->coderecv++;
+
+ /*
+ * Copy the raw offsets and sort into ascending order
+ */
+ for (i = 0; i < pp->nstages; i++)
+ off[i] = pp->filter[i];
+ qsort((char *)off, pp->nstages, sizeof(l_fp), refclock_cmpl_fp);
+
+ /*
+ * Reject the furthest from the median of nstages samples until
+ * nskeep samples remain.
+ */
+ i = 0;
+ n = pp->nstages;
+ while ((n - i) > nskeep) {
+ lftmp = off[n - 1];
+ median = off[(n + i) / 2];
+ L_SUB(&lftmp, &median);
+ L_SUB(&median, &off[i]);
+ if (L_ISHIS(&median, &lftmp)) {
+ /* reject low end */
+ i++;
+ } else {
+ /* reject high end */
+ n--;
+ }
+ }
+
+ /*
+ * Compute the dispersion based on the difference between the
+ * extremes of the remaining offsets. Add to this the time since
+ * the last clock update, which represents the dispersion
+ * increase with time. We know that NTP_MAXSKEW is 16. If the
+ * sum is greater than the allowed sample dispersion, bail out.
+ * If the loop is unlocked, return the most recent offset;
+ * otherwise, return the median offset. In either case include
+ * the configured fudgetime1 adjustment.
+ */
+ lftmp = off[n - 1];
+ L_SUB(&lftmp, &off[i]);
+ disp = LFPTOFP(&lftmp) + current_time - pp->lasttime;
+ if (disp > REFCLOCKMAXDISPERSE)
+ return (0);
+ pp->offset = offset;
+ L_ADD(&pp->offset, &pp->fudgetime1);
+ pp->dispersion = disp;
+ return (1);
+}
+
+
+/*
+ * refclock_receive - simulate the receive and packet procedures
+ *
+ * This routine simulates the NTP receive and packet procedures for a
+ * reference clock. This provides a mechanism in which the ordinary NTP
+ * filter, selection and combining algorithms can be used to suppress
+ * misbehaving radios and to mitigate between them when more than one is
+ * available for backup.
+ */
+void
+refclock_receive(peer, offset, delay, dispersion, reftime, rectime, leap)
+ struct peer *peer; /* peer structure pointer */
+ l_fp *offset; /* computed offset (s) */
+ s_fp delay; /* computed delay to peer */
+ u_fp dispersion; /* computed dispersion to peer */
+ l_fp *reftime; /* time at last clock update */
+ l_fp *rectime; /* time at last peer update */
+ int leap; /* synchronization/leap code */
+{
+ int restrict;
+ int trustable;
+ u_fp precision;
+
+ peer->received++;
+#ifdef DEBUG
+ if (debug)
+ printf("refclock_receive: %s %s %s %s)\n",
+ ntoa(&peer->srcadr), lfptoa(offset, 6),
+ fptoa(delay, 5), ufptoa(dispersion, 5));
+#endif
+
+ /*
+ * The authentication and access-control machinery works, but
+ * its utility may be questionable.
+ */
+ restrict = restrictions(&peer->srcadr);
+ if (restrict & (RES_IGNORE|RES_DONTSERVE))
+ return;
+ peer->processed++;
+ peer->timereceived = current_time;
+ if (restrict & RES_DONTTRUST)
+ trustable = 0;
+ else
+ trustable = 1;
+
+ if (peer->flags & FLAG_AUTHENABLE) {
+ if (trustable)
+ peer->flags |= FLAG_AUTHENTIC;
+ else
+ peer->flags &= ~FLAG_AUTHENTIC;
+ }
+ peer->leap = leap;
+
+ /*
+ * Set the timestamps. rec and org are in local time, while ref
+ * is in timecode time.
+ */
+ peer->rec = peer->org = *rectime;
+ peer->reftime = *reftime;
+
+ /*
+ * If the interface has been set to any_interface, set it to the
+ * loopback address if we have one. This is so that peers which
+ * are unreachable are easy to see in the peer display.
+ */
+ if (peer->dstadr == any_interface && loopback_interface != 0)
+ peer->dstadr = loopback_interface;
+
+ /*
+ * Set peer.pmode based on the hmode. For appearances only.
+ */
+ switch (peer->hmode) {
+
+ case MODE_ACTIVE:
+ peer->pmode = MODE_PASSIVE;
+ break;
+
+ default:
+ peer->pmode = MODE_SERVER;
+ break;
+ }
+
+ /*
+ * Abandon ship if the radio came bum. We only got this far
+ * in order to make pretty billboards, even if bum.
+ */
+ if (leap == LEAP_NOTINSYNC)
+ return;
+ /*
+ * If this guy was previously unreachable, report him
+ * reachable.
+ */
+ if (peer->reach == 0) report_event(EVNT_REACH, peer);
+ peer->reach |= 1;
+
+ /*
+ * Give the data to the clock filter and update the clock. Note
+ * the clock reading precision initialized by the driver is
+ * added at this point.
+ */
+ precision = FP_SECOND >> -(int)peer->precision;
+ if (precision == 0)
+ precision = 1;
+ refclock_report(peer, CEVNT_NOMINAL);
+ clock_filter(peer, offset, delay, dispersion + precision);
+ clock_update(peer);
+}
+
+
+/*
+ * refclock_gtlin - groom next input line and extract timestamp
+ *
+ * This routine processes the timecode received from the clock and
+ * removes the parity bit and control characters. If a timestamp is
+ * present in the timecode, as produced by the tty_clk line
+ * discipline/streams module, it returns that as the timestamp;
+ * otherwise, it returns the buffer timestamp. The routine return code
+ * is the number of characters in the line.
+ */
+int
+refclock_gtlin(rbufp, lineptr, bmax, tsptr)
+ struct recvbuf *rbufp; /* receive buffer pointer */
+ char *lineptr; /* current line pointer */
+ int bmax; /* remaining characters in line */
+ l_fp *tsptr; /* pointer to timestamp returned */
+{
+ char *dpt, *dpend, *dp;
+ int i;
+ l_fp trtmp, tstmp;
+ char c;
+
+ /*
+ * Check for the presence of a timestamp left by the tty_clock
+ * line discipline/streams module and, if present, use that
+ * instead of the buffer timestamp captured by the I/O routines.
+ * We recognize a timestamp by noting its value is earlier than
+ * the buffer timestamp, but not more than one second earlier.
+ */
+ dpt = (char *)&rbufp->recv_space;
+ dpend = dpt + rbufp->recv_length;
+ trtmp = rbufp->recv_time;
+ if (dpend >= dpt + 8) {
+ if (buftvtots(dpend - 8, &tstmp)) {
+ L_SUB(&trtmp, &tstmp);
+ if (trtmp.l_ui == 0) {
+#ifdef DEBUG
+ if (debug) {
+ printf(
+ "refclock_gtlin: fd %d ldisc %s",
+ rbufp->fd,
+ lfptoa(&trtmp, 6));
+ gettstamp(&trtmp);
+ L_SUB(&trtmp, &tstmp);
+ printf(" sigio %s\n",
+ lfptoa(&trtmp, 6));
+ }
+#endif
+ dpend -= 8;
+ trtmp = tstmp;
+ } else
+ trtmp = rbufp->recv_time;
+ }
+ }
+
+ /*
+ * Edit timecode to remove control chars. Don't monkey with the
+ * line buffer if the input buffer contains no ASCII printing
+ * characters.
+ */
+ if (dpend - dpt > bmax - 1)
+ dpend = dpt + bmax - 1;
+ for (dp = lineptr; dpt < dpend; dpt++) {
+ c = *dpt & 0x7f;
+ if (c >= ' ')
+ *dp++ = c;
+ }
+ i = dp - lineptr;
+ if (i > 0)
+ *dp = '\0';
+
+#ifdef DEBUG
+ if (debug)
+ printf("refclock_gtlin: fd %d time %s timecode %d %s\n",
+ rbufp->fd, ulfptoa(&trtmp, 6), i, lineptr);
+#endif
+ *tsptr = trtmp;
+ return (i);
+}
+
+
+/*
+ * refclock_open - open serial port for reference clock
+ *
+ * This routine opens a serial port for I/O and sets default options. It
+ * returns the file descriptor if success and zero if failure.
+ */
+int
+refclock_open(dev, speed, flags)
+ char *dev; /* device name pointer */
+ int speed; /* serial port speed (code) */
+ int flags; /* line discipline flags */
+{
+ int fd;
+#ifdef HAVE_TERMIOS
+ struct termios ttyb, *ttyp;
+#endif /* HAVE_TERMIOS */
+#ifdef HAVE_SYSV_TTYS
+ struct termio ttyb, *ttyp;
+#endif /* HAVE_SYSV_TTYS */
+#ifdef HAVE_BSD_TTYS
+ struct sgttyb ttyb, *ttyp;
+#endif /* HAVE_BSD_TTYS */
+#ifdef HAVE_MODEM_CONTROL
+ u_long ltemp;
+#endif /* HAVE_MODEM_CONTROL */
+
+ /*
+ * Open serial port and set default options
+ */
+ fd = open(dev, O_RDWR, 0777);
+ if (fd == -1) {
+ syslog(LOG_ERR, "refclock_open: %s: %m", dev);
+ return (0);
+ }
+
+ /*
+ * The following sections initialize the serial line port in
+ * canonical (line-oriented) mode and set the specified line
+ * speed, 8 bits and no parity. The modem control, break, erase
+ * and kill functions are normally disabled. There is a
+ * different section for each terminal interface, as selected at
+ * compile time.
+ */
+ ttyp = &ttyb;
+#ifdef HAVE_TERMIOS
+
+ /*
+ * POSIX serial line parameters (termios interface)
+ */
+ if (tcgetattr(fd, ttyp) < 0) {
+ syslog(LOG_ERR,
+ "refclock_open: fd %d tcgetattr %m", fd);
+ return (0);
+ }
+
+ /*
+ * Set canonical mode and local connection; set specified speed,
+ * 8 bits and no parity; map CR to NL; ignore break.
+ */
+ ttyp->c_iflag = IGNBRK | IGNPAR | ICRNL;
+ ttyp->c_oflag = 0;
+ ttyp->c_cflag = CS8 | CLOCAL | CREAD;
+ (void)cfsetispeed(&ttyb, speed);
+ (void)cfsetospeed(&ttyb, speed);
+ ttyp->c_lflag = ICANON;
+ ttyp->c_cc[VERASE] = ttyp->c_cc[VKILL] = '\0';
+#ifdef HAVE_MODEM_CONTROL
+ /*
+ * If we have modem control, check to see if modem leads are
+ * active; if so, set remote connection. This is necessary for
+ * the kernel pps mods to work.
+ */
+ ltemp = 0;
+ if (ioctl(fd, TIOCMGET, (char *)&ltemp) < 0)
+ syslog(LOG_ERR,
+ "refclock_open: fd %d TIOCMGET %m", fd);
+#if DEBUG
+ if (debug)
+ printf("refclock_open: fd %d modem status %lx\n",
+ fd, ltemp);
+#endif
+ if (ltemp & TIOCM_DSR)
+ ttyp->c_cflag &= ~CLOCAL;
+#endif /* HAVE_MODEM_CONTROL */
+ if (tcsetattr(fd, TCSANOW, ttyp) < 0) {
+ syslog(LOG_ERR,
+ "refclock_open: fd %d tcsetattr %m", fd);
+ return (0);
+ }
+ if (tcflush(fd, TCIOFLUSH) < 0) {
+ syslog(LOG_ERR,
+ "refclock_open: fd %d tcflush %m", fd);
+ return (0);
+ }
+#endif /* HAVE_TERMIOS */
+
+#ifdef HAVE_SYSV_TTYS
+
+ /*
+ * System V serial line parameters (termio interface)
+ *
+ */
+ if (ioctl(fd, TCGETA, ttyp) < 0) {
+ syslog(LOG_ERR,
+ "refclock_open: fd %d TCGETA %m", fd);
+ return (0);
+ }
+
+ /*
+ * Set canonical mode and local connection; set specified speed,
+ * 8 bits and no parity; map CR to NL; ignore break.
+ */
+ ttyp->c_iflag = IGNBRK | IGNPAR | ICRNL;
+ ttyp->c_oflag = 0;
+ ttyp->c_cflag = speed | CS8 | CLOCAL | CREAD;
+ ttyp->c_lflag = ICANON;
+ ttyp->c_cc[VERASE] = ttyp->c_cc[VKILL] = '\0';
+#ifdef HAVE_MODEM_CONTROL
+ /*
+ * If we have modem control, check to see if modem leads are
+ * active; if so, set remote connection. This is necessary for
+ * the kernel pps mods to work.
+ */
+ ltemp = 0;
+ if (ioctl(fd, TIOCMGET, (char *)&ltemp) < 0)
+ syslog(LOG_ERR,
+ "refclock_open: fd %d TIOCMGET %m", fd);
+#if DEBUG
+ if (debug)
+ printf("refclock_open: fd %d modem status %lx\n",
+ fd, ltemp);
+#endif
+ if (ltemp & TIOCM_DSR)
+ ttyp->c_cflag &= ~CLOCAL;
+#endif /* HAVE_MODEM_CONTROL */
+ if (ioctl(fd, TCSETA, ttyp) < 0) {
+ syslog(LOG_ERR,
+ "refclock_open: fd %d TCSETA %m", fd);
+ return (0);
+ }
+#endif /* HAVE_SYSV_TTYS */
+
+#ifdef HAVE_BSD_TTYS
+
+ /*
+ * 4.3bsd serial line parameters (sgttyb interface)
+ */
+ if (ioctl(fd, TIOCGETP, (char *)ttyp) < 0) {
+ syslog(LOG_ERR,
+ "refclock_open: fd %d TIOCGETP %m", fd);
+ return (0);
+ }
+ ttyp->sg_ispeed = ttyp->sg_ospeed = speed;
+ ttyp->sg_flags = EVENP | ODDP | CRMOD;
+ if (ioctl(fd, TIOCSETP, (char *)ttyp) < 0) {
+ syslog(LOG_ERR,
+ "refclock_open: TIOCSETP %m");
+ return (0);
+ }
+#endif /* HAVE_BSD_TTYS */
+
+ if (!refclock_ioctl(fd, flags)) {
+ (void)close(fd);
+ syslog(LOG_ERR, "refclock_open: fd %d ioctl fails",
+ fd);
+ return (0);
+ }
+ return (fd);
+}
+
+
+/*
+ * refclock_ioctl - set serial port control functions
+ *
+ * This routine attempts to hide the internal, system-specific details
+ * of serial ports. It can handle POSIX (termios), SYSV (termio) and BSD
+ * (sgtty) interfaces with varying degrees of success. The routine sets
+ * up the tty_clk, chu_clk and ppsclock streams module/line discipline,
+ * if compiled in the daemon and requested in the call. The routine
+ * returns one if success and zero if failure.
+ */
+int
+refclock_ioctl(fd, flags)
+ int fd; /* file descriptor */
+ int flags; /* line discipline flags */
+{
+#ifdef HAVE_TERMIOS
+ struct termios ttyb, *ttyp;
+#endif /* HAVE_TERMIOS */
+#ifdef HAVE_SYSV_TTYS
+ struct termio ttyb, *ttyp;
+#endif /* HAVE_SYSV_TTYS */
+#ifdef HAVE_BSD_TTYS
+ struct sgttyb ttyb, *ttyp;
+#endif /* HAVE_BSD_TTYS */
+
+#ifdef DEBUG
+ if (debug)
+ printf("refclock_ioctl: fd %d flags %x\n",
+ fd, flags);
+#endif
+
+ /*
+ * The following sections select optional features, such as
+ * modem control, line discipline and so forth. Some require
+ * specific operating system support in the form of streams
+ * modules, which can be loaded and unloaded at run time without
+ * rebooting the kernel, or line discipline modules, which must
+ * be compiled in the kernel. The streams modules require System
+ * V STREAMS support, while the line discipline modules require
+ * 4.3bsd or later. The checking frenzy is attenuated here,
+ * since the device is already open.
+ *
+ * Note that both the clk and ppsclock modules are optional; the
+ * dang thing still works, but the accuracy improvement using
+ * them will not be available. The ppsclock module is associated
+ * with a specific, declared line and should be used only once.
+ * If requested, the chu module is mandatory, since the driver
+ * will not work without it.
+ *
+ * Use the LDISC_PPS option ONLY with Sun baseboard ttya or
+ * ttyb. Using it with the SPIF multipexor crashes the kernel.
+ */
+ if (flags == 0)
+ return (1);
+
+#if !(defined(HAVE_TERMIOS) || defined(HAVE_BSD_TTYS))
+ if (flags & (LDISC_CLK | LDISC_CHU | LDISC_PPS | LDISC_ACTS))
+ syslog(LOG_ERR,
+ "refclock_ioctl: unsupported terminal interface");
+ return (0);
+#endif /* HAVE_TERMIOS HAVE_BSD_TTYS */
+
+ ttyp = &ttyb;
+
+#ifdef STREAM
+#ifdef CLK
+
+ /*
+ * The CLK option provides timestamping at the driver level.
+ * It requires the tty_clk streams module and System V STREAMS
+ * support.
+ */
+ if (flags & (LDISC_CLK | LDISC_CLKPPS | LDISC_ACTS)) {
+ if (ioctl(fd, I_PUSH, "clk") < 0)
+ syslog(LOG_NOTICE,
+ "refclock_ioctl: optional clk streams module unavailable");
+ else {
+ char *str;
+
+ if (flags & LDISC_PPS)
+ str = "\377";
+ else if (flags & LDISC_ACTS)
+ str = "*";
+ else
+ str = "\n";
+ if (ioctl(fd, CLK_SETSTR, str) < 0)
+ syslog(LOG_ERR,
+ "refclock_ioctl: CLK_SETSTR %m");
+ }
+ }
+
+ /*
+ * The ACTS line discipline requires additional line-ending
+ * character '*'.
+ */
+ if (flags & LDISC_ACTS) {
+ (void)tcgetattr(fd, ttyp);
+ ttyp->c_cc[VEOL] = '*';
+ (void)tcsetattr(fd, TCSANOW, ttyp);
+ }
+#else
+ if (flags & LDISC_CLK)
+ syslog(LOG_NOTICE,
+ "refclock_ioctl: optional clk streams module unsupported");
+#endif /* CLK */
+#ifdef CHU
+
+ /*
+ * The CHU option provides timestamping and decoding for the CHU
+ * timecode. It requires the tty_chu streams module and System V
+ * STREAMS support.
+ */
+ if (flags & LDISC_CHU) {
+ (void)tcgetattr(fd, ttyp);
+ ttyp->c_lflag = 0;
+ ttyp->c_cc[VERASE] = ttyp->c_cc[VKILL] = '\0';
+ ttyp->c_cc[VMIN] = 1;
+ ttyp->c_cc[VTIME] = 0;
+ (void)tcsetattr(fd, TCSANOW, ttyp);
+ (void)tcflush(fd, TCIOFLUSH);
+ while (ioctl(fd, I_POP, 0) >= 0);
+ if (ioctl(fd, I_PUSH, "chu") < 0) {
+ syslog(LOG_ERR,
+ "refclock_ioctl: required chu streams module unavailable");
+ return (0);
+ }
+ }
+#else
+ if (flags & LDISC_CHU) {
+ syslog(LOG_ERR,
+ "refclock_ioctl: required chu streams module unsupported");
+ return (0);
+ }
+#endif /* CHU */
+#ifdef PPS
+
+ /*
+ * The PPS option provides timestamping at the driver level.
+ * It uses a 1-pps signal and level converter (gadget box) and
+ * requires the ppsclock streams module and System V STREAMS
+ * support.
+ */
+ if (flags & LDISC_PPS) {
+ if (fdpps != -1) {
+ syslog(LOG_ERR,
+ "refclock_ioctl: ppsclock already configured");
+ return (0);
+ }
+ if (ioctl(fd, I_PUSH, "ppsclock") < 0)
+ syslog(LOG_NOTICE,
+ "refclock_ioctl: optional ppsclock streams module unavailable");
+ else
+ fdpps = fd;
+ }
+#else
+ if (flags & LDISC_PPS)
+ syslog(LOG_NOTICE,
+ "refclock_ioctl: optional ppsclock streams module unsupported");
+#endif /* PPS */
+
+#else /* STREAM */
+
+#ifdef HAVE_TERMIOS
+#ifdef CLK
+
+ /*
+ * The CLK option provides timestamping at the driver level. It
+ * requires the tty_clk line discipline and 4.3bsd or later.
+ */
+ if (flags & (LDISC_CLK | LDISC_CLKPPS | LDISC_ACTS)) {
+ (void)tcgetattr(fd, ttyp);
+ ttyp->c_lflag = 0;
+ if (flags & LDISC_CLKPPS)
+ ttyp->c_cc[VERASE] = ttyp->c_cc[VKILL] = '\377';
+ else if (flags & LDISC_ACTS) {
+ ttyp->c_cc[VERASE] = '*';
+ ttyp->c_cc[VKILL] = '#';
+ } else
+ ttyp->c_cc[VERASE] = ttyp->c_cc[VKILL] = '\n';
+ ttyp->c_cc[VMIN] = 1;
+ ttyp->c_cc[VTIME] = 0;
+ ttyp->c_line = CLKLDISC;
+ (void)tcsetattr(fd, TCSANOW, ttyp);
+ (void)tcflush(fd, TCIOFLUSH);
+ }
+#else
+ if (flags & LDISC_CLK)
+ syslog(LOG_NOTICE,
+ "refclock_ioctl: optional clk line discipline unsupported");
+#endif /* CLK */
+#ifdef CHU
+ /*
+ * The CHU option provides timestamping and decoding for the CHU
+ * timecode. It requires the tty_chu line disciplne and 4.3bsd
+ * or later.
+ */
+ if (flags & LDISC_CHU) {
+ (void)tcgetattr(fd, ttyp);
+ ttyp->c_lflag = 0;
+ ttyp->c_cc[VERASE] = ttyp->c_cc[VKILL] = '\r';
+ ttyp->c_cc[VMIN] = 1;
+ ttyp->c_cc[VTIME] = 0;
+ ttyp->c_line = CHULDISC;
+ (void)tcsetattr(fd, TCSANOW, ttyp) < 0);
+ (void)tcflush(fd, TCIOFLUSH);
+ }
+#else
+ if (flags & LDISC_CHU) {
+ syslog(LOG_ERR,
+ "refclock_ioctl: required chu line discipline unsupported");
+ return (0);
+ }
+#endif /* CHU */
+#endif /* HAVE_TERMIOS */
+
+#ifdef HAVE_BSD_TTYS
+#ifdef CLK
+
+ /*
+ * The CLK option provides timestamping at the driver level. It
+ * requires the tty_clk line discipline and 4.3bsd or later.
+ */
+ if (flags & (LDISC_CLK | LDISC_CLKPPS | LDISC_ACTS)) {
+ int ldisc = CLKLDISC;
+
+ (void)ioctl(fd, TIOCGETP, (char *)ttyp);
+ if (flags & LDISC_CLKPPS)
+ ttyp->sg_erase = ttyp->sg_kill = '\377';
+ else if (flags & LDISC_ACTS) {
+ ttyp->sg_erase = '*';
+ ttyp->sg_kill = '#';
+ } else
+ ttyp->sg_erase = ttyp->sg_kill = '\r';
+ ttyp->sg_flags = RAW;
+ (void)ioctl(fd, TIOCSETP, ttyp);
+ if (ioctl(fd, TIOCSETD, (char *)&ldisc) < 0)
+ syslog(LOG_NOTICE,
+ "refclock_ioctl: optional clk line discipline unavailable");
+ }
+#else
+ if (flags & LDISC_CLK)
+ syslog(LOG_NOTICE,
+ "refclock_ioctl: optional clk line discipline unsupported");
+
+#endif /* CLK */
+#ifdef CHU
+
+ /*
+ * The CHU option provides timestamping and decoding for the CHU
+ * timecode. It requires the tty_chu line disciplne and 4.3bsd
+ * or later.
+ */
+ if (flags & LDISC_CHU) {
+ int ldisc = CHULDISC;
+
+ (void)ioctl(fd, TIOCGETP, (char *)ttyp);
+ ttyp->sg_erase = ttyp->sg_kill = '\r';
+ ttyp->sg_flags = RAW;
+ (void)ioctl(fd, TIOCSETP, (char *)ttyp);
+ if (ioctl(fd, TIOCSETD, (char *)&ldisc) < 0) {
+ syslog(LOG_ERR,
+ "refclock_ioctl: required chu line discipline unavailable");
+ return (0);
+ }
+ }
+#else
+ if (flags & LDISC_CHU) {
+ syslog(LOG_ERR,
+ "refclock_ioctl: required chu line discipline unsupported");
+ return (0);
+ }
+#endif /* CHU */
+#endif /* HAVE_BSD_TTYS */
+
+#endif /* STREAM */
+
+ return (1);
+}
+
+
+/*
+ * refclock_control - set and/or return clock values
+ *
+ * This routine is used mainly for debugging. It returns designated
+ * values from the interface structure that can be displayed using
+ * xntpdc and the clockstat command. It can also be used to initialize
+ * configuration variables, such as fudgetimes, fudgevalues, reference
+ * ID and stratum.
+ */
+void
+refclock_control(srcadr, in, out)
+ struct sockaddr_in *srcadr;
+ struct refclockstat *in;
+ struct refclockstat *out;
+{
+ struct peer *peer;
+ struct refclockproc *pp;
+ u_char clktype;
+ int unit;
+
+ /*
+ * Check for valid address and running peer
+ */
+ if (!ISREFCLOCKADR(srcadr))
+ return;
+ clktype = REFCLOCKTYPE(srcadr);
+ unit = REFCLOCKUNIT(srcadr);
+ if (clktype >= num_refclock_conf || unit > MAXUNIT)
+ return;
+ if (!(peer = typeunit[clktype][unit]))
+ return;
+ pp = peer->procptr;
+
+ /*
+ * Initialize requested data
+ */
+ if (in != 0) {
+ if (in->haveflags & CLK_HAVETIME1)
+ pp->fudgetime1 = in->fudgetime1;
+ if (in->haveflags & CLK_HAVETIME2)
+ pp->fudgetime2 = in->fudgetime2;
+ if (in->haveflags & CLK_HAVEVAL1)
+ peer->stratum = in->fudgeval1;
+ if (in->haveflags & CLK_HAVEVAL2)
+ pp->refid = in->fudgeval2;
+ if (peer->stratum <= 1)
+ peer->refid = pp->refid;
+ else
+ peer->refid = peer->srcadr.sin_addr.s_addr;
+ if (in->haveflags & CLK_HAVEFLAG1) {
+ pp->sloppyclockflag &= ~CLK_FLAG1;
+ pp->sloppyclockflag |= in->flags & CLK_FLAG1;
+ }
+ if (in->haveflags & CLK_HAVEFLAG2) {
+ pp->sloppyclockflag &= ~CLK_FLAG2;
+ pp->sloppyclockflag |= in->flags & CLK_FLAG2;
+ }
+ if (in->haveflags & CLK_HAVEFLAG3) {
+ pp->sloppyclockflag &= ~CLK_FLAG3;
+ pp->sloppyclockflag |= in->flags & CLK_FLAG3;
+ }
+ if (in->haveflags & CLK_HAVEFLAG4) {
+ pp->sloppyclockflag &= ~CLK_FLAG4;
+ pp->sloppyclockflag |= in->flags & CLK_FLAG4;
+ }
+ if (in->flags & CLK_FLAG3)
+ (void)refclock_ioctl(pp->io.fd, LDISC_PPS);
+ }
+
+ /*
+ * Readback requested data
+ */
+ if (out != 0) {
+ out->haveflags = CLK_HAVETIME1 | CLK_HAVEVAL1 |
+ CLK_HAVEVAL2 | CLK_HAVEFLAG4;
+ out->fudgetime1 = pp->fudgetime1;
+ out->fudgetime2 = pp->fudgetime2;
+ out->fudgeval1 = peer->stratum;
+ out->fudgeval2 = pp->refid;
+ out->flags = pp->sloppyclockflag;
+
+ out->timereset = current_time - pp->timestarted;
+ out->polls = pp->polls;
+ out->noresponse = pp->noreply;
+ out->badformat = pp->badformat;
+ out->baddata = pp->baddata;
+
+ out->lastevent = pp->lastevent;
+ out->currentstatus = pp->currentstatus;
+ out->type = pp->type;
+ out->clockdesc = pp->clockdesc;
+ out->lencode = pp->lencode;
+ out->lastcode = pp->lastcode;
+ }
+
+ /*
+ * Give the stuff to the clock
+ */
+ if (refclock_conf[clktype]->clock_control != noentry)
+ (refclock_conf[clktype]->clock_control)(unit, in, out);
+}
+
+
+/*
+ * refclock_buginfo - return debugging info
+ *
+ * This routine is used mainly for debugging. It returns designated
+ * values from the interface structure that can be displayed using
+ * xntpdc and the clkbug command.
+ */
+void
+refclock_buginfo(srcadr, bug)
+ struct sockaddr_in *srcadr; /* clock address */
+ struct refclockbug *bug; /* output structure */
+{
+ struct peer *peer;
+ struct refclockproc *pp;
+ u_char clktype;
+ int unit;
+ int i;
+
+ /*
+ * Check for valid address and peer structure
+ */
+ if (!ISREFCLOCKADR(srcadr))
+ return;
+ clktype = REFCLOCKTYPE(srcadr);
+ unit = REFCLOCKUNIT(srcadr);
+ if (clktype >= num_refclock_conf || unit > MAXUNIT)
+ return;
+ if (!(peer = typeunit[clktype][unit]))
+ return;
+ pp = peer->procptr;
+
+ /*
+ * Copy structure values
+ */
+ bug->nvalues = 8;
+ bug->values[0] = pp->year;
+ bug->values[1] = pp->day;
+ bug->values[2] = pp->hour;
+ bug->values[3] = pp->minute;
+ bug->values[4] = pp->second;
+ bug->values[5] = pp->msec;
+ bug->values[6] = pp->yearstart;
+ bug->values[7] = pp->coderecv;
+
+ bug->ntimes = pp->nstages + 3;
+ if (bug->ntimes > NCLKBUGTIMES)
+ bug->ntimes = NCLKBUGTIMES;
+ bug->stimes = 0xfffffffc;
+ bug->times[0] = pp->lastref;
+ bug->times[1] = pp->lastrec;
+ UFPTOLFP(pp->dispersion, &bug->times[2]);
+ for (i = 0; i < bug->ntimes; i++)
+ bug->times[i + 3] = pp->filter[i];
+
+ /*
+ * Give the stuff to the clock
+ */
+ if (refclock_conf[clktype]->clock_buginfo != noentry)
+ (refclock_conf[clktype]->clock_buginfo)(unit, bug);
+}
+
+#endif /* REFCLOCK */
diff --git a/usr.sbin/xntpd/xntpd/ntp_request.c b/usr.sbin/xntpd/xntpd/ntp_request.c
new file mode 100644
index 0000000..9f58d22
--- /dev/null
+++ b/usr.sbin/xntpd/xntpd/ntp_request.c
@@ -0,0 +1,2453 @@
+/*
+ * ntp_request.c - respond to information requests
+ */
+#include <sys/types.h>
+#include <stdio.h>
+#include <errno.h>
+#include <sys/time.h>
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "signal.h"
+#include "ntp_request.h"
+#include "ntp_control.h"
+#include "ntp_refclock.h"
+#include "ntp_if.h"
+#include "ntp_stdlib.h"
+
+#ifdef KERNEL_PLL
+#ifdef HAVE_SYS_TIMEX_H
+#include <sys/timex.h>
+#else
+#include "sys/timex.h"
+#endif
+
+#ifndef NTP_SYSCALLS_LIBC
+#define ntp_gettime(t) syscall(SYS_ntp_gettime, (t))
+#define ntp_adjtime(t) syscall(SYS_ntp_adjtime, (t))
+#endif
+#endif /* KERNEL_PLL */
+
+/*
+ * Structure to hold request procedure information
+ */
+#define NOAUTH 0
+#define AUTH 1
+
+#define NO_REQUEST (-1)
+
+struct req_proc {
+ short request_code; /* defined request code */
+ short needs_auth; /* true when authentication needed */
+ short sizeofitem; /* size of request data item */
+ void (*handler)(); /* routine to handle request */
+};
+
+/*
+ * Universal request codes
+ */
+static struct req_proc univ_codes[] = {
+ { NO_REQUEST, NOAUTH, 0, 0 }
+};
+
+static void req_ack P((struct sockaddr_in *, struct interface *, struct req_pkt *, int));
+static char * prepare_pkt P((struct sockaddr_in *, struct interface *, struct req_pkt *, u_int));
+static char * more_pkt P((void));
+static void flush_pkt P((void));
+static void peer_list P((struct sockaddr_in *, struct interface *, struct req_pkt *));
+static void peer_list_sum P((struct sockaddr_in *, struct interface *, struct req_pkt *));
+static void peer_info P((struct sockaddr_in *, struct interface *, struct req_pkt *));
+static void peer_stats P((struct sockaddr_in *, struct interface *, struct req_pkt *));
+static void sys_info P((struct sockaddr_in *, struct interface *, struct req_pkt *));
+static void sys_stats P((struct sockaddr_in *, struct interface *, struct req_pkt *));
+static void mem_stats P((struct sockaddr_in *, struct interface *, struct req_pkt *));
+static void io_stats P((struct sockaddr_in *, struct interface *, struct req_pkt *));
+static void timer_stats P((struct sockaddr_in *, struct interface *, struct req_pkt *));
+static void loop_info P((struct sockaddr_in *, struct interface *, struct req_pkt *));
+static void do_conf P((struct sockaddr_in *, struct interface *, struct req_pkt *));
+static void do_unconf P((struct sockaddr_in *, struct interface *, struct req_pkt *));
+static void set_sys_flag P((struct sockaddr_in *, struct interface *, struct req_pkt *));
+static void clr_sys_flag P((struct sockaddr_in *, struct interface *, struct req_pkt *));
+static void setclr_flags P((struct sockaddr_in *, struct interface *, struct req_pkt *, u_long));
+static void do_monitor P((struct sockaddr_in *, struct interface *, struct req_pkt *));
+static void do_nomonitor P((struct sockaddr_in *, struct interface *, struct req_pkt *));
+static void list_restrict P((struct sockaddr_in *, struct interface *, struct req_pkt *));
+static void do_resaddflags P((struct sockaddr_in *, struct interface *, struct req_pkt *));
+static void do_ressubflags P((struct sockaddr_in *, struct interface *, struct req_pkt *));
+static void do_unrestrict P((struct sockaddr_in *, struct interface *, struct req_pkt *));
+static void do_restrict P((struct sockaddr_in *, struct interface *, struct req_pkt *, int));
+static void mon_getlist_0 P((struct sockaddr_in *, struct interface *, struct req_pkt *));
+static void mon_getlist_1 P((struct sockaddr_in *, struct interface *, struct req_pkt *));
+static void reset_stats P((struct sockaddr_in *, struct interface *, struct req_pkt *));
+static void reset_peer P((struct sockaddr_in *, struct interface *, struct req_pkt *));
+static void do_key_reread P((struct sockaddr_in *, struct interface *, struct req_pkt *));
+static void do_dirty_hack P((struct sockaddr_in *, struct interface *, struct req_pkt *));
+static void dont_dirty_hack P((struct sockaddr_in *, struct interface *, struct req_pkt *));
+static void trust_key P((struct sockaddr_in *, struct interface *, struct req_pkt *));
+static void untrust_key P((struct sockaddr_in *, struct interface *, struct req_pkt *));
+static void do_trustkey P((struct sockaddr_in *, struct interface *, struct req_pkt *, int));
+static void get_auth_info P((struct sockaddr_in *, struct interface *, struct req_pkt *));
+static void reset_auth_stats P((void));
+static void req_get_traps P((struct sockaddr_in *, struct interface *, struct req_pkt *));
+static void req_set_trap P((struct sockaddr_in *, struct interface *, struct req_pkt *));
+static void req_clr_trap P((struct sockaddr_in *, struct interface *, struct req_pkt *));
+static void do_setclr_trap P((struct sockaddr_in *, struct interface *, struct req_pkt *, int));
+static void set_request_keyid P((struct sockaddr_in *, struct interface *, struct req_pkt *));
+static void set_control_keyid P((struct sockaddr_in *, struct interface *, struct req_pkt *));
+static void get_ctl_stats P((struct sockaddr_in *, struct interface *, struct req_pkt *));
+static void get_leap_info P((struct sockaddr_in *, struct interface *, struct req_pkt *));
+#ifdef KERNEL_PLL
+static void get_kernel_info P((struct sockaddr_in *, struct interface *, struct req_pkt *));
+#endif /* KERNEL_PLL */
+#ifdef REFCLOCK
+static void get_clock_info P((struct sockaddr_in *, struct interface *, struct req_pkt *));
+static void set_clock_fudge P((struct sockaddr_in *, struct interface *, struct req_pkt *));
+#endif /* REFCLOCK */
+static void set_precision P((struct sockaddr_in *, struct interface *, struct req_pkt *));
+#ifdef REFCLOCK
+static void get_clkbug_info P((struct sockaddr_in *, struct interface *, struct req_pkt *));
+#endif /* REFCLOCK */
+
+/*
+ * Xntpd request codes
+ */
+static struct req_proc xntp_codes[] = {
+ { REQ_PEER_LIST, NOAUTH, 0, peer_list },
+ { REQ_PEER_LIST_SUM, NOAUTH, 0, peer_list_sum },
+ { REQ_PEER_INFO, NOAUTH, sizeof(struct info_peer_list), peer_info },
+ { REQ_PEER_STATS, NOAUTH, sizeof(struct info_peer_list), peer_stats },
+ { REQ_SYS_INFO, NOAUTH, 0, sys_info },
+ { REQ_SYS_STATS, NOAUTH, 0, sys_stats },
+ { REQ_IO_STATS, NOAUTH, 0, io_stats },
+ { REQ_MEM_STATS, NOAUTH, 0, mem_stats },
+ { REQ_LOOP_INFO, NOAUTH, 0, loop_info },
+ { REQ_TIMER_STATS, NOAUTH, 0, timer_stats },
+ { REQ_CONFIG, AUTH, sizeof(struct conf_peer), do_conf },
+ { REQ_UNCONFIG, AUTH, sizeof(struct conf_unpeer), do_unconf },
+ { REQ_SET_SYS_FLAG, AUTH, sizeof(struct conf_sys_flags), set_sys_flag },
+ { REQ_CLR_SYS_FLAG, AUTH, sizeof(struct conf_sys_flags), clr_sys_flag },
+ { REQ_MONITOR, AUTH, 0, do_monitor },
+ { REQ_NOMONITOR, AUTH, 0, do_nomonitor },
+ { REQ_GET_RESTRICT, NOAUTH, 0, list_restrict },
+ { REQ_RESADDFLAGS, AUTH, sizeof(struct conf_restrict), do_resaddflags },
+ { REQ_RESSUBFLAGS, AUTH, sizeof(struct conf_restrict), do_ressubflags },
+ { REQ_UNRESTRICT, AUTH, sizeof(struct conf_restrict), do_unrestrict },
+ { REQ_MON_GETLIST, NOAUTH, 0, mon_getlist_0 },
+ { REQ_MON_GETLIST_1, NOAUTH, 0, mon_getlist_1 },
+ { REQ_RESET_STATS, AUTH, sizeof(struct reset_flags), reset_stats },
+ { REQ_RESET_PEER, AUTH, sizeof(struct conf_unpeer), reset_peer },
+ { REQ_REREAD_KEYS, AUTH, 0, do_key_reread },
+ { REQ_DO_DIRTY_HACK, AUTH, 0, do_dirty_hack },
+ { REQ_DONT_DIRTY_HACK, AUTH, 0, dont_dirty_hack },
+ { REQ_TRUSTKEY, AUTH, sizeof(u_long), trust_key },
+ { REQ_UNTRUSTKEY, AUTH, sizeof(u_long), untrust_key },
+ { REQ_AUTHINFO, NOAUTH, 0, get_auth_info },
+ { REQ_TRAPS, NOAUTH, 0, req_get_traps },
+ { REQ_ADD_TRAP, AUTH, sizeof(struct conf_trap), req_set_trap },
+ { REQ_CLR_TRAP, AUTH, sizeof(struct conf_trap), req_clr_trap },
+ { REQ_REQUEST_KEY, AUTH, sizeof(u_long), set_request_keyid },
+ { REQ_CONTROL_KEY, AUTH, sizeof(u_long), set_control_keyid },
+ { REQ_GET_CTLSTATS, NOAUTH, 0, get_ctl_stats },
+ { REQ_GET_LEAPINFO, NOAUTH, 0, get_leap_info },
+ { REQ_SET_PRECISION, AUTH, sizeof(long), set_precision },
+#ifdef KERNEL_PLL
+ { REQ_GET_KERNEL, NOAUTH, 0, get_kernel_info },
+#endif /* KERNEL_PLL */
+#ifdef REFCLOCK
+ { REQ_GET_CLOCKINFO, NOAUTH, sizeof(U_LONG), get_clock_info },
+ { REQ_SET_CLKFUDGE, AUTH, sizeof(struct conf_fudge), set_clock_fudge },
+ { REQ_GET_CLKBUGINFO, NOAUTH, sizeof(U_LONG), get_clkbug_info },
+#endif
+ { NO_REQUEST, NOAUTH, 0, 0 }
+};
+
+
+/*
+ * Authentication keyid used to authenticate requests. Zero means we
+ * don't allow writing anything.
+ */
+u_long info_auth_keyid;
+
+
+/*
+ * Statistic counters to keep track of requests and responses.
+ */
+u_long numrequests; /* number of requests we've received */
+u_long numresppkts; /* number of resp packets sent with data */
+
+u_long errorcounter[INFO_ERR_AUTH+1]; /* lazy way to count errors, indexed */
+ /* by the error code */
+
+#if defined(KERNEL_PLL) && !defined(NTP_SYSCALLS_LIBC)
+extern int syscall P((int, void *, ...));
+#endif /* KERNEL_PLL */
+
+/*
+ * Imported from the I/O module
+ */
+extern struct interface *any_interface;
+
+/*
+ * Imported from the main routines
+ */
+extern int debug;
+
+/*
+ * Imported from the timer module
+ */
+extern u_long current_time;
+
+/*
+ * Imported from ntp_loopfilter.c
+ */
+extern int pll_control;
+extern int pll_enable;
+extern int pps_control;
+
+/*
+ * Imported from ntp_monitor.c
+ */
+extern int mon_enabled;
+
+/*
+ * Imported from ntp_util.c
+ */
+extern int stats_control;
+
+extern struct peer *peer_hash[];
+extern struct peer *sys_peer;
+
+/*
+ * A hack. To keep the authentication module clear of xntp-ism's, we
+ * include a time reset variable for its stats here.
+ */
+static u_long auth_timereset;
+
+/*
+ * Response packet used by these routines. Also some state information
+ * so that we can handle packet formatting within a common set of
+ * subroutines. Note we try to enter data in place whenever possible,
+ * but the need to set the more bit correctly means we occasionally
+ * use the extra buffer and copy.
+ */
+static struct resp_pkt rpkt;
+static int seqno;
+static int nitems;
+static int itemsize;
+static int databytes;
+static char exbuf[RESP_DATA_SIZE];
+static int usingexbuf;
+static struct sockaddr_in *toaddr;
+static struct interface *frominter;
+
+/*
+ * init_request - initialize request data
+ */
+void
+init_request()
+{
+ int i;
+
+ numrequests = 0;
+ numresppkts = 0;
+ auth_timereset = 0;
+ info_auth_keyid = 0; /* by default, can't do this */
+
+ for (i = 0; i < sizeof(errorcounter)/sizeof(errorcounter[0]); i++)
+ errorcounter[i] = 0;
+}
+
+
+/*
+ * req_ack - acknowledge request with no data
+ */
+static void
+req_ack(srcadr, inter, inpkt, errcode)
+ struct sockaddr_in *srcadr;
+ struct interface *inter;
+ struct req_pkt *inpkt;
+ int errcode;
+{
+ /*
+ * fill in the fields
+ */
+ rpkt.rm_vn_mode = RM_VN_MODE(RESP_BIT, 0);
+ rpkt.auth_seq = AUTH_SEQ(0, 0);
+ rpkt.implementation = inpkt->implementation;
+ rpkt.request = inpkt->request;
+ rpkt.err_nitems = ERR_NITEMS(errcode, 0);
+ rpkt.mbz_itemsize = MBZ_ITEMSIZE(0);
+
+ /*
+ * send packet and bump counters
+ */
+ sendpkt(srcadr, inter, -1, (struct pkt *)&rpkt, RESP_HEADER_SIZE);
+ errorcounter[errcode]++;
+}
+
+
+/*
+ * prepare_pkt - prepare response packet for transmission, return pointer
+ * to storage for data item.
+ */
+static char *
+prepare_pkt(srcadr, inter, pkt, structsize)
+ struct sockaddr_in *srcadr;
+ struct interface *inter;
+ struct req_pkt *pkt;
+ u_int structsize;
+{
+#ifdef DEBUG
+ if (debug > 3)
+ printf("request: preparing pkt\n");
+#endif
+
+ /*
+ * Fill in the implementation, reqest and itemsize fields
+ * since these won't change.
+ */
+ rpkt.implementation = pkt->implementation;
+ rpkt.request = pkt->request;
+ rpkt.mbz_itemsize = MBZ_ITEMSIZE(structsize);
+
+ /*
+ * Compute the static data needed to carry on.
+ */
+ toaddr = srcadr;
+ frominter = inter;
+ seqno = 0;
+ nitems = 0;
+ itemsize = structsize;
+ databytes = 0;
+ usingexbuf = 0;
+
+ /*
+ * return the beginning of the packet buffer.
+ */
+ return &rpkt.data[0];
+}
+
+
+/*
+ * more_pkt - return a data pointer for a new item.
+ */
+static char *
+more_pkt()
+{
+ /*
+ * If we were using the extra buffer, send the packet.
+ */
+ if (usingexbuf) {
+#ifdef DEBUG
+ if (debug > 2)
+ printf("request: sending pkt\n");
+#endif
+ rpkt.rm_vn_mode = RM_VN_MODE(RESP_BIT, MORE_BIT);
+ rpkt.auth_seq = AUTH_SEQ(0, seqno);
+ rpkt.err_nitems = htons((u_short)nitems);
+ sendpkt(toaddr, frominter, -1, (struct pkt *)&rpkt,
+ RESP_HEADER_SIZE+databytes);
+ numresppkts++;
+
+ /*
+ * Copy data out of exbuf into the packet.
+ */
+ memmove(&rpkt.data[0], exbuf, itemsize);
+ seqno++;
+ databytes = 0;
+ nitems = 0;
+ usingexbuf = 0;
+ }
+
+ databytes += itemsize;
+ nitems++;
+ if (databytes + itemsize <= RESP_DATA_SIZE) {
+#ifdef DEBUG
+ if (debug > 3)
+ printf("request: giving him more data\n");
+#endif
+ /*
+ * More room in packet. Give him the
+ * next address.
+ */
+ return &rpkt.data[databytes];
+ } else {
+ /*
+ * No room in packet. Give him the extra
+ * buffer unless this was the last in the sequence.
+ */
+#ifdef DEBUG
+ if (debug > 3)
+ printf("request: into extra buffer\n");
+#endif
+ if (seqno == MAXSEQ)
+ return (char *)0;
+ else {
+ usingexbuf = 1;
+ return exbuf;
+ }
+ }
+}
+
+
+/*
+ * flush_pkt - we're done, return remaining information.
+ */
+static void
+flush_pkt()
+{
+#ifdef DEBUG
+ if (debug > 2)
+ printf("request: flushing packet, %d items\n", nitems);
+#endif
+ /*
+ * Must send the last packet. If nothing in here and nothing
+ * has been sent, send an error saying no data to be found.
+ */
+ if (seqno == 0 && nitems == 0)
+ req_ack(toaddr, frominter, (struct req_pkt *)&rpkt,
+ INFO_ERR_NODATA);
+ else {
+ rpkt.rm_vn_mode = RM_VN_MODE(RESP_BIT, 0);
+ rpkt.auth_seq = AUTH_SEQ(0, seqno);
+ rpkt.err_nitems = htons((u_short)nitems);
+ sendpkt(toaddr, frominter, -1, (struct pkt *)&rpkt,
+ RESP_HEADER_SIZE+databytes);
+ numresppkts++;
+ }
+}
+
+
+
+/*
+ * process_private - process private mode (7) packets
+ */
+void
+process_private(rbufp, mod_okay)
+ struct recvbuf *rbufp;
+ int mod_okay;
+{
+ struct req_pkt *inpkt;
+ struct sockaddr_in *srcadr;
+ struct interface *inter;
+ struct req_proc *proc;
+
+ /*
+ * Initialize pointers, for convenience
+ */
+ inpkt = (struct req_pkt *)&rbufp->recv_pkt;
+ srcadr = &rbufp->recv_srcadr;
+ inter = rbufp->dstadr;
+
+#ifdef DEBUG
+ if (debug > 2)
+ printf("prepare_pkt: impl %d req %d\n",
+ inpkt->implementation, inpkt->request);
+#endif
+
+ /*
+ * Do some sanity checks on the packet. Return a format
+ * error if it fails.
+ */
+ if (ISRESPONSE(inpkt->rm_vn_mode)
+ || ISMORE(inpkt->rm_vn_mode)
+ || INFO_VERSION(inpkt->rm_vn_mode) > NTP_VERSION
+ || INFO_VERSION(inpkt->rm_vn_mode) < NTP_OLDVERSION
+ || INFO_SEQ(inpkt->auth_seq) != 0
+ || INFO_ERR(inpkt->err_nitems) != 0
+ || INFO_MBZ(inpkt->mbz_itemsize) != 0
+ || rbufp->recv_length > REQ_LEN_MAC
+ || rbufp->recv_length < REQ_LEN_NOMAC) {
+ req_ack(srcadr, inter, inpkt, INFO_ERR_FMT);
+ return;
+ }
+
+
+ /*
+ * Get the appropriate procedure list to search.
+ */
+ if (inpkt->implementation == IMPL_UNIV)
+ proc = univ_codes;
+ else if (inpkt->implementation == IMPL_XNTPD)
+ proc = xntp_codes;
+ else {
+ req_ack(srcadr, inter, inpkt, INFO_ERR_IMPL);
+ return;
+ }
+
+
+ /*
+ * Search the list for the request codes. If it isn't one
+ * we know, return an error.
+ */
+ while (proc->request_code != NO_REQUEST) {
+ if (proc->request_code == (short) inpkt->request)
+ break;
+ proc++;
+ }
+ if (proc->request_code == NO_REQUEST) {
+ req_ack(srcadr, inter, inpkt, INFO_ERR_REQ);
+ return;
+ }
+
+#ifdef DEBUG
+ if (debug > 3)
+ printf("found request in tables\n");
+#endif
+
+ /*
+ * If we need to authenticate, do so. Note that an
+ * authenticatable packet must include a mac field, must
+ * have used key info_auth_keyid and must have included
+ * a time stamp in the appropriate field. The time stamp
+ * must be within INFO_TS_MAXSKEW of the receive
+ * time stamp.
+ */
+ if (proc->needs_auth) {
+ l_fp ftmp;
+
+ /*
+ * If this guy is restricted from doing this, don't let him
+ * If wrong key was used, or packet doesn't have mac, return.
+ */
+ if (!INFO_IS_AUTH(inpkt->auth_seq) || info_auth_keyid == 0
+ || ntohl(inpkt->keyid) != info_auth_keyid) {
+#ifdef DEBUG
+ if (debug > 4)
+ printf(
+ "failed auth %d info_auth_keyid %lu pkt keyid %u\n",
+ INFO_IS_AUTH(inpkt->auth_seq),
+ info_auth_keyid, ntohl(inpkt->keyid));
+#endif
+ req_ack(srcadr, inter, inpkt, INFO_ERR_AUTH);
+ return;
+ }
+ if (rbufp->recv_length > REQ_LEN_MAC) {
+#ifdef DEBUG
+ if (debug > 4)
+ printf("bad pkt length %d\n",
+ rbufp->recv_length);
+#endif
+ req_ack(srcadr, inter, inpkt, INFO_ERR_FMT);
+ return;
+ }
+ if (!mod_okay || !authhavekey(info_auth_keyid)) {
+#ifdef DEBUG
+ if (debug > 4)
+ printf("failed auth mod_okay %d\n", mod_okay);
+#endif
+ req_ack(srcadr, inter, inpkt, INFO_ERR_AUTH);
+ return;
+ }
+
+ /*
+ * calculate absolute time difference between xmit time stamp
+ * and receive time stamp. If too large, too bad.
+ */
+ NTOHL_FP(&inpkt->tstamp, &ftmp);
+ L_SUB(&ftmp, &rbufp->recv_time);
+ if (L_ISNEG(&ftmp))
+ L_NEG(&ftmp);
+
+ if (ftmp.l_ui >= INFO_TS_MAXSKEW_UI) {
+ /*
+ * He's a loser. Tell him.
+ */
+ req_ack(srcadr, inter, inpkt, INFO_ERR_AUTH);
+ return;
+ }
+
+ /*
+ * So far so good. See if decryption works out okay.
+ */
+ if (!authdecrypt(info_auth_keyid, (U_LONG *)inpkt,
+ REQ_LEN_NOMAC)) {
+ req_ack(srcadr, inter, inpkt, INFO_ERR_AUTH);
+ return;
+ }
+ }
+
+ /*
+ * If we need data, check to see if we have some. If we
+ * don't, check to see that there is none (picky, picky).
+ */
+ if (INFO_ITEMSIZE(inpkt->mbz_itemsize) != proc->sizeofitem) {
+ req_ack(srcadr, inter, inpkt, INFO_ERR_FMT);
+ return;
+ }
+ if (proc->sizeofitem != 0)
+ if (proc->sizeofitem*INFO_NITEMS(inpkt->err_nitems)
+ > sizeof(inpkt->data)) {
+ req_ack(srcadr, inter, inpkt, INFO_ERR_FMT);
+ return;
+ }
+
+#ifdef DEBUG
+ if (debug > 3)
+ printf("process_private: all okay, into handler\n");
+#endif
+
+ /*
+ * Packet is okay. Call the handler to send him data.
+ */
+ (proc->handler)(srcadr, inter, inpkt);
+}
+
+
+/*
+ * peer_list - send a list of the peers
+ */
+static void
+peer_list(srcadr, inter, inpkt)
+ struct sockaddr_in *srcadr;
+ struct interface *inter;
+ struct req_pkt *inpkt;
+{
+ register struct info_peer_list *ip;
+ register struct peer *pp;
+ register int i;
+
+ ip = (struct info_peer_list *)prepare_pkt(srcadr, inter, inpkt,
+ sizeof(struct info_peer_list));
+ for (i = 0; i < HASH_SIZE && ip != 0; i++) {
+ pp = peer_hash[i];
+ while (pp != 0 && ip != 0) {
+ ip->address = pp->srcadr.sin_addr.s_addr;
+ ip->port = pp->srcadr.sin_port;
+ ip->hmode = pp->hmode;
+ ip->flags = 0;
+ if (pp->flags & FLAG_CONFIG)
+ ip->flags |= INFO_FLAG_CONFIG;
+ if (pp == sys_peer)
+ ip->flags |= INFO_FLAG_SYSPEER;
+ if (pp->candidate != 0)
+ ip->flags |= INFO_FLAG_SEL_CANDIDATE;
+ if (pp->select != 0)
+ ip->flags |= INFO_FLAG_SHORTLIST;
+ ip = (struct info_peer_list *)more_pkt();
+ pp = pp->next;
+ }
+ }
+ flush_pkt();
+}
+
+
+/*
+ * peer_list_sum - return extended peer list
+ */
+static void
+peer_list_sum(srcadr, inter, inpkt)
+ struct sockaddr_in *srcadr;
+ struct interface *inter;
+ struct req_pkt *inpkt;
+{
+ register struct info_peer_summary *ips;
+ register struct peer *pp;
+ register int i;
+
+#ifdef DEBUG
+ if (debug > 2)
+ printf("wants peer list summary\n");
+#endif
+
+ ips = (struct info_peer_summary *)prepare_pkt(srcadr, inter, inpkt,
+ sizeof(struct info_peer_summary));
+ for (i = 0; i < HASH_SIZE && ips != 0; i++) {
+ pp = peer_hash[i];
+ while (pp != 0 && ips != 0) {
+#ifdef DEBUG
+ if (debug > 3)
+ printf("sum: got one\n");
+#endif
+ ips->dstadr = (pp->processed) ?
+ pp->cast_flags == MDF_BCAST ?
+ pp->dstadr->bcast.sin_addr.s_addr:
+ pp->cast_flags ?
+ pp->dstadr->sin.sin_addr.s_addr ?
+ pp->dstadr->sin.sin_addr.s_addr:
+ pp->dstadr->bcast.sin_addr.s_addr:
+ 1 : 5;
+ ips->srcadr = pp->srcadr.sin_addr.s_addr;
+ ips->srcport = pp->srcadr.sin_port;
+ ips->stratum = pp->stratum;
+ ips->hpoll = pp->hpoll;
+ ips->ppoll = pp->ppoll;
+ ips->reach = pp->reach;
+ ips->flags = 0;
+ if (pp == sys_peer)
+ ips->flags |= INFO_FLAG_SYSPEER;
+ if (pp->flags & FLAG_CONFIG)
+ ips->flags |= INFO_FLAG_CONFIG;
+ if (pp->flags & FLAG_REFCLOCK)
+ ips->flags |= INFO_FLAG_REFCLOCK;
+ if (pp->flags & FLAG_AUTHENABLE)
+ ips->flags |= INFO_FLAG_AUTHENABLE;
+ if (pp->flags & FLAG_PREFER)
+ ips->flags |= INFO_FLAG_PREFER;
+ if (pp->candidate != 0)
+ ips->flags |= INFO_FLAG_SEL_CANDIDATE;
+ if (pp->select != 0)
+ ips->flags |= INFO_FLAG_SHORTLIST;
+ ips->hmode = pp->hmode;
+ ips->delay = HTONS_FP(pp->delay);
+ HTONL_FP(&pp->offset, &ips->offset);
+ ips->dispersion = HTONS_FP(pp->dispersion);
+
+ pp = pp->next;
+ ips = (struct info_peer_summary *)more_pkt();
+ }
+ }
+ flush_pkt();
+}
+
+
+/*
+ * peer_info - send information for one or more peers
+ */
+static void
+peer_info (srcadr, inter, inpkt)
+ struct sockaddr_in *srcadr;
+ struct interface *inter;
+ struct req_pkt *inpkt;
+{
+ register struct info_peer_list *ipl;
+ register struct peer *pp;
+ register struct info_peer *ip;
+ register int items;
+ register int i, j;
+ struct sockaddr_in addr;
+ extern struct peer *sys_peer;
+
+ memset((char *)&addr, 0, sizeof addr);
+ addr.sin_family = AF_INET;
+ items = INFO_NITEMS(inpkt->err_nitems);
+ ipl = (struct info_peer_list *) inpkt->data;
+ ip = (struct info_peer *)prepare_pkt(srcadr, inter, inpkt,
+ sizeof(struct info_peer));
+ while (items-- > 0 && ip != 0) {
+ addr.sin_port = ipl->port;
+ addr.sin_addr.s_addr = ipl->address;
+ ipl++;
+ if ((pp = findexistingpeer(&addr, (struct peer *)0)) == 0)
+ continue;
+ ip->dstadr = (pp->processed) ?
+ pp->cast_flags == MDF_BCAST ?
+ pp->dstadr->bcast.sin_addr.s_addr:
+ pp->cast_flags ?
+ pp->dstadr->sin.sin_addr.s_addr ?
+ pp->dstadr->sin.sin_addr.s_addr:
+ pp->dstadr->bcast.sin_addr.s_addr:
+ 2 : 6;
+ ip->srcadr = NSRCADR(&pp->srcadr);
+ ip->srcport = NSRCPORT(&pp->srcadr);
+ ip->flags = 0;
+ if (pp == sys_peer)
+ ip->flags |= INFO_FLAG_SYSPEER;
+ if (pp->flags & FLAG_CONFIG)
+ ip->flags |= INFO_FLAG_CONFIG;
+ if (pp->flags & FLAG_REFCLOCK)
+ ip->flags |= INFO_FLAG_REFCLOCK;
+ if (pp->flags & FLAG_AUTHENABLE)
+ ip->flags |= INFO_FLAG_AUTHENABLE;
+ if (pp->flags & FLAG_PREFER)
+ ip->flags |= INFO_FLAG_PREFER;
+ if (pp->candidate != 0)
+ ip->flags |= INFO_FLAG_SEL_CANDIDATE;
+ if (pp->select != 0)
+ ip->flags |= INFO_FLAG_SHORTLIST;
+ ip->leap = pp->leap;
+ ip->hmode = pp->hmode;
+ ip->keyid = pp->keyid;
+ ip->pkeyid = pp->pkeyid;
+ ip->stratum = pp->stratum;
+ ip->ppoll = pp->ppoll;
+ ip->hpoll = pp->hpoll;
+ ip->precision = pp->precision;
+ ip->version = pp->version;
+ ip->valid = pp->valid;
+ ip->reach = pp->reach;
+ ip->unreach = pp->unreach;
+ ip->flash = pp->flash;
+ ip->estbdelay = HTONS_FP(pp->estbdelay);
+ ip->ttl = pp->ttl;
+ ip->associd = htons(pp->associd);
+ ip->rootdelay = HTONS_FP(pp->rootdelay);
+ ip->rootdispersion = HTONS_FP(pp->rootdispersion);
+ ip->refid = pp->refid;
+ ip->timer = htonl(pp->event_timer.event_time - current_time);
+ HTONL_FP(&pp->reftime, &ip->reftime);
+ HTONL_FP(&pp->org, &ip->org);
+ HTONL_FP(&pp->rec, &ip->rec);
+ HTONL_FP(&pp->xmt, &ip->xmt);
+ j = pp->filter_nextpt - 1;
+ for (i = 0; i < NTP_SHIFT; i++, j--) {
+ if (j < 0)
+ j = NTP_SHIFT-1;
+ ip->filtdelay[i] = HTONS_FP(pp->filter_delay[j]);
+ HTONL_FP(&pp->filter_offset[j], &ip->filtoffset[i]);
+ ip->order[i] = (pp->filter_nextpt+NTP_SHIFT-1)
+ - pp->filter_order[i];
+ if (ip->order[i] >= NTP_SHIFT)
+ ip->order[i] -= NTP_SHIFT;
+ }
+ HTONL_FP(&pp->offset, &ip->offset);
+ ip->delay = HTONS_FP(pp->delay);
+ ip->dispersion = HTONS_FP(pp->dispersion);
+ ip->selectdisp = HTONS_FP(pp->selectdisp);
+ ip = (struct info_peer *)more_pkt();
+ }
+ flush_pkt();
+}
+
+
+/*
+ * peer_stats - send statistics for one or more peers
+ */
+static void
+peer_stats (srcadr, inter, inpkt)
+ struct sockaddr_in *srcadr;
+ struct interface *inter;
+ struct req_pkt *inpkt;
+{
+ register struct info_peer_list *ipl;
+ register struct peer *pp;
+ register struct info_peer_stats *ip;
+ register int items;
+ struct sockaddr_in addr;
+ extern struct peer *sys_peer;
+
+ memset((char *)&addr, 0, sizeof addr);
+ addr.sin_family = AF_INET;
+ items = INFO_NITEMS(inpkt->err_nitems);
+ ipl = (struct info_peer_list *) inpkt->data;
+ ip = (struct info_peer_stats *)prepare_pkt(srcadr, inter, inpkt,
+ sizeof(struct info_peer_stats));
+ while (items-- > 0 && ip != 0) {
+ addr.sin_port = ipl->port;
+ addr.sin_addr.s_addr = ipl->address;
+ ipl++;
+ if ((pp = findexistingpeer(&addr, (struct peer *)0)) == 0)
+ continue;
+ ip->dstadr = (pp->processed) ?
+ pp->cast_flags == MDF_BCAST ?
+ pp->dstadr->bcast.sin_addr.s_addr:
+ pp->cast_flags ?
+ pp->dstadr->sin.sin_addr.s_addr ?
+ pp->dstadr->sin.sin_addr.s_addr:
+ pp->dstadr->bcast.sin_addr.s_addr:
+ 3 : 7;
+ ip->srcadr = NSRCADR(&pp->srcadr);
+ ip->srcport = NSRCPORT(&pp->srcadr);
+ ip->flags = 0;
+ if (pp == sys_peer)
+ ip->flags |= INFO_FLAG_SYSPEER;
+ if (pp->flags & FLAG_CONFIG)
+ ip->flags |= INFO_FLAG_CONFIG;
+ if (pp->flags & FLAG_REFCLOCK)
+ ip->flags |= INFO_FLAG_REFCLOCK;
+ if (pp->flags & FLAG_AUTHENABLE)
+ ip->flags |= INFO_FLAG_AUTHENABLE;
+ if (pp->flags & FLAG_PREFER)
+ ip->flags |= INFO_FLAG_PREFER;
+ if (pp->candidate != 0)
+ ip->flags |= INFO_FLAG_SEL_CANDIDATE;
+ if (pp->select != 0)
+ ip->flags |= INFO_FLAG_SHORTLIST;
+ ip->timereceived = htonl(current_time - pp->timereceived);
+ ip->timetosend
+ = htonl(pp->event_timer.event_time - current_time);
+ ip->timereachable = htonl(current_time - pp->timereachable);
+ ip->sent = htonl(pp->sent);
+ ip->processed = htonl(pp->processed);
+ ip->badauth = htonl(pp->badauth);
+ ip->bogusorg = htonl(pp->bogusorg);
+ ip->oldpkt = htonl(pp->oldpkt);
+ ip->seldisp = htonl(pp->seldisptoolarge);
+ ip->selbroken = htonl(pp->selbroken);
+ ip->candidate = pp->candidate;
+ ip = (struct info_peer_stats *)more_pkt();
+ }
+ flush_pkt();
+}
+
+
+/*
+ * sys_info - return system info
+ */
+static void
+sys_info(srcadr, inter, inpkt)
+ struct sockaddr_in *srcadr;
+ struct interface *inter;
+ struct req_pkt *inpkt;
+{
+ register struct info_sys *is;
+
+ /*
+ * Importations from the protocol module
+ */
+ extern u_char sys_leap;
+ extern u_char sys_stratum;
+ extern s_char sys_precision;
+ extern s_fp sys_rootdelay;
+ extern u_fp sys_rootdispersion;
+ extern u_long sys_refid;
+ extern l_fp sys_reftime;
+ extern u_char sys_poll;
+ extern struct peer *sys_peer;
+ extern int sys_bclient;
+ extern s_fp sys_bdelay;
+ extern int sys_authenticate;
+ extern u_long sys_authdelay;
+ extern u_fp clock_stability;
+ extern s_fp clock_frequency;
+
+ is = (struct info_sys *)prepare_pkt(srcadr, inter, inpkt,
+ sizeof(struct info_sys));
+
+ if (sys_peer != 0) {
+ is->peer = NSRCADR(&sys_peer->srcadr);
+ is->peer_mode = sys_peer->hmode;
+ } else {
+ is->peer = 0;
+ is->peer_mode = 0;
+ }
+ is->leap = sys_leap;
+ is->stratum = sys_stratum;
+ is->precision = sys_precision;
+ is->rootdelay = htonl(sys_rootdelay);
+ is->rootdispersion = htonl(sys_rootdispersion);
+ is->frequency = htonl(clock_frequency);
+ is->stability = htonl(clock_stability);
+ is->refid = sys_refid;
+ HTONL_FP(&sys_reftime, &is->reftime);
+
+ is->poll = sys_poll;
+
+ is->flags = 0;
+ if (sys_bclient)
+ is->flags |= INFO_FLAG_BCLIENT;
+ if (sys_authenticate)
+ is->flags |= INFO_FLAG_AUTHENTICATE;
+ if (pll_enable)
+ is->flags |= INFO_FLAG_PLL;
+ if (pll_control)
+ is->flags |= INFO_FLAG_PLL_SYNC;
+ if (pps_control)
+ is->flags |= INFO_FLAG_PPS_SYNC;
+ if (mon_enabled != MON_OFF)
+ is->flags |= INFO_FLAG_MONITOR;
+ if (stats_control)
+ is->flags |= INFO_FLAG_FILEGEN;
+ is->bdelay = HTONS_FP(sys_bdelay);
+ HTONL_UF(sys_authdelay, &is->authdelay);
+
+ (void) more_pkt();
+ flush_pkt();
+}
+
+
+/*
+ * sys_stats - return system statistics
+ */
+static void
+sys_stats(srcadr, inter, inpkt)
+ struct sockaddr_in *srcadr;
+ struct interface *inter;
+ struct req_pkt *inpkt;
+{
+ register struct info_sys_stats *ss;
+
+ /*
+ * Importations from the protocol module
+ */
+ extern u_long sys_stattime;
+ extern u_long sys_badstratum;
+ extern u_long sys_oldversionpkt;
+ extern u_long sys_newversionpkt;
+ extern u_long sys_unknownversion;
+ extern u_long sys_badlength;
+ extern u_long sys_processed;
+ extern u_long sys_badauth;
+ extern u_long sys_limitrejected;
+
+ ss = (struct info_sys_stats *)prepare_pkt(srcadr, inter, inpkt,
+ sizeof(struct info_sys_stats));
+
+ ss->timeup = htonl(current_time);
+ ss->timereset = htonl(current_time - sys_stattime);
+ ss->badstratum = htonl(sys_badstratum);
+ ss->oldversionpkt = htonl(sys_oldversionpkt);
+ ss->newversionpkt = htonl(sys_newversionpkt);
+ ss->unknownversion = htonl(sys_unknownversion);
+ ss->badlength = htonl(sys_badlength);
+ ss->processed = htonl(sys_processed);
+ ss->badauth = htonl(sys_badauth);
+ ss->limitrejected = htonl(sys_limitrejected);
+ (void) more_pkt();
+ flush_pkt();
+}
+
+
+/*
+ * mem_stats - return memory statistics
+ */
+static void
+mem_stats(srcadr, inter, inpkt)
+ struct sockaddr_in *srcadr;
+ struct interface *inter;
+ struct req_pkt *inpkt;
+{
+ register struct info_mem_stats *ms;
+ register int i;
+
+ /*
+ * Importations from the peer module
+ */
+ extern int peer_hash_count[HASH_SIZE];
+ extern int peer_free_count;
+ extern u_long peer_timereset;
+ extern u_long findpeer_calls;
+ extern u_long peer_allocations;
+ extern u_long peer_demobilizations;
+ extern int total_peer_structs;
+
+ ms = (struct info_mem_stats *)prepare_pkt(srcadr, inter, inpkt,
+ sizeof(struct info_mem_stats));
+
+ ms->timereset = htonl(current_time - peer_timereset);
+ ms->totalpeermem = htons((u_short)total_peer_structs);
+ ms->freepeermem = htons((u_short)peer_free_count);
+ ms->findpeer_calls = htonl(findpeer_calls);
+ ms->allocations = htonl(peer_allocations);
+ ms->demobilizations = htonl(peer_demobilizations);
+
+ for (i = 0; i < HASH_SIZE; i++) {
+ if (peer_hash_count[i] > 255)
+ ms->hashcount[i] = 255;
+ else
+ ms->hashcount[i] = (u_char)peer_hash_count[i];
+ }
+
+ (void) more_pkt();
+ flush_pkt();
+}
+
+
+/*
+ * io_stats - return io statistics
+ */
+static void
+io_stats(srcadr, inter, inpkt)
+ struct sockaddr_in *srcadr;
+ struct interface *inter;
+ struct req_pkt *inpkt;
+{
+ register struct info_io_stats *io;
+
+ /*
+ * Importations from the io module
+ */
+ extern u_long io_timereset;
+ extern u_long full_recvbufs;
+ extern u_long free_recvbufs;
+ extern u_long total_recvbufs;
+ extern u_long lowater_additions;
+ extern u_long packets_dropped;
+ extern u_long packets_ignored;
+ extern u_long packets_received;
+ extern u_long packets_sent;
+ extern u_long packets_notsent;
+ extern u_long handler_calls;
+ extern u_long handler_pkts;
+
+ io = (struct info_io_stats *)prepare_pkt(srcadr, inter, inpkt,
+ sizeof(struct info_io_stats));
+
+ io->timereset = htonl(current_time - io_timereset);
+ io->totalrecvbufs = htons((u_short) total_recvbufs);
+ io->freerecvbufs = htons((u_short) free_recvbufs);
+ io->fullrecvbufs = htons((u_short) full_recvbufs);
+ io->lowwater = htons((u_short) lowater_additions);
+ io->dropped = htonl(packets_dropped);
+ io->ignored = htonl(packets_ignored);
+ io->received = htonl(packets_received);
+ io->sent = htonl(packets_sent);
+ io->notsent = htonl(packets_notsent);
+ io->interrupts = htonl(handler_calls);
+ io->int_received = htonl(handler_pkts);
+
+ (void) more_pkt();
+ flush_pkt();
+}
+
+
+/*
+ * timer_stats - return timer statistics
+ */
+static void
+timer_stats(srcadr, inter, inpkt)
+ struct sockaddr_in *srcadr;
+ struct interface *inter;
+ struct req_pkt *inpkt;
+{
+ register struct info_timer_stats *ts;
+
+ /*
+ * Importations from the timer module
+ */
+ extern u_long alarm_overflow;
+ extern u_long timer_timereset;
+ extern u_long timer_overflows;
+ extern u_long timer_xmtcalls;
+
+ ts = (struct info_timer_stats *)prepare_pkt(srcadr, inter, inpkt,
+ sizeof(struct info_timer_stats));
+
+ ts->timereset = htonl(current_time - timer_timereset);
+ ts->alarms = htonl(alarm_overflow);
+ ts->overflows = htonl(timer_overflows);
+ ts->xmtcalls = htonl(timer_xmtcalls);
+
+ (void) more_pkt();
+ flush_pkt();
+}
+
+
+/*
+ * loop_info - return the current state of the loop filter
+ */
+static void
+loop_info(srcadr, inter, inpkt)
+ struct sockaddr_in *srcadr;
+ struct interface *inter;
+ struct req_pkt *inpkt;
+{
+ register struct info_loop *li;
+ l_fp tmp;
+
+ /*
+ * Importations from the loop filter module
+ */
+ extern l_fp last_offset;
+ extern s_fp drift_comp;
+ extern int tc_counter;
+ extern u_long last_time;
+
+ li = (struct info_loop *)prepare_pkt(srcadr, inter, inpkt,
+ sizeof(struct info_loop));
+
+ HTONL_FP(&last_offset, &li->last_offset);
+ FPTOLFP(drift_comp, &tmp);
+ HTONL_FP(&tmp, &li->drift_comp);
+ li->compliance = htonl(tc_counter);
+ li->watchdog_timer = htonl(current_time - last_time);
+
+ (void) more_pkt();
+ flush_pkt();
+}
+
+
+/*
+ * do_conf - add a peer to the configuration list
+ */
+static void
+do_conf(srcadr, inter, inpkt)
+ struct sockaddr_in *srcadr;
+ struct interface *inter;
+ struct req_pkt *inpkt;
+{
+ register struct conf_peer *cp;
+ register int items;
+ struct sockaddr_in peeraddr;
+ int fl;
+
+ /*
+ * Do a check of everything to see that it looks
+ * okay. If not, complain about it. Note we are
+ * very picky here.
+ */
+ items = INFO_NITEMS(inpkt->err_nitems);
+ cp = (struct conf_peer *)inpkt->data;
+
+ fl = 0;
+ while (items-- > 0 && !fl) {
+ if (cp->version > NTP_VERSION
+ || cp->version < NTP_OLDVERSION)
+ fl = 1;
+ if (cp->hmode != MODE_ACTIVE
+ && cp->hmode != MODE_CLIENT
+ && cp->hmode != MODE_BROADCAST)
+ fl = 1;
+ if (cp->flags & ~(CONF_FLAG_AUTHENABLE | CONF_FLAG_PREFER))
+ fl = 1;
+ cp++;
+ }
+
+ if (fl) {
+ req_ack(srcadr, inter, inpkt, INFO_ERR_FMT);
+ return;
+ }
+
+ /*
+ * Looks okay, try it out
+ */
+ items = INFO_NITEMS(inpkt->err_nitems);
+ cp = (struct conf_peer *)inpkt->data;
+ memset((char *)&peeraddr, 0, sizeof(struct sockaddr_in));
+ peeraddr.sin_family = AF_INET;
+ peeraddr.sin_port = htons(NTP_PORT);
+
+ /*
+ * Make sure the address is valid
+ */
+#ifdef REFCLOCK
+ if (!ISREFCLOCKADR(&peeraddr) && ISBADADR(&peeraddr)) {
+#else
+ if (ISBADADR(&peeraddr)) {
+#endif
+ req_ack(srcadr, inter, inpkt, INFO_ERR_FMT);
+ return;
+ }
+
+ while (items-- > 0) {
+ fl = 0;
+ if (cp->flags & CONF_FLAG_AUTHENABLE)
+ fl |= FLAG_AUTHENABLE;
+ if (cp->flags & CONF_FLAG_PREFER)
+ fl |= FLAG_PREFER;
+ peeraddr.sin_addr.s_addr = cp->peeraddr;
+ /* XXX W2DO? minpoll/maxpoll arguments ??? */
+ if (peer_config(&peeraddr, (struct interface *)0,
+ cp->hmode, cp->version, cp->minpoll, cp->maxpoll,
+ fl, cp->ttl, cp->keyid) == 0) {
+ req_ack(srcadr, inter, inpkt, INFO_ERR_NODATA);
+ return;
+ }
+ cp++;
+ }
+
+ req_ack(srcadr, inter, inpkt, INFO_OKAY);
+}
+
+
+/*
+ * do_unconf - remove a peer from the configuration list
+ */
+static void
+do_unconf(srcadr, inter, inpkt)
+ struct sockaddr_in *srcadr;
+ struct interface *inter;
+ struct req_pkt *inpkt;
+{
+ register struct conf_unpeer *cp;
+ register int items;
+ register struct peer *peer;
+ struct sockaddr_in peeraddr;
+ int bad, found;
+
+ /*
+ * This is a bit unstructured, but I like to be careful.
+ * We check to see that every peer exists and is actually
+ * configured. If so, we remove them. If not, we return
+ * an error.
+ */
+ peeraddr.sin_family = AF_INET;
+ peeraddr.sin_port = htons(NTP_PORT);
+
+ items = INFO_NITEMS(inpkt->err_nitems);
+ cp = (struct conf_unpeer *)inpkt->data;
+
+ bad = 0;
+ while (items-- > 0 && !bad) {
+ peeraddr.sin_addr.s_addr = cp->peeraddr;
+ found = 0;
+ peer = (struct peer *)0;
+ while (!found) {
+ peer = findexistingpeer(&peeraddr, peer);
+ if (peer == (struct peer *)0)
+ break;
+ if (peer->flags & FLAG_CONFIG)
+ found = 1;
+ }
+ if (!found)
+ bad = 1;
+ cp++;
+ }
+
+ if (bad) {
+ req_ack(srcadr, inter, inpkt, INFO_ERR_NODATA);
+ return;
+ }
+
+ /*
+ * Now do it in earnest.
+ */
+
+ items = INFO_NITEMS(inpkt->err_nitems);
+ cp = (struct conf_unpeer *)inpkt->data;
+ while (items-- > 0) {
+ peeraddr.sin_addr.s_addr = cp->peeraddr;
+ peer_unconfig(&peeraddr, (struct interface *)0);
+ cp++;
+ }
+
+ req_ack(srcadr, inter, inpkt, INFO_OKAY);
+}
+
+
+/*
+ * set_sys_flag - set system flags
+ */
+static void
+set_sys_flag(srcadr, inter, inpkt)
+ struct sockaddr_in *srcadr;
+ struct interface *inter;
+ struct req_pkt *inpkt;
+{
+ setclr_flags(srcadr, inter, inpkt, 1);
+}
+
+
+/*
+ * clr_sys_flag - clear system flags
+ */
+static void
+clr_sys_flag(srcadr, inter, inpkt)
+ struct sockaddr_in *srcadr;
+ struct interface *inter;
+ struct req_pkt *inpkt;
+{
+ setclr_flags(srcadr, inter, inpkt, 0);
+}
+
+
+/*
+ * setclr_flags - do the grunge work of flag setting/clearing
+ */
+static void
+setclr_flags(srcadr, inter, inpkt, set)
+ struct sockaddr_in *srcadr;
+ struct interface *inter;
+ struct req_pkt *inpkt;
+ u_long set;
+{
+ register u_long flags;
+
+ if (INFO_NITEMS(inpkt->err_nitems) > 1) {
+ req_ack(srcadr, inter, inpkt, INFO_ERR_FMT);
+ return;
+ }
+
+ flags = ((struct conf_sys_flags *)inpkt->data)->flags;
+
+ if (flags & ~(SYS_FLAG_BCLIENT | SYS_FLAG_AUTHENTICATE |
+ SYS_FLAG_PLL | SYS_FLAG_MONITOR |
+ SYS_FLAG_FILEGEN)) {
+ req_ack(srcadr, inter, inpkt, INFO_ERR_FMT);
+ return;
+ }
+
+ if (flags & SYS_FLAG_BCLIENT)
+ proto_config(PROTO_BROADCLIENT, set);
+ if (flags & SYS_FLAG_AUTHENTICATE)
+ proto_config(PROTO_AUTHENTICATE, set);
+ if (flags & SYS_FLAG_PLL)
+ proto_config(PROTO_PLL, set);
+ if (flags & SYS_FLAG_MONITOR)
+ proto_config(PROTO_MONITOR, set);
+ if (flags & SYS_FLAG_FILEGEN)
+ proto_config(PROTO_FILEGEN, set);
+ req_ack(srcadr, inter, inpkt, INFO_OKAY);
+}
+
+
+/*
+ * do_monitor - turn on monitoring
+ */
+static void
+do_monitor(srcadr, inter, inpkt)
+ struct sockaddr_in *srcadr;
+ struct interface *inter;
+ struct req_pkt *inpkt;
+{
+ mon_start(MON_ON);
+ req_ack(srcadr, inter, inpkt, INFO_OKAY);
+}
+
+
+/*
+ * do_nomonitor - turn off monitoring
+ */
+static void
+do_nomonitor(srcadr, inter, inpkt)
+ struct sockaddr_in *srcadr;
+ struct interface *inter;
+ struct req_pkt *inpkt;
+{
+ mon_stop(MON_ON);
+ req_ack(srcadr, inter, inpkt, INFO_OKAY);
+}
+
+
+/*
+ * list_restrict - return the restrict list
+ */
+static void
+list_restrict(srcadr, inter, inpkt)
+ struct sockaddr_in *srcadr;
+ struct interface *inter;
+ struct req_pkt *inpkt;
+{
+ register struct info_restrict *ir;
+ register struct restrictlist *rl;
+ extern struct restrictlist *restrictlist;
+
+#ifdef DEBUG
+ if (debug > 2)
+ printf("wants peer list summary\n");
+#endif
+
+ ir = (struct info_restrict *)prepare_pkt(srcadr, inter, inpkt,
+ sizeof(struct info_restrict));
+ for (rl = restrictlist; rl != 0 && ir != 0; rl = rl->next) {
+ ir->addr = htonl(rl->addr);
+ ir->mask = htonl(rl->mask);
+ ir->count = htonl(rl->count);
+ ir->flags = htons(rl->flags);
+ ir->mflags = htons(rl->mflags);
+ ir = (struct info_restrict *)more_pkt();
+ }
+ flush_pkt();
+}
+
+
+
+/*
+ * do_resaddflags - add flags to a restrict entry (or create one)
+ */
+static void
+do_resaddflags(srcadr, inter, inpkt)
+ struct sockaddr_in *srcadr;
+ struct interface *inter;
+ struct req_pkt *inpkt;
+{
+ do_restrict(srcadr, inter, inpkt, RESTRICT_FLAGS);
+}
+
+
+
+/*
+ * do_ressubflags - remove flags from a restrict entry
+ */
+static void
+do_ressubflags(srcadr, inter, inpkt)
+ struct sockaddr_in *srcadr;
+ struct interface *inter;
+ struct req_pkt *inpkt;
+{
+ do_restrict(srcadr, inter, inpkt, RESTRICT_UNFLAG);
+}
+
+
+/*
+ * do_unrestrict - remove a restrict entry from the list
+ */
+static void
+do_unrestrict(srcadr, inter, inpkt)
+ struct sockaddr_in *srcadr;
+ struct interface *inter;
+ struct req_pkt *inpkt;
+{
+ do_restrict(srcadr, inter, inpkt, RESTRICT_REMOVE);
+}
+
+
+
+
+
+/*
+ * do_restrict - do the dirty stuff of dealing with restrictions
+ */
+static void
+do_restrict(srcadr, inter, inpkt, op)
+ struct sockaddr_in *srcadr;
+ struct interface *inter;
+ struct req_pkt *inpkt;
+ int op;
+{
+ register struct conf_restrict *cr;
+ register int items;
+ struct sockaddr_in matchaddr;
+ struct sockaddr_in matchmask;
+ int bad;
+
+ /*
+ * Do a check of the flags to make sure that only
+ * the NTPPORT flag is set, if any. If not, complain
+ * about it. Note we are very picky here.
+ */
+ items = INFO_NITEMS(inpkt->err_nitems);
+ cr = (struct conf_restrict *)inpkt->data;
+
+ bad = 0;
+ while (items-- > 0 && !bad) {
+ if (cr->mflags & ~(RESM_NTPONLY))
+ bad = 1;
+ if (cr->flags & ~(RES_ALLFLAGS))
+ bad = 1;
+ if (cr->addr == htonl(INADDR_ANY) && cr->mask != htonl(INADDR_ANY))
+ bad = 1;
+ cr++;
+ }
+
+ if (bad) {
+ req_ack(srcadr, inter, inpkt, INFO_ERR_FMT);
+ return;
+ }
+
+ /*
+ * Looks okay, try it out
+ */
+ items = INFO_NITEMS(inpkt->err_nitems);
+ cr = (struct conf_restrict *)inpkt->data;
+ memset((char *)&matchaddr, 0, sizeof(struct sockaddr_in));
+ memset((char *)&matchmask, 0, sizeof(struct sockaddr_in));
+ matchaddr.sin_family = AF_INET;
+ matchmask.sin_family = AF_INET;
+
+ while (items-- > 0) {
+ matchaddr.sin_addr.s_addr = cr->addr;
+ matchmask.sin_addr.s_addr = cr->mask;
+ restrict(op, &matchaddr, &matchmask, cr->mflags,
+ cr->flags);
+ cr++;
+ }
+
+ req_ack(srcadr, inter, inpkt, INFO_OKAY);
+}
+
+
+/*
+ * mon_getlist - return monitor data
+ */
+static void
+mon_getlist_0(srcadr, inter, inpkt)
+ struct sockaddr_in *srcadr;
+ struct interface *inter;
+ struct req_pkt *inpkt;
+{
+ register struct info_monitor *im;
+ register struct mon_data *md;
+ extern struct mon_data mon_mru_list;
+ extern int mon_enabled;
+
+#ifdef DEBUG
+ if (debug > 2)
+ printf("wants monitor 0 list\n");
+#endif
+ if (!mon_enabled) {
+ req_ack(srcadr, inter, inpkt, INFO_ERR_NODATA);
+ return;
+ }
+
+ im = (struct info_monitor *)prepare_pkt(srcadr, inter, inpkt,
+ sizeof(struct info_monitor));
+ for (md = mon_mru_list.mru_next; md != &mon_mru_list && im != 0;
+ md = md->mru_next) {
+ im->lasttime = htonl(current_time - md->lasttime);
+ im->firsttime = htonl(current_time - md->firsttime);
+ if (md->lastdrop)
+ im->lastdrop = htonl(current_time - md->lastdrop);
+ else
+ im->lastdrop = 0;
+ im->count = htonl(md->count);
+ im->addr = md->rmtadr;
+ im->port = md->rmtport;
+ im->mode = md->mode;
+ im->version = md->version;
+ im = (struct info_monitor *)more_pkt();
+ }
+ flush_pkt();
+}
+
+/*
+ * mon_getlist - return monitor data
+ */
+static void
+mon_getlist_1(srcadr, inter, inpkt)
+ struct sockaddr_in *srcadr;
+ struct interface *inter;
+ struct req_pkt *inpkt;
+{
+ register struct info_monitor_1 *im;
+ register struct mon_data *md;
+ extern struct mon_data mon_mru_list;
+ extern int mon_enabled;
+
+#ifdef DEBUG
+ if (debug > 2)
+ printf("wants monitor 1 list\n");
+#endif
+ if (!mon_enabled) {
+ req_ack(srcadr, inter, inpkt, INFO_ERR_NODATA);
+ return;
+ }
+
+ im = (struct info_monitor_1 *)prepare_pkt(srcadr, inter, inpkt,
+ sizeof(struct info_monitor_1));
+ for (md = mon_mru_list.mru_next; md != &mon_mru_list && im != 0;
+ md = md->mru_next) {
+ im->lasttime = htonl(current_time - md->lasttime);
+ im->firsttime = htonl(current_time - md->firsttime);
+ if (md->lastdrop)
+ im->lastdrop = htonl(current_time - md->lastdrop);
+ else
+ im->lastdrop = 0;
+ im->count = htonl(md->count);
+ im->addr = md->rmtadr;
+ im->daddr = md->cast_flags == MDF_BCAST ?
+ md->interface->bcast.sin_addr.s_addr :
+ md->cast_flags ?
+ md->interface->sin.sin_addr.s_addr ?
+ md->interface->sin.sin_addr.s_addr :
+ md->interface->bcast.sin_addr.s_addr :
+ 4;
+ im->flags = md->cast_flags;
+ im->port = md->rmtport;
+ im->mode = md->mode;
+ im->version = md->version;
+ im = (struct info_monitor_1 *)more_pkt();
+ }
+ flush_pkt();
+}
+
+/*
+ * Module entry points and the flags they correspond with
+ */
+struct reset_entry {
+ int flag; /* flag this corresponds to */
+ void (*handler)(); /* routine to handle request */
+};
+
+struct reset_entry reset_entries[] = {
+ { RESET_FLAG_ALLPEERS, peer_all_reset },
+ { RESET_FLAG_IO, io_clr_stats },
+ { RESET_FLAG_SYS, proto_clr_stats },
+ { RESET_FLAG_MEM, peer_clr_stats },
+ { RESET_FLAG_TIMER, timer_clr_stats },
+ { RESET_FLAG_AUTH, reset_auth_stats },
+ { RESET_FLAG_CTL, ctl_clr_stats },
+ { 0, 0 }
+};
+
+/*
+ * reset_stats - reset statistic counters here and there
+ */
+static void
+reset_stats(srcadr, inter, inpkt)
+ struct sockaddr_in *srcadr;
+ struct interface *inter;
+ struct req_pkt *inpkt;
+{
+ u_long flags;
+ struct reset_entry *rent;
+
+ if (INFO_NITEMS(inpkt->err_nitems) > 1) {
+ req_ack(srcadr, inter, inpkt, INFO_ERR_FMT);
+ return;
+ }
+
+ flags = ((struct reset_flags *)inpkt->data)->flags;
+
+ if (flags & ~RESET_ALLFLAGS) {
+ req_ack(srcadr, inter, inpkt, INFO_ERR_FMT);
+ return;
+ }
+
+ for (rent = reset_entries; rent->flag != 0; rent++) {
+ if (flags & rent->flag)
+ (rent->handler)();
+ }
+ req_ack(srcadr, inter, inpkt, INFO_OKAY);
+}
+
+
+/*
+ * reset_peer - clear a peer's statistics
+ */
+static void
+reset_peer(srcadr, inter, inpkt)
+ struct sockaddr_in *srcadr;
+ struct interface *inter;
+ struct req_pkt *inpkt;
+{
+ register struct conf_unpeer *cp;
+ register int items;
+ register struct peer *peer;
+ struct sockaddr_in peeraddr;
+ int bad;
+
+ /*
+ * We check first to see that every peer exists. If not,
+ * we return an error.
+ */
+ peeraddr.sin_family = AF_INET;
+ peeraddr.sin_port = htons(NTP_PORT);
+
+ items = INFO_NITEMS(inpkt->err_nitems);
+ cp = (struct conf_unpeer *)inpkt->data;
+
+ bad = 0;
+ while (items-- > 0 && !bad) {
+ peeraddr.sin_addr.s_addr = cp->peeraddr;
+ peer = findexistingpeer(&peeraddr, (struct peer *)0);
+ if (peer == (struct peer *)0)
+ bad++;
+ cp++;
+ }
+
+ if (bad) {
+ req_ack(srcadr, inter, inpkt, INFO_ERR_NODATA);
+ return;
+ }
+
+ /*
+ * Now do it in earnest.
+ */
+
+ items = INFO_NITEMS(inpkt->err_nitems);
+ cp = (struct conf_unpeer *)inpkt->data;
+ while (items-- > 0) {
+ peeraddr.sin_addr.s_addr = cp->peeraddr;
+ peer = findexistingpeer(&peeraddr, (struct peer *)0);
+ peer_reset(peer);
+ cp++;
+ }
+
+ req_ack(srcadr, inter, inpkt, INFO_OKAY);
+}
+
+
+/*
+ * do_key_reread - reread the encryption key file
+ */
+static void
+do_key_reread(srcadr, inter, inpkt)
+ struct sockaddr_in *srcadr;
+ struct interface *inter;
+ struct req_pkt *inpkt;
+{
+ rereadkeys();
+ req_ack(srcadr, inter, inpkt, INFO_OKAY);
+}
+
+
+/*
+ * do_dirty_hack
+ */
+static void
+do_dirty_hack(srcadr, inter, inpkt)
+ struct sockaddr_in *srcadr;
+ struct interface *inter;
+ struct req_pkt *inpkt;
+{
+ /* historical placeholder */
+ req_ack(srcadr, inter, inpkt, INFO_OKAY);
+}
+
+
+/*
+ * dont_dirty_hack
+ */
+static void
+dont_dirty_hack(srcadr, inter, inpkt)
+ struct sockaddr_in *srcadr;
+ struct interface *inter;
+ struct req_pkt *inpkt;
+{
+ /* historical placeholder */
+ req_ack(srcadr, inter, inpkt, INFO_OKAY);
+}
+
+
+/*
+ * trust_key - make one or more keys trusted
+ */
+static void
+trust_key(srcadr, inter, inpkt)
+ struct sockaddr_in *srcadr;
+ struct interface *inter;
+ struct req_pkt *inpkt;
+{
+ do_trustkey(srcadr, inter, inpkt, 1);
+}
+
+
+/*
+ * untrust_key - make one or more keys untrusted
+ */
+static void
+untrust_key(srcadr, inter, inpkt)
+ struct sockaddr_in *srcadr;
+ struct interface *inter;
+ struct req_pkt *inpkt;
+{
+ do_trustkey(srcadr, inter, inpkt, 0);
+}
+
+
+/*
+ * do_trustkey - make keys either trustable or untrustable
+ */
+static void
+do_trustkey(srcadr, inter, inpkt, trust)
+ struct sockaddr_in *srcadr;
+ struct interface *inter;
+ struct req_pkt *inpkt;
+ int trust;
+{
+ register u_long *kp;
+ register int items;
+
+ items = INFO_NITEMS(inpkt->err_nitems);
+ kp = (u_long *)inpkt->data;
+ while (items-- > 0) {
+ authtrust(*kp, trust);
+ kp++;
+ }
+
+ req_ack(srcadr, inter, inpkt, INFO_OKAY);
+}
+
+
+/*
+ * get_auth_info - return some stats concerning the authentication module
+ */
+static void
+get_auth_info(srcadr, inter, inpkt)
+ struct sockaddr_in *srcadr;
+ struct interface *inter;
+ struct req_pkt *inpkt;
+{
+ register struct info_auth *ia;
+
+ /*
+ * Importations from the authentication module
+ */
+ extern u_long authnumkeys;
+ extern u_long authnumfreekeys;
+ extern u_long authkeylookups;
+ extern u_long authkeynotfound;
+ extern u_long authencryptions;
+ extern u_long authdecryptions;
+ extern u_long authkeyuncached;
+
+ ia = (struct info_auth *)prepare_pkt(srcadr, inter, inpkt,
+ sizeof(struct info_auth));
+
+ ia->numkeys = htonl(authnumkeys);
+ ia->numfreekeys = htonl(authnumfreekeys);
+ ia->keylookups = htonl(authkeylookups);
+ ia->keynotfound = htonl(authkeynotfound);
+ ia->encryptions = htonl(authencryptions);
+ ia->decryptions = htonl(authdecryptions);
+ ia->keyuncached = htonl(authkeyuncached);
+ ia->timereset = htonl(current_time - auth_timereset);
+
+ (void) more_pkt();
+ flush_pkt();
+}
+
+
+
+/*
+ * reset_auth_stats - reset the authentication stat counters. Done here
+ * to keep xntp-isms out of the authentication module
+ */
+static void
+reset_auth_stats()
+{
+ /*
+ * Importations from the authentication module
+ */
+ extern u_long authkeylookups;
+ extern u_long authkeynotfound;
+ extern u_long authencryptions;
+ extern u_long authdecryptions;
+ extern u_long authkeyuncached;
+
+ authkeylookups = 0;
+ authkeynotfound = 0;
+ authencryptions = 0;
+ authdecryptions = 0;
+ authkeyuncached = 0;
+ auth_timereset = current_time;
+}
+
+
+/*
+ * req_get_traps - return information about current trap holders
+ */
+static void
+req_get_traps(srcadr, inter, inpkt)
+ struct sockaddr_in *srcadr;
+ struct interface *inter;
+ struct req_pkt *inpkt;
+{
+ register struct info_trap *it;
+ register struct ctl_trap *tr;
+ register int i;
+
+ /*
+ * Imported from the control module
+ */
+ extern struct ctl_trap ctl_trap[];
+ extern int num_ctl_traps;
+
+ if (num_ctl_traps == 0) {
+ req_ack(srcadr, inter, inpkt, INFO_ERR_NODATA);
+ return;
+ }
+
+ it = (struct info_trap *)prepare_pkt(srcadr, inter, inpkt,
+ sizeof(struct info_trap));
+
+ for (i = 0, tr = ctl_trap; i < CTL_MAXTRAPS; i++, tr++) {
+ if (tr->tr_flags & TRAP_INUSE) {
+ if (tr->tr_localaddr == any_interface)
+ it->local_address = 0;
+ else
+ it->local_address
+ = NSRCADR(&tr->tr_localaddr->sin);
+ it->trap_address = NSRCADR(&tr->tr_addr);
+ it->trap_port = NSRCPORT(&tr->tr_addr);
+ it->sequence = htons(tr->tr_sequence);
+ it->settime = htonl(current_time - tr->tr_settime);
+ it->origtime = htonl(current_time - tr->tr_origtime);
+ it->resets = htonl(tr->tr_resets);
+ it->flags = htonl((u_long)tr->tr_flags);
+ it = (struct info_trap *)more_pkt();
+ }
+ }
+ flush_pkt();
+}
+
+
+/*
+ * req_set_trap - configure a trap
+ */
+static void
+req_set_trap(srcadr, inter, inpkt)
+ struct sockaddr_in *srcadr;
+ struct interface *inter;
+ struct req_pkt *inpkt;
+{
+ do_setclr_trap(srcadr, inter, inpkt, 1);
+}
+
+
+
+/*
+ * req_clr_trap - unconfigure a trap
+ */
+static void
+req_clr_trap(srcadr, inter, inpkt)
+ struct sockaddr_in *srcadr;
+ struct interface *inter;
+ struct req_pkt *inpkt;
+{
+ do_setclr_trap(srcadr, inter, inpkt, 0);
+}
+
+
+
+/*
+ * do_setclr_trap - do the grunge work of (un)configuring a trap
+ */
+static void
+do_setclr_trap(srcadr, inter, inpkt, set)
+ struct sockaddr_in *srcadr;
+ struct interface *inter;
+ struct req_pkt *inpkt;
+ int set;
+{
+ register struct conf_trap *ct;
+ register struct interface *linter;
+ int res;
+ struct sockaddr_in laddr;
+
+ /*
+ * Prepare sockaddr_in structure
+ */
+ memset((char *)&laddr, 0, sizeof laddr);
+ laddr.sin_family = AF_INET;
+ laddr.sin_port = ntohs(NTP_PORT);
+
+ /*
+ * Restrict ourselves to one item only. This eliminates
+ * the error reporting problem.
+ */
+ if (INFO_NITEMS(inpkt->err_nitems) > 1) {
+ req_ack(srcadr, inter, inpkt, INFO_ERR_FMT);
+ return;
+ }
+ ct = (struct conf_trap *)inpkt->data;
+
+ /*
+ * Look for the local interface. If none, use the default.
+ */
+ if (ct->local_address == 0) {
+ linter = any_interface;
+ } else {
+ laddr.sin_addr.s_addr = ct->local_address;
+ linter = findinterface(&laddr);
+ if (linter == NULL) {
+ req_ack(srcadr, inter, inpkt, INFO_ERR_NODATA);
+ return;
+ }
+ }
+
+ laddr.sin_addr.s_addr = ct->trap_address;
+ if (ct->trap_port != 0)
+ laddr.sin_port = ct->trap_port;
+ else
+ laddr.sin_port = htons(TRAPPORT);
+
+ if (set) {
+ res = ctlsettrap(&laddr, linter, 0,
+ INFO_VERSION(inpkt->rm_vn_mode));
+ } else {
+ res = ctlclrtrap(&laddr, linter, 0);
+ }
+
+ if (!res) {
+ req_ack(srcadr, inter, inpkt, INFO_ERR_NODATA);
+ } else {
+ req_ack(srcadr, inter, inpkt, INFO_OKAY);
+ }
+ return;
+}
+
+
+
+/*
+ * set_request_keyid - set the keyid used to authenticate requests
+ */
+static void
+set_request_keyid(srcadr, inter, inpkt)
+ struct sockaddr_in *srcadr;
+ struct interface *inter;
+ struct req_pkt *inpkt;
+{
+ u_long keyid;
+
+ /*
+ * Restrict ourselves to one item only.
+ */
+ if (INFO_NITEMS(inpkt->err_nitems) > 1) {
+ req_ack(srcadr, inter, inpkt, INFO_ERR_FMT);
+ return;
+ }
+
+ keyid = ntohl(*((u_long *)(inpkt->data)));
+ info_auth_keyid = keyid;
+ req_ack(srcadr, inter, inpkt, INFO_OKAY);
+}
+
+
+
+/*
+ * set_control_keyid - set the keyid used to authenticate requests
+ */
+static void
+set_control_keyid(srcadr, inter, inpkt)
+ struct sockaddr_in *srcadr;
+ struct interface *inter;
+ struct req_pkt *inpkt;
+{
+ u_long keyid;
+ extern u_long ctl_auth_keyid;
+
+ /*
+ * Restrict ourselves to one item only.
+ */
+ if (INFO_NITEMS(inpkt->err_nitems) > 1) {
+ req_ack(srcadr, inter, inpkt, INFO_ERR_FMT);
+ return;
+ }
+
+ keyid = ntohl(*((u_long *)(inpkt->data)));
+ ctl_auth_keyid = keyid;
+ req_ack(srcadr, inter, inpkt, INFO_OKAY);
+}
+
+
+
+/*
+ * get_ctl_stats - return some stats concerning the control message module
+ */
+static void
+get_ctl_stats(srcadr, inter, inpkt)
+ struct sockaddr_in *srcadr;
+ struct interface *inter;
+ struct req_pkt *inpkt;
+{
+ register struct info_control *ic;
+
+ /*
+ * Importations from the control module
+ */
+ extern u_long ctltimereset;
+ extern u_long numctlreq;
+ extern u_long numctlbadpkts;
+ extern u_long numctlresponses;
+ extern u_long numctlfrags;
+ extern u_long numctlerrors;
+ extern u_long numctltooshort;
+ extern u_long numctlinputresp;
+ extern u_long numctlinputfrag;
+ extern u_long numctlinputerr;
+ extern u_long numctlbadoffset;
+ extern u_long numctlbadversion;
+ extern u_long numctldatatooshort;
+ extern u_long numctlbadop;
+ extern u_long numasyncmsgs;
+
+ ic = (struct info_control *)prepare_pkt(srcadr, inter, inpkt,
+ sizeof(struct info_control));
+
+ ic->ctltimereset = htonl(current_time - ctltimereset);
+ ic->numctlreq = htonl(numctlreq);
+ ic->numctlbadpkts = htonl(numctlbadpkts);
+ ic->numctlresponses = htonl(numctlresponses);
+ ic->numctlfrags = htonl(numctlfrags);
+ ic->numctlerrors = htonl(numctlerrors);
+ ic->numctltooshort = htonl(numctltooshort);
+ ic->numctlinputresp = htonl(numctlinputresp);
+ ic->numctlinputfrag = htonl(numctlinputfrag);
+ ic->numctlinputerr = htonl(numctlinputerr);
+ ic->numctlbadoffset = htonl(numctlbadoffset);
+ ic->numctlbadversion = htonl(numctlbadversion);
+ ic->numctldatatooshort = htonl(numctldatatooshort);
+ ic->numctlbadop = htonl(numctlbadop);
+ ic->numasyncmsgs = htonl(numasyncmsgs);
+
+ (void) more_pkt();
+ flush_pkt();
+}
+
+
+
+/*
+ * get_leap_info - return some stats concerning the control message module
+ */
+static void
+get_leap_info(srcadr, inter, inpkt)
+ struct sockaddr_in *srcadr;
+ struct interface *inter;
+ struct req_pkt *inpkt;
+{
+ register struct info_leap *il;
+
+ /*
+ * Imported from the protocol module
+ */
+ extern u_char sys_leap;
+
+ /*
+ * Importations from the leap module
+ */
+ extern u_char leap_indicator;
+ extern u_char leap_warning;
+ extern u_char leapbits;
+ extern u_long leap_timer;
+ extern u_long leap_processcalls;
+ extern u_long leap_notclose;
+ extern u_long leap_monthofleap;
+ extern u_long leap_dayofleap;
+ extern u_long leap_hoursfromleap;
+ extern u_long leap_happened;
+
+ il = (struct info_leap *)prepare_pkt(srcadr, inter, inpkt,
+ sizeof(struct info_leap));
+
+ il->sys_leap = sys_leap;
+ il->leap_indicator = leap_indicator;
+ il->leap_warning = leap_warning;
+ il->leap_bits = (leapbits & INFO_LEAP_MASK)
+ | ((leap_indicator != LEAP_NOWARNING) ? INFO_LEAP_OVERRIDE : 0);
+ il->leap_timer = htonl(leap_timer - current_time);
+ il->leap_processcalls = htonl(leap_processcalls);
+ il->leap_notclose = htonl(leap_notclose);
+ il->leap_monthofleap = htonl(leap_monthofleap);
+ il->leap_dayofleap = htonl(leap_dayofleap);
+ il->leap_hoursfromleap = htonl(leap_hoursfromleap);
+ il->leap_happened = htonl(leap_happened);
+
+ (void) more_pkt();
+ flush_pkt();
+}
+
+
+#ifdef KERNEL_PLL
+/*
+ * get_kernel_info - get kernel pll/pps information
+ */
+static void
+get_kernel_info(srcadr, inter, inpkt)
+ struct sockaddr_in *srcadr;
+ struct interface *inter;
+ struct req_pkt *inpkt;
+{
+ register struct info_kernel *ik;
+ struct timex ntx;
+
+ if (!pll_control)
+ return;
+ memset((char *)&ntx, 0, sizeof(ntx));
+ (void)ntp_adjtime(&ntx);
+
+ ik = (struct info_kernel *)prepare_pkt(srcadr, inter, inpkt,
+ sizeof(struct info_kernel));
+
+ /*
+ * pll variables
+ */
+ ik->offset = htonl(ntx.offset);
+ ik->freq = htonl(ntx.freq);
+ ik->maxerror = htonl(ntx.maxerror);
+ ik->esterror = htonl(ntx.esterror);
+ ik->status = htons(ntx.status);
+ ik->constant = htonl(ntx.constant);
+ ik->precision = htonl(ntx.precision);
+ ik->tolerance = htonl(ntx.tolerance);
+
+ /*
+ * pps variables
+ */
+ ik->ppsfreq = htonl(ntx.ppsfreq);
+ ik->jitter = htonl(ntx.jitter);
+ ik->shift = htons(ntx.shift);
+ ik->stabil = htonl(ntx.stabil);
+ ik->jitcnt = htonl(ntx.jitcnt);
+ ik->calcnt = htonl(ntx.calcnt);
+ ik->errcnt = htonl(ntx.errcnt);
+ ik->stbcnt = htonl(ntx.stbcnt);
+
+ (void) more_pkt();
+ flush_pkt();
+}
+#endif /* KERNEL_PLL */
+
+
+#ifdef REFCLOCK
+/*
+ * get_clock_info - get info about a clock
+ */
+static void
+get_clock_info(srcadr, inter, inpkt)
+ struct sockaddr_in *srcadr;
+ struct interface *inter;
+ struct req_pkt *inpkt;
+{
+ register struct info_clock *ic;
+ register U_LONG *clkaddr;
+ register int items;
+ struct refclockstat clock;
+ struct sockaddr_in addr;
+
+ memset((char *)&addr, 0, sizeof addr);
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(NTP_PORT);
+ items = INFO_NITEMS(inpkt->err_nitems);
+ clkaddr = (U_LONG *) inpkt->data;
+
+ ic = (struct info_clock *)prepare_pkt(srcadr, inter, inpkt,
+ sizeof(struct info_clock));
+
+ while (items-- > 0) {
+ addr.sin_addr.s_addr = *clkaddr++;
+ if (!ISREFCLOCKADR(&addr) ||
+ findexistingpeer(&addr, (struct peer *)0) == 0) {
+ req_ack(srcadr, inter, inpkt, INFO_ERR_NODATA);
+ return;
+ }
+
+ clock.kv_list = (struct ctl_var *)0;
+
+ refclock_control(&addr, (struct refclockstat *)0, &clock);
+
+ ic->clockadr = addr.sin_addr.s_addr;
+ ic->type = clock.type;
+ ic->flags = clock.flags;
+ ic->lastevent = clock.lastevent;
+ ic->currentstatus = clock.currentstatus;
+ ic->polls = htonl(clock.polls);
+ ic->noresponse = htonl(clock.noresponse);
+ ic->badformat = htonl(clock.badformat);
+ ic->baddata = htonl(clock.baddata);
+ ic->timestarted = htonl(clock.timereset);
+ HTONL_FP(&clock.fudgetime1, &ic->fudgetime1);
+ HTONL_FP(&clock.fudgetime2, &ic->fudgetime2);
+ ic->fudgeval1 = htonl(clock.fudgeval1);
+ ic->fudgeval2 = htonl(clock.fudgeval2);
+
+ free_varlist(clock.kv_list);
+
+ ic = (struct info_clock *)more_pkt();
+ }
+ flush_pkt();
+}
+
+
+
+/*
+ * set_clock_fudge - get a clock's fudge factors
+ */
+static void
+set_clock_fudge(srcadr, inter, inpkt)
+ struct sockaddr_in *srcadr;
+ struct interface *inter;
+ struct req_pkt *inpkt;
+{
+ register struct conf_fudge *cf;
+ register int items;
+ struct refclockstat clock;
+ struct sockaddr_in addr;
+
+ memset((char *)&addr, 0, sizeof addr);
+ memset((char *)&clock, 0, sizeof clock);
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(NTP_PORT);
+ items = INFO_NITEMS(inpkt->err_nitems);
+ cf = (struct conf_fudge *) inpkt->data;
+
+ while (items-- > 0) {
+ addr.sin_addr.s_addr = cf->clockadr;
+ if (!ISREFCLOCKADR(&addr) ||
+ findexistingpeer(&addr, (struct peer *)0) == 0) {
+ req_ack(srcadr, inter, inpkt, INFO_ERR_NODATA);
+ return;
+ }
+
+ switch(ntohl(cf->which)) {
+ case FUDGE_TIME1:
+ NTOHL_FP(&cf->fudgetime, &clock.fudgetime1);
+ clock.haveflags = CLK_HAVETIME1;
+ break;
+ case FUDGE_TIME2:
+ NTOHL_FP(&cf->fudgetime, &clock.fudgetime2);
+ clock.haveflags = CLK_HAVETIME2;
+ break;
+ case FUDGE_VAL1:
+ clock.fudgeval1 = ntohl(cf->fudgeval_flags);
+ clock.haveflags = CLK_HAVEVAL1;
+ break;
+ case FUDGE_VAL2:
+ clock.fudgeval2 = ntohl(cf->fudgeval_flags);
+ clock.haveflags = CLK_HAVEVAL2;
+ break;
+ case FUDGE_FLAGS:
+ clock.flags = ntohl(cf->fudgeval_flags) & 0xf;
+ clock.haveflags =
+ (CLK_HAVEFLAG1|CLK_HAVEFLAG2|CLK_HAVEFLAG3|CLK_HAVEFLAG4);
+ break;
+ default:
+ req_ack(srcadr, inter, inpkt, INFO_ERR_FMT);
+ return;
+ }
+
+ refclock_control(&addr, &clock, (struct refclockstat *)0);
+ }
+
+ req_ack(srcadr, inter, inpkt, INFO_OKAY);
+}
+#endif
+
+/*
+ * set_precision - set the system precision
+ */
+static void
+set_precision(srcadr, inter, inpkt)
+ struct sockaddr_in *srcadr;
+ struct interface *inter;
+ struct req_pkt *inpkt;
+{
+ register long precision;
+
+ precision = ntohl(*(long *)(inpkt->data));
+
+ if (INFO_NITEMS(inpkt->err_nitems) > 1 ||
+ precision > -1 || precision < -20) {
+ req_ack(srcadr, inter, inpkt, INFO_ERR_FMT);
+ return;
+ }
+
+ proto_config(PROTO_PRECISION, precision);
+ req_ack(srcadr, inter, inpkt, INFO_OKAY);
+}
+
+#ifdef REFCLOCK
+/*
+ * get_clkbug_info - get debugging info about a clock
+ */
+static void
+get_clkbug_info(srcadr, inter, inpkt)
+ struct sockaddr_in *srcadr;
+ struct interface *inter;
+ struct req_pkt *inpkt;
+{
+ register int i;
+ register struct info_clkbug *ic;
+ register U_LONG *clkaddr;
+ register int items;
+ struct refclockbug bug;
+ struct sockaddr_in addr;
+
+ memset((char *)&addr, 0, sizeof addr);
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(NTP_PORT);
+ items = INFO_NITEMS(inpkt->err_nitems);
+ clkaddr = (U_LONG *) inpkt->data;
+
+ ic = (struct info_clkbug *)prepare_pkt(srcadr, inter, inpkt,
+ sizeof(struct info_clkbug));
+
+ while (items-- > 0) {
+ addr.sin_addr.s_addr = *clkaddr++;
+ if (!ISREFCLOCKADR(&addr) ||
+ findexistingpeer(&addr, (struct peer *)0) == 0) {
+ req_ack(srcadr, inter, inpkt, INFO_ERR_NODATA);
+ return;
+ }
+
+ memset((char *)&bug, 0, sizeof bug);
+ refclock_buginfo(&addr, &bug);
+ if (bug.nvalues == 0 && bug.ntimes == 0) {
+ req_ack(srcadr, inter, inpkt, INFO_ERR_NODATA);
+ return;
+ }
+
+ ic->clockadr = addr.sin_addr.s_addr;
+ i = bug.nvalues;
+ if (i > NUMCBUGVALUES)
+ i = NUMCBUGVALUES;
+ ic->nvalues = (u_char)i;
+ ic->svalues = htons((u_short)bug.svalues & ((1<<i)-1));
+ while (--i >= 0)
+ ic->values[i] = htonl(bug.values[i]);
+
+ i = bug.ntimes;
+ if (i > NUMCBUGTIMES)
+ i = NUMCBUGTIMES;
+ ic->ntimes = (u_char)i;
+ ic->stimes = htonl(bug.stimes);
+ while (--i >= 0) {
+ HTONL_FP(&bug.times[i], &ic->times[i]);
+ }
+
+ ic = (struct info_clkbug *)more_pkt();
+ }
+ flush_pkt();
+}
+#endif
diff --git a/usr.sbin/xntpd/xntpd/ntp_restrict.c b/usr.sbin/xntpd/xntpd/ntp_restrict.c
new file mode 100644
index 0000000..174d07e
--- /dev/null
+++ b/usr.sbin/xntpd/xntpd/ntp_restrict.c
@@ -0,0 +1,459 @@
+/*
+ * ntp_restrict.c - find out what restrictions this host is running under
+ */
+#include <stdio.h>
+#include <sys/types.h>
+
+#include "ntpd.h"
+#include "ntp_if.h"
+#include "ntp_stdlib.h"
+
+/*
+ * This code keeps a simple address-and-mask list of hosts we want
+ * to place restrictions on (or remove them from). The restrictions
+ * are implemented as a set of flags which tell you what the host
+ * can't do. There is a subroutine entry to return the flags. The
+ * list is kept sorted to reduce the average number of comparisons
+ * and make sure you get the set of restrictions most specific to
+ * the address.
+ *
+ * The algorithm is that, when looking up a host, it is first assumed
+ * that the default set of restrictions will apply. It then searches
+ * down through the list. Whenever it finds a match it adopts the match's
+ * flags instead. When you hit the point where the sorted address is
+ * greater than the target, you return with the last set of flags you
+ * found. Because of the ordering of the list, the most specific match
+ * will provide the final set of flags.
+ *
+ * This was originally intended to restrict you from sync'ing to your
+ * own broadcasts when you are doing that, by restricting yourself
+ * from your own interfaces. It was also thought it would sometimes
+ * be useful to keep a misbehaving host or two from abusing your primary
+ * clock. It has been expanded, however, to suit the needs of those
+ * with more restrictive access policies.
+ */
+
+/*
+ * Memory allocation parameters. We allocate INITRESLIST entries
+ * initially, and add INCRESLIST entries to the free list whenever
+ * we run out.
+ */
+#define INITRESLIST 10
+#define INCRESLIST 5
+
+/*
+ * The restriction list
+ */
+ struct restrictlist *restrictlist;
+static int restrictcount; /* count of entries in the restriction list */
+
+/*
+ * The free list and associated counters. Also some uninteresting
+ * stat counters.
+ */
+static struct restrictlist *resfree;
+static int numresfree; /* number of structures on free list */
+
+u_long res_calls;
+u_long res_found;
+u_long res_not_found;
+u_long res_timereset;
+
+/*
+ * Parameters of the RES_LIMITED restriction option.
+ * client_limit is the number of hosts allowed per source net
+ * client_limit_period is the number of seconds after which an entry
+ * is no longer considered for client limit determination
+ */
+u_long client_limit;
+u_long client_limit_period;
+/*
+ * count number of restriction entries referring to RES_LIMITED
+ * controls activation/deactivation of monitoring
+ * (with respect ro RES_LIMITED control)
+ */
+u_long res_limited_refcnt;
+
+/*
+ * Our initial allocation of list entries.
+ */
+static struct restrictlist resinit[INITRESLIST];
+
+/*
+ * Imported from the timer module
+ */
+extern u_long current_time;
+
+/*
+ * debug flag
+ */
+extern int debug;
+
+/*
+ * init_restrict - initialize the restriction data structures
+ */
+void
+init_restrict()
+{
+ register int i;
+ char bp[80];
+
+ /*
+ * Zero the list and put all but one on the free list
+ */
+ resfree = 0;
+ memset((char *)resinit, 0, sizeof resinit);
+
+ for (i = 1; i < INITRESLIST; i++) {
+ resinit[i].next = resfree;
+ resfree = &resinit[i];
+ }
+
+ numresfree = INITRESLIST-1;
+
+ /*
+ * Put the remaining item at the head of the
+ * list as our default entry. Everything in here
+ * should be zero for now.
+ */
+ resinit[0].addr = htonl(INADDR_ANY);
+ resinit[0].mask = 0;
+ restrictlist = &resinit[0];
+ restrictcount = 1;
+
+
+ /*
+ * fix up stat counters
+ */
+ res_calls = 0;
+ res_found = 0;
+ res_not_found = 0;
+ res_timereset = 0;
+
+ /*
+ * set default values for RES_LIMIT functionality
+ */
+ client_limit = 3;
+ client_limit_period = 3600;
+ res_limited_refcnt = 0;
+
+ sprintf(bp, "client_limit=%ld", client_limit);
+ set_sys_var(bp, strlen(bp)+1, RO);
+ sprintf(bp, "client_limit_period=%ld", client_limit_period);
+ set_sys_var(bp, strlen(bp)+1, RO);
+}
+
+
+/*
+ * restrictions - return restrictions for this host
+ */
+int
+restrictions(srcadr)
+ struct sockaddr_in *srcadr;
+{
+ register struct restrictlist *rl;
+ register struct restrictlist *match;
+ register u_long hostaddr;
+ register int isntpport;
+
+ res_calls++;
+ /*
+ * We need the host address in host order. Also need to know
+ * whether this is from the ntp port or not.
+ */
+ hostaddr = SRCADR(srcadr);
+ isntpport = (SRCPORT(srcadr) == NTP_PORT);
+
+ /*
+ * Set match to first entry, which is default entry. Work our
+ * way down from there.
+ */
+ match = restrictlist;
+
+ for (rl = match->next; rl != 0 && rl->addr <= hostaddr; rl = rl->next)
+ if ((hostaddr & rl->mask) == rl->addr) {
+ if ((rl->mflags & RESM_NTPONLY) && !isntpport)
+ continue;
+ match = rl;
+ }
+
+ match->count++;
+ if (match == restrictlist)
+ res_not_found++;
+ else
+ res_found++;
+
+ /*
+ * The following implements limiting the number of clients
+ * accepted from a given network. The notion of "same network"
+ * is determined by the mask and addr fields of the restrict
+ * list entry. The monitor mechanism has to be enabled for
+ * collecting info on current clients.
+ *
+ * The policy is as follows:
+ * - take the list of clients recorded
+ * from the given "network" seen within the last
+ * client_limit_period seconds
+ * - if there are at most client_limit entries:
+ * --> access allowed
+ * - otherwise sort by time first seen
+ * - current client among the first client_limit seen
+ * hosts?
+ * if yes: access allowed
+ * else: eccess denied
+ */
+ if (match->flags & RES_LIMITED) {
+ int lcnt;
+ struct mon_data *md, *this_client;
+ extern int mon_enabled;
+ extern struct mon_data mon_fifo_list, mon_mru_list;
+
+#ifdef DEBUG
+ if (debug > 2)
+ printf("limited clients check: %ld clients, period %ld seconds, net is 0x%lX\n",
+ client_limit, client_limit_period,
+ netof(hostaddr));
+#endif /*DEBUG*/
+ if (mon_enabled == MON_OFF) {
+#ifdef DEBUG
+ if (debug > 4)
+ printf("no limit - monitoring is off\n");
+#endif
+ return (int)(match->flags & ~RES_LIMITED);
+ }
+
+ /*
+ * How nice, MRU list provides our current client as the
+ * first entry in the list.
+ * Monitoring was verified to be active above, thus we
+ * know an entry for our client must exist, or some
+ * brain dead set the memory limit for mon entries to ZERO!!!
+ */
+ this_client = mon_mru_list.mru_next;
+
+ for (md = mon_fifo_list.fifo_next,lcnt = 0;
+ md != &mon_fifo_list;
+ md = md->fifo_next) {
+ if ((current_time - md->lasttime)
+ > client_limit_period) {
+#ifdef DEBUG
+ if (debug > 5)
+ printf("checking: %s: ignore: too old: %ld\n",
+ numtoa(md->rmtadr),
+ current_time - md->lasttime);
+#endif
+ continue;
+ }
+ if (md->mode == MODE_BROADCAST ||
+ md->mode == MODE_CONTROL ||
+ md->mode == MODE_PRIVATE) {
+#ifdef DEBUG
+ if (debug > 5)
+ printf("checking: %s: ignore mode %d\n",
+ numtoa(md->rmtadr),
+ md->mode);
+#endif
+ continue;
+ }
+ if (netof(md->rmtadr) !=
+ netof(hostaddr)) {
+#ifdef DEBUG
+ if (debug > 5)
+ printf("checking: %s: different net 0x%lX\n",
+ numtoa(md->rmtadr),
+ netof(md->rmtadr));
+#endif
+ continue;
+ }
+ lcnt++;
+ if (lcnt > client_limit ||
+ md->rmtadr == hostaddr) {
+#ifdef DEBUG
+ if (debug > 5)
+ printf("considering %s: found host\n",
+ numtoa(md->rmtadr));
+#endif
+ break;
+ }
+#ifdef DEBUG
+ else {
+ if (debug > 5)
+ printf("considering %s: same net\n",
+ numtoa(md->rmtadr));
+ }
+#endif
+
+ }
+#ifdef DEBUG
+ if (debug > 4)
+ printf("this one is rank %d in list, limit is %lu: %s\n",
+ lcnt, client_limit,
+ (lcnt <= client_limit) ? "ALLOW" : "REJECT");
+#endif
+ if (lcnt <= client_limit) {
+ this_client->lastdrop = 0;
+ return (int)(match->flags & ~RES_LIMITED);
+ } else {
+ this_client->lastdrop = current_time;
+ }
+ }
+ return (int)match->flags;
+}
+
+
+/*
+ * restrict - add/subtract/manipulate entries on the restrict list
+ */
+void
+restrict(op, resaddr, resmask, mflags, flags)
+ int op;
+ struct sockaddr_in *resaddr;
+ struct sockaddr_in *resmask;
+ int mflags;
+ int flags;
+{
+ register u_long addr;
+ register u_long mask;
+ register struct restrictlist *rl;
+ register struct restrictlist *rlprev;
+ int i;
+
+ /*
+ * Get address and mask in host byte order
+ */
+ addr = SRCADR(resaddr);
+ mask = SRCADR(resmask);
+ addr &= mask; /* make sure low bits are zero */
+
+ /*
+ * If this is the default address, point at first on list. Else
+ * go searching for it.
+ */
+ if (addr == htonl(INADDR_ANY)) {
+ rlprev = 0;
+ rl = restrictlist;
+ } else {
+ rlprev = restrictlist;
+ rl = rlprev->next;
+ while (rl != 0) {
+ if (rl->addr > addr) {
+ rl = 0;
+ break;
+ } else if (rl->addr == addr) {
+ if (rl->mask == mask) {
+ if ((mflags & RESM_NTPONLY)
+ == (rl->mflags & RESM_NTPONLY))
+ break; /* exact match */
+ if (!(mflags & RESM_NTPONLY)) {
+ /*
+ * No flag fits before flag
+ */
+ rl = 0;
+ break;
+ }
+ /* continue on */
+ } else if (rl->mask > mask) {
+ rl = 0;
+ break;
+ }
+ }
+ rlprev = rl;
+ rl = rl->next;
+ }
+ }
+ /*
+ * In case the above wasn't clear :-), either rl now points
+ * at the entry this call refers to, or rl is zero and rlprev
+ * points to the entry prior to where this one should go in
+ * the sort.
+ */
+
+ /*
+ * Switch based on operation
+ */
+ switch (op) {
+ case RESTRICT_FLAGS:
+ /*
+ * Here we add bits to the flags. If this is a new
+ * restriction add it.
+ */
+ if (rl == 0) {
+ if (numresfree == 0) {
+ rl = (struct restrictlist *) emalloc(
+ INCRESLIST*sizeof(struct restrictlist));
+ memset((char *)rl, 0,
+ INCRESLIST*sizeof(struct restrictlist));
+
+ for (i = 0; i < INCRESLIST; i++) {
+ rl->next = resfree;
+ resfree = rl;
+ rl++;
+ }
+ numresfree = INCRESLIST;
+ }
+
+ rl = resfree;
+ resfree = rl->next;
+ numresfree--;
+
+ rl->addr = addr;
+ rl->mask = mask;
+ rl->mflags = (u_short)mflags;
+
+ rl->next = rlprev->next;
+ rlprev->next = rl;
+ restrictcount++;
+ }
+ if ((rl->flags ^ (u_short)flags) & RES_LIMITED) {
+ res_limited_refcnt++;
+ mon_start(MON_RES); /* ensure data gets collected */
+ }
+ rl->flags |= (u_short)flags;
+ break;
+
+ case RESTRICT_UNFLAG:
+ /*
+ * Remove some bits from the flags. If we didn't
+ * find this one, just return.
+ */
+ if (rl != 0) {
+ if ((rl->flags ^ (u_short)flags) & RES_LIMITED) {
+ res_limited_refcnt--;
+ if (res_limited_refcnt == 0)
+ mon_stop(MON_RES);
+ }
+ rl->flags &= (u_short)~flags;
+ }
+ break;
+
+ case RESTRICT_REMOVE:
+ /*
+ * Remove an entry from the table entirely if we found one.
+ * Don't remove the default entry and don't remove an
+ * interface entry.
+ */
+ if (rl != 0
+ && rl->addr != htonl(INADDR_ANY)
+ && !(rl->mflags & RESM_INTERFACE)) {
+ rlprev->next = rl->next;
+ restrictcount--;
+ if (rl->flags & RES_LIMITED) {
+ res_limited_refcnt--;
+ if (res_limited_refcnt == 0)
+ mon_stop(MON_RES);
+ }
+ memset((char *)rl, 0, sizeof(struct restrictlist));
+
+ rl->next = resfree;
+ resfree = rl;
+ numresfree++;
+ }
+ break;
+
+ default:
+ /* Oh, well */
+ break;
+ }
+
+ /* done! */
+}
diff --git a/usr.sbin/xntpd/xntpd/ntp_timer.c b/usr.sbin/xntpd/xntpd/ntp_timer.c
new file mode 100644
index 0000000..99551f7
--- /dev/null
+++ b/usr.sbin/xntpd/xntpd/ntp_timer.c
@@ -0,0 +1,187 @@
+/*
+ * ntp_event.c - event timer support routines
+ */
+#include <stdio.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <signal.h>
+#include <sys/signal.h>
+
+#include "ntpd.h"
+#include "ntp_stdlib.h"
+
+/*
+ * These routines provide support for the event timer. The timer is
+ * implemented by an interrupt routine which sets a flag once every
+ * 2**EVENT_TIMEOUT seconds (currently 4), and a timer routine which
+ * is called when the mainline code gets around to seeing the flag.
+ * The timer routine dispatches the clock adjustment code if its time
+ * has come, then searches the timer queue for expiries which are
+ * dispatched to the transmit procedure. Finally, we call the hourly
+ * procedure to do cleanup and print a message.
+ */
+
+/*
+ * Alarm flag. The mainline code imports this.
+ */
+int alarm_flag;
+
+/*
+ * adjust and hourly counters
+ */
+static u_long adjust_timer;
+static u_long hourly_timer;
+
+/*
+ * Imported from the leap module. The leap timer.
+ */
+extern u_long leap_timer;
+
+/*
+ * Statistics counter for the interested.
+ */
+u_long alarm_overflow;
+
+#define HOUR (60*60)
+
+/*
+ * Current_time holds the number of seconds since we started, in
+ * increments of 2**EVENT_TIMEOUT seconds. The timer queue is the
+ * hash into which we sort timer entries.
+ */
+u_long current_time;
+struct event timerqueue[TIMER_NSLOTS];
+
+/*
+ * Stats. Number of overflows and number of calls to transmit().
+ */
+u_long timer_timereset;
+u_long timer_overflows;
+u_long timer_xmtcalls;
+
+static RETSIGTYPE alarming P((int));
+
+/*
+ * init_timer - initialize the timer data structures
+ */
+void
+init_timer()
+{
+ register int i;
+ struct itimerval itimer;
+
+ /*
+ * Initialize...
+ */
+ alarm_flag = 0;
+ alarm_overflow = 0;
+ adjust_timer = (1<<CLOCK_ADJ);
+ hourly_timer = HOUR;
+ current_time = 0;
+ timer_overflows = 0;
+ timer_xmtcalls = 0;
+ timer_timereset = 0;
+
+ for (i = 0; i < TIMER_NSLOTS; i++) {
+ /*
+ * Queue pointers should point at themselves. Event
+ * times must be set to 0 since this is used to
+ * detect the queue end.
+ */
+ timerqueue[i].next = &timerqueue[i];
+ timerqueue[i].prev = &timerqueue[i];
+ timerqueue[i].event_time = 0;
+ }
+
+ /*
+ * Set up the alarm interrupt. The first comes 2**EVENT_TIMEOUT
+ * seconds from now and they continue on every 2**EVENT_TIMEOUT
+ * seconds.
+ */
+ (void) signal_no_reset(SIGALRM, alarming);
+ itimer.it_interval.tv_sec = itimer.it_value.tv_sec = (1<<EVENT_TIMEOUT);
+ itimer.it_interval.tv_usec = itimer.it_value.tv_usec = 0;
+ setitimer(ITIMER_REAL, &itimer, (struct itimerval *)0);
+}
+
+
+
+/*
+ * timer - dispatch anyone who needs to be
+ */
+void
+timer()
+{
+ register struct event *ev;
+ register struct event *tq;
+
+ current_time += (1<<EVENT_TIMEOUT);
+
+ /*
+ * Adjustment timeout first
+ */
+ if (adjust_timer <= current_time) {
+ adjust_timer += (1<<CLOCK_ADJ);
+ adj_host_clock();
+ }
+
+ /*
+ * Leap timer next.
+ */
+ if (leap_timer != 0 && leap_timer <= current_time)
+ leap_process();
+
+ /*
+ * Now dispatch any peers whose event timer has expired.
+ */
+ tq = &timerqueue[TIMER_SLOT(current_time)];
+ ev = tq->next;
+ while (ev->event_time != 0
+ && ev->event_time < (current_time + (1<<EVENT_TIMEOUT))) {
+ tq->next = ev->next;
+ tq->next->prev = tq;
+ ev->prev = ev->next = 0;
+ timer_xmtcalls++;
+ ev->event_handler(ev->peer);
+ ev = tq->next;
+ }
+
+ /*
+ * Finally, call the hourly routine
+ */
+ if (hourly_timer <= current_time) {
+ hourly_timer += HOUR;
+ hourly_stats();
+ }
+}
+
+
+/*
+ * alarming - tell the world we've been alarmed
+ */
+static RETSIGTYPE
+alarming(sig)
+int sig;
+{
+ extern int initializing; /* from main line code */
+
+ if (initializing)
+ return;
+ if (alarm_flag)
+ alarm_overflow++;
+ else
+ alarm_flag++;
+}
+
+
+/*
+ * timer_clr_stats - clear timer module stat counters
+ */
+void
+timer_clr_stats()
+{
+ timer_overflows = 0;
+ timer_xmtcalls = 0;
+ timer_timereset = current_time;
+}
diff --git a/usr.sbin/xntpd/xntpd/ntp_unixclock.c b/usr.sbin/xntpd/xntpd/ntp_unixclock.c
new file mode 100644
index 0000000..bc771b6
--- /dev/null
+++ b/usr.sbin/xntpd/xntpd/ntp_unixclock.c
@@ -0,0 +1,606 @@
+/*
+ * ntp_unixclock.c - routines for reading and adjusting a 4BSD-style
+ * system clock
+ */
+
+#include <nlist.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+
+#if defined(SYS_HPUX) || defined(sgi) || defined(SYS_BSDI) || defined(SYS_44BSD)
+#include <sys/param.h>
+#include <utmp.h>
+#endif
+
+#if defined(HAVE_GETBOOTFILE)
+#include <paths.h>
+#endif
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_unixtime.h"
+#include "ntp_stdlib.h"
+
+#if defined(HAVE_LIBKVM)
+#if defined(SYS_BSDI) || defined(SYS_44BSD)
+#include <sys/proc.h>
+#endif /* SYS_BSDI */
+#include <kvm.h>
+#include <limits.h>
+
+#ifndef _POSIX2_LINE_MAX
+#define _POSIX2_LINE_MAX 2048
+#endif
+#endif /* HAVE_LIBKVM */
+
+
+#ifdef RS6000
+#undef hz
+#endif /* RS6000 */
+
+extern int debug;
+/*
+ * These routines (init_systime, get_systime, step_systime, adj_systime)
+ * implement an interface between the (more or less) system independent
+ * bits of NTP and the peculiarities of dealing with the Unix system
+ * clock. These routines will run with good precision fairly independently
+ * of your kernel's value of tickadj. I couldn't tell the difference
+ * between tickadj==40 and tickadj==5 on a microvax, though I prefer
+ * to set tickadj == 500/hz when in doubt. At your option you
+ * may compile this so that your system's clock is always slewed to the
+ * correct time even for large corrections. Of course, all of this takes
+ * a lot of code which wouldn't be needed with a reasonable tickadj and
+ * a willingness to let the clock be stepped occasionally. Oh well.
+ */
+
+/*
+ * Clock variables. We round calls to adjtime() to adj_precision
+ * microseconds, and limit the adjustment to tvu_maxslew microseconds
+ * (tsf_maxslew fractional sec) in one adjustment interval. As we are
+ * thus limited in the speed and precision with which we can adjust the
+ * clock, we compensate by keeping the known "error" in the system time
+ * in sys_clock_offset. This is added to timestamps returned by get_systime().
+ * We also remember the clock precision we computed from the kernel in
+ * case someone asks us.
+ */
+extern long adj_precision; /* adj precision in usec (tickadj) */
+extern long tvu_maxslew; /* maximum adjust doable in 1<<CLOCK_ADJ sec (usec) */
+
+extern u_long tsf_maxslew; /* same as above, as long format */
+
+extern l_fp sys_clock_offset; /* correction for current system time */
+
+/*
+ * Import sys_clock (it is updated in get_systime)
+ */
+extern long sys_clock;
+
+static void clock_parms P((u_long *, u_long *));
+
+/*
+ * init_systime - initialize the system clock support code, return
+ * clock precision.
+ *
+ * Note that this code obtains to kernel variables related to the local
+ * clock, tickadj and tick. The code knows how the Berkeley adjtime
+ * call works, and assumes these two variables are obtainable and are
+ * used in the same manner. Tick is supposed to be the number of
+ * microseconds which are added to the system clock at clock interrupt
+ * time when the time isn't being slewed. Tickadj is supposed to be
+ * the number of microseconds which are added or subtracted from tick when
+ * the time is being slewed.
+ *
+ * If either of these two variables is missing, or is there but is used
+ * for a purpose different than that described, you are SOL and may have
+ * to do some custom kludging.
+ *
+ * This really shouldn't be in here.
+ */
+void
+init_systime()
+{
+ u_long tickadj;
+ u_long tick;
+ u_long hz;
+
+ /*
+ * Obtain the values
+ */
+ clock_parms(&tickadj, &tick);
+#ifdef DEBUG
+ if (debug)
+ printf("kernel vars: tickadj = %ld, tick = %ld\n", tickadj, tick);
+#endif
+
+ /*
+ * If tickadj or hz wasn't found, we're doomed. If hz is
+ * unreasonably small, forget it.
+ */
+ if (tickadj == 0 || tick == 0) {
+ syslog(LOG_ERR, "tickadj or tick unknown, exiting");
+ exit(3);
+ }
+ if (tick > 65535) {
+ syslog(LOG_ERR, "tick value of %lu is unreasonably large",
+ tick);
+ exit(3);
+ }
+
+ /*
+ * Estimate hz from tick
+ */
+ hz = 1000000L / tick;
+
+ /*
+ * Set adj_precision and the maximum slew based on this. Note
+ * that maxslew is set slightly shorter than it needs to be as
+ * insurance that all slews requested will complete in 1<<CLOCK_ADJ
+ * seconds.
+ */
+#ifdef ADJTIME_IS_ACCURATE
+ adj_precision = 1;
+#else
+ adj_precision = tickadj;
+#endif /* ADJTIME_IS_ACCURATE */
+#if defined(SLEWALWAYS) && !defined(ADJTIME_IS_ACCURATE)
+ /*
+ * give us more time if we are always slewing... just in case
+ */
+ tvu_maxslew = tickadj * (hz-3) * (1<<CLOCK_ADJ);
+#else
+ tvu_maxslew = tickadj * (hz-1) * (1<<CLOCK_ADJ);
+#endif /* SLEWALWAYS */
+ if (tvu_maxslew > 999990) {
+ /*
+ * Don't let the maximum slew exceed 1 second in 4. This
+ * simplifies calculations a lot since we can then deal
+ * with less-than-one-second fractions.
+ */
+ tvu_maxslew = (999990/adj_precision) * adj_precision;
+ }
+ TVUTOTSF(tvu_maxslew, tsf_maxslew);
+ syslog(LOG_NOTICE, "tickadj = %d, tick = %d, tvu_maxslew = %d",
+ tickadj, tick, tvu_maxslew);
+#ifdef DEBUG
+ if (debug)
+ printf(
+ "adj_precision = %ld, tvu_maxslew = %ld, tsf_maxslew = 0.%08lx\n",
+ adj_precision, tvu_maxslew, tsf_maxslew);
+#endif
+
+ /*
+ * Set the current offset to 0
+ */
+ L_CLR(&sys_clock_offset);
+}
+
+#ifdef HAVE_LIBKVM
+/*
+ * clock_parms - return the local clock tickadj and tick parameters
+ *
+ * This version uses the SunOS libkvm (or the bsd compatability version).
+ */
+static void
+clock_parms(tickadj, tick)
+ u_long *tickadj;
+ u_long *tick;
+{
+ static struct nlist nl[] = {
+#define N_TICKADJ 0
+ { "_tickadj" },
+#define N_TICK 1
+ { "_tick" },
+ { "" },
+ };
+#if __convex__ /* { */
+ if (K_open((char *)0,O_RDONLY,"/vmunix")!=0) {
+ syslog(LOG_ERR, "K_open failed");
+ exit(3);
+ }
+ kusenlist(1);
+ if (knlist(nl)!=0
+ || nl[N_TICKADJ].n_value==0
+ || nl[N_TICK].n_value==0) {
+ syslog(LOG_ERR, "knlist failed");
+ exit(3);
+ }
+ if (K_read(tickadj,sizeof(*tickadj),nl[N_TICKADJ].n_value) !=
+ sizeof(*tickadj)) {
+ syslog(LOG_ERR, "K_read tickadj failed");
+ exit(3);
+ }
+ if (K_read(tick,sizeof(*tick),nl[N_TICK].n_value) !=
+ sizeof(*tick)) {
+ syslog(LOG_ERR, "K_read tick failed");
+ exit(3);
+ }
+ (void)K_close();
+#else /* }__convex__{ */
+ register kvm_t *kd;
+ if ((kd = kvm_open(NULL, NULL, NULL, O_RDONLY, NULL)) == NULL) {
+ syslog(LOG_ERR, "kvm_open failed");
+ exit(3);
+ }
+ if (kvm_nlist(kd, nl) != 0) {
+ syslog(LOG_ERR, "kvm_nlist failed");
+ exit(3);
+ }
+ if (kvm_read(kd, nl[N_TICKADJ].n_value, (char *)tickadj, sizeof(*tickadj)) !=
+ sizeof(*tickadj)) {
+ syslog(LOG_ERR, "kvm_read tickadj failed");
+ exit(3);
+ }
+ if (kvm_read(kd, nl[N_TICK].n_value, (char *)tick, sizeof(*tick)) !=
+ sizeof(*tick)) {
+ syslog(LOG_ERR, "kvm_read tick failed");
+ exit(3);
+ }
+ if (kvm_close(kd) < 0) {
+ syslog(LOG_ERR, "kvm_close failed");
+ exit(3);
+ }
+#endif /*}convex*/
+#undef N_TICKADJ
+#undef N_TICK
+}
+#endif /* HAVE_LIBKVM */
+
+
+#ifdef HAVE_READKMEM
+/*
+ * clock_parms - return the local clock tickadj and tick parameters
+ *
+ * Note that this version grovels about in /dev/kmem to determine
+ * these values. This probably should be elsewhere.
+ */
+#if defined(SYS_UNIXWARE1)
+/*
+ * clock_parms - return the local clock tickadj and tick parameters
+ *
+ * The values set here were determined experimentally on a 486 system
+ * I'm not confident in them. - RAS
+ *
+ */
+static void
+clock_parms(tickadj, tick)
+ u_long *tickadj;
+ u_long *tick;
+{
+ *tick = 10000; /* microseconds */
+ *tickadj = 80; /* microseconds */
+}
+#else /* SYS_UNIXWARE1 */
+
+#if defined(SYS_AUX3) || defined(SYS_AUX2) || defined(SYS_SVR4) || defined(SYS_PTX)
+#define K_TICKADJ_NAME "tickadj"
+#define K_TICK_NAME "tick"
+#endif
+
+#ifdef SYS_HPUX
+#if defined(hp9000s300)
+#define K_TICKADJ_NAME "_tickadj"
+#define K_TICK_NAME "_old_tick"
+#else
+#define K_TICKADJ_NAME "tickadj"
+#define K_TICK_NAME "old_tick"
+#endif
+#endif
+
+/* The defaults if not defined previously */
+#if !defined(K_TICKADJ_NAME)
+#define K_TICKADJ_NAME "_tickadj"
+#endif
+#if !defined(K_TICK_NAME)
+#define K_TICK_NAME "_tick"
+#endif
+
+static void
+clock_parms(tickadj, tick)
+ u_long *tickadj;
+ u_long *tick;
+{
+ register int i;
+ int kmem;
+#if defined(HAVE_N_UN)
+#define N_NAME n_un.n_name
+ static struct nlist nl[] =
+ { {{K_TICKADJ_NAME}},
+ {{K_TICK_NAME}},
+ {{""}},
+ };
+#else
+#define N_NAME n_name
+ static struct nlist nl[] =
+ { {K_TICKADJ_NAME},
+ {K_TICK_NAME},
+ {""},
+ };
+#endif
+#ifdef HAVE_GETBOOTFILE
+ const char *kernelname;
+#else
+ static char *kernelnames[] = {
+ "/kernel",
+ "/vmunix",
+ "/unix",
+ "/mach",
+ "/hp-ux",
+ "/386bsd",
+ "/netbsd",
+#ifdef KERNELFILE
+ KERNELFILE,
+#endif
+ NULL
+ };
+#endif
+ struct stat stbuf;
+ int vars[2];
+
+#define K_TICKADJ 0
+#define K_TICK 1
+
+ /*
+ * Check to see what to use for the object file for names and get
+ * the locations of the necessary kernel variables.
+ */
+#ifdef HAVE_GETBOOTFILE
+ kernelname = getbootfile();
+ if (kernelname &&
+ ((stat(kernelname, &stbuf) == -1) || (nlist(kernelname, nl) < 0))) {
+#else
+ for (i = 0; kernelnames[i] != NULL; i++) {
+ if (stat(kernelnames[i], &stbuf) == -1)
+ continue;
+ if (nlist(kernelnames[i], nl) >= 0)
+ break;
+ }
+ if (kernelnames[i] == NULL) {
+#endif
+ syslog(LOG_ERR,
+ "Clock init couldn't find kernel object file");
+ exit(3);
+ }
+
+ /*
+ * Read clock parameters from kernel
+ */
+ kmem = open("/dev/kmem", O_RDONLY);
+ if (kmem < 0) {
+ syslog(LOG_ERR, "Can't open /dev/kmem for reading: %m");
+#ifdef DEBUG
+ if (debug)
+ perror("/dev/kmem");
+#endif
+ exit(3);
+ }
+
+ for (i = 0; i < (sizeof(vars)/sizeof(vars[0])); i++) {
+ off_t where;
+
+ vars[i] = 0;
+ if ((where = nl[i].n_value) == 0) {
+ syslog(LOG_ERR, "Unknown kernal var %s",
+ nl[i].N_NAME);
+ continue;
+ }
+ if (lseek(kmem, where, SEEK_SET) == -1) {
+ syslog(LOG_ERR, "lseek for %s fails: %m",
+ nl[i].N_NAME);
+ continue;
+ }
+ if (read(kmem, &vars[i], sizeof(int)) != sizeof(int)) {
+ syslog(LOG_ERR, "read for %s fails: %m",
+ nl[i].N_NAME);
+ }
+ }
+ close(kmem);
+
+ *tickadj = (u_long)vars[K_TICKADJ];
+ *tick = (u_long)vars[K_TICK];
+
+#undef K_TICKADJ
+#undef K_TICK
+#undef K_TICKADJ_NAME
+#undef K_TICK_NAME
+#undef N_NAME
+}
+#endif /* SYS_UNIXWARE1 */
+#endif /* HAVE_READKMEM */
+
+#if defined(SOLARIS)&&defined(ADJTIME_IS_ACCURATE)
+/*
+ * clock_parms for Solaris 2.2 and later, with high-res timer kernel code.
+ * The clock code changed in Solaris 2.2, and tickadj went away.
+ * The good news is that ADJTIME_IS_ACCURATE and tick is available through
+ * sysconf().
+ */
+static void
+clock_parms(tickadj, tick)
+ u_long *tickadj;
+ u_long *tick;
+{
+ int hz;
+
+ hz = (int) sysconf (_SC_CLK_TCK);
+ *tick = 1000000L/hz;
+ *tickadj = (*tick/16); /* There is no tickadj, and it is only set here
+ for tvu_maxslew calculation above. Really,
+ clock_parms should return adj_precision
+ and tvu_maxslew, instead of the very
+ BSD-centric tickadj */
+
+#ifdef DEBUG
+ if (debug) printf ("Solaris tick = %d\n", *tick);
+#endif
+}
+#endif /* SOLARIS_HRTIME */
+
+
+#if defined(sgi)
+/*
+ * clock_parms - return the local clock tickadj and tick parameters
+ *
+ * The values set here were determined experimentally on a 4D/220 and
+ * an R4000-50 server under IRIX 4.0.5.
+ */
+static void
+clock_parms(tickadj, tick)
+ u_long *tickadj;
+ u_long *tick;
+{
+ *tick = 10000;
+ *tickadj = 150;
+}
+#endif /* sgi */
+
+#ifdef NOKMEM
+
+#ifndef HZ
+#define HZ 60
+#endif
+
+/*
+ * clock_parms - return the local clock tickadj and tick parameters
+ *
+ * Note that this version uses static values!
+ */
+static void
+clock_parms(tickadj, tick)
+ u_long *tickadj;
+ u_long *tick;
+{
+#ifdef RS6000
+ *tickadj = 1000;
+#else /*RS6000*/
+#if SYS_DOMAINOS
+ *tickadj = 668;
+#else /*SYS_DOMAINOS*/
+ *tickadj = 500 / HZ;
+#endif /*SYS_DOMAINOS*/
+#endif /*RS6000*/
+ *tick = 1000000L / HZ;
+
+#ifdef DEBUG
+ if (debug)
+ printf("NOTE: Using preset values for tick and tickadj !!\n");
+#endif
+}
+#endif /*NOKMEM*/
+
+#if ((defined(SOLARIS)&&!defined(ADJTIME_IS_ACCURATE))|| (defined(RS6000)&&!defined(NOKMEM))||defined(SYS_SINIXM) )
+#ifndef _SC_CLK_TCK
+#include <unistd.h>
+#endif
+/*
+ * clock_parms - return the local clock tickadj and tick parameters
+ *
+ * Note that this version grovels about in /dev/kmem to determine
+ * these values. This probably should be elsewhere.
+ */
+static void
+clock_parms(tickadj, tick)
+ u_long *tickadj;
+ u_long *tick;
+{
+ register int i;
+ int kmem;
+#define N_NAME n_name
+ static struct nlist nl[] =
+ { {"tickadj"},
+ {""},
+ };
+ static char *kernelnames[] = {
+ "/kernel/unix",
+ "/unix",
+ NULL
+ };
+ struct stat stbuf;
+ int vars[1];
+
+#define K_TICKADJ 0
+ /*
+ * Read clock parameters from kernel
+ */
+ kmem = open("/dev/kmem", O_RDONLY);
+ if (kmem < 0) {
+ syslog(LOG_ERR, "Can't open /dev/kmem for reading: %m");
+#ifdef DEBUG
+ if (debug)
+ perror("/dev/kmem");
+#endif
+ exit(3);
+ }
+
+ for (i = 0; kernelnames[i] != NULL; i++) {
+ if (stat(kernelnames[i], &stbuf) == -1)
+ continue;
+ if (nlist(kernelnames[i], nl) >= 0)
+ break;
+ }
+ if (kernelnames[i] == NULL) {
+ syslog(LOG_ERR,
+ "Clock init couldn't find kernel as either /vmunix or /unix");
+ exit(3);
+ }
+
+ for (i = 0; i < (sizeof(vars)/sizeof(vars[0])); i++) {
+ off_t where;
+
+ vars[i] = 0;
+ if ((where = nl[i].n_value) == 0) {
+ syslog(LOG_ERR, "Unknown kernal var %s",
+ nl[i].N_NAME);
+ continue;
+ }
+ if (lseek(kmem, where, SEEK_SET) == -1) {
+ syslog(LOG_ERR, "lseek for %s fails: %m",
+ nl[i].N_NAME);
+ continue;
+ }
+ if (read(kmem, &vars[i], sizeof(int)) != sizeof(int)) {
+ syslog(LOG_ERR, "read for %s fails: %m",
+ nl[i].N_NAME);
+ }
+#if defined(RS6000)
+ /*
+ * Aix requires one more round of indirection.
+ */
+ if (lseek(kmem, vars[i], SEEK_SET) == -1) {
+ syslog(LOG_ERR, "lseek for %s fails: %m",
+ nl[i].N_NAME);
+ continue;
+ }
+ if (read(kmem, &vars[i], sizeof(int)) != sizeof(int)) {
+ syslog(LOG_ERR, "read for %s fails: %m",
+ nl[i].N_NAME);
+ }
+#endif
+ }
+ close(kmem);
+
+ *tickadj = (u_long)vars[K_TICKADJ];
+ *tick = (u_long)(1000000/sysconf(_SC_CLK_TCK));
+
+#undef K_TICKADJ
+#undef N_NAME
+}
+#endif /* SOLARIS */
+
+#ifdef SYS_LINUX
+#include "sys/timex.h"
+static void
+clock_parms(tickadj, tick)
+ u_long *tickadj;
+ u_long *tick;
+{
+ struct timex txc;
+
+ txc.mode = 0;
+ __adjtimex(&txc);
+
+ *tickadj = (u_long)1; /* our adjtime is accurate */
+ *tick = (u_long)txc.tick;
+}
+#endif /* SYS_LINUX */
diff --git a/usr.sbin/xntpd/xntpd/ntp_util.c b/usr.sbin/xntpd/xntpd/ntp_util.c
new file mode 100644
index 0000000..e449276
--- /dev/null
+++ b/usr.sbin/xntpd/xntpd/ntp_util.c
@@ -0,0 +1,441 @@
+/*
+ * ntp_util.c - stuff I didn't have any other place for
+ */
+#include <stdio.h>
+#include <errno.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_unixtime.h"
+#include "ntp_filegen.h"
+#include "ntp_if.h"
+#include "ntp_stdlib.h"
+
+#ifdef DOSYNCTODR
+#include <sys/resource.h>
+#endif
+
+/*
+ * This contains odds and ends. Right now the only thing you'll find
+ * in here is the hourly stats printer and some code to support rereading
+ * the keys file, but I may eventually put other things in here such as
+ * code to do something with the leap bits.
+ */
+
+/*
+ * Name of the keys file
+ */
+static char *key_file_name;
+
+/*
+ * The name of the drift_comp file and the temporary.
+ */
+static char *stats_drift_file;
+static char *stats_temp_file;
+
+/*
+ * Statistics file stuff
+ */
+#ifndef NTP_VAR
+#define NTP_VAR "/var/NTP/" /* NOTE the trailing '/' */
+#endif
+
+#ifndef MAXPATHLEN
+#define MAXPATHLEN 1024
+#endif
+
+static char statsdir[MAXPATHLEN] = NTP_VAR;
+
+static FILEGEN peerstats;
+static FILEGEN loopstats;
+static FILEGEN clockstats;
+/*
+ * We query the errno to see what kind of error occured
+ * when opening the drift file.
+ */
+extern int errno;
+
+/*
+ * This controls whether stats are written to the fileset. Provided
+ * so that xntpdc can turn off stats when the file system fills up.
+ */
+int stats_control;
+
+#ifdef DEBUG
+extern int debug;
+#endif
+
+/*
+ * init_util - initialize the utilities
+ */
+void
+init_util()
+{
+ stats_drift_file = 0;
+ stats_temp_file = 0;
+ key_file_name = 0;
+
+#define PEERNAME "peerstats"
+#define LOOPNAME "loopstats"
+#define CLOCKNAME "clockstats"
+ peerstats.fp = NULL;
+ peerstats.prefix = &statsdir[0];
+ peerstats.basename = emalloc(strlen(PEERNAME)+1);
+ strcpy(peerstats.basename, PEERNAME);
+ peerstats.id = 0;
+ peerstats.type = FILEGEN_DAY;
+ peerstats.flag = FGEN_FLAG_LINK; /* not yet enabled !!*/
+ filegen_register("peerstats", &peerstats);
+
+ loopstats.fp = NULL;
+ loopstats.prefix = &statsdir[0];
+ loopstats.basename = emalloc(strlen(LOOPNAME)+1);
+ strcpy(loopstats.basename, LOOPNAME);
+ loopstats.id = 0;
+ loopstats.type = FILEGEN_DAY;
+ loopstats.flag = FGEN_FLAG_LINK; /* not yet enabled !!*/
+ filegen_register("loopstats", &loopstats);
+
+ clockstats.fp = NULL;
+ clockstats.prefix = &statsdir[0];
+ clockstats.basename = emalloc(strlen(CLOCKNAME)+1);
+ strcpy(clockstats.basename, CLOCKNAME);
+ clockstats.id = 0;
+ clockstats.type = FILEGEN_DAY;
+ clockstats.flag = FGEN_FLAG_LINK; /* not yet enabled !!*/
+ filegen_register("clockstats", &clockstats);
+
+#undef PEERNAME
+#undef LOOPNAME
+#undef CLOCKNAME
+
+}
+
+
+/*
+ * hourly_stats - print some interesting stats
+ */
+void
+hourly_stats()
+{
+ FILE *fp;
+ extern l_fp last_offset;
+ extern s_fp drift_comp;
+ extern u_char sys_poll;
+ extern int pll_status;
+
+#ifdef DOSYNCTODR
+ struct timeval tv;
+ int o_prio;
+
+ /*
+ * Sometimes having a Sun can be a drag.
+ *
+ * The kernel variable dosynctodr controls whether the system's
+ * soft clock is kept in sync with the battery clock. If it
+ * is zero, then the soft clock is not synced, and the battery
+ * clock is simply left to rot. That means that when the system
+ * reboots, the battery clock (which has probably gone wacky)
+ * sets the soft clock. That means xntpd starts off with a very
+ * confused idea of what time it is. It then takes a large
+ * amount of time to figure out just how wacky the battery clock
+ * has made things drift, etc, etc. The solution is to make the
+ * battery clock sync up to system time. The way to do THAT is
+ * to simply set the time of day to the current time of day, but
+ * as quickly as possible. This may, or may not be a sensible
+ * thing to do.
+ *
+ * CAVEAT: settimeofday() steps the sun clock by about 800 us,
+ * so setting DOSYNCTODR seems a bad idea in the
+ * case of us resolution
+ */
+
+ o_prio=getpriority(PRIO_PROCESS,0); /* Save setting */
+ if (setpriority(PRIO_PROCESS,0,-20) != 0) /* overdrive */
+ {
+ syslog(LOG_ERR, "can't elevate priority: %m");
+ goto skip;
+ }
+ GETTIMEOFDAY(&tv,(struct timezone *)NULL);
+ if (SETTIMEOFDAY(&tv,(struct timezone *)NULL) != 0)
+ {
+ syslog(LOG_ERR, "can't sync battery time: %m");
+ }
+ setpriority(PRIO_PROCESS,0,o_prio); /* downshift */
+
+ skip:
+#endif
+
+ syslog(LOG_INFO, "offset %s freq %s poll %d",
+ lfptoa(&last_offset, 6), fptoa(drift_comp, 3),
+ sys_poll);
+
+ if (stats_drift_file != 0) {
+ if ((fp = fopen(stats_temp_file, "w")) == NULL) {
+ syslog(LOG_ERR, "can't open %s: %m",
+ stats_temp_file);
+ return;
+ }
+ fprintf(fp, "%s %x\n", fptoa(drift_comp, 3),
+ pll_status);
+ (void)fclose(fp);
+ /* atomic */
+ (void) rename(stats_temp_file, stats_drift_file);
+ }
+}
+
+
+/*
+ * stats_config - configure the stats operation
+ */
+void
+stats_config(item, value)
+ int item;
+ char *value; /* only one type so far */
+{
+ FILE *fp;
+ char buf[128];
+ l_fp old_drift;
+ int temp = 0;
+ int len;
+
+ switch(item) {
+ case STATS_FREQ_FILE:
+ if (stats_drift_file != 0) {
+ (void) free(stats_drift_file);
+ (void) free(stats_temp_file);
+ stats_drift_file = 0;
+ stats_temp_file = 0;
+ }
+
+ if (value == 0 || (len = strlen(value)) == 0)
+ break;
+
+ stats_drift_file = emalloc((u_int)(len + 1));
+ stats_temp_file = emalloc((u_int)(len +
+ sizeof(".TEMP")));
+ memmove(stats_drift_file, value, len+1);
+ memmove(stats_temp_file, value, len);
+ memmove(stats_temp_file + len, ".TEMP",
+ sizeof(".TEMP"));
+ L_CLR(&old_drift);
+
+ /*
+ * Open drift file and read frequency and mode.
+ */
+ if ((fp = fopen(stats_drift_file, "r")) == NULL) {
+ if (errno != ENOENT)
+ syslog(LOG_ERR, "can't open %s: %m",
+ stats_drift_file);
+ loop_config(LOOP_DRIFTCOMP, &old_drift, 0);
+ break;
+ }
+
+ if (fscanf(fp, "%s %x", buf, &temp) == 0) {
+ syslog(LOG_ERR, "can't read %s: %m",
+ stats_drift_file);
+ (void) fclose(fp);
+ loop_config(LOOP_DRIFTCOMP, &old_drift, 0);
+ break;
+ }
+ (void) fclose(fp);
+ if (!atolfp(buf, &old_drift)) {
+ syslog(LOG_ERR, "drift value %s invalid", buf);
+ break;
+ }
+ loop_config(LOOP_DRIFTCOMP, &old_drift, temp);
+ break;
+
+ case STATS_STATSDIR:
+ if (strlen(value) >= sizeof(statsdir)) {
+ syslog(LOG_ERR,
+ "value for statsdir too long (>%d, sigh)",
+ sizeof(statsdir)-1);
+ } else {
+ l_fp now;
+
+ gettstamp(&now);
+ strcpy(statsdir,value);
+ if(peerstats.prefix == &statsdir[0] &&
+ peerstats.fp != NULL) {
+ fclose(peerstats.fp);
+ peerstats.fp = NULL;
+ filegen_setup(&peerstats, now.l_ui);
+ }
+ if(loopstats.prefix == &statsdir[0] &&
+ loopstats.fp != NULL) {
+ fclose(loopstats.fp);
+ loopstats.fp = NULL;
+ filegen_setup(&loopstats, now.l_ui);
+ }
+ if(clockstats.prefix == &statsdir[0] &&
+ clockstats.fp != NULL) {
+ fclose(clockstats.fp);
+ clockstats.fp = NULL;
+ filegen_setup(&clockstats, now.l_ui);
+ }
+ }
+ break;
+
+ case STATS_PID_FILE:
+ if ((fp = fopen(value, "w")) == NULL) {
+ syslog(LOG_ERR, "Can't open %s: %m", value);
+ break;
+ }
+ fprintf(fp, "%d", getpid());
+ fclose(fp);;
+ break;
+
+ default:
+ /* oh well */
+ break;
+ }
+}
+
+/*
+ * record_peer_stats - write peer statistics to file
+ *
+ * file format:
+ * day (mjd)
+ * time (s past midnight)
+ * peer (ip address)
+ * peer status word (hex)
+ * peer offset (s)
+ * peer delay (s)
+ * peer dispersion (s)
+ */
+void
+record_peer_stats(addr, status, offset, delay, dispersion)
+ struct sockaddr_in *addr;
+ int status;
+ l_fp *offset;
+ s_fp delay;
+ u_fp dispersion;
+{
+ struct timeval tv;
+ u_long day, sec, msec;
+
+ if (!stats_control)
+ return;
+ GETTIMEOFDAY(&tv, (struct timezone *)NULL);
+ day = tv.tv_sec / 86400 + MJD_1970;
+ sec = tv.tv_sec % 86400;
+ msec = tv.tv_usec / 1000;
+
+ filegen_setup(&peerstats, (u_long)(tv.tv_sec + JAN_1970));
+ if (peerstats.fp != NULL) {
+ fprintf(peerstats.fp, "%lu %lu.%03lu %s %x %s %s %s\n",
+ day, sec, msec, ntoa(addr), status, lfptoa(offset, 6),
+ fptoa(delay, 5), ufptoa(dispersion, 5));
+ fflush(peerstats.fp);
+ }
+}
+/*
+ * record_loop_stats - write loop filter statistics to file
+ *
+ * file format:
+ * day (mjd)
+ * time (s past midnight)
+ * offset (s)
+ * frequency (approx ppm)
+ * time constant (log base 2)
+ */
+void
+record_loop_stats(offset, freq, poll)
+ l_fp *offset;
+ s_fp freq;
+ u_char poll;
+{
+ struct timeval tv;
+ u_long day, sec, msec;
+
+ if (!stats_control)
+ return;
+ GETTIMEOFDAY(&tv, (struct timezone *)NULL);
+ day = tv.tv_sec / 86400 + MJD_1970;
+ sec = tv.tv_sec % 86400;
+ msec = tv.tv_usec / 1000;
+
+ filegen_setup(&loopstats, (u_long)(tv.tv_sec + JAN_1970));
+ if (loopstats.fp != NULL) {
+ fprintf(loopstats.fp, "%lu %lu.%03lu %s %s %d\n",
+ day, sec, msec, lfptoa(offset, 6),
+ fptoa(freq, 4), poll);
+ fflush(loopstats.fp);
+ }
+}
+
+/*
+ * record_clock_stats - write clock statistics to file
+ *
+ * file format:
+ * day (mjd)
+ * time (s past midnight)
+ * peer (ip address)
+ * text message
+ */
+void
+record_clock_stats(addr, text)
+ struct sockaddr_in *addr;
+ char *text;
+{
+ struct timeval tv;
+ u_long day, sec, msec;
+
+ if (!stats_control)
+ return;
+ GETTIMEOFDAY(&tv, (struct timezone *)NULL);
+ day = tv.tv_sec / 86400 + MJD_1970;
+ sec = tv.tv_sec % 86400;
+ msec = tv.tv_usec / 1000;
+
+ filegen_setup(&clockstats, (u_long)(tv.tv_sec + JAN_1970));
+ if (clockstats.fp != NULL) {
+ fprintf(clockstats.fp, "%lu %lu.%03lu %s %s\n",
+ day, sec, msec, ntoa(addr), text);
+ fflush(clockstats.fp);
+ }
+}
+
+/*
+ * getauthkeys - read the authentication keys from the specified file
+ */
+void
+getauthkeys(keyfile)
+ char *keyfile;
+{
+ int len;
+
+ len = strlen(keyfile);
+ if (len == 0)
+ return;
+
+ if (key_file_name != 0) {
+ if (len > (int)strlen(key_file_name)) {
+ (void) free(key_file_name);
+ key_file_name = 0;
+ }
+ }
+
+ if (key_file_name == 0)
+ key_file_name = emalloc((u_int)(len + 1));
+
+ memmove(key_file_name, keyfile, len+1);
+
+ authreadkeys(key_file_name);
+}
+
+
+/*
+ * rereadkeys - read the authentication key file over again.
+ */
+void
+rereadkeys()
+{
+ if (key_file_name != 0)
+ authreadkeys(key_file_name);
+}
diff --git a/usr.sbin/xntpd/xntpd/ntpd.c b/usr.sbin/xntpd/xntpd/ntpd.c
new file mode 100644
index 0000000..ae06f56
--- /dev/null
+++ b/usr.sbin/xntpd/xntpd/ntpd.c
@@ -0,0 +1,461 @@
+/*
+ * ntpd.c - main program for the fixed point NTP daemon
+ */
+#include <stdio.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/param.h>
+#include <sys/signal.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#if defined(SYS_HPUX)
+#include <sys/lock.h>
+#include <sys/rtprio.h>
+#endif
+
+#if defined(SYS_SVR4) || defined (SYS_UNIXWARE1)
+#include <termios.h>
+#endif
+
+#if (defined(SYS_SOLARIS)&&!defined(bsd)) || defined(__svr4__)
+#include <termios.h>
+#endif
+
+#include "ntpd.h"
+#include "ntp_select.h"
+#include "ntp_io.h"
+#include "ntp_stdlib.h"
+
+#ifdef LOCK_PROCESS
+#ifdef SYS_SOLARIS
+#include <sys/mman.h>
+#else
+#include <sys/lock.h>
+#endif
+#endif
+
+/*
+ * Signals we catch for debugging. If not debugging we ignore them.
+ */
+#define MOREDEBUGSIG SIGUSR1
+#define LESSDEBUGSIG SIGUSR2
+
+/*
+ * Signals which terminate us gracefully.
+ */
+#define SIGDIE1 SIGHUP
+#define SIGDIE2 SIGINT
+#define SIGDIE3 SIGQUIT
+#define SIGDIE4 SIGTERM
+
+/*
+ * Scheduling priority we run at
+ */
+#define NTPD_PRIO (-12)
+
+/*
+ * Debugging flag
+ */
+int debug;
+
+/*
+ * Initializing flag. All async routines watch this and only do their
+ * thing when it is clear.
+ */
+int initializing;
+
+/*
+ * Version declaration
+ */
+extern char *Version;
+
+/*
+ * Alarm flag. Imported from timer module
+ */
+extern int alarm_flag;
+
+#if !defined(SYS_386BSD) && !defined(SYS_BSDI) && !defined(SYS_44BSD)
+/*
+ * We put this here, since the argument profile is syscall-specific
+ */
+extern int syscall P((int, struct timeval *, struct timeval *));
+#endif /* !SYS_386BSD */
+
+#ifdef SIGDIE1
+static RETSIGTYPE finish P((int));
+#endif /* SIGDIE1 */
+
+#ifdef DEBUG
+static RETSIGTYPE moredebug P((int));
+static RETSIGTYPE lessdebug P((int));
+#endif /* DEBUG */
+
+/*
+ * Main program. Initialize us, disconnect us from the tty if necessary,
+ * and loop waiting for I/O and/or timer expiries.
+ */
+void
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ char *cp;
+ int was_alarmed;
+ struct recvbuf *rbuflist;
+ struct recvbuf *rbuf;
+
+ initializing = 1; /* mark that we are initializing */
+ debug = 0; /* no debugging by default */
+
+ getstartup(argc, argv); /* startup configuration, may set debug */
+
+#ifndef NODETACH
+ /*
+ * Detach us from the terminal. May need an #ifndef GIZMO.
+ */
+#ifdef DEBUG
+ if (!debug) {
+#endif /* DEBUG */
+#undef BSD19906
+#if defined(BSD)&&!defined(sun)&&!defined(SYS_SINIXM)
+#if (BSD >= 199006 && !defined(i386))
+#define BSD19906
+#endif /* BSD... */
+#endif /* BSD sun */
+#if defined(BSD19906) || defined(SYS_44BSD)
+ daemon(0, 0);
+#else /* BSD19906 */
+ if (fork())
+ exit(0);
+
+ {
+ u_long s;
+ int max_fd;
+#if defined(NTP_POSIX_SOURCE) && !defined(SYS_386BSD)
+ max_fd = sysconf(_SC_OPEN_MAX);
+#else /* NTP_POSIX_SOURCE */
+ max_fd = getdtablesize();
+#endif /* NTP_POSIX_SOURCE */
+ for (s = 0; s < max_fd; s++)
+ (void) close(s);
+ (void) open("/", 0);
+ (void) dup2(0, 1);
+ (void) dup2(0, 2);
+#ifdef NTP_POSIX_SOURCE
+#if defined(SOLARIS) || defined(SYS_PTX) || defined(SYS_AUX3) || defined(SYS_AIX) || defined(SYS_ULTRIX)
+ (void) setsid();
+#else
+ (void) setpgid(0, 0);
+#endif
+#else /* NTP_POSIX_SOURCE */
+#ifdef HAVE_ATT_SETPGRP
+ (void) setpgrp();
+#else /* HAVE_ATT_SETPGRP */
+ (void) setpgrp(0, getpid());
+#endif /* HAVE_ATT_SETPGRP */
+#if defined(SYS_HPUX)
+ if (fork())
+ exit(0);
+#else /* SYS_HPUX */
+#ifdef SYS_DOMAINOS
+/*
+ * This breaks... the program fails to listen to any packets coming
+ * in on the UDP socket. So how do you break terminal affiliation?
+ */
+#else /* SYS_DOMAINOS */
+ {
+ int fid;
+
+ fid = open("/dev/tty", 2);
+ if (fid >= 0) {
+ (void) ioctl(fid, (u_long) TIOCNOTTY,
+ (char *) 0);
+ (void) close(fid);
+ }
+ (void) setpgrp(0, getpid());
+ }
+#endif /* SYS_DOMAINOS */
+#endif /* SYS_HPUX */
+#endif /* NTP_POSIX_SOURCE */
+ }
+#endif /* BSD19906 */
+#ifdef DEBUG
+ }
+#endif /* DEBUG */
+#endif /* NODETACH */
+
+ /*
+ * Logging. This may actually work on the gizmo board. Find a name
+ * to log with by using the basename of argv[0]
+ */
+ cp = strrchr(argv[0], '/');
+ if (cp == 0)
+ cp = argv[0];
+ else
+ cp++;
+
+#ifndef LOG_DAEMON
+ openlog(cp, LOG_PID);
+#else
+
+#ifndef LOG_NTP
+#define LOG_NTP LOG_DAEMON
+#endif
+ openlog(cp, LOG_PID | LOG_NDELAY, LOG_NTP);
+#ifdef DEBUG
+ if (debug)
+ setlogmask(LOG_UPTO(LOG_DEBUG));
+ else
+#endif /* DEBUG */
+ setlogmask(LOG_UPTO(LOG_DEBUG)); /* @@@ was INFO */
+#endif /* LOG_DAEMON */
+
+ syslog(LOG_NOTICE, Version);
+
+
+#if defined(SYS_HPUX)
+ /*
+ * Lock text into ram, set real time priority
+ */
+ if (plock(TXTLOCK) < 0)
+ syslog(LOG_ERR, "plock() error: %m");
+ if (rtprio(0, 120) < 0)
+ syslog(LOG_ERR, "rtprio() error: %m");
+#else
+#if defined(LOCK_PROCESS)
+#if defined(MCL_CURRENT) && defined(MCL_FUTURE)
+ /*
+ * lock the process into memory
+ */
+ if (mlockall(MCL_CURRENT|MCL_FUTURE) < 0)
+ syslog(LOG_ERR, "mlockall(): %m");
+#else
+#if defined(PROCLOCK)
+ /*
+ * lock the process into memory
+ */
+ if (plock(PROCLOCK) < 0)
+ syslog(LOG_ERR, "plock(): %m");
+#endif
+#endif
+#endif
+#if defined(NTPD_PRIO) && NTPD_PRIO != 0
+ /*
+ * Set the priority.
+ */
+#ifdef HAVE_ATT_NICE
+ nice (NTPD_PRIO);
+#endif /* HAVE_ATT_NICE */
+#ifdef HAVE_BSD_NICE
+ (void) setpriority(PRIO_PROCESS, 0, NTPD_PRIO);
+#endif /* HAVE_BSD_NICE */
+
+#endif /* !PROCLOCK || !LOCK_PROCESS */
+#endif /* SYS_HPUX */
+
+ /*
+ * Set up signals we pay attention to locally.
+ */
+#ifdef SIGDIE1
+ (void) signal_no_reset(SIGDIE1, finish);
+#endif /* SIGDIE1 */
+#ifdef SIGDIE2
+ (void) signal_no_reset(SIGDIE2, finish);
+#endif /* SIGDIE2 */
+#ifdef SIGDIE3
+ (void) signal_no_reset(SIGDIE3, finish);
+#endif /* SIGDIE3 */
+#ifdef SIGDIE4
+ (void) signal_no_reset(SIGDIE4, finish);
+#endif /* SIGDIE4 */
+
+#ifdef DEBUG
+ (void) signal_no_reset(MOREDEBUGSIG, moredebug);
+ (void) signal_no_reset(LESSDEBUGSIG, lessdebug);
+#else
+ (void) signal_no_reset(MOREDEBUGSIG, SIG_IGN);
+ (void) signal_no_reset(LESSDEBUGSIG, SIG_IGN);
+#endif /* DEBUG */
+
+ /*
+ * Set up signals we should never pay attention to.
+ */
+#ifdef SIGPIPE
+ (void) signal_no_reset(SIGPIPE, SIG_IGN);
+#endif /* SIGPIPE */
+
+ /*
+ * Call the init_ routines to initialize the data structures.
+ * Note that init_systime() may run a protocol to get a crude
+ * estimate of the time as an NTP client when running on the
+ * gizmo board. It is important that this be run before
+ * init_subs() since the latter uses the time of day to seed
+ * the random number generator. That is not the only
+ * dependency between these, either, be real careful about
+ * reordering.
+ */
+ init_auth();
+ init_util();
+ init_restrict();
+ init_mon();
+ init_systime();
+ init_timer();
+ init_lib();
+ init_random();
+ init_request();
+ init_control();
+ init_leap();
+ init_peer();
+#ifdef REFCLOCK
+ init_refclock();
+#endif
+ init_proto();
+ init_io();
+ init_loopfilter();
+
+ mon_start(MON_ON); /* monitor on by default now */
+ /* turn off in config if unwanted */
+
+ /*
+ * Get configuration. This (including argument list parsing) is
+ * done in a separate module since this will definitely be different
+ * for the gizmo board.
+ */
+ getconfig(argc, argv);
+ initializing = 0;
+
+ /*
+ * Report that we're up to any trappers
+ */
+ report_event(EVNT_SYSRESTART, (struct peer *)0);
+
+ /*
+ * Use select() on all on all input fd's for unlimited
+ * time. select() will terminate on SIGALARM or on the
+ * reception of input. Using select() means we can't do
+ * robust signal handling and we get a potential race
+ * between checking for alarms and doing the select().
+ * Mostly harmless, I think.
+ */
+ was_alarmed = 0;
+ rbuflist = (struct recvbuf *)0;
+ for (;;) {
+#ifndef HAVE_SIGNALED_IO
+ extern fd_set activefds;
+ extern int maxactivefd;
+
+ fd_set rdfdes;
+ int nfound;
+#else
+ block_io_and_alarm();
+#endif
+
+
+ rbuflist = getrecvbufs(); /* get received buffers */
+ if (alarm_flag) { /* alarmed? */
+ was_alarmed = 1;
+ alarm_flag = 0;
+ }
+
+ if (!was_alarmed && rbuflist == (struct recvbuf *)0) {
+ /*
+ * Nothing to do. Wait for something.
+ */
+#ifndef HAVE_SIGNALED_IO
+ rdfdes = activefds;
+ nfound = select(maxactivefd+1, &rdfdes, (fd_set *)0,
+ (fd_set *)0, (struct timeval *)0);
+ if (nfound > 0) {
+ l_fp ts;
+
+ get_systime(&ts);
+ (void)input_handler(&ts);
+ } else if (nfound == -1 && errno != EINTR)
+ syslog(LOG_ERR, "select() error: %m");
+#else
+ wait_for_signal();
+#endif
+ if (alarm_flag) { /* alarmed? */
+ was_alarmed = 1;
+ alarm_flag = 0;
+ }
+ rbuflist = getrecvbufs(); /* get received buffers */
+ }
+#ifdef HAVE_SIGNALED_IO
+ unblock_io_and_alarm();
+#endif
+
+ /*
+ * Out here, signals are unblocked. Call timer routine
+ * to process expiry.
+ */
+ if (was_alarmed) {
+ timer();
+ was_alarmed = 0;
+ }
+
+ /*
+ * Call the data procedure to handle each received
+ * packet.
+ */
+ while (rbuflist != (struct recvbuf *)0) {
+ rbuf = rbuflist;
+ rbuflist = rbuf->next;
+ (rbuf->receiver)(rbuf);
+ freerecvbuf(rbuf);
+ }
+ /*
+ * Go around again
+ */
+ }
+}
+
+
+#ifdef SIGDIE1
+/*
+ * finish - exit gracefully
+ */
+static RETSIGTYPE
+finish(sig)
+int sig;
+{
+
+ /*
+ * Log any useful info before exiting.
+ */
+#ifdef notdef
+ log_exit_stats();
+#endif
+ exit(0);
+}
+#endif /* SIGDIE1 */
+
+
+#ifdef DEBUG
+/*
+ * moredebug - increase debugging verbosity
+ */
+static RETSIGTYPE
+moredebug(sig)
+int sig;
+{
+ if (debug < 255) {
+ debug++;
+ syslog(LOG_DEBUG, "debug raised to %d", debug);
+ }
+}
+
+/*
+ * lessdebug - decrease debugging verbosity
+ */
+static RETSIGTYPE
+lessdebug(sig)
+int sig;
+{
+ if (debug > 0) {
+ debug--;
+ syslog(LOG_DEBUG, "debug lowered to %d", debug);
+ }
+}
+#endif /* DEBUG */
diff --git a/usr.sbin/xntpd/xntpd/refclock_acts.c b/usr.sbin/xntpd/xntpd/refclock_acts.c
new file mode 100644
index 0000000..39ff433
--- /dev/null
+++ b/usr.sbin/xntpd/xntpd/refclock_acts.c
@@ -0,0 +1,895 @@
+/*
+ * refclock_acts - clock driver for the NIST Automated Computer Time
+ * Service aka Amalgamated Containerized Trash Service (ACTS)
+ */
+#if defined(REFCLOCK) && defined(ACTS)
+
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/time.h>
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_unixtime.h"
+#include "ntp_refclock.h"
+#include "ntp_stdlib.h"
+
+/*
+ * This driver supports the NIST Automated Computer Time Service (ACTS).
+ * It periodically dials a prespecified telephone number, receives the
+ * NIST timecode data and calculates the local clock correction. It is
+ * designed primarily for use as a backup when neither a radio clock nor
+ * connectivity to Internet time servers is available. For the best
+ * accuracy, the individual telephone line/modem delay needs to be
+ * calibrated using outside sources.
+ *
+ * The ACTS is located at NIST Boulder, CO, telephone 303 494 4774. A
+ * toll call from a residence telephone in Newark, DE, costs between 14
+ * and 27 cents, depending on time of day, and from a campus telephone
+ * between 3 and 4 cents, although it is not clear what carrier and time
+ * of day discounts apply in this case. The modem dial string will
+ * differ depending on local telephone configuration, etc., and is
+ * specified by the phone command in the configuration file. The
+ * argument to this command is an AT command for a Hayes compatible
+ * modem.
+ *
+ * The accuracy produced by this driver should be in the range of a
+ * millisecond or two, but may need correction due to the delay
+ * characteristics of the individual modem involved. For undetermined
+ * reasons, some modems work with the ACTS echo-delay measurement scheme
+ * and some don't. This driver tries to do the best it can with what it
+ * gets. Initial experiments with a Practical Peripherals 9600SA modem
+ * here in Delaware suggest an accuracy of a millisecond or two can be
+ * achieved without the scheme by using a fudge time1 value of 65.0 ms.
+ * In either case, the dispersion for a single call involving ten
+ * samples is about 1.3 ms.
+ *
+ * The driver can operate in either of three modes, as determined by
+ * the mode parameter in the server configuration command. In mode 0
+ * (automatic) the driver operates continuously at intervals depending
+ * on the prediction error, as measured by the driver, usually in the
+ * order of several hours. In mode 1 (backup) the driver is enabled in
+ * automatic mode only when no other source of synchronization is
+ * available and when more than MAXOUTAGE (3600 s) have elapsed since
+ * last synchronized by other sources. In mode 2 (manual) the driver
+ * operates only when enabled using a fudge flags switch, as described
+ * below.
+ *
+ * For reliable call management, this driver requires a 1200-bps modem
+ * with a Hayes-compatible command set and control over the modem data
+ * terminal ready (DTR) control line. Present restrictions require the
+ * use of a POSIX-compatible programming interface, although other
+ * interfaces may work as well. The modem setup string is hard-coded in
+ * the driver and may require changes for nonstandard modems or special
+ * circumstances.
+ *
+ * Further information can be found in the README.refclock file in the
+ * xntp3 distribution.
+ *
+ * Fudge Factors
+ *
+ * Ordinarily, the propagation time correction is computed automatically
+ * by ACTS and the driver. When this is not possible or erratic due to
+ * individual modem characteristics, the fudge flag2 switch should be
+ * set to disable the ACTS echo-delay scheme. In any case, the fudge
+ * time1 parameter can be used to adjust the propagation delay as
+ * required.
+ *
+ * The ACTS call interval is determined in one of three ways. In manual
+ * mode a call is initiated by setting fudge flag1 using xntpdc, either
+ * manually or via a cron job. In AUTO mode this flag is set by the peer
+ * timer, which is controlled by the sys_poll variable in response to
+ * measured errors. In backup mode the driver is ordinarily asleep, but
+ * awakes (in auto mode) if all other synchronization sources are lost.
+ * In either auto or backup modes, the call interval increases as long
+ * as the measured errors do not exceed the value of the fudge time2
+ * parameter.
+ *
+ * When the fudge flag1 is set, the ACTS calling program is activated.
+ * This program dials each number listed in the phones command of the
+ * configuration file in turn. If a call attempt fails, the next number
+ * in the list is dialed. The fudge flag1 and counter are reset and the
+ * calling program terminated if (a) a valid clock update has been
+ * determined, (b) no more numbers remain in the list, (c) a device
+ * fault or timeout occurs or (d) fudge flag1 is reset manually using
+ * xntpdc.
+ *
+ * In automatic and backup modes, the driver determines the call
+ * interval using a procedure depending on the measured prediction
+ * error and the fudge time2 parameter. If the error exceeds time2 for a
+ * number of times depending on the current interval, the interval is
+ * decreased, but not less than about 1000 s. If the error is less than
+ * time2 for some number of times, the interval is increased, but not
+ * more than about 18 h. With the default value of zero for fudge time2,
+ * the interval will increase from 1000 s to the 4000-8000-s range, in
+ * which the expected accuracy should be in the 1-2-ms range. Setting
+ * fudge time2 to a large value, like 0.1 s, may result in errors of
+ * that order, but increase the call interval to the maximum. The exact
+ * value for each configuration will depend on the modem and operating
+ * system involved, so some experimentation may be necessary.
+ */
+
+/*
+ * DESCRIPTION OF THE AUTOMATED COMPUTER TELEPHONE SERVICE (ACTS)
+ * (reformatted from ACTS on-line computer help information)
+ *
+ * The following is transmitted (at 1200 baud) following completion of
+ * the telephone connection.
+ *
+ * National Institute of Standards and Technology
+ * Telephone Time Service, Generator 3B
+ * Enter question mark "?" for HELP
+ * D L D
+ * MJD YR MO DA H M S ST S UT1 msADV <OTM>
+ * 47999 90-04-18 21:39:15 50 0 +.1 045.0 UTC(NIST) *
+ * 47999 90-04-18 21:39:16 50 0 +.1 045.0 UTC(NIST) *
+ * 47999 90-04-18 21:39:17 50 0 +.1 045.0 UTC(NIST) *
+ * 47999 90-04-18 21:39:18 50 0 +.1 045.0 UTC(NIST) *
+ * 47999 90-04-18 21:39:19 50 0 +.1 037.6 UTC(NIST) #
+ * 47999 90-04-18 21:39:20 50 0 +.1 037.6 UTC(NIST) #
+ * etc..etc...etc.......
+ *
+ * UTC = Universal Time Coordinated, the official world time referred to
+ * the zero meridian.
+ *
+ * DST Daylight savings time characters, valid for the continental
+ * U.S., are set as follows:
+ *
+ * 00 We are on standard time (ST).
+ * 01-49 Now on DST, go to ST when your local time is 2:00 am and
+ * the count is 01. The count is decremented daily at 00
+ * (UTC).
+ * 50 We are on DST.
+ * 51-99 Now on ST, go to DST when your local time is 2:00 am and
+ * the count is 51. The count is decremented daily at 00
+ * (UTC).
+ *
+ * The two DST characters provide up to 48 days advance notice of a
+ * change in time. The count remains at 00 or 50 at other times.
+ *
+ * LS Leap second flag is set to "1" to indicate that a leap second is
+ * to be added as 23:59:60 (UTC) on the last day of the current UTC
+ * month. The LS flag will be reset to "0" starting with 23:59:60
+ * (UTC). The flag will remain on for the entire month before the
+ * second is added. Leap seconds are added as needed at the end of
+ * any month. Usually June and/or December are chosen.
+ *
+ * The leap second flag will be set to a "2" to indicate that a
+ * leap second is to be deleted at 23:59:58--00:00:00 on the last
+ * day of the current month. (This latter provision is included per
+ * international recommendation, however it is not likely to be
+ * required in the near future.)
+ *
+ * DUT1 Approximate difference between earth rotation time (UT1) and
+ * UTC, in steps of 0.1 second: DUT1 = UT1 - UTC.
+ *
+ * MJD Modified Julian Date, often used to tag certain scientific data.
+ *
+ * The full time format is sent at 1200 baud, 8 bit, 1 stop, no parity.
+ * The format at 300 Baud is also 8 bit, 1 stop, no parity. At 300 Baud
+ * the MJD and DUT1 values are deleted and the time is transmitted only
+ * on even seconds.
+ *
+ * Maximum on line time will be 56 seconds. If all lines are busy at any
+ * time, the oldest call will be terminated if it has been on line more
+ * than 28 seconds, otherwise, the call that first reaches 28 seconds
+ * will be terminated.
+ *
+ * Current time is valid at the "on-time" marker (OTM), either "*" or
+ * "#". The nominal on-time marker (*) will be transmitted 45 ms early
+ * to account for the 8 ms required to send 1 character at 1200 Baud,
+ * plus an additional 7 ms for delay from NIST to the user, and
+ * approximately 30 ms "scrambler" delay inherent in 1200 Baud modems.
+ * If the caller echoes all characters, NIST will measure the round trip
+ * delay and advance the on-time marker so that the midpoint of the stop
+ * bit arrives at the user on time. The amount of msADV will reflect the
+ * actual required advance in milliseconds and the OTM will be a "#".
+ *
+ * (The NIST system requires 4 or 5 consecutive delay measurements which
+ * are consistent before switching from "*" to "#". If the user has a
+ * 1200 Baud modem with the same internal delay as that used by NIST,
+ * then the "#" OTM should arrive at the user within +-2 ms of the
+ * correct time.
+ *
+ * However, NIST has studied different brands of 1200 Baud modems and
+ * found internal delays from 24 ms to 40 ms and offsets of the "#" OTM
+ * of +-10 ms. For many computer users, +-10 ms accuracy should be more
+ * than adequate since many computer internal clocks can only be set
+ * with granularity of 20 to 50 ms. In any case, the repeatability of
+ * the offset for the "#" OTM should be within +-2 ms, if the dial-up
+ * path is reciprocal and the user doesn't change the brand or model of
+ * modem used.
+ *
+ * This should be true even if the dial-up path on one day is a land-
+ * line of less than 40 ms (one way) and on the next day is a satellite
+ * link of 260 to 300 ms. In the rare event that the path is one way by
+ * satellite and the other way by land line with a round trip
+ * measurement in the range of 90 to 260 ms, the OTM will remain a "*"
+ * indicating 45 ms advance.
+ *
+ * For user comments write:
+ * NIST-ACTS
+ * Time and Frequency Division
+ * Mail Stop 847
+ * 325 Broadway
+ * Boulder, CO 80303
+ *
+ * Software for setting (PC)DOS compatable machines is available on a
+ * 360-kbyte diskette for $35.00 from: NIST Office of Standard Reference
+ * Materials B311-Chemistry Bldg, NIST, Gaithersburg, MD, 20899, (301)
+ * 975-6776
+ */
+
+/*
+ * Interface definitions
+ */
+#define DEVICE "/dev/acts%d" /* device name and unit */
+#define SPEED232 B1200 /* uart speed (1200 cowardly baud) */
+#define PRECISION (-10) /* precision assumed (about 1 ms) */
+#define REFID "ACTS" /* reference ID */
+#define DESCRIPTION "NIST Automated Computer Time Service" /* WRU */
+
+#define MODE_AUTO 0 /* automatic mode */
+#define MODE_BACKUP 1 /* backup mode */
+#define MODE_MANUAL 2 /* manual mode */
+
+#define NSAMPLES 3 /* stages of median filter */
+#define MSGCNT 10 /* we need this many ACTS messages */
+#define SMAX 80 /* max token string length */
+#define LENCODE 50 /* length of valid timecode string */
+#define ACTS_MINPOLL 10 /* log2 min poll interval (1024 s) */
+#define ACTS_MAXPOLL 14 /* log2 max poll interval (16384 s) */
+#define MAXOUTAGE 3600 /* max before ACTS kicks in (s) */
+
+/*
+ * Modem control strings. These may have to be changed for some modems.
+ *
+ * AT command prefix
+ * B1 initiate call negotiation using Bell 212A
+ * &C1 enable carrier detect
+ * &D2 hang up and return to command mode on DTR transition
+ * E0 modem command echo disabled
+ * l1 set modem speaker volume to low level
+ * M1 speaker enabled untill carrier detect
+ * Q0 return result codes
+ * V1 return result codes as English words
+ */
+#define MODEM_SETUP "ATB1&C1&D2E0L1M1Q0V1" /* modem setup */
+#define MODEM_HANGUP "ATH" /* modem disconnect */
+
+/*
+ * Timeouts
+ */
+#define IDLE 60 /* idle timeout (s) */
+#define WAIT 2 /* wait timeout (s) */
+#define ANSWER 30 /* answer timeout (s) */
+#define CONNECT 10 /* connect timeout (s) */
+#define TIMECODE 15 /* timecode timeout (s) */
+
+/*
+ * Imported from ntp_timer module
+ */
+extern u_long current_time; /* current time (s) */
+extern u_long last_time; /* last clock update time (s) */
+extern struct event timerqueue[]; /* inner space */
+
+/*
+ * Imported from ntpd module
+ */
+extern int debug; /* global debug flag */
+
+/*
+ * Imported from ntp_config module
+ */
+extern char sys_phone[][MAXDIAL]; /* modem dial strings */
+
+/*
+ * Imported from ntp_proto module
+ */
+extern struct peer *sys_peer; /* who is running the show */
+extern u_char sys_poll; /* log2 of system poll interval */
+extern struct peer *sys_peer; /* system peer structure pointer */
+
+/*
+ * Tables to compute the ddd of year form icky dd/mm timecode. Viva la
+ * leap.
+ */
+static day1tab[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
+static day2tab[] = {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
+
+/*
+ * Unit control structure
+ */
+struct actsunit {
+ struct event timer; /* timeout timer */
+ int pollcnt; /* poll message counter */
+
+ int state; /* the first one was Delaware */
+ int run; /* call program run switch */
+ int msgcnt; /* count of ACTS messages received */
+ long redial; /* interval to next automatic call */
+ double msADV; /* millisecond advance of last message */
+};
+
+/*
+ * Function prototypes
+ */
+static int acts_start P((int, struct peer *));
+static void acts_shutdown P((int, struct peer *));
+static void acts_receive P((struct recvbuf *));
+static void acts_poll P((int, struct peer *));
+static void acts_timeout P((struct peer *));
+static void acts_disc P((struct peer *));
+static int acts_write P((struct peer *, char *));
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_acts = {
+ acts_start, /* start up driver */
+ acts_shutdown, /* shut down driver */
+ acts_poll, /* transmit poll message */
+ noentry, /* not used (old acts_control) */
+ noentry, /* not used (old acts_init) */
+ noentry, /* not used (old acts_buginfo) */
+ NOFLAGS /* not used */
+};
+
+
+/*
+ * acts_start - open the devices and initialize data for processing
+ */
+static int
+acts_start(unit, peer)
+ int unit;
+ struct peer *peer;
+{
+ register struct actsunit *up;
+ struct refclockproc *pp;
+ int fd;
+ char device[20];
+ int dtr = TIOCM_DTR;
+
+ /*
+ * Open serial port. Use ACTS line discipline, if available. It
+ * pumps a timestamp into the data stream at every on-time
+ * character '*' found. Note: the port must have modem control
+ * or deep pockets for the phone bill. HP-UX 9.03 users should
+ * have very deep pockets.
+ */
+ (void)sprintf(device, DEVICE, unit);
+ if (!(fd = refclock_open(device, SPEED232, LDISC_ACTS)))
+ return (0);
+ if (ioctl(fd, TIOCMBIC, (char *)&dtr) < 0) {
+ syslog(LOG_ERR, "clock %s ACTS no modem control",
+ ntoa(&peer->srcadr));
+ return (0);
+ }
+
+ /*
+ * Allocate and initialize unit structure
+ */
+ if (!(up = (struct actsunit *)
+ emalloc(sizeof(struct actsunit)))) {
+ (void) close(fd);
+ return (0);
+ }
+ memset((char *)up, 0, sizeof(struct actsunit));
+ pp = peer->procptr;
+ pp->io.clock_recv = acts_receive;
+ pp->io.srcclock = (caddr_t)peer;
+ pp->io.datalen = 0;
+ pp->io.fd = fd;
+ if (!io_addclock(&pp->io)) {
+ (void) close(fd);
+ free(up);
+ return (0);
+ }
+ pp->unitptr = (caddr_t)up;
+
+ /*
+ * Initialize miscellaneous variables
+ */
+ peer->precision = PRECISION;
+ pp->clockdesc = DESCRIPTION;
+ memcpy((char *)&pp->refid, REFID, 4);
+ peer->minpoll = ACTS_MINPOLL;
+ peer->maxpoll = ACTS_MAXPOLL;
+
+ /*
+ * Initialize modem and kill DTR. We skedaddle if this comes
+ * bum.
+ */
+ if (!acts_write(peer, MODEM_SETUP)) {
+ (void) close(fd);
+ free(up);
+ return (0);
+ }
+
+ /*
+ * Set up the driver timeout
+ */
+ up->timer.peer = (struct peer *)peer;
+ up->timer.event_handler = acts_timeout;
+ up->timer.event_time = current_time + WAIT;
+ TIMER_INSERT(timerqueue, &up->timer);
+ return (1);
+}
+
+
+/*
+ * acts_shutdown - shut down the clock
+ */
+static void
+acts_shutdown(unit, peer)
+ int unit;
+ struct peer *peer;
+{
+ register struct actsunit *up;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ up = (struct actsunit *)pp->unitptr;
+ TIMER_DEQUEUE(&up->timer);
+ io_closeclock(&pp->io);
+ free(up);
+}
+
+
+/*
+ * acts_receive - receive data from the serial interface
+ */
+static void
+acts_receive(rbufp)
+ struct recvbuf *rbufp;
+{
+ register struct actsunit *up;
+ struct refclockproc *pp;
+ struct peer *peer;
+ char str[SMAX];
+ int i;
+ l_fp tstmp;
+ u_fp disp;
+ char hangup = '%'; /* ACTS hangup */
+ int day; /* day of the month */
+ int month; /* month of the year */
+ u_long mjd; /* Modified Julian Day */
+ u_int dst; /* daylight/standard time indicator */
+ u_int leap; /* leap-second indicator */
+ double dut1; /* DUT adjustment */
+ double msADV; /* ACTS transmit advance (ms) */
+ char utc[10]; /* this is NIST and you're not */
+ char flag; /* calibration flag */
+
+ /*
+ * Initialize pointers and read the timecode and timestamp. If
+ * the OK modem status code, leave it where folks can find it.
+ */
+ peer = (struct peer *)rbufp->recv_srcclock;
+ pp = peer->procptr;
+ up = (struct actsunit *)pp->unitptr;
+ pp->lencode = refclock_gtlin(rbufp, pp->lastcode, BMAX,
+ &pp->lastrec);
+ if (pp->lencode == 0) {
+ if (strcmp(pp->lastcode, "OK") == 0)
+ pp->lencode = 2;
+ return;
+ }
+#ifdef DEBUG
+ if (debug)
+ printf("acts: timecode %d %s\n", pp->lencode,
+ pp->lastcode);
+#endif
+
+ switch (up->state) {
+
+ case 0:
+
+ /*
+ * State 0. We are not expecting anything. Probably
+ * modem disconnect noise. Go back to sleep.
+ */
+ return;
+
+ case 1:
+
+ /*
+ * State 1. We are waiting for the call to be answered.
+ * All we care about here is CONNECT as the first token
+ * in the string. If the modem signals BUSY, ERROR, NO
+ * ANSWER, NO CARRIER or NO DIALTONE, we immediately
+ * hang up the phone. If CONNECT doesn't happen after
+ * ANSWER seconds, hang up the phone. If everything is
+ * okay, start the connect timeout and slide into state
+ * 2.
+ */
+ (void)strncpy(str, strtok(pp->lastcode, " "), SMAX);
+ if (strcmp(str, "BUSY") == 0 || strcmp(str, "ERROR") ==
+ 0 || strcmp(str, "NO") == 0) {
+ TIMER_DEQUEUE(&up->timer);
+ syslog(LOG_NOTICE,
+ "clock %s ACTS modem status %s",
+ ntoa(&peer->srcadr), pp->lastcode);
+ acts_disc(peer);
+ } else if (strcmp(str, "CONNECT") == 0) {
+ TIMER_DEQUEUE(&up->timer);
+ up->timer.event_time = current_time + CONNECT;
+ TIMER_INSERT(timerqueue, &up->timer);
+ up->msgcnt = 0;
+ up->state++;
+ }
+ return;
+
+ case 2:
+
+ /*
+ * State 2. The call has been answered and we are
+ * waiting for the first ACTS message. If this doesn't
+ * happen within the timecode timeout, hang up the
+ * phone. We probably got a wrong number or ACTS is
+ * down.
+ */
+ TIMER_DEQUEUE(&up->timer);
+ up->timer.event_time = current_time + TIMECODE;
+ TIMER_INSERT(timerqueue, &up->timer);
+ up->state++;
+ }
+
+ /*
+ * Real yucky things here. Ignore everything except timecode
+ * messages, as determined by the message length. We told the
+ * terminal routines to end the line with '*' and the line
+ * discipline to strike a timestamp on that character. However,
+ * when the ACTS echo-delay scheme works, the '*' eventually
+ * becomes a '#'. In this case the message is ended by the <CR>
+ * that comes about 200 ms after the '#' and the '#' cannot be
+ * echoed at the proper time. But, this may not be a lose, since
+ * we already have good data from prior messages and only need
+ * the millisecond advance calculated by ACTS. So, if the
+ * message is long enough and has an on-time character at the
+ * right place, we consider the message (but not neccesarily the
+ * timestmap) to be valid.
+ */
+ if (pp->lencode != LENCODE)
+ return;
+
+ /*
+ * We apparently have a valid timecode message, so dismember it
+ * with sscan(). This routine does a good job in spotting syntax
+ * errors without becoming overly pedantic.
+ *
+ * D L D
+ * MJD YR MO DA H M S ST S UT1 msADV OTM
+ * 47222 88-03-02 21:39:15 83 0 +.3 045.0 UTC(NBS) *
+ */
+ if (sscanf(pp->lastcode,
+ "%5ld %2d-%2d-%2d %2d:%2d:%2d %2d %1d %3lf %5lf %s %c",
+ &mjd, &pp->year, &month, &day, &pp->hour, &pp->minute,
+ &pp->second, &dst, &leap, &dut1, &msADV, utc, &flag) != 13) {
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+
+ /*
+ * Some modems can't be trusted (the Practical Peripherals
+ * 9600SA comes to mind) and, even if they manage to unstick
+ * ACTS, the millisecond advance is wrong, so we use CLK_FLAG2
+ * to disable echoes, if neccessary.
+ */
+ if ((flag == '*' || flag == '#') && !(pp->sloppyclockflag &
+ CLK_FLAG2))
+ (void)write(pp->io.fd, &flag, 1);
+
+ /*
+ * Yes, I know this code incorrectly thinks that 2000 is a leap
+ * year. The ACTS timecode format croaks then anyway. Life is
+ * short. Would only the timecode mavens resist the urge to
+ * express months of the year and days of the month in favor of
+ * days of the year.
+ */
+ if (month < 1 || month > 12 || day < 1) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ if (pp->year % 4) {
+ if (day > day1tab[month - 1]) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ for (i = 0; i < month - 1; i++)
+ day += day1tab[i];
+ } else {
+ if (day > day2tab[month - 1]) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ for (i = 0; i < month - 1; i++)
+ day += day2tab[i];
+ }
+ pp->day = day;
+ if (leap == 1)
+ pp->leap = LEAP_ADDSECOND;
+ else if (pp->leap == 2)
+ pp->leap = LEAP_DELSECOND;
+ else
+ pp->leap = 0;
+ pp->lasttime = current_time;
+
+ /*
+ * Colossal hack here. We process each sample in a trimmed-mean
+ * filter and determine the reference clock offset and
+ * dispersion. The fudge time1 value is added to each sample as
+ * received. If we collect MSGCNT samples before the '#' on-time
+ * character, we use the results of the filter as is. If the '#'
+ * is found before that, the adjusted msADV is used to correct
+ * the propagation delay.
+ */
+ up->msgcnt++;
+ if (flag == '#') {
+ L_CLR(&tstmp);
+ TVUTOTSF((long)((msADV - up->msADV) * 1000.),
+ tstmp.l_uf);
+ L_ADD(&pp->offset, &tstmp);
+ } else {
+ up->msADV = msADV;
+ if (!refclock_process(pp, up->msgcnt, up->msgcnt -
+ up->msgcnt / 3)) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ } else if (up->msgcnt < MSGCNT)
+ return;
+ }
+
+ /*
+ * We have a filtered sample offset ready for peer processing.
+ * We use lastrec as both the reference time and receive time in
+ * order to avoid being cute, like setting the reference time
+ * later than the receive time, which may cause a paranoid
+ * protocol module to chuck out the data. Finaly, we unhook the
+ * timeout, arm for the next call, fold the tent and go home.
+ * The little dance with the '%' character is an undocumented
+ * ACTS feature that hangs up the phone real quick without
+ * waiting for carrier loss or long-space disconnect, but we do
+ * these clumsy things anyway.
+ */
+ disp = LFPTOFP(&pp->fudgetime2);
+ record_clock_stats(&peer->srcadr, pp->lastcode);
+ refclock_receive(peer, &pp->offset, 0, pp->dispersion +
+ (u_fp)disp, &pp->lastrec, &pp->lastrec, pp->leap);
+ pp->sloppyclockflag &= ~CLK_FLAG1;
+ up->pollcnt = 0;
+ TIMER_DEQUEUE(&up->timer);
+ (void)write(pp->io.fd, &hangup, 1);
+ up->state = 0;
+ acts_disc(peer);
+}
+
+
+/*
+ * acts_poll - called by the transmit routine
+ */
+static void
+acts_poll(unit, peer)
+ int unit;
+ struct peer *peer;
+{
+ register struct actsunit *up;
+ struct refclockproc *pp;
+
+ /*
+ * If the driver is running, we set the enable flag (fudge
+ * flag1), which causes the driver timeout routine to initiate a
+ * call to ACTS. If not, the enable flag can be set using
+ * xntpdc. If this is the sustem peer, then follow the system
+ * poll interval.
+ */
+ pp = peer->procptr;
+ up = (struct actsunit *)pp->unitptr;
+ if (up->run) {
+ pp->sloppyclockflag |= CLK_FLAG1;
+ if (peer == sys_peer)
+ peer->hpoll = sys_poll;
+ else
+ peer->hpoll = peer->minpoll;
+ }
+}
+
+
+/*
+ * acts_timeout - called by the timer interrupt
+ */
+static void
+acts_timeout(peer)
+ struct peer *peer;
+{
+ register struct actsunit *up;
+ struct refclockproc *pp;
+ int dtr = TIOCM_DTR;
+
+ /*
+ * If a timeout occurs in other than state 0, the call has
+ * failed. If in state 0, we just see if there is other work to
+ * do.
+ */
+ pp = peer->procptr;
+ up = (struct actsunit *)pp->unitptr;
+ if (up->state) {
+ acts_disc(peer);
+ return;
+ }
+ switch (peer->ttl) {
+
+ /*
+ * In manual mode the ACTS calling program is activated
+ * by the xntpdc program using the enable flag (fudge
+ * flag1), either manually or by a cron job.
+ */
+ case MODE_MANUAL:
+ up->run = 0;
+ break;
+
+ /*
+ * In automatic mode the ACTS calling program runs
+ * continuously at intervals determined by the sys_poll
+ * variable.
+ */
+ case MODE_AUTO:
+ if (!up->run)
+ pp->sloppyclockflag |= CLK_FLAG1;
+ up->run = 1;
+ break;
+
+ /*
+ * In backup mode the ACTS calling program is disabled,
+ * unless no system peer has been selected for MAXOUTAGE
+ * (3600 s). Once enabled, it runs until some other NTP
+ * peer shows up.
+ */
+ case MODE_BACKUP:
+ if (!up->run && sys_peer == 0) {
+ if (current_time - last_time > MAXOUTAGE) {
+ up->run = 1;
+ peer->hpoll = peer->minpoll;
+ syslog(LOG_NOTICE,
+ "clock %s ACTS backup started ",
+ ntoa(&peer->srcadr));
+ }
+ } else if (up->run && sys_peer->refclktype !=
+ REFCLK_NIST_ACTS) {
+ peer->hpoll = peer->minpoll;
+ up->run = 0;
+ syslog(LOG_NOTICE,
+ "clock %s ACTS backup stopped",
+ ntoa(&peer->srcadr));
+ }
+ break;
+
+ default:
+ syslog(LOG_NOTICE,
+ "clock %s ACTS invalid mode", ntoa(&peer->srcadr));
+
+ }
+
+ /*
+ * The fudge flag1 is used as an enable/disable; if set either
+ * by the code or via xntpdc, the ACTS calling program is
+ * started; if reset, the phones stop ringing.
+ */
+ if (!(pp->sloppyclockflag & CLK_FLAG1)) {
+ up->pollcnt = 0;
+ up->timer.event_time = current_time + IDLE;
+ TIMER_INSERT(timerqueue, &up->timer);
+ return;
+ }
+
+ /*
+ * Initiate a call to the ACTS service. If we wind up here in
+ * other than state 0, a successful call could not be completed
+ * within minpoll seconds. We advance to the next modem dial
+ * string. If none are left, we log a notice and clear the
+ * enable flag. For future enhancement: call the site RP and
+ * leave an obscene message in his voicemail.
+ */
+ if (sys_phone[up->pollcnt][0] == '\0') {
+ refclock_report(peer, CEVNT_TIMEOUT);
+ syslog(LOG_NOTICE,
+ "clock %s ACTS calling program terminated",
+ ntoa(&peer->srcadr));
+ pp->sloppyclockflag &= ~CLK_FLAG1;
+#ifdef DEBUG
+ if (debug)
+ printf("acts: calling program terminated\n");
+#endif
+ up->pollcnt = 0;
+ up->timer.event_time = current_time + IDLE;
+ TIMER_INSERT(timerqueue, &up->timer);
+ return;
+ }
+
+ /*
+ * Raise DTR, call ACTS and start the answer timeout. We think
+ * it strange if the OK status has not been received from the
+ * modem, but plow ahead anyway.
+ */
+ if (strcmp(pp->lastcode, "OK") != 0)
+ syslog(LOG_NOTICE, "clock %s ACTS no modem status",
+ ntoa(&peer->srcadr));
+ (void)ioctl(pp->io.fd, TIOCMBIS, (char *)&dtr);
+ (void)acts_write(peer, sys_phone[up->pollcnt]);
+ syslog(LOG_NOTICE, "clock %s ACTS calling %s\n",
+ ntoa(&peer->srcadr), sys_phone[up->pollcnt]);
+ up->state = 1;
+ up->pollcnt++;
+ pp->polls++;
+ up->timer.event_time = current_time + ANSWER;
+ TIMER_INSERT(timerqueue, &up->timer);
+}
+
+
+/*
+ * acts_disc - disconnect the call and wait for the ruckus to cool
+ */
+static void
+acts_disc(peer)
+ struct peer *peer;
+{
+ register struct actsunit *up;
+ struct refclockproc *pp;
+ int dtr = TIOCM_DTR;
+
+ /*
+ * We should never get here other than in state 0, unless a call
+ * has timed out. We drop DTR, which will reliably get the modem
+ * off the air, even while ACTS is hammering away full tilt.
+ */
+ pp = peer->procptr;
+ up = (struct actsunit *)pp->unitptr;
+ (void)ioctl(pp->io.fd, TIOCMBIC, (char *)&dtr);
+ if (up->state > 0) {
+ up->state = 0;
+ syslog(LOG_NOTICE, "clock %s ACTS call failed %d",
+ ntoa(&peer->srcadr), up->state);
+#ifdef DEBUG
+ if (debug)
+ printf("acts: call failed %d\n", up->state);
+#endif
+ }
+ up->timer.event_time = current_time + WAIT;
+ TIMER_INSERT(timerqueue, &up->timer);
+}
+
+
+/*
+ * acts_write - write a message to the serial port
+ */
+static int
+acts_write(peer, str)
+ struct peer *peer;
+ char *str;
+{
+ register struct actsunit *up;
+ struct refclockproc *pp;
+ int len;
+ int code;
+ char cr = '\r';
+
+ /*
+ * Not much to do here, other than send the message, handle
+ * debug and report faults.
+ */
+ pp = peer->procptr;
+ up = (struct actsunit *)pp->unitptr;
+ len = strlen(str);
+#ifdef DEBUG
+ if (debug)
+ printf("acts: state %d send %d %s\n", up->state, len,
+ str);
+#endif
+ code = write(pp->io.fd, str, len) == len;
+ code |= write(pp->io.fd, &cr, 1) == 1;
+ if (!code)
+ refclock_report(peer, CEVNT_FAULT);
+ return (code);
+}
+
+#endif
diff --git a/usr.sbin/xntpd/xntpd/refclock_as2201.c b/usr.sbin/xntpd/xntpd/refclock_as2201.c
new file mode 100644
index 0000000..afacb6f
--- /dev/null
+++ b/usr.sbin/xntpd/xntpd/refclock_as2201.c
@@ -0,0 +1,453 @@
+/*
+ * refclock_as2201 - clock driver for the Austron 2201A GPS
+ * Timing Receiver
+ */
+#if defined(REFCLOCK) && defined(AS2201)
+
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/time.h>
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_unixtime.h"
+#include "ntp_stdlib.h"
+
+#ifdef PPS
+#include <sys/ppsclock.h>
+#endif /* PPS */
+
+/*
+ * This driver supports the Austron 2200A/2201A GPS Receiver with
+ * Buffered RS-232-C Interface Module. Note that the original 2200/2201
+ * receivers will not work reliably with this driver, since the older
+ * design cannot accept input commands at any reasonable data rate.
+ *
+ * The program sends a "*toc\r" to the radio and expects a response of
+ * the form "yy:ddd:hh:mm:ss.mmm\r" where yy = year of century, ddd =
+ * day of year, hh:mm:ss = second of day and mmm = millisecond of
+ * second. Then, it sends statistics commands to the radio and expects
+ * a multi-line reply showing the corresponding statistics or other
+ * selected data. Statistics commands are sent in order as determined by
+ * a vector of commands; these might have to be changed with different
+ * radio options. If flag4 of the fudge configuration command is set to
+ * 1, the statistics data are written to the clockstats file for later
+ * processing.
+ *
+ * In order for this code to work, the radio must be placed in non-
+ * interactive mode using the "off" command and with a single <cr>
+ * resonse using the "term cr" command. The setting of the "echo"
+ * and "df" commands does not matter. The radio should select UTC
+ * timescale using the "ts utc" command.
+ *
+ * There are two modes of operation for this driver. The first with
+ * default configuration is used with stock kernels and serial-line
+ * drivers and works with almost any machine. In this mode the driver
+ * assumes the radio captures a timestamp upon receipt of the "*" that
+ * begins the driver query. Accuracies in this mode are in the order of
+ * a millisecond or two and the receiver can be connected to only one
+ * host.
+ *
+ * The second mode of operation can be used for SunOS kernels that have
+ * been modified with the ppsclock streams module included in this
+ * distribution. The mode is enabled if flag3 of the fudge configuration
+ * command has been set to 1. In this mode a precise timestamp is
+ * available using a gadget box and 1-pps signal from the receiver. This
+ * improves the accuracy to the order of a few tens of microseconds. In
+ * addition, the serial output and 1-pps signal can be bussed to
+ * additional receivers.
+ */
+
+/*
+ * GPS Definitions
+ */
+#define SMAX 200 /* statistics buffer length */
+#define DEVICE "/dev/gps%d" /* device name and unit */
+#define SPEED232 B9600 /* uart speed (9600 baud) */
+#define PRECISION (-10) /* precision assumed (about 1 ms) */
+#define REFID "GPS\0" /* reference ID */
+#define DESCRIPTION "Austron 2201A GPS Receiver" /* WRU */
+
+#define NSAMPLES 3 /* stages of median filter */
+#define LENTOC 19 /* yy:ddd:hh:mm:ss.mmm timecode lngth */
+
+/*
+ * Imported from ntp_timer module
+ */
+extern u_long current_time; /* current time (s) */
+
+/*
+ * Imported from ntpd module
+ */
+extern int debug; /* global debug flag */
+
+#ifdef PPS
+/*
+ * Imported from loop_filter module
+ */
+extern int fdpps; /* ppsclock file descriptor */
+#endif /* PPS */
+
+/*
+ * AS2201 unit control structure.
+ */
+struct as2201unit {
+ int pollcnt; /* poll message counter */
+
+ char *lastptr; /* statistics buffer pointer */
+ char stats[SMAX]; /* statistics buffer */
+
+#ifdef PPS
+ u_long lastev; /* last ppsclock second */
+#endif /* PPS */
+
+ int linect; /* count of lines remaining */
+ int index; /* current statistics command */
+};
+
+/*
+ * Radio commands to extract statitistics
+ *
+ * A command consists of an ASCII string terminated by a <cr> (\r). The
+ * command list consist of a sequence of commands terminated by a null
+ * string ("\0"). One command from the list is sent immediately
+ * following each received timecode (*toc\r command) and the ASCII
+ * strings received from the radio are saved along with the timecode in
+ * the clockstats file. Subsequent commands are sent at each timecode,
+ * with the last one in the list followed by the first one. The data
+ * received from the radio consist of ASCII strings, each terminated by
+ * a <cr> (\r) character. The number of strings for each command is
+ * specified as the first line of output as an ASCII-encode number. Note
+ * that the ETF command requires the Input Buffer Module and the LORAN
+ * commands require the LORAN Assist Module. However, if these modules
+ * are not installed, the radio and this driver will continue to operate
+ * successfuly, but no data will be captured for these commands.
+ */
+static char stat_command[][30] = {
+ "ITF\r", /* internal time/frequency */
+ "ETF\r", /* external time/frequency */
+ "LORAN ENSEMBLE\r", /* GPS/LORAN ensemble statistics */
+ "LORAN TDATA\r", /* LORAN signal data */
+ "ID;OPT;VER\r", /* model; options; software version */
+
+ "ITF\r", /* internal time/frequency */
+ "ETF\r", /* external time/frequency */
+ "LORAN ENSEMBLE\r", /* GPS/LORAN ensemble statistics */
+ "TRSTAT\r", /* satellite tracking status */
+ "POS;PPS;PPSOFF\r", /* position, pps source, offsets */
+
+ "ITF\r", /* internal time/frequency */
+ "ETF\r", /* external time/frequency */
+ "LORAN ENSEMBLE\r", /* GPS/LORAN ensemble statistics */
+ "LORAN TDATA\r", /* LORAN signal data */
+ "UTC\r", /* UTC leap info */
+
+ "ITF\r", /* internal time/frequency */
+ "ETF\r", /* external time/frequency */
+ "LORAN ENSEMBLE\r", /* GPS/LORAN ensemble statistics */
+ "TRSTAT\r", /* satellite tracking status */
+ "OSC;ET;TEMP\r", /* osc type; tune volts; oven temp */
+ "\0" /* end of table */
+};
+
+/*
+ * Function prototypes
+ */
+static int as2201_start P((int, struct peer *));
+static void as2201_shutdown P((int, struct peer *));
+static void as2201_receive P((struct recvbuf *));
+static void as2201_poll P((int, struct peer *));
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_as2201 = {
+ as2201_start, /* start up driver */
+ as2201_shutdown, /* shut down driver */
+ as2201_poll, /* transmit poll message */
+ noentry, /* not used (old as2201_control) */
+ noentry, /* initialize driver (not used) */
+ noentry, /* not used (old as2201_buginfo) */
+ NOFLAGS /* not used */
+};
+
+
+/*
+ * as2201_start - open the devices and initialize data for processing
+ */
+static int
+as2201_start(unit, peer)
+ int unit;
+ struct peer *peer;
+{
+ register struct as2201unit *up;
+ struct refclockproc *pp;
+ int fd;
+ char gpsdev[20];
+
+ /*
+ * Open serial port. Use CLK line discipline, if available.
+ */
+ (void)sprintf(gpsdev, DEVICE, unit);
+ if (!(fd = refclock_open(gpsdev, SPEED232, LDISC_CLK)))
+ return (0);
+
+ /*
+ * Allocate and initialize unit structure
+ */
+ if (!(up = (struct as2201unit *)
+ emalloc(sizeof(struct as2201unit)))) {
+ (void) close(fd);
+ return (0);
+ }
+ memset((char *)up, 0, sizeof(struct as2201unit));
+ pp = peer->procptr;
+ pp->io.clock_recv = as2201_receive;
+ pp->io.srcclock = (caddr_t)peer;
+ pp->io.datalen = 0;
+ pp->io.fd = fd;
+ if (!io_addclock(&pp->io)) {
+ (void) close(fd);
+ free(up);
+ return (0);
+ }
+ pp->unitptr = (caddr_t)up;
+
+ /*
+ * Initialize miscellaneous variables
+ */
+ peer->precision = PRECISION;
+ pp->clockdesc = DESCRIPTION;
+ memcpy((char *)&pp->refid, REFID, 4);
+ up->pollcnt = 2;
+
+ up->lastptr = up->stats;
+ up->index = 0;
+ return (1);
+}
+
+
+/*
+ * as2201_shutdown - shut down the clock
+ */
+static void
+as2201_shutdown(unit, peer)
+ int unit;
+ struct peer *peer;
+{
+ register struct as2201unit *up;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ up = (struct as2201unit *)pp->unitptr;
+ io_closeclock(&pp->io);
+ free(up);
+}
+
+
+/*
+ * as2201__receive - receive data from the serial interface
+ */
+static void
+as2201_receive(rbufp)
+ struct recvbuf *rbufp;
+{
+ register struct as2201unit *up;
+ struct refclockproc *pp;
+ struct peer *peer;
+ l_fp trtmp;
+#ifdef PPS
+ long ltemp;
+ struct ppsclockev ev;
+#endif /* PPS */
+
+ /*
+ * Initialize pointers and read the timecode and timestamp.
+ */
+ peer = (struct peer *)rbufp->recv_srcclock;
+ pp = peer->procptr;
+ up = (struct as2201unit *)pp->unitptr;
+ pp->lencode = refclock_gtlin(rbufp, pp->lastcode, BMAX, &trtmp);
+#ifdef DEBUG
+ if (debug)
+ printf("gps: timecode %d %d %s\n",
+ up->linect, pp->lencode, pp->lastcode);
+#endif
+ if (pp->lencode == 0)
+ return;
+
+ /*
+ * If linect is greater than zero, we must be in the middle of a
+ * statistics operation, so simply tack the received data at the
+ * end of the statistics string. If not, we could either have
+ * just received the timecode itself or a decimal number
+ * indicating the number of following lines of the statistics
+ * reply. In the former case, write the accumulated statistics
+ * data to the clockstats file and continue onward to process
+ * the timecode; in the later case, save the number of lines and
+ * quietly return.
+ */
+ if (up->linect > 0) {
+ up->linect--;
+ if (up->lastptr - up->stats + pp->lencode > SMAX - 2)
+ return;
+ *up->lastptr++ = ' ';
+ (void)strcpy(up->lastptr, pp->lastcode);
+ up->lastptr += pp->lencode;
+ return;
+ } else {
+ if (pp->lencode == 1) {
+ up->linect = atoi(pp->lastcode);
+ return;
+ } else {
+ up->pollcnt = 2;
+ record_clock_stats(&peer->srcadr, up->stats);
+#ifdef DEBUG
+ if (debug)
+ printf("gps: stat %s\n", up->stats);
+#endif
+ }
+ }
+ up->lastptr = up->stats;
+ *up->lastptr = '\0';
+
+ /*
+ * We get down to business, check the timecode format and decode
+ * its contents. If the timecode has invalid length or is not in
+ * proper format, we declare bad format and exit.
+ */
+ if (pp->lencode < LENTOC) {
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+
+ /*
+ * Timecode format: "yy:ddd:hh:mm:ss.mmm"
+ */
+ if (sscanf(pp->lastcode, "%2d:%3d:%2d:%2d:%2d.%3d", &pp->year,
+ &pp->day, &pp->hour, &pp->minute, &pp->second, &pp->msec)
+ != 6) {
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+
+ /*
+ * Test for synchronization (this is a temporary crock).
+ */
+ if (pp->lastcode[2] != ':') {
+ pp->leap = LEAP_NOTINSYNC;
+ } else {
+ pp->leap = 0;
+ pp->lasttime = current_time;
+ }
+#ifdef PPS
+
+ /*
+ * If CLK_FLAG3 is set and the local time is within +-0.5 second
+ * of the timecode, use the pps offset instead. Note that we
+ * believe the ppsclock timestamp only if the ioctl works and
+ * the new timestamp is greater than the previous one.
+ */
+ if (pp->sloppyclockflag & CLK_FLAG3 && fdpps != -1) {
+ if (!clocktime(pp->day, pp->hour, pp->minute,
+ pp->second, GMT, pp->lastrec.l_ui, &pp->yearstart,
+ &pp->lastref.l_ui)) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ MSUTOTSF(pp->msec, pp->lastref.l_uf);
+ pp->lastrec = trtmp;
+ L_SUB(&trtmp, &pp->lastref);
+ if (L_ISNEG(&trtmp))
+ L_NEG(&trtmp);
+ if (trtmp.l_i < CLOCK_MAX_I || (trtmp.l_i == CLOCK_MAX_I
+ && trtmp.l_uf < CLOCK_MAX_F)) {
+ if (ioctl(fdpps, CIOGETEV, (caddr_t)&ev) >= 0) {
+ if (up->lastev < ev.tv.tv_sec) {
+ TVUTOTSF(ev.tv.tv_usec, ltemp);
+ pp->lastrec = pp->lastref;
+ L_ADDF(&pp->lastrec, ltemp);
+ }
+ up->lastev = ev.tv.tv_sec;
+ }
+ }
+ }
+#endif /* PPS */
+#ifdef DEBUG
+ if (debug)
+ printf("gps: times %s %s %s\n",
+ ulfptoa(&pp->lastref, 6), ulfptoa(&pp->lastrec, 6),
+ lfptoa(&trtmp, 6));
+#endif
+
+ /*
+ * Process the new sample in the median filter and determine the
+ * reference clock offset and dispersion. We use lastrec as both
+ * the reference time and receive time in order to avoid being
+ * cute, like setting the reference time later than the receive
+ * time, which may cause a paranoid protocol module to chuck out
+ * the data.
+ */
+ if (!refclock_process(pp, NSAMPLES, NSAMPLES)) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ refclock_receive(peer, &pp->offset, 0, pp->dispersion,
+ &pp->lastrec, &pp->lastrec, pp->leap);
+
+ /*
+ * If CLK_FLAG4 is set, initialize the statistics buffer and
+ * send the next command. If not, simply write the timecode to
+ * the clockstats file.
+ */
+ (void)strcpy(up->lastptr, pp->lastcode);
+ up->lastptr += pp->lencode;
+ if (pp->sloppyclockflag & CLK_FLAG4) {
+ *up->lastptr++ = ' ';
+ (void)strcpy(up->lastptr, stat_command[up->index]);
+ up->lastptr += strlen(stat_command[up->index]);
+ up->lastptr--;
+ *up->lastptr = '\0';
+ (void)write(pp->io.fd, stat_command[up->index],
+ strlen(stat_command[up->index]));
+ up->index++;
+ if (*stat_command[up->index] == '\0')
+ up->index = 0;
+ }
+}
+
+
+/*
+ * as2201_poll - called by the transmit procedure
+ *
+ * We go to great pains to avoid changing state here, since there may be
+ * more than one eavesdropper receiving the same timecode.
+ */
+static void
+as2201_poll(unit, peer)
+ int unit;
+ struct peer *peer;
+{
+ register struct as2201unit *up;
+ struct refclockproc *pp;
+
+ /*
+ * Send a "\r*toc\r" to get things going. We go to great pains
+ * to avoid changing state, since there may be more than one
+ * eavesdropper watching the radio.
+ */
+ pp = peer->procptr;
+ up = (struct as2201unit *)pp->unitptr;
+ if (up->pollcnt == 0)
+ refclock_report(peer, CEVNT_TIMEOUT);
+ else
+ up->pollcnt--;
+ gettstamp(&pp->lastrec);
+ if (write(pp->io.fd, "\r*toc\r", 6) != 6) {
+ refclock_report(peer, CEVNT_FAULT);
+ } else
+ pp->polls++;
+}
+
+#endif /* REFCLOCK */
diff --git a/usr.sbin/xntpd/xntpd/refclock_atom.c b/usr.sbin/xntpd/xntpd/refclock_atom.c
new file mode 100644
index 0000000..8df49d2
--- /dev/null
+++ b/usr.sbin/xntpd/xntpd/refclock_atom.c
@@ -0,0 +1,499 @@
+/*
+ * refclock_atom - clock driver for 1-pps signals
+ */
+#if defined(REFCLOCK) && defined(ATOM)
+
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/time.h>
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_unixtime.h"
+#include "ntp_refclock.h"
+#include "ntp_stdlib.h"
+
+#ifdef PPS
+#include <sys/ppsclock.h>
+#endif /* PPS */
+
+/*
+ * This driver furnishes an interface for pulse-per-second (PPS) signals
+ * produced by a cesium clock, timing receiver or related equipment. It
+ * can be used to remove accumulated jitter and retime a secondary
+ * server when synchronized to a primary server over a congested, wide-
+ * area network and before redistributing the time to local clients.
+ *
+ * In order for this driver to work, the local clock must be set to
+ * within +-500 ms by another means, such as a radio clock or NTP
+ * itself. The 1-pps signal is connected via a serial port and gadget
+ * box consisting of a one-shot and RS232 level converter. When operated
+ * at 38.4 kbps with a SPARCstation IPC, this arrangement has a worst-
+ * case jitter less than 26 us.
+ *
+ * There are three ways in which this driver can be used. The first way
+ * uses the LDISC_PPS line discipline and works only for the baseboard
+ * serial ports of the Sun SPARCstation. The PPS signal is connected via
+ * a gadget box to the carrier detect (CD) line of a serial port and
+ * flag3 of the driver configured for that port is set. This causes the
+ * ppsclock streams module to be configured for that port and capture a
+ * timestamp at the on-time transition of the PPS signal. This driver
+ * then reads the timestamp directly by a designated ioctl() system
+ * call. This provides the most accurate time and least jitter of any
+ * other scheme. There is no need to configure a dedicated device for
+ * this purpose, which ordinarily is the device used for the associated
+ * radio clock.
+ *
+ * The second way uses the LDISC_CLKPPS line discipline and works for
+ * any architecture supporting a serial port. If after a few seconds
+ * this driver finds no ppsclock module configured, it attempts to open
+ * a serial port device /dev/pps%d, where %d is the unit number, and
+ * assign the LDISC_CLKPPS line discipline to it. If the line discipline
+ * fails, no harm is done except the accuracy is reduced somewhat. The
+ * pulse generator in the gadget box is adjusted to produce a start bit
+ * of length 26 usec at 38400 bps. Used with the LDISC_CLKPPS line
+ * discipline, this produces an ASCII DEL character ('\377') followed by
+ * a timestamp at each seconds epoch.
+ *
+ * The third way involves an auxiliary radio clock driver which calls
+ * the PPS driver with a timestamp captured by that driver. This use is
+ * documented in the source code for the driver(s) involved.
+ *
+ * Fudge Factors
+ *
+ * There are no special fudge factors other than the generic and those
+ * explicitly defined above. The fudge time1 parameter can be used to
+ * compensate for miscellaneous UART and OS delays. Allow about 247 us
+ * for uart delays at 38400 bps and about 1 ms for SunOS streams
+ * nonsense.
+ */
+
+/*
+ * Interface definitions
+ */
+#define DEVICE "/dev/pps%d" /* device name and unit */
+#ifdef B38400
+#define SPEED232 B38400 /* uart speed (38400 baud) */
+#else
+#define SPEED232 EXTB /* as above */
+#endif
+#define PRECISION (-20) /* precision assumed (about 1 usec) */
+#define REFID "PPS\0" /* reference ID */
+#define DESCRIPTION "PPS Clock Discipline" /* WRU */
+
+#define PPSMAXDISPERSE (FP_SECOND / 100) /* max sample dispersion */
+#define NSAMPLES 32 /* final stages of median filter */
+#ifdef PPS
+#define PPS_POLL 2 /* ppsclock poll interval (s) */
+#endif /* PPS */
+
+/*
+ * Imported from ntp_timer module
+ */
+extern u_long current_time; /* current time (s) */
+extern struct event timerqueue[]; /* inner space */
+
+/*
+ * Imported from ntpd module
+ */
+extern int debug; /* global debug flag */
+
+/*
+ * Imported from ntp_loopfilter module
+ */
+extern int fdpps; /* pps file descriptor */
+extern int pps_update; /* prefer peer valid update */
+
+/*
+ * Imported from ntp_proto module
+ */
+extern struct peer *sys_peer; /* somebody in charge */
+
+/*
+ * Unit control structure
+ */
+struct atomunit {
+#ifdef PPS
+ struct event timer; /* pps poll interval timer */
+ struct ppsclockev ev; /* ppsclock control */
+#endif /* PPS */
+ int pollcnt; /* poll message counter */
+};
+
+/*
+ * Global variables
+ */
+struct peer *last_atom_peer; /* peer structure pointer */
+
+/*
+ * Function prototypes
+ */
+static int atom_start P((int, struct peer *));
+static void atom_shutdown P((int, struct peer *));
+static void atom_receive P((struct recvbuf *));
+static void atom_poll P((int, struct peer *));
+#ifdef PPS
+static void atom_pps P((struct peer *));
+#endif /* PPS */
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_atom = {
+ atom_start, /* start up driver */
+ atom_shutdown, /* shut down driver */
+ atom_poll, /* transmit poll message */
+ noentry, /* not used (old atom_control) */
+ noentry, /* initialize driver */
+ noentry, /* not used (old atom_buginfo) */
+ NOFLAGS /* not used */
+};
+
+
+/*
+ * atom_start - initialize data for processing
+ */
+static int
+atom_start(unit, peer)
+ int unit;
+ struct peer *peer;
+{
+ register struct atomunit *up;
+ struct refclockproc *pp;
+
+ /*
+ * Allocate and initialize unit structure
+ */
+ if (!(up = (struct atomunit *)
+ emalloc(sizeof(struct atomunit))))
+ return (0);
+ memset((char *)up, 0, sizeof(struct atomunit));
+ pp = peer->procptr;
+ pp->unitptr = (caddr_t)up;
+
+ /*
+ * Initialize miscellaneous variables
+ */
+ peer->precision = PRECISION;
+ pp->clockdesc = DESCRIPTION;
+ memcpy((char *)&pp->refid, REFID, 4);
+ up->pollcnt = 2;
+ pp->nstages = MAXSTAGE;
+
+#ifdef PPS
+ /*
+ * Arm the timer for the first interrupt. Give it ten seconds to
+ * allow the ppsclock line to be configured, since it could be
+ * assigned to another driver.
+ */
+ up->timer.peer = (struct peer *)peer;
+ up->timer.event_handler = atom_pps;
+ up->timer.event_time = current_time + 10;
+ TIMER_INSERT(timerqueue, &up->timer);
+#endif /* PPS */
+ last_atom_peer = peer;
+ return (1);
+}
+
+
+/*
+ * atom_shutdown - shut down the clock
+ */
+static void
+atom_shutdown(unit, peer)
+ int unit;
+ struct peer *peer;
+{
+ register struct atomunit *up;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ up = (struct atomunit *)pp->unitptr;
+
+ if (last_atom_peer == peer)
+ last_atom_peer = 0;
+#ifdef PPS
+ TIMER_DEQUEUE(&up->timer);
+#endif /* PPS */
+ if (pp->io.fd)
+ io_closeclock(&pp->io);
+ free(up);
+}
+
+/*
+ * pps_sample - process pps sample offset -- backwards compatible
+ * interface
+ */
+int
+pps_sample(tsr)
+ l_fp *tsr;
+{
+ struct peer *peer;
+ struct refclockproc *pp;
+ register struct atomunit *up;
+ int i;
+ l_fp lftemp; /* l_fp temps */
+
+ /*
+ * This routine is called once per second by an auxilliary
+ * routine in another driver. It saves the sign-extended
+ * fraction supplied in the argument in a circular buffer for
+ * processing at the next poll event.
+ */
+ peer = last_atom_peer;
+ if (!peer)
+ return (-1); /* no ATOM configured ? Forget it ! */
+
+ pp = peer->procptr;
+ up = (struct atomunit *)pp->unitptr;
+
+ L_CLR(&lftemp);
+ L_ADDF(&lftemp, tsr->l_f);
+ i = ((int)(pp->coderecv)) % pp->nstages;
+ pp->filter[i] = lftemp;
+ if (pp->coderecv == 0)
+ for (i = 1; i < pp->nstages; i++)
+ pp->filter[i] = pp->filter[0];
+ pp->coderecv++;
+ up->pollcnt = 2;
+
+ /* HACK -- use the local UN*X clock to get the time -- this is wrong */
+ pp->lastrec.l_ui = time(0) - 2 + JAN_1970;
+ pp->lastrec.l_uf = 0;
+
+ return (0);
+}
+
+#ifdef PPS
+/*
+ * atom_pps - receive data from the LDISC_PPS discipline
+ */
+static void
+atom_pps(peer)
+ struct peer *peer;
+{
+ register struct atomunit *up;
+ struct refclockproc *pp;
+ l_fp lftmp;
+ int i;
+
+ /*
+ * This routine is called once per second when the LDISC_PPS
+ * discipline is present. It snatches the pps timestamp from the
+ * kernel and saves the sign-extended fraction in a circular
+ * buffer for processing at the next poll event.
+ */
+ pp = peer->procptr;
+ up = (struct atomunit *)pp->unitptr;
+
+ /*
+ * Arm the timer for the next interrupt
+ */
+ up->timer.event_time = current_time + PPS_POLL;
+ TIMER_INSERT(timerqueue, &up->timer);
+
+ /*
+ * Convert the timeval to l_fp and save for billboards. Sign-
+ * extend the fraction and stash in the buffer. No harm is done
+ * if previous data are overwritten. If the discipline comes bum
+ * or the data grow stale, just forget it.
+ */
+ i = up->ev.serial;
+ if (ioctl(fdpps, CIOGETEV, (caddr_t)&up->ev) < 0)
+ return;
+ if (i == up->ev.serial)
+ return;
+ pp->lastrec.l_ui = up->ev.tv.tv_sec + JAN_1970;
+ TVUTOTSF(up->ev.tv.tv_usec, pp->lastrec.l_uf);
+ L_CLR(&lftmp);
+ L_ADDF(&lftmp, pp->lastrec.l_f);
+ L_NEG(&lftmp);
+ i = ((int)(pp->coderecv)) % pp->nstages;
+ pp->filter[i] = lftmp;
+ if (pp->coderecv == 0)
+ for (i = 1; i < pp->nstages; i++)
+ pp->filter[i] = pp->filter[0];
+ pp->coderecv++;
+ up->pollcnt = 2;
+}
+#endif /* PPS */
+
+/*
+ * atom_receive - receive data from the serial line interface
+ */
+static void
+atom_receive(rbufp)
+ struct recvbuf *rbufp;
+{
+ register struct atomunit *up;
+ struct refclockproc *pp;
+ struct peer *peer;
+ l_fp lftmp;
+ int i;
+
+ /*
+ * This routine is called once per second when the serial
+ * interface is in use. It snatches the timestamp from the
+ * buffer and saves the sign-extended fraction in a circular
+ * buffer for processing at the next poll event.
+ */
+ peer = (struct peer *)rbufp->recv_srcclock;
+ pp = peer->procptr;
+ up = (struct atomunit *)pp->unitptr;
+ pp->lencode = refclock_gtlin(rbufp, pp->lastcode, BMAX,
+ &pp->lastrec);
+
+ /*
+ * Save the timestamp for billboards. Sign-extend the fraction
+ * and stash in the buffer. No harm is done if previous data are
+ * overwritten.
+ */
+ L_CLR(&lftmp);
+ L_ADDF(&lftmp, pp->lastrec.l_f);
+ L_NEG(&lftmp);
+ i = ((int)(pp->coderecv)) % pp->nstages;
+ pp->filter[i] = lftmp;
+ if (pp->coderecv == 0)
+ for (i = 1; i < pp->nstages; i++)
+ pp->filter[i] = pp->filter[0];
+ pp->coderecv++;
+ up->pollcnt = 2;
+}
+
+/*
+ * Compare two l_fp's - used with qsort()
+ */
+static int
+atom_cmpl_fp(p1, p2)
+ register void *p1, *p2; /* l_fp to compare */
+{
+
+ if (!L_ISGEQ((l_fp *)p1, (l_fp *)p2))
+ return (-1);
+ if (L_ISEQU((l_fp *)p1, (l_fp *)p2))
+ return (0);
+ return (1);
+}
+
+/*
+ * atom_poll - called by the transmit procedure
+ */
+static void
+atom_poll(unit, peer)
+ int unit;
+ struct peer *peer;
+{
+ register struct atomunit *up;
+ struct refclockproc *pp;
+ int i, n;
+ l_fp median, lftmp;
+ l_fp off[MAXSTAGE];
+ u_fp disp;
+
+ /*
+ * At each poll we check for timeout. At the first timeout we
+ * test to see if the LDISC_PPS discipline is present and, if
+ * so, use that. If not, we attempt to open a serial line with
+ * LDISC_CLKPPS discipline. If that fails, we bitch to the log
+ * and clam up.
+ */
+ pp = peer->procptr;
+ up = (struct atomunit *)pp->unitptr;
+ pp->polls++;
+ if (up->pollcnt == 0) {
+ refclock_report(peer, CEVNT_FAULT);
+ return;
+ }
+ up->pollcnt--;
+ if (up->pollcnt == 0) {
+ if (!pp->io.fd && fdpps == -1) {
+ int fd;
+ char device[20];
+
+ /*
+ * Open serial port. Use CLKPPS line discipline,
+ * if available. If unavailable, the code works
+ * anyway, but at reduced accuracy.
+ */
+ (void)sprintf(device, DEVICE, unit);
+ if (!(fd = refclock_open(device, SPEED232,
+ LDISC_CLKPPS))) {
+ refclock_report(peer, CEVNT_FAULT);
+ return;
+ }
+ pp->io.clock_recv = atom_receive;
+ pp->io.srcclock = (caddr_t)peer;
+ pp->io.datalen = 0;
+ pp->io.fd = fd;
+ if (!io_addclock(&pp->io)) {
+ (void) close(fd);
+ refclock_report(peer, CEVNT_FAULT);
+ return;
+ }
+ }
+ }
+
+ /*
+ * Valid time (leap bits zero) is returned only if the prefer
+ * peer has survived the intersection algorithm and within
+ * CLOCK_MAX of local time and not too long ago. This insures
+ * the pps time is within +-0.5 s of the local time and the
+ * seconds numbering is unambiguous.
+ */
+ if (pps_update) {
+ pp->leap = 0;
+ pp->lasttime = current_time;
+ } else
+ pp->leap = LEAP_NOTINSYNC;
+
+ /*
+ * Copy the raw offsets and sort into ascending order
+ */
+ for (i = 0; i < MAXSTAGE; i++)
+ off[i] = pp->filter[i];
+ qsort((char *)off, pp->nstages, sizeof(l_fp), atom_cmpl_fp);
+
+ /*
+ * Reject the furthest from the median of nstages samples until
+ * nskeep samples remain.
+ */
+ i = 0;
+ n = pp->nstages;
+ while ((n - i) > NSAMPLES) {
+ lftmp = off[n - 1];
+ median = off[(n + i) / 2];
+ L_SUB(&lftmp, &median);
+ L_SUB(&median, &off[i]);
+ if (L_ISHIS(&median, &lftmp)) {
+ /* reject low end */
+ i++;
+ } else {
+ /* reject high end */
+ n--;
+ }
+ }
+
+ /*
+ * Compute the dispersion based on the difference between the
+ * extremes of the remaining offsets. Add to this the time since
+ * the last clock update, which represents the dispersion
+ * increase with time. We know that NTP_MAXSKEW is 16. If the
+ * sum is greater than the allowed sample dispersion, bail out.
+ * Otherwise, return the median offset plus the configured
+ * fudgetime1 value.
+ */
+ lftmp = off[n - 1];
+ L_SUB(&lftmp, &off[i]);
+ disp = LFPTOFP(&lftmp) + current_time - pp->lasttime;
+ if (disp > PPSMAXDISPERSE) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ pp->offset = off[(n + 1) / 2];
+ L_ADD(&pp->offset, &pp->fudgetime1);
+ pp->dispersion = disp;
+ refclock_receive(peer, &pp->offset, 0, pp->dispersion,
+ &pp->lastrec, &pp->lastrec, pp->leap);
+}
+
+#endif
diff --git a/usr.sbin/xntpd/xntpd/refclock_chu.c b/usr.sbin/xntpd/xntpd/refclock_chu.c
new file mode 100644
index 0000000..7b710fc
--- /dev/null
+++ b/usr.sbin/xntpd/xntpd/refclock_chu.c
@@ -0,0 +1,800 @@
+/*
+ * refclock_chu - clock driver for the CHU time code
+ */
+#if defined(REFCLOCK) && defined(CHU)
+
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/time.h>
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_unixtime.h"
+#include <sys/chudefs.h>
+#include "ntp_stdlib.h"
+
+/*
+ * The CHU time signal includes a time code which is modulated at the
+ * standard Bell 103 frequencies (i.e. mark=2225Hz, space=2025Hz).
+ * and formatted into 8 bit characters with one start bit and two
+ * stop bits. The time code is composed of 10 8-bit characters.
+ * The second 5 bytes of the timecode are a redundancy check, and
+ * are a copy of the first 5 bytes.
+ *
+ * It is assumed that you have built or modified a Bell 103 standard
+ * modem, attached the input to the output of a radio and cabled the
+ * output to a serial port on your computer, i.e. what you are receiving
+ * is essentially the output of your radio. It is also assumed you have
+ * installed a special CHU line discipline to condition the output from
+ * the terminal driver and take accurate time stamps.
+ *
+ * There are two types of timecodes. One is sent in the 32nd
+ * through 39th second of the minute.
+ *
+ * 6dddhhmmss6dddhhmmss
+ *
+ * where ddd is the day of the year, hh is the hour (in UTC), mm is
+ * the minute and ss the second. The 6 is a constant. Note that
+ * the code is sent twice.
+ *
+ * The second sort of timecode is sent only during the 31st second
+ * past the minute.
+ *
+ * xdyyyyttabXDYYYYTTAB
+ *
+ * In this case, the second part of the code is the one's complement
+ * of the code. This differentiates it from the other timecode
+ * format.
+ *
+ * d is the absolute value of DUT (in tenths of a second). yyyy
+ * is the year. tt is the difference between UTC and TAI. a is
+ * a canadian daylight time flag and b is a serial number.
+ * x is a bitwise field. The least significant bit of x is
+ * one if DUT is negative. The 2nd bit is set if a leap second
+ * will be added at the next opportunity. The 3rd bit is set if
+ * a leap second will be deleted at the next opportunity.
+ * The 4th bit is an even parity bit for the other three bits
+ * in this nibble.
+ *
+ * The start bit in each character has a precise relationship to
+ * the on-time second. Most often UART's synchronize themselves to the
+ * start bit and will post an interrupt at the center of the first stop
+ * bit. Thus each character's interrupt should occur at a fixed offset
+ * from the on-time second. This means that a timestamp taken at the
+ * arrival of each character in the code will provide an independent
+ * estimate of the offset. Since there are 10 characters in the time
+ * code and the code is sent 9 times per minute, this means you
+ * potentially get 90 offset samples per minute. Much of the code in
+ * here is dedicated to producing a single offset estimate from these
+ * samples.
+ *
+ * A note about the line discipline. It is possible to receive the
+ * CHU time code in raw mode, but this has disadvantages. In particular,
+ * this puts a lot of code between the interrupt and the time you freeze
+ * a time stamp, decreasing precision. It is also expensive in terms of
+ * context switches, and made even more expensive by the way I do I/O.
+ * Worse, since you are listening directly to the output of your radio,
+ * CHU is noisy and will make you spend a lot of time receiving noise.
+ *
+ * The line discipline fixes a lot of this. It knows that the CHU time
+ * code consists of 10 bytes which arrive with an intercharacter
+ * spacing of about 37 ms, and that the data is BCD, and filters on this
+ * basis. It delivers block of ten characters plus their associated time
+ * stamps all at once. The time stamps are hence about as accurate as
+ * a Unix machine can get them, and much of the noise disappears in the
+ * kernel with no context switching cost.
+ *
+ * The kernel module also will insure that the packets that are
+ * delivered have the correct redundancy bytes, and will return
+ * a flag in chutype to differentiate one sort of packet from
+ * the other.
+ */
+
+/*
+ * CHU definitions
+ */
+#define DEVICE "/dev/chu%d" /* device name and unit */
+#define SPEED232 B300 /* uart speed (300 baud) */
+#define PRECISION (-9) /* what the heck */
+#define REFID "CHU\0" /* reference ID */
+#define DESCRIPTION "Scratchbuilt CHU Receiver" /* WRU */
+
+#define NCHUCODES 8 /* expect 8 CHU codes per minute */
+#ifndef CHULDISC
+#define CHULDISC 10 /* XXX temp CHU line discipline */
+#endif
+
+/*
+ * To compute a quality for the estimate (a pseudo dispersion) we add a
+ * fixed 10 ms for each missing code in the minute and add to this
+ * the sum of the differences between the remaining offsets and the
+ * estimated sample offset.
+ */
+#define CHUDELAYPENALTY 0x0000028f
+
+/*
+ * Default fudge factors
+ */
+#define DEFPROPDELAY 0x00624dd3 /* 0.0015 seconds, 1.5 ms */
+#define DEFFILTFUDGE 0x000d1b71 /* 0.0002 seconds, 200 us */
+
+/*
+ * Hacks to avoid excercising the multiplier. I have no pride.
+ */
+#define MULBY10(x) (((x)<<3) + ((x)<<1))
+#define MULBY60(x) (((x)<<6) - ((x)<<2)) /* watch overflow */
+#define MULBY24(x) (((x)<<4) + ((x)<<3))
+
+/*
+ * Constants for use when multiplying by 0.1. ZEROPTONE is 0.1
+ * as an l_fp fraction, NZPOBITS is the number of significant bits
+ * in ZEROPTONE.
+ */
+#define ZEROPTONE 0x1999999a
+#define NZPOBITS 29
+
+static char hexstring[]="0123456789abcdef";
+
+/*
+ * Unit control structure.
+ */
+struct chuunit {
+ struct peer *peer; /* peer structure pointer */
+ struct event chutimer; /* timeout timer structure */
+ l_fp offsets[NCHUCODES]; /* offsets computed from each code */
+ l_fp rectimes[NCHUCODES]; /* times we received this stuff */
+ u_long reftimes[NCHUCODES]; /* time of last code received */
+ u_char lastcode[NCHUCHARS * 4]; /* last code we received */
+ u_char expect; /* the next offset expected */
+ u_short haveoffset; /* flag word indicating valid offsets */
+ u_short flags; /* operational flags */
+ u_long responses; /* number of responses */
+ int pollcnt; /* poll message counter */
+};
+
+#define CHUTIMERSET 0x1 /* timer is set to fire */
+
+
+/*
+ * The CHU table. This gives the expected time of arrival of each
+ * character after the on-time second and is computed as follows:
+ * The CHU time code is sent at 300 bps. Your average UART will
+ * synchronize at the edge of the start bit and will consider the
+ * character complete at the middle of the first stop bit, i.e.
+ * 0.031667 ms later (some UARTS may complete the character at the
+ * end of the stop bit instead of the middle, but you can fudge this).
+ * Thus the expected time of each interrupt is the start bit time plus
+ * 0.031667 seconds. These times are in chutable[].
+ */
+#define CHARDELAY 0x081b4e82
+
+static u_long chutable[NCHUCHARS] = {
+ 0x22222222 + CHARDELAY, /* 0.1333333333 */
+ 0x2b851eb8 + CHARDELAY, /* 0.170 (exactly) */
+ 0x34e81b4e + CHARDELAY, /* 0.2066666667 */
+ 0x3f92c5f9 + CHARDELAY, /* 0.2483333333 */
+ 0x47ae147b + CHARDELAY, /* 0.280 (exactly) */
+ 0x51111111 + CHARDELAY, /* 0.3166666667 */
+ 0x5a740da7 + CHARDELAY, /* 0.3533333333 */
+ 0x63d70a3d + CHARDELAY, /* 0.390 (exactly) */
+ 0x6d3a06d4 + CHARDELAY, /* 0.4266666667 */
+ 0x769d0370 + CHARDELAY, /* 0.4633333333 */
+};
+
+/*
+ * Imported from the timer module
+ */
+extern u_long current_time;
+extern struct event timerqueue[];
+
+/*
+ * Imported from ntpd module
+ */
+extern int debug; /* global debug flag */
+
+/*
+ * Function prototypes
+ */
+static int chu_start P((int, struct peer *));
+static void chu_shutdown P((int, struct peer *));
+static void chu_receive P((struct recvbuf *));
+static void chu_process P((struct chuunit *));
+static void chu_poll P((int, struct peer *));
+static void chu_timeout P((struct peer *));
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_chu = {
+ chu_start, /* start up driver */
+ chu_shutdown, /* shut down driver */
+ chu_poll, /* transmit poll message */
+ noentry, /* not used (old chu_control) */
+ noentry, /* initialize driver (not used) */
+ noentry, /* not used (old chu_buginfo) */
+ NOFLAGS /* not used */
+};
+
+
+/*
+ * chu_start - open the CHU device and initialize data for processing
+ */
+static int
+chu_start(unit, peer)
+ int unit;
+ struct peer *peer;
+{
+ register struct chuunit *up;
+ struct refclockproc *pp;
+ int fd;
+ char device[20];
+
+ /*
+ * Open serial port and set CHU line discipline
+ */
+ (void) sprintf(device, DEVICE, unit);
+ if (!(fd = refclock_open(device, SPEED232, LDISC_CHU)))
+ return (0);
+
+ /*
+ * Allocate and initialize unit structure
+ */
+ if (!(up = (struct chuunit *)
+ emalloc(sizeof(struct chuunit)))) {
+ (void) close(fd);
+ return (0);
+ }
+ memset((char *)up, 0, sizeof(struct chuunit));
+ up->chutimer.peer = (struct peer *)up;
+ up->chutimer.event_handler = chu_timeout;
+ up->peer = peer;
+ pp = peer->procptr;
+ pp->io.clock_recv = chu_receive;
+ pp->io.srcclock = (caddr_t)peer;
+ pp->io.datalen = 0;
+ pp->io.fd = fd;
+ if (!io_addclock(&pp->io)) {
+ (void) close(fd);
+ free(up);
+ return (0);
+ }
+ pp->unitptr = (caddr_t)up;
+
+ /*
+ * Initialize miscellaneous variables
+ */
+ peer->precision = PRECISION;
+ pp->clockdesc = DESCRIPTION;
+ memcpy((char *)&pp->refid, REFID, 4);
+ up->pollcnt = 2;
+ return (1);
+}
+
+
+/*
+ * chu_shutdown - shut down the clock
+ */
+static void
+chu_shutdown(unit, peer)
+ int unit;
+ struct peer *peer;
+{
+ register struct chuunit *up;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ up = (struct chuunit *)pp->unitptr;
+ io_closeclock(&pp->io);
+ free(up);
+}
+
+
+/*
+ * chu_receive - receive data from a CHU clock, do format checks and compute
+ * an estimate from the sample data
+ */
+static void
+chu_receive(rbufp)
+ struct recvbuf *rbufp;
+{
+ register struct chuunit *up;
+ struct refclockproc *pp;
+ struct peer *peer;
+ int i;
+ u_long date_ui;
+ u_long tmp;
+ u_char *code;
+ struct chucode *chuc;
+ int isneg;
+ u_long reftime;
+ l_fp off[NCHUCHARS];
+ int day, hour, minute, second;
+
+ /*
+ * Do a length check on the data. Should be what we asked for.
+ */
+ if (rbufp->recv_length != sizeof(struct chucode)) {
+ syslog(LOG_ERR,
+ "chu_receive: received %d bytes, expected %d",
+ rbufp->recv_length, sizeof(struct chucode));
+ return;
+ }
+
+ /*
+ * Get the clock this applies to and a pointer to the data
+ */
+ peer = (struct peer *)rbufp->recv_srcclock;
+ pp = peer->procptr;
+ up = (struct chuunit *)pp->unitptr;
+ chuc = (struct chucode *)&rbufp->recv_space;
+ up->responses++;
+
+ /*
+ * Just for fun, we can debug the whole frame if
+ * we want.
+ */
+ for (i = 0; i < NCHUCHARS; i++) {
+ pp->lastcode[2 * i] = hexstring[chuc->codechars[i] &
+ 0xf];
+ pp->lastcode[2 * i + 1] = hexstring[chuc->codechars[i]
+ >> 4];
+ }
+ pp->lencode = 2 * i;
+ pp->lastcode[pp->lencode] = '\0';
+#ifdef DEBUG
+ if (debug > 3) {
+ printf("chu: %s packet\n", (chuc->chutype == CHU_YEAR)?
+ "year":"time");
+ for (i = 0; i < NCHUCHARS; i++) {
+ char c[64];
+
+ sprintf(c,"%c%c %s",
+ hexstring[chuc->codechars[i] & 0xf],
+ hexstring[chuc->codechars[i] >> 4],
+ ctime(&(chuc->codetimes[i].tv_sec)));
+ c[strlen(c) - 1] = 0; /* ctime() adds \n */
+ printf("chu: %s .%06d\n", c,
+ chuc->codetimes[i].tv_usec);
+ }
+ }
+#endif
+
+ /*
+ * At this point we're assured that both halves of the
+ * data match because of what the kernel has done.
+ * But there's more than one data format. We need to
+ * check chutype to see what to do now. If it's a
+ * year packet, then we fiddle with it specially.
+ */
+
+ if (chuc->chutype == CHU_YEAR)
+ {
+ u_char leapbits,parity;
+
+ /*
+ * Break out the code into the BCD nibbles.
+ * Put it in the half of lastcode.
+ */
+ code = up->lastcode;
+ code += 2*NCHUCHARS;
+ for (i = 0; i < NCHUCHARS; i++) {
+ *code++ = chuc->codechars[i] & 0xf;
+ *code++ = (chuc->codechars[i] >> 4) & 0xf;
+ }
+
+ leapbits = chuc->codechars[0]&0xf;
+
+ /*
+ * Now make sure that the leap nibble
+ * is even parity.
+ */
+
+ parity = (leapbits ^ (leapbits >> 2))&0x3;
+ parity = (parity ^ (parity>>1))&0x1;
+ if (parity)
+ {
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+
+ /*
+ * This just happens to work. :-)
+ */
+
+ pp->leap = (leapbits >> 1) & 0x3;
+
+ return;
+ }
+
+ if (chuc->chutype != CHU_TIME)
+ {
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+
+ /*
+ * Break out the code into the BCD nibbles. Only need to fiddle
+ * with the first half since both are identical. Note the first
+ * BCD character is the low order nibble, the second the high order.
+ */
+ code = up->lastcode;
+ for (i = 0; i < NCHUCHARS; i++) {
+ *code++ = chuc->codechars[i] & 0xf;
+ *code++ = (chuc->codechars[i] >> 4) & 0xf;
+ }
+
+ /*
+ * Format check. Make sure the two halves match.
+ * There's really no need for this, but it can't hurt.
+ */
+ for (i = 0; i < NCHUCHARS/2; i++)
+ if (chuc->codechars[i] !=
+ chuc->codechars[i+(NCHUCHARS/2)]) {
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+
+ /*
+ * If the first nibble isn't a 6, we're up the creek
+ */
+ code = up->lastcode;
+ if (*code++ != 6) {
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+
+ /*
+ * Collect the day, the hour, the minute and the second.
+ */
+ day = *code++;
+ day = MULBY10(day) + *code++;
+ day = MULBY10(day) + *code++;
+ hour = *code++;
+ hour = MULBY10(hour) + *code++;
+ minute = *code++;
+ minute = MULBY10(minute) + *code++;
+ second = *code++;
+ second = MULBY10(second) + *code++;
+
+ /*
+ * Sanity check the day and time. Note that this
+ * only occurs on the 32st through the 39th second
+ * of the minute.
+ */
+ if (day < 1 || day > 366
+ || hour > 23 || minute > 59
+ || second < 32 || second > 39) {
+ pp->baddata++;
+ if (day < 1 || day > 366) {
+ refclock_report(peer, CEVNT_BADDATE);
+ } else {
+ refclock_report(peer, CEVNT_BADTIME);
+ }
+ return;
+ }
+
+ /*
+ * Compute the NTP date from the input data and the
+ * receive timestamp. If this doesn't work, mark the
+ * date as bad and forget it.
+ */
+ if (!clocktime(day, hour, minute, second, 0,
+ rbufp->recv_time.l_ui, &pp->yearstart, (U_LONG *)&reftime)) {
+ refclock_report(peer, CEVNT_BADDATE);
+ return;
+ }
+ date_ui = reftime;;
+
+ /*
+ * We've now got the integral seconds part of the time code (we hope).
+ * The fractional part comes from the table. We next compute
+ * the offsets for each character.
+ */
+ for (i = 0; i < NCHUCHARS; i++) {
+ register u_long tmp2;
+
+ off[i].l_ui = date_ui;
+ off[i].l_uf = chutable[i];
+ tmp = chuc->codetimes[i].tv_sec + JAN_1970;
+ TVUTOTSF(chuc->codetimes[i].tv_usec, tmp2);
+ M_SUB(off[i].l_ui, off[i].l_uf, tmp, tmp2);
+ }
+
+ if (!pp->sloppyclockflag) {
+ u_short ord[NCHUCHARS];
+ /*
+ * In here we assume the clock has adequate bits
+ * to take timestamps with reasonable accuracy.
+ * Note that the time stamps may contain errors
+ * for a couple of reasons. Timing is actually
+ * referenced to the start bit in each character
+ * in the time code. If this is obscured by static
+ * you can still get a valid character but have the
+ * timestamp offset by +-1.5 ms. Also, we may suffer
+ * from interrupt delays if the interrupt is being
+ * held off when the character arrives. Note the
+ * latter error is always in the form of a delay.
+ *
+ * After fiddling I arrived at the following scheme.
+ * We sort the times into order by offset. We then
+ * drop the most positive 2 offset values (which may
+ * correspond to a character arriving early due to
+ * static) and the most negative 4 (which may correspond
+ * to delayed characters, either from static or from
+ * interrupt latency). We then take the mean of the
+ * remaining 4 offsets as our estimate.
+ */
+
+ /*
+ * Set up the order array.
+ */
+ for (i = 0; i < NCHUCHARS; i++)
+ ord[i] = (u_short)i;
+
+ /*
+ * Sort them into order. Reuse variables with abandon.
+ */
+ for (tmp = 0; tmp < (NCHUCHARS-1); tmp++) {
+ for (i = (int)tmp+1; i < NCHUCHARS; i++) {
+ if (!L_ISGEQ(&off[ord[i]], &off[ord[tmp]])) {
+ date_ui = (u_long)ord[i];
+ ord[i] = ord[tmp];
+ ord[tmp] = (u_short)date_ui;
+ }
+ }
+ }
+
+ /*
+ * Done the sort. We drop 0, 1, 2 and 3 at the negative
+ * end, and 8 and 9 at the positive. Take the sum of
+ * 4, 5, 6 and 7.
+ */
+ date_ui = off[ord[4]].l_ui;
+ tmp = off[ord[4]].l_uf;
+ for (i = 5; i <= 7; i++)
+ M_ADD(date_ui, tmp, off[ord[i]].l_ui, off[ord[i]].l_uf);
+
+ /*
+ * Round properly, then right shift two bits for the
+ * divide by four.
+ */
+ if (tmp & 0x2)
+ M_ADDUF(date_ui, tmp, 0x4);
+ M_RSHIFT(date_ui, tmp);
+ M_RSHIFT(date_ui, tmp);
+ } else {
+ /*
+ * Here is a *big* problem. On a machine where the
+ * low order bit in the clock is on the order of half
+ * a millisecond or more we don't really have enough
+ * precision to make intelligent choices about which
+ * samples might be in error and which aren't. More
+ * than this, in the case of error free data we can
+ * pick up a few bits of precision by taking the mean
+ * of the whole bunch. This is what we do. The problem
+ * comes when it comes time to divide the 64 bit sum of
+ * the 10 samples by 10, a procedure which really sucks.
+ * Oh, well, grin and bear it. Compute the sum first.
+ */
+ date_ui = 0;
+ tmp = 0;
+ for (i = 0; i < NCHUCHARS; i++)
+ M_ADD(date_ui, tmp, off[i].l_ui, off[i].l_uf);
+ if (M_ISNEG(date_ui, tmp))
+ isneg = 1;
+ else
+ isneg = 0;
+
+ /*
+ * Here is a multiply-by-0.1 optimization that should apply
+ * just about everywhere. If the magnitude of the sum
+ * is less than 9 we don't have to worry about overflow
+ * out of a 64 bit product, even after rounding.
+ */
+ if (date_ui < 9 || date_ui > 0xfffffff7) {
+ register u_long prod_ui;
+ register u_long prod_uf;
+
+ prod_ui = prod_uf = 0;
+ /*
+ * This code knows the low order bit in 0.1 is zero
+ */
+ for (i = 1; i < NZPOBITS; i++) {
+ M_LSHIFT(date_ui, tmp);
+ if (ZEROPTONE & (1<<i))
+ M_ADD(prod_ui, prod_uf, date_ui, tmp);
+ }
+
+ /*
+ * Done, round it correctly. Prod_ui contains the
+ * fraction.
+ */
+ if (prod_uf & 0x80000000)
+ prod_ui++;
+ if (isneg)
+ date_ui = 0xffffffff;
+ else
+ date_ui = 0;
+ tmp = prod_ui;
+ /*
+ * date_ui is integral part, tmp is fraction.
+ */
+ } else {
+ register u_long prod_ovr;
+ register u_long prod_ui;
+ register u_long prod_uf;
+ register u_long highbits;
+
+ prod_ovr = prod_ui = prod_uf = 0;
+ if (isneg)
+ highbits = 0xffffffff; /* sign extend */
+ else
+ highbits = 0;
+ /*
+ * This code knows the low order bit in 0.1 is zero
+ */
+ for (i = 1; i < NZPOBITS; i++) {
+ M_LSHIFT3(highbits, date_ui, tmp);
+ if (ZEROPTONE & (1<<i))
+ M_ADD3(prod_ovr, prod_uf, prod_ui,
+ highbits, date_ui, tmp);
+ }
+
+ if (prod_uf & 0x80000000)
+ M_ADDUF(prod_ovr, prod_ui, (u_long)1);
+ date_ui = prod_ovr;
+ tmp = prod_ui;
+ }
+ }
+
+ /*
+ * At this point we have the mean offset, with the integral
+ * part in date_ui and the fractional part in tmp. Store
+ * it in the structure.
+ */
+ i = second - 32; /* gives a value 0 through 8 */
+ if (i < (int)up->expect) {
+ /*
+ * This shouldn't actually happen, but might if a single
+ * bit error occurred in the code which fooled us.
+ * Throw away all previous data.
+ */
+ up->expect = 0;
+ up->haveoffset = 0;
+ if (up->flags & CHUTIMERSET) {
+ TIMER_DEQUEUE(&up->chutimer);
+ up->flags &= ~CHUTIMERSET;
+ }
+ }
+
+ up->offsets[i].l_ui = date_ui;
+ up->offsets[i].l_uf = tmp;
+ up->rectimes[i] = rbufp->recv_time;
+ up->reftimes[i] = reftime;
+
+ up->expect = i + 1;
+ up->haveoffset |= (1 << i);
+
+ if (up->expect >= NCHUCODES) {
+ /*
+ * Got a full second's worth. Dequeue timer and
+ * process this.
+ */
+ if (up->flags & CHUTIMERSET) {
+ TIMER_DEQUEUE(&up->chutimer);
+ up->flags &= ~CHUTIMERSET;
+ }
+ chu_process(up);
+ } else if (!(up->flags & CHUTIMERSET)) {
+ /*
+ * Try to take an interrupt sometime after the
+ * 42 second mark (leaves an extra 2 seconds for
+ * slop). Round it up to an even multiple of
+ * 4 seconds.
+ */
+ up->chutimer.event_time =
+ current_time + (u_long)(10 - i) + (1<<EVENT_TIMEOUT);
+ up->chutimer.event_time &= ~((1<<EVENT_TIMEOUT) - 1);
+ TIMER_INSERT(timerqueue, &up->chutimer);
+ up->flags |= CHUTIMERSET;
+ }
+}
+
+
+/*
+ * chu_timeout - process a timeout event
+ */
+static void
+chu_timeout(fakepeer)
+ struct peer *fakepeer;
+{
+ /*
+ * If we got here it means we received some time codes
+ * but didn't get the one which should have arrived on
+ * the 39th second. Process what we have.
+ */
+ ((struct chuunit *)fakepeer)->flags &= ~CHUTIMERSET;
+ chu_process((struct chuunit *)fakepeer);
+}
+
+
+/*
+ * chu_process - process the raw offset estimates we have and pass
+ * the results on to the NTP clock filters.
+ */
+static void
+chu_process(up)
+ register struct chuunit *up;
+{
+ struct peer *peer;
+ struct refclockproc *pp;
+ int i;
+ s_fp bestoff;
+ s_fp tmpoff;
+ u_fp dispersion;
+ int imax;
+
+ /*
+ * The most positive offset.
+ */
+ peer = up->peer;
+ pp = peer->procptr;
+ imax = NCHUCODES;
+ for (i = 0; i < NCHUCODES; i++)
+ if (up->haveoffset & (1<<i))
+ if (i < imax || L_ISGEQ(&up->offsets[i],
+ &up->offsets[imax]))
+ imax = i;
+
+ /*
+ * The most positive estimate is our best bet. Go through
+ * the list again computing the dispersion.
+ */
+ bestoff = LFPTOFP(&up->offsets[imax]);
+ dispersion = 0;
+ for (i = 0; i < NCHUCODES; i++) {
+ if (up->haveoffset & (1<<i)) {
+ tmpoff = LFPTOFP(&up->offsets[i]);
+ dispersion += (bestoff - tmpoff);
+ } else {
+ dispersion += CHUDELAYPENALTY;
+ }
+ }
+
+ pp->lasttime = current_time;
+ up->pollcnt = 2;
+ record_clock_stats(&peer->srcadr, pp->lastcode);
+ refclock_receive(peer, &up->offsets[imax], 0,
+ dispersion, &up->rectimes[imax], &up->rectimes[imax],
+ pp->leap);
+
+ /*
+ * Zero out unit for next code series
+ */
+ up->haveoffset = 0;
+ up->expect = 0;
+ refclock_report(peer, CEVNT_NOMINAL);
+}
+
+
+/*
+ * chu_poll - called by the transmit procedure
+ */
+static void
+chu_poll(unit, peer)
+ int unit;
+ struct peer *peer;
+{
+ register struct chuunit *up;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ up = (struct chuunit *)pp->unitptr;
+ if (up->pollcnt == 0)
+ refclock_report(peer, CEVNT_TIMEOUT);
+ else
+ up->pollcnt--;
+}
+
+#endif
diff --git a/usr.sbin/xntpd/xntpd/refclock_conf.c b/usr.sbin/xntpd/xntpd/refclock_conf.c
new file mode 100644
index 0000000..c0674d9
--- /dev/null
+++ b/usr.sbin/xntpd/xntpd/refclock_conf.c
@@ -0,0 +1,185 @@
+/*
+ * refclock_conf.c - reference clock configuration
+ */
+#include <stdio.h>
+#include <sys/types.h>
+
+#include "ntpd.h"
+#include "ntp_refclock.h"
+#include "ntp_stdlib.h"
+
+#ifdef REFCLOCK
+
+static struct refclock refclock_none = {
+ noentry, noentry, noentry, noentry, noentry, noentry, NOFLAGS
+};
+
+#ifdef LOCAL_CLOCK
+extern struct refclock refclock_local;
+#else
+#define refclock_local refclock_none
+#endif
+
+#if defined(TRAK) || defined(TRAKCLK) || defined(TRAKPPS)
+extern struct refclock refclock_trak;
+#else
+#define refclock_trak refclock_none
+#endif
+
+#if defined(PST)
+extern struct refclock refclock_pst;
+#else
+#define refclock_pst refclock_none
+#endif
+
+#if defined(CHU)
+extern struct refclock refclock_chu;
+#else
+#define refclock_chu refclock_none
+#endif
+
+#if defined(GOES) || defined(GOESCLK) || defined(GOESPPS)
+extern struct refclock refclock_goes;
+#else
+#define refclock_goes refclock_none
+#endif
+
+#if defined(WWVB)
+extern struct refclock refclock_wwvb;
+#else
+#define refclock_wwvb refclock_none
+#endif
+
+#if defined(PARSE) || defined(PARSEPPS)
+extern struct refclock refclock_parse;
+#else
+#define refclock_parse refclock_none
+#endif
+
+#if defined(PPS) && (defined(MX4200) || defined(MX4200CLK) || defined(MX4200PPS))
+extern struct refclock refclock_mx4200;
+#else
+#define refclock_mx4200 refclock_none
+#endif
+
+#if defined(AS2201)
+extern struct refclock refclock_as2201;
+#else
+#define refclock_as2201 refclock_none
+#endif
+
+#if defined(OMEGA) || defined(OMEGACLK) || defined(OMEGAPPS)
+extern struct refclock refclock_omega;
+#else
+#define refclock_omega refclock_none
+#endif
+
+#if defined(TPRO) && defined(sun) /* XXX sun only */
+extern struct refclock refclock_tpro;
+#else
+#define refclock_tpro refclock_none
+#endif
+
+#if defined(LEITCH) || defined(LEITCHCLK) || defined(LEITCHPPS)
+extern struct refclock refclock_leitch;
+#else
+#define refclock_leitch refclock_none
+#endif
+
+#if defined(IRIG) && defined(sun) /* XXX sun only */
+extern struct refclock refclock_irig;
+#else
+#define refclock_irig refclock_none
+#endif
+
+#if defined(MSFEESPPS)
+extern struct refclock refclock_msfees;
+#else
+#define refclock_msfees refclock_none
+#endif
+
+#if defined(GPSTM) || defined(GPSTMCLK) || defined(GPSTMPPS)
+extern struct refclock refclock_gpstm;
+#else
+#define refclock_gpstm refclock_none
+#endif
+
+#if defined(BANC) || defined(BANCCLK) || defined(BANCPPS)
+extern struct refclock refclock_bancomm;
+#else
+#define refclock_bancomm refclock_none
+#endif
+
+#ifdef DATUM
+extern struct refclock refclock_datum;
+#else
+#define refclock_datum refclock_none
+#endif
+
+#ifdef ACTS
+extern struct refclock refclock_acts;
+#else
+#define refclock_acts refclock_none
+#endif
+
+#ifdef HEATH
+extern struct refclock refclock_heath;
+#else
+#define refclock_heath refclock_none
+#endif
+
+#ifdef NMEA
+extern struct refclock refclock_nmea;
+#else
+#define refclock_nmea refclock_none
+#endif
+
+#ifdef MOTO
+extern struct refclock refclock_moto;
+#else
+#define refclock_moto refclock_none
+#endif
+
+#ifdef ATOM
+extern struct refclock refclock_atom;
+#else
+#define refclock_atom refclock_none
+#endif
+
+/*
+ * Order is clock_start(), clock_shutdown(), clock_poll(),
+ * clock_control(), clock_init(), clock_buginfo, clock_flags;
+ *
+ * Types are defined in ntp.h. The index must match this.
+ */
+struct refclock *refclock_conf[] = {
+ &refclock_none, /* 0 REFCLK_NONE */
+ &refclock_local, /* 1 REFCLK_LOCAL */
+ &refclock_trak, /* 2 REFCLK_GPS_TRAK */
+ &refclock_pst, /* 3 REFCLK_WWV_PST */
+ &refclock_wwvb, /* 4 REFCLK_WWVB_SPECTRACOM */
+ &refclock_goes, /* 5 REFCLK_GOES_TRUETIME */
+ &refclock_irig, /* 6 REFCLK_IRIG_AUDIO */
+ &refclock_chu, /* 7 REFCLK_CHU */
+ &refclock_parse, /* 8 REFCLK_PARSE */
+ &refclock_mx4200, /* 9 REFCLK_GPS_MX4200 */
+ &refclock_as2201, /* 10 REFCLK_GPS_AS2201 */
+ &refclock_omega, /* 11 REFCLK_OMEGA_TRUETIME */
+ &refclock_tpro, /* 12 REFCLK_IRIG_TPRO */
+ &refclock_leitch, /* 13 REFCLK_ATOM_LEITCH */
+ &refclock_msfees, /* 14 REFCLK_MSF_EES */
+ &refclock_gpstm, /* 15 REFCLK_GPSTM_TRUETIME */
+ &refclock_bancomm, /* 16 REFCLK_IRIG_BANCOMM */
+ &refclock_datum, /* 17 REFCLK_GPS_DATUM */
+ &refclock_acts, /* 18 REFCLK_NIST_ACTS */
+ &refclock_heath, /* 19 REFCLK_WWV_HEATH */
+ &refclock_nmea, /* 20 REFCLK_GPS_NMEA */
+ &refclock_moto, /* 21 REFCLK_GPS_MOTO */
+ &refclock_atom, /* 22 REFCLK_ATOM_PPS */
+ &refclock_none, /* 23 reserved */
+ &refclock_none, /* 24 reserved */
+};
+
+u_char num_refclock_conf = sizeof(refclock_conf)/sizeof(struct refclock *);
+
+#endif
diff --git a/usr.sbin/xntpd/xntpd/refclock_datum.c b/usr.sbin/xntpd/xntpd/refclock_datum.c
new file mode 100644
index 0000000..00449c0
--- /dev/null
+++ b/usr.sbin/xntpd/xntpd/refclock_datum.c
@@ -0,0 +1,871 @@
+/*
+** refclock_datum - clock driver for the Datum Programmable Time Server
+**
+** Important note: This driver assumes that you have termios. If you have
+** a system that does not have termios, you will have to modify this driver.
+**
+** Sorry, I have only tested this driver on SUN and HP platforms.
+*/
+
+#if defined(REFCLOCK) && (defined(DATUM) || defined(DATUMCLK) || defined(DATUMPPS))
+
+/*
+** Include Files
+*/
+
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/time.h>
+#include <sys/errno.h>
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_unixtime.h"
+
+#if defined(HAVE_BSD_TTYS)
+#include <sgtty.h>
+#endif /* HAVE_BSD_TTYS */
+
+#if defined(HAVE_SYSV_TTYS)
+#include <termio.h>
+#endif /* HAVE_SYSV_TTYS */
+
+#if defined(HAVE_TERMIOS)
+#include <termios.h>
+#endif
+#if defined(STREAM)
+#include <stropts.h>
+#if defined(WWVBCLK)
+#include <sys/clkdefs.h>
+#endif /* WWVBCLK */
+#endif /* STREAM */
+
+#if defined (WWVBPPS)
+#include <sys/ppsclock.h>
+#endif /* WWVBPPS */
+
+#include "ntp_stdlib.h"
+
+/*
+** This driver supports the Datum Programmable Time System (PTS) clock.
+** The clock works in very straight forward manner. When it receives a
+** time code request (e.g., the ascii string "//k/mn"), it responds with
+** a seven byte BCD time code. This clock only responds with a
+** time code after it first receives the "//k/mn" message. It does not
+** periodically send time codes back at some rate once it is started.
+** the returned time code can be broken down into the following fields.
+**
+** _______________________________
+** Bit Index | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
+** ===============================
+** byte 0: | - - - - | H D |
+** ===============================
+** byte 1: | T D | U D |
+** ===============================
+** byte 2: | - - | T H | U H |
+** ===============================
+** byte 3: | - | T M | U M |
+** ===============================
+** byte 4: | - | T S | U S |
+** ===============================
+** byte 5: | t S | h S |
+** ===============================
+** byte 6: | m S | - - - - |
+** ===============================
+**
+** In the table above:
+**
+** "-" means don't care
+** "H D", "T D", and "U D" means Hundreds, Tens, and Units of Days
+** "T H", and "UH" means Tens and Units of Hours
+** "T M", and "U M" means Tens and Units of Minutes
+** "T S", and "U S" means Tens and Units of Seconds
+** "t S", "h S", and "m S" means tenths, hundredths, and thousandths
+** of seconds
+**
+** The Datum PTS communicates throught the RS232 port on your machine.
+** Right now, it assumes that you have termios. This driver has been tested
+** on SUN and HP workstations. The Datum PTS supports various IRIG and
+** NASA input codes. This driver assumes that the name of the device is
+** /dev/datum. You will need to make a soft link to your RS232 device or
+** create a new driver to use this refclock.
+*/
+
+/*
+** Datum PTS defines
+*/
+
+/*
+** Note that if GMT is defined, then the Datum PTS must use Greenwich
+** time. Otherwise, this driver allows the Datum PTS to use the current
+** wall clock for its time. It determines the time zone offset by minimizing
+** the error after trying several time zone offsets. If the Datum PTS
+** time is Greenwich time and GMT is not defined, everything should still
+** work since the time zone will be found to be 0. What this really means
+** is that your system time (at least to start with) must be within the
+** correct time by less than +- 30 minutes. The default is for GMT to not
+** defined. If you really want to force GMT without the funny +- 30 minute
+** stuff then you must define (uncomment) GMT below.
+*/
+
+/*
+#define GMT
+#define DEBUG_DATUM_PTC
+#define LOG_TIME_ERRORS
+*/
+
+
+#define PTSPRECISION (-10) /* precision assumed 1/1024 ms */
+#define DATMREFID "DATM" /* reference id */
+#define DATUM_DISPERSION 0 /* fixed dispersion = 0 ms */
+#define DATUM_MAX_ERROR 0.100 /* limits on sigma squared */
+
+#define DATUM_MAX_ERROR2 (DATUM_MAX_ERROR*DATUM_MAX_ERROR)
+
+/*
+** External Variables
+*/
+
+extern u_long current_time; /* current time (s) - not really used */
+extern int debug; /* global debug flag - not relly used */
+
+/*
+** The Datum PTS structure
+*/
+
+/*
+** I don't use a fixed array of MAXUNITS like everyone else just because
+** I don't like to program that way. Sorry if this bothers anyone. I assume
+** that you can use any id for your unit and I will search for it in a
+** dynamic array of units until I find it. I was worried that users might
+** enter a bad id in their configuration file (larger than MAXUNITS) and
+** besides, it is just cleaner not to have to assume that you have a fixed
+** number of anything in a program.
+*/
+
+struct datum_pts_unit {
+ struct peer *peer; /* peer used by xntp */
+ struct refclockio io; /* io structure used by xntp */
+ int PTS_fd; /* file descriptor for PTS */
+ u_int unit; /* id for unit */
+ u_long timestarted; /* time started */
+ l_fp lastrec; /* time tag for the receive time (system) */
+ l_fp lastref; /* reference time (Datum time) */
+ u_long yearstart; /* the year that this clock started */
+ int coderecv; /* number of time codes received */
+ int day; /* day */
+ int hour; /* hour */
+ int minute; /* minutes */
+ int second; /* seconds */
+ int msec; /* miliseconds */
+ int usec; /* miliseconds */
+ u_char leap; /* funny leap character code */
+ char retbuf[8]; /* returned time from the datum pts */
+ char nbytes; /* number of bytes received from datum pts */
+ double sigma2; /* average squared error (roughly) */
+ int tzoff; /* time zone offest from GMT */
+};
+
+/*
+** PTS static constant variables for internal use
+*/
+
+static char TIME_REQUEST[6]; /* request message sent to datum for time */
+static FILE *logfile; /* log file for logging information */
+static int nunits; /* number of active units */
+static struct datum_pts_unit
+ **datum_pts_unit; /* dynamic array of datum PTS structures */
+
+/*
+** Callback function prototypes that xntpd needs to know about.
+*/
+
+static int datum_pts_start P((int, struct peer *));
+static void datum_pts_shutdown P((int, struct peer *));
+static void datum_pts_poll P((int, struct peer *));
+static void datum_pts_control P((int, struct refclockstat *,
+ struct refclockstat *));
+static void datum_pts_init P((void));
+static void datum_pts_buginfo P((int, struct refclockbug *));
+
+/*
+** This is the call back function structure that xntpd actually uses for
+** this refclock.
+*/
+
+struct refclock refclock_datum = {
+ datum_pts_start, /* start up a new Datum refclock */
+ datum_pts_shutdown, /* shutdown a Datum refclock */
+ datum_pts_poll, /* sends out the time request */
+ datum_pts_control, /* not used */
+ datum_pts_init, /* initialization (called first) */
+ datum_pts_buginfo, /* not used */
+ NOFLAGS /* we are not setting any special flags */
+};
+
+/*
+** The datum_pts_receive callback function is handled differently from the
+** rest. It is passed to the xntpd io data structure. Basically, every
+** 64 seconds, the datum_pts_poll() routine is called. It sends out the time
+** request message to the Datum Programmable Time System. Then, xntpd
+** waits on a select() call to receive data back. The datum_pts_receive()
+** function is called as data comes back. We expect a seven byte time
+** code to be returned but the datum_pts_receive() function may only get
+** a few bytes passed to it at a time. In other words, this routine may
+** get called by the io stuff in xntpd a few times before we get all seven
+** bytes. Once the last byte is received, we process it and then pass the
+** new time measurement to xntpd for updating the system time. For now,
+** there is no 3 state filtering done on the time measurements. The
+** jitter may be a little high but at least for its current use, it is not
+** a problem. We have tried to keep things as simple as possible. This
+** clock should not jitter more than 1 or 2 mseconds at the most once
+** things settle down. It is important to get the right drift calibrated
+** in the xntpd.drift file as well as getting the right tick set up right
+** using tickadj for SUNs. Tickadj is not used for the HP but you need to
+** remember to bring up the adjtime daemon because HP does not support
+** the adjtime() call.
+*/
+
+static void datum_pts_receive P((struct recvbuf *));
+
+/*......................................................................*/
+/* datum_pts_start - start up the datum PTS. This means open the */
+/* RS232 device and set up the data structure for my unit. */
+/*......................................................................*/
+
+static int datum_pts_start(unit, peer)
+ int unit;
+ struct peer *peer;
+{
+ struct datum_pts_unit **temp_datum_pts_unit;
+ struct datum_pts_unit *datum_pts;
+
+#ifdef HAVE_TERMIOS
+ struct termios arg;
+#endif
+
+#ifdef DEBUG_DATUM_PTC
+ fprintf(logfile, "Starting Datum PTS unit %d\n", unit);
+ fflush(logfile);
+#endif
+
+/*
+** Create the memory for the new unit
+*/
+
+ temp_datum_pts_unit = (struct datum_pts_unit **)
+ malloc((nunits+1)*sizeof(struct datum_pts_unit *));
+ if (nunits > 0) memcpy(temp_datum_pts_unit, datum_pts_unit,
+ nunits*sizeof(struct datum_pts_unit *));
+ free(datum_pts_unit);
+ datum_pts_unit = temp_datum_pts_unit;
+ datum_pts_unit[nunits] = (struct datum_pts_unit *)
+ malloc(sizeof(struct datum_pts_unit));
+ datum_pts = datum_pts_unit[nunits];
+
+ datum_pts->unit = unit; /* set my unit id */
+ datum_pts->yearstart = 0; /* initialize the yearstart to 0 */
+ datum_pts->sigma2 = 0.0; /* initialize the sigma2 to 0 */
+
+/*
+** Open the Datum PTS device
+*/
+
+ datum_pts->PTS_fd = open("/dev/datum",O_RDWR);
+
+ fcntl(datum_pts->PTS_fd, F_SETFL, 0); /* clear the descriptor flags */
+
+#ifdef DEBUG_DATUM_PTC
+ fprintf(logfile,"Opening RS232 port with file descriptor %d\n",
+ datum_pts->PTS_fd);
+ fflush(logfile);
+#endif
+
+/*
+** Set up the RS232 terminal device information. Note that we assume that
+** we have termios. This code has only been tested on SUNs and HPs. If your
+** machine does not have termios then this program exits. You can change this
+** if you want by editing this source. Please give the changes back to the
+** xntp folks so that it can become part of their regular distribution.
+*/
+
+#ifdef HAVE_TERMIOS
+
+ arg.c_iflag = IGNBRK;
+ arg.c_oflag = 0;
+ arg.c_cflag = B9600 | CS8 | CREAD | PARENB | CLOCAL;
+ arg.c_lflag = 0;
+ arg.c_cc[VMIN] = 0; /* start timeout timer right away (not used) */
+ arg.c_cc[VTIME] = 30; /* 3 second timout on reads (not used) */
+
+ tcsetattr(datum_pts->PTS_fd, TCSANOW, &arg);
+
+#else
+
+ syslog(LOG_ERR, "Datum_PTS: Exiting - Termios not supported in this driver");
+ exit(1);
+
+#endif
+
+/*
+** Initialize the xntpd IO structure
+*/
+
+ datum_pts->peer = peer;
+ datum_pts->timestarted = current_time;
+
+ datum_pts->io.clock_recv = datum_pts_receive;
+ datum_pts->io.srcclock = (caddr_t)datum_pts;
+ datum_pts->io.datalen = 0;
+ datum_pts->io.fd = datum_pts->PTS_fd;
+
+ if (!io_addclock(&(datum_pts->io))) {
+
+#ifdef DEBUG_DATUM_PTC
+ fprintf(logfile,"Problem adding clock\n");
+ fflush(logfile);
+#endif
+
+ syslog(LOG_ERR, "Datum_PTS: Problem adding clock");
+
+ }
+
+ peer->precision = PTSPRECISION;
+ peer->rootdelay = 0;
+ peer->rootdispersion = 0;
+ peer->stratum = 0;
+ memcpy((char *)&peer->refid, DATMREFID, 4);
+
+/*
+** Now add one to the number of units and return a successful code
+*/
+
+ nunits++;
+ return 1;
+
+}
+
+
+/*......................................................................*/
+/* datum_pts_shutdown - this routine shuts doen the device and */
+/* removes the memory for the unit. */
+/*......................................................................*/
+
+static void datum_pts_shutdown(unit, peer)
+ int unit;
+ struct peer *peer;
+{
+ int i,j;
+ struct datum_pts_unit **temp_datum_pts_unit;
+
+#ifdef DEBUG_DATUM_PTC
+ fprintf(logfile,"Shutdown Datum PTS\n");
+ fflush(logfile);
+#endif
+
+ syslog(LOG_ERR, "Datum_PTS: Shutdown Datum PTS");
+
+/*
+** First we have to find the right unit (i.e., the one with the same id).
+** We do this by looping through the dynamic array of units intil we find
+** it. Note, that I don't simply use an array with a maximimum number of
+** Datum PTS units. Everything is completely dynamic.
+*/
+
+ for (i=0; i<nunits; i++) {
+ if (datum_pts_unit[i]->unit == unit) {
+
+/*
+** We found the unit so close the file descriptor and free up the memory used
+** by the structure.
+*/
+
+ io_closeclock(&datum_pts_unit[i]->io);
+ close(datum_pts_unit[i]->PTS_fd);
+ free(datum_pts_unit[i]);
+
+/*
+** Now clean up the datum_pts_unit dynamic array so that there are no holes.
+** This may mean moving pointers around, etc., to keep things compact.
+*/
+
+ if (nunits > 1) {
+
+ temp_datum_pts_unit = (struct datum_pts_unit **)
+ malloc((nunits-1)*sizeof(struct datum_pts_unit *));
+ if (i!= 0) memcpy(temp_datum_pts_unit, datum_pts_unit,
+ i*sizeof(struct datum_pts_unit *));
+
+ for (j=i+1; j<nunits; j++) {
+ temp_datum_pts_unit[j-1] = datum_pts_unit[j];
+ }
+
+ free(datum_pts_unit);
+ datum_pts_unit = temp_datum_pts_unit;
+
+ }else{
+
+ free(datum_pts_unit);
+ datum_pts_unit = NULL;
+
+ }
+
+ return;
+
+ }
+ }
+
+#ifdef DEBUG_DATUM_PTC
+ fprintf(logfile,"Error, could not shut down unit %d\n",unit);
+ fflush(logfile);
+#endif
+
+ syslog(LOG_ERR, "Datum_PTS: Could not shut down Datum PTS unit %d",unit);
+
+}
+
+/*......................................................................*/
+/* datum_pts_poll - this routine sends out the time request to the */
+/* Datum PTS device. The time will be passed back in the */
+/* datum_pts_receive() routine. */
+/*......................................................................*/
+
+static void datum_pts_poll(unit, peer)
+ int unit;
+ struct peer *peer;
+{
+ int i;
+ int index;
+ int error_code;
+ struct datum_pts_unit *datum_pts;
+
+#ifdef DEBUG_DATUM_PTC
+ fprintf(logfile,"Poll Datum PTS\n");
+ fflush(logfile);
+#endif
+
+/*
+** Find the right unit and send out a time request once it is found.
+*/
+
+ index = -1;
+ for (i=0; i<nunits; i++) {
+ if (datum_pts_unit[i]->unit == unit) {
+ index = i;
+ datum_pts = datum_pts_unit[i];
+ error_code = write(datum_pts->PTS_fd, TIME_REQUEST, 6);
+ if (error_code != 6) perror("TIME_REQUEST");
+ datum_pts->nbytes = 0;
+ break;
+ }
+ }
+
+/*
+** Print out an error message if we could not find the right unit.
+*/
+
+ if (index == -1) {
+
+#ifdef DEBUG_DATUM_PTC
+ fprintf(logfile,"Error, could not poll unit %d\n",unit);
+ fflush(logfile);
+#endif
+
+ syslog(LOG_ERR, "Datum_PTS: Could not poll unit %d",unit);
+ return;
+
+ }
+
+}
+
+
+/*......................................................................*/
+/* datum_pts_control - not used */
+/*......................................................................*/
+
+static void datum_pts_control(unit, in, out)
+ int unit;
+ struct refclockstat *in;
+ struct refclockstat *out;
+{
+
+#ifdef DEBUG_DATUM_PTC
+ fprintf(logfile,"Control Datum PTS\n");
+ fflush(logfile);
+#endif
+
+}
+
+
+/*......................................................................*/
+/* datum_pts_init - initializes things for all possible Datum */
+/* time code generators that might be used. In practice, this is */
+/* only called once at the beginning before anything else is */
+/* called. */
+/*......................................................................*/
+
+static void datum_pts_init()
+{
+
+/* */
+/*...... open up the log file if we are debugging ......................*/
+/* */
+
+/*
+** Open up the log file if we are debugging. For now, send data out to the
+** screen (stdout).
+*/
+
+#if defined(DEBUG_DATUM_PTC) || defined(LOG_TIME_ERRORS)
+/*
+ logfile = fopen("xntpd.log", "w");
+*/
+#endif
+
+ logfile = stdout;
+
+#ifdef DEBUG_DATUM_PTC
+ fprintf(logfile,"Init Datum PTS\n");
+ fflush(logfile);
+#endif
+
+/*
+** Initialize the time request command string. This is the only message
+** that we ever have to send to the Datum PTS (although others are defined).
+*/
+
+ memcpy(TIME_REQUEST, "//k/mn",6);
+
+/*
+** Initialize the number of units to 0 and set the dynamic array of units to
+** NULL since there are no units defined yet.
+*/
+
+ datum_pts_unit = NULL;
+ nunits = 0;
+
+}
+
+
+/*......................................................................*/
+/* datum_pts_buginfo - not used */
+/*......................................................................*/
+
+static void datum_pts_buginfo(unit, bug)
+ int unit;
+ register struct refclockbug *bug;
+{
+
+#ifdef DEBUG_DATUM_PTC
+ fprintf(logfile,"Buginfo Datum PTS\n");
+ fflush(logfile);
+#endif
+
+}
+
+
+/*......................................................................*/
+/* datum_pts_receive - receive the time buffer that was read in */
+/* by the xntpd io handling routines. When 7 bytes have been */
+/* received (it may take several tries before all 7 bytes are */
+/* received), then the time code must be unpacked and sent to */
+/* the xntpd clock_receive() routine which causes the systems */
+/* clock to be updated (several layers down). */
+/*......................................................................*/
+
+static void datum_pts_receive(rbufp)
+ struct recvbuf *rbufp;
+{
+ int i;
+ l_fp tstmp;
+ struct datum_pts_unit *datum_pts;
+ char *dpt;
+ int dpend;
+ int tzoff;
+ int timerr;
+ double ftimerr, abserr;
+ u_fp dispersion;
+ int goodtime;
+
+/*
+** Get the time code (maybe partial) message out of the rbufp buffer.
+*/
+
+ datum_pts = (struct datum_pts_unit *)rbufp->recv_srcclock;
+ dpt = (char *)&rbufp->recv_space;
+ dpend = rbufp->recv_length;
+
+#ifdef DEBUG_DATUM_PTC
+ fprintf(logfile,"Receive Datum PTS: %d bytes\n", dpend);
+ fflush(logfile);
+#endif
+
+/* */
+/*...... save the ntp system time when the first byte is received ......*/
+/* */
+
+/*
+** Save the ntp system time when the first byte is received. Note that
+** because it may take several calls to this routine before all seven
+** bytes of our return message are finally received by the io handlers in
+** xntpd, we really do want to use the time tag when the first byte is
+** received to reduce the jitter.
+*/
+
+ if (datum_pts->nbytes == 0) {
+ datum_pts->lastrec = rbufp->recv_time;
+ }
+
+/*
+** Increment our count to the number of bytes received so far. Return if we
+** haven't gotten all seven bytes yet.
+*/
+
+ for (i=0; i<dpend; i++) {
+ datum_pts->retbuf[datum_pts->nbytes+i] = dpt[i];
+ }
+
+ datum_pts->nbytes += dpend;
+
+ if (datum_pts->nbytes != 7) {
+ return;
+ }
+
+/*
+** Convert the seven bytes received in our time buffer to day, hour, minute,
+** second, and msecond values. The usec value is not used for anything
+** currently. It is just the fractional part of the time stored in units
+** of microseconds.
+*/
+
+ datum_pts->day = 100*(datum_pts->retbuf[0] & 0x0f) +
+ 10*((datum_pts->retbuf[1] & 0xf0)>>4) +
+ (datum_pts->retbuf[1] & 0x0f);
+
+ datum_pts->hour = 10*((datum_pts->retbuf[2] & 0x30)>>4) +
+ (datum_pts->retbuf[2] & 0x0f);
+
+ datum_pts->minute = 10*((datum_pts->retbuf[3] & 0x70)>>4) +
+ (datum_pts->retbuf[3] & 0x0f);
+
+ datum_pts->second = 10*((datum_pts->retbuf[4] & 0x70)>>4) +
+ (datum_pts->retbuf[4] & 0x0f);
+
+ datum_pts->msec = 100*((datum_pts->retbuf[5] & 0xf0) >> 4) +
+ 10*(datum_pts->retbuf[5] & 0x0f) +
+ ((datum_pts->retbuf[6] & 0xf0)>>4);
+
+ datum_pts->usec = 1000*datum_pts->msec;
+
+#ifdef DEBUG_DATUM_PTC
+ fprintf(logfile,"day %d, hour %d, minute %d, second %d, msec %d\n",
+ datum_pts->day,
+ datum_pts->hour,
+ datum_pts->minute,
+ datum_pts->second,
+ datum_pts->msec);
+ fflush(logfile);
+#endif
+
+/*
+** Get the GMT time zone offset. Note that GMT should be zero if the Datum
+** reference time is using GMT as its time base. Otherwise we have to
+** determine the offset if the Datum PTS is using time of day as its time
+** base.
+*/
+
+ goodtime = 0; /* We are not sure about the time and offset yet */
+
+#ifdef GMT
+
+/*
+** This is the case where the Datum PTS is using GMT so there is no time
+** zone offset.
+*/
+
+ tzoff = 0; /* set time zone offset to 0 */
+
+#else
+
+/*
+** This is the case where the Datum PTS is using regular time of day for its
+** time so we must compute the time zone offset. The way we do it is kind of
+** funny but it works. We loop through different time zones (0 to 24) and
+** pick the one that gives the smallest error (+- one half hour). The time
+** zone offset is stored in the datum_pts structure for future use. Normally,
+** the clocktime() routine is only called once (unless the time zone offset
+** changes due to daylight savings) since the goodtime flag is set when a
+** good time is found (with a good offset). Note that even if the Datum
+** PTS is using GMT, this mechanism will still work since it should come up
+** with a value for tzoff = 0 (assuming that your system clock is within
+** a half hour of the Datum time (even with time zone differences).
+*/
+
+ for (tzoff=0; tzoff<24; tzoff++) {
+ if (clocktime( datum_pts->day,
+ datum_pts->hour,
+ datum_pts->minute,
+ datum_pts->second,
+ (tzoff + datum_pts->tzoff) % 24,
+ datum_pts->lastrec.l_ui,
+ &datum_pts->yearstart,
+ &datum_pts->lastref.l_ui) ) {
+
+ error = datum_pts->lastref.l_ui - datum_pts->lastrec.l_ui;
+
+#ifdef DEBUG_DATUM_PTC
+ printf("Time Zone (clocktime method) = %d, error = %d\n", tzoff, error);
+#endif
+
+ if ((error < 1799) && (error > -1799)) {
+ tzoff = (tzoff + datum_pts->tzoff) % 24;
+ datum_pts->tzoff = tzoff;
+ goodtime = 1;
+
+#ifdef DEBUG_DATUM_PTC
+ printf("Time Zone found (clocktime method) = %d\n",tzoff);
+#endif
+
+ break;
+ }
+
+ }
+ }
+
+#endif
+
+/*
+** Make sure that we have a good time from the Datum PTS. Clocktime() also
+** sets yearstart and lastref.l_ui. We will have to set astref.l_uf (i.e.,
+** the fraction of a second) stuff later.
+*/
+
+ if (!goodtime) {
+
+ if (!clocktime( datum_pts->day,
+ datum_pts->hour,
+ datum_pts->minute,
+ datum_pts->second,
+ tzoff,
+ datum_pts->lastrec.l_ui,
+ &datum_pts->yearstart,
+ &datum_pts->lastref.l_ui) ) {
+
+#ifdef DEBUG_DATUM_PTC
+ fprintf(logfile,"Error: bad clocktime\n");
+ fprintf(logfile,"GMT %d, lastrec %d, yearstart %d, lastref %d\n",
+ tzoff,
+ datum_pts->lastrec.l_ui,
+ datum_pts->yearstart,
+ datum_pts->lastref.l_ui);
+ fflush(logfile);
+#endif
+
+ syslog(LOG_ERR, "Datum_PTS: Bad clocktime");
+
+ return;
+
+ }else{
+
+#ifdef DEBUG_DATUM_PTC
+ fprintf(logfile,"Good clocktime\n");
+ fflush(logfile);
+#endif
+
+ }
+
+ }
+
+/*
+** We have datum_pts->lastref.l_ui set (which is the integer part of the
+** time. Now set the microseconds field.
+*/
+
+ TVUTOTSF(datum_pts->usec, datum_pts->lastref.l_uf);
+
+/*
+** Compute the time correction as the difference between the reference
+** time (i.e., the Datum time) minus the receive time (system time).
+*/
+
+ tstmp = datum_pts->lastref; /* tstmp is the datum ntp time */
+ L_SUB(&tstmp, &datum_pts->lastrec); /* tstmp is now the correction */
+ datum_pts->coderecv++; /* increment a counter */
+
+ dispersion = DATUM_DISPERSION; /* set the dispersion to 0 */
+
+#ifdef DEBUG_DATUM_PTC
+ ftimerr = dispersion;
+ ftimerr /= (1024.0 * 64.0);
+ fprintf(logfile,"dispersion = %d, %f\n", dispersion, ftimerr);
+ fflush(logfile);
+#endif
+
+/*
+** Pass the new time to xntpd through the refclock_receive function. Note
+** that we are not trying to make any corrections due to the time it takes
+** for the Datum PTS to send the message back. I am (erroneously) assuming
+** that the time for the Datum PTS to send the time back to us is negligable.
+** I suspect that this time delay may be as much as 15 ms or so (but probably
+** less). For our needs at JPL, this kind of error is ok so it is not
+** necessary to use fudge factors in the ntp.conf file. Maybe later we will.
+*/
+
+ refclock_receive( datum_pts->peer,
+ &tstmp,
+ tzoff,
+ dispersion,
+ &datum_pts->lastrec,
+ &datum_pts->lastrec,
+ datum_pts->leap );
+
+/*
+** Compute sigma squared (not used currently). Maybe later, this could be
+** used for the dispersion estimate. The problem is that xntpd does not link
+** in the math library so sqrt() is not available. Anyway, this is useful
+** for debugging. Maybe later I will just use absolute values for the time
+** error to come up with my dispersion estimate. Anyway, for now my dispersion
+** is set to 0.
+*/
+
+ timerr = tstmp.l_ui<<20;
+ timerr |= (tstmp.l_uf>>12) & 0x000fffff;
+ ftimerr = timerr;
+ ftimerr /= 1024*1024;
+ abserr = ftimerr;
+ if (ftimerr < 0.0) abserr = -ftimerr;
+
+ if (datum_pts->sigma2 == 0.0) {
+ if (abserr < DATUM_MAX_ERROR) {
+ datum_pts->sigma2 = abserr*abserr;
+ }else{
+ datum_pts->sigma2 = DATUM_MAX_ERROR2;
+ }
+ }else{
+ if (abserr < DATUM_MAX_ERROR) {
+ datum_pts->sigma2 = 0.95*datum_pts->sigma2 + 0.05*abserr*abserr;
+ }else{
+ datum_pts->sigma2 = 0.95*datum_pts->sigma2 + 0.05*DATUM_MAX_ERROR2;
+ }
+ }
+
+#ifdef DEBUG_DATUM_PTC
+ fprintf(logfile,"Time error = %f seconds\n", ftimerr);
+ fflush(logfile);
+#endif
+
+#if defined(DEBUG_DATUM_PTC) || defined(LOG_TIME_ERRORS)
+ fprintf(logfile,
+ "PTS: day %d, hour %d, minute %d, second %d, msec %d, Time Error %f\n",
+ datum_pts->day,
+ datum_pts->hour,
+ datum_pts->minute,
+ datum_pts->second,
+ datum_pts->msec,
+ ftimerr);
+ fflush(logfile);
+#endif
+
+}
+#endif
diff --git a/usr.sbin/xntpd/xntpd/refclock_goes.c b/usr.sbin/xntpd/xntpd/refclock_goes.c
new file mode 100644
index 0000000..512131b
--- /dev/null
+++ b/usr.sbin/xntpd/xntpd/refclock_goes.c
@@ -0,0 +1,533 @@
+/*
+ * refclock_goes - clock driver for the Kinemetrics Truetime GOES
+ * Receiver Version 3.0C - tested plain, with CLKLDISC
+ * Developement work being done:
+ * - Properly handle varying satellite positions (more acurately)
+ * - Integrate GPSTM and/or OMEGA and/or TRAK and/or ??? drivers
+ */
+
+#if defined(REFCLOCK) && defined(GOES)
+
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/time.h>
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_unixtime.h"
+#include "ntp_stdlib.h"
+
+/*
+ * Support for Kinemetrics Truetime 468-DC GOES Receiver
+ * OM-DC OMEGA and GPS-TM/TMD support in progress...
+ *
+ * Most of this code is originally from refclock_wwvb.c with thanks.
+ * It has been so mangled that wwvb is not a recognizable ancestor.
+ *
+ * Timcode format: ADDD:HH:MM:SSQCL
+ * A - control A
+ * Q Quality indication: indicates possible error of
+ * C - Carriage return
+ * L - Line feed
+ *
+ * Quality codes indicate possible error of
+ * 468-DC GOES Receiver:
+ * GPS-TM/TMD Receiver:
+ * ? +/- 500 milliseconds # +/- 50 milliseconds
+ * * +/- 5 milliseconds . +/- 1 millisecond
+ * space less than 1 millisecond
+ * OM-DC OMEGA Receiver:
+ * > >+- 5 seconds
+ * ? >+/- 500 milliseconds # >+/- 50 milliseconds
+ * * >+/- 5 milliseconds . >+/- 1 millisecond
+ * A-H less than 1 millisecond. Character indicates which station
+ * is being received as follows:
+ * A = Norway, B = Liberia, C = Hawaii, D = North Dakota,
+ * E = La Reunion, F = Argentina, G = Australia, H = Japan.
+ *
+ * The carriage return start bit begins on 0 seconds and extends to 1
+ * bit time
+ *
+ * Notes on 468-DC and OMEGA receiver:
+ *
+ * Send the clock a 'R' or 'C' and once per second a timestamp will
+ * appear. Send a 'P' to get the satellite position once.
+ *
+ * Notes on the 468-DC receiver:
+ *
+ * Unless you live on 125 degrees west longitude, you can't
+ * set your clock propagation delay settings correctly and still use
+ * automatic mode. The manual says to use a compromise when setting the
+ * switches. This results in significant errors. The solution; use fudge
+ * time1 and time2 to incorporate corrections. If your clock is set for
+ * 50 and it should be 58 for using the west and 46 for using the east,
+ * use the line
+ *
+ * fudge 127.127.5.0 time1 +0.008 time2 -0.004
+ *
+ * This corrects the 4 milliseconds advance and 8 milliseconds retard
+ * needed. The software will ask the clock which satellite it sees.
+ *
+ * Ntp.conf parameters:
+ * time1 - offset applied to samples when reading WEST satellite (default = 0)
+ * time2 - offset applied to samples when reading EAST satellite (default = 0)
+ * val1 - stratum to assign to this clock (default = 0)
+ * val2 - refid assigned to this clock (default = "GOES", see below)
+ * flag1 - will silence the clock side of xntpd, just reading the clock
+ * without trying to write to it. (default = 0)
+ * flag2 - not assigned
+ * flag3 - enable ppsclock streams module
+ * flag4 - not assigned
+ *
+ */
+
+/*
+ * Definitions
+ */
+#define DEVICE "/dev/goes%d"
+#define SPEED232 B9600 /* 9600 baud */
+
+/*
+ * Radio interface parameters
+ */
+#define MAXDISPERSE (FP_SECOND>>1) /* max error for synchronized clock (0.5 s as an u_fp) */
+#define PRECISION (-10) /* precision assumed (about 1 ms) */
+#define REFID "GOES" /* reference id */
+#define DESCRIPTION "TrueTime GPS/GOES Receivers" /* WRU */
+#define NSAMPLES 3 /* stages of median filter */
+
+/*
+ * Tags which station (satellite) we see
+ */
+#define GOES_WEST 0 /* Default to WEST satellite and apply time1 */
+#define GOES_EAST 1 /* until you discover otherwise */
+
+/*
+ * used by the state machine
+ */
+enum goes_event {e_Init, e_F18, e_F50, e_F51, e_TS};
+
+/*
+ * Imported from the timer module
+ */
+extern u_long current_time;
+
+/*
+ * Imported from ntpd module
+ */
+extern int debug; /* global debug flag */
+
+/*
+ * unit control structure
+ */
+struct goesunit {
+ int pollcnt; /* poll message counter */
+ u_short station; /* which station we are on */
+ u_short polled; /* Hand in a time sample? */
+ enum {Base, Start, F18, F50, F51, F08}
+ State; /* State machine */
+};
+
+/*
+ * Function prototypes
+ */
+static int goes_start P((int, struct peer *));
+static void goes_shutdown P((int, struct peer *));
+static void goes_receive P((struct recvbuf *));
+static void goes_poll P((int, struct peer *));
+static void goes_send P((struct peer *, char *));
+static void goes_initstate P((struct peer *));
+static void goes_doevent P((struct peer *, enum goes_event));
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_goes = {
+ goes_start, /* start up driver */
+ goes_shutdown, /* shut down driver */
+ goes_poll, /* transmit poll message */
+ noentry, /* not used (old goes_control) */
+ noentry, /* initialize driver (not used) */
+ noentry, /* not used (old goes_buginfo) */
+ NOFLAGS /* not used */
+};
+
+
+/*
+ * goes_start - open the devices and initialize data for processing
+ */
+static int
+goes_start(unit, peer)
+ int unit;
+ struct peer *peer;
+{
+ register struct goesunit *up;
+ struct refclockproc *pp;
+ int fd;
+ char device[20];
+
+ /*
+ * Open serial port
+ */
+ (void)sprintf(device, DEVICE, unit);
+ if (!(fd = refclock_open(device, SPEED232, LDISC_CLK)))
+ return (0);
+
+ /*
+ * Allocate and initialize unit structure
+ */
+ if (!(up = (struct goesunit *)
+ emalloc(sizeof(struct goesunit)))) {
+ (void) close(fd);
+ return (0);
+ }
+ memset((char *)up, 0, sizeof(struct goesunit));
+ pp = peer->procptr;
+ pp->io.clock_recv = goes_receive;
+ pp->io.srcclock = (caddr_t)peer;
+ pp->io.datalen = 0;
+ pp->io.fd = fd;
+ if (!io_addclock(&pp->io)) {
+ (void) close(fd);
+ free(up);
+ return (0);
+ }
+ pp->unitptr = (caddr_t)up;
+
+ /*
+ * Initialize miscellaneous variables
+ */
+ peer->precision = PRECISION;
+ pp->clockdesc = DESCRIPTION;
+ memcpy((char *)&pp->refid, REFID, 4);
+ up->pollcnt = 2;
+/* goes_initstate(peer);*/
+ return (1);
+}
+
+
+/*
+ * goes_shutdown - shut down the clock
+ */
+static void
+goes_shutdown(unit, peer)
+ int unit;
+ struct peer *peer;
+{
+ register struct goesunit *up;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ up = (struct goesunit *)pp->unitptr;
+ io_closeclock(&pp->io);
+ free(up);
+}
+
+
+/*
+ * goes_receive - receive data from the serial interface on a
+ * Kinimetrics clock
+ */
+static void
+goes_receive(rbufp)
+ struct recvbuf *rbufp;
+{
+ register struct goesunit *up;
+ struct refclockproc *pp;
+ struct peer *peer;
+ l_fp tmp_l_fp;
+ u_short new_station;
+ char sync, c1, c2;
+ int i;
+ int lat, lon, off; /* GOES Satellite position */
+
+ /*
+ * Get the clock this applies to and pointers to the data
+ */
+ peer = (struct peer *)rbufp->recv_srcclock;
+ pp = peer->procptr;
+ up = (struct goesunit *)pp->unitptr;
+
+ /*
+ * Read clock output. Automatically handles STREAMS, CLKLDISC
+ */
+ pp->lencode = refclock_gtlin(rbufp, pp->lastcode, BMAX,
+ &pp->lastrec);
+
+ /*
+ * There is a case where <cr><lf> generates 2 timestamps
+ */
+ if (pp->lencode == 0)
+ return;
+
+ up->pollcnt = 2;
+ record_clock_stats(&peer->srcadr, pp->lastcode);
+
+ /*
+ * We get down to business, check the timecode format and decode
+ * its contents. This code decodes a multitude of different
+ * clock messages. Timecodes are processed if needed. All replies
+ * will be run through the state machine to tweak driver options
+ * and program the clock.
+ */
+
+ /*
+ * Timecode: "nnnnn+nnn-nnn"
+ */
+ if (sscanf(pp->lastcode, "%5d%c%3d%c%3d",
+ &lon, &c1, &lat, &c2, &off) == 5 &&
+ (c1 == '+' || c1 == '-') &&
+ (c2 == '+' || c2 == '-')) {
+
+ /*
+ * This is less than perfect. Call the (satellite)
+ * either EAST or WEST and adjust slop accodingly
+ * Perfectionists would recalcuted the exact delay
+ * and adjust accordingly...
+ */
+ if (lon > 7000 && lon < 14000) {
+ if (lon < 10000)
+ new_station = GOES_EAST;
+ else
+ new_station = GOES_WEST;
+#ifdef DEBUG
+ if (debug) {
+ if (new_station == GOES_EAST)
+ printf("goes: station EAST\n");
+ if (new_station == GOES_WEST)
+ printf("goes: station WEST\n");
+ }
+#endif
+ if (new_station != up->station) {
+ tmp_l_fp = pp->fudgetime1;
+ pp->fudgetime1 = pp->fudgetime2;
+ pp->fudgetime2 = tmp_l_fp;
+ up->station = new_station;
+ }
+ }
+ else {
+ refclock_report(peer, CEVNT_BADREPLY);
+#ifdef DEBUG
+ if (debug)
+ printf("goes: station UNKNONW\n");
+#endif
+ }
+ /*
+ * Switch back to on-second time codes and return.
+ */
+ goes_send(peer, "C");
+
+ return;
+ }
+
+ /*
+ * Timecode: "Fnn"
+ */
+ if (sscanf(pp->lastcode, "F%2d", &i) == 1 &&
+ i > 0 && i < 80) {
+ enum goes_event event = 0;
+
+ if (i == 50) event = e_F50;
+ if (i == 51) event = e_F51;
+ if (i == 50 || i == 51) {
+ goes_doevent(peer, event);
+ return;
+ }
+ }
+
+ /*
+ * Timecode:" TRUETIME Mk III"
+ */
+ if (strcmp(pp->lastcode, " TRUETIME Mk III") == 0) {
+ enum goes_event event;
+
+ event = e_F18;
+ goes_doevent(peer, event);
+ return;
+ }
+
+ /*
+ * Timecode: "ddd:hh:mm:ssQ"
+ */
+ if (sscanf(pp->lastcode, "%3d:%2d:%2d:%2d%c",
+ &pp->day, &pp->hour, &pp->minute,
+ &pp->second, &sync) == 5) {
+
+ /*
+ * Adjust the synchronize indicator according to timecode
+ */
+ if (sync !=' ' && sync !='.' && sync !='*')
+ pp->leap = LEAP_NOTINSYNC;
+ else {
+ pp->leap = 0;
+ pp->lasttime = current_time;
+ }
+
+ /* goes_doevent(peer, e_TS); */
+
+ /*
+ * The clock will blurt a timecode every second but we only
+ * want one when polled. If we havn't been polled, bail out.
+ */
+ if (!up->polled)
+ return;
+
+ /*
+ * After each poll, check the station (satellite)
+ */
+ goes_send(peer, "P");
+
+ /*
+ * Process the new sample in the median filter and determine the
+ * reference clock offset and dispersion. We use lastrec as both
+ * the reference time and receive time in order to avoid being
+ * cute, like setting the reference time later than the receive
+ * time, which may cause a paranoid protocol module to chuck out
+ * the data.
+ */
+ if (!refclock_process(pp, NSAMPLES, NSAMPLES)) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ refclock_receive(peer, &pp->offset, 0, pp->dispersion,
+ &pp->lastrec, &pp->lastrec, pp->leap);
+
+ /*
+ * We have succedded in answering the poll.
+ * Turn off the flag and return
+ */
+ up->polled = 0;
+
+ return;
+ }
+
+ /*
+ * No match to known timecodes, report failure and return
+ */
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+}
+
+
+/*
+ * goes_send - time to send the clock a signal to cough up a time sample
+ */
+static void
+goes_send(peer, cmd)
+ struct peer *peer;
+ char *cmd;
+{
+ struct refclockproc *pp;
+ register int len = strlen(cmd);
+
+ pp = peer->procptr;
+ if (!pp->sloppyclockflag & CLK_FLAG1) {
+#ifdef DEBUG
+ if (debug)
+ printf("goes: Send '%s'\n", cmd);
+#endif
+ if (write(pp->io.fd, cmd, len) != len) {
+ refclock_report(peer, CEVNT_FAULT);
+ } else {
+ pp->polls++;
+ }
+ }
+}
+
+
+/*
+ * state machine for initializing the clock
+ */
+static void
+goes_doevent(peer, event)
+ struct peer *peer;
+ enum goes_event event;
+{
+ struct goesunit *up;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ up = (struct goesunit *)pp->unitptr;
+
+#ifdef DEBUG
+ if (debug) {
+ printf("goes_doevent: %d\n", (int)event);
+ }
+#endif
+ if (event == e_TS && up->State != F51 && up->State != F08) {
+ goes_send(peer, "\03\r");
+ }
+
+ switch (event) {
+ case e_Init:
+ goes_send(peer, "F18\r");
+ up->State = Start;
+ break;
+ case e_F18:
+ goes_send(peer, "F50\r");
+ up->State = F18;
+ break;
+ case e_F50:
+ goes_send(peer, "F51\r");
+ up->State = F50;
+ break;
+ case e_F51:
+ goes_send(peer, "F08\r");
+ up->State = F51;
+ break;
+ case e_TS:
+ /* nothing to send - we like this mode */
+ up->State = F08;
+ break;
+ }
+}
+
+static void
+goes_initstate(peer)
+ struct peer *peer;
+{
+ struct goesunit *up;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ up = (struct goesunit *)pp->unitptr;
+ up->State = Base; /* just in case */
+ goes_doevent(peer, e_Init);
+}
+
+
+/*
+ * goes_poll - called by the transmit procedure
+ */
+static void
+goes_poll(unit, peer)
+ int unit;
+ struct peer *peer;
+{
+ struct goesunit *up;
+ struct refclockproc *pp;
+
+ /*
+ * You don't need to poll this clock. It puts out timecodes
+ * once per second. If asked for a timestamp, take note.
+ * The next time a timecode comes in, it will be fed back.
+ */
+ pp = peer->procptr;
+ up = (struct goesunit *)pp->unitptr;
+ if (up->pollcnt == 0) {
+ refclock_report(peer, CEVNT_TIMEOUT);
+ goes_send(peer, "C");
+ }
+ else
+ up->pollcnt--;
+
+ /*
+ * polled every 64 seconds. Ask goes_receive to hand in a
+ * timestamp.
+ */
+ up->polled = 1;
+ pp->polls++;
+}
+
+#endif
diff --git a/usr.sbin/xntpd/xntpd/refclock_gpstm.c b/usr.sbin/xntpd/xntpd/refclock_gpstm.c
new file mode 100644
index 0000000..9dfc87c
--- /dev/null
+++ b/usr.sbin/xntpd/xntpd/refclock_gpstm.c
@@ -0,0 +1,999 @@
+/*
+ * refclock_gpstm - clock driver for the Kinimetrics Truetime GPSTM/TMD rcvr
+ * Version 1.0 (from Version 2.0 of the GOES driver, as of 03Jan94)
+ */
+
+#if defined(REFCLOCK) && (defined(GPSTM) || defined(GPSTMCLK) \
+ || defined(GPSTMPPS))
+
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/time.h>
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_unixtime.h"
+
+#ifdef SYS_BSDI
+#undef HAVE_BSD_TTYS
+#include <sys/ioctl.h>
+#endif
+
+#if defined(HAVE_BSD_TTYS)
+#include <sgtty.h>
+#endif /* HAVE_BSD_TTYS */
+
+#if defined(HAVE_SYSV_TTYS)
+#include <termio.h>
+#endif /* HAVE_SYSV_TTYS */
+
+#if defined(HAVE_TERMIOS)
+#include <termios.h>
+#endif
+
+#if defined(STREAM)
+#include <stropts.h>
+#if defined(GPSTMCLK)
+#include <clkdefs.h>
+#endif /* GPSTMCLK */
+#endif /* STREAM */
+
+#if defined(GPSTMPPS)
+#include <sys/ppsclock.h>
+#endif /* GPSTMPPS */
+
+#include "ntp_stdlib.h"
+
+/*
+ * Support for Kinemetrics Truetime GPS-TM/TMD Receiver
+ *
+ * Most of this code is copied from refclock_goes.c with thanks.
+ *
+ * the time code looks like follows:
+ *
+ * ADDD:HH:MM:SSQCL
+ * A - control A
+ * Q Quality indication: indicates possible error of
+ * ? +/- 500 milliseconds # +/- 50 milliseconds
+ * * +/- 5 milliseconds . +/- 1 millisecond
+ * space less than 1 millisecond
+ * C - Carriage return
+ * L - Line feed
+ * The carriage return start bit begins on 0 seconds and extends to 1 bit time.
+ *
+ * Flag1 set to 1 will silence the clock side of xntpd, just reading the
+ * clock without trying to write to it. This is usefull if several
+ * xntpds listen to the same clock. This has not been tested yet...
+ */
+
+/*
+ * Definitions
+ */
+#define MAXUNITS 4 /* max number of GPSTM units */
+#define GPSTM232 "/dev/gpstm%d"
+#define SPEED232 B9600 /* 9600 baud */
+
+/*
+ * Radio interface parameters
+ */
+#define MAXDISPERSE (FP_SECOND>>1) /* max error for synchronized clock (0.5 s as an u_fp) */
+#define PRECISION (-10) /* precision assumed (about 1 ms) */
+#define REFID "GPS\0" /* reference id */
+#define DESCRIPTION "Kinemetrics GPS-TM/TMD Receiver" /* who we are */
+#define GMT 0 /* hour offset from Greenwich */
+#define NCODES 3 /* stages of median filter */
+#define CODEDIFF 0x20000000 /* 0.125 seconds as an l_fp fraction */
+#define TIMEOUT 180 /* ping the clock if it's silent this long */
+
+/*
+ * used by the state machine
+ */
+enum gpstm_event {e_Init, e_F18, e_F50, e_F51, e_TS};
+static enum {Base, Start, F18, F50, F51, F08} State[MAXUNITS];
+static void gpstm_doevent P((int, enum gpstm_event));
+static void gpstm_initstate P((int));
+
+/*
+ * Hack to avoid excercising the multiplier. I have no pride.
+ */
+#define MULBY10(x) (((x)<<3) + ((x)<<1))
+
+/*
+ * Imported from the timer module
+ */
+extern U_LONG current_time;
+extern struct event timerqueue[];
+
+/*
+ * Imported from ntp_loopfilter module
+ */
+extern int fdpps; /* pps file descriptor */
+
+/*
+ * Imported from ntpd module
+ */
+extern int debug; /* global debug flag */
+
+/*
+ * GPSTM unit control structure
+ */
+struct gpstm_unit {
+ struct peer *peer; /* associated peer structure */
+ struct refclockio io; /* given to the I/O handler */
+ l_fp lastrec; /* last receive time */
+ l_fp lastref; /* last timecode time */
+ l_fp offset[NCODES]; /* recent sample offsets */
+ char lastcode[BMAX]; /* last timecode received */
+ u_short polled; /* Hand in a time sample? */
+ u_char lencode; /* length of last timecode */
+ U_LONG lasttime; /* last time clock heard from */
+ u_char unit; /* unit number for this guy */
+ u_char status; /* clock status */
+ u_char lastevent; /* last clock event */
+ u_char reason; /* reason for last abort */
+ u_char year; /* year of eternity */
+ u_short day; /* day of year */
+ u_char hour; /* hour of day */
+ u_char minute; /* minute of hour */
+ u_char second; /* seconds of minute */
+ u_char leap; /* leap indicators */
+ u_short msec; /* millisecond of second */
+ u_char quality; /* quality character */
+ u_long yearstart; /* start of current year */
+ /*
+ * Status tallies
+ */
+ U_LONG polls; /* polls sent */
+ U_LONG noreply; /* no replies to polls */
+ U_LONG coderecv; /* timecodes received */
+ U_LONG badformat; /* bad format */
+ U_LONG baddata; /* bad data */
+ U_LONG timestarted; /* time we started this */
+};
+
+/*
+ * Data space for the unit structures. Note that we allocate these on
+ * the fly, but never give them back.
+ */
+static struct gpstm_unit *gpstm_units[MAXUNITS];
+static u_char unitinuse[MAXUNITS];
+
+/*
+ * Keep the fudge factors separately so they can be set even
+ * when no clock is configured.
+ */
+static l_fp fudgefactor1[MAXUNITS];
+static l_fp fudgefactor2[MAXUNITS];
+static u_char stratumtouse[MAXUNITS];
+static u_char readonlyclockflag[MAXUNITS];
+static U_LONG refid[MAXUNITS];
+
+/*
+ * Function prototypes
+ */
+static void gpstm_init P((void));
+static int gpstm_start P((int, struct peer *));
+static void gpstm_shutdown P((int, struct peer *));
+static void gpstm_rep_event P((struct gpstm_unit *, int));
+static void gpstm_receive P((struct recvbuf *));
+static char gpstm_process P((struct gpstm_unit *, l_fp *, u_fp *));
+static void gpstm_poll P((int, struct peer *));
+static void gpstm_control P((int, struct refclockstat *,
+ struct refclockstat *));
+static void gpstm_buginfo P((int, struct refclockbug *));
+static void gpstm_send P((struct gpstm_unit *, char *));
+
+struct refclock refclock_gpstm = {
+ gpstm_start, gpstm_shutdown, gpstm_poll,
+ gpstm_control, gpstm_init, gpstm_buginfo, NOFLAGS
+};
+
+/*
+ * gpstm_init - initialize internal driver data
+ */
+static void
+gpstm_init()
+{
+ register int i;
+ /*
+ * Just zero the data arrays
+ */
+ memset((char *)gpstm_units, 0, sizeof gpstm_units);
+ memset((char *)unitinuse, 0, sizeof unitinuse);
+
+ /*
+ * Initialize fudge factors to default.
+ */
+ for (i = 0; i < MAXUNITS; i++) {
+ fudgefactor1[i].l_ui = 0;
+ fudgefactor1[i].l_uf = 0;
+ fudgefactor2[i].l_ui = 0;
+ fudgefactor2[i].l_uf = 0;
+ stratumtouse[i] = 0;
+ readonlyclockflag[i] = 0;
+ memcpy((char *)&refid[i], REFID, 4);
+ }
+}
+
+
+/*
+ * gpstm_start - open the device and initialize data for processing
+ */
+static int
+gpstm_start(unit, peer)
+ int unit;
+ struct peer *peer;
+{
+ register struct gpstm_unit *gpstm;
+ register int i;
+ int fd232;
+ char dev[20];
+
+ /*
+ * Check configuration info
+ */
+ if (unit >= MAXUNITS) {
+ syslog(LOG_ERR, "gpstm_start: unit %d invalid", unit);
+ return 0;
+ }
+ if (unitinuse[unit]) {
+ syslog(LOG_ERR, "gpstm_start: unit %d in use", unit);
+ return 0;
+ }
+
+ /*
+ * Open serial port
+ */
+ (void) sprintf(dev, GPSTM232, unit);
+ fd232 = open(dev, O_RDWR, 0777);
+ if (fd232 == -1) {
+ syslog(LOG_ERR, "gpstm_start: open of %s: %m", dev);
+ return 0;
+ }
+
+#if defined(HAVE_SYSV_TTYS)
+ /*
+ * System V serial line parameters (termio interface)
+ *
+ */
+ { struct termio ttyb;
+ if (ioctl(fd232, TCGETA, &ttyb) < 0) {
+ syslog(LOG_ERR,
+ "gpstm_start: ioctl(%s, TCGETA): %m", dev);
+ goto screwed;
+ }
+ ttyb.c_iflag = IGNBRK|IGNPAR|ICRNL;
+ ttyb.c_oflag = 0;
+ ttyb.c_cflag = SPEED232|CS8|CLOCAL|CREAD;
+ ttyb.c_lflag = ICANON;
+ ttyb.c_cc[VERASE] = ttyb.c_cc[VKILL] = '\0';
+ if (ioctl(fd232, TCSETA, &ttyb) < 0) {
+ syslog(LOG_ERR,
+ "gpstm_start: ioctl(%s, TCSETA): %m", dev);
+ goto screwed;
+ }
+ }
+#endif /* HAVE_SYSV_TTYS */
+#if defined(HAVE_TERMIOS)
+ /*
+ * POSIX serial line parameters (termios interface)
+ *
+ * The GPSTMCLK option provides timestamping at the driver level.
+ * It requires the tty_clk streams module.
+ *
+ * The GPSTMPPS option provides timestamping at the driver level.
+ * It uses a 1-pps signal and level converter (gadget box) and
+ * requires the ppsclock streams module and SunOS 4.1.1 or
+ * later.
+ */
+ { struct termios ttyb, *ttyp;
+ ttyp = &ttyb;
+
+ if (tcgetattr(fd232, ttyp) < 0) {
+ syslog(LOG_ERR,
+ "gpstm_start: tcgetattr(%s): %m", dev);
+ goto screwed;
+ }
+ ttyp->c_iflag = IGNBRK|IGNPAR|ICRNL;
+ ttyp->c_oflag = 0;
+ ttyp->c_cflag = SPEED232|CS8|CLOCAL|CREAD;
+ ttyp->c_lflag = ICANON;
+ ttyp->c_cc[VERASE] = ttyp->c_cc[VKILL] = '\0';
+ if (tcsetattr(fd232, TCSANOW, ttyp) < 0) {
+ syslog(LOG_ERR,
+ "gpstm_start: tcsetattr(%s): %m", dev);
+ goto screwed;
+ }
+ if (tcflush(fd232, TCIOFLUSH) < 0) {
+ syslog(LOG_ERR,
+ "gpstm_start: tcflush(%s): %m", dev);
+ goto screwed;
+ }
+#if defined(STREAM)
+#if defined(GPSTMCLK)
+ if (ioctl(fd232, I_PUSH, "clk") < 0)
+ syslog(LOG_ERR,
+ "gpstm_start: ioctl(%s, I_PUSH, clk): %m", dev);
+ if (ioctl(fd232, CLK_SETSTR, "\n") < 0)
+ syslog(LOG_ERR,
+ "gpstm_start: ioctl(%s, CLK_SETSTR): %m", dev);
+#endif /* GPSTMCLK */
+#if defined(GPSTMPPS)
+ if (ioctl(fd232, I_PUSH, "ppsclock") < 0)
+ syslog(LOG_ERR,
+ "gpstm_start: ioctl(%s, I_PUSH, ppsclock): %m", dev);
+ else
+ fdpps = fd232;
+#endif /* GPSTMPPS */
+#endif /* STREAM */
+ }
+#endif /* HAVE_TERMIOS */
+#if defined(HAVE_BSD_TTYS)
+ /*
+ * 4.3bsd serial line parameters (sgttyb interface)
+ *
+ * The GPSTMCLK option provides timestamping at the driver level.
+ * It requires the tty_clk line discipline and 4.3bsd or later.
+ */
+ { struct sgttyb ttyb;
+#if defined(GPSTMCLK)
+ int ldisc = CLKLDISC;
+#endif /* GPSTMCLK */
+
+ if (ioctl(fd232, TIOCGETP, &ttyb) < 0) {
+ syslog(LOG_ERR,
+ "gpstm_start: ioctl(%s, TIOCGETP): %m", dev);
+ goto screwed;
+ }
+ ttyb.sg_ispeed = ttyb.sg_ospeed = SPEED232;
+#if defined(GPSTMCLK)
+ ttyb.sg_erase = ttyb.sg_kill = '\r';
+ ttyb.sg_flags = RAW;
+#else
+ ttyb.sg_erase = ttyb.sg_kill = '\0';
+ ttyb.sg_flags = EVENP|ODDP|CRMOD;
+#endif /* GPSTMCLK */
+ if (ioctl(fd232, TIOCSETP, &ttyb) < 0) {
+ syslog(LOG_ERR,
+ "gpstm_start: ioctl(%s, TIOCSETP): %m", dev);
+ goto screwed;
+ }
+#if defined(GPSTMCLK)
+ if (ioctl(fd232, TIOCSETD, &ldisc) < 0) {
+ syslog(LOG_ERR,
+ "gpstm_start: ioctl(%s, TIOCSETD): %m", dev);
+ goto screwed;
+ }
+#endif /* GPSTMCLK */
+ }
+#endif /* HAVE_BSD_TTYS */
+
+ /*
+ * Allocate unit structure
+ */
+ if (gpstm_units[unit] != 0) {
+ gpstm = gpstm_units[unit]; /* The one we want is okay */
+ } else {
+ for (i = 0; i < MAXUNITS; i++) {
+ if (!unitinuse[i] && gpstm_units[i] != 0)
+ break;
+ }
+ if (i < MAXUNITS) {
+ /*
+ * Reclaim this one
+ */
+ gpstm = gpstm_units[i];
+ gpstm_units[i] = 0;
+ } else {
+ gpstm = (struct gpstm_unit *)
+ emalloc(sizeof(struct gpstm_unit));
+ }
+ }
+ memset((char *)gpstm, 0, sizeof(struct gpstm_unit));
+ gpstm_units[unit] = gpstm;
+
+ /*
+ * Set up the structures
+ */
+ gpstm->peer = peer;
+ gpstm->unit = (u_char)unit;
+ gpstm->timestarted = current_time;
+
+ gpstm->io.clock_recv = gpstm_receive;
+ gpstm->io.srcclock = (caddr_t)gpstm;
+ gpstm->io.datalen = 0;
+ gpstm->io.fd = fd232;
+ if (!io_addclock(&gpstm->io)) {
+ goto screwed;
+ }
+
+ /*
+ * All done. Initialize a few random peer variables, then
+ * return success.
+ */
+ peer->precision = PRECISION;
+ peer->rootdelay = 0;
+ peer->rootdispersion = 0;
+ peer->stratum = stratumtouse[unit];
+ peer->refid = refid[unit];
+ unitinuse[unit] = 1;
+ gpstm_initstate(unit);
+ return 1;
+
+ /*
+ * Something broke; abandon ship
+ */
+screwed:
+ (void) close(fd232);
+ return 0;
+}
+
+/*
+ * gpstm_shutdown - shut down a clock
+ */
+static void
+gpstm_shutdown(unit, peer)
+ int unit;
+ struct peer *peer;
+{
+ register struct gpstm_unit *gpstm;
+
+ if (unit >= MAXUNITS) {
+ syslog(LOG_ERR, "gpstm_shutdown: unit %d invalid", unit);
+ return;
+ }
+ if (!unitinuse[unit]) {
+ syslog(LOG_ERR, "gpstm_shutdown: unit %d not in use", unit);
+ return;
+ }
+
+ /*
+ * Tell the I/O module to turn us off. We're history.
+ */
+ gpstm = gpstm_units[unit];
+ io_closeclock(&gpstm->io);
+ unitinuse[unit] = 0;
+}
+
+
+/*
+ * gpstm_rep_event - note the occurance of an event
+ */
+static void
+gpstm_rep_event(gpstm, code)
+ struct gpstm_unit *gpstm;
+ int code;
+{
+ struct peer *peer;
+
+ peer = gpstm->peer;
+ if (gpstm->status != (u_char)code) {
+ gpstm->status = (u_char)code;
+ if (code != CEVNT_NOMINAL)
+ gpstm->lastevent = (u_char)code;
+ syslog(LOG_INFO,
+ "clock %s event %x\n", ntoa(&peer->srcadr), code);
+#ifdef DEBUG
+ if (debug) {
+ printf("gpstm_rep_event(gpstm%d, code %d)\n",
+ gpstm->unit, code);
+ }
+#endif
+ }
+ if (code == CEVNT_BADREPLY)
+ gpstm_initstate(gpstm->unit);
+}
+
+
+/*
+ * gpstm_receive - receive data from the serial interface on a clock
+ */
+static void
+gpstm_receive(rbufp)
+ struct recvbuf *rbufp;
+{
+ register int i;
+ register struct gpstm_unit *gpstm;
+ register u_char *dpt;
+ register char *cp;
+ register u_char *dpend;
+ l_fp tstmp;
+ u_fp dispersion;
+
+ /*
+ * Get the clock this applies to and a pointers to the data
+ */
+ gpstm = (struct gpstm_unit *)rbufp->recv_srcclock;
+ dpt = (u_char *)&rbufp->recv_space;
+
+ /*
+ * Edit timecode to remove control chars
+ */
+ dpend = dpt + rbufp->recv_length;
+ cp = gpstm->lastcode;
+ while (dpt < dpend) {
+ if ((*cp = 0x7f & *dpt++) >= ' ') cp++;
+#ifdef GPSTMCLK
+ else if (*cp == '\r') {
+ if (dpend - dpt < 8) {
+ /* short timestamp */
+ return;
+ }
+ if (!buftvtots(dpt,&gpstm->lastrec)) {
+ /* screwy timestamp */
+ return;
+ }
+ dpt += 8;
+ }
+#endif
+ }
+ *cp = '\0';
+ gpstm->lencode = cp - gpstm->lastcode;
+ if (gpstm->lencode == 0)
+ return;
+#ifndef GPSTMCLK
+ gpstm->lastrec = rbufp->recv_time;
+#endif /* GPSTMCLK */
+#if !defined(GPSTMCLK) && !defined(GPSTMPPS) && defined(TIOCMODT)
+ do {
+ auto struct timeval cur, now;
+ register long usec;
+
+ if (ioctl(gpstm->io.fd, TIOCMODT, &cur) < 0) {
+ syslog(LOG_ERR, "TIOCMODT: %m");
+#ifdef DEBUG
+ if (debug) perror("TIOCMODT");
+ break;
+#endif
+ }
+ if (cur.tv_sec == 0) {
+ /* no timestamps yet */
+ if (debug) printf("MODT tv_sec == 0\n");
+ break;
+ }
+
+ gettimeofday(&now, NULL);
+ usec = 1000000 * (now.tv_sec - cur.tv_sec)
+ + (now.tv_usec - cur.tv_usec);
+#ifdef DEBUG
+ if (debug) printf("lastmodem: delay=%d us\n", usec);
+#endif
+ if (usec < 0 || usec > 10000) {
+ /* time warp or stale timestamp */
+ break;
+ }
+ if (!buftvtots((char *)&cur, &gpstm->lastrec)) {
+ /* screwy timestamp */
+ break;
+ }
+ } while (0);
+#endif /*TIOCMODT*/
+
+#ifdef DEBUG
+ if (debug)
+ printf("gpstm: timecode %d %s\n",
+ gpstm->lencode, gpstm->lastcode);
+#endif
+
+ cp = gpstm->lastcode;
+ gpstm->leap = 0;
+ if ((cp[0] == 'F' && isdigit(cp[1]) && isdigit(cp[2]))
+ || (cp[0] == ' ' && cp[1] == 'T' && cp[2] == 'R')) {
+ enum gpstm_event event;
+
+ syslog(LOG_NOTICE, "gpstm%d: \"%s\"", gpstm->unit, cp);
+ if (cp[1] == '5' && cp[2] == '0')
+ event = e_F50;
+ else if (cp[1] == '5' && cp[2] == '1')
+ event = e_F51;
+ else if (!strncmp(" TRUETIME Mk III", cp, 16))
+ event = e_F18;
+ else {
+ gpstm_rep_event(gpstm, CEVNT_BADREPLY);
+ return;
+ }
+ gpstm_doevent(gpstm->unit, event);
+ return;
+ } else if (gpstm->lencode == 13) {
+ /*
+ * Check timecode format 0
+ */
+ if (!isdigit(cp[0]) /* day of year */
+ || !isdigit(cp[1])
+ || !isdigit(cp[2])
+ || cp[3] != ':' /* : separator */
+ || !isdigit(cp[4]) /* hours */
+ || !isdigit(cp[5])
+ || cp[6] != ':' /* : separator */
+ || !isdigit(cp[7]) /* minutes */
+ || !isdigit(cp[8])
+ || cp[9] != ':' /* : separator */
+ || !isdigit(cp[10]) /* seconds */
+ || !isdigit(cp[11]))
+ {
+ gpstm->badformat++;
+ gpstm_rep_event(gpstm, CEVNT_BADREPLY);
+ return;
+ }
+
+ /*
+ * Convert format 0 and check values
+ */
+ gpstm->year = 0; /* fake */
+ gpstm->day = cp[0] - '0';
+ gpstm->day = MULBY10(gpstm->day) + cp[1] - '0';
+ gpstm->day = MULBY10(gpstm->day) + cp[2] - '0';
+ gpstm->hour = MULBY10(cp[4] - '0') + cp[5] - '0';
+ gpstm->minute = MULBY10(cp[7] - '0') + cp[8] - '0';
+ gpstm->second = MULBY10(cp[10] - '0') + cp[11] - '0';
+ gpstm->msec = 0;
+
+ if (cp[12] != ' ' && cp[12] != '.' && cp[12] != '*')
+ gpstm->leap = LEAP_NOTINSYNC;
+ else
+ gpstm->lasttime = current_time;
+
+ if (gpstm->day < 1 || gpstm->day > 366) {
+ gpstm->baddata++;
+ gpstm_rep_event(gpstm, CEVNT_BADDATE);
+ return;
+ }
+ if (gpstm->hour > 23 || gpstm->minute > 59
+ || gpstm->second > 59) {
+ gpstm->baddata++;
+ gpstm_rep_event(gpstm, CEVNT_BADTIME);
+ return;
+ }
+ gpstm_doevent(gpstm->unit, e_TS);
+ } else {
+ gpstm_rep_event(gpstm, CEVNT_BADREPLY);
+ return;
+ }
+
+ /*
+ * The clock will blurt a timecode every second but we only
+ * want one when polled. If we havn't been polled, bail out.
+ */
+ if (!gpstm->polled)
+ return;
+
+ /*
+ * Now, compute the reference time value. Use the heavy
+ * machinery for the seconds and the millisecond field for the
+ * fraction when present.
+ *
+ * this code does not yet know how to do the years
+ */
+ tstmp = gpstm->lastrec;
+ if (!clocktime(gpstm->day, gpstm->hour, gpstm->minute,
+ gpstm->second, GMT, tstmp.l_ui,
+ &gpstm->yearstart, &gpstm->lastref.l_ui))
+ {
+ gpstm->baddata++;
+ gpstm_rep_event(gpstm, CEVNT_BADTIME);
+ return;
+ }
+ MSUTOTSF(gpstm->msec, gpstm->lastref.l_uf);
+
+ i = ((int)(gpstm->coderecv)) % NCODES;
+ gpstm->offset[i] = gpstm->lastref;
+ L_SUB(&gpstm->offset[i], &tstmp);
+ if (gpstm->coderecv == 0)
+ for (i = 1; i < NCODES; i++)
+ gpstm->offset[i] = gpstm->offset[0];
+
+ gpstm->coderecv++;
+
+ /*
+ * Process the median filter, and pass the
+ * offset and dispersion along. We use lastrec as both the
+ * reference time and receive time in order to avoid being cute,
+ * like setting the reference time later than the receive time,
+ * which may cause a paranoid protocol module to chuck out the
+ * data.
+ */
+ if (!gpstm_process(gpstm, &tstmp, &dispersion)) {
+ gpstm->baddata++;
+ gpstm_rep_event(gpstm, CEVNT_BADTIME);
+ return;
+ }
+ refclock_receive(gpstm->peer, &tstmp, GMT, dispersion,
+ &gpstm->lastrec, &gpstm->lastrec, gpstm->leap);
+
+ /*
+ * We have succedded in answering the poll. Turn off the flag
+ */
+ gpstm->polled = 0;
+}
+
+/*
+ * gpstm_send - time to send the clock a signal to cough up a time sample
+ */
+static void
+gpstm_send(gpstm, cmd)
+ struct gpstm_unit *gpstm;
+ char *cmd;
+{
+#ifdef DEBUG
+ if (debug) {
+ printf("gpstm_send(gpstm%d): %s\n", gpstm->unit, cmd);
+ }
+#endif
+ if (!readonlyclockflag[gpstm->unit]) {
+ register int len = strlen(cmd);
+
+ if (write(gpstm->io.fd, cmd, len) != len) {
+ syslog(LOG_ERR, "gpstm_send: unit %d: %m",
+ gpstm->unit);
+ gpstm_rep_event(gpstm, CEVNT_FAULT);
+ }
+ }
+}
+
+/*
+ * state machine for initializing the clock
+ */
+
+static void
+gpstm_doevent(unit, event)
+ int unit;
+ enum gpstm_event event;
+{
+ struct gpstm_unit *gpstm = gpstm_units[unit];
+
+#ifdef DEBUG
+ if (debug) {
+ printf("gpstm_doevent(gpstm%d, %d)\n", unit, (int)event);
+ }
+#endif
+ if (event == e_TS && State[unit] != F51 && State[unit] != F08) {
+ gpstm_send(gpstm, "\03\r");
+ }
+
+ switch (event) {
+ case e_Init:
+ gpstm_send(gpstm, "F18\r");
+ State[unit] = Start;
+ break;
+ case e_F18:
+ gpstm_send(gpstm, "F50\r");
+ State[unit] = F18;
+ break;
+ case e_F50:
+ gpstm_send(gpstm, "F51\r");
+ State[unit] = F50;
+ break;
+ case e_F51:
+ gpstm_send(gpstm, "F08\r");
+ State[unit] = F51;
+ break;
+ case e_TS:
+ /* nothing to send - we like this mode */
+ State[unit] = F08;
+ break;
+ }
+}
+
+static void
+gpstm_initstate(unit)
+ int unit;
+ {
+ State[unit] = Base; /* just in case */
+ gpstm_doevent(unit, e_Init);
+}
+
+/*
+ * gpstm_process - process a pile of samples from the clock
+ */
+static char
+gpstm_process(gpstm, offset, dispersion)
+ struct gpstm_unit *gpstm;
+ l_fp *offset;
+ u_fp *dispersion;
+{
+ register int i, j;
+ register U_LONG tmp_ui, tmp_uf;
+ int not_median1 = -1; /* XXX correct? */
+ int not_median2 = -1; /* XXX correct? */
+ int median;
+ u_fp disp_tmp, disp_tmp2;
+
+ /*
+ * This code implements a three-stage median filter. First, we
+ * check if the samples are within 125 ms of each other. If not,
+ * dump the sample set. We take the median of the three offsets
+ * and use that as the sample offset. We take the maximum
+ * difference and use that as the sample dispersion. There
+ * probably is not much to be gained by a longer filter, since
+ * the clock filter in ntp_proto should do its thing.
+ */
+ disp_tmp2 = 0;
+ for (i = 0; i < NCODES-1; i++) {
+ for (j = i+1; j < NCODES; j++) {
+ tmp_ui = gpstm->offset[i].l_ui;
+ tmp_uf = gpstm->offset[i].l_uf;
+ M_SUB(tmp_ui, tmp_uf, gpstm->offset[j].l_ui,
+ gpstm->offset[j].l_uf);
+ if (M_ISNEG(tmp_ui, tmp_uf)) {
+ M_NEG(tmp_ui, tmp_uf);
+ }
+ if (tmp_ui != 0 || tmp_uf > CODEDIFF) {
+ return 0;
+ }
+ disp_tmp = MFPTOFP(0, tmp_uf);
+ if (disp_tmp > disp_tmp2) {
+ disp_tmp2 = disp_tmp;
+ not_median1 = i;
+ not_median2 = j;
+ }
+ }
+ }
+
+ /*
+ * It seems as if all are within 125 ms of each other.
+ * Now to determine the median of the three. Whlie the
+ * 125 ms check was going on, we also subtly catch the
+ * dispersion and set-up for a very easy median calculation.
+ * The largest difference between any two samples constitutes
+ * the dispersion. The sample not involve in the dispersion is
+ * the median sample. EASY!
+ */
+ if (gpstm->lasttime == 0 || disp_tmp2 > MAXDISPERSE)
+ disp_tmp2 = MAXDISPERSE;
+ if (not_median1 == 0) {
+ if (not_median2 == 1)
+ median = 2;
+ else
+ median = 1;
+ } else {
+ median = 0;
+ }
+ *offset = gpstm->offset[median];
+ *dispersion = disp_tmp2;
+ return 1;
+}
+
+/*
+ * gpstm_poll - called by the transmit procedure
+ */
+static void
+gpstm_poll(unit, peer)
+ int unit;
+ struct peer *peer;
+{
+ struct gpstm_unit *gpstm;
+
+ /*
+ * You don't need to poll this clock. It puts out timecodes
+ * once per second. If asked for a timestamp, take note.
+ * The next time a timecode comes in, it will be fed back.
+ */
+ if (unit >= MAXUNITS) {
+ syslog(LOG_ERR, "gpstm_poll: unit %d invalid", unit);
+ return;
+ }
+ if (!unitinuse[unit]) {
+ syslog(LOG_ERR, "gpstm_poll: unit %d not in use", unit);
+ return;
+ }
+ gpstm = gpstm_units[unit];
+ if ((current_time - gpstm->lasttime) > 150) {
+ gpstm->noreply++;
+ gpstm_rep_event(gpstm_units[unit], CEVNT_TIMEOUT);
+ gpstm_initstate(gpstm->unit);
+ }
+
+ /*
+ * polled every 64 seconds. Ask our receiver to hand in a timestamp.
+ */
+ gpstm->polled = 1;
+ gpstm->polls++;
+}
+
+/*
+ * gpstm_control - set fudge factors, return statistics
+ */
+static void
+gpstm_control(unit, in, out)
+ int unit;
+ struct refclockstat *in;
+ struct refclockstat *out;
+{
+ register struct gpstm_unit *gpstm;
+
+ if (unit >= MAXUNITS) {
+ syslog(LOG_ERR, "gpstm_control: unit %d invalid", unit);
+ return;
+ }
+
+ if (in != 0) {
+ if (in->haveflags & CLK_HAVETIME1)
+ fudgefactor1[unit] = in->fudgetime1;
+ if (in->haveflags & CLK_HAVETIME2)
+ fudgefactor2[unit] = in->fudgetime2;
+ if (in->haveflags & CLK_HAVEVAL1)
+ stratumtouse[unit] = (u_char)(in->fudgeval1);
+ if (in->haveflags & CLK_HAVEVAL2)
+ refid[unit] = in->fudgeval2;
+ if (in->haveflags & CLK_HAVEFLAG1)
+ readonlyclockflag[unit] = in->flags & CLK_FLAG1;
+ if (unitinuse[unit]) {
+ struct peer *peer;
+
+ peer = gpstm_units[unit]->peer;
+ peer->stratum = stratumtouse[unit];
+ peer->refid = refid[unit];
+ }
+ }
+
+ if (out != 0) {
+ memset((char *)out, 0, sizeof (struct refclockstat));
+ out->type = REFCLK_GPSTM_TRUETIME;
+ out->haveflags = CLK_HAVETIME1 | CLK_HAVETIME2 | CLK_HAVEVAL1 |
+ CLK_HAVEVAL2 | CLK_HAVEFLAG1;
+ out->clockdesc = DESCRIPTION;
+ out->fudgetime1 = fudgefactor1[unit];
+ out->fudgetime2 = fudgefactor2[unit];
+ out->fudgeval1 = (LONG)stratumtouse[unit];
+ out->fudgeval2 = refid[unit];
+ out->flags = readonlyclockflag[unit];
+ if (unitinuse[unit]) {
+ gpstm = gpstm_units[unit];
+ out->lencode = gpstm->lencode;
+ out->lastcode = gpstm->lastcode;
+ out->timereset = current_time - gpstm->timestarted;
+ out->polls = gpstm->polls;
+ out->noresponse = gpstm->noreply;
+ out->badformat = gpstm->badformat;
+ out->baddata = gpstm->baddata;
+ out->lastevent = gpstm->lastevent;
+ out->currentstatus = gpstm->status;
+ }
+ }
+}
+
+/*
+ * gpstm_buginfo - return clock dependent debugging info
+ */
+static void
+gpstm_buginfo(unit, bug)
+ int unit;
+ register struct refclockbug *bug;
+{
+ register struct gpstm_unit *gpstm;
+
+ if (unit >= MAXUNITS) {
+ syslog(LOG_ERR, "gpstm_buginfo: unit %d invalid", unit);
+ return;
+ }
+
+ if (!unitinuse[unit])
+ return;
+ gpstm = gpstm_units[unit];
+
+ bug->nvalues = 11;
+ bug->ntimes = 5;
+ if (gpstm->lasttime != 0)
+ bug->values[0] = current_time - gpstm->lasttime;
+ else
+ bug->values[0] = 0;
+ bug->values[1] = (U_LONG)gpstm->reason;
+ bug->values[2] = (U_LONG)gpstm->year;
+ bug->values[3] = (U_LONG)gpstm->day;
+ bug->values[4] = (U_LONG)gpstm->hour;
+ bug->values[5] = (U_LONG)gpstm->minute;
+ bug->values[6] = (U_LONG)gpstm->second;
+ bug->values[7] = (U_LONG)gpstm->msec;
+ bug->values[8] = gpstm->noreply;
+ bug->values[9] = gpstm->yearstart;
+ bug->values[10] = gpstm->quality;
+ bug->stimes = 0x1c;
+ bug->times[0] = gpstm->lastref;
+ bug->times[1] = gpstm->lastrec;
+ bug->times[2] = gpstm->offset[0];
+ bug->times[3] = gpstm->offset[1];
+ bug->times[4] = gpstm->offset[2];
+}
+
+#endif /*GPSTM et al*/
diff --git a/usr.sbin/xntpd/xntpd/refclock_heath.c b/usr.sbin/xntpd/xntpd/refclock_heath.c
new file mode 100644
index 0000000..a1b602c
--- /dev/null
+++ b/usr.sbin/xntpd/xntpd/refclock_heath.c
@@ -0,0 +1,393 @@
+/*
+ * refclock_heath - clock driver for Heath GC-1000 Most Accurate Clock
+ */
+#if defined(REFCLOCK) && defined(HEATH)
+
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/time.h>
+#include <time.h>
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_stdlib.h"
+
+/*
+ * This driver supports the Heath GC-1000 Most Accurate Clock, with
+ * RS232C Output Accessory. This is a WWV/WWVH receiver somewhat less
+ * robust than other supported receivers. Its claimed accuracy is 100 ms
+ * when actually synchronized to the broadcast signal, but this doesn't
+ * happen even most of the time, due to propagation conditions, ambient
+ * noise sources, etc. When not synchronized, the accuracy is at the
+ * whim of the internal clock oscillator, which can wander into the
+ * sunset without warning. Since the indicated precision is 100 ms,
+ * expect a host synchronized only to this thing to wander to and fro,
+ * occasionally being rudely stepped when the offset exceeds the default
+ * CLOCK_MAX of 128 ms.
+ *
+ * The internal DIPswitches should be set to operate at 1200 baud in
+ * MANUAL mode and the current year. The external DIPswitches should be
+ * set to GMT and 24-hour format, or to the host local time zone (with
+ * DST) and 12-hour format. It is very important that the year be
+ * set correctly in the DIPswitches. Otherwise, the day of year will be
+ * incorrect after 28 April of a normal or leap year. In 12-hour mode
+ * with DST selected the clock will be incorrect by an hour for an
+ * indeterminate amount of time between 0000Z and 0200 on the day DST
+ * changes.
+ *
+ * In MANUAL mode the clock responds to a rising edge of the request to
+ * send (RTS) modem control line by sending the timecode. Therefore, it
+ * is necessary that the operating system implement the TIOCMBIC and
+ * TIOCMBIS ioctl system calls and TIOCM_RTS control bit. Present
+ * restrictions require the use of a POSIX-compatible programming
+ * interface, although other interfaces may work as well.
+ *
+ * A simple hardware modification to the clock can be made which
+ * prevents the clock hearing the request to send (RTS) if the HI SPEC
+ * lamp is out. Route the HISPEC signal to the tone decoder board pin
+ * 19, from the display, pin 19. Isolate pin 19 of the decoder board
+ * first, but maintain connection with pin 10. Also isolate pin 38 of
+ * the CPU on the tone board, and use half an added 7400 to gate the
+ * original signal to pin 38 with that from pin 19.
+ *
+ * The clock message consists of 23 ASCII printing characters in the
+ * following format:
+ *
+ * hh:mm:ss.f AM dd/mm/yr<cr>
+ *
+ * hh:mm:ss.f = hours, minutes, seconds
+ * f = deciseconds ('?' when out of spec)
+ * AM/PM/bb = blank in 24-hour mode
+ * dd/mm/yr = day, month, year
+ *
+ * The alarm condition is indicated by '?', rather than a digit, at f.
+ * Note that 0?:??:??.? is displayed before synchronization is first
+ * established and hh:mm:ss.? once synchronization is established and
+ * then lost again for about a day.
+ *
+ * Fudge Factors
+ *
+ * A fudge time1 value of .04 s appears to center the clock offset
+ * residuals. The fudge time2 parameter is the local time offset east of
+ * Greenwich, which depends on DST. Sorry about that, but the clock
+ * gives no hint on what the DIPswitches say.
+ */
+
+/*
+ * Interface definitions
+ */
+#define DEVICE "/dev/heath%d" /* device name and unit */
+#define SPEED232 B1200 /* uart speed (1200 baud) */
+#define PRECISION (-4) /* precision assumed (about 100 ms) */
+#define REFID "WWV\0" /* reference ID */
+#define DESCRIPTION "Heath GC-1000 Most Accurate Clock" /* WRU */
+
+#define NSAMPLES 3 /* stages of median filter */
+#define LENHEATH 23 /* min timecode length */
+
+/*
+ * Imported from ntp_timer module
+ */
+extern u_long current_time; /* current time (s) */
+
+/*
+ * Imported from ntpd module
+ */
+extern int debug; /* global debug flag */
+
+/*
+ * Tables to compute the ddd of year form icky dd/mm timecode. Viva la
+ * leap.
+ */
+static day1tab[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
+static day2tab[] = {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
+
+/*
+ * Unit control structure
+ */
+struct heathunit {
+ int pollcnt; /* poll message counter */
+ l_fp tstamp; /* timestamp of last poll */
+};
+
+/*
+ * Function prototypes
+ */
+static int heath_start P((int, struct peer *));
+static void heath_shutdown P((int, struct peer *));
+static void heath_receive P((struct recvbuf *));
+static void heath_poll P((int, struct peer *));
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_heath = {
+ heath_start, /* start up driver */
+ heath_shutdown, /* shut down driver */
+ heath_poll, /* transmit poll message */
+ noentry, /* not used (old heath_control) */
+ noentry, /* initialize driver */
+ noentry, /* not used (old heath_buginfo) */
+ NOFLAGS /* not used */
+};
+
+
+/*
+ * heath_start - open the devices and initialize data for processing
+ */
+static int
+heath_start(unit, peer)
+ int unit;
+ struct peer *peer;
+{
+ register struct heathunit *up;
+ struct refclockproc *pp;
+ int fd;
+ char device[20];
+
+ /*
+ * Open serial port
+ */
+ (void)sprintf(device, DEVICE, unit);
+ if (!(fd = refclock_open(device, SPEED232, 0)))
+ return (0);
+
+ /*
+ * Allocate and initialize unit structure
+ */
+ if (!(up = (struct heathunit *)
+ emalloc(sizeof(struct heathunit)))) {
+ (void) close(fd);
+ return (0);
+ }
+ memset((char *)up, 0, sizeof(struct heathunit));
+ pp = peer->procptr;
+ pp->io.clock_recv = heath_receive;
+ pp->io.srcclock = (caddr_t)peer;
+ pp->io.datalen = 0;
+ pp->io.fd = fd;
+ if (!io_addclock(&pp->io)) {
+ (void) close(fd);
+ free(up);
+ return (0);
+ }
+ pp->unitptr = (caddr_t)up;
+
+ /*
+ * Initialize miscellaneous variables
+ */
+ peer->precision = PRECISION;
+ pp->clockdesc = DESCRIPTION;
+ memcpy((char *)&pp->refid, REFID, 4);
+ up->pollcnt = 2;
+ return (1);
+}
+
+
+/*
+ * heath_shutdown - shut down the clock
+ */
+static void
+heath_shutdown(unit, peer)
+ int unit;
+ struct peer *peer;
+{
+ register struct heathunit *up;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ up = (struct heathunit *)pp->unitptr;
+ io_closeclock(&pp->io);
+ free(up);
+}
+
+
+/*
+ * heath_receive - receive data from the serial interface
+ */
+static void
+heath_receive(rbufp)
+ struct recvbuf *rbufp;
+{
+ register struct heathunit *up;
+ struct refclockproc *pp;
+ struct peer *peer;
+ l_fp trtmp;
+ int month, day;
+ int i;
+ char dsec, a[5];
+
+ /*
+ * Initialize pointers and read the timecode and timestamp
+ */
+ peer = (struct peer *)rbufp->recv_srcclock;
+ pp = peer->procptr;
+ up = (struct heathunit *)pp->unitptr;
+ pp->lencode = refclock_gtlin(rbufp, pp->lastcode, BMAX, &trtmp);
+
+ /*
+ * We get a buffer and timestamp for each <cr>; however, we use
+ * the timestamp captured at the RTS modem control line toggle
+ * on the assumption that's what the radio bases the timecode
+ * on. Apparently, the radio takes about a second to make up its
+ * mind to send a timecode, so the receive timestamp is
+ * worthless.
+ */
+ pp->lastrec = up->tstamp;
+ up->pollcnt = 2;
+ record_clock_stats(&peer->srcadr, pp->lastcode);
+#ifdef DEBUG
+ if (debug)
+ printf("heath: timecode %d %s\n", pp->lencode,
+ pp->lastcode);
+#endif
+
+ /*
+ * We get down to business, check the timecode format and decode
+ * its contents. If the timecode has invalid length or is not in
+ * proper format, we declare bad format and exit.
+ */
+ if (pp->lencode < LENHEATH) {
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+
+ /*
+ * Timecode format: "hh:mm:ss.f AM mm/dd/yy"
+ */
+ if (sscanf(pp->lastcode, "%2d:%2d:%2d.%c%5c%2d/%2d/%2d",
+ &pp->hour, &pp->minute, &pp->second, &dsec, a, &month, &day,
+ &pp->year) != 8) {
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+
+ /*
+ * If AM or PM is received, assume the clock is displaying local
+ * time. First, convert to 24-hour format, then add the local
+ * time correction (in hours east of Greenwich) from
+ * fudgetime2.
+ */
+ switch (a[1]) {
+ case 'P':
+ if (pp->hour < 12)
+ pp->hour += 12;
+ break;
+
+ case 'A':
+ if (pp->hour == 12)
+ pp->hour -= 12;
+ break;
+ }
+ i = (int)pp->hour - (int)pp->fudgetime2.l_ui;
+ if (i < 0)
+ i += 24;
+ pp->hour = i % 24;
+
+ /*
+ * We determine the day of the year from the DIPswitches. This
+ * should be fixed, since somebody might forget to set them.
+ * Someday this hazard will be fixed by a fiendish scheme that
+ * looks at the timecode and year the radio shows, then computes
+ * the residue of the seconds mod the seconds in a leap cycle.
+ * If in the third year of that cycle and the third and later
+ * months of that year, add one to the day. Then, correct the
+ * timecode accordingly. Icky pooh. This bit of nonsense could
+ * be avoided if the engineers had been required to write a
+ * device driver before finalizing the timecode format.
+ *
+ * Yes, I know this code incorrectly thinks that 2000 is a leap
+ * year; but, the latest year that can be set by the DIPswitches
+ * is 1997 anyay. Life is short.
+ */
+ if (month < 1 || month > 12 || day < 1) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ if (pp->year % 4) {
+ if (day > day1tab[month - 1]) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ for (i = 0; i < month - 1; i++)
+ day += day1tab[i];
+ } else {
+ if (day > day2tab[month - 1]) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ for (i = 0; i < month - 1; i++)
+ day += day2tab[i];
+ }
+ pp->day = day;
+
+ /*
+ * Determine synchronization and last update
+ */
+ if (!isdigit(dsec)) {
+ pp->leap = LEAP_NOTINSYNC;
+ } else {
+ pp->leap = 0;
+ pp->lasttime = current_time;
+ pp->msec = (dsec - '0') * 100;
+ }
+
+ /*
+ * Process the new sample in the median filter and determine the
+ * reference clock offset and dispersion. We use lastrec as both
+ * the reference time and receive time, in order to avoid being
+ * cute, like setting the reference time later than the receive
+ * time, which may cause a paranoid protocol module to chuck out
+ * the data.
+ */
+ if (!refclock_process(pp, NSAMPLES, NSAMPLES)) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ refclock_receive(peer, &pp->offset, 0, pp->dispersion,
+ &pp->lastrec, &pp->lastrec, pp->leap);
+}
+
+
+/*
+ * heath_poll - called by the transmit procedure
+ */
+static void
+heath_poll(unit, peer)
+ int unit;
+ struct peer *peer;
+{
+ register struct heathunit *up;
+ struct refclockproc *pp;
+ int bits = TIOCM_RTS;
+
+ /*
+ * At each poll we check for timeout and toggle the RTS modem
+ * control line, then take a timestamp. Presumably, this is the
+ * event the radio captures to generate the timecode.
+ */
+ pp = peer->procptr;
+ up = (struct heathunit *)pp->unitptr;
+ if (up->pollcnt == 0)
+ refclock_report(peer, CEVNT_TIMEOUT);
+ else
+ up->pollcnt--;
+ pp->polls++;
+
+ /*
+ * We toggle the RTS modem control lead to kick a timecode loose
+ * from the radio. This code works only for POSIX and SYSV
+ * interfaces. With bsd you are on your own. We take a timestamp
+ * between the up and down edges to lengthen the pulse, which
+ * should be about 50 usec on a Sun IPC. With hotshot CPUs, the
+ * pulse might get too short. Later.
+ */
+ if (ioctl(pp->io.fd, TIOCMBIC, (char *)&bits) < 0)
+ refclock_report(peer, CEVNT_FAULT);
+ gettstamp(&up->tstamp);
+ ioctl(pp->io.fd, TIOCMBIS, (char *)&bits);
+
+}
+
+#endif
diff --git a/usr.sbin/xntpd/xntpd/refclock_irig.c b/usr.sbin/xntpd/xntpd/refclock_irig.c
new file mode 100644
index 0000000..bc93fd7
--- /dev/null
+++ b/usr.sbin/xntpd/xntpd/refclock_irig.c
@@ -0,0 +1,259 @@
+/*
+ * refclock_irig - clock driver for the IRIG audio decoder
+ */
+#if defined(REFCLOCK) && defined(IRIG) && defined(sun)
+
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/time.h>
+#include <sys/ioccom.h>
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_unixtime.h"
+#include <sys/bsd_audioirig.h>
+#include "ntp_stdlib.h"
+
+/*
+ * This driver supports the IRIG audio decoder. This clever gadget uses
+ * a modified BSD audio driver for the Sun SPARCstation which provides
+ * a timestamp, raw binary timecode, status byte and decoded ASCII
+ * timecode. The data are represented in the structure in the
+ * sys/bsd_audioirig.h header file:
+ *
+ * struct irig_time {
+ * struct timeval stamp; timestamp
+ * u_char bits[13]; 100 IRIG data bits
+ * u_char status; status byte
+ * char time[14]; time string (null terminated)
+ *
+ * where stamp represents a timestamp at the zero crossing of the index
+ * marker at the second's epoch, bits is a 13-octet, zero-padded binary-
+ * coded string representing code elements 1 through 100 in the IRIG-B
+ * code format, and status is a status bute, The decoded timestamp is a
+ * 13-octet, null-terminated ASCII string "ddd hh:mm:ss*", where ddd is
+ * the day of year, hh:mm:ss the time of day and * is a status
+ * indicator, with " " indicating valid time and "?" indicating
+ * something wrong.
+ *
+ * The timestamp is in Unix timeval format, consisting of two 32-bit
+ * words, the first of which is the seconds since 1970 and the second is
+ * the fraction of the second in microseconds. The status byte is zero
+ * if (a) the input signal is within amplitude tolerances, (b) the raw
+ * binary timecode contains only valid code elements, (c) 11 position
+ * identifiers have been found at the expected element positions, (d)
+ * the clock status byte contained in the timecode is valid, and (e) a
+ * time determination has been made since the last read() system call.
+ *
+ * The 100 elements of the IRIG-B timecode are numbered from 0 through
+ * 99. Position identifiers occur at elements 0, 9, 19 and every ten
+ * thereafter to 99. The control function (CF) elements begin at element
+ * 50 (CF 1) and extend to element 78 (CF 27). The straight-binary-
+ * seconds (SBS) field, which encodes the seconds of the UTC day, begins
+ * at element 80 (CF 28) and extends to element 97 (CF 44). The encoding
+ * of elements 50 (CF 1) through 78 (CF 27) is device dependent. This
+ * driver presently does not use the CF elements.
+ *
+ * Where feasible, the interface should be operated with signature
+ * control, so that, if the IRIG signal is lost or malformed, the
+ * interface produces an unmodulated signal, rather than possibly random
+ * digits. The driver will declare "unsynchronized" in this case.
+ *
+ * Spectracom Netclock/2 WWVB Synchronized Clock
+ *
+ * Element CF Function
+ * -------------------------------------
+ * 55 6 time sync status
+ * 60-63 10-13 bcd year units
+ * 65-68 15-18 bcd year tens
+ *
+ */
+
+/*
+ * IRIG interface definitions
+ */
+#define DEVICE "/dev/irig%d" /* device name and unit */
+#define PRECISION (-13) /* precision assumed (100 us) */
+#define REFID "IRIG" /* reference ID */
+#define DESCRIPTION "IRIG Audio Decoder" /* WRU */
+
+#define NSAMPLES 3 /* stages of median filter */
+#define IRIG_FORMAT 1 /* IRIG timestamp format */
+
+/*
+ * Imported from ntp_timer module
+ */
+extern u_long current_time; /* current time (s) */
+
+/*
+ * Imported from ntpd module
+ */
+extern int debug; /* global debug flag */
+
+/*
+ * Function prototypes
+ */
+static int irig_start P((int, struct peer *));
+static void irig_shutdown P((int, struct peer *));
+static void irig_poll P((int, struct peer *));
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_irig = {
+ irig_start, /* start up driver */
+ irig_shutdown, /* shut down driver */
+ irig_poll, /* transmit poll message */
+ noentry, /* not used (old irig_control) */
+ noentry, /* initialize driver (not used) */
+ noentry, /* not used (old irig_buginfo) */
+ NOFLAGS /* not used */
+};
+
+
+/*
+ * irig_start - open the device and initialize data for processing
+ */
+static int
+irig_start(unit, peer)
+ int unit;
+ struct peer *peer;
+{
+ register struct refclockproc *pp;
+ char device[20];
+ int fd;
+ int format = IRIG_FORMAT;
+
+ /*
+ * Open audio device and set format
+ */
+ (void)sprintf(device, DEVICE, unit);
+ fd = open(device, O_RDONLY | O_NDELAY, 0777);
+ if (fd == -1) {
+ syslog(LOG_ERR, "irig_start: open of %s: %m", device);
+ return (0);
+ }
+ if (ioctl(fd, AUDIO_IRIG_OPEN, 0) < 0) {
+ syslog(LOG_ERR, "irig_start: AUDIO_IRIG_OPEN %m");
+ close(fd);
+ return (0);
+ }
+ if (ioctl(fd, AUDIO_IRIG_SETFORMAT, (char *)&format) < 0) {
+ syslog(LOG_ERR, "irig_start: AUDIO_IRIG_SETFORMAT %m",
+ DEVICE);
+ close(fd);
+ return (0);
+ }
+ pp = peer->procptr;
+ pp->io.clock_recv = noentry;
+ pp->io.srcclock = (caddr_t)peer;
+ pp->io.datalen = 0;
+ pp->io.fd = fd;
+
+ /*
+ * Initialize miscellaneous variables
+ */
+ peer->precision = PRECISION;
+ pp->clockdesc = DESCRIPTION;
+ memcpy((char *)&pp->refid, REFID, 4);
+ return (1);
+}
+
+
+/*
+ * irig_shutdown - shut down the clock
+ */
+static void
+irig_shutdown(unit, peer)
+ int unit;
+ struct peer *peer;
+{
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ io_closeclock(&pp->io);
+}
+
+
+/*
+ * irig_poll - called by the transmit procedure
+ */
+static void
+irig_poll(unit, peer)
+ int unit;
+ struct peer *peer;
+{
+
+ struct refclockproc *pp;
+ struct irig_time buf;
+ char *cp, *dp;
+ u_char *dpt;
+ int i;
+
+ pp = peer->procptr;
+ if (read(pp->io.fd, (char *) &buf, sizeof(buf)) != sizeof(buf)) {
+ refclock_report(peer, CEVNT_FAULT);
+ return;
+ }
+ pp->polls++;
+
+#ifdef DEBUG
+ if (debug) {
+ dpt = (u_char *)&buf;
+ printf("irig: ");
+ for (i = 0; i < sizeof(buf); i++)
+ printf("%02x", *dpt++);
+ printf("\n");
+ }
+#endif
+
+ buf.stamp.tv_sec += JAN_1970;
+ TVTOTS(&buf.stamp, &pp->lastrec);
+ cp = buf.time;
+ dp = pp->lastcode;
+ for (i = 0; i < sizeof(buf.time); i++)
+ *dp++ = *cp++;
+ *--dp = '\0';
+ pp->lencode = dp - pp->lastcode;
+
+#ifdef DEBUG
+ if (debug)
+ printf("irig: time %s timecode %d %s\n",
+ ulfptoa(&pp->lastrec, 6), pp->lencode,
+ pp->lastcode);
+#endif
+ record_clock_stats(&peer->srcadr, pp->lastcode);
+
+ /*
+ * Get IRIG time and convert to timestamp format
+ */
+ if (sscanf(pp->lastcode, "%3d %2d:%2d:%2d",
+ &pp->day, &pp->hour, &pp->minute, &pp->second) != 4) {
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+ if (pp->lastcode[12] != ' ') {
+ pp->leap = LEAP_NOTINSYNC;
+ } else {
+ pp->leap = 0;
+ pp->lasttime = current_time;
+ }
+
+ /*
+ * Process the new sample in the median filter and determine the
+ * reference clock offset and dispersion. We use lastrec as both
+ * the reference time and receive time in order to avoid being
+ * cute, like setting the reference time later than the receive
+ * time, which may cause a paranoid protocol module to chuck out
+ * the data.
+ */
+ if (!refclock_process(pp, NSAMPLES, NSAMPLES)) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ refclock_receive(peer, &pp->offset, 0, pp->dispersion,
+ &pp->lastrec, &pp->lastrec, pp->leap);
+}
+
+#endif
diff --git a/usr.sbin/xntpd/xntpd/refclock_leitch.c b/usr.sbin/xntpd/xntpd/refclock_leitch.c
new file mode 100644
index 0000000..b6b002f
--- /dev/null
+++ b/usr.sbin/xntpd/xntpd/refclock_leitch.c
@@ -0,0 +1,710 @@
+/*
+ * refclock_leitch - clock driver for the Leitch CSD-5300 Master Clock
+ */
+#if defined(REFCLOCK) && (defined(LEITCH) || defined(LEITCHCLK) || defined(LEITCHPPS))
+
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/time.h>
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_unixtime.h"
+
+#if defined(HAVE_BSD_TTYS)
+#include <sgtty.h>
+#endif /* HAVE_BSD_TTYS */
+
+#if defined(HAVE_SYSV_TTYS)
+#include <termio.h>
+#endif /* HAVE_SYSV_TTYS */
+
+#if defined(HAVE_TERMIOS)
+#include <termios.h>
+#endif
+#ifdef STREAM
+#include <stropts.h>
+#if defined(LEITCHCLK)
+#include <sys/clkdefs.h>
+#endif /* LEITCHCLK */
+#endif /* STREAM */
+
+#if defined (LEITCHPPS)
+#include <sys/ppsclock.h>
+#endif /* LEITCHPPS */
+
+#include "ntp_stdlib.h"
+
+/*
+ * Driver for Leitch CSD-5300 Master Clock System
+ *
+ * COMMANDS:
+ * DATE: D <CR>
+ * TIME: T <CR>
+ * STATUS: S <CR>
+ * LOOP: L <CR>
+ *
+ * FORMAT:
+ * DATE: YYMMDD<CR>
+ * TIME: <CR>/HHMMSS <CR>/HHMMSS <CR>/HHMMSS <CR>/
+ * second bondaried on the stop bit of the <CR>
+ * second boundaries at '/' above.
+ * STATUS: G (good), D (diag fail), T (time not provided) or
+ * P (last phone update failed)
+ */
+#define MAXUNITS 1 /* max number of LEITCH units */
+#define LEITCHREFID "ATOM" /* reference id */
+#define LEITCH_DESCRIPTION "Leitch: CSD 5300 Master Clock System Driver"
+#define LEITCH232 "/dev/leitch%d" /* name of radio device */
+#define SPEED232 B300 /* uart speed (300 baud) */
+#define leitch_send(A,M) \
+ if (debug) fprintf(stderr,"write leitch %s\n",M); \
+ if ((write(A->leitchio.fd,M,sizeof(M)) < 0)) {\
+ if (debug) \
+ fprintf(stderr, "leitch_send: unit %d send failed\n", A->unit); \
+ else \
+ syslog(LOG_ERR, "leitch_send: unit %d send failed %m",A->unit);}
+
+#define STATE_IDLE 0
+#define STATE_DATE 1
+#define STATE_TIME1 2
+#define STATE_TIME2 3
+#define STATE_TIME3 4
+
+extern struct event timerqueue[];
+
+/*
+ * Imported from ntp_loopfilter module
+ */
+extern int fdpps; /* pps file descriptor */
+
+/*
+ * Imported from ntpd module
+ */
+extern int debug; /* global debug flag */
+
+/*
+ * LEITCH unit control structure
+ */
+struct leitchunit {
+ struct peer *peer;
+ struct event leitchtimer;
+ struct refclockio leitchio;
+ u_char unit;
+ short year;
+ short yearday;
+ short month;
+ short day;
+ short hour;
+ short second;
+ short minute;
+ short state;
+ u_short fudge1;
+ l_fp reftime1;
+ l_fp reftime2;
+ l_fp reftime3;
+ l_fp codetime1;
+ l_fp codetime2;
+ l_fp codetime3;
+ u_long yearstart;
+};
+
+/*
+ * Function prototypes
+ */
+static void leitch_init P((void));
+static int leitch_start P((int, struct peer *));
+static void leitch_shutdown P((int, struct peer *));
+static void leitch_poll P((int, struct peer *));
+static void leitch_control P((int, struct refclockstat *, struct refclockstat *));
+#define leitch_buginfo noentry
+static void leitch_receive P((struct recvbuf *));
+static void leitch_process P((struct leitchunit *));
+static void leitch_timeout P((struct peer *));
+static int leitch_get_date P((struct recvbuf *, struct leitchunit *));
+static int leitch_get_time P((struct recvbuf *, struct leitchunit *, int));
+static int dysize P((int));
+
+static struct leitchunit leitchunits[MAXUNITS];
+static u_char unitinuse[MAXUNITS];
+static u_char stratumtouse[MAXUNITS];
+static U_LONG refid[MAXUNITS];
+
+static char days_in_month [] = { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_leitch = {
+ leitch_start, leitch_shutdown, leitch_poll,
+ leitch_control, leitch_init, leitch_buginfo, NOFLAGS
+};
+
+/*
+ * leitch_init - initialize internal leitch driver data
+ */
+static void
+leitch_init()
+{
+ int i;
+
+ memset((char*)leitchunits, 0, sizeof(leitchunits));
+ memset((char*)unitinuse, 0, sizeof(unitinuse));
+ for (i = 0; i < MAXUNITS; i++)
+ memcpy((char *)&refid[i], LEITCHREFID, 4);
+}
+
+/*
+ * leitch_shutdown - shut down a LEITCH clock
+ */
+static void
+leitch_shutdown(unit, peer)
+ int unit;
+ struct peer *peer;
+{
+#ifdef DEBUG
+ if (debug)
+ fprintf(stderr, "leitch_shutdown()\n");
+#endif
+}
+
+/*
+ * leitch_poll - called by the transmit procedure
+ */
+static void
+leitch_poll(unit, peer)
+ int unit;
+ struct peer *peer;
+{
+ struct leitchunit *leitch;
+
+ /* start the state machine rolling */
+
+#ifdef DEBUG
+ if (debug)
+ fprintf(stderr, "leitch_poll()\n");
+#endif
+ if (unit > MAXUNITS) {
+ /* XXXX syslog it */
+ return;
+ }
+
+ leitch = &leitchunits[unit];
+
+ if (leitch->state != STATE_IDLE) {
+ /* reset and wait for next poll */
+ /* XXXX syslog it */
+ leitch->state = STATE_IDLE;
+ } else {
+ leitch_send(leitch,"D\r");
+ leitch->state = STATE_DATE;
+ }
+}
+
+static void
+leitch_control(unit, in, out)
+ int unit;
+ struct refclockstat *in;
+ struct refclockstat *out;
+{
+ if (unit >= MAXUNITS) {
+ syslog(LOG_ERR,
+ "leitch_control: unit %d invalid", unit);
+ return;
+ }
+
+ if (in) {
+ if (in->haveflags & CLK_HAVEVAL1)
+ stratumtouse[unit] = (u_char)(in->fudgeval1);
+ if (in->haveflags & CLK_HAVEVAL2)
+ refid[unit] = in->fudgeval2;
+ if (unitinuse[unit]) {
+ struct peer *peer;
+
+ peer = (&leitchunits[unit])->peer;
+ peer->stratum = stratumtouse[unit];
+ peer->refid = refid[unit];
+ }
+ }
+
+ if (out) {
+ memset((char *)out, 0, sizeof (struct refclockstat));
+ out->type = REFCLK_ATOM_LEITCH;
+ out->haveflags = CLK_HAVEVAL1 | CLK_HAVEVAL2;
+ out->fudgeval1 = (LONG)stratumtouse[unit];
+ out->fudgeval2 = refid[unit];
+ out->lastcode = "";
+ out->clockdesc = LEITCH_DESCRIPTION;
+ }
+}
+
+/*
+ * leitch_start - open the LEITCH devices and initialize data for processing
+ */
+static int
+leitch_start(unit, peer)
+ int unit;
+ struct peer *peer;
+{
+ struct leitchunit *leitch;
+ int fd232;
+ char leitchdev[20];
+
+ /*
+ * Check configuration info.
+ */
+ if (unit >= MAXUNITS) {
+ syslog(LOG_ERR, "leitch_start: unit %d invalid", unit);
+ return (0);
+ }
+
+ if (unitinuse[unit]) {
+ syslog(LOG_ERR, "leitch_start: unit %d in use", unit);
+ return (0);
+ }
+
+ /*
+ * Open serial port.
+ */
+ (void) sprintf(leitchdev, LEITCH232, unit);
+ fd232 = open(leitchdev, O_RDWR, 0777);
+ if (fd232 == -1) {
+ syslog(LOG_ERR,
+ "leitch_start: open of %s: %m", leitchdev);
+ return (0);
+ }
+
+ leitch = &leitchunits[unit];
+ memset((char*)leitch, 0, sizeof(*leitch));
+
+#if defined(HAVE_SYSV_TTYS)
+ /*
+ * System V serial line parameters (termio interface)
+ *
+ */
+ { struct termio ttyb;
+ if (ioctl(fd232, TCGETA, &ttyb) < 0) {
+ syslog(LOG_ERR,
+ "leitch_start: ioctl(%s, TCGETA): %m", leitchdev);
+ goto screwed;
+ }
+ ttyb.c_iflag = IGNBRK|IGNPAR|ICRNL;
+ ttyb.c_oflag = 0;
+ ttyb.c_cflag = SPEED232|CS8|CLOCAL|CREAD;
+ ttyb.c_lflag = ICANON;
+ ttyb.c_cc[VERASE] = ttyb.c_cc[VKILL] = '\0';
+ if (ioctl(fd232, TCSETA, &ttyb) < 0) {
+ syslog(LOG_ERR,
+ "leitch_start: ioctl(%s, TCSETA): %m", leitchdev);
+ goto screwed;
+ }
+ }
+#endif /* HAVE_SYSV_TTYS */
+#if defined(HAVE_TERMIOS)
+ /*
+ * POSIX serial line parameters (termios interface)
+ *
+ * The LEITCHCLK option provides timestamping at the driver level.
+ * It requires the tty_clk streams module.
+ *
+ * The LEITCHPPS option provides timestamping at the driver level.
+ * It uses a 1-pps signal and level converter (gadget box) and
+ * requires the ppsclock streams module and SunOS 4.1.1 or
+ * later.
+ */
+ { struct termios ttyb, *ttyp;
+
+ ttyp = &ttyb;
+ if (tcgetattr(fd232, ttyp) < 0) {
+ syslog(LOG_ERR,
+ "leitch_start: tcgetattr(%s): %m", leitchdev);
+ goto screwed;
+ }
+ ttyp->c_iflag = IGNBRK|IGNPAR|ICRNL;
+ ttyp->c_oflag = 0;
+ ttyp->c_cflag = SPEED232|CS8|CLOCAL|CREAD;
+ ttyp->c_lflag = ICANON;
+ ttyp->c_cc[VERASE] = ttyp->c_cc[VKILL] = '\0';
+ if (tcsetattr(fd232, TCSANOW, ttyp) < 0) {
+ syslog(LOG_ERR,
+ "leitch_start: tcsetattr(%s): %m", leitchdev);
+ goto screwed;
+ }
+ if (tcflush(fd232, TCIOFLUSH) < 0) {
+ syslog(LOG_ERR,
+ "leitch_start: tcflush(%s): %m", leitchdev);
+ goto screwed;
+ }
+ }
+#endif /* HAVE_TERMIOS */
+#ifdef STREAM
+#if defined(LEITCHCLK)
+ if (ioctl(fd232, I_PUSH, "clk") < 0)
+ syslog(LOG_ERR,
+ "leitch_start: ioctl(%s, I_PUSH, clk): %m", leitchdev);
+ if (ioctl(fd232, CLK_SETSTR, "\n") < 0)
+ syslog(LOG_ERR,
+ "leitch_start: ioctl(%s, CLK_SETSTR): %m", leitchdev);
+#endif /* LEITCHCLK */
+#if defined(LEITCHPPS)
+ if (ioctl(fd232, I_PUSH, "ppsclock") < 0)
+ syslog(LOG_ERR,
+ "leitch_start: ioctl(%s, I_PUSH, ppsclock): %m", leitchdev);
+ else
+ fdpps = fd232;
+#endif /* LEITCHPPS */
+#endif /* STREAM */
+#if defined(HAVE_BSD_TTYS)
+ /*
+ * 4.3bsd serial line parameters (sgttyb interface)
+ *
+ * The LEITCHCLK option provides timestamping at the driver level.
+ * It requires the tty_clk line discipline and 4.3bsd or later.
+ */
+ { struct sgttyb ttyb;
+#if defined(LEITCHCLK)
+ int ldisc = CLKLDISC;
+#endif /* LEITCHCLK */
+
+ if (ioctl(fd232, TIOCGETP, &ttyb) < 0) {
+ syslog(LOG_ERR,
+ "leitch_start: ioctl(%s, TIOCGETP): %m", leitchdev);
+ goto screwed;
+ }
+ ttyb.sg_ispeed = ttyb.sg_ospeed = SPEED232;
+#if defined(LEITCHCLK)
+ ttyb.sg_erase = ttyb.sg_kill = '\r';
+ ttyb.sg_flags = RAW;
+#else
+ ttyb.sg_erase = ttyb.sg_kill = '\0';
+ ttyb.sg_flags = EVENP|ODDP|CRMOD;
+#endif /* LEITCHCLK */
+ if (ioctl(fd232, TIOCSETP, &ttyb) < 0) {
+ syslog(LOG_ERR,
+ "leitch_start: ioctl(%s, TIOCSETP): %m", leitchdev);
+ goto screwed;
+ }
+#if defined(LEITCHCLK)
+ if (ioctl(fd232, TIOCSETD, &ldisc) < 0) {
+ syslog(LOG_ERR,
+ "leitch_start: ioctl(%s, TIOCSETD): %m",leitchdev);
+ goto screwed;
+ }
+#endif /* LEITCHCLK */
+ }
+#endif /* HAVE_BSD_TTYS */
+
+ /*
+ * Set up the structures
+ */
+ leitch->peer = peer;
+ leitch->unit = unit;
+ leitch->state = STATE_IDLE;
+ leitch->fudge1 = 15; /* 15ms */
+
+ leitch->leitchio.clock_recv = leitch_receive;
+ leitch->leitchio.srcclock = (caddr_t) leitch;
+ leitch->leitchio.datalen = 0;
+ leitch->leitchio.fd = fd232;
+ if (!io_addclock(&leitch->leitchio)) {
+ goto screwed;
+ }
+
+ /*
+ * All done. Initialize a few random peer variables, then
+ * return success. Note that root delay and root dispersion are
+ * always zero for this clock.
+ */
+ peer->precision = 0;
+ peer->rootdelay = 0;
+ peer->rootdispersion = 0;
+ peer->stratum = stratumtouse[unit];
+ peer->refid = refid[unit];
+ unitinuse[unit] = 1;
+ return(1);
+
+ /*
+ * Something broke; abandon ship.
+ */
+screwed:
+ close(fd232);
+ return(0);
+}
+
+/*
+ * leitch_receive - receive data from the serial interface on a leitch
+ * clock
+ */
+static void
+leitch_receive(rbufp)
+ struct recvbuf *rbufp;
+{
+ struct leitchunit *leitch = (struct leitchunit *)rbufp->recv_srcclock;
+
+#ifdef DEBUG
+ if (debug)
+ fprintf(stderr, "leitch_recieve(%*.*s)\n",
+ rbufp->recv_length, rbufp->recv_length,
+ rbufp->recv_buffer);
+#endif
+ if (rbufp->recv_length != 7)
+ return; /* The date is return with a trailing newline,
+ discard it. */
+
+ switch (leitch->state) {
+ case STATE_IDLE: /* unexpected, discard and resync */
+ return;
+ case STATE_DATE:
+ if (!leitch_get_date(rbufp,leitch)) {
+ leitch->state = STATE_IDLE;
+ break;
+ }
+ leitch_send(leitch,"T\r");
+#ifdef DEBUG
+ if (debug)
+ fprintf(stderr, "%u\n",leitch->yearday);
+#endif
+ leitch->state = STATE_TIME1;
+ break;
+ case STATE_TIME1:
+ if (!leitch_get_time(rbufp,leitch,1)) {
+ }
+ if (!clocktime(leitch->yearday,leitch->hour,leitch->minute,
+ leitch->second, 0, rbufp->recv_time.l_ui,
+ &leitch->yearstart, &leitch->reftime1.l_ui)) {
+ leitch->state = STATE_IDLE;
+ break;
+ }
+#ifdef DEBUG
+ if (debug)
+ fprintf(stderr, "%u\n", leitch->reftime1.l_ui);
+#endif
+ MSUTOTSF(leitch->fudge1, leitch->reftime1.l_uf);
+ leitch->codetime1 = rbufp->recv_time;
+ leitch->state = STATE_TIME2;
+ break;
+ case STATE_TIME2:
+ if (!leitch_get_time(rbufp,leitch,2)) {
+ }
+ if (!clocktime(leitch->yearday,leitch->hour,leitch->minute,
+ leitch->second, 0, rbufp->recv_time.l_ui,
+ &leitch->yearstart, &leitch->reftime2.l_ui)) {
+ leitch->state = STATE_IDLE;
+ break;
+ }
+#ifdef DEBUG
+ if (debug)
+ fprintf(stderr, "%u\n", leitch->reftime2.l_ui);
+#endif
+ MSUTOTSF(leitch->fudge1, leitch->reftime2.l_uf);
+ leitch->codetime2 = rbufp->recv_time;
+ leitch->state = STATE_TIME3;
+ break;
+ case STATE_TIME3:
+ if (!leitch_get_time(rbufp,leitch,3)) {
+ }
+ if (!clocktime(leitch->yearday,leitch->hour,leitch->minute,
+ leitch->second, 0, rbufp->recv_time.l_ui,
+ &leitch->yearstart, &leitch->reftime3.l_ui)) {
+ leitch->state = STATE_IDLE;
+ break;
+ }
+#ifdef DEBUG
+ if (debug)
+ fprintf(stderr, "%u\n", leitch->reftime3.l_ui);
+#endif
+ MSUTOTSF(leitch->fudge1, leitch->reftime3.l_uf);
+ leitch->codetime3 = rbufp->recv_time;
+ leitch_process(leitch);
+ leitch->state = STATE_IDLE;
+ break;
+ default:
+ syslog(LOG_ERR,
+ "leitech_receive: invalid state %d unit %d",
+ leitch->state, leitch->unit);
+ }
+}
+
+/*
+ * leitch_process - process a pile of samples from the clock
+ *
+ * This routine uses a three-stage median filter to calculate offset and
+ * dispersion. reduce jitter. The dispersion is calculated as the span
+ * of the filter (max - min), unless the quality character (format 2) is
+ * non-blank, in which case the dispersion is calculated on the basis of
+ * the inherent tolerance of the internal radio oscillator, which is
+ * +-2e-5 according to the radio specifications.
+ */
+static void
+leitch_process(leitch)
+ struct leitchunit *leitch;
+{
+ l_fp off;
+ s_fp delay;
+ l_fp codetime;
+ l_fp tmp_fp;
+ int isinsync = 1;
+ u_fp dispersion = 10;
+
+ delay = 20;
+
+ codetime = leitch->codetime3;
+
+ off = leitch->reftime1;
+ L_SUB(&off,&leitch->codetime1);
+
+#ifdef DEBUG
+ if (debug)
+ fprintf(stderr,"%u %u %u %u %d %d\n",
+ leitch->codetime1.l_ui, leitch->codetime1.l_uf,
+ leitch->reftime1.l_ui, leitch->reftime1.l_uf,
+ off.l_ui, off.l_uf);
+#endif
+ tmp_fp = leitch->reftime2;
+ L_SUB(&tmp_fp,&leitch->codetime2);
+ if (L_ISGEQ(&off,&tmp_fp))
+ off = tmp_fp;
+#ifdef DEBUG
+ if (debug)
+ fprintf(stderr,"%u %u %u %u %d %d\n",
+ leitch->codetime2.l_ui, leitch->codetime2.l_uf,
+ leitch->reftime2.l_ui, leitch->reftime2.l_uf,
+ off.l_ui, off.l_uf);
+#endif
+ tmp_fp = leitch->reftime3;
+ L_SUB(&tmp_fp,&leitch->codetime3);
+
+ if (L_ISGEQ(&off,&tmp_fp))
+ off = tmp_fp;
+
+#ifdef DEBUG
+ if (debug)
+ fprintf(stderr,"%u %u %u %u %d %d\n",
+ leitch->codetime3.l_ui, leitch->codetime3.l_uf,
+ leitch->reftime3.l_ui, leitch->reftime3.l_uf,
+ off.l_ui, off.l_uf);
+#endif
+ refclock_receive(leitch->peer, &off, 0, dispersion, &codetime,
+ &codetime, isinsync);
+}
+
+/*
+ * leitch_timeout
+ */
+static void
+leitch_timeout(fp)
+ struct peer *fp;
+{
+
+#ifdef DEBUG
+ if (debug)
+ fprintf(stderr, "leitch_timeout()\n");
+#endif
+
+#ifdef NOTYET
+ { struct leitchunit *leitch = (struct leitchunit *)fp;
+
+ switch(leitch->state) {
+ case STATE_IDLE:
+ leitch_send(leitch,"D\r");
+ leitch->state = STATE_DATE;
+ break;
+ case STATE_DATE:
+ leitch_send(leitch,"T\r");
+ leitch->state = STATE_TIME1;
+ break;
+ case STATE_TIME1:
+ case STATE_TIME2:
+ case STATE_TIME3:
+ default:
+ break;
+ }
+
+ leitch->leitchtimer.event_time += 30;
+ TIMER_ENQUEUE(timerqueue, &leitch->leitchtimer);
+ }
+#endif /* NOTYET */
+}
+
+/*
+ * dysize
+ */
+static int
+dysize(year)
+int year;
+{
+ if (year%4) { /* not a potential leap year */
+ return (365);
+ } else {
+ if (year % 100) { /* is a leap year */
+ return (366);
+ } else {
+ if (year % 400) {
+ return (365);
+ } else {
+ return (366);
+ }
+ }
+ }
+}
+
+static int
+leitch_get_date(rbufp,leitch)
+ struct recvbuf *rbufp;
+ struct leitchunit *leitch;
+{
+ int i;
+
+ if (rbufp->recv_length < 6)
+ return(0);
+#define BAD(A) (rbufp->recv_buffer[A] < '0') || (rbufp->recv_buffer[A] > '9')
+ if (BAD(0)||BAD(1)||BAD(2)||BAD(3)||BAD(4)||BAD(5))
+ return(0);
+#define ATOB(A) ((rbufp->recv_buffer[A])-'0')
+ leitch->year = ATOB(0)*10 + ATOB(1);
+ leitch->month = ATOB(2)*10 + ATOB(3);
+ leitch->day = ATOB(4)*10 + ATOB(5);
+
+ /* sanity checks */
+ if (leitch->month > 12)
+ return(0);
+ if (leitch->day > days_in_month[leitch->month-1])
+ return(0);
+
+ /* calculate yearday */
+ i = 0;
+ leitch->yearday = leitch->day;
+
+ while ( i < (leitch->month-1) )
+ leitch->yearday += days_in_month[i++];
+
+ if ((dysize((leitch->year>90?1900:2000)+leitch->year)==365) &&
+ leitch->month > 2)
+ leitch->yearday--;
+
+ return(1);
+}
+
+/*
+ * leitch_get_time
+ */
+static int
+leitch_get_time(rbufp,leitch,which)
+ struct recvbuf *rbufp;
+ struct leitchunit *leitch;
+ int which;
+{
+ if (BAD(0)||BAD(1)||BAD(2)||BAD(3)||BAD(4)||BAD(5))
+ return(0);
+ leitch->hour = ATOB(0)*10 +ATOB(1);
+ leitch->minute = ATOB(2)*10 +ATOB(3);
+ leitch->second = ATOB(4)*10 +ATOB(5);
+
+ if ((leitch->hour > 23) || (leitch->minute > 60) ||
+ (leitch->second > 60))
+ return(0);
+ return(1);
+}
+
+#endif
diff --git a/usr.sbin/xntpd/xntpd/refclock_local.c b/usr.sbin/xntpd/xntpd/refclock_local.c
new file mode 100644
index 0000000..552e712
--- /dev/null
+++ b/usr.sbin/xntpd/xntpd/refclock_local.c
@@ -0,0 +1,170 @@
+/*
+ * refclock_local - local pseudo-clock driver
+ */
+#if defined(REFCLOCK) && defined(LOCAL_CLOCK)
+
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/time.h>
+
+#include "ntpd.h"
+#include "ntp_refclock.h"
+#include "ntp_stdlib.h"
+
+/*
+ * This is a hack to allow a machine to use its own system clock as a
+ * reference clock, i.e., to free-run using no outside clock discipline
+ * source. This is useful if you want to use NTP in an isolated
+ * environment with no radio clock or NIST modem available. Pick a
+ * machine that you figure has a good clock oscillator and configure it
+ * with this driver. Set the clock using the best means available, like
+ * eyeball-and-wristwatch. Then, point all the other machines at this
+ * one or use broadcast (not multicast) mode to distribute time.
+ *
+ * Another application for this driver is if you want to use a
+ * particular server's clock as the clock of last resort when all other
+ * normal synchronization sources have gone away. This is especially
+ * useful if that server has an ovenized oscillator. For this you would
+ * configure this driver at a higher stratum (say 3 or 4) to prevent the
+ * server's stratum from falling below that.
+ *
+ * A third application for this driver is when an external discipline
+ * source is available, such as the NIST "lockclock" program, which
+ * synchronizes the local clock via a telephone modem and the NIST
+ * Automated Computer Time Service (ACTS), or the Digital Time
+ * Synchronization Service (DTSS), which runs on DCE machines. In this
+ * case the stratum should be set at zero, indicating a bona fide
+ * stratum-1 source. Exercise some caution with this, since there is no
+ * easy way to telegraph via NTP that something might be wrong in the
+ * discipline source itself. In the case of DTSS, the local clock can
+ * have a rather large jitter, depending on the interval between
+ * corrections and the intrinsic frequency error of the clock
+ * oscillator. In extreme cases, this can cause clients to exceed the
+ * 128-ms slew window and drop off the NTP subnet.
+ *
+ * In the default mode the behavior of the clock selection algorithm is
+ * modified when this driver is in use. The algorithm is designed so
+ * that this driver will never be selected unless no other discipline
+ * source is available. This can be overriden with the prefer keyword of
+ * the server configuration command, in which case only this driver will
+ * be selected for synchronization and all other discipline sources will
+ * be ignored. This behavior is intended for use when an external
+ * discipline source controls the system clock.
+ *
+ * Fudge Factors
+ *
+ * The stratum for this driver LCLSTRATUM is set at 3 by default, but
+ * can be changed by the fudge command and/or the xntpdc utility. The
+ * reference ID is "LCL" by default, but can be changed using the same
+ * mechanisms. *NEVER* configure this driver to operate at a stratum
+ * which might possibly disrupt a client with access to a bona fide
+ * primary server, unless athe local clock oscillator is reliably
+ * disciplined by another source. *NEVER NEVER* configure a server which
+ * might devolve to an undisciplined local clock to use multicast mode.
+ *
+ * This driver provides a mechanism to trim the local clock in both time
+ * and frequency, as well as a way to manipulate the leap bits. The
+ * fudge time1 parameter adjusts the time, in seconds, and the fudge
+ * time2 parameter adjusts the frequency, in ppm. Both parameters are
+ * additive; that is, they add increments in time or frequency to the
+ * present values. The fudge flag1 and fudge flag2 bits set the
+ * corresponding leap bits; for example, setting flag1 causes a leap
+ * second to be added at the end of the UTC day. These bits are not
+ * reset automatically when the leap takes place; they must be turned
+ * off manually after the leap event.
+ */
+
+/*
+ * Local interface definitions
+ */
+#define PRECISION (-7) /* about 10 ms precision */
+#define REFID "LCL\0" /* reference ID */
+#define DESCRIPTION "Undisciplined local clock" /* WRU */
+
+#define STRATUM 3 /* default stratum */
+#define DISPERSION (FP_SECOND / 100) /* default dispersion (10 ms) */
+
+/*
+ * Imported from the timer module
+ */
+extern u_long current_time;
+
+/*
+ * Imported from ntp_proto
+ */
+extern s_char sys_precision;
+
+/*
+ * Function prototypes
+ */
+static int local_start P((int, struct peer *));
+static void local_poll P((int, struct peer *));
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_local = {
+ local_start, /* start up driver */
+ noentry, /* shut down driver (not used) */
+ local_poll, /* transmit poll message */
+ noentry, /* not used (old lcl_control) */
+ noentry, /* initialize driver (not used) */
+ noentry, /* not used (old lcl_buginfo) */
+ NOFLAGS /* not used */
+};
+
+
+/*
+ * local_start - start up the clock
+ */
+static int
+local_start(unit, peer)
+ int unit;
+ struct peer *peer;
+{
+ register struct refclockproc *pp;
+
+ pp = peer->procptr;
+
+ /*
+ * Initialize miscellaneous variables
+ */
+ peer->precision = sys_precision;
+ pp->clockdesc = DESCRIPTION;
+ peer->stratum = STRATUM;
+ memcpy((char *)&pp->refid, REFID, 4);
+ return (1);
+}
+
+
+/*
+ * local_poll - called by the transmit procedure
+ */
+static void
+local_poll(unit, peer)
+ int unit;
+ struct peer *peer;
+{
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ pp->polls++;
+ pp->lasttime = current_time;
+
+ /*
+ * Ramble through the usual filtering and grooming code, which
+ * is essentially a no-op and included mostly for pretty
+ * billboards. We fudge flags as the leap indicators and allow a
+ * one-time adjustment in time using fudge time1 (s) and
+ * frequency using fudge time 2 (ppm).
+ */
+ pp->dispersion = DISPERSION;
+ gettstamp(&pp->lastrec);
+ refclock_receive(peer, &pp->fudgetime1, 0, pp->dispersion,
+ &pp->lastrec, &pp->lastrec, pp->sloppyclockflag);
+ adj_frequency(LFPTOFP(&pp->fudgetime2));
+ L_CLR(&pp->fudgetime1);
+ L_CLR(&pp->fudgetime2);
+}
+
+#endif /* REFCLOCK */
diff --git a/usr.sbin/xntpd/xntpd/refclock_moto.c b/usr.sbin/xntpd/xntpd/refclock_moto.c
new file mode 100644
index 0000000..2e888bc
--- /dev/null
+++ b/usr.sbin/xntpd/xntpd/refclock_moto.c
@@ -0,0 +1,2 @@
+#if defined(REFCLOCK) && defined(NMEA)
+#endif
diff --git a/usr.sbin/xntpd/xntpd/refclock_msfees.c b/usr.sbin/xntpd/xntpd/refclock_msfees.c
new file mode 100644
index 0000000..17e4235
--- /dev/null
+++ b/usr.sbin/xntpd/xntpd/refclock_msfees.c
@@ -0,0 +1,1557 @@
+/* refclock_ees - clock driver for the EES M201 receiver */
+
+#if defined(REFCLOCK) && defined(MSFEESPPS) && defined(STREAM)
+
+/* Currently REQUIRES STREAM and PPSCD. CLK and CBREAK modes
+ * were removed as the code was overly hairy, they weren't in use
+ * (hence probably didn't work). Still in RCS file at cl.cam.ac.uk
+ */
+
+#include <ctype.h>
+#include <sys/time.h>
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_unixtime.h"
+#include "ntp_calendar.h"
+#if defined(HAVE_BSD_TTYS)
+#include <sgtty.h>
+#endif /* HAVE_BSD_TTYS */
+#if defined(HAVE_SYSV_TTYS)
+#include <termio.h>
+#endif /* HAVE_SYSV_TTYS */
+#include <termios.h>
+#include <stropts.h>
+#include <sys/ppsclock.h>
+#include "ntp_stdlib.h"
+
+ /*
+ fudgefactor = fudgetime1;
+ os_delay = fudgetime2;
+ offset_fudge = os_delay + fudgefactor + inherent_delay;
+ stratumtouse = fudgeval1 & 0xf
+ debug = fudgeval2;
+ sloppyclockflag = flags & CLK_FLAG1;
+ 1 log smoothing summary when processing sample
+ 4 dump the buffer from the clock
+ 8 EIOGETKD the last n uS time stamps
+ if (flags & CLK_FLAG2 && unitinuse) ees->leaphold = 0;
+ ees->dump_vals = flags & CLK_FLAG3;
+ ees->usealldata = flags & CLK_FLAG4;
+
+
+ bug->values[0] = (ees->lasttime) ? current_time - ees->lasttime : 0;
+ bug->values[1] = (ees->clocklastgood)?current_time-ees->clocklastgood:0;
+ bug->values[2] = (u_long)ees->status;
+ bug->values[3] = (u_long)ees->lastevent;
+ bug->values[4] = (u_long)ees->reason;
+ bug->values[5] = (u_long)ees->nsamples;
+ bug->values[6] = (u_long)ees->codestate;
+ bug->values[7] = (u_long)ees->day;
+ bug->values[8] = (u_long)ees->hour;
+ bug->values[9] = (u_long)ees->minute;
+ bug->values[10] = (u_long)ees->second;
+ bug->values[11] = (u_long)ees->tz;
+ bug->values[12] = ees->yearstart;
+ bug->values[13] = (ees->leaphold > current_time) ?
+ ees->leaphold - current_time : 0;
+ bug->values[14] = inherent_delay[unit].l_uf;
+ bug->values[15] = offset_fudge[unit].l_uf;
+
+ bug->times[0] = ees->reftime;
+ bug->times[1] = ees->arrvtime;
+ bug->times[2] = ees->lastsampletime;
+ bug->times[3] = ees->offset;
+ bug->times[4] = ees->lowoffset;
+ bug->times[5] = ees->highoffset;
+ bug->times[6] = inherent_delay[unit];
+ bug->times[8] = os_delay[unit];
+ bug->times[7] = fudgefactor[unit];
+ bug->times[9] = offset_fudge[unit];
+ bug->times[10]= ees->yearstart, 0;
+ */
+
+/* This should support the use of an EES M201 receiver with RS232
+ * output (modified to transmit time once per second).
+ *
+ * For the format of the message sent by the clock, see the EESM_
+ * definitions below.
+ *
+ * It appears to run free for an integral number of minutes, until the error
+ * reaches 4mS, at which point it steps at second = 01.
+ * It appears that sometimes it steps 4mS (say at 7 min interval),
+ * then the next minute it decides that it was an error, so steps back.
+ * On the next minute it steps forward again :-(
+ * This is typically 16.5uS/S then 3975uS at the 4min re-sync,
+ * or 9.5uS/S then 3990.5uS at a 7min re-sync,
+ * at which point it may loose the "00" second time stamp.
+ * I assume that the most accurate time is just AFTER the re-sync.
+ * Hence remember the last cycle interval,
+ *
+ * Can run in any one of:
+ *
+ * PPSCD PPS signal sets CD which interupts, and grabs the current TOD
+ * (sun) *in the interupt code*, so as to avoid problems with
+ * the STREAMS scheduling.
+ *
+ * It appears that it goes 16.5 uS slow each second, then every 4 mins it
+ * generates no "00" second tick, and gains 3975 uS. Ho Hum ! (93/2/7)
+ */
+
+/* Definitions */
+#ifndef MAXUNITS
+#define MAXUNITS 4 /* maximum number of EES units permitted */
+#endif
+
+#ifndef EES232
+#define EES232 "/dev/ees%d" /* Device to open to read the data */
+#endif
+
+/* Other constant stuff */
+#ifndef EESPRECISION
+#define EESPRECISION (-10) /* what the heck - 2**-10 = 1ms */
+#endif
+#ifndef EESREFID
+#define EESREFID "MSF\0" /* String to identify the clock */
+#endif
+#ifndef EESHSREFID
+#define EESHSREFID (0x7f7f0000 | ((REFCLK_MSF_EES) << 8)) /* Numeric refid */
+#endif
+
+/* Description of clock */
+#define EESDESCRIPTION "EES M201 MSF Receiver"
+
+/* Speed we run the clock port at. If this is changed the UARTDELAY
+ * value should be recomputed to suit.
+ */
+#ifndef SPEED232
+#define SPEED232 B9600 /* 9600 baud */
+#endif
+
+/* What is the inherent delay for this mode of working, i.e. when is the
+ * data time stamped.
+ */
+#define SAFETY_SHIFT 10 /* Split the shift to avoid overflow */
+#define BITS_TO_L_FP(bits, baud) \
+ (((((bits)*2 +1) << (FRACTION_PREC-SAFETY_SHIFT)) / (2*baud)) << SAFETY_SHIFT)
+#define INH_DELAY_CBREAK BITS_TO_L_FP(119, 9600)
+#define INH_DELAY_PPS BITS_TO_L_FP( 0, 9600)
+
+#ifndef STREAM_PP1
+#define STREAM_PP1 "ppsclocd\0<-- patch space for module name1 -->"
+#endif
+#ifndef STREAM_PP2
+#define STREAM_PP2 "ppsclock\0<-- patch space for module name2 -->"
+#endif
+
+/* Offsets of the bytes of the serial line code. The clock gives
+ * local time with a GMT/BST indication. The EESM_ definitions
+ * give offsets into ees->lastcode.
+ */
+#define EESM_CSEC 0 /* centiseconds - always zero in our clock */
+#define EESM_SEC 1 /* seconds in BCD */
+#define EESM_MIN 2 /* minutes in BCD */
+#define EESM_HOUR 3 /* hours in BCD */
+#define EESM_DAYWK 4 /* day of week (Sun = 0 etc) */
+#define EESM_DAY 5 /* day of month in BCD */
+#define EESM_MON 6 /* month in BCD */
+#define EESM_YEAR 7 /* year MOD 100 in BCD */
+#define EESM_LEAP 8 /* 0x0f if leap year, otherwise zero */
+#define EESM_BST 9 /* 0x03 if BST, 0x00 if GMT */
+#define EESM_MSFOK 10 /* 0x3f if radio good, otherwise zero */
+ /* followed by a frame alignment byte (0xff) /
+ / which is not put into the lastcode buffer*/
+
+/* Length of the serial time code, in characters. The first length
+ * is less the frame alignment byte.
+ */
+#define LENEESPRT (EESM_MSFOK+1)
+#define LENEESCODE (LENEESPRT+1)
+
+/* Code state. */
+#define EESCS_WAIT 0 /* waiting for start of timecode */
+#define EESCS_GOTSOME 1 /* have an incomplete time code buffered */
+
+/* Default fudge factor and character to receive */
+#define DEFFUDGETIME 0 /* Default user supplied fudge factor */
+#ifndef DEFOSTIME
+#define DEFOSTIME 0 /* Default OS delay -- passed by Make ? */
+#endif
+#define DEFINHTIME INH_DELAY_PPS /* inherent delay due to sample point*/
+
+/* Limits on things. Reduce the number of samples to SAMPLEREDUCE by median
+ * elimination. If we're running with an accurate clock, chose the BESTSAMPLE
+ * as the estimated offset, otherwise average the remainder.
+ */
+#define FULLSHIFT 6 /* NCODES root 2 */
+#define NCODES (1<< FULLSHIFT) /* 64 */
+#define REDUCESHIFT (FULLSHIFT -1) /* SAMPLEREDUCE root 2 */
+
+/* Towards the high ( Why ?) end of half */
+#define BESTSAMPLE ((samplereduce * 3) /4) /* 24 */
+
+/* Leap hold time. After a leap second the clock will no longer be
+ * reliable until it resynchronizes. Hope 40 minutes is enough. */
+#define EESLEAPHOLD (40 * 60)
+
+#define EES_STEP_F (1 << 24) /* the receiver steps in units of about 4ms */
+#define EES_STEP_F_GRACE (EES_STEP_F/8) /*Allow for slop of 1/8 which is .5ms*/
+#define EES_STEP_NOTE (1 << 21)/* Log any unexpected jumps, say .5 ms .... */
+#define EES_STEP_NOTES 50 /* Only do a limited number */
+#define MAX_STEP 16 /* Max number of steps to remember */
+
+/* debug is a bit mask of debugging that is wanted */
+#define DB_SYSLOG_SMPLI 0x0001
+#define DB_SYSLOG_SMPLE 0x0002
+#define DB_SYSLOG_SMTHI 0x0004
+#define DB_SYSLOG_NSMTHE 0x0008
+#define DB_SYSLOG_NSMTHI 0x0010
+#define DB_SYSLOG_SMTHE 0x0020
+#define DB_PRINT_EV 0x0040
+#define DB_PRINT_CDT 0x0080
+#define DB_PRINT_CDTC 0x0100
+#define DB_SYSLOG_KEEPD 0x0800
+#define DB_SYSLOG_KEEPE 0x1000
+#define DB_LOG_DELTAS 0x2000
+#define DB_PRINT_DELTAS 0x4000
+#define DB_LOG_AWAITMORE 0x8000
+#define DB_LOG_SAMPLES 0x10000
+#define DB_NO_PPS 0x20000
+#define DB_INC_PPS 0x40000
+#define DB_DUMP_DELTAS 0x80000
+
+struct eesunit { /* EES unit control structure. */
+ struct peer *peer; /* associated peer structure */
+ struct refclockio io; /* given to the I/O handler */
+ l_fp reftime; /* reference time */
+ l_fp lastsampletime; /* time as in txt from last EES msg */
+ l_fp arrvtime; /* Time at which pkt arrived */
+ l_fp codeoffsets[NCODES]; /* the time of arrival of 232 codes */
+ l_fp offset; /* chosen offset (for clkbug) */
+ l_fp lowoffset; /* lowest sample offset (for clkbug) */
+ l_fp highoffset; /* highest " " (for clkbug) */
+ char lastcode[LENEESCODE+6]; /* last time code we received */
+ u_long lasttime; /* last time clock heard from */
+ u_long clocklastgood; /* last time good radio seen */
+ u_char lencode; /* length of code in buffer */
+ u_char nsamples; /* number of samples we've collected */
+ u_char codestate; /* state of 232 code reception */
+ u_char unit; /* unit number for this guy */
+ u_char status; /* clock status */
+ u_char lastevent; /* last clock event */
+ u_char reason; /* reason for last abort */
+ u_char hour; /* hour of day */
+ u_char minute; /* minute of hour */
+ u_char second; /* seconds of minute */
+ char tz; /* timezone from clock */
+ u_char ttytype; /* method used */
+ u_char dump_vals; /* Should clock values be dumped */
+ u_char usealldata; /* Use ALL samples */
+ u_short day; /* day of year from last code */
+ u_long yearstart; /* start of current year */
+ u_long leaphold; /* time of leap hold expiry */
+ u_long badformat; /* number of bad format codes */
+ u_long baddata; /* number of invalid time codes */
+ u_long timestarted; /* time we started this */
+ long last_pps_no; /* The serial # of the last PPS */
+ char fix_pending; /* Is a "sync to time" pending ? */
+ /* Fine tuning - compensate for 4 mS ramping .... */
+ l_fp last_l; /* last time stamp */
+ u_char last_steps[MAX_STEP]; /* Most recent n steps */
+ int best_av_step; /* Best guess at average step */
+ char best_av_step_count; /* # of steps over used above */
+ char this_step; /* Current pos in buffer */
+ int last_step_late; /* How late the last step was (0-59) */
+ long jump_fsecs; /* # of fractions of a sec last jump */
+ u_long last_step; /* time of last step */
+ int last_step_secs; /* Number of seconds in last step */
+ int using_ramp; /* 1 -> noemal, -1 -> over stepped */
+};
+#define last_sec last_l.l_ui
+#define last_sfsec last_l.l_f
+#define this_uisec ((ees->arrvtime).l_ui)
+#define this_sfsec ((ees->arrvtime).l_f)
+#define msec(x) ((x) / (1<<22))
+#define LAST_STEPS (sizeof ees->last_steps / sizeof ees->last_steps[0])
+#define subms(x) ((((((x < 0) ? (-(x)) : (x)) % (1<<22))/2) * 625) / (1<<(22 -5)))
+
+/* Bitmask for what methods to try to use -- currently only PPS enabled */
+#define T_CBREAK 1
+#define T_PPS 8
+/* macros to test above */
+#define is_cbreak(x) ((x)->ttytype & T_CBREAK)
+#define is_pps(x) ((x)->ttytype & T_PPS)
+#define is_any(x) ((x)->ttytype)
+
+#define CODEREASON 20 /* reason codes */
+
+/* Data space for the unit structures. Note that we allocate these on
+ * the fly, but never give them back. */
+static struct eesunit *eesunits[MAXUNITS];
+static u_char unitinuse[MAXUNITS];
+
+/* Keep the fudge factors separately so they can be set even
+ * when no clock is configured. */
+static l_fp inherent_delay[MAXUNITS]; /* when time stamp is taken */
+static l_fp fudgefactor[MAXUNITS]; /* fudgetime1 */
+static l_fp os_delay[MAXUNITS]; /* fudgetime2 */
+static l_fp offset_fudge[MAXUNITS]; /* Sum of above */
+static u_char stratumtouse[MAXUNITS];
+static u_char sloppyclockflag[MAXUNITS];
+
+static int deltas[60];
+
+static l_fp acceptable_slop; /* = { 0, 1 << (FRACTION_PREC -2) }; */
+static l_fp onesec; /* = { 1, 0 }; */
+
+/* Imported from the timer module */
+extern u_long current_time;
+
+extern s_char sys_precision;
+
+#ifdef DEBUG
+static int debug;
+#endif
+
+#ifndef DUMP_BUF_SIZE /* Size of buffer to be used by dump_buf */
+#define DUMP_BUF_SIZE 10112
+#endif
+
+/* ees_reset - reset the count back to zero */
+#define ees_reset(ees) (ees)->nsamples = 0; \
+ (ees)->codestate = EESCS_WAIT
+
+/* ees_event - record and report an event */
+#define ees_event(ees, evcode) if ((ees)->status != (u_char)(evcode)) \
+ ees_report_event((ees), (evcode))
+
+/* Find the precision of the system clock by reading it */
+#define USECS 1000000
+#define MINSTEP 5 /* some systems increment uS on each call */
+#define MAXLOOPS (USECS/9)
+
+static void dump_buf(coffs, from, to, text)
+l_fp *coffs;
+int from;
+int to;
+char *text;
+{
+ char buff[DUMP_BUF_SIZE + 80];
+ int i;
+ register char *ptr = buff;
+ sprintf(ptr, text);
+ for (i=from; i<to; i++)
+ { while (*ptr) ptr++;
+ if ((ptr-buff) > DUMP_BUF_SIZE) syslog(LOG_DEBUG, "D: %s", ptr=buff);
+ sprintf(ptr, " %06d", ((int)coffs[i].l_f) / 4295);
+ }
+ syslog(LOG_DEBUG, "D: %s", buff);
+}
+
+/* msfees_init - initialize internal ees driver data */
+static void msfees_init()
+{
+ register int i;
+ /* Just zero the data arrays */
+ memset((char *)eesunits, 0, sizeof eesunits);
+ memset((char *)unitinuse, 0, sizeof unitinuse);
+
+ acceptable_slop.l_ui = 0;
+ acceptable_slop.l_uf = 1 << (FRACTION_PREC -2);
+
+ onesec.l_ui = 1;
+ onesec.l_uf = 0;
+
+ /* Initialize fudge factors to default. */
+ for (i = 0; i < MAXUNITS; i++) {
+ fudgefactor[i].l_ui = 0;
+ fudgefactor[i].l_uf = DEFFUDGETIME;
+ os_delay[i].l_ui = 0;
+ os_delay[i].l_uf = DEFOSTIME;
+ inherent_delay[i].l_ui = 0;
+ inherent_delay[i].l_uf = DEFINHTIME;
+ offset_fudge[i] = os_delay[i];
+ L_ADD(&offset_fudge[i], &fudgefactor[i]);
+ L_ADD(&offset_fudge[i], &inherent_delay[i]);
+ stratumtouse[i] = 0;
+ sloppyclockflag[i] = 0;
+ }
+}
+
+
+/* msfees_start - open the EES devices and initialize data for processing */
+static int msfees_start(unit, peer)
+ int unit;
+ struct peer *peer;
+{
+ register struct eesunit *ees;
+ register int i;
+ int fd232 = -1;
+ char eesdev[20];
+ struct termios ttyb, *ttyp;
+ static void ees_receive();
+ extern int io_addclock();
+ extern void io_closeclock();
+ extern char *emalloc();
+ struct refclockproc *pp;
+ pp = peer->procptr;
+
+ if (unit >= MAXUNITS) {
+ syslog(LOG_ERR, "ees clock: unit number %d invalid (max %d)",
+ unit, MAXUNITS-1);
+ return 0;
+ }
+ if (unitinuse[unit]) {
+ syslog(LOG_ERR, "ees clock: unit number %d in use", unit);
+ return 0;
+ }
+
+ /* Unit okay, attempt to open the devices. We do them both at
+ * once to make sure we can */
+ (void) sprintf(eesdev, EES232, unit);
+
+ fd232 = open(eesdev, O_RDWR, 0777);
+ if (fd232 == -1) {
+ syslog(LOG_ERR, "ees clock: open of %s failed: %m", eesdev);
+ return 0;
+ }
+
+#ifdef TIOCEXCL
+ /* Set for exclusive use */
+ if (ioctl(fd232, TIOCEXCL, (char *)0) < 0) {
+ syslog(LOG_ERR, "ees clock: ioctl(%s, TIOCEXCL): %m", eesdev);
+ goto screwed;
+ }
+#endif
+
+ /* STRIPPED DOWN VERSION: Only PPS CD is supported at the moment */
+
+ /* Set port characteristics. If we don't have a STREAMS module or
+ * a clock line discipline, cooked mode is just usable, even though it
+ * strips the top bit. The only EES byte which uses the top
+ * bit is the year, and we don't use that anyway. If we do
+ * have the line discipline, we choose raw mode, and the
+ * line discipline code will block up the messages.
+ */
+
+ /* STIPPED DOWN VERSION: Only PPS CD is supported at the moment */
+
+ ttyp = &ttyb;
+ if (tcgetattr(fd232, ttyp) < 0) {
+ syslog(LOG_ERR, "msfees_start: tcgetattr(%s): %m", eesdev);
+ goto screwed;
+ }
+
+ ttyp->c_iflag = IGNBRK|IGNPAR|ICRNL;
+ ttyp->c_cflag = SPEED232|CS8|CLOCAL|CREAD;
+ ttyp->c_oflag = 0;
+ ttyp->c_lflag = ICANON;
+ ttyp->c_cc[VERASE] = ttyp->c_cc[VKILL] = '\0';
+ if (tcsetattr(fd232, TCSANOW, ttyp) < 0) {
+ syslog(LOG_ERR, "msfees_start: tcsetattr(%s): %m", eesdev);
+ goto screwed;
+ }
+
+ if (tcflush(fd232, TCIOFLUSH) < 0) {
+ syslog(LOG_ERR, "msfees_start: tcflush(%s): %m", eesdev);
+ goto screwed;
+ }
+
+ inherent_delay[unit].l_uf = INH_DELAY_PPS;
+
+ /* offset fudge (how *late* the timestamp is) = fudge + os delays */
+ offset_fudge[unit] = os_delay[unit];
+ L_ADD(&offset_fudge[unit], &fudgefactor[unit]);
+ L_ADD(&offset_fudge[unit], &inherent_delay[unit]);
+
+ /* Looks like this might succeed. Find memory for the structure.
+ * Look to see if there are any unused ones, if not we malloc() one.
+ */
+ if (eesunits[unit] != 0) /* The one we want is okay */
+ ees = eesunits[unit];
+ else {
+ /* Look for an unused, but allocated struct */
+ for (i = 0; i < MAXUNITS; i++) {
+ if (!unitinuse[i] && eesunits[i] != 0)
+ break;
+ }
+
+ if (i < MAXUNITS) { /* Reclaim this one */
+ ees = eesunits[i];
+ eesunits[i] = 0;
+ } /* no spare -- make a new one */
+ else ees = (struct eesunit *) emalloc(sizeof(struct eesunit));
+ }
+ memset((char *)ees, 0, sizeof(struct eesunit));
+ eesunits[unit] = ees;
+
+ /* Set up the structures */
+ ees->peer = peer;
+ ees->unit = (u_char)unit;
+ ees->timestarted= current_time;
+ ees->ttytype = 0;
+ ees->io.clock_recv= ees_receive;
+ ees->io.srcclock= (caddr_t)ees;
+ ees->io.datalen = 0;
+ ees->io.fd = fd232;
+
+ /* Okay. Push one of the two (linked into the kernel, or dynamically
+ * loaded) STREAMS module, and give it to the I/O code to start
+ * receiving stuff.
+ */
+
+ {
+ int rc1;
+ /* Pop any existing onews first ... */
+ while (ioctl(fd232, I_POP, 0 ) >= 0) ;
+
+ /* Now try pushing either of the possible modules */
+ if ((rc1=ioctl(fd232, I_PUSH, STREAM_PP1)) < 0 &&
+ ioctl(fd232, I_PUSH, STREAM_PP2) < 0) {
+ syslog(LOG_ERR,
+ "ees clock: Push of `%s' and `%s' to %s failed %m",
+ STREAM_PP1, STREAM_PP2, eesdev);
+ goto screwed;
+ }
+ else {
+ syslog(LOG_INFO, "I: ees clock: PUSHed %s on %s",
+ (rc1 >= 0) ? STREAM_PP1 : STREAM_PP2, eesdev);
+ ees->ttytype |= T_PPS;
+ }
+ }
+
+ /* Add the clock */
+ if (!io_addclock(&ees->io)) {
+ /* Oh shit. Just close and return. */
+ syslog(LOG_ERR, "ees clock: io_addclock(%s): %m", eesdev);
+ goto screwed;
+ }
+
+
+ /* All done. Initialize a few random peer variables, then
+ * return success. */
+ peer->precision = sys_precision;
+ peer->stratum = stratumtouse[unit];
+ peer->rootdelay = 0; /* ++++ */
+ peer->rootdispersion = 0; /* ++++ */
+ if (stratumtouse[unit] <= 1) {
+ memcpy((char *)&pp->refid, EESREFID, 4);
+ if (unit > 0 && unit < 10)
+ ((char *)&pp->refid)[3] = '0' + unit;
+ } else {
+ peer->refid = htonl(EESHSREFID);
+ }
+ unitinuse[unit] = 1;
+ pp->unitptr = (caddr_t) &eesunits[unit];
+ pp->clockdesc = EESDESCRIPTION;
+ pp->nstages = MAXSTAGE;
+ syslog(LOG_ERR, "ees clock: %s OK on %d", eesdev, unit);
+ return (1);
+
+screwed:
+ if (fd232 != -1)
+ (void) close(fd232);
+ return (0);
+}
+
+
+/* msfees_shutdown - shut down a EES clock */
+static void msfees_shutdown(unit, peer)
+ int unit;
+ struct peer *peer;
+{
+ register struct eesunit *ees;
+ extern void io_closeclock();
+
+ if (unit >= MAXUNITS) {
+ syslog(LOG_ERR,
+ "ees clock: INTERNAL ERROR, unit number %d invalid (max %d)",
+ unit, MAXUNITS);
+ return;
+ }
+ if (!unitinuse[unit]) {
+ syslog(LOG_ERR,
+ "ees clock: INTERNAL ERROR, unit number %d not in use", unit);
+ return;
+ }
+
+ /* Tell the I/O module to turn us off. We're history. */
+ ees = eesunits[unit];
+ io_closeclock(&ees->io);
+ unitinuse[unit] = 0;
+}
+
+
+/* ees_report_event - note the occurance of an event */
+static void ees_report_event(ees, code)
+ struct eesunit *ees;
+ int code;
+{
+ if (ees->status != (u_char)code) {
+ ees->status = (u_char)code;
+ if (code != CEVNT_NOMINAL)
+ ees->lastevent = (u_char)code;
+ /* Should report event to trap handler in here.
+ * Soon...
+ */
+ }
+}
+
+
+/* ees_receive - receive data from the serial interface on an EES clock */
+static void ees_receive(rbufp)
+ struct recvbuf *rbufp;
+{
+ register int n_sample;
+ register int day;
+ register struct eesunit *ees;
+ register u_char *dpt; /* Data PoinTeR: move along ... */
+ register u_char *dpend; /* Points just *after* last data char */
+ register char *cp;
+ l_fp tmp;
+ static void ees_process();
+ int call_pps_sample = 0;
+ l_fp pps_arrvstamp;
+ int sincelast;
+ int pps_step = 0;
+ int suspect_4ms_step = 0;
+ struct ppsclockev ppsclockev;
+ long *ptr = (long *) &ppsclockev;
+ extern errno;
+ int rc;
+
+ /* Get the clock this applies to and a pointer to the data */
+ ees = (struct eesunit *)rbufp->recv_srcclock;
+ dpt = (u_char *)&rbufp->recv_space;
+ dpend = dpt + rbufp->recv_length;
+ if ((debug & DB_LOG_AWAITMORE) && (rbufp->recv_length != LENEESCODE))
+ printf("[%d] ", rbufp->recv_length);
+
+ /* Check out our state and process appropriately */
+ switch (ees->codestate) {
+ case EESCS_WAIT:
+ /* Set an initial guess at the timestamp as the recv time.
+ * If just running in CBREAK mode, we can't improve this.
+ * If we have the CLOCK Line Discipline, PPSCD, or sime such,
+ * then we will do better later ....
+ */
+ ees->arrvtime = rbufp->recv_time;
+ ees->codestate = EESCS_GOTSOME;
+ ees->lencode = 0;
+ /*FALLSTHROUGH*/
+
+ case EESCS_GOTSOME:
+ cp = &(ees->lastcode[ees->lencode]);
+
+ /* Gobble the bytes until the final (possibly stripped) 0xff */
+ while (dpt < dpend && (*dpt & 0x7f) != 0x7f) {
+ *cp++ = (char)*dpt++;
+ ees->lencode++;
+ /* Oh dear -- too many bytes .. */
+ if (ees->lencode > LENEESPRT) {
+ syslog(LOG_INFO,
+"I: ees clock: %d + %d > %d [%02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x]",
+ ees->lencode, dpend - dpt, LENEESPRT,
+#define D(x) (ees->lastcode[x])
+ D(0), D(1), D(2), D(3), D(4), D(5), D(6),
+ D(7), D(8), D(9), D(10), D(11), D(12));
+#undef D
+ ees->badformat++;
+ ees->reason = CODEREASON + 1;
+ ees_event(ees, CEVNT_BADREPLY);
+ ees_reset(ees);
+ return;
+ }
+ }
+ /* Gave up because it was end of the buffer, rather than ff */
+ if (dpt == dpend) {
+ /* Incomplete. Wait for more. */
+ if (debug & DB_LOG_AWAITMORE) syslog(LOG_INFO,
+ "I: ees clock %d: %d == %d: await more",
+ ees->unit, dpt, dpend);
+ return;
+ }
+
+ /* This shouldn't happen ... ! */
+ if ((*dpt & 0x7f) != 0x7f) {
+ syslog(LOG_INFO, "I: ees clock: %0x & 0x7f != 0x7f", *dpt);
+ ees->badformat++;
+ ees->reason = CODEREASON + 2;
+ ees_event(ees, CEVNT_BADREPLY);
+ ees_reset(ees);
+ return;
+ }
+
+ /* Skip the 0xff */
+ dpt++;
+
+ /* Finally, got a complete buffer. Mainline code will
+ * continue on. */
+ cp = ees->lastcode;
+ break;
+
+ default:
+ syslog(LOG_ERR, "ees clock: INTERNAL ERROR: %d state %d",
+ ees->unit, ees->codestate);
+ ees->reason = CODEREASON + 5;
+ ees_event(ees, CEVNT_FAULT);
+ ees_reset(ees);
+ return;
+ }
+
+ /* Boy! After all that crap, the lastcode buffer now contains
+ * something we hope will be a valid time code. Do length
+ * checks and sanity checks on constant data.
+ */
+ ees->codestate = EESCS_WAIT;
+ ees->lasttime = current_time;
+ if (ees->lencode != LENEESPRT) {
+ ees->badformat++;
+ ees->reason = CODEREASON + 6;
+ ees_event(ees, CEVNT_BADREPLY);
+ ees_reset(ees);
+ return;
+ }
+
+ cp = ees->lastcode;
+
+ /* Check that centisecond is zero */
+ if (cp[EESM_CSEC] != 0) {
+ ees->baddata++;
+ ees->reason = CODEREASON + 7;
+ ees_event(ees, CEVNT_BADREPLY);
+ ees_reset(ees);
+ return;
+ }
+
+ /* Check flag formats */
+ if (cp[EESM_LEAP] != 0 && cp[EESM_LEAP] != 0x0f) {
+ ees->badformat++;
+ ees->reason = CODEREASON + 8;
+ ees_event(ees, CEVNT_BADREPLY);
+ ees_reset(ees);
+ return;
+ }
+
+ if (cp[EESM_BST] != 0 && cp[EESM_BST] != 0x03) {
+ ees->badformat++;
+ ees->reason = CODEREASON + 9;
+ ees_event(ees, CEVNT_BADREPLY);
+ ees_reset(ees);
+ return;
+ }
+
+ if (cp[EESM_MSFOK] != 0 && cp[EESM_MSFOK] != 0x3f) {
+ ees->badformat++;
+ ees->reason = CODEREASON + 10;
+ ees_event(ees, CEVNT_BADREPLY);
+ ees_reset(ees);
+ return;
+ }
+
+ /* So far, so good. Compute day, hours, minutes, seconds,
+ * time zone. Do range checks on these.
+ */
+
+#define bcdunpack(val) ( (((val)>>4) & 0x0f) * 10 + ((val) & 0x0f) )
+#define istrue(x) ((x)?1:0)
+
+ ees->second = bcdunpack(cp[EESM_SEC]); /* second */
+ ees->minute = bcdunpack(cp[EESM_MIN]); /* minute */
+ ees->hour = bcdunpack(cp[EESM_HOUR]); /* hour */
+
+ day = bcdunpack(cp[EESM_DAY]); /* day of month */
+
+ switch (bcdunpack(cp[EESM_MON])) { /* month */
+
+ /* Add in lengths of all previous months. Add one more
+ if it is a leap year and after February.
+ */
+ case 12: day += NOV; /*FALLSTHROUGH*/
+ case 11: day += OCT; /*FALLSTHROUGH*/
+ case 10: day += SEP; /*FALLSTHROUGH*/
+ case 9: day += AUG; /*FALLSTHROUGH*/
+ case 8: day += JUL; /*FALLSTHROUGH*/
+ case 7: day += JUN; /*FALLSTHROUGH*/
+ case 6: day += MAY; /*FALLSTHROUGH*/
+ case 5: day += APR; /*FALLSTHROUGH*/
+ case 4: day += MAR; /*FALLSTHROUGH*/
+ case 3: day += FEB;
+ if (istrue(cp[EESM_LEAP])) day++; /*FALLSTHROUGH*/
+ case 2: day += JAN; /*FALLSTHROUGH*/
+ case 1: break;
+ default: ees->baddata++;
+ ees->reason = CODEREASON + 11;
+ ees_event(ees, CEVNT_BADDATE);
+ ees_reset(ees);
+ return;
+ }
+
+ ees->day = day;
+
+ /* Get timezone. The clocktime routine wants the number
+ * of hours to add to the delivered time to get UT.
+ * Currently -1 if BST flag set, 0 otherwise. This
+ * is the place to tweak things if double summer time
+ * ever happens.
+ */
+ ees->tz = istrue(cp[EESM_BST]) ? -1 : 0;
+
+ if (ees->day > 366 || ees->day < 1 ||
+ ees->hour > 23 || ees->minute > 59 || ees->second > 59) {
+ ees->baddata++;
+ ees->reason = CODEREASON + 12;
+ ees_event(ees, CEVNT_BADDATE);
+ ees_reset(ees);
+ return;
+ }
+
+ n_sample = ees->nsamples;
+
+ /* Now, compute the reference time value: text -> tmp.l_ui */
+ if (!clocktime(ees->day, ees->hour, ees->minute, ees->second,
+ ees->tz, rbufp->recv_time.l_ui, &ees->yearstart,
+ &tmp.l_ui)) {
+ ees->baddata++;
+ ees->reason = CODEREASON + 13;
+ ees_event(ees, CEVNT_BADDATE);
+ ees_reset(ees);
+ return;
+ }
+ tmp.l_uf = 0;
+
+ /* DON'T use ees->arrvtime -- it may be < reftime */
+ ees->lastsampletime = tmp;
+
+ /* If we are synchronised to the radio, update the reference time.
+ * Also keep a note of when clock was last good.
+ */
+ if (istrue(cp[EESM_MSFOK])) {
+ ees->reftime = tmp;
+ ees->clocklastgood = current_time;
+ }
+
+
+ /* Compute the offset. For the fractional part of the
+ * offset we use the expected delay for the message.
+ */
+ ees->codeoffsets[n_sample].l_ui = tmp.l_ui;
+ ees->codeoffsets[n_sample].l_uf = 0;
+
+ /* Number of seconds since the last step */
+ sincelast = this_uisec - ees->last_step;
+
+ memset((char *) &ppsclockev, 0, sizeof ppsclockev);
+
+ rc = ioctl(ees->io.fd, CIOGETEV, (char *) &ppsclockev);
+ if (debug & DB_PRINT_EV) fprintf(stderr,
+ "[%x] CIOGETEV u%d %d (%lx %d) gave %d (%d): %08lx %08lx %ld\n",
+ DB_PRINT_EV, ees->unit, ees->io.fd, CIOGETEV, is_pps(ees),
+ rc, errno, ptr[0], ptr[1], ptr[2]);
+
+ /* If we managed to get the time of arrival, process the info */
+ if (rc >= 0) {
+ int conv = -1;
+ pps_step = ppsclockev.serial - ees->last_pps_no;
+
+ /* Possible that PPS triggered, but text message didn't */
+ if (pps_step == 2) syslog(LOG_ERR, "pps step = 2 @ %02d", ees->second);
+ if (pps_step == 2 && ees->second == 1) suspect_4ms_step |= 1;
+ if (pps_step == 2 && ees->second == 2) suspect_4ms_step |= 4;
+
+ /* allow for single loss of PPS only */
+ if (pps_step != 1 && pps_step != 2)
+ fprintf(stderr, "PPS step: %d too far off %ld (%d)\n",
+ ppsclockev.serial, ees->last_pps_no, pps_step);
+ else if (!buftvtots((char *) &(ppsclockev.tv), &pps_arrvstamp))
+ fprintf(stderr, "buftvtots failed\n");
+ else { /* if ((ABS(time difference) - 0.25) < 0)
+ * then believe it ...
+ */
+ l_fp diff;
+ diff = pps_arrvstamp;
+ conv = 0;
+ L_SUB(&diff, &ees->arrvtime);
+if (debug & DB_PRINT_CDT) printf("[%x] Have %lx.%08lx and %lx.%08lx -> %lx.%08lx @ %s",
+ DB_PRINT_CDT, ees->arrvtime.l_ui, ees->arrvtime.l_uf,
+ pps_arrvstamp.l_ui, pps_arrvstamp.l_uf,
+ diff.l_ui, diff.l_uf,
+ ctime(&(ppsclockev.tv.tv_sec)));
+ if (L_ISNEG(&diff)) M_NEG(diff.l_ui, diff.l_uf);
+ L_SUB(&diff, &acceptable_slop);
+ if (L_ISNEG(&diff)) { /* AOK -- pps_sample */
+ ees->arrvtime = pps_arrvstamp;
+ conv++;
+ call_pps_sample++;
+ }
+ /* Some loss of some signals around sec = 1 */
+ else if (ees->second == 1) {
+ diff = pps_arrvstamp;
+ L_ADD(&diff, &onesec);
+ L_SUB(&diff, &ees->arrvtime);
+ if (L_ISNEG(&diff)) M_NEG(diff.l_ui, diff.l_uf);
+ L_SUB(&diff, &acceptable_slop);
+syslog(LOG_ERR, "Have sec==1 slip %ds a=%08x-p=%08x -> %x.%08x (u=%d) %s",
+ pps_arrvstamp.l_ui - ees->arrvtime.l_ui,
+ pps_arrvstamp.l_uf,
+ ees->arrvtime.l_uf,
+ diff.l_ui, diff.l_uf,
+ ppsclockev.tv.tv_usec,
+ ctime(&(ppsclockev.tv.tv_sec)));
+ if (L_ISNEG(&diff)) { /* AOK -- pps_sample */
+ suspect_4ms_step |= 2;
+ ees->arrvtime = pps_arrvstamp;
+ L_ADD(&ees->arrvtime, &onesec);
+ conv++;
+ call_pps_sample++;
+ }
+ }
+ }
+ ees->last_pps_no = ppsclockev.serial;
+ if (debug & DB_PRINT_CDTC) printf(
+ "[%x] %08lx %08lx %d u%d (%d %d)\n",
+ DB_PRINT_CDTC, pps_arrvstamp.l_ui,
+ pps_arrvstamp.l_uf, conv, ees->unit,
+ call_pps_sample, pps_step);
+ }
+
+ /* See if there has been a 4ms jump at a minute boundry */
+ { l_fp delta;
+#define delta_isec delta.l_ui
+#define delta_ssec delta.l_i
+#define delta_sfsec delta.l_f
+ long delta_f_abs;
+
+ delta.l_i = ees->arrvtime.l_i;
+ delta.l_f = ees->arrvtime.l_f;
+
+ L_SUB(&delta, &ees->last_l);
+ delta_f_abs = delta_sfsec;
+ if (delta_f_abs < 0) delta_f_abs = -delta_f_abs;
+
+ /* Dump the deltas each minute */
+ if (debug & DB_DUMP_DELTAS)
+ { if (/*0 <= ees->second && */
+ ees->second < ((sizeof deltas) / (sizeof deltas[0]))) deltas[ees->second] = delta_sfsec;
+ /* Dump on second 1, as second 0 sometimes missed */
+ if (ees->second == 1) {
+ char text[16 * ((sizeof deltas) / (sizeof deltas[0]))];
+ char *ptr=text;
+ int i;
+ for (i=0; i<((sizeof deltas) / (sizeof deltas[0])); i++) {
+ sprintf(ptr, " %d.%04d",
+ msec(deltas[i]), subms(deltas[i]));
+ while (*ptr) ptr++;
+ }
+ syslog(LOG_ERR, "Deltas: %d.%04d<->%d.%04d: %s",
+ msec(EES_STEP_F - EES_STEP_F_GRACE), subms(EES_STEP_F - EES_STEP_F_GRACE),
+ msec(EES_STEP_F + EES_STEP_F_GRACE), subms(EES_STEP_F + EES_STEP_F_GRACE),
+ text+1);
+ for (i=0; i<((sizeof deltas) / (sizeof deltas[0])); i++) deltas[i] = 0;
+ }
+ }
+
+ /* Lets see if we have a 4 mS step at a minute boundaary */
+ if ( ((EES_STEP_F - EES_STEP_F_GRACE) < delta_f_abs) &&
+ (delta_f_abs < (EES_STEP_F + EES_STEP_F_GRACE)) &&
+ (ees->second == 0 || ees->second == 1 || ees->second == 2) &&
+ (sincelast < 0 || sincelast > 122)
+ ) { /* 4ms jump at min boundry */
+ int old_sincelast;
+ int count=0;
+ int sum = 0;
+ /* Yes -- so compute the ramp time */
+ if (ees->last_step == 0) sincelast = 0;
+ old_sincelast = sincelast;
+
+ /* First time in, just set "ees->last_step" */
+ if(ees->last_step) {
+ int other_step = 0;
+ int third_step = 0;
+ int this_step = (sincelast + (60 /2)) / 60;
+ int p_step = ees->this_step;
+ int p;
+ ees->last_steps[p_step] = this_step;
+ p= p_step;
+ p_step++;
+ if (p_step >= LAST_STEPS) p_step = 0;
+ ees->this_step = p_step;
+ /* Find the "average" interval */
+ while (p != p_step) {
+ int this = ees->last_steps[p];
+ if (this == 0) break;
+ if (this != this_step) {
+ if (other_step == 0 && (
+ this== (this_step +2) ||
+ this== (this_step -2) ||
+ this== (this_step +1) ||
+ this== (this_step -1)))
+ other_step = this;
+ if (other_step != this) {
+ int delta = (this_step - other_step);
+ if (delta < 0) delta = - delta;
+ if (third_step == 0 && (
+ (delta == 1) ? (
+ this == (other_step +1) ||
+ this == (other_step -1) ||
+ this == (this_step +1) ||
+ this == (this_step -1))
+ :
+ (
+ this == (this_step + other_step)/2
+ )
+ )) third_step = this;
+ if (third_step != this) break;
+ }
+ }
+ sum += this;
+ p--;
+ if (p < 0) p += LAST_STEPS;
+ count++;
+ }
+syslog(LOG_ERR, "MSF%d: %d: This=%d (%d), other=%d/%d, sum=%d, count=%d, pps_step=%d, suspect=%x", ees->unit, p, ees->last_steps[p], this_step, other_step, third_step, sum, count, pps_step, suspect_4ms_step);
+ if (count != 0) sum = ((sum * 60) + (count /2)) / count;
+#define SV(x) (ees->last_steps[(x + p_step) % LAST_STEPS])
+syslog(LOG_ERR, "MSF%d: %x steps %d: %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d",
+ ees->unit, suspect_4ms_step, p_step, SV(0), SV(1), SV(2), SV(3), SV(4), SV(5), SV(6),
+ SV(7), SV(8), SV(9), SV(10), SV(11), SV(12), SV(13), SV(14), SV(15));
+printf("MSF%d: steps %d: %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d\n",
+ ees->unit, p_step, SV(0), SV(1), SV(2), SV(3), SV(4), SV(5), SV(6),
+ SV(7), SV(8), SV(9), SV(10), SV(11), SV(12), SV(13), SV(14), SV(15));
+#undef SV
+ ees->jump_fsecs = delta_sfsec;
+ ees->using_ramp = 1;
+ if (sincelast > 170)
+ ees->last_step_late += sincelast - ((sum) ? sum : ees->last_step_secs);
+ else ees->last_step_late = 30;
+ if (ees->last_step_late < -60 || ees->last_step_late > 120) ees->last_step_late = 30;
+ if (ees->last_step_late < 0) ees->last_step_late = 0;
+ if (ees->last_step_late >= 60) ees->last_step_late = 59;
+ sincelast = 0;
+ }
+ else { /* First time in -- just save info */
+ ees->last_step_late = 30;
+ ees->jump_fsecs = delta_sfsec;
+ ees->using_ramp = 1;
+ sum = 4 * 60;
+ }
+ ees->last_step = this_uisec;
+printf("MSF%d: d=%3ld.%04ld@%d :%d:%d:$%d:%d:%d\n",
+ees->unit, msec(delta_sfsec), subms(delta_sfsec), ees->second, old_sincelast, ees->last_step_late, count, sum, ees->last_step_secs);
+syslog(LOG_ERR, "MSF%d: d=%3d.%04d@%d :%d:%d:%d:%d:%d",
+ees->unit, msec(delta_sfsec), subms(delta_sfsec), ees->second, old_sincelast, ees->last_step_late, count, sum, ees->last_step_secs);
+ if (sum) ees->last_step_secs = sum;
+ }
+ /* OK, so not a 4ms step at a minute boundry */
+ else {
+ if (suspect_4ms_step) syslog(LOG_ERR,
+ "MSF%d: suspect = %x, but delta of %d.%04d [%d.%04d<%d.%04d<%d.%04d: %d %d]",
+ ees->unit, suspect_4ms_step, msec(delta_sfsec), subms(delta_sfsec),
+ msec(EES_STEP_F - EES_STEP_F_GRACE),
+ subms(EES_STEP_F - EES_STEP_F_GRACE),
+ msec(delta_f_abs),
+ subms(delta_f_abs),
+ msec(EES_STEP_F + EES_STEP_F_GRACE),
+ subms(EES_STEP_F + EES_STEP_F_GRACE),
+ ees->second,
+ sincelast);
+ if ((delta_f_abs > EES_STEP_NOTE) && ees->last_l.l_i) {
+ static ees_step_notes = EES_STEP_NOTES;
+ if (ees_step_notes > 0) {
+ ees_step_notes--;
+printf("MSF%d: D=%3ld.%04ld@%02d :%d%s\n",
+ees->unit, msec(delta_sfsec), subms(delta_sfsec), ees->second, sincelast, ees_step_notes ? "" : " -- NO MORE !");
+syslog(LOG_ERR, "MSF%d: D=%3d.%04d@%02d :%d%s",
+ees->unit, msec(delta_sfsec), subms(delta_sfsec), ees->second, (ees->last_step) ? sincelast : -1, ees_step_notes ? "" : " -- NO MORE !");
+ }
+ }
+ }
+ }
+ ees->last_l = ees->arrvtime;
+
+ /* IF we have found that it's ramping
+ * && it's within twice the expected ramp period
+ * && there is a non zero step size (avoid /0 !)
+ * THEN we twiddle things
+ */
+ if (ees->using_ramp &&
+ sincelast < (ees->last_step_secs)*2 &&
+ ees->last_step_secs)
+ { long sec_of_ramp = sincelast + ees->last_step_late;
+ long fsecs;
+ l_fp inc;
+
+ /* Ramp time may vary, so may ramp for longer than last time */
+ if (sec_of_ramp > (ees->last_step_secs + 120))
+ sec_of_ramp = ees->last_step_secs;
+
+ /* sec_of_ramp * ees->jump_fsecs may overflow 2**32 */
+ fsecs = sec_of_ramp * (ees->jump_fsecs / ees->last_step_secs);
+
+ if (debug & DB_LOG_DELTAS) syslog(LOG_ERR,
+ "[%x] MSF%d: %3d/%03d -> d=%11d (%d|%d)",
+ DB_LOG_DELTAS,
+ ees->unit, sec_of_ramp, ees->last_step_secs, fsecs,
+ pps_arrvstamp.l_f, pps_arrvstamp.l_f + fsecs);
+ if (debug & DB_PRINT_DELTAS) printf(
+ "MSF%d: %3ld/%03d -> d=%11ld (%ld|%ld)\n",
+ ees->unit, sec_of_ramp, ees->last_step_secs, fsecs,
+ pps_arrvstamp.l_f, pps_arrvstamp.l_f + fsecs);
+
+ /* Must sign extend the result */
+ inc.l_i = (fsecs < 0) ? -1 : 0;
+ inc.l_f = fsecs;
+ if (debug & DB_INC_PPS)
+ { L_SUB(&pps_arrvstamp, &inc);
+ L_SUB(&ees->arrvtime, &inc);
+ }
+ else
+ { L_ADD(&pps_arrvstamp, &inc);
+ L_ADD(&ees->arrvtime, &inc);
+ }
+ }
+ else {
+ if (debug & DB_LOG_DELTAS) syslog(LOG_ERR,
+ "[%x] MSF%d: ees->using_ramp=%d, sincelast=%x / %x, ees->last_step_secs=%x",
+ DB_LOG_DELTAS,
+ ees->unit, ees->using_ramp,
+ sincelast,
+ (ees->last_step_secs)*2,
+ ees->last_step_secs);
+ if (debug & DB_PRINT_DELTAS) printf(
+ "[%x] MSF%d: ees->using_ramp=%d, sincelast=%x / %x, ees->last_step_secs=%x\n",
+ DB_LOG_DELTAS,
+ ees->unit, ees->using_ramp,
+ sincelast,
+ (ees->last_step_secs)*2,
+ ees->last_step_secs);
+ }
+
+ L_SUB(&ees->arrvtime, &offset_fudge[ees->unit]);
+ L_SUB(&pps_arrvstamp, &offset_fudge[ees->unit]);
+
+ if (call_pps_sample && !(debug & DB_NO_PPS)) {
+ /* Sigh -- it expects its args negated */
+ L_NEG(&pps_arrvstamp);
+ (void) pps_sample(&pps_arrvstamp);
+ }
+
+ /* Subtract off the local clock time stamp */
+ L_SUB(&ees->codeoffsets[n_sample], &ees->arrvtime);
+ if (debug & DB_LOG_SAMPLES) syslog(LOG_ERR,
+ "MSF%d: [%x] %d (ees: %d %d) (pps: %d %d)%s",
+ ees->unit, DB_LOG_DELTAS, n_sample,
+ ees->codeoffsets[n_sample].l_f,
+ ees->codeoffsets[n_sample].l_f / 4295,
+ pps_arrvstamp.l_f,
+ pps_arrvstamp.l_f /4295,
+ (debug & DB_NO_PPS) ? " [no PPS]" : "");
+
+ if (ees->nsamples++ == NCODES-1) ees_process(ees);
+
+ /* Done! */
+}
+
+
+static void set_x(fp_offset)
+l_fp *fp_offset;
+{
+ step_systime_real(fp_offset);
+}
+
+
+/* offcompare - auxiliary comparison routine for offset sort */
+
+static int
+offcompare(a, b)
+l_fp *a, *b;
+{
+ return(L_ISGEQ(a, b) ? (L_ISEQU(a, b) ? 0 : 1) : -1);
+}
+
+
+/* ees_process - process a pile of samples from the clock */
+static void ees_process(ees)
+ struct eesunit *ees;
+{
+ static last_samples = -1;
+ register int i, j;
+ register int noff;
+ register l_fp *coffs = ees->codeoffsets;
+ l_fp offset, tmp;
+ u_fp dispersion; /* ++++ */
+ int lostsync, isinsync;
+ int samples = ees->nsamples;
+ int samplelog = 0; /* keep "gcc -Wall" happy ! */
+ int samplereduce = (samples + 1) / 2;
+
+ /* Reset things to zero so we don't have to worry later */
+ ees_reset(ees);
+
+ if (sloppyclockflag[ees->unit]) {
+ samplelog = (samples < 2) ? 0 :
+ (samples < 5) ? 1 :
+ (samples < 9) ? 2 :
+ (samples < 17) ? 3 :
+ (samples < 33) ? 4 : 5;
+ samplereduce = (1 << samplelog);
+ }
+
+ if (samples != last_samples &&
+ ((samples != (last_samples-1)) || samples < 3)) {
+ syslog(LOG_ERR, "Samples=%d (%d), samplereduce=%d ....",
+ samples, last_samples, samplereduce);
+ last_samples = samples;
+ }
+ if (samples < 1) return;
+
+ /* If requested, dump the raw data we have in the buffer */
+ if (ees->dump_vals) dump_buf(coffs, 0, samples, "Raw data is:");
+
+ /* Sort the offsets, trim off the extremes, then choose one. */
+ qsort((char *) coffs, samples, sizeof(l_fp), offcompare);
+
+ noff = samples;
+ i = 0;
+ while ((noff - i) > samplereduce) {
+ /* Trim off the sample which is further away
+ * from the median. We work this out by doubling
+ * the median, subtracting off the end samples, and
+ * looking at the sign of the answer, using the
+ * identity (c-b)-(b-a) == 2*b-a-c
+ */
+ tmp = coffs[(noff + i)/2];
+ L_ADD(&tmp, &tmp);
+ L_SUB(&tmp, &coffs[i]);
+ L_SUB(&tmp, &coffs[noff-1]);
+ if (L_ISNEG(&tmp)) noff--; else i++;
+ }
+
+ /* If requested, dump the reduce data we have in the buffer */
+ if (ees->dump_vals) dump_buf(coffs, i, noff, "Reduced to:");
+
+ /* What we do next depends on the setting of the sloppy clock flag.
+ * If it is on, average the remainder to derive our estimate.
+ * Otherwise, just pick a representative value from the remaining stuff
+ */
+ if (sloppyclockflag[ees->unit]) {
+ offset.l_ui = offset.l_uf = 0;
+ for (j = i; j < noff; j++)
+ L_ADD(&offset, &coffs[j]);
+ for (j = samplelog; j > 0; j--)
+ L_RSHIFTU(&offset);
+ }
+ else offset = coffs[i+BESTSAMPLE];
+
+ /* Compute the dispersion as the difference between the
+ * lowest and highest offsets that remain in the
+ * consideration list.
+ *
+ * It looks like MOST clocks have MOD (max error), so halve it !
+ */
+ tmp = coffs[noff-1];
+ L_SUB(&tmp, &coffs[i]);
+#define FRACT_SEC(n) ((1 << 30) / (n/2))
+ dispersion = LFPTOFP(&tmp) / 2; /* ++++ */
+ if (debug & (DB_SYSLOG_SMPLI | DB_SYSLOG_SMPLE)) syslog(
+ (debug & DB_SYSLOG_SMPLE) ? LOG_ERR : LOG_INFO,
+ "I: [%x] Offset=%06d (%d), disp=%06d%s [%d], %d %d=%d %d:%d %d=%d %d",
+ debug & (DB_SYSLOG_SMPLI | DB_SYSLOG_SMPLE),
+ offset.l_f / 4295, offset.l_f,
+ (dispersion * 1526) / 100,
+ (sloppyclockflag[ees->unit]) ? " by averaging" : "",
+ FRACT_SEC(10) / 4295,
+ (coffs[0].l_f) / 4295,
+ i,
+ (coffs[i].l_f) / 4295,
+ (coffs[samples/2].l_f) / 4295,
+ (coffs[i+BESTSAMPLE].l_f) / 4295,
+ noff-1,
+ (coffs[noff-1].l_f) / 4295,
+ (coffs[samples-1].l_f) / 4295);
+
+ /* Are we playing silly wotsits ?
+ * If we are using all data, see if there is a "small" delta,
+ * and if so, blurr this with 3/4 of the delta from the last value
+ */
+ if (ees->usealldata && ees->offset.l_uf) {
+ long diff = (long) (ees->offset.l_uf - offset.l_uf);
+
+ /* is the delta small enough ? */
+ if ((- FRACT_SEC(100)) < diff && diff < FRACT_SEC(100)) {
+ int samd = (64 * 4) / samples;
+ long new;
+ if (samd < 2) samd = 2;
+ new = offset.l_uf + ((diff * (samd -1)) / samd);
+
+ /* Sign change -> need to fix up int part */
+ if ((new & (1 << 31)) !=
+ (((long) offset.l_uf) & ( 1 << 31)))
+ { syslog(LOG_INFO, "I: %x != %x (%x %x), so add %d",
+ new & (1 << 31),
+ ((long) offset.l_uf) & ( 1 << 31),
+ new, (long) offset.l_uf,
+ (new < 0) ? -1 : 1);
+ offset.l_ui += (new < 0) ? -1 : 1;
+ }
+ dispersion /= 4;
+ if (debug & (DB_SYSLOG_SMTHI | DB_SYSLOG_SMTHE)) syslog(
+ (debug & DB_SYSLOG_SMTHE) ? LOG_ERR : LOG_INFO,
+ "I: [%x] Smooth data: %d -> %d, dispersion now %d",
+ debug & (DB_SYSLOG_SMTHI | DB_SYSLOG_SMTHE),
+ ((long) offset.l_uf) / 4295, new / 4295,
+ (dispersion * 1526) / 100);
+ offset.l_uf = (unsigned long) new;
+ }
+ else if (debug & (DB_SYSLOG_NSMTHI | DB_SYSLOG_NSMTHE)) syslog(
+ (debug & DB_SYSLOG_NSMTHE) ? LOG_ERR : LOG_INFO,
+ "[%x] No smooth as delta not %d < %d < %d",
+ debug & (DB_SYSLOG_NSMTHI | DB_SYSLOG_NSMTHE),
+ - FRACT_SEC(100), diff, FRACT_SEC(100));
+ }
+ else if (debug & (DB_SYSLOG_NSMTHI | DB_SYSLOG_NSMTHE)) syslog(
+ (debug & DB_SYSLOG_NSMTHE) ? LOG_ERR : LOG_INFO,
+ "I: [%x] No smooth as flag=%x and old=%x=%d (%d:%d)",
+ debug & (DB_SYSLOG_NSMTHI | DB_SYSLOG_NSMTHE),
+ ees->usealldata, ees->offset.l_f, ees->offset.l_uf,
+ offset.l_f, ees->offset.l_f - offset.l_f);
+
+ /* Collect offset info for debugging info */
+ ees->offset = offset;
+ ees->lowoffset = coffs[i];
+ ees->highoffset = coffs[noff-1];
+
+ /* Determine synchronization status. Can be unsync'd either
+ * by a report from the clock or by a leap hold.
+ *
+ * Loss of the radio signal for a short time does not cause
+ * us to go unsynchronised, since the receiver keeps quite
+ * good time on its own. The spec says 20ms in 4 hours; the
+ * observed drift in our clock (Cambridge) is about a second
+ * a day, but even that keeps us within the inherent tolerance
+ * of the clock for about 15 minutes. Observation shows that
+ * the typical "short" outage is 3 minutes, so to allow us
+ * to ride out those, we will give it 5 minutes.
+ */
+ lostsync = current_time - ees->clocklastgood > 300 ? 1 : 0;
+ isinsync = (lostsync || ees->leaphold > current_time) ? 0 : 1;
+
+ /* Done. Use time of last good, synchronised code as the
+ * reference time, and lastsampletime as the receive time.
+ */
+ if (ees->fix_pending) {
+ syslog(LOG_ERR, "MSF%d: fix_pending=%d -> jump %x.%08x\n",
+ ees->fix_pending, ees->unit, offset.l_i, offset.l_f);
+ ees->fix_pending = 0;
+ set_x(&offset);
+ L_CLR(&offset);
+ }
+ refclock_receive(ees->peer,
+ &offset,
+ 0, /* delay */
+ dispersion,
+ &ees->reftime,
+ &ees->lastsampletime, /* receive time */
+ (isinsync) ? 0 : LEAP_NOTINSYNC);
+ ees_event(ees, lostsync ? CEVNT_PROP : CEVNT_NOMINAL);
+}
+
+/* msfees_poll - called by the transmit procedure */
+static void msfees_poll(unit, peer)
+ int unit;
+ char *peer;
+{
+ if (unit >= MAXUNITS) {
+ syslog(LOG_ERR, "ees clock poll: INTERNAL: unit %d invalid",
+ unit);
+ return;
+ }
+ if (!unitinuse[unit]) {
+ syslog(LOG_ERR, "ees clock poll: INTERNAL: unit %d unused",
+ unit);
+ return;
+ }
+
+ ees_process(eesunits[unit]);
+
+ if ((current_time - eesunits[unit]->lasttime) > 150)
+ ees_event(eesunits[unit], CEVNT_FAULT);
+}
+
+/* msfees_leap - called when a leap second occurs */
+static void msfees_leap()
+{
+ register int i;
+
+ /* This routine should be entered a few seconds after
+ * midnight UTC when a leap second occurs. To ensure we
+ * don't believe foolish time from the clock(s) we set a
+ * 40 minute hold on them. It shouldn't take anywhere
+ * near this amount of time to adjust if the clock is getTING
+ * data, but doing anything else is complicated.
+ */
+ for (i = 0; i < MAXUNITS; i++) if (unitinuse[i])
+ eesunits[i]->leaphold = current_time + EESLEAPHOLD;
+}
+
+/* msfees_control - set fudge factors, return statistics */
+static void msfees_control(unit, in, out)
+ int unit;
+ struct refclockstat *in;
+ struct refclockstat *out;
+{
+ register struct eesunit *ees = eesunits[unit];
+
+ if (unit >= MAXUNITS) {
+ syslog(LOG_ERR, "ees clock: unit %d invalid (max %d)",
+ unit, MAXUNITS-1);
+ return;
+ }
+
+ if (in != 0) {
+ if (in->haveflags & CLK_HAVETIME1)
+ fudgefactor[unit] = in->fudgetime1;
+ if (in->haveflags & CLK_HAVETIME2)
+ os_delay[unit] = in->fudgetime2;
+ offset_fudge[unit] = os_delay[unit];
+ L_ADD(&offset_fudge[unit], &fudgefactor[unit]);
+ L_ADD(&offset_fudge[unit], &inherent_delay[unit]);
+ if (in->haveflags & CLK_HAVEVAL1) {
+ stratumtouse[unit] = (u_char)(in->fudgeval1 & 0xf);
+ if (unitinuse[unit]) {
+ /* Should actually reselect clock, but
+ * will wait for the next timecode
+ */
+ struct peer *peer = ees->peer;
+ struct refclockproc *pp = peer->procptr;
+ peer->stratum = stratumtouse[unit];
+ if (stratumtouse[unit] <= 1) {
+ memmove((char *)&pp->refid,
+ EESREFID, 4);
+ if (unit>0 && unit<10)
+ ((char *)&pp->refid)[3] =
+ '0' + unit;
+ }
+ else peer->refid = htonl(EESHSREFID);
+ }
+ }
+ if (in->haveflags & CLK_HAVEVAL2) {
+ printf("Debug: %x -> %lx\n", debug, in->fudgeval2);
+ syslog(LOG_ERR, "MSF%d: debug %x -> %x",
+ unit, debug, in->fudgeval2);
+ debug = in->fudgeval2;
+ }
+ if (in->haveflags & CLK_HAVEFLAG1) {
+ sloppyclockflag[unit] = in->flags & CLK_FLAG1;
+ }
+ if (in->haveflags & CLK_HAVEFLAG2) {
+ ees->fix_pending++;
+ /* if (in->flags & CLK_FLAG2 && unitinuse[unit])
+ ees->leaphold = 0; */
+ }
+ if (in->haveflags & CLK_HAVEFLAG3 && unitinuse[unit]) {
+ printf("dump_vals: %x -> %x\n", ees->dump_vals, in->flags & CLK_FLAG3);
+ ees->dump_vals = in->flags & CLK_FLAG3;
+ }
+ if (in->haveflags & CLK_HAVEFLAG4 && unitinuse[unit]) {
+ ees->usealldata = in->flags & CLK_FLAG4;
+ }
+ }
+
+ if (out != 0) {
+ struct peer *peer = ees->peer;
+ struct refclockproc *pp = peer->procptr;
+ out->type = REFCLK_MSF_EES;
+ out->haveflags
+ = CLK_HAVETIME1|CLK_HAVETIME2|CLK_HAVEVAL1|CLK_HAVEVAL2|CLK_HAVEFLAG1|CLK_HAVEFLAG3|CLK_HAVEFLAG4;
+ out->clockdesc = pp->clockdesc;
+ out->fudgetime1 = fudgefactor[unit];
+ out->fudgetime2 = os_delay[unit];
+ out->fudgeval1 = (long)stratumtouse[unit];
+ /*out->fudgeval2= debug*/;
+ memmove((char *)&out->fudgeval2, (char *)&pp->refid, 4);
+ out->flags = sloppyclockflag[unit];
+ if (unitinuse[unit]) {
+ out->flags |= ees->dump_vals | ees->usealldata;
+ out->lencode = ees->lencode;
+ out->lastcode = ees->lastcode;
+ out->timereset = current_time - ees->timestarted;
+ out->polls = 0; /* we don't poll */
+ out->noresponse = 0; /* ditto */
+ out->badformat = ees->badformat;
+ out->baddata = ees->baddata;
+ out->lastevent = ees->lastevent;
+ out->currentstatus = ees->status;
+ } else {
+ out->lencode = 0;
+ out->lastcode = "";
+ out->polls = out->noresponse = 0;
+ out->badformat = out->baddata = 0;
+ out->timereset = 0;
+ out->currentstatus = out->lastevent = CEVNT_NOMINAL;
+ }
+ }
+}
+
+
+/* msfees_buginfo - return clock dependent debugging info */
+static void msfees_buginfo(unit, bug)
+ int unit;
+ register struct refclockbug *bug;
+{
+ register struct eesunit *ees;
+
+ bug->nvalues = bug->ntimes = 0;
+
+ if (unit >= MAXUNITS) {
+ syslog(LOG_ERR, "ees clock: unit %d invalid (max %d)",
+ unit, MAXUNITS-1);
+ return;
+ }
+
+ if (!unitinuse[unit])
+ return;
+ ees = eesunits[unit];
+
+ bug->nvalues = 16;
+ bug->svalues = 0x0800;
+ bug->values[0] = (ees->lasttime) ? current_time - ees->lasttime : 0;
+ bug->values[1] = (ees->clocklastgood)?current_time-ees->clocklastgood:0;
+ bug->values[2] = (u_long)ees->status;
+ bug->values[3] = (u_long)ees->lastevent;
+ bug->values[4] = (u_long)ees->reason;
+ bug->values[5] = (u_long)ees->nsamples;
+ bug->values[6] = (u_long)ees->codestate;
+ bug->values[7] = (u_long)ees->day;
+ bug->values[8] = (u_long)ees->hour;
+ bug->values[9] = (u_long)ees->minute;
+ bug->values[10] = (u_long)ees->second;
+ bug->values[11] = (u_long)ees->tz;
+ bug->values[12] = ees->yearstart;
+ bug->values[13] = (ees->leaphold > current_time) ?
+ ees->leaphold - current_time : 0;
+ bug->values[14] = inherent_delay[unit].l_uf;
+ bug->values[15] = offset_fudge[unit].l_uf;
+
+ bug->ntimes = 11;
+ bug->stimes = 0x3f8;
+ bug->times[0] = ees->reftime;
+ bug->times[1] = ees->arrvtime;
+ bug->times[2] = ees->lastsampletime;
+ bug->times[3] = ees->offset;
+ bug->times[4] = ees->lowoffset;
+ bug->times[5] = ees->highoffset;
+ bug->times[6] = inherent_delay[unit];
+ bug->times[8] = os_delay[unit];
+ bug->times[7] = fudgefactor[unit];
+ bug->times[9] = offset_fudge[unit];
+ bug->times[10].l_ui = ees->yearstart;
+ bug->times[10].l_uf = 0;
+}
+
+struct refclock refclock_msfees = {
+ msfees_start, msfees_shutdown, msfees_poll,
+ msfees_control, msfees_init, msfees_buginfo, NOFLAGS
+};
+#endif /* defined(REFCLOCK) && defined(MSFEESPPS) && defined(STREAM) */
diff --git a/usr.sbin/xntpd/xntpd/refclock_mx4200.c b/usr.sbin/xntpd/xntpd/refclock_mx4200.c
new file mode 100644
index 0000000..577193d
--- /dev/null
+++ b/usr.sbin/xntpd/xntpd/refclock_mx4200.c
@@ -0,0 +1,1343 @@
+/*
+ * This software was developed by the Computer Systems Engineering group
+ * at Lawrence Berkeley Laboratory under DARPA contract BG 91-66.
+ *
+ * Copyright (c) 1992 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Lawrence Berkeley Laboratory.
+ * 4. The name of the University may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#if defined(REFCLOCK) && defined(PPS) && (defined(MX4200) || defined(MX4200CLK) || defined(MX4200PPS))
+
+#if !defined(lint) && !defined(__GNUC__)
+static char rcsid[] =
+ "@(#) /src/master/xntp-930612/xntpd/refclock_mx4200.c,v 1.5 1993/06/18 21:19:54 jbj Exp (LBL) ";
+#endif
+
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/time.h>
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_calendar.h"
+#include "ntp_unixtime.h"
+
+#if __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+#if defined(HAVE_BSD_TTYS)
+#include <sgtty.h>
+#endif /* HAVE_BSD_TTYS */
+
+#if defined(HAVE_SYSV_TTYS)
+#include <termio.h>
+#endif /* HAVE_SYSV_TTYS */
+
+#if defined(HAVE_TERMIOS)
+#include <termios.h>
+#endif
+#if defined(STREAM)
+#include <stropts.h>
+#if defined(MX4200CLK)
+#include <sys/clkdefs.h>
+#endif /* MX4200CLK */
+#endif /* STREAM */
+
+#include <sys/ppsclock.h>
+
+#include "mx4200.h"
+#include "ntp_stdlib.h"
+
+/*
+ * This driver supports the Magnavox Model MX4200 GPS Receiver.
+ */
+
+/*
+ * Definitions
+ */
+#define MAXUNITS 2 /* max number of mx4200 units */
+#define MX4200232 "/dev/gps%d"
+#define SPEED232 B4800 /* baud */
+
+/*
+ * The number of raw samples which we acquire to derive a single estimate.
+ */
+#define NSTAMPS 64
+
+/*
+ * Radio interface parameters
+ */
+#define MX4200PRECISION (-18) /* precision assumed (about 4 us) */
+#define MX4200REFID "GPS" /* reference id */
+#define MX4200DESCRIPTION "Magnavox MX4200 GPS Receiver" /* who we are */
+#define DEFFUDGETIME 0 /* default fudge time (ms) */
+
+/* Leap stuff */
+extern U_LONG leap_hoursfromleap;
+extern U_LONG leap_happened;
+static int leap_debug;
+
+/*
+ * mx4200_reset - reset the count back to zero
+ */
+#define mx4200_reset(mx4200) \
+ do { \
+ (mx4200)->nsamples = 0; \
+ } while (0)
+
+/*
+ * mx4200_event - record and report an event
+ */
+#define mx4200_event(mx4200, evcode) \
+ do { \
+ if ((mx4200)->status != (u_char)(evcode)) \
+ mx4200_report_event((mx4200), (evcode)); \
+ } while (0)
+
+/*
+ * Imported from the timer module
+ */
+extern U_LONG current_time;
+extern struct event timerqueue[];
+
+/*
+ * Imported from ntp_loopfilter module
+ */
+extern int fdpps; /* pps file descriptor */
+
+/*
+ * Imported from ntpd module
+ */
+extern int debug; /* global debug flag */
+
+/*
+ * MX4200 unit control structure.
+ */
+struct mx4200unit {
+ struct peer *peer; /* associated peer structure */
+ struct refclockio io; /* given to the I/O handler */
+ U_LONG gpssamples[NSTAMPS]; /* the GPS time samples */
+ l_fp unixsamples[NSTAMPS]; /* the UNIX time samples */
+
+
+ l_fp lastsampletime; /* time of last estimate */
+ u_int lastserial; /* last pps serial number */
+#ifdef notdef
+ l_fp lastrec; /* last receive time */
+ l_fp lastref; /* last timecode time */
+#endif
+ char lastcode[RX_BUFF_SIZE]; /* last timecode received */
+ U_LONG lasttime; /* last time clock heard from */
+ u_char nsamples; /* number of samples we've collected */
+ u_char unit; /* unit number for this guy */
+ u_char status; /* clock status */
+ u_char lastevent; /* last clock event */
+ u_char reason; /* reason for last abort */
+ u_char lencode; /* length of last timecode */
+ u_char year; /* year of eternity */
+ u_short monthday; /* day of month */
+ u_char hour; /* hour of day */
+ u_char minute; /* minute of hour */
+ u_char second; /* seconds of minute */
+ u_char leap; /* leap indicators */
+ /*
+ * Status tallies
+ */
+#ifdef notdef
+ U_LONG polls; /* polls sent */
+ U_LONG noresponse; /* number of nonresponses */
+#endif
+ U_LONG badformat; /* bad format */
+ U_LONG baddata; /* bad data */
+ U_LONG timestarted; /* time we started this */
+};
+
+/*
+ * We demand that consecutive PPS samples are more than 0.995 seconds
+ * and less than 1.005 seconds apart.
+ */
+#define PPSLODIFF_UI 0 /* 0.900 as an l_fp */
+#define PPSLODIFF_UF 0xe6666610
+
+#define PPSHIDIFF_UI 1 /* 1.100 as an l_fp */
+#define PPSHIDIFF_UF 0x19999990
+
+/*
+ * reason codes
+ */
+#define PPSREASON 20
+#define CODEREASON 40
+#define PROCREASON 60
+
+/*
+ * Data space for the unit structures. Note that we allocate these on
+ * the fly, but never give them back.
+ */
+static struct mx4200unit *mx4200units[MAXUNITS];
+static u_char unitinuse[MAXUNITS];
+
+/*
+ * Keep the fudge factors separately so they can be set even
+ * when no clock is configured.
+ */
+static l_fp fudgefactor[MAXUNITS];
+static u_char stratumtouse[MAXUNITS];
+static u_char sloppyclockflag[MAXUNITS];
+static U_LONG refid[MAXUNITS];
+
+static const char pmvxg[] = "PMVXG";
+
+/*
+ * Function prototypes
+ */
+static void mx4200_init P((void));
+static int mx4200_start P((int, struct peer *));
+static void mx4200_shutdown P((int, struct peer *));
+static void mx4200_receive P((struct recvbuf *));
+static void mx4200_process P((struct mx4200unit *));
+static void mx4200_report_event P((struct mx4200unit *, int));
+static void mx4200_poll P((int, struct peer *));
+static void mx4200_control P((int, struct refclockstat *, struct refclockstat *));
+static void mx4200_buginfo P((int, struct refclockbug *));
+
+static char * mx4200_parse P((char *, struct calendar *, int *, int *));
+static int mx4200_needconf P((char *));
+static void mx4200_config P((struct mx4200unit *));
+static void mx4200_send P((int, const char *, ...));
+static int mx4200_cmpl_fp P((void *, void *));
+static u_char cksum P((char *, u_int));
+
+#ifdef DEBUG
+static void opendfile P((int));
+static void checkdfile P((void));
+#endif /* DEBUG */
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_mx4200 = {
+ mx4200_start, mx4200_shutdown, mx4200_poll,
+ mx4200_control, mx4200_init, mx4200_buginfo, NOFLAGS
+};
+
+/*
+ * mx4200_init - initialize internal mx4200 driver data
+ */
+static void
+mx4200_init()
+{
+ register int i;
+ /*
+ * Just zero the data arrays
+ */
+ memset((char *)mx4200units, 0, sizeof mx4200units);
+ memset((char *)unitinuse, 0, sizeof unitinuse);
+
+ /*
+ * Initialize fudge factors to default.
+ */
+ for (i = 0; i < MAXUNITS; i++) {
+ fudgefactor[i].l_ui = 0;
+ fudgefactor[i].l_uf = DEFFUDGETIME;
+ stratumtouse[i] = 0;
+ sloppyclockflag[i] = 0;
+ memcpy((char *)&refid[i], MX4200REFID, 4);
+ }
+}
+
+#ifdef DEBUG
+static char dfile[] = "/var/tmp/MX4200.debug";
+static FILE *df = NULL;
+
+static void
+opendfile(create)
+ int create;
+{
+ if (!create && access(dfile, F_OK) < 0) {
+ syslog(LOG_ERR, "mx4200: open %s: %m", dfile);
+ return;
+ }
+ df = fopen(dfile, "a");
+ if (df == NULL)
+ syslog(LOG_ERR, "mx4200: open %s: %m", dfile);
+ else if (setvbuf(df, NULL, _IOLBF, 0) < 0)
+ syslog(LOG_ERR, "mx4200: setvbuf %s: %m", dfile);
+}
+
+static void
+checkdfile()
+{
+
+ if (df == NULL)
+ return;
+
+ if (access(dfile, F_OK) < 0) {
+ fclose(df);
+ opendfile(1);
+ }
+}
+
+#endif
+
+
+/*
+ * mx4200_start - open the MX4200 devices and initialize data for processing
+ */
+static int
+mx4200_start(unit, peer)
+ int unit;
+ struct peer *peer;
+{
+ register struct mx4200unit *mx4200;
+ register int i;
+ int fd232;
+ char mx4200dev[20];
+
+ /*
+ * Check configuration info
+ */
+ if (unit >= MAXUNITS) {
+ syslog(LOG_ERR, "mx4200_start: unit %d invalid", unit);
+ return (0);
+ }
+ if (unitinuse[unit]) {
+ syslog(LOG_ERR, "mx4200_start: unit %d in use", unit);
+ return (0);
+ }
+
+ /*
+ * Open serial port
+ */
+ (void) sprintf(mx4200dev, MX4200232, unit);
+ fd232 = open(mx4200dev, O_RDWR, 0777);
+ if (fd232 == -1) {
+ syslog(LOG_ERR,
+ "mx4200_start: open of %s: %m", mx4200dev);
+ return (0);
+ }
+
+#if defined(HAVE_SYSV_TTYS)
+ /*
+ * System V serial line parameters (termio interface)
+ *
+ */
+ { struct termio ttyb;
+ if (ioctl(fd232, TCGETA, &ttyb) < 0) {
+ syslog(LOG_ERR,
+ "mx4200_start: ioctl(%s, TCGETA): %m", mx4200dev);
+ goto screwed;
+ }
+ ttyb.c_iflag = IGNBRK|IGNPAR|ICRNL;
+ ttyb.c_oflag = 0;
+ ttyb.c_cflag = SPEED232|CS8|CLOCAL|CREAD;
+ ttyb.c_lflag = ICANON;
+ ttyb.c_cc[VERASE] = ttyb.c_cc[VKILL] = '\0';
+ if (ioctl(fd232, TCSETA, &ttyb) < 0) {
+ syslog(LOG_ERR,
+ "mx4200_start: ioctl(%s, TCSETA): %m", mx4200dev);
+ goto screwed;
+ }
+ }
+#endif /* HAVE_SYSV_TTYS */
+#if defined(HAVE_TERMIOS)
+ /*
+ * POSIX serial line parameters (termios interface)
+ *
+ * The MX4200CLK option provides timestamping at the driver level.
+ * It requires the tty_clk streams module.
+ *
+ * The MX4200PPS option provides timestamping at the driver level.
+ * It uses a 1-pps signal and level converter (gadget box) and
+ * requires the ppsclock streams module and SunOS 4.1.1 or
+ * later.
+ */
+ { struct termios ttyb, *ttyp;
+
+ ttyp = &ttyb;
+ if (tcgetattr(fd232, ttyp) < 0) {
+ syslog(LOG_ERR,
+ "mx4200_start: tcgetattr(%s): %m", mx4200dev);
+ goto screwed;
+ }
+ ttyp->c_iflag = IGNBRK|IGNPAR|ICRNL;
+ ttyp->c_oflag = 0;
+ ttyp->c_cflag = SPEED232|CS8|CLOCAL|CREAD;
+ ttyp->c_lflag = ICANON;
+ ttyp->c_cc[VERASE] = ttyp->c_cc[VKILL] = '\0';
+ if (tcsetattr(fd232, TCSANOW, ttyp) < 0) {
+ syslog(LOG_ERR,
+ "mx4200_start: tcsetattr(%s): %m", mx4200dev);
+ goto screwed;
+ }
+ if (tcflush(fd232, TCIOFLUSH) < 0) {
+ syslog(LOG_ERR,
+ "mx4200_start: tcflush(%s): %m", mx4200dev);
+ goto screwed;
+ }
+ }
+#endif /* HAVE_TERMIOS */
+#ifdef STREAM
+#if defined(MX4200CLK)
+ if (ioctl(fd232, I_PUSH, "clk") < 0)
+ syslog(LOG_ERR,
+ "mx4200_start: ioctl(%s, I_PUSH, clk): %m", mx4200dev);
+ if (ioctl(fd232, CLK_SETSTR, "\n") < 0)
+ syslog(LOG_ERR,
+ "mx4200_start: ioctl(%s, CLK_SETSTR): %m", mx4200dev);
+#endif /* MX4200CLK */
+#if defined(MX4200PPS)
+ if (ioctl(fd232, I_PUSH, "ppsclock") < 0)
+ syslog(LOG_ERR,
+ "mx4200_start: ioctl(%s, I_PUSH, ppsclock): %m", mx4200dev);
+ else
+ fdpps = fd232;
+#endif /* MX4200PPS */
+#endif /* STREAM */
+#if defined(HAVE_BSD_TTYS)
+ /*
+ * 4.3bsd serial line parameters (sgttyb interface)
+ *
+ * The MX4200CLK option provides timestamping at the driver level.
+ * It requires the tty_clk line discipline and 4.3bsd or later.
+ */
+ { struct sgttyb ttyb;
+#if defined(MX4200CLK)
+ int ldisc = CLKLDISC;
+#endif /* MX4200CLK */
+
+ if (ioctl(fd232, TIOCGETP, &ttyb) < 0) {
+ syslog(LOG_ERR,
+ "mx4200_start: ioctl(%s, TIOCGETP): %m", mx4200dev);
+ goto screwed;
+ }
+ ttyb.sg_ispeed = ttyb.sg_ospeed = SPEED232;
+#if defined(MX4200CLK)
+ ttyb.sg_erase = ttyb.sg_kill = '\r';
+ ttyb.sg_flags = RAW;
+#else
+ ttyb.sg_erase = ttyb.sg_kill = '\0';
+ ttyb.sg_flags = EVENP|ODDP|CRMOD;
+#endif /* MX4200CLK */
+ if (ioctl(fd232, TIOCSETP, &ttyb) < 0) {
+ syslog(LOG_ERR,
+ "mx4200_start: ioctl(%s, TIOCSETP): %m", mx4200dev);
+ goto screwed;
+ }
+#if defined(MX4200CLK)
+ if (ioctl(fd232, TIOCSETD, &ldisc) < 0) {
+ syslog(LOG_ERR,
+ "mx4200_start: ioctl(%s, TIOCSETD): %m",mx4200dev);
+ goto screwed;
+ }
+#endif /* MX4200CLK */
+ }
+#endif /* HAVE_BSD_TTYS */
+
+ /*
+ * Allocate unit structure
+ */
+ if (mx4200units[unit] != 0) {
+ mx4200 = mx4200units[unit]; /* The one we want is okay */
+ } else {
+ for (i = 0; i < MAXUNITS; i++) {
+ if (!unitinuse[i] && mx4200units[i] != 0)
+ break;
+ }
+ if (i < MAXUNITS) {
+ /*
+ * Reclaim this one
+ */
+ mx4200 = mx4200units[i];
+ mx4200units[i] = 0;
+ } else {
+ mx4200 = (struct mx4200unit *)
+ emalloc(sizeof(struct mx4200unit));
+ }
+ }
+
+ memset((char *)mx4200, 0, sizeof(struct mx4200unit));
+ mx4200units[unit] = mx4200;
+
+ /*
+ * Set up the structures
+ */
+ mx4200->peer = peer;
+ mx4200->unit = (u_char)unit;
+ mx4200->timestarted = current_time;
+
+ mx4200->io.clock_recv = mx4200_receive;
+ mx4200->io.srcclock = (caddr_t)mx4200;
+ mx4200->io.datalen = 0;
+ mx4200->io.fd = fd232;
+ if (!io_addclock(&mx4200->io))
+ goto screwed;
+
+ /*
+ * All done. Initialize a few random peer variables, then
+ * return success.
+ */
+ peer->precision = MX4200PRECISION;
+ peer->rootdelay = 0;
+ peer->rootdispersion = 0;
+ peer->stratum = stratumtouse[unit];
+ peer->refid = refid[unit];
+ unitinuse[unit] = 1;
+
+ /* Insure the receiver is properly configured */
+ mx4200_config(mx4200);
+
+#ifdef DEBUG
+ opendfile(0);
+#endif
+ return (1);
+
+ /*
+ * Something broke; abandon ship
+ */
+screwed:
+ (void) close(fd232);
+ return (0);
+}
+
+/*
+ * mx4200_shutdown - shut down a MX4200 clock
+ */
+static void
+mx4200_shutdown(unit, peer)
+ int unit;
+ struct peer *peer;
+{
+ register struct mx4200unit *mx4200;
+
+ if (unit >= MAXUNITS) {
+ syslog(LOG_ERR, "mx4200_shutdown: unit %d invalid", unit);
+ return;
+ }
+ if (!unitinuse[unit]) {
+ syslog(LOG_ERR, "mx4200_shutdown: unit %d not in use", unit);
+ return;
+ }
+
+ /*
+ * Tell the I/O module to turn us off. We're history.
+ */
+ mx4200 = mx4200units[unit];
+ io_closeclock(&mx4200->io);
+ unitinuse[unit] = 0;
+}
+
+static void
+mx4200_config(mx4200)
+ register struct mx4200unit *mx4200;
+{
+ register int fd = mx4200->io.fd;
+
+syslog(LOG_DEBUG, "mx4200_config");
+
+ /* Zero the output list (do it twice to flush possible junk) */
+ mx4200_send(fd, "%s,%03d,,%d,,,,,,", pmvxg, PMVXG_S_PORTCONF, 1);
+ mx4200_send(fd, "%s,%03d,,%d,,,,,,", pmvxg, PMVXG_S_PORTCONF, 1);
+
+ /* Switch to 2d mode */
+ mx4200_send(fd, "%s,%03d,%d,,%.1f,%.1f,,%d,%d,%c,%d",
+ pmvxg, PMVXG_S_INITMODEB,
+ 2, /* 2d mode */
+ 0.1, /* hor accel fact as per Steve */
+ 0.1, /* ver accel fact as per Steve */
+ 10, /* hdop limit as per Steve */
+ 5, /* elevation limit as per Steve */
+ 'U', /* time output mode */
+ 0); /* local time offset from gmt */
+
+ /* Configure time recovery */
+ mx4200_send(fd, "%s,%03d,%c,%c,%c,%d,%d,%d,",
+ pmvxg, PMVXG_S_TRECOVCONF,
+#ifdef notdef
+ 'K', /* known position */
+ 'D', /* dynamic position */
+#else
+ 'S', /* static position */
+#endif
+ 'U', /* steer clock to gps time */
+ 'A', /* always output time pulse */
+ 500, /* max time error in ns */
+ 0, /* user bias in ns */
+ 1); /* output to control port */
+}
+
+/*
+ * mx4200_report_event - note the occurrence of an event
+ */
+static void
+mx4200_report_event(mx4200, code)
+ struct mx4200unit *mx4200;
+ int code;
+{
+ struct peer *peer;
+
+ peer = mx4200->peer;
+ if (mx4200->status != (u_char)code) {
+ mx4200->status = (u_char)code;
+ if (code != CEVNT_NOMINAL)
+ mx4200->lastevent = (u_char)code;
+ syslog(LOG_INFO,
+ "mx4200 clock %s event %x", ntoa(&peer->srcadr), code);
+ }
+}
+
+/*
+ * mx4200_poll - mx4200 watchdog routine
+ */
+static void
+mx4200_poll(unit, peer)
+ int unit;
+ struct peer *peer;
+{
+ register struct mx4200unit *mx4200;
+
+ if (unit >= MAXUNITS) {
+ syslog(LOG_ERR, "mx4200_poll: unit %d invalid", unit);
+ return;
+ }
+ if (!unitinuse[unit]) {
+ syslog(LOG_ERR, "mx4200_poll: unit %d not used", unit);
+ return;
+ }
+
+ mx4200 = mx4200units[unit];
+ if ((current_time - mx4200->lasttime) > 150) {
+ mx4200_event(mx4200, CEVNT_FAULT);
+
+ /* Request a status message which should trigger a reconfig */
+ mx4200_send(mx4200->io.fd, "%s,%03d", "CDGPQ", PMVXG_D_STATUS);
+ syslog(LOG_DEBUG, "mx4200_poll: request status");
+ }
+}
+
+static const char char2hex[] = "0123456789ABCDEF";
+
+/*
+ * mx4200_receive - receive gps data
+ */
+static void
+mx4200_receive(rbufp)
+ struct recvbuf *rbufp;
+{
+ register struct mx4200unit *mx4200;
+ register char *dpt, *cp;
+ register U_LONG tmp_ui;
+ register U_LONG tmp_uf;
+ register U_LONG gpstime;
+ struct ppsclockev ev;
+ register struct calendar *jt;
+ struct calendar sjt;
+ register int n;
+ int valid, leapsec;
+ register u_char ck;
+
+ mx4200 = (struct mx4200unit *)rbufp->recv_srcclock;
+
+#ifdef DEBUG
+ if (debug > 3)
+ printf("mx4200_receive: nsamples = %d\n", mx4200->nsamples);
+#endif
+
+ /* Record the time of this event */
+ mx4200->lasttime = current_time;
+
+ /* Get the pps value */
+ if (ioctl(mx4200->io.fd, CIOGETEV, (char *)&ev) < 0) {
+ /* XXX Actually, if this fails, we're pretty much screwed */
+#ifdef DEBUG
+ if (debug) {
+ fprintf(stderr, "mx4200_receive: ");
+ perror("CIOGETEV");
+ }
+#endif
+ mx4200->reason = PPSREASON + 1;
+ mx4200_event(mx4200, CEVNT_FAULT);
+ mx4200_reset(mx4200);
+ return;
+ }
+ tmp_ui = ev.tv.tv_sec + (U_LONG)JAN_1970;
+ TVUTOTSF(ev.tv.tv_usec, tmp_uf);
+
+ /* Get buffer and length; sock away last timecode */
+ n = rbufp->recv_length;
+ dpt = rbufp->recv_buffer;
+ if (n <= 1)
+ return;
+ mx4200->lencode = n;
+ memmove(mx4200->lastcode, dpt, n);
+
+ /*
+ * We expect to see something like:
+ *
+ * $PMVXG,830,T,1992,07,09,04:18:34,U,S,-02154,00019,000000,00*1D\n
+ *
+ * Reject if any important landmarks are missing.
+ */
+ cp = dpt + n - 4;
+ if (cp < dpt || *dpt != '$' || cp[0] != '*' || cp[3] != '\n') {
+#ifdef DEBUG
+ if (debug)
+ printf("mx4200_receive: bad format\n");
+#endif
+ mx4200->badformat++;
+ mx4200->reason = PPSREASON + 2;
+ mx4200_event(mx4200, CEVNT_BADREPLY);
+ mx4200_reset(mx4200);
+ return;
+ }
+
+ /* Check checksum */
+ ck = cksum(&dpt[1], n - 5);
+ if (char2hex[ck >> 4] != cp[1] || char2hex[ck & 0xf] != cp[2]) {
+#ifdef DEBUG
+ if (debug)
+ printf("mx4200_receive: bad checksum\n");
+#endif
+ mx4200->badformat++;
+ mx4200->reason = PPSREASON + 3;
+ mx4200_event(mx4200, CEVNT_BADREPLY);
+ mx4200_reset(mx4200);
+ return;
+ }
+
+ /* Truncate checksum (and the buffer for that matter) */
+ *cp = '\0';
+
+ /* Leap second debugging stuff */
+ if ((leap_hoursfromleap && !leap_happened) || leap_debug > 0) {
+ /* generate reports for awhile after leap */
+ if (leap_hoursfromleap && !leap_happened)
+ leap_debug = 3600;
+ else
+ --leap_debug;
+ syslog(LOG_INFO, "mx4200 leap: %s \"%s\"",
+ umfptoa(tmp_ui, tmp_uf, 6), dpt);
+ }
+
+ /* Parse time recovery message */
+ jt = &sjt;
+ if ((cp = mx4200_parse(dpt, jt, &valid, &leapsec)) != NULL) {
+ /* Configure the receiver if necessary */
+ if (mx4200_needconf(dpt))
+ mx4200_config(mx4200);
+#ifdef DEBUG
+ if (debug)
+ printf("mx4200_receive: mx4200_parse: %s\n", cp);
+#endif
+ mx4200->badformat++;
+ mx4200->reason = PPSREASON + 5;
+ mx4200_event(mx4200, CEVNT_BADREPLY);
+ mx4200_reset(mx4200);
+ return;
+ }
+
+ /* Setup leap second indicator */
+ if (leapsec == 0)
+ mx4200->leap = LEAP_NOWARNING;
+ else if (leapsec == 1)
+ mx4200->leap = LEAP_ADDSECOND;
+ else if (leapsec == -1)
+ mx4200->leap = LEAP_DELSECOND;
+ else
+ mx4200->leap = LEAP_NOTINSYNC; /* shouldn't happen */
+
+ /* Check parsed time (allow for possible leap seconds) */
+ if (jt->second >= 61 || jt->minute >= 60 || jt->hour >= 24) {
+#ifdef DEBUG
+ if (debug) {
+ printf("mx4200_receive: bad time %d:%02d:%02d",
+ jt->hour, jt->minute, jt->second);
+ if (leapsec != 0)
+ printf(" (leap %+d)", leapsec);
+ putchar('\n');
+ }
+#endif
+ mx4200->baddata++;
+ mx4200->reason = PPSREASON + 6;
+ mx4200_event(mx4200, CEVNT_BADTIME);
+ mx4200_reset(mx4200);
+ /* Eat the next pulse which the clock claims will be bad */
+ mx4200->nsamples = -1;
+ return;
+ }
+
+ /* Check parsed date */
+ if (jt->monthday > 31 || jt->month > 12 || jt->year < 1900) {
+#ifdef DEBUG
+ if (debug)
+ printf("mx4200_receive: bad date (%d/%d/%d)\n",
+ jt->monthday, jt->month, jt->year);
+#endif
+ mx4200->baddata++;
+ mx4200->reason = PPSREASON + 7;
+ mx4200_event(mx4200, CEVNT_BADDATE);
+ mx4200_reset(mx4200);
+ return;
+ }
+
+ /* Convert to ntp time */
+ gpstime = caltontp(jt);
+
+ /* The gps message describes the *next* pulse; pretend it's this one */
+ --gpstime;
+
+ /* Debugging */
+#ifdef DEBUG
+ checkdfile();
+ if (df != NULL) {
+ l_fp t;
+
+ t.l_ui = gpstime;
+ t.l_uf = 0;
+ M_SUB(t.l_ui, t.l_uf, tmp_ui, tmp_uf);
+ fprintf(df, "%s\t%s",
+ umfptoa(tmp_ui, tmp_uf, 6), mfptoa(t.l_ui, t.l_uf, 6));
+ if (debug > 3)
+ fprintf(df, "\t(gps: %lu)", (u_long)gpstime);
+ if (leapsec != 0)
+ fprintf(df, "\t(leap sec %+d)", leapsec);
+ if (!valid)
+ fprintf(df, "\t(pulse not valid)");
+ fputc('\n', df);
+ }
+#endif
+
+ /* Check pps serial number against last one */
+ if (mx4200->lastserial + 1 != ev.serial && mx4200->lastserial != 0) {
+#ifdef DEBUG
+ if (debug) {
+ if (ev.serial == mx4200->lastserial)
+ printf("mx4200_receive: no new pps event\n");
+ else
+ printf("mx4200_receive: missed %d pps events\n",
+ ev.serial - mx4200->lastserial - 1);
+ }
+#endif
+ mx4200->reason = PPSREASON + 8;
+ mx4200_event(mx4200, CEVNT_FAULT);
+ mx4200_reset(mx4200);
+ /* fall through and this one collect as first sample */
+ }
+ mx4200->lastserial = ev.serial;
+
+/*
+ * XXX
+ * Since this message is for the next pulse, it's really the next pulse
+ * that the clock might be telling us will be invalid.
+ */
+ /* Toss if not designated "valid" by the gps */
+ if (!valid) {
+#ifdef DEBUG
+ if (debug)
+ printf("mx4200_receive: pps not valid\n");
+#endif
+ mx4200->reason = PPSREASON + 9;
+ mx4200_event(mx4200, CEVNT_BADTIME);
+ mx4200_reset(mx4200);
+ return;
+ }
+
+ /* Copy time into mx4200unit struct */
+ /* XXX (why?) */
+ mx4200->year = jt->year;
+ mx4200->monthday = jt->monthday;
+ mx4200->hour = jt->hour;
+ mx4200->minute = jt->minute;
+ mx4200->second = jt->second;
+
+ /* Sock away the GPS and UNIX timesamples */
+ n = mx4200->nsamples++;
+ if (n < 0)
+ return; /* oops, this pulse is bad */
+ mx4200->gpssamples[n] = gpstime;
+ mx4200->unixsamples[n].l_ui = mx4200->lastsampletime.l_ui = tmp_ui;
+ mx4200->unixsamples[n].l_uf = mx4200->lastsampletime.l_uf = tmp_uf;
+ if (mx4200->nsamples >= NSTAMPS) {
+ /*
+ * Here we've managed to complete an entire NSTAMPS
+ * second cycle without major mishap. Process what has
+ * been received.
+ */
+ mx4200_process(mx4200);
+ mx4200_reset(mx4200);
+ }
+}
+
+/* Compare two l_fp's, used with qsort() */
+static int
+mx4200_cmpl_fp(p1, p2)
+ register void *p1, *p2;
+{
+
+ if (!L_ISGEQ((l_fp *)p1, (l_fp *)p2))
+ return (-1);
+ if (L_ISEQU((l_fp *)p1, (l_fp *)p2))
+ return (0);
+ return (1);
+}
+
+/*
+ * mx4200_process - process a pile of samples from the clock
+ */
+static void
+mx4200_process(mx4200)
+ struct mx4200unit *mx4200;
+{
+ register int i, n;
+ register l_fp *fp, *op;
+ register U_LONG *lp;
+ l_fp off[NSTAMPS];
+ register U_LONG tmp_ui, tmp_uf;
+ register U_LONG date_ui, date_uf;
+ u_fp dispersion;
+
+ /* Compute offsets from the raw data. */
+ fp = mx4200->unixsamples;
+ op = off;
+ lp = mx4200->gpssamples;
+ for (i = 0; i < NSTAMPS; ++i, ++lp, ++op, ++fp) {
+ op->l_ui = *lp;
+ op->l_uf = 0;
+ L_SUB(op, fp);
+ }
+
+ /* Sort offsets into ascending order. */
+ qsort((char *)off, NSTAMPS, sizeof(l_fp), mx4200_cmpl_fp);
+
+ /*
+ * Reject the furthest from the median until 8 samples left
+ */
+ i = 0;
+ n = NSTAMPS;
+ while ((n - i) > 8) {
+ tmp_ui = off[n-1].l_ui;
+ tmp_uf = off[n-1].l_uf;
+ date_ui = off[(n+i)/2].l_ui;
+ date_uf = off[(n+i)/2].l_uf;
+ M_SUB(tmp_ui, tmp_uf, date_ui, date_uf);
+ M_SUB(date_ui, date_uf, off[i].l_ui, off[i].l_uf);
+ if (M_ISHIS(date_ui, date_uf, tmp_ui, tmp_uf)) {
+ /*
+ * reject low end
+ */
+ i++;
+ } else {
+ /*
+ * reject high end
+ */
+ n--;
+ }
+ }
+
+ /*
+ * Compute the dispersion based on the difference between the
+ * extremes of the remaining offsets.
+ */
+ tmp_ui = off[n-1].l_ui;
+ tmp_uf = off[n-1].l_uf;
+ M_SUB(tmp_ui, tmp_uf, off[i].l_ui, off[i].l_uf);
+ dispersion = MFPTOFP(tmp_ui, tmp_uf);
+
+ /*
+ * Now compute the offset estimate. If the sloppy clock
+ * flag is set, average the remainder, otherwise pick the
+ * median.
+ */
+ if (sloppyclockflag[mx4200->unit]) {
+ tmp_ui = tmp_uf = 0;
+ while (i < n) {
+ M_ADD(tmp_ui, tmp_uf, off[i].l_ui, off[i].l_uf);
+ i++;
+ }
+ M_RSHIFT(tmp_ui, tmp_uf);
+ M_RSHIFT(tmp_ui, tmp_uf);
+ M_RSHIFT(tmp_ui, tmp_uf);
+ i = 0;
+ off[0].l_ui = tmp_ui;
+ off[0].l_uf = tmp_uf;
+ } else {
+ i = (n + i) / 2;
+ }
+
+ /*
+ * Add the default MX4200 QT delay into this.
+ */
+#ifdef notdef
+ L_ADDUF(&off[i], MX4200QTFUDGE);
+#endif
+
+ /*
+ * Done. Use lastref as the reference time and lastrec
+ * as the receive time. ** note this can result in tossing
+ * out the peer in the protocol module if lastref > lastrec,
+ * so last rec is used for both values - dlm ***
+ */
+ refclock_receive(mx4200->peer, &off[i],
+ (s_fp)0, /* delay */
+ dispersion,
+ &mx4200->unixsamples[NSTAMPS-1], /* reftime */
+ &mx4200->unixsamples[NSTAMPS-1], /* rectime */
+ mx4200->leap);
+
+ mx4200_event(mx4200, CEVNT_NOMINAL);
+}
+
+/*
+ * mx4200_control - set fudge factors, return statistics
+ */
+static void
+mx4200_control(unit, in, out)
+ int unit;
+ struct refclockstat *in;
+ struct refclockstat *out;
+{
+ register struct mx4200unit *mx4200;
+
+ if (unit >= MAXUNITS) {
+ syslog(LOG_ERR, "mx4200_control: unit %d invalid", unit);
+ return;
+ }
+
+ if (in != 0) {
+ if (in->haveflags & CLK_HAVETIME1)
+ fudgefactor[unit] = in->fudgetime1;
+ if (in->haveflags & CLK_HAVEVAL1)
+ stratumtouse[unit] = (u_char)(in->fudgeval1);
+ if (in->haveflags & CLK_HAVEVAL2)
+ refid[unit] = in->fudgeval2;
+ if (in->haveflags & CLK_HAVEFLAG1)
+ sloppyclockflag[unit] = in->flags & CLK_FLAG1;
+ if (unitinuse[unit]) {
+ struct peer *peer;
+
+ peer = mx4200units[unit]->peer;
+ peer->stratum = stratumtouse[unit];
+ peer->refid = refid[unit];
+ }
+ }
+
+ if (out != 0) {
+ memset((char *)out, 0, sizeof (struct refclockstat));
+ out->type = REFCLK_GPS_MX4200;
+ out->haveflags = CLK_HAVETIME1 | CLK_HAVEVAL1 | CLK_HAVEVAL2 |
+ CLK_HAVEFLAG1;
+ out->clockdesc = MX4200DESCRIPTION;
+ out->fudgetime1 = fudgefactor[unit];
+ out->fudgeval1 = (LONG)stratumtouse[unit];
+ out->fudgeval2 = refid[unit];;
+ out->flags = sloppyclockflag[unit];
+ if (unitinuse[unit]) {
+ mx4200 = mx4200units[unit];
+ out->lencode = mx4200->lencode;
+ out->lastcode = mx4200->lastcode;
+ out->lastevent = mx4200->lastevent;
+ out->currentstatus = mx4200->status;
+
+ out->polls = 0; /* mx4200->polls; */
+ out->noresponse = 0; /* mx4200->noresponse; */
+ out->badformat = mx4200->badformat;
+ out->baddata = mx4200->baddata;
+ out->timereset = current_time - mx4200->timestarted;
+ }
+ }
+}
+
+/*
+ * mx4200_buginfo - return clock dependent debugging info
+ */
+static void
+mx4200_buginfo(unit, bug)
+ int unit;
+ register struct refclockbug *bug;
+{
+ register struct mx4200unit *mx4200;
+
+ if (unit >= MAXUNITS) {
+ syslog(LOG_ERR, "mx4200_buginfo: unit %d invalid", unit);
+ return;
+ }
+
+ if (!unitinuse[unit])
+ return;
+ mx4200 = mx4200units[unit];
+
+ memset((char *)bug, 0, sizeof(*bug));
+ bug->nvalues = 10;
+ bug->ntimes = 2;
+ if (mx4200->lasttime != 0)
+ bug->values[0] = current_time - mx4200->lasttime;
+ else
+ bug->values[0] = 0;
+ bug->values[1] = (U_LONG)mx4200->reason;
+ bug->values[2] = (U_LONG)mx4200->year;
+ bug->values[3] = (U_LONG)mx4200->monthday;
+ bug->values[4] = (U_LONG)mx4200->hour;
+ bug->values[5] = (U_LONG)mx4200->minute;
+ bug->values[6] = (U_LONG)mx4200->second;
+#ifdef notdef
+ bug->values[7] = mx4200->msec;
+ bug->values[8] = mx4200->noreply;
+ bug->values[9] = mx4200->yearstart;
+#endif
+ bug->stimes = 0x1c;
+#ifdef notdef
+ bug->times[0] = mx4200->lastref;
+ bug->times[1] = mx4200->lastrec;
+#endif
+}
+
+/*
+ * Returns true if the this is a status message. We use this as
+ * an indication that the receiver needs to be initialized.
+ */
+static int
+mx4200_needconf(buf)
+ char *buf;
+{
+ register LONG v;
+ char *cp;
+
+ cp = buf;
+
+ if ((cp = strchr(cp, ',')) == NULL)
+ return (0);
+ ++cp;
+
+ /* Record type */
+ v = strtol(cp, &cp, 10);
+ if (v != PMVXG_D_STATUS)
+ return (0);
+ /*
+ * XXX
+ * Since we configure the receiver to not give us status
+ * messages and since the receiver outputs status messages by
+ * default after being reset to factory defaults when sent the
+ * "$PMVXG,018,C\r\n" message, any status message we get
+ * indicates the reciever needs to be initialized; thus, it is
+ * not necessary to decode the status message.
+ */
+#ifdef notdef
+ ++cp;
+
+ /* Receiver status */
+ if ((cp = strchr(cp, ',')) == NULL)
+ return (0);
+ ++cp;
+
+ /* Number of satellites which should be visible */
+ if ((cp = strchr(cp, ',')) == NULL)
+ return (0);
+ ++cp;
+
+ /* Number of satellites being tracked */
+ if ((cp = strchr(cp, ',')) == NULL)
+ return (0);
+ ++cp;
+
+ /* Time since last NAV */
+ if ((cp = strchr(cp, ',')) == NULL)
+ return (0);
+ ++cp;
+
+ /* Initialization status */
+ v = strtol(cp, &cp, 10);
+ if (v == 0)
+#endif
+ return (1);
+}
+
+/* Parse a mx4200 time recovery message. Returns a string if error */
+static char *
+mx4200_parse(buf, jt, validp, leapsecp)
+ register char *buf;
+ register struct calendar *jt;
+ register int *validp, *leapsecp;
+{
+ register LONG v;
+ char *cp;
+
+ cp = buf;
+ memset((char *)jt, 0, sizeof(*jt));
+
+ if ((cp = strchr(cp, ',')) == NULL)
+ return ("no rec-type");
+ ++cp;
+
+ /* Record type */
+ v = strtol(cp, &cp, 10);
+ if (v != PMVXG_D_TRECOVOUT)
+ return ("wrong rec-type");
+
+ /* Pulse valid indicator */
+ if (*cp++ != ',')
+ return ("no pulse-valid");
+ if (*cp == 'T')
+ *validp = 1;
+ else if (*cp == 'F')
+ *validp = 0;
+ else
+ return ("bad pulse-valid");
+ ++cp;
+
+ /* Year */
+ if (*cp++ != ',')
+ return ("no year");
+ jt->year = strtol(cp, &cp, 10);
+
+ /* Month of year */
+ if (*cp++ != ',')
+ return ("no month");
+ jt->month = strtol(cp, &cp, 10);
+
+ /* Day of month */
+ if (*cp++ != ',')
+ return ("no month day");
+ jt->monthday = strtol(cp, &cp, 10);
+
+ /* Hour */
+ if (*cp++ != ',')
+ return ("no hour");
+ jt->hour = strtol(cp, &cp, 10);
+
+ /* Minute */
+ if (*cp++ != ':')
+ return ("no minute");
+ jt->minute = strtol(cp, &cp, 10);
+
+ /* Second */
+ if (*cp++ != ':')
+ return ("no second");
+ jt->second = strtol(cp, &cp, 10);
+
+ /* Time indicator */
+ if (*cp++ != ',' || *cp++ == '\0')
+ return ("no time indicator");
+
+ /* Time recovery mode */
+ if (*cp++ != ',' || *cp++ == '\0')
+ return ("no time mode");
+
+ /* Oscillator offset */
+ if ((cp = strchr(cp, ',')) == NULL)
+ return ("no osc off");
+ ++cp;
+
+ /* Time mark error */
+ if ((cp = strchr(cp, ',')) == NULL)
+ return ("no time mark err");
+ ++cp;
+
+ /* User time bias */
+ if ((cp = strchr(cp, ',')) == NULL)
+ return ("no user bias");
+ ++cp;
+
+ /* Leap second flag */
+ if ((cp = strchr(cp, ',')) == NULL)
+ return ("no leap");
+ ++cp;
+ *leapsecp = strtol(cp, &cp, 10);
+
+ return (NULL);
+}
+
+/* Calculate the checksum */
+static u_char
+cksum(cp, n)
+ register char *cp;
+ register u_int n;
+{
+ register u_char ck;
+
+ for (ck = 0; n-- > 0; ++cp)
+ ck ^= *cp;
+ return (ck);
+}
+
+static void
+#if __STDC__
+mx4200_send(register int fd, const char *fmt, ...)
+#else
+mx4200_send(fd, fmt, va_alist)
+ register int fd;
+ const char *fmt;
+ va_dcl
+#endif
+{
+ register char *cp;
+ register int n, m;
+ va_list ap;
+ char buf[1024];
+ u_char ck;
+
+#if __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+ cp = buf;
+ *cp++ = '$';
+#ifdef notdef
+ /* BSD is rational */
+ n = vsnprintf(cp, sizeof(buf) - 1, fmt, ap);
+#else
+ /* SunOS sucks */
+ (void)vsprintf(cp, fmt, ap);
+ n = strlen(cp);
+#endif
+ ck = cksum(cp, n);
+ cp += n;
+ ++n;
+#ifdef notdef
+ /* BSD is rational */
+ n += snprintf(cp, sizeof(buf) - n - 5, "*%02X\r\n", ck);
+#else
+ /* SunOS sucks */
+ sprintf(cp, "*%02X\r\n", ck);
+ n += strlen(cp);
+#endif
+
+ m = write(fd, buf, n);
+ if (m < 0)
+ syslog(LOG_ERR, "mx4200_send: write: %m (%s)", buf);
+ else if (m != n)
+ syslog(LOG_ERR, "mx4200_send: write: %d != %d (%s)", m, n, buf);
+ va_end(ap);
+}
+#endif
diff --git a/usr.sbin/xntpd/xntpd/refclock_nmea.c b/usr.sbin/xntpd/xntpd/refclock_nmea.c
new file mode 100644
index 0000000..f8ae4bc
--- /dev/null
+++ b/usr.sbin/xntpd/xntpd/refclock_nmea.c
@@ -0,0 +1,391 @@
+/*
+ * refclock_nmea.c - clock driver for an NMEA GPS CLOCK
+ * Michael Petry Jun 20, 1994
+ * based on refclock_heath.c
+ */
+#if defined(REFCLOCK) && defined(NMEA)
+
+#define DEBUG 1
+
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/time.h>
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_stdlib.h"
+
+/*
+ * This driver supports the NMEA GPS Receiver with
+ *
+ * Protype was refclock_trak.c, Thanks a lot.
+ *
+ * The reciever used spits out the NMEA sentences for boat navigation.
+ * And you thought it was an information superhighway. Try a raging river
+ * filled with rapids and whirlpools that rip away your data and warp time.
+ */
+
+/*
+ * Definitions
+ */
+#define DEVICE "/dev/gps%d" /* name of radio device */
+#define SPEED232 B4800 /* uart speed (4800 bps) */
+#define PRECISION (-9) /* precision assumed (about 2 ms) */
+#define REFID "GPS\0" /* reference id */
+#define DESCRIPTION "NMEA GPS Clock" /* who we are */
+
+#define NSAMPLES 3 /* stages of median filter */
+#define LENNMEA 75 /* min timecode length */
+
+/*
+ * Imported from ntp_timer module
+ */
+extern u_long current_time; /* current time (s) */
+
+/*
+ * Imported from ntp_loopfilter module
+ */
+extern int fdpps; /* pps file descriptor */
+
+/*
+ * Imported from ntpd module
+ */
+extern int debug; /* global debug flag */
+
+/*
+ * Tables to compute the ddd of year form icky dd/mm timecode. Viva la
+ * leap.
+ */
+static day1tab[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
+static day2tab[] = {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
+
+/*
+ * Unit control structure
+ */
+struct nmeaunit {
+ int pollcnt; /* poll message counter */
+ l_fp tstamp; /* timestamp of last poll */
+};
+
+/*
+ * Function prototypes
+ */
+static int nmea_start P((int, struct peer *));
+static void nmea_shutdown P((int, struct peer *));
+static void nmea_receive P((struct recvbuf *));
+static void nmea_poll P((int, struct peer *));
+static void gps_send P((int, char *, struct peer *));
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_nmea = {
+ nmea_start, /* start up driver */
+ nmea_shutdown, /* shut down driver */
+ nmea_poll, /* transmit poll message */
+ noentry, /* handle control */
+ noentry, /* initialize driver */
+ noentry, /* buginfo */
+ NOFLAGS /* not used */
+};
+
+/*
+ * nmea_start - open the GPS devices and initialize data for processing
+ */
+static int
+nmea_start(unit, peer)
+ int unit;
+ struct peer *peer;
+{
+ register struct nmeaunit *up;
+ struct refclockproc *pp;
+ int fd;
+ char device[20];
+
+ /*
+ * Open serial port. Use CLK line discipline, if available.
+ */
+ (void)sprintf(device, DEVICE, unit);
+ if (!(fd = refclock_open(device, SPEED232, LDISC_CLK)))
+ return (0);
+
+ /*
+ * Allocate and initialize unit structure
+ */
+ if (!(up = (struct nmeaunit *)
+ emalloc(sizeof(struct nmeaunit)))) {
+ (void) close(fd);
+ return (0);
+ }
+ memset((char *)up, 0, sizeof(struct nmeaunit));
+ pp = peer->procptr;
+ pp->io.clock_recv = nmea_receive;
+ pp->io.srcclock = (caddr_t)peer;
+ pp->io.datalen = 0;
+ pp->io.fd = fd;
+ if (!io_addclock(&pp->io)) {
+ (void) close(fd);
+ free(up);
+ return (0);
+ }
+ pp->unitptr = (caddr_t)up;
+
+ /*
+ * Initialize miscellaneous variables
+ */
+ peer->precision = PRECISION;
+ pp->clockdesc = DESCRIPTION;
+ memcpy((char *)&pp->refid, REFID, 4);
+ up->pollcnt = 2;
+ gps_send(pp->io.fd,"$PMOTG,RMC,0000\n", peer);
+ return (1);
+}
+
+/*
+ * nmea_shutdown - shut down a GPS clock
+ */
+static void
+nmea_shutdown(unit, peer)
+ int unit;
+ struct peer *peer;
+{
+ register struct nmeaunit *up;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ up = (struct nmeaunit *)pp->unitptr;
+ io_closeclock(&pp->io);
+ free(up);
+}
+
+/*
+ * nmea_receive - receive data from the serial interface
+ */
+static void
+nmea_receive(rbufp)
+ struct recvbuf *rbufp;
+{
+ register struct nmeaunit *up;
+ struct refclockproc *pp;
+ struct peer *peer;
+ l_fp trtmp;
+ int month, day;
+ int i;
+ char *cp, *dp;
+ int cmdtype;
+ char *field_parse();
+
+ /*
+ * Initialize pointers and read the timecode and timestamp
+ */
+ peer = (struct peer *)rbufp->recv_srcclock;
+ pp = peer->procptr;
+ up = (struct nmeaunit *)pp->unitptr;
+ pp->lencode = refclock_gtlin(rbufp, pp->lastcode, BMAX, &trtmp);
+
+ /*
+ * There is a case that a <CR><LF> gives back a "blank" line
+ */
+ if (pp->lencode == 0)
+ return;
+
+ /*
+ * We get a buffer and timestamp for each <cr>; however, we use
+ * the timestamp of "now" since this may be a broadcast instead
+ * of a poll. This needs to be checked empeerically
+ */
+ gettstamp(&up->tstamp); /* HACK */
+ pp->lastrec = up->tstamp;
+ up->pollcnt = 2;
+ record_clock_stats(&peer->srcadr, pp->lastcode);
+#ifdef DEBUG
+ if (debug)
+ printf("nmea: timecode %d %s\n", pp->lencode,
+ pp->lastcode);
+#endif
+
+ /*
+ * We check the timecode format and decode its contents. The
+ * we only care about a few of them. The most important being
+ * the $GPRMC format
+ * $GPRMC,hhmmss,a,fddmm.xx,n,dddmmm.xx,w,zz.z,yyy.,ddmmyy,dd,v*CC
+ */
+#define GPRMC 0
+#define GPXXX 1
+ cp = pp->lastcode;
+ pp->leap = 0;
+ cmdtype=0;
+ if(strncmp(cp,"$GPRMC",6)==0) {
+ cmdtype=GPRMC;
+ }
+ else if(strncmp(cp,"$GPXXX",6)==0) {
+ cmdtype=GPXXX;
+ }
+ else
+ return;
+
+ switch( cmdtype ) {
+ case GPRMC:
+ /*
+ * Check time code format of NMEA
+ */
+
+ dp = field_parse(cp,1);
+ if( !isdigit(dp[0]) ||
+ !isdigit(dp[1]) ||
+ !isdigit(dp[2]) ||
+ !isdigit(dp[3]) ||
+ !isdigit(dp[4]) ||
+ !isdigit(dp[5])
+ ) {
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+ break;
+ case GPXXX:
+ return;
+ default:
+ return;
+
+ }
+
+ dp = field_parse(cp,9);
+ /*
+ * Convert date and check values.
+ */
+ day = dp[0] - '0';
+ day = (day * 10) + dp[1] - '0';
+ month = dp[2] - '0';
+ month = (month * 10) + dp[3] - '0';
+ pp->year = dp[4] - '0';
+ pp->year = (pp->year * 10) + dp[5] - '0';
+
+ if (month < 1 || month > 12 || day < 1) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+
+ if (pp->year % 4) {
+ if (day > day1tab[month - 1]) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ for (i = 0; i < month - 1; i++)
+ day += day1tab[i];
+ } else {
+ if (day > day2tab[month - 1]) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ for (i = 0; i < month - 1; i++)
+ day += day2tab[i];
+ }
+ pp->day = day;
+
+ dp = field_parse(cp,1);
+ /*
+ * Convert time and check values.
+ */
+ pp->hour = ((dp[0] - '0') * 10) + dp[1] - '0';
+ pp->minute = ((dp[2] - '0') * 10) + dp[3] - '0';
+ pp->second = ((dp[4] - '0') * 10) + dp[5] - '0';
+ pp->msec = 0;
+
+ if (pp->hour > 23 || pp->minute > 59 || pp->second > 59) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+
+ /*
+ * Test for synchronization Check for quality byte. (soon)
+ */
+ pp->leap = 0;
+ pp->lasttime = current_time;
+
+ /*
+ * Process the new sample in the median filter and determine the
+ * reference clock offset and dispersion. We use lastrec as both
+ * the reference time and receive time, in order to avoid being
+ * cute, like setting the reference time later than the receive
+ * time, which may cause a paranoid protocol module to chuck out
+ * the data.
+ */
+ if (!refclock_process(pp, NSAMPLES, NSAMPLES)) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+
+ refclock_receive(peer, &pp->offset, 0, pp->dispersion,
+ &pp->lastrec, &pp->lastrec, pp->leap);
+}
+
+/*
+ * nmea_poll - called by the transmit procedure
+ *
+ * We go to great pains to avoid changing state here, since there may be
+ * more than one eavesdropper receiving the same timecode.
+ */
+static void
+nmea_poll(unit, peer)
+ int unit;
+ struct peer *peer;
+{
+ register struct nmeaunit *up;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ up = (struct nmeaunit *)pp->unitptr;
+ if (up->pollcnt == 0)
+ refclock_report(peer, CEVNT_TIMEOUT);
+ else
+ up->pollcnt--;
+ pp->polls++;
+
+ /*
+ * usually nmea_receive can get a timestamp every second
+ */
+
+ gps_send(pp->io.fd,"$PMOTG,RMC,0\n", peer);
+}
+
+/*
+ *
+ * gps_send(fd,cmd, peer) Sends a command to the GPS receiver.
+ * as gps_send(fd,"rqts,u\r", peer);
+ *
+ * We don't currently send any data, but would like to send
+ * RTCM SC104 messages for differential positioning. It should
+ * also give us better time. Without a PPS output, we're
+ * Just fooling ourselves because of the serial code paths
+ *
+ */
+static void
+gps_send(fd, cmd, peer)
+ int fd;
+ char *cmd;
+ struct peer *peer;
+{
+
+ if (write(fd, cmd, strlen(cmd)) == -1) {
+ refclock_report(peer, CEVNT_FAULT);
+ }
+}
+
+char *
+field_parse(cp, fn)
+ char *cp;
+ int fn;
+{
+ char *tp;
+ int i = fn;
+
+ for (tp = cp; *tp != '\0'; tp++) {
+ if (*tp == ',')
+ i--;
+ if (i == 0)
+ break;
+ }
+ return (++tp);
+}
+#endif
diff --git a/usr.sbin/xntpd/xntpd/refclock_omega.c b/usr.sbin/xntpd/xntpd/refclock_omega.c
new file mode 100644
index 0000000..1dc31ff
--- /dev/null
+++ b/usr.sbin/xntpd/xntpd/refclock_omega.c
@@ -0,0 +1,999 @@
+/*
+ * refclock_omega - clock driver for the Kinemetrics Truetime OM-DC OMEGA
+ * receiver.
+ *
+ * Version 1.0 11-Dec-92 Steve Clift (clift@ml.csiro.au)
+ * Initial version, mostly lifted from refclock_goes.c.
+ *
+ * 1.1 03-May-93 Steve Clift
+ * Tarted up the sample filtering mechanism to give improved
+ * one-off measurements. Improved measurement dispersion code
+ * to account for accumulated drift when the clock loses lock.
+ *
+ */
+
+#if defined(REFCLOCK) && (defined(OMEGA) || defined(OMEGACLK) || defined(OMEGAPPS))
+
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/time.h>
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_unixtime.h"
+
+#if defined(HAVE_BSD_TTYS)
+#include <sgtty.h>
+#endif /* HAVE_BSD_TTYS */
+
+#if defined(HAVE_SYSV_TTYS)
+#include <termio.h>
+#endif /* HAVE_SYSV_TTYS */
+
+#if defined(HAVE_TERMIOS)
+#include <termios.h>
+#endif
+#if defined(STREAM)
+#include <stropts.h>
+#if defined(OMEGACLK)
+#include <sys/clkdefs.h>
+#endif /* OMEGACLK */
+#endif /* STREAM */
+
+#if defined (OMEGAPPS)
+#include <sys/ppsclock.h>
+#endif /* OMEGAPPS */
+
+#include "ntp_stdlib.h"
+
+/*
+ * Support for Kinemetrics Truetime OM-DC OMEGA Receiver
+ *
+ * Most of this code is copied from refclock_goes.c with thanks.
+ *
+ * the time code looks like follows; Send the clock a R or C and once per
+ * second a timestamp will appear that looks like this:
+ * ADDD:HH:MM:SSQCL
+ * A - control A
+ * Q Quality indication: indicates possible error of
+ * > >+- 5 seconds
+ * ? >+/- 500 milliseconds # >+/- 50 milliseconds
+ * * >+/- 5 milliseconds . >+/- 1 millisecond
+ * A-H less than 1 millisecond. Character indicates which station
+ * is being received as follows:
+ * A = Norway, B = Liberia, C = Hawaii, D = North Dakota,
+ * E = La Reunion, F = Argentina, G = Australia, H = Japan.
+ * C - Carriage return
+ * L - Line feed
+ * The carriage return start bit begins on 0 seconds and extends to 1 bit time.
+ */
+
+/*
+ * Definitions
+ */
+#define MAXUNITS 4 /* max number of OMEGA units */
+#define OMEGA232 "/dev/omega%d"
+#define SPEED232 B9600 /* 9600 baud */
+
+/*
+ * Radio interface parameters
+ */
+#define OMEGADESCRIPTION "Kinemetrics OM-DC OMEGA Receiver" /* who we are */
+#define OMEGAMAXDISPERSE (FP_SECOND/32) /* max allowed sample dispersion */
+#define OMEGAPRECISION (-10) /* precision assumed (about 1 ms) */
+#define OMEGAREFID "VLF\0" /* reference id */
+#define LENOMEGA 13 /* length of standard response */
+#define GMT 0 /* hour offset from Greenwich */
+#define NSTAMPS 9 /* samples collected when polled */
+#define NSKEEP 5 /* samples to keep after discards */
+
+/*
+ * The OM-DC puts out the start bit of the <CR> on the second, but
+ * we see the result after the <LF> is received, about 2ms later at
+ * 9600 baud. Use this as the default fudge time, and let the user
+ * fiddle it to account for driver latency etc.
+ */
+#define DEFFUDGETIME 0x00830000 /* default fudge time (~2ms) */
+
+/*
+ * Clock drift errors as u_fp values.
+ */
+#define U_FP5000MS (5*FP_SECOND) /* 5 seconds */
+#define U_FP500MS (FP_SECOND/2) /* 500 msec */
+#define U_FP50MS (FP_SECOND/20) /* 50 msec */
+#define U_FP5MS (FP_SECOND/200) /* 5 msec */
+
+/*
+ * Station codes
+ */
+#define STATION_NONE 0
+#define STATION_NORWAY 1
+#define STATION_LIBERIA 2
+#define STATION_HAWAII 3
+#define STATION_N_DAKOTA 4
+#define STATION_LA_REUNION 5
+#define STATION_ARGENTINA 6
+#define STATION_AUSTRALIA 7
+#define STATION_JAPAN 8
+
+/*
+ * Hack to avoid excercising the multiplier. I have no pride.
+ */
+#define MULBY10(x) (((x)<<3) + ((x)<<1))
+
+/*
+ * Imported from the timer module
+ */
+extern U_LONG current_time;
+extern struct event timerqueue[];
+
+/*
+ * Imported from ntp_loopfilter module
+ */
+extern int fdpps; /* pps file descriptor */
+
+/*
+ * Imported from ntpd module
+ */
+extern int debug; /* global debug flag */
+
+/*
+ * OMEGA unit control structure
+ */
+struct omegaunit {
+ struct peer *peer; /* associated peer structure */
+ struct refclockio io; /* given to the I/O handler */
+ l_fp lastrec; /* last receive time */
+ l_fp lastref; /* last timecode time */
+ l_fp offset[NSTAMPS]; /* recent sample offsets */
+ char lastcode[BMAX]; /* last timecode received */
+ u_short station; /* which station we're locked to */
+ u_short polled; /* Hand in a time sample? */
+ U_LONG coderecv; /* timecodes received */
+ u_char lencode; /* length of last timecode */
+ U_LONG lasttime; /* last time clock heard from */
+ u_char unit; /* unit number for this guy */
+ u_char status; /* clock status */
+ u_char lastevent; /* last clock event */
+ u_char reason; /* reason for last failure */
+ u_char year; /* year of eternity */
+ u_short day; /* day of year */
+ u_char hour; /* hour of day */
+ u_char minute; /* minute of hour */
+ u_char second; /* seconds of minute */
+ u_char leap; /* leap indicators */
+ u_short msec; /* millisecond of second */
+ u_char quality; /* quality char from last timecode */
+ u_long yearstart; /* start of current year */
+ /*
+ * Status tallies
+ */
+ U_LONG polls; /* polls sent */
+ U_LONG noreply; /* no replies to polls */
+ U_LONG badformat; /* bad format */
+ U_LONG baddata; /* bad data */
+ U_LONG timestarted; /* time we started this */
+};
+
+/*
+ * Data space for the unit structures. Note that we allocate these on
+ * the fly, but never give them back.
+ */
+static struct omegaunit *omegaunits[MAXUNITS];
+static u_char unitinuse[MAXUNITS];
+
+/*
+ * Keep the fudge factors separately so they can be set even
+ * when no clock is configured.
+ */
+static l_fp fudgefactor1[MAXUNITS];
+static l_fp fudgefactor2[MAXUNITS];
+static u_char stratumtouse[MAXUNITS];
+static u_char readonlyclockflag[MAXUNITS];
+static U_LONG refid[MAXUNITS];
+
+/*
+ * Function prototypes
+ */
+static void omega_init P((void));
+static int omega_start P((int, struct peer *));
+static void omega_shutdown P((int, struct peer *));
+static void omega_report_event P((struct omegaunit *, int));
+static void omega_receive P((struct recvbuf *));
+static char omega_process P((struct omegaunit *, l_fp *, u_fp *));
+static void omega_poll P((int, struct peer *));
+static void omega_control P((int, struct refclockstat *, struct refclockstat *));
+static void omega_buginfo P((int, struct refclockbug *));
+static void omega_send P((struct omegaunit *, char *));
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_omega = {
+ omega_start, omega_shutdown, omega_poll,
+ omega_control, omega_init, omega_buginfo, NOFLAGS
+};
+
+/*
+ * omega_init - initialize internal omega driver data
+ */
+static void
+omega_init()
+{
+ register int i;
+ /*
+ * Just zero the data arrays
+ */
+ memset((char *)omegaunits, 0, sizeof omegaunits);
+ memset((char *)unitinuse, 0, sizeof unitinuse);
+
+ /*
+ * Initialize fudge factors to default.
+ */
+ for (i = 0; i < MAXUNITS; i++) {
+ fudgefactor1[i].l_ui = 0;
+ fudgefactor1[i].l_uf = DEFFUDGETIME;
+ fudgefactor2[i].l_ui = 0;
+ fudgefactor2[i].l_uf = 0;
+ stratumtouse[i] = 0;
+ readonlyclockflag[i] = 0;
+ memcpy((char *)&refid[i], OMEGAREFID, 4);
+ }
+}
+
+
+/*
+ * omega_start - open the OMEGA devices and initialize data for processing
+ */
+static int
+omega_start(unit, peer)
+ int unit;
+ struct peer *peer;
+{
+ register struct omegaunit *omega;
+ register int i;
+ int fd232;
+ char omegadev[20];
+
+ /*
+ * Check configuration info
+ */
+ if (unit >= MAXUNITS) {
+ syslog(LOG_ERR,"omega_start: unit %d invalid", unit);
+ return 0;
+ }
+ if (unitinuse[unit]) {
+ syslog(LOG_ERR, "omega_start: unit %d in use", unit);
+ return 0;
+ }
+
+ /*
+ * Open serial port
+ */
+ (void) sprintf(omegadev, OMEGA232, unit);
+ fd232 = open(omegadev, O_RDWR, 0777);
+ if (fd232 == -1) {
+ syslog(LOG_ERR, "omega_start: open of %s: %m", omegadev);
+ return 0;
+ }
+
+#if defined(HAVE_SYSV_TTYS)
+ /*
+ * System V serial line parameters (termio interface)
+ *
+ */
+ { struct termio ttyb;
+ if (ioctl(fd232, TCGETA, &ttyb) < 0) {
+ syslog(LOG_ERR,
+ "omega_start: ioctl(%s, TCGETA): %m", omegadev);
+ goto screwed;
+ }
+ ttyb.c_iflag = IGNBRK|IGNPAR|ICRNL;
+ ttyb.c_oflag = 0;
+ ttyb.c_cflag = SPEED232|CS8|CLOCAL|CREAD;
+ ttyb.c_lflag = ICANON;
+ ttyb.c_cc[VERASE] = ttyb.c_cc[VKILL] = '\0';
+ if (ioctl(fd232, TCSETA, &ttyb) < 0) {
+ syslog(LOG_ERR,
+ "omega_start: ioctl(%s, TCSETA): %m", omegadev);
+ goto screwed;
+ }
+ }
+#endif /* HAVE_SYSV_TTYS */
+#if defined(HAVE_TERMIOS)
+ /*
+ * POSIX serial line parameters (termios interface)
+ *
+ * The OMEGACLK option provides timestamping at the driver level.
+ * It requires the tty_clk streams module.
+ *
+ * The OMEGAPPS option provides timestamping at the driver level.
+ * It uses a 1-pps signal and level converter (gadget box) and
+ * requires the ppsclock streams module and SunOS 4.1.1 or
+ * later.
+ */
+ { struct termios ttyb, *ttyp;
+
+ ttyp = &ttyb;
+ if (tcgetattr(fd232, ttyp) < 0) {
+ syslog(LOG_ERR,
+ "omega_start: tcgetattr(%s): %m", omegadev);
+ goto screwed;
+ }
+ ttyp->c_iflag = IGNBRK|IGNPAR|ICRNL;
+ ttyp->c_oflag = 0;
+ ttyp->c_cflag = SPEED232|CS8|CLOCAL|CREAD;
+ ttyp->c_lflag = ICANON;
+ ttyp->c_cc[VERASE] = ttyp->c_cc[VKILL] = '\0';
+ if (tcsetattr(fd232, TCSANOW, ttyp) < 0) {
+ syslog(LOG_ERR,
+ "omega_start: tcsetattr(%s): %m", omegadev);
+ goto screwed;
+ }
+ if (tcflush(fd232, TCIOFLUSH) < 0) {
+ syslog(LOG_ERR,
+ "omega_start: tcflush(%s): %m", omegadev);
+ goto screwed;
+ }
+ }
+#endif /* HAVE_TERMIOS */
+#ifdef STREAM
+#if defined(OMEGACLK)
+ if (ioctl(fd232, I_PUSH, "clk") < 0)
+ syslog(LOG_ERR,
+ "omega_start: ioctl(%s, I_PUSH, clk): %m", omegadev);
+ if (ioctl(fd232, CLK_SETSTR, "\n") < 0)
+ syslog(LOG_ERR,
+ "omega_start: ioctl(%s, CLK_SETSTR): %m", omegadev);
+#endif /* OMEGACLK */
+#if defined(OMEGAPPS)
+ if (ioctl(fd232, I_PUSH, "ppsclock") < 0)
+ syslog(LOG_ERR,
+ "omega_start: ioctl(%s, I_PUSH, ppsclock): %m", omegadev);
+ else
+ fdpps = fd232;
+#endif /* OMEGAPPS */
+#endif /* STREAM */
+#if defined(HAVE_BSD_TTYS)
+ /*
+ * 4.3bsd serial line parameters (sgttyb interface)
+ *
+ * The OMEGACLK option provides timestamping at the driver level.
+ * It requires the tty_clk line discipline and 4.3bsd or later.
+ */
+ { struct sgttyb ttyb;
+#if defined(OMEGACLK)
+ int ldisc = CLKLDISC;
+#endif /* OMEGACLK */
+
+ if (ioctl(fd232, TIOCGETP, &ttyb) < 0) {
+ syslog(LOG_ERR,
+ "omega_start: ioctl(%s, TIOCGETP): %m", omegadev);
+ goto screwed;
+ }
+ ttyb.sg_ispeed = ttyb.sg_ospeed = SPEED232;
+#if defined(OMEGACLK)
+ ttyb.sg_erase = ttyb.sg_kill = '\r';
+ ttyb.sg_flags = RAW;
+#else
+ ttyb.sg_erase = ttyb.sg_kill = '\0';
+ ttyb.sg_flags = EVENP|ODDP|CRMOD;
+#endif /* OMEGACLK */
+ if (ioctl(fd232, TIOCSETP, &ttyb) < 0) {
+ syslog(LOG_ERR,
+ "omega_start: ioctl(%s, TIOCSETP): %m", omegadev);
+ goto screwed;
+ }
+#if defined(OMEGACLK)
+ if (ioctl(fd232, TIOCSETD, &ldisc) < 0) {
+ syslog(LOG_ERR,
+ "omega_start: ioctl(%s, TIOCSETD): %m",omegadev);
+ goto screwed;
+ }
+#endif /* OMEGACLK */
+ }
+#endif /* HAVE_BSD_TTYS */
+
+ /*
+ * Allocate unit structure
+ */
+ if (omegaunits[unit] != 0) {
+ omega = omegaunits[unit]; /* The one we want is okay */
+ } else {
+ for (i = 0; i < MAXUNITS; i++) {
+ if (!unitinuse[i] && omegaunits[i] != 0)
+ break;
+ }
+ if (i < MAXUNITS) {
+ /*
+ * Reclaim this one
+ */
+ omega = omegaunits[i];
+ omegaunits[i] = 0;
+ } else {
+ omega = (struct omegaunit *)
+ emalloc(sizeof(struct omegaunit));
+ }
+ }
+ memset((char *)omega, 0, sizeof(struct omegaunit));
+ omegaunits[unit] = omega;
+
+ /*
+ * Set up the structures
+ */
+ omega->peer = peer;
+ omega->unit = (u_char)unit;
+ omega->timestarted = current_time;
+ omega->station = STATION_NONE;
+
+ omega->io.clock_recv = omega_receive;
+ omega->io.srcclock = (caddr_t)omega;
+ omega->io.datalen = 0;
+ omega->io.fd = fd232;
+ if (!io_addclock(&omega->io)) {
+ goto screwed;
+ }
+
+ /*
+ * All done. Initialize a few random peer variables, then
+ * return success.
+ */
+ peer->precision = OMEGAPRECISION;
+ peer->rootdelay = 0;
+ peer->rootdispersion = 0;
+ peer->stratum = stratumtouse[unit];
+ peer->refid = refid[unit];
+ unitinuse[unit] = 1;
+ return 1;
+
+ /*
+ * Something broke; abandon ship
+ */
+screwed:
+ (void) close(fd232);
+ return 0;
+}
+
+
+/*
+ * omega_shutdown - shut down a OMEGA clock
+ */
+static void
+omega_shutdown(unit, peer)
+ int unit;
+ struct peer *peer;
+{
+ register struct omegaunit *omega;
+
+ if (unit >= MAXUNITS) {
+ syslog(LOG_ERR, "omega_shutdown: unit %d invalid",
+ unit);
+ return;
+ }
+ if (!unitinuse[unit]) {
+ syslog(LOG_ERR, "omega_shutdown: unit %d not in use", unit);
+ return;
+ }
+
+ /*
+ * Tell the I/O module to turn us off. We're history.
+ */
+ omega = omegaunits[unit];
+ io_closeclock(&omega->io);
+ unitinuse[unit] = 0;
+}
+
+
+/*
+ * omega_report_event - note the occurance of an event
+ */
+static void
+omega_report_event(omega, code)
+ struct omegaunit *omega;
+ int code;
+{
+ struct peer *peer;
+
+ peer = omega->peer;
+ if (omega->status != (u_char)code) {
+ omega->status = (u_char)code;
+ if (code != CEVNT_NOMINAL)
+ omega->lastevent = (u_char)code;
+ syslog(LOG_INFO,
+ "omega clock %s event %x\n", ntoa(&peer->srcadr), code);
+ }
+}
+
+
+/*
+ * omega_receive - receive data from the serial interface on a
+ * Kinemetrics OM-DC OMEGA clock.
+ */
+static void
+omega_receive(rbufp)
+ struct recvbuf *rbufp;
+{
+ register int i;
+ register struct omegaunit *omega;
+ register u_char *dpt;
+ register char *cp, *cpend;
+ register u_char *dpend;
+ l_fp tstmp;
+ u_fp dispersion, drift;
+
+ /*
+ * Get the clock this applies to and a pointers to the data
+ */
+ omega = (struct omegaunit *)rbufp->recv_srcclock;
+ dpt = (u_char *)&rbufp->recv_space;
+
+#ifndef PEDANTIC
+ /*
+ * The OM-DC outputs a timecode every second, but we only want
+ * a set of NSTAMPS timecodes when polled (every 64 seconds).
+ * Setting PEDANTIC causes a sanity check on every timecode.
+ */
+ if (!omega->polled)
+ return;
+#endif
+
+ /*
+ * Edit timecode to remove control chars
+ */
+ dpend = dpt + rbufp->recv_length;
+ cp = omega->lastcode;
+ cpend = omega->lastcode + BMAX - 1;
+ while (dpt < dpend && cp < cpend) {
+ if ((*cp = 0x7f & *dpt++) >= ' ') cp++;
+#ifdef OMEGACLK
+ else if (*cp == '\r') {
+ if (dpend - dpt < 8) {
+ /* short timestamp */
+ return;
+ }
+ if (!buftvtots(dpt,&omega->lastrec)) {
+ /* screwy timestamp */
+ return;
+ }
+ dpt += 8;
+ }
+#endif
+ }
+ *cp = '\0';
+ omega->lencode = cp - omega->lastcode;
+
+ if (omega->lencode == 0)
+ return;
+ else if (omega->lencode != LENOMEGA) {
+ omega->badformat++;
+ /* Sometimes get a lot of these, filling the log with noise */
+ /* omega_report_event(omega, CEVNT_BADREPLY); */
+ return;
+ }
+
+#ifndef OMEGACLK
+ omega->lastrec = rbufp->recv_time;
+#endif
+
+#ifdef DEBUG
+ if (debug)
+ printf("omega: timecode %d %s\n",
+ omega->lencode, omega->lastcode);
+#endif
+
+ /*
+ * We get down to business, check the timecode format
+ * and decode its contents.
+ */
+ cp = omega->lastcode;
+ omega->leap = 0;
+ /*
+ * Check timecode format.
+ */
+ if (!isdigit(cp[0]) || /* day of year */
+ !isdigit(cp[1]) ||
+ !isdigit(cp[2]) ||
+ cp[3] != ':' || /* <sp> */
+ !isdigit(cp[4]) || /* hours */
+ !isdigit(cp[5]) ||
+ cp[6] != ':' || /* : separator */
+ !isdigit(cp[7]) || /* minutes */
+ !isdigit(cp[8]) ||
+ cp[9] != ':' || /* : separator */
+ !isdigit(cp[10]) || /* seconds */
+ !isdigit(cp[11])) {
+ omega->badformat++;
+ omega_report_event(omega, CEVNT_BADREPLY);
+ return;
+ }
+
+ /*
+ * Convert and check values.
+ */
+ omega->year = 0; /* fake */
+ omega->day = cp[0] - '0';
+ omega->day = MULBY10(omega->day) + cp[1] - '0';
+ omega->day = MULBY10(omega->day) + cp[2] - '0';
+ omega->hour = MULBY10(cp[4] - '0') + cp[5] - '0';
+ omega->minute = MULBY10(cp[7] - '0') + cp[8] - '0';
+ omega->second = MULBY10(cp[10] - '0') + cp[11] - '0';
+ omega->msec = 0;
+
+ if (omega->day < 1 || omega->day > 366) {
+ omega->baddata++;
+ omega_report_event(omega, CEVNT_BADDATE);
+ return;
+ }
+ if (omega->hour > 23 || omega->minute > 59 || omega->second > 59) {
+ omega->baddata++;
+ omega_report_event(omega, CEVNT_BADTIME);
+ return;
+ }
+
+ /*
+ * Check quality/station-id flag. The OM-DC should normally stay
+ * permanently locked to a station, and its time error should be less
+ * than 1 msec. If it loses lock for any reason, it makes a worst
+ * case drift estimate based on the internally stored stability figure
+ * for its reference oscillator. The stability figure can be adjusted
+ * by the user based on experience. The default value is 1E05, which
+ * is pretty bad - 2E07 is about right for the unit I have.
+ *
+ * The following is arbitrary, change it if you're offended:
+ * For errors less than 50 msec, just clear the station indicator.
+ * For errors greater than 50 msec, flag loss of sync and report a
+ * propagation problem. If the error is greater than 500 msec,
+ * something is dreadfully wrong - report a clock fault.
+ *
+ * In each case, we set a drift estimate which is used below as an
+ * estimate of measurement accuracy.
+ */
+ omega->quality = cp[12];
+ if (cp[12] == '>' || cp[12] == '?') {
+ /* Error 500 to 5000 msec */
+ omega_report_event(omega, CEVNT_FAULT);
+ omega->leap = LEAP_NOTINSYNC;
+ omega->station = STATION_NONE;
+ drift = U_FP5000MS;
+ } else if (cp[12] == '#') {
+ /* Error 50 to 500 msec */
+ omega_report_event(omega, CEVNT_PROP);
+ omega->leap = LEAP_NOTINSYNC;
+ omega->station = STATION_NONE;
+ drift = U_FP500MS;
+ } else if (cp[12] == '*') {
+ /* Error 5 to 50 msec */
+ omega->lasttime = current_time;
+ omega->station = STATION_NONE;
+ drift = U_FP50MS;
+ } else if (cp[12] == '.') {
+ /* Error 1 to 5 msec */
+ omega->lasttime = current_time;
+ omega->station = STATION_NONE;
+ drift = U_FP5MS;
+ } else if ('A' <= cp[12] && cp[12] <= 'H') {
+ /* Error less than 1 msec */
+ omega->lasttime = current_time;
+ omega->station = cp[12] - 'A' + 1;
+ drift = 0;
+ } else {
+ omega->badformat++;
+ omega_report_event(omega, CEVNT_BADREPLY);
+ return;
+ }
+
+#ifdef PEDANTIC
+ /* If we haven't been polled, bail out. */
+ if (!omega->polled)
+ return;
+#endif
+
+ /*
+ * Now, compute the reference time value. Use the heavy
+ * machinery for the seconds and the millisecond field for the
+ * fraction when present.
+ *
+ * this code does not yet know how to do the years
+ */
+ tstmp = omega->lastrec;
+ if (!clocktime(omega->day, omega->hour, omega->minute,
+ omega->second, GMT, tstmp.l_ui,
+ &omega->yearstart, &omega->lastref.l_ui)) {
+ omega->baddata++;
+ omega_report_event(omega, CEVNT_BADTIME);
+ return;
+ }
+ MSUTOTSF(omega->msec, omega->lastref.l_uf);
+
+ /*
+ * Adjust the read value by fudgefactor1 to correct RS232 delays.
+ */
+ L_ADD(&omega->lastref, &fudgefactor1[omega->unit]);
+
+ /* Carousel of NSTAMPS offsets. */
+ i = omega->coderecv % NSTAMPS;
+ omega->offset[i] = omega->lastref;
+ L_SUB(&omega->offset[i], &tstmp);
+ omega->coderecv++;
+
+ /* If we don't yet have a full set, return. */
+ if (omega->coderecv < NSTAMPS)
+ return;
+
+ /*
+ * Filter the samples, add the fudge factor and pass the
+ * offset and dispersion along. We use lastrec as both the
+ * reference time and receive time in order to avoid being cute,
+ * like setting the reference time later than the receive time,
+ * which may cause a paranoid protocol module to chuck out the
+ * data. If the sample filter chokes because of excessive
+ * dispersion or whatever, get a new sample (omega->coderecv
+ * is still >= NSTAMPS) and try again.
+ */
+ if (!omega_process(omega, &tstmp, &dispersion)) {
+ omega->baddata++;
+ omega_report_event(omega, CEVNT_BADTIME);
+ return;
+ }
+
+ /*
+ * Add accumulated clock drift to the dispersion to get
+ * a (hopefully) meaningful measurement accuracy estimate.
+ */
+ dispersion += drift;
+ refclock_receive(omega->peer, &tstmp, GMT, dispersion,
+ &omega->lastrec, &omega->lastrec, omega->leap);
+
+ /*
+ * We have succeeded in answering the poll. If the clock
+ * is locked, we're nominal.
+ */
+ omega->polled = 0;
+ omega->coderecv = 0;
+ if (omega->leap != LEAP_NOTINSYNC)
+ omega_report_event(omega, CEVNT_NOMINAL);
+}
+
+
+/*
+ * omega_send - time to send the clock a signal to cough up a time sample
+ */
+static void
+omega_send(omega,cmd)
+ struct omegaunit *omega;
+ char *cmd;
+{
+ if (!readonlyclockflag[omega->unit]) {
+ /*
+ * Send a command to the clock.
+ */
+ if (write(omega->io.fd, cmd, 1) != 1) {
+ syslog(LOG_ERR, "omega_send: unit %d: %m", omega->unit);
+ omega_report_event(omega, CEVNT_FAULT);
+ }
+ }
+}
+
+
+/*
+ * Compare two l_fp's, used with qsort()
+ */
+static int
+omega_cmpl_fp(p1, p2)
+ register void *p1, *p2;
+{
+
+ if (!L_ISGEQ((l_fp *)p1, (l_fp *)p2))
+ return (-1);
+ if (L_ISEQU((l_fp *)p1, (l_fp *)p2))
+ return (0);
+ return (1);
+}
+
+
+/*
+ * omega_process - process a pile of samples from the clock
+ */
+static char
+omega_process(omega, offset, dispersion)
+ struct omegaunit *omega;
+ l_fp *offset;
+ u_fp *dispersion;
+{
+ register int i, n;
+ register U_LONG med_ui, med_uf, tmp_ui, tmp_uf;
+ l_fp off[NSTAMPS];
+ u_fp disp;
+
+ /* Copy in offsets and sort into ascending order */
+ for (i = 0; i < NSTAMPS; i++)
+ off[i] = omega->offset[i];
+ qsort((char *)off, NSTAMPS, sizeof(l_fp), omega_cmpl_fp);
+ /*
+ * Reject the furthest from the median until NSKEEP samples remain
+ */
+ i = 0;
+ n = NSTAMPS;
+ while ((n - i) > NSKEEP) {
+ tmp_ui = off[n-1].l_ui;
+ tmp_uf = off[n-1].l_uf;
+ med_ui = off[(n+i)/2].l_ui;
+ med_uf = off[(n+i)/2].l_uf;
+ M_SUB(tmp_ui, tmp_uf, med_ui, med_uf);
+ M_SUB(med_ui, med_uf, off[i].l_ui, off[i].l_uf);
+ if (M_ISHIS(med_ui, med_uf, tmp_ui, tmp_uf)) {
+ /* reject low end */
+ i++;
+ } else {
+ /* reject high end */
+ n--;
+ }
+ }
+
+ /*
+ * Compute the dispersion based on the difference between the
+ * extremes of the remaining offsets. If this is greater than
+ * the allowed sample set dispersion, bail out. Otherwise,
+ * return the median offset and the dispersion.
+ */
+ tmp_ui = off[n-1].l_ui;
+ tmp_uf = off[n-1].l_uf;
+ M_SUB(tmp_ui, tmp_uf, off[i].l_ui, off[i].l_uf);
+ disp = MFPTOFP(tmp_ui, tmp_uf);
+ if (disp > OMEGAMAXDISPERSE)
+ return 0;
+ *offset = off[(n+1)/2];
+ *dispersion = disp;
+ return 1;
+}
+
+
+/*
+ * omega_poll - called by the transmit procedure
+ */
+static void
+omega_poll(unit, peer)
+ int unit;
+ struct peer *peer;
+{
+ struct omegaunit *omega;
+
+ /*
+ * You don't need to poll this clock. It puts out timecodes
+ * once per second. If asked for a timestamp, take note.
+ * The next time a timecode comes in, it will be fed back.
+ */
+ if (unit >= MAXUNITS) {
+ syslog(LOG_ERR, "omega_poll: unit %d invalid", unit);
+ return;
+ }
+ if (!unitinuse[unit]) {
+ syslog(LOG_ERR, "omega_poll: unit %d not in use", unit);
+ return;
+ }
+ omega = omegaunits[unit];
+ if ((current_time - omega->lasttime) > 150) {
+ omega->noreply++;
+ omega_report_event(omegaunits[unit], CEVNT_TIMEOUT);
+ }
+
+ /*
+ * polled every 64 seconds. Ask OMEGA_RECEIVE to hand in a timestamp.
+ */
+ omega->polled = 1;
+ omega->polls++;
+ /*
+ * Ensure the clock is running in the correct mode - on-second
+ * timestamps.
+ */
+ omega_send(omega,"C");
+}
+
+
+/*
+ * omega_control - set fudge factors, return statistics
+ */
+static void
+omega_control(unit, in, out)
+ int unit;
+ struct refclockstat *in;
+ struct refclockstat *out;
+{
+ register struct omegaunit *omega;
+
+ if (unit >= MAXUNITS) {
+ syslog(LOG_ERR, "omega_control: unit %d invalid", unit);
+ return;
+ }
+
+ if (in != 0) {
+ if (in->haveflags & CLK_HAVETIME1)
+ fudgefactor1[unit] = in->fudgetime1;
+ if (in->haveflags & CLK_HAVETIME2)
+ fudgefactor2[unit] = in->fudgetime2;
+ if (in->haveflags & CLK_HAVEVAL1)
+ stratumtouse[unit] = (u_char)(in->fudgeval1);
+ if (in->haveflags & CLK_HAVEVAL2)
+ refid[unit] = in->fudgeval2;
+ if (in->haveflags & CLK_HAVEFLAG1)
+ readonlyclockflag[unit] = in->flags & CLK_FLAG1;
+ if (unitinuse[unit]) {
+ struct peer *peer;
+
+ peer = omegaunits[unit]->peer;
+ peer->stratum = stratumtouse[unit];
+ peer->refid = refid[unit];
+ }
+ }
+
+ if (out != 0) {
+ out->type = REFCLK_OMEGA_TRUETIME;
+ out->haveflags = CLK_HAVETIME1 | CLK_HAVETIME2 | CLK_HAVEVAL1 |
+ CLK_HAVEVAL2| CLK_HAVEFLAG1;
+ out->clockdesc = OMEGADESCRIPTION;
+ out->fudgetime1 = fudgefactor1[unit];
+ out->fudgetime2 = fudgefactor2[unit];
+ out->fudgeval1 = (LONG)stratumtouse[unit];
+ out->fudgeval2 = refid[unit];
+ out->flags = readonlyclockflag[unit];
+ if (unitinuse[unit]) {
+ omega = omegaunits[unit];
+ out->flags |= omega->station << 1;
+ out->lencode = omega->lencode;
+ out->lastcode = omega->lastcode;
+ out->timereset = current_time - omega->timestarted;
+ out->polls = omega->polls;
+ out->noresponse = omega->noreply;
+ out->badformat = omega->badformat;
+ out->baddata = omega->baddata;
+ out->lastevent = omega->lastevent;
+ out->currentstatus = omega->status;
+ }
+ }
+}
+
+
+/*
+ * omega_buginfo - return clock dependent debugging info
+ */
+static void
+omega_buginfo(unit, bug)
+ int unit;
+ register struct refclockbug *bug;
+{
+ register struct omegaunit *omega;
+
+ if (unit >= MAXUNITS) {
+ syslog(LOG_ERR, "omega_buginfo: unit %d invalid", unit);
+ return;
+ }
+
+ if (!unitinuse[unit])
+ return;
+ omega = omegaunits[unit];
+
+ bug->nvalues = 11;
+ bug->ntimes = 5;
+ if (omega->lasttime != 0)
+ bug->values[0] = current_time - omega->lasttime;
+ else
+ bug->values[0] = 0;
+ bug->values[1] = (U_LONG)omega->reason;
+ bug->values[2] = (U_LONG)omega->year;
+ bug->values[3] = (U_LONG)omega->day;
+ bug->values[4] = (U_LONG)omega->hour;
+ bug->values[5] = (U_LONG)omega->minute;
+ bug->values[6] = (U_LONG)omega->second;
+ bug->values[7] = (U_LONG)omega->msec;
+ bug->values[8] = omega->noreply;
+ bug->values[9] = omega->yearstart;
+ bug->values[10] = omega->quality;
+ bug->stimes = 0x1c;
+ bug->times[0] = omega->lastref;
+ bug->times[1] = omega->lastrec;
+ bug->times[2] = omega->offset[0];
+ bug->times[3] = omega->offset[1];
+ bug->times[4] = omega->offset[2];
+}
+#endif
diff --git a/usr.sbin/xntpd/xntpd/refclock_parse.c b/usr.sbin/xntpd/xntpd/refclock_parse.c
new file mode 100644
index 0000000..6f7683e
--- /dev/null
+++ b/usr.sbin/xntpd/xntpd/refclock_parse.c
@@ -0,0 +1,3925 @@
+#if defined(REFCLOCK) && (defined(PARSE) || defined(PARSEPPS))
+/*
+ * /src/NTP/REPOSITORY/v3/xntpd/refclock_parse.c,v 3.53 1994/03/25 13:07:39 kardel Exp
+ *
+ * refclock_parse.c,v 3.53 1994/03/25 13:07:39 kardel Exp
+ *
+ * generic reference clock driver for receivers
+ *
+ * Added support for the Boeder DCF77 receiver on FreeBSD
+ * by Vincenzo Capuano 1995/04/18.
+ *
+ * make use of a STREAMS module for input processing where
+ * available and configured. Currently the STREAMS module
+ * is only available for Suns running SunOS 4.x and SunOS5.x (new - careful!)
+ *
+ * Copyright (c) 1989,1990,1991,1992,1993,1994
+ * Frank Kardel Friedrich-Alexander Universitaet Erlangen-Nuernberg
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ */
+
+/*
+ * Defines:
+ * REFCLOCK && (PARSE||PARSEPPS)
+ * - enable this mess
+ * STREAM - allow for STREAMS modules
+ * ("parse", "ppsclocd", "ppsclock")
+ * PARSEPPS - provide PPS information to loopfilter (for
+ * backward compatibilty only)
+ * PPS - supply loopfilter with PPS samples (if configured)
+ * PPSPPS - notify loopfilter of PPS file descriptor
+ *
+ * FREEBSD_CONRAD - Make very cheap "Conrad DCF77 RS-232" gadget work
+ * with FreeBSD.
+ * BOEDER - Make cheap "Boeder DCF77 RS-232" receiver work
+ * with FreeBSD.
+ * TTY defines:
+ * HAVE_BSD_TTYS - currently unsupported
+ * HAVE_SYSV_TTYS - will use termio.h
+ * HAVE_TERMIOS - will use termios.h
+ * STREAM - will use streams and implies HAVE_TERMIOS
+ */
+
+/*
+ * This driver currently provides the support for
+ * - Meinberg DCF77 receiver DCF77 PZF 535 (TCXO version) (DCF)
+ * - Meinberg DCF77 receiver DCF77 PZF 535 (OCXO version) (DCF)
+ * - Meinberg DCF77 receiver U/A 31 (DCF)
+ * - ELV DCF7000 (DCF)
+ * - Schmid clock (DCF)
+ * - Conrad DCF77 receiver module (DCF)
+ * - Boeder DCF77 receiver (DCF)
+ * - FAU DCF77 NTP receiver (TimeBrick) (DCF)
+ * - Meinberg GPS166 (GPS)
+ * - Trimble SV6 (TSIP and TAIP protocol) (GPS)
+ *
+ */
+
+/*
+ * Meinberg receivers are connected via a 9600 baud serial line
+ *
+ * Receivers that do NOT support:
+ * - leap second indication
+ * DCF U/A 31
+ * DCF PZF535 (stock version)
+ *
+ * so...
+ * - for PZF535 please ask for revision PZFUERL4.6 or higher
+ * (support for leap second and alternate antenna)
+ *
+ * The Meinberg GPS receiver also has a special NTP time stamp
+ * format. The firmware release is Uni-Erlangen. Only this
+ * firmware release is supported by xntp3.
+ *
+ * Meinberg generic receiver setup:
+ * output time code every second
+ * Baud rate 9600 7E2S
+ */
+
+#include "ntpd.h"
+#include "ntp_refclock.h"
+#include "ntp_unixtime.h"
+#include "ntp_control.h"
+
+#include <stdio.h>
+#include <ctype.h>
+#include <time.h>
+
+#include <sys/errno.h>
+#ifdef FREEBSD_CONRAD
+#include <sys/ioctl.h>
+#endif
+extern int errno;
+
+#if !defined(STREAM) && !defined(HAVE_SYSV_TTYS) && !defined(HAVE_BSD_TTYS) && !defined(HAVE_TERMIOS)
+/* #error NEED TO DEFINE ONE OF "STREAM" or "HAVE_SYSV_TTYS" */
+NEED TO DEFINE ONE OF "STREAM", "HAVE_SYSV_TTYS" or "HAVE_TERMIOS"
+#endif
+
+#ifdef STREAM
+#include <sys/stream.h>
+#include <sys/stropts.h>
+#ifndef HAVE_TERMIOS
+#define HAVE_TERMIOS
+#endif
+#endif
+
+#ifdef HAVE_TERMIOS
+#include <termios.h>
+#define TTY_GETATTR(_FD_, _ARG_) tcgetattr((_FD_), (_ARG_))
+#define TTY_SETATTR(_FD_, _ARG_) tcsetattr((_FD_), TCSANOW, (_ARG_))
+#undef HAVE_SYSV_TTYS
+#endif
+
+#ifdef HAVE_SYSV_TTYS
+#include <termio.h>
+#define TTY_GETATTR(_FD_, _ARG_) ioctl((_FD_), TCGETA, (_ARG_))
+#define TTY_SETATTR(_FD_, _ARG_) ioctl((_FD_), TCSETAW, (_ARG_))
+#endif
+
+#ifdef HAVE_BSD_TTYS
+/* #error CURRENTLY NO BSD TTY SUPPORT */
+CURRENTLY NO BSD TTY SUPPORT
+#endif
+
+#if !defined(O_RDWR) /* XXX SOLARIS */
+#include <fcntl.h>
+#endif /* !def(O_RDWR) */
+
+#ifdef PPSPPS
+#include <sys/ppsclock.h>
+#endif
+
+#include "ntp_select.h"
+#include "ntp_stdlib.h"
+
+#include "parse.h"
+
+#if !defined(NO_SCCSID) && !defined(lint) && !defined(__GNUC__)
+static char rcsid[]="refclock_parse.c,v 3.53 1994/03/25 13:07:39 kardel Exp";
+#endif
+
+/**===========================================================================
+ ** external interface to xntp mechanism
+ **/
+
+static void parse_init P((void));
+static int parse_start P((int, struct peer *));
+static void parse_shutdown P((int, struct peer *));
+static void parse_poll P((int, struct peer *));
+static void parse_control P((int, struct refclockstat *, struct refclockstat *));
+
+#define parse_buginfo noentry
+
+struct refclock refclock_parse = {
+ parse_start,
+ parse_shutdown,
+ parse_poll,
+ parse_control,
+ parse_init,
+ parse_buginfo,
+ NOFLAGS
+};
+
+/*
+ * the unit field selects for one the prototype to be used (lower 4 bits)
+ * and for the other the clock type in case of different but similar
+ * receivers (bits 4-6)
+ * the most significant bit encodes PPS support
+ * when the most significant bit is set the pps telegrams will be used
+ * for controlling the local clock (ntp_loopfilter.c)
+ * receiver specific configration data is kept in the clockinfo field.
+ */
+
+/*
+ * Definitions
+ */
+#define MAXUNITS 4 /* maximum number of "PARSE" units permitted */
+#define PARSEDEVICE "/dev/refclock-%d" /* device to open %d is unit number */
+
+/**===========================================================================
+ ** function vector for dynamically binding io handling mechanism
+ **/
+
+typedef struct bind
+{
+ char *bd_description; /* name of type of binding */
+ int (*bd_init)(); /* initialize */
+ void (*bd_end)(); /* end */
+ int (*bd_setcs)(); /* set character size */
+ int (*bd_disable)(); /* disable */
+ int (*bd_enable)(); /* enable */
+ int (*bd_getfmt)(); /* get format */
+ int (*bd_setfmt)(); /* setfmt */
+ int (*bd_getstat)(); /* getstat */
+ int (*bd_setstat)(); /* setstat */
+ int (*bd_timecode)(); /* get time code */
+ void (*bd_receive)(); /* receive operation */
+ void (*bd_poll)(); /* poll operation */
+} bind_t;
+
+#define PARSE_END(_X_) (*(_X_)->binding->bd_end)(_X_)
+#define PARSE_SETCS(_X_, _CS_) (*(_X_)->binding->bd_setcs)(_X_, _CS_)
+#define PARSE_ENABLE(_X_) (*(_X_)->binding->bd_enable)(_X_)
+#define PARSE_DISABLE(_X_) (*(_X_)->binding->bd_disable)(_X_)
+#define PARSE_GETFMT(_X_, _DCT_) (*(_X_)->binding->bd_getfmt)(_X_, _DCT_)
+#define PARSE_SETFMT(_X_, _DCT_) (*(_X_)->binding->bd_setfmt)(_X_, _DCT_)
+#define PARSE_GETSTAT(_X_, _DCT_) (*(_X_)->binding->bd_getstat)(_X_, _DCT_)
+#define PARSE_SETSTAT(_X_, _DCT_) (*(_X_)->binding->bd_setstat)(_X_, _DCT_)
+#define PARSE_GETTIMECODE(_X_, _DCT_) (*(_X_)->binding->bd_timecode)(_X_, _DCT_)
+#define PARSE_POLL(_X_) (*(_X_)->binding->bd_poll)(_X_)
+
+/*
+ * io modes
+ */
+#define PARSE_F_NOPOLLONLY 0x0001 /* always do async io (possible PPS support via PARSE) */
+#define PARSE_F_POLLONLY 0x0002 /* never do async io (no PPS support via PARSE) */
+#define PARSE_F_PPSPPS 0x0004 /* use loopfilter PPS code (CIOGETEV) */
+#define PARSE_F_PPSONSECOND 0x0008 /* PPS pulses are on second */
+
+/**===========================================================================
+ ** refclock instance data
+ **/
+
+struct parseunit
+{
+ /*
+ * XNTP management
+ */
+ struct peer *peer; /* backlink to peer structure - refclock inactive if 0 */
+ int fd; /* device file descriptor */
+ u_char unit; /* encoded unit/type/PPS */
+
+ /*
+ * XNTP io
+ */
+ struct refclockio io; /* io system structure (used in PPS mode) */
+ bind_t *binding; /* io handling binding */
+
+ /*
+ * parse state
+ */
+ parse_t parseio; /* io handling structure (user level parsing) */
+
+ /*
+ * type specific parameters
+ */
+ struct my_clockinfo *parse_type; /* link to clock description */
+
+ /*
+ * clock specific configuration
+ */
+ l_fp basedelay; /* clock local phase offset */
+ l_fp ppsdelay; /* clock local pps phase offset */
+
+ /*
+ * clock state handling/reporting
+ */
+ u_char flags; /* flags (leap_control) */
+ u_char status; /* current status */
+ u_char lastevent; /* last not NORMAL status */
+ U_LONG lastchange; /* time (xntp) when last state change accured */
+ U_LONG statetime[CEVNT_MAX+1]; /* accumulated time of clock states */
+ struct event stattimer; /* statistics timer */
+ U_LONG polls; /* polls from NTP protocol machine */
+ U_LONG noresponse; /* number of expected but not seen datagrams */
+ U_LONG badformat; /* bad format (failed format conversions) */
+ U_LONG baddata; /* usually bad receive length, bad format */
+
+ u_char pollonly; /* 1 for polling only (no PPS mode) */
+ u_char pollneeddata; /* 1 for receive sample expected in PPS mode */
+ U_LONG laststatus; /* last packet status (error indication) */
+ u_short lastformat; /* last format used */
+ U_LONG lastsync; /* time (xntp) when clock was last seen fully synchronized */
+ U_LONG timestarted; /* time (xntp) when peer clock was instantiated */
+ U_LONG nosynctime; /* time (xntp) when last nosync message was posted */
+ U_LONG lastmissed; /* time (xntp) when poll didn't get data (powerup heuristic) */
+ U_LONG ppsserial; /* magic cookie for ppsclock serials (avoids stale ppsclock data) */
+ parsetime_t time; /* last (parse module) data */
+ void *localdata; /* optional local data */
+};
+
+
+/**===========================================================================
+ ** Clockinfo section all parameter for specific clock types
+ ** includes NTP paramaters, TTY parameters and IO handling parameters
+ **/
+
+static void poll_dpoll P((struct parseunit *));
+static void poll_poll P((struct parseunit *));
+static int poll_init P((struct parseunit *));
+static void poll_end P((struct parseunit *));
+
+typedef struct poll_info
+{
+ U_LONG rate; /* poll rate - once every "rate" seconds - 0 off */
+ char * string; /* string to send for polling */
+ U_LONG count; /* number of charcters in string */
+} poll_info_t;
+
+#define NO_FLAGS 0
+#define NO_POLL (void (*)())0
+#define NO_INIT (int (*)())0
+#define NO_END (void (*)())0
+#define NO_DATA (void *)0
+#define NO_FORMAT ""
+#define NO_PPSDELAY 0
+
+#define DCF_ID "DCF" /* generic DCF */
+#define DCF_A_ID "DCFa" /* AM demodulation */
+#define DCF_P_ID "DCFp" /* psuedo random phase shift */
+#define GPS_ID "GPS" /* GPS receiver */
+
+#define NOCLOCK_ROOTDELAY 0x00000000
+#define NOCLOCK_BASEDELAY 0x00000000
+#define NOCLOCK_DESCRIPTION ((char *)0)
+#define NOCLOCK_MAXUNSYNC 0
+#define NOCLOCK_CFLAG 0
+#define NOCLOCK_IFLAG 0
+#define NOCLOCK_OFLAG 0
+#define NOCLOCK_LFLAG 0
+#define NOCLOCK_ID "TILT"
+#define NOCLOCK_POLL NO_POLL
+#define NOCLOCK_INIT NO_INIT
+#define NOCLOCK_END NO_END
+#define NOCLOCK_DATA NO_DATA
+#define NOCLOCK_FORMAT NO_FORMAT
+#define NOCLOCK_TYPE CTL_SST_TS_UNSPEC
+
+#define DCF_TYPE CTL_SST_TS_LF
+#define GPS_TYPE CTL_SST_TS_UHF
+
+/*
+ * receiver specific constants
+ */
+#define MBG_CFLAG19200 (B19200|CS7|PARENB|CREAD|HUPCL)
+#define MBG_CFLAG (B9600|CS7|PARENB|CREAD|HUPCL)
+#define MBG_IFLAG (IGNBRK|IGNPAR|ISTRIP)
+#define MBG_OFLAG 0
+#define MBG_LFLAG 0
+/*
+ * Meinberg DCF U/A 31 (AM) receiver
+ */
+#define DCFUA31_ROOTDELAY 0x00000D00 /* 50.78125ms */
+#define DCFUA31_BASEDELAY 0x02C00000 /* 10.7421875ms: 10 ms (+/- 3 ms) */
+#define DCFUA31_DESCRIPTION "Meinberg DCF U/A 31"
+#define DCFUA31_MAXUNSYNC 60*30 /* only trust clock for 1/2 hour */
+#define DCFUA31_CFLAG MBG_CFLAG
+#define DCFUA31_IFLAG MBG_IFLAG
+#define DCFUA31_OFLAG MBG_OFLAG
+#define DCFUA31_LFLAG MBG_LFLAG
+
+/*
+ * Meinberg DCF PZF535/TCXO (FM/PZF) receiver
+ */
+#define DCFPZF535_ROOTDELAY 0x00000034 /* 800us */
+#define DCFPZF535_BASEDELAY 0x00800000 /* 1.968ms +- 104us (oscilloscope) - relative to start (end of STX) */
+#define DCFPZF535_DESCRIPTION "Meinberg DCF PZF 535/TCXO"
+#define DCFPZF535_MAXUNSYNC 60*60*12 /* only trust clock for 12 hours
+ * @ 5e-8df/f we have accumulated
+ * at most 2.16 ms (thus we move to
+ * NTP synchronisation */
+#define DCFPZF535_CFLAG MBG_CFLAG
+#define DCFPZF535_IFLAG MBG_IFLAG
+#define DCFPZF535_OFLAG MBG_OFLAG
+#define DCFPZF535_LFLAG MBG_LFLAG
+
+
+/*
+ * Meinberg DCF PZF535/OCXO receiver
+ */
+#define DCFPZF535OCXO_ROOTDELAY 0x00000034 /* 800us (max error * 10) */
+#define DCFPZF535OCXO_BASEDELAY 0x00800000 /* 1.968ms +- 104us (oscilloscope) - relative to start (end of STX) */
+#define DCFPZF535OCXO_DESCRIPTION "Meinberg DCF PZF 535/OCXO"
+#define DCFPZF535OCXO_MAXUNSYNC 60*60*96 /* only trust clock for 4 days
+ * @ 5e-9df/f we have accumulated
+ * at most an error of 1.73 ms
+ * (thus we move to NTP synchronisation) */
+#define DCFPZF535OCXO_CFLAG MBG_CFLAG
+#define DCFPZF535OCXO_IFLAG MBG_IFLAG
+#define DCFPZF535OCXO_OFLAG MBG_OFLAG
+#define DCFPZF535OCXO_LFLAG MBG_LFLAG
+
+/*
+ * Meinberg GPS166 receiver
+ */
+#define GPS166_ROOTDELAY 0x00000000 /* nothing here */
+#define GPS166_BASEDELAY 0x00800000 /* XXX to be fixed ! 1.968ms +- 104us (oscilloscope) - relative to start (end of STX) */
+#define GPS166_DESCRIPTION "Meinberg GPS166 receiver"
+#define GPS166_MAXUNSYNC 0 /* this clock is immediately lost */
+#define GPS166_CFLAG MBG_CFLAG
+#define GPS166_IFLAG MBG_IFLAG
+#define GPS166_OFLAG MBG_OFLAG
+#define GPS166_LFLAG MBG_LFLAG
+#define GPS166_POLL NO_POLL
+#define GPS166_INIT NO_INIT
+#define GPS166_END NO_END
+#define GPS166_DATA NO_DATA
+#define GPS166_ID GPS_ID
+#define GPS166_FORMAT NO_FORMAT
+
+/*
+ * ELV DCF7000 Wallclock-Receiver/Switching Clock (Kit)
+ *
+ * This is really not the hottest clock - but before you have nothing ...
+ */
+#define DCF7000_ROOTDELAY 0x00000364 /* 13 ms */
+#define DCF7000_BASEDELAY 0x67AE0000 /* 405 ms - slow blow */
+#define DCF7000_DESCRIPTION "ELV DCF7000"
+#define DCF7000_MAXUNSYNC (60*5) /* sorry - but it just was not build as a clock */
+#define DCF7000_CFLAG (B9600|CS8|CREAD|PARENB|PARODD|CLOCAL|HUPCL)
+#define DCF7000_IFLAG (IGNBRK)
+#define DCF7000_OFLAG 0
+#define DCF7000_LFLAG 0
+
+/*
+ * Schmid DCF Receiver Kit
+ *
+ * When the WSDCF clock is operating optimally we want the primary clock
+ * distance to come out at 300 ms. Thus, peer.distance in the WSDCF peer
+ * structure is set to 290 ms and we compute delays which are at least
+ * 10 ms long. The following are 290 ms and 10 ms expressed in u_fp format
+ */
+#define WS_POLLRATE 1 /* every second - watch interdependency with poll routine */
+#define WS_POLLCMD "\163"
+#define WS_CMDSIZE 1
+
+static poll_info_t wsdcf_pollinfo = { WS_POLLRATE, WS_POLLCMD, WS_CMDSIZE };
+
+#define WSDCF_INIT poll_init
+#define WSDCF_POLL poll_dpoll
+#define WSDCF_END poll_end
+#define WSDCF_DATA ((void *)(&wsdcf_pollinfo))
+#define WSDCF_ROOTDELAY 0X00004A3D /* ~ 290ms */
+#define WSDCF_BASEDELAY 0x028F5C29 /* ~ 10ms */
+#define WSDCF_DESCRIPTION "WS/DCF Receiver"
+#define WSDCF_FORMAT "Schmid"
+#define WSDCF_MAXUNSYNC (60*60) /* assume this beast hold at 1 h better than 2 ms XXX-must verify */
+#define WSDCF_CFLAG (B1200|CS8|CREAD|CLOCAL)
+#define WSDCF_IFLAG 0
+#define WSDCF_OFLAG 0
+#define WSDCF_LFLAG 0
+
+/*
+ * RAW DCF77 - input of DCF marks via RS232 - many variants
+ */
+#define RAWDCF_FLAGS PARSE_F_NOPOLLONLY
+#define RAWDCF_ROOTDELAY 0x00000364 /* 13 ms */
+#define RAWDCF_FORMAT "RAW DCF77 Timecode"
+#define RAWDCF_MAXUNSYNC (0) /* sorry - its a true receiver - no signal - no time */
+
+#if defined(FREEBSD_CONRAD) || (defined(SYS_FREEBSD) && defined(BOEDER))
+#define RAWDCF_CFLAG (CS8|CREAD|CLOCAL)
+#else
+#define RAWDCF_CFLAG (B50|CS8|CREAD|CLOCAL)
+#endif
+#define RAWDCF_IFLAG 0
+#define RAWDCF_OFLAG 0
+#define RAWDCF_LFLAG 0
+
+/*
+ * RAW DCF variants
+ */
+/*
+ * Conrad receiver
+ *
+ * simplest (cheapest) DCF clock - e. g. DCF77 receiver by Conrad
+ * (~40DM - roughly $30 ) followed by a level converter for RS232
+ */
+#define CONRAD_BASEDELAY 0x420C49B0 /* ~258 ms - Conrad receiver @ 50 Baud on a Sun */
+#define CONRAD_DESCRIPTION "RAW DCF77 CODE (Conrad DCF77 receiver module)"
+
+/*
+ * Boeder receiver
+ *
+ * simple (cheap) DCF clock - e. g. DCF77 receiver by Boeder
+ * followed by a level converter for RS232
+ */
+#define BOEDER_BASEDELAY 0x420C49B0 /* ~258 ms - Conrad receiver @ 50 Baud */
+#define BOEDER_DESCRIPTION "RAW DCF77 CODE (BOEDER DCF77 receiver)"
+
+/*
+ * TimeBrick receiver
+ */
+#define TIMEBRICK_BASEDELAY 0x35C29000 /* ~210 ms - TimeBrick @ 50 Baud on a Sun */
+#define TIMEBRICK_DESCRIPTION "RAW DCF77 CODE (TimeBrick)"
+
+/*
+ * Trimble SV6 GPS receivers (TAIP and TSIP protocols)
+ */
+#define ETX 0x03
+#define DLE 0x10
+
+#define TRIM_POLLRATE 0 /* only true direct polling */
+
+#define TRIM_TAIPPOLLCMD ">QTM<"
+#define TRIM_TAIPCMDSIZE 5
+static poll_info_t trimbletaip_pollinfo = { TRIM_POLLRATE, TRIM_TAIPPOLLCMD, TRIM_TAIPCMDSIZE };
+static int trimbletaip_init P((struct parseunit *));
+
+/* query time & UTC correction data */
+static char tsipquery[] = { DLE, 0x21, DLE, ETX, DLE, 0x2F, DLE, ETX };
+
+static poll_info_t trimbletsip_pollinfo = { TRIM_POLLRATE, tsipquery, sizeof(tsipquery) };
+static int trimbletsip_init P((struct parseunit *));
+
+#define TRIMBLETAIP_CFLAG (B4800|CS8|CREAD)
+#define TRIMBLETAIP_IFLAG (BRKINT|IGNPAR|ISTRIP|ICRNL|IXON)
+#define TRIMBLETAIP_OFLAG (OPOST|ONLCR)
+#define TRIMBLETAIP_LFLAG (ICANON|ECHOK)
+#define TRIMBLETSIP_CFLAG (B9600|CS8|CLOCAL|CREAD|PARENB|PARODD)
+#define TRIMBLETSIP_IFLAG (IGNBRK)
+#define TRIMBLETSIP_OFLAG (0)
+#define TRIMBLETSIP_LFLAG (0)
+
+#define TRIMBLETAIP_FLAGS (PARSE_F_PPSPPS|PARSE_F_PPSONSECOND)
+#define TRIMBLETSIP_FLAGS (TRIMBLETAIP_FLAGS|PARSE_F_NOPOLLONLY)
+
+#define TRIMBLETAIP_POLL poll_dpoll
+#define TRIMBLETSIP_POLL poll_dpoll
+
+#define TRIMBLETAIP_INIT trimbletaip_init
+#define TRIMBLETSIP_INIT trimbletsip_init
+
+#define TRIMBLETAIP_END poll_end
+#define TRIMBLETSIP_END poll_end
+
+#define TRIMBLETAIP_DATA ((void *)(&trimbletaip_pollinfo))
+#define TRIMBLETSIP_DATA ((void *)(&trimbletsip_pollinfo))
+
+#define TRIMBLETAIP_ID GPS_ID
+#define TRIMBLETSIP_ID GPS_ID
+
+#define TRIMBLETAIP_FORMAT NO_FORMAT
+#define TRIMBLETSIP_FORMAT "Trimble SV6/TSIP"
+
+#define TRIMBLETAIP_ROOTDELAY 0x0
+#define TRIMBLETSIP_ROOTDELAY 0x0
+
+#define TRIMBLETAIP_BASEDELAY 0x0
+#define TRIMBLETSIP_BASEDELAY 0x51EB852 /* 20 ms as a l_uf - avg GPS time message latency */
+
+#define TRIMBLETAIP_DESCRIPTION "Trimble GPS (TAIP) receiver"
+#define TRIMBLETSIP_DESCRIPTION "Trimble GPS (TSIP) receiver"
+
+#define TRIMBLETAIP_MAXUNSYNC 0
+#define TRIMBLETSIP_MAXUNSYNC 0
+
+#define TRIMBLETAIP_EOL '<'
+
+static struct my_clockinfo
+{
+ U_LONG cl_flags; /* operation flags (io modes) */
+ void (*cl_poll)(); /* active poll routine */
+ int (*cl_init)(); /* active poll init routine */
+ void (*cl_end)(); /* active poll end routine */
+ void *cl_data; /* local data area for "poll" mechanism */
+ u_fp cl_rootdelay; /* rootdelay */
+ U_LONG cl_basedelay; /* current offset - unsigned l_fp fractional part */
+ U_LONG cl_ppsdelay; /* current PPS offset - unsigned l_fp fractional part */
+ char *cl_id; /* ID code (usually "DCF") */
+ char *cl_description; /* device name */
+ char *cl_format; /* fixed format */
+ u_char cl_type; /* clock type (ntp control) */
+ U_LONG cl_maxunsync; /* time to trust oscillator after loosing synch */
+ U_LONG cl_cflag; /* terminal io flags */
+ U_LONG cl_iflag; /* terminal io flags */
+ U_LONG cl_oflag; /* terminal io flags */
+ U_LONG cl_lflag; /* terminal io flags */
+} clockinfo[] =
+{ /* 0. 0.0.128 - base offset for PPS support */
+ { /* 127.127.8.<device> */
+ NO_FLAGS,
+ NO_POLL,
+ NO_INIT,
+ NO_END,
+ NO_DATA,
+ DCFPZF535_ROOTDELAY,
+ DCFPZF535_BASEDELAY,
+ NO_PPSDELAY,
+ DCF_P_ID,
+ DCFPZF535_DESCRIPTION,
+ NO_FORMAT,
+ DCF_TYPE,
+ DCFPZF535_MAXUNSYNC,
+ DCFPZF535_CFLAG,
+ DCFPZF535_IFLAG,
+ DCFPZF535_OFLAG,
+ DCFPZF535_LFLAG
+ },
+ { /* 127.127.8.4+<device> */
+ NO_FLAGS,
+ NO_POLL,
+ NO_INIT,
+ NO_END,
+ NO_DATA,
+ DCFPZF535OCXO_ROOTDELAY,
+ DCFPZF535OCXO_BASEDELAY,
+ NO_PPSDELAY,
+ DCF_P_ID,
+ DCFPZF535OCXO_DESCRIPTION,
+ NO_FORMAT,
+ DCF_TYPE,
+ DCFPZF535OCXO_MAXUNSYNC,
+ DCFPZF535OCXO_CFLAG,
+ DCFPZF535OCXO_IFLAG,
+ DCFPZF535OCXO_OFLAG,
+ DCFPZF535OCXO_LFLAG
+ },
+ { /* 127.127.8.8+<device> */
+ NO_FLAGS,
+ NO_POLL,
+ NO_INIT,
+ NO_END,
+ NO_DATA,
+ DCFUA31_ROOTDELAY,
+ DCFUA31_BASEDELAY,
+ NO_PPSDELAY,
+ DCF_A_ID,
+ DCFUA31_DESCRIPTION,
+ NO_FORMAT,
+ DCF_TYPE,
+ DCFUA31_MAXUNSYNC,
+ DCFUA31_CFLAG,
+ DCFUA31_IFLAG,
+ DCFUA31_OFLAG,
+ DCFUA31_LFLAG
+ },
+ { /* 127.127.8.12+<device> */
+ NO_FLAGS,
+ NO_POLL,
+ NO_INIT,
+ NO_END,
+ NO_DATA,
+ DCF7000_ROOTDELAY,
+ DCF7000_BASEDELAY,
+ NO_PPSDELAY,
+ DCF_A_ID,
+ DCF7000_DESCRIPTION,
+ NO_FORMAT,
+ DCF_TYPE,
+ DCF7000_MAXUNSYNC,
+ DCF7000_CFLAG,
+ DCF7000_IFLAG,
+ DCF7000_OFLAG,
+ DCF7000_LFLAG
+ },
+ { /* 127.127.8.16+<device> */
+ NO_FLAGS,
+ WSDCF_POLL,
+ WSDCF_INIT,
+ WSDCF_END,
+ WSDCF_DATA,
+ WSDCF_ROOTDELAY,
+ WSDCF_BASEDELAY,
+ NO_PPSDELAY,
+ DCF_A_ID,
+ WSDCF_DESCRIPTION,
+ WSDCF_FORMAT,
+ DCF_TYPE,
+ WSDCF_MAXUNSYNC,
+ WSDCF_CFLAG,
+ WSDCF_IFLAG,
+ WSDCF_OFLAG,
+ WSDCF_LFLAG
+ },
+ { /* 127.127.8.20+<device> */
+ RAWDCF_FLAGS,
+ NO_POLL,
+ NO_INIT,
+ NO_END,
+ NO_DATA,
+ RAWDCF_ROOTDELAY,
+ CONRAD_BASEDELAY,
+ NO_PPSDELAY,
+ DCF_A_ID,
+ CONRAD_DESCRIPTION,
+ RAWDCF_FORMAT,
+ DCF_TYPE,
+ RAWDCF_MAXUNSYNC,
+ RAWDCF_CFLAG,
+ RAWDCF_IFLAG,
+ RAWDCF_OFLAG,
+ RAWDCF_LFLAG
+ },
+ { /* 127.127.8.24+<device> */
+ RAWDCF_FLAGS,
+ NO_POLL,
+ NO_INIT,
+ NO_END,
+ NO_DATA,
+ RAWDCF_ROOTDELAY,
+ TIMEBRICK_BASEDELAY,
+ NO_PPSDELAY,
+ DCF_A_ID,
+ TIMEBRICK_DESCRIPTION,
+ RAWDCF_FORMAT,
+ DCF_TYPE,
+ RAWDCF_MAXUNSYNC,
+ RAWDCF_CFLAG,
+ RAWDCF_IFLAG,
+ RAWDCF_OFLAG,
+ RAWDCF_LFLAG
+ },
+ { /* 127.127.8.28+<device> */
+ NO_FLAGS,
+ GPS166_POLL,
+ GPS166_INIT,
+ GPS166_END,
+ GPS166_DATA,
+ GPS166_ROOTDELAY,
+ GPS166_BASEDELAY,
+ NO_PPSDELAY,
+ GPS166_ID,
+ GPS166_DESCRIPTION,
+ GPS166_FORMAT,
+ GPS_TYPE,
+ GPS166_MAXUNSYNC,
+ GPS166_CFLAG,
+ GPS166_IFLAG,
+ GPS166_OFLAG,
+ GPS166_LFLAG
+ },
+ { /* 127.127.8.32+<device> */
+ TRIMBLETAIP_FLAGS,
+ TRIMBLETAIP_POLL,
+ TRIMBLETAIP_INIT,
+ TRIMBLETAIP_END,
+ TRIMBLETAIP_DATA,
+ TRIMBLETAIP_ROOTDELAY,
+ TRIMBLETAIP_BASEDELAY,
+ NO_PPSDELAY,
+ TRIMBLETAIP_ID,
+ TRIMBLETAIP_DESCRIPTION,
+ TRIMBLETAIP_FORMAT,
+ GPS_TYPE,
+ TRIMBLETAIP_MAXUNSYNC,
+ TRIMBLETAIP_CFLAG,
+ TRIMBLETAIP_IFLAG,
+ TRIMBLETAIP_OFLAG,
+ TRIMBLETAIP_LFLAG
+ },
+ { /* 127.127.8.36+<device> */
+ TRIMBLETSIP_FLAGS,
+ TRIMBLETSIP_POLL,
+ TRIMBLETSIP_INIT,
+ TRIMBLETSIP_END,
+ TRIMBLETSIP_DATA,
+ TRIMBLETSIP_ROOTDELAY,
+ TRIMBLETSIP_BASEDELAY,
+ NO_PPSDELAY,
+ TRIMBLETSIP_ID,
+ TRIMBLETSIP_DESCRIPTION,
+ TRIMBLETSIP_FORMAT,
+ GPS_TYPE,
+ TRIMBLETSIP_MAXUNSYNC,
+ TRIMBLETSIP_CFLAG,
+ TRIMBLETSIP_IFLAG,
+ TRIMBLETSIP_OFLAG,
+ TRIMBLETSIP_LFLAG
+ },
+ { /* 127.127.8.40+<device> */
+ RAWDCF_FLAGS,
+ NO_POLL,
+ NO_INIT,
+ NO_END,
+ NO_DATA,
+ RAWDCF_ROOTDELAY,
+ BOEDER_BASEDELAY,
+ NO_PPSDELAY,
+ DCF_A_ID,
+ BOEDER_DESCRIPTION,
+ RAWDCF_FORMAT,
+ DCF_TYPE,
+ RAWDCF_MAXUNSYNC,
+ RAWDCF_CFLAG,
+ RAWDCF_IFLAG,
+ RAWDCF_OFLAG,
+ RAWDCF_LFLAG
+ }
+};
+
+static int ncltypes = sizeof(clockinfo) / sizeof(struct my_clockinfo);
+
+#define CL_REALTYPE(x) (((x) >> 2) & 0x1F)
+#define CL_TYPE(x) ((CL_REALTYPE(x) >= ncltypes) ? ~0 : CL_REALTYPE(x))
+#define CL_PPS(x) ((x) & 0x80)
+#define CL_UNIT(x) ((x) & 0x3)
+
+/*
+ * Other constant stuff
+ */
+#define PARSEHSREFID 0x7f7f08ff /* 127.127.8.255 refid for hi strata */
+
+#define PARSENOSYNCREPEAT (10*60) /* mention uninitialized clocks all 10 minutes */
+#define PARSESTATISTICS (60*60) /* output state statistics every hour */
+
+static struct parseunit *parseunits[MAXUNITS];
+
+extern U_LONG current_time;
+extern s_char sys_precision;
+extern struct event timerqueue[];
+#ifdef PPSPPS
+extern int fdpps;
+#endif
+
+static int notice = 0;
+
+#define PARSE_STATETIME(parse, i) ((parse->status == i) ? parse->statetime[i] + current_time - parse->lastchange : parse->statetime[i])
+
+static void parse_event P((struct parseunit *, int));
+static void parse_process P((struct parseunit *, parsetime_t *));
+
+/**===========================================================================
+ ** implementation of i/o handling methods
+ ** (all STREAM, partial STREAM, user level)
+ **/
+
+/*
+ * define possible io handling methods
+ */
+#ifdef STREAM
+static int ppsclock_init P((struct parseunit *));
+static int stream_init P((struct parseunit *));
+static void stream_nop P((struct parseunit *));
+static int stream_enable P((struct parseunit *));
+static int stream_disable P((struct parseunit *));
+static int stream_setcs P((struct parseunit *, parsectl_t *));
+static int stream_getfmt P((struct parseunit *, parsectl_t *));
+static int stream_setfmt P((struct parseunit *, parsectl_t *));
+static int stream_getstat P((struct parseunit *, parsectl_t *));
+static int stream_setstat P((struct parseunit *, parsectl_t *));
+static int stream_timecode P((struct parseunit *, parsectl_t *));
+static void stream_receive P((struct recvbuf *));
+static void stream_poll P((struct parseunit *));
+#endif
+
+static int local_init P((struct parseunit *));
+static void local_end P((struct parseunit *));
+static int local_nop P((struct parseunit *));
+static int local_setcs P((struct parseunit *, parsectl_t *));
+static int local_getfmt P((struct parseunit *, parsectl_t *));
+static int local_setfmt P((struct parseunit *, parsectl_t *));
+static int local_getstat P((struct parseunit *, parsectl_t *));
+static int local_setstat P((struct parseunit *, parsectl_t *));
+static int local_timecode P((struct parseunit *, parsectl_t *));
+static void local_receive P((struct recvbuf *));
+static void local_poll P((struct parseunit *));
+
+static bind_t io_bindings[] =
+{
+#ifdef STREAM
+ {
+ "parse STREAM",
+ stream_init,
+ stream_nop,
+ stream_setcs,
+ stream_disable,
+ stream_enable,
+ stream_getfmt,
+ stream_setfmt,
+ stream_getstat,
+ stream_setstat,
+ stream_timecode,
+ stream_receive,
+ stream_poll
+ },
+ {
+ "ppsclock STREAM",
+ ppsclock_init,
+ local_end,
+ local_setcs,
+ local_nop,
+ local_nop,
+ local_getfmt,
+ local_setfmt,
+ local_getstat,
+ local_setstat,
+ local_timecode,
+ local_receive,
+ local_poll
+ },
+#endif
+ {
+ "normal",
+ local_init,
+ local_end,
+ local_setcs,
+ local_nop,
+ local_nop,
+ local_getfmt,
+ local_setfmt,
+ local_getstat,
+ local_setstat,
+ local_timecode,
+ local_receive,
+ local_poll
+ },
+ {
+ (char *)0,
+ }
+};
+
+#ifdef STREAM
+/*--------------------------------------------------
+ * ppsclock STREAM init
+ */
+static int
+ppsclock_init(parse)
+ struct parseunit *parse;
+{
+ /*
+ * now push the parse streams module
+ * it will ensure exclusive access to the device
+ */
+ if (ioctl(parse->fd, I_PUSH, (caddr_t)"ppsclocd") == -1 &&
+ ioctl(parse->fd, I_PUSH, (caddr_t)"ppsclock") == -1)
+ {
+ syslog(LOG_ERR, "PARSE receiver #%d: ppsclock_init: ioctl(fd, I_PUSH, \"ppsclock\"): %m",
+ CL_UNIT(parse->unit));
+ return 0;
+ }
+ if (!local_init(parse))
+ {
+ (void)ioctl(parse->fd, I_POP, (caddr_t)0);
+ return 0;
+ }
+
+ parse->flags |= PARSE_PPSCLOCK;
+ return 1;
+}
+
+/*--------------------------------------------------
+ * parse STREAM init
+ */
+static int
+stream_init(parse)
+ struct parseunit *parse;
+{
+ /*
+ * now push the parse streams module
+ * to test whether it is there (Oh boy - neat kernel interface)
+ */
+ if (ioctl(parse->fd, I_PUSH, (caddr_t)"parse") == -1)
+ {
+ syslog(LOG_ERR, "PARSE receiver #%d: stream_init: ioctl(fd, I_PUSH, \"parse\"): %m", CL_UNIT(parse->unit));
+ return 0;
+ }
+ else
+ {
+ while(ioctl(parse->fd, I_POP, (caddr_t)0) == 0)
+ /* empty loop */;
+
+ /*
+ * now push it a second time after we have removed all
+ * module garbage
+ */
+ if (ioctl(parse->fd, I_PUSH, (caddr_t)"parse") == -1)
+ {
+ syslog(LOG_ERR, "PARSE receiver #%d: stream_init: ioctl(fd, I_PUSH, \"parse\"): %m", CL_UNIT(parse->unit));
+ return 0;
+ }
+ else
+ {
+ return 1;
+ }
+ }
+}
+
+ /*--------------------------------------------------
+ * STREAM setcs
+ */
+static int
+stream_setcs(parse, tcl)
+ struct parseunit *parse;
+ parsectl_t *tcl;
+{
+ struct strioctl strioc;
+
+ strioc.ic_cmd = PARSEIOC_SETCS;
+ strioc.ic_timout = 0;
+ strioc.ic_dp = (char *)tcl;
+ strioc.ic_len = sizeof (*tcl);
+
+ if (ioctl(parse->fd, I_STR, (caddr_t)&strioc) == -1)
+ {
+ syslog(LOG_ERR, "PARSE receiver #%d: stream_setcs: ioctl(fd, I_STR, PARSEIOC_SETCS): %m", CL_UNIT(parse->unit));
+ return 0;
+ }
+ return 1;
+}
+
+/*--------------------------------------------------
+ * STREAM nop
+ */
+static void
+stream_nop(parse)
+ struct parseunit *parse;
+{
+}
+
+/*--------------------------------------------------
+ * STREAM enable
+ */
+static int
+stream_enable(parse)
+ struct parseunit *parse;
+{
+ struct strioctl strioc;
+
+ strioc.ic_cmd = PARSEIOC_ENABLE;
+ strioc.ic_timout = 0;
+ strioc.ic_dp = (char *)0;
+ strioc.ic_len = 0;
+
+ if (ioctl(parse->fd, I_STR, (caddr_t)&strioc) == -1)
+ {
+ syslog(LOG_ERR, "PARSE receiver #%d: stream_enable: ioctl(fd, I_STR, PARSEIOC_ENABLE): %m", CL_UNIT(parse->unit));
+ return 0;
+ }
+ return 1;
+}
+
+/*--------------------------------------------------
+ * STREAM disable
+ */
+static int
+stream_disable(parse)
+ struct parseunit *parse;
+{
+ struct strioctl strioc;
+
+ strioc.ic_cmd = PARSEIOC_DISABLE;
+ strioc.ic_timout = 0;
+ strioc.ic_dp = (char *)0;
+ strioc.ic_len = 0;
+
+ if (ioctl(parse->fd, I_STR, (caddr_t)&strioc) == -1)
+ {
+ syslog(LOG_ERR, "PARSE receiver #%d: stream_disable: ioctl(fd, I_STR, PARSEIOC_DISABLE): %m", CL_UNIT(parse->unit));
+ return 0;
+ }
+ return 1;
+}
+
+/*--------------------------------------------------
+ * STREAM getfmt
+ */
+static int
+stream_getfmt(parse, tcl)
+ struct parseunit *parse;
+ parsectl_t *tcl;
+{
+ struct strioctl strioc;
+
+ strioc.ic_cmd = PARSEIOC_GETFMT;
+ strioc.ic_timout = 0;
+ strioc.ic_dp = (char *)tcl;
+ strioc.ic_len = sizeof (*tcl);
+ if (ioctl(parse->fd, I_STR, (caddr_t)&strioc) == -1)
+ {
+ syslog(LOG_ERR, "PARSE receiver #%d: ioctl(fd, I_STR, PARSEIOC_GETFMT): %m", CL_UNIT(parse->unit));
+ return 0;
+ }
+ return 1;
+}
+
+/*--------------------------------------------------
+ * STREAM setfmt
+ */
+static int
+stream_setfmt(parse, tcl)
+ struct parseunit *parse;
+ parsectl_t *tcl;
+{
+ struct strioctl strioc;
+
+ strioc.ic_cmd = PARSEIOC_SETFMT;
+ strioc.ic_timout = 0;
+ strioc.ic_dp = (char *)tcl;
+ strioc.ic_len = sizeof (*tcl);
+
+ if (ioctl(parse->fd, I_STR, (caddr_t)&strioc) == -1)
+ {
+ syslog(LOG_ERR, "PARSE receiver #%d: stream_setfmt: ioctl(fd, I_STR, PARSEIOC_SETFMT): %m", CL_UNIT(parse->unit));
+ return 0;
+ }
+ return 1;
+}
+
+/*--------------------------------------------------
+ * STREAM getstat
+ */
+static int
+stream_getstat(parse, tcl)
+ struct parseunit *parse;
+ parsectl_t *tcl;
+{
+ struct strioctl strioc;
+
+ strioc.ic_cmd = PARSEIOC_GETSTAT;
+ strioc.ic_timout = 0;
+ strioc.ic_dp = (char *)tcl;
+ strioc.ic_len = sizeof (*tcl);
+
+ if (ioctl(parse->fd, I_STR, (caddr_t)&strioc) == -1)
+ {
+ syslog(LOG_ERR, "PARSE receiver #%d: stream_getstat: ioctl(fd, I_STR, PARSEIOC_GETSTAT): %m", CL_UNIT(parse->unit));
+ return 0;
+ }
+ return 1;
+}
+
+/*--------------------------------------------------
+ * STREAM setstat
+ */
+static int
+stream_setstat(parse, tcl)
+ struct parseunit *parse;
+ parsectl_t *tcl;
+{
+ struct strioctl strioc;
+
+ strioc.ic_cmd = PARSEIOC_SETSTAT;
+ strioc.ic_timout = 0;
+ strioc.ic_dp = (char *)tcl;
+ strioc.ic_len = sizeof (*tcl);
+
+ if (ioctl(parse->fd, I_STR, (caddr_t)&strioc) == -1)
+ {
+ syslog(LOG_ERR, "PARSE receiver #%d: stream_setstat: ioctl(fd, I_STR, PARSEIOC_SETSTAT): %m", CL_UNIT(parse->unit));
+ return 0;
+ }
+ return 1;
+}
+
+/*--------------------------------------------------
+ * STREAM timecode
+ */
+static int
+stream_timecode(parse, tcl)
+ struct parseunit *parse;
+ parsectl_t *tcl;
+{
+ struct strioctl strioc;
+
+ strioc.ic_cmd = PARSEIOC_TIMECODE;
+ strioc.ic_timout = 0;
+ strioc.ic_dp = (char *)tcl;
+ strioc.ic_len = sizeof (*tcl);
+
+ if (ioctl(parse->fd, I_STR, (caddr_t)&strioc) == -1)
+ {
+ syslog(LOG_ERR, "PARSE receiver #%d: parse_process: ioctl(fd, I_STR, PARSEIOC_TIMECODE): %m", CL_UNIT(parse->unit), parse->fd);
+ return 0;
+ }
+ return 1;
+}
+
+/*--------------------------------------------------
+ * STREAM receive
+ */
+static void
+stream_receive(rbufp)
+ struct recvbuf *rbufp;
+{
+ struct parseunit *parse = (struct parseunit *)rbufp->recv_srcclock;
+ parsetime_t parsetime;
+
+ if (rbufp->recv_length != sizeof(parsetime_t))
+ {
+ syslog(LOG_ERR,"PARSE receiver #%d: parse_receive: bad size (got %d expected %d)",
+ CL_UNIT(parse->unit), rbufp->recv_length, sizeof(parsetime_t));
+ parse->baddata++;
+ parse_event(parse, CEVNT_BADREPLY);
+ return;
+ }
+ memmove((caddr_t)&parsetime,
+ (caddr_t)&rbufp->recv_space,
+ sizeof(parsetime_t));
+
+ /*
+ * switch time stamp world - be sure to normalize small usec field
+ * errors.
+ */
+
+#define fix_ts(_X_) \
+ if ((&(_X_))->tv.tv_usec >= 1000000) \
+ { \
+ (&(_X_))->tv.tv_usec -= 1000000; \
+ (&(_X_))->tv.tv_sec += 1; \
+ }
+
+#define cvt_ts(_X_, _Y_) \
+ { \
+ l_fp ts; \
+ \
+ fix_ts((_X_)); \
+ if (!buftvtots((const char *)&(&(_X_))->tv, &ts)) \
+ { \
+ syslog(LOG_ERR,"parse: stream_receive: timestamp conversion error (buftvtots) (%s) (%d.%06d) ", (_Y_), (&(_X_))->tv.tv_sec, (&(_X_))->tv.tv_usec);\
+ return; \
+ } \
+ else \
+ { \
+ (&(_X_))->fp = ts; \
+ } \
+ }
+
+ if (PARSE_TIMECODE(parsetime.parse_state))
+ {
+ cvt_ts(parsetime.parse_time, "parse_time");
+ cvt_ts(parsetime.parse_stime, "parse_stime");
+ }
+
+ if (PARSE_PPS(parsetime.parse_state))
+ cvt_ts(parsetime.parse_ptime, "parse_ptime");
+
+ parse_process(parse, &parsetime);
+}
+
+/*--------------------------------------------------
+ * STREAM poll
+ */
+static void
+stream_poll(parse)
+ struct parseunit *parse;
+{
+ register int fd, i, rtc;
+ fd_set fdmask;
+ struct timeval timeout, starttime, curtime, selecttime;
+ parsetime_t parsetime;
+
+ /*
+ * now we do the following:
+ * - read the first packet from the parse module (OLD !!!)
+ * - read the second packet from the parse module (fresh)
+ * - compute values for xntp
+ */
+
+ FD_ZERO(&fdmask);
+ fd = parse->fd;
+ FD_SET(fd, &fdmask);
+ timeout.tv_sec = 0;
+ timeout.tv_usec = 500000; /* 0.5 sec */
+
+ if (parse->parse_type->cl_poll)
+ {
+ parse->parse_type->cl_poll(parse);
+ }
+
+ if (GETTIMEOFDAY(&starttime, 0L) == -1)
+ {
+ syslog(LOG_ERR,"gettimeofday failed: %m");
+ exit(1);
+ }
+
+ selecttime = timeout;
+
+ while ((rtc = select(fd + 1, &fdmask, 0, 0, &selecttime)) != 1)
+ {
+ /* no data from the radio clock */
+
+ if (rtc == -1)
+ {
+ if (errno == EINTR)
+ {
+ if (GETTIMEOFDAY(&curtime, 0L) == -1)
+ {
+ syslog(LOG_ERR,"gettimeofday failed: %m");
+ exit(1);
+ }
+ selecttime.tv_sec = curtime.tv_sec - starttime.tv_sec;
+ if (curtime.tv_usec < starttime.tv_usec)
+ {
+ selecttime.tv_sec -= 1;
+ selecttime.tv_usec = 1000000 + curtime.tv_usec - starttime.tv_usec;
+ }
+ else
+ {
+ selecttime.tv_usec = curtime.tv_usec - starttime.tv_usec;
+ }
+
+
+ if (timercmp(&selecttime, &timeout, >))
+ {
+ /*
+ * elapsed real time passed timeout value - consider it timed out
+ */
+ break;
+ }
+
+ /*
+ * calculate residual timeout value
+ */
+ selecttime.tv_sec = timeout.tv_sec - selecttime.tv_sec;
+
+ if (selecttime.tv_usec > timeout.tv_usec)
+ {
+ selecttime.tv_sec -= 1;
+ selecttime.tv_usec = 1000000 + timeout.tv_usec - selecttime.tv_usec;
+ }
+ else
+ {
+ selecttime.tv_usec = timeout.tv_usec - selecttime.tv_usec;
+ }
+
+ FD_SET(fd, &fdmask);
+ continue;
+ }
+ else
+ {
+ syslog(LOG_WARNING, "PARSE receiver #%d: no data[old] from device (select() error: %m)", CL_UNIT(parse->unit));
+ }
+ }
+ else
+ {
+ syslog(LOG_WARNING, "PARSE receiver #%d: no data[old] from device", CL_UNIT(parse->unit));
+ }
+ parse->noresponse++;
+ parse->lastmissed = current_time;
+ parse_event(parse, CEVNT_TIMEOUT);
+
+ return;
+ }
+
+ while (((i = read(fd, (char *)&parsetime, sizeof(parsetime))) < sizeof(parsetime)))
+ {
+ /* bad packet */
+ if ( i == -1)
+ {
+ if (errno == EINTR)
+ {
+ continue;
+ }
+ else
+ {
+ syslog(LOG_WARNING, "PARSE receiver #%d: bad read[old] from streams module (read() error: %m)", CL_UNIT(parse->unit), i, sizeof(parsetime));
+ }
+ }
+ else
+ {
+ syslog(LOG_WARNING, "PARSE receiver #%d: bad read[old] from streams module (got %d bytes - expected %d bytes)", CL_UNIT(parse->unit), i, sizeof(parsetime));
+ }
+ parse->baddata++;
+ parse_event(parse, CEVNT_BADREPLY);
+
+ return;
+ }
+
+ if (parse->parse_type->cl_poll)
+ {
+ parse->parse_type->cl_poll(parse);
+ }
+
+ timeout.tv_sec = 1;
+ timeout.tv_usec = 500000; /* 1.500 sec */
+ FD_ZERO(&fdmask);
+ FD_SET(fd, &fdmask);
+
+ if (GETTIMEOFDAY(&starttime, 0L) == -1)
+ {
+ syslog(LOG_ERR,"gettimeofday failed: %m");
+ exit(1);
+ }
+
+ selecttime = timeout;
+
+ while ((rtc = select(fd + 1, &fdmask, 0, 0, &selecttime)) != 1)
+ {
+ /* no data from the radio clock */
+
+ if (rtc == -1)
+ {
+ if (errno == EINTR)
+ {
+ if (GETTIMEOFDAY(&curtime, 0L) == -1)
+ {
+ syslog(LOG_ERR,"gettimeofday failed: %m");
+ exit(1);
+ }
+ selecttime.tv_sec = curtime.tv_sec - starttime.tv_sec;
+ if (curtime.tv_usec < starttime.tv_usec)
+ {
+ selecttime.tv_sec -= 1;
+ selecttime.tv_usec = 1000000 + curtime.tv_usec - starttime.tv_usec;
+ }
+ else
+ {
+ selecttime.tv_usec = curtime.tv_usec - starttime.tv_usec;
+ }
+
+
+ if (timercmp(&selecttime, &timeout, >))
+ {
+ /*
+ * elapsed real time passed timeout value - consider it timed out
+ */
+ break;
+ }
+
+ /*
+ * calculate residual timeout value
+ */
+ selecttime.tv_sec = timeout.tv_sec - selecttime.tv_sec;
+
+ if (selecttime.tv_usec > timeout.tv_usec)
+ {
+ selecttime.tv_sec -= 1;
+ selecttime.tv_usec = 1000000 + timeout.tv_usec - selecttime.tv_usec;
+ }
+ else
+ {
+ selecttime.tv_usec = timeout.tv_usec - selecttime.tv_usec;
+ }
+
+ FD_SET(fd, &fdmask);
+ continue;
+ }
+ else
+ {
+ syslog(LOG_WARNING, "PARSE receiver #%d: no data[new] from device (select() error: %m)", CL_UNIT(parse->unit));
+ }
+ }
+ else
+ {
+ syslog(LOG_WARNING, "PARSE receiver #%d: no data[new] from device", CL_UNIT(parse->unit));
+ }
+
+ /*
+ * we will return here iff we got a good old sample as this would
+ * be misinterpreted. bad samples are passed on to be logged into the
+ * state statistics
+ */
+ if ((parsetime.parse_status & CVT_MASK) == CVT_OK)
+ {
+ parse->noresponse++;
+ parse->lastmissed = current_time;
+ parse_event(parse, CEVNT_TIMEOUT);
+ return;
+ }
+ }
+
+ /*
+ * we get here either by a possible read() (rtc == 1 - while assertion)
+ * or by a timeout or a system call error. when a read() is possible we
+ * get the new data, otherwise we stick with the old
+ */
+ if ((rtc == 1) && ((i = read(fd, (char *)&parsetime, sizeof(parsetime))) < sizeof(parsetime)))
+ {
+ /* bad packet */
+ if ( i== -1)
+ {
+ syslog(LOG_WARNING, "PARSE receiver #%d: bad read[new] from streams module (read() error: %m)", CL_UNIT(parse->unit), i, sizeof(parsetime));
+ }
+ else
+ {
+ syslog(LOG_WARNING, "PARSE receiver #%d: bad read[new] from streams module (got %d bytes - expected %d bytes)", CL_UNIT(parse->unit), i, sizeof(parsetime));
+ }
+ parse->baddata++;
+ parse_event(parse, CEVNT_BADREPLY);
+
+ return;
+ }
+
+ /*
+ * process what we got
+ */
+ parse_process(parse, &parsetime);
+}
+#endif
+
+/*--------------------------------------------------
+ * local init
+ */
+static int
+local_init(parse)
+ struct parseunit *parse;
+{
+ return parse_ioinit(&parse->parseio);
+}
+
+/*--------------------------------------------------
+ * local end
+ */
+static void
+local_end(parse)
+ struct parseunit *parse;
+{
+ parse_ioend(&parse->parseio);
+}
+
+
+/*--------------------------------------------------
+ * local nop
+ */
+static int
+local_nop(parse)
+ struct parseunit *parse;
+{
+ return 1;
+}
+
+/*--------------------------------------------------
+ * local setcs
+ */
+static int
+local_setcs(parse, tcl)
+ struct parseunit *parse;
+ parsectl_t *tcl;
+{
+ return parse_setcs(tcl, &parse->parseio);
+}
+
+/*--------------------------------------------------
+ * local getfmt
+ */
+static int
+local_getfmt(parse, tcl)
+ struct parseunit *parse;
+ parsectl_t *tcl;
+{
+ return parse_getfmt(tcl, &parse->parseio);
+}
+
+/*--------------------------------------------------
+ * local setfmt
+ */
+static int
+local_setfmt(parse, tcl)
+ struct parseunit *parse;
+ parsectl_t *tcl;
+{
+ return parse_setfmt(tcl, &parse->parseio);
+}
+
+/*--------------------------------------------------
+ * local getstat
+ */
+static int
+local_getstat(parse, tcl)
+ struct parseunit *parse;
+ parsectl_t *tcl;
+{
+ return parse_getstat(tcl, &parse->parseio);
+}
+
+/*--------------------------------------------------
+ * local setstat
+ */
+static int
+local_setstat(parse, tcl)
+ struct parseunit *parse;
+ parsectl_t *tcl;
+{
+ return parse_setstat(tcl, &parse->parseio);
+}
+
+/*--------------------------------------------------
+ * local timecode
+ */
+static int
+local_timecode(parse, tcl)
+ struct parseunit *parse;
+ parsectl_t *tcl;
+{
+ return parse_timecode(tcl, &parse->parseio);
+}
+
+
+/*--------------------------------------------------
+ * local receive
+ */
+static void
+local_receive(rbufp)
+ struct recvbuf *rbufp;
+{
+ struct parseunit *parse = (struct parseunit *)rbufp->recv_srcclock;
+ register int count;
+ register char *s;
+#ifdef FREEBSD_CONRAD
+ struct timeval foo;
+#endif
+
+ /*
+ * eat all characters, parsing then and feeding complete samples
+ */
+ count = rbufp->recv_length;
+ s = rbufp->recv_buffer;
+#ifdef FREEBSD_CONRAD
+ ioctl(parse->fd,TIOCTIMESTAMP,&foo);
+ TVTOTS(&foo, &rbufp->recv_time);
+ rbufp->recv_time.l_uf += TS_ROUNDBIT;
+ rbufp->recv_time.l_ui += JAN_1970;
+ rbufp->recv_time.l_uf &= TS_MASK;
+#endif
+
+ while (count--)
+ {
+ if (parse_ioread(&parse->parseio, *s++, (timestamp_t *)&rbufp->recv_time))
+ {
+ /*
+ * got something good to eat
+ */
+#ifdef PPSPPS
+ if (!PARSE_PPS(parse->parseio.parse_dtime.parse_state) &&
+ (parse->flags & PARSE_PPSCLOCK))
+ {
+ l_fp ts;
+ struct ppsclockev ev;
+
+ if (ioctl(parse->fd, CIOGETEV, (caddr_t)&ev) == 0)
+ {
+ if (ev.serial != parse->ppsserial)
+ {
+ /*
+ * add PPS time stamp if available via ppsclock module
+ * and not supplied already.
+ */
+ if (!buftvtots((const char *)&ev.tv, &ts))
+ {
+ syslog(LOG_ERR,"parse: local_receive: timestamp conversion error (buftvtots) (ppsclockev.tv)");
+ }
+ else
+ {
+ parse->parseio.parse_dtime.parse_ptime.fp = ts;
+ parse->parseio.parse_dtime.parse_state |= PARSEB_PPS|PARSEB_S_PPS;
+ }
+ }
+ parse->ppsserial = ev.serial;
+ }
+ }
+#endif
+ parse_process(parse, &parse->parseio.parse_dtime);
+ parse_iodone(&parse->parseio);
+ }
+ }
+}
+
+/*--------------------------------------------------
+ * local poll
+ */
+static void
+local_poll(parse)
+ struct parseunit *parse;
+{
+ register int fd, i, rtc;
+ fd_set fdmask;
+ struct timeval timeout, starttime, curtime, selecttime;
+ static struct timeval null_time = { 0, 0};
+ timestamp_t ts;
+
+ FD_ZERO(&fdmask);
+ fd = parse->fd;
+ FD_SET(fd, &fdmask);
+ timeout.tv_sec = 1;
+ timeout.tv_usec = 500000; /* 1.5 sec */
+
+ if (parse->parse_type->cl_poll)
+ {
+ parse->parse_type->cl_poll(parse);
+ }
+
+ if (GETTIMEOFDAY(&starttime, 0L) == -1)
+ {
+ syslog(LOG_ERR,"gettimeofday failed: %m");
+ exit(1);
+ }
+
+ selecttime = timeout;
+
+ do
+ {
+ while ((rtc = select(fd + 1, &fdmask, 0, 0, &selecttime)) != 1)
+ {
+ /* no data from the radio clock */
+
+ if (rtc == -1)
+ {
+ if (errno == EINTR)
+ {
+ if (GETTIMEOFDAY(&curtime, 0L) == -1)
+ {
+ syslog(LOG_ERR,"gettimeofday failed: %m");
+ exit(1);
+ }
+ selecttime.tv_sec = curtime.tv_sec - starttime.tv_sec;
+ if (curtime.tv_usec < starttime.tv_usec)
+ {
+ selecttime.tv_sec -= 1;
+ selecttime.tv_usec = 1000000 + curtime.tv_usec - starttime.tv_usec;
+ }
+ else
+ {
+ selecttime.tv_usec = curtime.tv_usec - starttime.tv_usec;
+ }
+
+
+ if (!timercmp(&selecttime, &timeout, >))
+ {
+ /*
+ * calculate residual timeout value
+ */
+ selecttime.tv_sec = timeout.tv_sec - selecttime.tv_sec;
+
+ if (selecttime.tv_usec > timeout.tv_usec)
+ {
+ selecttime.tv_sec -= 1;
+ selecttime.tv_usec = 1000000 + timeout.tv_usec - selecttime.tv_usec;
+ }
+ else
+ {
+ selecttime.tv_usec = timeout.tv_usec - selecttime.tv_usec;
+ }
+
+ FD_SET(fd, &fdmask);
+ continue;
+ }
+ }
+ else
+ {
+ syslog(LOG_WARNING, "PARSE receiver #%d: no data from device (select() error: %m)", CL_UNIT(parse->unit));
+ }
+ }
+ else
+ {
+ syslog(LOG_WARNING, "PARSE receiver #%d: no data from device", CL_UNIT(parse->unit));
+ }
+
+ parse->noresponse++;
+ parse->lastmissed = current_time;
+ parse_event(parse, CEVNT_TIMEOUT);
+
+ return;
+ }
+
+ /*
+ * at least 1 character is available - gobble everthing up that is available
+ */
+ do
+ {
+ char inbuf[256];
+
+ register char *s = inbuf;
+
+ rtc = i = read(fd, inbuf, sizeof(inbuf));
+
+ get_systime(&ts.fp);
+
+ while (i-- > 0)
+ {
+ if (parse_ioread(&parse->parseio, *s++, &ts))
+ {
+ /*
+ * got something good to eat
+ */
+ parse_process(parse, &parse->parseio.parse_dtime);
+ parse_iodone(&parse->parseio);
+ /*
+ * done if no more characters are available
+ */
+ FD_SET(fd, &fdmask);
+ if ((i == 0) &&
+ (select(fd + 1, &fdmask, 0, 0, &null_time) == 0))
+ return;
+ }
+ }
+ FD_SET(fd, &fdmask);
+ } while ((rtc = select(fd + 1, &fdmask, 0, 0, &null_time)) == 1);
+ FD_SET(fd, &fdmask);
+ } while (1);
+}
+
+/*--------------------------------------------------
+ * init_iobinding - find and initialize lower layers
+ */
+static bind_t *
+init_iobinding(parse)
+ struct parseunit *parse;
+{
+ register bind_t *b = io_bindings;
+
+ while (b->bd_description != (char *)0)
+ {
+ if ((*b->bd_init)(parse))
+ {
+ return b;
+ }
+ b++;
+ }
+ return (bind_t *)0;
+}
+
+/**===========================================================================
+ ** support routines
+ **/
+
+/*--------------------------------------------------
+ * convert a flag field to a string
+ */
+static char *
+parsestate(state, buffer)
+ unsigned LONG state;
+ char *buffer;
+{
+ static struct bits
+ {
+ unsigned LONG bit;
+ char *name;
+ } flagstrings[] =
+ {
+ { PARSEB_ANNOUNCE, "DST SWITCH WARNING" },
+ { PARSEB_POWERUP, "NOT SYNCHRONIZED" },
+ { PARSEB_NOSYNC, "TIME CODE NOT CONFIRMED" },
+ { PARSEB_DST, "DST" },
+ { PARSEB_UTC, "UTC DISPLAY" },
+ { PARSEB_LEAPADD, "LEAP ADD WARNING" },
+ { PARSEB_LEAPDEL, "LEAP DELETE WARNING" },
+ { PARSEB_LEAPSECOND, "LEAP SECOND" },
+ { PARSEB_ALTERNATE,"ALTERNATE ANTENNA" },
+ { PARSEB_TIMECODE, "TIME CODE" },
+ { PARSEB_PPS, "PPS" },
+ { PARSEB_POSITION, "POSITION" },
+ { 0 }
+ };
+
+ static struct sbits
+ {
+ unsigned LONG bit;
+ char *name;
+ } sflagstrings[] =
+ {
+ { PARSEB_S_LEAP, "LEAP INDICATION" },
+ { PARSEB_S_PPS, "PPS SIGNAL" },
+ { PARSEB_S_ANTENNA, "ANTENNA" },
+ { PARSEB_S_POSITION, "POSITION" },
+ { 0 }
+ };
+ int i;
+
+ *buffer = '\0';
+
+ i = 0;
+ while (flagstrings[i].bit)
+ {
+ if (flagstrings[i].bit & state)
+ {
+ if (buffer[0])
+ strcat(buffer, "; ");
+ strcat(buffer, flagstrings[i].name);
+ }
+ i++;
+ }
+
+ if (state & (PARSEB_S_LEAP|PARSEB_S_ANTENNA|PARSEB_S_PPS|PARSEB_S_POSITION))
+ {
+ register char *s, *t;
+
+ if (buffer[0])
+ strcat(buffer, "; ");
+
+ strcat(buffer, "(");
+
+ t = s = buffer + strlen(buffer);
+
+ i = 0;
+ while (sflagstrings[i].bit)
+ {
+ if (sflagstrings[i].bit & state)
+ {
+ if (t != s)
+ {
+ strcpy(t, "; ");
+ t += 2;
+ }
+
+ strcpy(t, sflagstrings[i].name);
+ t += strlen(t);
+ }
+ i++;
+ }
+ strcpy(t, ")");
+ }
+ return buffer;
+}
+
+/*--------------------------------------------------
+ * convert a status flag field to a string
+ */
+static char *
+parsestatus(state, buffer)
+ unsigned LONG state;
+ char *buffer;
+{
+ static struct bits
+ {
+ unsigned LONG bit;
+ char *name;
+ } flagstrings[] =
+ {
+ { CVT_OK, "CONVERSION SUCCESSFUL" },
+ { CVT_NONE, "NO CONVERSION" },
+ { CVT_FAIL, "CONVERSION FAILED" },
+ { CVT_BADFMT, "ILLEGAL FORMAT" },
+ { CVT_BADDATE, "DATE ILLEGAL" },
+ { CVT_BADTIME, "TIME ILLEGAL" },
+ { 0 }
+ };
+ int i;
+
+ *buffer = '\0';
+
+ i = 0;
+ while (flagstrings[i].bit)
+ {
+ if (flagstrings[i].bit & state)
+ {
+ if (buffer[0])
+ strcat(buffer, "; ");
+ strcat(buffer, flagstrings[i].name);
+ }
+ i++;
+ }
+
+ return buffer;
+}
+
+/*--------------------------------------------------
+ * convert a clock status flag field to a string
+ */
+static char *
+clockstatus(state)
+ unsigned LONG state;
+{
+ static char buffer[20];
+ static struct status
+ {
+ unsigned LONG value;
+ char *name;
+ } flagstrings[] =
+ {
+ { CEVNT_NOMINAL, "NOMINAL" },
+ { CEVNT_TIMEOUT, "NO RESPONSE" },
+ { CEVNT_BADREPLY,"BAD FORMAT" },
+ { CEVNT_FAULT, "FAULT" },
+ { CEVNT_PROP, "PROPAGATION DELAY" },
+ { CEVNT_BADDATE, "ILLEGAL DATE" },
+ { CEVNT_BADTIME, "ILLEGAL TIME" },
+ { ~0 }
+ };
+ int i;
+
+ i = 0;
+ while (flagstrings[i].value != ~0)
+ {
+ if (flagstrings[i].value == state)
+ {
+ return flagstrings[i].name;
+ }
+ i++;
+ }
+
+ sprintf(buffer, "unknown #%d", state);
+
+ return buffer;
+}
+
+/*--------------------------------------------------
+ * mkascii - make a printable ascii string
+ * assumes (unless defined better) 7-bit ASCII
+ */
+#ifndef isprint
+#define isprint(_X_) (((_X_) > 0x1F) && ((_X_) < 0x7F))
+#endif
+
+static char *
+mkascii(buffer, blen, src, srclen)
+ register char *buffer;
+ register LONG blen;
+ register char *src;
+ register LONG srclen;
+{
+ register char *b = buffer;
+ register char *endb = (char *)0;
+
+ if (blen < 4)
+ return (char *)0; /* don't bother with mini buffers */
+
+ endb = buffer + blen - 4;
+
+ blen--; /* account for '\0' */
+
+ while (blen && srclen--)
+ {
+ if ((*src != '\\') && isprint(*src))
+ { /* printables are easy... */
+ *buffer++ = *src++;
+ blen--;
+ }
+ else
+ {
+ if (blen < 4)
+ {
+ while (blen--)
+ {
+ *buffer++ = '.';
+ }
+ *buffer = '\0';
+ return b;
+ }
+ else
+ {
+ if (*src == '\\')
+ {
+ strcpy(buffer,"\\\\");
+ buffer += 2;
+ blen -= 2;
+ }
+ else
+ {
+ sprintf(buffer, "\\x%02x", *src++);
+ blen -= 4;
+ buffer += 4;
+ }
+ }
+ }
+ if (srclen && !blen && endb) /* overflow - set last chars to ... */
+ strcpy(endb, "...");
+ }
+
+ *buffer = '\0';
+ return b;
+}
+
+
+/*--------------------------------------------------
+ * l_mktime - make representation of a relative time
+ */
+static char *
+l_mktime(delta)
+ unsigned LONG delta;
+{
+ unsigned LONG tmp, m, s;
+ static char buffer[40];
+
+ buffer[0] = '\0';
+
+ if ((tmp = delta / (60*60*24)) != 0)
+ {
+ sprintf(buffer, "%dd+", tmp);
+ delta -= tmp * 60*60*24;
+ }
+
+ s = delta % 60;
+ delta /= 60;
+ m = delta % 60;
+ delta /= 60;
+
+ sprintf(buffer+strlen(buffer), "%02d:%02d:%02d",
+ delta, m, s);
+
+ return buffer;
+}
+
+
+/*--------------------------------------------------
+ * parse_statistics - list summary of clock states
+ */
+static void
+parse_statistics(parse)
+ register struct parseunit *parse;
+{
+ register int i;
+
+ syslog(LOG_INFO, "PARSE receiver #%d: running time: %s",
+ CL_UNIT(parse->unit),
+ l_mktime(current_time - parse->timestarted));
+
+ syslog(LOG_INFO, "PARSE receiver #%d: current status: %s",
+ CL_UNIT(parse->unit),
+ clockstatus(parse->status));
+
+ for (i = 0; i <= CEVNT_MAX; i++)
+ {
+ register unsigned LONG stime;
+ register unsigned LONG percent, div = current_time - parse->timestarted;
+
+ percent = stime = PARSE_STATETIME(parse, i);
+
+ while (((unsigned LONG)(~0) / 10000) < percent)
+ {
+ percent /= 10;
+ div /= 10;
+ }
+
+ if (div)
+ percent = (percent * 10000) / div;
+ else
+ percent = 10000;
+
+ if (stime)
+ syslog(LOG_INFO, "PARSE receiver #%d: state %18s: %13s (%3d.%02d%%)",
+ CL_UNIT(parse->unit),
+ clockstatus(i),
+ l_mktime(stime),
+ percent / 100, percent % 100);
+ }
+}
+
+/*--------------------------------------------------
+ * cparse_statistics - wrapper for statistics call
+ */
+static void
+cparse_statistics(peer)
+ register struct peer *peer;
+{
+ register struct parseunit *parse = (struct parseunit *)peer;
+
+ parse_statistics(parse);
+ parse->stattimer.event_time = current_time + PARSESTATISTICS;
+ TIMER_ENQUEUE(timerqueue, &parse->stattimer);
+}
+
+/**===========================================================================
+ ** xntp interface routines
+ **/
+
+/*--------------------------------------------------
+ * parse_init - initialize internal parse driver data
+ */
+static void
+parse_init()
+{
+ memset((caddr_t)parseunits, 0, sizeof parseunits);
+}
+
+
+/*--------------------------------------------------
+ * parse_shutdown - shut down a PARSE clock
+ */
+static void
+parse_shutdown(unit, peer)
+ int unit;
+ struct peer *peer;
+{
+ register struct parseunit *parse;
+
+ unit = CL_UNIT(unit);
+
+ if (unit >= MAXUNITS) {
+ syslog(LOG_ERR,
+ "PARSE receiver #%d: parse_shutdown: INTERNAL ERROR, unit invalid (max %d)",
+ unit,MAXUNITS);
+ return;
+ }
+
+ parse = parseunits[unit];
+
+ if (parse && !parse->peer) {
+ syslog(LOG_ERR,
+ "PARSE receiver #%d: parse_shutdown: INTERNAL ERROR, unit not in use", unit);
+ return;
+ }
+
+ /*
+ * print statistics a last time and
+ * stop statistics machine
+ */
+ parse_statistics(parse);
+ TIMER_DEQUEUE(&parse->stattimer);
+
+#if PPSPPS
+ {
+ /*
+ * kill possible PPS association
+ */
+ if (fdpps == parse->fd)
+ fdpps = -1;
+ }
+#endif
+
+ if (parse->parse_type->cl_end)
+ {
+ parse->parse_type->cl_end(parse);
+ }
+
+ if (parse->binding)
+ PARSE_END(parse);
+
+ /*
+ * Tell the I/O module to turn us off. We're history.
+ */
+ if (!parse->pollonly)
+ io_closeclock(&parse->io);
+ else
+ (void) close(parse->fd);
+
+ syslog(LOG_INFO, "PARSE receiver #%d: reference clock \"%s\" removed",
+ CL_UNIT(parse->unit), parse->parse_type->cl_description);
+
+ parse->peer = (struct peer *)0; /* unused now */
+}
+
+/*--------------------------------------------------
+ * parse_start - open the PARSE devices and initialize data for processing
+ */
+static int
+parse_start(sysunit, peer)
+ int sysunit;
+ struct peer *peer;
+{
+ u_int unit;
+ int fd232, i;
+#ifdef HAVE_TERMIOS
+ struct termios tm; /* NEEDED FOR A LONG TIME ! */
+#endif
+#ifdef HAVE_SYSV_TTYS
+ struct termio tm; /* NEEDED FOR A LONG TIME ! */
+#endif
+ struct parseunit * parse;
+ char parsedev[sizeof(PARSEDEVICE)+20];
+ parsectl_t tmp_ctl;
+ u_int type;
+
+ type = CL_TYPE(sysunit);
+ unit = CL_UNIT(sysunit);
+
+ if (unit >= MAXUNITS)
+ {
+ syslog(LOG_ERR, "PARSE receiver #%d: parse_start: unit number invalid (max %d)",
+ unit, MAXUNITS-1);
+ return 0;
+ }
+
+ if ((type == ~0) || (clockinfo[type].cl_description == (char *)0))
+ {
+ syslog(LOG_ERR, "PARSE receiver #%d: parse_start: unsupported clock type %d (max %d)",
+ unit, CL_REALTYPE(sysunit), ncltypes-1);
+ return 0;
+ }
+
+ if (parseunits[unit] && parseunits[unit]->peer)
+ {
+ syslog(LOG_ERR, "PARSE receiver #%d: parse_start: unit in use", unit);
+ return 0;
+ }
+
+ /*
+ * Unit okay, attempt to open the device.
+ */
+ (void) sprintf(parsedev, PARSEDEVICE, unit);
+
+#if defined(SYS_FREEBSD) && defined(BOEDER)
+ fd232 = open(parsedev, O_RDONLY | O_NONBLOCK, 0777);
+#else
+#ifndef O_NOCTTY
+#define O_NOCTTY 0
+#endif
+
+ fd232 = open(parsedev, O_RDWR|O_NOCTTY, 0777);
+#endif
+ if (fd232 == -1)
+ {
+ syslog(LOG_ERR, "PARSE receiver #%d: parse_start: open of %s failed: %m", unit, parsedev);
+ return 0;
+ }
+
+ /*
+ * Looks like this might succeed. Find memory for the structure.
+ * Look to see if there are any unused ones, if not we malloc()
+ * one.
+ */
+ if (parseunits[unit])
+ {
+ parse = parseunits[unit]; /* The one we want is okay - and free */
+ }
+ else
+ {
+ for (i = 0; i < MAXUNITS; i++)
+ {
+ if (parseunits[i] && !parseunits[i]->peer)
+ break;
+ }
+ if (i < MAXUNITS)
+ {
+ /*
+ * Reclaim this one
+ */
+ parse = parseunits[i];
+ parseunits[i] = (struct parseunit *)0;
+ }
+ else
+ {
+ parse = (struct parseunit *)
+ emalloc(sizeof(struct parseunit));
+ }
+ }
+
+ memset((char *)parse, 0, sizeof(struct parseunit));
+ parseunits[unit] = parse;
+
+ /*
+ * Set up the structures
+ */
+ parse->unit = (u_char)sysunit;
+ parse->timestarted = current_time;
+ parse->lastchange = current_time;
+ /*
+ * we want to filter input for the sake of
+ * getting an impression on dispersion
+ * also we like to average the median range
+ */
+ parse->flags = PARSE_STAT_FILTER|PARSE_STAT_AVG;
+ parse->pollneeddata = 0;
+ parse->pollonly = 1; /* go for default polling mode */
+ parse->lastformat = ~0; /* assume no format known */
+ parse->status = CEVNT_TIMEOUT; /* expect the worst */
+ parse->laststatus = ~0; /* be sure to mark initial status change */
+ parse->nosynctime = 0; /* assume clock reasonable */
+ parse->lastmissed = 0; /* assume got everything */
+ parse->ppsserial = 0;
+ parse->localdata = (void *)0;
+
+ parse->parse_type = &clockinfo[type];
+
+ parse->basedelay.l_ui = 0; /* we can only pre-configure delays less than 1 second */
+ parse->basedelay.l_uf = parse->parse_type->cl_basedelay;
+
+ parse->ppsdelay.l_ui = 0; /* we can only pre-configure delays less than 1 second */
+ parse->ppsdelay.l_uf = parse->parse_type->cl_ppsdelay;
+
+ peer->rootdelay = parse->parse_type->cl_rootdelay;
+ peer->sstclktype = parse->parse_type->cl_type;
+ peer->precision = sys_precision;
+ peer->stratum = STRATUM_REFCLOCK;
+ if (peer->stratum <= 1)
+ memmove((char *)&peer->refid, parse->parse_type->cl_id, 4);
+ else
+ peer->refid = htonl(PARSEHSREFID);
+
+ parse->fd = fd232;
+
+ parse->peer = peer; /* marks it also as busy */
+
+ parse->binding = init_iobinding(parse);
+
+ if (parse->binding == (bind_t *)0)
+ {
+ syslog(LOG_ERR, "PARSE receiver #%d: parse_start: io sub system initialisation failed.");
+ parse_shutdown(parse->unit, peer); /* let our cleaning staff do the work */
+ return 0; /* well, ok - special initialisation broke */
+ }
+
+ /*
+ * configure terminal line
+ */
+ if (TTY_GETATTR(fd232, &tm) == -1)
+ {
+ syslog(LOG_ERR, "PARSE receiver #%d: parse_start: tcgetattr(%d, &tm): %m", unit, fd232);
+ parse_shutdown(parse->unit, peer); /* let our cleaning staff do the work */
+ return 0;
+ }
+ else
+ {
+#ifndef _PC_VDISABLE
+ memset((char *)tm.c_cc, 0, sizeof(tm.c_cc));
+#else
+ int disablec;
+ errno = 0; /* pathconf can deliver -1 without changing errno ! */
+
+ disablec = fpathconf(parse->fd, _PC_VDISABLE);
+ if (disablec == -1 && errno)
+ {
+ syslog(LOG_ERR, "PARSE receiver #%d: parse_start: fpathconf(fd, _PC_VDISABLE): %m", CL_UNIT(parse->unit));
+ memset((char *)tm.c_cc, 0, sizeof(tm.c_cc)); /* best guess */
+ }
+ else
+ if (disablec != -1)
+ memset((char *)tm.c_cc, disablec, sizeof(tm.c_cc));
+#endif
+
+ tm.c_cflag = clockinfo[type].cl_cflag;
+ tm.c_iflag = clockinfo[type].cl_iflag;
+ tm.c_oflag = clockinfo[type].cl_oflag;
+ tm.c_lflag = clockinfo[type].cl_lflag;
+#if defined(SYS_FREEBSD) && (defined(BOEDER) || defined(FREEBSD_CONRAD))
+ if (cfsetspeed(&tm, B50) == -1)
+ {
+ syslog(LOG_ERR,
+ "PARSE receiver #%d: parse_start: cfsetspeed(&tm, B50): %m",
+ unit);
+ parse_shutdown(parse->unit, peer); /* let our cleaning staff do the work */
+ return 0;
+ }
+#endif
+ if (TTY_SETATTR(fd232, &tm) == -1)
+ {
+ syslog(LOG_ERR, "PARSE receiver #%d: parse_start: tcsetattr(%d, &tm): %m", unit, fd232);
+ parse_shutdown(parse->unit, peer); /* let our cleaning staff do the work */
+ return 0;
+ }
+ }
+
+ /*
+ * as we always(?) get 8 bit chars we want to be
+ * sure, that the upper bits are zero for less
+ * than 8 bit I/O - so we pass that information on.
+ * note that there can be only one bit count format
+ * per file descriptor
+ */
+
+ switch (tm.c_cflag & CSIZE)
+ {
+ case CS5:
+ tmp_ctl.parsesetcs.parse_cs = PARSE_IO_CS5;
+ break;
+
+ case CS6:
+ tmp_ctl.parsesetcs.parse_cs = PARSE_IO_CS6;
+ break;
+
+ case CS7:
+ tmp_ctl.parsesetcs.parse_cs = PARSE_IO_CS7;
+ break;
+
+ case CS8:
+ tmp_ctl.parsesetcs.parse_cs = PARSE_IO_CS8;
+ break;
+ }
+
+ if (!PARSE_SETCS(parse, &tmp_ctl))
+ {
+ syslog(LOG_ERR, "PARSE receiver #%d: parse_start: parse_setcs() FAILED.", unit);
+ parse_shutdown(parse->unit, peer); /* let our cleaning staff do the work */
+ return 0; /* well, ok - special initialisation broke */
+ }
+
+#ifdef FREEBSD_CONRAD
+ {
+ int i,j;
+ struct timeval tv;
+ ioctl(parse->fd,TIOCTIMESTAMP,&tv);
+ j = TIOCM_RTS;
+ i = ioctl(fd232, TIOCMBIC, &j);
+ if (i < 0) {
+ syslog(LOG_ERR,
+ "PARSE receiver #%d: lowrts_poll: failed to lower RTS: %m",
+ CL_UNIT(parse->unit));
+ }
+ }
+#endif
+#if defined(SYS_FREEBSD) && defined(BOEDER)
+ if (fcntl(fd232, F_SETFL, fcntl(fd232, F_GETFL, 0) & ~O_NONBLOCK) == -1)
+ {
+ syslog(LOG_ERR,
+ "PARSE receiver #%d: parse_start: fcntl(%d, F_SETFL, ...): %m",
+ unit, fd232);
+ parse_shutdown(parse->unit, peer); /* let our cleaning staff do the work */
+ return 0;
+ }
+
+ if (ioctl(fd232, TIOCCDTR, 0) == -1)
+ {
+ syslog(LOG_ERR,
+ "PARSE receiver #%d: parse_start: ioctl(%d, TIOCCDTR, 0): %m",
+ unit, fd232);
+ parse_shutdown(parse->unit, peer); /* let our cleaning staff do the work */
+ return 0;
+ }
+#endif
+
+ strcpy(tmp_ctl.parseformat.parse_buffer, parse->parse_type->cl_format);
+ tmp_ctl.parseformat.parse_count = strlen(tmp_ctl.parseformat.parse_buffer);
+
+ if (!PARSE_SETFMT(parse, &tmp_ctl))
+ {
+ syslog(LOG_ERR, "PARSE receiver #%d: parse_start: parse_setfmt() FAILED.", unit);
+ parse_shutdown(parse->unit, peer); /* let our cleaning staff do the work */
+ return 0; /* well, ok - special initialisation broke */
+ }
+
+#ifdef TCFLSH
+ /*
+ * get rid of all IO accumulated so far
+ */
+ {
+#ifndef TCIOFLUSH
+#define TCIOFLUSH 2
+#endif
+ int flshcmd = TCIOFLUSH;
+
+ (void) ioctl(parse->fd, TCFLSH, (caddr_t)&flshcmd);
+ }
+#endif
+
+ tmp_ctl.parsestatus.flags = parse->flags & PARSE_STAT_FLAGS;
+
+ if (!PARSE_SETSTAT(parse, &tmp_ctl))
+ {
+ syslog(LOG_ERR, "PARSE receiver #%d: parse_start: parse_setstat() FAILED.", unit);
+ parse_shutdown(parse->unit, peer); /* let our cleaning staff do the work */
+ return 0; /* well, ok - special initialisation broke */
+ }
+
+ /*
+ * try to do any special initializations
+ */
+ if (parse->parse_type->cl_init)
+ {
+ if (parse->parse_type->cl_init(parse))
+ {
+ parse_shutdown(parse->unit, peer); /* let our cleaning staff do the work */
+ return 0; /* well, ok - special initialisation broke */
+ }
+ }
+
+ if (!(parse->parse_type->cl_flags & PARSE_F_POLLONLY) &&
+ (CL_PPS(parse->unit) || (parse->parse_type->cl_flags & PARSE_F_NOPOLLONLY)))
+ {
+ /*
+ * Insert in async io device list.
+ */
+ parse->io.clock_recv = parse->binding->bd_receive; /* pick correct receive routine */
+ parse->io.srcclock = (caddr_t)parse;
+ parse->io.datalen = 0;
+ parse->io.fd = parse->fd; /* replicated, but what the heck */
+ if (!io_addclock(&parse->io))
+ {
+ if (parse->parse_type->cl_flags & PARSE_F_NOPOLLONLY)
+ {
+ syslog(LOG_ERR,
+ "PARSE receiver #%d: parse_start: addclock %s fails (ABORT - clock type requires async io)", CL_UNIT(parse->unit), parsedev);
+ parse_shutdown(parse->unit, peer); /* let our cleaning staff do the work */
+ return 0;
+ }
+ else
+ {
+ syslog(LOG_ERR,
+ "PARSE receiver #%d: parse_start: addclock %s fails (switching to polling mode)", CL_UNIT(parse->unit), parsedev);
+ }
+ }
+ else
+ {
+ parse->pollonly = 0; /*
+ * update at receipt of time_stamp - also
+ * supports PPS processing
+ */
+ }
+ }
+
+#ifdef PPSPPS
+ if (parse->pollonly || (parse->parse_type->cl_flags & PARSE_F_PPSPPS))
+ {
+ if (fdpps == -1)
+ {
+ fdpps = parse->fd;
+ if (!PARSE_DISABLE(parse))
+ {
+ syslog(LOG_ERR, "PARSE receiver #%d: parse_start: parse_disable() FAILED", CL_UNIT(parse->unit));
+ parse_shutdown(parse->unit, peer); /* let our cleaning staff do the work */
+ return 0;
+ }
+ }
+ else
+ {
+ syslog(LOG_NOTICE, "PARSE receiver #%d: parse_start: loopfilter PPS already active - no PPS via CIOGETEV", CL_UNIT(parse->unit));
+ }
+ }
+#endif
+
+ /*
+ * wind up statistics timer
+ */
+ parse->stattimer.peer = (struct peer *)parse; /* we know better, but what the heck */
+ parse->stattimer.event_handler = cparse_statistics;
+ parse->stattimer.event_time = current_time + PARSESTATISTICS;
+ TIMER_ENQUEUE(timerqueue, &parse->stattimer);
+
+ /*
+ * get out Copyright information once
+ */
+ if (!notice)
+ {
+ syslog(LOG_INFO, "NTP PARSE support: Copyright (c) 1989-1993, Frank Kardel");
+ notice = 1;
+ }
+
+ /*
+ * print out configuration
+ */
+ syslog(LOG_INFO, "PARSE receiver #%d: reference clock \"%s\" (device %s) added",
+ CL_UNIT(parse->unit),
+ parse->parse_type->cl_description, parsedev);
+
+ syslog(LOG_INFO, "PARSE receiver #%d: Stratum %d, %sPPS support, trust time %s, precision %d",
+ CL_UNIT(parse->unit),
+ parse->peer->stratum, (parse->pollonly || !CL_PPS(parse->unit)) ? "no " : "",
+ l_mktime(parse->parse_type->cl_maxunsync), parse->peer->precision);
+
+ syslog(LOG_INFO, "PARSE receiver #%d: rootdelay %s s, phaseadjust %s s, %s IO handling",
+ CL_UNIT(parse->unit),
+ ufptoa(parse->parse_type->cl_rootdelay, 6),
+ lfptoa(&parse->basedelay, 8),
+ parse->binding->bd_description);
+
+ syslog(LOG_INFO, "PARSE receiver #%d: Format recognition: %s", CL_UNIT(parse->unit),
+ !(*parse->parse_type->cl_format) ? "<AUTOMATIC>" : parse->parse_type->cl_format);
+
+#ifdef PPSPPS
+ syslog(LOG_INFO, "PARSE receiver #%d: %sCD PPS support",
+ CL_UNIT(parse->unit),
+ (fdpps == parse->fd) ? "" : "NO ");
+#endif
+
+ return 1;
+}
+
+/*--------------------------------------------------
+ * parse_poll - called by the transmit procedure
+ */
+static void
+parse_poll(unit, peer)
+ int unit;
+ struct peer *peer;
+{
+ register struct parseunit *parse;
+
+ unit = CL_UNIT(unit);
+
+ if (unit >= MAXUNITS)
+ {
+ syslog(LOG_ERR, "PARSE receiver #%d: poll: INTERNAL: unit invalid",
+ unit);
+ return;
+ }
+
+ parse = parseunits[unit];
+
+ if (!parse->peer)
+ {
+ syslog(LOG_ERR, "PARSE receiver #%d: poll: INTERNAL: unit unused",
+ unit);
+ return;
+ }
+
+ if (peer != parse->peer)
+ {
+ syslog(LOG_ERR,
+ "PARSE receiver #%d: poll: INTERNAL: peer incorrect",
+ unit);
+ return;
+ }
+
+ /*
+ * Update clock stat counters
+ */
+ parse->polls++;
+
+ /*
+ * in PPS mode we just mark that we want the next sample
+ * for the clock filter
+ */
+ if (!parse->pollonly)
+ {
+ if (parse->pollneeddata)
+ {
+ /*
+ * bad news - didn't get a response last time
+ */
+ parse->noresponse++;
+ parse->lastmissed = current_time;
+ parse_event(parse, CEVNT_TIMEOUT);
+
+ syslog(LOG_WARNING, "PARSE receiver #%d: no data from device within poll interval", CL_UNIT(parse->unit));
+ }
+ parse->pollneeddata = 1;
+ if (parse->parse_type->cl_poll)
+ {
+ parse->parse_type->cl_poll(parse);
+ }
+ return;
+ }
+
+ /*
+ * the following code is only executed only when polling is used
+ */
+
+ PARSE_POLL(parse);
+}
+
+/*--------------------------------------------------
+ * parse_leap - called when a leap second occurs
+ */
+
+static void
+parse_leap()
+{
+ /*
+ * PARSE encodes the LEAP correction direction.
+ * For timecodes that do not pass on the leap correction direction
+ * the default PARSEB_LEAPADD must be used. It may then be modified
+ * with a fudge flag (flag2).
+ */
+}
+
+
+/*--------------------------------------------------
+ * parse_control - set fudge factors, return statistics
+ */
+static void
+parse_control(unit, in, out)
+ int unit;
+ struct refclockstat *in;
+ struct refclockstat *out;
+{
+ register struct parseunit *parse;
+ parsectl_t tmpctl;
+ unsigned LONG type;
+ static char outstatus[400]; /* status output buffer */
+
+ type = CL_TYPE(unit);
+ unit = CL_UNIT(unit);
+
+ if (out)
+ {
+ out->lencode = 0;
+ out->lastcode = 0;
+ out->polls = out->noresponse = 0;
+ out->badformat = out->baddata = 0;
+ out->timereset = 0;
+ out->currentstatus = out->lastevent = CEVNT_NOMINAL;
+ out->kv_list = (struct ctl_var *)0;
+ }
+
+ if (unit >= MAXUNITS)
+ {
+ syslog(LOG_ERR, "PARSE receiver #%d: parse_control: unit invalid (max %d)",
+ unit, MAXUNITS-1);
+ return;
+ }
+
+ parse = parseunits[unit];
+
+ if (!parse || !parse->peer)
+ {
+ syslog(LOG_ERR, "PARSE receiver #%d: parse_control: unit invalid (UNIT INACTIVE)",
+ unit);
+ return;
+ }
+
+ if (in)
+ {
+ if (in->haveflags & CLK_HAVETIME1)
+ parse->basedelay = in->fudgetime1;
+
+ if (in->haveflags & CLK_HAVETIME2)
+ {
+ parse->ppsdelay = in->fudgetime2;
+ }
+
+ if (in->haveflags & CLK_HAVEVAL1)
+ {
+ parse->peer->stratum = (u_char)(in->fudgeval1 & 0xf);
+ if (parse->peer->stratum <= 1)
+ memmove((char *)&parse->peer->refid,
+ parse->parse_type->cl_id,
+ 4);
+ else
+ parse->peer->refid = htonl(PARSEHSREFID);
+ }
+
+ /*
+ * NOT USED - yet
+ *
+ if (in->haveflags & CLK_HAVEVAL2)
+ {
+ }
+ */
+ if (in->haveflags & (CLK_HAVEFLAG1|CLK_HAVEFLAG2|CLK_HAVEFLAG3|CLK_HAVEFLAG4))
+ {
+ parse->flags = (in->flags & (CLK_FLAG1|CLK_FLAG2|CLK_FLAG3|CLK_FLAG4)) |
+ (parse->flags & ~PARSE_STAT_FLAGS);
+ }
+
+ if (in->haveflags & (CLK_HAVEVAL2|CLK_HAVETIME2|CLK_HAVEFLAG1|CLK_HAVEFLAG2|CLK_HAVEFLAG3|CLK_HAVEFLAG4))
+ {
+ parsectl_t tmpctl;
+ tmpctl.parsestatus.flags = parse->flags & PARSE_STAT_FLAGS;
+
+ if (!PARSE_SETSTAT(parse, &tmpctl))
+ {
+ syslog(LOG_ERR, "PARSE receiver #%d: parse_control: parse_setstat() FAILED", unit);
+ }
+ }
+ }
+
+ if (out)
+ {
+ register unsigned LONG sum = 0;
+ register char *t, *tt;
+ register struct tm *tm;
+ register short utcoff;
+ register char sign;
+ register int i;
+ time_t tim;
+
+ outstatus[0] = '\0';
+
+ out->haveflags = CLK_HAVETIME1|CLK_HAVETIME2|CLK_HAVEVAL1|CLK_HAVEFLAG1|CLK_HAVEFLAG2|CLK_HAVEFLAG3;
+ out->clockdesc = parse->parse_type->cl_description;
+
+ out->fudgetime1 = parse->basedelay;
+
+ out->fudgetime2 = parse->ppsdelay;
+
+ out->fudgeval1 = (LONG)parse->peer->stratum;
+
+ out->fudgeval2 = 0;
+
+ out->flags = parse->flags & PARSE_STAT_FLAGS;
+
+ out->type = REFCLK_PARSE;
+
+ /*
+ * figure out skew between PPS and RS232 - just for informational
+ * purposes - returned in time2 value
+ */
+ if (PARSE_SYNC(parse->time.parse_state))
+ {
+ if (PARSE_PPS(parse->time.parse_state) && PARSE_TIMECODE(parse->time.parse_state))
+ {
+ l_fp off;
+
+ /*
+ * we have a PPS and RS232 signal - calculate the skew
+ * WARNING: assumes on TIMECODE == PULSE (timecode after pulse)
+ */
+ off = parse->time.parse_stime.fp;
+ L_SUB(&off, &parse->time.parse_ptime.fp); /* true offset */
+ tt = add_var(&out->kv_list, 40, RO);
+ sprintf(tt, "refclock_ppsskew=%s", lfptoms(&off, 6));
+ }
+ }
+
+ if (PARSE_PPS(parse->time.parse_state))
+ {
+ tt = add_var(&out->kv_list, 80, RO|DEF);
+ sprintf(tt, "refclock_ppstime=\"%s\"", prettydate(&parse->time.parse_ptime.fp));
+ }
+
+ /*
+ * all this for just finding out the +-xxxx part (there are always
+ * new and changing fields in the standards 8-().
+ *
+ * but we do it for the human user...
+ */
+ tim = parse->time.parse_time.fp.l_ui - JAN_1970;
+ tm = gmtime(&tim);
+ utcoff = tm->tm_hour * 60 + tm->tm_min;
+ tm = localtime(&tim);
+ utcoff = tm->tm_hour * 60 + tm->tm_min - utcoff + 12 * 60;
+ utcoff += 24 * 60;
+ utcoff %= 24 * 60;
+ utcoff -= 12 * 60;
+ if (utcoff < 0)
+ {
+ utcoff = -utcoff;
+ sign = '-';
+ }
+ else
+ {
+ sign = '+';
+ }
+
+ tt = add_var(&out->kv_list, 128, RO|DEF);
+ sprintf(tt, "refclock_time=\"");
+ tt += strlen(tt);
+
+ if (parse->time.parse_time.fp.l_ui == 0)
+ {
+ strcpy(tt, "<UNDEFINED>\"");
+ }
+ else
+ {
+ strcpy(tt, prettydate(&parse->time.parse_time.fp));
+ t = tt + strlen(tt);
+
+ sprintf(t, " (%c%02d%02d)\"", sign, utcoff / 60, utcoff % 60);
+ }
+
+ if (!PARSE_GETTIMECODE(parse, &tmpctl))
+ {
+ syslog(LOG_ERR, "PARSE receiver #%d: parse_control: parse_timecode() FAILED", unit);
+ }
+ else
+ {
+ tt = add_var(&out->kv_list, 128, RO|DEF);
+ sprintf(tt, "refclock_status=\"");
+ tt += strlen(tt);
+
+ /*
+ * copy PPS flags from last read transaction (informational only)
+ */
+ tmpctl.parsegettc.parse_state |= parse->time.parse_state &
+ (PARSEB_PPS|PARSEB_S_PPS);
+
+ (void) parsestate(tmpctl.parsegettc.parse_state, tt);
+
+ strcat(tt, "\"");
+
+ if (tmpctl.parsegettc.parse_count)
+ mkascii(outstatus+strlen(outstatus), sizeof(outstatus)- strlen(outstatus) - 1,
+ tmpctl.parsegettc.parse_buffer, tmpctl.parsegettc.parse_count - 1);
+
+ parse->badformat += tmpctl.parsegettc.parse_badformat;
+ }
+
+ tmpctl.parseformat.parse_format = tmpctl.parsegettc.parse_format;
+
+ if (!PARSE_GETFMT(parse, &tmpctl))
+ {
+ syslog (LOG_ERR, "PARSE receiver #%d: parse_control: parse_getfmt() FAILED", unit);
+ }
+ else
+ {
+ tt = add_var(&out->kv_list, 80, RO|DEF);
+ sprintf(tt, "refclock_format=\"");
+
+ strncat(tt, tmpctl.parseformat.parse_buffer, tmpctl.parseformat.parse_count);
+ strcat(tt,"\"");
+ }
+
+ /*
+ * gather state statistics
+ */
+
+ tt = add_var(&out->kv_list, 200, RO|DEF);
+ strcpy(tt, "refclock_states=\"");
+ tt += strlen(tt);
+
+ for (i = 0; i <= CEVNT_MAX; i++)
+ {
+ register unsigned LONG stime;
+ register unsigned LONG div = current_time - parse->timestarted;
+ register unsigned LONG percent;
+
+ percent = stime = PARSE_STATETIME(parse, i);
+
+ while (((unsigned LONG)(~0) / 10000) < percent)
+ {
+ percent /= 10;
+ div /= 10;
+ }
+
+ if (div)
+ percent = (percent * 10000) / div;
+ else
+ percent = 10000;
+
+ if (stime)
+ {
+ sprintf(tt, "%s%s%s: %s (%d.%02d%%)",
+ sum ? "; " : "",
+ (parse->status == i) ? "*" : "",
+ clockstatus(i),
+ l_mktime(stime),
+ percent / 100, percent % 100);
+ sum += stime;
+ tt += strlen(tt);
+ }
+ }
+
+ sprintf(tt, "; running time: %s\"", l_mktime(sum));
+
+ tt = add_var(&out->kv_list, 32, RO);
+ sprintf(tt, "refclock_id=\"%s\"", parse->parse_type->cl_id);
+
+ tt = add_var(&out->kv_list, 80, RO);
+ sprintf(tt, "refclock_iomode=\"%s\"", parse->binding->bd_description);
+
+ tt = add_var(&out->kv_list, 128, RO);
+ sprintf(tt, "refclock_driver_version=\"refclock_parse.c,v 3.53 1994/03/25 13:07:39 kardel Exp\"");
+
+ out->lencode = strlen(outstatus);
+ out->lastcode = outstatus;
+ out->timereset = parse->timestarted;
+ out->polls = parse->polls;
+ out->noresponse = parse->noresponse;
+ out->badformat = parse->badformat;
+ out->baddata = parse->baddata;
+ out->lastevent = parse->lastevent;
+ out->currentstatus = parse->status;
+ }
+}
+
+/**===========================================================================
+ ** processing routines
+ **/
+
+/*--------------------------------------------------
+ * event handling - note that nominal events will also be posted
+ */
+static void
+parse_event(parse, event)
+ struct parseunit *parse;
+ int event;
+{
+ if (parse->status != (u_char) event)
+ {
+ parse->statetime[parse->status] += current_time - parse->lastchange;
+ parse->lastchange = current_time;
+
+ parse->status = (u_char)event;
+ if (event != CEVNT_NOMINAL)
+ parse->lastevent = parse->status;
+
+ report_event(EVNT_PEERCLOCK, parse->peer);
+ }
+}
+
+/*--------------------------------------------------
+ * process a PARSE time sample
+ */
+static void
+parse_process(parse, parsetime)
+ struct parseunit *parse;
+ parsetime_t *parsetime;
+{
+ unsigned char leap;
+ struct timeval usecdisp;
+ l_fp off, rectime, reftime, dispersion;
+
+ /*
+ * check for changes in conversion status
+ * (only one for each new status !)
+ */
+ if (parse->laststatus != parsetime->parse_status)
+ {
+ char buffer[200];
+
+ syslog(LOG_WARNING, "PARSE receiver #%d: conversion status \"%s\"",
+ CL_UNIT(parse->unit), parsestatus(parsetime->parse_status, buffer));
+
+ if ((parsetime->parse_status & CVT_MASK) == CVT_FAIL)
+ {
+ /*
+ * tell more about the story - list time code
+ * there is a slight change for a race condition and
+ * the time code might be overwritten by the next packet
+ */
+ parsectl_t tmpctl;
+
+ if (!PARSE_GETTIMECODE(parse, &tmpctl))
+ {
+ syslog(LOG_ERR, "PARSE receiver #%d: parse_process: parse_timecode() FAILED", CL_UNIT(parse->unit));
+ }
+ else
+ {
+ syslog(LOG_WARNING, "PARSE receiver #%d: FAILED TIMECODE: \"%s\"",
+ CL_UNIT(parse->unit), mkascii(buffer, sizeof buffer, tmpctl.parsegettc.parse_buffer, tmpctl.parsegettc.parse_count - 1));
+ parse->badformat += tmpctl.parsegettc.parse_badformat;
+ }
+ }
+
+ parse->laststatus = parsetime->parse_status;
+ }
+
+ /*
+ * examine status and post appropriate events
+ */
+ if ((parsetime->parse_status & CVT_MASK) != CVT_OK)
+ {
+ /*
+ * got bad data - tell the rest of the system
+ */
+ switch (parsetime->parse_status & CVT_MASK)
+ {
+ case CVT_NONE:
+ break; /* well, still waiting - timeout is handled at higher levels */
+
+ case CVT_FAIL:
+ parse->badformat++;
+ if (parsetime->parse_status & CVT_BADFMT)
+ {
+ parse_event(parse, CEVNT_BADREPLY);
+ }
+ else
+ if (parsetime->parse_status & CVT_BADDATE)
+ {
+ parse_event(parse, CEVNT_BADDATE);
+ }
+ else
+ if (parsetime->parse_status & CVT_BADTIME)
+ {
+ parse_event(parse, CEVNT_BADTIME);
+ }
+ else
+ {
+ parse_event(parse, CEVNT_BADREPLY); /* for the lack of something better */
+ }
+ }
+ return; /* skip the rest - useless */
+ }
+
+ /*
+ * check for format changes
+ * (in case somebody has swapped clocks 8-)
+ */
+ if (parse->lastformat != parsetime->parse_format)
+ {
+ parsectl_t tmpctl;
+
+ tmpctl.parseformat.parse_format = parsetime->parse_format;
+
+ if (!PARSE_GETFMT(parse, &tmpctl))
+ {
+ syslog(LOG_ERR, "PARSE receiver #%d: parse_getfmt() FAILED", CL_UNIT(parse->unit));
+ }
+ else
+ {
+ syslog(LOG_INFO, "PARSE receiver #%d: new packet format \"%s\"",
+ CL_UNIT(parse->unit), tmpctl.parseformat.parse_buffer);
+ }
+ parse->lastformat = parsetime->parse_format;
+ }
+
+ /*
+ * now, any changes ?
+ */
+ if (parse->time.parse_state != parsetime->parse_state)
+ {
+ char tmp1[200];
+ char tmp2[200];
+ /*
+ * something happend
+ */
+
+ (void) parsestate(parsetime->parse_state, tmp1);
+ (void) parsestate(parse->time.parse_state, tmp2);
+
+ syslog(LOG_INFO,"PARSE receiver #%d: STATE CHANGE: %s -> %s",
+ CL_UNIT(parse->unit), tmp2, tmp1);
+ }
+
+ /*
+ * remember for future
+ */
+ parse->time = *parsetime;
+
+ /*
+ * check to see, whether the clock did a complete powerup or lost PZF signal
+ * and post correct events for current condition
+ */
+ if (PARSE_POWERUP(parsetime->parse_state))
+ {
+ /*
+ * this is bad, as we have completely lost synchronisation
+ * well this is a problem with the receiver here
+ * for PARSE U/A 31 the lost synchronisation ist true
+ * as it is the powerup state and the time is taken
+ * from a crude real time clock chip
+ * for the PZF series this is only partly true, as
+ * PARSE_POWERUP only means that the pseudo random
+ * phase shift sequence cannot be found. this is only
+ * bad, if we have never seen the clock in the SYNC
+ * state, where the PHASE and EPOCH are correct.
+ * for reporting events the above business does not
+ * really matter, but we can use the time code
+ * even in the POWERUP state after having seen
+ * the clock in the synchronized state (PZF class
+ * receivers) unless we have had a telegram disruption
+ * after having seen the clock in the SYNC state. we
+ * thus require having seen the clock in SYNC state
+ * *after* having missed telegrams (noresponse) from
+ * the clock. one problem remains: we might use erroneously
+ * POWERUP data if the disruption is shorter than 1 polling
+ * interval. fortunately powerdowns last usually longer than 64
+ * seconds and the receiver is at least 2 minutes in the
+ * POWERUP or NOSYNC state before switching to SYNC
+ */
+ parse_event(parse, CEVNT_FAULT);
+ if (parse->nosynctime)
+ {
+ /*
+ * repeated POWERUP/NOSYNC state - look whether
+ * the message should be repeated
+ */
+ if (current_time - parse->nosynctime > PARSENOSYNCREPEAT)
+ {
+ syslog(LOG_ERR,"PARSE receiver #%d: *STILL* NOT SYNCHRONIZED (POWERUP or no PZF signal)",
+ CL_UNIT(parse->unit));
+ parse->nosynctime = current_time;
+ }
+ }
+ else
+ {
+ syslog(LOG_ERR,"PARSE receiver #%d: NOT SYNCHRONIZED",
+ CL_UNIT(parse->unit));
+ parse->nosynctime = current_time;
+ }
+ }
+ else
+ {
+ /*
+ * we have two states left
+ *
+ * SYNC:
+ * this state means that the EPOCH (timecode) and PHASE
+ * information has be read correctly (at least two
+ * successive PARSE timecodes were received correctly)
+ * this is the best possible state - full trust
+ *
+ * NOSYNC:
+ * The clock should be on phase with respect to the second
+ * signal, but the timecode has not been received correctly within
+ * at least the last two minutes. this is a sort of half baked state
+ * for PARSE U/A 31 this is bad news (clock running without timecode
+ * confirmation)
+ * PZF 535 has also no time confirmation, but the phase should be
+ * very precise as the PZF signal can be decoded
+ */
+ parse->nosynctime = 0; /* current state is better than worst state */
+
+ if (PARSE_SYNC(parsetime->parse_state))
+ {
+ /*
+ * currently completely synchronized - best possible state
+ */
+ parse->lastsync = current_time;
+ /*
+ * log OK status
+ */
+ parse_event(parse, CEVNT_NOMINAL);
+ }
+ else
+ {
+ /*
+ * we have had some problems receiving the time code
+ */
+ parse_event(parse, CEVNT_PROP);
+ }
+ }
+
+ if (PARSE_TIMECODE(parsetime->parse_state))
+ {
+ l_fp offset;
+
+ /*
+ * calculate time offset including systematic delays
+ * off = PARSE-timestamp + propagation delay - kernel time stamp
+ */
+ offset = parse->basedelay;
+
+ off = parsetime->parse_time.fp;
+
+ reftime = off;
+
+ L_ADD(&off, &offset);
+ rectime = off; /* this makes org time and xmt time somewhat artificial */
+
+ L_SUB(&off, &parsetime->parse_stime.fp);
+
+ if ((parse->flags & PARSE_STAT_FILTER) &&
+ (off.l_i > -60) &&
+ (off.l_i < 60)) /* take usec error only if within +- 60 secs */
+ {
+ struct timeval usecerror;
+ /*
+ * offset is already calculated
+ */
+ usecerror.tv_sec = parsetime->parse_usecerror / 1000000;
+ usecerror.tv_usec = parsetime->parse_usecerror % 1000000;
+
+ sTVTOTS(&usecerror, &off);
+ L_ADD(&off, &offset);
+ }
+ }
+
+ if (PARSE_PPS(parsetime->parse_state) && CL_PPS(parse->unit))
+ {
+ l_fp offset;
+
+ /*
+ * we have a PPS signal - much better than the RS232 stuff (we hope)
+ */
+ offset = parsetime->parse_ptime.fp;
+
+ L_ADD(&offset, &parse->ppsdelay);
+
+ if (PARSE_TIMECODE(parsetime->parse_state))
+ {
+ if (M_ISGEQ(off.l_i, off.l_f, -1, 0x80000000) &&
+ M_ISGEQ(0, 0x7fffffff, off.l_i, off.l_f))
+ {
+ /*
+ * RS232 offsets within [-0.5..0.5[ - take PPS offsets
+ */
+
+ if (parse->parse_type->cl_flags & PARSE_F_PPSONSECOND)
+ {
+ reftime = off = offset;
+ rectime = offset;
+ /*
+ * implied on second offset
+ */
+ off.l_uf = ~off.l_uf; /* map [0.5..1[ -> [-0.5..0[ */
+ off.l_ui = (off.l_f < 0) ? ~0 : 0; /* sign extend */
+ }
+ else
+ {
+ /*
+ * time code describes pulse
+ */
+ off = parsetime->parse_time.fp;
+
+ rectime = reftime = off; /* take reference time - fake rectime */
+
+ L_SUB(&off, &offset); /* true offset */
+ }
+ }
+ /*
+ * take RS232 offset when PPS when out of bounds
+ */
+ }
+ else
+ {
+ /*
+ * Well, no time code to guide us - assume on second pulse
+ * and pray, that we are within [-0.5..0.5[
+ */
+ reftime = off = offset;
+ rectime = offset;
+ /*
+ * implied on second offset
+ */
+ off.l_uf = ~off.l_uf; /* map [0.5..1[ -> [-0.5..0[ */
+ off.l_ui = (off.l_f < 0) ? ~0 : 0; /* sign extend */
+ }
+ }
+ else
+ {
+ if (!PARSE_TIMECODE(parsetime->parse_state))
+ {
+ /*
+ * Well, no PPS, no TIMECODE, no more work ...
+ */
+ return;
+ }
+ }
+
+
+#if defined(PPS) || defined(PPSCLK) || defined(PPSPPS) || defined(PARSEPPS)
+ if (CL_PPS(parse->unit) && !parse->pollonly && PARSE_SYNC(parsetime->parse_state))
+ {
+ /*
+ * only provide PPS information when clock
+ * is in sync
+ * thus PHASE and EPOCH are correct and PPS is not
+ * done via the CIOGETEV loopfilter mechanism
+ */
+#ifdef PPSPPS
+ if (fdpps != parse->fd)
+#endif
+ (void) pps_sample(&off);
+ }
+#endif /* PPS || PPSCLK || PPSPPS || PARSEPPS */
+
+ /*
+ * ready, unless the machine wants a sample
+ */
+ if (!parse->pollonly && !parse->pollneeddata)
+ return;
+
+ parse->pollneeddata = 0;
+
+ if (PARSE_PPS(parsetime->parse_state))
+ {
+ L_CLR(&dispersion);
+ }
+ else
+ {
+ /*
+ * convert usec dispersion into NTP TS world
+ */
+
+ usecdisp.tv_sec = parsetime->parse_usecdisp / 1000000;
+ usecdisp.tv_usec = parsetime->parse_usecdisp % 1000000;
+
+ TVTOTS(&usecdisp, &dispersion);
+ }
+
+ /*
+ * and now stick it into the clock machine
+ * samples are only valid iff lastsync is not too old and
+ * we have seen the clock in sync at least once
+ * after the last time we didn't see an expected data telegram
+ * see the clock states section above for more reasoning
+ */
+ if (((current_time - parse->lastsync) > parse->parse_type->cl_maxunsync) ||
+ (parse->lastsync <= parse->lastmissed))
+ {
+ leap = LEAP_NOTINSYNC;
+ }
+ else
+ {
+ if (PARSE_LEAPADD(parsetime->parse_state))
+ {
+ /*
+ * we pick this state also for time code that pass leap warnings
+ * without direction information (as earth is currently slowing
+ * down).
+ */
+ leap = (parse->flags & PARSE_LEAP_DELETE) ? LEAP_DELSECOND : LEAP_ADDSECOND;
+ }
+ else
+ if (PARSE_LEAPDEL(parsetime->parse_state))
+ {
+ leap = LEAP_DELSECOND;
+ }
+ else
+ {
+ leap = LEAP_NOWARNING;
+ }
+ }
+
+ refclock_receive(parse->peer, &off, 0, LFPTOFP(&dispersion), &reftime, &rectime, leap);
+}
+
+/**===========================================================================
+ ** clock polling support
+ **/
+
+struct poll_timer
+{
+ struct event timer; /* we'd like to poll a a higher rate than 1/64s */
+};
+
+typedef struct poll_timer poll_timer_t;
+
+/*--------------------------------------------------
+ * direct poll routine
+ */
+static void
+poll_dpoll(parse)
+ struct parseunit *parse;
+{
+ register int rtc;
+ register char *ps = ((poll_info_t *)parse->parse_type->cl_data)->string;
+ register int ct = ((poll_info_t *)parse->parse_type->cl_data)->count;
+
+ rtc = write(parse->fd, ps, ct);
+ if (rtc < 0)
+ {
+ syslog(LOG_ERR, "PARSE receiver #%d: poll_dpoll: failed to send cmd to clock: %m", CL_UNIT(parse->unit));
+ }
+ else
+ if (rtc != ct)
+ {
+ syslog(LOG_ERR, "PARSE receiver #%d: poll_dpoll: failed to send cmd incomplete (%d of %d bytes sent)", CL_UNIT(parse->unit), rtc, ct);
+ }
+}
+
+/*--------------------------------------------------
+ * periodic poll routine
+ */
+static void
+poll_poll(parse)
+ struct parseunit *parse;
+{
+ register poll_timer_t *pt = (poll_timer_t *)parse->localdata;
+
+ poll_dpoll(parse);
+
+ if (pt != (poll_timer_t *)0)
+ {
+ pt->timer.event_time = current_time + ((poll_info_t *)parse->parse_type->cl_data)->rate;
+ TIMER_ENQUEUE(timerqueue, &pt->timer);
+ }
+}
+
+/*--------------------------------------------------
+ * init routine - setup timer
+ */
+static int
+poll_init(parse)
+ struct parseunit *parse;
+{
+ register poll_timer_t *pt;
+
+ if (((poll_info_t *)parse->parse_type->cl_data)->rate)
+ {
+ parse->localdata = (void *)malloc(sizeof(poll_timer_t));
+ memset((char *)parse->localdata, 0, sizeof(poll_timer_t));
+
+ pt = (poll_timer_t *)parse->localdata;
+
+ pt->timer.peer = (struct peer *)parse; /* well, only we know what it is */
+ pt->timer.event_handler = poll_poll;
+ poll_poll(parse);
+ }
+ else
+ {
+ parse->localdata = (void *)0;
+ }
+
+ return 0;
+}
+
+/*--------------------------------------------------
+ * end routine - clean up timer
+ */
+static void
+poll_end(parse)
+ struct parseunit *parse;
+{
+ if (parse->localdata != (void *)0)
+ {
+ TIMER_DEQUEUE(&((poll_timer_t *)parse->localdata)->timer);
+ free((char *)parse->localdata);
+ parse->localdata = (void *)0;
+ }
+}
+
+/**===========================================================================
+ ** special code for special clocks
+ **/
+
+
+/*--------------------------------------------------
+ * trimble TAIP init routine - setup EOL and then do poll_init.
+ */
+static int
+trimbletaip_init(parse)
+ struct parseunit *parse;
+{
+#ifdef HAVE_TERMIOS
+ struct termios tm;
+#endif
+#ifdef HAVE_SYSV_TTYS
+ struct termio tm;
+#endif
+ /*
+ * configure terminal line for trimble receiver
+ */
+ if (TTY_GETATTR(parse->fd, &tm) == -1)
+ {
+ syslog(LOG_ERR, "PARSE receiver #%d: trimbletaip_init: tcgetattr(fd, &tm): %m", CL_UNIT(parse->unit));
+ return 0;
+ }
+ else
+ {
+ tm.c_cc[VEOL] = TRIMBLETAIP_EOL;
+
+ if (TTY_SETATTR(parse->fd, &tm) == -1)
+ {
+ syslog(LOG_ERR, "PARSE receiver #%d: trimbletaip_init: tcsetattr(fd, &tm): %m", CL_UNIT(parse->unit));
+ return 0;
+ }
+ }
+ return poll_init(parse);
+}
+
+/*
+ * This driver supports the Trimble SVee Six Plus GPS receiver module.
+ * It should support other Trimble receivers which use the Trimble Standard
+ * Interface Protocol (see below).
+ *
+ * The module has a serial I/O port for command/data and a 1 pulse-per-second
+ * output, about 1 microsecond wide. The leading edge of the pulse is
+ * coincident with the change of the GPS second. This is the same as
+ * the change of the UTC second +/- ~1 microsecond. Some other clocks
+ * specifically use a feature in the data message as a timing reference, but
+ * the SVee Six Plus does not do this. In fact there is considerable jitter
+ * on the timing of the messages, so this driver only supports the use
+ * of the PPS pulse for accurate timing. Where it is determined that
+ * the offset is way off, when first starting up xntpd for example,
+ * the timing of the data stream is used until the offset becomes low enough
+ * (|offset| < CLOCK_MAX), at which point the pps offset is used.
+ *
+ * It can use either option for receiving PPS information - the 'ppsclock'
+ * stream pushed onto the serial data interface to timestamp the Carrier
+ * Detect interrupts, where the 1PPS connects to the CD line. This only
+ * works on SunOS 4.1.x currently. To select this, define PPSPPS in
+ * Config.local. The other option is to use a pulse-stretcher/level-converter
+ * to convert the PPS pulse into a RS232 start pulse & feed this into another
+ * tty port. To use this option, define PPSCLK in Config.local. The pps input,
+ * by whichever method, is handled in ntp_loopfilter.c
+ *
+ * The receiver uses a serial message protocol called Trimble Standard
+ * Interface Protocol (it can support others but this driver only supports
+ * TSIP). Messages in this protocol have the following form:
+ *
+ * <DLE><id> ... <data> ... <DLE><ETX>
+ *
+ * Any bytes within the <data> portion of value 10 hex (<DLE>) are doubled
+ * on transmission and compressed back to one on reception. Otherwise
+ * the values of data bytes can be anything. The serial interface is RS-422
+ * asynchronous using 9600 baud, 8 data bits with odd party (**note** 9 bits
+ * in total!), and 1 stop bit. The protocol supports byte, integer, single,
+ * and double datatypes. Integers are two bytes, sent most significant first.
+ * Singles are IEEE754 single precision floating point numbers (4 byte) sent
+ * sign & exponent first. Doubles are IEEE754 double precision floating point
+ * numbers (8 byte) sent sign & exponent first.
+ * The receiver supports a large set of messages, only a small subset of
+ * which are used here. From driver to receiver the following are used:
+ *
+ * ID Description
+ *
+ * 21 Request current time
+ * 22 Mode Select
+ * 2C Set/Request operating parameters
+ * 2F Request UTC info
+ * 35 Set/Request I/O options
+
+ * From receiver to driver the following are recognised:
+ *
+ * ID Description
+ *
+ * 41 GPS Time
+ * 44 Satellite selection, PDOP, mode
+ * 46 Receiver health
+ * 4B Machine code/status
+ * 4C Report operating parameters (debug only)
+ * 4F UTC correction data (used to get leap second warnings)
+ * 55 I/O options (debug only)
+ *
+ * All others are accepted but ignored.
+ *
+ */
+
+#define PI 3.1415926535898 /* lots of sig figs */
+#define D2R PI/180.0
+
+/*-------------------------------------------------------------------
+ * sendcmd, sendbyte, sendetx, sendflt, sendint implement the command
+ * interface to the receiver.
+ *
+ * CAVEAT: the sendflt, sendint routines are byte order dependend and
+ * float implementation dependend - these must be converted to portable
+ * versions !
+ */
+
+union {
+ u_char bd[8];
+ int iv;
+ float fv;
+ double dv;
+} uval;
+
+struct txbuf
+{
+ short idx; /* index to first unused byte */
+ u_char *txt; /* pointer to actual data buffer */
+};
+
+void
+sendcmd(buf, c)
+ struct txbuf *buf;
+ u_char c;
+{
+ buf->txt[0] = DLE;
+ buf->txt[1] = c;
+ buf->idx = 2;
+}
+
+void sendbyte(buf, b)
+ struct txbuf *buf;
+ u_char b;
+{
+ if (b == DLE)
+ buf->txt[buf->idx++] = DLE;
+ buf->txt[buf->idx++] = b;
+}
+
+void
+sendetx(buf, parse)
+ struct txbuf *buf;
+ struct parseunit *parse;
+{
+ buf->txt[buf->idx++] = DLE;
+ buf->txt[buf->idx++] = ETX;
+
+ if (write(parse->fd, buf->txt, buf->idx) != buf->idx)
+ {
+ syslog(LOG_ERR, "PARSE receiver #%d: sendetx: failed to send cmd to clock: %m", CL_UNIT(parse->unit));
+ }
+}
+
+void
+sendint(buf, a)
+ struct txbuf *buf;
+ int a;
+{
+ uval.iv = a;
+ sendbyte(buf, uval.bd[2]);
+ sendbyte(buf, uval.bd[3]);
+}
+
+void
+sendflt(buf, a)
+ struct txbuf *buf;
+ float a;
+{
+ int i;
+
+ uval.fv = a;
+ for (i=0; i<=3; i++)
+ sendbyte(buf, uval.bd[i]);
+}
+
+/*--------------------------------------------------
+ * trimble TSIP init routine
+ */
+static int
+trimbletsip_init(parse)
+ struct parseunit *parse;
+{
+ u_char buffer[256];
+ struct txbuf buf;
+
+ buf.txt = buffer;
+
+ if (!poll_init(parse))
+ {
+ sendcmd(&buf, 0x1f); /* request software versions */
+ sendetx(&buf, parse);
+
+ sendcmd(&buf, 0x2c); /* set operating parameters */
+ sendbyte(&buf, 4); /* static */
+ sendflt(&buf, 5.0*D2R); /* elevation angle mask = 10 deg XXX */
+ sendflt(&buf, 4.0); /* s/n ratio mask = 6 XXX */
+ sendflt(&buf, 12.0); /* PDOP mask = 12 */
+ sendflt(&buf, 8.0); /* PDOP switch level = 8 */
+ sendetx(&buf, parse);
+
+ sendcmd(&buf, 0x22); /* fix mode select */
+ sendbyte(&buf, 0); /* automatic */
+ sendetx(&buf, parse);
+
+ sendcmd(&buf, 0x28); /* request system message */
+ sendetx(&buf, parse);
+
+ sendcmd(&buf, 0x8e); /* superpacket fix */
+ sendbyte(&buf, 0x2); /* binary mode */
+ sendetx(&buf, parse);
+
+ sendcmd(&buf, 0x35); /* set I/O options */
+ sendbyte(&buf, 0); /* no position output */
+ sendbyte(&buf, 0); /* no velocity output */
+ sendbyte(&buf, 7); /* UTC, compute on seconds, send only on request */
+ sendbyte(&buf, 0); /* no raw measurements */
+ sendetx(&buf, parse);
+
+ sendcmd(&buf, 0x2f); /* request UTC correction data */
+ sendetx(&buf, parse);
+ return 0;
+ }
+ else
+ return 1;
+}
+
+#endif /* defined(REFCLOCK) && defined(PARSE) */
+
+/*
+ * History:
+ *
+ * refclock_parse.c,v
+ * Revision 3.53 1994/03/25 13:07:39 kardel
+ * fixed offset calculation for large (>4 Min) offsets
+ *
+ * Revision 3.52 1994/03/03 09:58:00 kardel
+ * stick -kv in cvs is no fun
+ *
+ * Revision 3.49 1994/02/20 13:26:00 kardel
+ * rcs id cleanup
+ *
+ * Revision 3.48 1994/02/20 13:04:56 kardel
+ * parse add/delete second support
+ *
+ * Revision 3.47 1994/02/02 17:44:30 kardel
+ * rcs ids fixed
+ *
+ * Revision 3.45 1994/01/25 19:06:27 kardel
+ * 94/01/23 reconcilation
+ *
+ * Revision 3.44 1994/01/25 17:32:23 kardel
+ * settable extended variables
+ *
+ * Revision 3.43 1994/01/23 16:28:39 kardel
+ * HAVE_TERMIOS introduced
+ *
+ * Revision 3.42 1994/01/22 11:35:04 kardel
+ * added HAVE_TERMIOS
+ *
+ * Revision 3.41 1993/11/27 18:44:37 kardel
+ * can't trust GPS166 on unsync
+ *
+ * Revision 3.40 1993/11/21 18:03:36 kardel
+ * useless declaration deleted
+ *
+ * Revision 3.39 1993/11/21 15:30:15 kardel
+ * static funcitions may be declared only at outer level
+ *
+ * Revision 3.38 1993/11/15 21:26:49 kardel
+ * conditional define comments fixed
+ *
+ * Revision 3.37 1993/11/11 11:20:49 kardel
+ * declaration fixes
+ *
+ * Revision 3.36 1993/11/10 12:17:14 kardel
+ * #ifdef glitch
+ *
+ * Revision 3.35 1993/11/01 21:15:06 kardel
+ * comments updated
+ *
+ * Revision 3.34 1993/11/01 20:01:08 kardel
+ * parse Solaris support (initial version)
+ *
+ * Revision 3.33 1993/10/30 09:44:58 kardel
+ * conditional compilation flag cleanup
+ *
+ * Revision 3.32 1993/10/22 14:28:43 kardel
+ * Oct. 22nd 1993 reconcilation
+ *
+ * Revision 3.31 1993/10/10 21:19:10 kardel
+ * compilation cleanup - (minimal porting tests)
+ *
+ * Revision 3.30 1993/10/09 21:44:35 kardel
+ * syslog strings fixed
+ *
+ * Revision 3.29 1993/10/09 14:40:15 kardel
+ * default precision setting fixed
+ *
+ * Revision 3.28 1993/10/08 14:48:22 kardel
+ * Changed offset determination logic:
+ * Take the PPS offset if it is available and the time
+ * code offset is within [-0.5..0.5[, otherwise stick
+ * to the time code offset
+ *
+ * Revision 3.27 1993/10/08 00:53:17 kardel
+ * announce also simulated PPS via CIOGETEV in ntpq cl
+ *
+ * Revision 3.26 1993/10/07 23:29:35 kardel
+ * trimble fixes
+ *
+ * Revision 3.25 1993/10/06 21:13:35 kardel
+ * test reversed (CIOGETEV support)
+ *
+ * Revision 3.24 1993/10/03 20:18:26 kardel
+ * Well, values > 999999 in the usec field from uniqtime() timestamps
+ * can prove harmful.
+ *
+ * Revision 3.23 1993/10/03 19:49:54 kardel
+ * buftvtots where failing on uninitialized time stamps
+ *
+ * Revision 3.22 1993/10/03 19:11:09 kardel
+ * restructured I/O handling
+ *
+ * Revision 3.21 1993/09/29 11:30:18 kardel
+ * special init for trimble to set EOL
+ *
+ * Revision 3.20 1993/09/27 22:46:28 kardel
+ * preserve module stack if I_PUSH parse fails
+ *
+ * Revision 3.19 1993/09/27 21:10:11 kardel
+ * wrong structure member
+ *
+ * Revision 3.18 1993/09/27 13:05:06 kardel
+ * Trimble is true polling only
+ *
+ * Revision 3.17 1993/09/27 12:47:10 kardel
+ * poll string support generalized
+ *
+ * Revision 3.16 1993/09/26 23:40:56 kardel
+ * new parse driver logic
+ *
+ * Revision 3.15 1993/09/24 15:00:51 kardel
+ * Sep 23rd distribution...
+ *
+ * Revision 3.14 1993/09/22 18:21:15 kardel
+ * support ppsclock streams module (-DSTREAM -DPPSPPS -DPARSEPPS -UPARSESTREAM)
+ *
+ * Revision 3.13 1993/09/05 15:38:33 kardel
+ * not every cpp understands #error...
+ *
+ * Revision 3.12 1993/09/02 20:04:19 kardel
+ * TTY cleanup
+ *
+ * Revision 3.11 1993/09/01 21:48:47 kardel
+ * conditional cleanup
+ *
+ * Revision 3.10 1993/09/01 11:32:45 kardel
+ * assuming HAVE_POSIX_TTYS when STREAM defined
+ *
+ * Revision 3.9 1993/08/31 22:31:46 kardel
+ * SINIX-M SysVR4 integration
+ *
+ * Revision 3.8 1993/08/27 00:29:50 kardel
+ * compilation cleanup
+ *
+ * Revision 3.7 1993/08/24 22:27:30 kardel
+ * cleaned up AUTOCONF DCF77 mess 8-) - wasn't too bad
+ *
+ * Revision 3.6 1993/08/24 21:36:23 kardel
+ * casting and ifdefs
+ *
+ * Revision 3.5 1993/07/09 23:36:59 kardel
+ * HAVE_POSIX_TTYS used to produce errors 8-( - BSD driver support still lacking
+ *
+ * Revision 3.4 1993/07/09 12:42:29 kardel
+ * RAW DCF now officially released
+ *
+ * Revision 3.3 1993/07/09 11:50:37 kardel
+ * running GPS also on 960 to be able to switch GPS/DCF77
+ *
+ * Revision 3.2 1993/07/09 11:37:34 kardel
+ * Initial restructured version + GPS support
+ *
+ * Revision 3.1 1993/07/06 10:01:07 kardel
+ * DCF77 driver goes generic...
+ *
+ */
diff --git a/usr.sbin/xntpd/xntpd/refclock_pst.c b/usr.sbin/xntpd/xntpd/refclock_pst.c
new file mode 100644
index 0000000..edc77f9
--- /dev/null
+++ b/usr.sbin/xntpd/xntpd/refclock_pst.c
@@ -0,0 +1,329 @@
+/*
+ * refclock_pst - clock driver for PSTI/Traconex WWV/WWVH receivers
+ */
+#if defined(REFCLOCK) && defined(PST)
+
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/time.h>
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_stdlib.h"
+
+/*
+ * This driver supports the PSTI 1010 and Traconex 1020 WWV/WWVH
+ * Receivers. No specific claim of accuracy is made for these receiver,
+ * but actual experience suggests that 10 ms would be a conservative
+ * assumption.
+ *
+ * The DIPswitches should be set for 9600 bps line speed, 24-hour day-
+ * of-year format and UTC time zone. Automatic correction for DST should
+ * be disabled. It is very important that the year be set correctly in
+ * the DIPswitches; otherwise, the day of year will be incorrect after
+ * 28 April of a normal or leap year. The propagation delay DIPswitches
+ * should be set according to the distance from the transmitter for both
+ * WWV and WWVH, as described in the instructions. While the delay can
+ * be set only to within 11 ms, the fudge time1 parameter can be used
+ * for vernier corrections.
+ *
+ * Using the poll sequence QTQDQM, the response timecode is in three
+ * sections totalling 50 ASCII printing characters, as concatenated by
+ * the driver, in the following format:
+ *
+ * ahh:mm:ss.fffs<cr> yy/dd/mm/ddd<cr> frdzycchhSSFTttttuuxx<cr>
+ *
+ * on-time = first <cr> * hh:mm:ss.fff = hours, minutes, seconds, milliseconds
+ * a = AM/PM indicator (' ' for 24-hour mode)
+ * yy = year (from internal switches)
+ * dd/mm/ddd = day of month, month, day of year
+ * s = daylight-saving indicator (' ' for 24-hour mode)
+ * f = frequency enable (O = all frequencies enabled)
+ * r = baud rate (3 = 1200, 6 = 9600)
+ * d = features indicator (@ = month/day display enabled)
+ * z = time zone (0 = UTC)
+ * y = year (5 = 91)
+ * cc = WWV propagation delay (52 = 22 ms)
+ * hh = WWVH propagation delay (81 = 33 ms)
+ * SS = status (80 or 82 = operating correctly)
+ * F = current receive frequency (4 = 15 MHz)
+ * T = transmitter (C = WWV, H = WWVH)
+ * tttt = time since last update (0000 = minutes)
+ * uu = flush character (03 = ^c)
+ * xx = 94 (unknown)
+ *
+ * The alarm condition is indicated by other than '8' at A, which occurs
+ * during initial synchronization and when received signal is lost for
+ * an extended period; unlock condition is indicated by other than
+ * "0000" in the tttt subfield at Q.
+ *
+ * Fudge Factors
+ *
+ * There are no special fudge factors other than the generic.
+ */
+
+/*
+ * Interface definitions
+ */
+#define DEVICE "/dev/pst%d" /* device name and unit */
+#define SPEED232 B9600 /* uart speed (9600 baud) */
+#define PRECISION (-10) /* precision assumed (about 1 ms) */
+#define WWVREFID "WWV\0" /* WWV reference ID */
+#define WWVHREFID "WWVH" /* WWVH reference ID */
+#define DESCRIPTION "PSTI/Traconex WWV/WWVH Receiver" /* WRU */
+
+#define NSAMPLES 3 /* stages of median filter */
+#define LENPST 46 /* min timecode length */
+
+/*
+ * Imported from ntp_timer module
+ */
+extern u_long current_time; /* current time (s) */
+
+/*
+ * Imported from ntpd module
+ */
+extern int debug; /* global debug flag */
+
+/*
+ * Unit control structure
+ */
+struct pstunit {
+ int pollcnt; /* poll message counter */
+
+ u_char tcswitch; /* timecode switch */
+ char *lastptr; /* pointer to timecode data */
+};
+
+/*
+ * Function prototypes
+ */
+static int pst_start P((int, struct peer *));
+static void pst_shutdown P((int, struct peer *));
+static void pst_receive P((struct recvbuf *));
+static void pst_poll P((int, struct peer *));
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_pst = {
+ pst_start, /* start up driver */
+ pst_shutdown, /* shut down driver */
+ pst_poll, /* transmit poll message */
+ noentry, /* not used (old pst_control) */
+ noentry, /* initialize driver */
+ noentry, /* not used (old pst_buginfo) */
+ NOFLAGS /* not used */
+};
+
+
+/*
+ * pst_start - open the devices and initialize data for processing
+ */
+static int
+pst_start(unit, peer)
+ int unit;
+ struct peer *peer;
+{
+ register struct pstunit *up;
+ struct refclockproc *pp;
+ int fd;
+ char device[20];
+
+ /*
+ * Open serial port. Use CLK line discipline, if available.
+ */
+ (void)sprintf(device, DEVICE, unit);
+ if (!(fd = refclock_open(device, SPEED232, LDISC_CLK)))
+ return (0);
+
+ /*
+ * Allocate and initialize unit structure
+ */
+ if (!(up = (struct pstunit *)
+ emalloc(sizeof(struct pstunit)))) {
+ (void) close(fd);
+ return (0);
+ }
+ memset((char *)up, 0, sizeof(struct pstunit));
+ pp = peer->procptr;
+ pp->io.clock_recv = pst_receive;
+ pp->io.srcclock = (caddr_t)peer;
+ pp->io.datalen = 0;
+ pp->io.fd = fd;
+ if (!io_addclock(&pp->io)) {
+ (void) close(fd);
+ free(up);
+ return (0);
+ }
+ pp->unitptr = (caddr_t)up;
+
+ /*
+ * Initialize miscellaneous variables
+ */
+ peer->precision = PRECISION;
+ pp->clockdesc = DESCRIPTION;
+ memcpy((char *)&pp->refid, WWVREFID, 4);
+ up->pollcnt = 2;
+ return (1);
+}
+
+
+/*
+ * pst_shutdown - shut down the clock
+ */
+static void
+pst_shutdown(unit, peer)
+ int unit;
+ struct peer *peer;
+{
+ register struct pstunit *up;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ up = (struct pstunit *)pp->unitptr;
+ io_closeclock(&pp->io);
+ free(up);
+}
+
+
+/*
+ * pst_receive - receive data from the serial interface
+ */
+static void
+pst_receive(rbufp)
+ struct recvbuf *rbufp;
+{
+ register struct pstunit *up;
+ struct refclockproc *pp;
+ struct peer *peer;
+ l_fp trtmp;
+ u_long ltemp;
+ char ampmchar; /* AM/PM indicator */
+ char daychar; /* standard/daylight indicator */
+ char junque[10]; /* "yy/dd/mm/" discard */
+ char info[14]; /* "frdzycchhSSFT" clock info */
+
+ /*
+ * Initialize pointers and read the timecode and timestamp
+ */
+ peer = (struct peer *)rbufp->recv_srcclock;
+ pp = peer->procptr;
+ up = (struct pstunit *)pp->unitptr;
+ up->lastptr += refclock_gtlin(rbufp, up->lastptr, pp->lastcode
+ + BMAX - 2 - up->lastptr, &trtmp);
+ *up->lastptr++ = ' ';
+ *up->lastptr = '\0';
+
+ /*
+ * Note we get a buffer and timestamp for each <cr>, but only
+ * the first timestamp is retained.
+ */
+ if (!up->tcswitch)
+ pp->lastrec = trtmp;
+ up->tcswitch++;
+ pp->lencode = up->lastptr - pp->lastcode;
+ if (up->tcswitch < 3)
+ return;
+ up->pollcnt = 2;
+ record_clock_stats(&peer->srcadr, pp->lastcode);
+#ifdef DEBUG
+ if (debug)
+ printf("pst: timecode %d %s\n", pp->lencode,
+ pp->lastcode);
+#endif
+
+ /*
+ * We get down to business, check the timecode format and decode
+ * its contents. If the timecode has invalid length or is not in
+ * proper format, we declare bad format and exit.
+ */
+ if (pp->lencode < LENPST) {
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+
+ /*
+ * Timecode format:
+ * "ahh:mm:ss.fffs yy/dd/mm/ddd frdzycchhSSFTttttuuxx"
+ */
+ if (sscanf(pp->lastcode, "%c%2d:%2d:%2d.%3d%c %9s%3d%13s%4ld",
+ &ampmchar, &pp->hour, &pp->minute, &pp->second,
+ &pp->msec, &daychar, junque, &pp->day,
+ info, &ltemp) != 10) {
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+
+ /*
+ * Decode synchronization, quality and last update. If
+ * unsynchronized, set the leap bits accordingly and exit. Once
+ * synchronized, the dispersion depends only on when the clock
+ * was last heard, which depends on the time since last update,
+ * as reported by the clock.
+ */
+ if (info[9] != '8') {
+ pp->leap = LEAP_NOTINSYNC;
+ } else {
+ pp->leap = 0;
+ pp->lasttime = current_time - ltemp;
+ if (info[12] == 'H')
+ memcpy((char *)&pp->refid, WWVHREFID, 4);
+ else
+ memcpy((char *)&pp->refid, WWVREFID, 4);
+ if (peer->stratum <= 1)
+ peer->refid = pp->refid;
+ }
+
+ /*
+ * Process the new sample in the median filter and determine the
+ * reference clock offset and dispersion. We use lastrec as both
+ * the reference time and receive time in order to avoid being
+ * cute, like setting the reference time later than the receive
+ * time, which may cause a paranoid protocol module to chuck out
+ * the data.
+ */
+ if (!refclock_process(pp, NSAMPLES, NSAMPLES)) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ trtmp = pp->lastrec;
+ trtmp.l_ui -= ltemp;
+ refclock_receive(peer, &pp->offset, 0, pp->dispersion, &trtmp,
+ &pp->lastrec, pp->leap);
+}
+
+
+/*
+ * pst_poll - called by the transmit procedure
+ */
+static void
+pst_poll(unit, peer)
+ int unit;
+ struct peer *peer;
+{
+ register struct pstunit *up;
+ struct refclockproc *pp;
+
+ /*
+ * Time to poll the clock. The PSTI/Traconex clock responds to a
+ * "QTQDQMT" by returning a timecode in the format specified
+ * above. If nothing is heard from the clock for two polls,
+ * declare a timeout and keep going.
+ */
+ pp = peer->procptr;
+ up = (struct pstunit *)pp->unitptr;
+ if (up->pollcnt == 0)
+ refclock_report(peer, CEVNT_TIMEOUT);
+ else
+ up->pollcnt--;
+ up->tcswitch = 0;
+ up->lastptr = pp->lastcode;
+ if (write(pp->io.fd, "QTQDQMT", 6) != 6) {
+ refclock_report(peer, CEVNT_FAULT);
+ } else
+ pp->polls++;
+}
+
+#endif
diff --git a/usr.sbin/xntpd/xntpd/refclock_tpro.c b/usr.sbin/xntpd/xntpd/refclock_tpro.c
new file mode 100644
index 0000000..d530fbf
--- /dev/null
+++ b/usr.sbin/xntpd/xntpd/refclock_tpro.c
@@ -0,0 +1,227 @@
+/*
+ * refclock_tpro - clock driver for the KSI/Odetics TPRO-S IRIG-B reader
+ */
+#if defined(REFCLOCK) && defined(TPRO) && defined(sun)
+
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/time.h>
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_unixtime.h"
+#include "sys/tpro.h"
+#include "ntp_stdlib.h"
+
+/*
+ * This driver supports the KSI/Odetecs TPRO-S IRIG-B reader and TPRO-
+ * SAT GPS receiver for the Sun Microsystems SBus. It requires that the
+ * tpro.o device driver be installed and loaded.
+ */
+
+/*
+ * TPRO interface definitions
+ */
+#define DEVICE "/dev/tpro%d" /* device name and unit */
+#define PRECISION (-20) /* precision assumed (1 us) */
+#define REFID "IRIG" /* reference ID */
+#define DESCRIPTION "KSI/Odetics TPRO/S IRIG Interface" /* WRU */
+
+#define NSAMPLES 3 /* stages of median filter */
+
+/*
+ * Imported from ntp_timer module
+ */
+extern u_long current_time; /* current time (s) */
+
+/*
+ * Imported from ntpd module
+ */
+extern int debug; /* global debug flag */
+
+/*
+ * Unit control structure
+ */
+struct tprounit {
+ struct tproval tprodata; /* data returned from tpro read */
+};
+
+/*
+ * Function prototypes
+ */
+static int tpro_start P((int, struct peer *));
+static void tpro_shutdown P((int, struct peer *));
+static void tpro_poll P((int unit, struct peer *));
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_tpro = {
+ tpro_start, /* start up driver */
+ tpro_shutdown, /* shut down driver */
+ tpro_poll, /* transmit poll message */
+ noentry, /* not used (old tpro_control) */
+ noentry, /* initialize driver (not used) */
+ noentry, /* not used (old tpro_buginfo) */
+ NOFLAGS /* not used */
+};
+
+
+/*
+ * tpro_start - open the TPRO device and initialize data for processing
+ */
+static int
+tpro_start(unit, peer)
+ int unit;
+ struct peer *peer;
+{
+ register struct tprounit *up;
+ struct refclockproc *pp;
+ char device[20];
+ int fd;
+
+ /*
+ * Open TPRO device
+ */
+ (void)sprintf(device, DEVICE, unit);
+ fd = open(device, O_RDONLY | O_NDELAY, 0777);
+ if (fd == -1) {
+ syslog(LOG_ERR, "tpro_start: open of %s: %m", device);
+ return (0);
+ }
+
+ /*
+ * Allocate and initialize unit structure
+ */
+ if (!(up = (struct tprounit *)
+ emalloc(sizeof(struct tprounit)))) {
+ (void) close(fd);
+ return (0);
+ }
+ memset((char *)up, 0, sizeof(struct tprounit));
+ pp = peer->procptr;
+ pp->io.clock_recv = noentry;
+ pp->io.srcclock = (caddr_t)peer;
+ pp->io.datalen = 0;
+ pp->io.fd = fd;
+ if (!io_addclock(&pp->io)) {
+ (void) close(fd);
+ free(up);
+ return (0);
+ }
+ pp->unitptr = (caddr_t)up;
+
+ /*
+ * Initialize miscellaneous peer variables
+ */
+ peer->precision = PRECISION;
+ pp->clockdesc = DESCRIPTION;
+ memcpy((char *)&pp->refid, REFID, 4);
+ return (1);
+}
+
+
+/*
+ * tpro_shutdown - shut down the clock
+ */
+static void
+tpro_shutdown(unit, peer)
+ int unit;
+ struct peer *peer;
+{
+ register struct tprounit *up;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ up = (struct tprounit *)pp->unitptr;
+ io_closeclock(&pp->io);
+ free(up);
+}
+
+
+/*
+ * tpro_poll - called by the transmit procedure
+ */
+static void
+tpro_poll(unit, peer)
+ int unit;
+ struct peer *peer;
+{
+ register struct tprounit *up;
+ struct refclockproc *pp;
+ struct tproval *tp;
+
+ /*
+ * This is the main routine. It snatches the time from the TPRO
+ * board and tacks on a local timestamp.
+ */
+ pp = peer->procptr;
+ up = (struct tprounit *)pp->unitptr;
+
+ tp = &up->tprodata;
+ if (read(pp->io.fd, (char *)tp, sizeof(struct tproval)) < 0) {
+ refclock_report(peer, CEVNT_FAULT);
+ return;
+ }
+ gettstamp(&pp->lastrec);
+ pp->lasttime = current_time;
+ pp->polls++;
+
+ /*
+ * We get down to business, check the timecode format and decode
+ * its contents. If the timecode has invalid length or is not in
+ * proper format, we declare bad format and exit. Note: we
+ * can't use the sec/usec conversion produced by the driver,
+ * since the year may be suspect. All format error checking is
+ * done by the sprintf() and sscanf() routines.
+ */
+ if (sprintf(pp->lastcode,
+ "%1x%1x%1x %1x%1x:%1x%1x:%1x%1x.%1x%1x%1x%1x%1x%1x %1x",
+ tp->day100, tp->day10, tp->day1, tp->hour10, tp->hour1,
+ tp->min10, tp->min1, tp->sec10, tp->sec1, tp->ms100,
+ tp->ms10, tp->ms1, tp->usec100, tp->usec10, tp->usec1,
+ tp->status)) {
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+
+#ifdef DEBUG
+ if (debug)
+ printf("tpro: time %s timecode %d %s\n",
+ ulfptoa(&pp->lastrec, 6), pp->lencode,
+ pp->lastcode);
+#endif
+ record_clock_stats(&peer->srcadr, pp->lastcode);
+ if (sscanf(pp->lastcode, "%3d %2d:%2d:%2d.%6ld", &pp->day,
+ &pp->hour, &pp->minute, &pp->second, &pp->usec)
+ != 5) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ if (tp->status != 0xff) {
+ pp->leap = LEAP_NOTINSYNC;
+ refclock_report(peer, CEVNT_FAULT);
+ return;
+ } else {
+ pp->leap = 0;
+ pp->lasttime = current_time;
+ }
+
+ /*
+ * Process the new sample in the median filter and determine the
+ * reference clock offset and dispersion. We use lastrec as both
+ * the reference time and receive time in order to avoid being
+ * cute, like setting the reference time later than the receive
+ * time, which may cause a paranoid protocol module to chuck out
+ * the data.
+ */
+ if (!refclock_process(pp, NSAMPLES, NSAMPLES)) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ refclock_receive(peer, &pp->offset, 0, pp->dispersion,
+ &pp->lastrec, &pp->lastrec, pp->leap);
+}
+
+#endif
diff --git a/usr.sbin/xntpd/xntpd/refclock_trak.c b/usr.sbin/xntpd/xntpd/refclock_trak.c
new file mode 100644
index 0000000..d10d752
--- /dev/null
+++ b/usr.sbin/xntpd/xntpd/refclock_trak.c
@@ -0,0 +1,339 @@
+/*
+ * refclock_trak - clock driver for the TRAK 8820 GPS Station Clock
+ *
+ * Thanks to Tomoaki TSURUOKA <tsuruoka@nc.fukuoka-u.ac.jp> for the
+ * previous version from which this one was developed.
+ */
+#if defined(REFCLOCK) && defined(TRAK)
+
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/time.h>
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_stdlib.h"
+
+/*
+ * This driver supports the TRAK 8820 GPS Station Clock. The claimed
+ * accuracy at the 1-pps output is 200-300 ns relative to the broadcast
+ * signal; however, in most cases the actual accuracy is limited by the
+ * precision of the timecode and the latencies of the serial interface
+ * and operating system.
+ *
+ * For best accuracy, this radio requires the LDISC_ACTS line
+ * discipline, which captures a timestamp at the '*' on-time character
+ * of the timecode. Using this discipline the jitter is in the order of
+ * 1 ms and systematic error about 0.5 ms. If unavailable, the buffer
+ * timestamp is used, which is captured at the \r ending the timecode
+ * message. This introduces a systematic error of 23 character times, or
+ * about 24 ms at 9600 bps, together with a jitter well over 8 ms on Sun
+ * IPC-class machines.
+ *
+ * Using the memus, the radio should be set for 9600 bps, one stop bit
+ * and no parity. It should be set to operate in computer (no echo)
+ * mode. The timecode format includes neither the year nor leap-second
+ * warning. No provisions are included in this preliminary version of
+ * the driver to read and record detailed internal radio status.
+ *
+ * In operation, this driver sends a RQTS\r request to the radio at
+ * initialization in order to put it in continuous time output mode. The
+ * radio then sends the following message once each second:
+ *
+ * *RQTS U,ddd:hh:mm:ss.0,q<cr><lf>
+ *
+ * on-time = '*' * ddd = day of year
+ * hh:mm:ss = hours, minutes, seconds
+ * q = quality indicator (phase error), 0-6:
+ * 0 > 20 us
+ * 6 > 10 us
+ * 5 > 1 us
+ * 4 > 100 ns
+ * 3 > 10 ns
+ * 2 < 10 ns
+ *
+ * The alarm condition is indicated by '0' at Q, which means the radio
+ * has a phase error than 20 usec relative to the broadcast time. The
+ * absence of year, DST and leap-second warning in this format is also
+ * alarming.
+ *
+ * The continuous time mode is disabled using the RQTX<cr> request,
+ * following which the radio sends a RQTX DONE<cr><lf> response. In the
+ * normal mode, other control and status requests are effective,
+ * including the leap-second status request RQLS<cr>. The radio responds
+ * wtih RQLS yy,mm,dd<cr><lf>, where yy,mm,dd are the year, month and
+ * day. Presumably, this gives the epoch of the next leap second,
+ * RQLS 00,00,00 if none is specified in the GPS message. Specified in
+ * this form, the information is generally useless and is ignored by
+ * the driver.
+ *
+ * Fudge Factors
+ *
+ * There are no special fudge factors other than the generic.
+ */
+
+/*
+ * Interface definitions
+ */
+#define DEVICE "/dev/trak%d" /* device name and unit */
+#define SPEED232 B9600 /* uart speed (9600 baud) */
+#define PRECISION (-10) /* precision assumed (about 1 ms) */
+#define REFID "TRAK" /* reference ID */
+#define DESCRIPTION "TRACK 8810/8820 Station Clock" /* WRU */
+
+#define NSAMPLES 3 /* stages of median filter */
+#define LENTRAK 24 /* timecode length */
+#define C_CTO "RQTS\r" /* start continuous time output */
+
+/*
+ * Imported from ntp_timer module
+ */
+extern u_long current_time; /* current time (s) */
+
+/*
+ * Imported from ntpd module
+ */
+extern int debug; /* global debug flag */
+
+/*
+ * Unit control structure
+ */
+struct wwvbunit {
+ int pollcnt; /* poll message counter */
+
+ u_char tcswitch; /* timecode switch */
+ char qualchar; /* quality indicator */
+};
+
+/*
+ * Function prototypes
+ */
+static int trak_start P((int, struct peer *));
+static void trak_shutdown P((int, struct peer *));
+static void trak_receive P((struct recvbuf *));
+static void trak_poll P((int, struct peer *));
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_trak = {
+ trak_start, /* start up driver */
+ trak_shutdown, /* shut down driver */
+ trak_poll, /* transmit poll message */
+ noentry, /* not used (old trak_control) */
+ noentry, /* initialize driver (not used) */
+ noentry, /* not used (old trak_buginfo) */
+ NOFLAGS /* not used */
+};
+
+
+/*
+ * trak_start - open the devices and initialize data for processing
+ */
+static int
+trak_start(unit, peer)
+ int unit;
+ struct peer *peer;
+{
+ register struct wwvbunit *up;
+ struct refclockproc *pp;
+ int fd;
+ char device[20];
+
+ /*
+ * Open serial port. The LDISC_ACTS line discipline inserts a
+ * timestamp following the "*" on-time character of the
+ * timecode.
+ */
+ (void)sprintf(device, DEVICE, unit);
+ if (!(fd = refclock_open(device, SPEED232, LDISC_ACTS)))
+ return (0);
+
+ /*
+ * Allocate and initialize unit structure
+ */
+ if (!(up = (struct wwvbunit *)
+ emalloc(sizeof(struct wwvbunit)))) {
+ (void) close(fd);
+ return (0);
+ }
+ memset((char *)up, 0, sizeof(struct wwvbunit));
+ pp = peer->procptr;
+ pp->io.clock_recv = trak_receive;
+ pp->io.srcclock = (caddr_t)peer;
+ pp->io.datalen = 0;
+ pp->io.fd = fd;
+ if (!io_addclock(&pp->io)) {
+ (void) close(fd);
+ free(up);
+ return (0);
+ }
+ pp->unitptr = (caddr_t)up;
+
+ /*
+ * Initialize miscellaneous variables
+ */
+ peer->precision = PRECISION;
+ pp->clockdesc = DESCRIPTION;
+ memcpy((char *)&pp->refid, REFID, 4);
+ up->pollcnt = 2;
+
+ /*
+ * Start continuous time output. If something breaks, fold the
+ * tent and go home.
+ */
+ if (write(pp->io.fd, C_CTO, sizeof(C_CTO)) != sizeof(C_CTO)) {
+ refclock_report(peer, CEVNT_FAULT);
+ (void) close(fd);
+ free(up);
+ return (0);
+ }
+ return (1);
+}
+
+
+/*
+ * trak_shutdown - shut down the clock
+ */
+static void
+trak_shutdown(unit, peer)
+ int unit;
+ struct peer *peer;
+{
+ register struct wwvbunit *up;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ up = (struct wwvbunit *)pp->unitptr;
+ io_closeclock(&pp->io);
+ free(up);
+}
+
+
+/*
+ * trak_receive - receive data from the serial interface
+ */
+static void
+trak_receive(rbufp)
+ struct recvbuf *rbufp;
+{
+ register struct wwvbunit *up;
+ struct refclockproc *pp;
+ struct peer *peer;
+ l_fp trtmp;
+ char *dpt, *dpend;
+ char qchar;
+
+ /*
+ * Initialize pointers and read the timecode and timestamp. We
+ * then chuck out everything, including runts, except one
+ * message each poll interval.
+ */
+ peer = (struct peer *)rbufp->recv_srcclock;
+ pp = peer->procptr;
+ up = (struct wwvbunit *)pp->unitptr;
+ pp->lencode = refclock_gtlin(rbufp, pp->lastcode, BMAX,
+ &pp->lastrec);
+ if (up->tcswitch || pp->lencode < 9)
+ return;
+ up->tcswitch = 1;
+
+ /*
+ * We get a buffer and timestamp following the '*' on-time
+ * character. If a valid timestamp, we use that in place of the
+ * buffer timestamp and edit out the timestamp for prettyprint
+ * billboards.
+ */
+ dpt = pp->lastcode;
+ dpend = dpt + pp->lencode;
+ if (*dpt == '*' && buftvtots(dpt + 1, &trtmp)) {
+ if (trtmp.l_i == pp->lastrec.l_i || trtmp.l_i ==
+ pp->lastrec.l_i + 1) {
+ pp->lastrec = trtmp;
+ dpt += 9;
+ while (dpt < dpend)
+ *(dpt - 8) = *dpt++;
+ }
+ }
+ up->pollcnt = 2;
+ record_clock_stats(&peer->srcadr, pp->lastcode);
+#ifdef DEBUG
+ if (debug)
+ printf("trak: timecode %d %s\n", pp->lencode,
+ pp->lastcode);
+#endif
+
+ /*
+ * We get down to business, check the timecode format and decode
+ * its contents. If the timecode has invalid length or is not in
+ * proper format, we declare bad format and exit.
+ */
+ if (pp->lencode < LENTRAK) {
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+
+ /*
+ * Timecode format: "*RQTS U,ddd:hh:mm:ss.0,q"
+ */
+ if (sscanf(pp->lastcode, "*RQTS U,%3d:%2d:%2d:%2d.0,%c",
+ &pp->day, &pp->hour, &pp->minute, &pp->second, &qchar) != 5) {
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+
+ /*
+ * Decode quality and leap characters. If unsynchronized, set
+ * the leap bits accordingly and exit.
+ */
+ if (qchar == '0')
+ pp->leap = LEAP_NOTINSYNC;
+ else {
+ pp->leap = 0;
+ pp->lasttime = current_time;
+ }
+
+ /*
+ * Process the new sample in the median filter and determine the
+ * reference clock offset and dispersion. We use lastrec as both
+ * the reference time and receive time in order to avoid being
+ * cute, like setting the reference time later than the receive
+ * time, which may cause a paranoid protocol module to chuck out
+ * the data.
+ */
+ if (!refclock_process(pp, NSAMPLES, NSAMPLES)) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ refclock_receive(peer, &pp->offset, 0, pp->dispersion,
+ &pp->lastrec, &pp->lastrec, pp->leap);
+}
+
+
+/*
+ * trak_poll - called by the transmit procedure
+ */
+static void
+trak_poll(unit, peer)
+ int unit;
+ struct peer *peer;
+{
+ register struct wwvbunit *up;
+ struct refclockproc *pp;
+
+ /*
+ * We don't really do anything here, except arm the receiving
+ * side to capture a sample and check for timeouts.
+ */
+ pp = peer->procptr;
+ up = (struct wwvbunit *)pp->unitptr;
+ if (up->pollcnt == 0)
+ refclock_report(peer, CEVNT_TIMEOUT);
+ else
+ up->pollcnt--;
+ up->tcswitch = 0;
+ pp->polls++;
+}
+
+#endif
diff --git a/usr.sbin/xntpd/xntpd/refclock_wwvb.c b/usr.sbin/xntpd/xntpd/refclock_wwvb.c
new file mode 100644
index 0000000..ea0c82e
--- /dev/null
+++ b/usr.sbin/xntpd/xntpd/refclock_wwvb.c
@@ -0,0 +1,420 @@
+/*
+ * refclock_wwvb - clock driver for Spectracom WWVB receivers
+ */
+#if defined(REFCLOCK) && defined(WWVB)
+
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/time.h>
+
+#include "ntpd.h"
+#include "ntp_io.h"
+#include "ntp_refclock.h"
+#include "ntp_stdlib.h"
+
+/*
+ * This driver supports the Spectracom Model 8170 and Netclock/2 WWVB
+ * Synchronized Clock. This clock has proven a reliable source of time,
+ * except in some cases of high ambient conductive RF interference. The
+ * claimed accuracy of the clock is 100 usec relative to the broadcast
+ * signal; however, in most cases the actual accuracy is limited by the
+ * precision of the timecode and the latencies of the serial interface
+ * and operating system.
+ *
+ * The DIPswitches on this clock should be set to 24-hour display, AUTO
+ * DST off, time zone 0 (UTC), data format 0 or 2 (see below) and baud
+ * rate 9600. If this clock is to used as the source for the IRIG Audio
+ * Decoder (refclock_irig.c in this distribution), set the DIPswitches
+ * for AM IRIG output and IRIG format 1 (IRIG B with signature control).
+ *
+ * There are two timecode formats used by these clocks. Format 0, which
+ * is available with both the Netclock/2 and 8170, and format 2, which
+ * is available only with the Netclock/2 and specially modified 8170.
+ *
+ * Format 0 (22 ASCII printing characters):
+ *
+ * <cr><lf>i ddd hh:mm:ss TZ=zz<cr><lf>
+ *
+ * on-time = first <cr> * hh:mm:ss = hours, minutes, seconds
+ * i = synchronization flag (' ' = in synch, '?' = out of synch)
+ *
+ * The alarm condition is indicated by other than ' ' at A, which occurs
+ * during initial synchronization and when received signal is lost for
+ * about ten hours.
+ *
+ * Format 2 (24 ASCII printing characters):
+ *
+ * <cr><lf>iqyy ddd hh:mm:ss.fff ld
+ *
+ * on-time = <cr>
+ * i = synchronization flag (' ' = in synch, '?' = out of synch)
+ * q = quality indicator (' ' = locked, 'A'...'D' = unlocked)
+ * yy = year (as broadcast)
+ * ddd = day of year
+ * hh:mm:ss.fff = hours, minutes, seconds, milliseconds
+ *
+ * The alarm condition is indicated by other than ' ' at A, which occurs
+ * during initial synchronization and when received signal is lost for
+ * about ten hours. The unlock condition is indicated by other than ' '
+ * at Q.
+ *
+ * The Q is normally ' ' when the time error is less than 1 ms and a
+ * character in the set 'A'...'D' when the time error is less than 10,
+ * 100, 500 and greater than 500 ms respectively. The L is normally ' ',
+ * but is set to 'L' early in the month of an upcoming UTC leap second
+ * and reset to ' ' on the first day of the following month. The D is
+ * set to 'S' for standard time 'I' on the day preceding a switch to
+ * daylight time, 'D' for daylight time and 'O' on the day preceding a
+ * switch to standard time. The start bit of the first <cr> is
+ * synchronized to the indicated time as returned.
+ *
+ * This driver does not need to be told which format is in use - it
+ * figures out which one from the length of the message. A three-stage
+ * median filter is used to reduce jitter and provide a dispersion
+ * measure. The driver makes no attempt to correct for the intrinsic
+ * jitter of the radio itself, which is a known problem with the older
+ * radios.
+ *
+ * Fudge Factors
+ *
+ * This driver can retrieve a table of quality data maintained
+ * internally by the Netclock/2 receiver. If flag4 of the fudge
+ * configuration command is set to 1, the driver will retrieve this
+ * table and write it to the clockstats file on when the first timecode
+ * message of a new day is received.
+ */
+
+/*
+ * Interface definitions
+ */
+#define DEVICE "/dev/wwvb%d" /* device name and unit */
+#define SPEED232 B9600 /* uart speed (9600 baud) */
+#define PRECISION (-10) /* precision assumed (about 1 ms) */
+#define REFID "WWVB" /* reference ID */
+#define DESCRIPTION "Spectracom WWVB Receiver" /* WRU */
+
+#define NSAMPLES 3 /* stages of median filter */
+#define LENWWVB0 22 /* format 0 timecode length */
+#define LENWWVB2 24 /* format 2 timecode length */
+#define MONLIN 15 /* number of monitoring lines */
+
+/*
+ * Imported from ntp_timer module
+ */
+extern u_long current_time; /* current time (s) */
+
+/*
+ * Imported from ntpd module
+ */
+extern int debug; /* global debug flag */
+
+/*
+ * WWVB unit control structure
+ */
+struct wwvbunit {
+ int pollcnt; /* poll message counter */
+
+ u_char tcswitch; /* timecode switch */
+ l_fp laststamp; /* last receive timestamp */
+ u_char lasthour; /* last hour (for monitor) */
+ u_char linect; /* count ignored lines (for monitor */
+};
+
+/*
+ * Function prototypes
+ */
+static int wwvb_start P((int, struct peer *));
+static void wwvb_shutdown P((int, struct peer *));
+static void wwvb_receive P((struct recvbuf *));
+static void wwvb_poll P((int, struct peer *));
+
+/*
+ * Transfer vector
+ */
+struct refclock refclock_wwvb = {
+ wwvb_start, /* start up driver */
+ wwvb_shutdown, /* shut down driver */
+ wwvb_poll, /* transmit poll message */
+ noentry, /* not used (old wwvb_control) */
+ noentry, /* initialize driver (not used) */
+ noentry, /* not used (old wwvb_buginfo) */
+ NOFLAGS /* not used */
+};
+
+
+/*
+ * wwvb_start - open the devices and initialize data for processing
+ */
+static int
+wwvb_start(unit, peer)
+ int unit;
+ struct peer *peer;
+{
+ register struct wwvbunit *up;
+ struct refclockproc *pp;
+ int fd;
+ char device[20];
+
+ /*
+ * Open serial port. Use CLK line discipline, if available.
+ */
+ (void)sprintf(device, DEVICE, unit);
+ if (!(fd = refclock_open(device, SPEED232, LDISC_CLK)))
+ return (0);
+
+ /*
+ * Allocate and initialize unit structure
+ */
+ if (!(up = (struct wwvbunit *)
+ emalloc(sizeof(struct wwvbunit)))) {
+ (void) close(fd);
+ return (0);
+ }
+ memset((char *)up, 0, sizeof(struct wwvbunit));
+ pp = peer->procptr;
+ pp->io.clock_recv = wwvb_receive;
+ pp->io.srcclock = (caddr_t)peer;
+ pp->io.datalen = 0;
+ pp->io.fd = fd;
+ if (!io_addclock(&pp->io)) {
+ (void) close(fd);
+ free(up);
+ return (0);
+ }
+ pp->unitptr = (caddr_t)up;
+
+ /*
+ * Initialize miscellaneous variables
+ */
+ peer->precision = PRECISION;
+ pp->clockdesc = DESCRIPTION;
+ memcpy((char *)&pp->refid, REFID, 4);
+ up->pollcnt = 2;
+ return (1);
+}
+
+
+/*
+ * wwvb_shutdown - shut down the clock
+ */
+static void
+wwvb_shutdown(unit, peer)
+ int unit;
+ struct peer *peer;
+{
+ register struct wwvbunit *up;
+ struct refclockproc *pp;
+
+ pp = peer->procptr;
+ up = (struct wwvbunit *)pp->unitptr;
+ io_closeclock(&pp->io);
+ free(up);
+}
+
+
+/*
+ * wwvb_receive - receive data from the serial interface
+ */
+static void
+wwvb_receive(rbufp)
+ struct recvbuf *rbufp;
+{
+ register struct wwvbunit *up;
+ struct refclockproc *pp;
+ struct peer *peer;
+ l_fp trtmp;
+ u_long ltemp;
+ int temp;
+ char syncchar; /* synchronization indicator */
+ char qualchar; /* quality indicator */
+ char leapchar; /* leap indicator */
+
+ /*
+ * Initialize pointers and read the timecode and timestamp
+ */
+ peer = (struct peer *)rbufp->recv_srcclock;
+ pp = peer->procptr;
+ up = (struct wwvbunit *)pp->unitptr;
+ temp = refclock_gtlin(rbufp, pp->lastcode, BMAX, &trtmp);
+
+ /*
+ * Note we get a buffer and timestamp for both a <cr> and <lf>,
+ * but only the <cr> timestamp is retained. Note: in format 0 on
+ * a Netclock/2 or upgraded 8170 the start bit is delayed 100
+ * +-50 us relative to the pps; however, on an unmodified 8170
+ * the start bit can be delayed up to 10 ms. In format 2 the
+ * reading precision is only to the millisecond. Thus, unless
+ * you have a pps gadget and don't have to have the year, format
+ * 0 provides the lowest jitter.
+ */
+ if (temp == 0) {
+ if (up->tcswitch == 0) {
+ up->tcswitch = 1;
+ up->laststamp = trtmp;
+ } else
+ up->tcswitch = 0;
+ return;
+ }
+ pp->lencode = temp;
+ pp->lastrec = up->laststamp;
+ up->laststamp = trtmp;
+ up->tcswitch = 1;
+ up->pollcnt = 2;
+ record_clock_stats(&peer->srcadr, pp->lastcode);
+#ifdef DEBUG
+ if (debug)
+ printf("wwvb: timecode %d %s\n", pp->lencode,
+ pp->lastcode);
+#endif
+
+ /*
+ * We get down to business, check the timecode format and decode
+ * its contents. This code uses the timecode length to determine
+ * whether format 0 or format 2. If the timecode has invalid
+ * length or is not in proper format, we declare bad format and
+ * exit.
+ */
+ switch (pp->lencode) {
+
+ case LENWWVB0:
+
+ /*
+ * Timecode format 0: "I ddd hh:mm:ss TZ=nn"
+ */
+ qualchar = leapchar = ' ';
+ if (sscanf(pp->lastcode, "%c %3d %2d:%2d:%2d",
+ &syncchar, &pp->day, &pp->hour, &pp->minute,
+ &pp->second) == 5)
+ break;
+
+ case LENWWVB2:
+
+ /*
+ * Timecode format 2: "IQyy ddd hh:mm:ss.mmm LD"
+ */
+ if (sscanf(pp->lastcode, "%c%c %2d %3d %2d:%2d:%2d.%3d %c",
+ &syncchar, &qualchar, &pp->year, &pp->day,
+ &pp->hour, &pp->minute, &pp->second, &pp->msec,
+ &leapchar) == 9)
+ break;
+
+ default:
+
+ if (up->linect > 0)
+ up->linect--;
+ else
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+
+ /*
+ * Decode synchronization, quality and leap characters. If
+ * unsynchronized, set the leap bits accordingly and exit.
+ * Otherwise, set the leap bits according to the leap character.
+ * Once synchronized, the dispersion depends only on when the
+ * clock was last heard. The first time the clock is heard, the
+ * time last heard is faked based on the quality indicator. The
+ * magic numbers (in seconds) are from the clock specifications.
+ */
+ switch (qualchar) {
+
+ case ' ':
+ ltemp = 0;
+ break;
+
+ case 'A':
+ ltemp = 800;
+ break;
+
+ case 'B':
+ ltemp = 5300;
+ break;
+
+ case 'C':
+ ltemp = 25300;
+ break;
+
+ case 'D':
+ ltemp = NTP_MAXAGE;
+ break;
+
+ default:
+ refclock_report(peer, CEVNT_BADREPLY);
+ return;
+ }
+ if (syncchar != ' ')
+ pp->leap = LEAP_NOTINSYNC;
+ else {
+ if (leapchar == 'L')
+ pp->leap = LEAP_ADDSECOND;
+ else
+ pp->leap = 0;
+ pp->lasttime = current_time - ltemp;
+ }
+
+ /*
+ * If the monitor flag is set (flag4), we dump the internal
+ * quality table at the first timecode beginning the day.
+ */
+ if (pp->sloppyclockflag & CLK_FLAG4 && pp->hour <
+ up->lasthour)
+ up->linect = MONLIN;
+ up->lasthour = pp->hour;
+
+ /*
+ * Process the new sample in the median filter and determine the
+ * reference clock offset and dispersion. We use lastrec as both
+ * the reference time and receive time in order to avoid being
+ * cute, like setting the reference time later than the receive
+ * time, which may cause a paranoid protocol module to chuck out
+ * the data.
+ */
+ if (!refclock_process(pp, NSAMPLES, NSAMPLES)) {
+ refclock_report(peer, CEVNT_BADTIME);
+ return;
+ }
+ trtmp = pp->lastrec;
+ trtmp.l_ui -= ltemp;
+ refclock_receive(peer, &pp->offset, 0, pp->dispersion,
+ &trtmp, &pp->lastrec, pp->leap);
+}
+
+
+/*
+ * wwvb_poll - called by the transmit procedure
+ */
+static void
+wwvb_poll(unit, peer)
+ int unit;
+ struct peer *peer;
+{
+ register struct wwvbunit *up;
+ struct refclockproc *pp;
+ char poll;
+
+ /*
+ * Time to poll the clock. The Spectracom clock responds to a
+ * 'T' by returning a timecode in the format(s) specified above.
+ * Note there is no checking on state, since this may not be the
+ * only customer reading the clock. Only one customer need poll
+ * the clock; all others just listen in. If nothing is heard
+ * from the clock for two polls, declare a timeout and keep
+ * going.
+ */
+ pp = peer->procptr;
+ up = (struct wwvbunit *)pp->unitptr;
+ if (up->pollcnt == 0)
+ refclock_report(peer, CEVNT_TIMEOUT);
+ else
+ up->pollcnt--;
+ if (up->linect > 0)
+ poll = 'R';
+ else
+ poll = 'T';
+ if (write(pp->io.fd, &poll, 1) != 1) {
+ refclock_report(peer, CEVNT_FAULT);
+ } else
+ pp->polls++;
+}
+
+#endif
diff --git a/usr.sbin/xntpd/xntpdc/Makefile b/usr.sbin/xntpd/xntpdc/Makefile
new file mode 100644
index 0000000..5f545e1
--- /dev/null
+++ b/usr.sbin/xntpd/xntpdc/Makefile
@@ -0,0 +1,28 @@
+#
+# $Id$
+#
+
+CFLAGS+= -I${.CURDIR}/../include
+
+.if exists(${.OBJDIR}/../lib)
+LDADD+= -L${.OBJDIR}/../lib
+DPADD+= ${.OBJDIR}/../lib/libntp.a
+.else
+LDADD+= -L${.CURDIR}/../lib
+DPADD+= ${.CURDIR}/../lib/libntp.a
+.endif
+
+LDADD+= -lntp
+
+PROG= xntpdc
+MAN8= ${.CURDIR}/../doc/xntpdc.8
+CLEANFILES+= .version version.c
+
+SRCS= ntpdc.c ntpdc_ops.c version.c
+
+beforedepend: version.c
+
+version.c: ${.CURDIR}/../VERSION
+ sh -e ${.CURDIR}/../scripts/mkversion xntpdc
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/xntpd/xntpdc/README b/usr.sbin/xntpd/xntpdc/README
new file mode 100644
index 0000000..9d0b661
--- /dev/null
+++ b/usr.sbin/xntpd/xntpdc/README
@@ -0,0 +1,6 @@
+README file for directory ./xntpdc of the NTP Version 3 distribution
+
+This directory contains the sources for the xntpdc utility program. See
+the README and RELNOTES files in the parent directory for directions on
+how to make and install this program. The current version number of this
+program is in the version.c file.
diff --git a/usr.sbin/xntpd/xntpdc/ntpdc.c b/usr.sbin/xntpd/xntpdc/ntpdc.c
new file mode 100644
index 0000000..bb42a19
--- /dev/null
+++ b/usr.sbin/xntpd/xntpdc/ntpdc.c
@@ -0,0 +1,1532 @@
+/*
+ * xntpdc - control and monitor your xntpd daemon
+ */
+#include <stdio.h>
+#include <ctype.h>
+#include <signal.h>
+#include <setjmp.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <netdb.h>
+
+#include "ntpdc.h"
+#include "ntp_select.h"
+#include "ntp_io.h"
+#include "ntp_stdlib.h"
+
+/*
+ * Because we now potentially understand a lot of commands (and
+ * it requires a lot of commands to talk to xntpd) we will run
+ * interactive if connected to a terminal.
+ */
+static int interactive = 0; /* set to 1 when we should prompt */
+static char * prompt = "xntpdc> "; /* prompt to ask him about */
+
+
+/*
+ * Keyid used for authenticated requests. Obtained on the fly.
+ */
+static u_long info_auth_keyid;
+
+/*
+ * Type of key md5 or des
+ */
+#define KEY_TYPE_DES 3
+#define KEY_TYPE_MD5 4
+
+static int info_auth_keytype = KEY_TYPE_DES; /* DES */
+
+
+/*
+ * Built in command handler declarations
+ */
+static int openhost P((char *));
+static int sendpkt P((char *, int));
+static void growpktdata P((void));
+static int getresponse P((int, int, int *, int *, char **));
+static int sendrequest P((int, int, int, int, int, char *));
+static void getcmds P((void));
+static RETSIGTYPE abortcmd P((int));
+static void docmd P((char *));
+static void tokenize P((char *, char **, int *));
+static int findcmd P((char *, struct xcmd *, struct xcmd *, struct xcmd **));
+static int getarg P((char *, int, arg_v *));
+static int getnetnum P((char *, u_long *, char *));
+static void help P((struct parse *, FILE *));
+#if defined(sgi) || defined(SYS_BSDI) || defined(__STDC__)
+static int helpsort P((const void *, const void *));
+#else
+static int helpsort P((char **, char **));
+#endif /* sgi */
+static void printusage P((struct xcmd *, FILE *));
+static void timeout P((struct parse *, FILE *));
+static void delay P((struct parse *, FILE *));
+static void host P((struct parse *, FILE *));
+static void keyid P((struct parse *, FILE *));
+static void keytype P((struct parse *, FILE *));
+static void passwd P((struct parse *, FILE *));
+static void hostnames P((struct parse *, FILE *));
+static void setdebug P((struct parse *, FILE *));
+static void quit P((struct parse *, FILE *));
+static void version P((struct parse *, FILE *));
+static void warning P((char *, char *, char *));
+static void error P((char *, char *, char *));
+static u_long getkeyid P((char *));
+
+
+/*
+ * Built-in commands we understand
+ */
+static struct xcmd builtins[] = {
+ { "?", help, { OPT|NTP_STR, NO, NO, NO },
+ { "command", "", "", "" },
+ "tell the use and syntax of commands" },
+ { "help", help, { OPT|NTP_STR, NO, NO, NO },
+ { "command", "", "", "" },
+ "tell the use and syntax of commands" },
+ { "timeout", timeout, { OPT|UINT, NO, NO, NO },
+ { "msec", "", "", "" },
+ "set the primary receive time out" },
+ { "delay", delay, { OPT|INT, NO, NO, NO },
+ { "msec", "", "", "" },
+ "set the delay added to encryption time stamps" },
+ { "host", host, { OPT|NTP_STR, NO, NO, NO },
+ { "hostname", "", "", "" },
+ "specify the host whose NTP server we talk to" },
+ { "passwd", passwd, { OPT|NTP_STR, NO, NO, NO },
+ { "", "", "", "" },
+ "specify a password to use for authenticated requests"},
+ { "hostnames", hostnames, { OPT|NTP_STR, NO, NO, NO },
+ { "yes|no", "", "", "" },
+ "specify whether hostnames or net numbers are printed"},
+ { "debug", setdebug, { OPT|NTP_STR, NO, NO, NO },
+ { "no|more|less", "", "", "" },
+ "set/change debugging level" },
+ { "quit", quit, { NO, NO, NO, NO },
+ { "", "", "", "" },
+ "exit xntpdc" },
+ { "keyid", keyid, { OPT|UINT, NO, NO, NO },
+ { "key#", "", "", "" },
+ "set keyid to use for authenticated requests" },
+ { "keytype", keytype, { NTP_STR, NO, NO, NO },
+ { "key type (md5|des)", "", "", "" },
+ "set key type to use for authenticated requests (des|md5)" },
+ { "version", version, { NO, NO, NO, NO },
+ { "", "", "", "" },
+ "print version number" },
+ { 0, 0, { NO, NO, NO, NO },
+ { "", "", "", "" }, "" }
+};
+
+
+/*
+ * Default values we use.
+ */
+#define DEFTIMEOUT (5) /* 5 second time out */
+#define DEFSTIMEOUT (2) /* 2 second time out after first */
+#define DEFDELAY 0x51EB852 /* 20 milliseconds, l_fp fraction */
+#define DEFHOST "localhost" /* default host name */
+#define LENHOSTNAME 256 /* host name is 256 characters long */
+#define MAXCMDS 100 /* maximum commands on cmd line */
+#define MAXHOSTS 100 /* maximum hosts on cmd line */
+#define MAXLINE 512 /* maximum line length */
+#define MAXTOKENS (1+MAXARGS+2) /* maximum number of usable tokens */
+
+/*
+ * Some variables used and manipulated locally
+ */
+static struct timeval tvout = { DEFTIMEOUT, 0 }; /* time out for reads */
+static struct timeval tvsout = { DEFSTIMEOUT, 0 }; /* secondary time out */
+static l_fp delay_time; /* delay time */
+static char currenthost[LENHOSTNAME]; /* current host name */
+static struct sockaddr_in hostaddr = { 0 }; /* host address */
+static int showhostnames = 1; /* show host names by default */
+
+static int sockfd; /* fd socket is openned on */
+static int havehost = 0; /* set to 1 when host open */
+ struct servent *server_entry = NULL; /* server entry for ntp */
+
+/*
+ * Holds data returned from queries. We allocate INITDATASIZE
+ * octets to begin with, increasing this as we need to.
+ */
+#define INITDATASIZE (sizeof(struct resp_pkt) * 16)
+#define INCDATASIZE (sizeof(struct resp_pkt) * 8)
+
+static char *pktdata;
+static int pktdatasize;
+
+/*
+ * For commands typed on the command line (with the -c option)
+ */
+static int numcmds = 0;
+static char *ccmds[MAXCMDS];
+#define ADDCMD(cp) if (numcmds < MAXCMDS) ccmds[numcmds++] = (cp)
+
+/*
+ * When multiple hosts are specified.
+ */
+static int numhosts = 0;
+static char *chosts[MAXHOSTS];
+#define ADDHOST(cp) if (numhosts < MAXHOSTS) chosts[numhosts++] = (cp)
+
+/*
+ * Error codes for internal use
+ */
+#define ERR_INCOMPLETE 16
+#define ERR_TIMEOUT 17
+
+/*
+ * Macro definitions we use
+ */
+#define ISSPACE(c) ((c) == ' ' || (c) == '\t')
+#define ISEOL(c) ((c) == '\n' || (c) == '\r' || (c) == '\0')
+#define STREQ(a, b) (*(a) == *(b) && strcmp((a), (b)) == 0)
+
+/*
+ * For converting time stamps to dates
+ */
+#define JAN_1970 2208988800 /* 1970 - 1900 in seconds */
+
+/*
+ * Jump buffer for longjumping back to the command level
+ */
+static jmp_buf interrupt_buf;
+static int jump = 0;
+
+/*
+ * Pointer to current output unit
+ */
+static FILE *current_output;
+
+/*
+ * Command table imported from ntpdc_ops.c
+ */
+extern struct xcmd opcmds[];
+
+char *progname;
+int debug;
+
+/*
+ * main - parse arguments and handle options
+ */
+void
+main(argc, argv)
+int argc;
+char *argv[];
+{
+ int c;
+ int errflg = 0;
+ extern int ntp_optind;
+ extern char *ntp_optarg;
+
+ delay_time.l_ui = 0;
+ delay_time.l_uf = DEFDELAY;
+
+ progname = argv[0];
+ while ((c = ntp_getopt(argc, argv, "c:dilnps")) != EOF)
+ switch (c) {
+ case 'c':
+ ADDCMD(ntp_optarg);
+ break;
+ case 'd':
+ ++debug;
+ break;
+ case 'i':
+ interactive = 1;
+ break;
+ case 'l':
+ ADDCMD("listpeers");
+ break;
+ case 'n':
+ showhostnames = 0;
+ break;
+ case 'p':
+ ADDCMD("peers");
+ break;
+ case 's':
+ ADDCMD("dmpeers");
+ break;
+ default:
+ errflg++;
+ break;
+ }
+ if (errflg) {
+ (void) fprintf(stderr,
+ "usage: %s [-dilnps] [-c cmd] host ...\n",
+ progname);
+ exit(2);
+ }
+ if (ntp_optind == argc) {
+ ADDHOST(DEFHOST);
+ } else {
+ for (; ntp_optind < argc; ntp_optind++)
+ ADDHOST(argv[ntp_optind]);
+ }
+
+ if (numcmds == 0 && interactive == 0
+ && isatty(fileno(stdin)) && isatty(fileno(stderr))) {
+ interactive = 1;
+ }
+
+ if (interactive)
+ (void) signal_no_reset(SIGINT, abortcmd);
+
+ /*
+ * Initialize the packet data buffer
+ */
+ pktdata = (char *)malloc(INITDATASIZE);
+ if (pktdata == NULL) {
+ (void) fprintf(stderr, "%s: malloc() failed!\n", progname);
+ exit(1);
+ }
+ pktdatasize = INITDATASIZE;
+
+ if (numcmds == 0) {
+ (void) openhost(chosts[0]);
+ getcmds();
+ } else {
+ int ihost;
+ int icmd;
+
+ for (ihost = 0; ihost < numhosts; ihost++) {
+ if (openhost(chosts[ihost]))
+ for (icmd = 0; icmd < numcmds; icmd++) {
+ if (numhosts > 1)
+ printf ("--- %s ---\n",chosts[ihost]);
+ docmd(ccmds[icmd]);
+ }
+ }
+ }
+ exit(0);
+}
+
+
+/*
+ * openhost - open a socket to a host
+ */
+static int
+openhost(hname)
+ char *hname;
+{
+ u_long netnum;
+ char temphost[LENHOSTNAME];
+
+ if (server_entry == NULL) {
+ server_entry = getservbyname("ntp", "udp");
+ if (server_entry == NULL) {
+ (void) fprintf(stderr, "%s: ntp/udp: unknown service\n",
+ progname);
+ exit(1);
+ }
+ if (debug > 2)
+ printf("Got ntp/udp service entry\n");
+ }
+
+ if (!getnetnum(hname, &netnum, temphost))
+ return 0;
+
+ if (debug > 2)
+ printf("Opening host %s\n", temphost);
+
+ if (havehost == 1) {
+ if (debug > 2)
+ printf("Closing old host %s\n", currenthost);
+ (void) close(sockfd);
+ havehost = 0;
+ }
+ (void) strcpy(currenthost, temphost);
+
+ hostaddr.sin_family = AF_INET;
+ hostaddr.sin_port = server_entry->s_port;
+ hostaddr.sin_addr.s_addr = netnum;
+
+ sockfd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sockfd == -1)
+ error("socket", "", "");
+
+#if defined(SYS_HPUX) && (SYS_HPUX < 8)
+#ifdef SO_RCVBUF
+ { int rbufsize = INITDATASIZE + 2048; /* 2K for slop */
+ if (setsockopt(sockfd, SOL_SOCKET, SO_RCVBUF,
+ &rbufsize, sizeof(int)) == -1)
+ error("setsockopt", "", "");
+ }
+#endif
+#endif
+
+ if (connect(sockfd, (struct sockaddr *)&hostaddr,
+ sizeof(hostaddr)) == -1)
+ error("connect", "", "");
+
+ havehost = 1;
+ return 1;
+}
+
+
+/* XXX ELIMINATE sendpkt similar in ntpq.c, ntpdc.c, ntp_io.c, ntptrace.c */
+/*
+ * sendpkt - send a packet to the remote host
+ */
+static int
+sendpkt(xdata, xdatalen)
+ char *xdata;
+ int xdatalen;
+{
+ if (write(sockfd, xdata, xdatalen) == -1) {
+ warning("write to %s failed", currenthost, "");
+ return -1;
+ }
+
+ return 0;
+}
+
+
+/*
+ * growpktdata - grow the packet data area
+ */
+static void
+growpktdata()
+{
+ pktdatasize += INCDATASIZE;
+ pktdata = (char *)realloc(pktdata, pktdatasize);
+ if (pktdata == 0) {
+ (void) fprintf(stderr, "%s: realloc() failed!\n", progname);
+ exit(1);
+ }
+}
+
+
+/*
+ * getresponse - get a (series of) response packet(s) and return the data
+ */
+static int
+getresponse(implcode, reqcode, ritems, rsize, rdata)
+ int implcode;
+ int reqcode;
+ int *ritems;
+ int *rsize;
+ char **rdata;
+{
+ struct resp_pkt rpkt;
+ struct timeval tvo;
+ int items;
+ int size;
+ int datasize;
+ char *datap;
+ char haveseq[MAXSEQ+1];
+ int firstpkt;
+ int lastseq;
+ int numrecv;
+ int seq;
+ fd_set fds;
+ int n;
+
+ /*
+ * This is pretty tricky. We may get between 1 and many packets
+ * back in response to the request. We peel the data out of
+ * each packet and collect it in one long block. When the last
+ * packet in the sequence is received we'll know how many we
+ * should have had. Note we use one long time out, should reconsider.
+ */
+ *ritems = 0;
+ *rsize = 0;
+ firstpkt = 1;
+ numrecv = 0;
+ *rdata = datap = pktdata;
+ lastseq = 999; /* too big to be a sequence number */
+ memset(haveseq, 0, sizeof(haveseq));
+ FD_ZERO(&fds);
+
+again:
+ if (firstpkt)
+ tvo = tvout;
+ else
+ tvo = tvsout;
+
+ FD_SET(sockfd, &fds);
+ n = select(sockfd+1, &fds, (fd_set *)0, (fd_set *)0, &tvo);
+
+ if (n == -1) {
+ warning("select fails", "", "");
+ return -1;
+ }
+ if (n == 0) {
+ /*
+ * Timed out. Return what we have
+ */
+ if (firstpkt) {
+ (void) fprintf(stderr,
+ "%s: timed out, nothing received\n", currenthost);
+ return ERR_TIMEOUT;
+ } else {
+ (void) fprintf(stderr,
+ "%s: timed out with incomplete data\n",
+ currenthost);
+ if (debug) {
+ printf("Received sequence numbers");
+ for (n = 0; n <= MAXSEQ; n++)
+ if (haveseq[n])
+ printf(" %d,", n);
+ if (lastseq != 999)
+ printf(" last frame received\n");
+ else
+ printf(" last frame not received\n");
+ }
+ return ERR_INCOMPLETE;
+ }
+ }
+
+ n = read(sockfd, (char *)&rpkt, sizeof(rpkt));
+ if (n == -1) {
+ warning("read", "", "");
+ return -1;
+ }
+
+
+ /*
+ * Check for format errors. Bug proofing.
+ */
+ if (n < RESP_HEADER_SIZE) {
+ if (debug)
+ printf("Short (%d byte) packet received\n", n);
+ goto again;
+ }
+ if (INFO_VERSION(rpkt.rm_vn_mode) != NTP_VERSION) {
+ if (debug)
+ printf("Packet received with version %d\n",
+ INFO_VERSION(rpkt.rm_vn_mode));
+ goto again;
+ }
+ if (INFO_MODE(rpkt.rm_vn_mode) != MODE_PRIVATE) {
+ if (debug)
+ printf("Packet received with mode %d\n",
+ INFO_MODE(rpkt.rm_vn_mode));
+ goto again;
+ }
+ if (INFO_IS_AUTH(rpkt.auth_seq)) {
+ if (debug)
+ printf("Encrypted packet received\n");
+ goto again;
+ }
+ if (!ISRESPONSE(rpkt.rm_vn_mode)) {
+ if (debug)
+ printf("Received request packet, wanted response\n");
+ goto again;
+ }
+ if (INFO_MBZ(rpkt.mbz_itemsize) != 0) {
+ if (debug)
+ printf("Received packet with nonzero MBZ field!\n");
+ goto again;
+ }
+
+ /*
+ * Check implementation/request. Could be old data getting to us.
+ */
+ if (rpkt.implementation != implcode || rpkt.request != reqcode) {
+ if (debug)
+ printf(
+ "Received implementation/request of %d/%d, wanted %d/%d",
+ rpkt.implementation, rpkt.request,
+ implcode, reqcode);
+ goto again;
+ }
+
+ /*
+ * Check the error code. If non-zero, return it.
+ */
+ if (INFO_ERR(rpkt.err_nitems) != INFO_OKAY) {
+ if (debug && ISMORE(rpkt.rm_vn_mode)) {
+ printf("Error code %d received on not-final packet\n",
+ INFO_ERR(rpkt.err_nitems));
+ }
+ return (int)INFO_ERR(rpkt.err_nitems);
+ }
+
+
+ /*
+ * Collect items and size. Make sure they make sense.
+ */
+ items = INFO_NITEMS(rpkt.err_nitems);
+ size = INFO_ITEMSIZE(rpkt.mbz_itemsize);
+
+ if ((datasize = items*size) > (n-RESP_HEADER_SIZE)) {
+ if (debug)
+ printf(
+ "Received items %d, size %d (total %d), data in packet is %d\n",
+ items, size, datasize, n-RESP_HEADER_SIZE);
+ goto again;
+ }
+
+ /*
+ * If this isn't our first packet, make sure the size matches
+ * the other ones.
+ */
+ if (!firstpkt && size != *rsize) {
+ if (debug)
+ printf("Received itemsize %d, previous %d\n",
+ size, *rsize);
+ goto again;
+ }
+
+ /*
+ * If we've received this before, toss it
+ */
+ seq = INFO_SEQ(rpkt.auth_seq);
+ if (haveseq[seq]) {
+ if (debug)
+ printf("Received duplicate sequence number %d\n", seq);
+ goto again;
+ }
+ haveseq[seq] = 1;
+
+ /*
+ * If this is the last in the sequence, record that.
+ */
+ if (!ISMORE(rpkt.rm_vn_mode)) {
+ if (lastseq != 999) {
+ printf("Received second end sequence packet\n");
+ goto again;
+ }
+ lastseq = seq;
+ }
+
+ /*
+ * So far, so good. Copy this data into the output array.
+ */
+ if ((datap + datasize) > (pktdata + pktdatasize)) {
+ int offset = datap - pktdata;
+ growpktdata();
+ *rdata = pktdata; /* might have been realloced ! */
+ datap = pktdata + offset;
+ }
+ memmove(datap, (char *)rpkt.data, datasize);
+ datap += datasize;
+ if (firstpkt) {
+ firstpkt = 0;
+ *rsize = size;
+ }
+ *ritems += items;
+
+ /*
+ * Finally, check the count of received packets. If we've got them
+ * all, return
+ */
+ ++numrecv;
+ if (numrecv <= lastseq)
+ goto again;
+ return INFO_OKAY;
+}
+
+
+/*
+ * sendrequest - format and send a request packet
+ */
+static int
+sendrequest(implcode, reqcode, auth, qitems, qsize, qdata)
+ int implcode;
+ int reqcode;
+ int auth;
+ int qitems;
+ int qsize;
+ char *qdata;
+{
+ struct req_pkt qpkt;
+ int datasize;
+
+ memset((char *)&qpkt, 0, sizeof qpkt);
+
+ qpkt.rm_vn_mode = RM_VN_MODE(0, 0);
+ qpkt.implementation = (u_char)implcode;
+ qpkt.request = (u_char)reqcode;
+
+ datasize = qitems * qsize;
+ if (datasize != 0 && qdata != NULL) {
+ memmove((char *)qpkt.data, qdata, datasize);
+ qpkt.err_nitems = ERR_NITEMS(0, qitems);
+ qpkt.mbz_itemsize = MBZ_ITEMSIZE(qsize);
+ } else {
+ qpkt.err_nitems = ERR_NITEMS(0, 0);
+ qpkt.mbz_itemsize = MBZ_ITEMSIZE(0);
+ }
+
+ if (!auth) {
+ qpkt.auth_seq = AUTH_SEQ(0, 0);
+ return sendpkt((char *)&qpkt, REQ_LEN_NOMAC);
+ } else {
+ l_fp ts;
+ char *pass;
+
+ if (info_auth_keyid == 0) {
+ info_auth_keyid = getkeyid("Keyid: ");
+ if (info_auth_keyid == 0) {
+ (void) fprintf(stderr,
+ "Keyid must be defined, request not sent\n");
+ return 1;
+ }
+ }
+ if (!auth_havekey(info_auth_keyid)) {
+ pass = getpass("Password: ");
+ if (*pass != '\0')
+ authusekey(info_auth_keyid, info_auth_keytype,
+ pass);
+ }
+ if (auth_havekey(info_auth_keyid)) {
+ int maclen;
+
+ qpkt.auth_seq = AUTH_SEQ(1, 0);
+ qpkt.keyid = htonl(info_auth_keyid);
+ auth1crypt(info_auth_keyid, (U_LONG *)&qpkt,
+ REQ_LEN_NOMAC);
+ gettstamp(&ts);
+ L_ADD(&ts, &delay_time);
+ HTONL_FP(&ts, &qpkt.tstamp);
+ maclen = auth2crypt(info_auth_keyid, (U_LONG *)&qpkt,
+ REQ_LEN_NOMAC);
+ return sendpkt((char *)&qpkt, REQ_LEN_NOMAC+maclen);
+ } else {
+ (void) fprintf(stderr,
+ "No password, request not sent\n");
+ return 1;
+ }
+ }
+ /*NOTREACHED*/
+}
+
+
+/*
+ * doquery - send a request and process the response
+ */
+int
+doquery(implcode, reqcode, auth, qitems, qsize, qdata, ritems, rsize, rdata,
+ quiet_mask)
+ int implcode;
+ int reqcode;
+ int auth;
+ int qitems;
+ int qsize;
+ char *qdata;
+ int *ritems;
+ int *rsize;
+ char **rdata;
+ int quiet_mask;
+{
+ int res;
+ char junk[512];
+ fd_set fds;
+ struct timeval tvzero;
+
+ /*
+ * Check to make sure host is open
+ */
+ if (!havehost) {
+ (void) fprintf(stderr, "***No host open, use `host' command\n");
+ return -1;
+ }
+
+ /*
+ * Poll the socket and clear out any pending data
+ */
+ do {
+ tvzero.tv_sec = tvzero.tv_usec = 0;
+ FD_ZERO(&fds);
+ FD_SET(sockfd, &fds);
+ res = select(sockfd+1, &fds, (fd_set *)0, (fd_set *)0, &tvzero);
+
+ if (res == -1) {
+ warning("polling select", "", "");
+ return -1;
+ } else if (res > 0)
+ (void) read(sockfd, junk, sizeof junk);
+ } while (res > 0);
+
+
+ /*
+ * send a request
+ */
+ res = sendrequest(implcode, reqcode, auth, qitems, qsize, qdata);
+ if (res != 0)
+ return res;
+
+ /*
+ * Get the response. If we got a standard error, print a message
+ */
+ res = getresponse(implcode, reqcode, ritems, rsize, rdata);
+
+ /* log error message if not told to be quiet */
+ if ((res > 0) && (((1 << res) & quiet_mask) == 0)) {
+ switch(res) {
+ case INFO_ERR_IMPL:
+ (void) fprintf(stderr,
+ "***Server implementation incompatable with our own\n");
+ break;
+ case INFO_ERR_REQ:
+ (void) fprintf(stderr,
+ "***Server doesn't implement this request\n");
+ break;
+ case INFO_ERR_FMT:
+ (void) fprintf(stderr,
+"***Server reports a format error in the received packet (shouldn't happen)\n");
+ break;
+ case INFO_ERR_NODATA:
+ (void) fprintf(stderr,
+ "***Server reports data not found\n");
+ break;
+ case INFO_ERR_AUTH:
+ (void) fprintf(stderr, "***Permission denied\n");
+ break;
+ case ERR_TIMEOUT:
+ (void) fprintf(stderr, "***Request timed out\n");
+ break;
+ case ERR_INCOMPLETE:
+ (void) fprintf(stderr,
+ "***Response from server was incomplete\n");
+ break;
+ default:
+ (void) fprintf(stderr,
+ "***Server returns unknown error code %d\n", res);
+ break;
+ }
+ }
+ return res;
+}
+
+
+/*
+ * getcmds - read commands from the standard input and execute them
+ */
+static void
+getcmds()
+{
+ char line[MAXLINE];
+
+ for (;;) {
+ if (interactive) {
+ (void) fputs(prompt, stderr);
+ (void) fflush(stderr);
+ }
+
+ if (fgets(line, sizeof line, stdin) == NULL)
+ return;
+
+ docmd(line);
+ }
+}
+
+
+/*
+ * abortcmd - catch interrupts and abort the current command
+ */
+static RETSIGTYPE
+abortcmd(sig)
+int sig;
+{
+ if (current_output == stdout)
+ (void) fflush(stdout);
+ putc('\n', stderr);
+ (void) fflush(stderr);
+ if (jump) longjmp(interrupt_buf, 1);
+}
+
+
+/*
+ * docmd - decode the command line and execute a command
+ */
+static void
+docmd(cmdline)
+ char *cmdline;
+{
+ char *tokens[1+MAXARGS+2];
+ struct parse pcmd;
+ int ntok;
+ static int i;
+ struct xcmd *xcmd;
+
+ /*
+ * Tokenize the command line. If nothing on it, return.
+ */
+ tokenize(cmdline, tokens, &ntok);
+ if (ntok == 0)
+ return;
+
+ /*
+ * Find the appropriate command description.
+ */
+ i = findcmd(tokens[0], builtins, opcmds, &xcmd);
+ if (i == 0) {
+ (void) fprintf(stderr, "***Command `%s' unknown\n",
+ tokens[0]);
+ return;
+ } else if (i >= 2) {
+ (void) fprintf(stderr, "***Command `%s' ambiguous\n",
+ tokens[0]);
+ return;
+ }
+
+ /*
+ * Save the keyword, then walk through the arguments, interpreting
+ * as we go.
+ */
+ pcmd.keyword = tokens[0];
+ pcmd.nargs = 0;
+ for (i = 0; i < MAXARGS && xcmd->arg[i] != NO; i++) {
+ if ((i+1) >= ntok) {
+ if (!(xcmd->arg[i] & OPT)) {
+ printusage(xcmd, stderr);
+ return;
+ }
+ break;
+ }
+ if ((xcmd->arg[i] & OPT) && (*tokens[i+1] == '>'))
+ break;
+ if (!getarg(tokens[i+1], (int)xcmd->arg[i], &pcmd.argval[i]))
+ return;
+ pcmd.nargs++;
+ }
+
+ i++;
+ if (i < ntok && *tokens[i] == '>') {
+ char *fname;
+
+ if (*(tokens[i]+1) != '\0')
+ fname = tokens[i]+1;
+ else if ((i+1) < ntok)
+ fname = tokens[i+1];
+ else {
+ (void) fprintf(stderr, "***No file for redirect\n");
+ return;
+ }
+
+ current_output = fopen(fname, "w");
+ if (current_output == NULL) {
+ (void) fprintf(stderr, "***Error opening %s: ", fname);
+ perror("");
+ return;
+ }
+ i = 1; /* flag we need a close */
+ } else {
+ current_output = stdout;
+ i = 0; /* flag no close */
+ }
+
+ if (interactive && setjmp(interrupt_buf)) {
+ return;
+ } else {
+ jump = 1;
+ (xcmd->handler)(&pcmd, current_output);
+ if (i) (void) fclose(current_output);
+ }
+}
+
+
+/*
+ * tokenize - turn a command line into tokens
+ */
+static void
+tokenize(line, tokens, ntok)
+ char *line;
+ char **tokens;
+ int *ntok;
+{
+ register char *cp;
+ register char *sp;
+ static char tspace[MAXLINE];
+
+ sp = tspace;
+ cp = line;
+ for (*ntok = 0; *ntok < MAXTOKENS; (*ntok)++) {
+ tokens[*ntok] = sp;
+ while (ISSPACE(*cp))
+ cp++;
+ if (ISEOL(*cp))
+ break;
+ do {
+ *sp++ = *cp++;
+ } while (!ISSPACE(*cp) && !ISEOL(*cp));
+
+ *sp++ = '\0';
+ }
+}
+
+
+
+/*
+ * findcmd - find a command in a command description table
+ */
+static int
+findcmd(str, clist1, clist2, cmd)
+ register char *str;
+ struct xcmd *clist1;
+ struct xcmd *clist2;
+ struct xcmd **cmd;
+{
+ register struct xcmd *cl;
+ register int clen;
+ int nmatch;
+ struct xcmd *nearmatch = NULL;
+ struct xcmd *clist;
+
+ clen = strlen(str);
+ nmatch = 0;
+ if (clist1 != 0)
+ clist = clist1;
+ else if (clist2 != 0)
+ clist = clist2;
+ else
+ return 0;
+
+again:
+ for (cl = clist; cl->keyword != 0; cl++) {
+ /* do a first character check, for efficiency */
+ if (*str != *(cl->keyword))
+ continue;
+ if (strncmp(str, cl->keyword, clen) == 0) {
+ /*
+ * Could be extact match, could be approximate.
+ * Is exact if the length of the keyword is the
+ * same as the str.
+ */
+ if (*((cl->keyword) + clen) == '\0') {
+ *cmd = cl;
+ return 1;
+ }
+ nmatch++;
+ nearmatch = cl;
+ }
+ }
+
+ /*
+ * See if there is more to do. If so, go again. Sorry about the
+ * goto, too much looking at BSD sources...
+ */
+ if (clist == clist1 && clist2 != 0) {
+ clist = clist2;
+ goto again;
+ }
+
+ /*
+ * If we got extactly 1 near match, use it, else return number
+ * of matches.
+ */
+ if (nmatch == 1) {
+ *cmd = nearmatch;
+ return 1;
+ }
+ return nmatch;
+}
+
+
+/*
+ * getarg - interpret an argument token
+ */
+static int
+getarg(str, code, argp)
+ char *str;
+ int code;
+ arg_v *argp;
+{
+ int isneg;
+ char *cp, *np;
+ static char *digits = "0123456789";
+
+ switch (code & ~OPT) {
+ case NTP_STR:
+ argp->string = str;
+ break;
+ case ADD:
+ if (!getnetnum(str, &(argp->netnum), (char *)0)) {
+ return 0;
+ }
+ break;
+ case INT:
+ case UINT:
+ isneg = 0;
+ np = str;
+ if (*np == '-') {
+ np++;
+ isneg = 1;
+ }
+
+ argp->uval = 0;
+ do {
+ cp = strchr(digits, *np);
+ if (cp == NULL) {
+ (void) fprintf(stderr,
+ "***Illegal integer value %s\n", str);
+ return 0;
+ }
+ argp->uval *= 10;
+ argp->uval += (cp - digits);
+ } while (*(++np) != '\0');
+
+ if (isneg) {
+ if ((code & ~OPT) == UINT) {
+ (void) fprintf(stderr,
+ "***Value %s should be unsigned\n", str);
+ return 0;
+ }
+ argp->ival = -argp->ival;
+ }
+ break;
+ }
+
+ return 1;
+}
+
+
+/*
+ * getnetnum - given a host name, return its net number
+ * and (optional) full name
+ */
+static int
+getnetnum(host, num, fullhost)
+ char *host;
+ u_long *num;
+ char *fullhost;
+{
+ struct hostent *hp;
+
+ if (decodenetnum(host, num)) {
+ if (fullhost != 0) {
+ (void) sprintf(fullhost,
+ "%u.%u.%u.%u", (u_int)((htonl(*num)>>24)&0xff),
+ (u_int)((htonl(*num)>>16)&0xff), (u_int)((htonl(*num)>>8)&0xff),
+ (u_int)(htonl(*num)&0xff));
+ }
+ return 1;
+ } else if ((hp = gethostbyname(host)) != 0) {
+ memmove((char *)num, hp->h_addr, sizeof(u_long));
+ if (fullhost != 0)
+ (void) strcpy(fullhost, hp->h_name);
+ return 1;
+ } else {
+ (void) fprintf(stderr, "***Can't find host %s\n", host);
+ return 0;
+ }
+ /*NOTREACHED*/
+}
+
+/*
+ * nntohost - convert network number to host name. This routine enforces
+ * the showhostnames setting.
+ */
+char *
+nntohost(netnum)
+ u_long netnum;
+{
+ if (!showhostnames)
+ return numtoa(netnum);
+ if ((ntohl(netnum) & REFCLOCK_MASK) == REFCLOCK_ADDR)
+ return refnumtoa(netnum);
+ return numtohost(netnum);
+}
+
+
+/*
+ * Finally, the built in command handlers
+ */
+
+/*
+ * help - tell about commands, or details of a particular command
+ */
+static void
+help(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ int i;
+ int n;
+ struct xcmd *xcp;
+ char *cmd;
+ char *cmdsort[100];
+ int length[100];
+ int maxlength;
+ int numperline;
+ static char *spaces = " "; /* 20 spaces */
+
+ if (pcmd->nargs == 0) {
+ n = 0;
+ for (xcp = builtins; xcp->keyword != 0; xcp++) {
+ if (*(xcp->keyword) != '?')
+ cmdsort[n++] = xcp->keyword;
+ }
+ for (xcp = opcmds; xcp->keyword != 0; xcp++)
+ cmdsort[n++] = xcp->keyword;
+
+#if defined(sgi) || defined(SYS_BSDI) || defined(__STDC__)
+ qsort((void *)cmdsort, n, sizeof(char *), helpsort);
+#else
+ qsort((char *)cmdsort, n, sizeof(char *), helpsort);
+#endif /* sgi */
+
+ maxlength = 0;
+ for (i = 0; i < n; i++) {
+ length[i] = strlen(cmdsort[i]);
+ if (length[i] > maxlength)
+ maxlength = length[i];
+ }
+ maxlength++;
+ numperline = 76 / maxlength;
+
+ (void) fprintf(fp, "Commands available:\n");
+ for (i = 0; i < n; i++) {
+ if ((i % numperline) == (numperline-1)
+ || i == (n-1))
+ (void) fprintf(fp, "%s\n", cmdsort[i]);
+ else
+ (void) fprintf(fp, "%s%s", cmdsort[i],
+ spaces+20-maxlength+length[i]);
+ }
+ } else {
+ cmd = pcmd->argval[0].string;
+ n = findcmd(cmd, builtins, opcmds, &xcp);
+ if (n == 0) {
+ (void) fprintf(stderr,
+ "Command `%s' is unknown\n", cmd);
+ return;
+ } else if (n >= 2) {
+ (void) fprintf(stderr,
+ "Command `%s' is ambiguous\n", cmd);
+ return;
+ }
+ (void) fprintf(fp, "function: %s\n", xcp->comment);
+ printusage(xcp, fp);
+ }
+}
+
+
+/*
+ * helpsort - do hostname qsort comparisons
+ */
+static int
+#if defined(sgi) || defined(SYS_BSDI) || defined(__STDC__)
+helpsort(t1, t2)
+ const void *t1;
+ const void *t2;
+{
+ const char **name1 = (const char **)t1;
+ const char **name2 = (const char **)t2;
+#else
+helpsort(name1, name2)
+ char **name1;
+ char **name2;
+{
+#endif /* sgi || bsdi */
+ return strcmp(*name1, *name2);
+}
+
+
+/*
+ * printusage - print usage information for a command
+ */
+static void
+printusage(xcp, fp)
+ struct xcmd *xcp;
+ FILE *fp;
+{
+ register int i;
+
+ (void) fprintf(fp, "usage: %s", xcp->keyword);
+ for (i = 0; i < MAXARGS && xcp->arg[i] != NO; i++) {
+ if (xcp->arg[i] & OPT)
+ (void) fprintf(fp, " [ %s ]", xcp->desc[i]);
+ else
+ (void) fprintf(fp, " %s", xcp->desc[i]);
+ }
+ (void) fprintf(fp, "\n");
+}
+
+
+/*
+ * timeout - set time out time
+ */
+static void
+timeout(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ int val;
+
+ if (pcmd->nargs == 0) {
+ val = tvout.tv_sec * 1000 + tvout.tv_usec / 1000;
+ (void) fprintf(fp, "primary timeout %d ms\n", val);
+ } else {
+ tvout.tv_sec = pcmd->argval[0].uval / 1000;
+ tvout.tv_usec = (pcmd->argval[0].uval - (tvout.tv_sec * 1000))
+ * 1000;
+ }
+}
+
+
+/*
+ * delay - set delay for auth requests
+ */
+static void
+delay(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ int isneg;
+ u_long val;
+
+ if (pcmd->nargs == 0) {
+ val = delay_time.l_ui * 1000 + delay_time.l_uf / 4294967;
+ (void) fprintf(fp, "delay %lu ms\n", val);
+ } else {
+ if (pcmd->argval[0].ival < 0) {
+ isneg = 1;
+ val = (u_long)(-pcmd->argval[0].ival);
+ } else {
+ isneg = 0;
+ val = (u_long)pcmd->argval[0].ival;
+ }
+
+ delay_time.l_ui = val / 1000;
+ val %= 1000;
+ delay_time.l_uf = val * 4294967; /* 2**32/1000 */
+
+ if (isneg)
+ L_NEG(&delay_time);
+ }
+}
+
+
+/*
+ * host - set the host we are dealing with.
+ */
+static void
+host(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ if (pcmd->nargs == 0) {
+ if (havehost)
+ (void) fprintf(fp, "current host is %s\n", currenthost);
+ else
+ (void) fprintf(fp, "no current host\n");
+ } else if (openhost(pcmd->argval[0].string)) {
+ (void) fprintf(fp, "current host set to %s\n", currenthost);
+ } else {
+ if (havehost)
+ (void) fprintf(fp,
+ "current host remains %s\n", currenthost);
+ else
+ (void) fprintf(fp, "still no current host\n");
+ }
+}
+
+
+/*
+ * keyid - get a keyid to use for authenticating requests
+ */
+static void
+keyid(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ if (pcmd->nargs == 0) {
+ if (info_auth_keyid == 0)
+ (void) fprintf(fp, "no keyid defined\n");
+ else
+ (void) fprintf(fp, "keyid is %lu\n", info_auth_keyid);
+ } else {
+ info_auth_keyid = pcmd->argval[0].uval;
+ }
+}
+
+
+/*
+ * keytype - get type of key to use for authenticating requests
+ */
+static void
+keytype(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ if (pcmd->nargs == 0)
+ fprintf(fp, "keytype is %s",
+ (info_auth_keytype == KEY_TYPE_MD5) ? "md5" : "des");
+ else
+ switch (*(pcmd->argval[0].string)) {
+ case 'm':
+ case 'M':
+ info_auth_keytype = KEY_TYPE_MD5;
+ break;
+
+ case 'd':
+ case 'D':
+ info_auth_keytype = KEY_TYPE_DES;
+ break;
+
+ default:
+ fprintf(fp, "keytype must be 'md5' or 'des'\n");
+ }
+}
+
+
+
+/*
+ * passwd - get an authentication key
+ */
+/*ARGSUSED*/
+static void
+passwd(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ char *pass;
+
+ if (info_auth_keyid == 0) {
+ info_auth_keyid = getkeyid("Keyid: ");
+ if (info_auth_keyid == 0) {
+ (void)fprintf(fp, "Keyid must be defined\n");
+ return;
+ }
+ }
+ if (!interactive) {
+ authusekey(info_auth_keyid, info_auth_keytype,
+ pcmd->argval[0].string);
+ } else {
+ pass = getpass("Password: ");
+ if (*pass == '\0')
+ (void) fprintf(fp, "Password unchanged\n");
+ else
+ authusekey(info_auth_keyid, info_auth_keytype, pass);
+ }
+}
+
+
+/*
+ * hostnames - set the showhostnames flag
+ */
+static void
+hostnames(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ if (pcmd->nargs == 0) {
+ if (showhostnames)
+ (void) fprintf(fp, "hostnames being shown\n");
+ else
+ (void) fprintf(fp, "hostnames not being shown\n");
+ } else {
+ if (STREQ(pcmd->argval[0].string, "yes"))
+ showhostnames = 1;
+ else if (STREQ(pcmd->argval[0].string, "no"))
+ showhostnames = 0;
+ else
+ (void)fprintf(stderr, "What?\n");
+ }
+}
+
+
+/*
+ * setdebug - set/change debugging level
+ */
+static void
+setdebug(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ if (pcmd->nargs == 0) {
+ (void) fprintf(fp, "debug level is %d\n", debug);
+ return;
+ } else if (STREQ(pcmd->argval[0].string, "no")) {
+ debug = 0;
+ } else if (STREQ(pcmd->argval[0].string, "more")) {
+ debug++;
+ } else if (STREQ(pcmd->argval[0].string, "less")) {
+ debug--;
+ } else {
+ (void) fprintf(fp, "What?\n");
+ return;
+ }
+ (void) fprintf(fp, "debug level set to %d\n", debug);
+}
+
+
+/*
+ * quit - stop this nonsense
+ */
+/*ARGSUSED*/
+static void
+quit(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ if (havehost)
+ (void) close(sockfd); /* cleanliness next to godliness */
+ exit(0);
+}
+
+
+/*
+ * version - print the current version number
+ */
+/*ARGSUSED*/
+static void
+version(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ extern char *Version;
+
+ (void) fprintf(fp, "%s\n", Version);
+}
+
+
+/*
+ * warning - print a warning message
+ */
+static void
+warning(fmt, st1, st2)
+ char *fmt;
+ char *st1;
+ char *st2;
+{
+ (void) fprintf(stderr, "%s: ", progname);
+ (void) fprintf(stderr, fmt, st1, st2);
+ (void) fprintf(stderr, ": ");
+ perror("");
+}
+
+
+/*
+ * error - print a message and exit
+ */
+static void
+error(fmt, st1, st2)
+ char *fmt;
+ char *st1;
+ char *st2;
+{
+ warning(fmt, st1, st2);
+ exit(1);
+}
+
+/*
+ * getkeyid - prompt the user for a keyid to use
+ */
+static u_long
+getkeyid(prompt)
+char *prompt;
+{
+ register char *p;
+ register c;
+ FILE *fi;
+ char pbuf[20];
+
+ if ((fi = fdopen(open("/dev/tty", 2), "r")) == NULL)
+ fi = stdin;
+ else
+ setbuf(fi, (char *)NULL);
+ fprintf(stderr, "%s", prompt); fflush(stderr);
+ for (p=pbuf; (c = getc(fi))!='\n' && c!=EOF;) {
+ if (p < &pbuf[18])
+ *p++ = c;
+ }
+ *p = '\0';
+ if (fi != stdin)
+ fclose(fi);
+ return (u_long)atoi(pbuf);
+}
diff --git a/usr.sbin/xntpd/xntpdc/ntpdc.h b/usr.sbin/xntpd/xntpdc/ntpdc.h
new file mode 100644
index 0000000..bb23024
--- /dev/null
+++ b/usr.sbin/xntpd/xntpdc/ntpdc.h
@@ -0,0 +1,59 @@
+/*
+ * ntpdc.h - definitions of interest to xntpdc
+ */
+#include "ntp_fp.h"
+#include "ntp.h"
+#include "ntp_request.h"
+#include "ntp_string.h"
+#include "ntp_malloc.h"
+
+/*
+ * Maximum number of arguments
+ */
+#define MAXARGS 4
+
+/*
+ * Flags for forming descriptors.
+ */
+#define OPT 0x80 /* this argument is optional, or'd with type */
+
+#define NO 0x0
+#define NTP_STR 0x1 /* string argument */
+#define UINT 0x2 /* unsigned integer */
+#define INT 0x3 /* signed integer */
+#define ADD 0x4 /* IP network address */
+
+/*
+ * Arguments are returned in a union
+ */
+typedef union {
+ char *string;
+ long ival;
+ u_long uval;
+ u_long netnum;
+} arg_v;
+
+/*
+ * Structure for passing parsed command line
+ */
+struct parse {
+ char *keyword;
+ arg_v argval[MAXARGS];
+ int nargs;
+};
+
+/*
+ * xntpdc includes a command parser which could charitably be called
+ * crude. The following structure is used to define the command
+ * syntax.
+ */
+struct xcmd {
+ char *keyword; /* command key word */
+ void (*handler) P((struct parse *, FILE *)); /* command handler */
+ u_char arg[MAXARGS]; /* descriptors for arguments */
+ char *desc[MAXARGS]; /* descriptions for arguments */
+ char *comment;
+};
+
+extern int doquery P((int, int, int, int, int, char *, int *, int *, char **, int));
+extern char * nntohost P((u_long));
diff --git a/usr.sbin/xntpd/xntpdc/ntpdc_ops.c b/usr.sbin/xntpd/xntpdc/ntpdc_ops.c
new file mode 100644
index 0000000..7010f9b
--- /dev/null
+++ b/usr.sbin/xntpd/xntpdc/ntpdc_ops.c
@@ -0,0 +1,2441 @@
+/*
+ * ntpdc_ops.c - subroutines which are called to perform operations by xntpdc
+ */
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <netdb.h>
+#ifndef __bsdi__
+#include <netinet/in.h>
+#endif
+#include <arpa/inet.h>
+
+#include "ntpdc.h"
+#include "ntp_control.h"
+#include "ntp_refclock.h"
+#include "ntp_stdlib.h"
+
+/*
+ * Declarations for command handlers in here
+ */
+static int checkitems P((int, FILE *));
+static int checkitemsize P((int, int));
+static int check1item P((int, FILE *));
+static void peerlist P((struct parse *, FILE *));
+static void peers P((struct parse *, FILE *));
+static void dmpeers P((struct parse *, FILE *));
+static void dopeers P((struct parse *, FILE *, int));
+static void printpeer P((struct info_peer *, FILE *));
+static void showpeer P((struct parse *, FILE *));
+static void peerstats P((struct parse *, FILE *));
+static void loopinfo P((struct parse *, FILE *));
+static void sysinfo P((struct parse *, FILE *));
+static void sysstats P((struct parse *, FILE *));
+static void iostats P((struct parse *, FILE *));
+static void memstats P((struct parse *, FILE *));
+static void timerstats P((struct parse *, FILE *));
+static void addpeer P((struct parse *, FILE *));
+static void addserver P((struct parse *, FILE *));
+static void broadcast P((struct parse *, FILE *));
+static void doconfig P((struct parse *, FILE *, int));
+static void unconfig P((struct parse *, FILE *));
+static void set P((struct parse *, FILE *));
+static void sys_clear P((struct parse *, FILE *));
+static void doset P((struct parse *, FILE *, int));
+static void reslist P((struct parse *, FILE *));
+static void restrict P((struct parse *, FILE *));
+static void unrestrict P((struct parse *, FILE *));
+static void delrestrict P((struct parse *, FILE *));
+static void do_restrict P((struct parse *, FILE *, int));
+static void monlist P((struct parse *, FILE *));
+static void monitor P((struct parse *, FILE *));
+static void reset P((struct parse *, FILE *));
+static void preset P((struct parse *, FILE *));
+static void readkeys P((struct parse *, FILE *));
+static void trustkey P((struct parse *, FILE *));
+static void untrustkey P((struct parse *, FILE *));
+static void do_trustkey P((struct parse *, FILE *, int));
+static void authinfo P((struct parse *, FILE *));
+static void traps P((struct parse *, FILE *));
+static void addtrap P((struct parse *, FILE *));
+static void clrtrap P((struct parse *, FILE *));
+static void do_addclr_trap P((struct parse *, FILE *, int));
+static void requestkey P((struct parse *, FILE *));
+static void controlkey P((struct parse *, FILE *));
+static void do_changekey P((struct parse *, FILE *, int));
+static void ctlstats P((struct parse *, FILE *));
+static void leapinfo P((struct parse *, FILE *));
+static void clockstat P((struct parse *, FILE *));
+static void fudge P((struct parse *, FILE *));
+static void clkbug P((struct parse *, FILE *));
+static void setprecision P((struct parse *, FILE *));
+static void kerninfo P((struct parse *, FILE *));
+
+/*
+ * Commands we understand. Ntpdc imports this.
+ */
+struct xcmd opcmds[] = {
+ { "listpeers", peerlist, { NO, NO, NO, NO },
+ { "", "", "", "" },
+ "display list of peers the server knows about" },
+ { "peers", peers, { NO, NO, NO, NO },
+ { "", "", "", "" },
+ "display peer summary information" },
+ { "dmpeers", dmpeers, { NO, NO, NO, NO },
+ { "", "", "", "" },
+ "display peer summary info the way Dave Mills likes it" },
+ { "showpeer", showpeer, { ADD, OPT|ADD, OPT|ADD, OPT|ADD },
+ { "peer_address", "peer2_addr", "peer3_addr", "peer4_addr" },
+ "display detailed information for one or more peers" },
+ { "pstats", peerstats, { ADD, OPT|ADD, OPT|ADD, OPT|ADD },
+ { "peer_address", "peer2_addr", "peer3_addr", "peer4_addr" },
+ "display statistical information for one or more peers" },
+ { "loopinfo", loopinfo, { OPT|NTP_STR, NO, NO, NO },
+ { "oneline|multiline", "", "", "" },
+ "display loop filter information" },
+ { "sysinfo", sysinfo, { NO, NO, NO, NO },
+ { "", "", "", "" },
+ "display local server information" },
+ { "sysstats", sysstats, { NO, NO, NO, NO },
+ { "", "", "", "" },
+ "display local server statistics" },
+ { "memstats", memstats, { NO, NO, NO, NO },
+ { "", "", "", "" },
+ "display peer memory usage statistics" },
+ { "iostats", iostats, { NO, NO, NO, NO },
+ { "", "", "", "" },
+ "display I/O subsystem statistics" },
+ { "timerstats", timerstats, { NO, NO, NO, NO },
+ { "", "", "", "" },
+ "display event timer subsystem statistics" },
+ { "addpeer", addpeer, { ADD, OPT|UINT, OPT|UINT, OPT|NTP_STR },
+ { "addr", "keyid", "version", "minpoll|prefer" },
+ "configure a new peer association" },
+ { "addserver", addserver, { ADD, OPT|UINT, OPT|UINT, OPT|NTP_STR },
+ { "addr", "keyid", "version", "minpoll|prefer" },
+ "configure a new server" },
+ { "broadcast", broadcast, { ADD, OPT|UINT, OPT|UINT, OPT|NTP_STR },
+ { "addr", "keyid", "version", "minpoll" },
+ "configure broadcasting time service" },
+ { "unconfig", unconfig, { ADD, OPT|ADD, OPT|ADD, OPT|ADD },
+ { "peer_address", "peer2_addr", "peer3_addr", "peer4_addr" },
+ "unconfigure existing peer assocations" },
+ { "enable", set, { NTP_STR, OPT|NTP_STR, OPT|NTP_STR, OPT|NTP_STR },
+ { "auth|bclient|pll|pps|monitor|stats", "...", "...", "..." },
+ "set a system flag (auth, bclient, pll, pps, monitor, stats)" },
+ { "disable", sys_clear, { NTP_STR, OPT|NTP_STR, OPT|NTP_STR, OPT|NTP_STR },
+ { "auth|bclient|pll|pps|monitor|stats", "...", "...", "..." },
+ "clear a system flag (auth, bclient, pll, pps, monitor, stats)" },
+ { "reslist", reslist, { NO, NO, NO, NO },
+ { "", "", "", "" },
+ "display the server's restrict list" },
+ { "restrict", restrict, { ADD, ADD, NTP_STR, OPT|NTP_STR },
+ { "address", "mask",
+ "ntpport|ignore|noserve|notrust|noquery|nomodify|nopeer",
+ "..." },
+ "create restrict entry/add flags to entry" },
+ { "unrestrict", unrestrict, { ADD, ADD, NTP_STR, OPT|NTP_STR },
+ { "address", "mask",
+ "ntpport|ignore|noserve|notrust|noquery|nomodify|nopeer",
+ "..." },
+ "remove flags from a restrict entry" },
+ { "delrestrict", delrestrict, { ADD, ADD, OPT|NTP_STR, NO },
+ { "address", "mask", "ntpport", "" },
+ "delete a restrict entry" },
+ { "monlist", monlist, { OPT|INT, NO, NO, NO },
+ { "version", "", "", "" },
+ "display data the server's monitor routines have collected" },
+ { "monitor", monitor, { NTP_STR, NO, NO, NO },
+ { "on|off", "", "", "" },
+ "turn the server's monitoring facility on or off" },
+ { "reset", reset, { NTP_STR, OPT|NTP_STR, OPT|NTP_STR, OPT|NTP_STR },
+ { "io|sys|mem|timer|auth|allpeers", "...", "...", "..." },
+ "reset various subsystem statistics counters" },
+ { "preset", preset, { ADD, OPT|ADD, OPT|ADD, OPT|ADD },
+ { "peer_address", "peer2_addr", "peer3_addr", "peer4_addr" },
+ "reset stat counters associated with particular peer(s)" },
+ { "readkeys", readkeys, { NO, NO, NO, NO },
+ { "", "", "", "" },
+ "request a reread of the keys file and re-init of system keys" },
+ { "trustkey", trustkey, { UINT, OPT|UINT, OPT|UINT, OPT|UINT },
+ { "keyid", "keyid", "keyid", "keyid" },
+ "add one or more key ID's to the trusted list" },
+ { "untrustkey", untrustkey, { UINT, OPT|UINT, OPT|UINT, OPT|UINT },
+ { "keyid", "keyid", "keyid", "keyid" },
+ "remove one or more key ID's from the trusted list" },
+ { "authinfo", authinfo, { NO, NO, NO, NO },
+ { "", "", "", "" },
+ "display the state of the authentication code" },
+ { "traps", traps, { NO, NO, NO, NO },
+ { "", "", "", "" },
+ "display the traps set in the server" },
+ { "addtrap", addtrap, { ADD, OPT|UINT, OPT|ADD, NO },
+ { "address", "port", "interface", "" },
+ "configure a trap in the server" },
+ { "clrtrap", clrtrap, { ADD, OPT|UINT, OPT|ADD, NO },
+ { "address", "port", "interface", "" },
+ "remove a trap (configured or otherwise) from the server" },
+ { "requestkey", requestkey, { UINT, NO, NO, NO },
+ { "keyid", "", "", "" },
+ "change the keyid the server uses to authenticate requests" },
+ { "controlkey", controlkey, { UINT, NO, NO, NO },
+ { "keyid", "", "", "" },
+ "change the keyid the server uses to authenticate control messages" },
+ { "ctlstats", ctlstats, { NO, NO, NO, NO },
+ { "", "", "", "" },
+ "display packet count statistics from the control module" },
+ { "leapinfo", leapinfo, { NO, NO, NO, NO },
+ { "", "", "", "" },
+ "display the current leap second state" },
+ { "clockstat", clockstat, { ADD, OPT|ADD, OPT|ADD, OPT|ADD },
+ { "address", "address", "address", "address" },
+ "display clock status information" },
+ { "fudge", fudge, { ADD, NTP_STR, NTP_STR, NO },
+ { "address", "time1|time2|val1|val2|flags", "value", "" },
+ "set/change one of a clock's fudge factors" },
+ { "clkbug", clkbug, { ADD, OPT|ADD, OPT|ADD, OPT|ADD },
+ { "address", "address", "address", "address" },
+ "display clock debugging information" },
+ { "setprecision", setprecision, { INT, NO, NO, NO },
+ { "sys_precision", "", "", "" },
+ "set the server's advertised precision" },
+ { "kerninfo", kerninfo, { NO, NO, NO, NO },
+ { "", "", "", "" },
+ "display the kernel pll/pps variables" },
+
+ { 0, 0, { NO, NO, NO, NO },
+ { "", "", "", "" }, "" }
+};
+
+
+/*
+ * Imported from ntpdc.c
+ */
+extern int showhostnames;
+extern int debug;
+extern struct servent *server_entry;
+
+/*
+ * For quick string comparisons
+ */
+#define STREQ(a, b) (*(a) == *(b) && strcmp((a), (b)) == 0)
+
+
+/*
+ * checkitems - utility to print a message if no items were returned
+ */
+static int
+checkitems(items, fp)
+ int items;
+ FILE *fp;
+{
+ if (items == 0) {
+ (void) fprintf(fp, "No data returned in response to query\n");
+ return 0;
+ }
+ return 1;
+}
+
+
+/*
+ * checkitemsize - utility to print a message if the item size is wrong
+ */
+static int
+checkitemsize(itemsize, expected)
+ int itemsize;
+ int expected;
+{
+ if (itemsize != expected) {
+ (void) fprintf(stderr,
+ "***Incorrect item size returned by remote host (%d should be %d)\n",
+ itemsize, expected);
+ return 0;
+ }
+ return 1;
+}
+
+
+/*
+ * check1item - check to make sure we have exactly one item
+ */
+static int
+check1item(items, fp)
+ int items;
+ FILE *fp;
+{
+ if (items == 0) {
+ (void) fprintf(fp, "No data returned in response to query\n");
+ return 0;
+ }
+ if (items > 1) {
+ (void) fprintf(fp, "Expected one item in response, got %d\n",
+ items);
+ return 0;
+ }
+ return 1;
+}
+
+
+
+/*
+ * peerlist - get a short list of peers
+ */
+/*ARGSUSED*/
+static void
+peerlist(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ struct info_peer_list *plist;
+ int items;
+ int itemsize;
+ int res;
+
+ res = doquery(IMPL_XNTPD, REQ_PEER_LIST, 0, 0, 0, (char *)NULL, &items,
+ &itemsize, (char **)&plist, 0);
+
+ if (res != 0 && items == 0)
+ return;
+
+ if (!checkitems(items, fp))
+ return;
+
+ if (!checkitemsize(itemsize, sizeof(struct info_peer_list)))
+ return;
+
+ while (items > 0) {
+ (void) fprintf(fp, "%-9s %s\n", modetoa(plist->hmode),
+ nntohost(plist->address));
+ plist++;
+ items--;
+ }
+}
+
+
+/*
+ * peers - show peer summary
+ */
+static void
+peers(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ dopeers(pcmd, fp, 0);
+}
+
+/*
+ * dmpeers - show peer summary, Dave Mills style
+ */
+static void
+dmpeers(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ dopeers(pcmd, fp, 1);
+}
+
+
+/*
+ * peers - show peer summary
+ */
+/*ARGSUSED*/
+static void
+dopeers(pcmd, fp, dmstyle)
+ struct parse *pcmd;
+ FILE *fp;
+ int dmstyle;
+{
+ struct info_peer_summary *plist;
+ int items;
+ int itemsize;
+ int ntp_poll;
+ int res;
+ int c;
+ l_fp tempts;
+
+ res = doquery(IMPL_XNTPD, REQ_PEER_LIST_SUM, 0, 0, 0, (char *)NULL,
+ &items, &itemsize, (char **)&plist, 0);
+
+ if (res != 0 && items == 0)
+ return;
+
+ if (!checkitems(items, fp))
+ return;
+
+ if (!checkitemsize(itemsize, sizeof(struct info_peer_summary)))
+ return;
+
+ (void) fprintf(fp,
+ " remote local st poll reach delay offset disp\n");
+ (void) fprintf(fp,
+ "=======================================================================\n");
+ while (items > 0) {
+ if (!dmstyle) {
+ if (plist->flags & INFO_FLAG_SYSPEER)
+ c = '*';
+ else if (plist->hmode == MODE_ACTIVE)
+ c = '+';
+ else if (plist->hmode == MODE_PASSIVE)
+ c = '-';
+ else if (plist->hmode == MODE_CLIENT)
+ c = '=';
+ else if (plist->hmode == MODE_BROADCAST)
+ c = '^';
+ else if (plist->hmode == MODE_BCLIENT)
+ c = '~';
+ else
+ c = ' ';
+ } else {
+ if (plist->flags & INFO_FLAG_SYSPEER)
+ c = '*';
+ else if (plist->flags & INFO_FLAG_SHORTLIST)
+ c = '+';
+ else if (plist->flags & INFO_FLAG_SEL_CANDIDATE)
+ c = '.';
+ else
+ c = ' ';
+ }
+ NTOHL_FP(&(plist->offset), &tempts);
+ ntp_poll = 1<<max(min3(plist->ppoll, plist->hpoll, NTP_MAXPOLL),
+ NTP_MINPOLL);
+ (void) fprintf(fp,
+ "%c%-15.15s %-15.15s %2d %4d %3o %7.7s %9.9s %7.7s\n",
+ c, nntohost(plist->srcadr),
+ numtoa(plist->dstadr),
+ plist->stratum, ntp_poll, plist->reach,
+ fptoa(NTOHS_FP(plist->delay), 5),
+ lfptoa(&tempts, 6),
+ ufptoa(NTOHS_FP(plist->dispersion), 5));
+
+ plist++;
+ items--;
+ }
+}
+
+/* Convert a refid & stratum (in host order) to a string */
+static char*
+refid_string(refid, stratum)
+ u_long refid;
+ int stratum;
+{
+ if (stratum <= 1) {
+ static char junk[5];
+ junk[4] = 0;
+ memmove(junk, (char *)&refid, 4);
+ return junk;
+ }
+
+ return numtoa(refid);
+}
+
+/*
+ * printpeer - print detail information for a peer
+ */
+static void
+printpeer(pp, fp)
+ register struct info_peer *pp;
+ FILE *fp;
+{
+ register int i;
+ char *str;
+ l_fp tempts;
+
+ (void) fprintf(fp, "remote %s, local %s\n",
+ numtoa(pp->srcadr), numtoa(pp->dstadr));
+
+ (void) fprintf(fp, "hmode %s, pmode %s, stratum %d, precision %d\n",
+ modetoa(pp->hmode), modetoa(pp->pmode),
+ pp->stratum, pp->precision);
+
+ (void) fprintf(fp,
+ "leap %c%c, refid [%s], rootdistance %s, rootdispersion %s\n",
+ pp->leap & 0x2 ? '1' : '0',
+ pp->leap & 0x1 ? '1' : '0',
+ refid_string(pp->refid, pp->stratum), fptoa(NTOHS_FP(pp->rootdelay), 5),
+ ufptoa(NTOHS_FP(pp->rootdispersion), 5));
+
+ (void) fprintf(fp,
+ "ppoll %d, hpoll %d, keyid %lu, version %d, association %u\n",
+ pp->ppoll, pp->hpoll, (u_long)pp->keyid, pp->version, ntohs(pp->associd));
+
+ (void) fprintf(fp,
+ "valid %d, reach %03o, unreach %d, flash %03o, ",
+ pp->valid, pp->reach, pp->unreach, pp->flash);
+
+ (void) fprintf(fp, "boffset %s, ttl %d\n",
+ fptoa(NTOHS_FP(pp->estbdelay), 5), pp->ttl);
+
+ (void) fprintf(fp, "timer %lds, flags", (long)ntohl(pp->timer));
+ if (pp->flags == 0) {
+ (void) fprintf(fp, " none\n");
+ } else {
+ str = "";
+ if (pp->flags & INFO_FLAG_SYSPEER) {
+ (void) fprintf(fp, " system_peer");
+ str = ",";
+ }
+ if (pp->flags & INFO_FLAG_CONFIG) {
+ (void) fprintf(fp, "%s config", str);
+ str = ",";
+ }
+ if (pp->flags & INFO_FLAG_REFCLOCK) {
+ (void) fprintf(fp, "%s refclock", str);
+ str = ",";
+ }
+ if (pp->flags & INFO_FLAG_AUTHENABLE) {
+ (void) fprintf(fp, "%s auth", str);
+ str = ",";
+ }
+ if (pp->flags & INFO_FLAG_BCLIENT) {
+ (void) fprintf(fp, "%s bclient", str);
+ str = ",";
+ }
+ if (pp->flags & INFO_FLAG_PREFER) {
+ (void) fprintf(fp, "%s prefer", str);
+ }
+ (void) fprintf(fp, "\n");
+ }
+
+ NTOHL_FP(&pp->reftime, &tempts);
+ (void) fprintf(fp, "reference time: %s\n",
+ prettydate(&tempts));
+ NTOHL_FP(&pp->org, &tempts);
+ (void) fprintf(fp, "originate timestamp: %s\n",
+ prettydate(&tempts));
+ NTOHL_FP(&pp->rec, &tempts);
+ (void) fprintf(fp, "receive timestamp: %s\n",
+ prettydate(&tempts));
+ NTOHL_FP(&pp->xmt, &tempts);
+ (void) fprintf(fp, "transmit timestamp: %s\n",
+ prettydate(&tempts));
+
+ (void) fprintf(fp, "filter delay: ");
+ for (i = 0; i < NTP_SHIFT; i++) {
+ (void) fprintf(fp, " %-8.8s",
+ fptoa(NTOHS_FP(pp->filtdelay[i]), 5));
+ if (i == (NTP_SHIFT>>1)-1)
+ (void) fprintf(fp, "\n ");
+ }
+ (void) fprintf(fp, "\n");
+
+ (void) fprintf(fp, "filter offset:");
+ for (i = 0; i < NTP_SHIFT; i++) {
+ NTOHL_FP(&pp->filtoffset[i], &tempts);
+ (void) fprintf(fp, " %-8.8s", lfptoa(&tempts, 6));
+ if (i == (NTP_SHIFT>>1)-1)
+ (void) fprintf(fp, "\n ");
+ }
+ (void) fprintf(fp, "\n");
+
+ (void) fprintf(fp, "filter order: ");
+ for (i = 0; i < NTP_SHIFT; i++) {
+ (void) fprintf(fp, " %-8d", pp->order[i]);
+ if (i == (NTP_SHIFT>>1)-1)
+ (void) fprintf(fp, "\n ");
+ }
+ (void) fprintf(fp, "\n");
+
+
+ NTOHL_FP(&pp->offset, &tempts);
+ (void) fprintf(fp,
+ "offset %s, delay %s, dispersion %s, selectdisp %s\n",
+ lfptoa(&tempts, 6), fptoa(NTOHS_FP(pp->delay), 5),
+ ufptoa(NTOHS_FP(pp->dispersion), 5),
+ ufptoa(NTOHS_FP(pp->selectdisp), 5));
+}
+
+
+/*
+ * showpeer - show detailed information for a peer
+ */
+static void
+showpeer(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ struct info_peer *pp;
+ /* 4 is the maximum number of peers which will fit in a packet */
+ struct info_peer_list plist[min(MAXARGS, 4)];
+ int qitems;
+ int items;
+ int itemsize;
+ int res;
+
+ for (qitems = 0; qitems < min(pcmd->nargs, 4); qitems++) {
+ plist[qitems].address = pcmd->argval[qitems].netnum;
+ plist[qitems].port = server_entry->s_port;
+ plist[qitems].hmode = plist[qitems].flags = 0;
+ }
+
+ res = doquery(IMPL_XNTPD, REQ_PEER_INFO, 0, qitems,
+ sizeof(struct info_peer_list), (char *)plist, &items,
+ &itemsize, (char **)&pp, 0);
+
+ if (res != 0 && items == 0)
+ return;
+
+ if (!checkitems(items, fp))
+ return;
+
+ if (!checkitemsize(itemsize, sizeof(struct info_peer)))
+ return;
+
+ while (items-- > 0) {
+ printpeer(pp, fp);
+ if (items > 0)
+ (void) fprintf(fp, "\n");
+ pp++;
+ }
+}
+
+
+/*
+ * peerstats - return statistics for a peer
+ */
+static void
+peerstats(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ struct info_peer_stats *pp;
+ /* 4 is the maximum number of peers which will fit in a packet */
+ struct info_peer_list plist[min(MAXARGS, 4)];
+ int qitems;
+ int items;
+ int itemsize;
+ int res;
+
+ for (qitems = 0; qitems < min(pcmd->nargs, 4); qitems++) {
+ plist[qitems].address = pcmd->argval[qitems].netnum;
+ plist[qitems].port = server_entry->s_port;
+ plist[qitems].hmode = plist[qitems].flags = 0;
+ }
+
+ res = doquery(IMPL_XNTPD, REQ_PEER_STATS, 0, qitems,
+ sizeof(struct info_peer_list), (char *)plist, &items,
+ &itemsize, (char **)&pp, 0);
+
+ if (res != 0 && items == 0)
+ return;
+
+ if (!checkitems(items, fp))
+ return;
+
+ if (!checkitemsize(itemsize, sizeof(struct info_peer_stats)))
+ return;
+
+ while (items-- > 0) {
+ (void) fprintf(fp, "remote host: %s\n",
+ nntohost(pp->srcadr));
+ (void) fprintf(fp, "local interface: %s\n",
+ numtoa(pp->dstadr));
+ (void) fprintf(fp, "time last received: %lds\n",
+ (long)ntohl(pp->timereceived));
+ (void) fprintf(fp, "time until next send: %lds\n",
+ (long)ntohl(pp->timetosend));
+ (void) fprintf(fp, "reachability change: %lds\n",
+ (long)ntohl(pp->timereachable));
+ (void) fprintf(fp, "packets sent: %ld\n",
+ (long)ntohl(pp->sent));
+ (void) fprintf(fp, "packets received: %ld\n",
+ (long)ntohl(pp->processed));
+ (void) fprintf(fp, "bad authentication: %ld\n",
+ (long)ntohl(pp->badauth));
+ (void) fprintf(fp, "bogus origin: %ld\n",
+ (long)ntohl(pp->bogusorg));
+ (void) fprintf(fp, "duplicate: %ld\n",
+ (long)ntohl(pp->oldpkt));
+ (void) fprintf(fp, "bad dispersion: %ld\n",
+ (long)ntohl(pp->seldisp));
+ (void) fprintf(fp, "bad reference time: %ld\n",
+ (long)ntohl(pp->selbroken));
+ (void) fprintf(fp, "candidate order: %d\n",
+ (int)pp->candidate);
+ if (items > 0)
+ (void) fprintf(fp, "\n");
+ pp++;
+ }
+}
+
+
+/*
+ * loopinfo - show loop filter information
+ */
+static void
+loopinfo(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ struct info_loop *il;
+ int items;
+ int itemsize;
+ int oneline = 0;
+ int res;
+ l_fp tempts;
+
+ if (pcmd->nargs > 0) {
+ if (STREQ(pcmd->argval[0].string, "oneline"))
+ oneline = 1;
+ else if (STREQ(pcmd->argval[0].string, "multiline"))
+ oneline = 0;
+ else {
+ (void) fprintf(stderr, "How many lines?\n");
+ return;
+ }
+ }
+
+ res = doquery(IMPL_XNTPD, REQ_LOOP_INFO, 0, 0, 0, (char *)NULL,
+ &items, &itemsize, (char **)&il, 0);
+
+ if (res != 0 && items == 0)
+ return;
+
+ if (!check1item(items, fp))
+ return;
+
+ if (!checkitemsize(itemsize, sizeof(struct info_loop)))
+ return;
+
+ if (oneline) {
+ l_fp temp2ts;
+
+ NTOHL_FP(&il->last_offset, &tempts);
+ NTOHL_FP(&il->drift_comp, &temp2ts);
+
+ (void) fprintf(fp,
+ "offset %s, frequency %s, time_const %ld, watchdog %ld\n",
+ lfptoa(&tempts, 6),
+ lfptoa(&temp2ts, 3),
+ (u_long)ntohl(il->compliance),
+ (u_long)ntohl(il->watchdog_timer));
+ } else {
+ NTOHL_FP(&il->last_offset, &tempts);
+ (void) fprintf(fp, "offset: %s s\n",
+ lfptoa(&tempts, 6));
+ NTOHL_FP(&il->drift_comp, &tempts);
+ (void) fprintf(fp, "frequency: %s ppm\n",
+ lfptoa(&tempts, 3));
+ (void) fprintf(fp, "poll adjust: %ld\n",
+ (u_long)ntohl(il->compliance));
+ (void) fprintf(fp, "watchdog timer: %ld s\n",
+ (u_long)ntohl(il->watchdog_timer));
+ }
+}
+
+
+/*
+ * sysinfo - show current system state
+ */
+/*ARGSUSED*/
+static void
+sysinfo(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ struct info_sys *is;
+ int items;
+ int itemsize;
+ int res;
+ l_fp tempts;
+
+ res = doquery(IMPL_XNTPD, REQ_SYS_INFO, 0, 0, 0, (char *)NULL,
+ &items, &itemsize, (char **)&is, 0);
+
+ if (res != 0 && items == 0)
+ return;
+
+ if (!check1item(items, fp))
+ return;
+
+ if (!checkitemsize(itemsize, sizeof(struct info_sys)))
+ return;
+
+ (void) fprintf(fp, "system peer: %s\n", nntohost(is->peer));
+ (void) fprintf(fp, "system peer mode: %s\n", modetoa(is->peer_mode));
+ (void) fprintf(fp, "leap indicator: %c%c\n",
+ is->leap & 0x2 ? '1' : '0',
+ is->leap & 0x1 ? '1' : '0');
+ (void) fprintf(fp, "stratum: %d\n", (int)is->stratum);
+ (void) fprintf(fp, "precision: %d\n", (int)is->precision);
+ (void) fprintf(fp, "root distance: %s s\n",
+ fptoa(NTOHS_FP(is->rootdelay), 5));
+ (void) fprintf(fp, "root dispersion: %s s\n",
+ ufptoa(NTOHS_FP(is->rootdispersion), 5));
+ (void) fprintf(fp, "reference ID: [%s]\n",
+ refid_string(is->refid, is->stratum));
+ NTOHL_FP(&is->reftime, &tempts);
+ (void) fprintf(fp, "reference time: %s\n", prettydate(&tempts));
+
+ (void) fprintf(fp, "system flags: ");
+ if ((is->flags & (INFO_FLAG_BCLIENT | INFO_FLAG_AUTHENABLE |
+ INFO_FLAG_PLL | INFO_FLAG_PPS | INFO_FLAG_PLL_SYNC |
+ INFO_FLAG_PPS_SYNC | INFO_FLAG_MONITOR | INFO_FLAG_FILEGEN)) == 0) {
+ (void) fprintf(fp, "none\n");
+ } else {
+ if (is->flags & INFO_FLAG_BCLIENT)
+ (void) fprintf(fp, "bclient ");
+ if (is->flags & INFO_FLAG_AUTHENABLE)
+ (void) fprintf(fp, "auth ");
+ if (is->flags & INFO_FLAG_PLL)
+ (void) fprintf(fp, "pll ");
+ if (is->flags & INFO_FLAG_PPS)
+ (void) fprintf(fp, "pps ");
+ if (is->flags & INFO_FLAG_PLL_SYNC)
+ (void) fprintf(fp, "kernel_sync ");
+ if (is->flags & INFO_FLAG_PPS_SYNC)
+ (void) fprintf(fp, "pps_sync ");
+ if (is->flags & INFO_FLAG_MONITOR)
+ (void) fprintf(fp, "monitor ");
+ if (is->flags & INFO_FLAG_FILEGEN)
+ (void) fprintf(fp, "stats ");
+ (void) fprintf(fp, "\n");
+ }
+ (void) fprintf(fp, "frequency: %s ppm\n",
+ fptoa(ntohl(is->frequency), 3));
+ (void) fprintf(fp, "stability: %s ppm\n",
+ ufptoa(ntohl(is->stability), 3));
+ (void) fprintf(fp, "broadcastdelay: %s s\n",
+ fptoa(NTOHS_FP(is->bdelay), 6));
+ NTOHL_FP(&is->authdelay, &tempts);
+ (void) fprintf(fp, "authdelay: %s s\n", lfptoa(&tempts, 6));
+}
+
+
+/*
+ * sysstats - print system statistics
+ */
+/*ARGSUSED*/
+static void
+sysstats(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ struct info_sys_stats *ss;
+ int items;
+ int itemsize;
+ int res;
+
+ res = doquery(IMPL_XNTPD, REQ_SYS_STATS, 0, 0, 0, (char *)NULL,
+ &items, &itemsize, (char **)&ss, 0);
+
+ if (res != 0 && items == 0)
+ return;
+
+ if (!check1item(items, fp))
+ return;
+
+ if (itemsize != sizeof(struct info_sys_stats) &&
+ itemsize != sizeof(struct old_info_sys_stats)) {
+ /* issue warning according to new structure size */
+ checkitemsize(itemsize, sizeof(struct info_sys_stats));
+ return;
+ }
+
+ (void) fprintf(fp, "system uptime: %ld\n",
+ (u_long)ntohl(ss->timeup));
+ (void) fprintf(fp, "time since reset: %ld\n",
+ (u_long)ntohl(ss->timereset));
+ (void) fprintf(fp, "bad stratum in packet: %ld\n",
+ (u_long)ntohl(ss->badstratum));
+ (void) fprintf(fp, "old version packets: %ld\n",
+ (u_long)ntohl(ss->oldversionpkt));
+ (void) fprintf(fp, "new version packets: %ld\n",
+ (u_long)ntohl(ss->newversionpkt));
+ (void) fprintf(fp, "unknown version number: %ld\n",
+ (u_long)ntohl(ss->unknownversion));
+ (void) fprintf(fp, "bad packet length: %ld\n",
+ (u_long)ntohl(ss->badlength));
+ (void) fprintf(fp, "packets processed: %ld\n",
+ (u_long)ntohl(ss->processed));
+ (void) fprintf(fp, "bad authentication: %ld\n",
+ (u_long)ntohl(ss->badauth));
+ if (itemsize != sizeof(struct info_sys_stats))
+ return;
+
+ (void) fprintf(fp, "limitation rejects: %ld\n",
+ (u_long)ntohl(ss->limitrejected));
+}
+
+
+
+/*
+ * iostats - print I/O statistics
+ */
+/*ARGSUSED*/
+static void
+iostats(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ struct info_io_stats *io;
+ int items;
+ int itemsize;
+ int res;
+
+ res = doquery(IMPL_XNTPD, REQ_IO_STATS, 0, 0, 0, (char *)NULL,
+ &items, &itemsize, (char **)&io, 0);
+
+ if (res != 0 && items == 0)
+ return;
+
+ if (!check1item(items, fp))
+ return;
+
+ if (!checkitemsize(itemsize, sizeof(struct info_io_stats)))
+ return;
+
+ (void) fprintf(fp, "time since reset: %ld\n",
+ (u_long)ntohl(io->timereset));
+ (void) fprintf(fp, "receive buffers: %d\n",
+ ntohs(io->totalrecvbufs));
+ (void) fprintf(fp, "free receive buffers: %d\n",
+ ntohs(io->freerecvbufs));
+ (void) fprintf(fp, "used receive buffers: %d\n",
+ ntohs(io->fullrecvbufs));
+ (void) fprintf(fp, "low water refills: %d\n",
+ ntohs(io->lowwater));
+ (void) fprintf(fp, "dropped packets: %ld\n",
+ (u_long)ntohl(io->dropped));
+ (void) fprintf(fp, "ignored packets: %ld\n",
+ (u_long)ntohl(io->ignored));
+ (void) fprintf(fp, "received packets: %ld\n",
+ (u_long)ntohl(io->received));
+ (void) fprintf(fp, "packets sent: %ld\n",
+ (u_long)ntohl(io->sent));
+ (void) fprintf(fp, "packets not sent: %ld\n",
+ (u_long)ntohl(io->notsent));
+ (void) fprintf(fp, "interrupts handled: %ld\n",
+ (u_long)ntohl(io->interrupts));
+ (void) fprintf(fp, "received by int: %ld\n",
+ (u_long)ntohl(io->int_received));
+}
+
+
+/*
+ * memstats - print peer memory statistics
+ */
+/*ARGSUSED*/
+static void
+memstats(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ struct info_mem_stats *mem;
+ int i;
+ int items;
+ int itemsize;
+ int res;
+
+ res = doquery(IMPL_XNTPD, REQ_MEM_STATS, 0, 0, 0, (char *)NULL,
+ &items, &itemsize, (char **)&mem, 0);
+
+ if (res != 0 && items == 0)
+ return;
+
+ if (!check1item(items, fp))
+ return;
+
+ if (!checkitemsize(itemsize, sizeof(struct info_mem_stats)))
+ return;
+
+ (void) fprintf(fp, "time since reset: %ld\n",
+ (u_long)ntohl(mem->timereset));
+ (void) fprintf(fp, "total peer memory: %d\n",
+ ntohs(mem->totalpeermem));
+ (void) fprintf(fp, "free peer memory: %d\n",
+ ntohs(mem->freepeermem));
+ (void) fprintf(fp, "calls to findpeer: %ld\n",
+ (u_long)ntohl(mem->findpeer_calls));
+ (void) fprintf(fp, "new peer allocations: %ld\n",
+ (u_long)ntohl(mem->allocations));
+ (void) fprintf(fp, "peer demobilizations: %ld\n",
+ (u_long)ntohl(mem->demobilizations));
+
+ (void) fprintf(fp, "hash table counts: ");
+ for (i = 0; i < HASH_SIZE; i++) {
+ (void) fprintf(fp, "%4d", (int)mem->hashcount[i]);
+ if ((i % 8) == 7 && i != (HASH_SIZE-1)) {
+ (void) fprintf(fp, "\n ");
+ }
+ }
+ (void) fprintf(fp, "\n");
+}
+
+
+
+/*
+ * timerstats - print timer statistics
+ */
+/*ARGSUSED*/
+static void
+timerstats(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ struct info_timer_stats *tim;
+ int items;
+ int itemsize;
+ int res;
+
+ res = doquery(IMPL_XNTPD, REQ_TIMER_STATS, 0, 0, 0, (char *)NULL,
+ &items, &itemsize, (char **)&tim, 0);
+
+ if (res != 0 && items == 0)
+ return;
+
+ if (!check1item(items, fp))
+ return;
+
+ if (!checkitemsize(itemsize, sizeof(struct info_timer_stats)))
+ return;
+
+ (void) fprintf(fp, "time since reset: %ld\n",
+ (u_long)ntohl(tim->timereset));
+ (void) fprintf(fp, "alarms handled: %ld\n",
+ (u_long)ntohl(tim->alarms));
+ (void) fprintf(fp, "alarm overruns: %ld\n",
+ (u_long)ntohl(tim->overflows));
+ (void) fprintf(fp, "calls to transmit: %ld\n",
+ (u_long)ntohl(tim->xmtcalls));
+}
+
+
+/*
+ * addpeer - configure an active mode association
+ */
+static void
+addpeer(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ doconfig(pcmd, fp, MODE_ACTIVE);
+}
+
+
+/*
+ * addserver - configure a client mode association
+ */
+static void
+addserver(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ doconfig(pcmd, fp, MODE_CLIENT);
+}
+
+/*
+ * broadcast - configure a broadcast mode association
+ */
+static void
+broadcast(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ doconfig(pcmd, fp, MODE_BROADCAST);
+}
+
+
+/*
+ * config - configure a new peer association
+ */
+static void
+doconfig(pcmd, fp, mode)
+ struct parse *pcmd;
+ FILE *fp;
+ int mode;
+{
+ struct conf_peer cpeer;
+ int items;
+ int itemsize;
+ char *dummy;
+ u_long keyid;
+ u_int version;
+ u_int flags;
+ int res;
+
+ keyid = 0;
+ version = NTP_VERSION;
+ flags = 0;
+ res = 0;
+ if (pcmd->nargs > 1) {
+ keyid = pcmd->argval[1].uval;
+ if (keyid > 0) {
+ flags |= CONF_FLAG_AUTHENABLE;
+ }
+ if (pcmd->nargs > 2) {
+ version = (u_int)pcmd->argval[2].uval;
+ if (version > NTP_VERSION
+ || version < NTP_OLDVERSION) {
+ (void) fprintf(fp,
+ "funny version number %u specified\n",
+ version);
+ res++;
+ }
+
+ items = 3;
+ while (pcmd->nargs > items) {
+ if (STREQ(pcmd->argval[items].string,
+ "prefer"))
+ flags |= CONF_FLAG_PREFER;
+ else {
+ (void) fprintf(fp,
+ "%s not understood\n",
+ pcmd->argval[3].string);
+ res++;
+ break;
+ }
+ items++;
+ }
+ }
+ }
+
+ if (res)
+ return;
+
+ cpeer.peeraddr = pcmd->argval[0].netnum;
+ cpeer.hmode = (u_char) mode;
+ cpeer.keyid = keyid;
+ cpeer.version = (u_char) version;
+ cpeer.minpoll = NTP_MINDPOLL;
+ cpeer.maxpoll = NTP_MAXPOLL;
+ cpeer.flags = (u_char)flags;
+
+ res = doquery(IMPL_XNTPD, REQ_CONFIG, 1, 1,
+ sizeof(struct conf_peer), (char *)&cpeer, &items,
+ &itemsize, &dummy, 0);
+
+ if (res == 0)
+ (void) fprintf(fp, "done!\n");
+ return;
+}
+
+
+/*
+ * unconfig - unconfigure some associations
+ */
+static void
+unconfig(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ /* 8 is the maximum number of peers which will fit in a packet */
+ struct conf_unpeer plist[min(MAXARGS, 8)];
+ int qitems;
+ int items;
+ int itemsize;
+ char *dummy;
+ int res;
+
+ for (qitems = 0; qitems < min(pcmd->nargs, 8); qitems++) {
+ plist[qitems].peeraddr = pcmd->argval[qitems].netnum;
+ }
+
+ res = doquery(IMPL_XNTPD, REQ_UNCONFIG, 1, qitems,
+ sizeof(struct conf_unpeer), (char *)plist, &items,
+ &itemsize, &dummy, 0);
+
+ if (res == 0)
+ (void) fprintf(fp, "done!\n");
+}
+
+
+/*
+ * set - set some system flags
+ */
+static void
+set(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ doset(pcmd, fp, REQ_SET_SYS_FLAG);
+}
+
+
+/*
+ * clear - clear some system flags
+ */
+static void
+sys_clear(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ doset(pcmd, fp, REQ_CLR_SYS_FLAG);
+}
+
+
+/*
+ * doset - set/clear system flags
+ */
+static void
+doset(pcmd, fp, req)
+ struct parse *pcmd;
+ FILE *fp;
+ int req;
+{
+ /* 8 is the maximum number of peers which will fit in a packet */
+ struct conf_sys_flags sys;
+ int items;
+ int itemsize;
+ char *dummy;
+ int res;
+
+ sys.flags = 0;
+ res = 0;
+ for (items = 0; items < pcmd->nargs; items++) {
+ if (STREQ(pcmd->argval[items].string, "bclient"))
+ sys.flags |= SYS_FLAG_BCLIENT;
+ else if (STREQ(pcmd->argval[items].string, "auth"))
+ sys.flags |= SYS_FLAG_AUTHENTICATE;
+ else if (STREQ(pcmd->argval[items].string, "pll"))
+ sys.flags |= SYS_FLAG_PLL;
+ else if (STREQ(pcmd->argval[items].string, "pps"))
+ sys.flags |= SYS_FLAG_PPS;
+ else if (STREQ(pcmd->argval[items].string, "monitor"))
+ sys.flags |= SYS_FLAG_MONITOR;
+ else if (STREQ(pcmd->argval[items].string, "stats"))
+ sys.flags |= SYS_FLAG_FILEGEN;
+ else {
+ (void) fprintf(fp, "Unknown flag %s\n",
+ pcmd->argval[items].string);
+ res = 1;
+ }
+ }
+
+ if (res || sys.flags == 0)
+ return;
+
+ res = doquery(IMPL_XNTPD, req, 1, 1,
+ sizeof(struct conf_sys_flags), (char *)&sys, &items,
+ &itemsize, &dummy, 0);
+
+ if (res == 0)
+ (void) fprintf(fp, "done!\n");
+}
+
+
+/*
+ * data for printing/interrpreting the restrict flags
+ */
+struct resflags {
+ char *str;
+ int bit;
+};
+
+static struct resflags resflags[] = {
+ { "ignore", RES_IGNORE },
+ { "noserve", RES_DONTSERVE },
+ { "notrust", RES_DONTTRUST },
+ { "noquery", RES_NOQUERY },
+ { "nomodify", RES_NOMODIFY },
+ { "nopeer", RES_NOPEER },
+ { "notrap", RES_NOTRAP },
+ { "lptrap", RES_LPTRAP },
+ { "limited", RES_LIMITED },
+ { "", 0 }
+};
+
+static struct resflags resmflags[] = {
+ { "ntpport", RESM_NTPONLY },
+ { "interface", RESM_INTERFACE },
+ { "", 0 }
+};
+
+
+/*
+ * reslist - obtain and print the server's restrict list
+ */
+/*ARGSUSED*/
+static void
+reslist(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ struct info_restrict *rl;
+ int items;
+ int itemsize;
+ int res;
+ char *addr;
+ char *mask;
+ struct resflags *rf;
+ u_long count;
+ u_short flags;
+ u_short mflags;
+ char flagstr[300];
+ static char *comma = ", ";
+
+ res = doquery(IMPL_XNTPD, REQ_GET_RESTRICT, 0, 0, 0, (char *)NULL,
+ &items, &itemsize, (char **)&rl, 0);
+
+ if (res != 0 && items == 0)
+ return;
+
+ if (!checkitems(items, fp))
+ return;
+
+ if (!checkitemsize(itemsize, sizeof(struct info_restrict)))
+ return;
+
+ (void) fprintf(fp,
+ " address mask count flags\n");
+ (void) fprintf(fp,
+ "=====================================================================\n");
+ while (items > 0) {
+ if ((rl->mask == (U_LONG)0xffffffff))
+ addr = numtohost(rl->addr);
+ else
+ addr = numtoa( rl->addr );
+ mask = numtoa(rl->mask);
+ count = ntohl(rl->count);
+ flags = ntohs(rl->flags);
+ mflags = ntohs(rl->mflags);
+ flagstr[0] = '\0';
+
+ res = 1;
+ rf = &resmflags[0];
+ while (rf->bit != 0) {
+ if (mflags & rf->bit) {
+ if (!res)
+ (void) strcat(flagstr, comma);
+ res = 0;
+ (void) strcat(flagstr, rf->str);
+ }
+ rf++;
+ }
+
+ rf = &resflags[0];
+ while (rf->bit != 0) {
+ if (flags & rf->bit) {
+ if (!res)
+ (void) strcat(flagstr, comma);
+ res = 0;
+ (void) strcat(flagstr, rf->str);
+ }
+ rf++;
+ }
+
+ if (flagstr[0] == '\0')
+ (void) strcpy(flagstr, "none");
+
+ (void) fprintf(fp, "%-15.15s %-15.15s %9ld %s\n",
+ addr, mask, count, flagstr);
+ rl++;
+ items--;
+ }
+}
+
+
+
+/*
+ * restrict - create/add a set of restrictions
+ */
+static void
+restrict(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ do_restrict(pcmd, fp, REQ_RESADDFLAGS);
+}
+
+
+/*
+ * unrestrict - remove restriction flags from existing entry
+ */
+static void
+unrestrict(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ do_restrict(pcmd, fp, REQ_RESSUBFLAGS);
+}
+
+
+/*
+ * delrestrict - delete an existing restriction
+ */
+static void
+delrestrict(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ do_restrict(pcmd, fp, REQ_UNRESTRICT);
+}
+
+
+/*
+ * do_restrict - decode commandline restrictions and make the request
+ */
+static void
+do_restrict(pcmd, fp, req_code)
+ struct parse *pcmd;
+ FILE *fp;
+ int req_code;
+{
+ struct conf_restrict cres;
+ int items;
+ int itemsize;
+ char *dummy;
+ u_long num;
+ u_long bit;
+ int i;
+ int res;
+ int err;
+
+ cres.addr = pcmd->argval[0].netnum;
+ cres.mask = pcmd->argval[1].netnum;
+ cres.flags = 0;
+ cres.mflags = 0;
+ err = 0;
+ for (res = 2; res < pcmd->nargs; res++) {
+ if (STREQ(pcmd->argval[res].string, "ntpport")) {
+ cres.mflags |= RESM_NTPONLY;
+ } else {
+ for (i = 0; resflags[i].bit != 0; i++) {
+ if (STREQ(pcmd->argval[res].string,
+ resflags[i].str))
+ break;
+ }
+ if (resflags[i].bit != 0) {
+ cres.flags |= resflags[i].bit;
+ if (req_code == REQ_UNRESTRICT) {
+ (void) fprintf(fp,
+ "Flag %s inappropriate\n",
+ resflags[i].str);
+ err++;
+ }
+ } else {
+ (void) fprintf(fp, "Unknown flag %s\n",
+ pcmd->argval[res].string);
+ err++;
+ }
+ }
+ }
+
+ /*
+ * Make sure mask for default address is zero. Otherwise,
+ * make sure mask bits are contiguous.
+ */
+ if (cres.addr == 0) {
+ cres.mask = 0;
+ } else {
+ num = ntohl(cres.mask);
+ for (bit = 0x80000000; bit != 0; bit >>= 1)
+ if ((num & bit) == 0)
+ break;
+ for ( ; bit != 0; bit >>= 1)
+ if ((num & bit) != 0)
+ break;
+ if (bit != 0) {
+ (void) fprintf(fp, "Invalid mask %s\n",
+ numtoa(cres.mask));
+ err++;
+ }
+ }
+
+ if (err)
+ return;
+
+ res = doquery(IMPL_XNTPD, req_code, 1, 1,
+ sizeof(struct conf_restrict), (char *)&cres, &items,
+ &itemsize, &dummy, 0);
+
+ if (res == 0)
+ (void) fprintf(fp, "done!\n");
+ return;
+}
+
+
+/*
+ * monlist - obtain and print the server's monitor data
+ */
+/*ARGSUSED*/
+static void
+monlist(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ char *struct_star;
+ struct in_addr addr;
+ int items;
+ int itemsize;
+ int res;
+ int version = -1;
+
+ if (pcmd->nargs > 0) {
+ version = pcmd->argval[0].ival;
+ }
+
+ res = doquery(IMPL_XNTPD,
+ (version == 1 || version == -1) ? REQ_MON_GETLIST_1 :
+ REQ_MON_GETLIST, 0, 0, 0, (char *)NULL,
+ &items, &itemsize, &struct_star,
+ (version < 0) ? (1 << INFO_ERR_REQ) : 0);
+
+ if (res == INFO_ERR_REQ && version < 0)
+ res = doquery(IMPL_XNTPD, REQ_MON_GETLIST, 0, 0, 0, (char *)NULL,
+ &items, &itemsize, &struct_star, 0);
+
+ if (res != 0 && items == 0)
+ return;
+
+ if (!checkitems(items, fp))
+ return;
+
+ if (itemsize == sizeof(struct info_monitor_1)) {
+ struct info_monitor_1 *ml = (struct info_monitor_1 *) struct_star;
+
+ (void) fprintf(fp,
+ "remote address port local address count m ver drop last first\n");
+ (void) fprintf(fp,
+ "===============================================================================\n");
+ while (items > 0) {
+ addr.s_addr = ml->daddr;
+ (void) fprintf(fp,
+ "%-22.22s %5d %-15s %8ld %1d %1d %6lu %6lu %7lu\n",
+ nntohost(ml->addr),
+ ntohs(ml->port),
+ inet_ntoa(addr),
+ (u_long)ntohl(ml->count),
+ ml->mode,
+ ml->version,
+ (u_long)ntohl(ml->lastdrop),
+ (u_long)ntohl(ml->lasttime),
+ (u_long)ntohl(ml->firsttime));
+ ml++;
+ items--;
+ }
+ } else if (itemsize == sizeof(struct info_monitor)) {
+ struct info_monitor *ml = (struct info_monitor *) struct_star;
+
+ (void) fprintf(fp,
+ " address port count mode ver lastdrop lasttime firsttime\n");
+ (void) fprintf(fp,
+ "===============================================================================\n");
+ while (items > 0) {
+ addr.s_addr = ml->lastdrop;
+ (void) fprintf(fp,
+ "%-25.25s %5d %9ld %4d %2d %9lu %9lu %9lu\n",
+ nntohost(ml->addr),
+ ntohs(ml->port),
+ (u_long)ntohl(ml->count),
+ ml->mode,
+ ml->version,
+ (u_long)ntohl(ml->lastdrop),
+ (u_long)ntohl(ml->lasttime),
+ (u_long)ntohl(ml->firsttime));
+ ml++;
+ items--;
+ }
+ } else if (itemsize == sizeof(struct old_info_monitor)) {
+ struct old_info_monitor *oml = (struct old_info_monitor *)struct_star;
+ (void) fprintf(fp,
+ " address port count mode version lasttime firsttime\n");
+ (void) fprintf(fp,
+ "======================================================================\n");
+ while (items > 0) {
+ (void) fprintf(fp, "%-20.20s %5d %9ld %4d %3d %9lu %9lu\n",
+ nntohost(oml->addr),
+ ntohs(oml->port),
+ (u_long)ntohl(oml->count),
+ oml->mode,
+ oml->version,
+ (u_long)ntohl(oml->lasttime),
+ (u_long)ntohl(oml->firsttime));
+ oml++;
+ items--;
+ }
+ } else {
+ /* issue warning according to new info_monitor size */
+ checkitemsize(itemsize, sizeof(struct info_monitor));
+ }
+}
+
+
+/*
+ * monitor - turn the server's monitor facility on or off
+ */
+static void
+monitor(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ int items;
+ int itemsize;
+ char *dummy;
+ int req_code;
+ int res;
+
+ if (STREQ(pcmd->argval[0].string, "on"))
+ req_code = REQ_MONITOR;
+ else if (STREQ(pcmd->argval[0].string, "off"))
+ req_code = REQ_NOMONITOR;
+ else {
+ (void) fprintf(fp, "monitor what?\n");
+ return;
+ }
+
+ res = doquery(IMPL_XNTPD, req_code, 1, 0, 0, (char *)0,
+ &items, &itemsize, &dummy, 0);
+
+ if (res == 0)
+ (void) fprintf(fp, "done!\n");
+ return;
+}
+
+
+
+/*
+ * Mapping between command line strings and stat reset flags
+ */
+struct statreset {
+ char *str;
+ int flag;
+} sreset[] = {
+ { "io", RESET_FLAG_IO },
+ { "sys", RESET_FLAG_SYS },
+ { "mem", RESET_FLAG_MEM },
+ { "timer", RESET_FLAG_TIMER },
+ { "auth", RESET_FLAG_AUTH },
+ { "allpeers", RESET_FLAG_ALLPEERS },
+ { "", 0 }
+};
+
+/*
+ * reset - reset statistic counters
+ */
+static void
+reset(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ struct reset_flags rflags;
+ int items;
+ int itemsize;
+ char *dummy;
+ int i;
+ int res;
+ int err;
+
+ err = 0;
+ rflags.flags = 0;
+ for (res = 0; res < pcmd->nargs; res++) {
+ for (i = 0; sreset[i].flag != 0; i++) {
+ if (STREQ(pcmd->argval[res].string, sreset[i].str))
+ break;
+ }
+ if (sreset[i].flag == 0) {
+ (void) fprintf(fp, "Flag %s unknown\n",
+ pcmd->argval[res].string);
+ err++;
+ } else {
+ rflags.flags |= sreset[i].flag;
+ }
+ }
+
+ if (err) {
+ (void) fprintf(fp, "Not done due to errors\n");
+ return;
+ }
+
+ res = doquery(IMPL_XNTPD, REQ_RESET_STATS, 1, 1,
+ sizeof(struct reset_flags), (char *)&rflags, &items,
+ &itemsize, &dummy, 0);
+
+ if (res == 0)
+ (void) fprintf(fp, "done!\n");
+ return;
+}
+
+
+
+/*
+ * preset - reset stat counters for particular peers
+ */
+static void
+preset(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ /* 8 is the maximum number of peers which will fit in a packet */
+ struct conf_unpeer plist[min(MAXARGS, 8)];
+ int qitems;
+ int items;
+ int itemsize;
+ char *dummy;
+ int res;
+
+ for (qitems = 0; qitems < min(pcmd->nargs, 8); qitems++) {
+ plist[qitems].peeraddr = pcmd->argval[qitems].netnum;
+ }
+
+ res = doquery(IMPL_XNTPD, REQ_RESET_PEER, 1, qitems,
+ sizeof(struct conf_unpeer), (char *)plist, &items,
+ &itemsize, &dummy, 0);
+
+ if (res == 0)
+ (void) fprintf(fp, "done!\n");
+}
+
+
+/*
+ * readkeys - request the server to reread the keys file
+ */
+/*ARGSUSED*/
+static void
+readkeys(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ int items;
+ int itemsize;
+ char *dummy;
+ int res;
+
+ res = doquery(IMPL_XNTPD, REQ_REREAD_KEYS, 1, 0, 0, (char *)0,
+ &items, &itemsize, &dummy, 0);
+
+ if (res == 0)
+ (void) fprintf(fp, "done!\n");
+ return;
+}
+
+
+/*
+ * trustkey - add some keys to the trusted key list
+ */
+static void
+trustkey(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ do_trustkey(pcmd, fp, REQ_TRUSTKEY);
+}
+
+
+/*
+ * untrustkey - remove some keys from the trusted key list
+ */
+static void
+untrustkey(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ do_trustkey(pcmd, fp, REQ_UNTRUSTKEY);
+}
+
+
+/*
+ * do_trustkey - do grunge work of adding/deleting keys
+ */
+static void
+do_trustkey(pcmd, fp, req)
+ struct parse *pcmd;
+ FILE *fp;
+ int req;
+{
+ u_long keyids[MAXARGS];
+ int i;
+ int items;
+ int itemsize;
+ char *dummy;
+ int ritems;
+ int res;
+
+ ritems = 0;
+ for (i = 0; i < pcmd->nargs; i++) {
+ keyids[ritems++] = pcmd->argval[i].uval;
+ }
+
+ res = doquery(IMPL_XNTPD, req, 1, ritems, sizeof(u_long),
+ (char *)keyids, &items, &itemsize, &dummy, 0);
+
+ if (res == 0)
+ (void) fprintf(fp, "done!\n");
+ return;
+}
+
+
+
+/*
+ * authinfo - obtain and print info about authentication
+ */
+/*ARGSUSED*/
+static void
+authinfo(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ struct info_auth *ia;
+ int items;
+ int itemsize;
+ int res;
+
+ res = doquery(IMPL_XNTPD, REQ_AUTHINFO, 0, 0, 0, (char *)NULL,
+ &items, &itemsize, (char **)&ia, 0);
+
+ if (res != 0 && items == 0)
+ return;
+
+ if (!check1item(items, fp))
+ return;
+
+ if (!checkitemsize(itemsize, sizeof(struct info_auth)))
+ return;
+
+ (void) fprintf(fp, "time since reset: %ld\n",
+ (u_long)ntohl(ia->timereset));
+ (void) fprintf(fp, "key lookups: %ld\n",
+ (u_long)ntohl(ia->keylookups));
+ (void) fprintf(fp, "keys not found: %ld\n",
+ (u_long)ntohl(ia->keynotfound));
+ (void) fprintf(fp, "uncached keys: %ld\n",
+ (u_long)ntohl(ia->keyuncached));
+ (void) fprintf(fp, "encryptions: %ld\n",
+ (u_long)ntohl(ia->encryptions));
+ (void) fprintf(fp, "decryptions: %ld\n",
+ (u_long)ntohl(ia->decryptions));
+}
+
+
+
+/*
+ * traps - obtain and print a list of traps
+ */
+/*ARGSUSED*/
+static void
+traps(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ int i;
+ struct info_trap *it;
+ int items;
+ int itemsize;
+ int res;
+
+ res = doquery(IMPL_XNTPD, REQ_TRAPS, 0, 0, 0, (char *)NULL,
+ &items, &itemsize, (char **)&it, 0);
+
+ if (res != 0 && items == 0)
+ return;
+
+ if (!checkitems(items, fp))
+ return;
+
+ if (!checkitemsize(itemsize, sizeof(struct info_trap)))
+ return;
+
+ for (i = 0; i < items; i++ ) {
+ if (i != 0)
+ (void) fprintf(fp, "\n");
+ (void) fprintf(fp, "address %s, port %d\n",
+ numtoa(it->trap_address), ntohs(it->trap_port));
+ (void) fprintf(fp, "interface: %s, ",
+ it->local_address==0?"wildcard":numtoa(it->local_address));
+
+ if (htonl(it->flags) & TRAP_CONFIGURED)
+ (void) fprintf(fp, "configured\n");
+ else if (it->flags & TRAP_NONPRIO)
+ (void) fprintf(fp, "low priority\n");
+ else
+ (void) fprintf(fp, "normal priority\n");
+
+ (void) fprintf(fp, "set for %ld secs, last set %ld secs ago\n",
+ (long)it->origtime, (long)it->settime);
+ (void) fprintf(fp, "sequence %d, number of resets %ld\n",
+ it->sequence, (long)it->resets);
+ }
+}
+
+
+/*
+ * addtrap - configure a trap
+ */
+static void
+addtrap(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ do_addclr_trap(pcmd, fp, REQ_ADD_TRAP);
+}
+
+
+/*
+ * clrtrap - clear a trap from the server
+ */
+static void
+clrtrap(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ do_addclr_trap(pcmd, fp, REQ_CLR_TRAP);
+}
+
+
+/*
+ * do_addclr_trap - do grunge work of adding/deleting traps
+ */
+static void
+do_addclr_trap(pcmd, fp, req)
+ struct parse *pcmd;
+ FILE *fp;
+ int req;
+{
+ struct conf_trap ctrap;
+ int items;
+ int itemsize;
+ char *dummy;
+ int res;
+
+ ctrap.trap_address = pcmd->argval[0].netnum;
+ ctrap.local_address = 0;
+ ctrap.trap_port = htons(TRAPPORT);
+ ctrap.unused = 0;
+
+ if (pcmd->nargs > 1) {
+ ctrap.trap_port
+ = htons((u_short)(pcmd->argval[1].uval & 0xffff));
+ if (pcmd->nargs > 2)
+ ctrap.local_address = pcmd->argval[2].netnum;
+ }
+
+ res = doquery(IMPL_XNTPD, req, 1, 1, sizeof(struct conf_trap),
+ (char *)&ctrap, &items, &itemsize, &dummy, 0);
+
+ if (res == 0)
+ (void) fprintf(fp, "done!\n");
+ return;
+}
+
+
+
+/*
+ * requestkey - change the server's request key (a dangerous request)
+ */
+static void
+requestkey(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ do_changekey(pcmd, fp, REQ_REQUEST_KEY);
+}
+
+
+/*
+ * controlkey - change the server's control key
+ */
+static void
+controlkey(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ do_changekey(pcmd, fp, REQ_CONTROL_KEY);
+}
+
+
+
+/*
+ * do_changekey - do grunge work of changing keys
+ */
+static void
+do_changekey(pcmd, fp, req)
+ struct parse *pcmd;
+ FILE *fp;
+ int req;
+{
+ u_long key;
+ int items;
+ int itemsize;
+ char *dummy;
+ int res;
+
+
+ key = htonl(pcmd->argval[0].uval);
+
+ res = doquery(IMPL_XNTPD, req, 1, 1, sizeof(u_long),
+ (char *)&key, &items, &itemsize, &dummy, 0);
+
+ if (res == 0)
+ (void) fprintf(fp, "done!\n");
+ return;
+}
+
+
+
+/*
+ * ctlstats - obtain and print info about authentication
+ */
+/*ARGSUSED*/
+static void
+ctlstats(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ struct info_control *ic;
+ int items;
+ int itemsize;
+ int res;
+
+ res = doquery(IMPL_XNTPD, REQ_GET_CTLSTATS, 0, 0, 0, (char *)NULL,
+ &items, &itemsize, (char **)&ic, 0);
+
+ if (res != 0 && items == 0)
+ return;
+
+ if (!check1item(items, fp))
+ return;
+
+ if (!checkitemsize(itemsize, sizeof(struct info_control)))
+ return;
+
+ (void) fprintf(fp, "time since reset: %ld\n",
+ (u_long)ntohl(ic->ctltimereset));
+ (void) fprintf(fp, "requests received: %ld\n",
+ (u_long)ntohl(ic->numctlreq));
+ (void) fprintf(fp, "responses sent: %ld\n",
+ (u_long)ntohl(ic->numctlresponses));
+ (void) fprintf(fp, "fragments sent: %ld\n",
+ (u_long)ntohl(ic->numctlfrags));
+ (void) fprintf(fp, "async messages sent: %ld\n",
+ (u_long)ntohl(ic->numasyncmsgs));
+ (void) fprintf(fp, "error msgs sent: %ld\n",
+ (u_long)ntohl(ic->numctlerrors));
+ (void) fprintf(fp, "total bad pkts: %ld\n",
+ (u_long)ntohl(ic->numctlbadpkts));
+ (void) fprintf(fp, "packet too short: %ld\n",
+ (u_long)ntohl(ic->numctltooshort));
+ (void) fprintf(fp, "response on input: %ld\n",
+ (u_long)ntohl(ic->numctlinputresp));
+ (void) fprintf(fp, "fragment on input: %ld\n",
+ (u_long)ntohl(ic->numctlinputfrag));
+ (void) fprintf(fp, "error set on input: %ld\n",
+ (u_long)ntohl(ic->numctlinputerr));
+ (void) fprintf(fp, "bad offset on input: %ld\n",
+ (u_long)ntohl(ic->numctlbadoffset));
+ (void) fprintf(fp, "bad version packets: %ld\n",
+ (u_long)ntohl(ic->numctlbadversion));
+ (void) fprintf(fp, "data in pkt too short: %ld\n",
+ (u_long)ntohl(ic->numctldatatooshort));
+ (void) fprintf(fp, "unknown op codes: %ld\n",
+ (u_long)ntohl(ic->numctlbadop));
+}
+
+
+
+/*
+ * Table for human printing leap bits
+ */
+char *leapbittab[] = {
+ "00 (no leap second scheduled)",
+ "01 (second to be added at end of month)",
+ "10 (second to be deleted at end of month)",
+ "11 (clock out of sync)"
+};
+
+char *controlleapbittab[] = {
+ "00 (leap controlled by lower stratum)",
+ "01 (second to be added at end of month)",
+ "10 (second to be deleted at end of month)",
+ "11 (lower stratum leap information ignored - no leap)"
+};
+
+/*
+ * leapinfo - obtain information about the state of the leap second support
+ */
+/*ARGSUSED*/
+static void
+leapinfo(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ struct info_leap *il;
+ int items;
+ int itemsize;
+ int res;
+ l_fp ts;
+
+ res = doquery(IMPL_XNTPD, REQ_GET_LEAPINFO, 0, 0, 0, (char *)NULL,
+ &items, &itemsize, (char **)&il, 0);
+
+ if (res != 0 && items == 0)
+ return;
+
+ if (!check1item(items, fp))
+ return;
+
+ if (!checkitemsize(itemsize, sizeof(struct info_leap)))
+ return;
+
+ (void) fprintf(fp, "sys.leap: %s\n",
+ leapbittab[il->sys_leap & INFO_LEAP_MASK]);
+ (void) fprintf(fp, "leap.indicator: %s\n",
+ controlleapbittab[il->leap_indicator & INFO_LEAP_MASK]);
+ (void) fprintf(fp, "leap.warning: %s\n",
+ controlleapbittab[il->leap_warning & INFO_LEAP_MASK]);
+ (void) fprintf(fp, "leap.bits: %s\n",
+ leapbittab[il->leap_bits & INFO_LEAP_MASK]);
+ if (il->leap_bits & INFO_LEAP_OVERRIDE)
+ (void) fprintf(fp, "Leap overide option in effect\n");
+ if (il->leap_bits & INFO_LEAP_SEENSTRATUM1)
+ (void) fprintf(fp, "Stratum 1 restrictions in effect\n");
+ (void) fprintf(fp, "time to next leap interrupt: %ld s\n",
+ (u_long)ntohl(il->leap_timer));
+ gettstamp(&ts);
+ (void) fprintf(fp, "date of next leap interrupt: %s\n",
+ humandate(ts.l_ui + ntohl(il->leap_timer)));
+ (void) fprintf(fp, "calls to leap process: %lu\n",
+ (u_long)ntohl(il->leap_processcalls));
+ (void) fprintf(fp, "leap more than month away: %lu\n",
+ (u_long)ntohl(il->leap_notclose));
+ (void) fprintf(fp, "leap less than month away: %lu\n",
+ (u_long)ntohl(il->leap_monthofleap));
+ (void) fprintf(fp, "leap less than day away: %lu\n",
+ (u_long)ntohl(il->leap_dayofleap));
+ (void) fprintf(fp, "leap in less than 2 hours: %lu\n",
+ (u_long)ntohl(il->leap_hoursfromleap));
+ (void) fprintf(fp, "leap happened: %lu\n",
+ (u_long)ntohl(il->leap_happened));
+}
+
+
+/*
+ * clockstat - get and print clock status information
+ */
+static void
+clockstat(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ extern struct clktype clktypes[];
+ struct info_clock *cl;
+ /* 8 is the maximum number of clocks which will fit in a packet */
+ u_long clist[min(MAXARGS, 8)];
+ int qitems;
+ int items;
+ int itemsize;
+ int res;
+ l_fp ts;
+ struct clktype *clk;
+ u_long ltemp;
+
+ for (qitems = 0; qitems < min(pcmd->nargs, 8); qitems++)
+ clist[qitems] = pcmd->argval[qitems].netnum;
+
+ res = doquery(IMPL_XNTPD, REQ_GET_CLOCKINFO, 0, qitems,
+ sizeof(U_LONG), (char *)clist, &items,
+ &itemsize, (char **)&cl, 0);
+
+ if (res != 0 && items == 0)
+ return;
+
+ if (!checkitems(items, fp))
+ return;
+
+ if (!checkitemsize(itemsize, sizeof(struct info_clock)))
+ return;
+
+ while (items-- > 0) {
+ (void) fprintf(fp, "clock address: %s\n",
+ numtoa(cl->clockadr));
+ for (clk = clktypes; clk->code >= 0; clk++)
+ if (clk->code == cl->type)
+ break;
+ if (clk->code >= 0)
+ (void) fprintf(fp, "clock type: %s\n",
+ clk->clocktype);
+ else
+ (void) fprintf(fp, "clock type: unknown type (%d)\n",
+ cl->type);
+ (void) fprintf(fp, "last event: %d\n",
+ cl->lastevent);
+ (void) fprintf(fp, "current status: %d\n",
+ cl->currentstatus);
+ (void) fprintf(fp, "number of polls: %lu\n",
+ (u_long)ntohl(cl->polls));
+ (void) fprintf(fp, "no response to poll: %lu\n",
+ (u_long)ntohl(cl->noresponse));
+ (void) fprintf(fp, "bad format responses: %lu\n",
+ (u_long)ntohl(cl->badformat));
+ (void) fprintf(fp, "bad data responses: %lu\n",
+ (u_long)ntohl(cl->baddata));
+ (void) fprintf(fp, "running time: %lu\n",
+ (u_long)ntohl(cl->timestarted));
+ NTOHL_FP(&cl->fudgetime1, &ts);
+ (void) fprintf(fp, "fudge time 1: %s\n",
+ lfptoa(&ts, 6));
+ NTOHL_FP(&cl->fudgetime2, &ts);
+ (void) fprintf(fp, "fudge time 2: %s\n",
+ lfptoa(&ts, 6));
+ (void) fprintf(fp, "stratum: %ld\n",
+ (u_long)ntohl(cl->fudgeval1));
+ ltemp = ntohl(cl->fudgeval2);
+ (void) fprintf(fp, "reference ID: %s\n",
+ (char *)&ltemp);
+ (void) fprintf(fp, "fudge flags: 0x%x\n",
+ cl->flags);
+
+ if (items > 0)
+ (void) fprintf(fp, "\n");
+ cl++;
+ }
+}
+
+
+/*
+ * fudge - set clock fudge factors
+ */
+static void
+fudge(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ struct conf_fudge fudgedata;
+ int items;
+ int itemsize;
+ char *dummy;
+ l_fp ts;
+ int res;
+ long val;
+ int err;
+
+
+ err = 0;
+ memset((char *)&fudgedata, 0, sizeof fudgedata);
+ fudgedata.clockadr = pcmd->argval[0].netnum;
+
+ if (STREQ(pcmd->argval[1].string, "time1")) {
+ fudgedata.which = htonl(FUDGE_TIME1);
+ if (!atolfp(pcmd->argval[2].string, &ts))
+ err = 1;
+ else
+ NTOHL_FP(&ts, &fudgedata.fudgetime);
+ } else if (STREQ(pcmd->argval[1].string, "time2")) {
+ fudgedata.which = htonl(FUDGE_TIME2);
+ if (!atolfp(pcmd->argval[2].string, &ts))
+ err = 1;
+ else
+ NTOHL_FP(&ts, &fudgedata.fudgetime);
+ } else if (STREQ(pcmd->argval[1].string, "val1")) {
+ fudgedata.which = htonl(FUDGE_VAL1);
+ if (!atoint(pcmd->argval[2].string, &val))
+ err = 1;
+ else
+ fudgedata.fudgeval_flags = htonl(val);
+ } else if (STREQ(pcmd->argval[1].string, "val2")) {
+ fudgedata.which = htonl(FUDGE_VAL2);
+ if (!atoint(pcmd->argval[2].string, &val))
+ err = 1;
+ else
+ fudgedata.fudgeval_flags = htonl(val);
+ } else if (STREQ(pcmd->argval[1].string, "flags")) {
+ fudgedata.which = htonl(FUDGE_FLAGS);
+ if (!atoint(pcmd->argval[2].string, &val))
+ err = 1;
+ else
+ fudgedata.fudgeval_flags = htonl(val & 0xf);
+ } else {
+ (void) fprintf(stderr, "What fudge is %s?\n",
+ pcmd->argval[1].string);
+ return;
+ }
+
+ if (err) {
+ (void) fprintf(stderr, "Unknown fudge parameter %s\n",
+ pcmd->argval[2].string);
+ return;
+ }
+
+
+ res = doquery(IMPL_XNTPD, REQ_SET_CLKFUDGE, 1, 1,
+ sizeof(struct conf_fudge), (char *)&fudgedata, &items,
+ &itemsize, &dummy, 0);
+
+ if (res == 0)
+ (void) fprintf(fp, "done!\n");
+ return;
+}
+
+/*
+ * clkbug - get and print clock debugging information
+ */
+static void
+clkbug(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ register int i;
+ register int n;
+ register u_long s;
+ struct info_clkbug *cl;
+ /* 8 is the maximum number of clocks which will fit in a packet */
+ u_long clist[min(MAXARGS, 8)];
+ u_long ltemp;
+ int qitems;
+ int items;
+ int itemsize;
+ int res;
+ l_fp ts;
+
+ for (qitems = 0; qitems < min(pcmd->nargs, 8); qitems++)
+ clist[qitems] = pcmd->argval[qitems].netnum;
+
+ res = doquery(IMPL_XNTPD, REQ_GET_CLKBUGINFO, 0, qitems,
+ sizeof(U_LONG), (char *)clist, &items,
+ &itemsize, (char **)&cl, 0);
+
+ if (res != 0 && items == 0)
+ return;
+
+ if (!checkitems(items, fp))
+ return;
+
+ if (!checkitemsize(itemsize, sizeof(struct info_clkbug)))
+ return;
+
+ while (items-- > 0) {
+ (void) fprintf(fp, "clock address: %s\n",
+ numtoa(cl->clockadr));
+ n = (int)cl->nvalues;
+ (void) fprintf(fp, "values: %d", n);
+ s = ntohs(cl->svalues);
+ if (n > NUMCBUGVALUES)
+ n = NUMCBUGVALUES;
+ for (i = 0; i < n; i++) {
+ ltemp = (u_long)ntohl(cl->values[i]);
+ ltemp &= 0xffffffff;
+ if ((i & 0x3) == 0)
+ (void) fprintf(fp, "\n");
+ if (s & (1 << i))
+ (void) fprintf(fp, "%12ld", ltemp);
+ else
+ (void) fprintf(fp, "%12lu", ltemp);
+ }
+ (void) fprintf(fp, "\n");
+
+ n = (int)cl->ntimes;
+ (void) fprintf(fp, "times: %d", n);
+ s = ntohl(cl->stimes);
+ if (n > NUMCBUGTIMES)
+ n = NUMCBUGTIMES;
+ for (i = 0; i < n; i++) {
+ int needsp = 0;
+ if ((i & 0x1) == 0)
+ (void) fprintf(fp, "\n");
+ else {
+ for (;needsp > 0; needsp--)
+ putc(' ', fp);
+ }
+ NTOHL_FP(&cl->times[i], &ts);
+ if (s & (1 << i)) {
+ (void) fprintf(fp, "%17s",
+ lfptoa(&ts, 6));
+ needsp = 22;
+ } else {
+ (void) fprintf(fp, "%37s",
+ uglydate(&ts));
+ needsp = 2;
+ }
+ }
+ (void) fprintf(fp, "\n");
+ if (items > 0) {
+ cl++;
+ (void) fprintf(fp, "\n");
+ }
+ }
+}
+
+
+/*
+ * setprecision - set the server's value of sys.precision
+ */
+static void
+setprecision(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ long precision;
+ int items;
+ int itemsize;
+ char *dummy;
+ int res;
+
+ precision = htonl(pcmd->argval[0].ival);
+
+ res = doquery(IMPL_XNTPD, REQ_SET_PRECISION, 1, 1, sizeof(long),
+ (char *)&precision, &items, &itemsize, &dummy, 0);
+
+ if (res == 0)
+ (void) fprintf(fp, "done!\n");
+ return;
+}
+
+
+/*
+ * kerninfo - display the kernel pll/pps variables
+ */
+static void
+kerninfo(pcmd, fp)
+ struct parse *pcmd;
+ FILE *fp;
+{
+ struct info_kernel *ik;
+ int items;
+ int itemsize;
+ int res;
+
+ res = doquery(IMPL_XNTPD, REQ_GET_KERNEL, 0, 0, 0, (char *)NULL,
+ &items, &itemsize, (char **)&ik, 0);
+ if (res != 0 && items == 0)
+ return;
+ if (!check1item(items, fp))
+ return;
+ if (!checkitemsize(itemsize, sizeof(struct info_kernel)))
+ return;
+
+ /*
+ * pll variables
+ */
+ (void)fprintf(fp, "pll offset: %ld us\n",
+ (u_long)ntohl(ik->offset));
+ (void)fprintf(fp, "pll frequency: %s ppm\n",
+ fptoa((s_fp)ntohl(ik->freq), 3));
+ (void)fprintf(fp, "maximum error: %ld us\n",
+ (u_long)ntohl(ik->maxerror));
+ (void)fprintf(fp, "estimated error: %ld us\n",
+ (u_long)ntohl(ik->esterror));
+ (void)fprintf(fp, "status: %04x\n",
+ ntohs(ik->status & 0xffff));
+ (void)fprintf(fp, "pll time constant: %ld\n",
+ (u_long)ntohl(ik->constant));
+ (void)fprintf(fp, "precision: %ld us\n",
+ (u_long)ntohl(ik->precision));
+ (void)fprintf(fp, "frequency tolerance: %s ppm\n",
+ fptoa((s_fp)ntohl(ik->tolerance), 0));
+
+ /*
+ * For backwards compatibility (ugh), we find the pps variables
+ * only if the shift member is nonzero.
+ */
+ if (!ik->shift)
+ return;
+
+ /*
+ * pps variables
+ */
+ (void)fprintf(fp, "pps frequency: %s ppm\n",
+ fptoa((s_fp)ntohl(ik->ppsfreq), 3));
+ (void)fprintf(fp, "pps stability: %s ppm\n",
+ fptoa((s_fp)ntohl(ik->stabil), 3));
+ (void)fprintf(fp, "pps jitter: %ld us\n",
+ (u_long)ntohl(ik->jitter));
+ (void)fprintf(fp, "calibration interval: %d s\n",
+ 1 << ntohs(ik->shift));
+ (void)fprintf(fp, "calibration cycles: %ld\n",
+ (u_long)ntohl(ik->calcnt));
+ (void)fprintf(fp, "jitter exceeded: %ld\n",
+ (u_long)ntohl(ik->jitcnt));
+ (void)fprintf(fp, "stability exceeded: %ld\n",
+ (u_long)ntohl(ik->stbcnt));
+ (void)fprintf(fp, "calibration errors: %ld\n",
+ (u_long)ntohl(ik->errcnt));
+}
diff --git a/usr.sbin/xten/Makefile b/usr.sbin/xten/Makefile
new file mode 100644
index 0000000..02d408d
--- /dev/null
+++ b/usr.sbin/xten/Makefile
@@ -0,0 +1,7 @@
+# Makefile for xten (Stark) 10/30/93
+# $Id$
+
+PROG= xten
+CFLAGS+= -I${.CURDIR}/../../libexec/xtend
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/xten/README b/usr.sbin/xten/README
new file mode 100644
index 0000000..7ff07fa
--- /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 vector twintr
+
+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 vector twintr
+device lpt0 at isa? port? tty irq 7 vector lptintr
+
+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..4a00f33
--- /dev/null
+++ b/usr.sbin/xten/xten.1
@@ -0,0 +1,113 @@
+.\" Copyright (c) 1992, 1993 Eugene W. Stark
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by Eugene W. Stark.
+.\" 4. The name of the author may not be used to endorse or promote products
+.\" derived from this software without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY EUGENE W. STARK (THE AUTHOR) ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+.\" INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+.\" (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+.\" SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $Id: xten.1,v 1.6 1997/06/23 04:52:13 steve Exp $
+.\"
+.Dd October 30, 1993
+.Dt XTEN 1
+.Os
+.Sh NAME
+.Nm xten
+.Nd transmit X-10 commands
+.Sh SYNOPSIS
+.Nm xten
+.Op Fl ""
+.Ar house Ar key Ns Op Ar :cnt
+.Oo
+.Op Ar house
+.Ar key Ns Op Ar :cnt
+.Ar ...
+.Oc
+.Sh DESCRIPTION
+.Nm Xten
+is a command-line interface to the X-10 daemon.
+When invoked with a one-letter house code (A-P) and a series of key/unit
+codes as arguments, it requests the X-10 daemon to transmit a corresponding
+series of X-10 packets. The X-10 daemon makes its best effort to ensure
+that the packets are all transmitted correctly, though in general it is
+not possible to tell whether the commands were actually received and
+executed by the remote X-10 devices.
+.Pp
+When invoked with the single argument
+.Fl "" ,
+.Nm
+enters an interactive mode in which a line is repeatedly read from the
+standard input, sent to the X-10 daemon, and the one-line response from
+the daemon printed on the standard output.
+.Sh OPTIONS
+The
+.Ar house
+argument is a one-letter house code in the range A-P.
+All the X-10 requests generated will refer to this house code.
+Each
+.Ar key
+is either a numeric unit code in the range 1-16, or else
+is a string that specifies an X-10 function. The possible
+function code strings are:
+.Bl -diag
+.It AllUnitsOff
+.It AllLightsOn
+.It On
+.It Off
+.It Dim
+.It Bright
+.It AllLightsOff
+.It ExtendedCode
+.It HailRequest
+.It HailAcknowledge
+.It PreSetDim0
+.It PreSetDim1
+.It ExtendedData
+.It StatusOn
+.It StatusOff
+.It StatusRequest
+.El
+.Pp
+Each
+.Ar key
+may be followed by an optional numeric
+.Ar cnt ,
+which specifies the number of packets that are to be sent with that
+key code without gaps. If this argument is omitted, two packets
+are transmitted. The ability to specify numbers of packets other than
+two is used by the X-10
+.Em Dim
+and
+.Em Bright
+commands.
+.Sh SEE ALSO
+.Xr tw 4 ,
+.Xr xtend 8
+.Sh FILES
+.Bl -tag -width /var/spool/xten/Status -compact
+.It Pa /dev/tw0
+the TW523 special file
+.El
+.Sh AUTHOR
+.An Eugene W. Stark Aq stark@cs.sunysb.edu
diff --git a/usr.sbin/xten/xten.c b/usr.sbin/xten/xten.c
new file mode 100644
index 0000000..f59e76f
--- /dev/null
+++ b/usr.sbin/xten/xten.c
@@ -0,0 +1,178 @@
+/*-
+ * Copyright (c) 1992, 1993 Eugene W. Stark
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Eugene W. Stark.
+ * 4. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY EUGENE W. STARK (THE AUTHOR) ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+/*
+ * Xten - user command interface to X-10 daemon
+ */
+
+#include <err.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include "xtend.h"
+#include "xten.h"
+#include "paths.h"
+
+#define RETRIES 10
+#define CMDLEN 512
+
+char *X10housenames[] = {
+ "A", "B", "C", "D", "E", "F", "G", "H",
+ "I", "J", "K", "L", "M", "N", "O", "P",
+ NULL
+};
+
+char *X10cmdnames[] = {
+ "1", "2", "3", "4", "5", "6", "7", "8",
+ "9", "10", "11", "12", "13", "14", "15", "16",
+ "AllUnitsOff", "AllLightsOn", "On", "Off", "Dim", "Bright", "AllLightsOff",
+ "ExtendedCode", "HailRequest", "HailAcknowledge", "PreSetDim0", "PreSetDim1",
+ "ExtendedData", "StatusOn", "StatusOff", "StatusRequest",
+ NULL
+};
+
+int find __P((char *, char *[]));
+static void usage __P((void));
+
+int
+main(argc, argv)
+int argc;
+char *argv[];
+{
+ int c, tmp, h, k, sock, error;
+ FILE *daemon;
+ struct sockaddr_un sa;
+ char *sockpath = SOCKPATH;
+ char reply[CMDLEN], cmd[CMDLEN], *cp;
+ int interactive = 0;
+
+ if(argc == 2 && !strcmp(argv[1], "-")) interactive++;
+ else if(argc < 3)
+ usage();
+ if((sock = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
+ errx(1, "can't create socket");
+ strcpy(sa.sun_path, sockpath);
+ sa.sun_family = AF_UNIX;
+ if(connect(sock, (struct sockaddr *)(&sa), strlen(sa.sun_path) + 2) < 0)
+ errx(1, "can't connect to X-10 daemon");
+ if((daemon = fdopen(sock, "w+")) == NULL)
+ errx(1, "can't attach stream to socket");
+ /*
+ * If interactive, copy standard input to daemon and report results
+ * on standard output.
+ */
+ if(interactive) {
+ while(!feof(stdin)) {
+ if(fgets(cmd, CMDLEN, stdin) != NULL) {
+ fprintf(daemon, "%s", cmd);
+ fflush(daemon);
+ if(fgets(reply, CMDLEN, daemon) != NULL) {
+ fprintf(stdout, "%s", reply);
+ fflush(stdout);
+ }
+ }
+ }
+ exit(0);
+ }
+ /*
+ * Otherwise, interpret arguments and issue commands to daemon,
+ * handling retries in case of errors.
+ */
+ if((h = find(argv[1], X10housenames)) < 0)
+ errx(1, "invalid house code: %s", argv[1]);
+ argv++;
+ argv++;
+ while(argc >= 3) {
+ cp = argv[0];
+ if((tmp = find(cp, X10housenames)) >= 0) {
+ h = tmp;
+ argv++;
+ argc--;
+ continue;
+ }
+ while(*cp != '\0' && *cp != ':') cp++;
+ if(*cp == ':') c = atoi(cp+1);
+ else c = 2;
+ *cp = '\0';
+ if((k = find(argv[0], X10cmdnames)) < 0) {
+ warnx("invalid key/unit code: %s", argv[0]);
+ error++;
+ }
+ error = 0;
+ while(error < RETRIES) {
+ fprintf(daemon, "send %s %s %d\n", X10housenames[h], X10cmdnames[k], c);
+ fflush(daemon);
+ fgets(reply, CMDLEN, daemon);
+ if(strncmp(reply, "ERROR", 5)) break;
+ error++;
+ usleep(200000);
+ }
+ if(error == RETRIES) {
+ warnx("command failed: send %s %s %d",
+ X10housenames[h], X10cmdnames[k], c);
+ }
+ argc--;
+ argv++;
+ }
+ fprintf(daemon, "done\n");
+ fgets(reply, CMDLEN, daemon);
+ exit(0);
+}
+
+static void
+usage()
+{
+ fprintf(stderr,
+ "usage: xten house key[:cnt] [[house] key[:cnt] ...]\n");
+ exit(1);
+}
+
+int
+find(s, tab)
+char *s;
+char *tab[];
+{
+ int i;
+
+ for(i = 0; tab[i] != NULL; i++) {
+ if(strcasecmp(s, tab[i]) == 0) return(i);
+ }
+ return(-1);
+}
diff --git a/usr.sbin/yp_mkdb/Makefile b/usr.sbin/yp_mkdb/Makefile
new file mode 100644
index 0000000..b886d19
--- /dev/null
+++ b/usr.sbin/yp_mkdb/Makefile
@@ -0,0 +1,13 @@
+# $Id$
+
+PROG= yp_mkdb
+SRCS= yp_mkdb.c yp_dblookup.c yp_dbwrite.c
+
+MAN8= yp_mkdb.8
+
+.PATH: ${.CURDIR}/../../libexec/ypxfr ${.CURDIR}/../ypserv
+
+CFLAGS+= -Dyp_error=warnx -I${.CURDIR}/../../libexec/ypxfr
+CFLAGS+= -I${.CURDIR}/../../usr.sbin/ypserv
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/yp_mkdb/yp_mkdb.8 b/usr.sbin/yp_mkdb/yp_mkdb.8
new file mode 100644
index 0000000..c9e8dc8
--- /dev/null
+++ b/usr.sbin/yp_mkdb/yp_mkdb.8
@@ -0,0 +1,171 @@
+.\" Copyright (c) 1995, 1996
+.\" Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by Bill Paul
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $Id: yp_mkdb.8,v 1.6 1997/04/15 07:06:15 jmg Exp $
+.\"
+.Dd March 12, 1996
+.Dt YP_MKDB 8
+.Os
+.Sh NAME
+.Nm yp_mkdb
+.Nd "generate the NIS databases"
+.Sh SYNOPSIS
+.Nm yp_mkdb
+.Fl c
+.Nm yp_mkdb
+.Fl u Ar dbname
+.Nm yp_mkdb
+.Op Fl c
+.Op Fl b
+.Op Fl s
+.Op Fl i Ar inputfile
+.Op Fl o Ar outputfile
+.Op Fl d Ar domainname
+.Op Fl m Ar mastername
+.Ar inputfile
+.Ar dbname
+.Sh DESCRIPTION
+.Nm Yp_mkdb
+creates
+.Xr db 3
+style databases for use with FreeBSD's NIS server.
+.Nm Yp_mkdb
+reads data from
+.Ar inputfile ,
+and writes it to
+.Ar dbname
+in
+.Xr db 3
+format (using the hash table method).
+The input should be in 'key data' format, which is to say
+two fields of
+.Tn ASCII
+data separated by white space. The first field
+is assumed to be the key, and everything else is assumed to be
+the data.
+These databases are typically stored in
+.Pa /var/yp/[domainname]
+where
+.Ar domainname
+is the name of the NIS domain being served.
+.Nm Yp_mkdb
+is usually invoked by
+.Pa /var/yp/Makefile .
+.Nm Yp_mkdb
+can also be used to dump an NIS database file so that its
+contents can be examined. For security reasons, all databases that
+.Nm
+creates are readable and writable by owner only (and usually the
+owner is root).
+.Sh OPTIONS
+The
+.Nm
+command supports the following flags and options:
+.Bl -tag -width indent
+.It Fl c
+Cause
+.Nm
+to send a YPPROC_CLEAR request to
+.Xr ypserv 8
+on the local host. This signal tells the server to close any open
+database descriptors and flush out its database cache. If used alone,
+this flag signals the server and does nothing else. If used as part
+of a database creation command,
+.Nm
+will send the signal only after the new database has been successfully
+created.
+.It Fl b
+This flag causes
+.Nm
+to add a special entry to the database with a key of
+.Em YP_INTERDOMAIN
+and an empty data field. If this key is present in a map, it alters the
+behavior of the 'match' procedure in
+.Xr ypserv 8
+slightly. If a match query fails (because the server couldn't find
+a record that matched the supplied key), and the
+.Em YP_INTERDOMAIN
+key exists within the queried map,
+.Xr ypserv 8
+will try to match the entry again using a DNS lookup. Note that this
+special behavior only applies to the
+.Em hosts
+maps. Using the
+.Fl b
+flag for other maps has no effect.
+.It Fl s
+This flag is used to add a special entry to the database with a key of
+.Em YP_SECURE
+and an empty data field. If this key is present in a map,
+.Xr ypserv 8
+will deny access to the map to any client that is not using a
+reserved port for its query. This is used mainly for the
+.Em master.passwd
+maps, which should be restricted to privileged access only.
+.It Fl u Ar dbname
+Dump (or 'unwind') an NIS database. This option can be used to
+inspect the contents of an existing NIS database.
+.It Fl i Ar inputfile
+When generating an NIS map, encode
+.Ar inputfile
+as a special entry in the database with a key of
+.Em YP_INPUT_FILE .
+.It Fl o Ar outputfile
+When generating an NIS map, encode
+.Ar outputfile
+as a special entry in the database with a key of
+.Em YP_OUTPUT_FILE .
+.It Fl d Ar domainname
+When generating an NIS map, encode
+.Ar domainname
+as a special entry in the database with a key of
+.Em YP_DOMAIN_NAME .
+.It Fl m Ar mastername
+When generating an NIS map, encode
+.Ar mastername
+as a special entry in the database with a key of
+.Em YP_MASTER_NAME .
+This entry in the database is frequently used by various NIS utilities
+to determine the name of an NIS master server for a domain. By default,
+.Nm
+assumes that the local host is the NIS master; the
+.Fl m
+option is used to override this default.
+.Sh FILES
+.Bl -tag -width /var/yp/Makefile -compact
+.It Pa /var/yp/Makefile
+the Makefile that calls
+.Nm
+to build the NIS databases
+.Sh SEE ALSO
+.Xr db 3 ,
+.Xr ypserv 8
+.Sh AUTHOR
+.An Bill Paul Aq wpaul@ctr.columbia.edu
diff --git a/usr.sbin/yp_mkdb/yp_mkdb.c b/usr.sbin/yp_mkdb/yp_mkdb.c
new file mode 100644
index 0000000..aea610f
--- /dev/null
+++ b/usr.sbin/yp_mkdb/yp_mkdb.c
@@ -0,0 +1,338 @@
+/*
+ * Copyright (c) 1995, 1996
+ * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#include <err.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <rpc/rpc.h>
+#include <rpcsvc/yp.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include "yp_extern.h"
+#include "ypxfr_extern.h"
+
+char *yp_dir = ""; /* No particular default needed. */
+int _rpcpmstart = 0;
+int debug = 1;
+
+static void usage()
+{
+ fprintf(stderr, "%s\n%s\n%s\n%s\n",
+ "usage: yp_mkdb -c",
+ " yp_mkdb -u dbname",
+ " yp_mkdb [-c] [-b] [-s] [-i inputfile] [-o outputfile]",
+ " [-d domainname ] [-m mastername] inputfile dbname");
+ exit(1);
+}
+
+#define PERM_SECURE (S_IRUSR|S_IWUSR)
+
+static DB *open_db(path, flags)
+ char *path;
+ int flags;
+{
+ extern HASHINFO openinfo;
+
+ return(dbopen(path, flags, PERM_SECURE, DB_HASH, &openinfo));
+}
+
+static void unwind(map)
+ char *map;
+{
+ DB *dbp;
+ DBT key, data;
+
+ dbp = open_db(map, O_RDONLY);
+
+ if (dbp == NULL)
+ err(1, "open_db(%s) failed", map);
+
+ key.data = NULL;
+ while(yp_next_record(dbp, &key, &data, 1, 1) == YP_TRUE)
+ printf("%.*s %.*s\n", key.size,key.data,data.size,data.data);
+
+ (void)(dbp->close)(dbp);
+ return;
+}
+
+int main (argc, argv)
+ int argc;
+ char *argv[];
+{
+ int ch;
+ int un = 0;
+ int clear = 0;
+ char *infile = NULL;
+ char *map = NULL;
+ char *domain = NULL;
+ char *infilename = NULL;
+ char *outfilename = NULL;
+ char *mastername = NULL;
+ int interdom = 0;
+ int secure = 0;
+ DB *dbp;
+ DBT key, data;
+ char buf[10240];
+ char *keybuf, *datbuf;
+ FILE *ifp;
+ char hname[MAXHOSTNAMELEN + 2];
+
+ while ((ch = getopt(argc, argv, "uhcbsd:i:o:m:")) != -1) {
+ switch(ch) {
+ case 'u':
+ un++;
+ break;
+ case 'c':
+ clear++;
+ break;
+ case 'b':
+ interdom++;
+ break;
+ case 's':
+ secure++;
+ break;
+ case 'd':
+ domain = optarg;
+ break;
+ case 'i':
+ infilename = optarg;
+ break;
+ case 'o':
+ outfilename = optarg;
+ break;
+ case 'm':
+ mastername = optarg;
+ break;
+ case 'h':
+ default:
+ usage();
+ break;
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if (un) {
+ map = argv[0];
+ if (map == NULL)
+ usage();
+ unwind(map);
+ exit(0);
+
+ }
+
+ infile = argv[0];
+ map = argv[1];
+
+ if (infile == NULL || map == NULL) {
+ if (clear)
+ goto doclear;
+ usage();
+ }
+
+ if (mastername == NULL) {
+ if (gethostname((char *)&hname, sizeof(hname)) == -1)
+ err(1, "gethostname() failed");
+ mastername = (char *)&hname;
+ }
+
+ /*
+ * Note that while we can read from stdin, we can't
+ * write to stdout; the db library doesn't let you
+ * write to a file stream like that.
+ */
+
+ if (!strcmp(infile, "-")) {
+ ifp = stdin;
+ } else {
+ if ((ifp = fopen(infile, "r")) == NULL)
+ err(1, "failed to open %s", infile);
+ }
+
+ if ((dbp = open_db(map, O_RDWR|O_EXLOCK|O_EXCL|O_CREAT)) == NULL)
+ err(1, "open_db(%s) failed", map);
+
+ if (interdom) {
+ key.data = "YP_INTERDOMAIN";
+ key.size = sizeof("YP_INTERDOMAIN") - 1;
+ data.data = "";
+ data.size = 0;
+ yp_put_record(dbp, &key, &data, 0);
+ }
+
+ if (secure) {
+ key.data = "YP_SECURE";
+ key.size = sizeof("YP_SECURE") - 1;
+ data.data = "";
+ data.size = 0;
+ yp_put_record(dbp, &key, &data, 0);
+ }
+
+ key.data = "YP_MASTER_NAME";
+ key.size = sizeof("YP_MASTER_NAME") - 1;
+ data.data = mastername;
+ data.size = strlen(mastername);
+ yp_put_record(dbp, &key, &data, 0);
+
+ key.data = "YP_LAST_MODIFIED";
+ key.size = sizeof("YP_LAST_MODIFIED") - 1;
+ snprintf(buf, sizeof(buf), "%lu", time(NULL));
+ data.data = (char *)&buf;
+ data.size = strlen(buf);
+ yp_put_record(dbp, &key, &data, 0);
+
+ if (infilename) {
+ key.data = "YP_INPUT_FILE";
+ key.size = sizeof("YP_INPUT_FILE") - 1;
+ data.data = infilename;
+ data.size = strlen(infilename);
+ yp_put_record(dbp, &key, &data, 0);
+ }
+
+ if (outfilename) {
+ key.data = "YP_OUTPUT_FILE";
+ key.size = sizeof("YP_OUTPUT_FILE") - 1;
+ data.data = outfilename;
+ data.size = strlen(outfilename);
+ yp_put_record(dbp, &key, &data, 0);
+ }
+
+ if (domain) {
+ key.data = "YP_DOMAIN_NAME";
+ key.size = sizeof("YP_DOMAIN_NAME") - 1;
+ data.data = domain;
+ data.size = strlen(domain);
+ yp_put_record(dbp, &key, &data, 0);
+ }
+
+ while(fgets((char *)&buf, sizeof(buf), ifp)) {
+ char *sep = NULL;
+ int rval;
+
+ /* NUL terminate */
+ if ((sep = strchr(buf, '\n')))
+ *sep = '\0';
+
+ /* handle backslash line continuations */
+ while(buf[strlen(buf) - 1] == '\\') {
+ fgets((char *)&buf[strlen(buf) - 1],
+ sizeof(buf) - strlen(buf), ifp);
+ if ((sep = strchr(buf, '\n')))
+ *sep = '\0';
+ }
+
+ /* find the separation between the key and data */
+ if ((sep = strpbrk(buf, " \t")) == NULL) {
+ warnx("bad input -- no white space: %s", buf);
+ continue;
+ }
+
+ /* separate the strings */
+ keybuf = (char *)&buf;
+ datbuf = sep + 1;
+ *sep = '\0';
+
+ /* set datbuf to start at first non-whitespace character */
+ while (*datbuf == ' ' || *datbuf == '\t')
+ datbuf++;
+
+ /* Check for silliness. */
+ if (*keybuf == '+' || *keybuf == '-' ||
+ *datbuf == '+' || *datbuf == '-') {
+ warnx("bad character at start of line: %s", buf);
+ continue;
+ }
+
+ if (strlen(keybuf) > YPMAXRECORD) {
+ warnx("key too long: %s", keybuf);
+ continue;
+ }
+
+ if (!strlen(keybuf)) {
+ warnx("no key -- check source file for blank lines");
+ continue;
+ }
+
+ if (strlen(datbuf) > YPMAXRECORD) {
+ warnx("data too long: %s", datbuf);
+ continue;
+ }
+
+ key.data = keybuf;
+ key.size = strlen(keybuf);
+ data.data = datbuf;
+ data.size = strlen(datbuf);
+
+ if ((rval = yp_put_record(dbp, &key, &data, 0)) != YP_TRUE) {
+ switch(rval) {
+ case YP_FALSE:
+ warnx("duplicate key '%s' - skipping", keybuf);
+ break;
+ case YP_BADDB:
+ default:
+ err(1,"failed to write new record - exiting");
+ break;
+ }
+ }
+
+ }
+
+ (void)(dbp->close)(dbp);
+
+doclear:
+
+ if (clear) {
+ char in = 0;
+ char *out = NULL;
+ int stat;
+ if ((stat = callrpc("localhost",YPPROG,YPVERS,YPPROC_CLEAR,
+ xdr_void, (void *)&in,
+ xdr_void, (void *)out)) != RPC_SUCCESS) {
+ warnx("failed to send 'clear' to local ypserv: %s",
+ clnt_sperrno((enum clnt_stat) stat));
+ }
+ }
+
+ exit(0);
+}
diff --git a/usr.sbin/ypbind/Makefile b/usr.sbin/ypbind/Makefile
new file mode 100644
index 0000000..ec83451
--- /dev/null
+++ b/usr.sbin/ypbind/Makefile
@@ -0,0 +1,9 @@
+# from: @(#)Makefile 5.8 (Berkeley) 7/28/90
+# $Id: Makefile,v 1.4 1997/02/22 16:14:53 peter Exp $
+
+SRCS= ypbind.c yp_ping.c
+PROG= ypbind
+MAN8= ypbind.8
+CFLAGS+=-DDAEMON
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/ypbind/yp_ping.c b/usr.sbin/ypbind/yp_ping.c
new file mode 100644
index 0000000..6550ab0
--- /dev/null
+++ b/usr.sbin/ypbind/yp_ping.c
@@ -0,0 +1,545 @@
+/*
+ * Copyright (c) 1996, 1997
+ * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/*
+ * What follows is a special version of clntudp_call() that has been
+ * hacked to send requests and receive replies asynchronously. Similar
+ * magic is used inside rpc.nisd(8) for the special non-blocking,
+ * non-fork()ing, non-threading callback support.
+ */
+
+/*
+ * Sun RPC is a product of Sun Microsystems, Inc. and is provided for
+ * unrestricted use provided that this legend is included on all tape
+ * media and as a part of the software program in whole or part. Users
+ * may copy or modify Sun RPC without charge, but are not authorized
+ * to license or distribute it to anyone else except as part of a product or
+ * program developed by the user.
+ *
+ * SUN RPC IS PROVIDED AS IS WITH NO WARRANTIES OF ANY KIND INCLUDING THE
+ * WARRANTIES OF DESIGN, MERCHANTIBILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, OR ARISING FROM A COURSE OF DEALING, USAGE OR TRADE PRACTICE.
+ *
+ * Sun RPC is provided with no support and without any obligation on the
+ * part of Sun Microsystems, Inc. to assist in its use, correction,
+ * modification or enhancement.
+ *
+ * SUN MICROSYSTEMS, INC. SHALL HAVE NO LIABILITY WITH RESPECT TO THE
+ * INFRINGEMENT OF COPYRIGHTS, TRADE SECRETS OR ANY PATENTS BY SUN RPC
+ * OR ANY PART THEREOF.
+ *
+ * In no event will Sun Microsystems, Inc. be liable for any lost revenue
+ * or profits or other special, indirect and consequential damages, even if
+ * Sun has been advised of the possibility of such damages.
+ *
+ * Sun Microsystems, Inc.
+ * 2550 Garcia Avenue
+ * Mountain View, California 94043
+ */
+
+#ifndef lint
+#if 0
+static char *sccsid = "@(#)from: clnt_udp.c 1.39 87/08/11 Copyr 1984 Sun Micro";
+static char *sccsid = "@(#)from: clnt_udp.c 2.2 88/08/01 4.0 RPCSRC";
+#endif
+static const char rcsid[] =
+ "$Id$";
+#endif
+
+/*
+ * clnt_udp.c, Implements a UDP/IP based, client side RPC.
+ *
+ * Copyright (C) 1984, Sun Microsystems, Inc.
+ */
+
+#include <errno.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <rpc/rpc.h>
+#include <rpc/pmap_clnt.h>
+#include <rpc/pmap_prot.h>
+#include <rpcsvc/yp.h>
+#include <sys/socket.h>
+#include <sys/ioctl.h>
+#include <net/if.h>
+#include "yp_ping.h"
+
+#ifndef timeradd
+#ifndef KERNEL /* use timevaladd/timevalsub in kernel */
+/* NetBSD/OpenBSD compatable interfaces */
+#define timeradd(tvp, uvp, vvp) \
+ do { \
+ (vvp)->tv_sec = (tvp)->tv_sec + (uvp)->tv_sec; \
+ (vvp)->tv_usec = (tvp)->tv_usec + (uvp)->tv_usec; \
+ if ((vvp)->tv_usec >= 1000000) { \
+ (vvp)->tv_sec++; \
+ (vvp)->tv_usec -= 1000000; \
+ } \
+ } while (0)
+#define timersub(tvp, uvp, vvp) \
+ do { \
+ (vvp)->tv_sec = (tvp)->tv_sec - (uvp)->tv_sec; \
+ (vvp)->tv_usec = (tvp)->tv_usec - (uvp)->tv_usec; \
+ if ((vvp)->tv_usec < 0) { \
+ (vvp)->tv_sec--; \
+ (vvp)->tv_usec += 1000000; \
+ } \
+ } while (0)
+#endif
+#endif
+
+/*
+ * Private data kept per client handle
+ */
+struct cu_data {
+ int cu_sock;
+ bool_t cu_closeit;
+ struct sockaddr_in cu_raddr;
+ int cu_rlen;
+ struct timeval cu_wait;
+ struct timeval cu_total;
+ struct rpc_err cu_error;
+ XDR cu_outxdrs;
+ u_int cu_xdrpos;
+ u_int cu_sendsz;
+ char *cu_outbuf;
+ u_int cu_recvsz;
+ char cu_inbuf[1];
+};
+
+static enum clnt_stat
+clntudp_a_call(cl, proc, xargs, argsp, xresults, resultsp, utimeout)
+ register CLIENT *cl; /* client handle */
+ u_long proc; /* procedure number */
+ xdrproc_t xargs; /* xdr routine for args */
+ caddr_t argsp; /* pointer to args */
+ xdrproc_t xresults; /* xdr routine for results */
+ caddr_t resultsp; /* pointer to results */
+ struct timeval utimeout; /* seconds to wait before giving up */
+{
+ register struct cu_data *cu = (struct cu_data *)cl->cl_private;
+ register XDR *xdrs;
+ register int outlen = 0;
+ register int inlen;
+ int fromlen;
+ fd_set *fds, readfds;
+ struct sockaddr_in from;
+ struct rpc_msg reply_msg;
+ XDR reply_xdrs;
+ struct timeval time_waited, start, after, tmp1, tmp2, tv;
+ bool_t ok;
+ int nrefreshes = 2; /* number of times to refresh cred */
+ struct timeval timeout;
+
+ if (cu->cu_total.tv_usec == -1)
+ timeout = utimeout; /* use supplied timeout */
+ else
+ timeout = cu->cu_total; /* use default timeout */
+
+ if (cu->cu_sock + 1 > FD_SETSIZE) {
+ int bytes = howmany(cu->cu_sock + 1, NFDBITS) * sizeof(fd_mask);
+ fds = (fd_set *)malloc(bytes);
+ if (fds == NULL)
+ return (cu->cu_error.re_status = RPC_CANTSEND);
+ memset(fds, 0, bytes);
+ } else {
+ fds = &readfds;
+ FD_ZERO(fds);
+ }
+
+ timerclear(&time_waited);
+
+call_again:
+ xdrs = &(cu->cu_outxdrs);
+ if (xargs == NULL)
+ goto get_reply;
+ xdrs->x_op = XDR_ENCODE;
+ XDR_SETPOS(xdrs, cu->cu_xdrpos);
+ /*
+ * the transaction is the first thing in the out buffer
+ */
+ (*(u_short *)(cu->cu_outbuf))++;
+ if ((! XDR_PUTLONG(xdrs, (long *)&proc)) ||
+ (! AUTH_MARSHALL(cl->cl_auth, xdrs)) ||
+ (! (*xargs)(xdrs, argsp))) {
+ if (fds != &readfds)
+ free(fds);
+ return (cu->cu_error.re_status = RPC_CANTENCODEARGS);
+ }
+ outlen = (int)XDR_GETPOS(xdrs);
+
+send_again:
+ if (sendto(cu->cu_sock, cu->cu_outbuf, outlen, 0,
+ (struct sockaddr *)&(cu->cu_raddr), cu->cu_rlen) != outlen) {
+ cu->cu_error.re_errno = errno;
+ if (fds != &readfds)
+ free(fds);
+ return (cu->cu_error.re_status = RPC_CANTSEND);
+ }
+
+ /*
+ * Hack to provide rpc-based message passing
+ */
+ if (!timerisset(&timeout)) {
+ if (fds != &readfds)
+ free(fds);
+ return (cu->cu_error.re_status = RPC_TIMEDOUT);
+ }
+
+get_reply:
+
+ /*
+ * sub-optimal code appears here because we have
+ * some clock time to spare while the packets are in flight.
+ * (We assume that this is actually only executed once.)
+ */
+ reply_msg.acpted_rply.ar_verf = _null_auth;
+ reply_msg.acpted_rply.ar_results.where = resultsp;
+ reply_msg.acpted_rply.ar_results.proc = xresults;
+
+ gettimeofday(&start, NULL);
+ for (;;) {
+ /* XXX we know the other bits are still clear */
+ FD_SET(cu->cu_sock, fds);
+ tv = cu->cu_wait;
+ switch (select(cu->cu_sock+1, fds, NULL, NULL, &tv)) {
+
+ case 0:
+ timeradd(&time_waited, &cu->cu_wait, &tmp1);
+ time_waited = tmp1;
+ if (timercmp(&time_waited, &timeout, <))
+ goto send_again;
+ if (fds != &readfds)
+ free(fds);
+ return (cu->cu_error.re_status = RPC_TIMEDOUT);
+
+ case -1:
+ if (errno == EINTR) {
+ gettimeofday(&after, NULL);
+ timersub(&after, &start, &tmp1);
+ timeradd(&time_waited, &tmp1, &tmp2);
+ time_waited = tmp2;
+ if (timercmp(&time_waited, &timeout, <))
+ continue;
+ if (fds != &readfds)
+ free(fds);
+ return (cu->cu_error.re_status = RPC_TIMEDOUT);
+ }
+ cu->cu_error.re_errno = errno;
+ if (fds != &readfds)
+ free(fds);
+ return (cu->cu_error.re_status = RPC_CANTRECV);
+ }
+
+ do {
+ fromlen = sizeof(struct sockaddr);
+ inlen = recvfrom(cu->cu_sock, cu->cu_inbuf,
+ (int) cu->cu_recvsz, 0,
+ (struct sockaddr *)&from, &fromlen);
+ } while (inlen < 0 && errno == EINTR);
+ if (inlen < 0) {
+ if (errno == EWOULDBLOCK)
+ continue;
+ cu->cu_error.re_errno = errno;
+ if (fds != &readfds)
+ free(fds);
+ return (cu->cu_error.re_status = RPC_CANTRECV);
+ }
+ if (inlen < sizeof(u_int32_t))
+ continue;
+#ifdef dont_check_xid
+ /* see if reply transaction id matches sent id */
+ if (*((u_int32_t *)(cu->cu_inbuf)) != *((u_int32_t *)(cu->cu_outbuf)))
+ continue;
+#endif
+ /* we now assume we have the proper reply */
+ break;
+ }
+
+ /*
+ * now decode and validate the response
+ */
+ xdrmem_create(&reply_xdrs, cu->cu_inbuf, (u_int)inlen, XDR_DECODE);
+ ok = xdr_replymsg(&reply_xdrs, &reply_msg);
+ /* XDR_DESTROY(&reply_xdrs); save a few cycles on noop destroy */
+ if (ok) {
+ _seterr_reply(&reply_msg, &(cu->cu_error));
+ if (cu->cu_error.re_status == RPC_SUCCESS) {
+ if (! AUTH_VALIDATE(cl->cl_auth,
+ &reply_msg.acpted_rply.ar_verf)) {
+ cu->cu_error.re_status = RPC_AUTHERROR;
+ cu->cu_error.re_why = AUTH_INVALIDRESP;
+ }
+ if (reply_msg.acpted_rply.ar_verf.oa_base != NULL) {
+ xdrs->x_op = XDR_FREE;
+ (void)xdr_opaque_auth(xdrs,
+ &(reply_msg.acpted_rply.ar_verf));
+ }
+ } /* end successful completion */
+ else {
+ /* maybe our credentials need to be refreshed ... */
+ if (nrefreshes > 0 && AUTH_REFRESH(cl->cl_auth)) {
+ nrefreshes--;
+ goto call_again;
+ }
+ } /* end of unsuccessful completion */
+ } /* end of valid reply message */
+ else {
+ cu->cu_error.re_status = RPC_CANTDECODERES;
+ }
+ if (fds != &readfds)
+ free(fds);
+ return (cu->cu_error.re_status);
+}
+
+
+/*
+ * pmap_getport.c
+ * Client interface to pmap rpc service.
+ *
+ * Copyright (C) 1984, Sun Microsystems, Inc.
+ */
+
+
+static struct timeval timeout = { 1, 0 };
+static struct timeval tottimeout = { 1, 0 };
+
+/*
+ * Find the mapped port for program,version.
+ * Calls the pmap service remotely to do the lookup.
+ * Returns 0 if no map exists.
+ */
+static u_short
+__pmap_getport(address, program, version, protocol)
+ struct sockaddr_in *address;
+ u_long program;
+ u_long version;
+ u_int protocol;
+{
+ u_short port = 0;
+ int sock = -1;
+ register CLIENT *client;
+ struct pmap parms;
+
+ address->sin_port = htons(PMAPPORT);
+
+ client = clntudp_bufcreate(address, PMAPPROG,
+ PMAPVERS, timeout, &sock, RPCSMALLMSGSIZE, RPCSMALLMSGSIZE);
+ if (client != (CLIENT *)NULL) {
+ parms.pm_prog = program;
+ parms.pm_vers = version;
+ parms.pm_prot = protocol;
+ parms.pm_port = 0; /* not needed or used */
+ if (CLNT_CALL(client, PMAPPROC_GETPORT, xdr_pmap, &parms,
+ xdr_u_short, &port, tottimeout) != RPC_SUCCESS){
+ rpc_createerr.cf_stat = RPC_PMAPFAILURE;
+ clnt_geterr(client, &rpc_createerr.cf_error);
+ } else if (port == 0) {
+ rpc_createerr.cf_stat = RPC_PROGNOTREGISTERED;
+ }
+ CLNT_DESTROY(client);
+ }
+ if (sock != -1)
+ (void)close(sock);
+ address->sin_port = 0;
+ return (port);
+}
+
+/*
+ * Transmit to YPPROC_DOMAIN_NONACK, return immediately.
+ */
+static bool_t *
+ypproc_domain_nonack_2_send(domainname *argp, CLIENT *clnt)
+{
+ static bool_t clnt_res;
+ struct timeval TIMEOUT = { 0, 0 };
+
+ memset((char *)&clnt_res, 0, sizeof (clnt_res));
+ if (clnt_call(clnt, YPPROC_DOMAIN_NONACK,
+ (xdrproc_t) xdr_domainname, (caddr_t) argp,
+ (xdrproc_t) xdr_bool, (caddr_t) &clnt_res,
+ TIMEOUT) != RPC_SUCCESS) {
+ return (NULL);
+ }
+ return (&clnt_res);
+}
+
+/*
+ * Receive response from YPPROC_DOMAIN_NONACK asynchronously.
+ */
+static bool_t *
+ypproc_domain_nonack_2_recv(domainname *argp, CLIENT *clnt)
+{
+ static bool_t clnt_res;
+ struct timeval TIMEOUT = { 0, 0 };
+
+ memset((char *)&clnt_res, 0, sizeof (clnt_res));
+ if (clnt_call(clnt, YPPROC_DOMAIN_NONACK,
+ (xdrproc_t) NULL, (caddr_t) argp,
+ (xdrproc_t) xdr_bool, (caddr_t) &clnt_res,
+ TIMEOUT) != RPC_SUCCESS) {
+ return (NULL);
+ }
+ return (&clnt_res);
+}
+
+/*
+ * "We have the machine that goes 'ping!'" -- Monty Python
+ *
+ * This function blasts packets at the YPPROC_DOMAIN_NONACK procedures
+ * of the NIS servers listed in restricted_addrs structure.
+ * Whoever replies the fastest becomes our chosen server.
+ *
+ * Note: THIS IS NOT A BROADCAST OPERATION! We could use clnt_broadcast()
+ * for this, but that has the following problems:
+ * - We only get the address of the machine that replied in the
+ * 'eachresult' callback, and on multi-homed machines this can
+ * lead to confusion.
+ * - clnt_broadcast() only transmits to local networks, whereas with
+ * NIS+ you can have a perfectly good server located anywhere on or
+ * off the local network.
+ * - clnt_broadcast() blocks for an arbitrary amount of time which the
+ * caller can't control -- we want to avoid that.
+ *
+ * Also note that this has nothing to do with the NIS_PING procedure used
+ * for replica updates.
+ */
+
+struct ping_req {
+ struct sockaddr_in sin;
+ unsigned long xid;
+};
+
+int __yp_ping(restricted_addrs, cnt, dom, port)
+ struct in_addr *restricted_addrs;
+ int cnt;
+ char *dom;
+ short *port;
+{
+ struct timeval tv = { 5 , 0 };
+ struct ping_req **reqs;
+ unsigned long i;
+ struct sockaddr_in sin, *any;
+ int winner = -1;
+ time_t xid_seed, xid_lookup;
+ int sock, dontblock = 1;
+ CLIENT *clnt;
+ char *foo = dom;
+ struct cu_data *cu;
+ enum clnt_stat (*oldfunc)();
+ int validsrvs = 0;
+
+ /* Set up handles. */
+ reqs = calloc(1, sizeof(struct ping_req *) * cnt);
+ xid_seed = time(NULL) ^ getpid();
+
+ for (i = 0; i < cnt; i++) {
+ bzero((char *)&sin, sizeof(sin));
+ sin.sin_family = AF_INET;
+ bcopy((char *)&restricted_addrs[i],
+ (char *)&sin.sin_addr, sizeof(struct in_addr));
+ sin.sin_port = htons(__pmap_getport(&sin, YPPROG,
+ YPVERS, IPPROTO_UDP));
+ if (sin.sin_port == 0)
+ continue;
+ reqs[i] = calloc(1, sizeof(struct ping_req));
+ bcopy((char *)&sin, (char *)&reqs[i]->sin, sizeof(sin));
+ any = &reqs[i]->sin;
+ reqs[i]->xid = xid_seed;
+ xid_seed++;
+ validsrvs++;
+ }
+
+ /* Make sure at least one server was assigned */
+ if (!validsrvs) {
+ free(reqs);
+ return(-1);
+ }
+
+ /* Create RPC handle */
+ sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+ clnt = clntudp_create(any, YPPROG, YPVERS, tv, &sock);
+ if (clnt == NULL) {
+ close(sock);
+ for (i = 0; i < cnt; i++)
+ if (reqs[i] != NULL)
+ free(reqs[i]);
+ free(reqs);
+ return(-1);
+ }
+ clnt->cl_auth = authunix_create_default();
+ cu = (struct cu_data *)clnt->cl_private;
+ tv.tv_sec = 0;
+ clnt_control(clnt, CLSET_TIMEOUT, &tv);
+ ioctl(sock, FIONBIO, &dontblock);
+ oldfunc = clnt->cl_ops->cl_call;
+ clnt->cl_ops->cl_call = clntudp_a_call;
+
+ /* Transmit */
+ for (i = 0; i < cnt; i++) {
+ if (reqs[i] != NULL) {
+ /* subtract one; clntudp_call() will increment */
+ *((u_int32_t *)(cu->cu_outbuf)) = reqs[i]->xid - 1;
+ bcopy((char *)&reqs[i]->sin, (char *)&cu->cu_raddr,
+ sizeof(struct sockaddr_in));
+ ypproc_domain_nonack_2_send(&foo, clnt);
+ }
+ }
+
+ /* Receive reply */
+ ypproc_domain_nonack_2_recv(&foo, clnt);
+
+ /* Got a winner -- look him up. */
+ xid_lookup = *((u_int32_t *)(cu->cu_inbuf));
+ for (i = 0; i < cnt; i++) {
+ if (reqs[i] != NULL && reqs[i]->xid == xid_lookup) {
+ winner = i;
+ *port = reqs[i]->sin.sin_port;
+ }
+ }
+
+ /* Shut everything down */
+ clnt->cl_ops->cl_call = oldfunc;
+ auth_destroy(clnt->cl_auth);
+ clnt_destroy(clnt);
+ close(sock);
+
+ for (i = 0; i < cnt; i++)
+ if (reqs[i] != NULL)
+ free(reqs[i]);
+ free(reqs);
+
+ return(winner);
+}
diff --git a/usr.sbin/ypbind/yp_ping.h b/usr.sbin/ypbind/yp_ping.h
new file mode 100644
index 0000000..eadb5de
--- /dev/null
+++ b/usr.sbin/ypbind/yp_ping.h
@@ -0,0 +1 @@
+extern int __yp_ping __P(( struct in_addr *, int, char *, short * ));
diff --git a/usr.sbin/ypbind/ypbind.8 b/usr.sbin/ypbind/ypbind.8
new file mode 100644
index 0000000..3498ae4
--- /dev/null
+++ b/usr.sbin/ypbind/ypbind.8
@@ -0,0 +1,181 @@
+.\" Copyright (c) 1991, 1993, 1995
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $Id: ypbind.8,v 1.10 1997/05/25 19:49:32 wpaul Exp $
+.\"
+.Dd April 9, 1995
+.Dt YPBIND 8
+.Os
+.Sh NAME
+.Nm ypbind
+.Nd "NIS domain binding daemon"
+.Sh SYNOPSIS
+.Nm
+.Op Fl ypset
+.Op Fl ypsetme
+.Op Fl s
+.Op Fl m
+.Op Fl S Ar domainname,server1,server2,...
+.Sh DESCRIPTION
+.Nm Ypbind
+is the process that maintains NIS binding information. At startup,
+it searches for an NIS server responsible for serving the system's
+default domain (as set by the
+.Xr domainname 1
+command) using network broadcasts.
+Once it receives a reply,
+it will store the address of the server and other
+information in a special file located in
+.Pa /var/yp/binding .
+The NIS routines in the standard C library can then use this file
+when processing NIS requests. There may be several such files
+since it is possible for an NIS client to be bound to more than
+one domain.
+.Pp
+After a binding has been established,
+.Nm
+will send DOMAIN_NONACK requests to the NIS server at one minute
+intervals. If it fails to receive a reply to one of these requests,
+.Nm
+assumes that the server is no longer running and resumes its network
+broadcasts until another binding is established.
+.Nm Ypbind
+will also log warning messages using the
+.Xr syslog 3
+facility each time it detects that a server has stopped responding,
+as well as when it has bound to a new server.
+.Sh OPTIONS
+The following options are supported by
+.Nm Ns :
+.Bl -tag -width indent
+.It Fl ypset
+It is possible to force
+.Nm
+to bind to a particular NIS server host for a given domain by using the
+.Xr ypset 8
+command. However,
+.Nm
+refuses YPBINDPROC_SETDOM requests by default since it has no way of
+knowing exactly who is sending them. Using the
+.Fl ypset
+flag causes
+.Nm
+to accept YPBINDPROC_SETDOM requests from any host. This option should only
+be used for diagnostic purposes and only for limited periods since allowing
+arbitrary users to reset the binding of an NIS client poses a severe
+security risk.
+.It Fl ypsetme
+This is similar to the
+.Fl ypset
+flag, except that it only permits YPBINDPROC_SETDOM requests to be processed
+if they originated from the local host.
+.It Fl s
+Cause
+.Nm
+to run in secure mode: it will refuse to bind to any NIS server
+that is not running as root (i.e. that is not using privileged
+TCP ports).
+.It Fl S Ar domainname,server1,server2,server3,...
+Allow the system administrator to lock
+.Nm
+to a particular
+domain and group of NIS servers. Up to ten servers can be specified.
+There must not be any spaces between the commas in the domain/server
+specification. This option is used to insure that the system binds
+only to one domain and only to one of the specified servers, which
+is useful for systems that are both NIS servers and NIS
+clients: it provides a way to restrict what machines the system can
+bind to without the need for specifying the
+.Fl ypset
+or
+.Fl ypsetme
+options, which are often considered to be security holes. The specified
+servers must have valid entries in the local
+.Pa /etc/hosts
+file. IP addresses may be specified in place of hostnames. If
+.Nm
+can't make sense ouf of the arguments, it will ignore
+the
+.Fl S
+flag and continue running normally.
+.Pp
+Note that
+.Nm
+will consider the domainname specified with the
+.Fl S
+flag to be the system default domain.
+.It Fl m
+Cause
+.Nm
+to use a 'many-cast' rather than a broadcast for choosing a server
+from the restricted mode server list. In many-cast mode,
+.Nm
+will transmit directly to the YPPROC_DOMAIN_NONACK procedure of the
+servers specified in the restricted list and bind to the server that
+responds the fastest.
+This mode of operation is useful for NIS clients on remote subnets
+where no local NIS servers are available. The
+.Fl m
+flag can only be used in conjunction with the
+.Fl S
+flag above (if used without the
+.Fl S
+flag, it has no effect).
+.El
+.Sh NOTES
+The
+.Nm
+program will not make continuous attempts to keep secondary domains bound.
+If a server for a secondary domain fails to respond to a ping,
+.Nm
+will broadcast for a new server only once before giving up. If a
+client program attempts to reference the unbound domain,
+.Nm
+will try broadcasting again. By contrast,
+.Nm
+will automatically maintain a binding for the default domain whether
+client programs reference it ot not.
+.Sh FILES
+.Bl -tag -width /etc/rc.conf -compact
+.It Pa /var/yp/binding/[domainname].[version]
+the files used to hold binding information for each NIS domain
+.It Pa /etc/rc.conf
+system configuration file where the system default domain and
+ypbind startup options are specified
+.El
+.Sh SEE ALSO
+.Xr domainname 1 ,
+.Xr syslog 3 ,
+.Xr yp 4 ,
+.Xr ypserv 8 ,
+.Xr ypset 8
+.Sh AUTHOR
+.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..5c893f7
--- /dev/null
+++ b/usr.sbin/ypbind/ypbind.c
@@ -0,0 +1,1008 @@
+/*
+ * Copyright (c) 1992/3 Theo de Raadt <deraadt@fsa.ca>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/ioctl.h>
+#include <sys/signal.h>
+#include <sys/socket.h>
+#include <sys/file.h>
+#include <sys/fcntl.h>
+#include <sys/stat.h>
+#include <sys/uio.h>
+#include <ctype.h>
+#include <dirent.h>
+#include <err.h>
+#include <errno.h>
+#include <netdb.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <syslog.h>
+#include <unistd.h>
+#include <rpc/rpc.h>
+#include <rpc/xdr.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <rpc/pmap_clnt.h>
+#include <rpc/pmap_prot.h>
+#include <rpc/pmap_rmt.h>
+#include <rpc/rpc_com.h>
+#include <rpcsvc/yp.h>
+struct dom_binding{};
+#include <rpcsvc/ypclnt.h>
+#include "yp_ping.h"
+
+#ifndef BINDINGDIR
+#define BINDINGDIR "/var/yp/binding"
+#endif
+
+#ifndef YPBINDLOCK
+#define YPBINDLOCK "/var/run/ypbind.lock"
+#endif
+
+struct _dom_binding {
+ struct _dom_binding *dom_pnext;
+ char dom_domain[YPMAXDOMAIN + 1];
+ struct sockaddr_in dom_server_addr;
+ long int dom_vers;
+ int dom_lockfd;
+ int dom_alive;
+ int dom_broadcast_pid;
+ int dom_pipe_fds[2];
+ int dom_default;
+};
+
+#define READFD ypdb->dom_pipe_fds[0]
+#define WRITEFD ypdb->dom_pipe_fds[1]
+#define BROADFD broad_domain->dom_pipe_fds[1]
+
+extern bool_t xdr_domainname(), xdr_ypbind_resp();
+extern bool_t xdr_ypreq_key(), xdr_ypresp_val();
+extern bool_t xdr_ypbind_setdom();
+
+void checkwork __P((void));
+void *ypbindproc_null_2 __P((SVCXPRT *, void *, CLIENT *));
+void *ypbindproc_setdom_2 __P((SVCXPRT *, struct ypbind_setdom *, CLIENT *));
+void rpc_received __P((char *, struct sockaddr_in *, int ));
+void broadcast __P((struct _dom_binding *));
+int ping __P((struct _dom_binding *));
+int tell_parent __P((char *, struct sockaddr_in *));
+void handle_children __P(( struct _dom_binding * ));
+void reaper __P((int));
+void terminate __P((int));
+void yp_restricted_mode __P((char *));
+int verify __P((struct in_addr));
+
+char *domain_name;
+struct _dom_binding *ypbindlist;
+static struct _dom_binding *broad_domain;
+
+#define YPSET_NO 0
+#define YPSET_LOCAL 1
+#define YPSET_ALL 2
+int ypsetmode = YPSET_NO;
+int ypsecuremode = 0;
+int ppid;
+
+/*
+ * Special restricted mode variables: when in restricted mode, only the
+ * specified restricted_domain will be bound, and only the servers listed
+ * in restricted_addrs will be used for binding.
+ */
+#define RESTRICTED_SERVERS 10
+int yp_restricted = 0;
+int yp_manycast = 0;
+struct in_addr restricted_addrs[RESTRICTED_SERVERS];
+
+/* No more than MAX_CHILDREN child broadcasters at a time. */
+#ifndef MAX_CHILDREN
+#define MAX_CHILDREN 5
+#endif
+/* No more than MAX_DOMAINS simultaneous domains */
+#ifndef MAX_DOMAINS
+#define MAX_DOMAINS 200
+#endif
+/* RPC timeout value */
+#ifndef FAIL_THRESHOLD
+#define FAIL_THRESHOLD 20
+#endif
+
+/* Number of times to fish for a response froma particular set of hosts */
+#ifndef MAX_RETRIES
+#define MAX_RETRIES 30
+#endif
+
+int retries = 0;
+int children = 0;
+int domains = 0;
+int yplockfd;
+fd_set fdsr;
+
+SVCXPRT *udptransp, *tcptransp;
+
+void *
+ypbindproc_null_2(transp, argp, clnt)
+SVCXPRT *transp;
+void *argp;
+CLIENT *clnt;
+{
+ static char res;
+
+ bzero((char *)&res, sizeof(res));
+ return (void *)&res;
+}
+
+struct ypbind_resp *
+ypbindproc_domain_2(transp, argp, clnt)
+SVCXPRT *transp;
+domainname *argp;
+CLIENT *clnt;
+{
+ static struct ypbind_resp res;
+ struct _dom_binding *ypdb;
+ char path[MAXPATHLEN];
+
+ bzero((char *)&res, sizeof res);
+ res.ypbind_status = YPBIND_FAIL_VAL;
+ res.ypbind_resp_u.ypbind_error = YPBIND_ERR_NOSERV;
+
+ if (strchr(*argp, '/')) {
+ syslog(LOG_WARNING, "Domain name '%s' has embedded slash -- \
+rejecting.", *argp);
+ return(&res);
+ }
+
+ for(ypdb=ypbindlist; ypdb; ypdb=ypdb->dom_pnext) {
+ if( strcmp(ypdb->dom_domain, *argp) == 0)
+ break;
+ }
+
+ if(ypdb==NULL) {
+ if (yp_restricted) {
+ syslog(LOG_NOTICE, "Running in restricted mode -- request to bind domain \"%s\" rejected.\n", *argp);
+ return &res;
+ }
+
+ if (domains >= MAX_DOMAINS) {
+ syslog(LOG_WARNING, "domain limit (%d) exceeded",
+ MAX_DOMAINS);
+ res.ypbind_resp_u.ypbind_error = YPBIND_ERR_RESC;
+ return &res;
+ }
+ ypdb = (struct _dom_binding *)malloc(sizeof *ypdb);
+ if (ypdb == NULL) {
+ syslog(LOG_WARNING, "malloc: %m");
+ res.ypbind_resp_u.ypbind_error = YPBIND_ERR_RESC;
+ return &res;
+ }
+ bzero((char *)ypdb, sizeof *ypdb);
+ strncpy(ypdb->dom_domain, *argp, sizeof ypdb->dom_domain);
+ ypdb->dom_vers = YPVERS;
+ ypdb->dom_alive = 0;
+ ypdb->dom_default = 0;
+ ypdb->dom_lockfd = -1;
+ sprintf(path, "%s/%s.%ld", BINDINGDIR,
+ ypdb->dom_domain, ypdb->dom_vers);
+ unlink(path);
+ ypdb->dom_pnext = ypbindlist;
+ ypbindlist = ypdb;
+ domains++;
+ }
+
+ if (ping(ypdb)) {
+ return &res;
+ }
+
+ res.ypbind_status = YPBIND_SUCC_VAL;
+ res.ypbind_resp_u.ypbind_error = 0; /* Success */
+ *(u_long *)&res.ypbind_resp_u.ypbind_bindinfo.ypbind_binding_addr =
+ ypdb->dom_server_addr.sin_addr.s_addr;
+ *(u_short *)&res.ypbind_resp_u.ypbind_bindinfo.ypbind_binding_port =
+ ypdb->dom_server_addr.sin_port;
+ /*printf("domain %s at %s/%d\n", ypdb->dom_domain,
+ inet_ntoa(ypdb->dom_server_addr.sin_addr),
+ ntohs(ypdb->dom_server_addr.sin_port));*/
+ return &res;
+}
+
+void *
+ypbindproc_setdom_2(transp, argp, clnt)
+SVCXPRT *transp;
+ypbind_setdom *argp;
+CLIENT *clnt;
+{
+ struct sockaddr_in *fromsin, bindsin;
+
+ if (strchr(argp->ypsetdom_domain, '/')) {
+ syslog(LOG_WARNING, "Domain name '%s' has embedded slash -- \
+rejecting.", argp->ypsetdom_domain);
+ return(NULL);
+ }
+ fromsin = svc_getcaller(transp);
+
+ switch(ypsetmode) {
+ case YPSET_LOCAL:
+ if( fromsin->sin_addr.s_addr != htonl(INADDR_LOOPBACK)) {
+ svcerr_noprog(transp);
+ return(NULL);
+ }
+ break;
+ case YPSET_ALL:
+ break;
+ case YPSET_NO:
+ default:
+ svcerr_noprog(transp);
+ return(NULL);
+ }
+
+ if(ntohs(fromsin->sin_port) >= IPPORT_RESERVED) {
+ svcerr_noprog(transp);
+ return(NULL);
+ }
+
+ if(argp->ypsetdom_vers != YPVERS) {
+ svcerr_noprog(transp);
+ return(NULL);
+ }
+
+ bzero((char *)&bindsin, sizeof bindsin);
+ bindsin.sin_family = AF_INET;
+ bindsin.sin_addr.s_addr = *(u_long *)argp->ypsetdom_binding.ypbind_binding_addr;
+ bindsin.sin_port = *(u_short *)argp->ypsetdom_binding.ypbind_binding_port;
+ rpc_received(argp->ypsetdom_domain, &bindsin, 1);
+
+ return(NULL);
+}
+
+static void
+ypbindprog_2(rqstp, transp)
+struct svc_req *rqstp;
+register SVCXPRT *transp;
+{
+ union {
+ domainname ypbindproc_domain_2_arg;
+ struct ypbind_setdom ypbindproc_setdom_2_arg;
+ } argument;
+ struct authunix_parms *creds;
+ char *result;
+ bool_t (*xdr_argument)(), (*xdr_result)();
+ char *(*local)();
+
+ switch (rqstp->rq_proc) {
+ case YPBINDPROC_NULL:
+ xdr_argument = xdr_void;
+ xdr_result = xdr_void;
+ local = (char *(*)()) ypbindproc_null_2;
+ break;
+
+ case YPBINDPROC_DOMAIN:
+ xdr_argument = xdr_domainname;
+ xdr_result = xdr_ypbind_resp;
+ local = (char *(*)()) ypbindproc_domain_2;
+ 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;
+ break;
+
+ default:
+ svcerr_noproc(transp);
+ return;
+ }
+ bzero((char *)&argument, sizeof(argument));
+ if (!svc_getargs(transp, xdr_argument, (caddr_t)&argument)) {
+ svcerr_decode(transp);
+ return;
+ }
+ result = (*local)(transp, &argument, rqstp);
+ if (result != NULL && !svc_sendreply(transp, xdr_result, result)) {
+ svcerr_systemerr(transp);
+ }
+ return;
+}
+
+/* Jack the reaper */
+void reaper(sig)
+int sig;
+{
+ int st;
+
+ while(wait3(&st, WNOHANG, NULL) > 0)
+ children--;
+}
+
+void terminate(sig)
+int sig;
+{
+ struct _dom_binding *ypdb;
+ char path[MAXPATHLEN];
+
+ if (ppid != getpid())
+ exit(0);
+
+ for(ypdb=ypbindlist; ypdb; ypdb=ypdb->dom_pnext) {
+ close(ypdb->dom_lockfd);
+ if (ypdb->dom_broadcast_pid)
+ kill(ypdb->dom_broadcast_pid, SIGINT);
+ sprintf(path, "%s/%s.%ld", BINDINGDIR,
+ ypdb->dom_domain, ypdb->dom_vers);
+ unlink(path);
+ }
+ close(yplockfd);
+ unlink(YPBINDLOCK);
+ pmap_unset(YPBINDPROG, YPBINDVERS);
+ exit(0);
+}
+
+int
+main(argc, argv)
+int argc;
+char **argv;
+{
+ struct timeval tv;
+ int i;
+ DIR *dird;
+ struct dirent *dirp;
+ struct _dom_binding *ypdb;
+
+ /* Check that another ypbind isn't already running. */
+ if ((yplockfd = (open(YPBINDLOCK, O_RDONLY|O_CREAT, 0444))) == -1)
+ err(1, "%s", YPBINDLOCK);
+
+ if(flock(yplockfd, LOCK_EX|LOCK_NB) == -1 && errno == EWOULDBLOCK)
+ errx(1, "another ypbind is already running. Aborting");
+
+ /* XXX domainname will be overriden if we use restricted mode */
+ yp_get_default_domain(&domain_name);
+ if( domain_name[0] == '\0')
+ errx(1, "domainname not set. Aborting");
+
+ for(i=1; i<argc; i++) {
+ if( strcmp("-ypset", argv[i]) == 0)
+ ypsetmode = YPSET_ALL;
+ else if (strcmp("-ypsetme", argv[i]) == 0)
+ ypsetmode = YPSET_LOCAL;
+ else if (strcmp("-s", argv[i]) == 0)
+ ypsecuremode++;
+ else if (strcmp("-S", argv[i]) == 0 && argc > i)
+ yp_restricted_mode(argv[i+1]);
+ else if (strcmp("-m", argv[i]) == 0)
+ yp_manycast++;
+ }
+
+ /* blow away everything in BINDINGDIR (if it exists) */
+
+ if ((dird = opendir(BINDINGDIR)) != NULL) {
+ char path[MAXPATHLEN];
+ while ((dirp = readdir(dird)) != NULL)
+ if (strcmp(dirp->d_name, ".") &&
+ strcmp(dirp->d_name, "..")) {
+ sprintf(path,"%s/%s",BINDINGDIR,dirp->d_name);
+ unlink(path);
+ }
+ closedir(dird);
+ }
+
+#ifdef DAEMON
+ if (daemon(0,0))
+ err(1, "fork");
+#endif
+
+ pmap_unset(YPBINDPROG, YPBINDVERS);
+
+ udptransp = svcudp_create(RPC_ANYSOCK);
+ if (udptransp == NULL)
+ errx(1, "cannot create udp service");
+ if (!svc_register(udptransp, YPBINDPROG, YPBINDVERS, ypbindprog_2,
+ IPPROTO_UDP))
+ errx(1, "unable to register (YPBINDPROG, YPBINDVERS, udp)");
+
+ tcptransp = svctcp_create(RPC_ANYSOCK, 0, 0);
+ if (tcptransp == NULL)
+ errx(1, "cannot create tcp service");
+
+ if (!svc_register(tcptransp, YPBINDPROG, YPBINDVERS, ypbindprog_2,
+ IPPROTO_TCP))
+ errx(1, "unable to register (YPBINDPROG, YPBINDVERS, tcp)");
+
+ /* build initial domain binding, make it "unsuccessful" */
+ ypbindlist = (struct _dom_binding *)malloc(sizeof *ypbindlist);
+ if (ypbindlist == NULL)
+ errx(1, "malloc");
+ bzero((char *)ypbindlist, sizeof *ypbindlist);
+ strncpy(ypbindlist->dom_domain, domain_name, sizeof ypbindlist->dom_domain);
+ ypbindlist->dom_vers = YPVERS;
+ ypbindlist->dom_alive = 0;
+ ypbindlist->dom_lockfd = -1;
+ ypbindlist->dom_default = 1;
+ domains++;
+
+ signal(SIGCHLD, reaper);
+ signal(SIGTERM, terminate);
+
+ ppid = getpid(); /* Remember who we are. */
+
+ openlog(argv[0], LOG_PID, LOG_DAEMON);
+
+ /* Kick off the default domain */
+ broadcast(ypbindlist);
+
+ while(1) {
+ fdsr = svc_fdset;
+
+ tv.tv_sec = 60;
+ tv.tv_usec = 0;
+
+ switch(select(_rpc_dtablesize(), &fdsr, NULL, NULL, &tv)) {
+ case 0:
+ checkwork();
+ break;
+ case -1:
+ if (errno != EINTR)
+ syslog(LOG_WARNING, "select: %m");
+ break;
+ default:
+ for(ypdb=ypbindlist; ypdb; ypdb=ypdb->dom_pnext) {
+ if (READFD > 0 && FD_ISSET(READFD, &fdsr)) {
+ handle_children(ypdb);
+ if (children == (MAX_CHILDREN - 1))
+ checkwork();
+ }
+ }
+ svc_getreqset(&fdsr);
+ break;
+ }
+ }
+
+ /* NOTREACHED */
+ exit(1);
+}
+
+void
+checkwork()
+{
+ struct _dom_binding *ypdb;
+
+ for(ypdb=ypbindlist; ypdb; ypdb=ypdb->dom_pnext)
+ ping(ypdb);
+}
+
+/* The clnt_broadcast() callback mechanism sucks. */
+
+/*
+ * Receive results from broadcaster. Don't worry about passing
+ * bogus info to rpc_received() -- it can handle it. Note that we
+ * must be sure to invalidate the dom_pipe_fds descriptors here:
+ * since descriptors can be re-used, we have to make sure we
+ * don't mistake one of the RPC descriptors for one of the pipes.
+ * What's weird is that forgetting to invalidate the pipe descriptors
+ * doesn't always result in an error (otherwise I would have caught
+ * the mistake much sooner), even though logically it should.
+ */
+void handle_children(ypdb)
+struct _dom_binding *ypdb;
+{
+ char buf[YPMAXDOMAIN + 1];
+ struct sockaddr_in addr;
+ int d = 0, a = 0;
+ struct _dom_binding *y, *prev = NULL;
+ char path[MAXPATHLEN];
+
+ if ((d = read(READFD, &buf, sizeof(buf))) <= 0)
+ syslog(LOG_WARNING, "could not read from child: %m");
+
+ if ((a = read(READFD, &addr, sizeof(struct sockaddr_in))) < 0)
+ syslog(LOG_WARNING, "could not read from child: %m");
+
+ close(READFD);
+ FD_CLR(READFD, &fdsr);
+ FD_CLR(READFD, &svc_fdset);
+ READFD = WRITEFD = -1;
+ if (d > 0 && a > 0)
+ rpc_received((char *)&buf, &addr, 0);
+ else {
+ for(y=ypbindlist; y; y=y->dom_pnext) {
+ if (y == ypdb)
+ break;
+ prev = y;
+ }
+ switch(ypdb->dom_default) {
+ case 0:
+ if (prev == NULL)
+ ypbindlist = y->dom_pnext;
+ else
+ prev->dom_pnext = y->dom_pnext;
+ sprintf(path, "%s/%s.%ld", BINDINGDIR,
+ ypdb->dom_domain, YPVERS);
+ close(ypdb->dom_lockfd);
+ unlink(path);
+ free(ypdb);
+ domains--;
+ return;
+ case 1:
+ ypdb->dom_broadcast_pid = 0;
+ ypdb->dom_alive = 0;
+ broadcast(ypdb);
+ return;
+ default:
+ break;
+ }
+ }
+
+ return;
+}
+
+/*
+ * Send our dying words back to our parent before we perish.
+ */
+int
+tell_parent(dom, addr)
+char *dom;
+struct sockaddr_in *addr;
+{
+ char buf[YPMAXDOMAIN + 1];
+ struct timeval timeout;
+ fd_set fds;
+
+ timeout.tv_sec = 5;
+ timeout.tv_usec = 0;
+
+ sprintf(buf, "%s", broad_domain->dom_domain);
+ if (write(BROADFD, &buf, sizeof(buf)) < 0)
+ return(1);
+
+ /*
+ * Stay in sync with parent: wait for it to read our first
+ * message before sending the second.
+ */
+
+ FD_ZERO(&fds);
+ FD_SET(BROADFD, &fds);
+ if (select(FD_SETSIZE, NULL, &fds, NULL, &timeout) == -1)
+ return(1);
+ if (FD_ISSET(BROADFD, &fds)) {
+ if (write(BROADFD, addr, sizeof(struct sockaddr_in)) < 0)
+ return(1);
+ } else {
+ return(1);
+ }
+
+ close(BROADFD);
+ return (0);
+}
+
+bool_t broadcast_result(out, addr)
+bool_t *out;
+struct sockaddr_in *addr;
+{
+ if (retries >= MAX_RETRIES) {
+ bzero((char *)addr, sizeof(struct sockaddr_in));
+ if (tell_parent(broad_domain->dom_domain, addr))
+ syslog(LOG_WARNING, "lost connection to parent");
+ return TRUE;
+ }
+
+ if (yp_restricted && verify(addr->sin_addr)) {
+ retries++;
+ syslog(LOG_NOTICE, "NIS server at %s not in restricted mode access list -- rejecting.\n",inet_ntoa(addr->sin_addr));
+ return FALSE;
+ } else {
+ if (tell_parent(broad_domain->dom_domain, addr))
+ syslog(LOG_WARNING, "lost connection to parent");
+ return TRUE;
+ }
+}
+
+/*
+ * The right way to send RPC broadcasts.
+ * Use the clnt_broadcast() RPC service. Unfortunately, clnt_broadcast()
+ * blocks while waiting for replies, so we have to fork off seperate
+ * broadcaster processes that do the waiting and then transmit their
+ * results back to the parent for processing. We also have to remember
+ * to save the name of the domain we're trying to bind in a global
+ * variable since clnt_broadcast() provides no way to pass things to
+ * the 'eachresult' callback function.
+ */
+void
+broadcast(ypdb)
+struct _dom_binding *ypdb;
+{
+ bool_t out = FALSE;
+ enum clnt_stat stat;
+
+ if (children >= MAX_CHILDREN || ypdb->dom_broadcast_pid)
+ return;
+
+ if (pipe(ypdb->dom_pipe_fds) < 0) {
+ syslog(LOG_WARNING, "pipe: %m");
+ return;
+ }
+
+ if (ypdb->dom_vers == -1 && (long)ypdb->dom_server_addr.sin_addr.s_addr)
+ syslog(LOG_WARNING, "NIS server [%s] for domain \"%s\" not responding",
+ inet_ntoa(ypdb->dom_server_addr.sin_addr), ypdb->dom_domain);
+
+ broad_domain = ypdb;
+ flock(ypdb->dom_lockfd, LOCK_UN);
+
+ switch((ypdb->dom_broadcast_pid = fork())) {
+ case 0:
+ close(READFD);
+ signal(SIGCHLD, SIG_DFL);
+ signal(SIGTERM, SIG_DFL);
+ break;
+ case -1:
+ syslog(LOG_WARNING, "fork: %m");
+ close(READFD);
+ close(WRITEFD);
+ return;
+ default:
+ close(WRITEFD);
+ FD_SET(READFD, &svc_fdset);
+ children++;
+ return;
+ }
+
+ /* Release all locks before doing anything else. */
+ while(ypbindlist) {
+ close(ypbindlist->dom_lockfd);
+ ypbindlist = ypbindlist->dom_pnext;
+ }
+ close(yplockfd);
+
+ /*
+ * Special 'many-cast' behavior. If we're in restricted mode,
+ * we have a list of possible server addresses to try. What
+ * we can do is transmit to each ypserv's YPPROC_DOMAIN_NONACK
+ * procedure and time the replies. Whoever replies fastest
+ * gets to be our server. Note that this is not a broadcast
+ * operation: we transmit uni-cast datagrams only.
+ */
+ if (yp_restricted && yp_manycast) {
+ short port;
+ int i;
+ struct sockaddr_in sin;
+
+ i = __yp_ping(restricted_addrs, yp_restricted,
+ ypdb->dom_domain, &port);
+ if (i == -1) {
+ bzero((char *)&ypdb->dom_server_addr,
+ sizeof(struct sockaddr_in));
+ if (tell_parent(ypdb->dom_domain,
+ &ypdb->dom_server_addr))
+ syslog(LOG_WARNING, "lost connection to parent");
+ } else {
+ bzero((char *)&sin, sizeof(struct sockaddr_in));
+ bcopy((char *)&restricted_addrs[i],
+ (char *)&sin.sin_addr, sizeof(struct in_addr));
+ sin.sin_family = AF_INET;
+ sin.sin_port = port;
+ if (tell_parent(broad_domain->dom_domain, &sin))
+ syslog(LOG_WARNING,
+ "lost connection to parent");
+ }
+ _exit(0);
+ }
+
+ retries = 0;
+
+ {
+ char *ptr;
+
+ ptr = (char *)&ypdb->dom_domain;
+ stat = clnt_broadcast(YPPROG, YPVERS, YPPROC_DOMAIN_NONACK,
+ xdr_domainname, (char *)&ptr, xdr_bool, (char *)&out,
+ broadcast_result);
+ }
+
+ if (stat != RPC_SUCCESS) {
+ bzero((char *)&ypdb->dom_server_addr,
+ sizeof(struct sockaddr_in));
+ if (tell_parent(ypdb->dom_domain, &ypdb->dom_server_addr))
+ syslog(LOG_WARNING, "lost connection to parent");
+ }
+
+ _exit(0);
+}
+
+/*
+ * The right way to check if a server is alive.
+ * Attempt to get a client handle pointing to the server and send a
+ * YPPROC_DOMAIN. If we can't get a handle or we get a reply of FALSE,
+ * we invalidate this binding entry and send out a broadcast to try to
+ * establish a new binding. Note that we treat non-default domains
+ * specially: once bound, we keep tabs on our server, but if it
+ * goes away and fails to respond after one round of broadcasting, we
+ * abandon it until a client specifically references it again. We make
+ * every effort to keep our default domain bound, however, since we
+ * need it to keep the system on its feet.
+ */
+int
+ping(ypdb)
+struct _dom_binding *ypdb;
+{
+ bool_t out;
+ struct timeval interval, timeout;
+ enum clnt_stat stat;
+ int rpcsock = RPC_ANYSOCK;
+ CLIENT *client_handle;
+
+ interval.tv_sec = FAIL_THRESHOLD;
+ interval.tv_usec = 0;
+ timeout.tv_sec = FAIL_THRESHOLD;
+ timeout.tv_usec = 0;
+
+ if (ypdb->dom_broadcast_pid)
+ return(1);
+
+ if ((client_handle = clntudp_bufcreate(&ypdb->dom_server_addr,
+ YPPROG, YPVERS, interval, &rpcsock, RPCSMALLMSGSIZE,
+ RPCSMALLMSGSIZE)) == (CLIENT *)NULL) {
+ /* Can't get a handle: we're dead. */
+ ypdb->dom_alive = 0;
+ ypdb->dom_vers = -1;
+ broadcast(ypdb);
+ return(1);
+ }
+
+ {
+ char *ptr;
+
+ ptr = (char *)&ypdb->dom_domain;
+
+ if ((stat = clnt_call(client_handle, YPPROC_DOMAIN,
+ xdr_domainname, (char *)&ptr, xdr_bool, (char *)&out,
+ timeout)) != RPC_SUCCESS || out == FALSE) {
+ ypdb->dom_alive = 0;
+ ypdb->dom_vers = -1;
+ clnt_destroy(client_handle);
+ broadcast(ypdb);
+ return(1);
+ }
+ }
+
+ clnt_destroy(client_handle);
+ return(0);
+}
+
+void rpc_received(dom, raddrp, force)
+char *dom;
+struct sockaddr_in *raddrp;
+int force;
+{
+ struct _dom_binding *ypdb, *prev = NULL;
+ struct iovec iov[2];
+ struct ypbind_resp ybr;
+ char path[MAXPATHLEN];
+ int fd;
+
+ /*printf("returned from %s/%d about %s\n", inet_ntoa(raddrp->sin_addr),
+ ntohs(raddrp->sin_port), dom);*/
+
+ if(dom==NULL)
+ return;
+
+ for(ypdb=ypbindlist; ypdb; ypdb=ypdb->dom_pnext) {
+ if( strcmp(ypdb->dom_domain, dom) == 0)
+ break;
+ prev = ypdb;
+ }
+
+ if (ypdb && force) {
+ if (ypdb->dom_broadcast_pid) {
+ kill(ypdb->dom_broadcast_pid, SIGINT);
+ close(READFD);
+ FD_CLR(READFD, &fdsr);
+ FD_CLR(READFD, &svc_fdset);
+ READFD = WRITEFD = -1;
+ }
+ }
+
+ /* if in secure mode, check originating port number */
+ if ((ypsecuremode && (ntohs(raddrp->sin_port) >= IPPORT_RESERVED))) {
+ syslog(LOG_WARNING, "Rejected NIS server on [%s/%d] for domain %s.",
+ inet_ntoa(raddrp->sin_addr), ntohs(raddrp->sin_port),
+ dom);
+ if (ypdb != NULL) {
+ ypdb->dom_broadcast_pid = 0;
+ ypdb->dom_alive = 0;
+ }
+ return;
+ }
+
+ if (raddrp->sin_addr.s_addr == (long)0) {
+ switch(ypdb->dom_default) {
+ case 0:
+ if (prev == NULL)
+ ypbindlist = ypdb->dom_pnext;
+ else
+ prev->dom_pnext = ypdb->dom_pnext;
+ sprintf(path, "%s/%s.%ld", BINDINGDIR,
+ ypdb->dom_domain, YPVERS);
+ close(ypdb->dom_lockfd);
+ unlink(path);
+ free(ypdb);
+ domains--;
+ return;
+ case 1:
+ ypdb->dom_broadcast_pid = 0;
+ ypdb->dom_alive = 0;
+ broadcast(ypdb);
+ return;
+ default:
+ break;
+ }
+ }
+
+ if(ypdb==NULL) {
+ if (force == 0)
+ return;
+ ypdb = (struct _dom_binding *)malloc(sizeof *ypdb);
+ if (ypdb == NULL) {
+ syslog(LOG_WARNING, "malloc: %m");
+ return;
+ }
+ bzero((char *)ypdb, sizeof *ypdb);
+ strncpy(ypdb->dom_domain, dom, sizeof ypdb->dom_domain);
+ ypdb->dom_lockfd = -1;
+ ypdb->dom_default = 0;
+ ypdb->dom_pnext = ypbindlist;
+ ypbindlist = ypdb;
+ }
+
+ /* We've recovered from a crash: inform the world. */
+ if (ypdb->dom_vers == -1 && ypdb->dom_server_addr.sin_addr.s_addr)
+ syslog(LOG_WARNING, "NIS server [%s] for domain \"%s\" OK",
+ inet_ntoa(raddrp->sin_addr), ypdb->dom_domain);
+
+ bcopy((char *)raddrp, (char *)&ypdb->dom_server_addr,
+ sizeof ypdb->dom_server_addr);
+
+ ypdb->dom_vers = YPVERS;
+ ypdb->dom_alive = 1;
+ ypdb->dom_broadcast_pid = 0;
+
+ if(ypdb->dom_lockfd != -1)
+ close(ypdb->dom_lockfd);
+
+ sprintf(path, "%s/%s.%ld", BINDINGDIR,
+ ypdb->dom_domain, ypdb->dom_vers);
+#ifdef O_SHLOCK
+ if( (fd=open(path, O_CREAT|O_SHLOCK|O_RDWR|O_TRUNC, 0644)) == -1) {
+ (void)mkdir(BINDINGDIR, 0755);
+ if( (fd=open(path, O_CREAT|O_SHLOCK|O_RDWR|O_TRUNC, 0644)) == -1)
+ return;
+ }
+#else
+ if( (fd=open(path, O_CREAT|O_RDWR|O_TRUNC, 0644)) == -1) {
+ (void)mkdir(BINDINGDIR, 0755);
+ if( (fd=open(path, O_CREAT|O_RDWR|O_TRUNC, 0644)) == -1)
+ return;
+ }
+ flock(fd, LOCK_SH);
+#endif
+
+ /*
+ * ok, if BINDINGDIR exists, and we can create the binding file,
+ * then write to it..
+ */
+ ypdb->dom_lockfd = fd;
+
+ iov[0].iov_base = (caddr_t)&(udptransp->xp_port);
+ iov[0].iov_len = sizeof udptransp->xp_port;
+ iov[1].iov_base = (caddr_t)&ybr;
+ iov[1].iov_len = sizeof ybr;
+
+ bzero(&ybr, sizeof ybr);
+ ybr.ypbind_status = YPBIND_SUCC_VAL;
+ *(u_long *)&ybr.ypbind_resp_u.ypbind_bindinfo.ypbind_binding_addr = raddrp->sin_addr.s_addr;
+ *(u_short *)&ybr.ypbind_resp_u.ypbind_bindinfo.ypbind_binding_port = raddrp->sin_port;
+
+ if( writev(ypdb->dom_lockfd, iov, 2) != iov[0].iov_len + iov[1].iov_len) {
+ syslog(LOG_WARNING, "write: %m");
+ close(ypdb->dom_lockfd);
+ ypdb->dom_lockfd = -1;
+ return;
+ }
+}
+
+/*
+ * Check address against list of allowed servers. Return 0 if okay,
+ * 1 if not matched.
+ */
+int
+verify(addr)
+struct in_addr addr;
+{
+ int i;
+
+ for (i = 0; i < RESTRICTED_SERVERS; i++)
+ if (!bcmp((char *)&addr, (char *)&restricted_addrs[i],
+ sizeof(struct in_addr)))
+ return(0);
+
+ return(1);
+}
+
+/*
+ * Try to set restricted mode. We default to normal mode if we can't
+ * resolve the specified hostnames.
+ */
+void
+yp_restricted_mode(args)
+char *args;
+{
+ struct hostent *h;
+ int i = 0;
+ char *s;
+
+ /* Find the restricted domain. */
+ if ((s = strsep(&args, ",")) == NULL)
+ return;
+ domain_name = s;
+
+ /* Get the addresses of the servers. */
+ while ((s = strsep(&args, ",")) != NULL && i < RESTRICTED_SERVERS) {
+ if ((h = gethostbyname(s)) == NULL)
+ return;
+ bcopy ((char *)h->h_addr_list[0], (char *)&restricted_addrs[i],
+ sizeof(struct in_addr));
+ i++;
+ }
+
+ /* ypset and ypsetme not allowed with restricted mode */
+ ypsetmode = YPSET_NO;
+
+ yp_restricted = i;
+ return;
+}
diff --git a/usr.sbin/yppoll/Makefile b/usr.sbin/yppoll/Makefile
new file mode 100644
index 0000000..7d4a553
--- /dev/null
+++ b/usr.sbin/yppoll/Makefile
@@ -0,0 +1,7 @@
+# from: @(#)Makefile 5.8 (Berkeley) 7/28/90
+# $Id: Makefile,v 1.3 1997/02/22 16:14:58 peter Exp $
+
+PROG= yppoll
+MAN8= yppoll.8
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/yppoll/yppoll.8 b/usr.sbin/yppoll/yppoll.8
new file mode 100644
index 0000000..84219d3
--- /dev/null
+++ b/usr.sbin/yppoll/yppoll.8
@@ -0,0 +1,82 @@
+.\" $NetBSD: yppoll.8,v 1.4 1997/07/30 22:55:00 jtc Exp $
+.\"
+.\" Copyright (c) 1996 The NetBSD Foundation, Inc.
+.\" All rights reserved.
+.\"
+.\" This code is derived from software contributed to The NetBSD Foundation
+.\" by Jason R. Thorpe.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the NetBSD
+.\" Foundation, Inc. and its contributors.
+.\" 4. Neither the name of The NetBSD Foundation nor the names of its
+.\" contributors may be used to endorse or promote products derived
+.\" from this software without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+.\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+.\" TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+.\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+.\" BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+.\" POSSIBILITY OF SUCH DAMAGE.
+.\"
+.\" $Id: yppoll.8,v 1.1 1997/09/21 11:49:10 wosch Exp $
+.\"
+.Dd October 25, 1994
+.Dt YPPOLL 8
+.Os
+.Sh NAME
+.Nm yppoll
+.Nd ask version of YP map from YP server
+.Sh SYNOPSIS
+.Nm yppoll
+.Op Fl h Ar host
+.Op Fl d Ar domain
+.Ar mapname
+.Sh DESCRIPTION
+.Nm Yppoll
+asks a YP server process for the order number and which host is the master
+server for
+.Ar mapname .
+.Pp
+The options are as follows:
+.Bl -tag -width indent
+.It Fl h Ar host
+Ask the YP server process running on
+.Ar host
+for information about
+.Ar mapname .
+If
+.Ar host
+is not specified, the server polled is the default server returned by
+.Xr ypwhich 1 .
+.It Fl d Ar domain
+Use the YP domain
+.Ar domain
+instead of the default domain as returned by
+.Xr domainname 1 .
+.El
+.Sh SEE ALSO
+.Xr domainname 1 ,
+.Xr ypcat 1 ,
+.Xr ypmatch 1 ,
+.Xr ypwhich 1 ,
+.Xr yp 4 ,
+.Xr ypbind 8 ,
+.Xr ypset 8
+.Sh AUTHORS
+.An Theo De Raadt and An John Brezak
diff --git a/usr.sbin/yppoll/yppoll.c b/usr.sbin/yppoll/yppoll.c
new file mode 100644
index 0000000..3f1a305
--- /dev/null
+++ b/usr.sbin/yppoll/yppoll.c
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 1992/3 Theo de Raadt <deraadt@fsa.ca>
+ * Copyright (c) 1992/3 John Brezak
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <err.h>
+#include <stdio.h>
+#include <time.h>
+#include <unistd.h>
+
+#include <rpc/rpc.h>
+#include <rpc/xdr.h>
+#include <rpcsvc/yp_prot.h>
+#include <rpcsvc/ypclnt.h>
+
+static void
+usage()
+{
+ fprintf(stderr, "usage: yppoll [-h host] [-d domainname] mapname\n");
+ exit(1);
+}
+
+int
+main(argc, argv)
+char **argv;
+{
+ char *domainname;
+ char *hostname = "localhost";
+ char *inmap, *master;
+ int order;
+ int c, r;
+
+ yp_get_default_domain(&domainname);
+
+ while( (c=getopt(argc, argv, "h:d:?")) != -1)
+ switch(c) {
+ case 'd':
+ domainname = optarg;
+ break;
+ case 'h':
+ hostname = optarg;
+ break;
+ case '?':
+ usage();
+ /*NOTREACHED*/
+ }
+
+ if(optind + 1 != argc )
+ usage();
+
+ inmap = argv[optind];
+
+ r = yp_order(domainname, inmap, &order);
+ if (r != 0)
+ errx(1, "no such map %s. Reason: %s", inmap, yperr_string(r));
+ printf("Map %s has order number %d. %s", inmap, order,
+ ctime((time_t *)&order));
+ r = yp_master(domainname, inmap, &master);
+ if (r != 0)
+ errx(1, "no such map %s. Reason: %s", inmap, yperr_string(r));
+ printf("The master server is %s.\n", master);
+
+ exit(0);
+}
diff --git a/usr.sbin/yppush/Makefile b/usr.sbin/yppush/Makefile
new file mode 100644
index 0000000..fb58a1a
--- /dev/null
+++ b/usr.sbin/yppush/Makefile
@@ -0,0 +1,28 @@
+# $Id$
+
+PROG= yppush
+SRCS= yp_clnt.c ypxfr_getmap.c yp_dblookup.c yppush_svc.c \
+ yp_error.c ypxfr_misc.c yppush_main.c
+
+MAN8= yppush.8
+
+CFLAGS+=-I. -I${.CURDIR}/../../libexec/ypxfr
+
+RPCDIR= ${.CURDIR}/../../include/rpcsvc
+RPCGEN= rpcgen -C
+
+.PATH: ${RPCDIR} ${.CURDIR}/../../usr.sbin/ypserv \
+ ${.CURDIR}/../../libexec/ypxfr
+
+CLEANFILES= yp.h yp_clnt.c yppush_svc.c
+
+yppush_svc.c: yp.x yp.h
+ ${RPCGEN} -DYPPUSH_ONLY -m -o ${.TARGET} ${RPCDIR}/yp.x
+
+yp_clnt.c: yp.x yp.h
+ ${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..0f51ae9
--- /dev/null
+++ b/usr.sbin/yppush/yppush.8
@@ -0,0 +1,169 @@
+.\" Copyright (c) 1991, 1993, 1995
+.\" The Regents of the University of California. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by the University of
+.\" California, Berkeley and its contributors.
+.\" 4. Neither the name of the University nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $Id: yppush.8,v 1.6 1997/02/22 16:15:00 peter Exp $
+.\"
+.Dd February 5, 1995
+.Dt YPPUSH 8
+.Os
+.Sh NAME
+.Nm yppush
+.Nd "force propagation of updated NIS databases"
+.Sh SYNOPSIS
+.Nm yppush
+.Op Fl d Ar domain
+.Op Fl t Ar timeout
+.Op Fl j Ar #parallel jobs
+.Op Fl h Ar host
+.Op Fl p Ar path
+.Op Fl v
+.Ar mapname
+.Sh DESCRIPTION
+.Nm Yppush
+distributes updated NIS databases (or
+.Pa maps )
+from an NIS master server to NIS slave servers within an NIS
+domain. It is normally only run on the NIS master by
+.Pa /var/yp/Makefile
+whenever any of the NIS maps are updated. Note that
+.Pa /var/yp/Makefile
+does not invoke
+.Nm
+by default: the
+.Nm NOPUSH=True
+entry in the Makefile must first be commented out
+(the default
+.Bx Free
+configuration assumes a small network with only
+a single NIS server; in such a configuration,
+.Nm
+is not needed).
+.Pp
+By default,
+.Nm
+determines the names of the slave servers for a domain by searching the
+.Pa ypservers
+map. A destination host (or a list of hosts) can also be manually
+specified on the command line.
+Once it has a complete list of slave servers, it sends a 'map transfer'
+request to each slave, which in turn reads a copy of the map from
+the master NIS server using
+.Xr ypxfr 8 .
+Included within each request is the name of the map to be copied
+and some special information required by
+.Xr ypxfr 8
+to successfully 'callback' to
+.Nm
+and carry out the transfer. Any error messages
+.Nm
+receives from
+.Xr ypxfr 8
+via callback will be printed to stderr.
+.Pp
+.Sh OPTIONS
+The following options are available:
+.Bl -tag -width indent
+.It Fl d Ar domain
+Specify a particular domain. The NIS domain of
+the local host system is used by default. If the local host's domain
+name is not set, the domain name must be specified with this flag.
+.It Fl t Ar timeout
+Specify a timeout value in seconds. This timeout
+controls how long
+.Nm
+will wait for a response from a slave server before sending a
+map transfer request to the next slave server in its list.
+.It Fl j Ar #parallel jobs
+.Nm Yppush
+normally performs transfers serially, meaning that it will
+send a map transfer request to one slave server and then wait for
+it to respond before moving on to the next slave server. In environments
+with many slaves, it is more efficient to initiate several map transfers
+at once so that the transfers can take place in parallel. The
+.Fl j
+flag is used to specify the desired number of parallel jobs:
+.Nm
+will initiate the specified number of transfers immediately and
+listen for responses. If the number of specified parallel jobs is
+less than the number of slave servers,
+.Nm
+will initiate only the number of specified jobs and then wait
+for some of them to finish before starting any more.
+.Pp
+Note that
+.Nm
+handles callbacks asynchronously, which means that it will collect
+and display the callback information received from
+.Xr ypxfr 8
+as soon as it arrives, even it arrives before all of the map
+transfer requests have been sent.
+.It Fl h Ar host
+Can be used to transfer a map to a user-specified machine or
+group of machines instead of the list of servers contained in
+the
+.Pa ypservers
+map. A list of hosts can be specified by using multiple
+instances of the
+.Fl h
+flag.
+.It Fl p Ar path
+By default,
+.Nm
+expects all the local NIS maps to be stored under
+.Pa /var/yp .
+The
+.Fl p
+flag can be used to specify an alternate path in the event that
+the system administrator decides to store the NIS maps somewhere else.
+.It Fl v
+Verbose mode: causes
+.Nm
+to print debugging messages as it runs. Specifying this flag twice
+makes
+.Nm
+even more verbose.
+.Sh FILES
+.Bl -tag -width Pa -compact
+.It Pa /var/yp/[domainname]/ypservers
+The NIS ypservers map containing the names of all servers in
+a particular NIS domain.
+.El
+.Sh SEE ALSO
+.Xr yp 4 ,
+.Xr ypserv 8 ,
+.Xr ypxfr 8
+.Sh BUGS
+The mechanism for transferring NIS maps in NIS v1 is different
+than that in NIS version 2. This version of
+.Nm
+has support for transferring maps to NIS v2 systems only.
+.Sh AUTHOR
+.An Bill Paul Aq wpaul@ctr.columbia.edu
diff --git a/usr.sbin/yppush/yppush_extern.h b/usr.sbin/yppush/yppush_extern.h
new file mode 100644
index 0000000..6d95356
--- /dev/null
+++ b/usr.sbin/yppush/yppush_extern.h
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 1995
+ * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id$
+ */
+
+/* Privately defined error codes. */
+#define YPPUSH_TIMEDOUT 255 /* Timed out trying to talk to ypserv */
+#define YPPUSH_NOHOST 254 /* No such host */
+#define YPPUSH_YPSERV 252 /* Failed to contact ypserv. */
+#define YPPUSH_PMAP 251 /* Portmapper failure. */
+#ifndef YPPUSH_RESPONSE_TIMEOUT
+#define YPPUSH_RESPONSE_TIMEOUT 5*60
+#endif
+extern int _rpc_dtablesize __P((void));
+extern void yppush_xfrrespprog_1 __P(( struct svc_req *, SVCXPRT * ));
diff --git a/usr.sbin/yppush/yppush_main.c b/usr.sbin/yppush/yppush_main.c
new file mode 100644
index 0000000..cadbff7
--- /dev/null
+++ b/usr.sbin/yppush/yppush_main.c
@@ -0,0 +1,688 @@
+/*
+ * Copyright (c) 1995
+ * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#include <errno.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <sys/fcntl.h>
+#include <sys/wait.h>
+#include <sys/param.h>
+#include <rpc/rpc.h>
+#include <rpc/clnt.h>
+#include <rpc/pmap_clnt.h>
+#include <rpcsvc/yp.h>
+struct dom_binding {};
+#include <rpcsvc/ypclnt.h>
+#include "ypxfr_extern.h"
+#include "yppush_extern.h"
+
+char *progname = "yppush";
+int debug = 1;
+int _rpcpmstart = 0;
+char *yp_dir = _PATH_YP;
+
+char *yppush_mapname = NULL; /* Map to transfer. */
+char *yppush_domain = NULL; /* Domain in which map resides. */
+char *yppush_master = NULL; /* Master NIS server for said domain. */
+int verbose = 0; /* Toggle verbose mode. */
+unsigned long yppush_transid = 0;
+int yppush_timeout = 80; /* Default timeout. */
+int yppush_jobs = 0; /* Number of allowed concurrent jobs. */
+int yppush_running_jobs = 0; /* Number of currently running jobs. */
+int yppush_alarm_tripped = 0;
+
+/* Structure for holding information about a running job. */
+struct jobs {
+ unsigned long tid;
+ int sock;
+ int port;
+ ypxfrstat stat;
+ unsigned long prognum;
+ char *server;
+ char *map;
+ int polled;
+ struct jobs *next;
+};
+
+struct jobs *yppush_joblist; /* Linked list of running jobs. */
+
+/*
+ * Local error messages.
+ */
+static char *yppusherr_string(err)
+ int err;
+{
+ switch(err) {
+ case YPPUSH_TIMEDOUT: return("transfer or callback timed out");
+ case YPPUSH_YPSERV: return("failed to contact ypserv");
+ case YPPUSH_NOHOST: return("no such host");
+ case YPPUSH_PMAP: return("portmapper failure");
+ default: return("unknown error code");
+ }
+}
+
+/*
+ * Report state of a job.
+ */
+static int yppush_show_status(status, tid)
+ ypxfrstat status;
+ unsigned long tid;
+{
+ struct jobs *job;
+
+ job = yppush_joblist;
+
+ while(job) {
+ if (job->tid == tid)
+ break;
+ job = job->next;
+ }
+
+ if (job->polled) {
+ return(0);
+ }
+
+ if (verbose > 1)
+ yp_error("checking return status: transaction ID: %lu",
+ job->tid);
+ if (status != YPPUSH_SUCC || verbose) {
+ yp_error("transfer of map %s to server %s %s",
+ job->map, job->server, status == YPPUSH_SUCC ?
+ "succeeded" : "failed");
+ yp_error("status returned by ypxfr: %s", status > YPPUSH_AGE ?
+ yppusherr_string(status) :
+ ypxfrerr_string(status));
+ }
+
+ job->polled = 1;
+
+ svc_unregister(job->prognum, 1);
+
+ yppush_running_jobs--;
+ return(0);
+}
+
+/* Exit routine. */
+static void yppush_exit(now)
+ int now;
+{
+ struct jobs *jptr;
+ int still_pending = 1;
+
+ /* Let all the information trickle in. */
+ while(!now && still_pending) {
+ jptr = yppush_joblist;
+ still_pending = 0;
+ while (jptr) {
+ if (jptr->polled == 0) {
+ still_pending++;
+ if (verbose > 1)
+ yp_error("%s has not responded",
+ jptr->server);
+ } else {
+ if (verbose > 1)
+ yp_error("%s has responded",
+ jptr->server);
+ }
+ jptr = jptr->next;
+ }
+ if (still_pending) {
+ if (verbose > 1)
+ yp_error("%d transfer%sstill pending",
+ still_pending,
+ still_pending > 1 ? "s " : " ");
+ yppush_alarm_tripped = 0;
+ alarm(YPPUSH_RESPONSE_TIMEOUT);
+ pause();
+ alarm(0);
+ if (yppush_alarm_tripped == 1) {
+ yp_error("timed out");
+ now = 1;
+ }
+ } else {
+ if (verbose)
+ yp_error("all transfers complete");
+ break;
+ }
+ }
+
+
+ /* All stats collected and reported -- kill all the stragglers. */
+ jptr = yppush_joblist;
+ while(jptr) {
+ if (!jptr->polled)
+ yp_error("warning: exiting with transfer \
+to %s (transid = %lu) still pending", jptr->server, jptr->tid);
+ svc_unregister(jptr->prognum, 1);
+ jptr = jptr->next;
+ }
+
+ exit(0);
+}
+
+/*
+ * Handler for 'normal' signals.
+ */
+
+static void handler(sig)
+ int sig;
+{
+ if (sig == SIGTERM || sig == SIGINT || sig == SIGABRT) {
+ yppush_jobs = 0;
+ yppush_exit(1);
+ }
+
+ if (sig == SIGALRM) {
+ alarm(0);
+ yppush_alarm_tripped++;
+ }
+
+ return;
+}
+
+/*
+ * Dispatch loop for callback RPC services.
+ */
+static void yppush_svc_run()
+{
+#ifdef FD_SETSIZE
+ fd_set readfds;
+#else
+ int readfds;
+#endif /* def FD_SETSIZE */
+ struct timeval timeout;
+
+ timeout.tv_usec = 0;
+ timeout.tv_sec = 5;
+
+retry:
+#ifdef FD_SETSIZE
+ readfds = svc_fdset;
+#else
+ readfds = svc_fds;
+#endif /* def FD_SETSIZE */
+ switch (select(_rpc_dtablesize(), &readfds, NULL, NULL, &timeout)) {
+ case -1:
+ if (errno == EINTR)
+ goto retry;
+ yp_error("select failed: %s", strerror(errno));
+ break;
+ case 0:
+ yp_error("select() timed out");
+ break;
+ default:
+ svc_getreqset(&readfds);
+ break;
+ }
+ return;
+}
+
+/*
+ * Special handler for asynchronous socket I/O. We mark the
+ * sockets of the callback handlers as O_ASYNC and handle SIGIO
+ * events here, which will occur when the callback handler has
+ * something interesting to tell us.
+ */
+static void async_handler(sig)
+ int sig;
+{
+ yppush_svc_run();
+
+ /* reset any pending alarms. */
+ alarm(0);
+ yppush_alarm_tripped++;
+ kill(getpid(), SIGALRM);
+ return;
+}
+
+/*
+ * RPC service routines for callbacks.
+ */
+void *
+yppushproc_null_1_svc(void *argp, struct svc_req *rqstp)
+{
+ static char * result;
+ /* Do nothing -- RPC conventions call for all a null proc. */
+ return((void *) &result);
+}
+
+void *
+yppushproc_xfrresp_1_svc(yppushresp_xfr *argp, struct svc_req *rqstp)
+{
+ static char * result;
+ yppush_show_status(argp->status, argp->transid);
+ return((void *) &result);
+}
+
+/*
+ * Transmit a YPPROC_XFR request to ypserv.
+ */
+static int yppush_send_xfr(job)
+ struct jobs *job;
+{
+ ypreq_xfr req;
+/* ypresp_xfr *resp; */
+ DBT key, data;
+ CLIENT *clnt;
+ struct rpc_err err;
+ struct timeval timeout;
+
+ timeout.tv_usec = 0;
+ timeout.tv_sec = 0;
+
+ /*
+ * The ypreq_xfr structure has a member of type map_parms,
+ * which seems to require the order number of the map.
+ * It isn't actually used at the other end (at least the
+ * FreeBSD ypserv doesn't use it) but we fill it in here
+ * for the sake of completeness.
+ */
+ key.data = "YP_LAST_MODIFIED";
+ key.size = sizeof ("YP_LAST_MODIFIED") - 1;
+
+ if (yp_get_record(yppush_domain, yppush_mapname, &key, &data,
+ 1) != YP_TRUE) {
+ yp_error("failed to read order number from %s: %s: %s",
+ yppush_mapname, yperr_string(yp_errno),
+ strerror(errno));
+ return(1);
+ }
+
+ /* Fill in the request arguments */
+ req.map_parms.ordernum = atoi(data.data);
+ req.map_parms.domain = yppush_domain;
+ req.map_parms.peer = yppush_master;
+ req.map_parms.map = job->map;
+ req.transid = job->tid;
+ req.prog = job->prognum;
+ req.port = job->port;
+
+ /* Get a handle to the remote ypserv. */
+ if ((clnt = clnt_create(job->server, YPPROG, YPVERS, "udp")) == NULL) {
+ yp_error("%s: %s",job->server,clnt_spcreateerror("couldn't \
+create udp handle to NIS server"));
+ switch(rpc_createerr.cf_stat) {
+ case RPC_UNKNOWNHOST:
+ job->stat = YPPUSH_NOHOST;
+ break;
+ case RPC_PMAPFAILURE:
+ job->stat = YPPUSH_PMAP;
+ break;
+ default:
+ job->stat = YPPUSH_RPC;
+ break;
+ }
+ return(1);
+ }
+
+ /*
+ * Reduce timeout to nothing since we may not
+ * get a response from ypserv and we don't want to block.
+ */
+ if (clnt_control(clnt, CLSET_TIMEOUT, (char *)&timeout) == FALSE)
+ yp_error("failed to set timeout on ypproc_xfr call");
+
+ /* Invoke the ypproc_xfr service. */
+ if (ypproc_xfr_2(&req, clnt) == NULL) {
+ clnt_geterr(clnt, &err);
+ if (err.re_status != RPC_SUCCESS &&
+ err.re_status != RPC_TIMEDOUT) {
+ yp_error("%s: %s", job->server, clnt_sperror(clnt,
+ "yp_xfr failed"));
+ job->stat = YPPUSH_YPSERV;
+ clnt_destroy(clnt);
+ return(1);
+ }
+ }
+
+ clnt_destroy(clnt);
+
+ return(0);
+}
+
+/*
+ * Main driver function. Register the callback service, add the transfer
+ * request to the internal list, send the YPPROC_XFR request to ypserv
+ * do other magic things.
+ */
+int yp_push(server, map, tid)
+ char *server;
+ char *map;
+ unsigned long tid;
+{
+ unsigned long prognum;
+ int sock = RPC_ANYSOCK;
+ SVCXPRT *xprt;
+ struct jobs *job;
+
+ /*
+ * Register the callback service on the first free
+ * transient program number.
+ */
+ xprt = svcudp_create(sock);
+ for (prognum = 0x40000000; prognum < 0x5FFFFFFF; prognum++) {
+ if (svc_register(xprt, prognum, 1,
+ yppush_xfrrespprog_1, IPPROTO_UDP) == TRUE)
+ break;
+ }
+
+ /* Register the job in our linked list of jobs. */
+ if ((job = (struct jobs *)malloc(sizeof (struct jobs))) == NULL) {
+ yp_error("malloc failed");
+ yppush_exit(1);
+ }
+
+ /* Initialize the info for this job. */
+ job->stat = 0;
+ job->tid = tid;
+ job->port = xprt->xp_port;
+ job->sock = xprt->xp_sock; /*XXX: Evil!! EEEEEEEVIL!!! */
+ job->server = strdup(server);
+ job->map = strdup(map);
+ job->prognum = prognum;
+ job->polled = 0;
+ job->next = yppush_joblist;
+ yppush_joblist = job;
+
+ /*
+ * Set the RPC sockets to asynchronous mode. This will
+ * cause the system to smack us with a SIGIO when an RPC
+ * callback is delivered. This in turn allows us to handle
+ * the callback even though we may be in the middle of doing
+ * something else at the time.
+ *
+ * XXX This is a horrible thing to do for two reasons,
+ * both of which have to do with portability:
+ * 1) We really ought not to be sticking our grubby mits
+ * into the RPC service transport handle like this.
+ * 2) Even in this day and age, there are still some *NIXes
+ * that don't support async socket I/O.
+ */
+ if (fcntl(xprt->xp_sock, F_SETOWN, getpid()) == -1 ||
+ fcntl(xprt->xp_sock, F_SETFL, O_ASYNC) == -1) {
+ yp_error("failed to set async I/O mode: %s",
+ strerror(errno));
+ yppush_exit(1);
+ }
+
+ if (verbose) {
+ yp_error("initiating transfer: %s -> %s (transid = %lu)",
+ yppush_mapname, server, tid);
+ }
+
+ /*
+ * Send the XFR request to ypserv. We don't have to wait for
+ * a response here since we can handle them asynchronously.
+ */
+
+ if (yppush_send_xfr(job)){
+ /* Transfer request blew up. */
+ yppush_show_status(job->stat ? job->stat :
+ YPPUSH_YPSERV,job->tid);
+ } else {
+ if (verbose > 1)
+ yp_error("%s has been called", server);
+ }
+
+ return(0);
+}
+
+/*
+ * Called for each entry in the ypservers map from yp_get_map(), which
+ * is our private yp_all() routine.
+ */
+int yppush_foreach(status, key, keylen, val, vallen, data)
+ int status;
+ char *key;
+ int keylen;
+ char *val;
+ int vallen;
+ char *data;
+{
+ char server[YPMAXRECORD + 2];
+
+ if (status != YP_TRUE)
+ return (status);
+
+ snprintf(server, sizeof(server), "%.*s", vallen, val);
+
+ /*
+ * Restrict the number of concurrent jobs. If yppush_jobs number
+ * of jobs have already been dispatched and are still pending,
+ * wait for one of them to finish so we can reuse its slot.
+ */
+ if (yppush_jobs <= 1) {
+ yppush_alarm_tripped = 0;
+ while (!yppush_alarm_tripped && yppush_running_jobs) {
+ alarm(yppush_timeout);
+ yppush_alarm_tripped = 0;
+ pause();
+ alarm(0);
+ }
+ } else {
+ yppush_alarm_tripped = 0;
+ while (!yppush_alarm_tripped && yppush_running_jobs >= yppush_jobs) {
+ alarm(yppush_timeout);
+ yppush_alarm_tripped = 0;
+ pause();
+ alarm(0);
+ }
+ }
+
+ /* Cleared for takeoff: set everything in motion. */
+ if (yp_push(&server, yppush_mapname, yppush_transid))
+ return(yp_errno);
+
+ /* Bump the job counter and transaction ID. */
+ yppush_running_jobs++;
+ yppush_transid++;
+ return (0);
+}
+
+static void usage()
+{
+ fprintf (stderr, "%s\n%s\n",
+ "usage: yppush [-d domain] [-t timeout] [-j #parallel jobs] [-h host]",
+ " [-p path] mapname");
+ exit(1);
+}
+
+/*
+ * Entry point. (About time!)
+ */
+int
+main(argc,argv)
+ int argc;
+ char *argv[];
+{
+ int ch;
+ DBT key, data;
+ char myname[MAXHOSTNAMELEN];
+ struct hostlist {
+ char *name;
+ struct hostlist *next;
+ };
+ struct hostlist *yppush_hostlist = NULL;
+ struct hostlist *tmp;
+ struct sigaction sa;
+
+ while ((ch = getopt(argc, argv, "d:j:p:h:t:v")) != -1) {
+ switch(ch) {
+ case 'd':
+ yppush_domain = optarg;
+ break;
+ case 'j':
+ yppush_jobs = atoi(optarg);
+ if (yppush_jobs <= 0)
+ yppush_jobs = 1;
+ break;
+ case 'p':
+ yp_dir = optarg;
+ break;
+ case 'h': /* we can handle multiple hosts */
+ if ((tmp = (struct hostlist *)malloc(sizeof(struct hostlist))) == NULL) {
+ yp_error("malloc failed");
+ yppush_exit(1);
+ }
+ tmp->name = strdup(optarg);
+ tmp->next = yppush_hostlist;
+ yppush_hostlist = tmp;
+ break;
+ case 't':
+ yppush_timeout = atoi(optarg);
+ break;
+ case 'v':
+ verbose++;
+ break;
+ default:
+ usage();
+ break;
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ yppush_mapname = argv[0];
+
+ if (yppush_mapname == NULL) {
+ /* "No guts, no glory." */
+ usage();
+ }
+
+ /*
+ * If no domain was specified, try to find the default
+ * domain. If we can't find that, we're doomed and must bail.
+ */
+ if (yppush_domain == NULL) {
+ char *yppush_check_domain;
+ if (!yp_get_default_domain(&yppush_check_domain) &&
+ !_yp_check(&yppush_check_domain)) {
+ yp_error("no domain specified and NIS not running");
+ usage();
+ } else
+ yp_get_default_domain(&yppush_domain);
+ }
+
+ /* Check to see that we are the master for this map. */
+
+ if (gethostname ((char *)&myname, sizeof(myname))) {
+ yp_error("failed to get name of local host: %s",
+ strerror(errno));
+ yppush_exit(1);
+ }
+
+ key.data = "YP_MASTER_NAME";
+ key.size = sizeof("YP_MASTER_NAME") - 1;
+
+ if (yp_get_record(yppush_domain, yppush_mapname,
+ &key, &data, 1) != YP_TRUE) {
+ yp_error("couldn't open %s map: %s", yppush_mapname,
+ strerror(errno));
+ yppush_exit(1);
+ }
+
+ if (strncmp(myname, data.data, data.size)) {
+ yp_error("warning: this host is not the master for %s",
+ yppush_mapname);
+#ifdef NITPICKY
+ yppush_exit(1);
+#endif
+ }
+
+ yppush_master = malloc(data.size + 1);
+ strncpy(yppush_master, data.data, data.size);
+ yppush_master[data.size] = '\0';
+
+ /* Install some handy handlers. */
+ signal(SIGALRM, handler);
+ signal(SIGTERM, handler);
+ signal(SIGINT, handler);
+ signal(SIGABRT, handler);
+
+ /*
+ * Set up the SIGIO handler. Make sure that some of the
+ * other signals are blocked while the handler is running so
+ * select() doesn't get interrupted.
+ */
+ sigemptyset(&sa.sa_mask);
+ sigaddset(&sa.sa_mask, SIGIO); /* Goes without saying. */
+ sigaddset(&sa.sa_mask, SIGPIPE);
+ sigaddset(&sa.sa_mask, SIGCHLD);
+ sigaddset(&sa.sa_mask, SIGALRM);
+ sigaddset(&sa.sa_mask, SIGINT);
+ sa.sa_handler = async_handler;
+
+ sigaction(SIGIO, &sa, NULL);
+
+ /* set initial transaction ID */
+ time(&yppush_transid);
+
+ if (yppush_hostlist) {
+ /*
+ * Host list was specified on the command line:
+ * kick off the transfers by hand.
+ */
+ tmp = yppush_hostlist;
+ while(tmp) {
+ yppush_foreach(YP_TRUE, NULL, 0, tmp->name,
+ strlen(tmp->name));
+ tmp = tmp->next;
+ }
+ } else {
+ /*
+ * Do a yp_all() on the ypservers map and initiate a ypxfr
+ * for each one.
+ */
+ ypxfr_get_map("ypservers", yppush_domain,
+ "localhost", yppush_foreach);
+ }
+
+ if (verbose > 1)
+ yp_error("all jobs dispatched");
+
+ /* All done -- normal exit. */
+ yppush_exit(0);
+
+ /* Just in case. */
+ exit(0);
+}
diff --git a/usr.sbin/ypserv/Makefile b/usr.sbin/ypserv/Makefile
new file mode 100644
index 0000000..dd1a4ff
--- /dev/null
+++ b/usr.sbin/ypserv/Makefile
@@ -0,0 +1,39 @@
+# $Id$
+
+PROG= ypserv
+SRCS= yp_svc.c yp_server.c yp_dblookup.c yp_dnslookup.c \
+ ypxfr_clnt.c yp_main.c yp_error.c yp_access.c yp_svc_udp.c
+
+MAN8= ypserv.8
+
+CFLAGS+= -I. -DDB_CACHE
+
+CLEANFILES= yp_svc.c ypxfr_clnt.c yp.h
+
+RPCDIR= ${.CURDIR}/../../include/rpcsvc
+.PATH: ${RPCDIR}
+
+RPCGEN= rpcgen -I -C
+
+# We need to remove the 'static' keyword from _rpcsvcstate so that
+# yp_main.c can see it.
+yp_svc.c: yp.x yp.h
+ rm -f ${.TARGET}
+ ${RPCGEN} -DYPSERV_ONLY -m ${RPCDIR}/yp.x | \
+ sed s/"static int _rpcsvcstate"/"int _rpcsvcstate"/g > ${.TARGET}
+
+ypxfr_clnt.c: yp.x yp.h
+ ${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
+ @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..3c7308a
--- /dev/null
+++ b/usr.sbin/ypserv/Makefile.yp
@@ -0,0 +1,524 @@
+#
+# Makefile for the NIS databases
+#
+# $Id$
+#
+# 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
+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
+NFILE = /tmp/ypmake
+TMP = `$(RCAT) $(NFILE)`
+
+# 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
+YPDIR = /var/yp
+YPMAPDIR = $(YPDIR)/$(DOMAIN)
+
+# These are the files from which the NIS databases are built. You may edit
+# these to taste in the event that you wish to keep your NIS source files
+# seperate from your NIS server's actual configuration files. Note that the
+# NIS passwd and master.passwd files are stored in /var/yp: the server's
+# real password database is not used by default. However, you may use
+# the real /etc/passwd and /etc/master.passwd files by:
+#
+#
+# - invoking yppasswdd without the -m option (yppasswdd will use
+# /etc/master.passwd if no alternate master.passwd file is specified
+# and do a 'pwd_mkdb' as needed).
+# - Specifying the location of the master.passwd file using the
+# MASTER_PASSWD variable, i.e.:
+#
+# # make MASTER_PASSWD=/path/to/some/other/master.passwd
+#
+# - (optionally): editing this Makefile to change the default location.
+#
+# To add a user, edit $(YPDIR)/master.passwd and type 'make'. The raw
+# passwd file will be generated from the master.passwd file automagically.
+#
+ETHERS = $(YPSRCDIR)/ethers # ethernet addresses (for rarpd)
+BOOTPARAMS= $(YPSRCDIR)/bootparams # for booting Sun boxes (bootparamd)
+HOSTS = $(YPSRCDIR)/hosts
+NETWORKS = $(YPSRCDIR)/networks
+PROTOCOLS = $(YPSRCDIR)/protocols
+RPC = $(YPSRCDIR)/rpc
+SERVICES = $(YPSRCDIR)/services
+GROUP = $(YPSRCDIR)/group
+ALIASES = $(YPSRCDIR)/aliases
+NETGROUP = $(YPDIR)/netgroup
+PASSWD = $(YPDIR)/passwd
+.if !defined(MASTER_PASSWD)
+MASTER = $(YPDIR)/master.passwd
+.else
+MASTER = $(MASTER_PASSWD)
+.endif
+YPSERVERS = $(YPDIR)/ypservers # List of all NIS servers for a domain
+PUBLICKEY = $(YPSRCDIR)/publickey
+NETID = $(YPSRCDIR)/netid
+AMDHOST = $(YPSRCDIR)/amd.host
+
+target:
+ @$(RM) $(NFILE)
+ @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."
+
+# If you don't want some of these maps built, feel free to comment
+# them out from this list.
+# 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.
+#
+
+all: servers master.passwd passwd hosts group networks protocols rpc \
+ services netid
+ # aliases publickey netgrp ethers bootparam amd.host
+
+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
+
+mail.aliases: $(ALIASES)
+ @echo "Updating $@..."
+ @echo $@.$$$$ > $(NFILE)
+ @$(NEWALIASES) -oA$(ALIASES)
+ @$(MKDB) -u $(ALIASES).db \
+ | $(DBLOAD) -i $(ALIASES) -o $(YPMAPDIR)/$@ - $(TMP)
+ @$(MV) $(TMP) $@
+ @$(DBLOAD) -c
+ @if [ ! $(NOPUSH) ]; then $(YPPUSH) -d $(DOMAIN) $@; fi
+ @if [ ! $(NOPUSH) ]; then echo "Pushed $@ map." ; fi
+
+
+ypservers: $(YPSERVERS)
+ @echo "Updating $@..."
+ @echo $@.$$$$ > $(NFILE)
+ $(CAT) $(YPSERVERS) | \
+ $(AWK) '{ if ($$1 != "" && $$1 != "#") print $$0"\t"$$0 }' $^ \
+ | $(DBLOAD) -i $(YPSERVERS) -o $(YPMAPDIR)/$@ - $(TMP)
+ @$(MV) $(TMP) $@
+ @$(DBLOAD) -c
+ @if [ ! $(NOPUSH) ]; then $(YPPUSH) -d $(DOMAIN) $@; fi
+ @if [ ! $(NOPUSH) ]; then echo "Pushed $@ map." ; fi
+
+
+ethers.byname: $(ETHERS)
+ @echo "Updating $@..."
+ @echo $@.$$$$ > $(NFILE)
+ $(CAT) $(ETHERS) | \
+ $(AWK) '{ if ($$1 != "" && $$1 != "#" && $$1 != "+") \
+ print $$2"\t"$$0 }' $^ | $(DBLOAD) -i $(ETHERS) \
+ -o $(YPMAPDIR)/$@ - $(TMP)
+ @$(MV) $(TMP) $@
+ @$(DBLOAD) -c
+ @if [ ! $(NOPUSH) ]; then $(YPPUSH) -d $(DOMAIN) $@; fi
+ @if [ ! $(NOPUSH) ]; then echo "Pushed $@ map." ; fi
+
+
+ethers.byaddr: $(ETHERS)
+ @echo "Updating $@..."
+ @echo $@.$$$$ > $(NFILE)
+ $(CAT) $(ETHERS) | \
+ $(AWK) '{ if ($$1 != "" && $$1 != "#" && $$1 != "+") \
+ print $$1"\t"$$0 }' $^ | $(DBLOAD) -i $(ETHERS) \
+ -o $(YPMAPDIR)/$@ - $(TMP)
+ @$(MV) $(TMP) $@
+ @$(DBLOAD) -c
+ @if [ ! $(NOPUSH) ]; then $(YPPUSH) -d $(DOMAIN) $@; fi
+ @if [ ! $(NOPUSH) ]; then echo "Pushed $@ map." ; fi
+
+
+bootparams: $(BOOTPARAMS)
+ @echo "Updating $@..."
+ @echo $@.$$$$ > $(NFILE)
+ $(CAT) $(BOOTPARAMS) | \
+ $(AWK) '{ if ($$1 != "" && $$1 != "#" && $$1 != "+") \
+ print $$0 }' $^ | $(DBLOAD) -i $(BOOTPARAMS) \
+ -o $(YPMAPDIR)/$@ - $(TMP)
+ @$(MV) $(TMP) $@
+ @$(DBLOAD) -c
+ @if [ ! $(NOPUSH) ]; then $(YPPUSH) -d $(DOMAIN) $@; fi
+ @if [ ! $(NOPUSH) ]; then echo "Pushed $@ map." ; fi
+
+
+netgroup: $(NETGROUP) netgroup.byhost netgroup.byuser
+ @echo "Updating $@..."
+ @echo $@.$$$$ > $(NFILE)
+ $(CAT) $(NETGROUP) | \
+ $(AWK) '{ if ($$1 != "" && $$1 != "#" && $$1 != "+") \
+ print $$0 }' $^ | $(DBLOAD) -i $(NETGROUP) \
+ -o $(YPMAPDIR)/$@ - $(TMP)
+ @$(MV) $(TMP) $@
+ @$(DBLOAD) -c
+ @if [ ! $(NOPUSH) ]; then $(YPPUSH) -d $(DOMAIN) $@; fi
+ @if [ ! $(NOPUSH) ]; then echo "Pushed $@ map." ; fi
+ @$(MAKE) -f ../Makefile netid
+
+
+netgroup.byhost: $(NETGROUP)
+ @echo "Updating $@..."
+ @echo $@.$$$$ > $(NFILE)
+ $(CAT) $(NETGROUP) | $(REVNETGROUP) -h -f $(NETGROUP) | \
+ $(AWK) '{ if ($$1 != "" && $$1 != "#" && $$1 != "+") \
+ print $$0 }' $^ | $(DBLOAD) -i $(NETGROUP) \
+ -o $(YPMAPDIR)/$@ - $(TMP)
+ @$(MV) $(TMP) $@
+ @$(DBLOAD) -c
+ @if [ ! $(NOPUSH) ]; then $(YPPUSH) -d $(DOMAIN) $@; fi
+ @if [ ! $(NOPUSH) ]; then echo "Pushed $@ map." ; fi
+
+
+netgroup.byuser: $(NETGROUP)
+ @echo "Updating $@..."
+ @echo $@.$$$$ > $(NFILE)
+ $(CAT) $(NETGROUP) | $(REVNETGROUP) -u -f $(NETGROUP) | \
+ $(AWK) '{ if ($$1 != "" && $$1 != "#" && $$1 != "+") \
+ print $$0 }' $^ | $(DBLOAD) -i $(NETGROUP) \
+ -o $(YPMAPDIR)/$@ - $(TMP)
+ @$(MV) $(TMP) $@
+ @$(DBLOAD) -c
+ @if [ ! $(NOPUSH) ]; then $(YPPUSH) -d $(DOMAIN) $@; fi
+ @if [ ! $(NOPUSH) ]; then echo "Pushed $@ map." ; fi
+
+
+hosts.byname: $(HOSTS)
+ @echo "Updating $@..."
+ @echo $@.$$$$ > $(NFILE)
+ $(CAT) $(HOSTS) | \
+ $(AWK) '/^[0-9]/ { for (n=2; n<=NF && $$n !~ "#"; n++) \
+ print $$n"\t"$$0 }' $^ | $(DBLOAD) ${B} -i $(HOSTS) \
+ -o $(YPMAPDIR)/$@ - $(TMP)
+ @$(MV) $(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 $@..."
+ @echo $@.$$$$ > $(NFILE)
+ $(CAT) $(HOSTS) | \
+ $(AWK) '$$1 !~ "#" { print $$1"\t"$$0 }' $^ \
+ | $(DBLOAD) ${B} -i $(HOSTS) -o $(YPMAPDIR)/$@ - $(TMP)
+ @$(MV) $(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 $@..."
+ @echo $@.$$$$ > $(NFILE)
+ $(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)
+ @$(MV) $(TMP) $@
+ @$(DBLOAD) -c
+ @if [ ! $(NOPUSH) ]; then $(YPPUSH) -d $(DOMAIN) $@; fi
+ @if [ ! $(NOPUSH) ]; then echo "Pushed $@ map." ; fi
+
+
+networks.byaddr: $(NETWORKS)
+ @echo "Updating $@..."
+ @echo $@.$$$$ > $(NFILE)
+ $(CAT) $(NETWORKS) | \
+ $(AWK) '$$1 !~ "#" { print $$2"\t"$$0 }' $^ \
+ | $(DBLOAD) -i $(NETWORKS) -o $(YPMAPDIR)/$@ - $(TMP)
+ @$(MV) $(TMP) $@
+ @$(DBLOAD) -c
+ @if [ ! $(NOPUSH) ]; then $(YPPUSH) -d $(DOMAIN) $@; fi
+ @if [ ! $(NOPUSH) ]; then echo "Pushed $@ map." ; fi
+
+
+protocols.byname: $(PROTOCOLS)
+ @echo "Updating $@..."
+ @echo $@.$$$$ > $(NFILE)
+ $(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)
+ @$(MV) $(TMP) $@
+ @$(DBLOAD) -c
+ @if [ ! $(NOPUSH) ]; then $(YPPUSH) -d $(DOMAIN) $@; fi
+ @if [ ! $(NOPUSH) ]; then echo "Pushed $@ map." ; fi
+
+
+protocols.bynumber: $(PROTOCOLS)
+ @echo "Updating $@..."
+ @echo $@.$$$$ > $(NFILE)
+ $(CAT) $(PROTOCOLS) | \
+ $(AWK) '$$1 !~ "#" { print $$2"\t"$$0 }' $^ \
+ | $(DBLOAD) -i $(PROTOCOLS) -o $(YPMAPDIR)/$@ - $(TMP)
+ @$(MV) $(TMP) $@
+ @$(DBLOAD) -c
+ @if [ ! $(NOPUSH) ]; then $(YPPUSH) -d $(DOMAIN) $@; fi
+ @if [ ! $(NOPUSH) ]; then echo "Pushed $@ map." ; fi
+
+
+rpc.byname: $(RPC)
+ @echo "Updating $@..."
+ @echo $@.$$$$ > $(NFILE)
+ $(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)
+ @$(MV) $(TMP) $@
+ @$(DBLOAD) -c
+ @if [ ! $(NOPUSH) ]; then $(YPPUSH) -d $(DOMAIN) $@; fi
+ @if [ ! $(NOPUSH) ]; then echo "Pushed $@ map." ; fi
+
+
+rpc.bynumber: $(RPC)
+ @echo "Updating $@..."
+ @echo $@.$$$$ > $(NFILE)
+ $(CAT) $(RPC) | \
+ $(AWK) '$$1 !~ "#" { print $$2"\t"$$0 }' $^ \
+ | $(DBLOAD) -i $(RPC) -o $(YPMAPDIR)/$@ - $(TMP)
+ @$(MV) $(TMP) $@
+ @$(DBLOAD) -c
+ @if [ ! $(NOPUSH) ]; then $(YPPUSH) -d $(DOMAIN) $@; fi
+ @if [ ! $(NOPUSH) ]; then echo "Pushed $@ map." ; fi
+
+
+services.byname: $(SERVICES)
+ @echo "Updating $@..."
+ @echo $@.$$$$ > $(NFILE)
+ $(CAT) $(SERVICES) | \
+ $(AWK) \
+ '$$1 !~ "#" { for (n=1; n<=NF && $$n !~ "#"; n++) { \
+ if (index($$2,"udp")) { printf("%s/udp",$$n) } \
+ else { printf("%s/tcp",$$n) }; print "\t"$$0 ; \
+ if (n == 1) n = 2; \
+ } ; print $$2"\t"$$0 ; \
+ }' $^ | $(DBLOAD) -i $(SERVICES) -o $(YPMAPDIR)/$@ - $(TMP)
+ @$(MV) $(TMP) $@
+ @$(DBLOAD) -c
+ @if [ ! $(NOPUSH) ]; then $(YPPUSH) -d $(DOMAIN) $@; fi
+ @if [ ! $(NOPUSH) ]; then echo "Pushed $@ map." ; fi
+
+
+publickey.byname: $(PUBLICKEY)
+ @echo "Updating $@..."
+ @echo $@.$$$$ > $(NFILE)
+ $(CAT) $(PUBLICKEY) | \
+ $(AWK) '$$1 !~ "#" { print $$1"\t"$$2 }' $^ \
+ | $(DBLOAD) -i $(PUBLICKEY) -o $(YPMAPDIR)/$@ - $(TMP)
+ @$(MV) $(TMP) $@
+ @$(DBLOAD) -c
+ @if [ ! $(NOPUSH) ]; then $(YPPUSH) -d $(DOMAIN) $@; fi
+ @if [ ! $(NOPUSH) ]; then echo "Pushed $@ map." ; fi
+
+
+$(PASSWD): $(MASTER)
+ @echo "Creating new $@ file from $(MASTER)..."
+ @if [ ! $(UNSECURE) ]; then \
+ $(RCAT) $(MASTER) | \
+ $(AWK) -F: '{if ($$1 != "+") \
+ print $$1":*:"$$3":"$$4":"$$8":"$$9":"$$10}' $^ \
+ > $(PASSWD) ; \
+ else $(RCAT) $(MASTER) | \
+ $(AWK) -F: '{if ($$1 != "+") \
+ print $$1":"$$2":"$$3":"$$4":"$$8":"$$9":"$$10}' $^ \
+ > $(PASSWD) ; fi
+
+
+passwd.byname: $(PASSWD)
+ @echo "Updating $@..."
+ @echo $@.$$$$ > $(NFILE)
+ $(CAT) $(PASSWD) | \
+ $(AWK) -F: '{ if ($$1 != "+") print $$1"\t"$$0 }' $^ \
+ | $(DBLOAD) -i $(PASSWD) -o $(YPMAPDIR)/$@ - $(TMP)
+ @$(MV) $(TMP) $@
+ @$(DBLOAD) -c
+ @if [ ! $(NOPUSH) ]; then $(YPPUSH) -d $(DOMAIN) $@; fi
+ @if [ ! $(NOPUSH) ]; then echo "Pushed $@ map." ; fi
+
+
+passwd.byuid: $(PASSWD)
+ @echo "Updating $@..."
+ @echo $@.$$$$ > $(NFILE)
+ $(CAT) $(PASSWD) | \
+ $(AWK) -F: '{ if ($$1 != "+") print $$3"\t"$$0 }' $^ \
+ | $(DBLOAD) -i $(PASSWD) -o $(YPMAPDIR)/$@ - $(TMP)
+ @$(MV) $(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 $@..."
+ @echo $@.$$$$ > $(NFILE)
+ $(CAT) $(GROUP) | \
+ $(AWK) -F: '{ if ($$1 != "+") print $$1"\t"$$0 }' $^ \
+ | $(DBLOAD) -i $(GROUP) -o $(YPMAPDIR)/$@ - $(TMP)
+ @$(MV) $(TMP) $@
+ @$(DBLOAD) -c
+ @if [ ! $(NOPUSH) ]; then $(YPPUSH) -d $(DOMAIN) $@; fi
+ @if [ ! $(NOPUSH) ]; then echo "Pushed $@ map." ; fi
+
+
+group.bygid: $(GROUP)
+ @echo "Updating $@..."
+ @echo $@.$$$$ > $(NFILE)
+ $(CAT) $(GROUP) | \
+ $(AWK) -F: '{ if ($$1 != "+") print $$3"\t"$$0 }' $^ \
+ | $(DBLOAD) -i $(GROUP) -o $(YPMAPDIR)/$@ - $(TMP)
+ @$(MV) $(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 $@..."
+ @echo $@.$$$$ > $(NFILE)
+ @$(MKNETID) -q -p $(PASSWD) -g $(GROUP) -h $(HOSTS) -n $(NETID) \
+ -d $(DOMAIN) | $(DBLOAD) -o $(YPMAPDIR)/$@ - $(TMP)
+ @$(MV) $(TMP) $@
+ @$(DBLOAD) -c
+ @if [ ! $(NOPUSH) ]; then $(YPPUSH) -d $(DOMAIN) $@; fi
+ @if [ ! $(NOPUSH) ]; then echo "Pushed $@ map." ; fi
+
+
+master.passwd.byname: $(MASTER)
+ @echo "Updating $@..."
+ @echo $@.$$$$ > $(NFILE)
+ $(CAT) $(MASTER) | \
+ $(AWK) -F: '{ if ($$1 != "+") print $$1"\t"$$0 }' $^ \
+ | $(DBLOAD) ${S} -i $(MASTER) -o $(YPMAPDIR)/$@ - $(TMP)
+ @$(MV) $(TMP) $@
+ @$(DBLOAD) -c
+ @if [ ! $(NOPUSH) ]; then $(YPPUSH) -d $(DOMAIN) $@; fi
+ @if [ ! $(NOPUSH) ]; then echo "Pushed $@ map." ; fi
+
+
+master.passwd.byuid: $(MASTER)
+ @echo "Updating $@..."
+ @echo $@.$$$$ > $(NFILE)
+ $(CAT) $(MASTER) | \
+ $(AWK) -F: '{ if ($$1 != "+") print $$3"\t"$$0 }' $^ \
+ | $(DBLOAD) ${S} -i $(MASTER) -o $(YPMAPDIR)/$@ - $(TMP)
+ @$(MV) $(TMP) $@
+ @$(DBLOAD) -c
+ @if [ ! $(NOPUSH) ]; then $(YPPUSH) -d $(DOMAIN) $@; fi
+ @if [ ! $(NOPUSH) ]; then echo "Pushed $@ map." ; fi
+
+amd.host: $(AMDHOST)
+ @echo "Updating $@..."
+ @echo $@.$$$$ > $(NFILE)
+ $(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)
+ @$(MV) $(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..bb869f2
--- /dev/null
+++ b/usr.sbin/ypserv/yp_access.c
@@ -0,0 +1,324 @@
+/*
+ * Copyright (c) 1995
+ * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#include <stdlib.h>
+#include <rpc/rpc.h>
+#include <rpcsvc/yp.h>
+#include <rpcsvc/yppasswd.h>
+#include <rpcsvc/ypxfrd.h>
+#include <sys/types.h>
+#include <limits.h>
+#include <db.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <sys/stat.h>
+#include <sys/fcntl.h>
+#include <paths.h>
+#include <errno.h>
+#include <sys/param.h>
+#include "yp_extern.h"
+#ifdef TCP_WRAPPER
+#include "tcpd.h"
+#endif
+
+extern int debug;
+
+ /* NIS v1 */
+char *yp_procs[] = { "ypoldproc_null",
+ "ypoldproc_domain",
+ "ypoldproc_domain_nonack",
+ "ypoldproc_match",
+ "ypoldproc_first",
+ "ypoldproc_next",
+ "ypoldproc_poll",
+ "ypoldproc_push",
+ "ypoldproc_get",
+ "badproc1", /* placeholder */
+ "badproc2", /* placeholder */
+ "badproc3", /* placeholder */
+
+ /* NIS v2 */
+ "ypproc_null" ,
+ "ypproc_domain",
+ "ypproc_domain_nonack",
+ "ypproc_match",
+ "ypproc_first",
+ "ypproc_next",
+ "ypproc_xfr",
+ "ypproc_clear",
+ "ypproc_all",
+ "ypproc_master",
+ "ypproc_order",
+ "ypproc_maplist"
+ };
+
+
+#ifdef TCP_WRAPPER
+void load_securenets()
+{
+}
+#else
+struct securenet {
+ struct in_addr net;
+ struct in_addr mask;
+ struct securenet *next;
+};
+
+struct securenet *securenets;
+
+#define LINEBUFSZ 1024
+
+/*
+ * Read /var/yp/securenets file and initialize the securenets
+ * list. If the file doesn't exist, we set up a dummy entry that
+ * allows all hosts to connect.
+ */
+void load_securenets()
+{
+ FILE *fp;
+ char path[MAXPATHLEN + 2];
+ char linebuf[1024 + 2];
+ struct securenet *tmp;
+
+ /*
+ * If securenets is not NULL, we are being called to reload
+ * the list; free the existing list before re-reading the
+ * securenets file.
+ */
+ while(securenets) {
+ tmp = securenets->next;
+ free(securenets);
+ securenets = tmp;
+ }
+
+ snprintf(path, MAXPATHLEN, "%s/securenets", yp_dir);
+
+ if ((fp = fopen(path, "r")) == NULL) {
+ if (errno == ENOENT) {
+ securenets = (struct securenet *)malloc(sizeof(struct securenet));
+ securenets->net.s_addr = INADDR_ANY;
+ securenets->mask.s_addr = INADDR_ANY;
+ securenets->next = NULL;
+ return;
+ } else {
+ yp_error("fopen(%s) failed: %s", path, strerror(errno));
+ exit(1);
+ }
+ }
+
+ securenets = NULL;
+
+ while(fgets(linebuf, LINEBUFSZ, fp)) {
+ char addr1[20], addr2[20];
+
+ if (linebuf[0] == '#')
+ continue;
+ if (sscanf(linebuf, "%s %s", addr1, addr2) < 2) {
+ yp_error("badly formatted securenets entry: %s",
+ linebuf);
+ continue;
+ }
+
+ tmp = (struct securenet *)malloc(sizeof(struct securenet));
+
+ if (!inet_aton((char *)&addr1, (struct in_addr *)&tmp->net)) {
+ yp_error("badly formatted securenets entry: %s", addr1);
+ free(tmp);
+ continue;
+ }
+
+ if (!inet_aton((char *)&addr2, (struct in_addr *)&tmp->mask)) {
+ yp_error("badly formatted securenets entry: %s", addr2);
+ free(tmp);
+ continue;
+ }
+
+ tmp->next = securenets;
+ securenets = tmp;
+ }
+
+ fclose(fp);
+
+}
+#endif
+
+/*
+ * Access control functions.
+ *
+ * yp_access() checks the mapname and client host address and watches for
+ * the following things:
+ *
+ * - If the client is referencing one of the master.passwd.* maps, it must
+ * be using a privileged port to make its RPC to us. If it is, then we can
+ * assume that the caller is root and allow the RPC to succeed. If it
+ * isn't access is denied.
+ *
+ * - The client's IP address is checked against the securenets rules.
+ * There are two kinds of securenets support: the built-in support,
+ * which is very simple and depends on the presence of a
+ * /var/yp/securenets file, and tcp-wrapper support, which requires
+ * Wietse Venema's libwrap.a and tcpd.h. (Since the tcp-wrapper
+ * package does not ship with FreeBSD, we use the built-in support
+ * by default. Users can recompile the server with the tcp-wrapper library
+ * if they already have it installed and want to use hosts.allow and
+ * hosts.deny to control access instead of having a seperate securenets
+ * file.)
+ *
+ * If no /var/yp/securenets file is present, the host access checks
+ * are bypassed and all hosts are allowed to connect.
+ *
+ * The yp_validdomain() function checks the domain specified by the caller
+ * to make sure it's actually served by this server. This is more a sanity
+ * check than an a security check, but this seems to be the best place for
+ * it.
+ */
+
+#ifdef DB_CACHE
+int yp_access(map, domain, rqstp)
+#else
+int yp_access(map, rqstp)
+#endif
+ const char *map;
+#ifdef DB_CACHE
+ const char *domain;
+#endif
+ const struct svc_req *rqstp;
+{
+ struct sockaddr_in *rqhost;
+ int status = 0;
+ static unsigned long oldaddr = 0;
+#ifndef TCP_WRAPPER
+ struct securenet *tmp;
+#endif
+ char *yp_procedure = NULL;
+ char procbuf[50];
+
+ if (rqstp->rq_prog != YPPASSWDPROG && rqstp->rq_prog != YPPROG) {
+ snprintf(procbuf, sizeof(procbuf), "#%lu/#%lu", rqstp->rq_prog,
+ rqstp->rq_proc);
+ yp_procedure = (char *)&procbuf;
+ } else {
+ yp_procedure = rqstp->rq_prog == YPPASSWDPROG ?
+ "yppasswdprog_update" :
+ yp_procs[rqstp->rq_proc + (12 * (rqstp->rq_vers - 1))];
+ }
+
+ rqhost = svc_getcaller(rqstp->rq_xprt);
+
+ if (debug) {
+ yp_error("procedure %s called from %s:%d", yp_procedure,
+ inet_ntoa(rqhost->sin_addr),
+ ntohs(rqhost->sin_port));
+ if (map != NULL)
+ yp_error("client is referencing map \"%s\".", map);
+ }
+
+ /* Check the map name if one was supplied. */
+ if (map != NULL) {
+ if (strchr(map, '/')) {
+ yp_error("embedded slash in map name \"%s\" -- \
+possible spoof attempt from %s:%d",
+ map, inet_ntoa(rqhost->sin_addr),
+ ntohs(rqhost->sin_port));
+ return(1);
+ }
+#ifdef DB_CACHE
+ if ((yp_testflag((char *)map, (char *)domain, YP_SECURE) ||
+#else
+ if ((strstr(map, "master.passwd.") ||
+#endif
+ (rqstp->rq_prog == YPPROG &&
+ rqstp->rq_proc == YPPROC_XFR) ||
+ (rqstp->rq_prog == YPXFRD_FREEBSD_PROG &&
+ rqstp->rq_proc == YPXFRD_GETMAP)) &&
+ ntohs(rqhost->sin_port) >= IPPORT_RESERVED) {
+ yp_error("access to %s denied -- client %s:%d \
+not privileged", map, inet_ntoa(rqhost->sin_addr), ntohs(rqhost->sin_port));
+ return(1);
+ }
+ }
+
+#ifdef TCP_WRAPPER
+ status = hosts_ctl("ypserv", STRING_UNKNOWN,
+ inet_ntoa(rqhost->sin_addr), "");
+#else
+ tmp = securenets;
+ while(tmp) {
+ if (((rqhost->sin_addr.s_addr & ~tmp->mask.s_addr)
+ | tmp->net.s_addr) == rqhost->sin_addr.s_addr) {
+ status = 1;
+ break;
+ }
+ tmp = tmp->next;
+ }
+#endif
+
+ if (!status) {
+ if (rqhost->sin_addr.s_addr != oldaddr) {
+ yp_error("connect from %s:%d to procedure %s refused",
+ inet_ntoa(rqhost->sin_addr),
+ ntohs(rqhost->sin_port),
+ yp_procedure);
+ oldaddr = rqhost->sin_addr.s_addr;
+ }
+ return(1);
+ }
+ return(0);
+
+}
+
+int yp_validdomain(domain)
+ const char *domain;
+{
+ struct stat statbuf;
+ char dompath[MAXPATHLEN + 2];
+
+ if (domain == NULL || strstr(domain, "binding") ||
+ !strcmp(domain, ".") || !strcmp(domain, "..") ||
+ strchr(domain, '/') || strlen(domain) > YPMAXDOMAIN)
+ return(1);
+
+ snprintf(dompath, sizeof(dompath), "%s/%s", yp_dir, domain);
+
+ if (stat(dompath, &statbuf) < 0 || !S_ISDIR(statbuf.st_mode))
+ return(1);
+
+
+ return(0);
+}
diff --git a/usr.sbin/ypserv/yp_dblookup.c b/usr.sbin/ypserv/yp_dblookup.c
new file mode 100644
index 0000000..9041c09
--- /dev/null
+++ b/usr.sbin/ypserv/yp_dblookup.c
@@ -0,0 +1,750 @@
+/*
+ * Copyright (c) 1995
+ * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#include <db.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <paths.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <rpcsvc/yp.h>
+#include "yp_extern.h"
+
+int ypdb_debug = 0;
+enum ypstat yp_errno = YP_TRUE;
+
+#define PERM_SECURE (S_IRUSR|S_IWUSR)
+HASHINFO openinfo = {
+ 4096, /* bsize */
+ 32, /* ffactor */
+ 256, /* nelem */
+ 2048 * 512, /* cachesize */
+ NULL, /* hash */
+ 0, /* lorder */
+};
+
+#ifdef DB_CACHE
+#include <sys/queue.h>
+
+#ifndef MAXDBS
+#define MAXDBS 20
+#endif
+
+static int numdbs = 0;
+
+struct dbent {
+ DB *dbp;
+ char *name;
+ char *key;
+ int size;
+ int flags;
+};
+
+static CIRCLEQ_HEAD(circlehead, circleq_entry) qhead;
+
+struct circleq_entry {
+ struct dbent *dbptr;
+ CIRCLEQ_ENTRY(circleq_entry) links;
+};
+
+/*
+ * Initialize the circular queue.
+ */
+void yp_init_dbs()
+{
+ CIRCLEQ_INIT(&qhead);
+ return;
+}
+
+/*
+ * Dynamically allocate an entry for the circular queue.
+ * Return a NULL pointer on failure.
+ */
+static struct circleq_entry *yp_malloc_qent()
+{
+ register struct circleq_entry *q;
+
+ q = (struct circleq_entry *)malloc(sizeof(struct circleq_entry));
+ if (q == NULL) {
+ yp_error("failed to malloc() circleq entry");
+ return(NULL);
+ }
+ bzero((char *)q, sizeof(struct circleq_entry));
+ q->dbptr = (struct dbent *)malloc(sizeof(struct dbent));
+ if (q->dbptr == NULL) {
+ yp_error("failed to malloc() circleq entry");
+ free(q);
+ return(NULL);
+ }
+ bzero((char *)q->dbptr, sizeof(struct dbent));
+
+ return(q);
+}
+
+/*
+ * Free a previously allocated circular queue
+ * entry.
+ */
+static void yp_free_qent(q)
+ struct circleq_entry *q;
+{
+ /*
+ * First, close the database. In theory, this is also
+ * supposed to free the resources allocated by the DB
+ * package, including the memory pointed to by q->dbptr->key.
+ * This means we don't have to free q->dbptr->key here.
+ */
+ if (q->dbptr->dbp) {
+ (void)(q->dbptr->dbp->close)(q->dbptr->dbp);
+ q->dbptr->dbp = NULL;
+ }
+ /*
+ * Then free the database name, which was strdup()'ed.
+ */
+ free(q->dbptr->name);
+
+ /*
+ * Free the rest of the dbent struct.
+ */
+ free(q->dbptr);
+ q->dbptr = NULL;
+
+ /*
+ * Free the circleq struct.
+ */
+ free(q);
+ q = NULL;
+
+ return;
+}
+
+/*
+ * Zorch a single entry in the dbent queue and release
+ * all its resources. (This always removes the last entry
+ * in the queue.)
+ */
+static void yp_flush()
+{
+ register struct circleq_entry *qptr;
+
+ qptr = qhead.cqh_last;
+ CIRCLEQ_REMOVE(&qhead, qptr, links);
+ yp_free_qent(qptr);
+ numdbs--;
+
+ return;
+}
+
+/*
+ * Close all databases, erase all database names and empty the queue.
+ */
+void yp_flush_all()
+{
+ register struct circleq_entry *qptr;
+
+ while(qhead.cqh_first != (void *)&qhead) {
+ qptr = qhead.cqh_first; /* save this */
+ CIRCLEQ_REMOVE(&qhead, qhead.cqh_first, links);
+ yp_free_qent(qptr);
+ }
+ numdbs = 0;
+
+ return;
+}
+
+static char *inter_string = "YP_INTERDOMAIN";
+static char *secure_string = "YP_SECURE";
+static int inter_sz = sizeof("YP_INTERDOMAIN") - 1;
+static int secure_sz = sizeof("YP_SECURE") - 1;
+
+static int yp_setflags(dbp)
+ DB *dbp;
+{
+ DBT key = { NULL, 0 }, data = { NULL, 0 };
+ int flags = 0;
+
+ key.data = inter_string;
+ key.size = inter_sz;
+
+ if (!(dbp->get)(dbp, &key, &data, 0))
+ flags |= YP_INTERDOMAIN;
+
+ key.data = secure_string;
+ key.size = secure_sz;
+
+ if (!(dbp->get)(dbp, &key, &data, 0))
+ flags |= YP_SECURE;
+
+ return(flags);
+}
+
+int yp_testflag(map, domain, flag)
+ char *map;
+ char *domain;
+ int flag;
+{
+ char buf[MAXPATHLEN + 2];
+ register struct circleq_entry *qptr;
+
+ if (map == NULL || domain == NULL)
+ return(0);
+
+ strcpy(buf, domain);
+ strcat(buf, "/");
+ strcat(buf, map);
+
+ for (qptr = qhead.cqh_first; qptr != (void *)&qhead;
+ qptr = qptr->links.cqe_next) {
+ if (!strcmp(qptr->dbptr->name, buf)) {
+ if (qptr->dbptr->flags & flag)
+ return(1);
+ else
+ return(0);
+ }
+ }
+
+ if (yp_open_db_cache(domain, map, NULL, 0) == NULL)
+ return(0);
+
+ if (qhead.cqh_first->dbptr->flags & flag)
+ return(1);
+
+ return(0);
+}
+
+/*
+ * Add a DB handle and database name to the cache. We only maintain
+ * fixed number of entries in the cache, so if we're asked to store
+ * a new entry when all our slots are already filled, we have to kick
+ * out the entry in the last slot to make room.
+ */
+static int yp_cache_db(dbp, name, size)
+ DB *dbp;
+ char *name;
+ int size;
+{
+ register struct circleq_entry *qptr;
+
+ if (numdbs == MAXDBS) {
+ if (ypdb_debug)
+ yp_error("queue overflow -- releasing last slot");
+ yp_flush();
+ }
+
+ /*
+ * Allocate a new queue entry.
+ */
+
+ if ((qptr = yp_malloc_qent()) == NULL) {
+ yp_error("failed to allocate a new cache entry");
+ return(1);
+ }
+
+ qptr->dbptr->dbp = dbp;
+ qptr->dbptr->name = strdup(name);
+ qptr->dbptr->size = size;
+ qptr->dbptr->key = NULL;
+
+ qptr->dbptr->flags = yp_setflags(dbp);
+
+ CIRCLEQ_INSERT_HEAD(&qhead, qptr, links);
+ numdbs++;
+
+ return(0);
+}
+
+/*
+ * Search the list for a database matching 'name.' If we find it,
+ * move it to the head of the list and return its DB handle. If
+ * not, just fail: yp_open_db_cache() will subsequently try to open
+ * the database itself and call yp_cache_db() to add it to the
+ * list.
+ *
+ * The search works like this:
+ *
+ * - The caller specifies the name of a database to locate. We try to
+ * find an entry in our queue with a matching name.
+ *
+ * - If the caller doesn't specify a key or size, we assume that the
+ * first entry that we encounter with a matching name is returned.
+ * This will result in matches regardless of the key/size values
+ * stored in the queue entry.
+ *
+ * - If the caller also specifies a key and length, we check to see
+ * if the key and length saved in the queue entry also matches.
+ * This lets us return a DB handle that's already positioned at the
+ * correct location within a database.
+ *
+ * - Once we have a match, it gets migrated to the top of the queue
+ * so that it will be easier to find if another request for
+ * the same database comes in later.
+ */
+static DB *yp_find_db(name, key, size)
+ char *name;
+ char *key;
+ int size;
+{
+ register struct circleq_entry *qptr;
+
+ for (qptr = qhead.cqh_first; qptr != (void *)&qhead;
+ qptr = qptr->links.cqe_next) {
+ if (!strcmp(qptr->dbptr->name, name)) {
+ if (size) {
+ if (size != qptr->dbptr->size ||
+ strncmp(qptr->dbptr->key, key, size))
+ continue;
+ } else {
+ if (qptr->dbptr->size)
+ continue;
+ }
+ if (qptr != qhead.cqh_first) {
+ CIRCLEQ_REMOVE(&qhead, qptr, links);
+ CIRCLEQ_INSERT_HEAD(&qhead, qptr, links);
+ }
+ return(qptr->dbptr->dbp);
+ }
+ }
+
+ return(NULL);
+}
+
+/*
+ * Open a DB database and cache the handle for later use. We first
+ * check the cache to see if the required database is already open.
+ * If so, we fetch the handle from the cache. If not, we try to open
+ * the database and save the handle in the cache for later use.
+ */
+DB *yp_open_db_cache(domain, map, key, size)
+ const char *domain;
+ const char *map;
+ const char *key;
+ const int size;
+{
+ DB *dbp = NULL;
+ char buf[MAXPATHLEN + 2];
+/*
+ snprintf(buf, sizeof(buf), "%s/%s", domain, map);
+*/
+ yp_errno = YP_TRUE;
+
+ strcpy(buf, domain);
+ strcat(buf, "/");
+ strcat(buf, map);
+
+ if ((dbp = yp_find_db((char *)&buf, key, size)) != NULL) {
+ return(dbp);
+ } else {
+ if ((dbp = yp_open_db(domain, map)) != NULL) {
+ if (yp_cache_db(dbp, (char *)&buf, size)) {
+ (void)(dbp->close)(dbp);
+ yp_errno = YP_YPERR;
+ return(NULL);
+ }
+ }
+ }
+
+ return (dbp);
+}
+#endif
+
+/*
+ * Open a DB database.
+ */
+DB *yp_open_db(domain, map)
+ const char *domain;
+ const char *map;
+{
+ DB *dbp = NULL;
+ char buf[MAXPATHLEN + 2];
+
+ yp_errno = YP_TRUE;
+
+ if (map[0] == '.' || strchr(map, '/')) {
+ yp_errno = YP_BADARGS;
+ return (NULL);
+ }
+
+#ifdef DB_CACHE
+ if (yp_validdomain(domain)) {
+ yp_errno = YP_NODOM;
+ return(NULL);
+ }
+#endif
+ snprintf(buf, sizeof(buf), "%s/%s/%s", yp_dir, domain, map);
+
+#ifdef DB_CACHE
+again:
+#endif
+ dbp = dbopen(buf,O_RDONLY, PERM_SECURE, DB_HASH, NULL);
+
+ if (dbp == NULL) {
+ switch(errno) {
+#ifdef DB_CACHE
+ case ENFILE:
+ /*
+ * We ran out of file descriptors. Nuke an
+ * open one and try again.
+ */
+ yp_error("ran out of file descriptors");
+ yp_flush();
+ goto again;
+ break;
+#endif
+ case ENOENT:
+ yp_errno = YP_NOMAP;
+ break;
+ case EFTYPE:
+ yp_errno = YP_BADDB;
+ break;
+ default:
+ yp_errno = YP_YPERR;
+ break;
+ }
+ }
+
+ return (dbp);
+}
+
+/*
+ * Database access routines.
+ *
+ * - yp_get_record(): retrieve an arbitrary key/data pair given one key
+ * to match against.
+ *
+ * - yp_first_record(): retrieve first key/data base in a database.
+ *
+ * - yp_next_record(): retrieve key/data pair that sequentially follows
+ * the supplied key value in the database.
+ */
+
+#ifdef DB_CACHE
+int yp_get_record(dbp,key,data,allow)
+ DB *dbp;
+#else
+int yp_get_record(domain,map,key,data,allow)
+ const char *domain;
+ const char *map;
+#endif
+ const DBT *key;
+ DBT *data;
+ int allow;
+{
+#ifndef DB_CACHE
+ DB *dbp;
+#endif
+ int rval = 0;
+#ifndef DB_CACHE
+ static unsigned char buf[YPMAXRECORD];
+#endif
+
+ if (ypdb_debug)
+ yp_error("looking up key [%.*s]",
+ key->size, key->data);
+
+ /*
+ * Avoid passing back magic "YP_*" entries unless
+ * the caller specifically requested them by setting
+ * the 'allow' flag.
+ */
+ if (!allow && !strncmp(key->data, "YP_", 3))
+ return(YP_NOKEY);
+
+#ifndef DB_CACHE
+ if ((dbp = yp_open_db(domain, map)) == NULL) {
+ return(yp_errno);
+ }
+#endif
+
+ if ((rval = (dbp->get)(dbp, key, data, 0)) != 0) {
+#ifdef DB_CACHE
+ qhead.cqh_first->dbptr->size = 0;
+#else
+ (void)(dbp->close)(dbp);
+#endif
+ if (rval == 1)
+ return(YP_NOKEY);
+ else
+ return(YP_BADDB);
+ }
+
+ if (ypdb_debug)
+ yp_error("result of lookup: key: [%.*s] data: [%.*s]",
+ key->size, key->data, data->size, data->data);
+
+#ifdef DB_CACHE
+ if (qhead.cqh_first->dbptr->size) {
+ qhead.cqh_first->dbptr->key = key->data;
+ qhead.cqh_first->dbptr->size = key->size;
+ }
+#else
+ bcopy((char *)data->data, (char *)&buf, data->size);
+ data->data = (void *)&buf;
+ (void)(dbp->close)(dbp);
+#endif
+
+ return(YP_TRUE);
+}
+
+int yp_first_record(dbp,key,data,allow)
+ const DB *dbp;
+ DBT *key;
+ DBT *data;
+ int allow;
+{
+ int rval;
+#ifndef DB_CACHE
+ static unsigned char buf[YPMAXRECORD];
+#endif
+
+ if (ypdb_debug)
+ yp_error("retrieving first key in map");
+
+ if ((rval = (dbp->seq)(dbp,key,data,R_FIRST)) != 0) {
+#ifdef DB_CACHE
+ qhead.cqh_first->dbptr->size = 0;
+#endif
+ if (rval == 1)
+ return(YP_NOKEY);
+ else
+ return(YP_BADDB);
+ }
+
+ /* Avoid passing back magic "YP_*" records. */
+ while (!strncmp(key->data, "YP_", 3) && !allow) {
+ if ((rval = (dbp->seq)(dbp,key,data,R_NEXT)) != 0) {
+#ifdef DB_CACHE
+ qhead.cqh_first->dbptr->size = 0;
+#endif
+ if (rval == 1)
+ return(YP_NOKEY);
+ else
+ return(YP_BADDB);
+ }
+ }
+
+ if (ypdb_debug)
+ yp_error("result of lookup: key: [%.*s] data: [%.*s]",
+ key->size, key->data, data->size, data->data);
+
+#ifdef DB_CACHE
+ if (qhead.cqh_first->dbptr->size) {
+ qhead.cqh_first->dbptr->key = key->data;
+ qhead.cqh_first->dbptr->size = key->size;
+ }
+#else
+ bcopy((char *)data->data, (char *)&buf, data->size);
+ data->data = (void *)&buf;
+#endif
+
+ return(YP_TRUE);
+}
+
+int yp_next_record(dbp,key,data,all,allow)
+ const DB *dbp;
+ DBT *key;
+ DBT *data;
+ int all;
+ int allow;
+{
+ static DBT lkey = { NULL, 0 };
+ static DBT ldata = { NULL, 0 };
+ int rval;
+#ifndef DB_CACHE
+ static unsigned char keybuf[YPMAXRECORD];
+ static unsigned char datbuf[YPMAXRECORD];
+#endif
+
+ if (key == NULL || !key->size || key->data == NULL) {
+ rval = yp_first_record(dbp,key,data,allow);
+ if (rval == YP_NOKEY)
+ return(YP_NOMORE);
+ else {
+#ifdef DB_CACHE
+ qhead.cqh_first->dbptr->key = key->data;
+ qhead.cqh_first->dbptr->size = key->size;
+#endif
+ return(rval);
+ }
+ }
+
+ if (ypdb_debug)
+ yp_error("retrieving next key, previous was: [%.*s]",
+ key->size, key->data);
+
+ if (!all) {
+#ifdef DB_CACHE
+ if (qhead.cqh_first->dbptr->key == NULL) {
+#endif
+ (dbp->seq)(dbp,&lkey,&ldata,R_FIRST);
+ while(strncmp((char *)key->data,lkey.data,
+ (int)key->size) || key->size != lkey.size)
+ if ((dbp->seq)(dbp,&lkey,&ldata,R_NEXT)) {
+#ifdef DB_CACHE
+ qhead.cqh_first->dbptr->size = 0;
+#endif
+ return(YP_NOKEY);
+ }
+
+#ifdef DB_CACHE
+ }
+#endif
+ }
+
+ if ((dbp->seq)(dbp,key,data,R_NEXT)) {
+#ifdef DB_CACHE
+ qhead.cqh_first->dbptr->size = 0;
+#endif
+ return(YP_NOMORE);
+ }
+
+ /* Avoid passing back magic "YP_*" records. */
+ while (!strncmp(key->data, "YP_", 3) && !allow)
+ if ((dbp->seq)(dbp,key,data,R_NEXT)) {
+#ifdef DB_CACHE
+ qhead.cqh_first->dbptr->size = 0;
+#endif
+ return(YP_NOMORE);
+ }
+
+ if (ypdb_debug)
+ yp_error("result of lookup: key: [%.*s] data: [%.*s]",
+ key->size, key->data, data->size, data->data);
+
+#ifdef DB_CACHE
+ if (qhead.cqh_first->dbptr->size) {
+ qhead.cqh_first->dbptr->key = key->data;
+ qhead.cqh_first->dbptr->size = key->size;
+ }
+#else
+ bcopy((char *)key->data, (char *)&keybuf, key->size);
+ lkey.data = (void *)&keybuf;
+ lkey.size = key->size;
+ bcopy((char *)data->data, (char *)&datbuf, data->size);
+ data->data = (void *)&datbuf;
+#endif
+
+ return(YP_TRUE);
+}
+
+#ifdef DB_CACHE
+/*
+ * Database glue functions.
+ */
+
+static DB *yp_currmap_db = NULL;
+static int yp_allow_db = 0;
+
+ypstat yp_select_map(map, domain, key, allow)
+ char *map;
+ char *domain;
+ keydat *key;
+ int allow;
+{
+ yp_currmap_db = yp_open_db_cache(domain, map, key->keydat_val,
+ key->keydat_len);
+
+ yp_allow_db = allow;
+ return(yp_errno);
+}
+
+ypstat yp_getbykey(key, val)
+ keydat *key;
+ valdat *val;
+{
+ DBT db_key = { NULL, 0 }, db_val = { NULL, 0 };
+ ypstat rval;
+
+ db_key.data = key->keydat_val;
+ db_key.size = key->keydat_len;
+
+ rval = yp_get_record(yp_currmap_db,
+ &db_key, &db_val, yp_allow_db);
+
+ if (rval == YP_TRUE) {
+ val->valdat_val = db_val.data;
+ val->valdat_len = db_val.size;
+ }
+
+ return(rval);
+}
+
+ypstat yp_firstbykey(key, val)
+ keydat *key;
+ valdat *val;
+{
+ DBT db_key = { NULL, 0 }, db_val = { NULL, 0 };
+ ypstat rval;
+
+ rval = yp_first_record(yp_currmap_db, &db_key, &db_val, yp_allow_db);
+
+ if (rval == YP_TRUE) {
+ key->keydat_val = db_key.data;
+ key->keydat_len = db_key.size;
+ val->valdat_val = db_val.data;
+ val->valdat_len = db_val.size;
+ }
+
+ return(rval);
+}
+
+ypstat yp_nextbykey(key, val)
+ keydat *key;
+ valdat *val;
+{
+ DBT db_key = { NULL, 0 }, db_val = { NULL, 0 };
+ ypstat rval;
+
+ db_key.data = key->keydat_val;
+ db_key.size = key->keydat_len;
+
+ rval = yp_next_record(yp_currmap_db, &db_key, &db_val, 0, yp_allow_db);
+
+ if (rval == YP_TRUE) {
+ key->keydat_val = db_key.data;
+ key->keydat_len = db_key.size;
+ val->valdat_val = db_val.data;
+ val->valdat_len = db_val.size;
+ }
+
+ return(rval);
+}
+#endif
diff --git a/usr.sbin/ypserv/yp_dnslookup.c b/usr.sbin/ypserv/yp_dnslookup.c
new file mode 100644
index 0000000..f29750d
--- /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[] =
+ "$Id$";
+#endif /* not lint */
+
+/*
+ * Do standard and reverse DNS lookups using the resolver library.
+ * Take care of all the dirty work here so the main program only has to
+ * pass us a pointer to an array of characters.
+ *
+ * We have to use direct resolver calls here otherwise the YP server
+ * could end up looping by calling itself over and over again until
+ * it disappeared up its own belly button.
+ */
+
+#include <sys/param.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/fcntl.h>
+#include <sys/queue.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <arpa/nameser.h>
+
+#include <ctype.h>
+#include <errno.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <resolv.h>
+#include <unistd.h>
+
+#include <rpcsvc/yp.h>
+#include "yp_extern.h"
+
+static char *parse(hp)
+ struct hostent *hp;
+{
+ static char result[MAXHOSTNAMELEN * 2];
+ int len,i;
+ struct in_addr addr;
+
+ if (hp == NULL)
+ return(NULL);
+
+ len = 16 + strlen(hp->h_name);
+ for (i = 0; hp->h_aliases[i]; i++)
+ len += strlen(hp->h_aliases[i]) + 1;
+
+ bzero(result, sizeof(result));
+
+ bcopy(hp->h_addr, &addr, sizeof(struct in_addr));
+ snprintf(result, sizeof(result), "%s %s", inet_ntoa(addr), hp->h_name);
+
+ for (i = 0; hp->h_aliases[i]; i++) {
+ strcat(result, " ");
+ strcat(result, hp->h_aliases[i]);
+ }
+
+ return ((char *)&result);
+}
+
+#define MAXPACKET 1024
+#define DEF_TTL 50
+
+#define BY_DNS_ID 1
+#define BY_RPC_XID 2
+
+extern struct hostent *__dns_getanswer __P((char *, int, char *, int));
+
+static CIRCLEQ_HEAD(dns_qhead, circleq_dnsentry) qhead;
+
+struct circleq_dnsentry {
+ SVCXPRT *xprt;
+ unsigned long xid;
+ struct sockaddr_in client_addr;
+ unsigned long ypvers;
+ unsigned long id;
+ unsigned long ttl;
+ unsigned long type;
+ unsigned short prot_type;
+ char **domain;
+ char *name;
+ struct in_addr addr;
+ CIRCLEQ_ENTRY(circleq_dnsentry) links;
+};
+
+static int pending = 0;
+
+int yp_init_resolver()
+{
+ CIRCLEQ_INIT(&qhead);
+ if (!(_res.options & RES_INIT) && res_init() == -1) {
+ yp_error("res_init failed");
+ return(1);
+ }
+ if ((resfd = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
+ yp_error("couldn't create socket");
+ return(1);
+ }
+ if (fcntl(resfd, F_SETFL, O_NONBLOCK) == -1) {
+ yp_error("couldn't make resolver socket non-blocking");
+ return(1);
+ }
+ return(0);
+}
+
+static struct circleq_dnsentry *yp_malloc_dnsent()
+{
+ register struct circleq_dnsentry *q;
+
+ q = (struct circleq_dnsentry *)malloc(sizeof(struct circleq_dnsentry));
+
+ if (q == NULL) {
+ yp_error("failed to malloc() circleq dns entry");
+ return(NULL);
+ }
+
+ return(q);
+}
+
+/*
+ * Transmit a query.
+ */
+static unsigned long yp_send_dns_query(name, type)
+ char *name;
+ int type;
+{
+ char buf[MAXPACKET];
+ int n;
+ HEADER *hptr;
+ int ns;
+ int rval;
+ unsigned long id;
+
+ bzero(buf, sizeof(buf));
+
+ n = res_mkquery(QUERY,name,C_IN,type,NULL,0,NULL,buf,sizeof(buf));
+
+ if (n <= 0) {
+ yp_error("res_mkquery failed");
+ return(0);
+ }
+
+ hptr = (HEADER *)&buf;
+ id = ntohs(hptr->id);
+
+ for (ns = 0; ns < _res.nscount; ns++) {
+ rval = sendto(resfd, buf, n, 0,
+ (struct sockaddr *)&_res.nsaddr_list[ns],
+ sizeof(struct sockaddr));
+ if (rval == -1) {
+ yp_error("sendto failed");
+ return(0);
+ }
+ }
+
+ return(id);
+}
+
+static struct circleq_dnsentry *yp_find_dnsqent(id, type)
+ unsigned long id;
+ int type;
+{
+ register struct circleq_dnsentry *q;
+
+ for (q = qhead.cqh_first; q != (void *)&qhead; q = q->links.cqe_next) {
+ switch(type) {
+ case BY_RPC_XID:
+ if (id == q->xid)
+ return(q);
+ break;
+ case BY_DNS_ID:
+ default:
+ if (id == q->id)
+ return(q);
+ break;
+ }
+ }
+ return (NULL);
+}
+
+static void yp_send_dns_reply(q, buf)
+ struct circleq_dnsentry *q;
+ char *buf;
+{
+ ypresponse result_v1;
+ ypresp_val result_v2;
+ unsigned long xid;
+ struct sockaddr_in client_addr;
+ xdrproc_t xdrfunc;
+ char *result;
+
+ /*
+ * Set up correct reply struct and
+ * XDR filter depending on ypvers.
+ */
+ switch(q->ypvers) {
+ case YPVERS:
+ bzero((char *)&result_v2, sizeof(result_v2));
+
+ if (buf == NULL)
+ result_v2.stat = YP_NOKEY;
+ else {
+ result_v2.val.valdat_len = strlen(buf);
+ result_v2.val.valdat_val = buf;
+ result_v2.stat = YP_TRUE;
+ }
+ result = (char *)&result_v2;
+ xdrfunc = (xdrproc_t)xdr_ypresp_val;
+ break;
+ case YPOLDVERS:
+ /*
+ * The odds are we will _never_ execute this
+ * particular code, but we include it anyway
+ * for the sake of completeness.
+ */
+ bzero((char *)&result_v1, sizeof(result_v1));
+ result_v1.yp_resptype = YPRESP_VAL;
+# define YPVAL ypresponse_u.yp_resp_valtype
+
+ if (buf == NULL)
+ result_v1.YPVAL.stat = YP_NOKEY;
+ else {
+ result_v1.YPVAL.val.valdat_len = strlen(buf);
+ result_v1.YPVAL.val.valdat_val = buf;
+ result_v1.YPVAL.stat = YP_TRUE;
+ }
+ result = (char *)&result_v1;
+ xdrfunc = (xdrproc_t)xdr_ypresponse;
+ break;
+ default:
+ yp_error("bad YP program version (%lu)!", q->ypvers);
+ return;
+ break;
+ }
+
+ if (debug)
+ yp_error("sending dns reply to %s (%lu)",
+ inet_ntoa(q->client_addr.sin_addr), q->id);
+ /*
+ * XXX This is disgusting. There's basically one transport
+ * handle for UDP, but we're holding off on replying to a
+ * client until we're ready, by which time we may have received
+ * several other queries from other clients with different
+ * transaction IDs. So to make the delayed response thing work,
+ * we have to save the transaction ID and client address of
+ * each request, then jam them into the transport handle when
+ * we're ready to send a reply. Then after we've send the reply,
+ * we put the old transaction ID and remote address back the
+ * way we found 'em. This is _INCREDIBLY_ non-portable; it's
+ * not even supported by the RPC library.
+ */
+ /*
+ * XXX Don't frob the transaction ID for TCP handles.
+ */
+ if (q->prot_type == SOCK_DGRAM)
+ xid = svcudp_set_xid(q->xprt, q->xid);
+ client_addr = q->xprt->xp_raddr;
+ q->xprt->xp_raddr = q->client_addr;
+
+ if (!svc_sendreply(q->xprt, xdrfunc, result))
+ yp_error("svc_sendreply failed");
+
+ /*
+ * Now that we sent the reply,
+ * put the handle back the way it was.
+ */
+ if (q->prot_type == SOCK_DGRAM)
+ svcudp_set_xid(q->xprt, xid);
+ q->xprt->xp_raddr = client_addr;
+
+ return;
+}
+
+/*
+ * Decrement TTL on all queue entries, possibly nuking
+ * any that have been around too long without being serviced.
+ */
+void yp_prune_dnsq()
+{
+ register struct circleq_dnsentry *q, *n;
+
+ q = qhead.cqh_first;
+ while(q != (void *)&qhead) {
+ q->ttl--;
+ n = q->links.cqe_next;
+ if (!q->ttl) {
+ CIRCLEQ_REMOVE(&qhead, q, links);
+ free(q->name);
+ free(q);
+ pending--;
+ }
+ q = n;
+ }
+
+ if (pending < 0)
+ pending = 0;
+
+ return;
+}
+
+/*
+ * Data is pending on the DNS socket; check for valid replies
+ * to our queries and dispatch them to waiting clients.
+ */
+void yp_run_dnsq()
+{
+ register struct circleq_dnsentry *q;
+ char buf[sizeof(HEADER) + MAXPACKET];
+ char retrybuf[MAXHOSTNAMELEN];
+ struct sockaddr_in sin;
+ int rval;
+ int len;
+ HEADER *hptr;
+ struct hostent *hent;
+
+ if (debug)
+ yp_error("running dns queue");
+
+ bzero(buf, sizeof(buf));
+
+ len = sizeof(struct sockaddr_in);
+ rval = recvfrom(resfd, buf, sizeof(buf), 0,
+ (struct sockaddr *)&sin, &len);
+
+ if (rval == -1) {
+ yp_error("recvfrom failed: %s", strerror(errno));
+ return;
+ }
+
+ /*
+ * We may have data left in the socket that represents
+ * replies to earlier queries that we don't care about
+ * anymore. If there are no lookups pending or the packet
+ * ID doesn't match any of the queue IDs, just drop it
+ * on the floor.
+ */
+ hptr = (HEADER *)&buf;
+ if (!pending ||
+ (q = yp_find_dnsqent(ntohs(hptr->id), BY_DNS_ID)) == NULL) {
+ /* ignore */
+ return;
+ }
+
+ if (debug)
+ yp_error("got dns reply from %s", inet_ntoa(sin.sin_addr));
+
+ hent = __dns_getanswer(buf, rval, q->name, q->type);
+
+ /*
+ * If the lookup failed, try appending one of the domains
+ * from resolv.conf. If we have no domains to test, the
+ * query has failed.
+ */
+ if (hent == NULL) {
+ if ((h_errno == TRY_AGAIN || h_errno == NO_RECOVERY)
+ && q->domain && *q->domain) {
+ snprintf(retrybuf, sizeof(retrybuf), "%s.%s",
+ q->name, *q->domain);
+ if (debug)
+ yp_error("retrying with: %s", retrybuf);
+ q->id = yp_send_dns_query(retrybuf, q->type);
+ q->ttl = DEF_TTL;
+ q->domain++;
+ return;
+ }
+ } else {
+ if (q->type == T_PTR) {
+ hent->h_addr = (char *)&q->addr.s_addr;
+ hent->h_length = sizeof(struct in_addr);
+ }
+ }
+
+ /* Got an answer ready for a client -- send it off. */
+ yp_send_dns_reply(q, parse(hent));
+ pending--;
+ CIRCLEQ_REMOVE(&qhead, q, links);
+ free(q->name);
+ free(q);
+
+ /* Decrement TTLs on other entries while we're here. */
+ yp_prune_dnsq();
+
+ return;
+}
+
+/*
+ * Queue and transmit an asynchronous DNS hostname lookup.
+ */
+ypstat yp_async_lookup_name(rqstp, name)
+ struct svc_req *rqstp;
+ char *name;
+{
+ register struct circleq_dnsentry *q;
+ int type, len;
+
+ /* Check for SOCK_DGRAM or SOCK_STREAM -- we need to know later */
+ type = -1; len = sizeof(type);
+ if (getsockopt(rqstp->rq_xprt->xp_sock, SOL_SOCKET,
+ SO_TYPE, &type, &len) == -1) {
+ yp_error("getsockopt failed: %s", strerror(errno));
+ return(YP_YPERR);
+ }
+
+ /* Avoid transmitting dupe requests. */
+ if (type == SOCK_DGRAM &&
+ yp_find_dnsqent(svcudp_get_xid(rqstp->rq_xprt),BY_RPC_XID) != NULL)
+ return(YP_TRUE);
+
+ if ((q = yp_malloc_dnsent()) == NULL)
+ return(YP_YPERR);
+
+ q->type = T_A;
+ q->ttl = DEF_TTL;
+ q->xprt = rqstp->rq_xprt;
+ q->ypvers = rqstp->rq_vers;
+ q->prot_type = type;
+ if (q->prot_type == SOCK_DGRAM)
+ q->xid = svcudp_get_xid(q->xprt);
+ q->client_addr = q->xprt->xp_raddr;
+ if (!strchr(name, '.'))
+ q->domain = _res.dnsrch;
+ else
+ q->domain = NULL;
+ q->id = yp_send_dns_query(name, q->type);
+
+ if (q->id == 0) {
+ yp_error("DNS query failed");
+ free(q);
+ return(YP_YPERR);
+ }
+
+ q->name = strdup(name);
+ CIRCLEQ_INSERT_HEAD(&qhead, q, links);
+ pending++;
+
+ if (debug)
+ yp_error("queueing async DNS name lookup (%d)", q->id);
+
+ yp_prune_dnsq();
+ return(YP_TRUE);
+}
+
+/*
+ * Queue and transmit an asynchronous DNS IP address lookup.
+ */
+ypstat yp_async_lookup_addr(rqstp, addr)
+ struct svc_req *rqstp;
+ char *addr;
+{
+ register struct circleq_dnsentry *q;
+ char buf[MAXHOSTNAMELEN];
+ int a, b, c, d;
+ int type, len;
+
+ /* Check for SOCK_DGRAM or SOCK_STREAM -- we need to know later */
+ type = -1; len = sizeof(type);
+ if (getsockopt(rqstp->rq_xprt->xp_sock, SOL_SOCKET,
+ SO_TYPE, &type, &len) == -1) {
+ yp_error("getsockopt failed: %s", strerror(errno));
+ return(YP_YPERR);
+ }
+
+ /* Avoid transmitting dupe requests. */
+ if (type == SOCK_DGRAM &&
+ yp_find_dnsqent(svcudp_get_xid(rqstp->rq_xprt),BY_RPC_XID) != NULL)
+ return(YP_TRUE);
+
+ if ((q = yp_malloc_dnsent()) == NULL)
+ return(YP_YPERR);
+
+ if (sscanf(addr, "%d.%d.%d.%d", &a, &b, &c, &d) != 4)
+ return(YP_NOKEY);
+
+ snprintf(buf, sizeof(buf), "%d.%d.%d.%d.in-addr.arpa", d, c, b, a);
+
+ if (debug)
+ yp_error("DNS address is: %s", buf);
+
+ q->type = T_PTR;
+ q->ttl = DEF_TTL;
+ q->xprt = rqstp->rq_xprt;
+ q->ypvers = rqstp->rq_vers;
+ q->domain = NULL;
+ q->prot_type = type;
+ if (q->prot_type == SOCK_DGRAM)
+ q->xid = svcudp_get_xid(q->xprt);
+ q->client_addr = q->xprt->xp_raddr;
+ q->id = yp_send_dns_query(buf, q->type);
+
+ if (q->id == 0) {
+ yp_error("DNS query failed");
+ free(q);
+ return(YP_YPERR);
+ }
+
+ inet_aton(addr, &q->addr);
+ q->name = strdup(buf);
+ CIRCLEQ_INSERT_HEAD(&qhead, q, links);
+ pending++;
+
+ if (debug)
+ yp_error("queueing async DNS address lookup (%d)", q->id);
+
+ yp_prune_dnsq();
+ return(YP_TRUE);
+}
diff --git a/usr.sbin/ypserv/yp_error.c b/usr.sbin/ypserv/yp_error.c
new file mode 100644
index 0000000..94adf32
--- /dev/null
+++ b/usr.sbin/ypserv/yp_error.c
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 1995
+ * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+/*
+ * error logging/reporting facilities
+ * stolen from /usr/libexec/mail.local via ypserv
+ */
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <syslog.h>
+
+int debug;
+extern int _rpcpmstart;
+
+extern char *progname;
+
+#if __STDC__
+#include <stdarg.h>
+#else
+#include <varargs.h>
+#endif
+
+static void __verr(fmt, ap)
+ const char *fmt;
+ _BSD_VA_LIST_ ap;
+
+{
+ if (debug && !_rpcpmstart) {
+ fprintf(stderr,"%s: ",progname);
+ vfprintf(stderr, fmt, ap);
+ fprintf(stderr, "\n");
+ } else {
+ vsyslog(LOG_NOTICE, fmt, ap);
+ }
+}
+
+void
+#ifdef __STDC__
+yp_error(const char *fmt, ...)
+#else
+yp_error(fmt, va_list)
+ const char *fmt;
+ va_dcl
+#endif
+{
+ va_list ap;
+#ifdef __STDC__
+ va_start(ap, fmt);
+#else
+ va_start(ap);
+#endif
+ __verr(fmt,ap);
+ va_end(ap);
+}
diff --git a/usr.sbin/ypserv/yp_extern.h b/usr.sbin/ypserv/yp_extern.h
new file mode 100644
index 0000000..0aaee26
--- /dev/null
+++ b/usr.sbin/ypserv/yp_extern.h
@@ -0,0 +1,113 @@
+/*
+ * Copyright (c) 1995
+ * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $Id: yp_extern.h,v 1.11 1997/02/22 16:15:11 peter Exp $
+ */
+
+#include <db.h>
+#include <limits.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/cdefs.h>
+#include <sys/types.h>
+#include <rpc/rpc.h>
+#include <rpcsvc/yp.h>
+
+#ifndef _PATH_YP
+#define _PATH_YP "/var/yp/"
+#endif
+
+#ifndef _PATH_LIBEXEC
+#define _PATH_LIBEXEC "/usr/libexec/"
+#endif
+
+#ifndef MAX_CHILDREN
+#define MAX_CHILDREN 20
+#endif
+
+#define YP_SECURE 0x1
+#define YP_INTERDOMAIN 0x2
+
+/*
+ * External functions and variables.
+ */
+
+extern int debug;
+extern int ypdb_debug;
+extern int do_dns;
+extern int children;
+extern int resfd;
+extern char *progname;
+extern char *yp_dir;
+extern enum ypstat yp_errno;
+extern void yp_error __P((const char *, ...));
+#ifdef DB_CACHE
+extern int yp_get_record __P(( DB *, const DBT *, DBT *, int));
+#else
+extern int yp_get_record __P(( const char *, const char *, const DBT *, DBT *, int));
+#endif
+extern int yp_first_record __P((const DB *, DBT *, DBT *, int));
+extern int yp_next_record __P((const DB *, DBT *, DBT *, int, int));
+extern char *yp_dnsname __P(( char * ));
+extern char *yp_dnsaddr __P(( const char * ));
+#ifdef DB_CACHE
+extern int yp_access __P((const char *, const char *, const struct svc_req * ));
+#else
+extern int yp_access __P((const char *, const struct svc_req * ));
+#endif
+extern int yp_validdomain __P((const char * ));
+extern DB *yp_open_db __P(( const char *, const char *));
+extern DB *yp_open_db_cache __P(( const char *, const char *, const char *, int ));
+extern void yp_flush_all __P(( void ));
+extern void yp_init_dbs __P(( void ));
+extern int yp_testflag __P(( char *, char *, int ));
+extern void load_securenets __P(( void ));
+
+#ifdef DB_CACHE
+extern ypstat yp_select_map __P(( char *, char *, keydat *, int ));
+extern ypstat yp_getbykey __P(( keydat *, valdat * ));
+extern ypstat yp_firstbykey __P(( keydat *, valdat * ));
+extern ypstat yp_nextbykey __P(( keydat *, valdat * ));
+#endif
+
+extern unsigned long svcudp_set_xid __P(( SVCXPRT *, unsigned long ));
+extern unsigned long svcudp_get_xid __P(( SVCXPRT * ));
+
+#ifndef RESOLVER_TIMEOUT
+#define RESOLVER_TIMEOUT 3600
+#endif
+
+extern int yp_init_resolver __P(( void ));
+extern void yp_run_dnsq __P(( void ));
+extern void yp_prune_dnsq __P(( void ));
+extern ypstat yp_async_lookup_name __P(( struct svc_req *, char * ));
+extern ypstat yp_async_lookup_addr __P(( struct svc_req *, char * ));
diff --git a/usr.sbin/ypserv/yp_main.c b/usr.sbin/ypserv/yp_main.c
new file mode 100644
index 0000000..6aaa7e3
--- /dev/null
+++ b/usr.sbin/ypserv/yp_main.c
@@ -0,0 +1,331 @@
+/*
+ * Copyright (c) 1995
+ * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+/*
+ * ypserv startup function.
+ * We need out own main() since we have to do some additional work
+ * that rpcgen won't do for us. Most of this file was generated using
+ * rpcgen.new, and later modified.
+ */
+
+#include "yp.h"
+#include <err.h>
+#include <errno.h>
+#include <memory.h>
+#include <stdio.h>
+#include <signal.h>
+#include <stdlib.h> /* getenv, exit */
+#include <string.h> /* strcmp */
+#include <syslog.h>
+#include <unistd.h>
+#include <rpc/pmap_clnt.h> /* for pmap_unset */
+#include <sys/ttycom.h> /* TIOCNOTTY */
+#ifdef __cplusplus
+#include <sysent.h> /* getdtablesize, open */
+#endif /* __cplusplus */
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <sys/wait.h>
+#include "yp_extern.h"
+#include <rpc/rpc.h>
+
+#ifndef SIG_PF
+#define SIG_PF void(*)(int)
+#endif
+
+#define _RPCSVC_CLOSEDOWN 120
+int _rpcpmstart; /* Started by a port monitor ? */
+static int _rpcfdtype;
+ /* Whether Stream or Datagram ? */
+ /* States a server can be in wrt request */
+
+#define _IDLE 0
+#define _SERVED 1
+#define _SERVING 2
+
+extern void ypprog_1 __P((struct svc_req *, register SVCXPRT *));
+extern void ypprog_2 __P((struct svc_req *, register SVCXPRT *));
+extern int _rpc_dtablesize __P((void));
+extern int _rpcsvcstate; /* Set when a request is serviced */
+char *progname = "ypserv";
+char *yp_dir = _PATH_YP;
+int debug = 0;
+int do_dns = 0;
+int resfd;
+
+static
+void _msgout(char* msg)
+{
+ if (debug) {
+ if (_rpcpmstart)
+ syslog(LOG_ERR, msg);
+ else
+ warnx("%s", msg);
+ } else
+ syslog(LOG_ERR, msg);
+}
+
+static void
+yp_svc_run()
+{
+#ifdef FD_SETSIZE
+ fd_set readfds;
+#else
+ int readfds;
+#endif /* def FD_SETSIZE */
+ extern int forked;
+ int pid;
+ int fd_setsize = _rpc_dtablesize();
+ struct timeval timeout;
+
+ /* Establish the identity of the parent ypserv process. */
+ pid = getpid();
+
+ for (;;) {
+#ifdef FD_SETSIZE
+ readfds = svc_fdset;
+#else
+ readfds = svc_fds;
+#endif /* def FD_SETSIZE */
+
+ FD_SET(resfd, &readfds);
+
+ timeout.tv_sec = RESOLVER_TIMEOUT;
+ timeout.tv_usec = 0;
+ switch (select(fd_setsize, &readfds, NULL, NULL,
+ &timeout)) {
+ case -1:
+ if (errno == EINTR) {
+ continue;
+ }
+ warn("svc_run: - select failed");
+ return;
+ case 0:
+ yp_prune_dnsq();
+ break;
+ default:
+ if (FD_ISSET(resfd, &readfds)) {
+ yp_run_dnsq();
+ FD_CLR(resfd, &readfds);
+ }
+ svc_getreqset(&readfds);
+ if (forked && pid != getpid())
+ exit(0);
+ }
+ }
+}
+
+static void unregister()
+{
+ (void) pmap_unset(YPPROG, YPVERS);
+ (void) pmap_unset(YPPROG, YPOLDVERS);
+}
+
+static void reaper(sig)
+ int sig;
+{
+ int status;
+
+ if (sig == SIGHUP) {
+ load_securenets();
+#ifdef DB_CACHE
+ yp_flush_all();
+#endif
+ return;
+ }
+
+ if (sig == SIGCHLD) {
+ while (wait3(&status, WNOHANG, NULL) > 0)
+ children--;
+ } else {
+ unregister();
+ exit(0);
+ }
+}
+
+static void usage()
+{
+ fprintf(stderr, "usage: ypserv [-h] [-d] [-n] [-p path]\n");
+ exit(1);
+}
+
+static void
+closedown(int sig)
+{
+ if (_rpcsvcstate == _IDLE) {
+ extern fd_set svc_fdset;
+ static int size;
+ int i, openfd;
+
+ if (_rpcfdtype == SOCK_DGRAM) {
+ unregister();
+ exit(0);
+ }
+ if (size == 0) {
+ size = getdtablesize();
+ }
+ for (i = 0, openfd = 0; i < size && openfd < 2; i++)
+ if (FD_ISSET(i, &svc_fdset))
+ openfd++;
+ if (openfd <= 1) {
+ unregister();
+ exit(0);
+ }
+ }
+ if (_rpcsvcstate == _SERVED)
+ _rpcsvcstate = _IDLE;
+
+ (void) signal(SIGALRM, (SIG_PF) closedown);
+ (void) alarm(_RPCSVC_CLOSEDOWN/2);
+}
+
+int
+main(argc, argv)
+ int argc;
+ char *argv[];
+{
+ register SVCXPRT *transp = NULL;
+ int sock;
+ int proto = 0;
+ struct sockaddr_in saddr;
+ int asize = sizeof (saddr);
+ int ch;
+
+ while ((ch = getopt(argc, argv, "hdnp:")) != -1) {
+ switch(ch) {
+ case 'd':
+ debug = ypdb_debug = 1;
+ break;
+ case 'n':
+ do_dns = 1;
+ break;
+ case 'p':
+ yp_dir = optarg;
+ break;
+ case 'h':
+ default:
+ usage();
+ }
+ }
+
+ load_securenets();
+ yp_init_resolver();
+#ifdef DB_CACHE
+ yp_init_dbs();
+#endif
+ if (getsockname(0, (struct sockaddr *)&saddr, &asize) == 0) {
+ int ssize = sizeof (int);
+
+ if (saddr.sin_family != AF_INET)
+ exit(1);
+ if (getsockopt(0, SOL_SOCKET, SO_TYPE,
+ (char *)&_rpcfdtype, &ssize) == -1)
+ exit(1);
+ sock = 0;
+ _rpcpmstart = 1;
+ proto = 0;
+ openlog("ypserv", LOG_PID, LOG_DAEMON);
+ } else {
+ if (!debug) {
+ if (daemon(0,0)) {
+ err(1,"cannot fork");
+ }
+ openlog("ypserv", LOG_PID, LOG_DAEMON);
+ }
+ sock = RPC_ANYSOCK;
+ (void) pmap_unset(YPPROG, YPVERS);
+ (void) pmap_unset(YPPROG, 1);
+ }
+
+ if ((_rpcfdtype == 0) || (_rpcfdtype == SOCK_DGRAM)) {
+ transp = svcudp_create(sock);
+ if (transp == NULL) {
+ _msgout("cannot create udp service");
+ exit(1);
+ }
+ if (!_rpcpmstart)
+ proto = IPPROTO_UDP;
+ if (!svc_register(transp, YPPROG, YPOLDVERS, ypprog_1, proto)) {
+ _msgout("unable to register (YPPROG, YPOLDVERS, udp)");
+ exit(1);
+ }
+ if (!svc_register(transp, YPPROG, YPVERS, ypprog_2, proto)) {
+ _msgout("unable to register (YPPROG, YPVERS, udp)");
+ exit(1);
+ }
+ }
+
+ if ((_rpcfdtype == 0) || (_rpcfdtype == SOCK_STREAM)) {
+ transp = svctcp_create(sock, 0, 0);
+ if (transp == NULL) {
+ _msgout("cannot create tcp service");
+ exit(1);
+ }
+ if (!_rpcpmstart)
+ proto = IPPROTO_TCP;
+ if (!svc_register(transp, YPPROG, YPOLDVERS, ypprog_1, proto)) {
+ _msgout("unable to register (YPPROG, YPOLDVERS, tcp)");
+ exit(1);
+ }
+ if (!svc_register(transp, YPPROG, YPVERS, ypprog_2, proto)) {
+ _msgout("unable to register (YPPROG, YPVERS, tcp)");
+ exit(1);
+ }
+ }
+
+ if (transp == (SVCXPRT *)NULL) {
+ _msgout("could not create a handle");
+ exit(1);
+ }
+ if (_rpcpmstart) {
+ (void) signal(SIGALRM, (SIG_PF) closedown);
+ (void) alarm(_RPCSVC_CLOSEDOWN/2);
+ }
+/*
+ * Make sure SIGPIPE doesn't blow us away while servicing TCP
+ * connections.
+ */
+ (void) signal(SIGPIPE, SIG_IGN);
+ (void) signal(SIGCHLD, (SIG_PF) reaper);
+ (void) signal(SIGTERM, (SIG_PF) reaper);
+ (void) signal(SIGINT, (SIG_PF) reaper);
+ (void) signal(SIGHUP, (SIG_PF) reaper);
+ yp_svc_run();
+ _msgout("svc_run returned");
+ exit(1);
+ /* NOTREACHED */
+}
diff --git a/usr.sbin/ypserv/yp_server.c b/usr.sbin/ypserv/yp_server.c
new file mode 100644
index 0000000..9927f6b
--- /dev/null
+++ b/usr.sbin/ypserv/yp_server.c
@@ -0,0 +1,951 @@
+/*
+ * Copyright (c) 1995
+ * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#include "yp.h"
+#include "yp_extern.h"
+#include <dirent.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <rpc/rpc.h>
+
+int forked = 0;
+int children = 0;
+
+#define MASTER_STRING "YP_MASTER_NAME"
+#define MASTER_SZ sizeof(MASTER_STRING) - 1
+#define ORDER_STRING "YP_LAST_MODIFIED"
+#define ORDER_SZ sizeof(ORDER_STRING) - 1
+
+/*
+ * NIS v2 support. This is where most of the action happens.
+ */
+
+void *
+ypproc_null_2_svc(void *argp, struct svc_req *rqstp)
+{
+ static char * result;
+ static char rval = 0;
+
+#ifdef DB_CACHE
+ if (yp_access(NULL, NULL, (struct svc_req *)rqstp))
+#else
+ if (yp_access(NULL, (struct svc_req *)rqstp))
+#endif
+ return(NULL);
+
+ result = &rval;
+
+ return((void *) &result);
+}
+
+bool_t *
+ypproc_domain_2_svc(domainname *argp, struct svc_req *rqstp)
+{
+ static bool_t result;
+
+#ifdef DB_CACHE
+ if (yp_access(NULL, NULL, (struct svc_req *)rqstp)) {
+#else
+ if (yp_access(NULL, (struct svc_req *)rqstp)) {
+#endif
+ result = FALSE;
+ return (&result);
+ }
+
+ if (argp == NULL || yp_validdomain(*argp))
+ result = FALSE;
+ else
+ result = TRUE;
+
+ return (&result);
+}
+
+bool_t *
+ypproc_domain_nonack_2_svc(domainname *argp, struct svc_req *rqstp)
+{
+ static bool_t result;
+
+#ifdef DB_CACHE
+ if (yp_access(NULL, NULL, (struct svc_req *)rqstp))
+#else
+ if (yp_access(NULL, (struct svc_req *)rqstp))
+#endif
+ return (NULL);
+
+ if (argp == NULL || yp_validdomain(*argp))
+ return (NULL);
+ else
+ result = TRUE;
+
+ return (&result);
+}
+
+ypresp_val *
+ypproc_match_2_svc(ypreq_key *argp, struct svc_req *rqstp)
+{
+ static ypresp_val result;
+
+ result.val.valdat_val = "";
+ result.val.valdat_len = 0;
+
+#ifdef DB_CACHE
+ if (yp_access(argp->map, argp->domain, (struct svc_req *)rqstp)) {
+#else
+ if (yp_access(argp->map, (struct svc_req *)rqstp)) {
+#endif
+ result.stat = YP_YPERR;
+ return (&result);
+ }
+
+ if (argp->domain == NULL || argp->map == NULL) {
+ result.stat = YP_BADARGS;
+ return (&result);
+ }
+
+ if (yp_select_map(argp->map, argp->domain, &argp->key, 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, &result.key, 0) != YP_TRUE) {
+ result.stat = yp_errno;
+ return(&result);
+ }
+
+ result.stat = yp_firstbykey(&result.key, &result.val);
+
+ return (&result);
+}
+
+ypresp_key_val *
+ypproc_next_2_svc(ypreq_key *argp, struct svc_req *rqstp)
+{
+ static ypresp_key_val result;
+
+ result.val.valdat_val = result.key.keydat_val = "";
+ result.val.valdat_len = result.key.keydat_len = 0;
+
+#ifdef DB_CACHE
+ if (yp_access(argp->map, argp->domain, (struct svc_req *)rqstp)) {
+#else
+ if (yp_access(argp->map, (struct svc_req *)rqstp)) {
+#endif
+ result.stat = YP_YPERR;
+ return (&result);
+ }
+
+ if (argp->domain == NULL || argp->map == NULL) {
+ result.stat = YP_BADARGS;
+ return (&result);
+ }
+
+ if (yp_select_map(argp->map, argp->domain, &argp->key, 0) != YP_TRUE) {
+ result.stat = yp_errno;
+ return(&result);
+ }
+
+ result.key.keydat_len = argp->key.keydat_len;
+ result.key.keydat_val = argp->key.keydat_val;
+
+ result.stat = yp_nextbykey(&result.key, &result.val);
+
+ return (&result);
+}
+
+static void ypxfr_callback(rval,addr,transid,prognum,port)
+ ypxfrstat rval;
+ struct sockaddr_in *addr;
+ unsigned int transid;
+ unsigned int prognum;
+ unsigned long port;
+{
+ CLIENT *clnt;
+ int sock = RPC_ANYSOCK;
+ struct timeval timeout;
+ yppushresp_xfr ypxfr_resp;
+ struct rpc_err err;
+
+ timeout.tv_sec = 5;
+ timeout.tv_usec = 0;
+ addr->sin_port = htons(port);
+
+ if ((clnt = clntudp_create(addr,prognum,1,timeout,&sock)) == NULL) {
+ yp_error("%s: %s", inet_ntoa(addr->sin_addr),
+ clnt_spcreateerror("failed to establish callback handle"));
+ return;
+ }
+
+ ypxfr_resp.status = rval;
+ ypxfr_resp.transid = transid;
+
+ /* Turn the timeout off -- we don't want to block. */
+ timeout.tv_sec = 0;
+ if (clnt_control(clnt, CLSET_TIMEOUT, (char *)&timeout) == FALSE)
+ yp_error("failed to set timeout on ypproc_xfr callback");
+
+ if (yppushproc_xfrresp_1(&ypxfr_resp, clnt) == NULL) {
+ clnt_geterr(clnt, &err);
+ if (err.re_status != RPC_SUCCESS &&
+ err.re_status != RPC_TIMEDOUT)
+ yp_error("%s", clnt_sperror(clnt,
+ "ypxfr callback failed"));
+ }
+
+ clnt_destroy(clnt);
+ return;
+}
+
+#define YPXFR_RETURN(CODE) \
+ /* Order is important: send regular RPC reply, then callback */ \
+ result.xfrstat = CODE; \
+ svc_sendreply(rqstp->rq_xprt, xdr_ypresp_xfr, (char *)&result); \
+ ypxfr_callback(CODE,rqhost,argp->transid, \
+ argp->prog,argp->port); \
+ return(NULL);
+
+ypresp_xfr *
+ypproc_xfr_2_svc(ypreq_xfr *argp, struct svc_req *rqstp)
+{
+ static ypresp_xfr result;
+ struct sockaddr_in *rqhost;
+ ypresp_master *mres;
+ ypreq_nokey mreq;
+
+ result.transid = argp->transid;
+ rqhost = svc_getcaller(rqstp->rq_xprt);
+
+#ifdef DB_CACHE
+ if (yp_access(argp->map_parms.map,
+ argp->map_parms.domain, (struct svc_req *)rqstp)) {
+#else
+ if (yp_access(argp->map_parms.map, (struct svc_req *)rqstp)) {
+#endif
+ YPXFR_RETURN(YPXFR_REFUSED)
+ }
+
+
+ if (argp->map_parms.domain == NULL) {
+ YPXFR_RETURN(YPXFR_BADARGS)
+ }
+
+ if (yp_validdomain(argp->map_parms.domain)) {
+ YPXFR_RETURN(YPXFR_NODOM)
+ }
+
+ /*
+ * Determine the master host ourselves. The caller may
+ * be up to no good. This has the side effect of verifying
+ * that the requested map and domain actually exist.
+ */
+
+ mreq.domain = argp->map_parms.domain;
+ mreq.map = argp->map_parms.map;
+
+ mres = ypproc_master_2_svc(&mreq, rqstp);
+
+ if (mres->stat != YP_TRUE) {
+ yp_error("couldn't find master for map %s@%s",
+ argp->map_parms.map,
+ argp->map_parms.domain);
+ yp_error("host at %s (%s) may be pulling my leg",
+ argp->map_parms.peer,
+ inet_ntoa(rqhost->sin_addr));
+ YPXFR_RETURN(YPXFR_REFUSED)
+ }
+
+ switch(fork()) {
+ case 0:
+ {
+ char g[11], t[11], p[11];
+ char ypxfr_command[MAXPATHLEN + 2];
+
+ sprintf (ypxfr_command, "%sypxfr", _PATH_LIBEXEC);
+ sprintf (t, "%u", argp->transid);
+ sprintf (g, "%u", argp->prog);
+ sprintf (p, "%u", argp->port);
+ if (debug) {
+ close(0); close(1); close(2);
+ }
+ if (strcmp(yp_dir, _PATH_YP)) {
+ execl(ypxfr_command, "ypxfr",
+ "-d", argp->map_parms.domain,
+ "-h", mres->peer,
+ "-p", yp_dir, "-C", t,
+ g, inet_ntoa(rqhost->sin_addr),
+ p, argp->map_parms.map,
+ NULL);
+ } else {
+ execl(ypxfr_command, "ypxfr",
+ "-d", argp->map_parms.domain,
+ "-h", mres->peer,
+ "-C", t,
+ g, inet_ntoa(rqhost->sin_addr),
+ p, argp->map_parms.map,
+ NULL);
+ }
+ forked++;
+ yp_error("ypxfr execl(%s): %s", ypxfr_command, strerror(errno));
+ YPXFR_RETURN(YPXFR_XFRERR)
+ break;
+ }
+ case -1:
+ yp_error("ypxfr fork(): %s", strerror(errno));
+ YPXFR_RETURN(YPXFR_XFRERR)
+ break;
+ default:
+ result.xfrstat = YPXFR_SUCC;
+ children++;
+ forked = 0;
+ break;
+ }
+
+ return (&result);
+}
+#undef YPXFR_RETURN
+
+void *
+ypproc_clear_2_svc(void *argp, struct svc_req *rqstp)
+{
+ static char * result;
+ static char rval = 0;
+
+#ifdef DB_CACHE
+ if (yp_access(NULL, NULL, (struct svc_req *)rqstp))
+#else
+ if (yp_access(NULL, (struct svc_req *)rqstp))
+#endif
+ return (NULL);
+#ifdef DB_CACHE
+ /* clear out the database cache */
+ yp_flush_all();
+#endif
+ /* Re-read the securenets database for the hell of it. */
+ load_securenets();
+
+ result = &rval;
+ return((void *) &result);
+}
+
+/*
+ * For ypproc_all, we have to send a stream of ypresp_all structures
+ * via TCP, but the XDR filter generated from the yp.x protocol
+ * definition file only serializes one such structure. This means that
+ * to send the whole stream, you need a wrapper which feeds all the
+ * records into the underlying XDR routine until it hits an 'EOF.'
+ * But to use the wrapper, you have to violate the boundaries between
+ * RPC layers by calling svc_sendreply() directly from the ypproc_all
+ * service routine instead of letting the RPC dispatcher do it.
+ *
+ * Bleah.
+ */
+
+/*
+ * Custom XDR routine for serialzing results of ypproc_all: keep
+ * reading from the database and spew until we run out of records
+ * or encounter an error.
+ */
+static bool_t
+xdr_my_ypresp_all(register XDR *xdrs, ypresp_all *objp)
+{
+ while (1) {
+ /* Get a record. */
+ if ((objp->ypresp_all_u.val.stat =
+ yp_nextbykey(&objp->ypresp_all_u.val.key,
+ &objp->ypresp_all_u.val.val)) == YP_TRUE) {
+ objp->more = TRUE;
+ } else {
+ objp->more = FALSE;
+ }
+
+ /* Serialize. */
+ if (!xdr_ypresp_all(xdrs, objp))
+ return(FALSE);
+ if (objp->more == FALSE)
+ return(TRUE);
+ }
+}
+
+ypresp_all *
+ypproc_all_2_svc(ypreq_nokey *argp, struct svc_req *rqstp)
+{
+ static ypresp_all result;
+
+ /*
+ * Set this here so that the client will be forced to make
+ * at least one attempt to read from us even if all we're
+ * doing is returning an error.
+ */
+ result.more = TRUE;
+ result.ypresp_all_u.val.key.keydat_len = 0;
+ result.ypresp_all_u.val.key.keydat_val = "";
+
+#ifdef DB_CACHE
+ if (yp_access(argp->map, argp->domain, (struct svc_req *)rqstp)) {
+#else
+ if (yp_access(argp->map, (struct svc_req *)rqstp)) {
+#endif
+ result.ypresp_all_u.val.stat = YP_YPERR;
+ return (&result);
+ }
+
+ if (argp->domain == NULL || argp->map == NULL) {
+ result.ypresp_all_u.val.stat = YP_BADARGS;
+ return (&result);
+ }
+
+ /*
+ * XXX If we hit the child limit, fail the request.
+ * If we don't, and the map is large, we could block for
+ * a long time in the parent.
+ */
+ if (children >= MAX_CHILDREN) {
+ result.ypresp_all_u.val.stat = YP_YPERR;
+ return(&result);
+ }
+
+ /*
+ * The ypproc_all procedure can take a while to complete.
+ * Best to handle it in a subprocess so the parent doesn't
+ * block. (Is there a better way to do this? Maybe with
+ * async socket I/O?)
+ */
+ if (!debug && children < MAX_CHILDREN && fork()) {
+ children++;
+ forked = 0;
+ return (NULL);
+ } else {
+ forked++;
+ }
+
+ if (yp_select_map(argp->map, argp->domain,
+ &result.ypresp_all_u.val.key, 0) != YP_TRUE) {
+ result.ypresp_all_u.val.stat = yp_errno;
+ return(&result);
+ }
+
+ /* Kick off the actual data transfer. */
+ svc_sendreply(rqstp->rq_xprt, xdr_my_ypresp_all, (char *)&result);
+
+ /*
+ * Returning NULL prevents the dispatcher from calling
+ * svc_sendreply() since we already did it.
+ */
+ return (NULL);
+}
+
+ypresp_master *
+ypproc_master_2_svc(ypreq_nokey *argp, struct svc_req *rqstp)
+{
+ static ypresp_master result;
+ static char ypvalbuf[YPMAXRECORD];
+ keydat key = { MASTER_SZ, MASTER_STRING };
+ valdat val;
+
+ result.peer = "";
+
+#ifdef DB_CACHE
+ if (yp_access(argp->map, argp->domain, (struct svc_req *)rqstp)) {
+#else
+ if (yp_access(argp->map, (struct svc_req *)rqstp)) {
+#endif
+ result.stat = YP_YPERR;
+ return(&result);
+ }
+
+ if (argp->domain == NULL) {
+ result.stat = YP_BADARGS;
+ return (&result);
+ }
+
+ if (yp_select_map(argp->map, argp->domain, &key, 1) != YP_TRUE) {
+ result.stat = yp_errno;
+ return(&result);
+ }
+
+ /*
+ * Note that we copy the data retrieved from the database to
+ * a private buffer and NUL terminate the buffer rather than
+ * terminating the data in place. We do this because by stuffing
+ * a '\0' into data.data, we will actually be corrupting memory
+ * allocated by the DB package. This is a bad thing now that we
+ * cache DB handles rather than closing the database immediately.
+ */
+ result.stat = yp_getbykey(&key, &val);
+ if (result.stat == YP_TRUE) {
+ bcopy((char *)val.valdat_val, (char *)&ypvalbuf,
+ val.valdat_len);
+ ypvalbuf[val.valdat_len] = '\0';
+ result.peer = (char *)&ypvalbuf;
+ } else
+ result.peer = "";
+
+ return (&result);
+}
+
+ypresp_order *
+ypproc_order_2_svc(ypreq_nokey *argp, struct svc_req *rqstp)
+{
+ static ypresp_order result;
+ keydat key = { ORDER_SZ, ORDER_STRING };
+ valdat val;
+
+ result.ordernum = 0;
+
+#ifdef DB_CACHE
+ if (yp_access(argp->map, argp->domain, (struct svc_req *)rqstp)) {
+#else
+ if (yp_access(argp->map, (struct svc_req *)rqstp)) {
+#endif
+ result.stat = YP_YPERR;
+ return(&result);
+ }
+
+ if (argp->domain == NULL) {
+ result.stat = YP_BADARGS;
+ return (&result);
+ }
+
+ /*
+ * We could just check the timestamp on the map file,
+ * but that's a hack: we'll only know the last time the file
+ * was touched, not the last time the database contents were
+ * updated.
+ */
+
+ if (yp_select_map(argp->map, argp->domain, &key, 1) != YP_TRUE) {
+ result.stat = yp_errno;
+ return(&result);
+ }
+
+ result.stat = yp_getbykey(&key, &val);
+
+ if (result.stat == YP_TRUE)
+ result.ordernum = atoi((char *)val.valdat_val);
+ else
+ result.ordernum = 0;
+
+ return (&result);
+}
+
+static void yp_maplist_free(yp_maplist)
+ struct ypmaplist *yp_maplist;
+{
+ register struct ypmaplist *next;
+
+ while(yp_maplist) {
+ next = yp_maplist->next;
+ free(yp_maplist->map);
+ free(yp_maplist);
+ yp_maplist = next;
+ }
+ return;
+}
+
+static struct ypmaplist *yp_maplist_create(domain)
+ const char *domain;
+{
+ char yp_mapdir[MAXPATHLEN + 2];
+ char yp_mapname[MAXPATHLEN + 2];
+ struct ypmaplist *cur = NULL;
+ struct ypmaplist *yp_maplist = NULL;
+ DIR *dird;
+ struct dirent *dirp;
+ struct stat statbuf;
+
+ snprintf(yp_mapdir, sizeof(yp_mapdir), "%s/%s", yp_dir, domain);
+
+ if ((dird = opendir(yp_mapdir)) == NULL) {
+ yp_error("opendir(%s) failed: %s", yp_mapdir, strerror(errno));
+ return(NULL);
+ }
+
+ while ((dirp = readdir(dird)) != NULL) {
+ if (strcmp(dirp->d_name, ".") && strcmp(dirp->d_name, "..")) {
+ snprintf(yp_mapname, sizeof(yp_mapname), "%s/%s",
+ yp_mapdir,dirp->d_name);
+ if (stat(yp_mapname, &statbuf) < 0 ||
+ !S_ISREG(statbuf.st_mode))
+ continue;
+ if ((cur = (struct ypmaplist *)
+ malloc(sizeof(struct ypmaplist))) == NULL) {
+ yp_error("malloc() failed");
+ closedir(dird);
+ yp_maplist_free(yp_maplist);
+ return(NULL);
+ }
+ if ((cur->map = (char *)strdup(dirp->d_name)) == NULL) {
+ yp_error("strdup() failed: %s",strerror(errno));
+ closedir(dird);
+ yp_maplist_free(yp_maplist);
+ return(NULL);
+ }
+ cur->next = yp_maplist;
+ yp_maplist = cur;
+ if (debug)
+ yp_error("map: %s", yp_maplist->map);
+ }
+
+ }
+ closedir(dird);
+ return(yp_maplist);
+}
+
+ypresp_maplist *
+ypproc_maplist_2_svc(domainname *argp, struct svc_req *rqstp)
+{
+ static ypresp_maplist result = { 0, NULL };
+
+#ifdef DB_CACHE
+ if (yp_access(NULL, NULL, (struct svc_req *)rqstp)) {
+#else
+ if (yp_access(NULL, (struct svc_req *)rqstp)) {
+#endif
+ result.stat = YP_YPERR;
+ return(&result);
+ }
+
+ if (argp == NULL) {
+ result.stat = YP_BADARGS;
+ return (&result);
+ }
+
+ if (yp_validdomain(*argp)) {
+ result.stat = YP_NODOM;
+ return (&result);
+ }
+
+ /*
+ * We have to construct a linked list for the ypproc_maplist
+ * procedure using dynamically allocated memory. Since the XDR
+ * layer won't free this list for us, we have to deal with it
+ * ourselves. We call yp_maplist_free() first to free any
+ * previously allocated data we may have accumulated to insure
+ * that we have only one linked list in memory at any given
+ * time.
+ */
+
+ yp_maplist_free(result.maps);
+
+ if ((result.maps = yp_maplist_create(*argp)) == NULL) {
+ yp_error("yp_maplist_create failed");
+ result.stat = YP_YPERR;
+ return(&result);
+ } else
+ result.stat = YP_TRUE;
+
+ return (&result);
+}
+
+/*
+ * NIS v1 support. The nullproc, domain and domain_nonack
+ * functions from v1 are identical to those in v2, so all
+ * we have to do is hand off to them.
+ *
+ * The other functions are mostly just wrappers around their v2
+ * counterparts. For example, for the v1 'match' procedure, we
+ * crack open the argument structure, make a request to the v2
+ * 'match' function, repackage the data into a v1 response and
+ * then send it on its way.
+ *
+ * Note that we don't support the pull, push and get procedures.
+ * There's little documentation available to show what they
+ * do, and I suspect they're meant largely for map transfers
+ * between master and slave servers.
+ */
+
+void *
+ypoldproc_null_1_svc(void *argp, struct svc_req *rqstp)
+{
+ return(ypproc_null_2_svc(argp, rqstp));
+}
+
+bool_t *
+ypoldproc_domain_1_svc(domainname *argp, struct svc_req *rqstp)
+{
+ return(ypproc_domain_2_svc(argp, rqstp));
+}
+
+bool_t *
+ypoldproc_domain_nonack_1_svc(domainname *argp, struct svc_req *rqstp)
+{
+ return (ypproc_domain_nonack_2_svc(argp, rqstp));
+}
+
+/*
+ * the 'match' procedure sends a response of type YPRESP_VAL
+ */
+ypresponse *
+ypoldproc_match_1_svc(yprequest *argp, struct svc_req *rqstp)
+{
+ static ypresponse result;
+ ypresp_val *v2_result;
+
+ result.yp_resptype = YPRESP_VAL;
+ result.ypresponse_u.yp_resp_valtype.val.valdat_val = "";
+ result.ypresponse_u.yp_resp_valtype.val.valdat_len = 0;
+
+ if (argp->yp_reqtype != YPREQ_KEY) {
+ result.ypresponse_u.yp_resp_valtype.stat = YP_BADARGS;
+ return(&result);
+ }
+
+ v2_result = ypproc_match_2_svc(&argp->yprequest_u.yp_req_keytype,rqstp);
+ if (v2_result == NULL)
+ return(NULL);
+
+ bcopy((char *)v2_result,
+ (char *)&result.ypresponse_u.yp_resp_valtype,
+ sizeof(ypresp_val));
+
+ return (&result);
+}
+
+/*
+ * the 'first' procedure sends a response of type YPRESP_KEY_VAL
+ */
+ypresponse *
+ypoldproc_first_1_svc(yprequest *argp, struct svc_req *rqstp)
+{
+ static ypresponse result;
+ ypresp_key_val *v2_result;
+
+ result.yp_resptype = YPRESP_KEY_VAL;
+ result.ypresponse_u.yp_resp_key_valtype.val.valdat_val =
+ result.ypresponse_u.yp_resp_key_valtype.key.keydat_val = "";
+ result.ypresponse_u.yp_resp_key_valtype.val.valdat_len =
+ result.ypresponse_u.yp_resp_key_valtype.key.keydat_len = 0;
+
+ if (argp->yp_reqtype != YPREQ_NOKEY) {
+ result.ypresponse_u.yp_resp_key_valtype.stat = YP_BADARGS;
+ return(&result);
+ }
+
+ v2_result = ypproc_first_2_svc(&argp->yprequest_u.yp_req_nokeytype,
+ rqstp);
+ if (v2_result == NULL)
+ return(NULL);
+
+ bcopy((char *)v2_result,
+ (char *)&result.ypresponse_u.yp_resp_key_valtype,
+ sizeof(ypresp_key_val));
+
+ return (&result);
+}
+
+/*
+ * the 'next' procedure sends a response of type YPRESP_KEY_VAL
+ */
+ypresponse *
+ypoldproc_next_1_svc(yprequest *argp, struct svc_req *rqstp)
+{
+ static ypresponse result;
+ ypresp_key_val *v2_result;
+
+ result.yp_resptype = YPRESP_KEY_VAL;
+ result.ypresponse_u.yp_resp_key_valtype.val.valdat_val =
+ result.ypresponse_u.yp_resp_key_valtype.key.keydat_val = "";
+ result.ypresponse_u.yp_resp_key_valtype.val.valdat_len =
+ result.ypresponse_u.yp_resp_key_valtype.key.keydat_len = 0;
+
+ if (argp->yp_reqtype != YPREQ_KEY) {
+ result.ypresponse_u.yp_resp_key_valtype.stat = YP_BADARGS;
+ return(&result);
+ }
+
+ v2_result = ypproc_next_2_svc(&argp->yprequest_u.yp_req_keytype,rqstp);
+ if (v2_result == NULL)
+ return(NULL);
+
+ bcopy((char *)v2_result,
+ (char *)&result.ypresponse_u.yp_resp_key_valtype,
+ sizeof(ypresp_key_val));
+
+ return (&result);
+}
+
+/*
+ * the 'poll' procedure sends a response of type YPRESP_MAP_PARMS
+ */
+ypresponse *
+ypoldproc_poll_1_svc(yprequest *argp, struct svc_req *rqstp)
+{
+ static ypresponse result;
+ ypresp_master *v2_result1;
+ ypresp_order *v2_result2;
+
+ result.yp_resptype = YPRESP_MAP_PARMS;
+ result.ypresponse_u.yp_resp_map_parmstype.domain =
+ argp->yprequest_u.yp_req_nokeytype.domain;
+ result.ypresponse_u.yp_resp_map_parmstype.map =
+ argp->yprequest_u.yp_req_nokeytype.map;
+ /*
+ * Hmm... there is no 'status' value in the
+ * yp_resp_map_parmstype structure, so I have to
+ * guess at what to do to indicate a failure.
+ * I hope this is right.
+ */
+ result.ypresponse_u.yp_resp_map_parmstype.ordernum = 0;
+ result.ypresponse_u.yp_resp_map_parmstype.peer = "";
+
+ if (argp->yp_reqtype != YPREQ_MAP_PARMS) {
+ return(&result);
+ }
+
+ v2_result1 = ypproc_master_2_svc(&argp->yprequest_u.yp_req_nokeytype,
+ rqstp);
+ if (v2_result1 == NULL)
+ return(NULL);
+
+ if (v2_result1->stat != YP_TRUE) {
+ return(&result);
+ }
+
+ v2_result2 = ypproc_order_2_svc(&argp->yprequest_u.yp_req_nokeytype,
+ rqstp);
+ if (v2_result2 == NULL)
+ return(NULL);
+
+ if (v2_result2->stat != YP_TRUE) {
+ return(&result);
+ }
+
+ result.ypresponse_u.yp_resp_map_parmstype.peer =
+ v2_result1->peer;
+ result.ypresponse_u.yp_resp_map_parmstype.ordernum =
+ v2_result2->ordernum;
+
+ return (&result);
+}
+
+ypresponse *
+ypoldproc_push_1_svc(yprequest *argp, struct svc_req *rqstp)
+{
+ static ypresponse result;
+
+ /*
+ * Not implemented.
+ */
+
+ return (&result);
+}
+
+ypresponse *
+ypoldproc_pull_1_svc(yprequest *argp, struct svc_req *rqstp)
+{
+ static ypresponse result;
+
+ /*
+ * Not implemented.
+ */
+
+ return (&result);
+}
+
+ypresponse *
+ypoldproc_get_1_svc(yprequest *argp, struct svc_req *rqstp)
+{
+ static ypresponse result;
+
+ /*
+ * Not implemented.
+ */
+
+ return (&result);
+}
diff --git a/usr.sbin/ypserv/yp_svc_udp.c b/usr.sbin/ypserv/yp_svc_udp.c
new file mode 100644
index 0000000..728a181
--- /dev/null
+++ b/usr.sbin/ypserv/yp_svc_udp.c
@@ -0,0 +1,89 @@
+/*
+ * Copyright (c) 1996
+ * Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by Bill Paul.
+ * 4. Neither the name of the author nor the names of any co-contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#include <rpc/rpc.h>
+#include "yp_extern.h"
+
+/*
+ * XXX Must not diverge from what's in src/lib/libc/rpc/svc_udp.c
+ */
+
+/*
+ * kept in xprt->xp_p2
+ */
+struct svcudp_data {
+ u_int su_iosz; /* byte size of send.recv buffer */
+ u_long su_xid; /* transaction id */
+ XDR su_xdrs; /* XDR handle */
+ char su_verfbody[MAX_AUTH_BYTES]; /* verifier body */
+ char * su_cache; /* cached data, NULL if no cache */
+};
+#define su_data(xprt) ((struct svcudp_data *)(xprt->xp_p2))
+
+/*
+ * We need to be able to manually set the transaction ID in the
+ * UDP transport handle, but the standard library offers us no way
+ * to do that. Hence we need this garbage.
+ */
+
+unsigned long
+svcudp_get_xid(xprt)
+ SVCXPRT *xprt;
+{
+ struct svcudp_data *su;
+
+ if (xprt == NULL)
+ return(0);
+ su = su_data(xprt);
+ return(su->su_xid);
+}
+
+unsigned long
+svcudp_set_xid(xprt, xid)
+ SVCXPRT *xprt;
+ unsigned long xid;
+{
+ struct svcudp_data *su;
+ unsigned long old_xid;
+
+ if (xprt == NULL)
+ return(0);
+ su = su_data(xprt);
+ old_xid = su->su_xid;
+ su->su_xid = xid;
+ return(old_xid);
+}
diff --git a/usr.sbin/ypserv/ypserv.8 b/usr.sbin/ypserv/ypserv.8
new file mode 100644
index 0000000..740313c
--- /dev/null
+++ b/usr.sbin/ypserv/ypserv.8
@@ -0,0 +1,420 @@
+.\" Copyright (c) 1995
+.\" Bill Paul <wpaul@ctr.columbia.edu>. All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by Bill Paul.
+.\" 4. Neither the name of the author nor the names of any co-contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL Bill Paul OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $Id: ypserv.8,v 1.13 1997/10/29 07:25:05 charnier Exp $
+.\"
+.Dd February 4, 1995
+.Dt YPSERV 8
+.Os
+.Sh NAME
+.Nm ypserv
+.Nd NIS database server
+.Sh SYNOPSIS
+.Nm
+.Op Fl n
+.Op Fl d
+.Op Fl p Ar path
+.Sh DESCRIPTION
+.Tn NIS
+is an RPC-based service designed to allow a number of UNIX-based
+machines to share a common set of configuration files. Rather than
+requiring a system administrator to update several copies of files
+such as
+.Pa /etc/hosts ,
+.Pa /etc/passwd
+and
+.Pa /etc/group ,
+which tend to require frequent changes in most environments,
+.Tn NIS
+allows groups of computers to share one set of data which can be
+updated from a single location.
+.Pp
+The
+.Nm
+program is the server that distributes
+.Tn NIS
+databases to client systems within an
+.Tn NIS
+.Em domain .
+Each client in an
+.Tn NIS
+domain must have its domainname set to
+one of the domains served by
+.Nm
+using the
+.Xr domainname 1
+command. The clients must also run
+.Xr ypbind 8
+in order to attach to a particular server, since it is possible to
+have several servers within a single
+.Tn NIS
+domain.
+.Pp
+The databases distributed by
+.Nm
+are stored in
+.Pa /var/yp/[domainname]
+where
+.Pa domainname
+is the name of the domain being served. There can be several
+such directories with different domainnames, and you need only one
+.Nm
+daemon to handle them all.
+.Pp
+The databases, or
+.Pa maps
+as they are often called,
+are created by
+.Pa /var/yp/Makefile
+using several system files as source. The database files are in
+.Xr db 3
+format to help speed retrieval when there are many records involved.
+In
+.Bx Free ,
+the maps are always readable and writable only by root for security
+reasons. Technically this is only necessary for the password
+maps, but since the data in the other maps can be found in
+other world-readable files anyway, it doesn't hurt and it's considered
+good general practice.
+.Pp
+The
+.Nm
+program is started by
+.Pa /etc/rc.network
+if it has been enabled in
+.Pa /etc/rc.conf .
+.Sh SPECIAL FEATURES
+There are some problems associated with distributing FreeBSD's password
+database via
+.Tn NIS Ns :
+.Bx Free
+normally only stores encrypted passwords
+in
+.Pa /etc/master.passwd ,
+which is readable and writable only by root. By turning this file
+into an
+.Tn NIS
+map, this security feature would be completely defeated.
+.Pp
+To make up for this, the
+.Bx Free
+version of
+.Nm
+handles the
+.Pa master.passwd.byname
+and
+.Pa master.basswd.byuid
+maps in a special way. When the server receives a request to access
+either of these two maps, it will check the TCP port from which the
+request originated and return an error if the port number is greater
+than 1023. Since only the superuser is allowed to bind to TCP ports
+with values less than 1024, the server can use this test to determine
+whether or not the access request came from a privileged user.
+Any requests made by non-privileged users are therefore rejected.
+.Pp
+Furthermore, the
+.Xr getpwent 3
+routines in
+.Bx Free Ns 's
+standard C library will only attempt to retrieve
+data from the
+.Pa master.passwd.byname
+and
+.Pa master.passwd.byuid
+maps for the superuser: if a normal user calls any of these functions,
+the standard
+.Pa passwd.byname
+and
+.Pa passwd.byuid
+maps will be accessed instead. The latter two maps are constructed by
+.Pa /var/yp/Makefile
+by parsing the
+.Pa master.passwd
+file and stripping out the password fields, and are therefore
+safe to pass on to unprivileged users. In this way, the shadow password
+aspect of the protected
+.Pa master.passwd
+database is maintained through
+.Tn NIS .
+.Pp
+.Sh NOTES
+.Ss Limitations
+There are two problems inherent with password shadowing in
+.Tn NIS
+that users should
+be aware of:
+.Bl -enum -offset indent
+.It
+The
+.Sq TCP port less than 1024
+test is trivial to defeat for users with
+unrestricted access to machines on your network (even those machines
+which do not run UNIX-based operating systems).
+.It
+If you plan to use a
+.Bx Free
+system to serve
+.Bx non-Free
+clients that
+have no support for password shadowing (which is most of them), you
+will have to disable the password shadowing entirely by uncommenting the
+.Em UNSECURE=True
+entry in
+.Pa /var/yp/Makefile .
+This will cause the standard
+.Pa passwd.byname
+and
+.Pa passwd.byuid
+maps to be generated with valid encrypted password fields, which is
+necessary in order for
+.Bx non-Free
+clients to perform user
+authentication through
+.Tn NIS .
+.El
+.Pp
+.Ss Security
+In general, any remote user can issue an RPC to
+.Nm
+and retrieve the contents of your
+.Tn NIS
+maps, provided the remote user
+knows your domain name. To prevent such unauthorized transactions,
+.Nm
+supports a feature called
+.Pa securenets
+which can be used to restrict access to a given set of hosts.
+At startup,
+.Nm
+will attempt to load the securenets information from a file
+called
+.Pa /var/yp/securenets .
+(Note that this path varies depending on the path specified with
+the
+.Fl p
+option, which is explained below.) This file contains entries
+that consist of a network specification and a network mask separated
+by white space.
+Lines starting with
+.Dq \&#
+are considered to be comments. A
+sample securenets file might look like this:
+.Bd -unfilled -offset indent
+# allow connections from local host -- mandatory
+127.0.0.1 255.255.255.255
+# allow connections from any host
+# on the 192.168.128.0 network
+192.168.128.0 255.255.255.0
+# allow connections from any host
+# between 10.0.0.0 to 10.0.15.255
+10.0.0.0 255.255.240.0
+.Ed
+.Pp
+If
+.Nm
+receives a request from an address that matches one of these rules,
+it will process the request normally. If the address fails to match
+a rule, the request will be ignored and a warning message will be
+logged. If the
+.Pa /var/yp/securenets
+file does not exist,
+.Nm
+will allow connections from any host.
+.Pp
+The
+.Nm
+program also has support for Wietse Venema's
+.Em tcpwrapper
+package, though it is not compiled in by default since
+the
+.Em tcpwrapper
+package is not distributed with
+.Bx Free .
+However, if you have
+.Pa libwrap.a
+and
+.Pa tcpd.h ,
+you can easily recompile
+.Nm
+with them. This allows the administrator to use the tcpwrapper
+configuration files (
+.Pa /etc/hosts.allow
+and
+.Pa /etc/hosts.deny )
+for access control instead of
+.Pa /var/yp/securenets .
+.Pp
+Note: while both of these access control mechanisms provide some
+security, they, like the privileged port test, are both vulnerable
+to
+.Dq IP spoofing
+attacks.
+.Pp
+.Ss NIS v1 compatibility
+This version of
+.Nm
+has some support for serving
+.Tn NIS
+v1 clients.
+.Bx Free Ns 's
+.Tn NIS
+implementation only uses the
+.Tn NIS
+v2 protocol, however other implementations
+include support for the v1 protocol for backwards compatibility
+with older systems. The
+.Xr ypbind 8
+daemons supplied with these systems will try to establish a binding
+to an
+.Tn NIS
+v1 server even though they may never actually need it (and they may
+persist in broadcasting in search of one even after they receive a
+response from a v2 server). Note that while
+support for normal client calls is provided, this version of
+.Nm
+does not handle v1 map transfer requests; consequently, it can not
+be used as a master or slave in conjunction with older
+.Tn NIS
+servers that
+only support the v1 protocol. Fortunately, there probably aren't any
+such servers still in use today.
+.Ss NIS servers that are also NIS clients
+Care must be taken when running
+.Nm
+in a multi-server domain where the server machines are also
+.Tn NIS
+clients. It is generally a good idea to force the servers to
+bind to themselves rather than allowing them to broadcast bind
+requests and possibly become bound to each other: strange failure
+modes can result if one server goes down and
+others are dependent upon on it. (Eventually all the clients will
+time out and attempt to bind to other servers, but the delay
+involved can be considerable and the failure mode is still present
+since the servers might bind to each other all over again).
+.Pp
+Refer to the
+.Xr ypbind 8
+man page for details on how to force it to bind to a particular
+server.
+.Sh OPTIONS
+The following options are supported by
+.Nm Ns :
+.Bl -tag -width flag
+.It Fl n
+This option affects the way
+.Nm
+handles yp_match requests for the
+.Pa hosts.byname
+and
+.Pa hosts.byaddress
+maps. By default, if
+.Nm
+can't find an entry for a given host in its hosts maps, it will
+return an error and perform no further processing. With the
+.Fl n
+flag,
+.Nm
+will go one step further: rather than giving up immediately, it
+will try to resolve the hostname or address using a DNS nameserver
+query. If the query is successful,
+.Nm
+will construct a fake database record and return it to the client,
+thereby making it seem as though the client's yp_match request
+succeeded.
+.Pp
+This feature is provided for compatiblity with SunOS 4.1.x,
+which has brain-damaged resolver functions in its standard C
+library that depend on
+.Tn NIS
+for hostname and address resolution.
+.Bx Free Ns 's
+resolver can be configured to do DNS
+queries directly, therefore it is not necessary to enable this
+option when serving only
+.Bx Free
+.Tn NIS
+clients.
+.It Fl d
+Cause the server to run in debugging mode. Normally,
+.Nm
+reports only unusual errors (access violations, file access failures)
+using the
+.Xr syslog 3
+facility. In debug mode, the server does not background
+itself and prints extra status messages to stderr for each
+request that it receives. Also, while running in debug mode,
+.Nm
+will not spawn any additional subprocesses as it normally does
+when handling yp_all requests or doing DNS lookups. (These actions
+often take a fair amount of time to complete and are therefore handled
+in subprocesses, allowing the parent server process to go on handling
+other requests.) This makes it easier to trace the server with
+a debugging tool.
+.It Fl p Ar path
+Normally,
+.Nm
+assumes that all
+.Tn NIS
+maps are stored under
+.Pa /var/yp .
+The
+.Fl p
+flag may be used to specify an alternate
+.Tn NIS
+root path, allowing
+the system administrator to move the map files to a different place
+within the filesystem.
+.El
+.Sh FILES
+.Bl -tag -width Pa -compact
+.It Pa /var/yp/[domainname]/[maps]
+the
+.Tn NIS
+maps
+.It Pa /etc/host.conf
+resolver configuration file
+.It Pa /var/yp/securenets
+host access control file
+.El
+.Sh SEE ALSO
+.Xr ypcat 1 ,
+.Xr db 3 ,
+.Xr yp 4 ,
+.Xr ypbind 8 ,
+.Xr yppasswdd 8 ,
+.Xr yppush 8 ,
+.Xr ypxfr 8
+.Sh AUTHOR
+.An Bill Paul Aq wpaul@ctr.columbia.edu
+.Sh HISTORY
+This version of
+.Nm
+first appeared in
+.Fx 2.2 .
diff --git a/usr.sbin/ypset/Makefile b/usr.sbin/ypset/Makefile
new file mode 100644
index 0000000..2859110
--- /dev/null
+++ b/usr.sbin/ypset/Makefile
@@ -0,0 +1,7 @@
+# from: @(#)Makefile 5.8 (Berkeley) 7/28/90
+# Makefile,v 1.1 1994/08/08 01:10:27 wollman Exp
+
+PROG= ypset
+MAN8= ypset.8
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/ypset/ypset.8 b/usr.sbin/ypset/ypset.8
new file mode 100644
index 0000000..60c6fac
--- /dev/null
+++ b/usr.sbin/ypset/ypset.8
@@ -0,0 +1,85 @@
+.\"
+.\" Copyright (c) 1994 Jason R. Thorpe
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\" must display the following acknowledgement:
+.\" This product includes software developed by Jason Thorpe.
+.\" 4. Neither the name of the author nor the names of its contributors
+.\" may be used to endorse or promote products derived from this software
+.\" without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $Id: ypset.8,v 1.4 1997/02/22 16:15:19 peter Exp $
+.\"
+.Dd October 25, 1994
+.Dt YPSET 8
+.Os
+.Sh NAME
+.Nm ypset
+.Nd tell
+.Xr ypbind 8
+which YP server process to use
+.Sh SYNOPSIS
+.Nm ypset
+.Op Fl h Ar host
+.Op Fl d Ar domain
+.Ar server
+.Sh DESCRIPTION
+.Nm Ypset
+tells the
+.Xr ypbind 8
+process on the current machine which YP server process to communicate with.
+If
+.Ar server
+is down or is not running a YP server process, it is not discovered until
+a YP client process attempts to access a YP map, at which time
+.Xr ypbind 8
+tests the binding and takes appropriate action.
+.Pp
+.Nm Ypset
+is most useful for binding a YP client that is not on the same broadcast
+network as the closest YP server, but can also be used for debugging
+a local network's YP configuration, testing specific YP client
+programs, or binding to a specific server when there are many servers on
+the local network supplying YP maps.
+.Pp
+The options are as follows:
+.Bl -tag -width indent
+.It Fl h Ar host
+Set the YP binding on
+.Ar host
+instead of the local machine.
+.It Fl d Ar domain
+Use the YP domain
+.Ar domain
+instead of the default domain as returned by
+.Xr domainname 1 .
+.El
+.Sh SEE ALSO
+.Xr domainname 1 ,
+.Xr ypcat 1 ,
+.Xr ypmatch 1 ,
+.Xr yp 4 ,
+.Xr ypbind 8
+.Sh AUTHOR
+.An Theo De Raadt
diff --git a/usr.sbin/ypset/ypset.c b/usr.sbin/ypset/ypset.c
new file mode 100644
index 0000000..1ab8f3d
--- /dev/null
+++ b/usr.sbin/ypset/ypset.c
@@ -0,0 +1,156 @@
+/*
+ * Copyright (c) 1992/3 Theo de Raadt <deraadt@fsa.ca>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior written
+ * permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+#include <err.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <sys/param.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <rpc/rpc.h>
+#include <rpc/xdr.h>
+#include <rpcsvc/yp.h>
+struct dom_binding{};
+#include <rpcsvc/ypclnt.h>
+#include <arpa/inet.h>
+
+extern bool_t xdr_domainname();
+
+static void
+usage()
+{
+ fprintf(stderr, "usage: ypset [-h host] [-d domain] server\n");
+ exit(1);
+}
+
+int
+bind_tohost(sin, dom, server)
+struct sockaddr_in *sin;
+char *dom, *server;
+{
+ struct ypbind_setdom ypsd;
+ struct timeval tv;
+ struct hostent *hp;
+ CLIENT *client;
+ int sock, port;
+ int r;
+ unsigned long server_addr;
+
+ if( (port=htons(getrpcport(server, YPPROG, YPPROC_NULL, IPPROTO_UDP))) == 0)
+ errx(1, "%s not running ypserv", server);
+
+ bzero(&ypsd, sizeof ypsd);
+
+ if( (hp = gethostbyname (server)) != NULL ) {
+ /* is this the most compatible way?? */
+ bcopy (hp->h_addr_list[0],
+ (u_long *)&ypsd.ypsetdom_binding.ypbind_binding_addr,
+ sizeof (unsigned long));
+ } else if( (long)(server_addr = inet_addr (server)) == -1) {
+ errx(1, "can't find address for %s", server);
+ } else
+ bcopy (&server_addr,
+ *(u_long *)&ypsd.ypsetdom_binding.ypbind_binding_addr,
+ sizeof (server_addr));
+
+/* strncpy(ypsd.ypsetdom_domain, dom, sizeof ypsd.ypsetdom_domain); */
+ ypsd.ypsetdom_domain = dom;
+ *(u_long *)&ypsd.ypsetdom_binding.ypbind_binding_port = port;
+ ypsd.ypsetdom_vers = YPVERS;
+
+ tv.tv_sec = 15;
+ tv.tv_usec = 0;
+ sock = RPC_ANYSOCK;
+ client = clntudp_create(sin, YPBINDPROG, YPBINDVERS, tv, &sock);
+ if (client==NULL) {
+ warnx("can't yp_bind, reason: %s", yperr_string(YPERR_YPBIND));
+ return YPERR_YPBIND;
+ }
+ client->cl_auth = authunix_create_default();
+
+ r = clnt_call(client, YPBINDPROC_SETDOM,
+ xdr_ypbind_setdom, &ypsd, xdr_void, NULL, tv);
+ if(r) {
+ warnx("sorry, cannot ypset for domain %s on host", dom);
+ clnt_destroy(client);
+ return YPERR_YPBIND;
+ }
+ clnt_destroy(client);
+ return 0;
+}
+
+int
+main(argc, argv)
+char **argv;
+{
+ struct sockaddr_in sin;
+ struct hostent *hent;
+ extern char *optarg;
+ extern int optind;
+ char *domainname;
+ int c;
+
+ yp_get_default_domain(&domainname);
+
+ bzero(&sin, sizeof sin);
+ sin.sin_family = AF_INET;
+ sin.sin_addr.s_addr = htonl(0x7f000001);
+
+ while( (c=getopt(argc, argv, "h:d:")) != -1)
+ switch(c) {
+ case 'd':
+ domainname = optarg;
+ break;
+ case 'h':
+ if( (sin.sin_addr.s_addr=inet_addr(optarg)) == -1) {
+ hent = gethostbyname(optarg);
+ if(hent==NULL)
+ errx(1, "host %s unknown", optarg);
+ bcopy(&hent->h_addr_list[0], &sin.sin_addr,
+ sizeof sin.sin_addr);
+ }
+ break;
+ default:
+ usage();
+ }
+
+ if(optind + 1 != argc )
+ usage();
+
+ if (bind_tohost(&sin, domainname, argv[optind]))
+ exit(1);
+ exit(0);
+}
diff --git a/usr.sbin/zic/Makefile b/usr.sbin/zic/Makefile
new file mode 100644
index 0000000..963a0ee
--- /dev/null
+++ b/usr.sbin/zic/Makefile
@@ -0,0 +1,5 @@
+# $Id$
+SUBDIR= zic zdump
+
+.include <bsd.subdir.mk>
+
diff --git a/usr.sbin/zic/Makefile.inc b/usr.sbin/zic/Makefile.inc
new file mode 100644
index 0000000..c961f85
--- /dev/null
+++ b/usr.sbin/zic/Makefile.inc
@@ -0,0 +1,3 @@
+# $Id$
+
+.include "${.CURDIR}/../../Makefile.inc"
diff --git a/usr.sbin/zic/Music b/usr.sbin/zic/Music
new file mode 100644
index 0000000..9fb0cec
--- /dev/null
+++ b/usr.sbin/zic/Music
@@ -0,0 +1,81 @@
+@(#)Music 7.4
+
+Data on recordings of "Save That Time," Russ Long, Serrob Publishing, BMI:
+--------------------------------------------------------------------------
+Artist: Karrin Allyson
+CD: I Didn't Know About You
+Copyright Date: 1993
+Label: Concord Jazz, Inc.
+ID: CCD-4543
+Track Time: 3:44
+Personnel: Karrin Allyson, vocal
+ Russ Long, piano
+ Gerald Spaits, bass
+ Todd Strait, drums
+Notes: CD notes "additional lyric by Karrin Allyson;
+ arranged by Russ Long and Karrin Allyson"
+Rating: 1 star
+--------------------------------------------------------------------------
+Artist: Kevin Mahogany
+CD: Double Rainbow
+Copyright Date: 1993
+Label: Enja Records
+ID: ENJ-7097 2
+Track Time: 6:27
+Personnel: Kevin Mahogany, vocal
+ Kenny Barron, piano
+ Ray Drummond, bss
+ Ralph Moore, tenor saxophone
+ Lewis Nash, drums
+Rating: 1.5 stars
+--------------------------------------------------------------------------
+Artist: Joe Williams
+CD: Here's to Life
+Copyright Date: 1994
+Label: Telarc International Corporation
+ID: CD-83357
+Track Time: 3:58
+Personnel: Joe Williams, vocal
+ The Robert Farnon [39 piece] Orchestra
+Rating: black dot
+--------------------------------------------------------------------------
+Artist: Charles Fambrough
+CD: Keeper of the Spirit
+Copyright Date: 1995
+Label: AudioQuest Music
+ID: AQ-CD1033
+Track Time: 7:07
+Personnel: Charles Fambrough, bass
+ Joel Levine, tenor recorder
+ Edward Simon, piano
+ Lenny White, drums
+ Marion Simon, percussion
+Rating: 2 stars
+==========================================================================
+Also of note:
+Artist: Milt Hinton
+CD: Old Man Time
+Date: 1993
+Label: Chiaroscuro
+ID: CR(D) 310
+Total Time: 149:38 (two CDs)
+Personnel: Milt Hinton, bass
+ Doc Cheatham, Dizzy Gillespie, Clark Terry, trumpet
+ Al Grey, trombone
+ Eddier Barefield, Joe Camel (Flip Phillips), Buddy Tate,
+ clarinet and saxophone
+ John Bunch, Red Richards, Norman Simmons, Derek Smith,
+ Ralph Sutton, piano
+ Danny Barker, Al Casey, guitar
+ Gus Johnson, Gerryck King, Bob Rosengarden, Jackie Williams,
+ drums
+ Lionel Hampton, vibraphone
+ Cab Calloway, Joe Williams, vocal
+ Buck Clayton, arrangements
+Notes: tunes include Old Man Time, Time After Time,
+ Sometimes I'm Happy,
+ A Hot Time in the Old Town Tonight,
+ Four or Five Times, Now's the Time,
+ Time on My Hands, This Time It's Us,
+ and Good Time Charlie
+Rating: 3 stars
diff --git a/usr.sbin/zic/README b/usr.sbin/zic/README
new file mode 100644
index 0000000..3ca53ca
--- /dev/null
+++ b/usr.sbin/zic/README
@@ -0,0 +1,66 @@
+@(#)README 7.8
+
+"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 on 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 "-lz" 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..2e34663
--- /dev/null
+++ b/usr.sbin/zic/Theory
@@ -0,0 +1,120 @@
+@(#)Theory 7.4
+
+These time and date functions are much like the System V Release 2.0 (SVR2)
+time and date functions; there are a few additions and changes to extend
+the usefulness of the SVR2 functions:
+
+* In SVR2, time display in a process is controlled by the environment
+ variable TZ, which "must be a three-letter time zone name, followed
+ by a number representing the difference between local time and
+ Greenwich Mean Time in hours, followed by an optional three-letter
+ name for a daylight time zone;" when the optional daylight time zone is
+ present, "standard U.S.A. Daylight Savings Time conversion is applied."
+ This means that SVR2 can't deal with other (for example, Australian)
+ daylight savings time rules, or situations where more than two
+ time zone abbreviations are used in an area.
+
+* In SVR2, time conversion information is compiled into each program
+ that does time conversion. This means that when 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 SVR2, time conversion fails for near-minimum or near-maximum
+ time_t values when doing conversions for places that don't use GMT.
+
+* In SVR2, 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 GMT" to get
+ around the problem, doing so is inconvenient and precludes handling
+ daylight savings time shifts--as might be required to limit phone
+ calls to off-peak hours.)
+
+* These functions can account for leap seconds, thanks to Bradley White
+ (bww@k.cs.cmu.edu).
+
+These are the changes that have been made to the SVR2 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 SVR2, 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 or supported. (You can use a compile-time option to cause
+ these variables to be defined and to be 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.)
+
+Points of interest to folks with Version 7 or BSD systems:
+
+* The BSD "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 BSD gettimeofday function is not used in this package;
+ this lets users control the time zone used in doing time conversions.
+ Users who don't try to control things (that is, users who do not set
+ the environment variable TZ) get the time conversion specified in the
+ file "/etc/zoneinfo/localtime"; see the time zone compiler writeup for
+ information on how to initialize this file.
+
+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
+close to SVR2 (with the exceptions outlined above) to ensure its broad
+acceptability. If more powerful time conversion functions can be standardized,
+so much the better.
diff --git a/usr.sbin/zic/WWW b/usr.sbin/zic/WWW
new file mode 100644
index 0000000..d2fd684
--- /dev/null
+++ b/usr.sbin/zic/WWW
@@ -0,0 +1,71 @@
+# '@(#)WWW 7.3'
+
+# From Paul Eggert <eggert@twinsun.com> (1995-11-03)
+#
+# The Web has several other sources for time zone and daylight savings data.
+# Here are some recent links that may be of interest.
+#
+# Date and Time Gateway
+# http://www.bsdi.com/date
+# A text-based source for tables of current time throughout the world.
+# Its point-and-click interface accesses a recent version of the tz data.
+#
+# Local Times Around the World
+# http://www.hilink.com.au/times/
+# This text-based system contains links to local time servers
+# throughout the world, and though the coverage is limited,
+# the live data provide a nice way to check one's tables.
+#
+# World Time Zones
+# http://tycho.usno.navy.mil/tzones.html
+# US Naval Observatory data, used as the source for `usno1995'.
+#
+# Standard Time Zones of the World
+# http://www.odci.gov/cia/publications/95fact/802389.gif [54 kB]
+# http://www.odci.gov/cia/publications/95fact/802389h.gif [1317 kB]
+# A static time zone map, available in both low-resolution and
+# high-resolution versions. The quality is good, but the map does not
+# indicate summer time, and parts of the data are a few years out of date.
+#
+# VIBE's World Map
+# http://pathfinder.com/vibe/vibeworld
+# An active time zone map. You can point to the map and find out what
+# time it is at that location. The map and data are not as good as
+# other sources.
+
+###############################################################################
+
+# From Manavendra Thakur <Manavendra_Thakur@NeXT.COM> (1995-11-06)
+#
+# To Paul's list of time zone information on the web, I would add the
+# following URL:
+# http://www.dhl.com/dhl/dhlinfo/1bb.html
+# or more simply:
+# http://www.dhl.com/
+#
+# This is run by DHL (the courier company), and it presents a list of the
+# countries served by that company. If you then click on a particular
+# country, here's an example of what you'll see (graphics stripped out):
+#
+# United Kingdom
+#
+# HOLIDAYS: Jan 1, 2, Apr 14, 17, May 1, 29, Aug 28, Dec 25, 26
+#
+# INTERNATIONAL DIALING CODE: +44
+#
+# CURRENT LOCAL TIME: 09:41 Monday 6 November 1995
+#
+# I find this rather handy, and given that DHL covers 217 countries and
+# territories, it's pretty comprehensive coverage.
+#
+# (I have no idea what system DHL is using to calculate the local time, but
+# it's been accurate so far.)
+
+###############################################################################
+
+
+# From Arthur David Olson <arthur_david_olson@nih.gov> (1996-01-04)
+#
+# A good source of information about ISO 8601 seems to be
+# http://www.ft.uni-erlangen.de/~mskuhn/iso-time.html
+# maintained by Markus Kuhn.
diff --git a/usr.sbin/zic/ialloc.c b/usr.sbin/zic/ialloc.c
new file mode 100644
index 0000000..2fd76c3
--- /dev/null
+++ b/usr.sbin/zic/ialloc.c
@@ -0,0 +1,93 @@
+#ifndef lint
+#ifndef NOID
+static char elsieid[] = "@(#)ialloc.c 8.28";
+#endif /* !defined NOID */
+#endif /* !defined lint */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+/*LINTLIBRARY*/
+
+#include "private.h"
+
+#define nonzero(n) (((n) == 0) ? 1 : (n))
+
+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 ifree P((char * pointer));
+
+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/scheck.c b/usr.sbin/zic/scheck.c
new file mode 100644
index 0000000..313068d
--- /dev/null
+++ b/usr.sbin/zic/scheck.c
@@ -0,0 +1,67 @@
+#ifndef lint
+#ifndef NOID
+static char elsieid[] = "@(#)scheck.c 8.13";
+#endif /* !defined lint */
+#endif /* !defined NOID */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id$";
+#endif /* not lint */
+
+/*LINTLIBRARY*/
+
+#include "private.h"
+
+extern char * imalloc P((int n));
+extern void ifree P((char * p));
+
+char *
+scheck(string, format)
+const char * const string;
+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..78b17cb
--- /dev/null
+++ b/usr.sbin/zic/zdump.8
@@ -0,0 +1,39 @@
+.TH ZDUMP 8
+.SH NAME
+zdump \- timezone dumper
+.SH SYNOPSIS
+.B zdump
+[
+.B \-v
+] [
+.B \-c
+cutoffyear ] [ zonename ... ]
+.SH DESCRIPTION
+.I Zdump
+prints the current time in each
+.I zonename
+named on the command line.
+.PP
+These options are available:
+.TP
+.B \-v
+For each
+.I 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
+.B isdst=1
+if the given time is Daylight Saving Time or
+.B isdst=0
+otherwise.
+.TP
+.BI "\-c " cutoffyear
+Cut off the verbose output near the start of the given year.
+.SH "SEE ALSO"
+ctime(3), tzfile(5), zic(8)
+.\" @(#)zdump.8 7.3
diff --git a/usr.sbin/zic/zdump.c b/usr.sbin/zic/zdump.c
new file mode 100644
index 0000000..096f862
--- /dev/null
+++ b/usr.sbin/zic/zdump.c
@@ -0,0 +1,367 @@
+#ifndef lint
+#ifndef NOID
+static char elsieid[] = "@(#)zdump.c 7.24";
+#endif /* !defined NOID */
+#endif /* !defined lint */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id$";
+#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 */
+
+extern char ** environ;
+extern char * tzname[2];
+
+static char * abbr();
+static long delta();
+static time_t hunt();
+static int longest;
+static void show();
+static void usage __P((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 ||
+ (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()
+{
+ 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;
+}
+
+extern struct tm * localtime();
+
+static void
+show(zone, t, v)
+char * zone;
+time_t t;
+int v;
+{
+ struct tm * tmp;
+
+ (void) printf("%-*s ", longest, zone);
+ if (v)
+ (void) printf("%.24s GMT = ", asctime(gmtime(&t)));
+ tmp = localtime(&t);
+ (void) printf("%.24s", asctime(tmp));
+ if (*abbr(tmp) != '\0')
+ (void) printf(" %s", abbr(tmp));
+ if (v) {
+ (void) printf(" isdst=%d", tmp->tm_isdst);
+#ifdef TM_GMTOFF
+ (void) printf(" gmtoff=%ld", tmp->TM_GMTOFF);
+#endif /* defined TM_GMTOFF */
+ }
+ (void) printf("\n");
+}
+
+static char *
+abbr(tmp)
+struct tm * tmp;
+{
+ register char * result;
+ static char nada;
+
+ if (tmp->tm_isdst != 0 && tmp->tm_isdst != 1)
+ return &nada;
+ result = tzname[tmp->tm_isdst];
+ return (result == NULL) ? &nada : result;
+}
diff --git a/usr.sbin/zic/zdump/Makefile b/usr.sbin/zic/zdump/Makefile
new file mode 100644
index 0000000..46af3ca
--- /dev/null
+++ b/usr.sbin/zic/zdump/Makefile
@@ -0,0 +1,13 @@
+# $Id$
+
+.PATH: ${.CURDIR}/..
+
+PROG= zdump
+
+SRCS= zdump.c ialloc.c scheck.c
+MAN8= ${.CURDIR}/../zdump.8
+CFLAGS+= -I${.CURDIR}/.. -I${.CURDIR}/../../../lib/libc/stdtime
+CFLAGS+= -DTM_GMTOFF=tm_gmtoff -DTM_ZONE=tm_zone -DSTD_INSPIRED -DPCTS
+CFLAGS+= -DHAVE_LONG_DOUBLE -DTZDIR=\"/usr/share/zoneinfo\" -Demkdir=mkdir
+
+.include <bsd.prog.mk>
diff --git a/usr.sbin/zic/zic.8 b/usr.sbin/zic/zic.8
new file mode 100644
index 0000000..7491e7a
--- /dev/null
+++ b/usr.sbin/zic/zic.8
@@ -0,0 +1,383 @@
+.Dd
+.Dt ZIC 8
+.Os
+.Sh NAME
+.Nm zic
+.Nd timezone compiler
+.Sh SYNOPSIS
+.Nm zic
+.Op Fl v
+.Op Fl d Ar directory
+.Op Fl l Ar localtime
+.Op Fl p Ar posixrules
+.Op Fl L Ar leapsecondfilename
+.Op Fl s
+.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 Ar directory
+Create time conversion information files in the named directory rather than
+in the standard directory named below.
+.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
+.sp
+.ti +.5i
+Link \fItimezone\fP localtime
+.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
+.sp
+.ti +.5i
+Link \fItimezone\fP posixrules
+.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 v
+Complain if a year that appears in a data file is outside the range
+of years representable by
+.Xr time 2
+values.
+.It Fl s
+Limit time values stored in output files to values that are the same
+whether they're taken to be signed or unsigned.
+You can use this option to generate SVVS-compatible files.
+.It Fl y Ar command
+Use the given
+.Ar command
+rather than
+.Em yearistype
+when checking year types (see below).
+.El
+.Pp
+Input lines are made up of fields.
+Fields are separated from one another by any number of white space characters.
+Leading and trailing white space on input lines is ignored.
+An unquoted sharp character (#) in the input introduces a comment which extends
+to the end of the line the sharp character appears on.
+White space characters and sharp characters may be enclosed in double quotes
+(") if they're to be used as part of a field.
+Any line that is blank (after comment stripping) is ignored.
+Non-blank lines are expected to be of one of three types:
+rule lines, zone lines, and link lines.
+.Pp
+A rule line has the form
+.nf
+.ti +.5i
+.ta \w'Rule\0\0'u +\w'NAME\0\0'u +\w'FROM\0\0'u +\w'1973\0\0'u +\w'TYPE\0\0'u +\w'Apr\0\0'u +\w'lastSun\0\0'u +\w'2:00\0\0'u +\w'SAVE\0\0'u
+.sp
+Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S
+.sp
+For example:
+.ti +.5i
+.sp
+Rule US 1967 1973 \- Apr lastSun 2:00 1:00 D
+.sp
+.fi
+The fields that make up a rule line are:
+.Bl -tag -width 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
+.ti +.5i
+\fByearistype\fP \fIyear\fP \fItype\fP
+.br
+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:
+.nf
+.in +.5i
+.sp
+.ta \w'Sun<=25\0\0'u
+5 the fifth of the month
+lastSun the last Sunday in the month
+lastMon the last Monday in the month
+Sun>=8 first Sunday on or after the eighth
+Sun<=25 last Sunday on or before the 25th
+.fi
+.in -.5i
+.sp
+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:
+.nf
+.in +.5i
+.sp
+.ta \w'1:28:13\0\0'u
+2 time in hours
+2:00 time in hours and minutes
+15:00 24-hour format time (for times after noon)
+1:28:14 time in hours, minutes, and seconds
+.fi
+.in -.5i
+.sp
+Any of these forms may be followed by the letter
+.Em w
+if the given time is local
+.q "wall clock"
+time,
+.Em s
+if the given time is local
+.q standard
+time, or
+.Em u
+(or
+.Em g
+or
+.Em 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
+.Em w
+and
+.Em s
+suffixes are not used).
+.It LETTER/S
+Give the
+.q "variable part"
+(for example, the
+.q S
+or
+.q D
+in
+.q EST
+or
+.q 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
+.sp
+.nf
+.ti +.5i
+.ta \w'Zone\0\0'u +\w'Australia/Adelaide\0\0'u +\w'GMTOFF\0\0'u +\w'RULES/SAVE\0\0'u +\w'FORMAT\0\0'u
+Zone NAME GMTOFF RULES/SAVE FORMAT [UNTIL]
+.sp
+For example:
+.sp
+.ti +.5i
+Zone Australia/Adelaide 9:30 Aus CST 1971 Oct 31 2:00
+.sp
+.fi
+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 GMT 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 GMT.
+.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
+.q "variable part"
+of the time zone abbreviation goes.
+Alternately,
+a slash (/)
+separates standard and daylight abbreviations.
+.It UNTIL
+The time at which the GMT 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 GMT offset
+and rule change until the time specified.
+.Pp
+The next line must be a
+.q continuation
+line; this has the same form as a zone line except that the
+string
+.q 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
+.sp
+.nf
+.ti +.5i
+.ta \w'Link\0\0'u +\w'Europe/Istanbul\0\0'u
+Link LINK-FROM LINK-TO
+.sp
+For example:
+.sp
+.ti +.5i
+Link Europe/Istanbul Asia/Istanbul
+.sp
+.fi
+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:
+.nf
+.ti +.5i
+.ta \w'Leap\0\0'u +\w'YEAR\0\0'u +\w'MONTH\0\0'u +\w'DAY\0\0'u +\w'HH:MM:SS\0\0'u +\w'CORR\0\0'u
+.sp
+Leap YEAR MONTH DAY HH:MM:SS CORR R/S
+.sp
+For example:
+.ti +.5i
+.sp
+Leap 1974 Dec 31 23:59:60 + S
+.sp
+.fi
+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
+.q +
+if a second was added
+or
+.q -
+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)
+.q Stationary
+if the leap second time given by the other fields should be interpreted as GMT
+or
+(an abbreviation of)
+.q Rolling
+if the leap second time given by the other fields should be interpreted as
+local wall clock time.
+.Sh NOTE
+For areas with more than two types of local time,
+you may need to use local standard time in the
+.Em AT
+field of the earliest transition time's rule to ensure that
+the earliest transition time recorded in the compiled file is correct.
+.Sh FILE
+.Bl -tag -width /usr/share/zoneinfo -compact
+/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.12
diff --git a/usr.sbin/zic/zic.c b/usr.sbin/zic/zic.c
new file mode 100644
index 0000000..89866ba
--- /dev/null
+++ b/usr.sbin/zic/zic.c
@@ -0,0 +1,2105 @@
+#ifndef lint
+#ifndef NOID
+static char elsieid[] = "@(#)zic.c 7.77";
+#endif /* !defined NOID */
+#endif /* !defined lint */
+
+#ifndef lint
+static const char rcsid[] =
+ "$Id$";
+#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;
+};
+
+extern int getopt P((int argc, char * const argv[],
+ const char * options));
+extern char * icatalloc P((char * old, const char * new));
+extern char * icpyalloc P((const char * string));
+extern void ifree P((char * p));
+extern char * imalloc P((int n));
+extern void * irealloc P((void * old, int n));
+extern int link P((const char * fromname, const char * toname));
+extern char * optarg;
+extern int optind;
+extern char * scheck P((const char * string, const char * format));
+
+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 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
+static char * strerror P((int));
+#endif /* !HAVE_STRERROR */
+
+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 time_t min_time;
+static int min_year;
+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
+static char *
+strerror(errnum)
+int errnum;
+{
+ extern char *sys_errlist[];
+ extern int sys_nerr;
+
+ if (errnum > 0 && errnum <= sys_nerr)
+ return sys_errlist[errnum];
+ return "Unknown system error";
+}
+#endif /* ! HAVE_STRERROR */
+
+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(string);
+ 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;
+
+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, "d:l:p:L:vsy:")) != -1)
+ switch (c) {
+ default:
+ usage();
+ case 'd':
+ if (directory == NULL)
+ directory = optarg;
+ else
+ errx(EXIT_FAILURE,
+_("more than one -d option specified"));
+ break;
+ case 'l':
+ if (lcltime == NULL)
+ lcltime = optarg;
+ else
+ errx(EXIT_FAILURE,
+_("more than one -l option specified"));
+ break;
+ case 'p':
+ if (psxrules == NULL)
+ psxrules = optarg;
+ else
+ errx(EXIT_FAILURE,
+_("more than one -p option specified"));
+ 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) {
+ if (mkdirs(toname) != 0)
+ (void) exit(EXIT_FAILURE);
+ if (link(fromname, toname) != 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;
+}
+
+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) {
+ 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 GMT 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';
+ 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;
+ }
+ 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;
+ }
+ 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);
+ }
+ /*
+ ** 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;
+ if (isdsts[0] == 0)
+ while (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);
+#define DO(field) (void) fwrite((void *) tzh.field, (size_t) sizeof tzh.field, (size_t) 1, fp)
+ 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);
+}
+
+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 GMT
+ ** 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 abbrevation 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;
+const int type;
+{
+ 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')
+ 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.
+ */
+ if (mkdir(name, 0755) != 0) {
+ 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;
+}
+
+/*
+** UNIX was a registered trademark of UNIX System Laboratories in 1993.
+*/
diff --git a/usr.sbin/zic/zic/Makefile b/usr.sbin/zic/zic/Makefile
new file mode 100644
index 0000000..2f6e49d
--- /dev/null
+++ b/usr.sbin/zic/zic/Makefile
@@ -0,0 +1,14 @@
+# $Id$
+
+.PATH: ${.CURDIR}/..
+
+PROG= zic
+
+SRCS= zic.c ialloc.c scheck.c
+MAN8= ${.CURDIR}/../zic.8
+CFLAGS+= -I${.CURDIR}/.. -I${.CURDIR}/../../../lib/libc/stdtime
+CFLAGS+= -DTM_GMTOFF=tm_gmtoff -DTM_ZONE=tm_zone -DSTD_INSPIRED -DPCTS
+CFLAGS+= -DHAVE_LONG_DOUBLE -DTZDIR=\"/usr/share/zoneinfo\" -Demkdir=mkdir
+CFLAGS+= -DHAVE_STRERROR -DHAVE_UNISTD_H
+
+.include <bsd.prog.mk>
OpenPOWER on IntegriCloud